蓝牙驱动分析

这个驱动分析的是OK6410开发板自带的内核版本是linux3.0.1,所支持的wifi和蓝牙一体芯片是marvell的8688和8787.根据开发板的设计,芯片与主机之间是通过sdio协议接口通信的,所以驱动也是通过sdio的方式写的。

个人分析驱动的过程是从插入设备驱动的动作开始的。

首先每次插入设备和拔出设备驱动都会通过终端打印相应的信息,判断在sd卡槽中肯定是触发中断的,通过看硬件原理图和数据手册中的SDMMC控制器可知用于mmc的中断号分别为56和57,回到代码中。在内核启动过程中会调用到smdk6410_machine_init()函数来初始化smdk6410这个机器平台,在这个函数中会通过调用s3c_sdhci0_set_platdata()函数来设置sdhci0这个设备的的平台数据,最后会依次注册smdk6410_devices全局变量中的每个设备其中就包括s3c_device_hsmmc0这个平台设备,设备是注册好了可是驱动有时什么时候注册的呢?通过设备名在代码中查找驱动位于驱动目录下的mmc/host目录下的sdhci-s3c.c文件中。通过该驱动的module_init可知由系统自动运行的是sdhci_s3c_init()函数而在该函数中就是一个sdhci_s3c_driver平台驱动的注册。由平台设备驱动注册的原理知道驱动注册的时候会通过驱动名去匹配挂在平台总线上的设备,而之前我们已经将设备注册过了,所以会匹配到同名的设备,匹配到之后就会调用设备驱动的probe函数,接下来我们来分析sdhci_s3c_driver的probe函数。

在函数的刚开始是通过platform_get_irq()和platform_get_resource()两个函数来获取在设备中分配的中断资源和内存资源,接着就是创建分配内存给struct sdhci_s3c *sc;这个指针,说到这个结构体的内存分配就牵扯到到linux中一个经典的嵌套结构体如何分配到连续内存空间的方法,在这里简单介绍下想分配struct sdhci_s3c结构体就牵扯到struct sdhci_host和struct mmc_host 结构体,因为他们是用指针嵌套的关系通过查看结构体的成员我们可以看到结构体的最后一个变量是unsigned long private[0] ____cacheline_aligned正是由于这个长度为0的long型数组在内存分配时记录下了上层结构体的地址。有点跑题,回到驱动上来,接着分析probe函数接着就是对分配结构体里面的变量依次初始化了,初始化结束后通过sdhci_add_host()函数将主控制器的结构体添加到sdhci中,接着分析sdhci_add_host()函数。这个函数很长主要是对struct mmc_host结构体的初始化,接着我们可以看到如下的代码:

tasklet_init(&host->card_tasklet,

sdhci_tasklet_card, (unsigned long)host);

tasklet_init(&host->finish_tasklet,

sdhci_tasklet_finish, (unsigned long)host);

初始化了两个中断的底半部处理函数,接着代码通过request_irq()就进行了中断的申请和注册中断函数sdhci_irq(),到此我们知道每次当我们插入设备的时候就会触发这个中断并运行中断处理函数,在中断处理函数中我们看到tasklet_schedule(&host->card_tasklet);这个语句将任务交给底半部sdhci_tasklet_card函数去处理,在底半部中最后通过mmc_detect_change(host->mmc, msecs_to_jiffies(200));函数延时200毫秒后调用host->mmc->detect()函数即,可是detect函数指针的实体是哪个函数呢?我们想应该是在申请初始化mmc结构体的时候初始化的查看代码是在mmc_alloc_host函数中通过

INIT_DELAYED_WORK(&host->detect, mmc_rescan);

语句初始化延时工作的。由此可知中断底半部实际上调用的是mmc_rescan()函数接着我们分析这个函数,在这个函数中首先通过传入的指针经结构体的转换获取struct mmc_host的指针,接着会判断bus_ops指针是否为空,由于之前并没有对bus_ops赋值,所以程序会继续运行到mmc_rescan_try_freq()函数,在mmc_rescan_try_freq函数中首先通过mmc控制器给mmc设备上电,上电之后发送SD_SEND_IF_COND 几CMD8命令给sd看是否有回应来判断是sd 2.0的卡还是1.0的卡,接着我们可以看到如下几行代码

if (!mmc_attach_sdio(host))

