免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 926 | 回复: 0
打印 上一主题 下一主题

writing-an-alsa-driver(编写一个ALSA驱动)翻译稿 第四章 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2008-06-20 13:56 |只看该作者 |倒序浏览

       
       
翻译:creator

sz111@126.com

第四章
PCI[color="#000000"]资源管理
[color="#000000"]代码示例
   
本节我们会完成一个chip-specific[color="#000000"]的构造函数,析构函数和PCI
entries[color="#000000"]。先来看代码。
   
1.Example4-1.PCI[color="#000000"]资源管理示例
    struct
mychip{
         struct
snd_card *card;
         struct
pci_dev *pci;
         unsigned
long port;
         int
irq;
[color="#000000"]    };
    static
int snd_mychip_free(struct mychip *chip)
[color="#000000"]    {
        /*disable
hardware here if any*/
      
....//[color="#000000"]这篇文档没有实现
       /*release
the irq*/
       if
(chip->irq >= 0)
         
free_irq(chip->irq,chip);
      
/*[color="#000000"]释放io[color="#000000"]和memory*/
      
pci_release_regions(chip->pci);
       /*disable
the PCI entry*/
      
pci_disable_device(chip->pci);
       /*release
the data*/
      
kfree(chip);
       return
0;
[color="#000000"]    }
[color="#000000"]   
   
/*chip-specific
constructor*/
    static
