From c7a0708a236210523e241fa436e2cb8b33395746 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Wed, 5 Jul 2006 17:42:16 +0200 Subject: [PATCH] Control API - add TLV support snd_ctl_elem_tlv_read snd_ctl_elem_tlv_write snd_ctl_elem_tlv_command snd_ctl_elem_info_is_tlv_readable snd_ctl_elem_info_is_tlv_writable snd_ctl_elem_info_is_tlv_commandable snd_hctl_elem_tlv_read snd_hctl_elem_tlv_write snd_hctl_elem_tlv_command --- include/control.h | 21 +++++++- include/sound/asound.h | 16 ++++++- src/Versions | 14 ++++++ src/control/control.c | 114 +++++++++++++++++++++++++++++++++++++++++++- src/control/control_hw.c | 34 +++++++++++++ src/control/control_local.h | 2 + src/control/hcontrol.c | 51 +++++++++++++++++++- 7 files changed, 248 insertions(+), 4 deletions(-) diff --git a/include/control.h b/include/control.h index 28c1fc8f..af0f91a6 100644 --- a/include/control.h +++ b/include/control.h @@ -122,6 +122,8 @@ typedef enum _snd_ctl_event_type { #define SND_CTL_EVENT_MASK_INFO (1<<1) /** Element has been added \hideinitializer */ #define SND_CTL_EVENT_MASK_ADD (1<<2) +/** Element's TLV value has been changed \hideinitializer */ +#define SND_CTL_EVENT_MASK_TLV (1<<3) /** CTL name helper */ #define SND_CTL_NAME_NONE "" @@ -164,6 +166,11 @@ typedef enum _snd_ctl_event_type { /** ACPI/PCI Power State D3cold */ #define SND_CTL_POWER_D3cold (SND_CTL_POWER_D3|0x0001) +/** TLV type - Container */ +#define SND_CTL_TLVT_CONTAINER 0x0000 +/** TLV type - basic dB scale */ +#define SND_CTL_TLVT_DB_SCALE 0x0001 + /** CTL type */ typedef enum _snd_ctl_type { /** Kernel level CTL */ @@ -212,12 +219,18 @@ int snd_ctl_poll_descriptors(snd_ctl_t *ctl, struct pollfd *pfds, unsigned int s int snd_ctl_poll_descriptors_revents(snd_ctl_t *ctl, struct pollfd *pfds, unsigned int nfds, unsigned short *revents); int snd_ctl_subscribe_events(snd_ctl_t *ctl, int subscribe); int snd_ctl_card_info(snd_ctl_t *ctl, snd_ctl_card_info_t *info); -int snd_ctl_elem_list(snd_ctl_t *ctl, snd_ctl_elem_list_t * list); +int snd_ctl_elem_list(snd_ctl_t *ctl, snd_ctl_elem_list_t *list); int snd_ctl_elem_info(snd_ctl_t *ctl, snd_ctl_elem_info_t *info); int snd_ctl_elem_read(snd_ctl_t *ctl, snd_ctl_elem_value_t *value); int snd_ctl_elem_write(snd_ctl_t *ctl, snd_ctl_elem_value_t *value); int snd_ctl_elem_lock(snd_ctl_t *ctl, snd_ctl_elem_id_t *id); int snd_ctl_elem_unlock(snd_ctl_t *ctl, snd_ctl_elem_id_t *id); +int snd_ctl_elem_tlv_read(snd_ctl_t *ctl, const snd_ctl_elem_id_t *id, + unsigned int *tlv, unsigned int tlv_size); +int snd_ctl_elem_tlv_write(snd_ctl_t *ctl, const snd_ctl_elem_id_t *id, + const unsigned int *tlv); +int snd_ctl_elem_tlv_command(snd_ctl_t *ctl, const snd_ctl_elem_id_t *id, + const unsigned int *tlv); int snd_ctl_hwdep_next_device(snd_ctl_t *ctl, int * device); int snd_ctl_hwdep_info(snd_ctl_t *ctl, snd_hwdep_info_t * info); int snd_ctl_pcm_next_device(snd_ctl_t *ctl, int *device); @@ -340,6 +353,9 @@ int snd_ctl_elem_info_is_writable(const snd_ctl_elem_info_t *obj); int snd_ctl_elem_info_is_volatile(const snd_ctl_elem_info_t *obj); int snd_ctl_elem_info_is_inactive(const snd_ctl_elem_info_t *obj); int snd_ctl_elem_info_is_locked(const snd_ctl_elem_info_t *obj); +int snd_ctl_elem_info_is_tlv_readable(const snd_ctl_elem_info_t *obj); +int snd_ctl_elem_info_is_tlv_writable(const snd_ctl_elem_info_t *obj); +int snd_ctl_elem_info_is_tlv_commandable(const snd_ctl_elem_info_t *obj); int snd_ctl_elem_info_is_owner(const snd_ctl_elem_info_t *obj); int snd_ctl_elem_info_is_user(const snd_ctl_elem_info_t *obj); pid_t snd_ctl_elem_info_get_owner(const snd_ctl_elem_info_t *obj); @@ -485,6 +501,9 @@ snd_hctl_elem_t *snd_hctl_elem_prev(snd_hctl_elem_t *elem); int snd_hctl_elem_info(snd_hctl_elem_t *elem, snd_ctl_elem_info_t * info); int snd_hctl_elem_read(snd_hctl_elem_t *elem, snd_ctl_elem_value_t * value); int snd_hctl_elem_write(snd_hctl_elem_t *elem, snd_ctl_elem_value_t * value); +int snd_hctl_elem_tlv_read(snd_hctl_elem_t *elem, unsigned int *tlv, unsigned int tlv_size); +int snd_hctl_elem_tlv_write(snd_hctl_elem_t *elem, const unsigned int *tlv); +int snd_hctl_elem_tlv_command(snd_hctl_elem_t *elem, const unsigned int *tlv); snd_hctl_t *snd_hctl_elem_get_hctl(snd_hctl_elem_t *elem); diff --git a/include/sound/asound.h b/include/sound/asound.h index ae5fcd49..412029e7 100644 --- a/include/sound/asound.h +++ b/include/sound/asound.h @@ -756,10 +756,15 @@ enum sndrv_ctl_elem_iface { #define SNDRV_CTL_ELEM_ACCESS_WRITE (1<<1) #define SNDRV_CTL_ELEM_ACCESS_READWRITE (SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE) #define SNDRV_CTL_ELEM_ACCESS_VOLATILE (1<<2) /* control value may be changed without a notification */ -#define SNDRV_CTL_ELEM_ACCESS_TIMESTAMP (1<<2) /* when was control changed */ +#define SNDRV_CTL_ELEM_ACCESS_TIMESTAMP (1<<3) /* when was control changed */ +#define SNDRV_CTL_ELEM_ACCESS_TLV_READ (1<<4) /* TLV read is supported */ +#define SNDRV_CTL_ELEM_ACCESS_TLV_WRITE (1<<5) /* TLV write is supported */ +#define SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE (SNDRV_CTL_ELEM_ACCESS_TLV_READ|SNDRV_CTL_ELEM_ACCESS_TLV_WRITE) +#define SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND (1<<6) /* TLV command is possible */ #define SNDRV_CTL_ELEM_ACCESS_INACTIVE (1<<8) /* control does actually nothing, but may be updated */ #define SNDRV_CTL_ELEM_ACCESS_LOCK (1<<9) /* write lock */ #define SNDRV_CTL_ELEM_ACCESS_OWNER (1<<10) /* write lock owner */ +#define SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK (1<<28) /* flag only for kernel */ #define SNDRV_CTL_ELEM_ACCESS_USER (1<<29) /* user space element */ #define SNDRV_CTL_ELEM_ACCESS_DINDIRECT (1<<30) /* indirect access for matrix dimensions in the info structure */ #define SNDRV_CTL_ELEM_ACCESS_INDIRECT (1<<31) /* indirect access for element value in the value structure */ @@ -847,6 +852,12 @@ struct sndrv_ctl_elem_value { unsigned char reserved[128-sizeof(struct timespec)]; }; +struct sndrv_ctl_tlv { + unsigned int numid; /* control element numeric identification */ + unsigned int length; /* in bytes aligned to 4 */ + unsigned int tlv[0]; /* first TLV */ +}; + enum { SNDRV_CTL_IOCTL_PVERSION = _IOR('U', 0x00, int), SNDRV_CTL_IOCTL_CARD_INFO = _IOR('U', 0x01, struct sndrv_ctl_card_info), @@ -860,6 +871,9 @@ enum { SNDRV_CTL_IOCTL_ELEM_ADD = _IOWR('U', 0x17, struct sndrv_ctl_elem_info), SNDRV_CTL_IOCTL_ELEM_REPLACE = _IOWR('U', 0x18, struct sndrv_ctl_elem_info), SNDRV_CTL_IOCTL_ELEM_REMOVE = _IOWR('U', 0x19, struct sndrv_ctl_elem_id), + SNDRV_CTL_IOCTL_TLV_READ = _IOWR('U', 0x1a, struct sndrv_ctl_tlv), + SNDRV_CTL_IOCTL_TLV_WRITE = _IOWR('U', 0x1b, struct sndrv_ctl_tlv), + SNDRV_CTL_IOCTL_TLV_COMMAND = _IOWR('U', 0x1c, struct sndrv_ctl_tlv), SNDRV_CTL_IOCTL_HWDEP_NEXT_DEVICE = _IOWR('U', 0x20, int), SNDRV_CTL_IOCTL_HWDEP_INFO = _IOR('U', 0x21, struct sndrv_hwdep_info), SNDRV_CTL_IOCTL_PCM_NEXT_DEVICE = _IOR('U', 0x30, int), diff --git a/src/Versions b/src/Versions index cb4cc229..ad85b9b4 100644 --- a/src/Versions +++ b/src/Versions @@ -274,3 +274,17 @@ ALSA_1.0.11 { snd_pcm_set_params; snd_pcm_get_params; } ALSA_1.0.10; + +ALSA_1.0.12 { + global: + + snd_ctl_elem_tlv_read; + snd_ctl_elem_tlv_write; + snd_ctl_elem_tlv_command; + snd_ctl_elem_info_is_tlv_readable; + snd_ctl_elem_info_is_tlv_writable; + snd_ctl_elem_info_is_tlv_commandable; + snd_hctl_elem_tlv_read; + snd_hctl_elem_tlv_write; + snd_hctl_elem_tlv_command; +} ALSA_1.0.11; diff --git a/src/control/control.c b/src/control/control.c index 4bf96e57..d0707af7 100644 --- a/src/control/control.c +++ b/src/control/control.c @@ -404,7 +404,9 @@ int snd_ctl_elem_read(snd_ctl_t *ctl, snd_ctl_elem_value_t *control) * \brief Set CTL element value * \param ctl CTL handle * \param control CTL element id/value pointer - * \return 0 on success otherwise a negative error code + * \retval 0 on success + * \retval >0 on success when value was changed + * \retval <0 a negative error code */ int snd_ctl_elem_write(snd_ctl_t *ctl, snd_ctl_elem_value_t *control) { @@ -412,6 +414,83 @@ int snd_ctl_elem_write(snd_ctl_t *ctl, snd_ctl_elem_value_t *control) return ctl->ops->element_write(ctl, control); } +static int snd_ctl_tlv_do(snd_ctl_t *ctl, int op_flag, + const snd_ctl_elem_id_t *id, + unsigned int *tlv, unsigned int tlv_size) +{ + snd_ctl_elem_info_t *info = NULL; + int err; + + if (id->numid == 0) { + info = calloc(1, sizeof(*info)); + if (info == NULL) + return -ENOMEM; + info->id = *id; + id = &info->id; + err = snd_ctl_elem_info(ctl, info); + if (err < 0) + goto __err; + if (id->numid == 0) { + err = -ENOENT; + goto __err; + } + } + err = ctl->ops->element_tlv(ctl, op_flag, id->numid, tlv, tlv_size); + __err: + if (info) + free(info); + return err; +} + + + +/** + * \brief Get CTL element TLV value + * \param ctl CTL handle + * \param id CTL element id pointer + * \param tlv TLV array pointer to store + * \param tlv_size TLV array size in bytes + * \return 0 on success otherwise a negative error code + */ +int snd_ctl_elem_tlv_read(snd_ctl_t *ctl, const snd_ctl_elem_id_t *id, + unsigned int *tlv, unsigned int tlv_size) +{ + assert(ctl && id && (id->name[0] || id->numid) && tlv); + return snd_ctl_tlv_do(ctl, 0, id, tlv, tlv_size); +} + +/** + * \brief Set CTL element TLV value + * \param ctl CTL handle + * \param id CTL element id pointer + * \param tlv TLV array pointer to store + * \retval 0 on success + * \retval >0 on success when value was changed + * \retval <0 a negative error code + */ +int snd_ctl_elem_tlv_write(snd_ctl_t *ctl, const snd_ctl_elem_id_t *id, + const unsigned int *tlv) +{ + assert(ctl && id && (id->name[0] || id->numid) && tlv); + return snd_ctl_tlv_do(ctl, 1, id, (unsigned int *)tlv, tlv[1] + 2 * sizeof(unsigned int)); +} + +/** + * \brief Process CTL element TLV command + * \param ctl CTL handle + * \param id CTL element id pointer + * \param tlv TLV array pointer to process + * \retval 0 on success + * \retval >0 on success when value was changed + * \retval <0 a negative error code + */ +int snd_ctl_elem_tlv_command(snd_ctl_t *ctl, const snd_ctl_elem_id_t *id, + const unsigned int *tlv) +{ + assert(ctl && id && (id->name[0] || id->numid) && tlv); + return snd_ctl_tlv_do(ctl, -1, id, (unsigned int *)tlv, tlv[1] + 2 * sizeof(unsigned int)); +} + /** * \brief Lock CTL element * \param ctl CTL handle @@ -1742,6 +1821,39 @@ int snd_ctl_elem_info_is_user(const snd_ctl_elem_info_t *obj) } /** + * \brief Get info about TLV readability from a CTL element id/info + * \param obj CTL element id/info + * \return 0 if element's TLV is not readable, 1 if element's TLV is readable + */ +int snd_ctl_elem_info_is_tlv_readable(const snd_ctl_elem_info_t *obj) +{ + assert(obj); + return !!(obj->access & SNDRV_CTL_ELEM_ACCESS_TLV_READ); +} + +/** + * \brief Get info about TLV writeability from a CTL element id/info + * \param obj CTL element id/info + * \return 0 if element's TLV is not writable, 1 if element's TLV is writable + */ +int snd_ctl_elem_info_is_tlv_writable(const snd_ctl_elem_info_t *obj) +{ + assert(obj); + return !!(obj->access & SNDRV_CTL_ELEM_ACCESS_TLV_WRITE); +} + +/** + * \brief Get info about TLV command possibility from a CTL element id/info + * \param obj CTL element id/info + * \return 0 if element's TLV command is not possible, 1 if element's TLV command is supported + */ +int snd_ctl_elem_info_is_tlv_commandable(const snd_ctl_elem_info_t *obj) +{ + assert(obj); + return !!(obj->access & SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND); +} + +/** * \brief (DEPRECATED) Get info about values passing policy from a CTL element value * \param obj CTL element id/info * \return 0 if element value need to be passed by contents, 1 if need to be passed with a pointer diff --git a/src/control/control_hw.c b/src/control/control_hw.c index ac1f231c..88770eeb 100644 --- a/src/control/control_hw.c +++ b/src/control/control_hw.c @@ -200,6 +200,39 @@ static int snd_ctl_hw_elem_unlock(snd_ctl_t *handle, snd_ctl_elem_id_t *id) return 0; } +static int snd_ctl_hw_elem_tlv(snd_ctl_t *handle, int op_flag, + unsigned int numid, + unsigned int *tlv, unsigned int tlv_size) +{ + int inum; + snd_ctl_hw_t *hw = handle->private_data; + struct sndrv_ctl_tlv *xtlv; + + switch (op_flag) { + case -1: inum = SNDRV_CTL_IOCTL_TLV_COMMAND; break; + case 0: inum = SNDRV_CTL_IOCTL_TLV_READ; break; + case 1: inum = SNDRV_CTL_IOCTL_TLV_WRITE; break; + default: return -EINVAL; + } + xtlv = malloc(sizeof(struct sndrv_ctl_tlv) + tlv_size); + if (xtlv == NULL) + return -ENOMEM; + xtlv->numid = numid; + xtlv->length = tlv_size; + memcpy(xtlv->tlv, tlv, tlv_size); + if (ioctl(hw->fd, inum, xtlv) < 0) { + free(xtlv); + return -errno; + } + if (op_flag == 0) { + if (xtlv->tlv[1] + 2 * sizeof(unsigned int) > tlv_size) + return -EFAULT; + memcpy(tlv, xtlv->tlv, xtlv->tlv[1] + 2 * sizeof(unsigned int)); + } + free(xtlv); + return 0; +} + static int snd_ctl_hw_hwdep_next_device(snd_ctl_t *handle, int * device) { snd_ctl_hw_t *hw = handle->private_data; @@ -305,6 +338,7 @@ snd_ctl_ops_t snd_ctl_hw_ops = { .element_write = snd_ctl_hw_elem_write, .element_lock = snd_ctl_hw_elem_lock, .element_unlock = snd_ctl_hw_elem_unlock, + .element_tlv = snd_ctl_hw_elem_tlv, .hwdep_next_device = snd_ctl_hw_hwdep_next_device, .hwdep_info = snd_ctl_hw_hwdep_info, .pcm_next_device = snd_ctl_hw_pcm_next_device, diff --git a/src/control/control_local.h b/src/control/control_local.h index ce7bb837..ce81aea4 100644 --- a/src/control/control_local.h +++ b/src/control/control_local.h @@ -36,6 +36,8 @@ typedef struct _snd_ctl_ops { int (*element_write)(snd_ctl_t *handle, snd_ctl_elem_value_t *control); int (*element_lock)(snd_ctl_t *handle, snd_ctl_elem_id_t *lock); int (*element_unlock)(snd_ctl_t *handle, snd_ctl_elem_id_t *unlock); + int (*element_tlv)(snd_ctl_t *handle, int op_flag, unsigned int numid, + unsigned int *tlv, unsigned int tlv_size); int (*hwdep_next_device)(snd_ctl_t *handle, int *device); int (*hwdep_info)(snd_ctl_t *handle, snd_hwdep_info_t * info); int (*pcm_next_device)(snd_ctl_t *handle, int *device); diff --git a/src/control/hcontrol.c b/src/control/hcontrol.c index 05d28832..5a234a22 100644 --- a/src/control/hcontrol.c +++ b/src/control/hcontrol.c @@ -821,7 +821,9 @@ int snd_hctl_elem_read(snd_hctl_elem_t *elem, snd_ctl_elem_value_t * value) * \brief Set value for an HCTL element * \param elem HCTL element * \param value HCTL element value - * \return 0 otherwise a negative error code on failure + * \retval 0 on success + * \ratval >1 on success when value was changed + * \retval <0 a negative error code on failure */ int snd_hctl_elem_write(snd_hctl_elem_t *elem, snd_ctl_elem_value_t * value) { @@ -833,6 +835,53 @@ int snd_hctl_elem_write(snd_hctl_elem_t *elem, snd_ctl_elem_value_t * value) } /** + * \brief Get TLV value for an HCTL element + * \param elem HCTL element + * \param tlv TLV array for value + * \param tlv_size size of TLV array in bytes + * \return 0 otherwise a negative error code on failure + */ +int snd_hctl_elem_tlv_read(snd_hctl_elem_t *elem, unsigned int *tlv, unsigned int tlv_size) +{ + assert(elem); + assert(tlv); + assert(tlv_size >= 12); + return snd_ctl_elem_tlv_read(elem->hctl->ctl, &elem->id, tlv, tlv_size); +} + +/** + * \brief Set TLV value for an HCTL element + * \param elem HCTL element + * \param tlv TLV array for value + * \retval 0 on success + * \ratval >1 on success when value was changed + * \retval <0 a negative error code on failure + */ +int snd_hctl_elem_tlv_write(snd_hctl_elem_t *elem, const unsigned int *tlv) +{ + assert(elem); + assert(tlv); + assert(tlv[1] >= 4); + return snd_ctl_elem_tlv_write(elem->hctl->ctl, &elem->id, tlv); +} + +/** + * \brief Set TLV value for an HCTL element + * \param elem HCTL element + * \param tlv TLV array for value + * \retval 0 on success + * \ratval >1 on success when value was changed + * \retval <0 a negative error code on failure + */ +int snd_hctl_elem_tlv_command(snd_hctl_elem_t *elem, const unsigned int *tlv) +{ + assert(elem); + assert(tlv); + assert(tlv[1] >= 4); + return snd_ctl_elem_tlv_command(elem->hctl->ctl, &elem->id, tlv); +} + +/** * \brief Get HCTL handle for an HCTL element * \param elem HCTL element * \return HCTL handle -- 2.11.0