From 36323f817af0376c78612cfdab714b0feb05fea5 Mon Sep 17 00:00:00 2001 From: Thomas Huehn Date: Mon, 23 Jul 2012 21:33:42 +0200 Subject: mac80211: move TX station pointer and restructure TX Remove the control.sta pointer from ieee80211_tx_info to free up sufficient space in the TX skb control buffer for the upcoming Transmit Power Control (TPC). Instead, the pointer is now on the stack in a new control struct that is passed as a function parameter to the drivers' tx method. Signed-off-by: Thomas Huehn Signed-off-by: Alina Friedrichsen Signed-off-by: Felix Fietkau [reworded commit message] Signed-off-by: Johannes Berg --- drivers/net/wireless/p54/lmac.h | 4 +++- drivers/net/wireless/p54/main.c | 2 +- drivers/net/wireless/p54/txrx.c | 15 +++++++++------ 3 files changed, 13 insertions(+), 8 deletions(-) (limited to 'drivers/net/wireless/p54') diff --git a/drivers/net/wireless/p54/lmac.h b/drivers/net/wireless/p54/lmac.h index 3d8d622bec55..de1d46bf97df 100644 --- a/drivers/net/wireless/p54/lmac.h +++ b/drivers/net/wireless/p54/lmac.h @@ -526,7 +526,9 @@ int p54_init_leds(struct p54_common *priv); void p54_unregister_leds(struct p54_common *priv); /* xmit functions */ -void p54_tx_80211(struct ieee80211_hw *dev, struct sk_buff *skb); +void p54_tx_80211(struct ieee80211_hw *dev, + struct ieee80211_tx_control *control, + struct sk_buff *skb); int p54_tx_cancel(struct p54_common *priv, __le32 req_id); void p54_tx(struct p54_common *priv, struct sk_buff *skb); diff --git a/drivers/net/wireless/p54/main.c b/drivers/net/wireless/p54/main.c index 7cffea795ad2..5e91ad06dd5d 100644 --- a/drivers/net/wireless/p54/main.c +++ b/drivers/net/wireless/p54/main.c @@ -158,7 +158,7 @@ static int p54_beacon_update(struct p54_common *priv, * to cancel the old beacon template by hand, instead the firmware * will release the previous one through the feedback mechanism. */ - p54_tx_80211(priv->hw, beacon); + p54_tx_80211(priv->hw, NULL, beacon); priv->tsf_high32 = 0; priv->tsf_low32 = 0; diff --git a/drivers/net/wireless/p54/txrx.c b/drivers/net/wireless/p54/txrx.c index f38786e02623..5861e13a6fd8 100644 --- a/drivers/net/wireless/p54/txrx.c +++ b/drivers/net/wireless/p54/txrx.c @@ -676,8 +676,9 @@ int p54_rx(struct ieee80211_hw *dev, struct sk_buff *skb) EXPORT_SYMBOL_GPL(p54_rx); static void p54_tx_80211_header(struct p54_common *priv, struct sk_buff *skb, - struct ieee80211_tx_info *info, u8 *queue, - u32 *extra_len, u16 *flags, u16 *aid, + struct ieee80211_tx_info *info, + struct ieee80211_sta *sta, + u8 *queue, u32 *extra_len, u16 *flags, u16 *aid, bool *burst_possible) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; @@ -746,8 +747,8 @@ static void p54_tx_80211_header(struct p54_common *priv, struct sk_buff *skb, } } - if (info->control.sta) - *aid = info->control.sta->aid; + if (sta) + *aid = sta->aid; break; } } @@ -767,7 +768,9 @@ static u8 p54_convert_algo(u32 cipher) } } -void p54_tx_80211(struct ieee80211_hw *dev, struct sk_buff *skb) +void p54_tx_80211(struct ieee80211_hw *dev, + struct ieee80211_tx_control *control, + struct sk_buff *skb) { struct p54_common *priv = dev->priv; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); @@ -784,7 +787,7 @@ void p54_tx_80211(struct ieee80211_hw *dev, struct sk_buff *skb) u8 nrates = 0, nremaining = 8; bool burst_allowed = false; - p54_tx_80211_header(priv, skb, info, &queue, &extra_len, + p54_tx_80211_header(priv, skb, info, control->sta, &queue, &extra_len, &hdr_flags, &aid, &burst_allowed); if (p54_tx_qos_accounting_alloc(priv, skb, queue)) { -- cgit v1.2.3 From 95a96e08967fed6e64e53b2d6a5e153a4d605e11 Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Tue, 24 Jul 2012 21:16:42 +0200 Subject: p54pci: convert driver to use asynchronous firmware loading Drivers that load firmware from their probe routine have problems with the latest versions of udev as they get timeouts while waiting for user space to start. The problem is fixed by using request_firmware_nowait() and delaying the start of mac80211 until the firmware is loaded. To prevent the possibility of the driver being unloaded while the firmware loading callback is still active, a completion queue entry is used. Signed-off-by: Larry Finger Signed-off-by: Christian Lamparter Signed-off-by: John W. Linville --- drivers/net/wireless/p54/p54pci.c | 88 +++++++++++++++++++++++++++------------ drivers/net/wireless/p54/p54pci.h | 1 + 2 files changed, 63 insertions(+), 26 deletions(-) (limited to 'drivers/net/wireless/p54') diff --git a/drivers/net/wireless/p54/p54pci.c b/drivers/net/wireless/p54/p54pci.c index 89318adc8c7f..b4390797d78c 100644 --- a/drivers/net/wireless/p54/p54pci.c +++ b/drivers/net/wireless/p54/p54pci.c @@ -488,6 +488,58 @@ static int p54p_open(struct ieee80211_hw *dev) return 0; } +static void p54p_firmware_step2(const struct firmware *fw, + void *context) +{ + struct p54p_priv *priv = context; + struct ieee80211_hw *dev = priv->common.hw; + struct pci_dev *pdev = priv->pdev; + int err; + + if (!fw) { + dev_err(&pdev->dev, "Cannot find firmware (isl3886pci)\n"); + err = -ENOENT; + goto out; + } + + priv->firmware = fw; + + err = p54p_open(dev); + if (err) + goto out; + err = p54_read_eeprom(dev); + p54p_stop(dev); + if (err) + goto out; + + err = p54_register_common(dev, &pdev->dev); + if (err) + goto out; + +out: + + complete(&priv->fw_loaded); + + if (err) { + struct device *parent = pdev->dev.parent; + + if (parent) + device_lock(parent); + + /* + * This will indirectly result in a call to p54p_remove. + * Hence, we don't need to bother with freeing any + * allocated ressources at all. + */ + device_release_driver(&pdev->dev); + + if (parent) + device_unlock(parent); + } + + pci_dev_put(pdev); +} + static int __devinit p54p_probe(struct pci_dev *pdev, const struct pci_device_id *id) { @@ -496,6 +548,7 @@ static int __devinit p54p_probe(struct pci_dev *pdev, unsigned long mem_addr, mem_len; int err; + pci_dev_get(pdev); err = pci_enable_device(pdev); if (err) { dev_err(&pdev->dev, "Cannot enable new PCI device\n"); @@ -537,6 +590,7 @@ static int __devinit p54p_probe(struct pci_dev *pdev, priv = dev->priv; priv->pdev = pdev; + init_completion(&priv->fw_loaded); SET_IEEE80211_DEV(dev, &pdev->dev); pci_set_drvdata(pdev, dev); @@ -561,32 +615,12 @@ static int __devinit p54p_probe(struct pci_dev *pdev, spin_lock_init(&priv->lock); tasklet_init(&priv->tasklet, p54p_tasklet, (unsigned long)dev); - err = request_firmware(&priv->firmware, "isl3886pci", - &priv->pdev->dev); - if (err) { - dev_err(&pdev->dev, "Cannot find firmware (isl3886pci)\n"); - err = request_firmware(&priv->firmware, "isl3886", - &priv->pdev->dev); - if (err) - goto err_free_common; - } - - err = p54p_open(dev); - if (err) - goto err_free_common; - err = p54_read_eeprom(dev); - p54p_stop(dev); - if (err) - goto err_free_common; - - err = p54_register_common(dev, &pdev->dev); - if (err) - goto err_free_common; - - return 0; + err = request_firmware_nowait(THIS_MODULE, 1, "isl3886pci", + &priv->pdev->dev, GFP_KERNEL, + priv, p54p_firmware_step2); + if (!err) + return 0; - err_free_common: - release_firmware(priv->firmware); pci_free_consistent(pdev, sizeof(*priv->ring_control), priv->ring_control, priv->ring_control_dma); @@ -601,6 +635,7 @@ static int __devinit p54p_probe(struct pci_dev *pdev, pci_release_regions(pdev); err_disable_dev: pci_disable_device(pdev); + pci_dev_put(pdev); return err; } @@ -612,8 +647,9 @@ static void __devexit p54p_remove(struct pci_dev *pdev) if (!dev) return; - p54_unregister_common(dev); priv = dev->priv; + wait_for_completion(&priv->fw_loaded); + p54_unregister_common(dev); release_firmware(priv->firmware); pci_free_consistent(pdev, sizeof(*priv->ring_control), priv->ring_control, priv->ring_control_dma); diff --git a/drivers/net/wireless/p54/p54pci.h b/drivers/net/wireless/p54/p54pci.h index 7aa509f7e387..68405c142f97 100644 --- a/drivers/net/wireless/p54/p54pci.h +++ b/drivers/net/wireless/p54/p54pci.h @@ -105,6 +105,7 @@ struct p54p_priv { struct sk_buff *tx_buf_data[32]; struct sk_buff *tx_buf_mgmt[4]; struct completion boot_comp; + struct completion fw_loaded; }; #endif /* P54USB_H */ -- cgit v1.2.3 From 9bc63816be9bf504414d350e605a4b30f857907d Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Sat, 28 Jul 2012 02:57:51 +0200 Subject: p54: parse output power table For the upcoming tpc changes, the driver needs to provide sensible max output values for each supported channel. And while the eeprom always had a output_limit table, which defines the upper limit for each frequency and modulation, it was never really useful for anything... until now. Note: For anyone wondering about what your card is calibrated for: check "iw list". * 2412 MHz [1] (18.0 dBm) * 2437 MHz [6] (19.0 dBm) [...] * 5180 MHz [36] (18.0 dBm) * 5260 MHz [52] (17.0 dBm) (radar detection) * 5680 MHz [136] (19.0 dBm) (radar detection) (for a Dell Wireless 1450 USB Adapter) Signed-off-by: Christian Lamparter Signed-off-by: John W. Linville --- drivers/net/wireless/p54/eeprom.c | 104 +++++++++++++++++++++++++++++--------- drivers/net/wireless/p54/eeprom.h | 12 +++++ 2 files changed, 92 insertions(+), 24 deletions(-) (limited to 'drivers/net/wireless/p54') diff --git a/drivers/net/wireless/p54/eeprom.c b/drivers/net/wireless/p54/eeprom.c index 14037092ba89..d4d86107e05a 100644 --- a/drivers/net/wireless/p54/eeprom.c +++ b/drivers/net/wireless/p54/eeprom.c @@ -76,6 +76,7 @@ struct p54_channel_entry { u16 freq; u16 data; int index; + int max_power; enum ieee80211_band band; }; @@ -173,6 +174,7 @@ static int p54_generate_band(struct ieee80211_hw *dev, for (i = 0, j = 0; (j < list->band_channel_num[band]) && (i < list->entries); i++) { struct p54_channel_entry *chan = &list->channels[i]; + struct ieee80211_channel *dest = &tmp->channels[j]; if (chan->band != band) continue; @@ -190,14 +192,15 @@ static int p54_generate_band(struct ieee80211_hw *dev, continue; } - tmp->channels[j].band = chan->band; - tmp->channels[j].center_freq = chan->freq; + dest->band = chan->band; + dest->center_freq = chan->freq; + dest->max_power = chan->max_power; priv->survey[*chan_num].channel = &tmp->channels[j]; priv->survey[*chan_num].filled = SURVEY_INFO_NOISE_DBM | SURVEY_INFO_CHANNEL_TIME | SURVEY_INFO_CHANNEL_TIME_BUSY | SURVEY_INFO_CHANNEL_TIME_TX; - tmp->channels[j].hw_value = (*chan_num); + dest->hw_value = (*chan_num); j++; (*chan_num)++; } @@ -229,10 +232,11 @@ err_out: return ret; } -static void p54_update_channel_param(struct p54_channel_list *list, - u16 freq, u16 data) +static struct p54_channel_entry *p54_update_channel_param(struct p54_channel_list *list, + u16 freq, u16 data) { - int band, i; + int i; + struct p54_channel_entry *entry = NULL; /* * usually all lists in the eeprom are mostly sorted. @@ -241,30 +245,74 @@ static void p54_update_channel_param(struct p54_channel_list *list, */ for (i = list->entries; i >= 0; i--) { if (freq == list->channels[i].freq) { - list->channels[i].data |= data; + entry = &list->channels[i]; break; } } if ((i < 0) && (list->entries < list->max_entries)) { /* entry does not exist yet. Initialize a new one. */ - band = p54_get_band_from_freq(freq); + int band = p54_get_band_from_freq(freq); /* * filter out frequencies which don't belong into * any supported band. */ - if (band < 0) - return ; + if (band >= 0) { + i = list->entries++; + list->band_channel_num[band]++; + + entry = &list->channels[i]; + entry->freq = freq; + entry->band = band; + entry->index = ieee80211_frequency_to_channel(freq); + entry->max_power = 0; + entry->data = 0; + } + } - i = list->entries++; - list->band_channel_num[band]++; + if (entry) + entry->data |= data; - list->channels[i].freq = freq; - list->channels[i].data = data; - list->channels[i].band = band; - list->channels[i].index = ieee80211_frequency_to_channel(freq); - /* TODO: parse output_limit and fill max_power */ + return entry; +} + +static int p54_get_maxpower(struct p54_common *priv, void *data) +{ + switch (priv->rxhw & PDR_SYNTH_FRONTEND_MASK) { + case PDR_SYNTH_FRONTEND_LONGBOW: { + struct pda_channel_output_limit_longbow *pda = data; + int j; + u16 rawpower = 0; + pda = data; + for (j = 0; j < ARRAY_SIZE(pda->point); j++) { + struct pda_channel_output_limit_point_longbow *point = + &pda->point[j]; + rawpower = max(rawpower, le16_to_cpu(point->val_qpsk)); + rawpower = max(rawpower, le16_to_cpu(point->val_bpsk)); + rawpower = max(rawpower, le16_to_cpu(point->val_16qam)); + rawpower = max(rawpower, le16_to_cpu(point->val_64qam)); + } + /* longbow seems to use 1/16 dBm units */ + return rawpower / 16; + } + + case PDR_SYNTH_FRONTEND_DUETTE3: + case PDR_SYNTH_FRONTEND_DUETTE2: + case PDR_SYNTH_FRONTEND_FRISBEE: + case PDR_SYNTH_FRONTEND_XBOW: { + struct pda_channel_output_limit *pda = data; + u8 rawpower = 0; + rawpower = max(rawpower, pda->val_qpsk); + rawpower = max(rawpower, pda->val_bpsk); + rawpower = max(rawpower, pda->val_16qam); + rawpower = max(rawpower, pda->val_64qam); + /* raw values are in 1/4 dBm units */ + return rawpower / 4; + } + + default: + return 20; } } @@ -315,12 +363,19 @@ static int p54_generate_channel_lists(struct ieee80211_hw *dev) } if (i < priv->output_limit->entries) { - freq = le16_to_cpup((__le16 *) (i * - priv->output_limit->entry_size + - priv->output_limit->offset + - priv->output_limit->data)); - - p54_update_channel_param(list, freq, CHAN_HAS_LIMIT); + struct p54_channel_entry *tmp; + + void *data = (void *) ((unsigned long) i * + priv->output_limit->entry_size + + priv->output_limit->offset + + priv->output_limit->data); + + freq = le16_to_cpup((__le16 *) data); + tmp = p54_update_channel_param(list, freq, + CHAN_HAS_LIMIT); + if (tmp) { + tmp->max_power = p54_get_maxpower(priv, data); + } } if (i < priv->curve_data->entries) { @@ -834,11 +889,12 @@ good_eeprom: goto err; } + priv->rxhw = synth & PDR_SYNTH_FRONTEND_MASK; + err = p54_generate_channel_lists(dev); if (err) goto err; - priv->rxhw = synth & PDR_SYNTH_FRONTEND_MASK; if (priv->rxhw == PDR_SYNTH_FRONTEND_XBOW) p54_init_xbow_synth(priv); if (!(synth & PDR_SYNTH_24_GHZ_DISABLED)) diff --git a/drivers/net/wireless/p54/eeprom.h b/drivers/net/wireless/p54/eeprom.h index afde72b84606..20ebe39a3f4e 100644 --- a/drivers/net/wireless/p54/eeprom.h +++ b/drivers/net/wireless/p54/eeprom.h @@ -57,6 +57,18 @@ struct pda_channel_output_limit { u8 rate_set_size; } __packed; +struct pda_channel_output_limit_point_longbow { + __le16 val_bpsk; + __le16 val_qpsk; + __le16 val_16qam; + __le16 val_64qam; +} __packed; + +struct pda_channel_output_limit_longbow { + __le16 freq; + struct pda_channel_output_limit_point_longbow point[3]; +} __packed; + struct pda_pa_curve_data_sample_rev0 { u8 rf_power; u8 pa_detector; -- cgit v1.2.3 From 22c5649eef0fc37532e20c14d2656b28ca708a69 Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Sat, 11 Aug 2012 13:09:20 +0200 Subject: p54: fix powerpc gcc warnings My commit "p54: parse output power table" introduced the following compiler warnings for powerpc-allmodconfig eeprom.c: In function 'p54_get_maxpower': eeprom.c:291 warning: comparison of distinct pointer types lacks a cast eeporm.c:292 warning: comparison of distinct pointer types lacks a cast eeprom.c:293 warning: comparison of distinct pointer types lacks a cast eeprom.c:294 warning: comparison of distinct pointer types lacks a cast This patch fixes those by using max_t(u16 which forces a type cast. Reported-by: Fengguang Wu Tested-by: Fengguang Wu Signed-off-by: Christian Lamparter Signed-off-by: John W. Linville --- drivers/net/wireless/p54/eeprom.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'drivers/net/wireless/p54') diff --git a/drivers/net/wireless/p54/eeprom.c b/drivers/net/wireless/p54/eeprom.c index d4d86107e05a..1ef1bfe6a9d7 100644 --- a/drivers/net/wireless/p54/eeprom.c +++ b/drivers/net/wireless/p54/eeprom.c @@ -288,10 +288,14 @@ static int p54_get_maxpower(struct p54_common *priv, void *data) for (j = 0; j < ARRAY_SIZE(pda->point); j++) { struct pda_channel_output_limit_point_longbow *point = &pda->point[j]; - rawpower = max(rawpower, le16_to_cpu(point->val_qpsk)); - rawpower = max(rawpower, le16_to_cpu(point->val_bpsk)); - rawpower = max(rawpower, le16_to_cpu(point->val_16qam)); - rawpower = max(rawpower, le16_to_cpu(point->val_64qam)); + rawpower = max_t(u16, + rawpower, le16_to_cpu(point->val_qpsk)); + rawpower = max_t(u16, + rawpower, le16_to_cpu(point->val_bpsk)); + rawpower = max_t(u16, + rawpower, le16_to_cpu(point->val_16qam)); + rawpower = max_t(u16, + rawpower, le16_to_cpu(point->val_64qam)); } /* longbow seems to use 1/16 dBm units */ return rawpower / 16; -- cgit v1.2.3 From 390fd9d6c223980190adf44af5c7ba0662ff61e3 Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Sat, 8 Sep 2012 01:28:42 +0200 Subject: p54: fix off-by-one null pointer bug in p54_tx_80211 This patch fixes a regression which was introduced in: "mac80211: move TX station pointer and restructure TX" IP: p54_tx_80211+0x21/0x513 [p54common] Oops: 0000 [#1] SMP Modules linked in: p54usb p54common [...] Pid: 13394, comm: hostapd 3.6.0-rc4-wl+ RIP: 0010:p54_tx_80211+0x21/0x513 RSP: 0018:... EFLAGS: 00010292 [...] Process hostapd Stack: [...] Call Trace: p54_bss_info_changed+0x204/0x21e [p54common] ieee80211_del_station+0x16/0x32 [mac80211] ieee80211_start_ap+0x10f/0x157 [mac80211] nl80211_start_ap+0x315/0x361 [cfg80211] p54_tx_80211 function is called as part of the beacon update. The caller p54_bss_info_changed has to supply a valid tx control struct, or the control->sta will lead to a null pointer dereference. Signed-off-by: Christian Lamparter Signed-off-by: John W. Linville --- drivers/net/wireless/p54/main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/net/wireless/p54') diff --git a/drivers/net/wireless/p54/main.c b/drivers/net/wireless/p54/main.c index 5e91ad06dd5d..2969d5321ca6 100644 --- a/drivers/net/wireless/p54/main.c +++ b/drivers/net/wireless/p54/main.c @@ -139,6 +139,7 @@ static int p54_beacon_format_ie_tim(struct sk_buff *skb) static int p54_beacon_update(struct p54_common *priv, struct ieee80211_vif *vif) { + struct ieee80211_tx_control control = { }; struct sk_buff *beacon; int ret; @@ -158,7 +159,7 @@ static int p54_beacon_update(struct p54_common *priv, * to cancel the old beacon template by hand, instead the firmware * will release the previous one through the feedback mechanism. */ - p54_tx_80211(priv->hw, NULL, beacon); + p54_tx_80211(priv->hw, &control, beacon); priv->tsf_high32 = 0; priv->tsf_low32 = 0; -- cgit v1.2.3 From 960334a7d33ecd54ba5337275dc4ea701d1dbf5e Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Sat, 8 Sep 2012 01:48:19 +0200 Subject: p54: connect to 11w protected networks Previously, it was not possible to connect to networks which requires 11w to be supported by the stations. Note: As all current (and old) firmwares corrupt incoming, protected management frames, the decryption offloading needs to be disabled. This will be done automatically if needed. Signed-off-by: Christian Lamparter Signed-off-by: John W. Linville --- drivers/net/wireless/p54/main.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'drivers/net/wireless/p54') diff --git a/drivers/net/wireless/p54/main.c b/drivers/net/wireless/p54/main.c index 2969d5321ca6..aadda99989c0 100644 --- a/drivers/net/wireless/p54/main.c +++ b/drivers/net/wireless/p54/main.c @@ -515,6 +515,17 @@ static int p54_set_key(struct ieee80211_hw *dev, enum set_key_cmd cmd, if (modparam_nohwcrypt) return -EOPNOTSUPP; + if (key->flags & IEEE80211_KEY_FLAG_RX_MGMT) { + /* + * Unfortunately most/all firmwares are trying to decrypt + * incoming management frames if a suitable key can be found. + * However, in doing so the data in these frames gets + * corrupted. So, we can't have firmware supported crypto + * offload in this case. + */ + return -EOPNOTSUPP; + } + mutex_lock(&priv->conf_mutex); if (cmd == SET_KEY) { switch (key->cipher) { @@ -738,6 +749,7 @@ struct ieee80211_hw *p54_init_common(size_t priv_data_len) IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_SUPPORTS_PS | IEEE80211_HW_PS_NULLFUNC_STACK | + IEEE80211_HW_MFP_CAPABLE | IEEE80211_HW_REPORTS_TX_ACK_STATUS; dev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | -- cgit v1.2.3