@ -1,33 +1,27 @@
/**
* udc . c - Core UDC Framework
* udc - core . c - Core UDC Framework
*
* Copyright ( C ) 2015 Texas Instruments Incorporated - http : //www.ti.com
*
* Copyright ( C ) 2010 Texas Instruments
* Author : Felipe Balbi < balbi @ ti . com >
*
* This program is free software : you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 of
* the License as published by the Free Software Foundation .
* Taken from Linux Kernel v3 .19 - rc1 ( drivers / usb / gadget / udc - core . c ) and ported
* to uboot .
*
* 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 .
* commit 02e8 c96627 : usb : gadget : udc : core : prepend udc_attach_driver with
* usb_
*
* You should have received a copy of the GNU General Public License
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
* SPDX - License - Identifier : GPL - 2.0
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/device.h>
# include <linux/list.h>
# include <linux/err.h>
# include <linux/dma-mapping.h>
# include <linux/workqueue.h>
# include <linux/compat.h>
# include <malloc.h>
# include <asm/cache.h>
# include <asm/dma-mapping.h>
# include <common.h>
# include <linux/usb/ch9.h>
# include <linux/usb/gadget.h>
# include <linux/usb.h>
/**
* struct usb_udc - describes one usb device controller
@ -48,38 +42,18 @@ struct usb_udc {
static struct class * udc_class ;
static LIST_HEAD ( udc_list ) ;
static DEFINE_MUTEX ( udc_lock ) ;
DEFINE_MUTEX ( udc_lock ) ;
/* ------------------------------------------------------------------------- */
# ifdef CONFIG_HAS_DMA
int usb_gadget_map_request ( struct usb_gadget * gadget ,
struct usb_request * req , int is_in )
{
if ( req - > length = = 0 )
return 0 ;
if ( req - > num_sgs ) {
int mapped ;
mapped = dma_map_sg ( & gadget - > dev , req - > sg , req - > num_sgs ,
is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE ) ;
if ( mapped = = 0 ) {
dev_err ( & gadget - > dev , " failed to map SGs \n " ) ;
return - EFAULT ;
}
req - > num_mapped_sgs = mapped ;
} else {
req - > dma = dma_map_single ( & gadget - > dev , req - > buf , req - > length ,
is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE ) ;
if ( dma_mapping_error ( & gadget - > dev , req - > dma ) ) {
dev_err ( & gadget - > dev , " failed to map buffer \n " ) ;
return - EFAULT ;
}
}
req - > dma = dma_map_single ( req - > buf , req - > length ,
is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE ) ;
return 0 ;
}
@ -91,20 +65,11 @@ void usb_gadget_unmap_request(struct usb_gadget *gadget,
if ( req - > length = = 0 )
return ;
if ( req - > num_mapped_sgs ) {
dma_unmap_sg ( & gadget - > dev , req - > sg , req - > num_mapped_sgs ,
is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE ) ;
req - > num_mapped_sgs = 0 ;
} else {
dma_unmap_single ( & gadget - > dev , req - > dma , req - > length ,
is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE ) ;
}
dma_unmap_single ( ( void * ) req - > dma , req - > length ,
is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE ) ;
}
EXPORT_SYMBOL_GPL ( usb_gadget_unmap_request ) ;
# endif /* CONFIG_HAS_DMA */
/* ------------------------------------------------------------------------- */
/**
@ -117,39 +82,16 @@ EXPORT_SYMBOL_GPL(usb_gadget_unmap_request);
void usb_gadget_giveback_request ( struct usb_ep * ep ,
struct usb_request * req )
{
if ( likely ( req - > status = = 0 ) )
usb_led_activity ( USB_LED_EVENT_GADGET ) ;
req - > complete ( ep , req ) ;
}
EXPORT_SYMBOL_GPL ( usb_gadget_giveback_request ) ;
/* ------------------------------------------------------------------------- */
static void usb_gadget_state_work ( struct work_struct * work )
{
struct usb_gadget * gadget = work_to_gadget ( work ) ;
struct usb_udc * udc = NULL ;
mutex_lock ( & udc_lock ) ;
list_for_each_entry ( udc , & udc_list , list )
if ( udc - > gadget = = gadget )
goto found ;
mutex_unlock ( & udc_lock ) ;
return ;
found :
mutex_unlock ( & udc_lock ) ;
sysfs_notify ( & udc - > dev . kobj , NULL , " state " ) ;
}
void usb_gadget_set_state ( struct usb_gadget * gadget ,
enum usb_device_state state )
{
gadget - > state = state ;
schedule_work ( & gadget - > work ) ;
}
EXPORT_SYMBOL_GPL ( usb_gadget_set_state ) ;
@ -219,17 +161,9 @@ static void usb_udc_release(struct device *dev)
struct usb_udc * udc ;
udc = container_of ( dev , struct usb_udc , dev ) ;
dev_dbg ( dev , " releasing '%s' \n " , dev_name ( dev ) ) ;
kfree ( udc ) ;
}
static const struct attribute_group * usb_udc_attr_groups [ ] ;
static void usb_udc_nop_release ( struct device * dev )
{
dev_vdbg ( dev , " %s \n " , __func__ ) ;
}
/**
* usb_add_gadget_udc_release - adds a new gadget to the udc class driver list
* @ parent : the parent device to this udc . Usually the controller driver ' s
@ -250,59 +184,23 @@ int usb_add_gadget_udc_release(struct device *parent, struct usb_gadget *gadget,
goto err1 ;
dev_set_name ( & gadget - > dev , " gadget " ) ;
INIT_WORK ( & gadget - > work , usb_gadget_state_work ) ;
gadget - > dev . parent = parent ;
# ifdef CONFIG_HAS_DMA
dma_set_coherent_mask ( & gadget - > dev , parent - > coherent_dma_mask ) ;
gadget - > dev . dma_parms = parent - > dma_parms ;
gadget - > dev . dma_mask = parent - > dma_mask ;
# endif
if ( release )
gadget - > dev . release = release ;
else
gadget - > dev . release = usb_udc_nop_release ;
ret = device_register ( & gadget - > dev ) ;
if ( ret )
goto err2 ;
device_initialize ( & udc - > dev ) ;
udc - > dev . release = usb_udc_release ;
udc - > dev . class = udc_class ;
udc - > dev . groups = usb_udc_attr_groups ;
udc - > dev . parent = parent ;
ret = dev_set_name ( & udc - > dev , " %s " , kobject_name ( & parent - > kobj ) ) ;
if ( ret )
goto err3 ;
udc - > gadget = gadget ;
mutex_lock ( & udc_lock ) ;
list_add_tail ( & udc - > list , & udc_list ) ;
ret = device_add ( & udc - > dev ) ;
if ( ret )
goto err4 ;
usb_gadget_set_state ( gadget , USB_STATE_NOTATTACHED ) ;
mutex_unlock ( & udc_lock ) ;
return 0 ;
err4 :
list_del ( & udc - > list ) ;
mutex_unlock ( & udc_lock ) ;
err3 :
put_device ( & udc - > dev ) ;
err2 :
put_device ( & gadget - > dev ) ;
kfree ( udc ) ;
err1 :
return ret ;
}
@ -327,16 +225,12 @@ static void usb_gadget_remove_driver(struct usb_udc *udc)
dev_dbg ( & udc - > dev , " unregistering UDC driver [%s] \n " ,
udc - > driver - > function ) ;
kobject_uevent ( & udc - > dev . kobj , KOBJ_CHANGE ) ;
usb_gadget_disconnect ( udc - > gadget ) ;
udc - > driver - > disconnect ( udc - > gadget ) ;
udc - > driver - > unbind ( udc - > gadget ) ;
usb_gadget_udc_stop ( udc ) ;
udc - > driver = NULL ;
udc - > dev . driver = NULL ;
udc - > gadget - > dev . driver = NULL ;
}
/**
@ -368,11 +262,6 @@ found:
if ( udc - > driver )
usb_gadget_remove_driver ( udc ) ;
kobject_uevent ( & udc - > dev . kobj , KOBJ_REMOVE ) ;
flush_work ( & gadget - > work ) ;
device_unregister ( & udc - > dev ) ;
device_unregister ( & gadget - > dev ) ;
}
EXPORT_SYMBOL_GPL ( usb_del_gadget_udc ) ;
@ -386,10 +275,8 @@ static int udc_bind_to_driver(struct usb_udc *udc, struct usb_gadget_driver *dri
driver - > function ) ;
udc - > driver = driver ;
udc - > dev . driver = & driver - > driver ;
udc - > gadget - > dev . driver = & driver - > driver ;
ret = driver - > bind ( udc - > gadget , driver ) ;
ret = driver - > bind ( udc - > gadget ) ;
if ( ret )
goto err1 ;
ret = usb_gadget_udc_start ( udc ) ;
@ -399,43 +286,14 @@ static int udc_bind_to_driver(struct usb_udc *udc, struct usb_gadget_driver *dri
}
usb_gadget_connect ( udc - > gadget ) ;
kobject_uevent ( & udc - > dev . kobj , KOBJ_CHANGE ) ;
return 0 ;
err1 :
if ( ret ! = - EISNAM )
dev_err ( & udc - > dev , " failed to start %s: %d \n " ,
udc - > driver - > function , ret ) ;
udc - > driver = NULL ;
udc - > dev . driver = NULL ;
udc - > gadget - > dev . driver = NULL ;
return ret ;
}
int usb_udc_attach_driver ( const char * name , struct usb_gadget_driver * driver )
{
struct usb_udc * udc = NULL ;
int ret = - ENODEV ;
mutex_lock ( & udc_lock ) ;
list_for_each_entry ( udc , & udc_list , list ) {
ret = strcmp ( name , dev_name ( & udc - > dev ) ) ;
if ( ! ret )
break ;
}
if ( ret ) {
ret = - ENODEV ;
goto out ;
}
if ( udc - > driver ) {
ret = - EBUSY ;
goto out ;
}
ret = udc_bind_to_driver ( udc , driver ) ;
out :
mutex_unlock ( & udc_lock ) ;
return ret ;
}
EXPORT_SYMBOL_GPL ( usb_udc_attach_driver ) ;
int usb_gadget_probe_driver ( struct usb_gadget_driver * driver )
{
@ -452,7 +310,7 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver)
goto found ;
}
pr_debug ( " couldn't find an available UDC \n " ) ;
printf ( " couldn't find an available UDC \n " ) ;
mutex_unlock ( & udc_lock ) ;
return - ENODEV ;
found :
@ -462,6 +320,12 @@ found:
}
EXPORT_SYMBOL_GPL ( usb_gadget_probe_driver ) ;
int usb_gadget_register_driver ( struct usb_gadget_driver * driver )
{
return usb_gadget_probe_driver ( driver ) ;
}
EXPORT_SYMBOL_GPL ( usb_gadget_register_driver ) ;
int usb_gadget_unregister_driver ( struct usb_gadget_driver * driver )
{
struct usb_udc * udc = NULL ;
@ -485,153 +349,6 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
}
EXPORT_SYMBOL_GPL ( usb_gadget_unregister_driver ) ;
/* ------------------------------------------------------------------------- */
static ssize_t usb_udc_srp_store ( struct device * dev ,
struct device_attribute * attr , const char * buf , size_t n )
{
struct usb_udc * udc = container_of ( dev , struct usb_udc , dev ) ;
if ( sysfs_streq ( buf , " 1 " ) )
usb_gadget_wakeup ( udc - > gadget ) ;
return n ;
}
static DEVICE_ATTR ( srp , S_IWUSR , NULL , usb_udc_srp_store ) ;
static ssize_t usb_udc_softconn_store ( struct device * dev ,
struct device_attribute * attr , const char * buf , size_t n )
{
struct usb_udc * udc = container_of ( dev , struct usb_udc , dev ) ;
if ( ! udc - > driver ) {
dev_err ( dev , " soft-connect without a gadget driver \n " ) ;
return - EOPNOTSUPP ;
}
if ( sysfs_streq ( buf , " connect " ) ) {
usb_gadget_udc_start ( udc ) ;
usb_gadget_connect ( udc - > gadget ) ;
} else if ( sysfs_streq ( buf , " disconnect " ) ) {
usb_gadget_disconnect ( udc - > gadget ) ;
udc - > driver - > disconnect ( udc - > gadget ) ;
usb_gadget_udc_stop ( udc ) ;
} else {
dev_err ( dev , " unsupported command '%s' \n " , buf ) ;
return - EINVAL ;
}
return n ;
}
static DEVICE_ATTR ( soft_connect , S_IWUSR , NULL , usb_udc_softconn_store ) ;
static ssize_t state_show ( struct device * dev , struct device_attribute * attr ,
char * buf )
{
struct usb_udc * udc = container_of ( dev , struct usb_udc , dev ) ;
struct usb_gadget * gadget = udc - > gadget ;
return sprintf ( buf , " %s \n " , usb_state_string ( gadget - > state ) ) ;
}
static DEVICE_ATTR_RO ( state ) ;
# define USB_UDC_SPEED_ATTR(name, param) \
ssize_t name # # _show ( struct device * dev , \
struct device_attribute * attr , char * buf ) \
{ \
struct usb_udc * udc = container_of ( dev , struct usb_udc , dev ) ; \
return snprintf ( buf , PAGE_SIZE , " %s \n " , \
usb_speed_string ( udc - > gadget - > param ) ) ; \
} \
static DEVICE_ATTR_RO ( name )
static USB_UDC_SPEED_ATTR ( current_speed , speed ) ;
static USB_UDC_SPEED_ATTR ( maximum_speed , max_speed ) ;
# define USB_UDC_ATTR(name) \
ssize_t name # # _show ( struct device * dev , \
struct device_attribute * attr , char * buf ) \
{ \
struct usb_udc * udc = container_of ( dev , struct usb_udc , dev ) ; \
struct usb_gadget * gadget = udc - > gadget ; \
\
return snprintf ( buf , PAGE_SIZE , " %d \n " , gadget - > name ) ; \
} \
static DEVICE_ATTR_RO ( name )
static USB_UDC_ATTR ( is_otg ) ;
static USB_UDC_ATTR ( is_a_peripheral ) ;
static USB_UDC_ATTR ( b_hnp_enable ) ;
static USB_UDC_ATTR ( a_hnp_support ) ;
static USB_UDC_ATTR ( a_alt_hnp_support ) ;
static struct attribute * usb_udc_attrs [ ] = {
& dev_attr_srp . attr ,
& dev_attr_soft_connect . attr ,
& dev_attr_state . attr ,
& dev_attr_current_speed . attr ,
& dev_attr_maximum_speed . attr ,
& dev_attr_is_otg . attr ,
& dev_attr_is_a_peripheral . attr ,
& dev_attr_b_hnp_enable . attr ,
& dev_attr_a_hnp_support . attr ,
& dev_attr_a_alt_hnp_support . attr ,
NULL ,
} ;
static const struct attribute_group usb_udc_attr_group = {
. attrs = usb_udc_attrs ,
} ;
static const struct attribute_group * usb_udc_attr_groups [ ] = {
& usb_udc_attr_group ,
NULL ,
} ;
static int usb_udc_uevent ( struct device * dev , struct kobj_uevent_env * env )
{
struct usb_udc * udc = container_of ( dev , struct usb_udc , dev ) ;
int ret ;
ret = add_uevent_var ( env , " USB_UDC_NAME=%s " , udc - > gadget - > name ) ;
if ( ret ) {
dev_err ( dev , " failed to add uevent USB_UDC_NAME \n " ) ;
return ret ;
}
if ( udc - > driver ) {
ret = add_uevent_var ( env , " USB_UDC_DRIVER=%s " ,
udc - > driver - > function ) ;
if ( ret ) {
dev_err ( dev , " failed to add uevent USB_UDC_DRIVER \n " ) ;
return ret ;
}
}
return 0 ;
}
static int __init usb_udc_init ( void )
{
udc_class = class_create ( THIS_MODULE , " udc " ) ;
if ( IS_ERR ( udc_class ) ) {
pr_err ( " failed to create udc class --> %ld \n " ,
PTR_ERR ( udc_class ) ) ;
return PTR_ERR ( udc_class ) ;
}
udc_class - > dev_uevent = usb_udc_uevent ;
return 0 ;
}
subsys_initcall ( usb_udc_init ) ;
static void __exit usb_udc_exit ( void )
{
class_destroy ( udc_class ) ;
}
module_exit ( usb_udc_exit ) ;
MODULE_DESCRIPTION ( " UDC Framework " ) ;
MODULE_AUTHOR ( " Felipe Balbi <balbi@ti.com> " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;