/* Private per-URB data */ struct urb_priv { struct list_head list; struct urb *urb; int req_id; /* RING_REQUEST id for submitting */ int unlink_req_id; /* RING_REQUEST id for unlinking */ int status; bool unlinked; /* dequeued marker */
};
/* virtual roothub port status */ struct rhport_status {
__u32 status; bool resuming; /* in resuming */ bool c_connection; /* connection changed */ unsignedlong timeout;
};
/* status of attached device */ struct vdevice_status { int devnum; enum usb_device_state status; enum usb_device_speed speed;
};
#ifdef CONFIG_PM staticint xenhcd_bus_suspend(struct usb_hcd *hcd)
{ struct xenhcd_info *info = xenhcd_hcd_to_info(hcd); int ret = 0; int i, ports;
ports = info->rh_numports;
spin_lock_irq(&info->lock); if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) {
ret = -ESHUTDOWN;
} else { /* suspend any active ports*/ for (i = 1; i <= ports; i++)
xenhcd_rhport_suspend(info, i);
}
spin_unlock_irq(&info->lock);
timer_delete_sync(&info->watchdog);
return ret;
}
staticint xenhcd_bus_resume(struct usb_hcd *hcd)
{ struct xenhcd_info *info = xenhcd_hcd_to_info(hcd); int ret = 0; int i, ports;
ports = info->rh_numports;
spin_lock_irq(&info->lock); if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) {
ret = -ESHUTDOWN;
} else { /* resume any suspended ports*/ for (i = 1; i <= ports; i++)
xenhcd_rhport_resume(info, i);
}
spin_unlock_irq(&info->lock);
/* bitmaps for DeviceRemovable and PortPwrCtrlMask */
memset(&desc->u.hs.DeviceRemovable[0], 0, temp);
memset(&desc->u.hs.DeviceRemovable[temp], 0xff, temp);
/* per-port over current reporting and no power switching */
temp = 0x000a;
desc->wHubCharacteristics = cpu_to_le16(temp);
}
/* port status change mask for hub_status_data */ #define PORT_C_MASK ((USB_PORT_STAT_C_CONNECTION | \
USB_PORT_STAT_C_ENABLE | \
USB_PORT_STAT_C_SUSPEND | \
USB_PORT_STAT_C_OVERCURRENT | \
USB_PORT_STAT_C_RESET) << 16)
/* * See USB 2.0 Spec, 11.12.4 Hub and Port Status Change Bitmap. * If port status changed, writes the bitmap to buf and return * that length(number of bytes). * If Nothing changed, return 0.
*/ staticint xenhcd_hub_status_data(struct usb_hcd *hcd, char *buf)
{ struct xenhcd_info *info = xenhcd_hcd_to_info(hcd); int ports; int i; unsignedlong flags; int ret; int changed = 0;
/* initialize the status to no-changes */
ports = info->rh_numports;
ret = 1 + (ports / 8);
memset(buf, 0, ret);
spin_lock_irqsave(&info->lock, flags);
for (i = 0; i < ports; i++) { /* check status for each port */ if (info->ports[i].status & PORT_C_MASK) {
buf[(i + 1) / 8] |= 1 << (i + 1) % 8;
changed = 1;
}
}
if ((hcd->state == HC_STATE_SUSPENDED) && (changed == 1))
usb_hcd_resume_root_hub(hcd);
spin_unlock_irqrestore(&info->lock, flags);
return changed ? ret : 0;
}
staticint xenhcd_hub_control(struct usb_hcd *hcd, __u16 typeReq, __u16 wValue,
__u16 wIndex, char *buf, __u16 wLength)
{ struct xenhcd_info *info = xenhcd_hcd_to_info(hcd); int ports = info->rh_numports; unsignedlong flags; int ret = 0; int i; int changed = 0;
spin_lock_irqsave(&info->lock, flags); switch (typeReq) { case ClearHubFeature: /* ignore this request */ break; case ClearPortFeature: if (!wIndex || wIndex > ports) goto error;
switch (wValue) { case USB_PORT_FEAT_SUSPEND:
xenhcd_rhport_resume(info, wIndex); break; case USB_PORT_FEAT_POWER:
xenhcd_rhport_power_off(info, wIndex); break; case USB_PORT_FEAT_ENABLE:
xenhcd_rhport_disable(info, wIndex); break; case USB_PORT_FEAT_C_CONNECTION:
info->ports[wIndex - 1].c_connection = false;
fallthrough; default:
info->ports[wIndex - 1].status &= ~(1 << wValue); break;
} break; case GetHubDescriptor:
xenhcd_hub_descriptor(info, (struct usb_hub_descriptor *)buf); break; case GetHubStatus: /* always local power supply good and no over-current exists. */
*(__le32 *)buf = cpu_to_le32(0); break; case GetPortStatus: if (!wIndex || wIndex > ports) goto error;
switch (info->devices[wIndex].speed) { case XENUSB_SPEED_LOW:
info->ports[wIndex].status |=
USB_PORT_STAT_LOW_SPEED; break; case XENUSB_SPEED_HIGH:
info->ports[wIndex].status |=
USB_PORT_STAT_HIGH_SPEED; break; default: break;
}
}
*(__le32 *)buf = cpu_to_le32(info->ports[wIndex].status); break; case SetPortFeature: if (!wIndex || wIndex > ports) goto error;
switch (wValue) { case USB_PORT_FEAT_POWER:
xenhcd_rhport_power_on(info, wIndex); break; case USB_PORT_FEAT_RESET:
xenhcd_rhport_reset(info, wIndex); break; case USB_PORT_FEAT_SUSPEND:
xenhcd_rhport_suspend(info, wIndex); break; default: if (info->ports[wIndex-1].status & USB_PORT_STAT_POWER)
info->ports[wIndex-1].status |= (1 << wValue);
} break;
case SetHubFeature: /* not supported */ default:
error:
ret = -EPIPE;
}
spin_unlock_irqrestore(&info->lock, flags);
/* check status for each port */ for (i = 0; i < ports; i++) { if (info->ports[i].status & PORT_C_MASK)
changed = 1;
} if (changed)
usb_hcd_poll_rh_status(hcd);
staticint xenhcd_submit_urb(struct xenhcd_info *info, struct urb_priv *urbp)
{ int ret;
if (RING_FULL(&info->urb_ring)) {
list_add_tail(&urbp->list, &info->pending_submit_list);
xenhcd_timer_action(info, TIMER_RING_WATCHDOG); return 0;
}
if (!list_empty(&info->pending_submit_list)) {
list_add_tail(&urbp->list, &info->pending_submit_list);
xenhcd_timer_action(info, TIMER_SCAN_PENDING_URBS); return 0;
}
ret = xenhcd_do_request(info, urbp); if (ret == 0)
list_add_tail(&urbp->list, &info->in_progress_list);
return ret;
}
staticint xenhcd_unlink_urb(struct xenhcd_info *info, struct urb_priv *urbp)
{ int ret;
/* already unlinked? */ if (urbp->unlinked) return -EBUSY;
urbp->unlinked = true;
/* the urb is still in pending_submit queue */ if (urbp->req_id == ~0) {
list_move_tail(&urbp->list, &info->giveback_waiting_list);
xenhcd_timer_action(info, TIMER_SCAN_PENDING_URBS); return 0;
}
/* send unlink request to backend */ if (RING_FULL(&info->urb_ring)) {
list_move_tail(&urbp->list, &info->pending_unlink_list);
xenhcd_timer_action(info, TIMER_RING_WATCHDOG); return 0;
}
if (!list_empty(&info->pending_unlink_list)) {
list_move_tail(&urbp->list, &info->pending_unlink_list);
xenhcd_timer_action(info, TIMER_SCAN_PENDING_URBS); return 0;
}
ret = xenhcd_do_request(info, urbp); if (ret == 0)
list_move_tail(&urbp->list, &info->in_progress_list);
rp = info->urb_ring.sring->rsp_prod; if (RING_RESPONSE_PROD_OVERFLOW(&info->urb_ring, rp)) {
xenhcd_set_error(info, "Illegal index on urb-ring"); goto err;
}
rmb(); /* ensure we see queued responses up to "rp" */
for (i = info->urb_ring.rsp_cons; i != rp; i++) {
RING_COPY_RESPONSE(&info->urb_ring, i, &res);
id = res.id; if (id >= XENUSB_URB_RING_SIZE) {
xenhcd_set_error(info, "Illegal data on urb-ring"); goto err;
}
if (likely(xenusb_pipesubmit(info->shadow[id].req.pipe))) {
xenhcd_gnttab_done(info, id); if (info->error) goto err;
xenhcd_res_to_urb(info, &res, info->shadow[id].urb);
}
xenhcd_add_id_to_freelist(info, id);
*eoiflag = 0;
}
info->urb_ring.rsp_cons = i;
if (i != info->urb_ring.req_prod_pvt)
RING_FINAL_CHECK_FOR_RESPONSES(&info->urb_ring, more_to_do); else
info->urb_ring.sring->rsp_event = i + 1;
rc = info->conn_ring.rsp_cons;
rp = info->conn_ring.sring->rsp_prod; if (RING_RESPONSE_PROD_OVERFLOW(&info->conn_ring, rp)) {
xenhcd_set_error(info, "Illegal index on conn-ring");
spin_unlock_irqrestore(&info->lock, flags); return 0;
}
rmb(); /* ensure we see queued responses up to "rp" */
while (rc != rp) {
RING_COPY_RESPONSE(&info->conn_ring, rc, &res);
id = res.id;
portnum = res.portnum;
speed = res.speed;
info->conn_ring.rsp_cons = ++rc;
if (xenhcd_rhport_connect(info, portnum, speed)) {
xenhcd_set_error(info, "Illegal data on conn-ring");
spin_unlock_irqrestore(&info->lock, flags); return 0;
}
if (info->ports[portnum - 1].c_connection)
port_changed = 1;
/* * called as .urb_dequeue()
*/ staticint xenhcd_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
{ struct xenhcd_info *info = xenhcd_hcd_to_info(hcd); struct urb_priv *urbp; unsignedlong flags; int ret = 0;
spin_lock_irqsave(&info->lock, flags);
urbp = urb->hcpriv; if (urbp) {
urbp->status = status;
ret = xenhcd_unlink_urb(info, urbp);
}
spin_unlock_irqrestore(&info->lock, flags);
return ret;
}
/* * called from usb_get_current_frame_number(), * but, almost all drivers not use such function.
*/ staticint xenhcd_get_frame(struct usb_hcd *hcd)
{ /* it means error, but probably no problem :-) */ return 0;
}
err = xenbus_scanf(XBT_NIL, dev->otherend, "usb-ver", "%d", &usb_ver); if (err != 1) {
xenbus_dev_fatal(dev, err, "reading usb-ver"); return ERR_PTR(-EINVAL);
} switch (usb_ver) { case XENUSB_VER_USB11:
hcd = usb_create_hcd(&xenhcd_usb11_hc_driver, &dev->dev,
dev_name(&dev->dev)); break; case XENUSB_VER_USB20:
hcd = usb_create_hcd(&xenhcd_usb20_hc_driver, &dev->dev,
dev_name(&dev->dev)); break; default:
xenbus_dev_fatal(dev, err, "invalid usb-ver"); return ERR_PTR(-EINVAL);
} if (!hcd) {
xenbus_dev_fatal(dev, err, "fail to allocate USB host controller"); return ERR_PTR(-ENOMEM);
}
info = xenhcd_hcd_to_info(hcd);
info->xbdev = dev;
info->rh_numports = num_ports;
for (i = 0; i < XENUSB_URB_RING_SIZE; i++) {
info->shadow[i].req.id = i + 1;
info->shadow[i].urb = NULL;
info->shadow[i].in_flight = false;
}
info->shadow[XENUSB_URB_RING_SIZE - 1].req.id = 0x0fff;
return hcd;
}
staticvoid xenhcd_backend_changed(struct xenbus_device *dev, enum xenbus_state backend_state)
{ switch (backend_state) { case XenbusStateInitialising: case XenbusStateReconfiguring: case XenbusStateReconfigured: case XenbusStateUnknown: break;
case XenbusStateInitWait: case XenbusStateInitialised: case XenbusStateConnected: if (dev->state != XenbusStateInitialising) break; if (!xenhcd_connect(dev))
xenbus_switch_state(dev, XenbusStateConnected); break;
case XenbusStateClosed: if (dev->state == XenbusStateClosed) break;
fallthrough; /* Missed the backend's Closing state. */ case XenbusStateClosing:
xenhcd_disconnect(dev); break;
default:
xenbus_dev_fatal(dev, -EINVAL, "saw state %d at frontend",
backend_state); break;
}
}
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.