usbled.c 5.6 KB

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