📄 usbspeakerlib.c
字号:
USB_DESCR_INTERFACE) != NULL) rangeLen = cfgLen - tmpLen; else rangeLen = cfgLen; parseInterface (pSeqDev, pIfDescr, pCfgBfr, rangeLen); } } return OK; }/***************************************************************************** selectFormat - selects an audio format to match the caller's request** <pReqFmt> points to a USB_SPEAKER_AUDIO_FORMAT structure in which the* "formatTag" field has been initialized to the desired format as* USB_AUDIO_TYPEn_xxxx and "formatType" has been initialized with the * format type as USB_AUDIO_FORMAT_TYPEn. ** Depending on the specified format, the fields "channels", "subFrameSize", * "bitRes", "sampleFrequency", "maxBitRate", and "samplesPerFrame" may also * need to be specified to qualify the format. The other fields of the * structure *do not* need to be specified.** The field "sampleFrequency", which must be set for Type I & Type 3 formats* must be set with the desired playback frequency. The frequency is * expressed as samples per sec. For example, a sampleFrequency of 88,500 * is 88,500 samples per second per channel.** This function will attempt to find an audio format supported by the* speaker which is an exact match for the specified format. If found, that* format will be selected. If no exact match can be found, an error will be* returned.** RETURNS: OK if successful, else ERROR.*/LOCAL STATUS selectFormat ( pUSB_SPKR_SEQ_DEV pSeqDev, pUSB_SPKR_AUDIO_FORMAT pReqFmt ) { pUSB_SPKR_AUDIO_FORMAT pFmt; UINT16 i; /* Set the format type to "unspecified". */ pSeqDev->pSelFmt = NULL; pSeqDev->sampleFrequency = 0; pSeqDev->sampleSize = 0; /* Look for an exact match for the format */ for (i = 0; i < pSeqDev->fmtCount; i++) { pFmt = &pSeqDev->pFmt [i]; switch (pReqFmt->formatType) { case USB_AUDIO_FORMAT_TYPE1: case USB_AUDIO_FORMAT_TYPE3: if (pReqFmt->formatTag == pFmt->formatTag && pReqFmt->channels == pFmt->channels && pReqFmt->subFrameSize == pFmt->subFrameSize && pReqFmt->bitRes == pFmt->bitRes) { pSeqDev->pSelFmt = pFmt; pSeqDev->sampleFrequency = pReqFmt->sampleFrequency; pSeqDev->sampleSize = pFmt->channels * pFmt->subFrameSize; pSeqDev->bytesPerSec = pSeqDev->sampleFrequency * pSeqDev->sampleSize; return OK; } break; case USB_AUDIO_FORMAT_TYPE2: if (pReqFmt->formatTag == pFmt->formatTag && pReqFmt->maxBitRate == pFmt->maxBitRate && pReqFmt->samplesPerFrame == pFmt->samplesPerFrame) { pSeqDev->pSelFmt = pFmt; pSeqDev->sampleFrequency = pReqFmt->sampleFrequency; pSeqDev->sampleSize = 1; pSeqDev->bytesPerSec = pSeqDev->sampleFrequency * pSeqDev->sampleSize; return OK; } break; default: /* not supported */ return ERROR; } } /* No compatible format was found. */ return ERROR; }/***************************************************************************** parseAudioConfig - parse configuration descriptor for audio device** Examine the configuration descriptor and build a map of the device's* capabilities. To be used by this speaker class driver, the device must* have an "Output Terminal". If it does, then determine which audio* formats can be passed to the device.** RETURNS: OK if successful, else ERROR*/LOCAL STATUS parseAudioConfig ( pUSB_SPKR_SEQ_DEV pSeqDev, pUINT8 pCfgBfr, UINT16 cfgLen, pUSB_CONFIG_DESCR pCfgDescr ) { /* Find the Audio Control descriptor and build a map of the device */ if (buildAudioMap (pSeqDev, pCfgBfr, cfgLen) != OK) return ERROR; if (pSeqDev->outputTerminalId == 0 || pSeqDev->inputTerminalId == 0) return ERROR; /* The audio device has the necessary Output Terminal. Parse the * configuration descriptor and make a list of the supported audio * formats. */ if (buildFormatList (pSeqDev, pCfgBfr, cfgLen) != OK) return ERROR; if (pSeqDev->fmtCount == 0) return ERROR; return OK; }/***************************************************************************** configureSeqDev - configure USB speaker for operation** Examine the device's configuration & associated descriptors to determine* the audio device's capability. If the device is an audio device capable* of output (e.g., it includes an "Output Terminal") then build a structure* to describe the device and configure it.** RETURNS: OK if successful, else ERROR if failed to configure channel*/LOCAL STATUS configureSeqDev ( pUSB_SPKR_SEQ_DEV pSeqDev ) { pUSB_CONFIG_DESCR pCfgDescr; pUINT8 pCfgBfr; UINT16 cfgLen; STATUS retcode; /* Read the configuration descriptor so we can determine the device's * capabilities. */ if (usbConfigDescrGet (usbdHandle, pSeqDev->nodeId, pSeqDev->configuration, &cfgLen, &pCfgBfr) != OK) return ERROR; if ((pCfgDescr = usbDescrParse (pCfgBfr, cfgLen, USB_DESCR_CONFIGURATION)) == NULL) { retcode = ERROR; } else { /* Parse the configuration descriptor to see if the device has the * capabilities we need to use it as a speaker. */ retcode = parseAudioConfig (pSeqDev, pCfgBfr, cfgLen, pCfgDescr); } if (retcode == OK) { /* Select the configuration. */ if (usbdConfigurationSet (usbdHandle, pSeqDev->nodeId, pCfgDescr->configurationValue, pCfgDescr->maxPower * USB_POWER_MA_PER_UNIT) != OK) { retcode = ERROR; } } /* Release the buffer allocated by usbConfigDescrGet(). */ OSS_FREE (pCfgBfr); return retcode; }/***************************************************************************** destroyAudioStream - releases audio stream, bandwidth, resources** Releases the resources allocated for an audio stream as long as play* has completed. If <unconditional> is TRUE, then the stream is * destroyed whether or not play has completed. ** RETURNS: N/A*/LOCAL VOID destroyAudioStream ( pUSB_SPKR_SEQ_DEV pSeqDev, BOOL unconditional ) { /* If still playing, return. */ if (unconditional || pSeqDev->streamFault || (!pSeqDev->open && pSeqDev->audioBfrCount == 0)) { /* Destroy the isoch. pipe. */ if (pSeqDev->isochPipeHandle != NULL) { usbdPipeDestroy (usbdHandle, pSeqDev->isochPipeHandle); pSeqDev->isochPipeHandle = NULL; } /* Release buffer-not-full semaphore */ if (pSeqDev->bfrNotFullSem != NULL) { OSS_SEM_DESTROY (pSeqDev->bfrNotFullSem); pSeqDev->bfrNotFullSem = NULL; } /* Release the audio data buffer */ if (pSeqDev->pAudioBfr != NULL) { OSS_FREE (pSeqDev->pAudioBfr); pSeqDev->pAudioBfr = NULL; } } }/***************************************************************************** openAudioStream - Sets up speaker to begin receiving audio data** RETURNS: OK if successful, else EIO if error.*/LOCAL STATUS openAudioStream ( pUSB_SPKR_SEQ_DEV pSeqDev ) { pUSB_SPKR_AUDIO_FORMAT pFmt = pSeqDev->pSelFmt; UINT32 halfBfrLen; /* If the audio stream is already open or data is still in the bfr, * return an error. */ if (pSeqDev->open || pSeqDev->audioBfrCount > 0) return EIO; /* Return an error if no audio format specified. */ if (pFmt == NULL) return EIO; /* Select the audio interface/alt setting for the indicated format */ if (usbdInterfaceSet (usbdHandle, pSeqDev->nodeId, pFmt->interface, pFmt->altSetting) != OK) return EIO; /* Calculate the size of the data buffer for the audio stream. We * calculate the buffer size to ensure one second of buffering capability * and to ensure that each half of the buffer accomodates an exactly * multiple of the sample size. */ halfBfrLen = pSeqDev->bytesPerSec / 2; halfBfrLen = ((halfBfrLen + pSeqDev->sampleSize - 1) / pSeqDev->sampleSize) * pSeqDev->sampleSize; /* Allocate resources for the stream. */ pSeqDev->streamFault = FALSE; if ((pSeqDev->pAudioBfr = OSS_MALLOC (halfBfrLen * 2)) == NULL) return EIO; pSeqDev->audioBfrLen = halfBfrLen * 2; pSeqDev->audioBfrHalf = halfBfrLen; pSeqDev->audioBfrCount = 0; pSeqDev->audioBfrIn = 0; pSeqDev->audioBfrOut = 0; pSeqDev->audioBfrPending = 0; pSeqDev->audioBfrTotal = 0; pSeqDev->foregroundWaiting = FALSE; if (OSS_SEM_CREATE (1, 0, &pSeqDev->bfrNotFullSem) != OK) { destroyAudioStream (pSeqDev, TRUE); return EIO; } pSeqDev->nextStartFrame = 0; pSeqDev->blocksSent = 0; pSeqDev->nextIrp = 0; pSeqDev->nextDoneIrp = 0; pSeqDev->irpsInUse = 0; /* Create the isochronous transmit pipe for the audio data. */ if (usbdPipeCreate (usbdHandle, pSeqDev->nodeId, pFmt->endpoint, pSeqDev->configuration, pFmt->interface, USB_XFRTYPE_ISOCH, USB_DIR_OUT, pFmt->maxPacketSize, pSeqDev->bytesPerSec, 1, &pSeqDev->isochPipeHandle) != OK) { destroyAudioStream (pSeqDev, TRUE); return EIO; } pSeqDev->open = TRUE; return OK; }/***************************************************************************** closeAudioStream - Marks end of audio stream.** RETURNS: OK if successful, else EIO if error.*/LOCAL STATUS closeAudioStream ( pUSB_SPKR_SEQ_DEV pSeqDev ) { /* If we're not playing, return an error. */ if (!pSeqDev->open) return EIO; /* tear down the isoch pipe, etc., if the audio transmission is * complete...if still transmitting, the stream will be torn down * upon completion of the data transfer. */ pSeqDev->open = FALSE; destroyAudioStream (pSeqDev, FALSE); return OK; }/***************************************************************************** updateTransmitter - starts/restarts double-buffered I/O** NOTE: Caller must ensure speakerMutex is taken before calling this* routine.** RETURNS: OK if successful, else ERROR.*/LOCAL STATUS updateTransmitter ( pUSB_SPKR_SEQ_DEV pSeqDev ) { pUSB_IRP pIrp; UINT32 currentFrame; UINT32 extLen; UINT32 frameCount; STATUS status = OK; /* If an IRP is available and there is data available but not yet * being transmitted, then send it. */ while (!pSeqDev->streamFault && pSeqDev->irpsInUse < IRP_COUNT && ((pSeqDev->audioBfrCount - pSeqDev->audioBfrPending >= pSeqDev->audioBfrHalf) || !pSeqDev->open)) { /* If this is the first block of data, initialize the starting * frame number. */ if (pSeqDev->blocksSent == 0) { if (usbdCurrentFrameGet (usbdHandle, pSeqDev->nodeId, ¤tFrame, &pSeqDev->frameWindow) != OK) currentFrame = 0; pSeqDev->nextStartFrame = (currentFrame + FRAME_SKIP) % pSeqDev->frameWindow; } /* Use the next IRP to transmit a block of data. Each IRP always * transfers half of the data buffer or the entire block of * remaining data if the stream has been closed. */ extLen = pSeqDev->audioBfrCount - pSeqDev->audioBfrPending; extLen = min (extLen, pSeqDev->audioBfrHalf); if (extLen < pSeqDev->audioBfrHalf && pSeqDev->open) break; frameCount = ((extLen * MSEC_PER_SEC) + pSeqDev->bytesPerSec - 1) / pSeqDev->bytesPerSec; if (frameCount == 0) break; /* Initialize the IRP. */ pIrp = &pSeqDev->irp [pSeqDev->nextIrp]; pSeqDev->irpsInUse++; if (++pSeqDev->nextIrp == IRP_COUNT) pSeqDev->nextIrp = 0; memset (pIrp, 0, sizeof (*pIrp)); pIrp->userPtr = pSeqDev; pIrp->irpLen = sizeof (*pIrp); pIrp->userCallback = usbSpkrIrpCallback; pIrp->timeout = USB_TIMEOUT_NONE; pIrp->startFrame = pSeqDev->nextStartFrame; pIrp->dataBlockSize = pSeqDev->sampleSize; pIrp->transferLen = extLen; pIrp->bfrCount = 1; pIrp->bfrList [0].pid = USB_PID_OUT; pIrp->bfrList [0].pBfr = &pSeqDev->pAudioBfr [pSeqDev->audioBfrOut]; pIrp->bfrList [0].bfrLen = extLen; pSeqDev->audioBfrPending += extLen; if ((pSeqDev->audioBfrOut += extLen) == pSeqDev->audioBfrLen) pSeqDev->audioBfrOut = 0; pSeqDev->nextStartFrame = (pSeqDev->nextStartFrame + frameCount) % pSeqDev->frameWindow; pSeqDev->blocksSent++; /* Send the data */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -