[RFC v2] USB 3.0 Hub Changes

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hub descriptor structure now has a union field to account for
different superspeed fields.

Signed-off-by: John Youn <johnyoun@xxxxxxxxxxxx>
---

This might not be the best solution.  I had to touch a whole bunch of
hcd's that initialize the DeviceRemovable field for their root hub
descriptor.  Using a cast might be cleaner for existing code.

Tested with a several different hubs superspeed and HS.



 drivers/usb/core/hub.c          |   71 ++++++++++++++++++++++++++++++++++----
 drivers/usb/host/ehci-hub.c     |    4 +-
 drivers/usb/host/imx21-hcd.c    |    4 +-
 drivers/usb/host/isp116x-hcd.c  |    4 +-
 drivers/usb/host/isp1362-hcd.c  |    4 +-
 drivers/usb/host/isp1760-hcd.c  |    4 +-
 drivers/usb/host/ohci-hub.c     |   10 +++---
 drivers/usb/host/oxu210hp-hcd.c |    4 +-
 drivers/usb/host/r8a66597-hcd.c |    4 +-
 drivers/usb/host/sl811-hcd.c    |    4 +-
 drivers/usb/host/u132-hcd.c     |   10 +++---
 drivers/usb/host/xhci-hub.c     |    4 +-
 drivers/usb/wusbcore/rh.c       |    4 +-
 include/linux/usb/ch11.h        |   50 +++++++++++++++++++++++++--
 14 files changed, 139 insertions(+), 42 deletions(-)

diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 70cccc7..f79a0f6 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -82,6 +82,10 @@ struct usb_hub {
 	void			**port_owners;
 };
 
+static inline int hub_is_superspeed(struct usb_device *hdev)
+{
+	return (hdev->descriptor.bDeviceProtocol == 3);
+}
 
 /* Protect struct usb_device->state and ->children members
  * Note: Both are also protected by ->dev.sem, except that ->state can
@@ -172,14 +176,24 @@ static struct usb_hub *hdev_to_hub(struct usb_device *hdev)
 }
 
 /* USB 2.0 spec Section 11.24.4.5 */
