| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 1 | /* | 
 | 2 | 	Copyright (C) 2004 - 2007 rt2x00 SourceForge Project | 
 | 3 | 	<http://rt2x00.serialmonkey.com> | 
 | 4 |  | 
 | 5 | 	This program is free software; you can redistribute it and/or modify | 
 | 6 | 	it under the terms of the GNU General Public License as published by | 
 | 7 | 	the Free Software Foundation; either version 2 of the License, or | 
 | 8 | 	(at your option) any later version. | 
 | 9 |  | 
 | 10 | 	This program is distributed in the hope that it will be useful, | 
 | 11 | 	but WITHOUT ANY WARRANTY; without even the implied warranty of | 
 | 12 | 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 
 | 13 | 	GNU General Public License for more details. | 
 | 14 |  | 
 | 15 | 	You should have received a copy of the GNU General Public License | 
 | 16 | 	along with this program; if not, write to the | 
 | 17 | 	Free Software Foundation, Inc., | 
 | 18 | 	59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | 
 | 19 |  */ | 
 | 20 |  | 
 | 21 | /* | 
 | 22 | 	Module: rt2x00lib | 
 | 23 | 	Abstract: rt2x00 generic device routines. | 
 | 24 |  */ | 
 | 25 |  | 
 | 26 | /* | 
 | 27 |  * Set enviroment defines for rt2x00.h | 
 | 28 |  */ | 
 | 29 | #define DRV_NAME "rt2x00lib" | 
 | 30 |  | 
 | 31 | #include <linux/kernel.h> | 
 | 32 | #include <linux/module.h> | 
 | 33 |  | 
 | 34 | #include "rt2x00.h" | 
 | 35 | #include "rt2x00lib.h" | 
 | 36 |  | 
 | 37 | /* | 
 | 38 |  * Ring handler. | 
 | 39 |  */ | 
 | 40 | struct data_ring *rt2x00lib_get_ring(struct rt2x00_dev *rt2x00dev, | 
 | 41 | 				     const unsigned int queue) | 
 | 42 | { | 
| Ivo van Doorn | 066cb63 | 2007-09-25 20:55:39 +0200 | [diff] [blame] | 43 | 	int beacon = test_bit(DRIVER_REQUIRE_BEACON_RING, &rt2x00dev->flags); | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 44 |  | 
 | 45 | 	/* | 
 | 46 | 	 * Check if we are requesting a reqular TX ring, | 
 | 47 | 	 * or if we are requesting a Beacon or Atim ring. | 
 | 48 | 	 * For Atim rings, we should check if it is supported. | 
 | 49 | 	 */ | 
 | 50 | 	if (queue < rt2x00dev->hw->queues && rt2x00dev->tx) | 
 | 51 | 		return &rt2x00dev->tx[queue]; | 
 | 52 |  | 
 | 53 | 	if (!rt2x00dev->bcn || !beacon) | 
 | 54 | 		return NULL; | 
 | 55 |  | 
 | 56 | 	if (queue == IEEE80211_TX_QUEUE_BEACON) | 
 | 57 | 		return &rt2x00dev->bcn[0]; | 
 | 58 | 	else if (queue == IEEE80211_TX_QUEUE_AFTER_BEACON) | 
 | 59 | 		return &rt2x00dev->bcn[1]; | 
 | 60 |  | 
 | 61 | 	return NULL; | 
 | 62 | } | 
 | 63 | EXPORT_SYMBOL_GPL(rt2x00lib_get_ring); | 
 | 64 |  | 
 | 65 | /* | 
 | 66 |  * Link tuning handlers | 
 | 67 |  */ | 
 | 68 | static void rt2x00lib_start_link_tuner(struct rt2x00_dev *rt2x00dev) | 
 | 69 | { | 
 | 70 | 	rt2x00_clear_link(&rt2x00dev->link); | 
 | 71 |  | 
 | 72 | 	/* | 
 | 73 | 	 * Reset the link tuner. | 
 | 74 | 	 */ | 
 | 75 | 	rt2x00dev->ops->lib->reset_tuner(rt2x00dev); | 
 | 76 |  | 
 | 77 | 	queue_delayed_work(rt2x00dev->hw->workqueue, | 
 | 78 | 			   &rt2x00dev->link.work, LINK_TUNE_INTERVAL); | 
 | 79 | } | 
 | 80 |  | 
 | 81 | static void rt2x00lib_stop_link_tuner(struct rt2x00_dev *rt2x00dev) | 
 | 82 | { | 
| Ivo van Doorn | 3e30968 | 2007-09-25 20:56:36 +0200 | [diff] [blame] | 83 | 	cancel_delayed_work_sync(&rt2x00dev->link.work); | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 84 | } | 
 | 85 |  | 
 | 86 | void rt2x00lib_reset_link_tuner(struct rt2x00_dev *rt2x00dev) | 
 | 87 | { | 
| Ivo van Doorn | fdd0abc | 2007-09-25 20:57:49 +0200 | [diff] [blame] | 88 | 	if (!test_bit(DEVICE_ENABLED_RADIO, &rt2x00dev->flags)) | 
 | 89 | 		return; | 
 | 90 |  | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 91 | 	rt2x00lib_stop_link_tuner(rt2x00dev); | 
 | 92 | 	rt2x00lib_start_link_tuner(rt2x00dev); | 
 | 93 | } | 
 | 94 |  | 
 | 95 | /* | 
 | 96 |  * Radio control handlers. | 
 | 97 |  */ | 
 | 98 | int rt2x00lib_enable_radio(struct rt2x00_dev *rt2x00dev) | 
 | 99 | { | 
 | 100 | 	int status; | 
 | 101 |  | 
 | 102 | 	/* | 
 | 103 | 	 * Don't enable the radio twice. | 
 | 104 | 	 * And check if the hardware button has been disabled. | 
 | 105 | 	 */ | 
 | 106 | 	if (test_bit(DEVICE_ENABLED_RADIO, &rt2x00dev->flags) || | 
| Ivo van Doorn | 81873e9 | 2007-10-06 14:14:06 +0200 | [diff] [blame] | 107 | 	    test_bit(DEVICE_DISABLED_RADIO_HW, &rt2x00dev->flags)) | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 108 | 		return 0; | 
 | 109 |  | 
 | 110 | 	/* | 
 | 111 | 	 * Enable radio. | 
 | 112 | 	 */ | 
 | 113 | 	status = rt2x00dev->ops->lib->set_device_state(rt2x00dev, | 
 | 114 | 						       STATE_RADIO_ON); | 
 | 115 | 	if (status) | 
 | 116 | 		return status; | 
 | 117 |  | 
 | 118 | 	__set_bit(DEVICE_ENABLED_RADIO, &rt2x00dev->flags); | 
 | 119 |  | 
 | 120 | 	/* | 
 | 121 | 	 * Enable RX. | 
 | 122 | 	 */ | 
| Ivo van Doorn | 5cbf830 | 2007-10-06 14:16:09 +0200 | [diff] [blame] | 123 | 	rt2x00lib_toggle_rx(rt2x00dev, STATE_RADIO_RX_ON); | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 124 |  | 
 | 125 | 	/* | 
 | 126 | 	 * Start the TX queues. | 
 | 127 | 	 */ | 
 | 128 | 	ieee80211_start_queues(rt2x00dev->hw); | 
 | 129 |  | 
 | 130 | 	return 0; | 
 | 131 | } | 
 | 132 |  | 
 | 133 | void rt2x00lib_disable_radio(struct rt2x00_dev *rt2x00dev) | 
 | 134 | { | 
 | 135 | 	if (!__test_and_clear_bit(DEVICE_ENABLED_RADIO, &rt2x00dev->flags)) | 
 | 136 | 		return; | 
 | 137 |  | 
 | 138 | 	/* | 
| Johannes Berg | 4150c57 | 2007-09-17 01:29:23 -0400 | [diff] [blame] | 139 | 	 * Stop all scheduled work. | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 140 | 	 */ | 
 | 141 | 	if (work_pending(&rt2x00dev->beacon_work)) | 
 | 142 | 		cancel_work_sync(&rt2x00dev->beacon_work); | 
| Johannes Berg | 4150c57 | 2007-09-17 01:29:23 -0400 | [diff] [blame] | 143 | 	if (work_pending(&rt2x00dev->filter_work)) | 
 | 144 | 		cancel_work_sync(&rt2x00dev->filter_work); | 
| Ivo van Doorn | 5c58ee5 | 2007-10-06 13:34:52 +0200 | [diff] [blame] | 145 | 	if (work_pending(&rt2x00dev->config_work)) | 
 | 146 | 		cancel_work_sync(&rt2x00dev->config_work); | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 147 |  | 
 | 148 | 	/* | 
 | 149 | 	 * Stop the TX queues. | 
 | 150 | 	 */ | 
 | 151 | 	ieee80211_stop_queues(rt2x00dev->hw); | 
 | 152 |  | 
 | 153 | 	/* | 
 | 154 | 	 * Disable RX. | 
 | 155 | 	 */ | 
| Ivo van Doorn | 5cbf830 | 2007-10-06 14:16:09 +0200 | [diff] [blame] | 156 | 	rt2x00lib_toggle_rx(rt2x00dev, STATE_RADIO_RX_OFF); | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 157 |  | 
 | 158 | 	/* | 
 | 159 | 	 * Disable radio. | 
 | 160 | 	 */ | 
 | 161 | 	rt2x00dev->ops->lib->set_device_state(rt2x00dev, STATE_RADIO_OFF); | 
 | 162 | } | 
 | 163 |  | 
