diff options
| -rw-r--r-- | drivers/char/virtio_console.c | 164 | ||||
| -rw-r--r-- | include/linux/virtio_console.h | 1 |
2 files changed, 164 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) { |
diff --git a/include/linux/virtio_console.h b/include/linux/virtio_console.h index f4d183b5b493..bd0e2a596f93 100644 --- a/include/linux/virtio_console.h +++ b/include/linux/virtio_console.h | |||
| @@ -39,6 +39,7 @@ struct virtio_console_control { | |||
| 39 | #define VIRTIO_CONSOLE_PORT_READY 0 | 39 | #define VIRTIO_CONSOLE_PORT_READY 0 |
| 40 | #define VIRTIO_CONSOLE_CONSOLE_PORT 1 | 40 | #define VIRTIO_CONSOLE_CONSOLE_PORT 1 |
| 41 | #define VIRTIO_CONSOLE_RESIZE 2 | 41 | #define VIRTIO_CONSOLE_RESIZE 2 |
| 42 | #define VIRTIO_CONSOLE_PORT_OPEN 3 | ||
| 42 | 43 | ||
| 43 | #ifdef __KERNEL__ | 44 | #ifdef __KERNEL__ |
| 44 | int __init virtio_cons_early_init(int (*put_chars)(u32, const char *, int)); | 45 | int __init virtio_cons_early_init(int (*put_chars)(u32, const char *, int)); |
