第3篇 ALSA Core层(生成字符设备)
参考:韦东山Linux驱动
生成字符设备接口:
(1)在文件 linux-2.6.31.14\sound\core\sound.c
static int __init alsa_sound_init(void)
|
|register_chrdev(major, "alsa", &snd_fops)
static const struct file_operations snd_fops = //重要
{
.owner = THIS_MODULE,
.open = snd_open //
};
snd_open(struct inode *inode, struct file *file)
|
|__snd_open(inode, file);
|
|struct snd_minor *mptr = NULL;
|const struct file_operations *old_fops;
|mptr = snd_minors[minor]; //从 snd_minors 中取出新的 file_operations
|old_fops = file->f_op;
|file->f_op = fops_get(mptr->f_ops); //赋予新的 f_op
|err = file->f_op->open(inode, file); //
//重要函数, 核心函数
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)
|
|struct snd_minor *preg;
|preg = kmalloc(sizeof *preg, GFP_KERNEL);
|preg->f_ops = f_ops;
|snd_minors[minor] = preg; //
|preg->dev = device_create(sound_class, device, MKDEV(major, minor), //
private_data, "%s", name);
init_soundcore(void) //linux-2.6.31.14\sound\sound_core.c
|
|sound_class = class_create(THIS_MODULE, "sound");
(2)控制接口 方面的调用
snd_card_create(int idx, const char *xid, struct module *module, int extra_size, struct snd_card **card_ret) //linux-2.6.31.14\sound\core\init.c
|
|snd_ctl_create(card); //linux-2.6.31.14\sound\core\control.c
|
|static struct snd_device_ops ops = {
| .dev_free = snd_ctl_dev_free,
| .dev_register = snd_ctl_dev_register, //
| .dev_disconnect = snd_ctl_dev_disconnect,
|};
|return snd_device_new(card, SNDRV_DEV_CONTROL, card, &ops); //创建 CONTROL 控制类逻辑设备
snd_ctl_dev_register(struct snd_device *device) //linux-2.6.31.14\sound\core\control.c
|
|snd_register_device(SNDRV_DEVICE_TYPE_CONTROL, card, -1, //linux-2.6.31.14\include\sound\core.h
&snd_ctl_f_ops, card, name))
|
|return snd_register_device_for_dev(type, card, dev, f_ops, //核心函数
private_data, name,
static const struct file_operations snd_ctl_f_ops = //即对应上面提到的 从 snd_minors 中取出新的 file_operations
{ //重要 , 应该会调用后面的 platform 和 codec 的函数
.owner = THIS_MODULE,
.read = snd_ctl_read,
.open = snd_ctl_open,
.release = snd_ctl_release,
.poll = snd_ctl_poll,
.unlocked_ioctl = snd_ctl_ioctl, //重要
.compat_ioctl = snd_ctl_ioctl_compat,
.fasync = snd_ctl_fasync,
};
(3)PCM 方面的调用: (Pulse-code modulation 的缩写, 脉冲编码调制)
snd_pcm_new(struct snd_card *card, const char *id, int device, //linux-2.6.31.14\sound\core\pcm.c
int playback_count, int capture_count,
struct snd_pcm ** rpcm)
|
|static struct snd_device_ops ops = {
| .dev_free = snd_pcm_dev_free,
| .dev_register = snd_pcm_dev_register,
| .dev_disconnect = snd_pcm_dev_disconnect,
|};
|snd_device_new(card, SNDRV_DEV_PCM, pcm, &ops)) //创建声卡 PCM 类逻辑设备
snd_pcm_dev_register(struct snd_device *device) //linux-2.6.31.14\sound\core\pcm.c
|
|for (cidx = 0; cidx < 2; cidx++) { //
| switch (cidx) {
| case SNDRV_PCM_STREAM_PLAYBACK:
| sprintf(str, "pcmC%iD%ip", pcm->card->number, pcm->device);
| devtype = SNDRV_DEVICE_TYPE_PCM_PLAYBACK;
| break;
| case SNDRV_PCM_STREAM_CAPTURE:
| sprintf(str, "pcmC%iD%ic", pcm->card->number, pcm->device);
| devtype = SNDRV_DEVICE_TYPE_PCM_CAPTURE;
| break;
| }
| snd_register_device_for_dev(devtype, pcm->card, //核心函数 注册两个设备,一个播放,一个录音
| pcm->device,
| &snd_pcm_f_ops[cidx],
| pcm, str, dev);
const struct file_operations snd_pcm_f_ops[2] = { //重要 , 应该会调用后面的 platform 和 codec 的函数
{
.owner = THIS_MODULE,
.write = snd_pcm_write,
.aio_write = snd_pcm_aio_write,
.open = snd_pcm_playback_open,
.release = snd_pcm_release,
.poll = snd_pcm_playback_poll,
.unlocked_ioctl = snd_pcm_playback_ioctl, //
.compat_ioctl = snd_pcm_ioctl_compat,
.mmap = snd_pcm_mmap,
.fasync = snd_pcm_fasync,
.get_unmapped_area = dummy_get_unmapped_area,
},
{
.owner = THIS_MODULE,
.read = snd_pcm_read,
.aio_read = snd_pcm_aio_read,
.open = snd_pcm_capture_open,
.release = snd_pcm_release,
.poll = snd_pcm_capture_poll,
.unlocked_ioctl = snd_pcm_capture_ioctl,
.compat_ioctl = snd_pcm_ioctl_compat,
.mmap = snd_pcm_mmap,
.fasync = snd_pcm_fasync,
.get_unmapped_area = dummy_get_unmapped_area,
}
};
(4)snd_card_register(struct snd_card *card) //linux-2.6.31.14\sound\core\init.c
|
|snd_device_register_all(card))
|
|list_for_each_entry(dev, &card->devices, list) {
dev->ops->dev_register(dev)) //即调用上面的 snd_ctl_dev_register 和 snd_pcm_dev_register
怎么写 ALSA 声卡驱动:
(1)创建 CONTROL 控制类逻辑设备 //只有在调用 snd_card_register 后才会真正创建设备
snd_card_create
|
|snd_ctl_create
|
|snd_device_new(card, SNDRV_DEV_CONTROL, card, &ops) //ops.dev_register (对应有1个 file_operations )
(2)初始化, 创建 PCM 逻辑设备 //只有在调用 snd_card_register 后才会真正创建设备
snd_pcm_new
|
|snd_device_new(card, SNDRV_DEV_PCM, pcm, &ops)) //ops.dev_register (对应有2个 file_operations, 一个播放,一个录音 )
(3)snd_card_register //真正创建设备节点
|
|dev->ops->dev_register //调用 CONTROL 和 PCM 的 dev_register 函数
|
|snd_register_device_for_dev //重要函数, 核心函数
|
|preg->f_ops = f_ops;
|preg->dev = device_create(sound_class, device, MKDEV(major, minor), private_data, "%s", name);
浙公网安备 33010602011771号