diff options
Diffstat (limited to 'drivers/char/virtio_console.c')
-rw-r--r-- | drivers/char/virtio_console.c | 81 |
1 files changed, 61 insertions, 20 deletions
diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index f404ccfc9c2..196428c2287 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c | |||
@@ -25,6 +25,7 @@ | |||
25 | #include <linux/list.h> | 25 | #include <linux/list.h> |
26 | #include <linux/poll.h> | 26 | #include <linux/poll.h> |
27 | #include <linux/sched.h> | 27 | #include <linux/sched.h> |
28 | #include <linux/slab.h> | ||
28 | #include <linux/spinlock.h> | 29 | #include <linux/spinlock.h> |
29 | #include <linux/virtio.h> | 30 | #include <linux/virtio.h> |
30 | #include <linux/virtio_console.h> | 31 | #include <linux/virtio_console.h> |
@@ -32,6 +33,35 @@ | |||
32 | #include <linux/workqueue.h> | 33 | #include <linux/workqueue.h> |
33 | #include "hvc_console.h" | 34 | #include "hvc_console.h" |
34 | 35 | ||
36 | /* Moved here from .h file in order to disable MULTIPORT. */ | ||
37 | #define VIRTIO_CONSOLE_F_MULTIPORT 1 /* Does host provide multiple ports? */ | ||
38 | |||
39 | struct virtio_console_multiport_conf { | ||
40 | struct virtio_console_config config; | ||
41 | /* max. number of ports this device can hold */ | ||
42 | __u32 max_nr_ports; | ||
43 | /* number of ports added so far */ | ||
44 | __u32 nr_ports; | ||
45 | } __attribute__((packed)); | ||
46 | |||
47 | /* | ||
48 | * A message that's passed between the Host and the Guest for a | ||
49 | * particular port. | ||
50 | */ | ||
51 | struct virtio_console_control { | ||
52 | __u32 id; /* Port number */ | ||
53 | __u16 event; /* The kind of control event (see below) */ | ||
54 | __u16 value; /* Extra information for the key */ | ||
55 | }; | ||
56 | |||
57 | /* Some events for control messages */ | ||
58 | #define VIRTIO_CONSOLE_PORT_READY 0 | ||
59 | #define VIRTIO_CONSOLE_CONSOLE_PORT 1 | ||
60 | #define VIRTIO_CONSOLE_RESIZE 2 | ||
61 | #define VIRTIO_CONSOLE_PORT_OPEN 3 | ||
62 | #define VIRTIO_CONSOLE_PORT_NAME 4 | ||
63 | #define VIRTIO_CONSOLE_PORT_REMOVE 5 | ||
64 | |||
35 | /* | 65 | /* |
36 | * This is a global struct for storing common data for all the devices | 66 | * This is a global struct for storing common data for all the devices |
37 | * this driver handles. | 67 | * this driver handles. |
@@ -120,7 +150,7 @@ struct ports_device { | |||
120 | spinlock_t cvq_lock; | 150 | spinlock_t cvq_lock; |
121 | 151 | ||
122 | /* The current config space is stored here */ | 152 | /* The current config space is stored here */ |
123 | struct virtio_console_config config; | 153 | struct virtio_console_multiport_conf config; |
124 | 154 | ||
125 | /* The virtio device we're associated with */ | 155 | /* The virtio device we're associated with */ |
126 | struct virtio_device *vdev; | 156 | struct virtio_device *vdev; |
@@ -415,20 +445,16 @@ static ssize_t send_buf(struct port *port, void *in_buf, size_t in_count) | |||
415 | out_vq->vq_ops->kick(out_vq); | 445 | out_vq->vq_ops->kick(out_vq); |
416 | 446 | ||
417 | if (ret < 0) { | 447 | if (ret < 0) { |
418 | len = 0; | 448 | in_count = 0; |
419 | goto fail; | 449 | goto fail; |
420 | } | 450 | } |
421 | 451 | ||
422 | /* | 452 | /* Wait till the host acknowledges it pushed out the data we sent. */ |
423 | * Wait till the host acknowledges it pushed out the data we | ||
424 | * sent. Also ensure we return to userspace the number of | ||
425 | * bytes that were successfully consumed by the host. | ||
426 | */ | ||
427 | while (!out_vq->vq_ops->get_buf(out_vq, &len)) | 453 | while (!out_vq->vq_ops->get_buf(out_vq, &len)) |
428 | cpu_relax(); | 454 | cpu_relax(); |
429 | fail: | 455 | fail: |
430 | /* We're expected to return the amount of data we wrote */ | 456 | /* We're expected to return the amount of data we wrote */ |
431 | return len; | 457 | return in_count; |
432 | } | 458 | } |
433 | 459 | ||
434 | /* | 460 | /* |
@@ -645,13 +671,13 @@ static int put_chars(u32 vtermno, const char *buf, int count) | |||
645 | { | 671 | { |
646 | struct port *port; | 672 | struct port *port; |
647 | 673 | ||
674 | if (unlikely(early_put_chars)) | ||
675 | return early_put_chars(vtermno, buf, count); | ||
676 | |||
648 | port = find_port_by_vtermno(vtermno); | 677 | port = find_port_by_vtermno(vtermno); |
649 | if (!port) | 678 | if (!port) |
650 | return 0; | 679 | return 0; |
651 | 680 | ||
652 | if (unlikely(early_put_chars)) | ||
653 | return early_put_chars(vtermno, buf, count); | ||
654 | |||
655 | return send_buf(port, (void *)buf, count); | 681 | return send_buf(port, (void *)buf, count); |
656 | } | 682 | } |
657 | 683 | ||
@@ -681,6 +707,10 @@ static void resize_console(struct port *port) | |||
681 | struct virtio_device *vdev; | 707 | struct virtio_device *vdev; |
682 | struct winsize ws; | 708 | struct winsize ws; |
683 | 709 | ||
710 | /* The port could have been hot-unplugged */ | ||
711 | if (!port) | ||
712 | return; | ||
713 | |||
684 | vdev = port->portdev->vdev; | 714 | vdev = port->portdev->vdev; |
685 | if (virtio_has_feature(vdev, VIRTIO_CONSOLE_F_SIZE)) { | 715 | if (virtio_has_feature(vdev, VIRTIO_CONSOLE_F_SIZE)) { |
686 | vdev->config->get(vdev, | 716 | vdev->config->get(vdev, |
@@ -947,11 +977,18 @@ static void handle_control_message(struct ports_device *portdev, | |||
947 | */ | 977 | */ |
948 | err = sysfs_create_group(&port->dev->kobj, | 978 | err = sysfs_create_group(&port->dev->kobj, |
949 | &port_attribute_group); | 979 | &port_attribute_group); |
950 | if (err) | 980 | if (err) { |
951 | dev_err(port->dev, | 981 | dev_err(port->dev, |
952 | "Error %d creating sysfs device attributes\n", | 982 | "Error %d creating sysfs device attributes\n", |
953 | err); | 983 | err); |
954 | 984 | } else { | |
985 | /* | ||
986 | * Generate a udev event so that appropriate | ||
987 | * symlinks can be created based on udev | ||
988 | * rules. | ||
989 | */ | ||
990 | kobject_uevent(&port->dev->kobj, KOBJ_CHANGE); | ||
991 | } | ||
955 | break; | 992 | break; |
956 | case VIRTIO_CONSOLE_PORT_REMOVE: | 993 | case VIRTIO_CONSOLE_PORT_REMOVE: |
957 | /* | 994 | /* |
@@ -1206,7 +1243,7 @@ fail: | |||
1206 | */ | 1243 | */ |
1207 | static void config_work_handler(struct work_struct *work) | 1244 | static void config_work_handler(struct work_struct *work) |
1208 | { | 1245 | { |
1209 | struct virtio_console_config virtconconf; | 1246 | struct virtio_console_multiport_conf virtconconf; |
1210 | struct ports_device *portdev; | 1247 | struct ports_device *portdev; |
1211 | struct virtio_device *vdev; | 1248 | struct virtio_device *vdev; |
1212 | int err; | 1249 | int err; |
@@ -1215,7 +1252,8 @@ static void config_work_handler(struct work_struct *work) | |||
1215 | 1252 | ||
1216 | vdev = portdev->vdev; | 1253 | vdev = portdev->vdev; |
1217 | vdev->config->get(vdev, | 1254 | vdev->config->get(vdev, |
1218 | offsetof(struct virtio_console_config, nr_ports), | 1255 | offsetof(struct virtio_console_multiport_conf, |
1256 | nr_ports), | ||
1219 | &virtconconf.nr_ports, | 1257 | &virtconconf.nr_ports, |
1220 | sizeof(virtconconf.nr_ports)); | 1258 | sizeof(virtconconf.nr_ports)); |
1221 | 1259 | ||
@@ -1407,16 +1445,19 @@ static int __devinit virtcons_probe(struct virtio_device *vdev) | |||
1407 | multiport = false; | 1445 | multiport = false; |
1408 | portdev->config.nr_ports = 1; | 1446 | portdev->config.nr_ports = 1; |
1409 | portdev->config.max_nr_ports = 1; | 1447 | portdev->config.max_nr_ports = 1; |
1448 | #if 0 /* Multiport is not quite ready yet --RR */ | ||
1410 | if (virtio_has_feature(vdev, VIRTIO_CONSOLE_F_MULTIPORT)) { | 1449 | if (virtio_has_feature(vdev, VIRTIO_CONSOLE_F_MULTIPORT)) { |
1411 | multiport = true; | 1450 | multiport = true; |
1412 | vdev->features[0] |= 1 << VIRTIO_CONSOLE_F_MULTIPORT; | 1451 | vdev->features[0] |= 1 << VIRTIO_CONSOLE_F_MULTIPORT; |
1413 | 1452 | ||
1414 | vdev->config->get(vdev, offsetof(struct virtio_console_config, | 1453 | vdev->config->get(vdev, |
1415 | nr_ports), | 1454 | offsetof(struct virtio_console_multiport_conf, |
1455 | nr_ports), | ||
1416 | &portdev->config.nr_ports, | 1456 | &portdev->config.nr_ports, |
1417 | sizeof(portdev->config.nr_ports)); | 1457 | sizeof(portdev->config.nr_ports)); |
1418 | vdev->config->get(vdev, offsetof(struct virtio_console_config, | 1458 | vdev->config->get(vdev, |
1419 | max_nr_ports), | 1459 | offsetof(struct virtio_console_multiport_conf, |
1460 | max_nr_ports), | ||
1420 | &portdev->config.max_nr_ports, | 1461 | &portdev->config.max_nr_ports, |
1421 | sizeof(portdev->config.max_nr_ports)); | 1462 | sizeof(portdev->config.max_nr_ports)); |
1422 | if (portdev->config.nr_ports > portdev->config.max_nr_ports) { | 1463 | if (portdev->config.nr_ports > portdev->config.max_nr_ports) { |
@@ -1432,6 +1473,7 @@ static int __devinit virtcons_probe(struct virtio_device *vdev) | |||
1432 | 1473 | ||
1433 | /* Let the Host know we support multiple ports.*/ | 1474 | /* Let the Host know we support multiple ports.*/ |
1434 | vdev->config->finalize_features(vdev); | 1475 | vdev->config->finalize_features(vdev); |
1476 | #endif | ||
1435 | 1477 | ||
1436 | err = init_vqs(portdev); | 1478 | err = init_vqs(portdev); |
1437 | if (err < 0) { | 1479 | if (err < 0) { |
@@ -1514,7 +1556,6 @@ static struct virtio_device_id id_table[] = { | |||
1514 | 1556 | ||
1515 | static unsigned int features[] = { | 1557 | static unsigned int features[] = { |
1516 | VIRTIO_CONSOLE_F_SIZE, | 1558 | VIRTIO_CONSOLE_F_SIZE, |
1517 | VIRTIO_CONSOLE_F_MULTIPORT, | ||
1518 | }; | 1559 | }; |
1519 | 1560 | ||
1520 | static struct virtio_driver virtio_console = { | 1561 | static struct virtio_driver virtio_console = { |