?? vhci_hcd.c
字號:
vdev->udev = urb->dev; goto out; default: /* NOT REACHED */ uerr("invalid request to devnum 0 bRequest %u, wValue %u\n", ctrlreq->bRequest, ctrlreq->wValue); ret = -EINVAL; goto no_need_xmit; } }out: vhci_tx_urb(urb); spin_unlock_irqrestore(&the_controller->lock, flags); return 0;no_need_xmit: spin_unlock_irqrestore(&the_controller->lock, flags);#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19) usb_hcd_giveback_urb(vhci_to_hcd(the_controller), urb);#else usb_hcd_giveback_urb(vhci_to_hcd(the_controller), urb, NULL);#endif return 0;}/* * 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 * */static int vhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb){ unsigned long flags; struct vhci_priv *priv; struct vhci_device *vdev; uinfo("vhci_hcd: dequeue a urb %p\n", urb); spin_lock_irqsave(&the_controller->lock, flags); priv = urb->hcpriv; if (!priv) { /* URB was never linked! or will be soon given back by vhci_rx. */ spin_unlock_irqrestore(&the_controller->lock, flags); return 0; } /* send unlink request here? */ vdev = priv->vdev; if (!vdev->ud.tcp_socket) { /* tcp connection is closed */ unsigned long flags2; spin_lock_irqsave(&vdev->priv_lock, flags2); uinfo("vhci_hcd: device %p seems to be disconnected\n", vdev); list_del(&priv->list); kfree(priv); urb->hcpriv = NULL; spin_unlock_irqrestore(&vdev->priv_lock, flags2); } else { /* tcp connection is alive */ unsigned long flags2; struct vhci_unlink *unlink; spin_lock_irqsave(&vdev->priv_lock, flags2); /* setup CMD_UNLINK pdu */ unlink = kzalloc(sizeof(struct vhci_unlink), GFP_ATOMIC); if (!unlink) { uerr("malloc vhci_unlink\n"); spin_unlock_irqrestore(&vdev->priv_lock, flags2); spin_unlock_irqrestore(&the_controller->lock, flags); usbip_event_add(&vdev->ud, VDEV_EVENT_ERROR_MALLOC); return -ENOMEM; } unlink->seqnum = atomic_inc_return(&the_controller->seqnum); if (unlink->seqnum == 0xffff) uinfo("seqnum max\n"); unlink->unlink_seqnum = priv->seqnum; uinfo("vhci_hcd: device %p seems to be still connected\n", vdev); /* send cmd_unlink and try to cancel the pending URB in the peer */ list_add_tail(&unlink->list, &vdev->unlink_tx); wake_up(&vdev->waitq_tx); spin_unlock_irqrestore(&vdev->priv_lock, flags2); } spin_unlock_irqrestore(&the_controller->lock, flags); /* * If tcp connection is alive, we have sent CMD_UNLINK. * vhci_rx will receive RET_UNLINK and giveb back the URB. * Otherwise, we give back it here. */ if (!vdev->ud.tcp_socket) { /* tcp connection is closed */ uinfo("vhci_hcd: vhci_urb_dequeue() gives back urb %p\n", urb);#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19) usb_hcd_giveback_urb(vhci_to_hcd(the_controller), urb);#else usb_hcd_giveback_urb(vhci_to_hcd(the_controller), urb, NULL);#endif } dbg_vhci_hc("leave\n"); return 0;}static void vhci_device_unlink_cleanup(struct vhci_device *vdev){ struct vhci_unlink *unlink, *tmp; spin_lock(&vdev->priv_lock); list_for_each_entry_safe(unlink, tmp, &vdev->unlink_tx, list) { list_del(&unlink->list); kfree(unlink); } list_for_each_entry_safe(unlink, tmp, &vdev->unlink_rx, list) { list_del(&unlink->list); kfree(unlink); } spin_unlock(&vdev->priv_lock);}/* * 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. */static void 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) { udbg("shutdown tcp_socket %p\n", ud->tcp_socket); ud->tcp_socket->ops->shutdown(ud->tcp_socket, 2); } usbip_stop_threads(&vdev->ud); uinfo("stop threads\n"); /* active connection is closed */ if (vdev->ud.tcp_socket != NULL) { sock_release(vdev->ud.tcp_socket); vdev->ud.tcp_socket = NULL; } uinfo("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 * deteched 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 shutdowning * 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->rhport); uinfo("disconnect device\n");}static void vhci_device_reset(struct usbip_device *ud){ struct vhci_device *vdev = container_of(ud, struct vhci_device, ud); spin_lock(&ud->lock); vdev->speed = 0; vdev->devid = 0; ud->tcp_socket = NULL; ud->status = VDEV_ST_NULL; spin_unlock(&ud->lock);}static void vhci_device_unusable(struct usbip_device* ud){ spin_lock(&ud->lock); ud->status = VDEV_ST_ERROR; spin_unlock(&ud->lock);}static void vhci_device_init(struct vhci_device *vdev){ memset(vdev, 0, sizeof(*vdev)); usbip_task_init(&vdev->ud.tcp_rx, "vhci_rx", vhci_rx_loop); usbip_task_init(&vdev->ud.tcp_tx, "vhci_tx", vhci_tx_loop); vdev->ud.side = USBIP_VHCI; vdev->ud.status = VDEV_ST_NULL; // vdev->ud.lock = SPIN_LOCK_UNLOCKED; spin_lock_init(&vdev->ud.lock); INIT_LIST_HEAD(&vdev->priv_rx); INIT_LIST_HEAD(&vdev->priv_tx); INIT_LIST_HEAD(&vdev->unlink_tx); INIT_LIST_HEAD(&vdev->unlink_rx); // vdev->priv_lock = SPIN_LOCK_UNLOCKED; spin_lock_init(&vdev->priv_lock); init_waitqueue_head(&vdev->waitq_tx); vdev->ud.eh_ops.shutdown = vhci_shutdown_connection; vdev->ud.eh_ops.reset = vhci_device_reset; vdev->ud.eh_ops.unusable= vhci_device_unusable; usbip_start_eh(&vdev->ud);}/*----------------------------------------------------------------------*/static int vhci_start(struct usb_hcd *hcd){ struct vhci_hcd *vhci = hcd_to_vhci(hcd); int rhport; int err = 0; dbg_vhci_hc("enter vhci_start\n"); /* initialize private data of usb_hcd */ for(rhport=0; rhport < VHCI_NPORTS; rhport++) { struct vhci_device *vdev = &vhci->vdev[rhport]; vhci_device_init(vdev); vdev->rhport = rhport; } atomic_set(&vhci->seqnum, 0); spin_lock_init(&vhci->lock); //hcd->power_budget = 0; /* no limit ? */ hcd->state = HC_STATE_RUNNING; //hcd->uses_new_polling = 1; /* vhci_hcd is now ready to be controlled through sysfs */ err = sysfs_create_group(&vhci_dev(vhci)->kobj, &dev_attr_group); if (err) { uerr("create sysfs files\n"); return err; } return 0;}static void vhci_stop(struct usb_hcd *hcd){ struct vhci_hcd *vhci = hcd_to_vhci(hcd); int rhport = 0; dbg_vhci_hc("stop VHCI controller\n"); /* 1. remove the userland interface of vhci_hcd */ sysfs_remove_group(&vhci_dev(vhci)->kobj, &dev_attr_group); /* 2. shutdown all the ports of vhci_hcd */ for(rhport = 0 ; rhport < VHCI_NPORTS; rhport++) { struct vhci_device *vdev = &vhci->vdev[rhport]; usbip_event_add(&vdev->ud, VDEV_EVENT_REMOVED); usbip_stop_eh(&vdev->ud); } uinfo("vhci_stop done\n");}/*----------------------------------------------------------------------*/static int vhci_get_frame_number(struct usb_hcd *hcd){ uerr("Not yet implemented\n"); return 0;}#ifdef CONFIG_PMstatic int vhci_bus_suspend(struct usb_hcd *hcd){ dbg("%s\n", __FUNCTION__); return 0;}static int vhci_bus_resume(struct usb_hcd *hcd){ dbg("%s\n", __FUNCTION__); return 0;}#else#define vhci_bus_suspend NULL#define vhci_bus_resume NULL#endifstatic struct hc_driver vhci_hc_driver = { .description = driver_name, .product_desc = driver_desc, .hcd_priv_size = sizeof(struct vhci_hcd), .flags = HCD_USB2, .start = vhci_start, .stop = vhci_stop, .urb_enqueue = vhci_urb_enqueue, .urb_dequeue = vhci_urb_dequeue, .get_frame_number = vhci_get_frame_number, .hub_status_data = vhci_hub_status, .hub_control = vhci_hub_control, .bus_suspend = vhci_bus_suspend, .bus_resume = vhci_bus_resume,};static int vhci_hcd_probe(struct platform_device *pdev){ struct usb_hcd *hcd; int ret; uinfo("proving...\n"); dbg_vhci_hc("name %s id %d\n", pdev->name, pdev->id); /* will be removed */ if (pdev->dev.dma_mask) { info("vhci_hcd DMA not supported\n"); return -EINVAL; } /* * Allocate and initialize hcd. * Our private data is also allocated automatically. */ hcd = usb_create_hcd(&vhci_hc_driver, &pdev->dev, pdev->dev.bus_id); if (!hcd) { uerr("create hcd failed\n"); return -ENOMEM; } /* this is private data for vhci_hcd */ the_controller = hcd_to_vhci(hcd); /* * Finish generic HCD structure initialization and register. * Call the driver's reset() and start() routines. */ ret = usb_add_hcd(hcd, 0, 0); if (ret != 0) { uerr("usb_add_hcd failed %d\n", ret); usb_put_hcd(hcd); the_controller = NULL; return ret; } dbg_vhci_hc("bye\n"); return 0;}static int vhci_hcd_remove(struct platform_device *pdev){ struct usb_hcd *hcd; hcd = platform_get_drvdata(pdev); if(!hcd) return 0; /* * Disconnects the root hub, * then reverses the effects of usb_add_hcd(), * invoking the HCD's stop() methods. */ usb_remove_hcd(hcd); usb_put_hcd(hcd); the_controller = NULL; return 0;}#ifdef CONFIG_PM/* what should happen for USB/IP under suspend/resume? */static int vhci_hcd_suspend(struct platform_device *pdev, pm_message_t state){ dev_dbg(&pdev->dev, "%s\n", __FUNCTION__); pdev->dev.power.power_state = state; return 0;}static int vhci_hcd_resume(struct platform_device *pdev){ dev_dbg(&pdev->dev, "%s\n", __FUNCTION__); pdev->dev.power.power_state = PMSG_ON; return 0;}#else#define vhci_hcd_suspend NULL#define vhci_hcd_resume NULL#endifstatic struct platform_driver vhci_driver = { .probe = vhci_hcd_probe, .remove = __devexit_p(vhci_hcd_remove), .suspend = vhci_hcd_suspend, .resume = vhci_hcd_resume, .driver = { .name = (char *) driver_name, .owner = THIS_MODULE, },};/*----------------------------------------------------------------------*//* * The VHCI 'device' is 'virtual'; not a real plug&play hardware. * We need to add this virtual device as a platform device arbitrarily: * 1. platform_device_register() */static void the_pdev_release(struct device *dev){ return;}static struct platform_device the_pdev = { /* should be the same name as driver_name */ .name = (char *) driver_name, .id = -1, .dev = { //.driver = &vhci_driver, .release = the_pdev_release, },};static int __init vhci_init(void){ int ret; dbg_vhci_hc("enter\n"); if (usb_disabled()) return -ENODEV; info("driver %s, %s\n", driver_name, DRIVER_VERSION); ret = platform_driver_register(&vhci_driver); if (ret < 0) goto err_driver_register; ret = platform_device_register(&the_pdev); if (ret < 0) goto err_platform_device_register; dbg_vhci_hc("bye\n"); return ret; /* error occurred */err_platform_device_register: platform_driver_unregister(&vhci_driver);err_driver_register: dbg_vhci_hc("bye\n"); return ret;}module_init(vhci_init);static void __exit vhci_cleanup(void){ dbg_vhci_hc("enter\n"); platform_device_unregister(&the_pdev); platform_driver_unregister(&vhci_driver); dbg_vhci_hc("bye\n");}module_exit(vhci_cleanup);
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -