LCOV - code coverage report
Current view: top level - dev/usb - uhub.c (source / functions) Hit Total Coverage
Test: 6.4 Lines: 0 248 0.0 %
Date: 2018-10-19 03:25:38 Functions: 0 6 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*      $OpenBSD: uhub.c,v 1.90 2017/04/08 02:57:25 deraadt Exp $ */
       2             : /*      $NetBSD: uhub.c,v 1.64 2003/02/08 03:32:51 ichiro Exp $ */
       3             : /*      $FreeBSD: src/sys/dev/usb/uhub.c,v 1.18 1999/11/17 22:33:43 n_hibma Exp $       */
       4             : 
       5             : /*
       6             :  * Copyright (c) 1998 The NetBSD Foundation, Inc.
       7             :  * All rights reserved.
       8             :  *
       9             :  * This code is derived from software contributed to The NetBSD Foundation
      10             :  * by Lennart Augustsson (lennart@augustsson.net) at
      11             :  * Carlstedt Research & Technology.
      12             :  *
      13             :  * Redistribution and use in source and binary forms, with or without
      14             :  * modification, are permitted provided that the following conditions
      15             :  * are met:
      16             :  * 1. Redistributions of source code must retain the above copyright
      17             :  *    notice, this list of conditions and the following disclaimer.
      18             :  * 2. Redistributions in binary form must reproduce the above copyright
      19             :  *    notice, this list of conditions and the following disclaimer in the
      20             :  *    documentation and/or other materials provided with the distribution.
      21             :  *
      22             :  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
      23             :  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
      24             :  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
      25             :  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
      26             :  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
      27             :  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
      28             :  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
      29             :  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
      30             :  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
      31             :  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
      32             :  * POSSIBILITY OF SUCH DAMAGE.
      33             :  */
      34             : 
      35             : #include <sys/param.h>
      36             : #include <sys/systm.h>
      37             : #include <sys/kernel.h>
      38             : #include <sys/malloc.h>
      39             : #include <sys/device.h>
      40             : 
      41             : #include <machine/bus.h>
      42             : 
      43             : #include <dev/usb/usb.h>
      44             : #include <dev/usb/usbdi.h>
      45             : #include <dev/usb/usbdi_util.h>
      46             : #include <dev/usb/usbdivar.h>
      47             : 
      48             : #define UHUB_INTR_INTERVAL 255  /* ms */
      49             : 
      50             : #ifdef UHUB_DEBUG
      51             : #define DPRINTF(x...)   do { printf(x); } while (0)
      52             : #else
      53             : #define DPRINTF(x...)
      54             : #endif
      55             : 
      56             : #define DEVNAME(sc)     ((sc)->sc_dev.dv_xname)
      57             : 
      58             : struct uhub_softc {
      59             :         struct device           sc_dev;         /* base device */
      60             :         struct usbd_device      *sc_hub;        /* USB device */
      61             :         struct usbd_pipe        *sc_ipipe;      /* interrupt pipe */
      62             : 
      63             :         uint32_t                 sc_status;     /* status from last interrupt */
      64             :         uint8_t                 *sc_statusbuf;  /* per port status buffer */
      65             :         size_t                   sc_statuslen;  /* status bufferlen */
      66             : 
      67             :         u_char                  sc_running;
      68             : };
      69             : #define UHUB_PROTO(sc) ((sc)->sc_hub->ddesc.bDeviceProtocol)
      70             : #define UHUB_IS_HIGH_SPEED(sc) (UHUB_PROTO(sc) != UDPROTO_FSHUB)
      71             : #define UHUB_IS_SINGLE_TT(sc) (UHUB_PROTO(sc) == UDPROTO_HSHUBSTT)
      72             : 
      73             : int uhub_explore(struct usbd_device *hub);
      74             : void uhub_intr(struct usbd_xfer *, void *, usbd_status);
      75             : int uhub_port_connect(struct uhub_softc *, int, int, int);
      76             : 
      77             : /*
      78             :  * We need two attachment points:
      79             :  * hub to usb and hub to hub
      80             :  * Every other driver only connects to hubs
      81             :  */
      82             : 
      83             : int uhub_match(struct device *, void *, void *); 
      84             : void uhub_attach(struct device *, struct device *, void *); 
      85             : int uhub_detach(struct device *, int); 
      86             : 
      87             : struct cfdriver uhub_cd = { 
      88             :         NULL, "uhub", DV_DULL 
      89             : }; 
      90             : 
      91             : const struct cfattach uhub_ca = {
      92             :         sizeof(struct uhub_softc), uhub_match, uhub_attach,  uhub_detach
      93             : };
      94             : 
      95             : const struct cfattach uhub_uhub_ca = {
      96             :         sizeof(struct uhub_softc), uhub_match, uhub_attach,  uhub_detach
      97             : };
      98             : 
      99             : int
     100           0 : uhub_match(struct device *parent, void *match, void *aux)
     101             : {
     102           0 :         struct usb_attach_arg *uaa = aux;
     103           0 :         usb_device_descriptor_t *dd = usbd_get_device_descriptor(uaa->device);
     104             : 
     105           0 :         if (uaa->iface == NULL)
     106           0 :                 return (UMATCH_NONE);
     107             : 
     108             :         /*
     109             :          * The subclass for hubs seems to be 0 for some and 1 for others,
     110             :          * so we just ignore the subclass.
     111             :          */
     112           0 :         if (dd->bDeviceClass == UDCLASS_HUB)
     113           0 :                 return (UMATCH_DEVCLASS_DEVSUBCLASS);
     114           0 :         return (UMATCH_NONE);
     115           0 : }
     116             : 
     117             : void
     118           0 : uhub_attach(struct device *parent, struct device *self, void *aux)
     119             : {
     120           0 :         struct uhub_softc *sc = (struct uhub_softc *)self;
     121           0 :         struct usb_attach_arg *uaa = aux;
     122           0 :         struct usbd_device *dev = uaa->device;
     123             :         struct usbd_hub *hub = NULL;
     124           0 :         union {
     125             :                 usb_hub_descriptor_t    hs;
     126             :                 usb_hub_ss_descriptor_t ss;
     127             :         } hd;
     128             :         int p, port, nports, powerdelay;
     129           0 :         struct usbd_interface *iface;
     130             :         usb_endpoint_descriptor_t *ed;
     131             :         struct usbd_tt *tts = NULL;
     132             :         uint8_t ttthink = 0;
     133             :         usbd_status err;
     134             : #ifdef UHUB_DEBUG
     135             :         int nremov;
     136             : #endif
     137             : 
     138           0 :         sc->sc_hub = dev;
     139             : 
     140           0 :         if (dev->depth > USB_HUB_MAX_DEPTH) {
     141           0 :                 printf("%s: hub depth (%d) exceeded, hub ignored\n",
     142           0 :                        sc->sc_dev.dv_xname, USB_HUB_MAX_DEPTH);
     143           0 :                 return;
     144             :         }
     145             : 
     146             :         /*
     147             :          * Super-Speed hubs need to know their depth to be able to
     148             :          * parse the bits of the route-string that correspond to
     149             :          * their downstream port number.
     150             :          *
     151             :          * This does no apply to root hubs.
     152             :          */
     153           0 :         if (dev->depth != 0 && dev->speed == USB_SPEED_SUPER) {
     154           0 :                 if (usbd_set_hub_depth(dev, dev->depth - 1)) {
     155           0 :                         printf("%s: unable to set HUB depth\n",
     156           0 :                             sc->sc_dev.dv_xname);
     157           0 :                         return;
     158             :                 }
     159             :         }
     160             : 
     161             :         /* Get hub descriptor. */
     162           0 :         if (dev->speed == USB_SPEED_SUPER) {
     163           0 :                 err = usbd_get_hub_ss_descriptor(dev, &hd.ss, 1);
     164           0 :                 nports = hd.ss.bNbrPorts;
     165           0 :                 powerdelay = (hd.ss.bPwrOn2PwrGood * UHD_PWRON_FACTOR);
     166           0 :                 if (!err && nports > 7)
     167           0 :                         usbd_get_hub_ss_descriptor(dev, &hd.ss, nports);
     168             :         } else {
     169           0 :                 err = usbd_get_hub_descriptor(dev, &hd.hs, 1);
     170           0 :                 nports = hd.hs.bNbrPorts;
     171           0 :                 powerdelay = (hd.hs.bPwrOn2PwrGood * UHD_PWRON_FACTOR);
     172           0 :                 ttthink = UGETW(hd.hs.wHubCharacteristics) & UHD_TT_THINK;
     173           0 :                 if (!err && nports > 7)
     174           0 :                         usbd_get_hub_descriptor(dev, &hd.hs, nports);
     175             :         }
     176             : 
     177           0 :         if (err) {
     178             :                 DPRINTF("%s: getting hub descriptor failed, error=%s\n",
     179             :                          sc->sc_dev.dv_xname, usbd_errstr(err));
     180           0 :                 return;
     181             :         }
     182             : 
     183             : #ifdef UHUB_DEBUG
     184             :         for (nremov = 0, port = 1; port <= nports; port++) {
     185             :                 if (dev->speed == USB_SPEED_SUPER) {
     186             :                         if (!UHD_NOT_REMOV(&hd.ss, port))
     187             :                                 nremov++;
     188             :                 } else {
     189             :                         if (!UHD_NOT_REMOV(&hd.hs, port))
     190             :                                 nremov++;
     191             :                 }
     192             :         }
     193             : 
     194             :         printf("%s: %d port%s with %d removable, %s powered",
     195             :                sc->sc_dev.dv_xname, nports, nports != 1 ? "s" : "",
     196             :                nremov, dev->self_powered ? "self" : "bus");
     197             : 
     198             :         if (dev->depth > 0 && UHUB_IS_HIGH_SPEED(sc)) {
     199             :                 printf(", %s transaction translator%s",
     200             :                     UHUB_IS_SINGLE_TT(sc) ? "single" : "multiple",
     201             :                     UHUB_IS_SINGLE_TT(sc) ? "" : "s");
     202             :         }
     203             : 
     204             :         printf("\n");
     205             : #endif
     206             : 
     207           0 :         if (nports == 0) {
     208           0 :                 printf("%s: no ports, hub ignored\n", sc->sc_dev.dv_xname);
     209           0 :                 goto bad;
     210             :         }
     211             : 
     212           0 :         hub = malloc(sizeof(*hub), M_USBDEV, M_NOWAIT);
     213           0 :         if (hub == NULL)
     214           0 :                 return;
     215           0 :         hub->ports = mallocarray(nports, sizeof(struct usbd_port),
     216             :             M_USBDEV, M_NOWAIT);
     217           0 :         if (hub->ports == NULL) {
     218           0 :                 free(hub, M_USBDEV, sizeof *hub);
     219           0 :                 return;
     220             :         }
     221           0 :         dev->hub = hub;
     222           0 :         dev->hub->hubsoftc = sc;
     223           0 :         hub->explore = uhub_explore;
     224           0 :         hub->nports = nports;
     225           0 :         hub->powerdelay = powerdelay;
     226           0 :         hub->ttthink = ttthink >> 5;
     227             : 
     228           0 :         if (!dev->self_powered && dev->powersrc->parent != NULL &&
     229           0 :             !dev->powersrc->parent->self_powered) {
     230           0 :                 printf("%s: bus powered hub connected to bus powered hub, "
     231           0 :                        "ignored\n", sc->sc_dev.dv_xname);
     232           0 :                 goto bad;
     233             :         }
     234             : 
     235             :         /* Set up interrupt pipe. */
     236           0 :         err = usbd_device2interface_handle(dev, 0, &iface);
     237           0 :         if (err) {
     238           0 :                 printf("%s: no interface handle\n", sc->sc_dev.dv_xname);
     239           0 :                 goto bad;
     240             :         }
     241           0 :         ed = usbd_interface2endpoint_descriptor(iface, 0);
     242           0 :         if (ed == NULL) {
     243           0 :                 printf("%s: no endpoint descriptor\n", sc->sc_dev.dv_xname);
     244           0 :                 goto bad;
     245             :         }
     246           0 :         if ((ed->bmAttributes & UE_XFERTYPE) != UE_INTERRUPT) {
     247           0 :                 printf("%s: bad interrupt endpoint\n", sc->sc_dev.dv_xname);
     248           0 :                 goto bad;
     249             :         }
     250             : 
     251           0 :         sc->sc_statuslen = (nports + 1 + 7) / 8;
     252           0 :         sc->sc_statusbuf = malloc(sc->sc_statuslen, M_USBDEV, M_NOWAIT);
     253           0 :         if (!sc->sc_statusbuf)
     254             :                 goto bad;
     255             : 
     256           0 :         err = usbd_open_pipe_intr(iface, ed->bEndpointAddress,
     257           0 :                   USBD_SHORT_XFER_OK, &sc->sc_ipipe, sc, sc->sc_statusbuf,
     258           0 :                   sc->sc_statuslen, uhub_intr, UHUB_INTR_INTERVAL);
     259           0 :         if (err) {
     260           0 :                 printf("%s: cannot open interrupt pipe\n",
     261           0 :                        sc->sc_dev.dv_xname);
     262           0 :                 goto bad;
     263             :         }
     264             : 
     265             :         /* Wait with power off for a while. */
     266           0 :         usbd_delay_ms(dev, USB_POWER_DOWN_TIME);
     267             : 
     268             :         /*
     269             :          * To have the best chance of success we do things in the exact same
     270             :          * order as Windoze98.  This should not be necessary, but some
     271             :          * devices do not follow the USB specs to the letter.
     272             :          *
     273             :          * These are the events on the bus when a hub is attached:
     274             :          *  Get device and config descriptors (see attach code)
     275             :          *  Get hub descriptor (see above)
     276             :          *  For all ports
     277             :          *     turn on power
     278             :          *     wait for power to become stable
     279             :          * (all below happens in explore code)
     280             :          *  For all ports
     281             :          *     clear C_PORT_CONNECTION
     282             :          *  For all ports
     283             :          *     get port status
     284             :          *     if device connected
     285             :          *        wait 100 ms
     286             :          *        turn on reset
     287             :          *        wait
     288             :          *        clear C_PORT_RESET
     289             :          *        get port status
     290             :          *        proceed with device attachment
     291             :          */
     292             : 
     293           0 :         if (UHUB_IS_HIGH_SPEED(sc)) {
     294           0 :                 tts = mallocarray((UHUB_IS_SINGLE_TT(sc) ? 1 : nports),
     295             :                     sizeof (struct usbd_tt), M_USBDEV, M_NOWAIT);
     296           0 :                 if (!tts)
     297             :                         goto bad;
     298             :         }
     299             :         /* Set up data structures */
     300           0 :         for (p = 0; p < nports; p++) {
     301           0 :                 struct usbd_port *up = &hub->ports[p];
     302           0 :                 up->device = NULL;
     303           0 :                 up->parent = dev;
     304           0 :                 up->portno = p + 1;
     305           0 :                 if (dev->self_powered)
     306             :                         /* Self powered hub, give ports maximum current. */
     307           0 :                         up->power = USB_MAX_POWER;
     308             :                 else
     309           0 :                         up->power = USB_MIN_POWER;
     310           0 :                 up->restartcnt = 0;
     311           0 :                 up->reattach = 0;
     312           0 :                 if (UHUB_IS_HIGH_SPEED(sc)) {
     313           0 :                         up->tt = &tts[UHUB_IS_SINGLE_TT(sc) ? 0 : p];
     314           0 :                         up->tt->hub = hub;
     315           0 :                 } else {
     316           0 :                         up->tt = NULL;
     317             :                 }
     318             :         }
     319             : 
     320           0 :         for (port = 1; port <= nports; port++) {
     321             :                 /* Turn the power on. */
     322           0 :                 err = usbd_set_port_feature(dev, port, UHF_PORT_POWER);
     323           0 :                 if (err)
     324           0 :                         printf("%s: port %d power on failed, %s\n",
     325           0 :                                sc->sc_dev.dv_xname, port,
     326           0 :                                usbd_errstr(err));
     327             :                 /* Make sure we check the port status at least once. */
     328           0 :                 sc->sc_status |= (1 << port);
     329             :         }
     330             : 
     331             :         /* Wait for stable power. */
     332           0 :         if (dev->powersrc->parent != NULL)
     333           0 :                 usbd_delay_ms(dev, powerdelay + USB_EXTRA_POWER_UP_TIME);
     334             : 
     335             :         /* The usual exploration will finish the setup. */
     336             : 
     337           0 :         sc->sc_running = 1;
     338             : 
     339           0 :         return;
     340             : 
     341             :  bad:
     342           0 :         if (sc->sc_statusbuf)
     343           0 :                 free(sc->sc_statusbuf, M_USBDEV, sc->sc_statuslen);
     344           0 :         if (hub) {
     345           0 :                 if (hub->ports)
     346           0 :                         free(hub->ports, M_USBDEV, 0);
     347           0 :                 free(hub, M_USBDEV, sizeof *hub);
     348           0 :         }
     349           0 :         dev->hub = NULL;
     350           0 : }
     351             : 
     352             : int
     353           0 : uhub_explore(struct usbd_device *dev)
     354             : {
     355           0 :         struct uhub_softc *sc = dev->hub->hubsoftc;
     356             :         struct usbd_port *up;
     357             :         int status, change;
     358             :         int port;
     359             : 
     360           0 :         if (usbd_is_dying(sc->sc_hub))
     361           0 :                 return (EIO);
     362             : 
     363           0 :         if (!sc->sc_running)
     364           0 :                 return (ENXIO);
     365             : 
     366             :         /* Ignore hubs that are too deep. */
     367           0 :         if (sc->sc_hub->depth > USB_HUB_MAX_DEPTH)
     368           0 :                 return (EOPNOTSUPP);
     369             : 
     370           0 :         for (port = 1; port <= sc->sc_hub->hub->nports; port++) {
     371           0 :                 up = &sc->sc_hub->hub->ports[port-1];
     372             : 
     373             :                 change = 0;
     374             :                 status = 0;
     375             : 
     376           0 :                 if ((sc->sc_status & (1 << port)) || up->reattach) {
     377           0 :                         sc->sc_status &= ~(1 << port);
     378             : 
     379           0 :                         if (usbd_get_port_status(dev, port, &up->status))
     380             :                                 continue;
     381             : 
     382           0 :                         status = UGETW(up->status.wPortStatus);
     383           0 :                         change = UGETW(up->status.wPortChange);
     384             :                         DPRINTF("%s: port %d status=0x%04x change=0x%04x\n",
     385             :                             sc->sc_dev.dv_xname, port, status, change);
     386           0 :                 }
     387             : 
     388           0 :                 if (up->reattach) {
     389           0 :                         change |= UPS_C_CONNECT_STATUS;
     390           0 :                         up->reattach = 0;
     391           0 :                 }
     392             : 
     393           0 :                 if (change & UPS_C_PORT_ENABLED) {
     394           0 :                         usbd_clear_port_feature(sc->sc_hub, port,
     395             :                             UHF_C_PORT_ENABLE);
     396           0 :                         if (change & UPS_C_CONNECT_STATUS) {
     397             :                                 /* Ignore the port error if the device
     398             :                                    vanished. */
     399           0 :                         } else if (status & UPS_PORT_ENABLED) {
     400           0 :                                 printf("%s: illegal enable change, port %d\n",
     401           0 :                                        sc->sc_dev.dv_xname, port);
     402           0 :                         } else {
     403             :                                 /* Port error condition. */
     404           0 :                                 if (up->restartcnt) /* no message first time */
     405           0 :                                         printf("%s: port error, restarting "
     406             :                                                "port %d\n",
     407           0 :                                                sc->sc_dev.dv_xname, port);
     408             : 
     409           0 :                                 if (up->restartcnt++ < USBD_RESTART_MAX)
     410           0 :                                         change |= UPS_C_CONNECT_STATUS;
     411             :                                 else
     412           0 :                                         printf("%s: port error, giving up "
     413             :                                                "port %d\n",
     414           0 :                                                sc->sc_dev.dv_xname, port);
     415             :                         }
     416             :                 }
     417             : 
     418           0 :                 if (change & UPS_C_PORT_RESET) {
     419           0 :                         usbd_clear_port_feature(sc->sc_hub, port,
     420             :                             UHF_C_PORT_RESET);
     421           0 :                         change |= UPS_C_CONNECT_STATUS;
     422           0 :                 }
     423             : 
     424           0 :                 if (change & UPS_C_BH_PORT_RESET &&
     425           0 :                     sc->sc_hub->speed == USB_SPEED_SUPER) {
     426           0 :                         usbd_clear_port_feature(sc->sc_hub, port,
     427             :                             UHF_C_BH_PORT_RESET);
     428           0 :                 }
     429             : 
     430           0 :                 if (change & UPS_C_CONNECT_STATUS) {
     431           0 :                         if (uhub_port_connect(sc, port, status, change))
     432             :                                 continue;
     433             : 
     434             :                         /* The port set up succeeded, reset error count. */
     435           0 :                         up->restartcnt = 0;
     436           0 :                 }
     437             : 
     438           0 :                 if (change & UPS_C_PORT_LINK_STATE) {
     439           0 :                         usbd_clear_port_feature(sc->sc_hub, port,
     440             :                             UHF_C_PORT_LINK_STATE);
     441           0 :                 }
     442             : 
     443             :                 /* Recursive explore. */
     444           0 :                 if (up->device != NULL && up->device->hub != NULL)
     445           0 :                         up->device->hub->explore(up->device);
     446             :         }
     447             : 
     448           0 :         return (0);
     449           0 : }
     450             : 
     451             : /*
     452             :  * Called from process context when the hub is gone.
     453             :  * Detach all devices on active ports.
     454             :  */
     455             : int
     456           0 : uhub_detach(struct device *self, int flags)
     457             : {
     458           0 :         struct uhub_softc *sc = (struct uhub_softc *)self;
     459           0 :         struct usbd_hub *hub = sc->sc_hub->hub;
     460             :         struct usbd_port *rup;
     461             :         int port;
     462             : 
     463           0 :         if (hub == NULL)                /* Must be partially working */
     464           0 :                 return (0);
     465             : 
     466           0 :         usbd_abort_pipe(sc->sc_ipipe);
     467           0 :         usbd_close_pipe(sc->sc_ipipe);
     468             : 
     469           0 :         for (port = 0; port < hub->nports; port++) {
     470           0 :                 rup = &hub->ports[port];
     471           0 :                 if (rup->device != NULL) {
     472           0 :                         usbd_detach(rup->device, self);
     473           0 :                         rup->device = NULL;
     474           0 :                 }
     475             :         }
     476             : 
     477           0 :         if (hub->ports[0].tt)
     478           0 :                 free(hub->ports[0].tt, M_USBDEV, 0);
     479           0 :         if (sc->sc_statusbuf)
     480           0 :                 free(sc->sc_statusbuf, M_USBDEV, sc->sc_statuslen);
     481           0 :         if (hub->ports)
     482           0 :                 free(hub->ports, M_USBDEV, 0);
     483           0 :         free(hub, M_USBDEV, sizeof *hub);
     484           0 :         sc->sc_hub->hub = NULL;
     485             : 
     486           0 :         return (0);
     487           0 : }
     488             : 
     489             : /*
     490             :  * This is an indication that some port has changed status.  Remember
     491             :  * the ports that need attention and notify the USB task thread that
     492             :  * we need to be explored again.
     493             :  */
     494             : void
     495           0 : uhub_intr(struct usbd_xfer *xfer, void *addr, usbd_status status)
     496             : {
     497           0 :         struct uhub_softc *sc = addr;
     498             :         uint32_t stats = 0;
     499             :         int i;
     500             : 
     501           0 :         if (usbd_is_dying(sc->sc_hub))
     502           0 :                 return;
     503             : 
     504             :         DPRINTF("%s: intr status=%d\n", sc->sc_dev.dv_xname, status);
     505             : 
     506           0 :         if (status == USBD_STALLED)
     507           0 :                 usbd_clear_endpoint_stall_async(sc->sc_ipipe);
     508           0 :         else if (status == USBD_NORMAL_COMPLETION) {
     509           0 :                 for (i = 0; i < xfer->actlen; i++)
     510           0 :                         stats |= (uint32_t)(xfer->buffer[i]) << (i * 8);
     511           0 :                 sc->sc_status |= stats;
     512             : 
     513           0 :                 usb_needs_explore(sc->sc_hub, 0);
     514           0 :         }
     515           0 : }
     516             : 
     517             : int
     518           0 : uhub_port_connect(struct uhub_softc *sc, int port, int status, int change)
     519             : {
     520           0 :         struct usbd_port *up = &sc->sc_hub->hub->ports[port-1];
     521             :         int speed;
     522             : 
     523             :         /* We have a connect status change, handle it. */
     524           0 :         usbd_clear_port_feature(sc->sc_hub, port, UHF_C_PORT_CONNECTION);
     525             : 
     526             :         /*
     527             :          * If there is already a device on the port the change status
     528             :          * must mean that is has disconnected.  Looking at the
     529             :          * current connect status is not enough to figure this out
     530             :          * since a new unit may have been connected before we handle
     531             :          * the disconnect.
     532             :          */
     533           0 :         if (up->device != NULL) {
     534             :                 /* Disconnected */
     535           0 :                 usbd_detach(up->device, &sc->sc_dev);
     536           0 :                 up->device = NULL;
     537           0 :         }
     538             : 
     539             :         /* Nothing connected, just ignore it. */
     540           0 :         if ((status & UPS_CURRENT_CONNECT_STATUS) == 0)
     541           0 :                 return (0);
     542             : 
     543             :         /* Connected */
     544           0 :         if ((status & (UPS_PORT_POWER|UPS_PORT_POWER_SS)) == 0) {
     545           0 :                 printf("%s: connected port %d has no power\n", DEVNAME(sc),
     546             :                     port);
     547           0 :                 return (-1);
     548             :         }
     549             : 
     550             :         /* Wait for maximum device power up time. */
     551           0 :         usbd_delay_ms(sc->sc_hub, USB_PORT_POWERUP_DELAY);
     552             : 
     553             :         /* Reset port, which implies enabling it. */
     554           0 :         if (usbd_reset_port(sc->sc_hub, port)) {
     555           0 :                 printf("%s: port %d reset failed\n", DEVNAME(sc), port);
     556           0 :                 return (-1);
     557             :         }
     558             :         /* Get port status again, it might have changed during reset */
     559           0 :         if (usbd_get_port_status(sc->sc_hub, port, &up->status))
     560           0 :                 return (-1);
     561             : 
     562           0 :         status = UGETW(up->status.wPortStatus);
     563           0 :         change = UGETW(up->status.wPortChange);
     564             :         DPRINTF("%s: port %d status=0x%04x change=0x%04x\n", DEVNAME(sc),
     565             :             port, status, change);
     566             : 
     567             :         /* Nothing connected, just ignore it. */
     568           0 :         if ((status & UPS_CURRENT_CONNECT_STATUS) == 0) {
     569             :                 DPRINTF("%s: port %d, device disappeared after reset\n",
     570             :                     DEVNAME(sc), port);
     571           0 :                 return (-1);
     572             :         }
     573             : 
     574             :         /*
     575             :          * Figure out device speed.  This is a bit tricky because
     576             :          * UPS_PORT_POWER_SS and UPS_LOW_SPEED share the same bit.
     577             :          */
     578           0 :         if ((status & UPS_PORT_POWER) == 0)
     579           0 :                 status &= ~UPS_PORT_POWER_SS;
     580             : 
     581           0 :         if (status & UPS_HIGH_SPEED)
     582           0 :                 speed = USB_SPEED_HIGH;
     583           0 :         else if (status & UPS_LOW_SPEED)
     584           0 :                 speed = USB_SPEED_LOW;
     585             :         else {
     586             :                 /*
     587             :                  * If there is no power bit set, it is certainly
     588             :                  * a Super Speed device, so use the speed of its
     589             :                  * parent hub.
     590             :                  */
     591           0 :                 if (status & UPS_PORT_POWER)
     592           0 :                         speed = USB_SPEED_FULL;
     593             :                 else
     594           0 :                         speed = sc->sc_hub->speed;
     595             :         }
     596             : 
     597             :         /*
     598             :          * Reduce the speed, otherwise we won't setup the proper
     599             :          * transfer methods.
     600             :          */
     601           0 :         if (speed > sc->sc_hub->speed)
     602           0 :                 speed = sc->sc_hub->speed;
     603             : 
     604             :         /* Get device info and set its address. */
     605           0 :         if (usbd_new_device(&sc->sc_dev, sc->sc_hub->bus, sc->sc_hub->depth + 1,
     606             :             speed, port, up)) {
     607             :                 /*
     608             :                  * The unit refused to accept a new address, or had
     609             :                  * some other serious problem.  Since we cannot leave
     610             :                  * at 0 we have to disable the port instead.
     611             :                  */
     612           0 :                 printf("%s: device problem, disabling port %d\n", DEVNAME(sc),
     613             :                     port);
     614           0 :                 usbd_clear_port_feature(sc->sc_hub, port, UHF_PORT_ENABLE);
     615             : 
     616           0 :                 return (-1);
     617             :         }
     618             : 
     619           0 :         return (0);
     620           0 : }

Generated by: LCOV version 1.13