| /* |
| * Copyright (c) 2010 Broadcom Corporation |
| * |
| * 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 <linux/kernel.h> |
| #include <linux/string.h> |
| #include <linux/netdevice.h> |
| #include <linux/module.h> |
| #include <linux/firmware.h> |
| #include <brcmu_wifi.h> |
| #include <brcmu_utils.h> |
| #include <defs.h> |
| #include "core.h" |
| #include "bus.h" |
| #include "debug.h" |
| #include "fwil.h" |
| #include "fwil_types.h" |
| #include "tracepoint.h" |
| #include "common.h" |
| #include "of.h" |
| #include "firmware.h" |
| |
| MODULE_AUTHOR("Broadcom Corporation"); |
| MODULE_DESCRIPTION("Broadcom 802.11 wireless LAN fullmac driver."); |
| MODULE_LICENSE("Dual BSD/GPL"); |
| |
| const u8 ALLFFMAC[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; |
| |
| #define BRCMF_DEFAULT_SCAN_CHANNEL_TIME 40 |
| #define BRCMF_DEFAULT_SCAN_UNASSOC_TIME 40 |
| |
| /* default boost value for RSSI_DELTA in preferred join selection */ |
| #define BRCMF_JOIN_PREF_RSSI_BOOST 8 |
| |
| #define BRCMF_DEFAULT_TXGLOM_SIZE 32 /* max tx frames in glom chain */ |
| |
| static int brcmf_sdiod_txglomsz = BRCMF_DEFAULT_TXGLOM_SIZE; |
| module_param_named(txglomsz, brcmf_sdiod_txglomsz, int, 0); |
| MODULE_PARM_DESC(txglomsz, "Maximum tx packet chain size [SDIO]"); |
| |
| /* Debug level configuration. See debug.h for bits, sysfs modifiable */ |
| int brcmf_msg_level; |
| module_param_named(debug, brcmf_msg_level, int, S_IRUSR | S_IWUSR); |
| MODULE_PARM_DESC(debug, "Level of debug output"); |
| |
| static int brcmf_p2p_enable; |
| module_param_named(p2pon, brcmf_p2p_enable, int, 0); |
| MODULE_PARM_DESC(p2pon, "Enable legacy p2p management functionality"); |
| |
| static int brcmf_feature_disable; |
| module_param_named(feature_disable, brcmf_feature_disable, int, 0); |
| MODULE_PARM_DESC(feature_disable, "Disable features"); |
| |
| static char brcmf_firmware_path[BRCMF_FW_ALTPATH_LEN]; |
| module_param_string(alternative_fw_path, brcmf_firmware_path, |
| BRCMF_FW_ALTPATH_LEN, S_IRUSR); |
| MODULE_PARM_DESC(alternative_fw_path, "Alternative firmware path"); |
| |
| static int brcmf_fcmode; |
| module_param_named(fcmode, brcmf_fcmode, int, 0); |
| MODULE_PARM_DESC(fcmode, "Mode of firmware signalled flow control"); |
| |
| static int brcmf_roamoff; |
| module_param_named(roamoff, brcmf_roamoff, int, S_IRUSR); |
| MODULE_PARM_DESC(roamoff, "Do not use internal roaming engine"); |
| |
| #ifdef DEBUG |
| /* always succeed brcmf_bus_started() */ |
| static int brcmf_ignore_probe_fail; |
| module_param_named(ignore_probe_fail, brcmf_ignore_probe_fail, int, 0); |
| MODULE_PARM_DESC(ignore_probe_fail, "always succeed probe for debugging"); |
| #endif |
| |
| /* Nest Sapphire-9229 mac addr */ |
| char nest_etheraddr[32] = {'\0'}; |
| module_param_string(nest_etheraddr, nest_etheraddr, 32, 0); |
| static int nest_set_macaddr(struct brcmf_if *ifp, char *value); |
| |
| static int nest_keepalive_interval = 0; |
| module_param(nest_keepalive_interval, int, 0); |
| static int nest_set_keepalive(struct brcmf_if *ifp, int value); |
| |
| /* NEST: enable keepalive debugging */ |
| int g_keepalive_debug = 0; |
| module_param(g_keepalive_debug, int, 0644); |
| |
| /* NEST: seconds of no beacons before declaring loss */ |
| int g_beacon_loss_timeout = 3; |
| module_param(g_beacon_loss_timeout, int, 0644); |
| |
| /* NEST: number of times for 43340 to SW retry assoc */ |
| int g_assoc_retry_max = 0; |
| module_param(g_assoc_retry_max, int, 0644); |
| |
| /* NEST: inactivity timer in milliseconds before device starts 802.11 PS */ |
| int g_pm2_sleep_ret = 200; |
| module_param(g_pm2_sleep_ret, int, 0644); |
| |
| int g_remote_ka_timeout = 390; /* number of seconds without seeing a TCPKLV before waking */ |
| module_param(g_remote_ka_timeout, int, 0644); |
| |
| int g_ampdu_enable = 1; /* if enable ampdu */ |
| module_param(g_ampdu_enable, int, 0644); |
| |
| int g_ampdu_mpdu = -1; /* Default power mode */ |
| module_param(g_ampdu_mpdu, int, 0644); |
| |
| int g_poll_sdio_bus = 0; |
| module_param(g_poll_sdio_bus, int, 0644); |
| |
| int dhd_power_mode = PM_FAST; /* Default power mode */ |
| module_param(dhd_power_mode, int, 0644); |
| |
| int g_4334x_pkt_filter = 1; /* Default 4334x pkt filter */ |
| module_param(g_4334x_pkt_filter, int, 0644); |
| |
| int g_buf_key_b4_m4 = 0; |
| module_param(g_buf_key_b4_m4, int, 0644); |
| |
| #define MAX_DEV_TYPE 10 |
| static const char nest_devtype[MAX_DEV_TYPE][4] = { |
| "NA", "D3", "O1", "F1", "QV2", "RQ1", "BQ1", "TQ1", "A2", "AG1" |
| }; |
| |
| int g_devtype = 0; /* device type defined by nest_devtype[] */ |
| module_param(g_devtype, int, 0644); |
| |
| char g_country_abbrev[BRCMF_COUNTRY_BUF_SZ] = {'\0'}; |
| module_param_string(g_country_abbrev, g_country_abbrev, BRCMF_COUNTRY_BUF_SZ, 0); |
| |
| int g_pfn_enable = 0; /* if enable pfn */ |
| module_param(g_pfn_enable, int, 0644); |
| |
| static struct brcmfmac_platform_data *brcmfmac_pdata; |
| struct brcmf_mp_global_t brcmf_mp_global; |
| |
| void brcmf_c_set_joinpref_default(struct brcmf_if *ifp) |
| { |
| struct brcmf_join_pref_params join_pref_params[2]; |
| int err; |
| |
| /* Setup join_pref to select target by RSSI (boost on 5GHz) */ |
| join_pref_params[0].type = BRCMF_JOIN_PREF_RSSI_DELTA; |
| join_pref_params[0].len = 2; |
| join_pref_params[0].rssi_gain = BRCMF_JOIN_PREF_RSSI_BOOST; |
| join_pref_params[0].band = WLC_BAND_5G; |
| |
| join_pref_params[1].type = BRCMF_JOIN_PREF_RSSI; |
| join_pref_params[1].len = 2; |
| join_pref_params[1].rssi_gain = 0; |
| join_pref_params[1].band = 0; |
| err = brcmf_fil_iovar_data_set(ifp, "join_pref", join_pref_params, |
| sizeof(join_pref_params)); |
| if (err) |
| brcmf_err("Set join_pref error (%d)\n", err); |
| } |
| |
| int brcmf_c_download_2_dongle(struct brcmf_if *ifp, char *dcmd, u16 flag, |
| u16 dload_type, char *dload_buf, u32 len) |
| { |
| struct brcmf_dload_data_le *dload_ptr; |
| u32 dload_data_offset; |
| u16 flags; |
| s32 err; |
| |
| dload_ptr = (struct brcmf_dload_data_le *)dload_buf; |
| dload_data_offset = offsetof(struct brcmf_dload_data_le, data); |
| flags = flag | (DLOAD_HANDLER_VER << DLOAD_FLAG_VER_SHIFT); |
| |
| dload_ptr->flag = cpu_to_le16(flags); |
| dload_ptr->dload_type = cpu_to_le16(dload_type); |
| dload_ptr->len = cpu_to_le32(len - dload_data_offset); |
| dload_ptr->crc = cpu_to_le32(0); |
| len = len + 8 - (len % 8); |
| |
| err = brcmf_fil_iovar_data_set(ifp, dcmd, (void *)dload_buf, len); |
| |
| return err; |
| } |
| |
| int brcmf_c_get_clm_name(struct brcmf_if *ifp, u8 *clm_name) |
| { |
| struct brcmf_rev_info_le revinfo; |
| struct brcmf_bus *bus = ifp->drvr->bus_if; |
| u8 fw_name[BRCMF_FW_NAME_LEN]; |
| u8 *ptr; |
| size_t len; |
| u32 chipnum; |
| u32 chiprev; |
| s32 err; |
| |
| err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_REVINFO, &revinfo, |
| sizeof(revinfo)); |
| if (err < 0) { |
| brcmf_err("retrieving revision info failed (%d)\n", err); |
| goto done; |
| } |
| |
| chipnum = le32_to_cpu(revinfo.chipnum); |
| chiprev = le32_to_cpu(revinfo.chiprev); |
| |
| memset(fw_name, 0, BRCMF_FW_NAME_LEN); |
| err = brcmf_bus_get_fwname(bus, chipnum, chiprev, fw_name); |
| if (err) { |
| brcmf_err("get firmware name failed (%d)\n", err); |
| goto done; |
| } |
| |
| /* generate CLM blob file name */ |
| ptr = strrchr(fw_name, '.'); |
| len = ptr - fw_name + 1; |
| if (len + strlen(".clm_blob") > BRCMF_FW_NAME_LEN) { |
| err = -E2BIG; |
| } else { |
| strlcpy(clm_name, fw_name, len); |
| strlcat(clm_name, ".clm_blob", BRCMF_FW_NAME_LEN); |
| } |
| done: |
| return err; |
| } |
| |
| int brcmf_c_process_clm_blob(struct brcmf_if *ifp) |
| { |
| struct device *dev = ifp->drvr->bus_if->dev; |
| const struct firmware *clm = NULL; |
| u8 buf[BRCMF_DCMD_SMLEN]; |
| u8 clm_name[BRCMF_FW_NAME_LEN]; |
| u32 data_offset; |
| u32 size2alloc; |
| u8 *chunk_buf; |
| u32 chunk_len; |
| u32 datalen; |
| u32 cumulative_len = 0; |
| u16 dl_flag = DL_BEGIN; |
| s32 err; |
| |
| brcmf_dbg(INFO, "Enter\n"); |
| |
| memset(clm_name, 0, BRCMF_FW_NAME_LEN); |
| err = brcmf_c_get_clm_name(ifp, clm_name); |
| if (err) { |
| brcmf_err("get CLM blob file name failed (%d)\n", err); |
| return err; |
| } |
| |
| err = request_firmware_direct(&clm, clm_name, dev); |
| if (err) { |
| if (err == -ENOENT || err == -ENOMEM) |
| return 0; |
| brcmf_err("request CLM blob file failed (%d)\n", err); |
| return err; |
| } |
| |
| datalen = clm->size; |
| data_offset = offsetof(struct brcmf_dload_data_le, data); |
| size2alloc = data_offset + MAX_CHUNK_LEN; |
| |
| chunk_buf = kzalloc(size2alloc, GFP_KERNEL); |
| if (!chunk_buf) { |
| err = -ENOMEM; |
| goto done; |
| } |
| |
| do { |
| if (datalen > MAX_CHUNK_LEN) { |
| chunk_len = MAX_CHUNK_LEN; |
| } else { |
| chunk_len = datalen; |
| dl_flag |= DL_END; |
| } |
| |
| memcpy(chunk_buf + data_offset, clm->data + cumulative_len, |
| chunk_len); |
| |
| err = brcmf_c_download_2_dongle(ifp, "clmload", dl_flag, |
| DL_TYPE_CLM, chunk_buf, |
| data_offset + chunk_len); |
| |
| dl_flag &= ~DL_BEGIN; |
| |
| cumulative_len += chunk_len; |
| datalen -= chunk_len; |
| } while ((datalen > 0) && (err == 0)); |
| |
| if (err) { |
| brcmf_err("clmload (%d byte file) failed (%d); ", |
| (u32)clm->size, err); |
| /* Retrieve clmload_status and print */ |
| memset(buf, 0, BRCMF_DCMD_SMLEN); |
| err = brcmf_fil_iovar_data_get(ifp, "clmload_status", buf, |
| BRCMF_DCMD_SMLEN); |
| if (err) |
| brcmf_err("get clmload_status failed (%d)\n", err); |
| else |
| brcmf_err("clmload_status=%d\n", *((int *)buf)); |
| err = -EIO; |
| } |
| |
| kfree(chunk_buf); |
| done: |
| release_firmware(clm); |
| return err; |
| } |
| |
| extern s32 brcmf_translate_country_code(struct brcmf_pub *drvr, char alpha2[2], struct brcmf_fil_country_le *ccreq); |
| extern s32 brcmf_set_restricted_band(struct brcmf_if *ifp, struct brcmf_fil_country_le *ccreq); |
| |
| int brcmf_c_preinit_dcmds(struct brcmf_if *ifp) |
| { |
| s8 eventmask[BRCMF_EVENTING_MASK_LEN]; |
| u8 buf[BRCMF_DCMD_SMLEN]; |
| struct brcmf_rev_info_le revinfo; |
| struct brcmf_rev_info *ri; |
| char *clmver; |
| char *ptr; |
| s32 err; |
| |
| /* Do any CLM downloading */ |
| err = brcmf_c_process_clm_blob(ifp); |
| if (err < 0) { |
| brcmf_err("download CLM blob file failed, %d\n", err); |
| goto done; |
| } |
| |
| /* Nest SAPPHIRE-9229 */ |
| if (nest_etheraddr[0] && ((err = nest_set_macaddr(ifp, nest_etheraddr))) < 0) { |
| brcmf_err("error=%d, nest_etheraddr=%s\n", err, nest_etheraddr); |
| } |
| |
| /* retreive mac address */ |
| err = brcmf_fil_iovar_data_get(ifp, "cur_etheraddr", ifp->mac_addr, |
| sizeof(ifp->mac_addr)); |
| if (err < 0) { |
| brcmf_err("Retreiving cur_etheraddr failed, %d\n", err); |
| goto done; |
| } |
| memcpy(ifp->drvr->mac, ifp->mac_addr, sizeof(ifp->drvr->mac)); |
| |
| err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_REVINFO, |
| &revinfo, sizeof(revinfo)); |
| ri = &ifp->drvr->revinfo; |
| if (err < 0) { |
| brcmf_err("retrieving revision info failed, %d\n", err); |
| } else { |
| ri->vendorid = le32_to_cpu(revinfo.vendorid); |
| ri->deviceid = le32_to_cpu(revinfo.deviceid); |
| ri->radiorev = le32_to_cpu(revinfo.radiorev); |
| ri->chiprev = le32_to_cpu(revinfo.chiprev); |
| ri->corerev = le32_to_cpu(revinfo.corerev); |
| ri->boardid = le32_to_cpu(revinfo.boardid); |
| ri->boardvendor = le32_to_cpu(revinfo.boardvendor); |
| ri->boardrev = le32_to_cpu(revinfo.boardrev); |
| ri->driverrev = le32_to_cpu(revinfo.driverrev); |
| ri->ucoderev = le32_to_cpu(revinfo.ucoderev); |
| ri->bus = le32_to_cpu(revinfo.bus); |
| ri->chipnum = le32_to_cpu(revinfo.chipnum); |
| ri->phytype = le32_to_cpu(revinfo.phytype); |
| ri->phyrev = le32_to_cpu(revinfo.phyrev); |
| ri->anarev = le32_to_cpu(revinfo.anarev); |
| ri->chippkg = le32_to_cpu(revinfo.chippkg); |
| ri->nvramrev = le32_to_cpu(revinfo.nvramrev); |
| } |
| ri->result = err; |
| |
| /* query for 'ver' to get version info from firmware */ |
| memset(buf, 0, sizeof(buf)); |
| strcpy(buf, "ver"); |
| err = brcmf_fil_iovar_data_get(ifp, "ver", buf, sizeof(buf)); |
| if (err < 0) { |
| brcmf_err("Retreiving version information failed, %d\n", |
| err); |
| goto done; |
| } |
| ptr = (char *)buf; |
| strsep(&ptr, "\n"); |
| |
| /* Print fw version info */ |
| brcmf_info("Firmware version = %s\n", buf); |
| |
| /* locate firmware version number for ethtool */ |
| ptr = strrchr(buf, ' ') + 1; |
| strlcpy(ifp->drvr->fwver, ptr, sizeof(ifp->drvr->fwver)); |
| |
| /* Query for 'clmver' to get CLM version info from firmware */ |
| memset(buf, 0, sizeof(buf)); |
| err = brcmf_fil_iovar_data_get(ifp, "clmver", buf, sizeof(buf)); |
| if (err) { |
| if (err == -23) { |
| brcmf_dbg(INFO, "clmver iovar unsupported, %d\n", err); |
| } else { |
| brcmf_err("retrieving clmver failed, %d\n", err); |
| goto done; |
| } |
| } else { |
| clmver = (char *)buf; |
| /* Replace all newline/linefeed characters with space |
| * character |
| */ |
| ptr = clmver; |
| while ((ptr = strchr(ptr, '\n')) != NULL) |
| *ptr = ' '; |
| |
| brcmf_err("CLM version = %s\n", clmver); |
| } |
| |
| /* set mpc */ |
| err = brcmf_fil_iovar_int_set(ifp, "mpc", 1); |
| if (err) { |
| brcmf_err("failed setting mpc\n"); |
| goto done; |
| } |
| |
| brcmf_c_set_joinpref_default(ifp); |
| |
| /* Setup event_msgs, enable E_IF */ |
| err = brcmf_fil_iovar_data_get(ifp, "event_msgs", eventmask, |
| BRCMF_EVENTING_MASK_LEN); |
| if (err) { |
| brcmf_err("Get event_msgs error (%d)\n", err); |
| goto done; |
| } |
| setbit(eventmask, BRCMF_E_IF); |
| err = brcmf_fil_iovar_data_set(ifp, "event_msgs", eventmask, |
| BRCMF_EVENTING_MASK_LEN); |
| if (err) { |
| brcmf_err("Set event_msgs error (%d)\n", err); |
| goto done; |
| } |
| |
| /* Setup default scan channel time */ |
| err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_CHANNEL_TIME, |
| BRCMF_DEFAULT_SCAN_CHANNEL_TIME); |
| if (err) { |
| brcmf_err("BRCMF_C_SET_SCAN_CHANNEL_TIME error (%d)\n", |
| err); |
| goto done; |
| } |
| |
| /* Setup default scan unassoc time */ |
| err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_UNASSOC_TIME, |
| BRCMF_DEFAULT_SCAN_UNASSOC_TIME); |
| if (err) { |
| brcmf_err("BRCMF_C_SET_SCAN_UNASSOC_TIME error (%d)\n", |
| err); |
| goto done; |
| } |
| |
| /* Enable tx beamforming, errors can be ignored (not supported) */ |
| (void)brcmf_fil_iovar_int_set(ifp, "txbf", 1); |
| |
| if (nest_keepalive_interval && ((err = nest_set_keepalive(ifp, nest_keepalive_interval))) < 0) { |
| brcmf_err("error=%d, nest_keepalive_interval=%d\n", err, nest_keepalive_interval); |
| goto done; |
| } |
| |
| /* All Nest-added configurable parameters and their values */ |
| brcmf_info("g_keepalive_debug %d\n", g_keepalive_debug); |
| brcmf_info("g_beacon_loss_timeout %d\n", g_beacon_loss_timeout); |
| brcmf_info("g_assoc_retry_max %d\n", g_assoc_retry_max); |
| brcmf_info("g_pm2_sleep_ret %d\n", g_pm2_sleep_ret); |
| brcmf_info("g_remote_ka_timeout = %d\n", g_remote_ka_timeout); |
| brcmf_info("nest_etheraddr[] = %s\n", nest_etheraddr); |
| if (g_devtype < 0 || g_devtype >= MAX_DEV_TYPE) { |
| brcmf_err("UNKNOWN DEVICE TYPE [%d] (set to default type = 0)!!!\n", g_devtype); |
| g_devtype = 0; |
| } |
| brcmf_info("g_devtype = %s\n", nest_devtype[g_devtype]); |
| brcmf_info("g_ampdu_enable = %d\n", g_ampdu_enable); |
| brcmf_info("g_ampdu_mpdu = %d\n", g_ampdu_mpdu); |
| brcmf_info("g_poll_sdio_bus = %d\n", g_poll_sdio_bus); |
| brcmf_info("dhd_power_mode = %d\n", dhd_power_mode); |
| brcmf_info("g_buf_key_b4_m4 = %d\n", g_buf_key_b4_m4); |
| brcmf_info("g_country_abbrev = %s\n", g_country_abbrev); |
| brcmf_info("g_4334x_pkt_filter = %d\n", g_4334x_pkt_filter); |
| brcmf_info("g_pfn_enable = %d\n", g_pfn_enable); |
| |
| err = brcmf_fil_iovar_int_set(ifp, "assoc_retry_max", g_assoc_retry_max); |
| if (err < 0) |
| brcmf_err("Set assoc_retry_max failed %d\n", err); |
| |
| err = brcmf_fil_iovar_int_set(ifp, "pm2_sleep_ret", g_pm2_sleep_ret); |
| if (err < 0) |
| brcmf_err("Set pm2_sleep_ret failed %d\n", err); |
| |
| if (g_ampdu_enable == 0) { |
| /* only send iovar when disable */ |
| err = brcmf_fil_iovar_int_set(ifp, "ampdu", g_ampdu_enable); |
| if (err < 0) |
| brcmf_err("Set g_ampdu_enable failed %d\n", err); |
| } |
| |
| if (g_ampdu_mpdu != -1) { |
| err = brcmf_fil_iovar_int_set(ifp, "ampdu_mpdu", g_ampdu_mpdu); |
| if (err < 0) |
| brcmf_err("Set g_ampdu_mpdu failed %d\n", err); |
| } |
| |
| if (g_buf_key_b4_m4 != 0) { |
| err = brcmf_fil_iovar_int_set(ifp, "buf_key_b4_m4", g_buf_key_b4_m4); |
| if (err < 0) |
| brcmf_err("Set g_buf_key_b4_m4 failed %d\n", err); |
| } |
| |
| if (g_country_abbrev[0] != 0) { |
| struct brcmf_fil_country_le ccreq; |
| |
| memset(&ccreq, 0, sizeof(ccreq)); |
| |
| err = brcmf_translate_country_code(ifp->drvr, g_country_abbrev, &ccreq); |
| if (err) |
| brcmf_err("Translate country %s failed %d\n", g_country_abbrev, err); |
| else { |
| err = brcmf_fil_iovar_data_set(ifp, "country", &ccreq, sizeof(ccreq)); |
| if (err) |
| brcmf_err("Firmware rejected country setting\n"); |
| else |
| brcmf_info("Set country to abbrev %s ccode %s success\n", ccreq.country_abbrev, ccreq.ccode); |
| } |
| |
| brcmf_set_restricted_band(ifp, &ccreq); |
| } |
| |
| /* do bus specific preinit here */ |
| err = brcmf_bus_preinit(ifp->drvr->bus_if); |
| done: |
| return err; |
| } |
| |
| /* Nest SAPPHIRE-9229 */ |
| static int nest_set_macaddr(struct brcmf_if *ifp, char *value) |
| { |
| char buf[ETH_ALEN], *a = value, *c = NULL; |
| int i = 0, err; |
| |
| memset(buf, 0, ETH_ALEN); |
| for (;;) { |
| buf[i++] = (char)simple_strtoul(a, &c, 16); |
| if (!*c++ || i == ETH_ALEN) |
| break; |
| a = c; |
| } |
| |
| if (i != ETH_ALEN) { |
| brcmf_err("Wrong Macaddr %s\n", value); |
| err = -1; |
| goto exit; |
| } |
| |
| err = memcmp(buf, ifp->drvr->mac, ETH_ALEN); |
| if (err == 0) { |
| brcmf_err("Same Macaddr\n"); |
| goto exit; |
| } |
| |
| brcmf_info("Change Macaddr = %02X:%02X:%02X:%02X:%02X:%02X\n", |
| buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]); |
| |
| err = brcmf_fil_iovar_data_set(ifp, "cur_etheraddr", buf, ETH_ALEN); |
| if (err < 0) { |
| brcmf_err("Setting cur_etheraddr failed, %d\n", err); |
| } else { |
| memcpy(ifp->mac_addr, buf, ETH_ALEN); |
| memcpy(ifp->drvr->mac, buf, ETH_ALEN); |
| } |
| |
| exit: |
| return err; |
| } |
| |
| static int nest_set_keepalive(struct brcmf_if *ifp, int value) |
| { |
| wl_mkeep_alive_pkt_t mkeep_alive_pkt = {0}; |
| int res = -1; |
| |
| brcmf_dbg(TRACE, ("execution\n")); |
| |
| mkeep_alive_pkt.period_msec = cpu_to_le32(value); |
| mkeep_alive_pkt.version = cpu_to_le16(WL_MKEEP_ALIVE_VERSION); |
| mkeep_alive_pkt.length = cpu_to_le16(WL_MKEEP_ALIVE_FIXED_LEN); |
| /* Setup keep alive zero for null packet generation */ |
| mkeep_alive_pkt.keep_alive_id = 0; |
| mkeep_alive_pkt.len_bytes = 0; |
| memset(mkeep_alive_pkt.data, 0, sizeof(mkeep_alive_pkt.data)); |
| |
| res = brcmf_fil_iovar_data_set(ifp, "mkeep_alive", &mkeep_alive_pkt, sizeof(mkeep_alive_pkt)); |
| if (res < 0) { |
| brcmf_err("Setting mkeep_alive failed, %d\n", res); |
| } |
| |
| return res; |
| } |
| |
| #ifndef CPTCFG_BRCM_TRACING |
| void __brcmf_err(const char *func, const char *fmt, ...) |
| { |
| struct va_format vaf; |
| va_list args; |
| |
| va_start(args, fmt); |
| |
| vaf.fmt = fmt; |
| vaf.va = &args; |
| pr_err("%s: %pV", func, &vaf); |
| |
| va_end(args); |
| } |
| #endif |
| |
| #if defined(CPTCFG_BRCM_TRACING) || defined(CPTCFG_BRCMDBG) |
| void __brcmf_dbg(u32 level, const char *func, const char *fmt, ...) |
| { |
| struct va_format vaf = { |
| .fmt = fmt, |
| }; |
| va_list args; |
| |
| va_start(args, fmt); |
| vaf.va = &args; |
| if (brcmf_msg_level & level) |
| pr_info("%s %pV", func, &vaf); |
| trace_brcmf_dbg(level, func, &vaf); |
| va_end(args); |
| } |
| #endif |
| |
| static void brcmf_mp_attach(void) |
| { |
| /* If module param firmware path is set then this will always be used, |
| * if not set then if available use the platform data version. To make |
| * sure it gets initialized at all, always copy the module param version |
| */ |
| strlcpy(brcmf_mp_global.firmware_path, brcmf_firmware_path, |
| BRCMF_FW_ALTPATH_LEN); |
| if ((brcmfmac_pdata) && (brcmfmac_pdata->fw_alternative_path) && |
| (brcmf_mp_global.firmware_path[0] == '\0')) { |
| strlcpy(brcmf_mp_global.firmware_path, |
| brcmfmac_pdata->fw_alternative_path, |
| BRCMF_FW_ALTPATH_LEN); |
| } |
| } |
| |
| struct brcmf_mp_device *brcmf_get_module_param(struct device *dev, |
| enum brcmf_bus_type bus_type, |
| u32 chip, u32 chiprev) |
| { |
| struct brcmf_mp_device *settings; |
| struct brcmfmac_pd_device *device_pd; |
| bool found; |
| int i; |
| |
| brcmf_dbg(INFO, "Enter, bus=%d, chip=%d, rev=%d\n", bus_type, chip, |
| chiprev); |
| settings = kzalloc(sizeof(*settings), GFP_ATOMIC); |
| if (!settings) |
| return NULL; |
| |
| /* start by using the module paramaters */ |
| settings->p2p_enable = !!brcmf_p2p_enable; |
| settings->feature_disable = brcmf_feature_disable; |
| settings->fcmode = brcmf_fcmode; |
| settings->roamoff = !!brcmf_roamoff; |
| #ifdef DEBUG |
| settings->ignore_probe_fail = !!brcmf_ignore_probe_fail; |
| #endif |
| |
| if (bus_type == BRCMF_BUSTYPE_SDIO) |
| settings->bus.sdio.txglomsz = brcmf_sdiod_txglomsz; |
| |
| /* See if there is any device specific platform data configured */ |
| found = false; |
| if (brcmfmac_pdata) { |
| for (i = 0; i < brcmfmac_pdata->device_count; i++) { |
| device_pd = &brcmfmac_pdata->devices[i]; |
| if ((device_pd->bus_type == bus_type) && |
| (device_pd->id == chip) && |
| ((device_pd->rev == chiprev) || |
| (device_pd->rev == -1))) { |
| brcmf_dbg(INFO, "Platform data for device found\n"); |
| settings->country_codes = |
| device_pd->country_codes; |
| if (device_pd->bus_type == BRCMF_BUSTYPE_SDIO) |
| memcpy(&settings->bus.sdio, |
| &device_pd->bus.sdio, |
| sizeof(settings->bus.sdio)); |
| found = true; |
| break; |
| } |
| } |
| } |
| if (!found) { |
| /* No platform data for this device, try OF (Open Firwmare) */ |
| brcmf_of_probe(dev, bus_type, settings); |
| } |
| return settings; |
| } |
| |
| void brcmf_release_module_param(struct brcmf_mp_device *module_param) |
| { |
| kfree(module_param); |
| } |
| |
| static int __init brcmf_common_pd_probe(struct platform_device *pdev) |
| { |
| brcmf_dbg(INFO, "Enter\n"); |
| |
| brcmfmac_pdata = dev_get_platdata(&pdev->dev); |
| |
| if (brcmfmac_pdata->power_on) |
| brcmfmac_pdata->power_on(); |
| |
| return 0; |
| } |
| |
| static int brcmf_common_pd_remove(struct platform_device *pdev) |
| { |
| brcmf_dbg(INFO, "Enter\n"); |
| |
| if (brcmfmac_pdata->power_off) |
| brcmfmac_pdata->power_off(); |
| |
| return 0; |
| } |
| |
| static struct platform_driver brcmf_pd = { |
| .remove = brcmf_common_pd_remove, |
| .driver = { |
| .name = BRCMFMAC_PDATA_NAME, |
| } |
| }; |
| |
| static int __init brcmfmac_module_init(void) |
| { |
| int err; |
| |
| /* Initialize debug system first */ |
| brcmf_debugfs_init(); |
| |
| /* Get the platform data (if available) for our devices */ |
| err = platform_driver_probe(&brcmf_pd, brcmf_common_pd_probe); |
| if (err == -ENODEV) |
| brcmf_dbg(INFO, "No platform data available.\n"); |
| |
| /* Initialize global module paramaters */ |
| brcmf_mp_attach(); |
| |
| /* Continue the initialization by registering the different busses */ |
| err = brcmf_core_init(); |
| if (err) { |
| brcmf_debugfs_exit(); |
| if (brcmfmac_pdata) |
| platform_driver_unregister(&brcmf_pd); |
| } |
| |
| return err; |
| } |
| |
| static void __exit brcmfmac_module_exit(void) |
| { |
| brcmf_core_exit(); |
| if (brcmfmac_pdata) |
| platform_driver_unregister(&brcmf_pd); |
| brcmf_debugfs_exit(); |
| } |
| |
| module_init(brcmfmac_module_init); |
| module_exit(brcmfmac_module_exit); |
| |