声卡设备alsa音频架构1_weixin_34356555的博客-程序员宅基地

技术标签: php  

最近研讨声卡设备,稍微总结一下,后以继续补充:

 第一分部 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-lib的明说http://www.alsa-project.org/main/index.php/Main_Page
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.

    还有一个带图形界面的alsamixer具工,当相大强

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

文章结束给大家分享下程序员的一些笑话语录: IT业众生相
第一级:神人,天资过人而又是技术狂热者同时还拥有过人的商业头脑,高瞻远瞩,技术过人,大器也。如丁磊,求伯君。
第二级:高人,有天赋,技术过人但没有过人的商业头脑,通常此类人不是顶尖黑客就是技术总监之流。
第三级:牛人,技术精湛,熟悉行业知识,敢于创新,有自己的公司和软件产品。
第四级:工头,技术精湛,有领导团队的能力,此类人大公司项目经理居多。
第五级:技术工人,技术精湛,熟悉行业知识但领导能力欠加,此类人大多为系分人员或资深程序员,基本上桀骜不逊,自视清高,不愿于一般技术人员为伍,在论坛上基本以高手面目出现。
第六级:熟练工人,技术有广度无深度,喜欢钻研但浅尝辄止。此类人大多为老程序员,其中一部分喜欢利用工具去查找网上有漏洞的服务器,干点坏事以获取成绩感。如果心情好,在论坛上他们会回答菜鸟的大部分问题。此级别为软件业苦力的重要组成部分。
第七级:工人,某些技术较熟练但缺乏深度和广度,此类人大多为程序员级别,经常在论坛上提问偶尔也回答菜鸟的问题。为软件产业苦力的主要组成部分。
第八级:菜鸟,入门时间不长,在论坛上会反复提问很初级的问题,有一种唐僧的精神。虽然招人烦但基本很可爱。只要认真钻研,一两年后就能升级到上一层。
第九级:大忽悠,利用中国教育的弊病,顶着一顶高学历的帽子,在小公司里混个软件部经理,设计不行,代码不行,只会胡乱支配下属,拍领导马屁,在领导面前胡吹海侃,把自己打扮成技术高手的模样。把勾心斗角的办公室文化引入技术部门,实在龌龊!
第十级:驴或傻X,会写SELECT语句就说自己精通ORALCE,连寄存器有几种都不知道就说自己懂汇编,建议全部送到日本当IT产业工人,挣了日本人的钱还严重打击日本的软件业!

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_34356555/article/details/85556480

智能推荐

java tomcatgbk,Tomcat乱码与端口占用的解决方案_weixin_42365804的博客-程序员宅基地

文章目录问题一:Tomcat的startup.bat启动后出现乱码问题二:一闪而退之端口占用问题三:Java Tomcat 服务器是一个免费的开放源代码的 Web 应用服务器,属于轻量级应用服务器,在中小型系统和并发访问用户不是很多的场合下被普遍使用,是开发和调试 JSP 程序的首选。但是有些小伙伴在启动Tomcat时也会遇到各种各样的问题,比...

linux 内核 strftime,linux 下时间函数strftime()的用法_weixin_39580749的博客-程序员宅基地

strftime() 函数将时间格式化我们可以使用strftime()函数将时间格式化为我们想要的格式。它的原型如下:size_t strftime(char *strDest,size_t maxsize,const char *format,const struct tm *timeptr);我们可以根据format指向字符串中格式命令把timeptr中保存的时间信息放在strDest指向的字...

Codeforces Gym 100589A Queries on the Tree 树状数组 + 分块_Gatevin的博客-程序员宅基地

题目大意:就是现在对于一棵有向树, 以1为根, 初始的时候每个节点上的硬币数量都是0, 树的结点个数为N 大致思路:首先如果对于每次更新操作用树状数组维护暴力执行的话, 对于同一深度的结点数量很多的时候这个复杂度会达到O(N*M*logN)显然是不能接受的, 那么考虑一下分块的思想首先用时间戳的思想将这棵树映射到一个区间上然后对于每次修改, 当要修改的点数小于sqrt(N)

