1. 關鍵代碼及註釋:
1. intel聲卡初始化流程:
/sound/pci/hda/hda_intel.c
azx_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) {
snd_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE, 0, &card); {
card->dev = parent;
card->number = idx;
card->module = module;
INIT_LIST_HEAD(&card->devices);
#define INIT_LIST_HEAD(ptr) do { \
(ptr)->next = (ptr); (ptr)->prev = (ptr); \
} while (0)
init_rwsem(&card->controls_rwsem);
rwlock_init(&card->ctl_files_rwlock);
mutex_init(&card->user_ctl_lock);
INIT_LIST_HEAD(&card->controls);
INIT_LIST_HEAD(&card->ctl_files);
spin_lock_init(&card->files_lock);
INIT_LIST_HEAD(&card->files_list);
device_initialize(&card->card_dev);
err = kobject_set_name(&card->card_dev.kobj, "card%d", idx);
err = snd_ctl_create(card);{
snd_device_initialize(&card->ctl_dev, card);
dev_set_name(&card->ctl_dev, "controlC%d", card->number);
err = snd_device_new(card, SNDRV_DEV_CONTROL, card, &ops);
}
err = snd_info_card_create(card);{
sprintf(str, "card%i", card->number);
entry = create_subdir(card->module, str); {
entry = snd_info_create_module_entry(mod, name, NULL); {
struct snd_info_entry *entry = snd_info_create_entry(name, parent);
}
snd_info_register(entry){
if (S_ISDIR(entry->mode)) {
p = proc_mkdir_mode(entry->name, entry->mode, root);
if (!p) {
mutex_unlock(&info_mutex);
return -ENOMEM;
}
} else {
const struct file_operations *ops;
if (entry->content == SNDRV_INFO_CONTENT_DATA)
ops = &snd_info_entry_operations;
else
ops = &snd_info_text_entry_ops;
p = proc_create_data(entry->name, entry->mode, root,
ops, entry);
if (!p) {
mutex_unlock(&info_mutex);
return -ENOMEM;
}
proc_set_size(p, entry->size);
}
}
}
}
}
azx_create(card, pci, dev, pci_id->driver_data, &chip){
INIT_LIST_HEAD(&chip->pcm_list);
INIT_WORK(&hda->irq_pending_work, azx_irq_pending_work);
INIT_LIST_HEAD(&hda->list);
init_vga_switcheroo(chip);
init_completion(&hda->probe_wait);
azx_bus_init(chip, model[dev]);{
snd_hdac_bus_init(&bus->core, chip->card->dev, &bus_core_ops, io_ops); {
bus->io_ops = io_ops;
INIT_LIST_HEAD(&bus->stream_list);
INIT_LIST_HEAD(&bus->codec_list);
INIT_WORK(&bus->unsol_work, process_unsol_events);
spin_lock_init(&bus->reg_lock);
mutex_init(&bus->cmd_mutex);
}
}
snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);{
struct snd_device *pdev = list_entry(p, struct snd_device, list);
}
INIT_WORK(&hda->probe_work, azx_probe_work){
azx_probe_continue(&hda->chip){
azx_first_init(chip){
err = pci_request_regions(chip->pci, "LS HD audio");
bus->addr = pci_resource_start(chip->pci, 0);
bus->remap_addr = pci_ioremap_bar(chip->pci, 0);
azx_acquire_irq(chip, 0) {
request_irq(chip->pci->irq, azx_interrupt, chip->msi ? 0 : IRQF_SHARED, KBUILD_MODNAME, chip)
}
gcap = azx_readw(chip, GCAP);
dev_dbg(card->dev, "chipset global capabilities = 0x%x\n", gcap);
if ((gcap & AZX_GCAP_64OK) && !dma_set_mask(chip->card->dev, DMA_BIT_MASK(64)))
dma_set_coherent_mask(chip->card->dev, DMA_BIT_MASK(64));
else {
dma_set_mask(chip->card->dev, DMA_BIT_MASK(32));
dma_set_coherent_mask(chip->card->dev, DMA_BIT_MASK(32));
}
chip->capture_streams = (gcap >> 8) & 0x0f;
chip->playback_streams = (gcap >> 12) & 0x0f;
if (!chip->playback_streams && !chip->capture_streams) {
chip->playback_streams = ICH6_NUM_PLAYBACK;
chip->capture_streams = ICH6_NUM_CAPTURE;
}
err = azx_init_streams(chip);{
dir = stream_direction(chip, i);
snd_hdac_stream_init(azx_bus(chip), azx_stream(azx_dev), i, dir, tag);{
azx_dev->bus = bus;
azx_dev->sd_addr = bus->remap_addr + (0x20 * idx + 0x80);
azx_dev->sd_int_sta_mask = 1 << idx;
azx_dev->index = idx;
azx_dev->direction = direction;
azx_dev->stream_tag = tag;
snd_hdac_dsp_lock_init(azx_dev);
list_add_tail(&azx_dev->list, &bus->stream_list);
}
}
azx_alloc_stream_pages(chip);{
snd_hdac_bus_alloc_stream_pages(azx_bus(chip)){
err = bus->io_ops->dma_alloc_pages(bus, SNDRV_DMA_TYPE_DEV, BDL_SIZE, &s->bdl);
err = bus->io_ops->dma_alloc_pages(bus, SNDRV_DMA_TYPE_DEV, num_streams * 8, &bus->posbuf);
list_for_each_entry(s, &bus->stream_list, list)
s->posbuf = (__le32 *)(bus->posbuf.area + s->index * 8);
err = bus->io_ops->dma_alloc_pages(bus, SNDRV_DMA_TYPE_DEV, PAGE_SIZE, &bus->rb);
}
}
azx_init_chip(chip, (probe_only[dev] & 2) == 0); {
snd_hdac_bus_init_chip(azx_bus(chip), full_reset){
if (bus->chip_init)
return false;
azx_reset(bus, full_reset);
azx_int_clear(bus);
azx_int_enable(bus);
snd_hdac_bus_init_cmd_io(bus);{
spin_lock_irq(&bus->reg_lock);
snd_hdac_chip_writeb(bus, RIRBCTL, 0);
snd_hdac_chip_writeb(bus, CORBCTL, 0);
hdac_wait_for_cmd_dmas(bus);
snd_hdac_chip_updatel(bus, GCTL, AZX_GCTL_UNSOL, 0);
spin_unlock_irq(&bus->reg_lock);
}
if (bus->use_posbuf && bus->posbuf.addr) {
snd_hdac_chip_writel(bus, DPLBASE, (u32)bus->posbuf.addr);
snd_hdac_chip_writel(bus, DPUBASE, upper_32_bits(bus->posbuf.addr));
}
bus->chip_init = true;
}
azx_writew(chip, RINTCNT, 0xc0);
}
snd_hdac_i915_set_bclk(bus);
strcpy(card->driver, "HDA-Intel");
strlcpy(card->shortname, driver_short_names[chip->driver_type], sizeof(card->shortname));
snprintf(card->longname, sizeof(card->longname), "%s at 0x%lx irq %i", card->shortname, bus->addr, bus->irq);
}
if (bus->codec_mask) {
err = azx_probe_codecs(chip, azx_max_codecs[chip->driver_type]);{
robe_codec(chip, c){
snd_hdac_bus_send_cmd(bus, cmd);
snd_hdac_bus_get_response(bus, addr, &res);
snd_hdac_ext_bus_device_init(ebus, addr); {
snd_hdac_device_init(hdev, bus, name, addr); {
snd_hdac_bus_add_device(bus, codec);{
list_add_tail(&codec->list, &bus->codec_list);
}
codec->vendor_id = snd_hdac_read_parm(codec, AC_NODE_ROOT, AC_PAR_VENDOR_ID);
codec->subsystem_id = snd_hdac_read_parm(codec, AC_NODE_ROOT, AC_PAR_SUBSYSTEM_ID);
err = snd_hdac_refresh_widgets(codec);
codec->power_caps = snd_hdac_read_parm(codec, fg, AC_PAR_POWER_STATE);
err = get_codec_vendor_name(codec);
}
ret = snd_hdac_device_register(hdev); {
err = device_add(&card->card_dev);
err = snd_device_register_all(card))
snd_info_card_register(card); {
proc_symlink(card->id, snd_proc_root->p, card->proc_root->name);
}
init_info_for_card(card);{
entry = snd_info_create_card_entry(card, "id", card->proc_root);
}
}
}
}
azx_stop_chip(chip);
azx_init_chip(chip, true);
}
if (err < 0)
goto out_free;
}
err = snd_card_register(chip->card);
azx_add_card_list(chip);
}
}
}
schedule_work(&hda->probe_work);
}
- 具體註冊過程:
module_init(alsa_card_azx_init)初始化聲卡模塊,會調用alsa_card_azx_init函數,在該函數中去調用platform_driver_register去註冊azx_driver,而azx_driver是一個結構體,
裏面存儲有.driver.name = "ls-audio",
發現一個變量:cards_limit=1, 感覺像是聲卡數量限制1個,在alsa_sound_init時會調用到.
幾個細節:
platform_driver_register(&azx_pci_driver){
__platform_driver_register(drv, THIS_MODULE){
drv->driver.owner = owner;
drv->driver.bus = &platform_bus_type;
drv->driver.probe = platform_drv_probe;
drv->driver.remove = platform_drv_remove;
drv->driver.shutdown = platform_drv_shutdown;
driver_register(&drv->driver){
other = driver_find(drv->name, drv->bus);
ret = bus_add_driver(drv);{
bus = bus_get(drv->bus);
klist_init(&priv->klist_devices, NULL, NULL);
error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,
"%s", drv->name);
klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);
error = driver_attach(drv);
module_add_driver(drv->owner, drv);
error = driver_create_file(drv, &driver_attr_uevent);
error = driver_add_groups(drv, bus->drv_groups);
error = add_bind_files(drv);
}
ret = driver_add_groups(drv, drv->groups);
kobject_uevent(&drv->p->kobj, KOBJ_ADD);
}
}
}
- 相關知識點:
char *kstrdup(const char *s, gfp_t gfp)
snd_hdac_chip_writel
snd_hdac_chip_writeb(bus, CORBSIZE, 0x02);
snd_hdac_chip_writew(bus, CORBWP, 0);
snd_hdac_chip_writew(bus, CORBRP, AZX_CORBRP_RST);
snd_hdac_chip_writew(bus, CORBRP, AZX_CORBRP_RST);
proc_symlink(card->id, snd_proc_root->p, card->proc_root->name);
創建目錄函數proc_mkdir();
struct proc_dir_entry *proc_mkdir( const char *name, struct proc_dir_entry *parent)
該函數將創建一個目錄,父目錄爲parent。
創建符號鏈接函數proc_symlink();
struct proc_dir_entry *proc_symlink( const char *name, struct proc_dir_entry *parent, char *dest)
該函數在parent目錄下創建一個名字爲name的符號鏈接文件,鏈接的目標是dest。
創建設備文件函數proc_mknod();
struct proc_dir_entry *proc_mknod( const char *name, mode_t mode, struct proc_dir_entry *parent, kdev_t *rdev)
該函數在parent目錄下創建一個名字爲name的設備文件,文件類型和權限爲mode,設備號爲rdev。
snd_info_create_card_entry(card, "id", card->proc_root);