xref: /netbsd-src/sys/dev/usb/irmce.c (revision 20ab753edea6efbe7804bc29a9b74c2a5a840af0)
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