diff options
author | Amit Shah <amit.shah@redhat.com> | 2009-12-21 11:19:30 -0500 |
---|---|---|
committer | Rusty Russell <rusty@rustcorp.com.au> | 2010-02-23 22:52:54 -0500 |
commit | 2030fa496d74b49220308eaccf656e2338019cfd (patch) | |
tree | cf8d937551d8bfefadbbc5389caaf587f2d0b13b /drivers/char/virtio_console.c | |
parent | fb08bd274df61967f40d49c4625fe6ed75a69ab5 (diff) |
virtio: console: Add file operations to ports for open/read/write/poll
Allow guest userspace applications to open, read from, write to, poll
the ports via the char dev interface.
When a port gets opened, a notification is sent to the host via a
control message indicating a connection has been established. Similarly,
on closing of the port, a notification is sent indicating disconnection.
Signed-off-by: Amit Shah <amit.shah@redhat.com>
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Diffstat (limited to 'drivers/char/virtio_console.c')
-rw-r--r-- | drivers/char/virtio_console.c | 164 |
1 files changed, 163 insertions, 1 deletions
diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index 64ef476d6557..ece1546fbb20 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c | |||
@@ -19,11 +19,15 @@ | |||
19 | #include <linux/cdev.h> | 19 | #include <linux/cdev.h> |
20 | #include <linux/device.h> | 20 | #include <linux/device.h> |
21 | #include <linux/err.h> | 21 | #include <linux/err.h> |
22 | #include <linux/fs.h> | ||
22 | #include <linux/init.h> | 23 | #include <linux/init.h> |
23 | #include <linux/list.h> | 24 | #include <linux/list.h> |
25 | #include <linux/poll.h> | ||
26 | #include <linux/sched.h> | ||
24 | #include <linux/spinlock.h> | 27 | #include <linux/spinlock.h> |
25 | #include <linux/virtio.h> | 28 | #include <linux/virtio.h> |
26 | #include <linux/virtio_console.h> | 29 | #include <linux/virtio_console.h> |
30 | #include <linux/wait.h> | ||
27 | #include <linux/workqueue.h> | 31 | #include <linux/workqueue.h> |
28 | #include "hvc_console.h" | 32 | #include "hvc_console.h" |
29 | 33 | ||
@@ -163,8 +167,14 @@ struct port { | |||
163 | struct cdev cdev; | 167 | struct cdev cdev; |
164 | struct device *dev; | 168 | struct device *dev; |
165 | 169 | ||
170 | /* A waitqueue for poll() or blocking read operations */ | ||
171 | wait_queue_head_t waitqueue; | ||
172 | |||
166 | /* The 'id' to identify the port with the Host */ | 173 | /* The 'id' to identify the port with the Host */ |
167 | u32 id; | 174 | u32 id; |
175 | |||
176 | /* Is the host device open */ | ||
177 | bool host_connected; | ||
168 | }; | 178 | }; |
169 | 179 | ||
170 | /* This is the very early arch-specified put chars function. */ | 180 | /* This is the very early arch-specified put chars function. */ |
@@ -417,6 +427,146 @@ static ssize_t fill_readbuf(struct port *port, char *out_buf, size_t out_count, | |||
417 | return out_count; | 427 | return out_count; |
418 | } | 428 | } |
419 | 429 | ||
430 | /* The condition that must be true for polling to end */ | ||
431 | static bool wait_is_over(struct port *port) | ||
432 | { | ||
433 | return port_has_data(port) || !port->host_connected; | ||
434 | } | ||
435 | |||
436 | static ssize_t port_fops_read(struct file *filp, char __user *ubuf, | ||
437 | size_t count, loff_t *offp) | ||
438 | { | ||
439 | struct port *port; | ||
440 | ssize_t ret; | ||
441 | |||
442 | port = filp->private_data; | ||
443 | |||
444 | if (!port_has_data(port)) { | ||
445 | /* | ||
446 | * If nothing's connected on the host just return 0 in | ||
447 | * case of list_empty; this tells the userspace app | ||
448 | * that there's no connection | ||
449 | */ | ||
450 | if (!port->host_connected) | ||
451 | return 0; | ||
452 | if (filp->f_flags & O_NONBLOCK) | ||
453 | return -EAGAIN; | ||
454 | |||
455 | ret = wait_event_interruptible(port->waitqueue, | ||
456 | wait_is_over(port)); | ||
457 | if (ret < 0) | ||
458 | return ret; | ||
459 | } | ||
460 | /* | ||
461 | * We could've received a disconnection message while we were | ||
462 | * waiting for more data. | ||
463 | * | ||
464 | * This check is not clubbed in the if() statement above as we | ||
465 | * might receive some data as well as the host could get | ||
466 | * disconnected after we got woken up from our wait. So we | ||
467 | * really want to give off whatever data we have and only then | ||
468 | * check for host_connected. | ||
469 | */ | ||
470 | if (!port_has_data(port) && !port->host_connected) | ||
471 | return 0; | ||
472 | |||
473 | return fill_readbuf(port, ubuf, count, true); | ||
474 | } | ||
475 | |||
476 | static ssize_t port_fops_write(struct file *filp, const char __user *ubuf, | ||
477 | size_t count, loff_t *offp) | ||
478 | { | ||
479 | struct port *port; | ||
480 | char *buf; | ||
481 | ssize_t ret; | ||
482 | |||
483 | port = filp->private_data; | ||
484 | |||
485 | count = min((size_t)(32 * 1024), count); | ||
486 | |||
487 | buf = kmalloc(count, GFP_KERNEL); | ||
488 | if (!buf) | ||
489 | return -ENOMEM; | ||
490 | |||
491 | ret = copy_from_user(buf, ubuf, count); | ||
492 | if (ret) { | ||
493 | ret = -EFAULT; | ||
494 | goto free_buf; | ||
495 | } | ||
496 | |||
497 | ret = send_buf(port, buf, count); | ||
498 | free_buf: | ||
499 | kfree(buf); | ||
500 | return ret; | ||
501 | } | ||
502 | |||
503 | static unsigned int port_fops_poll(struct file *filp, poll_table *wait) | ||
504 | { | ||
505 | struct port *port; | ||
506 | unsigned int ret; | ||
507 | |||
508 | port = filp->private_data; | ||
509 | poll_wait(filp, &port->waitqueue, wait); | ||
510 | |||
511 | ret = 0; | ||
512 | if (port->inbuf) | ||
513 | ret |= POLLIN | POLLRDNORM; | ||
514 | if (port->host_connected) | ||
515 | ret |= POLLOUT; | ||
516 | if (!port->host_connected) | ||
517 | ret |= POLLHUP; | ||
518 | |||
519 | return ret; | ||
520 | } | ||
521 | |||
522 | static int port_fops_release(struct inode *inode, struct file *filp) | ||
523 | { | ||
524 | struct port *port; | ||
525 | |||
526 | port = filp->private_data; | ||
527 | |||
528 | /* Notify host of port being closed */ | ||
529 | send_control_msg(port, VIRTIO_CONSOLE_PORT_OPEN, 0); | ||
530 | |||
531 | return 0; | ||
532 | } | ||
533 | |||
534 | static int port_fops_open(struct inode *inode, struct file *filp) | ||
535 | { | ||
536 | struct cdev *cdev = inode->i_cdev; | ||
537 | struct port *port; | ||
538 | |||
539 | port = container_of(cdev, struct port, cdev); | ||
540 | filp->private_data = port; | ||
541 | |||
542 | /* | ||
543 | * Don't allow opening of console port devices -- that's done | ||
544 | * via /dev/hvc | ||
545 | */ | ||
546 | if (is_console_port(port)) | ||
547 | return -ENXIO; | ||
548 | |||
549 | /* Notify host of port being opened */ | ||
550 | send_control_msg(filp->private_data, VIRTIO_CONSOLE_PORT_OPEN, 1); | ||
551 | |||
552 | return 0; | ||
553 | } | ||
554 | |||
555 | /* | ||
556 | * The file operations that we support: programs in the guest can open | ||
557 | * a console device, read from it, write to it, poll for data and | ||
558 | * close it. The devices are at | ||
559 | * /dev/vport<device number>p<port number> | ||
560 | */ | ||
561 | static const struct file_operations port_fops = { | ||
562 | .owner = THIS_MODULE, | ||
563 | .open = port_fops_open, | ||
564 | .read = port_fops_read, | ||
565 | .write = port_fops_write, | ||
566 | .poll = port_fops_poll, | ||
567 | .release = port_fops_release, | ||
568 | }; | ||
569 | |||
420 | /* | 570 | /* |
421 | * The put_chars() callback is pretty straightforward. | 571 | * The put_chars() callback is pretty straightforward. |
422 | * | 572 | * |
@@ -560,6 +710,9 @@ int init_port_console(struct port *port) | |||
560 | list_add_tail(&port->cons.list, &pdrvdata.consoles); | 710 | list_add_tail(&port->cons.list, &pdrvdata.consoles); |
561 | spin_unlock_irq(&pdrvdata_lock); | 711 | spin_unlock_irq(&pdrvdata_lock); |
562 | 712 | ||
713 | /* Notify host of port being opened */ | ||
714 | send_control_msg(port, VIRTIO_CONSOLE_PORT_OPEN, 1); | ||
715 | |||
563 | return 0; | 716 | return 0; |
564 | } | 717 | } |
565 | 718 | ||
@@ -599,6 +752,10 @@ static void handle_control_message(struct ports_device *portdev, | |||
599 | port->cons.hvc->irq_requested = 1; | 752 | port->cons.hvc->irq_requested = 1; |
600 | resize_console(port); | 753 | resize_console(port); |
601 | break; | 754 | break; |
755 | case VIRTIO_CONSOLE_PORT_OPEN: | ||
756 | port->host_connected = cpkt->value; | ||
757 | wake_up_interruptible(&port->waitqueue); | ||
758 | break; | ||
602 | } | 759 | } |
603 | } | 760 | } |
604 | 761 | ||
@@ -645,6 +802,8 @@ static void in_intr(struct virtqueue *vq) | |||
645 | 802 | ||
646 | spin_unlock_irqrestore(&port->inbuf_lock, flags); | 803 | spin_unlock_irqrestore(&port->inbuf_lock, flags); |
647 | 804 | ||
805 | wake_up_interruptible(&port->waitqueue); | ||
806 | |||
648 | if (is_console_port(port) && hvc_poll(port->cons.hvc)) | 807 | if (is_console_port(port) && hvc_poll(port->cons.hvc)) |
649 | hvc_kick(); | 808 | hvc_kick(); |
650 | } | 809 | } |
@@ -697,10 +856,12 @@ static int add_port(struct ports_device *portdev, u32 id) | |||
697 | port->inbuf = NULL; | 856 | port->inbuf = NULL; |
698 | port->cons.hvc = NULL; | 857 | port->cons.hvc = NULL; |
699 | 858 | ||
859 | port->host_connected = false; | ||
860 | |||
700 | port->in_vq = portdev->in_vqs[port->id]; | 861 | port->in_vq = portdev->in_vqs[port->id]; |
701 | port->out_vq = portdev->out_vqs[port->id]; | 862 | port->out_vq = portdev->out_vqs[port->id]; |
702 | 863 | ||
703 | cdev_init(&port->cdev, NULL); | 864 | cdev_init(&port->cdev, &port_fops); |
704 | 865 | ||
705 | devt = MKDEV(portdev->chr_major, id); | 866 | devt = MKDEV(portdev->chr_major, id); |
706 | err = cdev_add(&port->cdev, devt, 1); | 867 | err = cdev_add(&port->cdev, devt, 1); |
@@ -721,6 +882,7 @@ static int add_port(struct ports_device *portdev, u32 id) | |||
721 | } | 882 | } |
722 | 883 | ||
723 | spin_lock_init(&port->inbuf_lock); | 884 | spin_lock_init(&port->inbuf_lock); |
885 | init_waitqueue_head(&port->waitqueue); | ||
724 | 886 | ||
725 | inbuf = alloc_buf(PAGE_SIZE); | 887 | inbuf = alloc_buf(PAGE_SIZE); |
726 | if (!inbuf) { | 888 | if (!inbuf) { |