Nut/OS  4.10.3
API Reference
spi_vscodec.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2008-2009 by egnite GmbH
00003  * Copyright (C) 2001-2007 by egnite Software GmbH
00004  *
00005  * All rights reserved.
00006  *
00007  * Redistribution and use in source and binary forms, with or without
00008  * modification, are permitted provided that the following conditions
00009  * are met:
00010  *
00011  * 1. Redistributions of source code must retain the above copyright
00012  *    notice, this list of conditions and the following disclaimer.
00013  * 2. Redistributions in binary form must reproduce the above copyright
00014  *    notice, this list of conditions and the following disclaimer in the
00015  *    documentation and/or other materials provided with the distribution.
00016  * 3. Neither the name of the copyright holders nor the names of
00017  *    contributors may be used to endorse or promote products derived
00018  *    from this software without specific prior written permission.
00019  *
00020  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00021  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00022  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00023  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00024  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00025  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00026  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
00027  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
00028  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
00029  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
00030  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00031  * SUCH DAMAGE.
00032  *
00033  * For additional information see http://www.ethernut.de/
00034  */
00035 
00036 /*
00037  * $Id: spi_vscodec.c 2702 2009-09-17 11:01:21Z haraldkipp $
00038  */
00039 
00040 #include <dev/vscodec.h>
00041 
00042 #include <sys/event.h>
00043 #include <sys/timer.h>
00044 #include <sys/nutdebug.h>
00045 #include <sys/bankmem.h>
00046 
00047 #include <stdlib.h>
00048 #include <string.h>
00049 #include <memdebug.h>
00050 #include <fcntl.h>
00051 
00056 
00060 #define VSREQ_PLAY      0x00000001
00061 
00062 #define VSREQ_CANCEL    0x00000002
00063 
00064 #define VSREQ_VOLUPD    0x00000004
00065 
00066 #define VSREQ_AUDIOE    0x00000008
00067 
00068 #define VSREQ_BEEP      0x00000010
00069 
00070 #define VSREQ_RECORD    0x00000020
00071 
00074 /* Used to send zeros to the decoder. */
00075 uint8_t zero_chunk[VSCODEC_DATA_CHUNK_SIZE];
00076 
00085 int VsCodecWaitReady(NUTDEVICE *dev, uint32_t tmo)
00086 {
00087     VSDCB *dcb = (VSDCB *) dev->dev_dcb;
00088 
00089     while (!(* dcb->dcb_isready)()) {
00090         if (NutEventWait(&dcb->dcb_feedme, tmo)) {
00091             return -1;
00092         }
00093     }
00094     return 0;
00095 }
00096 
00097 /*
00098  * \brief Read from or write to a VLSI audio codec register.
00099  *
00100  * \param dev  Specifies the codec device.
00101  * \param op   Operation, either \ref VS_OPCODE_READ or
00102  *             \ref VS_OPCODE_WRITE.
00103  * \param reg  The register index.
00104  * \param val  This value will be stored in the register on a write
00105  *             operation. It is ignored on read operations.
00106  *
00107  * \return Register contents. On write operations, the contents before
00108  *         the modification is returned.
00109  */
00110 uint16_t VsCodecReg(NUTDEVICE *dev, uint_fast8_t op, uint_fast8_t reg, uint_fast16_t val)
00111 {
00112     uint8_t cmd[4];
00113     VSDCB *dcb = (VSDCB *) dev->dev_dcb;
00114 
00115     /* Assemble command buffer. */
00116     cmd[0] = (uint8_t) op;
00117     cmd[1] = (uint8_t) reg;
00118     cmd[2] = (uint8_t) (val >> 8);
00119     cmd[3] = (uint8_t) val;
00120 
00121     VsCodecWaitReady(dev, VSCODEC_CMD_TIMEOUT);
00122     (*dcb->dcb_sendcmd)(cmd, 4);
00123     val = cmd[2];
00124     val <<= 8;
00125     val |= cmd[3];
00126     return (uint16_t) val;
00127 }
00128 
00141 uint16_t VsCodecMode(NUTDEVICE *dev, uint_fast16_t flags, uint_fast16_t mask)
00142 {
00143     uint16_t rc;
00144 
00145     /* Read the mode register. */
00146     rc = VsCodecReg(dev, VS_OPCODE_READ, VS_MODE_REG, 0);
00147     if (mask | flags) {
00148         VsCodecReg(dev, VS_OPCODE_WRITE, VS_MODE_REG, (rc & ~mask) | flags);
00149         /* Add delay after software reset, if configured. */
00150 #if defined(VSCODEC_SWRST_RECOVER)
00151         if (flags & VS_SM_RESET) {
00152             NutSleep(VSCODEC_SWRST_RECOVER);
00153         }
00154 #endif
00155     }
00156     return rc;
00157 }
00158 
00168 int VsDecoderSetVolume(NUTDEVICE *dev, int left, int right)
00169 {
00170     VSDCB *dcb = (VSDCB *)dev->dev_dcb;
00171     uint16_t l;
00172     uint16_t r;
00173 
00174     /* Honor limits. */
00175     left = left > AUDIO_DAC_MAX_GAIN ? AUDIO_DAC_MAX_GAIN : left;
00176     left = left < AUDIO_DAC_MIN_GAIN ? AUDIO_DAC_MIN_GAIN : left;
00177     right = right > AUDIO_DAC_MAX_GAIN ? AUDIO_DAC_MAX_GAIN : right;
00178     right = right < AUDIO_DAC_MIN_GAIN ? AUDIO_DAC_MIN_GAIN : right;
00179 
00180     /* Convert to register values. */
00181     l = (uint16_t)(-2 * left);
00182     r = (uint16_t)(-2 * right);
00183 
00184     VsCodecReg(dev, VS_OPCODE_WRITE, VS_VOL_REG, (l << VS_VOL_LEFT_LSB) | (r << VS_VOL_RIGHT_LSB));
00185     dcb->dcb_lvol = left;
00186     dcb->dcb_rvol = right;
00187 
00188     return 0;
00189 }
00190 
00202 int VsDecoderSetBass(NUTDEVICE *dev, int treb, int tfin, int bass, int bfin)
00203 {
00204 #ifdef VS_HAS_BASS_REG
00205     VSDCB *dcb = (VSDCB *)dev->dev_dcb;
00206 //    uint16_t t;
00207 //    uint16_t b;
00208 
00209     //printf("bass: t:%ddB tf:%dHz b:%ddB bf:%dHz\n", treb, tfin*1000, bass, bfin);
00210     /* Honor limits. */
00211     treb = treb > AUDIO_DAC_MAX_TREB ? AUDIO_DAC_MAX_TREB : treb;
00212     treb = treb < 0 ? 0 : treb;
00213     tfin = tfin > AUDIO_DAC_MAX_TFIN ? AUDIO_DAC_MAX_TFIN : tfin;
00214     tfin = tfin < 0 ? 0 : tfin;
00215     bass = bass > AUDIO_DAC_MAX_BASS ? AUDIO_DAC_MAX_BASS : bass;
00216     bass = bass < 0 ? 0 : bass;
00217     bfin = bfin > AUDIO_DAC_MAX_BFIN ? AUDIO_DAC_MAX_BFIN : bfin;
00218     bfin = bfin < 0 ? 0 : bfin;
00219 
00220     /* Convert to register values. */
00221     /*
00222     l = (uint16_t)(-2 * left);
00223     r = (uint16_t)(-2 * right);
00224     */
00225 
00226     VsCodecReg(dev, VS_OPCODE_WRITE, VS_BASS_REG,
00227         (treb << VS_ST_AMPLITUDE_LSB) | (tfin << VS_ST_FREQLIMIT_LSB) |
00228         (treb << VS_SB_AMPLITUDE_LSB) | (tfin << VS_SB_FREQLIMIT_LSB)
00229     );
00230 
00231     dcb->dcb_treb = treb;
00232     dcb->dcb_tfin = tfin;
00233     dcb->dcb_bass = bass;
00234     dcb->dcb_bfin = bfin;
00235 #endif
00236 
00237     return 0;
00238 }
00239 
00251 uint16_t VsCodecBeep(NUTDEVICE *dev, uint16_t fsin)
00252 {
00253     uint16_t rc = 0;
00254     static uint8_t on[] = { 0x53, 0xEF, 0x6E, 0x3F, 0x00, 0x00, 0x00, 0x00 };
00255     static CONST uint8_t off[] = { 0x45, 0x78, 0x69, 0x74, 0x00, 0x00, 0x00, 0x00 };
00256     static CONST uint16_t ftab[] = { 44100, 48000, 32000, 22050, 24000, 16000, 11025, 12000 };
00257     static uint16_t mode;
00258     VSDCB *dcb = (VSDCB *)dev->dev_dcb;
00259 
00260     if (fsin) {
00261         uint_fast16_t s;
00262         uint32_t f;
00263         uint_fast16_t d;
00264         int_fast8_t i = sizeof(ftab) / sizeof(ftab[0]);
00265         int_fast8_t ie = 0;
00266         int_fast16_t dmin = fsin;
00267         uint32_t fs = (long)fsin * 128;
00268 
00269         while (--i >= ie) {
00270             /* Calculate skip speed. */
00271             f = ftab[i];
00272             s = (fs + f / 2) / f;
00273             /* Check that skip is within 5-bit range. */
00274             if (s && s < 32) {
00275                 /* Calculate the absolute deviation. */
00276                 f *= s;
00277                 f += 64;
00278                 f >>= 7;
00279                 d = (uint_fast16_t)(fsin > f ? fsin - f : f - fsin);
00280                 /* Check if this is a new minimum. */
00281                 if (d < dmin) {
00282                     dmin = d;
00283                     rc = (uint16_t) f;
00284                     /* Set new skip speed and frequency index. */
00285                     on[3] = (i << 5) | s;
00286                     /* We can stop earlier with this fit. */
00287                     if (i > 1) {
00288                         ie = i - 2;
00289                     }
00290                 }
00291             }
00292         }
00293         mode = VsCodecMode(dev, VS_SM_RESET, VS_SM_RESET);
00294 #ifdef VS_SM_TESTS
00295         VsCodecMode(dev, mode | VS_SM_TESTS, 0xffff);
00296 #endif
00297         (*dcb->dcb_senddata)(on, sizeof(on));
00298     } else {
00299         (*dcb->dcb_senddata)(off, sizeof(off));
00300 #ifdef VS_SM_TESTS
00301         VsCodecMode(dev, VS_SM_RESET, VS_SM_RESET | VS_SM_TESTS);
00302 #else
00303         VsCodecMode(dev, VS_SM_RESET, VS_SM_RESET);
00304 #endif
00305         VsCodecMode(dev, mode, 0xffff);
00306     }
00307     return rc;
00308 }
00309 
00310 /*
00311  * Initialize the stream output buffer with a given size.
00312  *
00313  * \param dev Specifies the audio codec device.
00314  */
00315 int VsDecoderBufferInit(NUTDEVICE *dev, uint32_t size)
00316 {
00317     VSDCB *dcb = (VSDCB *)dev->dev_dcb;
00318 
00319     /* Make sure that the decoder is idle. */
00320     if (dcb->dcb_pbstat != CODEC_STATUS_IDLE) {
00321         return -1;
00322     }
00323     /* Set new buffer size. */
00324     if (NutSegBufInit((size_t)size) == NULL) {
00325         return -1;
00326     }
00327     /* Set watermark defaults. */
00328     dcb->dcb_pbwlo = NutSegBufAvailable() / 3;
00329     dcb->dcb_pbwhi = dcb->dcb_pbwlo * 2;
00330 
00331     return 0;
00332 }
00333 
00334 /*
00335  * VLSI decoder feeding thread.
00336  */
00337 THREAD(FeederThread, arg)
00338 {
00339     uint8_t *bp;
00340     size_t avail;
00341     int filled;
00342     uint_fast8_t intest = 0;
00343     uint_fast16_t idlefill = 0;
00344     NUTDEVICE *dev = (NUTDEVICE *)arg;
00345     VSDCB *dcb = (VSDCB *)dev->dev_dcb;
00346 
00347     /* We are a high priority thread. */
00348     NutThreadSetPriority(7);
00349     for (;;) {
00350         /*
00351         ** Idle mode processing.
00352         */
00353         if (dcb->dcb_pbstat == CODEC_STATUS_IDLE) {
00354             /* Loop while in test mode. */
00355             do {
00356                 /* Wait for a request or a buffer update. */
00357                 NutEventWait(&dcb->dcb_feedme, NUT_WAIT_INFINITE);
00358                 /* Process beep commands. */
00359                 if (dcb->dcb_scmd & VSREQ_BEEP) {
00360                     if ((*dcb->dcb_isready)()) {
00361                         dcb->dcb_scmd &= ~VSREQ_BEEP;
00362                         intest = VsCodecBeep(dev, dcb->dcb_sinefreq) != 0;
00363                     }
00364                 }
00365             } while (intest);
00366             /* Check if we should change to record mode. */
00367             if (dcb->dcb_scmd & VSREQ_RECORD) {
00368                 dcb->dcb_scmd &= ~(VSREQ_RECORD | VSREQ_CANCEL);
00369                 dcb->dcb_pbstat = CODEC_STATUS_RECORDING;
00370                 /* Activate the encoder. */
00371                 VsCodecReg(dev, VS_OPCODE_WRITE, VS_AIADDR_REG, 0x0034);
00372             }
00373             /* Check if we should change to play mode. This will happen
00374             ** if we receive a play request or if the buffer contents
00375             ** reaches the high watermark. */
00376             else if ((dcb->dcb_scmd & VSREQ_PLAY) != 0 || NutSegBufUsed() >=  dcb->dcb_pbwhi) {
00377                 dcb->dcb_scmd &= ~(VSREQ_PLAY | VSREQ_CANCEL);
00378                 dcb->dcb_pbstat = CODEC_STATUS_PLAYING;
00379                 VsDecoderSetVolume(dev, dcb->dcb_lvol, dcb->dcb_rvol);
00380             }
00381         }
00382 
00383         /*
00384         ** Play mode processing.
00385         */
00386         if (dcb->dcb_pbstat == CODEC_STATUS_PLAYING) {
00387             /* Wait while decoder is busy. */
00388             VsCodecWaitReady(dev, NUT_WAIT_INFINITE);
00389             /* Process cancel requests. */
00390             if (dcb->dcb_scmd & VSREQ_CANCEL) {
00391                 dcb->dcb_scmd &= ~VSREQ_CANCEL;
00392                 NutSegBufReset();
00393 #if VS_HAS_SM_CANCEL
00394                 VsCodecMode(dev, VS_SM_CANCEL, VS_SM_CANCEL);
00395 #endif
00396             }
00397             /* Retrieve new data from the input buffer. */
00398             bp = (uint8_t *) NutSegBufReadRequest(&avail);
00399             if (avail) {
00400                 /* More data available, send as much as possible to the
00401                 ** decoder. If no data was sent, then the bus may be
00402                 ** blocked by another device. */
00403                 filled = (*dcb->dcb_senddata)(bp, avail);
00404                 if (filled) {
00405                     /* Update the buffer's read position. */
00406                     NutSegBufReadLast(filled);
00407                     NutEventPost(&dcb->dcb_bufque);
00408                     idlefill = 2048;
00409                 }
00410                 if (dcb->dcb_scmd & VSREQ_VOLUPD) {
00411                     VsDecoderSetVolume(dev, dcb->dcb_lvol, dcb->dcb_rvol);
00412                     dcb->dcb_scmd &= ~VSREQ_VOLUPD;
00413                 }
00414                 if (dcb->dcb_scmd & VSREQ_AUDIOE) {
00415                     VsDecoderSetBass( dev, dcb->dcb_treb, dcb->dcb_tfin, dcb->dcb_bass, dcb->dcb_bfin);
00416                     dcb->dcb_scmd &= ~VSREQ_AUDIOE;
00417                 }
00418             } else if (NutSegBufUsed() == 0) {
00419                 /* Running out of data. */
00420                 if (idlefill) {
00421                     /* For some reason the HDAT0/HDAT1 registers in the
00422                     ** VS1053 do not contain zero when the codec runs
00423                     ** empty. I expected this when reading the datasheet.
00424                     ** Instead we fill the buffer with zeros, to make
00425                     ** sure that the whole buffer is decoded before we
00426                     ** enter idle mode. */
00427                     idlefill -= (*dcb->dcb_senddata)(NULL, idlefill);
00428                 }
00429                 if (idlefill == 0) {
00430                     /* Finally we reached idle mode. */
00431                     dcb->dcb_pbstat = CODEC_STATUS_IDLE;
00432                     NutEventPost(&dcb->dcb_bufque);
00433                 }
00434             }
00435         }
00436 
00437         /* 
00438         ** Record mode processing. 
00439         */
00440         else if (dcb->dcb_pbstat == CODEC_STATUS_RECORDING) {
00441             for (;;) {
00442                 /* Process cancel requests. */
00443                 if (dcb->dcb_scmd & VSREQ_CANCEL) {
00444                     dcb->dcb_scmd &= ~VSREQ_CANCEL;
00445                     dcb->dcb_pbstat = CODEC_STATUS_IDLE;
00446                     break;
00447                 }
00448                 bp = (uint8_t *)NutSegBufWriteRequest(&avail);
00449                 avail >>= 1;
00450                 if (avail == 0) {
00451                     /* Buffer overflow. */
00452                     dcb->dcb_pbstat = CODEC_STATUS_IDLE;
00453                     break;
00454                 }
00455 #if defined(VS_HDAT0_REG) && defined(VS_HDAT1_REG)
00456                 /* TODO: Missing register definitions give compiler error. */
00457                 filled = VsCodecReg(dev, VS_OPCODE_READ, VS_HDAT1_REG, 0);
00458                 if (filled > 255) {
00459                     if (avail > filled) {
00460                         avail = filled;
00461                     } else {
00462                         filled = avail;
00463                     }
00464                     while (filled--) {
00465                         uint16_t data = VsCodecReg(dev, VS_OPCODE_READ, VS_HDAT0_REG, 0);
00466                         *bp++ = (uint8_t) data >> 8;
00467                         *bp++ = (uint8_t) data >> 8;
00468                     }
00469                     NutSegBufWriteLast(avail << 1);
00470                     NutEventPost(&dcb->dcb_bufque);
00471                 } else {
00472                     NutSleep(200);
00473                 }
00474 #endif
00475             }
00476             NutEventPost(&dcb->dcb_bufque);
00477         }
00478     }
00479 }
00480 
00481 /* TODO: This is a preview and may not work as expected. */
00482 static int VsCodecLoadPlugIn(NUTDEVICE *dev, VS_PLUGIN_INFO *plg)
00483 {
00484     uint_fast8_t reg;
00485     uint_fast16_t cnt;
00486     size_t i = 0;
00487 
00488 #ifdef VS_SM_ADPCM
00489     VsCodecMode(dev, 0, VS_SM_ADPCM);
00490 #endif
00491     VsCodecReg(dev, VS_OPCODE_WRITE, VS_CLOCKF_REG, 0xC000);
00492     NutSleep(100);
00493 #if VS_HAS_BASS_REG
00494     VsCodecReg(dev, VS_OPCODE_WRITE, VS_BASS_REG, 0);
00495 #endif
00496         VsCodecReg(dev, VS_OPCODE_WRITE, VS_AIADDR_REG, 0);
00497         VsCodecReg(dev, VS_OPCODE_WRITE, VS_WRAMADDR_REG, 0xC01A);
00498         VsCodecReg(dev, VS_OPCODE_WRITE, VS_WRAM_REG, 0x0002);
00499     while (i + 3 <= plg->vsplg_size) {
00500         reg = (uint_fast8_t)plg->vsplg_data[i++];
00501         cnt = plg->vsplg_data[i++];
00502 
00503         if (cnt & 0x8000) {
00504             cnt &= 0x7FFF;
00505             while (cnt--) {
00506                 VsCodecReg(dev, VS_OPCODE_WRITE, reg, plg->vsplg_data[i]);
00507             }
00508             i++;
00509         } 
00510         else if (i + cnt <= plg->vsplg_size) {
00511             while (cnt--) {
00512                 VsCodecReg(dev, VS_OPCODE_WRITE, reg, plg->vsplg_data[i]);
00513                 i++;
00514             }
00515         } 
00516         else {
00517             break;
00518         }
00519     }
00520     if (i != plg->vsplg_size) {
00521         return -1;
00522     }
00523 
00524 #ifdef VS_SM_ADPCM
00525         // set bits 12 and 13 of register SCI_MODE
00526         // 12 is the ADPCM bit, 13 is the MIC/LINE bit.
00527     VsCodecMode(dev, VS_SM_LINE_IN | VS_SM_ADPCM, VS_SM_LINE_IN | VS_SM_ADPCM);
00528 #endif
00529 
00530 #if VS_HAS_AICTRL0_REG
00531         // write 0 to SCI_AICTRL0 (maximum signal level, set by encoder to read later on)
00532         VsCodecReg(dev, VS_OPCODE_WRITE, VS_AICTRL0_REG, 0);
00533 #endif
00534 
00535     // recording gain 0: automatic gain control on
00536         //CodecReg(&codec_node, VS_OPCODE_WRITE, VS_AICTRL1_REG, 0);
00537 
00538 #if VS_HAS_AICTRL1_REG
00539         // recording gain 1
00540         VsCodecReg(dev, VS_OPCODE_WRITE, VS_AICTRL1_REG, 1024);
00541 #endif
00542 
00543 #if VS_HAS_AICTRL2_REG
00544         // maximum autogain amplification, 4096 (=4x) is recommended
00545         VsCodecReg(dev, VS_OPCODE_WRITE, VS_AICTRL2_REG, 4096);
00546 #endif
00547 
00548 #if VS_HAS_AICTRL3_REG  
00549         // setting SCI_AICTRL3 should be fine, too...
00550         VsCodecReg(dev, VS_OPCODE_WRITE, VS_AICTRL3_REG, 0);
00551 #endif
00552 
00553     return 0;
00554 }
00555 
00577 int VsCodecIOCtl(NUTDEVICE * dev, int req, void *conf)
00578 {
00579     int rc = 0;
00580     uint32_t *lvp = (uint32_t *) conf;
00581     int *ivp = (int *) conf;
00582     VSDCB *dcb = (VSDCB *)dev->dev_dcb;
00583 
00584     switch (req) {
00585     case AUDIO_PLAY:
00586         if (dcb->dcb_pbstat == CODEC_STATUS_IDLE) {
00587             /* Immediately start playing. */
00588             dcb->dcb_scmd |= VSREQ_PLAY;
00589         }
00590         NutEventPost(&dcb->dcb_feedme);
00591         break;
00592     case AUDIO_CANCEL:
00593         if (dcb->dcb_pbstat == CODEC_STATUS_PLAYING) {
00594             /* Immediately stop playing and discard buffer. */
00595             dcb->dcb_scmd |= VSREQ_CANCEL;
00596         }
00597         NutEventPost(&dcb->dcb_feedme);
00598         break;
00599     case AUDIO_GET_STATUS:
00600         *ivp = dcb->dcb_pbstat;
00601         break;
00602     case AUDIO_GET_PLAYGAIN:
00603         *ivp = dcb->dcb_rvol > dcb->dcb_lvol ? dcb->dcb_rvol : dcb->dcb_lvol;
00604         break;
00605     case AUDIO_SET_PLAYGAIN:
00606         dcb->dcb_lvol = *ivp;
00607         dcb->dcb_rvol = *ivp;
00608         dcb->dcb_scmd |= VSREQ_VOLUPD;
00609         break;
00610     case AUDIO_SET_TREB:
00611         dcb->dcb_treb = *ivp;
00612         dcb->dcb_scmd |= VSREQ_AUDIOE;
00613         break;
00614     case AUDIO_SET_TFIN:
00615         dcb->dcb_tfin = *ivp;
00616         dcb->dcb_scmd |= VSREQ_AUDIOE;
00617         break;
00618     case AUDIO_SET_BASS:
00619         dcb->dcb_bass = *ivp;
00620         dcb->dcb_scmd |= VSREQ_AUDIOE;
00621         break;
00622     case AUDIO_SET_BFIN:
00623         dcb->dcb_bfin = *ivp;
00624         dcb->dcb_scmd |= VSREQ_AUDIOE;
00625         break;
00626     case AUDIO_GET_PBSIZE:
00627         *lvp = NutSegBufAvailable() + NutSegBufUsed();
00628         break;
00629     case AUDIO_SET_PBSIZE:
00630         rc = VsDecoderBufferInit(dev, *lvp);
00631         break;
00632     case AUDIO_GET_PBLEVEL:
00633         *lvp = NutSegBufUsed();
00634         break;
00635     case AUDIO_GET_PBWLOW:
00636         *lvp = dcb->dcb_pbwlo;
00637         break;
00638     case AUDIO_SET_PBWLOW:
00639         dcb->dcb_pbwlo = *lvp;
00640         break;
00641     case AUDIO_GET_PBWHIGH:
00642         *lvp = dcb->dcb_pbwhi;
00643         break;
00644     case AUDIO_SET_PBWHIGH:
00645         dcb->dcb_pbwhi = *lvp;
00646         break;
00647     case AUDIO_SETWRITETIMEOUT:
00648         dcb->dcb_wtmo = *lvp;
00649         break;
00650     case AUDIO_GETWRITETIMEOUT:
00651         *lvp = dcb->dcb_wtmo;
00652         break;
00653     case AUDIO_BEEP:
00654         if (dcb->dcb_pbstat == CODEC_STATUS_IDLE) {
00655             dcb->dcb_sinefreq = *((uint16_t *) conf);
00656             dcb->dcb_scmd |= VSREQ_BEEP;
00657             NutEventPost(&dcb->dcb_feedme);
00658         } else {
00659             rc = -1;
00660         }
00661         break;
00662     case AUDIO_GET_DECCAPS:
00663         /* Retrieve decoder capabilities. */
00664         *lvp = dcb->dcb_dec_caps;
00665         break;
00666     case AUDIO_GET_CODCAPS:
00667         /* Retrieve encoder capabilities. */
00668         *lvp = dcb->dcb_cod_caps;
00669         break;
00670     case AUDIO_GET_MIDCAPS:
00671         /* Retrieve midi capabilities. */
00672         *lvp = dcb->dcb_midi_caps;
00673         break;
00674 
00675 #if 0
00676     case AUDIO_GET_DECINFO:
00677         /* Retrieve decoder information. */
00678         break;
00679     case AUDIO_GET_CODINFO:
00680         /* Retrieve encoder information. */
00681         break;
00682     case AUDIO_GET_MIDINFO:
00683         /* Retrieve midi information. */
00684         break;
00685 #endif
00686     case AUDIO_PLUGIN_UPLOAD:
00687         rc = VsCodecLoadPlugIn(dev, (VS_PLUGIN_INFO *) conf);
00688         break;
00689 
00690     default:
00691         rc = (*dcb->dcb_control)(req, conf);
00692         break;
00693     }
00694     return rc;
00695 }
00696 
00709 static int VsDecoderBufferFlush(NUTDEVICE *dev, uint32_t tmo)
00710 {
00711     int rc = 0;
00712     VSDCB *dcb = dev->dev_dcb;
00713 
00714     for (;;) {
00715         /* Keep the player running if the buffer contains data. */
00716         if (NutSegBufUsed()) {
00717             VsCodecIOCtl(dev, AUDIO_PLAY, NULL);
00718         }
00719         /* No data in buffer. If idle, then we are done. */
00720         else if (dcb->dcb_pbstat == CODEC_STATUS_IDLE) {
00721             /* No data and player is idle. */
00722             break;
00723         }
00724         /* Wait for a new buffer update. */
00725         rc = NutEventWait(&dcb->dcb_bufque, tmo);
00726         if (rc) {
00727             break;
00728         }
00729     }
00730     return rc;
00731 }
00732 
00746 int VsCodecRead(NUTFILE * nfp, void *data, int len)
00747 {
00748     int rc = 0;
00749     uint8_t *bp;
00750     uint8_t *dp;
00751     size_t rbytes;
00752     NUTDEVICE *dev;
00753     VSDCB *dcb;
00754 
00755     dev = nfp->nf_dev;
00756     dcb = dev->dev_dcb;
00757 
00758     /* Flush buffer if data pointer is a NULL pointer. */
00759     if (data == NULL || len == 0) {
00760         return 0;
00761     }
00762     if (dcb->dcb_pbstat == CODEC_STATUS_PLAYING) {
00763         /* Cannot read decoder data. */
00764         return -1;
00765     } else if (dcb->dcb_pbstat == CODEC_STATUS_IDLE) {
00766         /* Start recording if idle. */
00767         if (dcb->dcb_cod_mode & AUDIO_FMT_VORBIS) {
00768         }
00769         dcb->dcb_scmd |= VSREQ_RECORD;
00770         NutEventPost(&dcb->dcb_feedme);
00771     }
00772     dp = data;
00773     while (len) {
00774         /* Retrieve new data from the input buffer. */
00775         bp = (uint8_t *) NutSegBufReadRequest(&rbytes);
00776         if (rbytes) {
00777             if (rbytes > len) {
00778                 rbytes = len;
00779             }
00780             memcpy(dp, bp, rbytes);
00781             NutSegBufReadLast(rbytes);
00782             NutEventPost(&dcb->dcb_feedme);
00783             len -= rbytes;
00784             rc += rbytes;
00785             dp += rbytes;
00786         }
00787         /* Wait for data. */
00788         else if (NutEventWait(&dcb->dcb_bufque, dcb->dcb_rtmo)) {
00789             /* Read timeout. */
00790             break;
00791         }
00792     }
00793     return rc;
00794 }
00795 
00809 int VsCodecWrite(NUTFILE * nfp, CONST void *data, int len)
00810 {
00811     int rc = 0;
00812     uint8_t *bp;
00813     CONST uint8_t *dp;
00814     size_t rbytes;
00815     VSDCB *dcb = nfp->nf_dev->dev_dcb;
00816 
00817     /* Flush buffer if data pointer is a NULL pointer. */
00818     if (data == NULL || len == 0) {
00819         return VsDecoderBufferFlush(nfp->nf_dev, dcb->dcb_wtmo);
00820     }
00821     dp = data;
00822     while (len) {
00823         bp = (uint8_t *)NutSegBufWriteRequest(&rbytes);
00824         if (rbytes == 0) {
00825             /* No buffer space, wait for change. */
00826             if (NutEventWait(&dcb->dcb_bufque, dcb->dcb_wtmo)) {
00827                 /* Write timeout. */
00828                 break;
00829             }
00830         } else {
00831             if (rbytes > len) {
00832                 rbytes = len;
00833             }
00834             memcpy(bp, dp, rbytes);
00835             NutSegBufWriteLast(rbytes);
00836             NutEventPost(&dcb->dcb_feedme);
00837             len -= rbytes;
00838             rc += rbytes;
00839             dp += rbytes;
00840         }
00841     }
00842     return rc;
00843 }
00844 
00845 #ifdef __HARVARD_ARCH__
00846 
00868 int VsCodecWrite_P(NUTFILE * nfp, PGM_P buffer, int len)
00869 {
00870     return -1;
00871 }
00872 #endif
00873 
00874 /*
00875  * Open codec stream.
00876  */
00877 NUTFILE *VsCodecOpen(NUTDEVICE * dev, CONST char *name, int mode, int acc)
00878 {
00879     NUTFILE *nfp;
00880     VSDCB *dcb;
00881 
00882 #if defined(VS_SW_RESET_ON_OPEN)
00883     VsCodecMode(dev, VS_SM_RESET, VS_SM_RESET);
00884 #endif
00885 
00886     dcb = dev->dev_dcb;
00887     if (mode & _O_WRONLY) {
00888         uint32_t on = 1;
00889         /* Decoder. */
00890         if (VsCodecIOCtl(dev, AUDIO_IRQ_ENABLE, &on)) {
00891             return NULL;
00892         }
00893     } else {
00894         /* Encoder. */
00895         if (strcmp(name, "vorbis") == 0) {
00896             dcb->dcb_cod_mode = AUDIO_FMT_VORBIS;
00897         } else {
00898             dcb->dcb_cod_mode = AUDIO_FMT_WAV_IMA_ADPCM;
00899         }
00900     }
00901 
00902     nfp = malloc(sizeof(NUTFILE));
00903     if (nfp) {
00904         nfp->nf_next = NULL;
00905         nfp->nf_dev = dev;
00906         nfp->nf_fcb = NULL;
00907     }
00908     NutSegBufReset();
00909 
00910     return nfp;
00911 }
00912 
00913 /*
00914  * Close codec stream.
00915  */
00916 int VsCodecClose(NUTFILE * nfp)
00917 {
00918     VSDCB *dcb = nfp->nf_dev->dev_dcb;
00919 
00920     int rc = VsDecoderBufferFlush(nfp->nf_dev, dcb->dcb_wtmo);
00921 
00922     if (nfp) {
00923         free(nfp);
00924     }
00925     return rc;
00926 }
00927