不浮躁
分类: 嵌入式
2015-02-05 20:05:40
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)