FreeBSD/Linux Kernel Cross Reference
sys/dev/led/led.c
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: src/sys/dev/led/led.c,v 1.13 2004/07/10 15:38:27 phk Exp $");
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 struct cdev *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 if (*sc->ptr == '.') {
56 sc->ptr = NULL;
57 continue;
58 } else if (*sc->ptr >= 'a' && *sc->ptr <= 'j') {
59 sc->func(sc->private, 0);
60 } else if (*sc->ptr >= 'A' && *sc->ptr <= 'J') {
61 sc->func(sc->private, 1);
62 }
63 sc->count = *sc->ptr & 0xf;
64 sc->count--;
65 sc->ptr++;
66 if (*sc->ptr == '\0')
67 sc->ptr = sc->str;
68 }
69 mtx_unlock(&led_mtx);
70 timeout(led_timeout, p, hz / 10);
71 return;
72 }
73
74 static int
75 led_write(struct cdev *dev, struct uio *uio, int ioflag)
76 {
77 int error;
78 char *s, *s2;
79 struct ledsc *sc;
80 struct sbuf *sb;
81 struct sbuf *sb2;
82 int i;
83
84 sc = dev->si_drv1;
85
86 if (uio->uio_resid > 512)
87 return (EINVAL);
88 s2 = s = malloc(uio->uio_resid + 1, M_DEVBUF, M_WAITOK);
89 s[uio->uio_resid] = '\0';
90 error = uiomove(s, uio->uio_resid, uio);
91 if (error) {
92 free(s2, M_DEVBUF);
93 return (error);
94 }
95
96 /*
97 * Handle "on" and "off" immediately so people can flash really
98 * fast from userland if they want to
99 */
100 if (*s == '' || *s == '1') {
101 mtx_lock(&led_mtx);
102 sb2 = sc->spec;
103 sc->spec = NULL;
104 sc->str = NULL;
105 sc->ptr = NULL;
106 sc->count = 0;
107 sc->func(sc->private, *s & 1);
108 mtx_unlock(&led_mtx);
109 if (sb2 != NULL)
110 sbuf_delete(sb2);
111 free(s2, M_DEVBUF);
112 return(0);
113 }
114
115 sb = sbuf_new(NULL, NULL, 0, SBUF_AUTOEXTEND);
116 if (sb == NULL) {
117 free(s2, M_DEVBUF);
118 return (ENOMEM);
119 }
120
121 switch(s[0]) {
122 /*
123 * Flash, default is 100msec/100msec.
124 * 'f2' sets 200msec/200msec etc.
125 */
126 case 'f':
127 if (s[1] >= '1' && s[1] <= '9')
128 i = s[1] - '1';
129 else
130 i = 0;
131 sbuf_printf(sb, "%c%c", 'A' + i, 'a' + i);
132 break;
133 /*
134 * Digits, flashes out numbers.
135 * 'd12' becomes -__________-_-______________________________
136 */
137 case 'd':
138 for(s++; *s; s++) {
139 if (!isdigit(*s))
140 continue;
141 i = *s - '';
142 if (i == 0)
143 i = 10;
144 for (; i > 1; i--)
145 sbuf_cat(sb, "Aa");
146 sbuf_cat(sb, "Aj");
147 }
148 sbuf_cat(sb, "jj");
149 break;
150 /*
151 * String, roll your own.
152 * 'a-j' gives "off" for n/10 sec.
153 * 'A-J' gives "on" for n/10 sec.
154 * no delay before repeat
155 * 'sAaAbBa' becomes _-_--__-
156 */
157 case 's':
158 for(s++; *s; s++) {
159 if ((*s >= 'a' && *s <= 'j') ||
160 (*s >= 'A' && *s <= 'J') ||
161 *s == '.')
162 sbuf_bcat(sb, s, 1);
163 }
164 break;
165 /*
166 * Morse.
167 * '.' becomes _-
168 * '-' becomes _---
169 * ' ' becomes __
170 * '\n' becomes ____
171 * 1sec pause between repeats
172 * '... --- ...' -> _-_-_-___---_---_---___-_-_-__________
173 */
174 case 'm':
175 for(s++; *s; s++) {
176 if (*s == '.')
177 sbuf_cat(sb, "aA");
178 else if (*s == '-')
179 sbuf_cat(sb, "aC");
180 else if (*s == ' ')
181 sbuf_cat(sb, "b");
182 else if (*s == '\n')
183 sbuf_cat(sb, "d");
184 }
185 sbuf_cat(sb, "j");
186 break;
187 default:
188 sbuf_delete(sb);
189 free(s2, M_DEVBUF);
190 return (EINVAL);
191 }
192 sbuf_finish(sb);
193 free(s2, M_DEVBUF);
194 if (sbuf_overflowed(sb)) {
195 sbuf_delete(sb);
196 return (ENOMEM);
197 }
198 if (sbuf_len(sb) == 0) {
199 sbuf_delete(sb);
200 return (0);
201 }
202
203 mtx_lock(&led_mtx);
204 sb2 = sc->spec;
205 sc->spec = sb;
206 sc->str = sbuf_data(sb);
207 sc->ptr = sc->str;
208 sc->count = 0;
209 mtx_unlock(&led_mtx);
210 if (sb2 != NULL)
211 sbuf_delete(sb2);
212 return(0);
213 }
214
215 static struct cdevsw led_cdevsw = {
216 .d_version = D_VERSION,
217 .d_flags = D_NEEDGIANT,
218 .d_write = led_write,
219 .d_name = "LED",
220 };
221
222 struct cdev *
223 led_create(led_t *func, void *priv, char const *name)
224 {
225 struct ledsc *sc;
226 struct sbuf *sb;
227
228 if (next_minor == 0) {
229 mtx_init(&led_mtx, "LED mtx", NULL, MTX_DEF);
230 timeout(led_timeout, NULL, hz / 10);
231 }
232
233 sb = sbuf_new(NULL, NULL, SPECNAMELEN, SBUF_FIXEDLEN);
234 if (sb == NULL)
235 return (NULL);
236 sbuf_cpy(sb, "led/");
237 sbuf_cat(sb, name);
238 sbuf_finish(sb);
239 if (sbuf_overflowed(sb)) {
240 sbuf_delete(sb);
241 return (NULL);
242 }
243
244 sc = malloc(sizeof *sc, M_LED, M_WAITOK | M_ZERO);
245 sc->private = priv;
246 sc->func = func;
247 sc->dev = make_dev(&led_cdevsw, unit2minor(next_minor),
248 UID_ROOT, GID_WHEEL, 0600, sbuf_data(sb));
249 sc->dev->si_drv1 = sc;
250 next_minor++;
251 sbuf_delete(sb);
252 mtx_lock(&led_mtx);
253 LIST_INSERT_HEAD(&led_list, sc, list);
254 sc->func(sc->private, 0);
255 mtx_unlock(&led_mtx);
256 return (sc->dev);
257 }
258
259 void
260 led_destroy(struct cdev *dev)
261 {
262 struct ledsc *sc;
263
264 sc = dev->si_drv1;
265 mtx_lock(&led_mtx);
266 LIST_REMOVE(sc, list);
267 mtx_unlock(&led_mtx);
268 if (sc->spec != NULL)
269 sbuf_delete(sc->spec);
270 destroy_dev(dev);
271 free(sc, M_LED);
272 }
Cache object: e02194b1a45bd3864419b41ec3ceac57
|