文章目录

ALSA声卡驱动(一)初识alsa

目录

  • 文章目录
  • 1、声卡实例
    • 1.1、struct snd_card
    • 1.2、struct snd_devide
  • 2、声卡的创建流程
    • 2.1、创建一个声卡实例
    • 2.2、创建声卡芯片的专用数据
    • 2.3、设置Driver和ID和名字
    • 2.4、创建声卡的功能部件(逻辑设备)
    • 2.5、注册声卡
  • 3、snd_card_create()和snd_card_new()
  • 4、snd_card_register()
  • 总结

1、声卡实例

1.1、struct snd_card

  snd_card可以说是整个ALSA音频驱动最顶层的一个结构,几乎所有与声卡相关的逻辑设备都是在snd_card的管理之下。声卡驱动的第一个动作通常就是创建一个snd_card结构体。
  snd_card结构体定义在头文件include/sound/core.h中:

struct snd_card {int number;         /* number of soundcard (index tosnd_cards) */char id[16];            /* id string of this card */char driver[16];        /* driver name */char shortname[32];     /* short name of this soundcard */char longname[80];      /* name of this soundcard */char irq_descr[32];     /* Interrupt description */char mixername[80];     /* mixer name */char components[128];       /* card components delimited withspace */struct module *module;      /* top-level module */void *private_data;     /* private data for soundcard */void (*private_free) (struct snd_card *card); /* callback for freeing ofprivate data */struct list_head devices;   /* devices */struct device ctl_dev;      /* control device */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;  /* all controls for this card */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 shutdownstate */spinlock_t files_lock;      /* lock the files for this card */int shutdown;           /* this card is going down */struct completion *release_completion;struct device *dev;     /* device assigned to this card */struct device card_dev;     /* cardX object for sysfs */const struct attribute_group *dev_groups[4]; /* assigned sysfs attr */bool registered;        /* card_dev is registered? */
#ifdef CONFIG_PMunsigned int power_state;   /* power state */wait_queue_head_t power_sleep;
#endif#if IS_ENABLED(CONFIG_SND_MIXER_OSS)struct snd_mixer_oss *mixer_oss;int mixer_oss_change_count;
#endif
};

  struct list_head devices 记录该声卡下所有逻辑设备的链表
  struct list_head controls 记录该声卡下所有的控制单元的链表
  void *private_data 声卡的私有数据,可以在创建声卡时通过参数指定数据的大小

1.2、struct snd_devide

  snd_card结构表示一个声卡,而snd_devide结构来抽象表示一个声卡的逻辑设备。

struct snd_device {                                                                                                                                                                                                                                                           struct list_head list;      /* list of registered devices */struct snd_card *card;      /* card which holds this device */enum snd_device_state state;    /* state of the device */enum snd_device_type type;  /* device type */void *device_data;      /* device structure */struct snd_device_ops *ops; /* operations */
};

  
  
  

2、声卡的创建流程

2.1、创建一个声卡实例

  声卡的创建使用snd_card_create() 函数(注意:现在很多地方都换成了snd_card_new()函数):

struct snd_card *card;
int err;
...
err = snd_card_create(index, id, THIS_MODULE, 0, &card);

  index是一个整数值,表示该声卡的编号。
  id是一个字符串,表示声卡的标识符。
  第四个参数决定在创建声卡实例的时候需要同时额外分配的私有数据大小,私有数据的指针最终会赋值给snd_cardprivate_data数据成员。
  card将返回所创建的snd_card实例指针。

2.2、创建声卡芯片的专用数据

  声卡的专用数据主要用于存放声卡的资源信息,例如中断资源、io资源、dma资源等。
  创建的方法有两种:

  • 第一种:给snd_card_create的第四个参数递一个值,让函数自行创建:
//struct mychip 用于保存专用数据
err = snd_card_create(index, id, THIS_MODULE,sizeof(struct mychip), &card);
// 从private_data中取出
struct mychip *chip = card‐>private_data;
  • 第二种:自行创建:
