1 | /* $NetBSD: irframe.c,v 1.46 2014/07/25 08:10:37 dholland Exp $ */ |
2 | |
3 | /* |
4 | * Copyright (c) 2001 The NetBSD Foundation, Inc. |
5 | * All rights reserved. |
6 | * |
7 | * This code is derived from software contributed to The NetBSD Foundation |
8 | * by Lennart Augustsson (lennart@augustsson.net). |
9 | * |
10 | * Redistribution and use in source and binary forms, with or without |
11 | * modification, are permitted provided that the following conditions |
12 | * are met: |
13 | * 1. Redistributions of source code must retain the above copyright |
14 | * notice, this list of conditions and the following disclaimer. |
15 | * 2. Redistributions in binary form must reproduce the above copyright |
16 | * notice, this list of conditions and the following disclaimer in the |
17 | * documentation and/or other materials provided with the distribution. |
18 | * |
19 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS |
20 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
21 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
22 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS |
23 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
29 | * POSSIBILITY OF SUCH DAMAGE. |
30 | */ |
31 | |
32 | #include <sys/cdefs.h> |
33 | __KERNEL_RCSID(0, "$NetBSD: irframe.c,v 1.46 2014/07/25 08:10:37 dholland Exp $" ); |
34 | |
35 | #include "irframe.h" |
36 | |
37 | #include <sys/param.h> |
38 | #include <sys/systm.h> |
39 | #include <sys/ioctl.h> |
40 | #include <sys/kernel.h> |
41 | #include <sys/device.h> |
42 | #include <sys/conf.h> |
43 | #include <sys/malloc.h> |
44 | #include <sys/poll.h> |
45 | #include <sys/select.h> |
46 | #include <sys/vnode.h> |
47 | |
48 | #include <dev/ir/ir.h> |
49 | #include <dev/ir/irdaio.h> |
50 | #include <dev/ir/irframevar.h> |
51 | |
52 | #ifdef IRFRAME_DEBUG |
53 | #define DPRINTF(x) if (irframedebug) printf x |
54 | #define Static |
55 | int irframedebug = 0; |
56 | #else |
57 | #define DPRINTF(x) |
58 | #define Static static |
59 | #endif |
60 | |
61 | dev_type_open(irframeopen); |
62 | dev_type_close(irframeclose); |
63 | dev_type_read(irframeread); |
64 | dev_type_write(irframewrite); |
65 | dev_type_ioctl(irframeioctl); |
66 | dev_type_poll(irframepoll); |
67 | dev_type_kqfilter(irframekqfilter); |
68 | |
69 | const struct cdevsw irframe_cdevsw = { |
70 | .d_open = irframeopen, |
71 | .d_close = irframeclose, |
72 | .d_read = irframeread, |
73 | .d_write = irframewrite, |
74 | .d_ioctl = irframeioctl, |
75 | .d_stop = nostop, |
76 | .d_tty = notty, |
77 | .d_poll = irframepoll, |
78 | .d_mmap = nommap, |
79 | .d_kqfilter = irframekqfilter, |
80 | .d_discard = nodiscard, |
81 | .d_flag = D_OTHER |
82 | }; |
83 | |
84 | int irframe_match(device_t parent, cfdata_t match, void *aux); |
85 | |
86 | Static int irf_set_params(struct irframe_softc *sc, struct irda_params *p); |
87 | Static int irf_reset_params(struct irframe_softc *sc); |
88 | |
89 | #if NIRFRAME == 0 |
90 | /* In case we just have tty attachment. */ |
91 | CFDRIVER_DECL(irframe, DV_DULL, NULL); |
92 | #endif |
93 | |
94 | CFATTACH_DECL_NEW(irframe, sizeof(struct irframe_softc), |
95 | irframe_match, irframe_attach, irframe_detach, NULL); |
96 | |
97 | extern struct cfdriver irframe_cd; |
98 | |
99 | #define IRFRAMEUNIT(dev) (minor(dev)) |
100 | |
101 | int |
102 | irframe_match(device_t parent, cfdata_t match, void *aux) |
103 | { |
104 | struct ir_attach_args *ia = aux; |
105 | |
106 | return (ia->ia_type == IR_TYPE_IRFRAME); |
107 | } |
108 | |
109 | void |
110 | irframe_attach(device_t parent, device_t self, void *aux) |
111 | { |
112 | struct irframe_softc *sc = device_private(self); |
113 | struct ir_attach_args *ia = aux; |
114 | const char *delim; |
115 | int speeds = 0; |
116 | |
117 | sc->sc_dev = self; |
118 | sc->sc_methods = ia->ia_methods; |
119 | sc->sc_handle = ia->ia_handle; |
120 | |
121 | #ifdef DIAGNOSTIC |
122 | if (sc->sc_methods->im_read == NULL || |
123 | sc->sc_methods->im_write == NULL || |
124 | sc->sc_methods->im_poll == NULL || |
125 | sc->sc_methods->im_kqfilter == NULL || |
126 | sc->sc_methods->im_set_params == NULL || |
127 | sc->sc_methods->im_get_speeds == NULL || |
128 | sc->sc_methods->im_get_turnarounds == NULL) |
129 | panic("%s: missing methods" , device_xname(self)); |
130 | #endif |
131 | |
132 | (void)sc->sc_methods->im_get_speeds(sc->sc_handle, &speeds); |
133 | sc->sc_speedmask = speeds; |
134 | delim = ":" ; |
135 | if (speeds & IRDA_SPEEDS_SIR) { |
136 | printf("%s SIR" , delim); |
137 | delim = "," ; |
138 | } |
139 | if (speeds & IRDA_SPEEDS_MIR) { |
140 | printf("%s MIR" , delim); |
141 | delim = "," ; |
142 | } |
143 | if (speeds & IRDA_SPEEDS_FIR) { |
144 | printf("%s FIR" , delim); |
145 | delim = "," ; |
146 | } |
147 | if (speeds & IRDA_SPEEDS_VFIR) { |
148 | printf("%s VFIR" , delim); |
149 | delim = "," ; |
150 | } |
151 | printf("\n" ); |
152 | |
153 | if (!pmf_device_register(self, NULL, NULL)) |
154 | aprint_error_dev(self, "couldn't establish power handler\n" ); |
155 | } |
156 | |
157 | int |
158 | irframe_detach(device_t self, int flags) |
159 | { |
160 | /*struct irframe_softc *sc = device_private(self);*/ |
161 | int maj, mn; |
162 | |
163 | pmf_device_deregister(self); |
164 | |
165 | /* XXX needs reference count */ |
166 | |
167 | /* locate the major number */ |
168 | maj = cdevsw_lookup_major(&irframe_cdevsw); |
169 | |
170 | /* Nuke the vnodes for any open instances (calls close). */ |
171 | mn = device_unit(self); |
172 | vdevgone(maj, mn, mn, VCHR); |
173 | |
174 | return (0); |
175 | } |
176 | |
177 | int |
178 | irframeopen(dev_t dev, int flag, int mode, struct lwp *l) |
179 | { |
180 | struct irframe_softc *sc; |
181 | int error; |
182 | |
183 | sc = device_lookup_private(&irframe_cd, IRFRAMEUNIT(dev)); |
184 | if (sc == NULL) |
185 | return (ENXIO); |
186 | if (!device_is_active(sc->sc_dev)) |
187 | return (EIO); |
188 | if (sc->sc_open) |
189 | return (EBUSY); |
190 | if (sc->sc_methods->im_open != NULL) { |
191 | error = sc->sc_methods->im_open(sc->sc_handle, flag, mode, l); |
192 | if (error) |
193 | return (error); |
194 | } |
195 | sc->sc_open = 1; |
196 | #ifdef DIAGNOSTIC |
197 | sc->sc_speed = IRDA_DEFAULT_SPEED; |
198 | #endif |
199 | (void)irf_reset_params(sc); |
200 | return (0); |
201 | } |
202 | |
203 | int |
204 | irframeclose(dev_t dev, int flag, int mode, struct lwp *l) |
205 | { |
206 | struct irframe_softc *sc; |
207 | int error; |
208 | |
209 | sc = device_lookup_private(&irframe_cd, IRFRAMEUNIT(dev)); |
210 | if (sc == NULL) |
211 | return (ENXIO); |
212 | sc->sc_open = 0; |
213 | if (sc->sc_methods->im_close != NULL) |
214 | error = sc->sc_methods->im_close(sc->sc_handle, flag, mode, l); |
215 | else |
216 | error = 0; |
217 | return (error); |
218 | } |
219 | |
220 | int |
221 | irframeread(dev_t dev, struct uio *uio, int flag) |
222 | { |
223 | struct irframe_softc *sc; |
224 | |
225 | sc = device_lookup_private(&irframe_cd, IRFRAMEUNIT(dev)); |
226 | if (sc == NULL) |
227 | return (ENXIO); |
228 | if (!device_is_active(sc->sc_dev) || !sc->sc_open) |
229 | return (EIO); |
230 | if (uio->uio_resid < sc->sc_params.maxsize) { |
231 | #ifdef DIAGNOSTIC |
232 | printf("irframeread: short read %ld < %d\n" , |
233 | (long)uio->uio_resid, sc->sc_params.maxsize); |
234 | #endif |
235 | return (EINVAL); |
236 | } |
237 | return (sc->sc_methods->im_read(sc->sc_handle, uio, flag)); |
238 | } |
239 | |
240 | int |
241 | irframewrite(dev_t dev, struct uio *uio, int flag) |
242 | { |
243 | struct irframe_softc *sc; |
244 | |
245 | sc = device_lookup_private(&irframe_cd, IRFRAMEUNIT(dev)); |
246 | if (sc == NULL) |
247 | return (ENXIO); |
248 | if (!device_is_active(sc->sc_dev) || !sc->sc_open) |
249 | return (EIO); |
250 | if (uio->uio_resid > sc->sc_params.maxsize) { |
251 | #ifdef DIAGNOSTIC |
252 | printf("irframeread: long write %ld > %d\n" , |
253 | (long)uio->uio_resid, sc->sc_params.maxsize); |
254 | #endif |
255 | return (EINVAL); |
256 | } |
257 | return (sc->sc_methods->im_write(sc->sc_handle, uio, flag)); |
258 | } |
259 | |
260 | int |
261 | irf_set_params(struct irframe_softc *sc, struct irda_params *p) |
262 | { |
263 | int error; |
264 | |
265 | DPRINTF(("irf_set_params: set params speed=%u ebofs=%u maxsize=%u " |
266 | "speedmask=0x%x\n" , p->speed, p->ebofs, p->maxsize, |
267 | sc->sc_speedmask)); |
268 | |
269 | if (p->maxsize > IRDA_MAX_FRAME_SIZE) { |
270 | #ifdef IRFRAME_DEBUG |
271 | printf("irf_set_params: bad maxsize=%u\n" , p->maxsize); |
272 | #endif |
273 | return (EINVAL); |
274 | } |
275 | |
276 | if (p->ebofs > IRDA_MAX_EBOFS) { |
277 | #ifdef IRFRAME_DEBUG |
278 | printf("irf_set_params: bad maxsize=%u\n" , p->maxsize); |
279 | #endif |
280 | return (EINVAL); |
281 | } |
282 | |
283 | #define CONC(x,y) x##y |
284 | #define CASE(s) case s: if (!(sc->sc_speedmask & CONC(IRDA_SPEED_,s))) return (EINVAL); break |
285 | switch (p->speed) { |
286 | CASE(2400); |
287 | CASE(9600); |
288 | CASE(19200); |
289 | CASE(38400); |
290 | CASE(57600); |
291 | CASE(115200); |
292 | CASE(576000); |
293 | CASE(1152000); |
294 | CASE(4000000); |
295 | CASE(16000000); |
296 | default: return (EINVAL); |
297 | } |
298 | #undef CONC |
299 | #undef CASE |
300 | |
301 | error = sc->sc_methods->im_set_params(sc->sc_handle, p); |
302 | if (!error) { |
303 | sc->sc_params = *p; |
304 | DPRINTF(("irf_set_params: ok\n" )); |
305 | #ifdef DIAGNOSTIC |
306 | if (p->speed != sc->sc_speed) { |
307 | sc->sc_speed = p->speed; |
308 | aprint_verbose_dev(sc->sc_dev, "set speed %u\n" , |
309 | sc->sc_speed); |
310 | } |
311 | #endif |
312 | } else { |
313 | #ifdef IRFRAME_DEBUG |
314 | printf("irf_set_params: error=%d\n" , error); |
315 | #endif |
316 | } |
317 | return (error); |
318 | } |
319 | |
320 | int |
321 | irf_reset_params(struct irframe_softc *sc) |
322 | { |
323 | struct irda_params params; |
324 | |
325 | params.speed = IRDA_DEFAULT_SPEED; |
326 | params.ebofs = IRDA_DEFAULT_EBOFS; |
327 | params.maxsize = IRDA_DEFAULT_SIZE; |
328 | return (irf_set_params(sc, ¶ms)); |
329 | } |
330 | |
331 | int |
332 | irframeioctl(dev_t dev, u_long cmd, void *addr, int flag, |
333 | struct lwp *l) |
334 | { |
335 | struct irframe_softc *sc; |
336 | void *vaddr = addr; |
337 | int error; |
338 | |
339 | sc = device_lookup_private(&irframe_cd, IRFRAMEUNIT(dev)); |
340 | if (sc == NULL) |
341 | return (ENXIO); |
342 | if (!device_is_active(sc->sc_dev) || !sc->sc_open) |
343 | return (EIO); |
344 | |
345 | switch (cmd) { |
346 | case FIONBIO: |
347 | /* All handled in the upper FS layer. */ |
348 | error = 0; |
349 | break; |
350 | |
351 | case IRDA_SET_PARAMS: |
352 | error = irf_set_params(sc, vaddr); |
353 | break; |
354 | |
355 | case IRDA_RESET_PARAMS: |
356 | error = irf_reset_params(sc); |
357 | break; |
358 | |
359 | case IRDA_GET_SPEEDMASK: |
360 | error = sc->sc_methods->im_get_speeds(sc->sc_handle, vaddr); |
361 | break; |
362 | |
363 | case IRDA_GET_TURNAROUNDMASK: |
364 | error = sc->sc_methods->im_get_turnarounds(sc->sc_handle,vaddr); |
365 | break; |
366 | |
367 | default: |
368 | error = EINVAL; |
369 | break; |
370 | } |
371 | return (error); |
372 | } |
373 | |
374 | int |
375 | irframepoll(dev_t dev, int events, struct lwp *l) |
376 | { |
377 | struct irframe_softc *sc; |
378 | |
379 | sc = device_lookup_private(&irframe_cd, IRFRAMEUNIT(dev)); |
380 | if (sc == NULL) |
381 | return (POLLHUP); |
382 | if (!device_is_active(sc->sc_dev) || !sc->sc_open) |
383 | return (POLLHUP); |
384 | |
385 | return (sc->sc_methods->im_poll(sc->sc_handle, events, l)); |
386 | } |
387 | |
388 | int |
389 | irframekqfilter(dev_t dev, struct knote *kn) |
390 | { |
391 | struct irframe_softc *sc; |
392 | |
393 | sc = device_lookup_private(&irframe_cd, IRFRAMEUNIT(dev)); |
394 | if (!device_is_active(sc->sc_dev) || !sc->sc_open) |
395 | return (1); |
396 | |
397 | return (sc->sc_methods->im_kqfilter(sc->sc_handle, kn)); |
398 | } |
399 | |