evenaye
驱动牛犊
驱动牛犊
  • 注册日期2007-04-02
  • 最后登录2016-01-09
  • 粉丝0
  • 关注0
  • 积分210分
  • 威望23点
  • 贡献值0点
  • 好评度22点
  • 原创分0分
  • 专家分0分
阅读:3387回复:13

请教Linux下PCI驱动初始化问题

楼主#
更多 发布于:2007-05-03 00:46
  rt
在做一块PCI采集卡的驱动,2.4下,用的9052,碰到个问题,请教一下
PCI驱动的file_operations结构是如何被初始化的啊?在网上找了几个驱动看了看,没看到这部分,pci_module_init()初始化的是一个pci_driver结构,而常规的char设备初始化(register_chrdev())的是file_operations结构,这里如何与file_operations联系到一起的啊?
请高手指教一下,或者能提供一个完整的PCI驱动学习一下,谢谢的说!
cyliu
论坛版主
论坛版主
  • 注册日期2003-06-13
  • 最后登录2014-04-11
  • 粉丝5
  • 关注0
  • 积分1238分
  • 威望2531点
  • 贡献值0点
  • 好评度577点
  • 原创分14分
  • 专家分10分
沙发#
发布于:2007-05-04 10:18
pci只有sysfs文件系统

1 bus_register(&pci_bus_type) : 注册bus总线

2 int __pci_register_driver(struct pci_driver *drv, struct module *owner)注册pci驱动

int __pci_register_driver(struct pci_driver *drv, struct module *owner)
{
    int error;

    /* initialize common driver fields */
    drv->driver.name = drv->name;
    drv->driver.bus = &pci_bus_type;
    drv->driver.owner = owner;
    drv->driver.kobj.ktype = &pci_driver_kobj_type;//注意这里

    spin_lock_init(&drv->dynids.lock);
    INIT_LIST_HEAD(&drv->dynids.list);

    /* register with core */
    error = driver_register(&drv->driver); //注意这里

    if (!error)
        error = pci_create_newid_file(drv);//注意这里

    return error;
}

3 driver_register中调用了
int bus_add_driver(struct device_driver * drv)
{
    struct bus_type * bus = get_bus(drv->bus);
    int error = 0;

    if (bus) {
        pr_debug("bus %s: add driver %s\n", bus->name, drv->name);
        error = kobject_set_name(&drv->kobj, "%s", drv->name);
        if (error) {
            put_bus(bus);
            return error;
        }
        drv->kobj.kset = &bus->drivers;
        if ((error = kobject_register(&drv->kobj))) {
            put_bus(bus);
            return error;
        }

        driver_attach(drv);
        klist_add_tail(&drv->knode_bus, &bus->klist_drivers);
        module_add_driver(drv->owner, drv);

        driver_add_attrs(bus, drv);
        add_bind_files(drv);//注意这里
    }
    return error;
}

4 static void add_bind_files(struct device_driver *drv)
{
    driver_create_file(drv, &driver_attr_unbind);
    driver_create_file(drv, &driver_attr_bind);
}

int driver_create_file(struct device_driver * drv, struct driver_attribute * attr)
{
    int error;
    if (get_driver(drv)) {
        error = sysfs_create_file(&drv->kobj, &attr->attr); //注册sysfs文件系统
        put_driver(drv);
    } else
        error = -EINVAL;
    return error;
}

5 pci_create_newid_file同样也注册了文件系统
走走看看开源好 Solaris vs Linux
evenaye
驱动牛犊
驱动牛犊
  • 注册日期2007-04-02
  • 最后登录2016-01-09
  • 粉丝0
  • 关注0
  • 积分210分
  • 威望23点
  • 贡献值0点
  • 好评度22点
  • 原创分0分
  • 专家分0分
板凳#
发布于:2007-05-09 10:46
先感谢一下,hoho
但是还没看的很清楚,需要在阅读一下代码
先说一下偶看到的东西,pci_module_init()中仅完成了驱动和设备的关联啊,从代码上看,仅仅能probe,能remove,但是设备的具体操作,就是那些open,close,ioctrl等函数(file_operations结构成员)是如何和设备关联的啊,当然在chr设备中是在初始化时调用register_chrdev(&file_operations)将设备和操作建立关联的。但是PCI设备呢?当用户在应用程序中open设备时,这个open动作是怎么调用到或者说怎么找到驱动里的open函数呢?还是有些不明白
驱动结构参照 http://industry.ccidnet.com/art/302/20050518/252693_3.html 这篇文章

下面是对pci_module_init()的分析
pci_module_init(struct pci_driver *drv)->pci_register_driver(struct pci_driver *drv);

