2006-06-14 12:14:34 -07:00
/*
* drivers / usb / core / endpoint . c
*
* ( C ) Copyright 2002 , 2004 , 2006 Greg Kroah - Hartman
* ( C ) Copyright 2002 , 2004 IBM Corp .
* ( C ) Copyright 2006 Novell Inc .
*
* Endpoint sysfs stuff
*
*/
# include <linux/kernel.h>
2006-11-18 22:30:16 -08:00
# include <linux/spinlock.h>
# include <linux/idr.h>
2006-06-14 12:14:34 -07:00
# include <linux/usb.h>
# include "usb.h"
2006-06-14 12:14:34 -07:00
struct ep_device {
2006-06-14 12:14:34 -07:00
struct usb_endpoint_descriptor * desc ;
struct usb_device * udev ;
2006-06-14 12:14:34 -07:00
struct device dev ;
2006-06-14 12:14:34 -07:00
} ;
2006-06-14 12:14:34 -07:00
# define to_ep_device(_dev) \
container_of ( _dev , struct ep_device , dev )
2006-06-14 12:14:34 -07:00
2009-05-04 19:48:32 +02:00
struct device_type usb_ep_device_type = {
. name = " usb_endpoint " ,
} ;
2006-06-14 12:14:34 -07:00
struct ep_attribute {
struct attribute attr ;
ssize_t ( * show ) ( struct usb_device * ,
struct usb_endpoint_descriptor * , char * ) ;
} ;
# define to_ep_attribute(_attr) \
container_of ( _attr , struct ep_attribute , attr )
# define usb_ep_attr(field, format_string) \
2006-06-14 12:14:34 -07:00
static ssize_t show_ep_ # # field ( struct device * dev , \
struct device_attribute * attr , \
char * buf ) \
2006-06-14 12:14:34 -07:00
{ \
2006-06-14 12:14:34 -07:00
struct ep_device * ep = to_ep_device ( dev ) ; \
return sprintf ( buf , format_string , ep - > desc - > field ) ; \
2006-06-14 12:14:34 -07:00
} \
2006-06-14 12:14:34 -07:00
static DEVICE_ATTR ( field , S_IRUGO , show_ep_ # # field , NULL ) ;
2006-06-14 12:14:34 -07:00
usb_ep_attr ( bLength , " %02x \n " )
usb_ep_attr ( bEndpointAddress , " %02x \n " )
usb_ep_attr ( bmAttributes , " %02x \n " )
usb_ep_attr ( bInterval , " %02x \n " )
2006-06-14 12:14:34 -07:00
static ssize_t show_ep_wMaxPacketSize ( struct device * dev ,
struct device_attribute * attr , char * buf )
2006-06-14 12:14:34 -07:00
{
2006-06-14 12:14:34 -07:00
struct ep_device * ep = to_ep_device ( dev ) ;
2006-06-14 12:14:34 -07:00
return sprintf ( buf , " %04x \n " ,
2006-06-14 12:14:34 -07:00
le16_to_cpu ( ep - > desc - > wMaxPacketSize ) & 0x07ff ) ;
2006-06-14 12:14:34 -07:00
}
2006-06-14 12:14:34 -07:00
static DEVICE_ATTR ( wMaxPacketSize , S_IRUGO , show_ep_wMaxPacketSize , NULL ) ;
2006-06-14 12:14:34 -07:00
2006-06-14 12:14:34 -07:00
static ssize_t show_ep_type ( struct device * dev , struct device_attribute * attr ,
char * buf )
2006-06-14 12:14:34 -07:00
{
2006-06-14 12:14:34 -07:00
struct ep_device * ep = to_ep_device ( dev ) ;
2006-06-14 12:14:34 -07:00
char * type = " unknown " ;
2008-12-29 11:22:14 +01:00
switch ( usb_endpoint_type ( ep - > desc ) ) {
2006-06-14 12:14:34 -07:00
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 ) ;
}
2006-06-14 12:14:34 -07:00
static DEVICE_ATTR ( type , S_IRUGO , show_ep_type , NULL ) ;
2006-06-14 12:14:34 -07:00
2006-06-14 12:14:34 -07:00
static ssize_t show_ep_interval ( struct device * dev ,
struct device_attribute * attr , char * buf )
2006-06-14 12:14:34 -07:00
{
2006-06-14 12:14:34 -07:00
struct ep_device * ep = to_ep_device ( dev ) ;
2006-06-14 12:14:34 -07:00
char unit ;
unsigned interval = 0 ;
unsigned in ;
2006-06-14 12:14:34 -07:00
in = ( ep - > desc - > bEndpointAddress & USB_DIR_IN ) ;
2006-06-14 12:14:34 -07:00
2008-12-29 11:22:14 +01:00
switch ( usb_endpoint_type ( ep - > desc ) ) {
2006-06-14 12:14:34 -07:00
case USB_ENDPOINT_XFER_CONTROL :
2006-06-14 12:14:34 -07:00
if ( ep - > udev - > speed = = USB_SPEED_HIGH ) /* uframes per NAK */
interval = ep - > desc - > bInterval ;
2006-06-14 12:14:34 -07:00
break ;
case USB_ENDPOINT_XFER_ISOC :
2006-06-14 12:14:34 -07:00
interval = 1 < < ( ep - > desc - > bInterval - 1 ) ;
2006-06-14 12:14:34 -07:00
break ;
case USB_ENDPOINT_XFER_BULK :
2006-06-14 12:14:34 -07:00
if ( ep - > udev - > speed = = USB_SPEED_HIGH & & ! in ) /* uframes per NAK */
interval = ep - > desc - > bInterval ;
2006-06-14 12:14:34 -07:00
break ;
case USB_ENDPOINT_XFER_INT :
2006-06-14 12:14:34 -07:00
if ( ep - > udev - > speed = = USB_SPEED_HIGH )
interval = 1 < < ( ep - > desc - > bInterval - 1 ) ;
2006-06-14 12:14:34 -07:00
else
2006-06-14 12:14:34 -07:00
interval = ep - > desc - > bInterval ;
2006-06-14 12:14:34 -07:00
break ;
}
2006-06-14 12:14:34 -07:00
interval * = ( ep - > udev - > speed = = USB_SPEED_HIGH ) ? 125 : 1000 ;
2006-06-14 12:14:34 -07:00
if ( interval % 1000 )
unit = ' u ' ;
else {
unit = ' m ' ;
interval / = 1000 ;
}
return sprintf ( buf , " %d%cs \n " , interval , unit ) ;
}
2006-06-14 12:14:34 -07:00
static DEVICE_ATTR ( interval , S_IRUGO , show_ep_interval , NULL ) ;
2006-06-14 12:14:34 -07:00
2006-06-14 12:14:34 -07:00
static ssize_t show_ep_direction ( struct device * dev ,
struct device_attribute * attr , char * buf )
2006-06-14 12:14:34 -07:00
{
2006-06-14 12:14:34 -07:00
struct ep_device * ep = to_ep_device ( dev ) ;
2006-06-14 12:14:34 -07:00
char * direction ;
2008-12-29 11:22:14 +01:00
if ( usb_endpoint_xfer_control ( ep - > desc ) )
2006-06-14 12:14:34 -07:00
direction = " both " ;
2008-12-29 11:22:14 +01:00
else if ( usb_endpoint_dir_in ( ep - > desc ) )
2006-06-14 12:14:34 -07:00
direction = " in " ;
else
direction = " out " ;
return sprintf ( buf , " %s \n " , direction ) ;
}
2006-06-14 12:14:34 -07:00
static DEVICE_ATTR ( direction , S_IRUGO , show_ep_direction , NULL ) ;
static struct attribute * ep_dev_attrs [ ] = {
& dev_attr_bLength . attr ,
& dev_attr_bEndpointAddress . attr ,
& dev_attr_bmAttributes . attr ,
& dev_attr_bInterval . attr ,
& dev_attr_wMaxPacketSize . attr ,
& dev_attr_interval . attr ,
& dev_attr_type . attr ,
& dev_attr_direction . attr ,
2006-06-14 12:14:34 -07:00
NULL ,
} ;
2006-06-14 12:14:34 -07:00
static struct attribute_group ep_dev_attr_grp = {
. attrs = ep_dev_attrs ,
} ;
2009-06-24 10:06:31 -07:00
static const struct attribute_group * ep_dev_groups [ ] = {
2008-04-30 15:37:19 -04:00
& ep_dev_attr_grp ,
NULL
} ;
2006-06-14 12:14:34 -07:00
2006-06-14 12:14:34 -07:00
static void ep_device_release ( struct device * dev )
{
struct ep_device * ep_dev = to_ep_device ( dev ) ;
2006-06-14 12:14:34 -07:00
2006-06-14 12:14:34 -07:00
kfree ( ep_dev ) ;
}
2006-06-14 12:14:34 -07:00
2008-12-05 14:10:34 -05:00
int usb_create_ep_devs ( struct device * parent ,
2006-08-28 11:43:25 -07:00
struct usb_host_endpoint * endpoint ,
struct usb_device * udev )
2006-06-14 12:14:34 -07:00
{
2006-06-14 12:14:34 -07:00
struct ep_device * ep_dev ;
int retval ;
ep_dev = kzalloc ( sizeof ( * ep_dev ) , GFP_KERNEL ) ;
if ( ! ep_dev ) {
retval = - ENOMEM ;
2009-05-04 19:48:32 +02:00
goto exit ;
2006-11-18 22:30:16 -08:00
}
2006-06-14 12:14:34 -07:00
2006-06-14 12:14:34 -07:00
ep_dev - > desc = & endpoint - > desc ;
ep_dev - > udev = udev ;
2008-04-30 15:37:19 -04:00
ep_dev - > dev . groups = ep_dev_groups ;
2009-05-04 19:48:32 +02:00
ep_dev - > dev . type = & usb_ep_device_type ;
2006-06-14 12:14:34 -07:00
ep_dev - > dev . parent = parent ;
ep_dev - > dev . release = ep_device_release ;
2009-05-04 19:48:32 +02:00
dev_set_name ( & ep_dev - > dev , " ep_%02x " , endpoint - > desc . bEndpointAddress ) ;
2006-06-14 12:14:34 -07:00
2006-06-14 12:14:34 -07:00
retval = device_register ( & ep_dev - > dev ) ;
if ( retval )
2009-05-04 19:48:32 +02:00
goto error_register ;
2006-06-14 12:14:34 -07:00
2006-10-10 11:56:26 -04:00
endpoint - > ep_dev = ep_dev ;
2006-08-28 11:43:25 -07:00
return retval ;
2006-10-10 11:56:26 -04:00
error_register :
2006-06-14 12:14:34 -07:00
kfree ( ep_dev ) ;
2006-10-10 11:56:26 -04:00
exit :
2006-08-28 11:43:25 -07:00
return retval ;
2006-06-14 12:14:34 -07:00
}
2008-12-05 14:10:34 -05:00
void usb_remove_ep_devs ( struct usb_host_endpoint * endpoint )
2006-06-14 12:14:34 -07:00
{
2006-11-18 22:30:16 -08:00
struct ep_device * ep_dev = endpoint - > ep_dev ;
2006-06-14 12:14:34 -07:00
2006-11-18 22:30:16 -08:00
if ( ep_dev ) {
device_unregister ( & ep_dev - > dev ) ;
2006-06-14 12:14:34 -07:00
endpoint - > ep_dev = NULL ;
2006-06-14 12:14:34 -07:00
}
}