struct mychip {struct snd_card *card;...
};
struct snd_card *card;
struct mychip *chip;
chip = kzalloc(sizeof(*chip), GFP_KERNEL);
...
err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
chip‐>card = card;/* 再把芯片的专有数据注册为声卡的一个低阶设备 */
static int snd_mychip_dev_free(struct snd_device *device)
{return snd_mychip_free(device‐>device_data);
}
static struct snd_device_ops ops = {.dev_free = snd_mychip_dev_free,
};
...
snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);

  注册为低阶设备主要是为了当声卡被注销时,芯片专用数据所占用的内存可以被自动地释放。

2.3、设置Driver和ID和名字

strcpy(card‐>driver, "My Chip");
strcpy(card‐>shortname, "My Own Chip 123");
sprintf(card‐>longname, "%s at 0x%lx irq %i",card‐>shortname, chip‐>ioport, chip‐>irq);

  snd_carddriver域保存着芯片的ID字符串,用户空间的alsa-lib会使用到该字符串,必须保证ID的唯一性。
  shortname更多的是用于打印信息;
  longname域则会出现在 /proc/asound/cards中。

2.4、创建声卡的功能部件(逻辑设备)

  声卡的每一种部件的创建最终都会调用snd_device_new() 来生成一个snd_device实例,并把这个实例链接到snd_carddevices链表中。
  通常alsa-driver会提供一些常用部件的创建函数,不必直接调用snd_device_new() ,比如:

PCM     —>  snd_pcm_new()
RAWMIDI   —>  snd_rawmidi_new()
CONTROL   —>  snd_ctl_create()
TIMER     —>  snd_timer_new()
INFO      —>  snd_card_proc_new()
JACK      —>   snd_jack_new()

2.5、注册声卡

err = snd_card_register(card);
if (err < 0) {snd_card_free(card);return err;
}

  经过以上的创建步骤之后,声卡的逻辑结构如下图所示:

3、snd_card_create()和snd_card_new()

  snd_card_create() 函数在新的内核版本中被替换成了snd_card_new() 函数,定义在sound/core/init.c中:

/***  snd_card_new - create and initialize a soundcard structure*  @parent: the parent device object*  @idx: card index (address) [0 ... (SNDRV_CARDS-1)]*  @xid: card identification (ASCII string)*  @module: top level module for locking*  @extra_size: allocate this extra size after the main soundcard structure*  @card_ret: the pointer to store the created card instance**  Creates and initializes a soundcard structure.**  The function allocates snd_card instance via kzalloc with the given*  space for the driver to use freely.  The allocated struct is stored*  in the given card_ret pointer.**  Return: Zero if successful or a negative error code.*/
int snd_card_new(struct device *parent, int idx, const char *xid,                                                                                                                                                                                                             struct module *module, int extra_size,struct snd_card **card_ret)

  根据extra_size参数的大小分配内存,该内存区通常作为芯片的私有数据使用:

    if (extra_size < 0)extra_size = 0;card = kzalloc(sizeof(*card) + extra_size, GFP_KERNEL);if (!card)return -ENOMEM;if (extra_size > 0)card->private_data = (char *)card + sizeof(struct snd_card);

  拷贝声卡ID字符串:

    if (xid)strlcpy(card->id, xid, sizeof(card->id));

  如果传入的声卡编号为-1,自动分配一个新的索引号:

    if (idx < 0) /* first check the matching module-name slot */idx = get_slot_from_bitmask(idx, module_slot_match, module);if (idx < 0) /* if not matched, assign an empty slot */idx = get_slot_from_bitmask(idx, check_empty_slot, module);

  初始化snd_card结构中必要的字段:

    card->dev = parent;card->number = idx;card->module = 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);
#ifdef CONFIG_PMinit_waitqueue_head(&card->power_sleep);
#endif

  建立逻辑设备: Control

/* the control interface cannot be accessed from the user space until *//* snd_cards_bitmask and snd_cards are set with snd_card_register */err = snd_ctl_create(card);if (err < 0) {dev_err(parent, "unable to register control minors\n");goto __error;}

  建立proc文件中的info节点:通常就是 /proc/asound/card0

    err = snd_info_card_create(card);if (err < 0) {dev_err(parent, "unable to create card info\n");goto __error_ctl;}

  返回创建的snd_card对象:

