11.2Sjmcneill/* $NetBSD: si.c,v 1.2 2025/12/11 01:13:49 jmcneill Exp $ */
21.1Sjmcneill
31.1Sjmcneill/*-
41.1Sjmcneill * Copyright (c) 2025 Jared McNeill <jmcneill@invisible.ca>
51.1Sjmcneill * All rights reserved.
61.1Sjmcneill *
71.1Sjmcneill * Redistribution and use in source and binary forms, with or without
81.1Sjmcneill * modification, are permitted provided that the following conditions
91.1Sjmcneill * are met:
101.1Sjmcneill * 1. Redistributions of source code must retain the above copyright
111.1Sjmcneill *    notice, this list of conditions and the following disclaimer.
121.1Sjmcneill * 2. Redistributions in binary form must reproduce the above copyright
131.1Sjmcneill *    notice, this list of conditions and the following disclaimer in the
141.1Sjmcneill *    documentation and/or other materials provided with the distribution.
151.1Sjmcneill *
161.1Sjmcneill * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
171.1Sjmcneill * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
181.1Sjmcneill * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
191.1Sjmcneill * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
201.1Sjmcneill * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
211.1Sjmcneill * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
221.1Sjmcneill * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
231.1Sjmcneill * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
241.1Sjmcneill * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
251.1Sjmcneill * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
261.1Sjmcneill * SUCH DAMAGE.
271.1Sjmcneill */
281.1Sjmcneill
291.1Sjmcneill#include <sys/cdefs.h>
301.2Sjmcneill__KERNEL_RCSID(0, "$NetBSD: si.c,v 1.2 2025/12/11 01:13:49 jmcneill Exp $");
311.1Sjmcneill
321.1Sjmcneill#include <sys/param.h>
331.1Sjmcneill#include <sys/bus.h>
341.1Sjmcneill#include <sys/device.h>
351.1Sjmcneill#include <sys/systm.h>
361.1Sjmcneill#include <sys/bitops.h>
371.1Sjmcneill#include <sys/mutex.h>
381.1Sjmcneill#include <sys/tty.h>
391.1Sjmcneill#include <uvm/uvm_extern.h>
401.1Sjmcneill
411.1Sjmcneill#include <machine/wii.h>
421.1Sjmcneill#include <machine/pio.h>
431.1Sjmcneill
441.1Sjmcneill#include <dev/hid/hidev.h>
451.1Sjmcneill
461.1Sjmcneill#include "locators.h"
471.1Sjmcneill#include "mainbus.h"
481.1Sjmcneill#include "si.h"
491.1Sjmcneill#include "gcpad_rdesc.h"
501.1Sjmcneill
511.1Sjmcneill#define SI_NUM_CHAN		4
521.1Sjmcneill
531.1Sjmcneill#define SICOUTBUF(n)		((n) * 0xc + 0x00)
541.1Sjmcneill#define SICINBUFH(n)		((n) * 0xc + 0x04)
551.1Sjmcneill#define SICINBUFL(n)		((n) * 0xc + 0x08)
561.1Sjmcneill#define SIPOLL			0x30
571.1Sjmcneill#define  SIPOLL_X		__BITS(25, 16)
581.1Sjmcneill#define  SIPOLL_Y		__BITS(15, 8)
591.1Sjmcneill#define  SIPOLL_EN(n)		(__BIT(7 - n))
601.1Sjmcneill#define SICOMCSR		0x34
611.1Sjmcneill#define  SICOMCSR_TCINT		__BIT(31)
621.1Sjmcneill#define  SICOMCSR_TCINTMSK	__BIT(30)
631.1Sjmcneill#define  SICOMCSR_RDSTINT	__BIT(28)
641.1Sjmcneill#define  SICOMCSR_RDSTINTMSK	__BIT(27)
651.1Sjmcneill#define  SICOMCSR_OUTLNGTH	__BITS(22, 16)
661.1Sjmcneill#define  SICOMCSR_INLNGTH	__BITS(14, 8)
671.1Sjmcneill#define  SICOMCSR_TSTART	__BIT(0)
681.1Sjmcneill#define SISR			0x38
691.1Sjmcneill#define  SISR_OFF(n)		((3 - (n)) * 8)
701.1Sjmcneill#define  SISR_WR(n)		__BIT(SISR_OFF(n) + 7)
711.1Sjmcneill#define  SISR_RDST(n)		__BIT(SISR_OFF(n) + 5)
721.1Sjmcneill#define  SISR_WRST(n)		__BIT(SISR_OFF(n) + 4)
731.1Sjmcneill#define  SISR_NOREP(n)		__BIT(SISR_OFF(n) + 3)
741.1Sjmcneill#define  SISR_COLL(n)		__BIT(SISR_OFF(n) + 2)
751.1Sjmcneill#define  SISR_OVRUN(n)		__BIT(SISR_OFF(n) + 1)
761.1Sjmcneill#define  SISR_UNRUN(n)		__BIT(SISR_OFF(n) + 0)
771.1Sjmcneill#define  SISR_ERROR_MASK(n)	(SISR_NOREP(n) | SISR_COLL(n) | \
781.1Sjmcneill				 SISR_OVRUN(n) | SISR_UNRUN(n))
791.1Sjmcneill#define  SISR_ERROR_ACK_ALL	(SISR_ERROR_MASK(0) | SISR_ERROR_MASK(1) | \
801.1Sjmcneill				 SISR_ERROR_MASK(2) | SISR_ERROR_MASK(3))
811.1Sjmcneill#define SIEXILK			0x3c
821.1Sjmcneill#define SIIOBUF			0x80
831.1Sjmcneill
841.1Sjmcneill#define GCPAD_REPORT_SIZE	9
851.1Sjmcneill#define GCPAD_START(_buf)	ISSET((_buf)[0], 0x10)
861.1Sjmcneill#define GCPAD_Y(_buf)		ISSET((_buf)[0], 0x08)
871.1Sjmcneill#define GCPAD_X(_buf)		ISSET((_buf)[0], 0x04)
881.1Sjmcneill#define GCPAD_B(_buf)		ISSET((_buf)[0], 0x02)
891.1Sjmcneill#define GCPAD_A(_buf)		ISSET((_buf)[0], 0x01)
901.1Sjmcneill#define GCPAD_LCLICK(_buf)	ISSET((_buf)[1], 0x40)
911.1Sjmcneill#define GCPAD_RCLICK(_buf)	ISSET((_buf)[1], 0x20)
921.1Sjmcneill#define GCPAD_Z(_buf)		ISSET((_buf)[1], 0x10)
931.1Sjmcneill#define GCPAD_UP(_buf)		ISSET((_buf)[1], 0x08)
941.1Sjmcneill#define GCPAD_DOWN(_buf)	ISSET((_buf)[1], 0x04)
951.1Sjmcneill#define GCPAD_RIGHT(_buf)	ISSET((_buf)[1], 0x02)
961.1Sjmcneill#define GCPAD_LEFT(_buf)	ISSET((_buf)[1], 0x01)
971.1Sjmcneill
981.1Sjmcneillstruct si_softc;
991.1Sjmcneill
1001.1Sjmcneillstruct si_channel {
1011.1Sjmcneill	struct si_softc		*ch_sc;
1021.1Sjmcneill	device_t		ch_dev;
1031.1Sjmcneill	unsigned		ch_index;
1041.1Sjmcneill	struct hidev_tag	ch_hidev;
1051.1Sjmcneill	kmutex_t		ch_lock;
1061.1Sjmcneill	kcondvar_t		ch_cv;
1071.1Sjmcneill	uint8_t			ch_state;
1081.1Sjmcneill#define SI_STATE_OPEN		__BIT(0)
1091.1Sjmcneill#define SI_STATE_STOPPED	__BIT(1)
1101.1Sjmcneill	void			(*ch_intr)(void *, void *, u_int);
1111.1Sjmcneill	void			*ch_intrarg;
1121.1Sjmcneill	uint8_t			ch_buf[GCPAD_REPORT_SIZE];
1131.1Sjmcneill	void			*ch_desc;
1141.1Sjmcneill	int			ch_descsize;
1151.2Sjmcneill	void			*ch_si;
1161.1Sjmcneill};
1171.1Sjmcneill
1181.1Sjmcneillstruct si_softc {
1191.1Sjmcneill	device_t		sc_dev;
1201.1Sjmcneill	bus_space_tag_t		sc_bst;
1211.1Sjmcneill	bus_space_handle_t	sc_bsh;
1221.1Sjmcneill
1231.1Sjmcneill	struct si_channel	sc_chan[SI_NUM_CHAN];
1241.1Sjmcneill};
1251.1Sjmcneill
1261.1Sjmcneill#define RD4(sc, reg)							\
1271.1Sjmcneill	bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg))
1281.1Sjmcneill#define WR4(sc, reg, val)						\
1291.1Sjmcneill	bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val))
1301.1Sjmcneill
1311.1Sjmcneillstatic int	si_match(device_t, cfdata_t, void *);
1321.1Sjmcneillstatic void	si_attach(device_t, device_t, void *);
1331.1Sjmcneill
1341.1Sjmcneillstatic int	si_intr(void *);
1351.2Sjmcneillstatic void	si_softintr(void *);
1361.1Sjmcneill
1371.1Sjmcneillstatic int	si_rescan(device_t, const char *, const int *);
1381.1Sjmcneillstatic int	si_print(void *, const char *);
1391.1Sjmcneill
1401.1Sjmcneillstatic void	si_get_report_desc(void *, void **, int *);
1411.1Sjmcneillstatic int	si_open(void *, void (*)(void *, void *, unsigned), void *);
1421.1Sjmcneillstatic void	si_stop(void *);
1431.1Sjmcneillstatic void	si_close(void *);
1441.1Sjmcneillstatic usbd_status si_set_report(void *, int, void *, int);
1451.1Sjmcneillstatic usbd_status si_get_report(void *, int, void *, int);
1461.1Sjmcneillstatic usbd_status si_write(void *, void *, int);
1471.1Sjmcneill
1481.1SjmcneillCFATTACH_DECL_NEW(si, sizeof(struct si_softc),
1491.1Sjmcneill	si_match, si_attach, NULL, NULL);
1501.1Sjmcneill
1511.1Sjmcneillstatic int
1521.1Sjmcneillsi_match(device_t parent, cfdata_t cf, void *aux)
1531.1Sjmcneill{
1541.1Sjmcneill	struct mainbus_attach_args *maa = aux;
1551.1Sjmcneill
1561.1Sjmcneill	return strcmp(maa->maa_name, "si") == 0;
1571.1Sjmcneill}
1581.1Sjmcneill
1591.1Sjmcneillstatic void
1601.1Sjmcneillsi_attach(device_t parent, device_t self, void *aux)
1611.1Sjmcneill{
1621.1Sjmcneill	struct mainbus_attach_args * const maa = aux;
1631.1Sjmcneill	struct si_softc * const sc = device_private(self);
1641.1Sjmcneill	unsigned chan;
1651.1Sjmcneill	void *ih;
1661.1Sjmcneill
1671.1Sjmcneill	KASSERT(device_unit(self) == 0);
1681.1Sjmcneill
1691.1Sjmcneill	aprint_naive("\n");
1701.1Sjmcneill	aprint_normal(": Serial Interface\n");
1711.1Sjmcneill
1721.1Sjmcneill	sc->sc_dev = self;
1731.1Sjmcneill	sc->sc_bst = maa->maa_bst;
1741.1Sjmcneill	if (bus_space_map(sc->sc_bst, maa->maa_addr, SI_SIZE, 0,
1751.1Sjmcneill	    &sc->sc_bsh) != 0) {
1761.1Sjmcneill		aprint_error_dev(self, "couldn't map registers\n");
1771.1Sjmcneill		return;
1781.1Sjmcneill	}
1791.1Sjmcneill
1801.1Sjmcneill	for (chan = 0; chan < SI_NUM_CHAN; chan++) {
1811.1Sjmcneill		struct si_channel *ch;
1821.1Sjmcneill		struct hidev_tag *t;
1831.1Sjmcneill
1841.1Sjmcneill		ch = &sc->sc_chan[chan];
1851.1Sjmcneill		ch->ch_sc = sc;
1861.1Sjmcneill		ch->ch_index = chan;
1871.1Sjmcneill		mutex_init(&ch->ch_lock, MUTEX_DEFAULT, IPL_VM);
1881.1Sjmcneill		cv_init(&ch->ch_cv, "sich");
1891.2Sjmcneill		ch->ch_si = softint_establish(SOFTINT_SERIAL,
1901.2Sjmcneill		    si_softintr, ch);
1911.2Sjmcneill		KASSERT(ch->ch_si != NULL);
1921.1Sjmcneill
1931.1Sjmcneill		t = &ch->ch_hidev;
1941.1Sjmcneill		t->_cookie = &sc->sc_chan[chan];
1951.1Sjmcneill		t->_get_report_desc = si_get_report_desc;
1961.1Sjmcneill		t->_open = si_open;
1971.1Sjmcneill		t->_stop = si_stop;
1981.1Sjmcneill		t->_close = si_close;
1991.1Sjmcneill		t->_set_report = si_set_report;
2001.1Sjmcneill		t->_get_report = si_get_report;
2011.1Sjmcneill		t->_write = si_write;
2021.1Sjmcneill	}
2031.1Sjmcneill
2041.1Sjmcneill	WR4(sc, SIPOLL,
2051.1Sjmcneill	    __SHIFTIN(7, SIPOLL_X) |
2061.1Sjmcneill	    __SHIFTIN(1, SIPOLL_Y));
2071.1Sjmcneill	WR4(sc, SICOMCSR, SICOMCSR_RDSTINT | SICOMCSR_RDSTINTMSK);
2081.1Sjmcneill
2091.1Sjmcneill	ih = intr_establish_xname(maa->maa_irq, IST_LEVEL, IPL_VM, si_intr, sc,
2101.1Sjmcneill	    device_xname(self));
2111.1Sjmcneill	KASSERT(ih != NULL);
2121.1Sjmcneill
2131.1Sjmcneill	si_rescan(self, NULL, NULL);
2141.1Sjmcneill}
2151.1Sjmcneill
2161.1Sjmcneillstatic int
2171.1Sjmcneillsi_rescan(device_t self, const char *ifattr, const int *locs)
2181.1Sjmcneill{
2191.1Sjmcneill	struct si_softc * const sc = device_private(self);
2201.1Sjmcneill	struct si_attach_args saa;
2211.1Sjmcneill	unsigned chan;
2221.1Sjmcneill
2231.1Sjmcneill	for (chan = 0; chan < SI_NUM_CHAN; chan++) {
2241.1Sjmcneill		struct si_channel *ch = &sc->sc_chan[chan];
2251.1Sjmcneill
2261.1Sjmcneill		if (ch->ch_dev == NULL) {
2271.1Sjmcneill			saa.saa_hidev = &ch->ch_hidev;
2281.1Sjmcneill			saa.saa_index = ch->ch_index;
2291.1Sjmcneill
2301.1Sjmcneill			ch->ch_dev = config_found(self, &saa, si_print,
2311.1Sjmcneill			    CFARGS(.submatch = config_stdsubmatch,
2321.1Sjmcneill				   .locators = locs));
2331.1Sjmcneill		}
2341.1Sjmcneill	}
2351.1Sjmcneill
2361.1Sjmcneill	return 0;
2371.1Sjmcneill}
2381.1Sjmcneill
2391.1Sjmcneillstatic int
2401.1Sjmcneillsi_print(void *aux, const char *pnp)
2411.1Sjmcneill{
2421.1Sjmcneill	struct si_attach_args *saa = aux;
2431.1Sjmcneill
2441.1Sjmcneill	if (pnp != NULL) {
2451.1Sjmcneill		aprint_normal("uhid at %s", pnp);
2461.1Sjmcneill	}
2471.1Sjmcneill
2481.1Sjmcneill	/*
2491.1Sjmcneill	 * The Wii Operations Manual for RVL-001 refers to the controller
2501.1Sjmcneill	 * ports as "Nintendo GameCube Controller Sockets".
2511.1Sjmcneill	 */
2521.1Sjmcneill	aprint_normal(" socket %d", saa->saa_index + 1);
2531.1Sjmcneill
2541.1Sjmcneill	return UNCONF;
2551.1Sjmcneill}
2561.1Sjmcneill
2571.1Sjmcneillstatic void
2581.1Sjmcneillsi_make_report(struct si_softc *sc, unsigned chan, void *report, bool with_rid)
2591.1Sjmcneill{
2601.1Sjmcneill	uint32_t inbuf[2];
2611.1Sjmcneill	uint8_t *iptr = (uint8_t *)inbuf;
2621.1Sjmcneill	uint8_t *optr = report;
2631.1Sjmcneill	unsigned off = 0;
2641.1Sjmcneill
2651.1Sjmcneill	inbuf[0] = RD4(sc, SICINBUFH(chan));
2661.1Sjmcneill	inbuf[1] = RD4(sc, SICINBUFL(chan));
2671.1Sjmcneill
2681.1Sjmcneill	if (with_rid) {
2691.1Sjmcneill		optr[off++] = chan + 1;
2701.1Sjmcneill	}
2711.1Sjmcneill
2721.1Sjmcneill	optr[off] = 0;
2731.1Sjmcneill	optr[off] |= GCPAD_X(iptr)	? 0x01 : 0;
2741.1Sjmcneill	optr[off] |= GCPAD_A(iptr)	? 0x02 : 0;
2751.1Sjmcneill	optr[off] |= GCPAD_B(iptr)	? 0x04 : 0;
2761.1Sjmcneill	optr[off] |= GCPAD_Y(iptr)	? 0x08 : 0;
2771.1Sjmcneill	optr[off] |= GCPAD_LCLICK(iptr)	? 0x10 : 0;
2781.1Sjmcneill	optr[off] |= GCPAD_RCLICK(iptr)	? 0x20 : 0;
2791.1Sjmcneill	optr[off] |= GCPAD_Z(iptr)	? 0x80 : 0;
2801.1Sjmcneill	off++;
2811.1Sjmcneill
2821.1Sjmcneill	optr[off] = 0;
2831.1Sjmcneill	optr[off] |= GCPAD_START(iptr)	? 0x02 : 0;
2841.1Sjmcneill	optr[off] |= GCPAD_UP(iptr)	? 0x10 : 0;
2851.1Sjmcneill	optr[off] |= GCPAD_RIGHT(iptr)	? 0x20 : 0;
2861.1Sjmcneill	optr[off] |= GCPAD_DOWN(iptr)	? 0x40 : 0;
2871.1Sjmcneill	optr[off] |= GCPAD_LEFT(iptr)	? 0x80 : 0;
2881.1Sjmcneill	off++;
2891.1Sjmcneill
2901.1Sjmcneill	memcpy(&optr[off], &iptr[2], 6);
2911.1Sjmcneill	off += 6;
2921.1Sjmcneill
2931.1Sjmcneill	optr[off++] = 0;
2941.1Sjmcneill}
2951.1Sjmcneill
2961.2Sjmcneillstatic void
2971.2Sjmcneillsi_softintr(void *priv)
2981.2Sjmcneill{
2991.2Sjmcneill	struct si_channel *ch = priv;
3001.2Sjmcneill
3011.2Sjmcneill	if (ISSET(ch->ch_state, SI_STATE_OPEN)) {
3021.2Sjmcneill		ch->ch_intr(ch->ch_intrarg, ch->ch_buf, sizeof(ch->ch_buf));
3031.2Sjmcneill	}
3041.2Sjmcneill}
3051.2Sjmcneill
3061.1Sjmcneillstatic int
3071.1Sjmcneillsi_intr(void *priv)
3081.1Sjmcneill{
3091.1Sjmcneill	struct si_softc *sc = priv;
3101.1Sjmcneill	unsigned chan;
3111.1Sjmcneill	uint32_t comcsr, sr;
3121.1Sjmcneill	int ret = 0;
3131.1Sjmcneill
3141.1Sjmcneill	comcsr = RD4(sc, SICOMCSR);
3151.1Sjmcneill	sr = RD4(sc, SISR);
3161.1Sjmcneill
3171.1Sjmcneill	if (ISSET(comcsr, SICOMCSR_TCINT)) {
3181.1Sjmcneill		WR4(sc, SICOMCSR, comcsr | SICOMCSR_TCINT);
3191.1Sjmcneill	}
3201.1Sjmcneill
3211.1Sjmcneill	if (ISSET(comcsr, SICOMCSR_RDSTINT)) {
3221.1Sjmcneill		for (chan = 0; chan < SI_NUM_CHAN; chan++) {
3231.1Sjmcneill			struct si_channel *ch = &sc->sc_chan[chan];
3241.1Sjmcneill
3251.1Sjmcneill			if (ISSET(sr, SISR_RDST(chan))) {
3261.1Sjmcneill				/* Reading INBUF[HL] de-asserts RDSTINT. */
3271.1Sjmcneill				si_make_report(sc, chan, ch->ch_buf, false);
3281.1Sjmcneill
3291.1Sjmcneill				if (ISSET(ch->ch_state, SI_STATE_OPEN)) {
3301.2Sjmcneill					softint_schedule(ch->ch_si);
3311.1Sjmcneill				}
3321.1Sjmcneill			}
3331.1Sjmcneill
3341.1Sjmcneill			ret = 1;
3351.1Sjmcneill		}
3361.1Sjmcneill	}
3371.1Sjmcneill
3381.1Sjmcneill	WR4(sc, SISR, sr & SISR_ERROR_ACK_ALL);
3391.1Sjmcneill
3401.1Sjmcneill	return ret;
3411.1Sjmcneill}
3421.1Sjmcneill
3431.1Sjmcneillstatic void
3441.1Sjmcneillsi_get_report_desc(void *cookie, void **desc, int *size)
3451.1Sjmcneill{
3461.1Sjmcneill	*desc = gcpad_report_descr;
3471.1Sjmcneill	*size = sizeof(gcpad_report_descr);
3481.1Sjmcneill}
3491.1Sjmcneill
3501.1Sjmcneillstatic int
3511.1Sjmcneillsi_open(void *cookie, void (*intr)(void *, void *, u_int), void *arg)
3521.1Sjmcneill{
3531.1Sjmcneill	struct si_channel *ch = cookie;
3541.1Sjmcneill	struct si_softc *sc = ch->ch_sc;
3551.1Sjmcneill	int error;
3561.1Sjmcneill
3571.1Sjmcneill	mutex_enter(&ch->ch_lock);
3581.1Sjmcneill
3591.1Sjmcneill	if (ISSET(ch->ch_state, SI_STATE_OPEN)) {
3601.1Sjmcneill		error = EBUSY;
3611.1Sjmcneill		goto unlock;
3621.1Sjmcneill	}
3631.1Sjmcneill
3641.1Sjmcneill	ch->ch_intr = intr;
3651.1Sjmcneill	ch->ch_intrarg = arg;
3661.1Sjmcneill	ch->ch_state |= SI_STATE_OPEN;
3671.1Sjmcneill
3681.1Sjmcneill	(void)RD4(sc, SICINBUFH(ch->ch_index));
3691.1Sjmcneill	(void)RD4(sc, SICINBUFL(ch->ch_index));
3701.1Sjmcneill
3711.1Sjmcneill	/* Init controller */
3721.1Sjmcneill	WR4(sc, SICOUTBUF(ch->ch_index), 0x00400300);
3731.1Sjmcneill
3741.1Sjmcneill	/* Enable polling */
3751.1Sjmcneill	WR4(sc, SIPOLL, RD4(sc, SIPOLL) | SIPOLL_EN(ch->ch_index));
3761.1Sjmcneill
3771.1Sjmcneill	WR4(sc, SISR, SISR_WR(ch->ch_index));
3781.1Sjmcneill	WR4(sc, SICOMCSR, RD4(sc, SICOMCSR) | SICOMCSR_TSTART);
3791.1Sjmcneill
3801.1Sjmcneill	error = 0;
3811.1Sjmcneill
3821.1Sjmcneillunlock:
3831.1Sjmcneill	mutex_exit(&ch->ch_lock);
3841.1Sjmcneill
3851.1Sjmcneill	return error;
3861.1Sjmcneill}
3871.1Sjmcneill
3881.1Sjmcneillstatic void
3891.1Sjmcneillsi_stop(void *cookie)
3901.1Sjmcneill{
3911.1Sjmcneill	struct si_channel *ch = cookie;
3921.1Sjmcneill
3931.1Sjmcneill	mutex_enter(&ch->ch_lock);
3941.1Sjmcneill
3951.1Sjmcneill	ch->ch_state |= SI_STATE_STOPPED;
3961.1Sjmcneill
3971.1Sjmcneill	cv_broadcast(&ch->ch_cv);
3981.1Sjmcneill	mutex_exit(&ch->ch_lock);
3991.1Sjmcneill}
4001.1Sjmcneill
4011.1Sjmcneillstatic void
4021.1Sjmcneillsi_close(void *cookie)
4031.1Sjmcneill{
4041.1Sjmcneill	struct si_channel *ch = cookie;
4051.1Sjmcneill	struct si_softc *sc = ch->ch_sc;
4061.1Sjmcneill
4071.1Sjmcneill	mutex_enter(&ch->ch_lock);
4081.1Sjmcneill
4091.1Sjmcneill	/* Diable polling */
4101.1Sjmcneill	WR4(sc, SIPOLL, RD4(sc, SIPOLL) & ~SIPOLL_EN(ch->ch_index));
4111.1Sjmcneill
4121.1Sjmcneill	ch->ch_state &= ~(SI_STATE_OPEN | SI_STATE_STOPPED);
4131.1Sjmcneill	ch->ch_intr = NULL;
4141.1Sjmcneill	ch->ch_intrarg = NULL;
4151.1Sjmcneill
4161.1Sjmcneill	cv_broadcast(&ch->ch_cv);
4171.1Sjmcneill	mutex_exit(&ch->ch_lock);
4181.1Sjmcneill}
4191.1Sjmcneill
4201.1Sjmcneillstatic usbd_status
4211.1Sjmcneillsi_set_report(void *cookie, int type, void *data, int len)
4221.1Sjmcneill{
4231.1Sjmcneill        return USBD_INVAL;
4241.1Sjmcneill}
4251.1Sjmcneill
4261.1Sjmcneillstatic usbd_status
4271.1Sjmcneillsi_get_report(void *cookie, int type, void *data, int len)
4281.1Sjmcneill{
4291.1Sjmcneill	struct si_channel *ch = cookie;
4301.1Sjmcneill	struct si_softc *sc = ch->ch_sc;
4311.1Sjmcneill	uint32_t *inbuf = data;
4321.1Sjmcneill
4331.1Sjmcneill	if (len != GCPAD_REPORT_SIZE + 1) {
4341.1Sjmcneill		return USBD_IOERROR;
4351.1Sjmcneill	}
4361.1Sjmcneill
4371.1Sjmcneill	mutex_enter(&ch->ch_lock);
4381.1Sjmcneill	si_make_report(sc, ch->ch_index, inbuf, true);
4391.1Sjmcneill	mutex_exit(&ch->ch_lock);
4401.1Sjmcneill
4411.1Sjmcneill	return USBD_NORMAL_COMPLETION;
4421.1Sjmcneill}
4431.1Sjmcneill
4441.1Sjmcneillstatic usbd_status
4451.1Sjmcneillsi_write(void *cookie, void *data, int len)
4461.1Sjmcneill{
4471.1Sjmcneill        return USBD_INVAL;
4481.1Sjmcneill}
449