diff options
author | Ulrich Kunitz <kune@deine-taler.de> | 2006-12-10 14:13:12 -0500 |
---|---|---|
committer | John W. Linville <linville@linville-t43.mobile> | 2006-12-19 16:09:59 -0500 |
commit | 4d1feabcbf41f875447a392015acd0796f57baf6 (patch) | |
tree | b47ceb21cfed93f410e6329040784d43864dab33 /drivers/net/wireless/zd1211rw | |
parent | e25db641c0e6dd49c5db24dbe154048d4a466727 (diff) |
[PATCH] zd1211rw: Call ieee80211_rx in tasklet
The driver called ieee80211_rx in hardware interrupt context. This has
been against the intention of the ieee80211_rx function. It caused a bug
in the crypto routines used by WPA. This patch calls ieee80211_rx in a
tasklet.
Signed-off-by: Ulrich Kunitz <kune@deine-taler.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless/zd1211rw')
-rw-r--r-- | drivers/net/wireless/zd1211rw/zd_mac.c | 96 | ||||
-rw-r--r-- | drivers/net/wireless/zd1211rw/zd_mac.h | 5 | ||||
-rw-r--r-- | drivers/net/wireless/zd1211rw/zd_usb.c | 4 |
3 files changed, 76 insertions, 29 deletions
diff --git a/drivers/net/wireless/zd1211rw/zd_mac.c b/drivers/net/wireless/zd1211rw/zd_mac.c index 00ca704ece35..a08524191b5d 100644 --- a/drivers/net/wireless/zd1211rw/zd_mac.c +++ b/drivers/net/wireless/zd1211rw/zd_mac.c | |||
@@ -41,6 +41,8 @@ static void housekeeping_disable(struct zd_mac *mac); | |||
41 | 41 | ||
42 | static void set_multicast_hash_handler(struct work_struct *work); | 42 | static void set_multicast_hash_handler(struct work_struct *work); |
43 | 43 | ||
44 | static void do_rx(unsigned long mac_ptr); | ||
45 | |||
44 | int zd_mac_init(struct zd_mac *mac, | 46 | int zd_mac_init(struct zd_mac *mac, |
45 | struct net_device *netdev, | 47 | struct net_device *netdev, |
46 | struct usb_interface *intf) | 48 | struct usb_interface *intf) |
@@ -53,6 +55,10 @@ int zd_mac_init(struct zd_mac *mac, | |||
53 | INIT_DELAYED_WORK(&mac->set_rts_cts_work, set_rts_cts_work); | 55 | INIT_DELAYED_WORK(&mac->set_rts_cts_work, set_rts_cts_work); |
54 | INIT_DELAYED_WORK(&mac->set_basic_rates_work, set_basic_rates_work); | 56 | INIT_DELAYED_WORK(&mac->set_basic_rates_work, set_basic_rates_work); |
55 | 57 | ||
58 | skb_queue_head_init(&mac->rx_queue); | ||
59 | tasklet_init(&mac->rx_tasklet, do_rx, (unsigned long)mac); | ||
60 | tasklet_disable(&mac->rx_tasklet); | ||
61 | |||
56 | ieee_init(ieee); | 62 | ieee_init(ieee); |
57 | softmac_init(ieee80211_priv(netdev)); | 63 | softmac_init(ieee80211_priv(netdev)); |
58 | zd_chip_init(&mac->chip, netdev, intf); | 64 | zd_chip_init(&mac->chip, netdev, intf); |
@@ -140,6 +146,8 @@ out: | |||
140 | void zd_mac_clear(struct zd_mac *mac) | 146 | void zd_mac_clear(struct zd_mac *mac) |
141 | { | 147 | { |
142 | flush_workqueue(zd_workqueue); | 148 | flush_workqueue(zd_workqueue); |
149 | skb_queue_purge(&mac->rx_queue); | ||
150 | tasklet_kill(&mac->rx_tasklet); | ||
143 | zd_chip_clear(&mac->chip); | 151 | zd_chip_clear(&mac->chip); |
144 | ZD_ASSERT(!spin_is_locked(&mac->lock)); | 152 | ZD_ASSERT(!spin_is_locked(&mac->lock)); |
145 | ZD_MEMCLEAR(mac, sizeof(struct zd_mac)); | 153 | ZD_MEMCLEAR(mac, sizeof(struct zd_mac)); |
@@ -168,6 +176,8 @@ int zd_mac_open(struct net_device *netdev) | |||
168 | struct zd_chip *chip = &mac->chip; | 176 | struct zd_chip *chip = &mac->chip; |
169 | int r; | 177 | int r; |
170 | 178 | ||
179 | tasklet_enable(&mac->rx_tasklet); | ||
180 | |||
171 | r = zd_chip_enable_int(chip); | 181 | r = zd_chip_enable_int(chip); |
172 | if (r < 0) | 182 | if (r < 0) |
173 | goto out; | 183 | goto out; |
@@ -218,6 +228,8 @@ int zd_mac_stop(struct net_device *netdev) | |||
218 | */ | 228 | */ |
219 | 229 | ||
220 | zd_chip_disable_rx(chip); | 230 | zd_chip_disable_rx(chip); |
231 | skb_queue_purge(&mac->rx_queue); | ||
232 | tasklet_disable(&mac->rx_tasklet); | ||
221 | housekeeping_disable(mac); | 233 | housekeeping_disable(mac); |
222 | ieee80211softmac_stop(netdev); | 234 | ieee80211softmac_stop(netdev); |
223 | 235 | ||
@@ -470,13 +482,13 @@ static void bssinfo_change(struct net_device *netdev, u32 changes) | |||
470 | 482 | ||
471 | if (changes & IEEE80211SOFTMAC_BSSINFOCHG_RATES) { | 483 | if (changes & IEEE80211SOFTMAC_BSSINFOCHG_RATES) { |
472 | /* Set RTS rate to highest available basic rate */ | 484 | /* Set RTS rate to highest available basic rate */ |
473 | u8 rate = ieee80211softmac_highest_supported_rate(softmac, | 485 | u8 hi_rate = ieee80211softmac_highest_supported_rate(softmac, |
474 | &bssinfo->supported_rates, 1); | 486 | &bssinfo->supported_rates, 1); |
475 | rate = rate_to_zd_rate(rate); | 487 | hi_rate = rate_to_zd_rate(hi_rate); |
476 | 488 | ||
477 | spin_lock_irqsave(&mac->lock, flags); | 489 | spin_lock_irqsave(&mac->lock, flags); |
478 | if (rate != mac->rts_rate) { | 490 | if (hi_rate != mac->rts_rate) { |
479 | mac->rts_rate = rate; | 491 | mac->rts_rate = hi_rate; |
480 | need_set_rts_cts = 1; | 492 | need_set_rts_cts = 1; |
481 | } | 493 | } |
482 | spin_unlock_irqrestore(&mac->lock, flags); | 494 | spin_unlock_irqrestore(&mac->lock, flags); |
@@ -1072,43 +1084,75 @@ static int fill_rx_stats(struct ieee80211_rx_stats *stats, | |||
1072 | return 0; | 1084 | return 0; |
1073 | } | 1085 | } |
1074 | 1086 | ||
1075 | int zd_mac_rx(struct zd_mac *mac, const u8 *buffer, unsigned int length) | 1087 | static void zd_mac_rx(struct zd_mac *mac, struct sk_buff *skb) |
1076 | { | 1088 | { |
1077 | int r; | 1089 | int r; |
1078 | struct ieee80211_device *ieee = zd_mac_to_ieee80211(mac); | 1090 | struct ieee80211_device *ieee = zd_mac_to_ieee80211(mac); |
1079 | struct ieee80211_rx_stats stats; | 1091 | struct ieee80211_rx_stats stats; |
1080 | const struct rx_status *status; | 1092 | const struct rx_status *status; |
1081 | struct sk_buff *skb; | ||
1082 | 1093 | ||
1083 | if (length < ZD_PLCP_HEADER_SIZE + IEEE80211_1ADDR_LEN + | 1094 | if (skb->len < ZD_PLCP_HEADER_SIZE + IEEE80211_1ADDR_LEN + |
1084 | IEEE80211_FCS_LEN + sizeof(struct rx_status)) | 1095 | IEEE80211_FCS_LEN + sizeof(struct rx_status)) |
1085 | return -EINVAL; | 1096 | { |
1097 | dev_dbg_f(zd_mac_dev(mac), "Packet with length %u to small.\n", | ||
1098 | skb->len); | ||
1099 | goto free_skb; | ||
1100 | } | ||
1086 | 1101 | ||
1087 | r = fill_rx_stats(&stats, &status, mac, buffer, length); | 1102 | r = fill_rx_stats(&stats, &status, mac, skb->data, skb->len); |
1088 | if (r) | 1103 | if (r) { |
1089 | return r; | 1104 | /* Only packets with rx errors are included here. */ |
1105 | goto free_skb; | ||
1106 | } | ||
1090 | 1107 | ||
1091 | length -= ZD_PLCP_HEADER_SIZE+IEEE80211_FCS_LEN+ | 1108 | __skb_pull(skb, ZD_PLCP_HEADER_SIZE); |
1092 | sizeof(struct rx_status); | 1109 | __skb_trim(skb, skb->len - |
1093 | buffer += ZD_PLCP_HEADER_SIZE; | 1110 | (IEEE80211_FCS_LEN + sizeof(struct rx_status))); |
1094 | 1111 | ||
1095 | update_qual_rssi(mac, buffer, length, stats.signal, stats.rssi); | 1112 | update_qual_rssi(mac, skb->data, skb->len, stats.signal, |
1113 | status->signal_strength); | ||
1096 | 1114 | ||
1097 | r = filter_rx(ieee, buffer, length, &stats); | 1115 | r = filter_rx(ieee, skb->data, skb->len, &stats); |
1098 | if (r <= 0) | 1116 | if (r <= 0) { |
1099 | return r; | 1117 | if (r < 0) |
1118 | dev_dbg_f(zd_mac_dev(mac), "Error in packet.\n"); | ||
1119 | goto free_skb; | ||
1120 | } | ||
1100 | 1121 | ||
1101 | skb = dev_alloc_skb(sizeof(struct zd_rt_hdr) + length); | ||
1102 | if (!skb) | ||
1103 | return -ENOMEM; | ||
1104 | if (ieee->iw_mode == IW_MODE_MONITOR) | 1122 | if (ieee->iw_mode == IW_MODE_MONITOR) |
1105 | fill_rt_header(skb_put(skb, sizeof(struct zd_rt_hdr)), mac, | 1123 | fill_rt_header(skb_push(skb, sizeof(struct zd_rt_hdr)), mac, |
1106 | &stats, status); | 1124 | &stats, status); |
1107 | memcpy(skb_put(skb, length), buffer, length); | ||
1108 | 1125 | ||
1109 | r = ieee80211_rx(ieee, skb, &stats); | 1126 | r = ieee80211_rx(ieee, skb, &stats); |
1110 | if (!r) | 1127 | if (r) |
1111 | dev_kfree_skb_any(skb); | 1128 | return; |
1129 | free_skb: | ||
1130 | /* We are always in a soft irq. */ | ||
1131 | dev_kfree_skb(skb); | ||
1132 | } | ||
1133 | |||
1134 | static void do_rx(unsigned long mac_ptr) | ||
1135 | { | ||
1136 | struct zd_mac *mac = (struct zd_mac *)mac_ptr; | ||
1137 | struct sk_buff *skb; | ||
1138 | |||
1139 | while ((skb = skb_dequeue(&mac->rx_queue)) != NULL) | ||
1140 | zd_mac_rx(mac, skb); | ||
1141 | } | ||
1142 | |||
1143 | int zd_mac_rx_irq(struct zd_mac *mac, const u8 *buffer, unsigned int length) | ||
1144 | { | ||
1145 | struct sk_buff *skb; | ||
1146 | |||
1147 | skb = dev_alloc_skb(sizeof(struct zd_rt_hdr) + length); | ||
1148 | if (!skb) { | ||
1149 | dev_warn(zd_mac_dev(mac), "Could not allocate skb.\n"); | ||
1150 | return -ENOMEM; | ||
1151 | } | ||
1152 | skb_reserve(skb, sizeof(struct zd_rt_hdr)); | ||
1153 | memcpy(__skb_put(skb, length), buffer, length); | ||
1154 | skb_queue_tail(&mac->rx_queue, skb); | ||
1155 | tasklet_schedule(&mac->rx_tasklet); | ||
1112 | return 0; | 1156 | return 0; |
1113 | } | 1157 | } |
1114 | 1158 | ||
diff --git a/drivers/net/wireless/zd1211rw/zd_mac.h b/drivers/net/wireless/zd1211rw/zd_mac.h index f0cf05dc7d3e..faf4c7828d4e 100644 --- a/drivers/net/wireless/zd1211rw/zd_mac.h +++ b/drivers/net/wireless/zd1211rw/zd_mac.h | |||
@@ -138,6 +138,9 @@ struct zd_mac { | |||
138 | struct delayed_work set_rts_cts_work; | 138 | struct delayed_work set_rts_cts_work; |
139 | struct delayed_work set_basic_rates_work; | 139 | struct delayed_work set_basic_rates_work; |
140 | 140 | ||
141 | struct tasklet_struct rx_tasklet; | ||
142 | struct sk_buff_head rx_queue; | ||
143 | |||
141 | unsigned int stats_count; | 144 | unsigned int stats_count; |
142 | u8 qual_buffer[ZD_MAC_STATS_BUFFER_SIZE]; | 145 | u8 qual_buffer[ZD_MAC_STATS_BUFFER_SIZE]; |
143 | u8 rssi_buffer[ZD_MAC_STATS_BUFFER_SIZE]; | 146 | u8 rssi_buffer[ZD_MAC_STATS_BUFFER_SIZE]; |
@@ -193,7 +196,7 @@ int zd_mac_stop(struct net_device *netdev); | |||
193 | int zd_mac_set_mac_address(struct net_device *dev, void *p); | 196 | int zd_mac_set_mac_address(struct net_device *dev, void *p); |
194 | void zd_mac_set_multicast_list(struct net_device *netdev); | 197 | void zd_mac_set_multicast_list(struct net_device *netdev); |
195 | 198 | ||
196 | int zd_mac_rx(struct zd_mac *mac, const u8 *buffer, unsigned int length); | 199 | int zd_mac_rx_irq(struct zd_mac *mac, const u8 *buffer, unsigned int length); |
197 | 200 | ||
198 | int zd_mac_set_regdomain(struct zd_mac *zd_mac, u8 regdomain); | 201 | int zd_mac_set_regdomain(struct zd_mac *zd_mac, u8 regdomain); |
199 | u8 zd_mac_get_regdomain(struct zd_mac *zd_mac); | 202 | u8 zd_mac_get_regdomain(struct zd_mac *zd_mac); |
diff --git a/drivers/net/wireless/zd1211rw/zd_usb.c b/drivers/net/wireless/zd1211rw/zd_usb.c index aa782e88754b..605e96e74057 100644 --- a/drivers/net/wireless/zd1211rw/zd_usb.c +++ b/drivers/net/wireless/zd1211rw/zd_usb.c | |||
@@ -598,13 +598,13 @@ static void handle_rx_packet(struct zd_usb *usb, const u8 *buffer, | |||
598 | n = l+k; | 598 | n = l+k; |
599 | if (n > length) | 599 | if (n > length) |
600 | return; | 600 | return; |
601 | zd_mac_rx(mac, buffer+l, k); | 601 | zd_mac_rx_irq(mac, buffer+l, k); |
602 | if (i >= 2) | 602 | if (i >= 2) |
603 | return; | 603 | return; |
604 | l = (n+3) & ~3; | 604 | l = (n+3) & ~3; |
605 | } | 605 | } |
606 | } else { | 606 | } else { |
607 | zd_mac_rx(mac, buffer, length); | 607 | zd_mac_rx_irq(mac, buffer, length); |
608 | } | 608 | } |
609 | } | 609 | } |
610 | 610 | ||