Skip to content

Commit 094f164

Browse files
committed
[PATCH] USB: add endpoint information to sysfs
This patch adds endpoint information for both devices and interfaces to sysfs. Previously it was only possible to get the endpoint information from usbfs, and never possible to get any information on endpoint 0. Signed-off-by: Greg Kroah-Hartman <[email protected]> drivers/usb/core/sysfs.c | 195 ++++++++++++++++++++++++++++++++++++++++++++++- include/linux/usb.h | 4 2 files changed, 197 insertions(+), 2 deletions(-)
1 parent 8da608c commit 094f164

File tree

2 files changed

+197
-2
lines changed

2 files changed

+197
-2
lines changed

drivers/usb/core/sysfs.c

Lines changed: 193 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,174 @@
2222

2323
#include "usb.h"
2424

25+
/* endpoint stuff */
26+
struct endpoint_attribute {
27+
struct device_attribute dev_attr;
28+
struct usb_endpoint_descriptor *endpoint;
29+
struct usb_device *udev;
30+
};
31+
#define to_endpoint_attr(_dev_attr) \
32+
container_of(_dev_attr, struct endpoint_attribute, dev_attr)
33+
34+
#define usb_ep_attr(field, format_string) \
35+
static ssize_t show_ep_##field(struct device *dev, struct device_attribute *attr, \
36+
char *buf) \
37+
{ \
38+
struct endpoint_attribute *endpoint_attr = to_endpoint_attr(attr); \
39+
\
40+
return sprintf(buf, format_string, endpoint_attr->endpoint->field); \
41+
}
42+
usb_ep_attr(bLength, "%02x\n")
43+
usb_ep_attr(bDescriptorType, "%02x\n")
44+
usb_ep_attr(bEndpointAddress, "%02x\n")
45+
usb_ep_attr(bmAttributes, "%02x\n")
46+
usb_ep_attr(bInterval, "%02x\n")
47+
48+
static ssize_t show_ep_wMaxPacketSize(struct device *dev,
49+
struct device_attribute *attr, char *buf)
50+
{
51+
struct endpoint_attribute *endpoint_attr = to_endpoint_attr(attr);
52+
53+
return sprintf(buf, "%04x\n",
54+
le16_to_cpu(endpoint_attr->endpoint->wMaxPacketSize) & 0x07ff);
55+
}
56+
57+
static ssize_t show_ep_type(struct device *dev, struct device_attribute *attr, char *buf)
58+
{
59+
struct endpoint_attribute *endpoint_attr = to_endpoint_attr(attr);
60+
char *type = "unknown";
61+
62+
switch (endpoint_attr->endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
63+
case USB_ENDPOINT_XFER_CONTROL:
64+
type = "Control";
65+
break;
66+
case USB_ENDPOINT_XFER_ISOC:
67+
type = "Isoc";
68+
break;
69+
case USB_ENDPOINT_XFER_BULK:
70+
type = "Bulk";
71+
break;
72+
case USB_ENDPOINT_XFER_INT:
73+
type = "Interrupt";
74+
break;
75+
}
76+
return sprintf(buf, "%s\n", type);
77+
}
78+
79+
static ssize_t show_ep_interval(struct device *dev, struct device_attribute *attr, char *buf)
80+
{
81+
struct endpoint_attribute *endpoint_attr = to_endpoint_attr(attr);
82+
struct usb_device *udev = endpoint_attr->udev;
83+
struct usb_endpoint_descriptor *endpoint = endpoint_attr->endpoint;
84+
char unit;
85+
unsigned interval = 0;
86+
unsigned in;
87+
88+
in = (endpoint->bEndpointAddress & USB_DIR_IN);
89+
90+
switch (endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
91+
case USB_ENDPOINT_XFER_CONTROL:
92+
if (udev->speed == USB_SPEED_HIGH) /* uframes per NAK */
93+
interval = endpoint->bInterval;
94+
break;
95+
case USB_ENDPOINT_XFER_ISOC:
96+
interval = 1 << (endpoint->bInterval - 1);
97+
break;
98+
case USB_ENDPOINT_XFER_BULK:
99+
if (udev->speed == USB_SPEED_HIGH && !in) /* uframes per NAK */
100+
interval = endpoint->bInterval;
101+
break;
102+
case USB_ENDPOINT_XFER_INT:
103+
if (udev->speed == USB_SPEED_HIGH) {
104+
interval = 1 << (endpoint->bInterval - 1);
105+
} else
106+
interval = endpoint->bInterval;
107+
break;
108+
}
109+
interval *= (udev->speed == USB_SPEED_HIGH) ? 125 : 1000;
110+
if (interval % 1000)
111+
unit = 'u';
112+
else {
113+
unit = 'm';
114+
interval /= 1000;
115+
}
116+
117+
return sprintf(buf, "%d%cs\n", interval, unit);
118+
}
119+
120+
static ssize_t show_ep_direction(struct device *dev, struct device_attribute *attr, char *buf)
121+
{
122+
struct endpoint_attribute *endpoint_attr = to_endpoint_attr(attr);
123+
char *direction;
124+
125+
if ((endpoint_attr->endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
126+
USB_ENDPOINT_XFER_CONTROL)
127+
direction = "both";
128+
else if (endpoint_attr->endpoint->bEndpointAddress & USB_DIR_IN)
129+
direction = "in";
130+
else
131+
direction = "out";
132+
return sprintf(buf, "%s\n", direction);
133+
}
134+
135+
static struct endpoint_attribute *create_ep_attr(struct usb_endpoint_descriptor *endpoint,
136+
struct usb_device *udev, char *name,
137+
ssize_t (*show)(struct device *dev, struct device_attribute *attr, char *buf))
138+
{
139+
struct endpoint_attribute *ep_attr;
140+
141+
ep_attr = kzalloc(sizeof(*ep_attr), GFP_KERNEL);
142+
if (ep_attr) {
143+
ep_attr->endpoint = endpoint;
144+
ep_attr->udev = udev;
145+
ep_attr->dev_attr.attr.name = name;
146+
ep_attr->dev_attr.attr.mode = 0444;
147+
ep_attr->dev_attr.attr.owner = THIS_MODULE;
148+
ep_attr->dev_attr.show = show;
149+
}
150+
return ep_attr;
151+
}
152+
153+
static void usb_create_ep_files(struct kobject *kobj, struct usb_host_endpoint *endpoint, struct usb_device *udev)
154+
{
155+
struct usb_endpoint_descriptor *ep;
156+
157+
ep = &endpoint->desc;
158+
159+
endpoint->attrs = kzalloc(sizeof(struct attribute *) * 10, GFP_KERNEL);
160+
endpoint->attrs[0] = &(create_ep_attr(ep, udev, "direction", show_ep_direction)->dev_attr.attr);
161+
endpoint->attrs[1] = &(create_ep_attr(ep, udev, "type", show_ep_type)->dev_attr.attr);
162+
endpoint->attrs[2] = &(create_ep_attr(ep, udev, "bLength", show_ep_bLength)->dev_attr.attr);
163+
endpoint->attrs[3] = &(create_ep_attr(ep, udev, "bDescriptorType", show_ep_bDescriptorType)->dev_attr.attr);
164+
endpoint->attrs[4] = &(create_ep_attr(ep, udev, "bEndpointAddress", show_ep_bEndpointAddress)->dev_attr.attr);
165+
endpoint->attrs[5] = &(create_ep_attr(ep, udev, "bmAttributes", show_ep_bmAttributes)->dev_attr.attr);
166+
endpoint->attrs[6] = &(create_ep_attr(ep, udev, "wMaxPacketSize", show_ep_wMaxPacketSize)->dev_attr.attr);
167+
endpoint->attrs[7] = &(create_ep_attr(ep, udev, "bInterval", show_ep_bInterval)->dev_attr.attr);
168+
endpoint->attrs[8] = &(create_ep_attr(ep, udev, "interval", show_ep_interval)->dev_attr.attr);
169+
endpoint->attrs[9] = NULL;
170+
endpoint->num_attrs = 9;
171+
172+
endpoint->attr_group = kzalloc(sizeof(*endpoint->attr_group), GFP_KERNEL);
173+
endpoint->attr_name = kzalloc(10, GFP_KERNEL);
174+
sprintf(endpoint->attr_name, "ep_%02x", endpoint->desc.bEndpointAddress);
175+
176+
endpoint->attr_group->attrs = endpoint->attrs;
177+
endpoint->attr_group->name = endpoint->attr_name;
178+
sysfs_create_group(kobj, endpoint->attr_group);
179+
}
180+
181+
static void usb_remove_ep_files(struct kobject *kobj, struct usb_host_endpoint *endpoint)
182+
{
183+
int i;
184+
185+
sysfs_remove_group(kobj, endpoint->attr_group);
186+
kfree(endpoint->attr_group);
187+
kfree(endpoint->attr_name);
188+
for (i = 0; i < endpoint->num_attrs; ++i)
189+
kfree(endpoint->attrs[i]);
190+
kfree(endpoint->attrs);
191+
}
192+
25193
/* Active configuration fields */
26194
#define usb_actconfig_show(field, multiplier, format_string) \
27195
static ssize_t show_##field (struct device *dev, struct device_attribute *attr, char *buf) \
@@ -236,12 +404,14 @@ void usb_create_sysfs_dev_files (struct usb_device *udev)
236404
if (udev->serial)
237405
device_create_file (dev, &dev_attr_serial);
238406
device_create_file (dev, &dev_attr_configuration);
407+
usb_create_ep_files(&dev->kobj, &udev->ep0, udev);
239408
}
240409