-static int get_hub_descriptor(struct usb_device *hdev, void *data, int size)
+static int get_hub_descriptor(struct usb_device *hdev, void *data)
 {
-	int i, ret;
+	int i, ret, size;
+	unsigned dtype;
+
+	if (hub_is_superspeed(hdev)) {
+		dtype = USB_DT_SS_HUB;
+		size = USB_DT_SS_HUB_SIZE;
+	}
+	else {
+		dtype = USB_DT_HUB;
+		size = sizeof(struct usb_hub_descriptor);
+	}
 
 	for (i = 0; i < 3; i++) {
 		ret = usb_control_msg(hdev, usb_rcvctrlpipe(hdev, 0),
 			USB_REQ_GET_DESCRIPTOR, USB_DIR_IN | USB_RT_HUB,
-			USB_DT_HUB << 8, 0, data, size,
+			dtype << 8, 0, data, size,
 			USB_CTRL_GET_TIMEOUT);
 		if (ret >= (USB_DT_HUB_NONVAR_SIZE + 2))
 			return ret;
@@ -365,6 +379,17 @@ static int hub_port_status(struct usb_hub *hub, int port1,
 	} else {
 		*status = le16_to_cpu(hub->status->port.wPortStatus);
 		*change = le16_to_cpu(hub->status->port.wPortChange);
+
+		if ((hub->hdev->parent != NULL) && hub_is_superspeed(hub->hdev)) {
+			/* Translate the USB 3 port status */
+			u16 tmp = *status & USB3_PORT_STAT_MASK;
+			if (*status & USB3_PORT_STAT_POWER)
+				tmp |= USB_PORT_STAT_POWER;
+			if ((*status & USB3_PORT_STAT_SPEED) == USB3_PORT_STAT_SPEED_SUPER)
+				tmp |= USB_PORT_STAT_SUPER_SPEED;
+			*status = tmp;
+		}
+
 		ret = 0;
 	}
 	mutex_unlock(&hub->status_mutex);
@@ -607,7 +632,7 @@ static int hub_port_disable(struct usb_hub *hub, int port1, int set_state)
 	if (hdev->children[port1-1] && set_state)
 		usb_set_device_state(hdev->children[port1-1],
 				USB_STATE_NOTATTACHED);
-	if (!hub->error)
+	if (!hub->error && !hub_is_superspeed(hub->hdev))
 		ret = clear_port_feature(hdev, port1, USB_PORT_FEAT_ENABLE);
 	if (ret)
 		dev_err(hub->intfdev, "cannot disable port %d (err = %d)\n",
@@ -771,6 +796,11 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
 			clear_port_feature(hub->hdev, port1,
 					USB_PORT_FEAT_C_ENABLE);
 		}
+		if (portchange & USB_PORT_STAT_C_LINK_STATE) {
+			need_debounce_delay = true;
+			clear_port_feature(hub->hdev, port1,
+					USB_PORT_FEAT_C_PORT_LINK_STATE);
+		}
 
 		/* We can forget about a "removed" device when there's a
 		 * physical disconnect or the connect status changes.
@@ -940,12 +970,23 @@ static int hub_configure(struct usb_hub *hub,
 		goto fail;
 	}
 
+	if (hub_is_superspeed(hdev) && (hdev->parent != NULL)) {
+		ret = usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0),
+				HUB_SET_DEPTH, USB_RT_HUB,
+				hdev->level - 1, 0, NULL, 0,
+				USB_CTRL_SET_TIMEOUT);
+
+		if (ret < 0) {
+			message = "can't set hub depth";
+			goto fail;
+		}
+	}
+
 	/* Request the entire hub descriptor.
 	 * hub->descriptor can handle USB_MAXCHILDREN ports,
 	 * but the hub can/will return fewer bytes here.
 	 */
-	ret = get_hub_descriptor(hdev, hub->descriptor,
-			sizeof(*hub->descriptor));
+	ret = get_hub_descriptor(hdev, hub->descriptor);
 	if (ret < 0) {
 		message = "can't read hub descriptor";
 		goto fail;
@@ -967,12 +1008,13 @@ static int hub_configure(struct usb_hub *hub,
 
 	wHubCharacteristics = le16_to_cpu(hub->descriptor->wHubCharacteristics);
 
-	if (wHubCharacteristics & HUB_CHAR_COMPOUND) {
+	/* FIXME for USB 3.0, skip for now */
+	if ((wHubCharacteristics & HUB_CHAR_COMPOUND) && !(hub_is_superspeed(hdev))) {
 		int	i;
 		char	portstr [USB_MAXCHILDREN + 1];
 
 		for (i = 0; i < hdev->maxchild; i++)
-			portstr[i] = hub->descriptor->DeviceRemovable
+			portstr[i] = hub->descriptor->u.hs.DeviceRemovable
 				    [((i + 1) / 8)] & (1 << ((i + 1) % 8))
 				? 'F' : 'R';
 		portstr[hdev->maxchild] = 0;
@@ -1988,6 +2030,8 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1,
 				udev->speed = USB_SPEED_HIGH;
 			else if (portstatus & USB_PORT_STAT_LOW_SPEED)
 				udev->speed = USB_SPEED_LOW;
+			else if (portstatus & USB_PORT_STAT_SUPER_SPEED)
+				udev->speed = USB_SPEED_SUPER;
 			else
 				udev->speed = USB_SPEED_FULL;
 			return 0;
@@ -3380,6 +3424,17 @@ static void hub_events(void)
 				clear_port_feature(hdev, i,
 					USB_PORT_FEAT_C_RESET);
 			}
+			if (portchange & USB_PORT_STAT_C_LINK_STATE) {
+				clear_port_feature(hub->hdev, i,
+						USB_PORT_FEAT_C_PORT_LINK_STATE);
+			}
+			if (portchange & USB_PORT_STAT_C_CONFIG_ERROR) {
+				dev_warn (hub_dev,
+					"config error on port %d\n",
+					i);
+				clear_port_feature(hub->hdev, i,
+						USB_PORT_FEAT_C_PORT_CONFIG_ERROR);
+			}
 
 			if (connect_change)
 				hub_port_connect_change(hub, i,
diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c
index e7d3d8d..57ba6f5 100644
--- a/drivers/usb/host/ehci-hub.c
+++ b/drivers/usb/host/ehci-hub.c
@@ -676,8 +676,8 @@ ehci_hub_descriptor (
 	desc->bDescLength = 7 + 2 * temp;
 
 	/* two bitmaps:  ports removable, and usb 1.0 legacy PortPwrCtrlMask */
-	memset (&desc->bitmap [0], 0, temp);
-	memset (&desc->bitmap [temp], 0xff, temp);
+	memset (&desc->u.hs.bitmap [0], 0, temp);
+	memset (&desc->u.hs.bitmap [temp], 0xff, temp);
 
 	temp = 0x0008;			/* per-port overcurrent reporting */
 	if (HCS_PPC (ehci->hcs_params))
diff --git a/drivers/usb/host/imx21-hcd.c b/drivers/usb/host/imx21-hcd.c
index ca0e98d..873e4e7 100644
--- a/drivers/usb/host/imx21-hcd.c
+++ b/drivers/usb/host/imx21-hcd.c
@@ -1320,8 +1320,8 @@ static int get_hub_descriptor(struct usb_hcd *hcd,
 		0x0010 |	/* No over current protection */
 		0);
 
-	desc->bitmap[0] = 1 << 1;
-	desc->bitmap[1] = ~0;
+	desc->u.hs.bitmap[0] = 1 << 1;
+	desc->u.hs.bitmap[1] = ~0;
 	return 0;
 }
 
diff --git a/drivers/usb/host/isp116x-hcd.c b/drivers/usb/host/isp116x-hcd.c
index d9e8212..b3d5a41 100644
--- a/drivers/usb/host/isp116x-hcd.c
+++ b/drivers/usb/host/isp116x-hcd.c
@@ -952,8 +952,8 @@ static void isp116x_hub_descriptor(struct isp116x *isp116x,
 	desc->wHubCharacteristics = cpu_to_le16((u16) ((reg >> 8) & 0x1f));
 	desc->bPwrOn2PwrGood = (u8) ((reg >> 24) & 0xff);
 	/* two bitmaps:  ports removable, and legacy PortPwrCtrlMask */
-	desc->bitmap[0] = 0;
-	desc->bitmap[1] = ~0;
+	desc->u.hs.bitmap[0] = 0;
+	desc->u.hs.bitmap[1] = ~0;
 }
 
 /* Perform reset of a given port.
diff --git a/drivers/usb/host/isp1362-hcd.c b/drivers/usb/host/isp1362-hcd.c
index 0587ad4..3103368 100644
--- a/drivers/usb/host/isp1362-hcd.c
+++ b/drivers/usb/host/isp1362-hcd.c
@@ -1557,8 +1557,8 @@ static void isp1362_hub_descriptor(struct isp1362_hcd *isp1362_hcd,
 	DBG(0, "%s: hubcharacteristics = %02x\n", __func__, cpu_to_le16((reg >> 8) & 0x1f));
 	desc->bPwrOn2PwrGood = (reg >> 24) & 0xff;
 	/* two bitmaps:  ports removable, and legacy PortPwrCtrlMask */
-	desc->bitmap[0] = desc->bNbrPorts == 1 ? 1 << 1 : 3 << 1;
-	desc->bitmap[1] = ~0;
+	desc->u.hs.bitmap[0] = desc->bNbrPorts == 1 ? 1 << 1 : 3 << 1;
+	desc->u.hs.bitmap[1] = ~0;
 
 	DBG(3, "%s: exit\n", __func__);
 }
diff --git a/drivers/usb/host/isp1760-hcd.c b/drivers/usb/host/isp1760-hcd.c
index dbcafa2..1a05f7a 100644
--- a/drivers/usb/host/isp1760-hcd.c
+++ b/drivers/usb/host/isp1760-hcd.c
@@ -1844,8 +1844,8 @@ static void isp1760_hub_descriptor(struct isp1760_hcd *priv,
 	desc->bDescLength = 7 + 2 * temp;
 
 	/* two bitmaps:  ports removable, and usb 1.0 legacy PortPwrCtrlMask */
-	memset(&desc->bitmap[0], 0, temp);
-	memset(&desc->bitmap[temp], 0xff, temp);
+	memset(&desc->u.hs.bitmap[0], 0, temp);
+	memset(&desc->u.hs.bitmap[temp], 0xff, temp);
 
 	/* per-port overcurrent reporting */
 	temp = 0x0008;
diff --git a/drivers/usb/host/ohci-hub.c b/drivers/usb/host/ohci-hub.c
index 65cac8c..7883cbb 100644
--- a/drivers/usb/host/ohci-hub.c
+++ b/drivers/usb/host/ohci-hub.c
@@ -573,13 +573,13 @@ ohci_hub_descriptor (
 
 	/* two bitmaps:  ports removable, and usb 1.0 legacy PortPwrCtrlMask */
 	rh = roothub_b (ohci);
-	memset(desc->bitmap, 0xff, sizeof(desc->bitmap));
-	desc->bitmap [0] = rh & RH_B_DR;
+	memset(desc->u.hs.bitmap, 0xff, sizeof(desc->u.hs.bitmap));
+	desc->u.hs.bitmap [0] = rh & RH_B_DR;
 	if (ohci->num_ports > 7) {
-		desc->bitmap [1] = (rh & RH_B_DR) >> 8;
-		desc->bitmap [2] = 0xff;
+		desc->u.hs.bitmap [1] = (rh & RH_B_DR) >> 8;
+		desc->u.hs.bitmap [2] = 0xff;
 	} else
-		desc->bitmap [1] = 0xff;
+		desc->u.hs.bitmap [1] = 0xff;
 }
 
 /*-------------------------------------------------------------------------*/
diff --git a/drivers/usb/host/oxu210hp-hcd.c b/drivers/usb/host/oxu210hp-hcd.c
index f608dfd..e190a55 100644
--- a/drivers/usb/host/oxu210hp-hcd.c
+++ b/drivers/usb/host/oxu210hp-hcd.c
@@ -452,8 +452,8 @@ static void ehci_hub_descriptor(struct oxu_hcd *oxu,
 	desc->bDescLength = 7 + 2 * temp;
 
 	/* two bitmaps:  ports removable, and usb 1.0 legacy PortPwrCtrlMask */
-	memset(&desc->bitmap[0], 0, temp);
-	memset(&desc->bitmap[temp], 0xff, temp);
+	memset(&desc->u.hs.bitmap[0], 0, temp);
+	memset(&desc->u.hs.bitmap[temp], 0xff, temp);
 
 	temp = 0x0008;			/* per-port overcurrent reporting */
 	if (HCS_PPC(oxu->hcs_params))
diff --git a/drivers/usb/host/r8a66597-hcd.c b/drivers/usb/host/r8a66597-hcd.c
index 77be3c2..ae66928 100644
--- a/drivers/usb/host/r8a66597-hcd.c
+++ b/drivers/usb/host/r8a66597-hcd.c
@@ -2150,8 +2150,8 @@ static void r8a66597_hub_descriptor(struct r8a66597 *r8a66597,
 	desc->bDescLength = 9;
 	desc->bPwrOn2PwrGood = 0;
 	desc->wHubCharacteristics = cpu_to_le16(0x0011);
-	desc->bitmap[0] = ((1 << r8a66597->max_root_hub) - 1) << 1;
-	desc->bitmap[1] = ~0;
+	desc->u.hs.bitmap[0] = ((1 << r8a66597->max_root_hub) - 1) << 1;
+	desc->u.hs.bitmap[1] = ~0;
 }
 
 static int r8a66597_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
diff --git a/drivers/usb/host/sl811-hcd.c b/drivers/usb/host/sl811-hcd.c
index bcf9f0e..fa811fa 100644
--- a/drivers/usb/host/sl811-hcd.c
+++ b/drivers/usb/host/sl811-hcd.c
@@ -1108,8 +1108,8 @@ sl811h_hub_descriptor (
 	desc->wHubCharacteristics = cpu_to_le16(temp);
 
 	/* two bitmaps:  ports removable, and legacy PortPwrCtrlMask */
-	desc->bitmap[0] = 0 << 1;
-	desc->bitmap[1] = ~0;
+	desc->u.hs.bitmap[0] = 0 << 1;
+	desc->u.hs.bitmap[1] = ~0;
 }
 
 static void
diff --git a/drivers/usb/host/u132-hcd.c b/drivers/usb/host/u132-hcd.c
index 5b31bae..dab178f 100644
--- a/drivers/usb/host/u132-hcd.c
+++ b/drivers/usb/host/u132-hcd.c
@@ -2607,13 +2607,13 @@ static int u132_roothub_descriptor(struct u132 *u132,
 	retval = u132_read_pcimem(u132, roothub.b, &rh_b);
 	if (retval)
 		return retval;
-	memset(desc->bitmap, 0xff, sizeof(desc->bitmap));
-	desc->bitmap[0] = rh_b & RH_B_DR;
+	memset(desc->u.hs.bitmap, 0xff, sizeof(desc->u.hs.bitmap));
+	desc->u.hs.bitmap[0] = rh_b & RH_B_DR;
 	if (u132->num_ports > 7) {
-		desc->bitmap[1] = (rh_b & RH_B_DR) >> 8;
-		desc->bitmap[2] = 0xff;
+		desc->u.hs.bitmap[1] = (rh_b & RH_B_DR) >> 8;
+		desc->u.hs.bitmap[2] = 0xff;
 	} else
-		desc->bitmap[1] = 0xff;
+		desc->u.hs.bitmap[1] = 0xff;
 	return 0;
 }
 
diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c
index a1a7a97..e230b7a 100644
--- a/drivers/usb/host/xhci-hub.c
+++ b/drivers/usb/host/xhci-hub.c
@@ -42,8 +42,8 @@ static void xhci_hub_descriptor(struct xhci_hcd *xhci,
 	desc->bDescLength = 7 + 2 * temp;
 
 	/* Why does core/hcd.h define bitmap?  It's just confusing. */
-	memset(&desc->DeviceRemovable[0], 0, temp);
-	memset(&desc->DeviceRemovable[temp], 0xff, temp);
+	memset(&desc->u.hs.DeviceRemovable[0], 0, temp);
+	memset(&desc->u.hs.DeviceRemovable[temp], 0xff, temp);
 
 	/* Ugh, these should be #defines, FIXME */
 	/* Using table 11-13 in USB 2.0 spec. */
diff --git a/drivers/usb/wusbcore/rh.c b/drivers/usb/wusbcore/rh.c
index a68ad7a..248017a 100644
--- a/drivers/usb/wusbcore/rh.c
+++ b/drivers/usb/wusbcore/rh.c
@@ -184,8 +184,8 @@ static int wusbhc_rh_get_hub_descr(struct wusbhc *wusbhc, u16 wValue,
 	descr->bPwrOn2PwrGood = 0;
 	descr->bHubContrCurrent = 0;
 	/* two bitmaps:  ports removable, and usb 1.0 legacy PortPwrCtrlMask */
-	memset(&descr->bitmap[0], 0, temp);
-	memset(&descr->bitmap[temp], 0xff, temp);
+	memset(&descr->u.hs.bitmap[0], 0, temp);
+	memset(&descr->u.hs.bitmap[temp], 0xff, temp);
 	return 0;
 }
 
diff --git a/include/linux/usb/ch11.h b/include/linux/usb/ch11.h
index 119194c..f145b40 100644
--- a/include/linux/usb/ch11.h
+++ b/include/linux/usb/ch11.h
@@ -26,6 +26,7 @@
 #define HUB_RESET_TT		9
 #define HUB_GET_TT_STATE	10
 #define HUB_STOP_TT		11
+#define HUB_SET_DEPTH		12
 
 /*
  * Hub Class feature numbers
@@ -54,6 +55,12 @@
 #define USB_PORT_FEAT_TEST              21
 #define USB_PORT_FEAT_INDICATOR         22
 #define USB_PORT_FEAT_C_PORT_L1         23
+#define USB_PORT_FEAT_C_PORT_LINK_STATE	25
+#define USB_PORT_FEAT_C_PORT_CONFIG_ERROR 26
+#define USB_PORT_FEAT_PORT_REMOTE_WAKE_MASK 27
+#define USB_PORT_FEAT_BH_PORT_RESET     28
+#define USB_PORT_FEAT_C_BH_PORT_RESET   29
+#define USB_PORT_FEAT_FORCE_LINKPM_ACCEPT 30
 
 /*
  * Hub Status and Hub Change results
@@ -84,6 +91,21 @@ struct usb_port_status {
 #define USB_PORT_STAT_SUPER_SPEED	0x8000	/* Linux-internal */
 
 /*
+ * USB 3.0 wPortStatus bit fields
+ * See USB 3.0 spec Table 10-10
+ */
+/* Bits that are the same from USB 2.0 */
+#define USB3_PORT_STAT_MASK (USB_PORT_STAT_CONNECTION |	    \
+				USB_PORT_STAT_ENABLE |	    \
+				USB_PORT_STAT_OVERCURRENT | \
+				USB_PORT_STAT_RESET)
+/* 3.0 specific bits */
+#define USB3_PORT_STAT_LINK_STATE	0x01e0
+#define USB3_PORT_STAT_POWER		0x0200
+#define USB3_PORT_STAT_SPEED		0x1c00
+#define USB3_PORT_STAT_SPEED_SUPER	0x0
+
+/*
  * wPortChange bit field
  * See USB 2.0 spec Table 11-22
  * Bits 0 to 4 shown, bits 5 to 15 are reserved
@@ -94,6 +116,13 @@ struct usb_port_status {
 #define USB_PORT_STAT_C_OVERCURRENT	0x0008
 #define USB_PORT_STAT_C_RESET		0x0010
 #define USB_PORT_STAT_C_L1		0x0020
+/*
+ * USB 3.0 wPortChange bit fields
+ * See USB 3.0 spec Table 10-11
+ */
+#define USB_PORT_STAT_C_BH_RESET	0x0020
+#define USB_PORT_STAT_C_LINK_STATE	0x0040
+#define USB_PORT_STAT_C_CONFIG_ERROR	0x0080
 
 /*
  * wHubCharacteristics (masks)
@@ -128,7 +157,9 @@ struct usb_hub_status {
  */
 
 #define USB_DT_HUB			(USB_TYPE_CLASS | 0x09)
+#define USB_DT_SS_HUB			(USB_TYPE_CLASS | 0x0a)
 #define USB_DT_HUB_NONVAR_SIZE		7
+#define USB_DT_SS_HUB_SIZE              12
 
 struct usb_hub_descriptor {
 	__u8  bDescLength;
@@ -137,11 +168,22 @@ struct usb_hub_descriptor {
 	__le16 wHubCharacteristics;
 	__u8  bPwrOn2PwrGood;
 	__u8  bHubContrCurrent;
-		/* add 1 bit for hub status change; round to bytes */
-	__u8  DeviceRemovable[(USB_MAXCHILDREN + 1 + 7) / 8];
-	__u8  PortPwrCtrlMask[(USB_MAXCHILDREN + 1 + 7) / 8];
-} __attribute__ ((packed));
 
+	/* 2.0 and 3.0 hubs differ here */
+	union {
+		struct {
+			/* add 1 bit for hub status change; round to bytes */
+			__u8  DeviceRemovable[(USB_MAXCHILDREN + 1 + 7) / 8];
+			__u8  PortPwrCtrlMask[(USB_MAXCHILDREN + 1 + 7) / 8];
+		}  __attribute__ ((packed)) hs;
+
+		struct {
+			__u8 bHubHdrDecLat;
+			__u16 wHubDelay;
+			__u16 DeviceRemovable;
+		}  __attribute__ ((packed)) ss;
+	} u;
+} __attribute__ ((packed));
 
 /* port indicator status selectors, tables 11-7 and 11-25 */
 #define HUB_LED_AUTO	0
-- 
1.7.2.1

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux