The Idea:

For a Radio Station (eg. SubEther) you need to control your mixer volumes smoothly in an analog way to fade from one channel to another.

The Concept:

The well known Joystick port is just an Analog interface, and almost any mainstream PC Soundcard has one. The generic support provieds 4 Axis and 4 Buttons. We can use one Axis per mixer channel. Thus we can controll 4 different mixer channels on one fader board.

The Heck:

So let me see. The Linux kernel already has 'joyport' support. So compile the nesecarry modules:
	Character devices  --->
		Joysticks  --->
			[M] Game port support
			[M]   Classic ISA/PnP gameports
			[M]   Microsoft SideWinder digital joysticks and gamepads

Normally the input module ns558.o supports 4 channels (Axis: X, Y) so we are able to fade VOL, PCM, LINE and CD. I had a SideWinder for testing purpose. This can be used with an additional module that can read 6 analog channels (2 more Axis: for Ruder and Throttle) from a digital controller inside the joystick.
Is a good point to start reading. And a few googles later there was some code:

The Code:

#include <stdio.h>
#include <unistd.h>

#include <string.h>
#include <stddef.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>

#include <sys/time.h>
#include <sys/socket.h>
#include <libintl.h>
#include <locale.h>

Just the default bunch of include headers. And joystick.h for the js kernel API

#include <linux/joystick.h>

The Sound stuff is included because we need to access the mixer device

#include <linux/sound.h>
#include <linux/soundcard.h>

Some predefines

#define JS_EVENT_BUTTON         0x01    /* button pressed/released */
#define JS_EVENT_AXIS           0x02    /* joystick moved */
#define JS_EVENT_INIT           0x80    /* initial state of device */

#define NAME_LENGTH 128

#define SCALE1  28934
#define DIVIDE1 290
#define SCALE2  27273
#define DIVIDE2 273
#define SCALE3  27624
#define DIVIDE3 277
#define SCALE4  29372
#define DIVIDE4 294

#define SHIFT   99


int main(int argv, char *argc[])
  int   fd, mixfd;
  unsigned char axes    = 4;
  unsigned char buttons = 4;
  int   version = 0x000800;
  char  name[NAME_LENGTH] = "Unknown";
  int *axis;
  int *button;
  unsigned char   i,j,k,l;
  int             m,n,o,p;
  struct js_event js;

Open the devices /dev/js0 and /dev/mixer

  if ((fd = open("/dev/js0"     , O_RDONLY)) < 0) {
    return 1;
  if ((mixfd = open("/dev/mixer", O_RDWR)) < 0) {
    return 1;

setup the joystick device

  ioctl(fd, JSIOCGVERSION          , &version);
  ioctl(fd, JSIOCGAXES             , &axes);
  ioctl(fd, JSIOCGBUTTONS          , &buttons);
  ioctl(fd, JSIOCGNAME(NAME_LENGTH), name);
  axis   = (int*)calloc(axes   , sizeof(int));
  button = (int*)calloc(buttons, sizeof(char));

Now the ugly part, first an infinite loop.

  while (1) {

this reads the structure with status information from the joystick
    if (read(fd, &js, sizeof(struct js_event)) != sizeof(struct js_event)) {
      perror("\npotmix: error reading");
      return 2;

write the button or axis state into an array

    switch(js.type & ~JS_EVENT_INIT) {
      case JS_EVENT_BUTTON:
        button[js.number] = js.value;
      case JS_EVENT_AXIS:
        axis[js.number]   = js.value;

due to the fact that we recive the axis state as signed integer, we have to calculate values between 0-100(decimal) for the mixer.
    if (axes) {
      // + 32757 ) / 655;
      i = ((((axis[0])*(-1) + SCALE1 ) / DIVIDE1 ) - SHIFT) ;
      j = ((((axis[1])*(-1) + SCALE2 ) / DIVIDE2 ) - SHIFT) ;
      k = ((((axis[2])*(-1) + SCALE3 ) / DIVIDE3 ) - SHIFT) ;
      l = ((((axis[3])*(-1) + SCALE4 ) / DIVIDE4 ) - SHIFT) ;

this part is used to wirte the new value to the mixer device, thanks roh for
      m = ((((i) & 0xff) <<8) | ((i) & 0xff));
      n = ((((j) & 0xff) <<8) | ((j) & 0xff));
      o = ((((k) & 0xff) <<8) | ((k) & 0xff));
      p = ((((l) & 0xff) <<8) | ((l) & 0xff));
      ioctl(mixfd, MIXER_WRITE(SOUND_MIXER_VOLUME), &m);
      ioctl(mixfd, MIXER_WRITE(SOUND_MIXER_PCM)   , &n);
      ioctl(mixfd, MIXER_WRITE(SOUND_MIXER_CD)    , &o);
      ioctl(mixfd, MIXER_WRITE(SOUND_MIXER_LINE)  , &p);

the rest is foo, the whole thing is an evil hack, does never reach the return, and hopefully will live a ling time.

      printf("\r (%5d,%5d,%5d,%5d)  0x%2x  0x%2x  0x%2x  0x%2x                                                           ",i,j,k,l,m,n,o,p);
  return 23;
exit 23;

The Hardware:

100 kOhm oder so zeuch...roh