/drivers/net/wireless/sd8797/mlinux/moal_proc.c
C | 631 lines | 461 code | 42 blank | 128 comment | 76 complexity | 06840b2cb0ba4c2edd2879c93cdd46ad MD5 | raw file
Possible License(s): LGPL-2.0, AGPL-1.0, GPL-2.0
1/** @file moal_proc.c 2 * 3 * @brief This file contains functions for proc file. 4 * 5 * Copyright (C) 2008-2010, Marvell International Ltd. 6 * 7 * This software file (the "File") is distributed by Marvell International 8 * Ltd. under the terms of the GNU General Public License Version 2, June 1991 9 * (the "License"). You may use, redistribute and/or modify this File in 10 * accordance with the terms and conditions of the License, a copy of which 11 * is available by writing to the Free Software Foundation, Inc., 12 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the 13 * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. 14 * 15 * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE 17 * ARE EXPRESSLY DISCLAIMED. The License provides additional details about 18 * this warranty disclaimer. 19 * 20 */ 21 22/******************************************************** 23Change log: 24 10/21/2008: initial version 25********************************************************/ 26 27#include "moal_main.h" 28#ifdef UAP_SUPPORT 29#include "moal_uap.h" 30#endif 31#include "moal_sdio.h" 32 33/******************************************************** 34 Local Variables 35********************************************************/ 36#ifdef CONFIG_PROC_FS 37#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) 38#define PROC_DIR NULL 39#define MWLAN_PROC_DIR "mwlan/" 40/** Proc top level directory entry */ 41struct proc_dir_entry *proc_mwlan = NULL; 42#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24) 43#define PROC_DIR &proc_root 44#else 45#define PROC_DIR proc_net 46#endif 47 48#ifdef STA_SUPPORT 49static char *szModes[] = { 50 "Unknown", 51 "Managed", 52 "Ad-hoc", 53 "Auto", 54}; 55#endif 56 57extern int drv_mode; 58 59/******************************************************** 60 Global Variables 61********************************************************/ 62 63/******************************************************** 64 Local Functions 65********************************************************/ 66/** 67 * @brief Proc read function for info 68 * 69 * @param page Pointer to buffer 70 * @param start Read data starting position 71 * @param offset Offset 72 * @param count Counter 73 * @param eof End of file flag 74 * @param data Data to output 75 * 76 * @return Number of output data 77 */ 78static int 79woal_info_proc_read(char *page, char **start, off_t offset, 80 int count, int *eof, void *data) 81{ 82 char *p = page; 83 struct net_device *netdev = data; 84 char fmt[MLAN_MAX_VER_STR_LEN]; 85 moal_private *priv = (moal_private *) netdev_priv(netdev); 86#ifdef STA_SUPPORT 87 int i = 0; 88 moal_handle *handle = priv->phandle; 89 mlan_bss_info info; 90#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35) 91 struct dev_mc_list *mcptr = netdev->mc_list; 92 int mc_count = netdev->mc_count; 93#else 94 struct netdev_hw_addr *mcptr = NULL; 95 int mc_count = netdev_mc_count(netdev); 96#endif /* < 2.6.35 */ 97#else 98#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29) 99 int i = 0; 100#endif /* >= 2.6.29 */ 101#endif 102#ifdef UAP_SUPPORT 103 mlan_ds_uap_stats ustats; 104#endif 105 106 ENTER(); 107 108 if (offset) { 109 *eof = 1; 110 goto exit; 111 } 112 memset(fmt, 0, sizeof(fmt)); 113#ifdef UAP_SUPPORT 114 if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) { 115 p += sprintf(p, "driver_name = " "\"uap\"\n"); 116 woal_uap_get_version(priv, fmt, sizeof(fmt) - 1); 117 if (MLAN_STATUS_SUCCESS != 118 woal_uap_get_stats(priv, MOAL_PROC_WAIT, &ustats)) { 119 *eof = 1; 120 goto exit; 121 } 122 } 123#endif /* UAP_SUPPORT */ 124#ifdef STA_SUPPORT 125 if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) { 126 woal_get_version(handle, fmt, sizeof(fmt) - 1); 127 memset(&info, 0, sizeof(info)); 128 if (MLAN_STATUS_SUCCESS != 129 woal_get_bss_info(priv, MOAL_PROC_WAIT, &info)) { 130 *eof = 1; 131 goto exit; 132 } 133 p += sprintf(p, "driver_name = " "\"wlan\"\n"); 134 } 135#endif 136 p += sprintf(p, "driver_version = %s", fmt); 137 p += sprintf(p, "\ninterface_name=\"%s\"\n", netdev->name); 138#if defined(WIFI_DIRECT_SUPPORT) 139 if (priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT) { 140 if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) 141 p += sprintf(p, "bss_mode = \"WIFIDIRECT-Client\"\n"); 142 else 143 p += sprintf(p, "bss_mode = \"WIFIDIRECT-GO\"\n"); 144 } 145#endif 146#ifdef STA_SUPPORT 147 if (priv->bss_type == MLAN_BSS_TYPE_STA) 148 p += sprintf(p, "bss_mode =\"%s\"\n", szModes[info.bss_mode]); 149#endif 150 p += sprintf(p, "media_state=\"%s\"\n", 151 ((priv->media_connected == 152 MFALSE) ? "Disconnected" : "Connected")); 153 p += sprintf(p, "mac_address=\"%02x:%02x:%02x:%02x:%02x:%02x\"\n", 154 netdev->dev_addr[0], netdev->dev_addr[1], netdev->dev_addr[2], 155 netdev->dev_addr[3], netdev->dev_addr[4], netdev->dev_addr[5]); 156#ifdef STA_SUPPORT 157 if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) { 158 p += sprintf(p, "multicast_count=\"%d\"\n", mc_count); 159 p += sprintf(p, "essid=\"%s\"\n", info.ssid.ssid); 160 p += sprintf(p, "bssid=\"%02x:%02x:%02x:%02x:%02x:%02x\"\n", 161 info.bssid[0], info.bssid[1], 162 info.bssid[2], info.bssid[3], 163 info.bssid[4], info.bssid[5]); 164 p += sprintf(p, "channel=\"%d\"\n", (int) info.bss_chan); 165 p += sprintf(p, "region_code = \"%02x\"\n", (t_u8) info.region_code); 166 167 /* 168 * Put out the multicast list 169 */ 170#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35) 171 for (i = 0; i < netdev->mc_count; i++) { 172 p += sprintf(p, 173 "multicast_address[%d]=\"%02x:%02x:%02x:%02x:%02x:%02x\"\n", 174 i, 175 mcptr->dmi_addr[0], mcptr->dmi_addr[1], 176 mcptr->dmi_addr[2], mcptr->dmi_addr[3], 177 mcptr->dmi_addr[4], mcptr->dmi_addr[5]); 178 179 mcptr = mcptr->next; 180 } 181#else 182 netdev_for_each_mc_addr(mcptr, netdev) 183 p += sprintf(p, 184 "multicast_address[%d]=\"%02x:%02x:%02x:%02x:%02x:%02x\"\n", 185 i++, 186 mcptr->addr[0], mcptr->addr[1], 187 mcptr->addr[2], mcptr->addr[3], 188 mcptr->addr[4], mcptr->addr[5]); 189#endif /* < 2.6.35 */ 190 } 191#endif 192 p += sprintf(p, "num_tx_bytes = %lu\n", priv->stats.tx_bytes); 193 p += sprintf(p, "num_rx_bytes = %lu\n", priv->stats.rx_bytes); 194 p += sprintf(p, "num_tx_pkts = %lu\n", priv->stats.tx_packets); 195 p += sprintf(p, "num_rx_pkts = %lu\n", priv->stats.rx_packets); 196 p += sprintf(p, "num_tx_pkts_dropped = %lu\n", priv->stats.tx_dropped); 197 p += sprintf(p, "num_rx_pkts_dropped = %lu\n", priv->stats.rx_dropped); 198 p += sprintf(p, "num_tx_pkts_err = %lu\n", priv->stats.tx_errors); 199 p += sprintf(p, "num_rx_pkts_err = %lu\n", priv->stats.rx_errors); 200 p += sprintf(p, "carrier %s\n", 201 ((netif_carrier_ok(priv->netdev)) ? "on" : "off")); 202#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29) 203 for (i = 0; i < netdev->num_tx_queues; i++) { 204 p += sprintf(p, "tx queue %d: %s\n", i, 205 ((netif_tx_queue_stopped(netdev_get_tx_queue(netdev, 0))) ? 206 "stopped" : "started")); 207 } 208#else 209 p += sprintf(p, "tx queue %s\n", 210 ((netif_queue_stopped(priv->netdev)) ? "stopped" : "started")); 211#endif 212#ifdef UAP_SUPPORT 213 if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) { 214 p += sprintf(p, "tkip_mic_failures = %u\n", ustats.tkip_mic_failures); 215 p += sprintf(p, "ccmp_decrypt_errors = %u\n", 216 ustats.ccmp_decrypt_errors); 217 p += sprintf(p, "wep_undecryptable_count = %u\n", 218 ustats.wep_undecryptable_count); 219 p += sprintf(p, "wep_icv_error_count = %u\n", 220 ustats.wep_icv_error_count); 221 p += sprintf(p, "decrypt_failure_count = %u\n", 222 ustats.decrypt_failure_count); 223 p += sprintf(p, "mcast_tx_count = %u\n", ustats.mcast_tx_count); 224 p += sprintf(p, "failed_count = %u\n", ustats.failed_count); 225 p += sprintf(p, "retry_count = %u\n", ustats.retry_count); 226 p += sprintf(p, "multiple_retry_count = %u\n", 227 ustats.multi_retry_count); 228 p += sprintf(p, "frame_duplicate_count = %u\n", ustats.frame_dup_count); 229 p += sprintf(p, "rts_success_count = %u\n", ustats.rts_success_count); 230 p += sprintf(p, "rts_failure_count = %u\n", ustats.rts_failure_count); 231 p += sprintf(p, "ack_failure_count = %u\n", ustats.ack_failure_count); 232 p += sprintf(p, "rx_fragment_count = %u\n", ustats.rx_fragment_count); 233 p += sprintf(p, "mcast_rx_frame_count = %u\n", 234 ustats.mcast_rx_frame_count); 235 p += sprintf(p, "fcs_error_count = %u\n", ustats.fcs_error_count); 236 p += sprintf(p, "tx_frame_count = %u\n", ustats.tx_frame_count); 237 p += sprintf(p, "rsna_tkip_cm_invoked = %u\n", 238 ustats.rsna_tkip_cm_invoked); 239 p += sprintf(p, "rsna_4way_hshk_failures = %u\n", 240 ustats.rsna_4way_hshk_failures); 241 } 242#endif /* UAP_SUPPORT */ 243 exit: 244 LEAVE(); 245 return (p - page); 246} 247 248#define CMD52_STR_LEN 50 249/* 250 * @brief Parse cmd52 string 251 * 252 * @param buffer A pointer user buffer 253 * @param len Length user buffer 254 * @param func Parsed func number 255 * @param reg Parsed reg value 256 * @param val Parsed value to set 257 * @return BT_STATUS_SUCCESS 258 */ 259static int 260parse_cmd52_string(const char __user * buffer, size_t len, int *func, int *reg, 261 int *val) 262{ 263 int ret = MLAN_STATUS_SUCCESS; 264 char *string = NULL; 265 char *pos = NULL; 266 267 ENTER(); 268 269 string = (char *) kmalloc(CMD52_STR_LEN, GFP_KERNEL); 270 memset(string, 0, CMD52_STR_LEN); 271 memcpy(string, buffer + strlen("sdcmd52rw="), len - strlen("sdcmd52rw=")); 272 string = strstrip(string); 273 274 *func = -1; 275 *reg = -1; 276 *val = -1; 277 278 /* Get func */ 279 pos = strsep(&string, " \t"); 280 if (pos) { 281 *func = woal_string_to_number(pos); 282 } 283 284 /* Get reg */ 285 pos = strsep(&string, " \t"); 286 if (pos) { 287 *reg = woal_string_to_number(pos); 288 } 289 290 /* Get val (optional) */ 291 pos = strsep(&string, " \t"); 292 if (pos) { 293 *val = woal_string_to_number(pos); 294 } 295 if (string) 296 kfree(string); 297 LEAVE(); 298 return ret; 299} 300 301/** 302 * @brief config proc write function 303 * 304 * @param f file pointer 305 * @param buf pointer to data buffer 306 * @param cnt data number to write 307 * @param data data to write 308 * @return number of data 309 */ 310static int 311woal_config_write(struct file *f, const char *buf, unsigned long cnt, 312 void *data) 313{ 314 char databuf[101]; 315 char *line; 316 t_u32 config_data = 0; 317 moal_handle *handle = (moal_handle *) data; 318 int func, reg, val; 319 320 ENTER(); 321 if (!MODULE_GET) { 322 LEAVE(); 323 return 0; 324 } 325 326 if (cnt >= sizeof(databuf)) { 327 MODULE_PUT; 328 LEAVE(); 329 return (int) cnt; 330 } 331 memset(databuf, 0, sizeof(databuf)); 332 if (copy_from_user(databuf, buf, cnt)) { 333 MODULE_PUT; 334 LEAVE(); 335 return 0; 336 } 337 line = databuf; 338 if (!strncmp(databuf, "soft_reset", strlen("soft_reset"))) { 339 line += strlen("soft_reset") + 1; 340 config_data = (t_u32) woal_string_to_number(line); 341 PRINTM(MINFO, "soft_reset: %d\n", (int) config_data); 342 if (woal_request_soft_reset(handle) == MLAN_STATUS_SUCCESS) { 343 handle->hardware_status = HardwareStatusReset; 344 } else { 345 PRINTM(MERROR, "Could not perform soft reset\n"); 346 } 347 } 348 if (!strncmp(databuf, "drv_mode", strlen("drv_mode"))) { 349 line += strlen("drv_mode") + 1; 350 config_data = (t_u32) woal_string_to_number(line); 351 PRINTM(MINFO, "drv_mode: %d\n", (int) config_data); 352 if (config_data != (t_u32) drv_mode) 353 if (woal_switch_drv_mode(handle, config_data) != 354 MLAN_STATUS_SUCCESS) { 355 PRINTM(MERROR, "Could not switch drv mode\n"); 356 } 357 } 358 if (!strncmp(databuf, "sdcmd52rw=", strlen("sdcmd52rw="))) { 359 parse_cmd52_string(databuf, (size_t) cnt, &func, ®, &val); 360 woal_sdio_read_write_cmd52(handle, func, reg, val); 361 } 362 MODULE_PUT; 363 LEAVE(); 364 return (int) cnt; 365} 366 367/** 368 * @brief config proc read function 369 * 370 * @param page pointer to buffer 371 * @param s read data starting position 372 * @param off offset 373 * @param cnt counter 374 * @param eof end of file flag 375 * @param data data to output 376 * @return number of output data 377 */ 378static int 379woal_config_read(char *page, char **s, off_t off, int cnt, int *eof, void *data) 380{ 381 char *p = page; 382 moal_handle *handle = (moal_handle *) data; 383 384 ENTER(); 385 if (!MODULE_GET) { 386 LEAVE(); 387 return 0; 388 } 389 p += sprintf(p, "hardware_status=%d\n", (int) handle->hardware_status); 390 p += sprintf(p, "netlink_num=%d\n", (int) handle->netlink_num); 391 p += sprintf(p, "drv_mode=%d\n", (int) drv_mode); 392 p += sprintf(p, "sdcmd52rw=%d 0x%0x 0x%02X\n", handle->cmd52_func, 393 handle->cmd52_reg, handle->cmd52_val); 394 MODULE_PUT; 395 LEAVE(); 396 return p - page; 397} 398 399/******************************************************** 400 Global Functions 401********************************************************/ 402/** 403 * @brief Convert string to number 404 * 405 * @param s Pointer to numbered string 406 * 407 * @return Converted number from string s 408 */ 409int 410woal_string_to_number(char *s) 411{ 412 int r = 0; 413 int base = 0; 414 int pn = 1; 415 416 if (!strncmp(s, "-", 1)) { 417 pn = -1; 418 s++; 419 } 420 if (!strncmp(s, "0x", 2) || !strncmp(s, "0X", 2)) { 421 base = 16; 422 s += 2; 423 } else 424 base = 10; 425 426 for (s = s; *s; s++) { 427 if ((*s >= '0') && (*s <= '9')) 428 r = (r * base) + (*s - '0'); 429 else if ((*s >= 'A') && (*s <= 'F')) 430 r = (r * base) + (*s - 'A' + 10); 431 else if ((*s >= 'a') && (*s <= 'f')) 432 r = (r * base) + (*s - 'a' + 10); 433 else 434 break; 435 } 436 437 return (r * pn); 438} 439 440/** 441 * @brief Create the top level proc directory 442 * 443 * @param handle Pointer to woal_handle 444 * 445 * @return N/A 446 */ 447void 448woal_proc_init(moal_handle * handle) 449{ 450 struct proc_dir_entry *r; 451#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26) 452 struct proc_dir_entry *pde = PROC_DIR; 453#endif 454 char config_proc_dir[20]; 455 456 ENTER(); 457 458 PRINTM(MINFO, "Create Proc Interface\n"); 459 if (!handle->proc_mwlan) { 460#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26) 461 /* Check if directory already exists */ 462 for (pde = pde->subdir; pde; pde = pde->next) { 463 if (pde->namelen && !strcmp("mwlan", pde->name)) { 464 /* Directory exists */ 465 PRINTM(MWARN, "proc interface already exists!\n"); 466 handle->proc_mwlan = pde; 467 break; 468 } 469 } 470 if (pde == NULL) { 471 handle->proc_mwlan = proc_mkdir("mwlan", PROC_DIR); 472 if (!handle->proc_mwlan) 473 PRINTM(MERROR, "Cannot create proc interface!\n"); 474#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) 475 else 476 atomic_set(&handle->proc_mwlan->count, 1); 477#endif 478 } 479#else 480 if (!proc_mwlan) { 481 handle->proc_mwlan = proc_mkdir("mwlan", PROC_DIR); 482 if (!handle->proc_mwlan) { 483 PRINTM(MERROR, "Cannot create proc interface!\n"); 484 } 485 } else { 486 handle->proc_mwlan = proc_mwlan; 487 } 488#endif 489 if (handle->proc_mwlan) { 490 if (handle->handle_idx) 491 sprintf(config_proc_dir, "config%d", handle->handle_idx); 492 else 493 strcpy(config_proc_dir, "config"); 494 495 r = create_proc_entry(config_proc_dir, 0644, handle->proc_mwlan); 496 497 if (r) { 498 r->data = handle; 499 r->read_proc = woal_config_read; 500 r->write_proc = woal_config_write; 501#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,30) 502 r->owner = THIS_MODULE; 503#endif 504 } else 505 PRINTM(MMSG, "Fail to create proc config\n"); 506 } 507#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,26) 508 proc_mwlan = handle->proc_mwlan; 509#endif 510 } 511 512 LEAVE(); 513} 514 515/** 516 * @brief Remove the top level proc directory 517 * 518 * @param handle pointer moal_handle 519 * 520 * @return N/A 521 */ 522void 523woal_proc_exit(moal_handle * handle) 524{ 525 ENTER(); 526 527 PRINTM(MINFO, "Remove Proc Interface\n"); 528 if (handle->proc_mwlan) { 529 char config_proc_dir[20]; 530 if (handle->handle_idx) 531 sprintf(config_proc_dir, "config%d", handle->handle_idx); 532 else 533 strcpy(config_proc_dir, "config"); 534 remove_proc_entry(config_proc_dir, handle->proc_mwlan); 535 536 /* Remove only if we are the only instance using this */ 537 if (atomic_read(&(handle->proc_mwlan->count)) > 1) { 538 PRINTM(MWARN, "More than one interface using proc!\n"); 539 } else { 540#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) 541 atomic_dec(&(handle->proc_mwlan->count)); 542#endif 543 remove_proc_entry("mwlan", PROC_DIR); 544 handle->proc_mwlan = NULL; 545#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,26) 546 proc_mwlan = NULL; 547#endif 548 } 549 } 550 551 LEAVE(); 552} 553 554/** 555 * @brief Create proc file for interface 556 * 557 * @param priv pointer moal_private 558 * 559 * @return N/A 560 */ 561void 562woal_create_proc_entry(moal_private * priv) 563{ 564 struct net_device *dev = priv->netdev; 565#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) 566 char proc_dir_name[20]; 567#endif 568 569 ENTER(); 570 571#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) 572 if (!priv->proc_entry) { 573 memset(proc_dir_name, 0, sizeof(proc_dir_name)); 574 strcpy(proc_dir_name, MWLAN_PROC_DIR); 575 strcat(proc_dir_name, dev->name); 576 /* Try to create mwlan/mlanX first */ 577 priv->proc_entry = proc_mkdir(proc_dir_name, PROC_DIR); 578 if (priv->proc_entry) { 579 /* Success. Continue normally */ 580 if (!priv->phandle->proc_mwlan) { 581 priv->phandle->proc_mwlan = priv->proc_entry->parent; 582 } 583 atomic_inc(&(priv->phandle->proc_mwlan->count)); 584 } else { 585 /* Failure. mwlan may not exist. Try to create that first */ 586 priv->phandle->proc_mwlan = proc_mkdir("mwlan", PROC_DIR); 587 if (!priv->phandle->proc_mwlan) { 588 /* Failure. Something broken */ 589 LEAVE(); 590 return; 591 } else { 592 /* Success. Now retry creating mlanX */ 593 priv->proc_entry = proc_mkdir(proc_dir_name, PROC_DIR); 594 atomic_inc(&(priv->phandle->proc_mwlan->count)); 595 } 596 } 597#else 598 if (priv->phandle->proc_mwlan && !priv->proc_entry) { 599 priv->proc_entry = proc_mkdir(dev->name, priv->phandle->proc_mwlan); 600 atomic_inc(&(priv->phandle->proc_mwlan->count)); 601#endif 602 strcpy(priv->proc_entry_name, dev->name); 603 if (priv->proc_entry) { 604 create_proc_read_entry("info", 0, priv->proc_entry, 605 woal_info_proc_read, dev); 606 } 607 } 608 609 LEAVE(); 610} 611 612/** 613 * @brief Remove proc file 614 * 615 * @param priv Pointer moal_private 616 * 617 * @return N/A 618 */ 619void 620woal_proc_remove(moal_private * priv) 621{ 622 ENTER(); 623 if (priv->phandle->proc_mwlan && priv->proc_entry) { 624 remove_proc_entry("info", priv->proc_entry); 625 remove_proc_entry(priv->proc_entry_name, priv->phandle->proc_mwlan); 626 atomic_dec(&(priv->phandle->proc_mwlan->count)); 627 priv->proc_entry = NULL; 628 } 629 LEAVE(); 630} 631#endif