hd_audio引脚: RST(Intel  High Definition Audio Reset): This signal is the master hardware reset to external codec(s). SYNC(Intel High Definition Audio Sync): This signal is a 48 kHz fixed rate sample sync to the codec(s). It is also used to encode the stream number. BIT_CLK(Intel High Definition Audio Bit Clock Output):This signal is a 24.000 MHz serial data clock generated by the Intel High Definition  Audio controller . This signal has an integrated pull-down resistor so that ACZ_BIT_CLK doesn’t float when an Intel High Definition Audio codec  (or no codec) is connected but the signals are temporarily configured as AC ’97. DS_OUT(High Definition Audio Serial Data Out):This signal is the serial TDM data output to the codec(s). This serial output is  double-pumped for a bit rate of 48 Mb/s for Intel High Definition Audio. SD_IN(High Definition Audio Serial Data In [2:0]): These signals are serial TDM data inputs from the three codecs. The serial  input is single-pumped for a bit rate of 24 Mb/s for Intel® High Definition Audio. These signals have integrated pull-down resistors  that are always enabled. a Buffer Descriptor List (BDL) GCAP – Global Capabilities  Command Ring Buffer (CORB)  Response Inbound Ring Buffer(响应入栈的环形缓冲区) – RIRB

通过一些函数查找源头,找到了module.inc里有它的初始化函数DRIVER_NAME(void);

一.驱动初始化

1.确定pci设备

声卡设备属于pci设备,查看目标板上pci设备信息:pciConfigTopoShow()

hd audio驱动-编程知识网

找到一个pci相关的设备,本来想看到audio的信息,结果没发现,发现一个可能相关的选项。

然后用pciHeaderShow()查看一下相关信息:

hd audio驱动-编程知识网

查找到他的版本号和设备号。对应驱动中的id_table中的0x808627d8。所有intel的南桥芯片的设备商号都是8086.

利用pci设备商号和设备号,我们google到了该南桥的型号:Intel Corporation 82801G .

查看内核中含有pci字符的函数,在vxworks shell中执行:lkup "pci"

2.初始化

在DRIVER_NAME()这个初始化函数关键代码如下:

while ((d=id_table[i]) != 0) { //获取pci设备商号和设备号
vendor_id = (d >> 16) & 0xffff; dev_id = d & 0xffff; //从instance=0的顺序号开始查找设备总线号,设备号,功能号并给pcidev赋值,如果找到则创建设备
while (pciFindDevice(vendor_id, dev_id, instance,&bus, &dev, &func) == OK) { //为pcidev设备申请空间
oss_pci_device_t *pcidev = malloc(sizeof(*pcidev)); //创建设备 osdev_create ((dev_info_t*)pcidev, DRIVER_TYPE, instance++, DRIVER_NICK,NULL)) ; //将设备绑定到总线上
DRIVER_ATTACH (osdev); } i++; }

那么 osdev_create到底做了什么?

#define DRIVER_NICK "oss_hdaudio"

nick是"oss_hdaudio";dip就是包含总线号,设备号和功能的代表设备的结构体pcidev;dev_type是DRV_PCI;instance是设备所在队列的序列号;而handle为NULL。

oss_device_t * osdev_create (dev_info_t * dip, int dev_type, int instance, const char *nick, const char *handle) { oss_device_t *osdev;
osdev = PMALLOC (NULL, sizeof (*osdev)); if (osdev == NULL) { cmn_err (CE_WARN, "osdev_create: Out of memory\n"); return NULL; }
memset (osdev, 0, sizeof (*osdev)); /*osdev名称osdev->nick='oss_hdaudio0'*/ sprintf (osdev->nick, "%s%d", nick, instance); /*在链表中的第几个*/ osdev->instance = instance; /*pci设备的总线号,设备号,功能号*/ osdev->dip = dip; /*设备是否有效*/ osdev->available = 1; /*是否有混频器加进去*/ osdev->first_mixer = -1; /*modname='oss_hdaudio'*/ strcpy (osdev->modname, nick); /*不知道什么作用*/ if (handle == NULL) handle = nick; /*判断声卡的数目是否超过系统最大能支持的*/ if (oss_num_cards >= MAX_CARDS) cmn_err (CE_WARN, "Too many OSS devices. At most %d permitted.\n", MAX_CARDS); else { /*cardnum是导入驱动时的顺序号,用全局变量cards来保存osdev*/ osdev->cardnum = oss_num_cards; cards[oss_num_cards++] = osdev; } /* Create the device handle*/ strcpy(osdev->handle, "PCICARD");
return osdev; }

