diff options
author | Andrey Smirnov <andrew.smirnov@gmail.com> | 2018-04-12 14:33:14 -0700 |
---|---|---|
committer | Sascha Hauer <s.hauer@pengutronix.de> | 2018-04-17 09:21:10 +0200 |
commit | c2ee90fb9ed90c3612301e2b86c05cfb3c008fde (patch) | |
tree | bf1200bdc97642a02a7ed89cca57be1daf57ef9c /common/serdev.c | |
parent | 84fc6119d3aaa33344d154428ce37800a96a39b9 (diff) | |
download | barebox-c2ee90fb9ed90c3612301e2b86c05cfb3c008fde.tar.gz barebox-c2ee90fb9ed90c3612301e2b86c05cfb3c008fde.tar.xz |
console: Add simplified 'serdev' framework from Linux kernel
Port 'serdev' UART-slave deivce framework found in recent Linux
kernels (post 4.13) in order to be able to port 'serdev' slave drivers
from Linux.
Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Diffstat (limited to 'common/serdev.c')
-rw-r--r-- | common/serdev.c | 89 |
1 files changed, 89 insertions, 0 deletions
diff --git a/common/serdev.c b/common/serdev.c new file mode 100644 index 0000000000..32743738ed --- /dev/null +++ b/common/serdev.c @@ -0,0 +1,89 @@ + +#include <common.h> +#include <serdev.h> + +static void serdev_device_poller(void *context) +{ + struct serdev_device *serdev = context; + struct console_device *cdev = to_console_device(serdev); + unsigned char *buf = serdev->buf; + int ret, len; + + /* + * Since this callback is a part of poller infrastructure we + * want to use _non_interruptible version of the function + * below to prevent recursion from happening (regular + * console_drain will call is_timeout, which might end up + * calling this function again). + */ + len = console_drain_non_interruptible(cdev, serdev->fifo, buf, + PAGE_SIZE, + serdev->polling_window); + while (len > 0) { + ret = serdev->receive_buf(serdev, buf, len); + len -= ret; + buf += ret; + } + + if (serdev->polling_interval) { + /* + * Re-schedule ourselves in 'serdev->polling_interval' + * nanoseconds + */ + poller_call_async(&serdev->poller, + serdev->polling_interval, + serdev_device_poller, + serdev); + } +} + +int serdev_device_open(struct serdev_device *serdev) +{ + struct console_device *cdev = to_console_device(serdev); + int ret; + + if (!cdev->putc || !cdev->getc) + return -EINVAL; + + if (!serdev->polling_window) + return -EINVAL; + + serdev->buf = xzalloc(PAGE_SIZE); + serdev->fifo = kfifo_alloc(PAGE_SIZE); + if (!serdev->fifo) + return -ENOMEM; + + ret = poller_async_register(&serdev->poller); + if (ret) + return ret; + + return console_open(cdev); +} + +unsigned int serdev_device_set_baudrate(struct serdev_device *serdev, + unsigned int speed) +{ + struct console_device *cdev = to_console_device(serdev); + + if (console_set_baudrate(cdev, speed) < 0) + return 0; + + return console_get_baudrate(cdev); +} + +int serdev_device_write(struct serdev_device *serdev, const unsigned char *buf, + size_t count, unsigned long timeout) +{ + struct console_device *cdev = to_console_device(serdev); + + while (count--) + cdev->putc(cdev, *buf++); + /* + * Poll Rx once right after we just send some data in case our + * serdev device implements command/response type of a + * protocol and we need to start draining input as soon as + * possible. + */ + serdev_device_poller(serdev); + return 0; +} |