[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