blob: fdfaa7885515d075330921d17442bbc80f1dcf06 [file] [log] [blame]
Greg Kroah-Hartman84412f62006-06-14 12:14:34 -07001/*
2 * drivers/usb/core/endpoint.c
3 *
4 * (C) Copyright 2002,2004,2006 Greg Kroah-Hartman
5 * (C) Copyright 2002,2004 IBM Corp.
6 * (C) Copyright 2006 Novell Inc.
7 *
8 * Endpoint sysfs stuff
9 *
10 */
11
12#include <linux/kernel.h>
Sarah Bailey7e277802006-11-18 22:30:16 -080013#include <linux/spinlock.h>
14#include <linux/idr.h>
Greg Kroah-Hartman84412f62006-06-14 12:14:34 -070015#include <linux/usb.h>
16#include "usb.h"
17
Greg Kroah-Hartman9bde7492006-06-14 12:14:34 -070018struct ep_device {
Greg Kroah-Hartman84412f62006-06-14 12:14:34 -070019 struct usb_endpoint_descriptor *desc;
20 struct usb_device *udev;
Greg Kroah-Hartman9bde7492006-06-14 12:14:34 -070021 struct device dev;
Greg Kroah-Hartman84412f62006-06-14 12:14:34 -070022};
Greg Kroah-Hartman9bde7492006-06-14 12:14:34 -070023#define to_ep_device(_dev) \
24 container_of(_dev, struct ep_device, dev)
Greg Kroah-Hartman84412f62006-06-14 12:14:34 -070025
Kay Sievers55129662009-05-04 19:48:32 +020026struct device_type usb_ep_device_type = {
27 .name = "usb_endpoint",
28};
29
Greg Kroah-Hartman84412f62006-06-14 12:14:34 -070030struct ep_attribute {
31 struct attribute attr;
32 ssize_t (*show)(struct usb_device *,
33 struct usb_endpoint_descriptor *, char *);
34};
35#define to_ep_attribute(_attr) \
36 container_of(_attr, struct ep_attribute, attr)
37
Greg Kroah-Hartman84412f62006-06-14 12:14:34 -070038#define usb_ep_attr(field, format_string) \
Greg Kroah-Hartman9bde7492006-06-14 12:14:34 -070039static ssize_t show_ep_##field(struct device *dev, \
40 struct device_attribute *attr, \
41 char *buf) \
Greg Kroah-Hartman84412f62006-06-14 12:14:34 -070042{ \
Greg Kroah-Hartman9bde7492006-06-14 12:14:34 -070043 struct ep_device *ep = to_ep_device(dev); \
44 return sprintf(buf, format_string, ep->desc->field); \
Greg Kroah-Hartman84412f62006-06-14 12:14:34 -070045} \
Greg Kroah-Hartman9bde7492006-06-14 12:14:34 -070046static DEVICE_ATTR(field, S_IRUGO, show_ep_##field, NULL);
Greg Kroah-Hartman84412f62006-06-14 12:14:34 -070047
48usb_ep_attr(bLength, "%02x\n")
49usb_ep_attr(bEndpointAddress, "%02x\n")
50usb_ep_attr(bmAttributes, "%02x\n")
51usb_ep_attr(bInterval, "%02x\n")
52
Greg Kroah-Hartman9bde7492006-06-14 12:14:34 -070053static ssize_t show_ep_wMaxPacketSize(struct device *dev,
54 struct device_attribute *attr, char *buf)
Greg Kroah-Hartman84412f62006-06-14 12:14:34 -070055{
Greg Kroah-Hartman9bde7492006-06-14 12:14:34 -070056 struct ep_device *ep = to_ep_device(dev);
Greg Kroah-Hartman84412f62006-06-14 12:14:34 -070057 return sprintf(buf, "%04x\n",
Greg Kroah-Hartman9bde7492006-06-14 12:14:34 -070058 le16_to_cpu(ep->desc->wMaxPacketSize) & 0x07ff);
Greg Kroah-Hartman84412f62006-06-14 12:14:34 -070059}
Greg Kroah-Hartman9bde7492006-06-14 12:14:34 -070060static DEVICE_ATTR(wMaxPacketSize, S_IRUGO, show_ep_wMaxPacketSize, NULL);
Greg Kroah-Hartman84412f62006-06-14 12:14:34 -070061
Greg Kroah-Hartman9bde7492006-06-14 12:14:34 -070062static ssize_t show_ep_type(struct device *dev, struct device_attribute *attr,
63 char *buf)
Greg Kroah-Hartman84412f62006-06-14 12:14:34 -070064{
Greg Kroah-Hartman9bde7492006-06-14 12:14:34 -070065 struct ep_device *ep = to_ep_device(dev);
Greg Kroah-Hartman84412f62006-06-14 12:14:34 -070066 char *type = "unknown";
67
Julia Lawall2e0fe702008-12-29 11:22:14 +010068 switch (usb_endpoint_type(ep->desc)) {
Greg Kroah-Hartman84412f62006-06-14 12:14:34 -070069 case USB_ENDPOINT_XFER_CONTROL:
70 type = "Control";
71 break;
72 case USB_ENDPOINT_XFER_ISOC:
73 type = "Isoc";
74 break;
75 case USB_ENDPOINT_XFER_BULK:
76 type = "Bulk";
77 break;
78 case USB_ENDPOINT_XFER_INT:
79 type = "Interrupt";
80 break;
81 }
82 return sprintf(buf, "%s\n", type);
83}
Greg Kroah-Hartman9bde7492006-06-14 12:14:34 -070084static DEVICE_ATTR(type, S_IRUGO, show_ep_type, NULL);
Greg Kroah-Hartman84412f62006-06-14 12:14:34 -070085
Greg Kroah-Hartman9bde7492006-06-14 12:14:34 -070086static ssize_t show_ep_interval(struct device *dev,
87 struct device_attribute *attr, char *buf)
Greg Kroah-Hartman84412f62006-06-14 12:14:34 -070088{
Greg Kroah-Hartman9bde7492006-06-14 12:14:34 -070089 struct ep_device *ep = to_ep_device(dev);
Greg Kroah-Hartman84412f62006-06-14 12:14:34 -070090 char unit;
91 unsigned interval = 0;
92 unsigned in;
93
Greg Kroah-Hartman9bde7492006-06-14 12:14:34 -070094 in = (ep->desc->bEndpointAddress & USB_DIR_IN);
Greg Kroah-Hartman84412f62006-06-14 12:14:34 -070095
Julia Lawall2e0fe702008-12-29 11:22:14 +010096 switch (usb_endpoint_type(ep->desc)) {
Greg Kroah-Hartman84412f62006-06-14 12:14:34 -070097 case USB_ENDPOINT_XFER_CONTROL:
Greg Kroah-Hartman9bde7492006-06-14 12:14:34 -070098 if (ep->udev->speed == USB_SPEED_HIGH) /* uframes per NAK */
99 interval = ep->desc->bInterval;
Greg Kroah-Hartman84412f62006-06-14 12:14:34 -0700100 break;
101 case USB_ENDPOINT_XFER_ISOC:
Greg Kroah-Hartman9bde7492006-06-14 12:14:34 -0700102 interval = 1 << (ep->desc->bInterval - 1);
Greg Kroah-Hartman84412f62006-06-14 12:14:34 -0700103 break;
104 case USB_ENDPOINT_XFER_BULK:
Greg Kroah-Hartman9bde7492006-06-14 12:14:34 -0700105 if (ep->udev->speed == USB_SPEED_HIGH && !in) /* uframes per NAK */
106 interval = ep->desc->bInterval;
Greg Kroah-Hartman84412f62006-06-14 12:14:34 -0700107 break;
108 case USB_ENDPOINT_XFER_INT:
Greg Kroah-Hartman9bde7492006-06-14 12:14:34 -0700109 if (ep->udev->speed == USB_SPEED_HIGH)
110 interval = 1 << (ep->desc->bInterval - 1);
Greg Kroah-Hartman84412f62006-06-14 12:14:34 -0700111 else
Greg Kroah-Hartman9bde7492006-06-14 12:14:34 -0700112 interval = ep->desc->bInterval;
Greg Kroah-Hartman84412f62006-06-14 12:14:34 -0700113 break;
114 }
Greg Kroah-Hartman9bde7492006-06-14 12:14:34 -0700115 interval *= (ep->udev->speed == USB_SPEED_HIGH) ? 125 : 1000;
Greg Kroah-Hartman84412f62006-06-14 12:14:34 -0700116 if (interval % 1000)
117 unit = 'u';
118 else {
119 unit = 'm';
120 interval /= 1000;
121 }
122
123 return sprintf(buf, "%d%cs\n", interval, unit);
124}
Greg Kroah-Hartman9bde7492006-06-14 12:14:34 -0700125static DEVICE_ATTR(interval, S_IRUGO, show_ep_interval, NULL);
Greg Kroah-Hartman84412f62006-06-14 12:14:34 -0700126
Greg Kroah-Hartman9bde7492006-06-14 12:14:34 -0700127static ssize_t show_ep_direction(struct device *dev,
128 struct device_attribute *attr, char *buf)
Greg Kroah-Hartman84412f62006-06-14 12:14:34 -0700129{
Greg Kroah-Hartman9bde7492006-06-14 12:14:34 -0700130 struct ep_device *ep = to_ep_device(dev);
Greg Kroah-Hartman84412f62006-06-14 12:14:34 -0700131 char *direction;
132
Julia Lawall2e0fe702008-12-29 11:22:14 +0100133 if (usb_endpoint_xfer_control(ep->desc))
Greg Kroah-Hartman84412f62006-06-14 12:14:34 -0700134 direction = "both";
Julia Lawall2e0fe702008-12-29 11:22:14 +0100135 else if (usb_endpoint_dir_in(ep->desc))
Greg Kroah-Hartman84412f62006-06-14 12:14:34 -0700136 direction = "in";
137 else
138 direction = "out";
139 return sprintf(buf, "%s\n", direction);
140}
Greg Kroah-Hartman9bde7492006-06-14 12:14:34 -0700141static DEVICE_ATTR(direction, S_IRUGO, show_ep_direction, NULL);
Greg Kroah-Hartman84412f62006-06-14 12:14:34 -0700142
Greg Kroah-Hartman9bde7492006-06-14 12:14:34 -0700143static struct attribute *ep_dev_attrs[] = {
144 &dev_attr_bLength.attr,
145 &dev_attr_bEndpointAddress.attr,
146 &dev_attr_bmAttributes.attr,
147 &dev_attr_bInterval.attr,
148 &dev_attr_wMaxPacketSize.attr,
149 &dev_attr_interval.attr,
150 &dev_attr_type.attr,
151 &dev_attr_direction.attr,
Greg Kroah-Hartman84412f62006-06-14 12:14:34 -0700152 NULL,
153};
Greg Kroah-Hartman9bde7492006-06-14 12:14:34 -0700154static struct attribute_group ep_dev_attr_grp = {
155 .attrs = ep_dev_attrs,
Greg Kroah-Hartman84412f62006-06-14 12:14:34 -0700156};
David Brownella4dbd672009-06-24 10:06:31 -0700157static const struct attribute_group *ep_dev_groups[] = {
Alan Stern2e5f10e2008-04-30 15:37:19 -0400158 &ep_dev_attr_grp,
159 NULL
160};
Greg Kroah-Hartman84412f62006-06-14 12:14:34 -0700161
Greg Kroah-Hartman9bde7492006-06-14 12:14:34 -0700162static void ep_device_release(struct device *dev)
163{
164 struct ep_device *ep_dev = to_ep_device(dev);
165
Greg Kroah-Hartman9bde7492006-06-14 12:14:34 -0700166 kfree(ep_dev);
167}
Greg Kroah-Hartman84412f62006-06-14 12:14:34 -0700168
Alan Stern3b23dd62008-12-05 14:10:34 -0500169int usb_create_ep_devs(struct device *parent,
Greg Kroah-Hartman1b21d5e2006-08-28 11:43:25 -0700170 struct usb_host_endpoint *endpoint,
171 struct usb_device *udev)
Greg Kroah-Hartman84412f62006-06-14 12:14:34 -0700172{
Greg Kroah-Hartman9bde7492006-06-14 12:14:34 -0700173 struct ep_device *ep_dev;
Greg Kroah-Hartman9bde7492006-06-14 12:14:34 -0700174 int retval;
Greg Kroah-Hartman84412f62006-06-14 12:14:34 -0700175
Greg Kroah-Hartman9bde7492006-06-14 12:14:34 -0700176 ep_dev = kzalloc(sizeof(*ep_dev), GFP_KERNEL);
177 if (!ep_dev) {
178 retval = -ENOMEM;
Kay Sievers55129662009-05-04 19:48:32 +0200179 goto exit;
Sarah Bailey7e277802006-11-18 22:30:16 -0800180 }
Greg Kroah-Hartman84412f62006-06-14 12:14:34 -0700181
Greg Kroah-Hartman9bde7492006-06-14 12:14:34 -0700182 ep_dev->desc = &endpoint->desc;
183 ep_dev->udev = udev;
Alan Stern2e5f10e2008-04-30 15:37:19 -0400184 ep_dev->dev.groups = ep_dev_groups;
Kay Sievers55129662009-05-04 19:48:32 +0200185 ep_dev->dev.type = &usb_ep_device_type;
Greg Kroah-Hartman9bde7492006-06-14 12:14:34 -0700186 ep_dev->dev.parent = parent;
187 ep_dev->dev.release = ep_device_release;
Kay Sievers55129662009-05-04 19:48:32 +0200188 dev_set_name(&ep_dev->dev, "ep_%02x", endpoint->desc.bEndpointAddress);
Greg Kroah-Hartman9bde7492006-06-14 12:14:34 -0700189
190 retval = device_register(&ep_dev->dev);
191 if (retval)
Kay Sievers55129662009-05-04 19:48:32 +0200192 goto error_register;
Greg Kroah-Hartman9bde7492006-06-14 12:14:34 -0700193
Alan Sternd5477c12006-10-10 11:56:26 -0400194 endpoint->ep_dev = ep_dev;
Greg Kroah-Hartman1b21d5e2006-08-28 11:43:25 -0700195 return retval;
196
Alan Sternd5477c12006-10-10 11:56:26 -0400197error_register:
Greg Kroah-Hartman9bde7492006-06-14 12:14:34 -0700198 kfree(ep_dev);
Alan Sternd5477c12006-10-10 11:56:26 -0400199exit:
Greg Kroah-Hartman1b21d5e2006-08-28 11:43:25 -0700200 return retval;
Greg Kroah-Hartman84412f62006-06-14 12:14:34 -0700201}
202
Alan Stern3b23dd62008-12-05 14:10:34 -0500203void usb_remove_ep_devs(struct usb_host_endpoint *endpoint)
Greg Kroah-Hartman84412f62006-06-14 12:14:34 -0700204{
Sarah Bailey7e277802006-11-18 22:30:16 -0800205 struct ep_device *ep_dev = endpoint->ep_dev;
Greg Kroah-Hartman84412f62006-06-14 12:14:34 -0700206
Sarah Bailey7e277802006-11-18 22:30:16 -0800207 if (ep_dev) {
Sarah Bailey7e277802006-11-18 22:30:16 -0800208 device_unregister(&ep_dev->dev);
Greg Kroah-Hartman9bde7492006-06-14 12:14:34 -0700209 endpoint->ep_dev = NULL;
Greg Kroah-Hartman84412f62006-06-14 12:14:34 -0700210 }
Greg Kroah-Hartman84412f62006-06-14 12:14:34 -0700211}