1 | /* $NetBSD: irmce.c,v 1.2 2016/04/23 10:15:32 skrll Exp $ */ |
2 | |
3 | /*- |
4 | * Copyright (c) 2011 Jared D. McNeill <jmcneill@invisible.ca> |
5 | * All rights reserved. |
6 | * |
7 | * Redistribution and use in source and binary forms, with or without |
8 | * modification, are permitted provided that the following conditions |
9 | * are met: |
10 | * 1. Redistributions of source code must retain the above copyright |
11 | * notice, this list of conditions and the following disclaimer. |
12 | * 2. Redistributions in binary form must reproduce the above copyright |
13 | * notice, this list of conditions and the following disclaimer in the |
14 | * documentation and/or other materials provided with the distribution. |
15 | * |
16 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' |
17 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
18 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
19 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS |
20 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
21 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
22 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
23 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
24 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
25 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
26 | * POSSIBILITY OF SUCH DAMAGE. |
27 | */ |
28 | |
29 | /* |
30 | * IR receiver/transceiver for Windows Media Center |
31 | */ |
32 | |
33 | #include <sys/cdefs.h> |
34 | __KERNEL_RCSID(0, "$NetBSD: irmce.c,v 1.2 2016/04/23 10:15:32 skrll Exp $" ); |
35 | |
36 | #include <sys/types.h> |
37 | #include <sys/param.h> |
38 | #include <sys/systm.h> |
39 | #include <sys/device.h> |
40 | #include <sys/conf.h> |
41 | #include <sys/bus.h> |
42 | #include <sys/select.h> |
43 | #include <sys/module.h> |
44 | |
45 | #include <dev/usb/usb.h> |
46 | #include <dev/usb/usbdi.h> |
47 | #include <dev/usb/usbdi_util.h> |
48 | #include <dev/usb/usbdevs.h> |
49 | |
50 | #include <dev/ir/ir.h> |
51 | #include <dev/ir/cirio.h> |
52 | #include <dev/ir/cirvar.h> |
53 | |
54 | enum irmce_state { |
55 | , |
56 | IRMCE_STATE_IRDATA, |
57 | , |
58 | IRMCE_STATE_CMDDATA, |
59 | }; |
60 | |
61 | struct irmce_softc { |
62 | device_t sc_dev; |
63 | device_t sc_cirdev; |
64 | |
65 | struct usbd_device * sc_udev; |
66 | struct usbd_interface * sc_iface; |
67 | |
68 | int sc_bulkin_ep; |
69 | uint16_t sc_bulkin_maxpktsize; |
70 | struct usbd_pipe * sc_bulkin_pipe; |
71 | struct usbd_xfer * sc_bulkin_xfer; |
72 | uint8_t * sc_bulkin_buffer; |
73 | |
74 | int sc_bulkout_ep; |
75 | uint16_t sc_bulkout_maxpktsize; |
76 | struct usbd_pipe * sc_bulkout_pipe; |
77 | struct usbd_xfer * sc_bulkout_xfer; |
78 | uint8_t * sc_bulkout_buffer; |
79 | |
80 | bool sc_raw; |
81 | |
82 | uint8_t sc_ir_buf[16]; |
83 | size_t sc_ir_bufused; |
84 | size_t sc_ir_resid; |
85 | enum irmce_state sc_ir_state; |
86 | uint8_t ; |
87 | |
88 | bool sc_rc6_hb[256]; |
89 | size_t sc_rc6_nhb; |
90 | }; |
91 | |
92 | static int irmce_match(device_t, cfdata_t, void *); |
93 | static void irmce_attach(device_t, device_t, void *); |
94 | static int irmce_detach(device_t, int); |
95 | static void irmce_childdet(device_t, device_t); |
96 | static int irmce_activate(device_t, enum devact); |
97 | static int irmce_rescan(device_t, const char *, const int *); |
98 | |
99 | static int irmce_print(void *, const char *); |
100 | |
101 | static int irmce_reset(struct irmce_softc *); |
102 | |
103 | static int irmce_open(void *, int, int, struct proc *); |
104 | static int irmce_close(void *, int, int, struct proc *); |
105 | static int irmce_read(void *, struct uio *, int); |
106 | static int irmce_write(void *, struct uio *, int); |
107 | static int irmce_setparams(void *, struct cir_params *); |
108 | |
109 | static const struct cir_methods irmce_cir_methods = { |
110 | .im_open = irmce_open, |
111 | .im_close = irmce_close, |
112 | .im_read = irmce_read, |
113 | .im_write = irmce_write, |
114 | .im_setparams = irmce_setparams, |
115 | }; |
116 | |
117 | static const struct { |
118 | uint16_t vendor; |
119 | uint16_t product; |
120 | } irmce_devices[] = { |
121 | { USB_VENDOR_SMK, USB_PRODUCT_SMK_MCE_IR }, |
122 | }; |
123 | |
124 | CFATTACH_DECL2_NEW(irmce, sizeof(struct irmce_softc), |
125 | irmce_match, irmce_attach, irmce_detach, irmce_activate, |
126 | irmce_rescan, irmce_childdet); |
127 | |
128 | static int |
129 | irmce_match(device_t parent, cfdata_t match, void *opaque) |
130 | { |
131 | struct usbif_attach_arg *uiaa = opaque; |
132 | unsigned int i; |
133 | |
134 | for (i = 0; i < __arraycount(irmce_devices); i++) { |
135 | if (irmce_devices[i].vendor == uiaa->uiaa_vendor && |
136 | irmce_devices[i].product == uiaa->uiaa_product) |
137 | return UMATCH_VENDOR_PRODUCT; |
138 | } |
139 | |
140 | return UMATCH_NONE; |
141 | } |
142 | |
143 | static void |
144 | irmce_attach(device_t parent, device_t self, void *opaque) |
145 | { |
146 | struct irmce_softc *sc = device_private(self); |
147 | struct usbif_attach_arg *uiaa = opaque; |
148 | usb_endpoint_descriptor_t *ed; |
149 | char *devinfop; |
150 | unsigned int i; |
151 | uint8_t nep; |
152 | |
153 | pmf_device_register(self, NULL, NULL); |
154 | |
155 | aprint_naive("\n" ); |
156 | |
157 | devinfop = usbd_devinfo_alloc(uiaa->uiaa_device, 0); |
158 | aprint_normal(": %s\n" , devinfop); |
159 | usbd_devinfo_free(devinfop); |
160 | |
161 | sc->sc_dev = self; |
162 | sc->sc_udev = uiaa->uiaa_device; |
163 | sc->sc_iface = uiaa->uiaa_iface; |
164 | |
165 | nep = 0; |
166 | usbd_endpoint_count(sc->sc_iface, &nep); |
167 | sc->sc_bulkin_ep = sc->sc_bulkout_ep = -1; |
168 | for (i = 0; i < nep; i++) { |
169 | int dir, type; |
170 | |
171 | ed = usbd_interface2endpoint_descriptor(sc->sc_iface, i); |
172 | if (ed == NULL) { |
173 | aprint_error_dev(self, |
174 | "couldn't read endpoint descriptor %d\n" , i); |
175 | continue; |
176 | } |
177 | |
178 | dir = UE_GET_DIR(ed->bEndpointAddress); |
179 | type = UE_GET_XFERTYPE(ed->bmAttributes); |
180 | |
181 | if (type != UE_BULK) |
182 | continue; |
183 | |
184 | if (dir == UE_DIR_IN && sc->sc_bulkin_ep == -1) { |
185 | sc->sc_bulkin_ep = ed->bEndpointAddress; |
186 | sc->sc_bulkin_maxpktsize = |
187 | UE_GET_SIZE(UGETW(ed->wMaxPacketSize)) * |
188 | (UE_GET_TRANS(UGETW(ed->wMaxPacketSize)) + 1); |
189 | } |
190 | if (dir == UE_DIR_OUT && sc->sc_bulkout_ep == -1) { |
191 | sc->sc_bulkout_ep = ed->bEndpointAddress; |
192 | sc->sc_bulkout_maxpktsize = |
193 | UE_GET_SIZE(UGETW(ed->wMaxPacketSize)) * |
194 | (UE_GET_TRANS(UGETW(ed->wMaxPacketSize)) + 1); |
195 | } |
196 | } |
197 | |
198 | aprint_debug_dev(self, "in 0x%02x/%d out 0x%02x/%d\n" , |
199 | sc->sc_bulkin_ep, sc->sc_bulkin_maxpktsize, |
200 | sc->sc_bulkout_ep, sc->sc_bulkout_maxpktsize); |
201 | |
202 | if (sc->sc_bulkin_maxpktsize < 16 || sc->sc_bulkout_maxpktsize < 16) { |
203 | aprint_error_dev(self, "bad maxpktsize\n" ); |
204 | return; |
205 | } |
206 | usbd_status err; |
207 | |
208 | err = usbd_open_pipe(sc->sc_iface, sc->sc_bulkin_ep, |
209 | USBD_EXCLUSIVE_USE, &sc->sc_bulkin_pipe); |
210 | if (err) { |
211 | aprint_error_dev(sc->sc_dev, |
212 | "couldn't open bulk-in pipe: %s\n" , usbd_errstr(err)); |
213 | return; |
214 | } |
215 | err = usbd_open_pipe(sc->sc_iface, sc->sc_bulkout_ep, |
216 | USBD_EXCLUSIVE_USE, &sc->sc_bulkout_pipe); |
217 | if (err) { |
218 | aprint_error_dev(sc->sc_dev, |
219 | "couldn't open bulk-out pipe: %s\n" , usbd_errstr(err)); |
220 | usbd_close_pipe(sc->sc_bulkin_pipe); |
221 | sc->sc_bulkin_pipe = NULL; |
222 | return; |
223 | } |
224 | |
225 | int error; |
226 | error = usbd_create_xfer(sc->sc_bulkin_pipe, sc->sc_bulkin_maxpktsize, |
227 | USBD_SHORT_XFER_OK, 0, &sc->sc_bulkin_xfer); |
228 | if (error) { |
229 | goto fail; |
230 | } |
231 | |
232 | error = usbd_create_xfer(sc->sc_bulkout_pipe, |
233 | sc->sc_bulkout_maxpktsize, USBD_FORCE_SHORT_XFER, 0, |
234 | &sc->sc_bulkout_xfer); |
235 | if (error) { |
236 | goto fail; |
237 | } |
238 | sc->sc_bulkin_buffer = usbd_get_buffer(sc->sc_bulkin_xfer); |
239 | sc->sc_bulkout_buffer = usbd_get_buffer(sc->sc_bulkout_xfer); |
240 | |
241 | irmce_rescan(self, NULL, NULL); |
242 | return; |
243 | |
244 | fail: |
245 | if (sc->sc_bulkin_xfer) |
246 | usbd_destroy_xfer(sc->sc_bulkin_xfer); |
247 | if (sc->sc_bulkout_xfer) |
248 | usbd_destroy_xfer(sc->sc_bulkout_xfer); |
249 | } |
250 | |
251 | static int |
252 | irmce_detach(device_t self, int flags) |
253 | { |
254 | struct irmce_softc *sc = device_private(self); |
255 | int error; |
256 | |
257 | if (sc->sc_cirdev) { |
258 | error = config_detach(sc->sc_cirdev, flags); |
259 | if (error) |
260 | return error; |
261 | } |
262 | |
263 | if (sc->sc_bulkin_pipe) { |
264 | usbd_abort_pipe(sc->sc_bulkin_pipe); |
265 | } |
266 | if (sc->sc_bulkout_pipe) { |
267 | usbd_abort_pipe(sc->sc_bulkout_pipe); |
268 | } |
269 | if (sc->sc_bulkin_xfer) { |
270 | usbd_destroy_xfer(sc->sc_bulkin_xfer); |
271 | sc->sc_bulkin_buffer = NULL; |
272 | sc->sc_bulkin_xfer = NULL; |
273 | } |
274 | if (sc->sc_bulkout_xfer) { |
275 | usbd_destroy_xfer(sc->sc_bulkout_xfer); |
276 | sc->sc_bulkout_buffer = NULL; |
277 | sc->sc_bulkout_xfer = NULL; |
278 | } |
279 | if (sc->sc_bulkin_pipe) { |
280 | usbd_close_pipe(sc->sc_bulkin_pipe); |
281 | sc->sc_bulkin_pipe = NULL; |
282 | } |
283 | if (sc->sc_bulkout_pipe) { |
284 | usbd_close_pipe(sc->sc_bulkout_pipe); |
285 | sc->sc_bulkout_pipe = NULL; |
286 | } |
287 | |
288 | pmf_device_deregister(self); |
289 | |
290 | return 0; |
291 | } |
292 | |
293 | static int |
294 | irmce_activate(device_t self, enum devact act) |
295 | { |
296 | return 0; |
297 | } |
298 | |
299 | static int |
300 | irmce_rescan(device_t self, const char *ifattr, const int *locators) |
301 | { |
302 | struct irmce_softc *sc = device_private(self); |
303 | struct ir_attach_args iaa; |
304 | |
305 | if (sc->sc_cirdev == NULL) { |
306 | iaa.ia_type = IR_TYPE_CIR; |
307 | iaa.ia_methods = &irmce_cir_methods; |
308 | iaa.ia_handle = sc; |
309 | sc->sc_cirdev = config_found_ia(self, "irbus" , |
310 | &iaa, irmce_print); |
311 | } |
312 | |
313 | return 0; |
314 | } |
315 | |
316 | static int |
317 | irmce_print(void *priv, const char *pnp) |
318 | { |
319 | if (pnp) |
320 | aprint_normal("cir at %s" , pnp); |
321 | |
322 | return UNCONF; |
323 | } |
324 | |
325 | static void |
326 | irmce_childdet(device_t self, device_t child) |
327 | { |
328 | struct irmce_softc *sc = device_private(self); |
329 | |
330 | if (sc->sc_cirdev == child) |
331 | sc->sc_cirdev = NULL; |
332 | } |
333 | |
334 | static int |
335 | irmce_reset(struct irmce_softc *sc) |
336 | { |
337 | static const uint8_t reset_cmd[] = { 0x00, 0xff, 0xaa }; |
338 | uint8_t *p = sc->sc_bulkout_buffer; |
339 | usbd_status err; |
340 | uint32_t wlen; |
341 | unsigned int n; |
342 | |
343 | for (n = 0; n < __arraycount(reset_cmd); n++) |
344 | *p++ = reset_cmd[n]; |
345 | |
346 | wlen = sizeof(reset_cmd); |
347 | err = usbd_bulk_transfer(sc->sc_bulkout_xfer, sc->sc_bulkout_pipe, |
348 | USBD_FORCE_SHORT_XFER, USBD_DEFAULT_TIMEOUT, |
349 | sc->sc_bulkout_buffer, &wlen); |
350 | if (err != USBD_NORMAL_COMPLETION) { |
351 | if (err == USBD_INTERRUPTED) |
352 | return EINTR; |
353 | else if (err == USBD_TIMEOUT) |
354 | return ETIMEDOUT; |
355 | else |
356 | return EIO; |
357 | } |
358 | |
359 | return 0; |
360 | } |
361 | |
362 | static int |
363 | irmce_open(void *priv, int flag, int mode, struct proc *p) |
364 | { |
365 | struct irmce_softc *sc = priv; |
366 | int err = irmce_reset(sc); |
367 | if (err) { |
368 | aprint_error_dev(sc->sc_dev, |
369 | "couldn't reset device: %s\n" , usbd_errstr(err)); |
370 | } |
371 | sc->sc_ir_state = IRMCE_STATE_HEADER; |
372 | sc->sc_rc6_nhb = 0; |
373 | |
374 | return 0; |
375 | } |
376 | |
377 | static int |
378 | irmce_close(void *priv, int flag, int mode, struct proc *p) |
379 | { |
380 | struct irmce_softc *sc = priv; |
381 | |
382 | if (sc->sc_bulkin_pipe) { |
383 | usbd_abort_pipe(sc->sc_bulkin_pipe); |
384 | } |
385 | if (sc->sc_bulkout_pipe) { |
386 | usbd_abort_pipe(sc->sc_bulkout_pipe); |
387 | } |
388 | |
389 | return 0; |
390 | } |
391 | |
392 | static int |
393 | irmce_rc6_decode(struct irmce_softc *sc, uint8_t *buf, size_t buflen, |
394 | struct uio *uio) |
395 | { |
396 | bool *hb = &sc->sc_rc6_hb[0]; |
397 | unsigned int n; |
398 | int state, pulse; |
399 | uint32_t data; |
400 | uint8_t mode; |
401 | bool idle = false; |
402 | |
403 | for (n = 0; n < buflen; n++) { |
404 | state = (buf[n] & 0x80) ? 1 : 0; |
405 | pulse = (buf[n] & 0x7f) * 50; |
406 | |
407 | if (pulse >= 300 && pulse <= 600) { |
408 | hb[sc->sc_rc6_nhb++] = state; |
409 | } else if (pulse >= 680 && pulse <= 1080) { |
410 | hb[sc->sc_rc6_nhb++] = state; |
411 | hb[sc->sc_rc6_nhb++] = state; |
412 | } else if (pulse >= 1150 && pulse <= 1450) { |
413 | hb[sc->sc_rc6_nhb++] = state; |
414 | hb[sc->sc_rc6_nhb++] = state; |
415 | hb[sc->sc_rc6_nhb++] = state; |
416 | } else if (pulse >= 2400 && pulse <= 2800) { |
417 | hb[sc->sc_rc6_nhb++] = state; |
418 | hb[sc->sc_rc6_nhb++] = state; |
419 | hb[sc->sc_rc6_nhb++] = state; |
420 | hb[sc->sc_rc6_nhb++] = state; |
421 | hb[sc->sc_rc6_nhb++] = state; |
422 | hb[sc->sc_rc6_nhb++] = state; |
423 | } else if (pulse > 3000) { |
424 | if (sc->sc_rc6_nhb & 1) |
425 | hb[sc->sc_rc6_nhb++] = state; |
426 | idle = true; |
427 | break; |
428 | } else { |
429 | aprint_debug_dev(sc->sc_dev, |
430 | "error parsing RC6 stream (pulse=%d)\n" , pulse); |
431 | return EIO; |
432 | } |
433 | } |
434 | |
435 | if (!idle) |
436 | return 0; |
437 | |
438 | if (sc->sc_rc6_nhb < 20) { |
439 | aprint_debug_dev(sc->sc_dev, "not enough RC6 data\n" ); |
440 | return EIO; |
441 | } |
442 | |
443 | /* RC6 leader 11111100 */ |
444 | if (!hb[0] || !hb[1] || !hb[2] || !hb[3] || !hb[4] || !hb[5] || |
445 | hb[6] || hb[7]) { |
446 | aprint_debug_dev(sc->sc_dev, "bad RC6 leader\n" ); |
447 | return EIO; |
448 | } |
449 | |
450 | /* start bit 10 */ |
451 | if (!hb[8] || hb[9]) { |
452 | aprint_debug_dev(sc->sc_dev, "missing RC6 start bit\n" ); |
453 | return EIO; |
454 | } |
455 | |
456 | /* mode info */ |
457 | mode = 0x00; |
458 | for (n = 10; n < 15; n += 2) { |
459 | if (hb[n] && !hb[n + 1]) |
460 | mode = (mode << 1) | 1; |
461 | else if (!hb[n] && hb[n + 1]) |
462 | mode = (mode << 1) | 0; |
463 | else { |
464 | aprint_debug_dev(sc->sc_dev, "bad RC6 mode bits\n" ); |
465 | return EIO; |
466 | } |
467 | } |
468 | |
469 | data = 0; |
470 | for (n = 20; n < sc->sc_rc6_nhb; n += 2) { |
471 | if (hb[n] && !hb[n + 1]) |
472 | data = (data << 1) | 1; |
473 | else if (!hb[n] && hb[n + 1]) |
474 | data = (data << 1) | 0; |
475 | else { |
476 | aprint_debug_dev(sc->sc_dev, "bad RC6 data bits\n" ); |
477 | return EIO; |
478 | } |
479 | } |
480 | |
481 | sc->sc_rc6_nhb = 0; |
482 | |
483 | return uiomove(&data, sizeof(data), uio); |
484 | } |
485 | |
486 | static int |
487 | irmce_process(struct irmce_softc *sc, uint8_t *buf, size_t buflen, |
488 | struct uio *uio) |
489 | { |
490 | uint8_t *p = buf; |
491 | uint8_t data, cmd; |
492 | int error; |
493 | |
494 | while (p - buf < (ssize_t)buflen) { |
495 | switch (sc->sc_ir_state) { |
496 | case IRMCE_STATE_HEADER: |
497 | sc->sc_ir_header = data = *p++; |
498 | if ((data & 0xe0) == 0x80 && (data & 0x1f) != 0x1f) { |
499 | sc->sc_ir_bufused = 0; |
500 | sc->sc_ir_resid = data & 0x1f; |
501 | sc->sc_ir_state = IRMCE_STATE_IRDATA; |
502 | if (sc->sc_ir_resid > sizeof(sc->sc_ir_buf)) |
503 | return EIO; |
504 | if (sc->sc_ir_resid == 0) |
505 | sc->sc_ir_state = IRMCE_STATE_HEADER; |
506 | } else { |
507 | sc->sc_ir_state = IRMCE_STATE_CMDHEADER; |
508 | } |
509 | break; |
510 | case IRMCE_STATE_CMDHEADER: |
511 | cmd = *p++; |
512 | data = sc->sc_ir_header; |
513 | if (data == 0x00 && cmd == 0x9f) |
514 | sc->sc_ir_resid = 1; |
515 | else if (data == 0xff && cmd == 0x0b) |
516 | sc->sc_ir_resid = 2; |
517 | else if (data == 0x9f) { |
518 | if (cmd == 0x04 || cmd == 0x06 || |
519 | cmd == 0x0c || cmd == 0x15) { |
520 | sc->sc_ir_resid = 2; |
521 | } else if (cmd == 0x01 || cmd == 0x08 || |
522 | cmd == 0x14) { |
523 | sc->sc_ir_resid = 1; |
524 | } |
525 | } |
526 | if (sc->sc_ir_resid > 0) |
527 | sc->sc_ir_state = IRMCE_STATE_CMDDATA; |
528 | else |
529 | sc->sc_ir_state = IRMCE_STATE_HEADER; |
530 | break; |
531 | case IRMCE_STATE_IRDATA: |
532 | sc->sc_ir_resid--; |
533 | sc->sc_ir_buf[sc->sc_ir_bufused++] = *p; |
534 | p++; |
535 | if (sc->sc_ir_resid == 0) { |
536 | sc->sc_ir_state = IRMCE_STATE_HEADER; |
537 | error = irmce_rc6_decode(sc, |
538 | sc->sc_ir_buf, sc->sc_ir_bufused, uio); |
539 | if (error) |
540 | sc->sc_rc6_nhb = 0; |
541 | } |
542 | break; |
543 | case IRMCE_STATE_CMDDATA: |
544 | p++; |
545 | sc->sc_ir_resid--; |
546 | if (sc->sc_ir_resid == 0) |
547 | sc->sc_ir_state = IRMCE_STATE_HEADER; |
548 | break; |
549 | } |
550 | |
551 | } |
552 | |
553 | return 0; |
554 | } |
555 | |
556 | static int |
557 | irmce_read(void *priv, struct uio *uio, int flag) |
558 | { |
559 | struct irmce_softc *sc = priv; |
560 | usbd_status err; |
561 | uint32_t rlen; |
562 | int error = 0; |
563 | |
564 | while (uio->uio_resid > 0) { |
565 | rlen = sc->sc_bulkin_maxpktsize; |
566 | err = usbd_bulk_transfer(sc->sc_bulkin_xfer, |
567 | sc->sc_bulkin_pipe, USBD_SHORT_XFER_OK, |
568 | USBD_DEFAULT_TIMEOUT, sc->sc_bulkin_buffer, &rlen); |
569 | if (err != USBD_NORMAL_COMPLETION) { |
570 | if (err == USBD_INTERRUPTED) |
571 | return EINTR; |
572 | else if (err == USBD_TIMEOUT) |
573 | continue; |
574 | else |
575 | return EIO; |
576 | } |
577 | |
578 | if (sc->sc_raw) { |
579 | error = uiomove(sc->sc_bulkin_buffer, rlen, uio); |
580 | break; |
581 | } else { |
582 | error = irmce_process(sc, sc->sc_bulkin_buffer, |
583 | rlen, uio); |
584 | if (error) |
585 | break; |
586 | } |
587 | } |
588 | |
589 | return error; |
590 | } |
591 | |
592 | static int |
593 | irmce_write(void *priv, struct uio *uio, int flag) |
594 | { |
595 | return EIO; |
596 | } |
597 | |
598 | static int |
599 | irmce_setparams(void *priv, struct cir_params *params) |
600 | { |
601 | struct irmce_softc *sc = priv; |
602 | |
603 | if (params->raw > 1) |
604 | return EINVAL; |
605 | sc->sc_raw = params->raw; |
606 | |
607 | return 0; |
608 | } |
609 | |
610 | MODULE(MODULE_CLASS_DRIVER, irmce, NULL); |
611 | |
612 | #ifdef _MODULE |
613 | #include "ioconf.c" |
614 | #endif |
615 | |
616 | static int |
617 | irmce_modcmd(modcmd_t cmd, void *opaque) |
618 | { |
619 | switch (cmd) { |
620 | case MODULE_CMD_INIT: |
621 | #ifdef _MODULE |
622 | return config_init_component(cfdriver_ioconf_irmce, |
623 | cfattach_ioconf_irmce, cfdata_ioconf_irmce); |
624 | #else |
625 | return 0; |
626 | #endif |
627 | case MODULE_CMD_FINI: |
628 | #ifdef _MODULE |
629 | return config_fini_component(cfdriver_ioconf_irmce, |
630 | cfattach_ioconf_irmce, cfdata_ioconf_irmce); |
631 | #else |
632 | return 0; |
633 | #endif |
634 | default: |
635 | return ENOTTY; |
636 | } |
637 | } |
638 | |