diff options
author | Jouni Malinen <jkmaline@cc.hut.fi> | 2005-05-12 22:54:16 -0400 |
---|---|---|
committer | Jeff Garzik <jgarzik@pobox.com> | 2005-05-12 22:54:16 -0400 |
commit | ff1d2767d5a43c85f944e86a45284b721f66196c (patch) | |
tree | 91c1b6dd20bd772bc112c0012830678b46b69604 /drivers/net/wireless/hostap/hostap_pci.c | |
parent | 88d7bd8cb9eb8d64bf7997600b0d64f7834047c5 (diff) |
Add HostAP wireless driver.
Includes minor cleanups from Adrian Bunk <bunk@stusta.de>.
Diffstat (limited to 'drivers/net/wireless/hostap/hostap_pci.c')
-rw-r--r-- | drivers/net/wireless/hostap/hostap_pci.c | 453 |
1 files changed, 453 insertions, 0 deletions
diff --git a/drivers/net/wireless/hostap/hostap_pci.c b/drivers/net/wireless/hostap/hostap_pci.c new file mode 100644 index 000000000000..3a208989333e --- /dev/null +++ b/drivers/net/wireless/hostap/hostap_pci.c | |||
@@ -0,0 +1,453 @@ | |||
1 | #define PRISM2_PCI | ||
2 | |||
3 | /* Host AP driver's support for Intersil Prism2.5 PCI cards is based on | ||
4 | * driver patches from Reyk Floeter <reyk@vantronix.net> and | ||
5 | * Andy Warner <andyw@pobox.com> */ | ||
6 | |||
7 | #include <linux/config.h> | ||
8 | #include <linux/version.h> | ||
9 | #include <linux/module.h> | ||
10 | #include <linux/init.h> | ||
11 | #include <linux/if.h> | ||
12 | #include <linux/skbuff.h> | ||
13 | #include <linux/netdevice.h> | ||
14 | #include <linux/workqueue.h> | ||
15 | #include <linux/wireless.h> | ||
16 | #include <net/iw_handler.h> | ||
17 | |||
18 | #include <linux/ioport.h> | ||
19 | #include <linux/pci.h> | ||
20 | #include <asm/io.h> | ||
21 | |||
22 | #include "hostap_wlan.h" | ||
23 | |||
24 | |||
25 | static char *version = PRISM2_VERSION " (Jouni Malinen <jkmaline@cc.hut.fi>)"; | ||
26 | static char *dev_info = "hostap_pci"; | ||
27 | |||
28 | |||
29 | MODULE_AUTHOR("Jouni Malinen"); | ||
30 | MODULE_DESCRIPTION("Support for Intersil Prism2.5-based 802.11 wireless LAN " | ||
31 | "PCI cards."); | ||
32 | MODULE_SUPPORTED_DEVICE("Intersil Prism2.5-based WLAN PCI cards"); | ||
33 | MODULE_LICENSE("GPL"); | ||
34 | |||
35 | |||
36 | /* FIX: do we need mb/wmb/rmb with memory operations? */ | ||
37 | |||
38 | |||
39 | static struct pci_device_id prism2_pci_id_table[] __devinitdata = { | ||
40 | /* Intersil Prism3 ISL3872 11Mb/s WLAN Controller */ | ||
41 | { 0x1260, 0x3872, PCI_ANY_ID, PCI_ANY_ID }, | ||
42 | /* Intersil Prism2.5 ISL3874 11Mb/s WLAN Controller */ | ||
43 | { 0x1260, 0x3873, PCI_ANY_ID, PCI_ANY_ID }, | ||
44 | /* Samsung MagicLAN SWL-2210P */ | ||
45 | { 0x167d, 0xa000, PCI_ANY_ID, PCI_ANY_ID }, | ||
46 | { 0 } | ||
47 | }; | ||
48 | |||
49 | |||
50 | #ifdef PRISM2_IO_DEBUG | ||
51 | |||
52 | static inline void hfa384x_outb_debug(struct net_device *dev, int a, u8 v) | ||
53 | { | ||
54 | struct hostap_interface *iface; | ||
55 | local_info_t *local; | ||
56 | unsigned long flags; | ||
57 | |||
58 | iface = netdev_priv(dev); | ||
59 | local = iface->local; | ||
60 | |||
61 | spin_lock_irqsave(&local->lock, flags); | ||
62 | prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTB, a, v); | ||
63 | writeb(v, local->mem_start + a); | ||
64 | spin_unlock_irqrestore(&local->lock, flags); | ||
65 | } | ||
66 | |||
67 | static inline u8 hfa384x_inb_debug(struct net_device *dev, int a) | ||
68 | { | ||
69 | struct hostap_interface *iface; | ||
70 | local_info_t *local; | ||
71 | unsigned long flags; | ||
72 | u8 v; | ||
73 | |||
74 | iface = netdev_priv(dev); | ||
75 | local = iface->local; | ||
76 | |||
77 | spin_lock_irqsave(&local->lock, flags); | ||
78 | v = readb(local->mem_start + a); | ||
79 | prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INB, a, v); | ||
80 | spin_unlock_irqrestore(&local->lock, flags); | ||
81 | return v; | ||
82 | } | ||
83 | |||
84 | static inline void hfa384x_outw_debug(struct net_device *dev, int a, u16 v) | ||
85 | { | ||
86 | struct hostap_interface *iface; | ||
87 | local_info_t *local; | ||
88 | unsigned long flags; | ||
89 | |||
90 | iface = netdev_priv(dev); | ||
91 | local = iface->local; | ||
92 | |||
93 | spin_lock_irqsave(&local->lock, flags); | ||
94 | prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTW, a, v); | ||
95 | writew(v, local->mem_start + a); | ||
96 | spin_unlock_irqrestore(&local->lock, flags); | ||
97 | } | ||
98 | |||
99 | static inline u16 hfa384x_inw_debug(struct net_device *dev, int a) | ||
100 | { | ||
101 | struct hostap_interface *iface; | ||
102 | local_info_t *local; | ||
103 | unsigned long flags; | ||
104 | u16 v; | ||
105 | |||
106 | iface = netdev_priv(dev); | ||
107 | local = iface->local; | ||
108 | |||
109 | spin_lock_irqsave(&local->lock, flags); | ||
110 | v = readw(local->mem_start + a); | ||
111 | prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INW, a, v); | ||
112 | spin_unlock_irqrestore(&local->lock, flags); | ||
113 | return v; | ||
114 | } | ||
115 | |||
116 | #define HFA384X_OUTB(v,a) hfa384x_outb_debug(dev, (a), (v)) | ||
117 | #define HFA384X_INB(a) hfa384x_inb_debug(dev, (a)) | ||
118 | #define HFA384X_OUTW(v,a) hfa384x_outw_debug(dev, (a), (v)) | ||
119 | #define HFA384X_INW(a) hfa384x_inw_debug(dev, (a)) | ||
120 | #define HFA384X_OUTW_DATA(v,a) hfa384x_outw_debug(dev, (a), cpu_to_le16((v))) | ||
121 | #define HFA384X_INW_DATA(a) (u16) le16_to_cpu(hfa384x_inw_debug(dev, (a))) | ||
122 | |||
123 | #else /* PRISM2_IO_DEBUG */ | ||
124 | |||
125 | static inline void hfa384x_outb(struct net_device *dev, int a, u8 v) | ||
126 | { | ||
127 | struct hostap_interface *iface; | ||
128 | local_info_t *local; | ||
129 | iface = netdev_priv(dev); | ||
130 | local = iface->local; | ||
131 | writeb(v, local->mem_start + a); | ||
132 | } | ||
133 | |||
134 | static inline u8 hfa384x_inb(struct net_device *dev, int a) | ||
135 | { | ||
136 | struct hostap_interface *iface; | ||
137 | local_info_t *local; | ||
138 | iface = netdev_priv(dev); | ||
139 | local = iface->local; | ||
140 | return readb(local->mem_start + a); | ||
141 | } | ||
142 | |||
143 | static inline void hfa384x_outw(struct net_device *dev, int a, u16 v) | ||
144 | { | ||
145 | struct hostap_interface *iface; | ||
146 | local_info_t *local; | ||
147 | iface = netdev_priv(dev); | ||
148 | local = iface->local; | ||
149 | writew(v, local->mem_start + a); | ||
150 | } | ||
151 | |||
152 | static inline u16 hfa384x_inw(struct net_device *dev, int a) | ||
153 | { | ||
154 | struct hostap_interface *iface; | ||
155 | local_info_t *local; | ||
156 | iface = netdev_priv(dev); | ||
157 | local = iface->local; | ||
158 | return readw(local->mem_start + a); | ||
159 | } | ||
160 | |||
161 | #define HFA384X_OUTB(v,a) hfa384x_outb(dev, (a), (v)) | ||
162 | #define HFA384X_INB(a) hfa384x_inb(dev, (a)) | ||
163 | #define HFA384X_OUTW(v,a) hfa384x_outw(dev, (a), (v)) | ||
164 | #define HFA384X_INW(a) hfa384x_inw(dev, (a)) | ||
165 | #define HFA384X_OUTW_DATA(v,a) hfa384x_outw(dev, (a), cpu_to_le16((v))) | ||
166 | #define HFA384X_INW_DATA(a) (u16) le16_to_cpu(hfa384x_inw(dev, (a))) | ||
167 | |||
168 | #endif /* PRISM2_IO_DEBUG */ | ||
169 | |||
170 | |||
171 | static int hfa384x_from_bap(struct net_device *dev, u16 bap, void *buf, | ||
172 | int len) | ||
173 | { | ||
174 | u16 d_off; | ||
175 | u16 *pos; | ||
176 | |||
177 | d_off = (bap == 1) ? HFA384X_DATA1_OFF : HFA384X_DATA0_OFF; | ||
178 | pos = (u16 *) buf; | ||
179 | |||
180 | for ( ; len > 1; len -= 2) | ||
181 | *pos++ = HFA384X_INW_DATA(d_off); | ||
182 | |||
183 | if (len & 1) | ||
184 | *((char *) pos) = HFA384X_INB(d_off); | ||
185 | |||
186 | return 0; | ||
187 | } | ||
188 | |||
189 | |||
190 | static int hfa384x_to_bap(struct net_device *dev, u16 bap, void *buf, int len) | ||
191 | { | ||
192 | u16 d_off; | ||
193 | u16 *pos; | ||
194 | |||
195 | d_off = (bap == 1) ? HFA384X_DATA1_OFF : HFA384X_DATA0_OFF; | ||
196 | pos = (u16 *) buf; | ||
197 | |||
198 | for ( ; len > 1; len -= 2) | ||
199 | HFA384X_OUTW_DATA(*pos++, d_off); | ||
200 | |||
201 | if (len & 1) | ||
202 | HFA384X_OUTB(*((char *) pos), d_off); | ||
203 | |||
204 | return 0; | ||
205 | } | ||
206 | |||
207 | |||
208 | /* FIX: This might change at some point.. */ | ||
209 | #include "hostap_hw.c" | ||
210 | |||
211 | static void prism2_pci_cor_sreset(local_info_t *local) | ||
212 | { | ||
213 | struct net_device *dev = local->dev; | ||
214 | u16 reg; | ||
215 | |||
216 | reg = HFA384X_INB(HFA384X_PCICOR_OFF); | ||
217 | printk(KERN_DEBUG "%s: Original COR value: 0x%0x\n", dev->name, reg); | ||
218 | |||
219 | /* linux-wlan-ng uses extremely long hold and settle times for | ||
220 | * COR sreset. A comment in the driver code mentions that the long | ||
221 | * delays appear to be necessary. However, at least IBM 22P6901 seems | ||
222 | * to work fine with shorter delays. | ||
223 | * | ||
224 | * Longer delays can be configured by uncommenting following line: */ | ||
225 | /* #define PRISM2_PCI_USE_LONG_DELAYS */ | ||
226 | |||
227 | #ifdef PRISM2_PCI_USE_LONG_DELAYS | ||
228 | int i; | ||
229 | |||
230 | HFA384X_OUTW(reg | 0x0080, HFA384X_PCICOR_OFF); | ||
231 | mdelay(250); | ||
232 | |||
233 | HFA384X_OUTW(reg & ~0x0080, HFA384X_PCICOR_OFF); | ||
234 | mdelay(500); | ||
235 | |||
236 | /* Wait for f/w to complete initialization (CMD:BUSY == 0) */ | ||
237 | i = 2000000 / 10; | ||
238 | while ((HFA384X_INW(HFA384X_CMD_OFF) & HFA384X_CMD_BUSY) && --i) | ||
239 | udelay(10); | ||
240 | |||
241 | #else /* PRISM2_PCI_USE_LONG_DELAYS */ | ||
242 | |||
243 | HFA384X_OUTW(reg | 0x0080, HFA384X_PCICOR_OFF); | ||
244 | mdelay(2); | ||
245 | HFA384X_OUTW(reg & ~0x0080, HFA384X_PCICOR_OFF); | ||
246 | mdelay(2); | ||
247 | |||
248 | #endif /* PRISM2_PCI_USE_LONG_DELAYS */ | ||
249 | |||
250 | if (HFA384X_INW(HFA384X_CMD_OFF) & HFA384X_CMD_BUSY) { | ||
251 | printk(KERN_DEBUG "%s: COR sreset timeout\n", dev->name); | ||
252 | } | ||
253 | } | ||
254 | |||
255 | |||
256 | static void prism2_pci_genesis_reset(local_info_t *local, int hcr) | ||
257 | { | ||
258 | struct net_device *dev = local->dev; | ||
259 | |||
260 | HFA384X_OUTW(0x00C5, HFA384X_PCICOR_OFF); | ||
261 | mdelay(10); | ||
262 | HFA384X_OUTW(hcr, HFA384X_PCIHCR_OFF); | ||
263 | mdelay(10); | ||
264 | HFA384X_OUTW(0x0045, HFA384X_PCICOR_OFF); | ||
265 | mdelay(10); | ||
266 | } | ||
267 | |||
268 | |||
269 | static struct prism2_helper_functions prism2_pci_funcs = | ||
270 | { | ||
271 | .card_present = NULL, | ||
272 | .cor_sreset = prism2_pci_cor_sreset, | ||
273 | .dev_open = NULL, | ||
274 | .dev_close = NULL, | ||
275 | .genesis_reset = prism2_pci_genesis_reset, | ||
276 | .hw_type = HOSTAP_HW_PCI, | ||
277 | }; | ||
278 | |||
279 | |||
280 | static int prism2_pci_probe(struct pci_dev *pdev, | ||
281 | const struct pci_device_id *id) | ||
282 | { | ||
283 | unsigned long phymem; | ||
284 | void __iomem *mem = NULL; | ||
285 | local_info_t *local = NULL; | ||
286 | struct net_device *dev = NULL; | ||
287 | static int cards_found /* = 0 */; | ||
288 | int irq_registered = 0; | ||
289 | struct hostap_interface *iface; | ||
290 | |||
291 | if (pci_enable_device(pdev)) | ||
292 | return -EIO; | ||
293 | |||
294 | phymem = pci_resource_start(pdev, 0); | ||
295 | |||
296 | if (!request_mem_region(phymem, pci_resource_len(pdev, 0), "Prism2")) { | ||
297 | printk(KERN_ERR "prism2: Cannot reserve PCI memory region\n"); | ||
298 | goto err_out_disable; | ||
299 | } | ||
300 | |||
301 | mem = ioremap(phymem, pci_resource_len(pdev, 0)); | ||
302 | if (mem == NULL) { | ||
303 | printk(KERN_ERR "prism2: Cannot remap PCI memory region\n") ; | ||
304 | goto fail; | ||
305 | } | ||
306 | |||
307 | #ifdef PRISM2_BUS_MASTER | ||
308 | pci_set_master(pdev); | ||
309 | #endif /* PRISM2_BUS_MASTER */ | ||
310 | |||
311 | dev = prism2_init_local_data(&prism2_pci_funcs, cards_found); | ||
312 | if (dev == NULL) | ||
313 | goto fail; | ||
314 | iface = netdev_priv(dev); | ||
315 | local = iface->local; | ||
316 | cards_found++; | ||
317 | |||
318 | dev->irq = pdev->irq; | ||
319 | local->mem_start = mem; | ||
320 | |||
321 | prism2_pci_cor_sreset(local); | ||
322 | |||
323 | pci_set_drvdata(pdev, dev); | ||
324 | |||
325 | if (request_irq(dev->irq, prism2_interrupt, SA_SHIRQ, dev->name, | ||
326 | dev)) { | ||
327 | printk(KERN_WARNING "%s: request_irq failed\n", dev->name); | ||
328 | goto fail; | ||
329 | } else | ||
330 | irq_registered = 1; | ||
331 | |||
332 | if (!local->pri_only && prism2_hw_config(dev, 1)) { | ||
333 | printk(KERN_DEBUG "%s: hardware initialization failed\n", | ||
334 | dev_info); | ||
335 | goto fail; | ||
336 | } | ||
337 | |||
338 | printk(KERN_INFO "%s: Intersil Prism2.5 PCI: " | ||
339 | "mem=0x%lx, irq=%d\n", dev->name, phymem, dev->irq); | ||
340 | |||
341 | return hostap_hw_ready(dev); | ||
342 | |||
343 | fail: | ||
344 | if (irq_registered && dev) | ||
345 | free_irq(dev->irq, dev); | ||
346 | |||
347 | if (mem) | ||
348 | iounmap(mem); | ||
349 | |||
350 | release_mem_region(phymem, pci_resource_len(pdev, 0)); | ||
351 | |||
352 | err_out_disable: | ||
353 | pci_disable_device(pdev); | ||
354 | prism2_free_local_data(dev); | ||
355 | |||
356 | return -ENODEV; | ||
357 | } | ||
358 | |||
359 | |||
360 | static void prism2_pci_remove(struct pci_dev *pdev) | ||
361 | { | ||
362 | struct net_device *dev; | ||
363 | struct hostap_interface *iface; | ||
364 | void __iomem *mem_start; | ||
365 | |||
366 | dev = pci_get_drvdata(pdev); | ||
367 | iface = netdev_priv(dev); | ||
368 | |||
369 | /* Reset the hardware, and ensure interrupts are disabled. */ | ||
370 | prism2_pci_cor_sreset(iface->local); | ||
371 | hfa384x_disable_interrupts(dev); | ||
372 | |||
373 | if (dev->irq) | ||
374 | free_irq(dev->irq, dev); | ||
375 | |||
376 | mem_start = iface->local->mem_start; | ||
377 | prism2_free_local_data(dev); | ||
378 | |||
379 | iounmap(mem_start); | ||
380 | |||
381 | release_mem_region(pci_resource_start(pdev, 0), | ||
382 | pci_resource_len(pdev, 0)); | ||
383 | pci_disable_device(pdev); | ||
384 | } | ||
385 | |||
386 | |||
387 | #ifdef CONFIG_PM | ||
388 | static int prism2_pci_suspend(struct pci_dev *pdev, u32 state) | ||
389 | { | ||
390 | struct net_device *dev = pci_get_drvdata(pdev); | ||
391 | |||
392 | if (netif_running(dev)) { | ||
393 | netif_stop_queue(dev); | ||
394 | netif_device_detach(dev); | ||
395 | } | ||
396 | prism2_suspend(dev); | ||
397 | pci_save_state(pdev); | ||
398 | pci_disable_device(pdev); | ||
399 | pci_set_power_state(pdev, 3); | ||
400 | |||
401 | return 0; | ||
402 | } | ||
403 | |||
404 | static int prism2_pci_resume(struct pci_dev *pdev) | ||
405 | { | ||
406 | struct net_device *dev = pci_get_drvdata(pdev); | ||
407 | |||
408 | pci_enable_device(pdev); | ||
409 | pci_restore_state(pdev); | ||
410 | prism2_hw_config(dev, 0); | ||
411 | if (netif_running(dev)) { | ||
412 | netif_device_attach(dev); | ||
413 | netif_start_queue(dev); | ||
414 | } | ||
415 | |||
416 | return 0; | ||
417 | } | ||
418 | #endif /* CONFIG_PM */ | ||
419 | |||
420 | |||
421 | MODULE_DEVICE_TABLE(pci, prism2_pci_id_table); | ||
422 | |||
423 | static struct pci_driver prism2_pci_drv_id = { | ||
424 | .name = "prism2_pci", | ||
425 | .id_table = prism2_pci_id_table, | ||
426 | .probe = prism2_pci_probe, | ||
427 | .remove = prism2_pci_remove, | ||
428 | #ifdef CONFIG_PM | ||
429 | .suspend = prism2_pci_suspend, | ||
430 | .resume = prism2_pci_resume, | ||
431 | #endif /* CONFIG_PM */ | ||
432 | /* Linux 2.4.6 added save_state and enable_wake that are not used here | ||
433 | */ | ||
434 | }; | ||
435 | |||
436 | |||
437 | static int __init init_prism2_pci(void) | ||
438 | { | ||
439 | printk(KERN_INFO "%s: %s\n", dev_info, version); | ||
440 | |||
441 | return pci_register_driver(&prism2_pci_drv_id); | ||
442 | } | ||
443 | |||
444 | |||
445 | static void __exit exit_prism2_pci(void) | ||
446 | { | ||
447 | pci_unregister_driver(&prism2_pci_drv_id); | ||
448 | printk(KERN_INFO "%s: Driver unloaded\n", dev_info); | ||
449 | } | ||
450 | |||
451 | |||
452 | module_init(init_prism2_pci); | ||
453 | module_exit(exit_prism2_pci); | ||