diff options
Diffstat (limited to 'drivers/net/wireless/at76c50x-usb.c')
| -rw-r--r-- | drivers/net/wireless/at76c50x-usb.c | 183 | 
1 files changed, 149 insertions, 34 deletions
diff --git a/drivers/net/wireless/at76c50x-usb.c b/drivers/net/wireless/at76c50x-usb.c index 34c8a33cac0..d48776e4f34 100644 --- a/drivers/net/wireless/at76c50x-usb.c +++ b/drivers/net/wireless/at76c50x-usb.c @@ -365,15 +365,15 @@ static inline unsigned long at76_get_timeout(struct dfu_status *s)  static int at76_usbdfu_download(struct usb_device *udev, u8 *buf, u32 size,  				int manifest_sync_timeout)  { -	u8 *block; -	struct dfu_status dfu_stat_buf;  	int ret = 0;  	int need_dfu_state = 1;  	int is_done = 0; -	u8 dfu_state = 0;  	u32 dfu_timeout = 0;  	int bsize = 0;  	int blockno = 0; +	struct dfu_status *dfu_stat_buf = NULL; +	u8 *dfu_state = NULL; +	u8 *block = NULL;  	at76_dbg(DBG_DFU, "%s( %p, %u, %d)", __func__, buf, size,  		 manifest_sync_timeout); @@ -383,13 +383,28 @@ static int at76_usbdfu_download(struct usb_device *udev, u8 *buf, u32 size,  		return -EINVAL;  	} +	dfu_stat_buf = kmalloc(sizeof(struct dfu_status), GFP_KERNEL); +	if (!dfu_stat_buf) { +		ret = -ENOMEM; +		goto exit; +	} +  	block = kmalloc(FW_BLOCK_SIZE, GFP_KERNEL); -	if (!block) -		return -ENOMEM; +	if (!block) { +		ret = -ENOMEM; +		goto exit; +	} + +	dfu_state = kmalloc(sizeof(u8), GFP_KERNEL); +	if (!dfu_state) { +		ret = -ENOMEM; +		goto exit; +	} +	*dfu_state = 0;  	do {  		if (need_dfu_state) { -			ret = at76_dfu_get_state(udev, &dfu_state); +			ret = at76_dfu_get_state(udev, dfu_state);  			if (ret < 0) {  				dev_err(&udev->dev,  					"cannot get DFU state: %d\n", ret); @@ -398,13 +413,13 @@ static int at76_usbdfu_download(struct usb_device *udev, u8 *buf, u32 size,  			need_dfu_state = 0;  		} -		switch (dfu_state) { +		switch (*dfu_state) {  		case STATE_DFU_DOWNLOAD_SYNC:  			at76_dbg(DBG_DFU, "STATE_DFU_DOWNLOAD_SYNC"); -			ret = at76_dfu_get_status(udev, &dfu_stat_buf); +			ret = at76_dfu_get_status(udev, dfu_stat_buf);  			if (ret >= 0) { -				dfu_state = dfu_stat_buf.state; -				dfu_timeout = at76_get_timeout(&dfu_stat_buf); +				*dfu_state = dfu_stat_buf->state; +				dfu_timeout = at76_get_timeout(dfu_stat_buf);  				need_dfu_state = 0;  			} else  				dev_err(&udev->dev, @@ -447,12 +462,12 @@ static int at76_usbdfu_download(struct usb_device *udev, u8 *buf, u32 size,  		case STATE_DFU_MANIFEST_SYNC:  			at76_dbg(DBG_DFU, "STATE_DFU_MANIFEST_SYNC"); -			ret = at76_dfu_get_status(udev, &dfu_stat_buf); +			ret = at76_dfu_get_status(udev, dfu_stat_buf);  			if (ret < 0)  				break; -			dfu_state = dfu_stat_buf.state; -			dfu_timeout = at76_get_timeout(&dfu_stat_buf); +			*dfu_state = dfu_stat_buf->state; +			dfu_timeout = at76_get_timeout(dfu_stat_buf);  			need_dfu_state = 0;  			/* override the timeout from the status response, @@ -484,14 +499,17 @@ static int at76_usbdfu_download(struct usb_device *udev, u8 *buf, u32 size,  			break;  		default: -			at76_dbg(DBG_DFU, "DFU UNKNOWN STATE (%d)", dfu_state); +			at76_dbg(DBG_DFU, "DFU UNKNOWN STATE (%d)", *dfu_state);  			ret = -EINVAL;  			break;  		}  	} while (!is_done && (ret >= 0));  exit: +	kfree(dfu_state);  	kfree(block); +	kfree(dfu_stat_buf); +  	if (ret >= 0)  		ret = 0; @@ -1277,6 +1295,7 @@ static int at76_load_external_fw(struct usb_device *udev, struct fwentry *fwe)  			dev_err(&udev->dev,  				"loading %dth firmware block failed: %d\n",  				blockno, ret); +			ret = -EIO;  			goto exit;  		}  		buf += bsize; @@ -1410,6 +1429,8 @@ static int at76_startup_device(struct at76_priv *priv)  	/* remove BSSID from previous run */  	memset(priv->bssid, 0, ETH_ALEN); +	priv->scanning = false; +  	if (at76_set_radio(priv, 1) == 1)  		at76_wait_completion(priv, CMD_RADIO_ON); @@ -1483,6 +1504,52 @@ static void at76_work_submit_rx(struct work_struct *work)  	mutex_unlock(&priv->mtx);  } +/* This is a workaround to make scan working: + * currently mac80211 does not process frames with no frequency + * information. + * However during scan the HW performs a sweep by itself, and we + * are unable to know where the radio is actually tuned. + * This function tries to do its best to guess this information.. + * During scan, If the current frame is a beacon or a probe response, + * the channel information is extracted from it. + * When not scanning, for other frames, or if it happens that for + * whatever reason we fail to parse beacons and probe responses, this + * function returns the priv->channel information, that should be correct + * at least when we are not scanning. + */ +static inline int at76_guess_freq(struct at76_priv *priv) +{ +	size_t el_off; +	const u8 *el; +	int channel = priv->channel; +	int len = priv->rx_skb->len; +	struct ieee80211_hdr *hdr = (void *)priv->rx_skb->data; + +	if (!priv->scanning) +		goto exit; + +	if (len < 24) +		goto exit; + +	if (ieee80211_is_probe_resp(hdr->frame_control)) { +		el_off = offsetof(struct ieee80211_mgmt, u.probe_resp.variable); +		el = ((struct ieee80211_mgmt *)hdr)->u.probe_resp.variable; +	} else if (ieee80211_is_beacon(hdr->frame_control)) { +		el_off = offsetof(struct ieee80211_mgmt, u.beacon.variable); +		el = ((struct ieee80211_mgmt *)hdr)->u.beacon.variable; +	} else { +		goto exit; +	} +	len -= el_off; + +	el = cfg80211_find_ie(WLAN_EID_DS_PARAMS, el, len); +	if (el && el[1] > 0) +		channel = el[2]; + +exit: +	return ieee80211_channel_to_frequency(channel, IEEE80211_BAND_2GHZ); +} +  static void at76_rx_tasklet(unsigned long param)  {  	struct urb *urb = (struct urb *)param; @@ -1523,6 +1590,8 @@ static void at76_rx_tasklet(unsigned long param)  	rx_status.signal = buf->rssi;  	rx_status.flag |= RX_FLAG_DECRYPTED;  	rx_status.flag |= RX_FLAG_IV_STRIPPED; +	rx_status.band = IEEE80211_BAND_2GHZ; +	rx_status.freq = at76_guess_freq(priv);  	at76_dbg(DBG_MAC80211, "calling ieee80211_rx_irqsafe(): %d/%d",  		 priv->rx_skb->len, priv->rx_skb->data_len); @@ -1721,7 +1790,7 @@ static void at76_mac80211_tx(struct ieee80211_hw *hw,  	 * following workaround is necessary. If the TX frame is an  	 * authentication frame extract the bssid and send the CMD_JOIN. */  	if (mgmt->frame_control & cpu_to_le16(IEEE80211_STYPE_AUTH)) { -		if (!ether_addr_equal(priv->bssid, mgmt->bssid)) { +		if (!ether_addr_equal_64bits(priv->bssid, mgmt->bssid)) {  			memcpy(priv->bssid, mgmt->bssid, ETH_ALEN);  			ieee80211_queue_work(hw, &priv->work_join_bssid);  			dev_kfree_skb_any(skb); @@ -1875,6 +1944,8 @@ static void at76_dwork_hw_scan(struct work_struct *work)  	if (is_valid_ether_addr(priv->bssid))  		at76_join(priv); +	priv->scanning = false; +  	mutex_unlock(&priv->mtx);  	ieee80211_scan_completed(priv->hw, false); @@ -1929,6 +2000,7 @@ static int at76_hw_scan(struct ieee80211_hw *hw,  		goto exit;  	} +	priv->scanning = true;  	ieee80211_queue_delayed_work(priv->hw, &priv->dwork_hw_scan,  				     SCAN_POLL_INTERVAL); @@ -2020,6 +2092,44 @@ static void at76_configure_filter(struct ieee80211_hw *hw,  	ieee80211_queue_work(hw, &priv->work_set_promisc);  } +static int at76_set_wep(struct at76_priv *priv) +{ +	int ret = 0; +	struct mib_mac_wep *mib_data = &priv->mib_buf.data.wep_mib; + +	priv->mib_buf.type = MIB_MAC_WEP; +	priv->mib_buf.size = sizeof(struct mib_mac_wep); +	priv->mib_buf.index = 0; + +	memset(mib_data, 0, sizeof(*mib_data)); + +	if (priv->wep_enabled) { +		if (priv->wep_keys_len[priv->wep_key_id] > WEP_SMALL_KEY_LEN) +			mib_data->encryption_level = 2; +		else +			mib_data->encryption_level = 1; + +		/* always exclude unencrypted if WEP is active */ +		mib_data->exclude_unencrypted = 1; +	} else { +		mib_data->exclude_unencrypted = 0; +		mib_data->encryption_level = 0; +	} + +	mib_data->privacy_invoked = priv->wep_enabled; +	mib_data->wep_default_key_id = priv->wep_key_id; +	memcpy(mib_data->wep_default_keyvalue, priv->wep_keys, +	       sizeof(priv->wep_keys)); + +	ret = at76_set_mib(priv, &priv->mib_buf); + +	if (ret < 0) +		wiphy_err(priv->hw->wiphy, +			  "set_mib (wep) failed: %d\n", ret); + +	return ret; +} +  static int at76_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,  			struct ieee80211_vif *vif, struct ieee80211_sta *sta,  			struct ieee80211_key_conf *key) @@ -2062,7 +2172,7 @@ static int at76_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,  			priv->wep_enabled = 1;  	} -	at76_startup_device(priv); +	at76_set_wep(priv);  	mutex_unlock(&priv->mtx); @@ -2112,7 +2222,6 @@ static struct at76_priv *at76_alloc_new_device(struct usb_device *udev)  	priv->pm_period = 0;  	/* unit us */ -	priv->hw->channel_change_time = 100000;  	return priv;  } @@ -2331,16 +2440,22 @@ static int at76_probe(struct usb_interface *interface,  	struct usb_device *udev;  	int op_mode;  	int need_ext_fw = 0; -	struct mib_fw_version fwv; +	struct mib_fw_version *fwv = NULL;  	int board_type = (int)id->driver_info;  	udev = usb_get_dev(interface_to_usbdev(interface)); +	fwv = kmalloc(sizeof(*fwv), GFP_KERNEL); +	if (!fwv) { +		ret = -ENOMEM; +		goto exit; +	} +  	/* Load firmware into kernel memory */  	fwe = at76_load_firmware(udev, board_type);  	if (!fwe) {  		ret = -ENOENT; -		goto error; +		goto exit;  	}  	op_mode = at76_get_op_mode(udev); @@ -2354,7 +2469,7 @@ static int at76_probe(struct usb_interface *interface,  		dev_err(&interface->dev,  			"cannot handle a device in HW_CONFIG_MODE\n");  		ret = -EBUSY; -		goto error; +		goto exit;  	}  	if (op_mode != OPMODE_NORMAL_NIC_WITH_FLASH @@ -2367,10 +2482,10 @@ static int at76_probe(struct usb_interface *interface,  			dev_err(&interface->dev,  				"error %d downloading internal firmware\n",  				ret); -			goto error; +			goto exit;  		}  		usb_put_dev(udev); -		return ret; +		goto exit;  	}  	/* Internal firmware already inside the device.  Get firmware @@ -2383,8 +2498,8 @@ static int at76_probe(struct usb_interface *interface,  	 * query the device for the fw version */  	if ((fwe->fw_version.major > 0 || fwe->fw_version.minor >= 100)  	    || (op_mode == OPMODE_NORMAL_NIC_WITH_FLASH)) { -		ret = at76_get_mib(udev, MIB_FW_VERSION, &fwv, sizeof(fwv)); -		if (ret < 0 || (fwv.major | fwv.minor) == 0) +		ret = at76_get_mib(udev, MIB_FW_VERSION, fwv, sizeof(*fwv)); +		if (ret < 0 || (fwv->major | fwv->minor) == 0)  			need_ext_fw = 1;  	} else  		/* No way to check firmware version, reload to be sure */ @@ -2395,37 +2510,37 @@ static int at76_probe(struct usb_interface *interface,  			   "downloading external firmware\n");  		ret = at76_load_external_fw(udev, fwe); -		if (ret) -			goto error; +		if (ret < 0) +			goto exit;  		/* Re-check firmware version */ -		ret = at76_get_mib(udev, MIB_FW_VERSION, &fwv, sizeof(fwv)); +		ret = at76_get_mib(udev, MIB_FW_VERSION, fwv, sizeof(*fwv));  		if (ret < 0) {  			dev_err(&interface->dev,  				"error %d getting firmware version\n", ret); -			goto error; +			goto exit;  		}  	}  	priv = at76_alloc_new_device(udev);  	if (!priv) {  		ret = -ENOMEM; -		goto error; +		goto exit;  	}  	usb_set_intfdata(interface, priv); -	memcpy(&priv->fw_version, &fwv, sizeof(struct mib_fw_version)); +	memcpy(&priv->fw_version, fwv, sizeof(struct mib_fw_version));  	priv->board_type = board_type;  	ret = at76_init_new_device(priv, interface);  	if (ret < 0)  		at76_delete_device(priv); -	return ret; - -error: -	usb_put_dev(udev); +exit: +	kfree(fwv); +	if (ret < 0) +		usb_put_dev(udev);  	return ret;  }  | 