pci_register_driver(struct pci_driver *drv)的原型如下:

int pci_register_driver(struct pci_driver *drv)

{
   struct pci_dev *dev;
   int count = 0;
   list_add_tail(&drv->node, &pci_drivers); //把drv链接到双向链表pci_drivers
   pci_for_each_dev(dev) { //正向遍历全局设备链表pce_devices中的每一个PCI 设备
     if (!pci_dev_driver(dev)) //如果设备还没有相应的驱动
       count += pci_announce_device(drv, dev); //如果找到相应的驱动则返回1
   }
   return count;
}

函数pci_dev_driver()
struct pci_driver *
pci_dev_driver(const struct pci_dev *dev)
{
  if (dev->driver)
      return dev->driver;
  else {
      int i;
      for(i=0; i<=PCI_ROM_RESOURCE; i++)
        if (dev->resource.flags & IORESOURCE_BUSY)
           return &pci_compat_driver;
  }
  return NULL;
}
cyliu
论坛版主
论坛版主
  • 注册日期2003-06-13
  • 最后登录2014-04-11
  • 粉丝5
  • 关注0
  • 积分1238分
  • 威望2531点
  • 贡献值0点
  • 好评度577点
  • 原创分14分
  • 专家分10分
地板#
发布于:2007-05-09 15:31
晕。

不知道你用的那个版本,我说的是2.6内核的。2.6内核的改为sysfs系统了。主要看

int driver_create_file(struct device_driver * drv, struct driver_attribute * attr)
{
  int error;
  if (get_driver(drv)) {
    error = sysfs_create_file(&drv->kobj, &attr->attr); //注册sysfs文件系统
    put_driver(drv);
  } else
    error = -EINVAL;
  return error;
}
走走看看开源好 Solaris vs Linux
evenaye
驱动牛犊
驱动牛犊
  • 注册日期2007-04-02
  • 最后登录2016-01-09
  • 粉丝0
  • 关注0
  • 积分210分
  • 威望23点
  • 贡献值0点
  • 好评度22点
  • 原创分0分
  • 专家分0分
地下室#
发布于:2007-05-09 16:10
说过了啊,2.4下啊,hoho,redhat9
一样感谢!!!
也希望其他朋友帮忙解答一下了
cyliu
论坛版主
论坛版主
  • 注册日期2003-06-13
  • 最后登录2014-04-11
  • 粉丝5
  • 关注0
  • 积分1238分
  • 威望2531点
  • 贡献值0点
  • 好评度577点
  • 原创分14分
  • 专家分10分
5楼#
发布于:2007-05-09 19:52
sorry,没看到。呵呵
走走看看开源好 Solaris vs Linux
evenaye
驱动牛犊
驱动牛犊
  • 注册日期2007-04-02
  • 最后登录2016-01-09
  • 粉丝0
  • 关注0
  • 积分210分
  • 威望23点
  • 贡献值0点
  • 好评度22点
  • 原创分0分
  • 专家分0分
6楼#
发布于:2007-05-09 22:29
没看到无所谓啊
问题呢?hoho
问了好几个地方了,就你和学校的一个兄弟给个回答,而且都是斑竹...
cyliu
论坛版主
论坛版主
  • 注册日期2003-06-13
  • 最后登录2014-04-11
  • 粉丝5
  • 关注0
  • 积分1238分
  • 威望2531点
  • 贡献值0点
  • 好评度577点
  • 原创分14分
  • 专家分10分
7楼#
发布于:2007-05-10 13:09
static int __init pci_proc_init(void)
{
         if (pci_present()) {
                 struct proc_dir_entry *entry;
                 struct pci_dev *dev;
                
                 /* 创建文件操作 */
                 proc_bus_pci_dir = proc_mkdir("pci", proc_bus);
                 entry = create_proc_entry("devices", 0, proc_bus_pci_dir);
                 if (entry)
                         entry->proc_fops = &proc_bus_pci_dev_operations;
                        
                /* 这里pci_proc_init是__init操作,而其他pci设备初始化应该是__devinit,
                 *
                 * 因此把该文件操作附加到所有已经创建的pci设备上。
                 *
                 * 可以看到pci_proc_attach_device是导出符号,对于hotplug设备,他们热差把时调用pci_bus_add_device等附加到设备
                 */
                pci_for_each_dev(dev) {
                         pci_proc_attach_device(dev);
                 }
                 entry = create_proc_entry("pci", 0, NULL);
                 if (entry)
                         entry->proc_fops = &proc_pci_operations;
         }
         return 0;
}
走走看看开源好 Solaris vs Linux
evenaye
驱动牛犊
驱动牛犊
  • 注册日期2007-04-02
  • 最后登录2016-01-09
  • 粉丝0
  • 关注0
  • 积分210分
  • 威望23点
  • 贡献值0点
  • 好评度22点
  • 原创分0分
  • 专家分0分
