linux/drivers/usb/misc/usbled.c
<<
>>
Prefs
   1/*
   2 * USB LED driver
   3 *
   4 * Copyright (C) 2004 Greg Kroah-Hartman (greg@kroah.com)
   5 *
   6 *      This program is free software; you can redistribute it and/or
   7 *      modify it under the terms of the GNU General Public License as
   8 *      published by the Free Software Foundation, version 2.
   9 *
  10 */
  11
  12#include <linux/kernel.h>
  13#include <linux/errno.h>
  14#include <linux/init.h>
  15#include <linux/slab.h>
  16#include <linux/module.h>
  17#include <linux/usb.h>
  18
  19
  20#define DRIVER_AUTHOR "Greg Kroah-Hartman, greg@kroah.com"
  21#define DRIVER_DESC "USB LED Driver"
  22
  23enum led_type {
  24        DELCOM_VISUAL_SIGNAL_INDICATOR,
  25        DREAM_CHEEKY_WEBMAIL_NOTIFIER,
  26};
  27
  28/* table of devices that work with this driver */
  29static const struct usb_device_id id_table[] = {
  30        { USB_DEVICE(0x0fc5, 0x1223),
  31                        .driver_info = DELCOM_VISUAL_SIGNAL_INDICATOR },
  32        { USB_DEVICE(0x1d34, 0x0004),
  33                        .driver_info = DREAM_CHEEKY_WEBMAIL_NOTIFIER },
  34        { USB_DEVICE(0x1d34, 0x000a),
  35                        .driver_info = DREAM_CHEEKY_WEBMAIL_NOTIFIER },
  36        { },
  37};
  38MODULE_DEVICE_TABLE(usb, id_table);
  39
  40struct usb_led {
  41        struct usb_device       *udev;
  42        unsigned char           blue;
  43        unsigned char           red;
  44        unsigned char           green;
  45        enum led_type           type;
  46};
  47
  48static void change_color(struct usb_led *led)
  49{
  50        int retval = 0;
  51        unsigned char *buffer;
  52
  53        buffer = kmalloc(8, GFP_KERNEL);
  54        if (!buffer) {
  55                dev_err(&led->udev->dev, "out of memory\n");
  56                return;
  57        }
  58
  59        switch (led->type) {
  60        case DELCOM_VISUAL_SIGNAL_INDICATOR: {
  61                unsigned char color = 0x07;
  62
  63                if (led->blue)
  64                        color &= ~0x04;
  65                if (led->red)
  66                        color &= ~0x02;
  67                if (led->green)
  68                        color &= ~0x01;
  69                dev_dbg(&led->udev->dev,
  70                        "blue = %d, red = %d, green = %d, color = %.2x\n",
  71                        led->blue, led->red, led->green, color);
  72
  73                retval = usb_control_msg(led->udev,
  74                                        usb_sndctrlpipe(led->udev, 0),
  75                                        0x12,
  76                                        0xc8,
  77                                        (0x02 * 0x100) + 0x0a,
  78                                        (0x00 * 0x100) + color,
  79                                        buffer,
  80                                        8,
  81                                        2000);
  82                break;
  83        }
  84
  85        case DREAM_CHEEKY_WEBMAIL_NOTIFIER:
  86                dev_dbg(&led->udev->dev,
  87                        "red = %d, green = %d, blue = %d\n",
  88                        led->red, led->green, led->blue);
  89
  90                buffer[0] = led->red;
  91                buffer[1] = led->green;
  92                buffer[2] = led->blue;
  93                buffer[3] = buffer[4] = buffer[5] = 0;
  94                buffer[6] = 0x1a;
  95                buffer[7] = 0x05;
  96
  97                retval = usb_control_msg(led->udev,
  98                                        usb_sndctrlpipe(led->udev, 0),
  99                                        0x09,
 100                                        0x21,
 101                                        0x200,
 102                                        0,
 103                                        buffer,
 104                                        8,
 105                                        2000);
 106                break;
 107
 108        default:
 109                dev_err(&led->udev->dev, "unknown device type %d\n", led->type);
 110        }
 111
 112        if (retval)
 113                dev_dbg(&led->udev->dev, "retval = %d\n", retval);
 114        kfree(buffer);
 115}
 116
 117#define show_set(value) \
 118static ssize_t show_##value(struct device *dev, struct device_attribute *attr,\
 119                            char *buf)                                  \
 120{                                                                       \
 121        struct usb_interface *intf = to_usb_interface(dev);             \
 122        struct usb_led *led = usb_get_intfdata(intf);                   \
 123                                                                        \
 124        return sprintf(buf, "%d\n", led->value);                        \
 125}                                                                       \
 126static ssize_t set_##value(struct device *dev, struct device_attribute *attr,\
 127                           const char *buf, size_t count)               \
 128{                                                                       \
 129        struct usb_interface *intf = to_usb_interface(dev);             \
 130        struct usb_led *led = usb_get_intfdata(intf);                   \
 131        int temp = simple_strtoul(buf, NULL, 10);                       \
 132                                                                        \
 133        led->value = temp;                                              \
 134        change_color(led);                                              \
 135        return count;                                                   \
 136}                                                                       \
 137static DEVICE_ATTR(value, S_IRUGO | S_IWUSR, show_##value, set_##value);
 138show_set(blue);
 139show_set(red);
 140show_set(green);
 141
 142static int led_probe(struct usb_interface *interface,
 143                     const struct usb_device_id *id)
 144{
 145        struct usb_device *udev = interface_to_usbdev(interface);
 146        struct usb_led *dev = NULL;
 147        int retval = -ENOMEM;
 148
 149        dev = kzalloc(sizeof(struct usb_led), GFP_KERNEL);
 150        if (dev == NULL) {
 151                dev_err(&interface->dev, "out of memory\n");
 152                goto error_mem;
 153        }
 154
 155        dev->udev = usb_get_dev(udev);
 156        dev->type = id->driver_info;
 157
 158        usb_set_intfdata(interface, dev);
 159
 160        retval = device_create_file(&interface->dev, &dev_attr_blue);
 161        if (retval)
 162                goto error;
 163        retval = device_create_file(&interface->dev, &dev_attr_red);
 164        if (retval)
 165                goto error;
 166        retval = device_create_file(&interface->dev, &dev_attr_green);
 167        if (retval)
 168                goto error;
 169
 170        if (dev->type == DREAM_CHEEKY_WEBMAIL_NOTIFIER) {
 171                unsigned char *enable;
 172
 173                enable = kmemdup("\x1f\x02\0\x5f\0\0\x1a\x03", 8, GFP_KERNEL);
 174                if (!enable) {
 175                        dev_err(&interface->dev, "out of memory\n");
 176                        retval = -ENOMEM;
 177                        goto error;
 178                }
 179
 180                retval = usb_control_msg(udev,
 181                                        usb_sndctrlpipe(udev, 0),
 182                                        0x09,
 183                                        0x21,
 184                                        0x200,
 185                                        0,
 186                                        enable,
 187                                        8,
 188                                        2000);
 189
 190                kfree(enable);
 191                if (retval != 8)
 192                        goto error;
 193        }
 194
 195        dev_info(&interface->dev, "USB LED device now attached\n");
 196        return 0;
 197
 198error:
 199        device_remove_file(&interface->dev, &dev_attr_blue);
 200        device_remove_file(&interface->dev, &dev_attr_red);
 201        device_remove_file(&interface->dev, &dev_attr_green);
 202        usb_set_intfdata(interface, NULL);
 203        usb_put_dev(dev->udev);
 204        kfree(dev);
 205error_mem:
 206        return retval;
 207}
 208
 209static void led_disconnect(struct usb_interface *interface)
 210{
 211        struct usb_led *dev;
 212
 213        dev = usb_get_intfdata(interface);
 214
 215        device_remove_file(&interface->dev, &dev_attr_blue);
 216        device_remove_file(&interface->dev, &dev_attr_red);
 217        device_remove_file(&interface->dev, &dev_attr_green);
 218
 219        /* first remove the files, then set the pointer to NULL */
 220        usb_set_intfdata(interface, NULL);
 221
 222        usb_put_dev(dev->udev);
 223
 224        kfree(dev);
 225
 226        dev_info(&interface->dev, "USB LED now disconnected\n");
 227}
 228
 229static struct usb_driver led_driver = {
 230        .name =         "usbled",
 231        .probe =        led_probe,
 232        .disconnect =   led_disconnect,
 233        .id_table =     id_table,
 234};
 235
 236module_usb_driver(led_driver);
 237
 238MODULE_AUTHOR(DRIVER_AUTHOR);
 239MODULE_DESCRIPTION(DRIVER_DESC);
 240MODULE_LICENSE("GPL");
 241