diff options
-rw-r--r-- | drivers/usb/core/sysfs.c | 195 | ||||
-rw-r--r-- | include/linux/usb.h | 4 |
2 files changed, 197 insertions, 2 deletions
diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c index 00297f113849..eae413bf8c2a 100644 --- a/drivers/usb/core/sysfs.c +++ b/drivers/usb/core/sysfs.c | |||
@@ -22,6 +22,174 @@ | |||
22 | 22 | ||
23 | #include "usb.h" | 23 | #include "usb.h" |
24 | 24 | ||
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 | |||
25 | /* Active configuration fields */ | 193 | /* Active configuration fields */ |
26 | #define usb_actconfig_show(field, multiplier, format_string) \ | 194 | #define usb_actconfig_show(field, multiplier, format_string) \ |
27 | static ssize_t show_##field (struct device *dev, struct device_attribute *attr, char *buf) \ | 195 | 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) | |||
236 | if (udev->serial) | 404 | if (udev->serial) |
237 | device_create_file (dev, &dev_attr_serial); | 405 | device_create_file (dev, &dev_attr_serial); |
238 | device_create_file (dev, &dev_attr_configuration); | 406 | device_create_file (dev, &dev_attr_configuration); |
407 | usb_create_ep_files(&dev->kobj, &udev->ep0, udev); | ||
239 | } | 408 | } |
240 | 409 | ||
241 | void usb_remove_sysfs_dev_files (struct usb_device *udev) | 410 | void usb_remove_sysfs_dev_files (struct usb_device *udev) |
242 | { | 411 | { |
243 | struct device *dev = &udev->dev; | 412 | struct device *dev = &udev->dev; |
244 | 413 | ||
414 | usb_remove_ep_files(&dev->kobj, &udev->ep0); | ||
245 | sysfs_remove_group(&dev->kobj, &dev_attr_grp); | 415 | sysfs_remove_group(&dev->kobj, &dev_attr_grp); |
246 | 416 | ||
247 | if (udev->descriptor.iManufacturer) | 417 | if (udev->descriptor.iManufacturer) |
@@ -333,20 +503,41 @@ static struct attribute_group intf_attr_grp = { | |||
333 | .attrs = intf_attrs, | 503 | .attrs = intf_attrs, |
334 | }; | 504 | }; |
335 | 505 | ||
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 | |||
336 | void usb_create_sysfs_intf_files (struct usb_interface *intf) | 527 | void usb_create_sysfs_intf_files (struct usb_interface *intf) |
337 | { | 528 | { |
338 | sysfs_create_group(&intf->dev.kobj, &intf_attr_grp); | 529 | sysfs_create_group(&intf->dev.kobj, &intf_attr_grp); |
339 | 530 | ||
340 | if (intf->cur_altsetting->string) | 531 | if (intf->cur_altsetting->string) |
341 | device_create_file(&intf->dev, &dev_attr_interface); | 532 | device_create_file(&intf->dev, &dev_attr_interface); |
342 | 533 | usb_create_intf_ep_files(intf); | |
343 | } | 534 | } |
344 | 535 | ||
345 | void usb_remove_sysfs_intf_files (struct usb_interface *intf) | 536 | void usb_remove_sysfs_intf_files (struct usb_interface *intf) |
346 | { | 537 | { |
538 | usb_remove_intf_ep_files(intf); | ||
347 | sysfs_remove_group(&intf->dev.kobj, &intf_attr_grp); | 539 | sysfs_remove_group(&intf->dev.kobj, &intf_attr_grp); |
348 | 540 | ||
349 | if (intf->cur_altsetting->string) | 541 | if (intf->cur_altsetting->string) |
350 | device_remove_file(&intf->dev, &dev_attr_interface); | 542 | device_remove_file(&intf->dev, &dev_attr_interface); |
351 | |||
352 | } | 543 | } |
diff --git a/include/linux/usb.h b/include/linux/usb.h index 8f731e8f2821..4512210e97e7 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h | |||
@@ -57,6 +57,10 @@ struct usb_host_endpoint { | |||
57 | struct usb_endpoint_descriptor desc; | 57 | struct usb_endpoint_descriptor desc; |
58 | struct list_head urb_list; | 58 | struct list_head urb_list; |
59 | void *hcpriv; | 59 | void *hcpriv; |
60 | char *attr_name; | ||
61 | struct attribute_group *attr_group; | ||
62 | struct attribute **attrs; | ||
63 | int num_attrs; | ||
60 | 64 | ||
61 | unsigned char *extra; /* Extra descriptors */ | 65 | unsigned char *extra; /* Extra descriptors */ |
62 | int extralen; | 66 | int extralen; |