broadcom 6356s模组驱动分析笔记

模组broadcom 6356s 蓝牙wifi双模

wifi驱动的通用的软件架构

  1. 分为两部分,上面为主机端驱动,下面是我们之前所说的firmware

  2. 其中固件部分的主要工作是:因为天线接受和发送回来的都是802.11帧的帧,而主机接受和传送出来的数据都必须是802.3的帧,所以必须由firmware来负责802.3的帧和802.11帧之间的转换

  3. 当天线收到数据,并被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,

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_IFDEBUG
                    if (cfg->iface_cnt == 0) {
                        wl_dump_ifinfo(cfg);
                    }
#endif
                    cfg->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;
}
                cfg->evt_handler[e->etype](cfg, wdev_to_cfgdev(wdev),
                    &e->emsg, e->edata);

在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_SUPPORT
    cfg->evt_handler[WLC_E_PFN_NET_FOUND] = wl_notify_pfn_status;
#endif /* PNO_SUPPORT */
#ifdef GSCAN_SUPPORT
    cfg->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 WLTDLS
    cfg->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_RELMCAST
    cfg->evt_handler[WLC_E_RMC_EVENT] = wl_notify_rmc_status;
#endif
#ifdef BT_WIFI_HANDOVER
    cfg->evt_handler[WLC_E_BT_WIFI_HANDOVER_REQ] = wl_notify_bt_wifi_handover_req;
#endif
#ifdef WL_NAN
    cfg->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_ROAMING
    cfg->evt_handler[WLC_E_ROAM_PREP] = wl_notify_roam_prep_status;
#endif
    cfg->evt_handler[WLC_E_AP_STARTED] = wl_ap_start_ind;
#ifdef CUSTOM_EVENT_PM_WAKE
    cfg->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;
}

如扫描完成调用
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;
#endif
    WL_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);
#else
        cfg80211_scan_done(cfg->scan_request, false);//通知内核无线子系统扫描完成,让上层去获取扫描结果等操作
