Support external device playback and capture on sink2/source2 84/275684/10
authorJaechul Lee <jcsing.lee@samsung.com>
Fri, 3 Dec 2021 02:15:30 +0000 (11:15 +0900)
committerJaechul Lee <jcsing.lee@samsung.com>
Thu, 23 Jun 2022 07:54:50 +0000 (16:54 +0900)
module-alsa-card tries to use 'device_id' that is from udev but
tizenaudio-sink2/source2 can't load devices with device_id.

This patch makes devices load with device_id and device_string both.

[Version] 15.0.20
[Issue Type] New feature

Change-Id: I2dcd7b4abdc88abacec5ba997050217c0c8bfc48
Signed-off-by: Jaechul Lee <jcsing.lee@samsung.com>
12 files changed:
Makefile.am
packaging/pulseaudio-modules-tizen.spec
src/hal-interface.c
src/hal-interface.h
src/module-tizenaudio-sink2.c
src/module-tizenaudio-source2.c
src/tizenaudio-sink2.c
src/tizenaudio-sink2.h
src/tizenaudio-source2.c
src/tizenaudio-source2.h
src/tizenaudio-util.c [new file with mode: 0644]
src/tizenaudio-util.h [new file with mode: 0644]

index 12098ed..b3d0913 100644 (file)
@@ -31,21 +31,21 @@ MODULE_LDFLAGS = $(AM_LDFLAGS) $(PACORE_LDFLAGS) $(PA_LDFLAGS) -module -disable-
 MODULE_LIBADD = $(AM_LIBADD) $(PACORE_LIBS) $(PA_LIBS)
 
 pulsemodlibexec_LTLIBRARIES = \
-               libhal-interface.la \
-               libprocessor.la \
-               libcommunicator.la \
-               libtizenaudio-util.la \
-               module-tizenaudio-sink.la \
-               module-tizenaudio-source.la \
-               module-tizenaudio-sink2.la \
-               module-tizenaudio-source2.la \
-               module-tizenaudio-policy.la \
-               module-tizenaudio-discover.la \
-               module-tizenaudio-publish.la \
-               module-tizenaudio-echo-cancel.la \
-               module-sound-player.la \
-               module-tone-player.la \
-               module-poweroff.la
+          libhal-interface.la \
+          libprocessor.la \
+          libcommunicator.la \
+          libtizenaudio-util.la \
+          module-tizenaudio-sink.la \
+          module-tizenaudio-source.la \
+          module-tizenaudio-sink2.la \
+          module-tizenaudio-source2.la \
+          module-tizenaudio-policy.la \
+          module-tizenaudio-discover.la \
+          module-tizenaudio-publish.la \
+          module-tizenaudio-echo-cancel.la \
+          module-sound-player.la \
+          module-tone-player.la \
+          module-poweroff.la
 if ENABLE_HALTC
 pulsemodlibexec_LTLIBRARIES += module-tizenaudio-haltc.la
 endif
@@ -75,7 +75,14 @@ module_tizenaudio_source_la_LDFLAGS = $(MODULE_LDFLAGS)
 module_tizenaudio_source_la_LIBADD = $(MODULE_LIBADD) libhal-interface.la
 module_tizenaudio_source_la_CFLAGS = $(MODULE_CFLAGS) -DPA_MODULE_NAME=module_tizenaudio_source
 
-libtizenaudio_util_la_SOURCES = src/tizenaudio-sink2.c src/tizenaudio-sink2.h src/tizenaudio-source2.c src/tizenaudio-source2.h src/echo-cancel/echo-cancel-def.h
+libtizenaudio_util_la_SOURCES = \
+          src/tizenaudio-sink2.c \
+          src/tizenaudio-sink2.h \
+          src/tizenaudio-source2.c \
+          src/tizenaudio-source2.h \
+          src/tizenaudio-util.c \
+          src/tizenaudio-util.h \
+          src/echo-cancel/echo-cancel-def.h
 libtizenaudio_util_la_LDFLAGS = $(AM_LDFLAGS) $(PA_LDFLAGS) -avoid-version
 libtizenaudio_util_la_LIBADD = $(AM_LIBADD) $(PA_LIBS) libhal-interface.la
 libtizenaudio_util_la_CFLAGS = $(MODULE_CFLAGS)
index 5a5282f..cd1cfef 100644 (file)
@@ -2,7 +2,7 @@
 
 Name:             pulseaudio-modules-tizen
 Summary:          Pulseaudio modules for Tizen
-Version:          15.0.19
+Version:          15.0.20
 Release:          0
 Group:            Multimedia/Audio
 License:          LGPL-2.1+