return 0;

if (!mmc_attach_sd(host))

return 0;

if (!mmc_attach_mmc(host))

return 0;

从函数名的字面意思我们可以看出是依次来匹配插入的设备是sdio存储卡还是sd卡还是spi的设备。我们的蓝牙wifi模块使用的是sdio的接口因此在mmc_attach_sdio函数中就应该匹配到并返回0值,但是具体怎么匹配的我们进入到mmc_attach_sdio函数中看。在mmc_attach_sdio函数中首先会发送CMD5命令给设备通过判断是否有回应和回应的标志位来判断是不是sdio设备,如果发送CMD5命令后有回应则可判断为sdio设备函数继续执行否则就会返回,接着函数会调用mmc_attach_bus函数来初始化bus_ops指针为mmc_sdio_ops总线操作集。接着设置和插入设备相匹配的电压,接着调用mmc_sdio_init_card函数来申请初始化struct card结构体,接着调用sdio_init_func函数来初始化card结构体里面的sdio_func结构体就是我们所对应sdio的设备。初始化结束后我们看到通过mmc_add_card(host->card);函数将card设备注册到内核的mmc_bus_type总线上,接着会调用sdio_add_func函数将func设备即我们的sdio设备注册到sdio_bus_type总线上,到目前为止从我们的设备插入到卡槽后内核所做的动作基本完成,虽然我们将具体的sdio设备注册到了内核中可是又怎么匹配到我们预先的驱动的呢?我们知道插入设备最后我们是将该sdio的设备注册到sdio_bus_type总线上的,我们知道在设备注册时会去在总线上匹配相应的驱动是通过总线的match函数即sdio_bus_match函数,通过分析该函数我们知道该总线的匹配原则是判断驱动的sdio_device_id结构体的sdio接口ID、设备的厂商ID和设备ID与插入设备的厂商ID、设备ID和sdio接口ID是否相等来匹配驱动的。可是在设备插入的时候是怎么知道该设备的厂商信息和设备ID的呢,继续返回代码查看得知在调用sdio_init_func函数的时候在函数里面调用sdio_read_func_cis函数去读取设备的CIS寄存器从而获得设备的信息与驱动进行匹配。

好了我们回到marvell的蓝牙wifi一体芯片上来,驱动文件在驱动目录下的bluetooth中文件为btmrvl_sdio.c,模块的初始化函数为btmrvl_sdio_init_module该函数比较简短如下

static int __init btmrvl_sdio_init_module(void)

{

if (sdio_register_driver(&bt_mrvl_sdio) != 0) {

BT_ERR("SDIO Driver Registration Failed");

return -ENODEV;

}

user_rmmod = 0;

return 0;

}

我们看红色标志的函数如下:

int sdio_register_driver(struct sdio_driver *drv)

{

drv->drv.name = drv->name;

drv->drv.bus = &sdio_bus_type;

return driver_register(&drv->drv);

}

比较简单看到了我们之前注册插入设备的总线(红色部分),这样驱动和设备都注册到了sdio_bus_type总线上并且通过设备的信息相互匹配。我们再来看看驱动定义设备信息是在什么地方,我们看注册的驱动结构体bt_mrvl_sdio

static struct sdio_driver bt_mrvl_sdio = {

.name = "btmrvl_sdio",

.id_table = btmrvl_sdio_ids,

.probe = btmrvl_sdio_probe,

.remove = btmrvl_sdio_remove,

};

static const struct sdio_device_id btmrvl_sdio_ids[] = {

{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x9105),

.driver_data = (unsigned long) &btmrvl_sdio_sd6888 },

{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x911A),

.driver_data = (unsigned long) &btmrvl_sdio_sd8787 },

{ }

};

可知在btmrvl_sdio_ids中预先定义了设备的信息。

