📄 pcm86.c
字号:
/* * PC-9801-86 PCM driver for FreeBSD(98). * * Copyright (c) 1995 NAGAO Tadaaki (ABTK) * All rights reserved. * * 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 AND 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. * * $FreeBSD: src/sys/i386/isa/sound/pcm86.c,v 1.1.2.2 1999/09/05 08:14:06 peter Exp $ *//* * !! NOTE !! : * This file DOES NOT belong to the VoxWare distribution though it works * as part of the VoxWare drivers. It is FreeBSD(98) original. * -- Nagao (nagao@cs.titech.ac.jp) */#include <i386/isa/sound/sound_config.h>#ifdef CONFIGURE_SOUNDCARD#if !defined(EXCLUDE_PCM86) && !defined(EXCLUDE_AUDIO)/* * Constants */#define YES 1#define NO 0#define IMODE_NONE 0#define IMODE_INPUT 1#define IMODE_OUTPUT 2/* PC-9801-86 specific constants */#define PCM86_IOBASE 0xa460 /* PCM I/O ports */#define PCM86_FIFOSIZE 32768 /* There is a 32kB FIFO buffer on 86-board *//* XXX -- These values should be chosen appropriately. */#define PCM86_INTRSIZE_OUT 1024#define PCM86_INTRSIZE_IN (PCM86_FIFOSIZE / 2 - 128)#define DEFAULT_VOLUME 15 /* 0(min) - 15(max) *//* * Switches for debugging and experiments *//* #define PCM86_DEBUG */#ifdef PCM86_DEBUG# ifdef DEB# undef DEB# endif# define DEB(x) x#endif/* * Private variables and types */typedef unsigned char pcm_data;enum board_type { NO_SUPPORTED_BOARD = 0, PC980186_FAMILY = 1, PC980173_FAMILY = 2};static char *board_name[] = { /* Each must be of the length less than 32 bytes. */ "No supported board", "PC-9801-86 soundboard", "PC-9801-73 soundboard"};/* Current status of the driver */static struct { int iobase; int irq; enum board_type board_type; int opened; int format; int bytes; int chipspeedno; int chipspeed; int speed; int stereo; int volume; int intr_busy; int intr_size; int intr_mode; int intr_last; int intr_trailer; pcm_data * pdma_buf; int pdma_count; int pdma_chunkcount; int acc; int last_l; int last_r;} pcm_s;static struct { pcm_data buff[4]; int size;} tmpbuf;static int my_dev = 0;static char pcm_initialized = NO;/* 86-board supports only the following rates. */static int rates_tbl[8] = {#ifndef WAVEMASTER_FREQ 44100, 33075, 22050, 16538, 11025, 8269, 5513, 4134#else /* * It is said that Q-Vision's WaveMaster of some earlier lot(s?) has * sampling rates incompatible with PC-9801-86. * But I'm not sure whether the following rates are correct, especially * 4000Hz. */ 44100, 33075, 22050, 16000, 11025, 8000, 5510, 4000#endif};/* u-law to linear translation table */static pcm_data ulaw2linear[256] = { 130, 134, 138, 142, 146, 150, 154, 158, 162, 166, 170, 174, 178, 182, 186, 190, 193, 195, 197, 199, 201, 203, 205, 207, 209, 211, 213, 215, 217, 219, 221, 223, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 240, 241, 241, 242, 242, 243, 243, 244, 244, 245, 245, 246, 246, 247, 247, 248, 248, 248, 249, 249, 249, 249, 250, 250, 250, 250, 251, 251, 251, 251, 252, 252, 252, 252, 252, 252, 253, 253, 253, 253, 253, 253, 253, 253, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 125, 121, 117, 113, 109, 105, 101, 97, 93, 89, 85, 81, 77, 73, 69, 65, 62, 60, 58, 56, 54, 52, 50, 48, 46, 44, 42, 40, 38, 36, 34, 32, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 15, 14, 14, 13, 13, 12, 12, 11, 11, 10, 10, 9, 9, 8, 8, 7, 7, 7, 6, 6, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};/* linear to u-law translation table */static pcm_data linear2ulaw[256] = { 255, 231, 219, 211, 205, 201, 197, 193, 190, 188, 186, 184, 182, 180, 178, 176, 175, 174, 173, 172, 171, 170, 169, 168, 167, 166, 165, 164, 163, 162, 161, 160, 159, 159, 158, 158, 157, 157, 156, 156, 155, 155, 154, 154, 153, 153, 152, 152, 151, 151, 150, 150, 149, 149, 148, 148, 147, 147, 146, 146, 145, 145, 144, 144, 143, 143, 143, 143, 142, 142, 142, 142, 141, 141, 141, 141, 140, 140, 140, 140, 139, 139, 139, 139, 138, 138, 138, 138, 137, 137, 137, 137, 136, 136, 136, 136, 135, 135, 135, 135, 134, 134, 134, 134, 133, 133, 133, 133, 132, 132, 132, 132, 131, 131, 131, 131, 130, 130, 130, 130, 129, 129, 129, 129, 128, 128, 128, 128, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 10, 10, 10, 10, 11, 11, 11, 11, 12, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 23, 24, 24, 25, 25, 26, 26, 27, 27, 28, 28, 29, 29, 30, 30, 31, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 50, 52, 54, 56, 58, 60, 62, 65, 69, 73, 77, 83, 91, 103};/* * Prototypes */static int pcm86_detect(struct address_info *);static int pcm86_open(int, int);static void pcm86_close(int);static void pcm86_output_block(int, unsigned long, int, int, int);static void pcm86_start_input(int, unsigned long, int, int, int);static int pcm86_ioctl(int, unsigned int, unsigned int, int);static int pcm86_prepare_for_input(int, int, int);static int pcm86_prepare_for_output(int, int, int);static void pcm86_reset(int);static void pcm86_halt_xfer(int);static void dsp73_send_command(unsigned char);static void dsp73_send_data(unsigned char);static void dsp73_init(void);static int set_format(int);static int set_speed(int);static int set_stereo(int);static void set_volume(int);static void fifo_start(int);static void fifo_stop(void);static void fifo_reset(void);static void fifo_output_block(void);static int fifo_send(pcm_data *, int);static void fifo_sendtrailer(int);static void fifo_send_stereo(pcm_data *, int);static void fifo_send_monoral(pcm_data *, int);static void fifo_send_stereo_ulaw(pcm_data *, int);static void fifo_send_stereo_8(pcm_data *, int, int);static void fifo_send_stereo_16le(pcm_data *, int, int);static void fifo_send_stereo_16be(pcm_data *, int, int);static void fifo_send_mono_ulaw(pcm_data *, int);static void fifo_send_mono_8(pcm_data *, int, int);static void fifo_send_mono_16le(pcm_data *, int, int);static void fifo_send_mono_16be(pcm_data *, int, int);static void fifo_input_block(void);static void fifo_recv(pcm_data *, int);static void fifo_recv_stereo(pcm_data *, int);static void fifo_recv_monoral(pcm_data *, int);static void fifo_recv_stereo_ulaw(pcm_data *, int);static void fifo_recv_stereo_8(pcm_data *, int, int);static void fifo_recv_stereo_16le(pcm_data *, int, int);static void fifo_recv_stereo_16be(pcm_data *, int, int);static void fifo_recv_mono_ulaw(pcm_data *, int);static void fifo_recv_mono_8(pcm_data *, int, int);static void fifo_recv_mono_16le(pcm_data *, int, int);static void fifo_recv_mono_16be(pcm_data *, int, int);static void pcm_stop(void);static void pcm_init(void);/* * Identity */static struct audio_operations pcm86_operations ={ "PC-9801-86 SoundBoard", /* filled in properly by auto configuration */ NOTHING_SPECIAL, ( AFMT_MU_LAW | AFMT_U8 | AFMT_S16_LE | AFMT_S16_BE | AFMT_S8 | AFMT_U16_LE | AFMT_U16_BE ), NULL, pcm86_open, pcm86_close, pcm86_output_block, pcm86_start_input, pcm86_ioctl, pcm86_prepare_for_input, pcm86_prepare_for_output, pcm86_reset, pcm86_halt_xfer, NULL, NULL};/* * Codes for internal use */static voiddsp73_send_command(unsigned char command){ /* wait for RDY */ while ((inb(pcm_s.iobase + 2) & 0x48) != 8); /* command mode */ outb(pcm_s.iobase + 2, (inb(pcm_s.iobase + 2) & 0x20) | 3); /* wait for RDY */ while ((inb(pcm_s.iobase + 2) & 0x48) != 8); /* send command */ outb(pcm_s.iobase + 4, command);}static voiddsp73_send_data(unsigned char data){ /* wait for RDY */ while ((inb(pcm_s.iobase + 2) & 0x48) != 8); /* data mode */ outb(pcm_s.iobase + 2, (inb(pcm_s.iobase + 2) & 0x20) | 0x83); /* wait for RDY */ while ((inb(pcm_s.iobase + 2) & 0x48) != 8); /* send command */ outb(pcm_s.iobase + 4, data);}static voiddsp73_init(void){ const unsigned char dspinst[15] = { 0x00, 0x00, 0x27, 0x3f, 0xe0, 0x01, 0x00, 0x00, 0x27, 0x36, 0x5a, 0x0d, 0x3e, 0x60, 0x04 }; unsigned char t; int i; /* reset DSP */ t = inb(pcm_s.iobase + 2); outb(pcm_s.iobase + 2, (t & 0x80) | 0x23); /* mute on */ dsp73_send_command(0x04); dsp73_send_data(0x6f); dsp73_send_data(0x3c); /* write DSP instructions */ dsp73_send_command(0x01); dsp73_send_data(0x00); for (i = 0; i < 16; i++) dsp73_send_data(dspinst[i]); /* mute off */ dsp73_send_command(0x04); dsp73_send_data(0x6f); dsp73_send_data(0x30); /* wait for RDY */ while ((inb(pcm_s.iobase + 2) & 0x48) != 8); outb(pcm_s.iobase + 2, 3);}static intset_format(int format){ switch (format) { case AFMT_MU_LAW: case AFMT_S8: case AFMT_U8: pcm_s.format = format; pcm_s.bytes = 1; /* 8bit */ break; case AFMT_S16_LE: case AFMT_U16_LE: case AFMT_S16_BE: case AFMT_U16_BE: pcm_s.format = format; pcm_s.bytes = 2; /* 16bit */ break; case AFMT_QUERY: break; default: return -1; } return pcm_s.format;}static intset_speed(int speed){ int i; if (speed < 4000) /* Minimum 4000Hz */ speed = 4000; if (speed > 44100) /* Maximum 44100Hz */ speed = 44100; for (i = 7; i >= 0; i--) { if (speed <= rates_tbl[i]) { pcm_s.chipspeedno = i; pcm_s.chipspeed = rates_tbl[i]; break; } } pcm_s.speed = speed; return speed;}static intset_stereo(int stereo){ pcm_s.stereo = stereo ? YES : NO; return pcm_s.stereo;}static voidset_volume(int volume){ if (volume < 0) volume = 0; if (volume > 15) volume = 15; pcm_s.volume = volume; outb(pcm_s.iobase + 6, 0xaf - volume); /* D/A -> LINE OUT */ outb(0x5f,0); outb(0x5f,0); outb(0x5f,0); outb(0x5f,0); outb(pcm_s.iobase + 6, 0x20); /* FM -> A/D */ outb(0x5f,0); outb(0x5f,0); outb(0x5f,0); outb(0x5f,0); outb(pcm_s.iobase + 6, 0x60); /* LINE IN -> A/D */ outb(0x5f,0); outb(0x5f,0); outb(0x5f,0); outb(0x5f,0);}static voidfifo_start(int mode){ unsigned char tmp; /* Set frame length & panpot(LR). */ tmp = inb(pcm_s.iobase + 10) & 0x88; outb(pcm_s.iobase + 10, tmp | ((pcm_s.bytes == 1) ? 0x72 : 0x32)); tmp = pcm_s.chipspeedno; if (mode == IMODE_INPUT) tmp |= 0x40; /* Reset intr. flag. */ outb(pcm_s.iobase + 8, tmp); outb(pcm_s.iobase + 8, tmp | 0x10); /* Enable FIFO intr. */ outb(pcm_s.iobase + 8, tmp | 0x30); /* Set intr. interval. */ outb(pcm_s.iobase + 10, pcm_s.intr_size / 128 - 1); /* Start intr. */ outb(pcm_s.iobase + 8, tmp | 0xb0);}static voidfifo_stop(void){ unsigned char tmp; /* Reset intr. flag, and disable FIFO intr. */ tmp = inb(pcm_s.iobase + 8) & 0x0f; outb(pcm_s.iobase + 8, tmp);}static voidfifo_reset(void){ unsigned char tmp; /* Reset FIFO. */ tmp = inb(pcm_s.iobase + 8) & 0x77; outb(pcm_s.iobase + 8, tmp | 0x8); outb(pcm_s.iobase + 8, tmp);}static voidfifo_output_block(void){ int chunksize, count; if (pcm_s.pdma_chunkcount) { /* Update chunksize and then send the next chunk to FIFO. */ chunksize = pcm_s.pdma_count / pcm_s.pdma_chunkcount--; count = fifo_send(pcm_s.pdma_buf, chunksize); } else { /* ??? something wrong... */ printk("pcm0: chunkcount overrun\n"); chunksize = count = 0; } if (((audio_devs[my_dev]->dmap->qlen < 2) && (pcm_s.pdma_chunkcount == 0)) || (count < pcm_s.intr_size)) { /* The sent chunk seems to be the last one. */ fifo_sendtrailer(pcm_s.intr_size); pcm_s.intr_last = YES; } pcm_s.pdma_buf += chunksize; pcm_s.pdma_count -= chunksize;}static intfifo_send(pcm_data *buf, int count){ int i, length, r, cnt, rslt; pcm_data *p;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -