linux /dev/snd,/dev/snd/controlC0

1,首先来明确这个设备节点的由来。这个节点代表声卡的控制接口。

设备的路径:/dev/snd/controlC0

由于基本的linux的操作是由ioctl进行用户空间和内核空间的数据交互已达到实现控制。那么就看看这个设备的由来。

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);

if ((err = snd_register_device(SNDRV_DEVICE_TYPE_CONTROL, card, -1,

&snd_ctl_f_ops, card, name)) < 0)

return err;

return 0;

}

首先先来认识一下snd_ctl_f_ops:

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,

.fasync                = snd_ctl_fasync,

};

最后调用snd_register_device这个函数,这个函数就是注册了设备的节点,其中涉及到了设备节点的创建和管理,在另一篇文章中有介绍。但是这里要注意snd_minors[ ]这个数组,这个数组其实是

struct snd_minor {

int type;                          /* SNDRV_DEVICE_TYPE_XXX */

int card;                          /* card number */

int device;                    /* device number */

const struct file_operations *f_ops;   /* file operations */

void *private_data;                /* private data for f_ops->open */

struct device *dev;                /* device for sysfs */

};

经过函数的调用跟踪发现创建节点的函数更具dev->devt这个识别节点。这个参数就是创建节点时候MKDEV宏使用的结果,并且把这个结果传递给底下创建节点的函数:

vfs_mknod(nd.path.dentry->d_inode, dentry, mode, dev->devt);

Ps:以上的代码还请参考另外一篇文章。

这里加上一点理解:

Major和minor的关系,Major代表了一类设备,minor代表了一类设备下具体的哪一种代表了控制接口的具体实列。Open函数就是这样的一个函数确定了到底是使用哪个minor,代表着主设备下的某一个从设备,并调用相关的read write iotcl等相关函数。

回到函数中再看看函数:

/**

* snd_register_device_for_dev - Register the ALSA device file for the card

* @type: the device type, SNDRV_DEVICE_TYPE_XXX

* @card: the card instance

* @dev: the device index

* @f_ops: the file operations

* @private_data: user pointer for f_ops->open()

* @name: the device file name

* @device: the &struct device to link this new device to

*

* Registers an ALSA device file for the given card.

* The operators have to be set in reg parameter.

*

* Returns zero if successful, or a negative error code on failure.

*/

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)

{

……

minor = snd_kernel_minor(type, card, dev);

……

snd_minors[minor] = preg;

……

preg->dev = device_create(sound_class, device, MKDEV(major, minor),

private_data, "%s", name);

……

return 0;

}

static int snd_kernel_minor(int type, struct snd_card *card, int dev)

{

int minor;

switch (type) {

case SNDRV_DEVICE_TYPE_SEQUENCER:

case SNDRV_DEVICE_TYPE_TIMER:

minor = type;

break;

case SNDRV_DEVICE_TYPE_CONTROL:

if (snd_BUG_ON(!card))

return -EINVAL;

minor = SNDRV_MINOR(card->number, type);

break;

case SNDRV_DEVICE_TYPE_HWDEP:

case SNDRV_DEVICE_TYPE_RAWMIDI:

case SNDRV_DEVICE_TYPE_PCM_PLAYBACK:

case SNDRV_DEVICE_TYPE_PCM_CAPTURE:

if (snd_BUG_ON(!card))

return -EINVAL;

minor = SNDRV_MINOR(card->number, type + dev);

break;

default:

return -EINVAL;

}

if (snd_BUG_ON(minor < 0 || minor >= SNDRV_OS_MINORS))

return -EINVAL;

return minor;

}

确定minor的数值的函数就是这个函数了,在确定之后就是创建。可以看见在最上层函数调用的时候传递下来的参数为:SNDRV_DEVICE_TYPE_CONTROL,minor又cardnum和type所决定,并且办数组中的某一个数组成员在调用函数中使用。

2,初始化CONTROL节点

应该说知道了结果但是不知道原因,还是有点麻木,总好像少了什么。所以再看看创建函数的调用关系。

不具体介绍一些初始化函数的中的具体事宜,其实sound相关的函数中涉及了很多的结构体变量等等,没有一一仔细的查看。这里只是简单介绍一下调用的关系。

(A)注册初始化函数

int snd_card_create(int idx, const char *xid,

struct module *module, int extra_size,

struct snd_card **card_ret)

|->

/* the control interface cannot be accessed from the user space until */

/* snd_cards_bitmask and snd_cards are set with snd_card_register */

snd_ctl_create (struct snd_card *card))

注意:以上snd_ctl_create这个函数并没有调用到snd_ctl_dev_register,这个函数只是把dev这个结构体初始化,其中ops函数指针指向了snd_device_ops ops。这个ops是static的,函数退出后还是存在。可以再以后的函数中通过函数指针找到并调用。初始化dev结构,并且挂上card的dev list,为了今后使用。

(B)调用初始化函数

最后为了找到调用点即ops-> dev_register。还要从其他的文件切入。

static void snd_soc_instantiate_card(struct snd_soc_card *card)

|->

/**

* 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.

*

* Returns zero otherwise a negative error code if the registrain failed.

*/

看到原版注释了,control interface是在这个函数调用以后才能使用。

int snd_card_register(struct snd_card *card)

|->

/*

* register all the devices on the card.

* called from init.c

*/

int snd_device_register_all(struct snd_card *card)

(C)

以上分别看了和介绍了:调用和初始化注册函数的两部分。在函数执行流程的时候一定要保证先注册,后调用的原则,否则就是一场空。这个机制怎么完成的,还要涉及其他的部分逻辑。

在snd_soc_instantiate_card函数中有这样的一段代码:

if (codec_dev->probe) {

ret = codec_dev->probe(pdev);

if (ret < 0)

goto cpu_dai_err;

}

其中snd_soc_codec_device *codec_dev = card->socdev->codec_dev; codec_dev就是soc_codec_dev_wm9713,其中的probe即wm9713_soc_probe

wm9713_soc_probe

|-> int snd_soc_new_pcms(struct snd_soc_device *socdev,

|                                         int idx, const char *xid)

|      |->

|      int snd_card_create(int idx, const char *xid,

|                             struct module *module, int extra_size,

|                             struct snd_card **card_ret)

|-> int snd_card_register(struct snd_card *card)

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值