]>
Commit | Line | Data |
---|---|---|
099dc4fb DS |
1 | /* |
2 | * IPWireless 3G PCMCIA Network Driver | |
3 | * | |
4 | * Original code | |
5 | * by Stephen Blackheath <stephen@blacksapphire.com>, | |
6 | * Ben Martel <benm@symmetric.co.nz> | |
7 | * | |
8 | * Copyrighted as follows: | |
9 | * Copyright (C) 2004 by Symmetric Systems Ltd (NZ) | |
10 | * | |
11 | * Various driver changes and rewrites, port to new kernels | |
12 | * Copyright (C) 2006-2007 Jiri Kosina | |
13 | * | |
14 | * Misc code cleanups and updates | |
15 | * Copyright (C) 2007 David Sterba | |
16 | */ | |
17 | ||
18 | #include "hardware.h" | |
19 | #include "network.h" | |
20 | #include "main.h" | |
21 | #include "tty.h" | |
22 | ||
23 | #include <linux/delay.h> | |
24 | #include <linux/init.h> | |
25 | #include <linux/io.h> | |
26 | #include <linux/kernel.h> | |
27 | #include <linux/module.h> | |
28 | #include <linux/sched.h> | |
29 | #include <linux/slab.h> | |
30 | ||
099dc4fb DS |
31 | #include <pcmcia/cisreg.h> |
32 | #include <pcmcia/device_id.h> | |
33 | #include <pcmcia/ss.h> | |
34 | #include <pcmcia/ds.h> | |
35 | #include <pcmcia/cs.h> | |
36 | ||
37 | static struct pcmcia_device_id ipw_ids[] = { | |
38 | PCMCIA_DEVICE_MANF_CARD(0x02f2, 0x0100), | |
39 | PCMCIA_DEVICE_MANF_CARD(0x02f2, 0x0200), | |
40 | PCMCIA_DEVICE_NULL | |
41 | }; | |
42 | MODULE_DEVICE_TABLE(pcmcia, ipw_ids); | |
43 | ||
44 | static void ipwireless_detach(struct pcmcia_device *link); | |
45 | ||
46 | /* | |
47 | * Module params | |
48 | */ | |
49 | /* Debug mode: more verbose, print sent/recv bytes */ | |
50 | int ipwireless_debug; | |
51 | int ipwireless_loopback; | |
bee9c7c0 | 52 | int ipwireless_out_queue = 10; |
099dc4fb DS |
53 | |
54 | module_param_named(debug, ipwireless_debug, int, 0); | |
55 | module_param_named(loopback, ipwireless_loopback, int, 0); | |
56 | module_param_named(out_queue, ipwireless_out_queue, int, 0); | |
57 | MODULE_PARM_DESC(debug, "switch on debug messages [0]"); | |
58 | MODULE_PARM_DESC(loopback, | |
59 | "debug: enable ras_raw channel [0]"); | |
bee9c7c0 | 60 | MODULE_PARM_DESC(out_queue, "debug: set size of outgoing PPP queue [10]"); |
099dc4fb DS |
61 | |
62 | /* Executes in process context. */ | |
63 | static void signalled_reboot_work(struct work_struct *work_reboot) | |
64 | { | |
65 | struct ipw_dev *ipw = container_of(work_reboot, struct ipw_dev, | |
66 | work_reboot); | |
67 | struct pcmcia_device *link = ipw->link; | |
cbf624f0 | 68 | pcmcia_reset_card(link->socket); |
099dc4fb DS |
69 | } |
70 | ||
71 | static void signalled_reboot_callback(void *callback_data) | |
72 | { | |
73 | struct ipw_dev *ipw = (struct ipw_dev *) callback_data; | |
74 | ||
75 | /* Delegate to process context. */ | |
76 | schedule_work(&ipw->work_reboot); | |
77 | } | |
78 | ||
af757923 DB |
79 | static int ipwireless_probe(struct pcmcia_device *p_dev, |
80 | cistpl_cftable_entry_t *cfg, | |
81 | cistpl_cftable_entry_t *dflt, | |
82 | unsigned int vcc, | |
83 | void *priv_data) | |
aaa8cfda | 84 | { |
af757923 DB |
85 | struct ipw_dev *ipw = priv_data; |
86 | struct resource *io_resource; | |
af757923 DB |
87 | int ret; |
88 | ||
90abdc3b DB |
89 | p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO; |
90 | p_dev->resource[0]->start = cfg->io.win[0].base; | |
91 | p_dev->resource[0]->end = cfg->io.win[0].len; | |
aaa8cfda | 92 | |
aaa8cfda DB |
93 | /* 0x40 causes it to generate level mode interrupts. */ |
94 | /* 0x04 enables IREQ pin. */ | |
95 | p_dev->conf.ConfigIndex = cfg->index | 0x44; | |
90abdc3b DB |
96 | p_dev->io_lines = 16; |
97 | ret = pcmcia_request_io(p_dev); | |
af757923 DB |
98 | if (ret) |
99 | return ret; | |
aaa8cfda | 100 | |
9a017a91 DB |
101 | io_resource = request_region(p_dev->resource[0]->start, |
102 | resource_size(p_dev->resource[0]), | |
af757923 | 103 | IPWIRELESS_PCCARD_NAME); |
099dc4fb | 104 | |
af757923 DB |
105 | if (cfg->mem.nwin == 0) |
106 | return 0; | |
099dc4fb | 107 | |
af757923 DB |
108 | ipw->request_common_memory.Attributes = |
109 | WIN_DATA_WIDTH_16 | WIN_MEMORY_TYPE_CM | WIN_ENABLE; | |
110 | ipw->request_common_memory.Base = cfg->mem.win[0].host_addr; | |
111 | ipw->request_common_memory.Size = cfg->mem.win[0].len; | |
112 | if (ipw->request_common_memory.Size < 0x1000) | |
113 | ipw->request_common_memory.Size = 0x1000; | |
114 | ipw->request_common_memory.AccessSpeed = 0; | |
115 | ||
6838b03f | 116 | ret = pcmcia_request_window(p_dev, &ipw->request_common_memory, |
af757923 | 117 | &ipw->handle_common_memory); |
099dc4fb | 118 | |
cbf624f0 | 119 | if (ret != 0) |
af757923 | 120 | goto exit1; |
099dc4fb | 121 | |
868575d1 | 122 | ret = pcmcia_map_mem_page(p_dev, ipw->handle_common_memory, |
b5cb259e | 123 | cfg->mem.win[0].card_addr); |
099dc4fb | 124 | |
cbf624f0 | 125 | if (ret != 0) |
af757923 | 126 | goto exit2; |
099dc4fb | 127 | |
af757923 | 128 | ipw->is_v2_card = cfg->mem.win[0].len == 0x100; |
099dc4fb | 129 | |
af757923 DB |
130 | ipw->common_memory = ioremap(ipw->request_common_memory.Base, |
131 | ipw->request_common_memory.Size); | |
132 | request_mem_region(ipw->request_common_memory.Base, | |
133 | ipw->request_common_memory.Size, | |
134 | IPWIRELESS_PCCARD_NAME); | |
135 | ||
136 | ipw->request_attr_memory.Attributes = | |
137 | WIN_DATA_WIDTH_16 | WIN_MEMORY_TYPE_AM | WIN_ENABLE; | |
138 | ipw->request_attr_memory.Base = 0; | |
139 | ipw->request_attr_memory.Size = 0; /* this used to be 0x1000 */ | |
140 | ipw->request_attr_memory.AccessSpeed = 0; | |
099dc4fb | 141 | |
6838b03f | 142 | ret = pcmcia_request_window(p_dev, &ipw->request_attr_memory, |
af757923 | 143 | &ipw->handle_attr_memory); |
099dc4fb | 144 | |
cbf624f0 | 145 | if (ret != 0) |
af757923 | 146 | goto exit2; |
099dc4fb | 147 | |
b5cb259e | 148 | ret = pcmcia_map_mem_page(p_dev, ipw->handle_attr_memory, 0); |
cbf624f0 | 149 | if (ret != 0) |
af757923 | 150 | goto exit3; |
099dc4fb | 151 | |
af757923 DB |
152 | ipw->attr_memory = ioremap(ipw->request_attr_memory.Base, |
153 | ipw->request_attr_memory.Size); | |
154 | request_mem_region(ipw->request_attr_memory.Base, | |
155 | ipw->request_attr_memory.Size, IPWIRELESS_PCCARD_NAME); | |
099dc4fb | 156 | |
af757923 | 157 | return 0; |
099dc4fb | 158 | |
af757923 | 159 | exit3: |
f5560da5 | 160 | pcmcia_release_window(p_dev, ipw->handle_attr_memory); |
af757923 DB |
161 | exit2: |
162 | if (ipw->common_memory) { | |
163 | release_mem_region(ipw->request_common_memory.Base, | |
09e491e9 | 164 | ipw->request_common_memory.Size); |
af757923 | 165 | iounmap(ipw->common_memory); |
f5560da5 | 166 | pcmcia_release_window(p_dev, ipw->handle_common_memory); |
af757923 | 167 | } else |
f5560da5 | 168 | pcmcia_release_window(p_dev, ipw->handle_common_memory); |
af757923 DB |
169 | exit1: |
170 | release_resource(io_resource); | |
171 | pcmcia_disable_device(p_dev); | |
172 | return -1; | |
173 | } | |
099dc4fb | 174 | |
af757923 DB |
175 | static int config_ipwireless(struct ipw_dev *ipw) |
176 | { | |
177 | struct pcmcia_device *link = ipw->link; | |
178 | int ret = 0; | |
099dc4fb | 179 | |
af757923 | 180 | ipw->is_v2_card = 0; |
099dc4fb | 181 | |
af757923 | 182 | ret = pcmcia_loop_config(link, ipwireless_probe, ipw); |
cbf624f0 | 183 | if (ret != 0) |
af757923 | 184 | return ret; |
099dc4fb | 185 | |
af757923 DB |
186 | link->conf.Attributes = CONF_ENABLE_IRQ; |
187 | link->conf.IntType = INT_MEMORY_AND_IO; | |
099dc4fb | 188 | |
099dc4fb DS |
189 | INIT_WORK(&ipw->work_reboot, signalled_reboot_work); |
190 | ||
9a017a91 | 191 | ipwireless_init_hardware_v1(ipw->hardware, link->resource[0]->start, |
099dc4fb DS |
192 | ipw->attr_memory, ipw->common_memory, |
193 | ipw->is_v2_card, signalled_reboot_callback, | |
194 | ipw); | |
195 | ||
eb14120f | 196 | ret = pcmcia_request_irq(link, ipwireless_interrupt); |
cbf624f0 | 197 | if (ret != 0) |
af757923 | 198 | goto exit; |
099dc4fb | 199 | |
099dc4fb DS |
200 | printk(KERN_INFO IPWIRELESS_PCCARD_NAME ": Card type %s\n", |
201 | ipw->is_v2_card ? "V2/V3" : "V1"); | |
202 | printk(KERN_INFO IPWIRELESS_PCCARD_NAME | |
9a017a91 | 203 | ": I/O ports %pR, irq %d\n", link->resource[0], |
eb14120f | 204 | (unsigned int) link->irq); |
099dc4fb DS |
205 | if (ipw->attr_memory && ipw->common_memory) |
206 | printk(KERN_INFO IPWIRELESS_PCCARD_NAME | |
622e713e | 207 | ": attr memory 0x%08lx-0x%08lx, common memory 0x%08lx-0x%08lx\n", |
09e491e9 DS |
208 | ipw->request_attr_memory.Base, |
209 | ipw->request_attr_memory.Base | |
210 | + ipw->request_attr_memory.Size - 1, | |
211 | ipw->request_common_memory.Base, | |
212 | ipw->request_common_memory.Base | |
213 | + ipw->request_common_memory.Size - 1); | |
099dc4fb DS |
214 | |
215 | ipw->network = ipwireless_network_create(ipw->hardware); | |
216 | if (!ipw->network) | |
af757923 | 217 | goto exit; |
099dc4fb | 218 | |
b498ada6 | 219 | ipw->tty = ipwireless_tty_create(ipw->hardware, ipw->network); |
099dc4fb | 220 | if (!ipw->tty) |
af757923 | 221 | goto exit; |
099dc4fb DS |
222 | |
223 | ipwireless_init_hardware_v2_v3(ipw->hardware); | |
224 | ||
225 | /* | |
226 | * Do the RequestConfiguration last, because it enables interrupts. | |
227 | * Then we don't get any interrupts before we're ready for them. | |
228 | */ | |
229 | ret = pcmcia_request_configuration(link, &link->conf); | |
230 | ||
cbf624f0 | 231 | if (ret != 0) |
af757923 | 232 | goto exit; |
099dc4fb | 233 | |
099dc4fb DS |
234 | return 0; |
235 | ||
af757923 | 236 | exit: |
099dc4fb | 237 | if (ipw->attr_memory) { |
09e491e9 DS |
238 | release_mem_region(ipw->request_attr_memory.Base, |
239 | ipw->request_attr_memory.Size); | |
099dc4fb | 240 | iounmap(ipw->attr_memory); |
f5560da5 | 241 | pcmcia_release_window(link, ipw->handle_attr_memory); |
099dc4fb | 242 | } |
099dc4fb | 243 | if (ipw->common_memory) { |
09e491e9 DS |
244 | release_mem_region(ipw->request_common_memory.Base, |
245 | ipw->request_common_memory.Size); | |
099dc4fb | 246 | iounmap(ipw->common_memory); |
f5560da5 | 247 | pcmcia_release_window(link, ipw->handle_common_memory); |
099dc4fb | 248 | } |
099dc4fb | 249 | pcmcia_disable_device(link); |
099dc4fb DS |
250 | return -1; |
251 | } | |
252 | ||
253 | static void release_ipwireless(struct ipw_dev *ipw) | |
254 | { | |
09e491e9 DS |
255 | if (ipw->common_memory) { |
256 | release_mem_region(ipw->request_common_memory.Base, | |
257 | ipw->request_common_memory.Size); | |
099dc4fb | 258 | iounmap(ipw->common_memory); |
09e491e9 DS |
259 | } |
260 | if (ipw->attr_memory) { | |
261 | release_mem_region(ipw->request_attr_memory.Base, | |
262 | ipw->request_attr_memory.Size); | |
099dc4fb | 263 | iounmap(ipw->attr_memory); |
09e491e9 | 264 | } |
099dc4fb | 265 | if (ipw->common_memory) |
f5560da5 | 266 | pcmcia_release_window(ipw->link, ipw->handle_common_memory); |
099dc4fb | 267 | if (ipw->attr_memory) |
f5560da5 | 268 | pcmcia_release_window(ipw->link, ipw->handle_attr_memory); |
09e491e9 | 269 | |
09e491e9 | 270 | pcmcia_disable_device(ipw->link); |
099dc4fb DS |
271 | } |
272 | ||
273 | /* | |
274 | * ipwireless_attach() creates an "instance" of the driver, allocating | |
275 | * local data structures for one device (one interface). The device | |
276 | * is registered with Card Services. | |
277 | * | |
278 | * The pcmcia_device structure is initialized, but we don't actually | |
279 | * configure the card at this point -- we wait until we receive a | |
280 | * card insertion event. | |
281 | */ | |
282 | static int ipwireless_attach(struct pcmcia_device *link) | |
283 | { | |
284 | struct ipw_dev *ipw; | |
285 | int ret; | |
286 | ||
287 | ipw = kzalloc(sizeof(struct ipw_dev), GFP_KERNEL); | |
288 | if (!ipw) | |
289 | return -ENOMEM; | |
290 | ||
291 | ipw->link = link; | |
292 | link->priv = ipw; | |
099dc4fb | 293 | |
099dc4fb DS |
294 | ipw->hardware = ipwireless_hardware_create(); |
295 | if (!ipw->hardware) { | |
296 | kfree(ipw); | |
297 | return -ENOMEM; | |
298 | } | |
299 | /* RegisterClient will call config_ipwireless */ | |
300 | ||
301 | ret = config_ipwireless(ipw); | |
302 | ||
303 | if (ret != 0) { | |
099dc4fb DS |
304 | ipwireless_detach(link); |
305 | return ret; | |
306 | } | |
307 | ||
308 | return 0; | |
309 | } | |
310 | ||
311 | /* | |
312 | * This deletes a driver "instance". The device is de-registered with | |
313 | * Card Services. If it has been released, all local data structures | |
314 | * are freed. Otherwise, the structures will be freed when the device | |
315 | * is released. | |
316 | */ | |
317 | static void ipwireless_detach(struct pcmcia_device *link) | |
318 | { | |
319 | struct ipw_dev *ipw = link->priv; | |
320 | ||
321 | release_ipwireless(ipw); | |
322 | ||
099dc4fb DS |
323 | if (ipw->tty != NULL) |
324 | ipwireless_tty_free(ipw->tty); | |
325 | if (ipw->network != NULL) | |
326 | ipwireless_network_free(ipw->network); | |
327 | if (ipw->hardware != NULL) | |
328 | ipwireless_hardware_free(ipw->hardware); | |
329 | kfree(ipw); | |
330 | } | |
331 | ||
332 | static struct pcmcia_driver me = { | |
333 | .owner = THIS_MODULE, | |
334 | .probe = ipwireless_attach, | |
335 | .remove = ipwireless_detach, | |
336 | .drv = { .name = IPWIRELESS_PCCARD_NAME }, | |
337 | .id_table = ipw_ids | |
338 | }; | |
339 | ||
340 | /* | |
341 | * Module insertion : initialisation of the module. | |
342 | * Register the card with cardmgr... | |
343 | */ | |
344 | static int __init init_ipwireless(void) | |
345 | { | |
346 | int ret; | |
347 | ||
348 | printk(KERN_INFO IPWIRELESS_PCCARD_NAME " " | |
349 | IPWIRELESS_PCMCIA_VERSION " by " IPWIRELESS_PCMCIA_AUTHOR "\n"); | |
350 | ||
351 | ret = ipwireless_tty_init(); | |
352 | if (ret != 0) | |
353 | return ret; | |
354 | ||
355 | ret = pcmcia_register_driver(&me); | |
356 | if (ret != 0) | |
357 | ipwireless_tty_release(); | |
358 | ||
359 | return ret; | |
360 | } | |
361 | ||
362 | /* | |
363 | * Module removal | |
364 | */ | |
365 | static void __exit exit_ipwireless(void) | |
366 | { | |
367 | printk(KERN_INFO IPWIRELESS_PCCARD_NAME " " | |
368 | IPWIRELESS_PCMCIA_VERSION " removed\n"); | |
369 | ||
370 | pcmcia_unregister_driver(&me); | |
371 | ipwireless_tty_release(); | |
372 | } | |
373 | ||
374 | module_init(init_ipwireless); | |
375 | module_exit(exit_ipwireless); | |
376 | ||
377 | MODULE_AUTHOR(IPWIRELESS_PCMCIA_AUTHOR); | |
378 | MODULE_DESCRIPTION(IPWIRELESS_PCCARD_NAME " " IPWIRELESS_PCMCIA_VERSION); | |
379 | MODULE_LICENSE("GPL"); |