| Ivo van Doorn | 5cbf830 | 2007-10-06 14:16:09 +0200 | [diff] [blame] | 164 | void rt2x00lib_toggle_rx(struct rt2x00_dev *rt2x00dev, enum dev_state state) | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 165 | { | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 166 | 	/* | 
 | 167 | 	 * When we are disabling the RX, we should also stop the link tuner. | 
 | 168 | 	 */ | 
| Ivo van Doorn | 5cbf830 | 2007-10-06 14:16:09 +0200 | [diff] [blame] | 169 | 	if (state == STATE_RADIO_RX_OFF) | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 170 | 		rt2x00lib_stop_link_tuner(rt2x00dev); | 
 | 171 |  | 
 | 172 | 	rt2x00dev->ops->lib->set_device_state(rt2x00dev, state); | 
 | 173 |  | 
 | 174 | 	/* | 
 | 175 | 	 * When we are enabling the RX, we should also start the link tuner. | 
 | 176 | 	 */ | 
| Ivo van Doorn | 5cbf830 | 2007-10-06 14:16:09 +0200 | [diff] [blame] | 177 | 	if (state == STATE_RADIO_RX_ON && | 
 | 178 | 	    is_interface_present(&rt2x00dev->interface)) | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 179 | 		rt2x00lib_start_link_tuner(rt2x00dev); | 
 | 180 | } | 
 | 181 |  | 
 | 182 | static void rt2x00lib_precalculate_link_signal(struct link *link) | 
 | 183 | { | 
 | 184 | 	if (link->rx_failed || link->rx_success) | 
 | 185 | 		link->rx_percentage = | 
 | 186 | 		    (link->rx_success * 100) / | 
 | 187 | 		    (link->rx_failed + link->rx_success); | 
 | 188 | 	else | 
 | 189 | 		link->rx_percentage = 50; | 
 | 190 |  | 
 | 191 | 	if (link->tx_failed || link->tx_success) | 
 | 192 | 		link->tx_percentage = | 
 | 193 | 		    (link->tx_success * 100) / | 
 | 194 | 		    (link->tx_failed + link->tx_success); | 
 | 195 | 	else | 
 | 196 | 		link->tx_percentage = 50; | 
 | 197 |  | 
 | 198 | 	link->rx_success = 0; | 
 | 199 | 	link->rx_failed = 0; | 
 | 200 | 	link->tx_success = 0; | 
 | 201 | 	link->tx_failed = 0; | 
 | 202 | } | 
 | 203 |  | 
 | 204 | static int rt2x00lib_calculate_link_signal(struct rt2x00_dev *rt2x00dev, | 
 | 205 | 					   int rssi) | 
 | 206 | { | 
 | 207 | 	int rssi_percentage = 0; | 
 | 208 | 	int signal; | 
 | 209 |  | 
 | 210 | 	/* | 
 | 211 | 	 * We need a positive value for the RSSI. | 
 | 212 | 	 */ | 
 | 213 | 	if (rssi < 0) | 
 | 214 | 		rssi += rt2x00dev->rssi_offset; | 
 | 215 |  | 
 | 216 | 	/* | 
 | 217 | 	 * Calculate the different percentages, | 
 | 218 | 	 * which will be used for the signal. | 
 | 219 | 	 */ | 
 | 220 | 	if (rt2x00dev->rssi_offset) | 
 | 221 | 		rssi_percentage = (rssi * 100) / rt2x00dev->rssi_offset; | 
 | 222 |  | 
 | 223 | 	/* | 
 | 224 | 	 * Add the individual percentages and use the WEIGHT | 
 | 225 | 	 * defines to calculate the current link signal. | 
 | 226 | 	 */ | 
 | 227 | 	signal = ((WEIGHT_RSSI * rssi_percentage) + | 
 | 228 | 		  (WEIGHT_TX * rt2x00dev->link.tx_percentage) + | 
 | 229 | 		  (WEIGHT_RX * rt2x00dev->link.rx_percentage)) / 100; | 
 | 230 |  | 
 | 231 | 	return (signal > 100) ? 100 : signal; | 
 | 232 | } | 
 | 233 |  | 
 | 234 | static void rt2x00lib_link_tuner(struct work_struct *work) | 
 | 235 | { | 
 | 236 | 	struct rt2x00_dev *rt2x00dev = | 
 | 237 | 	    container_of(work, struct rt2x00_dev, link.work.work); | 
 | 238 |  | 
 | 239 | 	/* | 
| Ivo van Doorn | 25ab002 | 2007-09-25 20:57:04 +0200 | [diff] [blame] | 240 | 	 * When the radio is shutting down we should | 
 | 241 | 	 * immediately cease all link tuning. | 
 | 242 | 	 */ | 
 | 243 | 	if (!test_bit(DEVICE_ENABLED_RADIO, &rt2x00dev->flags)) | 
 | 244 | 		return; | 
 | 245 |  | 
 | 246 | 	/* | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 247 | 	 * Update statistics. | 
 | 248 | 	 */ | 
 | 249 | 	rt2x00dev->ops->lib->link_stats(rt2x00dev); | 
 | 250 |  | 
 | 251 | 	rt2x00dev->low_level_stats.dot11FCSErrorCount += | 
 | 252 | 	    rt2x00dev->link.rx_failed; | 
 | 253 |  | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 254 | 	/* | 
 | 255 | 	 * Only perform the link tuning when Link tuning | 
 | 256 | 	 * has been enabled (This could have been disabled from the EEPROM). | 
 | 257 | 	 */ | 
 | 258 | 	if (!test_bit(CONFIG_DISABLE_LINK_TUNING, &rt2x00dev->flags)) | 
 | 259 | 		rt2x00dev->ops->lib->link_tuner(rt2x00dev); | 
 | 260 |  | 
 | 261 | 	/* | 
| Ivo van Doorn | 725d99d | 2007-09-25 20:53:20 +0200 | [diff] [blame] | 262 | 	 * Precalculate a portion of the link signal which is | 
 | 263 | 	 * in based on the tx/rx success/failure counters. | 
 | 264 | 	 */ | 
 | 265 | 	rt2x00lib_precalculate_link_signal(&rt2x00dev->link); | 
 | 266 |  | 
 | 267 | 	/* | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 268 | 	 * Increase tuner counter, and reschedule the next link tuner run. | 
 | 269 | 	 */ | 
 | 270 | 	rt2x00dev->link.count++; | 
 | 271 | 	queue_delayed_work(rt2x00dev->hw->workqueue, &rt2x00dev->link.work, | 
 | 272 | 			   LINK_TUNE_INTERVAL); | 
 | 273 | } | 
 | 274 |  | 
| Johannes Berg | 4150c57 | 2007-09-17 01:29:23 -0400 | [diff] [blame] | 275 | static void rt2x00lib_packetfilter_scheduled(struct work_struct *work) | 
 | 276 | { | 
 | 277 | 	struct rt2x00_dev *rt2x00dev = | 
 | 278 | 	    container_of(work, struct rt2x00_dev, filter_work); | 
| Ivo van Doorn | 5886d0d | 2007-10-06 14:13:38 +0200 | [diff] [blame] | 279 | 	unsigned int filter = rt2x00dev->interface.filter; | 
 | 280 |  | 
 | 281 | 	/* | 
 | 282 | 	 * Since we had stored the filter inside interface.filter, | 
 | 283 | 	 * we should now clear that field. Otherwise the driver will | 
 | 284 | 	 * assume nothing has changed (*total_flags will be compared | 
 | 285 | 	 * to interface.filter to determine if any action is required). | 
 | 286 | 	 */ | 
 | 287 | 	rt2x00dev->interface.filter = 0; | 
| Johannes Berg | 4150c57 | 2007-09-17 01:29:23 -0400 | [diff] [blame] | 288 |  | 
 | 289 | 	rt2x00dev->ops->hw->configure_filter(rt2x00dev->hw, | 
| Ivo van Doorn | 5886d0d | 2007-10-06 14:13:38 +0200 | [diff] [blame] | 290 | 					     filter, &filter, 0, NULL); | 
| Johannes Berg | 4150c57 | 2007-09-17 01:29:23 -0400 | [diff] [blame] | 291 | } | 
 | 292 |  | 
