[PATCH] The ioctl wext etc, seems to be broken because they don't take any lock during shutdown. If the user do an echo to the state variabile of the rfkill during the network scanning the system can go in panic. It introduces a reference count to the ar priv data and a hw_available variable and a new spin lock. If a process has a ioctl pending, the shutdown process set the hardware unvailable and wait for pending request.

michael michael at panicking.kicks-ass.org
Wed Mar 18 11:38:36 CET 2009


Signed-off-by: Michael Trimarchi <michael at panicking.kicks-ass.org>
---
 drivers/ar6000/ar6000/ar6000_drv.c   |   24 ++-
 drivers/ar6000/ar6000/ar6000_drv.h   |   18 ++
 drivers/ar6000/ar6000/wireless_ext.c |  492 ++++++++++++++++++++++++++--------
 3 files changed, 413 insertions(+), 121 deletions(-)

diff --git a/drivers/ar6000/ar6000/ar6000_drv.c b/drivers/ar6000/ar6000/ar6000_drv.c
index 8165576..df3a399 100644
--- a/drivers/ar6000/ar6000/ar6000_drv.c
+++ b/drivers/ar6000/ar6000/ar6000_drv.c
@@ -854,6 +854,9 @@ ar6000_avail_ev(HTC_HANDLE HTCHandle)
     }
 
     spin_lock_init(&ar->arLock);
+    spin_lock_init(&ar->arUsed);
+    atomic_set(&ar->refcnt, 0);
+    ar->hw_available = 1;
 
     /* Don't install the init function if BMI is requested */
     if(!bmienable)
@@ -940,7 +943,18 @@ static void
 ar6000_unavail_ev(void *Instance)
 {
     AR_SOFTC_T *ar = (AR_SOFTC_T *)Instance;
-        /* NULL out it's entry in the global list */
+    /* NULL out it's entry in the global list */
+retry:
+    spin_lock(&ar->arUsed);
+    ar->hw_available = 0;
+    BUG_ON(atomic_read(&ar->refcnt) < 0);
+    if (atomic_read(&ar->refcnt) > 0) {
+        cpu_relax();
+        spin_unlock(&ar->arUsed);
+        goto retry;
+    }
+    spin_unlock(&ar->arUsed);
+
     ar6000_devices[ar->arDeviceIndex] = NULL;
     ar6000_destroy(ar->arNetDev, 1);
 }
@@ -1037,7 +1051,7 @@ ar6000_destroy(struct net_device *dev, unsigned int unregister)
         AR_DEBUG_PRINTF(" Host does not want target reset. \n");
     }
 
-       /* Done with cookies */
+    /* Done with cookies */
     ar6000_cookie_cleanup(ar);
 
     /* Cleanup BMI */
@@ -1048,14 +1062,12 @@ ar6000_destroy(struct net_device *dev, unsigned int unregister)
     memset(tx_post, 0, sizeof(tx_post));
     memset(tx_complete, 0, sizeof(tx_complete));
 
-
     /* Free up the device data structure */
     if (unregister)
 	    unregister_netdev(dev);
 
     free_raw_buffers(ar);
-
-#ifndef free_netdev
+#ifndef HAVE_FREE_NETDEV
     kfree(dev);
 #else
     free_netdev(dev);
@@ -2785,7 +2797,7 @@ ar6000_control_tx(void *devt, void *osbuf, WMI_PRI_STREAM_ID streamID)
     struct ar_cookie *cookie = NULL;
     int i;
 
