linux/drivers/usb/core/endpoint.c
<<
>>
Prefs
   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/spinlock.h>
  14#include <linux/idr.h>
  15#include <linux/usb.h>
  16#include "usb.h"
  17
  18#define MAX_ENDPOINT_MINORS (64*128*32)
  19static int usb_endpoint_major;
  20static DEFINE_IDR(endpoint_idr);
  21
  22struct ep_device {
  23        struct usb_endpoint_descriptor *desc;
  24        struct usb_device *udev;
  25        struct device dev;
  26        int minor;
  27};
  28#define to_ep_device(_dev) \
  29        container_of(_dev, struct ep_device, dev)
  30
  31struct ep_attribute {
  32        struct attribute attr;
  33        ssize_t (*show)(struct usb_device *,
  34                        struct usb_endpoint_descriptor *, char *);
  35};
  36#define to_ep_attribute(_attr) \
  37        container_of(_attr, struct ep_attribute, attr)
  38
  39#define usb_ep_attr(field, format_string)                       \
  40static ssize_t show_ep_##field(struct device *dev,              \
  41                               struct device_attribute *attr,   \
  42                               char *buf)                       \
  43{                                                               \
  44        struct ep_device *ep = to_ep_device(dev);               \
  45        return sprintf(buf, format_string, ep->desc->field);    \
  46}                                                               \
  47static DEVICE_ATTR(field, S_IRUGO, show_ep_##field, NULL);
  48
  49usb_ep_attr(bLength, "%02x\n")
  50usb_ep_attr(bEndpointAddress, "%02x\n")
  51usb_ep_attr(bmAttributes, "%02x\n")
  52usb_ep_attr(bInterval, "%02x\n")
  53
  54static ssize_t show_ep_wMaxPacketSize(struct device *dev,
  55                                      struct device_attribute *attr, char *buf)
  56{
  57        struct ep_device *ep = to_ep_device(dev);
  58        return sprintf(buf, "%04x\n",
  59                        le16_to_cpu(ep->desc->wMaxPacketSize) & 0x07ff);
  60}
  61static DEVICE_ATTR(wMaxPacketSize, S_IRUGO, show_ep_wMaxPacketSize, NULL);
  62
  63static ssize_t show_ep_type(struct device *dev, struct device_attribute *attr,
  64                            char *buf)
  65{
  66        struct ep_device *ep = to_ep_device(dev);
  67        char *type = "unknown";
  68
  69        switch (ep->desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
  70        case USB_ENDPOINT_XFER_CONTROL:
  71                type = "Control";
  72                break;
  73        case USB_ENDPOINT_XFER_ISOC:
  74                type = "Isoc";
  75                break;
  76        case USB_ENDPOINT_XFER_BULK:
  77                type = "Bulk";
  78                break;
  79        case USB_ENDPOINT_XFER_INT:
  80                type = "Interrupt";
  81                break;
  82        }
  83        return sprintf(buf, "%s\n", type);
  84}
  85static DEVICE_ATTR(type, S_IRUGO, show_ep_type, NULL);
  86
  87static ssize_t show_ep_interval(struct device *dev,
  88                                struct device_attribute *attr, char *buf)
  89{
  90        struct ep_device *ep = to_ep_device(dev);
  91        char unit;
  92        unsigned interval = 0;
  93        unsigned in;
  94
  95        in = (ep->desc->bEndpointAddress & USB_DIR_IN);
  96
  97        switch (ep->desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
  98        case USB_ENDPOINT_XFER_CONTROL:
  99                if (ep->udev->speed == USB_SPEED_HIGH)  /* uframes per NAK */
 100                        interval = ep->desc->bInterval;
 101                break;
 102        case USB_ENDPOINT_XFER_ISOC:
 103                interval = 1 << (ep->desc->bInterval - 1);
 104                break;
 105        case USB_ENDPOINT_XFER_BULK:
 106                if (ep->udev->speed == USB_SPEED_HIGH && !in) /* uframes per NAK */
 107                        interval = ep->desc->bInterval;
 108                break;
 109        case USB_ENDPOINT_XFER_INT:
 110                if (ep->udev->speed == USB_SPEED_HIGH)
 111                        interval = 1 << (ep->desc->bInterval - 1);
 112                else
 113                        interval = ep->desc->bInterval;
 114                break;
 115        }
 116        interval *= (ep->udev->speed == USB_SPEED_HIGH) ? 125 : 1000;
 117        if (interval % 1000)
 118                unit = 'u';
 119        else {
 120                unit = 'm';
 121                interval /= 1000;
 122        }
 123
 124        return sprintf(buf, "%d%cs\n", interval, unit);
 125}
 126static DEVICE_ATTR(interval, S_IRUGO, show_ep_interval, NULL);
 127
 128static ssize_t show_ep_direction(struct device *dev,
 129                                 struct device_attribute *attr, char *buf)
 130{
 131        struct ep_device *ep = to_ep_device(dev);
 132        char *direction;
 133
 134        if ((ep->desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
 135                        USB_ENDPOINT_XFER_CONTROL)
 136                direction = "both";
 137        else if (ep->desc->bEndpointAddress & USB_DIR_IN)
 138                direction = "in";
 139        else
 140                direction = "out";
 141        return sprintf(buf, "%s\n", direction);
 142}
 143static DEVICE_ATTR(direction, S_IRUGO, show_ep_direction, NULL);
 144
 145static struct attribute *ep_dev_attrs[] = {
 146        &dev_attr_bLength.attr,
 147        &dev_attr_bEndpointAddress.attr,
 148        &dev_attr_bmAttributes.attr,
 149        &dev_attr_bInterval.attr,
 150        &dev_attr_wMaxPacketSize.attr,
 151        &dev_attr_interval.attr,
 152        &dev_attr_type.attr,
 153        &dev_attr_direction.attr,
 154        NULL,
 155};
 156static struct attribute_group ep_dev_attr_grp = {
 157        .attrs = ep_dev_attrs,
 158};
 159
 160static int usb_endpoint_major_init(void)
 161{
 162        dev_t dev;
 163        int error;
 164
 165        error = alloc_chrdev_region(&dev, 0, MAX_ENDPOINT_MINORS,
 166                                    "usb_endpoint");
 167        if (error) {
 168                err("unable to get a dynamic major for usb endpoints");
 169                return error;
 170        }
 171        usb_endpoint_major = MAJOR(dev);
 172
 173        return error;
 174}
 175
 176static void usb_endpoint_major_cleanup(void)
 177{
 178        unregister_chrdev_region(MKDEV(usb_endpoint_major, 0),
 179                                 MAX_ENDPOINT_MINORS);
 180}
 181
 182static int endpoint_get_minor(struct ep_device *ep_dev)
 183{
 184        static DEFINE_MUTEX(minor_lock);
 185        int retval = -ENOMEM;
 186        int id;
 187
 188        mutex_lock(&minor_lock);
 189        if (idr_pre_get(&endpoint_idr, GFP_KERNEL) == 0)
 190                goto exit;
 191
 192        retval = idr_get_new(&endpoint_idr, ep_dev, &id);
 193        if (retval < 0) {
 194                if (retval == -EAGAIN)
 195                        retval = -ENOMEM;
 196                goto exit;
 197        }
 198        ep_dev->minor = id & MAX_ID_MASK;
 199exit:
 200        mutex_unlock(&minor_lock);
 201        return retval;
 202}
 203
 204static void endpoint_free_minor(struct ep_device *ep_dev)
 205{
 206        idr_remove(&endpoint_idr, ep_dev->minor);
 207}
 208
 209static struct endpoint_class {
 210        struct kref kref;
 211        struct class *class;
 212} *ep_class;
 213
 214static int init_endpoint_class(void)
 215{
 216        int result = 0;
 217
 218        if (ep_class != NULL) {
 219                kref_get(&ep_class->kref);
 220                goto exit;
 221        }
 222
 223        ep_class = kmalloc(sizeof(*ep_class), GFP_KERNEL);
 224        if (!ep_class) {
 225                result = -ENOMEM;
 226                goto exit;
 227        }
 228
 229        kref_init(&ep_class->kref);
 230        ep_class->class = class_create(THIS_MODULE, "usb_endpoint");
 231        if (IS_ERR(ep_class->class)) {
 232                result = PTR_ERR(ep_class->class);
 233                goto class_create_error;
 234        }
 235
 236        result = usb_endpoint_major_init();
 237        if (result)
 238                goto endpoint_major_error;
 239
 240        goto exit;
 241
 242endpoint_major_error:
 243        class_destroy(ep_class->class);
 244class_create_error:
 245        kfree(ep_class);
 246        ep_class = NULL;
 247exit:
 248        return result;
 249}
 250
 251static void release_endpoint_class(struct kref *kref)
 252{
 253        /* Ok, we cheat as we know we only have one ep_class */
 254        class_destroy(ep_class->class);
 255        kfree(ep_class);
 256        ep_class = NULL;
 257        usb_endpoint_major_cleanup();
 258}
 259
 260static void destroy_endpoint_class(void)
 261{
 262        if (ep_class)
 263                kref_put(&ep_class->kref, release_endpoint_class);
 264}
 265
 266static void ep_device_release(struct device *dev)
 267{
 268        struct ep_device *ep_dev = to_ep_device(dev);
 269
 270        endpoint_free_minor(ep_dev);
 271        kfree(ep_dev);
 272}
 273
 274int usb_create_ep_files(struct device *parent,
 275                        struct usb_host_endpoint *endpoint,
 276                        struct usb_device *udev)
 277{
 278        char name[8];
 279        struct ep_device *ep_dev;
 280        int retval;
 281
 282        retval = init_endpoint_class();
 283        if (retval)
 284                goto exit;
 285
 286        ep_dev = kzalloc(sizeof(*ep_dev), GFP_KERNEL);
 287        if (!ep_dev) {
 288                retval = -ENOMEM;
 289                goto error_alloc;
 290        }
 291
 292        retval = endpoint_get_minor(ep_dev);
 293        if (retval) {
 294                dev_err(parent, "can not allocate minor number for %s\n",
 295                        ep_dev->dev.bus_id);
 296                goto error_register;
 297        }
 298
 299        ep_dev->desc = &endpoint->desc;
 300        ep_dev->udev = udev;
 301        ep_dev->dev.devt = MKDEV(usb_endpoint_major, ep_dev->minor);
 302        ep_dev->dev.class = ep_class->class;
 303        ep_dev->dev.parent = parent;
 304        ep_dev->dev.release = ep_device_release;
 305        snprintf(ep_dev->dev.bus_id, BUS_ID_SIZE, "usbdev%d.%d_ep%02x",
 306                 udev->bus->busnum, udev->devnum,
 307                 endpoint->desc.bEndpointAddress);
 308
 309        retval = device_register(&ep_dev->dev);
 310        if (retval)
 311                goto error_chrdev;
 312        retval = sysfs_create_group(&ep_dev->dev.kobj, &ep_dev_attr_grp);
 313        if (retval)
 314                goto error_group;
 315
 316        /* create the symlink to the old-style "ep_XX" directory */
 317        sprintf(name, "ep_%02x", endpoint->desc.bEndpointAddress);
 318        retval = sysfs_create_link(&parent->kobj, &ep_dev->dev.kobj, name);
 319        if (retval)
 320                goto error_link;
 321        endpoint->ep_dev = ep_dev;
 322        return retval;
 323
 324error_link:
 325        sysfs_remove_group(&ep_dev->dev.kobj, &ep_dev_attr_grp);
 326error_group:
 327        device_unregister(&ep_dev->dev);
 328        destroy_endpoint_class();
 329        return retval;
 330
 331error_chrdev:
 332        endpoint_free_minor(ep_dev);
 333
 334error_register:
 335        kfree(ep_dev);
 336error_alloc:
 337        destroy_endpoint_class();
 338exit:
 339        return retval;
 340}
 341
 342void usb_remove_ep_files(struct usb_host_endpoint *endpoint)
 343{
 344        struct ep_device *ep_dev = endpoint->ep_dev;
 345
 346        if (ep_dev) {
 347                char name[8];
 348
 349                sprintf(name, "ep_%02x", endpoint->desc.bEndpointAddress);
 350                sysfs_remove_link(&ep_dev->dev.parent->kobj, name);
 351                sysfs_remove_group(&ep_dev->dev.kobj, &ep_dev_attr_grp);
 352                device_unregister(&ep_dev->dev);
 353                endpoint->ep_dev = NULL;
 354                destroy_endpoint_class();
 355        }
 356}
 357