Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

mt7921-usb驱动注册分析 #60

Open
taikulawo opened this issue Nov 3, 2023 · 0 comments
Open

mt7921-usb驱动注册分析 #60

taikulawo opened this issue Nov 3, 2023 · 0 comments
Labels

Comments

@taikulawo
Copy link
Collaborator

taikulawo commented Nov 3, 2023

以联发科 mt7921 无线网卡驱动为例

kernel mt7921/ 下有pci,usb接口的驱动,usb接口驱动入口是usb.c。手里有mt7921芯片的USB无线网卡,对应的驱动为mt7921u

pci驱动为 mt7921e
USB接口 MT7921 外观
usb

PCI接口的MT7921网卡外观

pci-e

驱动采用USB总线注册到kernel,使用kernel提供的 module_usb_driver(mt7921u_driver);,调用 usb_register_driver进行注册

kernel驱动采用callback式,,linux为各种接口设备提供helper

驱动使用kernel module_usb_driver进行注册

#define module_usb_driver(__usb_driver) \
    module_driver(__usb_driver, usb_register, \
               usb_deregister)

经过usb的一系列宏替换,module_init调用usb的通用init函数 usb_register 初始化,最终调用 struct usb_driver#probe 开始驱动初始化

#define module_driver(__driver, __register, __unregister, ...) \
static int __init __driver##_init(void) \
{ \
    return __register(&(__driver) , ##__VA_ARGS__); \
} \
module_init(__driver##_init); \
static void __exit __driver##_exit(void) \
{ \
    __unregister(&(__driver) , ##__VA_ARGS__); \
} \
module_exit(__driver##_exit);

usb 是接口类型,usb接口探测完成后,驱动调用kernel 80211一系列helper,在kernel中注册为无线网卡。

call stack

驱动的80211回调。有数据需要发送kernel则调用.tx

const struct ieee80211_ops mt7921_ops = {
    .tx = mt792x_tx,
    .start = mt7921_start,
    .stop = mt7921_stop,
    .add_interface = mt7921_add_interface,
    .remove_interface = mt792x_remove_interface,
    .config = mt7921_config,
    .conf_tx = mt792x_conf_tx,
    .configure_filter = mt7921_configure_filter,
    .bss_info_changed = mt7921_bss_info_changed,
    .start_ap = mt7921_start_ap,
    .stop_ap = mt7921_stop_ap,
    .sta_state = mt7921_sta_state,
    .sta_pre_rcu_remove = mt76_sta_pre_rcu_remove,
    .set_key = mt7921_set_key,
    .sta_set_decap_offload = mt7921_sta_set_decap_offload,
#if IS_ENABLED(CONFIG_IPV6)
    .ipv6_addr_change = mt7921_ipv6_addr_change,
#endif /* CONFIG_IPV6 */
    .ampdu_action = mt7921_ampdu_action,
    .set_rts_threshold = mt7921_set_rts_threshold,
    .wake_tx_queue = mt76_wake_tx_queue,
    .release_buffered_frames = mt76_release_buffered_frames,
    .channel_switch_beacon = mt7921_channel_switch_beacon,
    .get_txpower = mt76_get_txpower,
    .get_stats = mt792x_get_stats,
    .get_et_sset_count = mt792x_get_et_sset_count,
    .get_et_strings = mt792x_get_et_strings,
    .get_et_stats = mt792x_get_et_stats,
    .get_tsf = mt792x_get_tsf,
    .set_tsf = mt792x_set_tsf,
    .get_survey = mt76_get_survey,
    .get_antenna = mt76_get_antenna,
    .set_antenna = mt7921_set_antenna,
    .set_coverage_class = mt792x_set_coverage_class,
    .hw_scan = mt7921_hw_scan,
    .cancel_hw_scan = mt7921_cancel_hw_scan,
    .sta_statistics = mt792x_sta_statistics,
    .sched_scan_start = mt7921_start_sched_scan,
    .sched_scan_stop = mt7921_stop_sched_scan,
    CFG80211_TESTMODE_CMD(mt7921_testmode_cmd)
    CFG80211_TESTMODE_DUMP(mt7921_testmode_dump)
#ifdef CONFIG_PM
    .suspend = mt7921_suspend,
    .resume = mt7921_resume,
    .set_wakeup = mt792x_set_wakeup,
    .set_rekey_data = mt7921_set_rekey_data,
#endif /* CONFIG_PM */
    .flush = mt792x_flush,
    .set_sar_specs = mt7921_set_sar_specs,
    .remain_on_channel = mt7921_remain_on_channel,
    .cancel_remain_on_channel = mt7921_cancel_remain_on_channel,
    .add_chanctx = mt7921_add_chanctx,
    .remove_chanctx = mt7921_remove_chanctx,
    .change_chanctx = mt7921_change_chanctx,
    .assign_vif_chanctx = mt792x_assign_vif_chanctx,
    .unassign_vif_chanctx = mt792x_unassign_vif_chanctx,
    .mgd_prepare_tx = mt7921_mgd_prepare_tx,
    .mgd_complete_tx = mt7921_mgd_complete_tx,
};

追踪 .tx调用路径

收包送到kernel协议栈路径

调用路径有两个,pci 接口走的DMA,而USB接口则是kthread polling

int __mt76_worker_fn(void *ptr)
{
    struct mt76_worker *w = ptr;

    while (!kthread_should_stop()) {
        set_current_state(TASK_INTERRUPTIBLE);

        if (kthread_should_park()) {
            kthread_parkme();
            continue;
        }

        if (!test_and_clear_bit(MT76_WORKER_SCHEDULED, &w->state)) {
            schedule();
            continue;
        }

        set_bit(MT76_WORKER_RUNNING, &w->state);
        set_current_state(TASK_RUNNING);
        // kthread 持续调用 mt76u_rx_worker
        w->fn(w);
        cond_resched();
        clear_bit(MT76_WORKER_RUNNING, &w->state);
    }

    return 0;
}

// https://github.com/torvalds/linux/blob/v6.6/drivers/net/wireless/mediatek/mt76/usb.c#L628
static void mt76u_rx_worker(struct mt76_worker *w)
{
    struct mt76_usb *usb = container_of(w, struct mt76_usb, rx_worker);
    struct mt76_dev *dev = container_of(usb, struct mt76_dev, usb);
    int i;

    rcu_read_lock();
    // 遍历每个队列处理rx数据
    mt76_for_each_q_rx(dev, i)
        mt76u_process_rx_queue(dev, &dev->q_rx[i]);
    rcu_read_unlock();
}

qemu绑定mt7921u网卡,分析驱动调用栈

qemu-system-x86_64 --kernel ./arch/x86/boot/bzImage -initrd ./rootfs.cpio -device e1000,netdev=eth0 -netdev user,id=eth0,hostfwd=tcp::5555-:22,net=192.168.76.0/24,dhcpstart=192.168.76.9  -append "nokaslr console=ttyS0" -S -nographic -gdb tcp::1234 -virtfs local,path=/,security_model=none,mount_tag=guestroot -usb -device usb-host,vendorid=0x0e8d,productid=0x7961

mt7921 probe调用栈/也是USB module调用栈

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant