uhub(4) USB device (dis)connect

2014-10-31 Thread Martin Pieuchot
Device below changes uhub(4) to no longer check *all* the ports status
of *all* the hubs on bus when one of those hubs requests an explore.

This has the nice effect of reducing the delay/bus traffic when a port
status change is detected which reduces (avoid) some timing related
problems when connection devices.

To do so, we only query the status of the ports having reported a status
changed in the interrupt.

I'd appreciate tests on various HC/archs.

Comments, ok?

Index: uhub.c
===
RCS file: /cvs/src/sys/dev/usb/uhub.c,v
retrieving revision 1.74
diff -u -p -r1.74 uhub.c
--- uhub.c  1 Oct 2014 08:29:01 -   1.74
+++ uhub.c  31 Oct 2014 22:00:38 -
@@ -57,8 +57,11 @@ struct uhub_softc {
struct device   sc_dev; /* base device */
struct usbd_device  *sc_hub;/* USB device */
struct usbd_pipe*sc_ipipe;  /* interrupt pipe */
-   u_int8_t*sc_statusbuf;  /* per port status buffer */
-   size_t  sc_statuslen;   /* status bufferlen */
+
+   uint32_t sc_status; /* status from last interrupt */
+   uint8_t *sc_statusbuf;  /* per port status buffer */
+   size_t   sc_statuslen;  /* status bufferlen */
+
u_char  sc_running;
 };
 #define UHUB_PROTO(sc) ((sc)-sc_hub-ddesc.bDeviceProtocol)
@@ -349,18 +352,24 @@ uhub_explore(struct usbd_device *dev)
 
