@ -9,6 +9,20 @@
# include "u_f.h"
# include "u_os_desc.h"
# ifdef CONFIG_USB_CONFIGFS_UEVENT
# include <linux/platform_device.h>
# include <linux/kdev_t.h>
# include <linux/usb/ch9.h>
# include "u_fs.h"
# ifdef CONFIG_USB_CONFIGFS_F_ACC
extern int acc_ctrlrequest ( struct usb_composite_dev * cdev ,
const struct usb_ctrlrequest * ctrl ) ;
void acc_disconnect ( void ) ;
# endif
static struct class * android_class ;
# endif
int check_user_usb_string ( const char * name ,
struct usb_gadget_strings * stringtab_dev )
{
@ -60,6 +74,12 @@ struct gadget_info {
bool use_os_desc ;
char b_vendor_code ;
char qw_sign [ OS_STRING_QW_SIGN_LEN ] ;
# ifdef CONFIG_USB_CONFIGFS_UEVENT
bool connected ;
bool sw_connected ;
struct work_struct work ;
struct device * dev ;
# endif
} ;
static inline struct gadget_info * to_gadget_info ( struct config_item * item )
@ -265,7 +285,7 @@ static ssize_t gadget_dev_desc_UDC_store(struct config_item *item,
mutex_lock ( & gi - > lock ) ;
if ( ! strlen ( name ) ) {
if ( ! strlen ( name ) | | strcmp ( name , " none " ) = = 0 ) {
ret = unregister_gadget ( gi ) ;
if ( ret )
goto err ;
@ -1370,6 +1390,57 @@ err_comp_cleanup:
return ret ;
}
# ifdef CONFIG_USB_CONFIGFS_UEVENT
static void android_work ( struct work_struct * data )
{
struct gadget_info * gi = container_of ( data , struct gadget_info , work ) ;
struct usb_composite_dev * cdev = & gi - > cdev ;
char * disconnected [ 2 ] = { " USB_STATE=DISCONNECTED " , NULL } ;
char * connected [ 2 ] = { " USB_STATE=CONNECTED " , NULL } ;
char * configured [ 2 ] = { " USB_STATE=CONFIGURED " , NULL } ;
/* 0-connected 1-configured 2-disconnected*/
bool status [ 3 ] = { false , false , false } ;
unsigned long flags ;
bool uevent_sent = false ;
spin_lock_irqsave ( & cdev - > lock , flags ) ;
if ( cdev - > config )
status [ 1 ] = true ;
if ( gi - > connected ! = gi - > sw_connected ) {
if ( gi - > connected )
status [ 0 ] = true ;
else
status [ 2 ] = true ;
gi - > sw_connected = gi - > connected ;
}
spin_unlock_irqrestore ( & cdev - > lock , flags ) ;
if ( status [ 0 ] ) {
kobject_uevent_env ( & gi - > dev - > kobj , KOBJ_CHANGE , connected ) ;
pr_info ( " %s: sent uevent %s \n " , __func__ , connected [ 0 ] ) ;
uevent_sent = true ;
}
if ( status [ 1 ] ) {
kobject_uevent_env ( & gi - > dev - > kobj , KOBJ_CHANGE , configured ) ;
pr_info ( " %s: sent uevent %s \n " , __func__ , configured [ 0 ] ) ;
uevent_sent = true ;
}
if ( status [ 2 ] ) {
kobject_uevent_env ( & gi - > dev - > kobj , KOBJ_CHANGE , disconnected ) ;
pr_info ( " %s: sent uevent %s \n " , __func__ , disconnected [ 0 ] ) ;
uevent_sent = true ;
}
if ( ! uevent_sent ) {
pr_info ( " %s: did not send uevent (%d %d %p) \n " , __func__ ,
gi - > connected , gi - > sw_connected , cdev - > config ) ;
}
}
# endif
static void configfs_composite_unbind ( struct usb_gadget * gadget )
{
struct usb_composite_dev * cdev ;
@ -1389,14 +1460,78 @@ static void configfs_composite_unbind(struct usb_gadget *gadget)
set_gadget_data ( gadget , NULL ) ;
}
# ifdef CONFIG_USB_CONFIGFS_UEVENT
static int android_setup ( struct usb_gadget * gadget ,
const struct usb_ctrlrequest * c )
{
struct usb_composite_dev * cdev = get_gadget_data ( gadget ) ;
unsigned long flags ;
struct gadget_info * gi = container_of ( cdev , struct gadget_info , cdev ) ;
int value = - EOPNOTSUPP ;
struct usb_function_instance * fi ;
spin_lock_irqsave ( & cdev - > lock , flags ) ;
if ( ! gi - > connected ) {
gi - > connected = 1 ;
schedule_work ( & gi - > work ) ;
}
spin_unlock_irqrestore ( & cdev - > lock , flags ) ;
list_for_each_entry ( fi , & gi - > available_func , cfs_list ) {
if ( fi ! = NULL & & fi - > f ! = NULL & & fi - > f - > setup ! = NULL ) {
value = fi - > f - > setup ( fi - > f , c ) ;
if ( value > = 0 )
break ;
}
}
# ifdef CONFIG_USB_CONFIGFS_F_ACC
if ( value < 0 )
value = acc_ctrlrequest ( cdev , c ) ;
# endif
if ( value < 0 )
value = composite_setup ( gadget , c ) ;
spin_lock_irqsave ( & cdev - > lock , flags ) ;
if ( c - > bRequest = = USB_REQ_SET_CONFIGURATION & &
cdev - > config ) {
schedule_work ( & gi - > work ) ;
}
spin_unlock_irqrestore ( & cdev - > lock , flags ) ;
return value ;
}
static void android_disconnect ( struct usb_gadget * gadget )
{
struct usb_composite_dev * cdev = get_gadget_data ( gadget ) ;
struct gadget_info * gi = container_of ( cdev , struct gadget_info , cdev ) ;
/* accessory HID support can be active while the
accessory function is not actually enabled ,
so we need to inform it when we are disconnected .
*/
# ifdef CONFIG_USB_CONFIGFS_F_ACC
acc_disconnect ( ) ;
# endif
gi - > connected = 0 ;
schedule_work ( & gi - > work ) ;
composite_disconnect ( gadget ) ;
}
# endif
static const struct usb_gadget_driver configfs_driver_template = {
. bind = configfs_composite_bind ,
. unbind = configfs_composite_unbind ,
# ifdef CONFIG_USB_CONFIGFS_UEVENT
. setup = android_setup ,
. disconnect = android_disconnect ,
# else
. setup = composite_setup ,
. reset = composite_disconnect ,
. disconnect = composite_disconnect ,
# endif
. suspend = composite_suspend ,
. resume = composite_resume ,
@ -1456,6 +1591,12 @@ static struct config_group *gadgets_make(
gi - > composite . gadget_driver . function = kstrdup ( name , GFP_KERNEL ) ;
gi - > composite . name = gi - > composite . gadget_driver . function ;
# ifdef CONFIG_USB_CONFIGFS_UEVENT
INIT_WORK ( & gi - > work , android_work ) ;
gi - > dev = device_create ( android_class , NULL ,
MKDEV ( 0 , 0 ) , NULL , " android0 " ) ;
# endif
if ( ! gi - > composite . gadget_driver . function )
goto err ;
@ -1507,6 +1648,13 @@ static int __init gadget_cfs_init(void)
config_group_init ( & gadget_subsys . su_group ) ;
ret = configfs_register_subsystem ( & gadget_subsys ) ;
# ifdef CONFIG_USB_CONFIGFS_UEVENT
android_class = class_create ( THIS_MODULE , " android_usb " ) ;
if ( IS_ERR ( android_class ) )
return PTR_ERR ( android_class ) ;
# endif
return ret ;
}
module_init ( gadget_cfs_init ) ;