- 论坛徽章:
- 0
|
翻译: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 |
|