文章目录

  • 一、声卡的属性
  • 二、声卡的创建



许久没有更新关于audio的东西了,因为项目原因,接下来又要继续搞audio的东西了,所以继续写一些文章,作为一个温故知新的过程吧。


首先看一下声卡的架构驱动把

一、声卡的属性

开源代码路径

struct snd_card {
    int number;         /* number of soundcard (index to snd_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 with space */
    struct module *module;      /* top-level 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;   /* 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 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 */
    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? */
    int sync_irq;           /* assigned irq, used for PCM sync */
    wait_queue_head_t remove_sleep;
    size_t total_pcm_alloc_bytes;   /* total amount of allocated buffers */
    struct mutex memory_mutex;  /* protection for the above */
#ifdef CONFIG_PM
    unsigned 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
    ANDROID_KABI_RESERVE(1);
    ANDROID_KABI_RESERVE(2);
};

这里面每个参数都有对应的说明,比较重要的应该就是两个连表

struct list_head devices;   /* devices */ //记录该声卡下所有逻辑设备的链表 
struct list_head controls;  /* all controls for this card */ //记录该声卡下所有的控制单元的链表
struct list_head ctl_files; /* active control files */ // 用于管理该card下的active的control设备

这些从代码来看就是定义好了的,没有什么问题需要去修改。

二、声卡的创建

以前是通过 snd_card_create 去创建一个声卡,随着kernel的版本迭代,现在已经变为了 snd_card_new,具体是在哪个版本更新的不知道。这个可能需要注意以下。
基本上创建声卡的函数都是在驱动的probe函数里,
不同的平台可能函数也有一些不一样,这个大家得注意。

xxx_probe {
	error = snd_card_new(dev, index[n], id[n], THIS_MODULE,sizeof(struct snd_es1688), &card);
}
/*
这里面会传递一些参数
	dev  				总线
    index          	 	一个整数值,该声卡的编号
    id                		字符串,声卡的标识符
    第四个参数    	该参数决定在创建snd_card实例时,需要同时额外分配的私有数据的大小,该数据的指针最终会赋值给snd_card的private_data数据成员
    card             		返回所创建的snd_card实例的指针
*/

开源代码说明

声卡的专用数据主要用于存放该声卡的一些资源信息,例如中断资源、io资源、dma资源等.可以有两种创建方法:
通过上一步中snd_card_create()中的第四个参数,让snd_card_create自己创建

In general, there are two ways of allocating the chip record.

1. Allocating via :c:func:`snd_card_new()`.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

As mentioned above, you can pass the extra-data-length to the 5th
argument of :c:func:`snd_card_new()`, i.e.

::

  err = snd_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
                     sizeof(struct mychip), &card);

struct mychip is the type of the chip record.

In return, the allocated record can be accessed as

::

  struct mychip *chip = card->private_data;

With this method, you don't have to allocate twice. The record is
released together with the card instance.

2. Allocating an extra device.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

After allocating a card instance via :c:func:`snd_card_new()`
(with ``0`` on the 4th arg), call :c:func:`kzalloc()`.

::

  struct snd_card *card;
  struct mychip *chip;
  err = snd_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
                     0, &card);
  .....
  chip = kzalloc(sizeof(*chip), GFP_KERNEL);

The chip record should have the field to hold the card pointer at least,

::

  struct mychip {
          struct snd_card *card;
          ....
  };


Then, set the card pointer in the returned chip instance.

::

  chip->card = card;

Next, initialize the fields, and register this chip record as a
low-level device with a specified ``ops``,

::

  static const struct snd_device_ops ops = {
          .dev_free =        snd_mychip_dev_free,
  };
  ....
  snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);

:c:func:`snd_mychip_dev_free()` is the device-destructor
function, which will call the real destructor.

::

  static int snd_mychip_dev_free(struct snd_device *device)
  {
          return snd_mychip_free(device->device_data);
  }

where :c:func:`snd_mychip_free()` is the real destructor.

The demerit of this method is the obviously more amount of codes.
The merit is, however, you can trigger the own callback at registering
and disconnecting the card via setting in snd_device_ops.
About the registering and disconnecting the card, see the subsections
below.

英语不好读起来真的吃力阿 。。。

第一种方法相对来说会简单一些,不需要释放两次内存

然后就是设置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->port, chip->irq);

snd_card的driver字段保存着芯片的ID字符串,user空间的alsa-lib会使用到该字符串,所以必须要保证该ID的唯一性.shortname字段更多地用于打印信息,longname字段则会出现在/proc/asound/cards中.

当一个声卡注册完毕过后,就会去注册这些声卡的功能,包括Mixer和PCM这些,
而这些都是通过各个函数创建的

比如这种:

err = snd_pcm_new(chip->card, "My Chip", 0, 1, 1, &pcm);

在上面那个开源的文章里面,有很多例子,可以仔细去看看。
当所有的东西都农好了过后,最后就把这个声卡注册上去。

/* (6) */
              err = snd_card_register(card);
              if (err < 0)
                      goto error;

当使用完毕过后再释放这个声卡

/* (7) */
              pci_set_drvdata(pci, card);
              dev++;
              return 0;

内核文档真的写的很详细,有时间还是需要自己多去看看。