diff options
Diffstat (limited to 'drivers/usb/usbip/vhci_hcd.c')
-rw-r--r-- | drivers/usb/usbip/vhci_hcd.c | 285 |
1 files changed, 191 insertions, 94 deletions
diff --git a/drivers/usb/usbip/vhci_hcd.c b/drivers/usb/usbip/vhci_hcd.c index 2e0450bec1b1..96f2dacb27fa 100644 --- a/drivers/usb/usbip/vhci_hcd.c +++ b/drivers/usb/usbip/vhci_hcd.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2003-2008 Takahiro Hirofuchi + * Copyright (C) 2015-2016 Nobuo Iwata * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -56,7 +57,9 @@ 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; +int vhci_num_controllers = VHCI_NR_HCS; + +struct platform_device **vhci_pdevs; static const char * const bit_desc[] = { "CONNECTION", /*0*/ @@ -119,47 +122,59 @@ static void dump_port_status_diff(u32 prev_status, u32 new_status) pr_debug("\n"); } -void rh_port_connect(int rhport, enum usb_device_speed speed) +void rh_port_connect(struct vhci_device *vdev, enum usb_device_speed speed) { + struct vhci_hcd *vhci = vdev_to_vhci(vdev); + int rhport = vdev->rhport; + u32 status; unsigned long flags; usbip_dbg_vhci_rh("rh_port_connect %d\n", rhport); - spin_lock_irqsave(&the_controller->lock, flags); + spin_lock_irqsave(&vhci->lock, flags); + + status = vhci->port_status[rhport]; - the_controller->port_status[rhport] |= USB_PORT_STAT_CONNECTION - | (1 << USB_PORT_FEAT_C_CONNECTION); + status |= 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; + status |= USB_PORT_STAT_HIGH_SPEED; break; case USB_SPEED_LOW: - the_controller->port_status[rhport] |= USB_PORT_STAT_LOW_SPEED; + status |= USB_PORT_STAT_LOW_SPEED; break; default: break; } - spin_unlock_irqrestore(&the_controller->lock, flags); + vhci->port_status[rhport] = status; + + spin_unlock_irqrestore(&vhci->lock, flags); - usb_hcd_poll_rh_status(vhci_to_hcd(the_controller)); + usb_hcd_poll_rh_status(vhci_to_hcd(vhci)); } -static void rh_port_disconnect(int rhport) +static void rh_port_disconnect(struct vhci_device *vdev) { + struct vhci_hcd *vhci = vdev_to_vhci(vdev); + int rhport = vdev->rhport; + u32 status; unsigned long flags; usbip_dbg_vhci_rh("rh_port_disconnect %d\n", rhport); - spin_lock_irqsave(&the_controller->lock, flags); + spin_lock_irqsave(&vhci->lock, flags); + + status = vhci->port_status[rhport]; + + status &= ~USB_PORT_STAT_CONNECTION; + status |= (1 << USB_PORT_FEAT_C_CONNECTION); - the_controller->port_status[rhport] &= ~USB_PORT_STAT_CONNECTION; - the_controller->port_status[rhport] |= - (1 << USB_PORT_FEAT_C_CONNECTION); + vhci->port_status[rhport] = status; - spin_unlock_irqrestore(&the_controller->lock, flags); - usb_hcd_poll_rh_status(vhci_to_hcd(the_controller)); + spin_unlock_irqrestore(&vhci->lock, flags); + usb_hcd_poll_rh_status(vhci_to_hcd(vhci)); } #define PORT_C_MASK \ @@ -188,7 +203,7 @@ static int vhci_hub_status(struct usb_hcd *hcd, char *buf) int changed = 0; unsigned long flags; - retval = DIV_ROUND_UP(VHCI_NPORTS + 1, 8); + retval = DIV_ROUND_UP(VHCI_HC_PORTS + 1, 8); memset(buf, 0, retval); vhci = hcd_to_vhci(hcd); @@ -200,7 +215,7 @@ static int vhci_hub_status(struct usb_hcd *hcd, char *buf) } /* check pseudo status register for each port */ - for (rhport = 0; rhport < VHCI_NPORTS; rhport++) { + for (rhport = 0; rhport < VHCI_HC_PORTS; 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); @@ -225,7 +240,7 @@ static inline void hub_descriptor(struct usb_hub_descriptor *desc) desc->bDescLength = 9; desc->wHubCharacteristics = cpu_to_le16( HUB_CHAR_INDV_PORT_LPSM | HUB_CHAR_COMMON_OCPM); - desc->bNbrPorts = VHCI_NPORTS; + desc->bNbrPorts = VHCI_HC_PORTS; desc->u.hs.DeviceRemovable[0] = 0xff; desc->u.hs.DeviceRemovable[1] = 0xff; } @@ -238,7 +253,7 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, int rhport; unsigned long flags; - u32 prev_port_status[VHCI_NPORTS]; + u32 prev_port_status[VHCI_HC_PORTS]; if (!HCD_HW_ACCESSIBLE(hcd)) return -ETIMEDOUT; @@ -249,7 +264,7 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, */ usbip_dbg_vhci_rh("typeReq %x wValue %x wIndex %x\n", typeReq, wValue, wIndex); - if (wIndex > VHCI_NPORTS) + if (wIndex > VHCI_HC_PORTS) pr_err("invalid port number %d\n", wIndex); rhport = ((__u8)(wIndex & 0x00ff)) - 1; @@ -315,7 +330,7 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, break; case GetPortStatus: usbip_dbg_vhci_rh(" GetPortStatus port %x\n", wIndex); - if (wIndex > VHCI_NPORTS || wIndex < 1) { + if (wIndex > VHCI_HC_PORTS || wIndex < 1) { pr_err("invalid port number %d\n", wIndex); retval = -EPIPE; } @@ -416,14 +431,27 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, static struct vhci_device *get_vdev(struct usb_device *udev) { - int i; + struct platform_device *pdev; + struct usb_hcd *hcd; + struct vhci_hcd *vhci; + int pdev_nr, rhport; if (!udev) return NULL; - for (i = 0; i < VHCI_NPORTS; i++) - if (the_controller->vdev[i].udev == udev) - return port_to_vdev(i); + for (pdev_nr = 0; pdev_nr < vhci_num_controllers; pdev_nr++) { + pdev = *(vhci_pdevs + pdev_nr); + if (pdev == NULL) + continue; + hcd = platform_get_drvdata(pdev); + if (hcd == NULL) + continue; + vhci = hcd_to_vhci(hcd); + for (rhport = 0; rhport < VHCI_HC_PORTS; rhport++) { + if (vhci->vdev[rhport].udev == udev) + return &vhci->vdev[rhport]; + } + } return NULL; } @@ -432,6 +460,7 @@ static void vhci_tx_urb(struct urb *urb) { struct vhci_device *vdev = get_vdev(urb->dev); struct vhci_priv *priv; + struct vhci_hcd *vhci = vdev_to_vhci(vdev); unsigned long flags; if (!vdev) { @@ -447,7 +476,7 @@ static void vhci_tx_urb(struct urb *urb) spin_lock_irqsave(&vdev->priv_lock, flags); - priv->seqnum = atomic_inc_return(&the_controller->seqnum); + priv->seqnum = atomic_inc_return(&vhci->seqnum); if (priv->seqnum == 0xffff) dev_info(&urb->dev->dev, "seqnum max\n"); @@ -465,7 +494,9 @@ static void vhci_tx_urb(struct urb *urb) static int vhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags) { + struct vhci_hcd *vhci = hcd_to_vhci(hcd); struct device *dev = &urb->dev->dev; + u8 portnum = urb->dev->portnum; int ret = 0; struct vhci_device *vdev; unsigned long flags; @@ -473,26 +504,30 @@ static int vhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, usbip_dbg_vhci_hc("enter, usb_hcd %p urb %p mem_flags %d\n", hcd, urb, mem_flags); + if (portnum > VHCI_HC_PORTS) { + pr_err("invalid port number %d\n", portnum); + return -ENODEV; + } + vdev = &vhci->vdev[portnum-1]; + /* patch to usb_sg_init() is in 2.5.60 */ BUG_ON(!urb->transfer_buffer && urb->transfer_buffer_length); - spin_lock_irqsave(&the_controller->lock, flags); + spin_lock_irqsave(&vhci->lock, flags); if (urb->status != -EINPROGRESS) { dev_err(dev, "URB already unlinked!, status %d\n", urb->status); - spin_unlock_irqrestore(&the_controller->lock, flags); + spin_unlock_irqrestore(&vhci->lock, flags); 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_irqrestore(&the_controller->lock, flags); + spin_unlock_irqrestore(&vhci->lock, flags); return -ENODEV; } spin_unlock(&vdev->ud.lock); @@ -565,17 +600,16 @@ static int vhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, out: vhci_tx_urb(urb); - spin_unlock_irqrestore(&the_controller->lock, flags); + spin_unlock_irqrestore(&vhci->lock, flags); return 0; no_need_xmit: usb_hcd_unlink_urb_from_ep(hcd, urb); no_need_unlink: - spin_unlock_irqrestore(&the_controller->lock, flags); + spin_unlock_irqrestore(&vhci->lock, flags); if (!ret) - usb_hcd_giveback_urb(vhci_to_hcd(the_controller), - urb, urb->status); + usb_hcd_giveback_urb(hcd, urb, urb->status); return ret; } @@ -627,19 +661,20 @@ no_need_unlink: */ static int vhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) { + struct vhci_hcd *vhci = hcd_to_vhci(hcd); struct vhci_priv *priv; struct vhci_device *vdev; unsigned long flags; pr_info("dequeue a urb %p\n", urb); - spin_lock_irqsave(&the_controller->lock, flags); + spin_lock_irqsave(&vhci->lock, flags); priv = urb->hcpriv; if (!priv) { /* URB was never linked! or will be soon given back by * vhci_rx. */ - spin_unlock_irqrestore(&the_controller->lock, flags); + spin_unlock_irqrestore(&vhci->lock, flags); return -EIDRM; } @@ -648,7 +683,7 @@ static int vhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) ret = usb_hcd_check_unlink_urb(hcd, urb, status); if (ret) { - spin_unlock_irqrestore(&the_controller->lock, flags); + spin_unlock_irqrestore(&vhci->lock, flags); return ret; } } @@ -676,10 +711,9 @@ static int vhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) usb_hcd_unlink_urb_from_ep(hcd, urb); - spin_unlock_irqrestore(&the_controller->lock, flags); - usb_hcd_giveback_urb(vhci_to_hcd(the_controller), urb, - urb->status); - spin_lock_irqsave(&the_controller->lock, flags); + spin_unlock_irqrestore(&vhci->lock, flags); + usb_hcd_giveback_urb(vhci_to_hcd(vhci), urb, urb->status); + spin_lock_irqsave(&vhci->lock, flags); } else { /* tcp connection is alive */ @@ -691,12 +725,12 @@ static int vhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) unlink = kzalloc(sizeof(struct vhci_unlink), GFP_ATOMIC); if (!unlink) { spin_unlock(&vdev->priv_lock); - spin_unlock_irqrestore(&the_controller->lock, flags); + spin_unlock_irqrestore(&vhci->lock, flags); usbip_event_add(&vdev->ud, VDEV_EVENT_ERROR_MALLOC); return -ENOMEM; } - unlink->seqnum = atomic_inc_return(&the_controller->seqnum); + unlink->seqnum = atomic_inc_return(&vhci->seqnum); if (unlink->seqnum == 0xffff) pr_info("seqnum max\n"); @@ -712,7 +746,7 @@ static int vhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) spin_unlock(&vdev->priv_lock); } - spin_unlock_irqrestore(&the_controller->lock, flags); + spin_unlock_irqrestore(&vhci->lock, flags); usbip_dbg_vhci_hc("leave\n"); return 0; @@ -720,10 +754,12 @@ static int vhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) static void vhci_device_unlink_cleanup(struct vhci_device *vdev) { + struct vhci_hcd *vhci = vdev_to_vhci(vdev); + struct usb_hcd *hcd = vhci_to_hcd(vhci); struct vhci_unlink *unlink, *tmp; unsigned long flags; - spin_lock_irqsave(&the_controller->lock, flags); + spin_lock_irqsave(&vhci->lock, flags); spin_lock(&vdev->priv_lock); list_for_each_entry_safe(unlink, tmp, &vdev->unlink_tx, list) { @@ -752,24 +788,23 @@ static void vhci_device_unlink_cleanup(struct vhci_device *vdev) urb->status = -ENODEV; - usb_hcd_unlink_urb_from_ep(vhci_to_hcd(the_controller), urb); + usb_hcd_unlink_urb_from_ep(hcd, urb); list_del(&unlink->list); spin_unlock(&vdev->priv_lock); - spin_unlock_irqrestore(&the_controller->lock, flags); + spin_unlock_irqrestore(&vhci->lock, flags); - usb_hcd_giveback_urb(vhci_to_hcd(the_controller), urb, - urb->status); + usb_hcd_giveback_urb(hcd, urb, urb->status); - spin_lock_irqsave(&the_controller->lock, flags); + spin_lock_irqsave(&vhci->lock, flags); spin_lock(&vdev->priv_lock); kfree(unlink); } spin_unlock(&vdev->priv_lock); - spin_unlock_irqrestore(&the_controller->lock, flags); + spin_unlock_irqrestore(&vhci->lock, flags); } /* @@ -827,7 +862,7 @@ static void vhci_shutdown_connection(struct usbip_device *ud) * is actually given back by vhci_rx after receiving its return pdu. * */ - rh_port_disconnect(vdev->rhport); + rh_port_disconnect(vdev); pr_info("disconnect device\n"); } @@ -866,7 +901,7 @@ static void vhci_device_unusable(struct usbip_device *ud) static void vhci_device_init(struct vhci_device *vdev) { - memset(vdev, 0, sizeof(*vdev)); + memset(vdev, 0, sizeof(struct vhci_device)); vdev->ud.side = USBIP_VHCI; vdev->ud.status = VDEV_ST_NULL; @@ -887,17 +922,34 @@ static void vhci_device_init(struct vhci_device *vdev) usbip_start_eh(&vdev->ud); } +static int hcd_name_to_id(const char *name) +{ + char *c; + long val; + int ret; + + c = strchr(name, '.'); + if (c == NULL) + return 0; + + ret = kstrtol(c+1, 10, &val); + if (ret < 0) + return ret; + + return val; +} + static int vhci_start(struct usb_hcd *hcd) { struct vhci_hcd *vhci = hcd_to_vhci(hcd); - int rhport; + int id, 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++) { + for (rhport = 0; rhport < VHCI_HC_PORTS; rhport++) { struct vhci_device *vdev = &vhci->vdev[rhport]; vhci_device_init(vdev); @@ -910,11 +962,26 @@ static int vhci_start(struct usb_hcd *hcd) hcd->power_budget = 0; /* no limit */ hcd->uses_new_polling = 1; + id = hcd_name_to_id(hcd_name(hcd)); + if (id < 0) { + pr_err("invalid vhci name %s\n", hcd_name(hcd)); + return -EINVAL; + } + /* 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; + if (id == 0) { + err = vhci_init_attr_group(); + if (err) { + pr_err("init attr group\n"); + return err; + } + err = sysfs_create_group(&hcd_dev(hcd)->kobj, &vhci_attr_group); + if (err) { + pr_err("create sysfs files\n"); + vhci_finish_attr_group(); + return err; + } + pr_info("created sysfs %s\n", hcd_name(hcd)); } return 0; @@ -923,15 +990,19 @@ static int vhci_start(struct usb_hcd *hcd) static void vhci_stop(struct usb_hcd *hcd) { struct vhci_hcd *vhci = hcd_to_vhci(hcd); - int rhport = 0; + int id, rhport; 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); + id = hcd_name_to_id(hcd_name(hcd)); + if (id == 0) { + sysfs_remove_group(&hcd_dev(hcd)->kobj, &vhci_attr_group); + vhci_finish_attr_group(); + } /* 2. shutdown all the ports of vhci_hcd */ - for (rhport = 0; rhport < VHCI_NPORTS; rhport++) { + for (rhport = 0; rhport < VHCI_HC_PORTS; rhport++) { struct vhci_device *vdev = &vhci->vdev[rhport]; usbip_event_add(&vdev->ud, VDEV_EVENT_REMOVED); @@ -1025,9 +1096,6 @@ static int vhci_hcd_probe(struct platform_device *pdev) } 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. @@ -1036,7 +1104,6 @@ static int vhci_hcd_probe(struct platform_device *pdev) if (ret != 0) { pr_err("usb_add_hcd failed %d\n", ret); usb_put_hcd(hcd); - the_controller = NULL; return ret; } @@ -1059,7 +1126,6 @@ static int vhci_hcd_remove(struct platform_device *pdev) */ usb_remove_hcd(hcd); usb_put_hcd(hcd); - the_controller = NULL; return 0; } @@ -1070,21 +1136,24 @@ static int vhci_hcd_remove(struct platform_device *pdev) static int vhci_hcd_suspend(struct platform_device *pdev, pm_message_t state) { struct usb_hcd *hcd; - int rhport = 0; + struct vhci_hcd *vhci; + int rhport; int connected = 0; int ret = 0; unsigned long flags; hcd = platform_get_drvdata(pdev); + if (!hcd) + return 0; + vhci = hcd_to_vhci(hcd); - spin_lock_irqsave(&the_controller->lock, flags); + spin_lock_irqsave(&vhci->lock, flags); - for (rhport = 0; rhport < VHCI_NPORTS; rhport++) - if (the_controller->port_status[rhport] & - USB_PORT_STAT_CONNECTION) + for (rhport = 0; rhport < VHCI_HC_PORTS; rhport++) + if (vhci->port_status[rhport] & USB_PORT_STAT_CONNECTION) connected += 1; - spin_unlock_irqrestore(&the_controller->lock, flags); + spin_unlock_irqrestore(&vhci->lock, flags); if (connected > 0) { dev_info(&pdev->dev, @@ -1106,6 +1175,8 @@ static int vhci_hcd_resume(struct platform_device *pdev) dev_dbg(&pdev->dev, "%s\n", __func__); hcd = platform_get_drvdata(pdev); + if (!hcd) + return 0; set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); usb_hcd_poll_rh_status(hcd); @@ -1129,52 +1200,78 @@ static struct platform_driver vhci_driver = { }, }; -/* - * 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 int add_platform_device(int id) { + struct platform_device *pdev; + int dev_nr; + + if (id == 0) + dev_nr = -1; + else + dev_nr = id; + + pdev = platform_device_register_simple(driver_name, dev_nr, NULL, 0); + if (pdev == NULL) + return -ENODEV; + + *(vhci_pdevs + id) = pdev; + return 0; } -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 void del_platform_devices(void) +{ + struct platform_device *pdev; + int i; + + for (i = 0; i < vhci_num_controllers; i++) { + pdev = *(vhci_pdevs + i); + if (pdev != NULL) + platform_device_unregister(pdev); + *(vhci_pdevs + i) = NULL; + } + sysfs_remove_link(&platform_bus.kobj, driver_name); +} static int __init vhci_hcd_init(void) { - int ret; + int i, ret; if (usb_disabled()) return -ENODEV; + if (vhci_num_controllers < 1) + vhci_num_controllers = 1; + + vhci_pdevs = kcalloc(vhci_num_controllers, sizeof(void *), GFP_KERNEL); + if (vhci_pdevs == NULL) + return -ENOMEM; + 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; + for (i = 0; i < vhci_num_controllers; i++) { + ret = add_platform_device(i); + if (ret) + goto err_platform_device_register; + } pr_info(DRIVER_DESC " v" USBIP_VERSION "\n"); return ret; err_platform_device_register: + del_platform_devices(); platform_driver_unregister(&vhci_driver); err_driver_register: + kfree(vhci_pdevs); return ret; } static void __exit vhci_hcd_exit(void) { - platform_device_unregister(&the_pdev); + del_platform_devices(); platform_driver_unregister(&vhci_driver); + kfree(vhci_pdevs); } module_init(vhci_hcd_init); |