2 * module-audio-dsp.c -- PulseAudio module for audio-dsp
3 * Copyright (c) 2012, Intel Corporation.
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms and conditions of the GNU Lesser General Public License,
7 * version 2.1, as published by the Free Software Foundation.
9 * This program is distributed in the hope it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.
12 * See the GNU Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this program; if not, write to the
16 * Free Software Foundation, Inc., 51 Franklin St - Fifth Floor, Boston,
25 #include <sys/types.h>
29 #include <sys/ioctl.h>
32 #include <linux/limits.h>
35 #include <pulsecore/core.h>
36 #include <pulsecore/module.h>
37 #include <pulsecore/core-util.h>
38 #include <pulsecore/log.h>
39 #include <pulsecore/sink-input.h>
40 #include <pulsecore/namereg.h>
41 #include <pulsecore/modargs.h>
44 #define MEDIA_NAME_AUDIO_DSP "DSP PLAYBACK"
45 #define CLV_VOLUME_DEV_PATH "/dev/intel_sst_ctrl"
46 #define SST_VOLUME_TYPE 0x602
47 #define SST_VOLUME_SIZE 1
48 #define SST_PPP_VOL_STR_ID 0x03
49 #define SNDRV_SST_SET_ALGO _IOW('L', 0x30, struct snd_ppp_params *)
50 #define SNDRV_SST_GET_ALGO _IOWR('L', 0x31, struct snd_ppp_params *)
51 #define SST_CODEC_VOLUME_CONTROL 0x67
52 #define SNDRV_SST_MUTE _IOW('L', 0x13, struct snd_sst_mute *)
53 #define SST_VOLUME_MUTE 0xA0
54 #define BLUEZ_API "bluez"
56 PA_MODULE_AUTHOR("vivian zhang");
57 PA_MODULE_DESCRIPTION("audio-dsp");
58 PA_MODULE_VERSION(PACKAGE_VERSION);
59 PA_MODULE_LOAD_ONCE(TRUE);
62 static void (*change_volume) (pa_volume_t);
63 static void (*set_mute) (int);
65 static int _audio_dsp_plugin_get_list(const char *plugdir ,char ***list);
66 static void _audio_dsp_plugin_destroy_list(char **list);
67 static int audio_dsp_plugin_scan(const char *plugindir);
68 static int audio_dsp_plugin_open(char *file);
69 static void audio_dsp_plugin_close();
70 static char* __strcatdup(const char *str1, const char *str2);
76 pa_hook_slot *sink_input_new_hook_slot;
77 pa_hook_slot *sink_input_put_hook_slot;
78 pa_hook_slot *sink_input_put_hook_slot_rt_switch;
79 pa_subscription *subscription;
83 * @brief audio hardware decoder & software decoder switch
85 #define VCONFKEY_AUDIODEC_SWITCH "memory/sound/audiodec_switch"
89 VCONFKEY_AUDIODEC_NONE = 0x0000,
90 /** Request for switching to software decoder */
91 VCONFKEY_AUDIODEC_TO_SWDEC = 0x0001,
92 /** Request for switching to hardware decoder */
93 VCONFKEY_AUDIODEC_TO_HWDEC = 0x0002,
96 static int role_is_dsp_playback(const pa_proplist *proplist)
100 if (NULL == proplist) {
101 pa_log_debug("[AUDIODSP]Sink input lacks property data.");
105 /* Check whether it is a dsp sink-input */
106 role = pa_proplist_gets(proplist, PA_PROP_MEDIA_NAME);
108 pa_log_debug("[AUDIODSP]Sink input lacks property: %s", PA_PROP_MEDIA_NAME);
111 if (!pa_streq(MEDIA_NAME_AUDIO_DSP, role)) {
112 pa_log_debug("[AUDIODSP]Sink input property: %s=%s, not \"DSP PLAYBACK\", skip it.", PA_PROP_MEDIA_NAME, role);
119 /* check if this sink is bluez */
120 static pa_bool_t is_bluez_sink (pa_sink* sink)
122 const char* api_name = NULL;
125 pa_log_warn ("[AUDIODSP]input param sink is null");
129 api_name = pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_API);
131 if (pa_streq (api_name, BLUEZ_API)) {
139 static void sink_input_volume_changed (pa_sink_input *i)
142 pa_volume_t avgvol = pa_cvolume_avg(&i->volume);
143 change_volume(avgvol);
144 pa_log_debug("[AUDIODSP]Successful in set volume=%u", avgvol);
146 pa_log_debug("[AUDIODSP]Missing plugin provided change_volume func");
150 static void sink_input_mute_changed(pa_sink_input *i)
154 pa_log_debug("[AUDIODSP]Set mute succeed.");
156 pa_log_debug("[AUDIODSP]Missing plugin provided set_mute func.");
160 static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_new_data *new_data, struct userdata *u)
165 if (0 == role_is_dsp_playback(new_data->proplist))
168 PA_IDXSET_FOREACH(s, c->sinks, i) {
169 if (NULL != s->name && pa_streq(s->name, "null")) {
171 pa_log_debug("[AUDIODSP]set sink to null sink");
179 /* Called when new sink-input is creating */
180 static pa_hook_result_t sink_input_put_hook_callback(pa_core *core, pa_sink_input *i, struct userdata *u)
182 if (0 == role_is_dsp_playback(i->proplist))
185 pa_log_debug("[AUDIODSP]Set volume_changed and mute_changed function");
186 i->volume_changed = sink_input_volume_changed;
187 i->mute_changed = sink_input_mute_changed;
192 static pa_hook_result_t sink_input_put_hook_callback_rt_switch(pa_core *core, pa_sink_input *i, struct userdata *u)
194 pa_log_debug("[AUDIODSP]sink_input_put_hook_callback_rt_switch enter");
195 pa_sink *s = i->sink;
197 if (0 == role_is_dsp_playback(i->proplist)) {
198 pa_log_debug("[AUDIODSP]Not %s, skip.", MEDIA_NAME_AUDIO_DSP);
202 if (is_bluez_sink(s)) {
203 pa_log_debug("[AUDIODSP]Find new bt sink input, set %s to VCONFKEY_AUDIODEC_TO_SWDEC:%d", VCONFKEY_AUDIODEC_SWITCH, VCONFKEY_AUDIODEC_TO_SWDEC);
204 vconf_set_int(VCONFKEY_AUDIODEC_SWITCH, VCONFKEY_AUDIODEC_TO_SWDEC);
207 pa_log_debug("[AUDIODSP]sink_input_put_hook_callback_rt_switch leave");
211 static void subscribe_cb(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata)
213 struct userdata *u = userdata;
217 pa_log_debug("[AUDIODSP][%s] subscribe_cb() t=[0x%x], idx=[%d]", __func__, t, idx);
219 /* We only handle server changes */
220 if (t == (PA_SUBSCRIPTION_EVENT_SERVER|PA_SUBSCRIPTION_EVENT_CHANGE)) {
222 def = pa_namereg_get_default_sink(c);
224 pa_log_warn("[AUDIODSP][%s] pa_namereg_get_default_sink() returns null", __func__);
228 if (is_bluez_sink(def)) {
229 vconf_set_int(VCONFKEY_AUDIODEC_SWITCH, VCONFKEY_AUDIODEC_TO_SWDEC);
231 vconf_set_int(VCONFKEY_AUDIODEC_SWITCH, VCONFKEY_AUDIODEC_TO_HWDEC);
236 static int _audio_dsp_plugin_get_list(const char *plugdir ,char ***list)
238 struct dirent **entry = NULL;
243 static char curdir[PATH_MAX];
247 items = scandir(plugdir, &entry, NULL, alphasort);
248 pa_log_debug("[AUDIODSP]Items %d", items);
253 temp = (char **)malloc(sizeof(char *) * (items + 1));
258 memset(temp, 0, sizeof(char*) * (items + 1));
259 memset(curdir, '\0', sizeof(curdir));
260 if(NULL == getcwd(curdir, sizeof(curdir)-1)) {
268 /* FIXME : need to handle error case */
271 for(item_idx = items; item_idx--; ) {
272 if(stat(entry[item_idx]->d_name, &finfo) < 0) {
273 pa_log_error("[AUDIODSP]Stat error");
282 pa_log_debug("[AUDIODSP]item %d is %s", item_idx, entry[item_idx]->d_name);
284 if (S_ISREG(finfo.st_mode)) {
285 temp[tn] = __strcatdup(plugdir, "/");
286 temp[tn] = __strcatdup(temp[tn], entry[item_idx]->d_name);
292 for(item_idx = 0; item_idx < items; item_idx++) {
293 free(entry[item_idx]);
299 static void _audio_dsp_plugin_destroy_list(char **list)
308 static int audio_dsp_plugin_scan(const char *plugindir)
314 int plugin_index = 0;
316 pa_log_debug("[AUDIODSP] Plugin dir :: %s", plugindir);
317 err = _audio_dsp_plugin_get_list(plugindir, &list);
321 while((item = list[index++]) != NULL) {
322 if(audio_dsp_plugin_open(item) != 0) {
323 pa_log_warn("[AUDIODSP]%s is not sound plugin", item);
329 _audio_dsp_plugin_destroy_list(list);
334 static int audio_dsp_plugin_open(char *file)
336 void (*func_change_vol) (pa_volume_t) = NULL;
337 void (*func_set_mute) (int) = NULL;
339 plugin = dlopen(file, RTLD_NOW|RTLD_GLOBAL);
341 if (NULL == plugin) {
342 pa_log_debug("[AUDIODSP]%s", dlerror());
346 func_change_vol = (void (*)(pa_volume_t))dlsym(plugin, "change_dsp_volume");
347 if (NULL == func_change_vol) {
350 pa_log_debug("[AUDIODSP]Cannot find symbol : change_dsp_volume");
354 func_set_mute = (void (*)(int))dlsym(plugin, "set_mute");
355 if (NULL == func_set_mute) {
358 pa_log_debug("[AUDIODSP]Cannot find symbol : set_mute");
362 change_volume = func_change_vol;
363 set_mute = func_set_mute;
368 static void audio_dsp_plugin_close()
376 int pa__init(pa_module *m)
381 m->userdata = u = pa_xnew0(struct userdata, 1);
384 u->sink_input_new_hook_slot =
385 pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_NEW], PA_HOOK_EARLY, (pa_hook_cb_t) sink_input_new_hook_callback, u);
386 u->sink_input_put_hook_slot =
387 pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_PUT], PA_HOOK_EARLY, (pa_hook_cb_t) sink_input_put_hook_callback, u);
388 u->sink_input_put_hook_slot_rt_switch =
389 pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_PUT], PA_HOOK_LATE, (pa_hook_cb_t) sink_input_put_hook_callback_rt_switch, u);
391 u->subscription = pa_subscription_new(u->core, PA_SUBSCRIPTION_MASK_SERVER, subscribe_cb, u);
396 audio_dsp_plugin_scan(plugin_clvdir);
398 #error "plugin_clvdir undefined"
401 pa_log_info("[AUDIODSP]audio dsp module is loaded\n");
405 void pa__done(pa_module *m)
409 if (!(u = m->userdata))
411 if (u->sink_input_new_hook_slot)
412 pa_hook_slot_free(u->sink_input_new_hook_slot);
413 if (u->sink_input_put_hook_slot)
414 pa_hook_slot_free(u->sink_input_put_hook_slot);
415 if (u->sink_input_put_hook_slot_rt_switch)
416 pa_hook_slot_free(u->sink_input_put_hook_slot_rt_switch);
419 pa_subscription_free(u->subscription);
421 audio_dsp_plugin_close();
426 static char* __strcatdup(const char *str1, const char *str2)
430 len = strlen(str1) + strlen(str2) + 1;
431 dest = (char*) malloc(len*sizeof(char));
434 strncpy(dest, str1, len-1);
435 strncat(dest, str2, len-1);