版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、Linux 下wifi 驱动开发(三) SDIO接口WiFi驱动浅析 SDIO-Wifi模块是基于SDIO接口的符合wifi无线网络标准的嵌入式模块,内置无线网络协议IEEE802.11协议栈以及TCP/IP协议栈,能够实现用户主平台数据通过SDIO口到无线网络之间的转换。SDIO具有传输数据快,兼容SD、MMC接口等特点。 对于SDIO接口的wifi,首先,它是一个sdio的卡的设备,然后具备了wifi的功能,所以,注册的时候还是先以sdio的卡的设备去注册的。然后检测到卡之后就要驱动他的wifi功能了,
2、显然,他是用sdio的协议,通过发命令和数据来控制的。下面先简单回顾一下SDIO的相关知识:一、SDIO相关基础知识解析1、SDIO接口 SDIO 故名思义,就是 SD 的 I/O 接口(interface)的意思,不过这样解释可能还有点抽像。更具体的说明,SD 本来是记忆卡的标准,但是现在也可以把 SD 拿来插上一些外围接口使用,这样的技术便是 SDIO。 所以 SDIO 本身是一种相当单纯的技术,透过 SD 的 I/O 接脚来连接外部外围,并且透过 SD 上
3、的 I/O 数据接位与这些外围传输数据,而且 SD 协会会员也推出很完整的 SDIO stack 驱动程序,使得 SDIO 外围(我们称为SDIO 卡)的开发与应用变得相当热门。 现在已经有非常多的手机或是手持装置都支持 SDIO 的功能(SD 标准原本就是针对 mobile device 而制定),而且许多 SDIO 外围也都被开发出来,让手机外接外围更加容易,并且开发上更有弹性(不需要内建外围)。目前常见的 SDIO 外围(SDIO 卡)有:· Wi-Fi card(无线网络卡) · CMOS sensor
4、card(照相模块) · GPS card · GSM/GPRS modem card · Bluetooth card SDIO 的应用将是未来嵌入式系统最重要的接口技术之一,并且也会取代目前 GPIO 式的 SPI 接口。2、SDIO总线 SDIO总线 和 USB总线 类似,SDIO也有两端,其中一端是HOST端,另一端是device端。所有的通信都是由HOST端 发送 命令 开始的,Device端只要能解析命令,就可以相互通信。CLK信号
5、:HOST给DEVICE的 时钟信号,每个时钟周期传输一个命令。CMD信号:双向 的信号,用于传送 命令 和 反应。DAT0-DAT3 信号:四条用于传送的数据线。VDD信号:电源信号。VSS1,VSS2:电源地信号。3、SDIO热插拔原理方法:设置一个 定时器检查 或 插拔中断检测硬件:假如GPG10(EINT18)用于SD卡检测GPG10 为高电平 即没有插入SD卡GPG10为低电平 即插入了SD卡4、SDIO命令 SDIO总线上都是HOST端发起请求,然后DEVICE端回应请求。sdio命令由6个字节组成。
6、a - Command:用于开始传输的命令,是由HOST端发往DEVICE端的。其中命令是通过CMD信号线传送的。b - Response:回应是DEVICE返回的HOST的命令,作为Command的回应。也是通过CMD线传送的。c - Data:数据是双向的传送的。可以设置为1线模式,也可以设置为4线模式。数据是通过DAT0-DAT3信号线传输的。 SDIO的每次操作都是由HOST在CMD线上发起一个CMD,对于有的CMD,DEVICE需要返回Response,有的则不需要。 对于读命令,首先HOST会向DEVIC
7、E发送命令,紧接着DEVICE会返回一个握手信号,此时,当HOST收到回应的握手信号后,会将数据放在4位的数据线上,在传送数据的同时会跟随着CRC校验码。当整个读传送完毕后,HOST会再次发送一个命令,通知DEVICE操作完毕,DEVICE同时会返回一个响应。 对于写命令,首先HOST会向DEVICE发送命令,紧接着DEVICE会返回一个握手信号,此时,当HOST收到回应的握手信号后,会将数据放在4位的数据线上,在传送数据的同时会跟随着CRC校验码。当整个写传送完毕后,HOST会再次发送一个命令,通知DEVICE操作完毕,DEVICE同时会返回一个响应。二、SDIO接
8、口驱动 前面讲到,SDIO接口的wifi,首先,它是一个sdio的卡的设备,然后具备了wifi的功能,所以SDIO接口的WiFi驱动就是在wifi驱动外面套上了一个SDIO驱动的外壳,SDIO驱动仍然符合设备驱动的分层与分离思想: 设备驱动层(wifi 设备) |核心层(向上向下提供接口)
9、; |主机驱动层 (实现SDIO驱动) 下面先分析SDIO接口驱动的实现,看几个重要的数据结构(用于核心层与主机驱动层 的数据交换处理)。 /include/linux/mmc/host.h struct mmc_host 用来描述卡控制器struct mmc_card 用来描述卡struct mmc_driver 用来描述 mmc 卡驱动struct sdio_func
10、160; 用来描述 功能设备struct mmc_host_ops 用来描述卡控制器操作接口函数功能,用于从 主机控制器层向 core 层注册操作函数,从而将core 层与具体的主机控制器隔离。也就是说 core 要操作主机控制器,就用这个 ops 当中给的函数指针操作,不能直接调用具体主控制器的函数。 HOST层驱动分析在 前面的系列文章中 Linux SD卡驱动开发(二) SD 卡驱动分析HOST篇 有详细阐述,下面只简单回顾一下一些重要函数处理1、编写Host层驱动
11、;这里参考的是S3C24XX的HOST驱动程序 /drivers/mmc/host/s3cmci.c cpp view plain copy1. static struct platform_driver s3cmci_driver = 2. .driver = 3.
12、 .name = "s3c-sdi", /名称和平台设备定义中的对应 4. .owner = THIS_MODULE, 5. .pm = s3cmci_pm
13、_ops, 6. , 7. .id_table = s3cmci_driver_ids, 8. .probe = s3cmci_probe, /平台设备探测接口函数 9. &
14、#160; .remove = _devexit_p(s3cmci_remove), 10. .shutdown = s3cmci_shutdown, 11. ; 12. 13. s3cmci_probe(struct platform_device *pdev)
15、0;14. 15. /. 16. struct mmc_host *mmc; 17. mmc = mmc_alloc_host(sizeof(struct s3cmci_host), &pdev->dev); /分配mmc_host结构体 18.
16、;19. /. 20. 21. 22. /*注册中断处理函数s3cmci_irq,来处理数据收发过程引起的各种中断*/ 23. request_irq(host->irq, s3cmci_irq, 0, DRIVER_NAME, host) /注册中断处理函数s3cmci_irq 24. 25. /*注册中断处理s3cmci_irq_cd函数,来处理热拨插
17、引起的中断,中断触发的形式为上升沿、下降沿触发*/ 26. request_irq(host->irq_cd, s3cmci_irq_cd,IRQF_TRIGGER_RISING |IRQF_TRIGGER_FALLING, DRIVER_NAME, host) 27. 28. mmc_add_host(mmc); /initialise host hardware /向MMC core注册host驱动
18、60;29. -> device_add(&host->class_dev); /添加设备到mmc_bus_type总线上的设备链表中 30. -> mmc_start_host(host); /启动mmc host 31. 32. /*MMC drivers should call this when they detect a card has
19、60;been inserted or removed.检测sd卡是否插上或移除*/ 33. ->mmc_detect_change(host, 0); 34. 35. /*Schedule delayed work in the MMC work queue.调度延时工作队列*/ 36. mmc_schedule_delayed_work(&host->
20、;detect, delay); 搜索host->detected得到以下信息:/drivers/mmc/core/host.ccpp view plain copy1. NIT_DELAYED_WORK(&host->detect, mmc_rescan); 2. 3. mmc_rescan(struct work_struct *work) 4. ->mmc_bus_put(host);/card 从bus
21、上移除时,释放它占有的总线空间 5. 6. /*判断当前mmc host控制器是否被占用,当前mmc控制器如果被占用,那么 host->claimed = 1;否则为0 7. *如果为1,那么会在while(1)循环中调用schedule切换出自己,当占用mmc控制器的操作完成之后,执行 *mmc_release_host()的时候,会激活登记到等待队列&host->wq中的其他 程序获得mmc主控制器的使用权 8.
22、0;*/ 9. mmc_claim_host(host); 10. mmc_rescan_try_freq(host, max(freqsi, host->f_min); 11. 12. static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq) 13.
23、0; 14. 15. /* Order's important: probe SDIO, then SD, then MMC */ 16. if (!mmc_attach_sdio(host) 17.
24、0; return 0; 18. if (!mmc_attach_sd(host) 19. return 0; 20. if (!mmc_attach_mmc(host) &
25、#160;21. return 0; 22. . 23. 24. 25. mmc_attach_sdio(struct mmc_host *host) /匹配sdio接口卡 26.
26、 ->mmc_attach_bus(host, &mmc_sdio_ops); 27. 28. /*当card与总线上的驱动匹配,就初始化card*/ 29. mmc_sdio_init_card(host, host->ocr, NULL, 0); 30. ->card = mmc_alloc_card(host, NULL);/分配一个c
27、ard结构体 31. mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL); /设置mmc_bus的工作模式 32. 33. struct sdio_func *sdio_funcSDIO_MAX_FUNCS; /SDIO functions (devices) 34.
28、 35. sdio_init_func(host->card, i + 1); 36. ->func = sdio_alloc_func(card); /分配struct sdio_fun(sdio功能设备)结构体 37. mmc_io_rw_direct(); 38. &
29、#160; card->sdio_funcfn - 1 = func; 39. 40. mmc_add_card(host->card); /将具体的sdio设备挂载到mmc_bus_types 总线 41. sdio_add_func(host->card->sdio_funci); /将sdio功能设备挂载到sdi
30、o_bus_types总线 这里一系列函数调用在前面的SD驱动蚊帐中已经阐述过了,不再详细阐述2、SDIO设备的热插拔 当插拔SDIO设备,会触发中断通知到CPU,然后执行卡检测中断处理函数在这个中断服务函数中,mmc_detect_change->mmc_schedule_delayed_work(&host->detect,delay), INIT_DELAYED_WORK(&host->detect, mmc_rescan)会调度mmc_rescan函数延时调度工作队列,这样也会触发SDIO设
31、备的初始化流程,检测到有效的SDIO设备后,会将它注册到系统中去。cpp view plain copy1. static irqreturn_t s3cmci_irq_cd(int irq, void *dev_id) 2. 3. struct s3cmci_host *host = (struct s3cmci_host *)dev_id;
32、60;4. . 5. mmc_detect_change(host->mmc, msecs_to_jiffies(500); 6. 7. return IRQ_HANDLED; 8. 三、wifi 驱动部分解析wifi驱动的通用的软件架构1. 分为两部分,上面为主机端驱动,下面是
33、我们之前所说的firmware2. 其中固件部分的主要工作是:因为天线接受和发送回来的都是802.11帧的帧,而主机接受和传送出来的数据都必须是802.3的帧,所以必须由firmware来负责802.3的帧和802.11帧之间的转换3. 当天线收到数据,并被firmware处理好后会放在一个buffer里,并产生一个中断,主机在收到中断后就去读这个buffer。 SDIO设备的驱动由sdio_driver结构体定义,sdio_driver其实是driver的封装。通过sdio_register_drive
34、r函数将SDIO设备驱动加载进内核,其实就是挂载到sdio_bus_type总线上去。1、设备驱动的注册与匹配Drivers/net/wireless/libertas/if_sdio.ccpp view plain copy1. /* SDIO function device driver*/ 2. 3. struct sdio_driver 4. char *name;
35、160; /设备名 5. const struct sdio_device_id *id_table; /设备驱动ID 6. int (*probe)(struct sdio_func *, const struct sdio_device_id *);/匹配函数 7.
36、; void (*remove)(struct sdio_func *); 8. struct device_driver drv; 9. ; 下面是具体函数的填充:cpp view plain copy1. /*if_sdio.c*/ 2. 3. static struct sdio_driver
37、160;if_sdio_driver = 4. .name = "libertas_sdio", 5. .id_table = if_sdio_ids, /用于设备与驱动的匹配 6.
38、60; .probe = if_sdio_probe, 7. .remove = if_sdio_remove, 8. .drv = 9.
39、; .pm = &if_sdio_pm_ops, 10. 11. ; 设备注册函数cpp view plain copy1. /* 2. * sdio_register_driver - register a
40、function driver 3. * drv: SDIO function driver 4. */ 5. 6. int sdio_register_driver(struct sdio_driver *drv) 7. 8. drv-> = drv-&g
41、t;name; 9. drv->drv.bus = &sdio_bus_type; /设置driver的bus为sdio_bus_type 10. return driver_register(&drv->drv); 11. 总线函数cpp view plain copy1. static
42、 struct bus_type sdio_bus_type = 2. .name = "sdio", 3. .dev_attrs = sdio_dev_attrs, 4
43、. .match = sdio_bus_match, 5. .uevent = sdio_bus_uevent, 6. .probe
44、 = sdio_bus_probe, 7. .remove = sdio_bus_remove, 8. .pm = SDIO_PM_OPS_PTR, 9. ; 注意
45、:设备或者驱动注册到系统中的过程中,都会调用相应bus上的匹配函数来进行匹配合适的驱动或者设备,对于sdio设备的匹配是由sdio_bus_match和sdio_bus_probe函数来完成。cpp view plain copy1. static int sdio_bus_match(struct device *dev, struct device_driver *drv) 2. 3. struc
46、t sdio_func *func = dev_to_sdio_func(dev); 4. struct sdio_driver *sdrv = to_sdio_driver(drv); 5. if (sdio_match_device(func, sdrv) 6.
47、; return 1; 7. 8. return 0; 9. 10. 11. static const struct sdio_device_id *sdio_match_device(struct sdio_func *func, 12.
48、 struct sdio_driver *sdrv) 13. 14. const struct sdio_device_id *ids; 15. ids = sdrv->id_table;
49、160; 16. 17. if (sdio_match_one(func, ids) 18. return ids; 19. 由以上匹配过程来看
50、,通过匹配id_table 和 sdio_driver设备驱动中id,来匹配合适的驱动或设备。最终会调用.probe函数,来完成相关操作。2、If_sdio_probe函数 当检测到sdio卡插入了之后就会调用If_sdio_probe,而当卡被移除后就会调用If_sdio_remove。下面先看下If_sdio_probet函数,if_sdio_prob 函数 主要做了两件事 cpp view plain copy1. static struct sdio_driver if_sdio_driver
51、60;= 2. .name = "libertas_sdio", 3. .id_table = if_sdio_ids, /用于设备和驱动的匹配 4. .probe = if_sdio_probe, 5. .remove = if_sdio_remove, 6
52、. .drv = 7. .pm = &if_sdio_pm_ops, 8. , 9. ; 10. 11. 12. 1 /定义一个 if_sdio card的结构体 13. struct if_sdio_card *card; 14. &
53、#160;struct if_sdio_packet *packet; /sdio 包的结构体 15. struct mmc_host *host = func->card->host; 16. 17. / 查询是否有指定的功能寄存器在mmc 18. /_sdio_card中 19. fo
54、r (i = 0;i < func->card->num_info;i+) 20. if (sscanf(func->card->infoi, 21. "802.11 SDIO ID: %x", &model) = 1) 22. 23.
55、 /在这里进行片选 选择到我们使用的marvell 8686 的设备 24. case MODEL_8686: 25. card->scratch_reg = IF_SDIO_SCRATCH; 26. 27. 28. /创建sdio 的工作队列 29. card->workqueue =
56、create_workqueue("libertas_sdio"); 30. /调用下面的函数 31. INIT_WORK(&card->packet_worker, if_sdio_host_to_card_worker); 32. 33. 34. /主机到卡的工作队列 35. static void if_sdio_host_to_card_worker(struct work_st
57、ruct *work) 36. 37. /* Check if we support this card 选择我们所支持的卡的类型*/ 38. /赋值为sd8686_helper.bin sd8686.bin 39. /*fw_table 中的 MODEL_8686, "sd8686_helpe
58、r.bin", "sd8686.bin" ,?/ 40. for (i = 0; i < ARRAY_SIZE(fw_table); i+) 41. if (card->model = fw_tablei.model) 42.
59、 break; 43. 44. MODEL_8688, "libertas/sd8688_helper.bin", "libertas/sd8688.bin" , 45. 46. 47. /申请一个host 48. sdio_claim_host(func);&
60、#160; 49. /使能sdio 的功能 寄存器 50. ret = sdio_enable_func(func); 51. if (ret) 52. goto release; 53. 54. 2/申请 sdio 的中断 当有数据 ,命令 或者是事件 的时间执行中断 55. ret&
61、#160;= sdio_claim_irq(func, if_sdio_interrupt); 56. ret = if_sdio_card_to_host(card); /从无线网卡接收到数据 或者说是上报数据 57. ret = if_sdio_handle_data(card, card->buffer + 4, chunk - 4); /接收数据的处理&
62、#160; 58. ret = if_sdio_handle_cmd(card, card->buffer + 4, chunk - 4); /处理申请的命令中断 59. ret = if_sdio_handle_event(card, card->buffer + 4, chunk - 4);/处理申请的事件中断 60. &
63、#160; 61. 62. /添加网络结构体 分配设备并注册 63. priv = lbs_add_card(card, &func->dev); 64. 65. /分配Ethernet设备并注册 66. wdev = lbs_cfg_alloc(dmdev); 67. /802无线网的具体的操作函数 68.
64、 wdev->wiphy = wiphy_new(&lbs_cfg80211_ops, sizeof(struct lbs_private); 69. 70. 71. /分配网络设备是整个网络部分操作的 72. /的核心结构体 73. dev = alloc_netdev(0, "wlan%d", ether_setup); /实例化w
65、lan0的属性 74. dev->ieee80211_ptr = wdev; 75. dev->ml_priv = priv; 76. /设置设备的物理地址 77. SET_NETDEV_DEV(dev, dmdev); 78. wdev->netdev = dev; 79. priv->dev
66、60;= dev; 80. /初始化网络设备 ops. 看门狗 81. dev->netdev_ops = &lbs_netdev_ops; /网络设备的具体的操作函数 82. dev->watchdog_timeo = 5 * HZ;
67、;83. dev->ethtool_ops = &lbs_ethtool_ops; 84. dev->flags |= IFF_BROADCAST | IFF_MULTICAST; /广播或者多播 85. 86. 87. 88. /启动一个内核线程来管理这个网络设备的数据发
68、送,事件的处理(卡的拔出)和一些命令的处理 89. priv->main_thread = kthread_run(lbs_thread, dev, "lbs_main"); 90. /初始化相关的工作队列 91. priv->work_thread = create_singlethread_workqueue("lbs_worker"); 92.
69、;INIT_WORK(&priv->mcast_work, lbs_set_mcast_worker); 93. priv->wol_criteria = EHS_REMOVE_WAKEUP; 94. priv->wol_gpio = 0xff; 95. priv->wol_gap = 20; 96. priv->ehs_remove_supported
70、 = true; 97. 98. 99. /设置私有变量 100. /设置主机发送数据到卡 101. priv->hw_host_to_card = if_sdio_host_to_card; 102. priv->enter_deep_sleep = if_sdio_enter_deep_sleep;
71、160;103. priv->exit_deep_sleep = if_sdio_exit_deep_sleep; 104. priv->reset_deep_sleep_wakeup = if_sdio_reset_deep_sleep_wakeup; 105. sdio_claim_host(func); 106. 107. /启动卡设备
72、60;108. ret = lbs_start_card(priv); 109. if (lbs_cfg_register(priv) 110. 111. ret = register_netdev(priv->dev); 112. err = register_netdevice(dev); 113. 114.
73、 115. /具体的wifi设备驱动功能 116. /网络设备操作的具体函数 117. static const struct net_device_ops lbs_netdev_ops = 118. .ndo_open = lbs_dev_open, /打开 119. .ndo_stop
74、 = lbs_eth_stop, /停止 120. .ndo_start_xmit = lbs_hard_start_xmit, /开始发送数据 121. .ndo_set_mac_address = lbs_set_mac_address, /设置mac地址 122. .ndo_tx_timeout
75、;= lbs_tx_timeout, /发送超时 123. .ndo_set_multicast_list = lbs_set_multicast_list, /多播地址 124. .ndo_change_mtu = eth_change_mtu, /最大传输单元 125. .ndo_validate_addr =
76、0;eth_validate_addr, /判断地址的有效性 3、数据的接收,通过中断的方式来解决 网络设备接收数据的主要方法是由中断引发设备的中断处理函数,中断处理函数判断中断的类型,如果为接收中断,则读取接收到的数据,分配sk_buff数据结构和数据缓冲区,并将接收的数据复制到数据缓存区,并调用netif_rx()函数将sk_buff传递给上层协议。 搜索if_sdio_interrupt,可知道它是在if_sdio.c文件中if_sdio_probe()函数中
77、sdio_claim_irq(func, if_sdio_interrupt) ,func->irq_handler = if_sdio_interrupt。当s3cmci_irq中断处理函数的S3C2410_SDIIMSK_SDIOIRQ 中断被触发时将调用if_sdio_interrupt()函数,进行接收数据。cpp view plain copy1. static void if_sdio_interrupt(struct sdio_func *func) 2. 3. ret
78、 = if_sdio_card_to_host(card); /从无线网卡接收到数据 或者说是上报数据 4. /读取端口上的数据 ,放到card的buffer中 5. ret = sdio_readsb(card->func, card->buffer, card->ioport, chunk); 6. 1.在这里一方面处理中断 还有2
79、0; 7. switch (type) /处理cmd data event的请求 8. case MVMS_CMD: 9. ret = if_sdio_handle_cmd(card, card->buffer + 4, chunk - 4);
80、;/处理申请的命令中断 10. if (ret) 11. goto out; 12. break; 13. case MVMS_DAT: 14. ret = if_sdio_handle_data(card, card->buffer + 4, chunk -
81、160;4);/处理申请的数据中断 15. if (ret) 16. goto out; 17. break; 18. case MVMS_EVENT: 19. ret = if_sdio_handle_event(card, card->buffer + 4,
82、;chunk - 4);/处理申请的事件中断 20. 21. /读取包的过程 22. lbs_process_rxed_packet(card->priv, skb); 23. 24. /如果是中断 ,就把skb这个包提交给协议层,这个函数是 25. /协议层提供的 netif_rx(skb) 26.
83、 if (in_interrupt() 27. netif_rx(skb); /提交给协议层 28. 29. 30. 2/读取端口上的数据 ,放到card的buffer中 31. ret = sdio_readsb(card->func, card->buffer, ca
84、rd->ioport, chunk); 32. /读取地址,目的地址,数量 等 33. int sdio_readsb(struct sdio_func *func, void *dst, unsigned int addr, int count) 34. 35. retur
85、n sdio_io_rw_ext_helper(func, 0, addr, 0, dst, count); 36. 37. ret = mmc_io_rw_extended(func->card, write,func->num, addr,
86、60;incr_addr, buf,blocks, func->cur_blksize); 38. cmd.arg = write ? 0x80000000 : 0x00000000;
87、0; 39. 40.
88、160; /wait for request 41. mmc_wait_for_req(card->host, &mrq); 42.
89、0; 开始应答 43.
90、160; mmc_start_request(host, mrq); 44. wait_for_completion(&complete); 45.
91、60; 46.
92、; host->ops->request(host, mrq); 4、数据发送cpp view plain copy1. /IP层通过dev_queue_xmit()将数据交给网络设备协议接口层,网络接口层通过netdevice中的注册函数的数据发送函数 2. int dev_queue_xmit(struct sk_buff *skb) &
93、#160;3. 4. if (!netif_tx_queue_stopped(txq) 5. _this_cpu_inc(xmit_recursion); 6. /设备硬件开始发送 7. rc = dev_hard_start_xmit(skb, dev
94、, txq); 8. /调用wifi网络中的ops 9. 10. rc = ops->ndo_start_xmit(skb, dev); 11. 12. dev->netdev_ops = &lbs_netdev_ops; /设备的操作函数
95、13. 14. /处理sdio firware数据和内核的数据main_thread 主线程 15. priv->main_thread = kthread_run(lbs_thread, dev, "lbs_main"); 16. 17. /调用host_to_card 即if_sdio_card_t
96、o_host函数。 18. int ret = priv->hw_host_to_card(priv, MVMS_DAT,priv->tx_pending_buf,priv->tx_pending_len); 19. 为什么是if_sdio_to_host呢 ?因为在prob函数中定义了这一个 20. /设置主机发送数据到卡 21. priv->hw_host_to_card
97、 = if_sdio_host_to_card; 22. 23. static int if_sdio_host_to_card(struct lbs_private *priv,u8 type, u8 *buf, u16 nb) 24. /把buf中的数据 copy到sdio 包中,在对sd
98、io 的包进行处理 25. memcpy(packet->buffer + 4, buf, nb); 26. /创建工作队列 27. queue_work(card->workqueue, &card->pa
99、cket_worker); 28. /初始化队列 29. INIT_WORK(&card->packet_worker, if_sdio_host_to_card_worker); 30. 31. /sdio的写数据 32. ret = sdio_writesb(card->func, card
100、->ioport, packet->buffer, packet->nb); 33. /mmc写扩展口 34. ret = mmc_io_rw_extended(func->card, write,func->num, addr, incr_addr, buf,blocks, func->cur_blksize); &
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 网络攻击防御与数据安全恢复手册
- 小学主题班会课件:心理健康呵护快乐健康成长
- 小学主题班会课件:孝顺父母,尊敬师长
- 汽车销售业务技巧作业指导书
- 催办2026年年度财务结算确认函(6篇)
- 学习方法提升学习效率小学主题班会课件
- 建筑行业工程验收与质量检测标准手册
- 2026年铜川市耀州区事业单位人员招聘考试模拟试题及答案详解
- 2026年铁岭市银州区事业单位人员招聘笔试模拟试题及答案详解
- 2026年张家口市桥西区事业单位人员招聘考试参考题库及答案详解
- 2026秋人教版小学数学三升四年级暑期27天每日练习卷
- 2026年推拿手法学考试题及答案
- 反假币培训试题及答案
- 2025年山东公务员录用考试《申论》真题及答案解析
- 公司2026年上半年工作总结及下半年工作计划
- 房屋解押合同范本
- 八年级上册道德与法治知识点清单
- 乔木支撑架施工方案
- 急性左心衰的护理查房
- 沼气发电示范工程工程可研报告(完整版)资料
- GB/T 9276-1996涂层自然气候曝露试验方法
评论
0/150
提交评论