status |= USB_PORT_STAT_CONNECTION | (1 << USB_PORT_FEAT_C_CONNECTION);
switch (speed) { case USB_SPEED_HIGH:
status |= USB_PORT_STAT_HIGH_SPEED; break; case USB_SPEED_LOW:
status |= USB_PORT_STAT_LOW_SPEED; break; default: break;
}
/* * Returns 0 if the status hasn't changed, or the number of bytes in buf. * Ports are 0-indexed from the HCD point of view, * and 1-indexed from the USB core pointer of view. * * @buf: a bitmap to show which port status has been changed. * bit 0: reserved * bit 1: the status of port 0 has been changed. * bit 2: the status of port 1 has been changed. * ...
*/ staticint vhci_hub_status(struct usb_hcd *hcd, char *buf)
{ struct vhci_hcd *vhci_hcd = hcd_to_vhci_hcd(hcd); struct vhci *vhci = vhci_hcd->vhci; int retval = DIV_ROUND_UP(VHCI_HC_PORTS + 1, 8); int rhport; int changed = 0; unsignedlong flags;
memset(buf, 0, retval);
spin_lock_irqsave(&vhci->lock, flags); if (!HCD_HW_ACCESSIBLE(hcd)) {
usbip_dbg_vhci_rh("hw accessible flag not on?\n"); goto done;
}
/* check pseudo status register for each port */ for (rhport = 0; rhport < VHCI_HC_PORTS; rhport++) { if ((vhci_hcd->port_status[rhport] & PORT_C_MASK)) { /* The status of a port has been changed, */
usbip_dbg_vhci_rh("port %d status changed\n", rhport);
/* * NOTE: * wIndex (bits 0-7) shows the port number and begins from 1?
*/
wIndex = ((__u8)(wIndex & 0x00ff));
usbip_dbg_vhci_rh("typeReq %x wValue %x wIndex %x\n", typeReq, wValue,
wIndex);
/* * wIndex can be 0 for some request types (typeReq). rhport is * in valid range when wIndex >= 1 and < VHCI_HC_PORTS. * * Reference port_status[] only with valid rhport when * invalid_rhport is false.
*/ if (wIndex < 1 || wIndex > VHCI_HC_PORTS) {
invalid_rhport = true; if (wIndex > VHCI_HC_PORTS)
pr_err("invalid port number %d\n", wIndex);
} else
rhport = wIndex - 1;
/* store old status and compare now and old later */ if (usbip_dbg_flag_vhci_rh) { if (!invalid_rhport)
memcpy(prev_port_status, vhci_hcd->port_status, sizeof(prev_port_status));
}
switch (typeReq) { case ClearHubFeature:
usbip_dbg_vhci_rh(" ClearHubFeature\n"); break; case ClearPortFeature: if (invalid_rhport) {
pr_err("invalid port number %d\n", wIndex); goto error;
} switch (wValue) { case USB_PORT_FEAT_SUSPEND: if (hcd->speed >= HCD_USB3) {
pr_err(" ClearPortFeature: USB_PORT_FEAT_SUSPEND req not " "supported for USB 3.0 roothub\n"); goto error;
}
usbip_dbg_vhci_rh( " ClearPortFeature: USB_PORT_FEAT_SUSPEND\n"); if (vhci_hcd->port_status[rhport] & USB_PORT_STAT_SUSPEND) { /* 20msec signaling */
vhci_hcd->resuming = 1;
vhci_hcd->re_timeout = jiffies + msecs_to_jiffies(20);
} break; case USB_PORT_FEAT_POWER:
usbip_dbg_vhci_rh( " ClearPortFeature: USB_PORT_FEAT_POWER\n"); if (hcd->speed >= HCD_USB3)
vhci_hcd->port_status[rhport] &= ~USB_SS_PORT_STAT_POWER; else
vhci_hcd->port_status[rhport] &= ~USB_PORT_STAT_POWER; break; default:
usbip_dbg_vhci_rh(" ClearPortFeature: default %x\n",
wValue); if (wValue >= 32) goto error;
vhci_hcd->port_status[rhport] &= ~(1 << wValue); break;
} break; case GetHubDescriptor:
usbip_dbg_vhci_rh(" GetHubDescriptor\n"); if (hcd->speed >= HCD_USB3 &&
(wLength < USB_DT_SS_HUB_SIZE ||
wValue != (USB_DT_SS_HUB << 8))) {
pr_err("Wrong hub descriptor type for USB 3.0 roothub.\n"); goto error;
} if (hcd->speed >= HCD_USB3)
ss_hub_descriptor((struct usb_hub_descriptor *) buf); else
hub_descriptor((struct usb_hub_descriptor *) buf); break; case DeviceRequest | USB_REQ_GET_DESCRIPTOR: if (hcd->speed < HCD_USB3) goto error;
if ((wValue >> 8) != USB_DT_BOS) goto error;
memcpy(buf, &usb3_bos_desc, sizeof(usb3_bos_desc));
retval = sizeof(usb3_bos_desc); break; case GetHubStatus:
usbip_dbg_vhci_rh(" GetHubStatus\n");
*(__le32 *) buf = cpu_to_le32(0); break; case GetPortStatus:
usbip_dbg_vhci_rh(" GetPortStatus port %x\n", wIndex); if (invalid_rhport) {
pr_err("invalid port number %d\n", wIndex);
retval = -EPIPE; goto error;
}
/* we do not care about resume. */
/* whoever resets or resumes must GetPortStatus to * complete it!!
*/ if (vhci_hcd->resuming && time_after(jiffies, vhci_hcd->re_timeout)) {
vhci_hcd->port_status[rhport] |= (1 << USB_PORT_FEAT_C_SUSPEND);
vhci_hcd->port_status[rhport] &= ~(1 << USB_PORT_FEAT_SUSPEND);
vhci_hcd->resuming = 0;
vhci_hcd->re_timeout = 0;
}
/* * A few drivers do usb reset during probe when * the device could be in VDEV_ST_USED state
*/ if (vhci_hcd->vdev[rhport].ud.status ==
VDEV_ST_NOTASSIGNED ||
vhci_hcd->vdev[rhport].ud.status ==
VDEV_ST_USED) {
usbip_dbg_vhci_rh( " enable rhport %d (status %u)\n",
rhport,
vhci_hcd->vdev[rhport].ud.status);
vhci_hcd->port_status[rhport] |=
USB_PORT_STAT_ENABLE;
}
usbip_dbg_vhci_rh(" GetPortStatus bye %x %x\n", ((u16 *)buf)[0],
((u16 *)buf)[1]); break; case SetHubFeature:
usbip_dbg_vhci_rh(" SetHubFeature\n");
retval = -EPIPE; break; case SetPortFeature: switch (wValue) { case USB_PORT_FEAT_LINK_STATE:
usbip_dbg_vhci_rh( " SetPortFeature: USB_PORT_FEAT_LINK_STATE\n"); if (hcd->speed < HCD_USB3) {
pr_err("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:
usbip_dbg_vhci_rh( " SetPortFeature: USB_PORT_FEAT_U1_TIMEOUT\n");
fallthrough; case USB_PORT_FEAT_U2_TIMEOUT:
usbip_dbg_vhci_rh( " SetPortFeature: USB_PORT_FEAT_U2_TIMEOUT\n"); /* TODO: add suspend/resume support! */ if (hcd->speed < HCD_USB3) {
pr_err("USB_PORT_FEAT_U1/2_TIMEOUT req not " "supported for USB 2.0 roothub\n"); goto error;
} break; case USB_PORT_FEAT_SUSPEND:
usbip_dbg_vhci_rh( " SetPortFeature: USB_PORT_FEAT_SUSPEND\n"); /* Applicable only for USB2.0 hub */ if (hcd->speed >= HCD_USB3) {
pr_err("USB_PORT_FEAT_SUSPEND req not " "supported for USB 3.0 roothub\n"); goto error;
}
if (invalid_rhport) {
pr_err("invalid port number %d\n", wIndex); goto error;
}
vhci_hcd->port_status[rhport] |= USB_PORT_STAT_SUSPEND; break; case USB_PORT_FEAT_POWER:
usbip_dbg_vhci_rh( " SetPortFeature: USB_PORT_FEAT_POWER\n"); if (invalid_rhport) {
pr_err("invalid port number %d\n", wIndex); goto error;
} if (hcd->speed >= HCD_USB3)
vhci_hcd->port_status[rhport] |= USB_SS_PORT_STAT_POWER; else
vhci_hcd->port_status[rhport] |= USB_PORT_STAT_POWER; break; case USB_PORT_FEAT_BH_PORT_RESET:
usbip_dbg_vhci_rh( " SetPortFeature: USB_PORT_FEAT_BH_PORT_RESET\n"); if (invalid_rhport) {
pr_err("invalid port number %d\n", wIndex); goto error;
} /* Applicable only for USB3.0 hub */ if (hcd->speed < HCD_USB3) {
pr_err("USB_PORT_FEAT_BH_PORT_RESET req not " "supported for USB 2.0 roothub\n"); goto error;
}
fallthrough; case USB_PORT_FEAT_RESET:
usbip_dbg_vhci_rh( " SetPortFeature: USB_PORT_FEAT_RESET\n"); if (invalid_rhport) {
pr_err("invalid port number %d\n", wIndex); goto error;
} /* if it's already enabled, disable */ if (hcd->speed >= HCD_USB3) {
vhci_hcd->port_status[rhport] = 0;
vhci_hcd->port_status[rhport] =
(USB_SS_PORT_STAT_POWER |
USB_PORT_STAT_CONNECTION |
USB_PORT_STAT_RESET);
} elseif (vhci_hcd->port_status[rhport] & USB_PORT_STAT_ENABLE) {
vhci_hcd->port_status[rhport] &= ~(USB_PORT_STAT_ENABLE
| USB_PORT_STAT_LOW_SPEED
| USB_PORT_STAT_HIGH_SPEED);
}
/* 50msec reset signaling */
vhci_hcd->re_timeout = jiffies + msecs_to_jiffies(50);
fallthrough; default:
usbip_dbg_vhci_rh(" SetPortFeature: default %d\n",
wValue); if (invalid_rhport) {
pr_err("invalid port number %d\n", wIndex); goto error;
} if (wValue >= 32) goto error; if (hcd->speed >= HCD_USB3) { if ((vhci_hcd->port_status[rhport] &
USB_SS_PORT_STAT_POWER) != 0) {
vhci_hcd->port_status[rhport] |= (1 << wValue);
}
} else if ((vhci_hcd->port_status[rhport] &
USB_PORT_STAT_POWER) != 0) {
vhci_hcd->port_status[rhport] |= (1 << wValue);
}
} break; case GetPortErrorCount:
usbip_dbg_vhci_rh(" GetPortErrorCount\n"); if (hcd->speed < HCD_USB3) {
pr_err("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:
usbip_dbg_vhci_rh(" SetHubDepth\n"); if (hcd->speed < HCD_USB3) {
pr_err("SetHubDepth req not supported for " "USB 2.0 roothub\n"); goto error;
} break; default:
pr_err("default hub control req: %04x v%04x i%04x l%d\n",
typeReq, wValue, wIndex, wLength);
error: /* "protocol stall" on error */
retval = -EPIPE;
}
if (usbip_dbg_flag_vhci_rh) {
pr_debug("port %d\n", rhport); /* Only dump valid port status */ if (!invalid_rhport) {
dump_port_status_diff(prev_port_status[rhport],
vhci_hcd->port_status[rhport],
hcd->speed >= HCD_USB3);
}
}
usbip_dbg_vhci_rh(" bye\n");
if (portnum > VHCI_HC_PORTS) {
pr_err("invalid port number %d\n", portnum); return -ENODEV;
}
vdev = &vhci_hcd->vdev[portnum-1];
if (!urb->transfer_buffer && !urb->num_sgs &&
urb->transfer_buffer_length) {
dev_dbg(dev, "Null URB transfer buffer\n"); return -EINVAL;
}
spin_lock_irqsave(&vhci->lock, flags);
if (urb->status != -EINPROGRESS) {
dev_err(dev, "URB already unlinked!, status %d\n", urb->status);
spin_unlock_irqrestore(&vhci->lock, flags); return urb->status;
}
/* refuse enqueue for dead connection */
spin_lock(&vdev->ud.lock); if (vdev->ud.status == VDEV_ST_NULL ||
vdev->ud.status == VDEV_ST_ERROR) {
dev_err(dev, "enqueue for inactive port %d\n", vdev->rhport);
spin_unlock(&vdev->ud.lock);
spin_unlock_irqrestore(&vhci->lock, flags); return -ENODEV;
}
spin_unlock(&vdev->ud.lock);
ret = usb_hcd_link_urb_to_ep(hcd, urb); if (ret) goto no_need_unlink;
/* * The enumeration process is as follows; * * 1. Get_Descriptor request to DevAddrs(0) EndPoint(0) * to get max packet length of default pipe * * 2. Set_Address request to DevAddr(0) EndPoint(0) *
*/ if (usb_pipedevice(urb->pipe) == 0) { struct usb_device *old;
__u8 type = usb_pipetype(urb->pipe); struct usb_ctrlrequest *ctrlreq =
(struct usb_ctrlrequest *) urb->setup_packet;
if (type != PIPE_CONTROL || !ctrlreq) {
dev_err(dev, "invalid request to devnum 0\n");
ret = -EINVAL; goto no_need_xmit;
}
old = vdev->udev; switch (ctrlreq->bRequest) { case USB_REQ_SET_ADDRESS: /* set_address may come when a device is reset */
dev_info(dev, "SetAddress Request (%d) to port %d\n",
ctrlreq->wValue, vdev->rhport);
vdev->udev = usb_get_dev(urb->dev); /* * NOTE: A similar operation has been done via * USB_REQ_GET_DESCRIPTOR handler below, which is * supposed to always precede USB_REQ_SET_ADDRESS. * * It's not entirely clear if operating on a different * usb_device instance here is a real possibility, * otherwise this call and vdev->udev assignment above * should be dropped.
*/
dev_pm_syscore_device(&vdev->udev->dev, true);
usb_put_dev(old);
if (urb->status == -EINPROGRESS) { /* This request is successfully completed. */ /* If not -EINPROGRESS, possibly unlinked. */
urb->status = 0;
}
goto no_need_xmit;
case USB_REQ_GET_DESCRIPTOR: if (ctrlreq->wValue == cpu_to_le16(USB_DT_DEVICE << 8))
usbip_dbg_vhci_hc( "Not yet?:Get_Descriptor to device 0 (get max pipe size)\n");
vdev->udev = usb_get_dev(urb->dev); /* * Set syscore PM flag for the virtually attached * devices to ensure they will not enter suspend on * the client side. * * Note this doesn't have any impact on the physical * devices attached to the host system on the server * side, hence there is no need to undo the operation * on disconnect.
*/
dev_pm_syscore_device(&vdev->udev->dev, true);
usb_put_dev(old); goto out;
default: /* NOT REACHED */
dev_err(dev, "invalid request to devnum 0 bRequest %u, wValue %u\n",
ctrlreq->bRequest,
ctrlreq->wValue);
ret = -EINVAL; goto no_need_xmit;
}
no_need_xmit:
usb_hcd_unlink_urb_from_ep(hcd, urb);
no_need_unlink:
spin_unlock_irqrestore(&vhci->lock, flags); if (!ret) { /* usb_hcd_giveback_urb() should be called with * irqs disabled
*/
local_irq_disable();
usb_hcd_giveback_urb(hcd, urb, urb->status);
local_irq_enable();
} return ret;
}
/* * vhci_rx gives back the urb after receiving the reply of the urb. If an * unlink pdu is sent or not, vhci_rx receives a normal return pdu and gives * back its urb. For the driver unlinking the urb, the content of the urb is * not important, but the calling to its completion handler is important; the * completion of unlinking is notified by the completion handler. * * * CLIENT SIDE * * - When vhci_hcd receives RET_SUBMIT, * * - case 1a). the urb of the pdu is not unlinking. * - normal case * => just give back the urb * * - case 1b). the urb of the pdu is unlinking. * - usbip.ko will return a reply of the unlinking request. * => give back the urb now and go to case 2b). * * - When vhci_hcd receives RET_UNLINK, * * - case 2a). a submit request is still pending in vhci_hcd. * - urb was really pending in usbip.ko and urb_unlink_urb() was * completed there. * => free a pending submit request * => notify unlink completeness by giving back the urb * * - case 2b). a submit request is *not* pending in vhci_hcd. * - urb was already given back to the core driver. * => do not give back the urb * * * SERVER SIDE * * - When usbip receives CMD_UNLINK, * * - case 3a). the urb of the unlink request is now in submission. * => do usb_unlink_urb(). * => after the unlink is completed, send RET_UNLINK. * * - case 3b). the urb of the unlink request is not in submission. * - may be already completed or never be received * => send RET_UNLINK *
*/ staticint vhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
{ struct vhci_hcd *vhci_hcd = hcd_to_vhci_hcd(hcd); struct vhci *vhci = vhci_hcd->vhci; struct vhci_priv *priv; struct vhci_device *vdev; unsignedlong flags;
spin_lock_irqsave(&vhci->lock, flags);
priv = urb->hcpriv; if (!priv) { /* URB was never linked! or will be soon given back by
* vhci_rx. */
spin_unlock_irqrestore(&vhci->lock, flags); return -EIDRM;
}
{ int ret = 0;
ret = usb_hcd_check_unlink_urb(hcd, urb, status); if (ret) {
spin_unlock_irqrestore(&vhci->lock, flags); return ret;
}
}
/* * If tcp connection is alive, we have sent CMD_UNLINK. * vhci_rx will receive RET_UNLINK and give back the URB. * Otherwise, we give back it here.
*/
usb_hcd_unlink_urb_from_ep(hcd, urb);
staticvoid vhci_device_unlink_cleanup(struct vhci_device *vdev)
{ /* give back URB of unsent unlink request */
vhci_cleanup_unlink_list(vdev, &vdev->unlink_tx);
/* give back URB of unanswered unlink request */
vhci_cleanup_unlink_list(vdev, &vdev->unlink_rx);
}
/* * The important thing is that only one context begins cleanup. * This is why error handling and cleanup become simple. * We do not want to consider race condition as possible.
*/ staticvoid vhci_shutdown_connection(struct usbip_device *ud)
{ struct vhci_device *vdev = container_of(ud, struct vhci_device, ud);
/* need this? see stub_dev.c */ if (ud->tcp_socket) {
pr_debug("shutdown tcp_socket %d\n", ud->sockfd);
kernel_sock_shutdown(ud->tcp_socket, SHUT_RDWR);
}
/* kill threads related to this sdev */ if (vdev->ud.tcp_rx) {
kthread_stop_put(vdev->ud.tcp_rx);
vdev->ud.tcp_rx = NULL;
} if (vdev->ud.tcp_tx) {
kthread_stop_put(vdev->ud.tcp_tx);
vdev->ud.tcp_tx = NULL;
}
pr_info("stop threads\n");
/* active connection is closed */ if (vdev->ud.tcp_socket) {
sockfd_put(vdev->ud.tcp_socket);
vdev->ud.tcp_socket = NULL;
vdev->ud.sockfd = -1;
}
pr_info("release socket\n");
vhci_device_unlink_cleanup(vdev);
/* * rh_port_disconnect() is a trigger of ... * usb_disable_device(): * disable all the endpoints for a USB device. * usb_disable_endpoint(): * disable endpoints. pending urbs are unlinked(dequeued). * * NOTE: After calling rh_port_disconnect(), the USB device drivers of a * detached device should release used urbs in a cleanup function (i.e. * xxx_disconnect()). Therefore, vhci_hcd does not need to release * pushed urbs and their private data in this function. * * NOTE: vhci_dequeue() must be considered carefully. When shutting down * a connection, vhci_shutdown_connection() expects vhci_dequeue() * gives back pushed urbs and frees their private data by request of * the cleanup function of a USB driver. When unlinking a urb with an * active connection, vhci_dequeue() does not give back the urb which * is actually given back by vhci_rx after receiving its return pdu. *
*/
rh_port_disconnect(vdev);
if (usb_hcd_is_primary_hcd(hcd)) {
vhci->vhci_hcd_hs = hcd_to_vhci_hcd(hcd);
vhci->vhci_hcd_hs->vhci = vhci; /* * Mark the first roothub as being USB 2.0. * The USB 3.0 roothub will be registered later by * vhci_hcd_probe()
*/
hcd->speed = HCD_USB2;
hcd->self.root_hub->speed = USB_SPEED_HIGH;
} else {
vhci->vhci_hcd_ss = hcd_to_vhci_hcd(hcd);
vhci->vhci_hcd_ss->vhci = vhci;
hcd->speed = HCD_USB31;
hcd->self.root_hub->speed = USB_SPEED_SUPER_PLUS;
}
/* 1. remove the userland interface of vhci_hcd */
id = hcd_name_to_id(hcd_name(hcd)); if (id == 0 && usb_hcd_is_primary_hcd(hcd)) {
sysfs_remove_group(&hcd_dev(hcd)->kobj, &vhci_attr_group);
vhci_finish_attr_group();
}
/* 2. shutdown all the ports of vhci_hcd */ for (rhport = 0; rhport < VHCI_HC_PORTS; rhport++) { struct vhci_device *vdev = &vhci_hcd->vdev[rhport];
/* Change a group of bulk endpoints to support multiple stream IDs */ staticint vhci_alloc_streams(struct usb_hcd *hcd, struct usb_device *udev, struct usb_host_endpoint **eps, unsignedint num_eps, unsignedint num_streams, gfp_t mem_flags)
{
dev_dbg(&hcd->self.root_hub->dev, "vhci_alloc_streams not implemented\n"); return 0;
}
/* Reverts a group of bulk endpoints back to not using stream IDs. */ staticint vhci_free_streams(struct usb_hcd *hcd, struct usb_device *udev, struct usb_host_endpoint **eps, unsignedint num_eps,
gfp_t mem_flags)
{
dev_dbg(&hcd->self.root_hub->dev, "vhci_free_streams not implemented\n"); return 0;
}
/* * Disconnects the root hub, * then reverses the effects of usb_add_hcd(), * invoking the HCD's stop() methods.
*/
usb_remove_hcd(vhci_hcd_to_hcd(vhci->vhci_hcd_ss));
usb_put_hcd(vhci_hcd_to_hcd(vhci->vhci_hcd_ss));
/* what should happen for USB/IP under suspend/resume? */ staticint vhci_hcd_suspend(struct platform_device *pdev, pm_message_t state)
{ struct usb_hcd *hcd; struct vhci *vhci; int rhport; int connected = 0; int ret = 0; unsignedlong flags;
dev_dbg(&pdev->dev, "%s\n", __func__);
hcd = platform_get_drvdata(pdev); if (!hcd) return 0;
for (rhport = 0; rhport < VHCI_HC_PORTS; rhport++) { if (vhci->vhci_hcd_hs->port_status[rhport] &
USB_PORT_STAT_CONNECTION)
connected += 1;
if (vhci->vhci_hcd_ss->port_status[rhport] &
USB_PORT_STAT_CONNECTION)
connected += 1;
}
spin_unlock_irqrestore(&vhci->lock, flags);
if (connected > 0) {
dev_info(&pdev->dev, "We have %d active connection%s. Do not suspend.\n",
connected, str_plural(connected));
ret = -EBUSY;
} else {
dev_info(&pdev->dev, "suspend vhci_hcd");
clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
}
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.