diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2014-08-29 23:10:03 +0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-08-29 23:10:03 +0400 |
commit | 848298c6fb36fbe459854e376ce90af32ba6e1ce (patch) | |
tree | 60a2aeab0451c79e7c6bde9ddba053011c8450cc /drivers/usb | |
parent | bbf6c0a705d612a3e51c73280a2d6a608436abc6 (diff) | |
parent | a9ef803d740bfadf5e505fbc57efa57692e27025 (diff) | |
download | linux-848298c6fb36fbe459854e376ce90af32ba6e1ce.tar.xz |
Merge tag 'usb-3.17-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb
Pull USB fixes from Greg KH:
"Here are a bunch of fixes for the USB drivers for 3.17-rc3.
Also in here is the movement of the usbip driver out of staging, into
the "real" part of the kernel, it had to wait until after -rc1 to
handle the merge issues involved between the USB and staging trees.
The code is identical, just file movements there.
The USB fixes are all over the place, new device ids, xhci fixes for
reported issues and the usual gadget driver fixes as well. All have
been in linux-next for a while now"
* tag 'usb-3.17-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (46 commits)
USB: fix build error with CONFIG_PM_RUNTIME disabled
Revert "usb: ehci/ohci-exynos: Fix PHY getting sequence"
xhci: Disable streams on Via XHCI with device-id 0x3432
USB: serial: fix potential heap buffer overflow
USB: serial: fix potential stack buffer overflow
usb: ehci/ohci-exynos: Fix PHY getting sequence
usb: hub: Prevent hub autosuspend if usbcore.autosuspend is -1
USB: sisusb: add device id for Magic Control USB video
usb: dwc2: gadget: Set the default EP max packet value as 8 bytes
usb: ehci: using wIndex + 1 for hub port
USB: storage: add quirk for Newer Technology uSCSI SCSI-USB converter
MAINTAINERS: Add an entry for USB/IP driver
usbip: remove struct usb_device_id table
usbip: move usbip kernel code out of staging
usbip: move usbip userspace code out of staging
USB: whiteheat: Added bounds checking for bulk command response
usb: gadget: remove $(PWD) in ccflags-y
usb: pch_udc: usb gadget device support for Intel Quark X1000
usb: gadget: uvc: fix possible lockup in uvc gadget
usb: wusbcore: fix below build warning
...
Diffstat (limited to 'drivers/usb')
54 files changed, 5838 insertions, 134 deletions
diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig index e0cad4418085..cf1b19bca306 100644 --- a/drivers/usb/Kconfig +++ b/drivers/usb/Kconfig @@ -92,6 +92,8 @@ source "drivers/usb/storage/Kconfig" source "drivers/usb/image/Kconfig" +source "drivers/usb/usbip/Kconfig" + endif source "drivers/usb/musb/Kconfig" diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index 3cba892b83a2..d7be71778059 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -60,3 +60,5 @@ obj-$(CONFIG_USB_RENESAS_USBHS) += renesas_usbhs/ obj-$(CONFIG_USB_GADGET) += gadget/ obj-$(CONFIG_USB_COMMON) += common/ + +obj-$(CONFIG_USBIP_CORE) += usbip/ diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 8a4dcbc7a75f..46f5161c7891 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -1728,8 +1728,14 @@ static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id) * - Change autosuspend delay of hub can avoid unnecessary auto * suspend timer for hub, also may decrease power consumption * of USB bus. + * + * - If user has indicated to prevent autosuspend by passing + * usbcore.autosuspend = -1 then keep autosuspend disabled. */ - pm_runtime_set_autosuspend_delay(&hdev->dev, 0); +#ifdef CONFIG_PM_RUNTIME + if (hdev->dev.power.autosuspend_delay >= 0) + pm_runtime_set_autosuspend_delay(&hdev->dev, 0); +#endif /* * Hubs have proper suspend/resume support, except for root hubs @@ -2107,8 +2113,8 @@ void usb_disconnect(struct usb_device **pdev) { struct usb_port *port_dev = NULL; struct usb_device *udev = *pdev; - struct usb_hub *hub; - int port1; + struct usb_hub *hub = NULL; + int port1 = 1; /* mark the device as inactive, so any further urb submissions for * this device (and any of its children) will fail immediately. @@ -4631,9 +4637,7 @@ static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus, if (status != -ENODEV && port1 != unreliable_port && printk_ratelimit()) - dev_err(&udev->dev, "connect-debounce failed, port %d disabled\n", - port1); - + dev_err(&port_dev->dev, "connect-debounce failed\n"); portstatus &= ~USB_PORT_STAT_CONNECTION; unreliable_port = port1; } else { diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 0ba9c335b584..7c9618e916e2 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -1901,7 +1901,7 @@ static void s3c_hsotg_epint(struct s3c_hsotg *hsotg, unsigned int idx, static void s3c_hsotg_irq_enumdone(struct s3c_hsotg *hsotg) { u32 dsts = readl(hsotg->regs + DSTS); - int ep0_mps = 0, ep_mps; + int ep0_mps = 0, ep_mps = 8; /* * This should signal the finish of the enumeration phase diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c index ef4936ff626c..9dcfbe7cd5f5 100644 --- a/drivers/usb/dwc3/dwc3-omap.c +++ b/drivers/usb/dwc3/dwc3-omap.c @@ -425,7 +425,7 @@ static void dwc3_omap_set_utmi_mode(struct dwc3_omap *omap) static int dwc3_omap_extcon_register(struct dwc3_omap *omap) { - u32 ret; + int ret; struct device_node *node = omap->dev->of_node; struct extcon_dev *edev; diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index a186afeaa700..9add915d41f7 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -3,7 +3,7 @@ # subdir-ccflags-$(CONFIG_USB_GADGET_DEBUG) := -DDEBUG subdir-ccflags-$(CONFIG_USB_GADGET_VERBOSE) += -DVERBOSE_DEBUG -ccflags-y += -I$(PWD)/drivers/usb/gadget/udc +ccflags-y += -Idrivers/usb/gadget/udc obj-$(CONFIG_USB_LIBCOMPOSITE) += libcomposite.o libcomposite-y := usbstring.o config.o epautoconf.o diff --git a/drivers/usb/gadget/function/Makefile b/drivers/usb/gadget/function/Makefile index 6d91f21b52a6..83ae1065149d 100644 --- a/drivers/usb/gadget/function/Makefile +++ b/drivers/usb/gadget/function/Makefile @@ -2,8 +2,8 @@ # USB peripheral controller drivers # -ccflags-y := -I$(PWD)/drivers/usb/gadget/ -ccflags-y += -I$(PWD)/drivers/usb/gadget/udc/ +ccflags-y := -Idrivers/usb/gadget/ +ccflags-y += -Idrivers/usb/gadget/udc/ # USB Functions usb_f_acm-y := f_acm.o diff --git a/drivers/usb/gadget/function/u_ether.c b/drivers/usb/gadget/function/u_ether.c index d50adda913cf..6e6f87656e7b 100644 --- a/drivers/usb/gadget/function/u_ether.c +++ b/drivers/usb/gadget/function/u_ether.c @@ -1127,10 +1127,7 @@ void gether_disconnect(struct gether *link) DBG(dev, "%s\n", __func__); - netif_tx_lock(dev->net); netif_stop_queue(dev->net); - netif_tx_unlock(dev->net); - netif_carrier_off(dev->net); /* disable endpoints, forcing (synchronous) completion diff --git a/drivers/usb/gadget/function/uvc_video.c b/drivers/usb/gadget/function/uvc_video.c index 71e896d4c5ae..a5eb9a3fbb7a 100644 --- a/drivers/usb/gadget/function/uvc_video.c +++ b/drivers/usb/gadget/function/uvc_video.c @@ -195,6 +195,7 @@ uvc_video_complete(struct usb_ep *ep, struct usb_request *req) printk(KERN_INFO "Failed to queue request (%d).\n", ret); usb_ep_set_halt(ep); spin_unlock_irqrestore(&video->queue.irqlock, flags); + uvc_queue_cancel(queue, 0); goto requeue; } spin_unlock_irqrestore(&video->queue.irqlock, flags); @@ -281,6 +282,7 @@ error: static int uvc_video_pump(struct uvc_video *video) { + struct uvc_video_queue *queue = &video->queue; struct usb_request *req; struct uvc_buffer *buf; unsigned long flags; @@ -322,6 +324,7 @@ uvc_video_pump(struct uvc_video *video) printk(KERN_INFO "Failed to queue request (%d)\n", ret); usb_ep_set_halt(video->ep); spin_unlock_irqrestore(&video->queue.irqlock, flags); + uvc_queue_cancel(queue, 0); break; } spin_unlock_irqrestore(&video->queue.irqlock, flags); diff --git a/drivers/usb/gadget/legacy/Makefile b/drivers/usb/gadget/legacy/Makefile index a11aad5635df..edba2d1ee0f3 100644 --- a/drivers/usb/gadget/legacy/Makefile +++ b/drivers/usb/gadget/legacy/Makefile @@ -2,9 +2,9 @@ # USB gadget drivers # -ccflags-y := -I$(PWD)/drivers/usb/gadget/ -ccflags-y += -I$(PWD)/drivers/usb/gadget/udc/ -ccflags-y += -I$(PWD)/drivers/usb/gadget/function/ +ccflags-y := -Idrivers/usb/gadget/ +ccflags-y += -Idrivers/usb/gadget/udc/ +ccflags-y += -Idrivers/usb/gadget/function/ g_zero-y := zero.o g_audio-y := audio.o diff --git a/drivers/usb/gadget/legacy/dbgp.c b/drivers/usb/gadget/legacy/dbgp.c index 986fc511a2ed..225e385a6160 100644 --- a/drivers/usb/gadget/legacy/dbgp.c +++ b/drivers/usb/gadget/legacy/dbgp.c @@ -222,10 +222,12 @@ static void dbgp_unbind(struct usb_gadget *gadget) { #ifdef CONFIG_USB_G_DBGP_SERIAL kfree(dbgp.serial); + dbgp.serial = NULL; #endif if (dbgp.req) { kfree(dbgp.req->buf); usb_ep_free_request(gadget->ep0, dbgp.req); + dbgp.req = NULL; } gadget->ep0->driver_data = NULL; diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c index 2e4ce7704908..e96077b8bf79 100644 --- a/drivers/usb/gadget/legacy/inode.c +++ b/drivers/usb/gadget/legacy/inode.c @@ -440,7 +440,7 @@ ep_write (struct file *fd, const char __user *buf, size_t len, loff_t *ptr) value = -ENOMEM; kbuf = memdup_user(buf, len); - if (!kbuf) { + if (IS_ERR(kbuf)) { value = PTR_ERR(kbuf); goto free1; } diff --git a/drivers/usb/gadget/udc/Kconfig b/drivers/usb/gadget/udc/Kconfig index 5151f947a4f5..34ebaa68504c 100644 --- a/drivers/usb/gadget/udc/Kconfig +++ b/drivers/usb/gadget/udc/Kconfig @@ -332,7 +332,7 @@ config USB_GOKU gadget drivers to also be dynamically linked. config USB_EG20T - tristate "Intel EG20T PCH/LAPIS Semiconductor IOH(ML7213/ML7831) UDC" + tristate "Intel QUARK X1000/EG20T PCH/LAPIS Semiconductor IOH(ML7213/ML7831) UDC" depends on PCI help This is a USB device driver for EG20T PCH. @@ -353,6 +353,7 @@ config USB_EG20T ML7213/ML7831 is companion chip for Intel Atom E6xx series. ML7213/ML7831 is completely compatible for Intel EG20T PCH. + This driver can be used with Intel's Quark X1000 SOC platform # # LAST -- dummy/emulated controller # diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.c b/drivers/usb/gadget/udc/atmel_usba_udc.c index 906e65f0e4fa..c9fe67e29d35 100644 --- a/drivers/usb/gadget/udc/atmel_usba_udc.c +++ b/drivers/usb/gadget/udc/atmel_usba_udc.c @@ -1661,7 +1661,7 @@ static irqreturn_t usba_udc_irq(int irq, void *devid) if (dma_status) { int i; - for (i = 1; i < USBA_NR_DMAS; i++) + for (i = 1; i <= USBA_NR_DMAS; i++) if (dma_status & (1 << i)) usba_dma_irq(udc, &udc->usba_ep[i]); } diff --git a/drivers/usb/gadget/udc/fusb300_udc.c b/drivers/usb/gadget/udc/fusb300_udc.c index d40255f784df..5c5d1adda7eb 100644 --- a/drivers/usb/gadget/udc/fusb300_udc.c +++ b/drivers/usb/gadget/udc/fusb300_udc.c @@ -1398,13 +1398,17 @@ static int fusb300_probe(struct platform_device *pdev) /* initialize udc */ fusb300 = kzalloc(sizeof(struct fusb300), GFP_KERNEL); - if (fusb300 == NULL) + if (fusb300 == NULL) { + ret = -ENOMEM; goto clean_up; + } for (i = 0; i < FUSB300_MAX_NUM_EP; i++) { _ep[i] = kzalloc(sizeof(struct fusb300_ep), GFP_KERNEL); - if (_ep[i] == NULL) + if (_ep[i] == NULL) { + ret = -ENOMEM; goto clean_up; + } fusb300->ep[i] = _ep[i]; } diff --git a/drivers/usb/gadget/udc/pch_udc.c b/drivers/usb/gadget/udc/pch_udc.c index eb8c3bedb57a..460d953c91b6 100644 --- a/drivers/usb/gadget/udc/pch_udc.c +++ b/drivers/usb/gadget/udc/pch_udc.c @@ -343,6 +343,7 @@ struct pch_vbus_gpio_data { * @setup_data: Received setup data * @phys_addr: of device memory * @base_addr: for mapped device memory + * @bar: Indicates which PCI BAR for USB regs * @irq: IRQ line for the device * @cfg_data: current cfg, intf, and alt in use * @vbus_gpio: GPIO informaton for detecting VBUS @@ -370,14 +371,17 @@ struct pch_udc_dev { struct usb_ctrlrequest setup_data; unsigned long phys_addr; void __iomem *base_addr; + unsigned bar; unsigned irq; struct pch_udc_cfg_data cfg_data; struct pch_vbus_gpio_data vbus_gpio; }; #define to_pch_udc(g) (container_of((g), struct pch_udc_dev, gadget)) +#define PCH_UDC_PCI_BAR_QUARK_X1000 0 #define PCH_UDC_PCI_BAR 1 #define PCI_DEVICE_ID_INTEL_EG20T_UDC 0x8808 +#define PCI_DEVICE_ID_INTEL_QUARK_X1000_UDC 0x0939 #define PCI_VENDOR_ID_ROHM 0x10DB #define PCI_DEVICE_ID_ML7213_IOH_UDC 0x801D #define PCI_DEVICE_ID_ML7831_IOH_UDC 0x8808 @@ -3076,7 +3080,7 @@ static void pch_udc_remove(struct pci_dev *pdev) iounmap(dev->base_addr); if (dev->mem_region) release_mem_region(dev->phys_addr, - pci_resource_len(pdev, PCH_UDC_PCI_BAR)); + pci_resource_len(pdev, dev->bar)); if (dev->active) pci_disable_device(pdev); kfree(dev); @@ -3144,9 +3148,15 @@ static int pch_udc_probe(struct pci_dev *pdev, dev->active = 1; pci_set_drvdata(pdev, dev); + /* Determine BAR based on PCI ID */ + if (id->device == PCI_DEVICE_ID_INTEL_QUARK_X1000_UDC) + dev->bar = PCH_UDC_PCI_BAR_QUARK_X1000; + else + dev->bar = PCH_UDC_PCI_BAR; + /* PCI resource allocation */ - resource = pci_resource_start(pdev, 1); - len = pci_resource_len(pdev, 1); + resource = pci_resource_start(pdev, dev->bar); + len = pci_resource_len(pdev, dev->bar); if (!request_mem_region(resource, len, KBUILD_MODNAME)) { dev_err(&pdev->dev, "%s: pci device used already\n", __func__); @@ -3212,6 +3222,12 @@ finished: static const struct pci_device_id pch_udc_pcidev_id[] = { { + PCI_DEVICE(PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_QUARK_X1000_UDC), + .class = (PCI_CLASS_SERIAL_USB << 8) | 0xfe, + .class_mask = 0xffffffff, + }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_EG20T_UDC), .class = (PCI_CLASS_SERIAL_USB << 8) | 0xfe, .class_mask = 0xffffffff, diff --git a/drivers/usb/gadget/udc/r8a66597-udc.c b/drivers/usb/gadget/udc/r8a66597-udc.c index 46008421c1ec..de2a8713b428 100644 --- a/drivers/usb/gadget/udc/r8a66597-udc.c +++ b/drivers/usb/gadget/udc/r8a66597-udc.c @@ -1868,8 +1868,8 @@ static int r8a66597_probe(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_MEM, 0); reg = devm_ioremap_resource(&pdev->dev, res); - if (!reg) - return -ENODEV; + if (IS_ERR(reg)) + return PTR_ERR(reg); ires = platform_get_resource(pdev, IORESOURCE_IRQ, 0); irq = ires->start; diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index cc305c71ac3d..6130b7574908 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -1230,7 +1230,7 @@ int ehci_hub_control( if (selector == EHSET_TEST_SINGLE_STEP_SET_FEATURE) { spin_unlock_irqrestore(&ehci->lock, flags); retval = ehset_single_step_set_feature(hcd, - wIndex); + wIndex + 1); spin_lock_irqsave(&ehci->lock, flags); break; } diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index 687d36608155..c22a3e15a16e 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -101,6 +101,10 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci) /* AMD PLL quirk */ if (pdev->vendor == PCI_VENDOR_ID_AMD && usb_amd_find_chipset_info()) xhci->quirks |= XHCI_AMD_PLL_FIX; + + if (pdev->vendor == PCI_VENDOR_ID_AMD) + xhci->quirks |= XHCI_TRUST_TX_LENGTH; + if (pdev->vendor == PCI_VENDOR_ID_INTEL) { xhci->quirks |= XHCI_LPM_SUPPORT; xhci->quirks |= XHCI_INTEL_HOST; @@ -151,6 +155,11 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci) if (pdev->vendor == PCI_VENDOR_ID_VIA) xhci->quirks |= XHCI_RESET_ON_RESUME; + /* See https://bugzilla.kernel.org/show_bug.cgi?id=79511 */ + if (pdev->vendor == PCI_VENDOR_ID_VIA && + pdev->device == 0x3432) + xhci->quirks |= XHCI_BROKEN_STREAMS; + if (xhci->quirks & XHCI_RESET_ON_RESUME) xhci_dbg_trace(xhci, trace_xhci_dbg_quirks, "QUIRK: Resetting on resume"); diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 60fb52ae864b..abed30b82905 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -364,32 +364,6 @@ static void ring_doorbell_for_active_rings(struct xhci_hcd *xhci, } } -/* - * Find the segment that trb is in. Start searching in start_seg. - * If we must move past a segment that has a link TRB with a toggle cycle state - * bit set, then we will toggle the value pointed at by cycle_state. - */ -static struct xhci_segment *find_trb_seg( - struct xhci_segment *start_seg, - union xhci_trb *trb, int *cycle_state) -{ - struct xhci_segment *cur_seg = start_seg; - struct xhci_generic_trb *generic_trb; - - while (cur_seg->trbs > trb || - &cur_seg->trbs[TRBS_PER_SEGMENT - 1] < trb) { - generic_trb = &cur_seg->trbs[TRBS_PER_SEGMENT - 1].generic; - if (generic_trb->field[3] & cpu_to_le32(LINK_TOGGLE)) - *cycle_state ^= 0x1; - cur_seg = cur_seg->next; - if (cur_seg == start_seg) - /* Looped over the entire list. Oops! */ - return NULL; - } - return cur_seg; -} - - static struct xhci_ring *xhci_triad_to_transfer_ring(struct xhci_hcd *xhci, unsigned int slot_id, unsigned int ep_index, unsigned int stream_id) @@ -459,9 +433,12 @@ void xhci_find_new_dequeue_state(struct xhci_hcd *xhci, struct xhci_virt_device *dev = xhci->devs[slot_id]; struct xhci_virt_ep *ep = &dev->eps[ep_index]; struct xhci_ring *ep_ring; - struct xhci_generic_trb *trb; + struct xhci_segment *new_seg; + union xhci_trb *new_deq; dma_addr_t addr; u64 hw_dequeue; + bool cycle_found = false; + bool td_last_trb_found = false; ep_ring = xhci_triad_to_transfer_ring(xhci, slot_id, ep_index, stream_id); @@ -486,45 +463,45 @@ void xhci_find_new_dequeue_state(struct xhci_hcd *xhci, hw_dequeue = le64_to_cpu(ep_ctx->deq); } - /* Find virtual address and segment of hardware dequeue pointer */ - state->new_deq_seg = ep_ring->deq_seg; - state->new_deq_ptr = ep_ring->dequeue; - while (xhci_trb_virt_to_dma(state->new_deq_seg, state->new_deq_ptr) - != (dma_addr_t)(hw_dequeue & ~0xf)) { - next_trb(xhci, ep_ring, &state->new_deq_seg, - &state->new_deq_ptr); - if (state->new_deq_ptr == ep_ring->dequeue) { - WARN_ON(1); - return; - } - } + new_seg = ep_ring->deq_seg; + new_deq = ep_ring->dequeue; + state->new_cycle_state = hw_dequeue & 0x1; + /* - * Find cycle state for last_trb, starting at old cycle state of - * hw_dequeue. If there is only one segment ring, find_trb_seg() will - * return immediately and cannot toggle the cycle state if this search - * wraps around, so add one more toggle manually in that case. + * We want to find the pointer, segment and cycle state of the new trb + * (the one after current TD's last_trb). We know the cycle state at + * hw_dequeue, so walk the ring until both hw_dequeue and last_trb are + * found. */ - state->new_cycle_state = hw_dequeue & 0x1; - if (ep_ring->first_seg == ep_ring->first_seg->next && - cur_td->last_trb < state->new_deq_ptr) - state->new_cycle_state ^= 0x1; + do { + if (!cycle_found && xhci_trb_virt_to_dma(new_seg, new_deq) + == (dma_addr_t)(hw_dequeue & ~0xf)) { + cycle_found = true; + if (td_last_trb_found) + break; + } + if (new_deq == cur_td->last_trb) + td_last_trb_found = true; - state->new_deq_ptr = cur_td->last_trb; - xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb, - "Finding segment containing last TRB in TD."); - state->new_deq_seg = find_trb_seg(state->new_deq_seg, - state->new_deq_ptr, &state->new_cycle_state); - if (!state->new_deq_seg) { - WARN_ON(1); - return; - } + if (cycle_found && + TRB_TYPE_LINK_LE32(new_deq->generic.field[3]) && + new_deq->generic.field[3] & cpu_to_le32(LINK_TOGGLE)) + state->new_cycle_state ^= 0x1; + + next_trb(xhci, ep_ring, &new_seg, &new_deq); + + /* Search wrapped around, bail out */ + if (new_deq == ep->ring->dequeue) { + xhci_err(xhci, "Error: Failed finding new dequeue state\n"); + state->new_deq_seg = NULL; + state->new_deq_ptr = NULL; + return; + } + + } while (!cycle_found || !td_last_trb_found); - /* Increment to find next TRB after last_trb. Cycle if appropriate. */ - trb = &state->new_deq_ptr->generic; - if (TRB_TYPE_LINK_LE32(trb->field[3]) && - (trb->field[3] & cpu_to_le32(LINK_TOGGLE))) - state->new_cycle_state ^= 0x1; - next_trb(xhci, ep_ring, &state->new_deq_seg, &state->new_deq_ptr); + state->new_deq_seg = new_seg; + state->new_deq_ptr = new_deq; /* Don't update the ring cycle state for the producer (us). */ xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb, @@ -2487,7 +2464,8 @@ static int handle_tx_event(struct xhci_hcd *xhci, * last TRB of the previous TD. The command completion handle * will take care the rest. */ - if (!event_seg && trb_comp_code == COMP_STOP_INVAL) { + if (!event_seg && (trb_comp_code == COMP_STOP || + trb_comp_code == COMP_STOP_INVAL)) { ret = 0; goto cleanup; } diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index b6f21175b872..c020b094fe7d 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -2880,6 +2880,9 @@ void xhci_cleanup_stalled_ring(struct xhci_hcd *xhci, ep_index, ep->stopped_stream, ep->stopped_td, &deq_state); + if (!deq_state.new_deq_ptr || !deq_state.new_deq_seg) + return; + /* HW with the reset endpoint quirk will use the saved dequeue state to * issue a configure endpoint command later. */ diff --git a/drivers/usb/misc/sisusbvga/sisusb.c b/drivers/usb/misc/sisusbvga/sisusb.c index 06b5d77cd9ad..633caf643122 100644 --- a/drivers/usb/misc/sisusbvga/sisusb.c +++ b/drivers/usb/misc/sisusbvga/sisusb.c @@ -3250,6 +3250,7 @@ static const struct usb_device_id sisusb_table[] = { { USB_DEVICE(0x0711, 0x0918) }, { USB_DEVICE(0x0711, 0x0920) }, { USB_DEVICE(0x0711, 0x0950) }, + { USB_DEVICE(0x0711, 0x5200) }, { USB_DEVICE(0x182d, 0x021c) }, { USB_DEVICE(0x182d, 0x0269) }, { } diff --git a/drivers/usb/musb/ux500_dma.c b/drivers/usb/musb/ux500_dma.c index 9aad00f11bd5..221faed9f074 100644 --- a/drivers/usb/musb/ux500_dma.c +++ b/drivers/usb/musb/ux500_dma.c @@ -96,7 +96,7 @@ static bool ux500_configure_channel(struct dma_channel *channel, struct musb *musb = ux500_channel->controller->private_data; dev_dbg(musb->controller, - "packet_sz=%d, mode=%d, dma_addr=0x%llu, len=%d is_tx=%d\n", + "packet_sz=%d, mode=%d, dma_addr=0x%llx, len=%d is_tx=%d\n", packet_sz, mode, (unsigned long long) dma_addr, len, ux500_channel->is_tx); diff --git a/drivers/usb/phy/phy-gpio-vbus-usb.c b/drivers/usb/phy/phy-gpio-vbus-usb.c index ea9e705555df..f4b14bd97e14 100644 --- a/drivers/usb/phy/phy-gpio-vbus-usb.c +++ b/drivers/usb/phy/phy-gpio-vbus-usb.c @@ -260,10 +260,8 @@ static int gpio_vbus_probe(struct platform_device *pdev) gpio_vbus->phy.otg = devm_kzalloc(&pdev->dev, sizeof(struct usb_otg), GFP_KERNEL); - if (!gpio_vbus->phy.otg) { - kfree(gpio_vbus); + if (!gpio_vbus->phy.otg) return -ENOMEM; - } platform_set_drvdata(pdev, gpio_vbus); gpio_vbus->dev = &pdev->dev; diff --git a/drivers/usb/phy/phy-msm-usb.c b/drivers/usb/phy/phy-msm-usb.c index e4108eec5ef4..afc09087ec36 100644 --- a/drivers/usb/phy/phy-msm-usb.c +++ b/drivers/usb/phy/phy-msm-usb.c @@ -1601,8 +1601,8 @@ static int msm_otg_probe(struct platform_device *pdev) */ if (motg->phy_number) { phy_select = devm_ioremap_nocache(&pdev->dev, USB2_PHY_SEL, 4); - if (IS_ERR(phy_select)) - return PTR_ERR(phy_select); + if (!phy_select) + return -ENOMEM; /* Enable second PHY with the OTG port */ writel(0x1, phy_select); } diff --git a/drivers/usb/phy/phy-samsung-usb.h b/drivers/usb/phy/phy-samsung-usb.h index 68771bfd1825..80eedd45a20a 100644 --- a/drivers/usb/phy/phy-samsung-usb.h +++ b/drivers/usb/phy/phy-samsung-usb.h @@ -216,7 +216,7 @@ #define EXYNOS5_DRD_PHYPARAM1 (0x20) -#define PHYPARAM1_PCS_TXDEEMPH_MASK (0x1f << 0) +#define PHYPARAM1_PCS_TXDEEMPH_MASK (0x3f << 0) #define PHYPARAM1_PCS_TXDEEMPH (0x1c) #define EXYNOS5_DRD_PHYTERM (0x24) diff --git a/drivers/usb/phy/phy.c b/drivers/usb/phy/phy.c index 6d0f6080eceb..045cd309367a 100644 --- a/drivers/usb/phy/phy.c +++ b/drivers/usb/phy/phy.c @@ -232,6 +232,9 @@ struct usb_phy *usb_get_phy_dev(struct device *dev, u8 index) phy = __usb_find_phy_dev(dev, &phy_bind_list, index); if (IS_ERR(phy) || !try_module_get(phy->dev->driver->owner)) { dev_dbg(dev, "unable to find transceiver\n"); + if (!IS_ERR(phy)) + phy = ERR_PTR(-ENODEV); + goto err0; } diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 216ce3078270..824ea5e7ec8b 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -146,6 +146,7 @@ static const struct usb_device_id id_table_combined[] = { { USB_DEVICE(FTDI_VID, FTDI_AMC232_PID) }, { USB_DEVICE(FTDI_VID, FTDI_CANUSB_PID) }, { USB_DEVICE(FTDI_VID, FTDI_CANDAPTER_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_BM_ATOM_NANO_PID) }, { USB_DEVICE(FTDI_VID, FTDI_NXTCAM_PID) }, { USB_DEVICE(FTDI_VID, FTDI_EV3CON_PID) }, { USB_DEVICE(FTDI_VID, FTDI_SCS_DEVICE_0_PID) }, @@ -934,6 +935,8 @@ static const struct usb_device_id id_table_combined[] = { { USB_DEVICE(BRAINBOXES_VID, BRAINBOXES_US_842_2_PID) }, { USB_DEVICE(BRAINBOXES_VID, BRAINBOXES_US_842_3_PID) }, { USB_DEVICE(BRAINBOXES_VID, BRAINBOXES_US_842_4_PID) }, + /* ekey Devices */ + { USB_DEVICE(FTDI_VID, FTDI_EKEY_CONV_USB_PID) }, /* Infineon Devices */ { USB_DEVICE_INTERFACE_NUMBER(INFINEON_VID, INFINEON_TRIBOARD_PID, 1) }, { } /* Terminating entry */ diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h index 1e58d90a0b6c..70b0b1d88ae9 100644 --- a/drivers/usb/serial/ftdi_sio_ids.h +++ b/drivers/usb/serial/ftdi_sio_ids.h @@ -42,6 +42,8 @@ /* www.candapter.com Ewert Energy Systems CANdapter device */ #define FTDI_CANDAPTER_PID 0x9F80 /* Product Id */ +#define FTDI_BM_ATOM_NANO_PID 0xa559 /* Basic Micro ATOM Nano USB2Serial */ + /* * Texas Instruments XDS100v2 JTAG / BeagleBone A3 * http://processors.wiki.ti.com/index.php/XDS100 @@ -1378,3 +1380,8 @@ #define BRAINBOXES_US_160_6_PID 0x9006 /* US-160 16xRS232 1Mbaud Port 11 and 12 */ #define BRAINBOXES_US_160_7_PID 0x9007 /* US-160 16xRS232 1Mbaud Port 13 and 14 */ #define BRAINBOXES_US_160_8_PID 0x9008 /* US-160 16xRS232 1Mbaud Port 15 and 16 */ + +/* + * ekey biometric systems GmbH (http://ekey.net/) + */ +#define FTDI_EKEY_CONV_USB_PID 0xCB08 /* Converter USB */ diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index a9688940543d..54a8120897a6 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -275,8 +275,12 @@ static void option_instat_callback(struct urb *urb); #define ZTE_PRODUCT_MF622 0x0001 #define ZTE_PRODUCT_MF628 0x0015 #define ZTE_PRODUCT_MF626 0x0031 -#define ZTE_PRODUCT_MC2718 0xffe8 #define ZTE_PRODUCT_AC2726 0xfff1 +#define ZTE_PRODUCT_CDMA_TECH 0xfffe +#define ZTE_PRODUCT_AC8710T 0xffff +#define ZTE_PRODUCT_MC2718 0xffe8 +#define ZTE_PRODUCT_AD3812 0xffeb +#define ZTE_PRODUCT_MC2716 0xffed #define BENQ_VENDOR_ID 0x04a5 #define BENQ_PRODUCT_H10 0x4068 @@ -494,6 +498,10 @@ static void option_instat_callback(struct urb *urb); #define INOVIA_VENDOR_ID 0x20a6 #define INOVIA_SEW858 0x1105 +/* VIA Telecom */ +#define VIATELECOM_VENDOR_ID 0x15eb +#define VIATELECOM_PRODUCT_CDS7 0x0001 + /* some devices interfaces need special handling due to a number of reasons */ enum option_blacklist_reason { OPTION_BLACKLIST_NONE = 0, @@ -527,10 +535,18 @@ static const struct option_blacklist_info zte_k3765_z_blacklist = { .reserved = BIT(4), }; +static const struct option_blacklist_info zte_ad3812_z_blacklist = { + .sendsetup = BIT(0) | BIT(1) | BIT(2), +}; + static const struct option_blacklist_info zte_mc2718_z_blacklist = { .sendsetup = BIT(1) | BIT(2) | BIT(3) | BIT(4), }; +static const struct option_blacklist_info zte_mc2716_z_blacklist = { + .sendsetup = BIT(1) | BIT(2) | BIT(3), +}; + static const struct option_blacklist_info huawei_cdc12_blacklist = { .reserved = BIT(1) | BIT(2), }; @@ -1070,6 +1086,7 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_INTERFACE_CLASS(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_1012, 0xff) }, { USB_DEVICE(KYOCERA_VENDOR_ID, KYOCERA_PRODUCT_KPC650) }, { USB_DEVICE(KYOCERA_VENDOR_ID, KYOCERA_PRODUCT_KPC680) }, + { USB_DEVICE(QUALCOMM_VENDOR_ID, 0x6000)}, /* ZTE AC8700 */ { USB_DEVICE(QUALCOMM_VENDOR_ID, 0x6613)}, /* Onda H600/ZTE MF330 */ { USB_DEVICE(QUALCOMM_VENDOR_ID, 0x0023)}, /* ONYX 3G device */ { USB_DEVICE(QUALCOMM_VENDOR_ID, 0x9000)}, /* SIMCom SIM5218 */ @@ -1544,13 +1561,18 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff93, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff94, 0xff, 0xff, 0xff) }, - /* NOTE: most ZTE CDMA devices should be driven by zte_ev, not option */ + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_CDMA_TECH, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_AC2726, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_AC8710T, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_MC2718, 0xff, 0xff, 0xff), .driver_info = (kernel_ulong_t)&zte_mc2718_z_blacklist }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_AD3812, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&zte_ad3812_z_blacklist }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_MC2716, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&zte_mc2716_z_blacklist }, { USB_VENDOR_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff, 0x02, 0x01) }, { USB_VENDOR_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff, 0x02, 0x05) }, { USB_VENDOR_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff, 0x86, 0x10) }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_AC2726, 0xff, 0xff, 0xff) }, { USB_DEVICE(BENQ_VENDOR_ID, BENQ_PRODUCT_H10) }, { USB_DEVICE(DLINK_VENDOR_ID, DLINK_PRODUCT_DWM_652) }, @@ -1724,6 +1746,7 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(0x07d1, 0x3e01, 0xff, 0xff, 0xff) }, /* D-Link DWM-152/C1 */ { USB_DEVICE_AND_INTERFACE_INFO(0x07d1, 0x3e02, 0xff, 0xff, 0xff) }, /* D-Link DWM-156/C1 */ { USB_DEVICE(INOVIA_VENDOR_ID, INOVIA_SEW858) }, + { USB_DEVICE(VIATELECOM_VENDOR_ID, VIATELECOM_PRODUCT_CDS7) }, { } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, option_ids); @@ -1916,6 +1939,8 @@ static void option_instat_callback(struct urb *urb) dev_dbg(dev, "%s: type %x req %x\n", __func__, req_pkt->bRequestType, req_pkt->bRequest); } + } else if (status == -ENOENT || status == -ESHUTDOWN) { + dev_dbg(dev, "%s: urb stopped: %d\n", __func__, status); } else dev_err(dev, "%s: error %d\n", __func__, status); diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index b3d5a35c0d4b..e9bad928039f 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -45,6 +45,7 @@ static const struct usb_device_id id_table[] = { { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_GPRS) }, { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_HCR331) }, { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_MOTOROLA) }, + { USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_ZTEK) }, { USB_DEVICE(IODATA_VENDOR_ID, IODATA_PRODUCT_ID) }, { USB_DEVICE(IODATA_VENDOR_ID, IODATA_PRODUCT_ID_RSAQ5) }, { USB_DEVICE(ATEN_VENDOR_ID, ATEN_PRODUCT_ID) }, diff --git a/drivers/usb/serial/pl2303.h b/drivers/usb/serial/pl2303.h index 42bc082896ac..71fd9da1d6e7 100644 --- a/drivers/usb/serial/pl2303.h +++ b/drivers/usb/serial/pl2303.h @@ -22,6 +22,7 @@ #define PL2303_PRODUCT_ID_GPRS 0x0609 #define PL2303_PRODUCT_ID_HCR331 0x331a #define PL2303_PRODUCT_ID_MOTOROLA 0x0307 +#define PL2303_PRODUCT_ID_ZTEK 0xe1f1 #define ATEN_VENDOR_ID 0x0557 #define ATEN_VENDOR_ID2 0x0547 diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 02de3110fe94..475723c006f9 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -764,29 +764,39 @@ static int usb_serial_probe(struct usb_interface *interface, if (usb_endpoint_is_bulk_in(endpoint)) { /* we found a bulk in endpoint */ dev_dbg(ddev, "found bulk in on endpoint %d\n", i); - bulk_in_endpoint[num_bulk_in] = endpoint; - ++num_bulk_in; + if (num_bulk_in < MAX_NUM_PORTS) { + bulk_in_endpoint[num_bulk_in] = endpoint; + ++num_bulk_in; + } } if (usb_endpoint_is_bulk_out(endpoint)) { /* we found a bulk out endpoint */ dev_dbg(ddev, "found bulk out on endpoint %d\n", i); - bulk_out_endpoint[num_bulk_out] = endpoint; - ++num_bulk_out; + if (num_bulk_out < MAX_NUM_PORTS) { + bulk_out_endpoint[num_bulk_out] = endpoint; + ++num_bulk_out; + } } if (usb_endpoint_is_int_in(endpoint)) { /* we found a interrupt in endpoint */ dev_dbg(ddev, "found interrupt in on endpoint %d\n", i); - interrupt_in_endpoint[num_interrupt_in] = endpoint; - ++num_interrupt_in; + if (num_interrupt_in < MAX_NUM_PORTS) { + interrupt_in_endpoint[num_interrupt_in] = + endpoint; + ++num_interrupt_in; + } } if (usb_endpoint_is_int_out(endpoint)) { /* we found an interrupt out endpoint */ dev_dbg(ddev, "found interrupt out on endpoint %d\n", i); - interrupt_out_endpoint[num_interrupt_out] = endpoint; - ++num_interrupt_out; + if (num_interrupt_out < MAX_NUM_PORTS) { + interrupt_out_endpoint[num_interrupt_out] = + endpoint; + ++num_interrupt_out; + } } } @@ -809,8 +819,10 @@ static int usb_serial_probe(struct usb_interface *interface, if (usb_endpoint_is_int_in(endpoint)) { /* we found a interrupt in endpoint */ dev_dbg(ddev, "found interrupt in for Prolific device on separate interface\n"); - interrupt_in_endpoint[num_interrupt_in] = endpoint; - ++num_interrupt_in; + if (num_interrupt_in < MAX_NUM_PORTS) { + interrupt_in_endpoint[num_interrupt_in] = endpoint; + ++num_interrupt_in; + } } } } @@ -850,6 +862,11 @@ static int usb_serial_probe(struct usb_interface *interface, num_ports = type->num_ports; } + if (num_ports > MAX_NUM_PORTS) { + dev_warn(ddev, "too many ports requested: %d\n", num_ports); + num_ports = MAX_NUM_PORTS; + } + serial->num_ports = num_ports; serial->num_bulk_in = num_bulk_in; serial->num_bulk_out = num_bulk_out; diff --git a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c index e62f2dff8b7d..6c3734d2b45a 100644 --- a/drivers/usb/serial/whiteheat.c +++ b/drivers/usb/serial/whiteheat.c @@ -514,6 +514,10 @@ static void command_port_read_callback(struct urb *urb) dev_dbg(&urb->dev->dev, "%s - command_info is NULL, exiting.\n", __func__); return; } + if (!urb->actual_length) { + dev_dbg(&urb->dev->dev, "%s - empty response, exiting.\n", __func__); + return; + } if (status) { dev_dbg(&urb->dev->dev, "%s - nonzero urb status: %d\n", __func__, status); if (status != -ENOENT) @@ -534,7 +538,8 @@ static void command_port_read_callback(struct urb *urb) /* These are unsolicited reports from the firmware, hence no waiting command to wakeup */ dev_dbg(&urb->dev->dev, "%s - event received\n", __func__); - } else if (data[0] == WHITEHEAT_GET_DTR_RTS) { + } else if ((data[0] == WHITEHEAT_GET_DTR_RTS) && + (urb->actual_length - 1 <= sizeof(command_info->result_buffer))) { memcpy(command_info->result_buffer, &data[1], urb->actual_length - 1); command_info->command_finished = WHITEHEAT_CMD_COMPLETE; diff --git a/drivers/usb/serial/zte_ev.c b/drivers/usb/serial/zte_ev.c index e40ab739c4a6..1a132e9e947a 100644 --- a/drivers/usb/serial/zte_ev.c +++ b/drivers/usb/serial/zte_ev.c @@ -272,28 +272,8 @@ static void zte_ev_usb_serial_close(struct usb_serial_port *port) } static const struct usb_device_id id_table[] = { - /* AC8710, AC8710T */ - { USB_DEVICE_AND_INTERFACE_INFO(0x19d2, 0xffff, 0xff, 0xff, 0xff) }, - /* AC8700 */ - { USB_DEVICE_AND_INTERFACE_INFO(0x19d2, 0xfffe, 0xff, 0xff, 0xff) }, /* MG880 */ { USB_DEVICE(0x19d2, 0xfffd) }, - { USB_DEVICE(0x19d2, 0xfffc) }, - { USB_DEVICE(0x19d2, 0xfffb) }, - /* AC8710_V3 */ - { USB_DEVICE(0x19d2, 0xfff6) }, - { USB_DEVICE(0x19d2, 0xfff7) }, - { USB_DEVICE(0x19d2, 0xfff8) }, - { USB_DEVICE(0x19d2, 0xfff9) }, - { USB_DEVICE(0x19d2, 0xffee) }, - /* AC2716, MC2716 */ - { USB_DEVICE_AND_INTERFACE_INFO(0x19d2, 0xffed, 0xff, 0xff, 0xff) }, - /* AD3812 */ - { USB_DEVICE_AND_INTERFACE_INFO(0x19d2, 0xffeb, 0xff, 0xff, 0xff) }, - { USB_DEVICE(0x19d2, 0xffec) }, - { USB_DEVICE(0x05C6, 0x3197) }, - { USB_DEVICE(0x05C6, 0x6000) }, - { USB_DEVICE(0x05C6, 0x9008) }, { }, }; MODULE_DEVICE_TABLE(usb, id_table); diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index 80a5b366255f..7ef99b2f3aaf 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -922,6 +922,12 @@ UNUSUAL_DEV( 0x069b, 0x3004, 0x0001, 0x0001, USB_SC_DEVICE, USB_PR_DEVICE, NULL, US_FL_FIX_CAPACITY ), +UNUSUAL_DEV( 0x06ca, 0x2003, 0x0100, 0x0100, + "Newer Technology", + "uSCSI", + USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_euscsi_init, + US_FL_SCM_MULT_TARG ), + /* Reported by Adrian Pilchowiec <adi1981@epf.pl> */ UNUSUAL_DEV( 0x071b, 0x3203, 0x0000, 0x0000, "RockChip", diff --git a/drivers/usb/usbip/Kconfig b/drivers/usb/usbip/Kconfig new file mode 100644 index 000000000000..bd99e9e47e50 --- /dev/null +++ b/drivers/usb/usbip/Kconfig @@ -0,0 +1,41 @@ +config USBIP_CORE + tristate "USB/IP support" + depends on USB && NET + ---help--- + This enables pushing USB packets over IP to allow remote + machines direct access to USB devices. It provides the + USB/IP core that is required by both drivers. + + For more details, and to get the userspace utility + programs, please see <http://usbip.sourceforge.net/>. + + To compile this as a module, choose M here: the module will + be called usbip-core. + + If unsure, say N. + +config USBIP_VHCI_HCD + tristate "VHCI hcd" + depends on USBIP_CORE + ---help--- + This enables the USB/IP virtual host controller driver, + which is run on the remote machine. + + To compile this driver as a module, choose M here: the + module will be called vhci-hcd. + +config USBIP_HOST + tristate "Host driver" + depends on USBIP_CORE + ---help--- + This enables the USB/IP host driver, which is run on the + machine that is sharing the USB devices. + + To compile this driver as a module, choose M here: the + module will be called usbip-host. + +config USBIP_DEBUG + bool "Debug messages for USB/IP" + depends on USBIP_CORE + ---help--- + This enables the debug messages from the USB/IP drivers. diff --git a/drivers/usb/usbip/Makefile b/drivers/usb/usbip/Makefile new file mode 100644 index 000000000000..9ecd61545be1 --- /dev/null +++ b/drivers/usb/usbip/Makefile @@ -0,0 +1,10 @@ +ccflags-$(CONFIG_USBIP_DEBUG) := -DDEBUG + +obj-$(CONFIG_USBIP_CORE) += usbip-core.o +usbip-core-y := usbip_common.o usbip_event.o + +obj-$(CONFIG_USBIP_VHCI_HCD) += vhci-hcd.o +vhci-hcd-y := vhci_sysfs.o vhci_tx.o vhci_rx.o vhci_hcd.o + +obj-$(CONFIG_USBIP_HOST) += usbip-host.o +usbip-host-y := stub_dev.o stub_main.o stub_rx.o stub_tx.o diff --git a/drivers/usb/usbip/README b/drivers/usb/usbip/README new file mode 100644 index 000000000000..41a2cf2e77a6 --- /dev/null +++ b/drivers/usb/usbip/README @@ -0,0 +1,7 @@ +TODO: + - more discussion about the protocol + - testing + - review of the userspace interface + - document the protocol + +Please send patches for this code to Greg Kroah-Hartman <greg@kroah.com> diff --git a/drivers/usb/usbip/stub.h b/drivers/usb/usbip/stub.h new file mode 100644 index 000000000000..266e2b0ce9a8 --- /dev/null +++ b/drivers/usb/usbip/stub.h @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2003-2008 Takahiro Hirofuchi + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#ifndef __USBIP_STUB_H +#define __USBIP_STUB_H + +#include <linux/list.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include <linux/types.h> +#include <linux/usb.h> +#include <linux/wait.h> + +#define STUB_BUSID_OTHER 0 +#define STUB_BUSID_REMOV 1 +#define STUB_BUSID_ADDED 2 +#define STUB_BUSID_ALLOC 3 + +struct stub_device { + struct usb_interface *interface; + struct usb_device *udev; + + struct usbip_device ud; + __u32 devid; + + /* + * stub_priv preserves private data of each urb. + * It is allocated as stub_priv_cache and assigned to urb->context. + * + * stub_priv is always linked to any one of 3 lists; + * priv_init: linked to this until the comletion of a urb. + * priv_tx : linked to this after the completion of a urb. + * priv_free: linked to this after the sending of the result. + * + * Any of these list operations should be locked by priv_lock. + */ + spinlock_t priv_lock; + struct list_head priv_init; + struct list_head priv_tx; + struct list_head priv_free; + + /* see comments for unlinking in stub_rx.c */ + struct list_head unlink_tx; + struct list_head unlink_free; + + wait_queue_head_t tx_waitq; +}; + +/* private data into urb->priv */ +struct stub_priv { + unsigned long seqnum; + struct list_head list; + struct stub_device *sdev; + struct urb *urb; + + int unlinking; +}; + +struct stub_unlink { + unsigned long seqnum; + struct list_head list; + __u32 status; +}; + +/* same as SYSFS_BUS_ID_SIZE */ +#define BUSID_SIZE 32 + +struct bus_id_priv { + char name[BUSID_SIZE]; + char status; + int interf_count; + struct stub_device *sdev; + struct usb_device *udev; + char shutdown_busid; +}; + +/* stub_priv is allocated from stub_priv_cache */ +extern struct kmem_cache *stub_priv_cache; + +/* stub_dev.c */ +extern struct usb_device_driver stub_driver; + +/* stub_main.c */ +struct bus_id_priv *get_busid_priv(const char *busid); +int del_match_busid(char *busid); +void stub_device_cleanup_urbs(struct stub_device *sdev); + +/* stub_rx.c */ +int stub_rx_loop(void *data); + +/* stub_tx.c */ +void stub_enqueue_ret_unlink(struct stub_device *sdev, __u32 seqnum, + __u32 status); +void stub_complete(struct urb *urb); +int stub_tx_loop(void *data); + +#endif /* __USBIP_STUB_H */ diff --git a/drivers/usb/usbip/stub_dev.c b/drivers/usb/usbip/stub_dev.c new file mode 100644 index 000000000000..fac20e0434c0 --- /dev/null +++ b/drivers/usb/usbip/stub_dev.c @@ -0,0 +1,498 @@ +/* + * Copyright (C) 2003-2008 Takahiro Hirofuchi + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include <linux/device.h> +#include <linux/file.h> +#include <linux/kthread.h> +#include <linux/module.h> + +#include "usbip_common.h" +#include "stub.h" + +/* + * usbip_status shows the status of usbip-host as long as this driver is bound + * to the target device. + */ +static ssize_t usbip_status_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct stub_device *sdev = dev_get_drvdata(dev); + int status; + + if (!sdev) { + dev_err(dev, "sdev is null\n"); + return -ENODEV; + } + + spin_lock_irq(&sdev->ud.lock); + status = sdev->ud.status; + spin_unlock_irq(&sdev->ud.lock); + + return snprintf(buf, PAGE_SIZE, "%d\n", status); +} +static DEVICE_ATTR_RO(usbip_status); + +/* + * usbip_sockfd gets a socket descriptor of an established TCP connection that + * is used to transfer usbip requests by kernel threads. -1 is a magic number + * by which usbip connection is finished. + */ +static ssize_t store_sockfd(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct stub_device *sdev = dev_get_drvdata(dev); + int sockfd = 0; + struct socket *socket; + int rv; + + if (!sdev) { + dev_err(dev, "sdev is null\n"); + return -ENODEV; + } + + rv = sscanf(buf, "%d", &sockfd); + if (rv != 1) + return -EINVAL; + + if (sockfd != -1) { + int err; + + dev_info(dev, "stub up\n"); + + spin_lock_irq(&sdev->ud.lock); + + if (sdev->ud.status != SDEV_ST_AVAILABLE) { + dev_err(dev, "not ready\n"); + goto err; + } + + socket = sockfd_lookup(sockfd, &err); + if (!socket) + goto err; + + sdev->ud.tcp_socket = socket; + + spin_unlock_irq(&sdev->ud.lock); + + sdev->ud.tcp_rx = kthread_get_run(stub_rx_loop, &sdev->ud, + "stub_rx"); + sdev->ud.tcp_tx = kthread_get_run(stub_tx_loop, &sdev->ud, + "stub_tx"); + + spin_lock_irq(&sdev->ud.lock); + sdev->ud.status = SDEV_ST_USED; + spin_unlock_irq(&sdev->ud.lock); + + } else { + dev_info(dev, "stub down\n"); + + spin_lock_irq(&sdev->ud.lock); + if (sdev->ud.status != SDEV_ST_USED) + goto err; + + spin_unlock_irq(&sdev->ud.lock); + + usbip_event_add(&sdev->ud, SDEV_EVENT_DOWN); + } + + return count; + +err: + spin_unlock_irq(&sdev->ud.lock); + return -EINVAL; +} +static DEVICE_ATTR(usbip_sockfd, S_IWUSR, NULL, store_sockfd); + +static int stub_add_files(struct device *dev) +{ + int err = 0; + + err = device_create_file(dev, &dev_attr_usbip_status); + if (err) + goto err_status; + + err = device_create_file(dev, &dev_attr_usbip_sockfd); + if (err) + goto err_sockfd; + + err = device_create_file(dev, &dev_attr_usbip_debug); + if (err) + goto err_debug; + + return 0; + +err_debug: + device_remove_file(dev, &dev_attr_usbip_sockfd); +err_sockfd: + device_remove_file(dev, &dev_attr_usbip_status); +err_status: + return err; +} + +static void stub_remove_files(struct device *dev) +{ + device_remove_file(dev, &dev_attr_usbip_status); + device_remove_file(dev, &dev_attr_usbip_sockfd); + device_remove_file(dev, &dev_attr_usbip_debug); +} + +static void stub_shutdown_connection(struct usbip_device *ud) +{ + struct stub_device *sdev = container_of(ud, struct stub_device, ud); + + /* + * When removing an exported device, kernel panic sometimes occurred + * and then EIP was sk_wait_data of stub_rx thread. Is this because + * sk_wait_data returned though stub_rx thread was already finished by + * step 1? + */ + if (ud->tcp_socket) { + dev_dbg(&sdev->udev->dev, "shutdown tcp_socket %p\n", + ud->tcp_socket); + kernel_sock_shutdown(ud->tcp_socket, SHUT_RDWR); + } + + /* 1. stop threads */ + if (ud->tcp_rx) { + kthread_stop_put(ud->tcp_rx); + ud->tcp_rx = NULL; + } + if (ud->tcp_tx) { + kthread_stop_put(ud->tcp_tx); + ud->tcp_tx = NULL; + } + + /* + * 2. close the socket + * + * tcp_socket is freed after threads are killed so that usbip_xmit does + * not touch NULL socket. + */ + if (ud->tcp_socket) { + sockfd_put(ud->tcp_socket); + ud->tcp_socket = NULL; + } + + /* 3. free used data */ + stub_device_cleanup_urbs(sdev); + + /* 4. free stub_unlink */ + { + unsigned long flags; + struct stub_unlink *unlink, *tmp; + + spin_lock_irqsave(&sdev->priv_lock, flags); + list_for_each_entry_safe(unlink, tmp, &sdev->unlink_tx, list) { + list_del(&unlink->list); + kfree(unlink); + } + list_for_each_entry_safe(unlink, tmp, &sdev->unlink_free, + list) { + list_del(&unlink->list); + kfree(unlink); + } + spin_unlock_irqrestore(&sdev->priv_lock, flags); + } +} + +static void stub_device_reset(struct usbip_device *ud) +{ + struct stub_device *sdev = container_of(ud, struct stub_device, ud); + struct usb_device *udev = sdev->udev; + int ret; + + dev_dbg(&udev->dev, "device reset"); + + ret = usb_lock_device_for_reset(udev, sdev->interface); + if (ret < 0) { + dev_err(&udev->dev, "lock for reset\n"); + spin_lock_irq(&ud->lock); + ud->status = SDEV_ST_ERROR; + spin_unlock_irq(&ud->lock); + return; + } + + /* try to reset the device */ + ret = usb_reset_device(udev); + usb_unlock_device(udev); + + spin_lock_irq(&ud->lock); + if (ret) { + dev_err(&udev->dev, "device reset\n"); + ud->status = SDEV_ST_ERROR; + } else { + dev_info(&udev->dev, "device reset\n"); + ud->status = SDEV_ST_AVAILABLE; + } + spin_unlock_irq(&ud->lock); +} + +static void stub_device_unusable(struct usbip_device *ud) +{ + spin_lock_irq(&ud->lock); + ud->status = SDEV_ST_ERROR; + spin_unlock_irq(&ud->lock); +} + +/** + * stub_device_alloc - allocate a new stub_device struct + * @interface: usb_interface of a new device + * + * Allocates and initializes a new stub_device struct. + */ +static struct stub_device *stub_device_alloc(struct usb_device *udev) +{ + struct stub_device *sdev; + int busnum = udev->bus->busnum; + int devnum = udev->devnum; + + dev_dbg(&udev->dev, "allocating stub device"); + + /* yes, it's a new device */ + sdev = kzalloc(sizeof(struct stub_device), GFP_KERNEL); + if (!sdev) + return NULL; + + sdev->udev = usb_get_dev(udev); + + /* + * devid is defined with devnum when this driver is first allocated. + * devnum may change later if a device is reset. However, devid never + * changes during a usbip connection. + */ + sdev->devid = (busnum << 16) | devnum; + sdev->ud.side = USBIP_STUB; + sdev->ud.status = SDEV_ST_AVAILABLE; + spin_lock_init(&sdev->ud.lock); + sdev->ud.tcp_socket = NULL; + + INIT_LIST_HEAD(&sdev->priv_init); + INIT_LIST_HEAD(&sdev->priv_tx); + INIT_LIST_HEAD(&sdev->priv_free); + INIT_LIST_HEAD(&sdev->unlink_free); + INIT_LIST_HEAD(&sdev->unlink_tx); + spin_lock_init(&sdev->priv_lock); + + init_waitqueue_head(&sdev->tx_waitq); + + sdev->ud.eh_ops.shutdown = stub_shutdown_connection; + sdev->ud.eh_ops.reset = stub_device_reset; + sdev->ud.eh_ops.unusable = stub_device_unusable; + + usbip_start_eh(&sdev->ud); + + dev_dbg(&udev->dev, "register new device\n"); + + return sdev; +} + +static void stub_device_free(struct stub_device *sdev) +{ + kfree(sdev); +} + +static int stub_probe(struct usb_device *udev) +{ + struct stub_device *sdev = NULL; + const char *udev_busid = dev_name(&udev->dev); + int err = 0; + struct bus_id_priv *busid_priv; + int rc; + + dev_dbg(&udev->dev, "Enter\n"); + + /* check we should claim or not by busid_table */ + busid_priv = get_busid_priv(udev_busid); + if (!busid_priv || (busid_priv->status == STUB_BUSID_REMOV) || + (busid_priv->status == STUB_BUSID_OTHER)) { + dev_info(&udev->dev, + "%s is not in match_busid table... skip!\n", + udev_busid); + + /* + * Return value should be ENODEV or ENOXIO to continue trying + * other matched drivers by the driver core. + * See driver_probe_device() in driver/base/dd.c + */ + return -ENODEV; + } + + if (udev->descriptor.bDeviceClass == USB_CLASS_HUB) { + dev_dbg(&udev->dev, "%s is a usb hub device... skip!\n", + udev_busid); + return -ENODEV; + } + + if (!strcmp(udev->bus->bus_name, "vhci_hcd")) { + dev_dbg(&udev->dev, + "%s is attached on vhci_hcd... skip!\n", + udev_busid); + + return -ENODEV; + } + + /* ok, this is my device */ + sdev = stub_device_alloc(udev); + if (!sdev) + return -ENOMEM; + + dev_info(&udev->dev, + "usbip-host: register new device (bus %u dev %u)\n", + udev->bus->busnum, udev->devnum); + + busid_priv->shutdown_busid = 0; + + /* set private data to usb_device */ + dev_set_drvdata(&udev->dev, sdev); + busid_priv->sdev = sdev; + busid_priv->udev = udev; + + /* + * Claim this hub port. + * It doesn't matter what value we pass as owner + * (struct dev_state) as long as it is unique. + */ + rc = usb_hub_claim_port(udev->parent, udev->portnum, + (struct usb_dev_state *) udev); + if (rc) { + dev_dbg(&udev->dev, "unable to claim port\n"); + return rc; + } + + err = stub_add_files(&udev->dev); + if (err) { + dev_err(&udev->dev, "stub_add_files for %s\n", udev_busid); + dev_set_drvdata(&udev->dev, NULL); + usb_put_dev(udev); + kthread_stop_put(sdev->ud.eh); + + busid_priv->sdev = NULL; + stub_device_free(sdev); + return err; + } + busid_priv->status = STUB_BUSID_ALLOC; + + return 0; +} + +static void shutdown_busid(struct bus_id_priv *busid_priv) +{ + if (busid_priv->sdev && !busid_priv->shutdown_busid) { + busid_priv->shutdown_busid = 1; + usbip_event_add(&busid_priv->sdev->ud, SDEV_EVENT_REMOVED); + + /* wait for the stop of the event handler */ + usbip_stop_eh(&busid_priv->sdev->ud); + } +} + +/* + * called in usb_disconnect() or usb_deregister() + * but only if actconfig(active configuration) exists + */ +static void stub_disconnect(struct usb_device *udev) +{ + struct stub_device *sdev; + const char *udev_busid = dev_name(&udev->dev); + struct bus_id_priv *busid_priv; + int rc; + + dev_dbg(&udev->dev, "Enter\n"); + + busid_priv = get_busid_priv(udev_busid); + if (!busid_priv) { + BUG(); + return; + } + + sdev = dev_get_drvdata(&udev->dev); + + /* get stub_device */ + if (!sdev) { + dev_err(&udev->dev, "could not get device"); + return; + } + + dev_set_drvdata(&udev->dev, NULL); + + /* + * NOTE: rx/tx threads are invoked for each usb_device. + */ + stub_remove_files(&udev->dev); + + /* release port */ + rc = usb_hub_release_port(udev->parent, udev->portnum, + (struct usb_dev_state *) udev); + if (rc) { + dev_dbg(&udev->dev, "unable to release port\n"); + return; + } + + /* If usb reset is called from event handler */ + if (busid_priv->sdev->ud.eh == current) + return; + + /* shutdown the current connection */ + shutdown_busid(busid_priv); + + usb_put_dev(sdev->udev); + + /* free sdev */ + busid_priv->sdev = NULL; + stub_device_free(sdev); + + if (busid_priv->status == STUB_BUSID_ALLOC) { + busid_priv->status = STUB_BUSID_ADDED; + } else { + busid_priv->status = STUB_BUSID_OTHER; + del_match_busid((char *)udev_busid); + } +} + +#ifdef CONFIG_PM + +/* These functions need usb_port_suspend and usb_port_resume, + * which reside in drivers/usb/core/usb.h. Skip for now. */ + +static int stub_suspend(struct usb_device *udev, pm_message_t message) +{ + dev_dbg(&udev->dev, "stub_suspend\n"); + + return 0; +} + +static int stub_resume(struct usb_device *udev, pm_message_t message) +{ + dev_dbg(&udev->dev, "stub_resume\n"); + + return 0; +} + +#endif /* CONFIG_PM */ + +struct usb_device_driver stub_driver = { + .name = "usbip-host", + .probe = stub_probe, + .disconnect = stub_disconnect, +#ifdef CONFIG_PM + .suspend = stub_suspend, + .resume = stub_resume, +#endif + .supports_autosuspend = 0, +}; diff --git a/drivers/usb/usbip/stub_main.c b/drivers/usb/usbip/stub_main.c new file mode 100644 index 000000000000..44ab43fc4fcc --- /dev/null +++ b/drivers/usb/usbip/stub_main.c @@ -0,0 +1,335 @@ +/* + * Copyright (C) 2003-2008 Takahiro Hirofuchi + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include <linux/string.h> +#include <linux/module.h> +#include <linux/device.h> + +#include "usbip_common.h" +#include "stub.h" + +#define DRIVER_AUTHOR "Takahiro Hirofuchi" +#define DRIVER_DESC "USB/IP Host Driver" + +struct kmem_cache *stub_priv_cache; +/* + * busid_tables defines matching busids that usbip can grab. A user can change + * dynamically what device is locally used and what device is exported to a + * remote host. + */ +#define MAX_BUSID 16 +static struct bus_id_priv busid_table[MAX_BUSID]; +static spinlock_t busid_table_lock; + +static void init_busid_table(void) +{ + /* + * This also sets the bus_table[i].status to + * STUB_BUSID_OTHER, which is 0. + */ + memset(busid_table, 0, sizeof(busid_table)); + + spin_lock_init(&busid_table_lock); +} + +/* + * Find the index of the busid by name. + * Must be called with busid_table_lock held. + */ +static int get_busid_idx(const char *busid) +{ + int i; + int idx = -1; + + for (i = 0; i < MAX_BUSID; i++) + if (busid_table[i].name[0]) + if (!strncmp(busid_table[i].name, busid, BUSID_SIZE)) { + idx = i; + break; + } + return idx; +} + +struct bus_id_priv *get_busid_priv(const char *busid) +{ + int idx; + struct bus_id_priv *bid = NULL; + + spin_lock(&busid_table_lock); + idx = get_busid_idx(busid); + if (idx >= 0) + bid = &(busid_table[idx]); + spin_unlock(&busid_table_lock); + + return bid; +} + +static int add_match_busid(char *busid) +{ + int i; + int ret = -1; + + spin_lock(&busid_table_lock); + /* already registered? */ + if (get_busid_idx(busid) >= 0) { + ret = 0; + goto out; + } + + for (i = 0; i < MAX_BUSID; i++) + if (!busid_table[i].name[0]) { + strlcpy(busid_table[i].name, busid, BUSID_SIZE); + if ((busid_table[i].status != STUB_BUSID_ALLOC) && + (busid_table[i].status != STUB_BUSID_REMOV)) + busid_table[i].status = STUB_BUSID_ADDED; + ret = 0; + break; + } + +out: + spin_unlock(&busid_table_lock); + + return ret; +} + +int del_match_busid(char *busid) +{ + int idx; + int ret = -1; + + spin_lock(&busid_table_lock); + idx = get_busid_idx(busid); + if (idx < 0) + goto out; + + /* found */ + ret = 0; + + if (busid_table[idx].status == STUB_BUSID_OTHER) + memset(busid_table[idx].name, 0, BUSID_SIZE); + + if ((busid_table[idx].status != STUB_BUSID_OTHER) && + (busid_table[idx].status != STUB_BUSID_ADDED)) + busid_table[idx].status = STUB_BUSID_REMOV; + +out: + spin_unlock(&busid_table_lock); + + return ret; +} + +static ssize_t show_match_busid(struct device_driver *drv, char *buf) +{ + int i; + char *out = buf; + + spin_lock(&busid_table_lock); + for (i = 0; i < MAX_BUSID; i++) + if (busid_table[i].name[0]) + out += sprintf(out, "%s ", busid_table[i].name); + spin_unlock(&busid_table_lock); + out += sprintf(out, "\n"); + + return out - buf; +} + +static ssize_t store_match_busid(struct device_driver *dev, const char *buf, + size_t count) +{ + int len; + char busid[BUSID_SIZE]; + + if (count < 5) + return -EINVAL; + + /* busid needs to include \0 termination */ + len = strlcpy(busid, buf + 4, BUSID_SIZE); + if (sizeof(busid) <= len) + return -EINVAL; + + if (!strncmp(buf, "add ", 4)) { + if (add_match_busid(busid) < 0) + return -ENOMEM; + + pr_debug("add busid %s\n", busid); + return count; + } + + if (!strncmp(buf, "del ", 4)) { + if (del_match_busid(busid) < 0) + return -ENODEV; + + pr_debug("del busid %s\n", busid); + return count; + } + + return -EINVAL; +} +static DRIVER_ATTR(match_busid, S_IRUSR | S_IWUSR, show_match_busid, + store_match_busid); + +static ssize_t rebind_store(struct device_driver *dev, const char *buf, + size_t count) +{ + int ret; + int len; + struct bus_id_priv *bid; + + /* buf length should be less that BUSID_SIZE */ + len = strnlen(buf, BUSID_SIZE); + + if (!(len < BUSID_SIZE)) + return -EINVAL; + + bid = get_busid_priv(buf); + if (!bid) + return -ENODEV; + + ret = device_attach(&bid->udev->dev); + if (ret < 0) { + dev_err(&bid->udev->dev, "rebind failed\n"); + return ret; + } + + return count; +} + +static DRIVER_ATTR_WO(rebind); + +static struct stub_priv *stub_priv_pop_from_listhead(struct list_head *listhead) +{ + struct stub_priv *priv, *tmp; + + list_for_each_entry_safe(priv, tmp, listhead, list) { + list_del(&priv->list); + return priv; + } + + return NULL; +} + +static struct stub_priv *stub_priv_pop(struct stub_device *sdev) +{ + unsigned long flags; + struct stub_priv *priv; + + spin_lock_irqsave(&sdev->priv_lock, flags); + + priv = stub_priv_pop_from_listhead(&sdev->priv_init); + if (priv) + goto done; + + priv = stub_priv_pop_from_listhead(&sdev->priv_tx); + if (priv) + goto done; + + priv = stub_priv_pop_from_listhead(&sdev->priv_free); + +done: + spin_unlock_irqrestore(&sdev->priv_lock, flags); + + return priv; +} + +void stub_device_cleanup_urbs(struct stub_device *sdev) +{ + struct stub_priv *priv; + struct urb *urb; + + dev_dbg(&sdev->udev->dev, "free sdev %p\n", sdev); + + while ((priv = stub_priv_pop(sdev))) { + urb = priv->urb; + dev_dbg(&sdev->udev->dev, "free urb %p\n", urb); + usb_kill_urb(urb); + + kmem_cache_free(stub_priv_cache, priv); + + kfree(urb->transfer_buffer); + kfree(urb->setup_packet); + usb_free_urb(urb); + } +} + +static int __init usbip_host_init(void) +{ + int ret; + + init_busid_table(); + + stub_priv_cache = KMEM_CACHE(stub_priv, SLAB_HWCACHE_ALIGN); + if (!stub_priv_cache) { + pr_err("kmem_cache_create failed\n"); + return -ENOMEM; + } + + ret = usb_register_device_driver(&stub_driver, THIS_MODULE); + if (ret) { + pr_err("usb_register failed %d\n", ret); + goto err_usb_register; + } + + ret = driver_create_file(&stub_driver.drvwrap.driver, + &driver_attr_match_busid); + if (ret) { + pr_err("driver_create_file failed\n"); + goto err_create_file; + } + + ret = driver_create_file(&stub_driver.drvwrap.driver, + &driver_attr_rebind); + if (ret) { + pr_err("driver_create_file failed\n"); + goto err_create_file; + } + + pr_info(DRIVER_DESC " v" USBIP_VERSION "\n"); + return ret; + +err_create_file: + usb_deregister_device_driver(&stub_driver); +err_usb_register: + kmem_cache_destroy(stub_priv_cache); + return ret; +} + +static void __exit usbip_host_exit(void) +{ + driver_remove_file(&stub_driver.drvwrap.driver, + &driver_attr_match_busid); + + driver_remove_file(&stub_driver.drvwrap.driver, + &driver_attr_rebind); + + /* + * deregister() calls stub_disconnect() for all devices. Device + * specific data is cleared in stub_disconnect(). + */ + usb_deregister_device_driver(&stub_driver); + + kmem_cache_destroy(stub_priv_cache); +} + +module_init(usbip_host_init); +module_exit(usbip_host_exit); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); +MODULE_VERSION(USBIP_VERSION); diff --git a/drivers/usb/usbip/stub_rx.c b/drivers/usb/usbip/stub_rx.c new file mode 100644 index 000000000000..00e475c51a12 --- /dev/null +++ b/drivers/usb/usbip/stub_rx.c @@ -0,0 +1,594 @@ +/* + * Copyright (C) 2003-2008 Takahiro Hirofuchi + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include <asm/byteorder.h> +#include <linux/kthread.h> +#include <linux/usb.h> +#include <linux/usb/hcd.h> + +#include "usbip_common.h" +#include "stub.h" + +static int is_clear_halt_cmd(struct urb *urb) +{ + struct usb_ctrlrequest *req; + + req = (struct usb_ctrlrequest *) urb->setup_packet; + + return (req->bRequest == USB_REQ_CLEAR_FEATURE) && + (req->bRequestType == USB_RECIP_ENDPOINT) && + (req->wValue == USB_ENDPOINT_HALT); +} + +static int is_set_interface_cmd(struct urb *urb) +{ + struct usb_ctrlrequest *req; + + req = (struct usb_ctrlrequest *) urb->setup_packet; + + return (req->bRequest == USB_REQ_SET_INTERFACE) && + (req->bRequestType == USB_RECIP_INTERFACE); +} + +static int is_set_configuration_cmd(struct urb *urb) +{ + struct usb_ctrlrequest *req; + + req = (struct usb_ctrlrequest *) urb->setup_packet; + + return (req->bRequest == USB_REQ_SET_CONFIGURATION) && + (req->bRequestType == USB_RECIP_DEVICE); +} + +static int is_reset_device_cmd(struct urb *urb) +{ + struct usb_ctrlrequest *req; + __u16 value; + __u16 index; + + req = (struct usb_ctrlrequest *) urb->setup_packet; + value = le16_to_cpu(req->wValue); + index = le16_to_cpu(req->wIndex); + + if ((req->bRequest == USB_REQ_SET_FEATURE) && + (req->bRequestType == USB_RT_PORT) && + (value == USB_PORT_FEAT_RESET)) { + usbip_dbg_stub_rx("reset_device_cmd, port %u\n", index); + return 1; + } else + return 0; +} + +static int tweak_clear_halt_cmd(struct urb *urb) +{ + struct usb_ctrlrequest *req; + int target_endp; + int target_dir; + int target_pipe; + int ret; + + req = (struct usb_ctrlrequest *) urb->setup_packet; + + /* + * The stalled endpoint is specified in the wIndex value. The endpoint + * of the urb is the target of this clear_halt request (i.e., control + * endpoint). + */ + target_endp = le16_to_cpu(req->wIndex) & 0x000f; + + /* the stalled endpoint direction is IN or OUT?. USB_DIR_IN is 0x80. */ + target_dir = le16_to_cpu(req->wIndex) & 0x0080; + + if (target_dir) + target_pipe = usb_rcvctrlpipe(urb->dev, target_endp); + else + target_pipe = usb_sndctrlpipe(urb->dev, target_endp); + + ret = usb_clear_halt(urb->dev, target_pipe); + if (ret < 0) + dev_err(&urb->dev->dev, + "usb_clear_halt error: devnum %d endp %d ret %d\n", + urb->dev->devnum, target_endp, ret); + else + dev_info(&urb->dev->dev, + "usb_clear_halt done: devnum %d endp %d\n", + urb->dev->devnum, target_endp); + + return ret; +} + +static int tweak_set_interface_cmd(struct urb *urb) +{ + struct usb_ctrlrequest *req; + __u16 alternate; + __u16 interface; + int ret; + + req = (struct usb_ctrlrequest *) urb->setup_packet; + alternate = le16_to_cpu(req->wValue); + interface = le16_to_cpu(req->wIndex); + + usbip_dbg_stub_rx("set_interface: inf %u alt %u\n", + interface, alternate); + + ret = usb_set_interface(urb->dev, interface, alternate); + if (ret < 0) + dev_err(&urb->dev->dev, + "usb_set_interface error: inf %u alt %u ret %d\n", + interface, alternate, ret); + else + dev_info(&urb->dev->dev, + "usb_set_interface done: inf %u alt %u\n", + interface, alternate); + + return ret; +} + +static int tweak_set_configuration_cmd(struct urb *urb) +{ + struct stub_priv *priv = (struct stub_priv *) urb->context; + struct stub_device *sdev = priv->sdev; + struct usb_ctrlrequest *req; + __u16 config; + int err; + + req = (struct usb_ctrlrequest *) urb->setup_packet; + config = le16_to_cpu(req->wValue); + + err = usb_set_configuration(sdev->udev, config); + if (err && err != -ENODEV) + dev_err(&sdev->udev->dev, "can't set config #%d, error %d\n", + config, err); + return 0; +} + +static int tweak_reset_device_cmd(struct urb *urb) +{ + struct stub_priv *priv = (struct stub_priv *) urb->context; + struct stub_device *sdev = priv->sdev; + + dev_info(&urb->dev->dev, "usb_queue_reset_device\n"); + + /* + * With the implementation of pre_reset and post_reset the driver no + * longer unbinds. This allows the use of synchronous reset. + */ + + if (usb_lock_device_for_reset(sdev->udev, sdev->interface) < 0) { + dev_err(&urb->dev->dev, "could not obtain lock to reset device\n"); + return 0; + } + usb_reset_device(sdev->udev); + usb_unlock_device(sdev->udev); + + return 0; +} + +/* + * clear_halt, set_interface, and set_configuration require special tricks. + */ +static void tweak_special_requests(struct urb *urb) +{ + if (!urb || !urb->setup_packet) + return; + + if (usb_pipetype(urb->pipe) != PIPE_CONTROL) + return; + + if (is_clear_halt_cmd(urb)) + /* tweak clear_halt */ + tweak_clear_halt_cmd(urb); + + else if (is_set_interface_cmd(urb)) + /* tweak set_interface */ + tweak_set_interface_cmd(urb); + + else if (is_set_configuration_cmd(urb)) + /* tweak set_configuration */ + tweak_set_configuration_cmd(urb); + + else if (is_reset_device_cmd(urb)) + tweak_reset_device_cmd(urb); + else + usbip_dbg_stub_rx("no need to tweak\n"); +} + +/* + * stub_recv_unlink() unlinks the URB by a call to usb_unlink_urb(). + * By unlinking the urb asynchronously, stub_rx can continuously + * process coming urbs. Even if the urb is unlinked, its completion + * handler will be called and stub_tx will send a return pdu. + * + * See also comments about unlinking strategy in vhci_hcd.c. + */ +static int stub_recv_cmd_unlink(struct stub_device *sdev, + struct usbip_header *pdu) +{ + int ret; + unsigned long flags; + struct stub_priv *priv; + + spin_lock_irqsave(&sdev->priv_lock, flags); + + list_for_each_entry(priv, &sdev->priv_init, list) { + if (priv->seqnum != pdu->u.cmd_unlink.seqnum) + continue; + + dev_info(&priv->urb->dev->dev, "unlink urb %p\n", + priv->urb); + + /* + * This matched urb is not completed yet (i.e., be in + * flight in usb hcd hardware/driver). Now we are + * cancelling it. The unlinking flag means that we are + * now not going to return the normal result pdu of a + * submission request, but going to return a result pdu + * of the unlink request. + */ + priv->unlinking = 1; + + /* + * In the case that unlinking flag is on, prev->seqnum + * is changed from the seqnum of the cancelling urb to + * the seqnum of the unlink request. This will be used + * to make the result pdu of the unlink request. + */ + priv->seqnum = pdu->base.seqnum; + + spin_unlock_irqrestore(&sdev->priv_lock, flags); + + /* + * usb_unlink_urb() is now out of spinlocking to avoid + * spinlock recursion since stub_complete() is + * sometimes called in this context but not in the + * interrupt context. If stub_complete() is executed + * before we call usb_unlink_urb(), usb_unlink_urb() + * will return an error value. In this case, stub_tx + * will return the result pdu of this unlink request + * though submission is completed and actual unlinking + * is not executed. OK? + */ + /* In the above case, urb->status is not -ECONNRESET, + * so a driver in a client host will know the failure + * of the unlink request ? + */ + ret = usb_unlink_urb(priv->urb); + if (ret != -EINPROGRESS) + dev_err(&priv->urb->dev->dev, + "failed to unlink a urb %p, ret %d\n", + priv->urb, ret); + + return 0; + } + + usbip_dbg_stub_rx("seqnum %d is not pending\n", + pdu->u.cmd_unlink.seqnum); + + /* + * The urb of the unlink target is not found in priv_init queue. It was + * already completed and its results is/was going to be sent by a + * CMD_RET pdu. In this case, usb_unlink_urb() is not needed. We only + * return the completeness of this unlink request to vhci_hcd. + */ + stub_enqueue_ret_unlink(sdev, pdu->base.seqnum, 0); + + spin_unlock_irqrestore(&sdev->priv_lock, flags); + + return 0; +} + +static int valid_request(struct stub_device *sdev, struct usbip_header *pdu) +{ + struct usbip_device *ud = &sdev->ud; + int valid = 0; + + if (pdu->base.devid == sdev->devid) { + spin_lock_irq(&ud->lock); + if (ud->status == SDEV_ST_USED) { + /* A request is valid. */ + valid = 1; + } + spin_unlock_irq(&ud->lock); + } + + return valid; +} + +static struct stub_priv *stub_priv_alloc(struct stub_device *sdev, + struct usbip_header *pdu) +{ + struct stub_priv *priv; + struct usbip_device *ud = &sdev->ud; + unsigned long flags; + + spin_lock_irqsave(&sdev->priv_lock, flags); + + priv = kmem_cache_zalloc(stub_priv_cache, GFP_ATOMIC); + if (!priv) { + dev_err(&sdev->interface->dev, "alloc stub_priv\n"); + spin_unlock_irqrestore(&sdev->priv_lock, flags); + usbip_event_add(ud, SDEV_EVENT_ERROR_MALLOC); + return NULL; + } + + priv->seqnum = pdu->base.seqnum; + priv->sdev = sdev; + + /* + * After a stub_priv is linked to a list_head, + * our error handler can free allocated data. + */ + list_add_tail(&priv->list, &sdev->priv_init); + + spin_unlock_irqrestore(&sdev->priv_lock, flags); + + return priv; +} + +static int get_pipe(struct stub_device *sdev, int epnum, int dir) +{ + struct usb_device *udev = sdev->udev; + struct usb_host_endpoint *ep; + struct usb_endpoint_descriptor *epd = NULL; + + if (dir == USBIP_DIR_IN) + ep = udev->ep_in[epnum & 0x7f]; + else + ep = udev->ep_out[epnum & 0x7f]; + if (!ep) { + dev_err(&sdev->interface->dev, "no such endpoint?, %d\n", + epnum); + BUG(); + } + + epd = &ep->desc; + if (usb_endpoint_xfer_control(epd)) { + if (dir == USBIP_DIR_OUT) + return usb_sndctrlpipe(udev, epnum); + else + return usb_rcvctrlpipe(udev, epnum); + } + + if (usb_endpoint_xfer_bulk(epd)) { + if (dir == USBIP_DIR_OUT) + return usb_sndbulkpipe(udev, epnum); + else + return usb_rcvbulkpipe(udev, epnum); + } + + if (usb_endpoint_xfer_int(epd)) { + if (dir == USBIP_DIR_OUT) + return usb_sndintpipe(udev, epnum); + else + return usb_rcvintpipe(udev, epnum); + } + + if (usb_endpoint_xfer_isoc(epd)) { + if (dir == USBIP_DIR_OUT) + return usb_sndisocpipe(udev, epnum); + else + return usb_rcvisocpipe(udev, epnum); + } + + /* NOT REACHED */ + dev_err(&sdev->interface->dev, "get pipe, epnum %d\n", epnum); + return 0; +} + +static void masking_bogus_flags(struct urb *urb) +{ + int xfertype; + struct usb_device *dev; + struct usb_host_endpoint *ep; + int is_out; + unsigned int allowed; + + if (!urb || urb->hcpriv || !urb->complete) + return; + dev = urb->dev; + if ((!dev) || (dev->state < USB_STATE_UNAUTHENTICATED)) + return; + + ep = (usb_pipein(urb->pipe) ? dev->ep_in : dev->ep_out) + [usb_pipeendpoint(urb->pipe)]; + if (!ep) + return; + + xfertype = usb_endpoint_type(&ep->desc); + if (xfertype == USB_ENDPOINT_XFER_CONTROL) { + struct usb_ctrlrequest *setup = + (struct usb_ctrlrequest *) urb->setup_packet; + + if (!setup) + return; + is_out = !(setup->bRequestType & USB_DIR_IN) || + !setup->wLength; + } else { + is_out = usb_endpoint_dir_out(&ep->desc); + } + + /* enforce simple/standard policy */ + allowed = (URB_NO_TRANSFER_DMA_MAP | URB_NO_INTERRUPT | + URB_DIR_MASK | URB_FREE_BUFFER); + switch (xfertype) { + case USB_ENDPOINT_XFER_BULK: + if (is_out) + allowed |= URB_ZERO_PACKET; + /* FALLTHROUGH */ + case USB_ENDPOINT_XFER_CONTROL: + allowed |= URB_NO_FSBR; /* only affects UHCI */ + /* FALLTHROUGH */ + default: /* all non-iso endpoints */ + if (!is_out) + allowed |= URB_SHORT_NOT_OK; + break; + case USB_ENDPOINT_XFER_ISOC: + allowed |= URB_ISO_ASAP; + break; + } + urb->transfer_flags &= allowed; +} + +static void stub_recv_cmd_submit(struct stub_device *sdev, + struct usbip_header *pdu) +{ + int ret; + struct stub_priv *priv; + struct usbip_device *ud = &sdev->ud; + struct usb_device *udev = sdev->udev; + int pipe = get_pipe(sdev, pdu->base.ep, pdu->base.direction); + + priv = stub_priv_alloc(sdev, pdu); + if (!priv) + return; + + /* setup a urb */ + if (usb_pipeisoc(pipe)) + priv->urb = usb_alloc_urb(pdu->u.cmd_submit.number_of_packets, + GFP_KERNEL); + else + priv->urb = usb_alloc_urb(0, GFP_KERNEL); + + if (!priv->urb) { + dev_err(&sdev->interface->dev, "malloc urb\n"); + usbip_event_add(ud, SDEV_EVENT_ERROR_MALLOC); + return; + } + + /* allocate urb transfer buffer, if needed */ + if (pdu->u.cmd_submit.transfer_buffer_length > 0) { + priv->urb->transfer_buffer = + kzalloc(pdu->u.cmd_submit.transfer_buffer_length, + GFP_KERNEL); + if (!priv->urb->transfer_buffer) { + usbip_event_add(ud, SDEV_EVENT_ERROR_MALLOC); + return; + } + } + + /* copy urb setup packet */ + priv->urb->setup_packet = kmemdup(&pdu->u.cmd_submit.setup, 8, + GFP_KERNEL); + if (!priv->urb->setup_packet) { + dev_err(&sdev->interface->dev, "allocate setup_packet\n"); + usbip_event_add(ud, SDEV_EVENT_ERROR_MALLOC); + return; + } + + /* set other members from the base header of pdu */ + priv->urb->context = (void *) priv; + priv->urb->dev = udev; + priv->urb->pipe = pipe; + priv->urb->complete = stub_complete; + + usbip_pack_pdu(pdu, priv->urb, USBIP_CMD_SUBMIT, 0); + + + if (usbip_recv_xbuff(ud, priv->urb) < 0) + return; + + if (usbip_recv_iso(ud, priv->urb) < 0) + return; + + /* no need to submit an intercepted request, but harmless? */ + tweak_special_requests(priv->urb); + + masking_bogus_flags(priv->urb); + /* urb is now ready to submit */ + ret = usb_submit_urb(priv->urb, GFP_KERNEL); + + if (ret == 0) + usbip_dbg_stub_rx("submit urb ok, seqnum %u\n", + pdu->base.seqnum); + else { + dev_err(&sdev->interface->dev, "submit_urb error, %d\n", ret); + usbip_dump_header(pdu); + usbip_dump_urb(priv->urb); + + /* + * Pessimistic. + * This connection will be discarded. + */ + usbip_event_add(ud, SDEV_EVENT_ERROR_SUBMIT); + } + + usbip_dbg_stub_rx("Leave\n"); +} + +/* recv a pdu */ +static void stub_rx_pdu(struct usbip_device *ud) +{ + int ret; + struct usbip_header pdu; + struct stub_device *sdev = container_of(ud, struct stub_device, ud); + struct device *dev = &sdev->udev->dev; + + usbip_dbg_stub_rx("Enter\n"); + + memset(&pdu, 0, sizeof(pdu)); + + /* receive a pdu header */ + ret = usbip_recv(ud->tcp_socket, &pdu, sizeof(pdu)); + if (ret != sizeof(pdu)) { + dev_err(dev, "recv a header, %d\n", ret); + usbip_event_add(ud, SDEV_EVENT_ERROR_TCP); + return; + } + + usbip_header_correct_endian(&pdu, 0); + + if (usbip_dbg_flag_stub_rx) + usbip_dump_header(&pdu); + + if (!valid_request(sdev, &pdu)) { + dev_err(dev, "recv invalid request\n"); + usbip_event_add(ud, SDEV_EVENT_ERROR_TCP); + return; + } + + switch (pdu.base.command) { + case USBIP_CMD_UNLINK: + stub_recv_cmd_unlink(sdev, &pdu); + break; + + case USBIP_CMD_SUBMIT: + stub_recv_cmd_submit(sdev, &pdu); + break; + + default: + /* NOTREACHED */ + dev_err(dev, "unknown pdu\n"); + usbip_event_add(ud, SDEV_EVENT_ERROR_TCP); + break; + } +} + +int stub_rx_loop(void *data) +{ + struct usbip_device *ud = data; + + while (!kthread_should_stop()) { + if (usbip_event_happened(ud)) + break; + + stub_rx_pdu(ud); + } + + return 0; +} diff --git a/drivers/usb/usbip/stub_tx.c b/drivers/usb/usbip/stub_tx.c new file mode 100644 index 000000000000..dbcabc9dbe0d --- /dev/null +++ b/drivers/usb/usbip/stub_tx.c @@ -0,0 +1,398 @@ +/* + * Copyright (C) 2003-2008 Takahiro Hirofuchi + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include <linux/kthread.h> +#include <linux/socket.h> + +#include "usbip_common.h" +#include "stub.h" + +static void stub_free_priv_and_urb(struct stub_priv *priv) +{ + struct urb *urb = priv->urb; + + kfree(urb->setup_packet); + kfree(urb->transfer_buffer); + list_del(&priv->list); + kmem_cache_free(stub_priv_cache, priv); + usb_free_urb(urb); +} + +/* be in spin_lock_irqsave(&sdev->priv_lock, flags) */ +void stub_enqueue_ret_unlink(struct stub_device *sdev, __u32 seqnum, + __u32 status) +{ + struct stub_unlink *unlink; + + unlink = kzalloc(sizeof(struct stub_unlink), GFP_ATOMIC); + if (!unlink) { + usbip_event_add(&sdev->ud, VDEV_EVENT_ERROR_MALLOC); + return; + } + + unlink->seqnum = seqnum; + unlink->status = status; + + list_add_tail(&unlink->list, &sdev->unlink_tx); +} + +/** + * stub_complete - completion handler of a usbip urb + * @urb: pointer to the urb completed + * + * When a urb has completed, the USB core driver calls this function mostly in + * the interrupt context. To return the result of a urb, the completed urb is + * linked to the pending list of returning. + * + */ +void stub_complete(struct urb *urb) +{ + struct stub_priv *priv = (struct stub_priv *) urb->context; + struct stub_device *sdev = priv->sdev; + unsigned long flags; + + usbip_dbg_stub_tx("complete! status %d\n", urb->status); + + switch (urb->status) { + case 0: + /* OK */ + break; + case -ENOENT: + dev_info(&urb->dev->dev, + "stopped by a call to usb_kill_urb() because of cleaning up a virtual connection\n"); + return; + case -ECONNRESET: + dev_info(&urb->dev->dev, + "unlinked by a call to usb_unlink_urb()\n"); + break; + case -EPIPE: + dev_info(&urb->dev->dev, "endpoint %d is stalled\n", + usb_pipeendpoint(urb->pipe)); + break; + case -ESHUTDOWN: + dev_info(&urb->dev->dev, "device removed?\n"); + break; + default: + dev_info(&urb->dev->dev, + "urb completion with non-zero status %d\n", + urb->status); + break; + } + + /* link a urb to the queue of tx. */ + spin_lock_irqsave(&sdev->priv_lock, flags); + if (priv->unlinking) { + stub_enqueue_ret_unlink(sdev, priv->seqnum, urb->status); + stub_free_priv_and_urb(priv); + } else { + list_move_tail(&priv->list, &sdev->priv_tx); + } + spin_unlock_irqrestore(&sdev->priv_lock, flags); + + /* wake up tx_thread */ + wake_up(&sdev->tx_waitq); +} + +static inline void setup_base_pdu(struct usbip_header_basic *base, + __u32 command, __u32 seqnum) +{ + base->command = command; + base->seqnum = seqnum; + base->devid = 0; + base->ep = 0; + base->direction = 0; +} + +static void setup_ret_submit_pdu(struct usbip_header *rpdu, struct urb *urb) +{ + struct stub_priv *priv = (struct stub_priv *) urb->context; + + setup_base_pdu(&rpdu->base, USBIP_RET_SUBMIT, priv->seqnum); + usbip_pack_pdu(rpdu, urb, USBIP_RET_SUBMIT, 1); +} + +static void setup_ret_unlink_pdu(struct usbip_header *rpdu, + struct stub_unlink *unlink) +{ + setup_base_pdu(&rpdu->base, USBIP_RET_UNLINK, unlink->seqnum); + rpdu->u.ret_unlink.status = unlink->status; +} + +static struct stub_priv *dequeue_from_priv_tx(struct stub_device *sdev) +{ + unsigned long flags; + struct stub_priv *priv, *tmp; + + spin_lock_irqsave(&sdev->priv_lock, flags); + + list_for_each_entry_safe(priv, tmp, &sdev->priv_tx, list) { + list_move_tail(&priv->list, &sdev->priv_free); + spin_unlock_irqrestore(&sdev->priv_lock, flags); + return priv; + } + + spin_unlock_irqrestore(&sdev->priv_lock, flags); + + return NULL; +} + +static int stub_send_ret_submit(struct stub_device *sdev) +{ + unsigned long flags; + struct stub_priv *priv, *tmp; + + struct msghdr msg; + size_t txsize; + + size_t total_size = 0; + + while ((priv = dequeue_from_priv_tx(sdev)) != NULL) { + int ret; + struct urb *urb = priv->urb; + struct usbip_header pdu_header; + struct usbip_iso_packet_descriptor *iso_buffer = NULL; + struct kvec *iov = NULL; + int iovnum = 0; + + txsize = 0; + memset(&pdu_header, 0, sizeof(pdu_header)); + memset(&msg, 0, sizeof(msg)); + + if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) + iovnum = 2 + urb->number_of_packets; + else + iovnum = 2; + + iov = kcalloc(iovnum, sizeof(struct kvec), GFP_KERNEL); + + if (!iov) { + usbip_event_add(&sdev->ud, SDEV_EVENT_ERROR_MALLOC); + return -1; + } + + iovnum = 0; + + /* 1. setup usbip_header */ + setup_ret_submit_pdu(&pdu_header, urb); + usbip_dbg_stub_tx("setup txdata seqnum: %d urb: %p\n", + pdu_header.base.seqnum, urb); + usbip_header_correct_endian(&pdu_header, 1); + + iov[iovnum].iov_base = &pdu_header; + iov[iovnum].iov_len = sizeof(pdu_header); + iovnum++; + txsize += sizeof(pdu_header); + + /* 2. setup transfer buffer */ + if (usb_pipein(urb->pipe) && + usb_pipetype(urb->pipe) != PIPE_ISOCHRONOUS && + urb->actual_length > 0) { + iov[iovnum].iov_base = urb->transfer_buffer; + iov[iovnum].iov_len = urb->actual_length; + iovnum++; + txsize += urb->actual_length; + } else if (usb_pipein(urb->pipe) && + usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { + /* + * For isochronous packets: actual length is the sum of + * the actual length of the individual, packets, but as + * the packet offsets are not changed there will be + * padding between the packets. To optimally use the + * bandwidth the padding is not transmitted. + */ + + int i; + + for (i = 0; i < urb->number_of_packets; i++) { + iov[iovnum].iov_base = urb->transfer_buffer + + urb->iso_frame_desc[i].offset; + iov[iovnum].iov_len = + urb->iso_frame_desc[i].actual_length; + iovnum++; + txsize += urb->iso_frame_desc[i].actual_length; + } + + if (txsize != sizeof(pdu_header) + urb->actual_length) { + dev_err(&sdev->interface->dev, + "actual length of urb %d does not match iso packet sizes %zu\n", + urb->actual_length, + txsize-sizeof(pdu_header)); + kfree(iov); + usbip_event_add(&sdev->ud, + SDEV_EVENT_ERROR_TCP); + return -1; + } + } + + /* 3. setup iso_packet_descriptor */ + if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { + ssize_t len = 0; + + iso_buffer = usbip_alloc_iso_desc_pdu(urb, &len); + if (!iso_buffer) { + usbip_event_add(&sdev->ud, + SDEV_EVENT_ERROR_MALLOC); + kfree(iov); + return -1; + } + + iov[iovnum].iov_base = iso_buffer; + iov[iovnum].iov_len = len; + txsize += len; + iovnum++; + } + + ret = kernel_sendmsg(sdev->ud.tcp_socket, &msg, + iov, iovnum, txsize); + if (ret != txsize) { + dev_err(&sdev->interface->dev, + "sendmsg failed!, retval %d for %zd\n", + ret, txsize); + kfree(iov); + kfree(iso_buffer); + usbip_event_add(&sdev->ud, SDEV_EVENT_ERROR_TCP); + return -1; + } + + kfree(iov); + kfree(iso_buffer); + + total_size += txsize; + } + + spin_lock_irqsave(&sdev->priv_lock, flags); + list_for_each_entry_safe(priv, tmp, &sdev->priv_free, list) { + stub_free_priv_and_urb(priv); + } + spin_unlock_irqrestore(&sdev->priv_lock, flags); + + return total_size; +} + +static struct stub_unlink *dequeue_from_unlink_tx(struct stub_device *sdev) +{ + unsigned long flags; + struct stub_unlink *unlink, *tmp; + + spin_lock_irqsave(&sdev->priv_lock, flags); + + list_for_each_entry_safe(unlink, tmp, &sdev->unlink_tx, list) { + list_move_tail(&unlink->list, &sdev->unlink_free); + spin_unlock_irqrestore(&sdev->priv_lock, flags); + return unlink; + } + + spin_unlock_irqrestore(&sdev->priv_lock, flags); + + return NULL; +} + +static int stub_send_ret_unlink(struct stub_device *sdev) +{ + unsigned long flags; + struct stub_unlink *unlink, *tmp; + + struct msghdr msg; + struct kvec iov[1]; + size_t txsize; + + size_t total_size = 0; + + while ((unlink = dequeue_from_unlink_tx(sdev)) != NULL) { + int ret; + struct usbip_header pdu_header; + + txsize = 0; + memset(&pdu_header, 0, sizeof(pdu_header)); + memset(&msg, 0, sizeof(msg)); + memset(&iov, 0, sizeof(iov)); + + usbip_dbg_stub_tx("setup ret unlink %lu\n", unlink->seqnum); + + /* 1. setup usbip_header */ + setup_ret_unlink_pdu(&pdu_header, unlink); + usbip_header_correct_endian(&pdu_header, 1); + + iov[0].iov_base = &pdu_header; + iov[0].iov_len = sizeof(pdu_header); + txsize += sizeof(pdu_header); + + ret = kernel_sendmsg(sdev->ud.tcp_socket, &msg, iov, + 1, txsize); + if (ret != txsize) { + dev_err(&sdev->interface->dev, + "sendmsg failed!, retval %d for %zd\n", + ret, txsize); + usbip_event_add(&sdev->ud, SDEV_EVENT_ERROR_TCP); + return -1; + } + + usbip_dbg_stub_tx("send txdata\n"); + total_size += txsize; + } + + spin_lock_irqsave(&sdev->priv_lock, flags); + + list_for_each_entry_safe(unlink, tmp, &sdev->unlink_free, list) { + list_del(&unlink->list); + kfree(unlink); + } + + spin_unlock_irqrestore(&sdev->priv_lock, flags); + + return total_size; +} + +int stub_tx_loop(void *data) +{ + struct usbip_device *ud = data; + struct stub_device *sdev = container_of(ud, struct stub_device, ud); + + while (!kthread_should_stop()) { + if (usbip_event_happened(ud)) + break; + + /* + * send_ret_submit comes earlier than send_ret_unlink. stub_rx + * looks at only priv_init queue. If the completion of a URB is + * earlier than the receive of CMD_UNLINK, priv is moved to + * priv_tx queue and stub_rx does not find the target priv. In + * this case, vhci_rx receives the result of the submit request + * and then receives the result of the unlink request. The + * result of the submit is given back to the usbcore as the + * completion of the unlink request. The request of the + * unlink is ignored. This is ok because a driver who calls + * usb_unlink_urb() understands the unlink was too late by + * getting the status of the given-backed URB which has the + * status of usb_submit_urb(). + */ + if (stub_send_ret_submit(sdev) < 0) + break; + + if (stub_send_ret_unlink(sdev) < 0) + break; + + wait_event_interruptible(sdev->tx_waitq, + (!list_empty(&sdev->priv_tx) || + !list_empty(&sdev->unlink_tx) || + kthread_should_stop())); + } + + return 0; +} diff --git a/drivers/usb/usbip/usbip_common.c b/drivers/usb/usbip/usbip_common.c new file mode 100644 index 000000000000..facaaf003f19 --- /dev/null +++ b/drivers/usb/usbip/usbip_common.c @@ -0,0 +1,776 @@ +/* + * Copyright (C) 2003-2008 Takahiro Hirofuchi + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include <asm/byteorder.h> +#include <linux/file.h> +#include <linux/fs.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/stat.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <net/sock.h> + +#include "usbip_common.h" + +#define DRIVER_AUTHOR "Takahiro Hirofuchi <hirofuchi@users.sourceforge.net>" +#define DRIVER_DESC "USB/IP Core" + +#ifdef CONFIG_USBIP_DEBUG +unsigned long usbip_debug_flag = 0xffffffff; +#else +unsigned long usbip_debug_flag; +#endif +EXPORT_SYMBOL_GPL(usbip_debug_flag); +module_param(usbip_debug_flag, ulong, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(usbip_debug_flag, "debug flags (defined in usbip_common.h)"); + +/* FIXME */ +struct device_attribute dev_attr_usbip_debug; +EXPORT_SYMBOL_GPL(dev_attr_usbip_debug); + +static ssize_t usbip_debug_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%lx\n", usbip_debug_flag); +} + +static ssize_t usbip_debug_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + if (sscanf(buf, "%lx", &usbip_debug_flag) != 1) + return -EINVAL; + return count; +} +DEVICE_ATTR_RW(usbip_debug); + +static void usbip_dump_buffer(char *buff, int bufflen) +{ + print_hex_dump(KERN_DEBUG, "usbip-core", DUMP_PREFIX_OFFSET, 16, 4, + buff, bufflen, false); +} + +static void usbip_dump_pipe(unsigned int p) +{ + unsigned char type = usb_pipetype(p); + unsigned char ep = usb_pipeendpoint(p); + unsigned char dev = usb_pipedevice(p); + unsigned char dir = usb_pipein(p); + + pr_debug("dev(%d) ep(%d) [%s] ", dev, ep, dir ? "IN" : "OUT"); + + switch (type) { + case PIPE_ISOCHRONOUS: + pr_debug("ISO\n"); + break; + case PIPE_INTERRUPT: + pr_debug("INT\n"); + break; + case PIPE_CONTROL: + pr_debug("CTRL\n"); + break; + case PIPE_BULK: + pr_debug("BULK\n"); + break; + default: + pr_debug("ERR\n"); + break; + } +} + +static void usbip_dump_usb_device(struct usb_device *udev) +{ + struct device *dev = &udev->dev; + int i; + + dev_dbg(dev, " devnum(%d) devpath(%s) usb speed(%s)", + udev->devnum, udev->devpath, usb_speed_string(udev->speed)); + + pr_debug("tt %p, ttport %d\n", udev->tt, udev->ttport); + + dev_dbg(dev, " "); + for (i = 0; i < 16; i++) + pr_debug(" %2u", i); + pr_debug("\n"); + + dev_dbg(dev, " toggle0(IN) :"); + for (i = 0; i < 16; i++) + pr_debug(" %2u", (udev->toggle[0] & (1 << i)) ? 1 : 0); + pr_debug("\n"); + + dev_dbg(dev, " toggle1(OUT):"); + for (i = 0; i < 16; i++) + pr_debug(" %2u", (udev->toggle[1] & (1 << i)) ? 1 : 0); + pr_debug("\n"); + + dev_dbg(dev, " epmaxp_in :"); + for (i = 0; i < 16; i++) { + if (udev->ep_in[i]) + pr_debug(" %2u", + le16_to_cpu(udev->ep_in[i]->desc.wMaxPacketSize)); + } + pr_debug("\n"); + + dev_dbg(dev, " epmaxp_out :"); + for (i = 0; i < 16; i++) { + if (udev->ep_out[i]) + pr_debug(" %2u", + le16_to_cpu(udev->ep_out[i]->desc.wMaxPacketSize)); + } + pr_debug("\n"); + + dev_dbg(dev, "parent %p, bus %p\n", udev->parent, udev->bus); + + dev_dbg(dev, + "descriptor %p, config %p, actconfig %p, rawdescriptors %p\n", + &udev->descriptor, udev->config, + udev->actconfig, udev->rawdescriptors); + + dev_dbg(dev, "have_langid %d, string_langid %d\n", + udev->have_langid, udev->string_langid); + + dev_dbg(dev, "maxchild %d\n", udev->maxchild); +} + +static void usbip_dump_request_type(__u8 rt) +{ + switch (rt & USB_RECIP_MASK) { + case USB_RECIP_DEVICE: + pr_debug("DEVICE"); + break; + case USB_RECIP_INTERFACE: + pr_debug("INTERF"); + break; + case USB_RECIP_ENDPOINT: + pr_debug("ENDPOI"); + break; + case USB_RECIP_OTHER: + pr_debug("OTHER "); + break; + default: + pr_debug("------"); + break; + } +} + +static void usbip_dump_usb_ctrlrequest(struct usb_ctrlrequest *cmd) +{ + if (!cmd) { + pr_debug(" : null pointer\n"); + return; + } + + pr_debug(" "); + pr_debug("bRequestType(%02X) bRequest(%02X) wValue(%04X) wIndex(%04X) wLength(%04X) ", + cmd->bRequestType, cmd->bRequest, + cmd->wValue, cmd->wIndex, cmd->wLength); + pr_debug("\n "); + + if ((cmd->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) { + pr_debug("STANDARD "); + switch (cmd->bRequest) { + case USB_REQ_GET_STATUS: + pr_debug("GET_STATUS\n"); + break; + case USB_REQ_CLEAR_FEATURE: + pr_debug("CLEAR_FEAT\n"); + break; + case USB_REQ_SET_FEATURE: + pr_debug("SET_FEAT\n"); + break; + case USB_REQ_SET_ADDRESS: + pr_debug("SET_ADDRRS\n"); + break; + case USB_REQ_GET_DESCRIPTOR: + pr_debug("GET_DESCRI\n"); + break; + case USB_REQ_SET_DESCRIPTOR: + pr_debug("SET_DESCRI\n"); + break; + case USB_REQ_GET_CONFIGURATION: + pr_debug("GET_CONFIG\n"); + break; + case USB_REQ_SET_CONFIGURATION: + pr_debug("SET_CONFIG\n"); + break; + case USB_REQ_GET_INTERFACE: + pr_debug("GET_INTERF\n"); + break; + case USB_REQ_SET_INTERFACE: + pr_debug("SET_INTERF\n"); + break; + case USB_REQ_SYNCH_FRAME: + pr_debug("SYNC_FRAME\n"); + break; + default: + pr_debug("REQ(%02X)\n", cmd->bRequest); + break; + } + usbip_dump_request_type(cmd->bRequestType); + } else if ((cmd->bRequestType & USB_TYPE_MASK) == USB_TYPE_CLASS) { + pr_debug("CLASS\n"); + } else if ((cmd->bRequestType & USB_TYPE_MASK) == USB_TYPE_VENDOR) { + pr_debug("VENDOR\n"); + } else if ((cmd->bRequestType & USB_TYPE_MASK) == USB_TYPE_RESERVED) { + pr_debug("RESERVED\n"); + } +} + +void usbip_dump_urb(struct urb *urb) +{ + struct device *dev; + + if (!urb) { + pr_debug("urb: null pointer!!\n"); + return; + } + + if (!urb->dev) { + pr_debug("urb->dev: null pointer!!\n"); + return; + } + + dev = &urb->dev->dev; + + dev_dbg(dev, " urb :%p\n", urb); + dev_dbg(dev, " dev :%p\n", urb->dev); + + usbip_dump_usb_device(urb->dev); + + dev_dbg(dev, " pipe :%08x ", urb->pipe); + + usbip_dump_pipe(urb->pipe); + + dev_dbg(dev, " status :%d\n", urb->status); + dev_dbg(dev, " transfer_flags :%08X\n", urb->transfer_flags); + dev_dbg(dev, " transfer_buffer :%p\n", urb->transfer_buffer); + dev_dbg(dev, " transfer_buffer_length:%d\n", + urb->transfer_buffer_length); + dev_dbg(dev, " actual_length :%d\n", urb->actual_length); + dev_dbg(dev, " setup_packet :%p\n", urb->setup_packet); + + if (urb->setup_packet && usb_pipetype(urb->pipe) == PIPE_CONTROL) + usbip_dump_usb_ctrlrequest( + (struct usb_ctrlrequest *)urb->setup_packet); + + dev_dbg(dev, " start_frame :%d\n", urb->start_frame); + dev_dbg(dev, " number_of_packets :%d\n", urb->number_of_packets); + dev_dbg(dev, " interval :%d\n", urb->interval); + dev_dbg(dev, " error_count :%d\n", urb->error_count); + dev_dbg(dev, " context :%p\n", urb->context); + dev_dbg(dev, " complete :%p\n", urb->complete); +} +EXPORT_SYMBOL_GPL(usbip_dump_urb); + +void usbip_dump_header(struct usbip_header *pdu) +{ + pr_debug("BASE: cmd %u seq %u devid %u dir %u ep %u\n", + pdu->base.command, + pdu->base.seqnum, + pdu->base.devid, + pdu->base.direction, + pdu->base.ep); + + switch (pdu->base.command) { + case USBIP_CMD_SUBMIT: + pr_debug("USBIP_CMD_SUBMIT: x_flags %u x_len %u sf %u #p %d iv %d\n", + pdu->u.cmd_submit.transfer_flags, + pdu->u.cmd_submit.transfer_buffer_length, + pdu->u.cmd_submit.start_frame, + pdu->u.cmd_submit.number_of_packets, + pdu->u.cmd_submit.interval); + break; + case USBIP_CMD_UNLINK: + pr_debug("USBIP_CMD_UNLINK: seq %u\n", + pdu->u.cmd_unlink.seqnum); + break; + case USBIP_RET_SUBMIT: + pr_debug("USBIP_RET_SUBMIT: st %d al %u sf %d #p %d ec %d\n", + pdu->u.ret_submit.status, + pdu->u.ret_submit.actual_length, + pdu->u.ret_submit.start_frame, + pdu->u.ret_submit.number_of_packets, + pdu->u.ret_submit.error_count); + break; + case USBIP_RET_UNLINK: + pr_debug("USBIP_RET_UNLINK: status %d\n", + pdu->u.ret_unlink.status); + break; + default: + /* NOT REACHED */ + pr_err("unknown command\n"); + break; + } +} +EXPORT_SYMBOL_GPL(usbip_dump_header); + +/* Receive data over TCP/IP. */ +int usbip_recv(struct socket *sock, void *buf, int size) +{ + int result; + struct msghdr msg; + struct kvec iov; + int total = 0; + + /* for blocks of if (usbip_dbg_flag_xmit) */ + char *bp = buf; + int osize = size; + + usbip_dbg_xmit("enter\n"); + + if (!sock || !buf || !size) { + pr_err("invalid arg, sock %p buff %p size %d\n", sock, buf, + size); + return -EINVAL; + } + + do { + sock->sk->sk_allocation = GFP_NOIO; + iov.iov_base = buf; + iov.iov_len = size; + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_flags = MSG_NOSIGNAL; + + result = kernel_recvmsg(sock, &msg, &iov, 1, size, MSG_WAITALL); + if (result <= 0) { + pr_debug("receive sock %p buf %p size %u ret %d total %d\n", + sock, buf, size, result, total); + goto err; + } + + size -= result; + buf += result; + total += result; + } while (size > 0); + + if (usbip_dbg_flag_xmit) { + if (!in_interrupt()) + pr_debug("%-10s:", current->comm); + else + pr_debug("interrupt :"); + + pr_debug("receiving....\n"); + usbip_dump_buffer(bp, osize); + pr_debug("received, osize %d ret %d size %d total %d\n", + osize, result, size, total); + } + + return total; + +err: + return result; +} +EXPORT_SYMBOL_GPL(usbip_recv); + +/* there may be more cases to tweak the flags. */ +static unsigned int tweak_transfer_flags(unsigned int flags) +{ + flags &= ~URB_NO_TRANSFER_DMA_MAP; + return flags; +} + +static void usbip_pack_cmd_submit(struct usbip_header *pdu, struct urb *urb, + int pack) +{ + struct usbip_header_cmd_submit *spdu = &pdu->u.cmd_submit; + + /* + * Some members are not still implemented in usbip. I hope this issue + * will be discussed when usbip is ported to other operating systems. + */ + if (pack) { + spdu->transfer_flags = + tweak_transfer_flags(urb->transfer_flags); + spdu->transfer_buffer_length = urb->transfer_buffer_length; + spdu->start_frame = urb->start_frame; + spdu->number_of_packets = urb->number_of_packets; + spdu->interval = urb->interval; + } else { + urb->transfer_flags = spdu->transfer_flags; + urb->transfer_buffer_length = spdu->transfer_buffer_length; + urb->start_frame = spdu->start_frame; + urb->number_of_packets = spdu->number_of_packets; + urb->interval = spdu->interval; + } +} + +static void usbip_pack_ret_submit(struct usbip_header *pdu, struct urb *urb, + int pack) +{ + struct usbip_header_ret_submit *rpdu = &pdu->u.ret_submit; + + if (pack) { + rpdu->status = urb->status; + rpdu->actual_length = urb->actual_length; + rpdu->start_frame = urb->start_frame; + rpdu->number_of_packets = urb->number_of_packets; + rpdu->error_count = urb->error_count; + } else { + urb->status = rpdu->status; + urb->actual_length = rpdu->actual_length; + urb->start_frame = rpdu->start_frame; + urb->number_of_packets = rpdu->number_of_packets; + urb->error_count = rpdu->error_count; + } +} + +void usbip_pack_pdu(struct usbip_header *pdu, struct urb *urb, int cmd, + int pack) +{ + switch (cmd) { + case USBIP_CMD_SUBMIT: + usbip_pack_cmd_submit(pdu, urb, pack); + break; + case USBIP_RET_SUBMIT: + usbip_pack_ret_submit(pdu, urb, pack); + break; + default: + /* NOT REACHED */ + pr_err("unknown command\n"); + break; + } +} +EXPORT_SYMBOL_GPL(usbip_pack_pdu); + +static void correct_endian_basic(struct usbip_header_basic *base, int send) +{ + if (send) { + base->command = cpu_to_be32(base->command); + base->seqnum = cpu_to_be32(base->seqnum); + base->devid = cpu_to_be32(base->devid); + base->direction = cpu_to_be32(base->direction); + base->ep = cpu_to_be32(base->ep); + } else { + base->command = be32_to_cpu(base->command); + base->seqnum = be32_to_cpu(base->seqnum); + base->devid = be32_to_cpu(base->devid); + base->direction = be32_to_cpu(base->direction); + base->ep = be32_to_cpu(base->ep); + } +} + +static void correct_endian_cmd_submit(struct usbip_header_cmd_submit *pdu, + int send) +{ + if (send) { + pdu->transfer_flags = cpu_to_be32(pdu->transfer_flags); + + cpu_to_be32s(&pdu->transfer_buffer_length); + cpu_to_be32s(&pdu->start_frame); + cpu_to_be32s(&pdu->number_of_packets); + cpu_to_be32s(&pdu->interval); + } else { + pdu->transfer_flags = be32_to_cpu(pdu->transfer_flags); + + be32_to_cpus(&pdu->transfer_buffer_length); + be32_to_cpus(&pdu->start_frame); + be32_to_cpus(&pdu->number_of_packets); + be32_to_cpus(&pdu->interval); + } +} + +static void correct_endian_ret_submit(struct usbip_header_ret_submit *pdu, + int send) +{ + if (send) { + cpu_to_be32s(&pdu->status); + cpu_to_be32s(&pdu->actual_length); + cpu_to_be32s(&pdu->start_frame); + cpu_to_be32s(&pdu->number_of_packets); + cpu_to_be32s(&pdu->error_count); + } else { + be32_to_cpus(&pdu->status); + be32_to_cpus(&pdu->actual_length); + be32_to_cpus(&pdu->start_frame); + be32_to_cpus(&pdu->number_of_packets); + be32_to_cpus(&pdu->error_count); + } +} + +static void correct_endian_cmd_unlink(struct usbip_header_cmd_unlink *pdu, + int send) +{ + if (send) + pdu->seqnum = cpu_to_be32(pdu->seqnum); + else + pdu->seqnum = be32_to_cpu(pdu->seqnum); +} + +static void correct_endian_ret_unlink(struct usbip_header_ret_unlink *pdu, + int send) +{ + if (send) + cpu_to_be32s(&pdu->status); + else + be32_to_cpus(&pdu->status); +} + +void usbip_header_correct_endian(struct usbip_header *pdu, int send) +{ + __u32 cmd = 0; + + if (send) + cmd = pdu->base.command; + + correct_endian_basic(&pdu->base, send); + + if (!send) + cmd = pdu->base.command; + + switch (cmd) { + case USBIP_CMD_SUBMIT: + correct_endian_cmd_submit(&pdu->u.cmd_submit, send); + break; + case USBIP_RET_SUBMIT: + correct_endian_ret_submit(&pdu->u.ret_submit, send); + break; + case USBIP_CMD_UNLINK: + correct_endian_cmd_unlink(&pdu->u.cmd_unlink, send); + break; + case USBIP_RET_UNLINK: + correct_endian_ret_unlink(&pdu->u.ret_unlink, send); + break; + default: + /* NOT REACHED */ + pr_err("unknown command\n"); + break; + } +} +EXPORT_SYMBOL_GPL(usbip_header_correct_endian); + +static void usbip_iso_packet_correct_endian( + struct usbip_iso_packet_descriptor *iso, int send) +{ + /* does not need all members. but copy all simply. */ + if (send) { + iso->offset = cpu_to_be32(iso->offset); + iso->length = cpu_to_be32(iso->length); + iso->status = cpu_to_be32(iso->status); + iso->actual_length = cpu_to_be32(iso->actual_length); + } else { + iso->offset = be32_to_cpu(iso->offset); + iso->length = be32_to_cpu(iso->length); + iso->status = be32_to_cpu(iso->status); + iso->actual_length = be32_to_cpu(iso->actual_length); + } +} + +static void usbip_pack_iso(struct usbip_iso_packet_descriptor *iso, + struct usb_iso_packet_descriptor *uiso, int pack) +{ + if (pack) { + iso->offset = uiso->offset; + iso->length = uiso->length; + iso->status = uiso->status; + iso->actual_length = uiso->actual_length; + } else { + uiso->offset = iso->offset; + uiso->length = iso->length; + uiso->status = iso->status; + uiso->actual_length = iso->actual_length; + } +} + +/* must free buffer */ +struct usbip_iso_packet_descriptor* +usbip_alloc_iso_desc_pdu(struct urb *urb, ssize_t *bufflen) +{ + struct usbip_iso_packet_descriptor *iso; + int np = urb->number_of_packets; + ssize_t size = np * sizeof(*iso); + int i; + + iso = kzalloc(size, GFP_KERNEL); + if (!iso) + return NULL; + + for (i = 0; i < np; i++) { + usbip_pack_iso(&iso[i], &urb->iso_frame_desc[i], 1); + usbip_iso_packet_correct_endian(&iso[i], 1); + } + + *bufflen = size; + + return iso; +} +EXPORT_SYMBOL_GPL(usbip_alloc_iso_desc_pdu); + +/* some members of urb must be substituted before. */ +int usbip_recv_iso(struct usbip_device *ud, struct urb *urb) +{ + void *buff; + struct usbip_iso_packet_descriptor *iso; + int np = urb->number_of_packets; + int size = np * sizeof(*iso); + int i; + int ret; + int total_length = 0; + + if (!usb_pipeisoc(urb->pipe)) + return 0; + + /* my Bluetooth dongle gets ISO URBs which are np = 0 */ + if (np == 0) + return 0; + + buff = kzalloc(size, GFP_KERNEL); + if (!buff) + return -ENOMEM; + + ret = usbip_recv(ud->tcp_socket, buff, size); + if (ret != size) { + dev_err(&urb->dev->dev, "recv iso_frame_descriptor, %d\n", + ret); + kfree(buff); + + if (ud->side == USBIP_STUB) + usbip_event_add(ud, SDEV_EVENT_ERROR_TCP); + else + usbip_event_add(ud, VDEV_EVENT_ERROR_TCP); + + return -EPIPE; + } + + iso = (struct usbip_iso_packet_descriptor *) buff; + for (i = 0; i < np; i++) { + usbip_iso_packet_correct_endian(&iso[i], 0); + usbip_pack_iso(&iso[i], &urb->iso_frame_desc[i], 0); + total_length += urb->iso_frame_desc[i].actual_length; + } + + kfree(buff); + + if (total_length != urb->actual_length) { + dev_err(&urb->dev->dev, + "total length of iso packets %d not equal to actual length of buffer %d\n", + total_length, urb->actual_length); + + if (ud->side == USBIP_STUB) + usbip_event_add(ud, SDEV_EVENT_ERROR_TCP); + else + usbip_event_add(ud, VDEV_EVENT_ERROR_TCP); + + return -EPIPE; + } + + return ret; +} +EXPORT_SYMBOL_GPL(usbip_recv_iso); + +/* + * This functions restores the padding which was removed for optimizing + * the bandwidth during transfer over tcp/ip + * + * buffer and iso packets need to be stored and be in propeper endian in urb + * before calling this function + */ +void usbip_pad_iso(struct usbip_device *ud, struct urb *urb) +{ + int np = urb->number_of_packets; + int i; + int actualoffset = urb->actual_length; + + if (!usb_pipeisoc(urb->pipe)) + return; + + /* if no packets or length of data is 0, then nothing to unpack */ + if (np == 0 || urb->actual_length == 0) + return; + + /* + * if actual_length is transfer_buffer_length then no padding is + * present. + */ + if (urb->actual_length == urb->transfer_buffer_length) + return; + + /* + * loop over all packets from last to first (to prevent overwritting + * memory when padding) and move them into the proper place + */ + for (i = np-1; i > 0; i--) { + actualoffset -= urb->iso_frame_desc[i].actual_length; + memmove(urb->transfer_buffer + urb->iso_frame_desc[i].offset, + urb->transfer_buffer + actualoffset, + urb->iso_frame_desc[i].actual_length); + } +} +EXPORT_SYMBOL_GPL(usbip_pad_iso); + +/* some members of urb must be substituted before. */ +int usbip_recv_xbuff(struct usbip_device *ud, struct urb *urb) +{ + int ret; + int size; + + if (ud->side == USBIP_STUB) { + /* the direction of urb must be OUT. */ + if (usb_pipein(urb->pipe)) + return 0; + + size = urb->transfer_buffer_length; + } else { + /* the direction of urb must be IN. */ + if (usb_pipeout(urb->pipe)) + return 0; + + size = urb->actual_length; + } + + /* no need to recv xbuff */ + if (!(size > 0)) + return 0; + + ret = usbip_recv(ud->tcp_socket, urb->transfer_buffer, size); + if (ret != size) { + dev_err(&urb->dev->dev, "recv xbuf, %d\n", ret); + if (ud->side == USBIP_STUB) { + usbip_event_add(ud, SDEV_EVENT_ERROR_TCP); + } else { + usbip_event_add(ud, VDEV_EVENT_ERROR_TCP); + return -EPIPE; + } + } + + return ret; +} +EXPORT_SYMBOL_GPL(usbip_recv_xbuff); + +static int __init usbip_core_init(void) +{ + pr_info(DRIVER_DESC " v" USBIP_VERSION "\n"); + return 0; +} + +static void __exit usbip_core_exit(void) +{ + return; +} + +module_init(usbip_core_init); +module_exit(usbip_core_exit); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); +MODULE_VERSION(USBIP_VERSION); diff --git a/drivers/usb/usbip/usbip_common.h b/drivers/usb/usbip/usbip_common.h new file mode 100644 index 000000000000..86b08475c254 --- /dev/null +++ b/drivers/usb/usbip/usbip_common.h @@ -0,0 +1,335 @@ +/* + * Copyright (C) 2003-2008 Takahiro Hirofuchi + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#ifndef __USBIP_COMMON_H +#define __USBIP_COMMON_H + +#include <linux/compiler.h> +#include <linux/device.h> +#include <linux/interrupt.h> +#include <linux/net.h> +#include <linux/printk.h> +#include <linux/spinlock.h> +#include <linux/types.h> +#include <linux/usb.h> +#include <linux/wait.h> +#include <uapi/linux/usbip.h> + +#define USBIP_VERSION "1.0.0" + +#undef pr_fmt + +#ifdef DEBUG +#define pr_fmt(fmt) KBUILD_MODNAME ": %s:%d: " fmt, __func__, __LINE__ +#else +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#endif + +enum { + usbip_debug_xmit = (1 << 0), + usbip_debug_sysfs = (1 << 1), + usbip_debug_urb = (1 << 2), + usbip_debug_eh = (1 << 3), + + usbip_debug_stub_cmp = (1 << 8), + usbip_debug_stub_dev = (1 << 9), + usbip_debug_stub_rx = (1 << 10), + usbip_debug_stub_tx = (1 << 11), + + usbip_debug_vhci_rh = (1 << 8), + usbip_debug_vhci_hc = (1 << 9), + usbip_debug_vhci_rx = (1 << 10), + usbip_debug_vhci_tx = (1 << 11), + usbip_debug_vhci_sysfs = (1 << 12) +}; + +#define usbip_dbg_flag_xmit (usbip_debug_flag & usbip_debug_xmit) +#define usbip_dbg_flag_vhci_rh (usbip_debug_flag & usbip_debug_vhci_rh) +#define usbip_dbg_flag_vhci_hc (usbip_debug_flag & usbip_debug_vhci_hc) +#define usbip_dbg_flag_vhci_rx (usbip_debug_flag & usbip_debug_vhci_rx) +#define usbip_dbg_flag_vhci_tx (usbip_debug_flag & usbip_debug_vhci_tx) +#define usbip_dbg_flag_stub_rx (usbip_debug_flag & usbip_debug_stub_rx) +#define usbip_dbg_flag_stub_tx (usbip_debug_flag & usbip_debug_stub_tx) +#define usbip_dbg_flag_vhci_sysfs (usbip_debug_flag & usbip_debug_vhci_sysfs) + +extern unsigned long usbip_debug_flag; +extern struct device_attribute dev_attr_usbip_debug; + +#define usbip_dbg_with_flag(flag, fmt, args...) \ + do { \ + if (flag & usbip_debug_flag) \ + pr_debug(fmt, ##args); \ + } while (0) + +#define usbip_dbg_sysfs(fmt, args...) \ + usbip_dbg_with_flag(usbip_debug_sysfs, fmt , ##args) +#define usbip_dbg_xmit(fmt, args...) \ + usbip_dbg_with_flag(usbip_debug_xmit, fmt , ##args) +#define usbip_dbg_urb(fmt, args...) \ + usbip_dbg_with_flag(usbip_debug_urb, fmt , ##args) +#define usbip_dbg_eh(fmt, args...) \ + usbip_dbg_with_flag(usbip_debug_eh, fmt , ##args) + +#define usbip_dbg_vhci_rh(fmt, args...) \ + usbip_dbg_with_flag(usbip_debug_vhci_rh, fmt , ##args) +#define usbip_dbg_vhci_hc(fmt, args...) \ + usbip_dbg_with_flag(usbip_debug_vhci_hc, fmt , ##args) +#define usbip_dbg_vhci_rx(fmt, args...) \ + usbip_dbg_with_flag(usbip_debug_vhci_rx, fmt , ##args) +#define usbip_dbg_vhci_tx(fmt, args...) \ + usbip_dbg_with_flag(usbip_debug_vhci_tx, fmt , ##args) +#define usbip_dbg_vhci_sysfs(fmt, args...) \ + usbip_dbg_with_flag(usbip_debug_vhci_sysfs, fmt , ##args) + +#define usbip_dbg_stub_cmp(fmt, args...) \ + usbip_dbg_with_flag(usbip_debug_stub_cmp, fmt , ##args) +#define usbip_dbg_stub_rx(fmt, args...) \ + usbip_dbg_with_flag(usbip_debug_stub_rx, fmt , ##args) +#define usbip_dbg_stub_tx(fmt, args...) \ + usbip_dbg_with_flag(usbip_debug_stub_tx, fmt , ##args) + +/* + * USB/IP request headers + * + * Each request is transferred across the network to its counterpart, which + * facilitates the normal USB communication. The values contained in the headers + * are basically the same as in a URB. Currently, four request types are + * defined: + * + * - USBIP_CMD_SUBMIT: a USB request block, corresponds to usb_submit_urb() + * (client to server) + * + * - USBIP_RET_SUBMIT: the result of USBIP_CMD_SUBMIT + * (server to client) + * + * - USBIP_CMD_UNLINK: an unlink request of a pending USBIP_CMD_SUBMIT, + * corresponds to usb_unlink_urb() + * (client to server) + * + * - USBIP_RET_UNLINK: the result of USBIP_CMD_UNLINK + * (server to client) + * + */ +#define USBIP_CMD_SUBMIT 0x0001 +#define USBIP_CMD_UNLINK 0x0002 +#define USBIP_RET_SUBMIT 0x0003 +#define USBIP_RET_UNLINK 0x0004 + +#define USBIP_DIR_OUT 0x00 +#define USBIP_DIR_IN 0x01 + +/** + * struct usbip_header_basic - data pertinent to every request + * @command: the usbip request type + * @seqnum: sequential number that identifies requests; incremented per + * connection + * @devid: specifies a remote USB device uniquely instead of busnum and devnum; + * in the stub driver, this value is ((busnum << 16) | devnum) + * @direction: direction of the transfer + * @ep: endpoint number + */ +struct usbip_header_basic { + __u32 command; + __u32 seqnum; + __u32 devid; + __u32 direction; + __u32 ep; +} __packed; + +/** + * struct usbip_header_cmd_submit - USBIP_CMD_SUBMIT packet header + * @transfer_flags: URB flags + * @transfer_buffer_length: the data size for (in) or (out) transfer + * @start_frame: initial frame for isochronous or interrupt transfers + * @number_of_packets: number of isochronous packets + * @interval: maximum time for the request on the server-side host controller + * @setup: setup data for a control request + */ +struct usbip_header_cmd_submit { + __u32 transfer_flags; + __s32 transfer_buffer_length; + + /* it is difficult for usbip to sync frames (reserved only?) */ + __s32 start_frame; + __s32 number_of_packets; + __s32 interval; + + unsigned char setup[8]; +} __packed; + +/** + * struct usbip_header_ret_submit - USBIP_RET_SUBMIT packet header + * @status: return status of a non-iso request + * @actual_length: number of bytes transferred + * @start_frame: initial frame for isochronous or interrupt transfers + * @number_of_packets: number of isochronous packets + * @error_count: number of errors for isochronous transfers + */ +struct usbip_header_ret_submit { + __s32 status; + __s32 actual_length; + __s32 start_frame; + __s32 number_of_packets; + __s32 error_count; +} __packed; + +/** + * struct usbip_header_cmd_unlink - USBIP_CMD_UNLINK packet header + * @seqnum: the URB seqnum to unlink + */ +struct usbip_header_cmd_unlink { + __u32 seqnum; +} __packed; + +/** + * struct usbip_header_ret_unlink - USBIP_RET_UNLINK packet header + * @status: return status of the request + */ +struct usbip_header_ret_unlink { + __s32 status; +} __packed; + +/** + * struct usbip_header - common header for all usbip packets + * @base: the basic header + * @u: packet type dependent header + */ +struct usbip_header { + struct usbip_header_basic base; + + union { + struct usbip_header_cmd_submit cmd_submit; + struct usbip_header_ret_submit ret_submit; + struct usbip_header_cmd_unlink cmd_unlink; + struct usbip_header_ret_unlink ret_unlink; + } u; +} __packed; + +/* + * This is the same as usb_iso_packet_descriptor but packed for pdu. + */ +struct usbip_iso_packet_descriptor { + __u32 offset; + __u32 length; /* expected length */ + __u32 actual_length; + __u32 status; +} __packed; + +enum usbip_side { + USBIP_VHCI, + USBIP_STUB, +}; + +/* event handler */ +#define USBIP_EH_SHUTDOWN (1 << 0) +#define USBIP_EH_BYE (1 << 1) +#define USBIP_EH_RESET (1 << 2) +#define USBIP_EH_UNUSABLE (1 << 3) + +#define SDEV_EVENT_REMOVED (USBIP_EH_SHUTDOWN | USBIP_EH_RESET | USBIP_EH_BYE) +#define SDEV_EVENT_DOWN (USBIP_EH_SHUTDOWN | USBIP_EH_RESET) +#define SDEV_EVENT_ERROR_TCP (USBIP_EH_SHUTDOWN | USBIP_EH_RESET) +#define SDEV_EVENT_ERROR_SUBMIT (USBIP_EH_SHUTDOWN | USBIP_EH_RESET) +#define SDEV_EVENT_ERROR_MALLOC (USBIP_EH_SHUTDOWN | USBIP_EH_UNUSABLE) + +#define VDEV_EVENT_REMOVED (USBIP_EH_SHUTDOWN | USBIP_EH_BYE) +#define VDEV_EVENT_DOWN (USBIP_EH_SHUTDOWN | USBIP_EH_RESET) +#define VDEV_EVENT_ERROR_TCP (USBIP_EH_SHUTDOWN | USBIP_EH_RESET) +#define VDEV_EVENT_ERROR_MALLOC (USBIP_EH_SHUTDOWN | USBIP_EH_UNUSABLE) + +/* a common structure for stub_device and vhci_device */ +struct usbip_device { + enum usbip_side side; + enum usbip_device_status status; + + /* lock for status */ + spinlock_t lock; + + struct socket *tcp_socket; + + struct task_struct *tcp_rx; + struct task_struct *tcp_tx; + + unsigned long event; + struct task_struct *eh; + wait_queue_head_t eh_waitq; + + struct eh_ops { + void (*shutdown)(struct usbip_device *); + void (*reset)(struct usbip_device *); + void (*unusable)(struct usbip_device *); + } eh_ops; +}; + +#define kthread_get_run(threadfn, data, namefmt, ...) \ +({ \ + struct task_struct *__k \ + = kthread_create(threadfn, data, namefmt, ## __VA_ARGS__); \ + if (!IS_ERR(__k)) { \ + get_task_struct(__k); \ + wake_up_process(__k); \ + } \ + __k; \ +}) + +#define kthread_stop_put(k) \ + do { \ + kthread_stop(k); \ + put_task_struct(k); \ + } while (0) + +/* usbip_common.c */ +void usbip_dump_urb(struct urb *purb); +void usbip_dump_header(struct usbip_header *pdu); + +int usbip_recv(struct socket *sock, void *buf, int size); + +void usbip_pack_pdu(struct usbip_header *pdu, struct urb *urb, int cmd, + int pack); +void usbip_header_correct_endian(struct usbip_header *pdu, int send); + +struct usbip_iso_packet_descriptor* +usbip_alloc_iso_desc_pdu(struct urb *urb, ssize_t *bufflen); + +/* some members of urb must be substituted before. */ +int usbip_recv_iso(struct usbip_device *ud, struct urb *urb); +void usbip_pad_iso(struct usbip_device *ud, struct urb *urb); +int usbip_recv_xbuff(struct usbip_device *ud, struct urb *urb); + +/* usbip_event.c */ +int usbip_start_eh(struct usbip_device *ud); +void usbip_stop_eh(struct usbip_device *ud); +void usbip_event_add(struct usbip_device *ud, unsigned long event); +int usbip_event_happened(struct usbip_device *ud); + +static inline int interface_to_busnum(struct usb_interface *interface) +{ + struct usb_device *udev = interface_to_usbdev(interface); + + return udev->bus->busnum; +} + +static inline int interface_to_devnum(struct usb_interface *interface) +{ + struct usb_device *udev = interface_to_usbdev(interface); + + return udev->devnum; +} + +#endif /* __USBIP_COMMON_H */ diff --git a/drivers/usb/usbip/usbip_event.c b/drivers/usb/usbip/usbip_event.c new file mode 100644 index 000000000000..64933b993d7a --- /dev/null +++ b/drivers/usb/usbip/usbip_event.c @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2003-2008 Takahiro Hirofuchi + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include <linux/kthread.h> +#include <linux/export.h> + +#include "usbip_common.h" + +static int event_handler(struct usbip_device *ud) +{ + usbip_dbg_eh("enter\n"); + + /* + * Events are handled by only this thread. + */ + while (usbip_event_happened(ud)) { + usbip_dbg_eh("pending event %lx\n", ud->event); + + /* + * NOTE: shutdown must come first. + * Shutdown the device. + */ + if (ud->event & USBIP_EH_SHUTDOWN) { + ud->eh_ops.shutdown(ud); + ud->event &= ~USBIP_EH_SHUTDOWN; + } + + /* Reset the device. */ + if (ud->event & USBIP_EH_RESET) { + ud->eh_ops.reset(ud); + ud->event &= ~USBIP_EH_RESET; + } + + /* Mark the device as unusable. */ + if (ud->event & USBIP_EH_UNUSABLE) { + ud->eh_ops.unusable(ud); + ud->event &= ~USBIP_EH_UNUSABLE; + } + + /* Stop the error handler. */ + if (ud->event & USBIP_EH_BYE) + return -1; + } + + return 0; +} + +static int event_handler_loop(void *data) +{ + struct usbip_device *ud = data; + + while (!kthread_should_stop()) { + wait_event_interruptible(ud->eh_waitq, + usbip_event_happened(ud) || + kthread_should_stop()); + usbip_dbg_eh("wakeup\n"); + + if (event_handler(ud) < 0) + break; + } + + return 0; +} + +int usbip_start_eh(struct usbip_device *ud) +{ + init_waitqueue_head(&ud->eh_waitq); + ud->event = 0; + + ud->eh = kthread_run(event_handler_loop, ud, "usbip_eh"); + if (IS_ERR(ud->eh)) { + pr_warn("Unable to start control thread\n"); + return PTR_ERR(ud->eh); + } + + return 0; +} +EXPORT_SYMBOL_GPL(usbip_start_eh); + +void usbip_stop_eh(struct usbip_device *ud) +{ + if (ud->eh == current) + return; /* do not wait for myself */ + + kthread_stop(ud->eh); + usbip_dbg_eh("usbip_eh has finished\n"); +} +EXPORT_SYMBOL_GPL(usbip_stop_eh); + +void usbip_event_add(struct usbip_device *ud, unsigned long event) +{ + unsigned long flags; + + spin_lock_irqsave(&ud->lock, flags); + ud->event |= event; + wake_up(&ud->eh_waitq); + spin_unlock_irqrestore(&ud->lock, flags); +} +EXPORT_SYMBOL_GPL(usbip_event_add); + +int usbip_event_happened(struct usbip_device *ud) +{ + int happened = 0; + + spin_lock(&ud->lock); + if (ud->event != 0) + happened = 1; + spin_unlock(&ud->lock); + + return happened; +} +EXPORT_SYMBOL_GPL(usbip_event_happened); diff --git a/drivers/usb/usbip/usbip_protocol.txt b/drivers/usb/usbip/usbip_protocol.txt new file mode 100644 index 000000000000..16b6fe27284c --- /dev/null +++ b/drivers/usb/usbip/usbip_protocol.txt @@ -0,0 +1,358 @@ +PRELIMINARY DRAFT, MAY CONTAIN MISTAKES! +28 Jun 2011 + +The USB/IP protocol follows a server/client architecture. The server exports the +USB devices and the clients imports them. The device driver for the exported +USB device runs on the client machine. + +The client may ask for the list of the exported USB devices. To get the list the +client opens a TCP/IP connection towards the server, and sends an OP_REQ_DEVLIST +packet on top of the TCP/IP connection (so the actual OP_REQ_DEVLIST may be sent +in one or more pieces at the low level transport layer). The server sends back +the OP_REP_DEVLIST packet which lists the exported USB devices. Finally the +TCP/IP connection is closed. + + virtual host controller usb host + "client" "server" + (imports USB devices) (exports USB devices) + | | + | OP_REQ_DEVLIST | + | ----------------------------------------------> | + | | + | OP_REP_DEVLIST | + | <---------------------------------------------- | + | | + +Once the client knows the list of exported USB devices it may decide to use one +of them. First the client opens a TCP/IP connection towards the server and +sends an OP_REQ_IMPORT packet. The server replies with OP_REP_IMPORT. If the +import was successful the TCP/IP connection remains open and will be used +to transfer the URB traffic between the client and the server. The client may +send two types of packets: the USBIP_CMD_SUBMIT to submit an URB, and +USBIP_CMD_UNLINK to unlink a previously submitted URB. The answers of the +server may be USBIP_RET_SUBMIT and USBIP_RET_UNLINK respectively. + + virtual host controller usb host + "client" "server" + (imports USB devices) (exports USB devices) + | | + | OP_REQ_IMPORT | + | ----------------------------------------------> | + | | + | OP_REP_IMPORT | + | <---------------------------------------------- | + | | + | | + | USBIP_CMD_SUBMIT(seqnum = n) | + | ----------------------------------------------> | + | | + | USBIP_RET_SUBMIT(seqnum = n) | + | <---------------------------------------------- | + | . | + | : | + | | + | USBIP_CMD_SUBMIT(seqnum = m) | + | ----------------------------------------------> | + | | + | USBIP_CMD_SUBMIT(seqnum = m+1) | + | ----------------------------------------------> | + | | + | USBIP_CMD_SUBMIT(seqnum = m+2) | + | ----------------------------------------------> | + | | + | USBIP_RET_SUBMIT(seqnum = m) | + | <---------------------------------------------- | + | | + | USBIP_CMD_SUBMIT(seqnum = m+3) | + | ----------------------------------------------> | + | | + | USBIP_RET_SUBMIT(seqnum = m+1) | + | <---------------------------------------------- | + | | + | USBIP_CMD_SUBMIT(seqnum = m+4) | + | ----------------------------------------------> | + | | + | USBIP_RET_SUBMIT(seqnum = m+2) | + | <---------------------------------------------- | + | . | + | : | + | | + | USBIP_CMD_UNLINK | + | ----------------------------------------------> | + | | + | USBIP_RET_UNLINK | + | <---------------------------------------------- | + | | + +The fields are in network (big endian) byte order meaning that the most significant +byte (MSB) is stored at the lowest address. + + +OP_REQ_DEVLIST: Retrieve the list of exported USB devices. + + Offset | Length | Value | Description +-----------+--------+------------+--------------------------------------------------- + 0 | 2 | 0x0100 | Binary-coded decimal USBIP version number: v1.0.0 +-----------+--------+------------+--------------------------------------------------- + 2 | 2 | 0x8005 | Command code: Retrieve the list of exported USB + | | | devices. +-----------+--------+------------+--------------------------------------------------- + 4 | 4 | 0x00000000 | Status: unused, shall be set to 0 + +OP_REP_DEVLIST: Reply with the list of exported USB devices. + + Offset | Length | Value | Description +-----------+--------+------------+--------------------------------------------------- + 0 | 2 | 0x0100 | Binary-coded decimal USBIP version number: v1.0.0. +-----------+--------+------------+--------------------------------------------------- + 2 | 2 | 0x0005 | Reply code: The list of exported USB devices. +-----------+--------+------------+--------------------------------------------------- + 4 | 4 | 0x00000000 | Status: 0 for OK +-----------+--------+------------+--------------------------------------------------- + 8 | 4 | n | Number of exported devices: 0 means no exported + | | | devices. +-----------+--------+------------+--------------------------------------------------- + 0x0C | | | From now on the exported n devices are described, + | | | if any. If no devices are exported the message + | | | ends with the previous "number of exported + | | | devices" field. +-----------+--------+------------+--------------------------------------------------- + | 256 | | path: Path of the device on the host exporting the + | | | USB device, string closed with zero byte, e.g. + | | | "/sys/devices/pci0000:00/0000:00:1d.1/usb3/3-2" + | | | The unused bytes shall be filled with zero + | | | bytes. +-----------+--------+------------+--------------------------------------------------- + 0x10C | 32 | | busid: Bus ID of the exported device, string + | | | closed with zero byte, e.g. "3-2". The unused + | | | bytes shall be filled with zero bytes. +-----------+--------+------------+--------------------------------------------------- + 0x12C | 4 | | busnum +-----------+--------+------------+--------------------------------------------------- + 0x130 | 4 | | devnum +-----------+--------+------------+--------------------------------------------------- + 0x134 | 4 | | speed +-----------+--------+------------+--------------------------------------------------- + 0x138 | 2 | | idVendor +-----------+--------+------------+--------------------------------------------------- + 0x13A | 2 | | idProduct +-----------+--------+------------+--------------------------------------------------- + 0x13C | 2 | | bcdDevice +-----------+--------+------------+--------------------------------------------------- + 0x13E | 1 | | bDeviceClass +-----------+--------+------------+--------------------------------------------------- + 0x13F | 1 | | bDeviceSubClass +-----------+--------+------------+--------------------------------------------------- + 0x140 | 1 | | bDeviceProtocol +-----------+--------+------------+--------------------------------------------------- + 0x141 | 1 | | bConfigurationValue +-----------+--------+------------+--------------------------------------------------- + 0x142 | 1 | | bNumConfigurations +-----------+--------+------------+--------------------------------------------------- + 0x143 | 1 | | bNumInterfaces +-----------+--------+------------+--------------------------------------------------- + 0x144 | | m_0 | From now on each interface is described, all + | | | together bNumInterfaces times, with the + | | | the following 4 fields: +-----------+--------+------------+--------------------------------------------------- + | 1 | | bInterfaceClass +-----------+--------+------------+--------------------------------------------------- + 0x145 | 1 | | bInterfaceSubClass +-----------+--------+------------+--------------------------------------------------- + 0x146 | 1 | | bInterfaceProtocol +-----------+--------+------------+--------------------------------------------------- + 0x147 | 1 | | padding byte for alignment, shall be set to zero +-----------+--------+------------+--------------------------------------------------- + 0xC + | | | The second exported USB device starts at i=1 + i*0x138 + | | | with the busid field. + m_(i-1)*4 | | | + +OP_REQ_IMPORT: Request to import (attach) a remote USB device. + + Offset | Length | Value | Description +-----------+--------+------------+--------------------------------------------------- + 0 | 2 | 0x0100 | Binary-coded decimal USBIP version number: v1.0.0 +-----------+--------+------------+--------------------------------------------------- + 2 | 2 | 0x8003 | Command code: import a remote USB device. +-----------+--------+------------+--------------------------------------------------- + 4 | 4 | 0x00000000 | Status: unused, shall be set to 0 +-----------+--------+------------+--------------------------------------------------- + 8 | 32 | | busid: the busid of the exported device on the + | | | remote host. The possible values are taken + | | | from the message field OP_REP_DEVLIST.busid. + | | | A string closed with zero, the unused bytes + | | | shall be filled with zeros. +-----------+--------+------------+--------------------------------------------------- + +OP_REP_IMPORT: Reply to import (attach) a remote USB device. + + Offset | Length | Value | Description +-----------+--------+------------+--------------------------------------------------- + 0 | 2 | 0x0100 | Binary-coded decimal USBIP version number: v1.0.0 +-----------+--------+------------+--------------------------------------------------- + 2 | 2 | 0x0003 | Reply code: Reply to import. +-----------+--------+------------+--------------------------------------------------- + 4 | 4 | 0x00000000 | Status: 0 for OK + | | | 1 for error +-----------+--------+------------+--------------------------------------------------- + 8 | | | From now on comes the details of the imported + | | | device, if the previous status field was OK (0), + | | | otherwise the reply ends with the status field. +-----------+--------+------------+--------------------------------------------------- + | 256 | | path: Path of the device on the host exporting the + | | | USB device, string closed with zero byte, e.g. + | | | "/sys/devices/pci0000:00/0000:00:1d.1/usb3/3-2" + | | | The unused bytes shall be filled with zero + | | | bytes. +-----------+--------+------------+--------------------------------------------------- + 0x108 | 32 | | busid: Bus ID of the exported device, string + | | | closed with zero byte, e.g. "3-2". The unused + | | | bytes shall be filled with zero bytes. +-----------+--------+------------+--------------------------------------------------- + 0x128 | 4 | | busnum +-----------+--------+------------+--------------------------------------------------- + 0x12C | 4 | | devnum +-----------+--------+------------+--------------------------------------------------- + 0x130 | 4 | | speed +-----------+--------+------------+--------------------------------------------------- + 0x134 | 2 | | idVendor +-----------+--------+------------+--------------------------------------------------- + 0x136 | 2 | | idProduct +-----------+--------+------------+--------------------------------------------------- + 0x138 | 2 | | bcdDevice +-----------+--------+------------+--------------------------------------------------- + 0x139 | 1 | | bDeviceClass +-----------+--------+------------+--------------------------------------------------- + 0x13A | 1 | | bDeviceSubClass +-----------+--------+------------+--------------------------------------------------- + 0x13B | 1 | | bDeviceProtocol +-----------+--------+------------+--------------------------------------------------- + 0x13C | 1 | | bConfigurationValue +-----------+--------+------------+--------------------------------------------------- + 0x13D | 1 | | bNumConfigurations +-----------+--------+------------+--------------------------------------------------- + 0x13E | 1 | | bNumInterfaces + +USBIP_CMD_SUBMIT: Submit an URB + + Offset | Length | Value | Description +-----------+--------+------------+--------------------------------------------------- + 0 | 4 | 0x00000001 | command: Submit an URB +-----------+--------+------------+--------------------------------------------------- + 4 | 4 | | seqnum: the sequence number of the URB to submit +-----------+--------+------------+--------------------------------------------------- + 8 | 4 | | devid +-----------+--------+------------+--------------------------------------------------- + 0xC | 4 | | direction: 0: USBIP_DIR_OUT + | | | 1: USBIP_DIR_IN +-----------+--------+------------+--------------------------------------------------- + 0x10 | 4 | | ep: endpoint number, possible values are: 0...15 +-----------+--------+------------+--------------------------------------------------- + 0x14 | 4 | | transfer_flags: possible values depend on the + | | | URB transfer type, see below +-----------+--------+------------+--------------------------------------------------- + 0x18 | 4 | | transfer_buffer_length +-----------+--------+------------+--------------------------------------------------- + 0x1C | 4 | | start_frame: specify the selected frame to + | | | transmit an ISO frame, ignored if URB_ISO_ASAP + | | | is specified at transfer_flags +-----------+--------+------------+--------------------------------------------------- + 0x20 | 4 | | number_of_packets: number of ISO packets +-----------+--------+------------+--------------------------------------------------- + 0x24 | 4 | | interval: maximum time for the request on the + | | | server-side host controller +-----------+--------+------------+--------------------------------------------------- + 0x28 | 8 | | setup: data bytes for USB setup, filled with + | | | zeros if not used +-----------+--------+------------+--------------------------------------------------- + 0x30 | | | URB data. For ISO transfers the padding between + | | | each ISO packets is not transmitted. + + + Allowed transfer_flags | value | control | interrupt | bulk | isochronous + -------------------------+------------+---------+-----------+----------+------------- + URB_SHORT_NOT_OK | 0x00000001 | only in | only in | only in | no + URB_ISO_ASAP | 0x00000002 | no | no | no | yes + URB_NO_TRANSFER_DMA_MAP | 0x00000004 | yes | yes | yes | yes + URB_NO_FSBR | 0x00000020 | yes | no | no | no + URB_ZERO_PACKET | 0x00000040 | no | no | only out | no + URB_NO_INTERRUPT | 0x00000080 | yes | yes | yes | yes + URB_FREE_BUFFER | 0x00000100 | yes | yes | yes | yes + URB_DIR_MASK | 0x00000200 | yes | yes | yes | yes + + +USBIP_RET_SUBMIT: Reply for submitting an URB + + Offset | Length | Value | Description +-----------+--------+------------+--------------------------------------------------- + 0 | 4 | 0x00000003 | command +-----------+--------+------------+--------------------------------------------------- + 4 | 4 | | seqnum: URB sequence number +-----------+--------+------------+--------------------------------------------------- + 8 | 4 | | devid +-----------+--------+------------+--------------------------------------------------- + 0xC | 4 | | direction: 0: USBIP_DIR_OUT + | | | 1: USBIP_DIR_IN +-----------+--------+------------+--------------------------------------------------- + 0x10 | 4 | | ep: endpoint number +-----------+--------+------------+--------------------------------------------------- + 0x14 | 4 | | status: zero for successful URB transaction, + | | | otherwise some kind of error happened. +-----------+--------+------------+--------------------------------------------------- + 0x18 | 4 | n | actual_length: number of URB data bytes +-----------+--------+------------+--------------------------------------------------- + 0x1C | 4 | | start_frame: for an ISO frame the actually + | | | selected frame for transmit. +-----------+--------+------------+--------------------------------------------------- + 0x20 | 4 | | number_of_packets +-----------+--------+------------+--------------------------------------------------- + 0x24 | 4 | | error_count +-----------+--------+------------+--------------------------------------------------- + 0x28 | 8 | | setup: data bytes for USB setup, filled with + | | | zeros if not used +-----------+--------+------------+--------------------------------------------------- + 0x30 | n | | URB data bytes. For ISO transfers the padding + | | | between each ISO packets is not transmitted. + +USBIP_CMD_UNLINK: Unlink an URB + + Offset | Length | Value | Description +-----------+--------+------------+--------------------------------------------------- + 0 | 4 | 0x00000002 | command: URB unlink command +-----------+--------+------------+--------------------------------------------------- + 4 | 4 | | seqnum: URB sequence number to unlink: FIXME: is this so? +-----------+--------+------------+--------------------------------------------------- + 8 | 4 | | devid +-----------+--------+------------+--------------------------------------------------- + 0xC | 4 | | direction: 0: USBIP_DIR_OUT + | | | 1: USBIP_DIR_IN +-----------+--------+------------+--------------------------------------------------- + 0x10 | 4 | | ep: endpoint number: zero +-----------+--------+------------+--------------------------------------------------- + 0x14 | 4 | | seqnum: the URB sequence number given previously + | | | at USBIP_CMD_SUBMIT.seqnum field +-----------+--------+------------+--------------------------------------------------- + 0x30 | n | | URB data bytes. For ISO transfers the padding + | | | between each ISO packets is not transmitted. + +USBIP_RET_UNLINK: Reply for URB unlink + + Offset | Length | Value | Description +-----------+--------+------------+--------------------------------------------------- + 0 | 4 | 0x00000004 | command: reply for the URB unlink command +-----------+--------+------------+--------------------------------------------------- + 4 | 4 | | seqnum: the unlinked URB sequence number +-----------+--------+------------+--------------------------------------------------- + 8 | 4 | | devid +-----------+--------+------------+--------------------------------------------------- + 0xC | 4 | | direction: 0: USBIP_DIR_OUT + | | | 1: USBIP_DIR_IN +-----------+--------+------------+--------------------------------------------------- + 0x10 | 4 | | ep: endpoint number +-----------+--------+------------+--------------------------------------------------- + 0x14 | 4 | | status: This is the value contained in the + | | | urb->status in the URB completition handler. + | | | FIXME: a better explanation needed. +-----------+--------+------------+--------------------------------------------------- + 0x30 | n | | URB data bytes. For ISO transfers the padding + | | | between each ISO packets is not transmitted. diff --git a/drivers/usb/usbip/vhci.h b/drivers/usb/usbip/vhci.h new file mode 100644 index 000000000000..a863a98a91ce --- /dev/null +++ b/drivers/usb/usbip/vhci.h @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2003-2008 Takahiro Hirofuchi + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + */ + +#ifndef __USBIP_VHCI_H +#define __USBIP_VHCI_H + +#include <linux/device.h> +#include <linux/list.h> +#include <linux/spinlock.h> +#include <linux/sysfs.h> +#include <linux/types.h> +#include <linux/usb.h> +#include <linux/usb/hcd.h> +#include <linux/wait.h> + +struct vhci_device { + struct usb_device *udev; + + /* + * devid specifies a remote usb device uniquely instead + * of combination of busnum and devnum. + */ + __u32 devid; + + /* speed of a remote device */ + enum usb_device_speed speed; + + /* vhci root-hub port to which this device is attached */ + __u32 rhport; + + struct usbip_device ud; + + /* lock for the below link lists */ + spinlock_t priv_lock; + + /* vhci_priv is linked to one of them. */ + struct list_head priv_tx; + struct list_head priv_rx; + + /* vhci_unlink is linked to one of them */ + struct list_head unlink_tx; + struct list_head unlink_rx; + + /* vhci_tx thread sleeps for this queue */ + wait_queue_head_t waitq_tx; +}; + +/* urb->hcpriv, use container_of() */ +struct vhci_priv { + unsigned long seqnum; + struct list_head list; + + struct vhci_device *vdev; + struct urb *urb; +}; + +struct vhci_unlink { + /* seqnum of this request */ + unsigned long seqnum; + + struct list_head list; + + /* seqnum of the unlink target */ + unsigned long unlink_seqnum; +}; + +/* Number of supported ports. Value has an upperbound of USB_MAXCHILDREN */ +#define VHCI_NPORTS 8 + +/* for usb_bus.hcpriv */ +struct vhci_hcd { + spinlock_t lock; + + u32 port_status[VHCI_NPORTS]; + + unsigned resuming:1; + unsigned long re_timeout; + + atomic_t seqnum; + + /* + * NOTE: + * wIndex shows the port number and begins from 1. + * But, the index of this array begins from 0. + */ + struct vhci_device vdev[VHCI_NPORTS]; +}; + +extern struct vhci_hcd *the_controller; +extern const struct attribute_group dev_attr_group; + +/* vhci_hcd.c */ +void rh_port_connect(int rhport, enum usb_device_speed speed); + +/* vhci_rx.c */ +struct urb *pickup_urb_and_free_priv(struct vhci_device *vdev, __u32 seqnum); +int vhci_rx_loop(void *data); + +/* vhci_tx.c */ +int vhci_tx_loop(void *data); + +static inline struct vhci_device *port_to_vdev(__u32 port) +{ + return &the_controller->vdev[port]; +} + +static inline struct vhci_hcd *hcd_to_vhci(struct usb_hcd *hcd) +{ + return (struct vhci_hcd *) (hcd->hcd_priv); +} + +static inline struct usb_hcd *vhci_to_hcd(struct vhci_hcd *vhci) +{ + return container_of((void *) vhci, struct usb_hcd, hcd_priv); +} + +static inline struct device *vhci_dev(struct vhci_hcd *vhci) +{ + return vhci_to_hcd(vhci)->self.controller; +} + +#endif /* __USBIP_VHCI_H */ diff --git a/drivers/usb/usbip/vhci_hcd.c b/drivers/usb/usbip/vhci_hcd.c new file mode 100644 index 000000000000..c02374b6049c --- /dev/null +++ b/drivers/usb/usbip/vhci_hcd.c @@ -0,0 +1,1171 @@ +/* + * Copyright (C) 2003-2008 Takahiro Hirofuchi + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include <linux/init.h> +#include <linux/file.h> +#include <linux/kernel.h> +#include <linux/kthread.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +#include "usbip_common.h" +#include "vhci.h" + +#define DRIVER_AUTHOR "Takahiro Hirofuchi" +#define DRIVER_DESC "USB/IP 'Virtual' Host Controller (VHCI) Driver" + +/* + * TODO + * - update root hub emulation + * - move the emulation code to userland ? + * porting to other operating systems + * minimize kernel code + * - add suspend/resume code + * - clean up everything + */ + +/* See usb gadget dummy hcd */ + +static int vhci_hub_status(struct usb_hcd *hcd, char *buff); +static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, + u16 wIndex, char *buff, u16 wLength); +static int vhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, + gfp_t mem_flags); +static int vhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status); +static int vhci_start(struct usb_hcd *vhci_hcd); +static void vhci_stop(struct usb_hcd *hcd); +static int vhci_get_frame_number(struct usb_hcd *hcd); + +static const char driver_name[] = "vhci_hcd"; +static const char driver_desc[] = "USB/IP Virtual Host Controller"; + +struct vhci_hcd *the_controller; + +static const char * const bit_desc[] = { + "CONNECTION", /*0*/ + "ENABLE", /*1*/ + "SUSPEND", /*2*/ + "OVER_CURRENT", /*3*/ + "RESET", /*4*/ + "R5", /*5*/ + "R6", /*6*/ + "R7", /*7*/ + "POWER", /*8*/ + "LOWSPEED", /*9*/ + "HIGHSPEED", /*10*/ + "PORT_TEST", /*11*/ + "INDICATOR", /*12*/ + "R13", /*13*/ + "R14", /*14*/ + "R15", /*15*/ + "C_CONNECTION", /*16*/ + "C_ENABLE", /*17*/ + "C_SUSPEND", /*18*/ + "C_OVER_CURRENT", /*19*/ + "C_RESET", /*20*/ + "R21", /*21*/ + "R22", /*22*/ + "R23", /*23*/ + "R24", /*24*/ + "R25", /*25*/ + "R26", /*26*/ + "R27", /*27*/ + "R28", /*28*/ + "R29", /*29*/ + "R30", /*30*/ + "R31", /*31*/ +}; + +static void dump_port_status_diff(u32 prev_status, u32 new_status) +{ + int i = 0; + u32 bit = 1; + + pr_debug("status prev -> new: %08x -> %08x\n", prev_status, new_status); + while (bit) { + u32 prev = prev_status & bit; + u32 new = new_status & bit; + char change; + + if (!prev && new) + change = '+'; + else if (prev && !new) + change = '-'; + else + change = ' '; + + if (prev || new) + pr_debug(" %c%s\n", change, bit_desc[i]); + bit <<= 1; + i++; + } + pr_debug("\n"); +} + +void rh_port_connect(int rhport, enum usb_device_speed speed) +{ + usbip_dbg_vhci_rh("rh_port_connect %d\n", rhport); + + spin_lock(&the_controller->lock); + + the_controller->port_status[rhport] |= USB_PORT_STAT_CONNECTION + | (1 << USB_PORT_FEAT_C_CONNECTION); + + switch (speed) { + case USB_SPEED_HIGH: + the_controller->port_status[rhport] |= USB_PORT_STAT_HIGH_SPEED; + break; + case USB_SPEED_LOW: + the_controller->port_status[rhport] |= USB_PORT_STAT_LOW_SPEED; + break; + default: + break; + } + + spin_unlock(&the_controller->lock); + + usb_hcd_poll_rh_status(vhci_to_hcd(the_controller)); +} + +static void rh_port_disconnect(int rhport) +{ + usbip_dbg_vhci_rh("rh_port_disconnect %d\n", rhport); + + spin_lock(&the_controller->lock); + + the_controller->port_status[rhport] &= ~USB_PORT_STAT_CONNECTION; + the_controller->port_status[rhport] |= + (1 << USB_PORT_FEAT_C_CONNECTION); + + spin_unlock(&the_controller->lock); + usb_hcd_poll_rh_status(vhci_to_hcd(the_controller)); +} + +#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) + +/* + * 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. + * ... + */ +static int vhci_hub_status(struct usb_hcd *hcd, char *buf) +{ + struct vhci_hcd *vhci; + int retval; + int rhport; + int changed = 0; + + retval = DIV_ROUND_UP(VHCI_NPORTS + 1, 8); + memset(buf, 0, retval); + + vhci = hcd_to_vhci(hcd); + + spin_lock(&vhci->lock); + 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_NPORTS; rhport++) { + if ((vhci->port_status[rhport] & PORT_C_MASK)) { + /* The status of a port has been changed, */ + usbip_dbg_vhci_rh("port %d status changed\n", rhport); + + buf[(rhport + 1) / 8] |= 1 << (rhport + 1) % 8; + changed = 1; + } + } + + if ((hcd->state == HC_STATE_SUSPENDED) && (changed == 1)) + usb_hcd_resume_root_hub(hcd); + +done: + spin_unlock(&vhci->lock); + return changed ? retval : 0; +} + +static inline void hub_descriptor(struct usb_hub_descriptor *desc) +{ + memset(desc, 0, sizeof(*desc)); + desc->bDescriptorType = 0x29; + desc->bDescLength = 9; + desc->wHubCharacteristics = (__constant_cpu_to_le16(0x0001)); + desc->bNbrPorts = VHCI_NPORTS; + desc->u.hs.DeviceRemovable[0] = 0xff; + desc->u.hs.DeviceRemovable[1] = 0xff; +} + +static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, + u16 wIndex, char *buf, u16 wLength) +{ + struct vhci_hcd *dum; + int retval = 0; + int rhport; + + u32 prev_port_status[VHCI_NPORTS]; + + if (!HCD_HW_ACCESSIBLE(hcd)) + return -ETIMEDOUT; + + /* + * NOTE: + * wIndex shows the port number and begins from 1. + */ + usbip_dbg_vhci_rh("typeReq %x wValue %x wIndex %x\n", typeReq, wValue, + wIndex); + if (wIndex > VHCI_NPORTS) + pr_err("invalid port number %d\n", wIndex); + rhport = ((__u8)(wIndex & 0x00ff)) - 1; + + dum = hcd_to_vhci(hcd); + + spin_lock(&dum->lock); + + /* store old status and compare now and old later */ + if (usbip_dbg_flag_vhci_rh) { + memcpy(prev_port_status, dum->port_status, + sizeof(prev_port_status)); + } + + switch (typeReq) { + case ClearHubFeature: + usbip_dbg_vhci_rh(" ClearHubFeature\n"); + break; + case ClearPortFeature: + switch (wValue) { + case USB_PORT_FEAT_SUSPEND: + if (dum->port_status[rhport] & USB_PORT_STAT_SUSPEND) { + /* 20msec signaling */ + dum->resuming = 1; + dum->re_timeout = + jiffies + msecs_to_jiffies(20); + } + break; + case USB_PORT_FEAT_POWER: + usbip_dbg_vhci_rh( + " ClearPortFeature: USB_PORT_FEAT_POWER\n"); + dum->port_status[rhport] = 0; + dum->resuming = 0; + break; + case USB_PORT_FEAT_C_RESET: + usbip_dbg_vhci_rh( + " ClearPortFeature: USB_PORT_FEAT_C_RESET\n"); + switch (dum->vdev[rhport].speed) { + case USB_SPEED_HIGH: + dum->port_status[rhport] |= + USB_PORT_STAT_HIGH_SPEED; + break; + case USB_SPEED_LOW: + dum->port_status[rhport] |= + USB_PORT_STAT_LOW_SPEED; + break; + default: + break; + } + default: + usbip_dbg_vhci_rh(" ClearPortFeature: default %x\n", + wValue); + dum->port_status[rhport] &= ~(1 << wValue); + break; + } + break; + case GetHubDescriptor: + usbip_dbg_vhci_rh(" GetHubDescriptor\n"); + hub_descriptor((struct usb_hub_descriptor *) buf); + 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 (wIndex > VHCI_NPORTS || wIndex < 1) { + pr_err("invalid port number %d\n", wIndex); + retval = -EPIPE; + } + + /* we do not care about resume. */ + + /* whoever resets or resumes must GetPortStatus to + * complete it!! + */ + if (dum->resuming && time_after(jiffies, dum->re_timeout)) { + dum->port_status[rhport] |= + (1 << USB_PORT_FEAT_C_SUSPEND); + dum->port_status[rhport] &= + ~(1 << USB_PORT_FEAT_SUSPEND); + dum->resuming = 0; + dum->re_timeout = 0; + } + + if ((dum->port_status[rhport] & (1 << USB_PORT_FEAT_RESET)) != + 0 && time_after(jiffies, dum->re_timeout)) { + dum->port_status[rhport] |= + (1 << USB_PORT_FEAT_C_RESET); + dum->port_status[rhport] &= + ~(1 << USB_PORT_FEAT_RESET); + dum->re_timeout = 0; + + if (dum->vdev[rhport].ud.status == + VDEV_ST_NOTASSIGNED) { + usbip_dbg_vhci_rh( + " enable rhport %d (status %u)\n", + rhport, + dum->vdev[rhport].ud.status); + dum->port_status[rhport] |= + USB_PORT_STAT_ENABLE; + } + } + ((__le16 *) buf)[0] = cpu_to_le16(dum->port_status[rhport]); + ((__le16 *) buf)[1] = + cpu_to_le16(dum->port_status[rhport] >> 16); + + 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_SUSPEND: + usbip_dbg_vhci_rh( + " SetPortFeature: USB_PORT_FEAT_SUSPEND\n"); + break; + case USB_PORT_FEAT_RESET: + usbip_dbg_vhci_rh( + " SetPortFeature: USB_PORT_FEAT_RESET\n"); + /* if it's already running, disconnect first */ + if (dum->port_status[rhport] & USB_PORT_STAT_ENABLE) { + dum->port_status[rhport] &= + ~(USB_PORT_STAT_ENABLE | + USB_PORT_STAT_LOW_SPEED | + USB_PORT_STAT_HIGH_SPEED); + /* FIXME test that code path! */ + } + /* 50msec reset signaling */ + dum->re_timeout = jiffies + msecs_to_jiffies(50); + + /* FALLTHROUGH */ + default: + usbip_dbg_vhci_rh(" SetPortFeature: default %d\n", + wValue); + dum->port_status[rhport] |= (1 << wValue); + break; + } + break; + + default: + pr_err("default: no such request\n"); + + /* "protocol stall" on error */ + retval = -EPIPE; + } + + if (usbip_dbg_flag_vhci_rh) { + pr_debug("port %d\n", rhport); + /* Only dump valid port status */ + if (rhport >= 0) { + dump_port_status_diff(prev_port_status[rhport], + dum->port_status[rhport]); + } + } + usbip_dbg_vhci_rh(" bye\n"); + + spin_unlock(&dum->lock); + + return retval; +} + +static struct vhci_device *get_vdev(struct usb_device *udev) +{ + int i; + + if (!udev) + return NULL; + + for (i = 0; i < VHCI_NPORTS; i++) + if (the_controller->vdev[i].udev == udev) + return port_to_vdev(i); + + return NULL; +} + +static void vhci_tx_urb(struct urb *urb) +{ + struct vhci_device *vdev = get_vdev(urb->dev); + struct vhci_priv *priv; + + if (!vdev) { + pr_err("could not get virtual device"); + return; + } + + priv = kzalloc(sizeof(struct vhci_priv), GFP_ATOMIC); + if (!priv) { + usbip_event_add(&vdev->ud, VDEV_EVENT_ERROR_MALLOC); + return; + } + + spin_lock(&vdev->priv_lock); + + priv->seqnum = atomic_inc_return(&the_controller->seqnum); + if (priv->seqnum == 0xffff) + dev_info(&urb->dev->dev, "seqnum max\n"); + + priv->vdev = vdev; + priv->urb = urb; + + urb->hcpriv = (void *) priv; + + list_add_tail(&priv->list, &vdev->priv_tx); + + wake_up(&vdev->waitq_tx); + spin_unlock(&vdev->priv_lock); +} + +static int vhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, + gfp_t mem_flags) +{ + struct device *dev = &urb->dev->dev; + int ret = 0; + struct vhci_device *vdev; + + usbip_dbg_vhci_hc("enter, usb_hcd %p urb %p mem_flags %d\n", + hcd, urb, mem_flags); + + /* patch to usb_sg_init() is in 2.5.60 */ + BUG_ON(!urb->transfer_buffer && urb->transfer_buffer_length); + + spin_lock(&the_controller->lock); + + if (urb->status != -EINPROGRESS) { + dev_err(dev, "URB already unlinked!, status %d\n", urb->status); + spin_unlock(&the_controller->lock); + return urb->status; + } + + vdev = port_to_vdev(urb->dev->portnum-1); + + /* 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(&the_controller->lock); + 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) { + __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; + } + + 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); + + if (vdev->udev) + usb_put_dev(vdev->udev); + vdev->udev = usb_get_dev(urb->dev); + + spin_lock(&vdev->ud.lock); + vdev->ud.status = VDEV_ST_USED; + spin_unlock(&vdev->ud.lock); + + 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"); + + if (vdev->udev) + usb_put_dev(vdev->udev); + vdev->udev = usb_get_dev(urb->dev); + 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; + } + + } + +out: + vhci_tx_urb(urb); + spin_unlock(&the_controller->lock); + + return 0; + +no_need_xmit: + usb_hcd_unlink_urb_from_ep(hcd, urb); +no_need_unlink: + spin_unlock(&the_controller->lock); + usb_hcd_giveback_urb(vhci_to_hcd(the_controller), urb, urb->status); + 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 + * + */ +static int vhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) +{ + struct vhci_priv *priv; + struct vhci_device *vdev; + + pr_info("dequeue a urb %p\n", urb); + + spin_lock(&the_controller->lock); + + priv = urb->hcpriv; + if (!priv) { + /* URB was never linked! or will be soon given back by + * vhci_rx. */ + spin_unlock(&the_controller->lock); + return 0; + } + + { + int ret = 0; + + ret = usb_hcd_check_unlink_urb(hcd, urb, status); + if (ret) { + spin_unlock(&the_controller->lock); + return ret; + } + } + + /* send unlink request here? */ + vdev = priv->vdev; + + if (!vdev->ud.tcp_socket) { + /* tcp connection is closed */ + spin_lock(&vdev->priv_lock); + + pr_info("device %p seems to be disconnected\n", vdev); + list_del(&priv->list); + kfree(priv); + urb->hcpriv = NULL; + + spin_unlock(&vdev->priv_lock); + + /* + * 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. + */ + pr_info("gives back urb %p\n", urb); + + usb_hcd_unlink_urb_from_ep(hcd, urb); + + spin_unlock(&the_controller->lock); + usb_hcd_giveback_urb(vhci_to_hcd(the_controller), urb, + urb->status); + spin_lock(&the_controller->lock); + + } else { + /* tcp connection is alive */ + struct vhci_unlink *unlink; + + spin_lock(&vdev->priv_lock); + + /* setup CMD_UNLINK pdu */ + unlink = kzalloc(sizeof(struct vhci_unlink), GFP_ATOMIC); + if (!unlink) { + spin_unlock(&vdev->priv_lock); + spin_unlock(&the_controller->lock); + usbip_event_add(&vdev->ud, VDEV_EVENT_ERROR_MALLOC); + return -ENOMEM; + } + + unlink->seqnum = atomic_inc_return(&the_controller->seqnum); + if (unlink->seqnum == 0xffff) + pr_info("seqnum max\n"); + + unlink->unlink_seqnum = priv->seqnum; + + pr_info("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(&vdev->priv_lock); + } + + spin_unlock(&the_controller->lock); + + usbip_dbg_vhci_hc("leave\n"); + return 0; +} + +static void vhci_device_unlink_cleanup(struct vhci_device *vdev) +{ + struct vhci_unlink *unlink, *tmp; + + spin_lock(&the_controller->lock); + spin_lock(&vdev->priv_lock); + + list_for_each_entry_safe(unlink, tmp, &vdev->unlink_tx, list) { + pr_info("unlink cleanup tx %lu\n", unlink->unlink_seqnum); + list_del(&unlink->list); + kfree(unlink); + } + + while (!list_empty(&vdev->unlink_rx)) { + struct urb *urb; + + unlink = list_first_entry(&vdev->unlink_rx, struct vhci_unlink, + list); + + /* give back URB of unanswered unlink request */ + pr_info("unlink cleanup rx %lu\n", unlink->unlink_seqnum); + + urb = pickup_urb_and_free_priv(vdev, unlink->unlink_seqnum); + if (!urb) { + pr_info("the urb (seqnum %lu) was already given back\n", + unlink->unlink_seqnum); + list_del(&unlink->list); + kfree(unlink); + continue; + } + + urb->status = -ENODEV; + + usb_hcd_unlink_urb_from_ep(vhci_to_hcd(the_controller), urb); + + list_del(&unlink->list); + + spin_unlock(&vdev->priv_lock); + spin_unlock(&the_controller->lock); + + usb_hcd_giveback_urb(vhci_to_hcd(the_controller), urb, + urb->status); + + spin_lock(&the_controller->lock); + spin_lock(&vdev->priv_lock); + + kfree(unlink); + } + + spin_unlock(&vdev->priv_lock); + spin_unlock(&the_controller->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) { + pr_debug("shutdown tcp_socket %p\n", ud->tcp_socket); + 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; + } + 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->rhport); + + pr_info("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; + + if (vdev->udev) + usb_put_dev(vdev->udev); + vdev->udev = NULL; + + if (ud->tcp_socket) { + sockfd_put(ud->tcp_socket); + 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)); + + vdev->ud.side = USBIP_VHCI; + vdev->ud.status = VDEV_ST_NULL; + 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); + 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; + + usbip_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->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) { + pr_err("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; + + usbip_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); + } +} + +static int vhci_get_frame_number(struct usb_hcd *hcd) +{ + pr_err("Not yet implemented\n"); + return 0; +} + +#ifdef CONFIG_PM + +/* FIXME: suspend/resume */ +static int vhci_bus_suspend(struct usb_hcd *hcd) +{ + struct vhci_hcd *vhci = hcd_to_vhci(hcd); + + dev_dbg(&hcd->self.root_hub->dev, "%s\n", __func__); + + spin_lock(&vhci->lock); + hcd->state = HC_STATE_SUSPENDED; + spin_unlock(&vhci->lock); + + return 0; +} + +static int vhci_bus_resume(struct usb_hcd *hcd) +{ + struct vhci_hcd *vhci = hcd_to_vhci(hcd); + int rc = 0; + + dev_dbg(&hcd->self.root_hub->dev, "%s\n", __func__); + + spin_lock(&vhci->lock); + if (!HCD_HW_ACCESSIBLE(hcd)) + rc = -ESHUTDOWN; + else + hcd->state = HC_STATE_RUNNING; + spin_unlock(&vhci->lock); + + return rc; +} + +#else + +#define vhci_bus_suspend NULL +#define vhci_bus_resume NULL +#endif + +static 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; + + usbip_dbg_vhci_hc("name %s id %d\n", pdev->name, pdev->id); + + /* + * Allocate and initialize hcd. + * Our private data is also allocated automatically. + */ + hcd = usb_create_hcd(&vhci_hc_driver, &pdev->dev, dev_name(&pdev->dev)); + if (!hcd) { + pr_err("create hcd failed\n"); + return -ENOMEM; + } + hcd->has_tt = 1; + + /* 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) { + pr_err("usb_add_hcd failed %d\n", ret); + usb_put_hcd(hcd); + the_controller = NULL; + return ret; + } + + usbip_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) +{ + struct usb_hcd *hcd; + int rhport = 0; + int connected = 0; + int ret = 0; + + hcd = platform_get_drvdata(pdev); + + spin_lock(&the_controller->lock); + + for (rhport = 0; rhport < VHCI_NPORTS; rhport++) + if (the_controller->port_status[rhport] & + USB_PORT_STAT_CONNECTION) + connected += 1; + + spin_unlock(&the_controller->lock); + + if (connected > 0) { + dev_info(&pdev->dev, + "We have %d active connection%s. Do not suspend.\n", + connected, (connected == 1 ? "" : "s")); + ret = -EBUSY; + } else { + dev_info(&pdev->dev, "suspend vhci_hcd"); + clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); + } + + return ret; +} + +static int vhci_hcd_resume(struct platform_device *pdev) +{ + struct usb_hcd *hcd; + + dev_dbg(&pdev->dev, "%s\n", __func__); + + hcd = platform_get_drvdata(pdev); + set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); + usb_hcd_poll_rh_status(hcd); + + return 0; +} + +#else + +#define vhci_hcd_suspend NULL +#define vhci_hcd_resume NULL + +#endif + +static struct platform_driver vhci_driver = { + .probe = vhci_hcd_probe, + .remove = vhci_hcd_remove, + .suspend = vhci_hcd_suspend, + .resume = vhci_hcd_resume, + .driver = { + .name = 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) +{ +} + +static struct platform_device the_pdev = { + /* should be the same name as driver_name */ + .name = driver_name, + .id = -1, + .dev = { + .release = the_pdev_release, + }, +}; + +static int __init vhci_hcd_init(void) +{ + int ret; + + if (usb_disabled()) + return -ENODEV; + + ret = platform_driver_register(&vhci_driver); + if (ret) + goto err_driver_register; + + ret = platform_device_register(&the_pdev); + if (ret) + goto err_platform_device_register; + + pr_info(DRIVER_DESC " v" USBIP_VERSION "\n"); + return ret; + +err_platform_device_register: + platform_driver_unregister(&vhci_driver); +err_driver_register: + return ret; +} + +static void __exit vhci_hcd_exit(void) +{ + platform_device_unregister(&the_pdev); + platform_driver_unregister(&vhci_driver); +} + +module_init(vhci_hcd_init); +module_exit(vhci_hcd_exit); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); +MODULE_VERSION(USBIP_VERSION); diff --git a/drivers/usb/usbip/vhci_rx.c b/drivers/usb/usbip/vhci_rx.c new file mode 100644 index 000000000000..00e4a54308e4 --- /dev/null +++ b/drivers/usb/usbip/vhci_rx.c @@ -0,0 +1,268 @@ +/* + * Copyright (C) 2003-2008 Takahiro Hirofuchi + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include <linux/kthread.h> +#include <linux/slab.h> + +#include "usbip_common.h" +#include "vhci.h" + +/* get URB from transmitted urb queue. caller must hold vdev->priv_lock */ +struct urb *pickup_urb_and_free_priv(struct vhci_device *vdev, __u32 seqnum) +{ + struct vhci_priv *priv, *tmp; + struct urb *urb = NULL; + int status; + + list_for_each_entry_safe(priv, tmp, &vdev->priv_rx, list) { + if (priv->seqnum != seqnum) + continue; + + urb = priv->urb; + status = urb->status; + + usbip_dbg_vhci_rx("find urb %p vurb %p seqnum %u\n", + urb, priv, seqnum); + + switch (status) { + case -ENOENT: + /* fall through */ + case -ECONNRESET: + dev_info(&urb->dev->dev, + "urb %p was unlinked %ssynchronuously.\n", urb, + status == -ENOENT ? "" : "a"); + break; + case -EINPROGRESS: + /* no info output */ + break; + default: + dev_info(&urb->dev->dev, + "urb %p may be in a error, status %d\n", urb, + status); + } + + list_del(&priv->list); + kfree(priv); + urb->hcpriv = NULL; + + break; + } + + return urb; +} + +static void vhci_recv_ret_submit(struct vhci_device *vdev, + struct usbip_header *pdu) +{ + struct usbip_device *ud = &vdev->ud; + struct urb *urb; + + spin_lock(&vdev->priv_lock); + urb = pickup_urb_and_free_priv(vdev, pdu->base.seqnum); + spin_unlock(&vdev->priv_lock); + + if (!urb) { + pr_err("cannot find a urb of seqnum %u\n", pdu->base.seqnum); + pr_info("max seqnum %d\n", + atomic_read(&the_controller->seqnum)); + usbip_event_add(ud, VDEV_EVENT_ERROR_TCP); + return; + } + + /* unpack the pdu to a urb */ + usbip_pack_pdu(pdu, urb, USBIP_RET_SUBMIT, 0); + + /* recv transfer buffer */ + if (usbip_recv_xbuff(ud, urb) < 0) + return; + + /* recv iso_packet_descriptor */ + if (usbip_recv_iso(ud, urb) < 0) + return; + + /* restore the padding in iso packets */ + usbip_pad_iso(ud, urb); + + if (usbip_dbg_flag_vhci_rx) + usbip_dump_urb(urb); + + usbip_dbg_vhci_rx("now giveback urb %p\n", urb); + + spin_lock(&the_controller->lock); + usb_hcd_unlink_urb_from_ep(vhci_to_hcd(the_controller), urb); + spin_unlock(&the_controller->lock); + + usb_hcd_giveback_urb(vhci_to_hcd(the_controller), urb, urb->status); + + usbip_dbg_vhci_rx("Leave\n"); +} + +static struct vhci_unlink *dequeue_pending_unlink(struct vhci_device *vdev, + struct usbip_header *pdu) +{ + struct vhci_unlink *unlink, *tmp; + + spin_lock(&vdev->priv_lock); + + list_for_each_entry_safe(unlink, tmp, &vdev->unlink_rx, list) { + pr_info("unlink->seqnum %lu\n", unlink->seqnum); + if (unlink->seqnum == pdu->base.seqnum) { + usbip_dbg_vhci_rx("found pending unlink, %lu\n", + unlink->seqnum); + list_del(&unlink->list); + + spin_unlock(&vdev->priv_lock); + return unlink; + } + } + + spin_unlock(&vdev->priv_lock); + + return NULL; +} + +static void vhci_recv_ret_unlink(struct vhci_device *vdev, + struct usbip_header *pdu) +{ + struct vhci_unlink *unlink; + struct urb *urb; + + usbip_dump_header(pdu); + + unlink = dequeue_pending_unlink(vdev, pdu); + if (!unlink) { + pr_info("cannot find the pending unlink %u\n", + pdu->base.seqnum); + return; + } + + spin_lock(&vdev->priv_lock); + urb = pickup_urb_and_free_priv(vdev, unlink->unlink_seqnum); + spin_unlock(&vdev->priv_lock); + + if (!urb) { + /* + * I get the result of a unlink request. But, it seems that I + * already received the result of its submit result and gave + * back the URB. + */ + pr_info("the urb (seqnum %d) was already given back\n", + pdu->base.seqnum); + } else { + usbip_dbg_vhci_rx("now giveback urb %p\n", urb); + + /* If unlink is successful, status is -ECONNRESET */ + urb->status = pdu->u.ret_unlink.status; + pr_info("urb->status %d\n", urb->status); + + spin_lock(&the_controller->lock); + usb_hcd_unlink_urb_from_ep(vhci_to_hcd(the_controller), urb); + spin_unlock(&the_controller->lock); + + usb_hcd_giveback_urb(vhci_to_hcd(the_controller), urb, + urb->status); + } + + kfree(unlink); +} + +static int vhci_priv_tx_empty(struct vhci_device *vdev) +{ + int empty = 0; + + spin_lock(&vdev->priv_lock); + empty = list_empty(&vdev->priv_rx); + spin_unlock(&vdev->priv_lock); + + return empty; +} + +/* recv a pdu */ +static void vhci_rx_pdu(struct usbip_device *ud) +{ + int ret; + struct usbip_header pdu; + struct vhci_device *vdev = container_of(ud, struct vhci_device, ud); + + usbip_dbg_vhci_rx("Enter\n"); + + memset(&pdu, 0, sizeof(pdu)); + + /* receive a pdu header */ + ret = usbip_recv(ud->tcp_socket, &pdu, sizeof(pdu)); + if (ret < 0) { + if (ret == -ECONNRESET) + pr_info("connection reset by peer\n"); + else if (ret == -EAGAIN) { + /* ignore if connection was idle */ + if (vhci_priv_tx_empty(vdev)) + return; + pr_info("connection timed out with pending urbs\n"); + } else if (ret != -ERESTARTSYS) + pr_info("xmit failed %d\n", ret); + + usbip_event_add(ud, VDEV_EVENT_ERROR_TCP); + return; + } + if (ret == 0) { + pr_info("connection closed"); + usbip_event_add(ud, VDEV_EVENT_DOWN); + return; + } + if (ret != sizeof(pdu)) { + pr_err("received pdu size is %d, should be %d\n", ret, + (unsigned int)sizeof(pdu)); + usbip_event_add(ud, VDEV_EVENT_ERROR_TCP); + return; + } + + usbip_header_correct_endian(&pdu, 0); + + if (usbip_dbg_flag_vhci_rx) + usbip_dump_header(&pdu); + + switch (pdu.base.command) { + case USBIP_RET_SUBMIT: + vhci_recv_ret_submit(vdev, &pdu); + break; + case USBIP_RET_UNLINK: + vhci_recv_ret_unlink(vdev, &pdu); + break; + default: + /* NOT REACHED */ + pr_err("unknown pdu %u\n", pdu.base.command); + usbip_dump_header(&pdu); + usbip_event_add(ud, VDEV_EVENT_ERROR_TCP); + break; + } +} + +int vhci_rx_loop(void *data) +{ + struct usbip_device *ud = data; + + while (!kthread_should_stop()) { + if (usbip_event_happened(ud)) + break; + + vhci_rx_pdu(ud); + } + + return 0; +} diff --git a/drivers/usb/usbip/vhci_sysfs.c b/drivers/usb/usbip/vhci_sysfs.c new file mode 100644 index 000000000000..211f43f67ea2 --- /dev/null +++ b/drivers/usb/usbip/vhci_sysfs.c @@ -0,0 +1,252 @@ +/* + * Copyright (C) 2003-2008 Takahiro Hirofuchi + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include <linux/kthread.h> +#include <linux/file.h> +#include <linux/net.h> + +#include "usbip_common.h" +#include "vhci.h" + +/* TODO: refine locking ?*/ + +/* Sysfs entry to show port status */ +static ssize_t status_show(struct device *dev, struct device_attribute *attr, + char *out) +{ + char *s = out; + int i = 0; + + BUG_ON(!the_controller || !out); + + spin_lock(&the_controller->lock); + + /* + * output example: + * prt sta spd dev socket local_busid + * 000 004 000 000 c5a7bb80 1-2.3 + * 001 004 000 000 d8cee980 2-3.4 + * + * IP address can be retrieved from a socket pointer address by looking + * up /proc/net/{tcp,tcp6}. Also, a userland program may remember a + * port number and its peer IP address. + */ + out += sprintf(out, + "prt sta spd bus dev socket local_busid\n"); + + for (i = 0; i < VHCI_NPORTS; i++) { + struct vhci_device *vdev = port_to_vdev(i); + + spin_lock(&vdev->ud.lock); + out += sprintf(out, "%03u %03u ", i, vdev->ud.status); + + if (vdev->ud.status == VDEV_ST_USED) { + out += sprintf(out, "%03u %08x ", + vdev->speed, vdev->devid); + out += sprintf(out, "%16p ", vdev->ud.tcp_socket); + out += sprintf(out, "%s", dev_name(&vdev->udev->dev)); + + } else { + out += sprintf(out, "000 000 000 0000000000000000 0-0"); + } + + out += sprintf(out, "\n"); + spin_unlock(&vdev->ud.lock); + } + + spin_unlock(&the_controller->lock); + + return out - s; +} +static DEVICE_ATTR_RO(status); + +/* Sysfs entry to shutdown a virtual connection */ +static int vhci_port_disconnect(__u32 rhport) +{ + struct vhci_device *vdev; + + usbip_dbg_vhci_sysfs("enter\n"); + + /* lock */ + spin_lock(&the_controller->lock); + + vdev = port_to_vdev(rhport); + + spin_lock(&vdev->ud.lock); + if (vdev->ud.status == VDEV_ST_NULL) { + pr_err("not connected %d\n", vdev->ud.status); + + /* unlock */ + spin_unlock(&vdev->ud.lock); + spin_unlock(&the_controller->lock); + + return -EINVAL; + } + + /* unlock */ + spin_unlock(&vdev->ud.lock); + spin_unlock(&the_controller->lock); + + usbip_event_add(&vdev->ud, VDEV_EVENT_DOWN); + + return 0; +} + +static ssize_t store_detach(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int err; + __u32 rhport = 0; + + if (sscanf(buf, "%u", &rhport) != 1) + return -EINVAL; + + /* check rhport */ + if (rhport >= VHCI_NPORTS) { + dev_err(dev, "invalid port %u\n", rhport); + return -EINVAL; + } + + err = vhci_port_disconnect(rhport); + if (err < 0) + return -EINVAL; + + usbip_dbg_vhci_sysfs("Leave\n"); + + return count; +} +static DEVICE_ATTR(detach, S_IWUSR, NULL, store_detach); + +/* Sysfs entry to establish a virtual connection */ +static int valid_args(__u32 rhport, enum usb_device_speed speed) +{ + /* check rhport */ + if (rhport >= VHCI_NPORTS) { + pr_err("port %u\n", rhport); + return -EINVAL; + } + + /* check speed */ + switch (speed) { + case USB_SPEED_LOW: + case USB_SPEED_FULL: + case USB_SPEED_HIGH: + case USB_SPEED_WIRELESS: + break; + default: + pr_err("Failed attach request for unsupported USB speed: %s\n", + usb_speed_string(speed)); + return -EINVAL; + } + + return 0; +} + +/* + * To start a new USB/IP attachment, a userland program needs to setup a TCP + * connection and then write its socket descriptor with remote device + * information into this sysfs file. + * + * A remote device is virtually attached to the root-hub port of @rhport with + * @speed. @devid is embedded into a request to specify the remote device in a + * server host. + * + * write() returns 0 on success, else negative errno. + */ +static ssize_t store_attach(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct vhci_device *vdev; + struct socket *socket; + int sockfd = 0; + __u32 rhport = 0, devid = 0, speed = 0; + int err; + + /* + * @rhport: port number of vhci_hcd + * @sockfd: socket descriptor of an established TCP connection + * @devid: unique device identifier in a remote host + * @speed: usb device speed in a remote host + */ + if (sscanf(buf, "%u %u %u %u", &rhport, &sockfd, &devid, &speed) != 4) + return -EINVAL; + + usbip_dbg_vhci_sysfs("rhport(%u) sockfd(%u) devid(%u) speed(%u)\n", + rhport, sockfd, devid, speed); + + /* check received parameters */ + if (valid_args(rhport, speed) < 0) + return -EINVAL; + + /* Extract socket from fd. */ + socket = sockfd_lookup(sockfd, &err); + if (!socket) + return -EINVAL; + + /* now need lock until setting vdev status as used */ + + /* begin a lock */ + spin_lock(&the_controller->lock); + vdev = port_to_vdev(rhport); + spin_lock(&vdev->ud.lock); + + if (vdev->ud.status != VDEV_ST_NULL) { + /* end of the lock */ + spin_unlock(&vdev->ud.lock); + spin_unlock(&the_controller->lock); + + sockfd_put(socket); + + dev_err(dev, "port %d already used\n", rhport); + return -EINVAL; + } + + dev_info(dev, + "rhport(%u) sockfd(%d) devid(%u) speed(%u) speed_str(%s)\n", + rhport, sockfd, devid, speed, usb_speed_string(speed)); + + vdev->devid = devid; + vdev->speed = speed; + vdev->ud.tcp_socket = socket; + vdev->ud.status = VDEV_ST_NOTASSIGNED; + + spin_unlock(&vdev->ud.lock); + spin_unlock(&the_controller->lock); + /* end the lock */ + + vdev->ud.tcp_rx = kthread_get_run(vhci_rx_loop, &vdev->ud, "vhci_rx"); + vdev->ud.tcp_tx = kthread_get_run(vhci_tx_loop, &vdev->ud, "vhci_tx"); + + rh_port_connect(rhport, speed); + + return count; +} +static DEVICE_ATTR(attach, S_IWUSR, NULL, store_attach); + +static struct attribute *dev_attrs[] = { + &dev_attr_status.attr, + &dev_attr_detach.attr, + &dev_attr_attach.attr, + &dev_attr_usbip_debug.attr, + NULL, +}; + +const struct attribute_group dev_attr_group = { + .attrs = dev_attrs, +}; diff --git a/drivers/usb/usbip/vhci_tx.c b/drivers/usb/usbip/vhci_tx.c new file mode 100644 index 000000000000..409fd99f3257 --- /dev/null +++ b/drivers/usb/usbip/vhci_tx.c @@ -0,0 +1,224 @@ +/* + * Copyright (C) 2003-2008 Takahiro Hirofuchi + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include <linux/kthread.h> +#include <linux/slab.h> + +#include "usbip_common.h" +#include "vhci.h" + +static void setup_cmd_submit_pdu(struct usbip_header *pdup, struct urb *urb) +{ + struct vhci_priv *priv = ((struct vhci_priv *)urb->hcpriv); + struct vhci_device *vdev = priv->vdev; + + usbip_dbg_vhci_tx("URB, local devnum %u, remote devid %u\n", + usb_pipedevice(urb->pipe), vdev->devid); + + pdup->base.command = USBIP_CMD_SUBMIT; + pdup->base.seqnum = priv->seqnum; + pdup->base.devid = vdev->devid; + pdup->base.direction = usb_pipein(urb->pipe) ? + USBIP_DIR_IN : USBIP_DIR_OUT; + pdup->base.ep = usb_pipeendpoint(urb->pipe); + + usbip_pack_pdu(pdup, urb, USBIP_CMD_SUBMIT, 1); + + if (urb->setup_packet) + memcpy(pdup->u.cmd_submit.setup, urb->setup_packet, 8); +} + +static struct vhci_priv *dequeue_from_priv_tx(struct vhci_device *vdev) +{ + struct vhci_priv *priv, *tmp; + + spin_lock(&vdev->priv_lock); + + list_for_each_entry_safe(priv, tmp, &vdev->priv_tx, list) { + list_move_tail(&priv->list, &vdev->priv_rx); + spin_unlock(&vdev->priv_lock); + return priv; + } + + spin_unlock(&vdev->priv_lock); + + return NULL; +} + +static int vhci_send_cmd_submit(struct vhci_device *vdev) +{ + struct vhci_priv *priv = NULL; + + struct msghdr msg; + struct kvec iov[3]; + size_t txsize; + + size_t total_size = 0; + + while ((priv = dequeue_from_priv_tx(vdev)) != NULL) { + int ret; + struct urb *urb = priv->urb; + struct usbip_header pdu_header; + struct usbip_iso_packet_descriptor *iso_buffer = NULL; + + txsize = 0; + memset(&pdu_header, 0, sizeof(pdu_header)); + memset(&msg, 0, sizeof(msg)); + memset(&iov, 0, sizeof(iov)); + + usbip_dbg_vhci_tx("setup txdata urb %p\n", urb); + + /* 1. setup usbip_header */ + setup_cmd_submit_pdu(&pdu_header, urb); + usbip_header_correct_endian(&pdu_header, 1); + + iov[0].iov_base = &pdu_header; + iov[0].iov_len = sizeof(pdu_header); + txsize += sizeof(pdu_header); + + /* 2. setup transfer buffer */ + if (!usb_pipein(urb->pipe) && urb->transfer_buffer_length > 0) { + iov[1].iov_base = urb->transfer_buffer; + iov[1].iov_len = urb->transfer_buffer_length; + txsize += urb->transfer_buffer_length; + } + + /* 3. setup iso_packet_descriptor */ + if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { + ssize_t len = 0; + + iso_buffer = usbip_alloc_iso_desc_pdu(urb, &len); + if (!iso_buffer) { + usbip_event_add(&vdev->ud, + SDEV_EVENT_ERROR_MALLOC); + return -1; + } + + iov[2].iov_base = iso_buffer; + iov[2].iov_len = len; + txsize += len; + } + + ret = kernel_sendmsg(vdev->ud.tcp_socket, &msg, iov, 3, txsize); + if (ret != txsize) { + pr_err("sendmsg failed!, ret=%d for %zd\n", ret, + txsize); + kfree(iso_buffer); + usbip_event_add(&vdev->ud, VDEV_EVENT_ERROR_TCP); + return -1; + } + + kfree(iso_buffer); + usbip_dbg_vhci_tx("send txdata\n"); + + total_size += txsize; + } + + return total_size; +} + +static struct vhci_unlink *dequeue_from_unlink_tx(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_move_tail(&unlink->list, &vdev->unlink_rx); + spin_unlock(&vdev->priv_lock); + return unlink; + } + + spin_unlock(&vdev->priv_lock); + + return NULL; +} + +static int vhci_send_cmd_unlink(struct vhci_device *vdev) +{ + struct vhci_unlink *unlink = NULL; + + struct msghdr msg; + struct kvec iov[3]; + size_t txsize; + + size_t total_size = 0; + + while ((unlink = dequeue_from_unlink_tx(vdev)) != NULL) { + int ret; + struct usbip_header pdu_header; + + txsize = 0; + memset(&pdu_header, 0, sizeof(pdu_header)); + memset(&msg, 0, sizeof(msg)); + memset(&iov, 0, sizeof(iov)); + + usbip_dbg_vhci_tx("setup cmd unlink, %lu\n", unlink->seqnum); + + /* 1. setup usbip_header */ + pdu_header.base.command = USBIP_CMD_UNLINK; + pdu_header.base.seqnum = unlink->seqnum; + pdu_header.base.devid = vdev->devid; + pdu_header.base.ep = 0; + pdu_header.u.cmd_unlink.seqnum = unlink->unlink_seqnum; + + usbip_header_correct_endian(&pdu_header, 1); + + iov[0].iov_base = &pdu_header; + iov[0].iov_len = sizeof(pdu_header); + txsize += sizeof(pdu_header); + + ret = kernel_sendmsg(vdev->ud.tcp_socket, &msg, iov, 1, txsize); + if (ret != txsize) { + pr_err("sendmsg failed!, ret=%d for %zd\n", ret, + txsize); + usbip_event_add(&vdev->ud, VDEV_EVENT_ERROR_TCP); + return -1; + } + + usbip_dbg_vhci_tx("send txdata\n"); + + total_size += txsize; + } + + return total_size; +} + +int vhci_tx_loop(void *data) +{ + struct usbip_device *ud = data; + struct vhci_device *vdev = container_of(ud, struct vhci_device, ud); + + while (!kthread_should_stop()) { + if (vhci_send_cmd_submit(vdev) < 0) + break; + + if (vhci_send_cmd_unlink(vdev) < 0) + break; + + wait_event_interruptible(vdev->waitq_tx, + (!list_empty(&vdev->priv_tx) || + !list_empty(&vdev->unlink_tx) || + kthread_should_stop())); + + usbip_dbg_vhci_tx("pending urbs ?, now wake up\n"); + } + + return 0; +} diff --git a/drivers/usb/wusbcore/wa-xfer.c b/drivers/usb/wusbcore/wa-xfer.c index 3e2e4ed20157..e279015be466 100644 --- a/drivers/usb/wusbcore/wa-xfer.c +++ b/drivers/usb/wusbcore/wa-xfer.c @@ -2602,6 +2602,7 @@ static void wa_buf_in_cb(struct urb *urb) dev = &wa->usb_iface->dev; --(wa->active_buf_in_urbs); active_buf_in_urbs = wa->active_buf_in_urbs; + rpipe = xfer->ep->hcpriv; if (usb_pipeisoc(xfer->urb->pipe)) { struct usb_iso_packet_descriptor *iso_frame_desc = @@ -2659,7 +2660,6 @@ static void wa_buf_in_cb(struct urb *urb) resubmit_dti = (isoc_data_frame_count == urb_frame_count); } else if (active_buf_in_urbs == 0) { - rpipe = xfer->ep->hcpriv; dev_dbg(dev, "xfer %p 0x%08X#%u: data in done (%zu bytes)\n", xfer, wa_xfer_id(xfer), seg->index, @@ -2685,7 +2685,6 @@ static void wa_buf_in_cb(struct urb *urb) */ resubmit_dti = wa->dti_state != WA_DTI_TRANSFER_RESULT_PENDING; spin_lock_irqsave(&xfer->lock, flags); - rpipe = xfer->ep->hcpriv; if (printk_ratelimit()) dev_err(dev, "xfer %p 0x%08X#%u: data in error %d\n", xfer, wa_xfer_id(xfer), seg->index, |