1/* $NetBSD: irmce.c,v 1.9 2023/05/10 00:12:28 riastradh 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.9 2023/05/10 00:12:28 riastradh 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 54enum irmce_state { 55 IRMCE_STATE_HEADER, 56 IRMCE_STATE_IRDATA, 57 IRMCE_STATE_CMDHEADER, 58 IRMCE_STATE_CMDDATA, 59}; 60 61struct 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 sc_ir_header; 87 88 bool sc_rc6_hb[256]; 89 size_t sc_rc6_nhb; 90}; 91 92static int irmce_match(device_t, cfdata_t, void *); 93static void irmce_attach(device_t, device_t, void *); 94static int irmce_detach(device_t, int); 95static void irmce_childdet(device_t, device_t); 96static int irmce_activate(device_t, enum devact); 97static int irmce_rescan(device_t, const char *, const int *); 98 99static int irmce_print(void *, const char *); 100 101static int irmce_reset(struct irmce_softc *); 102 103static int irmce_open(void *, int, int, struct proc *); 104static int irmce_close(void *, int, int, struct proc *); 105static int irmce_read(void *, struct uio *, int); 106static int irmce_write(void *, struct uio *, int); 107static int irmce_setparams(void *, struct cir_params *); 108 109static 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 117static const struct { 118 uint16_t vendor; 119 uint16_t product; 120} irmce_devices[] = { 121 { USB_VENDOR_SMK, USB_PRODUCT_SMK_MCE_IR }, 122}; 123 124CFATTACH_DECL2_NEW(irmce, sizeof(struct irmce_softc), 125 irmce_match, irmce_attach, irmce_detach, irmce_activate, 126 irmce_rescan, irmce_childdet); 127 128static int 129irmce_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 143static void 144irmce_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 if (!pmf_device_register(self, NULL, NULL)) 154 aprint_error_dev(self, "couldn't establish power handler\n"); 155 156 aprint_naive("\n"); 157 158 devinfop = usbd_devinfo_alloc(uiaa->uiaa_device, 0); 159 aprint_normal(": %s\n", devinfop); 160 usbd_devinfo_free(devinfop); 161 162 sc->sc_dev = self; 163 sc->sc_udev = uiaa->uiaa_device; 164 sc->sc_iface = uiaa->uiaa_iface; 165 166 nep = 0; 167 usbd_endpoint_count(sc->sc_iface, &nep); 168 sc->sc_bulkin_ep = sc->sc_bulkout_ep = -1; 169 for (i = 0; i < nep; i++) { 170 int dir, type; 171 172 ed = usbd_interface2endpoint_descriptor(sc->sc_iface, i); 173 if (ed == NULL) { 174 aprint_error_dev(self, 175 "couldn't read endpoint descriptor %d\n", i); 176 continue; 177 } 178 179 dir = UE_GET_DIR(ed->bEndpointAddress); 180 type = UE_GET_XFERTYPE(ed->bmAttributes); 181 182 if (type != UE_BULK) 183 continue; 184 185 if (dir == UE_DIR_IN && sc->sc_bulkin_ep == -1) { 186 sc->sc_bulkin_ep = ed->bEndpointAddress; 187 sc->sc_bulkin_maxpktsize = 188 UE_GET_SIZE(UGETW(ed->wMaxPacketSize)) * 189 (UE_GET_TRANS(UGETW(ed->wMaxPacketSize)) + 1); 190 } 191 if (dir == UE_DIR_OUT && sc->sc_bulkout_ep == -1) { 192 sc->sc_bulkout_ep = ed->bEndpointAddress; 193 sc->sc_bulkout_maxpktsize = 194 UE_GET_SIZE(UGETW(ed->wMaxPacketSize)) * 195 (UE_GET_TRANS(UGETW(ed->wMaxPacketSize)) + 1); 196 } 197 } 198 199 aprint_debug_dev(self, "in 0x%02x/%d out 0x%02x/%d\n", 200 sc->sc_bulkin_ep, sc->sc_bulkin_maxpktsize, 201 sc->sc_bulkout_ep, sc->sc_bulkout_maxpktsize); 202 203 if (sc->sc_bulkin_maxpktsize < 16 || sc->sc_bulkout_maxpktsize < 16) { 204 aprint_error_dev(self, "bad maxpktsize\n"); 205 return; 206 } 207 usbd_status err; 208 209 err = usbd_open_pipe(sc->sc_iface, sc->sc_bulkin_ep, 210 USBD_EXCLUSIVE_USE, &sc->sc_bulkin_pipe); 211 if (err) { 212 aprint_error_dev(sc->sc_dev, 213 "couldn't open bulk-in pipe: %s\n", usbd_errstr(err)); 214 return; 215 } 216 err = usbd_open_pipe(sc->sc_iface, sc->sc_bulkout_ep, 217 USBD_EXCLUSIVE_USE, &sc->sc_bulkout_pipe); 218 if (err) { 219 aprint_error_dev(sc->sc_dev, 220 "couldn't open bulk-out pipe: %s\n", usbd_errstr(err)); 221 usbd_close_pipe(sc->sc_bulkin_pipe); 222 sc->sc_bulkin_pipe = NULL; 223 return; 224 } 225 226 int error; 227 error = usbd_create_xfer(sc->sc_bulkin_pipe, sc->sc_bulkin_maxpktsize, 228 0, 0, &sc->sc_bulkin_xfer); 229 if (error) { 230 goto fail; 231 } 232 233 error = usbd_create_xfer(sc->sc_bulkout_pipe, 234 sc->sc_bulkout_maxpktsize, USBD_FORCE_SHORT_XFER, 0, 235 &sc->sc_bulkout_xfer); 236 if (error) { 237 goto fail; 238 } 239 sc->sc_bulkin_buffer = usbd_get_buffer(sc->sc_bulkin_xfer); 240 sc->sc_bulkout_buffer = usbd_get_buffer(sc->sc_bulkout_xfer); 241 242 irmce_rescan(self, NULL, NULL); 243 return; 244 245fail: 246 if (sc->sc_bulkin_xfer) 247 usbd_destroy_xfer(sc->sc_bulkin_xfer); 248 if (sc->sc_bulkout_xfer) 249 usbd_destroy_xfer(sc->sc_bulkout_xfer); 250} 251 252static int 253irmce_detach(device_t self, int flags) 254{ 255 struct irmce_softc *sc = device_private(self); 256 int error; 257 258 error = config_detach_children(self, flags); 259 if (error) 260 return error; 261 262 if (sc->sc_bulkin_pipe) { 263 usbd_abort_pipe(sc->sc_bulkin_pipe); 264 } 265 if (sc->sc_bulkout_pipe) { 266 usbd_abort_pipe(sc->sc_bulkout_pipe); 267 } 268 if (sc->sc_bulkin_xfer) { 269 usbd_destroy_xfer(sc->sc_bulkin_xfer); 270 sc->sc_bulkin_buffer = NULL; 271 sc->sc_bulkin_xfer = NULL; 272 } 273 if (sc->sc_bulkout_xfer) { 274 usbd_destroy_xfer(sc->sc_bulkout_xfer); 275 sc->sc_bulkout_buffer = NULL; 276 sc->sc_bulkout_xfer = NULL; 277 } 278 if (sc->sc_bulkin_pipe) { 279 usbd_close_pipe(sc->sc_bulkin_pipe); 280 sc->sc_bulkin_pipe = NULL; 281 } 282 if (sc->sc_bulkout_pipe) { 283 usbd_close_pipe(sc->sc_bulkout_pipe); 284 sc->sc_bulkout_pipe = NULL; 285 } 286 287 pmf_device_deregister(self); 288 289 return 0; 290} 291 292static int 293irmce_activate(device_t self, enum devact act) 294{ 295 return 0; 296} 297 298static int 299irmce_rescan(device_t self, const char *ifattr, const int *locators) 300{ 301 struct irmce_softc *sc = device_private(self); 302 struct ir_attach_args iaa; 303 304 if (sc->sc_cirdev == NULL) { 305 iaa.ia_type = IR_TYPE_CIR; 306 iaa.ia_methods = &irmce_cir_methods; 307 iaa.ia_handle = sc; 308 sc->sc_cirdev = 309 config_found(self, &iaa, irmce_print, CFARGS_NONE); 310 } 311 312 return 0; 313} 314 315static int 316irmce_print(void *priv, const char *pnp) 317{ 318 if (pnp) 319 aprint_normal("cir at %s", pnp); 320 321 return UNCONF; 322} 323 324static void 325irmce_childdet(device_t self, device_t child) 326{ 327 struct irmce_softc *sc = device_private(self); 328 329 if (sc->sc_cirdev == child) 330 sc->sc_cirdev = NULL; 331} 332 333static int 334irmce_reset(struct irmce_softc *sc) 335{ 336 static const uint8_t reset_cmd[] = { 0x00, 0xff, 0xaa }; 337 uint8_t *p = sc->sc_bulkout_buffer; 338 usbd_status err; 339 uint32_t wlen; 340 unsigned int n; 341 342 for (n = 0; n < __arraycount(reset_cmd); n++) 343 *p++ = reset_cmd[n]; 344 345 wlen = sizeof(reset_cmd); 346 err = usbd_bulk_transfer(sc->sc_bulkout_xfer, sc->sc_bulkout_pipe, 347 USBD_FORCE_SHORT_XFER, USBD_DEFAULT_TIMEOUT, 348 sc->sc_bulkout_buffer, &wlen); 349 if (err != USBD_NORMAL_COMPLETION) { 350 if (err == USBD_INTERRUPTED) 351 return EINTR; 352 else if (err == USBD_TIMEOUT) 353 return ETIMEDOUT; 354 else 355 return EIO; 356 } 357 358 return 0; 359} 360 361static int 362irmce_open(void *priv, int flag, int mode, struct proc *p) 363{ 364 struct irmce_softc *sc = priv; 365 int err = irmce_reset(sc); 366 if (err) { 367 aprint_error_dev(sc->sc_dev, 368 "couldn't reset device: %s\n", usbd_errstr(err)); 369 } 370 sc->sc_ir_state = IRMCE_STATE_HEADER; 371 sc->sc_rc6_nhb = 0; 372 373 return 0; 374} 375 376static int 377irmce_close(void *priv, int flag, int mode, struct proc *p) 378{ 379 struct irmce_softc *sc = priv; 380 381 if (sc->sc_bulkin_pipe) { 382 usbd_abort_pipe(sc->sc_bulkin_pipe); 383 } 384 if (sc->sc_bulkout_pipe) { 385 usbd_abort_pipe(sc->sc_bulkout_pipe); 386 } 387 388 return 0; 389} 390 391static int 392irmce_rc6_decode(struct irmce_softc *sc, uint8_t *buf, size_t buflen, 393 struct uio *uio) 394{ 395 bool *hb = &sc->sc_rc6_hb[0]; 396 unsigned int n; 397 int state, pulse; 398 uint32_t data; 399 uint8_t mode; 400 bool idle = false; 401 402 for (n = 0; n < buflen; n++) { 403 state = (buf[n] & 0x80) ? 1 : 0; 404 pulse = (buf[n] & 0x7f) * 50; 405 406 if (pulse >= 300 && pulse <= 600) { 407 hb[sc->sc_rc6_nhb++] = state; 408 } else if (pulse >= 680 && pulse <= 1080) { 409 hb[sc->sc_rc6_nhb++] = state; 410 hb[sc->sc_rc6_nhb++] = state; 411 } else if (pulse >= 1150 && pulse <= 1450) { 412 hb[sc->sc_rc6_nhb++] = state; 413 hb[sc->sc_rc6_nhb++] = state; 414 hb[sc->sc_rc6_nhb++] = state; 415 } else if (pulse >= 2400 && pulse <= 2800) { 416 hb[sc->sc_rc6_nhb++] = state; 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 } else if (pulse > 3000) { 423 if (sc->sc_rc6_nhb & 1) 424 hb[sc->sc_rc6_nhb++] = state; 425 idle = true; 426 break; 427 } else { 428 aprint_debug_dev(sc->sc_dev, 429 "error parsing RC6 stream (pulse=%d)\n", pulse); 430 return EIO; 431 } 432 } 433 434 if (!idle) 435 return 0; 436 437 if (sc->sc_rc6_nhb < 20) { 438 aprint_debug_dev(sc->sc_dev, "not enough RC6 data\n"); 439 return EIO; 440 } 441 442 /* RC6 leader 11111100 */ 443 if (!hb[0] || !hb[1] || !hb[2] || !hb[3] || !hb[4] || !hb[5] || 444 hb[6] || hb[7]) { 445 aprint_debug_dev(sc->sc_dev, "bad RC6 leader\n"); 446 return EIO; 447 } 448 449 /* start bit 10 */ 450 if (!hb[8] || hb[9]) { 451 aprint_debug_dev(sc->sc_dev, "missing RC6 start bit\n"); 452 return EIO; 453 } 454 455 /* mode info */ 456 mode = 0x00; 457 for (n = 10; n < 15; n += 2) { 458 if (hb[n] && !hb[n + 1]) 459 mode = (mode << 1) | 1; 460 else if (!hb[n] && hb[n + 1]) 461 mode = (mode << 1) | 0; 462 else { 463 aprint_debug_dev(sc->sc_dev, "bad RC6 mode bits\n"); 464 return EIO; 465 } 466 } 467 468 data = 0; 469 for (n = 20; n < sc->sc_rc6_nhb; n += 2) { 470 if (hb[n] && !hb[n + 1]) 471 data = (data << 1) | 1; 472 else if (!hb[n] && hb[n + 1]) 473 data = (data << 1) | 0; 474 else { 475 aprint_debug_dev(sc->sc_dev, "bad RC6 data bits\n"); 476 return EIO; 477 } 478 } 479 480 sc->sc_rc6_nhb = 0; 481 482 return uiomove(&data, sizeof(data), uio); 483} 484 485static int 486irmce_process(struct irmce_softc *sc, uint8_t *buf, size_t buflen, 487 struct uio *uio) 488{ 489 uint8_t *p = buf; 490 uint8_t data, cmd; 491 int error; 492 493 while (p - buf < (ssize_t)buflen) { 494 switch (sc->sc_ir_state) { 495 case IRMCE_STATE_HEADER: 496 sc->sc_ir_header = data = *p++; 497 if ((data & 0xe0) == 0x80 && (data & 0x1f) != 0x1f) { 498 sc->sc_ir_bufused = 0; 499 sc->sc_ir_resid = data & 0x1f; 500 sc->sc_ir_state = IRMCE_STATE_IRDATA; 501 if (sc->sc_ir_resid > sizeof(sc->sc_ir_buf)) 502 return EIO; 503 if (sc->sc_ir_resid == 0) 504 sc->sc_ir_state = IRMCE_STATE_HEADER; 505 } else { 506 sc->sc_ir_state = IRMCE_STATE_CMDHEADER; 507 } 508 break; 509 case IRMCE_STATE_CMDHEADER: 510 cmd = *p++; 511 data = sc->sc_ir_header; 512 if (data == 0x00 && cmd == 0x9f) 513 sc->sc_ir_resid = 1; 514 else if (data == 0xff && cmd == 0x0b) 515 sc->sc_ir_resid = 2; 516 else if (data == 0x9f) { 517 if (cmd == 0x04 || cmd == 0x06 || 518 cmd == 0x0c || cmd == 0x15) { 519 sc->sc_ir_resid = 2; 520 } else if (cmd == 0x01 || cmd == 0x08 || 521 cmd == 0x14) { 522 sc->sc_ir_resid = 1; 523 } 524 } 525 if (sc->sc_ir_resid > 0) 526 sc->sc_ir_state = IRMCE_STATE_CMDDATA; 527 else 528 sc->sc_ir_state = IRMCE_STATE_HEADER; 529 break; 530 case IRMCE_STATE_IRDATA: 531 sc->sc_ir_resid--; 532 sc->sc_ir_buf[sc->sc_ir_bufused++] = *p; 533 p++; 534 if (sc->sc_ir_resid == 0) { 535 sc->sc_ir_state = IRMCE_STATE_HEADER; 536 error = irmce_rc6_decode(sc, 537 sc->sc_ir_buf, sc->sc_ir_bufused, uio); 538 if (error) 539 sc->sc_rc6_nhb = 0; 540 } 541 break; 542 case IRMCE_STATE_CMDDATA: 543 p++; 544 sc->sc_ir_resid--; 545 if (sc->sc_ir_resid == 0) 546 sc->sc_ir_state = IRMCE_STATE_HEADER; 547 break; 548 } 549 550 } 551 552 return 0; 553} 554 555static int 556irmce_read(void *priv, struct uio *uio, int flag) 557{ 558 struct irmce_softc *sc = priv; 559 usbd_status err; 560 uint32_t rlen; 561 int error = 0; 562 563 while (uio->uio_resid > 0) { 564 rlen = sc->sc_bulkin_maxpktsize; 565 err = usbd_bulk_transfer(sc->sc_bulkin_xfer, 566 sc->sc_bulkin_pipe, USBD_SHORT_XFER_OK, 567 USBD_DEFAULT_TIMEOUT, sc->sc_bulkin_buffer, &rlen); 568 if (err != USBD_NORMAL_COMPLETION) { 569 if (err == USBD_INTERRUPTED) 570 return EINTR; 571 else if (err == USBD_TIMEOUT) 572 continue; 573 else 574 return EIO; 575 } 576 577 if (sc->sc_raw) { 578 error = uiomove(sc->sc_bulkin_buffer, rlen, uio); 579 break; 580 } else { 581 error = irmce_process(sc, sc->sc_bulkin_buffer, 582 rlen, uio); 583 if (error) 584 break; 585 } 586 } 587 588 return error; 589} 590 591static int 592irmce_write(void *priv, struct uio *uio, int flag) 593{ 594 return EIO; 595} 596 597static int 598irmce_setparams(void *priv, struct cir_params *params) 599{ 600 struct irmce_softc *sc = priv; 601 602 if (params->raw > 1) 603 return EINVAL; 604 sc->sc_raw = params->raw; 605 606 return 0; 607} 608 609MODULE(MODULE_CLASS_DRIVER, irmce, NULL); 610 611#ifdef _MODULE 612#include "ioconf.c" 613#endif 614 615static int 616irmce_modcmd(modcmd_t cmd, void *opaque) 617{ 618 switch (cmd) { 619 case MODULE_CMD_INIT: 620#ifdef _MODULE 621 return config_init_component(cfdriver_ioconf_irmce, 622 cfattach_ioconf_irmce, cfdata_ioconf_irmce); 623#else 624 return 0; 625#endif 626 case MODULE_CMD_FINI: 627#ifdef _MODULE 628 return config_fini_component(cfdriver_ioconf_irmce, 629 cfattach_ioconf_irmce, cfdata_ioconf_irmce); 630#else 631 return 0; 632#endif 633 default: 634 return ENOTTY; 635 } 636} 637