2017-11-03 13:28:30 +03:00
// SPDX-License-Identifier: GPL-2.0
2005-04-17 02:20:36 +04:00
/*
* USB Serial Converter driver
*
2013-03-21 15:37:51 +04:00
* Copyright ( C ) 2009 - 2013 Johan Hovold ( jhovold @ gmail . com )
2012-05-16 02:40:00 +04:00
* Copyright ( C ) 1999 - 2012 Greg Kroah - Hartman ( greg @ kroah . com )
2005-04-17 02:20:36 +04:00
* Copyright ( C ) 2000 Peter Berger ( pberger @ brimson . com )
* Copyright ( C ) 2000 Al Borchers ( borchers @ steinerpoint . com )
*
2005-06-21 08:15:16 +04:00
* This driver was originally based on the ACM driver by Armin Fuerst ( which was
2005-04-17 02:20:36 +04:00
* based on a driver by Brad Keryan )
*
2008-07-22 14:12:24 +04:00
* See Documentation / usb / usb - serial . txt for more information on using this
* driver
2005-04-17 02:20:36 +04:00
*/
2012-09-14 03:30:31 +04:00
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2005-04-17 02:20:36 +04:00
# include <linux/kernel.h>
# include <linux/errno.h>
# include <linux/init.h>
# include <linux/slab.h>
# include <linux/tty.h>
# include <linux/tty_driver.h>
# include <linux/tty_flip.h>
# include <linux/module.h>
# include <linux/moduleparam.h>
2009-04-01 02:19:21 +04:00
# include <linux/seq_file.h>
2005-04-17 02:20:36 +04:00
# include <linux/spinlock.h>
2006-03-24 23:12:31 +03:00
# include <linux/mutex.h>
2005-04-17 02:20:36 +04:00
# include <linux/list.h>
2008-07-22 14:12:24 +04:00
# include <linux/uaccess.h>
2009-07-28 03:34:58 +04:00
# include <linux/serial.h>
2005-04-17 02:20:36 +04:00
# include <linux/usb.h>
2006-07-12 08:22:58 +04:00
# include <linux/usb/serial.h>
2009-08-28 23:54:27 +04:00
# include <linux/kfifo.h>
2013-06-07 22:04:28 +04:00
# include <linux/idr.h>
2005-04-17 02:20:36 +04:00
2012-09-18 20:10:29 +04:00
# define DRIVER_AUTHOR "Greg Kroah-Hartman <gregkh@linuxfoundation.org>"
2005-04-17 02:20:36 +04:00
# define DRIVER_DESC "USB Serial Driver core"
2013-06-06 21:31:35 +04:00
# define USB_SERIAL_TTY_MAJOR 188
# define USB_SERIAL_TTY_MINORS 512 /* should be enough for a while */
2005-04-17 02:20:36 +04:00
/* There is no MODULE_DEVICE_TABLE for usbserial.c. Instead
the MODULE_DEVICE_TABLE declarations in each serial driver
cause the " hotplug " program to pull in whatever module is necessary
via modprobe , and modprobe will load usbserial because the serial
drivers depend on it .
*/
2013-06-07 22:04:28 +04:00
static DEFINE_IDR ( serial_minors ) ;
2007-07-24 17:13:42 +04:00
static DEFINE_MUTEX ( table_lock ) ;
2005-04-17 02:20:36 +04:00
static LIST_HEAD ( usb_serial_driver_list ) ;
2009-09-01 19:38:59 +04:00
/*
2013-06-07 22:04:28 +04:00
* Look up the serial port structure . If it is found and it hasn ' t been
* disconnected , return with the parent usb_serial structure ' s disc_mutex held
* and its refcount incremented . Otherwise return NULL .
2009-09-01 19:38:59 +04:00
*/
2013-06-07 22:04:28 +04:00
struct usb_serial_port * usb_serial_port_get_by_minor ( unsigned minor )
2005-04-17 02:20:36 +04:00
{
2007-01-13 09:29:26 +03:00
struct usb_serial * serial ;
2013-06-07 22:04:28 +04:00
struct usb_serial_port * port ;
2007-01-13 09:29:26 +03:00
2007-07-24 17:13:42 +04:00
mutex_lock ( & table_lock ) ;
2013-06-07 22:04:28 +04:00
port = idr_find ( & serial_minors , minor ) ;
if ( ! port )
goto exit ;
serial = port - > serial ;
mutex_lock ( & serial - > disc_mutex ) ;
if ( serial - > disconnected ) {
mutex_unlock ( & serial - > disc_mutex ) ;
port = NULL ;
} else {
kref_get ( & serial - > kref ) ;
2009-09-01 19:38:59 +04:00
}
2013-06-07 22:04:28 +04:00
exit :
2007-07-24 17:13:42 +04:00
mutex_unlock ( & table_lock ) ;
2013-06-07 22:04:28 +04:00
return port ;
2005-04-17 02:20:36 +04:00
}
2013-06-07 22:04:28 +04:00
static int allocate_minors ( struct usb_serial * serial , int num_ports )
2005-04-17 02:20:36 +04:00
{
2013-06-07 22:04:28 +04:00
struct usb_serial_port * port ;
2005-04-17 02:20:36 +04:00
unsigned int i , j ;
2013-06-07 22:04:28 +04:00
int minor ;
2005-04-17 02:20:36 +04:00
2012-09-14 03:30:31 +04:00
dev_dbg ( & serial - > interface - > dev , " %s %d \n " , __func__ , num_ports ) ;
2005-04-17 02:20:36 +04:00
2007-07-24 17:13:42 +04:00
mutex_lock ( & table_lock ) ;
2013-06-07 22:04:28 +04:00
for ( i = 0 ; i < num_ports ; + + i ) {
port = serial - > port [ i ] ;
2016-05-08 21:08:03 +03:00
minor = idr_alloc ( & serial_minors , port , 0 ,
USB_SERIAL_TTY_MINORS , GFP_KERNEL ) ;
2013-06-07 22:04:28 +04:00
if ( minor < 0 )
goto error ;
port - > minor = minor ;
port - > port_number = i ;
2005-04-17 02:20:36 +04:00
}
2013-06-07 22:04:28 +04:00
serial - > minors_reserved = 1 ;
2007-07-24 17:13:42 +04:00
mutex_unlock ( & table_lock ) ;
2013-06-07 22:04:28 +04:00
return 0 ;
error :
/* unwind the already allocated minors */
for ( j = 0 ; j < i ; + + j )
idr_remove ( & serial_minors , serial - > port [ j ] - > minor ) ;
mutex_unlock ( & table_lock ) ;
return minor ;
2005-04-17 02:20:36 +04:00
}
2013-06-07 22:04:28 +04:00
static void release_minors ( struct usb_serial * serial )
2005-04-17 02:20:36 +04:00
{
int i ;
2009-09-01 19:38:59 +04:00
mutex_lock ( & table_lock ) ;
2008-07-22 14:12:24 +04:00
for ( i = 0 ; i < serial - > num_ports ; + + i )
2013-06-07 22:04:28 +04:00
idr_remove ( & serial_minors , serial - > port [ i ] - > minor ) ;
2009-09-01 19:38:59 +04:00
mutex_unlock ( & table_lock ) ;
2013-06-07 22:04:28 +04:00
serial - > minors_reserved = 0 ;
2005-04-17 02:20:36 +04:00
}
static void destroy_serial ( struct kref * kref )
{
struct usb_serial * serial ;
struct usb_serial_port * port ;
int i ;
serial = to_usb_serial ( kref ) ;
2007-03-13 18:30:50 +03:00
/* return the minor range that this device had */
2013-06-07 22:04:28 +04:00
if ( serial - > minors_reserved )
release_minors ( serial ) ;
2007-03-13 18:30:50 +03:00
2013-03-21 15:36:43 +04:00
if ( serial - > attached & & serial - > type - > release )
2009-10-09 20:43:12 +04:00
serial - > type - > release ( serial ) ;
2009-06-02 19:53:55 +04:00
2009-09-01 19:38:34 +04:00
/* Now that nothing is using the ports, they can be freed */
for ( i = 0 ; i < serial - > num_port_pointers ; + + i ) {
2009-06-02 19:53:55 +04:00
port = serial - > port [ i ] ;
2009-09-01 19:38:34 +04:00
if ( port ) {
port - > serial = NULL ;
2009-06-02 19:53:55 +04:00
put_device ( & port - > dev ) ;
2005-04-17 02:20:36 +04:00
}
}
2013-03-19 12:21:09 +04:00
usb_put_intf ( serial - > interface ) ;
2005-04-17 02:20:36 +04:00
usb_put_dev ( serial - > dev ) ;
2008-07-22 14:12:24 +04:00
kfree ( serial ) ;
2005-04-17 02:20:36 +04:00
}
2006-04-25 09:46:17 +04:00
void usb_serial_put ( struct usb_serial * serial )
{
kref_put ( & serial - > kref , destroy_serial ) ;
}
2005-04-17 02:20:36 +04:00
/*****************************************************************************
* Driver tty interface functions
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2009-09-01 19:38:44 +04:00
/**
* serial_install - install tty
* @ driver : the driver ( USB in our case )
* @ tty : the tty being created
*
* Create the termios objects for this tty . We use the default
* USB serial settings but permit them to be overridden by
* serial - > type - > init_termios .
2009-09-01 19:39:13 +04:00
*
* This is the first place a new tty gets used . Hence this is where we
* acquire references to the usb_serial structure and the driver module ,
* where we store a pointer to the port , and where we do an autoresume .
2009-09-27 20:00:42 +04:00
* All these actions are reversed in serial_cleanup ( ) .
2009-09-01 19:38:44 +04:00
*/
static int serial_install ( struct tty_driver * driver , struct tty_struct * tty )
{
int idx = tty - > index ;
struct usb_serial * serial ;
2009-09-01 19:39:13 +04:00
struct usb_serial_port * port ;
int retval = - ENODEV ;
2013-06-07 22:04:28 +04:00
port = usb_serial_port_get_by_minor ( idx ) ;
if ( ! port )
2009-09-01 19:39:13 +04:00
return retval ;
2013-06-07 22:04:28 +04:00
serial = port - > serial ;
2009-09-01 19:39:13 +04:00
if ( ! try_module_get ( serial - > type - > driver . owner ) )
goto error_module_get ;
retval = usb_autopm_get_interface ( serial - > interface ) ;
if ( retval )
goto error_get_interface ;
2009-09-01 19:38:44 +04:00
2018-05-16 12:42:07 +03:00
retval = tty_standard_install ( driver , tty ) ;
2012-01-31 00:14:29 +04:00
if ( retval )
goto error_init_termios ;
2009-09-01 19:39:13 +04:00
mutex_unlock ( & serial - > disc_mutex ) ;
2009-09-01 19:39:22 +04:00
/* allow the driver to update the settings */
if ( serial - > type - > init_termios )
serial - > type - > init_termios ( tty ) ;
2009-09-01 19:39:13 +04:00
tty - > driver_data = port ;
return retval ;
2009-09-01 19:39:22 +04:00
error_init_termios :
2012-01-31 00:14:29 +04:00
usb_autopm_put_interface ( serial - > interface ) ;
error_get_interface :
2009-09-01 19:39:13 +04:00
module_put ( serial - > type - > driver . owner ) ;
error_module_get :
usb_serial_put ( serial ) ;
mutex_unlock ( & serial - > disc_mutex ) ;
return retval ;
2009-09-01 19:38:44 +04:00
}
2013-03-21 15:36:23 +04:00
static int serial_port_activate ( struct tty_port * tport , struct tty_struct * tty )
2005-04-17 02:20:36 +04:00
{
2009-10-06 19:06:11 +04:00
struct usb_serial_port * port =
container_of ( tport , struct usb_serial_port , port ) ;
2009-09-01 19:39:59 +04:00
struct usb_serial * serial = port - > serial ;
int retval ;
2009-02-11 18:06:53 +03:00
2009-10-06 19:06:11 +04:00
mutex_lock ( & serial - > disc_mutex ) ;
if ( serial - > disconnected )
retval = - ENODEV ;
else
retval = port - > serial - > type - > open ( tty , port ) ;
mutex_unlock ( & serial - > disc_mutex ) ;
2011-11-10 20:38:13 +04:00
if ( retval < 0 )
retval = usb_translate_errors ( retval ) ;
2009-10-06 19:06:11 +04:00
return retval ;
}
2005-04-17 02:20:36 +04:00
2009-10-06 19:06:11 +04:00
static int serial_open ( struct tty_struct * tty , struct file * filp )
{
struct usb_serial_port * port = tty - > driver_data ;
2009-09-01 19:39:59 +04:00
2013-03-21 15:36:25 +04:00
dev_dbg ( tty - > dev , " %s \n " , __func__ ) ;
2009-10-06 19:06:11 +04:00
return tty_port_open ( & port - > port , tty , filp ) ;
2005-04-17 02:20:36 +04:00
}
2009-06-11 15:26:29 +04:00
/**
2013-03-21 15:36:23 +04:00
* serial_port_shutdown - shut down hardware
2009-10-06 19:06:36 +04:00
* @ tport : tty port to shut down
2009-06-11 15:26:29 +04:00
*
2013-02-23 19:11:35 +04:00
* Shut down a USB serial port . Serialized against activate by the
* tport mutex and kept to matching open / close pairs
2016-04-10 03:53:25 +03:00
* of calls by the initialized flag .
2013-02-23 19:11:35 +04:00
*
* Not called if tty is console .
2009-06-11 15:26:29 +04:00
*/
2013-03-21 15:36:23 +04:00
static void serial_port_shutdown ( struct tty_port * tport )
2005-04-17 02:20:36 +04:00
{
2009-10-06 19:06:36 +04:00
struct usb_serial_port * port =
container_of ( tport , struct usb_serial_port , port ) ;
2009-06-11 15:26:29 +04:00
struct usb_serial_driver * drv = port - > serial - > type ;
2013-02-23 19:11:35 +04:00
2009-06-11 15:26:29 +04:00
if ( drv - > close )
drv - > close ( port ) ;
}
2009-09-01 19:38:44 +04:00
static void serial_hangup ( struct tty_struct * tty )
{
struct usb_serial_port * port = tty - > driver_data ;
2012-09-14 03:30:31 +04:00
2013-03-21 15:36:25 +04:00
dev_dbg ( tty - > dev , " %s \n " , __func__ ) ;
2009-09-01 19:38:44 +04:00
tty_port_hangup ( & port - > port ) ;
}
static void serial_close ( struct tty_struct * tty , struct file * filp )
{
struct usb_serial_port * port = tty - > driver_data ;
2012-09-14 03:30:31 +04:00
2013-03-21 15:36:25 +04:00
dev_dbg ( tty - > dev , " %s \n " , __func__ ) ;
2009-10-06 19:06:36 +04:00
tty_port_close ( & port - > port , tty , filp ) ;
2009-09-01 19:38:44 +04:00
}
2009-06-11 15:26:29 +04:00
/**
2009-09-27 20:00:42 +04:00
* serial_cleanup - free resources post close / hangup
2009-09-01 19:38:44 +04:00
* @ port : port to free up
2009-06-11 15:26:29 +04:00
*
2009-09-01 19:38:44 +04:00
* Do the resource freeing and refcount dropping for the port .
* Avoid freeing the console .
2009-09-20 00:13:24 +04:00
*
2012-07-17 20:06:57 +04:00
* Called asynchronously after the last tty kref is dropped .
2009-06-11 15:26:29 +04:00
*/
2009-09-27 20:00:42 +04:00
static void serial_cleanup ( struct tty_struct * tty )
2009-06-11 15:26:29 +04:00
{
2009-09-20 00:13:24 +04:00
struct usb_serial_port * port = tty - > driver_data ;
2009-06-11 15:26:29 +04:00
struct usb_serial * serial ;
struct module * owner ;
2013-03-21 15:36:25 +04:00
dev_dbg ( tty - > dev , " %s \n " , __func__ ) ;
2009-09-01 19:38:44 +04:00
/* The console is magical. Do not hang up the console hardware
* or there will be tears .
*/
2010-03-09 06:50:12 +03:00
if ( port - > port . console )
2009-06-11 15:26:29 +04:00
return ;
2009-09-01 19:39:13 +04:00
tty - > driver_data = NULL ;
2009-06-11 15:26:29 +04:00
serial = port - > serial ;
owner = serial - > type - > driver . owner ;
2009-09-01 19:38:34 +04:00
2009-06-11 15:26:29 +04:00
mutex_lock ( & serial - > disc_mutex ) ;
if ( ! serial - > disconnected )
usb_autopm_put_interface ( serial - > interface ) ;
mutex_unlock ( & serial - > disc_mutex ) ;
2009-09-01 19:38:34 +04:00
2009-04-14 19:31:02 +04:00
usb_serial_put ( serial ) ;
2009-06-11 15:26:29 +04:00
module_put ( owner ) ;
}
2008-07-22 14:12:24 +04:00
static int serial_write ( struct tty_struct * tty , const unsigned char * buf ,
int count )
2005-04-17 02:20:36 +04:00
{
2005-07-04 21:32:51 +04:00
struct usb_serial_port * port = tty - > driver_data ;
2007-01-13 09:32:27 +03:00
int retval = - ENODEV ;
2005-04-17 02:20:36 +04:00
2008-04-30 11:54:13 +04:00
if ( port - > serial - > dev - > state = = USB_STATE_NOTATTACHED )
2005-11-29 00:16:05 +03:00
goto exit ;
2013-03-21 15:36:25 +04:00
dev_dbg ( tty - > dev , " %s - %d byte(s) \n " , __func__ , count ) ;
2005-04-17 02:20:36 +04:00
2008-07-22 14:09:07 +04:00
retval = port - > serial - > type - > write ( tty , port , buf , count ) ;
2011-11-10 20:40:44 +04:00
if ( retval < 0 )
retval = usb_translate_errors ( retval ) ;
2005-04-17 02:20:36 +04:00
exit :
return retval ;
}
2008-07-22 14:12:24 +04:00
static int serial_write_room ( struct tty_struct * tty )
2005-04-17 02:20:36 +04:00
{
2005-07-04 21:32:51 +04:00
struct usb_serial_port * port = tty - > driver_data ;
2012-09-14 03:30:31 +04:00
2013-03-21 15:36:25 +04:00
dev_dbg ( tty - > dev , " %s \n " , __func__ ) ;
2013-03-21 15:36:24 +04:00
2008-07-22 14:09:07 +04:00
return port - > serial - > type - > write_room ( tty ) ;
2005-04-17 02:20:36 +04:00
}
2008-07-22 14:12:24 +04:00
static int serial_chars_in_buffer ( struct tty_struct * tty )
2005-04-17 02:20:36 +04:00
{
2005-07-04 21:32:51 +04:00
struct usb_serial_port * port = tty - > driver_data ;
2013-01-14 19:52:56 +04:00
struct usb_serial * serial = port - > serial ;
2012-09-14 03:30:31 +04:00
2013-03-21 15:36:25 +04:00
dev_dbg ( tty - > dev , " %s \n " , __func__ ) ;
2005-04-17 02:20:36 +04:00
2013-01-14 19:52:56 +04:00
if ( serial - > disconnected )
2013-05-05 22:32:33 +04:00
return 0 ;
2013-01-14 19:52:56 +04:00
2013-05-05 22:32:33 +04:00
return serial - > type - > chars_in_buffer ( tty ) ;
2005-04-17 02:20:36 +04:00
}
2013-05-05 22:32:27 +04:00
static void serial_wait_until_sent ( struct tty_struct * tty , int timeout )
{
struct usb_serial_port * port = tty - > driver_data ;
struct usb_serial * serial = port - > serial ;
dev_dbg ( tty - > dev , " %s \n " , __func__ ) ;
if ( ! port - > serial - > type - > wait_until_sent )
return ;
mutex_lock ( & serial - > disc_mutex ) ;
if ( ! serial - > disconnected )
port - > serial - > type - > wait_until_sent ( tty , timeout ) ;
mutex_unlock ( & serial - > disc_mutex ) ;
}
2008-07-22 14:12:24 +04:00
static void serial_throttle ( struct tty_struct * tty )
2005-04-17 02:20:36 +04:00
{
2005-07-04 21:32:51 +04:00
struct usb_serial_port * port = tty - > driver_data ;
2012-09-14 03:30:31 +04:00
2013-03-21 15:36:25 +04:00
dev_dbg ( tty - > dev , " %s \n " , __func__ ) ;
2005-04-17 02:20:36 +04:00
if ( port - > serial - > type - > throttle )
2008-07-22 14:09:07 +04:00
port - > serial - > type - > throttle ( tty ) ;
2005-04-17 02:20:36 +04:00
}
2008-07-22 14:12:24 +04:00
static void serial_unthrottle ( struct tty_struct * tty )
2005-04-17 02:20:36 +04:00
{
2005-07-04 21:32:51 +04:00
struct usb_serial_port * port = tty - > driver_data ;
2012-09-14 03:30:31 +04:00
2013-03-21 15:36:25 +04:00
dev_dbg ( tty - > dev , " %s \n " , __func__ ) ;
2005-04-17 02:20:36 +04:00
if ( port - > serial - > type - > unthrottle )
2008-07-22 14:09:07 +04:00
port - > serial - > type - > unthrottle ( tty ) ;
2005-04-17 02:20:36 +04:00
}
2018-09-12 06:28:07 +03:00
static int serial_get_serial ( struct tty_struct * tty , struct serial_struct * ss )
{
struct usb_serial_port * port = tty - > driver_data ;
if ( port - > serial - > type - > get_serial )
return port - > serial - > type - > get_serial ( tty , ss ) ;
2018-09-12 14:46:51 +03:00
return - ENOTTY ;
2018-09-12 06:28:07 +03:00
}
static int serial_set_serial ( struct tty_struct * tty , struct serial_struct * ss )
{
struct usb_serial_port * port = tty - > driver_data ;
if ( port - > serial - > type - > set_serial )
return port - > serial - > type - > set_serial ( tty , ss ) ;
2018-09-12 14:46:51 +03:00
return - ENOTTY ;
2018-09-12 06:28:07 +03:00
}
2011-02-14 19:27:22 +03:00
static int serial_ioctl ( struct tty_struct * tty ,
2008-07-22 14:12:24 +04:00
unsigned int cmd , unsigned long arg )
2005-04-17 02:20:36 +04:00
{
2005-07-04 21:32:51 +04:00
struct usb_serial_port * port = tty - > driver_data ;
2013-06-05 14:21:11 +04:00
int retval = - ENOIOCTLCMD ;
2005-04-17 02:20:36 +04:00
2013-12-29 22:22:55 +04:00
dev_dbg ( tty - > dev , " %s - cmd 0x%04x \n " , __func__ , cmd ) ;
2005-04-17 02:20:36 +04:00
2013-03-21 15:36:51 +04:00
switch ( cmd ) {
case TIOCMIWAIT :
if ( port - > serial - > type - > tiocmiwait )
retval = port - > serial - > type - > tiocmiwait ( tty , arg ) ;
break ;
default :
if ( port - > serial - > type - > ioctl )
retval = port - > serial - > type - > ioctl ( tty , cmd , arg ) ;
}
2005-04-17 02:20:36 +04:00
return retval ;
}
2008-07-22 14:12:24 +04:00
static void serial_set_termios ( struct tty_struct * tty , struct ktermios * old )
2005-04-17 02:20:36 +04:00
{
2005-07-04 21:32:51 +04:00
struct usb_serial_port * port = tty - > driver_data ;
2012-09-14 03:30:31 +04:00
2013-03-21 15:36:25 +04:00
dev_dbg ( tty - > dev , " %s \n " , __func__ ) ;
2005-04-17 02:20:36 +04:00
if ( port - > serial - > type - > set_termios )
2008-07-22 14:09:07 +04:00
port - > serial - > type - > set_termios ( tty , port , old ) ;
2007-10-18 12:24:22 +04:00
else
2012-07-14 18:31:47 +04:00
tty_termios_copy_hw ( & tty - > termios , old ) ;
2005-04-17 02:20:36 +04:00
}
2008-07-22 14:18:03 +04:00
static int serial_break ( struct tty_struct * tty , int break_state )
2005-04-17 02:20:36 +04:00
{
2005-07-04 21:32:51 +04:00
struct usb_serial_port * port = tty - > driver_data ;
2005-04-17 02:20:36 +04:00
2013-03-21 15:36:25 +04:00
dev_dbg ( tty - > dev , " %s \n " , __func__ ) ;
2005-04-17 02:20:36 +04:00
2009-01-02 16:48:56 +03:00
if ( port - > serial - > type - > break_ctl )
2008-07-22 14:09:07 +04:00
port - > serial - > type - > break_ctl ( tty , break_state ) ;
2013-03-21 15:36:24 +04:00
2008-07-22 14:18:03 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2009-04-01 02:19:21 +04:00
static int serial_proc_show ( struct seq_file * m , void * v )
2005-04-17 02:20:36 +04:00
{
struct usb_serial * serial ;
2013-06-07 22:04:28 +04:00
struct usb_serial_port * port ;
2005-04-17 02:20:36 +04:00
int i ;
char tmp [ 40 ] ;
2009-04-01 02:19:21 +04:00
seq_puts ( m , " usbserinfo:1.0 driver:2.0 \n " ) ;
2013-06-06 21:31:35 +04:00
for ( i = 0 ; i < USB_SERIAL_TTY_MINORS ; + + i ) {
2013-06-07 22:04:28 +04:00
port = usb_serial_port_get_by_minor ( i ) ;
if ( port = = NULL )
2005-04-17 02:20:36 +04:00
continue ;
2013-06-07 22:04:28 +04:00
serial = port - > serial ;
2005-04-17 02:20:36 +04:00
2009-04-01 02:19:21 +04:00
seq_printf ( m , " %d: " , i ) ;
2005-06-21 08:15:16 +04:00
if ( serial - > type - > driver . owner )
2009-04-01 02:19:21 +04:00
seq_printf ( m , " module:%s " ,
2008-07-22 14:12:24 +04:00
module_name ( serial - > type - > driver . owner ) ) ;
2009-04-01 02:19:21 +04:00
seq_printf ( m , " name: \" %s \" " ,
2008-07-22 14:12:24 +04:00
serial - > type - > description ) ;
2009-04-01 02:19:21 +04:00
seq_printf ( m , " vendor:%04x product:%04x " ,
2008-07-22 14:12:24 +04:00
le16_to_cpu ( serial - > dev - > descriptor . idVendor ) ,
le16_to_cpu ( serial - > dev - > descriptor . idProduct ) ) ;
2009-04-01 02:19:21 +04:00
seq_printf ( m , " num_ports:%d " , serial - > num_ports ) ;
2013-06-07 22:04:28 +04:00
seq_printf ( m , " port:%d " , port - > port_number ) ;
2005-04-17 02:20:36 +04:00
usb_make_path ( serial - > dev , tmp , sizeof ( tmp ) ) ;
2009-04-01 02:19:21 +04:00
seq_printf ( m , " path:%s " , tmp ) ;
2008-07-22 14:12:24 +04:00
2009-04-01 02:19:21 +04:00
seq_putc ( m , ' \n ' ) ;
2006-04-25 09:46:17 +04:00
usb_serial_put ( serial ) ;
2009-09-01 19:38:59 +04:00
mutex_unlock ( & serial - > disc_mutex ) ;
2005-04-17 02:20:36 +04:00
}
2009-04-01 02:19:21 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2011-02-14 19:26:14 +03:00
static int serial_tiocmget ( struct tty_struct * tty )
2005-04-17 02:20:36 +04:00
{
2005-07-04 21:32:51 +04:00
struct usb_serial_port * port = tty - > driver_data ;
2005-04-17 02:20:36 +04:00
2013-03-21 15:36:25 +04:00
dev_dbg ( tty - > dev , " %s \n " , __func__ ) ;
2005-04-17 02:20:36 +04:00
if ( port - > serial - > type - > tiocmget )
2011-02-14 19:26:14 +03:00
return port - > serial - > type - > tiocmget ( tty ) ;
2005-04-17 02:20:36 +04:00
return - EINVAL ;
}
2011-02-14 19:26:50 +03:00
static int serial_tiocmset ( struct tty_struct * tty ,
2005-04-17 02:20:36 +04:00
unsigned int set , unsigned int clear )
{
2005-07-04 21:32:51 +04:00
struct usb_serial_port * port = tty - > driver_data ;
2005-04-17 02:20:36 +04:00
2013-03-21 15:36:25 +04:00
dev_dbg ( tty - > dev , " %s \n " , __func__ ) ;
2005-04-17 02:20:36 +04:00
if ( port - > serial - > type - > tiocmset )
2011-02-14 19:26:50 +03:00
return port - > serial - > type - > tiocmset ( tty , set , clear ) ;
2005-04-17 02:20:36 +04:00
return - EINVAL ;
}
2010-09-16 21:21:24 +04:00
static int serial_get_icount ( struct tty_struct * tty ,
struct serial_icounter_struct * icount )
{
struct usb_serial_port * port = tty - > driver_data ;
2013-03-21 15:36:25 +04:00
dev_dbg ( tty - > dev , " %s \n " , __func__ ) ;
2010-09-16 21:21:24 +04:00
if ( port - > serial - > type - > get_icount )
return port - > serial - > type - > get_icount ( tty , icount ) ;
return - EINVAL ;
}
2006-05-23 08:58:49 +04:00
/*
* We would be calling tty_wakeup here , but unfortunately some line
* disciplines have an annoying habit of calling tty - > write from
* the write wakeup callback ( e . g . n_hdlc . c ) .
*/
void usb_serial_port_softint ( struct usb_serial_port * port )
{
schedule_work ( & port - > work ) ;
}
2008-07-22 14:12:24 +04:00
EXPORT_SYMBOL_GPL ( usb_serial_port_softint ) ;
2006-05-23 08:58:49 +04:00
2006-11-22 17:57:56 +03:00
static void usb_serial_port_work ( struct work_struct * work )
2005-04-17 02:20:36 +04:00
{
2006-11-22 17:57:56 +03:00
struct usb_serial_port * port =
container_of ( work , struct usb_serial_port , work ) ;
2005-04-17 02:20:36 +04:00
2013-03-07 16:12:29 +04:00
tty_port_tty_wakeup ( & port - > port ) ;
2005-04-17 02:20:36 +04:00
}
2013-03-21 15:36:49 +04:00
static void usb_serial_port_poison_urbs ( struct usb_serial_port * port )
2006-06-22 02:00:45 +04:00
{
2010-05-06 01:57:37 +04:00
int i ;
2011-11-06 22:06:37 +04:00
for ( i = 0 ; i < ARRAY_SIZE ( port - > read_urbs ) ; + + i )
2013-03-21 15:36:49 +04:00
usb_poison_urb ( port - > read_urbs [ i ] ) ;
2010-05-06 01:57:37 +04:00
for ( i = 0 ; i < ARRAY_SIZE ( port - > write_urbs ) ; + + i )
2013-03-21 15:36:49 +04:00
usb_poison_urb ( port - > write_urbs [ i ] ) ;
usb_poison_urb ( port - > interrupt_in_urb ) ;
usb_poison_urb ( port - > interrupt_out_urb ) ;
}
static void usb_serial_port_unpoison_urbs ( struct usb_serial_port * port )
{
int i ;
for ( i = 0 ; i < ARRAY_SIZE ( port - > read_urbs ) ; + + i )
usb_unpoison_urb ( port - > read_urbs [ i ] ) ;
for ( i = 0 ; i < ARRAY_SIZE ( port - > write_urbs ) ; + + i )
usb_unpoison_urb ( port - > write_urbs [ i ] ) ;
usb_unpoison_urb ( port - > interrupt_in_urb ) ;
usb_unpoison_urb ( port - > interrupt_out_urb ) ;
2007-01-13 09:29:26 +03:00
}
2013-03-21 15:36:47 +04:00
static void usb_serial_port_release ( struct device * dev )
2007-01-13 09:29:26 +03:00
{
2009-09-01 19:38:34 +04:00
struct usb_serial_port * port = to_usb_serial_port ( dev ) ;
2010-05-06 01:57:37 +04:00
int i ;
2009-09-01 19:38:34 +04:00
2012-09-14 03:30:31 +04:00
dev_dbg ( dev , " %s \n " , __func__ ) ;
2009-09-01 19:38:34 +04:00
2007-01-13 09:29:26 +03:00
usb_free_urb ( port - > interrupt_in_urb ) ;
2005-04-17 02:20:36 +04:00
usb_free_urb ( port - > interrupt_out_urb ) ;
2011-11-06 22:06:37 +04:00
for ( i = 0 ; i < ARRAY_SIZE ( port - > read_urbs ) ; + + i ) {
usb_free_urb ( port - > read_urbs [ i ] ) ;
kfree ( port - > bulk_in_buffers [ i ] ) ;
}
2010-05-06 01:57:37 +04:00
for ( i = 0 ; i < ARRAY_SIZE ( port - > write_urbs ) ; + + i ) {
usb_free_urb ( port - > write_urbs [ i ] ) ;
kfree ( port - > bulk_out_buffers [ i ] ) ;
}
2009-12-23 11:10:48 +03:00
kfifo_free ( & port - > write_fifo ) ;
2005-04-17 02:20:36 +04:00
kfree ( port - > interrupt_in_buffer ) ;
kfree ( port - > interrupt_out_buffer ) ;
2012-11-15 12:49:56 +04:00
tty_port_destroy ( & port - > port ) ;
2005-04-17 02:20:36 +04:00
kfree ( port ) ;
}
2008-07-22 14:12:24 +04:00
static struct usb_serial * create_serial ( struct usb_device * dev ,
struct usb_interface * interface ,
struct usb_serial_driver * driver )
2005-04-17 02:20:36 +04:00
{
struct usb_serial * serial ;
2006-02-27 23:29:43 +03:00
serial = kzalloc ( sizeof ( * serial ) , GFP_KERNEL ) ;
2013-03-21 15:36:26 +04:00
if ( ! serial )
2005-04-17 02:20:36 +04:00
return NULL ;
serial - > dev = usb_get_dev ( dev ) ;
2005-06-21 08:15:16 +04:00
serial - > type = driver ;
2013-03-19 12:21:09 +04:00
serial - > interface = usb_get_intf ( interface ) ;
2005-04-17 02:20:36 +04:00
kref_init ( & serial - > kref ) ;
2008-01-16 19:18:52 +03:00
mutex_init ( & serial - > disc_mutex ) ;
2013-06-07 22:04:28 +04:00
serial - > minors_reserved = 0 ;
2005-04-17 02:20:36 +04:00
return serial ;
}
2006-12-17 23:50:23 +03:00
static const struct usb_device_id * match_dynamic_id ( struct usb_interface * intf ,
2008-07-22 14:12:24 +04:00
struct usb_serial_driver * drv )
2006-12-17 23:50:23 +03:00
{
struct usb_dynid * dynid ;
spin_lock ( & drv - > dynids . lock ) ;
list_for_each_entry ( dynid , & drv - > dynids . list , node ) {
if ( usb_match_one_id ( intf , & dynid - > id ) ) {
spin_unlock ( & drv - > dynids . lock ) ;
return & dynid - > id ;
}
}
spin_unlock ( & drv - > dynids . lock ) ;
return NULL ;
}
static const struct usb_device_id * get_iface_id ( struct usb_serial_driver * drv ,
struct usb_interface * intf )
{
const struct usb_device_id * id ;
id = usb_match_id ( intf , drv - > id_table ) ;
if ( id ) {
2012-09-14 03:30:31 +04:00
dev_dbg ( & intf - > dev , " static descriptor matches \n " ) ;
2006-12-17 23:50:23 +03:00
goto exit ;
}
id = match_dynamic_id ( intf , drv ) ;
if ( id )
2012-09-14 03:30:31 +04:00
dev_dbg ( & intf - > dev , " dynamic descriptor matches \n " ) ;
2006-12-17 23:50:23 +03:00
exit :
return id ;
}
2010-06-02 01:04:42 +04:00
/* Caller must hold table_lock */
2008-07-22 14:12:24 +04:00
static struct usb_serial_driver * search_serial_device (
struct usb_interface * iface )
2005-04-17 02:20:36 +04:00
{
2012-05-30 12:00:14 +04:00
const struct usb_device_id * id = NULL ;
2007-10-11 00:24:06 +04:00
struct usb_serial_driver * drv ;
2012-05-30 12:00:14 +04:00
struct usb_driver * driver = to_usb_driver ( iface - > dev . driver ) ;
2005-04-17 02:20:36 +04:00
2006-01-10 02:13:33 +03:00
/* Check if the usb id matches a known device */
2007-10-11 00:24:06 +04:00
list_for_each_entry ( drv , & usb_serial_driver_list , driver_list ) {
2012-05-30 12:00:14 +04:00
if ( drv - > usb_driver = = driver )
id = get_iface_id ( drv , iface ) ;
2006-12-17 23:50:23 +03:00
if ( id )
2007-10-11 00:24:06 +04:00
return drv ;
2005-04-17 02:20:36 +04:00
}
return NULL ;
}
2013-03-21 15:36:23 +04:00
static int serial_port_carrier_raised ( struct tty_port * port )
2009-06-11 15:26:29 +04:00
{
struct usb_serial_port * p = container_of ( port , struct usb_serial_port , port ) ;
struct usb_serial_driver * drv = p - > serial - > type ;
2011-11-10 20:40:43 +04:00
2009-06-11 15:26:29 +04:00
if ( drv - > carrier_raised )
return drv - > carrier_raised ( p ) ;
/* No carrier control - don't block */
2011-11-10 20:40:43 +04:00
return 1 ;
2009-06-11 15:26:29 +04:00
}
2013-03-21 15:36:23 +04:00
static void serial_port_dtr_rts ( struct tty_port * port , int on )
2009-06-11 15:26:29 +04:00
{
struct usb_serial_port * p = container_of ( port , struct usb_serial_port , port ) ;
2013-06-26 18:47:22 +04:00
struct usb_serial_driver * drv = p - > serial - > type ;
2011-11-10 20:40:43 +04:00
2013-06-26 18:47:22 +04:00
if ( drv - > dtr_rts )
2009-06-11 15:26:29 +04:00
drv - > dtr_rts ( p , on ) ;
}
2015-02-18 06:34:52 +03:00
static ssize_t port_number_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct usb_serial_port * port = to_usb_serial_port ( dev ) ;
return sprintf ( buf , " %u \n " , port - > port_number ) ;
}
static DEVICE_ATTR_RO ( port_number ) ;
static struct attribute * usb_serial_port_attrs [ ] = {
& dev_attr_port_number . attr ,
NULL
} ;
ATTRIBUTE_GROUPS ( usb_serial_port ) ;
2009-06-11 15:26:29 +04:00
static const struct tty_port_operations serial_port_ops = {
2013-03-21 15:36:23 +04:00
. carrier_raised = serial_port_carrier_raised ,
. dtr_rts = serial_port_dtr_rts ,
. activate = serial_port_activate ,
. shutdown = serial_port_shutdown ,
2009-06-11 15:26:29 +04:00
} ;
2014-08-21 21:33:25 +04:00
static void find_endpoints ( struct usb_serial * serial ,
struct usb_serial_endpoints * epds )
{
struct device * dev = & serial - > interface - > dev ;
struct usb_host_interface * iface_desc ;
struct usb_endpoint_descriptor * epd ;
unsigned int i ;
2017-03-02 14:51:18 +03:00
BUILD_BUG_ON ( ARRAY_SIZE ( epds - > bulk_in ) < USB_MAXENDPOINTS / 2 ) ;
BUILD_BUG_ON ( ARRAY_SIZE ( epds - > bulk_out ) < USB_MAXENDPOINTS / 2 ) ;
BUILD_BUG_ON ( ARRAY_SIZE ( epds - > interrupt_in ) < USB_MAXENDPOINTS / 2 ) ;
BUILD_BUG_ON ( ARRAY_SIZE ( epds - > interrupt_out ) < USB_MAXENDPOINTS / 2 ) ;
2014-08-21 21:33:25 +04:00
iface_desc = serial - > interface - > cur_altsetting ;
for ( i = 0 ; i < iface_desc - > desc . bNumEndpoints ; + + i ) {
epd = & iface_desc - > endpoint [ i ] . desc ;
if ( usb_endpoint_is_bulk_in ( epd ) ) {
dev_dbg ( dev , " found bulk in on endpoint %u \n " , i ) ;
epds - > bulk_in [ epds - > num_bulk_in + + ] = epd ;
} else if ( usb_endpoint_is_bulk_out ( epd ) ) {
dev_dbg ( dev , " found bulk out on endpoint %u \n " , i ) ;
epds - > bulk_out [ epds - > num_bulk_out + + ] = epd ;
} else if ( usb_endpoint_is_int_in ( epd ) ) {
dev_dbg ( dev , " found interrupt in on endpoint %u \n " , i ) ;
epds - > interrupt_in [ epds - > num_interrupt_in + + ] = epd ;
} else if ( usb_endpoint_is_int_out ( epd ) ) {
dev_dbg ( dev , " found interrupt out on endpoint %u \n " , i ) ;
epds - > interrupt_out [ epds - > num_interrupt_out + + ] = epd ;
}
}
}
2017-06-20 13:52:02 +03:00
static int setup_port_bulk_in ( struct usb_serial_port * port ,
struct usb_endpoint_descriptor * epd )
{
struct usb_serial_driver * type = port - > serial - > type ;
struct usb_device * udev = port - > serial - > dev ;
int buffer_size ;
int i ;
buffer_size = max_t ( int , type - > bulk_in_size , usb_endpoint_maxp ( epd ) ) ;
port - > bulk_in_size = buffer_size ;
port - > bulk_in_endpointAddress = epd - > bEndpointAddress ;
for ( i = 0 ; i < ARRAY_SIZE ( port - > read_urbs ) ; + + i ) {
set_bit ( i , & port - > read_urbs_free ) ;
port - > read_urbs [ i ] = usb_alloc_urb ( 0 , GFP_KERNEL ) ;
if ( ! port - > read_urbs [ i ] )
return - ENOMEM ;
port - > bulk_in_buffers [ i ] = kmalloc ( buffer_size , GFP_KERNEL ) ;
if ( ! port - > bulk_in_buffers [ i ] )
return - ENOMEM ;
usb_fill_bulk_urb ( port - > read_urbs [ i ] , udev ,
usb_rcvbulkpipe ( udev , epd - > bEndpointAddress ) ,
port - > bulk_in_buffers [ i ] , buffer_size ,
type - > read_bulk_callback , port ) ;
}
port - > read_urb = port - > read_urbs [ 0 ] ;
port - > bulk_in_buffer = port - > bulk_in_buffers [ 0 ] ;
return 0 ;
}
static int setup_port_bulk_out ( struct usb_serial_port * port ,
struct usb_endpoint_descriptor * epd )
{
struct usb_serial_driver * type = port - > serial - > type ;
struct usb_device * udev = port - > serial - > dev ;
int buffer_size ;
int i ;
if ( kfifo_alloc ( & port - > write_fifo , PAGE_SIZE , GFP_KERNEL ) )
return - ENOMEM ;
if ( type - > bulk_out_size )
buffer_size = type - > bulk_out_size ;
else
buffer_size = usb_endpoint_maxp ( epd ) ;
port - > bulk_out_size = buffer_size ;
port - > bulk_out_endpointAddress = epd - > bEndpointAddress ;
for ( i = 0 ; i < ARRAY_SIZE ( port - > write_urbs ) ; + + i ) {
set_bit ( i , & port - > write_urbs_free ) ;
port - > write_urbs [ i ] = usb_alloc_urb ( 0 , GFP_KERNEL ) ;
if ( ! port - > write_urbs [ i ] )
return - ENOMEM ;
port - > bulk_out_buffers [ i ] = kmalloc ( buffer_size , GFP_KERNEL ) ;
if ( ! port - > bulk_out_buffers [ i ] )
return - ENOMEM ;
usb_fill_bulk_urb ( port - > write_urbs [ i ] , udev ,
usb_sndbulkpipe ( udev , epd - > bEndpointAddress ) ,
port - > bulk_out_buffers [ i ] , buffer_size ,
type - > write_bulk_callback , port ) ;
}
port - > write_urb = port - > write_urbs [ 0 ] ;
port - > bulk_out_buffer = port - > bulk_out_buffers [ 0 ] ;
return 0 ;
}
static int setup_port_interrupt_in ( struct usb_serial_port * port ,
struct usb_endpoint_descriptor * epd )
{
struct usb_serial_driver * type = port - > serial - > type ;
struct usb_device * udev = port - > serial - > dev ;
int buffer_size ;
port - > interrupt_in_urb = usb_alloc_urb ( 0 , GFP_KERNEL ) ;
if ( ! port - > interrupt_in_urb )
return - ENOMEM ;
buffer_size = usb_endpoint_maxp ( epd ) ;
port - > interrupt_in_endpointAddress = epd - > bEndpointAddress ;
port - > interrupt_in_buffer = kmalloc ( buffer_size , GFP_KERNEL ) ;
if ( ! port - > interrupt_in_buffer )
return - ENOMEM ;
usb_fill_int_urb ( port - > interrupt_in_urb , udev ,
usb_rcvintpipe ( udev , epd - > bEndpointAddress ) ,
port - > interrupt_in_buffer , buffer_size ,
type - > read_int_callback , port ,
epd - > bInterval ) ;
return 0 ;
}
static int setup_port_interrupt_out ( struct usb_serial_port * port ,
struct usb_endpoint_descriptor * epd )
{
struct usb_serial_driver * type = port - > serial - > type ;
struct usb_device * udev = port - > serial - > dev ;
int buffer_size ;
port - > interrupt_out_urb = usb_alloc_urb ( 0 , GFP_KERNEL ) ;
if ( ! port - > interrupt_out_urb )
return - ENOMEM ;
buffer_size = usb_endpoint_maxp ( epd ) ;
port - > interrupt_out_size = buffer_size ;
port - > interrupt_out_endpointAddress = epd - > bEndpointAddress ;
port - > interrupt_out_buffer = kmalloc ( buffer_size , GFP_KERNEL ) ;
if ( ! port - > interrupt_out_buffer )
return - ENOMEM ;
usb_fill_int_urb ( port - > interrupt_out_urb , udev ,
usb_sndintpipe ( udev , epd - > bEndpointAddress ) ,
port - > interrupt_out_buffer , buffer_size ,
type - > write_int_callback , port ,
epd - > bInterval ) ;
return 0 ;
}
2012-05-08 01:46:48 +04:00
static int usb_serial_probe ( struct usb_interface * interface ,
2005-04-17 02:20:36 +04:00
const struct usb_device_id * id )
{
2012-09-14 03:30:31 +04:00
struct device * ddev = & interface - > dev ;
2008-07-22 14:12:24 +04:00
struct usb_device * dev = interface_to_usbdev ( interface ) ;
2005-04-17 02:20:36 +04:00
struct usb_serial * serial = NULL ;
struct usb_serial_port * port ;
2014-08-21 21:33:25 +04:00
struct usb_serial_endpoints * epds ;
2005-06-21 08:15:16 +04:00
struct usb_serial_driver * type = NULL ;
2005-04-17 02:20:36 +04:00
int retval ;
int i ;
int num_ports = 0 ;
2017-03-02 14:51:15 +03:00
unsigned char max_endpoints ;
2005-04-17 02:20:36 +04:00
2010-06-02 01:04:42 +04:00
mutex_lock ( & table_lock ) ;
2005-04-17 02:20:36 +04:00
type = search_serial_device ( interface ) ;
if ( ! type ) {
2010-06-02 01:04:42 +04:00
mutex_unlock ( & table_lock ) ;
2012-09-14 03:30:31 +04:00
dev_dbg ( ddev , " none matched \n " ) ;
2005-04-17 02:20:36 +04:00
return - ENODEV ;
}
2010-06-02 01:04:42 +04:00
if ( ! try_module_get ( type - > driver . owner ) ) {
mutex_unlock ( & table_lock ) ;
2012-09-14 03:30:31 +04:00
dev_err ( ddev , " module get failed, exiting \n " ) ;
2010-06-02 01:04:42 +04:00
return - EIO ;
}
mutex_unlock ( & table_lock ) ;
2008-07-22 14:12:24 +04:00
serial = create_serial ( dev , interface , type ) ;
2005-04-17 02:20:36 +04:00
if ( ! serial ) {
2017-03-02 14:51:14 +03:00
retval = - ENOMEM ;
goto err_put_module ;
2005-04-17 02:20:36 +04:00
}
/* if this device type has a probe function, call it */
if ( type - > probe ) {
const struct usb_device_id * id ;
2006-12-17 23:50:23 +03:00
id = get_iface_id ( type , interface ) ;
2005-04-17 02:20:36 +04:00
retval = type - > probe ( serial , id ) ;
if ( retval ) {
2012-09-14 03:30:31 +04:00
dev_dbg ( ddev , " sub driver rejected device \n " ) ;
2017-03-02 14:51:14 +03:00
goto err_put_serial ;
2005-04-17 02:20:36 +04:00
}
}
/* descriptor matches, let's find the endpoints needed */
2014-08-21 21:33:25 +04:00
epds = kzalloc ( sizeof ( * epds ) , GFP_KERNEL ) ;
if ( ! epds ) {
retval = - ENOMEM ;
goto err_put_serial ;
2005-04-17 02:20:36 +04:00
}
2014-08-21 21:33:25 +04:00
find_endpoints ( serial , epds ) ;
2017-03-02 14:51:19 +03:00
if ( epds - > num_bulk_in < type - > num_bulk_in | |
epds - > num_bulk_out < type - > num_bulk_out | |
epds - > num_interrupt_in < type - > num_interrupt_in | |
epds - > num_interrupt_out < type - > num_interrupt_out ) {
dev_err ( ddev , " required endpoints missing \n " ) ;
retval = - ENODEV ;
goto err_free_epds ;
}
2017-03-16 19:13:32 +03:00
if ( type - > calc_num_ports ) {
retval = type - > calc_num_ports ( serial , epds ) ;
if ( retval < 0 )
2017-03-02 14:51:19 +03:00
goto err_free_epds ;
2017-03-16 19:13:32 +03:00
num_ports = retval ;
2005-04-17 02:20:36 +04:00
}
2017-03-16 19:13:32 +03:00
if ( ! num_ports )
num_ports = type - > num_ports ;
2014-08-27 13:55:19 +04:00
if ( num_ports > MAX_NUM_PORTS ) {
dev_warn ( ddev , " too many ports requested: %d \n " , num_ports ) ;
num_ports = MAX_NUM_PORTS ;
}
2017-03-02 14:51:15 +03:00
serial - > num_ports = ( unsigned char ) num_ports ;
2014-08-21 21:33:25 +04:00
serial - > num_bulk_in = epds - > num_bulk_in ;
serial - > num_bulk_out = epds - > num_bulk_out ;
serial - > num_interrupt_in = epds - > num_interrupt_in ;
serial - > num_interrupt_out = epds - > num_interrupt_out ;
2005-04-17 02:20:36 +04:00
2007-10-11 00:24:06 +04:00
/* found all that we need */
2012-09-14 03:30:31 +04:00
dev_info ( ddev , " %s converter detected \n " , type - > description ) ;
2007-10-11 00:24:06 +04:00
2005-04-17 02:20:36 +04:00
/* create our ports, we need as many as the max endpoints */
2008-07-22 14:12:24 +04:00
/* we don't use num_ports here because some devices have more
endpoint pairs than ports */
2014-08-21 21:33:25 +04:00
max_endpoints = max ( epds - > num_bulk_in , epds - > num_bulk_out ) ;
max_endpoints = max ( max_endpoints , epds - > num_interrupt_in ) ;
max_endpoints = max ( max_endpoints , epds - > num_interrupt_out ) ;
2017-03-02 14:51:15 +03:00
max_endpoints = max ( max_endpoints , serial - > num_ports ) ;
2005-04-17 02:20:36 +04:00
serial - > num_port_pointers = max_endpoints ;
2007-01-13 09:31:27 +03:00
2014-03-12 22:09:42 +04:00
dev_dbg ( ddev , " setting up %d port structure(s) \n " , max_endpoints ) ;
2005-04-17 02:20:36 +04:00
for ( i = 0 ; i < max_endpoints ; + + i ) {
2006-02-27 23:29:43 +03:00
port = kzalloc ( sizeof ( struct usb_serial_port ) , GFP_KERNEL ) ;
2017-06-20 13:52:03 +03:00
if ( ! port ) {
retval = - ENOMEM ;
goto err_free_epds ;
}
2008-10-13 13:39:46 +04:00
tty_port_init ( & port - > port ) ;
2009-06-11 15:26:29 +04:00
port - > port . ops = & serial_port_ops ;
2005-04-17 02:20:36 +04:00
port - > serial = serial ;
2005-04-23 23:49:16 +04:00
spin_lock_init ( & port - > lock ) ;
2009-10-06 19:06:36 +04:00
/* Keep this for private driver use for the moment but
should probably go away */
2006-11-22 17:57:56 +03:00
INIT_WORK ( & port - > work , usb_serial_port_work ) ;
2005-04-17 02:20:36 +04:00
serial - > port [ i ] = port ;
2009-09-01 19:38:34 +04:00
port - > dev . parent = & interface - > dev ;
port - > dev . driver = NULL ;
port - > dev . bus = & usb_serial_bus_type ;
2013-03-21 15:36:47 +04:00
port - > dev . release = & usb_serial_port_release ;
2015-02-18 06:34:52 +03:00
port - > dev . groups = usb_serial_port_groups ;
2009-09-01 19:38:34 +04:00
device_initialize ( & port - > dev ) ;
2005-04-17 02:20:36 +04:00
}
/* set up the endpoint information */
2014-08-21 21:33:25 +04:00
for ( i = 0 ; i < epds - > num_bulk_in ; + + i ) {
2017-06-20 13:52:02 +03:00
retval = setup_port_bulk_in ( serial - > port [ i ] , epds - > bulk_in [ i ] ) ;
if ( retval )
2017-06-20 13:52:03 +03:00
goto err_free_epds ;
2005-04-17 02:20:36 +04:00
}
2014-08-21 21:33:25 +04:00
for ( i = 0 ; i < epds - > num_bulk_out ; + + i ) {
2017-06-20 13:52:02 +03:00
retval = setup_port_bulk_out ( serial - > port [ i ] ,
epds - > bulk_out [ i ] ) ;
if ( retval )
2017-06-20 13:52:03 +03:00
goto err_free_epds ;
2005-04-17 02:20:36 +04:00
}
if ( serial - > type - > read_int_callback ) {
2014-08-21 21:33:25 +04:00
for ( i = 0 ; i < epds - > num_interrupt_in ; + + i ) {
2017-06-20 13:52:02 +03:00
retval = setup_port_interrupt_in ( serial - > port [ i ] ,
epds - > interrupt_in [ i ] ) ;
if ( retval )
2017-06-20 13:52:03 +03:00
goto err_free_epds ;
2005-04-17 02:20:36 +04:00
}
2014-08-21 21:33:25 +04:00
} else if ( epds - > num_interrupt_in ) {
2012-09-14 03:30:31 +04:00
dev_dbg ( ddev , " The device claims to support interrupt in transfers, but read_int_callback is not defined \n " ) ;
2005-04-17 02:20:36 +04:00
}
2008-07-22 14:12:24 +04:00
2005-04-17 02:20:36 +04:00
if ( serial - > type - > write_int_callback ) {
2014-08-21 21:33:25 +04:00
for ( i = 0 ; i < epds - > num_interrupt_out ; + + i ) {
2017-06-20 13:52:02 +03:00
retval = setup_port_interrupt_out ( serial - > port [ i ] ,
epds - > interrupt_out [ i ] ) ;
if ( retval )
2017-06-20 13:52:03 +03:00
goto err_free_epds ;
2005-04-17 02:20:36 +04:00
}
2014-08-21 21:33:25 +04:00
} else if ( epds - > num_interrupt_out ) {
2012-09-14 03:30:31 +04:00
dev_dbg ( ddev , " The device claims to support interrupt out transfers, but write_int_callback is not defined \n " ) ;
2005-04-17 02:20:36 +04:00
}
2008-07-22 14:12:24 +04:00
2012-04-25 17:56:32 +04:00
usb_set_intfdata ( interface , serial ) ;
2005-04-17 02:20:36 +04:00
/* if this device type has an attach function, call it */
if ( type - > attach ) {
2008-07-22 14:12:24 +04:00
retval = type - > attach ( serial ) ;
2005-04-17 02:20:36 +04:00
if ( retval < 0 )
2017-06-20 13:52:03 +03:00
goto err_free_epds ;
2009-10-09 20:43:12 +04:00
serial - > attached = 1 ;
2005-04-17 02:20:36 +04:00
if ( retval > 0 ) {
2008-07-22 14:12:24 +04:00
/* quietly accept this device, but don't bind to a
serial port as it ' s about to disappear */
2009-05-27 19:25:52 +04:00
serial - > num_ports = 0 ;
2005-04-17 02:20:36 +04:00
goto exit ;
}
2009-10-09 20:43:12 +04:00
} else {
serial - > attached = 1 ;
2005-04-17 02:20:36 +04:00
}
2017-06-20 13:52:03 +03:00
retval = allocate_minors ( serial , num_ports ) ;
if ( retval ) {
2013-06-07 22:04:28 +04:00
dev_err ( ddev , " No more free serial minor numbers \n " ) ;
2017-06-20 13:52:03 +03:00
goto err_free_epds ;
2007-01-13 09:29:26 +03:00
}
2005-04-17 02:20:36 +04:00
/* register all of the individual ports with the driver core */
for ( i = 0 ; i < num_ports ; + + i ) {
port = serial - > port [ i ] ;
2013-06-06 21:32:00 +04:00
dev_set_name ( & port - > dev , " ttyUSB%d " , port - > minor ) ;
2014-03-12 22:09:42 +04:00
dev_dbg ( ddev , " registering %s \n " , dev_name ( & port - > dev ) ) ;
2010-07-13 19:56:24 +04:00
device_enable_async_suspend ( & port - > dev ) ;
2009-09-01 19:38:34 +04:00
retval = device_add ( & port - > dev ) ;
2012-03-29 00:10:49 +04:00
if ( retval )
2012-09-14 03:30:31 +04:00
dev_err ( ddev , " Error registering port device, continuing \n " ) ;
2005-04-17 02:20:36 +04:00
}
2016-10-21 13:56:27 +03:00
if ( num_ports > 0 )
usb_serial_console_init ( serial - > port [ 0 ] - > minor ) ;
2005-04-17 02:20:36 +04:00
exit :
2014-08-21 21:33:25 +04:00
kfree ( epds ) ;
2010-08-07 12:20:35 +04:00
module_put ( type - > driver . owner ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
2017-03-02 14:51:19 +03:00
err_free_epds :
kfree ( epds ) ;
2017-03-02 14:51:14 +03:00
err_put_serial :
2009-09-01 19:38:34 +04:00
usb_serial_put ( serial ) ;
2017-03-02 14:51:14 +03:00
err_put_module :
2010-08-07 12:20:35 +04:00
module_put ( type - > driver . owner ) ;
2017-03-02 14:51:14 +03:00
return retval ;
2005-04-17 02:20:36 +04:00
}
2012-05-08 01:02:13 +04:00
static void usb_serial_disconnect ( struct usb_interface * interface )
2005-04-17 02:20:36 +04:00
{
int i ;
2008-07-22 14:12:24 +04:00
struct usb_serial * serial = usb_get_intfdata ( interface ) ;
2005-04-17 02:20:36 +04:00
struct device * dev = & interface - > dev ;
struct usb_serial_port * port ;
2014-05-26 21:23:34 +04:00
struct tty_struct * tty ;
2005-04-17 02:20:36 +04:00
2006-04-25 09:46:17 +04:00
usb_serial_console_disconnect ( serial ) ;
2005-04-17 02:20:36 +04:00
2008-01-16 19:18:52 +03:00
mutex_lock ( & serial - > disc_mutex ) ;
/* must set a flag, to signal subdrivers */
serial - > disconnected = 1 ;
2009-04-14 19:31:02 +04:00
mutex_unlock ( & serial - > disc_mutex ) ;
2008-01-16 19:18:52 +03:00
for ( i = 0 ; i < serial - > num_ports ; + + i ) {
port = serial - > port [ i ] ;
2014-05-26 21:23:34 +04:00
tty = tty_port_tty_get ( & port - > port ) ;
if ( tty ) {
tty_vhangup ( tty ) ;
tty_kref_put ( tty ) ;
2009-04-14 19:31:02 +04:00
}
2014-05-26 21:23:34 +04:00
usb_serial_port_poison_urbs ( port ) ;
wake_up_interruptible ( & port - > port . delta_msr_wait ) ;
cancel_work_sync ( & port - > work ) ;
if ( device_is_registered ( & port - > dev ) )
device_del ( & port - > dev ) ;
2009-04-14 19:31:02 +04:00
}
2013-03-21 15:36:42 +04:00
if ( serial - > type - > disconnect )
serial - > type - > disconnect ( serial ) ;
2009-04-14 19:31:02 +04:00
2009-09-01 19:38:34 +04:00
/* let the last holder of this object cause it to be cleaned up */
2008-01-16 19:18:52 +03:00
usb_serial_put ( serial ) ;
2005-04-17 02:20:36 +04:00
dev_info ( dev , " device disconnected \n " ) ;
}
2007-04-27 22:54:57 +04:00
int usb_serial_suspend ( struct usb_interface * intf , pm_message_t message )
{
struct usb_serial * serial = usb_get_intfdata ( intf ) ;
int i , r = 0 ;
2009-02-05 18:54:25 +03:00
serial - > suspending = 1 ;
2013-03-15 08:08:54 +04:00
/*
* serial - > type - > suspend ( ) MUST return 0 in system sleep context ,
* otherwise , the resume callback has to recover device from
* previous suspend failure .
*/
2009-07-21 10:47:34 +04:00
if ( serial - > type - > suspend ) {
r = serial - > type - > suspend ( serial , message ) ;
2009-10-01 17:01:17 +04:00
if ( r < 0 ) {
serial - > suspending = 0 ;
2009-07-21 10:47:34 +04:00
goto err_out ;
2009-10-01 17:01:17 +04:00
}
2009-07-21 10:47:34 +04:00
}
2014-05-26 21:23:34 +04:00
for ( i = 0 ; i < serial - > num_ports ; + + i )
usb_serial_port_poison_urbs ( serial - > port [ i ] ) ;
2009-07-21 10:47:34 +04:00
err_out :
2007-04-27 22:54:57 +04:00
return r ;
}
EXPORT_SYMBOL ( usb_serial_suspend ) ;
2013-03-21 15:36:49 +04:00
static void usb_serial_unpoison_port_urbs ( struct usb_serial * serial )
{
int i ;
2014-05-26 21:23:34 +04:00
for ( i = 0 ; i < serial - > num_ports ; + + i )
usb_serial_port_unpoison_urbs ( serial - > port [ i ] ) ;
2013-03-21 15:36:49 +04:00
}
2007-04-27 22:54:57 +04:00
int usb_serial_resume ( struct usb_interface * intf )
{
struct usb_serial * serial = usb_get_intfdata ( intf ) ;
2009-02-06 20:06:43 +03:00
int rv ;
2007-04-27 22:54:57 +04:00
2013-03-21 15:36:49 +04:00
usb_serial_unpoison_port_urbs ( serial ) ;
2009-02-05 18:54:25 +03:00
serial - > suspending = 0 ;
2007-10-25 21:58:43 +04:00
if ( serial - > type - > resume )
2009-02-06 20:06:43 +03:00
rv = serial - > type - > resume ( serial ) ;
else
rv = usb_serial_generic_resume ( serial ) ;
2009-02-05 18:54:25 +03:00
2009-02-06 20:06:43 +03:00
return rv ;
2007-04-27 22:54:57 +04:00
}
EXPORT_SYMBOL ( usb_serial_resume ) ;
2012-05-16 02:40:00 +04:00
static int usb_serial_reset_resume ( struct usb_interface * intf )
{
struct usb_serial * serial = usb_get_intfdata ( intf ) ;
int rv ;
2013-03-21 15:36:49 +04:00
usb_serial_unpoison_port_urbs ( serial ) ;
2012-05-16 02:40:00 +04:00
serial - > suspending = 0 ;
2014-03-12 22:09:41 +04:00
if ( serial - > type - > reset_resume ) {
2012-05-16 02:40:00 +04:00
rv = serial - > type - > reset_resume ( serial ) ;
2014-03-12 22:09:41 +04:00
} else {
2012-05-16 19:37:17 +04:00
rv = - EOPNOTSUPP ;
intf - > needs_binding = 1 ;
}
2012-05-16 02:40:00 +04:00
return rv ;
}
2006-10-02 13:17:18 +04:00
static const struct tty_operations serial_ops = {
2005-04-17 02:20:36 +04:00
. open = serial_open ,
. close = serial_close ,
. write = serial_write ,
2011-11-10 20:40:43 +04:00
. hangup = serial_hangup ,
2005-04-17 02:20:36 +04:00
. write_room = serial_write_room ,
. ioctl = serial_ioctl ,
. set_termios = serial_set_termios ,
. throttle = serial_throttle ,
. unthrottle = serial_unthrottle ,
. break_ctl = serial_break ,
. chars_in_buffer = serial_chars_in_buffer ,
2013-05-05 22:32:27 +04:00
. wait_until_sent = serial_wait_until_sent ,
2005-04-17 02:20:36 +04:00
. tiocmget = serial_tiocmget ,
. tiocmset = serial_tiocmset ,
2011-11-10 20:40:43 +04:00
. get_icount = serial_get_icount ,
2018-09-12 06:28:07 +03:00
. set_serial = serial_set_serial ,
. get_serial = serial_get_serial ,
2011-11-10 20:40:43 +04:00
. cleanup = serial_cleanup ,
. install = serial_install ,
2018-04-13 22:04:45 +03:00
. proc_show = serial_proc_show ,
2005-04-17 02:20:36 +04:00
} ;
2009-06-11 15:26:29 +04:00
2005-04-17 02:20:36 +04:00
struct tty_driver * usb_serial_tty_driver ;
static int __init usb_serial_init ( void )
{
int result ;
2013-06-06 21:31:35 +04:00
usb_serial_tty_driver = alloc_tty_driver ( USB_SERIAL_TTY_MINORS ) ;
2005-04-17 02:20:36 +04:00
if ( ! usb_serial_tty_driver )
return - ENOMEM ;
/* Initialize our global data */
result = bus_register ( & usb_serial_bus_type ) ;
if ( result ) {
2012-09-14 03:30:31 +04:00
pr_err ( " %s - registering bus driver failed \n " , __func__ ) ;
2005-04-17 02:20:36 +04:00
goto exit_bus ;
}
usb_serial_tty_driver - > driver_name = " usbserial " ;
2011-11-10 20:40:43 +04:00
usb_serial_tty_driver - > name = " ttyUSB " ;
2013-06-06 21:31:35 +04:00
usb_serial_tty_driver - > major = USB_SERIAL_TTY_MAJOR ;
2005-04-17 02:20:36 +04:00
usb_serial_tty_driver - > minor_start = 0 ;
usb_serial_tty_driver - > type = TTY_DRIVER_TYPE_SERIAL ;
usb_serial_tty_driver - > subtype = SERIAL_TYPE_NORMAL ;
2008-07-22 14:12:24 +04:00
usb_serial_tty_driver - > flags = TTY_DRIVER_REAL_RAW |
TTY_DRIVER_DYNAMIC_DEV ;
2005-04-17 02:20:36 +04:00
usb_serial_tty_driver - > init_termios = tty_std_termios ;
2008-07-22 14:12:24 +04:00
usb_serial_tty_driver - > init_termios . c_cflag = B9600 | CS8 | CREAD
| HUPCL | CLOCAL ;
2008-04-08 20:16:06 +04:00
usb_serial_tty_driver - > init_termios . c_ispeed = 9600 ;
usb_serial_tty_driver - > init_termios . c_ospeed = 9600 ;
2005-04-17 02:20:36 +04:00
tty_set_operations ( usb_serial_tty_driver , & serial_ops ) ;
result = tty_register_driver ( usb_serial_tty_driver ) ;
if ( result ) {
2012-09-14 03:30:31 +04:00
pr_err ( " %s - tty_register_driver failed \n " , __func__ ) ;
2005-04-17 02:20:36 +04:00
goto exit_reg_driver ;
}
2005-05-26 16:55:55 +04:00
/* register the generic driver, if we should */
2012-09-18 19:05:17 +04:00
result = usb_serial_generic_register ( ) ;
2005-05-26 16:55:55 +04:00
if ( result < 0 ) {
2012-09-14 03:30:31 +04:00
pr_err ( " %s - registering generic driver failed \n " , __func__ ) ;
2005-05-26 16:55:55 +04:00
goto exit_generic ;
}
2005-04-17 02:20:36 +04:00
return result ;
2005-05-26 16:55:55 +04:00
exit_generic :
2005-04-17 02:20:36 +04:00
tty_unregister_driver ( usb_serial_tty_driver ) ;
exit_reg_driver :
bus_unregister ( & usb_serial_bus_type ) ;
exit_bus :
2012-09-14 03:30:31 +04:00
pr_err ( " %s - returning with error %d \n " , __func__ , result ) ;
2005-04-17 02:20:36 +04:00
put_tty_driver ( usb_serial_tty_driver ) ;
return result ;
}
static void __exit usb_serial_exit ( void )
{
usb_serial_console_exit ( ) ;
usb_serial_generic_deregister ( ) ;
tty_unregister_driver ( usb_serial_tty_driver ) ;
put_tty_driver ( usb_serial_tty_driver ) ;
bus_unregister ( & usb_serial_bus_type ) ;
2015-07-08 18:26:37 +03:00
idr_destroy ( & serial_minors ) ;
2005-04-17 02:20:36 +04:00
}
module_init ( usb_serial_init ) ;
module_exit ( usb_serial_exit ) ;
# define set_to_generic_if_null(type, function) \
do { \
if ( ! type - > function ) { \
type - > function = usb_serial_generic_ # # function ; \
2013-03-21 15:36:44 +04:00
pr_debug ( " %s: using generic " # function " \n " , \
type - > driver . name ) ; \
} \
2005-04-17 02:20:36 +04:00
} while ( 0 )
2013-03-21 15:36:44 +04:00
static void usb_serial_operations_init ( struct usb_serial_driver * device )
2005-04-17 02:20:36 +04:00
{
set_to_generic_if_null ( device , open ) ;
set_to_generic_if_null ( device , write ) ;
set_to_generic_if_null ( device , close ) ;
set_to_generic_if_null ( device , write_room ) ;
set_to_generic_if_null ( device , chars_in_buffer ) ;
2013-05-08 19:51:43 +04:00
if ( device - > tx_empty )
set_to_generic_if_null ( device , wait_until_sent ) ;
2005-04-17 02:20:36 +04:00
set_to_generic_if_null ( device , read_bulk_callback ) ;
set_to_generic_if_null ( device , write_bulk_callback ) ;
2010-03-18 01:05:57 +03:00
set_to_generic_if_null ( device , process_read_urb ) ;
2010-03-18 01:06:08 +03:00
set_to_generic_if_null ( device , prepare_write_buffer ) ;
2005-04-17 02:20:36 +04:00
}
2012-02-25 00:50:30 +04:00
static int usb_serial_register ( struct usb_serial_driver * driver )
2005-04-17 02:20:36 +04:00
{
int retval ;
2009-02-14 16:21:13 +03:00
if ( usb_disabled ( ) )
return - ENODEV ;
2005-06-21 08:15:16 +04:00
if ( ! driver - > description )
driver - > description = driver - > driver . name ;
2011-01-11 22:16:50 +03:00
if ( ! driver - > usb_driver ) {
WARN ( 1 , " Serial driver %s has no usb_driver \n " ,
driver - > description ) ;
return - EINVAL ;
}
2005-06-21 08:15:16 +04:00
2013-03-21 15:36:44 +04:00
usb_serial_operations_init ( driver ) ;
2005-04-17 02:20:36 +04:00
/* Add this device to our list of devices */
2010-06-02 01:04:42 +04:00
mutex_lock ( & table_lock ) ;
2005-06-21 08:15:16 +04:00
list_add ( & driver - > driver_list , & usb_serial_driver_list ) ;
2005-04-17 02:20:36 +04:00
2005-06-21 08:15:16 +04:00
retval = usb_serial_bus_register ( driver ) ;
2005-04-17 02:20:36 +04:00
if ( retval ) {
2012-09-14 03:30:31 +04:00
pr_err ( " problem %d when registering driver %s \n " , retval , driver - > description ) ;
2005-06-21 08:15:16 +04:00
list_del ( & driver - > driver_list ) ;
2014-03-12 22:09:41 +04:00
} else {
2012-09-18 20:10:29 +04:00
pr_info ( " USB Serial support registered for %s \n " , driver - > description ) ;
2014-03-12 22:09:41 +04:00
}
2010-06-02 01:04:42 +04:00
mutex_unlock ( & table_lock ) ;
2005-04-17 02:20:36 +04:00
return retval ;
}
2012-02-25 00:50:30 +04:00
static void usb_serial_deregister ( struct usb_serial_driver * device )
2005-04-17 02:20:36 +04:00
{
2012-09-18 20:10:29 +04:00
pr_info ( " USB Serial deregistering driver %s \n " , device - > description ) ;
2014-04-23 13:32:19 +04:00
2010-06-02 01:04:42 +04:00
mutex_lock ( & table_lock ) ;
2005-04-17 02:20:36 +04:00
list_del ( & device - > driver_list ) ;
2010-06-02 01:04:42 +04:00
mutex_unlock ( & table_lock ) ;
2014-04-23 13:32:19 +04:00
usb_serial_bus_deregister ( device ) ;
2005-04-17 02:20:36 +04:00
}
2012-02-23 23:55:59 +04:00
/**
* usb_serial_register_drivers - register drivers for a usb - serial module
* @ serial_drivers : NULL - terminated array of pointers to drivers to be registered
2012-05-09 02:46:14 +04:00
* @ name : name of the usb_driver for this set of @ serial_drivers
* @ id_table : list of all devices this @ serial_drivers set binds to
2012-02-23 23:55:59 +04:00
*
2012-05-09 02:46:14 +04:00
* Registers all the drivers in the @ serial_drivers array , and dynamically
* creates a struct usb_driver with the name @ name and id_table of @ id_table .
2012-02-23 23:55:59 +04:00
*/
2012-05-09 02:46:14 +04:00
int usb_serial_register_drivers ( struct usb_serial_driver * const serial_drivers [ ] ,
const char * name ,
const struct usb_device_id * id_table )
2012-02-23 23:55:59 +04:00
{
int rc ;
2012-05-09 02:46:14 +04:00
struct usb_driver * udriver ;
2012-02-23 23:55:59 +04:00
struct usb_serial_driver * const * sd ;
/*
* udriver must be registered before any of the serial drivers ,
* because the store_new_id ( ) routine for the serial drivers ( in
* bus . c ) probes udriver .
*
* Performance hack : We don ' t want udriver to be probed until
* the serial drivers are registered , because the probe would
* simply fail for lack of a matching serial driver .
2012-05-09 02:46:14 +04:00
* So we leave udriver ' s id_table set to NULL until we are all set .
2012-05-07 19:20:06 +04:00
*
* Suspend / resume support is implemented in the usb - serial core ,
* so fill in the PM - related fields in udriver .
2012-02-23 23:55:59 +04:00
*/
2012-05-09 02:46:14 +04:00
udriver = kzalloc ( sizeof ( * udriver ) , GFP_KERNEL ) ;
if ( ! udriver )
return - ENOMEM ;
2012-02-23 23:55:59 +04:00
2012-05-09 02:46:14 +04:00
udriver - > name = name ;
2012-02-23 23:55:59 +04:00
udriver - > no_dynamic_id = 1 ;
2012-05-07 19:20:06 +04:00
udriver - > supports_autosuspend = 1 ;
udriver - > suspend = usb_serial_suspend ;
udriver - > resume = usb_serial_resume ;
2012-05-08 00:48:33 +04:00
udriver - > probe = usb_serial_probe ;
2012-05-08 01:02:13 +04:00
udriver - > disconnect = usb_serial_disconnect ;
2012-05-16 02:40:00 +04:00
/* we only set the reset_resume field if the serial_driver has one */
for ( sd = serial_drivers ; * sd ; + + sd ) {
2012-09-19 11:15:21 +04:00
if ( ( * sd ) - > reset_resume ) {
2012-05-16 02:40:00 +04:00
udriver - > reset_resume = usb_serial_reset_resume ;
break ;
2012-09-19 11:15:21 +04:00
}
2012-05-16 02:40:00 +04:00
}
2012-02-23 23:55:59 +04:00
rc = usb_register ( udriver ) ;
if ( rc )
2016-08-08 04:34:46 +03:00
goto failed_usb_register ;
2012-02-23 23:55:59 +04:00
for ( sd = serial_drivers ; * sd ; + + sd ) {
( * sd ) - > usb_driver = udriver ;
rc = usb_serial_register ( * sd ) ;
if ( rc )
goto failed ;
}
2012-05-09 02:46:14 +04:00
/* Now set udriver's id_table and look for matches */
udriver - > id_table = id_table ;
2012-02-23 23:55:59 +04:00
rc = driver_attach ( & udriver - > drvwrap . driver ) ;
return 0 ;
failed :
while ( sd - - > serial_drivers )
usb_serial_deregister ( * sd ) ;
usb_deregister ( udriver ) ;
2016-08-08 04:34:46 +03:00
failed_usb_register :
kfree ( udriver ) ;
2012-02-23 23:55:59 +04:00
return rc ;
}
EXPORT_SYMBOL_GPL ( usb_serial_register_drivers ) ;
/**
* usb_serial_deregister_drivers - deregister drivers for a usb - serial module
* @ serial_drivers : NULL - terminated array of pointers to drivers to be deregistered
*
2012-05-09 02:46:14 +04:00
* Deregisters all the drivers in the @ serial_drivers array and deregisters and
* frees the struct usb_driver that was created by the call to
* usb_serial_register_drivers ( ) .
2012-02-23 23:55:59 +04:00
*/
2012-05-09 02:46:14 +04:00
void usb_serial_deregister_drivers ( struct usb_serial_driver * const serial_drivers [ ] )
2012-02-23 23:55:59 +04:00
{
2012-05-09 02:46:14 +04:00
struct usb_driver * udriver = ( * serial_drivers ) - > usb_driver ;
2012-02-23 23:55:59 +04:00
for ( ; * serial_drivers ; + + serial_drivers )
usb_serial_deregister ( * serial_drivers ) ;
usb_deregister ( udriver ) ;
2012-05-09 02:46:14 +04:00
kfree ( udriver ) ;
2012-02-23 23:55:59 +04:00
}
EXPORT_SYMBOL_GPL ( usb_serial_deregister_drivers ) ;
2005-04-17 02:20:36 +04:00
2008-07-22 14:12:24 +04:00
MODULE_AUTHOR ( DRIVER_AUTHOR ) ;
MODULE_DESCRIPTION ( DRIVER_DESC ) ;
2017-11-03 20:12:08 +03:00
MODULE_LICENSE ( " GPL v2 " ) ;