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 */ 10 11 #include <sys/cdefs.h> 12 __FBSDID("$FreeBSD$"); 13 14 #include <sys/param.h> 15 #include <sys/conf.h> 16 #include <sys/kernel.h> 17 #include <sys/systm.h> 18 #include <sys/malloc.h> 19 #include <sys/ctype.h> 20 #include <sys/sbuf.h> 21 #include <sys/queue.h> 22 #include <dev/led/led.h> 23 #include <sys/uio.h> 24 25 struct ledsc { 26 LIST_ENTRY(ledsc) list; 27 void *private; 28 led_t *func; 29 dev_t dev; 30 struct sbuf *spec; 31 char *str; 32 char *ptr; 33 int count; 34 }; 35 36 static unsigned next_minor; 37 static struct mtx led_mtx; 38 static LIST_HEAD(, ledsc) led_list = LIST_HEAD_INITIALIZER(&led_list); 39 40 MALLOC_DEFINE(M_LED, "LED", "LED driver"); 41 42 static void 43 led_timeout(void *p) 44 { 45 struct ledsc *sc; 46 47 mtx_lock(&led_mtx); 48 LIST_FOREACH(sc, &led_list, list) { 49 if (sc->ptr == NULL) 50 continue; 51 if (sc->count > 0) { 52 sc->count--; 53 continue; 54 } 55 sc->func(sc->private, sc->ptr[0] >= 'a' ? 1 : 0); 56 sc->count = sc->ptr[0] & 0xf; 57 if (*(++sc->ptr) == '\0') 58 sc->ptr = sc->str; 59 } 60 mtx_unlock(&led_mtx); 61 timeout(led_timeout, p, hz / 10); 62 return; 63 } 64 65 static int 66 led_write(dev_t dev, struct uio *uio, int ioflag) 67 { 68 int error; 69 char *s; 70 struct ledsc *sc; 71 struct sbuf *sb; 72 struct sbuf *sb2; 73 int i; 74 75 sc = dev->si_drv1; 76 77 if (uio->uio_resid > 512) 78 return (EINVAL); 79 s = malloc(uio->uio_resid + 1, M_DEVBUF, M_WAITOK); 80 if (s == NULL) 81 return (ENOMEM); 82 s[uio->uio_resid] = '\0'; 83 error = uiomove(s, uio->uio_resid, uio); 84 if (error) { 85 free(s, M_DEVBUF); 86 return (error); 87 } 88 89 /* 90 * Handle "on" and "off" immediately so people can flash really 91 * fast from userland if they want to 92 */ 93 if (*s == '0' || *s == '1') { 94 mtx_lock(&led_mtx); 95 sb2 = sc->spec; 96 sc->spec = NULL; 97 sc->str = NULL; 98 sc->ptr = NULL; 99 sc->count = 0; 100 sc->func(sc->private, *s & 1); 101 mtx_unlock(&led_mtx); 102 if (sb2 != NULL) 103 sbuf_delete(sb2); 104 free(s, M_DEVBUF); 105 return(0); 106 } 107 108 sb = sbuf_new(NULL, NULL, 0, SBUF_AUTOEXTEND); 109 if (sb == NULL) { 110 free(s, M_DEVBUF); 111 return (ENOMEM); 112 } 113 114 switch(s[0]) { 115 /* 116 * Flash, default is 100msec/100msec. 117 * 'f2' sets 200msec/200msec etc. 118 */ 119 case 'f': 120 if (s[1] >= '1' && s[1] <= '9') 121 i = s[1] - '1'; 122 else 123 i = 0; 124 sbuf_printf(sb, "%c%c", 'a' + i, 'A' + i); 125 break; 126 /* 127 * Digits, flashes out numbers. 128 * 'd12' becomes -__________-_-______________________________ 129 */ 130 case 'd': 131 for(s++; *s; s++) { 132 if (!isdigit(*s)) 133 continue; 134 i = *s - '0'; 135 if (i == 0) 136 i = 10; 137 for (; i > 1; i--) 138 sbuf_cat(sb, "aA"); 139 sbuf_cat(sb, "aJ"); 140 } 141 sbuf_cat(sb, "JJ"); 142 break; 143 /* 144 * String, roll your own. 145 * 'A-J' gives "off" for n/10 sec. 146 * 'a-j' gives "on" for n/10 sec. 147 * no delay before repeat 148 * 'sAaAbBa' becomes _-_--__- 149 */ 150 case 's': 151 for(s++; *s; s++) { 152 if ((*s & 0x0f) > 10) 153 continue; 154 if ((*s & 0xf0) < ' ') 155 continue; 156 sbuf_bcat(sb, s, 1); 157 } 158 break; 159 /* 160 * Morse. 161 * '.' becomes _- 162 * '-' becomes _--- 163 * ' ' becomes __ 164 * '\n' becomes ____ 165 * 1sec pause between repeats 166 * '... --- ...' -> _-_-_-___---_---_---___-_-_-__________ 167 */ 168 case 'm': 169 for(s++; *s; s++) { 170 if (*s == '.') 171 sbuf_cat(sb, "Aa"); 172 else if (*s == '-') 173 sbuf_cat(sb, "Ac"); 174 else if (*s == ' ') 175 sbuf_cat(sb, "B"); 176 else if (*s == '\n') 177 sbuf_cat(sb, "D"); 178 } 179 sbuf_cat(sb, "J"); 180 break; 181 default: 182 break; 183 } 184 sbuf_finish(sb); 185 free(s, M_DEVBUF); 186 if (sbuf_overflowed(sb)) { 187 sbuf_delete(sb); 188 return (ENOMEM); 189 } 190 if (sbuf_len(sb) == 0) { 191 sbuf_delete(sb); 192 return (0); 193 } 194 195 mtx_lock(&led_mtx); 196 sb2 = sc->spec; 197 sc->spec = sb; 198 sc->str = sbuf_data(sb); 199 sc->ptr = sc->str; 200 sc->count = 0; 201 mtx_unlock(&led_mtx); 202 if (sb2 != NULL) 203 sbuf_delete(sb2); 204 return(0); 205 } 206 207 static struct cdevsw led_cdevsw = { 208 .d_write = led_write, 209 .d_name = "LED", 210 }; 211 212 dev_t 213 led_create(led_t *func, void *priv, char const *name) 214 { 215 struct ledsc *sc; 216 struct sbuf *sb; 217 218 if (next_minor == 0) { 219 mtx_init(&led_mtx, "LED mtx", MTX_DEF, 0); 220 timeout(led_timeout, NULL, hz / 10); 221 } 222 223 sb = sbuf_new(NULL, NULL, SPECNAMELEN, SBUF_FIXEDLEN); 224 if (sb == NULL) 225 return (NODEV); 226 sbuf_cpy(sb, "led/"); 227 sbuf_cat(sb, name); 228 sbuf_finish(sb); 229 if (sbuf_overflowed(sb)) { 230 sbuf_delete(sb); 231 return (NODEV); 232 } 233 234 sc = malloc(sizeof *sc, M_LED, M_WAITOK | M_ZERO); 235 sc->private = priv; 236 sc->func = func; 237 sc->dev = make_dev(&led_cdevsw, unit2minor(next_minor), 238 UID_ROOT, GID_WHEEL, 0600, sbuf_data(sb)); 239 sc->dev->si_drv1 = sc; 240 next_minor++; 241 sbuf_delete(sb); 242 mtx_lock(&led_mtx); 243 LIST_INSERT_HEAD(&led_list, sc, list); 244 mtx_unlock(&led_mtx); 245 return (sc->dev); 246 } 247 248 void 249 led_destroy(dev_t dev) 250 { 251 struct ledsc *sc; 252 253 sc = dev->si_drv1; 254 mtx_lock(&led_mtx); 255 LIST_REMOVE(sc, list); 256 mtx_unlock(&led_mtx); 257 sbuf_delete(sc->spec); 258 destroy_dev(dev); 259 free(sc, M_LED); 260 } 261