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);
相关推荐
远离UE422 分钟前
UE5 渲染管线 学习笔记
笔记·学习·ue5
cwtlw1 小时前
CSS学习记录20
前端·css·笔记·学习
虾稿2 小时前
[手机Linux] 七,NextCloud优化设置
linux·运维·服务器
首发运维2 小时前
centos 释放系统预留内存并关闭Kdump服务
linux·运维·centos·linux操作系统问题
新子-存在了2 小时前
linux中 mysql备份
linux·运维·mysql
最后一个bug2 小时前
rt-linux中使用mlockall与free的差异
linux·c语言·arm开发·单片机·嵌入式硬件·算法
ZHOUPUYU2 小时前
VMware虚拟机超详细安装Linux教程(最新版)
linux·运维·服务器·windows·微软·centos·虚拟机
汇能感知2 小时前
光谱相机的工作原理
经验分享·笔记·科技·相机
紫罗兰盛开2 小时前
分布式调度框架学习笔记
笔记·学习
初级代码游戏2 小时前
关于linux的ld.so.conf.d
linux·运维·服务器