蓝牙驱动分析 linux的更多相关文章

  1. Linux 串口、usb转串口驱动分析(2-2) 【转】

    转自:http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=26807463&id=4186852 Linux 串口.usb转 ...

  2. Linux 串口、usb转串口驱动分析(2-1) 【转】

    转自:http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=26807463&id=4186851 Linux 串口.usb转 ...

  3. linux驱动基础系列--Linux mmc sd sdio驱动分析

    前言 主要是想对Linux mmc子系统(包含mmc sd sdio)驱动框架有一个整体的把控,因此会忽略某些细节,同时里面涉及到的一些驱动基础,比如平台驱动.块设备驱动.设备模型等也不进行详细说明原 ...

  4. linux驱动基础系列--Linux 串口、usb转串口驱动分析

    前言 主要是想对Linux 串口.usb转串口驱动框架有一个整体的把控,因此会忽略某些细节,同时里面涉及到的一些驱动基础,比如字符设备驱动.平台驱动等也不进行详细说明原理.如果有任何错误地方,请指出, ...

  5. linux的串口驱动分析

    1.串口驱动中的数据结构 • UART驱动程序结构:struct uart_driver  驱动 • UART端口结构: struct uart_port  串口 • UART相关操作函数结构: st ...

  6. linux内核SPI总线驱动分析(一)(转)

    linux内核SPI总线驱动分析(一)(转) 下面有两个大的模块: 一个是SPI总线驱动的分析            (研究了具体实现的过程) 另一个是SPI总线驱动的编写(不用研究具体的实现过程) ...

  7. linux串口驱动分析

    linux串口驱动分析 硬件资源及描写叙述 s3c2440A 通用异步接收器和发送器(UART)提供了三个独立的异步串行 I/O(SIO)port,每一个port都能够在中断模式或 DMA 模式下操作 ...

  8. linux i2c驱动架构-dm368 i2c驱动分析

      linux i2c驱动架构-dm368 i2c驱动分析   在阅读本文最好先熟悉一种i2c设备的驱动程序,并且浏览一下i2c-core.c以及芯片提供商的提供的i2c总线驱动(i2c-davinc ...

  9. Linux SD/MMC/SDIO驱动分析_转

    转自:Linux SD/MMC/SDIO驱动分析    https://www.cnblogs.com/cslunatic/p/3678045.html#3053341 一.SD/MMC/SDIO概念 ...

随机推荐

  1. 20181225模拟赛 T1 color (转化思想,分拆思想)

    题目: 有⼀块有 n 段的栅栏,要求第 i 段栅栏最终被刷成颜色 ci .每⼀次可以选择 l, r 把第l . . . r 都刷成某种颜色,后刷的颜⾊会覆盖之前的.⼀共有 m 种颜色,雇主知道只需要用 ...

  2. [Luogu] P4838 P哥破解密码

    题目背景 P哥是一个经常丢密码条的男孩子. 在ION 8102赛场上,P哥又弄丢了密码条,笔试满分的他当然知道这可是要扣5分作为惩罚的,于是他开始破解ION Xunil系统的密码. 题目描述 定义一个 ...

  3. NodeJs中数据库的使用

    另一遍通用的NODEJS数据库方法koa,express,node 通用方法连接MySQL 1.Node.js 连接 MySQL $ cnpm install mysql 连接mysql: var m ...

  4. LeetCode 123. Best Time to Buy and Sell Stock III (stock problem)

    Say you have an array for which the ith element is the price of a given stock on day i. Design an al ...

  5. git clone问题

    中秋节回来上班 竟然忘记带电脑了  ̄□ ̄||还好同事有备用电脑,这要是回去拿估计上午都不用干什么了,用同事电脑当然需要安装环境,下面说一下git上遇到的问题吧 (1)首先我尝试用https方式克隆代码 ...

  6. Android Studio 使用图片

    首先将图片放在drawable下 然后: <ImageView android:layout_width="wrap_content" android:layout_heig ...

  7. python接口测试之Http请求(三)

    python的强大之处在于提供了很多的标准库,这些标准库可以直接调用,本节部分,重点学习和总结在 接口测试中Python的Http请求的库的学习. 首先来看httplib,官方的解释为:本模块定义了类 ...

  8. c#读取MySQL数据表中的内容

    using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; usin ...

  9. 添物不花钱学JavaEE(基础篇)-XML

    XML(Extensible Markup Language) XML在日常工作中经常用到,必须有个了解,不过认识一下即可,不要太浪费时间.实际用到 参考图书 <XML入门经典>大而全,不 ...

  10. 华中农业大学第四届程序设计大赛网络同步赛-1020: Arithmetic Sequence,题挺好的,考思路;

    1020: Arithmetic Sequence Time Limit: 1 Sec  Memory Limit: 128 MB Submit:  ->打开链接<- Descriptio ...