USB core:
- add support for defragging of written device data. - improve handling of alternate settings in device side mode. - correct return value from usbd_get_no_alts() function. - reported by: HPS - P4 ID: 166156, 166168 - report USB device release information to devd and pnpinfo. - reported by: MIHIRA Sanpei Yoshiro - P4 ID: 166221 Submitted by: hps Approved by: re
This commit is contained in:
parent
b27b901cb4
commit
bd73b18714
@ -740,6 +740,8 @@ usb_fifo_reset(struct usb_fifo *f)
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* reset have fragment flag */
|
||||
f->flag_have_fragment = 0;
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*
|
||||
@ -783,6 +785,16 @@ usb_fifo_close(struct usb_fifo *f, int fflags)
|
||||
/* set flushing flag */
|
||||
f->flag_flushing = 1;
|
||||
|
||||
/* get the last packet in */
|
||||
if (f->flag_have_fragment) {
|
||||
struct usb_mbuf *m;
|
||||
f->flag_have_fragment = 0;
|
||||
USB_IF_DEQUEUE(&f->free_q, m);
|
||||
if (m) {
|
||||
USB_IF_ENQUEUE(&f->used_q, m);
|
||||
}
|
||||
}
|
||||
|
||||
/* start write transfer, if not already started */
|
||||
(f->methods->f_start_write) (f);
|
||||
|
||||
@ -1303,6 +1315,7 @@ usb_write(struct cdev *dev, struct uio *uio, int ioflag)
|
||||
struct usb_cdev_privdata* cpd;
|
||||
struct usb_fifo *f;
|
||||
struct usb_mbuf *m;
|
||||
uint8_t *pdata;
|
||||
int fflags;
|
||||
int resid;
|
||||
int io_len;
|
||||
@ -1373,33 +1386,59 @@ usb_write(struct cdev *dev, struct uio *uio, int ioflag)
|
||||
}
|
||||
tr_data = 1;
|
||||
|
||||
USB_MBUF_RESET(m);
|
||||
|
||||
io_len = MIN(m->cur_data_len, uio->uio_resid);
|
||||
|
||||
m->cur_data_len = io_len;
|
||||
if (f->flag_have_fragment == 0) {
|
||||
USB_MBUF_RESET(m);
|
||||
io_len = m->cur_data_len;
|
||||
pdata = m->cur_data_ptr;
|
||||
if (io_len > uio->uio_resid)
|
||||
io_len = uio->uio_resid;
|
||||
m->cur_data_len = io_len;
|
||||
} else {
|
||||
io_len = m->max_data_len - m->cur_data_len;
|
||||
pdata = m->cur_data_ptr + m->cur_data_len;
|
||||
if (io_len > uio->uio_resid)
|
||||
io_len = uio->uio_resid;
|
||||
m->cur_data_len += io_len;
|
||||
}
|
||||
|
||||
DPRINTFN(2, "transfer %d bytes to %p\n",
|
||||
io_len, m->cur_data_ptr);
|
||||
io_len, pdata);
|
||||
|
||||
err = usb_fifo_uiomove(f,
|
||||
m->cur_data_ptr, io_len, uio);
|
||||
err = usb_fifo_uiomove(f, pdata, io_len, uio);
|
||||
|
||||
if (err) {
|
||||
f->flag_have_fragment = 0;
|
||||
USB_IF_ENQUEUE(&f->free_q, m);
|
||||
break;
|
||||
}
|
||||
if (f->methods->f_filter_write) {
|
||||
/*
|
||||
* Sometimes it is convenient to process data at the
|
||||
* expense of a userland process instead of a kernel
|
||||
* process.
|
||||
*/
|
||||
(f->methods->f_filter_write) (f, m);
|
||||
}
|
||||
USB_IF_ENQUEUE(&f->used_q, m);
|
||||
|
||||
(f->methods->f_start_write) (f);
|
||||
/* check if the buffer is ready to be transmitted */
|
||||
|
||||
if ((f->flag_write_defrag == 0) ||
|
||||
(m->cur_data_len == m->max_data_len)) {
|
||||
f->flag_have_fragment = 0;
|
||||
|
||||
/*
|
||||
* Check for write filter:
|
||||
*
|
||||
* Sometimes it is convenient to process data
|
||||
* at the expense of a userland process
|
||||
* instead of a kernel process.
|
||||
*/
|
||||
if (f->methods->f_filter_write) {
|
||||
(f->methods->f_filter_write) (f, m);
|
||||
}
|
||||
|
||||
/* Put USB mbuf in the used queue */
|
||||
USB_IF_ENQUEUE(&f->used_q, m);
|
||||
|
||||
/* Start writing data, if not already started */
|
||||
(f->methods->f_start_write) (f);
|
||||
} else {
|
||||
/* Wait for more data or close */
|
||||
f->flag_have_fragment = 1;
|
||||
USB_IF_PREPEND(&f->free_q, m);
|
||||
}
|
||||
|
||||
} while (uio->uio_resid > 0);
|
||||
done:
|
||||
@ -2220,6 +2259,18 @@ usb_fifo_set_close_zlp(struct usb_fifo *f, uint8_t onoff)
|
||||
f->flag_short = onoff;
|
||||
}
|
||||
|
||||
void
|
||||
usb_fifo_set_write_defrag(struct usb_fifo *f, uint8_t onoff)
|
||||
{
|
||||
if (f == NULL)
|
||||
return;
|
||||
|
||||
/* defrag written data */
|
||||
f->flag_write_defrag = onoff;
|
||||
/* reset defrag state */
|
||||
f->flag_have_fragment = 0;
|
||||
}
|
||||
|
||||
void *
|
||||
usb_fifo_softc(struct usb_fifo *f)
|
||||
{
|
||||
|
@ -130,6 +130,8 @@ struct usb_fifo {
|
||||
uint8_t flag_short; /* set if short_ok or force_short
|
||||
* transfer flags should be set */
|
||||
uint8_t flag_stall; /* set if clear stall should be run */
|
||||
uint8_t flag_write_defrag; /* set to defrag written data */
|
||||
uint8_t flag_have_fragment; /* set if defragging */
|
||||
uint8_t iface_index; /* set to the interface we belong to */
|
||||
uint8_t fifo_index; /* set to the FIFO index in "struct
|
||||
* usb_device" */
|
||||
@ -144,11 +146,9 @@ extern struct cdevsw usb_devsw;
|
||||
int usb_fifo_wait(struct usb_fifo *fifo);
|
||||
void usb_fifo_signal(struct usb_fifo *fifo);
|
||||
uint8_t usb_fifo_opened(struct usb_fifo *fifo);
|
||||
void usb_fifo_free(struct usb_fifo *f);
|
||||
struct usb_symlink *usb_alloc_symlink(const char *target);
|
||||
void usb_free_symlink(struct usb_symlink *ps);
|
||||
int usb_read_symlink(uint8_t *user_ptr, uint32_t startentry,
|
||||
uint32_t user_len);
|
||||
void usb_fifo_set_close_zlp(struct usb_fifo *, uint8_t);
|
||||
|
||||
#endif /* _USB_DEV_H_ */
|
||||
|
@ -833,18 +833,13 @@ usbd_set_alt_interface_index(struct usb_device *udev,
|
||||
err = USB_ERR_INVAL;
|
||||
goto done;
|
||||
}
|
||||
if (udev->flags.usb_mode == USB_MODE_DEVICE) {
|
||||
usb_detach_device(udev, iface_index,
|
||||
USB_UNCFG_FLAG_FREE_SUBDEV);
|
||||
} else {
|
||||
if (iface->alt_index == alt_index) {
|
||||
/*
|
||||
* Optimise away duplicate setting of
|
||||
* alternate setting in USB Host Mode!
|
||||
*/
|
||||
err = 0;
|
||||
goto done;
|
||||
}
|
||||
if (iface->alt_index == alt_index) {
|
||||
/*
|
||||
* Optimise away duplicate setting of
|
||||
* alternate setting in USB Host Mode!
|
||||
*/
|
||||
err = 0;
|
||||
goto done;
|
||||
}
|
||||
#if USB_HAVE_UGEN
|
||||
/*
|
||||
@ -858,6 +853,12 @@ usbd_set_alt_interface_index(struct usb_device *udev,
|
||||
if (err) {
|
||||
goto done;
|
||||
}
|
||||
if (iface->alt_index != alt_index) {
|
||||
/* the alternate setting does not exist */
|
||||
err = USB_ERR_INVAL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
err = usbd_req_set_alt_interface_no(udev, NULL, iface_index,
|
||||
iface->idesc->bAlternateSetting);
|
||||
|
||||
@ -959,7 +960,6 @@ usb_reset_iface_endpoints(struct usb_device *udev, uint8_t iface_index)
|
||||
{
|
||||
struct usb_endpoint *ep;
|
||||
struct usb_endpoint *ep_end;
|
||||
usb_error_t err;
|
||||
|
||||
ep = udev->endpoints;
|
||||
ep_end = udev->endpoints + udev->endpoints_max;
|
||||
@ -971,10 +971,7 @@ usb_reset_iface_endpoints(struct usb_device *udev, uint8_t iface_index)
|
||||
continue;
|
||||
}
|
||||
/* simulate a clear stall from the peer */
|
||||
err = usbd_set_endpoint_stall(udev, ep, 0);
|
||||
if (err) {
|
||||
/* just ignore */
|
||||
}
|
||||
usbd_set_endpoint_stall(udev, ep, 0);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
@ -1286,6 +1283,7 @@ usb_probe_and_attach(struct usb_device *udev, uint8_t iface_index)
|
||||
uaa.info.bIfaceNum =
|
||||
iface->idesc->bInterfaceNumber;
|
||||
uaa.use_generic = 0;
|
||||
uaa.driver_info = 0; /* reset driver_info */
|
||||
|
||||
DPRINTFN(2, "iclass=%u/%u/%u iindex=%u/%u\n",
|
||||
uaa.info.bInterfaceClass,
|
||||
@ -1302,6 +1300,7 @@ usb_probe_and_attach(struct usb_device *udev, uint8_t iface_index)
|
||||
/* try generic interface drivers last */
|
||||
|
||||
uaa.use_generic = 1;
|
||||
uaa.driver_info = 0; /* reset driver_info */
|
||||
|
||||
if (usb_probe_and_attach_sub(udev, &uaa)) {
|
||||
/* ignore */
|
||||
@ -2334,6 +2333,7 @@ usb_notify_addq(const char *type, struct usb_device *udev)
|
||||
"devclass=0x%02x "
|
||||
"devsubclass=0x%02x "
|
||||
"sernum=\"%s\" "
|
||||
"release=0x%04x "
|
||||
"at "
|
||||
"port=%u "
|
||||
"on "
|
||||
@ -2345,6 +2345,7 @@ usb_notify_addq(const char *type, struct usb_device *udev)
|
||||
udev->ddesc.bDeviceClass,
|
||||
udev->ddesc.bDeviceSubClass,
|
||||
udev->serial,
|
||||
UGETW(udev->ddesc.bcdDevice),
|
||||
udev->port_no,
|
||||
udev->parent_hub != NULL ?
|
||||
udev->parent_hub->ugen_name :
|
||||
|
@ -46,6 +46,7 @@
|
||||
|
||||
#include <dev/usb/usb.h>
|
||||
#include <dev/usb/usbdi.h>
|
||||
#include <dev/usb/usbdi_util.h>
|
||||
#include "usb_if.h"
|
||||
|
||||
#define USB_DEBUG_VAR usb_debug
|
||||
@ -181,6 +182,30 @@ usb_handle_set_config(struct usb_xfer *xfer, uint8_t conf_no)
|
||||
return (err);
|
||||
}
|
||||
|
||||
static usb_error_t
|
||||
usb_check_alt_setting(struct usb_device *udev,
|
||||
struct usb_interface *iface, uint8_t alt_index)
|
||||
{
|
||||
uint8_t do_unlock;
|
||||
usb_error_t err = 0;
|
||||
|
||||
/* automatic locking */
|
||||
if (sx_xlocked(udev->default_sx + 1)) {
|
||||
do_unlock = 0;
|
||||
} else {
|
||||
do_unlock = 1;
|
||||
sx_xlock(udev->default_sx + 1);
|
||||
}
|
||||
|
||||
if (alt_index >= usbd_get_no_alts(udev->cdesc, iface->idesc))
|
||||
err = USB_ERR_INVAL;
|
||||
|
||||
if (do_unlock) {
|
||||
sx_unlock(udev->default_sx + 1);
|
||||
}
|
||||
return (err);
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*
|
||||
* usb_handle_iface_request
|
||||
*
|
||||
@ -285,42 +310,29 @@ usb_handle_iface_request(struct usb_xfer *xfer,
|
||||
switch (req.bRequest) {
|
||||
case UR_SET_INTERFACE:
|
||||
/*
|
||||
* Handle special case. If we have parent interface
|
||||
* we just reset the endpoints, because this is a
|
||||
* multi interface device and re-attaching only a
|
||||
* part of the device is not possible. Also if the
|
||||
* alternate setting is the same like before we just
|
||||
* reset the interface endoints.
|
||||
* We assume that the endpoints are the same
|
||||
* accross the alternate settings.
|
||||
*
|
||||
* Reset the endpoints, because re-attaching
|
||||
* only a part of the device is not possible.
|
||||
*/
|
||||
if ((iface_parent != NULL) ||
|
||||
(iface->alt_index == req.wValue[0])) {
|
||||
error = usb_reset_iface_endpoints(udev,
|
||||
iface_index);
|
||||
if (error) {
|
||||
DPRINTF("alt setting failed %s\n",
|
||||
usbd_errstr(error));
|
||||
goto tr_stalled;
|
||||
}
|
||||
break;
|
||||
error = usb_check_alt_setting(udev,
|
||||
iface, req.wValue[0]);
|
||||
if (error) {
|
||||
DPRINTF("alt setting does not exist %s\n",
|
||||
usbd_errstr(error));
|
||||
goto tr_stalled;
|
||||
}
|
||||
/*
|
||||
* Doing the alternate setting will detach the
|
||||
* interface aswell:
|
||||
*/
|
||||
error = usbd_set_alt_interface_index(udev,
|
||||
iface_index, req.wValue[0]);
|
||||
error = usb_reset_iface_endpoints(udev, iface_index);
|
||||
if (error) {
|
||||
DPRINTF("alt setting failed %s\n",
|
||||
usbd_errstr(error));
|
||||
goto tr_stalled;
|
||||
}
|
||||
error = usb_probe_and_attach(udev,
|
||||
iface_index);
|
||||
if (error) {
|
||||
DPRINTF("alt setting probe failed\n");
|
||||
goto tr_stalled;
|
||||
}
|
||||
/* update the current alternate setting */
|
||||
iface->alt_index = req.wValue[0];
|
||||
break;
|
||||
|
||||
default:
|
||||
goto tr_stalled;
|
||||
}
|
||||
|
@ -1020,12 +1020,14 @@ uhub_child_pnpinfo_string(device_t parent, device_t child,
|
||||
snprintf(buf, buflen, "vendor=0x%04x product=0x%04x "
|
||||
"devclass=0x%02x devsubclass=0x%02x "
|
||||
"sernum=\"%s\" "
|
||||
"release=0x%04x "
|
||||
"intclass=0x%02x intsubclass=0x%02x",
|
||||
UGETW(res.udev->ddesc.idVendor),
|
||||
UGETW(res.udev->ddesc.idProduct),
|
||||
res.udev->ddesc.bDeviceClass,
|
||||
res.udev->ddesc.bDeviceSubClass,
|
||||
res.udev->serial,
|
||||
UGETW(res.udev->ddesc.bcdDevice),
|
||||
iface->idesc->bInterfaceClass,
|
||||
iface->idesc->bInterfaceSubClass);
|
||||
} else {
|
||||
|
@ -215,20 +215,29 @@ usbd_get_no_descriptors(struct usb_config_descriptor *cd, uint8_t type)
|
||||
* usbd_get_no_alts
|
||||
*
|
||||
* Return value:
|
||||
* Number of alternate settings for the given interface descriptor pointer.
|
||||
* Number of alternate settings for the given interface descriptor
|
||||
* pointer. If the USB descriptor is corrupt, the returned value can
|
||||
* be greater than the actual number of alternate settings.
|
||||
*------------------------------------------------------------------------*/
|
||||
uint8_t
|
||||
usbd_get_no_alts(struct usb_config_descriptor *cd,
|
||||
struct usb_interface_descriptor *id)
|
||||
{
|
||||
struct usb_descriptor *desc;
|
||||
uint8_t n = 0;
|
||||
uint8_t n;
|
||||
uint8_t ifaceno;
|
||||
|
||||
/* Reset interface count */
|
||||
|
||||
n = 0;
|
||||
|
||||
/* Get the interface number */
|
||||
|
||||
ifaceno = id->bInterfaceNumber;
|
||||
|
||||
desc = (struct usb_descriptor *)id;
|
||||
/* Iterate all the USB descriptors */
|
||||
|
||||
desc = NULL;
|
||||
while ((desc = usb_desc_foreach(cd, desc))) {
|
||||
if ((desc->bDescriptorType == UDESC_INTERFACE) &&
|
||||
(desc->bLength >= sizeof(*id))) {
|
||||
@ -237,8 +246,7 @@ usbd_get_no_alts(struct usb_config_descriptor *cd,
|
||||
n++;
|
||||
if (n == 0xFF)
|
||||
break; /* crazy */
|
||||
} else
|
||||
break; /* end */
|
||||
}
|
||||
}
|
||||
}
|
||||
return (n);
|
||||
|
@ -1059,9 +1059,9 @@ usbd_req_get_alt_interface_no(struct usb_device *udev, struct mtx *mtx,
|
||||
struct usb_interface *iface = usbd_get_iface(udev, iface_index);
|
||||
struct usb_device_request req;
|
||||
|
||||
if ((iface == NULL) || (iface->idesc == NULL)) {
|
||||
if ((iface == NULL) || (iface->idesc == NULL))
|
||||
return (USB_ERR_INVAL);
|
||||
}
|
||||
|
||||
req.bmRequestType = UT_READ_INTERFACE;
|
||||
req.bRequest = UR_GET_INTERFACE;
|
||||
USETW(req.wValue, 0);
|
||||
@ -1085,9 +1085,9 @@ usbd_req_set_alt_interface_no(struct usb_device *udev, struct mtx *mtx,
|
||||
struct usb_interface *iface = usbd_get_iface(udev, iface_index);
|
||||
struct usb_device_request req;
|
||||
|
||||
if ((iface == NULL) || (iface->idesc == NULL)) {
|
||||
if ((iface == NULL) || (iface->idesc == NULL))
|
||||
return (USB_ERR_INVAL);
|
||||
}
|
||||
|
||||
req.bmRequestType = UT_WRITE_INTERFACE;
|
||||
req.bRequest = UR_SET_INTERFACE;
|
||||
req.wValue[0] = alt_no;
|
||||
|
Loading…
Reference in New Issue
Block a user