summaryrefslogtreecommitdiffstats
path: root/sys/dev/usb/usb_dev.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/usb/usb_dev.c')
-rw-r--r--sys/dev/usb/usb_dev.c114
1 files changed, 62 insertions, 52 deletions
diff --git a/sys/dev/usb/usb_dev.c b/sys/dev/usb/usb_dev.c
index 512b1f8..d3d98ff 100644
--- a/sys/dev/usb/usb_dev.c
+++ b/sys/dev/usb/usb_dev.c
@@ -72,7 +72,8 @@ SYSCTL_INT(_hw_usb2_dev, OID_AUTO, debug, CTLFLAG_RW,
/* prototypes */
-static int usb2_fifo_open(struct usb2_fifo *, int);
+static int usb2_fifo_open(struct usb2_cdev_privdata *,
+ struct usb2_fifo *, int);
static void usb2_fifo_close(struct usb2_fifo *, int);
static void usb2_dev_init(void *);
static void usb2_dev_init_post(void *);
@@ -160,7 +161,6 @@ usb2_ref_device(struct usb2_cdev_privdata* cpd, int need_uref)
{
struct usb2_fifo **ppf;
struct usb2_fifo *f;
- int dev_ep_index;
DPRINTFN(2, "usb2_ref_device, cpd=%p need uref=%d\n", cpd, need_uref);
@@ -180,7 +180,6 @@ usb2_ref_device(struct usb2_cdev_privdata* cpd, int need_uref)
goto error;
}
/* check if we are doing an open */
- dev_ep_index = cpd->ep_addr;
if (cpd->fflags == 0) {
/* set defaults */
cpd->txfifo = NULL;
@@ -200,15 +199,12 @@ usb2_ref_device(struct usb2_cdev_privdata* cpd, int need_uref)
cpd->is_write = 1; /* ref */
if (f == NULL || f->refcount == USB_FIFO_REF_MAX)
goto error;
+ if (f->curr_cpd != cpd)
+ goto error;
/* check if USB-FS is active */
if (f->fs_ep_max != 0) {
cpd->is_usbfs = 1;
}
- /*
- * Get real endpoint index associated with
- * this FIFO:
- */
- dev_ep_index = f->dev_ep_index;
} else {
cpd->txfifo = NULL;
cpd->is_write = 0; /* no ref */
@@ -222,15 +218,12 @@ usb2_ref_device(struct usb2_cdev_privdata* cpd, int need_uref)
cpd->is_read = 1; /* ref */
if (f == NULL || f->refcount == USB_FIFO_REF_MAX)
goto error;
+ if (f->curr_cpd != cpd)
+ goto error;
/* check if USB-FS is active */
if (f->fs_ep_max != 0) {
cpd->is_usbfs = 1;
}
- /*
- * Get real endpoint index associated with
- * this FIFO:
- */
- dev_ep_index = f->dev_ep_index;
} else {
cpd->rxfifo = NULL;
cpd->is_read = 0; /* no ref */
@@ -434,7 +427,7 @@ usb2_fifo_create(struct usb2_cdev_privdata *cpd)
/* wrong endpoint index */
continue;
}
- if (f->opened) {
+ if (f->curr_cpd != NULL) {
/* FIFO is opened */
is_busy = 1;
continue;
@@ -451,7 +444,7 @@ usb2_fifo_create(struct usb2_cdev_privdata *cpd)
/* wrong endpoint index */
continue;
}
- if (f->opened) {
+ if (f->curr_cpd != NULL) {
/* FIFO is opened */
is_busy = 1;
continue;
@@ -466,10 +459,20 @@ usb2_fifo_create(struct usb2_cdev_privdata *cpd)
if (no_null == 0) {
if (ep >= (USB_EP_MAX / 2)) {
/* we don't create any endpoints in this range */
- DPRINTFN(5, "dev_ep_index out of range\n");
+ DPRINTFN(5, "ep out of range\n");
return (is_busy ? EBUSY : EINVAL);
}
}
+
+ if ((ep != 0) && is_busy) {
+ /*
+ * Only the default control endpoint is allowed to be
+ * opened multiple times!
+ */
+ DPRINTFN(5, "busy\n");
+ return (EBUSY);
+ }
+
/* Check TX FIFO */
if (is_tx &&
(udev->fifo[n + USB_FIFO_TX] == NULL)) {
@@ -639,7 +642,8 @@ usb2_dev_get_pipe(struct usb2_device *udev, uint8_t ep_index, uint8_t dir)
* Else: Failure
*------------------------------------------------------------------------*/
static int
-usb2_fifo_open(struct usb2_fifo *f, int fflags)
+usb2_fifo_open(struct usb2_cdev_privdata *cpd,
+ struct usb2_fifo *f, int fflags)
{
int err;
@@ -660,11 +664,14 @@ usb2_fifo_open(struct usb2_fifo *f, int fflags)
/* check if we are already opened */
/* we don't need any locks when checking this variable */
- if (f->opened) {
+ if (f->curr_cpd != NULL) {
err = EBUSY;
goto done;
}
+ /* reset short flag before open */
+ f->flag_short = 0;
+
/* call open method */
err = (f->methods->f_open) (f, fflags);
if (err) {
@@ -690,9 +697,9 @@ usb2_fifo_open(struct usb2_fifo *f, int fflags)
/* reset ASYNC proc flag */
f->async_p = NULL;
- /* flag the fifo as opened to prevent others */
mtx_lock(&usb2_ref_lock);
- f->opened = 1;
+ /* flag the fifo as opened to prevent others */
+ f->curr_cpd = cpd;
mtx_unlock(&usb2_ref_lock);
/* reset queue */
@@ -733,14 +740,14 @@ usb2_fifo_close(struct usb2_fifo *f, int fflags)
int err;
/* check if we are not opened */
- if (!f->opened) {
+ if (f->curr_cpd == NULL) {
/* nothing to do - already closed */
return;
}
mtx_lock(f->priv_mtx);
- /* clear current file flag */
- f->opened = 0;
+ /* clear current cdev private data pointer */
+ f->curr_cpd = NULL;
/* check if we are selected */
if (f->flag_isselect) {
@@ -834,21 +841,6 @@ usb2_open(struct cdev *dev, int fflags, int devtype, struct thread *td)
}
cpd->fflags = fflags; /* access mode for open lifetime */
- /* Check if the endpoint is already open, we always allow EP0 */
- if (ep > 0) {
- if ((fflags & FREAD && cpd->udev->ep_rd_opened & (1 << ep)) ||
- (fflags & FWRITE && cpd->udev->ep_wr_opened & (1 << ep))) {
- DPRINTFN(2, "endpoint already open\n");
- usb2_unref_device(cpd);
- free(cpd, M_USBDEV);
- return (EBUSY);
- }
- if (fflags & FREAD)
- cpd->udev->ep_rd_opened |= (1 << ep);
- if (fflags & FWRITE)
- cpd->udev->ep_wr_opened |= (1 << ep);
- }
-
/* create FIFOs, if any */
err = usb2_fifo_create(cpd);
/* check for error */
@@ -859,7 +851,7 @@ usb2_open(struct cdev *dev, int fflags, int devtype, struct thread *td)
return (err);
}
if (fflags & FREAD) {
- err = usb2_fifo_open(cpd->rxfifo, fflags);
+ err = usb2_fifo_open(cpd, cpd->rxfifo, fflags);
if (err) {
DPRINTFN(2, "read open failed\n");
usb2_unref_device(cpd);
@@ -868,7 +860,7 @@ usb2_open(struct cdev *dev, int fflags, int devtype, struct thread *td)
}
}
if (fflags & FWRITE) {
- err = usb2_fifo_open(cpd->txfifo, fflags);
+ err = usb2_fifo_open(cpd, cpd->txfifo, fflags);
if (err) {
DPRINTFN(2, "write open failed\n");
if (fflags & FREAD) {
@@ -892,27 +884,20 @@ static void
usb2_close(void *arg)
{
struct usb2_cdev_privdata *cpd = arg;
- struct usb2_device *udev;
int err;
- DPRINTFN(2, "usb2_close, cpd=%p\n", cpd);
+ DPRINTFN(2, "cpd=%p\n", cpd);
err = usb2_ref_device(cpd, 1);
if (err) {
free(cpd, M_USBDEV);
return;
}
-
- udev = cpd->udev;
if (cpd->fflags & FREAD) {
usb2_fifo_close(cpd->rxfifo, cpd->fflags);
- /* clear read bitmask */
- udev->ep_rd_opened &= ~(1 << cpd->ep_addr);
}
if (cpd->fflags & FWRITE) {
usb2_fifo_close(cpd->txfifo, cpd->fflags);
- /* clear write bitmask */
- udev->ep_wr_opened &= ~(1 << cpd->ep_addr);
}
usb2_unref_device(cpd);
@@ -1621,7 +1606,6 @@ usb2_fifo_attach(struct usb2_device *udev, void *priv_sc,
/* initialise FIFO structures */
f_tx->fifo_index = n + USB_FIFO_TX;
- f_tx->dev_ep_index = (n / 2) + (USB_EP_MAX / 2);
f_tx->priv_mtx = priv_mtx;
f_tx->priv_sc0 = priv_sc;
f_tx->methods = pm;
@@ -1629,7 +1613,6 @@ usb2_fifo_attach(struct usb2_device *udev, void *priv_sc,
f_tx->udev = udev;
f_rx->fifo_index = n + USB_FIFO_RX;
- f_rx->dev_ep_index = (n / 2) + (USB_EP_MAX / 2);
f_rx->priv_mtx = priv_mtx;
f_rx->priv_sc0 = priv_sc;
f_rx->methods = pm;
@@ -1684,12 +1667,13 @@ usb2_fifo_attach(struct usb2_device *udev, void *priv_sc,
pd->bus_index = device_get_unit(udev->bus->bdev);
pd->dev_index = udev->device_index;
pd->ep_addr = -1; /* not an endpoint */
- pd->fifo_index = f_tx->fifo_index;
+ pd->fifo_index = f_tx->fifo_index & f_rx->fifo_index;
pd->mode = FREAD|FWRITE;
/* Now, create the device itself */
f_sc->dev = make_dev(&usb2_devsw, 0, uid, gid, mode,
devname);
+ /* XXX setting si_drv1 and creating the device is not atomic! */
f_sc->dev->si_drv1 = pd;
}
@@ -1762,7 +1746,9 @@ usb2_fifo_detach(struct usb2_fifo_sc *f_sc)
f_sc->fp[USB_FIFO_RX] = NULL;
if (f_sc->dev != NULL) {
- destroy_dev_sched_cb(f_sc->dev, usb2_fifo_cleanup, f_sc->dev->si_drv1);
+ destroy_dev_sched_cb(f_sc->dev,
+ usb2_fifo_cleanup, f_sc->dev->si_drv1);
+ f_sc->dev = NULL;
}
DPRINTFN(2, "detached %p\n", f_sc);
@@ -1949,6 +1935,13 @@ usb2_fifo_get_data(struct usb2_fifo *f, struct usb2_page_cache *pc,
break;
}
if (f->flag_flushing) {
+ /* check if we should send a short packet */
+ if (f->flag_short != 0) {
+ f->flag_short = 0;
+ tr_data = 1;
+ break;
+ }
+ /* flushing complete */
f->flag_flushing = 0;
usb2_fifo_wakeup(f);
}
@@ -2007,6 +2000,13 @@ usb2_fifo_get_data_linear(struct usb2_fifo *f, void *ptr,
break;
}
if (f->flag_flushing) {
+ /* check if we should send a short packet */
+ if (f->flag_short != 0) {
+ f->flag_short = 0;
+ tr_data = 1;
+ break;
+ }
+ /* flushing complete */
f->flag_flushing = 0;
usb2_fifo_wakeup(f);
}
@@ -2186,3 +2186,13 @@ usb2_read_symlink(uint8_t *user_ptr, uint32_t startentry, uint32_t user_len)
sx_unlock(&usb2_sym_lock);
return (error);
}
+
+void
+usb2_fifo_set_close_zlp(struct usb2_fifo *f, uint8_t onoff)
+{
+ if (f == NULL)
+ return;
+
+ /* send a Zero Length Packet, ZLP, before close */
+ f->flag_short = onoff;
+}
OpenPOWER on IntegriCloud