This commit was generated by cvs2svn to compensate for changes in r3252,

which included commits to RCS files with non-trunk default branches.
This commit is contained in:
Steven Wallace 1994-10-01 01:33:47 +00:00
commit 8f25169ce6
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=3253
10 changed files with 4652 additions and 0 deletions

953
sys/i386/isa/sound/ad1848.c Normal file
View File

@ -0,0 +1,953 @@
/*
* sound/ad1848.c
*
* The low level driver for the AD1848/CS4248 codec chip which
* is used for example in the MS Sound System.
*
* The CS4231 which is used in the GUS MAX and some other cards is
* upwards compatible with AD1848 and this driver is able to drive it.
*
* Copyright by Hannu Savolainen 1994
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met: 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer. 2.
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#define DEB(x)
#define DEB1(x)
#include "sound_config.h"
#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_AD1848)
#define IMODE_NONE 0
#define IMODE_OUTPUT 1
#define IMODE_INPUT 2
#define IMODE_INIT 3
#define IMODE_MIDI 4
typedef struct
{
int base;
int irq;
int dma_capture, dma_playback;
unsigned char MCE_bit;
int speed;
unsigned char speed_bits;
int channels;
int audio_format;
unsigned char format_bits;
int xfer_count;
int irq_mode;
int intr_active;
int opened;
char *chip_name;
int mode;
}
ad1848_info;
static int nr_ad1848_devs = 0;
static char irq2dev[16] =
{-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1};
static int ad_format_mask[2 /*devc->mode*/ ] =
{
AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW,
AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW | AFMT_U16_LE | AFMT_IMA_ADPCM
};
static ad1848_info dev_info[MAX_AUDIO_DEV];
#define io_Index_Addr(d) ((d)->base)
#define io_Indexed_Data(d) ((d)->base+1)
#define io_Status(d) ((d)->base+2)
#define io_Polled_IO(d) ((d)->base+3)
static int ad1848_open (int dev, int mode);
static void ad1848_close (int dev);
static int ad1848_ioctl (int dev, unsigned int cmd, unsigned int arg, int local);
static void ad1848_output_block (int dev, unsigned long buf, int count, int intrflag, int dma_restart);
static void ad1848_start_input (int dev, unsigned long buf, int count, int intrflag, int dma_restart);
static int ad1848_prepare_for_IO (int dev, int bsize, int bcount);
static void ad1848_reset (int dev);
static void ad1848_halt (int dev);
void ad1848_interrupt (int dev);
static int
ad_read (ad1848_info * devc, int reg)
{
unsigned long flags;
int x;
DISABLE_INTR (flags);
OUTB ((unsigned char) (reg & 0xff) | devc->MCE_bit, io_Index_Addr (devc));
x = INB (io_Indexed_Data (devc));
/* printk("(%02x<-%02x) ", reg|devc->MCE_bit, x); */
RESTORE_INTR (flags);
return x;
}
static void
ad_write (ad1848_info * devc, int reg, int data)
{
unsigned long flags;
DISABLE_INTR (flags);
OUTB ((unsigned char) (reg & 0xff) | devc->MCE_bit, io_Index_Addr (devc));
OUTB ((unsigned char) (data & 0xff), io_Indexed_Data (devc));
/* printk("(%02x->%02x) ", reg|devc->MCE_bit, data); */
RESTORE_INTR (flags);
}
static void
ad_set_MCE (ad1848_info * devc, int state)
{
unsigned long flags;
DISABLE_INTR (flags);
if (state)
devc->MCE_bit = 0x40;
else
devc->MCE_bit = 0x00;
OUTB (devc->MCE_bit, io_Index_Addr (devc));
RESTORE_INTR (flags);
}
static void
wait_for_calibration (ad1848_info * devc)
{
int timeout = 0;
/*
* Wait until the auto calibration process has finished.
*
* 1) Wait until the chip becomes ready (reads don't return 0x80).
* 2) Wait until the ACI bit of I11 gets on and then off.
*/
timeout = 100000;
while (timeout > 0 && INB (devc->base) == 0x80)
timeout--;
if (INB (devc->base) == 0x80)
printk ("ad1848: Auto calibration timed out(1).\n");
timeout = 100000;
while (timeout > 0 && !(ad_read (devc, 11) & 0x20))
timeout--;
if (!(ad_read (devc, 11) & 0x20))
printk ("ad1848: Auto calibration timed out(2).\n");
timeout = 100000;
while (timeout > 0 && ad_read (devc, 11) & 0x20)
timeout--;
if (ad_read (devc, 11) & 0x20)
printk ("ad1848: Auto calibration timed out(3).\n");
}
static struct audio_operations ad1848_pcm_operations[MAX_AUDIO_DEV] =
{
{
"Generic AD1848 codec",
DMA_AUTOMODE,
AFMT_U8, /* Will be set later */
NULL,
ad1848_open,
ad1848_close,
ad1848_output_block,
ad1848_start_input,
ad1848_ioctl,
ad1848_prepare_for_IO,
ad1848_prepare_for_IO,
ad1848_reset,
ad1848_halt,
NULL,
NULL
}};
static int
ad1848_open (int dev, int mode)
{
int err;
ad1848_info *devc = NULL;
unsigned long flags;
DEB (printk ("ad1848_open(int mode = %X)\n", mode));
if (dev < 0 || dev >= num_audiodevs)
return RET_ERROR (ENXIO);
devc = (ad1848_info *) audio_devs[dev]->devc;
DISABLE_INTR (flags);
if (devc->opened)
{
RESTORE_INTR (flags);
printk ("ad1848: Already opened\n");
return RET_ERROR (EBUSY);
}
if (devc->irq) /* Not managed by another driver */
if ((err = snd_set_irq_handler (devc->irq, ad1848_interrupt)) < 0)
{
printk ("ad1848: IRQ in use\n");
RESTORE_INTR (flags);
return err;
}
if (DMAbuf_open_dma (dev) < 0)
{
RESTORE_INTR (flags);
printk ("ad1848: DMA in use\n");
return RET_ERROR (EBUSY);
}
devc->intr_active = 0;
devc->opened = 1;
RESTORE_INTR (flags);
return 0;
}
static void
ad1848_close (int dev)
{
unsigned long flags;
ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
DEB (printk ("ad1848_close(void)\n"));
DISABLE_INTR (flags);
devc->intr_active = 0;
if (devc->irq) /* Not managed by another driver */
snd_release_irq (devc->irq);
ad1848_reset (dev);
DMAbuf_close_dma (dev);
devc->opened = 0;
RESTORE_INTR (flags);
}
static int
set_speed (ad1848_info * devc, int arg)
{
/*
* The sampling speed is encoded in the least significant nible of I8. The
* LSB selects the clock source (0=24.576 MHz, 1=16.9344 Mhz) and other
* three bits select the divisor (indirectly):
*
* The available speeds are in the following table. Keep the speeds in
* the increasing order.
*/
typedef struct
{
int speed;
unsigned char bits;
}
speed_struct;
static speed_struct speed_table[] =
{
{5510, (0 << 1) | 1},
{5510, (0 << 1) | 1},
{6620, (7 << 1) | 1},
{8000, (0 << 1) | 0},
{9600, (7 << 1) | 0},
{11025, (1 << 1) | 1},
{16000, (1 << 1) | 0},
{18900, (2 << 1) | 1},
{22050, (3 << 1) | 1},
{27420, (2 << 1) | 0},
{32000, (3 << 1) | 0},
{33075, (6 << 1) | 1},
{37800, (4 << 1) | 1},
{44100, (5 << 1) | 1},
{48000, (6 << 1) | 0}
};
int i, n, selected = -1;
n = sizeof (speed_table) / sizeof (speed_struct);
if (arg < speed_table[0].speed)
selected = 0;
if (arg > speed_table[n - 1].speed)
selected = n - 1;
for (i = 1 /*really*/ ; selected == -1 && i < n; i++)
if (speed_table[i].speed == arg)
selected = i;
else if (speed_table[i].speed > arg)
{
int diff1, diff2;
diff1 = arg - speed_table[i - 1].speed;
diff2 = speed_table[i].speed - arg;
if (diff1 < diff2)
selected = i - 1;
else
selected = i;
}
if (selected == -1)
{
printk ("ad1848: Can't find speed???\n");
selected = 3;
}
devc->speed = speed_table[selected].speed;
devc->speed_bits = speed_table[selected].bits;
return devc->speed;
}
static int
set_channels (ad1848_info * devc, int arg)
{
if (arg != 1 && arg != 2)
return devc->channels;
devc->channels = arg;
return arg;
}
static int
set_format (ad1848_info * devc, int arg)
{
static struct format_tbl
{
int format;
unsigned char bits;
}
format2bits [] =
{
{
0, 0
}
,
{
AFMT_MU_LAW, 1
}
,
{
AFMT_A_LAW, 3
}
,
{
AFMT_IMA_ADPCM, 5
}
,
{
AFMT_U8, 0
}
,
{
AFMT_S16_LE, 2
}
,
{
AFMT_S16_BE, 6
}
,
{
AFMT_S8, 0
}
,
{
AFMT_U16_LE, 0
}
,
{
AFMT_U16_BE, 0
}
};
int i, n = sizeof (format2bits) / sizeof (struct format_tbl);
if (!(arg & ad_format_mask[devc->mode]))
arg = AFMT_U8;
devc->audio_format = arg;
for (i = 0; i < n; i++)
if (format2bits[i].format == arg)
{
if ((devc->format_bits = format2bits[i].bits) == 0)
return devc->audio_format = AFMT_U8; /* Was not supported */
return arg;
}
/* Still hanging here. Something must be terribly wrong */
devc->format_bits = 0;
return devc->audio_format = AFMT_U8;
}
static int
ad1848_ioctl (int dev, unsigned int cmd, unsigned int arg, int local)
{
ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
switch (cmd)
{
case SOUND_PCM_WRITE_RATE:
if (local)
return set_speed (devc, arg);
return IOCTL_OUT (arg, set_speed (devc, IOCTL_IN (arg)));
case SOUND_PCM_READ_RATE:
if (local)
return devc->speed;
return IOCTL_OUT (arg, devc->speed);
case SNDCTL_DSP_STEREO:
if (local)
return set_channels (devc, arg + 1) - 1;
return IOCTL_OUT (arg, set_channels (devc, IOCTL_IN (arg) + 1) - 1);
case SOUND_PCM_WRITE_CHANNELS:
if (local)
return set_channels (devc, arg);
return IOCTL_OUT (arg, set_channels (devc, IOCTL_IN (arg)));
case SOUND_PCM_READ_CHANNELS:
if (local)
return devc->channels;
return IOCTL_OUT (arg, devc->channels);
case SNDCTL_DSP_SAMPLESIZE:
if (local)
return set_format (devc, arg);
return IOCTL_OUT (arg, set_format (devc, IOCTL_IN (arg)));
case SOUND_PCM_READ_BITS:
if (local)
return devc->audio_format;
return IOCTL_OUT (arg, devc->audio_format);
default:;
}
return RET_ERROR (EINVAL);
}
static void
ad1848_output_block (int dev, unsigned long buf, int count, int intrflag, int dma_restart)
{
unsigned long flags, cnt;
ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
cnt = count;
if (devc->audio_format == AFMT_IMA_ADPCM)
{
cnt /= 4;
}
else
{
if (devc->audio_format & (AFMT_S16_LE | AFMT_S16_BE)) /* 16 bit data */
cnt >>= 1;
}
if (devc->channels > 1)
cnt >>= 1;
cnt--;
if (audio_devs[dev]->flags & DMA_AUTOMODE &&
intrflag &&
cnt == devc->xfer_count)
{
devc->irq_mode = IMODE_OUTPUT;
devc->intr_active = 1;
return; /*
* Auto DMA mode on. No need to react
*/
}
DISABLE_INTR (flags);
if (dma_restart)
{
ad1848_halt (dev);
DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE);
}
ad_set_MCE (devc, 1);
ad_write (devc, 15, (unsigned char) (cnt & 0xff));
ad_write (devc, 14, (unsigned char) ((cnt >> 8) & 0xff));
ad_write (devc, 9, 0x0d); /*
* Playback enable, single DMA channel mode,
* auto calibration on.
*/
ad_set_MCE (devc, 0); /*
* Starts the calibration process and
* enters playback mode after it.
*/
wait_for_calibration (devc);
devc->xfer_count = cnt;
devc->irq_mode = IMODE_OUTPUT;
devc->intr_active = 1;
RESTORE_INTR (flags);
}
static void
ad1848_start_input (int dev, unsigned long buf, int count, int intrflag, int dma_restart)
{
unsigned long flags, cnt;
ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
/* int count_reg = (devc->mode == 1) ? 14 : 30; */
cnt = count;
if (devc->audio_format == AFMT_IMA_ADPCM)
{
cnt /= 4;
}
else
{
if (devc->audio_format & (AFMT_S16_LE | AFMT_S16_BE)) /* 16 bit data */
cnt >>= 1;
}
if (devc->channels > 1)
cnt >>= 1;
cnt--;
if (audio_devs[dev]->flags & DMA_AUTOMODE &&
intrflag &&
cnt == devc->xfer_count)
{
devc->irq_mode = IMODE_INPUT;
devc->intr_active = 1;
return; /*
* Auto DMA mode on. No need to react
*/
}
DISABLE_INTR (flags);
if (dma_restart)
{
ad1848_halt (dev);
DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ);
}
ad_set_MCE (devc, 1);
#if 0
ad_write (devc, count_reg + 1, (unsigned char) (cnt & 0xff));
ad_write (devc, count_reg, (unsigned char) ((cnt >> 8) & 0xff));
#else
ad_write (devc, 15, (unsigned char) (cnt & 0xff));
ad_write (devc, 14, (unsigned char) ((cnt >> 8) & 0xff));
if (devc->mode == 2)
{
ad_write (devc, 31, (unsigned char) (cnt & 0xff));
ad_write (devc, 32, (unsigned char) ((cnt >> 8) & 0xff));
}
#endif
ad_write (devc, 9, 0x0e); /*
* Capture enable, single DMA channel mode,
* auto calibration on.
*/
ad_set_MCE (devc, 0); /*
* Starts the calibration process and
* enters playback mode after it.
*/
wait_for_calibration (devc);
devc->xfer_count = cnt;
devc->irq_mode = IMODE_INPUT;
devc->intr_active = 1;
RESTORE_INTR (flags);
}
static int
ad1848_prepare_for_IO (int dev, int bsize, int bcount)
{
int timeout;
unsigned char fs;
unsigned long flags;
ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
DISABLE_INTR (flags);
ad_set_MCE (devc, 1); /* Enables changes to the format select reg */
fs = devc->speed_bits | (devc->format_bits << 5);
if (devc->channels > 1)
fs |= 0x10;
ad_write (devc, 8, fs);
/*
* Write to I8 starts resyncronization. Wait until it completes.
*/
timeout = 10000;
while (timeout > 0 && INB (devc->base) == 0x80)
timeout--;
ad_set_MCE (devc, 0); /*
* Starts the calibration process and
* enters playback mode after it.
*/
wait_for_calibration (devc);
RESTORE_INTR (flags);
/*
* If mode == 2 (CS4231), set I28 also. It's the capture format register.
*/
if (devc->mode == 2)
{
ad_set_MCE (devc, 1);
ad_write (devc, 28, fs);
/*
* Write to I28 starts resyncronization. Wait until it completes.
*/
timeout = 10000;
while (timeout > 0 && INB (devc->base) == 0x80)
timeout--;
ad_set_MCE (devc, 0); /*
* Starts the calibration process and
* enters playback mode after it.
*/
wait_for_calibration (devc);
RESTORE_INTR (flags);
}
devc->xfer_count = 0;
return 0;
}
static void
ad1848_reset (int dev)
{
ad1848_halt (dev);
}
static void
ad1848_halt (int dev)
{
ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc;
ad_write (devc, 9, 0); /* Clear the PEN and CEN bits (among others) */
OUTB (0, io_Status (devc)); /* Clear interrupt status */
}
int
ad1848_detect (int io_base)
{
#define DDB(x) x
unsigned char tmp;
int i;
ad1848_info *devc = &dev_info[nr_ad1848_devs];
unsigned char tmp1 = 0xff, tmp2 = 0xff;
if (nr_ad1848_devs >= MAX_AUDIO_DEV)
return 0;
devc->base = io_base;
devc->MCE_bit = 0x40;
devc->irq = 0;
devc->dma_capture = 0;
devc->dma_playback = 0;
devc->opened = 0;
devc->chip_name = "AD1848";
devc->mode = 1; /* MODE1 = original AD1848 */
/*
* Check that the I/O address is in use.
*
* The bit 0x80 of the base I/O port is known to be 0 after the
* chip has performed it's power on initialization. Just assume
* this has happened before the OS is starting.
*
* If the I/O address is unused, it typically returns 0xff.
*/
if ((INB (devc->base) & 0x80) != 0x00) /* Not a AD1884 */
{
DDB (printk ("ad_detect_A\n"));
return 0;
}
/*
* Test if it's possible to change contents of the indirect registers.
* Registers 0 and 1 are ADC volume registers. The bit 0x10 is read only
* so try to avoid using it.
*/
ad_write (devc, 0, 0xaa);
ad_write (devc, 1, 0x45); /* 0x55 with bit 0x10 clear */
if ((tmp1 = ad_read (devc, 0)) != 0xaa || (tmp2 = ad_read (devc, 1)) != 0x45)
{
DDB (printk ("ad_detect_B (%x/%x)\n", tmp1, tmp2));
return 0;
}
ad_write (devc, 0, 0x45);
ad_write (devc, 1, 0xaa);
if ((tmp1 = ad_read (devc, 0)) != 0x45 || (tmp2 = ad_read (devc, 1)) != 0xaa)
{
DDB (printk ("ad_detect_C (%x/%x)\n", tmp1, tmp2));
return 0;
}
/*
* The indirect register I12 has some read only bits. Lets
* try to change them.
*/
tmp = ad_read (devc, 12);
ad_write (devc, 12, (~tmp) & 0x0f);
if ((tmp & 0x0f) != ((tmp1 = ad_read (devc, 12)) & 0x0f))
{
DDB (printk ("ad_detect_D (%x)\n", tmp1));
return 0;
}
/*
* NOTE! Last 4 bits of the reg I12 tell the chip revision.
* 0x01=RevB and 0x0A=RevC.
*/
/*
* The original AD1848/CS4248 has just 15 indirect registers. This means
* that I0 and I16 should return the same value (etc.).
* Ensure that the Mode2 enable bit of I12 is 0. Otherwise this test fails
* with CS4231.
*/
ad_write (devc, 12, 0); /* Mode2=disabled */
for (i = 0; i < 16; i++)
if ((tmp1 = ad_read (devc, i)) != (tmp2 = ad_read (devc, i + 16)))
{
DDB (printk ("ad_detect_F(%d/%x/%x)\n", i, tmp1, tmp2));
return 0;
}
/*
* Try to switch the chip to mode2 (CS4231) by setting the MODE2 bit (0x40).
* The bit 0x80 is always 1 in CS4248 and CS4231.
*/
ad_write (devc, 12, 0x40); /* Set mode2, clear 0x80 */
tmp1 = ad_read (devc, 12);
if (tmp1 & 0x80)
devc->chip_name = "CS4248";
if ((tmp1 & 0xc0) == (0x80 | 0x40))
{
/*
* CS4231 detected - is it?
*
* Verify that setting I0 doesn't change I16.
*/
ad_write (devc, 16, 0); /* Set I16 to known value */
ad_write (devc, 0, 0x45);
if ((tmp1 = ad_read (devc, 16)) != 0x45) /* No change -> CS4231? */
{
ad_write (devc, 0, 0xaa);
if ((tmp1 = ad_read (devc, 16)) == 0xaa) /* Rotten bits? */
{
DDB (printk ("ad_detect_H(%x)\n", tmp1));
return 0;
}
/*
* It's a CS4231 - So what!
* (Mode2 will be supported later)
*/
devc->chip_name = "CS4231";
devc->mode = 2;
}
}
return 1;
}
void
ad1848_init (char *name, int io_base, int irq, int dma_playback, int dma_capture)
{
/*
* NOTE! If irq < 0, there is another driver which has allocated the IRQ
* so that this driver doesn't need to allocate/deallocate it.
* The actually used IRQ is ABS(irq).
*/
/*
* Initial values for the indirect registers of CS4248/AD1848.
*/
static int init_values[] =
{
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00,
0x00, 0x08, 0x02, 0x00, 0xca, 0x00, 0x00, 0x00
};
int i, my_dev;
ad1848_info *devc = &dev_info[nr_ad1848_devs];
if (!ad1848_detect (io_base))
return;
devc->irq = (irq > 0) ? irq : 0;
devc->dma_capture = dma_playback;
devc->dma_playback = dma_capture;
devc->opened = 0;
if (nr_ad1848_devs != 0)
{
memcpy ((char *) &ad1848_pcm_operations[nr_ad1848_devs],
(char *) &ad1848_pcm_operations[0],
sizeof (struct audio_operations));
}
for (i = 0; i < 16; i++)
ad_write (devc, i, init_values[i]);
OUTB (0, io_Status (devc)); /* Clear pending interrupts */
#ifndef SCO
sprintf (ad1848_pcm_operations[nr_ad1848_devs].name,
"%s (%s)", name, devc->chip_name);
#endif
if (irq > 0)
printk (" <%s>", ad1848_pcm_operations[nr_ad1848_devs].name);
if (num_audiodevs < MAX_AUDIO_DEV)
{
audio_devs[my_dev = num_audiodevs++] = &ad1848_pcm_operations[nr_ad1848_devs];
if (irq > 0)
irq2dev[irq] = my_dev;
else if (irq < 0)
irq2dev[-irq] = my_dev;
audio_devs[my_dev]->dmachan = dma_playback;
audio_devs[my_dev]->buffcount = 1;
audio_devs[my_dev]->buffsize = DSP_BUFFSIZE * 2;
audio_devs[my_dev]->devc = devc;
audio_devs[my_dev]->format_mask = ad_format_mask[devc->mode];
nr_ad1848_devs++;
}
else
printk ("AD1848: Too many PCM devices available\n");
}
void
ad1848_interrupt (int irq)
{
unsigned char status;
ad1848_info *devc;
int dev;
if (irq < 0 || irq > 15)
return; /* Bogus irq */
dev = irq2dev[irq];
if (dev < 0 || dev >= num_audiodevs)
return; /* Bogus dev */
devc = (ad1848_info *) audio_devs[dev]->devc;
status = INB (io_Status (devc));
if (status & 0x01)
{
if (devc->opened && devc->irq_mode == IMODE_OUTPUT)
{
DMAbuf_outputintr (dev, 1);
}
if (devc->opened && devc->irq_mode == IMODE_INPUT)
DMAbuf_inputintr (dev);
}
OUTB (0, io_Status (devc)); /* Clear interrupt status */
}
#endif
/*
* Some extra code for the MS Sound System
*/
#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_MSS)
int
probe_ms_sound (struct address_info *hw_config)
{
if ((INB (hw_config->io_base + 3) & 0x04) == 0)
return 0; /* WSS ID test failed */
if (hw_config->irq > 11)
return 0;
if (hw_config->dma != 0 && hw_config->dma != 1 && hw_config->dma != 3)
return 0;
return ad1848_detect (hw_config->io_base + 4);
}
long
attach_ms_sound (long mem_start, struct address_info *hw_config)
{
static unsigned char interrupt_bits[12] =
{-1, -1, -1, -1, -1, -1, -1, 0x08, -1, 0x10, 0x18, 0x20};
char bits;
static unsigned char dma_bits[4] = {1, 2, 0, 3};
int config_port = hw_config->io_base + 0, version_port = hw_config->io_base + 3;
if (!ad1848_detect (hw_config->io_base + 4))
return mem_start;
/*
* Set the IRQ and DMA addresses.
*/
bits = interrupt_bits[hw_config->irq];
if (bits == -1)
return mem_start;
OUTB (bits | 0x40, config_port); /* Verify IRQ (I guess) */
if ((INB (version_port) & 0x40) == 0)
printk ("[IRQ?]");
OUTB (bits | dma_bits[hw_config->dma], config_port); /* Write IRQ+DMA setup */
ad1848_init ("MS Sound System", hw_config->io_base + 4,
hw_config->irq,
hw_config->dma,
hw_config->dma);
return mem_start;
}
#endif

