On Sun, Mar 01, 2015 at 11:52:42PM -0300, Henrique Lengler wrote:
> On Sun, Mar 01, 2015 at 11:34:54AM -0430, Naim, Halim. wrote:
> > Thanks, I have applied and recompiled. I can attach/detach. ifconfig
> > down up, and sh /etc/netstart athn0, without any problems now. I will
> > use it through the day to see if It goes down by itself as it use to.
> 
> I will test this.

Please test this updated diff instead (after more discussion with mpi@).

I'm going to start committing bits of this soon but it's still worth
testing this entire diff ASAP.

Index: if_athn_usb.c
===================================================================
RCS file: /cvs/src/sys/dev/usb/if_athn_usb.c,v
retrieving revision 1.26
diff -u -p -r1.26 if_athn_usb.c
--- if_athn_usb.c       10 Feb 2015 23:25:46 -0000      1.26
+++ if_athn_usb.c       2 Mar 2015 10:46:13 -0000
@@ -117,8 +117,6 @@ int         athn_usb_htc_msg(struct athn_usb_so
 int            athn_usb_htc_setup(struct athn_usb_softc *);
 int            athn_usb_htc_connect_svc(struct athn_usb_softc *, uint16_t,
                    uint8_t, uint8_t, uint8_t *);
-void           athn_usb_wmieof(struct usbd_xfer *, void *,
-                   usbd_status);
 int            athn_usb_wmi_xcmd(struct athn_usb_softc *, uint16_t, void *,
                    int, void *);
 int            athn_usb_read_rom(struct athn_softc *);
@@ -126,6 +124,7 @@ uint32_t    athn_usb_read(struct athn_softc
 void           athn_usb_write(struct athn_softc *, uint32_t, uint32_t);
 void           athn_usb_write_barrier(struct athn_softc *);
 int            athn_usb_media_change(struct ifnet *);
+void           athn_usb_next_scan(void *);
 int            athn_usb_newstate(struct ieee80211com *, enum ieee80211_state,
                    int);
 void           athn_usb_newstate_cb(struct athn_usb_softc *, void *);
@@ -291,6 +290,8 @@ athn_usb_detach(struct device *self, int
        /* Wait for all async commands to complete. */
        athn_usb_wait_async(usc);
 
+       usbd_ref_wait(usc->sc_udev);
+
        /* Abort and close Tx/Rx pipes. */
        athn_usb_close_pipes(usc);
 
@@ -348,10 +349,7 @@ athn_usb_attachhook(void *xsc)
 #endif
        ic->ic_newstate = athn_usb_newstate;
        ic->ic_media.ifm_change = athn_usb_media_change;
-
-       /* Firmware cannot handle more than 8 STAs. */
-       if (ic->ic_max_nnodes > AR_USB_MAX_STA)
-               ic->ic_max_nnodes = AR_USB_MAX_STA;
+       timeout_set(&sc->scan_to, athn_usb_next_scan, usc);
 
        ops->rx_enable = athn_usb_rx_enable;
        splx(s);
@@ -436,18 +434,26 @@ athn_usb_open_pipes(struct athn_usb_soft
 void
 athn_usb_close_pipes(struct athn_usb_softc *usc)
 {
-       if (usc->tx_data_pipe != NULL)
+       if (usc->tx_data_pipe != NULL) {
                usbd_close_pipe(usc->tx_data_pipe);
-       if (usc->rx_data_pipe != NULL)
+               usc->tx_data_pipe = NULL;
+       }
+       if (usc->rx_data_pipe != NULL) {
                usbd_close_pipe(usc->rx_data_pipe);
-       if (usc->tx_intr_pipe != NULL)
+               usc->rx_data_pipe = NULL;
+       }
+       if (usc->tx_intr_pipe != NULL) {
                usbd_close_pipe(usc->tx_intr_pipe);
+               usc->tx_intr_pipe = NULL;
+       }
        if (usc->rx_intr_pipe != NULL) {
-               usbd_abort_pipe(usc->rx_intr_pipe);
                usbd_close_pipe(usc->rx_intr_pipe);
+               usc->rx_intr_pipe = NULL;
        }
-       if (usc->ibuf != NULL)
+       if (usc->ibuf != NULL) {
                free(usc->ibuf, M_USBDEV, 0);
+               usc->ibuf = NULL;
+       }
 }
 
 int
@@ -590,7 +596,6 @@ athn_usb_task(void *arg)
                ring->queued--;
                ring->next = (ring->next + 1) % ATHN_USB_HOST_CMD_RING_COUNT;
        }
-       wakeup(ring);
        splx(s);
 }
 
@@ -602,8 +607,11 @@ athn_usb_do_async(struct athn_usb_softc 
        struct athn_usb_host_cmd *cmd;
        int s;
 
-       if (ring->queued)
+       if (ring->queued == ATHN_USB_HOST_CMD_RING_COUNT) {
+               printf("%s: host cmd queue overrun\n", usc->usb_dev.dv_xname);
                return; /* XXX */
+       }
+       
        s = splusb();
        cmd = &ring->cmd[ring->cur];
        cmd->cb = cb;
@@ -621,8 +629,7 @@ void
 athn_usb_wait_async(struct athn_usb_softc *usc)
 {
        /* Wait for all queued asynchronous commands to complete. */
-       while (usc->cmdq.queued > 0)
-               tsleep(&usc->cmdq, 0, "cmdq", 0);
+       usb_wait_task(usc->sc_udev, &usc->sc_task);
 }
 
 int
@@ -830,19 +837,6 @@ athn_usb_htc_connect_svc(struct athn_usb
        return (0);
 }
 
-void
-athn_usb_wmieof(struct usbd_xfer *xfer, void *priv,
-    usbd_status status)
-{
-       struct athn_usb_softc *usc = priv;
-
-       if (__predict_false(status == USBD_STALLED))
-               usbd_clear_endpoint_stall_async(usc->tx_intr_pipe);
-
-       usc->wmi_done = 1;
-       wakeup(&usc->wmi_done);
-}
-
 int
 athn_usb_wmi_xcmd(struct athn_usb_softc *usc, uint16_t cmd_id, void *ibuf,
     int ilen, void *obuf)
@@ -852,6 +846,24 @@ athn_usb_wmi_xcmd(struct athn_usb_softc 
        struct ar_wmi_cmd_hdr *wmi;
        int s, error;
 
+       if (usbd_is_dying(usc->sc_udev))
+               return ENXIO;
+
+       s = splusb();
+       while (usc->wait_cmd_id) {
+               /* 
+                * The previous USB transfer is not done yet. We can't use
+                * data->xfer until it is done or we'll cause major confusion
+                * in the USB stack.
+                */
+               tsleep(&usc->wait_cmd_id, 0, "athnwmx", ATHN_USB_CMD_TIMEOUT);
+               if (usbd_is_dying(usc->sc_udev)) {
+                       splx(s);
+                       return ENXIO;
+               }
+       }
+       splx(s);
+
        htc = (struct ar_htc_frame_hdr *)data->buf;
        memset(htc, 0, sizeof(*htc));
        htc->endpoint_id = usc->ep_ctrl;
@@ -864,12 +876,11 @@ athn_usb_wmi_xcmd(struct athn_usb_softc 
 
        memcpy(&wmi[1], ibuf, ilen);
 
-       usbd_setup_xfer(data->xfer, usc->tx_intr_pipe, usc, data->buf,
+       usbd_setup_xfer(data->xfer, usc->tx_intr_pipe, NULL, data->buf,
            sizeof(*htc) + sizeof(*wmi) + ilen,
            USBD_SHORT_XFER_OK | USBD_NO_COPY, ATHN_USB_CMD_TIMEOUT,
-           athn_usb_wmieof);
+           NULL);
        s = splusb();
-       usc->wmi_done = 0;
        error = usbd_transfer(data->xfer);
        if (__predict_false(error != USBD_IN_PROGRESS && error != 0)) {
                splx(s);
@@ -877,12 +888,26 @@ athn_usb_wmi_xcmd(struct athn_usb_softc 
        }
        usc->obuf = obuf;
        usc->wait_cmd_id = cmd_id;
-       /* Wait for WMI command to complete. */
-       error = tsleep(&usc->wait_cmd_id, 0, "athnwmi", hz);
+       /* 
+        * Wait for WMI command complete interrupt. In case it does not fire
+        * wait until the USB transfer times out to avoid racing the transfer.
+        */
+       error = tsleep(&usc->wait_cmd_id, 0, "athnwmi", ATHN_USB_CMD_TIMEOUT);
+       if (error) {
+               if (error == EWOULDBLOCK) {
+                       printf("%s: firmware command 0x%x timed out)\n",
+                           usc->usb_dev.dv_xname, cmd_id);
+                       error = ETIMEDOUT;
+               }
+       }
+
+       /* 
+        * Both the WMI command and transfer are done or have timed out.
+        * Allow other threads to enter this function and use data->xfer.
+        */
        usc->wait_cmd_id = 0;
-       /* Most of the time this would have complete already. */
-       while (__predict_false(!usc->wmi_done))
-               tsleep(&usc->wmi_done, 0, "athnwmi", 0);
+       wakeup(&usc->wait_cmd_id);
+
        splx(s);
        return (error);
 }
@@ -956,8 +981,12 @@ athn_usb_write_barrier(struct athn_softc
 int
 athn_usb_media_change(struct ifnet *ifp)
 {
+       struct athn_usb_softc *usc = (struct athn_usb_softc *)ifp->if_softc;
        int error;
 
+       if (usbd_is_dying(usc->sc_udev))
+               return ENXIO;
+
        error = ieee80211_media_change(ifp);
        if (error != ENETRESET)
                return (error);
@@ -970,6 +999,27 @@ athn_usb_media_change(struct ifnet *ifp)
        return (error);
 }
 
+void
+athn_usb_next_scan(void *arg)
+{
+       struct athn_usb_softc *usc = arg;
+       struct athn_softc *sc = &usc->sc_sc;
+       struct ieee80211com *ic = &sc->sc_ic;
+       int s;
+
+       if (usbd_is_dying(usc->sc_udev))
+               return;
+
+       usbd_ref_incr(usc->sc_udev);
+
+       s = splnet();
+       if (ic->ic_state == IEEE80211_S_SCAN)
+               ieee80211_next_scan(&ic->ic_if);
+       splx(s);
+
+       usbd_ref_decr(usc->sc_udev);
+}
+
 int
 athn_usb_newstate(struct ieee80211com *ic, enum ieee80211_state nstate,
     int arg)
@@ -992,7 +1042,9 @@ athn_usb_newstate_cb(struct athn_usb_sof
        struct ieee80211com *ic = &sc->sc_ic;
        enum ieee80211_state ostate;
        uint32_t reg, imask;
+#ifndef IEEE80211_STA_ONLY
        uint8_t sta_index;
+#endif
        int s, error;
 
        timeout_del(&sc->calib_to);
@@ -1002,9 +1054,19 @@ athn_usb_newstate_cb(struct athn_usb_sof
        DPRINTF(("newstate %d -> %d\n", ostate, cmd->state));
 
        if (ostate == IEEE80211_S_RUN) {
-               sta_index = ((struct athn_node *)ic->ic_bss)->sta_index;
-               (void)athn_usb_wmi_xcmd(usc, AR_WMI_CMD_NODE_REMOVE,
-                   &sta_index, sizeof(sta_index), NULL);
+#ifndef IEEE80211_STA_ONLY
+               if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
+                       /* XXX really needed? */
+                       sta_index = ((struct athn_node *)ic->ic_bss)->sta_index;
+                       (void)athn_usb_wmi_xcmd(usc, AR_WMI_CMD_NODE_REMOVE,
+                           &sta_index, sizeof(sta_index), NULL);
+               }
+#endif
+               reg = AR_READ(sc, AR_RX_FILTER);
+               reg = (reg & ~AR_RX_FILTER_MYBEACON) |
+                   AR_RX_FILTER_BEACON;
+               AR_WRITE(sc, AR_RX_FILTER, reg);
+               AR_WRITE_BARRIER(sc);
        }
        switch (cmd->state) {
        case IEEE80211_S_INIT:
@@ -1014,7 +1076,8 @@ athn_usb_newstate_cb(struct athn_usb_sof
                /* Make the LED blink while scanning. */
                athn_set_led(sc, !sc->led_state);
                (void)athn_usb_switch_chan(sc, ic->ic_bss->ni_chan, NULL);
-               timeout_add_msec(&sc->scan_to, 200);
+               if (!usbd_is_dying(usc->sc_udev))
+                       timeout_add_msec(&sc->scan_to, 200);
                break;
        case IEEE80211_S_AUTH:
                athn_set_led(sc, 0);
@@ -1028,8 +1091,13 @@ athn_usb_newstate_cb(struct athn_usb_sof
                if (ic->ic_opmode == IEEE80211_M_MONITOR)
                        break;
 
-               /* Create node entry for our BSS. */
-               error = athn_usb_create_node(usc, ic->ic_bss);
+#ifndef IEEE80211_STA_ONLY
+               if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
+                       /* Create node entry for our BSS */
+                       /* XXX really needed? breaks station mode on down/up */
+                       error = athn_usb_create_node(usc, ic->ic_bss);
+               }
+#endif
 
                athn_set_bss(sc, ic->ic_bss);
                athn_usb_wmi_cmd(usc, AR_WMI_CMD_DISABLE_INTR);
@@ -1109,6 +1177,7 @@ athn_usb_node_leave_cb(struct athn_usb_s
 
        (void)athn_usb_wmi_xcmd(usc, AR_WMI_CMD_NODE_REMOVE,
            &sta_index, sizeof(sta_index), NULL);
+       usc->nnodes--;
 }
 
 int
@@ -1176,6 +1245,10 @@ athn_usb_create_node(struct athn_usb_sof
        struct ar_htc_target_rate rate;
        int error;
 
+       /* Firmware cannot handle more than 8 STAs. */
+       if (usc->nnodes > AR_USB_MAX_STA)
+               return ENOBUFS;
+
        an->sta_index = IEEE80211_AID(ni->ni_associd);
 
        /* Create node entry on target. */
@@ -1192,6 +1265,7 @@ athn_usb_create_node(struct athn_usb_sof
            &sta, sizeof(sta), NULL);
        if (error != 0)
                return (error);
+       usc->nnodes++;
 
        /* Setup supported rates. */
        memset(&rate, 0, sizeof(rate));
@@ -1263,11 +1337,6 @@ athn_usb_switch_chan(struct athn_softc *
                        return (error);
        }
 
-       error = athn_usb_wmi_cmd(usc, AR_WMI_CMD_START_RECV);
-       if (error != 0)
-               return (error);
-       athn_rx_start(sc);
-
        mode = htobe16(IEEE80211_IS_CHAN_2GHZ(c) ?
            AR_HTC_MODE_11NG : AR_HTC_MODE_11NA);
        error = athn_usb_wmi_xcmd(usc, AR_WMI_CMD_SET_MODE,
@@ -1275,6 +1344,11 @@ athn_usb_switch_chan(struct athn_softc *
        if (error != 0)
                return (error);
 
+       error = athn_usb_wmi_cmd(usc, AR_WMI_CMD_START_RECV);
+       if (error != 0)
+               return (error);
+       athn_rx_start(sc);
+
        /* Re-enable interrupts. */
        error = athn_usb_wmi_cmd(usc, AR_WMI_CMD_ENABLE_INTR);
        return (error);
@@ -1480,7 +1554,6 @@ athn_usb_rx_wmi_ctrl(struct athn_usb_sof
                        memcpy(usc->obuf, &wmi[1], len - sizeof(*wmi));
                }
                /* Notify caller of completion. */
-               usc->wait_cmd_id = 0;
                wakeup(&usc->wait_cmd_id);
                return;
        }
@@ -1518,6 +1591,15 @@ athn_usb_intr(struct usbd_xfer *xfer, vo
                DPRINTF(("intr status=%d\n", status));
                if (status == USBD_STALLED)
                        usbd_clear_endpoint_stall_async(usc->rx_intr_pipe);
+               else if (status == USBD_IOERROR) {
+                       /*
+                        * The device has gone away. If async commands are
+                        * pending or running ensure the device dies ASAP
+                        * and any blocked processes are woken up.
+                        */
+                       if (usc->cmdq.queued > 0)
+                               usbd_deactivate(usc->sc_udev);
+               }
                return;
        }
        usbd_get_xfer_status(xfer, NULL, NULL, &len, NULL);
@@ -1722,6 +1804,8 @@ athn_usb_rxeof(struct usbd_xfer *xfer, v
 {
        struct athn_usb_rx_data *data = priv;
        struct athn_usb_softc *usc = data->sc;
+       struct athn_softc *sc = &usc->sc_sc;
+       struct ifnet *ifp = &sc->sc_ic.ic_if;
        struct athn_usb_rx_stream *stream = &usc->rx_stream;
        uint8_t *buf = data->buf;
        struct ar_stream_hdr *hdr;
@@ -1790,6 +1874,10 @@ athn_usb_rxeof(struct usbd_xfer *xfer, v
                        }
                } else  /* Drop frames larger than MCLBYTES. */
                        m = NULL;
+
+               if (m == NULL)
+                       ifp->if_ierrors++;
+
                /*
                 * NB: m can be NULL, in which case the next pktlen bytes
                 * will be discarded from the Rx stream.
@@ -2048,11 +2136,17 @@ int
 athn_usb_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
 {
        struct athn_softc *sc = ifp->if_softc;
+       struct athn_usb_softc *usc = (struct athn_usb_softc *)sc;
        struct ieee80211com *ic = &sc->sc_ic;
        struct ifaddr *ifa;
        struct ifreq *ifr;
        int s, error = 0;
 
+       if (usbd_is_dying(usc->sc_udev))
+               return ENXIO;
+
+       usbd_ref_incr(usc->sc_udev);
+
        s = splnet();
 
        switch (cmd) {
@@ -2105,6 +2199,9 @@ athn_usb_ioctl(struct ifnet *ifp, u_long
                }
        }
        splx(s);
+
+       usbd_ref_decr(usc->sc_udev);
+
        return (error);
 }
 
@@ -2216,6 +2313,7 @@ athn_usb_init(struct ifnet *ifp)
            &sta, sizeof(sta), NULL);
        if (error != 0)
                goto fail;
+       usc->nnodes++;
 
        /* Update target capabilities. */
        memset(&hic, 0, sizeof(hic));
@@ -2298,6 +2396,7 @@ athn_usb_stop(struct ifnet *ifp)
        sta_index = 0;
        (void)athn_usb_wmi_xcmd(usc, AR_WMI_CMD_NODE_REMOVE,
            &sta_index, sizeof(sta_index), NULL);
+       usc->nnodes--;
 
        (void)athn_usb_wmi_cmd(usc, AR_WMI_CMD_DISABLE_INTR);
        (void)athn_usb_wmi_cmd(usc, AR_WMI_CMD_DRAIN_TXQ_ALL);
Index: if_athn_usb.h
===================================================================
RCS file: /cvs/src/sys/dev/usb/if_athn_usb.h,v
retrieving revision 1.4
diff -u -p -r1.4 if_athn_usb.h
--- if_athn_usb.h       15 Apr 2013 09:23:01 -0000      1.4
+++ if_athn_usb.h       2 Mar 2015 11:10:27 -0000
@@ -438,7 +438,6 @@ struct athn_usb_softc {
        struct ar_wmi_cmd_reg_write     wbuf[AR_MAX_WRITE_COUNT];
        int                             wcount;
 
-       int                             wmi_done;
        uint16_t                        wmi_seq_no;
        uint16_t                        wait_cmd_id;
        uint16_t                        wait_msg_id;
@@ -458,4 +457,7 @@ struct athn_usb_softc {
        uint8_t                         ep_uapsd;
        uint8_t                         ep_mgmt;
        uint8_t                         ep_data[EDCA_NUM_AC];
+
+       /* Firmware cannot handle more than 8 STAs. */
+       uint8_t                         nnodes;
 };

Reply via email to