| Ivo van Doorn | 5c58ee5 | 2007-10-06 13:34:52 +0200 | [diff] [blame] | 293 | static void rt2x00lib_configuration_scheduled(struct work_struct *work) | 
 | 294 | { | 
 | 295 | 	struct rt2x00_dev *rt2x00dev = | 
 | 296 | 	    container_of(work, struct rt2x00_dev, config_work); | 
 | 297 | 	int preamble = !test_bit(CONFIG_SHORT_PREAMBLE, &rt2x00dev->flags); | 
 | 298 |  | 
 | 299 | 	rt2x00mac_erp_ie_changed(rt2x00dev->hw, | 
 | 300 | 				 IEEE80211_ERP_CHANGE_PREAMBLE, 0, preamble); | 
 | 301 | } | 
 | 302 |  | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 303 | /* | 
 | 304 |  * Interrupt context handlers. | 
 | 305 |  */ | 
 | 306 | static void rt2x00lib_beacondone_scheduled(struct work_struct *work) | 
 | 307 | { | 
 | 308 | 	struct rt2x00_dev *rt2x00dev = | 
 | 309 | 	    container_of(work, struct rt2x00_dev, beacon_work); | 
 | 310 | 	struct data_ring *ring = | 
 | 311 | 	    rt2x00lib_get_ring(rt2x00dev, IEEE80211_TX_QUEUE_BEACON); | 
 | 312 | 	struct data_entry *entry = rt2x00_get_data_entry(ring); | 
 | 313 | 	struct sk_buff *skb; | 
 | 314 |  | 
 | 315 | 	skb = ieee80211_beacon_get(rt2x00dev->hw, | 
 | 316 | 				   rt2x00dev->interface.id, | 
 | 317 | 				   &entry->tx_status.control); | 
 | 318 | 	if (!skb) | 
 | 319 | 		return; | 
 | 320 |  | 
 | 321 | 	rt2x00dev->ops->hw->beacon_update(rt2x00dev->hw, skb, | 
 | 322 | 					  &entry->tx_status.control); | 
 | 323 |  | 
 | 324 | 	dev_kfree_skb(skb); | 
 | 325 | } | 
 | 326 |  | 
 | 327 | void rt2x00lib_beacondone(struct rt2x00_dev *rt2x00dev) | 
 | 328 | { | 
 | 329 | 	if (!test_bit(DEVICE_ENABLED_RADIO, &rt2x00dev->flags)) | 
 | 330 | 		return; | 
 | 331 |  | 
 | 332 | 	queue_work(rt2x00dev->hw->workqueue, &rt2x00dev->beacon_work); | 
 | 333 | } | 
 | 334 | EXPORT_SYMBOL_GPL(rt2x00lib_beacondone); | 
 | 335 |  | 
 | 336 | void rt2x00lib_txdone(struct data_entry *entry, | 
 | 337 | 		      const int status, const int retry) | 
 | 338 | { | 
 | 339 | 	struct rt2x00_dev *rt2x00dev = entry->ring->rt2x00dev; | 
 | 340 | 	struct ieee80211_tx_status *tx_status = &entry->tx_status; | 
 | 341 | 	struct ieee80211_low_level_stats *stats = &rt2x00dev->low_level_stats; | 
 | 342 | 	int success = !!(status == TX_SUCCESS || status == TX_SUCCESS_RETRY); | 
 | 343 | 	int fail = !!(status == TX_FAIL_RETRY || status == TX_FAIL_INVALID || | 
 | 344 | 		      status == TX_FAIL_OTHER); | 
 | 345 |  | 
 | 346 | 	/* | 
 | 347 | 	 * Update TX statistics. | 
 | 348 | 	 */ | 
 | 349 | 	tx_status->flags = 0; | 
 | 350 | 	tx_status->ack_signal = 0; | 
 | 351 | 	tx_status->excessive_retries = (status == TX_FAIL_RETRY); | 
 | 352 | 	tx_status->retry_count = retry; | 
 | 353 | 	rt2x00dev->link.tx_success += success; | 
 | 354 | 	rt2x00dev->link.tx_failed += retry + fail; | 
 | 355 |  | 
 | 356 | 	if (!(tx_status->control.flags & IEEE80211_TXCTL_NO_ACK)) { | 
 | 357 | 		if (success) | 
 | 358 | 			tx_status->flags |= IEEE80211_TX_STATUS_ACK; | 
 | 359 | 		else | 
 | 360 | 			stats->dot11ACKFailureCount++; | 
 | 361 | 	} | 
 | 362 |  | 
 | 363 | 	tx_status->queue_length = entry->ring->stats.limit; | 
 | 364 | 	tx_status->queue_number = tx_status->control.queue; | 
 | 365 |  | 
 | 366 | 	if (tx_status->control.flags & IEEE80211_TXCTL_USE_RTS_CTS) { | 
 | 367 | 		if (success) | 
 | 368 | 			stats->dot11RTSSuccessCount++; | 
 | 369 | 		else | 
 | 370 | 			stats->dot11RTSFailureCount++; | 
 | 371 | 	} | 
 | 372 |  | 
 | 373 | 	/* | 
 | 374 | 	 * Send the tx_status to mac80211, | 
 | 375 | 	 * that method also cleans up the skb structure. | 
 | 376 | 	 */ | 
 | 377 | 	ieee80211_tx_status_irqsafe(rt2x00dev->hw, entry->skb, tx_status); | 
 | 378 | 	entry->skb = NULL; | 
 | 379 | } | 
 | 380 | EXPORT_SYMBOL_GPL(rt2x00lib_txdone); | 
 | 381 |  | 
 | 382 | void rt2x00lib_rxdone(struct data_entry *entry, struct sk_buff *skb, | 
| Johannes Berg | 4150c57 | 2007-09-17 01:29:23 -0400 | [diff] [blame] | 383 | 		      struct rxdata_entry_desc *desc) | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 384 | { | 
 | 385 | 	struct rt2x00_dev *rt2x00dev = entry->ring->rt2x00dev; | 
 | 386 | 	struct ieee80211_rx_status *rx_status = &rt2x00dev->rx_status; | 
 | 387 | 	struct ieee80211_hw_mode *mode; | 
 | 388 | 	struct ieee80211_rate *rate; | 
 | 389 | 	unsigned int i; | 
 | 390 | 	int val = 0; | 
 | 391 |  | 
 | 392 | 	/* | 
 | 393 | 	 * Update RX statistics. | 
 | 394 | 	 */ | 
 | 395 | 	mode = &rt2x00dev->hwmodes[rt2x00dev->curr_hwmode]; | 
 | 396 | 	for (i = 0; i < mode->num_rates; i++) { | 
 | 397 | 		rate = &mode->rates[i]; | 
 | 398 |  | 
 | 399 | 		/* | 
 | 400 | 		 * When frame was received with an OFDM bitrate, | 
 | 401 | 		 * the signal is the PLCP value. If it was received with | 
 | 402 | 		 * a CCK bitrate the signal is the rate in 0.5kbit/s. | 
 | 403 | 		 */ | 
| Johannes Berg | 4150c57 | 2007-09-17 01:29:23 -0400 | [diff] [blame] | 404 | 		if (!desc->ofdm) | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 405 | 			val = DEVICE_GET_RATE_FIELD(rate->val, RATE); | 
 | 406 | 		else | 
 | 407 | 			val = DEVICE_GET_RATE_FIELD(rate->val, PLCP); | 
 | 408 |  | 
| Johannes Berg | 4150c57 | 2007-09-17 01:29:23 -0400 | [diff] [blame] | 409 | 		if (val == desc->signal) { | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 410 | 			val = rate->val; | 
 | 411 | 			break; | 
 | 412 | 		} | 
 | 413 | 	} | 
 | 414 |  | 
| Johannes Berg | 4150c57 | 2007-09-17 01:29:23 -0400 | [diff] [blame] | 415 | 	rt2x00_update_link_rssi(&rt2x00dev->link, desc->rssi); | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 416 | 	rt2x00dev->link.rx_success++; | 
 | 417 | 	rx_status->rate = val; | 
| Johannes Berg | 4150c57 | 2007-09-17 01:29:23 -0400 | [diff] [blame] | 418 | 	rx_status->signal = | 
 | 419 | 	    rt2x00lib_calculate_link_signal(rt2x00dev, desc->rssi); | 
 | 420 | 	rx_status->ssi = desc->rssi; | 
 | 421 | 	rx_status->flag = desc->flags; | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 422 |  | 
 | 423 | 	/* | 
 | 424 | 	 * Send frame to mac80211 | 
 | 425 | 	 */ | 
 | 426 | 	ieee80211_rx_irqsafe(rt2x00dev->hw, skb, rx_status); | 
 | 427 | } | 
 | 428 | EXPORT_SYMBOL_GPL(rt2x00lib_rxdone); | 
 | 429 |  | 
 | 430 | /* | 
 | 431 |  * TX descriptor initializer | 
 | 432 |  */ | 
 | 433 | void rt2x00lib_write_tx_desc(struct rt2x00_dev *rt2x00dev, | 
 | 434 | 			     struct data_desc *txd, | 
 | 435 | 			     struct ieee80211_hdr *ieee80211hdr, | 
 | 436 | 			     unsigned int length, | 
 | 437 | 			     struct ieee80211_tx_control *control) | 
 | 438 | { | 
| Johannes Berg | 4150c57 | 2007-09-17 01:29:23 -0400 | [diff] [blame] | 439 | 	struct txdata_entry_desc desc; | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 440 | 	struct data_ring *ring; | 
 | 441 | 	int tx_rate; | 
 | 442 | 	int bitrate; | 
 | 443 | 	int duration; | 
 | 444 | 	int residual; | 
 | 445 | 	u16 frame_control; | 
 | 446 | 	u16 seq_ctrl; | 
 | 447 |  | 
 | 448 | 	/* | 
 | 449 | 	 * Make sure the descriptor is properly cleared. | 
 | 450 | 	 */ | 
 | 451 | 	memset(&desc, 0x00, sizeof(desc)); | 
 | 452 |  | 
 | 453 | 	/* | 
 | 454 | 	 * Get ring pointer, if we fail to obtain the | 
 | 455 | 	 * correct ring, then use the first TX ring. | 
 | 456 | 	 */ | 
 | 457 | 	ring = rt2x00lib_get_ring(rt2x00dev, control->queue); | 
 | 458 | 	if (!ring) | 
 | 459 | 		ring = rt2x00lib_get_ring(rt2x00dev, IEEE80211_TX_QUEUE_DATA0); | 
 | 460 |  | 
 | 461 | 	desc.cw_min = ring->tx_params.cw_min; | 
 | 462 | 	desc.cw_max = ring->tx_params.cw_max; | 
 | 463 | 	desc.aifs = ring->tx_params.aifs; | 
 | 464 |  | 
 | 465 | 	/* | 
 | 466 | 	 * Identify queue | 
 | 467 | 	 */ | 
 | 468 | 	if (control->queue < rt2x00dev->hw->queues) | 
 | 469 | 		desc.queue = control->queue; | 
 | 470 | 	else if (control->queue == IEEE80211_TX_QUEUE_BEACON || | 
 | 471 | 		 control->queue == IEEE80211_TX_QUEUE_AFTER_BEACON) | 
 | 472 | 		desc.queue = QUEUE_MGMT; | 
 | 473 | 	else | 
 | 474 | 		desc.queue = QUEUE_OTHER; | 
 | 475 |  | 
 | 476 | 	/* | 
 | 477 | 	 * Read required fields from ieee80211 header. | 
 | 478 | 	 */ | 
 | 479 | 	frame_control = le16_to_cpu(ieee80211hdr->frame_control); | 
 | 480 | 	seq_ctrl = le16_to_cpu(ieee80211hdr->seq_ctrl); | 
 | 481 |  | 
 | 482 | 	tx_rate = control->tx_rate; | 
 | 483 |  | 
 | 484 | 	/* | 
 | 485 | 	 * Check if this is a RTS/CTS frame | 
 | 486 | 	 */ | 
 | 487 | 	if (is_rts_frame(frame_control) || is_cts_frame(frame_control)) { | 
 | 488 | 		__set_bit(ENTRY_TXD_BURST, &desc.flags); | 
 | 489 | 		if (is_rts_frame(frame_control)) | 
 | 490 | 			__set_bit(ENTRY_TXD_RTS_FRAME, &desc.flags); | 
 | 491 | 		if (control->rts_cts_rate) | 
 | 492 | 			tx_rate = control->rts_cts_rate; | 
 | 493 | 	} | 
 | 494 |  | 
 | 495 | 	/* | 
 | 496 | 	 * Check for OFDM | 
 | 497 | 	 */ | 
 | 498 | 	if (DEVICE_GET_RATE_FIELD(tx_rate, RATEMASK) & DEV_OFDM_RATEMASK) | 
 | 499 | 		__set_bit(ENTRY_TXD_OFDM_RATE, &desc.flags); | 
 | 500 |  | 
 | 501 | 	/* | 
 | 502 | 	 * Check if more fragments are pending | 
 | 503 | 	 */ | 
 | 504 | 	if (ieee80211_get_morefrag(ieee80211hdr)) { | 
 | 505 | 		__set_bit(ENTRY_TXD_BURST, &desc.flags); | 
 | 506 | 		__set_bit(ENTRY_TXD_MORE_FRAG, &desc.flags); | 
 | 507 | 	} | 
 | 508 |  | 
 | 509 | 	/* | 
 | 510 | 	 * Beacons and probe responses require the tsf timestamp | 
 | 511 | 	 * to be inserted into the frame. | 
 | 512 | 	 */ | 
 | 513 | 	if (control->queue == IEEE80211_TX_QUEUE_BEACON || | 
 | 514 | 	    is_probe_resp(frame_control)) | 
 | 515 | 		__set_bit(ENTRY_TXD_REQ_TIMESTAMP, &desc.flags); | 
 | 516 |  | 
 | 517 | 	/* | 
 | 518 | 	 * Determine with what IFS priority this frame should be send. | 
 | 519 | 	 * Set ifs to IFS_SIFS when the this is not the first fragment, | 
 | 520 | 	 * or this fragment came after RTS/CTS. | 
 | 521 | 	 */ | 
 | 522 | 	if ((seq_ctrl & IEEE80211_SCTL_FRAG) > 0 || | 
 | 523 | 	    test_bit(ENTRY_TXD_RTS_FRAME, &desc.flags)) | 
 | 524 | 		desc.ifs = IFS_SIFS; | 
 | 525 | 	else | 
 | 526 | 		desc.ifs = IFS_BACKOFF; | 
 | 527 |  | 
 | 528 | 	/* | 
 | 529 | 	 * PLCP setup | 
 | 530 | 	 * Length calculation depends on OFDM/CCK rate. | 
 | 531 | 	 */ | 
 | 532 | 	desc.signal = DEVICE_GET_RATE_FIELD(tx_rate, PLCP); | 
 | 533 | 	desc.service = 0x04; | 
 | 534 |  | 
 | 535 | 	if (test_bit(ENTRY_TXD_OFDM_RATE, &desc.flags)) { | 
 | 536 | 		desc.length_high = ((length + FCS_LEN) >> 6) & 0x3f; | 
 | 537 | 		desc.length_low = ((length + FCS_LEN) & 0x3f); | 
 | 538 | 	} else { | 
 | 539 | 		bitrate = DEVICE_GET_RATE_FIELD(tx_rate, RATE); | 
 | 540 |  | 
 | 541 | 		/* | 
 | 542 | 		 * Convert length to microseconds. | 
 | 543 | 		 */ | 
 | 544 | 		residual = get_duration_res(length + FCS_LEN, bitrate); | 
 | 545 | 		duration = get_duration(length + FCS_LEN, bitrate); | 
 | 546 |  | 
 | 547 | 		if (residual != 0) { | 
 | 548 | 			duration++; | 
 | 549 |  | 
 | 550 | 			/* | 
 | 551 | 			 * Check if we need to set the Length Extension | 
 | 552 | 			 */ | 
| Mattias Nissler | db15178 | 2007-10-13 16:26:52 +0200 | [diff] [blame] | 553 | 			if (bitrate == 110 && residual <= 30) | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 554 | 				desc.service |= 0x80; | 
 | 555 | 		} | 
 | 556 |  | 
 | 557 | 		desc.length_high = (duration >> 8) & 0xff; | 
 | 558 | 		desc.length_low = duration & 0xff; | 
 | 559 |  | 
 | 560 | 		/* | 
 | 561 | 		 * When preamble is enabled we should set the | 
 | 562 | 		 * preamble bit for the signal. | 
 | 563 | 		 */ | 
 | 564 | 		if (DEVICE_GET_RATE_FIELD(tx_rate, PREAMBLE)) | 
 | 565 | 			desc.signal |= 0x08; | 
 | 566 | 	} | 
 | 567 |  | 
 | 568 | 	rt2x00dev->ops->lib->write_tx_desc(rt2x00dev, txd, &desc, | 
 | 569 | 					   ieee80211hdr, length, control); | 
 | 570 | } | 
 | 571 | EXPORT_SYMBOL_GPL(rt2x00lib_write_tx_desc); | 
 | 572 |  | 
 | 573 | /* | 
 | 574 |  * Driver initialization handlers. | 
 | 575 |  */ | 
 | 576 | static void rt2x00lib_channel(struct ieee80211_channel *entry, | 
 | 577 | 			      const int channel, const int tx_power, | 
 | 578 | 			      const int value) | 
 | 579 | { | 
 | 580 | 	entry->chan = channel; | 
 | 581 | 	if (channel <= 14) | 
 | 582 | 		entry->freq = 2407 + (5 * channel); | 
 | 583 | 	else | 
 | 584 | 		entry->freq = 5000 + (5 * channel); | 
 | 585 | 	entry->val = value; | 
 | 586 | 	entry->flag = | 
 | 587 | 	    IEEE80211_CHAN_W_IBSS | | 
 | 588 | 	    IEEE80211_CHAN_W_ACTIVE_SCAN | | 
 | 589 | 	    IEEE80211_CHAN_W_SCAN; | 
 | 590 | 	entry->power_level = tx_power; | 
 | 591 | 	entry->antenna_max = 0xff; | 
 | 592 | } | 
 | 593 |  | 
 | 594 | static void rt2x00lib_rate(struct ieee80211_rate *entry, | 
 | 595 | 			   const int rate, const int mask, | 
 | 596 | 			   const int plcp, const int flags) | 
 | 597 | { | 
 | 598 | 	entry->rate = rate; | 
 | 599 | 	entry->val = | 
 | 600 | 	    DEVICE_SET_RATE_FIELD(rate, RATE) | | 
 | 601 | 	    DEVICE_SET_RATE_FIELD(mask, RATEMASK) | | 
 | 602 | 	    DEVICE_SET_RATE_FIELD(plcp, PLCP); | 
 | 603 | 	entry->flags = flags; | 
 | 604 | 	entry->val2 = entry->val; | 
 | 605 | 	if (entry->flags & IEEE80211_RATE_PREAMBLE2) | 
 | 606 | 		entry->val2 |= DEVICE_SET_RATE_FIELD(1, PREAMBLE); | 
 | 607 | 	entry->min_rssi_ack = 0; | 
 | 608 | 	entry->min_rssi_ack_delta = 0; | 
 | 609 | } | 
 | 610 |  | 
 | 611 | static int rt2x00lib_probe_hw_modes(struct rt2x00_dev *rt2x00dev, | 
 | 612 | 				    struct hw_mode_spec *spec) | 
 | 613 | { | 
 | 614 | 	struct ieee80211_hw *hw = rt2x00dev->hw; | 
 | 615 | 	struct ieee80211_hw_mode *hwmodes; | 
 | 616 | 	struct ieee80211_channel *channels; | 
 | 617 | 	struct ieee80211_rate *rates; | 
 | 618 | 	unsigned int i; | 
 | 619 | 	unsigned char tx_power; | 
 | 620 |  | 
 | 621 | 	hwmodes = kzalloc(sizeof(*hwmodes) * spec->num_modes, GFP_KERNEL); | 
 | 622 | 	if (!hwmodes) | 
 | 623 | 		goto exit; | 
 | 624 |  | 
 | 625 | 	channels = kzalloc(sizeof(*channels) * spec->num_channels, GFP_KERNEL); | 
 | 626 | 	if (!channels) | 
 | 627 | 		goto exit_free_modes; | 
 | 628 |  | 
 | 629 | 	rates = kzalloc(sizeof(*rates) * spec->num_rates, GFP_KERNEL); | 
 | 630 | 	if (!rates) | 
 | 631 | 		goto exit_free_channels; | 
 | 632 |  | 
 | 633 | 	/* | 
 | 634 | 	 * Initialize Rate list. | 
 | 635 | 	 */ | 
 | 636 | 	rt2x00lib_rate(&rates[0], 10, DEV_RATEMASK_1MB, | 
 | 637 | 		       0x00, IEEE80211_RATE_CCK); | 
 | 638 | 	rt2x00lib_rate(&rates[1], 20, DEV_RATEMASK_2MB, | 
 | 639 | 		       0x01, IEEE80211_RATE_CCK_2); | 
 | 640 | 	rt2x00lib_rate(&rates[2], 55, DEV_RATEMASK_5_5MB, | 
 | 641 | 		       0x02, IEEE80211_RATE_CCK_2); | 
 | 642 | 	rt2x00lib_rate(&rates[3], 110, DEV_RATEMASK_11MB, | 
 | 643 | 		       0x03, IEEE80211_RATE_CCK_2); | 
 | 644 |  | 
 | 645 | 	if (spec->num_rates > 4) { | 
 | 646 | 		rt2x00lib_rate(&rates[4], 60, DEV_RATEMASK_6MB, | 
 | 647 | 			       0x0b, IEEE80211_RATE_OFDM); | 
 | 648 | 		rt2x00lib_rate(&rates[5], 90, DEV_RATEMASK_9MB, | 
 | 649 | 			       0x0f, IEEE80211_RATE_OFDM); | 
 | 650 | 		rt2x00lib_rate(&rates[6], 120, DEV_RATEMASK_12MB, | 
 | 651 | 			       0x0a, IEEE80211_RATE_OFDM); | 
 | 652 | 		rt2x00lib_rate(&rates[7], 180, DEV_RATEMASK_18MB, | 
 | 653 | 			       0x0e, IEEE80211_RATE_OFDM); | 
 | 654 | 		rt2x00lib_rate(&rates[8], 240, DEV_RATEMASK_24MB, | 
 | 655 | 			       0x09, IEEE80211_RATE_OFDM); | 
 | 656 | 		rt2x00lib_rate(&rates[9], 360, DEV_RATEMASK_36MB, | 
 | 657 | 			       0x0d, IEEE80211_RATE_OFDM); | 
 | 658 | 		rt2x00lib_rate(&rates[10], 480, DEV_RATEMASK_48MB, | 
 | 659 | 			       0x08, IEEE80211_RATE_OFDM); | 
 | 660 | 		rt2x00lib_rate(&rates[11], 540, DEV_RATEMASK_54MB, | 
 | 661 | 			       0x0c, IEEE80211_RATE_OFDM); | 
 | 662 | 	} | 
 | 663 |  | 
 | 664 | 	/* | 
 | 665 | 	 * Initialize Channel list. | 
 | 666 | 	 */ | 
 | 667 | 	for (i = 0; i < spec->num_channels; i++) { | 
 | 668 | 		if (spec->channels[i].channel <= 14) | 
 | 669 | 			tx_power = spec->tx_power_bg[i]; | 
 | 670 | 		else if (spec->tx_power_a) | 
 | 671 | 			tx_power = spec->tx_power_a[i]; | 
 | 672 | 		else | 
 | 673 | 			tx_power = spec->tx_power_default; | 
 | 674 |  | 
 | 675 | 		rt2x00lib_channel(&channels[i], | 
 | 676 | 				  spec->channels[i].channel, tx_power, i); | 
 | 677 | 	} | 
 | 678 |  | 
 | 679 | 	/* | 
 | 680 | 	 * Intitialize 802.11b | 
 | 681 | 	 * Rates: CCK. | 
 | 682 | 	 * Channels: OFDM. | 
 | 683 | 	 */ | 
 | 684 | 	if (spec->num_modes > HWMODE_B) { | 
 | 685 | 		hwmodes[HWMODE_B].mode = MODE_IEEE80211B; | 
 | 686 | 		hwmodes[HWMODE_B].num_channels = 14; | 
 | 687 | 		hwmodes[HWMODE_B].num_rates = 4; | 
 | 688 | 		hwmodes[HWMODE_B].channels = channels; | 
 | 689 | 		hwmodes[HWMODE_B].rates = rates; | 
 | 690 | 	} | 
 | 691 |  | 
 | 692 | 	/* | 
 | 693 | 	 * Intitialize 802.11g | 
 | 694 | 	 * Rates: CCK, OFDM. | 
 | 695 | 	 * Channels: OFDM. | 
 | 696 | 	 */ | 
 | 697 | 	if (spec->num_modes > HWMODE_G) { | 
 | 698 | 		hwmodes[HWMODE_G].mode = MODE_IEEE80211G; | 
 | 699 | 		hwmodes[HWMODE_G].num_channels = 14; | 
 | 700 | 		hwmodes[HWMODE_G].num_rates = spec->num_rates; | 
 | 701 | 		hwmodes[HWMODE_G].channels = channels; | 
 | 702 | 		hwmodes[HWMODE_G].rates = rates; | 
 | 703 | 	} | 
 | 704 |  | 
 | 705 | 	/* | 
 | 706 | 	 * Intitialize 802.11a | 
 | 707 | 	 * Rates: OFDM. | 
 | 708 | 	 * Channels: OFDM, UNII, HiperLAN2. | 
 | 709 | 	 */ | 
 | 710 | 	if (spec->num_modes > HWMODE_A) { | 
 | 711 | 		hwmodes[HWMODE_A].mode = MODE_IEEE80211A; | 
 | 712 | 		hwmodes[HWMODE_A].num_channels = spec->num_channels - 14; | 
 | 713 | 		hwmodes[HWMODE_A].num_rates = spec->num_rates - 4; | 
 | 714 | 		hwmodes[HWMODE_A].channels = &channels[14]; | 
 | 715 | 		hwmodes[HWMODE_A].rates = &rates[4]; | 
 | 716 | 	} | 
 | 717 |  | 
 | 718 | 	if (spec->num_modes > HWMODE_G && | 
 | 719 | 	    ieee80211_register_hwmode(hw, &hwmodes[HWMODE_G])) | 
 | 720 | 		goto exit_free_rates; | 
 | 721 |  | 
 | 722 | 	if (spec->num_modes > HWMODE_B && | 
 | 723 | 	    ieee80211_register_hwmode(hw, &hwmodes[HWMODE_B])) | 
 | 724 | 		goto exit_free_rates; | 
 | 725 |  | 
 | 726 | 	if (spec->num_modes > HWMODE_A && | 
 | 727 | 	    ieee80211_register_hwmode(hw, &hwmodes[HWMODE_A])) | 
 | 728 | 		goto exit_free_rates; | 
 | 729 |  | 
 | 730 | 	rt2x00dev->hwmodes = hwmodes; | 
 | 731 |  | 
 | 732 | 	return 0; | 
 | 733 |  | 
 | 734 | exit_free_rates: | 
 | 735 | 	kfree(rates); | 
 | 736 |  | 
 | 737 | exit_free_channels: | 
 | 738 | 	kfree(channels); | 
 | 739 |  | 
 | 740 | exit_free_modes: | 
 | 741 | 	kfree(hwmodes); | 
 | 742 |  | 
 | 743 | exit: | 
 | 744 | 	ERROR(rt2x00dev, "Allocation ieee80211 modes failed.\n"); | 
 | 745 | 	return -ENOMEM; | 
 | 746 | } | 
 | 747 |  | 
 | 748 | static void rt2x00lib_remove_hw(struct rt2x00_dev *rt2x00dev) | 
 | 749 | { | 
| Ivo van Doorn | 066cb63 | 2007-09-25 20:55:39 +0200 | [diff] [blame] | 750 | 	if (test_bit(DEVICE_REGISTERED_HW, &rt2x00dev->flags)) | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 751 | 		ieee80211_unregister_hw(rt2x00dev->hw); | 
 | 752 |  | 
 | 753 | 	if (likely(rt2x00dev->hwmodes)) { | 
 | 754 | 		kfree(rt2x00dev->hwmodes->channels); | 
 | 755 | 		kfree(rt2x00dev->hwmodes->rates); | 
 | 756 | 		kfree(rt2x00dev->hwmodes); | 
 | 757 | 		rt2x00dev->hwmodes = NULL; | 
 | 758 | 	} | 
 | 759 | } | 
 | 760 |  | 
 | 761 | static int rt2x00lib_probe_hw(struct rt2x00_dev *rt2x00dev) | 
 | 762 | { | 
 | 763 | 	struct hw_mode_spec *spec = &rt2x00dev->spec; | 
 | 764 | 	int status; | 
 | 765 |  | 
 | 766 | 	/* | 
 | 767 | 	 * Initialize HW modes. | 
 | 768 | 	 */ | 
 | 769 | 	status = rt2x00lib_probe_hw_modes(rt2x00dev, spec); | 
 | 770 | 	if (status) | 
 | 771 | 		return status; | 
 | 772 |  | 
 | 773 | 	/* | 
 | 774 | 	 * Register HW. | 
 | 775 | 	 */ | 
 | 776 | 	status = ieee80211_register_hw(rt2x00dev->hw); | 
 | 777 | 	if (status) { | 
 | 778 | 		rt2x00lib_remove_hw(rt2x00dev); | 
 | 779 | 		return status; | 
 | 780 | 	} | 
 | 781 |  | 
| Ivo van Doorn | 066cb63 | 2007-09-25 20:55:39 +0200 | [diff] [blame] | 782 | 	__set_bit(DEVICE_REGISTERED_HW, &rt2x00dev->flags); | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 783 |  | 
 | 784 | 	return 0; | 
 | 785 | } | 
 | 786 |  | 
 | 787 | /* | 
 | 788 |  * Initialization/uninitialization handlers. | 
 | 789 |  */ | 
 | 790 | static int rt2x00lib_alloc_entries(struct data_ring *ring, | 
 | 791 | 				   const u16 max_entries, const u16 data_size, | 
 | 792 | 				   const u16 desc_size) | 
 | 793 | { | 
 | 794 | 	struct data_entry *entry; | 
 | 795 | 	unsigned int i; | 
 | 796 |  | 
 | 797 | 	ring->stats.limit = max_entries; | 
 | 798 | 	ring->data_size = data_size; | 
 | 799 | 	ring->desc_size = desc_size; | 
 | 800 |  | 
 | 801 | 	/* | 
 | 802 | 	 * Allocate all ring entries. | 
 | 803 | 	 */ | 
 | 804 | 	entry = kzalloc(ring->stats.limit * sizeof(*entry), GFP_KERNEL); | 
 | 805 | 	if (!entry) | 
 | 806 | 		return -ENOMEM; | 
 | 807 |  | 
 | 808 | 	for (i = 0; i < ring->stats.limit; i++) { | 
 | 809 | 		entry[i].flags = 0; | 
 | 810 | 		entry[i].ring = ring; | 
 | 811 | 		entry[i].skb = NULL; | 
 | 812 | 	} | 
 | 813 |  | 
 | 814 | 	ring->entry = entry; | 
 | 815 |  | 
 | 816 | 	return 0; | 
 | 817 | } | 
 | 818 |  | 
 | 819 | static int rt2x00lib_alloc_ring_entries(struct rt2x00_dev *rt2x00dev) | 
 | 820 | { | 
 | 821 | 	struct data_ring *ring; | 
 | 822 |  | 
 | 823 | 	/* | 
 | 824 | 	 * Allocate the RX ring. | 
 | 825 | 	 */ | 
 | 826 | 	if (rt2x00lib_alloc_entries(rt2x00dev->rx, RX_ENTRIES, DATA_FRAME_SIZE, | 
 | 827 | 				    rt2x00dev->ops->rxd_size)) | 
 | 828 | 		return -ENOMEM; | 
 | 829 |  | 
 | 830 | 	/* | 
 | 831 | 	 * First allocate the TX rings. | 
 | 832 | 	 */ | 
 | 833 | 	txring_for_each(rt2x00dev, ring) { | 
 | 834 | 		if (rt2x00lib_alloc_entries(ring, TX_ENTRIES, DATA_FRAME_SIZE, | 
 | 835 | 					    rt2x00dev->ops->txd_size)) | 
 | 836 | 			return -ENOMEM; | 
 | 837 | 	} | 
 | 838 |  | 
| Ivo van Doorn | 066cb63 | 2007-09-25 20:55:39 +0200 | [diff] [blame] | 839 | 	if (!test_bit(DRIVER_REQUIRE_BEACON_RING, &rt2x00dev->flags)) | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 840 | 		return 0; | 
 | 841 |  | 
 | 842 | 	/* | 
 | 843 | 	 * Allocate the BEACON ring. | 
 | 844 | 	 */ | 
 | 845 | 	if (rt2x00lib_alloc_entries(&rt2x00dev->bcn[0], BEACON_ENTRIES, | 
 | 846 | 				    MGMT_FRAME_SIZE, rt2x00dev->ops->txd_size)) | 
 | 847 | 		return -ENOMEM; | 
 | 848 |  | 
 | 849 | 	/* | 
 | 850 | 	 * Allocate the Atim ring. | 
 | 851 | 	 */ | 
 | 852 | 	if (rt2x00lib_alloc_entries(&rt2x00dev->bcn[1], ATIM_ENTRIES, | 
 | 853 | 				    DATA_FRAME_SIZE, rt2x00dev->ops->txd_size)) | 
 | 854 | 		return -ENOMEM; | 
 | 855 |  | 
 | 856 | 	return 0; | 
 | 857 | } | 
 | 858 |  | 
 | 859 | static void rt2x00lib_free_ring_entries(struct rt2x00_dev *rt2x00dev) | 
 | 860 | { | 
 | 861 | 	struct data_ring *ring; | 
 | 862 |  | 
 | 863 | 	ring_for_each(rt2x00dev, ring) { | 
 | 864 | 		kfree(ring->entry); | 
 | 865 | 		ring->entry = NULL; | 
 | 866 | 	} | 
 | 867 | } | 
 | 868 |  | 
 | 869 | void rt2x00lib_uninitialize(struct rt2x00_dev *rt2x00dev) | 
 | 870 | { | 
 | 871 | 	if (!__test_and_clear_bit(DEVICE_INITIALIZED, &rt2x00dev->flags)) | 
 | 872 | 		return; | 
 | 873 |  | 
 | 874 | 	/* | 
 | 875 | 	 * Unregister rfkill. | 
 | 876 | 	 */ | 
 | 877 | 	rt2x00rfkill_unregister(rt2x00dev); | 
 | 878 |  | 
 | 879 | 	/* | 
 | 880 | 	 * Allow the HW to uninitialize. | 
 | 881 | 	 */ | 
 | 882 | 	rt2x00dev->ops->lib->uninitialize(rt2x00dev); | 
 | 883 |  | 
 | 884 | 	/* | 
 | 885 | 	 * Free allocated ring entries. | 
 | 886 | 	 */ | 
 | 887 | 	rt2x00lib_free_ring_entries(rt2x00dev); | 
 | 888 | } | 
 | 889 |  | 
 | 890 | int rt2x00lib_initialize(struct rt2x00_dev *rt2x00dev) | 
 | 891 | { | 
 | 892 | 	int status; | 
 | 893 |  | 
 | 894 | 	if (test_bit(DEVICE_INITIALIZED, &rt2x00dev->flags)) | 
 | 895 | 		return 0; | 
 | 896 |  | 
 | 897 | 	/* | 
 | 898 | 	 * Allocate all ring entries. | 
 | 899 | 	 */ | 
 | 900 | 	status = rt2x00lib_alloc_ring_entries(rt2x00dev); | 
 | 901 | 	if (status) { | 
 | 902 | 		ERROR(rt2x00dev, "Ring entries allocation failed.\n"); | 
 | 903 | 		return status; | 
 | 904 | 	} | 
 | 905 |  | 
 | 906 | 	/* | 
 | 907 | 	 * Initialize the device. | 
 | 908 | 	 */ | 
 | 909 | 	status = rt2x00dev->ops->lib->initialize(rt2x00dev); | 
 | 910 | 	if (status) | 
 | 911 | 		goto exit; | 
 | 912 |  | 
 | 913 | 	__set_bit(DEVICE_INITIALIZED, &rt2x00dev->flags); | 
 | 914 |  | 
 | 915 | 	/* | 
 | 916 | 	 * Register the rfkill handler. | 
 | 917 | 	 */ | 
 | 918 | 	status = rt2x00rfkill_register(rt2x00dev); | 
 | 919 | 	if (status) | 
 | 920 | 		goto exit_unitialize; | 
 | 921 |  | 
 | 922 | 	return 0; | 
 | 923 |  | 
 | 924 | exit_unitialize: | 
 | 925 | 	rt2x00lib_uninitialize(rt2x00dev); | 
 | 926 |  | 
 | 927 | exit: | 
 | 928 | 	rt2x00lib_free_ring_entries(rt2x00dev); | 
 | 929 |  | 
 | 930 | 	return status; | 
 | 931 | } | 
 | 932 |  | 
 | 933 | /* | 
 | 934 |  * driver allocation handlers. | 
 | 935 |  */ | 
 | 936 | static int rt2x00lib_alloc_rings(struct rt2x00_dev *rt2x00dev) | 
 | 937 | { | 
 | 938 | 	struct data_ring *ring; | 
 | 939 |  | 
 | 940 | 	/* | 
 | 941 | 	 * We need the following rings: | 
 | 942 | 	 * RX: 1 | 
 | 943 | 	 * TX: hw->queues | 
 | 944 | 	 * Beacon: 1 (if required) | 
 | 945 | 	 * Atim: 1 (if required) | 
 | 946 | 	 */ | 
 | 947 | 	rt2x00dev->data_rings = 1 + rt2x00dev->hw->queues + | 
| Ivo van Doorn | 066cb63 | 2007-09-25 20:55:39 +0200 | [diff] [blame] | 948 | 	    (2 * test_bit(DRIVER_REQUIRE_BEACON_RING, &rt2x00dev->flags)); | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 949 |  | 
 | 950 | 	ring = kzalloc(rt2x00dev->data_rings * sizeof(*ring), GFP_KERNEL); | 
 | 951 | 	if (!ring) { | 
 | 952 | 		ERROR(rt2x00dev, "Ring allocation failed.\n"); | 
 | 953 | 		return -ENOMEM; | 
 | 954 | 	} | 
 | 955 |  | 
 | 956 | 	/* | 
 | 957 | 	 * Initialize pointers | 
 | 958 | 	 */ | 
 | 959 | 	rt2x00dev->rx = ring; | 
 | 960 | 	rt2x00dev->tx = &rt2x00dev->rx[1]; | 
| Ivo van Doorn | 066cb63 | 2007-09-25 20:55:39 +0200 | [diff] [blame] | 961 | 	if (test_bit(DRIVER_REQUIRE_BEACON_RING, &rt2x00dev->flags)) | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 962 | 		rt2x00dev->bcn = &rt2x00dev->tx[rt2x00dev->hw->queues]; | 
 | 963 |  | 
 | 964 | 	/* | 
 | 965 | 	 * Initialize ring parameters. | 
 | 966 | 	 * cw_min: 2^5 = 32. | 
 | 967 | 	 * cw_max: 2^10 = 1024. | 
 | 968 | 	 */ | 
 | 969 | 	ring_for_each(rt2x00dev, ring) { | 
 | 970 | 		ring->rt2x00dev = rt2x00dev; | 
 | 971 | 		ring->tx_params.aifs = 2; | 
 | 972 | 		ring->tx_params.cw_min = 5; | 
 | 973 | 		ring->tx_params.cw_max = 10; | 
 | 974 | 	} | 
 | 975 |  | 
 | 976 | 	return 0; | 
 | 977 | } | 
 | 978 |  | 
 | 979 | static void rt2x00lib_free_rings(struct rt2x00_dev *rt2x00dev) | 
 | 980 | { | 
 | 981 | 	kfree(rt2x00dev->rx); | 
 | 982 | 	rt2x00dev->rx = NULL; | 
 | 983 | 	rt2x00dev->tx = NULL; | 
 | 984 | 	rt2x00dev->bcn = NULL; | 
 | 985 | } | 
 | 986 |  | 
 | 987 | int rt2x00lib_probe_dev(struct rt2x00_dev *rt2x00dev) | 
 | 988 | { | 
 | 989 | 	int retval = -ENOMEM; | 
 | 990 |  | 
 | 991 | 	/* | 
 | 992 | 	 * Let the driver probe the device to detect the capabilities. | 
 | 993 | 	 */ | 
 | 994 | 	retval = rt2x00dev->ops->lib->probe_hw(rt2x00dev); | 
 | 995 | 	if (retval) { | 
 | 996 | 		ERROR(rt2x00dev, "Failed to allocate device.\n"); | 
 | 997 | 		goto exit; | 
 | 998 | 	} | 
 | 999 |  | 
 | 1000 | 	/* | 
 | 1001 | 	 * Initialize configuration work. | 
 | 1002 | 	 */ | 
 | 1003 | 	INIT_WORK(&rt2x00dev->beacon_work, rt2x00lib_beacondone_scheduled); | 
| Johannes Berg | 4150c57 | 2007-09-17 01:29:23 -0400 | [diff] [blame] | 1004 | 	INIT_WORK(&rt2x00dev->filter_work, rt2x00lib_packetfilter_scheduled); | 
| Ivo van Doorn | 5c58ee5 | 2007-10-06 13:34:52 +0200 | [diff] [blame] | 1005 | 	INIT_WORK(&rt2x00dev->config_work, rt2x00lib_configuration_scheduled); | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 1006 | 	INIT_DELAYED_WORK(&rt2x00dev->link.work, rt2x00lib_link_tuner); | 
 | 1007 |  | 
 | 1008 | 	/* | 
 | 1009 | 	 * Reset current working type. | 
 | 1010 | 	 */ | 
 | 1011 | 	rt2x00dev->interface.type = INVALID_INTERFACE; | 
 | 1012 |  | 
 | 1013 | 	/* | 
 | 1014 | 	 * Allocate ring array. | 
 | 1015 | 	 */ | 
 | 1016 | 	retval = rt2x00lib_alloc_rings(rt2x00dev); | 
 | 1017 | 	if (retval) | 
 | 1018 | 		goto exit; | 
 | 1019 |  | 
 | 1020 | 	/* | 
 | 1021 | 	 * Initialize ieee80211 structure. | 
 | 1022 | 	 */ | 
 | 1023 | 	retval = rt2x00lib_probe_hw(rt2x00dev); | 
 | 1024 | 	if (retval) { | 
 | 1025 | 		ERROR(rt2x00dev, "Failed to initialize hw.\n"); | 
 | 1026 | 		goto exit; | 
 | 1027 | 	} | 
 | 1028 |  | 
 | 1029 | 	/* | 
 | 1030 | 	 * Allocatie rfkill. | 
 | 1031 | 	 */ | 
 | 1032 | 	retval = rt2x00rfkill_allocate(rt2x00dev); | 
 | 1033 | 	if (retval) | 
 | 1034 | 		goto exit; | 
 | 1035 |  | 
 | 1036 | 	/* | 
 | 1037 | 	 * Open the debugfs entry. | 
 | 1038 | 	 */ | 
 | 1039 | 	rt2x00debug_register(rt2x00dev); | 
 | 1040 |  | 
| Ivo van Doorn | 066cb63 | 2007-09-25 20:55:39 +0200 | [diff] [blame] | 1041 | 	__set_bit(DEVICE_PRESENT, &rt2x00dev->flags); | 
 | 1042 |  | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 1043 | 	return 0; | 
 | 1044 |  | 
 | 1045 | exit: | 
 | 1046 | 	rt2x00lib_remove_dev(rt2x00dev); | 
 | 1047 |  | 
 | 1048 | 	return retval; | 
 | 1049 | } | 
 | 1050 | EXPORT_SYMBOL_GPL(rt2x00lib_probe_dev); | 
 | 1051 |  | 
 | 1052 | void rt2x00lib_remove_dev(struct rt2x00_dev *rt2x00dev) | 
 | 1053 | { | 
| Ivo van Doorn | 066cb63 | 2007-09-25 20:55:39 +0200 | [diff] [blame] | 1054 | 	__clear_bit(DEVICE_PRESENT, &rt2x00dev->flags); | 
 | 1055 |  | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 1056 | 	/* | 
 | 1057 | 	 * Disable radio. | 
 | 1058 | 	 */ | 
 | 1059 | 	rt2x00lib_disable_radio(rt2x00dev); | 
 | 1060 |  | 
 | 1061 | 	/* | 
 | 1062 | 	 * Uninitialize device. | 
 | 1063 | 	 */ | 
 | 1064 | 	rt2x00lib_uninitialize(rt2x00dev); | 
 | 1065 |  | 
 | 1066 | 	/* | 
 | 1067 | 	 * Close debugfs entry. | 
 | 1068 | 	 */ | 
 | 1069 | 	rt2x00debug_deregister(rt2x00dev); | 
 | 1070 |  | 
 | 1071 | 	/* | 
 | 1072 | 	 * Free rfkill | 
 | 1073 | 	 */ | 
 | 1074 | 	rt2x00rfkill_free(rt2x00dev); | 
 | 1075 |  | 
 | 1076 | 	/* | 
 | 1077 | 	 * Free ieee80211_hw memory. | 
 | 1078 | 	 */ | 
 | 1079 | 	rt2x00lib_remove_hw(rt2x00dev); | 
 | 1080 |  | 
 | 1081 | 	/* | 
 | 1082 | 	 * Free firmware image. | 
 | 1083 | 	 */ | 
 | 1084 | 	rt2x00lib_free_firmware(rt2x00dev); | 
 | 1085 |  | 
 | 1086 | 	/* | 
 | 1087 | 	 * Free ring structures. | 
 | 1088 | 	 */ | 
 | 1089 | 	rt2x00lib_free_rings(rt2x00dev); | 
 | 1090 | } | 
 | 1091 | EXPORT_SYMBOL_GPL(rt2x00lib_remove_dev); | 
 | 1092 |  | 
 | 1093 | /* | 
 | 1094 |  * Device state handlers | 
 | 1095 |  */ | 
 | 1096 | #ifdef CONFIG_PM | 
 | 1097 | int rt2x00lib_suspend(struct rt2x00_dev *rt2x00dev, pm_message_t state) | 
 | 1098 | { | 
 | 1099 | 	int retval; | 
 | 1100 |  | 
 | 1101 | 	NOTICE(rt2x00dev, "Going to sleep.\n"); | 
| Ivo van Doorn | 066cb63 | 2007-09-25 20:55:39 +0200 | [diff] [blame] | 1102 | 	__clear_bit(DEVICE_PRESENT, &rt2x00dev->flags); | 
 | 1103 |  | 
 | 1104 | 	/* | 
 | 1105 | 	 * Only continue if mac80211 has open interfaces. | 
 | 1106 | 	 */ | 
 | 1107 | 	if (!test_bit(DEVICE_STARTED, &rt2x00dev->flags)) | 
 | 1108 | 		goto exit; | 
| Ivo van Doorn | 6d7f987 | 2007-10-06 14:12:42 +0200 | [diff] [blame] | 1109 | 	__set_bit(DEVICE_STARTED_SUSPEND, &rt2x00dev->flags); | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 1110 |  | 
 | 1111 | 	/* | 
 | 1112 | 	 * Disable radio and unitialize all items | 
 | 1113 | 	 * that must be recreated on resume. | 
 | 1114 | 	 */ | 
| Ivo van Doorn | 6d7f987 | 2007-10-06 14:12:42 +0200 | [diff] [blame] | 1115 | 	rt2x00mac_stop(rt2x00dev->hw); | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 1116 | 	rt2x00lib_uninitialize(rt2x00dev); | 
 | 1117 | 	rt2x00debug_deregister(rt2x00dev); | 
 | 1118 |  | 
| Ivo van Doorn | 066cb63 | 2007-09-25 20:55:39 +0200 | [diff] [blame] | 1119 | exit: | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 1120 | 	/* | 
 | 1121 | 	 * Set device mode to sleep for power management. | 
 | 1122 | 	 */ | 
 | 1123 | 	retval = rt2x00dev->ops->lib->set_device_state(rt2x00dev, STATE_SLEEP); | 
 | 1124 | 	if (retval) | 
 | 1125 | 		return retval; | 
 | 1126 |  | 
 | 1127 | 	return 0; | 
 | 1128 | } | 
 | 1129 | EXPORT_SYMBOL_GPL(rt2x00lib_suspend); | 
 | 1130 |  | 
 | 1131 | int rt2x00lib_resume(struct rt2x00_dev *rt2x00dev) | 
 | 1132 | { | 
 | 1133 | 	struct interface *intf = &rt2x00dev->interface; | 
 | 1134 | 	int retval; | 
 | 1135 |  | 
 | 1136 | 	NOTICE(rt2x00dev, "Waking up.\n"); | 
| Ivo van Doorn | 066cb63 | 2007-09-25 20:55:39 +0200 | [diff] [blame] | 1137 | 	__set_bit(DEVICE_PRESENT, &rt2x00dev->flags); | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 1138 |  | 
 | 1139 | 	/* | 
 | 1140 | 	 * Open the debugfs entry. | 
 | 1141 | 	 */ | 
 | 1142 | 	rt2x00debug_register(rt2x00dev); | 
 | 1143 |  | 
 | 1144 | 	/* | 
| Ivo van Doorn | 6d7f987 | 2007-10-06 14:12:42 +0200 | [diff] [blame] | 1145 | 	 * Only continue if mac80211 had open interfaces. | 
| Ivo van Doorn | 066cb63 | 2007-09-25 20:55:39 +0200 | [diff] [blame] | 1146 | 	 */ | 
| Ivo van Doorn | 6d7f987 | 2007-10-06 14:12:42 +0200 | [diff] [blame] | 1147 | 	if (!__test_and_clear_bit(DEVICE_STARTED_SUSPEND, &rt2x00dev->flags)) | 
| Ivo van Doorn | 066cb63 | 2007-09-25 20:55:39 +0200 | [diff] [blame] | 1148 | 		return 0; | 
 | 1149 |  | 
 | 1150 | 	/* | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 1151 | 	 * Reinitialize device and all active interfaces. | 
 | 1152 | 	 */ | 
 | 1153 | 	retval = rt2x00mac_start(rt2x00dev->hw); | 
 | 1154 | 	if (retval) | 
 | 1155 | 		goto exit; | 
 | 1156 |  | 
 | 1157 | 	/* | 
 | 1158 | 	 * Reconfigure device. | 
 | 1159 | 	 */ | 
| Ivo van Doorn | 066cb63 | 2007-09-25 20:55:39 +0200 | [diff] [blame] | 1160 | 	rt2x00lib_config(rt2x00dev, &rt2x00dev->hw->conf, 1); | 
 | 1161 | 	if (!rt2x00dev->hw->conf.radio_enabled) | 
 | 1162 | 		rt2x00lib_disable_radio(rt2x00dev); | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 1163 |  | 
 | 1164 | 	rt2x00lib_config_mac_addr(rt2x00dev, intf->mac); | 
 | 1165 | 	rt2x00lib_config_bssid(rt2x00dev, intf->bssid); | 
 | 1166 | 	rt2x00lib_config_type(rt2x00dev, intf->type); | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 1167 |  | 
 | 1168 | 	/* | 
| Ivo van Doorn | 066cb63 | 2007-09-25 20:55:39 +0200 | [diff] [blame] | 1169 | 	 * It is possible that during that mac80211 has attempted | 
 | 1170 | 	 * to send frames while we were suspending or resuming. | 
 | 1171 | 	 * In that case we have disabled the TX queue and should | 
 | 1172 | 	 * now enable it again | 
 | 1173 | 	 */ | 
 | 1174 | 	ieee80211_start_queues(rt2x00dev->hw); | 
 | 1175 |  | 
 | 1176 | 	/* | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 1177 | 	 * When in Master or Ad-hoc mode, | 
 | 1178 | 	 * restart Beacon transmitting by faking a beacondone event. | 
 | 1179 | 	 */ | 
 | 1180 | 	if (intf->type == IEEE80211_IF_TYPE_AP || | 
 | 1181 | 	    intf->type == IEEE80211_IF_TYPE_IBSS) | 
 | 1182 | 		rt2x00lib_beacondone(rt2x00dev); | 
 | 1183 |  | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 1184 | 	return 0; | 
 | 1185 |  | 
 | 1186 | exit: | 
 | 1187 | 	rt2x00lib_disable_radio(rt2x00dev); | 
 | 1188 | 	rt2x00lib_uninitialize(rt2x00dev); | 
 | 1189 | 	rt2x00debug_deregister(rt2x00dev); | 
 | 1190 |  | 
| Ivo van Doorn | 95ea362 | 2007-09-25 17:57:13 -0700 | [diff] [blame] | 1191 | 	return retval; | 
 | 1192 | } | 
 | 1193 | EXPORT_SYMBOL_GPL(rt2x00lib_resume); | 
 | 1194 | #endif /* CONFIG_PM */ | 
 | 1195 |  | 
 | 1196 | /* | 
 | 1197 |  * rt2x00lib module information. | 
 | 1198 |  */ | 
 | 1199 | MODULE_AUTHOR(DRV_PROJECT); | 
 | 1200 | MODULE_VERSION(DRV_VERSION); | 
 | 1201 | MODULE_DESCRIPTION("rt2x00 library"); | 
 | 1202 | MODULE_LICENSE("GPL"); |