View File

@ -0,0 +1,830 @@
/*
* sound/configure.c - Configuration program for the Linux Sound Driver
*
* Copyright by Hannu Savolainen 1993
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met: 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer. 2.
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#include <stdio.h>
#define B(x) (1 << (x))
/*
* Option numbers
*/
#define OPT_PAS 0
#define OPT_SB 1
#define OPT_ADLIB 2
#define OPT_LAST_MUTUAL 2
#define OPT_GUS 3
#define OPT_MPU401 4
#define OPT_UART6850 5
#define OPT_PSS 6
#define OPT_GUS16 7
#define OPT_GUSMAX 8
#define OPT_MSS 9
#define OPT_HIGHLEVEL 10 /* This must be same than the next one */
#define OPT_SBPRO 10
#define OPT_SB16 11
#define OPT_AUDIO 12
#define OPT_MIDI_AUTO 13
#define OPT_MIDI 14
#define OPT_YM3812_AUTO 15
#define OPT_YM3812 16
#define OPT_SEQUENCER 17
#define OPT_LAST 17 /* Must be the same than the defined OPT */
#define ANY_DEVS (B(OPT_AUDIO)|B(OPT_MIDI)|B(OPT_SEQUENCER)|B(OPT_GUS)| \
B(OPT_MPU401)|B(OPT_PSS)|B(OPT_GUS16)|B(OPT_GUSMAX)|B(OPT_MSS))
/*
* Options that have been disabled for some reason (incompletely implemented
* and/or tested). Don't remove from this list before looking at file
* experimental.txt for further info.
*/
#define DISABLED_OPTIONS (B(OPT_PSS))
typedef struct
{
unsigned long conditions;
unsigned long exclusive_options;
char macro[20];
int verify;
int alias;
int default_answ;
}
hw_entry;
/*
* The rule table for the driver options. The first field defines a set of
* options which must be selected before this entry can be selected. The
* second field is a set of options which are not allowed with this one. If
* the fourth field is zero, the option is selected without asking
* confirmation from the user.
*
* With this version of the rule table it is possible to select just one type of
* hardware.
*
* NOTE! Keep the following table and the questions array in sync with the
* option numbering!
*/
hw_entry hw_table[] =
{
/*
* 0
*/
{0, 0, "PAS", 1, 0, 0},
{0, 0, "SB", 1, 0, 0},
{0, B (OPT_PAS) | B (OPT_SB), "ADLIB", 1, 0, 0},
{0, 0, "GUS", 1, 0, 0},
{0, 0, "MPU401", 1, 0, 0},
{0, 0, "UART6850", 1, 0, 0},
{0, 0, "PSS", 1, 0, 0},
{B (OPT_GUS), 0, "GUS16", 1, 0, 0},
{B (OPT_GUS), B (OPT_GUS16), "GUSMAX", 1, 0, 0},
{0, 0, "MSS", 1, 0, 0},
{B (OPT_SB), B (OPT_PAS), "SBPRO", 1, 0, 1},
{B (OPT_SB) | B (OPT_SBPRO), B (OPT_PAS), "SB16", 1, 0, 1},
{B (OPT_PSS) | B (OPT_SB) | B (OPT_PAS) | B (OPT_GUS), 0, "AUDIO", 1, 0, 1},
{B (OPT_MPU401), 0, "MIDI_AUTO", 0, OPT_MIDI, 0},
{B (OPT_PSS) | B (OPT_SB) | B (OPT_PAS) | B (OPT_MPU401) | B (OPT_GUS), 0, "MIDI", 1, 0, 1},
{B (OPT_ADLIB), 0, "YM3812_AUTO", 0, OPT_YM3812, 0},
{B (OPT_PSS) | B (OPT_SB) | B (OPT_PAS) | B (OPT_ADLIB), B (OPT_YM3812_AUTO), "YM3812", 1, 0, 1},
{B (OPT_MIDI) | B (OPT_YM3812) | B (OPT_YM3812_AUTO) | B (OPT_GUS), 0, "SEQUENCER", 0, 0, 1}
};
char *questions[] =
{
"ProAudioSpectrum 16 support",
"SoundBlaster support",
"AdLib support",
"Gravis Ultrasound support",
"MPU-401 support (NOT for SB16)",
"6850 UART Midi support",
"PSS (ECHO-ADI2111) support",
"16 bit sampling option of GUS (_NOT_ GUS MAX)",
"GUS MAX support",
"Microsoft Sound System support",
"SoundBlaster Pro support",
"SoundBlaster 16 support",
"digitized voice support",
"This should not be asked",
"MIDI interface support",
"This should not be asked",
"FM synthesizer (YM3812/OPL-3) support",
"/dev/sequencer support",
"Should I die"
};
unsigned long selected_options = 0;
int sb_dma = 0;
int
can_select_option (int nr)
{
switch (nr)
{
case 0:
fprintf (stderr, "The SoundBlaster, AdLib and ProAudioSpectrum\n"
"CARDS cannot be installed at the same time.\n\n"
"However the PAS16 has a SB emulator so you could select"
"the SoundBlaster DRIVER with it.\n");
fprintf (stderr, " - ProAudioSpectrum 16\n");
fprintf (stderr, " - SoundBlaster / SB Pro\n");
fprintf (stderr, " (Could be selected with a PAS16 also)\n");
fprintf (stderr, " - AdLib\n");
fprintf (stderr, "\nDon't enable SoundBlaster if you have GUS at 0x220!\n\n");
break;
case OPT_LAST_MUTUAL + 1:
fprintf (stderr, "\nThe following cards should work with any other cards.\n"
"CAUTION! Don't enable MPU-401 if you don't have it.\n");
break;
case OPT_HIGHLEVEL:
fprintf (stderr, "\nSelect one or more of the following options\n");
break;
}
if (hw_table[nr].conditions)
if (!(hw_table[nr].conditions & selected_options))
return 0;
if (hw_table[nr].exclusive_options)
if (hw_table[nr].exclusive_options & selected_options)
return 0;
if (DISABLED_OPTIONS & B (nr))
return 0;
return 1;
}
int
think_positively (int def_answ)
{
char answ[512];
int len;
if ((len = read (0, &answ, sizeof (answ))) < 1)
{
fprintf (stderr, "\n\nERROR! Cannot read stdin\n");
perror ("stdin");
printf ("#undef CONFIGURE_SOUNDCARD\n");
printf ("#undef KERNEL_SOUNDCARD\n");
exit (-1);
}
if (len < 2) /*
* There is an additional LF at the end
*/
return def_answ;
answ[len - 1] = 0;
if (!strcmp (answ, "y") || !strcmp (answ, "Y"))
return 1;
return 0;
}
int
ask_value (char *format, int default_answer)
{
char answ[512];
int len, num;
play_it_again_Sam:
if ((len = read (0, &answ, sizeof (answ))) < 1)
{
fprintf (stderr, "\n\nERROR! Cannot read stdin\n");
perror ("stdin");
printf ("#undef CONFIGURE_SOUNDCARD\n");
printf ("#undef KERNEL_SOUNDCARD\n");
exit (-1);
}
if (len < 2) /*
* There is an additional LF at the end
*/
return default_answer;
answ[len - 1] = 0;
if (sscanf (answ, format, &num) != 1)
{
fprintf (stderr, "Illegal format. Try again: ");
goto play_it_again_Sam;
}
return num;
}
int
main (int argc, char *argv[])
{
int i, num, def_size, full_driver = 1;
char answ[10];
printf ("/*\tGenerated by configure. Don't edit!!!!\t*/\n\n");
fprintf (stderr, "\nConfiguring the sound support\n\n");
fprintf (stderr, "Do you want to include full version of the sound driver (n/y) ? ");
if (think_positively (0))
{
/*
* Select all but some most dangerous cards. These cards are difficult to
* detect reliably or conflict with some other cards (SCSI, Mitsumi)
*/
selected_options = 0xffffffff &
~(B (OPT_MPU401) | B (OPT_UART6850) | B (OPT_PSS)) &
~DISABLED_OPTIONS;
fprintf (stderr, "Note! MPU-401, PSS and 6850 UART drivers not enabled\n");
full_driver = 1;
}
else
{
fprintf (stderr, "Do you want to DISABLE the Sound Driver (n/y) ?");
if (think_positively (0))
{
printf ("#undef CONFIGURE_SOUNDCARD\n");
printf ("#undef KERNEL_SOUNDCARD\n");
exit (0);
}
/*
* Partial driver
*/
full_driver = 0;
for (i = 0; i <= OPT_LAST; i++)
if (can_select_option (i))
{
if (!(selected_options & B (i))) /*
* Not selected yet
*/
if (!hw_table[i].verify)
{
if (hw_table[i].alias)
selected_options |= B (hw_table[i].alias);
else
selected_options |= B (i);
}
else
{
int def_answ = hw_table[i].default_answ;
fprintf (stderr,
def_answ ? " %s (y/n) ? " : " %s (n/y) ? ",
questions[i]);
if (think_positively (def_answ))
if (hw_table[i].alias)
selected_options |= B (hw_table[i].alias);
else
selected_options |= B (i);
}
}
}
if (selected_options & B (OPT_SBPRO))
{
fprintf(stderr, "Do you want support for the mixer of SG NX Pro ? ");
if (think_positively (0))
printf("#define __SGNXPRO__\n");
}
if (selected_options & B (OPT_SB16))
selected_options |= B (OPT_SBPRO);
if (selected_options & B (OPT_PSS))
{
genld_again:
fprintf
(stderr,
"if you wish to emulate the soundblaster and you have a DSPxxx.LD.\n"
"then you must include the LD in the kernel.\n"
"(do you wish to include a LD) ? ");
if (think_positively (0))
{
char path[512];
fprintf (stderr,
"Enter the path to your LD file (pwd is sound): ");
scanf ("%s", path);
fprintf (stderr, "including LD file %s\n", path);
selected_options |= B (OPT_SB) | B (OPT_MPU401) | B (OPT_ADLIB);
/* Gen LD header */
{
int fd;
int count;
char c;
int i = 0;
if ((fd = open (path, 0)) > 0)
{
FILE *sf = fopen ("synth-ld.h", "w");
fprintf (sf, "/* automaticaly generated by configure */\n");
fprintf (sf, "unsigned char pss_synth[] = {\n");
while (1)
{
count = read (fd, &c, 1);
if (count == 0)
break;
if (i != 0 && (i % 10) == 0)
fprintf (sf, "\n");
fprintf (sf, "0x%02x,", c & 0xFFL);
i++;
}
fprintf (sf, "};\n"
"#define pss_synthLen %d\n", i);
fclose (sf);
close (fd);
}
else
{
fprintf (stderr, "couldn't open %s as the ld file\n",
path);
fprintf (stderr, "try again with correct path? ");
if (think_positively (1))
goto genld_again;
}
}
}
else
{
FILE *sf = fopen ("synth-ld.h", "w");
fprintf (sf, "/* automaticaly generated by configure */\n");
fprintf (sf, "unsigned char pss_synth[1];\n"
"#define pss_synthLen 0\n");
fclose (sf);
}
}
if (!(selected_options & ANY_DEVS))
{
printf ("#undef CONFIGURE_SOUNDCARD\n");
printf ("#undef KERNEL_SOUNDCARD\n");
fprintf (stderr, "\n*** This combination is useless. Sound driver disabled!!! ***\n\n");
exit (0);
}
else
printf ("#define KERNEL_SOUNDCARD\n");
for (i = 0; i <= OPT_LAST; i++)
if (!hw_table[i].alias)
if (selected_options & B (i))
printf ("#undef EXCLUDE_%s\n", hw_table[i].macro);
else
printf ("#define EXCLUDE_%s\n", hw_table[i].macro);
/*
* IRQ and DMA settings
*/
printf ("\n");
#if defined(linux)
if ((selected_options & B (OPT_SB)) && selected_options & (B (OPT_AUDIO) | B (OPT_MIDI)))
{
fprintf (stderr, "\nI/O base for SB?\n"
"The factory default is 220\n"
"Enter the SB I/O base: ");
num = ask_value ("%x", 0x220);
fprintf (stderr, "SB I/O base set to %03x\n", num);
printf ("#define SBC_BASE 0x%03x\n", num);
fprintf (stderr, "\nIRQ number for SoundBlaster?\n"
"The IRQ address is defined by the jumpers on your card.\n"
"The factory default is either 5 or 7 (depending on the model).\n"
"Valid values are 9(=2), 5, 7 and 10.\n"
"Enter the value: ");
num = ask_value ("%d", 7);
if (num != 9 && num != 5 && num != 7 && num != 10)
{
fprintf (stderr, "*** Illegal input! ***\n");
num = 7;
}
fprintf (stderr, "SoundBlaster IRQ set to %d\n", num);
printf ("#define SBC_IRQ %d\n", num);
if (selected_options & (B (OPT_SBPRO) | B (OPT_PAS) | B (OPT_PSS)))
{
fprintf (stderr, "\nDMA channel for SoundBlaster?\n"
"For SB 1.0, 1.5 and 2.0 this MUST be 1\n"
"SB Pro supports DMA channels 0, 1 and 3 (jumper)\n"
"For SB16 give the 8 bit DMA# here\n"
"The default value is 1\n"
"Enter the value: ");
num = ask_value ("%d", 1);
if (num < 0 || num > 3)
{
fprintf (stderr, "*** Illegal input! ***\n");
num = 1;
}
fprintf (stderr, "SoundBlaster DMA set to %d\n", num);
printf ("#define SBC_DMA %d\n", num);
sb_dma = num;
}
if (selected_options & B (OPT_SB16))
{
fprintf (stderr, "\n16 bit DMA channel for SoundBlaster 16?\n"
"Possible values are 5, 6 or 7\n"
"The default value is 6\n"
"Enter the value: ");
num = ask_value ("%d", 6);
if ((num < 5 || num > 7) && (num != sb_dma))
{
fprintf (stderr, "*** Illegal input! ***\n");
num = 6;
}
fprintf (stderr, "SoundBlaster DMA set to %d\n", num);
printf ("#define SB16_DMA %d\n", num);
fprintf (stderr, "\nI/O base for SB16 Midi?\n"
"Possible values are 300 and 330\n"
"The factory default is 330\n"
"Enter the SB16 Midi I/O base: ");
num = ask_value ("%x", 0x330);
fprintf (stderr, "SB16 Midi I/O base set to %03x\n", num);
printf ("#define SB16MIDI_BASE 0x%03x\n", num);
}
}
if (selected_options & B (OPT_PAS))
{
if (selected_options & (B (OPT_AUDIO) | B (OPT_MIDI)))
{
fprintf (stderr, "\nIRQ number for ProAudioSpectrum?\n"
"The recommended value is the IRQ used under DOS.\n"
"Please refer to the ProAudioSpectrum User's Guide.\n"
"The default value is 10.\n"
"Enter the value: ");
num = ask_value ("%d", 10);
if (num == 6 || num < 3 || num > 15 || num == 2) /*
* Illegal
*/
{
fprintf (stderr, "*** Illegal input! ***\n");
num = 10;
}
fprintf (stderr, "ProAudioSpectrum IRQ set to %d\n", num);
printf ("#define PAS_IRQ %d\n", num);
}
if (selected_options & B (OPT_AUDIO))
{
fprintf (stderr, "\nDMA number for ProAudioSpectrum?\n"
"The recommended value is the DMA channel under DOS.\n"
"Please refer to the ProAudioSpectrum User's Guide.\n"
"The default value is 3\n"
"Enter the value: ");
num = ask_value ("%d", 3);
if (num == 4 || num < 0 || num > 7)
{
fprintf (stderr, "*** Illegal input! ***\n");
num = 3;
}
fprintf (stderr, "\nProAudioSpectrum DMA set to %d\n", num);
printf ("#define PAS_DMA %d\n", num);
}
}
if (selected_options & B (OPT_GUS))
{
fprintf (stderr, "\nI/O base for Gravis Ultrasound?\n"
"Valid choices are 210, 220, 230, 240, 250 or 260\n"
"The factory default is 220\n"
"Enter the GUS I/O base: ");
num = ask_value ("%x", 0x220);
if ((num > 0x260) || ((num & 0xf0f) != 0x200) || ((num & 0x0f0) > 0x060))
{
fprintf (stderr, "*** Illegal input! ***\n");
num = 0x220;
}
if ((selected_options & B (OPT_SB)) && (num == 0x220))
{
fprintf (stderr, "FATAL ERROR!!!!!!!!!!!!!!\n"
"\t0x220 cannot be used if SoundBlaster is enabled.\n"
"\tRun the config again.\n");
printf ("#undef CONFIGURE_SOUNDCARD\n");
printf ("#undef KERNEL_SOUNDCARD\n");
exit (-1);
}
fprintf (stderr, "GUS I/O base set to %03x\n", num);
printf ("#define GUS_BASE 0x%03x\n", num);
fprintf (stderr, "\nIRQ number for Gravis UltraSound?\n"
"The recommended value is the IRQ used under DOS.\n"
"Please refer to the Gravis Ultrasound User's Guide.\n"
"The default value is 15.\n"
"Enter the value: ");
num = ask_value ("%d", 15);
if (num == 6 || num < 3 || num > 15 || num == 2) /*
* Invalid
*/
{
fprintf (stderr, "*** Illegal input! ***\n");
num = 15;
}
fprintf (stderr, "Gravis UltraSound IRQ set to %d\n", num);
printf ("#define GUS_IRQ %d\n", num);
fprintf (stderr, "\nDMA number for Gravis UltraSound?\n"
"The recommended value is the DMA channel under DOS.\n"
"Please refer to the Gravis Ultrasound User's Guide.\n"
"The default value is 6\n"
"Enter the value: ");
num = ask_value ("%d", 6);
if (num == 4 || num < 0 || num > 7)
{
fprintf (stderr, "*** Illegal input! ***\n");
num = 6;
}
fprintf (stderr, "\nGravis UltraSound DMA set to %d\n", num);
printf ("#define GUS_DMA %d\n", num);
}
if (selected_options & B (OPT_MPU401))
{
fprintf (stderr, "\nI/O base for MPU-401?\n"
"The factory default is 330\n"
"Enter the MPU-401 I/O base: ");
num = ask_value ("%x", 0x330);
fprintf (stderr, "MPU-401 I/O base set to %03x\n", num);
printf ("#define MPU_BASE 0x%03x\n", num);
fprintf (stderr, "\nIRQ number for MPU-401?\n"
"Valid numbers are: 3, 4, 5, 7 and 9(=2).\n"
"The default value is 9.\n"
"Enter the value: ");
num = ask_value ("%d", 9);
if (num == 6 || num < 3 || num > 15) /*
* Used for floppy
*/
{
fprintf (stderr, "*** Illegal input! ***\n");
num = 5;
}
fprintf (stderr, "MPU-401 IRQ set to %d\n", num);
printf ("#define MPU_IRQ %d\n", num);
}
if (selected_options & B (OPT_UART6850))
{
fprintf (stderr, "\nI/O base for 6850 UART Midi?\n"
"Be carefull. No defaults.\n"
"Enter the 6850 UART I/O base: ");
num = ask_value ("%x", 0);
if (num == 0)
{
/*
* Invalid value entered
*/
printf ("#define EXCLUDE_UART6850\n");
}
else
{
fprintf (stderr, "6850 UART I/O base set to %03x\n", num);
printf ("#define U6850_BASE 0x%03x\n", num);
fprintf (stderr, "\nIRQ number for 6850 UART?\n"
"Valid numbers are: 3, 4, 5, 7 and 9(=2).\n"
"The default value is 5.\n"
"Enter the value: ");
num = ask_value ("%d", 5);
if (num == 6 || num < 3 || num > 15) /*
* Used for floppy
*/
{
fprintf (stderr, "*** Illegal input! ***\n");
num = 5;
}
fprintf (stderr, "6850 UART IRQ set to %d\n", num);
printf ("#define U6850_IRQ %d\n", num);
}
}
if (selected_options & B (OPT_PSS))
{
fprintf (stderr, "\nI/O base for PSS?\n"
"The factory default is 220\n"
"Enter the PSS I/O base: ");
num = ask_value ("%x", 0x220);
fprintf (stderr, "PSS I/O base set to %03x\n", num);
printf ("#define PSS_BASE 0x%03x\n", num);
fprintf (stderr, "\nIRQ number for PSS?\n"
"Valid numbers are: 3, 4, 5, 7, 9(=2) or 10.\n"
"The default value is 10.\n"
"Enter the value: ");
num = ask_value ("%d", 10);
if (num == 6 || num < 3 || num > 15) /* Used for floppy */
{
fprintf (stderr, "*** Illegal input! ***\n");
num = 7;
}
fprintf (stderr, "PSS IRQ set to %d\n", num);
printf ("#define PSS_IRQ %d\n", num);
fprintf (stderr, "\nDMA number for ECHO-PSS?\n"
"The default value is 3\n"
"Enter the value: ");
num = ask_value ("%d", 3);
if (num == 4 || num < 0 || num > 7)
{
fprintf (stderr, "*** Illegal input! ***\n");
num = 3;
}
fprintf (stderr, "\nECHO-PSS DMA set to %d\n", num);
printf ("#define PSS_DMA %d\n", num);
}
if (selected_options & B (OPT_MSS))
{
fprintf (stderr, "\nI/O base for MSS (MS Sound System)?\n"
"The factory default is 530\n"
"Other possible values are 604, E80 or F40\n"
"Enter the MSS I/O base: ");
num = ask_value ("%x", 0x530);
fprintf (stderr, "MSS I/O base set to %03x\n", num);
printf ("#define MSS_BASE 0x%03x\n", num);
fprintf (stderr, "\nIRQ number for MSS?\n"
"Valid numbers are: 7, 9(=2), 10 and 11.\n"
"The default value is 10.\n"
"Enter the value: ");
num = ask_value ("%d", 10);
if (num == 6 || num < 3 || num > 15) /* Used for floppy */
{
fprintf (stderr, "*** Illegal input! ***\n");
num = 7;
}
fprintf (stderr, "MSS IRQ set to %d\n", num);
printf ("#define MSS_IRQ %d\n", num);
fprintf (stderr, "\nDMA number for MSS?\n"
"Valid values are 1 and 3 (sometimes 0)"
"The default value is 3\n"
"Enter the value: ");
num = ask_value ("%d", 3);
if (num == 4 || num < 0 || num > 7)
{
fprintf (stderr, "*** Illegal input! ***\n");
num = 3;
}
fprintf (stderr, "\nMSS DMA set to %d\n", num);
printf ("#define MSS_DMA %d\n", num);
}
if (selected_options & B (OPT_GUS16))
{
fprintf (stderr, "\nI/O base for GUS16 (GUS 16 bit sampling option)?\n"
"The factory default is 530\n"
"Other possible values are 604, E80 or F40\n"
"Enter the GUS16 I/O base: ");
num = ask_value ("%x", 0x530);
fprintf (stderr, "GUS16 I/O base set to %03x\n", num);
printf ("#define GUS16_BASE 0x%03x\n", num);
fprintf (stderr, "\nIRQ number for GUS16?\n"
"Valid numbers are: 3, 4, 5, 7, or 9(=2).\n"
"The default value is 7.\n"
"Enter the value: ");
num = ask_value ("%d", 7);
if (num == 6 || num < 3 || num > 15) /* Used for floppy */
{
fprintf (stderr, "*** Illegal input! ***\n");
num = 7;
}
fprintf (stderr, "GUS16 IRQ set to %d\n", num);
printf ("#define GUS16_IRQ %d\n", num);
fprintf (stderr, "\nDMA number for GUS16?\n"
"The default value is 3\n"
"Enter the value: ");
num = ask_value ("%d", 3);
if (num < 0 || num > 3)
{
fprintf (stderr, "*** Illegal input! ***\n");
num = 3;
}
fprintf (stderr, "\nGUS16 DMA set to %d\n", num);
printf ("#define GUS16_DMA %d\n", num);
}
#endif
if (selected_options & B (OPT_AUDIO))
{
def_size = 16384;
if (selected_options & (B (OPT_SBPRO) | B (OPT_PAS) | B (OPT_SB16)))
def_size = 32768;
#ifndef __386BSD__
if ((selected_options & (B (OPT_PAS) | B (OPT_PAS) | B (OPT_GUS16) | B (OPT_GUSMAX) |
B (OPT_MSS) | B (OPT_PSS))) &&
!full_driver)
def_size = 65536; /*
* PAS16 or SB16
*/
#endif
fprintf (stderr, "\nSelect the DMA buffer size (4096, 16384, 32768 or 65536 bytes)\n"
"%d is recommended value for this configuration.\n"
"Enter the value: ", def_size);
num = ask_value ("%d", def_size);
if (num != 4096 && num != 16384 && num != 32768 && num != 65536)
{
fprintf (stderr, "*** Illegal input! ***\n");
num = def_size;
}
fprintf (stderr, "The DMA buffer size set to %d\n", num);
printf ("#define DSP_BUFFSIZE %d\n", num);
}
printf ("#define SELECTED_SOUND_OPTIONS\t0x%08x\n", selected_options);
fprintf (stderr, "The sound driver is now configured.\n");
#if defined(SCO) || defined(ISC) || defined(SYSV)
fprintf (stderr, "Remember to update the System file\n");
#endif
exit (0);
}

