From owner-p4-projects@FreeBSD.ORG Mon Jul 7 22:14:44 2008 Return-Path: Delivered-To: p4-projects@freebsd.org Received: by hub.freebsd.org (Postfix, from userid 32767) id 6FD071065673; Mon, 7 Jul 2008 22:14:44 +0000 (UTC) Delivered-To: perforce@FreeBSD.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 1A038106567D for ; Mon, 7 Jul 2008 22:14:44 +0000 (UTC) (envelope-from hselasky@FreeBSD.org) Received: from repoman.freebsd.org (repoman.freebsd.org [IPv6:2001:4f8:fff6::29]) by mx1.freebsd.org (Postfix) with ESMTP id 0737D8FC13 for ; Mon, 7 Jul 2008 22:14:44 +0000 (UTC) (envelope-from hselasky@FreeBSD.org) Received: from repoman.freebsd.org (localhost [127.0.0.1]) by repoman.freebsd.org (8.14.1/8.14.1) with ESMTP id m67MEi56018170 for ; Mon, 7 Jul 2008 22:14:44 GMT (envelope-from hselasky@FreeBSD.org) Received: (from perforce@localhost) by repoman.freebsd.org (8.14.2/8.14.1/Submit) id m67MEhnD018168 for perforce@freebsd.org; Mon, 7 Jul 2008 22:14:43 GMT (envelope-from hselasky@FreeBSD.org) Date: Mon, 7 Jul 2008 22:14:43 GMT Message-Id: <200807072214.m67MEhnD018168@repoman.freebsd.org> X-Authentication-Warning: repoman.freebsd.org: perforce set sender to hselasky@FreeBSD.org using -f From: Hans Petter Selasky To: Perforce Change Reviews Cc: Subject: PERFORCE change 144851 for review X-BeenThere: p4-projects@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: p4 projects tree changes List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 07 Jul 2008 22:14:44 -0000 http://perforce.freebsd.org/chv.cgi?CH=144851 Change 144851 by hselasky@hselasky_laptop001 on 2008/07/07 22:13:49 More patches to support LibUSB 1.0. Affected files ... .. //depot/projects/usb/src/sys/dev/usb2/core/usb2_dev.c#12 edit .. //depot/projects/usb/src/sys/dev/usb2/core/usb2_dev.h#7 edit .. //depot/projects/usb/src/sys/dev/usb2/core/usb2_generic.c#10 edit .. //depot/projects/usb/src/sys/dev/usb2/core/usb2_generic.h#3 edit .. //depot/projects/usb/src/sys/dev/usb2/include/usb2_ioctl.h#7 edit Differences ... ==== //depot/projects/usb/src/sys/dev/usb2/core/usb2_dev.c#12 (text+ko) ==== @@ -77,7 +77,6 @@ static void usb2_dev_init_post(void *arg); static void usb2_dev_uninit(void *arg); static int usb2_fifo_uiomove(struct usb2_fifo *f, void *cp, int n, struct uio *uio); -static void usb2_fifo_wakeup(struct usb2_fifo *f); static void usb2_fifo_check_methods(struct usb2_fifo_methods *pm); static void usb2_clone(void *arg, USB_UCRED char *name, int namelen, struct cdev **dev); static struct usb2_fifo *usb2_fifo_alloc(void); @@ -907,21 +906,77 @@ } /*------------------------------------------------------------------------* + * usb2_check_thread_perm + * + * Returns: + * 0: Has permission. + * Else: No permission. + *------------------------------------------------------------------------*/ +int +usb2_check_thread_perm(struct usb2_device *udev, struct thread *td, + int fflags, uint8_t iface_index, uint8_t ep_index) +{ + struct usb2_perm perm; + struct usb2_interface *iface; + int err; + + iface = usb2_get_iface(udev, iface_index); + if (iface == NULL) { + return (EINVAL); + } + if (iface->idesc == NULL) { + return (EINVAL); + } + /* set default value */ + bzero(&perm, sizeof(perm)); + + /* create a permissions mask */ + perm.uid = td->td_ucred->cr_ruid; + perm.gid = td->td_ucred->cr_rgid; + perm.mode = 0; + if (fflags & FREAD) + perm.mode |= 0444; + if (fflags & FWRITE) + perm.mode |= 0222; + perm.active = 1; + + mtx_lock(udev->default_mtx); + + /* scan down the permissions tree */ + if ((ep_index != 0) && iface && + usb2_match_perm(&perm, &iface->perm)) { + /* we got access through the interface */ + err = 0; + } else if (udev && usb2_match_perm(&perm, &udev->perm)) { + /* we got access through the device */ + err = 0; + } else if (udev->bus && usb2_match_perm(&perm, &(udev->bus->perm))) { + /* we got access through the USB bus */ + err = 0; + } else if (usb2_match_perm(&perm, &usb2_perm)) { + /* we got general access */ + err = 0; + } else { + /* no access */ + err = EPERM; + } + mtx_unlock(udev->default_mtx); + return (err); +} + +/*------------------------------------------------------------------------* * usb2_fdopen - cdev callback *------------------------------------------------------------------------*/ static int usb2_fdopen(struct cdev *dev, int xxx_oflags, struct thread *td, struct file *fp) { struct usb2_location loc; - struct usb2_perm perm; uint32_t devloc; int err; int fflags; DPRINTF(1, "oflags=0x%08x\n", xxx_oflags); - bzero(&perm, sizeof(perm)); - devloc = usb2_last_devloc; usb2_last_devloc = (0 - 1); /* reset "usb2_devloc" */ @@ -960,39 +1015,9 @@ DPRINTF(1, "cannot ref device\n"); return (ENXIO); } - /* create a permissions mask */ - perm.uid = td->td_ucred->cr_ruid; - perm.gid = td->td_ucred->cr_rgid; - perm.mode = 0; - if (fflags & FREAD) - perm.mode |= 0444; - if (fflags & FWRITE) - perm.mode |= 0222; - perm.active = 1; - - mtx_lock(loc.udev->default_mtx); + err = usb2_check_thread_perm(loc.udev, td, fflags, + loc.iface_index, loc.ep_index); - /* scan down the permissions tree */ - if ((loc.ep_index != 0) && loc.iface && - usb2_match_perm(&perm, &loc.iface->perm)) { - /* we got access through the interface */ - err = 0; - } else if (loc.udev && usb2_match_perm(&perm, &loc.udev->perm)) { - /* we got access through the device */ - err = 0; - } else if (loc.bus && usb2_match_perm(&perm, &loc.bus->perm)) { - /* we got access through the USB bus */ - err = 0; - } else if (usb2_match_perm(&perm, &usb2_perm)) { - /* we got general access */ - err = 0; - } else { - /* no access */ - err = EPERM; - } - - mtx_unlock(loc.udev->default_mtx); - /* check for error */ if (err) { usb2_unref_device(&loc); @@ -1281,7 +1306,6 @@ return (ENXIO); } - /* ARGSUSED */ static int usb2_poll_f(struct file *fp, int events, struct ucred *cred, struct thread *td) @@ -1297,17 +1321,18 @@ return (POLLHUP); } fflags = fp->f_flag; - f = loc.txfifo; if ((events & (POLLOUT | POLLWRNORM)) && (fflags & FWRITE)) { + f = loc.txfifo; + mtx_lock(f->priv_mtx); /* check if any packets are available */ USB_IF_POLL(&(f->free_q), m); - if (f->flag_iserror || m) { + if (f->flag_iserror || f->flag_iscomplete || m) { revents |= events & (POLLOUT | POLLWRNORM); } else { f->flag_isselect = 1; @@ -1316,17 +1341,17 @@ mtx_unlock(f->priv_mtx); } - f = loc.rxfifo; - if ((events & (POLLIN | POLLRDNORM)) && (fflags & FREAD)) { + f = loc.rxfifo; + mtx_lock(f->priv_mtx); /* check if any packets are available */ USB_IF_POLL(&(f->used_q), m); - if (f->flag_iserror || m) { + if (f->flag_iserror || f->flag_iscomplete || m) { revents |= events & (POLLIN | POLLRDNORM); } else { f->flag_isselect = 1; @@ -1381,8 +1406,6 @@ err = EIO; goto done; } - /* XXX TODO: support IO-vectors */ - while (uio->uio_resid > 0) { USB_IF_DEQUEUE(&(f->used_q), m); @@ -1505,8 +1528,6 @@ err = EIO; goto done; } - /* XXX TODO: support IO-vectors */ - while (uio->uio_resid > 0) { USB_IF_DEQUEUE(&(f->free_q), m); @@ -1613,7 +1634,7 @@ return; } -static void +void usb2_fifo_wakeup(struct usb2_fifo *f) { usb2_fifo_signal(f); ==== //depot/projects/usb/src/sys/dev/usb2/core/usb2_dev.h#7 (text+ko) ==== @@ -74,8 +74,10 @@ struct usb2_fifo_methods *methods; struct cdev *symlink[2]; /* our symlinks */ struct proc *async_p; /* process that wants SIGIO */ + struct usb2_fs_endpoint *fs_ep_ptr; struct usb2_device *udev; struct usb2_xfer *xfer[2]; + struct usb2_xfer **fs_xfer; struct mtx *priv_mtx; /* client data */ struct file *curr_file; /* set if FIFO is opened by a FILE */ void *priv_sc0; /* client data */ @@ -87,6 +89,7 @@ uint16_t dev_ep_index; /* our device endpoint index */ uint8_t flag_no_uref; /* set if FIFO is not control endpoint */ uint8_t flag_sleeping; /* set if FIFO is sleeping */ + uint8_t flag_iscomplete; /* set if a USB transfer is complete */ uint8_t flag_iserror; /* set if FIFO error happened */ uint8_t flag_isselect; /* set if FIFO is selected */ uint8_t flag_flushing; /* set if FIFO is flushing data */ @@ -96,6 +99,7 @@ uint8_t iface_index; /* set to the interface we belong to */ uint8_t fifo_index; /* set to the FIFO index in "struct * usb2_device" */ + uint8_t fs_ep_max; uint8_t fifo_zlp; /* zero length packet count */ uint8_t refcount; #define USB_FIFO_REF_MAX 0xFF @@ -121,5 +125,7 @@ uint8_t usb2_fifo_opened(struct usb2_fifo *fifo); void usb2_fifo_free(struct usb2_fifo *f); void usb2_fifo_reset(struct usb2_fifo *f); +int usb2_check_thread_perm(struct usb2_device *udev, struct thread *td, int fflags, uint8_t iface_index, uint8_t ep_index); +void usb2_fifo_wakeup(struct usb2_fifo *f); #endif /* _USB2_DEV_H_ */ ==== //depot/projects/usb/src/sys/dev/usb2/core/usb2_generic.c#10 (text+ko) ==== @@ -43,6 +43,7 @@ #include #include #include +#include #include #include @@ -61,6 +62,7 @@ static usb2_callback_t ugen_default_write_callback; static usb2_callback_t ugen_isoc_read_callback; static usb2_callback_t ugen_isoc_write_callback; +static usb2_callback_t ugen_default_fs_callback; static usb2_fifo_open_t ugen_open; static usb2_fifo_close_t ugen_close; @@ -78,8 +80,11 @@ static int ugen_get_sdesc(struct usb2_fifo *f, struct usb2_gen_descriptor *ugd); static int usb2_gen_fill_deviceinfo(struct usb2_fifo *f, struct usb2_device_info *di); static int ugen_re_enumerate(struct usb2_fifo *f); -static int ugen_iface_ioctl(struct usb2_fifo *f, u_long cmd, void *addr); +static int ugen_iface_ioctl(struct usb2_fifo *f, u_long cmd, void *addr, int fflags); static int ugen_ctrl_ioctl(struct usb2_fifo *f, u_long cmd, void *addr, int fflags); +static int ugen_fs_uninit(struct usb2_fifo *f); +static uint8_t ugen_fs_get_complete(struct usb2_fifo *f, uint8_t *pindex); + /* structures */ @@ -184,6 +189,10 @@ usb2_transfer_unsetup(f->xfer, 2); usb2_fifo_free_buffer(f); + + if (ugen_fs_uninit(f)) { + /* ignore any errors - we are closing */ + } return; } @@ -200,6 +209,10 @@ /* transfers are already opened */ return (0); } + if (f->fs_xfer) { + /* should not happen */ + return (EINVAL); + } bzero(usb2_config, sizeof(usb2_config)); usb2_config[1].type = UE_CONTROL; @@ -230,8 +243,8 @@ if (ugen_transfer_setup(f, usb2_config, 2)) { return (EIO); } - /* first transfer clears stall */ - f->flag_stall = 1; + /* first transfer does not clear stall */ + f->flag_stall = 0; break; case UE_ISOCHRONOUS: @@ -269,6 +282,10 @@ /* transfers are already opened */ return (0); } + if (f->fs_xfer) { + /* should not happen */ + return (EINVAL); + } bzero(usb2_config, sizeof(usb2_config)); usb2_config[1].type = UE_CONTROL; @@ -300,8 +317,8 @@ if (ugen_transfer_setup(f, usb2_config, 2)) { return (EIO); } - /* first transfer clears stall */ - f->flag_stall = 1; + /* first transfer does not clear stall */ + f->flag_stall = 0; break; case UE_ISOCHRONOUS: @@ -793,26 +810,25 @@ return (0); } -int -ugen_do_request(struct usb2_fifo *f, struct usb2_ctl_request *ur) +/*------------------------------------------------------------------------* + * ugen_check_request + * + * Return values: + * 0: Access allowed + * Else: No access + *------------------------------------------------------------------------*/ +static int +ugen_check_request(struct usb2_device_request *req) { - int error; - uint16_t len; - uint16_t actlen; - uint8_t isread; - void *data = NULL; - - if (f->flag_no_uref) { - /* control endpoint only */ - return (EINVAL); - } - /* avoid requests that would damage the bus integrity */ - if (((ur->ucr_request.bmRequestType == UT_WRITE_DEVICE) && - (ur->ucr_request.bRequest == UR_SET_ADDRESS)) || - ((ur->ucr_request.bmRequestType == UT_WRITE_DEVICE) && - (ur->ucr_request.bRequest == UR_SET_CONFIG)) || - ((ur->ucr_request.bmRequestType == UT_WRITE_INTERFACE) && - (ur->ucr_request.bRequest == UR_SET_INTERFACE))) { + /* + * Avoid requests that would damage the bus integrity: + */ + if (((req->bmRequestType == UT_WRITE_DEVICE) && + (req->bRequest == UR_SET_ADDRESS)) || + ((req->bmRequestType == UT_WRITE_DEVICE) && + (req->bRequest == UR_SET_CONFIG)) || + ((req->bmRequestType == UT_WRITE_INTERFACE) && + (req->bRequest == UR_SET_INTERFACE))) { if (suser(curthread)) { return (EPERM); } @@ -821,13 +837,32 @@ * Clearing the stall this way is not allowed, hence it does * not update the data toggle value in "struct usb2_pipe" ! */ - if (ur->ucr_request.bmRequestType == UT_WRITE_ENDPOINT) { + if (req->bmRequestType == UT_WRITE_ENDPOINT) { if (suser(curthread)) { return (EPERM); } } /* TODO: add more checks to verify the interface index */ + return (0); +} + +int +ugen_do_request(struct usb2_fifo *f, struct usb2_ctl_request *ur) +{ + int error; + uint16_t len; + uint16_t actlen; + uint8_t isread; + void *data = NULL; + + if (f->flag_no_uref) { + /* control endpoint only */ + return (EINVAL); + } + if (ugen_check_request(&ur->ucr_request)) { + return (EPERM); + } len = UGETW(ur->ucr_request.wLength); isread = (ur->ucr_request.bmRequestType & UT_READ) ? 1 : 0; @@ -901,7 +936,648 @@ } static int -ugen_iface_ioctl(struct usb2_fifo *f, u_long cmd, void *addr) +ugen_fs_uninit(struct usb2_fifo *f) +{ + if (f->fs_xfer == NULL) { + return (EINVAL); + } + usb2_transfer_unsetup(f->fs_xfer, f->fs_ep_max); + free(f->fs_xfer, M_USB); + f->fs_xfer = NULL; + f->fs_ep_max = 0; + f->fs_ep_ptr = NULL; + f->flag_iscomplete = 0; + usb2_fifo_free_buffer(f); + return (0); +} + +static uint8_t +ugen_fs_get_complete(struct usb2_fifo *f, uint8_t *pindex) +{ + struct usb2_mbuf *m; + + USB_IF_DEQUEUE(&(f->used_q), m); + + if (m) { + *pindex = *((uint8_t *)(m->cur_data_ptr)); + + USB_IF_ENQUEUE(&(f->free_q), m); + + return (0); /* success */ + } else { + f->flag_iscomplete = 0; + } + return (1); /* failure */ +} + +static void +ugen_fs_set_complete(struct usb2_fifo *f, uint8_t index) +{ + struct usb2_mbuf *m; + + USB_IF_DEQUEUE(&(f->free_q), m); + + USB_MBUF_RESET(m); + + *((uint8_t *)(m->cur_data_ptr)) = index; + + USB_IF_ENQUEUE(&(f->used_q), m); + + f->flag_iscomplete = 1; + + usb2_fifo_wakeup(f); + + return; +} + +static int +ugen_fs_copy_in(struct usb2_fifo *f, uint8_t ep_index) +{ + struct usb2_device_request *req; + struct usb2_xfer *xfer; + struct usb2_fs_endpoint fs_ep; + void *uaddr; + uint32_t offset; + uint32_t length; + uint32_t n; + uint32_t rem; + int error; + uint8_t isread; + + if (ep_index >= f->fs_ep_max) { + return (EINVAL); + } + xfer = f->fs_xfer[ep_index]; + if (xfer == NULL) { + return (EINVAL); + } + mtx_lock(f->priv_mtx); + if (usb2_transfer_pending(xfer)) { + mtx_unlock(f->priv_mtx); + return (EBUSY); /* should not happen */ + } + mtx_unlock(f->priv_mtx); + + /* security checks */ + + if (fs_ep.nFrames > xfer->max_frame_count) { + return (EINVAL); + } + if (fs_ep.nFrames == 0) { + return (EINVAL); + } + error = copyin(f->fs_ep_ptr + + ep_index, &fs_ep, sizeof(fs_ep)); + if (error) { + return (error); + } + error = copyin(fs_ep.ppBuffer, + &uaddr, sizeof(uaddr)); + if (error) { + return (error); + } + /* reset first frame */ + usb2_set_frame_offset(xfer, 0, 0); + + if (xfer->flags_int.control_xfr) { + + req = xfer->frbuffers[0].buffer; + + error = copyin(fs_ep.pLength, + &length, sizeof(length)); + if (error) { + return (error); + } + if (length >= sizeof(*req)) { + return (EINVAL); + } + if (length) { + error = copyin(uaddr, req, length); + if (error) { + return (error); + } + } + if (ugen_check_request(req)) { + return (EPERM); + } + xfer->frlengths[0] = length; + + /* Host mode only ! */ + if ((req->bmRequestType & + (UT_READ | UT_WRITE)) == UT_READ) { + isread = 1; + } else { + isread = 0; + } + n = 1; + offset = sizeof(*req); + + } else { + /* Device and Host mode */ + if (USB_GET_DATA_ISREAD(xfer)) { + isread = 1; + } else { + isread = 0; + } + n = 0; + offset = 0; + } + + rem = xfer->max_data_length; + xfer->nframes = fs_ep.nFrames; + xfer->timeout = fs_ep.timeout; + if (xfer->timeout > 65535) { + xfer->timeout = 65535; + } + if (fs_ep.flags & USB2_FS_FLAG_SINGLE_SHORT_OK) + xfer->flags.short_xfer_ok = 1; + else + xfer->flags.short_xfer_ok = 0; + + if (fs_ep.flags & USB2_FS_FLAG_MULTI_SHORT_OK) + xfer->flags.short_frames_ok = 1; + else + xfer->flags.short_frames_ok = 0; + + if (fs_ep.flags & USB2_FS_FLAG_FORCE_SHORT) + xfer->flags.force_short_xfer = 1; + else + xfer->flags.force_short_xfer = 0; + + if (fs_ep.flags & USB2_FS_FLAG_CLEAR_STALL) + xfer->flags.stall_pipe = 1; + else + xfer->flags.stall_pipe = 0; + + for (; n != xfer->nframes; n++) { + + error = copyin(fs_ep.pLength + n, + &length, sizeof(length)); + if (error) { + return (error); + } + xfer->frlengths[n] = length; + + if (length > rem) { + return (EINVAL); + } + rem -= length; + + if (!isread) { + + if (xfer->flags_int.isochronous_xfr) { + + /* move data */ + error = copyin(USB_ADD_BYTES(uaddr, offset), + USB_ADD_BYTES(xfer->frbuffers[0].buffer, + offset), length); + if (error) { + return (error); + } + } else { + /* we need to know the source buffer */ + error = copyin(fs_ep.ppBuffer + n, + &uaddr, sizeof(uaddr)); + if (error) { + return (error); + } + /* set current frame offset */ + usb2_set_frame_offset(xfer, offset, n); + + /* move data */ + error = copyin(uaddr, xfer->frbuffers[n].buffer, + length); + if (error) { + return (error); + } + } + } + offset += length; + } + return (error); +} + +static int +ugen_fs_copy_out(struct usb2_fifo *f, uint8_t ep_index) +{ + struct usb2_device_request *req; + struct usb2_xfer *xfer; + struct usb2_fs_endpoint fs_ep; + void *uaddr; + uint32_t offset; + uint32_t length; + uint32_t temp; + uint32_t n; + uint32_t rem; + int error; + + if (ep_index >= f->fs_ep_max) { + return (EINVAL); + } + xfer = f->fs_xfer[ep_index]; + if (xfer == NULL) { + return (EINVAL); + } + mtx_lock(f->priv_mtx); + if (usb2_transfer_pending(xfer)) { + mtx_unlock(f->priv_mtx); + return (EBUSY); /* should not happen */ + } + mtx_unlock(f->priv_mtx); + + error = copyin(f->fs_ep_ptr + + ep_index, &fs_ep, sizeof(fs_ep)); + if (error) { + return (error); + } + fs_ep.status = xfer->error; + fs_ep.aFrames = xfer->aframes; + if (xfer->error) { + goto complete; + } + error = copyin(fs_ep.ppBuffer, + &uaddr, sizeof(uaddr)); + if (error) { + return (error); + } + if (xfer->flags_int.control_xfr) { + req = xfer->frbuffers[0].buffer; + + /* Host mode only ! */ + if ((req->bmRequestType & (UT_READ | UT_WRITE)) == UT_WRITE) { + goto complete; + } + n = 1; + } else { + /* Device and Host mode */ + if (!USB_GET_DATA_ISREAD(xfer)) { + goto complete; + } + n = 0; + } + + rem = xfer->max_data_length; + + for (; n != xfer->nframes; n++) { + + if (xfer->flags_int.isochronous_xfr) { + + /* we need to know the initial length */ + error = copyin(fs_ep.pLength + n, + &length, sizeof(length)); + if (error) { + return (error); + } + /* range check */ + if (length > rem) { + return (EINVAL); + } + rem -= length; + temp = offset + length; + + /* limit */ + if (length > xfer->frlengths[n]) { + length = xfer->frlengths[n]; + } + /* move data */ + error = copyout(USB_ADD_BYTES(xfer->frbuffers[0].buffer, + offset), USB_ADD_BYTES(uaddr, offset), length); + if (error) { + return (error); + } + offset = temp; + } else { + + length = xfer->frlengths[n]; + + /* we need to know the destination buffer */ + error = copyin(fs_ep.ppBuffer + n, + &uaddr, sizeof(uaddr)); + if (error) { + return (error); + } + /* move data */ + error = copyout(xfer->frbuffers[n].buffer, + uaddr, length); + if (error) { + return (error); + } + } + + /* update length */ + error = copyout(&length, + fs_ep.pLength + n, sizeof(length)); + if (error) { + return (error); + } + } + +complete: + /* update "aFrames" */ + error = copyout(&fs_ep.aFrames, &(f->fs_ep_ptr + + ep_index)->aFrames, sizeof(fs_ep.aFrames)); + + if (error) { + return (error); + } + /* update "status" */ + error = copyout(&fs_ep.status, &(f->fs_ep_ptr + + ep_index)->status, sizeof(fs_ep.status)); + return (error); +} + +static uint8_t +ugen_fifo_in_use(struct usb2_fifo *f, int fflags) +{ + struct usb2_fifo *f_rx; + struct usb2_fifo *f_tx; + + f_rx = f->udev->fifo[(f->fifo_index & ~1) + USB_FIFO_RX]; + f_tx = f->udev->fifo[(f->fifo_index & ~1) + USB_FIFO_TX]; + + if ((fflags & FREAD) && f_rx && + (f_rx->xfer[0] || f_rx->xfer[1])) { + return (1); /* RX FIFO in use */ + } + if ((fflags & FWRITE) && f_tx && + (f_tx->xfer[0] || f_tx->xfer[1])) { + return (1); /* TX FIFO in use */ + } + return (0); /* not in use */ +} + +static int +ugen_fs_ioctl(struct usb2_fifo *f, u_long cmd, void *addr, int fflags) +{ + int error = 0; + + switch (cmd) { + case USB_FS_COMPLETE:{ + struct usb2_fs_complete *pd = addr; + uint8_t ep_index; + + mtx_lock(f->priv_mtx); + error = ugen_fs_get_complete(f, &ep_index); + mtx_unlock(f->priv_mtx); + + if (error) { + error = EBUSY; + break; + } + pd->ep_index = ep_index; + error = ugen_fs_copy_out(f, pd->ep_index); + break; + } + + case USB_FS_START:{ + struct usb2_fs_start *pd = addr; + + error = ugen_fs_copy_in(f, pd->ep_index); + if (error) { + break; + } + mtx_lock(f->priv_mtx); + usb2_transfer_start(f->fs_xfer[pd->ep_index]); + mtx_lock(f->priv_mtx); + break; + } + case USB_FS_STOP:{ + struct usb2_fs_stop *pd = addr; + + if (pd->ep_index >= f->fs_ep_max) { + error = EINVAL; + break; + } + mtx_lock(f->priv_mtx); + usb2_transfer_stop(f->fs_xfer[pd->ep_index]); + mtx_unlock(f->priv_mtx); + break; + } + case USB_FS_INIT:{ + struct usb2_fs_init *pd = addr; + + /* verify input parameters */ + if (pd->pEndpoints == NULL) { + error = EINVAL; + break; + } + if (pd->ep_index_max > 127) { + error = EINVAL; + break; + } + if (pd->ep_index_max == 0) { + error = EINVAL; + break; + } + if (f->fs_xfer != NULL) { + error = EBUSY; + break; + } + if (f->dev_ep_index != 0) { + error = EINVAL; + break; + } + if (ugen_fifo_in_use(f, fflags)) { + error = EBUSY; + break; + } + error = usb2_fifo_alloc_buffer(f, 1, pd->ep_index_max); + if (error) { + break; + } + f->fs_xfer = malloc(sizeof(f->fs_xfer[0]) * + pd->ep_index_max, M_USB, M_WAITOK | M_ZERO); + if (f->fs_xfer == NULL) { + usb2_fifo_free_buffer(f); + error = ENOMEM; + break; + } + f->fs_ep_max = pd->ep_index_max; + f->fs_ep_ptr = pd->pEndpoints; + f->flag_no_uref = 1; /* drop locks we don't need */ + break; + } + + case USB_FS_UNINIT:{ + struct usb2_fs_uninit *pd = addr; + + if (pd->dummy != 0) { + error = EINVAL; + break; + } + error = ugen_fs_uninit(f); + if (error == 0) { + f->flag_no_uref = 0; /* restore operation */ + } + break; + } + + case USB_FS_OPEN:{ + struct usb2_config usb2_config[1]; + struct usb2_fs_open *pd = addr; + struct usb2_pipe *pipe; + struct usb2_endpoint_descriptor *ed; + uint8_t iface_index; + uint8_t isread; + + if (pd->ep_index >= f->fs_ep_max) { + error = EINVAL; + break; + } + if (f->fs_xfer[pd->ep_index] != NULL) { + error = EBUSY; + break; + } + if (pd->max_bufsize > USB_FS_MAX_BUFSIZE) { + error = EINVAL; + break; + } + if (pd->max_frames > USB_FS_MAX_FRAMES) { + error = EINVAL; + break; + } + if (pd->max_frames == 0) { + error = EINVAL; + break; + } + pipe = usb2_get_pipe_by_addr(f->udev, pd->ep_no); + if (pipe == NULL) { + error = EINVAL; + break; + } + ed = pipe->edesc; + if (ed == NULL) { + error = ENXIO; + break; + } + iface_index = pipe->iface_index; + + error = usb2_check_thread_perm(f->udev, curthread, fflags, + iface_index, pd->ep_no); + if (error) { + break; + } + bzero(usb2_config, sizeof(usb2_config)); + + usb2_config[0].type = ed->bmAttributes & UE_XFERTYPE; + usb2_config[0].endpoint = ed->bEndpointAddress & UE_ADDR; + usb2_config[0].direction = ed->bEndpointAddress & (UE_DIR_OUT | UE_DIR_IN); + usb2_config[0].mh.interval = USB_DEFAULT_INTERVAL; + usb2_config[0].mh.flags.proxy_buffer = 1; + usb2_config[0].mh.callback = &ugen_default_fs_callback; + usb2_config[0].mh.timeout = 0; /* no timeout */ + usb2_config[0].mh.frames = pd->max_frames; + usb2_config[0].mh.bufsize = pd->max_bufsize; + usb2_config[0].md = usb2_config[0].mh; /* symmetric config */ + + if (usb2_config[0].type == UE_CONTROL) { + if (f->udev->flags.usb2_mode != USB_MODE_HOST) { + error = EINVAL; + break; + } + } else { + + isread = ((usb2_config[0].endpoint & + (UE_DIR_IN | UE_DIR_OUT)) == UE_DIR_IN); + + if (f->udev->flags.usb2_mode != USB_MODE_HOST) { + isread = !isread; + } + /* check permissions */ + if (isread) { + if (!(fflags & FREAD)) { + error = EPERM; + break; + } + } else { + if (!(fflags & FWRITE)) { + error = EPERM; + break; + } + } + } + error = usb2_transfer_setup(f->udev, &iface_index, + f->fs_xfer + pd->ep_index, usb2_config, 1, + f, f->priv_mtx); + if (error == 0) { + /* update maximum buffer size */ + pd->max_bufsize = >>> TRUNCATED FOR MAIL (1000 lines) <<<