[LWN Logo]

From:   Abramo Bagnara <abramo@alsa-project.org>
Subject: Proposal for kernel change
To:     Linus Torvalds <torvalds@www.transmeta.com>
Date:   Sat, 22 Jan 2000 14:37:10 +0100

This is a multi-part message in MIME format.
--------------3A5ABC749B40F1DD1874ED75
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit


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@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!
--------------3A5ABC749B40F1DD1874ED75
Content-Type: text/plain; charset=us-ascii;
 name="stream.c"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="stream.c"

#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;
}


--------------3A5ABC749B40F1DD1874ED75--


-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.rutgers.edu
Please read the FAQ at http://www.tux.org/lkml/