View File

@ -0,0 +1,22 @@
static unsigned char ctrl_def_values[128] =
{
0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 0 to 7 */
0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 8 to 15 */
0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 16 to 23 */
0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 24 to 31 */
0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 32 to 39 */
0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 40 to 47 */
0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 48 to 55 */
0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 56 to 63 */
0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 64 to 71 */
0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 72 to 79 */
0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 80 to 87 */
0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 88 to 95 */
0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 96 to 103 */
0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 104 to 111 */
0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 112 to 119 */
0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, /* 120 to 127 */
};

View File

@ -0,0 +1,474 @@
/*
* sound/midi_synth.c
*
* High level midi sequencer manager for dumb MIDI interfaces.
*
* Copyright by Hannu Savolainen 1993
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met: 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer. 2.
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#include "sound_config.h"
#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_MIDI)
#define _MIDI_SYNTH_C_
DEFINE_WAIT_QUEUE (sysex_sleeper, sysex_sleep_flag);
#include "midi_synth.h"
static int midi2synth[MAX_MIDI_DEV];
static unsigned char prev_out_status[MAX_MIDI_DEV];
static void
midi_outc (int midi_dev, int data)
{
int timeout;
for (timeout = 0; timeout < 32000; timeout++)
if (midi_devs[midi_dev]->putc (midi_dev, (unsigned char) (data & 0xff)))
{
if (data & 0x80) /*
* Status byte
*/
prev_out_status[midi_dev] =
(unsigned char) (data & 0xff); /*
* Store for running status
*/
return; /*
* Mission complete
*/
}
/*
* Sorry! No space on buffers.
*/
printk ("Midi send timed out\n");
}
static int
prefix_cmd (int midi_dev, unsigned char status)
{
if (midi_devs[midi_dev]->prefix_cmd == NULL)
return 1;
return midi_devs[midi_dev]->prefix_cmd (midi_dev, status);
}
static void
midi_synth_input (int dev, unsigned char data)
{
int orig_dev;
if (dev < 0 || dev > num_synths)
return;
if (data == 0xfe) /* Ignore active sensing */
return;
orig_dev = midi2synth[dev];
}
static void
midi_synth_output (int dev)
{
/*
* Currently NOP
*/
}
int
midi_synth_ioctl (int dev,
unsigned int cmd, unsigned int arg)
{
/*
* int orig_dev = synth_devs[dev]->midi_dev;
*/
switch (cmd)
{
case SNDCTL_SYNTH_INFO:
IOCTL_TO_USER ((char *) arg, 0, synth_devs[dev]->info,
sizeof (struct synth_info));
return 0;
break;
case SNDCTL_SYNTH_MEMAVL:
return 0x7fffffff;
break;
default:
return RET_ERROR (EINVAL);
}
}
int
midi_synth_kill_note (int dev, int channel, int note, int velocity)
{
int orig_dev = synth_devs[dev]->midi_dev;
int msg, chn;
if (note < 0 || note > 127)
return 0;
if (channel < 0 || channel > 15)
return 0;
if (velocity < 0)
velocity = 0;
if (velocity > 127)
velocity = 127;
msg = prev_out_status[orig_dev] & 0xf0;
chn = prev_out_status[orig_dev] & 0x0f;
if (chn == channel && ((msg == 0x90 && velocity == 64) || msg == 0x80))
{ /*
* Use running status
*/
if (!prefix_cmd (orig_dev, note))
return 0;
midi_outc (orig_dev, note);
if (msg == 0x90) /*
* Running status = Note on
*/
midi_outc (orig_dev, 0);/*
* Note on with velocity 0 == note
* off
*/
else
midi_outc (orig_dev, velocity);
}
else
{
if (velocity == 64)
{
if (!prefix_cmd (orig_dev, 0x90 | (channel & 0x0f)))
return 0;
midi_outc (orig_dev, 0x90 | (channel & 0x0f)); /*
* Note on
*/
midi_outc (orig_dev, note);
midi_outc (orig_dev, 0); /*
* Zero G
*/
}
else
{
if (!prefix_cmd (orig_dev, 0x80 | (channel & 0x0f)))
return 0;
midi_outc (orig_dev, 0x80 | (channel & 0x0f)); /*
* Note off
*/
midi_outc (orig_dev, note);
midi_outc (orig_dev, velocity);
}
}
return 0;
}
int
midi_synth_set_instr (int dev, int channel, int instr_no)
{
int orig_dev = synth_devs[dev]->midi_dev;
if (instr_no < 0 || instr_no > 127)
return 0;
if (channel < 0 || channel > 15)
return 0;
if (!prefix_cmd (orig_dev, 0xc0 | (channel & 0x0f)))
return 0;
midi_outc (orig_dev, 0xc0 | (channel & 0x0f)); /*
* Program change
*/
midi_outc (orig_dev, instr_no);
return 0;
}
int
midi_synth_start_note (int dev, int channel, int note, int velocity)
{
int orig_dev = synth_devs[dev]->midi_dev;
int msg, chn;
if (note < 0 || note > 127)
return 0;
if (channel < 0 || channel > 15)
return 0;
if (velocity < 0)
velocity = 0;
if (velocity > 127)
velocity = 127;
msg = prev_out_status[orig_dev] & 0xf0;
chn = prev_out_status[orig_dev] & 0x0f;
if (chn == channel && msg == 0x90)
{ /*
* Use running status
*/
if (!prefix_cmd (orig_dev, note))
return 0;
midi_outc (orig_dev, note);
midi_outc (orig_dev, velocity);
}
else
{
if (!prefix_cmd (orig_dev, 0x90 | (channel & 0x0f)))
return 0;
midi_outc (orig_dev, 0x90 | (channel & 0x0f)); /*
* Note on
*/
midi_outc (orig_dev, note);
midi_outc (orig_dev, velocity);
}
return 0;
}
void
midi_synth_reset (int dev)
{
}
int
midi_synth_open (int dev, int mode)
{
int orig_dev = synth_devs[dev]->midi_dev;
int err;
if (orig_dev < 0 || orig_dev > num_midis)
return RET_ERROR (ENXIO);
midi2synth[orig_dev] = dev;
prev_out_status[orig_dev] = 0;
if ((err = midi_devs[orig_dev]->open (orig_dev, mode,
midi_synth_input, midi_synth_output)) < 0)
return err;
return 1;
}
void
midi_synth_close (int dev)
{
int orig_dev = synth_devs[dev]->midi_dev;
/*
* Shut up the synths by sending just single active sensing message.
*/
midi_devs[orig_dev]->putc (orig_dev, 0xfe);
midi_devs[orig_dev]->close (orig_dev);
}
void
midi_synth_hw_control (int dev, unsigned char *event)
{
}
int
midi_synth_load_patch (int dev, int format, snd_rw_buf * addr,
int offs, int count, int pmgr_flag)
{
int orig_dev = synth_devs[dev]->midi_dev;
struct sysex_info sysex;
int i;
unsigned long left, src_offs, eox_seen = 0;
int first_byte = 1;
if (!prefix_cmd (orig_dev, 0xf0))
return 0;
if (format != SYSEX_PATCH)
{
printk ("MIDI Error: Invalid patch format (key) 0x%x\n", format);
return RET_ERROR (EINVAL);
}
if (count < sizeof (struct sysex_info))
{
printk ("MIDI Error: Patch header too short\n");
return RET_ERROR (EINVAL);
}
count -= sizeof (struct sysex_info);
/*
* Copy the header from user space but ignore the first bytes which have
* been transferred already.
*/
COPY_FROM_USER (&((char *) &sysex)[offs], addr, offs, sizeof (struct sysex_info) - offs);
if (count < sysex.len)
{
printk ("MIDI Warning: Sysex record too short (%d<%d)\n",
count, (int) sysex.len);
sysex.len = count;
}
left = sysex.len;
src_offs = 0;
RESET_WAIT_QUEUE (sysex_sleeper, sysex_sleep_flag);
for (i = 0; i < left && !PROCESS_ABORTING (sysex_sleeper, sysex_sleep_flag); i++)
{
unsigned char data;
GET_BYTE_FROM_USER (data, addr, sizeof (struct sysex_info) + i);
if (first_byte && data != 0xf0)
midi_outc (orig_dev, 0xf0); /* Sysex start */
eox_seen = (data == 0xf7);/*
* Last byte was end of sysex
*/
if (i == 0)
{
if (data != 0xf0) /*
* Sysex start
*/
return RET_ERROR (EINVAL);
}
while (!midi_devs[orig_dev]->putc (orig_dev, (unsigned char) (data & 0xff)) &&
!PROCESS_ABORTING (sysex_sleeper, sysex_sleep_flag))
DO_SLEEP (sysex_sleeper, sysex_sleep_flag, 1); /* Wait for timeout */
if (!first_byte && data & 0x80)
return 0;
first_byte = 0;
}
if (!eox_seen)
midi_outc (orig_dev, 0xf7);
return 0;
}
void
midi_synth_panning (int dev, int channel, int pressure)
{
}
void
midi_synth_aftertouch (int dev, int channel, int pressure)
{
int orig_dev = synth_devs[dev]->midi_dev;
int msg, chn;
if (pressure < 0 || pressure > 127)
return;
if (channel < 0 || channel > 15)
return;
msg = prev_out_status[orig_dev] & 0xf0;
chn = prev_out_status[orig_dev] & 0x0f;
if (msg != 0xd0 || chn != channel) /*
* Test for running status
*/
{
if (!prefix_cmd (orig_dev, 0xd0 | (channel & 0x0f)))
return;
midi_outc (orig_dev, 0xd0 | (channel & 0x0f)); /*
* Channel pressure
*/
}
else if (!prefix_cmd (orig_dev, pressure))
return;
midi_outc (orig_dev, pressure);
}
void
midi_synth_controller (int dev, int channel, int ctrl_num, int value)
{
int orig_dev = synth_devs[dev]->midi_dev;
int chn, msg;
if (ctrl_num < 1 || ctrl_num > 127)
return; /* NOTE! Controller # 0 ignored */
if (channel < 0 || channel > 15)
return;
msg = prev_out_status[orig_dev] & 0xf0;
chn = prev_out_status[orig_dev] & 0x0f;
if (msg != 0xb0 || chn != channel)
{
if (!prefix_cmd (orig_dev, 0xb0 | (channel & 0x0f)))
return;
midi_outc (orig_dev, 0xb0 | (channel & 0x0f));
}
else if (!prefix_cmd (orig_dev, ctrl_num))
return;
midi_outc (orig_dev, ctrl_num);
midi_outc (orig_dev, value & 0x7f);
}
int
midi_synth_patchmgr (int dev, struct patmgr_info *rec)
{
return RET_ERROR (EINVAL);
}
void
midi_synth_bender (int dev, int channel, int value)
{
int orig_dev = synth_devs[dev]->midi_dev;
int msg, prev_chn;
if (channel < 0 || channel > 15)
return;
if (value < 0 || value > 16383)
return;
msg = prev_out_status[orig_dev] & 0xf0;
prev_chn = prev_out_status[orig_dev] & 0x0f;
if (msg != 0xd0 || prev_chn != channel) /*
* * Test for running status */
{
if (!prefix_cmd (orig_dev, 0xe0 | (channel & 0x0f)))
return;
midi_outc (orig_dev, 0xe0 | (channel & 0x0f));
}
else if (!prefix_cmd (orig_dev, value & 0x7f))
return;
midi_outc (orig_dev, value & 0x7f);
midi_outc (orig_dev, (value >> 7) & 0x7f);
}
#endif

