| 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)) |
| 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 | |
| 158 | static int android_readwrite_file(const char *filename, char *rbuf, const char *wbuf, size_t length) |
| 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 | |
| 272 | static int ar6000_android_avail_ev(void *context, void *hif_handle) |
| 273 | { |
| 274 | int ret; |
| 275 | ar6000_enable_mmchost_detect_change(0); |
| 276 | ret = ar6000_avail_ev_p(context, hif_handle); |
| 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); |
| 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); |
| 334 | #endif |
| 335 | ar6000_enable_mmchost_detect_change(1); |
| 336 | } |
| 337 | |
| 338 | #ifdef CONFIG_PM |
| 339 | void android_ar6k_check_wow_status(AR_SOFTC_T *ar, struct sk_buff *skb, bool isEvent) |
| 340 | { |
| 341 | if ( |
| 342 | #ifdef CONFIG_HAS_EARLYSUSPEND |
| 343 | screen_is_off && |
| 344 | #endif |
| 345 | skb && ar->arConnected) { |
| 346 | bool needWake = false; |
| 347 | if (isEvent) { |
| 348 | if (A_NETBUF_LEN(skb) >= sizeof(u16)) { |
| 349 | u16 cmd = *(const u16 *)A_NETBUF_DATA(skb); |
| 350 | switch (cmd) { |
| 351 | case WMI_CONNECT_EVENTID: |
| 352 | case WMI_DISCONNECT_EVENTID: |
| 353 | needWake = true; |
| 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 */ |
| 368 | needWake = true; |
| 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*/ |
| 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 */ |