int __devinit snd_mychip_create(struct snd_card *card,
                 
                         struct
pci_dev *pci,
                 
                         struct
mychip **rchip)
[color="#000000"]    {
         struct
mychip *chip;
         int
err;
         static
struct snd_device_ops ops ={
            
.dev_free =
snd_mychip_dev_free,
[color="#000000"]         };
         *rchip
= NULL;
        
/*initialize
the PCI entry*/
         if
((err = pci_enable_device(pci))
            
return err;
         /*check
PCI availability (28bit DMA)*/
         if
(pci_set_dma_mask(pci, DMA_28BIT_MASK)
            
pci_set_consistent_dma_mask(pci,DMA_28BIT_MASK)
            
printk(KERN_ERR
“Error to set 28bit mask DMA\n”);
            
pci_disable_device(pci);
            
return
-ENXIO;
[color="#000000"]         }
   
   
         chip
= kzalloc(sizeof(*chip), GFP_KERNEL);
         if
(chip == NULL){
            
pci_disable_device(pci);
            
return
-ENOMEM;
[color="#000000"]         }
        
/*initialize
the stuff*/
        
chip->card
= card;
        
chip->pci
= pci;
        
chip->irq
= -1;
         /*(1)PCI
[color="#000000"]资源分配*/
         if
((err = pci_request_regions(pci, “My Chip”))
            
kfree(chip);
            
pci_disable_device(pci);
            
return err;
[color="#000000"]         }
        
chip->port
= pci_resource_start(pci,0);
         if
(request_irq(pci->irq, snd_mychip_interrupt,
                 
       IRQF_SHARED,
“My Chip”,chip){
            
printk(KERN_ERR
“Cannot grab irq %d\n”,pci->irq);
            
snd_mychip_free(chip);
            
return
-EBUSY;
[color="#000000"]         }
        
chip->irq
= pci->irq;
        
/*(2)chip
hardware[color="#000000"]的初始化*/
        
....//[color="#000000"]本文未实现
   
   
         if
((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL,
                 
                 chip,
&ops))
            
snd_mychip_free(chip);
            
return err;
[color="#000000"]         }
        
snd_card_set_dev(card,&pci->dev);
         *rchip
= chip;
         return
0;
[color="#000000"]     }
     /*PCI
Ids*/
     static
struct pci_device_id snd_mychip_ids[] = {
        
{PCI_VENDOR_ID_FOO,
PCI_DEVICE_ID_BAR,
         
PCI_ANY_ID,
PCI_ANY_ID,0,0,0,},
[color="#000000"]         ....
[color="#000000"]         {0,}
[color="#000000"]     };
   
MODULE_DEVICE_TABLE(pci,
snd_mychip_ids);
[color="#000000"]   
   
/*pci_driver[color="#000000"]定义*/
     static
struct pci_drvier driver={
         .name
= “My Own Chip”,
        
.id_table =
snd_mychip_ids,
         .probe
= snd_mychip_probe,
         .remove
= __devexit_p(snd_mychip_remove),
[color="#000000"]     };
   
/*module[color="#000000"]的初始化*/
     static
int __init alsa_card_mychip_init(void)
[color="#000000"]     {
         return
pci_register_driver(&driver);
[color="#000000"]     }
     /*clean
up the module*/
     static
void __exit alsa_card_mychip_exit(void)
[color="#000000"]     {
         
pci_unregister_drvier(&drvier);
[color="#000000"]      }
     
module_init(alsa_card_mychip_init);
     
module_exit(alsa_card_mychip_eixt);
   

     
EXPORT_NO_SYMBOLS
/*[color="#000000"]为了和老版本的内核兼容*/
[color="#000000"]一些必须做的事情
   
一般在probe()[color="#000000"]函数中分配PCI[color="#000000"]资源,通常采用一个xxx_create()函数来完成上述功能。
   在PCI[color="#000000"]设备驱动中,在分配资源之前先要调用pci_enable_device().[color="#000000"]同样,你也要设定合适的PCI
DMA mask[color="#000000"]限制i/o[color="#000000"]接口的范围。在一些情况下,你也需要设定pci_set_master().
资源分配
    利用标准的内核函数来分配I/O和中断。不像ALSA
ver0.5.x那样没有什么帮助。同时那些资源必须在析构函数中被释放(如下)。在ALSA
0.9.x,你不需要像0.5.x那样还要为PCI分配DMA。
   
现在假定PCI设备拥有8直接的I/O口和中断,mychip的结构体可以如下所示:
    struct
mychip{
        struct
snd_card *card;
        unsigned
long port;
        int
irq;
    }
   
   
对于一个I/O端口(或者也有内存区域),你需要得到可以进行标准的资源管理的资源的指针。对于中断,你必须保存它的中断号。但是你必须实际分配之前先把初始化中断号为
-1,因为中断号为0也是被允许的。端口地址和它的资源指针可以通过kzalloc()来分配初始化为null,所以你不用非要重新设定他们。
    I/O端口的分配可以采用如下方式:
    if
((err = pci_request_region(pci, “My Chip”))
        kfree(chip);
        pci_disable_device(pci);
        return
err;
    }
    chip->port
= pci_resource_start(pci, 0);
   
   
它将会保留给定PCI设备的8字节的I/O端口区域。返回值chip->res_port在request_region函数中通过kmalloc分配。这个分配的指针必须通过kfree()函数进行释放,但是这个部分有些问题,具体将会在下面详细分析。
    分配一个中断源如下所示:
    if
(request_irq(pci->irq, snd_mychip_interrupt,
                    IRQF_DISABLED
| IRQF_SHARED, “My Chip”,chip)){
        printk(KERN_ERR
“cannot grab irq %d\n”, pci->irq);
        snd_mychip_free(chip);
        return
-EBUSY;
    }
    chip->irq
= pci->irq;
   
   
snd_mychip_interrupt()就是下面定义的中断处理函数。注意request_irq()成功的时候,返回值要保持在chip->irq里面。
    在PCI
bus上,中断是共享的。因此,申请中断的函数request_irq要加入IRQF_SHARED标志位。
   
request_irq的最后一个参数是被传递给中断处理函数的数据指针。通常来说,chip-specific记录会用到它,你也可以按你喜欢的方式用它。
   
我不想在这时候详细解释中断向量函数,但是至少这里出现的会解释一下。中断向量如下所示:
    static
irqreturn_t snd_mychip_interrup(int irq, void *dev_id)
    {
        struct
mychip *chip = dev_id;
        return
IRQ_HANDLED;
    }
   
   
现在,让我们为上述的资源写一个相应的析构函数。析构函数是非常简单的:关闭硬件(假如它被激活)同时释放它的资源。到目前为止,我们没有硬件,所以关闭硬件的部分就不写了。
   
为了释放资源,“check-and-release”(检查然后释放)的方式是比较安全的。对于中断,采用如下的方式:
    if
(chip->irq >= 0)
         free_irq(chip->irq,chip);

   
因为irq号是从0开始的,所以你必须初始化chip->irq为一个负数(例如-1),所以你可以按上面的方式检查irq的有效性。
   
通过pci_request_region()或pci_request_regions()申请I/O端口和内存空间,相应的,通过pci_release_region()或pci_release_regions来释放。
    pci_release_regions(chip->pci);
   
通常可以通过request_region()或request_mem_region()来申请,也可以通过release_region()来释放。假定你把request_region返回的resource
pointer保存在chip->res_port,释放的程序如下所示:
   
release_and_free_resource(chip->res_port);
   
   
在所有都结束的时候不要忘记了调用pci_disable_device().最后释放chip-specific记录。
    kfree(chip);
    再提醒一下,不能在析构函数前面放__devexit。
   
我们在上面没有实现关闭硬件的功能部分。假如你需要做这些,请注意析构函数可能会在chip的初始化完成之前被调用。最好设定一个标志位确定是否硬件已经初始化,来决定是否略过这部分。
   
如果chip-data放置在在含有SNDRV_DEV_LOWLEVEL标志的snd_device_new()函数申请的设备中,它的析构函数将会最后被调用。那是因为,它要确认像PCM和一些控制组件已经被释放。你不必显式调用停止PCM的函数,只要在low-level中停止这些硬件。
   
mamory-mapped的区域的管理和i/o端口管理一样。需要如下3个结构变量:
    struct
mychip{
        ....
        unsigned
long iobase_phys;
        void
__iomem *iobase_virt;
    };
    通过如下方式来申请:
    if
((err = pci_request_regions(pci, “My Chip”))
        kfree(chip);
        return
err;
    }
    chip->iobase_phys
= pci_resource_start(pci, 0);
    chip->iobase_virt
= ioremap_nocache(chip->iobase_phys,
                                       
pci_resource_len(pci,
0));
    对应的析构函数如下:
    static
int snd_mychip_free(struct mychip *chip)
    {
          ....
          if
(chip->iobase_virt)
              
iounmap(chip->iobase_virt);
          ....
         
pci_release_regions(chip->pci);
          ....
    }
注册设备结构体
   
在一些地方,典型的是在调用snd_device_new(),假如你想通过udev或者ALSA提供的一些老版本内核的兼容的宏来控制设备,你需要注册chip的设备结构体。很简单如下所示:
    snd_card_set_dev(card,&pci->dev);
   
所以它保存了card的PCI设备指针。它将会在后续设备注册的时候被ALSA内核功能调用。
   
假如是非PCI设备,就需要传递一个合适的设备结构指针。(假如是不可以热拔插的ISA设备,你就不需要这么做了。)
PCI
Entries
   
到目前为止已经做得非常好了,下面让我们完成PCI的剩余的一些工作。首先,我们需要一个这个芯片组的pci_device_id表。它是一个含有PCI制造商和设备ID的表,还有一些mask。
    例如:
      static
struct pci_device_id snd_mychip_ids[] = {
        
{PCI_VENDOR_ID_FOO,
PCI_DEVICE_ID_BAR,
         
PCI_ANY_ID,
PCI_ANY_ID,0,0,0,},
[color="#000000"]         ....
[color="#000000"]         {0,}
[color="#000000"]      };
     
MODULE_DEVICE_TABLE(pci,snd_mychip_ids);
     
pci_device_id[color="#000000"]结构体的第一个和第二个结构变量是制造商和设备的ID[color="#000000"]。假如你没有关于设备的特别选择,你可以采用上述的。pci_device_id[color="#000000"]结构体的最后一个结构变量是一个私有变量。你可以放一些可以区别其他的值,如:可以区分每个设备ID[color="#000000"]的不同操作类型。这些例子出现在intel[color="#000000"]的驱动里面。最后一个table[color="#000000"]元素是代表结束符。必须把所有成员设定为0.
   
然后,我们来准备pci_driver[color="#000000"]记录:
    static
struct pci_drvier driver = {
        .name
= “My Own Chip”,
        .id_table
= snd_mychip_ids,
        .probe
= snd_mychip_probe,
        .remove
= __devexit_p(snd_mychip_remove),
[color="#000000"]    };
[color="#000000"]   
   
probe[color="#000000"]和remove[color="#000000"]函数在以前的章节已经介绍过。remove[color="#000000"]应该用一个__devexit_p()[color="#000000"]宏来定义。所以,它不是为那些固定的或不支持热拔插的设备定义的。name[color="#000000"]结构变量是标识设备的名字。注意你不能用“/”[color="#000000"]字符。
   
最后,module[color="#000000"]入口如下:
    static
int __init alsa_card_mychip_init(void)
[color="#000000"]    {
        return
pci_register_driver(&driver);
[color="#000000"]    }
    static
void __exit alsa_card_mychip_exit(void)
[color="#000000"]    {
      
pci_unregister_driver(&driver);
[color="#000000"]    }
   
module_init(alsa_card_mychip_init);
   
module_exit(alsa_card_mychip_exit);
   
注意module[color="#000000"]入口函数被标识为__init[color="#000000"]和__exit[color="#000000"]前缀,而不是__devinit[color="#000000"]或者__devexit.
   
哦,忘记了一件事,假如你没有export[color="#000000"]标号,如果是2.2[color="#000000"]或2.4[color="#000000"]内核你必须显式声明r[color="#000000"]如下:(当然,2.6[color="#000000"]内核已经不需要了。)
   
EXPORT_NO_SYMBOLS;
[color="#000000"]    就这些了。
               
               
               

本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u1/49088/showart_1006028.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP