2005-04-17 02:20:36 +04:00
/*
* USB Serial Converter Bus specific functions
*
* Copyright ( C ) 2002 Greg Kroah - Hartman ( greg @ kroah . 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 .
*/
# include <linux/kernel.h>
# include <linux/errno.h>
# include <linux/tty.h>
# include <linux/module.h>
# 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
2008-07-22 14:09:48 +04:00
static int usb_serial_device_match ( struct device * dev ,
struct device_driver * drv )
2005-04-17 02:20:36 +04:00
{
2005-06-21 08:15:16 +04:00
struct usb_serial_driver * driver ;
2005-04-17 02:20:36 +04:00
const struct usb_serial_port * port ;
/*
* drivers are already assigned to ports in serial_probe so it ' s
* a simple check here .
*/
port = to_usb_serial_port ( dev ) ;
if ( ! port )
return 0 ;
driver = to_usb_serial_driver ( drv ) ;
if ( driver = = port - > serial - > type )
return 1 ;
return 0 ;
}
2007-08-24 20:22:52 +04:00
static ssize_t show_port_number ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct usb_serial_port * port = to_usb_serial_port ( dev ) ;
return sprintf ( buf , " %d \n " , port - > number - port - > serial - > minor ) ;
}
static DEVICE_ATTR ( port_number , S_IRUGO , show_port_number , NULL ) ;
2008-07-22 14:09:48 +04:00
static int usb_serial_device_probe ( struct device * dev )
2005-04-17 02:20:36 +04:00
{
2005-06-21 08:15:16 +04:00
struct usb_serial_driver * driver ;
2005-04-17 02:20:36 +04:00
struct usb_serial_port * port ;
int retval = 0 ;
int minor ;
port = to_usb_serial_port ( dev ) ;
if ( ! port ) {
retval = - ENODEV ;
goto exit ;
}
driver = port - > serial - > type ;
if ( driver - > port_probe ) {
2005-06-21 08:15:16 +04:00
if ( ! try_module_get ( driver - > driver . owner ) ) {
2005-04-17 02:20:36 +04:00
dev_err ( dev , " module get failed, exiting \n " ) ;
retval = - EIO ;
goto exit ;
}
2008-07-22 14:09:48 +04:00
retval = driver - > port_probe ( port ) ;
2005-06-21 08:15:16 +04:00
module_put ( driver - > driver . owner ) ;
2005-04-17 02:20:36 +04:00
if ( retval )
goto exit ;
}
2007-08-24 20:22:52 +04:00
retval = device_create_file ( dev , & dev_attr_port_number ) ;
if ( retval )
goto exit ;
2005-04-17 02:20:36 +04:00
minor = port - > number ;
2008-07-22 14:09:48 +04:00
tty_register_device ( usb_serial_tty_driver , minor , dev ) ;
dev_info ( & port - > serial - > dev - > dev ,
2005-04-17 02:20:36 +04:00
" %s converter now attached to ttyUSB%d \n " ,
2005-06-21 08:15:16 +04:00
driver - > description , minor ) ;
2005-04-17 02:20:36 +04:00
exit :
return retval ;
}
2008-07-22 14:09:48 +04:00
static int usb_serial_device_remove ( struct device * dev )
2005-04-17 02:20:36 +04:00
{
2005-06-21 08:15:16 +04:00
struct usb_serial_driver * driver ;
2005-04-17 02:20:36 +04:00
struct usb_serial_port * port ;
int retval = 0 ;
int minor ;
port = to_usb_serial_port ( dev ) ;
2008-07-22 14:09:48 +04:00
if ( ! port )
2005-04-17 02:20:36 +04:00
return - ENODEV ;
2007-08-24 20:22:52 +04:00
device_remove_file ( & port - > dev , & dev_attr_port_number ) ;
2005-04-17 02:20:36 +04:00
driver = port - > serial - > type ;
if ( driver - > port_remove ) {
2005-06-21 08:15:16 +04:00
if ( ! try_module_get ( driver - > driver . owner ) ) {
2005-04-17 02:20:36 +04:00
dev_err ( dev , " module get failed, exiting \n " ) ;
retval = - EIO ;
goto exit ;
}
2008-07-22 14:09:48 +04:00
retval = driver - > port_remove ( port ) ;
2005-06-21 08:15:16 +04:00
module_put ( driver - > driver . owner ) ;
2005-04-17 02:20:36 +04:00
}
exit :
minor = port - > number ;
2008-07-22 14:09:48 +04:00
tty_unregister_device ( usb_serial_tty_driver , minor ) ;
2005-04-17 02:20:36 +04:00
dev_info ( dev , " %s converter now disconnected from ttyUSB%d \n " ,
2005-06-21 08:15:16 +04:00
driver - > description , minor ) ;
2005-04-17 02:20:36 +04:00
return retval ;
}
2006-12-17 23:50:23 +03:00
# ifdef CONFIG_HOTPLUG
static ssize_t store_new_id ( struct device_driver * driver ,
const char * buf , size_t count )
{
struct usb_serial_driver * usb_drv = to_usb_serial_driver ( driver ) ;
ssize_t retval = usb_store_new_id ( & usb_drv - > dynids , driver , buf , count ) ;
if ( retval > = 0 & & usb_drv - > usb_driver ! = NULL )
retval = usb_store_new_id ( & usb_drv - > usb_driver - > dynids ,
& usb_drv - > usb_driver - > drvwrap . driver ,
buf , count ) ;
return retval ;
}
static struct driver_attribute drv_attrs [ ] = {
__ATTR ( new_id , S_IWUSR , NULL , store_new_id ) ,
__ATTR_NULL ,
} ;
static void free_dynids ( struct usb_serial_driver * drv )
{
struct usb_dynid * dynid , * n ;
spin_lock ( & drv - > dynids . lock ) ;
list_for_each_entry_safe ( dynid , n , & drv - > dynids . list , node ) {
list_del ( & dynid - > node ) ;
kfree ( dynid ) ;
}
spin_unlock ( & drv - > dynids . lock ) ;
}
# else
static struct driver_attribute drv_attrs [ ] = {
__ATTR_NULL ,
} ;
2007-09-03 00:12:43 +04:00
static inline void free_dynids ( struct usb_serial_driver * drv )
2006-12-17 23:50:23 +03:00
{
}
# endif
2006-01-05 17:43:11 +03:00
struct bus_type usb_serial_bus_type = {
. name = " usb-serial " ,
. match = usb_serial_device_match ,
. probe = usb_serial_device_probe ,
. remove = usb_serial_device_remove ,
2006-12-17 23:50:23 +03:00
. drv_attrs = drv_attrs ,
2006-01-05 17:43:11 +03:00
} ;
2005-06-21 08:15:16 +04:00
int usb_serial_bus_register ( struct usb_serial_driver * driver )
2005-04-17 02:20:36 +04:00
{
int retval ;
2005-06-21 08:15:16 +04:00
driver - > driver . bus = & usb_serial_bus_type ;
2006-12-17 23:50:23 +03:00
spin_lock_init ( & driver - > dynids . lock ) ;
INIT_LIST_HEAD ( & driver - > dynids . list ) ;
2005-06-21 08:15:16 +04:00
retval = driver_register ( & driver - > driver ) ;
2005-04-17 02:20:36 +04:00
return retval ;
}
2005-06-21 08:15:16 +04:00
void usb_serial_bus_deregister ( struct usb_serial_driver * driver )
2005-04-17 02:20:36 +04:00
{
2006-12-17 23:50:23 +03:00
free_dynids ( driver ) ;
2005-06-21 08:15:16 +04:00
driver_unregister ( & driver - > driver ) ;
2005-04-17 02:20:36 +04:00
}