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/slab.h>
  15#include <linux/module.h>
  16#include <linux/usb.h>
  17
  18
  19#define DRIVER_AUTHOR "Greg Kroah-Hartman, greg@kroah.com"
  20#define DRIVER_DESC "USB LED Driver"
  21
  22enum led_type {
  23        DELCOM_VISUAL_SIGNAL_INDICATOR,
  24        DREAM_CHEEKY_WEBMAIL_NOTIFIER,
  25        RISO_KAGAKU_LED
  26};
  27
  28/* the Webmail LED made by RISO KAGAKU CORP. decodes a color index
  29   internally, we want to keep the red+green+blue sysfs api, so we decode
  30   from 1-bit RGB to the riso kagaku color index according to this table... */
  31
  32static unsigned const char riso_kagaku_tbl[] = {
  33/* R+2G+4B -> riso kagaku color index */
  34        [0] = 0, /* black   */
  35        [1] = 2, /* red     */
  36        [2] = 1, /* green   */
  37        [3] = 5, /* yellow  */
  38        [4] = 3, /* blue    */
  39        [5] = 6, /* magenta */
  40        [6] = 4, /* cyan    */
  41        [7] = 7  /* white   */
  42};
  43
  44#define RISO_KAGAKU_IX(r,g,b) riso_kagaku_tbl[((r)?1:0)+((g)?2:0)+((b)?4:0)]
  45
  46/* table of devices that work with this driver */
  47static const struct usb_device_id id_table[] = {
  48        { USB_DEVICE(0x0fc5, 0x1223),
  49                        .driver_info = DELCOM_VISUAL_SIGNAL_INDICATOR },
  50        { USB_DEVICE(0x1d34, 0x0004),
  51                        .driver_info = DREAM_CHEEKY_WEBMAIL_NOTIFIER },
  52        { USB_DEVICE(0x1d34, 0x000a),
  53                        .driver_info = DREAM_CHEEKY_WEBMAIL_NOTIFIER },
  54        { USB_DEVICE(0x1294, 0x1320),
  55                        .driver_info = RISO_KAGAKU_LED },
  56        { },
  57};
  58MODULE_DEVICE_TABLE(usb, id_table);
  59
  60struct usb_led {
  61        struct usb_device       *udev;
  62        unsigned char           blue;
  63        unsigned char           red;
  64        unsigned char           green;
  65        enum led_type           type;
  66};
  67
  68static void change_color(struct usb_led *led)
  69{
  70        int retval = 0;
  71        unsigned char *buffer;
  72        int actlength;
  73
  74        buffer = kmalloc(8, GFP_KERNEL);
  75        if (!buffer) {
  76                dev_err(&led->udev->dev, "out of memory\n");
  77                return;
  78        }
  79
  80        switch (led->type) {
  81        case DELCOM_VISUAL_SIGNAL_INDICATOR: {
  82                unsigned char color = 0x07;
  83
  84                if (led->blue)
  85                        color &= ~0x04;
  86                if (led->red)
  87                        color &= ~0x02;
  88                if (led->green)
  89                        color &= ~0x01;
  90                dev_dbg(&led->udev->dev,
  91                        "blue = %d, red = %d, green = %d, color = %.2x\n",
  92                        led->blue, led->red, led->green, color);
  93
  94                retval = usb_control_msg(led->udev,
  95                                        usb_sndctrlpipe(led->udev, 0),
  96                                        0x12,
  97                                        0xc8,
  98                                        (0x02 * 0x100) + 0x0a,
  99                                        (0x00 * 0x100) + color,
 100                                        buffer,
 101                                        8,
 102                                        2000);
 103                break;
 104        }
 105
 106        case DREAM_CHEEKY_WEBMAIL_NOTIFIER:
 107                dev_dbg(&led->udev->dev,
 108                        "red = %d, green = %d, blue = %d\n",
 109                        led->red, led->green, led->blue);
 110
 111                buffer[0] = led->red;
 112                buffer[1] = led->green;
 113                buffer[2] = led->blue;
 114                buffer[3] = buffer[4] = buffer[5] = 0;
 115                buffer[6] = 0x1a;
 116                buffer[7] = 0x05;
 117
 118                retval = usb_control_msg(led->udev,
 119                                        usb_sndctrlpipe(led->udev, 0),
 120                                        0x09,
 121                                        0x21,
 122                                        0x200,
 123                                        0,
 124                                        buffer,
 125                                        8,
 126                                        2000);
 127                break;
 128
 129        case RISO_KAGAKU_LED:
 130                buffer[0] = RISO_KAGAKU_IX(led->red, led->green, led->blue);
 131                buffer[1] = 0;
 132                buffer[2] = 0;
 133                buffer[3] = 0;
 134                buffer[4] = 0;
 135
 136                retval = usb_interrupt_msg(led->udev,
 137                        usb_sndctrlpipe(led->udev, 2),
 138                        buffer, 5, &actlength, 1000 /*ms timeout*/);
 139                break;
 140
 141        default:
 142                dev_err(&led->udev->dev, "unknown device type %d\n", led->type);
 143        }
 144
 145        if (retval)
 146                dev_dbg(&led->udev->dev, "retval = %d\n", retval);
 147        kfree(buffer);
 148}
 149
 150#define show_set(value) \
 151static ssize_t show_##value(struct device *dev, struct device_attribute *attr,\
 152                            char *buf)                                  \
 153{                                                                       \
 154        struct usb_interface *intf = to_usb_interface(dev);             \
 155        struct usb_led *led = usb_get_intfdata(intf);                   \
 156                                                                        \
 157        return sprintf(buf, "%d\n", led->value);                        \
 158}                                                                       \
 159static ssize_t set_##value(struct device *dev, struct device_attribute *attr,\
 160                           const char *buf, size_t count)               \
 161{                                                                       \
 162        struct usb_interface *intf = to_usb_interface(dev);             \
 163        struct usb_led *led = usb_get_intfdata(intf);                   \
 164        int temp = simple_strtoul(buf, NULL, 10);                       \
 165                                                                        \
 166        led->value = temp;                                              \
 167        change_color(led);                                              \
 168        return count;                                                   \
 169}                                                                       \
 170static DEVICE_ATTR(value, S_IRUGO | S_IWUSR, show_##value, set_##value);
 171show_set(blue);
 172show_set(red);
 173show_set(green);
 174
 175static int led_probe(struct usb_interface *interface,
 176                     const struct usb_device_id *id)
 177{
 178        struct usb_device *udev = interface_to_usbdev(interface);
 179        struct usb_led *dev = NULL;
 180        int retval = -ENOMEM;
 181
 182        dev = kzalloc(sizeof(struct usb_led), GFP_KERNEL);
 183        if (dev == NULL) {
 184                dev_err(&interface->dev, "out of memory\n");
 185                goto error_mem;
 186        }
 187
 188        dev->udev = usb_get_dev(udev);
 189        dev->type = id->driver_info;
 190
 191        usb_set_intfdata(interface, dev);
 192
 193        retval = device_create_file(&interface->dev, &dev_attr_blue);
 194        if (retval)
 195                goto error;
 196        retval = device_create_file(&interface->dev, &dev_attr_red);
 197        if (retval)
 198                goto error;
 199        retval = device_create_file(&interface->dev, &dev_attr_green);
 200        if (retval)
 201                goto error;
 202
 203        if (dev->type == DREAM_CHEEKY_WEBMAIL_NOTIFIER) {
 204                unsigned char *enable;
 205
 206                enable = kmemdup("\x1f\x02\0\x5f\0\0\x1a\x03", 8, GFP_KERNEL);
 207                if (!enable) {
 208                        dev_err(&interface->dev, "out of memory\n");
 209                        retval = -ENOMEM;
 210                        goto error;
 211                }
 212
 213                retval = usb_control_msg(udev,
 214                                        usb_sndctrlpipe(udev, 0),
 215                                        0x09,
 216                                        0x21,
 217                                        0x200,
 218                                        0,
 219                                        enable,
 220                                        8,
 221                                        2000);
 222
 223                kfree(enable);
 224                if (retval != 8)
 225                        goto error;
 226        }
 227
 228        dev_info(&interface->dev, "USB LED device now attached\n");
 229        return 0;
 230
 231error:
 232        device_remove_file(&interface->dev, &dev_attr_blue);
 233        device_remove_file(&interface->dev, &dev_attr_red);
 234        device_remove_file(&interface->dev, &dev_attr_green);
 235        usb_set_intfdata(interface, NULL);
 236        usb_put_dev(dev->udev);
 237        kfree(dev);
 238error_mem:
 239        return retval;
 240}
 241
 242static void led_disconnect(struct usb_interface *interface)
 243{
 244        struct usb_led *dev;
 245
 246        dev = usb_get_intfdata(interface);
 247
 248        device_remove_file(&interface->dev, &dev_attr_blue);
 249        device_remove_file(&interface->dev, &dev_attr_red);
 250        device_remove_file(&interface->dev, &dev_attr_green);
 251
 252        /* first remove the files, then set the pointer to NULL */
 253        usb_set_intfdata(interface, NULL);
 254
 255        usb_put_dev(dev->udev);
 256
 257        kfree(dev);
 258
 259        dev_info(&interface->dev, "USB LED now disconnected\n");
 260}
 261
 262static struct usb_driver led_driver = {
 263        .name =         "usbled",
 264        .probe =        led_probe,
 265        .disconnect =   led_disconnect,
 266        .id_table =     id_table,
 267};
 268
 269module_usb_driver(led_driver);
 270
 271MODULE_AUTHOR(DRIVER_AUTHOR);
 272MODULE_DESCRIPTION(DRIVER_DESC);
 273MODULE_LICENSE("GPL");
 274