1227569Sphilip// Copyright 2018 The Fuchsia Authors. All rights reserved. 2300607Sarybchik// Use of this source code is governed by a BSD-style license that can be 3283514Sarybchik// found in the LICENSE file. 4227569Sphilip 5227569Sphilip#include <ddk/binding.h> 6283514Sarybchik#include <ddk/device.h> 7227569Sphilip#include <ddk/driver.h> 8283514Sarybchik 9283514Sarybchik#include <zircon/syscalls.h> 10283514Sarybchik#include <zircon/types.h> 11283514Sarybchik 12283514Sarybchik#include <stdio.h> 13228078Sphilip#include <stdlib.h> 14283514Sarybchik#include <string.h> 15283514Sarybchik#include <threads.h> 16283514Sarybchik 17283514Sarybchik// fifo must be a power of 2 for the math to work 18283514Sarybchik#define FIFOSIZE 32768 19283514Sarybchik#define FIFOMASK (FIFOSIZE - 1) 20283514Sarybchik 21283514Sarybchiktypedef struct { 22283514Sarybchik zx_device_t* zxdev; 23283514Sarybchik mtx_t lock; 24283514Sarybchik uint32_t head; 25283514Sarybchik uint32_t tail; 26283514Sarybchik char data[FIFOSIZE]; 27283514Sarybchik} fifodev_t; 28283514Sarybchik 29283514Sarybchikstatic size_t fifo_readable(fifodev_t* fifo) { 30228078Sphilip return (fifo->head - fifo->tail) & FIFOMASK; 31227569Sphilip} 32227569Sphilip 33227569Sphilipstatic size_t fifo_writable(fifodev_t* fifo) { 34227569Sphilip return FIFOMASK - ((fifo->head - fifo->tail) & FIFOMASK); 35227569Sphilip} 36227569Sphilip 37227569Sphilipstatic size_t fifo_put(fifodev_t* fifo, const void* buf, size_t len) { 38227569Sphilip size_t count = fifo_writable(fifo); 39227569Sphilip uint32_t pos = fifo->head & FIFOMASK; 40227569Sphilip size_t space = FIFOSIZE - pos; 41227569Sphilip if (count > space) { // don't wrap around (single copy) 42279183Sarybchik count = space; 43279183Sarybchik } 44279183Sarybchik if (count > len) { // limit to requested count 45279183Sarybchik count = len; 46279183Sarybchik } 47279183Sarybchik memcpy(fifo->data + pos, buf, count); 48279183Sarybchik fifo->head += count; 49227569Sphilip return count; 50227569Sphilip} 51227569Sphilip 52227569Sphilipstatic size_t fifo_get(fifodev_t* fifo, void* buf, size_t len) { 53227569Sphilip size_t count = fifo_readable(fifo); 54227569Sphilip uint32_t pos = fifo->tail & FIFOMASK; 55227569Sphilip size_t space = FIFOSIZE - pos; 56227569Sphilip if (count > space) { // don't wrap around (single copy) 57227569Sphilip count = space; 58227569Sphilip } 59227569Sphilip if (count > len) { // limit to requested count 60227569Sphilip count = len; 61227569Sphilip } 62227569Sphilip memcpy(buf, fifo->data + pos, count); 63227569Sphilip fifo->tail += count; 64227569Sphilip return count; 65227569Sphilip} 66227569Sphilip 67227569Sphilip 68227569Sphilipstatic zx_status_t fifo_read(void* ctx, void* buf, size_t len, 69227569Sphilip zx_off_t off, size_t* actual) { 70227569Sphilip fifodev_t* fifo = ctx; 71227569Sphilip 72227569Sphilip mtx_lock(&fifo->lock); 73227569Sphilip size_t n = 0; 74227569Sphilip size_t count; 75227569Sphilip while ((count = fifo_get(fifo, buf, len)) > 0) { 76227569Sphilip len -= count; 77227569Sphilip buf += count; 78227569Sphilip n += count; 79227569Sphilip } 80227569Sphilip if (n == 0) { 81227569Sphilip device_state_clr(fifo->zxdev, DEV_STATE_READABLE); 82227569Sphilip } else { 83227569Sphilip device_state_set(fifo->zxdev, DEV_STATE_WRITABLE); 84227569Sphilip } 85227569Sphilip mtx_unlock(&fifo->lock); 86227569Sphilip *actual = n; 87227569Sphilip 88227569Sphilip return (n == 0) ? ZX_ERR_SHOULD_WAIT : ZX_OK; 89227569Sphilip} 90227569Sphilip 91227569Sphilipstatic zx_status_t fifo_write(void* ctx, const void* buf, size_t len, 92227569Sphilip zx_off_t off, size_t* actual) { 93227569Sphilip 94227569Sphilip fifodev_t* fifo = ctx; 95227569Sphilip 96227569Sphilip mtx_lock(&fifo->lock); 97227569Sphilip size_t n = 0; 98227569Sphilip size_t count; 99227569Sphilip while ((count = fifo_put(fifo, buf, len)) > 0) { 100227569Sphilip len -= count; 101227569Sphilip buf += count; 102227569Sphilip n += count; 103227569Sphilip } 104227569Sphilip if (n == 0) { 105227569Sphilip device_state_clr(fifo->zxdev, DEV_STATE_WRITABLE); 106227569Sphilip } else { 107227569Sphilip device_state_set(fifo->zxdev, DEV_STATE_READABLE); 108227569Sphilip } 109227569Sphilip mtx_unlock(&fifo->lock); 110227569Sphilip *actual = n; 111227569Sphilip 112227569Sphilip return (n == 0) ? ZX_ERR_SHOULD_WAIT : ZX_OK; 113227569Sphilip} 114227569Sphilip 115227569Sphilipstatic void fifo_release(void* ctx) { 116227569Sphilip fifodev_t* fifo = ctx; 117227569Sphilip free(fifo); 118227569Sphilip} 119227569Sphilip 120227569Sphilipstatic zx_protocol_device_t fifo_ops = { 121227569Sphilip .version = DEVICE_OPS_VERSION, 122227569Sphilip .read = fifo_read, 123227569Sphilip .write = fifo_write, 124227569Sphilip .release = fifo_release, 125227569Sphilip}; 126227569Sphilip 127227569Sphilipstatic zx_status_t fifo_bind(void* ctx, zx_device_t* parent) { 128227569Sphilip fifodev_t* fifo = calloc(1, sizeof(fifodev_t)); 129227569Sphilip if (fifo == NULL) { 130227569Sphilip return ZX_ERR_NO_MEMORY; 131227569Sphilip } 132227569Sphilip mtx_init(&fifo->lock, mtx_plain); 133227569Sphilip 134227569Sphilip device_add_args_t args = { 135227569Sphilip .version = DEVICE_ADD_ARGS_VERSION, 136227569Sphilip .name = "demo-fifo", 137227569Sphilip .ctx = fifo, 138227569Sphilip .ops = &fifo_ops, 139227569Sphilip }; 140227569Sphilip zx_status_t status = device_add(parent, &args, &fifo->zxdev); 141227569Sphilip if (status != ZX_OK) { 142227569Sphilip free(fifo); 143227569Sphilip return status; 144227569Sphilip } 145227569Sphilip 146227569Sphilip // initially we're empty, so writable but not readable 147227569Sphilip device_state_set(fifo->zxdev, DEV_STATE_WRITABLE); 148227569Sphilip 149227569Sphilip return ZX_OK; 150227569Sphilip} 151227569Sphilip 152227569Sphilipstatic zx_driver_ops_t fifo_driver_ops = { 153227569Sphilip .version = DRIVER_OPS_VERSION, 154227569Sphilip .bind = fifo_bind, 155227569Sphilip}; 156227569Sphilip 157227569SphilipZIRCON_DRIVER_BEGIN(demo_fifo, fifo_driver_ops, "zircon", "0.1", 1) 158227569Sphilip BI_MATCH_IF(EQ, BIND_PROTOCOL, ZX_PROTOCOL_MISC_PARENT), 159227569SphilipZIRCON_DRIVER_END(demo_fifo) 160227569Sphilip