Linux源码阅读笔记20-PCI设备驱动详解

PCI基础

  1. PCI总线为高性能局部总线,主要解决外部设备之间以及外部设备与主机之间高速数据传输。在数字图形、图像等处理,以及告诉实时数据采集与处理等队数据传输速率要求高的应用中,采用PCI总线进行数据传输。
  2. PCI规范能够实现32位并行数据传输,工作频率为33MHz或66MHz,最大吞吐率为266MB/s。PCI-E是目前PCI系列最具代表性的一种,与并行PCI总线区别在于它采用串行传输数据模式,最大支持32个串行连接。每个传输方向上吞吐率为250MB/s,方向上总传输速率达8GB/s。
  3. 串行通讯比并行通讯速度快,成本低。比如PCIe64位总线,133MHz,吞吐率1GB/s,内存于台式机和服务器,比PCI宽,但是能插入PCI卡。
  4. PCI总线特点
    • 具有隐含的中央仲裁系统。
    • 具有与处理器和存储器自诩痛完全并行操作的能力。
    • 提供地址和数据的奇偶校验,完全的多总线主控能力。

PCI数据结构

  1. 连接CPU和PCI系统对应的数据结构类型pci_host_bridge
  1. 描述PCI总线对应的数据结构类型pci_bus
  1. 用于指向PCI读写操作函数集的结构体类型pci_ops
  1. 专门用于描述总线的物理插槽的结构体类型pci_slot
  1. PCI设备结构体类型pci_dev
  1. PCI驱动程序结构体类型pci_driver
  1. PCI卡标识结构类型pci_driver_i

PCI驱动实例

c 复制代码
/* PCI设备驱动编程,必须包括两个核心重要的头文件 */
#include <linux/module.h>
#include <linux/pci.h>

/* 用户自定义结构体类型,作用于中断服务函数里面 */
struct pci_Card 
{
   resource_size_t io;
   long range, flags;
   void __iomem *ioaddr;
   int irq;
};


static struct pci_device_id ids[] = 
{
    { 
        PCI_DEVICE(PCI_VENDOR_ID_INTEL,
        0x100f)
    },

    { 
        PCI_DEVICE(PCI_VENDOR_ID_INTEL, 
        PCI_DEVICE_ID_INTEL_80332_0) 
    },

    {0,}
};


MODULE_DEVICE_TABLE(pci, ids);


void skel_get_configs(struct pci_dev *dev) 
{

    uint8_t revisionId;
    uint16_t vendorId, deviceId;
    uint32_t classId;

    pci_read_config_word(dev, PCI_VENDOR_ID, &vendorId);
    printk("vendorID = %x", vendorId);

    pci_read_config_word(dev, PCI_DEVICE_ID, &deviceId);
    printk("deviceID = %x", deviceId);

    pci_read_config_byte(dev, PCI_REVISION_ID, &revisionId);
    printk("revisionID = %x",revisionId);

    pci_read_config_dword(dev, PCI_CLASS_REVISION, &classId);
    printk("classID = %x",classId);
}


static irqreturn_t pci_Mcard_interrupt(int irq, void *dev_id) 
{
   struct pci_Card *pci_Mcard = (struct pci_Card *)dev_id;

   printk("irq = %d, pci_Mcard_irq = %d\n", irq, pci_Mcard->irq);
   return IRQ_HANDLED;
}


