summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--alsa-binding/Alsa-RegEvt.c347
-rw-r--r--conf.d/cmake/config.cmake1
2 files changed, 347 insertions, 1 deletions
diff --git a/alsa-binding/Alsa-RegEvt.c b/alsa-binding/Alsa-RegEvt.c
index a9cce2b..1a803cb 100644
--- a/alsa-binding/Alsa-RegEvt.c
+++ b/alsa-binding/Alsa-RegEvt.c
@@ -20,6 +20,8 @@
#include <sys/inotify.h>
+#include <urcu/list.h>
+
#include <afb-timer.h>
#include <wrap-json.h>
@@ -32,6 +34,9 @@
#define CARD_DIR_TO_WATCH "/dev/snd/by-path"
+#define PCM_DEV_FILE_STRING "/dev/snd/pcmC%iD%i%c"
+#define PCM_EVENT_NAME "pcm-%s-%i,%i,%i-monitoring"
+
#ifndef MAX_SND_HAL
#define MAX_SND_HAL 10
#endif
@@ -39,10 +44,28 @@
typedef enum {
UNKNOWN_EVENT = 0,
CARD_CONTROL_CHANGE_EVENTS = 1,
- CARD_MONITORING_EVENTS = 2
+ CARD_MONITORING_EVENTS = 2,
+ PCM_AVAILABILITY_EVENTS = 3
} EventTypeT;
typedef struct {
+ unsigned int card;
+ unsigned int device;
+ unsigned int subdevice;
+ snd_pcm_stream_t stream;
+ int available;
+
+ char *pcmFileToMonitor;
+ int fd;
+ int wd;
+
+ sd_event_source *src;
+ afb_event_t afbevt;
+
+ struct cds_list_head node;
+} pcmEvtHandleT;
+
+typedef struct {
int fd;
int wd;
sd_event_source *src;
@@ -65,6 +88,7 @@ typedef struct {
typedef struct {
sndHandleT sndHandles[MAX_SND_CARD];
sndCardEvtHandleT *cardMonitoring;
+ struct cds_list_head pcmMonitoringHead;
afb_api_t apiHandle;
} sndCardsT;
@@ -105,6 +129,9 @@ STATIC EventTypeT alsaGetDevIdFromString(char *eventTypeString)
if(! strcasecmp(eventTypeString, "cards"))
return CARD_MONITORING_EVENTS;
+ if(! strcasecmp(eventTypeString, "pcm"))
+ return PCM_AVAILABILITY_EVENTS;
+
return UNKNOWN_EVENT;
}
@@ -141,6 +168,31 @@ PUBLIC queryModeE alsaGetModeFromQuery(json_object *queryJ)
return mode;
}
+STATIC void freePcmHandle(pcmEvtHandleT *pcmEventHandle)
+{
+ if(! pcmEventHandle)
+ return;
+
+ if(pcmEventHandle->node.prev && pcmEventHandle->node.next)
+ cds_list_del(&pcmEventHandle->node);
+
+ if(pcmEventHandle->src)
+ sd_event_source_unref(pcmEventHandle->src);
+
+ if(afb_event_is_valid(pcmEventHandle->afbevt))
+ afb_event_unref(pcmEventHandle->afbevt);
+
+ if(pcmEventHandle->fd != -1 && pcmEventHandle->wd != -1)
+ inotify_rm_watch(pcmEventHandle->fd, pcmEventHandle->wd);
+
+ if(pcmEventHandle->fd != -1)
+ close(pcmEventHandle->fd);
+
+ free(pcmEventHandle->pcmFileToMonitor);
+
+ free(pcmEventHandle);
+}
+
STATIC void freeSndCardHandle(sndCardEvtHandleT **cardMonitoringHandle)
{
sndCardEvtHandleT *cardMonitoringHandleToFree;
@@ -268,6 +320,99 @@ STATIC int sndCardTimerForCreatedFileCB(TimerHandleT *context)
return 1;
}
+STATIC int pcmEventCB(sd_event_source* src, int fd, uint32_t revents, void* userData)
+{
+ int err, pcmStatus, subscriberCount = -1;
+ ssize_t length, i = 0;
+ char buffer[INOTIFY_EVENT_BUF_LEN], *pcmName;
+
+ struct inotify_event *event;
+
+ pcmEvtHandleT *pcmEventHandle = (pcmEvtHandleT *) userData;
+
+ json_object *eventJ = NULL;
+
+ if(revents & EPOLLERR) {
+ AFB_ERROR("An error has been send by event loop polling, prevent new errors to be fired by deleting event src");
+ freePcmHandle(pcmEventHandle);
+ return -1;
+ }
+
+ if(revents & EPOLLIN) {
+ length = read(fd, buffer, INOTIFY_EVENT_BUF_LEN);
+ if(length < 0) {
+ AFB_ERROR("Nothing read using inotify");
+ freePcmHandle(pcmEventHandle);
+ return -2;
+ }
+
+ while(i < length) {
+ event = (struct inotify_event *) &buffer[i];
+
+ i += INOTIFY_EVENT_SIZE + event->len;
+
+ if((event->mask & IN_DELETE) || (event->mask & IN_DELETE_SELF)) {
+ AFB_WARNING("Monitored file has been deleted, delete monitoring");
+ freePcmHandle(pcmEventHandle);
+ return -3;
+ }
+
+ if((event->mask & IN_OPEN) || (event->mask & IN_CLOSE)) {
+ pcmStatus = alsaIsPcmAvailableUsingId(pcmEventHandle->card,
+ pcmEventHandle->device,
+ pcmEventHandle->subdevice,
+ pcmEventHandle->stream);
+ if(pcmEventHandle->available != pcmStatus) {
+ pcmEventHandle->available = pcmStatus;
+
+ err = asprintf(&pcmName,
+ "hw:%i,%i,%i",
+ pcmEventHandle->card,
+ pcmEventHandle->device,
+ pcmEventHandle->subdevice);
+ if(err <= 0) {
+ AFB_ERROR("Did not succeed to generate pcm name string (card : %i, device : %i, subdevice : %i)",
+ pcmEventHandle->card,
+ pcmEventHandle->device,
+ pcmEventHandle->subdevice);
+ freePcmHandle(pcmEventHandle);
+ return -4;
+ }
+
+ err = wrap_json_pack(&eventJ,
+ "{s:s, s:i, s:i}",
+ "name", pcmName,
+ "stream", pcmEventHandle->stream,
+ "available", pcmEventHandle->available);
+ if(err) {
+ AFB_ERROR("Did not succeed to generate pcm event json info (name : %s, stream : %s, available : %i)",
+ pcmName,
+ (pcmEventHandle->stream == SND_PCM_STREAM_PLAYBACK) ? "playback" : "capture",
+ pcmEventHandle->available);
+ free(pcmName);
+ freePcmHandle(pcmEventHandle);
+ return -5;
+ }
+
+ subscriberCount = afb_event_push(pcmEventHandle->afbevt, eventJ);
+ }
+ }
+ }
+ }
+
+ if(! subscriberCount) {
+ AFB_WARNING("Nobody listening for pcm %s hw:%i,%i,%i availability events, stop monitoring from now on",
+ (pcmEventHandle->stream == SND_PCM_STREAM_PLAYBACK) ? "playback" : "capture",
+ pcmEventHandle->card,
+ pcmEventHandle->device,
+ pcmEventHandle->subdevice);
+ freePcmHandle(pcmEventHandle);
+ return -6;
+ }
+
+ return 0;
+}
+
// This routine is called when sound cards are added/removed (when inotify '/dev/snd/by-path' events are fired)
STATIC int sndCardEventCB(sd_event_source* src, int fd, uint32_t revents, void* userData)
{
@@ -415,6 +560,201 @@ STATIC int sndCtlEventCB(sd_event_source* src, int fd, uint32_t revents, void* u
return 0;
}
+STATIC afb_event_t alsaEvtSubscribeUnsubscribePcmAvailabilityEvent(afb_req_t request,
+ json_object *queryJ,
+ sndCardsT *sndCards,
+ EventSubscribeUnsubscribeT subscriptionType)
+{
+ int err,
+ card = NO_CARD_SELECTED,
+ device = NO_DEVICE_SELECTED,
+ subdevice = NO_SUBDEVICE_SELECTED;
+
+ char *pcmName = NULL, *pcmEventName = NULL;
+
+ snd_pcm_stream_t stream;
+
+ pcmEvtHandleT *requestedPcmEventHandle = NULL, *currentPcmEventHandle;
+
+ if(! request || ! queryJ || ! sndCards) {
+ afb_req_fail(request, "invalid-args", "Arguments of call are invalid");
+ return NULL;
+ }
+
+ err = wrap_json_unpack(queryJ,
+ "{s:s, s?:i, s?:i, s?:i, s?:s, s:i !}",
+ "event", NULL,
+ "card", &card,
+ "device", &device,
+ "subdevice", &subdevice,
+ "name", &pcmName,
+ "stream", &stream);
+ if(err || (! pcmName && (card == NO_CARD_SELECTED || device == NO_DEVICE_SELECTED || subdevice == NO_SUBDEVICE_SELECTED))) {
+ afb_req_fail_f(request, "invalid_request", "Request json is invalid (%s)", json_object_get_string(queryJ));
+ return NULL;
+ }
+
+ if(pcmName && (sscanf(pcmName, "hw:%i,%i,%i", &card, &device, &subdevice) != 3)) {
+ afb_req_fail_f(request, "invalid_request", "Request pcm name is invalid (%s)", pcmName);
+ return NULL;
+ }
+
+ if(card < 0 || card > MAX_SND_CARD ||
+ device < 0 || device > MAX_CARD_DEVICES ||
+ subdevice < 0 || subdevice > MAX_DEVICE_SUBDEVICES) {
+ afb_req_fail_f(request,
+ "invalid_request",
+ "Requested pcm is not valid (card : %i, device : %i, subdevice : %i)",
+ card,
+ device,
+ subdevice);
+ return NULL;
+ }
+
+ cds_list_for_each_entry(currentPcmEventHandle, &sndCards->pcmMonitoringHead, node) {
+ if(currentPcmEventHandle->card == card &&
+ currentPcmEventHandle->device == device &&
+ currentPcmEventHandle->subdevice == subdevice &&
+ currentPcmEventHandle->stream == stream) {
+ requestedPcmEventHandle = currentPcmEventHandle;
+ break;
+ }
+ }
+
+ if(requestedPcmEventHandle) {
+ return requestedPcmEventHandle->afbevt;
+ }
+ else if(subscriptionType == EVENT_UNSUBSCRIBE) {
+ afb_req_fail_f(request,
+ "unknown_event",
+ "Can not unsubscribe unknown event (card : %i, device : %i, subdevice : %i, stream : %i)",
+ card,
+ device,
+ subdevice,
+ stream);
+ return NULL;
+ }
+
+ requestedPcmEventHandle = calloc(1, sizeof(pcmEvtHandleT));
+ if(! requestedPcmEventHandle) {
+ afb_req_fail_f(request,
+ "eventhandle_alloc",
+ "Error when tried to allocate pcm event data structure (card : %i, device : %i, stream : %i, stream : %i)",
+ card,
+ device,
+ subdevice,
+ stream);
+ return NULL;;
+ }
+
+ requestedPcmEventHandle->card = card;
+ requestedPcmEventHandle->device = device;
+ requestedPcmEventHandle->subdevice = subdevice;
+ requestedPcmEventHandle->stream = stream;
+ requestedPcmEventHandle->fd = -1;
+ requestedPcmEventHandle->wd = -1;
+
+ err = asprintf(&requestedPcmEventHandle->pcmFileToMonitor,
+ PCM_DEV_FILE_STRING,
+ card,
+ device,
+ (stream == SND_PCM_STREAM_PLAYBACK) ? 'p' : 'c');
+ if(err <= 0) {
+ afb_req_fail_f(request,
+ "monitored_file",
+ "Did not succeed to generate pcm file to monitor string (card : %i, device : %i, stream : %i)",
+ card,
+ device,
+ stream);
+ freePcmHandle(requestedPcmEventHandle);
+ return NULL;
+ }
+
+ err = asprintf(&pcmEventName,
+ PCM_EVENT_NAME,
+ (stream == SND_PCM_STREAM_PLAYBACK) ? "playback" : "capture",
+ card,
+ device,
+ subdevice);
+ if(err <= 0) {
+ afb_req_fail_f(request,
+ "pcm_event_name",
+ "Did not succeed to generate pcm event name string (card : %i, device : %i, stream : %i, stream : %i)",
+ card,
+ device,
+ subdevice,
+ stream);
+ freePcmHandle(requestedPcmEventHandle);
+ return NULL;
+ }
+
+ requestedPcmEventHandle->afbevt = afb_daemon_make_event(pcmEventName);
+ if(! afb_event_is_valid(requestedPcmEventHandle->afbevt)) {
+ afb_req_fail_f(request, "pcm-monitoring-event", "Cannot register new %s event", pcmEventName);
+ free(pcmEventName);
+ freePcmHandle(requestedPcmEventHandle);
+ return NULL;
+ }
+
+ free(pcmEventName);
+
+ requestedPcmEventHandle->fd = inotify_init();
+ if(requestedPcmEventHandle->fd < 0) {
+ afb_req_fail_f(request, "inotify-init", "Error %s happened while getting file descriptor for inotify", strerror(errno));
+ freePcmHandle(requestedPcmEventHandle);
+ return NULL;
+ }
+
+ requestedPcmEventHandle->wd = inotify_add_watch(requestedPcmEventHandle->fd,
+ requestedPcmEventHandle->pcmFileToMonitor,
+ IN_ALL_EVENTS);
+ if(requestedPcmEventHandle->wd < 0) {
+ afb_req_fail_f(request,
+ "inotify-watch",
+ "Error %s happened while setting watcher on '%s' file using inotify",
+ strerror(errno),
+ requestedPcmEventHandle->pcmFileToMonitor);
+ freePcmHandle(requestedPcmEventHandle);
+ return NULL;
+ }
+
+ requestedPcmEventHandle->available = alsaIsPcmAvailableUsingId(card, device, subdevice, stream);
+ if(requestedPcmEventHandle->available < 0) {
+ afb_req_fail_f(request,
+ "pcm-availability",
+ "Did not succeed to get pcm current availability (card : %i, device : %i, stream : %i, stream : %i)",
+ card,
+ device,
+ subdevice,
+ stream);
+ freePcmHandle(requestedPcmEventHandle);
+ return NULL;
+ }
+
+ err = sd_event_add_io(afb_daemon_get_event_loop(),
+ &requestedPcmEventHandle->src,
+ requestedPcmEventHandle->fd,
+ EPOLLIN,
+ pcmEventCB,
+ requestedPcmEventHandle);
+ if(err < 0) {
+ afb_req_fail_f(request,
+ "register-mainloop",
+ "Cannot hook pcm %s hw:%i,%i,%i event to mainloop err=%d",
+ (stream == SND_PCM_STREAM_PLAYBACK) ? "playback" : "capture",
+ card,
+ device,
+ subdevice,
+ err);
+ freePcmHandle(requestedPcmEventHandle);
+ return NULL;
+ }
+
+ cds_list_add_tail(&requestedPcmEventHandle->node, &sndCards->pcmMonitoringHead);
+
+ return requestedPcmEventHandle->afbevt;
+}
+
// Subscribe to each time a sound card is added/removed (using inotify '/dev/snd/by-path' events)
STATIC afb_event_t alsaEvtSubscribeUnsubscribeSoundCardEvent(afb_req_t request,
sndCardsT *sndCards,
@@ -580,6 +920,7 @@ PUBLIC void alsaEvtSubscribeUnsubscribe(afb_req_t request, EventSubscribeUnsubsc
if(! sndCards) {
sndCards = calloc(1, sizeof(sndCardsT));
sndCards->apiHandle = afb_req_get_api(request);
+ CDS_INIT_LIST_HEAD(&sndCards->pcmMonitoringHead);
}
queryJ = afb_req_json(request);
@@ -600,6 +941,10 @@ PUBLIC void alsaEvtSubscribeUnsubscribe(afb_req_t request, EventSubscribeUnsubsc
eventToSubscribeUnsubscribe = alsaEvtSubscribeUnsubscribeSoundCardEvent(request, sndCards, subscriptionType);
break;
+ case PCM_AVAILABILITY_EVENTS:
+ eventToSubscribeUnsubscribe = alsaEvtSubscribeUnsubscribePcmAvailabilityEvent(request, queryJ, sndCards, subscriptionType);
+ break;
+
case UNKNOWN_EVENT:
default:
afb_req_fail_f(request, "unknown-event", "Invalid query='%s'", json_object_get_string(queryJ));
diff --git a/conf.d/cmake/config.cmake b/conf.d/cmake/config.cmake
index 27b0dfd..55dd171 100644
--- a/conf.d/cmake/config.cmake
+++ b/conf.d/cmake/config.cmake
@@ -75,6 +75,7 @@ set (PKG_REQUIRED_LIST
libmicrohttpd>=0.9.55
libafbwsc
alsa>=1.1.2
+ liburcu
afb-helpers
)