diff options
author | Ivo van Doorn <IvDoorn@gmail.com> | 2007-09-25 20:57:13 -0400 |
---|---|---|
committer | David S. Miller <davem@sunset.davemloft.net> | 2007-10-10 19:51:39 -0400 |
commit | 95ea36275f3c9a1d3d04c217b4b576c657c4e70e (patch) | |
tree | 55477b946a46aa871a087857a1dc698d74fe79d2 /drivers/net/wireless/rt2x00/rt2x00pci.c | |
parent | b481de9ca074528fe8c429604e2777db8b89806a (diff) |
[RT2x00]: add driver for Ralink wireless hardware
Signed-off-by: Ivo van Doorn <IvDoorn@gmail.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/wireless/rt2x00/rt2x00pci.c')
-rw-r--r-- | drivers/net/wireless/rt2x00/rt2x00pci.c | 481 |
1 files changed, 481 insertions, 0 deletions
diff --git a/drivers/net/wireless/rt2x00/rt2x00pci.c b/drivers/net/wireless/rt2x00/rt2x00pci.c new file mode 100644 index 000000000000..85629f1999ab --- /dev/null +++ b/drivers/net/wireless/rt2x00/rt2x00pci.c | |||
@@ -0,0 +1,481 @@ | |||
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: rt2x00pci | ||
23 | Abstract: rt2x00 generic pci device routines. | ||
24 | */ | ||
25 | |||
26 | /* | ||
27 | * Set enviroment defines for rt2x00.h | ||
28 | */ | ||
29 | #define DRV_NAME "rt2x00pci" | ||
30 | |||
31 | #include <linux/dma-mapping.h> | ||
32 | #include <linux/kernel.h> | ||
33 | #include <linux/module.h> | ||
34 | #include <linux/pci.h> | ||
35 | |||
36 | #include "rt2x00.h" | ||
37 | #include "rt2x00pci.h" | ||
38 | |||
39 | /* | ||
40 | * Beacon handlers. | ||
41 | */ | ||
42 | int rt2x00pci_beacon_update(struct ieee80211_hw *hw, struct sk_buff *skb, | ||
43 | struct ieee80211_tx_control *control) | ||
44 | { | ||
45 | struct rt2x00_dev *rt2x00dev = hw->priv; | ||
46 | struct data_ring *ring = | ||
47 | rt2x00lib_get_ring(rt2x00dev, IEEE80211_TX_QUEUE_BEACON); | ||
48 | struct data_entry *entry = rt2x00_get_data_entry(ring); | ||
49 | |||
50 | /* | ||
51 | * Just in case mac80211 doesn't set this correctly, | ||
52 | * but we need this queue set for the descriptor | ||
53 | * initialization. | ||
54 | */ | ||
55 | control->queue = IEEE80211_TX_QUEUE_BEACON; | ||
56 | |||
57 | /* | ||
58 | * Update the beacon entry. | ||
59 | */ | ||
60 | memcpy(entry->data_addr, skb->data, skb->len); | ||
61 | rt2x00lib_write_tx_desc(rt2x00dev, entry->priv, | ||
62 | (struct ieee80211_hdr *)skb->data, | ||
63 | skb->len, control); | ||
64 | |||
65 | /* | ||
66 | * Enable beacon generation. | ||
67 | */ | ||
68 | rt2x00dev->ops->lib->kick_tx_queue(rt2x00dev, control->queue); | ||
69 | |||
70 | return 0; | ||
71 | } | ||
72 | EXPORT_SYMBOL_GPL(rt2x00pci_beacon_update); | ||
73 | |||
74 | /* | ||
75 | * TX data handlers. | ||
76 | */ | ||
77 | int rt2x00pci_write_tx_data(struct rt2x00_dev *rt2x00dev, | ||
78 | struct data_ring *ring, struct sk_buff *skb, | ||
79 | struct ieee80211_tx_control *control) | ||
80 | { | ||
81 | struct ieee80211_hdr *ieee80211hdr = (struct ieee80211_hdr *)skb->data; | ||
82 | struct data_entry *entry = rt2x00_get_data_entry(ring); | ||
83 | struct data_desc *txd = entry->priv; | ||
84 | u32 word; | ||
85 | |||
86 | if (rt2x00_ring_full(ring)) { | ||
87 | ieee80211_stop_queue(rt2x00dev->hw, control->queue); | ||
88 | return -EINVAL; | ||
89 | } | ||
90 | |||
91 | rt2x00_desc_read(txd, 0, &word); | ||
92 | |||
93 | if (rt2x00_get_field32(word, TXD_ENTRY_OWNER_NIC) || | ||
94 | rt2x00_get_field32(word, TXD_ENTRY_VALID)) { | ||
95 | ERROR(rt2x00dev, | ||
96 | "Arrived at non-free entry in the non-full queue %d.\n" | ||
97 | "Please file bug report to %s.\n", | ||
98 | control->queue, DRV_PROJECT); | ||
99 | ieee80211_stop_queue(rt2x00dev->hw, control->queue); | ||
100 | return -EINVAL; | ||
101 | } | ||
102 | |||
103 | entry->skb = skb; | ||
104 | memcpy(&entry->tx_status.control, control, sizeof(*control)); | ||
105 | memcpy(entry->data_addr, skb->data, skb->len); | ||
106 | rt2x00lib_write_tx_desc(rt2x00dev, txd, ieee80211hdr, | ||
107 | skb->len, control); | ||
108 | |||
109 | rt2x00_ring_index_inc(ring); | ||
110 | |||
111 | if (rt2x00_ring_full(ring)) | ||
112 | ieee80211_stop_queue(rt2x00dev->hw, control->queue); | ||
113 | |||
114 | return 0; | ||
115 | } | ||
116 | EXPORT_SYMBOL_GPL(rt2x00pci_write_tx_data); | ||
117 | |||
118 | /* | ||
119 | * RX data handlers. | ||
120 | */ | ||
121 | void rt2x00pci_rxdone(struct rt2x00_dev *rt2x00dev) | ||
122 | { | ||
123 | struct data_ring *ring = rt2x00dev->rx; | ||
124 | struct data_entry *entry; | ||
125 | struct data_desc *rxd; | ||
126 | struct sk_buff *skb; | ||
127 | u32 desc; | ||
128 | int retval; | ||
129 | int signal; | ||
130 | int rssi; | ||
131 | int ofdm; | ||
132 | int size; | ||
133 | |||
134 | while (1) { | ||
135 | entry = rt2x00_get_data_entry(ring); | ||
136 | rxd = entry->priv; | ||
137 | rt2x00_desc_read(rxd, 0, &desc); | ||
138 | |||
139 | if (rt2x00_get_field32(desc, RXD_ENTRY_OWNER_NIC)) | ||
140 | break; | ||
141 | |||
142 | retval = rt2x00dev->ops->lib->fill_rxdone(entry, &signal, | ||
143 | &rssi, &ofdm, &size); | ||
144 | if (retval) | ||
145 | goto skip_entry; | ||
146 | |||
147 | /* | ||
148 | * Allocate the sk_buffer, initialize it and copy | ||
149 | * all data into it. | ||
150 | */ | ||
151 | skb = dev_alloc_skb(size + NET_IP_ALIGN); | ||
152 | if (!skb) | ||
153 | return; | ||
154 | |||
155 | skb_reserve(skb, NET_IP_ALIGN); | ||
156 | skb_put(skb, size); | ||
157 | memcpy(skb->data, entry->data_addr, size); | ||
158 | |||
159 | /* | ||
160 | * Send the frame to rt2x00lib for further processing. | ||
161 | */ | ||
162 | rt2x00lib_rxdone(entry, skb, signal, rssi, ofdm); | ||
163 | |||
164 | skip_entry: | ||
165 | if (test_bit(DEVICE_ENABLED_RADIO, &ring->rt2x00dev->flags)) { | ||
166 | rt2x00_set_field32(&desc, RXD_ENTRY_OWNER_NIC, 1); | ||
167 | rt2x00_desc_write(rxd, 0, desc); | ||
168 | } | ||
169 | |||
170 | rt2x00_ring_index_inc(ring); | ||
171 | } | ||
172 | } | ||
173 | EXPORT_SYMBOL_GPL(rt2x00pci_rxdone); | ||
174 | |||
175 | /* | ||
176 | * Device initialization handlers. | ||
177 | */ | ||
178 | #define priv_offset(__ring, __i) \ | ||
179 | ({ \ | ||
180 | ring->data_addr + (i * ring->desc_size); \ | ||
181 | }) | ||
182 | |||
183 | #define data_addr_offset(__ring, __i) \ | ||
184 | ({ \ | ||
185 | (__ring)->data_addr + \ | ||
186 | ((__ring)->stats.limit * (__ring)->desc_size) + \ | ||
187 | ((__i) * (__ring)->data_size); \ | ||
188 | }) | ||
189 | |||
190 | #define data_dma_offset(__ring, __i) \ | ||
191 | ({ \ | ||
192 | (__ring)->data_dma + \ | ||
193 | ((__ring)->stats.limit * (__ring)->desc_size) + \ | ||
194 | ((__i) * (__ring)->data_size); \ | ||
195 | }) | ||
196 | |||
197 | static int rt2x00pci_alloc_dma(struct rt2x00_dev *rt2x00dev, | ||
198 | struct data_ring *ring) | ||
199 | { | ||
200 | unsigned int i; | ||
201 | |||
202 | /* | ||
203 | * Allocate DMA memory for descriptor and buffer. | ||
204 | */ | ||
205 | ring->data_addr = pci_alloc_consistent(rt2x00dev_pci(rt2x00dev), | ||
206 | rt2x00_get_ring_size(ring), | ||
207 | &ring->data_dma); | ||
208 | if (!ring->data_addr) | ||
209 | return -ENOMEM; | ||
210 | |||
211 | /* | ||
212 | * Initialize all ring entries to contain valid | ||
213 | * addresses. | ||
214 | */ | ||
215 | for (i = 0; i < ring->stats.limit; i++) { | ||
216 | ring->entry[i].priv = priv_offset(ring, i); | ||
217 | ring->entry[i].data_addr = data_addr_offset(ring, i); | ||
218 | ring->entry[i].data_dma = data_dma_offset(ring, i); | ||
219 | } | ||
220 | |||
221 | return 0; | ||
222 | } | ||
223 | |||
224 | static void rt2x00pci_free_dma(struct rt2x00_dev *rt2x00dev, | ||
225 | struct data_ring *ring) | ||
226 | { | ||
227 | if (ring->data_addr) | ||
228 | pci_free_consistent(rt2x00dev_pci(rt2x00dev), | ||
229 | rt2x00_get_ring_size(ring), | ||
230 | ring->data_addr, ring->data_dma); | ||
231 | ring->data_addr = NULL; | ||
232 | } | ||
233 | |||
234 | int rt2x00pci_initialize(struct rt2x00_dev *rt2x00dev) | ||
235 | { | ||
236 | struct pci_dev *pci_dev = rt2x00dev_pci(rt2x00dev); | ||
237 | struct data_ring *ring; | ||
238 | int status; | ||
239 | |||
240 | /* | ||
241 | * Allocate DMA | ||
242 | */ | ||
243 | ring_for_each(rt2x00dev, ring) { | ||
244 | status = rt2x00pci_alloc_dma(rt2x00dev, ring); | ||
245 | if (status) | ||
246 | goto exit; | ||
247 | } | ||
248 | |||
249 | /* | ||
250 | * Register interrupt handler. | ||
251 | */ | ||
252 | status = request_irq(pci_dev->irq, rt2x00dev->ops->lib->irq_handler, | ||
253 | IRQF_SHARED, pci_name(pci_dev), rt2x00dev); | ||
254 | if (status) { | ||
255 | ERROR(rt2x00dev, "IRQ %d allocation failed (error %d).\n", | ||
256 | pci_dev->irq, status); | ||
257 | return status; | ||
258 | } | ||
259 | |||
260 | return 0; | ||
261 | |||
262 | exit: | ||
263 | rt2x00pci_uninitialize(rt2x00dev); | ||
264 | |||
265 | return status; | ||
266 | } | ||
267 | EXPORT_SYMBOL_GPL(rt2x00pci_initialize); | ||
268 | |||
269 | void rt2x00pci_uninitialize(struct rt2x00_dev *rt2x00dev) | ||
270 | { | ||
271 | struct data_ring *ring; | ||
272 | |||
273 | /* | ||
274 | * Free irq line. | ||
275 | */ | ||
276 | free_irq(rt2x00dev_pci(rt2x00dev)->irq, rt2x00dev); | ||
277 | |||
278 | /* | ||
279 | * Free DMA | ||
280 | */ | ||
281 | ring_for_each(rt2x00dev, ring) | ||
282 | rt2x00pci_free_dma(rt2x00dev, ring); | ||
283 | } | ||
284 | EXPORT_SYMBOL_GPL(rt2x00pci_uninitialize); | ||
285 | |||
286 | /* | ||
287 | * PCI driver handlers. | ||
288 | */ | ||
289 | static void rt2x00pci_free_reg(struct rt2x00_dev *rt2x00dev) | ||
290 | { | ||
291 | kfree(rt2x00dev->rf); | ||
292 | rt2x00dev->rf = NULL; | ||
293 | |||
294 | kfree(rt2x00dev->eeprom); | ||
295 | rt2x00dev->eeprom = NULL; | ||
296 | |||
297 | if (rt2x00dev->csr_addr) { | ||
298 | iounmap(rt2x00dev->csr_addr); | ||
299 | rt2x00dev->csr_addr = NULL; | ||
300 | } | ||
301 | } | ||
302 | |||
303 | static int rt2x00pci_alloc_reg(struct rt2x00_dev *rt2x00dev) | ||
304 | { | ||
305 | struct pci_dev *pci_dev = rt2x00dev_pci(rt2x00dev); | ||
306 | |||
307 | rt2x00dev->csr_addr = ioremap(pci_resource_start(pci_dev, 0), | ||
308 | pci_resource_len(pci_dev, 0)); | ||
309 | if (!rt2x00dev->csr_addr) | ||
310 | goto exit; | ||
311 | |||
312 | rt2x00dev->eeprom = kzalloc(rt2x00dev->ops->eeprom_size, GFP_KERNEL); | ||
313 | if (!rt2x00dev->eeprom) | ||
314 | goto exit; | ||
315 | |||
316 | rt2x00dev->rf = kzalloc(rt2x00dev->ops->rf_size, GFP_KERNEL); | ||
317 | if (!rt2x00dev->rf) | ||
318 | goto exit; | ||
319 | |||
320 | return 0; | ||
321 | |||
322 | exit: | ||
323 | ERROR_PROBE("Failed to allocate registers.\n"); | ||
324 | |||
325 | rt2x00pci_free_reg(rt2x00dev); | ||
326 | |||
327 | return -ENOMEM; | ||
328 | } | ||
329 | |||
330 | int rt2x00pci_probe(struct pci_dev *pci_dev, const struct pci_device_id *id) | ||
331 | { | ||
332 | struct rt2x00_ops *ops = (struct rt2x00_ops *)id->driver_data; | ||
333 | struct ieee80211_hw *hw; | ||
334 | struct rt2x00_dev *rt2x00dev; | ||
335 | int retval; | ||
336 | |||
337 | retval = pci_request_regions(pci_dev, pci_name(pci_dev)); | ||
338 | if (retval) { | ||
339 | ERROR_PROBE("PCI request regions failed.\n"); | ||
340 | return retval; | ||
341 | } | ||
342 | |||
343 | retval = pci_enable_device(pci_dev); | ||
344 | if (retval) { | ||
345 | ERROR_PROBE("Enable device failed.\n"); | ||
346 | goto exit_release_regions; | ||
347 | } | ||
348 | |||
349 | pci_set_master(pci_dev); | ||
350 | |||
351 | if (pci_set_mwi(pci_dev)) | ||
352 | ERROR_PROBE("MWI not available.\n"); | ||
353 | |||
354 | if (pci_set_dma_mask(pci_dev, DMA_64BIT_MASK) && | ||
355 | pci_set_dma_mask(pci_dev, DMA_32BIT_MASK)) { | ||
356 | ERROR_PROBE("PCI DMA not supported.\n"); | ||
357 | retval = -EIO; | ||
358 | goto exit_disable_device; | ||
359 | } | ||
360 | |||
361 | hw = ieee80211_alloc_hw(sizeof(struct rt2x00_dev), ops->hw); | ||
362 | if (!hw) { | ||
363 | ERROR_PROBE("Failed to allocate hardware.\n"); | ||
364 | retval = -ENOMEM; | ||
365 | goto exit_disable_device; | ||
366 | } | ||
367 | |||
368 | pci_set_drvdata(pci_dev, hw); | ||
369 | |||
370 | rt2x00dev = hw->priv; | ||
371 | rt2x00dev->dev = pci_dev; | ||
372 | rt2x00dev->ops = ops; | ||
373 | rt2x00dev->hw = hw; | ||
374 | |||
375 | retval = rt2x00pci_alloc_reg(rt2x00dev); | ||
376 | if (retval) | ||
377 | goto exit_free_device; | ||
378 | |||
379 | retval = rt2x00lib_probe_dev(rt2x00dev); | ||
380 | if (retval) | ||
381 | goto exit_free_reg; | ||
382 | |||
383 | return 0; | ||
384 | |||
385 | exit_free_reg: | ||
386 | rt2x00pci_free_reg(rt2x00dev); | ||
387 | |||
388 | exit_free_device: | ||
389 | ieee80211_free_hw(hw); | ||
390 | |||
391 | exit_disable_device: | ||
392 | if (retval != -EBUSY) | ||
393 | pci_disable_device(pci_dev); | ||
394 | |||
395 | exit_release_regions: | ||
396 | pci_release_regions(pci_dev); | ||
397 | |||
398 | pci_set_drvdata(pci_dev, NULL); | ||
399 | |||
400 | return retval; | ||
401 | } | ||
402 | EXPORT_SYMBOL_GPL(rt2x00pci_probe); | ||
403 | |||
404 | void rt2x00pci_remove(struct pci_dev *pci_dev) | ||
405 | { | ||
406 | struct ieee80211_hw *hw = pci_get_drvdata(pci_dev); | ||
407 | struct rt2x00_dev *rt2x00dev = hw->priv; | ||
408 | |||
409 | /* | ||
410 | * Free all allocated data. | ||
411 | */ | ||
412 | rt2x00lib_remove_dev(rt2x00dev); | ||
413 | rt2x00pci_free_reg(rt2x00dev); | ||
414 | ieee80211_free_hw(hw); | ||
415 | |||
416 | /* | ||
417 | * Free the PCI device data. | ||
418 | */ | ||
419 | pci_set_drvdata(pci_dev, NULL); | ||
420 | pci_disable_device(pci_dev); | ||
421 | pci_release_regions(pci_dev); | ||
422 | } | ||
423 | EXPORT_SYMBOL_GPL(rt2x00pci_remove); | ||
424 | |||
425 | #ifdef CONFIG_PM | ||
426 | int rt2x00pci_suspend(struct pci_dev *pci_dev, pm_message_t state) | ||
427 | { | ||
428 | struct ieee80211_hw *hw = pci_get_drvdata(pci_dev); | ||
429 | struct rt2x00_dev *rt2x00dev = hw->priv; | ||
430 | int retval; | ||
431 | |||
432 | retval = rt2x00lib_suspend(rt2x00dev, state); | ||
433 | if (retval) | ||
434 | return retval; | ||
435 | |||
436 | rt2x00pci_free_reg(rt2x00dev); | ||
437 | |||
438 | pci_save_state(pci_dev); | ||
439 | pci_disable_device(pci_dev); | ||
440 | return pci_set_power_state(pci_dev, pci_choose_state(pci_dev, state)); | ||
441 | } | ||
442 | EXPORT_SYMBOL_GPL(rt2x00pci_suspend); | ||
443 | |||
444 | int rt2x00pci_resume(struct pci_dev *pci_dev) | ||
445 | { | ||
446 | struct ieee80211_hw *hw = pci_get_drvdata(pci_dev); | ||
447 | struct rt2x00_dev *rt2x00dev = hw->priv; | ||
448 | int retval; | ||
449 | |||
450 | if (pci_set_power_state(pci_dev, PCI_D0) || | ||
451 | pci_enable_device(pci_dev) || | ||
452 | pci_restore_state(pci_dev)) { | ||
453 | ERROR(rt2x00dev, "Failed to resume device.\n"); | ||
454 | return -EIO; | ||
455 | } | ||
456 | |||
457 | retval = rt2x00pci_alloc_reg(rt2x00dev); | ||
458 | if (retval) | ||
459 | return retval; | ||
460 | |||
461 | retval = rt2x00lib_resume(rt2x00dev); | ||
462 | if (retval) | ||
463 | goto exit_free_reg; | ||
464 | |||
465 | return 0; | ||
466 | |||
467 | exit_free_reg: | ||
468 | rt2x00pci_free_reg(rt2x00dev); | ||
469 | |||
470 | return retval; | ||
471 | } | ||
472 | EXPORT_SYMBOL_GPL(rt2x00pci_resume); | ||
473 | #endif /* CONFIG_PM */ | ||
474 | |||
475 | /* | ||
476 | * rt2x00pci module information. | ||
477 | */ | ||
478 | MODULE_AUTHOR(DRV_PROJECT); | ||
479 | MODULE_VERSION(DRV_VERSION); | ||
480 | MODULE_DESCRIPTION("rt2x00 library"); | ||
481 | MODULE_LICENSE("GPL"); | ||