/* * Copyright 2017, Data61 * Commonwealth Scientific and Industrial Research Organisation (CSIRO) * ABN 41 687 119 230. * * This software may be distributed and modified according to the terms of * the BSD 2-Clause license. Note that NO WARRANTY is provided. * See "LICENSE_BSD2.txt" for details. * * @TAG(DATA61_BSD) */ #pragma once #include struct ps_clk; typedef struct ps_chardevice ps_chardevice_t; #include enum chardev_status { /// Transfer completed successfully CHARDEV_STAT_COMPLETE, /// Transfer was truncated or cancelled by the remote CHARDEV_STAT_INCOMPLETE, /// A transfer error occurred CHARDEV_STAT_ERROR, /// The transfer was aborted by the user CHARDEV_STAT_CANCELLED, }; typedef void (*chardev_callback_t)(ps_chardevice_t* device, enum chardev_status stat, size_t bytes_transfered, void* token); struct chardev_xmit_descriptor { /// A function to call when the transfer is complete chardev_callback_t callback; /// A token to pass unmodified to callback void* token; /// The number of bytes transfered thus far size_t bytes_transfered; /// The total number of bytes to transfer volatile size_t bytes_requested; /// The source or destination for the data void* data; }; struct ps_chardevice { /* identifier for the device */ enum chardev_id id; void* vaddr; /* Character operations for this device */ ssize_t (*read)(ps_chardevice_t* device, void* data, size_t bytes, chardev_callback_t cb, void* token); ssize_t (*write)(ps_chardevice_t* device, const void* data, size_t bytes, chardev_callback_t cb, void* token); void (*handle_irq)(ps_chardevice_t* device); /* array of irqs associated with this device */ const int *irqs; /// Transmit transfer data for use with IRQs struct chardev_xmit_descriptor read_descriptor; /// Receive transfer data for use with IRQs struct chardev_xmit_descriptor write_descriptor; /* Input clock for this device */ struct ps_clk* clk; /* OS specific memory operations */ ps_io_ops_t ioops; /* Device specific flags */ int flags; }; /* * Initialiase a device * @param id: the id of the character device * @param ops: a structure containing OS specific operations for memory access * @param dev: a character device structure to populate * @return : NULL on error, otherwise returns the device structure pointer */ ps_chardevice_t* ps_cdev_init(enum chardev_id id, const ps_io_ops_t* ops, ps_chardevice_t* dev); /* * Statically initialise a device * @param ops: a structure containing OS specific operations for memory access * @param dev: a character device structure to populate * @param params: a pointer generally used to pass machine or platform * specific parameters */ ps_chardevice_t* ps_cdev_static_init(const ps_io_ops_t *ops, ps_chardevice_t* dev, void *params); /* * Create a pseudo device: initialise with nop function pointers * @param o: a structure containing OS specific operations for memory access * @param d: a character device structure to populate * @return : NULL on error, otherwise returns the device structure pointer */ ps_chardevice_t* ps_cdev_new(const ps_io_ops_t* o, ps_chardevice_t* d); /** * Send a character to the device. New lines will be automatically be chased * by a character return * @param[in] d The character device to send a character to * @param[in] c The character to send */ static inline void ps_cdev_putchar(ps_chardevice_t* d, int c) { int ret; do { char data = c; ret = d->write(d, &data, 1, NULL, NULL); } while (ret < 1); } /** * Receive a character from the device * @param[in] d The device to receive a character from * @return The chracter received; negative values signal that an error occurred. */ static inline int ps_cdev_getchar(ps_chardevice_t* d) { int ret; char data; ret = d->read(d, &data, 1, NULL, NULL); return (ret == 1) ? data : EOF; } /** * Read data from a device * @param[in] d The device to read data from * @param[out] data The location to store the read data to * @param[in] size The number of bytes to read * @param[in] callback Optional: A function to call when the requested number of * bytes have been read. The caller must periodically call * the IRQ handler to satisfy the request. * @param[in] token An anonymous pointer to pass, unmodified, to the provided * callback function. * @return Returns the number of bytes read on succes, negative * values reprents an error. If a callback function is * provided, a return value of 0 will represent success * If no callback funtion is provided, this function will * read data from any internal fifos to meet the the request * and then return. It will not block until the requested * number of bytes are available. */ static inline ssize_t ps_cdev_read(ps_chardevice_t* d, void* data, size_t size, chardev_callback_t callback, void* token) { return d->read(d, data, size, callback, token); } /** * Write data to a device * @param[in] d The device to write data to * @param[out] data The location of the data to be written * @param[in] size The number of bytes to write * @param[in] callback Optional: A function to call when the requested number of * bytes have been written. The caller must periodically call * the IRQ handler to satisfy the request. * @param[in] token An anonymous pointer to pass, unmodified, to the provided * callback function. * @return Returns the number of bytes written on succes, negative * values reprents an error. If a callback function is * provided, a return value of 0 will represent success * If no callback funtion is provided, this function will * write data to any internal fifos to meet the the request * and then return. It will not block until the requested * number of bytes have been written. */ static inline ssize_t ps_cdev_write(ps_chardevice_t* d, void* data, size_t size, chardev_callback_t callback, void* token) { return d->write(d, data, size, callback, token); } /** * Pass control to the devices IRQ handler * @param[in] The device to pass control to * @param[in] The physical IRQ number that triggered the event */ static inline void ps_cdev_handle_irq(ps_chardevice_t* d, int irq UNUSED) { d->handle_irq(d); } /** * Check if the given device emits the given IRQ * @param[in] d The device to query * @param[in] irq An irq number * @return non-zero if the device will produce the given IRQ */ static inline int ps_cdev_produces_irq(const ps_chardevice_t* d, int irq) { int i; for (i = 0; d->irqs[i] != -1; i++) { if (d->irqs[i] == irq) { return 1; } } return 0; } /** * Set the device specific flags. * @param[in] d The character device to set the flags to * @param[in] flags The flags to set */ static inline void ps_cdev_set_flags(ps_chardevice_t* d, int flags) { d->flags = flags; }