1 /*- 2 * ---------------------------------------------------------------------------- 3 * "THE BEER-WARE LICENSE" (Revision 42): 4 * <phk@FreeBSD.org> wrote this file. As long as you retain this notice you 5 * can do whatever you want with this stuff. If we meet some day, and you think 6 * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp 7 * ---------------------------------------------------------------------------- 8 * 9 * $FreeBSD: head/sys/dev/led/led.c 247008 2013-02-19 19:25:50Z mav $ 10 */ 11 12 #include <sys/param.h> 13 #include <sys/conf.h> 14 #include <sys/kernel.h> 15 #include <sys/systm.h> 16 #include <sys/limits.h> 17 #include <sys/malloc.h> 18 #include <sys/ctype.h> 19 #include <sys/sbuf.h> 20 #include <sys/queue.h> 21 #include <dev/misc/led/led.h> 22 #include <sys/uio.h> 23 #include <sys/device.h> 24 #include <sys/module.h> 25 26 struct ledsc { 27 LIST_ENTRY(ledsc) list; 28 char *name; 29 void *private; 30 int unit; 31 led_t *func; 32 struct cdev *dev; 33 struct sbuf *spec; 34 char *str; 35 char *ptr; 36 int count; 37 time_t last_second; 38 }; 39 40 static struct unrhdr *led_unit; 41 static struct lock led_lock; 42 static struct lock led_lock2; 43 static LIST_HEAD(, ledsc) led_list = LIST_HEAD_INITIALIZER(led_list); 44 static struct callout led_ch; 45 static int blinkers = 0; 46 47 static MALLOC_DEFINE(M_LED, "LED", "LED driver"); 48 49 static void 50 led_timeout(void *p) 51 { 52 struct ledsc *sc; 53 54 lockmgr(&led_lock, LK_EXCLUSIVE); 55 LIST_FOREACH(sc, &led_list, list) { 56 if (sc->ptr == NULL) 57 continue; 58 if (sc->count > 0) { 59 sc->count--; 60 continue; 61 } 62 if (*sc->ptr == '.') { 63 sc->ptr = NULL; 64 blinkers--; 65 continue; 66 } else if (*sc->ptr == 'U' || *sc->ptr == 'u') { 67 if (sc->last_second == time_second) 68 continue; 69 sc->last_second = time_second; 70 sc->func(sc->private, *sc->ptr == 'U'); 71 } else if (*sc->ptr >= 'a' && *sc->ptr <= 'j') { 72 sc->func(sc->private, 0); 73 sc->count = (*sc->ptr & 0xf) - 1; 74 } else if (*sc->ptr >= 'A' && *sc->ptr <= 'J') { 75 sc->func(sc->private, 1); 76 sc->count = (*sc->ptr & 0xf) - 1; 77 } 78 sc->ptr++; 79 if (*sc->ptr == '\0') 80 sc->ptr = sc->str; 81 } 82 if (blinkers > 0) 83 callout_reset(&led_ch, hz / 10, led_timeout, p); 84 lockmgr(&led_lock, LK_RELEASE); 85 } 86 87 static int 88 led_state(struct ledsc *sc, struct sbuf **sb, int state) 89 { 90 struct sbuf *sb2 = NULL; 91 92 sb2 = sc->spec; 93 sc->spec = *sb; 94 if (*sb != NULL) { 95 sc->str = sbuf_data(*sb); 96 if (sc->ptr == NULL) { 97 blinkers++; 98 callout_reset(&led_ch, hz / 10, led_timeout, NULL); 99 } 100 sc->ptr = sc->str; 101 } else { 102 sc->str = NULL; 103 if (sc->ptr != NULL) 104 blinkers--; 105 sc->ptr = NULL; 106 sc->func(sc->private, state); 107 } 108 sc->count = 0; 109 *sb = sb2; 110 return(0); 111 } 112 113 static int 114 led_parse(const char *s, struct sbuf **sb, int *state) 115 { 116 int i, error; 117 118 /* 119 * Handle "on" and "off" immediately so people can flash really 120 * fast from userland if they want to 121 */ 122 if (*s == '0' || *s == '1') { 123 *state = *s & 1; 124 return (0); 125 } 126 127 *state = 0; 128 *sb = sbuf_new_auto(); 129 if (*sb == NULL) 130 return (ENOMEM); 131 switch(s[0]) { 132 /* 133 * Flash, default is 100msec/100msec. 134 * 'f2' sets 200msec/200msec etc. 135 */ 136 case 'f': 137 if (s[1] >= '1' && s[1] <= '9') 138 i = s[1] - '1'; 139 else 140 i = 0; 141 sbuf_printf(*sb, "%c%c", 'A' + i, 'a' + i); 142 break; 143 /* 144 * Digits, flashes out numbers. 145 * 'd12' becomes -__________-_-______________________________ 146 */ 147 case 'd': 148 for(s++; *s; s++) { 149 if (!isdigit(*s)) 150 continue; 151 i = *s - '0'; 152 if (i == 0) 153 i = 10; 154 for (; i > 1; i--) 155 sbuf_cat(*sb, "Aa"); 156 sbuf_cat(*sb, "Aj"); 157 } 158 sbuf_cat(*sb, "jj"); 159 break; 160 /* 161 * String, roll your own. 162 * 'a-j' gives "off" for n/10 sec. 163 * 'A-J' gives "on" for n/10 sec. 164 * no delay before repeat 165 * 'sAaAbBa' becomes _-_--__- 166 */ 167 case 's': 168 for(s++; *s; s++) { 169 if ((*s >= 'a' && *s <= 'j') || 170 (*s >= 'A' && *s <= 'J') || 171 *s == 'U' || *s <= 'u' || 172 *s == '.') 173 sbuf_bcat(*sb, s, 1); 174 } 175 break; 176 /* 177 * Morse. 178 * '.' becomes _- 179 * '-' becomes _--- 180 * ' ' becomes __ 181 * '\n' becomes ____ 182 * 1sec pause between repeats 183 * '... --- ...' -> _-_-_-___---_---_---___-_-_-__________ 184 */ 185 case 'm': 186 for(s++; *s; s++) { 187 if (*s == '.') 188 sbuf_cat(*sb, "aA"); 189 else if (*s == '-') 190 sbuf_cat(*sb, "aC"); 191 else if (*s == ' ') 192 sbuf_cat(*sb, "b"); 193 else if (*s == '\n') 194 sbuf_cat(*sb, "d"); 195 } 196 sbuf_cat(*sb, "j"); 197 break; 198 default: 199 sbuf_delete(*sb); 200 return (EINVAL); 201 } 202 error = sbuf_finish(*sb); 203 if (error != 0 || sbuf_len(*sb) == 0) { 204 *sb = NULL; 205 return (error); 206 } 207 return (0); 208 } 209 210 static int 211 led_open(struct dev_open_args *ap) 212 { 213 return (0); 214 } 215 216 static int 217 led_close(struct dev_close_args *ap) 218 { 219 return (0); 220 } 221 222 static int 223 led_write(struct dev_write_args *ap) 224 { 225 struct uio *uio = ap->a_uio; 226 cdev_t dev = ap->a_head.a_dev; 227 struct ledsc *sc; 228 char *s; 229 struct sbuf *sb = NULL; 230 int error, state = 0; 231 232 if (uio->uio_resid > 512) 233 return (EINVAL); 234 s = kmalloc(uio->uio_resid + 1, M_DEVBUF, M_WAITOK); 235 s[uio->uio_resid] = '\0'; 236 error = uiomove(s, uio->uio_resid, uio); 237 if (error) { 238 kfree(s, M_DEVBUF); 239 return (error); 240 } 241 error = led_parse(s, &sb, &state); 242 kfree(s, M_DEVBUF); 243 if (error) 244 return (error); 245 lockmgr(&led_lock, LK_EXCLUSIVE); 246 sc = dev->si_drv1; 247 if (sc != NULL) 248 error = led_state(sc, &sb, state); 249 lockmgr(&led_lock, LK_RELEASE); 250 if (sb != NULL) 251 sbuf_delete(sb); 252 return (error); 253 } 254 255 int 256 led_set(char const *name, char const *cmd) 257 { 258 struct ledsc *sc; 259 struct sbuf *sb = NULL; 260 int error, state = 0; 261 262 error = led_parse(cmd, &sb, &state); 263 if (error) 264 return (error); 265 lockmgr(&led_lock, LK_EXCLUSIVE); 266 LIST_FOREACH(sc, &led_list, list) { 267 if (strcmp(sc->name, name) == 0) 268 break; 269 } 270 if (sc != NULL) 271 error = led_state(sc, &sb, state); 272 else 273 error = ENOENT; 274 lockmgr(&led_lock, LK_RELEASE); 275 if (sb != NULL) 276 sbuf_delete(sb); 277 return (0); 278 } 279 280 static struct dev_ops led_ops = { 281 { "LED", 0, D_MPSAFE }, 282 .d_open = led_open, 283 .d_close = led_close, 284 .d_write = led_write, 285 }; 286 287 struct cdev * 288 led_create(led_t *func, void *priv, char const *name) 289 { 290 291 return (led_create_state(func, priv, name, 0)); 292 } 293 struct cdev * 294 led_create_state(led_t *func, void *priv, char const *name, int state) 295 { 296 struct ledsc *sc; 297 298 sc = kmalloc(sizeof *sc, M_LED, M_WAITOK | M_ZERO); 299 300 lockmgr(&led_lock2, LK_EXCLUSIVE); 301 sc->name = kstrdup(name, M_LED); 302 sc->unit = alloc_unr(led_unit); 303 sc->private = priv; 304 sc->func = func; 305 sc->dev = make_dev(&led_ops, sc->unit, 306 UID_ROOT, GID_WHEEL, 0600, "led/%s", name); 307 lockmgr(&led_lock2, LK_RELEASE); 308 309 lockmgr(&led_lock, LK_EXCLUSIVE); 310 sc->dev->si_drv1 = sc; 311 LIST_INSERT_HEAD(&led_list, sc, list); 312 sc->func(sc->private, state != 0); 313 lockmgr(&led_lock, LK_RELEASE); 314 315 return (sc->dev); 316 } 317 318 void 319 led_destroy(struct cdev *dev) 320 { 321 struct ledsc *sc; 322 323 lockmgr(&led_lock, LK_EXCLUSIVE); 324 sc = dev->si_drv1; 325 dev->si_drv1 = NULL; 326 if (sc->ptr != NULL) 327 blinkers--; 328 LIST_REMOVE(sc, list); 329 if (LIST_EMPTY(&led_list)) 330 callout_stop(&led_ch); 331 lockmgr(&led_lock, LK_RELEASE); 332 333 lockmgr(&led_lock2, LK_EXCLUSIVE); 334 free_unr(led_unit, sc->unit); 335 destroy_dev(dev); 336 if (sc->spec != NULL) 337 sbuf_delete(sc->spec); 338 kfree(sc->name, M_LED); 339 kfree(sc, M_LED); 340 lockmgr(&led_lock2, LK_RELEASE); 341 } 342 343 static int 344 led_drvinit(void) 345 { 346 347 led_unit = new_unrhdr(0, INT_MAX, NULL); 348 lockinit(&led_lock, "LED lock", 0, LK_CANRECURSE); 349 lockinit(&led_lock2, "LED lock2", 0, LK_CANRECURSE); 350 callout_init_mp(&led_ch); 351 return 0; 352 } 353 354 static int 355 led_drvexit(void) 356 { 357 int error = 0; 358 359 lockmgr(&led_lock, LK_EXCLUSIVE); 360 /* A minimal sanity check, before unloading. */ 361 if (!LIST_EMPTY(&led_list)) 362 error = EINVAL; 363 lockmgr(&led_lock, LK_RELEASE); 364 if (error == 0) { 365 callout_stop_sync(&led_ch); 366 delete_unrhdr(led_unit); 367 lockuninit(&led_lock); 368 lockuninit(&led_lock2); 369 } 370 return error; 371 } 372 373 static int 374 led_modevent(module_t mod, int type, void *unused) 375 { 376 int error; 377 378 switch (type) { 379 case MOD_LOAD: 380 error = led_drvinit(); 381 break; 382 case MOD_UNLOAD: 383 error = led_drvexit(); 384 break; 385 default: 386 error = EINVAL; 387 break; 388 } 389 return error; 390 } 391 392 static moduledata_t led_mod = { 393 "led", 394 led_modevent, 395 0 396 }; 397 DECLARE_MODULE(led, led_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); 398 MODULE_VERSION(led, 1); 399