home *** CD-ROM | disk | FTP | other *** search
/ Openstep 4.2 (Developer) / Openstep Developer 4.2.iso / NextDeveloper / Examples / DriverKit / SoundBlaster8 / SoundBlaster8_reloc.tproj / SoundBlaster8.m < prev    next >
Encoding:
Text File  |  1996-03-28  |  15.2 KB  |  636 lines

  1. /*
  2.  * Copyright (c) 1994-1996 NeXT Software, Inc.  All rights reserved. 
  3.  *
  4.  * HISTORY
  5.  * 4-Mar-94    Rakesh Dubey at NeXT
  6.  *      Created. 
  7.  */
  8.  
  9. #import "SoundBlaster8.h"
  10. #import "SoundBlaster8Registers.h"
  11.  
  12. #import <driverkit/generalFuncs.h>
  13.  
  14. static const char codecDeviceName[] = "SoundBlaster8";
  15. static const char codecDeviceKind[] = "Audio";
  16.  
  17. static  sbCardParameters_t sbCardType;         // hardware type
  18. static  BOOL lowSpeedDMA;             // different programming
  19.  
  20. /*
  21.  * Include inline functions. 
  22.  */
  23. #import "SoundBlaster8Inline.h"
  24.  
  25. @implementation SoundBlaster8
  26.  
  27. /*
  28.  * Probe and initialize new instance 
  29.  */
  30. + (BOOL) probe:deviceDescription
  31. {
  32.     SoundBlaster8       *dev;
  33.     IORange             *portRangeList;
  34.     int                 numPortRanges;
  35.     unsigned int        baseAddress;
  36.  
  37. #ifdef DEBUG
  38.     int                 i;
  39. #endif DEBUG
  40.  
  41.     dev = [self alloc];
  42.     if (dev == nil)
  43.         return NO;
  44.  
  45.     portRangeList = [deviceDescription portRangeList];
  46.     numPortRanges = [deviceDescription numPortRanges];
  47.     
  48.     if (numPortRanges < 1)
  49.         return NO;
  50.  
  51. #ifdef DEBUG
  52.     for (i=0; i < numPortRanges; i++) {
  53.         IOLog("SoundBlaster8: port %x %d\n",
  54.                 portRangeList[i].start, portRangeList[i].size);
  55.     }
  56. #endif DEBUG
  57.  
  58.     baseAddress = portRangeList[0].start;
  59. #ifdef DEBUG
  60.     IOLog("SoundBlaster8: Base address = 0x%x.\n", baseAddress);
  61. #endif DEBUG
  62.  
  63.  
  64.     /*
  65.      * Check base address to verify if this is a legal address.
  66.      */
  67.  
  68.     if ((baseAddress == SB_BASE_ADDRESS_1) ||
  69.         (baseAddress == SB_BASE_ADDRESS_2) ||
  70.         (baseAddress == SB_BASE_ADDRESS_3) ||
  71.         (baseAddress == SB_BASE_ADDRESS_4) ||
  72.         (baseAddress == SB_BASE_ADDRESS_5) ||
  73.         (baseAddress == SB_BASE_ADDRESS_6))     {
  74.         sbBaseRegisterAddress = baseAddress;
  75.     } else {
  76.         IOLog("SoundBlaster8: Invalid port address 0x%0x.\n", baseAddress);
  77.         [dev free];
  78.         return NO;
  79.     }
  80.  
  81.     /*
  82.      * Now assign all SB DSP and Mixer registers their addresses.
  83.      */
  84.     assignDSPRegAddresses();
  85.     assignMixerRegAddresses();
  86.     
  87.     return [dev initFromDeviceDescription:deviceDescription] != nil;
  88. }
  89.  
  90.  
  91. - (BOOL)reset
  92. {
  93.     unsigned int channel        = [[self deviceDescription] channel];
  94.     unsigned int interrupt      = [[self deviceDescription] interrupt];
  95.  
  96.     IOReturn ioReturn;
  97.  
  98.     [self setName:codecDeviceName];
  99.     [self setDeviceKind:codecDeviceKind];
  100.  
  101.     /*
  102.      * Are user selections valid?
  103.      */
  104.     if (checkSelectedDMAAndIRQ(channel, interrupt) == NO) {
  105.     return NO;
  106.     }
  107.     
  108.     /*
  109.      * Now that all hardware parameters have been assigned and/or verified
  110.      * initialize the hardware.
  111.      */
  112.     [self initializeHardware];
  113.  
  114.     /*
  115.      * This driver is only for 8-bit Sound Blaster cards. If this is not one
  116.      * of these systems we quit since the test is fully reliable.
  117.      */
  118.     
  119.     switch (sbCardType.version) {
  120.       case SB_CLASSIC:
  121.         sbCardType.name = "Classic";
  122.         break;
  123.       case SB_20:
  124.         sbCardType.name = "2.0";
  125.         break;
  126.       case SB_PRO:
  127.         sbCardType.name = "Pro";
  128.         break;
  129.       case SB_16:
  130.         sbCardType.name = "Pro";
  131.         sbCardType.version = SB_PRO;    /* SB16 will emulate it */
  132.         break;
  133.       default:  {
  134.         IOLog("SoundBlaster8: Hardware not detected at port 0x%0x.\n",
  135.         sbBaseRegisterAddress);
  136.         return NO;
  137.       }
  138.     }
  139.     
  140.     IOLog("SoundBlaster8: Sound Blaster %s (ver %d.%d) at port 0x%0x.\n", 
  141.          sbCardType.name, 
  142.          sbCardType.majorVersion, sbCardType.minorVersion, 
  143.          sbBaseRegisterAddress);
  144.     
  145.     /*
  146.      * Initialize DMA controller.
  147.      */
  148.      
  149.     [self disableChannel: 0];
  150.  
  151.     /*
  152.      * This call is only applicable in EISA systems. All dma channels
  153.      * that are available to this driver in ISA machines are 8-bit. So we do
  154.      * this setup only for EISA machines. 
  155.      */
  156.     if ([self isEISAPresent]) {
  157.         ioReturn = [self setDMATransferWidth:IO_8Bit forChannel:0];
  158.         if (ioReturn != IO_R_SUCCESS) {
  159.             IOLog("SoundBlaster8: could not set transfer width to 8 bits, error %d.\n", ioReturn);
  160.             return NO;
  161.         }
  162.     }
  163.     
  164.     ioReturn = [self setTransferMode: IO_Single forChannel: 0];
  165.     if (ioReturn != IO_R_SUCCESS)  {
  166.         IOLog("%s: dma transfer mode error %d\n", [self name], ioReturn);
  167.         return NO;
  168.     }
  169.  
  170.     /*
  171.      * We will program the DMA controller in auto-init mode but the card is
  172.      * in single cycle mode. So at every interrupt we only need to reprogram
  173.      * the card.
  174.      */
  175.     ioReturn = [self setAutoinitialize: YES forChannel: 0];
  176.     if (ioReturn != IO_R_SUCCESS) {
  177.         IOLog("%s: dma auto initialize error %d", [self name], ioReturn);
  178.         return NO;
  179.     }
  180.     
  181.     return YES;
  182. }
  183.  
  184.  
  185. - (void) initializeHardware
  186. {
  187.     resetHardware();
  188. }
  189.  
  190. /*
  191.  * Converts gain (0 - 32768) into hardware supported gain (0 - 7). If the
  192.  * input source is line (not supported now), simply double the gain. 
  193.  */
  194.  
  195. - (void)updateInputGainLeft
  196. {
  197.     unsigned int gain = [self inputGainLeft];
  198.     unsigned int left  = 0;
  199.     
  200.     if (gain)
  201.         left = ((gain * MAX_INPUT_GAIN_MICROPHONE)/32768);
  202.     else
  203.         left = gain;    // minimum input gain = 0
  204.         
  205.     setInputGain(LEFT_CHANNEL, left);
  206. #ifdef DEBUG
  207.     IOLog("%s: updateInputGainLeft %d based on gain %d\n", [self name],left, gain);
  208. #endif DEBUG
  209. }
  210.  
  211. /*
  212.  * Converts gain (0 - 32768) into hardware supported gain (0 - 7)
  213.  */
  214.  
  215. - (void)updateInputGainRight
  216. {
  217.     unsigned int gain = [self inputGainRight];
  218.     unsigned int right = 0;
  219.     
  220.     if (gain)
  221.         right = ((gain * MAX_INPUT_GAIN_MICROPHONE)/32768);
  222.     else
  223.         right = gain;   // minimum input gain = 0
  224.         
  225.     setInputGain(RIGHT_CHANNEL, right);
  226. #ifdef DEBUG
  227.     IOLog("%s: updateInputGainRight %d based on gain %d\n", [self name], right, gain);
  228. #endif DEBUG
  229. }
  230.  
  231. - (void)updateOutputMute
  232. {
  233.     enableAudioOutput(! [self isOutputMuted]);
  234. }
  235.  
  236. /*
  237.  * (0) - (-84) needs to be converted to hardware supported (0) - (15)
  238.  */
  239. - (void) updateOutputAttenuationLeft
  240. {
  241.     unsigned int attenuation = [self outputAttenuationLeft] + 84;
  242.     unsigned int left = 0;
  243.     
  244.     left = ((attenuation * MAX_MASTER_OUTPUT_VOLUME)/84);
  245.    
  246.     setOutputAttenuation(LEFT_CHANNEL, left);
  247.     
  248. #ifdef DEBUG
  249.     IOLog("%s: converted la: %d into %d\n", [self name], attenuation, left);
  250. #endif DEBUG
  251. }
  252.  
  253. /*
  254.  * (0) - (-84) needs to be converted to hardware supported (0) - (15)
  255.  */
  256. - (void) updateOutputAttenuationRight
  257. {
  258.     unsigned int attenuation = [self outputAttenuationRight] + 84;
  259.     unsigned int right = 0;
  260.     
  261.     right = ((attenuation * MAX_MASTER_OUTPUT_VOLUME)/84);
  262.     
  263.     setOutputAttenuation(RIGHT_CHANNEL, right);
  264.    
  265. #ifdef DEBUG
  266.     IOLog("SoundBlaster8: converted ra: %d into %d\n", attenuation, right);
  267. #endif DEBUG
  268. }
  269.  
  270. /*
  271.  * Program DSP.
  272.  */
  273. - (void)updateSampleRate
  274. {
  275.     unsigned int rate;
  276.     unsigned int mode;
  277.     
  278.     rate = [self sampleRate];
  279.     mode = ([self channelCount] == 2) ? DSP_STEREO_MODE : DSP_MONO_MODE;
  280.  
  281.     /*
  282.      * Programming sequence depends upon whether we are doing a low speed or
  283.      * high speed transfer. Rather messy, see SB SDK page 12-5.
  284.      */
  285.     if (sbCardType.version == SB_CLASSIC) {
  286.     lowSpeedDMA = YES;
  287.     } else if (sbCardType.version == SB_20) {
  288.     if (currentDMADirection == DMA_DIRECTION_IN)
  289.         lowSpeedDMA = (rate < SB_20_LOW_SPEED_RECORD) ? YES : NO;
  290.     else
  291.         lowSpeedDMA = (rate < SB_20_LOW_SPEED_PLAYBACK) ? YES : NO;
  292.     } else if (sbCardType.version == SB_PRO) {
  293.         if (mode == DSP_STEREO_MODE)
  294.         rate *= 2;
  295.     lowSpeedDMA = (rate < SB_PRO_LOW_SPEED) ? YES : NO;
  296.     }
  297.     
  298.     setCodecDataMode(mode, currentDMADirection);
  299.     setCodecSamplingRate(rate);
  300. }
  301.  
  302.  
  303. /*
  304.  * Sets the DMA Counter Load register which decides when the next interrupt
  305.  * will arrive.
  306.  */
  307. - (void) setBufferCount:(int)count
  308. {
  309.     setSampleBufferCounter(count);      
  310. }
  311.  
  312.  
  313. - (IOReturn) enableAllInterrupts
  314. {
  315.     enableCodecInterrupts();
  316.     return [super enableAllInterrupts];
  317. }
  318.  
  319. - (void) disableAllInterrupts
  320. {
  321.    disableCodecInterrupts();
  322.    [super disableAllInterrupts];
  323. }
  324.  
  325. /*
  326.  * Return NO if the hardware does not support this particular playback/record
  327.  * request. The parameter scheme in soundkit does not fit the anarchy
  328.  * of PC world very well.
  329.  */
  330. - (BOOL)isValidRequest: (BOOL)isRead
  331. {
  332.     unsigned int rate;
  333.     unsigned int mode;
  334.     unsigned int encoding;
  335.     
  336.     rate = [self sampleRate];
  337.     encoding = [self dataEncoding];
  338.     mode = ([self channelCount] == 2) ? DSP_STEREO_MODE : DSP_MONO_MODE;
  339.  
  340. #ifdef DEBUG
  341.     IOLog("SoundBlaster8: rate: %d ", rate);
  342.     
  343.     if (mode == DSP_MONO_MODE)
  344.         IOLog("dataMode: mono ");
  345.     else if (mode == DSP_STEREO_MODE)
  346.         IOLog("dataMode: stereo ");
  347.     else
  348.         IOLog("dataMode: unknown ");
  349.         
  350.     if (encoding == NX_SoundStreamDataEncoding_Linear16)
  351.         IOLog("dataEncoding: linear 16\n");
  352.     else if (encoding == NX_SoundStreamDataEncoding_Linear8)
  353.         IOLog("dataEncoding: linear 8\n");
  354.     else if (encoding == NX_SoundStreamDataEncoding_Mulaw8)
  355.         IOLog("dataEncoding: mulaw 8\n");
  356.     else if (encoding == NX_SoundStreamDataEncoding_Alaw8)
  357.         IOLog("dataEncoding: Alaw 8\n");
  358.     else
  359.         IOLog("dataEncoding: unknown\n");
  360. #endif DEBUG
  361.     
  362.     if (sbCardType.version == SB_PRO) {
  363.         if ((mode == DSP_STEREO_MODE) &&
  364.             (rate > SB_PRO_LOW_SPEED))
  365.             return NO;          
  366.     }
  367.     
  368.     if (sbCardType.version == SB_20) {
  369.     if (isRead && rate > SB_20_LOW_SPEED_RECORD)
  370.         return NO;
  371.     }
  372.                 
  373.     if (sbCardType.version == SB_CLASSIC) {
  374.     if (isRead && rate > SB_CLASSIC_MAX_SPEED_RECORD)
  375.         return NO;
  376.     if (!isRead && rate > SB_CLASSIC_MAX_SPEED_PLAYBACK)
  377.         return NO;
  378.     }
  379.     
  380.     return YES;
  381. }
  382.  
  383.  
  384. - (BOOL) startDMAForChannel: (unsigned int) localChannel
  385.         read: (BOOL) isRead
  386.         buffer: (IOEISADMABuffer) buffer
  387.         bufferSizeForInterrupts: (unsigned int) bufferSize
  388. {
  389.     IOReturn ioReturn;
  390.     
  391. #ifdef DEBUG
  392.     IOLog("SoundBlaster8: startDMAForChannel\n");
  393. #endif DEBUG
  394.  
  395.     isValidRequest = [self isValidRequest:isRead];
  396.     
  397.     interruptTimedOut = NO;
  398.     
  399.     if (isValidRequest == NO)   {
  400.         IOLog("%s: unsupported %s mode.\n", [self name],
  401.                 isRead ? "recording" : "playback");
  402.     
  403.     if (isRead)
  404.         return YES;
  405.     else
  406.         return NO;
  407.     }
  408.         
  409.     if (isRead)
  410.         currentDMADirection = DMA_DIRECTION_IN;
  411.     else
  412.         currentDMADirection = DMA_DIRECTION_OUT;
  413.  
  414.     /*
  415.      * Output must be off while recording. 
  416.      */
  417.     if (![self isOutputMuted])
  418.     enableAudioOutput(isRead ? NO : YES);
  419.  
  420.     [self updateSampleRate];
  421.     
  422.     dmaDescriptorSize = bufferSize;    // used by interrupt handler
  423.             
  424. #ifdef DEBUG
  425.     if (lowSpeedDMA)
  426.         IOLog("SoundBlaster8: starting low speed DMA ");
  427.     else
  428.         IOLog("SoundBlaster8: starting high speed DMA ");
  429.         
  430.     if (isRead)
  431.         IOLog("input.\n");
  432.     else
  433.         IOLog("output.\n");
  434. #endif DEBUG
  435.  
  436.     ioReturn = [self startDMAForBuffer: buffer channel: localChannel];
  437.  
  438.     if (ioReturn != IO_R_SUCCESS) {
  439.         IOLog("%s: could not start DMA channel error %d\n",
  440.                 [self name], ioReturn);
  441.         return NO;
  442.     }
  443.         
  444.     ioReturn = [self enableChannel: localChannel];
  445.     
  446.     if (ioReturn != IO_R_SUCCESS) {
  447.         IOLog("%s: could not enable DMA channel error %d\n",
  448.                 [self name], ioReturn);
  449.         return NO;
  450.     }
  451.  
  452.     (void) [self enableAllInterrupts];
  453.  
  454.     /*
  455.      * The order is important here. See SB SDK page 12-8 and 12-13. 
  456.      */
  457.     if (lowSpeedDMA)    {
  458.         if (isRead) {
  459.             startDMA(DMA_DIRECTION_IN);
  460.         } else {
  461.             startDMA(DMA_DIRECTION_OUT);
  462.         }
  463.         [self setBufferCount: dmaDescriptorSize];
  464.     } else {
  465.         [self setBufferCount: dmaDescriptorSize];
  466.         if (isRead) {
  467.             startDMA(DMA_DIRECTION_IN);
  468.         } else {
  469.             startDMA(DMA_DIRECTION_OUT);
  470.         }
  471.     }
  472.     
  473.     return YES;
  474. }
  475.  
  476. - (void) stopDMAForChannel: (unsigned int) localChannel read: (BOOL) isRead
  477. {
  478. #ifdef DEBUG
  479.     IOLog("SoundBlaster8: stopDMAForChannel\n");
  480. #endif DEBUG
  481.  
  482.     /*
  483.      * DMA request was denied bacause of lack of hardware support. 
  484.      */
  485.     if (isValidRequest == NO)
  486.         return;
  487.     
  488.     if (isRead) {
  489.         stopDMAInput();
  490.     } else {
  491.         stopDMAOutput();
  492.     }
  493.     
  494.     (void)[self disableAllInterrupts];
  495.     
  496.     /*
  497.      * Disable channel only after disabling capture and playback. 
  498.      */
  499.     [self disableChannel: localChannel];
  500.     
  501.     /*
  502.      * Reset DSP to stop high speed DMA transfer. This is necessary since the
  503.      * current "DMA block" might be continuing in case the transfer was
  504.      * interrupted. 
  505.      */
  506.     if (lowSpeedDMA == NO) {
  507.     resetDSPQuick();
  508.     }
  509. }
  510.  
  511. static void clearInterrupts(void)
  512. {
  513.     /*
  514.      * Acknowledge and clear the interrupt.
  515.      */
  516.  
  517.     inb(sbDataAvailableStatusReg);
  518. }
  519.  
  520. - (IOAudioInterruptClearFunc) interruptClearFunc
  521. {
  522.     return clearInterrupts;
  523. }
  524.  
  525. - (void) interruptOccurredForInput: (BOOL *) serviceInput  
  526.                          forOutput: (BOOL *) serviceOutput
  527. {
  528. #ifdef DEBUG
  529.     IOLog("SoundBlaster8: handleHardwareInterrupt\n");
  530. #endif DEBUG
  531.     
  532.     /*
  533.      * Acknowledge and clear the interrupt.
  534.      */
  535.  
  536.     inb(sbDataAvailableStatusReg);
  537.     
  538.     /*
  539.      * We do not have simultaneous playback and record in SB.
  540.      */
  541.     if (currentDMADirection == DMA_DIRECTION_OUT)
  542.         *serviceOutput = YES;
  543.     else
  544.         *serviceInput = YES;
  545.         
  546.     if (lowSpeedDMA)    {
  547.         if (currentDMADirection == DMA_DIRECTION_IN) {
  548.             startDMA(DMA_DIRECTION_IN);
  549.         } else {
  550.             startDMA(DMA_DIRECTION_OUT);
  551.         }
  552.         [self setBufferCount: dmaDescriptorSize];    /* needed here */
  553.     } else {
  554.         //[self setBufferCount: dmaDescriptorSize];     /* but not here */
  555.         if (currentDMADirection == DMA_DIRECTION_IN) {
  556.             startDMA(DMA_DIRECTION_IN);
  557.         } else {
  558.             startDMA(DMA_DIRECTION_OUT);
  559.         }
  560.     }
  561. }
  562.  
  563. /*
  564.  * This routine will be called if interrupts are not being received. Some
  565.  * cards seem to lock up once in a while. 
  566.  */
  567. - (void) timeoutOccurred
  568. {
  569. #ifdef DEBUG
  570.     IOLog("%s: timeout occurred.\n", [self name]);
  571. #endif DEBUG
  572.  
  573.     if (interruptTimedOut == NO) {
  574.     resetDSPQuick();
  575.     interruptTimedOut = YES;        // reset only once
  576.     }
  577. }
  578.  
  579. /*
  580.  * Choose between different input sources.
  581.  */
  582.  
  583. - (void)setAnalogInputSource:(NXSoundParameterTag) val
  584. {
  585.     if (val == NX_SoundDeviceAnalogInputSource_Microphone) {
  586.         setInputLevel(MICROPHONE_LEVEL_INPUT);
  587.     } else if (val == NX_SoundDeviceAnalogInputSource_LineIn) {
  588.         setInputLevel(LINE_LEVEL_INPUT);
  589.     } else {
  590.         setInputLevel(MICROPHONE_LEVEL_INPUT);  // default
  591.     }
  592. }
  593.  
  594. /*
  595.  * Parameter access.
  596.  */
  597.  
  598. - (BOOL)acceptsContinuousSamplingRates
  599. {
  600.     return YES;
  601. }
  602.  
  603. - (void)getSamplingRatesLow:(int *)lowRate
  604.                                          high:(int *)highRate
  605. {
  606.     *lowRate = 4000;
  607.     *highRate = 44100;
  608. }
  609.  
  610. - (void)getSamplingRates:(int *)rates
  611.                                 count:(unsigned int *)numRates
  612. {
  613.     /* Return some supported rates */
  614.     rates[0] = 4000;
  615.     rates[1] = 8000;
  616.     rates[2] = 11025;
  617.     rates[3] = 22050;
  618.     rates[4] = 44100;
  619.     *numRates = 5;
  620. }
  621.  
  622. - (void)getDataEncodings: (NXSoundParameterTag *)encodings
  623.                                 count:(unsigned int *)numEncodings
  624. {
  625.     encodings[0] = NX_SoundStreamDataEncoding_Linear8;
  626.     *numEncodings = 1;
  627. }
  628.  
  629. - (unsigned int)channelCountLimit
  630. {
  631.     return (sbCardType.version == SB_PRO) ? 2 : 1;      /* stereo and mono */
  632. }
  633.  
  634. @end
  635.  
  636.