Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
at v2.6.19 285 lines 7.0 kB view raw
1/* 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> 13#include <linux/usb.h> 14#include "usb.h" 15 16/* endpoint stuff */ 17 18struct ep_device { 19 struct usb_endpoint_descriptor *desc; 20 struct usb_device *udev; 21 struct device dev; 22}; 23#define to_ep_device(_dev) \ 24 container_of(_dev, struct ep_device, dev) 25 26struct ep_attribute { 27 struct attribute attr; 28 ssize_t (*show)(struct usb_device *, 29 struct usb_endpoint_descriptor *, char *); 30}; 31#define to_ep_attribute(_attr) \ 32 container_of(_attr, struct ep_attribute, attr) 33 34#define usb_ep_attr(field, format_string) \ 35static ssize_t show_ep_##field(struct device *dev, \ 36 struct device_attribute *attr, \ 37 char *buf) \ 38{ \ 39 struct ep_device *ep = to_ep_device(dev); \ 40 return sprintf(buf, format_string, ep->desc->field); \ 41} \ 42static DEVICE_ATTR(field, S_IRUGO, show_ep_##field, NULL); 43 44usb_ep_attr(bLength, "%02x\n") 45usb_ep_attr(bEndpointAddress, "%02x\n") 46usb_ep_attr(bmAttributes, "%02x\n") 47usb_ep_attr(bInterval, "%02x\n") 48 49static ssize_t show_ep_wMaxPacketSize(struct device *dev, 50 struct device_attribute *attr, char *buf) 51{ 52 struct ep_device *ep = to_ep_device(dev); 53 return sprintf(buf, "%04x\n", 54 le16_to_cpu(ep->desc->wMaxPacketSize) & 0x07ff); 55} 56static DEVICE_ATTR(wMaxPacketSize, S_IRUGO, show_ep_wMaxPacketSize, NULL); 57 58static ssize_t show_ep_type(struct device *dev, struct device_attribute *attr, 59 char *buf) 60{ 61 struct ep_device *ep = to_ep_device(dev); 62 char *type = "unknown"; 63 64 switch (ep->desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) { 65 case USB_ENDPOINT_XFER_CONTROL: 66 type = "Control"; 67 break; 68 case USB_ENDPOINT_XFER_ISOC: 69 type = "Isoc"; 70 break; 71 case USB_ENDPOINT_XFER_BULK: 72 type = "Bulk"; 73 break; 74 case USB_ENDPOINT_XFER_INT: 75 type = "Interrupt"; 76 break; 77 } 78 return sprintf(buf, "%s\n", type); 79} 80static DEVICE_ATTR(type, S_IRUGO, show_ep_type, NULL); 81 82static ssize_t show_ep_interval(struct device *dev, 83 struct device_attribute *attr, char *buf) 84{ 85 struct ep_device *ep = to_ep_device(dev); 86 char unit; 87 unsigned interval = 0; 88 unsigned in; 89 90 in = (ep->desc->bEndpointAddress & USB_DIR_IN); 91 92 switch (ep->desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) { 93 case USB_ENDPOINT_XFER_CONTROL: 94 if (ep->udev->speed == USB_SPEED_HIGH) /* uframes per NAK */ 95 interval = ep->desc->bInterval; 96 break; 97 case USB_ENDPOINT_XFER_ISOC: 98 interval = 1 << (ep->desc->bInterval - 1); 99 break; 100 case USB_ENDPOINT_XFER_BULK: 101 if (ep->udev->speed == USB_SPEED_HIGH && !in) /* uframes per NAK */ 102 interval = ep->desc->bInterval; 103 break; 104 case USB_ENDPOINT_XFER_INT: 105 if (ep->udev->speed == USB_SPEED_HIGH) 106 interval = 1 << (ep->desc->bInterval - 1); 107 else 108 interval = ep->desc->bInterval; 109 break; 110 } 111 interval *= (ep->udev->speed == USB_SPEED_HIGH) ? 125 : 1000; 112 if (interval % 1000) 113 unit = 'u'; 114 else { 115 unit = 'm'; 116 interval /= 1000; 117 } 118 119 return sprintf(buf, "%d%cs\n", interval, unit); 120} 121static DEVICE_ATTR(interval, S_IRUGO, show_ep_interval, NULL); 122 123static ssize_t show_ep_direction(struct device *dev, 124 struct device_attribute *attr, char *buf) 125{ 126 struct ep_device *ep = to_ep_device(dev); 127 char *direction; 128 129 if ((ep->desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == 130 USB_ENDPOINT_XFER_CONTROL) 131 direction = "both"; 132 else if (ep->desc->bEndpointAddress & USB_DIR_IN) 133 direction = "in"; 134 else 135 direction = "out"; 136 return sprintf(buf, "%s\n", direction); 137} 138static DEVICE_ATTR(direction, S_IRUGO, show_ep_direction, NULL); 139 140static struct attribute *ep_dev_attrs[] = { 141 &dev_attr_bLength.attr, 142 &dev_attr_bEndpointAddress.attr, 143 &dev_attr_bmAttributes.attr, 144 &dev_attr_bInterval.attr, 145 &dev_attr_wMaxPacketSize.attr, 146 &dev_attr_interval.attr, 147 &dev_attr_type.attr, 148 &dev_attr_direction.attr, 149 NULL, 150}; 151static struct attribute_group ep_dev_attr_grp = { 152 .attrs = ep_dev_attrs, 153}; 154 155static struct endpoint_class { 156 struct kref kref; 157 struct class *class; 158} *ep_class; 159 160static int init_endpoint_class(void) 161{ 162 int result = 0; 163 164 if (ep_class != NULL) { 165 kref_get(&ep_class->kref); 166 goto exit; 167 } 168 169 ep_class = kmalloc(sizeof(*ep_class), GFP_KERNEL); 170 if (!ep_class) { 171 result = -ENOMEM; 172 goto exit; 173 } 174 175 kref_init(&ep_class->kref); 176 ep_class->class = class_create(THIS_MODULE, "usb_endpoint"); 177 if (IS_ERR(ep_class->class)) { 178 result = IS_ERR(ep_class->class); 179 kfree(ep_class); 180 ep_class = NULL; 181 goto exit; 182 } 183 184exit: 185 return result; 186} 187 188static void release_endpoint_class(struct kref *kref) 189{ 190 /* Ok, we cheat as we know we only have one ep_class */ 191 class_destroy(ep_class->class); 192 kfree(ep_class); 193 ep_class = NULL; 194} 195 196static void destroy_endpoint_class(void) 197{ 198 if (ep_class) 199 kref_put(&ep_class->kref, release_endpoint_class); 200} 201 202static void ep_device_release(struct device *dev) 203{ 204 struct ep_device *ep_dev = to_ep_device(dev); 205 206 dev_dbg(dev, "%s called for %s\n", __FUNCTION__, dev->bus_id); 207 kfree(ep_dev); 208} 209 210int usb_create_ep_files(struct device *parent, 211 struct usb_host_endpoint *endpoint, 212 struct usb_device *udev) 213{ 214 char name[8]; 215 struct ep_device *ep_dev; 216 int minor; 217 int retval; 218 219 retval = init_endpoint_class(); 220 if (retval) 221 goto exit; 222 223 ep_dev = kzalloc(sizeof(*ep_dev), GFP_KERNEL); 224 if (!ep_dev) { 225 retval = -ENOMEM; 226 goto error_alloc; 227 } 228 229 /* fun calculation to determine the minor of this endpoint */ 230 minor = (((udev->bus->busnum - 1) * 128) * 16) + (udev->devnum - 1); 231 232 ep_dev->desc = &endpoint->desc; 233 ep_dev->udev = udev; 234 ep_dev->dev.devt = MKDEV(442, minor); // FIXME fake number... 235 ep_dev->dev.class = ep_class->class; 236 ep_dev->dev.parent = parent; 237 ep_dev->dev.release = ep_device_release; 238 snprintf(ep_dev->dev.bus_id, BUS_ID_SIZE, "usbdev%d.%d_ep%02x", 239 udev->bus->busnum, udev->devnum, 240 endpoint->desc.bEndpointAddress); 241 242 retval = device_register(&ep_dev->dev); 243 if (retval) 244 goto error_register; 245 retval = sysfs_create_group(&ep_dev->dev.kobj, &ep_dev_attr_grp); 246 if (retval) 247 goto error_group; 248 249 /* create the symlink to the old-style "ep_XX" directory */ 250 sprintf(name, "ep_%02x", endpoint->desc.bEndpointAddress); 251 retval = sysfs_create_link(&parent->kobj, &ep_dev->dev.kobj, name); 252 if (retval) 253 goto error_link; 254 endpoint->ep_dev = ep_dev; 255 return retval; 256 257error_link: 258 sysfs_remove_group(&ep_dev->dev.kobj, &ep_dev_attr_grp); 259error_group: 260 device_unregister(&ep_dev->dev); 261 destroy_endpoint_class(); 262 return retval; 263 264error_register: 265 kfree(ep_dev); 266error_alloc: 267 destroy_endpoint_class(); 268exit: 269 return retval; 270} 271 272void usb_remove_ep_files(struct usb_host_endpoint *endpoint) 273{ 274 275 if (endpoint->ep_dev) { 276 char name[8]; 277 278 sprintf(name, "ep_%02x", endpoint->desc.bEndpointAddress); 279 sysfs_remove_link(&endpoint->ep_dev->dev.parent->kobj, name); 280 sysfs_remove_group(&endpoint->ep_dev->dev.kobj, &ep_dev_attr_grp); 281 device_unregister(&endpoint->ep_dev->dev); 282 endpoint->ep_dev = NULL; 283 destroy_endpoint_class(); 284 } 285}