View File

@ -0,0 +1,45 @@
int midi_synth_ioctl (int dev,
unsigned int cmd, unsigned int arg);
int midi_synth_kill_note (int dev, int channel, int note, int velocity);
int midi_synth_set_instr (int dev, int channel, int instr_no);
int midi_synth_start_note (int dev, int channel, int note, int volume);
void midi_synth_reset (int dev);
int midi_synth_open (int dev, int mode);
void midi_synth_close (int dev);
void midi_synth_hw_control (int dev, unsigned char *event);
int midi_synth_load_patch (int dev, int format, snd_rw_buf * addr,
int offs, int count, int pmgr_flag);
void midi_synth_panning (int dev, int channel, int pressure);
void midi_synth_aftertouch (int dev, int channel, int pressure);
void midi_synth_controller (int dev, int channel, int ctrl_num, int value);
int midi_synth_patchmgr (int dev, struct patmgr_info *rec);
void midi_synth_bender (int dev, int chn, int value);
#ifndef _MIDI_SYNTH_C_
static struct synth_info std_synth_info =
{MIDI_SYNTH_NAME, 0, SYNTH_TYPE_MIDI, 0, 0, 128, 0, 128, MIDI_SYNTH_CAPS};
static struct synth_operations std_midi_synth =
{
&std_synth_info,
0,
SYNTH_TYPE_MIDI,
0,
midi_synth_open,
midi_synth_close,
midi_synth_ioctl,
midi_synth_kill_note,
midi_synth_start_note,
midi_synth_set_instr,
midi_synth_reset,
midi_synth_hw_control,
midi_synth_load_patch,
midi_synth_aftertouch,
midi_synth_controller,
midi_synth_panning,
NULL,
midi_synth_patchmgr,
midi_synth_bender
};
#endif