index e108f38..6837508 100644 (file)
@@ -433,11 +433,11 @@ int32_t pa_hal_interface_pcm_recover(pa_hal_interface *h, pcm_handle pcm_h, int
 }
 
 int32_t pa_hal_interface_pcm_get_params(pa_hal_interface *h, pcm_handle pcm_h, uint32_t direction,
-                                       void **sample_spec, uint32_t *period_size, uint32_t *periods) {
+                                       void *sample_spec, uint32_t *period_size, uint32_t *periods) {
     audio_return_e hal_ret = AUDIO_RET_OK;
 
     pa_assert(h);
-    pa_assert(*sample_spec);
+    pa_assert(sample_spec);
     pa_assert(period_size);
     pa_assert(periods);
 
index ebfca2f..800ff6c 100644 (file)
@@ -111,7 +111,7 @@ int32_t pa_hal_interface_pcm_write(pa_hal_interface *h, pcm_handle pcm_h, const
 int32_t pa_hal_interface_pcm_read(pa_hal_interface *h, pcm_handle pcm_h, void *buffer, uint32_t frames);
 int32_t pa_hal_interface_pcm_get_fd(pa_hal_interface *h, pcm_handle pcm_h, int *fd);
 int32_t pa_hal_interface_pcm_recover(pa_hal_interface *h, pcm_handle pcm_h, int err);
-int32_t pa_hal_interface_pcm_get_params(pa_hal_interface *h, pcm_handle pcm_h, uint32_t direction, void **sample_spec, uint32_t *period_size, uint32_t *periods);
+int32_t pa_hal_interface_pcm_get_params(pa_hal_interface *h, pcm_handle pcm_h, uint32_t direction, void *sample_spec, uint32_t *period_size, uint32_t *periods);
 int32_t pa_hal_interface_pcm_set_params(pa_hal_interface *h, pcm_handle pcm_h, uint32_t direction, void *sample_spec, uint32_t period_size, uint32_t periods);
 int32_t pa_hal_interface_add_message_callback(pa_hal_interface *h, hal_message_callback callback, void *user_data);
 int32_t pa_hal_interface_remove_message_callback(pa_hal_interface *h, hal_message_callback callback);
index 0861262..49410e0 100644 (file)
@@ -65,7 +65,7 @@ int pa__init(pa_module *m) {
         goto fail;
     }
 
-    if (!(m->userdata = pa_tizenaudio_sink2_new(m, ma, __FILE__)))
+    if (!(m->userdata = pa_tizenaudio_sink2_new(m, ma, __FILE__, NULL, NULL, NULL, NULL)))
         goto fail;
 
     pa_modargs_free(ma);
index 1df595e..cb821fb 100644 (file)
@@ -65,7 +65,7 @@ int pa__init(pa_module *m) {
         goto fail;
     }
 
-    if (!(m->userdata = pa_tizenaudio_source2_new(m, ma, __FILE__)))
+    if (!(m->userdata = pa_tizenaudio_source2_new(m, ma, __FILE__, NULL, NULL, NULL, NULL)))
         goto fail;
 
     pa_modargs_free(ma);
index a7ffd93..ff0dbc7 100644 (file)
 #include <pulsecore/poll.h>
 
 #include "hal-interface.h"
+#include "tizenaudio-util.h"
 #include "echo-cancel/echo-cancel-def.h"
 
 #define DEFAULT_SINK_NAME "tizenaudio-sink2"
 
 #define DEVICE_NAME_MAX                     30
-#define DEFAULT_FRAGMENT_MSEC               20
+#define DEFAULT_FRAGMENT_MSEC               10
 #define DEFAULT_FRAGMENTS                    4
 
 struct userdata {
@@ -377,59 +378,39 @@ finish:
     pa_log_debug("Thread shutting down");
 }
 
-static int parse_to_get_card(const char *modarg_device, char *card) {
-    const char *name_p;
-    char *card_p;
-
-    if (!strchr(modarg_device, ',')) {
-        pa_log_error("Failed to parse device argument : no comma");
-        return -1;
-    }
-
-    name_p = modarg_device;
-    card_p = card;
-    while (*name_p != ',')
-        *(card_p++) = *(name_p++);
-    *card_p = '\0';
-
-    return 0;
-}
-
-static int parse_to_get_device(const char *modarg_device, char *device) {
-    const char *comma_p;
-    char *device_p;
-
-    if (!(comma_p = strchr(modarg_device, ','))) {
-        pa_log_error("Failed to parse device argument : no comma");
-        return -1;
-    }
-
-    comma_p++;
-    device_p = device;
-    while (*comma_p != '\0')
-        *(device_p++) = *(comma_p++);
-    *device_p = '\0';
-
-    return 0;
-}
-
-pa_sink *pa_tizenaudio_sink2_new(pa_module *m, pa_modargs *ma, const char *driver) {
+pa_sink *pa_tizenaudio_sink2_new(pa_module *m,
+                                    pa_modargs *ma,
+                                    const char *driver,
+                                    pa_card *c,
+                                    char **device_string,
+                                    pa_sample_spec *sample_spec,
+                                    pa_channel_map *channel_map) {
     struct userdata *u = NULL;
     pa_sample_spec ss;
     pa_channel_map map;
     pa_sink_new_data data;
     uint32_t alternate_sample_rate;
     const char *modarg_device;
-    char card[DEVICE_NAME_MAX];
-    char device[DEVICE_NAME_MAX];
+    const char *modarg_device_id;
     size_t frame_size, buffer_size, period_frames, buffer_frames;
+    pa_pcm_params_t param;
+
+    uint32_t period_size;
+    uint32_t periods;
 
     pa_assert(m);
     pa_assert(ma);
 
+    /* TODO: It doesn't need to handle sample_spec, channels_map yet because it comes from UCM */
     ss = m->core->default_sample_spec;
     map = m->core->default_channel_map;
-    if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0) {
+
+    if (channel_map && channel_map->channels != ss.channels)
+        ss.channels = channel_map->channels;
+
+    pa_channel_map_init_extend(&map, ss.channels, PA_CHANNEL_MAP_ALSA);
+
+    if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_ALSA) < 0) {
         pa_log_error("Invalid sample format specification or channel map");
         goto fail;
     }
@@ -449,19 +430,13 @@ pa_sink *pa_tizenaudio_sink2_new(pa_module *m, pa_modargs *ma, const char *drive
     u->rtpoll = pa_rtpoll_new();
     pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll);
 
-    if (!(modarg_device = pa_modargs_get_value(ma, "device", NULL))) {
-        pa_log_error("device is invalid");
+    modarg_device_id = pa_modargs_get_value(ma, "device_id", NULL);
+    modarg_device = pa_modargs_get_value(ma, "device", NULL);
+    if (!modarg_device_id && !modarg_device) {
+        pa_log_error("Failed to get device id or device string");
         goto fail;
     }
 
-    if (parse_to_get_card(modarg_device, card) || parse_to_get_device(modarg_device, device)) {
-        pa_log_error("failed to parse device module argument, %s", modarg_device);
-        goto fail;
-    }
-
-    u->card = pa_xstrdup(card);
-    u->device = pa_xstrdup(device);
-
     u->frag_size = (uint32_t) pa_usec_to_bytes(DEFAULT_FRAGMENT_MSEC * PA_USEC_PER_MSEC, &ss);
     u->nfrags = DEFAULT_FRAGMENTS;
     if (pa_modargs_get_value_u32(ma, "fragment_size", &u->frag_size) < 0 ||
@@ -469,11 +444,83 @@ pa_sink *pa_tizenaudio_sink2_new(pa_module *m, pa_modargs *ma, const char *drive
         pa_log_error("fragment_size or fragments are invalid.");
         goto fail;
     }
-    pa_log_info("card(%s) device(%s) fragment_size(%u), fragments(%u)", u->card, u->device, u->frag_size, u->nfrags);
+
+    frame_size = pa_frame_size(&ss);
+    buffer_size = (size_t)(u->frag_size * u->nfrags);
+    buffer_frames = buffer_size / frame_size;
+    period_frames = u->frag_size / frame_size;
+
+    param.direction = DIRECTION_OUT;
+    param.ss = ss;
+    param.period_size = u->frag_size / pa_frame_size(&ss);
+    param.periods = u->nfrags;
+
+    pa_log_info("Try to open device with rate(%d), channels(%d), format(%s), period_size(%u), periods(%u)",
+                    ss.rate, ss.channels, pa_sample_format_to_string(ss.format), param.period_size, param.periods);
+
+    if (modarg_device_id && device_string) {
+        if (pa_tz_util_pcm_open_by_dev_id(
+                  u->hal_interface,
+                  modarg_device_id,
+                  device_string,
+                  &param,
+                  (void **)&u->pcm_handle,
+                  &u->card,
+                  &u->device)) {
+            pa_log_error("Error opening PCM device");
+            goto fail;
+        }
+    } else if (modarg_device) {
+        if (pa_tz_util_pcm_open_by_device(
+                  u->hal_interface,
+                  modarg_device,
+                  &param,
+                  (void **)&u->pcm_handle,
+                  &u->card,
+                  &u->device)) {
+            pa_log_error("Error opening PCM device");
+            goto fail;
+        }
+    } else {
+        pa_log_error("Device name doesn't exist");
+        goto fail;
+    }
+
+    if (!pa_sample_spec_equal(&ss, &param.ss)) {
+        pa_sample_spec _ss;
+        char requested_spec[PA_SAMPLE_SPEC_SNPRINT_MAX];
+        char selected_spec[PA_SAMPLE_SPEC_SNPRINT_MAX];
+
+        if (pa_hal_interface_pcm_get_params(u->hal_interface, u->pcm_handle, 0, (void *)&_ss, &period_size, &periods) < 0) {
+            pa_log_error("Failed to get pcm params");
+            goto fail;
+        }
+
+        frame_size = pa_frame_size(&param.ss);
+        u->frag_size = period_size * frame_size;
+         /* TODO: can't trust periods value from hal */
+        u->nfrags = param.periods;
+
+        buffer_size = (size_t)(u->frag_size * u->nfrags);
+        buffer_frames = buffer_size / frame_size;
+        period_frames = u->frag_size / frame_size;
+
+        pa_log_info("Device doesn't support sample_spec(%s). It was changed to (%s)",
+                    pa_sample_spec_snprint(requested_spec, PA_SAMPLE_SPEC_SNPRINT_MAX, &ss),
+                    pa_sample_spec_snprint(selected_spec, PA_SAMPLE_SPEC_SNPRINT_MAX, &param.ss));
+
+        ss = param.ss;
+    }
+
+    pa_log_info("Device opened. card(%s), device(%s) rate(%d), channels(%d), format(%s), "
+                "frag_size(%u), nfrags(%u), period_size(%u), periods(%u)",
+                    u->card, u->device, ss.rate, ss.channels, pa_sample_format_to_string(ss.format),
+                    u->frag_size, u->nfrags, param.period_size, param.periods);
 
     pa_sink_new_data_init(&data);
     data.driver = driver;
     data.module = m;
+    data.card = c;
     pa_sink_new_data_set_name(&data, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME));
     pa_sink_new_data_set_sample_spec(&data, &ss);
     pa_sink_new_data_set_channel_map(&data, &map);
@@ -483,14 +530,12 @@ pa_sink *pa_tizenaudio_sink2_new(pa_module *m, pa_modargs *ma, const char *drive
     pa_proplist_sets(data.proplist, "tizen.device", u->device);
     pa_proplist_sets(data.proplist, "tizen.version", "2");
     pa_proplist_sets(data.proplist, PA_PROP_DEVICE_API, "tizen");
-
-    frame_size = pa_frame_size(&ss);
-    buffer_size = (size_t)(u->frag_size * u->nfrags);
-    buffer_frames = buffer_size / frame_size;
-    period_frames = u->frag_size / frame_size;
     pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE, "%zu", buffer_frames * frame_size);
     pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE, "%zu", period_frames * frame_size);
 
+    if (pa_modargs_get_value(ma, "device_id", NULL))
+        pa_proplist_sets(data.proplist, PA_PROP_DEVICE_BUS, "usb");
+
     if (pa_modargs_get_proplist(ma, "sink_properties", data.proplist, PA_UPDATE_REPLACE) < 0) {
         pa_log_error("Invalid properties.");
         pa_sink_new_data_done(&data);
@@ -509,17 +554,7 @@ pa_sink *pa_tizenaudio_sink2_new(pa_module *m, pa_modargs *ma, const char *drive
     u->sink->set_state_in_io_thread = sink_set_state_in_io_thread_cb;
     u->sink->userdata = u;
 
-    if (pa_hal_interface_pcm_open(u->hal_interface,
-              u->card,
-              u->device,
-              DIRECTION_OUT,
-              &u->sink->sample_spec,
-              u->frag_size / pa_frame_size(&u->sink->sample_spec),
-              u->nfrags,
-              (void **)&u->pcm_handle)) {
-        pa_log_error("Error opening PCM device");
-        goto fail;
-    }
+    pa_log_info("card(%s) device(%s) fragment_size(%u), fragments(%u)", u->card, u->device, u->frag_size, u->nfrags);
 
     pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
     pa_sink_set_rtpoll(u->sink, u->rtpoll);
@@ -538,7 +573,9 @@ pa_sink *pa_tizenaudio_sink2_new(pa_module *m, pa_modargs *ma, const char *drive
     return u->sink;
 
 fail:
-    userdata_free(u);
+    if (u)
+        userdata_free(u);
+
     return NULL;
 }
 
index dd9e9e2..854ea9a 100644 (file)
 #include <pulsecore/module.h>
 #include <pulsecore/modargs.h>
 #include <pulsecore/sink.h>
-
-pa_sink *pa_tizenaudio_sink2_new(pa_module *m, pa_modargs *ma, const char *driver);
+#include <pulse/channelmap.h>
+#include <pulse/sample.h>
+
+pa_sink *pa_tizenaudio_sink2_new(pa_module *m,
+                                    pa_modargs *ma,
+                                    const char *driver,
+                                    pa_card *c,
+                                    char **device_string,
+                                    pa_sample_spec *sample_spec,
+                                    pa_channel_map *channel_map);
 
 void pa_tizenaudio_sink2_free(pa_sink *s);
 
index 8ddfa2f..846b086 100644 (file)
 #include <pulsecore/poll.h>
 
 #include "hal-interface.h"
+#include "tizenaudio-util.h"
 #include "echo-cancel/echo-cancel-def.h"
 
 #define DEFAULT_SOURCE_NAME "tizenaudio-source2"
 
 #define DEVICE_NAME_MAX                     30
-#define DEFAULT_FRAGMENT_MSEC               20
+#define DEFAULT_FRAGMENT_MSEC               10
 #define DEFAULT_FRAGMENTS                    4
 
 struct userdata {
@@ -382,58 +383,39 @@ finish:
     pa_log_debug("Thread shutting down");
 }
 
-static int parse_to_get_card(const char *modarg_device, char *card) {
-    const char *name_p;
-    char *card_p;
-
-    if (!strchr(modarg_device, ',')) {
-        pa_log_error("Failed to parse device argument : no comma");
-        return -1;
-    }
-
-    name_p = modarg_device;
-    card_p = card;
-    while (*name_p != ',')
-        *(card_p++) = *(name_p++);
-    *card_p = '\0';
-
-    return 0;
-}
-
-static int parse_to_get_device(const char *modarg_device, char *device) {
-    const char *comma_p;
-    char *device_p;
-
-    if (!(comma_p = strchr(modarg_device, ','))) {
-        pa_log_error("Failed to parse device argument : no comma");
-        return -1;
-    }
-
-    comma_p++;
-    device_p = device;
-    while (*comma_p != '\0')
-        *(device_p++) = *(comma_p++);
-    *device_p = '\0';
-
-    return 0;
-}
-
-pa_source *pa_tizenaudio_source2_new(pa_module *m, pa_modargs *ma, const char *driver) {
+pa_source *pa_tizenaudio_source2_new(pa_module *m,
+                                        pa_modargs *ma,
+                                        const char *driver,
+                                        pa_card *c,
+                                        char **device_string,
+                                        pa_sample_spec *sample_spec,
+                                        pa_channel_map *channel_map) {
     struct userdata *u = NULL;
     pa_sample_spec ss;
     pa_channel_map map;
     pa_source_new_data data;
     uint32_t alternate_sample_rate;
     const char *modarg_device;
-    char card[DEVICE_NAME_MAX];
-    char device[DEVICE_NAME_MAX];
+    const char *modarg_device_id;
     size_t frame_size, buffer_size, period_frames, buffer_frames;
+    pa_pcm_params_t param;
+
+    uint32_t period_size;
+    uint32_t periods;
 
     pa_assert(m);
+    pa_assert(ma);
 
+    /* TODO: It doesn't need to handle sample_spec, channels_map yet because it comes from UCM */
     ss = m->core->default_sample_spec;
     map = m->core->default_channel_map;
-    if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0) {
+
+    if (channel_map && channel_map->channels != ss.channels)
+        ss.channels = channel_map->channels;
+
+    pa_channel_map_init_extend(&map, ss.channels, PA_CHANNEL_MAP_ALSA);
+
+    if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_ALSA) < 0) {
         pa_log_error("Invalid sample format specification or channel map");
         goto fail;
     }
@@ -452,19 +434,13 @@ pa_source *pa_tizenaudio_source2_new(pa_module *m, pa_modargs *ma, const char *d
     u->rtpoll = pa_rtpoll_new();
     pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll);
 
-    if (!(modarg_device = pa_modargs_get_value(ma, "device", NULL))) {
-        pa_log_error("device is invalid");
+    modarg_device_id = pa_modargs_get_value(ma, "device_id", NULL);
+    modarg_device = pa_modargs_get_value(ma, "device", NULL);
+    if (!modarg_device_id && !modarg_device) {
+        pa_log_error("Failed to get device id or device string");
         goto fail;
     }
 
-    if (parse_to_get_card(modarg_device, card) || parse_to_get_device(modarg_device, device)) {
-        pa_log_error("failed to parse device module argument, %s", modarg_device);
-        goto fail;
-    }
-
-    u->card = pa_xstrdup(card);
-    u->device = pa_xstrdup(device);
-
     u->frag_size = (uint32_t) pa_usec_to_bytes(DEFAULT_FRAGMENT_MSEC * PA_USEC_PER_MSEC, &ss);
     u->nfrags = DEFAULT_FRAGMENTS;
     if (pa_modargs_get_value_u32(ma, "fragment_size", &u->frag_size) < 0 ||
@@ -472,11 +448,83 @@ pa_source *pa_tizenaudio_source2_new(pa_module *m, pa_modargs *ma, const char *d
         pa_log_error("fragment_size or fragments are invalid.");
         goto fail;
     }
-    pa_log_info("card(%s) device(%s) fragment_size(%u), fragments(%u)", u->card, u->device, u->frag_size, u->nfrags);
+
+    frame_size = pa_frame_size(&ss);
+    buffer_size = (size_t)(u->frag_size * u->nfrags);
+    buffer_frames = buffer_size / frame_size;
+    period_frames = u->frag_size / frame_size;
+
+    param.direction = DIRECTION_IN;
+    param.ss = ss;
+    param.period_size = u->frag_size / pa_frame_size(&ss);
+    param.periods = u->nfrags;
+
+    pa_log_info("Try to open device with rate(%d), channels(%d), format(%s), period_size(%u), periods(%u)",
+                    ss.rate, ss.channels, pa_sample_format_to_string(ss.format), param.period_size, param.periods);
+
+    if (modarg_device_id && device_string) {
+        if (pa_tz_util_pcm_open_by_dev_id(
+                  u->hal_interface,
+                  modarg_device_id,
+                  device_string,
+                  &param,
+                  (void **)&u->pcm_handle,
+                  &u->card,
+                  &u->device)) {
+            pa_log_error("Error opening PCM device");
+            goto fail;
+        }
+    } else if (modarg_device) {
+        if (pa_tz_util_pcm_open_by_device(
+                  u->hal_interface,
+                  modarg_device,
+                  &param,
+                  (void **)&u->pcm_handle,
+                  &u->card,
+                  &u->device)) {
+            pa_log_error("Error opening PCM device");
+            goto fail;
+        }
+    } else {
+        pa_log_error("Device name doesn't exist");
+        goto fail;
+    }
+
+    if (!pa_sample_spec_equal(&ss, &param.ss)) {
+        pa_sample_spec _ss;
+        char requested_spec[PA_SAMPLE_SPEC_SNPRINT_MAX];
+        char selected_spec[PA_SAMPLE_SPEC_SNPRINT_MAX];
+
+        if (pa_hal_interface_pcm_get_params(u->hal_interface, u->pcm_handle, 0, (void *)&_ss, &period_size, &periods) < 0) {
+            pa_log_error("Failed to get pcm params");
+            goto fail;
+        }
+
+        frame_size = pa_frame_size(&param.ss);
+        u->frag_size = period_size * frame_size;
+         /* TODO: can't trust periods value from hal */
+        u->nfrags = param.periods;
+
+        buffer_size = (size_t)(u->frag_size * u->nfrags);
+        buffer_frames = buffer_size / frame_size;
+        period_frames = u->frag_size / frame_size;
+
+        pa_log_info("Device doesn't support sample_spec(%s). It was changed to (%s)",
+                    pa_sample_spec_snprint(requested_spec, PA_SAMPLE_SPEC_SNPRINT_MAX, &ss),
+                    pa_sample_spec_snprint(selected_spec, PA_SAMPLE_SPEC_SNPRINT_MAX, &param.ss));
+
+        ss = param.ss;
+    }
+
+    pa_log_info("Device opened. card(%s), device(%s) rate(%d), channels(%d), format(%s), "
+                "frag_size(%u), nfrags(%u), period_size(%u), periods(%u)",
+                    u->card, u->device, ss.rate, ss.channels, pa_sample_format_to_string(ss.format),
+                    u->frag_size, u->nfrags, period_size, param.periods);
 
     pa_source_new_data_init(&data);
     data.driver = driver;
     data.module = m;
+    data.card = c;
     pa_source_new_data_set_name(&data, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME));
     pa_source_new_data_set_sample_spec(&data, &ss);
     pa_source_new_data_set_channel_map(&data, &map);
@@ -486,14 +534,12 @@ pa_source *pa_tizenaudio_source2_new(pa_module *m, pa_modargs *ma, const char *d
     pa_proplist_sets(data.proplist, "tizen.device", u->device);
     pa_proplist_sets(data.proplist, "tizen.version", "2");
     pa_proplist_sets(data.proplist, PA_PROP_DEVICE_API, "tizen");
-
-    frame_size = pa_frame_size(&ss);
-    buffer_size = (size_t)(u->frag_size * u->nfrags);
-    buffer_frames = buffer_size / frame_size;
-    period_frames = u->frag_size / frame_size;
     pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE, "%zu", buffer_frames * frame_size);
     pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE, "%zu", period_frames * frame_size);
 
+    if (pa_modargs_get_value(ma, "device_id", NULL))
+        pa_proplist_sets(data.proplist, PA_PROP_DEVICE_BUS, "usb");
+
     if (pa_modargs_get_proplist(ma, "source_properties", data.proplist, PA_UPDATE_REPLACE) < 0) {
         pa_log_error("Invalid properties.");
         pa_source_new_data_done(&data);
@@ -512,17 +558,8 @@ pa_source *pa_tizenaudio_source2_new(pa_module *m, pa_modargs *ma, const char *d
     u->source->set_state_in_io_thread = source_set_state_in_io_thread_cb;
     u->source->userdata = u;
 
-    if (pa_hal_interface_pcm_open(u->hal_interface,
-              u->card,
-              u->device,
-              DIRECTION_IN,
-              &u->source->sample_spec,
-              u->frag_size / pa_frame_size(&u->source->sample_spec),
-              u->nfrags,
-              (void **)&u->pcm_handle)) {
-        pa_log_error("Error opening PCM device");
-        goto fail;
-    }
+    pa_log_info("card(%s) device(%s) fragment_size(%u), fragments(%u)",
+                                 u->card, u->device, u->frag_size, u->nfrags);
 
     pa_source_set_asyncmsgq(u->source, u->thread_mq.inq);
     pa_source_set_rtpoll(u->source, u->rtpoll);
@@ -540,7 +577,9 @@ pa_source *pa_tizenaudio_source2_new(pa_module *m, pa_modargs *ma, const char *d
     return u->source;
 
 fail:
-    userdata_free(u);
+    if (u)
+        userdata_free(u);
+
     return NULL;
 }
 
index a8e67c0..c1cdcae 100644 (file)
 #include <pulsecore/module.h>
 #include <pulsecore/modargs.h>
 #include <pulsecore/source.h>
-
-pa_sink *pa_tizenaudio_source2_new(pa_module *m, pa_modargs *ma, const char *driver);
+#include <pulse/channelmap.h>
+#include <pulse/sample.h>
+
+pa_source *pa_tizenaudio_source2_new(pa_module *m,
+                                        pa_modargs *ma,
+                                        const char *driver,
+                                        pa_card *c,
+                                        char **device_string,
+                                        pa_sample_spec *sample_spec,
+                                        pa_channel_map *channel_map);
 
 void pa_tizenaudio_source2_free(pa_source *s);
 
diff --git a/src/tizenaudio-util.c b/src/tizenaudio-util.c
new file mode 100644 (file)
index 0000000..17be479
--- /dev/null
@@ -0,0 +1,217 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright (c) 2022 Samsung Electronics Co., Ltd. All rights reserved.
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/log.h>
+
+#include "hal-interface.h"
+#include "tizenaudio-util.h"
+
+#define CARD_NAME_MAX 32
+#define DEVICE_NAME_MAX 32
+
+/*
+ * Extract card string from the device_string that contains device name like 'hw:1,2'
+ * card variable will have '1' after calling this function.
+ */
+static int parse_to_get_card(const char *device_string, char *card, int n, const char *def) {
+    char *name_p;
+    char *card_p;
+    char *sep;
+    int i;
+
+    /* device_string contains various device name such as 'ALSA,0', 'hw:0,0" and "hw:1,1,1" */
+    if (!(sep = strchr(device_string, ':'))) {
+
+        /* It doesn't have a prefix like 'hw:', 'hdmi:' */
+        if ((sep = strchr(device_string, ','))) {
+            size_t size = sep - device_string;
+
+            memcpy(card, device_string, size);
+            card[size] = '\0';
+
+            return 0;
+        }
+
+        /* Can't find card number. Use default */
+        if (!def)
+            return -1;
+
+        snprintf(card, n, "%s", def);
+    } else {
+        name_p = ++sep;
+        card_p = card;
+
+        for (i = 0; i < n; i++) {
+            if (*name_p == ',' || *name_p == '\0')
+                break;
+
+            *(card_p++) = *(name_p++);
+        }
+
+        *card_p = '\0';
+    }
+
+    return 0;
+}
+
+/*
+ * Extract card string from the device_string that contains device name like 'hw:1,2'
+ * card variable will have '2' after calling this function.
+ */
+static int parse_to_get_device(const char *device_string, char *device, int n, const char *def) {
+    char *device_p;
+    char *sep;
+
+    if (!(sep = strchr(device_string, ','))) {
+        if (!def)
+            return -1;
+
+        n = snprintf(device, n, "%s", def);
+    } else {
+        sep++;
+        device_p = device;
+
+        while (*sep != '\0')
+            *(device_p++) = *(sep++);
+
+        *device_p = '\0';
+    }
+
+    return 0;
+}
+
+int32_t pa_hal_interface_pcm_open_by_dev_id(pa_hal_interface *h,
+                                            const char *dev_id,
+                                            char **device_string,
+                                            pa_pcm_params_t *param,
+                                            pcm_handle *pcm_h,
+                                            char **card,
+                                            char **device) {
+    char **i;
+    char _card[DEVICE_NAME_MAX];
+    char _device[DEVICE_NAME_MAX];
+
+    pa_assert(dev_id);
+    pa_assert(device_string);
+    pa_assert(device_string[0]);
+
+    pa_assert(card);
+    pa_assert(device);
+
+    for (i = device_string; *i; i++) {
+        char *d;
+
+        pa_log_debug("Try to open device(%s), id(%s)", *i, dev_id);
+
+        if (!(d = pa_replace(*i, "%f", dev_id))) {
+            pa_log_error("Error parsing i(%s) dev_id(%s)", *i, dev_id);
+            continue;
+        }
+
+        if (parse_to_get_device(d, _device, DEVICE_NAME_MAX, "0") < 0) {
+            pa_log_error("Failed to parse device_string. It doesn't contain device");
+            pa_xfree(d);
+            continue;
+        }
+
+        if (parse_to_get_card(d, _card, CARD_NAME_MAX, "0") < 0) {
+            pa_log_error("Failed to parse card");
+            pa_xfree(d);
+            continue;
+        }
+
+        if (pa_hal_interface_pcm_open(h,
+                    _card,
+                    _device,
+                    param->direction,
+                    &param->ss,
+                    param->period_size,
+                    param->periods,
+                    (void **)pcm_h)) {
+            pa_log_error("Failed to open PCM device (%s)", d);
+            pa_xfree(d);
+            continue;
+        }
+
+        *card = pa_xstrdup(_card);
+        *device = pa_xstrdup(_device);
+
+        pa_log_debug("card(%s) device(%s) opened successfully", *card, *device);
+
+        pa_xfree(d);
+
+        return 0;
+    }
+
+    pa_log_error("Error opening PCM device");
+
+    return -1;
+}
+
+int32_t pa_hal_interface_pcm_open_by_device(pa_hal_interface *h,
+                                            const char* dev,
+                                            pa_pcm_params_t *param,
+                                            pcm_handle *pcm_h,
+                                            char **card,
+                                            char **device) {
+    char _card[DEVICE_NAME_MAX];
+    char _device[DEVICE_NAME_MAX];
+
+    pa_assert(dev);
+
+    pa_log_debug("Try to open device(%s)", dev);
+
+    if (parse_to_get_device(dev, _device, DEVICE_NAME_MAX, NULL) < 0) {
+        pa_log_error("Failed to parse device. dev(%s)", dev);
+        return -1;
+    }
+
+    if (parse_to_get_card(dev, _card, CARD_NAME_MAX, NULL) < 0) {
+        pa_log_error("Failed to parse card. dev(%s)", dev);
+        return -1;
+    }
+
+    if (pa_hal_interface_pcm_open(h,
+                _card,
+                _device,
+                param->direction,
+                &param->ss,
+                param->period_size,
+                param->periods,
+                (void **)pcm_h)) {
+        pa_log_error("Failed to open PCM device (%s) card(%s), device(%s)", dev, _card, _device);
+        return -1;
+    }
+
+    *card = pa_xstrdup(_card);
+    *device = pa_xstrdup(_device);
+
+    pa_log_debug("card(%s) device(%s) opened successfully", *card, *device);
+
+    return 0;
+}
diff --git a/src/tizenaudio-util.h b/src/tizenaudio-util.h
new file mode 100644 (file)
index 0000000..ea74fb9
--- /dev/null
@@ -0,0 +1,54 @@
+#ifndef footizenaudioutilhfoo
+#define footizenaudioutilhfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright (c) 2022 Samsung Electronics Co., Ltd. All rights reserved.
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulse/sample.h>
+
+typedef struct pa_pcm_params {
+    io_direction_t direction;
+    pa_sample_spec ss;
+    uint32_t period_size;
+    uint32_t periods;
+} pa_pcm_params_t;
+
+
+int32_t pa_tz_util_pcm_open_by_dev_id(pa_hal_interface *h,
+                                            const char *dev_id,
+                                            char **device_string,
+                                            pa_pcm_params_t *param,
+                                            pcm_handle *pcm_h,
+                                            char **card,
+                                            char **device);
+
+int32_t pa_tz_util_pcm_open_by_device(pa_hal_interface *h,
+                                            const char* dev,
+                                            pa_pcm_params_t *param,
+                                            pcm_handle *pcm_h,
+                                            char **card,
+                                            char **device);
+#endif
+