doswss.c
上传用户:wstnjxml
上传日期:2014-04-03
资源大小:7248k
文件大小:13k
源码类别:

Windows CE

开发平台:

C/C++

  1. /* MikMod sound library
  2. (c) 1998, 1999 Miodrag Vallat and others - see file AUTHORS for
  3. complete list.
  4. This library is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU Library General Public License as
  6. published by the Free Software Foundation; either version 2 of
  7. the License, or (at your option) any later version.
  8.  
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12. GNU Library General Public License for more details.
  13.  
  14. You should have received a copy of the GNU Library General Public
  15. License along with this library; if not, write to the Free Software
  16. Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
  17. 02111-1307, USA.
  18. */
  19. /*==============================================================================
  20.   $Id: doswss.c,v 1.1 2004/02/01 02:01:17 raph Exp $
  21.   Windows Sound System I/O routines (CS42XX, ESS18XX, GUS+DaughterBoard etc)
  22. ==============================================================================*/
  23. /*
  24. Written by Andrew Zabolotny <bit@eltech.ru>
  25. */
  26. #include <stdlib.h>
  27. #include <dpmi.h>
  28. #include <go32.h>
  29. #include <dos.h>
  30. #include <sys/nearptr.h>
  31. #include <sys/farptr.h>
  32. #include <string.h>
  33. #include "doswss.h"
  34. /********************************************* Private variables/routines *****/
  35. __wss_state wss;
  36. /* WSS frequency rates... lower bit selects one of two frequency generators */
  37. static unsigned int wss_rates[14][2] = {
  38. {5510, 0x00 | WSSM_XTAL2},
  39. {6620, 0x0E | WSSM_XTAL2},
  40. {8000, 0x00 | WSSM_XTAL1},
  41. {9600, 0x0E | WSSM_XTAL1},
  42. {11025, 0x02 | WSSM_XTAL2},
  43. {16000, 0x02 | WSSM_XTAL1},
  44. {18900, 0x04 | WSSM_XTAL2},
  45. {22050, 0x06 | WSSM_XTAL2},
  46. {27420, 0x04 | WSSM_XTAL1},
  47. {32000, 0x06 | WSSM_XTAL1},
  48. {33075, 0x0C | WSSM_XTAL2},
  49. {37800, 0x08 | WSSM_XTAL2},
  50. {44100, 0x0A | WSSM_XTAL2},
  51. {48000, 0x0C | WSSM_XTAL1}
  52. };
  53. static void wss_irq()
  54. {
  55. /* Make sure its not a spurious IRQ */
  56. if (!irq_check(wss.irq_handle))
  57. return;
  58. wss.irqcount++;
  59. /* Clear IRQ status */
  60. outportb(WSS_STATUS, 0);
  61. /* Write transfer count again */
  62. __wss_outreg(WSSR_COUNT_LOW, wss.samples & 0xff);
  63. __wss_outreg(WSSR_COUNT_HIGH, wss.samples >> 8);
  64. irq_ack(wss.irq_handle);
  65. enable();
  66. if (wss.timer_callback)
  67. wss.timer_callback();
  68. }
  69. static void wss_irq_end()
  70. {
  71. }
  72. /* WSS accepts some conventional values instead of frequency in Hz... */
  73. static unsigned char __wss_getrate(unsigned int *freq)
  74. {
  75. int i, best = -1, delta = 0xffff;
  76. for (i = 0; i < 14; i++) {
  77. int newdelta = abs(wss_rates[i][0] - *freq);
  78. if (newdelta < delta)
  79. best = i, delta = newdelta;
  80. }
  81. *freq = wss_rates[best][0];
  82. return wss_rates[best][1];
  83. }
  84. /* Check if we really have a WSS compatible card on given address */
  85. static boolean __wss_ping()
  86. {
  87. /* Disable CODEC operations first */
  88. __wss_regbit_reset(WSSR_IFACE_CTRL, WSSM_PLAYBACK_ENABLE);
  89. /* Now put some harmless values in registers and check them */
  90. __wss_outreg(WSSR_COUNT_LOW, 0xaa);
  91. __wss_outreg(WSSR_COUNT_HIGH, 0x55);
  92. return (__wss_inreg(WSSR_COUNT_LOW) == 0xaa)
  93.   && (__wss_inreg(WSSR_COUNT_HIGH) == 0x55);
  94. }
  95. static boolean __wss_reset()
  96. {
  97. int count;
  98. /* Disable output */
  99. wss_output(FALSE);
  100. /* Now select the test/initialization register */
  101. count = 10000;
  102. while (inportb(WSS_ADDR) != WSSR_TEST_INIT) {
  103. outportb(WSS_ADDR, WSSR_TEST_INIT);
  104. if (!--count)
  105. return FALSE;
  106. }
  107. count = 10000;
  108. while (inportb(WSS_DATA) & WSSM_CALIB_IN_PROGRESS) {
  109. outportb(WSS_ADDR, WSSR_TEST_INIT);
  110. if (!--count)
  111. return FALSE;
  112. }
  113. /* Enable playback IRQ */
  114. __wss_regbit_set(WSSR_PIN_CTRL, WSSM_IRQ_ENABLE);
  115. __wss_outreg(WSSR_IRQ_STATUS, WSSM_PLAYBACK_IRQ);
  116. /* Clear IRQ status */
  117. outportb(WSS_STATUS, 0);
  118. return TRUE;
  119. }
  120. static boolean __wss_setformat(unsigned char format)
  121. {
  122. int count;
  123. outportb(WSS_ADDR, WSSM_MCE | WSSR_PLAY_FORMAT);
  124. outportb(WSS_DATA, format);
  125. inportb(WSS_DATA); /* ERRATA SHEETS ... */
  126. inportb(WSS_DATA); /* ERRATA SHEETS ... */
  127. /* Wait end of syncronization ... */
  128. if (!__wss_wait())
  129. return FALSE;
  130. /* Turn off the ModeChangeEnable bit: do it until it works */
  131. count = 10000;
  132. while (inportb(WSS_ADDR) != WSSR_PLAY_FORMAT) {
  133. outportb(WSS_ADDR, WSSR_PLAY_FORMAT);
  134. if (!--count)
  135. return FALSE;
  136. }
  137. return __wss_reset();
  138. }
  139. /**************************************************** WSS detection stuff *****/
  140. static int __wss_irq_irqdetect(int irqno)
  141. {
  142. unsigned char status = inportb(WSS_STATUS);
  143. /* Clear IRQ status */
  144. outportb(WSS_STATUS, 0);
  145. /* Reset transfer counter */
  146. __wss_outreg(WSSR_COUNT_LOW, 0);
  147. __wss_outreg(WSSR_COUNT_HIGH, 0);
  148. return (status & WSSM_INT);
  149. }
  150. static boolean __wss_detect()
  151. {
  152. /* First find the port number */
  153. if (!wss.port) {
  154. static unsigned int wss_ports[] =
  155.   { 0x32c, 0x530, 0x604, 0xE80, 0xF40 };
  156. int i;
  157. for (i = 0; i < 5; i++) {
  158. wss.port = wss_ports[i];
  159. if (__wss_ping())
  160. break;
  161. }
  162. if (i < 0) {
  163. wss.port = 0;
  164. return FALSE;
  165. }
  166. }
  167. /* Now disable output */
  168. wss_output(FALSE);
  169. /* Detect the DMA channel */
  170. if (!wss.dma) {
  171. static int __dma[] = { 0, 1, 3 };
  172. int i;
  173. /* Enable playback IRQ */
  174. __wss_regbit_set(WSSR_PIN_CTRL, WSSM_IRQ_ENABLE);
  175. __wss_outreg(WSSR_IRQ_STATUS, WSSM_PLAYBACK_IRQ);
  176. /* Start a short DMA transfer and check if DMA count is zero */
  177. for (i = 0; i < 3; i++) {
  178. unsigned int timer, status, freq = 44100;
  179. wss.dma = __dma[i];
  180. dma_disable(wss.dma);
  181. dma_set_mode(wss.dma, DMA_MODE_WRITE);
  182. dma_clear_ff(wss.dma);
  183. dma_set_count(wss.dma, 10);
  184. dma_enable(wss.dma);
  185. /* Clear IRQ status */
  186. outportb(WSS_STATUS, 0);
  187. __wss_setformat(__wss_getrate(&freq));
  188. __wss_outreg(WSSR_COUNT_LOW, 1);
  189. __wss_outreg(WSSR_COUNT_HIGH, 0);
  190. /* Tell codec to start transfer */
  191. __wss_regbit_set(WSSR_IFACE_CTRL, WSSM_PLAYBACK_ENABLE);
  192. _farsetsel(_dos_ds);
  193. timer = _farnspeekl(0x46c);
  194. while (_farnspeekl(0x46c) - timer <= 2)
  195. if (dma_get_count(wss.dma) == 0)
  196. break;
  197. __wss_regbit_reset(WSSR_IFACE_CTRL, WSSM_PLAYBACK_ENABLE);
  198. dma_disable(wss.dma);
  199. /* Now check if DMA transfer count is zero and an IRQ is pending */
  200. status = inportb(WSS_STATUS);
  201. outportb(WSS_STATUS, 0);
  202. if ((dma_get_count(wss.dma) == 0) && (status & WSSM_INT))
  203. break;
  204. wss.dma = 0;
  205. }
  206. if (!wss.dma)
  207. return FALSE;
  208. }
  209. /* Now detect the IRQ number */
  210. if (!wss.irq) {
  211. unsigned int i, irqmask, freq = 5510;
  212. unsigned long timer, delta = 0x7fffffff;
  213. /* IRQ can be one of 2,3,5,7,10 */
  214. irq_detect_start(0x04ac, __wss_irq_irqdetect);
  215. dma_disable(wss.dma);
  216. dma_set_mode(wss.dma, DMA_MODE_WRITE | DMA_MODE_AUTOINIT);
  217. dma_clear_ff(wss.dma);
  218. dma_set_count(wss.dma, 1);
  219. dma_enable(wss.dma);
  220. __wss_setformat(__wss_getrate(&freq));
  221. /* Clear IRQ status */
  222. outportb(WSS_STATUS, 0);
  223. __wss_outreg(WSSR_COUNT_LOW, 0);
  224. __wss_outreg(WSSR_COUNT_HIGH, 0);
  225. /* Prepare timeout counter */
  226. _farsetsel(_dos_ds);
  227. timer = _farnspeekl(0x46c);
  228. while (timer == _farnspeekl(0x46c));
  229. timer = _farnspeekl(0x46c);
  230. /* Reset all IRQ counters */
  231. irq_detect_clear();
  232. /* Tell codec to start transfer */
  233. __wss_regbit_set(WSSR_IFACE_CTRL, WSSM_PLAYBACK_ENABLE);
  234. /* Now wait 1/18 seconds */
  235. while (timer == _farnspeekl(0x46c));
  236. __wss_regbit_reset(WSSR_IFACE_CTRL, WSSM_PLAYBACK_ENABLE);
  237. dma_disable(wss.dma);
  238. /* Given frequency 5510Hz, a buffer size of 1 byte and a time interval
  239.    of 1/18.2 second, we should have received about 302 interrupts */
  240. for (i = 2; i <= 10; i++) {
  241. int count = abs(302 - irq_detect_get(i, &irqmask));
  242. if (count < delta)
  243. wss.irq = i, delta = count;
  244. }
  245. if (delta > 150)
  246. wss.irq = 0;
  247. irq_detect_end();
  248. if (!wss.irq)
  249. return FALSE;
  250. }
  251. return TRUE;
  252. }
  253. /*************************************************** High-level interface *****/
  254. /* Detect whenever WSS is present and fill "wss" structure */
  255. boolean wss_detect()
  256. {
  257. char *env;
  258. /* Try to find the port and DMA from environment */
  259. env = getenv("WSS");
  260. while (env && *env) {
  261. /* Skip whitespace */
  262. while ((*env == ' ') || (*env == 't'))
  263. env++;
  264. if (!*env)
  265. break;
  266. switch (*env++) {
  267.   case 'A':
  268.   case 'a':
  269. if (!wss.port)
  270. wss.port = strtol(env, &env, 16);
  271. break;
  272.   case 'I':
  273.   case 'i':
  274. if (!wss.irq)
  275. wss.irq = strtol(env, &env, 10);
  276. break;
  277.   case 'D':
  278.   case 'd':
  279. if (!wss.dma)
  280. wss.dma = strtol(env, &env, 10);
  281. break;
  282.   default:
  283. /* Skip other values */
  284. while (*env && (*env != ' ') && (*env != 't'))
  285. env++;
  286. break;
  287. }
  288. }
  289. /* Try to fill the gaps in wss hardware parameters */
  290. __wss_detect();
  291. if (!wss.port || !wss.irq || !wss.dma)
  292. return FALSE;
  293. if (!__wss_ping())
  294. return FALSE;
  295. if (!__wss_reset())
  296. return FALSE;
  297. wss.ok = 1;
  298. return TRUE;
  299. }
  300. /* Reset WSS */
  301. void wss_reset()
  302. {
  303. wss_stop_dma();
  304. __wss_reset();
  305. }
  306. /* Open WSS for usage */
  307. boolean wss_open()
  308. {
  309. __dpmi_meminfo struct_info;
  310. if (!wss.ok)
  311. if (!wss_detect())
  312. return FALSE;
  313. if (wss.open)
  314. return FALSE;
  315. /* Now lock the wss structure in memory */
  316. struct_info.address = __djgpp_base_address + (unsigned long)&wss;
  317. struct_info.size = sizeof(wss);
  318. if (__dpmi_lock_linear_region(&struct_info))
  319. return FALSE;
  320. /* Hook the WSS IRQ */
  321. wss.irq_handle =
  322.   irq_hook(wss.irq, wss_irq, (long)wss_irq_end - (long)wss_irq);
  323. if (!wss.irq_handle) {
  324. __dpmi_unlock_linear_region(&struct_info);
  325. return FALSE;
  326. }
  327. /* Enable the interrupt */
  328. irq_enable(wss.irq_handle);
  329. if (wss.irq > 7)
  330. _irq_enable(2);
  331. wss.open++;
  332. return TRUE;
  333. }
  334. /* Finish working with WSS */
  335. boolean wss_close()
  336. {
  337. __dpmi_meminfo struct_info;
  338. if (!wss.open)
  339. return FALSE;
  340. wss.open--;
  341. /* Stop/free DMA buffer */
  342. wss_stop_dma();
  343. /* Unhook IRQ */
  344. irq_unhook(wss.irq_handle);
  345. wss.irq_handle = NULL;
  346. /* Unlock the wss structure */
  347. struct_info.address = __djgpp_base_address + (unsigned long)&wss;
  348. struct_info.size = sizeof(wss);
  349. __dpmi_unlock_linear_region(&struct_info);
  350. return TRUE;
  351. }
  352. /* Adjust frequency rate to nearest WSS available */
  353. unsigned int wss_adjust_freq(unsigned int freq)
  354. {
  355. __wss_getrate(&freq);
  356. return freq;
  357. }
  358. /* Enable/disable speaker output */
  359. /* Start playing from DMA buffer in either 8/16 bit mono/stereo */
  360. boolean wss_start_dma(unsigned char mode, unsigned int freq)
  361. {
  362. int dmabuffsize;
  363. unsigned char format;
  364. /* Stop DMA transfer if it is enabled */
  365. wss_stop_dma();
  366. /* Sanity check: we support only 8-bit unsigned and 16-bit signed formats */
  367. if (((mode & WSSMODE_16BITS) && !(mode & WSSMODE_SIGNED))
  368. || (!(mode & WSSMODE_16BITS) && (mode & WSSMODE_SIGNED)))
  369. return FALSE;
  370. /* Find the nearest frequency divisor (rate) */
  371. format = __wss_getrate(&freq);
  372. wss.mode = mode;
  373. /* Get a DMA buffer enough for a 1sec interval... 4K <= dmasize <= 32K */
  374. dmabuffsize = freq;
  375. if (mode & WSSMODE_STEREO)
  376. dmabuffsize *= 2;
  377. if (mode & WSSMODE_16BITS)
  378. dmabuffsize *= 2;
  379. dmabuffsize >>= 2;
  380. if (dmabuffsize < 4096)
  381. dmabuffsize = 4096;
  382. if (dmabuffsize > 32768)
  383. dmabuffsize = 32768;
  384. dmabuffsize = (dmabuffsize + 255) & 0xffffff00;
  385. wss.dma_buff = dma_allocate(wss.dma, dmabuffsize);
  386. if (!wss.dma_buff)
  387. return FALSE;
  388. /* Fill DMA buffer with silence */
  389. dmabuffsize = wss.dma_buff->size;
  390. if (mode & WSSMODE_SIGNED)
  391. memset(wss.dma_buff->linear, 0, dmabuffsize);
  392. else
  393. memset(wss.dma_buff->linear, 0x80, dmabuffsize);
  394. /* Check data size and build a WSSR_PLAY_FORMAT value accordingly */
  395. wss.samples = dmabuffsize;
  396. if (mode & WSSMODE_16BITS) {
  397. wss.samples >>= 1;
  398. format |= WSSM_16BITS;
  399. }
  400. if (mode & WSSMODE_STEREO) {
  401. wss.samples >>= 1;
  402. format |= WSSM_STEREO;
  403. }
  404. if (!__wss_setformat(format)) {
  405. wss_stop_dma();
  406. return FALSE;
  407. }
  408. /* Prime DMA for transfer */
  409. dma_start(wss.dma_buff, dmabuffsize, DMA_MODE_WRITE | DMA_MODE_AUTOINIT);
  410. /* Tell codec how many samples to transfer */
  411. wss.samples = (wss.samples >> 1) - 1;
  412. __wss_outreg(WSSR_COUNT_LOW, wss.samples & 0xff);
  413. __wss_outreg(WSSR_COUNT_HIGH, wss.samples >> 8);
  414. /* Tell codec to start transfer */
  415. __wss_regbit_set(WSSR_IFACE_CTRL, WSSM_PLAYBACK_ENABLE);
  416. return TRUE;
  417. }
  418. /* Stop playing from DMA buffer */
  419. void wss_stop_dma()
  420. {
  421. if (!wss.dma_buff)
  422. return;
  423. __wss_regbit_reset(WSSR_IFACE_CTRL, WSSM_PLAYBACK_ENABLE);
  424. dma_disable(wss.dma);
  425. dma_free(wss.dma_buff);
  426. wss.dma_buff = NULL;
  427. }
  428. /* Query current position/total size of the DMA buffer */
  429. void wss_query_dma(unsigned int *dma_size, unsigned int *dma_pos)
  430. {
  431. unsigned int dma_left;
  432. *dma_size = wss.dma_buff->size;
  433. /* It can happen we try to read DMA count when HI/LO bytes will be
  434.    inconsistent */
  435. for (;;) {
  436. unsigned int dma_left_test;
  437. dma_clear_ff(wss.dma);
  438. dma_left_test = dma_get_count(wss.dma);
  439. dma_left = dma_get_count(wss.dma);
  440. if ((dma_left >= dma_left_test) && (dma_left - dma_left_test < 10))
  441. break;
  442. }
  443. *dma_pos = *dma_size - dma_left;
  444. }
  445. void wss_output(boolean enable)
  446. {
  447. if (enable)
  448. wss.curlevel = wss.level;
  449. else
  450. wss.curlevel = 0x3f;
  451. __wss_outreg(WSSR_MASTER_L, wss.curlevel);
  452. __wss_outreg(WSSR_MASTER_R, wss.curlevel);
  453. }
  454. void wss_level(int level)
  455. {
  456. if (level < 0)
  457. level = 0;
  458. if (level > 63)
  459. level = 63;
  460. wss.curlevel = wss.level = level ^ 63;
  461. __wss_outreg(WSSR_MASTER_L, wss.curlevel);
  462. __wss_outreg(WSSR_MASTER_R, wss.curlevel);
  463. }
  464. /* ex:set ts=4: */