diff --git a/etc/etc.evbppc/MAKEDEV.conf b/etc/etc.evbppc/MAKEDEV.conf index e866d6ad088fd..d8577b7240068 100644 --- a/etc/etc.evbppc/MAKEDEV.conf +++ b/etc/etc.evbppc/MAKEDEV.conf @@ -26,6 +26,8 @@ all_md) makedev virtio ;; +# TODO - i think i can add something here to create my device files +# instead of manually calling mknod i just dont know yet.. # ramdisk definition is found at distrib/evbppc/ramdisk/Makefile xlcom[0-9]*) diff --git a/sys/arch/evbppc/conf/NINTENDO b/sys/arch/evbppc/conf/NINTENDO index 03672719ce36e..efee8e23b10ba 100644 --- a/sys/arch/evbppc/conf/NINTENDO +++ b/sys/arch/evbppc/conf/NINTENDO @@ -133,6 +133,7 @@ options WSDISPLAY_MULTICONS ahb0 at mainbus0 irq 14 si0 at mainbus0 addr 0x0d006400 irq 3 # Serial interface uhid* at si0 +gcport* at si0 exi0 at mainbus0 addr 0x0d006800 irq 4 # External interface rtcsram0 at exi0 # RTC/SRAM chip gecko0 at exi0 # USB Gecko diff --git a/sys/arch/evbppc/conf/majors.evbppc b/sys/arch/evbppc/conf/majors.evbppc index 10f9eb8bf8427..20dce4457788a 100644 --- a/sys/arch/evbppc/conf/majors.evbppc +++ b/sys/arch/evbppc/conf/majors.evbppc @@ -83,6 +83,8 @@ device-major twe char 76 twe #device-major obsolete char 98 obsolete (nsmb) device-major xlcom char 99 xlcom +device-major gcport char 100 + # Majors up to 143 are reserved for machine-dependent drivers. # New machine-independent driver majors are assigned in # sys/conf/majors. diff --git a/sys/arch/evbppc/nintendo/dev/files.dev b/sys/arch/evbppc/nintendo/dev/files.dev index cd053d28aadb2..ad70a9ff3b5ab 100644 --- a/sys/arch/evbppc/nintendo/dev/files.dev +++ b/sys/arch/evbppc/nintendo/dev/files.dev @@ -37,6 +37,10 @@ file arch/evbppc/nintendo/dev/si.c si attach uhid at si with uhid_si file arch/evbppc/nintendo/dev/uhid_si.c uhid_si +device gcport +attach gcport at si with gcport_si +file arch/evbppc/nintendo/dev/gcport_si.c gcport_si + define ahb { [addr=-1], [irq=-1] } device ahb: ahb attach ahb at mainbus diff --git a/sys/arch/evbppc/nintendo/dev/gcport_si.c b/sys/arch/evbppc/nintendo/dev/gcport_si.c new file mode 100644 index 0000000000000..c708a0babebb3 --- /dev/null +++ b/sys/arch/evbppc/nintendo/dev/gcport_si.c @@ -0,0 +1,237 @@ +/* $NetBSD: gcport_si.c,v 1.0 2026/05/18 22:54:30 gummybuns Exp $ */ + +/*- + * Copyright (c) 2026 ZacBrown + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__KERNEL_RCSID(0, "$NetBSD: gcport_si.c,v 1.0 2026/05/18 22:53:30 gummybuns Exp $"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "si.h" +#include "joybus.h" + +struct si_payload { + uint32_t insize; /* bytes to receive. max 128 */ + uint32_t outsize; /* bytes to send. max 128 */ + uint32_t *status; /* sisr status for this channel */ + void *in; /* buffer to store response */ + void *out; /* buffer to send out to device */ + long delay; /* delay the transaction (microsec) */ +}; + +extern struct cfdriver gcport_cd; +struct gcport_softc { + device_t sc_dev; + struct si_channel *ch; + bus_space_tag_t sc_bst; + bus_space_handle_t sc_bsh; +}; + + +#define SI_SEND _IOWR(0, 1, struct si_payload) + +static int gcport_si_match(device_t, cfdata_t, void *); +static void gcport_si_attach(device_t, device_t, void *); +static int siioctl_send(struct si_channel *ch, struct si_payload *sp); + +dev_type_open(gcport_open); +dev_type_close(gcport_close); +dev_type_ioctl(gcport_ioctl); + +const struct cdevsw gcport_cdevsw = { + .d_open = gcport_open, + .d_close = gcport_close, + .d_ioctl = gcport_ioctl, + .d_read = noread, + .d_write = nowrite, + .d_stop = nostop, + .d_tty = notty, + .d_poll = nopoll, + .d_mmap = nommap, + .d_kqfilter = nokqfilter, + .d_discard = nodiscard, + .d_flag = D_OTHER +}; + +CFATTACH_DECL_NEW(gcport_si, sizeof(struct gcport_softc), + gcport_si_match, gcport_si_attach, NULL, NULL); + +static int +gcport_si_match(device_t parent, cfdata_t cf, void *aux) +{ + struct si_softc * const sc = device_private(parent); + struct si_attach_args * const saa = aux; + struct si_channel *ch; + unsigned chan; + + chan = saa->saa_index; + ch = &sc->sc_chan[chan]; + aprint_normal("gcport: checking 0x%08X...\n", ch->ch_id); + /* NOTE - existing functionality uses uhid for gc_pad. This will + * match any other device + */ + if (ch->ch_id != 0 && !(IS_GCPAD(ch->ch_id))) { + aprint_normal("gcport: identified ch%d as a device 0x%08X\n", + chan, ch->ch_id); + return 1; + } + + return 0; +} + +static void +gcport_si_attach(device_t parent, device_t self, void *aux) +{ + struct si_softc * const psc = device_private(parent); + struct si_attach_args * const saa = aux; + struct gcport_softc * const sc = device_private(self); + struct si_channel *ch = &psc->sc_chan[saa->saa_index]; + + sc->sc_dev = self; + sc->ch = ch; + sc->sc_bst = psc->sc_bst; + sc->sc_bsh = psc->sc_bsh; +} + +int +gcport_open(dev_t dev, int flags, int mode, struct lwp *l) +{ + struct gcport_softc *sc; + struct si_channel *ch; + int error; + + /* TODO maybe there is a way to have the minor number reflect the + * actual port instead of an auto increment thing + */ + sc = device_lookup_private(&gcport_cd, minor(dev)); + + if (sc == NULL) { + return ENXIO; + } + + ch = sc->ch; + mutex_enter(&ch->ch_lock); + + if (ISSET(ch->ch_state, SI_STATE_OPEN)) { + error = EBUSY; + goto unlock; + } + + ch->ch_state |= SI_STATE_OPEN; + error = 0; +unlock: + mutex_exit(&ch->ch_lock); + return error; +} + +int +gcport_close(dev_t dev, int flags, int mode, struct lwp *l) +{ + struct gcport_softc *sc = device_lookup_private(&gcport_cd, minor(dev)); + struct si_channel *ch = sc->ch; + + mutex_enter(&ch->ch_lock); + ch->ch_state &= ~(SI_STATE_OPEN | SI_STATE_STOPPED); + + /* cv_init is called in parent's si_attach */ + cv_broadcast(&ch->ch_cv); + + mutex_exit(&ch->ch_lock); + return 0; +} + +int +gcport_ioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l) +{ + struct gcport_softc *sc = device_lookup_private(&gcport_cd, minor(dev)); + struct si_channel *ch = sc->ch; + int err; + + switch(cmd) { + case SI_SEND: + err = siioctl_send(ch, (struct si_payload *)data); + break; + default: + err = EINVAL; + break; + } + + return err; +} + +static int +siioctl_send(struct si_channel *ch, struct si_payload *gbs) +{ + int err; + struct si_softc *sc; + struct siio_send sd; + + err = 0; + sc = ch->ch_sc; + if (gbs->outsize > SIIOBUF_SIZE || gbs->insize > SIIOBUF_SIZE) { + return EINVAL; + } + + sd.chan = ch->ch_index; + sd.outsize = gbs->outsize; + sd.insize = gbs->insize; + sd.out = kmem_alloc(gbs->outsize, KM_SLEEP); + sd.in = kmem_alloc(gbs->insize, KM_SLEEP); + sd.delay = gbs->delay; + + if ((err = copyin(gbs->out, sd.out, sd.outsize)) != 0) { + goto si_send_cleanup; + } + + if ((err = __si_send(sc, &sd)) != 0) { + goto si_send_cleanup; + } + + if ((err = copyout(sd.in, gbs->in, sd.insize)) != 0) { + goto si_send_cleanup; + } + + if ((err = copyout(&sd.status, gbs->status, sizeof(uint32_t))) != 0) { + goto si_send_cleanup; + } +si_send_cleanup: + kmem_free(sd.out, sd.outsize); + kmem_free(sd.in, sd.insize); + return err; +} diff --git a/sys/arch/evbppc/nintendo/dev/joybus.h b/sys/arch/evbppc/nintendo/dev/joybus.h new file mode 100644 index 0000000000000..cf268a5caa9ee --- /dev/null +++ b/sys/arch/evbppc/nintendo/dev/joybus.h @@ -0,0 +1,71 @@ +/* $NetBSD: joybus.h,v 1.0 2026/05/18 22:54:30 gummybuns Exp $ */ + +/*- + * Copyright (c) 2026 Zac Brown + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _JOYBUS_H_ +#define _JOYBUS_H_ + +#include +#include "si.h" + +#define JB_WIRELESS __BIT(15) +#define JB_WIRELESS_RECV __BIT(14) +#define JB_RUMBLE __BIT(13) +#define JB_CONTROLLER __BIT(11) +#define JB_WIRELESS_TYPE __BIT(10) +#define JB_WIRELESS_STATE __BIT(9) +#define JB_DOLPHIN __BIT(8) +#define JB_WIRELESS_ORIGIN __BIT(5) +#define JB_WIRELESS_FIXID __BIT(4) +#define JB_WIRELESS_NONCTRL __BIT(3) +#define JB_WIRELESS_LITE __BIT(2) + +#define CMD_IDENTIFY 0x00000000 /* pad with zeros to clear siiobuf */ +#define CMD_RESET 0xFF000000 /* pad with zeros to clear siiobuf */ +#define CMD_GBA_READ 0x14 +#define CMD_GBA_WRITE 0x15 + +#define JB_NONE 0x0000 +#define JB_N64 0x0500 +#define JB_N64MIC 0x0001 +#define JB_N64KB 0x0002 +#define JB_N64MS 0x0200 +#define JB_GBA 0x0004 +#define JB_GC 0x0900 +#define JB_WAVEBRD_RECV 0xe960 +#define JB_WAVEBRD JB_WIRELESS & JB_RUMBLE & JB_CONTROLLER +#define JB_GCKB 0x0802 +#define JB_GCSTEER 0x0800 +#define JB_GBABIOS 0x08 /* GBA BIOS actually sends a 1byte response and + * sets SISR to NOREP. The second byte will be + * whatever was in SIIOBUF before send */ + +#define IS_DOLPHIN(n) ISSET(n, JB_CONTROLLER) +#define IS_N64(n) !ISSET(n, JB_CONTROLLER) +#define IS_GCPAD(n) (((n) & (JB_CONTROLLER | JB_DOLPHIN)) == JB_GC) || \ + ISSET(n, JB_WIRELESS) +#endif diff --git a/sys/arch/evbppc/nintendo/dev/si.c b/sys/arch/evbppc/nintendo/dev/si.c index d55c06cfae1cf..160bd9a9ef33a 100644 --- a/sys/arch/evbppc/nintendo/dev/si.c +++ b/sys/arch/evbppc/nintendo/dev/si.c @@ -47,86 +47,8 @@ __KERNEL_RCSID(0, "$NetBSD: si.c,v 1.1 2026/01/09 22:54:30 jmcneill Exp $"); #include "mainbus.h" #include "si.h" #include "gcpad_rdesc.h" +#include "joybus.h" -#define SI_NUM_CHAN 4 - -#define SICOUTBUF(n) ((n) * 0xc + 0x00) -#define SICINBUFH(n) ((n) * 0xc + 0x04) -#define SICINBUFL(n) ((n) * 0xc + 0x08) -#define SIPOLL 0x30 -#define SIPOLL_X __BITS(25, 16) -#define SIPOLL_Y __BITS(15, 8) -#define SIPOLL_EN(n) (__BIT(7 - n)) -#define SICOMCSR 0x34 -#define SICOMCSR_TCINT __BIT(31) -#define SICOMCSR_TCINTMSK __BIT(30) -#define SICOMCSR_RDSTINT __BIT(28) -#define SICOMCSR_RDSTINTMSK __BIT(27) -#define SICOMCSR_OUTLNGTH __BITS(22, 16) -#define SICOMCSR_INLNGTH __BITS(14, 8) -#define SICOMCSR_TSTART __BIT(0) -#define SISR 0x38 -#define SISR_OFF(n) ((3 - (n)) * 8) -#define SISR_WR(n) __BIT(SISR_OFF(n) + 7) -#define SISR_RDST(n) __BIT(SISR_OFF(n) + 5) -#define SISR_WRST(n) __BIT(SISR_OFF(n) + 4) -#define SISR_NOREP(n) __BIT(SISR_OFF(n) + 3) -#define SISR_COLL(n) __BIT(SISR_OFF(n) + 2) -#define SISR_OVRUN(n) __BIT(SISR_OFF(n) + 1) -#define SISR_UNRUN(n) __BIT(SISR_OFF(n) + 0) -#define SISR_ERROR_MASK(n) (SISR_NOREP(n) | SISR_COLL(n) | \ - SISR_OVRUN(n) | SISR_UNRUN(n)) -#define SISR_ERROR_ACK_ALL (SISR_ERROR_MASK(0) | SISR_ERROR_MASK(1) | \ - SISR_ERROR_MASK(2) | SISR_ERROR_MASK(3)) -#define SIEXILK 0x3c -#define SIIOBUF 0x80 - -#define GCPAD_REPORT_SIZE 9 -#define GCPAD_START(_buf) ISSET((_buf)[0], 0x10) -#define GCPAD_Y(_buf) ISSET((_buf)[0], 0x08) -#define GCPAD_X(_buf) ISSET((_buf)[0], 0x04) -#define GCPAD_B(_buf) ISSET((_buf)[0], 0x02) -#define GCPAD_A(_buf) ISSET((_buf)[0], 0x01) -#define GCPAD_LCLICK(_buf) ISSET((_buf)[1], 0x40) -#define GCPAD_RCLICK(_buf) ISSET((_buf)[1], 0x20) -#define GCPAD_Z(_buf) ISSET((_buf)[1], 0x10) -#define GCPAD_UP(_buf) ISSET((_buf)[1], 0x08) -#define GCPAD_DOWN(_buf) ISSET((_buf)[1], 0x04) -#define GCPAD_RIGHT(_buf) ISSET((_buf)[1], 0x02) -#define GCPAD_LEFT(_buf) ISSET((_buf)[1], 0x01) - -struct si_softc; - -struct si_channel { - struct si_softc *ch_sc; - device_t ch_dev; - unsigned ch_index; - struct hidev_tag ch_hidev; - kmutex_t ch_lock; - kcondvar_t ch_cv; - uint8_t ch_state; -#define SI_STATE_OPEN __BIT(0) -#define SI_STATE_STOPPED __BIT(1) - void (*ch_intr)(void *, void *, u_int); - void *ch_intrarg; - uint8_t ch_buf[GCPAD_REPORT_SIZE]; - void *ch_desc; - int ch_descsize; - void *ch_si; -}; - -struct si_softc { - device_t sc_dev; - bus_space_tag_t sc_bst; - bus_space_handle_t sc_bsh; - - struct si_channel sc_chan[SI_NUM_CHAN]; -}; - -#define RD4(sc, reg) \ - bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg)) -#define WR4(sc, reg, val) \ - bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val)) static int si_match(device_t, cfdata_t, void *); static void si_attach(device_t, device_t, void *); @@ -136,6 +58,8 @@ static void si_softintr(void *); static int si_rescan(device_t, const char *, const int *); static int si_print(void *, const char *); +static uint32_t send_cmd(struct si_softc *, uint32_t, unsigned, long); +static int si_identify(device_t, unsigned); static void si_get_report_desc(void *, void **, int *); static int si_open(void *, void (*)(void *, void *, unsigned), void *); @@ -145,6 +69,8 @@ static usbd_status si_set_report(void *, int, void *, int); static usbd_status si_get_report(void *, int, void *, int); static usbd_status si_write(void *, void *, int); +kmutex_t sicomcsr_lock; + CFATTACH_DECL_NEW(si, sizeof(struct si_softc), si_match, si_attach, NULL, NULL); @@ -176,6 +102,7 @@ si_attach(device_t parent, device_t self, void *aux) aprint_error_dev(self, "couldn't map registers\n"); return; } + mutex_init(&sicomcsr_lock, MUTEX_DEFAULT, IPL_VM); for (chan = 0; chan < SI_NUM_CHAN; chan++) { struct si_channel *ch; @@ -190,6 +117,8 @@ si_attach(device_t parent, device_t self, void *aux) si_softintr, ch); KASSERT(ch->ch_si != NULL); + si_identify(self, chan); + t = &ch->ch_hidev; t->_cookie = &sc->sc_chan[chan]; t->_get_report_desc = si_get_report_desc; @@ -254,6 +183,42 @@ si_print(void *aux, const char *pnp) return UNCONF; } + +static uint32_t +send_cmd(struct si_softc *sc, uint32_t cmd, unsigned chan, long us) +{ + struct siio_send data; + uint32_t out[1]; + uint32_t in[1]; + out[0] = cmd; + + data.chan = chan; + data.outsize = 1; + data.insize = 3; + data.in = in; + data.out = out; + data.delay = us; + + __si_send(sc, &data); + return in[0]; +} + +static int +si_identify(device_t self, unsigned chan) +{ + struct si_softc * const sc = device_private(self); + struct si_channel *ch; + uint32_t id; + + ch = &sc->sc_chan[chan]; + + send_cmd(sc, CMD_RESET, chan, 1000); + id = send_cmd(sc, CMD_IDENTIFY, chan, 1000); + ch->ch_id = (uint16_t)(id >> 16); + + return 0; +} + static void si_make_report(struct si_softc *sc, unsigned chan, void *report, bool with_rid) { diff --git a/sys/arch/evbppc/nintendo/dev/si.h b/sys/arch/evbppc/nintendo/dev/si.h index fa7ccb037f8d3..64fd71c194aff 100644 --- a/sys/arch/evbppc/nintendo/dev/si.h +++ b/sys/arch/evbppc/nintendo/dev/si.h @@ -31,9 +31,171 @@ #include +#define SI_NUM_CHAN 4 + +#define SICOUTBUF(n) ((n) * 0xc + 0x00) +#define SICINBUFH(n) ((n) * 0xc + 0x04) +#define SICINBUFL(n) ((n) * 0xc + 0x08) +#define SIPOLL 0x30 +#define SIPOLL_X __BITS(25, 16) +#define SIPOLL_Y __BITS(15, 8) +#define SIPOLL_EN(n) (__BIT(4 + (n))) +#define SICOMCSR 0x34 +#define SICOMCSR_TCINT __BIT(31) +#define SICOMCSR_TCINTMSK __BIT(30) +#define SICOMCSR_COMERR __BIT(29) +#define SICOMCSR_RDSTINT __BIT(28) +#define SICOMCSR_RDSTINTMSK __BIT(27) +#define SICOMCSR_CH_EN __BIT(24) +#define SICOMCSR_OUTLNGTH __BITS(22, 16) +#define SICOMCSR_INLNGTH __BITS(14, 8) +#define SICOMCSR_CMD_EN __BIT(7) +#define SICOMCSR_CHANNEL __BITS(2, 1) +#define SICOMCSR_TSTART __BIT(0) +#define SISR 0x38 +#define SISR_OFF(n) ((3 - (n)) * 8) +#define SISR_WR(n) __BIT(SISR_OFF(n) + 7) +#define SISR_RDST(n) __BIT(SISR_OFF(n) + 5) +#define SISR_WRST(n) __BIT(SISR_OFF(n) + 4) +#define SISR_NOREP(n) __BIT(SISR_OFF(n) + 3) +#define SISR_COLL(n) __BIT(SISR_OFF(n) + 2) +#define SISR_OVRUN(n) __BIT(SISR_OFF(n) + 1) +#define SISR_UNRUN(n) __BIT(SISR_OFF(n) + 0) +#define SISR_ERROR_MASK(n) (SISR_NOREP(n) | SISR_COLL(n) | \ + SISR_OVRUN(n) | SISR_UNRUN(n)) +#define SISR_ERROR_ACK_ALL (SISR_ERROR_MASK(0) | SISR_ERROR_MASK(1) | \ + SISR_ERROR_MASK(2) | SISR_ERROR_MASK(3)) +#define SIEXILK 0x3c +#define SIIOBUF 0x80 +#define SIIOBUF_SIZE 128 + +#define GCPAD_REPORT_SIZE 9 +#define GCPAD_START(_buf) ISSET((_buf)[0], 0x10) +#define GCPAD_Y(_buf) ISSET((_buf)[0], 0x08) +#define GCPAD_X(_buf) ISSET((_buf)[0], 0x04) +#define GCPAD_B(_buf) ISSET((_buf)[0], 0x02) +#define GCPAD_A(_buf) ISSET((_buf)[0], 0x01) +#define GCPAD_LCLICK(_buf) ISSET((_buf)[1], 0x40) +#define GCPAD_RCLICK(_buf) ISSET((_buf)[1], 0x20) +#define GCPAD_Z(_buf) ISSET((_buf)[1], 0x10) +#define GCPAD_UP(_buf) ISSET((_buf)[1], 0x08) +#define GCPAD_DOWN(_buf) ISSET((_buf)[1], 0x04) +#define GCPAD_RIGHT(_buf) ISSET((_buf)[1], 0x02) +#define GCPAD_LEFT(_buf) ISSET((_buf)[1], 0x01) + +#define RD4(sc, reg) \ + bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg)) +#define WR4(sc, reg, val) \ + bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val)) +#define SIIOBUF_CLEAR(sc) \ + bus_space_set_region_1((sc)->sc_bst, (sc)->sc_bsh, SIIOBUF, 0, \ + SIIOBUF_SIZE) +#define SIIOBUF_WR(sc, buf, cnt) \ + bus_space_write_region_4((sc)->sc_bst, (sc)->sc_bsh, SIIOBUF, buf, cnt) +#define SIIOBUF_RD(sc, buf, cnt) \ + bus_space_read_region_1((sc)->sc_bst, (sc)->sc_bsh, SIIOBUF, buf, cnt) + +#define AWAIT_SISR(sc, chan) \ + do { while (RD4(sc, SISR) & SISR_WRST(chan)); } while (0) +#define AWAIT_SICOMCSR(sc) \ + do { while (RD4(sc, SICOMCSR) & SICOMCSR_TSTART); } while (0) + +struct si_softc; +extern kmutex_t sicomcsr_lock; + +struct si_channel { + struct si_softc *ch_sc; + device_t ch_dev; + unsigned ch_index; + struct hidev_tag ch_hidev; + kmutex_t ch_lock; + kcondvar_t ch_cv; + uint8_t ch_state; + uint16_t ch_id; + uint16_t ch_id_extra; +#define SI_STATE_OPEN __BIT(0) +#define SI_STATE_STOPPED __BIT(1) + void (*ch_intr)(void *, void *, u_int); + void *ch_intrarg; + uint8_t ch_buf[GCPAD_REPORT_SIZE]; + void *ch_desc; + int ch_descsize; + void *ch_si; +}; + +struct si_softc { + device_t sc_dev; + bus_space_tag_t sc_bst; + bus_space_handle_t sc_bsh; + + struct si_channel sc_chan[SI_NUM_CHAN]; +}; + struct si_attach_args { struct hidev_tag *saa_hidev; int saa_index; }; +struct siio_send { + unsigned chan; /* which controller port */ + uint32_t status; /* the sisr result for this channel */ + uint32_t insize; /* number of bytes for in buffer */ + uint32_t outsize; /* number of bytes for out buffer */ + void *in; /* buffer to store response */ + void *out; /* buffer to send out to ext device */ + long delay; /* delay the transaction (microsec) */ +}; + +static inline int +__si_send(struct si_softc *sc, struct siio_send *data) +{ + uint32_t cnt, comcsr, sisr, shift_amt, status; + unsigned chan; + + mutex_enter(&sicomcsr_lock); + chan = data->chan; + + SIIOBUF_CLEAR(sc); + data->status = 0; + + /* siiobuf must to be written in increments of 4 bytes */ + cnt = ((data->outsize+3)/4); + SIIOBUF_WR(sc, data->out, cnt); + + WR4(sc, SISR, SISR_ERROR_MASK(chan)); + sisr = RD4(sc, SISR); + + + /* + * libogc uses interrupts for non-blocking. i think this is ok for now + */ + delay(data->delay); + + AWAIT_SICOMCSR(sc); + WR4(sc, SICOMCSR, + SICOMCSR_CH_EN | + SICOMCSR_CMD_EN | + __SHIFTIN(0, SICOMCSR_TCINTMSK) | + __SHIFTIN(data->outsize, SICOMCSR_OUTLNGTH) | + __SHIFTIN(data->insize, SICOMCSR_INLNGTH) | + __SHIFTIN(chan, SICOMCSR_CHANNEL) | + SICOMCSR_TSTART + ); + AWAIT_SICOMCSR(sc); + + comcsr = RD4(sc, SICOMCSR); + WR4(sc, SICOMCSR, comcsr | SICOMCSR_TCINT); + if (ISSET(comcsr, SICOMCSR_COMERR)) { + sisr = RD4(sc, SISR); + shift_amt = 8 * (SI_NUM_CHAN - 1 - chan); + status = ((sisr & SISR_ERROR_MASK(chan)) >> shift_amt) & 0x3F; + data->status = status; + } + + SIIOBUF_RD(sc, data->in, data->insize); + + mutex_exit(&sicomcsr_lock); + return 0; +} + #endif /* _WII_DEV_SI_H_ */ diff --git a/sys/arch/evbppc/nintendo/dev/uhid_si.c b/sys/arch/evbppc/nintendo/dev/uhid_si.c index 61ff6914aec8b..d0191b0f332e1 100644 --- a/sys/arch/evbppc/nintendo/dev/uhid_si.c +++ b/sys/arch/evbppc/nintendo/dev/uhid_si.c @@ -39,6 +39,7 @@ __KERNEL_RCSID(0, "$NetBSD: uhid_si.c,v 1.1 2026/01/09 22:54:30 jmcneill Exp $") #include #include "si.h" +#include "joybus.h" #define UHID_SI_VENDOR 0x057e /* Nintendo */ #define UHID_SI_PRODUCT 0x0337 /* GameCube adapter */ @@ -55,7 +56,16 @@ CFATTACH_DECL_NEW(uhid_si, sizeof(struct uhid_softc), static int uhid_si_match(device_t parent, cfdata_t cf, void *aux) { - return 1; + struct si_softc * const sc = device_private(parent); + struct si_attach_args * const saa = aux; + struct si_channel *ch; + + ch = &sc->sc_chan[saa->saa_index]; + if (IS_GCPAD(ch->ch_id)) { + return 1; + } + + return 0; } static void