]>
Commit | Line | Data |
---|---|---|
13a9930d WS |
1 | /* |
2 | * Driver for KeyStream, KS7010 based SDIO cards. | |
3 | * | |
4 | * ks7010_sdio.c | |
5 | * $Id: ks7010_sdio.c 996 2009-09-14 02:54:21Z sekine $ | |
6 | * | |
7 | * Copyright (C) 2006-2008 KeyStream Corp. | |
8 | * Copyright (C) 2009 Renesas Technology Corp. | |
9 | * | |
10 | * This program is free software; you can redistribute it and/or modify | |
11 | * it undr the terms of the GNU General Public License version 2 as | |
12 | * published by the Free Sotware Foundation. | |
13 | */ | |
14 | ||
15 | #include <linux/workqueue.h> | |
16 | #include <asm/atomic.h> | |
17 | #include <linux/mmc/card.h> | |
18 | #include <linux/mmc/sdio_func.h> | |
19 | ||
20 | #include "ks_wlan.h" | |
21 | #include "ks_wlan_ioctl.h" | |
13a9930d WS |
22 | #include "ks_hostif.h" |
23 | ||
24 | #include "ks7010_sdio.h" | |
25 | ||
26 | #define KS7010_FUNC_NUM 1 | |
27 | #define KS7010_IO_BLOCK_SIZE 512 | |
28 | #define KS7010_MAX_CLOCK 25000000 | |
29 | ||
30 | static int reg_net = 0; | |
31 | ||
32 | static const struct sdio_device_id if_sdio_ids[] = { | |
cdf6ecc5 WS |
33 | {SDIO_DEVICE(SDIO_VENDOR_ID_KS_CODE_A, SDIO_DEVICE_ID_KS_7010)}, |
34 | {SDIO_DEVICE(SDIO_VENDOR_ID_KS_CODE_B, SDIO_DEVICE_ID_KS_7010)}, | |
13a9930d WS |
35 | { /* all zero */ } |
36 | }; | |
37 | ||
38 | struct ks_sdio_model { | |
cdf6ecc5 WS |
39 | int model; |
40 | const char *firmware; | |
13a9930d WS |
41 | }; |
42 | ||
43 | static struct ks_sdio_model ks_sdio_models[] = { | |
cdf6ecc5 WS |
44 | { |
45 | /* ks7010 */ | |
46 | .model = 0x10, | |
47 | .firmware = "ks7010sd.rom", | |
48 | }, | |
13a9930d WS |
49 | }; |
50 | ||
cdf6ecc5 WS |
51 | static int ks7910_sdio_probe(struct sdio_func *function, |
52 | const struct sdio_device_id *device); | |
13a9930d WS |
53 | static void ks7910_sdio_remove(struct sdio_func *function); |
54 | static void ks7010_rw_function(struct work_struct *work); | |
cdf6ecc5 WS |
55 | static int ks7010_sdio_read(struct ks_wlan_private *priv, unsigned int address, |
56 | unsigned char *buffer, int length); | |
57 | static int ks7010_sdio_write(struct ks_wlan_private *priv, unsigned int address, | |
58 | unsigned char *buffer, int length); | |
13a9930d WS |
59 | /* macro */ |
60 | ||
61 | #define inc_txqhead(priv) \ | |
62 | ( priv->tx_dev.qhead = (priv->tx_dev.qhead + 1) % TX_DEVICE_BUFF_SIZE ) | |
63 | #define inc_txqtail(priv) \ | |
64 | ( priv->tx_dev.qtail = (priv->tx_dev.qtail + 1) % TX_DEVICE_BUFF_SIZE ) | |
65 | #define cnt_txqbody(priv) \ | |
66 | (((priv->tx_dev.qtail + TX_DEVICE_BUFF_SIZE) - (priv->tx_dev.qhead)) % TX_DEVICE_BUFF_SIZE ) | |
67 | ||
68 | #define inc_rxqhead(priv) \ | |
69 | ( priv->rx_dev.qhead = (priv->rx_dev.qhead + 1) % RX_DEVICE_BUFF_SIZE ) | |
70 | #define inc_rxqtail(priv) \ | |
71 | ( priv->rx_dev.qtail = (priv->rx_dev.qtail + 1) % RX_DEVICE_BUFF_SIZE ) | |
72 | #define cnt_rxqbody(priv) \ | |
73 | (((priv->rx_dev.qtail + RX_DEVICE_BUFF_SIZE) - (priv->rx_dev.qhead)) % RX_DEVICE_BUFF_SIZE ) | |
74 | ||
feedcf1a | 75 | void ks_wlan_hw_sleep_doze_request(struct ks_wlan_private *priv) |
13a9930d WS |
76 | { |
77 | unsigned char rw_data; | |
78 | int retval; | |
79 | ||
80 | DPRINTK(4, "\n"); | |
81 | ||
82 | /* clear request */ | |
cdf6ecc5 | 83 | atomic_set(&priv->sleepstatus.doze_request, 0); |
13a9930d | 84 | |
cdf6ecc5 | 85 | if (atomic_read(&priv->sleepstatus.status) == 0) { |
13a9930d | 86 | rw_data = GCR_B_DOZE; |
cdf6ecc5 WS |
87 | retval = |
88 | ks7010_sdio_write(priv, GCR_B, &rw_data, sizeof(rw_data)); | |
89 | if (retval) { | |
13a9930d WS |
90 | DPRINTK(1, " error : GCR_B=%02X\n", rw_data); |
91 | goto out; | |
92 | } | |
93 | DPRINTK(4, "PMG SET!! : GCR_B=%02X\n", rw_data); | |
cdf6ecc5 | 94 | DPRINTK(3, "sleep_mode=SLP_SLEEP\n"); |
13a9930d | 95 | atomic_set(&priv->sleepstatus.status, 1); |
cdf6ecc5 WS |
96 | priv->last_doze = jiffies; |
97 | } else { | |
98 | DPRINTK(1, "sleep_mode=%d\n", priv->sleep_mode); | |
13a9930d WS |
99 | } |
100 | ||
cdf6ecc5 | 101 | out: |
13a9930d WS |
102 | priv->sleep_mode = atomic_read(&priv->sleepstatus.status); |
103 | return; | |
104 | } | |
105 | ||
feedcf1a | 106 | void ks_wlan_hw_sleep_wakeup_request(struct ks_wlan_private *priv) |
13a9930d WS |
107 | { |
108 | unsigned char rw_data; | |
109 | int retval; | |
110 | ||
111 | DPRINTK(4, "\n"); | |
112 | ||
113 | /* clear request */ | |
cdf6ecc5 | 114 | atomic_set(&priv->sleepstatus.wakeup_request, 0); |
13a9930d | 115 | |
cdf6ecc5 | 116 | if (atomic_read(&priv->sleepstatus.status) == 1) { |
13a9930d | 117 | rw_data = WAKEUP_REQ; |
cdf6ecc5 WS |
118 | retval = |
119 | ks7010_sdio_write(priv, WAKEUP, &rw_data, sizeof(rw_data)); | |
120 | if (retval) { | |
13a9930d WS |
121 | DPRINTK(1, " error : WAKEUP=%02X\n", rw_data); |
122 | goto out; | |
123 | } | |
124 | DPRINTK(4, "wake up : WAKEUP=%02X\n", rw_data); | |
125 | atomic_set(&priv->sleepstatus.status, 0); | |
cdf6ecc5 | 126 | priv->last_wakeup = jiffies; |
13a9930d | 127 | ++priv->wakeup_count; |
cdf6ecc5 WS |
128 | } else { |
129 | DPRINTK(1, "sleep_mode=%d\n", priv->sleep_mode); | |
13a9930d WS |
130 | } |
131 | ||
cdf6ecc5 | 132 | out: |
13a9930d WS |
133 | priv->sleep_mode = atomic_read(&priv->sleepstatus.status); |
134 | return; | |
135 | } | |
136 | ||
feedcf1a | 137 | void ks_wlan_hw_wakeup_request(struct ks_wlan_private *priv) |
13a9930d WS |
138 | { |
139 | unsigned char rw_data; | |
140 | int retval; | |
141 | ||
142 | DPRINTK(4, "\n"); | |
cdf6ecc5 | 143 | if (atomic_read(&priv->psstatus.status) == PS_SNOOZE) { |
13a9930d | 144 | rw_data = WAKEUP_REQ; |
cdf6ecc5 WS |
145 | retval = |
146 | ks7010_sdio_write(priv, WAKEUP, &rw_data, sizeof(rw_data)); | |
147 | if (retval) { | |
13a9930d WS |
148 | DPRINTK(1, " error : WAKEUP=%02X\n", rw_data); |
149 | } | |
150 | DPRINTK(4, "wake up : WAKEUP=%02X\n", rw_data); | |
cdf6ecc5 | 151 | priv->last_wakeup = jiffies; |
13a9930d | 152 | ++priv->wakeup_count; |
cdf6ecc5 WS |
153 | } else { |
154 | DPRINTK(1, "psstatus=%d\n", | |
155 | atomic_read(&priv->psstatus.status)); | |
13a9930d WS |
156 | } |
157 | } | |
158 | ||
feedcf1a | 159 | int _ks_wlan_hw_power_save(struct ks_wlan_private *priv) |
13a9930d | 160 | { |
cdf6ecc5 | 161 | int rc = 0; |
13a9930d WS |
162 | unsigned char rw_data; |
163 | int retval; | |
164 | ||
cdf6ecc5 | 165 | if (priv->reg.powermgt == POWMGT_ACTIVE_MODE) |
13a9930d WS |
166 | return rc; |
167 | ||
cdf6ecc5 WS |
168 | if (priv->reg.operation_mode == MODE_INFRASTRUCTURE && |
169 | (priv->connect_status & CONNECT_STATUS_MASK) == CONNECT_STATUS) { | |
13a9930d WS |
170 | |
171 | //DPRINTK(1,"psstatus.status=%d\n",atomic_read(&priv->psstatus.status)); | |
cdf6ecc5 WS |
172 | if (priv->dev_state == DEVICE_STATE_SLEEP) { |
173 | switch (atomic_read(&priv->psstatus.status)) { | |
174 | case PS_SNOOZE: /* 4 */ | |
175 | break; | |
176 | default: | |
177 | DPRINTK(5, "\npsstatus.status=%d\npsstatus.confirm_wait=%d\npsstatus.snooze_guard=%d\ncnt_txqbody=%d\n", | |
178 | atomic_read(&priv->psstatus.status), | |
179 | atomic_read(&priv->psstatus.confirm_wait), | |
180 | atomic_read(&priv->psstatus.snooze_guard), | |
181 | cnt_txqbody(priv)); | |
182 | ||
183 | if (!atomic_read(&priv->psstatus.confirm_wait) | |
184 | && !atomic_read(&priv->psstatus.snooze_guard) | |
185 | && !cnt_txqbody(priv)) { | |
186 | retval = | |
187 | ks7010_sdio_read(priv, INT_PENDING, | |
188 | &rw_data, | |
189 | sizeof(rw_data)); | |
190 | if (retval) { | |
191 | DPRINTK(1, | |
192 | " error : INT_PENDING=%02X\n", | |
193 | rw_data); | |
194 | queue_delayed_work(priv->ks_wlan_hw.ks7010sdio_wq, | |
195 | &priv->ks_wlan_hw.rw_wq, 1); | |
13a9930d WS |
196 | break; |
197 | } | |
cdf6ecc5 WS |
198 | if (!rw_data) { |
199 | rw_data = GCR_B_DOZE; | |
200 | retval = | |
201 | ks7010_sdio_write(priv, | |
202 | GCR_B, | |
203 | &rw_data, | |
204 | sizeof(rw_data)); | |
205 | if (retval) { | |
206 | DPRINTK(1, | |
207 | " error : GCR_B=%02X\n", | |
208 | rw_data); | |
209 | queue_delayed_work | |
210 | (priv->ks_wlan_hw.ks7010sdio_wq, | |
211 | &priv->ks_wlan_hw.rw_wq, 1); | |
212 | break; | |
213 | } | |
214 | DPRINTK(4, | |
215 | "PMG SET!! : GCR_B=%02X\n", | |
216 | rw_data); | |
217 | atomic_set(&priv->psstatus. | |
218 | status, PS_SNOOZE); | |
219 | DPRINTK(3, | |
220 | "psstatus.status=PS_SNOOZE\n"); | |
221 | } else { | |
222 | queue_delayed_work(priv->ks_wlan_hw.ks7010sdio_wq, | |
223 | &priv->ks_wlan_hw.rw_wq, 1); | |
224 | } | |
225 | } else { | |
226 | queue_delayed_work(priv->ks_wlan_hw. | |
227 | ks7010sdio_wq, | |
228 | &priv->ks_wlan_hw.rw_wq, | |
229 | 0); | |
13a9930d | 230 | } |
cdf6ecc5 | 231 | break; |
13a9930d | 232 | } |
13a9930d | 233 | } |
13a9930d WS |
234 | |
235 | } | |
236 | ||
237 | return rc; | |
238 | } | |
239 | ||
feedcf1a | 240 | int ks_wlan_hw_power_save(struct ks_wlan_private *priv) |
13a9930d | 241 | { |
cdf6ecc5 WS |
242 | queue_delayed_work(priv->ks_wlan_hw.ks7010sdio_wq, |
243 | &priv->ks_wlan_hw.rw_wq, 1); | |
13a9930d WS |
244 | return 0; |
245 | } | |
246 | ||
feedcf1a | 247 | static int ks7010_sdio_read(struct ks_wlan_private *priv, unsigned int address, |
13a9930d WS |
248 | unsigned char *buffer, int length) |
249 | { | |
250 | int rc = -1; | |
251 | struct ks_sdio_card *card; | |
252 | ||
253 | card = priv->ks_wlan_hw.sdio_card; | |
254 | ||
cdf6ecc5 | 255 | if (length == 1) /* CMD52 */ |
13a9930d | 256 | *buffer = sdio_readb(card->func, address, &rc); |
cdf6ecc5 | 257 | else /* CMD53 multi-block transfer */ |
13a9930d WS |
258 | rc = sdio_memcpy_fromio(card->func, buffer, address, length); |
259 | ||
cdf6ecc5 | 260 | if (rc != 0) { |
13a9930d WS |
261 | printk("sdio error erorr=%d size=%d\n", rc, length); |
262 | ++priv->sdio_error_count; | |
cdf6ecc5 WS |
263 | } else { |
264 | priv->sdio_error_count = 0; | |
13a9930d WS |
265 | } |
266 | ||
267 | return rc; | |
268 | } | |
269 | ||
feedcf1a | 270 | static int ks7010_sdio_write(struct ks_wlan_private *priv, unsigned int address, |
13a9930d WS |
271 | unsigned char *buffer, int length) |
272 | { | |
273 | int rc = -1; | |
274 | struct ks_sdio_card *card; | |
275 | ||
276 | card = priv->ks_wlan_hw.sdio_card; | |
277 | ||
cdf6ecc5 WS |
278 | if (length == 1) /* CMD52 */ |
279 | sdio_writeb(card->func, *buffer, (unsigned int)address, &rc); | |
280 | else /* CMD53 */ | |
281 | rc = sdio_memcpy_toio(card->func, (unsigned int)address, buffer, | |
282 | length); | |
13a9930d | 283 | |
cdf6ecc5 | 284 | if (rc != 0) { |
13a9930d WS |
285 | printk("sdio error erorr=%d size=%d\n", rc, length); |
286 | ++priv->sdio_error_count; | |
cdf6ecc5 WS |
287 | } else { |
288 | priv->sdio_error_count = 0; | |
13a9930d WS |
289 | } |
290 | ||
291 | return rc; | |
292 | } | |
293 | ||
cdf6ecc5 WS |
294 | static int enqueue_txdev(struct ks_wlan_private *priv, unsigned char *p, |
295 | unsigned long size, | |
296 | void (*complete_handler) (void *arg1, void *arg2), | |
297 | void *arg1, void *arg2) | |
13a9930d WS |
298 | { |
299 | struct tx_device_buffer *sp; | |
300 | ||
301 | if (priv->dev_state < DEVICE_STATE_BOOT) { | |
302 | kfree(p); | |
303 | if (complete_handler != NULL) | |
cdf6ecc5 | 304 | (*complete_handler) (arg1, arg2); |
13a9930d WS |
305 | return 1; |
306 | } | |
307 | ||
cdf6ecc5 | 308 | if ((TX_DEVICE_BUFF_SIZE - 1) <= cnt_txqbody(priv)) { |
13a9930d | 309 | /* in case of buffer overflow */ |
cdf6ecc5 | 310 | DPRINTK(1, "tx buffer overflow\n"); |
13a9930d WS |
311 | kfree(p); |
312 | if (complete_handler != NULL) | |
cdf6ecc5 | 313 | (*complete_handler) (arg1, arg2); |
13a9930d WS |
314 | return 1; |
315 | } | |
316 | ||
317 | sp = &priv->tx_dev.tx_dev_buff[priv->tx_dev.qtail]; | |
318 | sp->sendp = p; | |
319 | sp->size = size; | |
320 | sp->complete_handler = complete_handler; | |
321 | sp->arg1 = arg1; | |
322 | sp->arg2 = arg2; | |
323 | inc_txqtail(priv); | |
324 | ||
325 | return 0; | |
326 | } | |
327 | ||
328 | /* write data */ | |
cdf6ecc5 WS |
329 | static int write_to_device(struct ks_wlan_private *priv, unsigned char *buffer, |
330 | unsigned long size) | |
13a9930d | 331 | { |
cdf6ecc5 | 332 | int rc, retval; |
13a9930d WS |
333 | unsigned char rw_data; |
334 | struct hostif_hdr *hdr; | |
335 | hdr = (struct hostif_hdr *)buffer; | |
cdf6ecc5 | 336 | rc = 0; |
13a9930d | 337 | |
cdf6ecc5 WS |
338 | DPRINTK(4, "size=%d\n", hdr->size); |
339 | if (hdr->event < HIF_DATA_REQ || HIF_REQ_MAX < hdr->event) { | |
340 | DPRINTK(1, "unknown event=%04X\n", hdr->event); | |
13a9930d WS |
341 | return 0; |
342 | } | |
343 | ||
344 | retval = ks7010_sdio_write(priv, DATA_WINDOW, buffer, size); | |
cdf6ecc5 | 345 | if (retval) { |
13a9930d WS |
346 | DPRINTK(1, " write error : retval=%d\n", retval); |
347 | return -4; | |
348 | } | |
349 | ||
350 | rw_data = WRITE_STATUS_BUSY; | |
cdf6ecc5 WS |
351 | retval = |
352 | ks7010_sdio_write(priv, WRITE_STATUS, &rw_data, sizeof(rw_data)); | |
353 | if (retval) { | |
13a9930d WS |
354 | DPRINTK(1, " error : WRITE_STATUS=%02X\n", rw_data); |
355 | return -3; | |
356 | } | |
357 | ||
358 | return 0; | |
359 | } | |
360 | ||
361 | static void tx_device_task(void *dev) | |
362 | { | |
feedcf1a | 363 | struct ks_wlan_private *priv = (struct ks_wlan_private *)dev; |
cdf6ecc5 | 364 | struct tx_device_buffer *sp; |
13a9930d WS |
365 | int rc = 0; |
366 | ||
367 | DPRINTK(4, "\n"); | |
cdf6ecc5 WS |
368 | if (cnt_txqbody(priv) > 0 |
369 | && atomic_read(&priv->psstatus.status) != PS_SNOOZE) { | |
13a9930d | 370 | sp = &priv->tx_dev.tx_dev_buff[priv->tx_dev.qhead]; |
cdf6ecc5 | 371 | if (priv->dev_state >= DEVICE_STATE_BOOT) { |
13a9930d | 372 | rc = write_to_device(priv, sp->sendp, sp->size); |
cdf6ecc5 WS |
373 | if (rc) { |
374 | DPRINTK(1, "write_to_device error !!(%d)\n", | |
375 | rc); | |
376 | queue_delayed_work(priv->ks_wlan_hw. | |
377 | ks7010sdio_wq, | |
378 | &priv->ks_wlan_hw.rw_wq, 1); | |
13a9930d WS |
379 | return; |
380 | } | |
381 | ||
382 | } | |
cdf6ecc5 WS |
383 | kfree(sp->sendp); /* allocated memory free */ |
384 | if (sp->complete_handler != NULL) /* TX Complete */ | |
385 | (*sp->complete_handler) (sp->arg1, sp->arg2); | |
13a9930d WS |
386 | inc_txqhead(priv); |
387 | ||
cdf6ecc5 WS |
388 | if (cnt_txqbody(priv) > 0) { |
389 | queue_delayed_work(priv->ks_wlan_hw.ks7010sdio_wq, | |
390 | &priv->ks_wlan_hw.rw_wq, 0); | |
13a9930d WS |
391 | } |
392 | } | |
393 | return; | |
394 | } | |
395 | ||
cdf6ecc5 WS |
396 | int ks_wlan_hw_tx(struct ks_wlan_private *priv, void *p, unsigned long size, |
397 | void (*complete_handler) (void *arg1, void *arg2), | |
398 | void *arg1, void *arg2) | |
13a9930d | 399 | { |
cdf6ecc5 | 400 | int result = 0; |
13a9930d WS |
401 | struct hostif_hdr *hdr; |
402 | hdr = (struct hostif_hdr *)p; | |
403 | ||
cdf6ecc5 WS |
404 | if (hdr->event < HIF_DATA_REQ || HIF_REQ_MAX < hdr->event) { |
405 | DPRINTK(1, "unknown event=%04X\n", hdr->event); | |
13a9930d WS |
406 | return 0; |
407 | } | |
408 | ||
409 | /* add event to hostt buffer */ | |
410 | priv->hostt.buff[priv->hostt.qtail] = hdr->event; | |
cdf6ecc5 | 411 | priv->hostt.qtail = (priv->hostt.qtail + 1) % SME_EVENT_BUFF_SIZE; |
13a9930d | 412 | |
cdf6ecc5 | 413 | DPRINTK(4, "event=%04X\n", hdr->event); |
13a9930d WS |
414 | spin_lock(&priv->tx_dev.tx_dev_lock); |
415 | result = enqueue_txdev(priv, p, size, complete_handler, arg1, arg2); | |
416 | spin_unlock(&priv->tx_dev.tx_dev_lock); | |
417 | ||
cdf6ecc5 WS |
418 | if (cnt_txqbody(priv) > 0) { |
419 | queue_delayed_work(priv->ks_wlan_hw.ks7010sdio_wq, | |
420 | &priv->ks_wlan_hw.rw_wq, 0); | |
13a9930d WS |
421 | } |
422 | return result; | |
423 | } | |
424 | ||
425 | static void rx_event_task(unsigned long dev) | |
426 | { | |
cdf6ecc5 WS |
427 | struct ks_wlan_private *priv = (struct ks_wlan_private *)dev; |
428 | struct rx_device_buffer *rp; | |
13a9930d | 429 | |
cdf6ecc5 | 430 | DPRINTK(4, "\n"); |
13a9930d | 431 | |
cdf6ecc5 | 432 | if (cnt_rxqbody(priv) > 0 && priv->dev_state >= DEVICE_STATE_BOOT) { |
13a9930d WS |
433 | rp = &priv->rx_dev.rx_dev_buff[priv->rx_dev.qhead]; |
434 | hostif_receive(priv, rp->data, rp->size); | |
435 | inc_rxqhead(priv); | |
436 | ||
cdf6ecc5 | 437 | if (cnt_rxqbody(priv) > 0) { |
13a9930d WS |
438 | tasklet_schedule(&priv->ks_wlan_hw.rx_bh_task); |
439 | } | |
440 | } | |
441 | ||
442 | return; | |
443 | } | |
444 | ||
445 | static void ks_wlan_hw_rx(void *dev, uint16_t size) | |
446 | { | |
feedcf1a | 447 | struct ks_wlan_private *priv = (struct ks_wlan_private *)dev; |
13a9930d WS |
448 | int retval; |
449 | struct rx_device_buffer *rx_buffer; | |
450 | struct hostif_hdr *hdr; | |
cdf6ecc5 WS |
451 | unsigned char read_status; |
452 | unsigned short event = 0; | |
13a9930d | 453 | |
cdf6ecc5 | 454 | DPRINTK(4, "\n"); |
13a9930d WS |
455 | |
456 | /* receive data */ | |
cdf6ecc5 | 457 | if (cnt_rxqbody(priv) >= (RX_DEVICE_BUFF_SIZE - 1)) { |
13a9930d | 458 | /* in case of buffer overflow */ |
cdf6ecc5 | 459 | DPRINTK(1, "rx buffer overflow \n"); |
13a9930d WS |
460 | goto error_out; |
461 | } | |
462 | rx_buffer = &priv->rx_dev.rx_dev_buff[priv->rx_dev.qtail]; | |
463 | ||
cdf6ecc5 WS |
464 | retval = |
465 | ks7010_sdio_read(priv, DATA_WINDOW, &rx_buffer->data[0], | |
466 | hif_align_size(size)); | |
467 | if (retval) { | |
13a9930d WS |
468 | goto error_out; |
469 | } | |
470 | ||
471 | /* length check */ | |
cdf6ecc5 | 472 | if (size > 2046 || size == 0) { |
3215bb1a WS |
473 | #ifdef KS_WLAN_DEBUG |
474 | if (KS_WLAN_DEBUG > 5) | |
cdf6ecc5 WS |
475 | print_hex_dump_bytes("INVALID DATA dump: ", |
476 | DUMP_PREFIX_OFFSET, | |
3215bb1a WS |
477 | rx_buffer->data, 32); |
478 | #endif | |
13a9930d WS |
479 | /* rx_status update */ |
480 | read_status = READ_STATUS_IDLE; | |
cdf6ecc5 WS |
481 | retval = |
482 | ks7010_sdio_write(priv, READ_STATUS, &read_status, | |
483 | sizeof(read_status)); | |
484 | if (retval) { | |
13a9930d WS |
485 | DPRINTK(1, " error : READ_STATUS=%02X\n", read_status); |
486 | } | |
487 | goto error_out; | |
488 | } | |
489 | ||
490 | hdr = (struct hostif_hdr *)&rx_buffer->data[0]; | |
491 | rx_buffer->size = le16_to_cpu(hdr->size) + sizeof(hdr->size); | |
492 | event = hdr->event; | |
493 | inc_rxqtail(priv); | |
494 | ||
495 | /* read status update */ | |
496 | read_status = READ_STATUS_IDLE; | |
cdf6ecc5 WS |
497 | retval = |
498 | ks7010_sdio_write(priv, READ_STATUS, &read_status, | |
499 | sizeof(read_status)); | |
500 | if (retval) { | |
13a9930d WS |
501 | DPRINTK(1, " error : READ_STATUS=%02X\n", read_status); |
502 | } | |
503 | DPRINTK(4, "READ_STATUS=%02X\n", read_status); | |
504 | ||
cdf6ecc5 WS |
505 | if (atomic_read(&priv->psstatus.confirm_wait)) { |
506 | if (IS_HIF_CONF(event)) { | |
13a9930d WS |
507 | DPRINTK(4, "IS_HIF_CONF true !!\n"); |
508 | atomic_dec(&priv->psstatus.confirm_wait); | |
509 | } | |
510 | } | |
511 | ||
512 | /* rx_event_task((void *)priv); */ | |
513 | tasklet_schedule(&priv->ks_wlan_hw.rx_bh_task); | |
514 | ||
cdf6ecc5 | 515 | error_out: |
13a9930d WS |
516 | return; |
517 | } | |
518 | ||
519 | static void ks7010_rw_function(struct work_struct *work) | |
520 | { | |
521 | struct hw_info_t *hw; | |
522 | struct ks_wlan_private *priv; | |
523 | unsigned char rw_data; | |
524 | int retval; | |
525 | ||
526 | hw = container_of(work, struct hw_info_t, rw_wq.work); | |
527 | priv = container_of(hw, struct ks_wlan_private, ks_wlan_hw); | |
528 | ||
cdf6ecc5 | 529 | DPRINTK(4, "\n"); |
13a9930d | 530 | |
cdf6ecc5 WS |
531 | /* wiat after DOZE */ |
532 | if (time_after(priv->last_doze + ((30 * HZ) / 1000), jiffies)) { | |
533 | DPRINTK(4, "wait after DOZE \n"); | |
534 | queue_delayed_work(priv->ks_wlan_hw.ks7010sdio_wq, | |
535 | &priv->ks_wlan_hw.rw_wq, 1); | |
13a9930d WS |
536 | return; |
537 | } | |
538 | ||
539 | /* wiat after WAKEUP */ | |
cdf6ecc5 | 540 | while (time_after(priv->last_wakeup + ((30 * HZ) / 1000), jiffies)) { |
13a9930d WS |
541 | DPRINTK(4, "wait after WAKEUP \n"); |
542 | /* queue_delayed_work(priv->ks_wlan_hw.ks7010sdio_wq,&priv->ks_wlan_hw.rw_wq, | |
543 | (priv->last_wakeup + ((30*HZ)/1000) - jiffies));*/ | |
cdf6ecc5 WS |
544 | printk("wake: %lu %lu\n", priv->last_wakeup + (30 * HZ) / 1000, |
545 | jiffies); | |
13a9930d WS |
546 | msleep(30); |
547 | } | |
548 | ||
549 | sdio_claim_host(priv->ks_wlan_hw.sdio_card->func); | |
550 | ||
551 | /* power save wakeup */ | |
cdf6ecc5 WS |
552 | if (atomic_read(&priv->psstatus.status) == PS_SNOOZE) { |
553 | if (cnt_txqbody(priv) > 0) { | |
13a9930d | 554 | ks_wlan_hw_wakeup_request(priv); |
cdf6ecc5 WS |
555 | queue_delayed_work(priv->ks_wlan_hw.ks7010sdio_wq, |
556 | &priv->ks_wlan_hw.rw_wq, 1); | |
13a9930d WS |
557 | } |
558 | goto err_out; | |
559 | } | |
560 | ||
561 | /* sleep mode doze */ | |
cdf6ecc5 | 562 | if (atomic_read(&priv->sleepstatus.doze_request) == 1) { |
13a9930d WS |
563 | ks_wlan_hw_sleep_doze_request(priv); |
564 | goto err_out; | |
565 | } | |
566 | /* sleep mode wakeup */ | |
cdf6ecc5 | 567 | if (atomic_read(&priv->sleepstatus.wakeup_request) == 1) { |
13a9930d WS |
568 | ks_wlan_hw_sleep_wakeup_request(priv); |
569 | goto err_out; | |
570 | } | |
571 | ||
572 | /* read (WriteStatus/ReadDataSize FN1:00_0014) */ | |
cdf6ecc5 WS |
573 | retval = |
574 | ks7010_sdio_read(priv, WSTATUS_RSIZE, &rw_data, sizeof(rw_data)); | |
575 | if (retval) { | |
576 | DPRINTK(1, " error : WSTATUS_RSIZE=%02X psstatus=%d\n", rw_data, | |
577 | atomic_read(&priv->psstatus.status)); | |
13a9930d WS |
578 | goto err_out; |
579 | } | |
580 | DPRINTK(4, "WSTATUS_RSIZE=%02X\n", rw_data); | |
581 | ||
cdf6ecc5 WS |
582 | if (rw_data & RSIZE_MASK) { /* Read schedule */ |
583 | ks_wlan_hw_rx((void *)priv, | |
584 | (uint16_t) (((rw_data & RSIZE_MASK) << 4))); | |
13a9930d | 585 | } |
cdf6ecc5 | 586 | if ((rw_data & WSTATUS_MASK)) { |
13a9930d WS |
587 | tx_device_task((void *)priv); |
588 | } | |
589 | _ks_wlan_hw_power_save(priv); | |
590 | ||
cdf6ecc5 | 591 | err_out: |
13a9930d WS |
592 | sdio_release_host(priv->ks_wlan_hw.sdio_card->func); |
593 | ||
594 | return; | |
595 | } | |
596 | ||
13a9930d WS |
597 | static void ks_sdio_interrupt(struct sdio_func *func) |
598 | { | |
599 | int retval; | |
600 | struct ks_sdio_card *card; | |
feedcf1a | 601 | struct ks_wlan_private *priv; |
13a9930d WS |
602 | unsigned char status, rsize, rw_data; |
603 | ||
604 | card = sdio_get_drvdata(func); | |
605 | priv = card->priv; | |
606 | DPRINTK(4, "\n"); | |
607 | ||
cdf6ecc5 WS |
608 | if (priv->dev_state >= DEVICE_STATE_BOOT) { |
609 | retval = | |
610 | ks7010_sdio_read(priv, INT_PENDING, &status, | |
611 | sizeof(status)); | |
612 | if (retval) { | |
613 | DPRINTK(1, "read INT_PENDING Failed!!(%d)\n", retval); | |
13a9930d WS |
614 | goto intr_out; |
615 | } | |
616 | DPRINTK(4, "INT_PENDING=%02X\n", rw_data); | |
617 | ||
618 | /* schedule task for interrupt status */ | |
619 | /* bit7 -> Write General Communication B register */ | |
620 | /* read (General Communication B register) */ | |
621 | /* bit5 -> Write Status Idle */ | |
622 | /* bit2 -> Read Status Busy */ | |
cdf6ecc5 WS |
623 | if (status & INT_GCR_B |
624 | || atomic_read(&priv->psstatus.status) == PS_SNOOZE) { | |
625 | retval = | |
626 | ks7010_sdio_read(priv, GCR_B, &rw_data, | |
627 | sizeof(rw_data)); | |
628 | if (retval) { | |
13a9930d WS |
629 | DPRINTK(1, " error : GCR_B=%02X\n", rw_data); |
630 | goto intr_out; | |
631 | } | |
632 | /* DPRINTK(1, "GCR_B=%02X\n", rw_data); */ | |
cdf6ecc5 WS |
633 | if (rw_data == GCR_B_ACTIVE) { |
634 | if (atomic_read(&priv->psstatus.status) == | |
635 | PS_SNOOZE) { | |
636 | atomic_set(&priv->psstatus.status, | |
637 | PS_WAKEUP); | |
638 | priv->wakeup_count = 0; | |
13a9930d WS |
639 | } |
640 | complete(&priv->psstatus.wakeup_wait); | |
641 | } | |
642 | ||
13a9930d WS |
643 | } |
644 | ||
cdf6ecc5 | 645 | do { |
13a9930d | 646 | /* read (WriteStatus/ReadDataSize FN1:00_0014) */ |
cdf6ecc5 WS |
647 | retval = |
648 | ks7010_sdio_read(priv, WSTATUS_RSIZE, &rw_data, | |
649 | sizeof(rw_data)); | |
650 | if (retval) { | |
651 | DPRINTK(1, " error : WSTATUS_RSIZE=%02X\n", | |
652 | rw_data); | |
13a9930d WS |
653 | goto intr_out; |
654 | } | |
655 | DPRINTK(4, "WSTATUS_RSIZE=%02X\n", rw_data); | |
cdf6ecc5 WS |
656 | rsize = rw_data & RSIZE_MASK; |
657 | if (rsize) { /* Read schedule */ | |
658 | ks_wlan_hw_rx((void *)priv, | |
659 | (uint16_t) (((rsize) << 4))); | |
13a9930d | 660 | } |
cdf6ecc5 | 661 | if (rw_data & WSTATUS_MASK) { |
13a9930d | 662 | #if 0 |
cdf6ecc5 WS |
663 | if (status & INT_WRITE_STATUS |
664 | && !cnt_txqbody(priv)) { | |
13a9930d | 665 | /* dummy write for interrupt clear */ |
cdf6ecc5 WS |
666 | rw_data = 0; |
667 | retval = | |
668 | ks7010_sdio_write(priv, DATA_WINDOW, | |
669 | &rw_data, | |
670 | sizeof(rw_data)); | |
13a9930d | 671 | if (retval) { |
cdf6ecc5 WS |
672 | DPRINTK(1, |
673 | "write DATA_WINDOW Failed!!(%d)\n", | |
674 | retval); | |
13a9930d WS |
675 | } |
676 | status &= ~INT_WRITE_STATUS; | |
cdf6ecc5 | 677 | } else { |
13a9930d | 678 | #endif |
cdf6ecc5 WS |
679 | if (atomic_read(&priv->psstatus.status) == PS_SNOOZE) { |
680 | if (cnt_txqbody(priv)) { | |
13a9930d | 681 | ks_wlan_hw_wakeup_request(priv); |
cdf6ecc5 WS |
682 | queue_delayed_work |
683 | (priv->ks_wlan_hw. | |
684 | ks7010sdio_wq, | |
685 | &priv->ks_wlan_hw. | |
686 | rw_wq, 1); | |
13a9930d WS |
687 | return; |
688 | } | |
cdf6ecc5 | 689 | } else { |
13a9930d WS |
690 | tx_device_task((void *)priv); |
691 | } | |
cdf6ecc5 WS |
692 | #if 0 |
693 | } | |
694 | #endif | |
13a9930d | 695 | } |
cdf6ecc5 | 696 | } while (rsize); |
13a9930d WS |
697 | } |
698 | ||
cdf6ecc5 WS |
699 | intr_out: |
700 | queue_delayed_work(priv->ks_wlan_hw.ks7010sdio_wq, | |
701 | &priv->ks_wlan_hw.rw_wq, 0); | |
13a9930d WS |
702 | return; |
703 | } | |
704 | ||
cdf6ecc5 | 705 | static int trx_device_init(struct ks_wlan_private *priv) |
13a9930d WS |
706 | { |
707 | /* initialize values (tx) */ | |
708 | priv->tx_dev.qtail = priv->tx_dev.qhead = 0; | |
709 | ||
710 | /* initialize values (rx) */ | |
711 | priv->rx_dev.qtail = priv->rx_dev.qhead = 0; | |
712 | ||
713 | /* initialize spinLock (tx,rx) */ | |
714 | spin_lock_init(&priv->tx_dev.tx_dev_lock); | |
715 | spin_lock_init(&priv->rx_dev.rx_dev_lock); | |
716 | ||
cdf6ecc5 WS |
717 | tasklet_init(&priv->ks_wlan_hw.rx_bh_task, rx_event_task, |
718 | (unsigned long)priv); | |
13a9930d WS |
719 | |
720 | return 0; | |
721 | } | |
722 | ||
cdf6ecc5 | 723 | static void trx_device_exit(struct ks_wlan_private *priv) |
13a9930d | 724 | { |
cdf6ecc5 | 725 | struct tx_device_buffer *sp; |
13a9930d WS |
726 | |
727 | /* tx buffer clear */ | |
cdf6ecc5 | 728 | while (cnt_txqbody(priv) > 0) { |
13a9930d | 729 | sp = &priv->tx_dev.tx_dev_buff[priv->tx_dev.qhead]; |
cdf6ecc5 WS |
730 | kfree(sp->sendp); /* allocated memory free */ |
731 | if (sp->complete_handler != NULL) /* TX Complete */ | |
732 | (*sp->complete_handler) (sp->arg1, sp->arg2); | |
13a9930d WS |
733 | inc_txqhead(priv); |
734 | } | |
735 | ||
736 | tasklet_kill(&priv->ks_wlan_hw.rx_bh_task); | |
737 | ||
738 | return; | |
739 | } | |
cdf6ecc5 | 740 | |
feedcf1a | 741 | static int ks7010_sdio_update_index(struct ks_wlan_private *priv, u32 index) |
13a9930d | 742 | { |
cdf6ecc5 | 743 | int rc = 0; |
13a9930d WS |
744 | int retval; |
745 | unsigned char *data_buf; | |
746 | data_buf = NULL; | |
747 | ||
748 | data_buf = kmalloc(sizeof(u32), GFP_KERNEL); | |
cdf6ecc5 WS |
749 | if (!data_buf) { |
750 | rc = 1; | |
751 | goto error_out; | |
752 | } | |
13a9930d WS |
753 | |
754 | memcpy(data_buf, &index, sizeof(index)); | |
755 | retval = ks7010_sdio_write(priv, WRITE_INDEX, data_buf, sizeof(index)); | |
cdf6ecc5 WS |
756 | if (retval) { |
757 | rc = 2; | |
758 | goto error_out; | |
759 | } | |
13a9930d WS |
760 | |
761 | retval = ks7010_sdio_write(priv, READ_INDEX, data_buf, sizeof(index)); | |
cdf6ecc5 WS |
762 | if (retval) { |
763 | rc = 3; | |
764 | goto error_out; | |
765 | } | |
766 | error_out: | |
767 | if (data_buf) | |
768 | kfree(data_buf); | |
13a9930d WS |
769 | return rc; |
770 | } | |
771 | ||
772 | #define ROM_BUFF_SIZE (64*1024) | |
feedcf1a | 773 | static int ks7010_sdio_data_compare(struct ks_wlan_private *priv, u32 address, |
13a9930d WS |
774 | unsigned char *data, unsigned int size) |
775 | { | |
cdf6ecc5 | 776 | int rc = 0; |
13a9930d WS |
777 | int retval; |
778 | unsigned char *read_buf; | |
779 | read_buf = NULL; | |
780 | read_buf = kmalloc(ROM_BUFF_SIZE, GFP_KERNEL); | |
cdf6ecc5 WS |
781 | if (!read_buf) { |
782 | rc = 1; | |
783 | goto error_out; | |
784 | } | |
13a9930d | 785 | retval = ks7010_sdio_read(priv, address, read_buf, size); |
cdf6ecc5 WS |
786 | if (retval) { |
787 | rc = 2; | |
788 | goto error_out; | |
789 | } | |
13a9930d WS |
790 | retval = memcmp(data, read_buf, size); |
791 | ||
cdf6ecc5 WS |
792 | if (retval) { |
793 | DPRINTK(0, "data compare error (%d) \n", retval); | |
794 | rc = 3; | |
795 | goto error_out; | |
13a9930d | 796 | } |
cdf6ecc5 WS |
797 | error_out: |
798 | if (read_buf) | |
799 | kfree(read_buf); | |
13a9930d WS |
800 | return rc; |
801 | } | |
cdf6ecc5 | 802 | |
13a9930d | 803 | #include <linux/firmware.h> |
cdf6ecc5 WS |
804 | static int ks79xx_upload_firmware(struct ks_wlan_private *priv, |
805 | struct ks_sdio_card *card) | |
13a9930d | 806 | { |
cdf6ecc5 WS |
807 | unsigned int size, offset, n = 0; |
808 | unsigned char *rom_buf; | |
809 | unsigned char rw_data = 0; | |
810 | int retval, rc = 0; | |
13a9930d WS |
811 | int length; |
812 | const struct firmware *fw_entry = NULL; | |
13a9930d WS |
813 | |
814 | rom_buf = NULL; | |
815 | ||
816 | /* buffer allocate */ | |
817 | rom_buf = kmalloc(ROM_BUFF_SIZE, GFP_KERNEL); | |
cdf6ecc5 WS |
818 | if (!rom_buf) { |
819 | rc = 3; | |
820 | goto error_out0; | |
821 | } | |
13a9930d WS |
822 | |
823 | sdio_claim_host(card->func); | |
824 | ||
825 | /* Firmware running ? */ | |
826 | retval = ks7010_sdio_read(priv, GCR_A, &rw_data, sizeof(rw_data)); | |
cdf6ecc5 WS |
827 | if (rw_data == GCR_A_RUN) { |
828 | DPRINTK(0, "MAC firmware running ...\n"); | |
13a9930d WS |
829 | rc = 0; |
830 | goto error_out0; | |
831 | } | |
832 | ||
cdf6ecc5 WS |
833 | if (request_firmware |
834 | (&fw_entry, priv->reg.rom_file, | |
835 | &priv->ks_wlan_hw.sdio_card->func->dev) != 0) { | |
836 | DPRINTK(1, "error request_firmware() file=%s\n", | |
837 | priv->reg.rom_file); | |
13a9930d WS |
838 | return 1; |
839 | } | |
cdf6ecc5 WS |
840 | DPRINTK(4, "success request_firmware() file=%s size=%zu\n", |
841 | priv->reg.rom_file, fw_entry->size); | |
13a9930d | 842 | length = fw_entry->size; |
13a9930d WS |
843 | |
844 | /* Load Program */ | |
845 | n = 0; | |
cdf6ecc5 WS |
846 | do { |
847 | if (length >= ROM_BUFF_SIZE) { | |
13a9930d WS |
848 | size = ROM_BUFF_SIZE; |
849 | length = length - ROM_BUFF_SIZE; | |
cdf6ecc5 WS |
850 | } else { |
851 | size = length; | |
852 | length = 0; | |
13a9930d | 853 | } |
cdf6ecc5 WS |
854 | DPRINTK(4, "size = %d\n", size); |
855 | if (size == 0) | |
856 | break; | |
857 | memcpy(rom_buf, fw_entry->data + n, size); | |
13a9930d WS |
858 | /* Update write index */ |
859 | offset = n; | |
cdf6ecc5 WS |
860 | retval = |
861 | ks7010_sdio_update_index(priv, | |
862 | KS7010_IRAM_ADDRESS + offset); | |
863 | if (retval) { | |
864 | rc = 6; | |
865 | goto error_out1; | |
866 | } | |
13a9930d WS |
867 | |
868 | /* Write data */ | |
869 | retval = ks7010_sdio_write(priv, DATA_WINDOW, rom_buf, size); | |
cdf6ecc5 WS |
870 | if (retval) { |
871 | rc = 8; | |
872 | goto error_out1; | |
873 | } | |
13a9930d WS |
874 | |
875 | /* compare */ | |
cdf6ecc5 WS |
876 | retval = |
877 | ks7010_sdio_data_compare(priv, DATA_WINDOW, rom_buf, size); | |
878 | if (retval) { | |
879 | rc = 9; | |
880 | goto error_out1; | |
881 | } | |
13a9930d WS |
882 | n += size; |
883 | ||
cdf6ecc5 | 884 | } while (size); |
13a9930d WS |
885 | |
886 | /* Remap request */ | |
887 | rw_data = GCR_A_REMAP; | |
888 | retval = ks7010_sdio_write(priv, GCR_A, &rw_data, sizeof(rw_data)); | |
cdf6ecc5 | 889 | if (retval) { |
13a9930d WS |
890 | rc = 11; |
891 | goto error_out1; | |
892 | } | |
cdf6ecc5 | 893 | DPRINTK(4, " REMAP Request : GCR_A=%02X\n", rw_data); |
13a9930d WS |
894 | |
895 | /* Firmware running check */ | |
896 | for (n = 0; n < 50; ++n) { | |
cdf6ecc5 WS |
897 | mdelay(10); /* wait_ms(10); */ |
898 | retval = | |
899 | ks7010_sdio_read(priv, GCR_A, &rw_data, sizeof(rw_data)); | |
900 | if (retval) { | |
901 | rc = 11; | |
902 | goto error_out1; | |
903 | } | |
904 | if (rw_data == GCR_A_RUN) | |
905 | break; | |
13a9930d | 906 | } |
cdf6ecc5 | 907 | DPRINTK(4, "firmware wakeup (%d)!!!!\n", n); |
13a9930d WS |
908 | if ((50) <= n) { |
909 | DPRINTK(1, "firmware can't start\n"); | |
cdf6ecc5 | 910 | rc = 12; |
13a9930d WS |
911 | goto error_out1; |
912 | } | |
913 | ||
914 | rc = 0; | |
915 | ||
13a9930d WS |
916 | error_out1: |
917 | release_firmware(fw_entry); | |
13a9930d WS |
918 | error_out0: |
919 | sdio_release_host(card->func); | |
cdf6ecc5 | 920 | if (rom_buf) |
13a9930d WS |
921 | kfree(rom_buf); |
922 | return rc; | |
923 | } | |
924 | ||
e8593a8a | 925 | static void ks7010_card_init(struct ks_wlan_private *priv) |
13a9930d | 926 | { |
cdf6ecc5 | 927 | DPRINTK(5, "\ncard_init_task()\n"); |
13a9930d WS |
928 | |
929 | /* init_waitqueue_head(&priv->confirm_wait); */ | |
930 | init_completion(&priv->confirm_wait); | |
931 | ||
cdf6ecc5 | 932 | DPRINTK(5, "init_completion()\n"); |
13a9930d WS |
933 | |
934 | /* get mac address & firmware version */ | |
935 | hostif_sme_enqueue(priv, SME_START); | |
936 | ||
cdf6ecc5 | 937 | DPRINTK(5, "hostif_sme_enqueu()\n"); |
13a9930d | 938 | |
cdf6ecc5 WS |
939 | if (!wait_for_completion_interruptible_timeout |
940 | (&priv->confirm_wait, 5 * HZ)) { | |
941 | DPRINTK(1, "wait time out!! SME_START\n"); | |
13a9930d WS |
942 | } |
943 | ||
cdf6ecc5 | 944 | if (priv->mac_address_valid && priv->version_size) { |
13a9930d WS |
945 | priv->dev_state = DEVICE_STATE_PREINIT; |
946 | } | |
947 | ||
948 | hostif_sme_enqueue(priv, SME_GET_EEPROM_CKSUM); | |
949 | ||
950 | /* load initial wireless parameter */ | |
951 | hostif_sme_enqueue(priv, SME_STOP_REQUEST); | |
952 | ||
953 | hostif_sme_enqueue(priv, SME_RTS_THRESHOLD_REQUEST); | |
954 | hostif_sme_enqueue(priv, SME_FRAGMENTATION_THRESHOLD_REQUEST); | |
955 | ||
956 | hostif_sme_enqueue(priv, SME_WEP_INDEX_REQUEST); | |
957 | hostif_sme_enqueue(priv, SME_WEP_KEY1_REQUEST); | |
958 | hostif_sme_enqueue(priv, SME_WEP_KEY2_REQUEST); | |
959 | hostif_sme_enqueue(priv, SME_WEP_KEY3_REQUEST); | |
960 | hostif_sme_enqueue(priv, SME_WEP_KEY4_REQUEST); | |
961 | ||
962 | hostif_sme_enqueue(priv, SME_WEP_FLAG_REQUEST); | |
963 | hostif_sme_enqueue(priv, SME_RSN_ENABLED_REQUEST); | |
964 | hostif_sme_enqueue(priv, SME_MODE_SET_REQUEST); | |
965 | hostif_sme_enqueue(priv, SME_START_REQUEST); | |
966 | ||
cdf6ecc5 WS |
967 | if (!wait_for_completion_interruptible_timeout |
968 | (&priv->confirm_wait, 5 * HZ)) { | |
969 | DPRINTK(1, "wait time out!! wireless parameter set\n"); | |
13a9930d WS |
970 | } |
971 | ||
cdf6ecc5 | 972 | if (priv->dev_state >= DEVICE_STATE_PREINIT) { |
13a9930d WS |
973 | DPRINTK(1, "DEVICE READY!!\n"); |
974 | priv->dev_state = DEVICE_STATE_READY; | |
cdf6ecc5 WS |
975 | reg_net = register_netdev(priv->net_dev); |
976 | DPRINTK(3, "register_netdev=%d\n", reg_net); | |
977 | } else { | |
978 | DPRINTK(1, "dev_state=%d\n", priv->dev_state); | |
13a9930d WS |
979 | } |
980 | } | |
981 | ||
982 | static struct sdio_driver ks7010_sdio_driver = { | |
cdf6ecc5 WS |
983 | .name = "ks7910_sdio", |
984 | .id_table = if_sdio_ids, | |
985 | .probe = ks7910_sdio_probe, | |
986 | .remove = ks7910_sdio_remove, | |
13a9930d WS |
987 | }; |
988 | ||
13a9930d WS |
989 | extern int ks_wlan_net_start(struct net_device *dev); |
990 | extern int ks_wlan_net_stop(struct net_device *dev); | |
991 | ||
cdf6ecc5 WS |
992 | static int ks7910_sdio_probe(struct sdio_func *func, |
993 | const struct sdio_device_id *device) | |
13a9930d | 994 | { |
feedcf1a | 995 | struct ks_wlan_private *priv; |
13a9930d WS |
996 | struct ks_sdio_card *card; |
997 | struct net_device *netdev; | |
998 | unsigned char rw_data; | |
cdf6ecc5 | 999 | int i = 0, ret; |
13a9930d WS |
1000 | |
1001 | DPRINTK(5, "ks7910_sdio_probe()\n"); | |
1002 | ||
1003 | priv = NULL; | |
cdf6ecc5 | 1004 | netdev = NULL; |
13a9930d WS |
1005 | |
1006 | /* initilize ks_sdio_card */ | |
1007 | card = kzalloc(sizeof(struct ks_sdio_card), GFP_KERNEL); | |
1008 | if (!card) | |
1009 | return -ENOMEM; | |
1010 | ||
cdf6ecc5 | 1011 | card->func = func; |
13a9930d WS |
1012 | card->model = 0x10; |
1013 | spin_lock_init(&card->lock); | |
1014 | ||
1015 | /* select model */ | |
cdf6ecc5 | 1016 | for (i = 0; i < ARRAY_SIZE(ks_sdio_models); i++) { |
13a9930d WS |
1017 | if (card->model == ks_sdio_models[i].model) |
1018 | break; | |
1019 | } | |
1020 | ||
1021 | if (i == ARRAY_SIZE(ks_sdio_models)) { | |
1022 | DPRINTK(5, "unkown card model 0x%x\n", card->model); | |
1023 | goto error; | |
1024 | } | |
1025 | ||
1026 | card->firmware = ks_sdio_models[i].firmware; | |
1027 | ||
13a9930d WS |
1028 | /*** Initialize SDIO ***/ |
1029 | sdio_claim_host(func); | |
1030 | ||
1031 | /* bus setting */ | |
1032 | /* Issue config request to override clock rate */ | |
1033 | ||
1034 | /* function blocksize set */ | |
1035 | ret = sdio_set_block_size(func, KS7010_IO_BLOCK_SIZE); | |
cdf6ecc5 WS |
1036 | DPRINTK(5, "multi_block=%d sdio_set_block_size()=%d %d\n", |
1037 | func->card->cccr.multi_block, func->cur_blksize, ret); | |
13a9930d WS |
1038 | |
1039 | /* Allocate the slot current */ | |
1040 | ||
1041 | /* function enable */ | |
1042 | ret = sdio_enable_func(func); | |
1043 | DPRINTK(5, "sdio_enable_func() %d\n", ret); | |
1044 | if (ret) | |
1045 | goto error_free_card; | |
1046 | ||
1047 | /* interrupt disable */ | |
cdf6ecc5 | 1048 | sdio_writeb(func, 0, INT_ENABLE, &ret); |
13a9930d WS |
1049 | if (ret) |
1050 | goto error_free_card; | |
cdf6ecc5 | 1051 | sdio_writeb(func, 0xff, INT_PENDING, &ret); |
13a9930d WS |
1052 | if (ret) |
1053 | goto error_disable_func; | |
1054 | ||
1055 | /* setup interrupt handler */ | |
1056 | ret = sdio_claim_irq(func, ks_sdio_interrupt); | |
1057 | if (ret) | |
1058 | goto error_disable_func; | |
1059 | ||
1060 | sdio_release_host(func); | |
1061 | ||
1062 | sdio_set_drvdata(func, card); | |
1063 | ||
1064 | DPRINTK(5, "class = 0x%X, vendor = 0x%X, " | |
cdf6ecc5 | 1065 | "device = 0x%X\n", func->class, func->vendor, func->device); |
13a9930d WS |
1066 | |
1067 | /* private memory allocate */ | |
1068 | netdev = alloc_etherdev(sizeof(*priv)); | |
1069 | if (netdev == NULL) { | |
cdf6ecc5 | 1070 | printk(KERN_ERR "ks79xx : Unable to alloc new net device\n"); |
13a9930d WS |
1071 | goto error_release_irq; |
1072 | } | |
1073 | if (dev_alloc_name(netdev, netdev->name) < 0) { | |
cdf6ecc5 | 1074 | printk(KERN_ERR "ks79xx : Couldn't get name!\n"); |
13a9930d WS |
1075 | goto error_free_netdev; |
1076 | } | |
1077 | ||
1078 | priv = netdev_priv(netdev); | |
1079 | ||
1080 | card->priv = priv; | |
1081 | SET_NETDEV_DEV(netdev, &card->func->dev); /* for create sysfs symlinks */ | |
1082 | ||
1083 | /* private memory initialize */ | |
1084 | priv->ks_wlan_hw.sdio_card = card; | |
1085 | init_completion(&priv->ks_wlan_hw.ks7010_sdio_wait); | |
1086 | priv->ks_wlan_hw.read_buf = NULL; | |
1087 | priv->ks_wlan_hw.read_buf = kmalloc(RX_DATA_SIZE, GFP_KERNEL); | |
cdf6ecc5 | 1088 | if (!priv->ks_wlan_hw.read_buf) { |
13a9930d WS |
1089 | goto error_free_netdev; |
1090 | } | |
1091 | priv->dev_state = DEVICE_STATE_PREBOOT; | |
1092 | priv->net_dev = netdev; | |
1093 | priv->firmware_version[0] = '\0'; | |
1094 | priv->version_size = 0; | |
1095 | priv->last_doze = jiffies; /* set current jiffies */ | |
1096 | priv->last_wakeup = jiffies; | |
1097 | memset(&priv->nstats, 0, sizeof(priv->nstats)); | |
1098 | memset(&priv->wstats, 0, sizeof(priv->wstats)); | |
1099 | ||
1100 | /* sleep mode */ | |
cdf6ecc5 WS |
1101 | atomic_set(&priv->sleepstatus.doze_request, 0); |
1102 | atomic_set(&priv->sleepstatus.wakeup_request, 0); | |
1103 | atomic_set(&priv->sleepstatus.wakeup_request, 0); | |
13a9930d WS |
1104 | |
1105 | trx_device_init(priv); | |
1106 | hostif_init(priv); | |
cdf6ecc5 | 1107 | ks_wlan_net_start(netdev); |
13a9930d WS |
1108 | |
1109 | /* Read config file */ | |
1110 | ret = ks_wlan_read_config_file(priv); | |
1111 | if (ret) { | |
cdf6ecc5 WS |
1112 | printk(KERN_ERR |
1113 | "ks79xx: read configuration file failed !! retern code = %d\n", | |
1114 | ret); | |
13a9930d WS |
1115 | goto error_free_read_buf; |
1116 | } | |
1117 | ||
1118 | /* Upload firmware */ | |
cdf6ecc5 WS |
1119 | ret = ks79xx_upload_firmware(priv, card); /* firmware load */ |
1120 | if (ret) { | |
1121 | printk(KERN_ERR | |
1122 | "ks79xx: firmware load failed !! retern code = %d\n", | |
1123 | ret); | |
13a9930d WS |
1124 | goto error_free_read_buf; |
1125 | } | |
1126 | ||
1127 | /* interrupt setting */ | |
1128 | /* clear Interrupt status write (ARMtoSD_InterruptPending FN1:00_0024) */ | |
1129 | rw_data = 0xff; | |
1130 | sdio_claim_host(func); | |
1131 | ret = ks7010_sdio_write(priv, INT_PENDING, &rw_data, sizeof(rw_data)); | |
1132 | sdio_release_host(func); | |
cdf6ecc5 | 1133 | if (ret) { |
13a9930d WS |
1134 | DPRINTK(1, " error : INT_PENDING=%02X\n", rw_data); |
1135 | } | |
1136 | DPRINTK(4, " clear Interrupt : INT_PENDING=%02X\n", rw_data); | |
1137 | ||
13a9930d | 1138 | /* enable ks7010sdio interrupt (INT_GCR_B|INT_READ_STATUS|INT_WRITE_STATUS) */ |
cdf6ecc5 | 1139 | rw_data = (INT_GCR_B | INT_READ_STATUS | INT_WRITE_STATUS); |
13a9930d WS |
1140 | sdio_claim_host(func); |
1141 | ret = ks7010_sdio_write(priv, INT_ENABLE, &rw_data, sizeof(rw_data)); | |
1142 | sdio_release_host(func); | |
cdf6ecc5 | 1143 | if (ret) { |
13a9930d WS |
1144 | DPRINTK(1, " error : INT_ENABLE=%02X\n", rw_data); |
1145 | } | |
1146 | DPRINTK(4, " enable Interrupt : INT_ENABLE=%02X\n", rw_data); | |
1147 | priv->dev_state = DEVICE_STATE_BOOT; | |
1148 | ||
1149 | priv->ks_wlan_hw.ks7010sdio_wq = create_workqueue("ks7010sdio_wq"); | |
cdf6ecc5 | 1150 | if (!priv->ks_wlan_hw.ks7010sdio_wq) { |
13a9930d WS |
1151 | DPRINTK(1, "create_workqueue failed !!\n"); |
1152 | goto error_free_read_buf; | |
1153 | } | |
1154 | ||
13a9930d | 1155 | INIT_DELAYED_WORK(&priv->ks_wlan_hw.rw_wq, ks7010_rw_function); |
e8593a8a | 1156 | ks7010_card_init(priv); |
13a9930d WS |
1157 | |
1158 | return 0; | |
1159 | ||
cdf6ecc5 | 1160 | error_free_read_buf: |
13a9930d WS |
1161 | kfree(priv->ks_wlan_hw.read_buf); |
1162 | priv->ks_wlan_hw.read_buf = NULL; | |
cdf6ecc5 | 1163 | error_free_netdev: |
13a9930d WS |
1164 | free_netdev(priv->net_dev); |
1165 | card->priv = NULL; | |
cdf6ecc5 | 1166 | error_release_irq: |
13a9930d WS |
1167 | sdio_claim_host(func); |
1168 | sdio_release_irq(func); | |
cdf6ecc5 | 1169 | error_disable_func: |
13a9930d | 1170 | sdio_disable_func(func); |
cdf6ecc5 | 1171 | error_free_card: |
13a9930d WS |
1172 | sdio_release_host(func); |
1173 | sdio_set_drvdata(func, NULL); | |
1174 | kfree(card); | |
cdf6ecc5 | 1175 | error: |
13a9930d WS |
1176 | return -ENODEV; |
1177 | } | |
1178 | ||
1179 | static void ks7910_sdio_remove(struct sdio_func *func) | |
1180 | { | |
1181 | int ret; | |
1182 | struct ks_sdio_card *card; | |
1183 | struct ks_wlan_private *priv; | |
1184 | struct net_device *netdev; | |
1185 | DPRINTK(1, "ks7910_sdio_remove()\n"); | |
1186 | ||
1187 | card = sdio_get_drvdata(func); | |
1188 | ||
cdf6ecc5 | 1189 | if (card == NULL) |
13a9930d WS |
1190 | return; |
1191 | ||
1192 | DPRINTK(1, "priv = card->priv\n"); | |
1193 | priv = card->priv; | |
1194 | netdev = priv->net_dev; | |
cdf6ecc5 | 1195 | if (priv) { |
13a9930d WS |
1196 | ks_wlan_net_stop(netdev); |
1197 | DPRINTK(1, "ks_wlan_net_stop\n"); | |
1198 | ||
1199 | /* interrupt disable */ | |
1200 | sdio_claim_host(func); | |
cdf6ecc5 WS |
1201 | sdio_writeb(func, 0, INT_ENABLE, &ret); |
1202 | sdio_writeb(func, 0xff, INT_PENDING, &ret); | |
13a9930d WS |
1203 | sdio_release_host(func); |
1204 | DPRINTK(1, "interrupt disable\n"); | |
1205 | ||
1206 | /* send stop request to MAC */ | |
1207 | { | |
1208 | struct hostif_stop_request_t *pp; | |
cdf6ecc5 WS |
1209 | pp = (struct hostif_stop_request_t *) |
1210 | kzalloc(hif_align_size(sizeof(*pp)), GFP_KERNEL); | |
1211 | if (pp == NULL) { | |
1212 | DPRINTK(3, "allocate memory failed..\n"); | |
1213 | return; /* to do goto ni suru */ | |
13a9930d | 1214 | } |
cdf6ecc5 WS |
1215 | pp->header.size = |
1216 | cpu_to_le16((uint16_t) | |
1217 | (sizeof(*pp) - | |
1218 | sizeof(pp->header.size))); | |
1219 | pp->header.event = cpu_to_le16((uint16_t) HIF_STOP_REQ); | |
13a9930d WS |
1220 | |
1221 | sdio_claim_host(func); | |
cdf6ecc5 WS |
1222 | write_to_device(priv, (unsigned char *)pp, |
1223 | hif_align_size(sizeof(*pp))); | |
13a9930d WS |
1224 | sdio_release_host(func); |
1225 | kfree(pp); | |
1226 | } | |
1227 | DPRINTK(1, "STOP Req\n"); | |
1228 | ||
cdf6ecc5 | 1229 | if (priv->ks_wlan_hw.ks7010sdio_wq) { |
13a9930d WS |
1230 | flush_workqueue(priv->ks_wlan_hw.ks7010sdio_wq); |
1231 | destroy_workqueue(priv->ks_wlan_hw.ks7010sdio_wq); | |
1232 | } | |
cdf6ecc5 WS |
1233 | DPRINTK(1, |
1234 | "destroy_workqueue(priv->ks_wlan_hw.ks7010sdio_wq);\n"); | |
13a9930d | 1235 | |
13a9930d WS |
1236 | hostif_exit(priv); |
1237 | DPRINTK(1, "hostif_exit\n"); | |
1238 | ||
cdf6ecc5 | 1239 | if (!reg_net) |
13a9930d WS |
1240 | unregister_netdev(netdev); |
1241 | DPRINTK(1, "unregister_netdev\n"); | |
1242 | ||
1243 | trx_device_exit(priv); | |
cdf6ecc5 | 1244 | if (priv->ks_wlan_hw.read_buf) { |
13a9930d WS |
1245 | kfree(priv->ks_wlan_hw.read_buf); |
1246 | } | |
1247 | free_netdev(priv->net_dev); | |
1248 | card->priv = NULL; | |
1249 | } | |
1250 | ||
1251 | sdio_claim_host(func); | |
1252 | sdio_release_irq(func); | |
1253 | DPRINTK(1, "sdio_release_irq()\n"); | |
1254 | sdio_disable_func(func); | |
1255 | DPRINTK(1, "sdio_disable_func()\n"); | |
1256 | sdio_release_host(func); | |
1257 | ||
1258 | sdio_set_drvdata(func, NULL); | |
1259 | ||
1260 | kfree(card); | |
1261 | DPRINTK(1, "kfree()\n"); | |
1262 | ||
cdf6ecc5 | 1263 | DPRINTK(5, " Bye !!\n"); |
13a9930d WS |
1264 | return; |
1265 | } | |
1266 | ||
cdf6ecc5 | 1267 | static int __init ks7010_sdio_init(void) |
13a9930d WS |
1268 | { |
1269 | int status; | |
1270 | ||
1271 | /* register with bus driver core */ | |
1272 | status = sdio_register_driver(&ks7010_sdio_driver); | |
cdf6ecc5 WS |
1273 | if (status != 0) { |
1274 | DPRINTK(1, | |
1275 | "ks79xx_sdio : failed to register with bus driver, %d\n", | |
1276 | status); | |
13a9930d WS |
1277 | } |
1278 | return status; | |
1279 | } | |
1280 | ||
cdf6ecc5 | 1281 | static void __exit ks7010_sdio_exit(void) |
13a9930d | 1282 | { |
cdf6ecc5 | 1283 | DPRINTK(5, " \n"); |
13a9930d WS |
1284 | sdio_unregister_driver(&ks7010_sdio_driver); |
1285 | return; | |
1286 | } | |
1287 | ||
1288 | module_init(ks7010_sdio_init); | |
1289 | module_exit(ks7010_sdio_exit); | |
1290 | ||
1291 | MODULE_AUTHOR("Qi-Hardware based on KeyStream driver"); | |
1292 | MODULE_DESCRIPTION("Driver for KeyStream, KS7010 based SDIO cards. "); | |
1293 | #ifdef MODULE_LICENSE | |
1294 | MODULE_LICENSE("GPL"); | |
1295 | #endif | |
1296 | MODULE_SUPPORTED_DEVICE("KS7910"); |