2005-04-16 15:20:36 -07:00
/*
2008-06-19 17:52:07 -07:00
* serial . c - - USB gadget serial driver
2005-04-16 15:20:36 -07:00
*
2008-06-19 17:52:07 -07:00
* Copyright ( C ) 2003 Al Borchers ( alborchers @ steinerpoint . com )
* Copyright ( C ) 2008 by David Brownell
2008-06-19 18:19:03 -07:00
* Copyright ( C ) 2008 by Nokia Corporation
2005-04-16 15:20:36 -07:00
*
* This software is distributed under the terms of the GNU General
* Public License ( " GPL " ) as published by the Free Software Foundation ,
* either version 2 of that License or ( at your option ) any later version .
*/
# include <linux/kernel.h>
# include <linux/utsname.h>
# include <linux/device.h>
# include <linux/tty.h>
# include <linux/tty_flip.h>
2008-06-19 17:52:07 -07:00
# include "u_serial.h"
2005-04-16 15:20:36 -07:00
# include "gadget_chips.h"
/* Defines */
2008-06-19 18:19:03 -07:00
# define GS_VERSION_STR "v2.4"
# define GS_VERSION_NUM 0x2400
2005-04-16 15:20:36 -07:00
# define GS_LONG_NAME "Gadget Serial"
2008-06-19 17:52:07 -07:00
# define GS_VERSION_NAME GS_LONG_NAME " " GS_VERSION_STR
2008-06-19 18:19:03 -07:00
/*-------------------------------------------------------------------------*/
2005-04-16 15:20:36 -07:00
2008-08-18 17:41:02 -07:00
/*
* Kbuild is not very cooperative with respect to linking separately
* compiled library objects into one module . So for now we won ' t use
* separate compilation . . . ensuring init / exit sections work to shrink
* the runtime footprint , and giving us at least some parts of what
* a " gcc --combine ... part1.c part2.c part3.c ... " build would .
*/
# include "composite.c"
# include "usbstring.c"
# include "config.c"
# include "epautoconf.c"
# include "f_acm.c"
2008-08-18 17:39:30 -07:00
# include "f_obex.c"
2008-08-18 17:41:02 -07:00
# include "f_serial.c"
# include "u_serial.c"
/*-------------------------------------------------------------------------*/
2005-04-16 15:20:36 -07:00
/* Thanks to NetChip Technologies for donating this product ID.
2008-06-19 18:19:03 -07:00
*
* DO NOT REUSE THESE IDs with a protocol - incompatible driver ! ! Ever ! !
* Instead : allocate your own , using normal USB - IF procedures .
*/
2005-04-16 15:20:36 -07:00
# define GS_VENDOR_ID 0x0525 /* NetChip */
# define GS_PRODUCT_ID 0xa4a6 /* Linux-USB Serial Gadget */
# define GS_CDC_PRODUCT_ID 0xa4a7 /* ... as CDC-ACM */
2008-08-18 17:39:30 -07:00
# define GS_CDC_OBEX_PRODUCT_ID 0xa4a9 /* ... as CDC-OBEX */
2005-04-16 15:20:36 -07:00
2008-06-19 18:19:03 -07:00
/* string IDs are assigned dynamically */
2005-04-16 15:20:36 -07:00
2008-06-19 18:19:03 -07:00
# define STRING_MANUFACTURER_IDX 0
# define STRING_PRODUCT_IDX 1
# define STRING_DESCRIPTION_IDX 2
2008-06-19 17:52:07 -07:00
2005-04-16 15:20:36 -07:00
static char manufacturer [ 50 ] ;
2008-06-19 18:19:03 -07:00
static struct usb_string strings_dev [ ] = {
[ STRING_MANUFACTURER_IDX ] . s = manufacturer ,
[ STRING_PRODUCT_IDX ] . s = GS_VERSION_NAME ,
[ STRING_DESCRIPTION_IDX ] . s = NULL /* updated; f(use_acm) */ ,
2005-04-16 15:20:36 -07:00
{ } /* end of list */
} ;
2008-06-19 18:19:03 -07:00
static struct usb_gadget_strings stringtab_dev = {
. language = 0x0409 , /* en-us */
. strings = strings_dev ,
2005-04-16 15:20:36 -07:00
} ;
2008-06-19 18:19:03 -07:00
static struct usb_gadget_strings * dev_strings [ ] = {
& stringtab_dev ,
NULL ,
} ;
static struct usb_device_descriptor device_desc = {
2005-04-16 15:20:36 -07:00
. bLength = USB_DT_DEVICE_SIZE ,
. bDescriptorType = USB_DT_DEVICE ,
2009-02-11 14:11:36 -08:00
. bcdUSB = cpu_to_le16 ( 0x0200 ) ,
2008-06-19 18:19:03 -07:00
/* .bDeviceClass = f(use_acm) */
2005-04-16 15:20:36 -07:00
. bDeviceSubClass = 0 ,
. bDeviceProtocol = 0 ,
2008-06-19 18:19:03 -07:00
/* .bMaxPacketSize0 = f(hardware) */
2009-02-11 14:11:36 -08:00
. idVendor = cpu_to_le16 ( GS_VENDOR_ID ) ,
2008-06-19 18:19:03 -07:00
/* .idProduct = f(use_acm) */
/* .bcdDevice = f(hardware) */
/* .iManufacturer = DYNAMIC */
/* .iProduct = DYNAMIC */
. bNumConfigurations = 1 ,
2005-04-16 15:20:36 -07:00
} ;
2008-06-19 18:19:03 -07:00
static struct usb_otg_descriptor otg_descriptor = {
. bLength = sizeof otg_descriptor ,
2005-04-16 15:20:36 -07:00
. bDescriptorType = USB_DT_OTG ,
2008-06-19 18:19:03 -07:00
/* REVISIT SRP-only hardware is possible, although
* it would not be called " OTG " . . .
*/
. bmAttributes = USB_OTG_SRP | USB_OTG_HNP ,
2008-06-19 17:52:07 -07:00
} ;
2008-06-19 18:19:03 -07:00
static const struct usb_descriptor_header * otg_desc [ ] = {
( struct usb_descriptor_header * ) & otg_descriptor ,
2008-06-19 17:52:07 -07:00
NULL ,
} ;
2005-04-16 15:20:36 -07:00
2008-06-19 17:52:07 -07:00
/*-------------------------------------------------------------------------*/
2005-04-16 15:20:36 -07:00
2008-06-19 17:52:07 -07:00
/* Module */
MODULE_DESCRIPTION ( GS_VERSION_NAME ) ;
MODULE_AUTHOR ( " Al Borchers " ) ;
MODULE_AUTHOR ( " David Brownell " ) ;
MODULE_LICENSE ( " GPL " ) ;
2005-04-16 15:20:36 -07:00
2008-06-19 18:19:03 -07:00
static int use_acm = true ;
module_param ( use_acm , bool , 0 ) ;
MODULE_PARM_DESC ( use_acm , " Use CDC ACM, default=yes " ) ;
2005-04-16 15:20:36 -07:00
2008-08-18 17:39:30 -07:00
static int use_obex = false ;
module_param ( use_obex , bool , 0 ) ;
MODULE_PARM_DESC ( use_obex , " Use CDC OBEX, default=no " ) ;
2008-06-19 18:19:03 -07:00
static unsigned n_ports = 1 ;
module_param ( n_ports , uint , 0 ) ;
MODULE_PARM_DESC ( n_ports , " number of ports to create, default=1 " ) ;
2008-05-07 16:00:36 -07:00
2008-06-19 18:19:03 -07:00
/*-------------------------------------------------------------------------*/
2005-04-16 15:20:36 -07:00
2008-06-19 18:19:03 -07:00
static int __init serial_bind_config ( struct usb_configuration * c )
2008-05-07 16:00:36 -07:00
{
2008-06-19 18:19:03 -07:00
unsigned i ;
int status = 0 ;
2008-05-07 16:00:36 -07:00
2008-06-19 18:19:03 -07:00
for ( i = 0 ; i < n_ports & & status = = 0 ; i + + ) {
if ( use_acm )
status = acm_bind_config ( c , i ) ;
2008-08-18 17:39:30 -07:00
else if ( use_obex )
status = obex_bind_config ( c , i ) ;
2008-06-19 18:19:03 -07:00
else
status = gser_bind_config ( c , i ) ;
2008-05-07 16:00:36 -07:00
}
2008-06-19 18:19:03 -07:00
return status ;
2008-05-07 16:00:36 -07:00
}
2008-06-19 18:19:03 -07:00
static struct usb_configuration serial_config_driver = {
/* .label = f(use_acm) */
. bind = serial_bind_config ,
/* .bConfigurationValue = f(use_acm) */
/* .iConfiguration = DYNAMIC */
. bmAttributes = USB_CONFIG_ATT_SELFPOWER ,
} ;
static int __init gs_bind ( struct usb_composite_dev * cdev )
2005-04-16 15:20:36 -07:00
{
2008-06-19 18:19:03 -07:00
int gcnum ;
struct usb_gadget * gadget = cdev - > gadget ;
int status ;
2005-04-16 15:20:36 -07:00
2008-06-19 18:19:03 -07:00
status = gserial_setup ( cdev - > gadget , n_ports ) ;
if ( status < 0 )
return status ;
2008-06-19 17:52:07 -07:00
2008-06-19 18:19:03 -07:00
/* Allocate string descriptor numbers ... note that string
* contents can be overridden by the composite_dev glue .
2005-07-13 15:18:30 -07:00
*/
2005-04-16 15:20:36 -07:00
2008-06-19 18:19:03 -07:00
/* device description: manufacturer, product */
snprintf ( manufacturer , sizeof manufacturer , " %s %s with %s " ,
2006-10-02 02:18:13 -07:00
init_utsname ( ) - > sysname , init_utsname ( ) - > release ,
2005-04-16 15:20:36 -07:00
gadget - > name ) ;
2008-06-19 18:19:03 -07:00
status = usb_string_id ( cdev ) ;
if ( status < 0 )
goto fail ;
strings_dev [ STRING_MANUFACTURER_IDX ] . id = status ;
2005-04-16 15:20:36 -07:00
2008-06-19 18:19:03 -07:00
device_desc . iManufacturer = status ;
2005-04-16 15:20:36 -07:00
2008-06-19 18:19:03 -07:00
status = usb_string_id ( cdev ) ;
if ( status < 0 )
goto fail ;
strings_dev [ STRING_PRODUCT_IDX ] . id = status ;
2005-04-16 15:20:36 -07:00
2008-06-19 18:19:03 -07:00
device_desc . iProduct = status ;
2005-04-16 15:20:36 -07:00
2008-06-19 18:19:03 -07:00
/* config description */
status = usb_string_id ( cdev ) ;
if ( status < 0 )
goto fail ;
strings_dev [ STRING_DESCRIPTION_IDX ] . id = status ;
2005-04-16 15:20:36 -07:00
2008-06-19 18:19:03 -07:00
serial_config_driver . iConfiguration = status ;
2005-04-16 15:20:36 -07:00
2008-06-19 18:19:03 -07:00
/* set up other descriptors */
gcnum = usb_gadget_controller_number ( gadget ) ;
if ( gcnum > = 0 )
device_desc . bcdDevice = cpu_to_le16 ( GS_VERSION_NUM | gcnum ) ;
else {
/* this is so simple (for now, no altsettings) that it
* SHOULD NOT have problems with bulk - capable hardware .
* so warn about unrcognized controllers - - don ' t panic .
*
* things like configuration and altsetting numbering
* can need hardware - specific attention though .
2008-04-18 17:37:49 -07:00
*/
2008-06-19 18:19:03 -07:00
pr_warning ( " gs_bind: controller '%s' not recognized \n " ,
gadget - > name ) ;
device_desc . bcdDevice =
2009-02-11 14:11:36 -08:00
cpu_to_le16 ( GS_VERSION_NUM | 0x0099 ) ;
2008-04-18 17:37:49 -07:00
}
2008-06-19 18:19:03 -07:00
if ( gadget_is_otg ( cdev - > gadget ) ) {
serial_config_driver . descriptors = otg_desc ;
serial_config_driver . bmAttributes | = USB_CONFIG_ATT_WAKEUP ;
2005-04-16 15:20:36 -07:00
}
2008-05-07 16:00:36 -07:00
2008-06-19 18:19:03 -07:00
/* register our configuration */
status = usb_add_config ( cdev , & serial_config_driver ) ;
if ( status < 0 )
goto fail ;
2005-04-16 15:20:36 -07:00
2008-06-19 18:19:03 -07:00
INFO ( cdev , " %s \n " , GS_VERSION_NAME ) ;
2005-04-16 15:20:36 -07:00
2008-06-19 18:19:03 -07:00
return 0 ;
2005-04-16 15:20:36 -07:00
2008-06-19 18:19:03 -07:00
fail :
gserial_cleanup ( ) ;
return status ;
2005-04-16 15:20:36 -07:00
}
2008-06-19 18:19:03 -07:00
static struct usb_composite_driver gserial_driver = {
. name = " g_serial " ,
. dev = & device_desc ,
. strings = dev_strings ,
. bind = gs_bind ,
2008-05-07 16:00:36 -07:00
} ;
2008-06-19 18:19:03 -07:00
static int __init init ( void )
2005-04-16 15:20:36 -07:00
{
2008-06-19 18:19:03 -07:00
/* We *could* export two configs; that'd be much cleaner...
* but neither of these product IDs was defined that way .
2008-04-18 17:37:49 -07:00
*/
2005-04-16 15:20:36 -07:00
if ( use_acm ) {
2008-06-19 18:19:03 -07:00
serial_config_driver . label = " CDC ACM config " ;
serial_config_driver . bConfigurationValue = 2 ;
device_desc . bDeviceClass = USB_CLASS_COMM ;
device_desc . idProduct =
2009-02-11 14:11:36 -08:00
cpu_to_le16 ( GS_CDC_PRODUCT_ID ) ;
2008-08-18 17:39:30 -07:00
} else if ( use_obex ) {
serial_config_driver . label = " CDC OBEX config " ;
serial_config_driver . bConfigurationValue = 3 ;
device_desc . bDeviceClass = USB_CLASS_COMM ;
device_desc . idProduct =
2009-02-11 14:11:36 -08:00
cpu_to_le16 ( GS_CDC_OBEX_PRODUCT_ID ) ;
2005-04-16 15:20:36 -07:00
} else {
2008-06-19 18:19:03 -07:00
serial_config_driver . label = " Generic Serial config " ;
serial_config_driver . bConfigurationValue = 1 ;
device_desc . bDeviceClass = USB_CLASS_VENDOR_SPEC ;
device_desc . idProduct =
2009-02-11 14:11:36 -08:00
cpu_to_le16 ( GS_PRODUCT_ID ) ;
2005-04-16 15:20:36 -07:00
}
2008-06-19 18:19:03 -07:00
strings_dev [ STRING_DESCRIPTION_IDX ] . s = serial_config_driver . label ;
2008-05-07 16:00:36 -07:00
2008-06-19 18:19:03 -07:00
return usb_composite_register ( & gserial_driver ) ;
2008-05-07 16:00:36 -07:00
}
2008-06-19 18:19:03 -07:00
module_init ( init ) ;
2008-05-07 16:00:36 -07:00
2008-06-19 18:19:03 -07:00
static void __exit cleanup ( void )
2008-05-07 16:00:36 -07:00
{
2008-06-19 18:19:03 -07:00
usb_composite_unregister ( & gserial_driver ) ;
gserial_cleanup ( ) ;
2008-05-07 16:00:36 -07:00
}
2008-06-19 18:19:03 -07:00
module_exit ( cleanup ) ;