| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687 |
- /*
- * Copyright (C) 2008-2009 by egnite GmbH.
- * Copyright (C) 2001-2007 by egnite Software GmbH.
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the copyright holders nor the names of
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
- * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- * For additional information see http://www.ethernut.de/
- */
- /*
- * $Log$
- * Revision 1.4 2009/02/13 14:46:19 haraldkipp
- * Using the VS10xx driver as a template for redesign.
- *
- * Revision 1.3 2009/01/17 11:26:46 haraldkipp
- * Getting rid of two remaining BSD types in favor of stdint.
- * Replaced 'u_int' by 'unsinged int' and 'uptr_t' by 'uintptr_t'.
- *
- * Revision 1.2 2008/10/23 08:54:07 haraldkipp
- * Include the correct header file.
- *
- * Revision 1.1 2008/10/05 16:56:15 haraldkipp
- * Added Helix audio device.
- *
- */
- #include <sys/atom.h>
- #include <sys/event.h>
- #include <sys/timer.h>
- #include <sys/heap.h>
- #include <cfg/arch/gpio.h>
- #include <cfg/audio.h>
- #include <dev/irqreg.h>
- #include <contrib/hxmp3/mp3dec.h>
- #include <contrib/hxmp3/hermite.h>
- #include <dev/hxcodec.h>
- #include <dev/tlv320dac.h>
- #include <sys/bankmem.h>
- #include <stdlib.h>
- #include <stddef.h>
- #include <string.h>
- #include <memdebug.h>
- #if 0
- /* Use for local debugging. */
- #define NUTDEBUG
- #include <stdio.h>
- #endif
- #ifndef NUT_THREAD_HXCODEC0STACK
- /* arm-elf-gcc optimized code used 540 bytes. */
- #define NUT_THREAD_HXCODEC0STACK 768
- #endif
- #ifndef HXCODEC0_MAX_OUTPUT_BUFSIZ
- /*! \brief Output buffer size limit. */
- #define HXCODEC0_MAX_OUTPUT_BUFSIZ 393216
- #endif
- #ifndef DAC_OUTPUT_RATE
- #if defined (AT91SAM9260_EK)
- #define DAC_OUTPUT_RATE 44100
- #else
- #define DAC_OUTPUT_RATE 8000
- #endif
- #endif
- #ifndef MP3_BUFSIZ
- #if defined (AT91SAM9260_EK)
- #define MP3_BUFSIZ 1048576
- #else
- #define MP3_BUFSIZ (4 * MAINBUF_SIZE)
- #endif
- #endif
- #ifdef HXCODEC0_RESAMPLER
- static int rs_maxout;
- static short *rs_pcmbuf;
- #endif
- /*! \name Internal Command Flags */
- /*@{*/
- /*! \brief Force immediate player start. */
- #define HXREQ_PLAY 0x00000001
- /*! \brief Force immediate player stop. */
- #define HXREQ_CANCEL 0x00000002
- /*! \brief Volume update. */
- #define HXREQ_VOLUPD 0x00000004
- /*! \brief Sine wave test. */
- #define HXREQ_BEEP 0x00000008
- /*@}*/
- /*!
- * \addtogroup xgHelixCodec
- */
- /*@{*/
- typedef struct _HXDCB {
- int dcb_pbstat; /*!< \brief Playback status. */
- uint32_t dcb_scmd; /*!< \brief Requested command flags, see HXREQ_ flags. */
- int dcb_rvol; /*!< \brief Volume of right channel. */
- int dcb_lvol; /*!< \brief Volume of left channel. */
- uint32_t dcb_pbwlo; /*!< \brief Playback buffer low watermark. */
- uint32_t dcb_pbwhi; /*!< \brief Playback buffer high watermark. */
- uint32_t dcb_wtmo; /*!< \brief Write timeout. */
- HANDLE dcb_bufque; /*!< \brief Buffer change event. */
- HANDLE dcb_feedme; /*!< \brief Decoder hungry event. */
- } HXDCB;
- static HXDCB dcb;
- typedef struct _MP3PLAYERINFO {
- HMP3Decoder mpi_mp3dec;
- MP3FrameInfo mpi_frameinfo;
- } MP3PLAYERINFO;
- static MP3PLAYERINFO mpi;
- static short pi_pcmbuf[MAX_NCHAN * MAX_NGRAN * MAX_NSAMP];
- static int first_frame;
- static int samprate;
- /*!
- * \brief Set volume.
- *
- * \param dev Specifies the audio codec device.
- * \param left Left channel gain in db.
- * \param right Right channel gain in db.
- *
- * \return Always 0.
- */
- static int HelixPlayerSetVolume(NUTDEVICE *dev, int left, int right)
- {
- HXDCB *dcb = (HXDCB *)dev->dev_dcb;
- /* Honor limits. */
- left = left > AUDIO_DAC_MAX_GAIN ? AUDIO_DAC_MAX_GAIN : left;
- left = left < AUDIO_DAC_MIN_GAIN ? AUDIO_DAC_MIN_GAIN : left;
- right = right > AUDIO_DAC_MAX_GAIN ? AUDIO_DAC_MAX_GAIN : right;
- right = right < AUDIO_DAC_MIN_GAIN ? AUDIO_DAC_MIN_GAIN : right;
- Tlv320DacSetVolume(left, right);
- dcb->dcb_lvol = left;
- dcb->dcb_rvol = right;
- return 0;
- }
- static int DecodeFrame(uint8_t *buf, int len)
- {
- int rc = len;
- int skip;
- static void *hres;
- while (len > 2 * MAINBUF_SIZE) {
- if ((skip = MP3FindSyncWord(buf, len)) < 0) {
- return len;
- }
- if (skip) {
- len -= skip;
- buf += skip;
- } else {
- if (MP3Decode(mpi.mpi_mp3dec, &buf, &len, pi_pcmbuf, 0)) {
- return -1;
- }
- if (first_frame) {
- MP3GetLastFrameInfo(mpi.mpi_mp3dec, &mpi.mpi_frameinfo);
- if (mpi.mpi_frameinfo.nChans == 1) {
- samprate = mpi.mpi_frameinfo.samprate / 2;
- }
- else if (mpi.mpi_frameinfo.nChans == 2) {
- samprate = mpi.mpi_frameinfo.samprate;
- }
- else {
- return -1;
- }
- if (mpi.mpi_frameinfo.samprate < 8000 || samprate > DAC_OUTPUT_RATE) {
- return -1;
- }
- if (mpi.mpi_frameinfo.bitsPerSample != 16) {
- return -1;
- }
- #ifdef HXCODEC0_RESAMPLER
- /* If needed, initialize resampler. */
- if (samprate != DAC_OUTPUT_RATE) {
- if ((hres = RAInitResamplerHermite(samprate, DAC_OUTPUT_RATE, mpi.mpi_frameinfo.nChans)) == NULL) {
- return -1;
- }
- rs_maxout = RAGetMaxOutputHermite(mpi.mpi_frameinfo.outputSamps, hres);
- if ((rs_pcmbuf = malloc(rs_maxout * 2)) == NULL) {
- return -1;
- }
- }
- #else
- (void)hres;
- #endif
- first_frame = 0;
- }
- #ifdef HXCODEC0_RESAMPLER
- if (hres) {
- int os;
- if (mpi.mpi_frameinfo.nChans == 1) {
- os = RAResampleMonoHermite(pi_pcmbuf, mpi.mpi_frameinfo.outputSamps, rs_pcmbuf, hres);
- }
- else {
- os = RAResampleStereoHermite(pi_pcmbuf, mpi.mpi_frameinfo.outputSamps, rs_pcmbuf, hres);
- }
- if (os <= 0) {
- break;
- }
- if (Tlv320DacWrite(rs_pcmbuf, os)) {
- break;
- }
- } else
- #endif
- {
- if (Tlv320DacWrite(pi_pcmbuf, mpi.mpi_frameinfo.outputSamps)) {
- return -1;
- }
- }
- break;
- }
- }
- return rc - len;
- }
- /*
- * Initialize the stream output buffer with a given size.
- *
- * \param dev Specifies the audio codec device.
- */
- static int HelixPlayerBufferInit(NUTDEVICE *dev, uint32_t size)
- {
- HXDCB *dcb = (HXDCB *)dev->dev_dcb;
- /* Make sure that the decoder is idle. */
- if (dcb->dcb_pbstat != CODEC_STATUS_IDLE) {
- return -1;
- }
- /* Set new buffer size. */
- if (NutSegBufInit((size_t)size) == NULL) {
- return -1;
- }
- /* Set watermark defaults. */
- dcb->dcb_pbwlo = NutSegBufAvailable() / 3;
- dcb->dcb_pbwhi = dcb->dcb_pbwlo * 2;
- return 0;
- }
- /*
- * VLSI decoder feeding thread.
- */
- THREAD(FeederThread, arg)
- {
- uint8_t *bp;
- uint8_t *backbuf = NULL;
- int backcnt = 0;
- size_t avail;
- int filled;
- NUTDEVICE *dev = (NUTDEVICE *)arg;
- HXDCB *dcb = (HXDCB *)dev->dev_dcb;
- /* We are a high priority thread. */
- NutThreadSetPriority(7);
- for (;;) {
- /*
- ** Idle mode processing.
- */
- if (dcb->dcb_pbstat == CODEC_STATUS_IDLE) {
- /* Wait for a request or a buffer update. */
- NutEventWait(&dcb->dcb_feedme, NUT_WAIT_INFINITE);
- /* Check if we should change to play mode. This will happen
- ** if we receive a play request or if the buffer contents
- ** reaches the high watermark. */
- if ((dcb->dcb_scmd & HXREQ_PLAY) != 0 || NutSegBufUsed() >= dcb->dcb_pbwhi) {
- dcb->dcb_scmd &= ~(HXREQ_PLAY | HXREQ_CANCEL);
- dcb->dcb_pbstat = CODEC_STATUS_PLAYING;
- HelixPlayerSetVolume(dev, dcb->dcb_lvol, dcb->dcb_rvol);
- }
- }
- /*
- ** Play mode processing.
- */
- if (dcb->dcb_pbstat == CODEC_STATUS_PLAYING) {
- /* Process cancel requests. */
- if (dcb->dcb_scmd & HXREQ_CANCEL) {
- dcb->dcb_scmd &= ~HXREQ_CANCEL;
- NutSegBufReset();
- }
- if (dcb->dcb_scmd & HXREQ_VOLUPD) {
- HelixPlayerSetVolume(dev, dcb->dcb_lvol, dcb->dcb_rvol);
- dcb->dcb_scmd &= ~HXREQ_VOLUPD;
- }
- /* Retrieve new data from the input buffer. */
- bp = (uint8_t *) NutSegBufReadRequest(&avail);
- if (backcnt && backcnt + avail >= 4 * MAINBUF_SIZE) {
- memcpy(&backbuf[backcnt], bp, 4 * MAINBUF_SIZE - backcnt);
- filled = DecodeFrame(backbuf, 4 * MAINBUF_SIZE);
- if (filled < 0) {
- backcnt = 0;
- free(backbuf);
- } else if (filled < backcnt) {
- bp = backbuf;
- backcnt -= filled;
- backbuf = malloc(4 * MAINBUF_SIZE);
- memcpy(backbuf, &bp[filled], backcnt);
- free(bp);
- } else {
- if (filled > backcnt) {
- NutSegBufReadLast(filled - backcnt);
- }
- backcnt = 0;
- free(backbuf);
- }
- } else if (avail > 2 * MAINBUF_SIZE) {
- filled = DecodeFrame(bp, avail);
- if (filled < 0) {
- NutSegBufReadLast(1);
- } else if (filled) {
- NutSegBufReadLast(filled);
- }
- NutEventPost(&dcb->dcb_bufque);
- }
- else if (NutSegBufUsed() == 0) {
- /* Finally we reached idle mode. */
- dcb->dcb_pbstat = CODEC_STATUS_IDLE;
- NutEventPost(&dcb->dcb_bufque);
- } else if (avail && backcnt == 0) {
- backcnt = avail;
- backbuf = malloc(4 * MAINBUF_SIZE);
- memcpy(backbuf, bp, backcnt);
- NutSegBufReadLast(backcnt);
- NutEventPost(&dcb->dcb_bufque);
- } else {
- /* Wait for a request or a buffer update. */
- NutEventWait(&dcb->dcb_feedme, 1000);
- }
- }
- }
- }
- /*!
- * \brief Handle I/O controls for audio codec.
- *
- * - AUDIO_PLAY Force playback start, even if the buffer level is too lower.
- * - AUDIO_CANCEL Cancel playback and discard all buffered data.
- * - AUDIO_GET_STATUS Sets an int to 1 if the player is running, 0 if idle.
- * - AUDIO_GET_PLAYGAIN Sets an int to the current playback gain, 0..-127.
- * - AUDIO_SET_PLAYGAIN Reads the requested playback gain from an int, 0..-127.
- * - AUDIO_GET_PBSIZE Sets an unsigned long with the size of the playback buffer.
- * - AUDIO_SET_PBSIZE Sets the size the playback buffer using an unsigned long.
- * - AUDIO_GET_PBLEVEL Sets an unsigned long with the number of bytes in the playback buffer.
- * - AUDIO_GET_PBWLOW Sets an unsigned long with the low watermark of the playback buffer.
- * - AUDIO_SET_PBWLOW Sets the low watermark (unsigned long) of the playback buffer.
- * - AUDIO_GET_PBWHIGH Sets an unsigned long with the high watermark of the playback buffer.
- * - AUDIO_SET_PBWHIGH Sets the high watermark (unsigned long) of the playback buffer.
- * - AUDIO_BEEP Plays a short sine wave beep.
- *
- * \return 0 on success, -1 otherwise.
- */
- static int HelixIOCtl(NUTDEVICE * dev, int req, void *conf)
- {
- int rc = 0;
- uint32_t *lvp = (uint32_t *) conf;
- int *ivp = (int *) conf;
- HXDCB *dcb = (HXDCB *)dev->dev_dcb;
- switch (req) {
- case AUDIO_PLAY:
- if (dcb->dcb_pbstat == CODEC_STATUS_IDLE) {
- /* Immediately start playing. */
- dcb->dcb_scmd |= HXREQ_PLAY;
- }
- NutEventPost(&dcb->dcb_feedme);
- break;
- case AUDIO_CANCEL:
- if (dcb->dcb_pbstat == CODEC_STATUS_PLAYING) {
- /* Immediately stop playing and discard buffer. */
- dcb->dcb_scmd |= HXREQ_CANCEL;
- }
- NutEventPost(&dcb->dcb_feedme);
- break;
- case AUDIO_GET_STATUS:
- *ivp = dcb->dcb_pbstat;
- break;
- case AUDIO_GET_PLAYGAIN:
- *ivp = dcb->dcb_rvol > dcb->dcb_lvol ? dcb->dcb_rvol : dcb->dcb_lvol;
- break;
- case AUDIO_SET_PLAYGAIN:
- dcb->dcb_lvol = *ivp;
- dcb->dcb_rvol = *ivp;
- dcb->dcb_scmd |= HXREQ_VOLUPD;
- break;
- case AUDIO_GET_PBSIZE:
- *lvp = NutSegBufAvailable() + NutSegBufUsed();
- break;
- case AUDIO_SET_PBSIZE:
- rc = HelixPlayerBufferInit(dev, *lvp);
- break;
- case AUDIO_GET_PBLEVEL:
- *lvp = NutSegBufUsed();
- break;
- case AUDIO_GET_PBWLOW:
- *lvp = dcb->dcb_pbwlo;
- break;
- case AUDIO_SET_PBWLOW:
- dcb->dcb_pbwlo = *lvp;
- break;
- case AUDIO_GET_PBWHIGH:
- *lvp = dcb->dcb_pbwhi;
- break;
- case AUDIO_SET_PBWHIGH:
- dcb->dcb_pbwhi = *lvp;
- break;
- case AUDIO_SETWRITETIMEOUT:
- dcb->dcb_wtmo = *lvp;
- break;
- case AUDIO_GETWRITETIMEOUT:
- *lvp = dcb->dcb_wtmo;
- break;
- default:
- rc = -1;
- break;
- }
- return rc;
- }
- /*!
- * \brief Flush VLSI audio decoder buffer.
- *
- * Waits until all currently buffered data had been processed by the
- * decoder. This makes sure that the end of the stream will not be
- * cut off.
- *
- * \param dev Specifies the audio codec device.
- * \param tmo Timeout in milliseconds.
- *
- * \return 0 on success, -1 on timeout.
- */
- static int HelixPlayerFlush(NUTDEVICE *dev, uint32_t tmo)
- {
- int rc = 0;
- HXDCB *dcb = dev->dev_dcb;
- for (;;) {
- /* Keep the player running if the buffer contains data. */
- if (NutSegBufUsed()) {
- HelixIOCtl(dev, AUDIO_PLAY, NULL);
- }
- /* No data in buffer. If idle, then we are done. */
- else if (dcb->dcb_pbstat == CODEC_STATUS_IDLE) {
- /* No data and player is idle. */
- break;
- }
- /* Wait for a new buffer update. */
- rc = NutEventWait(&dcb->dcb_bufque, tmo);
- if (rc) {
- break;
- }
- }
- Tlv320DacFlush();
- return rc;
- }
- /*!
- * \brief Send data to the decoder.
- *
- * A carriage return character will be automatically appended
- * to any linefeed.
- *
- * \return Number of characters sent.
- */
- static int HelixWrite(NUTFILE * nfp, const void *data, int len)
- {
- int rc = 0;
- uint8_t *bp;
- const uint8_t *dp;
- size_t rbytes;
- HXDCB *dcb = nfp->nf_dev->dev_dcb;
- dcb->dcb_wtmo = 3000; //DEBUG
- /* Flush buffer if data pointer is a NULL pointer. */
- if (data == NULL || len == 0) {
- return HelixPlayerFlush(nfp->nf_dev, dcb->dcb_wtmo);
- }
- dp = data;
- while (len) {
- bp = (uint8_t *)NutSegBufWriteRequest(&rbytes);
- if (rbytes == 0) {
- /* No buffer space, wait for change. */
- if (NutEventWait(&dcb->dcb_bufque, dcb->dcb_wtmo)) {
- /* Write timeout. */
- break;
- }
- } else {
- if (rbytes > len) {
- rbytes = len;
- }
- memcpy(bp, dp, rbytes);
- NutSegBufWriteLast(rbytes);
- NutEventPost(&dcb->dcb_feedme);
- len -= rbytes;
- rc += rbytes;
- dp += rbytes;
- }
- }
- return rc;
- }
- #ifdef __HARVARD_ARCH__
- /*!
- * \brief Write to device.
- *
- * Similar to HelixWrite() except that the data is expected in program memory.
- *
- * This function is implemented for CPUs with Harvard Architecture only.
- *
- * This function is called by the low level output routines of the
- * \ref xrCrtLowio "C runtime library", using the
- * \ref _NUTDEVICE::dev_write_P entry.
- *
- * \param nfp Pointer to a \ref NUTFILE structure, obtained by a previous
- * call to PnutFileOpen().
- * \param buffer Pointer to the data in program space. If zero, then the
- * output buffer will be flushed.
- * \param len Number of bytes to write.
- *
- * \return The number of bytes written. A return value of -1 indicates an
- * error. Currently this function is not implemented and always
- * returns -1.
- *
- */
- static int HelixWrite_P(NUTFILE * nfp, PGM_P buffer, int len)
- {
- return -1;
- }
- #endif
- /*!
- * \brief Open codec device.
- *
- * \return Pointer to a static NUTFILE structure.
- */
- static NUTFILE *HelixOpen(NUTDEVICE * dev, const char *name, int mode, int acc)
- {
- NUTFILE *nfp;
- if ((mpi.mpi_mp3dec = MP3InitDecoder()) == NULL) {
- return NUTFILE_EOF;
- }
- first_frame = 1;
- NutSleep(2);
- nfp = malloc(sizeof(NUTFILE));
- nfp->nf_dev = dev;
- nfp->nf_fcb = NULL;
- NutSegBufReset();
- return nfp;
- }
- /*!
- * \brief Close codec device.
- */
- static int HelixClose(NUTFILE * nfp)
- {
- HXDCB *dcb = nfp->nf_dev->dev_dcb;
- int rc;
- rc = HelixPlayerFlush(nfp->nf_dev, dcb->dcb_wtmo);
- if (nfp) {
- if (mpi.mpi_mp3dec) {
- MP3FreeDecoder(mpi.mpi_mp3dec);
- mpi.mpi_mp3dec = NULL;
- }
- free(nfp);
- }
- return rc;
- }
- /*
- * Called via dev_init pointer when the device is registered.
- */
- static int HelixInit(NUTDEVICE * dev)
- {
- size_t avail;
- Tlv320DacInit(DAC_OUTPUT_RATE);
- dcb.dcb_rvol = -32;
- dcb.dcb_lvol = -32;
- Tlv320DacSetVolume(dcb.dcb_lvol, dcb.dcb_rvol);
- /*
- ** Initialize the decoder stream buffer.
- */
- #ifdef HXCODEC0_OUTPUT_BUFSIZ
- avail = HXCODEC0_OUTPUT_BUFSIZ;
- #else
- avail = NutHeapAvailable() / 2;
- if (avail > HXCODEC0_MAX_OUTPUT_BUFSIZ) {
- avail = HXCODEC0_MAX_OUTPUT_BUFSIZ;
- }
- #endif
- if (HelixPlayerBufferInit(dev, avail)) {
- return -1;
- }
- /* Start the feeder thread. */
- if (NutThreadCreate(dev->dev_name, FeederThread, dev,
- (NUT_THREAD_HXCODEC0STACK * NUT_THREAD_STACK_MULT) + NUT_THREAD_STACK_ADD) == 0) {
- return -1;
- }
- return 0;
- }
- /*!
- * \brief Device information structure.
- *
- * An application must pass a pointer to this structure to
- * NutRegisterDevice() before using this driver.
- *
- * The device is named \b audio0.
- *
- * \showinitializer
- */
- NUTDEVICE devHelixCodec = {
- 0, /* Pointer to next device, dev_next. */
- {'a', 'u', 'd', 'i', 'o', '0', 0, 0, 0}, /* Unique device name, dev_name. */
- IFTYP_CHAR, /* Type of device, dev_type. */
- 0, /* Base address, dev_base (not used). */
- 0, /* First interrupt number, dev_irq (not used). */
- 0, /* Interface control block, dev_icb (not used). */
- &dcb, /* Driver control block, dev_dcb. */
- HelixInit, /* Driver initialization routine, dev_init. */
- HelixIOCtl, /* Driver specific control function, dev_ioctl. */
- NULL, /* Read from device, dev_read. */
- HelixWrite, /* Write to device, dev_write. */
- #ifdef __HARVARD_ARCH__
- HelixWrite_P, /* Write data from program space to device, dev_write_P. */
- #endif
- HelixOpen, /* Open a device or file, dev_open. */
- HelixClose, /* Close a device or file, dev_close. */
- NULL, /* Request file size, dev_size. */
- NULL, /* Select function, optional, not yet implemented */
- };
- /*@}*/
|