📄 pcm.c
字号:
if (pcm->private_free) pcm->private_free(pcm); snd_pcm_lib_preallocate_free_for_all(pcm); snd_pcm_free_stream(&pcm->streams[SNDRV_PCM_STREAM_PLAYBACK]); snd_pcm_free_stream(&pcm->streams[SNDRV_PCM_STREAM_CAPTURE]); kfree(pcm); return 0;}static int snd_pcm_dev_free(struct snd_device *device){ struct snd_pcm *pcm = device->device_data; return snd_pcm_free(pcm);}static void snd_pcm_tick_timer_func(unsigned long data){ struct snd_pcm_substream *substream = (struct snd_pcm_substream *) data; snd_pcm_tick_elapsed(substream);}int snd_pcm_attach_substream(struct snd_pcm *pcm, int stream, struct file *file, struct snd_pcm_substream **rsubstream){ struct snd_pcm_str * pstr; struct snd_pcm_substream *substream; struct snd_pcm_runtime *runtime; struct snd_ctl_file *kctl; struct snd_card *card; int prefer_subdevice = -1; size_t size; snd_assert(rsubstream != NULL, return -EINVAL); *rsubstream = NULL; snd_assert(pcm != NULL, return -ENXIO); pstr = &pcm->streams[stream]; if (pstr->substream == NULL || pstr->substream_count == 0) return -ENODEV; card = pcm->card; down_read(&card->controls_rwsem); list_for_each_entry(kctl, &card->ctl_files, list) { if (kctl->pid == current->pid) { prefer_subdevice = kctl->prefer_pcm_subdevice; if (prefer_subdevice != -1) break; } } up_read(&card->controls_rwsem); switch (stream) { case SNDRV_PCM_STREAM_PLAYBACK: if (pcm->info_flags & SNDRV_PCM_INFO_HALF_DUPLEX) { for (substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; substream; substream = substream->next) { if (SUBSTREAM_BUSY(substream)) return -EAGAIN; } } break; case SNDRV_PCM_STREAM_CAPTURE: if (pcm->info_flags & SNDRV_PCM_INFO_HALF_DUPLEX) { for (substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; substream; substream = substream->next) { if (SUBSTREAM_BUSY(substream)) return -EAGAIN; } } break; default: return -EINVAL; } if (file->f_flags & O_APPEND) { if (prefer_subdevice < 0) { if (pstr->substream_count > 1) return -EINVAL; /* must be unique */ substream = pstr->substream; } else { for (substream = pstr->substream; substream; substream = substream->next) if (substream->number == prefer_subdevice) break; } if (! substream) return -ENODEV; if (! SUBSTREAM_BUSY(substream)) return -EBADFD; substream->ref_count++; *rsubstream = substream; return 0; } if (prefer_subdevice >= 0) { for (substream = pstr->substream; substream; substream = substream->next) if (!SUBSTREAM_BUSY(substream) && substream->number == prefer_subdevice) goto __ok; } for (substream = pstr->substream; substream; substream = substream->next) if (!SUBSTREAM_BUSY(substream)) break; __ok: if (substream == NULL) return -EAGAIN; runtime = kzalloc(sizeof(*runtime), GFP_KERNEL); if (runtime == NULL) return -ENOMEM; size = PAGE_ALIGN(sizeof(struct snd_pcm_mmap_status)); runtime->status = snd_malloc_pages(size, GFP_KERNEL); if (runtime->status == NULL) { kfree(runtime); return -ENOMEM; } memset((void*)runtime->status, 0, size); size = PAGE_ALIGN(sizeof(struct snd_pcm_mmap_control)); runtime->control = snd_malloc_pages(size, GFP_KERNEL); if (runtime->control == NULL) { snd_free_pages((void*)runtime->status, PAGE_ALIGN(sizeof(struct snd_pcm_mmap_status))); kfree(runtime); return -ENOMEM; } memset((void*)runtime->control, 0, size); init_waitqueue_head(&runtime->sleep); init_timer(&runtime->tick_timer); runtime->tick_timer.function = snd_pcm_tick_timer_func; runtime->tick_timer.data = (unsigned long) substream; runtime->status->state = SNDRV_PCM_STATE_OPEN; substream->runtime = runtime; substream->private_data = pcm->private_data; substream->ref_count = 1; substream->f_flags = file->f_flags; pstr->substream_opened++; *rsubstream = substream; return 0;}void snd_pcm_detach_substream(struct snd_pcm_substream *substream){ struct snd_pcm_runtime *runtime; runtime = substream->runtime; snd_assert(runtime != NULL, return); if (runtime->private_free != NULL) runtime->private_free(runtime); snd_free_pages((void*)runtime->status, PAGE_ALIGN(sizeof(struct snd_pcm_mmap_status))); snd_free_pages((void*)runtime->control, PAGE_ALIGN(sizeof(struct snd_pcm_mmap_control))); kfree(runtime->hw_constraints.rules); kfree(runtime); substream->runtime = NULL; substream->pstr->substream_opened--;}static ssize_t show_pcm_class(struct device *dev, struct device_attribute *attr, char *buf){ struct snd_pcm *pcm; const char *str; static const char *strs[SNDRV_PCM_CLASS_LAST + 1] = { [SNDRV_PCM_CLASS_GENERIC] = "generic", [SNDRV_PCM_CLASS_MULTI] = "multi", [SNDRV_PCM_CLASS_MODEM] = "modem", [SNDRV_PCM_CLASS_DIGITIZER] = "digitizer", }; if (! (pcm = dev_get_drvdata(dev)) || pcm->dev_class > SNDRV_PCM_CLASS_LAST) str = "none"; else str = strs[pcm->dev_class]; return snprintf(buf, PAGE_SIZE, "%s\n", str);}static struct device_attribute pcm_attrs = __ATTR(pcm_class, S_IRUGO, show_pcm_class, NULL);static int snd_pcm_dev_register(struct snd_device *device){ int cidx, err; struct snd_pcm_substream *substream; struct snd_pcm_notify *notify; char str[16]; struct snd_pcm *pcm = device->device_data; struct device *dev; snd_assert(pcm != NULL && device != NULL, return -ENXIO); mutex_lock(®ister_mutex); if (snd_pcm_search(pcm->card, pcm->device)) { mutex_unlock(®ister_mutex); return -EBUSY; } list_add_tail(&pcm->list, &snd_pcm_devices); for (cidx = 0; cidx < 2; cidx++) { int devtype = -1; if (pcm->streams[cidx].substream == NULL) continue; 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; } /* device pointer to use, pcm->dev takes precedence if * it is assigned, otherwise fall back to card's device * if possible */ dev = pcm->dev; if (!dev) dev = snd_card_get_device_link(pcm->card); /* register pcm */ err = snd_register_device_for_dev(devtype, pcm->card, pcm->device, &snd_pcm_f_ops[cidx], pcm, str, dev); if (err < 0) { list_del(&pcm->list); mutex_unlock(®ister_mutex); return err; } snd_add_device_sysfs_file(devtype, pcm->card, pcm->device, &pcm_attrs); for (substream = pcm->streams[cidx].substream; substream; substream = substream->next) snd_pcm_timer_init(substream); } list_for_each_entry(notify, &snd_pcm_notify_list, list) notify->n_register(pcm); mutex_unlock(®ister_mutex); return 0;}static int snd_pcm_dev_disconnect(struct snd_device *device){ struct snd_pcm *pcm = device->device_data; struct snd_pcm_notify *notify; struct snd_pcm_substream *substream; int cidx, devtype; mutex_lock(®ister_mutex); if (list_empty(&pcm->list)) goto unlock; list_del_init(&pcm->list); for (cidx = 0; cidx < 2; cidx++) for (substream = pcm->streams[cidx].substream; substream; substream = substream->next) if (substream->runtime) substream->runtime->status->state = SNDRV_PCM_STATE_DISCONNECTED; list_for_each_entry(notify, &snd_pcm_notify_list, list) { notify->n_disconnect(pcm); } for (cidx = 0; cidx < 2; cidx++) { devtype = -1; switch (cidx) { case SNDRV_PCM_STREAM_PLAYBACK: devtype = SNDRV_DEVICE_TYPE_PCM_PLAYBACK; break; case SNDRV_PCM_STREAM_CAPTURE: devtype = SNDRV_DEVICE_TYPE_PCM_CAPTURE; break; } snd_unregister_device(devtype, pcm->card, pcm->device); } unlock: mutex_unlock(®ister_mutex); return 0;}int snd_pcm_notify(struct snd_pcm_notify *notify, int nfree){ struct snd_pcm *pcm; snd_assert(notify != NULL && notify->n_register != NULL && notify->n_unregister != NULL && notify->n_disconnect, return -EINVAL); mutex_lock(®ister_mutex); if (nfree) { list_del(¬ify->list); list_for_each_entry(pcm, &snd_pcm_devices, list) notify->n_unregister(pcm); } else { list_add_tail(¬ify->list, &snd_pcm_notify_list); list_for_each_entry(pcm, &snd_pcm_devices, list) notify->n_register(pcm); } mutex_unlock(®ister_mutex); return 0;}EXPORT_SYMBOL(snd_pcm_notify);#ifdef CONFIG_PROC_FS/* * Info interface */static void snd_pcm_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer){ struct snd_pcm *pcm; mutex_lock(®ister_mutex); list_for_each_entry(pcm, &snd_pcm_devices, list) { snd_iprintf(buffer, "%02i-%02i: %s : %s", pcm->card->number, pcm->device, pcm->id, pcm->name); if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) snd_iprintf(buffer, " : playback %i", pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream_count); if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) snd_iprintf(buffer, " : capture %i", pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream_count); snd_iprintf(buffer, "\n"); } mutex_unlock(®ister_mutex);}static struct snd_info_entry *snd_pcm_proc_entry;static void snd_pcm_proc_init(void){ struct snd_info_entry *entry; if ((entry = snd_info_create_module_entry(THIS_MODULE, "pcm", NULL)) != NULL) { snd_info_set_text_ops(entry, NULL, snd_pcm_proc_read); if (snd_info_register(entry) < 0) { snd_info_free_entry(entry); entry = NULL; } } snd_pcm_proc_entry = entry;}static void snd_pcm_proc_done(void){ snd_info_free_entry(snd_pcm_proc_entry);}#else /* !CONFIG_PROC_FS */#define snd_pcm_proc_init()#define snd_pcm_proc_done()#endif /* CONFIG_PROC_FS *//* * ENTRY functions */static int __init alsa_pcm_init(void){ snd_ctl_register_ioctl(snd_pcm_control_ioctl); snd_ctl_register_ioctl_compat(snd_pcm_control_ioctl); snd_pcm_proc_init(); return 0;}static void __exit alsa_pcm_exit(void){ snd_ctl_unregister_ioctl(snd_pcm_control_ioctl); snd_ctl_unregister_ioctl_compat(snd_pcm_control_ioctl); snd_pcm_proc_done();}module_init(alsa_pcm_init)module_exit(alsa_pcm_exit)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -