virtio: add a virtio-console to the vbus backend
PATCH 1/1 virtio: add a virtio-console to the vbus backend
Hi Michael,
FYI: This was a patch I started to put together around the time vbus-v3 was released to demonstrate the interface for virtio devices that we needed. I just wanted to get it out to you so you have a working example to look at what it takes to write something like virtio-net against virtio-vbus.
What I will do next is make a minor v3.1 release of vbus on top of kvm.git and get this patch cleaned up so we can actually run it. Note that v3.1 will just a minor port of v3 to kvm.git/HEAD, not the new iosignalfd/irqfd stuff that I have queued for v4. Thats not ready for prime-time yet.
----------------------------
virtio: add a virtio-console to the vbus backend
A console probably doesnt need to live in the kernel since it is generally not performance limited. However, its also a very simple example, so lets make a vbus backend to demonstrate the virtio interfaces.
---
drivers/vbus/devices/Kconfig | 9 drivers/vbus/devices/Makefile | 1 drivers/vbus/devices/virtio-console-backend.c | 461 +++++++++++++++++++++++++ 3 files changed, 471 insertions(+), 0 deletions(-) create mode 100644 drivers/vbus/devices/virtio-console-backend.c
drivers/vbus/devices/Kconfig +9 -0
@@ -15,3 +15,12 @@ config VBUS_VENETTAP If unsure, say N +config VBUS_VIRTIO_CONSOLE + tristate "Virtual-Bus Backend for virtio-console drivers" + depends on VBUS_DEVICES + select VBUS_VIRTIO_BACKEND + default n + help + Provides a virtual console device within the vbus infrastructure + + If unsure, say N
drivers/vbus/devices/Makefile +1 -0
@@ -1 +1,2 @@ obj-$(CONFIG_VBUS_VENETTAP) += venet-tap.o +obj-$(CONFIG_VBUS_VIRTIO_CONSOLE) += virtio-console-backend.o
drivers/vbus/devices/virtio-console-backend.c new file +461 -0
461 lines not on this page · raw message
Quoted 5 lines · Gregory Haskins
Hi Michael,
FYI: This was a patch I started to put together around the time vbus-v3 was released to demonstrate the interface for virtio devices that we needed. I just wanted to get it out to you so you have a working example to look at what it takes to write something like virtio-net against virtio-vbus.
Thanks, Gregory,
I'll take a look.
Quoted 513 lines
What I will do next is make a minor v3.1 release of vbus on top of kvm.git and get this patch cleaned up so we can actually run it. Note that v3.1 will just a minor port of v3 to kvm.git/HEAD, not the new iosignalfd/irqfd stuff that I have queued for v4. Thats not ready for prime-time yet.
----------------------------
virtio: add a virtio-console to the vbus backend
A console probably doesnt need to live in the kernel since it is generally not performance limited. However, its also a very simple example, so lets make a vbus backend to demonstrate the virtio interfaces.
Signed-off-by Gregory Haskins <[email protected]>---
drivers/vbus/devices/Kconfig | 9 drivers/vbus/devices/Makefile | 1 drivers/vbus/devices/virtio-console-backend.c | 461 +++++++++++++++++++++++++ 3 files changed, 471 insertions(+), 0 deletions(-) create mode 100644 drivers/vbus/devices/virtio-console-backend.cdrivers/vbus/devices/Kconfig +9
-0@@ -15,3 +15,12 @@ config VBUS_VENETTAP If unsure, say N +config VBUS_VIRTIO_CONSOLE + tristate "Virtual-Bus Backend for virtio-console drivers" + depends on VBUS_DEVICES + select VBUS_VIRTIO_BACKEND + default n + help + Provides a virtual console device within the vbus infrastructure + + If unsure, say Ndrivers/vbus/devices/Makefile +1
-0@@ -1 +1,2 @@ obj-$(CONFIG_VBUS_VENETTAP) += venet-tap.o +obj-$(CONFIG_VBUS_VIRTIO_CONSOLE) += virtio-console-backend.odrivers/vbus/devices/virtio-console-backend.c new file +461
-0@@ -0,0 +1,461 @@ +/* +* virtio-console for the vbus backend +* +* Copyright (C) 2009 Novell, Gregory Haskins <[email protected]> +* +* This file is free software; you can redistribute it and/or modify +* it under the terms of version 2 of the GNU General Public License +* as published by the Free Software Foundation. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/moduleparam.h> + +#include <linux/cdev.h> +#include <linux/fs.h> +#include <linux/major.h> + +#include <linux/vbus_device.h> +#include <linux/virtio_vbus.h> +#include <linux/virtio_console.h> + +MODULE_AUTHOR ("Gregory Haskins" ); +MODULE_LICENSE ("GPL" ); + +#undef PDEBUG/* undef it, just in case */ +#ifdef VIRTIO_CONSOLE_DEBUG +# define PDEBUG(fmt, args...) printk(KERN_DEBUG "virtio-console: " fmt, ## args) +#else +# define PDEBUG(fmt, args...) /* not debugging: nothing */ +#endif + +struct _virtio_console { +spinlock_t lock; +char *name; +dev_t devno; +struct cdev cdev; +struct vbus_device dev; +struct virtio_device_interface intf; +struct virtio_connection conn; +struct vbus_memctx *ctx; +struct virtqueue *rx; +struct virtqueue *tx; +int enabled:1 ; +int connected:1 ; +int opened:1 ; +int tty:1 ; +int link:1 ; +}; + +static struct _virtio_console * +conn_to_priv (struct virtio_connection *conn) +{ + return container_of(conn, struct _virtio_console, conn); +} + +static struct _virtio_console * +intf_to_priv (struct virtio_device_interface *intf) +{ + return container_of(intf, struct _virtio_console, intf); +} + +static struct _virtio_console * +vdev_to_priv (struct vbus_device *vdev) +{ + return container_of(vdev, struct _virtio_console, dev); +} + +/* +* -------------------------------------------- +* Driver connection +* -------------------------------------------- +*/ + +static void +_virtio_console_add_vq (struct virtio_connection *vconn,int index, +struct virtqueue *vq) +{ +struct _virtio_console *priv =conn_to_priv (vconn); + +switch (index) { +case 0 : +if (!priv->rx) + priv->rx = vq; +break ; +case 1 : +if (!priv->tx) + priv->tx = vq; +break ; +default : +break ; + } +} + +static void +_virtio_console_del_vq (struct virtio_connection *vconn,int index) +{ +struct _virtio_console *priv =conn_to_priv (vconn); + +switch (index) { +case 0 : + priv->rx =NULL ; +break ; +case 1 : + priv->tx =NULL ; +break ; +default : +break ; + } +} + +static void +_virtio_console_notify_vq (struct virtio_connection *vconn,int index) +{ + +} + +/* +* This is called whenever the driver closes all references to our device +*/ +static void +_virtio_console_release (struct virtio_connection *conn) +{ +struct _virtio_console *priv =conn_to_priv (conn); + + priv->opened = false; +vbus_memctx_put (priv->ctx); +} + +static struct virtio_connection_ops virtio_console_connection_ops = { + .add_vq = _virtio_console_add_vq, + .del_vq = _virtio_console_del_vq, + .notify_vq = _virtio_console_notify_vq, + .release = _virtio_console_release, +}; + +/* +* -------------------------------------------- +* Interface +* -------------------------------------------- +*/ + +/* +* This is called whenever a driver wants to open our device_interface +* for communication. The connection is represented by a +* virtio_connection object. It is up to the implementation to decide +* if it allows more than one connection at a time. This simple example +* does not. +*/ +static int +virtio_console_intf_open (struct virtio_device_interface *intf, +struct vbus_memctx *ctx, +struct virtio_connection **conn) +{ +struct _virtio_console *priv =intf_to_priv (intf); +unsigned long flags; + +PDEBUG ("open\n" ); + +spin_lock_irqsave (&priv->lock, flags); + +/* +* We only allow one connection to this device +*/ +if (priv->opened) { +spin_unlock_irqrestore (&priv->lock, flags); +return -EBUSY ; + } + +memset (&priv->conn,0 ,sizeof (priv->conn)); + priv->conn.ops = &virtio_console_connection_ops; + + priv->opened = true; + priv->ctx = ctx; + +vbus_memctx_get (ctx); + +spin_unlock_irqrestore (&priv->lock, flags); + + *conn = &priv->conn; + +return 0 ; +} + +static void +virtio_console_intf_release (struct virtio_device_interface *intf) +{ +kobject_put (intf->parent->dev->kobj);/* acquired on bus-connect */ +} + +static struct virtio_device_interface_ops virtio_console_intf_ops = { + .open = virtio_console_intf_open, + .release = virtio_console_intf_release, +}; + +/* +* -------------------------------------------- +* Bus+Device interaction +* -------------------------------------------- +*/ + +/* +* This is called whenever the admin creates a symbolic link between +* a bus in /config/vbus/buses and our device. It represents a bus +* connection. Your device can chose to allow more than one bus to +* connect, or it can restrict it to one bus. It can also choose to +* register one or more device_interfaces on each bus that it +* successfully connects to. +* +* This example device only registers a single interface +*/ +static int +virtio_console_device_bus_connect (struct vbus_device *dev,struct vbus *vbus) +{ +struct _virtio_console *priv =vdev_to_priv (dev); +struct virtio_device_interface *intf = &priv->intf; + +/* We only allow one bus to connect */ +if (priv->connected) +return -EBUSY ; + +kobject_get (dev->kobj);/* released when we release the intf */ + + intf->id.vendor =0x1af4 ; + intf->id.device =VIRTIO_ID_CONSOLE ; + + intf->ops = &virtio_console_intf_ops; + + priv->connected = true; + +/* +* Our example only registers one interface. If you need +* more, simply call interface_register() multiple times +*/ +return virtio_device_interface_register (dev, vbus, intf); +} + +/* +* This is called whenever the admin removes the symbolic link between +* a bus in /config/vbus/buses and our device. +*/ +static int +virtio_console_device_bus_disconnect (struct vbus_device *dev,struct vbus *vbus) +{ +struct _virtio_console *priv =vdev_to_priv (dev); +struct virtio_device_interface *intf = &priv->intf; + +if (!priv->connected) +return -EINVAL ; + +virtio_device_interface_unregister (intf); + + priv->connected = false; + +return 0 ; +} + +static void +virtio_console_device_release (struct vbus_device *dev) +{ +struct _virtio_console *priv =vdev_to_priv (dev); + +unregister_chrdev_region (priv->devno,1 ); +kfree (priv); + +module_put (THIS_MODULE ); +} + + +static struct vbus_device_ops virtio_console_device_ops = { + .bus_connect = virtio_console_device_bus_connect, + .bus_disconnect = virtio_console_device_bus_disconnect, + .release = virtio_console_device_release, +}; + +#define VIRTIO_CONSOLE_TYPE "virtio-console" + +/* +* -------------------------------------------- +* Character Device +* -------------------------------------------- +*/ + +static int +virtio_console_chardev_open (struct inode *inode,struct file *filp) +{ +struct _virtio_console *priv; + + priv = container_of(inode->i_cdev, struct _virtio_console, cdev); + +kobject_get (priv->dev.kobj);/* released when we release the cd */ + filp->private_data = priv; + +return 0 ; +} + +static ssize_t +virtio_console_chardev_read (struct file *filp,char __user *buf,size_t len, +loff_t *ppos) +{ +struct _virtio_console *priv = filp->private_data; + +return -EINVAL ; +} + +static ssize_t +virtio_console_chardev_write (struct file *filp,const char __user *buf, +size_t len,loff_t *ppos) +{ +struct _virtio_console *priv = filp->private_data; + +return -EINVAL ; +} + +static int +virtio_console_chardev_release (struct inode *inode,struct file *filp) +{ +struct _virtio_console *priv = filp->private_data; + +kobject_put (priv->dev.kobj); + +return 0 ; +} + +static const struct file_operations virtio_console_chardev_ops = { + .open = virtio_console_chardev_open, + .read = virtio_console_chardev_read, + .write = virtio_console_chardev_write, + .release = virtio_console_chardev_release, +}; + +/* +* -------------------------------------------- +* Attributes +* -------------------------------------------- +*/ + +/* +* Interface attributes show up as files under +* /sys/vbus/devices/$devid +*/ + +static ssize_t +devno_show (struct vbus_device *dev,struct vbus_device_attribute *attr, +char *buf) +{ +struct _virtio_console *priv =vdev_to_priv (dev); + +return snprintf (buf,PAGE_SIZE ,"%d:%d\n" , +MAJOR (priv->devno),MINOR (priv->devno)); +} + +static struct vbus_device_attribute attr_devno = +__ATTR_RO (devno); + +static struct attribute *attrs[] = { + &attr_devno.attr, +NULL , +}; + +static struct attribute_group virtio_console_attr_group = { + .attrs = attrs, +}; + +/* +* -------------------------------------------- +* Device +* -------------------------------------------- +*/ + +/* +* This is called whenever the admin instantiates our devclass via +* "mkdir /config/vbus/devices/$(inst)/virtio-console" +*/ +static int +virtio_console_device_create (struct vbus_devclass *dc, +struct vbus_device **vdev) +{ +struct _virtio_console *priv; +struct vbus_device *_vdev; +int ret; + + priv =kzalloc (sizeof (*priv),GFP_KERNEL ); +if (!priv) +return -ENOMEM ; + +spin_lock_init (&priv->lock); + +/* +* cdev init +*/ + ret =alloc_chrdev_region (&priv->devno,TTY_MAJOR , +1 ,VIRTIO_CONSOLE_TYPE ); +if (ret <0 ) + goto out_free; + +cdev_init (&priv->cdev, &virtio_console_chardev_ops); + priv->cdev.owner =THIS_MODULE ; + priv->cdev.ops = &virtio_console_chardev_ops; + ret =cdev_add (&priv->cdev, priv->devno,1 ); +if (ret <0 ) + goto out; + +/* +* vbus init +*/ + _vdev = &priv->dev; + + _vdev->type =VIRTIO_CONSOLE_TYPE ; + _vdev->ops = &virtio_console_device_ops; + _vdev->attrs = &virtio_console_attr_group; + + *vdev = _vdev; + +/* +* We don't need a try_get because the reference is held by the +* infrastructure during a create() operation. This is released +* when our device is released +*/ +__module_get (THIS_MODULE ); + +return 0 ; + +out: +unregister_chrdev_region (priv->devno,1 ); +out_free: +kfree (priv); +return ret; +} + +static struct vbus_devclass_ops virtio_console_devclass_ops = { + .create = virtio_console_device_create, +}; + +static struct vbus_devclass virtio_console_devclass = { + .name =VIRTIO_CONSOLE_TYPE , + .ops = &virtio_console_devclass_ops, + .owner =THIS_MODULE , +}; + +static int __init virtio_console_init (void ) +{ +return vbus_devclass_register (&virtio_console_devclass); +} + +static void __exit virtio_console_cleanup(void) +{ +vbus_devclass_unregister (&virtio_console_devclass); +} + +module_init (virtio_console_init); +module_exit (virtio_console_cleanup);
Signature · 1 line
MST
Quoted 13 lines · Gregory Haskins
Hi Michael,
FYI: This was a patch I started to put together around the time vbus-v3 was released to demonstrate the interface for virtio devices that we needed. I just wanted to get it out to you so you have a working example to look at what it takes to write something like virtio-net against virtio-vbus.What I will do next is make a minor v3.1 release of vbus on top of kvm.git and get this patch cleaned up so we can actually run it. Note that v3.1 will just a minor port of v3 to kvm.git/HEAD, not the new iosignalfd/irqfd stuff that I have queued for v4. Thats not ready for prime-time yet.
----------------------------
Quoted 7 lines
virtio: add a virtio-console to the vbus backend
A console probably doesnt need to live in the kernel since it is generally not performance limited. However, its also a very simple example, so lets make a vbus backend to demonstrate the virtio interfaces.
Signed-off-by Gregory Haskins <[email protected]>
I'm a bit confused: does this live on the guest or host side?
Quoted 499 lines
---
drivers/vbus/devices/Kconfig | 9 drivers/vbus/devices/Makefile | 1 drivers/vbus/devices/virtio-console-backend.c | 461 +++++++++++++++++++++++++ 3 files changed, 471 insertions(+), 0 deletions(-) create mode 100644 drivers/vbus/devices/virtio-console-backend.cdrivers/vbus/devices/Kconfig +9
-0@@ -15,3 +15,12 @@ config VBUS_VENETTAP If unsure, say N +config VBUS_VIRTIO_CONSOLE + tristate "Virtual-Bus Backend for virtio-console drivers" + depends on VBUS_DEVICES + select VBUS_VIRTIO_BACKEND + default n + help + Provides a virtual console device within the vbus infrastructure + + If unsure, say Ndrivers/vbus/devices/Makefile +1
-0@@ -1 +1,2 @@ obj-$(CONFIG_VBUS_VENETTAP) += venet-tap.o +obj-$(CONFIG_VBUS_VIRTIO_CONSOLE) += virtio-console-backend.odrivers/vbus/devices/virtio-console-backend.c new file +461
-0@@ -0,0 +1,461 @@ +/* +* virtio-console for the vbus backend +* +* Copyright (C) 2009 Novell, Gregory Haskins <[email protected]> +* +* This file is free software; you can redistribute it and/or modify +* it under the terms of version 2 of the GNU General Public License +* as published by the Free Software Foundation. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/moduleparam.h> + +#include <linux/cdev.h> +#include <linux/fs.h> +#include <linux/major.h> + +#include <linux/vbus_device.h> +#include <linux/virtio_vbus.h> +#include <linux/virtio_console.h> + +MODULE_AUTHOR ("Gregory Haskins" ); +MODULE_LICENSE ("GPL" ); + +#undef PDEBUG/* undef it, just in case */ +#ifdef VIRTIO_CONSOLE_DEBUG +# define PDEBUG(fmt, args...) printk(KERN_DEBUG "virtio-console: " fmt, ## args) +#else +# define PDEBUG(fmt, args...) /* not debugging: nothing */ +#endif + +struct _virtio_console { +spinlock_t lock; +char *name; +dev_t devno; +struct cdev cdev; +struct vbus_device dev; +struct virtio_device_interface intf; +struct virtio_connection conn; +struct vbus_memctx *ctx; +struct virtqueue *rx; +struct virtqueue *tx; +int enabled:1 ; +int connected:1 ; +int opened:1 ; +int tty:1 ; +int link:1 ; +}; + +static struct _virtio_console * +conn_to_priv (struct virtio_connection *conn) +{ + return container_of(conn, struct _virtio_console, conn); +} + +static struct _virtio_console * +intf_to_priv (struct virtio_device_interface *intf) +{ + return container_of(intf, struct _virtio_console, intf); +} + +static struct _virtio_console * +vdev_to_priv (struct vbus_device *vdev) +{ + return container_of(vdev, struct _virtio_console, dev); +} + +/* +* -------------------------------------------- +* Driver connection +* -------------------------------------------- +*/ + +static void +_virtio_console_add_vq (struct virtio_connection *vconn,int index, +struct virtqueue *vq) +{ +struct _virtio_console *priv =conn_to_priv (vconn); + +switch (index) { +case 0 : +if (!priv->rx) + priv->rx = vq; +break ; +case 1 : +if (!priv->tx) + priv->tx = vq; +break ; +default : +break ; + } +} + +static void +_virtio_console_del_vq (struct virtio_connection *vconn,int index) +{ +struct _virtio_console *priv =conn_to_priv (vconn); + +switch (index) { +case 0 : + priv->rx =NULL ; +break ; +case 1 : + priv->tx =NULL ; +break ; +default : +break ; + } +} + +static void +_virtio_console_notify_vq (struct virtio_connection *vconn,int index) +{ + +} + +/* +* This is called whenever the driver closes all references to our device +*/ +static void +_virtio_console_release (struct virtio_connection *conn) +{ +struct _virtio_console *priv =conn_to_priv (conn); + + priv->opened = false; +vbus_memctx_put (priv->ctx); +} + +static struct virtio_connection_ops virtio_console_connection_ops = { + .add_vq = _virtio_console_add_vq, + .del_vq = _virtio_console_del_vq, + .notify_vq = _virtio_console_notify_vq, + .release = _virtio_console_release, +}; + +/* +* -------------------------------------------- +* Interface +* -------------------------------------------- +*/ + +/* +* This is called whenever a driver wants to open our device_interface +* for communication. The connection is represented by a +* virtio_connection object. It is up to the implementation to decide +* if it allows more than one connection at a time. This simple example +* does not. +*/ +static int +virtio_console_intf_open (struct virtio_device_interface *intf, +struct vbus_memctx *ctx, +struct virtio_connection **conn) +{ +struct _virtio_console *priv =intf_to_priv (intf); +unsigned long flags; + +PDEBUG ("open\n" ); + +spin_lock_irqsave (&priv->lock, flags); + +/* +* We only allow one connection to this device +*/ +if (priv->opened) { +spin_unlock_irqrestore (&priv->lock, flags); +return -EBUSY ; + } + +memset (&priv->conn,0 ,sizeof (priv->conn)); + priv->conn.ops = &virtio_console_connection_ops; + + priv->opened = true; + priv->ctx = ctx; + +vbus_memctx_get (ctx); + +spin_unlock_irqrestore (&priv->lock, flags); + + *conn = &priv->conn; + +return 0 ; +} + +static void +virtio_console_intf_release (struct virtio_device_interface *intf) +{ +kobject_put (intf->parent->dev->kobj);/* acquired on bus-connect */ +} + +static struct virtio_device_interface_ops virtio_console_intf_ops = { + .open = virtio_console_intf_open, + .release = virtio_console_intf_release, +}; + +/* +* -------------------------------------------- +* Bus+Device interaction +* -------------------------------------------- +*/ + +/* +* This is called whenever the admin creates a symbolic link between +* a bus in /config/vbus/buses and our device. It represents a bus +* connection. Your device can chose to allow more than one bus to +* connect, or it can restrict it to one bus. It can also choose to +* register one or more device_interfaces on each bus that it +* successfully connects to. +* +* This example device only registers a single interface +*/ +static int +virtio_console_device_bus_connect (struct vbus_device *dev,struct vbus *vbus) +{ +struct _virtio_console *priv =vdev_to_priv (dev); +struct virtio_device_interface *intf = &priv->intf; + +/* We only allow one bus to connect */ +if (priv->connected) +return -EBUSY ; + +kobject_get (dev->kobj);/* released when we release the intf */ + + intf->id.vendor =0x1af4 ; + intf->id.device =VIRTIO_ID_CONSOLE ; + + intf->ops = &virtio_console_intf_ops; + + priv->connected = true; + +/* +* Our example only registers one interface. If you need +* more, simply call interface_register() multiple times +*/ +return virtio_device_interface_register (dev, vbus, intf); +} + +/* +* This is called whenever the admin removes the symbolic link between +* a bus in /config/vbus/buses and our device. +*/ +static int +virtio_console_device_bus_disconnect (struct vbus_device *dev,struct vbus *vbus) +{ +struct _virtio_console *priv =vdev_to_priv (dev); +struct virtio_device_interface *intf = &priv->intf; + +if (!priv->connected) +return -EINVAL ; + +virtio_device_interface_unregister (intf); + + priv->connected = false; + +return 0 ; +} + +static void +virtio_console_device_release (struct vbus_device *dev) +{ +struct _virtio_console *priv =vdev_to_priv (dev); + +unregister_chrdev_region (priv->devno,1 ); +kfree (priv); + +module_put (THIS_MODULE ); +} + + +static struct vbus_device_ops virtio_console_device_ops = { + .bus_connect = virtio_console_device_bus_connect, + .bus_disconnect = virtio_console_device_bus_disconnect, + .release = virtio_console_device_release, +}; + +#define VIRTIO_CONSOLE_TYPE "virtio-console" + +/* +* -------------------------------------------- +* Character Device +* -------------------------------------------- +*/ + +static int +virtio_console_chardev_open (struct inode *inode,struct file *filp) +{ +struct _virtio_console *priv; + + priv = container_of(inode->i_cdev, struct _virtio_console, cdev); + +kobject_get (priv->dev.kobj);/* released when we release the cd */ + filp->private_data = priv; + +return 0 ; +} + +static ssize_t +virtio_console_chardev_read (struct file *filp,char __user *buf,size_t len, +loff_t *ppos) +{ +struct _virtio_console *priv = filp->private_data; + +return -EINVAL ; +} + +static ssize_t +virtio_console_chardev_write (struct file *filp,const char __user *buf, +size_t len,loff_t *ppos) +{ +struct _virtio_console *priv = filp->private_data; + +return -EINVAL ; +} + +static int +virtio_console_chardev_release (struct inode *inode,struct file *filp) +{ +struct _virtio_console *priv = filp->private_data; + +kobject_put (priv->dev.kobj); + +return 0 ; +} + +static const struct file_operations virtio_console_chardev_ops = { + .open = virtio_console_chardev_open, + .read = virtio_console_chardev_read, + .write = virtio_console_chardev_write, + .release = virtio_console_chardev_release, +}; + +/* +* -------------------------------------------- +* Attributes +* -------------------------------------------- +*/ + +/* +* Interface attributes show up as files under +* /sys/vbus/devices/$devid +*/ + +static ssize_t +devno_show (struct vbus_device *dev,struct vbus_device_attribute *attr, +char *buf) +{ +struct _virtio_console *priv =vdev_to_priv (dev); + +return snprintf (buf,PAGE_SIZE ,"%d:%d\n" , +MAJOR (priv->devno),MINOR (priv->devno)); +} + +static struct vbus_device_attribute attr_devno = +__ATTR_RO (devno); + +static struct attribute *attrs[] = { + &attr_devno.attr, +NULL , +}; + +static struct attribute_group virtio_console_attr_group = { + .attrs = attrs, +}; + +/* +* -------------------------------------------- +* Device +* -------------------------------------------- +*/ + +/* +* This is called whenever the admin instantiates our devclass via +* "mkdir /config/vbus/devices/$(inst)/virtio-console" +*/ +static int +virtio_console_device_create (struct vbus_devclass *dc, +struct vbus_device **vdev) +{ +struct _virtio_console *priv; +struct vbus_device *_vdev; +int ret; + + priv =kzalloc (sizeof (*priv),GFP_KERNEL ); +if (!priv) +return -ENOMEM ; + +spin_lock_init (&priv->lock); + +/* +* cdev init +*/ + ret =alloc_chrdev_region (&priv->devno,TTY_MAJOR , +1 ,VIRTIO_CONSOLE_TYPE ); +if (ret <0 ) + goto out_free; + +cdev_init (&priv->cdev, &virtio_console_chardev_ops); + priv->cdev.owner =THIS_MODULE ; + priv->cdev.ops = &virtio_console_chardev_ops; + ret =cdev_add (&priv->cdev, priv->devno,1 ); +if (ret <0 ) + goto out; + +/* +* vbus init +*/ + _vdev = &priv->dev; + + _vdev->type =VIRTIO_CONSOLE_TYPE ; + _vdev->ops = &virtio_console_device_ops; + _vdev->attrs = &virtio_console_attr_group; + + *vdev = _vdev; + +/* +* We don't need a try_get because the reference is held by the +* infrastructure during a create() operation. This is released +* when our device is released +*/ +__module_get (THIS_MODULE ); + +return 0 ; + +out: +unregister_chrdev_region (priv->devno,1 ); +out_free: +kfree (priv); +return ret; +} + +static struct vbus_devclass_ops virtio_console_devclass_ops = { + .create = virtio_console_device_create, +}; + +static struct vbus_devclass virtio_console_devclass = { + .name =VIRTIO_CONSOLE_TYPE , + .ops = &virtio_console_devclass_ops, + .owner =THIS_MODULE , +}; + +static int __init virtio_console_init (void ) +{ +return vbus_devclass_register (&virtio_console_devclass); +} + +static void __exit virtio_console_cleanup(void) +{ +vbus_devclass_unregister (&virtio_console_devclass); +} + +module_init (virtio_console_init); +module_exit (virtio_console_cleanup);
Signature · 1 line
MST
Quoted 17 lines · Michael S. Tsirkin
Quoted 15 lines · Gregory Haskins
Hi Michael,
FYI: This was a patch I started to put together around the time vbus-v3 was released to demonstrate the interface for virtio devices that we needed. I just wanted to get it out to you so you have a working example to look at what it takes to write something like virtio-net against virtio-vbus.What I will do next is make a minor v3.1 release of vbus on top of kvm.git and get this patch cleaned up so we can actually run it. Note that v3.1 will just a minor port of v3 to kvm.git/HEAD, not the new iosignalfd/irqfd stuff that I have queued for v4. Thats not ready for prime-time yet.
----------------------------
virtio: add a virtio-console to the vbus backend
Quoted 9 lines
Quoted 6 lines
A console probably doesnt need to live in the kernel since it is generally not performance limited. However, its also a very simple example, so lets make a vbus backend to demonstrate the virtio interfaces.
Signed-off-by Gregory Haskins <[email protected]>I'm a bit confused: does this live on the guest or host side?
Host side. The guest would run the existing virtio-console driver.
-Greg
Quoted 506 lines
Quoted 504 lines
---
drivers/vbus/devices/Kconfig | 9 drivers/vbus/devices/Makefile | 1 drivers/vbus/devices/virtio-console-backend.c | 461 +++++++++++++++++++++++++ 3 files changed, 471 insertions(+), 0 deletions(-) create mode 100644 drivers/vbus/devices/virtio-console-backend.cdrivers/vbus/devices/Kconfig +9
-0@@ -15,3 +15,12 @@ config VBUS_VENETTAP If unsure, say N +config VBUS_VIRTIO_CONSOLE + tristate "Virtual-Bus Backend for virtio-console drivers" + depends on VBUS_DEVICES + select VBUS_VIRTIO_BACKEND + default n + help + Provides a virtual console device within the vbus infrastructure + + If unsure, say Ndrivers/vbus/devices/Makefile +1
-0@@ -1 +1,2 @@ obj-$(CONFIG_VBUS_VENETTAP) += venet-tap.o +obj-$(CONFIG_VBUS_VIRTIO_CONSOLE) += virtio-console-backend.odrivers/vbus/devices/virtio-console-backend.c new file +461
-0@@ -0,0 +1,461 @@ +/* +* virtio-console for the vbus backend +* +* Copyright (C) 2009 Novell, Gregory Haskins <[email protected]> +* +* This file is free software; you can redistribute it and/or modify +* it under the terms of version 2 of the GNU General Public License +* as published by the Free Software Foundation. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/moduleparam.h> + +#include <linux/cdev.h> +#include <linux/fs.h> +#include <linux/major.h> + +#include <linux/vbus_device.h> +#include <linux/virtio_vbus.h> +#include <linux/virtio_console.h> + +MODULE_AUTHOR ("Gregory Haskins" ); +MODULE_LICENSE ("GPL" ); + +#undef PDEBUG/* undef it, just in case */ +#ifdef VIRTIO_CONSOLE_DEBUG +# define PDEBUG(fmt, args...) printk(KERN_DEBUG "virtio-console: " fmt, ## args) +#else +# define PDEBUG(fmt, args...) /* not debugging: nothing */ +#endif + +struct _virtio_console { +spinlock_t lock; +char *name; +dev_t devno; +struct cdev cdev; +struct vbus_device dev; +struct virtio_device_interface intf; +struct virtio_connection conn; +struct vbus_memctx *ctx; +struct virtqueue *rx; +struct virtqueue *tx; +int enabled:1 ; +int connected:1 ; +int opened:1 ; +int tty:1 ; +int link:1 ; +}; + +static struct _virtio_console * +conn_to_priv (struct virtio_connection *conn) +{ + return container_of(conn, struct _virtio_console, conn); +} + +static struct _virtio_console * +intf_to_priv (struct virtio_device_interface *intf) +{ + return container_of(intf, struct _virtio_console, intf); +} + +static struct _virtio_console * +vdev_to_priv (struct vbus_device *vdev) +{ + return container_of(vdev, struct _virtio_console, dev); +} + +/* +* -------------------------------------------- +* Driver connection +* -------------------------------------------- +*/ + +static void +_virtio_console_add_vq (struct virtio_connection *vconn,int index, +struct virtqueue *vq) +{ +struct _virtio_console *priv =conn_to_priv (vconn); + +switch (index) { +case 0 : +if (!priv->rx) + priv->rx = vq; +break ; +case 1 : +if (!priv->tx) + priv->tx = vq; +break ; +default : +break ; + } +} + +static void +_virtio_console_del_vq (struct virtio_connection *vconn,int index) +{ +struct _virtio_console *priv =conn_to_priv (vconn); + +switch (index) { +case 0 : + priv->rx =NULL ; +break ; +case 1 : + priv->tx =NULL ; +break ; +default : +break ; + } +} + +static void +_virtio_console_notify_vq (struct virtio_connection *vconn,int index) +{ + +} + +/* +* This is called whenever the driver closes all references to our device +*/ +static void +_virtio_console_release (struct virtio_connection *conn) +{ +struct _virtio_console *priv =conn_to_priv (conn); + + priv->opened = false; +vbus_memctx_put (priv->ctx); +} + +static struct virtio_connection_ops virtio_console_connection_ops = { + .add_vq = _virtio_console_add_vq, + .del_vq = _virtio_console_del_vq, + .notify_vq = _virtio_console_notify_vq, + .release = _virtio_console_release, +}; + +/* +* -------------------------------------------- +* Interface +* -------------------------------------------- +*/ + +/* +* This is called whenever a driver wants to open our device_interface +* for communication. The connection is represented by a +* virtio_connection object. It is up to the implementation to decide +* if it allows more than one connection at a time. This simple example +* does not. +*/ +static int +virtio_console_intf_open (struct virtio_device_interface *intf, +struct vbus_memctx *ctx, +struct virtio_connection **conn) +{ +struct _virtio_console *priv =intf_to_priv (intf); +unsigned long flags; + +PDEBUG ("open\n" ); + +spin_lock_irqsave (&priv->lock, flags); + +/* +* We only allow one connection to this device +*/ +if (priv->opened) { +spin_unlock_irqrestore (&priv->lock, flags); +return -EBUSY ; + } + +memset (&priv->conn,0 ,sizeof (priv->conn)); + priv->conn.ops = &virtio_console_connection_ops; + + priv->opened = true; + priv->ctx = ctx; + +vbus_memctx_get (ctx); + +spin_unlock_irqrestore (&priv->lock, flags); + + *conn = &priv->conn; + +return 0 ; +} + +static void +virtio_console_intf_release (struct virtio_device_interface *intf) +{ +kobject_put (intf->parent->dev->kobj);/* acquired on bus-connect */ +} + +static struct virtio_device_interface_ops virtio_console_intf_ops = { + .open = virtio_console_intf_open, + .release = virtio_console_intf_release, +}; + +/* +* -------------------------------------------- +* Bus+Device interaction +* -------------------------------------------- +*/ + +/* +* This is called whenever the admin creates a symbolic link between +* a bus in /config/vbus/buses and our device. It represents a bus +* connection. Your device can chose to allow more than one bus to +* connect, or it can restrict it to one bus. It can also choose to +* register one or more device_interfaces on each bus that it +* successfully connects to. +* +* This example device only registers a single interface +*/ +static int +virtio_console_device_bus_connect (struct vbus_device *dev,struct vbus *vbus) +{ +struct _virtio_console *priv =vdev_to_priv (dev); +struct virtio_device_interface *intf = &priv->intf; + +/* We only allow one bus to connect */ +if (priv->connected) +return -EBUSY ; + +kobject_get (dev->kobj);/* released when we release the intf */ + + intf->id.vendor =0x1af4 ; + intf->id.device =VIRTIO_ID_CONSOLE ; + + intf->ops = &virtio_console_intf_ops; + + priv->connected = true; + +/* +* Our example only registers one interface. If you need +* more, simply call interface_register() multiple times +*/ +return virtio_device_interface_register (dev, vbus, intf); +} + +/* +* This is called whenever the admin removes the symbolic link between +* a bus in /config/vbus/buses and our device. +*/ +static int +virtio_console_device_bus_disconnect (struct vbus_device *dev,struct vbus *vbus) +{ +struct _virtio_console *priv =vdev_to_priv (dev); +struct virtio_device_interface *intf = &priv->intf; + +if (!priv->connected) +return -EINVAL ; + +virtio_device_interface_unregister (intf); + + priv->connected = false; + +return 0 ; +} + +static void +virtio_console_device_release (struct vbus_device *dev) +{ +struct _virtio_console *priv =vdev_to_priv (dev); + +unregister_chrdev_region (priv->devno,1 ); +kfree (priv); + +module_put (THIS_MODULE ); +} + + +static struct vbus_device_ops virtio_console_device_ops = { + .bus_connect = virtio_console_device_bus_connect, + .bus_disconnect = virtio_console_device_bus_disconnect, + .release = virtio_console_device_release, +}; + +#define VIRTIO_CONSOLE_TYPE "virtio-console" + +/* +* -------------------------------------------- +* Character Device +* -------------------------------------------- +*/ + +static int +virtio_console_chardev_open (struct inode *inode,struct file *filp) +{ +struct _virtio_console *priv; + + priv = container_of(inode->i_cdev, struct _virtio_console, cdev); + +kobject_get (priv->dev.kobj);/* released when we release the cd */ + filp->private_data = priv; + +return 0 ; +} + +static ssize_t +virtio_console_chardev_read (struct file *filp,char __user *buf,size_t len, +loff_t *ppos) +{ +struct _virtio_console *priv = filp->private_data; + +return -EINVAL ; +} + +static ssize_t +virtio_console_chardev_write (struct file *filp,const char __user *buf, +size_t len,loff_t *ppos) +{ +struct _virtio_console *priv = filp->private_data; + +return -EINVAL ; +} + +static int +virtio_console_chardev_release (struct inode *inode,struct file *filp) +{ +struct _virtio_console *priv = filp->private_data; + +kobject_put (priv->dev.kobj); + +return 0 ; +} + +static const struct file_operations virtio_console_chardev_ops = { + .open = virtio_console_chardev_open, + .read = virtio_console_chardev_read, + .write = virtio_console_chardev_write, + .release = virtio_console_chardev_release, +}; + +/* +* -------------------------------------------- +* Attributes +* -------------------------------------------- +*/ + +/* +* Interface attributes show up as files under +* /sys/vbus/devices/$devid +*/ + +static ssize_t +devno_show (struct vbus_device *dev,struct vbus_device_attribute *attr, +char *buf) +{ +struct _virtio_console *priv =vdev_to_priv (dev); + +return snprintf (buf,PAGE_SIZE ,"%d:%d\n" , +MAJOR (priv->devno),MINOR (priv->devno)); +} + +static struct vbus_device_attribute attr_devno = +__ATTR_RO (devno); + +static struct attribute *attrs[] = { + &attr_devno.attr, +NULL , +}; + +static struct attribute_group virtio_console_attr_group = { + .attrs = attrs, +}; + +/* +* -------------------------------------------- +* Device +* -------------------------------------------- +*/ + +/* +* This is called whenever the admin instantiates our devclass via +* "mkdir /config/vbus/devices/$(inst)/virtio-console" +*/ +static int +virtio_console_device_create (struct vbus_devclass *dc, +struct vbus_device **vdev) +{ +struct _virtio_console *priv; +struct vbus_device *_vdev; +int ret; + + priv =kzalloc (sizeof (*priv),GFP_KERNEL ); +if (!priv) +return -ENOMEM ; + +spin_lock_init (&priv->lock); + +/* +* cdev init +*/ + ret =alloc_chrdev_region (&priv->devno,TTY_MAJOR , +1 ,VIRTIO_CONSOLE_TYPE ); +if (ret <0 ) + goto out_free; + +cdev_init (&priv->cdev, &virtio_console_chardev_ops); + priv->cdev.owner =THIS_MODULE ; + priv->cdev.ops = &virtio_console_chardev_ops; + ret =cdev_add (&priv->cdev, priv->devno,1 ); +if (ret <0 ) + goto out; + +/* +* vbus init +*/ + _vdev = &priv->dev; + + _vdev->type =VIRTIO_CONSOLE_TYPE ; + _vdev->ops = &virtio_console_device_ops; + _vdev->attrs = &virtio_console_attr_group; + + *vdev = _vdev; + +/* +* We don't need a try_get because the reference is held by the +* infrastructure during a create() operation. This is released +* when our device is released +*/ +__module_get (THIS_MODULE ); + +return 0 ; + +out: +unregister_chrdev_region (priv->devno,1 ); +out_free: +kfree (priv); +return ret; +} + +static struct vbus_devclass_ops virtio_console_devclass_ops = { + .create = virtio_console_device_create, +}; + +static struct vbus_devclass virtio_console_devclass = { + .name =VIRTIO_CONSOLE_TYPE , + .ops = &virtio_console_devclass_ops, + .owner =THIS_MODULE , +}; + +static int __init virtio_console_init (void ) +{ +return vbus_devclass_register (&virtio_console_devclass); +} + +static void __exit virtio_console_cleanup(void) +{ +vbus_devclass_unregister (&virtio_console_devclass); +} + +module_init (virtio_console_init); +module_exit (virtio_console_cleanup);
Quoted 98 lines · Gregory Haskins
Quoted 18 lines · Michael S. Tsirkin
Quoted 15 lines · Gregory Haskins
Hi Michael,
FYI: This was a patch I started to put together around the time vbus-v3 was released to demonstrate the interface for virtio devices that we needed. I just wanted to get it out to you so you have a working example to look at what it takes to write something like virtio-net against virtio-vbus.What I will do next is make a minor v3.1 release of vbus on top of kvm.git and get this patch cleaned up so we can actually run it. Note that v3.1 will just a minor port of v3 to kvm.git/HEAD, not the new iosignalfd/irqfd stuff that I have queued for v4. Thats not ready for prime-time yet.
----------------------------
virtio: add a virtio-console to the vbus backend
Quoted 10 lines
Quoted 7 lines
A console probably doesnt need to live in the kernel since it is generally not performance limited. However, its also a very simple example, so lets make a vbus backend to demonstrate the virtio interfaces.
Signed-off-by Gregory Haskins <[email protected]>I'm a bit confused: does this live on the guest or host side?
Host side. The guest would run the existing virtio-console driver.
-Greg
Quoted 63 lines
Quoted 63 lines
---
drivers/vbus/devices/Kconfig | 9 drivers/vbus/devices/Makefile | 1 drivers/vbus/devices/virtio-console-backend.c | 461 +++++++++++++++++++++++++ 3 files changed, 471 insertions(+), 0 deletions(-) create mode 100644 drivers/vbus/devices/virtio-console-backend.cdrivers/vbus/devices/Kconfig +9
-0@@ -15,3 +15,12 @@ config VBUS_VENETTAP If unsure, say N +config VBUS_VIRTIO_CONSOLE + tristate "Virtual-Bus Backend for virtio-console drivers" + depends on VBUS_DEVICES + select VBUS_VIRTIO_BACKEND + default n + help + Provides a virtual console device within the vbus infrastructure + + If unsure, say Ndrivers/vbus/devices/Makefile +1
-0@@ -1 +1,2 @@ obj-$(CONFIG_VBUS_VENETTAP) += venet-tap.o +obj-$(CONFIG_VBUS_VIRTIO_CONSOLE) += virtio-console-backend.odrivers/vbus/devices/virtio-console-backend.c new file +22
-0@@ -0,0 +1,461 @@ +/* +* virtio-console for the vbus backend +* +* Copyright (C) 2009 Novell, Gregory Haskins <[email protected]> +* +* This file is free software; you can redistribute it and/or modify +* it under the terms of version 2 of the GNU General Public License +* as published by the Free Software Foundation. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/moduleparam.h>
Quoted 8 lines
Quoted 8 lines
drivers/vbus/devices/virtio-console-backend.c line 29
Quoted 8 lines
drivers/vbus/devices/virtio-console-backend.c new file +7
-0+ +#include <linux/cdev.h> +#include <linux/fs.h> +#include <linux/major.h> + +#include <linux/vbus_device.h> +#include <linux/virtio_vbus.h>
^^^^^^^^^^^^^^^^^^^^^^^
For more details, see these two files in my tree. Here is a direct link via gitweb
and
HTH
-Greg
Quoted 439 lines
Quoted 436 lines
Quoted 434 lines
drivers/vbus/devices/virtio-console-backend.c new file +432
-0+#include <linux/virtio_console.h> + +MODULE_AUTHOR ("Gregory Haskins" ); +MODULE_LICENSE ("GPL" ); + +#undef PDEBUG/* undef it, just in case */ +#ifdef VIRTIO_CONSOLE_DEBUG +# define PDEBUG(fmt, args...) printk(KERN_DEBUG "virtio-console: " fmt, ## args) +#else +# define PDEBUG(fmt, args...) /* not debugging: nothing */ +#endif + +struct _virtio_console { +spinlock_t lock; +char *name; +dev_t devno; +struct cdev cdev; +struct vbus_device dev; +struct virtio_device_interface intf; +struct virtio_connection conn; +struct vbus_memctx *ctx; +struct virtqueue *rx; +struct virtqueue *tx; +int enabled:1 ; +int connected:1 ; +int opened:1 ; +int tty:1 ; +int link:1 ; +}; + +static struct _virtio_console * +conn_to_priv (struct virtio_connection *conn) +{ + return container_of(conn, struct _virtio_console, conn); +} + +static struct _virtio_console * +intf_to_priv (struct virtio_device_interface *intf) +{ + return container_of(intf, struct _virtio_console, intf); +} + +static struct _virtio_console * +vdev_to_priv (struct vbus_device *vdev) +{ + return container_of(vdev, struct _virtio_console, dev); +} + +/* +* -------------------------------------------- +* Driver connection +* -------------------------------------------- +*/ + +static void +_virtio_console_add_vq (struct virtio_connection *vconn,int index, +struct virtqueue *vq) +{ +struct _virtio_console *priv =conn_to_priv (vconn); + +switch (index) { +case 0 : +if (!priv->rx) + priv->rx = vq; +break ; +case 1 : +if (!priv->tx) + priv->tx = vq; +break ; +default : +break ; + } +} + +static void +_virtio_console_del_vq (struct virtio_connection *vconn,int index) +{ +struct _virtio_console *priv =conn_to_priv (vconn); + +switch (index) { +case 0 : + priv->rx =NULL ; +break ; +case 1 : + priv->tx =NULL ; +break ; +default : +break ; + } +} + +static void +_virtio_console_notify_vq (struct virtio_connection *vconn,int index) +{ + +} + +/* +* This is called whenever the driver closes all references to our device +*/ +static void +_virtio_console_release (struct virtio_connection *conn) +{ +struct _virtio_console *priv =conn_to_priv (conn); + + priv->opened = false; +vbus_memctx_put (priv->ctx); +} + +static struct virtio_connection_ops virtio_console_connection_ops = { + .add_vq = _virtio_console_add_vq, + .del_vq = _virtio_console_del_vq, + .notify_vq = _virtio_console_notify_vq, + .release = _virtio_console_release, +}; + +/* +* -------------------------------------------- +* Interface +* -------------------------------------------- +*/ + +/* +* This is called whenever a driver wants to open our device_interface +* for communication. The connection is represented by a +* virtio_connection object. It is up to the implementation to decide +* if it allows more than one connection at a time. This simple example +* does not. +*/ +static int +virtio_console_intf_open (struct virtio_device_interface *intf, +struct vbus_memctx *ctx, +struct virtio_connection **conn) +{ +struct _virtio_console *priv =intf_to_priv (intf); +unsigned long flags; + +PDEBUG ("open\n" ); + +spin_lock_irqsave (&priv->lock, flags); + +/* +* We only allow one connection to this device +*/ +if (priv->opened) { +spin_unlock_irqrestore (&priv->lock, flags); +return -EBUSY ; + } + +memset (&priv->conn,0 ,sizeof (priv->conn)); + priv->conn.ops = &virtio_console_connection_ops; + + priv->opened = true; + priv->ctx = ctx; + +vbus_memctx_get (ctx); + +spin_unlock_irqrestore (&priv->lock, flags); + + *conn = &priv->conn; + +return 0 ; +} + +static void +virtio_console_intf_release (struct virtio_device_interface *intf) +{ +kobject_put (intf->parent->dev->kobj);/* acquired on bus-connect */ +} + +static struct virtio_device_interface_ops virtio_console_intf_ops = { + .open = virtio_console_intf_open, + .release = virtio_console_intf_release, +}; + +/* +* -------------------------------------------- +* Bus+Device interaction +* -------------------------------------------- +*/ + +/* +* This is called whenever the admin creates a symbolic link between +* a bus in /config/vbus/buses and our device. It represents a bus +* connection. Your device can chose to allow more than one bus to +* connect, or it can restrict it to one bus. It can also choose to +* register one or more device_interfaces on each bus that it +* successfully connects to. +* +* This example device only registers a single interface +*/ +static int +virtio_console_device_bus_connect (struct vbus_device *dev,struct vbus *vbus) +{ +struct _virtio_console *priv =vdev_to_priv (dev); +struct virtio_device_interface *intf = &priv->intf; + +/* We only allow one bus to connect */ +if (priv->connected) +return -EBUSY ; + +kobject_get (dev->kobj);/* released when we release the intf */ + + intf->id.vendor =0x1af4 ; + intf->id.device =VIRTIO_ID_CONSOLE ; + + intf->ops = &virtio_console_intf_ops; + + priv->connected = true; + +/* +* Our example only registers one interface. If you need +* more, simply call interface_register() multiple times +*/ +return virtio_device_interface_register (dev, vbus, intf); +} + +/* +* This is called whenever the admin removes the symbolic link between +* a bus in /config/vbus/buses and our device. +*/ +static int +virtio_console_device_bus_disconnect (struct vbus_device *dev,struct vbus *vbus) +{ +struct _virtio_console *priv =vdev_to_priv (dev); +struct virtio_device_interface *intf = &priv->intf; + +if (!priv->connected) +return -EINVAL ; + +virtio_device_interface_unregister (intf); + + priv->connected = false; + +return 0 ; +} + +static void +virtio_console_device_release (struct vbus_device *dev) +{ +struct _virtio_console *priv =vdev_to_priv (dev); + +unregister_chrdev_region (priv->devno,1 ); +kfree (priv); + +module_put (THIS_MODULE ); +} + + +static struct vbus_device_ops virtio_console_device_ops = { + .bus_connect = virtio_console_device_bus_connect, + .bus_disconnect = virtio_console_device_bus_disconnect, + .release = virtio_console_device_release, +}; + +#define VIRTIO_CONSOLE_TYPE "virtio-console" + +/* +* -------------------------------------------- +* Character Device +* -------------------------------------------- +*/ + +static int +virtio_console_chardev_open (struct inode *inode,struct file *filp) +{ +struct _virtio_console *priv; + + priv = container_of(inode->i_cdev, struct _virtio_console, cdev); + +kobject_get (priv->dev.kobj);/* released when we release the cd */ + filp->private_data = priv; + +return 0 ; +} + +static ssize_t +virtio_console_chardev_read (struct file *filp,char __user *buf,size_t len, +loff_t *ppos) +{ +struct _virtio_console *priv = filp->private_data; + +return -EINVAL ; +} + +static ssize_t +virtio_console_chardev_write (struct file *filp,const char __user *buf, +size_t len,loff_t *ppos) +{ +struct _virtio_console *priv = filp->private_data; + +return -EINVAL ; +} + +static int +virtio_console_chardev_release (struct inode *inode,struct file *filp) +{ +struct _virtio_console *priv = filp->private_data; + +kobject_put (priv->dev.kobj); + +return 0 ; +} + +static const struct file_operations virtio_console_chardev_ops = { + .open = virtio_console_chardev_open, + .read = virtio_console_chardev_read, + .write = virtio_console_chardev_write, + .release = virtio_console_chardev_release, +}; + +/* +* -------------------------------------------- +* Attributes +* -------------------------------------------- +*/ + +/* +* Interface attributes show up as files under +* /sys/vbus/devices/$devid +*/ + +static ssize_t +devno_show (struct vbus_device *dev,struct vbus_device_attribute *attr, +char *buf) +{ +struct _virtio_console *priv =vdev_to_priv (dev); + +return snprintf (buf,PAGE_SIZE ,"%d:%d\n" , +MAJOR (priv->devno),MINOR (priv->devno)); +} + +static struct vbus_device_attribute attr_devno = +__ATTR_RO (devno); + +static struct attribute *attrs[] = { + &attr_devno.attr, +NULL , +}; + +static struct attribute_group virtio_console_attr_group = { + .attrs = attrs, +}; + +/* +* -------------------------------------------- +* Device +* -------------------------------------------- +*/ + +/* +* This is called whenever the admin instantiates our devclass via +* "mkdir /config/vbus/devices/$(inst)/virtio-console" +*/ +static int +virtio_console_device_create (struct vbus_devclass *dc, +struct vbus_device **vdev) +{ +struct _virtio_console *priv; +struct vbus_device *_vdev; +int ret; + + priv =kzalloc (sizeof (*priv),GFP_KERNEL ); +if (!priv) +return -ENOMEM ; + +spin_lock_init (&priv->lock); + +/* +* cdev init +*/ + ret =alloc_chrdev_region (&priv->devno,TTY_MAJOR , +1 ,VIRTIO_CONSOLE_TYPE ); +if (ret <0 ) + goto out_free; + +cdev_init (&priv->cdev, &virtio_console_chardev_ops); + priv->cdev.owner =THIS_MODULE ; + priv->cdev.ops = &virtio_console_chardev_ops; + ret =cdev_add (&priv->cdev, priv->devno,1 ); +if (ret <0 ) + goto out; + +/* +* vbus init +*/ + _vdev = &priv->dev; + + _vdev->type =VIRTIO_CONSOLE_TYPE ; + _vdev->ops = &virtio_console_device_ops; + _vdev->attrs = &virtio_console_attr_group; + + *vdev = _vdev; + +/* +* We don't need a try_get because the reference is held by the +* infrastructure during a create() operation. This is released +* when our device is released +*/ +__module_get (THIS_MODULE ); + +return 0 ; + +out: +unregister_chrdev_region (priv->devno,1 ); +out_free: +kfree (priv); +return ret; +} + +static struct vbus_devclass_ops virtio_console_devclass_ops = { + .create = virtio_console_device_create, +}; + +static struct vbus_devclass virtio_console_devclass = { + .name =VIRTIO_CONSOLE_TYPE , + .ops = &virtio_console_devclass_ops, + .owner =THIS_MODULE , +}; + +static int __init virtio_console_init (void ) +{ +return vbus_devclass_register (&virtio_console_devclass); +} + +static void __exit virtio_console_cleanup(void) +{ +vbus_devclass_unregister (&virtio_console_devclass); +} + +module_init (virtio_console_init); +module_exit (virtio_console_cleanup);