Commit | Line | Data |
---|---|---|
30295c89 VM |
1 | //------------------------------------------------------------------------------ |
2 | // Copyright (c) 2004-2010 Atheros Communications Inc. | |
3 | // All rights reserved. | |
4 | // | |
5 | // | |
6 | // | |
7 | // Permission to use, copy, modify, and/or distribute this software for any | |
8 | // purpose with or without fee is hereby granted, provided that the above | |
9 | // copyright notice and this permission notice appear in all copies. | |
10 | // | |
11 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
12 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
13 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
14 | // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
15 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
16 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
17 | // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
18 | // | |
19 | // | |
20 | // | |
21 | // Author(s): ="Atheros" | |
22 | //------------------------------------------------------------------------------ | |
23 | #include "ar6000_drv.h" | |
24 | #include "htc.h" | |
25 | #include <linux/vmalloc.h> | |
26 | #include <linux/fs.h> | |
27 | ||
30295c89 VM |
28 | #ifdef CONFIG_HAS_EARLYSUSPEND |
29 | #include <linux/earlysuspend.h> | |
30 | #endif | |
31 | ||
1071a134 | 32 | bool enable_mmc_host_detect_change = false; |
30295c89 VM |
33 | static void ar6000_enable_mmchost_detect_change(int enable); |
34 | ||
35 | ||
36 | char fwpath[256] = "/system/wifi"; | |
37 | int wowledon; | |
38 | unsigned int enablelogcat; | |
39 | ||
40 | extern int bmienable; | |
41 | extern struct net_device *ar6000_devices[]; | |
42 | extern char ifname[]; | |
43 | ||
30295c89 VM |
44 | const char def_ifname[] = "wlan0"; |
45 | module_param_string(fwpath, fwpath, sizeof(fwpath), 0644); | |
46 | module_param(enablelogcat, uint, 0644); | |
47 | module_param(wowledon, int, 0644); | |
48 | ||
49 | #ifdef CONFIG_HAS_EARLYSUSPEND | |
50 | static int screen_is_off; | |
51 | static struct early_suspend ar6k_early_suspend; | |
52 | #endif | |
53 | ||
1f4c34bd | 54 | static int (*ar6000_avail_ev_p)(void *, void *); |
30295c89 VM |
55 | |
56 | #if defined(CONFIG_ANDROID_LOGGER) && (!defined(CONFIG_MMC_MSM)) | |
57 | int logger_write(const enum logidx index, | |
58 | const unsigned char prio, | |
59 | const char __kernel * const tag, | |
60 | const char __kernel * const fmt, | |
61 | ...) | |
62 | { | |
63 | int ret = 0; | |
64 | va_list vargs; | |
65 | struct file *filp = (struct file *)-ENOENT; | |
66 | mm_segment_t oldfs; | |
67 | struct iovec vec[3]; | |
68 | int tag_bytes = strlen(tag) + 1, msg_bytes; | |
69 | char *msg; | |
70 | va_start(vargs, fmt); | |
71 | msg = kvasprintf(GFP_ATOMIC, fmt, vargs); | |
72 | va_end(vargs); | |
73 | if (!msg) | |
74 | return -ENOMEM; | |
75 | if (in_interrupt()) { | |
76 | /* we have no choice since aio_write may be blocked */ | |
77 | printk(KERN_ALERT "%s", msg); | |
78 | goto out_free_message; | |
79 | } | |
80 | msg_bytes = strlen(msg) + 1; | |
81 | if (msg_bytes <= 1) /* empty message? */ | |
82 | goto out_free_message; /* don't bother, then */ | |
83 | if ((msg_bytes + tag_bytes + 1) > 2048) { | |
84 | ret = -E2BIG; | |
85 | goto out_free_message; | |
86 | } | |
87 | ||
88 | vec[0].iov_base = (unsigned char *) &prio; | |
89 | vec[0].iov_len = 1; | |
90 | vec[1].iov_base = (void *) tag; | |
91 | vec[1].iov_len = strlen(tag) + 1; | |
92 | vec[2].iov_base = (void *) msg; | |
93 | vec[2].iov_len = strlen(msg) + 1; | |
94 | ||
95 | oldfs = get_fs(); | |
96 | set_fs(KERNEL_DS); | |
97 | do { | |
98 | filp = filp_open("/dev/log/main", O_WRONLY, S_IRUSR); | |
99 | if (IS_ERR(filp) || !filp->f_op) { | |
100 | AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("%s: filp_open /dev/log/main error\n", __FUNCTION__)); | |
101 | ret = -ENOENT; | |
102 | break; | |
103 | } | |
104 | ||
105 | if (filp->f_op->aio_write) { | |
106 | int nr_segs = sizeof(vec) / sizeof(vec[0]); | |
107 | int len = vec[0].iov_len + vec[1].iov_len + vec[2].iov_len; | |
108 | struct kiocb kiocb; | |
109 | init_sync_kiocb(&kiocb, filp); | |
110 | kiocb.ki_pos = 0; | |
111 | kiocb.ki_left = len; | |
112 | kiocb.ki_nbytes = len; | |
113 | ret = filp->f_op->aio_write(&kiocb, vec, nr_segs, kiocb.ki_pos); | |
114 | } | |
115 | ||
116 | } while (0); | |
117 | ||
118 | if (!IS_ERR(filp)) { | |
119 | filp_close(filp, NULL); | |
120 | } | |
121 | set_fs(oldfs); | |
122 | out_free_message: | |
123 | if (msg) { | |
124 | kfree(msg); | |
125 | } | |
126 | return ret; | |
127 | } | |
128 | #endif | |
129 | ||
130 | int android_logger_lv(void *module, int mask) | |
131 | { | |
132 | switch (mask) { | |
133 | case ATH_DEBUG_ERR: | |
134 | return 6; | |
135 | case ATH_DEBUG_INFO: | |
136 | return 4; | |
137 | case ATH_DEBUG_WARN: | |
138 | return 5; | |
139 | case ATH_DEBUG_TRC: | |
140 | return 3; | |
141 | default: | |
142 | #ifdef DEBUG | |
143 | if (!module) { | |
144 | return 3; | |
145 | } else if (module == &GET_ATH_MODULE_DEBUG_VAR_NAME(driver)) { | |
146 | return (mask <=ATH_DEBUG_MAKE_MODULE_MASK(3)) ? 3 : 2; | |
147 | } else if (module == &GET_ATH_MODULE_DEBUG_VAR_NAME(htc)) { | |
148 | return 2; | |
149 | } else { | |
150 | return 3; | |
151 | } | |
152 | #else | |
153 | return 3; /* DEBUG */ | |
154 | #endif | |
155 | } | |
156 | } | |
157 | ||
4c42080f | 158 | static int android_readwrite_file(const char *filename, char *rbuf, const char *wbuf, size_t length) |
30295c89 VM |
159 | { |
160 | int ret = 0; | |
161 | struct file *filp = (struct file *)-ENOENT; | |
162 | mm_segment_t oldfs; | |
163 | oldfs = get_fs(); | |
164 | set_fs(KERNEL_DS); | |
165 | do { | |
166 | int mode = (wbuf) ? O_RDWR : O_RDONLY; | |
167 | filp = filp_open(filename, mode, S_IRUSR); | |
168 | if (IS_ERR(filp) || !filp->f_op) { | |
169 | AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("%s: file %s filp_open error\n", __FUNCTION__, filename)); | |
170 | ret = -ENOENT; | |
171 | break; | |
172 | } | |
173 | ||
174 | if (length==0) { | |
175 | /* Read the length of the file only */ | |
176 | struct inode *inode; | |
177 | ||
178 | inode = GET_INODE_FROM_FILEP(filp); | |
179 | if (!inode) { | |
180 | AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("%s: Get inode from %s failed\n", __FUNCTION__, filename)); | |
181 | ret = -ENOENT; | |
182 | break; | |
183 | } | |
184 | ret = i_size_read(inode->i_mapping->host); | |
185 | break; | |
186 | } | |
187 | ||
188 | if (wbuf) { | |
189 | if ( (ret=filp->f_op->write(filp, wbuf, length, &filp->f_pos)) < 0) { | |
190 | AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("%s: Write %u bytes to file %s error %d\n", __FUNCTION__, | |
191 | length, filename, ret)); | |
192 | break; | |
193 | } | |
194 | } else { | |
195 | if ( (ret=filp->f_op->read(filp, rbuf, length, &filp->f_pos)) < 0) { | |
196 | AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("%s: Read %u bytes from file %s error %d\n", __FUNCTION__, | |
197 | length, filename, ret)); | |
198 | break; | |
199 | } | |
200 | } | |
201 | } while (0); | |
202 | ||
203 | if (!IS_ERR(filp)) { | |
204 | filp_close(filp, NULL); | |
205 | } | |
206 | set_fs(oldfs); | |
207 | ||
208 | return ret; | |
209 | } | |
210 | ||
211 | int android_request_firmware(const struct firmware **firmware_p, const char *name, | |
212 | struct device *device) | |
213 | { | |
214 | int ret = 0; | |
215 | struct firmware *firmware; | |
216 | char filename[256]; | |
217 | const char *raw_filename = name; | |
218 | *firmware_p = firmware = kzalloc(sizeof(*firmware), GFP_KERNEL); | |
219 | if (!firmware) | |
220 | return -ENOMEM; | |
221 | sprintf(filename, "%s/%s", fwpath, raw_filename); | |
222 | do { | |
223 | size_t length, bufsize, bmisize; | |
224 | ||
225 | if ( (ret=android_readwrite_file(filename, NULL, NULL, 0)) < 0) { | |
226 | break; | |
227 | } else { | |
228 | length = ret; | |
229 | } | |
230 | ||
231 | bufsize = ALIGN(length, PAGE_SIZE); | |
232 | bmisize = A_ROUND_UP(length, 4); | |
233 | bufsize = max(bmisize, bufsize); | |
234 | firmware->data = vmalloc(bufsize); | |
235 | firmware->size = length; | |
236 | if (!firmware->data) { | |
237 | AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("%s: Cannot allocate buffer for firmware\n", __FUNCTION__)); | |
238 | ret = -ENOMEM; | |
239 | break; | |
240 | } | |
241 | ||
242 | if ( (ret=android_readwrite_file(filename, (char*)firmware->data, NULL, length)) != length) { | |
243 | AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("%s: file read error, ret %d request %d\n", __FUNCTION__, ret, length)); | |
244 | ret = -1; | |
245 | break; | |
246 | } | |
247 | ||
248 | } while (0); | |
249 | ||
250 | if (ret<0) { | |
251 | if (firmware) { | |
252 | if (firmware->data) | |
253 | vfree(firmware->data); | |
254 | kfree(firmware); | |
255 | } | |
256 | *firmware_p = NULL; | |
257 | } else { | |
258 | ret = 0; | |
259 | } | |
260 | return ret; | |
261 | } | |
262 | ||
263 | void android_release_firmware(const struct firmware *firmware) | |
264 | { | |
265 | if (firmware) { | |
266 | if (firmware->data) | |
267 | vfree(firmware->data); | |
268 | kfree(firmware); | |
269 | } | |
270 | } | |
271 | ||
1f4c34bd | 272 | static int ar6000_android_avail_ev(void *context, void *hif_handle) |
30295c89 | 273 | { |
1f4c34bd | 274 | int ret; |
30295c89 VM |
275 | ar6000_enable_mmchost_detect_change(0); |
276 | ret = ar6000_avail_ev_p(context, hif_handle); | |
30295c89 VM |
277 | return ret; |
278 | } | |
279 | ||
280 | /* Useful for qualcom platform to detect our wlan card for mmc stack */ | |
281 | static void ar6000_enable_mmchost_detect_change(int enable) | |
282 | { | |
283 | #ifdef CONFIG_MMC_MSM | |
284 | #define MMC_MSM_DEV "msm_sdcc.1" | |
285 | char buf[3]; | |
286 | int length; | |
287 | ||
288 | if (!enable_mmc_host_detect_change) { | |
289 | return; | |
290 | } | |
291 | length = snprintf(buf, sizeof(buf), "%d\n", enable ? 1 : 0); | |
292 | if (android_readwrite_file("/sys/devices/platform/" MMC_MSM_DEV "/detect_change", | |
293 | NULL, buf, length) < 0) { | |
294 | /* fall back to polling */ | |
295 | android_readwrite_file("/sys/devices/platform/" MMC_MSM_DEV "/polling", NULL, buf, length); | |
296 | } | |
297 | #endif | |
298 | } | |
299 | ||
300 | #ifdef CONFIG_HAS_EARLYSUSPEND | |
301 | static void android_early_suspend(struct early_suspend *h) | |
302 | { | |
303 | screen_is_off = 1; | |
304 | } | |
305 | ||
306 | static void android_late_resume(struct early_suspend *h) | |
307 | { | |
308 | screen_is_off = 0; | |
309 | } | |
310 | #endif | |
311 | ||
312 | void android_module_init(OSDRV_CALLBACKS *osdrvCallbacks) | |
313 | { | |
314 | bmienable = 1; | |
315 | if (ifname[0] == '\0') | |
316 | strcpy(ifname, def_ifname); | |
30295c89 VM |
317 | #ifdef CONFIG_HAS_EARLYSUSPEND |
318 | ar6k_early_suspend.suspend = android_early_suspend; | |
319 | ar6k_early_suspend.resume = android_late_resume; | |
320 | ar6k_early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN; | |
321 | register_early_suspend(&ar6k_early_suspend); | |
322 | #endif | |
323 | ||
324 | ar6000_avail_ev_p = osdrvCallbacks->deviceInsertedHandler; | |
325 | osdrvCallbacks->deviceInsertedHandler = ar6000_android_avail_ev; | |
326 | ||
327 | ar6000_enable_mmchost_detect_change(1); | |
328 | } | |
329 | ||
330 | void android_module_exit(void) | |
331 | { | |
332 | #ifdef CONFIG_HAS_EARLYSUSPEND | |
333 | unregister_early_suspend(&ar6k_early_suspend); | |
30295c89 VM |
334 | #endif |
335 | ar6000_enable_mmchost_detect_change(1); | |
336 | } | |
337 | ||
338 | #ifdef CONFIG_PM | |
1071a134 | 339 | void android_ar6k_check_wow_status(AR_SOFTC_T *ar, struct sk_buff *skb, bool isEvent) |
30295c89 VM |
340 | { |
341 | if ( | |
342 | #ifdef CONFIG_HAS_EARLYSUSPEND | |
343 | screen_is_off && | |
344 | #endif | |
345 | skb && ar->arConnected) { | |
1071a134 | 346 | bool needWake = false; |
30295c89 | 347 | if (isEvent) { |
4853ac05 JP |
348 | if (A_NETBUF_LEN(skb) >= sizeof(u16)) { |
349 | u16 cmd = *(const u16 *)A_NETBUF_DATA(skb); | |
30295c89 VM |
350 | switch (cmd) { |
351 | case WMI_CONNECT_EVENTID: | |
352 | case WMI_DISCONNECT_EVENTID: | |
1071a134 | 353 | needWake = true; |
30295c89 VM |
354 | break; |
355 | default: | |
356 | /* dont wake lock the system for other event */ | |
357 | break; | |
358 | } | |
359 | } | |
360 | } else if (A_NETBUF_LEN(skb) >= sizeof(ATH_MAC_HDR)) { | |
361 | ATH_MAC_HDR *datap = (ATH_MAC_HDR *)A_NETBUF_DATA(skb); | |
362 | if (!IEEE80211_IS_MULTICAST(datap->dstMac)) { | |
363 | switch (A_BE2CPU16(datap->typeOrLen)) { | |
364 | case 0x0800: /* IP */ | |
365 | case 0x888e: /* EAPOL */ | |
366 | case 0x88c7: /* RSN_PREAUTH */ | |
367 | case 0x88b4: /* WAPI */ | |
1071a134 | 368 | needWake = true; |
30295c89 VM |
369 | break; |
370 | case 0x0806: /* ARP is not important to hold wake lock */ | |
371 | default: | |
372 | break; | |
373 | } | |
374 | } | |
375 | } | |
376 | if (needWake) { | |
377 | /* keep host wake up if there is any event and packate comming in*/ | |
30295c89 VM |
378 | if (wowledon) { |
379 | char buf[32]; | |
380 | int len = sprintf(buf, "on"); | |
381 | android_readwrite_file("/sys/power/state", NULL, buf, len); | |
382 | ||
383 | len = sprintf(buf, "%d", 127); | |
384 | android_readwrite_file("/sys/class/leds/lcd-backlight/brightness", | |
385 | NULL, buf,len); | |
386 | } | |
387 | } | |
388 | } | |
389 | } | |
390 | #endif /* CONFIG_PM */ |