/* * Copyright (c) 2015 iComm-semi Ltd. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include /* for proc_mkdir, create */ #include #include #include #include /* for copy_from_user */ #include #include //#include #include #include #include /* for isalpha & isdigit */ #include "ssv_cfg.h" #include "ssvdevice.h" #include "rftool/ssv_rftool.h" #include "ssv_debug.h" #if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0)) #if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0)) #define get_ds() KERNEL_DS #endif #endif MODULE_AUTHOR("iComm-semi, Ltd"); MODULE_DESCRIPTION("Shared library for SSV wireless LAN cards."); MODULE_LICENSE("Dual BSD/GPL"); char *stacfgpath = NULL; EXPORT_SYMBOL(stacfgpath); module_param(stacfgpath, charp, 0000); MODULE_PARM_DESC(stacfgpath, "Get path of sta cfg"); char *cfgfirmwarepath = NULL; EXPORT_SYMBOL(cfgfirmwarepath); module_param(cfgfirmwarepath, charp, 0000); MODULE_PARM_DESC(cfgfirmwarepath, "Get firmware path"); char* ssv_initmac = NULL; EXPORT_SYMBOL(ssv_initmac); module_param(ssv_initmac, charp, 0644); MODULE_PARM_DESC(ssv_initmac, "Wi-Fi MAC address"); /*****************/ /* For USB only. */ /*****************/ #ifndef CONFIG_USB_TX_MULTI_URB int ssv_usb_rx_nr_recvbuff = 2; #else int ssv_usb_rx_nr_recvbuff = 5; #endif EXPORT_SYMBOL(ssv_usb_rx_nr_recvbuff); module_param(ssv_usb_rx_nr_recvbuff, int, 0644); MODULE_PARM_DESC(ssv_usb_rx_nr_recvbuff, "USB RX buffer 1 ~ SSV_USB_MAX_NR_RECVBUFF"); /*****************/ /* For USB only. */ /*****************/ int ssv_rx_use_wq = 0; EXPORT_SYMBOL(ssv_rx_use_wq); module_param(ssv_rx_use_wq, int, 0444); MODULE_PARM_DESC(ssv_rx_use_wq, "USB RX uses workqueue instead of tasklet"); struct ssv6xxx_cfg ssv_cfg; struct ssv_rftool_cfg rftool_cfg; EXPORT_SYMBOL(ssv_cfg); #if 0 static int __string2s32(u8 *val_str, void *val) { char *endp; int base=10; if (val_str[0]=='0' && ((val_str[1]=='x')||(val_str[1]=='X'))) base = 16; *(int *)val = simple_strtoul(val_str, &endp, base); return 0; } #endif static int __string2bool(u8 *u8str, void *val, u32 arg) { char *endp; *(u8 *)val = !!simple_strtoul(u8str, &endp, 10); return 0; } static int __string2u32(u8 *u8str, void *val, u32 arg) { char *endp; int base=10; if (u8str[0]=='0' && ((u8str[1]=='x')||(u8str[1]=='X'))) base = 16; *(u32 *)val = simple_strtoul(u8str, &endp, base); return 0; } static int __string2u8(u8 *u8str, void *val, u32 arg) { char *endp; int base=10; if (u8str[0]=='0' && ((u8str[1]=='x')||(u8str[1]=='X'))) base = 16; *(u8 *)val = simple_strtoul(u8str, &endp, base); return 0; } #if 0 static int __string2s32(u8 *u8str, void *val, u32 arg) { char *endp; int base=10; *(s32 *)val = simple_strtol(u8str, &endp, base); return 0; } #endif static int __string2flag32(u8 *flag_str, void *flag, u32 arg) { u32 *val=(u32 *)flag; if (arg >= (sizeof(u32)<<3)) return -1; if (strcmp(flag_str, "on")==0) { *val |= (1<f_op && fp->f_op->read) num_read = fp->f_op->read(fp, &ch, 1, &fp->f_pos); #else #if LINUX_VERSION_CODE >= KERNEL_VERSION(4,14,0) num_read = kernel_read(fp, &ch, 1, &fp->f_pos); #else num_read = vfs_read(fp, &ch, 1, &fp->f_pos); #endif #endif if (num_read < 0) { if (num_read == EINTR) continue; else return -1; } else if (num_read == 0) { if (total_read == 0) return 0; else break; } else { if (ch == '#') start_ignore = 1; if (total_read < size - 1) { total_read++; if (start_ignore) *buffer++ = '\0'; else *buffer++ = ch; } if (ch == '\n') break; } } *buffer = '\0'; return total_read; } int ssv_ischar(char *c) { int is_char = 1; while(*c) { if (isalpha(*c) || isdigit(*c) || *c == '_' || *c == ':' || *c == '/' || *c == '.' || *c == '-'|| *c == ',') c++; else { is_char = 0; break; } } return is_char; } static void _set_initial_cfg_default(void) { size_t s; for(s=0; cfg_cmds[s].cfg_cmd != NULL; s++) { if ((cfg_cmds[s].def_val)!= NULL) { cfg_cmds[s].translate_func(cfg_cmds[s].def_val, cfg_cmds[s].var, cfg_cmds[s].arg); } } } static void _ssv_import_default_cfg (char *stacfgpath) { struct file *fp = (struct file *) NULL; char buf[MAX_CHARS_PER_LINE], cfg_cmd[32], cfg_value[32]; #if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0)) mm_segment_t fs; #endif size_t s, read_len = 0, is_cmd_support = 0; SSV_LOG_DBG("\n*** %s, %s ***\n\n", __func__, stacfgpath); // Init the buffer with 0 memset(&ssv_cfg, 0, sizeof(ssv_cfg)); // set default config value _set_initial_cfg_default(); if (stacfgpath == NULL) { //WARN_ON(1); SSV_LOG_DBG("Use default ssv configuration\n"); return; } memset(buf, 0, sizeof(buf)); fp = filp_open(stacfgpath, O_RDONLY, 0); if (IS_ERR(fp) || fp == NULL) { SSV_LOG_DBG("ERROR: filp_open\n"); WARN_ON(1); return; } if (fp->f_path.dentry == NULL) { SSV_LOG_DBG("ERROR: dentry NULL\n"); WARN_ON(1); return; } do { memset(cfg_cmd, '\0', sizeof(cfg_cmd)); memset(cfg_value, '\0', sizeof(cfg_value)); // Get current segment descriptor #if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0)) fs = get_fs(); // Set segment descriptor associated to kernel space set_fs(get_ds()); #endif // Read the file read_len = ssv_read_line(fp, buf, MAX_CHARS_PER_LINE); // Restore segment descriptor #if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0)) set_fs(fs); #endif sscanf(buf, "%s = %s", cfg_cmd, cfg_value); if (!ssv_ischar(cfg_cmd) || !ssv_ischar(cfg_value)) { SSV_LOG_DBG("ERORR invalid parameter: %s\n", buf); WARN_ON(1); continue; } is_cmd_support = 0; for(s=0; cfg_cmds[s].cfg_cmd != NULL; s++) { if (strcmp(cfg_cmds[s].cfg_cmd, cfg_cmd)==0) { cfg_cmds[s].translate_func(cfg_value, cfg_cmds[s].var, cfg_cmds[s].arg); //SSV_LOG_DBG("%scmd:%s, value:%s\n",buf, cfg_cmd, cfg_value); is_cmd_support = 1; break; } } if (!is_cmd_support && strlen(cfg_cmd) > 0) { SSV_LOG_DBG("ERROR Unsupported command: %s", cfg_cmd); WARN_ON(1); } } while (read_len > 0); //0: eof, < 0: error filp_close(fp, NULL); } extern int ssv6xxx_usb_init(void); extern int ssv6xxx_sdio_init(void); int ssv_device_init(void) { SSV_LOG_DBG("%s()\n", __FUNCTION__); // Initialize default configuration from file. _ssv_import_default_cfg(stacfgpath); #if (HWIF_SUPPORT != 2) ssv6xxx_usb_init(); #endif #if (HWIF_SUPPORT != 1) ssv6xxx_sdio_init(); #endif return 0; } extern void ssv6xxx_usb_exit(void); extern void ssv6xxx_sdio_exit(void); void ssv_device_exit(void) { SSV_LOG_DBG("%s()\n", __FUNCTION__); #if (HWIF_SUPPORT != 2) ssv6xxx_usb_exit(); #endif #if (HWIF_SUPPORT != 1) ssv6xxx_sdio_exit(); #endif } EXPORT_SYMBOL(ssv_device_init); EXPORT_SYMBOL(ssv_device_exit);