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;
> };