924
sys/i386/isa/sound/pss.c Normal file
View File

@ -0,0 +1,924 @@
/* Marc.Hoffman@analog.com
This is a pss driver.
it is based on Greg.Yukna@analog.com @file{host} for DOG
Unfortunately I can't distribute the ld file needed to
make the pss card to emulate the SB stuff.
I have provided a simple interface to the PSS unlike the
DOG version. to download a new algorithim just cat it to
/dev/pss 14,9.
You really need to rebuild this with the synth.ld file
get the <synth>.ld from your dos directory maybe
voyetra\dsp001.ld
ld2inc < synth.ld > synth-ld.h
(make config does the same).
rebuild
Okay if you blow things away no problem just
main(){ioctl(open("/dev/pss"),SNDCTL_PSS_RESET)};
and everything will be okay.
At first I was going to wory about applications that were using
the sound stuff and disallow the use of /dev/pss. But for
now I figured it doesn't matter.
And if you change algos all the other applications running die off
due to DMA problems. Yeah just pull the plug and watch em die.
If the registers get hosed
main(){ioctl(open("/dev/pss"),SNDCTL_PSS_SETUP_REGISTERS)};
Probably everything else can be done via mmap
Oh if you want to develope code for the ADSP-21xx or Program the
1848 just send me mail and I will hook you up.
marc.hoffman@analog.com
*/
#include "sound_config.h"
#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_PSS)
#ifndef PSS_MSS_BASE
#define PSS_MSS_BASE 0
#endif
#ifndef PSS_MPU_BASE
#define PSS_MPU_BASE 0
#endif
#ifndef PSS_MPU_IRQ
#define PSS_MPU_IRQ 0
#endif
#undef DEB
#define DEB(x) x
#include "pss.h"
static int pss_ok = 0;
static int sb_ok = 0;
static int pss_base;
static int pss_irq;
static int pss_dma;
static int gamePort = 0;
static int sbInt;
static int cdPol;
static int cdAddr = 0; /* 0x340; */
static int cdInt = 10;
/* Define these by hand in local.h */
static int wssAddr = PSS_MSS_BASE;
static int midiAddr = PSS_MPU_BASE;
static int midiInt = PSS_MPU_IRQ;
static int SoundPortAddress;
static int SoundPortData;
static int speaker = 1;
static struct pss_speaker default_speaker =
{0, 0, 0, PSS_STEREO};
DEFINE_WAIT_QUEUE (pss_sleeper, pss_sleep_flag);
#include "synth-ld.h"
static int pss_download_boot (unsigned char *block, int size);
static int pss_reset_dsp (void);
static inline void
pss_outpw (unsigned short port, unsigned short value)
{
__asm__ __volatile__ ("outw %w0, %w1"
: /* no outputs */
:"a" (value), "d" (port));
}
static inline unsigned int
pss_inpw (unsigned short port)
{
unsigned int _v;
__asm__ __volatile__ ("inw %w1,%w0"
:"=a" (_v):"d" (port), "0" (0));
return _v;
}
static void
PSS_write (int data)
{
int i, limit;
limit = GET_TIME () + 10; /* The timeout is 0.1 secods */
/*
* Note! the i<5000000 is an emergency exit. The dsp_command() is sometimes
* called while interrupts are disabled. This means that the timer is
* disabled also. However the timeout situation is a abnormal condition.
* Normally the DSP should be ready to accept commands after just couple of
* loops.
*/
for (i = 0; i < 5000000 && GET_TIME () < limit; i++)
{
if (pss_inpw (pss_base + PSS_STATUS) & PSS_WRITE_EMPTY)
{
pss_outpw (pss_base + PSS_DATA, data);
return;
}
}
printk ("PSS: DSP Command (%04x) Timeout.\n", data);
printk ("IRQ conflict???\n");
}
static void
pss_setaddr (int addr, int configAddr)
{
int val;
val = pss_inpw (configAddr);
val &= ADDR_MASK;
val |= (addr << 4);
pss_outpw (configAddr, val);
}
/*_____ pss_checkint
This function tests an interrupt number to see if
it is availible. It takes the interrupt button
as it's argument and returns TRUE if the interrupt
is ok.
*/
static int
pss_checkint (int intNum)
{
int val;
int ret;
int i;
/*_____ Set the interrupt bits */
switch (intNum)
{
case 3:
val = pss_inpw (pss_base + PSS_CONFIG);
val &= INT_MASK;
val |= INT_3_BITS;
pss_outpw (pss_base + PSS_CONFIG, val);
break;
case 5:
val = pss_inpw (pss_base + PSS_CONFIG);
val &= INT_MASK;
val |= INT_5_BITS;
pss_outpw (pss_base + PSS_CONFIG, val);
break;
case 7:
val = pss_inpw (pss_base + PSS_CONFIG);
val &= INT_MASK;
val |= INT_7_BITS;
pss_outpw (pss_base + PSS_CONFIG, val);
break;
case 9:
val = pss_inpw (pss_base + PSS_CONFIG);
val &= INT_MASK;
val |= INT_9_BITS;
pss_outpw (pss_base + PSS_CONFIG, val);
break;
case 10:
val = pss_inpw (pss_base + PSS_CONFIG);
val &= INT_MASK;
val |= INT_10_BITS;
pss_outpw (pss_base + PSS_CONFIG, val);
break;
case 11:
val = pss_inpw (pss_base + PSS_CONFIG);
val &= INT_MASK;
val |= INT_11_BITS;
pss_outpw (pss_base + PSS_CONFIG, val);
break;
case 12:
val = pss_inpw (pss_base + PSS_CONFIG);
val &= INT_MASK;
val |= INT_12_BITS;
pss_outpw (pss_base + PSS_CONFIG, val);
break;
default:
printk ("unknown interupt selected. %d\n", intNum);
return 0;
}
/*_____ Set the interrupt test bit */
val = pss_inpw (pss_base + PSS_CONFIG);
val |= INT_TEST_BIT;
pss_outpw (pss_base + PSS_CONFIG, val);
/*_____ Check if the interrupt is in use */
/*_____ Do it a few times in case there is a delay */
ret = 0;
for (i = 0; i < 5; i++)
{
val = pss_inpw (pss_base + PSS_CONFIG);
if (val & INT_TEST_PASS)
{
ret = 1;
break;
}
}
/*_____ Clear the Test bit and the interrupt bits */
val = pss_inpw (pss_base + PSS_CONFIG);
val &= INT_TEST_BIT_MASK;
val &= INT_MASK;
pss_outpw (pss_base + PSS_CONFIG, val);
return (ret);
}
/*____ pss_setint
This function sets the correct bits in the
configuration register to
enable the chosen interrupt.
*/
static void
pss_setint (int intNum, int configAddress)
{
int val;
switch (intNum)
{
case 0:
val = pss_inpw (configAddress);
val &= INT_MASK;
pss_outpw (configAddress, val);
break;
case 3:
val = pss_inpw (configAddress);
val &= INT_MASK;
val |= INT_3_BITS;
pss_outpw (configAddress, val);
break;
case 5:
val = pss_inpw (configAddress);
val &= INT_MASK;
val |= INT_5_BITS;
pss_outpw (configAddress, val);
break;
case 7:
val = pss_inpw (configAddress);
val &= INT_MASK;
val |= INT_7_BITS;
pss_outpw (configAddress, val);
break;
case 9:
val = pss_inpw (configAddress);
val &= INT_MASK;
val |= INT_9_BITS;
pss_outpw (configAddress, val);
break;
case 10:
val = pss_inpw (configAddress);
val &= INT_MASK;
val |= INT_10_BITS;
pss_outpw (configAddress, val);
break;
case 11:
val = pss_inpw (configAddress);
val &= INT_MASK;
val |= INT_11_BITS;
pss_outpw (configAddress, val);
break;
case 12:
val = pss_inpw (configAddress);
val &= INT_MASK;
val |= INT_12_BITS;
pss_outpw (configAddress, val);
break;
default:
printk ("pss_setint unkown int\n");
}
}
/*____ pss_setsbint
This function sets the correct bits in the
SoundBlaster configuration PSS register to
enable the chosen interrupt.
It takes a interrupt button as its argument.
*/
static void
pss_setsbint (int intNum)
{
int val;
int sbConfigAddress;
sbConfigAddress = pss_base + SB_CONFIG;
switch (intNum)
{
case 3:
val = pss_inpw (sbConfigAddress);
val &= INT_MASK;
val |= INT_3_BITS;
pss_outpw (sbConfigAddress, val);
break;
case 5:
val = pss_inpw (sbConfigAddress);
val &= INT_MASK;
val |= INT_5_BITS;
pss_outpw (sbConfigAddress, val);
break;
case 7:
val = pss_inpw (sbConfigAddress);
val &= INT_MASK;
val |= INT_7_BITS;
pss_outpw (sbConfigAddress, val);
break;
default:
printk ("pss_setsbint: unknown_int\n");
}
}
/*____ pss_setsbdma
This function sets the correct bits in the
SoundBlaster configuration PSS register to
enable the chosen DMA channel.
It takes a DMA button as its argument.
*/
static void
pss_setsbdma (int dmaNum)
{
int val;
int sbConfigAddress;
sbConfigAddress = pss_base + SB_CONFIG;
switch (dmaNum)
{
case 1:
val = pss_inpw (sbConfigAddress);
val &= DMA_MASK;
val |= DMA_1_BITS;
pss_outpw (sbConfigAddress, val);
break;
default:
printk ("Personal Sound System ERROR! pss_setsbdma: unknown_dma\n");
}
}
/*____ pss_setwssdma
This function sets the correct bits in the
WSS configuration PSS register to
enable the chosen DMA channel.
It takes a DMA button as its argument.
*/
static void
pss_setwssdma (int dmaNum)
{
int val;
int wssConfigAddress;
wssConfigAddress = pss_base + PSS_WSS_CONFIG;
switch (dmaNum)
{
case 0:
val = pss_inpw (wssConfigAddress);
val &= DMA_MASK;
val |= DMA_0_BITS;
pss_outpw (wssConfigAddress, val);
break;
case 1:
val = pss_inpw (wssConfigAddress);
val &= DMA_MASK;
val |= DMA_1_BITS;
pss_outpw (wssConfigAddress, val);
break;
case 3:
val = pss_inpw (wssConfigAddress);
val &= DMA_MASK;
val |= DMA_3_BITS;
pss_outpw (wssConfigAddress, val);
break;
default:
printk ("Personal Sound System ERROR! pss_setwssdma: unknown_dma\n");
}
}
/*_____ SetSpeakerOut
This function sets the Volume, Bass, Treble and Mode of
the speaker out channel.
*/
void
pss_setspeaker (struct pss_speaker *spk)
{
PSS_write (SET_MASTER_COMMAND);
if (spk->volume > PHILLIPS_VOL_MAX)
spk->volume = PHILLIPS_VOL_MAX;
if (spk->volume < PHILLIPS_VOL_MIN)
spk->volume = PHILLIPS_VOL_MIN;
PSS_write (MASTER_VOLUME_LEFT
| (PHILLIPS_VOL_CONSTANT + spk->volume / PHILLIPS_VOL_STEP));
PSS_write (SET_MASTER_COMMAND);
PSS_write (MASTER_VOLUME_RIGHT
| (PHILLIPS_VOL_CONSTANT + spk->volume / PHILLIPS_VOL_STEP));
if (spk->bass > PHILLIPS_BASS_MAX)
spk->bass = PHILLIPS_BASS_MAX;
if (spk->bass < PHILLIPS_BASS_MIN)
spk->bass = PHILLIPS_BASS_MIN;
PSS_write (SET_MASTER_COMMAND);
PSS_write (MASTER_BASS
| (PHILLIPS_BASS_CONSTANT + spk->bass / PHILLIPS_BASS_STEP));
if (spk->treb > PHILLIPS_TREBLE_MAX)
spk->treb = PHILLIPS_TREBLE_MAX;
if (spk->treb < PHILLIPS_TREBLE_MIN)
spk->treb = PHILLIPS_TREBLE_MIN;
PSS_write (SET_MASTER_COMMAND);
PSS_write (MASTER_TREBLE
| (PHILLIPS_TREBLE_CONSTANT + spk->treb / PHILLIPS_TREBLE_STEP));
PSS_write (SET_MASTER_COMMAND);
PSS_write (MASTER_SWITCH | spk->mode);
}
static void
pss_init1848 (void)
{
/*_____ Wait for 1848 to init */
while (INB (SoundPortAddress) & SP_IN_INIT);
/*_____ Wait for 1848 to autocal */
OUTB (SoundPortAddress, SP_TEST_AND_INIT);
while (INB (SoundPortData) & AUTO_CAL_IN_PROG);
}
static int
pss_configure_registers_to_look_like_sb (void)
{
pss_setaddr (wssAddr, pss_base + PSS_WSS_CONFIG);
SoundPortAddress = wssAddr + 4;
SoundPortData = wssAddr + 5;
DEB (printk ("Turning Game Port %s.\n",
gamePort ? "On" : "Off"));
/*_____ Turn on the Game port */
if (gamePort)
pss_outpw (pss_base + PSS_STATUS,
pss_inpw (pss_base + PSS_STATUS) | GAME_BIT);
else
pss_outpw (pss_base + PSS_STATUS,
pss_inpw (pss_base + PSS_STATUS) & GAME_BIT_MASK);
DEB (printk ("PSS attaching base %x irq %d dma %d\n",
pss_base, pss_irq, pss_dma));
/* Check if sb is enabled if it is check the interrupt */
pss_outpw (pss_base + SB_CONFIG, 0);
if (pss_irq != 0)
{
DEB (printk ("PSS Emulating Sound Blaster ADDR %04x\n", pss_base));
DEB (printk ("PSS SBC: attaching base %x irq %d dma %d\n",
SBC_BASE, SBC_IRQ, SBC_DMA));
if (pss_checkint (SBC_IRQ) == 0)
{
printk ("PSS! attach: int_error\n");
return 0;
}
pss_setsbint (SBC_IRQ);
pss_setsbdma (SBC_DMA);
sb_ok = 1;
}
else
{
sb_ok = 0;
printk ("PSS: sound blaster error init\n");
}
/* Check if cd is enabled if it is check the interrupt */
pss_outpw (pss_base + CD_CONFIG, 0);
if (cdAddr != 0)
{
DEB (printk ("PSS:CD drive %x irq: %d", cdAddr, cdInt));
if (cdInt != 0)
{
if (pss_checkint (cdInt) == 0)
{
printk ("Can't allocate cdInt %d\n", cdInt);
}
else
{
int val;
printk ("CD poll ");
pss_setaddr (cdAddr, pss_base + CD_CONFIG);
pss_setint (cdInt, pss_base + CD_CONFIG);
/* set the correct bit in the
configuration register to
set the irq polarity for the CD-Rom.
NOTE: This bit is in the address config
field, It must be configured after setting
the CD-ROM ADDRESS!!! */
val = pss_inpw (pss_base + CD_CONFIG);
pss_outpw (pss_base + CD_CONFIG, 0);
val &= CD_POL_MASK;
if (cdPol)
val |= CD_POL_BIT;
pss_outpw (pss_base + CD_CONFIG, val);
}
}
}
/* Check if midi is enabled if it is check the interrupt */
pss_outpw (pss_base + MIDI_CONFIG, 0);
if (midiAddr != 0)
{
printk ("midi init %x %d\n", midiAddr, midiInt);
if (pss_checkint (midiInt) == 0)
{
printk ("midi init int error %x %d\n", midiAddr, midiInt);
}
else
{
pss_setaddr (midiAddr, pss_base + MIDI_CONFIG);
pss_setint (midiInt, pss_base + MIDI_CONFIG);
}
}
return 1;
}
long
attach_pss (long mem_start, struct address_info *hw_config)
{
if (pss_ok)
{
if (hw_config)
{
printk (" <PSS-ESC614>");
}
return mem_start;
}
pss_ok = 1;
if (pss_configure_registers_to_look_like_sb () == 0)
return mem_start;
if (sb_ok)
if (pss_synthLen
&& pss_download_boot (pss_synth, pss_synthLen))
{
if (speaker)
pss_setspeaker (&default_speaker);
pss_ok = 1;
}
else
pss_reset_dsp ();
return mem_start;
}
int
probe_pss (struct address_info *hw_config)
{
pss_base = hw_config->io_base;
pss_irq = hw_config->irq;
pss_dma = hw_config->dma;
if ((pss_inpw (pss_base + 4) & 0xff00) == 0x4500)
{
attach_pss (0, hw_config);
return 1;
}
printk (" fail base %x irq %d dma %d\n", pss_base, pss_irq, pss_dma);
return 0;
}
static int
pss_reattach (void)
{
pss_ok = 0;
attach_pss (0, 0);
return 1;
}
static int
pss_reset_dsp ()
{
unsigned long i, limit = GET_TIME () + 10;
pss_outpw (pss_base + PSS_CONTROL, 0x2000);
for (i = 0; i < 32768 && GET_TIME () < limit; i++)
pss_inpw (pss_base + PSS_CONTROL);
pss_outpw (pss_base + PSS_CONTROL, 0x0000);
return 1;
}
static int
pss_download_boot (unsigned char *block, int size)
{
int i, limit, val, count;
printk ("PSS: downloading boot code synth.ld... ");
/*_____ Warn DSP software that a boot is coming */
pss_outpw (pss_base + PSS_DATA, 0x00fe);
limit = GET_TIME () + 10;
for (i = 0; i < 32768 && GET_TIME () < limit; i++)
if (pss_inpw (pss_base + PSS_DATA) == 0x5500)
break;
pss_outpw (pss_base + PSS_DATA, *block++);
pss_reset_dsp ();
printk ("start ");
count = 1;
while (1)
{
int j;
for (j = 0; j < 327670; j++)
{
/*_____ Wait for BG to appear */
if (pss_inpw (pss_base + PSS_STATUS) & PSS_FLAG3)
break;
}
if (j == 327670)
{
/* It's ok we timed out when the file was empty */
if (count >= size)
break;
else
{
printk ("\nPSS: DownLoad timeout problems, byte %d=%d\n",
count, size);
return 0;
}
}
/*_____ Send the next byte */
pss_outpw (pss_base + PSS_DATA, *block++);
count++;
}
/*_____ Why */
pss_outpw (pss_base + PSS_DATA, 0);
limit = GET_TIME () + 10;
for (i = 0; i < 32768 && GET_TIME () < limit; i++)
val = pss_inpw (pss_base + PSS_STATUS);
printk ("downloaded\n");
limit = GET_TIME () + 10;
for (i = 0; i < 32768 && GET_TIME () < limit; i++)
{
val = pss_inpw (pss_base + PSS_STATUS);
if (val & 0x4000)
break;
}
/* now read the version */
for (i = 0; i < 32000; i++)
{
val = pss_inpw (pss_base + PSS_STATUS_REG);
if (val & PSS_READ_FULL)
break;
}
if (i == 32000)
return 0;
val = pss_inpw (pss_base + PSS_DATA_REG);
return 1;
}
/* The following is a simple device driver for the pss.
All I really care about is comunication to and from the pss.
The ability to reinitialize the <synth.ld> This will be
default when release is choosen.
SNDCTL_PSS_DOWNLOAD:
Okay we need to creat new minor numbers for the
DOWNLOAD functionality.
14,0x19 -- /dev/pssld where a read operation would output the
current ld to user space
where a write operation would effectively
download a new ld.
14,0x09 -- /dev/psecho would open up a comunication path to the
esc614 asic. Given the ability to send
messages to the asic and recive messages too.
All messages would get read and written in the
same manner. It would be up to the application
and the ld to maintain a relationship
of what the messages mean.
for this device we need to implement select. */
#define CODE_BUFFER_LEN (64*1024)
static char *code_buffer;
static int code_length;
static int lock_pss = 0;
int
pss_open (int dev, struct fileinfo *file)
{
int mode;
DEB (printk ("pss_open\n"));
if (pss_ok == 0)
return RET_ERROR (EIO);
if (lock_pss)
return 0;
lock_pss = 1;
dev = dev >> 4;
mode = file->mode & O_ACCMODE;
if (mode == O_WRONLY)
{
printk ("pss-open for WRONLY\n");
code_length = 0;
}
RESET_WAIT_QUEUE (pss_sleeper, pss_sleep_flag);
return 1;
}
void
pss_release (int dev, struct fileinfo *file)
{
int mode;
DEB (printk ("pss_release\n"));
if (pss_ok == 0)
return RET_ERROR (EIO);
dev = dev >> 4;
mode = file->mode & O_ACCMODE;
if (mode == O_WRONLY && code_length > 0)
{
#ifdef linux
/* This just allows interrupts while the conversion is running */
__asm__ ("sti");
#endif
if (!pss_download_boot (code_buffer, code_length))
{
pss_reattach ();
}
}
lock_pss = 0;
}
int
pss_read (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
{
int c, p;
DEB (printk ("pss_read\n"));
if (pss_ok == 0)
return RET_ERROR (EIO);
dev = dev >> 4;
p = 0;
c = count;
return count - c;
}
int
pss_write (int dev, struct fileinfo *file, snd_rw_buf * buf, int count)
{
DEB (printk ("pss_write\n"));
if (pss_ok == 0)
return RET_ERROR (EIO);
dev = dev >> 4;
if (count) /* Flush output */
{
COPY_FROM_USER (&code_buffer[code_length], buf, 0, count);
code_length += count;
}
return count;
}
int
pss_ioctl (int dev, struct fileinfo *file,
unsigned int cmd, unsigned int arg)
{
DEB (printk ("pss_ioctl dev=%d cmd=%x\n", dev, cmd));
if (pss_ok == 0)
return RET_ERROR (EIO);
dev = dev >> 4;
switch (cmd)
{
case SNDCTL_PSS_RESET:
pss_reattach ();
return 1;
case SNDCTL_PSS_SETUP_REGISTERS:
pss_configure_registers_to_look_like_sb ();
return 1;
case SNDCTL_PSS_SPEAKER:
{
struct pss_speaker params;
COPY_FROM_USER (&params, (char *) arg, 0, sizeof (struct pss_speaker));
pss_setspeaker (&params);
return 0;
}
default:
return RET_ERROR (EIO);
}
}
/* This is going to be used to implement
waiting on messages sent from the DSP and to the
DSP when comunication is used via the pss directly.
We need to find out if the pss can generate a diffrent
interupt other than the one it has been setup for.
This way we can carry on a conversation with the pss
on a seprate chanel. This would be usefull for debugging. */
pss_select (int dev, struct fileinfo * file, int sel_type, select_table * wait)
{
return 0;
if (pss_ok == 0)
return RET_ERROR (EIO);
dev = dev >> 4;
switch (sel_type)
{
case SEL_IN:
select_wait (&pss_sleeper, wait);
return 0;
break;
case SEL_OUT:
select_wait (&pss_sleeper, wait);
return 0;
break;
case SEL_EX:
return 0;
}
return 0;
}
long
pss_init (long mem_start)
{
DEB (printk ("pss_init\n"));
if (pss_ok)
{
code_buffer = mem_start;
mem_start += CODE_BUFFER_LEN;
}
return mem_start;
}
#endif

371
sys/i386/isa/sound/pss.h Normal file
View File

@ -0,0 +1,371 @@
/******************************************************************************
def.h
Version 1.3 11/2/93
Copyright (c) 1993 Analog Devices Inc. All rights reserved
******************************************************************************/
/* Port offsets from base port for Sound Blaster DSP */
#define DSP_PORT_CMSD0 0x00 /* C/MS music voice 1-6 data port, write only */
#define DSP_PORT_CMSR0 0x01 /* C/MS music voice 1-6 register port, write only */
#define DSP_PORT_CMSD1 0x02 /* C/MS music voice 7-12 data port, write only */
#define DSP_PORT_CMSR1 0x03 /* C/MS music voice 7-12 register port, write only */
#define DSP_PORT_STATUS 0x04 /* DSP Status bits, read only */
#define DSP_PORT_CONTROL 0x04 /* DSP Control bits, write only */
#define DSP_PORT_DATA_LSB 0x05 /* Read or write LSB of 16 bit data */
#define DSP_PORT_RESET 0x06 /* DSP Reset, write only */
#define DSP_PORT_07h 0x07 /* reserved port */
#define DSP_PORT_FMD0 0x08 /* FM music data/status port, read/write */
#define DSP_PORT_FMR0 0x09 /* FM music data/status port, write only */
#define DSP_PORT_RDDATA 0x0A /* DSP Read data, read only reading signals DSP */
#define DSP_PORT_0Bh 0x0B /* reserved port */
#define DSP_PORT_WRDATA 0x0C /* DSP Write data or command, write */
#define DSP_PORT_WRBUSY 0x0C /* DSP Write buffer status (bit 7), read */
#define DSP_PORT_0Dh 0x0D /* reserved port */
#define DSP_PORT_DATAAVAIL 0x0E /* DSP Data available status (bit 7), read only */
#define DSP_PORT_INTERFACE 0x0E /* Sets DMA Channel and Interrupt, write only */
#define DSP_PORT_0Fh 0x0F /* reserved port (used on Pro cards) */
#define ADDR_MASK 0x003f
#define INT_MASK 0xffc7
#define INT_3_BITS 0x0008
#define INT_5_BITS 0x0010
#define INT_7_BITS 0x0018
#define INT_9_BITS 0x0020
#define INT_10_BITS 0x0028
#define INT_11_BITS 0x0030
#define INT_12_BITS 0x0038
#define GAME_BIT 0x0400
#define GAME_BIT_MASK 0xfbff
#define INT_TEST_BIT 0x0200
#define INT_TEST_PASS 0x0100
#define INT_TEST_BIT_MASK 0xFDFF
#define DMA_MASK 0xfff8
#define DMA_0_BITS 0x0001
#define DMA_1_BITS 0x0002
#define DMA_3_BITS 0x0003
#define DMA_5_BITS 0x0004
#define DMA_6_BITS 0x0005
#define DMA_7_BITS 0x0006
#define DMA_TEST_BIT 0x0080
#define DMA_TEST_PASS 0x0040
#define DMA_TEST_BIT_MASK 0xFF7F
/* Echo DSP Flags */
#define DSP_FLAG3 0x10
#define DSP_FLAG2 0x08
#define DSP_FLAG1 0x80
#define DSP_FLAG0 0x40
#define PSS_CONFIG 0x10
#define PSS_WSS_CONFIG 0x12
#define SB_CONFIG 0x14
#define MIDI_CONFIG 0x18
#define CD_CONFIG 0x16
#define UART_CONFIG 0x1a
#define PSS_DATA 0x00
#define PSS_STATUS 0x02
#define PSS_CONTROL 0x02
#define PSS_ID_VERS 0x04
#define PSS_FLAG3 0x0800
#define PSS_FLAG2 0x0400
#define PSS_FLAG1 0x1000
#define PSS_FLAG0 0x0800
/*_____ WSS defines */
#define WSS_BASE_ADDRESS 0x530
#define WSS_CONFIG 0x0
#define WSS_VERSION 0x03
#define WSS_SP0 0x04
#define WSS_SP1 0x05
#define WSS_SP2 0x06
#define WSS_SP3 0x07
/*_____ SoundPort register addresses */
#define SP_LIN_SOURCE_CTRL 0x00
#define SP_RIN_SOURCE_CTRL 0x01
#define SP_LIN_GAIN_CTRL 0x10
#define SP_RIN_GAIN_CTRL 0x11
#define SP_LAUX1_CTRL 0x02
#define SP_RAUX1_CTRL 0x03
#define SP_LAUX2_CTRL 0x04
#define SP_RAUX2_CTRL 0x05
#define SP_LOUT_CTRL 0x06
#define SP_ROUT_CTRL 0x07
#define SP_CLK_FORMAT 0x48
#define SP_INT_CONF 0x09
#define SP_INT_CONF_MCE 0x49
#define SP_PIN_CTRL 0x0a
#define SP_TEST_INIT 0x0b
#define SP_MISC_CTRL 0x0c
#define SP_MIX_CTRL 0x0d
#define SP_DMA_UCNT 0x0e
#define SP_DMA_LCNT 0x0f
/*_____ Gain constants */
#define GAIN_0 0x00
#define GAIN_1_5 0x01
#define GAIN_3 0x02
#define GAIN_4_5 0x03
#define GAIN_6 0x04
#define GAIN_7_5 0x05
#define GAIN_9 0x06
#define GAIN_10_5 0x07
#define GAIN_12 0x08
#define GAIN_13_5 0x09
#define GAIN_15 0x0a
#define GAIN_16_5 0x0b
#define GAIN_18 0x0c
#define GAIN_19_5 0x0d
#define GAIN_21 0x0e
#define GAIN_22_5 0x0f
#define MUTE 0XFFFF
/*_____ Attenuation constants */
#define ATTEN_0 0x00
#define ATTEN_1_5 0x01
#define ATTEN_3 0x02
#define ATTEN_4_5 0x03
#define ATTEN_6 0x04
#define ATTEN_7_5 0x05
#define ATTEN_9 0x06
#define ATTEN_10_5 0x07
#define ATTEN_12 0x08
#define ATTEN_13_5 0x09
#define ATTEN_15 0x0a
#define ATTEN_16_5 0x0b
#define ATTEN_18 0x0c
#define ATTEN_19_5 0x0d
#define ATTEN_21 0x0e
#define ATTEN_22_5 0x0f
#define PSS_WRITE_EMPTY 0x8000
#define CD_POL_MASK 0xFFBF
#define CD_POL_BIT 0x0040
/******************************************************************************
host.h
Version 1.2 9/27/93
Copyright (c) 1993 Analog Devices Inc. All rights reserved
******************************************************************************/
#define SB_WRITE_FULL 0x80
#define SB_READ_FULL 0x80
#define SB_WRITE_STATUS 0x0C
#define SB_READ_STATUS 0x0E
#define SB_READ_DATA 0x0A
#define SB_WRITE_DATA 0x0C
#define PSS_DATA_REG 0x00
#define PSS_STATUS_REG 0x02
#define PSS_WRITE_EMPTY 0x8000
#define PSS_READ_FULL 0x4000
/*_____ 1848 Sound Port bit defines */
#define SP_IN_INIT 0x80
#define MODE_CHANGE_ENABLE 0x40
#define MODE_CHANGE_MASK 0xbf
#define TRANSFER_DISABLE 0x20
#define TRANSFER_DISABLE_MASK 0xdf
#define ADDRESS_MASK 0xf0
/*_____ Status bits */
#define INTERRUPT_STATUS 0x01
#define PLAYBACK_READY 0x02
#define PLAYBACK_LEFT 0x04
/*_____ pbright is not left */
#define PLAYBACK_UPPER 0x08
/*_____ bplower is not upper */
#define SAMPLE_OVERRUN 0x10
#define SAMPLE_UNDERRUN 0x10
#define CAPTURE_READY 0x20
#define CAPTURE_LEFT 0x40
/*_____ cpright is not left */
#define CAPTURE_UPPER 0x08
/*_____ cplower is not upper */
/*_____ Input & Output regs bits */
#define LINE_INPUT 0x80
#define AUX_INPUT 0x40
#define MIC_INPUT 0x80
#define MIXED_DAC_INPUT 0xC0
#define INPUT_GAIN_MASK 0xf0
#define INPUT_MIC_GAIN_ENABLE 0x20
#define INPUT_MIC_GAIN_MASK 0xdf
#define INPUT_SOURCE_MASK 0x3f
#define AUX_INPUT_ATTEN_MASK 0xf0
#define AUX_INPUT_MUTE 0x80
#define AUX_INPUT_MUTE_MASK 0x7f
#define OUTPUT_MUTE 0x80
#define OUTPUT_MUTE_MASK 0x7f
#define OUTPUT_ATTEN_MASK 0xc0
/*_____ Clock and Data format reg bits */
#define CLOCK_SELECT_MASK 0xfe
#define CLOCK_XTAL2 0x01
#define CLOCK_XTAL1 0x00
#define CLOCK_FREQ_MASK 0xf1
#define STEREO_MONO_MASK 0xef
#define STEREO 0x10
#define AUDIO_MONO 0x00
#define LINEAR_COMP_MASK 0xdf
#define LINEAR 0x00
#define COMPANDED 0x20
#define FORMAT_MASK 0xbf
#define PCM 0x00
#define ULAW 0x00
#define TWOS_COMP 0x40
#define ALAW 0x40
/*_____ Interface Configuration reg bits */
#define PLAYBACK_ENABLE 0x01
#define PLAYBACK_ENABLE_MASK 0xfe
#define CAPTURE_ENABLE 0x02
#define CAPTURE_ENABLE_MASK 0xfd
#define SINGLE_DMA 0x04
#define SINGLE_DMA_MASK 0xfb
#define DUAL_DMA 0x00
#define AUTO_CAL_ENABLE 0x08
#define AUTO_CAL_DISABLE_MASK 0xf7
#define PLAYBACK_PIO_ENABLE 0x40
#define PLAYBACK_DMA_MASK 0xbf
#define CAPTURE_PIO_ENABLE 0x80
#define CAPTURE_DMA_MASK 0x7f
/*_____ Pin control bits */
#define INTERRUPT_ENABLE 0x02
#define INTERRUPT_MASK 0xfd
/*_____ Test and init reg bits */
#define OVERRANGE_LEFT_MASK 0xfc
#define OVERRANGE_RIGHT_MASK 0xf3
#define DATA_REQUEST_STATUS 0x10
#define AUTO_CAL_IN_PROG 0x20
#define PLAYBACK_UNDERRUN 0x40
#define CAPTURE_UNDERRUN 0x80
/*_____ Miscellaneous Control reg bits */
#define ID_MASK 0xf0
/*_____ Digital Mix Control reg bits */
#define DIGITAL_MIX1_MUTE_MASK 0xfe
#define MIX_ATTEN_MASK 0x03
/*_____ 1848 Sound Port reg defines */
#define SP_LEFT_INPUT_CONTROL 0x0
#define SP_RIGHT_INPUT_CONTROL 0x1
#define SP_LEFT_AUX1_CONTROL 0x2
#define SP_RIGHT_AUX1_CONTROL 0x3
#define SP_LEFT_AUX2_CONTROL 0x4
#define SP_RIGHT_AUX2_CONTROL 0x5
#define SP_LEFT_OUTPUT_CONTROL 0x6
#define SP_RIGHT_OUTPUT_CONTROL 0x7
#define SP_CLOCK_DATA_FORMAT 0x8
#define SP_INTERFACE_CONFIG 0x9
#define SP_PIN_CONTROL 0xA
#define SP_TEST_AND_INIT 0xB
#define SP_MISC_INFO 0xC
#define SP_DIGITAL_MIX 0xD
#define SP_UPPER_BASE_COUNT 0xE
#define SP_LOWER_BASE_COUNT 0xF
#define HOST_SP_ADDR (0x534)
#define HOST_SP_DATA (0x535)
/******************************************************************************
phillips.h
Version 1.2 9/27/93
Copyright (c) 1993 Analog Devices Inc. All rights reserved
******************************************************************************/
/*_____ Phillips control SW defines */
/*_____ Settings and ranges */
#define VOLUME_MAX 6
#define VOLUME_MIN (-64)
#define VOLUME_RANGE 70
#define VOLUME_STEP 2
#define BASS_MAX 15
#define BASS_MIN (-12)
#define BASS_STEP 2
#define BASS_RANGE 27
#define TREBLE_MAX 12
#define TREBLE_MIN (-12)
#define TREBLE_STEP 2
#define TREBLE_RANGE 24
#define VOLUME_CONSTANT 252
#define BASS_CONSTANT 246
#define TREBLE_CONSTANT 246
/*_____ Software commands */
#define SET_MASTER_COMMAND 0x0010
#define MASTER_VOLUME_LEFT 0x0000
#define MASTER_VOLUME_RIGHT 0x0100
#define MASTER_BASS 0x0200
#define MASTER_TREBLE 0x0300
#define MASTER_SWITCH 0x0800
#define STEREO_MODE 0x00ce
#define PSEUDO_MODE 0x00d6
#define SPATIAL_MODE 0x00de
#define MONO_MODE 0x00c6
#define PSS_STEREO 0x00ce
#define PSS_PSEUDO 0x00d6
#define PSS_SPATIAL 0x00de
#define PSS_MONO 0x00c6
#define PHILLIPS_VOL_MIN -64
#define PHILLIPS_VOL_MAX 6
#define PHILLIPS_VOL_DELTA 70
#define PHILLIPS_VOL_INITIAL -20
#define PHILLIPS_VOL_CONSTANT 252
#define PHILLIPS_VOL_STEP 2
#define PHILLIPS_BASS_MIN -12
#define PHILLIPS_BASS_MAX 15
#define PHILLIPS_BASS_DELTA 27
#define PHILLIPS_BASS_INITIAL 0
#define PHILLIPS_BASS_CONSTANT 246
#define PHILLIPS_BASS_STEP 2
#define PHILLIPS_TREBLE_MIN -12
#define PHILLIPS_TREBLE_MAX 12
#define PHILLIPS_TREBLE_DELTA 24
#define PHILLIPS_TREBLE_INITIAL 0
#define PHILLIPS_TREBLE_CONSTANT 246
#define PHILLIPS_TREBLE_STEP 2

View File

@ -0,0 +1,406 @@
/*
* sound/sound_timer.c
*
* Timer for the level 2 interface of the /dev/sequencer. Uses the
* 80 and 320 usec timers of OPL-3 (PAS16 only) and GUS.
*
* Copyright by Hannu Savolainen 1993
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met: 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer. 2.
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#define SEQUENCER_C
#include "sound_config.h"
#ifdef CONFIGURE_SOUNDCARD
#if !defined(EXCLUDE_SEQUENCER) && (!defined(EXCLUDE_GUS) || (!defined(EXCLUDE_PAS) && !defined(EXCLUDE_YM3812)))
static volatile int initialized = 0, opened = 0, tmr_running = 0;
static volatile time_t tmr_offs, tmr_ctr;
static volatile unsigned long ticks_offs;
static volatile int curr_tempo, curr_timebase;
static volatile unsigned long curr_ticks;
static volatile unsigned long next_event_time;
static unsigned long prev_event_time;
static volatile int select_addr, data_addr;
static volatile int curr_timer = 0;
static volatile unsigned long usecs_per_tmr; /* Length of the current interval */
static void
timer_command (unsigned int addr, unsigned int val)
{
int i;
OUTB ((unsigned char) (addr & 0xff), select_addr);
for (i = 0; i < 2; i++)
INB (select_addr);
OUTB ((unsigned char) (val & 0xff), data_addr);
for (i = 0; i < 2; i++)
INB (select_addr);
}
static void
arm_timer (int timer, unsigned int interval)
{
curr_timer = timer;
if (timer == 1)
{
gus_write8 (0x46, 256 - interval); /* Set counter for timer 1 */
gus_write8 (0x45, 0x04); /* Enable timer 1 IRQ */
timer_command (0x04, 0x01); /* Start timer 1 */
}
else
{
gus_write8 (0x47, 256 - interval); /* Set counter for timer 2 */
gus_write8 (0x45, 0x08); /* Enable timer 2 IRQ */
timer_command (0x04, 0x02); /* Start timer 2 */
}
}
static unsigned long
tmr2ticks (int tmr_value)
{
/*
* Convert timer ticks to MIDI ticks
*/
unsigned long tmp;
unsigned long scale;
tmp = tmr_value * usecs_per_tmr; /* Convert to usecs */
scale = (60 * 1000000) / (curr_tempo * curr_timebase); /* usecs per MIDI tick */
return (tmp + (scale / 2)) / scale;
}
static void
reprogram_timer (void)
{
unsigned long usecs_per_tick;
int timer_no, resolution;
int divisor;
usecs_per_tick = (60 * 1000000) / (curr_tempo * curr_timebase);
/*
* Don't kill the system by setting too high timer rate
*/
if (usecs_per_tick < 2000)
usecs_per_tick = 2000;
if (usecs_per_tick > (256 * 80))
{
timer_no = 2;
resolution = 320; /* usec */
}
else
{
timer_no = 1;
resolution = 80; /* usec */
}
divisor = (usecs_per_tick + (resolution / 2)) / resolution;
usecs_per_tmr = divisor * resolution;
arm_timer (timer_no, divisor);
}
static void
tmr_reset (void)
{
unsigned long flags;
DISABLE_INTR (flags);
tmr_offs = 0;
ticks_offs = 0;
tmr_ctr = 0;
next_event_time = 0xffffffff;
prev_event_time = 0;
curr_ticks = 0;
RESTORE_INTR (flags);
}
static int
timer_open (int dev, int mode)
{
if (opened)
return RET_ERROR (EBUSY);
tmr_reset ();
curr_tempo = 60;
curr_timebase = HZ;
opened = 1;
reprogram_timer ();
return 0;
}
static void
timer_close (int dev)
{
opened = tmr_running = 0;
gus_write8 (0x45, 0); /* Disable both timers */
}
static int
timer_event (int dev, unsigned char *event)
{
unsigned char cmd = event[1];
unsigned long parm = *(int *) &event[4];
switch (cmd)
{
case TMR_WAIT_REL:
parm += prev_event_time;
case TMR_WAIT_ABS:
if (parm > 0)
{
long time;
if (parm <= curr_ticks) /* It's the time */
return TIMER_NOT_ARMED;
time = parm;
next_event_time = prev_event_time = time;
return TIMER_ARMED;
}
break;
case TMR_START:
tmr_reset ();
tmr_running = 1;
reprogram_timer ();
break;
case TMR_STOP:
tmr_running = 0;
break;
case TMR_CONTINUE:
tmr_running = 1;
reprogram_timer ();
break;
case TMR_TEMPO:
if (parm)
{
if (parm < 8)
parm = 8;
if (parm > 250)
parm = 250;
tmr_offs = tmr_ctr;
ticks_offs += tmr2ticks (tmr_ctr);
tmr_ctr = 0;
curr_tempo = parm;
reprogram_timer ();
}
break;
case TMR_ECHO:
seq_copy_to_input (event, 8);
break;
default:;
}
return TIMER_NOT_ARMED;
}
static unsigned long
timer_get_time (int dev)
{
if (!opened)
return 0;
return curr_ticks;
}
static int
timer_ioctl (int dev,
unsigned int cmd, unsigned int arg)
{
switch (cmd)
{
case SNDCTL_TMR_SOURCE:
return IOCTL_OUT (arg, TMR_INTERNAL);
break;
case SNDCTL_TMR_START:
tmr_reset ();
tmr_running = 1;
return 0;
break;
case SNDCTL_TMR_STOP:
tmr_running = 0;
return 0;
break;
case SNDCTL_TMR_CONTINUE:
tmr_running = 1;
return 0;
break;
case SNDCTL_TMR_TIMEBASE:
{
int val = IOCTL_IN (arg);
if (val)
{
if (val < 1)
val = 1;
if (val > 1000)
val = 1000;
curr_timebase = val;
}
return IOCTL_OUT (arg, curr_timebase);
}
break;
case SNDCTL_TMR_TEMPO:
{
int val = IOCTL_IN (arg);
if (val)
{
if (val < 8)
val = 8;
if (val > 250)
val = 250;
tmr_offs = tmr_ctr;
ticks_offs += tmr2ticks (tmr_ctr);
tmr_ctr = 0;
curr_tempo = val;
reprogram_timer ();
}
return IOCTL_OUT (arg, curr_tempo);
}
break;
case SNDCTL_SEQ_CTRLRATE:
if (IOCTL_IN (arg) != 0) /* Can't change */
return RET_ERROR (EINVAL);
return IOCTL_OUT (arg, ((curr_tempo * curr_timebase) + 30) / 60);
break;
case SNDCTL_TMR_METRONOME:
/* NOP */
break;
default:
}
return RET_ERROR (EINVAL);
}
static void
timer_arm (int dev, long time)
{
if (time < 0)
time = curr_ticks + 1;
else if (time <= curr_ticks) /* It's the time */
return;
next_event_time = prev_event_time = time;
return;
}
static struct sound_timer_operations sound_timer =
{
{"OPL-3/GUS Timer", 0},
1, /* Priority */
0, /* Local device link */
timer_open,
timer_close,
timer_event,
timer_get_time,
timer_ioctl,
timer_arm
};
void
sound_timer_interrupt (void)
{
gus_write8 (0x45, 0); /* Ack IRQ */
timer_command (4, 0x80); /* Reset IRQ flags */
if (!opened)
return;
if (curr_timer == 1)
gus_write8 (0x45, 0x04); /* Start timer 1 again */
else
gus_write8 (0x45, 0x08); /* Start timer 2 again */
if (!tmr_running)
return;
tmr_ctr++;
curr_ticks = ticks_offs + tmr2ticks (tmr_ctr);
if (curr_ticks >= next_event_time)
{
next_event_time = 0xffffffff;
sequencer_timer ();
}
}
void
sound_timer_init (int io_base)
{
int n;
if (initialized)
return; /* There is already a similar timer */
select_addr = io_base;
data_addr = io_base + 1;
initialized = 1;
#if 1
if (num_sound_timers >= MAX_TIMER_DEV)
n = 0; /* Overwrite the system timer */
else
n = num_sound_timers++;
#else
n = 0;
#endif
sound_timer_devs[n] = &sound_timer;
}
#endif
#endif

View File

@ -0,0 +1,304 @@
/*
* sound/sys_timer.c
*
* The default timer for the Level 2 sequencer interface
* Uses the (100HZ) timer of kernel.
*
* Copyright by Hannu Savolainen 1993
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met: 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer. 2.
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#define SEQUENCER_C
#include "sound_config.h"
#ifdef CONFIGURE_SOUNDCARD
#ifndef EXCLUDE_SEQUENCER
static volatile int opened = 0, tmr_running = 0;
static volatile time_t tmr_offs, tmr_ctr;
static volatile unsigned long ticks_offs;
static volatile int curr_tempo, curr_timebase;
static volatile unsigned long curr_ticks;
static volatile unsigned long next_event_time;
static unsigned long prev_event_time;
static void poll_def_tmr (unsigned long dummy);
DEFINE_TIMER (def_tmr, poll_def_tmr);
static unsigned long
tmr2ticks (int tmr_value)
{
/*
* Convert system timer ticks (HZ) to MIDI ticks
*/
unsigned long tmp;
unsigned long scale;
tmp = (tmr_value * 1000) / HZ;/* Convert to msecs */
scale = (60 * 1000) / (curr_tempo * curr_timebase); /* msecs per MIDI tick */
return (tmp + (scale / 2)) / scale;
}
static void
poll_def_tmr (unsigned long dummy)
{
if (opened)
{
ACTIVATE_TIMER (def_tmr, poll_def_tmr, 1);
if (tmr_running)
{
tmr_ctr++;
curr_ticks = ticks_offs + tmr2ticks (tmr_ctr);
if (curr_ticks >= next_event_time)
{
next_event_time = 0xffffffff;
sequencer_timer ();
}
}
}
}
static void
tmr_reset (void)
{
unsigned long flags;
DISABLE_INTR (flags);
tmr_offs = 0;
ticks_offs = 0;
tmr_ctr = 0;
next_event_time = 0xffffffff;
prev_event_time = 0;
curr_ticks = 0;
RESTORE_INTR (flags);
}
static int
def_tmr_open (int dev, int mode)
{
if (opened)
return RET_ERROR (EBUSY);
tmr_reset ();
curr_tempo = 60;
curr_timebase = HZ;
opened = 1;
ACTIVATE_TIMER (def_tmr, poll_def_tmr, 1);
return 0;
}
static void
def_tmr_close (int dev)
{
opened = tmr_running = 0;
}
static int
def_tmr_event (int dev, unsigned char *event)
{
unsigned char cmd = event[1];
unsigned long parm = *(int *) &event[4];
switch (cmd)
{
case TMR_WAIT_REL:
parm += prev_event_time;
case TMR_WAIT_ABS:
if (parm > 0)
{
long time;
if (parm <= curr_ticks) /* It's the time */
return TIMER_NOT_ARMED;
time = parm;
next_event_time = prev_event_time = time;
return TIMER_ARMED;
}
break;
case TMR_START:
tmr_reset ();
tmr_running = 1;
break;
case TMR_STOP:
tmr_running = 0;
break;
case TMR_CONTINUE:
tmr_running = 1;
break;
case TMR_TEMPO:
if (parm)
{
if (parm < 8)
parm = 8;
if (parm > 250)
parm = 250;
tmr_offs = tmr_ctr;
ticks_offs += tmr2ticks (tmr_ctr);
tmr_ctr = 0;
curr_tempo = parm;
}
break;
case TMR_ECHO:
seq_copy_to_input (event, 8);
break;
default:;
}
return TIMER_NOT_ARMED;
}
static unsigned long
def_tmr_get_time (int dev)
{
if (!opened)
return 0;
return curr_ticks;
}
static int
def_tmr_ioctl (int dev,
unsigned int cmd, unsigned int arg)
{
switch (cmd)
{
case SNDCTL_TMR_SOURCE:
return IOCTL_OUT (arg, TMR_INTERNAL);
break;
case SNDCTL_TMR_START:
tmr_reset ();
tmr_running = 1;
return 0;
break;
case SNDCTL_TMR_STOP:
tmr_running = 0;
return 0;
break;
case SNDCTL_TMR_CONTINUE:
tmr_running = 1;
return 0;
break;
case SNDCTL_TMR_TIMEBASE:
{
int val = IOCTL_IN (arg);
if (val)
{
if (val < 1)
val = 1;
if (val > 1000)
val = 1000;
curr_timebase = val;
}
return IOCTL_OUT (arg, curr_timebase);
}
break;
case SNDCTL_TMR_TEMPO:
{
int val = IOCTL_IN (arg);
if (val)
{
if (val < 8)
val = 8;
if (val > 250)
val = 250;
tmr_offs = tmr_ctr;
ticks_offs += tmr2ticks (tmr_ctr);
tmr_ctr = 0;
curr_tempo = val;
}
return IOCTL_OUT (arg, curr_tempo);
}
break;
case SNDCTL_SEQ_CTRLRATE:
if (IOCTL_IN (arg) != 0) /* Can't change */
return RET_ERROR (EINVAL);
return IOCTL_OUT (arg, ((curr_tempo * curr_timebase) + 30) / 60);
break;
case SNDCTL_TMR_METRONOME:
/* NOP */
break;
default:;
}
return RET_ERROR (EINVAL);
}
static void
def_tmr_arm (int dev, long time)
{
if (time < 0)
time = curr_ticks + 1;
else if (time <= curr_ticks) /* It's the time */
return;
next_event_time = prev_event_time = time;
return;
}
struct sound_timer_operations default_sound_timer =
{
{"System Timer", 0},
0, /* Priority */
0, /* Local device link */
def_tmr_open,
def_tmr_close,
def_tmr_event,
def_tmr_get_time,
def_tmr_ioctl,
def_tmr_arm
};
#endif
#endif

