summaryrefslogtreecommitdiffstats
path: root/common/serdev.c
diff options
context:
space:
mode:
authorAndrey Smirnov <andrew.smirnov@gmail.com>2018-04-12 14:33:14 -0700
committerSascha Hauer <s.hauer@pengutronix.de>2018-04-17 09:21:10 +0200
commitc2ee90fb9ed90c3612301e2b86c05cfb3c008fde (patch)
treebf1200bdc97642a02a7ed89cca57be1daf57ef9c /common/serdev.c
parent84fc6119d3aaa33344d154428ce37800a96a39b9 (diff)
downloadbarebox-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.c89
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;
+}