模组broadcom 6356s 蓝牙wifi双模
wifi驱动的通用的软件架构
分为两部分,上面为主机端驱动,下面是我们之前所说的firmware
其中固件部分的主要工作是:因为天线接受和发送回来的都是802.11帧的帧,而主机接受和传送出来的数据都必须是802.3的帧,所以必须由firmware来负责802.3的帧和802.11帧之间的转换
当天线收到数据,并被firmware处理好后会放在一个buffer里,并产生一个中断,主机在收到中断后就去读这个buffer
代码分析:/ap6xxx/bcmdhd.1.363.59.144.x.cn
Firmware:
WiFi芯片内部有一个小系统,用来运行802.11协议,此部分代码就叫Firmware。有些芯片(例如 broadcom)的Firmware是以文件的形式存放的,有些芯片(例如 realteck)的Firmware是做到驱动代码中的。
Nvram:
WiFi芯片需要作相应的RF参数校准,校准值等信息一般放到到Nvram中。例如,同一个芯片bcm4330,做成不同的模块时,需要不同的Nvram。另外,有些芯片(例如realtek)将RF参数校准等信息写到芯片的EEPROM中,这部分工作在模块出厂时完成
WiFi芯片工作前,需要host先下载Firmware文件到WiFi芯片中,此部分工作在WiFi驱动中完成。
Broadcom WLAN模块同样存在着一个至关重要的文件:bcmdhd.cal,该文件定义了针对WLAN模块的NV值
路径:
Firmware与Nvram文件存放于external/wlan_loader/firmware/目录中,最终被编译到系统的/system/etc/firmware
WLAN Module工作的3种模式
(1)Station
(2)AP
(3)P2P
Broadcom WLAN Module所使用的2种Firmware
(1)fw_bcmdhd.bin
(2)fw_bcmdhd_apsta.bin
WLAN Module工作模式与固件的对应关系
(1)Station和P2P模式使用的固件相同,均为fw_bcmdhd.bin
(2)AP模式使用的固件为fw_bcmdhd_apsta.bin
扫描,ftrace信息
broadcom的驱动与标准不一样但大同小异:
事件来了的时候产生中断:
系统产生中断handle_irq_event_percpu
进入中断处理函数:wlan_oob_irq
调用数据处理函数:dhd_sched_dpc-》dhd_dpc_thread-》dhd_dpc_thread-》dhdsdio_readframes-》dhd_rx_frame-》
/* Process special event packets and then discard them */memset(&event, 0, sizeof(event));//非数据处理即事件如扫描完成,断开,加密出错等事件if (ntoh16(skb->protocol) == ETHER_TYPE_BRCM) {dhd_wl_host_event(dhd, &ifidx,
- 1
- 2
- 3
- 4
dhd_wl_host_event-》
wl_host_event
wl_iw_event通知上层(驱动以上)
wl_cfg80211_event驱动处理 {
DHD_EVENT_WAKE_LOCK(cfg->pub);
if (likely(!wl_enq_event(cfg, ndev, event_type, e, data))) {//入队
wl_wakeup_event(cfg);//唤醒处理线程
} else {
DHD_EVENT_WAKE_UNLOCK(cfg->pub);
}
}
上面打开网口时起了一个线程并睡眠
PROC_START(wl_event_handler, cfg, &cfg->event_tsk, 0, “wl_event_handler”);
static s32 wl_event_handler(void *data)
{struct bcm_cfg80211 *cfg = NULL;struct wl_event_q *e;tsk_ctl_t *tsk = (tsk_ctl_t *)data;struct wireless_dev *wdev = NULL;cfg = (struct bcm_cfg80211 *)tsk->parent;printf("tsk Enter, tsk = 0x%p\n", tsk);while (down_interruptible (&tsk->sema) == 0) {//由于来传来的参数是&cfg->event_tsk,此处&tsk->sema刚好是 &cfg->event_tsk.sema/*static void wl_wakeup_event(struct bcm_cfg80211 *cfg)
{dhd_pub_t *dhd = (dhd_pub_t *)(cfg->pub);if (dhd->up && (cfg->event_tsk.thr_pid >= 0)) {up(&cfg->event_tsk.sema);唤醒进程}
} * /SMP_RD_BARRIER_DEPENDS();if (tsk->terminated) {break;}while ((e = wl_deq_event(cfg))) {//出队,将刚接收到的事件取出WL_DBG(("event type (%d), ifidx: %d bssidx: %d \n",e->etype, e->emsg.ifidx, e->emsg.bsscfgidx));if (e->emsg.ifidx > WL_MAX_IFS) {WL_ERR((" Event ifidx not in range. val:%d \n", e->emsg.ifidx));goto fail;}if (!(wdev = wl_get_wdev_by_bssidx(cfg, e->emsg.bsscfgidx))) {/* For WLC_E_IF would be handled by wl_host_event */if (e->etype != WLC_E_IF)WL_ERR(("No wdev corresponding to bssidx: 0x%x found!"" Ignoring event.\n", e->emsg.bsscfgidx));} else if (e->etype < WLC_E_LAST && cfg->evt_handler[e->etype]) {dhd_pub_t *dhd = (struct dhd_pub *)(cfg->pub);if (dhd->busstate == DHD_BUS_DOWN) {WL_ERR((": BUS is DOWN.\n"));} else {
#ifdef DHD_IFDEBUGif (cfg->iface_cnt == 0) {wl_dump_ifinfo(cfg);}
#endifcfg->evt_handler[e->etype](cfg, wdev_to_cfgdev(wdev),&e->emsg, e->edata);//调用处理事件的函数,这是一个钩子函数}} else {WL_DBG(("Unknown Event (%d): ignoring\n", e->etype));}
fail:wl_put_event(e);DHD_EVENT_WAKE_UNLOCK(cfg->pub);}}printf("%s: was terminated\n", __FUNCTION__);complete_and_exit(&tsk->completed, 0);return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
cfg->evt_handler[e->etype](cfg, wdev_to_cfgdev(wdev),&e->emsg, e->edata);
- 1
- 2
- 3
在wl_init_event_handler中注册回调函数
static void wl_init_event_handler(struct bcm_cfg80211 *cfg)
{memset(cfg->evt_handler, 0, sizeof(cfg->evt_handler));cfg->evt_handler[WLC_E_SCAN_COMPLETE] = wl_notify_scan_status;cfg->evt_handler[WLC_E_AUTH] = wl_notify_connect_status;cfg->evt_handler[WLC_E_ASSOC] = wl_notify_connect_status;cfg->evt_handler[WLC_E_LINK] = wl_notify_connect_status;cfg->evt_handler[WLC_E_DEAUTH_IND] = wl_notify_connect_status;cfg->evt_handler[WLC_E_DEAUTH] = wl_notify_connect_status;cfg->evt_handler[WLC_E_DISASSOC_IND] = wl_notify_connect_status;cfg->evt_handler[WLC_E_ASSOC_IND] = wl_notify_connect_status;cfg->evt_handler[WLC_E_REASSOC_IND] = wl_notify_connect_status;cfg->evt_handler[WLC_E_ROAM] = wl_notify_roaming_status;cfg->evt_handler[WLC_E_MIC_ERROR] = wl_notify_mic_status;cfg->evt_handler[WLC_E_SET_SSID] = wl_notify_connect_status;cfg->evt_handler[WLC_E_ACTION_FRAME_RX] = wl_notify_rx_mgmt_frame;cfg->evt_handler[WLC_E_PROBREQ_MSG] = wl_notify_rx_mgmt_frame;cfg->evt_handler[WLC_E_P2P_PROBREQ_MSG] = wl_notify_rx_mgmt_frame;cfg->evt_handler[WLC_E_P2P_DISC_LISTEN_COMPLETE] = wl_cfgp2p_listen_complete;cfg->evt_handler[WLC_E_ACTION_FRAME_COMPLETE] = wl_cfgp2p_action_tx_complete;cfg->evt_handler[WLC_E_ACTION_FRAME_OFF_CHAN_COMPLETE] = wl_cfgp2p_action_tx_complete;cfg->evt_handler[WLC_E_JOIN] = wl_notify_connect_status;cfg->evt_handler[WLC_E_START] = wl_notify_connect_status;
#ifdef PNO_SUPPORTcfg->evt_handler[WLC_E_PFN_NET_FOUND] = wl_notify_pfn_status;
#endif /* PNO_SUPPORT */
#ifdef GSCAN_SUPPORTcfg->evt_handler[WLC_E_PFN_BEST_BATCHING] = wl_notify_gscan_event;cfg->evt_handler[WLC_E_PFN_SCAN_COMPLETE] = wl_notify_gscan_event;cfg->evt_handler[WLC_E_PFN_GSCAN_FULL_RESULT] = wl_notify_gscan_event;cfg->evt_handler[WLC_E_PFN_SWC] = wl_notify_gscan_event;cfg->evt_handler[WLC_E_PFN_BSSID_NET_FOUND] = wl_notify_gscan_event;cfg->evt_handler[WLC_E_PFN_BSSID_NET_LOST] = wl_notify_gscan_event;
#endif /* GSCAN_SUPPORT */
#ifdef WLTDLScfg->evt_handler[WLC_E_TDLS_PEER_EVENT] = wl_tdls_event_handler;
#endif /* WLTDLS */cfg->evt_handler[WLC_E_BSSID] = wl_notify_roaming_status;
#ifdef WL_RELMCASTcfg->evt_handler[WLC_E_RMC_EVENT] = wl_notify_rmc_status;
#endif
#ifdef BT_WIFI_HANDOVERcfg->evt_handler[WLC_E_BT_WIFI_HANDOVER_REQ] = wl_notify_bt_wifi_handover_req;
#endif
#ifdef WL_NANcfg->evt_handler[WLC_E_NAN] = wl_cfgnan_notify_nan_status;cfg->evt_handler[WLC_E_PROXD] = wl_cfgnan_notify_proxd_status;
#endif /* WL_NAN */cfg->evt_handler[WLC_E_CSA_COMPLETE_IND] = wl_csa_complete_ind;
#ifdef DHD_LOSSLESS_ROAMINGcfg->evt_handler[WLC_E_ROAM_PREP] = wl_notify_roam_prep_status;
#endifcfg->evt_handler[WLC_E_AP_STARTED] = wl_ap_start_ind;
#ifdef CUSTOM_EVENT_PM_WAKEcfg->evt_handler[WLC_E_EXCESS_PM_WAKE_EVENT] = wl_check_pmstatus;
#endif /* CUSTOM_EVENT_PM_WAKE */cfg->evt_handler[WLC_E_PSK_SUP] = wl_notify_idsup_status;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
如扫描完成调用
wl_notify_scan_status
static s32
wl_notify_scan_status(struct bcm_cfg80211 *cfg, bcm_struct_cfgdev *cfgdev,const wl_event_msg_t *e, void *data)
{struct channel_info channel_inform;struct wl_scan_results *bss_list;struct net_device *ndev = NULL;u32 len = WL_SCAN_BUF_MAX;s32 err = 0;unsigned long flags;#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0))struct cfg80211_scan_info info;
#endifWL_DBG(("Enter \n"));if (!wl_get_drv_status(cfg, SCANNING, ndev)) {WL_ERR(("scan is not ready \n"));return err;}ndev = cfgdev_to_wlc_ndev(cfgdev, cfg);mutex_lock(&cfg->usr_sync);wl_clr_drv_status(cfg, SCANNING, ndev);err = wldev_ioctl(ndev, WLC_GET_CHANNEL, &channel_inform,sizeof(channel_inform), false);//获取信道信息if (unlikely(err)) {WL_ERR(("scan busy (%d)\n", err));goto scan_done_out;}channel_inform.scan_channel = dtoh32(channel_inform.scan_channel);if (unlikely(channel_inform.scan_channel)) {WL_DBG(("channel_inform.scan_channel (%d)\n",channel_inform.scan_channel));}cfg->bss_list = cfg->scan_results;bss_list = cfg->bss_list;memset(bss_list, 0, len);bss_list->buflen = htod32(len);err = wldev_ioctl(ndev, WLC_SCAN_RESULTS, bss_list, len, false);//获取扫描结果if (unlikely(err) && unlikely(!cfg->scan_suppressed)) {WL_ERR(("%s Scan_results error (%d)\n", ndev->name, err));err = -EINVAL;goto scan_done_out;}bss_list->buflen = dtoh32(bss_list->buflen);bss_list->version = dtoh32(bss_list->version);bss_list->count = dtoh32(bss_list->count);err = wl_inform_bss(cfg);//更新bss信息,最后调用到cfg80211_inform_bss_frame更新内核无线子系统的bss信息scan_done_out:del_timer_sync(&cfg->scan_timeout);spin_lock_irqsave(&cfg->cfgdrv_lock, flags);if (cfg->scan_request) {
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0))info.aborted = false;cfg80211_scan_done(cfg->scan_request, &info);
#elsecfg80211_scan_done(cfg->scan_request, false);//通知内核无线子系统扫描完成,让上层去获取扫描结果等操作
#endifcfg->scan_request = NULL;}spin_unlock_irqrestore(&cfg->cfgdrv_lock, flags);WL_DBG(("cfg80211_scan_done\n"));mutex_unlock(&cfg->usr_sync);return err;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
wpa_supplicant-4774 [002] ...1 5463.125488: wl_cfg80211_scan <-nl80211_trigger_scanwpa_supplicant-4774 [002] ...1 5463.125495: wl_cfg_multip2p_operational <-wl_cfg80211_scanwpa_supplicant-4774 [002] ...1 5463.125498: wl_cfg80211_get_remain_on_channel_ndev <-wl_cfg80211_scanwpa_supplicant-4774 [002] ...1 5463.125504: wl_cfgp2p_discover_enable_search <-wl_cfg80211_scanwpa_supplicant-4774 [002] ...1 5463.125506: wl_cfg80211_set_mgmt_vndr_ies <-wl_cfg80211_scanwpa_supplicant-4774 [002] ...1 5463.125509: wl_cfg80211_parse_vndr_ies <-wl_cfg80211_set_mgmt_vndr_ieswpa_supplicant-4774 [002] ...1 5463.125511: bcm_next_tlv <-wl_cfg80211_parse_vndr_ieswpa_supplicant-4774 [002] ...1 5463.125513: bcm_next_tlv <-wl_cfg80211_parse_vndr_ieswpa_supplicant-4774 [002] ...1 5463.125514: bcm_next_tlv <-wl_cfg80211_parse_vndr_ieswpa_supplicant-4774 [002] ...1 5463.125515: bcm_next_tlv <-wl_cfg80211_parse_vndr_ieswpa_supplicant-4774 [002] ...1 5463.125519: $x <-wl_cfg80211_scanwpa_supplicant-4774 [002] ...1 5463.125521: dhd_ioctl_entry_local <-$xwpa_supplicant-4774 [002] ...1 5463.125522: dhd_net2idx <-dhd_ioctl_entry_localwpa_supplicant-4774 [002] ...1 5463.125524: dhd_os_wake_lock <-dhd_ioctl_entry_localwpa_supplicant-4774 [002] ...1 5463.125526: dhd_wl_ioctl <-dhd_ioctl_entry_localwpa_supplicant-4774 [002] ...1 5463.125528: dhd_os_proto_block <-dhd_wl_ioctlwpa_supplicant-4774 [002] ...1 5463.125530: dhd_os_general_spin_lock <-dhd_wl_ioctlwpa_supplicant-4774 [002] d..2 5463.125532: dhd_os_general_spin_unlock <-dhd_wl_ioctlwpa_supplicant-4774 [002] ...1 5463.125534: dhd_prot_ioctl <-dhd_wl_ioctlwpa_supplicant-4774 [002] ...1 5463.125536: $x <-dhd_prot_ioctlwpa_supplicant-4774 [002] ...1 5463.125537: dhd_os_wake_lock <-$xwpa_supplicant-4774 [002] ...1 5463.125539: dhd_bus_txctl <-$xwpa_supplicant-4774 [002] ...1 5463.125541: dhd_os_sdlock <-dhd_bus_txctlwpa_supplicant-4774 [002] ...1 5463.125544: dhdsdio_bussleep <-dhd_bus_txctlwpa_supplicant-4774 [002] ...1 5463.125545: dhdsdio_clk_devsleep_iovar <-dhdsdio_bussleepwpa_supplicant-4774 [002] ...1 5463.125547: dhdsdio_clk_kso_enab.isra.7 <-dhdsdio_clk_devsleep_iovarwpa_supplicant-4774 [002] ...1 5463.125549: dhdsdio_sleepcsr_get.isra.1 <-dhdsdio_clk_devsleep_iovarwpa_supplicant-4774 [002] ...1 5463.125550: bcmsdh_cfg_read <-dhdsdio_sleepcsr_get.isra.1wpa_supplicant-4774 [002] ...1 5463.125552: sdioh_cfg_read <-bcmsdh_cfg_readwpa_supplicant-4774 [002] ...1 5463.125554: sdioh_request_byte <-sdioh_cfg_readwpa_supplicant-4774 [002] ...1 5463.126236: bcmsdh_cfg_read <-dhdsdio_clk_devsleep_iovarwpa_supplicant-4774 [002] ...1 5463.126241: sdioh_cfg_read <-bcmsdh_cfg_readwpa_supplicant-4774 [002] ...1 5463.126242: sdioh_request_byte <-sdioh_cfg_readwpa_supplicant-4774 [002] ...1 5463.126484: dhdsdio_clkctl.isra.10 <-dhd_bus_txctlwpa_supplicant-4774 [002] ...1 5463.126487: dhd_os_wd_timer <-dhdsdio_clkctl.isra.10wpa_supplicant-4774 [002] ...1 5463.126489: dhd_os_wd_wake_lock <-dhd_os_wd_timerwpa_supplicant-4774 [002] d..2 5463.126494: dhd_os_wd_wake_lock <-dhd_os_wd_timerwpa_supplicant-4774 [002] ...1 5463.126498: dhd_os_wd_wake_unlock <-dhd_os_wd_timerwpa_supplicant-4774 [002] ...1 5463.126502: bcmsdh_cur_sbwad <-dhd_bus_txctlwpa_supplicant-4774 [002] ...1 5463.126503: dhd_bcmsdh_send_buf.constprop.27 <-dhd_bus_txctlwpa_supplicant-4774 [002] ...1 5463.126505: bcmsdh_send_buf <-dhd_bcmsdh_send_buf.constprop.27wpa_supplicant-4774 [002] ...1 5463.126506: bcmsdhsdio_set_sbaddr_window <-bcmsdh_send_bufwpa_supplicant-4774 [002] ...1 5463.126508: sdioh_request_buffer <-bcmsdh_send_bufwpa_supplicant-4774 [002] ...1 5463.126510: sdioh_buffer_tofrom_bus <-sdioh_request_buffer
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
内核netlink接收来自应用层的消息并处理
genl_rcv()接收到数据会直接调用genl_rcv_msg()
genl_family_rcv_msgstatic int genl_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
{//nlmsghdr中的type应该和family的id一致,//但是内核中genl注册family时,id是自动非配的,那用户空间发送的消息怎么确认id,family = genl_family_find_byid(nlh->nlmsg_type);//根据nlh中定义的cmd类型决定genl_family_rcv_msg(family, skb, nlh);{//在传入的nlh的载荷中包含着geml的头genlmsghdr,struct genlmsghdr *hdr = nlmsg_data(nlh);//genl 信息,里面有netlnik head,genl head,user head等信息,最终会由用户(nl80211)定义的ops处理struct genl_info info; //如果有family有体检需要处理的,可以放在该处err = family->pre_doit(ops, skb, &info);//通过cmd找到ops,对传入的数据进行处理ops = genl_get_cmd(hdr->cmd, family);//ops处理数据err = ops->doit(skb, &info);//family的后续处理family->post_doit(ops, skb, &info);}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
wifi beacon强度获取:本例子驱动不走这个,直接驱动代码调用cfg80211_inform_bss_frame这类接口更新数据
ieee80211_tasklet_handler ieee80211_rx__ieee80211_rx_handle_packet {ieee80211_scan_rx //probe_rsp beacon帧 { cfg80211_inform_bss_frame--> cfg80211_bss_update--> (list_add_tail(&res->list, &dev->bss_list);)--> cfg80211_scan_done->-nl80211_send_scan_done(NL80211_CMD_NEW_SCAN_RESULTS) 1) + 12.542 us | cfg80211_scan_done [cfg80211]();------------------------------------------1) wl_even-4966 => kworker-7064 ------------------------------------------1) | __cfg80211_scan_done [cfg80211]() {1) | ___cfg80211_scan_done [cfg80211]() {1) 0.834 us | cfg80211_sme_scan_done [cfg80211]();1) | nl80211_build_scan_msg [cfg80211]() {1) 7.541 us | nl80211_send_scan_msg.constprop.68 [cfg80211]();1) + 14.750 us | }1) + 22.000 us | nl80211_send_scan_result [cfg80211]();1) + 48.875 us | }1) + 53.667 us | } }ieee80211_prepare_and_rx_handle//数据帧,已连接上了后 {ieee80211_invoke_rx_handlersieee80211_rx_handlersieee80211_rx_h_sta_process中sta->last_signal = status->signal; }}获取给上层: const struct cfg80211_ops mac80211_config_ops = {.get_station = ieee80211_get_station,中:sta_set_sinfosinfo->signal = (s8)sta->last_signal;获取
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
模组测试模式下连接无密码热点,如在实验室测试
wl ver
wl scan
wl join 4366muap
wl status
wl btc mode 0
其中wl join 4366muap的具体流程用omnipeek抓包流程如下,4366muap为无加密ap,join不成功可能与ap的发射功率有关