doswss.c
资源名称:tcpmp.rar [点击查看]
上传用户:wstnjxml
上传日期:2014-04-03
资源大小:7248k
文件大小:13k
源码类别:
Windows CE
开发平台:
C/C++
- /* MikMod sound library
- (c) 1998, 1999 Miodrag Vallat and others - see file AUTHORS for
- complete list.
- This library is free software; you can redistribute it and/or modify
- it under the terms of the GNU Library General Public License as
- published by the Free Software Foundation; either version 2 of
- the License, or (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Library General Public License for more details.
- You should have received a copy of the GNU Library General Public
- License along with this library; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
- 02111-1307, USA.
- */
- /*==============================================================================
- $Id: doswss.c,v 1.1 2004/02/01 02:01:17 raph Exp $
- Windows Sound System I/O routines (CS42XX, ESS18XX, GUS+DaughterBoard etc)
- ==============================================================================*/
- /*
- Written by Andrew Zabolotny <bit@eltech.ru>
- */
- #include <stdlib.h>
- #include <dpmi.h>
- #include <go32.h>
- #include <dos.h>
- #include <sys/nearptr.h>
- #include <sys/farptr.h>
- #include <string.h>
- #include "doswss.h"
- /********************************************* Private variables/routines *****/
- __wss_state wss;
- /* WSS frequency rates... lower bit selects one of two frequency generators */
- static unsigned int wss_rates[14][2] = {
- {5510, 0x00 | WSSM_XTAL2},
- {6620, 0x0E | WSSM_XTAL2},
- {8000, 0x00 | WSSM_XTAL1},
- {9600, 0x0E | WSSM_XTAL1},
- {11025, 0x02 | WSSM_XTAL2},
- {16000, 0x02 | WSSM_XTAL1},
- {18900, 0x04 | WSSM_XTAL2},
- {22050, 0x06 | WSSM_XTAL2},
- {27420, 0x04 | WSSM_XTAL1},
- {32000, 0x06 | WSSM_XTAL1},
- {33075, 0x0C | WSSM_XTAL2},
- {37800, 0x08 | WSSM_XTAL2},
- {44100, 0x0A | WSSM_XTAL2},
- {48000, 0x0C | WSSM_XTAL1}
- };
- static void wss_irq()
- {
- /* Make sure its not a spurious IRQ */
- if (!irq_check(wss.irq_handle))
- return;
- wss.irqcount++;
- /* Clear IRQ status */
- outportb(WSS_STATUS, 0);
- /* Write transfer count again */
- __wss_outreg(WSSR_COUNT_LOW, wss.samples & 0xff);
- __wss_outreg(WSSR_COUNT_HIGH, wss.samples >> 8);
- irq_ack(wss.irq_handle);
- enable();
- if (wss.timer_callback)
- wss.timer_callback();
- }
- static void wss_irq_end()
- {
- }
- /* WSS accepts some conventional values instead of frequency in Hz... */
- static unsigned char __wss_getrate(unsigned int *freq)
- {
- int i, best = -1, delta = 0xffff;
- for (i = 0; i < 14; i++) {
- int newdelta = abs(wss_rates[i][0] - *freq);
- if (newdelta < delta)
- best = i, delta = newdelta;
- }
- *freq = wss_rates[best][0];
- return wss_rates[best][1];
- }
- /* Check if we really have a WSS compatible card on given address */
- static boolean __wss_ping()
- {
- /* Disable CODEC operations first */
- __wss_regbit_reset(WSSR_IFACE_CTRL, WSSM_PLAYBACK_ENABLE);
- /* Now put some harmless values in registers and check them */
- __wss_outreg(WSSR_COUNT_LOW, 0xaa);
- __wss_outreg(WSSR_COUNT_HIGH, 0x55);
- return (__wss_inreg(WSSR_COUNT_LOW) == 0xaa)
- && (__wss_inreg(WSSR_COUNT_HIGH) == 0x55);
- }
- static boolean __wss_reset()
- {
- int count;
- /* Disable output */
- wss_output(FALSE);
- /* Now select the test/initialization register */
- count = 10000;
- while (inportb(WSS_ADDR) != WSSR_TEST_INIT) {
- outportb(WSS_ADDR, WSSR_TEST_INIT);
- if (!--count)
- return FALSE;
- }
- count = 10000;
- while (inportb(WSS_DATA) & WSSM_CALIB_IN_PROGRESS) {
- outportb(WSS_ADDR, WSSR_TEST_INIT);
- if (!--count)
- return FALSE;
- }
- /* Enable playback IRQ */
- __wss_regbit_set(WSSR_PIN_CTRL, WSSM_IRQ_ENABLE);
- __wss_outreg(WSSR_IRQ_STATUS, WSSM_PLAYBACK_IRQ);
- /* Clear IRQ status */
- outportb(WSS_STATUS, 0);
- return TRUE;
- }
- static boolean __wss_setformat(unsigned char format)
- {
- int count;
- outportb(WSS_ADDR, WSSM_MCE | WSSR_PLAY_FORMAT);
- outportb(WSS_DATA, format);
- inportb(WSS_DATA); /* ERRATA SHEETS ... */
- inportb(WSS_DATA); /* ERRATA SHEETS ... */
- /* Wait end of syncronization ... */
- if (!__wss_wait())
- return FALSE;
- /* Turn off the ModeChangeEnable bit: do it until it works */
- count = 10000;
- while (inportb(WSS_ADDR) != WSSR_PLAY_FORMAT) {
- outportb(WSS_ADDR, WSSR_PLAY_FORMAT);
- if (!--count)
- return FALSE;
- }
- return __wss_reset();
- }
- /**************************************************** WSS detection stuff *****/
- static int __wss_irq_irqdetect(int irqno)
- {
- unsigned char status = inportb(WSS_STATUS);
- /* Clear IRQ status */
- outportb(WSS_STATUS, 0);
- /* Reset transfer counter */
- __wss_outreg(WSSR_COUNT_LOW, 0);
- __wss_outreg(WSSR_COUNT_HIGH, 0);
- return (status & WSSM_INT);
- }
- static boolean __wss_detect()
- {
- /* First find the port number */
- if (!wss.port) {
- static unsigned int wss_ports[] =
- { 0x32c, 0x530, 0x604, 0xE80, 0xF40 };
- int i;
- for (i = 0; i < 5; i++) {
- wss.port = wss_ports[i];
- if (__wss_ping())
- break;
- }
- if (i < 0) {
- wss.port = 0;
- return FALSE;
- }
- }
- /* Now disable output */
- wss_output(FALSE);
- /* Detect the DMA channel */
- if (!wss.dma) {
- static int __dma[] = { 0, 1, 3 };
- int i;
- /* Enable playback IRQ */
- __wss_regbit_set(WSSR_PIN_CTRL, WSSM_IRQ_ENABLE);
- __wss_outreg(WSSR_IRQ_STATUS, WSSM_PLAYBACK_IRQ);
- /* Start a short DMA transfer and check if DMA count is zero */
- for (i = 0; i < 3; i++) {
- unsigned int timer, status, freq = 44100;
- wss.dma = __dma[i];
- dma_disable(wss.dma);
- dma_set_mode(wss.dma, DMA_MODE_WRITE);
- dma_clear_ff(wss.dma);
- dma_set_count(wss.dma, 10);
- dma_enable(wss.dma);
- /* Clear IRQ status */
- outportb(WSS_STATUS, 0);
- __wss_setformat(__wss_getrate(&freq));
- __wss_outreg(WSSR_COUNT_LOW, 1);
- __wss_outreg(WSSR_COUNT_HIGH, 0);
- /* Tell codec to start transfer */
- __wss_regbit_set(WSSR_IFACE_CTRL, WSSM_PLAYBACK_ENABLE);
- _farsetsel(_dos_ds);
- timer = _farnspeekl(0x46c);
- while (_farnspeekl(0x46c) - timer <= 2)
- if (dma_get_count(wss.dma) == 0)
- break;
- __wss_regbit_reset(WSSR_IFACE_CTRL, WSSM_PLAYBACK_ENABLE);
- dma_disable(wss.dma);
- /* Now check if DMA transfer count is zero and an IRQ is pending */
- status = inportb(WSS_STATUS);
- outportb(WSS_STATUS, 0);
- if ((dma_get_count(wss.dma) == 0) && (status & WSSM_INT))
- break;
- wss.dma = 0;
- }
- if (!wss.dma)
- return FALSE;
- }
- /* Now detect the IRQ number */
- if (!wss.irq) {
- unsigned int i, irqmask, freq = 5510;
- unsigned long timer, delta = 0x7fffffff;
- /* IRQ can be one of 2,3,5,7,10 */
- irq_detect_start(0x04ac, __wss_irq_irqdetect);
- dma_disable(wss.dma);
- dma_set_mode(wss.dma, DMA_MODE_WRITE | DMA_MODE_AUTOINIT);
- dma_clear_ff(wss.dma);
- dma_set_count(wss.dma, 1);
- dma_enable(wss.dma);
- __wss_setformat(__wss_getrate(&freq));
- /* Clear IRQ status */
- outportb(WSS_STATUS, 0);
- __wss_outreg(WSSR_COUNT_LOW, 0);
- __wss_outreg(WSSR_COUNT_HIGH, 0);
- /* Prepare timeout counter */
- _farsetsel(_dos_ds);
- timer = _farnspeekl(0x46c);
- while (timer == _farnspeekl(0x46c));
- timer = _farnspeekl(0x46c);
- /* Reset all IRQ counters */
- irq_detect_clear();
- /* Tell codec to start transfer */
- __wss_regbit_set(WSSR_IFACE_CTRL, WSSM_PLAYBACK_ENABLE);
- /* Now wait 1/18 seconds */
- while (timer == _farnspeekl(0x46c));
- __wss_regbit_reset(WSSR_IFACE_CTRL, WSSM_PLAYBACK_ENABLE);
- dma_disable(wss.dma);
- /* Given frequency 5510Hz, a buffer size of 1 byte and a time interval
- of 1/18.2 second, we should have received about 302 interrupts */
- for (i = 2; i <= 10; i++) {
- int count = abs(302 - irq_detect_get(i, &irqmask));
- if (count < delta)
- wss.irq = i, delta = count;
- }
- if (delta > 150)
- wss.irq = 0;
- irq_detect_end();
- if (!wss.irq)
- return FALSE;
- }
- return TRUE;
- }
- /*************************************************** High-level interface *****/
- /* Detect whenever WSS is present and fill "wss" structure */
- boolean wss_detect()
- {
- char *env;
- /* Try to find the port and DMA from environment */
- env = getenv("WSS");
- while (env && *env) {
- /* Skip whitespace */
- while ((*env == ' ') || (*env == 't'))
- env++;
- if (!*env)
- break;
- switch (*env++) {
- case 'A':
- case 'a':
- if (!wss.port)
- wss.port = strtol(env, &env, 16);
- break;
- case 'I':
- case 'i':
- if (!wss.irq)
- wss.irq = strtol(env, &env, 10);
- break;
- case 'D':
- case 'd':
- if (!wss.dma)
- wss.dma = strtol(env, &env, 10);
- break;
- default:
- /* Skip other values */
- while (*env && (*env != ' ') && (*env != 't'))
- env++;
- break;
- }
- }
- /* Try to fill the gaps in wss hardware parameters */
- __wss_detect();
- if (!wss.port || !wss.irq || !wss.dma)
- return FALSE;
- if (!__wss_ping())
- return FALSE;
- if (!__wss_reset())
- return FALSE;
- wss.ok = 1;
- return TRUE;
- }
- /* Reset WSS */
- void wss_reset()
- {
- wss_stop_dma();
- __wss_reset();
- }
- /* Open WSS for usage */
- boolean wss_open()
- {
- __dpmi_meminfo struct_info;
- if (!wss.ok)
- if (!wss_detect())
- return FALSE;
- if (wss.open)
- return FALSE;
- /* Now lock the wss structure in memory */
- struct_info.address = __djgpp_base_address + (unsigned long)&wss;
- struct_info.size = sizeof(wss);
- if (__dpmi_lock_linear_region(&struct_info))
- return FALSE;
- /* Hook the WSS IRQ */
- wss.irq_handle =
- irq_hook(wss.irq, wss_irq, (long)wss_irq_end - (long)wss_irq);
- if (!wss.irq_handle) {
- __dpmi_unlock_linear_region(&struct_info);
- return FALSE;
- }
- /* Enable the interrupt */
- irq_enable(wss.irq_handle);
- if (wss.irq > 7)
- _irq_enable(2);
- wss.open++;
- return TRUE;
- }
- /* Finish working with WSS */
- boolean wss_close()
- {
- __dpmi_meminfo struct_info;
- if (!wss.open)
- return FALSE;
- wss.open--;
- /* Stop/free DMA buffer */
- wss_stop_dma();
- /* Unhook IRQ */
- irq_unhook(wss.irq_handle);
- wss.irq_handle = NULL;
- /* Unlock the wss structure */
- struct_info.address = __djgpp_base_address + (unsigned long)&wss;
- struct_info.size = sizeof(wss);
- __dpmi_unlock_linear_region(&struct_info);
- return TRUE;
- }
- /* Adjust frequency rate to nearest WSS available */
- unsigned int wss_adjust_freq(unsigned int freq)
- {
- __wss_getrate(&freq);
- return freq;
- }
- /* Enable/disable speaker output */
- /* Start playing from DMA buffer in either 8/16 bit mono/stereo */
- boolean wss_start_dma(unsigned char mode, unsigned int freq)
- {
- int dmabuffsize;
- unsigned char format;
- /* Stop DMA transfer if it is enabled */
- wss_stop_dma();
- /* Sanity check: we support only 8-bit unsigned and 16-bit signed formats */
- if (((mode & WSSMODE_16BITS) && !(mode & WSSMODE_SIGNED))
- || (!(mode & WSSMODE_16BITS) && (mode & WSSMODE_SIGNED)))
- return FALSE;
- /* Find the nearest frequency divisor (rate) */
- format = __wss_getrate(&freq);
- wss.mode = mode;
- /* Get a DMA buffer enough for a 1sec interval... 4K <= dmasize <= 32K */
- dmabuffsize = freq;
- if (mode & WSSMODE_STEREO)
- dmabuffsize *= 2;
- if (mode & WSSMODE_16BITS)
- dmabuffsize *= 2;
- dmabuffsize >>= 2;
- if (dmabuffsize < 4096)
- dmabuffsize = 4096;
- if (dmabuffsize > 32768)
- dmabuffsize = 32768;
- dmabuffsize = (dmabuffsize + 255) & 0xffffff00;
- wss.dma_buff = dma_allocate(wss.dma, dmabuffsize);
- if (!wss.dma_buff)
- return FALSE;
- /* Fill DMA buffer with silence */
- dmabuffsize = wss.dma_buff->size;
- if (mode & WSSMODE_SIGNED)
- memset(wss.dma_buff->linear, 0, dmabuffsize);
- else
- memset(wss.dma_buff->linear, 0x80, dmabuffsize);
- /* Check data size and build a WSSR_PLAY_FORMAT value accordingly */
- wss.samples = dmabuffsize;
- if (mode & WSSMODE_16BITS) {
- wss.samples >>= 1;
- format |= WSSM_16BITS;
- }
- if (mode & WSSMODE_STEREO) {
- wss.samples >>= 1;
- format |= WSSM_STEREO;
- }
- if (!__wss_setformat(format)) {
- wss_stop_dma();
- return FALSE;
- }
- /* Prime DMA for transfer */
- dma_start(wss.dma_buff, dmabuffsize, DMA_MODE_WRITE | DMA_MODE_AUTOINIT);
- /* Tell codec how many samples to transfer */
- wss.samples = (wss.samples >> 1) - 1;
- __wss_outreg(WSSR_COUNT_LOW, wss.samples & 0xff);
- __wss_outreg(WSSR_COUNT_HIGH, wss.samples >> 8);
- /* Tell codec to start transfer */
- __wss_regbit_set(WSSR_IFACE_CTRL, WSSM_PLAYBACK_ENABLE);
- return TRUE;
- }
- /* Stop playing from DMA buffer */
- void wss_stop_dma()
- {
- if (!wss.dma_buff)
- return;
- __wss_regbit_reset(WSSR_IFACE_CTRL, WSSM_PLAYBACK_ENABLE);
- dma_disable(wss.dma);
- dma_free(wss.dma_buff);
- wss.dma_buff = NULL;
- }
- /* Query current position/total size of the DMA buffer */
- void wss_query_dma(unsigned int *dma_size, unsigned int *dma_pos)
- {
- unsigned int dma_left;
- *dma_size = wss.dma_buff->size;
- /* It can happen we try to read DMA count when HI/LO bytes will be
- inconsistent */
- for (;;) {
- unsigned int dma_left_test;
- dma_clear_ff(wss.dma);
- dma_left_test = dma_get_count(wss.dma);
- dma_left = dma_get_count(wss.dma);
- if ((dma_left >= dma_left_test) && (dma_left - dma_left_test < 10))
- break;
- }
- *dma_pos = *dma_size - dma_left;
- }
- void wss_output(boolean enable)
- {
- if (enable)
- wss.curlevel = wss.level;
- else
- wss.curlevel = 0x3f;
- __wss_outreg(WSSR_MASTER_L, wss.curlevel);
- __wss_outreg(WSSR_MASTER_R, wss.curlevel);
- }
- void wss_level(int level)
- {
- if (level < 0)
- level = 0;
- if (level > 63)
- level = 63;
- wss.curlevel = wss.level = level ^ 63;
- __wss_outreg(WSSR_MASTER_L, wss.curlevel);
- __wss_outreg(WSSR_MASTER_R, wss.curlevel);
- }
- /* ex:set ts=4: */