1. struct snd_card
1.1. snd_card是什么
snd_card可以說是整個ALSA音頻驅(qū)動最頂層的一個結(jié)構(gòu),整個聲卡的軟件邏輯結(jié)構(gòu)開始于該結(jié)構(gòu),幾乎所有與聲音相關(guān)的邏輯設(shè)備都是在snd_card的管理之下,聲卡驅(qū)動的第一個動作通常就是創(chuàng)建一個snd_card結(jié)構(gòu)體。正因為如此,本節(jié)中,我們也從 struct cnd_card開始吧。
1.2. snd_card的定義
snd_card的定義位于改頭文件中:include/sound/core.h
[c-sharp]?view plain?copy
/*?main?structure?for?soundcard?*/??
struct?snd_card?{??
int?number;?????????/*?number?of?soundcard?(index?to?
snd_cards)?*/??
char?id[16];????????????/*?id?string?of?this?card?*/??
char?driver[16];????????/*?driver?name?*/??
char?shortname[32];?????/*?short?name?of?this?soundcard?*/??
char?longname[80];??????/*?name?of?this?soundcard?*/??
char?mixername[80];?????/*?mixer?name?*/??
char?components[128];???????/*?card?components?delimited?with?
space?*/??
struct?module?*module;??????/*?top-level?module?*/??
void?*private_data;?????/*?private?data?for?soundcard?*/??
void?(*private_free)?(struct?snd_card?*card);?/*?callback?for?freeing?of?
private?data?*/??
struct?list_head?devices;???/*?devices?*/??
unsigned?int?last_numid;????/*?last?used?numeric?ID?*/??
struct?rw_semaphore?controls_rwsem;?/*?controls?list?lock?*/??
rwlock_t?ctl_files_rwlock;??/*?ctl_files?list?lock?*/??
int?controls_count;?????/*?count?of?all?controls?*/??
int?user_ctl_count;?????/*?count?of?all?user?controls?*/??
struct?list_head?controls;??/*?all?controls?for?this?card?*/??
struct?list_head?ctl_files;?/*?active?control?files?*/??
struct?snd_info_entry?*proc_root;???/*?root?for?soundcard?specific?files?*/??
struct?snd_info_entry?*proc_id;?/*?the?card?id?*/??
struct?proc_dir_entry?*proc_root_link;??/*?number?link?to?real?id?*/??
struct?list_head?files_list;????/*?all?files?associated?to?this?card?*/??
struct?snd_shutdown_f_ops?*s_f_ops;?/*?file?operations?in?the?shutdown?
state?*/??
spinlock_t?files_lock;??????/*?lock?the?files?for?this?card?*/??
int?shutdown;???????????/*?this?card?is?going?down?*/??
int?free_on_last_close;?????/*?free?in?context?of?file_release?*/??
wait_queue_head_t?shutdown_sleep;??
struct?device?*dev;?????/*?device?assigned?to?this?card?*/??
#ifndef?CONFIG_SYSFS_DEPRECATED??
struct?device?*card_dev;????/*?cardX?object?for?sysfs?*/??
#endif??
#ifdef?CONFIG_PM??
unsigned?int?power_state;???/*?power?state?*/??
struct?mutex?power_lock;????/*?power?lock?*/??
wait_queue_head_t?power_sleep;??
#endif??
#if?defined(CONFIG_SND_MIXER_OSS)?||?defined(CONFIG_SND_MIXER_OSS_MODULE)??
struct?snd_mixer_oss?*mixer_oss;??
int?mixer_oss_change_count;??
#endif??
};??
struct list_head devices???? 記錄該聲卡下所有邏輯設(shè)備的鏈表
struct list_head controls??? 記錄該聲卡下所有的控制單元的鏈表
void *private_data??????????? 聲卡的私有數(shù)據(jù),可以在創(chuàng)建聲卡時通過參數(shù)指定數(shù)據(jù)的大小
2. 聲卡的建立流程
2.1.1. 第一步,創(chuàng)建snd_card的一個實例
[c-sharp]?view plain?copy
struct?snd_card?*card;??
int?err;??
....??
err?=?snd_card_create(index,?id,?THIS_MODULE,?0,?&card);??
index?????????? 一個整數(shù)值,該聲卡的編號
id??????????????? 字符串,聲卡的標(biāo)識符
第四個參數(shù)??? 該參數(shù)決定在創(chuàng)建snd_card實例時,需要同時額外分配的私有數(shù)據(jù)的大小,該數(shù)據(jù)的指針最終會賦值給snd_card的private_data數(shù)據(jù)成員
card???????????? 返回所創(chuàng)建的snd_card實例的指針
2.1.2. 第二步,創(chuàng)建聲卡的芯片專用數(shù)據(jù)
聲卡的專用數(shù)據(jù)主要用于存放該聲卡的一些資源信息,例如中斷資源、io資源、dma資源等??梢杂袃煞N創(chuàng)建方法:
通過上一步中snd_card_create()中的第四個參數(shù),讓snd_card_create自己創(chuàng)建
[c-sharp]?view plain?copy
//?struct?mychip?用于保存專用數(shù)據(jù)??
err?=?snd_card_create(index,?id,?THIS_MODULE,??
sizeof(struct?mychip),?&card);??
//?從private_data中取出??
struct?mychip?*chip?=?card->private_data;??
自己創(chuàng)建:
[c-sharp]?view plain?copy
struct?mychip?{??
struct?snd_card?*card;??
....??
};??
struct?snd_card?*card;??
struct?mychip?*chip;??
chip?=?kzalloc(sizeof(*chip),?GFP_KERNEL);??
......??
err?=?snd_card_create(index[dev],?id[dev],?THIS_MODULE,?0,?&card);??
//?專用數(shù)據(jù)記錄snd_card實例??
chip->card?=?card;??
.....??
然后,把芯片的專有數(shù)據(jù)注冊為聲卡的一個低階設(shè)備:
[c-sharp]?view plain?copy
static?int?snd_mychip_dev_free(struct?snd_device?*device)??
{??
return?snd_mychip_free(device->device_data);??
}??
static?struct?snd_device_ops?ops?=?{??
.dev_free?=?snd_mychip_dev_free,??
};??
....??
snd_device_new(card,?SNDRV_DEV_LOWLEVEL,?chip,?&ops);??
注冊為低階設(shè)備主要是為了當(dāng)聲卡被注銷時,芯片專用數(shù)據(jù)所占用的內(nèi)存可以被自動地釋放。
2.1.3. 第三步,設(shè)置Driver的ID和名字
[c-sharp]?view plain?copy
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);??
snd_card的driver字段保存著芯片的ID字符串,user空間的alsa-lib會使用到該字符串,所以必須要保證該ID的唯一性。shortname字段更多地用于打印信息,longname字段則會出現(xiàn)在/proc/asound/cards中。
2.1.4. 第四步,創(chuàng)建聲卡的功能部件(邏輯設(shè)備),例如PCM,Mixer,MIDI等
這時候可以創(chuàng)建聲卡的各種功能部件了,還記得開頭的snd_card結(jié)構(gòu)體的devices字段嗎?每一種部件的創(chuàng)建最終會調(diào)用snd_device_new()來生成一個snd_device實例,并把該實例鏈接到snd_card的devices鏈表中。
通常,alsa-driver的已經(jīng)提供了一些常用的部件的創(chuàng)建函數(shù),而不必直接調(diào)用snd_device_new(),比如:
PCM? ----??????? snd_pcm_new()
RAWMIDI --??? snd_rawmidi_new()
CONTROL --?? snd_ctl_create()
TIMER?? --?????? snd_timer_new()
INFO??? --??????? snd_card_proc_new()
JACK??? --??????? snd_jack_new()
2.1.5. 第五步,注冊聲卡
[c-sharp]?view plain?copy
err?=?snd_card_register(card);??
if?(err?0)?{??
snd_card_free(card);??
return?err;??
}??
2.2. 一個實際的例子
我把/sound/arm/pxa2xx-ac97.c的部分代碼貼上來:
[cpp]?view plain?copy
static?int?__devinit?pxa2xx_ac97_probe(struct?platform_device?*dev)??
{??
struct?snd_card?*card;??
struct?snd_ac97_bus?*ac97_bus;??
struct?snd_ac97_template?ac97_template;??
int?ret;??
pxa2xx_audio_ops_t?*pdata?=?dev->dev.platform_data;??
if?(dev->id?>=?0)?{??
dev_err(&dev->dev,?"PXA2xx?has?only?one?AC97?port./n");??
ret?=?-ENXIO;??
goto?err_dev;??
}??
////(1)////??
ret?=?snd_card_create(SNDRV_DEFAULT_IDX1,?SNDRV_DEFAULT_STR1,??
THIS_MODULE,?0,?&card);??
if?(ret?0)??
goto?err;??
card->dev?=?&dev->dev;??
////(3)////??
strncpy(card->driver,?dev->dev.driver->name,?sizeof(card->driver));??
////(4)////??
ret?=?pxa2xx_pcm_new(card,?&pxa2xx_ac97_pcm_client,?&pxa2xx_ac97_pcm);??
if?(ret)??
goto?err;??
////(2)////??
ret?=?pxa2xx_ac97_hw_probe(dev);??
if?(ret)??
goto?err;??
////(4)////??
ret?=?snd_ac97_bus(card,?0,?&pxa2xx_ac97_ops,?NULL,?&ac97_bus);??
if?(ret)??
goto?err_remove;??
memset(&ac97_template,?0,?sizeof(ac97_template));??
ret?=?snd_ac97_mixer(ac97_bus,?&ac97_template,?&pxa2xx_ac97_ac97);??
if?(ret)??
goto?err_remove;??
////(3)////??
snprintf(card->shortname,?sizeof(card->shortname),??
"%s",?snd_ac97_get_short_name(pxa2xx_ac97_ac97));??
snprintf(card->longname,?sizeof(card->longname),??
"%s?(%s)",?dev->dev.driver->name,?card->mixername);??
if?(pdata?&&?pdata->codec_pdata[0])??
snd_ac97_dev_add_pdata(ac97_bus->codec[0],?pdata->codec_pdata[0]);??
snd_card_set_dev(card,?&dev->dev);??
////(5)////??
ret?=?snd_card_register(card);??
if?(ret?==?0)?{??
platform_set_drvdata(dev,?card);??
return?0;??
}??
err_remove:??
pxa2xx_ac97_hw_remove(dev);??
err:??
if?(card)??
snd_card_free(card);??
err_dev:??
return?ret;??
}??
static?int?__devexit?pxa2xx_ac97_remove(struct?platform_device?*dev)??
{??
struct?snd_card?*card?=?platform_get_drvdata(dev);??
if?(card)?{??
snd_card_free(card);??
platform_set_drvdata(dev,?NULL);??
pxa2xx_ac97_hw_remove(dev);??
}??
return?0;??
}??
static?struct?platform_driver?pxa2xx_ac97_driver?=?{??
.probe??????=?pxa2xx_ac97_probe,??
.remove?????=?__devexit_p(pxa2xx_ac97_remove),??
.driver?????=?{??
.name???=?"pxa2xx-ac97",??
.owner??=?THIS_MODULE,??
#ifdef?CONFIG_PM??
.pm?=?&pxa2xx_ac97_pm_ops,??
#endif??
},??
};??
static?int?__init?pxa2xx_ac97_init(void)??
{??
return?platform_driver_register(&pxa2xx_ac97_driver);??
}??
static?void?__exit?pxa2xx_ac97_exit(void)??
{??
platform_driver_unregister(&pxa2xx_ac97_driver);??
}??
module_init(pxa2xx_ac97_init);??
module_exit(pxa2xx_ac97_exit);??
MODULE_AUTHOR("Nicolas?Pitre");??
MODULE_DESCRIPTION("AC97?driver?for?the?Intel?PXA2xx?chip");??
驅(qū)動程序通常由probe回調(diào)函數(shù)開始,對一下2.1中的步驟,是否有相似之處?
經(jīng)過以上的創(chuàng)建步驟之后,聲卡的邏輯結(jié)構(gòu)如下圖所示:
圖 2.2.1? 聲卡的軟件邏輯結(jié)構(gòu)
下面的章節(jié)里我們分別討論一下snd_card_create()和snd_card_register()這兩個函數(shù)。
3. snd_card_create()
snd_card_create()在/sound/core/init.c中定義。
[cpp]?view plain?copy
/**?
*??snd_card_create?-?create?and?initialize?a?soundcard?structure?
*??@idx:?card?index?(address)?[0?...?(SNDRV_CARDS-1)]?
*??@xid:?card?identification?(ASCII?string)?
*??@module:?top?level?module?for?locking?
*??@extra_size:?allocate?this?extra?size?after?the?main?soundcard?structure?
*??@card_ret:?the?pointer?to?store?the?created?card?instance?
*?
*??Creates?and?initializes?a?soundcard?structure.?
*?
*??The?function?allocates?snd_card?instance?via?kzalloc?with?the?given?
*??space?for?the?driver?to?use?freely.??The?allocated?struct?is?stored?
*??in?the?given?card_ret?pointer.?
*?
*??Returns?zero?if?successful?or?a?negative?error?code.?
*/??
int?snd_card_create(int?idx,?const?char?*xid,??
struct?module?*module,?int?extra_size,??
struct?snd_card?**card_ret)??
首先,根據(jù)extra_size參數(shù)的大小分配內(nèi)存,該內(nèi)存區(qū)可以作為芯片的專有數(shù)據(jù)使用(見前面的介紹):
[c-sharp]?view plain?copy
card?=?kzalloc(sizeof(*card)?+?extra_size,?GFP_KERNEL);??
if?(!card)??
return?-ENOMEM;??
拷貝聲卡的ID字符串:
[c-sharp]?view plain?copy
if?(xid)??
strlcpy(card->id,?xid,?sizeof(card->id));??
如果傳入的聲卡編號為-1,自動分配一個索引編號:
[c-sharp]?view plain?copy
if?(idx?0)?{??
for?(idx2?=?0;?idx2?
/*?idx?==?-1?==?0xffff?means:?take?any?free?slot?*/??
if?(~snd_cards_lock?&?idx?&?1<
if?(module_slot_match(module,?idx2))?{??
idx?=?idx2;??
break;??
}??
}??
}??
if?(idx?0)?{??
for?(idx2?=?0;?idx2?
/*?idx?==?-1?==?0xffff?means:?take?any?free?slot?*/??
if?(~snd_cards_lock?&?idx?&?1<
if?(!slots[idx2]?||?!*slots[idx2])?{??
idx?=?idx2;??
break;??
}??
}??
}??
初始化snd_card結(jié)構(gòu)中必要的字段:
[c-sharp]?view plain?copy
card->number?=?idx;??
card->module?=?module;??
INIT_LIST_HEAD(&card->devices);??
init_rwsem(&card->controls_rwsem);??
rwlock_init(&card->ctl_files_rwlock);??
INIT_LIST_HEAD(&card->controls);??
INIT_LIST_HEAD(&card->ctl_files);??
spin_lock_init(&card->files_lock);??
INIT_LIST_HEAD(&card->files_list);??
init_waitqueue_head(&card->shutdown_sleep);??
#ifdef?CONFIG_PM??
mutex_init(&card->power_lock);??
init_waitqueue_head(&card->power_sleep);??
#endif??
建立邏輯設(shè)備:Control
[c-sharp]?view plain?copy
/*?the?control?interface?cannot?be?accessed?from?the?user?space?until?*/??
/*?snd_cards_bitmask?and?snd_cards?are?set?with?snd_card_register?*/??
err?=?snd_ctl_create(card);??
建立proc文件中的info節(jié)點:通常就是/proc/asound/card0
[c-sharp]?view plain?copy
err?=?snd_info_card_create(card);??
把第一步分配的內(nèi)存指針放入private_data字段中:
[c-sharp]?view plain?copy
if?(extra_size?>?0)??
card->private_data?=?(char?*)card?+?sizeof(struct?snd_card);??
4. snd_card_register()
snd_card_create()在/sound/core/init.c中定義。
[c-sharp]?view plain?copy
/**?
*??snd_card_register?-?register?the?soundcard?
*??@card:?soundcard?structure?
*?
*??This?function?registers?all?the?devices?assigned?to?the?soundcard.?
*??Until?calling?this,?the?ALSA?control?interface?is?blocked?from?the?
*??external?accesses.??Thus,?you?should?call?this?function?at?the?end?
*??of?the?initialization?of?the?card.?
*?
*??Returns?zero?otherwise?a?negative?error?code?if?the?registrain?failed.?
*/??
int?snd_card_register(struct?snd_card?*card)??
首先,創(chuàng)建sysfs下的設(shè)備:
[c-sharp]?view plain?copy
if?(!card->card_dev)?{??
card->card_dev?=?device_create(sound_class,?card->dev,??
MKDEV(0,?0),?card,??
"card%i",?card->number);??
if?(IS_ERR(card->card_dev))??
card->card_dev?=?NULL;??
}??
其中,sound_class是在/sound/sound_core.c中創(chuàng)建的:
[c-sharp]?view plain?copy
static?char?*sound_devnode(struct?device?*dev,?mode_t?*mode)??
{??
if?(MAJOR(dev->devt)?==?SOUND_MAJOR)??
return?NULL;??
return?kasprintf(GFP_KERNEL,?"snd/%s",?dev_name(dev));??
}??
static?int?__init?init_soundcore(void)??
{??
int?rc;??
rc?=?init_oss_soundcore();??
if?(rc)??
return?rc;??
sound_class?=?class_create(THIS_MODULE,?"sound");??
if?(IS_ERR(sound_class))?{??
cleanup_oss_soundcore();??
return?PTR_ERR(sound_class);??
}??
sound_class->devnode?=?sound_devnode;??
return?0;??
}??
由此可見,聲卡的class將會出現(xiàn)在文件系統(tǒng)的/sys/class/sound/下面,并且,sound_devnode()也決定了相應(yīng)的設(shè)備節(jié)點也將會出現(xiàn)在/dev/snd/下面。
接下來的步驟,通過snd_device_register_all()注冊所有掛在該聲卡下的邏輯設(shè)備,snd_device_register_all()實際上是通過snd_card的devices鏈表,遍歷所有的snd_device,并且調(diào)用snd_device的ops->dev_register()來實現(xiàn)各自設(shè)備的注冊的。
[c-sharp]?view plain?copy
if?((err?=?snd_device_register_all(card))?0)??
return?err;??
最后就是建立一些相應(yīng)的proc和sysfs下的文件或?qū)傩怨?jié)點,代碼就不貼了。
至此,整個聲卡完成了建立過程。
?
評論
查看更多