|
@@ -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);
|