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