#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: umcpmio_transport.c,v 1.1 2025/11/29 18:39:14 brad Exp $");
#ifdef _KERNEL_OPT
#include "opt_usb.h"
#endif
#include <sys/param.h>
#include <sys/types.h>
#include <dev/hid/hid.h>
#include <dev/usb/uhidev.h>
#include <dev/usb/usb.h>
#include <dev/usb/usbdevs.h>
#include <dev/usb/usbdi.h>
#include <dev/usb/usbdi_util.h>
#include <dev/usb/usbhid.h>
#include <dev/usb/umcpmio.h>
#include <dev/usb/umcpmio_hid_reports.h>
#include <dev/usb/umcpmio_transport.h>
#include <dev/usb/umcpmio_io.h>
#include <dev/usb/umcpmio_subr.h>
#define UMCPMIO_DEBUG 1
#ifdef UMCPMIO_DEBUG
#define DPRINTF(x) do { if (umcpmiodebug) printf x; } while (0)
#define DPRINTFN(n, x) do { if (umcpmiodebug > (n)) printf x; } while (0)
extern int umcpmiodebug;
#else
#define DPRINTF(x) __nothing
#define DPRINTFN(n, x) __nothing
#endif
static void
umcpmio_uhidev_intr(void *cookie, void *ibuf, u_int len)
{
struct umcpmio_softc *sc = cookie;
if (sc->sc_dying)
return;
DPRINTFN(30, ("umcpmio_uhidev_intr: len=%d\n", len));
mutex_enter(&sc->sc_res_mutex);
switch (len) {
case MCP2221_RES_BUFFER_SIZE:
if (sc->sc_res_buffer != NULL) {
memcpy(sc->sc_res_buffer, ibuf,
MCP2221_RES_BUFFER_SIZE);
sc->sc_res_ready = true;
cv_signal(&sc->sc_res_cv);
} else {
int d = umcpmiodebug;
device_printf(sc->sc_dev,
"umcpmio_uhidev_intr: NULL sc_res_buffer:"
" len=%d\n",
len);
umcpmiodebug = 20;
umcpmio_dump_buffer(true, (uint8_t *) ibuf, len,
"umcpmio_uhidev_intr: ibuf");
umcpmiodebug = d;
}
break;
default:
device_printf(sc->sc_dev,
"umcpmio_uhidev_intr: Unknown interrupt length: %d",
len);
break;
}
mutex_exit(&sc->sc_res_mutex);
}
int
umcpmio_send_report(struct umcpmio_softc *sc, uint8_t *sendbuf,
size_t sendlen, uint8_t *resbuf, int timeout)
{
int err = 0;
int err_count = 0;
if (sc->sc_dying)
return EIO;
KASSERT(mutex_owned(&sc->sc_action_mutex));
if (sc->sc_res_buffer != NULL) {
sc->sc_res_buffer = NULL;
sc->sc_res_ready = false;
err = EIO;
goto out;
}
sc->sc_res_buffer = resbuf;
sc->sc_res_ready = false;
err = uhidev_write(sc->sc_hdev, sendbuf, sendlen);
if (err) {
DPRINTF(("umcpmio_send_report: uhidev_write errored with:"
" err=%d\n", err));
goto out;
}
DPRINTFN(30, ("umcpmio_send_report: about to wait on cv. err=%d\n",
err));
mutex_enter(&sc->sc_res_mutex);
while (!sc->sc_res_ready) {
DPRINTFN(20, ("umcpmio_send_report: LOOP for response."
" sc_res_ready=%d, err_count=%d, timeout=%d\n",
sc->sc_res_ready, err_count, mstohz(timeout)));
err = cv_timedwait_sig(&sc->sc_res_cv, &sc->sc_res_mutex,
mstohz(timeout));
if (err) {
DPRINTF(("umcpmio_send_report:"
" cv_timedwait_sig reported an error:"
" err=%d, sc->sc_res_ready=%d\n",
err, sc->sc_res_ready));
err_count++;
}
if ((err == ERESTART) && (sc->sc_res_ready)) {
DPRINTF(("umcpmio_send_report:"
" ERESTART and buffer is ready\n"));
err = 0;
break;
}
if (err_count > sc->sc_response_errcnt) {
DPRINTF(("umcpmio_send_report: err_count exceeded:"
" err=%d\n", err));
if (err == ERESTART)
err = EIO;
break;
}
if (err == EWOULDBLOCK) {
DPRINTF(("umcpmio_send_report: EWOULDBLOCK:"
" err_count=%d\n", err_count));
continue;
}
if ((err == ERESTART) && (!sc->sc_res_ready)) {
DPRINTF(("umcpmio_send_report:"
" ERESTART and buffer is NOT ready."
" err_count=%d\n", err_count));
continue;
}
}
sc->sc_res_buffer = NULL;
sc->sc_res_ready = false;
mutex_exit(&sc->sc_res_mutex);
if (err &&
err != ERESTART)
err = EIO;
out:
return err;
}
int
umcpmio_hid_open(struct umcpmio_softc *sc)
{
int err;
err = uhidev_open(sc->sc_hdev, &umcpmio_uhidev_intr, sc);
if (err) {
aprint_error_dev(sc->sc_dev, "umcpmio_hid_open: "
" uhidev_open: err=%d\n", err);
}
if (!err)
delay(1000);
return err;
}