到现在为止,找到了设备并得到了总线号,设备号,功能号,并初始化了pcidev和osdev。

3.下面这个是我加上去的

void audioInit() { //设置系统每秒产生1000个时间片 sysClkRateSet(1000); ossDrv(); }

int ossDrv (void) { //获取每秒产生时间片的数量 oss_hz = sysClkRateGet(); //导入系统驱动,返回驱动号 oss_driver_num = iosDrvInstall ((FUNCPTR) NULL,/* create */ (FUNCPTR) NULL,/* delete */ (FUNCPTR) ossOpen, (FUNCPTR) ossClose, (FUNCPTR) ossRead, (FUNCPTR) ossWrite, (FUNCPTR) ossIoctl/* ioctl */ ); /*********************************************************** //和DRIVER_INIT函数里一样,只是有两处不同 1./*osdev名称osdev->nick='osscore0'*/ sprintf (osdev->nick, "%s%d", nick, instance); 2./*modname='osscore'*/ strcpy (osdev->modname, nick); **********************************************************/ core_osdev =osdev_create (NULL, DRV_UNKNOWN, 0, "osscore", NULL); /*strcpy (osdev->name, name);只是将core_osdev的name设置为OSS core services*/ oss_register_device (core_osdev, "OSS core services"); oss_common_init (core_osdev); }

再看看oss_common_init (core_osdev);这个函数创建了几个设备,我们来看下

void oss_common_init (oss_device_t * osdev) { static int drivers_loaded = 0; if (drivers_loaded) return; drivers_loaded = 1; /*只是创建了一个锁*/ MUTEX_INIT (osdev, audio_global_mutex, MH_DRV);  install_vdsp (osdev); install_vmidi (osdev); install_dev_mixer (osdev); /*Check that the processor is compatible with vmix (has proper FP support).这个函数用到内嵌汇编和CPUID指令*/ vmix_core_init (osdev); }

void install_vdsp (oss_device_t * osdev) { /*void oss_install_chrdev (oss_device_t * osdev, char *name, int dev_class, int instance, oss_cdev_drv_t * drv, int flags) osdev就是我们传进来的core_osdev,name="/dev/dsp",dev_class=OSS_DEV_VDSP,instance=0,最后的flag为CHDEV_VIRTUAL; 其中的drv就是我们的static oss_cdev_drv_t vdsp_cdev_drv = { oss_open_vdsp, oss_audio_release, oss_audio_read, oss_audio_write, oss_audio_ioctl, };函数列表*/ oss_install_chrdev (osdev, "/dev/dsp", OSS_DEV_VDSP, 0, &vdsp_cdev_drv, CHDEV_VIRTUAL); }

void oss_install_chrdev (oss_device_t * osdev, char *name, int dev_class, int instance, oss_cdev_drv_t * drv, int flags) { int num; oss_cdev_t *cdev = NULL; if (oss_num_cdevs >= OSS_MAX_CDEVS) grow_array(osdev, &oss_cdevs, &oss_max_cdevs, 100); num = oss_num_cdevs++; cdev->dev_class = dev_class; cdev->instance = instance; cdev->d = drv; cdev->osdev = osdev; strncpy (cdev->name, name, sizeof (cdev->name)); cdev->name[sizeof (cdev->name) – 1] = 0; oss_cdevs[num] = cdev; strcpy (cdev->name, name); register_chrdev (cdev, name);

}

static void register_chrdev(oss_cdev_t *cdev, char *name) { iosDevAdd ((void *)cdev, name, oss_driver_num) ; }

就是在设备号上添加一个设备。其他创建过程类似,分别创建了dsp,midi,mixer;

最后vmix_core_init (osdev);判断是否有vmix能力,如果没有就报错。

3.oss_hdaudio_attach(osdev)

在DRIVER_NAME()函数中调用了DRIVER_ATTACH()调用oss_hdaudio_attach();该函数完成了读取pci配置空间,并赋给osdev

int oss_hdaudio_attach (oss_device_t * osdev) { //读取设备商号和设备号
pci_read_config_word (osdev, PCI_VENDOR_ID, &vendor); pci_read_config_word (osdev, PCI_DEVICE_ID, &device); //读取pci版本号,命令空间,中断线号,子系统号和子系统
pci_read_config_byte (osdev, PCI_REVISION_ID, &pci_revision); pci_read_config_word (osdev, PCI_COMMAND, &pci_command); pci_read_config_irq (osdev, PCI_INTERRUPT_LINE, &pci_irq_line); pci_read_config_word (osdev, PCI_SUBSYSTEM_VENDOR_ID, &subvendor); pci_read_config_word (osdev, PCI_SUBSYSTEM_ID, &subdevice); devc->osdev = osdev; osdev->devc = devc; devc->first_dev = -1;
devc->vendor_id = (vendor << 16) | device; devc->subvendor_id = (subvendor << 16) | subdevice;
oss_pci_byteswap (osdev, 1);
switch (device) { case INTEL_DEVICE_ICH7: devc->chip_name = "Intel HD Audio"; break; } //读取pci内存基地址
pci_read_config_dword (osdev, PCI_MEM_BASE_ADDRESS_0, &devc->membar_addr); devc->membar_addr &= ~7; /* get virtual address */映射虚拟地址 devc->azbar =(void *) MAP_PCI_MEM (devc->osdev, 0, devc->membar_addr, 16 * 1024); devc->irq = pci_irq_line; /* activate the device */ pci_command |= PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY; //写入pci command
pci_write_config_word (osdev, PCI_COMMAND, pci_command); oss_register_device (osdev, devc->chip_name); //读取配置空间中的中断号并使能中断
oss_register_interrupts (devc->osdev, 0, hdaintr, NULL); devc->base = devc->membar_addr;
/* Setup the TCSEL register. Don't do this with ATI chipsets. */ if (vendor != ATI_VENDOR_ID) { pci_read_config_byte (osdev, 0x44, &btmp); pci_write_config_byte (osdev, 0x44, btmp & 0xf8); } err = init_HDA (devc); }

其实就是读取配置空间,填充devc;

int pci_read_config_word   (oss_device_t * osdev, offset_t where, unsigned short *val) { return oss_pci_read_config_word(osdev,where,val); }

int oss_pci_write_config_word (oss_device_t * osdev, offset_t where,unsigned short val) { oss_pci_device_t *pd = osdev->dip; return pciConfigOutWord (pd->bus, pd->dev, pd->func, where, val); }

4.初始化hd_audio的init_HDA()

static int init_HDA (hda_devc_t * devc) { /* Reset controller */ reset_controller (devc); PCI_WRITEL (devc->osdev, devc->azbar + HDA_INTCTL, PCI_READL (devc->osdev, devc->azbar + HDA_INTCTL) | 0xc0000000);/* Intr enable */ /*Set CORB(Command Outbound Ring Buffer) and RIRB(Response Inbound Ring Buffer) sizes to 256, 16 or 2 entries(条目).设置命令 输出循环buffer和响应入栈的环形缓冲区的条目数  */ tmp = (PCI_READB (devc->osdev, devc->azbar + HDA_CORBSIZE) >> 4) & 0x07; if (tmp & 0x4) /* 256 entries */ PCI_WRITEB (devc->osdev, devc->azbar + HDA_CORBSIZE, 0x2); else if (tmp & 0x2) /* 16 entries */ PCI_WRITEB (devc->osdev, devc->azbar + HDA_CORBSIZE, 0x1); else /* Assume that 2 entries is supported */ PCI_WRITEB (devc->osdev, devc->azbar + HDA_CORBSIZE, 0x0);
tmp = (PCI_READB (devc->osdev, devc->azbar + HDA_RIRBSIZE) >> 4) & 0x07; if (tmp & 0x4) /* 256 entries */ PCI_WRITEB (devc->osdev, devc->azbar + HDA_RIRBSIZE, 0x2); else if (tmp & 0x2) /* 16 entries */ PCI_WRITEB (devc->osdev, devc->azbar + HDA_RIRBSIZE, 0x1); else /* Assume that 2 entries is supported */ PCI_WRITEB (devc->osdev, devc->azbar + HDA_RIRBSIZE, 0x0);
/* setup the CORB/RIRB structs :(1)Allocate the CORB and RIRB buffers.(2) Initialize CORB and RIRBregisters*/ setup_controller (devc); /* setup the engine structs 初始化输入输出引擎(两个引擎对应下面的两个输入和输出设备)*/ setup_engines (devc); //创建混频器设备,混频器提供随意混合使用多个通道(来源)的设施,其中还初始化了codec devc->mixer = hdaudio_mixer_create (devc->chip_name, devc, devc->osdev, do_corb_write, corb_read, devc->codecmask, devc->vendor_id, devc->subvendor_id); //将混频器设备给devc
devc->mixer_dev = hdaudio_mixer_get_mixdev (devc->mixer); //读取全局能力寄存器
gcap = PCI_READW (devc->osdev, devc->azbar + HDA_GCAP); //在这里,注册两个字符设备设置字符设备的各个函数接口 install_outputdevs (devc); install_inputdevs (devc); activate_vmix (devc); }

hd audio驱动-编程知识网