struct snd_card *card;
...
*card_ret = card;

4、snd_card_register()

  snd_card_register() 函数也定义在 /sound/core/init.c中:

/***  snd_card_register - register the soundcard*  @card: soundcard structure**  This function registers all the devices assigned to the soundcard.*  Until calling this, the ALSA control interface is blocked from the*  external accesses.  Thus, you should call this function at the end*  of the initialization of the card.**  Return: Zero otherwise a negative error code if the registration failed.*/
int snd_card_register(struct snd_card *card)

  创建sysfs下的设备:

    if (!card->registered) {err = device_add(&card->card_dev);                                                                                                                                                                                                                                    if (err < 0)return err;card->registered = true;}

  通过snd_device_register_all() 注册所有挂在该声卡下的逻辑设备, snd_device_register_all() 实际上是通过snd_carddevices链表,遍历所有的snd_device,并且调用snd_deviceops->dev_register() 来实现各自设备的注册的:

if ((err = snd_device_register_all(card)) < 0)return err;

  最后就是建立一些相应的proc和sysfs下的文件或属性节点:

    if (*card->id) {/* make a unique id name from the given string */char tmpid[sizeof(card->id)];memcpy(tmpid, card->id, sizeof(card->id));snd_card_set_id_no_lock(card, tmpid, tmpid);} else {/* create an id from either shortname or longname */const char *src;src = *card->shortname ? card->shortname : card->longname;snd_card_set_id_no_lock(card, src,retrieve_id_from_card_name(src));}snd_cards[card->number] = card;                                                                                                                                                                                                                                           init_info_for_card(card);          //前面都是在填充,这里建立节点

  另外,设备对应的class/sound/sound_core.c中创建:

static char *sound_devnode(struct device *dev, umode_t *mode)
{if (MAJOR(dev->devt) == SOUND_MAJOR)return NULL;return kasprintf(GFP_KERNEL, "snd/%s", dev_name(dev));
}static int __init init_soundcore(void)
{int rc; rc = init_oss_soundcore();if (rc)return rc; sound_class = class_create(THIS_MODULE, "sound");if (IS_ERR(sound_class)) {cleanup_oss_soundcore();return PTR_ERR(sound_class);}   sound_class->devnode = sound_devnode;return 0;
}

  由此可见,声卡的class将会出现在文件系统的 /sys/class/sound/ 下面,并且, sound_devnode() 也决定了相应的设备节点也将会出现在 /dev/snd/ 下面
  
  
  
  

总结

  声卡实例snd_cardsnd_card_new() 函数创建,指定了声卡的编号、标识符和私有数据等。然后填充DriverID还有shortnamelongname。紧接着创建声卡所需的逻辑设备,包括control、pcm等,最后将声卡注册到内核中,会在/proc和sysfs中生成相应的节点。
  
  

