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