2010-04-08 09:31:15 +02:00
/*
* f_hid . c - - USB HID function driver
*
* Copyright ( C ) 2010 Fabien Chouteau < fabien . chouteau @ barco . com >
*
* 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/kernel.h>
# include <linux/utsname.h>
# include <linux/module.h>
# include <linux/hid.h>
# include <linux/cdev.h>
# include <linux/mutex.h>
# include <linux/poll.h>
# include <linux/smp_lock.h>
# include <linux/uaccess.h>
# include <linux/wait.h>
# include <linux/usb/g_hid.h>
static int major , minors ;
static struct class * hidg_class ;
/*-------------------------------------------------------------------------*/
/* HID gadget struct */
struct f_hidg {
/* configuration */
unsigned char bInterfaceSubClass ;
unsigned char bInterfaceProtocol ;
unsigned short report_desc_length ;
char * report_desc ;
unsigned short report_length ;
/* recv report */
char * set_report_buff ;
unsigned short set_report_length ;
spinlock_t spinlock ;
wait_queue_head_t read_queue ;
/* send report */
struct mutex lock ;
bool write_pending ;
wait_queue_head_t write_queue ;
struct usb_request * req ;
int minor ;
struct cdev cdev ;
struct usb_function func ;
struct usb_ep * in_ep ;
struct usb_endpoint_descriptor * fs_in_ep_desc ;
struct usb_endpoint_descriptor * hs_in_ep_desc ;
} ;
static inline struct f_hidg * func_to_hidg ( struct usb_function * f )
{
return container_of ( f , struct f_hidg , func ) ;
}
/*-------------------------------------------------------------------------*/
/* Static descriptors */
static struct usb_interface_descriptor hidg_interface_desc = {
. bLength = sizeof hidg_interface_desc ,
. bDescriptorType = USB_DT_INTERFACE ,
/* .bInterfaceNumber = DYNAMIC */
. bAlternateSetting = 0 ,
. bNumEndpoints = 1 ,
. bInterfaceClass = USB_CLASS_HID ,
/* .bInterfaceSubClass = DYNAMIC */
/* .bInterfaceProtocol = DYNAMIC */
/* .iInterface = DYNAMIC */
} ;
static struct hid_descriptor hidg_desc = {
. bLength = sizeof hidg_desc ,
. bDescriptorType = HID_DT_HID ,
. bcdHID = 0x0101 ,
. bCountryCode = 0x00 ,
. bNumDescriptors = 0x1 ,
/*.desc[0].bDescriptorType = DYNAMIC */
/*.desc[0].wDescriptorLenght = DYNAMIC */
} ;
/* High-Speed Support */
static struct usb_endpoint_descriptor hidg_hs_in_ep_desc = {
. bLength = USB_DT_ENDPOINT_SIZE ,
. bDescriptorType = USB_DT_ENDPOINT ,
. bEndpointAddress = USB_DIR_IN ,
. bmAttributes = USB_ENDPOINT_XFER_INT ,
/*.wMaxPacketSize = DYNAMIC */
. bInterval = 4 , /* FIXME: Add this field in the
* HID gadget configuration ?
* ( struct hidg_func_descriptor )
*/
} ;
static struct usb_descriptor_header * hidg_hs_descriptors [ ] = {
( struct usb_descriptor_header * ) & hidg_interface_desc ,
( struct usb_descriptor_header * ) & hidg_desc ,
( struct usb_descriptor_header * ) & hidg_hs_in_ep_desc ,
NULL ,
} ;
/* Full-Speed Support */
static struct usb_endpoint_descriptor hidg_fs_in_ep_desc = {
. bLength = USB_DT_ENDPOINT_SIZE ,
. bDescriptorType = USB_DT_ENDPOINT ,
. bEndpointAddress = USB_DIR_IN ,
. bmAttributes = USB_ENDPOINT_XFER_INT ,
/*.wMaxPacketSize = DYNAMIC */
. bInterval = 10 , /* FIXME: Add this field in the
* HID gadget configuration ?
* ( struct hidg_func_descriptor )
*/
} ;
static struct usb_descriptor_header * hidg_fs_descriptors [ ] = {
( struct usb_descriptor_header * ) & hidg_interface_desc ,
( struct usb_descriptor_header * ) & hidg_desc ,
( struct usb_descriptor_header * ) & hidg_fs_in_ep_desc ,
NULL ,
} ;
/*-------------------------------------------------------------------------*/
/* Char Device */
static ssize_t f_hidg_read ( struct file * file , char __user * buffer ,
size_t count , loff_t * ptr )
{
2010-07-12 13:50:11 -07:00
struct f_hidg * hidg = file - > private_data ;
2010-04-08 09:31:15 +02:00
char * tmp_buff = NULL ;
unsigned long flags ;
if ( ! count )
return 0 ;
if ( ! access_ok ( VERIFY_WRITE , buffer , count ) )
return - EFAULT ;
spin_lock_irqsave ( & hidg - > spinlock , flags ) ;
# define READ_COND (hidg->set_report_buff != NULL)
while ( ! READ_COND ) {
spin_unlock_irqrestore ( & hidg - > spinlock , flags ) ;
if ( file - > f_flags & O_NONBLOCK )
return - EAGAIN ;
if ( wait_event_interruptible ( hidg - > read_queue , READ_COND ) )
return - ERESTARTSYS ;
spin_lock_irqsave ( & hidg - > spinlock , flags ) ;
}
count = min_t ( unsigned , count , hidg - > set_report_length ) ;
tmp_buff = hidg - > set_report_buff ;
hidg - > set_report_buff = NULL ;
spin_unlock_irqrestore ( & hidg - > spinlock , flags ) ;
if ( tmp_buff ! = NULL ) {
/* copy to user outside spinlock */
count - = copy_to_user ( buffer , tmp_buff , count ) ;
kfree ( tmp_buff ) ;
} else
count = - ENOMEM ;
return count ;
}
static void f_hidg_req_complete ( struct usb_ep * ep , struct usb_request * req )
{
struct f_hidg * hidg = ( struct f_hidg * ) ep - > driver_data ;
if ( req - > status ! = 0 ) {
ERROR ( hidg - > func . config - > cdev ,
" End Point Request ERROR: %d \n " , req - > status ) ;
}
hidg - > write_pending = 0 ;
wake_up ( & hidg - > write_queue ) ;
}
static ssize_t f_hidg_write ( struct file * file , const char __user * buffer ,
size_t count , loff_t * offp )
{
2010-07-12 13:50:11 -07:00
struct f_hidg * hidg = file - > private_data ;
2010-04-08 09:31:15 +02:00
ssize_t status = - ENOMEM ;
if ( ! access_ok ( VERIFY_READ , buffer , count ) )
return - EFAULT ;
mutex_lock ( & hidg - > lock ) ;
# define WRITE_COND (!hidg->write_pending)
/* write queue */
while ( ! WRITE_COND ) {
mutex_unlock ( & hidg - > lock ) ;
if ( file - > f_flags & O_NONBLOCK )
return - EAGAIN ;
if ( wait_event_interruptible_exclusive (
hidg - > write_queue , WRITE_COND ) )
return - ERESTARTSYS ;
mutex_lock ( & hidg - > lock ) ;
}
count = min_t ( unsigned , count , hidg - > report_length ) ;
status = copy_from_user ( hidg - > req - > buf , buffer , count ) ;
if ( status ! = 0 ) {
ERROR ( hidg - > func . config - > cdev ,
" copy_from_user error \n " ) ;
mutex_unlock ( & hidg - > lock ) ;
return - EINVAL ;
}
hidg - > req - > status = 0 ;
hidg - > req - > zero = 0 ;
hidg - > req - > length = count ;
hidg - > req - > complete = f_hidg_req_complete ;
hidg - > req - > context = hidg ;
hidg - > write_pending = 1 ;
status = usb_ep_queue ( hidg - > in_ep , hidg - > req , GFP_ATOMIC ) ;
if ( status < 0 ) {
ERROR ( hidg - > func . config - > cdev ,
" usb_ep_queue error on int endpoint %zd \n " , status ) ;
hidg - > write_pending = 0 ;
wake_up ( & hidg - > write_queue ) ;
} else {
status = count ;
}
mutex_unlock ( & hidg - > lock ) ;
return status ;
}
static unsigned int f_hidg_poll ( struct file * file , poll_table * wait )
{
2010-07-12 13:50:11 -07:00
struct f_hidg * hidg = file - > private_data ;
2010-04-08 09:31:15 +02:00
unsigned int ret = 0 ;
poll_wait ( file , & hidg - > read_queue , wait ) ;
poll_wait ( file , & hidg - > write_queue , wait ) ;
if ( WRITE_COND )
ret | = POLLOUT | POLLWRNORM ;
if ( READ_COND )
ret | = POLLIN | POLLRDNORM ;
return ret ;
}
# undef WRITE_COND
# undef READ_COND
static int f_hidg_release ( struct inode * inode , struct file * fd )
{
fd - > private_data = NULL ;
return 0 ;
}
static int f_hidg_open ( struct inode * inode , struct file * fd )
{
struct f_hidg * hidg =
container_of ( inode - > i_cdev , struct f_hidg , cdev ) ;
fd - > private_data = hidg ;
return 0 ;
}
/*-------------------------------------------------------------------------*/
/* usb_function */
static void hidg_set_report_complete ( struct usb_ep * ep , struct usb_request * req )
{
struct f_hidg * hidg = ( struct f_hidg * ) req - > context ;
if ( req - > status ! = 0 | | req - > buf = = NULL | | req - > actual = = 0 ) {
ERROR ( hidg - > func . config - > cdev , " %s FAILED \n " , __func__ ) ;
return ;
}
spin_lock ( & hidg - > spinlock ) ;
hidg - > set_report_buff = krealloc ( hidg - > set_report_buff ,
req - > actual , GFP_ATOMIC ) ;
if ( hidg - > set_report_buff = = NULL ) {
spin_unlock ( & hidg - > spinlock ) ;
return ;
}
hidg - > set_report_length = req - > actual ;
memcpy ( hidg - > set_report_buff , req - > buf , req - > actual ) ;
spin_unlock ( & hidg - > spinlock ) ;
wake_up ( & hidg - > read_queue ) ;
return ;
}
static int hidg_setup ( struct usb_function * f ,
const struct usb_ctrlrequest * ctrl )
{
struct f_hidg * hidg = func_to_hidg ( f ) ;
struct usb_composite_dev * cdev = f - > config - > cdev ;
struct usb_request * req = cdev - > req ;
int status = 0 ;
__u16 value , length ;
value = __le16_to_cpu ( ctrl - > wValue ) ;
length = __le16_to_cpu ( ctrl - > wLength ) ;
VDBG ( cdev , " hid_setup crtl_request : bRequestType:0x%x bRequest:0x%x "
" Value:0x%x \n " , ctrl - > bRequestType , ctrl - > bRequest , value ) ;
switch ( ( ctrl - > bRequestType < < 8 ) | ctrl - > bRequest ) {
case ( ( USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE ) < < 8
| HID_REQ_GET_REPORT ) :
VDBG ( cdev , " get_report \n " ) ;
/* send an empty report */
length = min_t ( unsigned , length , hidg - > report_length ) ;
memset ( req - > buf , 0x0 , length ) ;
goto respond ;
break ;
case ( ( USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE ) < < 8
| HID_REQ_GET_PROTOCOL ) :
VDBG ( cdev , " get_protocol \n " ) ;
goto stall ;
break ;
case ( ( USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE ) < < 8
| HID_REQ_SET_REPORT ) :
VDBG ( cdev , " set_report | wLenght=%d \n " , ctrl - > wLength ) ;
req - > context = hidg ;
req - > complete = hidg_set_report_complete ;
goto respond ;
break ;
case ( ( USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE ) < < 8
| HID_REQ_SET_PROTOCOL ) :
VDBG ( cdev , " set_protocol \n " ) ;
goto stall ;
break ;
case ( ( USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_INTERFACE ) < < 8
| USB_REQ_GET_DESCRIPTOR ) :
switch ( value > > 8 ) {
case HID_DT_REPORT :
VDBG ( cdev , " USB_REQ_GET_DESCRIPTOR: REPORT \n " ) ;
length = min_t ( unsigned short , length ,
hidg - > report_desc_length ) ;
memcpy ( req - > buf , hidg - > report_desc , length ) ;
goto respond ;
break ;
default :
VDBG ( cdev , " Unknown decriptor request 0x%x \n " ,
value > > 8 ) ;
goto stall ;
break ;
}
break ;
default :
VDBG ( cdev , " Unknown request 0x%x \n " ,
ctrl - > bRequest ) ;
goto stall ;
break ;
}
stall :
return - EOPNOTSUPP ;
respond :
req - > zero = 0 ;
req - > length = length ;
status = usb_ep_queue ( cdev - > gadget - > ep0 , req , GFP_ATOMIC ) ;
if ( status < 0 )
ERROR ( cdev , " usb_ep_queue error on ep0 %d \n " , value ) ;
return status ;
}
static void hidg_disable ( struct usb_function * f )
{
struct f_hidg * hidg = func_to_hidg ( f ) ;
usb_ep_disable ( hidg - > in_ep ) ;
hidg - > in_ep - > driver_data = NULL ;
return ;
}
static int hidg_set_alt ( struct usb_function * f , unsigned intf , unsigned alt )
{
struct usb_composite_dev * cdev = f - > config - > cdev ;
struct f_hidg * hidg = func_to_hidg ( f ) ;
const struct usb_endpoint_descriptor * ep_desc ;
int status = 0 ;
VDBG ( cdev , " hidg_set_alt intf:%d alt:%d \n " , intf , alt ) ;
if ( hidg - > in_ep ! = NULL ) {
/* restart endpoint */
if ( hidg - > in_ep - > driver_data ! = NULL )
usb_ep_disable ( hidg - > in_ep ) ;
ep_desc = ep_choose ( f - > config - > cdev - > gadget ,
hidg - > hs_in_ep_desc , hidg - > fs_in_ep_desc ) ;
status = usb_ep_enable ( hidg - > in_ep , ep_desc ) ;
if ( status < 0 ) {
ERROR ( cdev , " Enable endpoint FAILED! \n " ) ;
goto fail ;
}
hidg - > in_ep - > driver_data = hidg ;
}
fail :
return status ;
}
const struct file_operations f_hidg_fops = {
. owner = THIS_MODULE ,
. open = f_hidg_open ,
. release = f_hidg_release ,
. write = f_hidg_write ,
. read = f_hidg_read ,
. poll = f_hidg_poll ,
} ;
static int __init hidg_bind ( struct usb_configuration * c , struct usb_function * f )
{
struct usb_ep * ep ;
struct f_hidg * hidg = func_to_hidg ( f ) ;
int status ;
dev_t dev ;
/* allocate instance-specific interface IDs, and patch descriptors */
status = usb_interface_id ( c , f ) ;
if ( status < 0 )
goto fail ;
hidg_interface_desc . bInterfaceNumber = status ;
/* allocate instance-specific endpoints */
status = - ENODEV ;
ep = usb_ep_autoconfig ( c - > cdev - > gadget , & hidg_fs_in_ep_desc ) ;
if ( ! ep )
goto fail ;
ep - > driver_data = c - > cdev ; /* claim */
hidg - > in_ep = ep ;
/* preallocate request and buffer */
status = - ENOMEM ;
hidg - > req = usb_ep_alloc_request ( hidg - > in_ep , GFP_KERNEL ) ;
if ( ! hidg - > req )
goto fail ;
hidg - > req - > buf = kmalloc ( hidg - > report_length , GFP_KERNEL ) ;
if ( ! hidg - > req - > buf )
goto fail ;
/* set descriptor dynamic values */
hidg_interface_desc . bInterfaceSubClass = hidg - > bInterfaceSubClass ;
hidg_interface_desc . bInterfaceProtocol = hidg - > bInterfaceProtocol ;
hidg_hs_in_ep_desc . wMaxPacketSize = cpu_to_le16 ( hidg - > report_length ) ;
hidg_fs_in_ep_desc . wMaxPacketSize = cpu_to_le16 ( hidg - > report_length ) ;
hidg_desc . desc [ 0 ] . bDescriptorType = HID_DT_REPORT ;
hidg_desc . desc [ 0 ] . wDescriptorLength =
cpu_to_le16 ( hidg - > report_desc_length ) ;
hidg - > set_report_buff = NULL ;
/* copy descriptors */
f - > descriptors = usb_copy_descriptors ( hidg_fs_descriptors ) ;
if ( ! f - > descriptors )
goto fail ;
hidg - > fs_in_ep_desc = usb_find_endpoint ( hidg_fs_descriptors ,
f - > descriptors ,
& hidg_fs_in_ep_desc ) ;
if ( gadget_is_dualspeed ( c - > cdev - > gadget ) ) {
hidg_hs_in_ep_desc . bEndpointAddress =
hidg_fs_in_ep_desc . bEndpointAddress ;
f - > hs_descriptors = usb_copy_descriptors ( hidg_hs_descriptors ) ;
if ( ! f - > hs_descriptors )
goto fail ;
hidg - > hs_in_ep_desc = usb_find_endpoint ( hidg_hs_descriptors ,
f - > hs_descriptors ,
& hidg_hs_in_ep_desc ) ;
} else {
hidg - > hs_in_ep_desc = NULL ;
}
mutex_init ( & hidg - > lock ) ;
spin_lock_init ( & hidg - > spinlock ) ;
init_waitqueue_head ( & hidg - > write_queue ) ;
init_waitqueue_head ( & hidg - > read_queue ) ;
/* create char device */
cdev_init ( & hidg - > cdev , & f_hidg_fops ) ;
dev = MKDEV ( major , hidg - > minor ) ;
status = cdev_add ( & hidg - > cdev , dev , 1 ) ;
if ( status )
goto fail ;
device_create ( hidg_class , NULL , dev , NULL , " %s%d " , " hidg " , hidg - > minor ) ;
return 0 ;
fail :
ERROR ( f - > config - > cdev , " hidg_bind FAILED \n " ) ;
if ( hidg - > req ! = NULL ) {
kfree ( hidg - > req - > buf ) ;
if ( hidg - > in_ep ! = NULL )
usb_ep_free_request ( hidg - > in_ep , hidg - > req ) ;
}
usb_free_descriptors ( f - > hs_descriptors ) ;
usb_free_descriptors ( f - > descriptors ) ;
return status ;
}
static void hidg_unbind ( struct usb_configuration * c , struct usb_function * f )
{
struct f_hidg * hidg = func_to_hidg ( f ) ;
device_destroy ( hidg_class , MKDEV ( major , hidg - > minor ) ) ;
cdev_del ( & hidg - > cdev ) ;
/* disable/free request and end point */
usb_ep_disable ( hidg - > in_ep ) ;
usb_ep_dequeue ( hidg - > in_ep , hidg - > req ) ;
kfree ( hidg - > req - > buf ) ;
usb_ep_free_request ( hidg - > in_ep , hidg - > req ) ;
/* free descriptors copies */
usb_free_descriptors ( f - > hs_descriptors ) ;
usb_free_descriptors ( f - > descriptors ) ;
kfree ( hidg - > report_desc ) ;
kfree ( hidg - > set_report_buff ) ;
kfree ( hidg ) ;
}
/*-------------------------------------------------------------------------*/
/* Strings */
# define CT_FUNC_HID_IDX 0
static struct usb_string ct_func_string_defs [ ] = {
[ CT_FUNC_HID_IDX ] . s = " HID Interface " ,
{ } , /* end of list */
} ;
static struct usb_gadget_strings ct_func_string_table = {
. language = 0x0409 , /* en-US */
. strings = ct_func_string_defs ,
} ;
static struct usb_gadget_strings * ct_func_strings [ ] = {
& ct_func_string_table ,
NULL ,
} ;
/*-------------------------------------------------------------------------*/
/* usb_configuration */
int __init hidg_bind_config ( struct usb_configuration * c ,
struct hidg_func_descriptor * fdesc , int index )
{
struct f_hidg * hidg ;
int status ;
if ( index > = minors )
return - ENOENT ;
/* maybe allocate device-global string IDs, and patch descriptors */
if ( ct_func_string_defs [ CT_FUNC_HID_IDX ] . id = = 0 ) {
status = usb_string_id ( c - > cdev ) ;
if ( status < 0 )
return status ;
ct_func_string_defs [ CT_FUNC_HID_IDX ] . id = status ;
hidg_interface_desc . iInterface = status ;
}
/* allocate and initialize one new instance */
hidg = kzalloc ( sizeof * hidg , GFP_KERNEL ) ;
if ( ! hidg )
return - ENOMEM ;
hidg - > minor = index ;
hidg - > bInterfaceSubClass = fdesc - > subclass ;
hidg - > bInterfaceProtocol = fdesc - > protocol ;
hidg - > report_length = fdesc - > report_length ;
hidg - > report_desc_length = fdesc - > report_desc_length ;
hidg - > report_desc = kmemdup ( fdesc - > report_desc ,
fdesc - > report_desc_length ,
GFP_KERNEL ) ;
if ( ! hidg - > report_desc ) {
kfree ( hidg ) ;
return - ENOMEM ;
}
hidg - > func . name = " hid " ;
hidg - > func . strings = ct_func_strings ;
hidg - > func . bind = hidg_bind ;
hidg - > func . unbind = hidg_unbind ;
hidg - > func . set_alt = hidg_set_alt ;
hidg - > func . disable = hidg_disable ;
hidg - > func . setup = hidg_setup ;
status = usb_add_function ( c , & hidg - > func ) ;
if ( status )
kfree ( hidg ) ;
return status ;
}
int __init ghid_setup ( struct usb_gadget * g , int count )
{
int status ;
dev_t dev ;
hidg_class = class_create ( THIS_MODULE , " hidg " ) ;
status = alloc_chrdev_region ( & dev , 0 , count , " hidg " ) ;
if ( ! status ) {
major = MAJOR ( dev ) ;
minors = count ;
}
return status ;
}
void ghid_cleanup ( void )
{
if ( major ) {
unregister_chrdev_region ( MKDEV ( major , 0 ) , minors ) ;
major = minors = 0 ;
}
class_destroy ( hidg_class ) ;
hidg_class = NULL ;
}