ALSA声卡驱动(二)声卡创建相关推荐

  1. 安卓声卡驱动:2.Machine驱动与声卡

    一 声卡简介 一个音频系统由硬件和驱动这些硬件的软件组成. 在一台设备上,如果硬件完整,且驱动硬件的软件全部初始化成功后,ALSA系统就会注册一个声卡,通过这个声卡,应用程序可以控制硬件设备的链路联通 ...

  2. win7声卡驱动安装失败(不能安装)完美解决方法

    这篇文章完美解决 Win7声卡驱动安装失败 的问题! 首先请在 驱动人生 官网下载最新版本的驱动人生,自动更新一次 声卡驱动 ,更新后会自动修复系统漏洞,修复后可以使用手动更新安装驱动或安装品牌电脑的 ...

  3. Linux ALSA声卡驱动之二:Platform

    ALSA声卡驱动: 1.Linux ALSA声卡驱动之一:ALSA架构简介和ASOC架构简介 2.Linux ALSA声卡驱动之二:Platform 3. Linux ALSA声卡驱动之三:Platf ...

  4. Linux ALSA声卡驱动之四:Codec 以及Codec_dai

    ALSA声卡驱动: 1.Linux ALSA声卡驱动之一:ALSA架构简介和ASOC架构简介 2.Linux ALSA声卡驱动之二:Platform 3. Linux ALSA声卡驱动之三:Platf ...

  5. Linux ALSA声卡驱动之五:Machine 以及ALSA声卡的注册

    ALSA声卡驱动: 1.Linux ALSA声卡驱动之一:ALSA架构简介和ASOC架构简介 2.Linux ALSA声卡驱动之二:Platform 3. Linux ALSA声卡驱动之三:Platf ...

  6. Linux ALSA声卡驱动之三:Platform之Cpu_dai

    ALSA声卡驱动: 1.Linux ALSA声卡驱动之一:ALSA架构简介和ASOC架构简介 2.Linux ALSA声卡驱动之二:Platform 3. Linux ALSA声卡驱动之三:Platf ...

  7. Linux ALSA声卡驱动之七:录音(Capture) 调用流程

    ALSA声卡驱动: 1.Linux ALSA声卡驱动之一:ALSA架构简介和ASOC架构简介 2.Linux ALSA声卡驱动之二:Platform 3. Linux ALSA声卡驱动之三:Platf ...

  8. ALSA声卡驱动中的DAPM详解之六:精髓所在,牵一发而动全身

    设计dapm的主要目的之一,就是希望声卡上的各种部件的电源按需分配,需要的就上电,不需要的就下电,使得整个音频系统总是处于最小的耗电状态,最主要的就是,这一切对用户空间的应用程序是透明的,也就是说,用 ...

  9. 【硬创邦】跟hoowa学做智能路由(十二):网络音箱之声卡驱动

    题记:忽闻水上琵琶声,主人忘归客不发.转轴拨弦三两声,未成曲调先有情. 对于大多数年轻的女性来说,她们最关心的是生活和自己的男人.男人应该更多的关系自己身边的女人,人生是苦比乐多的,因此要乐观的面对自 ...

  10. ALSA声卡驱动中的DAPM详解之四:在驱动程序中初始化并注册widget和route

    前几篇文章我们从dapm的数据结构入手,了解了代表音频控件的widget,代表连接路径的route以及用于连接两个widget的path.之前都是一些概念的讲解以及对数据结构中各个字段的说明,从本章开 ...

最新文章

  1. 机器学习算法推导的较好例子
  2. Unity热更新技术整理
  3. 顺序表应用2:多余元素删除之建表算法
  4. 重载练习1_四种不同参数类型的方法
  5. linux用户和用户组
  6. Python攻克之路-高阶函数
  7. 【Flink】Flink 介绍Flink中 Timer 的使用
  8. Git -- 搭建git服务器
  9. poj2991 Crane
  10. python的遍历循环语句for、不能遍历的数据类型是_14、python循环遍历 for 语法
  11. Java、JSP网上零食销售系统的设计与实现毕业设计
  12. Javascript特效代码大全(420个)(转)
  13. IO编程——文件复制操作
  14. 史上最容易听错的歌词
  15. 【C语言】有n个人围成一圈,顺序排号。从第1个人开始报数(从1到3报数),凡报到3的人退出,问最后留下的人是原来第几号。
  16. 网易测试工程师笔经面经(已上岸)
  17. 程序员爬取 5000+ 口红商品数据,差点比女朋友更懂口红?
  18. Linux下oracle11Gr2(64位)安装说明
  19. App逆向 Frida - 夜神模拟器安装配置 基本使用
  20. 有利可图网_公布有利可图的辅助项目手册

热门文章

  1. 18、HX1838红外遥控模块控制led
  2. qq服务器正在升级维护中,建议您稍后再尝试打开.谢谢!,用友通维护锦集
  3. JAVASUNHome家政服务管理平台计算机毕业设计Mybatis+系统+数据库+调试部署
  4. PhotoShop中批量导出图片
  5. Opencv之高效函数convertTo
  6. 概率图模型——贝叶斯网络
  7. 强化狼群等级制度的灰狼优化算法-附代码
  8. 梅科尔工作室-李庆浩-网页前端7
  9. ACM竞赛入门分析与学习资源总结
  10. BNUOJ-4049-四叉树