Stefan Sperling <s...@stsp.name> writes:

> 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.
Everything continues to work with the new diff. Yesterday (with the
previous diff), I used the dongle for some 12 hours, without any
problems. 
>
> 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