// SPDX-License-Identifier: GPL-2.0+ /* * dummy_hcd.c -- Dummy/Loopback USB host and device emulator driver. * * Maintainer: Alan Stern <stern@rowland.harvard.edu> * * Copyright (C) 2003 David Brownell * Copyright (C) 2003-2005 Alan Stern
*/
/* * This exposes a device side "USB gadget" API, driven by requests to a * Linux-USB host controller driver. USB traffic is simulated; there's * no need for USB hardware. Use this with two other drivers: * * - Gadget driver, responding to requests (device); * - Host-side device driver, as already familiar in Linux. * * Having this all in one kernel can help some stages of development, * bypassing some hardware (and driver) issues. UML could help too. * * Note: The emulation does not include isochronous transfers!
*/
/* * Every device has ep0 for control requests, plus up to 30 more endpoints, * in one of two types: * * - Configurable: direction (in/out), type (bulk, iso, etc), and endpoint * number can be changed. Names like "ep-a" are used for this type. * * - Fixed Function: in other cases. some characteristics may be mutable; * that'd be hardware-specific. Names like "ep12out-bulk" are used. * * Gadget drivers are responsible for not setting up conflicting endpoint * configurations, illegal or unsupported packet lengths, and so on.
*/
/* or like sa1100: two fixed function endpoints */
EP_INFO("ep1out-bulk",
USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_OUT)),
EP_INFO("ep2in-bulk",
USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_IN)),
/* and now some generic EPs so we have enough in multi config */
EP_INFO("ep-aout",
USB_EP_CAPS(TYPE_BULK_OR_INT, USB_EP_CAPS_DIR_OUT)),
EP_INFO("ep-bin",
USB_EP_CAPS(TYPE_BULK_OR_INT, USB_EP_CAPS_DIR_IN)),
EP_INFO("ep-cout",
USB_EP_CAPS(TYPE_BULK_OR_INT, USB_EP_CAPS_DIR_OUT)),
EP_INFO("ep-dout",
USB_EP_CAPS(TYPE_BULK_OR_INT, USB_EP_CAPS_DIR_OUT)),
EP_INFO("ep-ein",
USB_EP_CAPS(TYPE_BULK_OR_INT, USB_EP_CAPS_DIR_IN)),
EP_INFO("ep-fout",
USB_EP_CAPS(TYPE_BULK_OR_INT, USB_EP_CAPS_DIR_OUT)),
EP_INFO("ep-gin",
USB_EP_CAPS(TYPE_BULK_OR_INT, USB_EP_CAPS_DIR_IN)),
EP_INFO("ep-hout",
USB_EP_CAPS(TYPE_BULK_OR_INT, USB_EP_CAPS_DIR_OUT)),
EP_INFO("ep-iout",
USB_EP_CAPS(TYPE_BULK_OR_INT, USB_EP_CAPS_DIR_OUT)),
EP_INFO("ep-jin",
USB_EP_CAPS(TYPE_BULK_OR_INT, USB_EP_CAPS_DIR_IN)),
EP_INFO("ep-kout",
USB_EP_CAPS(TYPE_BULK_OR_INT, USB_EP_CAPS_DIR_OUT)),
EP_INFO("ep-lin",
USB_EP_CAPS(TYPE_BULK_OR_INT, USB_EP_CAPS_DIR_IN)),
EP_INFO("ep-mout",
USB_EP_CAPS(TYPE_BULK_OR_INT, USB_EP_CAPS_DIR_OUT)),
/* caller must hold lock */ staticvoid stop_activity(struct dummy *dum)
{ int i;
/* prevent any more requests */
dum->address = 0;
/* The timer is left running so that outstanding URBs can fail */
/* nuke any pending requests first, so driver i/o is quiesced */ for (i = 0; i < DUMMY_ENDPOINTS; ++i)
nuke(dum, &dum->ep[i]);
/* driver now does any non-usb quiescing necessary */
}
/** * set_link_state_by_speed() - Sets the current state of the link according to * the hcd speed * @dum_hcd: pointer to the dummy_hcd structure to update the link state for * * This function updates the port_status according to the link state and the * speed of the hcd.
*/ staticvoid set_link_state_by_speed(struct dummy_hcd *dum_hcd)
{ struct dummy *dum = dum_hcd->dum;
/* DEVICE/GADGET SIDE DRIVER * * This only tracks gadget state. All the work is done when the host * side tries some (emulated) i/o operation. Real device controller * drivers would do real i/o using dma, fifos, irqs, timers, etc.
*/
ep = usb_ep_to_dummy_ep(_ep); if (!_ep || !desc || ep->desc || _ep->name == ep0name
|| desc->bDescriptorType != USB_DT_ENDPOINT) return -EINVAL;
dum = ep_to_dummy(ep); if (!dum->driver) return -ESHUTDOWN;
dum_hcd = gadget_to_dummy_hcd(&dum->gadget); if (!is_enabled(dum_hcd)) return -ESHUTDOWN;
/* * For HS/FS devices only bits 0..10 of the wMaxPacketSize represent the * maximum packet size. * For SS devices the wMaxPacketSize is limited by 1024.
*/
max = usb_endpoint_maxp(desc);
/* drivers must not request bad settings, since lower levels * (hardware or its drivers) may not check. some endpoints * can't do iso, many have maxpacket limitations, etc. * * since this "hardware" driver is here to help debugging, we * have some extra sanity checks. (there could be more though, * especially for "ep9out" style fixed function ones.)
*/
retval = -EINVAL; switch (usb_endpoint_type(desc)) { case USB_ENDPOINT_XFER_BULK: if (strstr(ep->ep.name, "-iso")
|| strstr(ep->ep.name, "-int")) { goto done;
} switch (dum->gadget.speed) { case USB_SPEED_SUPER: if (max == 1024) break; goto done; case USB_SPEED_HIGH: if (max == 512) break; goto done; case USB_SPEED_FULL: if (max == 8 || max == 16 || max == 32 || max == 64) /* we'll fake any legal size */ break; /* save a return statement */
fallthrough; default: goto done;
} break; case USB_ENDPOINT_XFER_INT: if (strstr(ep->ep.name, "-iso")) /* bulk is ok */ goto done; /* real hardware might not handle all packet sizes */ switch (dum->gadget.speed) { case USB_SPEED_SUPER: case USB_SPEED_HIGH: if (max <= 1024) break; /* save a return statement */
fallthrough; case USB_SPEED_FULL: if (max <= 64) break; /* save a return statement */
fallthrough; default: if (max <= 8) break; goto done;
} break; case USB_ENDPOINT_XFER_ISOC: if (strstr(ep->ep.name, "-bulk")
|| strstr(ep->ep.name, "-int")) goto done; /* real hardware might not handle all packet sizes */ switch (dum->gadget.speed) { case USB_SPEED_SUPER: case USB_SPEED_HIGH: if (max <= 1024) break; /* save a return statement */
fallthrough; case USB_SPEED_FULL: if (max <= 1023) break; /* save a return statement */
fallthrough; default: goto done;
} break; default: /* few chips support control except on ep0 */ goto done;
}
_ep->maxpacket = max; if (usb_ss_max_streams(_ep->comp_desc)) { if (!usb_endpoint_xfer_bulk(desc)) {
dev_err(udc_dev(dum), "Can't enable stream support on " "non-bulk ep %s\n", _ep->name); return -EINVAL;
}
ep->stream_en = 1;
}
ep->desc = desc;
/* at this point real hardware should be NAKing transfers * to that endpoint, until a buffer is queued to it.
*/
ep->halted = ep->wedged = 0;
retval = 0;
done: return retval;
}
dum = gadget_dev_to_dummy(&_gadget->dev);
dum_hcd = gadget_to_dummy_hcd(_gadget);
spin_lock_irqsave(&dum->lock, flags);
dum->pullup = (value != 0);
set_link_state(dum_hcd); if (value == 0) { /* * Emulate synchronize_irq(): wait for callbacks to finish. * This seems to be the best place to emulate the call to * synchronize_irq() that's in usb_gadget_remove_driver(). * Doing it in dummy_udc_stop() would be too late since it * is called after the unbind callback and unbind shouldn't * be invoked until all the other callbacks are finished.
*/ while (dum->callback_usage > 0) {
spin_unlock_irqrestore(&dum->lock, flags);
usleep_range(1000, 2000);
spin_lock_irqsave(&dum->lock, flags);
}
}
spin_unlock_irqrestore(&dum->lock, flags);
/* * Driver registration/unregistration. * * This is basically hardware-specific; there's usually only one real USB * device (not host) controller since that's how USB devices are intended * to work. So most implementations of these api calls will rely on the * fact that only one driver will ever bind to the hardware. But curious * hardware can be built with discrete components, so the gadget API doesn't * require that assumption. * * For this emulator, it might be convenient to create a usb device * for each driver that registers: just add to a big root hub.
*/
switch (g->speed) { /* All the speeds we support */ case USB_SPEED_LOW: case USB_SPEED_FULL: case USB_SPEED_HIGH: case USB_SPEED_SUPER: break; default:
dev_err(dummy_dev(dum_hcd), "Unsupported driver max speed %d\n",
driver->max_speed); return -EINVAL;
}
/* * DEVICE side init ... the layer above hardware, which * can't enumerate without help from the driver we're binding.
*/
/* The gadget structure is stored inside the hcd structure and will be
* released along with it. */ staticvoid init_dummy_udc_hw(struct dummy *dum)
{ int i;
INIT_LIST_HEAD(&dum->gadget.ep_list); for (i = 0; i < DUMMY_ENDPOINTS; i++) { struct dummy_ep *ep = &dum->ep[i];
index = usb_endpoint_num(desc) << 1; if (usb_endpoint_dir_in(desc))
index |= 1; return index;
}
/* HOST SIDE DRIVER * * this uses the hcd framework to hook up to host side drivers. * its root hub will only have one device, otherwise it acts like * a normal host controller. * * when urbs are queued, they're just stuck on a list that we * scan in a timer callback. that callback connects writes from * the host with reads from the device, and so on, based on the * usb 2.0 rules.
*/
index = dummy_get_ep_idx(desc); return (1 << index) & dum_hcd->stream_en_ep;
}
/* * The max stream number is saved as a nibble so for the 30 possible endpoints * we only 15 bytes of memory. Therefore we are limited to max 16 streams (0 * means we use only 1 stream). The maximum according to the spec is 16bit so * if the 16 stream limit is about to go, the array size should be incremented * to 30 elements of type u16.
*/ staticint get_max_streams_for_pipe(struct dummy_hcd *dum_hcd, unsignedint pipe)
{ int max_streams;
enabled = dummy_ep_stream_en(dum_hcd, urb); if (!urb->stream_id) { if (enabled) return -EINVAL; return 0;
} if (!enabled) return -EINVAL;
max_streams = get_max_streams_for_pipe(dum_hcd,
usb_pipeendpoint(urb->pipe)); if (urb->stream_id > max_streams) {
dev_err(dummy_dev(dum_hcd), "Stream id %d is out of range.\n",
urb->stream_id);
BUG(); return -EINVAL;
} return 0;
}
if (!dum_hcd->udev) {
dum_hcd->udev = urb->dev;
usb_get_dev(dum_hcd->udev);
} elseif (unlikely(dum_hcd->udev != urb->dev))
dev_err(dummy_dev(dum_hcd), "usb_device address has changed!\n");
list_add_tail(&urbp->urbp_list, &dum_hcd->urbp_list);
urb->hcpriv = urbp; if (!dum_hcd->next_frame_urbp)
dum_hcd->next_frame_urbp = urbp; if (usb_pipetype(urb->pipe) == PIPE_CONTROL)
urb->error_count = 1; /* mark as a new urb */
/* kick the scheduler, it'll do the rest */ if (!dum_hcd->timer_pending) {
dum_hcd->timer_pending = 1;
hrtimer_start(&dum_hcd->timer, ns_to_ktime(DUMMY_TIMER_INT_NSECS),
HRTIMER_MODE_REL_SOFT);
}
staticint dummy_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
{ struct dummy_hcd *dum_hcd; unsignedlong flags; int rc;
/* giveback happens automatically in timer callback,
* so make sure the callback happens */
dum_hcd = hcd_to_dummy_hcd(hcd);
spin_lock_irqsave(&dum_hcd->dum->lock, flags);
if (to_host)
memcpy(ubuf, rbuf, this_sg); else
memcpy(rbuf, ubuf, this_sg);
len -= this_sg;
if (!len) break;
next_sg = sg_miter_next(miter); if (next_sg == false) {
WARN_ON_ONCE(1); return -EINVAL;
}
rbuf += this_sg;
} while (1);
sg_miter_stop(miter); return trans;
}
/* transfer up to a frame's worth; caller must own lock */ staticint transfer(struct dummy_hcd *dum_hcd, struct urb *urb, struct dummy_ep *ep, int limit, int *status)
{ struct dummy *dum = dum_hcd->dum; struct dummy_request *req; int sent = 0;
top: /* if there's no request queued, the device is NAKing; return */
list_for_each_entry(req, &ep->queue, queue) { unsigned host_len, dev_len, len; int is_short, to_host; int rescan = 0;
if (dummy_ep_stream_en(dum_hcd, urb)) { if ((urb->stream_id != req->req.stream_id)) continue;
}
/* 1..N packets of ep->ep.maxpacket each ... the last one * may be short (including zero length). * * writer can send a zlp explicitly (length 0) or implicitly * (length mod maxpacket zero, and 'zero' flag); they always * terminate reads.
*/
host_len = urb->transfer_buffer_length - urb->actual_length;
dev_len = req->req.length - req->req.actual;
len = min(host_len, dev_len);
/* FIXME update emulated data toggle too */
to_host = usb_urb_dir_in(urb); if (unlikely(len == 0))
is_short = 1; else { /* not enough bandwidth left? */ if (limit < ep->ep.maxpacket && limit < len) break;
len = min_t(unsigned, len, limit); if (len == 0) break;
/* send multiple of maxpacket first, then remainder */ if (len >= ep->ep.maxpacket) {
is_short = 0; if (len % ep->ep.maxpacket)
rescan = 1;
len -= len % ep->ep.maxpacket;
} else {
is_short = 1;
}
/** * handle_control_request() - handles all control transfers * @dum_hcd: pointer to dummy (the_controller) * @urb: the urb request to handle * @setup: pointer to the setup data for a USB device control * request * @status: pointer to request handling status * * Return 0 - if the request was handled * 1 - if the request wasn't handles * error code on error
*/ staticint handle_control_request(struct dummy_hcd *dum_hcd, struct urb *urb, struct usb_ctrlrequest *setup, int *status)
{ struct dummy_ep *ep2; struct dummy *dum = dum_hcd->dum; int ret_val = 1; unsigned w_index; unsigned w_value;
/* * Drive both sides of the transfers; looks like irq handlers to both * drivers except that the callbacks are invoked from soft interrupt * context.
*/ staticenum hrtimer_restart dummy_timer(struct hrtimer *t)
{ struct dummy_hcd *dum_hcd = timer_container_of(dum_hcd, t,
timer); struct dummy *dum = dum_hcd->dum; struct urbp *urbp, *tmp; unsignedlong flags; int limit, total; int i;
/* simplistic model for one frame's bandwidth */ /* FIXME: account for transaction and packet overhead */ switch (dum->gadget.speed) { case USB_SPEED_LOW:
total = 8/*bytes*/ * 12/*packets*/; break; case USB_SPEED_FULL:
total = 64/*bytes*/ * 19/*packets*/; break; case USB_SPEED_HIGH:
total = 512/*bytes*/ * 13/*packets*/ * 8/*uframes*/; break; case USB_SPEED_SUPER: /* Bus speed is 500000 bytes/ms, so use a little less */
total = 490000; break; default: /* Can't happen */
dev_err(dummy_dev(dum_hcd), "bogus device speed\n");
total = 0; break;
}
/* look at each urb queued by the host side driver */
spin_lock_irqsave(&dum->lock, flags);
dum_hcd->timer_pending = 0;
if (!dum_hcd->udev) {
dev_err(dummy_dev(dum_hcd), "timer fired with no URBs pending?\n");
spin_unlock_irqrestore(&dum->lock, flags); return HRTIMER_NORESTART;
}
dum_hcd->next_frame_urbp = NULL;
for (i = 0; i < DUMMY_ENDPOINTS; i++) { if (!ep_info[i].name) break;
dum->ep[i].already_seen = 0;
}
/* Used up this frame's bandwidth? */ if (total <= 0) continue;
/* find the gadget's ep for this request (if configured) */
address = usb_pipeendpoint (urb->pipe); if (usb_urb_dir_in(urb))
address |= USB_DIR_IN;
ep = find_endpoint(dum, address); if (!ep) { /* set_configuration() disagreement */
dev_dbg(dummy_dev(dum_hcd), "no ep configured for urb %p\n",
urb);
status = -EPROTO; goto return_urb;
}
if (ep->already_seen) continue;
ep->already_seen = 1; if (ep == &dum->ep[0] && urb->error_count) {
ep->setup_stage = 1; /* a new urb */
urb->error_count = 0;
} if (ep->halted && !ep->setup_stage) { /* NOTE: must not be iso! */
dev_dbg(dummy_dev(dum_hcd), "ep %s halted, urb %p\n",
ep->ep.name, urb);
status = -EPIPE; goto return_urb;
} /* FIXME make sure both ends agree on maxpacket */
/* handle control requests */ if (ep == &dum->ep[0] && ep->setup_stage) { struct usb_ctrlrequest setup; int value;
setup = *(struct usb_ctrlrequest *) urb->setup_packet; /* paranoia, in case of stale queued data */
list_for_each_entry(req, &ep->queue, queue) {
list_del_init(&req->queue);
req->req.status = -EOVERFLOW;
dev_dbg(udc_dev(dum), "stale req = %p\n",
req);
/* gadget driver never sees set_address or operations * on standard feature flags. some hardware doesn't * even expose them.
*/
ep->last_io = jiffies;
ep->setup_stage = 0;
ep->halted = 0;
value = handle_control_request(dum_hcd, urb, &setup,
&status);
/* gadget driver handles all other requests. block * until setup() returns; no reentrancy issues etc.
*/ if (value > 0) {
++dum->callback_usage;
spin_unlock(&dum->lock);
value = dum->driver->setup(&dum->gadget,
&setup);
spin_lock(&dum->lock);
--dum->callback_usage;
if (value >= 0) { /* no delays (max 64KB data stage) */
limit = 64*1024; goto treat_control_like_bulk;
} /* error, see below */
}
if (value < 0) { if (value != -EOPNOTSUPP)
dev_dbg(udc_dev(dum), "setup --> %d\n",
value);
status = -EPIPE;
urb->actual_length = 0;
}
goto return_urb;
}
/* non-control requests */
limit = total; switch (usb_pipetype(urb->pipe)) { case PIPE_ISOCHRONOUS: /* * We don't support isochronous. But if we did, * here are some of the issues we'd have to face: * * Is it urb->interval since the last xfer? * Use urb->iso_frame_desc[i]. * Complete whether or not ep has requests queued. * Report random errors, to debug drivers.
*/
limit = max(limit, periodic_bytes(dum, ep));
status = -EINVAL; /* fail all xfers */ break;
case PIPE_INTERRUPT: /* FIXME is it urb->interval since the last xfer? * this almost certainly polls too fast.
*/
limit = max(limit, periodic_bytes(dum, ep));
fallthrough;
case GetHubStatus:
*(__le32 *) buf = cpu_to_le32(0); break; case GetPortStatus: if (wIndex != 1)
retval = -EPIPE;
/* whoever resets or resumes must GetPortStatus to * complete it!!
*/ if (dum_hcd->resuming &&
time_after_eq(jiffies, dum_hcd->re_timeout)) {
dum_hcd->port_status |= (USB_PORT_STAT_C_SUSPEND << 16);
dum_hcd->port_status &= ~USB_PORT_STAT_SUSPEND;
} if ((dum_hcd->port_status & USB_PORT_STAT_RESET) != 0 &&
time_after_eq(jiffies, dum_hcd->re_timeout)) {
dum_hcd->port_status |= (USB_PORT_STAT_C_RESET << 16);
dum_hcd->port_status &= ~USB_PORT_STAT_RESET; if (dum_hcd->dum->pullup) {
dum_hcd->port_status |= USB_PORT_STAT_ENABLE;
if (hcd->speed < HCD_USB3) { switch (dum_hcd->dum->gadget.speed) { case USB_SPEED_HIGH:
dum_hcd->port_status |=
USB_PORT_STAT_HIGH_SPEED; break; case USB_SPEED_LOW:
dum_hcd->dum->gadget.ep0->
maxpacket = 8;
dum_hcd->port_status |=
USB_PORT_STAT_LOW_SPEED; break; default: break;
}
}
}
}
set_link_state(dum_hcd);
((__le16 *) buf)[0] = cpu_to_le16(dum_hcd->port_status);
((__le16 *) buf)[1] = cpu_to_le16(dum_hcd->port_status >> 16); break; case SetHubFeature:
retval = -EPIPE; break; case SetPortFeature: switch (wValue) { case USB_PORT_FEAT_LINK_STATE: if (hcd->speed != HCD_USB3) {
dev_dbg(dummy_dev(dum_hcd), "USB_PORT_FEAT_LINK_STATE req not " "supported for USB 2.0 roothub\n"); goto error;
} /* * Since this is dummy we don't have an actual link so * there is nothing to do for the SET_LINK_STATE cmd
*/ break; case USB_PORT_FEAT_U1_TIMEOUT: case USB_PORT_FEAT_U2_TIMEOUT: /* TODO: add suspend/resume support! */ if (hcd->speed != HCD_USB3) {
dev_dbg(dummy_dev(dum_hcd), "USB_PORT_FEAT_U1/2_TIMEOUT req not " "supported for USB 2.0 roothub\n"); goto error;
} break; case USB_PORT_FEAT_SUSPEND: /* Applicable only for USB2.0 hub */ if (hcd->speed == HCD_USB3) {
dev_dbg(dummy_dev(dum_hcd), "USB_PORT_FEAT_SUSPEND req not " "supported for USB 3.0 roothub\n"); goto error;
} if (dum_hcd->active) {
dum_hcd->port_status |= USB_PORT_STAT_SUSPEND;
/* HNP would happen here; for now we * assume b_bus_req is always true.
*/
set_link_state(dum_hcd); if (((1 << USB_DEVICE_B_HNP_ENABLE)
& dum_hcd->dum->devstatus) != 0)
dev_dbg(dummy_dev(dum_hcd), "no HNP yet!\n");
} break; case USB_PORT_FEAT_POWER: if (hcd->speed == HCD_USB3)
dum_hcd->port_status |= USB_SS_PORT_STAT_POWER; else
dum_hcd->port_status |= USB_PORT_STAT_POWER;
set_link_state(dum_hcd); break; case USB_PORT_FEAT_BH_PORT_RESET: /* Applicable only for USB3.0 hub */ if (hcd->speed != HCD_USB3) {
dev_dbg(dummy_dev(dum_hcd), "USB_PORT_FEAT_BH_PORT_RESET req not " "supported for USB 2.0 roothub\n"); goto error;
}
fallthrough; case USB_PORT_FEAT_RESET: if (!(dum_hcd->port_status & USB_PORT_STAT_CONNECTION)) break; /* if it's already enabled, disable */ if (hcd->speed == HCD_USB3) {
dum_hcd->port_status =
(USB_SS_PORT_STAT_POWER |
USB_PORT_STAT_CONNECTION |
USB_PORT_STAT_RESET);
} else {
dum_hcd->port_status &= ~(USB_PORT_STAT_ENABLE
| USB_PORT_STAT_LOW_SPEED
| USB_PORT_STAT_HIGH_SPEED);
dum_hcd->port_status |= USB_PORT_STAT_RESET;
} /* * We want to reset device status. All but the * Self powered feature
*/
dum_hcd->dum->devstatus &=
(1 << USB_DEVICE_SELF_POWERED); /* * FIXME USB3.0: what is the correct reset signaling * interval? Is it still 50msec as for HS?
*/
dum_hcd->re_timeout = jiffies + msecs_to_jiffies(50);
set_link_state(dum_hcd); break; case USB_PORT_FEAT_C_CONNECTION: case USB_PORT_FEAT_C_RESET: case USB_PORT_FEAT_C_ENABLE: case USB_PORT_FEAT_C_SUSPEND: /* Not allowed for USB-3, and ignored for USB-2 */ if (hcd->speed == HCD_USB3) goto error; break; default: /* Disallow TEST, INDICATOR, and C_OVER_CURRENT */ goto error;
} break; case GetPortErrorCount: if (hcd->speed != HCD_USB3) {
dev_dbg(dummy_dev(dum_hcd), "GetPortErrorCount req not " "supported for USB 2.0 roothub\n"); goto error;
} /* We'll always return 0 since this is a dummy hub */
*(__le32 *) buf = cpu_to_le32(0); break; case SetHubDepth: if (hcd->speed != HCD_USB3) {
dev_dbg(dummy_dev(dum_hcd), "SetHubDepth req not supported for " "USB 2.0 roothub\n"); goto error;
} break; default:
dev_dbg(dummy_dev(dum_hcd), "hub control req%04x v%04x i%04x l%d\n",
typeReq, wValue, wIndex, wLength);
error: /* "protocol stall" on error */
retval = -EPIPE;
}
spin_unlock_irqrestore(&dum_hcd->dum->lock, flags);
if ((dum_hcd->port_status & PORT_C_MASK) != 0)
usb_hcd_poll_rh_status(hcd); return retval;
}
/* * HOST side init ... we emulate a root hub that'll only ever * talk to one device (the gadget side). Also appears in sysfs, * just like more familiar pci-based HCDs.
*/ if (!usb_hcd_is_primary_hcd(hcd)) return dummy_start_ss(dum_hcd);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.