diff options
Diffstat (limited to 'core.c')
-rw-r--r-- | core.c | 369 |
1 files changed, 132 insertions, 237 deletions
@@ -40,220 +40,36 @@ static struct snd_avirt_core core = { static LIST_HEAD(audiopath_list); -struct snd_avirt_audiopath_obj { - struct kobject kobj; - struct list_head list; - struct snd_avirt_audiopath *path; -}; - -static struct kset *snd_avirt_audiopath_kset; -static struct kobject *kobj; - -#define to_audiopath_obj(d) \ - container_of(d, struct snd_avirt_audiopath_obj, kobj) -#define to_audiopath_attr(a) \ - container_of(a, struct snd_avirt_audiopath_attribute, attr) - -/** - * struct snd_avirt_audiopath_attribute - access the attributes of Audio Path - * @attr: attributes of an Audio Path - * @show: pointer to the show function - * @store: pointer to the store function - */ -struct snd_avirt_audiopath_attribute { - struct attribute attr; - ssize_t (*show)(struct snd_avirt_audiopath_obj *d, - struct snd_avirt_audiopath_attribute *attr, char *buf); - ssize_t (*store)(struct snd_avirt_audiopath_obj *d, - struct snd_avirt_audiopath_attribute *attr, - const char *buf, size_t count); -}; - -/** - * audiopath_attr_show - show function of an Audio Path - * @kobj: pointer to kobject - * @attr: pointer to attribute struct - * @buf: buffer - */ -static ssize_t audiopath_attr_show(struct kobject *kobj, struct attribute *attr, - char *buf) -{ - struct snd_avirt_audiopath_attribute *audiopath_attr; - struct snd_avirt_audiopath_obj *audiopath_obj; - - audiopath_attr = to_audiopath_attr(attr); - audiopath_obj = to_audiopath_obj(kobj); - - if (!audiopath_attr->show) - return -EIO; - - return audiopath_attr->show(audiopath_obj, audiopath_attr, buf); -} - /** - * audiopath_attr_store - attribute store function of Audio Path object - * @kobj: pointer to kobject - * @attr: pointer to attribute struct - * @buf: buffer - * @len: length of buffer + * pcm_private_free - callback function to free private data allocated to pcm + * @pcm: the PCM object */ -static ssize_t audiopath_attr_store(struct kobject *kobj, - struct attribute *attr, const char *buf, - size_t len) -{ - struct snd_avirt_audiopath_attribute *audiopath_attr; - struct snd_avirt_audiopath_obj *audiopath_obj; - - audiopath_attr = to_audiopath_attr(attr); - audiopath_obj = to_audiopath_obj(kobj); - - if (!audiopath_attr->store) - return -EIO; - return audiopath_attr->store(audiopath_obj, audiopath_attr, buf, len); -} - -static const struct sysfs_ops snd_avirt_audiopath_sysfs_ops = { - .show = audiopath_attr_show, - .store = audiopath_attr_store, -}; - -/** - * snd_avirt_audiopath_release - Audio Path release function - * @kobj: pointer to Audio Paths's kobject - */ -static void snd_avirt_audiopath_release(struct kobject *kobj) -{ - struct snd_avirt_audiopath_obj *audiopath_obj = to_audiopath_obj(kobj); - - kfree(audiopath_obj); -} - -static ssize_t - audiopath_name_show(struct snd_avirt_audiopath_obj *audiopath_obj, - struct snd_avirt_audiopath_attribute *attr, - char *buf) -{ - return sprintf(buf, "%s\n", audiopath_obj->path->name); -} - -static ssize_t - audiopath_version_show(struct snd_avirt_audiopath_obj *audiopath_obj, - struct snd_avirt_audiopath_attribute *attr, - char *buf) -{ - struct snd_avirt_audiopath *audiopath = audiopath_obj->path; - - return sprintf(buf, "%d.%d.%d\n", audiopath->version[0], - audiopath->version[1], audiopath->version[2]); -} - -static struct snd_avirt_audiopath_attribute snd_avirt_audiopath_attrs[] = { - __ATTR_RO(audiopath_name), - __ATTR_RO(audiopath_version), -}; - -static struct attribute *snd_avirt_audiopath_def_attrs[] = { - &snd_avirt_audiopath_attrs[0].attr, - &snd_avirt_audiopath_attrs[1].attr, - NULL, -}; - -static struct kobj_type snd_avirt_audiopath_ktype = { - .sysfs_ops = &snd_avirt_audiopath_sysfs_ops, - .release = snd_avirt_audiopath_release, - .default_attrs = snd_avirt_audiopath_def_attrs, -}; - -/** - * create_snd_avirt_audiopath_obj - creates an Audio Path object - * @uid: Unique ID of the Audio Path - * - * This creates an Audio Path object and assigns the kset and registers - * it with sysfs. - * @return: Pointer to the Audio Path object or NULL if it failed. - */ -static struct snd_avirt_audiopath_obj * - create_snd_avirt_audiopath_obj(const char *uid) -{ - struct snd_avirt_audiopath_obj *snd_avirt_audiopath; - int retval; - - snd_avirt_audiopath = kzalloc(sizeof(*snd_avirt_audiopath), GFP_KERNEL); - if (!snd_avirt_audiopath) - return NULL; - snd_avirt_audiopath->kobj.kset = snd_avirt_audiopath_kset; - retval = kobject_init_and_add(&snd_avirt_audiopath->kobj, - &snd_avirt_audiopath_ktype, kobj, "%s", - uid); - if (retval) { - kobject_put(&snd_avirt_audiopath->kobj); - return NULL; - } - kobject_uevent(&snd_avirt_audiopath->kobj, KOBJ_ADD); - return snd_avirt_audiopath; -} - -/** - * destroy_snd_avirt_audiopath_obj - destroys an Audio Path object - * @name: the Audio Path object - */ -static void destroy_snd_avirt_audiopath_obj(struct snd_avirt_audiopath_obj *p) -{ - kobject_put(&p->kobj); -} - -/** - * pcm_private_data_free - callback function to free private data allocated to pcm - * @pcm: the PCM with private data - */ -static void pcm_private_data_free(struct snd_pcm *pcm) +static void pcm_private_free(struct snd_pcm *pcm) { struct snd_avirt_private_data *avirt_private_data; - D_PRINTK("Issuing free to private data struct"); - if (pcm->private_data) { - avirt_private_data = pcm->private_data; - if (avirt_private_data->ap_private_data) + /* Free Audio Path private data */ + avirt_private_data = (struct snd_avirt_private_data *)pcm->private_data; + if (avirt_private_data) { + if (avirt_private_data->ap_private_data && + avirt_private_data->ap_private_free) avirt_private_data->ap_private_free(pcm); } kfree(pcm->private_data); } -static struct snd_pcm *pcm_create(struct snd_avirt_stream *stream) +static struct snd_pcm *snd_avirt_pcm_create(struct snd_avirt_stream *stream) { struct snd_avirt_private_data *avirt_private_data; - struct snd_avirt_audiopath *audiopath; - struct snd_pcm_ops *pcm_ops, *pcm_ops_ap; struct snd_pcm *pcm; bool playback = false, capture = false; int err; - audiopath = snd_avirt_audiopath_get(stream->map); - if (!audiopath) { - D_ERRORK("Cannot find Audio Path uid: '%s'!", stream->map); - return ERR_PTR(-EFAULT); - } - - if (!stream->direction) { - pcm_ops_ap = (struct snd_pcm_ops *)audiopath->pcm_playback_ops; + if (!stream->direction) playback = true; - } else { - pcm_ops_ap = (struct snd_pcm_ops *)audiopath->pcm_capture_ops; + else capture = true; - } - pcm_ops = &pcm_ops_avirt; - - /* Set PCM ops for the Audio Path*/ - PCM_OPS_SET(pcm_ops_ap, &pcm_ops, close); - PCM_OPS_SET(pcm_ops_ap, &pcm_ops, prepare); - PCM_OPS_SET(pcm_ops_ap, &pcm_ops, trigger); - PCM_OPS_SET(pcm_ops_ap, &pcm_ops, pointer); - PCM_OPS_SET(pcm_ops_ap, &pcm_ops, get_time_info); - PCM_OPS_SET(pcm_ops_ap, &pcm_ops, silence); - PCM_OPS_SET(pcm_ops_ap, &pcm_ops, copy); - PCM_OPS_SET(pcm_ops_ap, &pcm_ops, mmap); - PCM_OPS_SET(pcm_ops_ap, &pcm_ops, ack); /** Special case: loopback */ if (!strcmp(stream->map, "ap_loopback")) { @@ -264,14 +80,18 @@ static struct snd_pcm *pcm_create(struct snd_avirt_stream *stream) err = snd_pcm_new(core.card, stream->name, stream->device, playback, capture, &pcm); - if (err < 0) + if (err < 0) { + D_ERRORK("Failed to create PCM device for stream: '%s'", + stream->name); return ERR_PTR(err); + } /** Register driver callbacks */ if (playback) - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, pcm_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, + stream->pcm_ops); if (capture) - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, pcm_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, stream->pcm_ops); pcm->info_flags = 0; strcpy(pcm->name, stream->name); @@ -279,11 +99,66 @@ static struct snd_pcm *pcm_create(struct snd_avirt_stream *stream) avirt_private_data = kzalloc(sizeof(*avirt_private_data), GFP_KERNEL); pcm->private_data = avirt_private_data; // Set the private free function for the private user data - pcm->private_free = pcm_private_data_free; + pcm->private_free = pcm_private_free; + + snd_device_register(core.card, pcm); return pcm; } +int snd_avirt_stream_set_map(struct snd_avirt_stream *stream, const char *map) +{ + struct snd_avirt_audiopath *audiopath; + struct snd_pcm_ops *pcm_ops_ap; + + /* If already sealed, we cannot create the stream */ + if (snd_avirt_streams_sealed()) { + D_ERRORK("Streams already sealed. Cannot set map: '%s'", map); + return -EPERM; + } + + if (!strcmp(stream->map, map)) + return -1; + + memcpy(stream->map, (char *)map, strlen(map)); + audiopath = snd_avirt_audiopath_get(map); + if (!audiopath) { + D_ERRORK("Cannot find Audio Path uid: '%s'!", stream->map); + return -EFAULT; + } + + if (!stream->direction) + pcm_ops_ap = (struct snd_pcm_ops *)audiopath->pcm_playback_ops; + else + pcm_ops_ap = (struct snd_pcm_ops *)audiopath->pcm_capture_ops; + + if (!pcm_ops_ap) { + D_ERRORK("No PCM ops for direction '%s' for Audio Path: %s", + (stream->direction) ? "capture" : "playback", + stream->map); + return -EFAULT; + } + + /* Set PCM ops for the Audio Path*/ + PCM_OPS_SET(pcm_ops_ap, &stream->pcm_ops, prepare); + PCM_OPS_SET(pcm_ops_ap, &stream->pcm_ops, trigger); + PCM_OPS_SET(pcm_ops_ap, &stream->pcm_ops, pointer); + PCM_OPS_SET(pcm_ops_ap, &stream->pcm_ops, get_time_info); + PCM_OPS_SET(pcm_ops_ap, &stream->pcm_ops, silence); + PCM_OPS_SET(pcm_ops_ap, &stream->pcm_ops, copy); + PCM_OPS_SET(pcm_ops_ap, &stream->pcm_ops, mmap); + PCM_OPS_SET(pcm_ops_ap, &stream->pcm_ops, ack); + + /* If not created, create the PCM device now */ + if (!stream->pcm) { + stream->pcm = snd_avirt_pcm_create(stream); + if (IS_ERR_OR_NULL(stream->pcm)) + return -EFAULT; + } + + return 0; +} + static int snd_avirt_streams_get(const char *map, struct snd_avirt_stream_array *stream_array) { @@ -311,14 +186,14 @@ struct snd_avirt_audiopath *snd_avirt_audiopath_get(const char *uid) { struct snd_avirt_audiopath_obj *ap_obj; - list_for_each_entry(ap_obj, &audiopath_list, list) { - // pr_info("get_ap %s\n", ap_obj->path->uid); + list_for_each_entry (ap_obj, &audiopath_list, list) { if (!strcmp(ap_obj->path->uid, uid)) return ap_obj->path; } return NULL; } +EXPORT_SYMBOL_GPL(snd_avirt_audiopath_get); /** * snd_avirt_audiopath_register - register Audio Path with AVIRT @@ -335,7 +210,7 @@ int snd_avirt_audiopath_register(struct snd_avirt_audiopath *audiopath) return -EINVAL; } - audiopath_obj = create_snd_avirt_audiopath_obj(audiopath->uid); + audiopath_obj = snd_avirt_audiopath_create_obj(audiopath->uid); if (!audiopath_obj) { D_INFOK("Failed to alloc driver object"); return -ENOMEM; @@ -380,7 +255,7 @@ int snd_avirt_audiopath_deregister(struct snd_avirt_audiopath *audiopath) } list_del(&audiopath_obj->list); - destroy_snd_avirt_audiopath_obj(audiopath_obj); + snd_avirt_audiopath_destroy_obj(audiopath_obj); D_INFOK("Deregistered Audio Path %s", audiopath->uid); return 0; @@ -388,6 +263,28 @@ int snd_avirt_audiopath_deregister(struct snd_avirt_audiopath *audiopath) EXPORT_SYMBOL_GPL(snd_avirt_audiopath_deregister); /** + * snd_avirt_route_create - Create audio route + * @name: The name designated to the audio route + * @direction: The PCM direction (SNDRV_PCM_STREAM_PLAYBACK or + * SNDRV_PCM_STREAM_CAPTURE) + * @return: The newly created audio route if successful, or an error pointer + */ +struct snd_avirt_route *snd_avirt_route_create(const char *name, int direction) +{ + struct snd_avirt_route *route; + + route = kzalloc(sizeof(*route), GFP_KERNEL); + if (!route) + return ERR_PTR(-ENOMEM); + + strcpy(route->name, name); + route->channels = 0; + route->direction = direction; + + return route; +} + +/** * snd_avirt_stream_create - Create audio stream, including it's ALSA PCM device * @name: The name designated to the audio stream * @direction: The PCM direction (SNDRV_PCM_STREAM_PLAYBACK or @@ -414,7 +311,16 @@ struct snd_avirt_stream *snd_avirt_stream_create(const char *name, stream->direction = direction; stream->device = core.stream_count++; - D_INFOK("name: %s device:%d", name, stream->device); + /* Initialize PCM ops table for this stream. + * Will be populated once map is known */ + stream->pcm_ops = kzalloc(sizeof(struct snd_pcm_ops), GFP_KERNEL); + if (!stream->pcm_ops) { + D_ERRORK("Failed to allocate PCM ops table"); + return ERR_PTR(-EFAULT); + } + memcpy(stream->pcm_ops, &pcm_ops_avirt, sizeof(struct snd_pcm_ops)); + + D_INFOK("name:%s device:%d", name, stream->device); return stream; } @@ -423,39 +329,25 @@ int snd_avirt_streams_seal(void) { int err = 0, i = 0; struct snd_avirt_audiopath_obj *ap_obj; - struct snd_avirt_stream *stream; struct snd_avirt_stream_array stream_array; - struct config_item *item; - struct list_head *entry; if (core.streams_sealed) { D_ERRORK("streams are already sealed!"); return -1; } - list_for_each(entry, &core.stream_group->cg_children) { - item = container_of(entry, struct config_item, ci_entry); - stream = snd_avirt_stream_from_config_item(item); - if (!stream) - return -EFAULT; - stream->pcm = pcm_create(stream); - if (IS_ERR_OR_NULL(stream->pcm)) - return (PTR_ERR(stream->pcm)); - } - - list_for_each_entry(ap_obj, &audiopath_list, list) { - D_INFOK("configure() AP uid: %s", ap_obj->path->uid); + list_for_each_entry (ap_obj, &audiopath_list, list) { for (i = 0; i < MAX_STREAMS; i++) stream_array.streams[i] = NULL; stream_array.count = 0; - if (snd_avirt_streams_get(ap_obj->path->uid, &stream_array) > 0) - ap_obj->path->configure(core.card, &stream_array); - } - - err = snd_card_register(core.card); - if (err < 0) { - D_ERRORK("Sound card registration failed!"); - snd_card_free(core.card); + if (snd_avirt_streams_get(ap_obj->path->uid, &stream_array) <= + 0) + continue; + + D_INFOK("Do configure for AP: %s streams:%d cb:%p", + ap_obj->path->uid, stream_array.count, + ap_obj->path->configure); + ap_obj->path->configure(core.card, &stream_array); } core.streams_sealed = true; @@ -479,7 +371,7 @@ struct snd_avirt_stream *snd_avirt_stream_find_by_device(unsigned int device) return ERR_PTR(-EINVAL); } - list_for_each(entry, &core.stream_group->cg_children) { + list_for_each (entry, &core.stream_group->cg_children) { item = container_of(entry, struct config_item, ci_entry); stream = snd_avirt_stream_from_config_item(item); if (!stream) @@ -554,27 +446,30 @@ static int __init snd_avirt_core_init(void) return PTR_ERR(core.class); } - core.dev = device_create(core.class, NULL, 0, NULL, "avirtcore"); + core.dev = device_create(core.class, NULL, 0, NULL, "core"); if (IS_ERR(core.dev)) { err = PTR_ERR(core.dev); goto exit_class; } - snd_avirt_audiopath_kset = - kset_create_and_add("audiopaths", NULL, &core.dev->kobj); - if (!snd_avirt_audiopath_kset) { - err = -ENOMEM; + err = snd_avirt_sysfs_init(&core); + if (err < 0) goto exit_class_container; + + err = snd_card_register(core.card); + if (err < 0) { + D_ERRORK("Sound card registration failed!"); + snd_card_free(core.card); } err = snd_avirt_configfs_init(&core); if (err < 0) - goto exit_kset; + goto exit_sysfs; return 0; -exit_kset: - kset_unregister(snd_avirt_audiopath_kset); +exit_sysfs: + snd_avirt_sysfs_exit(&core); exit_class_container: device_destroy(core.class, 0); exit_class: @@ -592,7 +487,7 @@ exit_platform_device: static void __exit snd_avirt_core_exit(void) { snd_avirt_configfs_exit(&core); - kset_unregister(snd_avirt_audiopath_kset); + snd_avirt_sysfs_exit(&core); device_destroy(core.class, 0); class_destroy(core.class); platform_device_unregister(core.plat_dev); |