pci设备驱动例程

/*必须包含的两个头文件*/
#include <linux/module.h>
#include <linux/pci.h>

/*自定义结构体,用在中断服务函数里面*/
struct pci_Card {
   resource_size_t io;     /*端口读写变量*/
   long range, flags;      /*io地址范围,标志位*/
   void __iomem *ioaddr;   /*io映射地址*/
   int irq;                /*中断号*/
};

/*驱动程序支持的设备列表,如果有匹配的设备,那么改驱动程序就会被执行*/
static struct pci_device_id ids[] = {
    { 
        PCI_DEVICE(PCI_VENDOR_ID_INTEL, /*vendor id, 厂家ID*/
        0x100f)                         /*device id, 设备ID,可以通过lspci或者去相应的目录里面看*/
    },

    { 
        PCI_DEVICE(PCI_VENDOR_ID_INTEL, 
        PCI_DEVICE_ID_INTEL_80332_0) 
    },

    {0,} /*最后一组为0,表示结束*/
};

/*进行注册,pci总线,ids为上面定义的设置*/
MODULE_DEVICE_TABLE(pci, ids);

/*打印配置空间里面的一些信息*/
void skel_get_configs(struct pci_dev *dev) {

    uint8_t revisionId;
    uint16_t vendorId, deviceId;
    uint32_t classId;

    /*
    从参数dev里面也是可以打印vendorID等信息的,
    这个结构体里面包含这个成员变量,用下面的API
    获取得到的结果也是一致的
    */

    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_unregister_driver(&pci_driver);
}

MODULE_LICENSE("GPL");

module_init(pci_module_init);
module_exit(pci_module_exit);

makefile

obj-m   := pci_module.o

KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD       := $(shell pwd)

all:
        $(MAKE) -C $(KERNELDIR) M=$(PWD)
clean:

执行前先把e1000卸载了

sudo rmmod e1000

然后,执行

sudo insmod pci_module.ko
dmesg

在这里插入图片描述
在这里插入图片描述
比较重要的一个结构体。

/*
    描述一个pci设备,每个pci驱动必须创建一个pci_driver实例
*/
struct pci_driver {
    struct list_head node;
    /* 驱动程序名,内核中所有pci驱动程序名都是唯一的 */
    const char *name;
    /* pci设备配置信息数组 */
    const struct pci_device_id *id_table;    /* must be non-NULL for probe to be called */
    /* 设备插入内核时调用 */
    int  (*probe)  (struct pci_dev *dev, const struct pci_device_id *id);    /* New device inserted */
    /* 设备从内核移除时调用 */
    void (*remove) (struct pci_dev *dev);    /* Device removed (NULL if not a hot-plug capable driver) */
    /* 设备被挂起时调用 */
    int  (*suspend) (struct pci_dev *dev, pm_message_t state);    /* Device suspended */
    int  (*suspend_late) (struct pci_dev *dev, pm_message_t state);
    int  (*resume_early) (struct pci_dev *dev);
    /* 设备被恢复时调用 */
    int  (*resume) (struct pci_dev *dev);                    /* Device woken up */
    /* 设备关闭时调用 */
    void (*shutdown) (struct pci_dev *dev);
    int (*sriov_configure) (struct pci_dev *dev, int num_vfs); /* PF pdev */
    /* 错误处理 */
    const struct pci_error_handlers *err_handler;
    struct device_driver    driver;
    struct pci_dynids dynids;
};

参考:
1:https://www.kernel.org/doc/html/latest/PCI/pci.html
2:http://blog.chinaaet.com/justlxy/p/5100053251

  • 0
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值