blob: 75e84b37fddb7707427b3f3328539fcc1bcfaad8 [file] [log] [blame]
Carl van Schaikfde8e2e2018-07-03 12:23:26 +10001/*
2 * drivers/char/okl4_vtty.c
3 *
4 * Copyright (c) 2012-2014 General Dynamics
5 * Copyright (c) 2014 Open Kernel Labs, Inc.
6 * Copyright (c) 2014-2017 Cog Systems Pty Ltd
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
11 *
12 * OKL4 Microvisor Virtual TTY driver.
13 *
14 * Clients using this driver must have vclient names of the form
15 * "vtty%d", where %d is the tty number, which must be
16 * unique and less than MAX_VTTYS.
17 */
18
19/* #define DEBUG 1 */
20/* #define VERBOSE_DEBUG 1 */
21
22#include <linux/init.h>
23#include <linux/err.h>
24#include <linux/string.h>
25#include <linux/kernel.h>
26#include <linux/module.h>
27#include <linux/interrupt.h>
28#include <linux/platform_device.h>
29#include <linux/mutex.h>
30#include <linux/tty.h>
31#include <linux/tty_driver.h>
32#include <linux/tty_flip.h>
33#include <linux/console.h>
34#include <linux/delay.h>
35#include <linux/workqueue.h>
36#include <linux/slab.h>
37#include <linux/version.h>
38#include <linux/of.h>
39#include <linux/uaccess.h>
40#include <linux/seq_file.h>
41#include <clocksource/arm_arch_timer.h>
42#include <asm-generic/okl4_virq.h>
43
44#include <microvisor/microvisor.h>
45#if 0
46#include <asm/okl4-microvisor/okl4tags.h>
47#include <asm/okl4-microvisor/microvisor_bus.h>
48#include <asm/okl4-microvisor/virq.h>
49#endif
50
51#define DRIVER_NAME "okl4-vtty"
52#define DEVICE_NAME "vtty"
53#define DEVICE_PREFIX "ttyV"
54
55/* FIXME: Jira ticket SDK-138 - philipd. */
56#define MAX_VTTYS 8
57
58struct vtty_port {
59 bool exists;
60 int vtty_id;
61
62 bool read_throttled, write_full, irq_registered;
63 struct work_struct read_work;
64 spinlock_t write_lock;
65
66 /*
67 * Buffer length is max_msg_size plus one u32, which encodes the
68 * message length.
69 */
70 char *read_buf;
71 int read_buf_pos, read_buf_len;
72 char *write_buf;
73 int write_buffered;
74 size_t max_msg_size;
75
76 okl4_kcap_t pipe_tx_kcap;
77 okl4_kcap_t pipe_rx_kcap;
78 int tx_irq;
79 int rx_irq;
80
81#ifdef CONFIG_OKL4_VTTY_CONSOLE
82 struct console console;
83#endif
84
85 struct device *dev;
86 struct tty_port port;
87};
88
89static struct workqueue_struct *read_workqueue;
90
91static struct vtty_port ports[MAX_VTTYS];
92
93static void
94vtty_read_irq(struct vtty_port *port)
95{
96 queue_work(read_workqueue, &port->read_work);
97}
98
99static int
100do_pipe_write(struct vtty_port *port, int count)
101{
102 okl4_error_t ret;
103 int send;
104
105 if (port->write_full)
106 return 0;
107
108 BUG_ON(count > port->max_msg_size);
109
110 *(u32 *)port->write_buf = count;
111 send = roundup(count + sizeof(u32), sizeof(u32));
112
113 ret = _okl4_sys_pipe_send(port->pipe_tx_kcap, send,
114 (void *)port->write_buf);
115
116 if (ret == OKL4_ERROR_PIPE_NOT_READY) {
117 okl4_pipe_control_t x = 0;
118
119 okl4_pipe_control_setdoop(&x, true);
120 okl4_pipe_control_setoperation(&x,
121 OKL4_PIPE_CONTROL_OP_SET_TX_READY);
122 _okl4_sys_pipe_control(port->pipe_tx_kcap, x);
123
124 ret = _okl4_sys_pipe_send(port->pipe_tx_kcap, send,
125 (void *)port->write_buf);
126 }
127
128 if (ret == OKL4_ERROR_PIPE_FULL ||
129 ret == OKL4_ERROR_PIPE_NOT_READY) {
130 port->write_full = true;
131 return 0;
132 }
133
134 if (ret != OKL4_OK)
135 return -EIO;
136
137 return count;
138}
139
140static void
141vtty_write_irq(struct vtty_port *port)
142{
143 struct tty_struct *tty = tty_port_tty_get(&port->port);
144
145 spin_lock(&port->write_lock);
146
147 port->write_full = false;
148
149 if (port->write_buffered &&
150 do_pipe_write(port, port->write_buffered) > 0)
151 port->write_buffered = 0;
152
153 if (tty)
154 tty_wakeup(tty);
155
156 spin_unlock(&port->write_lock);
157
158 tty_kref_put(tty);
159}
160
161static irqreturn_t
162vtty_tx_irq(int irq, void *dev)
163{
164 struct vtty_port *port = dev;
165 okl4_pipe_state_t payload = okl4_get_virq_payload(irq);
166
167 if (okl4_pipe_state_gettxavailable(&payload))
168 vtty_write_irq(port);
169
170 return IRQ_HANDLED;
171}
172
173static irqreturn_t
174vtty_rx_irq(int irq, void *dev)
175{
176 struct vtty_port *port = dev;
177 okl4_pipe_state_t payload = okl4_get_virq_payload(irq);
178
179 if (okl4_pipe_state_getrxavailable(&payload))
180 vtty_read_irq(port);
181
182 return IRQ_HANDLED;
183}
184
185static int
186vtty_install(struct tty_driver *driver, struct tty_struct *tty)
187{
188 int port_num = tty->index;
189 struct vtty_port *port;
190 int status;
191
192 if (port_num < 0 || port_num >= MAX_VTTYS)
193 return -ENXIO;
194
195 port = &ports[port_num];
196 if (!port->exists)
197 return -ENODEV;
198
199 tty->driver_data = port;
200
201 port->write_full = false;
202 port->read_throttled = false;
203 port->write_buffered = 0;
204
205 /*
206 * low_latency forces all tty read handling to be done by the
207 * read task.
208 */
209 port->port.low_latency = 1;
210
211 if (!port->irq_registered) {
212 status = devm_request_irq(port->dev, port->tx_irq,
213 vtty_tx_irq, 0, dev_name(port->dev), port);
214 if (status)
215 return status;
216
217 status = devm_request_irq(port->dev, port->rx_irq,
218 vtty_rx_irq, 0, dev_name(port->dev), port);
219 if (status) {
220 devm_free_irq(port->dev, port->tx_irq, port);
221 return status;
222 }
223
224 port->irq_registered = true;
225 }
226
227#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 7, 0)
228 tty_port_install(&port->port, driver, tty);
229#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)
230 tty->port = &port->port;
231 tty_standard_install(driver, tty);
232#else
233 tty->port = &port->port;
234 if (tty_init_termios(tty) != 0)
235 return -ENOMEM;
236
237 tty_driver_kref_get(driver);
238 tty->count++;
239 driver->ttys[tty->index] = tty;
240#endif
241
242 return 0;
243}
244
245static int
246vtty_open(struct tty_struct *tty, struct file *file)
247{
248 struct vtty_port *port = tty->driver_data;
249 okl4_pipe_control_t x = 0;
250
251 okl4_pipe_control_setdoop(&x, true);
252 okl4_pipe_control_setoperation(&x,
253 OKL4_PIPE_CONTROL_OP_SET_TX_READY);
254 _okl4_sys_pipe_control(port->pipe_tx_kcap, x);
255 okl4_pipe_control_setoperation(&x,
256 OKL4_PIPE_CONTROL_OP_SET_RX_READY);
257 _okl4_sys_pipe_control(port->pipe_rx_kcap, x);
258
259 return tty_port_open(&port->port, tty, file);
260}
261
262static void
263vtty_close(struct tty_struct *tty, struct file *file)
264{
265 struct vtty_port *port = tty->driver_data;
266 if (port)
267 tty_port_close(&port->port, tty, file);
268}
269
270static int
271vtty_activate(struct tty_port *port, struct tty_struct *tty)
272{
273 struct vtty_port *vtty_port = tty->driver_data;
274
275 /* Run the read task immediately to drain the channel */
276 queue_work(read_workqueue, &vtty_port->read_work);
277
278 return 0;
279}
280
281static void
282vtty_shutdown(struct tty_port *port)
283{
284 struct vtty_port *vtty_port =
285 container_of(port, struct vtty_port, port);
286
287 cancel_work_sync(&vtty_port->read_work);
288}
289
290static int
291do_vtty_write(struct tty_struct *tty, const unsigned char *buf, int count)
292{
293 struct vtty_port *port = tty->driver_data;
294 int retval = 0;
295 unsigned long flags;
296
297 spin_lock_irqsave(&port->write_lock, flags);
298
299 /* If we have a whole message, try to send it */
300 if (port->write_buffered == 0 && count >= port->max_msg_size) {
301 if (count > port->max_msg_size)
302 count = port->max_msg_size;
303
304 memcpy(&port->write_buf[sizeof(u32)], buf, count);
305 retval = do_pipe_write(port, count);
306 count -= retval;
307 }
308
309 /* If nothing was sent yet, buffer the data */
310 if (!retval) {
311 /* Determine how much data will fit in the buffer */
312 if (count > port->max_msg_size - port->write_buffered)
313 count = port->max_msg_size - port->write_buffered;
314
315 /* Copy into the buffer if possible */
316 if (count) {
317 memcpy(&port->write_buf[sizeof(u32) +
318 port->write_buffered], buf, count);
319 port->write_buffered += count;
320 retval = count;
321 }
322
323 /* Flush the buffer if it is full */
324 if (port->write_buffered == port->max_msg_size) {
325 if (do_pipe_write(port, port->write_buffered) > 0)
326 port->write_buffered = 0;
327 }
328 }
329
330 spin_unlock_irqrestore(&port->write_lock, flags);
331
332 return retval;
333}
334
335static void
336vtty_flush_chars(struct tty_struct *tty)
337{
338 struct vtty_port *port = tty->driver_data;
339 unsigned long flags;
340
341 spin_lock_irqsave(&port->write_lock, flags);
342
343 if (port->write_buffered && do_pipe_write(port,
344 port->write_buffered) > 0) {
345 port->write_buffered = 0;
346 tty_wakeup(tty);
347 }
348
349 spin_unlock_irqrestore(&port->write_lock, flags);
350}
351
352static int
353vtty_put_char(struct tty_struct *tty, unsigned char ch)
354{
355 return do_vtty_write(tty, &ch, 1);
356}
357
358static int
359vtty_write(struct tty_struct *tty, const unsigned char *buf, int count)
360{
361 int retval;
362
363 retval = do_vtty_write(tty, buf, count);
364 vtty_flush_chars(tty);
365
366 return retval;
367}
368
369static int
370vtty_write_room(struct tty_struct *tty)
371{
372 struct vtty_port *port = tty->driver_data;
373
374 /*
375 * If the channel is full, we have to buffer writes locally. While
376 * vtty_write() can handle that, we may as well tell the ldisc to wait
377 * for the channel to drain, so we return 0 here.
378 */
379 return port->write_full ? 0 : port->max_msg_size - port->write_buffered;
380}
381
382static int
383vtty_chars_in_buffer(struct tty_struct *tty)
384{
385 struct vtty_port *port = tty->driver_data;
386
387 return port->max_msg_size - vtty_write_room(tty);
388}
389
390static void
391vtty_throttle(struct tty_struct *tty)
392{
393 struct vtty_port *port = tty->driver_data;
394
395 port->read_throttled = true;
396}
397
398static void
399vtty_unthrottle(struct tty_struct *tty)
400{
401 struct vtty_port *port = tty->driver_data;
402
403 port->read_throttled = false;
404 queue_work(read_workqueue, &port->read_work);
405}
406
407static const struct tty_port_operations vtty_port_ops = {
408 .activate = vtty_activate,
409 .shutdown = vtty_shutdown,
410};
411
412static int vtty_proc_show(struct seq_file *m, void *v)
413{
414 int i;
415
416 seq_puts(m, "okl4vttyinfo:1.0 driver:1.0\n");
417 for (i = 0; i < sizeof(ports)/sizeof(ports[0]); i++) {
418 struct vtty_port *port = &ports[i];
419
420 if (!port->exists)
421 continue;
422 seq_printf(m, "%d: tx_kcap: %d tx_irq: %d rx_kcap: %d rx_irq: %d\n",
423 i, port->pipe_tx_kcap, port->tx_irq, port->pipe_rx_kcap, port->rx_irq);
424 }
425
426 return 0;
427}
428
429static int vtty_proc_open(struct inode *inode, struct file *file)
430{
431 return single_open(file, vtty_proc_show, NULL);
432}
433
434static const struct file_operations vtty_proc_fops = {
435 .owner = THIS_MODULE,
436 .open = vtty_proc_open,
437 .read = seq_read,
438 .llseek = seq_lseek,
439 .release = single_release,
440};
441
442static const struct tty_operations vtty_ops = {
443 .install = vtty_install,
444 .open = vtty_open,
445 .close = vtty_close,
446 .write = vtty_write,
447 .put_char = vtty_put_char,
448 .flush_chars = vtty_flush_chars,
449 .write_room = vtty_write_room,
450 .chars_in_buffer = vtty_chars_in_buffer,
451 .throttle = vtty_throttle,
452 .unthrottle = vtty_unthrottle,
453 .proc_fops = &vtty_proc_fops,
454};
455
456static void
457vtty_read_task(struct work_struct *work)
458{
459 struct vtty_port *port = container_of(work, struct vtty_port,
460 read_work);
461 struct tty_struct *tty = tty_port_tty_get(&port->port);
462 bool pushed = false;
463
464 if (!tty)
465 return;
466
467 while (true) {
468 struct _okl4_sys_pipe_recv_return ret_recv;
469 int space, len;
470
471 /* Stop reading if we are throttled. */
472 if (port->read_throttled)
473 break;
474
475 /* Find out how much space we have in the tty buffer. */
476 space = tty_buffer_request_room(&port->port,
477 port->max_msg_size);
478
479 if (space == 0) {
480 BUG_ON(pushed);
481 tty_flip_buffer_push(&port->port);
482 pushed = true;
483 continue;
484 } else {
485 pushed = false;
486 }
487
488 if (port->read_buf_pos == port->read_buf_len) {
489 /*
490 * We have run out of chars in our message buffer.
491 * Check whether there are any more messages in the
492 * queue.
493 */
494
495 ret_recv = _okl4_sys_pipe_recv(port->pipe_rx_kcap,
496 port->max_msg_size + sizeof(u32),
497 (void *)port->read_buf);
498 if (ret_recv.error == OKL4_ERROR_PIPE_NOT_READY) {
499 okl4_pipe_control_t x = 0;
500
501 okl4_pipe_control_setdoop(&x, true);
502 okl4_pipe_control_setoperation(&x,
503 OKL4_PIPE_CONTROL_OP_SET_RX_READY);
504 _okl4_sys_pipe_control(port->pipe_rx_kcap, x);
505
506 ret_recv = _okl4_sys_pipe_recv(port->pipe_rx_kcap,
507 port->max_msg_size + sizeof(u32),
508 (void *)port->read_buf);
509 }
510 if (ret_recv.error == OKL4_ERROR_PIPE_EMPTY ||
511 ret_recv.error == OKL4_ERROR_PIPE_NOT_READY) {
512 port->read_buf_pos = 0;
513 port->read_buf_len = 0;
514 break;
515 }
516
517 if (ret_recv.error != OKL4_OK) {
518 dev_err(port->dev,
519 "pipe receive returned error %d in vtty driver !\n",
520 (int)ret_recv.error);
521 port->read_buf_pos = 0;
522 port->read_buf_len = 0;
523 break;
524 }
525
526 port->read_buf_pos = sizeof(uint32_t);
527 port->read_buf_len = sizeof(uint32_t) +
528 *(uint32_t *)port->read_buf;
529 }
530
531 /* Send chars to tty layer. */
532 len = port->read_buf_len - port->read_buf_pos;
533 if (len > space)
534 len = space;
535
536 tty_insert_flip_string(&port->port, port->read_buf +
537 port->read_buf_pos, len);
538 port->read_buf_pos += len;
539 }
540
541 tty_flip_buffer_push(&port->port);
542
543 tty_kref_put(tty);
544}
545
546static struct tty_driver *vtty_driver;
547
548#ifdef CONFIG_OKL4_VTTY_CONSOLE
549static int vconsole_setup(struct console *co, char *options);
550static void vconsole_write(struct console *co, const char *p, unsigned count);
551static struct tty_driver *vconsole_device(struct console *co, int *index);
552#endif
553
554static int
555vtty_probe(struct platform_device *pdev)
556{
557 struct vtty_port *vtty_port;
558 struct device *tty_dev;
559 u32 reg[2];
560 int vtty_id, irq, err;
561
562 vtty_id = of_alias_get_id(pdev->dev.of_node, "vserial");
563 if (vtty_id < 0)
564 vtty_id = of_alias_get_id(pdev->dev.of_node, "serial");
565
566 if (vtty_id < 0 || vtty_id >= MAX_VTTYS) {
567 err = -ENXIO;
568 goto fail_vtty_id;
569 }
570
571 vtty_port = &ports[vtty_id];
572 if (vtty_port->exists) {
573 dev_err(&pdev->dev, "vtty port already exists\n");
574 err = -ENODEV;
575 goto fail_vtty_id;
576 }
577
578 if (of_property_read_u32_array(pdev->dev.of_node, "reg", reg, 2)) {
579 dev_err(&pdev->dev, "need 2 reg resources\n");
580 err = -ENODEV;
581 goto fail_vtty_id;
582 }
583
584 dev_set_drvdata(&pdev->dev, vtty_port);
585
586 /* Set up and register the tty port */
587 vtty_port->dev = &pdev->dev;
588 vtty_port->vtty_id = vtty_id;
589 tty_port_init(&vtty_port->port);
590 vtty_port->port.ops = &vtty_port_ops;
591
592 vtty_port->pipe_tx_kcap = reg[0];
593 vtty_port->pipe_rx_kcap = reg[1];
594 vtty_port->max_msg_size = 32;
595
596 irq = platform_get_irq(pdev, 0);
597 if (irq < 0) {
598 dev_err(&pdev->dev, "no tx irq resource?\n");
599 err = -ENODEV;
600 goto fail_of;
601 }
602 vtty_port->tx_irq = irq;
603
604 irq = platform_get_irq(pdev, 1);
605 if (irq < 0) {
606 dev_err(&pdev->dev, "no rx irq resource?\n");
607 err = -ENODEV;
608 goto fail_of;
609 }
610 vtty_port->rx_irq = irq;
611
612 vtty_port->exists = true;
613
614 spin_lock_init(&vtty_port->write_lock);
615 INIT_WORK(&vtty_port->read_work, vtty_read_task);
616
617 vtty_port->read_buf = kmalloc(vtty_port->max_msg_size + sizeof(u32),
618 GFP_KERNEL);
619 if (!vtty_port->read_buf) {
620 dev_err(&pdev->dev, "%s: bad kmalloc\n", __func__);
621 err = -ENOMEM;
622 goto fail_malloc_read;
623 }
624 vtty_port->read_buf_pos = 0;
625 vtty_port->read_buf_len = 0;
626
627 vtty_port->write_buf = kmalloc(vtty_port->max_msg_size + sizeof(u32),
628 GFP_KERNEL);
629 if (!vtty_port->write_buf) {
630 dev_err(&pdev->dev, "%s: bad kmalloc\n", __func__);
631 err = -ENOMEM;
632 goto fail_malloc_write;
633 }
634
635 tty_dev = tty_register_device(vtty_driver, vtty_id, &pdev->dev);
636 if (IS_ERR(tty_dev)) {
637 dev_err(&pdev->dev, "%s: can't register "DEVICE_NAME"%d: %ld",
638 __func__, vtty_id, PTR_ERR(tty_dev));
639 err = PTR_ERR(tty_dev);
640 goto fail_tty_register;
641 }
642
643#ifdef CONFIG_OKL4_VTTY_CONSOLE
644 /* Set up and register the port's console device */
645 strlcpy(vtty_port->console.name, DEVICE_PREFIX,
646 sizeof(vtty_port->console.name));
647 vtty_port->console.write = vconsole_write;
648 vtty_port->console.flags = CON_PRINTBUFFER;
649 vtty_port->console.device = vconsole_device;
650 vtty_port->console.setup = vconsole_setup;
651 vtty_port->console.index = vtty_id;
652
653 register_console(&vtty_port->console);
654#endif
655
656 return 0;
657
658fail_tty_register:
659 kfree(vtty_port->write_buf);
660fail_malloc_write:
661 kfree(vtty_port->read_buf);
662 vtty_port->exists = false;
663fail_of:
664fail_vtty_id:
665fail_malloc_read:
666 dev_set_drvdata(&pdev->dev, NULL);
667 return err;
668}
669
670static int
671vtty_remove(struct platform_device *pdev)
672{
673 struct vtty_port *vtty_port = dev_get_drvdata(&pdev->dev);
674
675 if (!vtty_port->exists)
676 return -ENOENT;
677
678#ifdef CONFIG_OKL4_VTTY_CONSOLE
679 unregister_console(&vtty_port->console);
680#endif
681 tty_unregister_device(vtty_driver, vtty_port->vtty_id);
682 vtty_port->exists = false;
683 kfree(vtty_port->write_buf);
684 kfree(vtty_port->read_buf);
685
686 dev_set_drvdata(&pdev->dev, NULL);
687 devm_kfree(&pdev->dev, vtty_port);
688
689 return 0;
690}
691
692static const struct of_device_id vtty_match[] = {
693 {
694 .compatible = "okl,pipe-tty",
695 },
696 {},
697};
698MODULE_DEVICE_TABLE(of, vtty_match);
699
700static struct platform_driver driver = {
701 .driver = {
702 .name = DRIVER_NAME,
703 .owner = THIS_MODULE,
704 .of_match_table = vtty_match,
705 },
706 .probe = vtty_probe,
707 .remove = vtty_remove,
708};
709
710
711static int __init vtty_init(void)
712{
713 int err;
714
715 /* Allocate workqueue */
716 read_workqueue = create_workqueue("okl4vtty");
717 if (read_workqueue == NULL) {
718 err = -ENOMEM;
719 goto fail_create_workqueue;
720 }
721
722 /* Set up the tty driver. */
723 vtty_driver = alloc_tty_driver(MAX_VTTYS);
724 if (vtty_driver == NULL) {
725 err = -ENOMEM;
726 goto fail_alloc_tty_driver;
727 }
728
729 vtty_driver->owner = THIS_MODULE;
730 vtty_driver->driver_name = DRIVER_NAME;
731 vtty_driver->name = DEVICE_PREFIX;
732 vtty_driver->type = TTY_DRIVER_TYPE_SERIAL;
733 vtty_driver->subtype = SERIAL_TYPE_NORMAL;
734 vtty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
735 vtty_driver->init_termios = tty_std_termios;
736
737 /* These flags don't really matter; just use sensible defaults. */
738 vtty_driver->init_termios.c_cflag =
739 B9600 | CS8 | CREAD | HUPCL | CLOCAL;
740 vtty_driver->init_termios.c_ispeed = 9600;
741 vtty_driver->init_termios.c_ospeed = 9600;
742
743 tty_set_operations(vtty_driver, &vtty_ops);
744
745 err = tty_register_driver(vtty_driver);
746 if (err)
747 goto fail_tty_driver_register;
748
749 err = platform_driver_register(&driver);
750 if (err)
751 goto fail_mv_driver_register;
752
753 return 0;
754
755fail_mv_driver_register:
756 tty_unregister_driver(vtty_driver);
757fail_tty_driver_register:
758 put_tty_driver(vtty_driver);
759 vtty_driver = NULL;
760fail_alloc_tty_driver:
761 destroy_workqueue(read_workqueue);
762 read_workqueue = NULL;
763fail_create_workqueue:
764 return err;
765}
766
767static void __exit vtty_exit(void)
768{
769 platform_driver_unregister(&driver);
770
771 tty_unregister_driver(vtty_driver);
772 put_tty_driver(vtty_driver);
773 vtty_driver = NULL;
774 destroy_workqueue(read_workqueue);
775 read_workqueue = NULL;
776}
777
778module_init(vtty_init);
779module_exit(vtty_exit);
780
781#ifdef CONFIG_OKL4_VTTY_CONSOLE
782
783static u32 cycle_limit = 0;
784
785static int
786vconsole_setup(struct console *co, char *options)
787{
788 struct vtty_port *port;
789
790 if (co->index < 0 || co->index >= MAX_VTTYS)
791 co->index = 0;
792
793 port = &ports[co->index];
794 if (!port->exists)
795 return -ENODEV;
796
797 cycle_limit = arch_timer_get_rate() * 20 / MSEC_PER_SEC;
798 if (cycle_limit == 0) {
799 cycle_limit = -1;
800 }
801 return 0;
802}
803
804#ifdef CONFIG_OKL4_INTERLEAVED_PRIORITIES
805extern int vcpu_prio_normal;
806#endif
807
808static void
809vconsole_write(struct console *co, const char *p, unsigned count)
810{
811 struct vtty_port *port = &ports[co->index];
812 size_t bytes_remaining = count;
813 char buf[port->max_msg_size + sizeof(u32)];
814 cycles_t last_sent_start = get_cycles();
815 static int pipe_full = 0;
816
817 memset(buf, 0, sizeof(buf));
818
819 while (bytes_remaining > 0) {
820 unsigned to_send = min(port->max_msg_size, bytes_remaining);
821 unsigned send = roundup(to_send + sizeof(u32), sizeof(u32));
822 okl4_error_t ret;
823
824 *(u32 *)buf = to_send;
825 memcpy(&buf[sizeof(u32)], p, to_send);
826
827 ret = _okl4_sys_pipe_send(port->pipe_tx_kcap, send,
828 (void *)buf);
829
830 if (ret == OKL4_ERROR_PIPE_NOT_READY) {
831 okl4_pipe_control_t x = 0;
832
833 okl4_pipe_control_setdoop(&x, true);
834 okl4_pipe_control_setoperation(&x,
835 OKL4_PIPE_CONTROL_OP_SET_TX_READY);
836 _okl4_sys_pipe_control(port->pipe_tx_kcap, x);
837 continue;
838 }
839
840 if (ret == OKL4_ERROR_PIPE_FULL) {
841 cycles_t last_sent_cycles = get_cycles() -
842 last_sent_start;
843 if (last_sent_cycles > cycle_limit || pipe_full) {
844 pipe_full = 1;
845 return;
846 }
847#ifdef CONFIG_OKL4_INTERLEAVED_PRIORITIES
848 _okl4_sys_priority_waive(vcpu_prio_normal);
849#else
850 _okl4_sys_priority_waive(0);
851#endif
852 continue;
853 }
854
855 if (ret != OKL4_OK) {
856 /*
857 * We cannot call printk here since that will end up
858 * calling back here and make things worse. We just
859 * have to return and hope that the problem corrects
860 * itself.
861 */
862 return;
863 }
864
865 p += to_send;
866 bytes_remaining -= to_send;
867 last_sent_start = get_cycles();
868 pipe_full = 0;
869 }
870}
871
872struct tty_driver *
873vconsole_device(struct console *co, int *index)
874{
875 *index = co->index;
876 return vtty_driver;
877}
878
879#endif /* CONFIG_OKL4_VTTY_CONSOLE */
880
881MODULE_DESCRIPTION("OKL4 virtual TTY driver");
882MODULE_AUTHOR("Philip Derrin <philipd@ok-labs.com>");