ledtrig-morse.c 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366
  1. /*
  2. * LED Morse Trigger
  3. *
  4. * Copyright (C) 2007 Gabor Juhos <juhosg at openwrt.org>
  5. *
  6. * This file was based on: drivers/led/ledtrig-timer.c
  7. * Copyright 2005-2006 Openedhand Ltd.
  8. * Author: Richard Purdie <rpurdie@openedhand.com>
  9. *
  10. * also based on the patch '[PATCH] 2.5.59 morse code panics' posted
  11. * in the LKML by Tomas Szepe at Thu, 30 Jan 2003
  12. * Copyright (C) 2002 Andrew Rodland <arodland@noln.com>
  13. * Copyright (C) 2003 Tomas Szepe <szepe@pinerecords.com>
  14. *
  15. * This program is free software; you can redistribute it and/or modify it
  16. * under the terms of the GNU General Public License version 2 as published
  17. * by the Free Software Foundation.
  18. *
  19. */
  20. #include <linux/kernel.h>
  21. #include <linux/version.h>
  22. #include <linux/module.h>
  23. #include <linux/jiffies.h>
  24. #include <linux/init.h>
  25. #include <linux/list.h>
  26. #include <linux/spinlock.h>
  27. #include <linux/device.h>
  28. #include <linux/sysdev.h>
  29. #include <linux/timer.h>
  30. #include <linux/ctype.h>
  31. #include <linux/leds.h>
  32. #include <linux/slab.h>
  33. #include "leds.h"
  34. #define MORSE_DELAY_BASE (HZ/2)
  35. #define MORSE_STATE_BLINK_START 0
  36. #define MORSE_STATE_BLINK_STOP 1
  37. #define MORSE_DIT_LEN 1
  38. #define MORSE_DAH_LEN 3
  39. #define MORSE_SPACE_LEN 7
  40. struct morse_trig_data {
  41. unsigned long delay;
  42. char *msg;
  43. unsigned char morse;
  44. unsigned char state;
  45. char *msgpos;
  46. struct timer_list timer;
  47. };
  48. const unsigned char morsetable[] = {
  49. 0122, 0, 0310, 0, 0, 0163, /* "#$%&' */
  50. 055, 0155, 0, 0, 0163, 0141, 0152, 0051, /* ()*+,-./ */
  51. 077, 076, 074, 070, 060, 040, 041, 043, 047, 057, /* 0-9 */
  52. 0107, 0125, 0, 0061, 0, 0114, 0, /* :;<=>?@ */
  53. 006, 021, 025, 011, 002, 024, 013, 020, 004, /* A-I */
  54. 036, 015, 022, 007, 005, 017, 026, 033, 012, /* J-R */
  55. 010, 003, 014, 030, 016, 031, 035, 023, /* S-Z */
  56. 0, 0, 0, 0, 0154 /* [\]^_ */
  57. };
  58. static inline unsigned char tomorse(char c) {
  59. if (c >= 'a' && c <= 'z')
  60. c = c - 'a' + 'A';
  61. if (c >= '"' && c <= '_') {
  62. return morsetable[c - '"'];
  63. } else
  64. return 0;
  65. }
  66. static inline unsigned long dit_len(struct morse_trig_data *morse_data)
  67. {
  68. return MORSE_DIT_LEN*morse_data->delay;
  69. }
  70. static inline unsigned long dah_len(struct morse_trig_data *morse_data)
  71. {
  72. return MORSE_DAH_LEN*morse_data->delay;
  73. }
  74. static inline unsigned long space_len(struct morse_trig_data *morse_data)
  75. {
  76. return MORSE_SPACE_LEN*morse_data->delay;
  77. }
  78. static void morse_timer_function(unsigned long data)
  79. {
  80. struct led_classdev *led_cdev = (struct led_classdev *)data;
  81. struct morse_trig_data *morse_data = led_cdev->trigger_data;
  82. unsigned long brightness = LED_OFF;
  83. unsigned long delay = 0;
  84. if (!morse_data->msg)
  85. goto set_led;
  86. switch (morse_data->state) {
  87. case MORSE_STATE_BLINK_START:
  88. /* Starting a new blink. We have a valid code in morse. */
  89. delay = (morse_data->morse & 001) ? dah_len(morse_data):
  90. dit_len(morse_data);
  91. brightness = LED_FULL;
  92. morse_data->state = MORSE_STATE_BLINK_STOP;
  93. morse_data->morse >>= 1;
  94. break;
  95. case MORSE_STATE_BLINK_STOP:
  96. /* Coming off of a blink. */
  97. morse_data->state = MORSE_STATE_BLINK_START;
  98. if (morse_data->morse > 1) {
  99. /* Not done yet, just a one-dit pause. */
  100. delay = dit_len(morse_data);
  101. break;
  102. }
  103. /* Get a new char, figure out how much space. */
  104. /* First time through */
  105. if (!morse_data->msgpos)
  106. morse_data->msgpos = (char *)morse_data->msg;
  107. if (!*morse_data->msgpos) {
  108. /* Repeating */
  109. morse_data->msgpos = (char *)morse_data->msg;
  110. delay = space_len(morse_data);
  111. } else {
  112. /* Inter-letter space */
  113. delay = dah_len(morse_data);
  114. }
  115. if (!(morse_data->morse = tomorse(*morse_data->msgpos))) {
  116. delay = space_len(morse_data);
  117. /* And get us back here */
  118. morse_data->state = MORSE_STATE_BLINK_STOP;
  119. }
  120. morse_data->msgpos++;
  121. break;
  122. }
  123. mod_timer(&morse_data->timer, jiffies + msecs_to_jiffies(delay));
  124. set_led:
  125. led_set_brightness(led_cdev, brightness);
  126. }
  127. static ssize_t _morse_delay_show(struct led_classdev *led_cdev, char *buf)
  128. {
  129. struct morse_trig_data *morse_data = led_cdev->trigger_data;
  130. sprintf(buf, "%lu\n", morse_data->delay);
  131. return strlen(buf) + 1;
  132. }
  133. static ssize_t _morse_delay_store(struct led_classdev *led_cdev,
  134. const char *buf, size_t size)
  135. {
  136. struct morse_trig_data *morse_data = led_cdev->trigger_data;
  137. char *after;
  138. unsigned long state = simple_strtoul(buf, &after, 10);
  139. size_t count = after - buf;
  140. int ret = -EINVAL;
  141. if (*after && isspace(*after))
  142. count++;
  143. if (count == size) {
  144. morse_data->delay = state;
  145. mod_timer(&morse_data->timer, jiffies + 1);
  146. ret = count;
  147. }
  148. return ret;
  149. }
  150. static ssize_t _morse_msg_show(struct led_classdev *led_cdev, char *buf)
  151. {
  152. struct morse_trig_data *morse_data = led_cdev->trigger_data;
  153. if (!morse_data->msg)
  154. sprintf(buf, "<none>\n");
  155. else
  156. sprintf(buf, "%s\n", morse_data->msg);
  157. return strlen(buf) + 1;
  158. }
  159. static ssize_t _morse_msg_store(struct led_classdev *led_cdev,
  160. const char *buf, size_t size)
  161. {
  162. struct morse_trig_data *morse_data = led_cdev->trigger_data;
  163. char *m;
  164. m = kmalloc(size, GFP_KERNEL);
  165. if (!m)
  166. return -ENOMEM;
  167. memcpy(m,buf,size);
  168. m[size]='\0';
  169. if (morse_data->msg)
  170. kfree(morse_data->msg);
  171. morse_data->msg = m;
  172. morse_data->msgpos = NULL;
  173. morse_data->state = MORSE_STATE_BLINK_STOP;
  174. mod_timer(&morse_data->timer, jiffies + 1);
  175. return size;
  176. }
  177. #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23)
  178. static ssize_t morse_delay_show(struct device *dev,
  179. struct device_attribute *attr, char *buf)
  180. {
  181. struct led_classdev *led_cdev = dev_get_drvdata(dev);
  182. return _morse_delay_show(led_cdev, buf);
  183. }
  184. static ssize_t morse_delay_store(struct device *dev,
  185. struct device_attribute *attr, const char *buf, size_t size)
  186. {
  187. struct led_classdev *led_cdev = dev_get_drvdata(dev);
  188. return _morse_delay_store(led_cdev, buf, size);
  189. }
  190. static ssize_t morse_msg_show(struct device *dev,
  191. struct device_attribute *attr, char *buf)
  192. {
  193. struct led_classdev *led_cdev = dev_get_drvdata(dev);
  194. return _morse_msg_show(led_cdev, buf);
  195. }
  196. static ssize_t morse_msg_store(struct device *dev,
  197. struct device_attribute *attr, const char *buf, size_t size)
  198. {
  199. struct led_classdev *led_cdev = dev_get_drvdata(dev);
  200. return _morse_msg_store(led_cdev, buf, size);
  201. }
  202. static DEVICE_ATTR(delay, 0644, morse_delay_show, morse_delay_store);
  203. static DEVICE_ATTR(message, 0644, morse_msg_show, morse_msg_store);
  204. #define led_device_create_file(leddev, attr) \
  205. device_create_file(leddev->dev, &dev_attr_ ## attr)
  206. #define led_device_remove_file(leddev, attr) \
  207. device_remove_file(leddev->dev, &dev_attr_ ## attr)
  208. #else
  209. static ssize_t morse_delay_show(struct class_device *dev, char *buf)
  210. {
  211. struct led_classdev *led_cdev = class_get_devdata(dev);
  212. return _morse_delay_show(led_cdev, buf);
  213. }
  214. static ssize_t morse_delay_store(struct class_device *dev, const char *buf,
  215. size_t size)
  216. {
  217. struct led_classdev *led_cdev = class_get_devdata(dev);
  218. return _morse_delay_store(led_cdev, buf, size);
  219. }
  220. static ssize_t morse_msg_show(struct class_device *dev, char *buf)
  221. {
  222. struct led_classdev *led_cdev = class_get_devdata(dev);
  223. return _morse_msg_show(led_cdev, buf);
  224. }
  225. static ssize_t morse_msg_store(struct class_device *dev, const char *buf,
  226. size_t size)
  227. {
  228. struct led_classdev *led_cdev = class_get_devdata(dev);
  229. return _morse_msg_store(led_cdev, buf, size);
  230. }
  231. static CLASS_DEVICE_ATTR(delay, 0644, morse_delay_show, morse_delay_store);
  232. static CLASS_DEVICE_ATTR(message, 0644, morse_msg_show, morse_msg_store);
  233. #define led_device_create_file(leddev, attr) \
  234. class_device_create_file(leddev->class_dev, &class_device_attr_ ## attr)
  235. #define led_device_remove_file(leddev, attr) \
  236. class_device_remove_file(leddev->class_dev, &class_device_attr_ ## attr)
  237. #endif
  238. static void morse_trig_activate(struct led_classdev *led_cdev)
  239. {
  240. struct morse_trig_data *morse_data;
  241. int rc;
  242. morse_data = kzalloc(sizeof(*morse_data), GFP_KERNEL);
  243. if (!morse_data)
  244. return;
  245. morse_data->delay = MORSE_DELAY_BASE;
  246. init_timer(&morse_data->timer);
  247. morse_data->timer.function = morse_timer_function;
  248. morse_data->timer.data = (unsigned long)led_cdev;
  249. rc = led_device_create_file(led_cdev, delay);
  250. if (rc) goto err;
  251. rc = led_device_create_file(led_cdev, message);
  252. if (rc) goto err_delay;
  253. led_cdev->trigger_data = morse_data;
  254. return;
  255. err_delay:
  256. led_device_remove_file(led_cdev, delay);
  257. err:
  258. kfree(morse_data);
  259. }
  260. static void morse_trig_deactivate(struct led_classdev *led_cdev)
  261. {
  262. struct morse_trig_data *morse_data = led_cdev->trigger_data;
  263. if (!morse_data)
  264. return;
  265. led_device_remove_file(led_cdev, message);
  266. led_device_remove_file(led_cdev, delay);
  267. del_timer_sync(&morse_data->timer);
  268. if (morse_data->msg)
  269. kfree(morse_data->msg);
  270. kfree(morse_data);
  271. }
  272. static struct led_trigger morse_led_trigger = {
  273. .name = "morse",
  274. .activate = morse_trig_activate,
  275. .deactivate = morse_trig_deactivate,
  276. };
  277. static int __init morse_trig_init(void)
  278. {
  279. return led_trigger_register(&morse_led_trigger);
  280. }
  281. static void __exit morse_trig_exit(void)
  282. {
  283. led_trigger_unregister(&morse_led_trigger);
  284. }
  285. module_init(morse_trig_init);
  286. module_exit(morse_trig_exit);
  287. MODULE_AUTHOR("Gabor Juhos <juhosg at openwrt.org>");
  288. MODULE_DESCRIPTION("Morse LED trigger");
  289. MODULE_LICENSE("GPL");