2010-01-18 16:44:55 +03:00
/*
* Copyright ( C ) 2006 , 2007 , 2009 Rusty Russell , IBM Corporation
2007-10-22 05:03:39 +04:00
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* 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 . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*/
# include <linux/err.h>
# include <linux/init.h>
# include <linux/virtio.h>
# include <linux/virtio_console.h>
# include "hvc_console.h"
2010-01-18 16:45:00 +03:00
struct port {
struct virtqueue * in_vq , * out_vq ;
struct virtio_device * vdev ;
/* This is our input buffer, and how much data is left in it. */
char * inbuf ;
unsigned int used_len , offset ;
/* The hvc device */
struct hvc_struct * hvc ;
} ;
2007-10-22 05:03:39 +04:00
2010-01-18 16:45:00 +03:00
/* We have one port ready to go immediately, for a console. */
static struct port console ;
2008-06-20 17:24:15 +04:00
2010-01-18 16:44:56 +03:00
/* This is the very early arch-specified put chars function. */
static int ( * early_put_chars ) ( u32 , const char * , int ) ;
2010-01-18 16:44:55 +03:00
/*
* The put_chars ( ) callback is pretty straightforward .
2007-10-22 05:03:39 +04:00
*
2010-01-18 16:44:55 +03:00
* We turn the characters into a scatter - gather list , add it to the
* output queue and then kick the Host . Then we sit here waiting for
* it to finish : inefficient in theory , but in practice
* implementations will do it immediately ( lguest ' s Launcher does ) .
*/
2007-10-22 05:03:39 +04:00
static int put_chars ( u32 vtermno , const char * buf , int count )
{
struct scatterlist sg [ 1 ] ;
unsigned int len ;
2010-01-18 16:45:00 +03:00
struct port * port ;
2007-10-22 05:03:39 +04:00
2010-01-18 16:44:56 +03:00
if ( unlikely ( early_put_chars ) )
return early_put_chars ( vtermno , buf , count ) ;
2010-01-18 16:45:00 +03:00
port = & console ;
2007-10-22 05:03:39 +04:00
/* This is a convenient routine to initialize a single-elem sg list */
sg_init_one ( sg , buf , count ) ;
2010-01-18 16:45:00 +03:00
/* This shouldn't fail: if it does, we lose chars. */
if ( port - > out_vq - > vq_ops - > add_buf ( port - > out_vq , sg , 1 , 0 , port ) > = 0 ) {
2007-10-22 05:03:39 +04:00
/* Tell Host to go! */
2010-01-18 16:45:00 +03:00
port - > out_vq - > vq_ops - > kick ( port - > out_vq ) ;
while ( ! port - > out_vq - > vq_ops - > get_buf ( port - > out_vq , & len ) )
2007-10-22 05:03:39 +04:00
cpu_relax ( ) ;
}
/* We're expected to return the amount of data we wrote: all of it. */
return count ;
}
2010-01-18 16:44:55 +03:00
/*
* Create a scatter - gather list representing our input buffer and put
* it in the queue .
*/
2010-01-18 16:45:00 +03:00
static void add_inbuf ( struct port * port )
2007-10-22 05:03:39 +04:00
{
struct scatterlist sg [ 1 ] ;
2010-01-18 16:45:00 +03:00
sg_init_one ( sg , port - > inbuf , PAGE_SIZE ) ;
2007-10-22 05:03:39 +04:00
2010-01-18 16:45:00 +03:00
/* Should always be able to add one buffer to an empty queue. */
if ( port - > in_vq - > vq_ops - > add_buf ( port - > in_vq , sg , 0 , 1 , port ) < 0 )
2007-10-22 05:03:39 +04:00
BUG ( ) ;
2010-01-18 16:45:00 +03:00
port - > in_vq - > vq_ops - > kick ( port - > in_vq ) ;
2007-10-22 05:03:39 +04:00
}
2010-01-18 16:44:55 +03:00
/*
* get_chars ( ) is the callback from the hvc_console infrastructure
* when an interrupt is received .
2007-10-22 05:03:39 +04:00
*
2010-01-18 16:44:55 +03:00
* Most of the code deals with the fact that the hvc_console ( )
* infrastructure only asks us for 16 bytes at a time . We keep
* in_offset and in_used fields for partially - filled buffers .
*/
2007-10-22 05:03:39 +04:00
static int get_chars ( u32 vtermno , char * buf , int count )
{
2010-01-18 16:45:00 +03:00
struct port * port ;
port = & console ;
2007-10-22 05:03:39 +04:00
/* If we don't have an input queue yet, we can't get input. */
2010-01-18 16:45:00 +03:00
BUG_ON ( ! port - > in_vq ) ;
2007-10-22 05:03:39 +04:00
2010-01-18 16:45:00 +03:00
/* No more in buffer? See if they've (re)used it. */
if ( port - > offset = = port - > used_len ) {
if ( ! port - > in_vq - > vq_ops - > get_buf ( port - > in_vq , & port - > used_len ) )
2007-10-22 05:03:39 +04:00
return 0 ;
2010-01-18 16:45:00 +03:00
port - > offset = 0 ;
2007-10-22 05:03:39 +04:00
}
/* You want more than we have to give? Well, try wanting less! */
2010-01-18 16:45:00 +03:00
if ( port - > offset + count > port - > used_len )
count = port - > used_len - port - > offset ;
2007-10-22 05:03:39 +04:00
/* Copy across to their buffer and increment offset. */
2010-01-18 16:45:00 +03:00
memcpy ( buf , port - > inbuf + port - > offset , count ) ;
port - > offset + = count ;
2007-10-22 05:03:39 +04:00
/* Finished? Re-register buffer so Host will use it again. */
2010-01-18 16:45:00 +03:00
if ( port - > offset = = port - > used_len )
add_inbuf ( port ) ;
2007-10-22 05:03:39 +04:00
return count ;
}
2008-11-25 15:36:26 +03:00
/*
* virtio console configuration . This supports :
* - console resize
*/
static void virtcons_apply_config ( struct virtio_device * dev )
{
struct winsize ws ;
if ( virtio_has_feature ( dev , VIRTIO_CONSOLE_F_SIZE ) ) {
dev - > config - > get ( dev ,
offsetof ( struct virtio_console_config , cols ) ,
& ws . ws_col , sizeof ( u16 ) ) ;
dev - > config - > get ( dev ,
offsetof ( struct virtio_console_config , rows ) ,
& ws . ws_row , sizeof ( u16 ) ) ;
2010-01-18 16:45:00 +03:00
hvc_resize ( console . hvc , ws ) ;
2008-11-25 15:36:26 +03:00
}
}
2008-06-20 17:24:15 +04:00
/*
2010-01-18 16:44:55 +03:00
* we support only one console , the hvc struct is a global var We set
* the configuration at this point , since we now have a tty
2008-06-20 17:24:15 +04:00
*/
static int notifier_add_vio ( struct hvc_struct * hp , int data )
{
hp - > irq_requested = 1 ;
2010-01-18 16:45:00 +03:00
virtcons_apply_config ( console . vdev ) ;
2008-11-25 15:36:26 +03:00
2008-06-20 17:24:15 +04:00
return 0 ;
}
static void notifier_del_vio ( struct hvc_struct * hp , int data )
{
hp - > irq_requested = 0 ;
}
static void hvc_handle_input ( struct virtqueue * vq )
{
2010-01-18 16:45:00 +03:00
if ( hvc_poll ( console . hvc ) )
2008-06-20 17:24:15 +04:00
hvc_kick ( ) ;
}
2010-01-18 16:44:56 +03:00
/* The operations for the console. */
2009-11-28 09:50:26 +03:00
static const struct hv_ops hv_ops = {
2010-01-18 16:44:56 +03:00
. get_chars = get_chars ,
. put_chars = put_chars ,
. notifier_add = notifier_add_vio ,
. notifier_del = notifier_del_vio ,
. notifier_hangup = notifier_del_vio ,
} ;
/*
* Console drivers are initialized very early so boot messages can go
* out , so we do things slightly differently from the generic virtio
* initialization of the net and block drivers .
*
* At this stage , the console is output - only . It ' s too early to set
* up a virtqueue , so we let the drivers do some boutique early - output
* thing .
*/
int __init virtio_cons_early_init ( int ( * put_chars ) ( u32 , const char * , int ) )
{
early_put_chars = put_chars ;
return hvc_instantiate ( 0 , 0 , & hv_ops ) ;
}
2010-01-18 16:44:55 +03:00
/*
* Once we ' re further in boot , we get probed like any other virtio
* device . At this stage we set up the output virtqueue .
2007-10-22 05:03:39 +04:00
*
2010-01-18 16:44:55 +03:00
* To set up and manage our virtual console , we call hvc_alloc ( ) .
* Since we never remove the console device we never need this pointer
* again .
2007-10-22 05:03:39 +04:00
*
2010-01-18 16:44:55 +03:00
* Finally we put our input buffer in the input queue , ready to
* receive .
*/
2010-01-18 16:45:00 +03:00
static int __devinit virtcons_probe ( struct virtio_device * vdev )
2007-10-22 05:03:39 +04:00
{
2009-06-13 08:16:36 +04:00
vq_callback_t * callbacks [ ] = { hvc_handle_input , NULL } ;
const char * names [ ] = { " input " , " output " } ;
struct virtqueue * vqs [ 2 ] ;
2010-01-18 16:45:00 +03:00
struct port * port ;
2007-10-22 05:03:39 +04:00
int err ;
2010-01-18 16:45:00 +03:00
port = & console ;
if ( port - > vdev ) {
dev_warn ( & port - > vdev - > dev ,
2010-01-18 16:44:59 +03:00
" Multiple virtio-console devices not supported yet \n " ) ;
return - EEXIST ;
}
2010-01-18 16:45:00 +03:00
port - > vdev = vdev ;
2007-10-22 05:03:39 +04:00
/* This is the scratch page we use to receive console input */
2010-01-18 16:45:00 +03:00
port - > used_len = 0 ;
port - > inbuf = kmalloc ( PAGE_SIZE , GFP_KERNEL ) ;
if ( ! port - > inbuf ) {
2007-10-22 05:03:39 +04:00
err = - ENOMEM ;
goto fail ;
}
2009-06-13 08:16:36 +04:00
/* Find the queues. */
err = vdev - > config - > find_vqs ( vdev , 2 , vqs , callbacks , names ) ;
if ( err )
2007-10-22 05:03:39 +04:00
goto free ;
2010-01-18 16:45:00 +03:00
port - > in_vq = vqs [ 0 ] ;
port - > out_vq = vqs [ 1 ] ;
2007-10-22 05:03:39 +04:00
2010-01-18 16:44:55 +03:00
/*
* The first argument of hvc_alloc ( ) is the virtual console
* number , so we use zero . The second argument is the
* parameter for the notification mechanism ( like irq
* number ) . We currently leave this as zero , virtqueues have
* implicit notifications .
2007-10-22 05:03:39 +04:00
*
2010-01-18 16:44:55 +03:00
* The third argument is a " struct hv_ops " containing the
* put_chars ( ) , get_chars ( ) , notifier_add ( ) and notifier_del ( )
* pointers . The final argument is the output buffer size : we
* can do any size , so we put PAGE_SIZE here .
*/
2010-01-18 16:45:00 +03:00
port - > hvc = hvc_alloc ( 0 , 0 , & hv_ops , PAGE_SIZE ) ;
if ( IS_ERR ( port - > hvc ) ) {
err = PTR_ERR ( port - > hvc ) ;
2009-06-13 08:16:36 +04:00
goto free_vqs ;
2007-10-22 05:03:39 +04:00
}
/* Register the input buffer the first time. */
2010-01-18 16:45:00 +03:00
add_inbuf ( port ) ;
2010-01-18 16:44:56 +03:00
/* Start using the new console output. */
early_put_chars = NULL ;
2007-10-22 05:03:39 +04:00
return 0 ;
2009-06-13 08:16:36 +04:00
free_vqs :
vdev - > config - > del_vqs ( vdev ) ;
2007-10-22 05:03:39 +04:00
free :
2010-01-18 16:45:00 +03:00
kfree ( port - > inbuf ) ;
2007-10-22 05:03:39 +04:00
fail :
return err ;
}
static struct virtio_device_id id_table [ ] = {
{ VIRTIO_ID_CONSOLE , VIRTIO_DEV_ANY_ID } ,
{ 0 } ,
} ;
2008-11-25 15:36:26 +03:00
static unsigned int features [ ] = {
VIRTIO_CONSOLE_F_SIZE ,
} ;
2007-10-22 05:03:39 +04:00
static struct virtio_driver virtio_console = {
2008-11-25 15:36:26 +03:00
. feature_table = features ,
. feature_table_size = ARRAY_SIZE ( features ) ,
2007-10-22 05:03:39 +04:00
. driver . name = KBUILD_MODNAME ,
. driver . owner = THIS_MODULE ,
. id_table = id_table ,
. probe = virtcons_probe ,
2008-11-25 15:36:26 +03:00
. config_changed = virtcons_apply_config ,
2007-10-22 05:03:39 +04:00
} ;
static int __init init ( void )
{
return register_virtio_driver ( & virtio_console ) ;
}
module_init ( init ) ;
MODULE_DEVICE_TABLE ( virtio , id_table ) ;
MODULE_DESCRIPTION ( " Virtio console driver " ) ;
MODULE_LICENSE ( " GPL " ) ;