[alsa-devel] Sample program for hwdep interface

Takashi Sakamoto o-takashi at sakamocchi.jp
Fri Dec 20 15:46:15 CET 2013


This is how to use hwdep interface for firewire devices.
I hope FFADO developers read this.

With hwdep interface, the program in user land can do:
1.get firewire node infomation
2.get notification when starting/stopping kernel streaming
3.lock/unlock kernel streaming

For jackd with FFADO backend, it's better to lock kernel streaming in 
advance. Then snd-bebob/snd-fireworks can't interrupt FFADO streaming.

And with snd-fireworks, the program can do:
4.transmit command of EFW transaction
5.receive response of EFW transaction

4/5 is my complete solution for ticket #360.
Make echo audiofire 8 and audiofire 12 work with recent firmwares
http://subversion.ffado.org/ticket/360

(I note 4 can be done via /dev/fw*. But I think 4 is easier.)

If someone works for snd-dice to support valid devices, it may be useful 
that the program get dice notification in the same way.
(I have a little knowledgement about Dice.)

I attach sample program (too rough...) to show how to use hwdep interface.


Regards,
Takashi Sakamoto


====
/* monitor.c: program to monitor the status of firewire sound devices */

#include <stdio.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <endian.h>
#include <time.h>
#include <sys/ioctl.h>
#include <sys/epoll.h>
#include <souund/firewire.h>

static struct snd_firewire_get_info hwdep_info;
bool try_lock;
bool fireworks = false;

static void print_event_lock(struct snd_firewire_event_lock_status *msg, 
int fd)
{
	printf("\nLock Status:\n");
	printf("Status:\t%s\n", msg->status ? "Locked" : "Unlocked" );

	if (try_lock) {
		if (ioctl(fd, SNDRV_FIREWIRE_IOCTL_LOCK) < 0)
			printf("lock failed\n");
		printf("lock success\n");
	}

	return;
}

static void print_event_dice(struct snd_firewire_event_dice_notification 
*msg)
{
	printf("\nDice Norification:\n");
	printf("Notification: 0x%x\n", msg->notification);
	return;
}

static void print_event_efw(struct snd_firewire_event_efw_response *evt, 
int count)
{
	uint32_t *resp;
	struct snd_efw_transaction *t;
	unsigned int i, index, length, params;

	resp = evt->response;
	count /= 4;

	index = 0;
	while (count > 0) {
		t = (struct snd_efw_transaction *)resp;
		length = be32toh(t->length);

		printf("\nEFW Response %d:\n", index++);
		printf("Length:\t\t%d\n", be32toh(t->length));
		printf("Version:\t%d\n", be32toh(t->version));
		printf("Seqnum:\t\t%d\n", be32toh(t->seqnum));
		printf("Category:\t%d\n", be32toh(t->category));
		printf("Command:\t%d\n", be32toh(t->command));
		printf("Status:\t\t%d\n", be32toh(t->status));

		params = length - sizeof(struct snd_efw_transaction) / sizeof(uint32_t);
		if (params > 0)
			for (i = 0; i < params; i++)
				printf("params[%d]:\t%08X\n", i, be32toh(t->params[i]));

		resp += length;
		count -= length * sizeof(uint32_t);
	}
}

static int read_event(int fd)
{
	int count;
	char buf[1024] = {0};
	struct snd_firewire_event_common *event;

	count = read(fd, buf, 1024);
	if (count < 0)
		return -1;

	event = (struct snd_firewire_event_common *)buf;
	if (event->type == SNDRV_FIREWIRE_EVENT_LOCK_STATUS)
		print_event_lock((struct snd_firewire_event_lock_status *)buf, fd);
	else if (event->type == SNDRV_FIREWIRE_EVENT_DICE_NOTIFICATION)
		print_event_dice((struct snd_firewire_event_dice_notification *)buf);
	else if (event->type == SNDRV_FIREWIRE_EVENT_EFW_RESPONSE)
		print_event_efw((struct snd_firewire_event_efw_response *)buf, count);

	return 0;
}

static int write_event(int fd)
{
	int count;
	struct snd_efw_transaction *t;
	char buf[1024] = {0};

	t = (struct snd_efw_transaction *)buf;
	t->length	= htobe32(6);
	t->version	= htobe32(1);
	t->seqnum	= htobe32(0);
	t->category	= htobe32(3);
	t->command	= htobe32(5);
	t->status	= htobe32(0);

	count = write(fd, buf, sizeof(struct snd_efw_transaction));
	if (count < 0)
		printf("err: %d\n", count);

	return count;
}

#define EVENTS 10
time_t cmd_deffer = 0;
static int main_loop(int fd)
{
	int epfd, count, i, err;
	time_t now;
	struct epoll_event ev, events[EVENTS];

	epfd = epoll_create(EVENTS);
	if (epfd < 0) {
		printf("error: %s\n", strerror(errno));
		err = errno;
		goto end;
	}

	memset(&ev, 0, sizeof(struct epoll_event));
	ev.events = EPOLLIN;
	ev.data.fd = fd;

	if (fireworks)
		ev.events |= EPOLLOUT;

	if (epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev) < 0)
		return errno;

	while (1) {
		count = epoll_wait(epfd, events, EVENTS, 200);
		if (count == 0)
			continue;
		else if (count < 0) {
			err = errno;
			break;
		}

		for (i = 0; i < count; i++) {
			if (events[i].events & EPOLLOUT) {
				if (time(&now) < 0)
					break;
				if (now > cmd_deffer) {
					if (write_event(events[i].data.fd) < 0) {
						err = errno;
						break;
					}
					cmd_deffer = now + 3;
				}
			}
			if (events[i].events & EPOLLIN) {
				if (read_event(events[i].data.fd) < 0) {
					err = errno;
					break;
				}
			}
		}
	}
end:
	close(epfd);
	return err;
}

int main(int argc, void *argv[])
{
	int fd, count, err;
	char buf[1024] = {};
	struct snd_firewire_event_common *event;

	if (argc > 2)
		try_lock = true;

	fd = open(argv[1], O_RDWR);
	if (fd < 0) {
		printf("fail to open: %s\n", (char *)argv[1]);
		return 1;
	}

	if (ioctl(fd, SNDRV_FIREWIRE_IOCTL_GET_INFO, &hwdep_info) < 0) {
		printf("error: %s\n", strerror(errno));
		return 1;
	}

	printf("Information of Firewire Sound Device\n");
	printf("type: %d\n", hwdep_info.type);
	printf("card: %d\n", hwdep_info.card);
	printf("GUID: 0x%02x%02x%02x%02x%02x%02x%02x%02x\n",
	       hwdep_info.guid[0], hwdep_info.guid[1], hwdep_info.guid[2],
	       hwdep_info.guid[3], hwdep_info.guid[4], hwdep_info.guid[5],
	       hwdep_info.guid[6], hwdep_info.guid[7] );
	printf("Name: %s\n\n", hwdep_info.device_name);

	if (hwdep_info.type == SNDRV_FIREWIRE_TYPE_FIREWORKS)
		fireworks = true;

	err = main_loop(fd);
	close(fd);
	return err;
}



More information about the Alsa-devel mailing list