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