Writing an alsa driver译文-第二章 PCI驱动的基本流程

第二章 2. PCI驱动的基本流程

大纲

    PCI声卡驱动的最简流程如下:

    定义PCI ID表(请看PCI条目部分,此部分在第四章:《PCI资源管理》中)。

    创建probe()回调。

    创建remove()回调。

    创建一个包含上面三个指针的pci_driver结构体。

    创建一个init()函数,只调用pci_register_driver()来注册pci_driver表来定义上面几项。

    创建一个exit()函数来调用pci_unregister_driver()函数。

完整的代码例子

    代码实例如下。目前有一部分是空着的,在后续的章节中会添加上。snd_mychip_probe()函数中的注释在接下来的章节中会详细解释。

    例子:2.1. PCI驱动的基本流 - 示例


/* constructor -- see "Constructor" sub-section */
static int __devinit snd_mychip_probe(struct pci_dev * pci, const struct pci_device_is * pci_id)
{
     static int dev;
     static snd_card * card;
     struct muychip * chip;
     int err;
     /* (1) */
     if (dev >= SNDRV_CARDS)
         return -ENODEV;
     if (!enable[dev]) {
         dev++;
         return -ENOENT;
     }
     /* (2) */
     err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
     if (err < 0)
         return err;
     /* (3) */
     err = snd_mychip_create(card, pci, &chip);
     if (err < 0) {
         snd_card_free(card);
         return err;
     }
     /* (4) */
     strcpy(card->driver, "My Chip");
     strcpy(card->shortname, "My Own Chip 123");
     sprintf(card->longname, "%s at 0x%lx irq %i",
     card->shortname, chip->ioport, chip->irq);
     /* (5) */
     .... /* implemented later */

     /* (6) */
     err = snd_card_register(card);
     if (err < 0) {
         snd_card_free(card);
         return err;
     }
     /* (7) */
     pci_set_drvdata(pci, card);
     dev++;
     return 0;
}
/* destructor -- see the "Destructor" sub-section */
static void __devexit snd_mychip_remove(struct pci_dev *pci)
{
     snd_card_free(pci_get_drvdata(pci));
     pci_set_drvdata(pci, NULL);
}


构造函数

    真正的PCI驱动构造函数是probe回调。probe回调函数和其他在probe回调函数中被调用的构造函数组件应该使用__devinit的前缀。你不能使用__init前缀,因为任何的PCI设备都可能是热插拔的设备。

    在probe回调函数中,下面这些组合经常被用到。

1)检查并且增加设备索引


static int dev;
    ....
if (dev >= SNDRV_CARDS)
    return -ENODEV;
if (!enable[dev]) {
    dev++;
    return -ENOENT;
}

    enabel[dev]是模块选项。

    每次probe回调函数被调用时,都会检查设备的可用性。如果不可用,只增加设备索引并且返回。dev在稍后会自加(见step7)。

2)创建一个卡实例



struct snd_card * card;
int err;
    ....
err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);

    细节方面见章节:《卡和组件的管理》

3)创建一个主组件

    在这个部分中,PCI资源会被分配。


struct mychip * chip;
    ....
err = snd_mychip_create(card, pci, &chip);
if (err < 0) {
    snd_card_free(card);
    return err;
}

    细节方面会在章节:《PCI资源管理》中会有描写。

4)设置驱动ID和名称字符串


strcpy(card->driver, "My Chip");
strcpy(card->shortname, "My Own Chip 123");
sprintf(card->longname, "%s at 0x%lx irq %i",
                 card->shortname, chip->ioport, chip->irq);

    代码中的"card->driver"部分保存着最简的芯片ID字符串。这个是供alsa-lib的配置使用的,故要保持它简单但唯一。即使相同的驱动也可以有不同的驱动ID,以区别每个不同类型芯片的功能。

    "card->shortname"部分用来显示更详细的名称。"card->longname"部分保存着/proc/asound/cards节点中显示的信息。

5)创建其他组件,例如mixer,MIDI等

    这里你会定义基本的组件,像PCM,mixer(e.g.AC97),MIDI(e.g.MPU-401),和其他的接口。同样,如果你想要一个proc文件(见第12章:Proc接口),也在这里定义。

6)注册一个卡实例


err = snd_card_register(card);
if (err < 0) {
    snd_card_free(card);
    return err;
}

    这个也会在:《卡和组件管理》章节中解释。

7)设置PCI驱动数据并且返回零

    
pci_set_drvdata(pci, card);
dev++;
return 0;

    上面的代码中,卡的记录被保存了。这个指针也会在remove回调函数和电源管理回调函数中使用。

析构函数

    析构函数,remove回调,单纯移除卡的实例。然后ALSA中间层会自动释放所有的附加组件;

    它会定义如下:


static void __devexit snd_mychip_remove(struct pci_dev *pci)
{
    snd_card_free(pci_get_drvdata(pci));
    pci_set_drvdata(pci, NULL);
}

    上面的代码假定了卡的指针被设置到了PCI驱动数据中。

头文件

    对于上面例子,至少需要如下的头文件。


#include 
   
   
    
    
#include 
    
    
     
     
#include 
     
     
      
      
#include 
      
      
       
       
#include 
       
       
      
      
     
     
    
    
   
   


    最后的那项只有在模块选项被定义在了源文件中时是需要的。如果代码被分散保存到几个文件夹下,那么没有模块选项的文件夹就用不到他们。

    除了这些头文件,你会需要<linux/interrupt.h>用来中断处理,和<asm/io.h>用于I/O访问。如果你使用mdelay()或udelay()函数,你也会需要导入<linux/delay.h>。

    ALSA接口,像PCM和控制APIs是在其他的<sound/xxx.h>头文件中定义的。它们必须要被放在<sound/core.h>后面导入。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值