1*9bef90feSriastradh /* $NetBSD: emdtv_ir.c,v 1.6 2022/06/26 22:49:09 riastradh Exp $ */
251f58a8aSjmcneill
351f58a8aSjmcneill /*-
451f58a8aSjmcneill * Copyright (c) 2008 Jared D. McNeill <jmcneill@invisible.ca>
551f58a8aSjmcneill * All rights reserved.
651f58a8aSjmcneill *
751f58a8aSjmcneill * Redistribution and use in source and binary forms, with or without
851f58a8aSjmcneill * modification, are permitted provided that the following conditions
951f58a8aSjmcneill * are met:
1051f58a8aSjmcneill * 1. Redistributions of source code must retain the above copyright
1151f58a8aSjmcneill * notice, this list of conditions and the following disclaimer.
1251f58a8aSjmcneill * 2. Redistributions in binary form must reproduce the above copyright
1351f58a8aSjmcneill * notice, this list of conditions and the following disclaimer in the
1451f58a8aSjmcneill * documentation and/or other materials provided with the distribution.
1551f58a8aSjmcneill *
1651f58a8aSjmcneill * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
1751f58a8aSjmcneill * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
1851f58a8aSjmcneill * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
1951f58a8aSjmcneill * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
2051f58a8aSjmcneill * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2151f58a8aSjmcneill * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2251f58a8aSjmcneill * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2351f58a8aSjmcneill * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2451f58a8aSjmcneill * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2551f58a8aSjmcneill * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2651f58a8aSjmcneill * POSSIBILITY OF SUCH DAMAGE.
2751f58a8aSjmcneill */
2851f58a8aSjmcneill
2951f58a8aSjmcneill #include <sys/cdefs.h>
30*9bef90feSriastradh __KERNEL_RCSID(0, "$NetBSD: emdtv_ir.c,v 1.6 2022/06/26 22:49:09 riastradh Exp $");
3151f58a8aSjmcneill
3251f58a8aSjmcneill #include <sys/select.h>
3351f58a8aSjmcneill #include <sys/param.h>
3451f58a8aSjmcneill #include <sys/systm.h>
3551f58a8aSjmcneill #include <sys/device.h>
3651f58a8aSjmcneill #include <sys/conf.h>
3751f58a8aSjmcneill #include <sys/bus.h>
3851f58a8aSjmcneill
3951f58a8aSjmcneill #include <dev/usb/usb.h>
4051f58a8aSjmcneill #include <dev/usb/usbdi.h>
4151f58a8aSjmcneill #include <dev/usb/usbdi_util.h>
4251f58a8aSjmcneill #include <dev/usb/usbdivar.h>
4351f58a8aSjmcneill #include <dev/usb/usbdevs.h>
4451f58a8aSjmcneill
4551f58a8aSjmcneill #include <dev/usb/emdtvvar.h>
4651f58a8aSjmcneill #include <dev/usb/emdtvreg.h>
4751f58a8aSjmcneill
4851f58a8aSjmcneill #include <dev/ir/ir.h>
4951f58a8aSjmcneill #include <dev/ir/cirio.h>
5051f58a8aSjmcneill #include <dev/ir/cirvar.h>
5151f58a8aSjmcneill
524e8e6643Sskrll static void emdtv_ir_intr(struct usbd_xfer *, void *,
5351f58a8aSjmcneill usbd_status);
5451f58a8aSjmcneill static void emdtv_ir_worker(struct work *, void *);
5551f58a8aSjmcneill
5651f58a8aSjmcneill static int emdtv_ir_open(void *, int, int, struct proc *);
5751f58a8aSjmcneill static int emdtv_ir_close(void *, int, int, struct proc *);
5851f58a8aSjmcneill static int emdtv_ir_read(void *, struct uio *, int);
5951f58a8aSjmcneill static int emdtv_ir_write(void *, struct uio *, int);
6051f58a8aSjmcneill static int emdtv_ir_setparams(void *, struct cir_params *);
6151f58a8aSjmcneill
6251f58a8aSjmcneill static const struct cir_methods emdtv_ir_methods = {
6351f58a8aSjmcneill .im_open = emdtv_ir_open,
6451f58a8aSjmcneill .im_close = emdtv_ir_close,
6551f58a8aSjmcneill .im_read = emdtv_ir_read,
6651f58a8aSjmcneill .im_write = emdtv_ir_write,
6751f58a8aSjmcneill .im_setparams = emdtv_ir_setparams,
6851f58a8aSjmcneill };
6951f58a8aSjmcneill
7051f58a8aSjmcneill void
emdtv_ir_attach(struct emdtv_softc * sc)7151f58a8aSjmcneill emdtv_ir_attach(struct emdtv_softc *sc)
7251f58a8aSjmcneill {
7351f58a8aSjmcneill struct ir_attach_args ia;
7451f58a8aSjmcneill usb_endpoint_descriptor_t *ed;
7551f58a8aSjmcneill usbd_status status;
7651f58a8aSjmcneill int err;
7751f58a8aSjmcneill
78*9bef90feSriastradh mutex_init(&sc->sc_ir_mutex, MUTEX_DEFAULT, IPL_VM);
79*9bef90feSriastradh
8051f58a8aSjmcneill ed = usbd_interface2endpoint_descriptor(sc->sc_iface, 0);
8151f58a8aSjmcneill if (ed == NULL)
8251f58a8aSjmcneill return;
8351f58a8aSjmcneill
84*9bef90feSriastradh err = workqueue_create(&sc->sc_ir_wq, "emdtvir",
85*9bef90feSriastradh emdtv_ir_worker, sc, PRI_NONE, IPL_VM, 0);
86*9bef90feSriastradh if (err) {
87*9bef90feSriastradh aprint_error_dev(sc->sc_dev, "couldn't create workqueue: %d\n",
88*9bef90feSriastradh err);
89*9bef90feSriastradh return;
90*9bef90feSriastradh }
91*9bef90feSriastradh
9251f58a8aSjmcneill status = usbd_open_pipe_intr(sc->sc_iface, ed->bEndpointAddress,
9351f58a8aSjmcneill USBD_EXCLUSIVE_USE, &sc->sc_intr_pipe, sc, &sc->sc_intr_buf, 1,
9451f58a8aSjmcneill emdtv_ir_intr, USBD_DEFAULT_INTERVAL);
9551f58a8aSjmcneill if (status != USBD_NORMAL_COMPLETION) {
9651f58a8aSjmcneill aprint_error_dev(sc->sc_dev, "couldn't open intr pipe: %s\n",
9751f58a8aSjmcneill usbd_errstr(status));
9851f58a8aSjmcneill return;
9951f58a8aSjmcneill }
10051f58a8aSjmcneill
10151f58a8aSjmcneill ia.ia_type = IR_TYPE_CIR;
10251f58a8aSjmcneill ia.ia_methods = &emdtv_ir_methods;
10351f58a8aSjmcneill ia.ia_handle = sc;
10451f58a8aSjmcneill
10551f58a8aSjmcneill sc->sc_cirdev =
1062685996bSthorpej config_found(sc->sc_dev, &ia, ir_print,
107c7fb772bSthorpej CFARGS(.iattr = "irbus"));
10851f58a8aSjmcneill }
10951f58a8aSjmcneill
11051f58a8aSjmcneill void
emdtv_ir_detach(struct emdtv_softc * sc,int flags)11151f58a8aSjmcneill emdtv_ir_detach(struct emdtv_softc *sc, int flags)
11251f58a8aSjmcneill {
11351f58a8aSjmcneill
11451f58a8aSjmcneill if (sc->sc_intr_pipe != NULL) {
11551f58a8aSjmcneill usbd_abort_pipe(sc->sc_intr_pipe);
11651f58a8aSjmcneill usbd_close_pipe(sc->sc_intr_pipe);
11751f58a8aSjmcneill sc->sc_intr_pipe = NULL;
11851f58a8aSjmcneill }
11951f58a8aSjmcneill
1206e8010fdSriastradh if (sc->sc_ir_wq != NULL)
1216e8010fdSriastradh workqueue_destroy(sc->sc_ir_wq);
12251f58a8aSjmcneill
1236e8010fdSriastradh mutex_destroy(&sc->sc_ir_mutex);
12451f58a8aSjmcneill }
12551f58a8aSjmcneill
12651f58a8aSjmcneill static void
emdtv_ir_intr(struct usbd_xfer * xfer,void * priv,usbd_status status)1274e8e6643Sskrll emdtv_ir_intr(struct usbd_xfer *xfer, void * priv,
12851f58a8aSjmcneill usbd_status status)
12951f58a8aSjmcneill {
13051f58a8aSjmcneill struct emdtv_softc *sc = priv;
13151f58a8aSjmcneill uint32_t len;
13251f58a8aSjmcneill
13351f58a8aSjmcneill usbd_get_xfer_status(xfer, NULL, NULL, &len, NULL);
13451f58a8aSjmcneill if (status == USBD_CANCELLED)
13551f58a8aSjmcneill return;
13651f58a8aSjmcneill
13751f58a8aSjmcneill if (sc->sc_ir_wq)
13851f58a8aSjmcneill workqueue_enqueue(sc->sc_ir_wq, &sc->sc_ir_work, NULL);
13951f58a8aSjmcneill }
14051f58a8aSjmcneill
14151f58a8aSjmcneill static void
emdtv_ir_worker(struct work * wk,void * opaque)14251f58a8aSjmcneill emdtv_ir_worker(struct work *wk, void *opaque)
14351f58a8aSjmcneill {
14451f58a8aSjmcneill struct emdtv_softc *sc = opaque;
14551f58a8aSjmcneill struct cir_softc *csc;
14651f58a8aSjmcneill uint8_t evt[3];
14751f58a8aSjmcneill int pos;
14851f58a8aSjmcneill
14951f58a8aSjmcneill if (sc->sc_cirdev == NULL || sc->sc_dying == true ||
15051f58a8aSjmcneill sc->sc_ir_open == false)
15151f58a8aSjmcneill return;
15251f58a8aSjmcneill
15351f58a8aSjmcneill emdtv_read_multi_1(sc, UR_GET_STATUS, EM28XX_REG_IR, evt, sizeof(evt));
15451f58a8aSjmcneill
15551f58a8aSjmcneill csc = device_private(sc->sc_cirdev);
15651f58a8aSjmcneill
15751f58a8aSjmcneill mutex_enter(&sc->sc_ir_mutex);
15851f58a8aSjmcneill pos = (sc->sc_ir_ptr + sc->sc_ir_cnt) % EMDTV_CIR_BUFLEN;
15951f58a8aSjmcneill memcpy(&sc->sc_ir_queue[pos], evt, sizeof(evt));
16051f58a8aSjmcneill if (sc->sc_ir_cnt < EMDTV_CIR_BUFLEN - 1) {
16151f58a8aSjmcneill ++sc->sc_ir_cnt;
16251f58a8aSjmcneill ++csc->sc_rdframes;
16351f58a8aSjmcneill }
16451f58a8aSjmcneill selnotify(&csc->sc_rdsel, 0, 1);
16551f58a8aSjmcneill mutex_exit(&sc->sc_ir_mutex);
16651f58a8aSjmcneill }
16751f58a8aSjmcneill
16851f58a8aSjmcneill /*
16951f58a8aSjmcneill * cir(4)
17051f58a8aSjmcneill */
17151f58a8aSjmcneill static int
emdtv_ir_open(void * opaque,int flag,int mode,struct proc * p)17251f58a8aSjmcneill emdtv_ir_open(void *opaque, int flag, int mode, struct proc *p)
17351f58a8aSjmcneill {
17451f58a8aSjmcneill struct emdtv_softc *sc = opaque;
17551f58a8aSjmcneill
17651f58a8aSjmcneill if (sc->sc_ir_open == true)
17751f58a8aSjmcneill return EBUSY;
17851f58a8aSjmcneill
17951f58a8aSjmcneill sc->sc_ir_cnt = 0;
18051f58a8aSjmcneill sc->sc_ir_ptr = EMDTV_CIR_BUFLEN - 1;
18151f58a8aSjmcneill sc->sc_ir_open = true;
18251f58a8aSjmcneill
18351f58a8aSjmcneill return 0;
18451f58a8aSjmcneill }
18551f58a8aSjmcneill
18651f58a8aSjmcneill static int
emdtv_ir_close(void * opaque,int flag,int mode,struct proc * p)18751f58a8aSjmcneill emdtv_ir_close(void *opaque, int flag, int mode, struct proc *p)
18851f58a8aSjmcneill {
18951f58a8aSjmcneill struct emdtv_softc *sc = opaque;
19051f58a8aSjmcneill
19151f58a8aSjmcneill sc->sc_ir_open = false;
19251f58a8aSjmcneill
19351f58a8aSjmcneill return 0;
19451f58a8aSjmcneill }
19551f58a8aSjmcneill
19651f58a8aSjmcneill static int
emdtv_ir_read(void * opaque,struct uio * uio,int flag)19751f58a8aSjmcneill emdtv_ir_read(void *opaque, struct uio *uio, int flag)
19851f58a8aSjmcneill {
19951f58a8aSjmcneill struct emdtv_softc *sc = opaque;
20051f58a8aSjmcneill struct cir_softc *csc;
20151f58a8aSjmcneill int error = 0;
20251f58a8aSjmcneill
20351f58a8aSjmcneill if (uio->uio_resid != 3) /* 3 byte protocol */
20451f58a8aSjmcneill return EINVAL;
20551f58a8aSjmcneill
20651f58a8aSjmcneill if (sc->sc_dying)
20751f58a8aSjmcneill return EIO;
20851f58a8aSjmcneill
20951f58a8aSjmcneill csc = device_private(sc->sc_cirdev);
21051f58a8aSjmcneill
21151f58a8aSjmcneill mutex_enter(&sc->sc_ir_mutex);
21251f58a8aSjmcneill if (sc->sc_ir_cnt == 0)
21351f58a8aSjmcneill goto out;
21451f58a8aSjmcneill
21551f58a8aSjmcneill error = uiomove(&sc->sc_ir_queue[sc->sc_ir_ptr], 3, uio);
21651f58a8aSjmcneill sc->sc_ir_ptr++;
21751f58a8aSjmcneill if (sc->sc_ir_ptr == EMDTV_CIR_BUFLEN)
21851f58a8aSjmcneill sc->sc_ir_ptr = 0;
21951f58a8aSjmcneill --sc->sc_ir_cnt;
22051f58a8aSjmcneill --csc->sc_rdframes;
22151f58a8aSjmcneill KASSERT(sc->sc_ir_cnt >= 0);
22251f58a8aSjmcneill
22351f58a8aSjmcneill out:
22451f58a8aSjmcneill mutex_exit(&sc->sc_ir_mutex);
22551f58a8aSjmcneill return error;
22651f58a8aSjmcneill }
22751f58a8aSjmcneill
22851f58a8aSjmcneill static int
emdtv_ir_write(void * opaque,struct uio * uio,int flag)22951f58a8aSjmcneill emdtv_ir_write(void *opaque, struct uio *uio, int flag)
23051f58a8aSjmcneill {
23151f58a8aSjmcneill return EINVAL;
23251f58a8aSjmcneill }
23351f58a8aSjmcneill
23451f58a8aSjmcneill static int
emdtv_ir_setparams(void * opaque,struct cir_params * cp)23551f58a8aSjmcneill emdtv_ir_setparams(void *opaque, struct cir_params *cp)
23651f58a8aSjmcneill {
23751f58a8aSjmcneill return 0;
23851f58a8aSjmcneill }
239