实时linux下的PCI驱动开发(上)

     第一篇博客,忆苦思甜下先,当然,我尽量长话短说,但说来话长倒也无妨......这是我研究生阶段写的第一个Linux驱动,一入Linux深似海,从此Windows是路人。那是2009年冬天的第一场雪,王老师兴冲冲地拿着一块板卡给我说,你不是会Linux吗?三个月,把它的Linux驱动写出来。而我此时所谓的“Linux”,也就是在虚拟机下装个RedHat,上上网听听歌,看看电影装装X。对linux内核一点概念都没有,更不用说驱动了,于是天天上网查资料,把师兄的新书都翻蓬松了,最后参照着一堆模板写下了人生的第一个驱动,写完了发现都不知道怎么编译......最后,当系统第一次显示驱动加载成功,打印出我的设备信息时,我高兴得一拳狠狠砸在在桌子上,把身边的同学都吓住了。其实这个时候驱动也就仅仅能加载成功而已,设备的很多功能都没实现,但是这个时候的成就感,比日后实现了驱动的全部功能与用户态应用程序时,都强烈得多。现在想来,真是感谢王老师对我的宏观指导点拨、胡师兄陪我的微观编译调试、以及黄师兄借我的那本《Linux设备驱动开发详解》。

       好了,正文开始,驱动的开发环境是Linux2.4.18与实时补丁Rtai-24.1.11,硬件平台是i386,就是自己的PC机啦。最开始接到任务的时候,完全不知道干嘛,如今看来,就是要写一个PCI2040 PCI-DSP桥接卡的驱动,驱动包括设备与主机连接的PCI部分以及与板载DSP连接的HPI部分。

       PCI驱动与其他驱动不同的地方主要在于PCI标准规定了每个PCI设备都有至少256字节的地址空间,其中前64字节是标准化的,其余字节是设备相关的。前64字节标准配置寄存器如下所示。驱动初始化时的很多步骤都会用到这个配置空间。


    

    首先自然要定义驱动的入口函数和出口函数,

    module_init(hpi_init_module);
    module_exit(hpi_cleanup_module);

    既然我们是要编写pci驱动,当然要定义一个pci_driver啦,具体实现如下

  static struct pci_driver driver_details __devinitdata=
  {
        .name     =  DRV_NAME,
        .id_table =  compatible_ids,
        .probe    =  pci_probe,
        .remove   =  __devexit_p(pci_remove),
  };

        这里我们就第一次用到了pci配置空间了,因为系统是根据id_table这一栏来识别PCI设备的:id_table的包含数个pci_device_id,每个pci_device_id标明了对应PCI设备的制造商ID和设备ID,同样,设备上的配置空间的前四个字节包含了制造商ID和设备ID,当驱动存在一个ID与PCI设备配置空间ID相匹配时,设备驱动才会与硬件关联起来。根据PCI2040的datasheet,我定义的pci_device_id如下:

#define VENDOR_ID 0x104c

#define DEVICE_ID 0xac60

static struct pci_device_id compatible_ids [] __devinitdata=
{
    {VENDOR_ID,DEVICE_ID,PCI_ANY_ID,PCI_ANY_ID,0,0,0},
    {0,}
};

     hpi_init_module()基本不需要干什么事情,只要调用函数pci_module_init(&driver_details)就可以了。这样,当系统检测到PCI设备时,会自动调用driver_details中的probe函数,也就是我们自己定义的pci_probe。驱动初始化的主要工作都在这个函数中进行。下面说明了一般PCI设备驱动需要的初始化工作,设备相关部分已被略去。

int __devinit pci_probe(struct pci_dev *dev, const struct pci_device_id * id)

{

......
//使能PCI设备

    err=pci_enable_device(dev);
......
//申请PCI资源空间
    err=pci_request_regions(dev, data->name);
......

//读取PCI设备的IO区域的首地址,每个PCI设备最多可有六个IO区域,第i个区域的首地址在PCI配置空间的0x10+(4*i)地址中,i=0~5。

    start=pci_resource_start(dev, i);

//读取PCI设备的IO区域的尾地址,首尾地址相减后加一即可得到这篇IO区域的长度,长度在ioremap时会用到
    end=pci_resource_end(dev, i);

//读取PCI设备的IO区域的标识,主要看是否是IO区域还是MEM区域
    flags=pci_resource_flags(dev, i);
    if(IS_VALID_PCI_RESOURCE(flags, IORESOURCE_MEM)){
    data->mmio_addr_csr=ioremap(start, end-start+1);

     }

//设备取得对PCI总线的控制   
    pci_set_master(dev);
......
//把设备的私有信息放入系统分配的PCI设备地址中
pci_set_drvdata(dev, data);

......
}

    至此,本驱动的PCI部分已经完成,系统可以识别我们的PCI设备,但是如何与板载的DSP通信呢?我们还需要在pci_probe中完成对HPI部分的初始化。




  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值