2005-04-16 15:20:36 -07:00
/*
* drivers / usb / core / sysfs . c
*
* ( C ) Copyright 2002 David Brownell
* ( C ) Copyright 2002 , 2004 Greg Kroah - Hartman
* ( C ) Copyright 2002 , 2004 IBM Corp .
*
* All of the sysfs file attributes for usb devices and interfaces .
*
*/
# include <linux/config.h>
# include <linux/kernel.h>
# ifdef CONFIG_USB_DEBUG
# define DEBUG
# else
# undef DEBUG
# endif
# include <linux/usb.h>
# include "usb.h"
2005-06-20 21:15:16 -07:00
/* endpoint stuff */
struct endpoint_attribute {
struct device_attribute dev_attr ;
struct usb_endpoint_descriptor * endpoint ;
struct usb_device * udev ;
} ;
# define to_endpoint_attr(_dev_attr) \
container_of ( _dev_attr , struct endpoint_attribute , dev_attr )
# define usb_ep_attr(field, format_string) \
static ssize_t show_ep_ # # field ( struct device * dev , struct device_attribute * attr , \
char * buf ) \
{ \
struct endpoint_attribute * endpoint_attr = to_endpoint_attr ( attr ) ; \
\
return sprintf ( buf , format_string , endpoint_attr - > endpoint - > field ) ; \
}
usb_ep_attr ( bLength , " %02x \n " )
usb_ep_attr ( bDescriptorType , " %02x \n " )
usb_ep_attr ( bEndpointAddress , " %02x \n " )
usb_ep_attr ( bmAttributes , " %02x \n " )
usb_ep_attr ( bInterval , " %02x \n " )
static ssize_t show_ep_wMaxPacketSize ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct endpoint_attribute * endpoint_attr = to_endpoint_attr ( attr ) ;
return sprintf ( buf , " %04x \n " ,
le16_to_cpu ( endpoint_attr - > endpoint - > wMaxPacketSize ) & 0x07ff ) ;
}
static ssize_t show_ep_type ( struct device * dev , struct device_attribute * attr , char * buf )
{
struct endpoint_attribute * endpoint_attr = to_endpoint_attr ( attr ) ;
char * type = " unknown " ;
switch ( endpoint_attr - > endpoint - > bmAttributes & USB_ENDPOINT_XFERTYPE_MASK ) {
case USB_ENDPOINT_XFER_CONTROL :
type = " Control " ;
break ;
case USB_ENDPOINT_XFER_ISOC :
type = " Isoc " ;
break ;
case USB_ENDPOINT_XFER_BULK :
type = " Bulk " ;
break ;
case USB_ENDPOINT_XFER_INT :
type = " Interrupt " ;
break ;
}
return sprintf ( buf , " %s \n " , type ) ;
}
static ssize_t show_ep_interval ( struct device * dev , struct device_attribute * attr , char * buf )
{
struct endpoint_attribute * endpoint_attr = to_endpoint_attr ( attr ) ;
struct usb_device * udev = endpoint_attr - > udev ;
struct usb_endpoint_descriptor * endpoint = endpoint_attr - > endpoint ;
char unit ;
unsigned interval = 0 ;
unsigned in ;
in = ( endpoint - > bEndpointAddress & USB_DIR_IN ) ;
switch ( endpoint - > bmAttributes & USB_ENDPOINT_XFERTYPE_MASK ) {
case USB_ENDPOINT_XFER_CONTROL :
if ( udev - > speed = = USB_SPEED_HIGH ) /* uframes per NAK */
interval = endpoint - > bInterval ;
break ;
case USB_ENDPOINT_XFER_ISOC :
interval = 1 < < ( endpoint - > bInterval - 1 ) ;
break ;
case USB_ENDPOINT_XFER_BULK :
if ( udev - > speed = = USB_SPEED_HIGH & & ! in ) /* uframes per NAK */
interval = endpoint - > bInterval ;
break ;
case USB_ENDPOINT_XFER_INT :
if ( udev - > speed = = USB_SPEED_HIGH ) {
interval = 1 < < ( endpoint - > bInterval - 1 ) ;
} else
interval = endpoint - > bInterval ;
break ;
}
interval * = ( udev - > speed = = USB_SPEED_HIGH ) ? 125 : 1000 ;
if ( interval % 1000 )
unit = ' u ' ;
else {
unit = ' m ' ;
interval / = 1000 ;
}
return sprintf ( buf , " %d%cs \n " , interval , unit ) ;
}
static ssize_t show_ep_direction ( struct device * dev , struct device_attribute * attr , char * buf )
{
struct endpoint_attribute * endpoint_attr = to_endpoint_attr ( attr ) ;
char * direction ;
if ( ( endpoint_attr - > endpoint - > bmAttributes & USB_ENDPOINT_XFERTYPE_MASK ) = =
USB_ENDPOINT_XFER_CONTROL )
direction = " both " ;
else if ( endpoint_attr - > endpoint - > bEndpointAddress & USB_DIR_IN )
direction = " in " ;
else
direction = " out " ;
return sprintf ( buf , " %s \n " , direction ) ;
}
static struct endpoint_attribute * create_ep_attr ( struct usb_endpoint_descriptor * endpoint ,
struct usb_device * udev , char * name ,
ssize_t ( * show ) ( struct device * dev , struct device_attribute * attr , char * buf ) )
{
struct endpoint_attribute * ep_attr ;
ep_attr = kzalloc ( sizeof ( * ep_attr ) , GFP_KERNEL ) ;
if ( ep_attr ) {
ep_attr - > endpoint = endpoint ;
ep_attr - > udev = udev ;
ep_attr - > dev_attr . attr . name = name ;
ep_attr - > dev_attr . attr . mode = 0444 ;
ep_attr - > dev_attr . attr . owner = THIS_MODULE ;
ep_attr - > dev_attr . show = show ;
}
return ep_attr ;
}
static void usb_create_ep_files ( struct kobject * kobj , struct usb_host_endpoint * endpoint , struct usb_device * udev )
{
struct usb_endpoint_descriptor * ep ;
ep = & endpoint - > desc ;
endpoint - > attrs = kzalloc ( sizeof ( struct attribute * ) * 10 , GFP_KERNEL ) ;
endpoint - > attrs [ 0 ] = & ( create_ep_attr ( ep , udev , " direction " , show_ep_direction ) - > dev_attr . attr ) ;
endpoint - > attrs [ 1 ] = & ( create_ep_attr ( ep , udev , " type " , show_ep_type ) - > dev_attr . attr ) ;
endpoint - > attrs [ 2 ] = & ( create_ep_attr ( ep , udev , " bLength " , show_ep_bLength ) - > dev_attr . attr ) ;
endpoint - > attrs [ 3 ] = & ( create_ep_attr ( ep , udev , " bDescriptorType " , show_ep_bDescriptorType ) - > dev_attr . attr ) ;
endpoint - > attrs [ 4 ] = & ( create_ep_attr ( ep , udev , " bEndpointAddress " , show_ep_bEndpointAddress ) - > dev_attr . attr ) ;
endpoint - > attrs [ 5 ] = & ( create_ep_attr ( ep , udev , " bmAttributes " , show_ep_bmAttributes ) - > dev_attr . attr ) ;
endpoint - > attrs [ 6 ] = & ( create_ep_attr ( ep , udev , " wMaxPacketSize " , show_ep_wMaxPacketSize ) - > dev_attr . attr ) ;
endpoint - > attrs [ 7 ] = & ( create_ep_attr ( ep , udev , " bInterval " , show_ep_bInterval ) - > dev_attr . attr ) ;
endpoint - > attrs [ 8 ] = & ( create_ep_attr ( ep , udev , " interval " , show_ep_interval ) - > dev_attr . attr ) ;
endpoint - > attrs [ 9 ] = NULL ;
endpoint - > num_attrs = 9 ;
endpoint - > attr_group = kzalloc ( sizeof ( * endpoint - > attr_group ) , GFP_KERNEL ) ;
endpoint - > attr_name = kzalloc ( 10 , GFP_KERNEL ) ;
sprintf ( endpoint - > attr_name , " ep_%02x " , endpoint - > desc . bEndpointAddress ) ;
endpoint - > attr_group - > attrs = endpoint - > attrs ;
endpoint - > attr_group - > name = endpoint - > attr_name ;
sysfs_create_group ( kobj , endpoint - > attr_group ) ;
}
static void usb_remove_ep_files ( struct kobject * kobj , struct usb_host_endpoint * endpoint )
{
int i ;
sysfs_remove_group ( kobj , endpoint - > attr_group ) ;
kfree ( endpoint - > attr_group ) ;
kfree ( endpoint - > attr_name ) ;
for ( i = 0 ; i < endpoint - > num_attrs ; + + i )
kfree ( endpoint - > attrs [ i ] ) ;
kfree ( endpoint - > attrs ) ;
}
2005-04-16 15:20:36 -07:00
/* Active configuration fields */
# define usb_actconfig_show(field, multiplier, format_string) \
2005-05-17 06:43:37 -04:00
static ssize_t show_ # # field ( struct device * dev , struct device_attribute * attr , char * buf ) \
2005-04-16 15:20:36 -07:00
{ \
struct usb_device * udev ; \
struct usb_host_config * actconfig ; \
\
udev = to_usb_device ( dev ) ; \
actconfig = udev - > actconfig ; \
if ( actconfig ) \
return sprintf ( buf , format_string , \
actconfig - > desc . field * multiplier ) ; \
else \
return 0 ; \
} \
# define usb_actconfig_attr(field, multiplier, format_string) \
usb_actconfig_show ( field , multiplier , format_string ) \
static DEVICE_ATTR ( field , S_IRUGO , show_ # # field , NULL ) ;
usb_actconfig_attr ( bNumInterfaces , 1 , " %2d \n " )
usb_actconfig_attr ( bmAttributes , 1 , " %2x \n " )
usb_actconfig_attr ( bMaxPower , 2 , " %3dmA \n " )
2005-05-17 06:43:37 -04:00
static ssize_t show_configuration_string ( struct device * dev , struct device_attribute * attr , char * buf )
2005-04-16 15:20:36 -07:00
{
struct usb_device * udev ;
struct usb_host_config * actconfig ;
int len ;
udev = to_usb_device ( dev ) ;
actconfig = udev - > actconfig ;
if ( ( ! actconfig ) | | ( ! actconfig - > string ) )
return 0 ;
len = sprintf ( buf , actconfig - > string , PAGE_SIZE ) ;
if ( len < 0 )
return 0 ;
buf [ len ] = ' \n ' ;
buf [ len + 1 ] = 0 ;
return len + 1 ;
}
static DEVICE_ATTR ( configuration , S_IRUGO , show_configuration_string , NULL ) ;
/* configuration value is always present, and r/w */
usb_actconfig_show ( bConfigurationValue , 1 , " %u \n " ) ;
static ssize_t
2005-05-17 06:43:37 -04:00
set_bConfigurationValue ( struct device * dev , struct device_attribute * attr , const char * buf , size_t count )
2005-04-16 15:20:36 -07:00
{
struct usb_device * udev = udev = to_usb_device ( dev ) ;
int config , value ;
if ( sscanf ( buf , " %u " , & config ) ! = 1 | | config > 255 )
return - EINVAL ;
usb_lock_device ( udev ) ;
value = usb_set_configuration ( udev , config ) ;
usb_unlock_device ( udev ) ;
return ( value < 0 ) ? value : count ;
}
static DEVICE_ATTR ( bConfigurationValue , S_IRUGO | S_IWUSR ,
show_bConfigurationValue , set_bConfigurationValue ) ;
/* String fields */
# define usb_string_attr(name) \
2005-05-17 06:43:37 -04:00
static ssize_t show_ # # name ( struct device * dev , struct device_attribute * attr , char * buf ) \
2005-04-16 15:20:36 -07:00
{ \
struct usb_device * udev ; \
int len ; \
\
udev = to_usb_device ( dev ) ; \
len = snprintf ( buf , 256 , " %s " , udev - > name ) ; \
if ( len < 0 ) \
return 0 ; \
buf [ len ] = ' \n ' ; \
buf [ len + 1 ] = 0 ; \
return len + 1 ; \
} \
static DEVICE_ATTR ( name , S_IRUGO , show_ # # name , NULL ) ;
usb_string_attr ( product ) ;
usb_string_attr ( manufacturer ) ;
usb_string_attr ( serial ) ;
static ssize_t
2005-05-17 06:43:37 -04:00
show_speed ( struct device * dev , struct device_attribute * attr , char * buf )
2005-04-16 15:20:36 -07:00
{
struct usb_device * udev ;
char * speed ;
udev = to_usb_device ( dev ) ;
switch ( udev - > speed ) {
case USB_SPEED_LOW :
speed = " 1.5 " ;
break ;
case USB_SPEED_UNKNOWN :
case USB_SPEED_FULL :
speed = " 12 " ;
break ;
case USB_SPEED_HIGH :
speed = " 480 " ;
break ;
default :
speed = " unknown " ;
}
return sprintf ( buf , " %s \n " , speed ) ;
}
static DEVICE_ATTR ( speed , S_IRUGO , show_speed , NULL ) ;
static ssize_t
2005-05-17 06:43:37 -04:00
show_devnum ( struct device * dev , struct device_attribute * attr , char * buf )
2005-04-16 15:20:36 -07:00
{
struct usb_device * udev ;
udev = to_usb_device ( dev ) ;
return sprintf ( buf , " %d \n " , udev - > devnum ) ;
}
static DEVICE_ATTR ( devnum , S_IRUGO , show_devnum , NULL ) ;
static ssize_t
2005-05-17 06:43:37 -04:00
show_version ( struct device * dev , struct device_attribute * attr , char * buf )
2005-04-16 15:20:36 -07:00
{
struct usb_device * udev ;
u16 bcdUSB ;
udev = to_usb_device ( dev ) ;
bcdUSB = le16_to_cpu ( udev - > descriptor . bcdUSB ) ;
return sprintf ( buf , " %2x.%02x \n " , bcdUSB > > 8 , bcdUSB & 0xff ) ;
}
static DEVICE_ATTR ( version , S_IRUGO , show_version , NULL ) ;
static ssize_t
2005-05-17 06:43:37 -04:00
show_maxchild ( struct device * dev , struct device_attribute * attr , char * buf )
2005-04-16 15:20:36 -07:00
{
struct usb_device * udev ;
udev = to_usb_device ( dev ) ;
return sprintf ( buf , " %d \n " , udev - > maxchild ) ;
}
static DEVICE_ATTR ( maxchild , S_IRUGO , show_maxchild , NULL ) ;
/* Descriptor fields */
# define usb_descriptor_attr_le16(field, format_string) \
static ssize_t \
2005-05-17 06:43:37 -04:00
show_ # # field ( struct device * dev , struct device_attribute * attr , char * buf ) \
2005-04-16 15:20:36 -07:00
{ \
struct usb_device * udev ; \
\
udev = to_usb_device ( dev ) ; \
return sprintf ( buf , format_string , \
le16_to_cpu ( udev - > descriptor . field ) ) ; \
} \
static DEVICE_ATTR ( field , S_IRUGO , show_ # # field , NULL ) ;
usb_descriptor_attr_le16 ( idVendor , " %04x \n " )
usb_descriptor_attr_le16 ( idProduct , " %04x \n " )
usb_descriptor_attr_le16 ( bcdDevice , " %04x \n " )
# define usb_descriptor_attr(field, format_string) \
static ssize_t \
2005-05-17 06:43:37 -04:00
show_ # # field ( struct device * dev , struct device_attribute * attr , char * buf ) \
2005-04-16 15:20:36 -07:00
{ \
struct usb_device * udev ; \
\
udev = to_usb_device ( dev ) ; \
return sprintf ( buf , format_string , udev - > descriptor . field ) ; \
} \
static DEVICE_ATTR ( field , S_IRUGO , show_ # # field , NULL ) ;
usb_descriptor_attr ( bDeviceClass , " %02x \n " )
usb_descriptor_attr ( bDeviceSubClass , " %02x \n " )
usb_descriptor_attr ( bDeviceProtocol , " %02x \n " )
usb_descriptor_attr ( bNumConfigurations , " %d \n " )
2005-06-29 16:53:29 -07:00
usb_descriptor_attr ( bMaxPacketSize0 , " %d \n " )
2005-04-16 15:20:36 -07:00
static struct attribute * dev_attrs [ ] = {
/* current configuration's attributes */
& dev_attr_bNumInterfaces . attr ,
& dev_attr_bConfigurationValue . attr ,
& dev_attr_bmAttributes . attr ,
& dev_attr_bMaxPower . attr ,
/* device attributes */
& dev_attr_idVendor . attr ,
& dev_attr_idProduct . attr ,
& dev_attr_bcdDevice . attr ,
& dev_attr_bDeviceClass . attr ,
& dev_attr_bDeviceSubClass . attr ,
& dev_attr_bDeviceProtocol . attr ,
& dev_attr_bNumConfigurations . attr ,
2005-06-29 16:53:29 -07:00
& dev_attr_bMaxPacketSize0 . attr ,
2005-04-16 15:20:36 -07:00
& dev_attr_speed . attr ,
& dev_attr_devnum . attr ,
& dev_attr_version . attr ,
& dev_attr_maxchild . attr ,
NULL ,
} ;
static struct attribute_group dev_attr_grp = {
. attrs = dev_attrs ,
} ;
void usb_create_sysfs_dev_files ( struct usb_device * udev )
{
struct device * dev = & udev - > dev ;
sysfs_create_group ( & dev - > kobj , & dev_attr_grp ) ;
if ( udev - > manufacturer )
device_create_file ( dev , & dev_attr_manufacturer ) ;
if ( udev - > product )
device_create_file ( dev , & dev_attr_product ) ;
if ( udev - > serial )
device_create_file ( dev , & dev_attr_serial ) ;
device_create_file ( dev , & dev_attr_configuration ) ;
2005-06-20 21:15:16 -07:00
usb_create_ep_files ( & dev - > kobj , & udev - > ep0 , udev ) ;
2005-04-16 15:20:36 -07:00
}
void usb_remove_sysfs_dev_files ( struct usb_device * udev )
{
struct device * dev = & udev - > dev ;
2005-06-20 21:15:16 -07:00
usb_remove_ep_files ( & dev - > kobj , & udev - > ep0 ) ;
2005-04-16 15:20:36 -07:00
sysfs_remove_group ( & dev - > kobj , & dev_attr_grp ) ;
if ( udev - > descriptor . iManufacturer )
device_remove_file ( dev , & dev_attr_manufacturer ) ;
if ( udev - > descriptor . iProduct )
device_remove_file ( dev , & dev_attr_product ) ;
if ( udev - > descriptor . iSerialNumber )
device_remove_file ( dev , & dev_attr_serial ) ;
device_remove_file ( dev , & dev_attr_configuration ) ;
}
/* Interface fields */
# define usb_intf_attr(field, format_string) \
static ssize_t \
2005-05-17 06:43:37 -04:00
show_ # # field ( struct device * dev , struct device_attribute * attr , char * buf ) \
2005-04-16 15:20:36 -07:00
{ \
struct usb_interface * intf = to_usb_interface ( dev ) ; \
\
return sprintf ( buf , format_string , intf - > cur_altsetting - > desc . field ) ; \
} \
static DEVICE_ATTR ( field , S_IRUGO , show_ # # field , NULL ) ;
usb_intf_attr ( bInterfaceNumber , " %02x \n " )
usb_intf_attr ( bAlternateSetting , " %2d \n " )
usb_intf_attr ( bNumEndpoints , " %02x \n " )
usb_intf_attr ( bInterfaceClass , " %02x \n " )
usb_intf_attr ( bInterfaceSubClass , " %02x \n " )
usb_intf_attr ( bInterfaceProtocol , " %02x \n " )
2005-05-17 06:43:37 -04:00
static ssize_t show_interface_string ( struct device * dev , struct device_attribute * attr , char * buf )
2005-04-16 15:20:36 -07:00
{
struct usb_interface * intf ;
struct usb_device * udev ;
int len ;
intf = to_usb_interface ( dev ) ;
udev = interface_to_usbdev ( intf ) ;
len = snprintf ( buf , 256 , " %s " , intf - > cur_altsetting - > string ) ;
if ( len < 0 )
return 0 ;
buf [ len ] = ' \n ' ;
buf [ len + 1 ] = 0 ;
return len + 1 ;
}
static DEVICE_ATTR ( interface , S_IRUGO , show_interface_string , NULL ) ;
2005-06-19 12:21:43 +02:00
static ssize_t show_modalias ( struct device * dev , struct device_attribute * attr , char * buf )
2005-05-10 06:45:10 -07:00
{
struct usb_interface * intf ;
struct usb_device * udev ;
2005-05-18 13:12:49 +01:00
int len ;
2005-05-10 06:45:10 -07:00
intf = to_usb_interface ( dev ) ;
udev = interface_to_usbdev ( intf ) ;
2005-05-18 13:12:49 +01:00
len = sprintf ( buf , " usb:v%04Xp%04Xd%04Xdc%02Xdsc%02Xdp%02Xic " ,
2005-05-10 06:45:10 -07:00
le16_to_cpu ( udev - > descriptor . idVendor ) ,
le16_to_cpu ( udev - > descriptor . idProduct ) ,
le16_to_cpu ( udev - > descriptor . bcdDevice ) ,
udev - > descriptor . bDeviceClass ,
udev - > descriptor . bDeviceSubClass ,
2005-05-18 13:12:49 +01:00
udev - > descriptor . bDeviceProtocol ) ;
buf + = len ;
if ( udev - > descriptor . bDeviceClass = = 0 ) {
struct usb_host_interface * alt = intf - > cur_altsetting ;
return len + sprintf ( buf , " %02Xisc%02Xip%02X \n " ,
2005-05-10 06:45:10 -07:00
alt - > desc . bInterfaceClass ,
alt - > desc . bInterfaceSubClass ,
alt - > desc . bInterfaceProtocol ) ;
} else {
2005-05-18 13:12:49 +01:00
return len + sprintf ( buf , " *isc*ip* \n " ) ;
2005-05-10 06:45:10 -07:00
}
}
static DEVICE_ATTR ( modalias , S_IRUGO , show_modalias , NULL ) ;
2005-04-16 15:20:36 -07:00
static struct attribute * intf_attrs [ ] = {
& dev_attr_bInterfaceNumber . attr ,
& dev_attr_bAlternateSetting . attr ,
& dev_attr_bNumEndpoints . attr ,
& dev_attr_bInterfaceClass . attr ,
& dev_attr_bInterfaceSubClass . attr ,
& dev_attr_bInterfaceProtocol . attr ,
2005-05-10 06:45:10 -07:00
& dev_attr_modalias . attr ,
2005-04-16 15:20:36 -07:00
NULL ,
} ;
static struct attribute_group intf_attr_grp = {
. attrs = intf_attrs ,
} ;
2005-06-20 21:15:16 -07:00
static void usb_create_intf_ep_files ( struct usb_interface * intf )
{
struct usb_host_interface * iface_desc ;
int i ;
iface_desc = intf - > cur_altsetting ;
for ( i = 0 ; i < iface_desc - > desc . bNumEndpoints ; + + i )
usb_create_ep_files ( & intf - > dev . kobj , & iface_desc - > endpoint [ i ] ,
interface_to_usbdev ( intf ) ) ;
}
static void usb_remove_intf_ep_files ( struct usb_interface * intf )
{
struct usb_host_interface * iface_desc ;
int i ;
iface_desc = intf - > cur_altsetting ;
for ( i = 0 ; i < iface_desc - > desc . bNumEndpoints ; + + i )
usb_remove_ep_files ( & intf - > dev . kobj , & iface_desc - > endpoint [ i ] ) ;
}
2005-04-16 15:20:36 -07:00
void usb_create_sysfs_intf_files ( struct usb_interface * intf )
{
sysfs_create_group ( & intf - > dev . kobj , & intf_attr_grp ) ;
if ( intf - > cur_altsetting - > string )
device_create_file ( & intf - > dev , & dev_attr_interface ) ;
2005-06-20 21:15:16 -07:00
usb_create_intf_ep_files ( intf ) ;
2005-04-16 15:20:36 -07:00
}
void usb_remove_sysfs_intf_files ( struct usb_interface * intf )
{
2005-06-20 21:15:16 -07:00
usb_remove_intf_ep_files ( intf ) ;
2005-04-16 15:20:36 -07:00
sysfs_remove_group ( & intf - > dev . kobj , & intf_attr_grp ) ;
if ( intf - > cur_altsetting - > string )
device_remove_file ( & intf - > dev , & dev_attr_interface ) ;
}