8楼#
发布于:2007-05-10 13:46
先感谢一下,hoho
斑竹的意见是不是通过proc实现?刚找到一个利用proc实现的例子(后面会给出)
现在假设用户的动作有2个,一个是配置本地寄存器(local端),另外一个是读写数据
在配置空间内,要用到基地址1(本地配置寄存器的PCI地址-映射到I/O的)、基地址2(本地地址空间0)
用基地址0进行动作一,也就是通过这个地址对本地寄存器进行配置
用基地址2进行动作二,也就是通过这个地址进行数据读写,假设卡是个32位的I/O卡,那么对基地址2到基地址2+3范围内的存储空间进行读写就是实现了数据的读写
那么write函数结构
write()
{
。。。
读配置空间,得到需要的地址(基地址1或2)
将数据写入得到的地址范围内
。。。
}
read()
{
。。。
读配置空间,得到需要的地址(基地址1或2)
将数据从得到的地址范围内读出
。。。
}
想的不仔细,大概样子就这样,不知道成不成,呵呵

前面说到的驱动框架
#ifndef CONFIG_PCI
#define CONFIG_PCI
#endif
#ifndef CONFIG_PROC_FS
#define CONFIG_PROC_FS
#endif
#include <linux/pci.h>
#include <linux/proc_fs.h>
#include <linux/module.h>
#include <asm/uaccess.h>
/*****************************************\
|*       IOCTL MACRO DEFINITIONS         *|
\*****************************************/
#define TRNDRV_IOC_MAGIC 'k'
#define TRNDRV_IOCREADCONFREGS _IOR(TRNDRV_IOC_MAGIC, 0, 0x40)
#define TRNDRV_IOCREADOPERREGS _IOR(TRNDRV_IOC_MAGIC, 1, 0x40)
#define TRNDRV_IOC_MAXNR 2
/*****************************************\
|*       IOCTL MACRO DEFINITIONS         *|
\*****************************************/
#define VENDORID 0x1234 // unsigned short for vendor id of the PCI card
#define DEVICEID 0x4321 // unsigned short for device id of the PCI card
/*****************************************\
|*          GLOBAL DEFINITIONS           *|
\*****************************************/
struct pci_dev *pdev = NULL;
int readconfregs(u8 *regs);
int readoperregs(u8 *regs);
/****************************************\
|*     Driver "trndrv.o"`s entries      *|
\****************************************/
loff_t trndrv_llseek(struct file *filp, loff_t off, int whence)
{
    loff_t newpos = -EINVAL;
    return newpos;
}
int trndrv_open(struct inode *inode, struct file *filp)
{
    return 0;
}
int trndrv_release(struct inode *inode, struct file *filp)
{
    return 0;
}
ssize_t trndrv_read(struct file *filp, char *buf, size_t count,
                loff_t *f_pos)
{
    return 0;
}
ssize_t trndrv_write(struct file *filp, const char *buf, size_t count,
                loff_t *f_pos)
{
    return 0;
}
int trndrv_ioctl(struct inode *inode, struct file *filp,
                 unsigned int cmd, unsigned long arg)
{
    u8 regs[256];
    int i;
    switch (cmd)
    {
    case TRNDRV_IOCREADCONFREGS:
        if (readconfregs(regs))
            return -PCIBIOS_BAD_REGISTER_NUMBER;
        for (i = 0; i < 256; i ++)
            put_user(regs, (u8 *)(arg + i));
        break;
    case TRNDRV_IOCREADOPERREGS:
        readoperregs(regs);
        break;
    default:
        return -EINVAL;
    }
    return 0;
}
struct file_operations trndrv_fops = {
    llseek:     trndrv_llseek,
    read:       trndrv_read,
    write:      trndrv_write,
    ioctl:      trndrv_ioctl,
    open:       trndrv_open,
    release:    trndrv_release,
};
/*****************************************\
|*        Self-defined Functions         *|
\*****************************************/
struct proc_dir_entry *create_proc_fops_entry(const char *name,
                          mode_t mode, struct proc_dir_entry *base,
                          void * data)
{
    struct proc_dir_entry *result = create_proc_entry(name, mode, base);
    if (result)
    {
        result->proc_fops = &trndrv_fops;
        result->data = data;
    }
    return result;
}
int readconfregs(u8 *regs)
{
    int result;
    int i;
    for (i = 0; i < 256; i ++)
    {
        result = pci_read_config_byte(pdev, i, regs + i);
        if (result)
        {
            printk(KERN_EMERG "reading config regs error\n");
            return result;
        }
    }
    return 0;
}
int readoperregs(u8 *regs)
{
    return 0;
}
void cleanup_module(void)
    remove_proc_entry("trndrv", 0);
}
int init_module(void)
{
    create_proc_fops_entry("trndrv", 0, NULL, NULL);
    pdev = pci_find_device(VENDORID, DEVICEID, NULL);
    if (pdev == NULL)
    {
        printk(KERN_EMERG "no pci card specified\n");
        return -PCIBIOS_DEVICE_NOT_FOUND;
    }
    return 0;
}