241410
void usb_remove_sysfs_dev_files (struct usb_device *udev)
242411
{
243412
struct device *dev = &udev->dev;
244413

414+
usb_remove_ep_files(&dev->kobj, &udev->ep0);
245415
sysfs_remove_group(&dev->kobj, &dev_attr_grp);
246416

247417
if (udev->descriptor.iManufacturer)
@@ -333,20 +503,41 @@ static struct attribute_group intf_attr_grp = {
333503
.attrs = intf_attrs,
334504
};
335505

506+
static void usb_create_intf_ep_files(struct usb_interface *intf)
507+
{
508+
struct usb_host_interface *iface_desc;
509+
int i;
510+
511+
iface_desc = intf->cur_altsetting;
512+
for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i)
513+
usb_create_ep_files(&intf->dev.kobj, &iface_desc->endpoint[i],
514+
interface_to_usbdev(intf));
515+
}
516+
517+
static void usb_remove_intf_ep_files(struct usb_interface *intf)
518+
{
519+
struct usb_host_interface *iface_desc;
520+
int i;
521+
522+
iface_desc = intf->cur_altsetting;
523+
for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i)
524+
usb_remove_ep_files(&intf->dev.kobj, &iface_desc->endpoint[i]);
525+
}
526+
336527
void usb_create_sysfs_intf_files (struct usb_interface *intf)
337528
{
338529
sysfs_create_group(&intf->dev.kobj, &intf_attr_grp);
339530

340531
if (intf->cur_altsetting->string)
341532
device_create_file(&intf->dev, &dev_attr_interface);
342-
533+
usb_create_intf_ep_files(intf);
343534
}
344535

345536
void usb_remove_sysfs_intf_files (struct usb_interface *intf)
346537
{
538+
usb_remove_intf_ep_files(intf);
347539
sysfs_remove_group(&intf->dev.kobj, &intf_attr_grp);
348540

349541
if (intf->cur_altsetting->string)
350542
device_remove_file(&intf->dev, &dev_attr_interface);
351-
352543
}

include/linux/usb.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,10 @@ struct usb_host_endpoint {
5757
struct usb_endpoint_descriptor desc;
5858
struct list_head urb_list;
5959
void *hcpriv;
60+
char *attr_name;
61+
struct attribute_group *attr_group;
62+
struct attribute **attrs;
63+
int num_attrs;
6064

6165
unsigned char *extra; /* Extra descriptors */
6266
int extralen;

0 commit comments

Comments
 (0)