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"
static struct virtqueue * in_vq , * out_vq ;
static struct virtio_device * vdev ;
/* This is our input buffer, and how much data is left in it. */
static unsigned int in_len ;
static char * in , * inbuf ;
2008-06-20 17:24:15 +04:00
/* The hvc device */
static struct hvc_struct * hvc ;
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:44:56 +03:00
if ( unlikely ( early_put_chars ) )
return early_put_chars ( vtermno , buf , count ) ;
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:44:55 +03:00
/*
* add_buf wants a token to identify this buffer : we hand it
* any non - NULL pointer , since there ' s only ever one buffer .
*/
2009-09-24 08:26:31 +04:00
if ( out_vq - > vq_ops - > add_buf ( out_vq , sg , 1 , 0 , ( void * ) 1 ) > = 0 ) {
2007-10-22 05:03:39 +04:00
/* Tell Host to go! */
out_vq - > vq_ops - > kick ( out_vq ) ;
/* Chill out until it's done with the buffer. */
while ( ! out_vq - > vq_ops - > get_buf ( out_vq , & len ) )
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 .
*/
2007-10-22 05:03:39 +04:00
static void add_inbuf ( void )
{
struct scatterlist sg [ 1 ] ;
sg_init_one ( sg , inbuf , PAGE_SIZE ) ;
/* We should always be able to add one buffer to an empty queue. */
2009-09-24 08:26:31 +04:00
if ( in_vq - > vq_ops - > add_buf ( in_vq , sg , 0 , 1 , inbuf ) < 0 )
2007-10-22 05:03:39 +04:00
BUG ( ) ;
in_vq - > vq_ops - > kick ( in_vq ) ;
}
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 )
{
/* If we don't have an input queue yet, we can't get input. */
BUG_ON ( ! in_vq ) ;
/* No buffer? Try to get one. */
if ( ! in_len ) {
in = in_vq - > vq_ops - > get_buf ( in_vq , & in_len ) ;
if ( ! in )
return 0 ;
}
/* You want more than we have to give? Well, try wanting less! */
if ( in_len < count )
count = in_len ;
/* Copy across to their buffer and increment offset. */
memcpy ( buf , in , count ) ;
in + = count ;
in_len - = count ;
/* Finished? Re-register buffer so Host will use it again. */
if ( in_len = = 0 )
add_inbuf ( ) ;
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 ) ) ;
hvc_resize ( hvc , ws ) ;
}
}
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 ;
2008-11-25 15:36:26 +03:00
virtcons_apply_config ( vdev ) ;
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 )
{
if ( hvc_poll ( hvc ) )
hvc_kick ( ) ;
}
2010-01-18 16:44:56 +03:00
/* The operations for the console. */
static struct hv_ops hv_ops = {
. 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 .
*/
2007-11-06 01:51:01 +03:00
static int __devinit virtcons_probe ( struct virtio_device * dev )
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 ] ;
2007-10-22 05:03:39 +04:00
int err ;
vdev = dev ;
/* This is the scratch page we use to receive console input */
inbuf = kmalloc ( PAGE_SIZE , GFP_KERNEL ) ;
if ( ! inbuf ) {
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 ;
2009-06-13 08:16:36 +04:00
in_vq = vqs [ 0 ] ;
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:44:56 +03:00
hvc = hvc_alloc ( 0 , 0 , & hv_ops , PAGE_SIZE ) ;
2007-10-22 05:03:39 +04:00
if ( IS_ERR ( hvc ) ) {
err = PTR_ERR ( 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. */
add_inbuf ( ) ;
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 :
kfree ( inbuf ) ;
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 " ) ;