js实现照片墙效果_路光.的博客-程序员宅基地_js照片墙特效

js实现照片墙效果 &lt;script src="underScore.js"&gt;&lt;/script&gt; &lt;style&gt; *{ margin: 0; padding: 0; border: 0; list-style: none; } html, body,ul{ width: 100%;

记录Android-Studio遇到的各种坑_WilsonXin的博客-程序员宅基地

开此文章,主要是为了记录在使用虐我千百遍,我还待他如初恋的AS的过程中所遇到的各种坑,一来是希望做个记录,方便查找,防止再次踏入同样的坑;二来也希望能帮助到有遇到有跟我相同问题的人。1,首先每次导入一个新的AS工程的时候,肯定会有各种gradle配置问题,只要把build.gradle改为与自己系统相匹配即可,如果报Error:(1, 0) Plugin is too old, p

P4208 [JSOI2008]最小生成树计数_luogu_wbling的博客-程序员宅基地

P4208 [JSOI2008]最小生成树计数#include &lt;bits/stdc++.h&gt;using namespace std;const int maxn = 1e3 + 10;const int mod = 31011;struct Edge { int now, to, val;}edge[maxn];//存边 struct Mintree { int l...

随便推点

【预测模型】基于鲸鱼算法优化卷积神经网络CNN预测matlab源码_Matlab科研辅导帮的博客-程序员宅基地_matlab一维卷积神经网络

1 简介1.1 鲸鱼算法1.2 卷积神经网络2 部分代码%%基于鲸鱼算法改进cnn预测clc;clear;closeallloadY1_all.matrxSymbloadSINR1_all.matsinr%%%序列的前 90% 用于训练,后 10% 用于测试shuru=size(rxSymb,2);%输入层数numTimeStepsTrain=floor(0.90*size(rxSymb,1));%训练样本数numTim...

一个简单的SpringColud项目结构及理解_忽觉夏长的博客-程序员宅基地

SpringColud文章目录SpringColud1.分布式应用*( 微服务)SpringCloud概述2.Eureka注册中心2.1 注册中心eureka-server2.2 商品服务product-server2.3 商品服务接口product-api搭建2.4 SpringCloud Eureka 自我保护机制3.微服务调用方式Ribbon3.1订单服务order-server3.2 使用Ribbon来实现远程调用3.3 使用Ribbon实现负载均衡4.微服务调用方式Feign4.1使用Feig

linux中sfdisk和parted用法_weixin_33739523的博客-程序员宅基地

1.sfdisk为磁盘分区工具程序,可显示分区的设定信息,并检查分区是否正常也可用来建立分区。用法:sfdisk[options]device选项说明:常用选项:-s[or--show-size]:显示一个分区的大小-c[or--id]:显示或者修改文件系统类型ID-l[or--list]:显示每个设备的分区表信息-...

PAT甲级1134 Vertex Cover_冷眼观world的博客-程序员宅基地

A vertex cover of a graph is a set of vertices such that each edge of the graph is incident to at least one vertex of the set. Now given a graph with several vertex sets, you are supposed to tell if each of them is a vertex cover or not.Input Specificatio

PHP清除HTML标签_JonLee2020的博客-程序员宅基地

单纯用php原生函数 strip_tags 清除仍有残留和空格,下面的方法完美解决,将html转换为纯文本。/** * 清除html标签 */function clear_tags($str){    $str = strip_tags($str);    //首先去掉头尾空格    $str = trim($str);    $str = preg_replace("/(\s

【Pandas】Pandas处理大数据集的方法(内存优化,减少内存使用量90%)_Charles.zhang的博客-程序员宅基地

目录将内存使用量减少高达90%的方法使用棒球比赛日志数据帧的内部表示了解子类型使用子类型优化数值列将Numeric与String存储进行比较使用分类优化对象类型读取数据时选择类型分析棒球比赛总结和后续步骤将内存使用量减少高达90%的方法当使用具有小数据(小于100兆字节)的pandas时,性能很少成为问题。当我们迁移到更大的数据(100兆字节到...