• 声卡设备alsa音频架构1


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

     第一分部 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产业工人,挣了日本人的钱还严重打击日本的软件业!

  • 相关阅读:
    使用JMeter进行RESTful API测试
    DVWA reCAPTCHA key: Missing
    SQL注入之DVWA平台测试mysql注入
    DVWA之SQL注入演练(low)
    浅谈CSRF攻击方式
    WAMPSERVER-服务器离线无法切换到在线状态问题的解决
    SQL注入攻击和防御
    WebScarab安装
    Intellij idea 自动完成的变量名称首字母变为小写
    在IDEA中代码自动提示第一个字母大小写必须匹配的解决
  • 原文地址:https://www.cnblogs.com/jiangu66/p/3063406.html
Copyright © 2020-2023  润新知