aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/p54
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/p54')
-rw-r--r--drivers/net/wireless/p54/p54.h74
-rw-r--r--drivers/net/wireless/p54/p54common.c348
-rw-r--r--drivers/net/wireless/p54/p54spi.c175
-rw-r--r--drivers/net/wireless/p54/p54usb.c314
-rw-r--r--drivers/net/wireless/p54/p54usb.h16
5 files changed, 518 insertions, 409 deletions
diff --git a/drivers/net/wireless/p54/p54.h b/drivers/net/wireless/p54/p54.h
index ecf8b6ed5a47..db3df947d8ed 100644
--- a/drivers/net/wireless/p54/p54.h
+++ b/drivers/net/wireless/p54/p54.h
@@ -125,6 +125,7 @@ struct p54_led_dev {
struct led_classdev led_dev;
char name[P54_LED_MAX_NAME_LEN + 1];
+ unsigned int toggled;
unsigned int index;
unsigned int registered;
};
@@ -133,55 +134,74 @@ struct p54_led_dev {
struct p54_common {
struct ieee80211_hw *hw;
- u32 rx_start;
- u32 rx_end;
- struct sk_buff_head tx_queue;
+ struct ieee80211_vif *vif;
void (*tx)(struct ieee80211_hw *dev, struct sk_buff *skb);
int (*open)(struct ieee80211_hw *dev);
void (*stop)(struct ieee80211_hw *dev);
- int mode;
+ struct sk_buff_head tx_queue;
+ struct mutex conf_mutex;
+
+ /* memory management (as seen by the firmware) */
+ u32 rx_start;
+ u32 rx_end;
u16 rx_mtu;
u8 headroom;
u8 tailroom;
- struct mutex conf_mutex;
- u8 mac_addr[ETH_ALEN];
- u8 bssid[ETH_ALEN];
+
+ /* firmware/hardware info */
+ unsigned int tx_hdr_len;
+ unsigned int fw_var;
+ unsigned int fw_interface;
+ u8 version;
+
+ /* (e)DCF / QOS state */
+ bool use_short_slot;
+ struct ieee80211_tx_queue_stats tx_stats[8];
+ struct p54_edcf_queue_param qos_params[8];
+
+ /* Radio data */
+ u16 rxhw;
u8 rx_diversity_mask;
u8 tx_diversity_mask;
+ unsigned int output_power;
+ int noise;
+ /* calibration, output power limit and rssi<->dBm conversation data */
struct pda_iq_autocal_entry *iq_autocal;
unsigned int iq_autocal_len;
- struct p54_cal_database *output_limit;
struct p54_cal_database *curve_data;
+ struct p54_cal_database *output_limit;
struct p54_rssi_linear_approximation rssical_db[IEEE80211_NUM_BANDS];
+
+ /* BBP/MAC state */
+ u8 mac_addr[ETH_ALEN];
+ u8 bssid[ETH_ALEN];
+ u16 wakeup_timer;
unsigned int filter_flags;
- bool use_short_slot;
- u16 rxhw;
- u8 version;
- unsigned int tx_hdr_len;
- unsigned int fw_var;
- unsigned int fw_interface;
- unsigned int output_power;
- u32 tsf_low32;
- u32 tsf_high32;
+ int mode;
+ u32 tsf_low32, tsf_high32;
u32 basic_rate_mask;
- u16 wakeup_timer;
u16 aid;
- struct ieee80211_tx_queue_stats tx_stats[8];
- struct p54_edcf_queue_param qos_params[8];
- struct ieee80211_low_level_stats stats;
- struct delayed_work work;
struct sk_buff *cached_beacon;
- int noise;
- void *eeprom;
- struct completion eeprom_comp;
+
+ /* cryptographic engine information */
u8 privacy_caps;
u8 rx_keycache_size;
+ unsigned long *used_rxkeys;
+
/* LED management */
#ifdef CONFIG_P54_LEDS
- struct p54_led_dev assoc_led;
- struct p54_led_dev tx_led;
+ struct p54_led_dev leds[4];
+ struct delayed_work led_work;
#endif /* CONFIG_P54_LEDS */
u16 softled_state; /* bit field of glowing LEDs */
+
+ /* statistics */
+ struct ieee80211_low_level_stats stats;
+ struct delayed_work work;
+
+ /* eeprom handling */
+ void *eeprom;
+ struct completion eeprom_comp;
};
int p54_rx(struct ieee80211_hw *dev, struct sk_buff *skb);
diff --git a/drivers/net/wireless/p54/p54common.c b/drivers/net/wireless/p54/p54common.c
index c8f0232ee5e0..b618bd14583f 100644
--- a/drivers/net/wireless/p54/p54common.c
+++ b/drivers/net/wireless/p54/p54common.c
@@ -249,7 +249,7 @@ int p54_parse_firmware(struct ieee80211_hw *dev, const struct firmware *fw)
dev->queues = P54_QUEUE_AC_NUM;
}
- if (!modparam_nohwcrypt)
+ if (!modparam_nohwcrypt) {
printk(KERN_INFO "%s: cryptographic accelerator "
"WEP:%s, TKIP:%s, CCMP:%s\n",
wiphy_name(dev->wiphy),
@@ -259,6 +259,26 @@ int p54_parse_firmware(struct ieee80211_hw *dev, const struct firmware *fw)
(priv->privacy_caps & BR_DESC_PRIV_CAP_AESCCMP) ?
"YES" : "no");
+ if (priv->rx_keycache_size) {
+ /*
+ * NOTE:
+ *
+ * The firmware provides at most 255 (0 - 254) slots
+ * for keys which are then used to offload decryption.
+ * As a result the 255 entry (aka 0xff) can be used
+ * safely by the driver to mark keys that didn't fit
+ * into the full cache. This trick saves us from
+ * keeping a extra list for uploaded keys.
+ */
+
+ priv->used_rxkeys = kzalloc(BITS_TO_LONGS(
+ priv->rx_keycache_size), GFP_KERNEL);
+
+ if (!priv->used_rxkeys)
+ return -ENOMEM;
+ }
+ }
+
return 0;
}
EXPORT_SYMBOL_GPL(p54_parse_firmware);
@@ -749,8 +769,6 @@ static int p54_rx_data(struct ieee80211_hw *dev, struct sk_buff *skb)
rx_status.signal = p54_rssi_to_dbm(dev, hdr->rssi);
rx_status.noise = priv->noise;
- /* XX correct? */
- rx_status.qual = (100 * hdr->rssi) / 127;
if (hdr->rate & 0x10)
rx_status.flag |= RX_FLAG_SHORTPRE;
if (dev->conf.channel->band == IEEE80211_BAND_5GHZ)
@@ -804,44 +822,37 @@ void p54_free_skb(struct ieee80211_hw *dev, struct sk_buff *skb)
struct ieee80211_tx_info *info;
struct p54_tx_info *range;
unsigned long flags;
- u32 freed = 0, last_addr = priv->rx_start;
- if (unlikely(!skb || !dev || !skb_queue_len(&priv->tx_queue)))
+ if (unlikely(!skb || !dev || skb_queue_empty(&priv->tx_queue)))
return;
- /*
- * don't try to free an already unlinked skb
+ /* There used to be a check here to see if the SKB was on the
+ * TX queue or not. This can never happen because all SKBs we
+ * see here successfully went through p54_assign_address()
+ * which means the SKB is on the ->tx_queue.
*/
- if (unlikely((!skb->next) || (!skb->prev)))
- return;
spin_lock_irqsave(&priv->tx_queue.lock, flags);
info = IEEE80211_SKB_CB(skb);
range = (void *)info->rate_driver_data;
- if (skb->prev != (struct sk_buff *)&priv->tx_queue) {
+ if (!skb_queue_is_first(&priv->tx_queue, skb)) {
struct ieee80211_tx_info *ni;
struct p54_tx_info *mr;
- ni = IEEE80211_SKB_CB(skb->prev);
+ ni = IEEE80211_SKB_CB(skb_queue_prev(&priv->tx_queue, skb));
mr = (struct p54_tx_info *)ni->rate_driver_data;
- last_addr = mr->end_addr;
}
- if (skb->next != (struct sk_buff *)&priv->tx_queue) {
+ if (!skb_queue_is_last(&priv->tx_queue, skb)) {
struct ieee80211_tx_info *ni;
struct p54_tx_info *mr;
- ni = IEEE80211_SKB_CB(skb->next);
+ ni = IEEE80211_SKB_CB(skb_queue_next(&priv->tx_queue, skb));
mr = (struct p54_tx_info *)ni->rate_driver_data;
- freed = mr->start_addr - last_addr;
- } else
- freed = priv->rx_end - last_addr;
+ }
__skb_unlink(skb, &priv->tx_queue);
spin_unlock_irqrestore(&priv->tx_queue.lock, flags);
dev_kfree_skb_any(skb);
-
- if (freed >= priv->headroom + sizeof(struct p54_hdr) + 48 +
- IEEE80211_MAX_RTS_THRESHOLD + priv->tailroom)
- p54_wake_free_queues(dev);
+ p54_wake_free_queues(dev);
}
EXPORT_SYMBOL_GPL(p54_free_skb);
@@ -853,15 +864,13 @@ static struct sk_buff *p54_find_tx_entry(struct ieee80211_hw *dev,
unsigned long flags;
spin_lock_irqsave(&priv->tx_queue.lock, flags);
- entry = priv->tx_queue.next;
- while (entry != (struct sk_buff *)&priv->tx_queue) {
+ skb_queue_walk(&priv->tx_queue, entry) {
struct p54_hdr *hdr = (struct p54_hdr *) entry->data;
if (hdr->req_id == req_id) {
spin_unlock_irqrestore(&priv->tx_queue.lock, flags);
return entry;
}
- entry = entry->next;
}
spin_unlock_irqrestore(&priv->tx_queue.lock, flags);
return NULL;
@@ -875,37 +884,29 @@ static void p54_rx_frame_sent(struct ieee80211_hw *dev, struct sk_buff *skb)
struct sk_buff *entry;
u32 addr = le32_to_cpu(hdr->req_id) - priv->headroom;
struct p54_tx_info *range = NULL;
- u32 freed = 0;
- u32 last_addr = priv->rx_start;
unsigned long flags;
int count, idx;
spin_lock_irqsave(&priv->tx_queue.lock, flags);
- entry = (struct sk_buff *) priv->tx_queue.next;
- while (entry != (struct sk_buff *)&priv->tx_queue) {
+ skb_queue_walk(&priv->tx_queue, entry) {
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(entry);
struct p54_hdr *entry_hdr;
struct p54_tx_data *entry_data;
unsigned int pad = 0, frame_len;
range = (void *)info->rate_driver_data;
- if (range->start_addr != addr) {
- last_addr = range->end_addr;
- entry = entry->next;
+ if (range->start_addr != addr)
continue;
- }
- if (entry->next != (struct sk_buff *)&priv->tx_queue) {
+ if (!skb_queue_is_last(&priv->tx_queue, entry)) {
struct ieee80211_tx_info *ni;
struct p54_tx_info *mr;
- ni = IEEE80211_SKB_CB(entry->next);
+ ni = IEEE80211_SKB_CB(skb_queue_next(&priv->tx_queue,
+ entry));
mr = (struct p54_tx_info *)ni->rate_driver_data;
- freed = mr->start_addr - last_addr;
- } else
- freed = priv->rx_end - last_addr;
+ }
- last_addr = range->end_addr;
__skb_unlink(entry, &priv->tx_queue);
spin_unlock_irqrestore(&priv->tx_queue.lock, flags);
@@ -992,9 +993,7 @@ static void p54_rx_frame_sent(struct ieee80211_hw *dev, struct sk_buff *skb)
spin_unlock_irqrestore(&priv->tx_queue.lock, flags);
out:
- if (freed >= priv->headroom + sizeof(struct p54_hdr) + 48 +
- IEEE80211_MAX_RTS_THRESHOLD + priv->tailroom)
- p54_wake_free_queues(dev);
+ p54_wake_free_queues(dev);
}
static void p54_rx_eeprom_readback(struct ieee80211_hw *dev,
@@ -1044,6 +1043,7 @@ static void p54_rx_stats(struct ieee80211_hw *dev, struct sk_buff *skb)
static void p54_rx_trap(struct ieee80211_hw *dev, struct sk_buff *skb)
{
+ struct p54_common *priv = dev->priv;
struct p54_hdr *hdr = (struct p54_hdr *) skb->data;
struct p54_trap *trap = (struct p54_trap *) hdr->data;
u16 event = le16_to_cpu(trap->event);
@@ -1057,6 +1057,8 @@ static void p54_rx_trap(struct ieee80211_hw *dev, struct sk_buff *skb)
wiphy_name(dev->wiphy), freq);
break;
case P54_TRAP_NO_BEACON:
+ if (priv->vif)
+ ieee80211_beacon_loss(priv->vif);
break;
case P54_TRAP_SCAN:
break;
@@ -1162,23 +1164,21 @@ static int p54_assign_address(struct ieee80211_hw *dev, struct sk_buff *skb,
}
}
- entry = priv->tx_queue.next;
- while (left--) {
+ skb_queue_walk(&priv->tx_queue, entry) {
u32 hole_size;
info = IEEE80211_SKB_CB(entry);
range = (void *)info->rate_driver_data;
hole_size = range->start_addr - last_addr;
if (!target_skb && hole_size >= len) {
- target_skb = entry->prev;
+ target_skb = skb_queue_prev(&priv->tx_queue, entry);
hole_size -= len;
target_addr = last_addr;
}
largest_hole = max(largest_hole, hole_size);
last_addr = range->end_addr;
- entry = entry->next;
}
if (!target_skb && priv->rx_end - last_addr >= len) {
- target_skb = priv->tx_queue.prev;
+ target_skb = skb_peek_tail(&priv->tx_queue);
largest_hole = max(largest_hole, priv->rx_end - last_addr - len);
if (!skb_queue_empty(&priv->tx_queue)) {
info = IEEE80211_SKB_CB(target_skb);
@@ -1452,7 +1452,8 @@ static int p54_tx_fill(struct ieee80211_hw *dev, struct sk_buff *skb,
if (info->control.sta)
*aid = info->control.sta->aid;
- else
+
+ if (info->flags & IEEE80211_TX_CTL_CLEAR_PS_FILT)
*flags |= P54_HDR_FLAG_DATA_OUT_NOCANCEL;
break;
}
@@ -1939,7 +1940,8 @@ static int p54_set_ps(struct ieee80211_hw *dev)
int i;
if (dev->conf.flags & IEEE80211_CONF_PS)
- mode = P54_PSM | P54_PSM_DTIM | P54_PSM_MCBC;
+ mode = P54_PSM | P54_PSM_BEACON_TIMEOUT | P54_PSM_DTIM |
+ P54_PSM_CHECKSUM | P54_PSM_MCBC;
else
mode = P54_PSM_CAM;
@@ -1957,9 +1959,10 @@ static int p54_set_ps(struct ieee80211_hw *dev)
psm->intervals[i].periods = cpu_to_le16(1);
}
- psm->beacon_rssi_skip_max = 60;
+ psm->beacon_rssi_skip_max = 200;
psm->rssi_delta_threshold = 0;
- psm->nr = 0;
+ psm->nr = 10;
+ psm->exclude[0] = 0;
priv->tx(dev, skb);
@@ -2081,20 +2084,21 @@ out:
static void p54_stop(struct ieee80211_hw *dev)
{
struct p54_common *priv = dev->priv;
- struct sk_buff *skb;
mutex_lock(&priv->conf_mutex);
priv->mode = NL80211_IFTYPE_UNSPECIFIED;
priv->softled_state = 0;
p54_set_leds(dev);
+#ifdef CONFIG_P54_LEDS
+ cancel_delayed_work_sync(&priv->led_work);
+#endif /* CONFIG_P54_LEDS */
cancel_delayed_work_sync(&priv->work);
if (priv->cached_beacon)
p54_tx_cancel(dev, priv->cached_beacon);
priv->stop(dev);
- while ((skb = skb_dequeue(&priv->tx_queue)))
- kfree_skb(skb);
+ skb_queue_purge(&priv->tx_queue);
priv->cached_beacon = NULL;
priv->tsf_high32 = priv->tsf_low32 = 0;
mutex_unlock(&priv->conf_mutex);
@@ -2111,6 +2115,8 @@ static int p54_add_interface(struct ieee80211_hw *dev,
return -EOPNOTSUPP;
}
+ priv->vif = conf->vif;
+
switch (conf->type) {
case NL80211_IFTYPE_STATION:
case NL80211_IFTYPE_ADHOC:
@@ -2135,6 +2141,7 @@ static void p54_remove_interface(struct ieee80211_hw *dev,
struct p54_common *priv = dev->priv;
mutex_lock(&priv->conf_mutex);
+ priv->vif = NULL;
if (priv->cached_beacon)
p54_tx_cancel(dev, priv->cached_beacon);
priv->mode = NL80211_IFTYPE_MONITOR;
@@ -2174,41 +2181,6 @@ out:
return ret;
}
-static int p54_config_interface(struct ieee80211_hw *dev,
- struct ieee80211_vif *vif,
- struct ieee80211_if_conf *conf)
-{
- struct p54_common *priv = dev->priv;
- int ret = 0;
-
- mutex_lock(&priv->conf_mutex);
- if (conf->changed & IEEE80211_IFCC_BSSID) {
- memcpy(priv->bssid, conf->bssid, ETH_ALEN);
- ret = p54_setup_mac(dev);
- if (ret)
- goto out;
- }
-
- if (conf->changed & IEEE80211_IFCC_BEACON) {
- ret = p54_scan(dev, P54_SCAN_EXIT, 0);
- if (ret)
- goto out;
- ret = p54_setup_mac(dev);
- if (ret)
- goto out;
- ret = p54_beacon_update(dev, vif);
- if (ret)
- goto out;
- ret = p54_set_edcf(dev);
- if (ret)
- goto out;
- }
-
-out:
- mutex_unlock(&priv->conf_mutex);
- return ret;
-}
-
static void p54_configure_filter(struct ieee80211_hw *dev,
unsigned int changed_flags,
unsigned int *total_flags,
@@ -2312,8 +2284,32 @@ static void p54_bss_info_changed(struct ieee80211_hw *dev,
u32 changed)
{
struct p54_common *priv = dev->priv;
+ int ret;
+
+ mutex_lock(&priv->conf_mutex);
+ if (changed & BSS_CHANGED_BSSID) {
+ memcpy(priv->bssid, info->bssid, ETH_ALEN);
+ ret = p54_setup_mac(dev);
+ if (ret)
+ goto out;
+ }
+
+ if (changed & BSS_CHANGED_BEACON) {
+ ret = p54_scan(dev, P54_SCAN_EXIT, 0);
+ if (ret)
+ goto out;
+ ret = p54_setup_mac(dev);
+ if (ret)
+ goto out;
+ ret = p54_beacon_update(dev, vif);
+ if (ret)
+ goto out;
+ }
+ /* XXX: this mimics having two callbacks... clean up */
+ out:
+ mutex_unlock(&priv->conf_mutex);
- if (changed & BSS_CHANGED_ERP_SLOT) {
+ if (changed & (BSS_CHANGED_ERP_SLOT | BSS_CHANGED_BEACON)) {
priv->use_short_slot = info->use_short_slot;
p54_set_edcf(dev);
}
@@ -2334,7 +2330,6 @@ static void p54_bss_info_changed(struct ieee80211_hw *dev,
p54_setup_mac(dev);
}
}
-
}
static int p54_set_key(struct ieee80211_hw *dev, enum set_key_cmd cmd,
@@ -2344,61 +2339,84 @@ static int p54_set_key(struct ieee80211_hw *dev, enum set_key_cmd cmd,
struct p54_common *priv = dev->priv;
struct sk_buff *skb;
struct p54_keycache *rxkey;
+ int slot, ret = 0;
u8 algo = 0;
if (modparam_nohwcrypt)
return -EOPNOTSUPP;
- if (cmd == DISABLE_KEY)
- algo = 0;
- else {
+ mutex_lock(&priv->conf_mutex);
+ if (cmd == SET_KEY) {
switch (key->alg) {
case ALG_TKIP:
if (!(priv->privacy_caps & (BR_DESC_PRIV_CAP_MICHAEL |
- BR_DESC_PRIV_CAP_TKIP)))
- return -EOPNOTSUPP;
+ BR_DESC_PRIV_CAP_TKIP))) {
+ ret = -EOPNOTSUPP;
+ goto out_unlock;
+ }
key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
algo = P54_CRYPTO_TKIPMICHAEL;
break;
case ALG_WEP:
- if (!(priv->privacy_caps & BR_DESC_PRIV_CAP_WEP))
- return -EOPNOTSUPP;
+ if (!(priv->privacy_caps & BR_DESC_PRIV_CAP_WEP)) {
+ ret = -EOPNOTSUPP;
+ goto out_unlock;
+ }
key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
algo = P54_CRYPTO_WEP;
break;
case ALG_CCMP:
- if (!(priv->privacy_caps & BR_DESC_PRIV_CAP_AESCCMP))
- return -EOPNOTSUPP;
+ if (!(priv->privacy_caps & BR_DESC_PRIV_CAP_AESCCMP)) {
+ ret = -EOPNOTSUPP;
+ goto out_unlock;
+ }
key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
algo = P54_CRYPTO_AESCCMP;
break;
default:
- return -EOPNOTSUPP;
+ ret = -EOPNOTSUPP;
+ goto out_unlock;
}
- }
+ slot = bitmap_find_free_region(priv->used_rxkeys,
+ priv->rx_keycache_size, 0);
- if (key->keyidx > priv->rx_keycache_size) {
- /*
- * The device supports the choosen algorithm, but the firmware
- * does not provide enough key slots to store all of them.
- * So, incoming frames have to be decoded by the mac80211 stack,
- * but we can still offload encryption for outgoing frames.
- */
+ if (slot < 0) {
+ /*
+ * The device supports the choosen algorithm, but the
+ * firmware does not provide enough key slots to store
+ * all of them.
+ * But encryption offload for outgoing frames is always
+ * possible, so we just pretend that the upload was
+ * successful and do the decryption in software.
+ */
- return 0;
+ /* mark the key as invalid. */
+ key->hw_key_idx = 0xff;
+ goto out_unlock;
+ }
+ } else {
+ slot = key->hw_key_idx;
+
+ if (slot == 0xff) {
+ /* This key was not uploaded into the rx key cache. */
+
+ goto out_unlock;
+ }
+
+ bitmap_release_region(priv->used_rxkeys, slot, 0);
+ algo = 0;
}
- mutex_lock(&priv->conf_mutex);
skb = p54_alloc_skb(dev, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*rxkey),
- P54_CONTROL_TYPE_RX_KEYCACHE, GFP_ATOMIC);
+ P54_CONTROL_TYPE_RX_KEYCACHE, GFP_KERNEL);
if (!skb) {
- mutex_unlock(&priv->conf_mutex);
- return -ENOMEM;
+ bitmap_release_region(priv->used_rxkeys, slot, 0);
+ ret = -ENOSPC;
+ goto out_unlock;
}
- /* TODO: some devices have 4 more free slots for rx keys */
rxkey = (struct p54_keycache *)skb_put(skb, sizeof(*rxkey));
- rxkey->entry = key->keyidx;
+ rxkey->entry = slot;
rxkey->key_id = key->keyidx;
rxkey->key_type = algo;
if (sta)
@@ -2416,11 +2434,51 @@ static int p54_set_key(struct ieee80211_hw *dev, enum set_key_cmd cmd,
}
priv->tx(dev, skb);
+ key->hw_key_idx = slot;
+
+out_unlock:
mutex_unlock(&priv->conf_mutex);
- return 0;
+ return ret;
}
#ifdef CONFIG_P54_LEDS
+static void p54_update_leds(struct work_struct *work)
+{
+ struct p54_common *priv = container_of(work, struct p54_common,
+ led_work.work);
+ int err, i, tmp, blink_delay = 400;
+ bool rerun = false;
+
+ /* Don't toggle the LED, when the device is down. */
+ if (priv->mode == NL80211_IFTYPE_UNSPECIFIED)
+ return ;
+
+ for (i = 0; i < ARRAY_SIZE(priv->leds); i++)
+ if (priv->leds[i].toggled) {
+ priv->softled_state |= BIT(i);
+
+ tmp = 70 + 200 / (priv->leds[i].toggled);
+ if (tmp < blink_delay)
+ blink_delay = tmp;
+
+ if (priv->leds[i].led_dev.brightness == LED_OFF)
+ rerun = true;
+
+ priv->leds[i].toggled =
+ !!priv->leds[i].led_dev.brightness;
+ } else
+ priv->softled_state &= ~BIT(i);
+
+ err = p54_set_leds(priv->hw);
+ if (err && net_ratelimit())
+ printk(KERN_ERR "%s: failed to update LEDs.\n",
+ wiphy_name(priv->hw->wiphy));
+
+ if (rerun)
+ queue_delayed_work(priv->hw->workqueue, &priv->led_work,
+ msecs_to_jiffies(blink_delay));
+}
+
static void p54_led_brightness_set(struct led_classdev *led_dev,
enum led_brightness brightness)
{
@@ -2428,28 +2486,23 @@ static void p54_led_brightness_set(struct led_classdev *led_dev,
led_dev);
struct ieee80211_hw *dev = led->hw_dev;
struct p54_common *priv = dev->priv;
- int err;
- /* Don't toggle the LED, when the device is down. */
if (priv->mode == NL80211_IFTYPE_UNSPECIFIED)
return ;
- if (brightness != LED_OFF)
- priv->softled_state |= BIT(led->index);
- else
- priv->softled_state &= ~BIT(led->index);
-
- err = p54_set_leds(dev);
- if (err && net_ratelimit())
- printk(KERN_ERR "%s: failed to update %s LED.\n",
- wiphy_name(dev->wiphy), led_dev->name);
+ if (brightness) {
+ led->toggled++;
+ queue_delayed_work(priv->hw->workqueue, &priv->led_work,
+ HZ/10);
+ }
}
static int p54_register_led(struct ieee80211_hw *dev,
- struct p54_led_dev *led,
unsigned int led_index,
char *name, char *trigger)
{
+ struct p54_common *priv = dev->priv;
+ struct p54_led_dev *led = &priv->leds[led_index];
int err;
if (led->registered)
@@ -2482,19 +2535,30 @@ static int p54_init_leds(struct ieee80211_hw *dev)
* TODO:
* Figure out if the EEPROM contains some hints about the number
* of available/programmable LEDs of the device.
- * But for now, we can assume that we have two programmable LEDs.
*/
- err = p54_register_led(dev, &priv->assoc_led, 0, "assoc",
+ INIT_DELAYED_WORK(&priv->led_work, p54_update_leds);
+
+ err = p54_register_led(dev, 0, "assoc",
ieee80211_get_assoc_led_name(dev));
if (err)
return err;
- err = p54_register_led(dev, &priv->tx_led, 1, "tx",
+ err = p54_register_led(dev, 1, "tx",
ieee80211_get_tx_led_name(dev));
if (err)
return err;
+ err = p54_register_led(dev, 2, "rx",
+ ieee80211_get_rx_led_name(dev));
+ if (err)
+ return err;
+
+ err = p54_register_led(dev, 3, "radio",
+ ieee80211_get_radio_led_name(dev));
+ if (err)
+ return err;
+
err = p54_set_leds(dev);
return err;
}
@@ -2502,11 +2566,11 @@ static int p54_init_leds(struct ieee80211_hw *dev)
static void p54_unregister_leds(struct ieee80211_hw *dev)
{
struct p54_common *priv = dev->priv;
+ int i;
- if (priv->tx_led.registered)
- led_classdev_unregister(&priv->tx_led.led_dev);
- if (priv->assoc_led.registered)
- led_classdev_unregister(&priv->assoc_led.led_dev);
+ for (i = 0; i < ARRAY_SIZE(priv->leds); i++)
+ if (priv->leds[i].registered)
+ led_classdev_unregister(&priv->leds[i].led_dev);
}
#endif /* CONFIG_P54_LEDS */
@@ -2520,7 +2584,6 @@ static const struct ieee80211_ops p54_ops = {
.sta_notify = p54_sta_notify,
.set_key = p54_set_key,
.config = p54_config,
- .config_interface = p54_config_interface,
.bss_info_changed = p54_bss_info_changed,
.configure_filter = p54_configure_filter,
.conf_tx = p54_conf_tx,
@@ -2607,21 +2670,10 @@ void p54_free_common(struct ieee80211_hw *dev)
kfree(priv->iq_autocal);
kfree(priv->output_limit);
kfree(priv->curve_data);
+ kfree(priv->used_rxkeys);
#ifdef CONFIG_P54_LEDS
p54_unregister_leds(dev);
#endif /* CONFIG_P54_LEDS */
}
EXPORT_SYMBOL_GPL(p54_free_common);
-
-static int __init p54_init(void)
-{
- return 0;
-}
-
-static void __exit p54_exit(void)
-{
-}
-
-module_init(p54_init);
-module_exit(p54_exit);
diff --git a/drivers/net/wireless/p54/p54spi.c b/drivers/net/wireless/p54/p54spi.c
index d1fe577de3d4..83116baeb110 100644
--- a/drivers/net/wireless/p54/p54spi.c
+++ b/drivers/net/wireless/p54/p54spi.c
@@ -96,7 +96,7 @@ static void p54spi_spi_write(struct p54s_priv *priv, u8 address,
spi_message_add_tail(&t[0], &m);
t[1].tx_buf = buf;
- t[1].len = len;
+ t[1].len = len & ~1;
spi_message_add_tail(&t[1], &m);
if (len % 2) {
@@ -167,15 +167,31 @@ static const struct p54spi_spi_reg p54spi_registers_array[] =
static int p54spi_wait_bit(struct p54s_priv *priv, u16 reg, __le32 bits)
{
int i;
- __le32 buffer;
for (i = 0; i < 2000; i++) {
- p54spi_spi_read(priv, reg, &buffer, sizeof(buffer));
- if (buffer == bits)
+ __le32 buffer = p54spi_read32(priv, reg);
+ if ((buffer & bits) == bits)
return 1;
+ }
+ return 0;
+}
- msleep(1);
+static int p54spi_spi_write_dma(struct p54s_priv *priv, __le32 base,
+ const void *buf, size_t len)
+{
+ if (!p54spi_wait_bit(priv, SPI_ADRS_DMA_WRITE_CTRL,
+ cpu_to_le32(HOST_ALLOWED))) {
+ dev_err(&priv->spi->dev, "spi_write_dma not allowed "
+ "to DMA write.\n");
+ return -EAGAIN;
}
+
+ p54spi_write16(priv, SPI_ADRS_DMA_WRITE_CTRL,
+ cpu_to_le16(SPI_DMA_WRITE_CTRL_ENABLE));
+
+ p54spi_write16(priv, SPI_ADRS_DMA_WRITE_LEN, cpu_to_le16(len));
+ p54spi_write32(priv, SPI_ADRS_DMA_WRITE_BASE, base);
+ p54spi_spi_write(priv, SPI_ADRS_DMA_DATA, buf, len);
return 0;
}
@@ -228,8 +244,15 @@ static int p54spi_request_eeprom(struct ieee80211_hw *dev)
static int p54spi_upload_firmware(struct ieee80211_hw *dev)
{
struct p54s_priv *priv = dev->priv;
- unsigned long fw_len, fw_addr;
- long _fw_len;
+ unsigned long fw_len, _fw_len;
+ unsigned int offset = 0;
+ int err = 0;
+ u8 *fw;
+
+ fw_len = priv->firmware->size;
+ fw = kmemdup(priv->firmware->data, fw_len, GFP_KERNEL);
+ if (!fw)
+ return -ENOMEM;
/* stop the device */
p54spi_write16(priv, SPI_ADRS_DEV_CTRL_STAT, cpu_to_le16(
@@ -244,36 +267,17 @@ static int p54spi_upload_firmware(struct ieee80211_hw *dev)
msleep(TARGET_BOOT_SLEEP);
- fw_addr = ISL38XX_DEV_FIRMWARE_ADDR;
- fw_len = priv->firmware->size;
-
while (fw_len > 0) {
_fw_len = min_t(long, fw_len, SPI_MAX_PACKET_SIZE);
- p54spi_write16(priv, SPI_ADRS_DMA_WRITE_CTRL,
- cpu_to_le16(SPI_DMA_WRITE_CTRL_ENABLE));
-
- if (p54spi_wait_bit(priv, SPI_ADRS_DMA_WRITE_CTRL,
- cpu_to_le32(HOST_ALLOWED)) == 0) {
- dev_err(&priv->spi->dev, "fw_upload not allowed "
- "to DMA write.");
- return -EAGAIN;
- }
-
- p54spi_write16(priv, SPI_ADRS_DMA_WRITE_LEN,
- cpu_to_le16(_fw_len));
- p54spi_write32(priv, SPI_ADRS_DMA_WRITE_BASE,
- cpu_to_le32(fw_addr));
-
- p54spi_spi_write(priv, SPI_ADRS_DMA_DATA,
- &priv->firmware->data, _fw_len);
+ err = p54spi_spi_write_dma(priv, cpu_to_le32(
+ ISL38XX_DEV_FIRMWARE_ADDR + offset),
+ (fw + offset), _fw_len);
+ if (err < 0)
+ goto out;
fw_len -= _fw_len;
- fw_addr += _fw_len;
-
- /* FIXME: I think this doesn't work if firmware is large,
- * this loop goes to second round. fw->data is not
- * increased at all! */
+ offset += _fw_len;
}
BUG_ON(fw_len != 0);
@@ -292,7 +296,10 @@ static int p54spi_upload_firmware(struct ieee80211_hw *dev)
p54spi_write16(priv, SPI_ADRS_DEV_CTRL_STAT, cpu_to_le16(
SPI_CTRL_STAT_HOST_OVERRIDE | SPI_CTRL_STAT_RAM_BOOT));
msleep(TARGET_BOOT_SLEEP);
- return 0;
+
+out:
+ kfree(fw);
+ return err;
}
static void p54spi_power_off(struct p54s_priv *priv)
@@ -318,29 +325,21 @@ static inline void p54spi_int_ack(struct p54s_priv *priv, u32 val)
p54spi_write32(priv, SPI_ADRS_HOST_INT_ACK, cpu_to_le32(val));
}
-static void p54spi_wakeup(struct p54s_priv *priv)
+static int p54spi_wakeup(struct p54s_priv *priv)
{
- unsigned long timeout;
- u32 ints;
-
/* wake the chip */
p54spi_write32(priv, SPI_ADRS_ARM_INTERRUPTS,
cpu_to_le32(SPI_TARGET_INT_WAKEUP));
/* And wait for the READY interrupt */
- timeout = jiffies + HZ;
-
- ints = p54spi_read32(priv, SPI_ADRS_HOST_INTERRUPTS);
- while (!(ints & SPI_HOST_INT_READY)) {
- if (time_after(jiffies, timeout))
- goto out;
- ints = p54spi_read32(priv, SPI_ADRS_HOST_INTERRUPTS);
+ if (!p54spi_wait_bit(priv, SPI_ADRS_HOST_INTERRUPTS,
+ cpu_to_le32(SPI_HOST_INT_READY))) {
+ dev_err(&priv->spi->dev, "INT_READY timeout\n");
+ return -EBUSY;
}
p54spi_int_ack(priv, SPI_HOST_INT_READY);
-
-out:
- return;
+ return 0;
}
static inline void p54spi_sleep(struct p54s_priv *priv)
@@ -372,27 +371,48 @@ static int p54spi_rx(struct p54s_priv *priv)
{
struct sk_buff *skb;
u16 len;
+ u16 rx_head[2];
+#define READAHEAD_SZ (sizeof(rx_head)-sizeof(u16))
- p54spi_wakeup(priv);
-
- /* dummy read to flush SPI DMA controller bug */
- p54spi_read16(priv, SPI_ADRS_GEN_PURP_1);
+ if (p54spi_wakeup(priv) < 0)
+ return -EBUSY;
- len = p54spi_read16(priv, SPI_ADRS_DMA_DATA);
+ /* Read data size and first data word in one SPI transaction
+ * This is workaround for firmware/DMA bug,
+ * when first data word gets lost under high load.
+ */
+ p54spi_spi_read(priv, SPI_ADRS_DMA_DATA, rx_head, sizeof(rx_head));
+ len = rx_head[0];
if (len == 0) {
- dev_err(&priv->spi->dev, "rx request of zero bytes");
+ p54spi_sleep(priv);
+ dev_err(&priv->spi->dev, "rx request of zero bytes\n");
return 0;
}
- skb = dev_alloc_skb(len);
+ /* Firmware may insert up to 4 padding bytes after the lmac header,
+ * but it does not amend the size of SPI data transfer.
+ * Such packets has correct data size in header, thus referencing
+ * past the end of allocated skb. Reserve extra 4 bytes for this case */
+ skb = dev_alloc_skb(len + 4);
if (!skb) {
+ p54spi_sleep(priv);
dev_err(&priv->spi->dev, "could not alloc skb");
- return 0;
+ return -ENOMEM;
}
- p54spi_spi_read(priv, SPI_ADRS_DMA_DATA, skb_put(skb, len), len);
+ if (len <= READAHEAD_SZ) {
+ memcpy(skb_put(skb, len), rx_head + 1, len);
+ } else {
+ memcpy(skb_put(skb, READAHEAD_SZ), rx_head + 1, READAHEAD_SZ);
+ p54spi_spi_read(priv, SPI_ADRS_DMA_DATA,
+ skb_put(skb, len - READAHEAD_SZ),
+ len - READAHEAD_SZ);
+ }
p54spi_sleep(priv);
+ /* Put additional bytes to compensate for the possible
+ * alignment-caused truncation */
+ skb_put(skb, 4);
if (p54_rx(priv->hw, skb) == 0)
dev_kfree_skb(skb);
@@ -414,39 +434,28 @@ static irqreturn_t p54spi_interrupt(int irq, void *config)
static int p54spi_tx_frame(struct p54s_priv *priv, struct sk_buff *skb)
{
struct p54_hdr *hdr = (struct p54_hdr *) skb->data;
- struct p54s_dma_regs dma_regs;
- unsigned long timeout;
int ret = 0;
- u32 ints;
-
- p54spi_wakeup(priv);
- dma_regs.cmd = cpu_to_le16(SPI_DMA_WRITE_CTRL_ENABLE);
- dma_regs.len = cpu_to_le16(skb->len);
- dma_regs.addr = hdr->req_id;
+ if (p54spi_wakeup(priv) < 0)
+ return -EBUSY;
- p54spi_spi_write(priv, SPI_ADRS_DMA_WRITE_CTRL, &dma_regs,
- sizeof(dma_regs));
-
- p54spi_spi_write(priv, SPI_ADRS_DMA_DATA, skb->data, skb->len);
+ ret = p54spi_spi_write_dma(priv, hdr->req_id, skb->data, skb->len);
+ if (ret < 0)
+ goto out;
- timeout = jiffies + 2 * HZ;
- ints = p54spi_read32(priv, SPI_ADRS_HOST_INTERRUPTS);
- while (!(ints & SPI_HOST_INT_WR_READY)) {
- if (time_after(jiffies, timeout)) {
- dev_err(&priv->spi->dev, "WR_READY timeout");
- ret = -1;
- goto out;
- }
- ints = p54spi_read32(priv, SPI_ADRS_HOST_INTERRUPTS);
+ if (!p54spi_wait_bit(priv, SPI_ADRS_HOST_INTERRUPTS,
+ cpu_to_le32(SPI_HOST_INT_WR_READY))) {
+ dev_err(&priv->spi->dev, "WR_READY timeout\n");
+ ret = -EAGAIN;
+ goto out;
}
p54spi_int_ack(priv, SPI_HOST_INT_WR_READY);
- p54spi_sleep(priv);
-out:
if (FREE_AFTER_TX(skb))
p54_free_skb(priv->hw, skb);
+out:
+ p54spi_sleep(priv);
return ret;
}
@@ -516,8 +525,7 @@ static void p54spi_work(struct work_struct *work)
mutex_lock(&priv->mutex);
- if (priv->fw_state == FW_STATE_OFF &&
- priv->fw_state == FW_STATE_RESET)
+ if (priv->fw_state == FW_STATE_OFF)
goto out;
ints = p54spi_read32(priv, SPI_ADRS_HOST_INTERRUPTS);
@@ -544,11 +552,6 @@ static void p54spi_work(struct work_struct *work)
}
ret = p54spi_wq_tx(priv);
- if (ret < 0)
- goto out;
-
- ints = p54spi_read32(priv, SPI_ADRS_HOST_INTERRUPTS);
-
out:
mutex_unlock(&priv->mutex);
}
diff --git a/drivers/net/wireless/p54/p54usb.c b/drivers/net/wireless/p54/p54usb.c
index 6cc6cbc9234f..f40c0f468b27 100644
--- a/drivers/net/wireless/p54/p54usb.c
+++ b/drivers/net/wireless/p54/p54usb.c
@@ -81,6 +81,29 @@ static struct usb_device_id p54u_table[] __devinitdata = {
MODULE_DEVICE_TABLE(usb, p54u_table);
+static const struct {
+ u32 intf;
+ enum p54u_hw_type type;
+ char fw[FIRMWARE_NAME_MAX];
+ char fw_legacy[FIRMWARE_NAME_MAX];
+ char hw[20];
+} p54u_fwlist[__NUM_P54U_HWTYPES] = {
+ {
+ .type = P54U_NET2280,
+ .intf = FW_LM86,
+ .fw = "isl3886usb",
+ .fw_legacy = "isl3890usb",
+ .hw = "ISL3886 + net2280",
+ },
+ {
+ .type = P54U_3887,
+ .intf = FW_LM87,
+ .fw = "isl3887usb",
+ .fw_legacy = "isl3887usb_bare",
+ .hw = "ISL3887",
+ },
+};
+
static void p54u_rx_cb(struct urb *urb)
{
struct sk_buff *skb = (struct sk_buff *) urb->context;
@@ -125,11 +148,7 @@ static void p54u_rx_cb(struct urb *urb)
}
skb_reset_tail_pointer(skb);
skb_trim(skb, 0);
- if (urb->transfer_buffer != skb_tail_pointer(skb)) {
- /* this should not happen */
- WARN_ON(1);
- urb->transfer_buffer = skb_tail_pointer(skb);
- }
+ urb->transfer_buffer = skb_tail_pointer(skb);
}
skb_queue_tail(&priv->rx_queue, skb);
usb_anchor_urb(urb, &priv->submitted);
@@ -206,53 +225,6 @@ static int p54u_init_urbs(struct ieee80211_hw *dev)
return ret;
}
-static void p54u_tx_3887(struct ieee80211_hw *dev, struct sk_buff *skb)
-{
- struct p54u_priv *priv = dev->priv;
- struct urb *addr_urb, *data_urb;
- int err = 0;
-
- addr_urb = usb_alloc_urb(0, GFP_ATOMIC);
- if (!addr_urb)
- return;
-
- data_urb = usb_alloc_urb(0, GFP_ATOMIC);
- if (!data_urb) {
- usb_free_urb(addr_urb);
- return;
- }
-
- usb_fill_bulk_urb(addr_urb, priv->udev,
- usb_sndbulkpipe(priv->udev, P54U_PIPE_DATA),
- &((struct p54_hdr *)skb->data)->req_id, 4,
- p54u_tx_dummy_cb, dev);
- usb_fill_bulk_urb(data_urb, priv->udev,
- usb_sndbulkpipe(priv->udev, P54U_PIPE_DATA),
- skb->data, skb->len, FREE_AFTER_TX(skb) ?
- p54u_tx_cb : p54u_tx_dummy_cb, skb);
- addr_urb->transfer_flags |= URB_ZERO_PACKET;
- data_urb->transfer_flags |= URB_ZERO_PACKET;
-
- usb_anchor_urb(addr_urb, &priv->submitted);
- err = usb_submit_urb(addr_urb, GFP_ATOMIC);
- if (err) {
- usb_unanchor_urb(addr_urb);
- goto out;
- }
-
- usb_anchor_urb(data_urb, &priv->submitted);
- err = usb_submit_urb(data_urb, GFP_ATOMIC);
- if (err)
- usb_unanchor_urb(data_urb);
-
- out:
- usb_free_urb(addr_urb);
- usb_free_urb(data_urb);
-
- if (err)
- p54_free_skb(dev, skb);
-}
-
static __le32 p54u_lm87_chksum(const __le32 *data, size_t length)
{
u32 chk = 0;
@@ -425,20 +397,16 @@ static int p54u_bulk_msg(struct p54u_priv *priv, unsigned int ep,
data, len, &alen, 2000);
}
-static const char p54u_romboot_3887[] = "~~~~";
-static const char p54u_firmware_upload_3887[] = "<\r";
-
-static int p54u_device_reset_3887(struct ieee80211_hw *dev)
+static int p54u_device_reset(struct ieee80211_hw *dev)
{
struct p54u_priv *priv = dev->priv;
int ret, lock = (priv->intf->condition != USB_INTERFACE_BINDING);
- u8 buf[4];
if (lock) {
ret = usb_lock_device_for_reset(priv->udev, priv->intf);
if (ret < 0) {
dev_err(&priv->udev->dev, "(p54usb) unable to lock "
- " device for reset: %d\n", ret);
+ "device for reset (%d)!\n", ret);
return ret;
}
}
@@ -447,26 +415,34 @@ static int p54u_device_reset_3887(struct ieee80211_hw *dev)
if (lock)
usb_unlock_device(priv->udev);
- if (ret) {
+ if (ret)
dev_err(&priv->udev->dev, "(p54usb) unable to reset "
- "device: %d\n", ret);
- return ret;
- }
+ "device (%d)!\n", ret);
+
+ return ret;
+}
+
+static const char p54u_romboot_3887[] = "~~~~";
+static int p54u_firmware_reset_3887(struct ieee80211_hw *dev)
+{
+ struct p54u_priv *priv = dev->priv;
+ u8 buf[4];
+ int ret;
memcpy(&buf, p54u_romboot_3887, sizeof(buf));
ret = p54u_bulk_msg(priv, P54U_PIPE_DATA,
buf, sizeof(buf));
if (ret)
dev_err(&priv->udev->dev, "(p54usb) unable to jump to "
- "boot ROM: %d\n", ret);
+ "boot ROM (%d)!\n", ret);
return ret;
}
+static const char p54u_firmware_upload_3887[] = "<\r";
static int p54u_upload_firmware_3887(struct ieee80211_hw *dev)
{
struct p54u_priv *priv = dev->priv;
- const struct firmware *fw_entry = NULL;
int err, alen;
u8 carry = 0;
u8 *buf, *tmp;
@@ -475,51 +451,29 @@ static int p54u_upload_firmware_3887(struct ieee80211_hw *dev)
struct x2_header *hdr;
unsigned long timeout;
+ err = p54u_firmware_reset_3887(dev);
+ if (err)
+ return err;
+
tmp = buf = kmalloc(P54U_FW_BLOCK, GFP_KERNEL);
if (!buf) {
dev_err(&priv->udev->dev, "(p54usb) cannot allocate firmware"
"upload buffer!\n");
- err = -ENOMEM;
- goto err_bufalloc;
- }
-
- err = p54u_device_reset_3887(dev);
- if (err)
- goto err_reset;
-
- err = request_firmware(&fw_entry, "isl3887usb", &priv->udev->dev);
- if (err) {
- dev_err(&priv->udev->dev, "p54usb: cannot find firmware "
- "(isl3887usb)\n");
- err = request_firmware(&fw_entry, "isl3887usb_bare",
- &priv->udev->dev);
- if (err)
- goto err_req_fw_failed;
- }
-
- err = p54_parse_firmware(dev, fw_entry);
- if (err)
- goto err_upload_failed;
-
- if (priv->common.fw_interface != FW_LM87) {
- dev_err(&priv->udev->dev, "wrong firmware, "
- "please get a LM87 firmware and try again.\n");
- err = -EINVAL;
- goto err_upload_failed;
+ return -ENOMEM;
}
- left = block_size = min((size_t)P54U_FW_BLOCK, fw_entry->size);
+ left = block_size = min((size_t)P54U_FW_BLOCK, priv->fw->size);
strcpy(buf, p54u_firmware_upload_3887);
left -= strlen(p54u_firmware_upload_3887);
tmp += strlen(p54u_firmware_upload_3887);
- data = fw_entry->data;
- remains = fw_entry->size;
+ data = priv->fw->data;
+ remains = priv->fw->size;
hdr = (struct x2_header *)(buf + strlen(p54u_firmware_upload_3887));
memcpy(hdr->signature, X2_SIGNATURE, X2_SIGNATURE_SIZE);
hdr->fw_load_addr = cpu_to_le32(ISL38XX_DEV_FIRMWARE_ADDR);
- hdr->fw_length = cpu_to_le32(fw_entry->size);
+ hdr->fw_length = cpu_to_le32(priv->fw->size);
hdr->crc = cpu_to_le32(~crc32_le(~0, (void *)&hdr->fw_load_addr,
sizeof(u32)*2));
left -= sizeof(*hdr);
@@ -561,7 +515,8 @@ static int p54u_upload_firmware_3887(struct ieee80211_hw *dev)
left = block_size = min((unsigned int)P54U_FW_BLOCK, remains);
}
- *((__le32 *)buf) = cpu_to_le32(~crc32_le(~0, fw_entry->data, fw_entry->size));
+ *((__le32 *)buf) = cpu_to_le32(~crc32_le(~0, priv->fw->data,
+ priv->fw->size));
err = p54u_bulk_msg(priv, P54U_PIPE_DATA, buf, sizeof(u32));
if (err) {
dev_err(&priv->udev->dev, "(p54usb) firmware upload failed!\n");
@@ -612,19 +567,14 @@ static int p54u_upload_firmware_3887(struct ieee80211_hw *dev)
if (err)
goto err_upload_failed;
- err_upload_failed:
- release_firmware(fw_entry);
- err_req_fw_failed:
- err_reset:
+err_upload_failed:
kfree(buf);
- err_bufalloc:
return err;
}
static int p54u_upload_firmware_net2280(struct ieee80211_hw *dev)
{
struct p54u_priv *priv = dev->priv;
- const struct firmware *fw_entry = NULL;
const struct p54p_csr *devreg = (const struct p54p_csr *) P54U_DEV_BASE;
int err, alen;
void *buf;
@@ -639,33 +589,6 @@ static int p54u_upload_firmware_net2280(struct ieee80211_hw *dev)
return -ENOMEM;
}
- err = request_firmware(&fw_entry, "isl3886usb", &priv->udev->dev);
- if (err) {
- dev_err(&priv->udev->dev, "(p54usb) cannot find firmware "
- "(isl3886usb)\n");
- err = request_firmware(&fw_entry, "isl3890usb",
- &priv->udev->dev);
- if (err) {
- kfree(buf);
- return err;
- }
- }
-
- err = p54_parse_firmware(dev, fw_entry);
- if (err) {
- kfree(buf);
- release_firmware(fw_entry);
- return err;
- }
-
- if (priv->common.fw_interface != FW_LM86) {
- dev_err(&priv->udev->dev, "wrong firmware, "
- "please get a LM86(USB) firmware and try again.\n");
- kfree(buf);
- release_firmware(fw_entry);
- return -EINVAL;
- }
-
#define P54U_WRITE(type, addr, data) \
do {\
err = p54u_write(priv, buf, type,\
@@ -765,8 +688,8 @@ static int p54u_upload_firmware_net2280(struct ieee80211_hw *dev)
P54U_WRITE(NET2280_DEV_U32, &devreg->int_ack, reg);
/* finally, we can upload firmware now! */
- remains = fw_entry->size;
- data = fw_entry->data;
+ remains = priv->fw->size;
+ data = priv->fw->data;
offset = ISL38XX_DEV_FIRMWARE_ADDR;
while (remains) {
@@ -875,12 +798,54 @@ static int p54u_upload_firmware_net2280(struct ieee80211_hw *dev)
#undef P54U_WRITE
#undef P54U_READ
- fail:
- release_firmware(fw_entry);
+fail:
kfree(buf);
return err;
}
+static int p54u_load_firmware(struct ieee80211_hw *dev)
+{
+ struct p54u_priv *priv = dev->priv;
+ int err, i;
+
+ BUILD_BUG_ON(ARRAY_SIZE(p54u_fwlist) != __NUM_P54U_HWTYPES);
+
+ for (i = 0; i < __NUM_P54U_HWTYPES; i++)
+ if (p54u_fwlist[i].type == priv->hw_type)
+ break;
+
+ if (i == __NUM_P54U_HWTYPES)
+ return -EOPNOTSUPP;
+
+ err = request_firmware(&priv->fw, p54u_fwlist[i].fw, &priv->udev->dev);
+ if (err) {
+ dev_err(&priv->udev->dev, "(p54usb) cannot load firmware %s "
+ "(%d)!\n", p54u_fwlist[i].fw, err);
+
+ err = request_firmware(&priv->fw, p54u_fwlist[i].fw_legacy,
+ &priv->udev->dev);
+ if (err)
+ return err;
+ }
+
+ err = p54_parse_firmware(dev, priv->fw);
+ if (err)
+ goto out;
+
+ if (priv->common.fw_interface != p54u_fwlist[i].intf) {
+ dev_err(&priv->udev->dev, "wrong firmware, please get "
+ "a firmware for \"%s\" and try again.\n",
+ p54u_fwlist[i].hw);
+ err = -EINVAL;
+ }
+
+out:
+ if (err)
+ release_firmware(priv->fw);
+
+ return err;
+}
+
static int p54u_open(struct ieee80211_hw *dev)
{
struct p54u_priv *priv = dev->priv;
@@ -922,6 +887,7 @@ static int __devinit p54u_probe(struct usb_interface *intf,
}
priv = dev->priv;
+ priv->hw_type = P54U_INVALID_HW;
SET_IEEE80211_DEV(dev, &intf->dev);
usb_set_intfdata(intf, dev);
@@ -953,37 +919,48 @@ static int __devinit p54u_probe(struct usb_interface *intf,
priv->common.open = p54u_open;
priv->common.stop = p54u_stop;
if (recognized_pipes < P54U_PIPE_NUMBER) {
+#ifdef CONFIG_PM
+ /* ISL3887 needs a full reset on resume */
+ udev->reset_resume = 1;
+ err = p54u_device_reset(dev);
+#endif
+
priv->hw_type = P54U_3887;
- err = p54u_upload_firmware_3887(dev);
- if (priv->common.fw_interface == FW_LM87) {
- dev->extra_tx_headroom += sizeof(struct lm87_tx_hdr);
- priv->common.tx_hdr_len = sizeof(struct lm87_tx_hdr);
- priv->common.tx = p54u_tx_lm87;
- } else
- priv->common.tx = p54u_tx_3887;
+ dev->extra_tx_headroom += sizeof(struct lm87_tx_hdr);
+ priv->common.tx_hdr_len = sizeof(struct lm87_tx_hdr);
+ priv->common.tx = p54u_tx_lm87;
+ priv->upload_fw = p54u_upload_firmware_3887;
} else {
priv->hw_type = P54U_NET2280;
dev->extra_tx_headroom += sizeof(struct net2280_tx_hdr);
priv->common.tx_hdr_len = sizeof(struct net2280_tx_hdr);
priv->common.tx = p54u_tx_net2280;
- err = p54u_upload_firmware_net2280(dev);
+ priv->upload_fw = p54u_upload_firmware_net2280;
}
+ err = p54u_load_firmware(dev);
if (err)
goto err_free_dev;
+ err = priv->upload_fw(dev);
+ if (err)
+ goto err_free_fw;
+
p54u_open(dev);
err = p54_read_eeprom(dev);
p54u_stop(dev);
if (err)
- goto err_free_dev;
+ goto err_free_fw;
err = p54_register_common(dev, &udev->dev);
if (err)
- goto err_free_dev;
+ goto err_free_fw;
return 0;
- err_free_dev:
+err_free_fw:
+ release_firmware(priv->fw);
+
+err_free_dev:
ieee80211_free_hw(dev);
usb_set_intfdata(intf, NULL);
usb_put_dev(udev);
@@ -1002,20 +979,64 @@ static void __devexit p54u_disconnect(struct usb_interface *intf)
priv = dev->priv;
usb_put_dev(interface_to_usbdev(intf));
+ release_firmware(priv->fw);
p54_free_common(dev);
ieee80211_free_hw(dev);
}
static int p54u_pre_reset(struct usb_interface *intf)
{
+ struct ieee80211_hw *dev = usb_get_intfdata(intf);
+
+ if (!dev)
+ return -ENODEV;
+
+ p54u_stop(dev);
return 0;
}
+static int p54u_resume(struct usb_interface *intf)
+{
+ struct ieee80211_hw *dev = usb_get_intfdata(intf);
+ struct p54u_priv *priv;
+
+ if (!dev)
+ return -ENODEV;
+
+ priv = dev->priv;
+ if (unlikely(!(priv->upload_fw && priv->fw)))
+ return 0;
+
+ return priv->upload_fw(dev);
+}
+
static int p54u_post_reset(struct usb_interface *intf)
{
+ struct ieee80211_hw *dev = usb_get_intfdata(intf);
+ struct p54u_priv *priv;
+ int err;
+
+ err = p54u_resume(intf);
+ if (err)
+ return err;
+
+ /* reinitialize old device state */
+ priv = dev->priv;
+ if (priv->common.mode != NL80211_IFTYPE_UNSPECIFIED)
+ ieee80211_restart_hw(dev);
+
return 0;
}
+#ifdef CONFIG_PM
+
+static int p54u_suspend(struct usb_interface *intf, pm_message_t message)
+{
+ return p54u_pre_reset(intf);
+}
+
+#endif /* CONFIG_PM */
+
static struct usb_driver p54u_driver = {
.name = "p54usb",
.id_table = p54u_table,
@@ -1023,6 +1044,11 @@ static struct usb_driver p54u_driver = {
.disconnect = p54u_disconnect,
.pre_reset = p54u_pre_reset,
.post_reset = p54u_post_reset,
+#ifdef CONFIG_PM
+ .suspend = p54u_suspend,
+ .resume = p54u_resume,
+ .reset_resume = p54u_resume,
+#endif /* CONFIG_PM */
.soft_unbind = 1,
};
diff --git a/drivers/net/wireless/p54/p54usb.h b/drivers/net/wireless/p54/p54usb.h
index 8bc58982d8dd..e935b79f7f75 100644
--- a/drivers/net/wireless/p54/p54usb.h
+++ b/drivers/net/wireless/p54/p54usb.h
@@ -123,18 +123,26 @@ struct p54u_rx_info {
struct ieee80211_hw *dev;
};
+enum p54u_hw_type {
+ P54U_INVALID_HW,
+ P54U_NET2280,
+ P54U_3887,
+
+ /* keep last */
+ __NUM_P54U_HWTYPES,
+};
+
struct p54u_priv {
struct p54_common common;
struct usb_device *udev;
struct usb_interface *intf;
- enum {
- P54U_NET2280 = 0,
- P54U_3887
- } hw_type;
+ int (*upload_fw)(struct ieee80211_hw *dev);
+ enum p54u_hw_type hw_type;
spinlock_t lock;
struct sk_buff_head rx_queue;
struct usb_anchor submitted;
+ const struct firmware *fw;
};
#endif /* P54USB_H */