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        { },
  35};
  36MODULE_DEVICE_TABLE (usb, id_table);
  37
  38struct usb_led {
  39        struct usb_device *     udev;
  40        unsigned char           blue;
  41        unsigned char           red;
  42        unsigned char           green;
  43        enum led_type           type;
  44};
  45
  46static void change_color(struct usb_led *led)
  47{
  48        int retval = 0;
  49        unsigned char *buffer;
  50
  51        buffer = kmalloc(8, GFP_KERNEL);
  52        if (!buffer) {
  53                dev_err(&led->udev->dev, "out of memory\n");
  54                return;
  55        }
  56
  57        switch (led->type) {
  58        case DELCOM_VISUAL_SIGNAL_INDICATOR: {
  59                unsigned char color = 0x07;
  60
  61                if (led->blue)
  62                        color &= ~0x04;
  63                if (led->red)
  64                        color &= ~0x02;
  65                if (led->green)
  66                        color &= ~0x01;
  67                dev_dbg(&led->udev->dev,
  68                        "blue = %d, red = %d, green = %d, color = %.2x\n",
  69                        led->blue, led->red, led->green, color);
  70
  71                retval = usb_control_msg(led->udev,
  72                                        usb_sndctrlpipe(led->udev, 0),
  73                                        0x12,
  74                                        0xc8,
  75                                        (0x02 * 0x100) + 0x0a,
  76                                        (0x00 * 0x100) + color,
  77                                        buffer,
  78                                        8,
  79                                        2000);
  80                break;
  81        }
  82
  83        case DREAM_CHEEKY_WEBMAIL_NOTIFIER:
  84                dev_dbg(&led->udev->dev,
  85                        "red = %d, green = %d, blue = %d\n",
  86                        led->red, led->green, led->blue);
  87
  88                buffer[0] = led->red;
  89                buffer[1] = led->green;
  90                buffer[2] = led->blue;
  91                buffer[3] = buffer[4] = buffer[5] = 0;
  92                buffer[6] = 0x1a;
  93                buffer[7] = 0x05;
  94
  95                retval = usb_control_msg(led->udev,
  96                                        usb_sndctrlpipe(led->udev, 0),
  97                                        0x09,
  98                                        0x21,
  99                                        0x200,
 100                                        0,
 101                                        buffer,
 102                                        8,
 103                                        2000);
 104                break;
 105
 106        default:
 107                dev_err(&led->udev->dev, "unknown device type %d\n", led->type);
 108        }
 109
 110        if (retval)
 111                dev_dbg(&led->udev->dev, "retval = %d\n", retval);
 112        kfree(buffer);
 113}
 114
 115#define show_set(value) \
 116static ssize_t show_##value(struct device *dev, struct device_attribute *attr, char *buf)               \
 117{                                                                       \
 118        struct usb_interface *intf = to_usb_interface(dev);             \
 119        struct usb_led *led = usb_get_intfdata(intf);                   \
 120                                                                        \
 121        return sprintf(buf, "%d\n", led->value);                        \
 122}                                                                       \
 123static ssize_t set_##value(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)    \
 124{                                                                       \
 125        struct usb_interface *intf = to_usb_interface(dev);             \
 126        struct usb_led *led = usb_get_intfdata(intf);                   \
 127        int temp = simple_strtoul(buf, NULL, 10);                       \
 128                                                                        \
 129        led->value = temp;                                              \
 130        change_color(led);                                              \
 131        return count;                                                   \
 132}                                                                       \
 133static DEVICE_ATTR(value, S_IRUGO | S_IWUSR, show_##value, set_##value);
 134show_set(blue);
 135show_set(red);
 136show_set(green);
 137
 138static int led_probe(struct usb_interface *interface, const struct usb_device_id *id)
 139{
 140        struct usb_device *udev = interface_to_usbdev(interface);
 141        struct usb_led *dev = NULL;
 142        int retval = -ENOMEM;
 143
 144        dev = kzalloc(sizeof(struct usb_led), GFP_KERNEL);
 145        if (dev == NULL) {
 146                dev_err(&interface->dev, "out of memory\n");
 147                goto error_mem;
 148        }
 149
 150        dev->udev = usb_get_dev(udev);
 151        dev->type = id->driver_info;
 152
 153        usb_set_intfdata (interface, dev);
 154
 155        retval = device_create_file(&interface->dev, &dev_attr_blue);
 156        if (retval)
 157                goto error;
 158        retval = device_create_file(&interface->dev, &dev_attr_red);
 159        if (retval)
 160                goto error;
 161        retval = device_create_file(&interface->dev, &dev_attr_green);
 162        if (retval)
 163                goto error;
 164
 165        if (dev->type == DREAM_CHEEKY_WEBMAIL_NOTIFIER) {
 166                unsigned char *enable;
 167
 168                enable = kmemdup("\x1f\x02\0\x5f\0\0\x1a\x03", 8, GFP_KERNEL);
 169                if (!enable) {
 170                        dev_err(&interface->dev, "out of memory\n");
 171                        retval = -ENOMEM;
 172                        goto error;
 173                }
 174
 175                retval = usb_control_msg(udev,
 176                                        usb_sndctrlpipe(udev, 0),
 177                                        0x09,
 178                                        0x21,
 179                                        0x200,
 180                                        0,
 181                                        enable,
 182                                        8,
 183                                        2000);
 184
 185                kfree(enable);
 186                if (retval != 8)
 187                        goto error;
 188        }
 189
 190        dev_info(&interface->dev, "USB LED device now attached\n");
 191        return 0;
 192
 193error:
 194        device_remove_file(&interface->dev, &dev_attr_blue);
 195        device_remove_file(&interface->dev, &dev_attr_red);
 196        device_remove_file(&interface->dev, &dev_attr_green);
 197        usb_set_intfdata (interface, NULL);
 198        usb_put_dev(dev->udev);
 199        kfree(dev);
 200error_mem:
 201        return retval;
 202}
 203
 204static void led_disconnect(struct usb_interface *interface)
 205{
 206        struct usb_led *dev;
 207
 208        dev = usb_get_intfdata (interface);
 209
 210        device_remove_file(&interface->dev, &dev_attr_blue);
 211        device_remove_file(&interface->dev, &dev_attr_red);
 212        device_remove_file(&interface->dev, &dev_attr_green);
 213
 214        /* first remove the files, then set the pointer to NULL */
 215        usb_set_intfdata (interface, NULL);
 216
 217        usb_put_dev(dev->udev);
 218
 219        kfree(dev);
 220
 221        dev_info(&interface->dev, "USB LED now disconnected\n");
 222}
 223
 224static struct usb_driver led_driver = {
 225        .name =         "usbled",
 226        .probe =        led_probe,
 227        .disconnect =   led_disconnect,
 228        .id_table =     id_table,
 229};
 230
 231static int __init usb_led_init(void)
 232{
 233        int retval = 0;
 234
 235        retval = usb_register(&led_driver);
 236        if (retval)
 237                err("usb_register failed. Error number %d", retval);
 238        return retval;
 239}
 240
 241static void __exit usb_led_exit(void)
 242{
 243        usb_deregister(&led_driver);
 244}
 245
 246module_init (usb_led_init);
 247module_exit (usb_led_exit);
 248
 249MODULE_AUTHOR(DRIVER_AUTHOR);
 250MODULE_DESCRIPTION(DRIVER_DESC);
 251MODULE_LICENSE("GPL");
 252