int uhid_do_ioctl(struct uhid_softc *sc, u_long cmd, caddr_t addr, int flag, usb_proc_ptr p) { struct usb_ctl_report_desc *rd; struct usb_ctl_report *re; int size, id; usbd_status err; DPRINTFN(2, ("uhidioctl: cmd=%lx\n", cmd)); if (sc->sc_dying) return (EIO); switch (cmd) { case FIONBIO: /* All handled in the upper FS layer. */ break; case FIOASYNC: if (*(int *)addr) { if (sc->sc_async != NULL) return (EBUSY); sc->sc_async = p->td_proc; /* XXXKSE */ DPRINTF(("uhid_do_ioctl: FIOASYNC %p\n", p->td_proc)); } else sc->sc_async = NULL; break; /* XXX this is not the most general solution. */ case TIOCSPGRP: if (sc->sc_async == NULL) return (EINVAL); if (*(int *)addr != sc->sc_async->p_pgid) return (EPERM); break; case USB_GET_REPORT_DESC: rd = (struct usb_ctl_report_desc *)addr; size = min(sc->sc_repdesc_size, sizeof rd->ucrd_data); rd->ucrd_size = size; memcpy(rd->ucrd_data, sc->sc_repdesc, size); break; case USB_SET_IMMED: if (*(int *)addr) { /* XXX should read into ibuf, but does it matter? */ err = usbd_get_report(sc->sc_iface, UHID_INPUT_REPORT, sc->sc_iid, sc->sc_ibuf, sc->sc_isize); if (err) return (EOPNOTSUPP); sc->sc_state |= UHID_IMMED; } else sc->sc_state &= ~UHID_IMMED; break; case USB_GET_REPORT: re = (struct usb_ctl_report *)addr; switch (re->ucr_report) { case UHID_INPUT_REPORT: size = sc->sc_isize; id = sc->sc_iid; break; case UHID_OUTPUT_REPORT: size = sc->sc_osize; id = sc->sc_oid; break; case UHID_FEATURE_REPORT: size = sc->sc_fsize; id = sc->sc_fid; break; default: return (EINVAL); } err = usbd_get_report(sc->sc_iface, re->ucr_report, id, re->ucr_data, size); if (err) return (EIO); break; case USB_SET_REPORT: re = (struct usb_ctl_report *)addr; switch (re->ucr_report) { case UHID_INPUT_REPORT: size = sc->sc_isize; id = sc->sc_iid; break; case UHID_OUTPUT_REPORT: size = sc->sc_osize; id = sc->sc_oid; break; case UHID_FEATURE_REPORT: size = sc->sc_fsize; id = sc->sc_fid; break; default: return (EINVAL); } err = usbd_set_report(sc->sc_iface, re->ucr_report, id, re->ucr_data, size); if (err) return (EIO); break; default: return (EINVAL); } return (0); }
usbd_status uhidev_get_report(struct uhidev *scd, int type, void *data, int len) { return usbd_get_report(scd->sc_parent->sc_iface, type, scd->sc_report_id, data, len); }
int uhid_do_read(struct uhid_softc *sc, struct uio *uio, int flag) { int s; int error = 0; size_t length; u_char buffer[UHID_CHUNK]; usbd_status err; DPRINTFN(1, ("uhidread\n")); if (sc->sc_state & UHID_IMMED) { DPRINTFN(1, ("uhidread immed\n")); err = usbd_get_report(sc->sc_iface, UHID_INPUT_REPORT, sc->sc_iid, buffer, sc->sc_isize); if (err) return (EIO); return (uiomove(buffer, sc->sc_isize, uio)); } s = splusb(); while (sc->sc_q.c_cc == 0) { if (flag & IO_NDELAY) { splx(s); return (EWOULDBLOCK); } sc->sc_state |= UHID_ASLP; DPRINTFN(5, ("uhidread: sleep on %p\n", &sc->sc_q)); error = tsleep(&sc->sc_q, PZERO | PCATCH, "uhidrea", 0); DPRINTFN(5, ("uhidread: woke, error=%d\n", error)); if (sc->sc_dying) error = EIO; if (error) { sc->sc_state &= ~UHID_ASLP; break; } if (sc->sc_state & UHID_NEEDCLEAR) { DPRINTFN(-1,("uhidread: clearing stall\n")); sc->sc_state &= ~UHID_NEEDCLEAR; usbd_clear_endpoint_stall(sc->sc_intrpipe); } } splx(s); /* Transfer as many chunks as possible. */ while (sc->sc_q.c_cc > 0 && uio->uio_resid > 0 && !error) { length = min(sc->sc_q.c_cc, uio->uio_resid); if (length > sizeof(buffer)) length = sizeof(buffer); /* Remove a small chunk from the input queue. */ (void) q_to_b(&sc->sc_q, buffer, length); DPRINTFN(5, ("uhidread: got %lu chars\n", (u_long)length)); /* Copy the data to the user process. */ if ((error = uiomove(buffer, length, uio)) != 0) break; } return (error); }