static int probe(struct pci_dev *dev, const struct pci_device_id *id) 
{
    int retval = 0;
    struct pci_Card *pci_Mcard;
    printk("probe func\n"); 
    
    /* 设备使能 */
    if(pci_enable_device(dev)) {
        printk (KERN_ERR "IO Error.\n");
        return -EIO;
    }

    pci_Mcard = kmalloc(sizeof(struct pci_Card),GFP_KERNEL);
    if(!pci_Mcard) {
        printk("In %s,kmalloc err!",__func__);
        return -ENOMEM;
    }

    /* 设备中断号 */
    pci_Mcard->irq = dev->irq;
    if(pci_Mcard->irq < 0) {
        printk("IRQ is %d, it's invalid!\n",pci_Mcard->irq);
        goto out_pci_Mcard;
    }

    /*获取io内存相关信息*/
    pci_Mcard->io = pci_resource_start(dev, 0);
    pci_Mcard->range = pci_resource_end(dev, 0) - pci_Mcard->io + 1;
    pci_Mcard->flags = pci_resource_flags(dev,0);
    printk("start %llx %lx %lx\n",pci_Mcard->io, pci_Mcard->range, pci_Mcard->flags);
    printk("PCI base addr 0 is io%s.\n",(pci_Mcard->flags & IORESOURCE_MEM)? "mem":"port");

    /*防止地址访问冲突,所以这里先申请*/
    retval = pci_request_regions(dev,"pci_module");
    if(retval) {
        printk("PCI request regions err!\n");
        goto out_pci_Mcard;
    }

    /*再进行映射*/
    pci_Mcard->ioaddr = pci_ioremap_bar(dev, 0);
    if(!pci_Mcard->ioaddr) {
      printk("ioremap err!\n");
      retval = -ENOMEM;
      goto out_regions;
    }

    /*申请中断IRQ并设定中断服务子函数*/
    retval = request_irq(pci_Mcard->irq, pci_Mcard_interrupt, IRQF_SHARED, "pci_module", pci_Mcard);
    if(retval) {
      printk (KERN_ERR "Can't get assigned IRQ %d.\n",pci_Mcard->irq);
      goto out_iounmap;
    }

    pci_set_drvdata(dev, pci_Mcard);
    skel_get_configs(dev);
    return 0;

out_iounmap:
    iounmap(pci_Mcard->ioaddr);
out_regions:
    pci_release_regions(dev);
out_pci_Mcard:
    kfree(pci_Mcard);
    return retval;
}

/* 移除PCI设备 */
static void remove(struct pci_dev *dev) 
{
   struct pci_Card *pci_Mcard = pci_get_drvdata(dev);
   free_irq (pci_Mcard->irq, pci_Mcard);
   iounmap(pci_Mcard->ioaddr);
   pci_release_regions(dev);
   kfree(pci_Mcard);
   pci_disable_device(dev);
   printk("remove pci device ok\n");
}

/* 结构体成员变量填充 */
static struct pci_driver pci_driver = 
{
    .name = "pci_module",
    .id_table = ids,
    .probe = probe,
    .remove = remove,
};

/* 模块入口函数 */
static int __init pci_module_init(void) 
{
    printk("PCI module entry function\n");

    return pci_register_driver(&pci_driver);
}

/* 模块退出函数 */
static void __exit pci_module_exit(void) 
{
    printk("PCI module exit function.\n");

    // PCI驱动被卸载时,需要调用pci_unregister_driver
    pci_unregister_driver(&pci_driver);
}

MODULE_LICENSE("GPL");

module_init(pci_module_init);
module_exit(pci_module_exit);
相关推荐
xuanzdhc8 分钟前
Linux 基础IO
linux·运维·服务器
愚润求学13 分钟前
【Linux】网络基础
linux·运维·网络
bantinghy44 分钟前
Linux进程单例模式运行
linux·服务器·单例模式
小和尚同志2 小时前
29.4k!使用 1Panel 来管理你的服务器吧
linux·运维
帽儿山的枪手2 小时前
为什么Linux需要3种NAT地址转换?一探究竟
linux·网络协议·安全
shadon1789 天前
回答 如何通过inode client的SSLVPN登录之后,访问需要通过域名才能打开的服务
linux
小米里的大麦9 天前
014 Linux 2.6内核进程调度队列(了解)
linux·运维·驱动开发
算法练习生9 天前
Linux文件元信息完全指南:权限、链接与时间属性
linux·运维·服务器
忘了ʷºᵇₐ9 天前
Linux系统能ping通ip但无法ping通域名的解决方法
linux·服务器·tcp/ip
浩浩测试一下9 天前
渗透测试指南(CS&&MSF):Windows 与 Linux 系统中的日志与文件痕迹清理
linux·运维·windows·安全·web安全·网络安全·系统安全