用户部分程序片段
...
int fd;
char *fname = "/proc/trndrv";
void configview(unsigned char *confregs);
void oprview(char *arg);
int main(int argc, char **argv)
{
    unsigned char confregs[256];
    unsigned long base_address;
    unsigned char interrupt_line;
    int result;
    fd = open(fname, O_RDONLY);
    if (fd < 0)
    {
 printf("%s: %s: %s\n", argv[0], fname, strerror(errno));
 exit(1);
    }
    result = ioctl(fd, TRNDRV_IOCREADCONFREGS, confregs);
    if (result < 0)
    {
        printf("%s: %s: reading config regs error\n", argv[0], fname);
        exit(1);
    }
...
evenaye
驱动牛犊
驱动牛犊
  • 注册日期2007-04-02
  • 最后登录2016-01-09
  • 粉丝0
  • 关注0
  • 积分210分
  • 威望23点
  • 贡献值0点
  • 好评度22点
  • 原创分0分
  • 专家分0分
9楼#
发布于:2007-05-10 13:49
在这个驱动代码内,偶终于看到了file_operations结构和设备关联起来了,网上看到的大部分PCI驱动结构都是一个人的,在里面没发现这个关联过程,不知道利用proc来实现PCI驱动可行不?不过在个例子里,用户程序用的也是proc,按照这个做法,似乎不用建立设备文件,即不需要mknod了,不知道偶的想法是否正确
再一次感谢
cyliu
论坛版主
论坛版主
  • 注册日期2003-06-13
  • 最后登录2014-04-11
  • 粉丝5
  • 关注0
  • 积分1238分
  • 威望2531点
  • 贡献值0点
  • 好评度577点
  • 原创分14分
  • 专家分10分
10楼#
发布于:2007-05-10 14:21
首先文件系统仅是方便用户对设备操作而提供的外部接口,不提供设备那么你想操作什么? 系统又如何获取设备的中断处理等?

其次对设备的配置一般采用ioctl,对设备的数据读取采用read/write,否则在read/write中自己区分想要让驱动工作。

最后建议对看看Linux内核自带的pci驱动,了解他们如何工作的,尽量不要去看编外的代码。原理可以,但是现实实现最好还是看kernel自己成熟的code。
走走看看开源好 Solaris vs Linux
ubuntu_amateur
驱动牛犊
驱动牛犊
  • 注册日期2006-08-04
  • 最后登录2008-12-04
  • 粉丝0
  • 关注0
  • 积分970分
  • 威望98点
  • 贡献值0点
  • 好评度97点
  • 原创分0分
  • 专家分0分
11楼#
发布于:2007-06-21 11:51
proc只是个伪文件系统,做调试用的吧?
there is a will,there is a way
mantian
驱动牛犊
驱动牛犊
  • 注册日期2005-08-09
  • 最后登录2009-11-04
  • 粉丝0
  • 关注0
  • 积分1分
  • 威望7点
  • 贡献值0点
  • 好评度5点
  • 原创分0分
  • 专家分0分
12楼#
发布于:2007-09-03 20:29
IO卡简单的数字量读写得话。都用ioctl就挺好。
:)
bnull
驱动牛犊
驱动牛犊
  • 注册日期2007-08-09
  • 最后登录2016-01-09
  • 粉丝0
  • 关注0
  • 积分30分
  • 威望4点
  • 贡献值0点
  • 好评度3点
  • 原创分0分
  • 专家分0分
13楼#
发布于:2007-09-30 10:46
1.首先想咨询一下楼主,你有没有尝试过proc文件系统,能否实现对PCI设备的读写操作,因为在很多资料里都介绍proc是调试文件。
2.如果这样不行PCI数据是怎样收发的。
谢谢!
游客

返回顶部