-        /* take lock to protect ar6000_alloc_cookie() */
+    /* take lock to protect ar6000_alloc_cookie() */
     AR6000_SPIN_LOCK(&ar->arLock, 0);
 
     do {
diff --git a/drivers/ar6000/ar6000/ar6000_drv.h b/drivers/ar6000/ar6000/ar6000_drv.h
index d5ff777..9b9d536 100644
--- a/drivers/ar6000/ar6000/ar6000_drv.h
+++ b/drivers/ar6000/ar6000/ar6000_drv.h
@@ -205,6 +205,9 @@ typedef struct ar6_softc {
     HTC_HANDLE              arHtcTarget;
     void                    *arHifDevice;
     spinlock_t              arLock;
+    spinlock_t              arUsed;
+    int                     hw_available;
+    atomic_t                refcnt;
     struct semaphore        arSem;
     int                     arRxBuffers[WMI_PRI_MAX_COUNT];
     int                     arSsidLen;
@@ -313,6 +316,21 @@ struct ar_giwscan_param {
 
 #define AR6000_STAT_INC(ar, stat)       (ar->arNetStats.stat++)
 
+static inline int ar6000_hw_avail(struct ar6_softc* ar)
+{
+	int ret = 0;
+	spin_lock(&ar->arUsed);
+
+	if (!ar->hw_available)
+		ret = -EAGAIN;
+	else
+		atomic_inc(&ar->refcnt);
+
+	spin_unlock(&ar->arUsed);
+
+	return ret;
+}
+
 #define AR6000_SPIN_LOCK(lock, param)   do {                            \
     if (irqs_disabled()) {                                              \
         AR_DEBUG_PRINTF("IRQs disabled:AR6000_LOCK\n");                 \
diff --git a/drivers/ar6000/ar6000/wireless_ext.c b/drivers/ar6000/ar6000/wireless_ext.c
index d9a5920..f8d3281 100644
--- a/drivers/ar6000/ar6000/wireless_ext.c
+++ b/drivers/ar6000/ar6000/wireless_ext.c
@@ -211,13 +211,20 @@ ar6000_ioctl_giwscan(struct net_device *dev,
     AR_SOFTC_T *ar = (AR_SOFTC_T *)netdev_priv(dev);
     struct ar_giwscan_param param;
     int i;
+    int ret = 0;
+
+    ret = ar6000_hw_avail(ar);
+    if (ret)
+        return ret;
 
     if (ar->arWlanState == WLAN_DISABLED) {
-        return -EIO;
+        ret = -EIO;
+        goto out;
     }
 
     if (ar->arWmiReady == FALSE) {
-        return -EIO;
+        ret = -EIO;
+        goto out;
     }
 
     param.current_ev = extra;
@@ -241,12 +248,15 @@ ar6000_ioctl_giwscan(struct net_device *dev,
         param.firstPass = FALSE;
         if (param.current_ev >= param.end_buf) {
             data->length = param.current_ev - extra;
-            return -E2BIG;
+            ret = -E2BIG;
+            goto out;
         }
     }
 
     data->length = param.current_ev - extra;
-    return 0;
+out:
+    atomic_dec(&ar->refcnt);
+    return ret;
 }
 
 extern int reconnect_flag;
@@ -259,13 +269,20 @@ ar6000_ioctl_siwessid(struct net_device *dev,
     AR_SOFTC_T *ar = (AR_SOFTC_T *)netdev_priv(dev);
     A_STATUS status;
     A_UINT8     arNetworkType;
+    int ret = 0;
+
+    ret = ar6000_hw_avail(ar);
+    if (ret)
+        return ret;
 
     if (ar->arWlanState == WLAN_DISABLED) {
-        return -EIO;
+        ret = -EIO;
+        goto out;
     }
 
     if (ar->arWmiReady == FALSE) {
-        return -EIO;
+        ret =  -EIO;
+        goto out;
     }
 
     /*
@@ -277,7 +294,8 @@ ar6000_ioctl_siwessid(struct net_device *dev,
         /*
          * ssid is invalid
          */
-        return -EINVAL;
+        ret = -EINVAL;
+        goto out;
     }
     /* Added for bug 25178, return an IOCTL error instead of target returning
        Illegal parameter error when either the BSSID or channel is missing
@@ -289,12 +307,14 @@ ar6000_ioctl_siwessid(struct net_device *dev,
              (!ar->arReqBssid[0] && !ar->arReqBssid[1] && !ar->arReqBssid[2] &&
               !ar->arReqBssid[3] && !ar->arReqBssid[4] && !ar->arReqBssid[5])))
         {
-            return -EINVAL;
+            ret = -EINVAL;
+            goto out;
         }
     }
 
     if (down_interruptible(&ar->arSem)) {
-        return -ERESTARTSYS;
+        ret = -ERESTARTSYS;
+        goto out;
     }
 
     if (ar->arTxPending[WMI_CONTROL_PRI]) {
@@ -304,7 +324,8 @@ ar6000_ioctl_siwessid(struct net_device *dev,
         wait_event_interruptible_timeout(arEvent,
             ar->arTxPending[WMI_CONTROL_PRI] == 0, wmitimeout * HZ);
         if (signal_pending(current)) {
-            return -EINTR;
+            ret = -EINTR;
+            goto out;
         }
     }
 
@@ -350,7 +371,7 @@ ar6000_ioctl_siwessid(struct net_device *dev,
                 }
                 if (!data->flags) {
                     up(&ar->arSem);
-                    return 0;
+                    goto out;
                 }
             } else {
                  up(&ar->arSem);
@@ -371,9 +392,10 @@ ar6000_ioctl_siwessid(struct net_device *dev,
                                            ar->arChannelHint);
                 up(&ar->arSem);
                 if (status != A_OK) {
-                    return -EIO;
+                    ret = -EIO;
+                    goto out;
                 }
-                return 0;
+                goto out;
             }
             else{
                 /*
@@ -381,7 +403,7 @@ ar6000_ioctl_siwessid(struct net_device *dev,
                  */
                 if(!(ar->arConnectPending)) {
                     up(&ar->arSem);
-                    return 0;
+                    goto out;
                 }
             }
         }
@@ -419,13 +441,16 @@ ar6000_ioctl_siwessid(struct net_device *dev,
         up(&ar->arSem);
 
         if (status != A_OK) {
-            return -EIO;
+            ret = -EIO;
+            goto out;
         }
         ar->arConnectPending = TRUE;
     }else{
       up(&ar->arSem);
     }
-    return 0;
+out:
+    atomic_dec(&ar->refcnt);
+    return ret;
 }
 
 /* SIOCGIWESSID */
@@ -435,16 +460,23 @@ ar6000_ioctl_giwessid(struct net_device *dev,
                      struct iw_point *data, char *essid)
 {
     AR_SOFTC_T *ar = (AR_SOFTC_T *)netdev_priv(dev);
+    int ret = 0;
+
+    ret = ar6000_hw_avail(ar);
+    if (ret)
+        return ret;
 
     if (ar->arWlanState == WLAN_DISABLED) {
-        return -EIO;
+        ret = -EIO;
+        goto out;
     }
 
     data->flags = 1;
     data->length = ar->arSsidLen;
     A_MEMCPY(essid, ar->arSsid, ar->arSsidLen);
-
-    return 0;
+out:
+    atomic_dec(&ar->refcnt);
+    return ret;
 }
 
 
@@ -484,9 +516,16 @@ ar6000_ioctl_setmlme(struct net_device *dev, struct iw_request_info *info,
 {
     AR_SOFTC_T *ar = (AR_SOFTC_T *)netdev_priv(dev);
     struct ieee80211req_mlme *mlme = (struct ieee80211req_mlme *)extra;
+    int ret = 0;
 
-    if ((ar->arWmiReady == FALSE) || (ar->arConnected != TRUE))
-		return -EIO;
+    ret = ar6000_hw_avail(ar);
+    if (ret)
+        return ret;
+
+    if ((ar->arWmiReady == FALSE) || (ar->arConnected != TRUE)) {
+        ret = -EIO;
+        goto out;
+    }
 
     switch (mlme->im_op) {
         case IEEE80211_MLME_DISASSOC:
@@ -496,7 +535,9 @@ ar6000_ioctl_setmlme(struct net_device *dev, struct iw_request_info *info,
         default:
             break;
     }
-    return 0;
+out:
+    atomic_dec(&ar->refcnt);
+    return ret;
 }
 
 
@@ -529,6 +570,9 @@ ar6000_ioctl_setauthalg(struct net_device *dev, struct iw_request_info *info,
     struct ieee80211req_authalg *req = (struct ieee80211req_authalg *)extra;
     int ret = 0;
 
+    ret = ar6000_hw_avail(ar);
+    if (ret)
+        return ret;
 
     AR6000_SPIN_LOCK(&ar->arLock, 0);
 
@@ -544,6 +588,7 @@ ar6000_ioctl_setauthalg(struct net_device *dev, struct iw_request_info *info,
 
     AR6000_SPIN_UNLOCK(&ar->arLock, 0);
 
+    atomic_dec(&ar->refcnt);
     return ret;
 }
 static int
@@ -553,9 +598,15 @@ ar6000_ioctl_addpmkid(struct net_device *dev, struct iw_request_info *info,
     AR_SOFTC_T *ar = (AR_SOFTC_T *)netdev_priv(dev);
     struct ieee80211req_addpmkid  *req = (struct ieee80211req_addpmkid *)extra;
     A_STATUS status;
+    int ret = 0;
+
+    ret = ar6000_hw_avail(ar);
+    if (ret)
+        return ret;
 
     if (ar->arWlanState == WLAN_DISABLED) {
-        return -EIO;
+        ret = -EIO;
+        goto out;
     }
 
     AR_DEBUG_PRINTF("Add pmkid for %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x en=%d\n",
@@ -567,10 +618,12 @@ ar6000_ioctl_addpmkid(struct net_device *dev, struct iw_request_info *info,
                               req->pi_enable);
 
     if (status != A_OK) {
-        return -EIO;
+        ret = -EIO;
+        goto out;
     }
-
-    return 0;
+out:
+    atomic_dec(&ar->refcnt);
+    return ret;
 }
 
 /*
@@ -583,6 +636,11 @@ ar6000_ioctl_siwrate(struct net_device *dev,
 {
     AR_SOFTC_T *ar = (AR_SOFTC_T *)netdev_priv(dev);
     A_UINT32  kbps;
+    int ret = 0;
+
+    ret = ar6000_hw_avail(ar);
+    if (ret)
+        return ret;
 
     if (rrq->fixed) {
         kbps = rrq->value / 1000;           /* rrq->value is in bps */
@@ -592,16 +650,19 @@ ar6000_ioctl_siwrate(struct net_device *dev,
     if(kbps != -1 && wmi_validate_bitrate(ar->arWmi, kbps) == A_EINVAL)
     {
         AR_DEBUG_PRINTF("BitRate is not Valid %d\n", kbps);
-        return -EINVAL;
+        ret = -EINVAL;
+        goto out;
     }
     ar->arBitRate = kbps;
     if(ar->arWmiReady == TRUE)
     {
         if (wmi_set_bitrate_cmd(ar->arWmi, kbps) != A_OK) {
-            return -EINVAL;
+            ret = -EINVAL;
         }
     }
-    return 0;
+out:
+    atomic_dec(&ar->refcnt);
+    return ret;
 }
 
 /*
@@ -615,15 +676,21 @@ ar6000_ioctl_giwrate(struct net_device *dev,
     AR_SOFTC_T *ar = (AR_SOFTC_T *)netdev_priv(dev);
     int ret = 0;
 
+    ret = ar6000_hw_avail(ar);
+    if (ret)
+        return ret;
+
     if (down_interruptible(&ar->arSem)) {
-        return -ERESTARTSYS;
+        ret = -ERESTARTSYS;
+        goto out;
     }
     if(ar->arWmiReady == TRUE)
     {
         ar->arBitRate = 0xFFFF;
         if (wmi_get_bitrate_cmd(ar->arWmi) != A_OK) {
             up(&ar->arSem);
-            return -EIO;
+            ret = -EIO;
+            goto out;
         }
         wait_event_interruptible_timeout(arEvent, ar->arBitRate != 0xFFFF, wmitimeout * HZ);
         if (signal_pending(current)) {
@@ -643,6 +710,8 @@ ar6000_ioctl_giwrate(struct net_device *dev,
 
     up(&ar->arSem);
 
+out:
+    atomic_dec(&ar->refcnt);
     return ret;
 }
 
@@ -656,26 +725,37 @@ ar6000_ioctl_siwtxpow(struct net_device *dev,
 {
     AR_SOFTC_T *ar = (AR_SOFTC_T *)netdev_priv(dev);
     A_UINT8 dbM;
+    int ret = 0;
+
+    ret = ar6000_hw_avail(ar);
+    if (ret)
+        return ret;
 
     if (ar->arWlanState == WLAN_DISABLED) {
-        return -EIO;
+        ret = -EIO;
+        goto out;
     }
 
     if (ar->arRadioSwitch == WLAN_ENABLED
 	&& rrq->disabled) {
-	    if (wmi_switch_radio(ar->arWmi, WLAN_DISABLED) < 0)
-		    return -EIO;
+	    if (wmi_switch_radio(ar->arWmi, WLAN_DISABLED) < 0) {
+		err = -EIO;
+		goto out;
+            }
 	    ar->arRadioSwitch = WLAN_DISABLED;
     } else if (ar->arRadioSwitch == WLAN_DISABLED
 	       && !rrq->disabled) {
-	    if (wmi_switch_radio(ar->arWmi, WLAN_ENABLED) < 0)
-		    return -EIO;
+	    if (wmi_switch_radio(ar->arWmi, WLAN_ENABLED) < 0) {
+		err = -EIO;
+		goto out;
+            }
 	    ar->arRadioSwitch = WLAN_ENABLED;
     }
 
     if (rrq->fixed) {
         if (rrq->flags != IW_TXPOW_DBM) {
-            return -EOPNOTSUPP;
+            ret = -EOPNOTSUPP;
+            goto out;
         }
         ar->arTxPwr= dbM = rrq->value;
         ar->arTxPwrSet = TRUE;
@@ -688,7 +768,9 @@ ar6000_ioctl_siwtxpow(struct net_device *dev,
         AR_DEBUG_PRINTF("Set tx pwr cmd %d dbM\n", dbM);
         wmi_set_txPwr_cmd(ar->arWmi, dbM);
     }
-    return 0;
+out:
+    atomic_dec(&ar->refcnt);
+    return ret;
 }
 
 /*
@@ -702,17 +784,24 @@ ar6000_ioctl_giwtxpow(struct net_device *dev,
     AR_SOFTC_T *ar = (AR_SOFTC_T *)netdev_priv(dev);
     int ret = 0;
 
+    ret = ar6000_hw_avail(ar);
+    if (ret)
+        return ret;
+
     if (ar->arWlanState == WLAN_DISABLED) {
-        return -EIO;
+        ret = -EIO;
+        goto out;
     }
 
     if (ar->arRadioSwitch == WLAN_DISABLED) {
 	    rrq->disabled = 1;
-	    return 0;
+	    ret = 0;
+            goto out;
     }
 
     if (down_interruptible(&ar->arSem)) {
-        return -ERESTARTSYS;
+        ret = -ERESTARTSYS;
+        goto out;
     }
     if((ar->arWmiReady == TRUE) && (ar->arConnected == TRUE))
     {
@@ -720,7 +809,8 @@ ar6000_ioctl_giwtxpow(struct net_device *dev,
 
         if (wmi_get_txPwr_cmd(ar->arWmi) != A_OK) {
             up(&ar->arSem);
-            return -EIO;
+            ret = -EIO;
+            goto out;
         }
 
         wait_event_interruptible_timeout(arEvent, ar->arTxPwr != 0, wmitimeout * HZ);
@@ -742,6 +832,8 @@ ar6000_ioctl_giwtxpow(struct net_device *dev,
 
     up(&ar->arSem);
 
+out:
+    atomic_dec(&ar->refcnt);
     return ret;
 }
 
@@ -756,31 +848,43 @@ ar6000_ioctl_siwretry(struct net_device *dev,
              struct iw_param *rrq, char *extra)
 {
     AR_SOFTC_T *ar = (AR_SOFTC_T *)netdev_priv(dev);
+    int ret = 0;
+
+    ret = ar6000_hw_avail(ar);
+    if (ret)
+        return ret;
 
     if (ar->arWlanState == WLAN_DISABLED) {
-        return -EIO;
+        ret = -EIO;
+        goto out;
     }
 
     if (rrq->disabled) {
-        return -EOPNOTSUPP;
+        ret = -EOPNOTSUPP;
+        goto out;
     }
 
     if ((rrq->flags & IW_RETRY_TYPE) != IW_RETRY_LIMIT) {
-        return -EOPNOTSUPP;
+        ret = -EOPNOTSUPP;
+        goto out;
     }
 
     if ( !(rrq->value >= WMI_MIN_RETRIES) || !(rrq->value <= WMI_MAX_RETRIES)) {
-            return - EINVAL;
+        ret = - EINVAL;
+        goto out;
     }
     if(ar->arWmiReady == TRUE)
     {
         if (wmi_set_retry_limits_cmd(ar->arWmi, DATA_FRAMETYPE, WMM_AC_BE,
                                      rrq->value, 0) != A_OK){
-            return -EINVAL;
+            ret = -EINVAL;
+            goto out;
         }
     }
     ar->arMaxRetries = rrq->value;
-    return 0;
+out:
+    atomic_dec(&ar->refcnt);
+    return ret;
 }
 
 /*
@@ -792,15 +896,22 @@ ar6000_ioctl_giwretry(struct net_device *dev,
              struct iw_param *rrq, char *extra)
 {
     AR_SOFTC_T *ar = (AR_SOFTC_T *)netdev_priv(dev);
+    int ret = 0;
+
+    ret = ar6000_hw_avail(ar);
+    if (ret)
+        return ret;
 
     if (ar->arWlanState == WLAN_DISABLED) {
-        return -EIO;
+        ret = -EIO;
+        goto out;
     }
 
     rrq->disabled = 0;
     switch (rrq->flags & IW_RETRY_TYPE) {
     case IW_RETRY_LIFETIME:
-        return -EOPNOTSUPP;
+        ret = -EOPNOTSUPP;
+        goto out;
         break;
     case IW_RETRY_LIMIT:
         rrq->flags = IW_RETRY_LIMIT;
@@ -816,7 +927,9 @@ ar6000_ioctl_giwretry(struct net_device *dev,
         }
         break;
     }
-    return 0;
+out:
+    atomic_dec(&ar->refcnt);
+    return ret;
 }
 
 /*
@@ -830,9 +943,15 @@ ar6000_ioctl_siwencode(struct net_device *dev,
     AR_SOFTC_T *ar = (AR_SOFTC_T *)netdev_priv(dev);
     int index;
     A_INT32 auth = ar->arDot11AuthMode;
+    int ret = 0;
+
+    ret = ar6000_hw_avail(ar);
+    if (ret)
+        return ret;
 
     if (ar->arWlanState == WLAN_DISABLED) {
-        return -EIO;
+        ret = -EIO;
+        goto out;
     }
 
     index = erq->flags & IW_ENCODE_INDEX;
@@ -840,7 +959,8 @@ ar6000_ioctl_siwencode(struct net_device *dev,
     if (index && (((index - 1) < WMI_MIN_KEY_INDEX) ||
                   ((index - 1) > WMI_MAX_KEY_INDEX)))
     {
-        return -EIO;
+        ret = -EIO;
+        goto out;
     }
 
     if (erq->flags & IW_ENCODE_DISABLED) {
@@ -876,7 +996,8 @@ ar6000_ioctl_siwencode(struct net_device *dev,
 
         if (erq->length) {
             if (!IEEE80211_IS_VALID_WEP_CIPHER_LEN(erq->length)) {
-                return -EIO;
+                ret = -EIO;
+                goto out;
             }
 
             A_MEMZERO(ar->arWepKeyList[index].arKey,
@@ -885,7 +1006,8 @@ ar6000_ioctl_siwencode(struct net_device *dev,
             ar->arWepKeyList[index].arKeyLen = erq->length;
         } else {
             if (ar->arWepKeyList[index].arKeyLen == 0) {
-                return -EIO;
+                ret = -EIO;
+                goto out;
             }
             ar->arDefTxKeyIndex = index;
         }
@@ -901,8 +1023,9 @@ ar6000_ioctl_siwencode(struct net_device *dev,
      */
     A_MEMZERO(ar->arSsid, sizeof(ar->arSsid));
     ar->arSsidLen = 0;
-
-    return 0;
+out:
+    atomic_dec(&ar->refcnt);
+    return ret;
 }
 
 static int
@@ -913,9 +1036,15 @@ ar6000_ioctl_giwencode(struct net_device *dev,
     AR_SOFTC_T *ar = (AR_SOFTC_T *)netdev_priv(dev);
     A_UINT8 keyIndex;
     struct ar_wep_key *wk;
+    int ret = 0;
+
+    ret = ar6000_hw_avail(ar);
+    if (ret)
+        return ret;
 
     if (ar->arWlanState == WLAN_DISABLED) {
-        return -EIO;
+        ret = -EIO;
+        goto out;
     }
 
     if (ar->arPairwiseCrypto == NONE_CRYPT) {
@@ -949,34 +1078,50 @@ ar6000_ioctl_giwencode(struct net_device *dev,
         }
     }
 
-    return 0;
+out:
+    atomic_dec(&ar->refcnt);
+    return ret;
 }
 
 static int ar6000_ioctl_siwpower(struct net_device *dev,
 				 struct iw_request_info *info,
 				 union iwreq_data *wrqu, char *extra)
 {
-	AR_SOFTC_T *ar = (AR_SOFTC_T *)netdev_priv(dev);
-	WMI_POWER_MODE power_mode;
+    AR_SOFTC_T *ar = (AR_SOFTC_T *)netdev_priv(dev);
+    WMI_POWER_MODE power_mode;
+    int ret = 0;
 
-	if (wrqu->power.disabled)
-		power_mode = MAX_PERF_POWER;
-	else
-		power_mode = REC_POWER;
+    ret = ar6000_hw_avail(ar);
+    if (ret)
+        return ret;
 
-	if (wmi_powermode_cmd(ar->arWmi, power_mode) < 0)
-		return -EIO;
+    if (wrqu->power.disabled)
+        power_mode = MAX_PERF_POWER;
+    else
+        power_mode = REC_POWER;
 
-	return 0;
+    if (wmi_powermode_cmd(ar->arWmi, power_mode) < 0)
+        ret = -EIO;
+
+    atomic_dec(&ar->refcnt);
+    return ret;
 }
 
 static int ar6000_ioctl_giwpower(struct net_device *dev,
 				 struct iw_request_info *info,
 				 union iwreq_data *wrqu, char *extra)
 {
-	AR_SOFTC_T *ar = (AR_SOFTC_T *)netdev_priv(dev);
+    AR_SOFTC_T *ar = (AR_SOFTC_T *)netdev_priv(dev);
+    int ret = 0;
+
+    ret = ar6000_hw_avail(ar);
+    if (ret)
+        return ret;
 
-	return wmi_get_power_mode_cmd(ar->arWmi);
+    ret = wmi_get_power_mode_cmd(ar->arWmi);
+
+    atomic_dec(&ar->refcnt);
+    return ret;
 }
 
 static int ar6000_ioctl_siwgenie(struct net_device *dev,
@@ -1003,6 +1148,11 @@ static int ar6000_ioctl_siwauth(struct net_device *dev,
 {
 	AR_SOFTC_T *ar = (AR_SOFTC_T *)netdev_priv(dev);
 	int reset = 0;
+	int ret = 0;
+
+	ret = ar6000_hw_avail(ar);
+	if (ret)
+		return ret;
 
 	switch (param->flags & IW_AUTH_INDEX) {
 	case IW_AUTH_WPA_VERSION:
@@ -1064,7 +1214,8 @@ static int ar6000_ioctl_siwauth(struct net_device *dev,
 
 	case IW_AUTH_TKIP_COUNTERMEASURES:
 		if (ar->arWmiReady == FALSE) {
-			return -EIO;
+			ret = -EIO;
+			goto out;
 		}
 		wmi_set_tkip_countermeasures_cmd(ar->arWmi, param->value);
 		break;
@@ -1100,7 +1251,7 @@ static int ar6000_ioctl_siwauth(struct net_device *dev,
 
 	default:
 		printk("%s(): Unknown flag 0x%x\n", __FUNCTION__, param->flags);
-		return -EOPNOTSUPP;
+		ret = -EOPNOTSUPP;
 	}
 
 	if (reset) {
@@ -1108,7 +1259,9 @@ static int ar6000_ioctl_siwauth(struct net_device *dev,
 		ar->arSsidLen = 0;
 	}
 
-	return 0;
+out:
+	atomic_dec(&ar->refcnt);
+	return ret;
 }
 
 static int ar6000_ioctl_giwauth(struct net_device *dev,
@@ -1128,16 +1281,24 @@ static int ar6000_ioctl_siwencodeext(struct net_device *dev,
 	struct iw_point *encoding = &wrqu->encoding;
 	struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
 	int alg = ext->alg, idx;
+	int ret = 0;
+
+	ret = ar6000_hw_avail(ar);
+	if (ret)
+		return ret;
 
 	if (ar->arWlanState == WLAN_DISABLED) {
-		return -EIO;
+		ret = -EIO;
+		goto out;
 	}
 
 	/* Determine and validate the key index */
 	idx = (encoding->flags & IW_ENCODE_INDEX) - 1;
 	if (idx) {
-		if (idx < 0 || idx > 3)
-			return -EINVAL;
+		if (idx < 0 || idx > 3) {
+			ret = -EINVAL;
+			goto out;
+		}
 	}
 
 	if ((alg == IW_ENCODE_ALG_TKIP) || (alg == IW_ENCODE_ALG_CCMP)) {
@@ -1189,19 +1350,22 @@ static int ar6000_ioctl_siwencodeext(struct net_device *dev,
 					ik.ik_keydata,
 					KEY_OP_INIT_VAL, SYNC_BEFORE_WMIFLAG);
 
-		if (status < 0)
-			return -EIO;
+		if (status < 0) {
+			ret = -EIO;
+			goto out;
+		}
 
 		ar->user_saved_keys.keyOk = TRUE;
-
-		return 0;
+		goto out;
 
 	} else {
 		/* WEP falls back to SIWENCODE */
-		return -EOPNOTSUPP;
+		ret = -EOPNOTSUPP;
 	}
 
-	return 0;
+out:
+	atomic_dec(&ar->refcnt);
+	return ret;
 }
 
 
@@ -1226,8 +1390,13 @@ ar6000_ioctl_setparam(struct net_device *dev,
     int ret = 0;
     A_BOOL profChanged = FALSE;
 
+    ret = ar6000_hw_avail(ar);
+    if (ret)
+        return ret;
+
     if (ar->arWlanState == WLAN_DISABLED) {
-        return -EIO;
+        ret = -EIO;
+        goto out;
     }
 
     switch (param) {
@@ -1332,7 +1501,8 @@ ar6000_ioctl_setparam(struct net_device *dev,
             break;
         case IEEE80211_PARAM_COUNTERMEASURES:
             if (ar->arWmiReady == FALSE) {
-                return -EIO;
+                ret = -EIO;
+                goto out;
             }
             wmi_set_tkip_countermeasures_cmd(ar->arWmi, value);
             break;
@@ -1348,6 +1518,8 @@ ar6000_ioctl_setparam(struct net_device *dev,
 	ar->arSsidLen = 0;
     }
 
+out:
+    atomic_dec(&ar->refcnt);
     return ret;
 }
 
@@ -1367,9 +1539,15 @@ ar6000_ioctl_setkey(struct net_device *dev, struct iw_request_info *info,
     KEY_USAGE keyUsage;
     A_STATUS status;
     CRYPTO_TYPE keyType = NONE_CRYPT;
+    int ret = 0;
+
+    ret = ar6000_hw_avail(ar);
+    if (ret)
+        return ret;
 
     if (ar->arWlanState == WLAN_DISABLED) {
-        return -EIO;
+        ret = -EIO;
+        goto out;
     }
 
     ar->user_saved_keys.keyOk = FALSE;
@@ -1402,7 +1580,8 @@ ar6000_ioctl_setkey(struct net_device *dev, struct iw_request_info *info,
 
     if (IEEE80211_CIPHER_CCKM_KRK != ik->ik_type) {
         if (NONE_CRYPT == keyType) {
-            return -EIO;
+            ret = -EIO;
+            goto out;
         }
 
         status = wmi_addKey_cmd(ar->arWmi, ik->ik_keyix, keyType, keyUsage,
@@ -1411,7 +1590,8 @@ ar6000_ioctl_setkey(struct net_device *dev, struct iw_request_info *info,
                                 SYNC_BEFORE_WMIFLAG);
 
         if (status != A_OK) {
-            return -EIO;
+            ret = -EIO;
+            goto out;
         }
     } else {
         status = wmi_add_krk_cmd(ar->arWmi, ik->ik_keydata);
@@ -1419,7 +1599,9 @@ ar6000_ioctl_setkey(struct net_device *dev, struct iw_request_info *info,
 
     ar->user_saved_keys.keyOk = TRUE;
 
-    return 0;
+out:
+    atomic_dec(&ar->refcnt);
+    return ret;
 }
 
 
@@ -1432,9 +1614,15 @@ ar6000_ioctl_giwname(struct net_device *dev,
            char *name, char *extra)
 {
     AR_SOFTC_T *ar = (AR_SOFTC_T *)netdev_priv(dev);
+    int ret = 0;
+
+    ret = ar6000_hw_avail(ar);
+    if (ret)
+        return ret;
 
     if (ar->arWlanState == WLAN_DISABLED) {
-        return -EIO;
+        ret = -EIO;
+        goto out;
     }
 
     switch (ar->arPhyCapability) {
@@ -1452,7 +1640,9 @@ ar6000_ioctl_giwname(struct net_device *dev,
         break;
     }
 
-    return 0;
+out:
+    atomic_dec(&ar->refcnt);
+    return ret;
 }
 
 /*
@@ -1464,9 +1654,15 @@ ar6000_ioctl_siwfreq(struct net_device *dev,
             struct iw_freq *freq, char *extra)
 {
     AR_SOFTC_T *ar = (AR_SOFTC_T *)netdev_priv(dev);
+    int ret = 0;
+
+    ret = ar6000_hw_avail(ar);
+    if (ret)
+        return ret;
 
     if (ar->arWlanState == WLAN_DISABLED) {
-        return -EIO;
+        ret = -EIO;
+        goto out;
     }
 
     /*
@@ -1477,7 +1673,8 @@ ar6000_ioctl_siwfreq(struct net_device *dev,
      * channel.
      */
     if (freq->e > 1) {
-        return -EINVAL;
+        ret = -EINVAL;
+        goto out;
     } else if (freq->e == 1) {
         ar->arChannelHint = freq->m / 100000;
     } else {
@@ -1485,7 +1682,9 @@ ar6000_ioctl_siwfreq(struct net_device *dev,
     }
 
     A_PRINTF("channel hint set to %d\n", ar->arChannelHint);
-    return 0;
+out:
+    atomic_dec(&ar->refcnt);
+    return ret;
 }
 
 /*
@@ -1497,19 +1696,28 @@ ar6000_ioctl_giwfreq(struct net_device *dev,
                 struct iw_freq *freq, char *extra)
 {
     AR_SOFTC_T *ar = (AR_SOFTC_T *)netdev_priv(dev);
+    int ret = 0;
+
+    ret = ar6000_hw_avail(ar);
+    if (ret)
+        return ret;
 
     if (ar->arWlanState == WLAN_DISABLED) {
-        return -EIO;
+        ret = -EIO;
+        goto out;
     }
 
     if (ar->arConnected != TRUE) {
-        return -EINVAL;
+        ret = -EINVAL;
+        goto out;
     }
 
     freq->m = ar->arBssChannel * 100000;
     freq->e = 1;
 
-    return 0;
+out:
+    atomic_dec(&ar->refcnt);
+    return ret;
 }
 
 /*
@@ -1521,9 +1729,15 @@ ar6000_ioctl_siwmode(struct net_device *dev,
             __u32 *mode, char *extra)
 {
     AR_SOFTC_T *ar = (AR_SOFTC_T *)netdev_priv(dev);
+    int ret = 0;
+
+    ret = ar6000_hw_avail(ar);
+    if (ret)
+        return ret;
 
     if (ar->arWlanState == WLAN_DISABLED) {
-        return -EIO;
+        ret = -EIO;
+        goto out;
     }
 
     switch (*mode) {
@@ -1534,10 +1748,12 @@ ar6000_ioctl_siwmode(struct net_device *dev,
         ar->arNetworkType = ADHOC_NETWORK;
         break;
     default:
-        return -EINVAL;
+        ret = -EINVAL;
     }
 
-    return 0;
+out:
+    atomic_dec(&ar->refcnt);
+    return ret;
 }
 
 /*
@@ -1549,9 +1765,15 @@ ar6000_ioctl_giwmode(struct net_device *dev,
             __u32 *mode, char *extra)
 {
     AR_SOFTC_T *ar = (AR_SOFTC_T *)netdev_priv(dev);
+    int ret = 0;
+
+    ret = ar6000_hw_avail(ar);
+    if (ret)
+        return ret;
 
     if (ar->arWlanState == WLAN_DISABLED) {
-        return -EIO;
+        ret = -EIO;
+        goto out;
     }
 
     switch (ar->arNetworkType) {
@@ -1562,9 +1784,11 @@ ar6000_ioctl_giwmode(struct net_device *dev,
         *mode = IW_MODE_ADHOC;
         break;
     default:
-        return -EIO;
+        ret = -EIO;
     }
-    return 0;
+out:
+    atomic_dec(&ar->refcnt);
+    return ret;
 }
 
 /*
@@ -1604,30 +1828,40 @@ ar6000_ioctl_giwrange(struct net_device *dev,
     struct iw_range *range = (struct iw_range *) extra;
     int i, ret = 0;
 
+    ret = ar6000_hw_avail(ar);
+    if (ret)
+        return ret;
+
     if (ar->arWmiReady == FALSE) {
-        return -EIO;
+        ret = -EIO;
+        goto out;
     }
 
     if (ar->arWlanState == WLAN_DISABLED) {
-        return -EIO;
+        ret = -EIO;
+        goto out;
     }
 
     if (down_interruptible(&ar->arSem)) {
-        return -ERESTARTSYS;
+        ret = -ERESTARTSYS;
+        goto out;
     }
+
     ar->arNumChannels = -1;
     A_MEMZERO(ar->arChannelList, sizeof (ar->arChannelList));
 
     if (wmi_get_channelList_cmd(ar->arWmi) != A_OK) {
         up(&ar->arSem);
-        return -EIO;
+        ret = -EIO;
+        goto out;
     }
 
     wait_event_interruptible_timeout(arEvent, ar->arNumChannels != -1, wmitimeout * HZ);
 
     if (signal_pending(current)) {
         up(&ar->arSem);
-        return -EINTR;
+        ret = -EINTR;
+        goto out;
     }
 
     data->length = sizeof(struct iw_range);
@@ -1699,6 +1933,8 @@ ar6000_ioctl_giwrange(struct net_device *dev,
 
     up(&ar->arSem);
 
+out:
+    atomic_dec(&ar->refcnt);
     return ret;
 }
 
@@ -1713,13 +1949,20 @@ ar6000_ioctl_siwap(struct net_device *dev,
               struct sockaddr *ap_addr, char *extra)
 {
     AR_SOFTC_T *ar = (AR_SOFTC_T *)netdev_priv(dev);
+    int ret = 0;
+
+    ret = ar6000_hw_avail(ar);
+    if (ret)
+        return ret;
 
     if (ar->arWlanState == WLAN_DISABLED) {
-        return -EIO;
+        ret = -EIO;
+        goto out;
     }
 
     if (ap_addr->sa_family != ARPHRD_ETHER) {
-        return -EIO;
+        ret = -EIO;
+        goto out;
     }
 
     if (A_MEMCMP(&ap_addr->sa_data, bcast_mac, AR6000_ETH_ADDR_LEN) == 0) {
@@ -1728,7 +1971,9 @@ ar6000_ioctl_siwap(struct net_device *dev,
         A_MEMCPY(ar->arReqBssid, &ap_addr->sa_data,  sizeof(ar->arReqBssid));
     }
 
-    return 0;
+out:
+    atomic_dec(&ar->refcnt);
+    return ret;
 }
 
 /*
@@ -1740,19 +1985,28 @@ ar6000_ioctl_giwap(struct net_device *dev,
               struct sockaddr *ap_addr, char *extra)
 {
     AR_SOFTC_T *ar = (AR_SOFTC_T *)netdev_priv(dev);
+    int ret = 0;
+
+    ret = ar6000_hw_avail(ar);
+    if (ret)
+        return ret;
 
     if (ar->arWlanState == WLAN_DISABLED) {
-        return -EIO;
+        ret = -EIO;
+        goto out;
     }
 
     if (ar->arConnected != TRUE) {
-        return -EINVAL;
+        ret = -EINVAL;
+        goto out;
     }
 
     A_MEMCPY(&ap_addr->sa_data, ar->arBssid, sizeof(ar->arBssid));
     ap_addr->sa_family = ARPHRD_ETHER;
 
-    return 0;
+out:
+    atomic_dec(&ar->refcnt);
+    return ret;
 }
 
 /*
@@ -1780,12 +2034,18 @@ ar6000_ioctl_siwscan(struct net_device *dev,
     AR_SOFTC_T *ar = (AR_SOFTC_T *)netdev_priv(dev);
     int ret = 0;
 
+    ret = ar6000_hw_avail(ar);
+    if (ret)
+        return ret;
+
     if (ar->arWmiReady == FALSE) {
-        return -EIO;
+        ret = -EIO;
+        goto out;
     }
 
     if (ar->arWlanState == WLAN_DISABLED) {
-        return -EIO;
+        ret = -EIO;
+        goto out;
     }
 
     /* We ask for everything from the target */
@@ -1808,6 +2068,8 @@ ar6000_ioctl_siwscan(struct net_device *dev,
 	    ret = -EIO;
     }
 
+out:
+    atomic_dec(&ar->refcnt);
     return ret;
 #undef  ACT_DWELLTIME_DEFAULT
 #undef HOME_TXDRAIN_TIME
-- 
1.5.6.5




More information about the openmoko-kernel mailing list