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 
28 #ifdef CONFIG_HAS_EARLYSUSPEND
29 #include <linux/earlysuspend.h>
30 #endif
31 
32 bool enable_mmc_host_detect_change = false;
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 
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 
54 static int (*ar6000_avail_ev_p)(void *, void *);
55 
56 #if defined(CONFIG_ANDROID_LOGGER) && (!defined(CONFIG_MMC_MSM))
logger_write(const enum logidx index,const unsigned char prio,const char __kernel * const tag,const char __kernel * const fmt,...)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     kfree(msg);
124     return ret;
125 }
126 #endif
127 
android_logger_lv(void * module,int mask)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 
android_readwrite_file(const char * filename,char * rbuf,const char * wbuf,size_t length)156 static int android_readwrite_file(const char *filename, char *rbuf, const char *wbuf, size_t length)
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 
android_request_firmware(const struct firmware ** firmware_p,const char * name,struct device * device)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 
android_release_firmware(const struct firmware * firmware)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 
ar6000_android_avail_ev(void * context,void * hif_handle)270 static int ar6000_android_avail_ev(void *context, void *hif_handle)
271 {
272     int ret;
273     ar6000_enable_mmchost_detect_change(0);
274     ret = ar6000_avail_ev_p(context, hif_handle);
275     return ret;
276 }
277 
278 /* Useful for qualcom platform to detect our wlan card for mmc stack */
ar6000_enable_mmchost_detect_change(int enable)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
android_early_suspend(struct early_suspend * h)299 static void android_early_suspend(struct early_suspend *h)
300 {
301     screen_is_off = 1;
302 }
303 
android_late_resume(struct early_suspend * h)304 static void android_late_resume(struct early_suspend *h)
305 {
306     screen_is_off = 0;
307 }
308 #endif
309 
android_module_init(OSDRV_CALLBACKS * osdrvCallbacks)310 void android_module_init(OSDRV_CALLBACKS *osdrvCallbacks)
311 {
312     bmienable = 1;
313     if (ifname[0] == '\0')
314         strcpy(ifname, def_ifname);
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 
android_module_exit(void)328 void android_module_exit(void)
329 {
330 #ifdef CONFIG_HAS_EARLYSUSPEND
331     unregister_early_suspend(&ar6k_early_suspend);
332 #endif
333     ar6000_enable_mmchost_detect_change(1);
334 }
335 
336 #ifdef CONFIG_PM
android_ar6k_check_wow_status(struct ar6_softc * ar,struct sk_buff * skb,bool isEvent)337 void android_ar6k_check_wow_status(struct ar6_softc *ar, struct sk_buff *skb, bool isEvent)
338 {
339     if (
340 #ifdef CONFIG_HAS_EARLYSUSPEND
341         screen_is_off &&
342 #endif
343             skb && ar->arConnected) {
344         bool needWake = false;
345         if (isEvent) {
346             if (A_NETBUF_LEN(skb) >= sizeof(u16)) {
347                 u16 cmd = *(const u16 *)A_NETBUF_DATA(skb);
348                 switch (cmd) {
349                 case WMI_CONNECT_EVENTID:
350                 case WMI_DISCONNECT_EVENTID:
351                     needWake = true;
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 */
366                      needWake = true;
367                      break;
368                 case 0x0806: /* ARP is not important to hold wake lock */
369                 default:
370                     break;
371                 }
372             }
373         }
374         if (needWake) {
375             /* keep host wake up if there is any event and packate coming in*/
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 */
389