最近研讨声卡设备,稍微总结一下,后以继续补充:
第一分部 alsa子系统症结构结体对象等
1.声卡设备类型定义
#define SNDRV_DEV_TOPLEVEL ((__force snd_device_type_t) 0) #define SNDRV_DEV_CONTROL ((__force snd_device_type_t) 1) #define SNDRV_DEV_LOWLEVEL_PRE ((__force snd_device_type_t) 2) #define SNDRV_DEV_LOWLEVEL_NORMAL ((__force snd_device_type_t) 0x1000) #define SNDRV_DEV_PCM ((__force snd_device_type_t) 0x1001) #define SNDRV_DEV_RAWMIDI ((__force snd_device_type_t) 0x1002) #define SNDRV_DEV_TIMER ((__force snd_device_type_t) 0x1003) #define SNDRV_DEV_SEQUENCER ((__force snd_device_type_t) 0x1004) #define SNDRV_DEV_HWDEP ((__force snd_device_type_t) 0x1005) #define SNDRV_DEV_INFO ((__force snd_device_type_t) 0x1006) #define SNDRV_DEV_BUS ((__force snd_device_type_t) 0x1007) #define SNDRV_DEV_CODEC ((__force snd_device_type_t) 0x1008) #define SNDRV_DEV_JACK ((__force snd_device_type_t) 0x1009) #define SNDRV_DEV_LOWLEVEL ((__force snd_device_type_t) 0x2000)
一个声卡可以有多个声卡设备,alsa顶用snd_card描述声卡对象,用snd_device描述声卡设备对象
2.声卡构结体
struct snd_card { int number; //声卡索引号 char id[16]; //id别识字串 char driver[16]; //驱动名 char shortname[32]; //短名 char longname[80]; //长名 char mixername[80]; /* mixer name */ char components[128]; /* card components delimited with space */ struct module *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; //设备表链 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; //控制表链 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; //设备文件 struct device *card_dev; //声卡设备文件 #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 };
2.1.全局变量snd_cards
在"/sound/core/init.c"
struct snd_card *snd_cards[SNDRV_CARDS];
SNDRV_CARDS为8也就是声卡最多8个
3.声卡设备构结体
struct snd_device { struct list_head list; //表链 struct snd_card *card; //所属的声卡 snd_device_state_t state; //设备状态 snd_device_type_t type; //设备类型 void *device_data; /* device structure */ struct snd_device_ops *ops; //声卡设备操纵数函集 };
3.1 设备状态的值
#define SNDRV_DEV_BUILD ((__force snd_device_state_t) 0)//创立 #define SNDRV_DEV_REGISTERED ((__force snd_device_state_t) 1)//注册 #define SNDRV_DEV_DISCONNECTED ((__force snd_device_state_t) 2)//开断连接
3.2 设备类型
也就是面上 1.声卡设备类型定义 所指定的类型
4.声卡操纵数函集
struct snd_device_ops { int (*dev_free)(struct snd_device *dev); //释放 int (*dev_register)(struct snd_device *dev); //注册 int (*dev_disconnect)(struct snd_device *dev); //开断连接 };
第二分部 声卡
1.声卡创立
递传进来的idx为负值,则系统会分配一个idx作为全局snd_cards组数的索引项值,xid字串用来分区描述声卡id,module一般为THIS_MODULE...
int snd_card_create(int idx, const char *xid,struct module *module, int extra_size,struct snd_card **card_ret) { struct snd_card *card; int err, idx2; if (snd_BUG_ON(!card_ret)) return -EINVAL; *card_ret = NULL; if (extra_size < 0) extra_size = 0; card = kzalloc(sizeof(*card) + extra_size, GFP_KERNEL); //分配声卡对象和外额空间的内存 if (!card) return -ENOMEM; if (xid) //若要需充填声卡id别识字串 strlcpy(card->id, xid, sizeof(card->id)); //card->id=xid 声卡id别识字串 err = 0; mutex_lock(&snd_card_mutex); //idx为负值则交由系统选择一个值______________________< if (idx < 0) { for (idx2 = 0; idx2 < SNDRV_CARDS; idx2++) /* idx == -1 == 0xffff means: take any free slot */ if (~snd_cards_lock & idx & 1<<idx2) { if (module_slot_match(module, idx2)) { idx = idx2; break; } } } if (idx < 0) { for (idx2 = 0; idx2 < SNDRV_CARDS; idx2++) /* idx == -1 == 0xffff means: take any free slot */ if (~snd_cards_lock & idx & 1<<idx2) { if (!slots[idx2] || !*slots[idx2]) { idx = idx2; break; } } } if (idx < 0) err = -ENODEV; else if (idx < snd_ecards_limit) { if (snd_cards_lock & (1 << idx)) err = -EBUSY; /* invalid */ } else if (idx >= SNDRV_CARDS) err = -ENODEV; if (err < 0) { mutex_unlock(&snd_card_mutex); snd_printk(KERN_ERR "cannot find the slot for index %d (range 0-%i), error: %d\n",idx, snd_ecards_limit - 1, err); goto __error; } snd_cards_lock |= 1 << idx; /* lock it */ if (idx >= snd_ecards_limit) snd_ecards_limit = idx + 1; /* increase the limit */ mutex_unlock(&snd_card_mutex); //______________________> card->number = idx; //声卡对象索引号 全局组数snd_cards的组数标下 card->module = module; //声卡对象块模全部者THIS_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 err = snd_ctl_create(card); //创立用于控制的声卡设备对象 if (err < 0) { snd_printk(KERN_ERR "unable to register control minors\n"); goto __error; } err = snd_info_card_create(card); //proc上面的接口 if (err < 0) { snd_printk(KERN_ERR "unable to create card info\n"); goto __error_ctl; } if (extra_size > 0) //有外额数据 card->private_data = (char *)card + sizeof(struct snd_card); *card_ret = card; return 0; __error_ctl: snd_device_free_all(card, SNDRV_DEV_CMD_PRE); __error: kfree(card); return err; } EXPORT_SYMBOL(snd_card_create);
这里要主是初始化了声卡的devices声卡设备表链,后以创立的声卡设备将挂在该表链上
并调用了snd_ctl_create创立了用于控制的声卡设备对象,这我们可以得出一个论断个每声卡都有一个声卡控制设备对象
第三分部 声卡设备
1.创立声卡设备
在这里 声卡设备与声卡捆绑,指定声卡设备类型,置设声卡设备状态,捆绑对应的snd_device_ops法方,添加声卡设备到声卡的devices表链
int snd_device_new(struct snd_card *card, snd_device_type_t type,void *device_data, struct snd_device_ops *ops) { struct snd_device *dev; if (snd_BUG_ON(!card || !device_data || !ops)) return -ENXIO; dev = kzalloc(sizeof(*dev), GFP_KERNEL); //分配声卡设备内存 if (dev == NULL) { snd_printk(KERN_ERR "Cannot allocate device\n"); return -ENOMEM; } dev->card = card; //设备捆绑对象 dev->type = type; //指定设备类型 dev->state = SNDRV_DEV_BUILD; //设备状态 已创立 dev->device_data = device_data; //设备数据 dev->ops = ops; //设备操纵数函集 list_add(&dev->list, &card->devices); //添加到声卡对象的设备devices表链中 return 0; } EXPORT_SYMBOL(snd_device_new);
这里声卡设备的snd_device_ops是递传进来的构结体指针,实际操纵中一般调用以下API来创立不同类型的声卡
1.1 创立声卡设备罕见API
snd_ctl_create -->snd_device_new(card, SNDRV_DEV_CONTROL, card, &ops); snd_card_proc_new -->snd_device_new(card, SNDRV_DEV_INFO, entry, &ops) snd_timer_new -->snd_device_new(card, SNDRV_DEV_TIMER, timer, &ops) snd_jack_new -->snd_device_new(card, SNDRV_DEV_JACK, jack, &ops) snd_pcm_new -->snd_device_new(card, SNDRV_DEV_PCM, pcm, &ops) snd_rawmidi_new -->snd_device_new(card, SNDRV_DEV_RAWMIDI, rmidi, &ops) snd_seq_device_new -->snd_device_new(card, SNDRV_DEV_SEQUENCER, dev, &dops) snd_hwdep_new -->snd_device_new(card, SNDRV_DEV_HWDEP, hwdep, &ops) snd_i2c_bus_create -->snd_device_new(card, SNDRV_DEV_BUS, bus, &ops) snd_hda_bus_new -->snd_device_new(card, SNDRV_DEV_BUS, bus, &dev_ops) snd_ac97_bus -->snd_device_new(card, SNDRV_DEV_BUS, bus, &dev_ops) ...//不完整
这些API都静态置设了对应类型的snd_device_ops构结体ops,然后调用snd_device_new并将ops递传进来
面前我们再针对不同的声卡设备类型开展分析
创立声卡设备后系统大致入下图描述
创立后的声卡设备的state值为SNDRV_DEV_BUILD
第四分部 注册声卡
1.注册声卡
int snd_card_register(struct snd_card *card) { int err; if (snd_BUG_ON(!card)) return -EINVAL; if (!card->card_dev) { //创立设备文件"/sys/class/sound/cardX" 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; } if ((err = snd_device_register_all(card)) < 0) //-->1.1.注册全部的声卡设备 return err; mutex_lock(&snd_card_mutex); if (snd_cards[card->number]) { //判断对应组数项否是已给占用"/sound/core/init.c" struct snd_card *snd_cards[SNDRV_CARDS]; mutex_unlock(&snd_card_mutex); return 0; } snd_card_set_id_no_lock(card, card->id[0] == '\0' ? NULL : card->id); snd_cards[card->number] = card; //充填全局snd_cards组数 mutex_unlock(&snd_card_mutex); init_info_for_card(card); //proc接口 #if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE) if (snd_mixer_oss_notify_callback) snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_REGISTER); #endif if (card->card_dev) { //创立性属文件 err = device_create_file(card->card_dev, &card_id_attrs); if (err < 0) return err; err = device_create_file(card->card_dev, &card_number_attrs); if (err < 0) return err; } return 0; } EXPORT_SYMBOL(snd_card_register);
要主是调用snd_device_register_all数函注册全部声卡设备,其次是充填了全局snd_cards组数对应的组数项
1.1注册挂在该声卡上面的全部声卡设备
int snd_device_register_all(struct snd_card *card) { struct snd_device *dev; int err; if (snd_BUG_ON(!card)) return -ENXIO; list_for_each_entry(dev, &card->devices, list) { //遍历声卡的设备devices表链 if (dev->state == SNDRV_DEV_BUILD && dev->ops->dev_register) {//状态为已立建且存在dev_register法方 if ((err = dev->ops->dev_register(dev)) < 0) //调用声卡设备的dev_register法方 return err; dev->state = SNDRV_DEV_REGISTERED; //改修状态为已注册 } } return 0; }
这里会遍历声卡对象的devices设备表链,然后调用声卡设备所捆绑的声卡设备操纵数函集合的dev_register法方,注册初始化对应的声卡设备
注册完声卡后,声卡设备的state值修为改SNDRV_DEV_REGISTERED
第五分部 注册声卡设备
不同类型的声卡设备的操纵数函集的dev_register不同,但是也有其个性,上面要主是针对个性来分析
1.snd_minor声卡符字设备构结体
struct snd_minor { int type; //声卡设备类型 SNDRV_DEVICE_TYPE_XXX int card; //声卡索引号 int device; /* device number */ const struct file_operations *f_ops; //文件操纵数函集 void *private_data; //私有数据 struct device *dev; //设备文件 };
1.1 snd_minor的type类型
enum { SNDRV_DEVICE_TYPE_CONTROL, //控制 SNDRV_DEVICE_TYPE_SEQUENCER, //音序器 SNDRV_DEVICE_TYPE_TIMER, //定时器 SNDRV_DEVICE_TYPE_HWDEP, //硬件赖依层 SNDRV_DEVICE_TYPE_RAWMIDI, //raw midi SNDRV_DEVICE_TYPE_PCM_PLAYBACK, //PCM放回 SNDRV_DEVICE_TYPE_PCM_CAPTURE, //PCM捉捕 };
1.2 全局snd_minors全局组数,组数项最大值SNDRV_OS_MINORS为256
static struct snd_minor *snd_minors[SNDRV_OS_MINORS];
2.注册设备文件snd_register_device
封装了(2.1)snd_register_device_for_dev数函
static inline int snd_register_device(int type, struct snd_card *card, int dev,const struct file_operations *f_ops,void *private_data,const char *name) { return snd_register_device_for_dev(type, card, dev, f_ops,private_data, name,snd_card_get_device_link(card)); }
2.1注册设备文件
int snd_register_device_for_dev(int type, struct snd_card *card, int dev,const struct file_operations *f_ops, void *private_data,const char *name, struct device *device) { int minor; //次设备号 struct snd_minor *preg; //明声一个snd_minor构结体 if (snd_BUG_ON(!name)) return -EINVAL; preg = kmalloc(sizeof *preg, GFP_KERNEL); //分配snd_minor构结体内存 if (preg == NULL) return -ENOMEM; preg->type = type; //置设snd_minor类型 preg->card = card ? card->number : -1; //声卡索引号 preg->device = dev; //设备文件 preg->f_ops = f_ops; //文件操纵数函集合 preg->private_data = private_data; //私有数据 mutex_lock(&sound_mutex); #ifdef CONFIG_SND_DYNAMIC_MINORS minor = snd_find_free_minor(); #else minor = snd_kernel_minor(type, card, dev); //取获次设备号 if (minor >= 0 && snd_minors[minor]) minor = -EBUSY; #endif if (minor < 0) { mutex_unlock(&sound_mutex); kfree(preg); return minor; } snd_minors[minor] = preg; //充填全局snd_minors组数项 preg->dev = device_create(sound_class, device, MKDEV(major, minor),private_data, "%s", name); //创立"/dev/snd/XXX" if (IS_ERR(preg->dev)) { snd_minors[minor] = NULL; mutex_unlock(&sound_mutex); minor = PTR_ERR(preg->dev); kfree(preg); return minor; } mutex_unlock(&sound_mutex); return 0; } EXPORT_SYMBOL(snd_register_device_for_dev);
要主是取获次设备号,并创立设备文件,捆绑文件操纵数函集合
第六分部 声卡驱动的编写框架
综合面上五个分部得出下图
编写程过为先调用snd_card_create创立声卡,接着调用创立声卡设备的API创立不同类型的声卡设备件组,接着调用snd_card_register注册声卡就行.
大致走完面上的流程后系统的框图
第七分部 声卡核心子系统的初始化任务
1.明声子系统
subsys_initcall(init_soundcore);
2.子系统初始化
static int __init init_soundcore(void) { int rc; rc = init_oss_soundcore();//初始化oss子系统分部 if (rc) return rc; sound_class = class_create(THIS_MODULE, "sound"); //创立设备类"/sys/class/sound/" if (IS_ERR(sound_class)) { cleanup_oss_soundcore(); return PTR_ERR(sound_class); } sound_class->devnode = sound_devnode; //创立设备节点的法方 return 0; }
要主初始化oss子系统分部
2.1 oss子系统初始化
static int __init init_oss_soundcore(void) { if (preclaim_oss && register_chrdev(SOUND_MAJOR, "sound", &soundcore_fops) == -1) {//创立符字设备 printk(KERN_ERR "soundcore: sound device already in use.\n"); return -EBUSY; } return 0; }
2.2 指定sound_class类的创立设备节点法方sound_devnode
static char *sound_devnode(struct device *dev, mode_t *mode) { if (MAJOR(dev->devt) == SOUND_MAJOR) //主设备号14 oss子系统 return NULL; return kasprintf(GFP_KERNEL, "snd/%s", dev_name(dev)); //alsa子系统 "/dev/sndX/" }
所以alsa子系统的音频设备会出当初/dev/snd/目录下
这里我们可以得悉alsa架构的设备节点在/dev/snd/目录下,oss架构的设备节点在/dev下
alsa的主设备号为116,oss架构的主设备号为14
alsa的主设备号在/sound/core/sound.c中定义
static int major = CONFIG_SND_MAJOR;
#define CONFIG_SND_MAJOR 116
第八分部 声卡控制设备浅析
面前讲到个每声卡都有一个声卡控制设备对象,所以研讨下声卡控制设备
在声卡创立数函snd_card_create中调用了snd_ctl_create数函创立声卡控制设备,并将声卡对象作为参数递传进来
1.创立声卡控制设备
int snd_ctl_create(struct snd_card *card) { static struct snd_device_ops ops = {//静态初始化snd_device_ops声卡设备操纵数函集构结体 .dev_free = snd_ctl_dev_free,//释放法方 .dev_register = snd_ctl_dev_register,//注册法方 .dev_disconnect = snd_ctl_dev_disconnect,//开断连接法方 }; if (snd_BUG_ON(!card)) return -ENXIO; return snd_device_new(card, SNDRV_DEV_CONTROL, card, &ops);//创立声卡控制设备 }
这里还需注意一下snd_device_new数函的参数,现发声卡控制设备的device_data是向指声卡对象的
在注册声卡程过中会调用snd_device_register_all数函,该数函则调用声卡控制设备dev_register法方,既snd_ctl_dev_register数函
2.注册声卡控制设备
static int snd_ctl_dev_register(struct snd_device *device) { struct snd_card *card = device->device_data; //取获声卡对象 int err, cardnum; char name[16]; if (snd_BUG_ON(!card)) return -ENXIO; cardnum = card->number; //取获声卡索引号 if (snd_BUG_ON(cardnum < 0 || cardnum >= SNDRV_CARDS)) return -ENXIO; sprintf(name, "controlC%i", cardnum); //置设名字-->"/dev/snd/controlCx" if ((err = snd_register_device(SNDRV_DEVICE_TYPE_CONTROL, card, -1,&snd_ctl_f_ops, card, name)) < 0) //注册声卡控制设备 return err; return 0; }
通过snd_register_device创立了/dev/snd/controlC0设备文件,假设是0号声卡吧!并捆绑了snd_ctl_f_ops设备文件操纵数函集
3.声卡控制设备对应的设备文件操纵数函集
static const struct file_operations snd_ctl_f_ops = { .owner = THIS_MODULE, .read = snd_ctl_read, //读法方 .open = snd_ctl_open, //打开法方 .release = snd_ctl_release, //释放法方 .llseek = no_llseek, .poll = snd_ctl_poll, //询轮法方 .unlocked_ioctl = snd_ctl_ioctl, //命令控制 .compat_ioctl = snd_ctl_ioctl_compat, //32位兼容的命令控制 .fasync = snd_ctl_fasync, //同步法方 };
这样就供给了应用层的接口法方了,比拟要重的是命令控制法方,要主有以下控制命令
#define SNDRV_CTL_IOCTL_PVERSION _IOR('U', 0x00, int)//打印alsa版本 #define SNDRV_CTL_IOCTL_CARD_INFO _IOR('U', 0x01, struct snd_ctl_card_info)//取获声卡信息 #define SNDRV_CTL_IOCTL_ELEM_LIST _IOWR('U', 0x10, struct snd_ctl_elem_list) #define SNDRV_CTL_IOCTL_ELEM_INFO _IOWR('U', 0x11, struct snd_ctl_elem_info) #define SNDRV_CTL_IOCTL_ELEM_READ _IOWR('U', 0x12, struct snd_ctl_elem_value) #define SNDRV_CTL_IOCTL_ELEM_WRITE _IOWR('U', 0x13, struct snd_ctl_elem_value) #define SNDRV_CTL_IOCTL_ELEM_LOCK _IOW('U', 0x14, struct snd_ctl_elem_id) #define SNDRV_CTL_IOCTL_ELEM_UNLOCK _IOW('U', 0x15, struct snd_ctl_elem_id) #define SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS _IOWR('U', 0x16, int) #define SNDRV_CTL_IOCTL_ELEM_ADD _IOWR('U', 0x17, struct snd_ctl_elem_info) #define SNDRV_CTL_IOCTL_ELEM_REPLACE _IOWR('U', 0x18, struct snd_ctl_elem_info) #define SNDRV_CTL_IOCTL_ELEM_REMOVE _IOWR('U', 0x19, struct snd_ctl_elem_id) #define SNDRV_CTL_IOCTL_TLV_READ _IOWR('U', 0x1a, struct snd_ctl_tlv) #define SNDRV_CTL_IOCTL_TLV_WRITE _IOWR('U', 0x1b, struct snd_ctl_tlv) #define SNDRV_CTL_IOCTL_TLV_COMMAND _IOWR('U', 0x1c, struct snd_ctl_tlv) #define SNDRV_CTL_IOCTL_POWER _IOWR('U', 0xd0, int)//还没支撑 #define SNDRV_CTL_IOCTL_POWER_STATE _IOR('U', 0xd1, int)//电源状态
alsa供给了很多具工可以应用
alsa-utils具工集
aconnect is a utility for connecting and disconnecting two existing ports in the ALSA sequencer system. alsaconf is a configuration tool which tries to detect the sound cards on your system and write a suitable configuration file for ALSA. This program is incompatible with Udev. alsactl is used to control advanced settings for the ALSA sound card drivers. alsaloop allows creation of a PCM loopback between a PCM capture device and a PCM playback device. alsamixer is an Ncurses based mixer program for use with the ALSA sound card drivers. amidi is used to read from and write to ALSA RawMIDI ports. amixer allows command-line control of the mixers for the ALSA sound card drivers. aplay is a command-line soundfile player for the ALSA sound card drivers. aplaymidi is a command-line utility that plays the specified MIDI file(s) to one or more ALSA sequencer ports. arecord is a command-line soundfile recorder for the ALSA sound card drivers. arecordmidi is a command-line utility that records a standard MIDI file from one or more ALSA sequencer ports. aseqdump is a command-line utility that prints the sequencer events it receives as text. aseqnet is an ALSA sequencer client which sends and receives event packets over a network. iecset is a small utility to set or dump the IEC958 (or so-called “S/PDIF”) status bits of the specified sound card via the ALSA control API. speaker-test is a command-line speaker test tone generator for ALSA.