Back to home page

Quest Cross Reference

 
 

    


Warning, cross-references for /kernel/drivers/sb16/sound.c need to be fixed.

0001 /*                    The Quest Operating System
0002  *  Copyright (C) 2005-2010  Richard West, Boston University
0003  *
0004  *  This program is free software: you can redistribute it and/or modify
0005  *  it under the terms of the GNU General Public License as published by
0006  *  the Free Software Foundation, either version 3 of the License, or
0007  *  (at your option) any later version.
0008  *
0009  *  This program is distributed in the hope that it will be useful,
0010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
0011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0012  *  GNU General Public License for more details.
0013  *
0014  *  You should have received a copy of the GNU General Public License
0015  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
0016  */
0017 
0018 #include "types.h"
0019 #include "drivers/sb16/sound.h"
0020 #include "kernel.h"
0021 #include "mem/mem.h"
0022 #include "fs/filesys.h"
0023 #include "smp/smp.h"
0024 #include "smp/apic.h"
0025 #include "util/printf.h"
0026 
0027 PRIVATE uint16 dsp_version;     // Version of the Digital Sound Processor
0028 
0029 // Driver capabilities
0030 PRIVATE SB_CAPABILITY driver_capability;        // Depends on the SB card capability
0031 
0032 PRIVATE uint16 dsp_base_address = 0x220;        // I/O Base Address of the DSP
0033 PRIVATE uint8 dsp_irq_number = 5;       // IRQ used for DMA transfer
0034 PRIVATE uint8 dsp_dma_channel_8 = 1;    // DMA channel in 8-bit mode
0035 PRIVATE uint8 dsp_dma_channel_16 = 5;   // DMA channel in 16-bit mode
0036 
0037 #ifdef __8_BIT__
0038 /* Port settings for page, count & memory address DMA registers (8-bit mode) */
0039 PRIVATE uint8 driver_dma_page[4] = { 0x87, 0x83, 0x81, 0x82 };
0040 PRIVATE uint8 driver_dma_length[4] = { 0x01, 0x03, 0x05, 0x07 };
0041 PRIVATE uint8 driver_dma_address[4] = { 0x00, 0x02, 0x04, 0x06 };
0042 #endif
0043 
0044 /* Port settings for page, count & memory address DMA registers (16-bit mode) */
0045 PRIVATE uint8 driver_dma_page[4] = { 0x8F, 0x8B, 0x89, 0x8A };
0046 PRIVATE uint8 driver_dma_length[4] = { 0xC2, 0xC6, 0xCA, 0xCE };
0047 PRIVATE uint8 driver_dma_address[4] = { 0xC0, 0xC4, 0xC8, 0xCC };
0048 
0049 /* Virtual address of DMA buffer */
0050 static uint32 dma_buffer_virt_base;
0051 /* Base physical address of a 64KB DMA buffer */
0052 uint32 dma_buffer_phys_base;
0053 
0054 static int filesize;
0055 static char filebuffer[0x10000];
0056 
0057 bool
0058 sb_dsp_reset (uint16 base_address)
0059 {
0060   int i, timeout;
0061   // int      start_time;
0062 
0063   // Write a 1 to the SB RESET port
0064   outb (1, base_address + SB_DSP_RESET);
0065 
0066   // Wait for >= 3 microseconds
0067   for (i = 0; i < 6; i++)
0068     inb (base_address + SB_DSP_WRITE_BUFFER_STATUS);
0069 
0070   // Write a 0 to the SB RESET port
0071   outb (0, base_address + SB_DSP_RESET);
0072 
0073   timeout = 1000;
0074 
0075   // Read the byte from the DATA_AVAILABLE port until bit 7 = 1
0076   while (timeout > 0 && (inb (base_address + SB_DSP_READ_BUFFER_STATUS) < 0x80))
0077     timeout--;
0078 
0079   if (timeout <= 0)
0080     return SB_RESET_DSP;
0081 
0082   timeout = 1000;
0083 
0084   // Poll for a ready byte (AAh) from the READ DATA port
0085   while (timeout > 0 && ((i = inb (base_address + SB_DSP_READ_DATA)) != 0xAA))
0086     timeout--;
0087 
0088   if (i != 0xAA)
0089     return (SB_RESET_DSP);
0090   return (SB_OK);
0091 }
0092 
0093 
0094 bool
0095 sb_dsp_detect_base_address (uint16 * base_address)
0096 {
0097 
0098   uint16 address;
0099 
0100   for (address = 0x220; address <= 0x280; address += 0x020)
0101     if (!sb_dsp_reset (address)) {
0102       *base_address = address;
0103       dsp_base_address = address;
0104       return (SB_OK);
0105     }
0106 
0107   return (SB_DETECT_DSP);
0108 }
0109 
0110 
0111 bool
0112 sb_dsp_detect_irq_number (uint16 base_address, uint8 * irq_number)
0113 {
0114 
0115   uint8 b;
0116 
0117   outb (INTERRUPT_SETUP, base_address + MIXER_REG_ADDR_PORT);
0118   b = inb (base_address + MIXER_DATA_PORT);
0119 
0120   switch (b) {                  /* Possible IRQ settings for sound card */
0121   case 1:
0122     dsp_irq_number = 2;
0123     break;                      /* IRQ 2 */
0124   case 2:
0125     dsp_irq_number = 5;
0126     break;                      /* IRQ 5 */
0127   case 4:
0128     dsp_irq_number = 7;
0129     break;                      /* IRQ 7 */
0130   case 8:
0131     dsp_irq_number = 10;
0132     break;                      /* IRQ 10 */
0133   default:
0134     dsp_irq_number = 0;         /* Error */
0135   }
0136 
0137   if ((*irq_number = dsp_irq_number))
0138     return (SB_OK);
0139   else
0140     return (SB_INVALID_IRQ);
0141 }
0142 
0143 
0144 bool
0145 sb_dsp_detect_dma (uint16 base_address, uint8 * dma8, uint8 * dma16)
0146 {
0147 
0148   uint8 b;
0149 
0150   outb (DMA_SETUP, base_address + MIXER_REG_ADDR_PORT);
0151   b = inb (base_address + MIXER_DATA_PORT);
0152 
0153   *dma8 = b & 0x08 ? 3 : b & 0x02 ? 1 : 0;
0154   *dma16 = b >> 7 ? 7 : b >> 6 ? 6 : 5;
0155 
0156   if (*dma8 && *dma16)
0157     return (SB_OK);
0158   else
0159     return (SB_INVALID_DMA);
0160 }
0161 
0162 
0163 
0164 bool
0165 sb_dsp_write (uint8 value)
0166 {
0167 
0168   if (!dsp_base_address)
0169     return (SB_NOT_INITIALIZED);
0170 
0171   // Read the DSP's BUFFER_STATUS port until bit 7 = 0
0172   while (inb (dsp_base_address + SB_DSP_WRITE_BUFFER_STATUS) & 0x80);
0173 
0174   // Write the value to the WRITE command/data port
0175   outb (value, dsp_base_address + SB_DSP_WRITE);
0176   return (SB_OK);
0177 }
0178 
0179 
0180 bool
0181 sb_dsp_read (uint8 * value)
0182 {
0183 
0184   if (!dsp_base_address)
0185     return (SB_NOT_INITIALIZED);
0186 
0187   // Read the DSP's DATA_AVAILABLE port until bit 7 = 1
0188   while (inb (dsp_base_address + SB_DSP_READ_BUFFER_STATUS) < 0x80);
0189 
0190   // Read the data from the READ_DATA port
0191   *value = inb (dsp_base_address + SB_DSP_READ_DATA);
0192   return (SB_OK);
0193 }
0194 
0195 
0196 bool
0197 sb_speaker_on (void)
0198 {
0199 
0200   return (sb_dsp_write (SB_SPEAKER_ON));
0201 }
0202 
0203 bool
0204 sb_speaker_off (void)
0205 {
0206 
0207   return (sb_dsp_write (SB_SPEAKER_OFF));
0208 }
0209 
0210 
0211 // Read the DSP version and sets capabilities
0212 //
0213 // Sound-Blaster AWE32   >=0x040C
0214 // Sound-Blaster 16      >=0x0400
0215 // Sound-Blaster Pro     >=0x0300
0216 // Sound-Blaster 2.0     >=0x0201
0217 // Sound-Blaster 1.0/1.5 else
0218 bool
0219 sb_dsp_get_version (uint16 * version)
0220 {
0221   uint8 value;
0222   bool result;
0223 
0224   if ((result = sb_dsp_write (SB_DSP_VERSION)))
0225     return (result);
0226 
0227   if ((result = sb_dsp_read (&value)))
0228     return (result);
0229 
0230   dsp_version = (uint16) value << 8;
0231   if ((result = sb_dsp_read (&value)))
0232     return (result);
0233   dsp_version += value;
0234   *version = dsp_version;
0235 
0236   if (dsp_version >= 0x040C)
0237     driver_capability = capability_sb_awe32;
0238   else if (dsp_version >= 0x0400)
0239     driver_capability = capability_sb_16;
0240   else if (dsp_version >= 0x0300)
0241     driver_capability = capability_sb_pro;
0242   else if (dsp_version >= 0x0201)
0243     driver_capability = capability_sb_20;
0244   else
0245     driver_capability = capability_sb_10;
0246   return (SB_OK);
0247 }
0248 
0249 
0250 PRIVATE bool
0251 driver_set_time_constant (uint16 frequency)
0252 {
0253 
0254   // Reset the mixer
0255   sb_mixer_register_set (MIXER_RESET, 0);
0256 
0257   // Set mono mode
0258   sb_mixer_register_set (MIXER_OUTPUT, 0x13);
0259 
0260   /* Sets the time constant / sampling rate for SB16 and above, DSP version
0261    * 4.xx */
0262   sb_dsp_write (0x41);
0263   sb_dsp_write (frequency >> 8);
0264   sb_dsp_write (frequency & 0xFF);
0265 
0266   return (SB_OK);
0267 }
0268 
0269 
0270 PRIVATE void
0271 driver_setup_dma_transfer (int memory_size)
0272 {
0273 
0274   /* Disable DMA */
0275   outb (0x04 + dsp_dma_channel_8, SB_DMA_MASK_16);
0276 
0277   /* Clear byte pointer flip-flop ready for DMA reprogramming */
0278   outb (0, SB_DMA_CLEAR_16);
0279 
0280   /* Single-cycle, addr increment, single-mode, read transfer */
0281   outb (0x48 + dsp_dma_channel_8, SB_DMA_MODE_16);
0282 
0283   /* 64K aligned DMA buffer physical "page" info */
0284   outb ((dma_buffer_phys_base >> 16) & 0xFF,
0285         driver_dma_page[dsp_dma_channel_8]);
0286   outb (0 /* offset (low byte) in 64K "page" --??-- for now, 0 */ ,
0287         driver_dma_address[dsp_dma_channel_8]);
0288   outb (0 /* offset (high byte, >>8) in 64K "page" --??-- for now, 0 */ ,
0289         driver_dma_address[dsp_dma_channel_8]);
0290   outb ((memory_size - 1) & 0xFF /* (DMA memory size-1) low byte */ ,
0291         driver_dma_length[dsp_dma_channel_8]);
0292   outb ((memory_size - 1) >> 8 /* (DMA memory size-1) high byte, >>8 */ ,
0293         driver_dma_length[dsp_dma_channel_8]);
0294 
0295   /* Enable DMA */
0296   outb (0x00 + dsp_dma_channel_8, SB_DMA_MASK_16);
0297 }
0298 
0299 PRIVATE void
0300 driver_setup_dsp_transfer (int memory_size)
0301 {
0302 
0303   memory_size >>= 1;
0304 
0305   /* 16-bit DMA mode digitized sound I/O */
0306   sb_dsp_write (0xB0);          /* command */
0307   sb_dsp_write (0x00);          /* mode: mono signal, unsigned data */
0308   sb_dsp_write ((memory_size - 1) & 0xFF);      /* length - low byte */
0309   sb_dsp_write ((memory_size - 1) >> 8);        /* length - high byte */
0310 }
0311 
0312 
0313 // Write a value to the mixer register
0314 bool
0315 sb_mixer_register_set (uint8 index, uint8 value)
0316 {
0317   if (!dsp_base_address)
0318     return (SB_NOT_INITIALIZED);
0319 
0320   // Set the register index
0321   outb (index, dsp_base_address + MIXER_REG_ADDR_PORT);
0322 
0323   // Write the value to the mixer register
0324   outb (value, dsp_base_address + MIXER_DATA_PORT);
0325   return (SB_OK);
0326 }
0327 
0328 
0329 // Read a value from the mixer register
0330 bool
0331 sb_mixer_register_get (uint8 index, uint8 * value)
0332 {
0333   if (!dsp_base_address)
0334     return (SB_NOT_INITIALIZED);
0335 
0336   // Set the register index
0337   outb (index, dsp_base_address + MIXER_REG_ADDR_PORT);
0338 
0339   // Read the value of the mixer register
0340   *value = inb (dsp_base_address + MIXER_DATA_PORT);
0341   return (SB_OK);
0342 }
0343 
0344 
0345 static void
0346 play_sample (void)
0347 {
0348 
0349   static int fileoffset;
0350   int chunksize;
0351 
0352   if (!filesize)
0353     /* all finished */
0354     return;
0355 
0356   chunksize = filesize > SB_MEMORY_SIZE ? SB_MEMORY_SIZE : filesize;
0357 
0358   memcpy ((void *) dma_buffer_virt_base, filebuffer + fileoffset, chunksize);
0359   fileoffset += chunksize;
0360 
0361   /* Single-cycle mode: need to reinitialise dma/dsp for next block */
0362   driver_setup_dma_transfer (chunksize);
0363   driver_setup_dsp_transfer (chunksize);
0364 
0365   filesize -= chunksize;
0366 }
0367 
0368 
0369 /* IRQ5 soundcard interrupt handler: --??-- in future, should not be hardcoded
0370    to specific IRQ */
0371 void
0372 _soundcard (void)
0373 {
0374 
0375   inb (dsp_base_address + SB_IRQ_ACK_16);
0376 
0377   play_sample ();
0378 
0379   /* Acknowledge the interrupt */
0380     /****************************************
0381      * if (dsp_irq_number == 10)            *
0382      *     outb (0x20, SB_PIC2_EOI);        *
0383      * outb (0x20, SB_PIC1_EOI);            *
0384      ****************************************/
0385   send_eoi ();
0386 }
0387 
0388 
0389 // Install the Sound Blaster Driver
0390 // 'frequency': sound driver frequency (Hz)
0391 // 'stereo': use stereo mode (not currently supported)
0392 bool
0393 sb_install_driver (uint16 frequency, bool use_stereo)
0394 {
0395   bool result;
0396 
0397   if ((result = driver_set_time_constant (frequency)))
0398     return (result);
0399 
0400   play_sample ();
0401 
0402   return (SB_OK);
0403 }
0404 
0405 
0406 bool
0407 sb_read_raw (char *pathname)
0408 {
0409 
0410   filesize = vfs_dir (pathname);        /* --??-- Need error checking */
0411 
0412   vfs_read (pathname, filebuffer, filesize);
0413 
0414   return (SB_OK);
0415 }
0416 
0417 
0418 bool
0419 sb16_init (void)
0420 {
0421 
0422   int i;
0423 
0424   /* Search for an unallocated contiguous aligned 64K block.  Each 32-bit
0425      section of the bitmap describes 32 4K pages, i.e. 128K.  We want to
0426      stay under the 16MB 24-bit DMA boundary, so we can scan as far as the
0427      16M/4K = 4096th bit. */
0428   for (i = 0; i < 0x80; i++)
0429     if ((mm_table[i] & 0xFFFF) == 0xFFFF) {
0430       /* found a free 64K region on a 128K boundary */
0431       mm_table[i] &= 0xFFFF0000;
0432       dma_buffer_phys_base = i << 17;
0433       break;
0434     } else if ((mm_table[i] & 0xFFFF0000) == 0xFFFF0000) {
0435       /* found a free 64K region halfway through a 128K boundary */
0436       mm_table[i] &= 0xFFFF;
0437       dma_buffer_phys_base = (i << 17) | 0x10000;
0438       break;
0439     }
0440 
0441   if (i >= 0x80)
0442     panic ("No suitable DMA buffer found");
0443 
0444   sb_read_raw ("/boot/welcome.raw");
0445 
0446   /* For now, just 4KB for buffer */
0447   dma_buffer_virt_base = (uint32) map_virtual_page (dma_buffer_phys_base | 3);
0448   if (sb_dsp_detect_base_address (&dsp_base_address) != SB_OK)
0449     goto fail;
0450   sb_dsp_detect_irq_number (dsp_base_address, &dsp_irq_number);
0451   sb_dsp_detect_dma (dsp_base_address,
0452                      &dsp_dma_channel_8, &dsp_dma_channel_16);
0453   sb_dsp_get_version (&dsp_version);
0454   sb_speaker_on ();
0455   sb_install_driver (11025, FALSE);
0456   com1_printf ("SB16 initialized.\n");
0457   return TRUE;
0458 
0459  fail:
0460   com1_printf ("SB16 not detected.\n");
0461   unmap_virtual_page ((void *)dma_buffer_virt_base);
0462   free_phys_frames (dma_buffer_phys_base, 0x10);
0463   return FALSE;
0464 }
0465 
0466 /* ************************************************** */
0467 
0468 #include "module/header.h"
0469 
0470 static const struct module_ops mod_ops = {
0471   .init = sb16_init
0472 };
0473 
0474 DEF_MODULE (sound___sb16, "Sound Blaster 16 driver", &mod_ops, {"vfs"});
0475 
0476 /*
0477  * Local Variables:
0478  * indent-tabs-mode: nil
0479  * mode: C
0480  * c-file-style: "gnu"
0481  * c-basic-offset: 2
0482  * End:
0483  */
0484 
0485 /* vi: set et sw=2 sts=2: */