View File

@ -0,0 +1,323 @@
/*
* sound/uart6850.c
*
* Copyright by Hannu Savolainen 1993
*
* Mon Nov 22 22:38:35 MET 1993 marco@driq.home.usn.nl:
* added 6850 support, used with COVOX SoundMaster II and custom cards.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met: 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer. 2.
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#include "sound_config.h"
#ifdef CONFIGURE_SOUNDCARD
#if !defined(EXCLUDE_UART6850) && !defined(EXCLUDE_MIDI)
#define DATAPORT (uart6850_base) /*
* * * Midi6850 Data I/O Port on IBM
* */
#define COMDPORT (uart6850_base+1) /*
* * * Midi6850 Command Port on IBM */
#define STATPORT (uart6850_base+1) /*
* * * Midi6850 Status Port on IBM */
#define uart6850_status() INB(STATPORT)
#define input_avail() ((uart6850_status()&INPUT_AVAIL))
#define output_ready() ((uart6850_status()&OUTPUT_READY))
#define uart6850_cmd(cmd) OUTB(cmd, COMDPORT)
#define uart6850_read() INB(DATAPORT)
#define uart6850_write(byte) OUTB(byte, DATAPORT)
#define OUTPUT_READY 0x02 /*
* * * Mask for Data Read Ready Bit */
#define INPUT_AVAIL 0x01 /*
* * * Mask for Data Send Ready Bit */
#define UART_RESET 0x95 /*
* * * 6850 Total Reset Command */
#define UART_MODE_ON 0x03 /*
* * * 6850 Send/Receive UART Mode */
static int uart6850_opened = 0;
static int uart6850_base = 0x330;
static int uart6850_irq;
static int uart6850_detected = 0;
static int my_dev;
static int reset_uart6850 (void);
static void (*midi_input_intr) (int dev, unsigned char data);
static void
uart6850_input_loop (void)
{
int count;
count = 10;
while (count) /*
* Not timed out
*/
if (input_avail ())
{
unsigned char c = uart6850_read ();
count = 100;
if (uart6850_opened & OPEN_READ)
midi_input_intr (my_dev, c);
}
else
while (!input_avail () && count)
count--;
}
void
m6850intr (int unit)
{
printk ("M");
if (input_avail ())
uart6850_input_loop ();
}
/*
* It looks like there is no input interrupts in the UART mode. Let's try
* polling.
*/
static void
poll_uart6850 (unsigned long dummy)
{
unsigned long flags;
DEFINE_TIMER (uart6850_timer, poll_uart6850);
if (!(uart6850_opened & OPEN_READ))
return; /*
* No longer required
*/
DISABLE_INTR (flags);
if (input_avail ())
uart6850_input_loop ();
ACTIVATE_TIMER (uart6850_timer, poll_uart6850, 1); /*
* Come back later
*/
RESTORE_INTR (flags);
}
static int
uart6850_open (int dev, int mode,
void (*input) (int dev, unsigned char data),
void (*output) (int dev)
)
{
if (uart6850_opened)
{
printk ("Midi6850: Midi busy\n");
return RET_ERROR (EBUSY);
}
uart6850_cmd (UART_RESET);
uart6850_input_loop ();
midi_input_intr = input;
uart6850_opened = mode;
poll_uart6850 (0); /*
* Enable input polling
*/
return 0;
}
static void
uart6850_close (int dev)
{
uart6850_cmd (UART_MODE_ON);
uart6850_opened = 0;
}
static int
uart6850_out (int dev, unsigned char midi_byte)
{
int timeout;
unsigned long flags;
/*
* Test for input since pending input seems to block the output.
*/
DISABLE_INTR (flags);
if (input_avail ())
uart6850_input_loop ();
RESTORE_INTR (flags);
/*
* Sometimes it takes about 13000 loops before the output becomes ready
* (After reset). Normally it takes just about 10 loops.
*/
for (timeout = 30000; timeout > 0 && !output_ready (); timeout--); /*
* Wait
*/
if (!output_ready ())
{
printk ("Midi6850: Timeout\n");
return 0;
}
uart6850_write (midi_byte);
return 1;
}
static int
uart6850_command (int dev, unsigned char *midi_byte)
{
return 1;
}
static int
uart6850_start_read (int dev)
{
return 0;
}
static int
uart6850_end_read (int dev)
{
return 0;
}
static int
uart6850_ioctl (int dev, unsigned cmd, unsigned arg)
{
return RET_ERROR (EINVAL);
}
static void
uart6850_kick (int dev)
{
}
static int
uart6850_buffer_status (int dev)
{
return 0; /*
* No data in buffers
*/
}
#define MIDI_SYNTH_NAME "6850 UART Midi"
#define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT
#include "midi_synth.h"
static struct midi_operations uart6850_operations =
{
{"6850 UART", 0, 0, SNDCARD_UART6850},
&std_midi_synth,
uart6850_open,
uart6850_close,
uart6850_ioctl,
uart6850_out,
uart6850_start_read,
uart6850_end_read,
uart6850_kick,
uart6850_command,
uart6850_buffer_status
};
long
attach_uart6850 (long mem_start, struct address_info *hw_config)
{
int ok, timeout;
unsigned long flags;
if (num_midis >= MAX_MIDI_DEV)
{
printk ("Sound: Too many midi devices detected\n");
return mem_start;
}
uart6850_base = hw_config->io_base;
uart6850_irq = hw_config->irq;
if (!uart6850_detected)
return RET_ERROR (EIO);
DISABLE_INTR (flags);
for (timeout = 30000; timeout < 0 && !output_ready (); timeout--); /*
* Wait
*/
uart6850_cmd (UART_MODE_ON);
ok = 1;
RESTORE_INTR (flags);
printk (" <6850 Midi Interface>");
std_midi_synth.midi_dev = my_dev = num_midis;
midi_devs[num_midis++] = &uart6850_operations;
return mem_start;
}
static int
reset_uart6850 (void)
{
uart6850_read ();
return 1; /*
* OK
*/
}
int
probe_uart6850 (struct address_info *hw_config)
{
int ok = 0;
uart6850_base = hw_config->io_base;
uart6850_irq = hw_config->irq;
if (snd_set_irq_handler (uart6850_irq, m6850intr) < 0)
return 0;
ok = reset_uart6850 ();
uart6850_detected = ok;
return ok;
}
#endif
#endif