Proposal for kernel change
Abramo Bagnara
abramo en alsa-project.org
Sab Ene 22 22:37:33 CST 2000
After a preliminary discussion with Alan and his green light, I send you
our proposal for writev/readv callback insertion to file_operation
struct.
We are willing to submit you a patch following your guidelines (if any).
In approval case let us know if the patch that you want is against
latest 2.3 or 2.5.0.
-----------
Introduction:
Many sound cards may (or need) to receive and send data with voices non
interleaved (N samples for the left channel and N for the right channel
by example).
This is due to having separate ring buffers or for other hardware
dependant consideration.
ALSA current write interface can work in two different mode called block
and stream mode.
In block mode the ring buffers area is divided in N fragments and the
write is accepted only if count % frag_size == 0.
In stream mode the data are sent to or received from hardware as soon as
it's available. In this way we reduce the latency to the minimum
possible.
The block mode can be used easily with non interleaved data keeping safe
that frag_size is divided in V parts (where V is the number of voices).
Currently the stream mode is not usable with non interleaved data
because:
- we'd have to specify bytes per voice (or deduce it using count / V)
- we'd have to recompose the stream after a partial transfer
- the transferred bytes are not contiguous
etc.
We can for sure affirm that to use that with standard write interface is
a mess and not efficient.
-----------
Proposal:
To extend struct file_operation for readv and writev functions.
If the pointer is NULL the current behaviour is retained.
This permit also to not treat the sock case as an exception (exceptions
may be thought as bad design symptoms ;-)
The writev/readv semantic is untouched (this syscall seem to be born for
our needs).
I attach a sample program that show the efficiency level reachable.
The program read many mono stream and output them as a non interleaved
multiple voices stream, trying to keep the latency the minimum possible.
--
Abramo Bagnara mailto:abramo en alsa-project.org
Opera Unica
Via Emilia Interna, 140 Phone: +39.0546.656023
48014 Castel Bolognese (RA) - Italy Fax: +39.0546.656023
ALSA project is http://www.alsa-project.org
sponsored by SuSE Linux http://www.suse.com
It sounds good!
------------ próxima parte ------------
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/uio.h>
#define BUFSIZE 1024
/* Require: new writev semantics: each iov_base represent a different voice
In this implementation write size is reduced by buffer boundary, but
this can be avoided using voices*2 iovec elements
Without to need a kernel change we may use a special ioctl to do the
writev stuff, but the writev solution is *far more* beautiful.
*/
int stream_with_new_writev(void)
{
int voices = 2;
int pfd = 3;
struct voice_buf {
char *buf;
int woff;
int used;
int free;
} bufs[voices];
struct iovec iovec[voices];
int fd[voices];
int v, result;
fd_set rfds, wfds;
int writable;
int fds = pfd;
int roff = 0;
for (v = 0; v < voices; ++v) {
bufs[v].buf = malloc(BUFSIZE);
bufs[v].woff = 0;
bufs[v].used = 0;
bufs[v].free = BUFSIZE;
if (fd[v] > fds)
fds = fd[v];
}
fds++;
writable = 0;
while (1) {
FD_ZERO(&rfds);
for (v = 0; v < voices; ++v) {
if (bufs[v].free > 0)
FD_SET(fd[v], &rfds);
}
FD_ZERO(&wfds);
if (writable > 0)
FD_SET(pfd, &wfds);
result = select(fds, &rfds, &wfds, NULL, NULL);
if (FD_ISSET(pfd, &wfds)) {
for (v = 0; v < voices; ++v) {
iovec[v].iov_base = bufs[v].buf+roff;
iovec[v].iov_len = writable;
}
result = writev(pfd, iovec, voices);
if (result > 0) {
int empty = 1;
if (result % voices != 0) {
printf("driver failure\n");
return -1;
}
result /= voices;
roff += result;
if (roff == BUFSIZE)
roff = 0;
for (v = 0; v < voices; ++v) {
bufs[v].used -= result;
bufs[v].free += result;
if (bufs[v].used > 0)
empty = 0;
}
if (empty && roff > 0) {
for (v = 0; v < voices; ++v)
bufs[v].woff = 0;
roff = 0;
}
} else {
printf("writev error\n");
return -1;
}
}
writable = BUFSIZE - roff;
for (v = 0; v < voices; ++v) {
if (FD_ISSET(fd[v], &rfds)) {
int readable = BUFSIZE - bufs[v].woff;
if (readable > bufs[v].free)
readable = bufs[v].free;
result = read(fd[v], bufs[v].buf + bufs[v].woff, readable);
if (result < 0) {
printf("read error\n");
return -1;
}
bufs[v].woff += result;
bufs[v].used += result;
bufs[v].free -= result;
if (bufs[v].woff == BUFSIZE)
bufs[v].woff = 0;
}
if (bufs[v].used < writable)
writable = bufs[v].used;
}
}
return 0;
}
Más información sobre la lista de distribución Ayuda