When port availability changes, send a subscription event for the
owning card.
+## v26, implemented by >= 2.0
+
+In reply from PA_COMMAND_GET_CARD_INFO (and thus
+PA_COMMAND_GET_CARD_INFO_LIST), the following is added:
+
+ uint32_t n_ports
+
+...followed by n_ports extended port entries, which look like this:
+
+ string name
+ string description
+ uint32_t priority
+ uint32_t available
+ uint8_t direction
+ proplist
+ uint32_t n_profiles
+ string profile_name_1
+ ...
+ string profile_name_n
+
+Profile names must match earlier sent profile names for the same card.
+
+
#### If you just changed the protocol, read this
## module-tunnel depends on the sink/source/sink-input/source-input protocol
## internals, so if you changed these, you might have broken module-tunnel.
AC_SUBST(PA_MAJORMINOR, pa_major.pa_minor)
AC_SUBST(PA_API_VERSION, 12)
-AC_SUBST(PA_PROTOCOL_VERSION, 25)
+AC_SUBST(PA_PROTOCOL_VERSION, 26)
# The stable ABI for client applications, for the version info x:y:z
# always will hold y=z
#define PA_CONTEXT_NOFAIL PA_CONTEXT_NOFAIL
/** \endcond */
+/** Direction bitfield - while we currently do not expose anything bidirectional,
+ one should test against the bit instead of the value (e g if (d & PA_DIRECTION_OUTPUT)),
+ because we might add bidirectional stuff in the future. \since 2.0
+*/
+typedef enum pa_direction {
+ PA_DIRECTION_OUTPUT = 0x0001U, /**< Output direction */
+ PA_DIRECTION_INPUT = 0x0002U /**< Input direction */
+} pa_direction_t;
+
+/** \cond fulldocs */
+#define PA_DIRECTION_OUTPUT PA_DIRECTION_OUTPUT
+#define PA_DIRECTION_INPUT PA_DIRECTION_INPUT
+/** \endcond */
+
/** The type of device we are dealing with */
typedef enum pa_device_type {
PA_DEVICE_TYPE_SINK, /**< Playback device */
/*** Card info ***/
+static void card_info_free(pa_card_info* i)
+{
+ if (i->proplist)
+ pa_proplist_free(i->proplist);
+
+ pa_xfree(i->profiles);
+
+ if (i->ports) {
+ uint32_t j;
+
+ for (j = 0; j < i->n_ports; j++) {
+ if (i->ports[j]) {
+ if (i->ports[j]->profiles)
+ pa_xfree(i->ports[j]->profiles);
+ if (i->ports[j]->proplist)
+ pa_proplist_free(i->ports[j]->proplist);
+ }
+ }
+
+ pa_xfree(i->ports[0]);
+ pa_xfree(i->ports);
+ }
+}
+
+static int fill_card_port_info(pa_tagstruct* t, pa_card_info* i)
+{
+ uint32_t j, k, l;
+
+ if (pa_tagstruct_getu32(t, &i->n_ports) < 0)
+ return -PA_ERR_PROTOCOL;
+
+ if (i->n_ports == 0) {
+ i->ports = NULL;
+ return 0;
+ }
+
+ i->ports = pa_xnew0(pa_card_port_info*, i->n_ports+1);
+ i->ports[0] = pa_xnew0(pa_card_port_info, i->n_ports);
+
+ for (j = 0; j < i->n_ports; j++) {
+ uint8_t direction;
+ uint32_t available;
+ pa_card_port_info* port = i->ports[j] = &i->ports[0][j];
+
+ port->proplist = pa_proplist_new();
+
+ if (pa_tagstruct_gets(t, &port->name) < 0 ||
+ pa_tagstruct_gets(t, &port->description) < 0 ||
+ pa_tagstruct_getu32(t, &port->priority) < 0 ||
+ pa_tagstruct_getu32(t, &available) < 0 ||
+ pa_tagstruct_getu8(t, &direction) < 0 ||
+ pa_tagstruct_get_proplist(t, port->proplist) < 0 ||
+ pa_tagstruct_getu32(t, &port->n_profiles) < 0) {
+
+ return -PA_ERR_PROTOCOL;
+ }
+
+ if (available > PA_PORT_AVAILABLE_YES ||
+ direction > PA_DIRECTION_OUTPUT + PA_DIRECTION_INPUT) {
+
+ return -PA_ERR_PROTOCOL;
+ }
+
+ port->direction = direction;
+ port->available = available;
+
+ if (port->n_profiles > 0) {
+ port->profiles = pa_xnew0(pa_card_profile_info*, i->n_profiles+1);
+
+ for (k = 0; k < port->n_profiles; k++) {
+ const char* profilename;
+
+ if (pa_tagstruct_gets(t, &profilename) < 0)
+ return -PA_ERR_PROTOCOL;
+
+ for (l = 0; l < i->n_profiles; l++) {
+ if (pa_streq(i->profiles[l].name, profilename)) {
+ port->profiles[k] = &i->profiles[l];
+ break;
+ }
+ }
+
+ if (l >= i->n_profiles)
+ return -PA_ERR_PROTOCOL;
+ }
+ }
+ }
+
+ return 0;
+}
+
static void context_get_card_info_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
pa_operation *o = userdata;
int eol = 1;
+ pa_card_info i;
pa_assert(pd);
pa_assert(o);
} else {
while (!pa_tagstruct_eof(t)) {
- pa_card_info i;
uint32_t j;
const char*ap;
pa_tagstruct_getu32(t, &i.n_profiles) < 0) {
pa_context_fail(o->context, PA_ERR_PROTOCOL);
+ card_info_free(&i);
goto finish;
}
pa_tagstruct_getu32(t, &i.profiles[j].priority) < 0) {
pa_context_fail(o->context, PA_ERR_PROTOCOL);
- pa_xfree(i.profiles);
+ card_info_free(&i);
goto finish;
}
}
pa_tagstruct_get_proplist(t, i.proplist) < 0) {
pa_context_fail(o->context, PA_ERR_PROTOCOL);
- pa_xfree(i.profiles);
- pa_proplist_free(i.proplist);
+ card_info_free(&i);
goto finish;
}
}
}
+ if (o->context->version >= 26) {
+ if (fill_card_port_info(t, &i) < 0) {
+ pa_context_fail(o->context, PA_ERR_PROTOCOL);
+ card_info_free(&i);
+ goto finish;
+ }
+ }
+
if (o->callback) {
pa_card_info_cb_t cb = (pa_card_info_cb_t) o->callback;
cb(o->context, &i, 0, o->userdata);
}
- pa_proplist_free(i.proplist);
- pa_xfree(i.profiles);
+ card_info_free(&i);
}
}
uint32_t priority; /**< The higher this value is the more useful this profile is as a default */
} pa_card_profile_info;
+/** Stores information about a specific port of a card. Please
+ * note that this structure can be extended as part of evolutionary
+ * API updates at any time in any new release. \since 2.0 */
+typedef struct pa_card_port_info {
+ const char *name; /**< Name of this port */
+ const char *description; /**< Description of this port */
+ uint32_t priority; /**< The higher this value is the more useful this port is as a default */
+ int available; /**< A \link pa_port_available_t, indicating availability status of this port. */
+ int direction; /**< This is a \link pa_direction_t enum, indicating the direction of this port. */
+ uint32_t n_profiles; /**< Number of entries in profile array */
+ pa_card_profile_info** profiles; /**< Array of pointers available profile, or NULL. Array is terminated by an entry set to NULL. */
+ pa_proplist *proplist; /**< Property list */
+} pa_card_port_info;
+
/** Stores information about cards. Please note that this structure
* can be extended as part of evolutionary API updates at any time in
* any new release. \since 0.9.15 */
pa_card_profile_info* profiles; /**< Array of available profile, or NULL. Array is terminated by an entry with name set to NULL. Number of entries is stored in n_profiles */
pa_card_profile_info* active_profile; /**< Pointer to active profile in the array, or NULL */
pa_proplist *proplist; /**< Property list */
+ uint32_t n_ports; /**< Number of entries in port array */
+ pa_card_port_info **ports; /**< Array of pointers to ports, or NULL. Array is terminated by an entry set to NULL. */
} pa_card_info;
/** Callback prototype for pa_context_get_card_info_...() \since 0.9.15 */
pa_tagstruct_puts(t, card->active_profile ? card->active_profile->name : NULL);
pa_tagstruct_put_proplist(t, card->proplist);
+
+ if (c->version < 26)
+ return;
+
+ if (card->ports) {
+ pa_device_port* port;
+ pa_proplist* proplist = pa_proplist_new(); /* For now - push an empty proplist */
+
+ pa_tagstruct_putu32(t, pa_hashmap_size(card->ports));
+
+ PA_HASHMAP_FOREACH(port, card->ports, state) {
+ pa_tagstruct_puts(t, port->name);
+ pa_tagstruct_puts(t, port->description);
+ pa_tagstruct_putu32(t, port->priority);
+ pa_tagstruct_putu32(t, port->available);
+ pa_tagstruct_putu8(t, /* FIXME: port->direction */ (port->is_input ? PA_DIRECTION_INPUT : 0) | (port->is_output ? PA_DIRECTION_OUTPUT : 0));
+ pa_tagstruct_put_proplist(t, proplist);
+
+ if (port->profiles) {
+ void* state2;
+ pa_tagstruct_putu32(t, pa_hashmap_size(port->profiles));
+ PA_HASHMAP_FOREACH(p, port->profiles, state2)
+ pa_tagstruct_puts(t, p->name);
+ } else
+ pa_tagstruct_putu32(t, 0);
+ }
+
+ pa_proplist_free(proplist);
+ } else
+ pa_tagstruct_putu32(t, 0);
}
static void module_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_module *module) {
printf(_("\tActive Profile: %s\n"),
i->active_profile->name);
+ if (i->ports) {
+ pa_card_port_info **p;
+
+ printf(_("\tPorts:\n"));
+ for (p = i->ports; *p; p++) {
+ pa_card_profile_info **pr = (*p)->profiles;
+ printf(_("\t\t%s: %s (priority %u)\n"), (*p)->name, (*p)->description, (*p)->priority);
+ if (pr) {
+ printf(_("\t\t\tPart of profile(s): %s"), pa_strnull((*pr)->name));
+ pr++;
+ while (*pr) {
+ printf(", %s", pa_strnull((*pr)->name));
+ pr++;
+ }
+ printf("\n");
+ }
+ }
+ }
+
pa_xfree(pl);
}