#endif
        cfg->scan_request = NULL;
    }
    spin_unlock_irqrestore(&cfg->cfgdrv_lock, flags);
    WL_DBG(("cfg80211_scan_done\n"));
    mutex_unlock(&cfg->usr_sync);
    return err;
}
wpa_supplicant-4774  [002] ...1  5463.125488: wl_cfg80211_scan <-nl80211_trigger_scan
  wpa_supplicant-4774  [002] ...1  5463.125495: wl_cfg_multip2p_operational <-wl_cfg80211_scan
  wpa_supplicant-4774  [002] ...1  5463.125498: wl_cfg80211_get_remain_on_channel_ndev <-wl_cfg80211_scan
  wpa_supplicant-4774  [002] ...1  5463.125504: wl_cfgp2p_discover_enable_search <-wl_cfg80211_scan
  wpa_supplicant-4774  [002] ...1  5463.125506: wl_cfg80211_set_mgmt_vndr_ies <-wl_cfg80211_scan
  wpa_supplicant-4774  [002] ...1  5463.125509: wl_cfg80211_parse_vndr_ies <-wl_cfg80211_set_mgmt_vndr_ies
  wpa_supplicant-4774  [002] ...1  5463.125511: bcm_next_tlv <-wl_cfg80211_parse_vndr_ies
  wpa_supplicant-4774  [002] ...1  5463.125513: bcm_next_tlv <-wl_cfg80211_parse_vndr_ies
  wpa_supplicant-4774  [002] ...1  5463.125514: bcm_next_tlv <-wl_cfg80211_parse_vndr_ies
  wpa_supplicant-4774  [002] ...1  5463.125515: bcm_next_tlv <-wl_cfg80211_parse_vndr_ies
  wpa_supplicant-4774  [002] ...1  5463.125519: $x <-wl_cfg80211_scan
  wpa_supplicant-4774  [002] ...1  5463.125521: dhd_ioctl_entry_local <-$x
  wpa_supplicant-4774  [002] ...1  5463.125522: dhd_net2idx <-dhd_ioctl_entry_local
  wpa_supplicant-4774  [002] ...1  5463.125524: dhd_os_wake_lock <-dhd_ioctl_entry_local
  wpa_supplicant-4774  [002] ...1  5463.125526: dhd_wl_ioctl <-dhd_ioctl_entry_local
  wpa_supplicant-4774  [002] ...1  5463.125528: dhd_os_proto_block <-dhd_wl_ioctl
  wpa_supplicant-4774  [002] ...1  5463.125530: dhd_os_general_spin_lock <-dhd_wl_ioctl
  wpa_supplicant-4774  [002] d..2  5463.125532: dhd_os_general_spin_unlock <-dhd_wl_ioctl
  wpa_supplicant-4774  [002] ...1  5463.125534: dhd_prot_ioctl <-dhd_wl_ioctl
  wpa_supplicant-4774  [002] ...1  5463.125536: $x <-dhd_prot_ioctl
  wpa_supplicant-4774  [002] ...1  5463.125537: dhd_os_wake_lock <-$x
  wpa_supplicant-4774  [002] ...1  5463.125539: dhd_bus_txctl <-$x
  wpa_supplicant-4774  [002] ...1  5463.125541: dhd_os_sdlock <-dhd_bus_txctl
  wpa_supplicant-4774  [002] ...1  5463.125544: dhdsdio_bussleep <-dhd_bus_txctl
  wpa_supplicant-4774  [002] ...1  5463.125545: dhdsdio_clk_devsleep_iovar <-dhdsdio_bussleep
  wpa_supplicant-4774  [002] ...1  5463.125547: dhdsdio_clk_kso_enab.isra.7 <-dhdsdio_clk_devsleep_iovar
  wpa_supplicant-4774  [002] ...1  5463.125549: dhdsdio_sleepcsr_get.isra.1 <-dhdsdio_clk_devsleep_iovar
  wpa_supplicant-4774  [002] ...1  5463.125550: bcmsdh_cfg_read <-dhdsdio_sleepcsr_get.isra.1
  wpa_supplicant-4774  [002] ...1  5463.125552: sdioh_cfg_read <-bcmsdh_cfg_read
  wpa_supplicant-4774  [002] ...1  5463.125554: sdioh_request_byte <-sdioh_cfg_read
  wpa_supplicant-4774  [002] ...1  5463.126236: bcmsdh_cfg_read <-dhdsdio_clk_devsleep_iovar
  wpa_supplicant-4774  [002] ...1  5463.126241: sdioh_cfg_read <-bcmsdh_cfg_read
  wpa_supplicant-4774  [002] ...1  5463.126242: sdioh_request_byte <-sdioh_cfg_read
  wpa_supplicant-4774  [002] ...1  5463.126484: dhdsdio_clkctl.isra.10 <-dhd_bus_txctl
  wpa_supplicant-4774  [002] ...1  5463.126487: dhd_os_wd_timer <-dhdsdio_clkctl.isra.10
  wpa_supplicant-4774  [002] ...1  5463.126489: dhd_os_wd_wake_lock <-dhd_os_wd_timer
  wpa_supplicant-4774  [002] d..2  5463.126494: dhd_os_wd_wake_lock <-dhd_os_wd_timer
  wpa_supplicant-4774  [002] ...1  5463.126498: dhd_os_wd_wake_unlock <-dhd_os_wd_timer
  wpa_supplicant-4774  [002] ...1  5463.126502: bcmsdh_cur_sbwad <-dhd_bus_txctl
  wpa_supplicant-4774  [002] ...1  5463.126503: dhd_bcmsdh_send_buf.constprop.27 <-dhd_bus_txctl
  wpa_supplicant-4774  [002] ...1  5463.126505: bcmsdh_send_buf <-dhd_bcmsdh_send_buf.constprop.27
  wpa_supplicant-4774  [002] ...1  5463.126506: bcmsdhsdio_set_sbaddr_window <-bcmsdh_send_buf
  wpa_supplicant-4774  [002] ...1  5463.126508: sdioh_request_buffer <-bcmsdh_send_buf
  wpa_supplicant-4774  [002] ...1  5463.126510: sdioh_buffer_tofrom_bus <-sdioh_request_buffer

内核netlink接收来自应用层的消息并处理

genl_rcv()接收到数据会直接调用genl_rcv_msg() 
genl_family_rcv_msg

static 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);
    }
}

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_handlers
    ieee80211_rx_handlers
    ieee80211_rx_h_sta_process中
    sta->last_signal = status->signal; 
}

}




获取给上层:
const struct cfg80211_ops mac80211_config_ops = {

    .get_station = ieee80211_get_station,

中:sta_set_sinfo
            sinfo->signal = (s8)sta->last_signal;获取

模组测试模式下连接无密码热点,如在实验室测试
wl ver
wl scan
wl join 4366muap
wl status
wl btc mode 0

其中wl join 4366muap的具体流程用omnipeek抓包流程如下,4366muap为无加密ap,join不成功可能与ap的发射功率有关
这里写图片描述

  • 2
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值