|
22 | 22 |
|
23 | 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 | 193 | /* Active configuration fields */
|
26 | 194 | #define usb_actconfig_show(field, multiplier, format_string) \
|
27 | 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 | 404 | if (udev->serial)
|
237 | 405 | device_create_file (dev, &dev_attr_serial);
|
238 | 406 | device_create_file (dev, &dev_attr_configuration);
|
| 407 | + usb_create_ep_files(&dev->kobj, &udev->ep0, udev); |
239 | 408 | }
|
240 | 409 |
|
241 | 410 | void usb_remove_sysfs_dev_files (struct usb_device *udev)
|
242 | 411 | {
|
243 | 412 | struct device *dev = &udev->dev;
|
244 | 413 |
|
| 414 | + usb_remove_ep_files(&dev->kobj, &udev->ep0); |
245 | 415 | sysfs_remove_group(&dev->kobj, &dev_attr_grp);
|
246 | 416 |
|
247 | 417 | if (udev->descriptor.iManufacturer)
|
@@ -333,20 +503,41 @@ static struct attribute_group intf_attr_grp = {
|
333 | 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 | 527 | void usb_create_sysfs_intf_files (struct usb_interface *intf)
|
337 | 528 | {
|
338 | 529 | sysfs_create_group(&intf->dev.kobj, &intf_attr_grp);
|
339 | 530 |
|
340 | 531 | if (intf->cur_altsetting->string)
|
341 | 532 | device_create_file(&intf->dev, &dev_attr_interface);
|
342 |
| - |
| 533 | + usb_create_intf_ep_files(intf); |
343 | 534 | }
|
344 | 535 |
|
345 | 536 | void usb_remove_sysfs_intf_files (struct usb_interface *intf)
|
346 | 537 | {
|
| 538 | + usb_remove_intf_ep_files(intf); |
347 | 539 | sysfs_remove_group(&intf->dev.kobj, &intf_attr_grp);
|
348 | 540 |
|
349 | 541 | if (intf->cur_altsetting->string)
|
350 | 542 | device_remove_file(&intf->dev, &dev_attr_interface);
|
351 |
| - |
352 | 543 | }
|
0 commit comments