2005-04-17 02:20:36 +04:00
/*
* USB Serial Converter driver
*
2005-06-21 08:15:16 +04:00
* Copyright ( C ) 1999 - 2005 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 )
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License version
* 2 as published by the Free Software Foundation .
*
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
*
*/
# 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>
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>
2005-04-17 02:20:36 +04:00
# include "pl2303.h"
/*
* Version Information
*/
# define DRIVER_AUTHOR "Greg Kroah-Hartman, greg@kroah.com, http: //www.kroah.com/linux/"
# define DRIVER_DESC "USB Serial Driver core"
2006-06-22 02:00:45 +04:00
static void port_free ( struct usb_serial_port * port ) ;
2005-04-17 02:20:36 +04:00
/* Driver structure we register with the USB core */
static struct usb_driver usb_serial_driver = {
. name = " usbserial " ,
. probe = usb_serial_probe ,
. disconnect = usb_serial_disconnect ,
2007-04-27 22:54:57 +04:00
. suspend = usb_serial_suspend ,
. resume = usb_serial_resume ,
2005-11-17 00:41:28 +03:00
. no_dynamic_id = 1 ,
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 .
*/
static int debug ;
2008-07-22 14:12:24 +04:00
/* initially all NULL */
static struct usb_serial * serial_table [ SERIAL_TTY_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 ) ;
struct usb_serial * usb_serial_get_by_index ( unsigned index )
{
2007-01-13 09:29:26 +03:00
struct usb_serial * serial ;
2007-07-24 17:13:42 +04:00
mutex_lock ( & table_lock ) ;
2007-01-13 09:29:26 +03:00
serial = serial_table [ index ] ;
2005-04-17 02:20:36 +04:00
if ( serial )
kref_get ( & serial - > kref ) ;
2007-07-24 17:13:42 +04:00
mutex_unlock ( & table_lock ) ;
2005-04-17 02:20:36 +04:00
return serial ;
}
2008-07-22 14:12:24 +04:00
static struct usb_serial * get_free_serial ( struct usb_serial * serial ,
int num_ports , unsigned int * minor )
2005-04-17 02:20:36 +04:00
{
unsigned int i , j ;
int good_spot ;
2008-03-04 03:08:34 +03:00
dbg ( " %s %d " , __func__ , num_ports ) ;
2005-04-17 02:20:36 +04:00
* minor = 0 ;
2007-07-24 17:13:42 +04:00
mutex_lock ( & table_lock ) ;
2005-04-17 02:20:36 +04:00
for ( i = 0 ; i < SERIAL_TTY_MINORS ; + + i ) {
if ( serial_table [ i ] )
continue ;
good_spot = 1 ;
for ( j = 1 ; j < = num_ports - 1 ; + + j )
if ( ( i + j > = SERIAL_TTY_MINORS ) | | ( serial_table [ i + j ] ) ) {
good_spot = 0 ;
i + = j ;
break ;
}
if ( good_spot = = 0 )
continue ;
* minor = i ;
2007-03-05 17:23:51 +03:00
j = 0 ;
2008-03-04 03:08:34 +03:00
dbg ( " %s - minor base = %d " , __func__ , * minor ) ;
2007-03-05 17:23:51 +03:00
for ( i = * minor ; ( i < ( * minor + num_ports ) ) & & ( i < SERIAL_TTY_MINORS ) ; + + i ) {
2005-04-17 02:20:36 +04:00
serial_table [ i ] = serial ;
2007-03-05 17:23:51 +03:00
serial - > port [ j + + ] - > number = i ;
}
2007-07-24 17:13:42 +04:00
mutex_unlock ( & table_lock ) ;
2005-04-17 02:20:36 +04:00
return serial ;
}
2007-07-24 17:13:42 +04:00
mutex_unlock ( & table_lock ) ;
2005-04-17 02:20:36 +04:00
return NULL ;
}
static void return_serial ( struct usb_serial * serial )
{
int i ;
2008-03-04 03:08:34 +03:00
dbg ( " %s " , __func__ ) ;
2005-04-17 02:20:36 +04:00
2008-07-22 14:12:24 +04:00
for ( i = 0 ; i < serial - > num_ports ; + + i )
2005-04-17 02:20:36 +04:00
serial_table [ serial - > minor + i ] = NULL ;
}
static void destroy_serial ( struct kref * kref )
{
struct usb_serial * serial ;
struct usb_serial_port * port ;
int i ;
serial = to_usb_serial ( kref ) ;
2008-03-04 03:08:34 +03:00
dbg ( " %s - %s " , __func__ , serial - > type - > description ) ;
2005-04-17 02:20:36 +04:00
2007-03-13 18:30:50 +03:00
/* return the minor range that this device had */
2008-07-29 20:01:04 +04:00
if ( serial - > minor ! = SERIAL_TTY_NO_MINOR )
return_serial ( serial ) ;
2007-03-13 18:30:50 +03:00
2005-04-17 02:20:36 +04:00
/* If this is a "fake" port, we have to clean it up here, as it will
* not get cleaned up in port_release ( ) as it was never registered with
* the driver core */
if ( serial - > num_ports < serial - > num_port_pointers ) {
2008-07-22 14:12:24 +04:00
for ( i = serial - > num_ports ;
i < serial - > num_port_pointers ; + + i ) {
2005-04-17 02:20:36 +04:00
port = serial - > port [ i ] ;
if ( ! port )
continue ;
2006-06-22 02:00:45 +04:00
port_free ( port ) ;
2005-04-17 02:20:36 +04:00
}
}
usb_put_dev ( serial - > dev ) ;
/* free up any memory that we allocated */
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 )
{
2007-07-24 17:13:42 +04:00
mutex_lock ( & table_lock ) ;
2006-04-25 09:46:17 +04:00
kref_put ( & serial - > kref , destroy_serial ) ;
2007-07-24 17:13:42 +04:00
mutex_unlock ( & table_lock ) ;
2006-04-25 09:46:17 +04:00
}
2005-04-17 02:20:36 +04:00
/*****************************************************************************
* Driver tty interface functions
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2008-07-22 14:12:24 +04:00
static int serial_open ( struct tty_struct * tty , struct file * filp )
2005-04-17 02:20:36 +04:00
{
struct usb_serial * serial ;
struct usb_serial_port * port ;
unsigned int portNumber ;
2009-04-14 19:31:02 +04:00
int retval = 0 ;
2008-07-22 14:12:24 +04:00
2008-03-04 03:08:34 +03:00
dbg ( " %s " , __func__ ) ;
2005-04-17 02:20:36 +04:00
/* get the serial object associated with this tty pointer */
serial = usb_serial_get_by_index ( tty - > index ) ;
if ( ! serial ) {
tty - > driver_data = NULL ;
return - ENODEV ;
}
2009-04-14 19:31:02 +04:00
mutex_lock ( & serial - > disc_mutex ) ;
2005-04-17 02:20:36 +04:00
portNumber = tty - > index - serial - > minor ;
port = serial - > port [ portNumber ] ;
2009-04-14 19:31:02 +04:00
if ( ! port | | serial - > disconnected )
2009-02-11 18:06:53 +03:00
retval = - ENODEV ;
2009-04-14 19:31:02 +04:00
else
get_device ( & port - > dev ) ;
/*
* Note : Our locking order requirement does not allow port - > mutex
* to be acquired while serial - > disc_mutex is held .
*/
mutex_unlock ( & serial - > disc_mutex ) ;
if ( retval )
goto bailout_serial_put ;
2009-02-11 18:06:53 +03:00
2006-05-12 05:34:24 +04:00
if ( mutex_lock_interruptible ( & port - > mutex ) ) {
retval = - ERESTARTSYS ;
2009-04-14 19:31:02 +04:00
goto bailout_port_put ;
2006-05-12 05:34:24 +04:00
}
2008-07-22 14:12:24 +04:00
2008-07-22 14:09:07 +04:00
+ + port - > port . count ;
2005-04-17 02:20:36 +04:00
2006-04-14 00:28:17 +04:00
/* set up our port structure making the tty driver
* remember our port object , and us it */
tty - > driver_data = port ;
2008-10-13 13:39:46 +04:00
tty_port_tty_set ( & port - > port , tty ) ;
2005-04-17 02:20:36 +04:00
2008-07-22 14:09:07 +04:00
if ( port - > port . count = = 1 ) {
2005-04-17 02:20:36 +04:00
/* lock this module before we call it
* this may fail , which means we must bail out ,
* safe because we are called with BKL held */
2005-06-21 08:15:16 +04:00
if ( ! try_module_get ( serial - > type - > driver . owner ) ) {
2005-04-17 02:20:36 +04:00
retval = - ENODEV ;
2006-05-12 05:34:24 +04:00
goto bailout_mutex_unlock ;
2005-04-17 02:20:36 +04:00
}
2009-04-14 19:31:02 +04:00
mutex_lock ( & serial - > disc_mutex ) ;
if ( serial - > disconnected )
retval = - ENODEV ;
else
retval = usb_autopm_get_interface ( serial - > interface ) ;
2007-11-14 04:10:09 +03:00
if ( retval )
goto bailout_module_put ;
2009-04-14 19:31:02 +04:00
2008-07-22 14:12:24 +04:00
/* only call the device specific open if this
2005-04-17 02:20:36 +04:00
* is the first time the port is opened */
2008-07-22 14:09:07 +04:00
retval = serial - > type - > open ( tty , port , filp ) ;
2005-04-17 02:20:36 +04:00
if ( retval )
2007-11-14 04:10:09 +03:00
goto bailout_interface_put ;
2009-04-14 19:31:02 +04:00
mutex_unlock ( & serial - > disc_mutex ) ;
2005-04-17 02:20:36 +04:00
}
2006-03-24 23:12:31 +03:00
mutex_unlock ( & port - > mutex ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
2007-11-14 04:10:09 +03:00
bailout_interface_put :
usb_autopm_put_interface ( serial - > interface ) ;
2005-04-17 02:20:36 +04:00
bailout_module_put :
2009-04-14 19:31:02 +04:00
mutex_unlock ( & serial - > disc_mutex ) ;
2005-06-21 08:15:16 +04:00
module_put ( serial - > type - > driver . owner ) ;
2006-05-12 05:34:24 +04:00
bailout_mutex_unlock :
2008-07-22 14:09:07 +04:00
port - > port . count = 0 ;
2006-06-14 17:52:05 +04:00
tty - > driver_data = NULL ;
2008-10-13 13:39:46 +04:00
tty_port_tty_set ( & port - > port , NULL ) ;
2006-03-24 23:12:31 +03:00
mutex_unlock ( & port - > mutex ) ;
2009-04-14 19:31:02 +04:00
bailout_port_put :
put_device ( & port - > dev ) ;
bailout_serial_put :
2006-04-25 09:46:17 +04:00
usb_serial_put ( serial ) ;
2005-04-17 02:20:36 +04:00
return retval ;
}
2008-07-22 14:12:24 +04:00
static void serial_close ( struct tty_struct * tty , struct file * filp )
2005-04-17 02:20:36 +04:00
{
2005-07-04 21:32:51 +04:00
struct usb_serial_port * port = tty - > driver_data ;
2009-04-14 19:31:02 +04:00
struct usb_serial * serial ;
struct module * owner ;
int count ;
2005-04-17 02:20:36 +04:00
if ( ! port )
return ;
2008-03-04 03:08:34 +03:00
dbg ( " %s - port %d " , __func__ , port - > number ) ;
2005-04-17 02:20:36 +04:00
2006-03-24 23:12:31 +03:00
mutex_lock ( & port - > mutex ) ;
2009-04-14 19:31:02 +04:00
serial = port - > serial ;
owner = serial - > type - > driver . owner ;
2005-11-29 00:16:07 +03:00
2008-07-22 14:09:07 +04:00
if ( port - > port . count = = 0 ) {
2006-03-24 23:12:31 +03:00
mutex_unlock ( & port - > mutex ) ;
2006-03-07 00:25:52 +03:00
return ;
}
2005-04-17 02:20:36 +04:00
2009-01-02 16:44:04 +03:00
if ( port - > port . count = = 1 )
2008-07-22 14:12:24 +04:00
/* only call the device specific close if this
2009-01-02 16:44:04 +03:00
* port is being closed by the last owner . Ensure we do
* this before we drop the port count . The call is protected
* by the port mutex
*/
2009-04-14 19:31:02 +04:00
serial - > type - > close ( tty , port , filp ) ;
2005-04-17 02:20:36 +04:00
2009-01-02 16:44:04 +03:00
if ( port - > port . count = = ( port - > console ? 2 : 1 ) ) {
2008-10-13 13:39:46 +04:00
struct tty_struct * tty = tty_port_tty_get ( & port - > port ) ;
if ( tty ) {
2009-01-02 16:44:04 +03:00
/* We must do this before we drop the port count to
zero . */
2008-10-13 13:39:46 +04:00
if ( tty - > driver_data )
tty - > driver_data = NULL ;
tty_port_tty_set ( & port - > port , NULL ) ;
2008-10-30 18:54:12 +03:00
tty_kref_put ( tty ) ;
2005-04-17 02:20:36 +04:00
}
USB: usb_serial: clean tty reference in the last close
When a usb serial adapter is used as console, the usb serial console
driver bumps the open_count on the port struct used but doesn't attach
a real tty to it (only a fake one temporaly). If this port is opened later
using the regular character device interface, the open method won't
initialize the port, which is the expected, and will receive a brand new
tty struct created by tty layer, which will be stored in port->tty.
When the last close is issued, open_count won't be 0 because of the
console usage and the port->tty will still contain the old tty value. This
is the last ttyUSB<n> close so the allocated tty will be freed by the
tty layer. The usb_serial and usb_serial_port are still in use by the
console, so port_free() won't be called (serial_close() ->
usb_serial_put() -> destroy_serial() -> port_free()), so the scheduled
work (port->work, usb_serial_port_work()) will still run. And
usb_serial_port_work() does:
(...)
tty = port->tty;
if (!tty)
return;
tty_wakeup(tty);
which causes (manually copied):
Faulting instruction address: 0x6b6b6b68
Oops: Kernel access of bad area, sig: 11 [#1]
PREEMPT PowerMac
Modules linked in: binfmt_misc ipv6 nfs lockd nfs_acl sunrpc dm_snapshot dm_mirror dm_mod hfsplus uinput ams input_polldev genrtc cpufreq_powersave i2c_powermac therm_adt746x snd_aoa_codec_tas snd_aoa_fabric_layout snd_aoa joydev snd_aoa_i2sbus snd_pcm_oss snd_mixer_oss snd_pcm snd_timer snd_page_alloc pmac_zilog serial_core evdev ide_cd cdrom snd appletouch soundcore snd_aoa_soundbus bcm43xx firmware_class usbhid ieee80211softmac ff_memless firewire_ohci firewire_core ieee80211 ieee80211_crypt crc_itu_t sungem sungem_phy uninorth_agp agpart ssb
NIP: 6b6b6b68 LR: c01b2108 CTR: 6b6b6b6b
REGS: c106de80 TRAP: 0400 Not tainted (2.6.24-rc2)
MSR: 40009032 <EE,ME,IR,DR> CR: 82004024 XER: 00000000
TASK = c106b4c0[5] 'events/0' THREAD: c106c000
GPR00: 6b6b6b6b c106df30 c106b4c0 c2d613a0 00009032 00000001 00001a00 00000001
GPR08: 00000008 00000000 00000000 c106c000 42004028 00000000 016ffbe0 0171a724
GPR16: 016ffcf4 00240e24 00240e70 016fee68 016ff9a4 c03046c4 c0327f50 c03046fc
GPR24: c106b6b9 c106b4c0 c101d610 c106c000 c02160fc c1eac1dc c2d613ac c2d613a0
NIP [6b6b6b68] 0x6b6b6b68
LR [c01b2108] tty_wakeup+0x6c/0x9c
Call Trace:
[c106df30] [c01b20e8] tty_wakeup+0x4c/0x9c (unreliable)
[c106df40] [c0216138] usb_serial_port_work+0x3c/0x78
[c106df50] [c00432e8] run_workqueue+0xc4/0x15c
[c106df90] [c0043798] worker_thread+0xa0/0x124
[c106dfd0] [c0048224] kthread+0x48/0x84
[c106dff0] [c00129bc] kernel_thread+0x44/0x60
Instruction dump:
XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX
XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX
Slab corruption: size-2048 start=c2d613a0, len=2048
Redzone: 0x9f911029d74e35b/0x9f911029d74e35b.
Last user: [<c01b16d8>](release_one_tty+0xbc/0xf4)
050: 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b
Prev obj: start=c2d60b88, len=2048
Redzone: 0x9f911029d74e35b/0x9f911029d74e35b.
Last user: [<c00f30ec>](show_stat+0x410/0x428)
000: 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b
010: 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b
This patch avoids this, clearing port->tty considering if the port is
used as serial console or not
Signed-off-by: Aristeu Rozanski <arozansk@redhat.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2007-11-12 23:15:02 +03:00
}
2005-04-17 02:20:36 +04:00
2009-01-02 16:44:04 +03:00
- - port - > port . count ;
2009-04-14 19:31:02 +04:00
count = port - > port . count ;
2006-03-24 23:12:31 +03:00
mutex_unlock ( & port - > mutex ) ;
2009-04-14 19:31:02 +04:00
put_device ( & port - > dev ) ;
/* Mustn't dereference port any more */
if ( count = = 0 ) {
mutex_lock ( & serial - > disc_mutex ) ;
if ( ! serial - > disconnected )
usb_autopm_put_interface ( serial - > interface ) ;
mutex_unlock ( & serial - > disc_mutex ) ;
}
usb_serial_put ( serial ) ;
/* Mustn't dereference serial any more */
if ( count = = 0 )
module_put ( owner ) ;
2005-04-17 02:20:36 +04:00
}
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 ;
2008-03-04 03:08:34 +03:00
dbg ( " %s - port %d, %d byte(s) " , __func__ , port - > number , count ) ;
2005-04-17 02:20:36 +04:00
2008-07-22 14:09:07 +04:00
/* count is managed under the mutex lock for the tty so cannot
2008-07-22 14:12:24 +04:00
drop to zero until after the last close completes */
2008-07-22 14:09:07 +04:00
WARN_ON ( ! port - > port . count ) ;
2005-04-17 02:20:36 +04:00
/* pass on to the driver specific version of this function */
2008-07-22 14:09:07 +04:00
retval = port - > serial - > type - > write ( tty , port , buf , count ) ;
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 ;
2008-03-04 03:08:34 +03:00
dbg ( " %s - port %d " , __func__ , port - > number ) ;
2008-07-22 14:09:07 +04:00
WARN_ON ( ! port - > port . count ) ;
2005-04-17 02:20:36 +04:00
/* pass on to the driver specific version of this function */
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 ;
2008-03-04 03:08:34 +03:00
dbg ( " %s = port %d " , __func__ , port - > number ) ;
2005-04-17 02:20:36 +04:00
2008-07-22 14:09:07 +04:00
WARN_ON ( ! port - > port . count ) ;
2009-01-02 16:47:06 +03:00
/* if the device was unplugged then any remaining characters
fell out of the connector ; ) */
if ( port - > serial - > disconnected )
return 0 ;
2005-04-17 02:20:36 +04:00
/* pass on to the driver specific version of this function */
2008-07-22 14:09:07 +04:00
return port - > serial - > type - > chars_in_buffer ( tty ) ;
2005-04-17 02:20:36 +04:00
}
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 ;
2008-03-04 03:08:34 +03:00
dbg ( " %s - port %d " , __func__ , port - > number ) ;
2005-04-17 02:20:36 +04:00
2008-07-22 14:09:07 +04:00
WARN_ON ( ! port - > port . count ) ;
2005-04-17 02:20:36 +04:00
/* pass on to the driver specific version of this function */
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 ;
2008-03-04 03:08:34 +03:00
dbg ( " %s - port %d " , __func__ , port - > number ) ;
2005-04-17 02:20:36 +04:00
2008-07-22 14:09:07 +04:00
WARN_ON ( ! port - > port . count ) ;
2005-04-17 02:20:36 +04:00
/* pass on to the driver specific version of this function */
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
}
2008-07-22 14:12:24 +04:00
static int serial_ioctl ( struct tty_struct * tty , struct file * file ,
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 ;
2005-04-17 02:20:36 +04:00
int retval = - ENODEV ;
2008-03-04 03:08:34 +03:00
dbg ( " %s - port %d, cmd 0x%.4x " , __func__ , port - > number , cmd ) ;
2005-04-17 02:20:36 +04:00
2008-07-22 14:09:07 +04:00
WARN_ON ( ! port - > port . count ) ;
2005-04-17 02:20:36 +04:00
2008-07-22 14:12:24 +04:00
/* pass on to the driver specific version of this function
if it is available */
2008-04-30 11:54:13 +04:00
if ( port - > serial - > type - > ioctl ) {
2008-07-22 14:09:07 +04:00
retval = port - > serial - > type - > ioctl ( tty , file , cmd , arg ) ;
2008-07-22 14:12:24 +04:00
} else
2005-04-17 02:20:36 +04:00
retval = - ENOIOCTLCMD ;
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 ;
2008-03-04 03:08:34 +03:00
dbg ( " %s - port %d " , __func__ , port - > number ) ;
2005-04-17 02:20:36 +04:00
2008-07-22 14:09:07 +04:00
WARN_ON ( ! port - > port . count ) ;
2008-07-22 14:12:24 +04:00
/* pass on to the driver specific version of this function
if it is available */
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
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
2008-03-04 03:08:34 +03:00
dbg ( " %s - port %d " , __func__ , port - > number ) ;
2005-04-17 02:20:36 +04:00
2008-07-22 14:09:07 +04:00
WARN_ON ( ! port - > port . count ) ;
2008-07-22 14:12:24 +04:00
/* pass on to the driver specific version of this function
if it is available */
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 ) ;
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 ;
int i ;
char tmp [ 40 ] ;
2008-03-04 03:08:34 +03:00
dbg ( " %s " , __func__ ) ;
2009-04-01 02:19:21 +04:00
seq_puts ( m , " usbserinfo:1.0 driver:2.0 \n " ) ;
for ( i = 0 ; i < SERIAL_TTY_MINORS ; + + i ) {
2005-04-17 02:20:36 +04:00
serial = usb_serial_get_by_index ( i ) ;
if ( serial = = NULL )
continue ;
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 ) ;
seq_printf ( m , " port:%d " , i - serial - > minor + 1 ) ;
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 ) ;
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
}
2009-04-01 02:19:21 +04:00
static int serial_proc_open ( struct inode * inode , struct file * file )
{
return single_open ( file , serial_proc_show , NULL ) ;
}
static const struct file_operations serial_proc_fops = {
. owner = THIS_MODULE ,
. open = serial_proc_open ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = single_release ,
} ;
2008-07-22 14:12:24 +04:00
static int serial_tiocmget ( struct tty_struct * tty , struct file * file )
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
2008-03-04 03:08:34 +03:00
dbg ( " %s - port %d " , __func__ , port - > number ) ;
2005-04-17 02:20:36 +04:00
2008-07-22 14:09:07 +04:00
WARN_ON ( ! port - > port . count ) ;
2005-04-17 02:20:36 +04:00
if ( port - > serial - > type - > tiocmget )
2008-07-22 14:09:07 +04:00
return port - > serial - > type - > tiocmget ( tty , file ) ;
2005-04-17 02:20:36 +04:00
return - EINVAL ;
}
2008-07-22 14:12:24 +04:00
static int serial_tiocmset ( struct tty_struct * tty , struct file * file ,
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
2008-03-04 03:08:34 +03:00
dbg ( " %s - port %d " , __func__ , port - > number ) ;
2005-04-17 02:20:36 +04:00
2008-07-22 14:09:07 +04:00
WARN_ON ( ! port - > port . count ) ;
2005-04-17 02:20:36 +04:00
if ( port - > serial - > type - > tiocmset )
2008-07-22 14:09:07 +04:00
return port - > serial - > type - > tiocmset ( tty , file , set , clear ) ;
2005-04-17 02:20:36 +04:00
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
struct tty_struct * tty ;
2008-03-04 03:08:34 +03:00
dbg ( " %s - port %d " , __func__ , port - > number ) ;
2008-07-22 14:12:24 +04:00
2008-10-13 13:39:46 +04:00
tty = tty_port_tty_get ( & port - > port ) ;
2005-04-17 02:20:36 +04:00
if ( ! tty )
return ;
tty_wakeup ( tty ) ;
2008-10-13 13:39:46 +04:00
tty_kref_put ( tty ) ;
2005-04-17 02:20:36 +04:00
}
static void port_release ( struct device * dev )
{
struct usb_serial_port * port = to_usb_serial_port ( dev ) ;
2008-05-02 08:02:41 +04:00
dbg ( " %s - %s " , __func__ , dev_name ( dev ) ) ;
2006-06-22 02:00:45 +04:00
port_free ( port ) ;
}
2007-01-13 09:29:26 +03:00
static void kill_traffic ( struct usb_serial_port * port )
2006-06-22 02:00:45 +04:00
{
2005-04-17 02:20:36 +04:00
usb_kill_urb ( port - > read_urb ) ;
usb_kill_urb ( port - > write_urb ) ;
2007-08-17 16:01:38 +04:00
/*
* This is tricky .
* Some drivers submit the read_urb in the
* handler for the write_urb or vice versa
* this order determines the order in which
* usb_kill_urb ( ) must be used to reliably
* kill the URBs . As it is unknown here ,
* both orders must be used in turn .
* The call below is not redundant .
*/
usb_kill_urb ( port - > read_urb ) ;
2005-04-17 02:20:36 +04:00
usb_kill_urb ( port - > interrupt_in_urb ) ;
usb_kill_urb ( port - > interrupt_out_urb ) ;
2007-01-13 09:29:26 +03:00
}
static void port_free ( struct usb_serial_port * port )
{
2009-04-14 19:31:02 +04:00
/*
* Stop all the traffic before cancelling the work , so that
* nobody will restart it by calling usb_serial_port_softint .
*/
2007-01-13 09:29:26 +03:00
kill_traffic ( port ) ;
2009-04-14 19:31:02 +04:00
cancel_work_sync ( & port - > work ) ;
2007-01-13 09:29:26 +03:00
usb_free_urb ( port - > read_urb ) ;
usb_free_urb ( port - > write_urb ) ;
usb_free_urb ( port - > interrupt_in_urb ) ;
2005-04-17 02:20:36 +04:00
usb_free_urb ( port - > interrupt_out_urb ) ;
kfree ( port - > bulk_in_buffer ) ;
kfree ( port - > bulk_out_buffer ) ;
kfree ( port - > interrupt_in_buffer ) ;
kfree ( port - > interrupt_out_buffer ) ;
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 ) ;
2005-04-17 02:20:36 +04:00
if ( ! serial ) {
2008-03-04 03:08:34 +03:00
dev_err ( & dev - > dev , " %s - out of memory \n " , __func__ ) ;
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 ;
2005-04-17 02:20:36 +04:00
serial - > interface = interface ;
kref_init ( & serial - > kref ) ;
2008-01-16 19:18:52 +03:00
mutex_init ( & serial - > disc_mutex ) ;
2008-07-29 20:01:04 +04:00
serial - > minor = SERIAL_TTY_NO_MINOR ;
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 ) {
dbg ( " static descriptor matches " ) ;
goto exit ;
}
id = match_dynamic_id ( intf , drv ) ;
if ( id )
dbg ( " dynamic descriptor matches " ) ;
exit :
return id ;
}
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
{
const struct usb_device_id * id ;
2007-10-11 00:24:06 +04:00
struct usb_serial_driver * drv ;
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 ) {
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 ;
}
int usb_serial_probe ( struct usb_interface * interface ,
const struct usb_device_id * id )
{
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 ;
struct usb_host_interface * iface_desc ;
struct usb_endpoint_descriptor * endpoint ;
struct usb_endpoint_descriptor * interrupt_in_endpoint [ MAX_NUM_PORTS ] ;
struct usb_endpoint_descriptor * interrupt_out_endpoint [ MAX_NUM_PORTS ] ;
struct usb_endpoint_descriptor * bulk_in_endpoint [ MAX_NUM_PORTS ] ;
struct usb_endpoint_descriptor * bulk_out_endpoint [ MAX_NUM_PORTS ] ;
2005-06-21 08:15:16 +04:00
struct usb_serial_driver * type = NULL ;
2005-04-17 02:20:36 +04:00
int retval ;
2008-06-18 17:56:00 +04:00
unsigned int minor ;
2005-04-17 02:20:36 +04:00
int buffer_size ;
int i ;
int num_interrupt_in = 0 ;
int num_interrupt_out = 0 ;
int num_bulk_in = 0 ;
int num_bulk_out = 0 ;
int num_ports = 0 ;
int max_endpoints ;
2007-01-13 09:31:27 +03:00
lock_kernel ( ) ; /* guard against unloading a serial driver module */
2005-04-17 02:20:36 +04:00
type = search_serial_device ( interface ) ;
if ( ! type ) {
2007-01-13 09:31:27 +03:00
unlock_kernel ( ) ;
2005-04-17 02:20:36 +04:00
dbg ( " none matched " ) ;
return - ENODEV ;
}
2008-07-22 14:12:24 +04:00
serial = create_serial ( dev , interface , type ) ;
2005-04-17 02:20:36 +04:00
if ( ! serial ) {
2007-01-13 09:31:27 +03:00
unlock_kernel ( ) ;
2008-03-04 03:08:34 +03:00
dev_err ( & interface - > dev , " %s - out of memory \n " , __func__ ) ;
2005-04-17 02:20:36 +04:00
return - ENOMEM ;
}
/* if this device type has a probe function, call it */
if ( type - > probe ) {
const struct usb_device_id * id ;
2005-06-21 08:15:16 +04:00
if ( ! try_module_get ( type - > driver . owner ) ) {
2007-01-13 09:31:27 +03:00
unlock_kernel ( ) ;
2008-07-22 14:12:24 +04:00
dev_err ( & interface - > dev ,
" module get failed, exiting \n " ) ;
kfree ( serial ) ;
2005-04-17 02:20:36 +04:00
return - EIO ;
}
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 ) ;
2005-06-21 08:15:16 +04:00
module_put ( type - > driver . owner ) ;
2005-04-17 02:20:36 +04:00
if ( retval ) {
2007-01-13 09:31:27 +03:00
unlock_kernel ( ) ;
2008-07-22 14:12:24 +04:00
dbg ( " sub driver rejected device " ) ;
kfree ( serial ) ;
2005-04-17 02:20:36 +04:00
return retval ;
}
}
/* descriptor matches, let's find the endpoints needed */
/* check out the endpoints */
iface_desc = interface - > cur_altsetting ;
for ( i = 0 ; i < iface_desc - > desc . bNumEndpoints ; + + i ) {
endpoint = & iface_desc - > endpoint [ i ] . desc ;
2006-09-27 22:58:53 +04:00
if ( usb_endpoint_is_bulk_in ( endpoint ) ) {
2005-04-17 02:20:36 +04:00
/* we found a bulk in endpoint */
dbg ( " found bulk in on endpoint %d " , i ) ;
bulk_in_endpoint [ num_bulk_in ] = endpoint ;
+ + num_bulk_in ;
}
2006-09-27 22:58:53 +04:00
if ( usb_endpoint_is_bulk_out ( endpoint ) ) {
2005-04-17 02:20:36 +04:00
/* we found a bulk out endpoint */
dbg ( " found bulk out on endpoint %d " , i ) ;
bulk_out_endpoint [ num_bulk_out ] = endpoint ;
+ + num_bulk_out ;
}
2006-09-27 22:58:53 +04:00
if ( usb_endpoint_is_int_in ( endpoint ) ) {
2005-04-17 02:20:36 +04:00
/* we found a interrupt in endpoint */
dbg ( " found interrupt in on endpoint %d " , i ) ;
interrupt_in_endpoint [ num_interrupt_in ] = endpoint ;
+ + num_interrupt_in ;
}
2006-09-27 22:58:53 +04:00
if ( usb_endpoint_is_int_out ( endpoint ) ) {
2005-04-17 02:20:36 +04:00
/* we found an interrupt out endpoint */
dbg ( " found interrupt out on endpoint %d " , i ) ;
interrupt_out_endpoint [ num_interrupt_out ] = endpoint ;
+ + num_interrupt_out ;
}
}
# if defined(CONFIG_USB_SERIAL_PL2303) || defined(CONFIG_USB_SERIAL_PL2303_MODULE)
2008-07-22 14:12:24 +04:00
/* BEGIN HORRIBLE HACK FOR PL2303 */
2005-04-17 02:20:36 +04:00
/* this is needed due to the looney way its endpoints are set up */
if ( ( ( le16_to_cpu ( dev - > descriptor . idVendor ) = = PL2303_VENDOR_ID ) & &
( le16_to_cpu ( dev - > descriptor . idProduct ) = = PL2303_PRODUCT_ID ) ) | |
( ( le16_to_cpu ( dev - > descriptor . idVendor ) = = ATEN_VENDOR_ID ) & &
2006-09-16 18:17:34 +04:00
( le16_to_cpu ( dev - > descriptor . idProduct ) = = ATEN_PRODUCT_ID ) ) | |
( ( le16_to_cpu ( dev - > descriptor . idVendor ) = = ALCOR_VENDOR_ID ) & &
2008-09-14 03:58:55 +04:00
( le16_to_cpu ( dev - > descriptor . idProduct ) = = ALCOR_PRODUCT_ID ) ) | |
( ( le16_to_cpu ( dev - > descriptor . idVendor ) = = SIEMENS_VENDOR_ID ) & &
( le16_to_cpu ( dev - > descriptor . idProduct ) = = SIEMENS_PRODUCT_ID_EF81 ) ) ) {
2005-04-17 02:20:36 +04:00
if ( interface ! = dev - > actconfig - > interface [ 0 ] ) {
/* check out the endpoints of the other interface*/
iface_desc = dev - > actconfig - > interface [ 0 ] - > cur_altsetting ;
for ( i = 0 ; i < iface_desc - > desc . bNumEndpoints ; + + i ) {
endpoint = & iface_desc - > endpoint [ i ] . desc ;
2006-09-27 22:58:53 +04:00
if ( usb_endpoint_is_int_in ( endpoint ) ) {
2005-04-17 02:20:36 +04:00
/* we found a interrupt in endpoint */
dbg ( " found interrupt in for Prolific device on separate interface " ) ;
interrupt_in_endpoint [ num_interrupt_in ] = endpoint ;
+ + num_interrupt_in ;
}
}
}
/* Now make sure the PL-2303 is configured correctly.
* If not , give up now and hope this hack will work
* properly during a later invocation of usb_serial_probe
*/
if ( num_bulk_in = = 0 | | num_bulk_out = = 0 ) {
2007-01-13 09:31:27 +03:00
unlock_kernel ( ) ;
2005-04-17 02:20:36 +04:00
dev_info ( & interface - > dev , " PL-2303 hack: descriptors matched but endpoints did not \n " ) ;
2008-07-22 14:12:24 +04:00
kfree ( serial ) ;
2005-04-17 02:20:36 +04:00
return - ENODEV ;
}
}
/* END HORRIBLE HACK FOR PL2303 */
# endif
# ifdef CONFIG_USB_SERIAL_GENERIC
if ( type = = & usb_serial_generic_device ) {
num_ports = num_bulk_out ;
if ( num_ports = = 0 ) {
2007-01-13 09:31:27 +03:00
unlock_kernel ( ) ;
2008-07-22 14:12:24 +04:00
dev_err ( & interface - > dev ,
" Generic device with no bulk out, not allowed. \n " ) ;
kfree ( serial ) ;
2005-04-17 02:20:36 +04:00
return - EIO ;
}
}
# endif
if ( ! num_ports ) {
/* if this device type has a calc_num_ports function, call it */
if ( type - > calc_num_ports ) {
2005-06-21 08:15:16 +04:00
if ( ! try_module_get ( type - > driver . owner ) ) {
2007-01-13 09:31:27 +03:00
unlock_kernel ( ) ;
2008-07-22 14:12:24 +04:00
dev_err ( & interface - > dev ,
" module get failed, exiting \n " ) ;
kfree ( serial ) ;
2005-04-17 02:20:36 +04:00
return - EIO ;
}
2008-07-22 14:12:24 +04:00
num_ports = type - > calc_num_ports ( serial ) ;
2005-06-21 08:15:16 +04:00
module_put ( type - > driver . owner ) ;
2005-04-17 02:20:36 +04:00
}
if ( ! num_ports )
num_ports = type - > num_ports ;
}
serial - > num_ports = num_ports ;
serial - > num_bulk_in = num_bulk_in ;
serial - > num_bulk_out = num_bulk_out ;
serial - > num_interrupt_in = num_interrupt_in ;
serial - > num_interrupt_out = num_interrupt_out ;
2007-10-11 00:24:06 +04:00
/* found all that we need */
dev_info ( & interface - > dev , " %s converter detected \n " ,
type - > description ) ;
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 */
2005-04-17 02:20:36 +04:00
max_endpoints = max ( num_bulk_in , num_bulk_out ) ;
max_endpoints = max ( max_endpoints , num_interrupt_in ) ;
max_endpoints = max ( max_endpoints , num_interrupt_out ) ;
max_endpoints = max ( max_endpoints , ( int ) serial - > num_ports ) ;
serial - > num_port_pointers = max_endpoints ;
2007-01-13 09:31:27 +03:00
unlock_kernel ( ) ;
2008-07-22 14:12:24 +04:00
dbg ( " %s - setting up %d port structures for this device " ,
__func__ , 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 ) ;
2005-04-17 02:20:36 +04:00
if ( ! port )
goto probe_error ;
2008-10-13 13:39:46 +04:00
tty_port_init ( & port - > port ) ;
2005-04-17 02:20:36 +04:00
port - > serial = serial ;
2005-04-23 23:49:16 +04:00
spin_lock_init ( & port - > lock ) ;
2006-03-24 23:12:31 +03:00
mutex_init ( & port - > mutex ) ;
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 ;
}
/* set up the endpoint information */
for ( i = 0 ; i < num_bulk_in ; + + i ) {
endpoint = bulk_in_endpoint [ i ] ;
port = serial - > port [ i ] ;
2008-07-22 14:12:24 +04:00
port - > read_urb = usb_alloc_urb ( 0 , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! port - > read_urb ) {
dev_err ( & interface - > dev , " No free urbs available \n " ) ;
goto probe_error ;
}
buffer_size = le16_to_cpu ( endpoint - > wMaxPacketSize ) ;
port - > bulk_in_size = buffer_size ;
port - > bulk_in_endpointAddress = endpoint - > bEndpointAddress ;
2008-07-22 14:12:24 +04:00
port - > bulk_in_buffer = kmalloc ( buffer_size , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! port - > bulk_in_buffer ) {
2008-07-22 14:12:24 +04:00
dev_err ( & interface - > dev ,
" Couldn't allocate bulk_in_buffer \n " ) ;
2005-04-17 02:20:36 +04:00
goto probe_error ;
}
2008-07-22 14:12:24 +04:00
usb_fill_bulk_urb ( port - > read_urb , dev ,
usb_rcvbulkpipe ( dev ,
endpoint - > bEndpointAddress ) ,
port - > bulk_in_buffer , buffer_size ,
serial - > type - > read_bulk_callback , port ) ;
2005-04-17 02:20:36 +04:00
}
for ( i = 0 ; i < num_bulk_out ; + + i ) {
endpoint = bulk_out_endpoint [ i ] ;
port = serial - > port [ i ] ;
port - > write_urb = usb_alloc_urb ( 0 , GFP_KERNEL ) ;
if ( ! port - > write_urb ) {
dev_err ( & interface - > dev , " No free urbs available \n " ) ;
goto probe_error ;
}
buffer_size = le16_to_cpu ( endpoint - > wMaxPacketSize ) ;
port - > bulk_out_size = buffer_size ;
port - > bulk_out_endpointAddress = endpoint - > bEndpointAddress ;
2008-07-22 14:12:24 +04:00
port - > bulk_out_buffer = kmalloc ( buffer_size , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! port - > bulk_out_buffer ) {
2008-07-22 14:12:24 +04:00
dev_err ( & interface - > dev ,
" Couldn't allocate bulk_out_buffer \n " ) ;
2005-04-17 02:20:36 +04:00
goto probe_error ;
}
2008-07-22 14:12:24 +04:00
usb_fill_bulk_urb ( port - > write_urb , dev ,
usb_sndbulkpipe ( dev ,
endpoint - > bEndpointAddress ) ,
port - > bulk_out_buffer , buffer_size ,
serial - > type - > write_bulk_callback , port ) ;
2005-04-17 02:20:36 +04:00
}
if ( serial - > type - > read_int_callback ) {
for ( i = 0 ; i < num_interrupt_in ; + + i ) {
endpoint = interrupt_in_endpoint [ i ] ;
port = serial - > port [ i ] ;
port - > interrupt_in_urb = usb_alloc_urb ( 0 , GFP_KERNEL ) ;
if ( ! port - > interrupt_in_urb ) {
2008-07-22 14:12:24 +04:00
dev_err ( & interface - > dev ,
" No free urbs available \n " ) ;
2005-04-17 02:20:36 +04:00
goto probe_error ;
}
buffer_size = le16_to_cpu ( endpoint - > wMaxPacketSize ) ;
2008-07-22 14:12:24 +04:00
port - > interrupt_in_endpointAddress =
endpoint - > bEndpointAddress ;
port - > interrupt_in_buffer = kmalloc ( buffer_size ,
GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! port - > interrupt_in_buffer ) {
2008-07-22 14:12:24 +04:00
dev_err ( & interface - > dev ,
" Couldn't allocate interrupt_in_buffer \n " ) ;
2005-04-17 02:20:36 +04:00
goto probe_error ;
}
2008-07-22 14:12:24 +04:00
usb_fill_int_urb ( port - > interrupt_in_urb , dev ,
usb_rcvintpipe ( dev ,
endpoint - > bEndpointAddress ) ,
port - > interrupt_in_buffer , buffer_size ,
serial - > type - > read_int_callback , port ,
endpoint - > bInterval ) ;
2005-04-17 02:20:36 +04:00
}
} else if ( num_interrupt_in ) {
dbg ( " the device claims to support interrupt in transfers, but read_int_callback is not defined " ) ;
}
2008-07-22 14:12:24 +04:00
2005-04-17 02:20:36 +04:00
if ( serial - > type - > write_int_callback ) {
for ( i = 0 ; i < num_interrupt_out ; + + i ) {
endpoint = interrupt_out_endpoint [ i ] ;
port = serial - > port [ i ] ;
port - > interrupt_out_urb = usb_alloc_urb ( 0 , GFP_KERNEL ) ;
if ( ! port - > interrupt_out_urb ) {
2008-07-22 14:12:24 +04:00
dev_err ( & interface - > dev ,
" No free urbs available \n " ) ;
2005-04-17 02:20:36 +04:00
goto probe_error ;
}
buffer_size = le16_to_cpu ( endpoint - > wMaxPacketSize ) ;
port - > interrupt_out_size = buffer_size ;
2008-07-22 14:12:24 +04:00
port - > interrupt_out_endpointAddress =
endpoint - > bEndpointAddress ;
port - > interrupt_out_buffer = kmalloc ( buffer_size ,
GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! port - > interrupt_out_buffer ) {
2008-07-22 14:12:24 +04:00
dev_err ( & interface - > dev ,
" Couldn't allocate interrupt_out_buffer \n " ) ;
2005-04-17 02:20:36 +04:00
goto probe_error ;
}
2008-07-22 14:12:24 +04:00
usb_fill_int_urb ( port - > interrupt_out_urb , dev ,
usb_sndintpipe ( dev ,
endpoint - > bEndpointAddress ) ,
port - > interrupt_out_buffer , buffer_size ,
serial - > type - > write_int_callback , port ,
endpoint - > bInterval ) ;
2005-04-17 02:20:36 +04:00
}
} else if ( num_interrupt_out ) {
dbg ( " the device claims to support interrupt out transfers, but write_int_callback is not defined " ) ;
}
2008-07-22 14:12:24 +04:00
2005-04-17 02:20:36 +04:00
/* if this device type has an attach function, call it */
if ( type - > attach ) {
2005-06-21 08:15:16 +04:00
if ( ! try_module_get ( type - > driver . owner ) ) {
2008-07-22 14:12:24 +04:00
dev_err ( & interface - > dev ,
" module get failed, exiting \n " ) ;
2005-04-17 02:20:36 +04:00
goto probe_error ;
}
2008-07-22 14:12:24 +04:00
retval = type - > attach ( serial ) ;
2005-06-21 08:15:16 +04:00
module_put ( type - > driver . owner ) ;
2005-04-17 02:20:36 +04:00
if ( retval < 0 )
goto probe_error ;
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 ;
}
}
2008-07-22 14:12:24 +04:00
if ( get_free_serial ( serial , num_ports , & minor ) = = NULL ) {
2007-01-13 09:29:26 +03:00
dev_err ( & interface - > dev , " No more free serial devices \n " ) ;
goto probe_error ;
}
2007-02-26 17:43:00 +03:00
serial - > minor = minor ;
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 ] ;
port - > dev . parent = & interface - > dev ;
port - > dev . driver = NULL ;
port - > dev . bus = & usb_serial_bus_type ;
port - > dev . release = & port_release ;
2008-05-02 08:02:41 +04:00
dev_set_name ( & port - > dev , " ttyUSB%d " , port - > number ) ;
2008-05-02 08:02:41 +04:00
dbg ( " %s - registering %s " , __func__ , dev_name ( & port - > dev ) ) ;
2006-08-28 22:43:25 +04:00
retval = device_register ( & port - > dev ) ;
if ( retval )
dev_err ( & port - > dev , " Error registering port device, "
" continuing \n " ) ;
2005-04-17 02:20:36 +04:00
}
2008-07-22 14:12:24 +04:00
usb_serial_console_init ( debug , minor ) ;
2005-04-17 02:20:36 +04:00
exit :
/* success */
2008-07-22 14:12:24 +04:00
usb_set_intfdata ( interface , serial ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
probe_error :
for ( i = 0 ; i < num_bulk_in ; + + i ) {
port = serial - > port [ i ] ;
if ( ! port )
continue ;
2006-11-08 17:36:51 +03:00
usb_free_urb ( port - > read_urb ) ;
2005-04-17 02:20:36 +04:00
kfree ( port - > bulk_in_buffer ) ;
}
for ( i = 0 ; i < num_bulk_out ; + + i ) {
port = serial - > port [ i ] ;
if ( ! port )
continue ;
2006-11-08 17:36:51 +03:00
usb_free_urb ( port - > write_urb ) ;
2005-04-17 02:20:36 +04:00
kfree ( port - > bulk_out_buffer ) ;
}
for ( i = 0 ; i < num_interrupt_in ; + + i ) {
port = serial - > port [ i ] ;
if ( ! port )
continue ;
2006-11-08 17:36:51 +03:00
usb_free_urb ( port - > interrupt_in_urb ) ;
2005-04-17 02:20:36 +04:00
kfree ( port - > interrupt_in_buffer ) ;
}
for ( i = 0 ; i < num_interrupt_out ; + + i ) {
port = serial - > port [ i ] ;
if ( ! port )
continue ;
2006-11-08 17:36:51 +03:00
usb_free_urb ( port - > interrupt_out_urb ) ;
2005-04-17 02:20:36 +04:00
kfree ( port - > interrupt_out_buffer ) ;
}
/* free up any memory that we allocated */
for ( i = 0 ; i < serial - > num_port_pointers ; + + i )
kfree ( serial - > port [ i ] ) ;
2008-07-22 14:12:24 +04:00
kfree ( serial ) ;
2005-04-17 02:20:36 +04:00
return - EIO ;
}
2008-07-22 14:12:24 +04:00
EXPORT_SYMBOL_GPL ( usb_serial_probe ) ;
2005-04-17 02:20:36 +04:00
void usb_serial_disconnect ( struct usb_interface * interface )
{
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 ;
2006-04-25 09:46:17 +04:00
usb_serial_console_disconnect ( serial ) ;
2008-07-22 14:12:24 +04:00
dbg ( " %s " , __func__ ) ;
2005-04-17 02:20:36 +04:00
2008-01-16 19:18:52 +03:00
mutex_lock ( & serial - > disc_mutex ) ;
2008-07-22 14:12:24 +04:00
usb_set_intfdata ( interface , NULL ) ;
2008-01-16 19:18:52 +03:00
/* must set a flag, to signal subdrivers */
serial - > disconnected = 1 ;
2009-04-14 19:31:02 +04:00
mutex_unlock ( & serial - > disc_mutex ) ;
/* Unfortunately, many of the sub-drivers expect the port structures
* to exist when their shutdown method is called , so we have to go
* through this awkward two - step unregistration procedure .
*/
2008-01-16 19:18:52 +03:00
for ( i = 0 ; i < serial - > num_ports ; + + i ) {
port = serial - > port [ i ] ;
if ( port ) {
2008-10-13 13:39:46 +04:00
struct tty_struct * tty = tty_port_tty_get ( & port - > port ) ;
if ( tty ) {
tty_hangup ( tty ) ;
tty_kref_put ( tty ) ;
}
2008-01-16 19:18:52 +03:00
kill_traffic ( port ) ;
2009-04-14 19:31:02 +04:00
cancel_work_sync ( & port - > work ) ;
device_del ( & port - > dev ) ;
}
}
serial - > type - > shutdown ( serial ) ;
for ( i = 0 ; i < serial - > num_ports ; + + i ) {
port = serial - > port [ i ] ;
if ( port ) {
put_device ( & port - > dev ) ;
serial - > port [ i ] = NULL ;
2005-04-17 02:20:36 +04:00
}
}
2009-04-14 19:31:02 +04:00
2008-01-16 19:18:52 +03:00
/* let the last holder of this object
* cause it to be cleaned up */
usb_serial_put ( serial ) ;
2005-04-17 02:20:36 +04:00
dev_info ( dev , " device disconnected \n " ) ;
}
2008-07-22 14:12:24 +04:00
EXPORT_SYMBOL_GPL ( usb_serial_disconnect ) ;
2005-04-17 02:20:36 +04:00
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 ) ;
struct usb_serial_port * port ;
int i , r = 0 ;
2009-02-05 18:54:25 +03:00
serial - > suspending = 1 ;
2007-07-23 10:58:39 +04:00
for ( i = 0 ; i < serial - > num_ports ; + + i ) {
port = serial - > port [ i ] ;
if ( port )
kill_traffic ( port ) ;
2007-04-27 22:54:57 +04:00
}
if ( serial - > type - > suspend )
2007-07-23 10:58:39 +04:00
r = serial - > type - > suspend ( serial , message ) ;
2007-04-27 22:54:57 +04:00
return r ;
}
EXPORT_SYMBOL ( usb_serial_suspend ) ;
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
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 ) ;
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 ,
. 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 ,
. tiocmget = serial_tiocmget ,
. tiocmset = serial_tiocmset ,
2009-04-01 02:19:21 +04:00
. proc_fops = & serial_proc_fops ,
2005-04-17 02:20:36 +04:00
} ;
struct tty_driver * usb_serial_tty_driver ;
static int __init usb_serial_init ( void )
{
int i ;
int result ;
usb_serial_tty_driver = alloc_tty_driver ( SERIAL_TTY_MINORS ) ;
if ( ! usb_serial_tty_driver )
return - ENOMEM ;
/* Initialize our global data */
2008-07-22 14:12:24 +04:00
for ( i = 0 ; i < SERIAL_TTY_MINORS ; + + i )
2005-04-17 02:20:36 +04:00
serial_table [ i ] = NULL ;
result = bus_register ( & usb_serial_bus_type ) ;
if ( result ) {
2008-08-21 03:56:34 +04:00
printk ( KERN_ERR " usb-serial: %s - registering bus driver "
" failed \n " , __func__ ) ;
2005-04-17 02:20:36 +04:00
goto exit_bus ;
}
usb_serial_tty_driver - > owner = THIS_MODULE ;
usb_serial_tty_driver - > driver_name = " usbserial " ;
usb_serial_tty_driver - > name = " ttyUSB " ;
usb_serial_tty_driver - > major = SERIAL_TTY_MAJOR ;
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 ) {
2008-08-21 03:56:34 +04:00
printk ( KERN_ERR " usb-serial: %s - tty_register_driver failed \n " ,
__func__ ) ;
2005-04-17 02:20:36 +04:00
goto exit_reg_driver ;
}
/* register the USB driver */
result = usb_register ( & usb_serial_driver ) ;
if ( result < 0 ) {
2008-08-21 03:56:34 +04:00
printk ( KERN_ERR " usb-serial: %s - usb_register failed \n " ,
__func__ ) ;
2005-04-17 02:20:36 +04:00
goto exit_tty ;
}
2005-05-26 16:55:55 +04:00
/* register the generic driver, if we should */
result = usb_serial_generic_register ( debug ) ;
if ( result < 0 ) {
2008-08-21 03:56:34 +04:00
printk ( KERN_ERR " usb-serial: %s - registering generic "
" driver failed \n " , __func__ ) ;
2005-05-26 16:55:55 +04:00
goto exit_generic ;
}
2008-08-19 00:21:04 +04:00
printk ( KERN_INFO KBUILD_MODNAME " : " DRIVER_DESC " \n " ) ;
2005-04-17 02:20:36 +04:00
return result ;
2005-05-26 16:55:55 +04:00
exit_generic :
usb_deregister ( & usb_serial_driver ) ;
2005-04-17 02:20:36 +04:00
exit_tty :
tty_unregister_driver ( usb_serial_tty_driver ) ;
exit_reg_driver :
bus_unregister ( & usb_serial_bus_type ) ;
exit_bus :
2008-08-21 03:56:34 +04:00
printk ( KERN_ERR " usb-serial: %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 ( ) ;
usb_deregister ( & usb_serial_driver ) ;
tty_unregister_driver ( usb_serial_tty_driver ) ;
put_tty_driver ( usb_serial_tty_driver ) ;
bus_unregister ( & usb_serial_bus_type ) ;
}
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 ; \
dbg ( " Had to override the " # function \
2008-07-22 14:12:24 +04:00
" usb serial operation with the generic one. " ) ; \
2005-04-17 02:20:36 +04:00
} \
} while ( 0 )
2005-06-21 08:15:16 +04:00
static void fixup_generic ( 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 ) ;
set_to_generic_if_null ( device , read_bulk_callback ) ;
set_to_generic_if_null ( device , write_bulk_callback ) ;
set_to_generic_if_null ( device , shutdown ) ;
}
2008-07-22 14:12:24 +04:00
int usb_serial_register ( struct usb_serial_driver * driver )
2005-04-17 02:20:36 +04:00
{
2008-07-22 14:12:24 +04:00
/* must be called with BKL held */
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
fixup_generic ( driver ) ;
2005-04-17 02:20:36 +04:00
2005-06-21 08:15:16 +04:00
if ( ! driver - > description )
driver - > description = driver - > driver . name ;
2005-04-17 02:20:36 +04:00
/* Add this device to our list of devices */
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 ) {
2008-08-21 03:56:34 +04:00
printk ( KERN_ERR " usb-serial: problem %d when registering "
" driver %s \n " , retval , driver - > description ) ;
2005-06-21 08:15:16 +04:00
list_del ( & driver - > driver_list ) ;
2008-07-22 14:12:24 +04:00
} else
2008-08-19 00:21:04 +04:00
printk ( KERN_INFO " USB Serial support registered for %s \n " ,
2008-07-22 14:12:24 +04:00
driver - > description ) ;
2005-04-17 02:20:36 +04:00
return retval ;
}
2008-07-22 14:12:24 +04:00
EXPORT_SYMBOL_GPL ( usb_serial_register ) ;
2005-04-17 02:20:36 +04:00
2008-07-22 14:12:24 +04:00
void usb_serial_deregister ( struct usb_serial_driver * device )
2005-04-17 02:20:36 +04:00
{
2008-07-22 14:12:24 +04:00
/* must be called with BKL held */
2008-08-19 00:21:04 +04:00
printk ( KERN_INFO " USB Serial deregistering driver %s \n " ,
device - > description ) ;
2005-04-17 02:20:36 +04:00
list_del ( & device - > driver_list ) ;
usb_serial_bus_deregister ( device ) ;
}
EXPORT_SYMBOL_GPL ( usb_serial_deregister ) ;
/* Module information */
2008-07-22 14:12:24 +04:00
MODULE_AUTHOR ( DRIVER_AUTHOR ) ;
MODULE_DESCRIPTION ( DRIVER_DESC ) ;
2005-04-17 02:20:36 +04:00
MODULE_LICENSE ( " GPL " ) ;
module_param ( debug , bool , S_IRUGO | S_IWUSR ) ;
MODULE_PARM_DESC ( debug , " Debug enabled or not " ) ;