for (port = 1; port = dev-hub-nports; port++) {
up = dev-hub-ports[port-1];
-   err = usbd_get_port_status(dev, port, up-status);
-   if (err) {
-   DPRINTF(%s: get port %d status failed, error=%s\n,
-   sc-sc_dev.dv_xname, port, usbd_errstr(err));
-   continue;
-   }
-   status = UGETW(up-status.wPortStatus);
-   change = UGETW(up-status.wPortChange);
+
reconnect = up-reattach;
up-reattach = 0;
-   DPRINTF(%s: port %d status=0x%04x change=0x%04x\n,
-   sc-sc_dev.dv_xname, port, status, change);
+   change = 0;
+   status = 0;
+
+   if (sc-sc_status  (1  port)) {
+   sc-sc_status = (1  port);
+
+   if (usbd_get_port_status(dev, port, up-status))
+   continue;
+
+   status = UGETW(up-status.wPortStatus);
+   change = UGETW(up-status.wPortChange);
+   DPRINTF(%s: port %d status=0x%04x change=0x%04x\n,
+   sc-sc_dev.dv_xname, port, status, change);
+   }
+
if (change  UPS_C_PORT_ENABLED) {
usbd_clear_port_feature(dev, port, UHF_C_PORT_ENABLE);
if (change  UPS_C_CONNECT_STATUS) {
@@ -556,6 +565,8 @@ void
 uhub_intr(struct usbd_xfer *xfer, void *addr, usbd_status status)
 {
struct uhub_softc *sc = addr;
+   uint32_t stats;
+   int i;
 
if (usbd_is_dying(sc-sc_hub))
return;
@@ -563,6 +574,11 @@ uhub_intr(struct usbd_xfer *xfer, void *
DPRINTF(%s: intr status=%d\n, sc-sc_dev.dv_xname, status);
if (status == USBD_STALLED)
usbd_clear_endpoint_stall_async(sc-sc_ipipe);
-   else if (status == USBD_NORMAL_COMPLETION)
+   else if (status == USBD_NORMAL_COMPLETION) {
+   for (i = 0; i  xfer-actlen; i++)
+   stats |= (uint32_t)(xfer-buffer[i])  (i * 8);
+   sc-sc_status = stats;
+
usb_needs_explore(sc-sc_hub, 0);
+   }
 }
Index: usbdivar.h
===
RCS file: /cvs/src/sys/dev/usb/usbdivar.h,v
retrieving revision 1.64
diff -u -p -r1.64 usbdivar.h
--- usbdivar.h  31 Oct 2014 12:43:33 -  1.64
+++ usbdivar.h  31 Oct 2014 21:12:55 -
@@ -182,7 +182,7 @@ struct usbd_pipe {
 struct usbd_xfer {
struct usbd_pipe   *pipe;
void   *priv;
-   void   *buffer;
+   char   *buffer;
u_int32_t   length;
u_int32_t   actlen;
u_int16_t   flags;



Re: uhub(4) USB device (dis)connect

2014-10-31 Thread Antoine Jacoutot
On Fri, Oct 31, 2014 at 11:19:22PM +0100, Martin Pieuchot wrote:
 Device below changes uhub(4) to no longer check *all* the ports status
 of *all* the hubs on bus when one of those hubs requests an explore.
 
 This has the nice effect of reducing the delay/bus traffic when a port
 status change is detected which reduces (avoid) some timing related
 problems when connection devices.
 
 To do so, we only query the status of the ports having reported a status
 changed in the interrupt.
 
 I'd appreciate tests on various HC/archs.
 
 Comments, ok?

wunderbar!

 Index: uhub.c
 ===
 RCS file: /cvs/src/sys/dev/usb/uhub.c,v
 retrieving revision 1.74
 diff -u -p -r1.74 uhub.c
 --- uhub.c1 Oct 2014 08:29:01 -   1.74
 +++ uhub.c31 Oct 2014 22:00:38 -
 @@ -57,8 +57,11 @@ struct uhub_softc {
   struct device   sc_dev; /* base device */
   struct usbd_device  *sc_hub;/* USB device */
   struct usbd_pipe*sc_ipipe;  /* interrupt pipe */
 - u_int8_t*sc_statusbuf;  /* per port status buffer */
 - size_t  sc_statuslen;   /* status bufferlen */
 +
 + uint32_t sc_status; /* status from last interrupt */
 + uint8_t *sc_statusbuf;  /* per port status buffer */
 + size_t   sc_statuslen;  /* status bufferlen */
 +
   u_char  sc_running;
  };
  #define UHUB_PROTO(sc) ((sc)-sc_hub-ddesc.bDeviceProtocol)
 @@ -349,18 +352,24 @@ uhub_explore(struct usbd_device *dev)
  
   for (port = 1; port = dev-hub-nports; port++) {
   up = dev-hub-ports[port-1];
 - err = usbd_get_port_status(dev, port, up-status);
 - if (err) {
 - DPRINTF(%s: get port %d status failed, error=%s\n,
 - sc-sc_dev.dv_xname, port, usbd_errstr(err));
 - continue;
 - }
 - status = UGETW(up-status.wPortStatus);
 - change = UGETW(up-status.wPortChange);
 +
   reconnect = up-reattach;
   up-reattach = 0;
 - DPRINTF(%s: port %d status=0x%04x change=0x%04x\n,
 - sc-sc_dev.dv_xname, port, status, change);
 + change = 0;
 + status = 0;
 +
 + if (sc-sc_status  (1  port)) {
 + sc-sc_status = (1  port);
 +
 + if (usbd_get_port_status(dev, port, up-status))
 + continue;
 +
 + status = UGETW(up-status.wPortStatus);
 + change = UGETW(up-status.wPortChange);
 + DPRINTF(%s: port %d status=0x%04x change=0x%04x\n,
 + sc-sc_dev.dv_xname, port, status, change);
 + }
 +
   if (change  UPS_C_PORT_ENABLED) {
   usbd_clear_port_feature(dev, port, UHF_C_PORT_ENABLE);
   if (change  UPS_C_CONNECT_STATUS) {
 @@ -556,6 +565,8 @@ void
  uhub_intr(struct usbd_xfer *xfer, void *addr, usbd_status status)
  {
   struct uhub_softc *sc = addr;
 + uint32_t stats;
 + int i;
  
   if (usbd_is_dying(sc-sc_hub))
   return;
 @@ -563,6 +574,11 @@ uhub_intr(struct usbd_xfer *xfer, void *
   DPRINTF(%s: intr status=%d\n, sc-sc_dev.dv_xname, status);
   if (status == USBD_STALLED)
   usbd_clear_endpoint_stall_async(sc-sc_ipipe);
 - else if (status == USBD_NORMAL_COMPLETION)
 + else if (status == USBD_NORMAL_COMPLETION) {
 + for (i = 0; i  xfer-actlen; i++)
 + stats |= (uint32_t)(xfer-buffer[i])  (i * 8);
 + sc-sc_status = stats;
 +
   usb_needs_explore(sc-sc_hub, 0);
 + }
  }
 Index: usbdivar.h
 ===
 RCS file: /cvs/src/sys/dev/usb/usbdivar.h,v
 retrieving revision 1.64
 diff -u -p -r1.64 usbdivar.h
 --- usbdivar.h31 Oct 2014 12:43:33 -  1.64
 +++ usbdivar.h31 Oct 2014 21:12:55 -
 @@ -182,7 +182,7 @@ struct usbd_pipe {
  struct usbd_xfer {
   struct usbd_pipe   *pipe;
   void   *priv;
 - void   *buffer;
 + char   *buffer;
   u_int32_t   length;
   u_int32_t   actlen;
   u_int16_t   flags;
 

-- 
Antoine



Re: uhub(4) USB device (dis)connect

2014-10-31 Thread Martin Pieuchot
On 31/10/14(Fri) 23:19, Martin Pieuchot wrote:
 Device below changes uhub(4) to no longer check *all* the ports status
 of *all* the hubs on bus when one of those hubs requests an explore.
 
 This has the nice effect of reducing the delay/bus traffic when a port
 status change is detected which reduces (avoid) some timing related
 problems when connection devices.
 
 To do so, we only query the status of the ports having reported a status
 changed in the interrupt.
 
 I'd appreciate tests on various HC/archs.
 
 Comments, ok?

Updated diff that correctly deals with devices asking for a reconnect
and xhci(4)'s root hub reporting only one port change per interrupt.

Index: uhub.c
===
RCS file: /cvs/src/sys/dev/usb/uhub.c,v
retrieving revision 1.74
diff -u -p -r1.74 uhub.c
--- uhub.c  1 Oct 2014 08:29:01 -   1.74
+++ uhub.c  1 Nov 2014 00:21:04 -
@@ -57,8 +57,11 @@ struct uhub_softc {
struct device   sc_dev; /* base device */
struct usbd_device  *sc_hub;/* USB device */
struct usbd_pipe*sc_ipipe;  /* interrupt pipe */
-   u_int8_t*sc_statusbuf;  /* per port status buffer */
-   size_t  sc_statuslen;   /* status bufferlen */
+
+   uint32_t sc_status; /* status from last interrupt */
+   uint8_t *sc_statusbuf;  /* per port status buffer */
+   size_t   sc_statuslen;  /* status bufferlen */
+
u_char  sc_running;
 };
 #define UHUB_PROTO(sc) ((sc)-sc_hub-ddesc.bDeviceProtocol)
@@ -349,18 +352,24 @@ uhub_explore(struct usbd_device *dev)
 
for (port = 1; port = dev-hub-nports; port++) {
up = dev-hub-ports[port-1];
-   err = usbd_get_port_status(dev, port, up-status);
-   if (err) {
-   DPRINTF(%s: get port %d status failed, error=%s\n,
-   sc-sc_dev.dv_xname, port, usbd_errstr(err));
-   continue;
-   }
-   status = UGETW(up-status.wPortStatus);
-   change = UGETW(up-status.wPortChange);
+
reconnect = up-reattach;
up-reattach = 0;
-   DPRINTF(%s: port %d status=0x%04x change=0x%04x\n,
-   sc-sc_dev.dv_xname, port, status, change);
+   change = 0;
+   status = 0;
+
+   if ((sc-sc_status  (1  port)) || reconnect) {
+   sc-sc_status = ~(1  port);
+
+   if (usbd_get_port_status(dev, port, up-status))
+   continue;
+
+   status = UGETW(up-status.wPortStatus);
+   change = UGETW(up-status.wPortChange);
+   DPRINTF(%s: port %d status=0x%04x change=0x%04x\n,
+   sc-sc_dev.dv_xname, port, status, change);
+   }
+
if (change  UPS_C_PORT_ENABLED) {
usbd_clear_port_feature(dev, port, UHF_C_PORT_ENABLE);
if (change  UPS_C_CONNECT_STATUS) {
@@ -393,7 +402,7 @@ uhub_explore(struct usbd_device *dev)
 
/* We have a connect status change, handle it. */
usbd_clear_port_feature(dev, port, UHF_C_PORT_CONNECTION);
-   /*usbd_clear_port_feature(dev, port, UHF_C_PORT_ENABLE);*/
+
/*
 * If there is already a device on the port the change status
 * must mean that is has disconnected.  Looking at the
@@ -556,13 +565,21 @@ void
 uhub_intr(struct usbd_xfer *xfer, void *addr, usbd_status status)
 {
struct uhub_softc *sc = addr;
+   uint32_t stats;
+   int i;
 
if (usbd_is_dying(sc-sc_hub))
return;
 
DPRINTF(%s: intr status=%d\n, sc-sc_dev.dv_xname, status);
+
if (status == USBD_STALLED)
usbd_clear_endpoint_stall_async(sc-sc_ipipe);
-   else if (status == USBD_NORMAL_COMPLETION)
+   else if (status == USBD_NORMAL_COMPLETION) {
+   for (i = 0; i  xfer-actlen; i++)
+   stats |= (uint32_t)(xfer-buffer[i])  (i * 8);
+   sc-sc_status |= stats;
+
usb_needs_explore(sc-sc_hub, 0);
+   }
 }
Index: usbdivar.h
===
RCS file: /cvs/src/sys/dev/usb/usbdivar.h,v
retrieving revision 1.64
diff -u -p -r1.64 usbdivar.h
--- usbdivar.h  31 Oct 2014 12:43:33 -  1.64
+++ usbdivar.h  31 Oct 2014 21:12:55 -
@@ -182,7 +182,7 @@ struct usbd_pipe {
 struct usbd_xfer {
struct usbd_pipe   *pipe;
void   *priv;
-   void   *buffer;
+   char   *buffer;
u_int32_t   length;
u_int32_t   actlen;
u_int16_t