in virtio_console.c [1562:1697]
static void handle_control_message(struct virtio_device *vdev,
struct ports_device *portdev,
struct port_buffer *buf)
{
struct virtio_console_control *cpkt;
struct port *port;
size_t name_size;
int err;
cpkt = (struct virtio_console_control *)(buf->buf + buf->offset);
port = find_port_by_id(portdev, virtio32_to_cpu(vdev, cpkt->id));
if (!port &&
cpkt->event != cpu_to_virtio16(vdev, VIRTIO_CONSOLE_PORT_ADD)) {
/* No valid header at start of buffer. Drop it. */
dev_dbg(&portdev->vdev->dev,
"Invalid index %u in control packet\n", cpkt->id);
return;
}
switch (virtio16_to_cpu(vdev, cpkt->event)) {
case VIRTIO_CONSOLE_PORT_ADD:
if (port) {
dev_dbg(&portdev->vdev->dev,
"Port %u already added\n", port->id);
send_control_msg(port, VIRTIO_CONSOLE_PORT_READY, 1);
break;
}
if (virtio32_to_cpu(vdev, cpkt->id) >=
portdev->max_nr_ports) {
dev_warn(&portdev->vdev->dev,
"Request for adding port with "
"out-of-bound id %u, max. supported id: %u\n",
cpkt->id, portdev->max_nr_ports - 1);
break;
}
add_port(portdev, virtio32_to_cpu(vdev, cpkt->id));
break;
case VIRTIO_CONSOLE_PORT_REMOVE:
unplug_port(port);
break;
case VIRTIO_CONSOLE_CONSOLE_PORT:
if (!cpkt->value)
break;
if (is_console_port(port))
break;
init_port_console(port);
complete(&early_console_added);
/*
* Could remove the port here in case init fails - but
* have to notify the host first.
*/
break;
case VIRTIO_CONSOLE_RESIZE: {
struct {
__u16 rows;
__u16 cols;
} size;
if (!is_console_port(port))
break;
memcpy(&size, buf->buf + buf->offset + sizeof(*cpkt),
sizeof(size));
set_console_size(port, size.rows, size.cols);
port->cons.hvc->irq_requested = 1;
resize_console(port);
break;
}
case VIRTIO_CONSOLE_PORT_OPEN:
port->host_connected = virtio16_to_cpu(vdev, cpkt->value);
wake_up_interruptible(&port->waitqueue);
/*
* If the host port got closed and the host had any
* unconsumed buffers, we'll be able to reclaim them
* now.
*/
spin_lock_irq(&port->outvq_lock);
reclaim_consumed_buffers(port);
spin_unlock_irq(&port->outvq_lock);
/*
* If the guest is connected, it'll be interested in
* knowing the host connection state changed.
*/
spin_lock_irq(&port->inbuf_lock);
send_sigio_to_port(port);
spin_unlock_irq(&port->inbuf_lock);
break;
case VIRTIO_CONSOLE_PORT_NAME:
/*
* If we woke up after hibernation, we can get this
* again. Skip it in that case.
*/
if (port->name)
break;
/*
* Skip the size of the header and the cpkt to get the size
* of the name that was sent
*/
name_size = buf->len - buf->offset - sizeof(*cpkt) + 1;
port->name = kmalloc(name_size, GFP_KERNEL);
if (!port->name) {
dev_err(port->dev,
"Not enough space to store port name\n");
break;
}
strncpy(port->name, buf->buf + buf->offset + sizeof(*cpkt),
name_size - 1);
port->name[name_size - 1] = 0;
/*
* Since we only have one sysfs attribute, 'name',
* create it only if we have a name for the port.
*/
err = sysfs_create_group(&port->dev->kobj,
&port_attribute_group);
if (err) {
dev_err(port->dev,
"Error %d creating sysfs device attributes\n",
err);
} else {
/*
* Generate a udev event so that appropriate
* symlinks can be created based on udev
* rules.
*/
kobject_uevent(&port->dev->kobj, KOBJ_CHANGE);
}
break;
}
}