1*20ab753eSriastradh /* $NetBSD: irmce.c,v 1.9 2023/05/10 00:12:28 riastradh Exp $ */
256161535Sjmcneill
356161535Sjmcneill /*-
456161535Sjmcneill * Copyright (c) 2011 Jared D. McNeill <jmcneill@invisible.ca>
556161535Sjmcneill * All rights reserved.
656161535Sjmcneill *
756161535Sjmcneill * Redistribution and use in source and binary forms, with or without
856161535Sjmcneill * modification, are permitted provided that the following conditions
956161535Sjmcneill * are met:
1056161535Sjmcneill * 1. Redistributions of source code must retain the above copyright
1156161535Sjmcneill * notice, this list of conditions and the following disclaimer.
1256161535Sjmcneill * 2. Redistributions in binary form must reproduce the above copyright
1356161535Sjmcneill * notice, this list of conditions and the following disclaimer in the
1456161535Sjmcneill * documentation and/or other materials provided with the distribution.
1556161535Sjmcneill *
1656161535Sjmcneill * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS''
1756161535Sjmcneill * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
1856161535Sjmcneill * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
1956161535Sjmcneill * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
2056161535Sjmcneill * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2156161535Sjmcneill * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2256161535Sjmcneill * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2356161535Sjmcneill * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2456161535Sjmcneill * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2556161535Sjmcneill * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2656161535Sjmcneill * POSSIBILITY OF SUCH DAMAGE.
2756161535Sjmcneill */
2856161535Sjmcneill
2956161535Sjmcneill /*
3056161535Sjmcneill * IR receiver/transceiver for Windows Media Center
3156161535Sjmcneill */
3256161535Sjmcneill
3356161535Sjmcneill #include <sys/cdefs.h>
34*20ab753eSriastradh __KERNEL_RCSID(0, "$NetBSD: irmce.c,v 1.9 2023/05/10 00:12:28 riastradh Exp $");
3556161535Sjmcneill
3656161535Sjmcneill #include <sys/types.h>
3756161535Sjmcneill #include <sys/param.h>
3856161535Sjmcneill #include <sys/systm.h>
3956161535Sjmcneill #include <sys/device.h>
4056161535Sjmcneill #include <sys/conf.h>
4156161535Sjmcneill #include <sys/bus.h>
4256161535Sjmcneill #include <sys/select.h>
4356161535Sjmcneill #include <sys/module.h>
4456161535Sjmcneill
4556161535Sjmcneill #include <dev/usb/usb.h>
4656161535Sjmcneill #include <dev/usb/usbdi.h>
4756161535Sjmcneill #include <dev/usb/usbdi_util.h>
4856161535Sjmcneill #include <dev/usb/usbdevs.h>
4956161535Sjmcneill
5056161535Sjmcneill #include <dev/ir/ir.h>
5156161535Sjmcneill #include <dev/ir/cirio.h>
5256161535Sjmcneill #include <dev/ir/cirvar.h>
5356161535Sjmcneill
5456161535Sjmcneill enum irmce_state {
5556161535Sjmcneill IRMCE_STATE_HEADER,
5656161535Sjmcneill IRMCE_STATE_IRDATA,
5756161535Sjmcneill IRMCE_STATE_CMDHEADER,
5856161535Sjmcneill IRMCE_STATE_CMDDATA,
5956161535Sjmcneill };
6056161535Sjmcneill
6156161535Sjmcneill struct irmce_softc {
6256161535Sjmcneill device_t sc_dev;
6356161535Sjmcneill device_t sc_cirdev;
6456161535Sjmcneill
654e8e6643Sskrll struct usbd_device * sc_udev;
664e8e6643Sskrll struct usbd_interface * sc_iface;
6756161535Sjmcneill
6856161535Sjmcneill int sc_bulkin_ep;
6956161535Sjmcneill uint16_t sc_bulkin_maxpktsize;
704e8e6643Sskrll struct usbd_pipe * sc_bulkin_pipe;
714e8e6643Sskrll struct usbd_xfer * sc_bulkin_xfer;
7256161535Sjmcneill uint8_t * sc_bulkin_buffer;
7356161535Sjmcneill
7456161535Sjmcneill int sc_bulkout_ep;
7556161535Sjmcneill uint16_t sc_bulkout_maxpktsize;
764e8e6643Sskrll struct usbd_pipe * sc_bulkout_pipe;
774e8e6643Sskrll struct usbd_xfer * sc_bulkout_xfer;
7856161535Sjmcneill uint8_t * sc_bulkout_buffer;
7956161535Sjmcneill
8056161535Sjmcneill bool sc_raw;
8156161535Sjmcneill
8256161535Sjmcneill uint8_t sc_ir_buf[16];
8356161535Sjmcneill size_t sc_ir_bufused;
8456161535Sjmcneill size_t sc_ir_resid;
8556161535Sjmcneill enum irmce_state sc_ir_state;
8656161535Sjmcneill uint8_t sc_ir_header;
8756161535Sjmcneill
8856161535Sjmcneill bool sc_rc6_hb[256];
8956161535Sjmcneill size_t sc_rc6_nhb;
9056161535Sjmcneill };
9156161535Sjmcneill
9256161535Sjmcneill static int irmce_match(device_t, cfdata_t, void *);
9356161535Sjmcneill static void irmce_attach(device_t, device_t, void *);
9456161535Sjmcneill static int irmce_detach(device_t, int);
9556161535Sjmcneill static void irmce_childdet(device_t, device_t);
9656161535Sjmcneill static int irmce_activate(device_t, enum devact);
9756161535Sjmcneill static int irmce_rescan(device_t, const char *, const int *);
9856161535Sjmcneill
9956161535Sjmcneill static int irmce_print(void *, const char *);
10056161535Sjmcneill
10156161535Sjmcneill static int irmce_reset(struct irmce_softc *);
10256161535Sjmcneill
10356161535Sjmcneill static int irmce_open(void *, int, int, struct proc *);
10456161535Sjmcneill static int irmce_close(void *, int, int, struct proc *);
10556161535Sjmcneill static int irmce_read(void *, struct uio *, int);
10656161535Sjmcneill static int irmce_write(void *, struct uio *, int);
10756161535Sjmcneill static int irmce_setparams(void *, struct cir_params *);
10856161535Sjmcneill
10956161535Sjmcneill static const struct cir_methods irmce_cir_methods = {
11056161535Sjmcneill .im_open = irmce_open,
11156161535Sjmcneill .im_close = irmce_close,
11256161535Sjmcneill .im_read = irmce_read,
11356161535Sjmcneill .im_write = irmce_write,
11456161535Sjmcneill .im_setparams = irmce_setparams,
11556161535Sjmcneill };
11656161535Sjmcneill
11756161535Sjmcneill static const struct {
11856161535Sjmcneill uint16_t vendor;
11956161535Sjmcneill uint16_t product;
12056161535Sjmcneill } irmce_devices[] = {
12156161535Sjmcneill { USB_VENDOR_SMK, USB_PRODUCT_SMK_MCE_IR },
12256161535Sjmcneill };
12356161535Sjmcneill
12456161535Sjmcneill CFATTACH_DECL2_NEW(irmce, sizeof(struct irmce_softc),
12556161535Sjmcneill irmce_match, irmce_attach, irmce_detach, irmce_activate,
12656161535Sjmcneill irmce_rescan, irmce_childdet);
12756161535Sjmcneill
12856161535Sjmcneill static int
irmce_match(device_t parent,cfdata_t match,void * opaque)12956161535Sjmcneill irmce_match(device_t parent, cfdata_t match, void *opaque)
13056161535Sjmcneill {
13156161535Sjmcneill struct usbif_attach_arg *uiaa = opaque;
13256161535Sjmcneill unsigned int i;
13356161535Sjmcneill
13456161535Sjmcneill for (i = 0; i < __arraycount(irmce_devices); i++) {
1354e8e6643Sskrll if (irmce_devices[i].vendor == uiaa->uiaa_vendor &&
1364e8e6643Sskrll irmce_devices[i].product == uiaa->uiaa_product)
13756161535Sjmcneill return UMATCH_VENDOR_PRODUCT;
13856161535Sjmcneill }
13956161535Sjmcneill
14056161535Sjmcneill return UMATCH_NONE;
14156161535Sjmcneill }
14256161535Sjmcneill
14356161535Sjmcneill static void
irmce_attach(device_t parent,device_t self,void * opaque)14456161535Sjmcneill irmce_attach(device_t parent, device_t self, void *opaque)
14556161535Sjmcneill {
14656161535Sjmcneill struct irmce_softc *sc = device_private(self);
14756161535Sjmcneill struct usbif_attach_arg *uiaa = opaque;
14856161535Sjmcneill usb_endpoint_descriptor_t *ed;
14956161535Sjmcneill char *devinfop;
15056161535Sjmcneill unsigned int i;
15156161535Sjmcneill uint8_t nep;
15256161535Sjmcneill
153b8f12c7fSmaya if (!pmf_device_register(self, NULL, NULL))
154b8f12c7fSmaya aprint_error_dev(self, "couldn't establish power handler\n");
15556161535Sjmcneill
15656161535Sjmcneill aprint_naive("\n");
15756161535Sjmcneill
1584e8e6643Sskrll devinfop = usbd_devinfo_alloc(uiaa->uiaa_device, 0);
15956161535Sjmcneill aprint_normal(": %s\n", devinfop);
16056161535Sjmcneill usbd_devinfo_free(devinfop);
16156161535Sjmcneill
16256161535Sjmcneill sc->sc_dev = self;
1634e8e6643Sskrll sc->sc_udev = uiaa->uiaa_device;
1644e8e6643Sskrll sc->sc_iface = uiaa->uiaa_iface;
16556161535Sjmcneill
16656161535Sjmcneill nep = 0;
16756161535Sjmcneill usbd_endpoint_count(sc->sc_iface, &nep);
16856161535Sjmcneill sc->sc_bulkin_ep = sc->sc_bulkout_ep = -1;
16956161535Sjmcneill for (i = 0; i < nep; i++) {
17056161535Sjmcneill int dir, type;
17156161535Sjmcneill
17256161535Sjmcneill ed = usbd_interface2endpoint_descriptor(sc->sc_iface, i);
17356161535Sjmcneill if (ed == NULL) {
17456161535Sjmcneill aprint_error_dev(self,
17556161535Sjmcneill "couldn't read endpoint descriptor %d\n", i);
17656161535Sjmcneill continue;
17756161535Sjmcneill }
17856161535Sjmcneill
17956161535Sjmcneill dir = UE_GET_DIR(ed->bEndpointAddress);
18056161535Sjmcneill type = UE_GET_XFERTYPE(ed->bmAttributes);
18156161535Sjmcneill
18256161535Sjmcneill if (type != UE_BULK)
18356161535Sjmcneill continue;
18456161535Sjmcneill
18556161535Sjmcneill if (dir == UE_DIR_IN && sc->sc_bulkin_ep == -1) {
18656161535Sjmcneill sc->sc_bulkin_ep = ed->bEndpointAddress;
18756161535Sjmcneill sc->sc_bulkin_maxpktsize =
18856161535Sjmcneill UE_GET_SIZE(UGETW(ed->wMaxPacketSize)) *
18956161535Sjmcneill (UE_GET_TRANS(UGETW(ed->wMaxPacketSize)) + 1);
19056161535Sjmcneill }
19156161535Sjmcneill if (dir == UE_DIR_OUT && sc->sc_bulkout_ep == -1) {
19256161535Sjmcneill sc->sc_bulkout_ep = ed->bEndpointAddress;
19356161535Sjmcneill sc->sc_bulkout_maxpktsize =
19456161535Sjmcneill UE_GET_SIZE(UGETW(ed->wMaxPacketSize)) *
19556161535Sjmcneill (UE_GET_TRANS(UGETW(ed->wMaxPacketSize)) + 1);
19656161535Sjmcneill }
19756161535Sjmcneill }
19856161535Sjmcneill
199aa3d6ec1Schristos aprint_debug_dev(self, "in 0x%02x/%d out 0x%02x/%d\n",
20056161535Sjmcneill sc->sc_bulkin_ep, sc->sc_bulkin_maxpktsize,
20156161535Sjmcneill sc->sc_bulkout_ep, sc->sc_bulkout_maxpktsize);
20256161535Sjmcneill
20356161535Sjmcneill if (sc->sc_bulkin_maxpktsize < 16 || sc->sc_bulkout_maxpktsize < 16) {
20456161535Sjmcneill aprint_error_dev(self, "bad maxpktsize\n");
20556161535Sjmcneill return;
20656161535Sjmcneill }
2074e8e6643Sskrll usbd_status err;
20856161535Sjmcneill
2094e8e6643Sskrll err = usbd_open_pipe(sc->sc_iface, sc->sc_bulkin_ep,
2104e8e6643Sskrll USBD_EXCLUSIVE_USE, &sc->sc_bulkin_pipe);
2114e8e6643Sskrll if (err) {
2124e8e6643Sskrll aprint_error_dev(sc->sc_dev,
2134e8e6643Sskrll "couldn't open bulk-in pipe: %s\n", usbd_errstr(err));
21456161535Sjmcneill return;
21556161535Sjmcneill }
2164e8e6643Sskrll err = usbd_open_pipe(sc->sc_iface, sc->sc_bulkout_ep,
2174e8e6643Sskrll USBD_EXCLUSIVE_USE, &sc->sc_bulkout_pipe);
2184e8e6643Sskrll if (err) {
2194e8e6643Sskrll aprint_error_dev(sc->sc_dev,
2204e8e6643Sskrll "couldn't open bulk-out pipe: %s\n", usbd_errstr(err));
2214e8e6643Sskrll usbd_close_pipe(sc->sc_bulkin_pipe);
2224e8e6643Sskrll sc->sc_bulkin_pipe = NULL;
22356161535Sjmcneill return;
22456161535Sjmcneill }
22556161535Sjmcneill
2264e8e6643Sskrll int error;
2274e8e6643Sskrll error = usbd_create_xfer(sc->sc_bulkin_pipe, sc->sc_bulkin_maxpktsize,
228b8421611Sskrll 0, 0, &sc->sc_bulkin_xfer);
2294e8e6643Sskrll if (error) {
2304e8e6643Sskrll goto fail;
2314e8e6643Sskrll }
2324e8e6643Sskrll
2334e8e6643Sskrll error = usbd_create_xfer(sc->sc_bulkout_pipe,
2344e8e6643Sskrll sc->sc_bulkout_maxpktsize, USBD_FORCE_SHORT_XFER, 0,
2354e8e6643Sskrll &sc->sc_bulkout_xfer);
2364e8e6643Sskrll if (error) {
2374e8e6643Sskrll goto fail;
2384e8e6643Sskrll }
2394e8e6643Sskrll sc->sc_bulkin_buffer = usbd_get_buffer(sc->sc_bulkin_xfer);
2404e8e6643Sskrll sc->sc_bulkout_buffer = usbd_get_buffer(sc->sc_bulkout_xfer);
2414e8e6643Sskrll
24256161535Sjmcneill irmce_rescan(self, NULL, NULL);
2434e8e6643Sskrll return;
2444e8e6643Sskrll
2454e8e6643Sskrll fail:
2464e8e6643Sskrll if (sc->sc_bulkin_xfer)
2474e8e6643Sskrll usbd_destroy_xfer(sc->sc_bulkin_xfer);
2484e8e6643Sskrll if (sc->sc_bulkout_xfer)
2494e8e6643Sskrll usbd_destroy_xfer(sc->sc_bulkout_xfer);
25056161535Sjmcneill }
25156161535Sjmcneill
25256161535Sjmcneill static int
irmce_detach(device_t self,int flags)25356161535Sjmcneill irmce_detach(device_t self, int flags)
25456161535Sjmcneill {
25556161535Sjmcneill struct irmce_softc *sc = device_private(self);
25656161535Sjmcneill int error;
25756161535Sjmcneill
258*20ab753eSriastradh error = config_detach_children(self, flags);
25956161535Sjmcneill if (error)
26056161535Sjmcneill return error;
26156161535Sjmcneill
2624e8e6643Sskrll if (sc->sc_bulkin_pipe) {
2634e8e6643Sskrll usbd_abort_pipe(sc->sc_bulkin_pipe);
2644e8e6643Sskrll }
2654e8e6643Sskrll if (sc->sc_bulkout_pipe) {
2664e8e6643Sskrll usbd_abort_pipe(sc->sc_bulkout_pipe);
2674e8e6643Sskrll }
26856161535Sjmcneill if (sc->sc_bulkin_xfer) {
2694e8e6643Sskrll usbd_destroy_xfer(sc->sc_bulkin_xfer);
27056161535Sjmcneill sc->sc_bulkin_buffer = NULL;
27156161535Sjmcneill sc->sc_bulkin_xfer = NULL;
27256161535Sjmcneill }
27356161535Sjmcneill if (sc->sc_bulkout_xfer) {
2744e8e6643Sskrll usbd_destroy_xfer(sc->sc_bulkout_xfer);
27556161535Sjmcneill sc->sc_bulkout_buffer = NULL;
27656161535Sjmcneill sc->sc_bulkout_xfer = NULL;
27756161535Sjmcneill }
2784e8e6643Sskrll if (sc->sc_bulkin_pipe) {
2794e8e6643Sskrll usbd_close_pipe(sc->sc_bulkin_pipe);
2804e8e6643Sskrll sc->sc_bulkin_pipe = NULL;
2814e8e6643Sskrll }
2824e8e6643Sskrll if (sc->sc_bulkout_pipe) {
2834e8e6643Sskrll usbd_close_pipe(sc->sc_bulkout_pipe);
2844e8e6643Sskrll sc->sc_bulkout_pipe = NULL;
2854e8e6643Sskrll }
28656161535Sjmcneill
28756161535Sjmcneill pmf_device_deregister(self);
28856161535Sjmcneill
28956161535Sjmcneill return 0;
29056161535Sjmcneill }
29156161535Sjmcneill
29256161535Sjmcneill static int
irmce_activate(device_t self,enum devact act)29356161535Sjmcneill irmce_activate(device_t self, enum devact act)
29456161535Sjmcneill {
29556161535Sjmcneill return 0;
29656161535Sjmcneill }
29756161535Sjmcneill
29856161535Sjmcneill static int
irmce_rescan(device_t self,const char * ifattr,const int * locators)29956161535Sjmcneill irmce_rescan(device_t self, const char *ifattr, const int *locators)
30056161535Sjmcneill {
30156161535Sjmcneill struct irmce_softc *sc = device_private(self);
30256161535Sjmcneill struct ir_attach_args iaa;
30356161535Sjmcneill
30456161535Sjmcneill if (sc->sc_cirdev == NULL) {
30556161535Sjmcneill iaa.ia_type = IR_TYPE_CIR;
30656161535Sjmcneill iaa.ia_methods = &irmce_cir_methods;
30756161535Sjmcneill iaa.ia_handle = sc;
3082685996bSthorpej sc->sc_cirdev =
309c7fb772bSthorpej config_found(self, &iaa, irmce_print, CFARGS_NONE);
31056161535Sjmcneill }
31156161535Sjmcneill
31256161535Sjmcneill return 0;
31356161535Sjmcneill }
31456161535Sjmcneill
31556161535Sjmcneill static int
irmce_print(void * priv,const char * pnp)31656161535Sjmcneill irmce_print(void *priv, const char *pnp)
31756161535Sjmcneill {
31856161535Sjmcneill if (pnp)
31956161535Sjmcneill aprint_normal("cir at %s", pnp);
32056161535Sjmcneill
32156161535Sjmcneill return UNCONF;
32256161535Sjmcneill }
32356161535Sjmcneill
32456161535Sjmcneill static void
irmce_childdet(device_t self,device_t child)32556161535Sjmcneill irmce_childdet(device_t self, device_t child)
32656161535Sjmcneill {
32756161535Sjmcneill struct irmce_softc *sc = device_private(self);
32856161535Sjmcneill
32956161535Sjmcneill if (sc->sc_cirdev == child)
33056161535Sjmcneill sc->sc_cirdev = NULL;
33156161535Sjmcneill }
33256161535Sjmcneill
33356161535Sjmcneill static int
irmce_reset(struct irmce_softc * sc)33456161535Sjmcneill irmce_reset(struct irmce_softc *sc)
33556161535Sjmcneill {
33656161535Sjmcneill static const uint8_t reset_cmd[] = { 0x00, 0xff, 0xaa };
33756161535Sjmcneill uint8_t *p = sc->sc_bulkout_buffer;
33856161535Sjmcneill usbd_status err;
33956161535Sjmcneill uint32_t wlen;
34056161535Sjmcneill unsigned int n;
34156161535Sjmcneill
34256161535Sjmcneill for (n = 0; n < __arraycount(reset_cmd); n++)
34356161535Sjmcneill *p++ = reset_cmd[n];
34456161535Sjmcneill
34556161535Sjmcneill wlen = sizeof(reset_cmd);
3464e8e6643Sskrll err = usbd_bulk_transfer(sc->sc_bulkout_xfer, sc->sc_bulkout_pipe,
3474e8e6643Sskrll USBD_FORCE_SHORT_XFER, USBD_DEFAULT_TIMEOUT,
3484e8e6643Sskrll sc->sc_bulkout_buffer, &wlen);
34956161535Sjmcneill if (err != USBD_NORMAL_COMPLETION) {
35056161535Sjmcneill if (err == USBD_INTERRUPTED)
35156161535Sjmcneill return EINTR;
35256161535Sjmcneill else if (err == USBD_TIMEOUT)
35356161535Sjmcneill return ETIMEDOUT;
35456161535Sjmcneill else
35556161535Sjmcneill return EIO;
35656161535Sjmcneill }
35756161535Sjmcneill
35856161535Sjmcneill return 0;
35956161535Sjmcneill }
36056161535Sjmcneill
36156161535Sjmcneill static int
irmce_open(void * priv,int flag,int mode,struct proc * p)36256161535Sjmcneill irmce_open(void *priv, int flag, int mode, struct proc *p)
36356161535Sjmcneill {
36456161535Sjmcneill struct irmce_softc *sc = priv;
3654e8e6643Sskrll int err = irmce_reset(sc);
36656161535Sjmcneill if (err) {
36756161535Sjmcneill aprint_error_dev(sc->sc_dev,
36856161535Sjmcneill "couldn't reset device: %s\n", usbd_errstr(err));
36956161535Sjmcneill }
37056161535Sjmcneill sc->sc_ir_state = IRMCE_STATE_HEADER;
37156161535Sjmcneill sc->sc_rc6_nhb = 0;
37256161535Sjmcneill
37356161535Sjmcneill return 0;
37456161535Sjmcneill }
37556161535Sjmcneill
37656161535Sjmcneill static int
irmce_close(void * priv,int flag,int mode,struct proc * p)37756161535Sjmcneill irmce_close(void *priv, int flag, int mode, struct proc *p)
37856161535Sjmcneill {
37956161535Sjmcneill struct irmce_softc *sc = priv;
38056161535Sjmcneill
38156161535Sjmcneill if (sc->sc_bulkin_pipe) {
38256161535Sjmcneill usbd_abort_pipe(sc->sc_bulkin_pipe);
38356161535Sjmcneill }
38456161535Sjmcneill if (sc->sc_bulkout_pipe) {
38556161535Sjmcneill usbd_abort_pipe(sc->sc_bulkout_pipe);
38656161535Sjmcneill }
38756161535Sjmcneill
38856161535Sjmcneill return 0;
38956161535Sjmcneill }
39056161535Sjmcneill
39156161535Sjmcneill static int
irmce_rc6_decode(struct irmce_softc * sc,uint8_t * buf,size_t buflen,struct uio * uio)39256161535Sjmcneill irmce_rc6_decode(struct irmce_softc *sc, uint8_t *buf, size_t buflen,
39356161535Sjmcneill struct uio *uio)
39456161535Sjmcneill {
39556161535Sjmcneill bool *hb = &sc->sc_rc6_hb[0];
39656161535Sjmcneill unsigned int n;
39756161535Sjmcneill int state, pulse;
39856161535Sjmcneill uint32_t data;
39956161535Sjmcneill uint8_t mode;
40056161535Sjmcneill bool idle = false;
40156161535Sjmcneill
40256161535Sjmcneill for (n = 0; n < buflen; n++) {
40356161535Sjmcneill state = (buf[n] & 0x80) ? 1 : 0;
40456161535Sjmcneill pulse = (buf[n] & 0x7f) * 50;
40556161535Sjmcneill
40656161535Sjmcneill if (pulse >= 300 && pulse <= 600) {
40756161535Sjmcneill hb[sc->sc_rc6_nhb++] = state;
40856161535Sjmcneill } else if (pulse >= 680 && pulse <= 1080) {
40956161535Sjmcneill hb[sc->sc_rc6_nhb++] = state;
41056161535Sjmcneill hb[sc->sc_rc6_nhb++] = state;
41156161535Sjmcneill } else if (pulse >= 1150 && pulse <= 1450) {
41256161535Sjmcneill hb[sc->sc_rc6_nhb++] = state;
41356161535Sjmcneill hb[sc->sc_rc6_nhb++] = state;
41456161535Sjmcneill hb[sc->sc_rc6_nhb++] = state;
41556161535Sjmcneill } else if (pulse >= 2400 && pulse <= 2800) {
41656161535Sjmcneill hb[sc->sc_rc6_nhb++] = state;
41756161535Sjmcneill hb[sc->sc_rc6_nhb++] = state;
41856161535Sjmcneill hb[sc->sc_rc6_nhb++] = state;
41956161535Sjmcneill hb[sc->sc_rc6_nhb++] = state;
42056161535Sjmcneill hb[sc->sc_rc6_nhb++] = state;
42156161535Sjmcneill hb[sc->sc_rc6_nhb++] = state;
42256161535Sjmcneill } else if (pulse > 3000) {
42356161535Sjmcneill if (sc->sc_rc6_nhb & 1)
42456161535Sjmcneill hb[sc->sc_rc6_nhb++] = state;
42556161535Sjmcneill idle = true;
42656161535Sjmcneill break;
42756161535Sjmcneill } else {
42856161535Sjmcneill aprint_debug_dev(sc->sc_dev,
42956161535Sjmcneill "error parsing RC6 stream (pulse=%d)\n", pulse);
43056161535Sjmcneill return EIO;
43156161535Sjmcneill }
43256161535Sjmcneill }
43356161535Sjmcneill
43456161535Sjmcneill if (!idle)
43556161535Sjmcneill return 0;
43656161535Sjmcneill
43756161535Sjmcneill if (sc->sc_rc6_nhb < 20) {
43856161535Sjmcneill aprint_debug_dev(sc->sc_dev, "not enough RC6 data\n");
43956161535Sjmcneill return EIO;
44056161535Sjmcneill }
44156161535Sjmcneill
44256161535Sjmcneill /* RC6 leader 11111100 */
44356161535Sjmcneill if (!hb[0] || !hb[1] || !hb[2] || !hb[3] || !hb[4] || !hb[5] ||
44456161535Sjmcneill hb[6] || hb[7]) {
44556161535Sjmcneill aprint_debug_dev(sc->sc_dev, "bad RC6 leader\n");
44656161535Sjmcneill return EIO;
44756161535Sjmcneill }
44856161535Sjmcneill
44956161535Sjmcneill /* start bit 10 */
45056161535Sjmcneill if (!hb[8] || hb[9]) {
45156161535Sjmcneill aprint_debug_dev(sc->sc_dev, "missing RC6 start bit\n");
45256161535Sjmcneill return EIO;
45356161535Sjmcneill }
45456161535Sjmcneill
45556161535Sjmcneill /* mode info */
45656161535Sjmcneill mode = 0x00;
45756161535Sjmcneill for (n = 10; n < 15; n += 2) {
45856161535Sjmcneill if (hb[n] && !hb[n + 1])
45956161535Sjmcneill mode = (mode << 1) | 1;
46056161535Sjmcneill else if (!hb[n] && hb[n + 1])
46156161535Sjmcneill mode = (mode << 1) | 0;
46256161535Sjmcneill else {
46356161535Sjmcneill aprint_debug_dev(sc->sc_dev, "bad RC6 mode bits\n");
46456161535Sjmcneill return EIO;
46556161535Sjmcneill }
46656161535Sjmcneill }
46756161535Sjmcneill
46856161535Sjmcneill data = 0;
46956161535Sjmcneill for (n = 20; n < sc->sc_rc6_nhb; n += 2) {
47056161535Sjmcneill if (hb[n] && !hb[n + 1])
47156161535Sjmcneill data = (data << 1) | 1;
47256161535Sjmcneill else if (!hb[n] && hb[n + 1])
47356161535Sjmcneill data = (data << 1) | 0;
47456161535Sjmcneill else {
47556161535Sjmcneill aprint_debug_dev(sc->sc_dev, "bad RC6 data bits\n");
47656161535Sjmcneill return EIO;
47756161535Sjmcneill }
47856161535Sjmcneill }
47956161535Sjmcneill
48056161535Sjmcneill sc->sc_rc6_nhb = 0;
48156161535Sjmcneill
48256161535Sjmcneill return uiomove(&data, sizeof(data), uio);
48356161535Sjmcneill }
48456161535Sjmcneill
48556161535Sjmcneill static int
irmce_process(struct irmce_softc * sc,uint8_t * buf,size_t buflen,struct uio * uio)48656161535Sjmcneill irmce_process(struct irmce_softc *sc, uint8_t *buf, size_t buflen,
48756161535Sjmcneill struct uio *uio)
48856161535Sjmcneill {
48956161535Sjmcneill uint8_t *p = buf;
49056161535Sjmcneill uint8_t data, cmd;
49156161535Sjmcneill int error;
49256161535Sjmcneill
49356161535Sjmcneill while (p - buf < (ssize_t)buflen) {
49456161535Sjmcneill switch (sc->sc_ir_state) {
49556161535Sjmcneill case IRMCE_STATE_HEADER:
49656161535Sjmcneill sc->sc_ir_header = data = *p++;
49756161535Sjmcneill if ((data & 0xe0) == 0x80 && (data & 0x1f) != 0x1f) {
49856161535Sjmcneill sc->sc_ir_bufused = 0;
49956161535Sjmcneill sc->sc_ir_resid = data & 0x1f;
50056161535Sjmcneill sc->sc_ir_state = IRMCE_STATE_IRDATA;
50156161535Sjmcneill if (sc->sc_ir_resid > sizeof(sc->sc_ir_buf))
50256161535Sjmcneill return EIO;
50356161535Sjmcneill if (sc->sc_ir_resid == 0)
50456161535Sjmcneill sc->sc_ir_state = IRMCE_STATE_HEADER;
50556161535Sjmcneill } else {
50656161535Sjmcneill sc->sc_ir_state = IRMCE_STATE_CMDHEADER;
50756161535Sjmcneill }
50856161535Sjmcneill break;
50956161535Sjmcneill case IRMCE_STATE_CMDHEADER:
51056161535Sjmcneill cmd = *p++;
51156161535Sjmcneill data = sc->sc_ir_header;
51256161535Sjmcneill if (data == 0x00 && cmd == 0x9f)
51356161535Sjmcneill sc->sc_ir_resid = 1;
51456161535Sjmcneill else if (data == 0xff && cmd == 0x0b)
51556161535Sjmcneill sc->sc_ir_resid = 2;
51656161535Sjmcneill else if (data == 0x9f) {
51756161535Sjmcneill if (cmd == 0x04 || cmd == 0x06 ||
51856161535Sjmcneill cmd == 0x0c || cmd == 0x15) {
51956161535Sjmcneill sc->sc_ir_resid = 2;
52056161535Sjmcneill } else if (cmd == 0x01 || cmd == 0x08 ||
52156161535Sjmcneill cmd == 0x14) {
52256161535Sjmcneill sc->sc_ir_resid = 1;
52356161535Sjmcneill }
52456161535Sjmcneill }
52556161535Sjmcneill if (sc->sc_ir_resid > 0)
52656161535Sjmcneill sc->sc_ir_state = IRMCE_STATE_CMDDATA;
52756161535Sjmcneill else
52856161535Sjmcneill sc->sc_ir_state = IRMCE_STATE_HEADER;
52956161535Sjmcneill break;
53056161535Sjmcneill case IRMCE_STATE_IRDATA:
53156161535Sjmcneill sc->sc_ir_resid--;
53256161535Sjmcneill sc->sc_ir_buf[sc->sc_ir_bufused++] = *p;
53356161535Sjmcneill p++;
53456161535Sjmcneill if (sc->sc_ir_resid == 0) {
53556161535Sjmcneill sc->sc_ir_state = IRMCE_STATE_HEADER;
53656161535Sjmcneill error = irmce_rc6_decode(sc,
53756161535Sjmcneill sc->sc_ir_buf, sc->sc_ir_bufused, uio);
53856161535Sjmcneill if (error)
53956161535Sjmcneill sc->sc_rc6_nhb = 0;
54056161535Sjmcneill }
54156161535Sjmcneill break;
54256161535Sjmcneill case IRMCE_STATE_CMDDATA:
54356161535Sjmcneill p++;
54456161535Sjmcneill sc->sc_ir_resid--;
54556161535Sjmcneill if (sc->sc_ir_resid == 0)
54656161535Sjmcneill sc->sc_ir_state = IRMCE_STATE_HEADER;
54756161535Sjmcneill break;
54856161535Sjmcneill }
54956161535Sjmcneill
55056161535Sjmcneill }
55156161535Sjmcneill
55256161535Sjmcneill return 0;
55356161535Sjmcneill }
55456161535Sjmcneill
55556161535Sjmcneill static int
irmce_read(void * priv,struct uio * uio,int flag)55656161535Sjmcneill irmce_read(void *priv, struct uio *uio, int flag)
55756161535Sjmcneill {
55856161535Sjmcneill struct irmce_softc *sc = priv;
55956161535Sjmcneill usbd_status err;
56056161535Sjmcneill uint32_t rlen;
56156161535Sjmcneill int error = 0;
56256161535Sjmcneill
56356161535Sjmcneill while (uio->uio_resid > 0) {
56456161535Sjmcneill rlen = sc->sc_bulkin_maxpktsize;
56556161535Sjmcneill err = usbd_bulk_transfer(sc->sc_bulkin_xfer,
5664e8e6643Sskrll sc->sc_bulkin_pipe, USBD_SHORT_XFER_OK,
5674e8e6643Sskrll USBD_DEFAULT_TIMEOUT, sc->sc_bulkin_buffer, &rlen);
56856161535Sjmcneill if (err != USBD_NORMAL_COMPLETION) {
56956161535Sjmcneill if (err == USBD_INTERRUPTED)
57056161535Sjmcneill return EINTR;
57156161535Sjmcneill else if (err == USBD_TIMEOUT)
57256161535Sjmcneill continue;
57356161535Sjmcneill else
57456161535Sjmcneill return EIO;
57556161535Sjmcneill }
57656161535Sjmcneill
57756161535Sjmcneill if (sc->sc_raw) {
57856161535Sjmcneill error = uiomove(sc->sc_bulkin_buffer, rlen, uio);
57956161535Sjmcneill break;
58056161535Sjmcneill } else {
58156161535Sjmcneill error = irmce_process(sc, sc->sc_bulkin_buffer,
58256161535Sjmcneill rlen, uio);
58356161535Sjmcneill if (error)
58456161535Sjmcneill break;
58556161535Sjmcneill }
58656161535Sjmcneill }
58756161535Sjmcneill
58856161535Sjmcneill return error;
58956161535Sjmcneill }
59056161535Sjmcneill
59156161535Sjmcneill static int
irmce_write(void * priv,struct uio * uio,int flag)59256161535Sjmcneill irmce_write(void *priv, struct uio *uio, int flag)
59356161535Sjmcneill {
59456161535Sjmcneill return EIO;
59556161535Sjmcneill }
59656161535Sjmcneill
59756161535Sjmcneill static int
irmce_setparams(void * priv,struct cir_params * params)59856161535Sjmcneill irmce_setparams(void *priv, struct cir_params *params)
59956161535Sjmcneill {
60056161535Sjmcneill struct irmce_softc *sc = priv;
60156161535Sjmcneill
60256161535Sjmcneill if (params->raw > 1)
60356161535Sjmcneill return EINVAL;
60456161535Sjmcneill sc->sc_raw = params->raw;
60556161535Sjmcneill
60656161535Sjmcneill return 0;
60756161535Sjmcneill }
60856161535Sjmcneill
60956161535Sjmcneill MODULE(MODULE_CLASS_DRIVER, irmce, NULL);
61056161535Sjmcneill
61156161535Sjmcneill #ifdef _MODULE
61256161535Sjmcneill #include "ioconf.c"
61356161535Sjmcneill #endif
61456161535Sjmcneill
61556161535Sjmcneill static int
irmce_modcmd(modcmd_t cmd,void * opaque)61656161535Sjmcneill irmce_modcmd(modcmd_t cmd, void *opaque)
61756161535Sjmcneill {
61856161535Sjmcneill switch (cmd) {
61956161535Sjmcneill case MODULE_CMD_INIT:
62056161535Sjmcneill #ifdef _MODULE
62156161535Sjmcneill return config_init_component(cfdriver_ioconf_irmce,
62256161535Sjmcneill cfattach_ioconf_irmce, cfdata_ioconf_irmce);
62356161535Sjmcneill #else
62456161535Sjmcneill return 0;
62556161535Sjmcneill #endif
62656161535Sjmcneill case MODULE_CMD_FINI:
62756161535Sjmcneill #ifdef _MODULE
62856161535Sjmcneill return config_fini_component(cfdriver_ioconf_irmce,
62956161535Sjmcneill cfattach_ioconf_irmce, cfdata_ioconf_irmce);
63056161535Sjmcneill #else
63156161535Sjmcneill return 0;
63256161535Sjmcneill #endif
63356161535Sjmcneill default:
63456161535Sjmcneill return ENOTTY;
63556161535Sjmcneill }
63656161535Sjmcneill }
637