| /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
| #include <string.h> |
| #include <errno.h> |
| |
| #include "internal.h" |
| #include "snd_tscm.h" |
| #include "hinawa_sigs_marshal.h" |
| |
| /** |
| * SECTION:snd_tscm |
| * @Title: HinawaSndTscm |
| * @Short_description: A state reader for Tascam FireWire models |
| * @include: snd_tscm.h |
| * |
| * A #HinawaSndTscm is an application of protocol defined by TASCAM. This |
| * inherits #HinawaSndUnit. |
| */ |
| |
| struct _HinawaSndTscmPrivate { |
| struct snd_firewire_tascam_state image; |
| guint32 state[SNDRV_FIREWIRE_TASCAM_STATE_COUNT]; |
| }; |
| G_DEFINE_TYPE_WITH_PRIVATE(HinawaSndTscm, hinawa_snd_tscm, HINAWA_TYPE_SND_UNIT) |
| |
| /* This object has one signal. */ |
| enum tscm_sig_type { |
| TSCM_SIG_TYPE_CTL, |
| TSCM_SIG_TYPE_COUNT, |
| }; |
| static guint tscm_sigs[TSCM_SIG_TYPE_COUNT] = { 0 }; |
| |
| static void hinawa_snd_tscm_class_init(HinawaSndTscmClass *klass) |
| { |
| /** |
| * HinawaSndTscm::control: |
| * @self: A #HinawaSndTscm |
| * @index: the numerical index on image of status and control info. |
| * @before: the value of info before changed. |
| * @after: the value of info after changed. |
| * |
| * When TASCAM FireWire unit transfer control message, the #HinawaSndTscm::control |
| * signal is emitted. |
| */ |
| tscm_sigs[TSCM_SIG_TYPE_CTL] = |
| g_signal_new("control", |
| G_OBJECT_CLASS_TYPE(klass), |
| G_SIGNAL_RUN_LAST, |
| G_STRUCT_OFFSET(HinawaSndTscmClass, control), |
| NULL, NULL, |
| hinawa_sigs_marshal_VOID__UINT_UINT_UINT, |
| G_TYPE_NONE, |
| 3, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT); |
| } |
| |
| static void hinawa_snd_tscm_init(HinawaSndTscm *self) |
| { |
| return; |
| } |
| |
| /** |
| * hinawa_snd_tscm_new: |
| * |
| * Instantiate #HinawaSndTscm object and return the instance. |
| * |
| * Returns: an instance of #HinawaSndTscm. |
| * Since: 1.3. |
| */ |
| HinawaSndTscm *hinawa_snd_tscm_new(void) |
| { |
| return g_object_new(HINAWA_TYPE_SND_TSCM, NULL); |
| } |
| |
| /** |
| * hinawa_snd_tscm_open: |
| * @self: A #HinawaSndUnit |
| * @path: A full path of a special file for ALSA hwdep character device |
| * @exception: A #GError. Error can be generated with three domains; #g_file_error_quark(), |
| * #hinawa_fw_node_error_quark(), and #hinawa_snd_unit_error_quark(). |
| * |
| * Open ALSA hwdep character device and check it for Dg00x devices. |
| */ |
| void hinawa_snd_tscm_open(HinawaSndTscm *self, gchar *path, GError **exception) |
| { |
| g_return_if_fail(HINAWA_IS_SND_TSCM(self)); |
| g_return_if_fail(path != NULL && strlen(path) > 0); |
| g_return_if_fail(exception == NULL || *exception == NULL); |
| |
| hinawa_snd_unit_open(&self->parent_instance, path, exception); |
| } |
| |
| /** |
| * hinawa_snd_tscm_get_state: |
| * @self: A #HinawaSndTscm |
| * @exception: A #GError. Error can be generated with domain of #hinawa_snd_unit_error_quark(). |
| * |
| * Get the latest states of target device. |
| * |
| * Returns: (element-type guint32) (array fixed-size=64) (transfer none): state |
| * image. |
| * |
| */ |
| const guint32 *hinawa_snd_tscm_get_state(HinawaSndTscm *self, |
| GError **exception) |
| { |
| HinawaSndTscmPrivate *priv; |
| int i; |
| |
| g_return_val_if_fail(HINAWA_IS_SND_TSCM(self), NULL); |
| g_return_val_if_fail(exception == NULL || *exception == NULL, NULL); |
| |
| priv = hinawa_snd_tscm_get_instance_private(self); |
| |
| hinawa_snd_unit_ioctl(&self->parent_instance, |
| SNDRV_FIREWIRE_IOCTL_TASCAM_STATE, &priv->image, |
| exception); |
| if (*exception != NULL) |
| return NULL; |
| |
| for (i = 0; i < SNDRV_FIREWIRE_TASCAM_STATE_COUNT; ++i) |
| priv->state[i] = GUINT32_FROM_BE(priv->image.data[i]); |
| return priv->state; |
| } |
| |
| void hinawa_snd_tscm_handle_control(HinawaSndTscm *self, const void *buf, |
| ssize_t len) |
| { |
| const struct snd_firewire_event_tascam_control *event = |
| (struct snd_firewire_event_tascam_control *)buf; |
| const struct snd_firewire_tascam_change *change; |
| |
| g_return_if_fail(HINAWA_IS_SND_TSCM(self)); |
| |
| if (len < sizeof(event->type)) |
| return; |
| len -= sizeof(event->type); |
| |
| change = event->changes; |
| while (len >= sizeof(*change)) { |
| g_signal_emit(self, tscm_sigs[TSCM_SIG_TYPE_CTL], 0, |
| change->index, |
| GUINT32_FROM_BE(change->before), |
| GUINT32_FROM_BE(change->after)); |
| ++change; |
| len -= sizeof(*change); |
| } |
| } |