aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHans Petter Selasky <hselasky@FreeBSD.org>2023-04-04 15:15:38 +0000
committerHans Petter Selasky <hselasky@FreeBSD.org>2023-04-08 14:52:20 +0000
commit03a2e432d5cc2eedb9304faea2b19051c84caecf (patch)
treedafb6eecfa70feb6d89803ed596403485ad117bc
parentee8f5c2a31ec6f5913f404daeade6e1ac30f4336 (diff)
downloadsrc-03a2e432d5cc2eedb9304faea2b19051c84caecf.tar.gz
src-03a2e432d5cc2eedb9304faea2b19051c84caecf.zip
usb(4): Code refactoring as a pre-step for adding missing synchronization mechanism.
Move code in switch cases into own functions to make later changes easier to track. No functional change, except for removing a superfluous break statement when range checking USB_FS_MAX_FRAMES, in the USB_FS_OPEN case. It should not have been there at all. Suggested by: emaste@ MFC after: 1 week Sponsored by: NVIDIA Networking
-rw-r--r--sys/dev/usb/usb_generic.c453
1 files changed, 226 insertions, 227 deletions
diff --git a/sys/dev/usb/usb_generic.c b/sys/dev/usb/usb_generic.c
index 7e82ec0d18d6..87a1e792c609 100644
--- a/sys/dev/usb/usb_generic.c
+++ b/sys/dev/usb/usb_generic.c
@@ -120,6 +120,7 @@ static int ugen_iface_ioctl(struct usb_fifo *, u_long, void *, int);
static uint8_t ugen_fs_get_complete(struct usb_fifo *, uint8_t *);
static int ugen_fs_uninit(struct usb_fifo *f);
static int ugen_fs_copyin(struct usb_fifo *, uint8_t, struct usb_fs_endpoint*);
+static uint8_t ugen_fifo_in_use(struct usb_fifo *, int fflags);
/* structures */
@@ -959,6 +960,37 @@ ugen_re_enumerate(struct usb_fifo *f)
return (0);
}
+static int
+ugen_fs_init(struct usb_fifo *f,
+ struct usb_fs_endpoint *fs_ep_ptr, usb_size_t fs_ep_sz,
+ int fflags, uint8_t ep_index_max)
+{
+ int error;
+
+ /* verify input parameters */
+ if (fs_ep_ptr == NULL || ep_index_max > 127)
+ return (EINVAL);
+
+ if (f->fs_xfer != NULL)
+ return (EBUSY);
+
+ if (f->dev_ep_index != 0 || ep_index_max == 0)
+ return (EINVAL);
+
+ if (ugen_fifo_in_use(f, fflags))
+ return (EBUSY);
+
+ error = usb_fifo_alloc_buffer(f, 1, ep_index_max);
+ if (error == 0) {
+ f->fs_xfer = malloc(sizeof(f->fs_xfer[0]) *
+ ep_index_max, M_USB, M_WAITOK | M_ZERO);
+ f->fs_ep_max = ep_index_max;
+ f->fs_ep_ptr = fs_ep_ptr;
+ f->fs_ep_sz = fs_ep_sz;
+ }
+ return (error);
+}
+
int
ugen_fs_uninit(struct usb_fifo *f)
{
@@ -975,6 +1007,157 @@ ugen_fs_uninit(struct usb_fifo *f)
return (0);
}
+static int
+usb_fs_open(struct usb_fifo *f, struct usb_fs_open *popen,
+ int fflags, usb_stream_t stream_id)
+{
+ struct usb_config usb_config[1] = {};
+ struct usb_endpoint *ep;
+ struct usb_endpoint_descriptor *ed;
+ uint8_t iface_index;
+ uint8_t pre_scale;
+ uint8_t isread;
+ int error;
+
+ if (popen->ep_index >= f->fs_ep_max)
+ return (EINVAL);
+
+ if (f->fs_xfer[popen->ep_index] != NULL)
+ return (EBUSY);
+
+ if (popen->max_bufsize > USB_FS_MAX_BUFSIZE)
+ popen->max_bufsize = USB_FS_MAX_BUFSIZE;
+
+ if (popen->max_frames & USB_FS_MAX_FRAMES_PRE_SCALE) {
+ pre_scale = 1;
+ popen->max_frames &= ~USB_FS_MAX_FRAMES_PRE_SCALE;
+ } else {
+ pre_scale = 0;
+ }
+
+ if (popen->max_frames > USB_FS_MAX_FRAMES)
+ popen->max_frames = USB_FS_MAX_FRAMES;
+
+ if (popen->max_frames == 0)
+ return (EINVAL);
+
+ ep = usbd_get_ep_by_addr(f->udev, popen->ep_no);
+ if (ep == NULL)
+ return (EINVAL);
+
+ ed = ep->edesc;
+ if (ed == NULL)
+ return (ENXIO);
+
+ iface_index = ep->iface_index;
+
+ usb_config[0].type = ed->bmAttributes & UE_XFERTYPE;
+ usb_config[0].endpoint = ed->bEndpointAddress & UE_ADDR;
+ usb_config[0].direction = ed->bEndpointAddress & (UE_DIR_OUT | UE_DIR_IN);
+ usb_config[0].interval = USB_DEFAULT_INTERVAL;
+ usb_config[0].flags.proxy_buffer = 1;
+ if (pre_scale != 0)
+ usb_config[0].flags.pre_scale_frames = 1;
+
+ usb_config[0].callback = &ugen_ctrl_fs_callback;
+ usb_config[0].timeout = 0; /* no timeout */
+ usb_config[0].frames = popen->max_frames;
+ usb_config[0].bufsize = popen->max_bufsize;
+ usb_config[0].usb_mode = USB_MODE_DUAL; /* both modes */
+ usb_config[0].stream_id = stream_id;
+
+ if (usb_config[0].type == UE_CONTROL) {
+ if (f->udev->flags.usb_mode != USB_MODE_HOST)
+ return (EINVAL);
+ } else {
+ isread = ((usb_config[0].endpoint &
+ (UE_DIR_IN | UE_DIR_OUT)) == UE_DIR_IN);
+
+ if (f->udev->flags.usb_mode != USB_MODE_HOST)
+ isread = !isread;
+
+ /* check permissions */
+ if (isread) {
+ if (!(fflags & FREAD))
+ return (EPERM);
+ } else {
+ if (!(fflags & FWRITE))
+ return (EPERM);
+ }
+ }
+ error = usbd_transfer_setup(f->udev, &iface_index,
+ f->fs_xfer + popen->ep_index, usb_config, 1,
+ f, f->priv_mtx);
+ if (error == 0) {
+ /* update maximums */
+ popen->max_packet_length =
+ f->fs_xfer[popen->ep_index]->max_frame_size;
+ popen->max_bufsize =
+ f->fs_xfer[popen->ep_index]->max_data_length;
+ /* update number of frames */
+ popen->max_frames =
+ f->fs_xfer[popen->ep_index]->nframes;
+ /* store index of endpoint */
+ f->fs_xfer[popen->ep_index]->priv_fifo =
+ ((uint8_t *)0) + popen->ep_index;
+ } else {
+ error = ENOMEM;
+ }
+ return (error);
+}
+
+static int
+usb_fs_close(struct usb_fifo *f, struct usb_fs_close *pclose)
+{
+ if (pclose->ep_index >= f->fs_ep_max)
+ return (EINVAL);
+
+ usbd_transfer_unsetup(f->fs_xfer + pclose->ep_index, 1);
+ return (0);
+}
+
+static int
+usb_fs_clear_stall_sync(struct usb_fifo *f, struct usb_fs_clear_stall_sync *pstall)
+{
+ struct usb_device_request req;
+ struct usb_endpoint *ep;
+ int error;
+
+ if (pstall->ep_index >= f->fs_ep_max)
+ return (EINVAL);
+
+ if (f->fs_xfer[pstall->ep_index] == NULL)
+ return (EINVAL);
+
+ if (f->udev->flags.usb_mode != USB_MODE_HOST)
+ return (EINVAL);
+
+ mtx_lock(f->priv_mtx);
+ error = usbd_transfer_pending(f->fs_xfer[pstall->ep_index]);
+ mtx_unlock(f->priv_mtx);
+
+ if (error)
+ return (EBUSY);
+
+ ep = f->fs_xfer[pstall->ep_index]->endpoint;
+
+ /* setup a clear-stall packet */
+ req.bmRequestType = UT_WRITE_ENDPOINT;
+ req.bRequest = UR_CLEAR_FEATURE;
+ USETW(req.wValue, UF_ENDPOINT_HALT);
+ req.wIndex[0] = ep->edesc->bEndpointAddress;
+ req.wIndex[1] = 0;
+ USETW(req.wLength, 0);
+
+ error = usbd_do_request(f->udev, NULL, &req, NULL);
+ if (error == 0) {
+ usbd_clear_data_toggle(f->udev, ep);
+ } else {
+ error = ENXIO;
+ }
+ return (error);
+}
+
static uint8_t
ugen_fs_get_complete(struct usb_fifo *f, uint8_t *pindex)
{
@@ -1488,8 +1671,6 @@ ugen_fifo_in_use(struct usb_fifo *f, int fflags)
static int
ugen_ioctl(struct usb_fifo *f, u_long cmd, void *addr, int fflags)
{
- struct usb_config usb_config[1];
- struct usb_device_request req;
union {
struct usb_fs_complete *pcomp;
struct usb_fs_start *pstart;
@@ -1500,14 +1681,9 @@ ugen_ioctl(struct usb_fifo *f, u_long cmd, void *addr, int fflags)
struct usb_fs_clear_stall_sync *pstall;
void *addr;
} u;
- struct usb_endpoint *ep;
- struct usb_endpoint_descriptor *ed;
struct usb_xfer *xfer;
- int error = 0;
- uint8_t iface_index;
- uint8_t isread;
+ int error;
uint8_t ep_index;
- uint8_t pre_scale;
u.addr = addr;
@@ -1519,44 +1695,45 @@ ugen_ioctl(struct usb_fifo *f, u_long cmd, void *addr, int fflags)
error = ugen_fs_get_complete(f, &ep_index);
mtx_unlock(f->priv_mtx);
- if (error) {
+ if (error != 0) {
error = EBUSY;
- break;
+ } else {
+ u.pcomp->ep_index = ep_index;
+ error = ugen_fs_copy_out(f, u.pcomp->ep_index);
}
- u.pcomp->ep_index = ep_index;
- error = ugen_fs_copy_out(f, u.pcomp->ep_index);
break;
case USB_FS_START:
error = ugen_fs_copy_in(f, u.pstart->ep_index);
- if (error)
- break;
- mtx_lock(f->priv_mtx);
- xfer = f->fs_xfer[u.pstart->ep_index];
- usbd_transfer_start(xfer);
- mtx_unlock(f->priv_mtx);
+ if (error == 0) {
+ mtx_lock(f->priv_mtx);
+ xfer = f->fs_xfer[u.pstart->ep_index];
+ usbd_transfer_start(xfer);
+ mtx_unlock(f->priv_mtx);
+ }
break;
case USB_FS_STOP:
+ mtx_lock(f->priv_mtx);
if (u.pstop->ep_index >= f->fs_ep_max) {
error = EINVAL;
- break;
- }
- mtx_lock(f->priv_mtx);
- xfer = f->fs_xfer[u.pstart->ep_index];
- if (usbd_transfer_pending(xfer)) {
- usbd_transfer_stop(xfer);
-
- /*
- * Check if the USB transfer was stopped
- * before it was even started and fake a
- * cancel event.
- */
- if (!xfer->flags_int.transferring &&
- !xfer->flags_int.started) {
- DPRINTF("Issuing fake completion event\n");
- ugen_fs_set_complete(xfer->priv_sc,
- USB_P2U(xfer->priv_fifo));
+ } else {
+ error = 0;
+ xfer = f->fs_xfer[u.pstart->ep_index];
+ if (usbd_transfer_pending(xfer)) {
+ usbd_transfer_stop(xfer);
+
+ /*
+ * Check if the USB transfer was
+ * stopped before it was even started
+ * and fake a cancel event.
+ */
+ if (!xfer->flags_int.transferring &&
+ !xfer->flags_int.started) {
+ DPRINTF("Issuing fake completion event\n");
+ ugen_fs_set_complete(xfer->priv_sc,
+ USB_P2U(xfer->priv_fifo));
+ }
}
}
mtx_unlock(f->priv_mtx);
@@ -1564,153 +1741,16 @@ ugen_ioctl(struct usb_fifo *f, u_long cmd, void *addr, int fflags)
case USB_FS_OPEN:
case USB_FS_OPEN_STREAM:
- if (u.popen->ep_index >= f->fs_ep_max) {
- error = EINVAL;
- break;
- }
- if (f->fs_xfer[u.popen->ep_index] != NULL) {
- error = EBUSY;
- break;
- }
- if (u.popen->max_bufsize > USB_FS_MAX_BUFSIZE) {
- u.popen->max_bufsize = USB_FS_MAX_BUFSIZE;
- }
- if (u.popen->max_frames & USB_FS_MAX_FRAMES_PRE_SCALE) {
- pre_scale = 1;
- u.popen->max_frames &= ~USB_FS_MAX_FRAMES_PRE_SCALE;
- } else {
- pre_scale = 0;
- }
- if (u.popen->max_frames > USB_FS_MAX_FRAMES) {
- u.popen->max_frames = USB_FS_MAX_FRAMES;
- break;
- }
- if (u.popen->max_frames == 0) {
- error = EINVAL;
- break;
- }
- ep = usbd_get_ep_by_addr(f->udev, u.popen->ep_no);
- if (ep == NULL) {
- error = EINVAL;
- break;
- }
- ed = ep->edesc;
- if (ed == NULL) {
- error = ENXIO;
- break;
- }
- iface_index = ep->iface_index;
-
- memset(usb_config, 0, sizeof(usb_config));
-
- usb_config[0].type = ed->bmAttributes & UE_XFERTYPE;
- usb_config[0].endpoint = ed->bEndpointAddress & UE_ADDR;
- usb_config[0].direction = ed->bEndpointAddress & (UE_DIR_OUT | UE_DIR_IN);
- usb_config[0].interval = USB_DEFAULT_INTERVAL;
- usb_config[0].flags.proxy_buffer = 1;
- if (pre_scale != 0)
- usb_config[0].flags.pre_scale_frames = 1;
- usb_config[0].callback = &ugen_ctrl_fs_callback;
- usb_config[0].timeout = 0; /* no timeout */
- usb_config[0].frames = u.popen->max_frames;
- usb_config[0].bufsize = u.popen->max_bufsize;
- usb_config[0].usb_mode = USB_MODE_DUAL; /* both modes */
- if (cmd == USB_FS_OPEN_STREAM)
- usb_config[0].stream_id = u.popen_stream->stream_id;
-
- if (usb_config[0].type == UE_CONTROL) {
- if (f->udev->flags.usb_mode != USB_MODE_HOST) {
- error = EINVAL;
- break;
- }
- } else {
- isread = ((usb_config[0].endpoint &
- (UE_DIR_IN | UE_DIR_OUT)) == UE_DIR_IN);
-
- if (f->udev->flags.usb_mode != USB_MODE_HOST) {
- isread = !isread;
- }
- /* check permissions */
- if (isread) {
- if (!(fflags & FREAD)) {
- error = EPERM;
- break;
- }
- } else {
- if (!(fflags & FWRITE)) {
- error = EPERM;
- break;
- }
- }
- }
- error = usbd_transfer_setup(f->udev, &iface_index,
- f->fs_xfer + u.popen->ep_index, usb_config, 1,
- f, f->priv_mtx);
- if (error == 0) {
- /* update maximums */
- u.popen->max_packet_length =
- f->fs_xfer[u.popen->ep_index]->max_frame_size;
- u.popen->max_bufsize =
- f->fs_xfer[u.popen->ep_index]->max_data_length;
- /* update number of frames */
- u.popen->max_frames =
- f->fs_xfer[u.popen->ep_index]->nframes;
- /* store index of endpoint */
- f->fs_xfer[u.popen->ep_index]->priv_fifo =
- ((uint8_t *)0) + u.popen->ep_index;
- } else {
- error = ENOMEM;
- }
+ error = usb_fs_open(f, u.popen, fflags,
+ (cmd == USB_FS_OPEN_STREAM) ? u.popen_stream->stream_id : 0);
break;
case USB_FS_CLOSE:
- if (u.pclose->ep_index >= f->fs_ep_max) {
- error = EINVAL;
- break;
- }
- if (f->fs_xfer[u.pclose->ep_index] == NULL) {
- error = EINVAL;
- break;
- }
- usbd_transfer_unsetup(f->fs_xfer + u.pclose->ep_index, 1);
+ error = usb_fs_close(f, u.pclose);
break;
case USB_FS_CLEAR_STALL_SYNC:
- if (u.pstall->ep_index >= f->fs_ep_max) {
- error = EINVAL;
- break;
- }
- if (f->fs_xfer[u.pstall->ep_index] == NULL) {
- error = EINVAL;
- break;
- }
- if (f->udev->flags.usb_mode != USB_MODE_HOST) {
- error = EINVAL;
- break;
- }
- mtx_lock(f->priv_mtx);
- error = usbd_transfer_pending(f->fs_xfer[u.pstall->ep_index]);
- mtx_unlock(f->priv_mtx);
-
- if (error) {
- return (EBUSY);
- }
- ep = f->fs_xfer[u.pstall->ep_index]->endpoint;
-
- /* setup a clear-stall packet */
- req.bmRequestType = UT_WRITE_ENDPOINT;
- req.bRequest = UR_CLEAR_FEATURE;
- USETW(req.wValue, UF_ENDPOINT_HALT);
- req.wIndex[0] = ep->edesc->bEndpointAddress;
- req.wIndex[1] = 0;
- USETW(req.wLength, 0);
-
- error = usbd_do_request(f->udev, NULL, &req, NULL);
- if (error == 0) {
- usbd_clear_data_toggle(f->udev, ep);
- } else {
- error = ENXIO;
- }
+ error = usb_fs_clear_stall_sync(f, u.pstall);
break;
default:
@@ -2161,9 +2201,6 @@ ugen_iface_ioctl(struct usb_fifo *f, u_long cmd, void *addr, int fflags)
static int
ugen_ioctl_post(struct usb_fifo *f, u_long cmd, void *addr, int fflags)
{
-#ifdef COMPAT_FREEBSD32
- struct usb_fs_init local_pinit;
-#endif
union {
struct usb_interface_descriptor *idesc;
struct usb_alt_interface *ai;
@@ -2183,7 +2220,6 @@ ugen_ioctl_post(struct usb_fifo *f, u_long cmd, void *addr, int fflags)
struct usb_device_descriptor *dtemp;
struct usb_config_descriptor *ctemp;
struct usb_interface *iface;
- size_t usb_fs_endpoint_sz = sizeof(struct usb_fs_endpoint);
int error = 0;
uint8_t n;
@@ -2191,18 +2227,6 @@ ugen_ioctl_post(struct usb_fifo *f, u_long cmd, void *addr, int fflags)
DPRINTFN(6, "cmd=0x%08lx\n", cmd);
-#ifdef COMPAT_FREEBSD32
- switch (cmd) {
- case USB_FS_INIT32:
- PTRIN_CP(*u.pinit32, local_pinit, pEndpoints);
- CP(*u.pinit32, local_pinit, ep_index_max);
- u.addr = &local_pinit;
- cmd = _IOC_NEWTYPE(USB_FS_INIT, struct usb_fs_init);
- usb_fs_endpoint_sz = sizeof(struct usb_fs_endpoint32);
- break;
- }
-#endif
-
switch (cmd) {
case USB_DISCOVER:
usb_needs_explore_all();
@@ -2397,42 +2421,17 @@ ugen_ioctl_post(struct usb_fifo *f, u_long cmd, void *addr, int fflags)
break;
case USB_FS_INIT:
- /* verify input parameters */
- if (u.pinit->pEndpoints == NULL) {
- error = EINVAL;
- break;
- }
- if (u.pinit->ep_index_max > 127) {
- error = EINVAL;
- break;
- }
- if (u.pinit->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 = usb_fifo_alloc_buffer(f, 1, u.pinit->ep_index_max);
- if (error) {
- break;
- }
- f->fs_xfer = malloc(sizeof(f->fs_xfer[0]) *
- u.pinit->ep_index_max, M_USB, M_WAITOK | M_ZERO);
- f->fs_ep_max = u.pinit->ep_index_max;
- f->fs_ep_ptr = u.pinit->pEndpoints;
- f->fs_ep_sz = usb_fs_endpoint_sz;
+ error = ugen_fs_init(f, u.pinit->pEndpoints,
+ sizeof(struct usb_fs_endpoint), fflags,
+ u.pinit->ep_index_max);
break;
-
+#ifdef COMPAT_FREEBSD32
+ case USB_FS_INIT32:
+ error = ugen_fs_init(f, PTRIN(u.pinit32->pEndpoints),
+ sizeof(struct usb_fs_endpoint32), fflags,
+ u.pinit32->ep_index_max);
+ break;
+#endif
case USB_FS_UNINIT:
if (u.puninit->dummy != 0) {
error = EINVAL;