1. struct snd_card
1.1. snd_card是什么
snd_card可以說是整個ALSA音頻驅動最頂層的一個結構,整個聲卡的軟件邏輯結構開始于該結構,幾乎所有與聲音相關的邏輯設備都是在snd_card的管理之下,聲卡驅動的第一個動作通常就是創建一個snd_card結構體。正因為如此,本節中,我們也從 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???? 記錄該聲卡下所有邏輯設備的鏈表
struct list_head controls??? 記錄該聲卡下所有的控制單元的鏈表
void *private_data??????????? 聲卡的私有數據,可以在創建聲卡時通過參數指定數據的大小
2. 聲卡的建立流程
2.1.1. 第一步,創建snd_card的一個實例
[c-sharp]?view plain?copy
struct?snd_card?*card;??
int?err;??
....??
err?=?snd_card_create(index,?id,?THIS_MODULE,?0,?&card);??
index?????????? 一個整數值,該聲卡的編號
id??????????????? 字符串,聲卡的標識符
第四個參數??? 該參數決定在創建snd_card實例時,需要同時額外分配的私有數據的大小,該數據的指針最終會賦值給snd_card的private_data數據成員
card???????????? 返回所創建的snd_card實例的指針
2.1.2. 第二步,創建聲卡的芯片專用數據
聲卡的專用數據主要用于存放該聲卡的一些資源信息,例如中斷資源、io資源、dma資源等。可以有兩種創建方法:
通過上一步中snd_card_create()中的第四個參數,讓snd_card_create自己創建
[c-sharp]?view plain?copy
//?struct?mychip?用于保存專用數據??
err?=?snd_card_create(index,?id,?THIS_MODULE,??
sizeof(struct?mychip),?&card);??
//?從private_data中取出??
struct?mychip?*chip?=?card->private_data;??
自己創建:
[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);??
//?專用數據記錄snd_card實例??
chip->card?=?card;??
.....??
然后,把芯片的專有數據注冊為聲卡的一個低階設備:
[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);??
注冊為低階設備主要是為了當聲卡被注銷時,芯片專用數據所占用的內存可以被自動地釋放。
2.1.3. 第三步,設置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字段則會出現在/proc/asound/cards中。
2.1.4. 第四步,創建聲卡的功能部件(邏輯設備),例如PCM,Mixer,MIDI等
這時候可以創建聲卡的各種功能部件了,還記得開頭的snd_card結構體的devices字段嗎?每一種部件的創建最終會調用snd_device_new()來生成一個snd_device實例,并把該實例鏈接到snd_card的devices鏈表中。
通常,alsa-driver的已經提供了一些常用的部件的創建函數,而不必直接調用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");??
驅動程序通常由probe回調函數開始,對一下2.1中的步驟,是否有相似之處?
經過以上的創建步驟之后,聲卡的邏輯結構如下圖所示:
圖 2.2.1? 聲卡的軟件邏輯結構
下面的章節里我們分別討論一下snd_card_create()和snd_card_register()這兩個函數。
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)??
首先,根據extra_size參數的大小分配內存,該內存區可以作為芯片的專有數據使用(見前面的介紹):
[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結構中必要的字段:
[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??
建立邏輯設備: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節點:通常就是/proc/asound/card0
[c-sharp]?view plain?copy
err?=?snd_info_card_create(card);??
把第一步分配的內存指針放入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)??
首先,創建sysfs下的設備:
[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中創建的:
[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將會出現在文件系統的/sys/class/sound/下面,并且,sound_devnode()也決定了相應的設備節點也將會出現在/dev/snd/下面。
接下來的步驟,通過snd_device_register_all()注冊所有掛在該聲卡下的邏輯設備,snd_device_register_all()實際上是通過snd_card的devices鏈表,遍歷所有的snd_device,并且調用snd_device的ops->dev_register()來實現各自設備的注冊的。
[c-sharp]?view plain?copy
if?((err?=?snd_device_register_all(card))?0)??
return?err;??
最后就是建立一些相應的proc和sysfs下的文件或屬性節點,代碼就不貼了。
至此,整個聲卡完成了建立過程。
?
評論
查看更多