rt2x00: Refactor beacon code to make use of start- and stop_queue
PATCH 1/21 rt2x00: Refactor beacon code to make use of start- and stop_queue
From: Helmut Schaa <[email protected]>
This patch allows to dynamically remove beaconing interfaces without shutting beaconing down on all interfaces.
The only place to start and stop beaconing are now the start- and stop_queue callbacks. Hence, we can remove some register writes during interface bring up (config_intf) and only write the correct sync mode to the register there.
When multiple beaconing interfaces are present we should enable beaconing as soon as mac80211 enables beaconing on at least one of them. The beacon queue gets stopped when the last beaconing interface was stopped by mac80211. Therefore, introduce another interface counter to keep track ot the number of enabled beaconing interfaces and start or stop the beacon queue accordingly.
To allow single interfaces to stop beaconing, add a new driver callback clear_beacon to clear a single interface's beacon without affecting the other interfaces. Don't overload the clear_entry callback for clearing beacons as that would introduce additional overhead (check for each TX queue) into the clear_entry callback which is used on the drivers TX/RX hotpaths.
Furthermore, the write beacon callback doesn't need to enable beaconing anymore but since beaconing should be disabled while a new beacon is written or cleared we still disable beacon generation and enable it afterwards again in the driver specific callbacks. However, beacon related interrupts should not be disabled/enabled here, that's solely done from the start- and stop queue callbacks. It would be nice to stop the beacon queue just before the beacon update and enable it afterwards in rt2x00queue itself instead of the current implementation that relies on the driver doing the right thing. However, since start- and stop_queue are mutex protected we cannot use them for atomic beacon updates.
--- drivers/net/wireless/rt2x00/rt2400pci.c | 4 -- drivers/net/wireless/rt2x00/rt2500pci.c | 4 -- drivers/net/wireless/rt2x00/rt2500usb.c | 2 - drivers/net/wireless/rt2x00/rt2800lib.c | 67 ++++++++++++++++------------- drivers/net/wireless/rt2x00/rt2800lib.h | 1 + drivers/net/wireless/rt2x00/rt2800pci.c | 9 ++++ drivers/net/wireless/rt2x00/rt2800usb.c | 1 + drivers/net/wireless/rt2x00/rt2x00.h | 4 ++ drivers/net/wireless/rt2x00/rt2x00dev.c | 4 +- drivers/net/wireless/rt2x00/rt2x00lib.h | 12 ++++- drivers/net/wireless/rt2x00/rt2x00mac.c | 41 ++++++++++++++++- drivers/net/wireless/rt2x00/rt2x00queue.c | 38 ++++++++++++---- drivers/net/wireless/rt2x00/rt61pci.c | 41 ++++++++++++------ drivers/net/wireless/rt2x00/rt73usb.c | 42 ++++++++++++------ 14 files changed, 185 insertions(+), 85 deletions(-)
drivers/net/wireless/rt2x00/rt2400pci.c +0 -4
@@ -305,9 +305,7 @@ static void rt2400pci_config_intf(struct rt2x00_dev *rt2x00dev,* Enable synchronisation. */ rt2x00pci_register_read (rt2x00dev,CSR14 , ®); -rt2x00_set_field32 (®,CSR14_TSF_COUNT ,1 );rt2x00_set_field32 (®,CSR14_TSF_SYNC , conf->sync); -rt2x00_set_field32 (®,CSR14_TBCN ,1 );rt2x00pci_register_write (rt2x00dev,CSR14 , reg); }
@@ -1183,8 +1181,6 @@ static void rt2400pci_write_beacon(struct queue_entry *entry,/* * Enable beaconing again. */ -rt2x00_set_field32 (®,CSR14_TSF_COUNT ,1 ); -rt2x00_set_field32 (®,CSR14_TBCN ,1 );rt2x00_set_field32 (®,CSR14_BEACON_GEN ,1 );rt2x00pci_register_write (rt2x00dev,CSR14 , reg); }
drivers/net/wireless/rt2x00/rt2500pci.c +0 -4
@@ -311,9 +311,7 @@ static void rt2500pci_config_intf(struct rt2x00_dev *rt2x00dev,* Enable synchronisation. */ rt2x00pci_register_read (rt2x00dev,CSR14 , ®); -rt2x00_set_field32 (®,CSR14_TSF_COUNT ,1 );rt2x00_set_field32 (®,CSR14_TSF_SYNC , conf->sync); -rt2x00_set_field32 (®,CSR14_TBCN ,1 );rt2x00pci_register_write (rt2x00dev,CSR14 , reg); }
@@ -1337,8 +1335,6 @@ static void rt2500pci_write_beacon(struct queue_entry *entry,/* * Enable beaconing again. */ -rt2x00_set_field32 (®,CSR14_TSF_COUNT ,1 ); -rt2x00_set_field32 (®,CSR14_TBCN ,1 );rt2x00_set_field32 (®,CSR14_BEACON_GEN ,1 );rt2x00pci_register_write (rt2x00dev,CSR14 , reg); }
drivers/net/wireless/rt2x00/rt2500usb.c +0 -2
@@ -478,9 +478,7 @@ static void rt2500usb_config_intf(struct rt2x00_dev *rt2x00dev,rt2500usb_register_write (rt2x00dev,TXRX_CSR18 , reg);rt2500usb_register_read (rt2x00dev,TXRX_CSR19 , ®); -rt2x00_set_field16 (®,TXRX_CSR19_TSF_COUNT ,1 );rt2x00_set_field16 (®,TXRX_CSR19_TSF_SYNC , conf->sync); -rt2x00_set_field16 (®,TXRX_CSR19_TBCN ,1 );rt2500usb_register_write (rt2x00dev,TXRX_CSR19 , reg); }
drivers/net/wireless/rt2x00/rt2800lib.c +37 -30
102 lines not on this page · raw message
drivers/net/wireless/rt2x00/rt2800lib.h +1 -0
@@ -156,6 +156,7 @@ void rt2800_txdone(struct rt2x00_dev *rt2x00dev);void rt2800_txdone_entry (struct queue_entry *entry,u32 status);void rt2800_write_beacon (struct queue_entry *entry,struct txentry_desc *txdesc); +void rt2800_clear_beacon (struct queue_entry *entry);extern const struct rt2x00debug rt2800_rt2x00debug;
drivers/net/wireless/rt2x00/rt2800pci.c +9 -0
@@ -205,6 +205,10 @@ static void rt2800pci_start_queue(struct data_queue *queue)rt2x00_set_field32 (®,BCN_TIME_CFG_TBTT_ENABLE ,1 );rt2x00_set_field32 (®,BCN_TIME_CFG_BEACON_GEN ,1 );rt2800_register_write (rt2x00dev,BCN_TIME_CFG , reg); + +rt2800_register_read (rt2x00dev,INT_TIMER_EN , ®); +rt2x00_set_field32 (®,INT_TIMER_EN_PRE_TBTT_TIMER ,1 ); +rt2800_register_write (rt2x00dev,INT_TIMER_EN , reg);break ;default :break ;
@@ -250,6 +254,10 @@ static void rt2800pci_stop_queue(struct data_queue *queue)rt2x00_set_field32 (®,BCN_TIME_CFG_TBTT_ENABLE ,0 );rt2x00_set_field32 (®,BCN_TIME_CFG_BEACON_GEN ,0 );rt2800_register_write (rt2x00dev,BCN_TIME_CFG , reg); + +rt2800_register_read (rt2x00dev,INT_TIMER_EN , ®); +rt2x00_set_field32 (®,INT_TIMER_EN_PRE_TBTT_TIMER ,0 ); +rt2800_register_write (rt2x00dev,INT_TIMER_EN , reg);break ;default :break ;
@@ -974,6 +982,7 @@ static const struct rt2x00lib_ops rt2800pci_rt2x00_ops = { .write_tx_desc = rt2800pci_write_tx_desc, .write_tx_data = rt2800_write_tx_data, .write_beacon = rt2800_write_beacon, + .clear_beacon = rt2800_clear_beacon, .fill_rxdone = rt2800pci_fill_rxdone, .config_shared_key = rt2800_config_shared_key, .config_pairwise_key = rt2800_config_pairwise_key,
drivers/net/wireless/rt2x00/rt2800usb.c +1 -0
@@ -633,6 +633,7 @@ static const struct rt2x00lib_ops rt2800usb_rt2x00_ops = { .write_tx_desc = rt2800usb_write_tx_desc, .write_tx_data = rt2800usb_write_tx_data, .write_beacon = rt2800_write_beacon, + .clear_beacon = rt2800_clear_beacon, .get_tx_data_len = rt2800usb_get_tx_data_len, .fill_rxdone = rt2800usb_fill_rxdone, .config_shared_key = rt2800_config_shared_key,
drivers/net/wireless/rt2x00/rt2x00.h +4 -0
@@ -368,6 +368,7 @@ struct rt2x00_intf {* dedicated beacon entry. */ struct queue_entry *beacon; +bool enable_beacon;/* * Actions that needed rescheduling.
@@ -573,6 +574,7 @@ struct rt2x00lib_ops { struct txentry_desc *txdesc);void (*write_beacon) (struct queue_entry *entry,struct txentry_desc *txdesc); +void (*clear_beacon) (struct queue_entry *entry);int (*get_tx_data_len) (struct queue_entry *entry);/*
@@ -788,10 +790,12 @@ struct rt2x00_dev {* - Open ap interface count. * - Open sta interface count. * - Association count. +* - Beaconing enabled count. */ unsigned int intf_ap_count;unsigned int intf_sta_count;unsigned int intf_associated; +unsigned int intf_beaconing;/* * Link quality
drivers/net/wireless/rt2x00/rt2x00dev.c +2 -2
@@ -121,7 +121,7 @@ static void rt2x00lib_intf_scheduled_iter(void *data, u8 *mac,return ;if (test_and_clear_bit (DELAYED_UPDATE_BEACON , &intf->delayed_flags)) -rt2x00queue_update_beacon (rt2x00dev, vif, true); +rt2x00queue_update_beacon (rt2x00dev, vif); }static void rt2x00lib_intf_scheduled (struct work_struct *work)
@@ -174,7 +174,7 @@ static void rt2x00lib_beaconupdate_iter(void *data, u8 *mac, vif->type != NL80211_IFTYPE_WDS)return ; -rt2x00queue_update_beacon (rt2x00dev, vif, true); +rt2x00queue_update_beacon (rt2x00dev, vif); }void rt2x00lib_beacondone (struct rt2x00_dev *rt2x00dev)
drivers/net/wireless/rt2x00/rt2x00lib.h +9 -3
@@ -160,11 +160,17 @@ int rt2x00queue_write_tx_frame(struct data_queue *queue, struct sk_buff *skb,* rt2x00queue_update_beacon - Send new beacon from mac80211 to hardware * @rt2x00dev: Pointer to &struct rt2x00_dev. * @vif: Interface for which the beacon should be updated. -* @enable_beacon: Enable beaconing */ int rt2x00queue_update_beacon (struct rt2x00_dev *rt2x00dev, -struct ieee80211_vif *vif, -const bool enable_beacon); +struct ieee80211_vif *vif); + +/** +* rt2x00queue_clear_beacon - Clear beacon in hardware +* @rt2x00dev: Pointer to &struct rt2x00_dev. +* @vif: Interface for which the beacon should be updated. +*/ +int rt2x00queue_clear_beacon (struct rt2x00_dev *rt2x00dev, +struct ieee80211_vif *vif);/** * rt2x00queue_index_inc - Index incrementation function
drivers/net/wireless/rt2x00/rt2x00mac.c +38 -3
47 lines not on this page · raw message
drivers/net/wireless/rt2x00/rt2x00queue.c +29 -9
@@ -566,9 +566,35 @@ int rt2x00queue_write_tx_frame(struct data_queue *queue, struct sk_buff *skb,return 0 ; } +int rt2x00queue_clear_beacon (struct rt2x00_dev *rt2x00dev, +struct ieee80211_vif *vif) +{ +struct rt2x00_intf *intf =vif_to_intf (vif); + +if (unlikely (!intf->beacon)) +return -ENOBUFS ; + +mutex_lock (&intf->beacon_skb_mutex); + +/* +* Clean up the beacon skb. +*/ +rt2x00queue_free_skb (intf->beacon); + +/* +* Clear beacon (single bssid devices don't need to clear the beacon +* since the beacon queue will get stopped anyway). +*/ +if (rt2x00dev->ops->lib->clear_beacon) + rt2x00dev->ops->lib->clear_beacon (intf->beacon); + +mutex_unlock (&intf->beacon_skb_mutex); + +return 0 ; +} +int rt2x00queue_update_beacon (struct rt2x00_dev *rt2x00dev, -struct ieee80211_vif *vif, -const bool enable_beacon) +struct ieee80211_vif *vif) {struct rt2x00_intf *intf =vif_to_intf (vif);struct skb_frame_desc *skbdesc;
@@ -584,12 +610,6 @@ int rt2x00queue_update_beacon(struct rt2x00_dev *rt2x00dev,*/ rt2x00queue_free_skb (intf->beacon); -if (!enable_beacon) { -rt2x00queue_stop_queue (intf->beacon->queue); -mutex_unlock (&intf->beacon_skb_mutex); -return 0 ; - } - intf->beacon->skb =ieee80211_beacon_get (rt2x00dev->hw, vif);if (!intf->beacon->skb) {mutex_unlock (&intf->beacon_skb_mutex);
@@ -611,7 +631,7 @@ int rt2x00queue_update_beacon(struct rt2x00_dev *rt2x00dev, skbdesc->entry = intf->beacon;/* -* Send beacon to hardware and enable beacon genaration.. +* Send beacon to hardware. */ rt2x00dev->ops->lib->write_beacon (intf->beacon, &txdesc);
drivers/net/wireless/rt2x00/rt61pci.c +27 -14
73 lines not on this page · raw message
drivers/net/wireless/rt2x00/rt73usb.c +28 -14
74 lines not on this page · raw message
Signature · 1 line
1.7.2.3
PATCH 2/21 rt2x00: Introduce beacon_update_locked that requires caller locking
From: Helmut Schaa <[email protected]>
Introduce a beacon_update_locked function that does not acquire the according beacon mutex to allow beacon updates from atomic context. The caller has to take care of synchronization.
No functional changes. Just preparation for beacon updates from tasklet context.
--- drivers/net/wireless/rt2x00/rt2x00lib.h | 12 +++++++++++- drivers/net/wireless/rt2x00/rt2x00queue.c | 24 ++++++++++++++++-------- 2 files changed, 27 insertions(+), 9 deletions(-)
drivers/net/wireless/rt2x00/rt2x00lib.h +11 -1
@@ -157,7 +157,8 @@ int rt2x00queue_write_tx_frame(struct data_queue *queue, struct sk_buff *skb, bool local);/** -* rt2x00queue_update_beacon - Send new beacon from mac80211 to hardware +* rt2x00queue_update_beacon - Send new beacon from mac80211 +* to hardware. Handles locking by itself (mutex). * @rt2x00dev: Pointer to &struct rt2x00_dev. * @vif: Interface for which the beacon should be updated. */
@@ -165,6 +166,15 @@ int rt2x00queue_update_beacon(struct rt2x00_dev *rt2x00dev, struct ieee80211_vif *vif);/** +* rt2x00queue_update_beacon_locked - Send new beacon from mac80211 +* to hardware. Caller needs to ensure locking. +* @rt2x00dev: Pointer to &struct rt2x00_dev. +* @vif: Interface for which the beacon should be updated. +*/ +int rt2x00queue_update_beacon_locked (struct rt2x00_dev *rt2x00dev, +struct ieee80211_vif *vif); + +/** * rt2x00queue_clear_beacon - Clear beacon in hardware * @rt2x00dev: Pointer to &struct rt2x00_dev. * @vif: Interface for which the beacon should be updated.
drivers/net/wireless/rt2x00/rt2x00queue.c +16 -8
@@ -593,8 +593,8 @@ int rt2x00queue_clear_beacon(struct rt2x00_dev *rt2x00dev,return 0 ; } -int rt2x00queue_update_beacon (struct rt2x00_dev *rt2x00dev, -struct ieee80211_vif *vif) +int rt2x00queue_update_beacon_locked (struct rt2x00_dev *rt2x00dev, +struct ieee80211_vif *vif) {struct rt2x00_intf *intf =vif_to_intf (vif);struct skb_frame_desc *skbdesc;
@@ -603,18 +603,14 @@ int rt2x00queue_update_beacon(struct rt2x00_dev *rt2x00dev,if (unlikely (!intf->beacon))return -ENOBUFS ; -mutex_lock (&intf->beacon_skb_mutex); -/* * Clean up the beacon skb. */ rt2x00queue_free_skb (intf->beacon); intf->beacon->skb =ieee80211_beacon_get (rt2x00dev->hw, vif); -if (!intf->beacon->skb) { -mutex_unlock (&intf->beacon_skb_mutex); +if (!intf->beacon->skb)return -ENOMEM ; - }/* * Copy all TX descriptor information into txdesc,
@@ -635,9 +631,21 @@ int rt2x00queue_update_beacon(struct rt2x00_dev *rt2x00dev,*/ rt2x00dev->ops->lib->write_beacon (intf->beacon, &txdesc); +return 0 ; + +} + +int rt2x00queue_update_beacon (struct rt2x00_dev *rt2x00dev, +struct ieee80211_vif *vif) +{ +struct rt2x00_intf *intf =vif_to_intf (vif); +int ret; + +mutex_lock (&intf->beacon_skb_mutex); + ret =rt2x00queue_update_beacon_locked (rt2x00dev, vif);mutex_unlock (&intf->beacon_skb_mutex); -return 0 ; +return ret; } void rt2x00queue_for_each_entry(struct data_queue *queue,
Signature · 1 line
1.7.2.3
PATCH 3/21 rt2x00: Limit beacon updates in bss_info_changed to USB devices
From: Helmut Schaa <[email protected]>
Currently there are two places that trigger a beacon update on PCI devices. The bss_info_changed callback and the periodic update triggered by the TBTT or PRETBTT interrupt.
Since the next TBTT or PRETBTT interrupt will periodically fetch an updated beacon remove the update_beacon call in the bss_info_changed callback for PCI devices.
In the worst case it will take one beacon interval longer to fetch the new beacon then before. For devices that have a PRETBTT interrupt there should be no change at all.
--- drivers/net/wireless/rt2x00/rt2x00mac.c | 5 +++-- 1 files changed, 3 insertions(+), 2 deletions(-)
drivers/net/wireless/rt2x00/rt2x00mac.c +3 -2
@@ -617,9 +617,10 @@ void rt2x00mac_bss_info_changed(struct ieee80211_hw *hw, bss_conf->bssid);/* -* Update the beacon. +* Update the beacon. This is only required on USB devices. PCI +* devices fetch beacons periodically. */ -if (changes &BSS_CHANGED_BEACON ) +if (changes &BSS_CHANGED_BEACON &&rt2x00_is_usb (rt2x00dev))rt2x00queue_update_beacon (rt2x00dev, vif);/*
Signature · 1 line
1.7.2.3
PATCH 4/21 rt2x00: Make periodic beacon updates for PCI devices atomic
From: Helmut Schaa <[email protected]>
Allow the beacondone and pretbtt functions to update the beacon from atomic context by using the beacon update functions with caller locking.
This is a preparation for moving the periodic beacon handling into tasklets that require atomic context.
--- drivers/net/wireless/rt2x00/rt2x00dev.c | 26 ++++++++++++++++---------- 1 files changed, 16 insertions(+), 10 deletions(-)
drivers/net/wireless/rt2x00/rt2x00dev.c +16 -10
@@ -174,7 +174,13 @@ static void rt2x00lib_beaconupdate_iter(void *data, u8 *mac, vif->type != NL80211_IFTYPE_WDS)return ; -rt2x00queue_update_beacon (rt2x00dev, vif); +/* +* Update the beacon without locking. This is safe on PCI devices +* as they only update the beacon periodically here. This should +* never be called for USB devices. +*/ +WARN_ON (rt2x00_is_usb (rt2x00dev)); +rt2x00queue_update_beacon_locked (rt2x00dev, vif); }void rt2x00lib_beacondone (struct rt2x00_dev *rt2x00dev)
@@ -183,9 +189,9 @@ void rt2x00lib_beacondone(struct rt2x00_dev *rt2x00dev)return ;/* send buffered bc/mc frames out for every bssid */ -ieee80211_iterate_active_interfaces (rt2x00dev->hw, - rt2x00lib_bc_buffer_iter, - rt2x00dev); +ieee80211_iterate_active_interfaces_atomic (rt2x00dev->hw, + rt2x00lib_bc_buffer_iter, + rt2x00dev);/* * Devices with pre tbtt interrupt don't need to update the beacon * here as they will fetch the next beacon directly prior to
@@ -195,9 +201,9 @@ void rt2x00lib_beacondone(struct rt2x00_dev *rt2x00dev)return ;/* fetch next beacon */ -ieee80211_iterate_active_interfaces (rt2x00dev->hw, - rt2x00lib_beaconupdate_iter, - rt2x00dev); +ieee80211_iterate_active_interfaces_atomic (rt2x00dev->hw, + rt2x00lib_beaconupdate_iter, + rt2x00dev); }EXPORT_SYMBOL_GPL (rt2x00lib_beacondone);
@@ -207,9 +213,9 @@ void rt2x00lib_pretbtt(struct rt2x00_dev *rt2x00dev)return ;/* fetch next beacon */ -ieee80211_iterate_active_interfaces (rt2x00dev->hw, - rt2x00lib_beaconupdate_iter, - rt2x00dev); +ieee80211_iterate_active_interfaces_atomic (rt2x00dev->hw, + rt2x00lib_beaconupdate_iter, + rt2x00dev); }EXPORT_SYMBOL_GPL (rt2x00lib_pretbtt);
Signature · 1 line
1.7.2.3
PATCH 5/21 rt2x00: Introduce tasklets for interrupt handling
From: Helmut Schaa <[email protected]>
No functional changes, just preparation for moving interrupt handling to tasklets. The tasklets are disabled by default. Drivers making use of them need to enable the tasklets when the device state is set to IRQ_ON.
--- drivers/net/wireless/rt2x00/rt2x00.h | 13 +++++++++++++ drivers/net/wireless/rt2x00/rt2x00dev.c | 21 +++++++++++++++++++++ 2 files changed, 34 insertions(+), 0 deletions(-)
drivers/net/wireless/rt2x00/rt2x00.h +13 -0
@@ -520,6 +520,10 @@ struct rt2x00lib_ops {* TX status tasklet handler. */ void (*txstatus_tasklet) (unsigned long data); +void (*pretbtt_tasklet) (unsigned long data); +void (*tbtt_tasklet) (unsigned long data); +void (*rxdone_tasklet) (unsigned long data); +void (*autowake_tasklet) (unsigned long data);/* * Device init handlers.
@@ -905,6 +909,15 @@ struct rt2x00_dev {* Tasklet for processing tx status reports (rt2800pci). */ struct tasklet_struct txstatus_tasklet; +struct tasklet_struct pretbtt_tasklet; +struct tasklet_struct tbtt_tasklet; +struct tasklet_struct rxdone_tasklet; +struct tasklet_struct autowake_tasklet; + +/* +* Protect the interrupt mask register. +*/ +spinlock_t irqmask_lock; };/*
drivers/net/wireless/rt2x00/rt2x00dev.c +21 -0
@@ -831,6 +831,26 @@ static int rt2x00lib_probe_hw(struct rt2x00_dev *rt2x00dev) }/* +* Initialize tasklets if used by the driver. Tasklets are +* disabled until the interrupts are turned on. The driver +* has to handle that. +*/ +#define RT2X00_TASKLET_INIT (taskletname) \ + if (rt2x00dev->ops->lib->taskletname) { \ + tasklet_init(&rt2x00dev->taskletname, \ + rt2x00dev->ops->lib->taskletname, \ + (unsigned long)rt2x00dev); \ + tasklet_disable(&rt2x00dev->taskletname); \ + } + +RT2X00_TASKLET_INIT (pretbtt_tasklet); +RT2X00_TASKLET_INIT (tbtt_tasklet); +RT2X00_TASKLET_INIT (rxdone_tasklet); +RT2X00_TASKLET_INIT (autowake_tasklet); + +#undef RT2X00_TASKLET_INIT + +/* * Register HW. */ status =ieee80211_register_hw (rt2x00dev->hw);
@@ -958,6 +978,7 @@ int rt2x00lib_probe_dev(struct rt2x00_dev *rt2x00dev) {int retval = -ENOMEM ; +spin_lock_init (&rt2x00dev->irqmask_lock);mutex_init (&rt2x00dev->csr_mutex);set_bit (DEVICE_STATE_PRESENT , &rt2x00dev->flags);
Signature · 1 line
1.7.2.3
PATCH 6/21 rt2x00: Disable txstatus tasklet by default
From: Helmut Schaa <[email protected]>
Enable the txstatus tasklet when interrupts are enabled and disable it together with the interrupts. Also make the txstatus tasklet useful even without the tx status FIFO and make use of the generic rt2x00 tasklet initialization macro.
--- drivers/net/wireless/rt2x00/rt2800pci.c | 5 ++++- drivers/net/wireless/rt2x00/rt2x00dev.c | 8 +------- 2 files changed, 5 insertions(+), 8 deletions(-)
drivers/net/wireless/rt2x00/rt2800pci.c +4 -1
@@ -416,7 +416,10 @@ static void rt2800pci_toggle_irq(struct rt2x00_dev *rt2x00dev,if (state ==STATE_RADIO_IRQ_ON ) {rt2800_register_read (rt2x00dev,INT_SOURCE_CSR , ®);rt2800_register_write (rt2x00dev,INT_SOURCE_CSR , reg); - } + +tasklet_enable (&rt2x00dev->txstatus_tasklet); + }else if (state ==STATE_RADIO_IRQ_OFF ) +tasklet_disable (&rt2x00dev->txstatus_tasklet);rt2800_register_read (rt2x00dev,INT_MASK_CSR , ®);rt2x00_set_field32 (®,INT_MASK_CSR_RXDELAYINT ,0 );
drivers/net/wireless/rt2x00/rt2x00dev.c +1 -7
@@ -821,13 +821,6 @@ static int rt2x00lib_probe_hw(struct rt2x00_dev *rt2x00dev) GFP_KERNEL);if (status)return status; - -/* tasklet for processing the tx status reports. */ -if (rt2x00dev->ops->lib->txstatus_tasklet) -tasklet_init (&rt2x00dev->txstatus_tasklet, - rt2x00dev->ops->lib->txstatus_tasklet, - (unsigned long )rt2x00dev); - }/*
@@ -843,6 +836,7 @@ static int rt2x00lib_probe_hw(struct rt2x00_dev *rt2x00dev)tasklet_disable (&rt2x00dev->taskletname); \ } +RT2X00_TASKLET_INIT (txstatus_tasklet);RT2X00_TASKLET_INIT (pretbtt_tasklet);RT2X00_TASKLET_INIT (tbtt_tasklet);RT2X00_TASKLET_INIT (rxdone_tasklet);
Signature · 1 line
1.7.2.3
PATCH 7/21 rt2x00: Convert rt2800pci to use tasklets
From: Helmut Schaa <[email protected]>
Fix interrupt processing on slow machines by using individual tasklets for each different device interrupt. This ensures that while a RX or TX status tasklet is scheduled only the according device interrupt is masked and other interrupts such as TBTT can still be processed.
Also, this allows us to use tasklet_hi_schedule for TBTT and PRETBTT processing which is required to not send out beacons with a wrong DTIM count (due to delayed periodic beacon updates). Furthermore, this improves the latency between the TBTT and sending out buffered multi- and broadcast traffic.
As a nice bonus, the interrupt handling overhead is reduced such that rt2800pci gains around 25% more throuhput on a rt3052 MIPS board.
--- drivers/net/wireless/rt2x00/rt2800pci.c | 173 ++++++++++++++++++++----------- 1 files changed, 114 insertions(+), 59 deletions(-)
drivers/net/wireless/rt2x00/rt2800pci.c +114 -59
@@ -200,6 +200,13 @@ static void rt2800pci_start_queue(struct data_queue *queue)rt2800_register_write (rt2x00dev,MAC_SYS_CTRL , reg);break ;case QID_BEACON : +/* +* Allow beacon tasklets to be scheduled for periodic +* beacon updates. +*/ +tasklet_enable (&rt2x00dev->tbtt_tasklet); +tasklet_enable (&rt2x00dev->pretbtt_tasklet); +rt2800_register_read (rt2x00dev,BCN_TIME_CFG , ®);rt2x00_set_field32 (®,BCN_TIME_CFG_TSF_TICKING ,1 );rt2x00_set_field32 (®,BCN_TIME_CFG_TBTT_ENABLE ,1 );
@@ -258,6 +265,12 @@ static void rt2800pci_stop_queue(struct data_queue *queue)rt2800_register_read (rt2x00dev,INT_TIMER_EN , ®);rt2x00_set_field32 (®,INT_TIMER_EN_PRE_TBTT_TIMER ,0 );rt2800_register_write (rt2x00dev,INT_TIMER_EN , reg); + +/* +* Wait for tbtt tasklets to finish. +*/ +tasklet_disable (&rt2x00dev->tbtt_tasklet); +tasklet_disable (&rt2x00dev->pretbtt_tasklet);break ;default :break ;
@@ -408,6 +421,7 @@ static void rt2800pci_toggle_irq(struct rt2x00_dev *rt2x00dev,int mask = (state ==STATE_RADIO_IRQ_ON ) || (state ==STATE_RADIO_IRQ_ON_ISR );u32 reg; +unsigned long flags;/* * When interrupts are being enabled, the interrupt registers
@@ -417,10 +431,16 @@ static void rt2800pci_toggle_irq(struct rt2x00_dev *rt2x00dev,rt2800_register_read (rt2x00dev,INT_SOURCE_CSR , ®);rt2800_register_write (rt2x00dev,INT_SOURCE_CSR , reg); +/* +* Enable tasklets. The beacon related tasklets are +* enabled when the beacon queue is started. +*/ tasklet_enable (&rt2x00dev->txstatus_tasklet); - }else if (state ==STATE_RADIO_IRQ_OFF ) -tasklet_disable (&rt2x00dev->txstatus_tasklet); +tasklet_enable (&rt2x00dev->rxdone_tasklet); +tasklet_enable (&rt2x00dev->autowake_tasklet); + } +spin_lock_irqsave (&rt2x00dev->irqmask_lock, flags);rt2800_register_read (rt2x00dev,INT_MASK_CSR , ®);rt2x00_set_field32 (®,INT_MASK_CSR_RXDELAYINT ,0 );rt2x00_set_field32 (®,INT_MASK_CSR_TXDELAYINT ,0 );
@@ -441,6 +461,17 @@ static void rt2800pci_toggle_irq(struct rt2x00_dev *rt2x00dev,rt2x00_set_field32 (®,INT_MASK_CSR_RX_COHERENT ,0 );rt2x00_set_field32 (®,INT_MASK_CSR_TX_COHERENT ,0 );rt2800_register_write (rt2x00dev,INT_MASK_CSR , reg); +spin_unlock_irqrestore (&rt2x00dev->irqmask_lock, flags); + +if (state ==STATE_RADIO_IRQ_OFF ) { +/* +* Ensure that all tasklets are finished before +* disabling the interrupts. +*/ +tasklet_disable (&rt2x00dev->txstatus_tasklet); +tasklet_disable (&rt2x00dev->rxdone_tasklet); +tasklet_disable (&rt2x00dev->autowake_tasklet); + } }static int rt2800pci_init_registers (struct rt2x00_dev *rt2x00dev)
@@ -721,45 +752,60 @@ static void rt2800pci_txdone(struct rt2x00_dev *rt2x00dev) } } -static void rt2800pci_txstatus_tasklet (unsigned long data) -{ -rt2800pci_txdone ((struct rt2x00_dev *)data); -} - -static irqreturn_t rt2800pci_interrupt_thread (int irq,void *dev_instance) +static void rt2800pci_enable_interrupt (struct rt2x00_dev *rt2x00dev, +struct rt2x00_field32 irq_field) { -struct rt2x00_dev *rt2x00dev = dev_instance; -u32 reg = rt2x00dev->irqvalue[0 ]; +unsigned long flags; +u32 reg;/* -* 1 - Pre TBTT interrupt. +* Enable a single interrupt. The interrupt mask register +* access needs locking. */ -if (rt2x00_get_field32 (reg,INT_SOURCE_CSR_PRE_TBTT )) -rt2x00lib_pretbtt (rt2x00dev); +spin_lock_irqsave (&rt2x00dev->irqmask_lock, flags); +rt2800_register_read (rt2x00dev,INT_MASK_CSR , ®); +rt2x00_set_field32 (®, irq_field,1 ); +rt2800_register_write (rt2x00dev,INT_MASK_CSR , reg); +spin_unlock_irqrestore (&rt2x00dev->irqmask_lock, flags); +} -/* -* 2 - Beacondone interrupt. -*/ -if (rt2x00_get_field32 (reg,INT_SOURCE_CSR_TBTT )) -rt2x00lib_beacondone (rt2x00dev); +static void rt2800pci_txstatus_tasklet (unsigned long data) +{ +rt2800pci_txdone ((struct rt2x00_dev *)data);/* -* 3 - Rx ring done interrupt. +* No need to enable the tx status interrupt here as we always +* leave it enabled to minimize the possibility of a tx status +* register overflow. See comment in interrupt handler. */ -if (rt2x00_get_field32 (reg,INT_SOURCE_CSR_RX_DONE )) -rt2x00pci_rxdone (rt2x00dev); +} -/* -* 4 - Auto wakeup interrupt. -*/ -if (rt2x00_get_field32 (reg,INT_SOURCE_CSR_AUTO_WAKEUP )) -rt2800pci_wakeup (rt2x00dev); +static void rt2800pci_pretbtt_tasklet (unsigned long data) +{ +struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; +rt2x00lib_pretbtt (rt2x00dev); +rt2800pci_enable_interrupt (rt2x00dev,INT_MASK_CSR_PRE_TBTT ); +} -/* Enable interrupts again. */ - rt2x00dev->ops->lib->set_device_state (rt2x00dev, -STATE_RADIO_IRQ_ON_ISR ); +static void rt2800pci_tbtt_tasklet (unsigned long data) +{ +struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; +rt2x00lib_beacondone (rt2x00dev); +rt2800pci_enable_interrupt (rt2x00dev,INT_MASK_CSR_TBTT ); +} -return IRQ_HANDLED ; +static void rt2800pci_rxdone_tasklet (unsigned long data) +{ +struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; +rt2x00pci_rxdone (rt2x00dev); +rt2800pci_enable_interrupt (rt2x00dev,INT_MASK_CSR_RX_DONE ); +} + +static void rt2800pci_autowake_tasklet (unsigned long data) +{ +struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; +rt2800pci_wakeup (rt2x00dev); +rt2800pci_enable_interrupt (rt2x00dev,INT_MASK_CSR_AUTO_WAKEUP ); }static void rt2800pci_txstatus_interrupt (struct rt2x00_dev *rt2x00dev)
@@ -805,8 +851,8 @@ static void rt2800pci_txstatus_interrupt(struct rt2x00_dev *rt2x00dev)static irqreturn_t rt2800pci_interrupt (int irq,void *dev_instance) {struct rt2x00_dev *rt2x00dev = dev_instance; -u32 reg; -irqreturn_t ret =IRQ_HANDLED ; +u32 reg, mask; +unsigned long flags;/* Read status and ACK all interrupts */ rt2800_register_read (rt2x00dev,INT_SOURCE_CSR , ®);
@@ -818,38 +864,44 @@ static irqreturn_t rt2800pci_interrupt(int irq, void *dev_instance)if (!test_bit (DEVICE_STATE_ENABLED_RADIO , &rt2x00dev->flags))return IRQ_HANDLED ; -if (rt2x00_get_field32 (reg,INT_SOURCE_CSR_TX_FIFO_STATUS )) -rt2800pci_txstatus_interrupt (rt2x00dev); +/* +* Since INT_MASK_CSR and INT_SOURCE_CSR use the same bits +* for interrupts and interrupt masks we can just use the value of +* INT_SOURCE_CSR to create the interrupt mask. +*/ + mask = ~reg; -if (rt2x00_get_field32 (reg,INT_SOURCE_CSR_PRE_TBTT ) || -rt2x00_get_field32 (reg,INT_SOURCE_CSR_TBTT ) || -rt2x00_get_field32 (reg,INT_SOURCE_CSR_RX_DONE ) || -rt2x00_get_field32 (reg,INT_SOURCE_CSR_AUTO_WAKEUP )) { +if (rt2x00_get_field32 (reg,INT_SOURCE_CSR_TX_FIFO_STATUS )) { +rt2800pci_txstatus_interrupt (rt2x00dev);/* -* All other interrupts are handled in the interrupt thread. -* Store irqvalue for use in the interrupt thread. +* Never disable the TX_FIFO_STATUS interrupt. */ - rt2x00dev->irqvalue[0 ] = reg; +rt2x00_set_field32 (&mask,INT_MASK_CSR_TX_FIFO_STATUS ,1 ); + } -/* -* Disable interrupts, will be enabled again in the -* interrupt thread. -*/ - rt2x00dev->ops->lib->set_device_state (rt2x00dev, -STATE_RADIO_IRQ_OFF_ISR ); +if (rt2x00_get_field32 (reg,INT_SOURCE_CSR_PRE_TBTT )) +tasklet_hi_schedule (&rt2x00dev->pretbtt_tasklet); -/* -* Leave the TX_FIFO_STATUS interrupt enabled to not lose any -* tx status reports. -*/ -rt2800_register_read (rt2x00dev,INT_MASK_CSR , ®); -rt2x00_set_field32 (®,INT_MASK_CSR_TX_FIFO_STATUS ,1 ); -rt2800_register_write (rt2x00dev,INT_MASK_CSR , reg); +if (rt2x00_get_field32 (reg,INT_SOURCE_CSR_TBTT )) +tasklet_hi_schedule (&rt2x00dev->tbtt_tasklet); - ret =IRQ_WAKE_THREAD ; - } +if (rt2x00_get_field32 (reg,INT_SOURCE_CSR_RX_DONE )) +tasklet_schedule (&rt2x00dev->rxdone_tasklet); -return ret; +if (rt2x00_get_field32 (reg,INT_SOURCE_CSR_AUTO_WAKEUP )) +tasklet_schedule (&rt2x00dev->autowake_tasklet); + +/* +* Disable all interrupts for which a tasklet was scheduled right now, +* the tasklet will reenable the appropriate interrupts. +*/ +spin_lock_irqsave (&rt2x00dev->irqmask_lock, flags); +rt2800_register_read (rt2x00dev,INT_MASK_CSR , ®); + reg &= mask; +rt2800_register_write (rt2x00dev,INT_MASK_CSR , reg); +spin_unlock_irqrestore (&rt2x00dev->irqmask_lock, flags); + +return IRQ_HANDLED ; }/*
@@ -964,8 +1016,11 @@ static const struct rt2800_ops rt2800pci_rt2800_ops = {static const struct rt2x00lib_ops rt2800pci_rt2x00_ops = { .irq_handler = rt2800pci_interrupt, - .irq_handler_thread = rt2800pci_interrupt_thread, - .txstatus_tasklet = rt2800pci_txstatus_tasklet, + .txstatus_tasklet = rt2800pci_txstatus_tasklet, + .pretbtt_tasklet = rt2800pci_pretbtt_tasklet, + .tbtt_tasklet = rt2800pci_tbtt_tasklet, + .rxdone_tasklet = rt2800pci_rxdone_tasklet, + .autowake_tasklet = rt2800pci_autowake_tasklet, .probe_hw = rt2800pci_probe_hw, .get_firmware_name = rt2800pci_get_firmware_name, .check_firmware = rt2800_check_firmware,
Signature · 1 line
1.7.2.3
PATCH 8/21 rt2x00: Convert rt61pci to use tasklets
From: Helmut Schaa <[email protected]>
Fix interrupt processing on slow machines by using individual tasklets for each different device interrupt. This ensures that while a RX or TX status tasklet is scheduled only the according device interrupt is masked and other interrupts such as TBTT can still be processed.
Also, this allows us to use tasklet_hi_schedule for TBTT processing which is required to not send out beacons with a wrong DTIM count (due to delayed periodic beacon updates). Furthermore, this improves the latency between the TBTT and sending out buffered multi- and broadcast traffic.
As a nice bonus, the interrupt handling overhead should be much lower.
Compile-tested only.
--- drivers/net/wireless/rt2x00/rt61pci.c | 175 ++++++++++++++++++++++++--------- 1 files changed, 130 insertions(+), 45 deletions(-)
drivers/net/wireless/rt2x00/rt61pci.c +130 -45
@@ -1142,6 +1142,11 @@ static void rt61pci_start_queue(struct data_queue *queue)rt2x00pci_register_write (rt2x00dev,TXRX_CSR0 , reg);break ;case QID_BEACON : +/* +* Allow the tbtt tasklet to be scheduled. +*/ +tasklet_enable (&rt2x00dev->tbtt_tasklet); +rt2x00pci_register_read (rt2x00dev,TXRX_CSR9 , ®);rt2x00_set_field32 (®,TXRX_CSR9_TSF_TICKING ,1 );rt2x00_set_field32 (®,TXRX_CSR9_TBTT_ENABLE ,1 );
@@ -1221,6 +1226,11 @@ static void rt61pci_stop_queue(struct data_queue *queue)rt2x00_set_field32 (®,TXRX_CSR9_TBTT_ENABLE ,0 );rt2x00_set_field32 (®,TXRX_CSR9_BEACON_GEN ,0 );rt2x00pci_register_write (rt2x00dev,TXRX_CSR9 , reg); + +/* +* Wait for possibly running tbtt tasklets. +*/ +tasklet_disable (&rt2x00dev->tbtt_tasklet);break ;default :break ;
@@ -1710,6 +1720,7 @@ static void rt61pci_toggle_irq(struct rt2x00_dev *rt2x00dev,int mask = (state ==STATE_RADIO_IRQ_OFF ) || (state ==STATE_RADIO_IRQ_OFF_ISR );u32 reg; +unsigned long flags;/* * When interrupts are being enabled, the interrupt registers
@@ -1721,12 +1732,21 @@ static void rt61pci_toggle_irq(struct rt2x00_dev *rt2x00dev,rt2x00pci_register_read (rt2x00dev,MCU_INT_SOURCE_CSR , ®);rt2x00pci_register_write (rt2x00dev,MCU_INT_SOURCE_CSR , reg); + +/* +* Enable tasklets. +*/ +tasklet_enable (&rt2x00dev->txstatus_tasklet); +tasklet_enable (&rt2x00dev->rxdone_tasklet); +tasklet_enable (&rt2x00dev->autowake_tasklet); }/* * Only toggle the interrupts bits we are going to use. * Non-checked interrupt bits are disabled by default. */ +spin_lock_irqsave (&rt2x00dev->irqmask_lock, flags); +rt2x00pci_register_read (rt2x00dev,INT_MASK_CSR , ®);rt2x00_set_field32 (®,INT_MASK_CSR_TXDONE , mask);rt2x00_set_field32 (®,INT_MASK_CSR_RXDONE , mask);
@@ -1746,6 +1766,17 @@ static void rt61pci_toggle_irq(struct rt2x00_dev *rt2x00dev,rt2x00_set_field32 (®,MCU_INT_MASK_CSR_7 , mask);rt2x00_set_field32 (®,MCU_INT_MASK_CSR_TWAKEUP , mask);rt2x00pci_register_write (rt2x00dev,MCU_INT_MASK_CSR , reg); + +spin_unlock_irqrestore (&rt2x00dev->irqmask_lock, flags); + +if (state ==STATE_RADIO_IRQ_OFF ) { +/* +* Ensure that all tasklets are finished. +*/ +tasklet_disable (&rt2x00dev->txstatus_tasklet); +tasklet_disable (&rt2x00dev->rxdone_tasklet); +tasklet_disable (&rt2x00dev->autowake_tasklet); + } }static int rt61pci_enable_radio (struct rt2x00_dev *rt2x00dev)
@@ -2223,61 +2254,80 @@ static void rt61pci_wakeup(struct rt2x00_dev *rt2x00dev)rt61pci_config (rt2x00dev, &libconf,IEEE80211_CONF_CHANGE_PS ); } -static irqreturn_t rt61pci_interrupt_thread (int irq,void *dev_instance) +static void rt61pci_enable_interrupt (struct rt2x00_dev *rt2x00dev, +struct rt2x00_field32 irq_field) { -struct rt2x00_dev *rt2x00dev = dev_instance; -u32 reg = rt2x00dev->irqvalue[0 ]; -u32 reg_mcu = rt2x00dev->irqvalue[1 ]; +unsigned long flags; +u32 reg;/* -* Handle interrupts, walk through all bits -* and run the tasks, the bits are checked in order of -* priority. +* Enable a single interrupt. The interrupt mask register +* access needs locking. */ +spin_lock_irqsave (&rt2x00dev->irqmask_lock, flags); -/* -* 1 - Rx ring done interrupt. -*/ -if (rt2x00_get_field32 (reg,INT_SOURCE_CSR_RXDONE )) -rt2x00pci_rxdone (rt2x00dev); +rt2x00pci_register_read (rt2x00dev,INT_MASK_CSR , ®); +rt2x00_set_field32 (®, irq_field,0 ); +rt2x00pci_register_write (rt2x00dev,INT_MASK_CSR , reg); -/* -* 2 - Tx ring done interrupt. -*/ -if (rt2x00_get_field32 (reg,INT_SOURCE_CSR_TXDONE )) -rt61pci_txdone (rt2x00dev); +spin_unlock_irqrestore (&rt2x00dev->irqmask_lock, flags); +} -/* -* 3 - Handle MCU command done. -*/ -if (reg_mcu) -rt2x00pci_register_write (rt2x00dev, -M2H_CMD_DONE_CSR ,0xffffffff ); +static void rt61pci_enable_mcu_interrupt (struct rt2x00_dev *rt2x00dev, +struct rt2x00_field32 irq_field) +{ +unsigned long flags; +u32 reg;/* -* 4 - MCU Autowakeup interrupt. +* Enable a single MCU interrupt. The interrupt mask register +* access needs locking. */ -if (rt2x00_get_field32 (reg_mcu,MCU_INT_SOURCE_CSR_TWAKEUP )) -rt61pci_wakeup (rt2x00dev); +spin_lock_irqsave (&rt2x00dev->irqmask_lock, flags); -/* -* 5 - Beacon done interrupt. -*/ -if (rt2x00_get_field32 (reg,INT_SOURCE_CSR_BEACON_DONE )) -rt2x00lib_beacondone (rt2x00dev); +rt2x00pci_register_read (rt2x00dev,MCU_INT_MASK_CSR , ®); +rt2x00_set_field32 (®, irq_field,0 ); +rt2x00pci_register_write (rt2x00dev,MCU_INT_MASK_CSR , reg); -/* Enable interrupts again. */ - rt2x00dev->ops->lib->set_device_state (rt2x00dev, -STATE_RADIO_IRQ_ON_ISR ); -return IRQ_HANDLED ; +spin_unlock_irqrestore (&rt2x00dev->irqmask_lock, flags); } +static void rt61pci_txstatus_tasklet (unsigned long data) +{ +struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; +rt61pci_txdone (rt2x00dev); +rt61pci_enable_interrupt (rt2x00dev,INT_MASK_CSR_TXDONE ); +} + +static void rt61pci_tbtt_tasklet (unsigned long data) +{ +struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; +rt2x00lib_beacondone (rt2x00dev); +rt61pci_enable_interrupt (rt2x00dev,INT_MASK_CSR_BEACON_DONE ); +} + +static void rt61pci_rxdone_tasklet (unsigned long data) +{ +struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; +rt2x00pci_rxdone (rt2x00dev); +rt61pci_enable_interrupt (rt2x00dev,INT_MASK_CSR_RXDONE ); +} + +static void rt61pci_autowake_tasklet (unsigned long data) +{ +struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; +rt61pci_wakeup (rt2x00dev); +rt2x00pci_register_write (rt2x00dev, +M2H_CMD_DONE_CSR ,0xffffffff ); +rt61pci_enable_mcu_interrupt (rt2x00dev,MCU_INT_MASK_CSR_TWAKEUP ); +}static irqreturn_t rt61pci_interrupt (int irq,void *dev_instance) {struct rt2x00_dev *rt2x00dev = dev_instance; -u32 reg_mcu; -u32 reg; +u32 reg_mcu, mask_mcu; +u32 reg, mask; +unsigned long flags;/* * Get the interrupt sources & saved to local variable.
@@ -2295,14 +2345,46 @@ static irqreturn_t rt61pci_interrupt(int irq, void *dev_instance)if (!test_bit (DEVICE_STATE_ENABLED_RADIO , &rt2x00dev->flags))return IRQ_HANDLED ; -/* Store irqvalues for use in the interrupt thread. */ - rt2x00dev->irqvalue[0 ] = reg; - rt2x00dev->irqvalue[1 ] = reg_mcu; +/* +* Schedule tasklets for interrupt handling. +*/ +if (rt2x00_get_field32 (reg,INT_SOURCE_CSR_RXDONE )) +tasklet_schedule (&rt2x00dev->rxdone_tasklet); + +if (rt2x00_get_field32 (reg,INT_SOURCE_CSR_TXDONE )) +tasklet_schedule (&rt2x00dev->txstatus_tasklet); + +if (rt2x00_get_field32 (reg,INT_SOURCE_CSR_BEACON_DONE )) +tasklet_hi_schedule (&rt2x00dev->tbtt_tasklet); + +if (rt2x00_get_field32 (reg_mcu,MCU_INT_SOURCE_CSR_TWAKEUP )) +tasklet_schedule (&rt2x00dev->autowake_tasklet); + +/* +* Since INT_MASK_CSR and INT_SOURCE_CSR use the same bits +* for interrupts and interrupt masks we can just use the value of +* INT_SOURCE_CSR to create the interrupt mask. +*/ + mask = reg; + mask_mcu = reg_mcu; + +/* +* Disable all interrupts for which a tasklet was scheduled right now, +* the tasklet will reenable the appropriate interrupts. +*/ +spin_lock_irqsave (&rt2x00dev->irqmask_lock, flags); + +rt2x00pci_register_read (rt2x00dev,INT_MASK_CSR , ®); + reg |= mask; +rt2x00pci_register_write (rt2x00dev,INT_MASK_CSR , reg); -/* Disable interrupts, will be enabled again in the interrupt thread. */ - rt2x00dev->ops->lib->set_device_state (rt2x00dev, -STATE_RADIO_IRQ_OFF_ISR ); -return IRQ_WAKE_THREAD ; +rt2x00pci_register_read (rt2x00dev,MCU_INT_MASK_CSR , ®); + reg |= mask_mcu; +rt2x00pci_register_write (rt2x00dev,MCU_INT_MASK_CSR , reg); + +spin_unlock_irqrestore (&rt2x00dev->irqmask_lock, flags); + +return IRQ_HANDLED ; }/*
@@ -2896,7 +2978,10 @@ static const struct ieee80211_ops rt61pci_mac80211_ops = {static const struct rt2x00lib_ops rt61pci_rt2x00_ops = { .irq_handler = rt61pci_interrupt, - .irq_handler_thread = rt61pci_interrupt_thread, + .txstatus_tasklet = rt61pci_txstatus_tasklet, + .tbtt_tasklet = rt61pci_tbtt_tasklet, + .rxdone_tasklet = rt61pci_rxdone_tasklet, + .autowake_tasklet = rt61pci_autowake_tasklet, .probe_hw = rt61pci_probe_hw, .get_firmware_name = rt61pci_get_firmware_name, .check_firmware = rt61pci_check_firmware,
Signature · 1 line
1.7.2.3
PATCH 9/21 rt2x00: Convert rt2500pci interrupt handling to use tasklets
From: Helmut Schaa <[email protected]>
Fix interrupt processing on slow machines by using individual tasklets for each different device interrupt. This ensures that while a RX or TX status tasklet is scheduled only the according device interrupt is masked and other interrupts such as TBTT can still be processed.
Also, this allows us to use tasklet_hi_schedule for TBTT processing which is required to not send out beacons with a wrong DTIM count (due to delayed periodic beacon updates). Furthermore, this improves the latency between the TBTT and sending out buffered multi- and broadcast traffic.
As a nice bonus, the interrupt handling overhead should be much lower.
Compile-tested only.
--- drivers/net/wireless/rt2x00/rt2500pci.c | 150 +++++++++++++++++++++++-------- 1 files changed, 111 insertions(+), 39 deletions(-)
drivers/net/wireless/rt2x00/rt2500pci.c +111 -39
@@ -735,6 +735,11 @@ static void rt2500pci_start_queue(struct data_queue *queue)rt2x00pci_register_write (rt2x00dev,RXCSR0 , reg);break ;case QID_BEACON : +/* +* Allow the tbtt tasklet to be scheduled. +*/ +tasklet_enable (&rt2x00dev->tbtt_tasklet); +rt2x00pci_register_read (rt2x00dev,CSR14 , ®);rt2x00_set_field32 (®,CSR14_TSF_COUNT ,1 );rt2x00_set_field32 (®,CSR14_TBCN ,1 );
@@ -796,6 +801,11 @@ static void rt2500pci_stop_queue(struct data_queue *queue)rt2x00_set_field32 (®,CSR14_TBCN ,0 );rt2x00_set_field32 (®,CSR14_BEACON_GEN ,0 );rt2x00pci_register_write (rt2x00dev,CSR14 , reg); + +/* +* Wait for possibly running tbtt tasklets. +*/ +tasklet_disable (&rt2x00dev->tbtt_tasklet);break ;default :break ;
@@ -1119,6 +1129,7 @@ static void rt2500pci_toggle_irq(struct rt2x00_dev *rt2x00dev,int mask = (state ==STATE_RADIO_IRQ_OFF ) || (state ==STATE_RADIO_IRQ_OFF_ISR );u32 reg; +unsigned long flags;/* * When interrupts are being enabled, the interrupt registers
@@ -1127,12 +1138,20 @@ static void rt2500pci_toggle_irq(struct rt2x00_dev *rt2x00dev,if (state ==STATE_RADIO_IRQ_ON ) {rt2x00pci_register_read (rt2x00dev,CSR7 , ®);rt2x00pci_register_write (rt2x00dev,CSR7 , reg); + +/* +* Enable tasklets. +*/ +tasklet_enable (&rt2x00dev->txstatus_tasklet); +tasklet_enable (&rt2x00dev->rxdone_tasklet); }/* * Only toggle the interrupts bits we are going to use. * Non-checked interrupt bits are disabled by default. */ +spin_lock_irqsave (&rt2x00dev->irqmask_lock, flags); +rt2x00pci_register_read (rt2x00dev,CSR8 , ®);rt2x00_set_field32 (®,CSR8_TBCN_EXPIRE , mask);rt2x00_set_field32 (®,CSR8_TXDONE_TXRING , mask);
@@ -1140,6 +1159,16 @@ static void rt2500pci_toggle_irq(struct rt2x00_dev *rt2x00dev,rt2x00_set_field32 (®,CSR8_TXDONE_PRIORING , mask);rt2x00_set_field32 (®,CSR8_RXDONE , mask);rt2x00pci_register_write (rt2x00dev,CSR8 , reg); + +spin_unlock_irqrestore (&rt2x00dev->irqmask_lock, flags); + +if (state ==STATE_RADIO_IRQ_OFF ) { +/* +* Ensure that all tasklets are finished. +*/ +tasklet_disable (&rt2x00dev->txstatus_tasklet); +tasklet_disable (&rt2x00dev->rxdone_tasklet); + } }static int rt2500pci_enable_radio (struct rt2x00_dev *rt2x00dev)
@@ -1418,58 +1447,71 @@ static void rt2500pci_txdone(struct rt2x00_dev *rt2x00dev, } } -static irqreturn_t rt2500pci_interrupt_thread (int irq,void *dev_instance) +static void rt2500pci_enable_interrupt (struct rt2x00_dev *rt2x00dev, +struct rt2x00_field32 irq_field) { -struct rt2x00_dev *rt2x00dev = dev_instance; -u32 reg = rt2x00dev->irqvalue[0 ]; +unsigned long flags; +u32 reg;/* -* Handle interrupts, walk through all bits -* and run the tasks, the bits are checked in order of -* priority. +* Enable a single interrupt. The interrupt mask register +* access needs locking. */ +spin_lock_irqsave (&rt2x00dev->irqmask_lock, flags); -/* -* 1 - Beacon timer expired interrupt. -*/ -if (rt2x00_get_field32 (reg,CSR7_TBCN_EXPIRE )) -rt2x00lib_beacondone (rt2x00dev); +rt2x00pci_register_read (rt2x00dev,CSR8 , ®); +rt2x00_set_field32 (®, irq_field,0 ); +rt2x00pci_register_write (rt2x00dev,CSR8 , reg); -/* -* 2 - Rx ring done interrupt. -*/ -if (rt2x00_get_field32 (reg,CSR7_RXDONE )) -rt2x00pci_rxdone (rt2x00dev); +spin_unlock_irqrestore (&rt2x00dev->irqmask_lock, flags); +} -/* -* 3 - Atim ring transmit done interrupt. -*/ -if (rt2x00_get_field32 (reg,CSR7_TXDONE_ATIMRING )) -rt2500pci_txdone (rt2x00dev,QID_ATIM ); +static void rt2500pci_txstatus_tasklet (unsigned long data) +{ +struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; +u32 reg; +unsigned long flags;/* -* 4 - Priority ring transmit done interrupt. +* Handle all tx queues. */ -if (rt2x00_get_field32 (reg,CSR7_TXDONE_PRIORING )) -rt2500pci_txdone (rt2x00dev,QID_AC_VO ); +rt2500pci_txdone (rt2x00dev,QID_ATIM ); +rt2500pci_txdone (rt2x00dev,QID_AC_VO ); +rt2500pci_txdone (rt2x00dev,QID_AC_VI );/* -* 5 - Tx ring transmit done interrupt. +* Enable all TXDONE interrupts again. */ -if (rt2x00_get_field32 (reg,CSR7_TXDONE_TXRING )) -rt2500pci_txdone (rt2x00dev,QID_AC_VI ); +spin_lock_irqsave (&rt2x00dev->irqmask_lock, flags); -/* Enable interrupts again. */ - rt2x00dev->ops->lib->set_device_state (rt2x00dev, -STATE_RADIO_IRQ_ON_ISR ); +rt2x00pci_register_read (rt2x00dev,CSR8 , ®); +rt2x00_set_field32 (®,CSR8_TXDONE_TXRING ,0 ); +rt2x00_set_field32 (®,CSR8_TXDONE_ATIMRING ,0 ); +rt2x00_set_field32 (®,CSR8_TXDONE_PRIORING ,0 ); +rt2x00pci_register_write (rt2x00dev,CSR8 , reg); -return IRQ_HANDLED ; +spin_unlock_irqrestore (&rt2x00dev->irqmask_lock, flags); +} + +static void rt2500pci_tbtt_tasklet (unsigned long data) +{ +struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; +rt2x00lib_beacondone (rt2x00dev); +rt2500pci_enable_interrupt (rt2x00dev,CSR8_TBCN_EXPIRE ); +} + +static void rt2500pci_rxdone_tasklet (unsigned long data) +{ +struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; +rt2x00pci_rxdone (rt2x00dev); +rt2500pci_enable_interrupt (rt2x00dev,CSR8_RXDONE ); }static irqreturn_t rt2500pci_interrupt (int irq,void *dev_instance) {struct rt2x00_dev *rt2x00dev = dev_instance; -u32 reg; +u32 reg, mask; +unsigned long flags;/* * Get the interrupt sources & saved to local variable.
@@ -1484,14 +1526,42 @@ static irqreturn_t rt2500pci_interrupt(int irq, void *dev_instance)if (!test_bit (DEVICE_STATE_ENABLED_RADIO , &rt2x00dev->flags))return IRQ_HANDLED ; -/* Store irqvalues for use in the interrupt thread. */ - rt2x00dev->irqvalue[0 ] = reg; + mask = reg; + +/* +* Schedule tasklets for interrupt handling. +*/ +if (rt2x00_get_field32 (reg,CSR7_TBCN_EXPIRE )) +tasklet_hi_schedule (&rt2x00dev->tbtt_tasklet); + +if (rt2x00_get_field32 (reg,CSR7_RXDONE )) +tasklet_schedule (&rt2x00dev->rxdone_tasklet); + +if (rt2x00_get_field32 (reg,CSR7_TXDONE_ATIMRING ) || +rt2x00_get_field32 (reg,CSR7_TXDONE_PRIORING ) || +rt2x00_get_field32 (reg,CSR7_TXDONE_TXRING )) { +tasklet_schedule (&rt2x00dev->txstatus_tasklet); +/* +* Mask out all txdone interrupts. +*/ +rt2x00_set_field32 (&mask,CSR8_TXDONE_TXRING ,1 ); +rt2x00_set_field32 (&mask,CSR8_TXDONE_ATIMRING ,1 ); +rt2x00_set_field32 (&mask,CSR8_TXDONE_PRIORING ,1 ); + } + +/* +* Disable all interrupts for which a tasklet was scheduled right now, +* the tasklet will reenable the appropriate interrupts. +*/ +spin_lock_irqsave (&rt2x00dev->irqmask_lock, flags); -/* Disable interrupts, will be enabled again in the interrupt thread. */ - rt2x00dev->ops->lib->set_device_state (rt2x00dev, -STATE_RADIO_IRQ_OFF_ISR ); +rt2x00pci_register_read (rt2x00dev,CSR8 , ®); + reg |= mask; +rt2x00pci_register_write (rt2x00dev,CSR8 , reg); + +spin_unlock_irqrestore (&rt2x00dev->irqmask_lock, flags); -return IRQ_WAKE_THREAD ; +return IRQ_HANDLED ; }/*
@@ -1948,7 +2018,9 @@ static const struct ieee80211_ops rt2500pci_mac80211_ops = {static const struct rt2x00lib_ops rt2500pci_rt2x00_ops = { .irq_handler = rt2500pci_interrupt, - .irq_handler_thread = rt2500pci_interrupt_thread, + .txstatus_tasklet = rt2500pci_txstatus_tasklet, + .tbtt_tasklet = rt2500pci_tbtt_tasklet, + .rxdone_tasklet = rt2500pci_rxdone_tasklet, .probe_hw = rt2500pci_probe_hw, .initialize = rt2x00pci_initialize, .uninitialize = rt2x00pci_uninitialize,
Signature · 1 line
1.7.2.3
PATCH 10/21 rt2x00: Convert rt2400pci interrupt handling to use tasklets
From: Helmut Schaa <[email protected]>
Fix interrupt processing on slow machines by using individual tasklets for each different device interrupt. This ensures that while a RX or TX status tasklet is scheduled only the according device interrupt is masked and other interrupts such as TBTT can still be processed.
Also, this allows us to use tasklet_hi_schedule for TBTT processing which is required to not send out beacons with a wrong DTIM count (due to delayed periodic beacon updates). Furthermore, this improves the latency between the TBTT and sending out buffered multi- and broadcast traffic.
As a nice bonus, the interrupt handling overhead should be much lower.
Compile-tested only.
--- drivers/net/wireless/rt2x00/rt2400pci.c | 154 +++++++++++++++++++++++-------- 1 files changed, 115 insertions(+), 39 deletions(-)
drivers/net/wireless/rt2x00/rt2400pci.c +115 -39
@@ -645,6 +645,11 @@ static void rt2400pci_start_queue(struct data_queue *queue)rt2x00pci_register_write (rt2x00dev,RXCSR0 , reg);break ;case QID_BEACON : +/* +* Allow the tbtt tasklet to be scheduled. +*/ +tasklet_enable (&rt2x00dev->tbtt_tasklet); +rt2x00pci_register_read (rt2x00dev,CSR14 , ®);rt2x00_set_field32 (®,CSR14_TSF_COUNT ,1 );rt2x00_set_field32 (®,CSR14_TBCN ,1 );
@@ -706,6 +711,11 @@ static void rt2400pci_stop_queue(struct data_queue *queue)rt2x00_set_field32 (®,CSR14_TBCN ,0 );rt2x00_set_field32 (®,CSR14_BEACON_GEN ,0 );rt2x00pci_register_write (rt2x00dev,CSR14 , reg); + +/* +* Wait for possibly running tbtt tasklets. +*/ +tasklet_disable (&rt2x00dev->tbtt_tasklet);break ;default :break ;
@@ -964,6 +974,7 @@ static void rt2400pci_toggle_irq(struct rt2x00_dev *rt2x00dev,int mask = (state ==STATE_RADIO_IRQ_OFF ) || (state ==STATE_RADIO_IRQ_OFF_ISR );u32 reg; +unsigned long flags;/* * When interrupts are being enabled, the interrupt registers
@@ -972,12 +983,20 @@ static void rt2400pci_toggle_irq(struct rt2x00_dev *rt2x00dev,if (state ==STATE_RADIO_IRQ_ON ) {rt2x00pci_register_read (rt2x00dev,CSR7 , ®);rt2x00pci_register_write (rt2x00dev,CSR7 , reg); + +/* +* Enable tasklets. +*/ +tasklet_enable (&rt2x00dev->txstatus_tasklet); +tasklet_enable (&rt2x00dev->rxdone_tasklet); }/* * Only toggle the interrupts bits we are going to use. * Non-checked interrupt bits are disabled by default. */ +spin_lock_irqsave (&rt2x00dev->irqmask_lock, flags); +rt2x00pci_register_read (rt2x00dev,CSR8 , ®);rt2x00_set_field32 (®,CSR8_TBCN_EXPIRE , mask);rt2x00_set_field32 (®,CSR8_TXDONE_TXRING , mask);
@@ -985,6 +1004,17 @@ static void rt2400pci_toggle_irq(struct rt2x00_dev *rt2x00dev,rt2x00_set_field32 (®,CSR8_TXDONE_PRIORING , mask);rt2x00_set_field32 (®,CSR8_RXDONE , mask);rt2x00pci_register_write (rt2x00dev,CSR8 , reg); + +spin_unlock_irqrestore (&rt2x00dev->irqmask_lock, flags); + +if (state ==STATE_RADIO_IRQ_OFF ) { +/* +* Ensure that all tasklets are finished before +* disabling the interrupts. +*/ +tasklet_disable (&rt2x00dev->txstatus_tasklet); +tasklet_disable (&rt2x00dev->rxdone_tasklet); + } }static int rt2400pci_enable_radio (struct rt2x00_dev *rt2x00dev)
@@ -1285,57 +1315,71 @@ static void rt2400pci_txdone(struct rt2x00_dev *rt2x00dev, } } -static irqreturn_t rt2400pci_interrupt_thread (int irq,void *dev_instance) +static void rt2400pci_enable_interrupt (struct rt2x00_dev *rt2x00dev, +struct rt2x00_field32 irq_field) { -struct rt2x00_dev *rt2x00dev = dev_instance; -u32 reg = rt2x00dev->irqvalue[0 ]; +unsigned long flags; +u32 reg;/* -* Handle interrupts, walk through all bits -* and run the tasks, the bits are checked in order of -* priority. +* Enable a single interrupt. The interrupt mask register +* access needs locking. */ +spin_lock_irqsave (&rt2x00dev->irqmask_lock, flags); -/* -* 1 - Beacon timer expired interrupt. -*/ -if (rt2x00_get_field32 (reg,CSR7_TBCN_EXPIRE )) -rt2x00lib_beacondone (rt2x00dev); +rt2x00pci_register_read (rt2x00dev,CSR8 , ®); +rt2x00_set_field32 (®, irq_field,0 ); +rt2x00pci_register_write (rt2x00dev,CSR8 , reg); -/* -* 2 - Rx ring done interrupt. -*/ -if (rt2x00_get_field32 (reg,CSR7_RXDONE )) -rt2x00pci_rxdone (rt2x00dev); +spin_unlock_irqrestore (&rt2x00dev->irqmask_lock, flags); +} -/* -* 3 - Atim ring transmit done interrupt. -*/ -if (rt2x00_get_field32 (reg,CSR7_TXDONE_ATIMRING )) -rt2400pci_txdone (rt2x00dev,QID_ATIM ); +static void rt2400pci_txstatus_tasklet (unsigned long data) +{ +struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; +u32 reg; +unsigned long flags;/* -* 4 - Priority ring transmit done interrupt. +* Handle all tx queues. */ -if (rt2x00_get_field32 (reg,CSR7_TXDONE_PRIORING )) -rt2400pci_txdone (rt2x00dev,QID_AC_VO ); +rt2400pci_txdone (rt2x00dev,QID_ATIM ); +rt2400pci_txdone (rt2x00dev,QID_AC_VO ); +rt2400pci_txdone (rt2x00dev,QID_AC_VI );/* -* 5 - Tx ring transmit done interrupt. +* Enable all TXDONE interrupts again. */ -if (rt2x00_get_field32 (reg,CSR7_TXDONE_TXRING )) -rt2400pci_txdone (rt2x00dev,QID_AC_VI ); +spin_lock_irqsave (&rt2x00dev->irqmask_lock, flags); -/* Enable interrupts again. */ - rt2x00dev->ops->lib->set_device_state (rt2x00dev, -STATE_RADIO_IRQ_ON_ISR ); -return IRQ_HANDLED ; +rt2x00pci_register_read (rt2x00dev,CSR8 , ®); +rt2x00_set_field32 (®,CSR8_TXDONE_TXRING ,0 ); +rt2x00_set_field32 (®,CSR8_TXDONE_ATIMRING ,0 ); +rt2x00_set_field32 (®,CSR8_TXDONE_PRIORING ,0 ); +rt2x00pci_register_write (rt2x00dev,CSR8 , reg); + +spin_unlock_irqrestore (&rt2x00dev->irqmask_lock, flags); +} + +static void rt2400pci_tbtt_tasklet (unsigned long data) +{ +struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; +rt2x00lib_beacondone (rt2x00dev); +rt2400pci_enable_interrupt (rt2x00dev,CSR8_TBCN_EXPIRE ); +} + +static void rt2400pci_rxdone_tasklet (unsigned long data) +{ +struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; +rt2x00pci_rxdone (rt2x00dev); +rt2400pci_enable_interrupt (rt2x00dev,CSR8_RXDONE ); }static irqreturn_t rt2400pci_interrupt (int irq,void *dev_instance) {struct rt2x00_dev *rt2x00dev = dev_instance; -u32 reg; +u32 reg, mask; +unsigned long flags;/* * Get the interrupt sources & saved to local variable.
@@ -1350,14 +1394,44 @@ static irqreturn_t rt2400pci_interrupt(int irq, void *dev_instance)if (!test_bit (DEVICE_STATE_ENABLED_RADIO , &rt2x00dev->flags))return IRQ_HANDLED ; -/* Store irqvalues for use in the interrupt thread. */ - rt2x00dev->irqvalue[0 ] = reg; + mask = reg; + +/* +* Schedule tasklets for interrupt handling. +*/ +if (rt2x00_get_field32 (reg,CSR7_TBCN_EXPIRE )) +tasklet_hi_schedule (&rt2x00dev->tbtt_tasklet); + +if (rt2x00_get_field32 (reg,CSR7_RXDONE )) +tasklet_schedule (&rt2x00dev->rxdone_tasklet); + +if (rt2x00_get_field32 (reg,CSR7_TXDONE_ATIMRING ) || +rt2x00_get_field32 (reg,CSR7_TXDONE_PRIORING ) || +rt2x00_get_field32 (reg,CSR7_TXDONE_TXRING )) { +tasklet_schedule (&rt2x00dev->txstatus_tasklet); +/* +* Mask out all txdone interrupts. +*/ +rt2x00_set_field32 (&mask,CSR8_TXDONE_TXRING ,1 ); +rt2x00_set_field32 (&mask,CSR8_TXDONE_ATIMRING ,1 ); +rt2x00_set_field32 (&mask,CSR8_TXDONE_PRIORING ,1 ); + } -/* Disable interrupts, will be enabled again in the interrupt thread. */ - rt2x00dev->ops->lib->set_device_state (rt2x00dev, -STATE_RADIO_IRQ_OFF_ISR ); +/* +* Disable all interrupts for which a tasklet was scheduled right now, +* the tasklet will reenable the appropriate interrupts. +*/ +spin_lock_irqsave (&rt2x00dev->irqmask_lock, flags); -return IRQ_WAKE_THREAD ; +rt2x00pci_register_read (rt2x00dev,CSR8 , ®); + reg |= mask; +rt2x00pci_register_write (rt2x00dev,CSR8 , reg); + +spin_unlock_irqrestore (&rt2x00dev->irqmask_lock, flags); + + + +return IRQ_HANDLED ; }/*
@@ -1651,7 +1725,9 @@ static const struct ieee80211_ops rt2400pci_mac80211_ops = {static const struct rt2x00lib_ops rt2400pci_rt2x00_ops = { .irq_handler = rt2400pci_interrupt, - .irq_handler_thread = rt2400pci_interrupt_thread, + .txstatus_tasklet = rt2400pci_txstatus_tasklet, + .tbtt_tasklet = rt2400pci_tbtt_tasklet, + .rxdone_tasklet = rt2400pci_rxdone_tasklet, .probe_hw = rt2400pci_probe_hw, .initialize = rt2x00pci_initialize, .uninitialize = rt2x00pci_uninitialize,
Signature · 1 line
1.7.2.3
PATCH 11/21 rt2x00: Remove interrupt thread registration
From: Helmut Schaa <[email protected]>
No driver uses interrupt threads anymore. Remove the remaining interrupt thread artifacts.
--- drivers/net/wireless/rt2x00/rt2x00.h | 11 ----------- drivers/net/wireless/rt2x00/rt2x00pci.c | 7 +++---- 2 files changed, 3 insertions(+), 15 deletions(-)
drivers/net/wireless/rt2x00/rt2x00.h +0 -11
@@ -512,11 +512,6 @@ struct rt2x00lib_ops {irq_handler_t irq_handler;/* -* Threaded Interrupt handlers. -*/ -irq_handler_t irq_handler_thread; - -/* * TX status tasklet handler. */ void (*txstatus_tasklet) (unsigned long data);
@@ -895,12 +890,6 @@ struct rt2x00_dev {const struct firmware *fw;/* -* Interrupt values, stored between interrupt service routine -* and interrupt thread routine. -*/ -u32 irqvalue[2 ]; - -/* * FIFO for storing tx status reports between isr and tasklet. */ DECLARE_KFIFO_PTR (txstatus_fifo, u32);
drivers/net/wireless/rt2x00/rt2x00pci.c +3 -4
@@ -160,10 +160,9 @@ int rt2x00pci_initialize(struct rt2x00_dev *rt2x00dev)/* * Register interrupt handler. */ - status =request_threaded_irq (rt2x00dev->irq, - rt2x00dev->ops->lib->irq_handler, - rt2x00dev->ops->lib->irq_handler_thread, -IRQF_SHARED , rt2x00dev->name, rt2x00dev); + status =request_irq (rt2x00dev->irq, + rt2x00dev->ops->lib->irq_handler, +IRQF_SHARED , rt2x00dev->name, rt2x00dev);if (status) {ERROR (rt2x00dev,"IRQ %d allocation failed (error %d).\n" , rt2x00dev->irq, status);
Signature · 1 line
1.7.2.3
PATCH 12/21 rt2x00: Remove STATE_RADIO_IRQ_OFF_ISR and STATE_RADIO_IRQ_ON_ISR
From: Helmut Schaa <[email protected]>
Remove STATE_RADIO_IRQ_OFF_ISR and STATE_RADIO_IRQ_ON_ISR as they are not used anymore.
--- drivers/net/wireless/rt2x00/rt2400pci.c | 5 +---- drivers/net/wireless/rt2x00/rt2500pci.c | 5 +---- drivers/net/wireless/rt2x00/rt2500usb.c | 2 -- drivers/net/wireless/rt2x00/rt2800pci.c | 5 +---- drivers/net/wireless/rt2x00/rt2800usb.c | 2 -- drivers/net/wireless/rt2x00/rt2x00reg.h | 2 -- drivers/net/wireless/rt2x00/rt61pci.c | 5 +---- drivers/net/wireless/rt2x00/rt73usb.c | 2 -- 8 files changed, 4 insertions(+), 24 deletions(-)
drivers/net/wireless/rt2x00/rt2400pci.c +1 -4
@@ -971,8 +971,7 @@ static int rt2400pci_init_bbp(struct rt2x00_dev *rt2x00dev)static void rt2400pci_toggle_irq (struct rt2x00_dev *rt2x00dev,enum dev_state state) { -int mask = (state ==STATE_RADIO_IRQ_OFF ) || - (state ==STATE_RADIO_IRQ_OFF_ISR ); +int mask = (state ==STATE_RADIO_IRQ_OFF );u32 reg;unsigned long flags;
@@ -1087,9 +1086,7 @@ static int rt2400pci_set_device_state(struct rt2x00_dev *rt2x00dev,rt2400pci_disable_radio (rt2x00dev);break ;case STATE_RADIO_IRQ_ON : -case STATE_RADIO_IRQ_ON_ISR :case STATE_RADIO_IRQ_OFF : -case STATE_RADIO_IRQ_OFF_ISR :rt2400pci_toggle_irq (rt2x00dev, state);break ;case STATE_DEEP_SLEEP :
drivers/net/wireless/rt2x00/rt2500pci.c +1 -4
@@ -1126,8 +1126,7 @@ static int rt2500pci_init_bbp(struct rt2x00_dev *rt2x00dev)static void rt2500pci_toggle_irq (struct rt2x00_dev *rt2x00dev,enum dev_state state) { -int mask = (state ==STATE_RADIO_IRQ_OFF ) || - (state ==STATE_RADIO_IRQ_OFF_ISR ); +int mask = (state ==STATE_RADIO_IRQ_OFF );u32 reg;unsigned long flags;
@@ -1241,9 +1240,7 @@ static int rt2500pci_set_device_state(struct rt2x00_dev *rt2x00dev,rt2500pci_disable_radio (rt2x00dev);break ;case STATE_RADIO_IRQ_ON : -case STATE_RADIO_IRQ_ON_ISR :case STATE_RADIO_IRQ_OFF : -case STATE_RADIO_IRQ_OFF_ISR :rt2500pci_toggle_irq (rt2x00dev, state);break ;case STATE_DEEP_SLEEP :
drivers/net/wireless/rt2x00/rt2500usb.c +0 -2
@@ -1054,9 +1054,7 @@ static int rt2500usb_set_device_state(struct rt2x00_dev *rt2x00dev,rt2500usb_disable_radio (rt2x00dev);break ;case STATE_RADIO_IRQ_ON : -case STATE_RADIO_IRQ_ON_ISR :case STATE_RADIO_IRQ_OFF : -case STATE_RADIO_IRQ_OFF_ISR :/* No support, but no error either */ break ;case STATE_DEEP_SLEEP :
drivers/net/wireless/rt2x00/rt2800pci.c +1 -4
@@ -418,8 +418,7 @@ static int rt2800pci_init_queues(struct rt2x00_dev *rt2x00dev)static void rt2800pci_toggle_irq (struct rt2x00_dev *rt2x00dev,enum dev_state state) { -int mask = (state ==STATE_RADIO_IRQ_ON ) || - (state ==STATE_RADIO_IRQ_ON_ISR ); +int mask = (state ==STATE_RADIO_IRQ_ON );u32 reg;unsigned long flags;
@@ -564,9 +563,7 @@ static int rt2800pci_set_device_state(struct rt2x00_dev *rt2x00dev,rt2800pci_set_state (rt2x00dev,STATE_SLEEP );break ;case STATE_RADIO_IRQ_ON : -case STATE_RADIO_IRQ_ON_ISR :case STATE_RADIO_IRQ_OFF : -case STATE_RADIO_IRQ_OFF_ISR :rt2800pci_toggle_irq (rt2x00dev, state);break ;case STATE_DEEP_SLEEP :
drivers/net/wireless/rt2x00/rt2800usb.c +0 -2
@@ -253,9 +253,7 @@ static int rt2800usb_set_device_state(struct rt2x00_dev *rt2x00dev,rt2800usb_set_state (rt2x00dev,STATE_SLEEP );break ;case STATE_RADIO_IRQ_ON : -case STATE_RADIO_IRQ_ON_ISR :case STATE_RADIO_IRQ_OFF : -case STATE_RADIO_IRQ_OFF_ISR :/* No support, but no error either */ break ;case STATE_DEEP_SLEEP :
drivers/net/wireless/rt2x00/rt2x00reg.h +0 -2
@@ -85,8 +85,6 @@ enum dev_state {STATE_RADIO_OFF ,STATE_RADIO_IRQ_ON , STATE_RADIO_IRQ_OFF, -STATE_RADIO_IRQ_ON_ISR , - STATE_RADIO_IRQ_OFF_ISR, };/*
drivers/net/wireless/rt2x00/rt61pci.c +1 -4
@@ -1717,8 +1717,7 @@ static int rt61pci_init_bbp(struct rt2x00_dev *rt2x00dev)static void rt61pci_toggle_irq (struct rt2x00_dev *rt2x00dev,enum dev_state state) { -int mask = (state ==STATE_RADIO_IRQ_OFF ) || - (state ==STATE_RADIO_IRQ_OFF_ISR ); +int mask = (state ==STATE_RADIO_IRQ_OFF );u32 reg;unsigned long flags;
@@ -1852,9 +1851,7 @@ static int rt61pci_set_device_state(struct rt2x00_dev *rt2x00dev,rt61pci_disable_radio (rt2x00dev);break ;case STATE_RADIO_IRQ_ON : -case STATE_RADIO_IRQ_ON_ISR :case STATE_RADIO_IRQ_OFF : -case STATE_RADIO_IRQ_OFF_ISR :rt61pci_toggle_irq (rt2x00dev, state);break ;case STATE_DEEP_SLEEP :
drivers/net/wireless/rt2x00/rt73usb.c +0 -2
@@ -1428,9 +1428,7 @@ static int rt73usb_set_device_state(struct rt2x00_dev *rt2x00dev,rt73usb_disable_radio (rt2x00dev);break ;case STATE_RADIO_IRQ_ON : -case STATE_RADIO_IRQ_ON_ISR :case STATE_RADIO_IRQ_OFF : -case STATE_RADIO_IRQ_OFF_ISR :/* No support, but no error either */ break ;case STATE_DEEP_SLEEP :
Signature · 1 line
1.7.2.3
PATCH 13/21 rt2x00: Correct initial value of US_CYC_CNT register for pcie interface
From: RA-Jay Hung <[email protected]>
CLOCK CYCLE: Clock cycle count in 1us
PCI:0x21, PCIE:0x7d, USB:0x1e
--- drivers/net/wireless/rt2x00/rt2800.h | 4 ++++ drivers/net/wireless/rt2x00/rt2800lib.c | 4 ++++ 2 files changed, 8 insertions(+), 0 deletions(-)
drivers/net/wireless/rt2x00/rt2800.h +4 -0
@@ -372,8 +372,12 @@/* * US_CYC_CNT +* BT_MODE_EN: Bluetooth mode enable +* CLOCK CYCLE: Clock cycle count in 1us. +* PCI:0x21, PCIE:0x7d, USB:0x1e */ #define US_CYC_CNT 0x02a4 +#define US_CYC_CNT_BT_MODE_EN FIELD32(0x00000100)#define US_CYC_CNT_CLOCK_CYCLE FIELD32(0x000000ff)/*
drivers/net/wireless/rt2x00/rt2800lib.c +4 -0
@@ -2207,6 +2207,10 @@ static int rt2800_init_registers(struct rt2x00_dev *rt2x00dev)rt2800_register_read (rt2x00dev,US_CYC_CNT , ®);rt2x00_set_field32 (®,US_CYC_CNT_CLOCK_CYCLE ,30 );rt2800_register_write (rt2x00dev,US_CYC_CNT , reg); + }else if (rt2x00_is_pcie (rt2x00dev)) { +rt2800_register_read (rt2x00dev,US_CYC_CNT , ®); +rt2x00_set_field32 (®,US_CYC_CNT_CLOCK_CYCLE ,125 ); +rt2800_register_write (rt2x00dev,US_CYC_CNT , reg); }rt2800_register_read (rt2x00dev,HT_FBK_CFG0 , ®);
Signature · 1 line
1.7.2.3
PATCH 14/21 rt2x00: Update MAINTAINERS
From: Helmut Schaa <[email protected]>
--- MAINTAINERS | 1 + 1 files changed, 1 insertions(+), 0 deletions(-)
MAINTAINERS +1 -0
@@ -5101,6 +5101,7 @@ RALINK RT2X00 WIRELESS LAN DRIVER P: rt2x00 project M: Ivo van Doorn <[email protected]> M: Gertjan van Wingerde <[email protected]> +M: Helmut Schaa <[email protected]> L: [email protected] L: [email protected] (moderated for non-subscribers) W: http://rt2x00.serialmonkey.com/
Signature · 1 line
1.7.2.3
PATCH 15/21 Trivial typo fix in comment
From: Mark Einon <[email protected]>
Fixing a trivial comment typo.
--- drivers/net/wireless/rt2x00/rt2400pci.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-)
drivers/net/wireless/rt2x00/rt2400pci.c +1 -1
@@ -46,7 +46,7 @@* These indirect registers work with busy bits, * and we will try maximal REGISTER_BUSY_COUNT times to access * the register while taking a REGISTER_BUSY_DELAY us delay -* between each attampt. When the busy bit is still set at that time, +* between each attempt. When the busy bit is still set at that time, * the access attempt is considered to have failed, * and we will print an error. */
Signature · 1 line
1.7.2.3
PATCH 16/21 rt2x00: trivial: add \n to WARNING message
From: Johannes Stezenbach <[email protected]>
--- drivers/net/wireless/rt2x00/rt2x00queue.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-)
drivers/net/wireless/rt2x00/rt2x00queue.c +1 -1
@@ -913,7 +913,7 @@ void rt2x00queue_flush_queue(struct data_queue *queue, bool drop)* The queue flush has failed... */ if (unlikely (!rt2x00queue_empty (queue))) -WARNING (queue->rt2x00dev,"Queue %d failed to flush" , queue->qid); +WARNING (queue->rt2x00dev,"Queue %d failed to flush\n" , queue->qid);/* * Restore the queue to the previous status
Signature · 1 line
1.7.2.3
PATCH 17/21 rt2x00: Fix WPA TKIP Michael MIC failures.
From: Gertjan van Wingerde <[email protected]>
As reported and found by Johannes Stezenbach:
rt2800{pci,usb} do not report the Michael MIC in RXed frames, but do check the Michael MIC in hardware. Therefore we have to report to mac80211 that the received frame does not include the Michael MIC.
--- drivers/net/wireless/rt2x00/rt2800pci.c | 6 ++++++ drivers/net/wireless/rt2x00/rt2800usb.c | 6 ++++++ 2 files changed, 12 insertions(+), 0 deletions(-)
drivers/net/wireless/rt2x00/rt2800pci.c +6 -0
@@ -675,6 +675,12 @@ static void rt2800pci_fill_rxdone(struct queue_entry *entry,*/ rxdesc->flags |=RX_FLAG_IV_STRIPPED ; +/* +* The hardware has already checked the Michael Mic and has +* stripped it from the frame. Signal this to mac80211. +*/ + rxdesc->flags |=RX_FLAG_MMIC_STRIPPED ; +if (rxdesc->cipher_status ==RX_CRYPTO_SUCCESS ) rxdesc->flags |=RX_FLAG_DECRYPTED ;else if (rxdesc->cipher_status ==RX_CRYPTO_FAIL_MIC )
drivers/net/wireless/rt2x00/rt2800usb.c +6 -0
@@ -484,6 +484,12 @@ static void rt2800usb_fill_rxdone(struct queue_entry *entry,*/ rxdesc->flags |=RX_FLAG_IV_STRIPPED ; +/* +* The hardware has already checked the Michael Mic and has +* stripped it from the frame. Signal this to mac80211. +*/ + rxdesc->flags |=RX_FLAG_MMIC_STRIPPED ; +if (rxdesc->cipher_status ==RX_CRYPTO_SUCCESS ) rxdesc->flags |=RX_FLAG_DECRYPTED ;else if (rxdesc->cipher_status ==RX_CRYPTO_FAIL_MIC )
Signature · 1 line
1.7.2.3
PATCH 18/21 rt2x00: Copy the MAC address to the WCID entry properly.
From: Gertjan van Wingerde <[email protected]>
Use the specific mac field of the wcid_entry structure to copy the MAC address to, instead of just overwriting the structure. Previous code resulted in the same, but this form is cleaner.
--- drivers/net/wireless/rt2x00/rt2800lib.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-)
drivers/net/wireless/rt2x00/rt2800lib.c +1 -1
@@ -1030,7 +1030,7 @@ static void rt2800_config_wcid_attr(struct rt2x00_dev *rt2x00dev,memset (&wcid_entry,0 ,sizeof (wcid_entry));if (crypto->cmd ==SET_KEY ) -memcpy (&wcid_entry, crypto->address,ETH_ALEN ); +memcpy (wcid_entry.mac, crypto->address,ETH_ALEN );rt2800_register_multiwrite (rt2x00dev, offset, &wcid_entry,sizeof (wcid_entry)); }
Signature · 1 line
1.7.2.3
PATCH 19/21 rt2x00: Fix FIXME comments in rt61pci and rt73usb on Michael MIC.
From: Gertjan van Wingerde <[email protected]>
Both rt61pci and rt73usb check the Michael MIC in hardware and strip the Michael MIC from received frames. This is perfectly allowed by mac80211 as long as this is properly reported to mac80211.
Both these drivers reported the Michael MIC handling properly to mac80211, but still contained a FIXME comment on this, which is not needed to be handled, since mac80211 doesn't really need the Michael MIC in this case.
--- drivers/net/wireless/rt2x00/rt61pci.c | 5 ++--- drivers/net/wireless/rt2x00/rt73usb.c | 5 ++--- 2 files changed, 4 insertions(+), 6 deletions(-)
drivers/net/wireless/rt2x00/rt61pci.c +2 -3
@@ -2118,9 +2118,8 @@ static void rt61pci_fill_rxdone(struct queue_entry *entry, rxdesc->flags |=RX_FLAG_IV_STRIPPED ;/* -* FIXME: Legacy driver indicates that the frame does -* contain the Michael Mic. Unfortunately, in rt2x00 -* the MIC seems to be missing completely... +* The hardware has already checked the Michael Mic and has +* stripped it from the frame. Signal this to mac80211. */ rxdesc->flags |=RX_FLAG_MMIC_STRIPPED ;
drivers/net/wireless/rt2x00/rt73usb.c +2 -3
@@ -1709,9 +1709,8 @@ static void rt73usb_fill_rxdone(struct queue_entry *entry, rxdesc->flags |=RX_FLAG_IV_STRIPPED ;/* -* FIXME: Legacy driver indicates that the frame does -* contain the Michael Mic. Unfortunately, in rt2x00 -* the MIC seems to be missing completely... +* The hardware has already checked the Michael Mic and has +* stripped it from the frame. Signal this to mac80211. */ rxdesc->flags |=RX_FLAG_MMIC_STRIPPED ;
Signature · 1 line
1.7.2.3
PATCH 20/21 rt2x00: Kill all tasklets during device removal
From: Ivo van Doorn <[email protected]>
During device removal all pending work and tasklets must be guaranteed to be halted. So far only the txstatus_tasklet was killed.
--- drivers/net/wireless/rt2x00/rt2x00dev.c | 4 ++++ 1 files changed, 4 insertions(+), 0 deletions(-)
drivers/net/wireless/rt2x00/rt2x00dev.c +4 -0
@@ -1067,6 +1067,10 @@ void rt2x00lib_remove_dev(struct rt2x00_dev *rt2x00dev)* Kill the tx status tasklet. */ tasklet_kill (&rt2x00dev->txstatus_tasklet); +tasklet_kill (&rt2x00dev->pretbtt_tasklet); +tasklet_kill (&rt2x00dev->tbtt_tasklet); +tasklet_kill (&rt2x00dev->rxdone_tasklet); +tasklet_kill (&rt2x00dev->autowake_tasklet);/* * Uninitialize device.
Signature · 1 line
1.7.2.3
PATCH 21/21 rt2x00: Move TX/RX work into dedicated workqueue
From: Ivo van Doorn <[email protected]>
The TX/RX work structures must be able to run independently of other workqueues. This is because mac80211 might use the flush() callback function from various context, which depends on the TX/RX work to complete while the main thread is blocked (until the the TX queues are empty).
This should reduce the number of 'Queue %d failed to flush' warnings.
--- drivers/net/wireless/rt2x00/rt2x00.h | 7 +++++++ drivers/net/wireless/rt2x00/rt2x00dev.c | 10 +++++++++- drivers/net/wireless/rt2x00/rt2x00link.c | 7 +++++-- drivers/net/wireless/rt2x00/rt2x00usb.c | 8 ++++---- 4 files changed, 25 insertions(+), 7 deletions(-)
drivers/net/wireless/rt2x00/rt2x00.h +7 -0
@@ -860,6 +860,13 @@ struct rt2x00_dev {*/ struct ieee80211_low_level_stats low_level_stats; +/** +* Work queue for all work which should not be placed +* on the mac80211 workqueue (because of dependencies +* between various work structures). +*/ +struct workqueue_struct *workqueue; +/* * Scheduled work. * NOTE: intf_work will use ieee80211_iterate_active_interfaces()
drivers/net/wireless/rt2x00/rt2x00dev.c +9 -1
@@ -997,8 +997,15 @@ int rt2x00lib_probe_dev(struct rt2x00_dev *rt2x00dev)BIT (NL80211_IFTYPE_WDS );/* -* Initialize configuration work. +* Initialize work. */ + rt2x00dev->workqueue = +alloc_ordered_workqueue (wiphy_name (rt2x00dev->hw->wiphy),0 ); +if (!rt2x00dev->workqueue) { + retval = -ENOMEM ; + goto exit; + } +INIT_WORK (&rt2x00dev->intf_work, rt2x00lib_intf_scheduled);/*
@@ -1057,6 +1064,7 @@ void rt2x00lib_remove_dev(struct rt2x00_dev *rt2x00dev)cancel_work_sync (&rt2x00dev->intf_work);cancel_work_sync (&rt2x00dev->rxdone_work);cancel_work_sync (&rt2x00dev->txdone_work); +destroy_workqueue (rt2x00dev->workqueue);/* * Free the tx status fifo.
drivers/net/wireless/rt2x00/rt2x00link.c +5 -2
@@ -417,7 +417,8 @@ void rt2x00link_start_watchdog(struct rt2x00_dev *rt2x00dev) !test_bit(DRIVER_SUPPORT_WATCHDOG, &rt2x00dev->flags)) return; -schedule_delayed_work (&link->watchdog_work,WATCHDOG_INTERVAL ); +ieee80211_queue_delayed_work (rt2x00dev->hw, + &link->watchdog_work,WATCHDOG_INTERVAL ); }void rt2x00link_stop_watchdog (struct rt2x00_dev *rt2x00dev)
@@ -441,7 +442,9 @@ static void rt2x00link_watchdog(struct work_struct *work) rt2x00dev->ops->lib->watchdog (rt2x00dev);if (test_bit (DEVICE_STATE_PRESENT , &rt2x00dev->flags)) -schedule_delayed_work (&link->watchdog_work,WATCHDOG_INTERVAL ); +ieee80211_queue_delayed_work (rt2x00dev->hw, + &link->watchdog_work, +WATCHDOG_INTERVAL ); }void rt2x00link_register (struct rt2x00_dev *rt2x00dev)
drivers/net/wireless/rt2x00/rt2x00usb.c +4 -4
@@ -227,7 +227,7 @@ static void rt2x00usb_interrupt_txdone(struct urb *urb)* Schedule the delayed work for reading the TX status * from the device. */ -ieee80211_queue_work (rt2x00dev->hw, &rt2x00dev->txdone_work); +queue_work (rt2x00dev->workqueue, &rt2x00dev->txdone_work); }static void rt2x00usb_kick_tx_entry (struct queue_entry *entry)
@@ -320,7 +320,7 @@ static void rt2x00usb_interrupt_rxdone(struct urb *urb)* Schedule the delayed work for reading the RX status * from the device. */ -ieee80211_queue_work (rt2x00dev->hw, &rt2x00dev->rxdone_work); +queue_work (rt2x00dev->workqueue, &rt2x00dev->rxdone_work); }static void rt2x00usb_kick_rx_entry (struct queue_entry *entry)
@@ -429,7 +429,7 @@ void rt2x00usb_flush_queue(struct data_queue *queue)* Schedule the completion handler manually, when this * worker function runs, it should cleanup the queue. */ -ieee80211_queue_work (queue->rt2x00dev->hw, completion); +queue_work (queue->rt2x00dev->workqueue, completion);/* * Wait for a little while to give the driver
@@ -453,7 +453,7 @@ static void rt2x00usb_watchdog_tx_status(struct data_queue *queue)WARNING (queue->rt2x00dev,"TX queue %d status timed out," " invoke forced tx handler\n" , queue->qid); -ieee80211_queue_work (queue->rt2x00dev->hw, &queue->rt2x00dev->txdone_work); +queue_work (queue->rt2x00dev->workqueue, &queue->rt2x00dev->txdone_work); }void rt2x00usb_watchdog (struct rt2x00_dev *rt2x00dev)
Signature · 1 line
1.7.2.3
Re: [PATCH 17/21] rt2x00: Fix WPA TKIP Michael MIC failures.
In reply to Ivo van Doorn2011/1/30 Ivo van Doorn <[email protected]>:
Quoted 6 lines
From: Gertjan van Wingerde <[email protected]>
As reported and found by Johannes Stezenbach:
rt2800{pci,usb} do not report the Michael MIC in RXed frames, but do check the Michael MIC in hardware. Therefore we have to report to mac80211 that the received frame does not include the Michael MIC.
Thank you, thank you very much! This fixes:
https://bugzilla.kernel.org/show_bug.cgi?id=16608
Should it go to 2.6.38-rcX? As it fixes bug, does not introduce new features and is quite "safe"?
Signature · 1 line
Rafał