hd audio驱动
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()
找到一个pci相关的设备,本来想看到audio的信息,结果没发现,发现一个可能相关的选项。
然后用pciHeaderShow()查看一下相关信息:
查找到他的版本号和设备号。对应驱动中的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);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.