]>
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: | |
4dd53810 | 123 | kfree(msg); |
30295c89 VM |
124 | return ret; |
125 | } | |
126 | #endif | |
127 | ||
128 | int android_logger_lv(void *module, int mask) | |
129 | { | |
130 | switch (mask) { | |
131 | case ATH_DEBUG_ERR: | |
132 | return 6; | |
133 | case ATH_DEBUG_INFO: | |
134 | return 4; | |
135 | case ATH_DEBUG_WARN: | |
136 | return 5; | |
137 | case ATH_DEBUG_TRC: | |
138 | return 3; | |
139 | default: | |
140 | #ifdef DEBUG | |
141 | if (!module) { | |
142 | return 3; | |
143 | } else if (module == &GET_ATH_MODULE_DEBUG_VAR_NAME(driver)) { | |
144 | return (mask <=ATH_DEBUG_MAKE_MODULE_MASK(3)) ? 3 : 2; | |
145 | } else if (module == &GET_ATH_MODULE_DEBUG_VAR_NAME(htc)) { | |
146 | return 2; | |
147 | } else { | |
148 | return 3; | |
149 | } | |
150 | #else | |
151 | return 3; /* DEBUG */ | |
152 | #endif | |
153 | } | |
154 | } | |
155 | ||
4c42080f | 156 | static int android_readwrite_file(const char *filename, char *rbuf, const char *wbuf, size_t length) |
30295c89 VM |
157 | { |
158 | int ret = 0; | |
159 | struct file *filp = (struct file *)-ENOENT; | |
160 | mm_segment_t oldfs; | |
161 | oldfs = get_fs(); | |
162 | set_fs(KERNEL_DS); | |
163 | do { | |
164 | int mode = (wbuf) ? O_RDWR : O_RDONLY; | |
165 | filp = filp_open(filename, mode, S_IRUSR); | |
166 | if (IS_ERR(filp) || !filp->f_op) { | |
167 | AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("%s: file %s filp_open error\n", __FUNCTION__, filename)); | |
168 | ret = -ENOENT; | |
169 | break; | |
170 | } | |
171 | ||
172 | if (length==0) { | |
173 | /* Read the length of the file only */ | |
174 | struct inode *inode; | |
175 | ||
176 | inode = GET_INODE_FROM_FILEP(filp); | |
177 | if (!inode) { | |
178 | AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("%s: Get inode from %s failed\n", __FUNCTION__, filename)); | |
179 | ret = -ENOENT; | |
180 | break; | |
181 | } | |
182 | ret = i_size_read(inode->i_mapping->host); | |
183 | break; | |
184 | } | |
185 | ||
186 | if (wbuf) { | |
187 | if ( (ret=filp->f_op->write(filp, wbuf, length, &filp->f_pos)) < 0) { | |
188 | AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("%s: Write %u bytes to file %s error %d\n", __FUNCTION__, | |
189 | length, filename, ret)); | |
190 | break; | |
191 | } | |
192 | } else { | |
193 | if ( (ret=filp->f_op->read(filp, rbuf, length, &filp->f_pos)) < 0) { | |
194 | AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("%s: Read %u bytes from file %s error %d\n", __FUNCTION__, | |
195 | length, filename, ret)); | |
196 | break; | |
197 | } | |
198 | } | |
199 | } while (0); | |
200 | ||
201 | if (!IS_ERR(filp)) { | |
202 | filp_close(filp, NULL); | |
203 | } | |
204 | set_fs(oldfs); | |
205 | ||
206 | return ret; | |
207 | } | |
208 | ||
209 | int android_request_firmware(const struct firmware **firmware_p, const char *name, | |
210 | struct device *device) | |
211 | { | |
212 | int ret = 0; | |
213 | struct firmware *firmware; | |
214 | char filename[256]; | |
215 | const char *raw_filename = name; | |
216 | *firmware_p = firmware = kzalloc(sizeof(*firmware), GFP_KERNEL); | |
217 | if (!firmware) | |
218 | return -ENOMEM; | |
219 | sprintf(filename, "%s/%s", fwpath, raw_filename); | |
220 | do { | |
221 | size_t length, bufsize, bmisize; | |
222 | ||
223 | if ( (ret=android_readwrite_file(filename, NULL, NULL, 0)) < 0) { | |
224 | break; | |
225 | } else { | |
226 | length = ret; | |
227 | } | |
228 | ||
229 | bufsize = ALIGN(length, PAGE_SIZE); | |
230 | bmisize = A_ROUND_UP(length, 4); | |
231 | bufsize = max(bmisize, bufsize); | |
232 | firmware->data = vmalloc(bufsize); | |
233 | firmware->size = length; | |
234 | if (!firmware->data) { | |
235 | AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("%s: Cannot allocate buffer for firmware\n", __FUNCTION__)); | |
236 | ret = -ENOMEM; | |
237 | break; | |
238 | } | |
239 | ||
240 | if ( (ret=android_readwrite_file(filename, (char*)firmware->data, NULL, length)) != length) { | |
241 | AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("%s: file read error, ret %d request %d\n", __FUNCTION__, ret, length)); | |
242 | ret = -1; | |
243 | break; | |
244 | } | |
245 | ||
246 | } while (0); | |
247 | ||
248 | if (ret<0) { | |
249 | if (firmware) { | |
250 | if (firmware->data) | |
251 | vfree(firmware->data); | |
252 | kfree(firmware); | |
253 | } | |
254 | *firmware_p = NULL; | |
255 | } else { | |
256 | ret = 0; | |
257 | } | |
258 | return ret; | |
259 | } | |
260 | ||
261 | void android_release_firmware(const struct firmware *firmware) | |
262 | { | |
263 | if (firmware) { | |
264 | if (firmware->data) | |
265 | vfree(firmware->data); | |
266 | kfree(firmware); | |
267 | } | |
268 | } | |
269 | ||
1f4c34bd | 270 | static int ar6000_android_avail_ev(void *context, void *hif_handle) |
30295c89 | 271 | { |
1f4c34bd | 272 | int ret; |
30295c89 VM |
273 | ar6000_enable_mmchost_detect_change(0); |
274 | ret = ar6000_avail_ev_p(context, hif_handle); | |
30295c89 VM |
275 | return ret; |
276 | } | |
277 | ||
278 | /* Useful for qualcom platform to detect our wlan card for mmc stack */ | |
279 | static void ar6000_enable_mmchost_detect_change(int enable) | |
280 | { | |
281 | #ifdef CONFIG_MMC_MSM | |
282 | #define MMC_MSM_DEV "msm_sdcc.1" | |
283 | char buf[3]; | |
284 | int length; | |
285 | ||
286 | if (!enable_mmc_host_detect_change) { | |
287 | return; | |
288 | } | |
289 | length = snprintf(buf, sizeof(buf), "%d\n", enable ? 1 : 0); | |
290 | if (android_readwrite_file("/sys/devices/platform/" MMC_MSM_DEV "/detect_change", | |
291 | NULL, buf, length) < 0) { | |
292 | /* fall back to polling */ | |
293 | android_readwrite_file("/sys/devices/platform/" MMC_MSM_DEV "/polling", NULL, buf, length); | |
294 | } | |
295 | #endif | |
296 | } | |
297 | ||
298 | #ifdef CONFIG_HAS_EARLYSUSPEND | |
299 | static void android_early_suspend(struct early_suspend *h) | |
300 | { | |
301 | screen_is_off = 1; | |
302 | } | |
303 | ||
304 | static void android_late_resume(struct early_suspend *h) | |
305 | { | |
306 | screen_is_off = 0; | |
307 | } | |
308 | #endif | |
309 | ||
310 | void android_module_init(OSDRV_CALLBACKS *osdrvCallbacks) | |
311 | { | |
312 | bmienable = 1; | |
313 | if (ifname[0] == '\0') | |
314 | strcpy(ifname, def_ifname); | |
30295c89 VM |
315 | #ifdef CONFIG_HAS_EARLYSUSPEND |
316 | ar6k_early_suspend.suspend = android_early_suspend; | |
317 | ar6k_early_suspend.resume = android_late_resume; | |
318 | ar6k_early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN; | |
319 | register_early_suspend(&ar6k_early_suspend); | |
320 | #endif | |
321 | ||
322 | ar6000_avail_ev_p = osdrvCallbacks->deviceInsertedHandler; | |
323 | osdrvCallbacks->deviceInsertedHandler = ar6000_android_avail_ev; | |
324 | ||
325 | ar6000_enable_mmchost_detect_change(1); | |
326 | } | |
327 | ||
328 | void android_module_exit(void) | |
329 | { | |
330 | #ifdef CONFIG_HAS_EARLYSUSPEND | |
331 | unregister_early_suspend(&ar6k_early_suspend); | |
30295c89 VM |
332 | #endif |
333 | ar6000_enable_mmchost_detect_change(1); | |
334 | } | |
335 | ||
336 | #ifdef CONFIG_PM | |
a71f0bf6 | 337 | void android_ar6k_check_wow_status(struct ar6_softc *ar, struct sk_buff *skb, bool isEvent) |
30295c89 VM |
338 | { |
339 | if ( | |
340 | #ifdef CONFIG_HAS_EARLYSUSPEND | |
341 | screen_is_off && | |
342 | #endif | |
343 | skb && ar->arConnected) { | |
1071a134 | 344 | bool needWake = false; |
30295c89 | 345 | if (isEvent) { |
4853ac05 JP |
346 | if (A_NETBUF_LEN(skb) >= sizeof(u16)) { |
347 | u16 cmd = *(const u16 *)A_NETBUF_DATA(skb); | |
30295c89 VM |
348 | switch (cmd) { |
349 | case WMI_CONNECT_EVENTID: | |
350 | case WMI_DISCONNECT_EVENTID: | |
1071a134 | 351 | needWake = true; |
30295c89 VM |
352 | break; |
353 | default: | |
354 | /* dont wake lock the system for other event */ | |
355 | break; | |
356 | } | |
357 | } | |
358 | } else if (A_NETBUF_LEN(skb) >= sizeof(ATH_MAC_HDR)) { | |
359 | ATH_MAC_HDR *datap = (ATH_MAC_HDR *)A_NETBUF_DATA(skb); | |
360 | if (!IEEE80211_IS_MULTICAST(datap->dstMac)) { | |
361 | switch (A_BE2CPU16(datap->typeOrLen)) { | |
362 | case 0x0800: /* IP */ | |
363 | case 0x888e: /* EAPOL */ | |
364 | case 0x88c7: /* RSN_PREAUTH */ | |
365 | case 0x88b4: /* WAPI */ | |
1071a134 | 366 | needWake = true; |
30295c89 VM |
367 | break; |
368 | case 0x0806: /* ARP is not important to hold wake lock */ | |
369 | default: | |
370 | break; | |
371 | } | |
372 | } | |
373 | } | |
374 | if (needWake) { | |
25985edc | 375 | /* keep host wake up if there is any event and packate coming in*/ |
30295c89 VM |
376 | if (wowledon) { |
377 | char buf[32]; | |
378 | int len = sprintf(buf, "on"); | |
379 | android_readwrite_file("/sys/power/state", NULL, buf, len); | |
380 | ||
381 | len = sprintf(buf, "%d", 127); | |
382 | android_readwrite_file("/sys/class/leds/lcd-backlight/brightness", | |
383 | NULL, buf,len); | |
384 | } | |
385 | } | |
386 | } | |
387 | } | |
388 | #endif /* CONFIG_PM */ |