/* * Shannon DSP driver. Originally called /os/shannon/devspi.c. * Links against shandsp.c (which was once phone.c). */ #include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "../port/error.h" #include "../port/netif.h" #include "io.h" #include "shandsp.h" static struct McpReg *mcp = MCPREG; static struct SspReg *spi = SSPREG; static struct PpcReg *ppc = PPCREG; static struct GpioReg *gpio = GPIOREG; #define SSP_TNF 0x0002 #define SSP_RNE 0x0004 #define SSP_BSY 0x0008 #define SSP_TFS 0x0010 #define SSP_ROR 0x0020 char permBuffer[500]; enum{ Qdir, Qdata1, Qdata2, Qdata3, Qdata4, Qdata5, Qdata6, }; Dirtab spiDirtab[]={ "dsp2mp", {Qdata1, 0}, 0, 0666, "mp2dsp", {Qdata2, 0}, 0, 0666, "std2mp", {Qdata3, 0}, 0, 0666, "speakperm", {Qdata4, 0}, 0, 0666, "po2dsp", {Qdata5, 0}, 0, 0666, "touch2dsp", {Qdata6, 0}, 0, 0666, }; int spiFlag = 0; static int spiDead = 1; uchar speakerOption = 0; uchar cidOption = ALL_CID_OPTS_ON; uchar dialCont = DTMF_CONTU; uchar dialStop = DTMF_BURST; static ulong clkper; static ulong clkper1; typedef struct { Rendez; ulong v; } GpioRendez; static void gpioPinIntr(Ureg *, void *); static void gpioPinIntrEnable(int, GpioRendez *); static int gpioPinUp(void *); static int gpioPinDown(void *); static int readMG( uchar * ); static uchar sspMG( uchar ); int sspSF( uchar, int, int, void *, int ); static void initSPI( void ); void spiDSPreset( void ); static void spiIntr(Ureg *, void *); static void dspIngressTask( void * ); static void dspEgressTask( void * ); static void periodicTask( void * ); int inferno_time_has_changed; // To sync DSP time with Inferno time extern void dspClockSet (void); // EMI/ESD fix for codec corruption. extern void setbrightness (ushort b); extern ushort getbrightness (void); extern void telInit( void ); extern void cidControl( int len, uchar x1, uchar x2 ); extern void telCommand( uchar *a, int n ); extern void telMessage( uchar *a, int n ); extern void spi_tsync(void); Queue *dspQueue; Queue *tel2limQueue; Queue *tel2stdQueue; Queue *permQueue; Queue *cwiQueue; static QLock spiLock; static QLock SFLock; int sanityCounter = 0; static GpioRendez dinRendez; static GpioRendez rtsRendez; static Rendez spiRendez; static int spiROR; union { uchar array[8]; ulong ltime; } dspTime; static int returnBusy( void *b ) { int *p = b; if (p) return(*p); return(0); } static void spiBusyTest( void *a ) { uchar dspData; USED( a ); print("\nspiBusyTest"); //shared resource loop1: qlock(&spiLock); while( 0 != readMG( &dspData ) ) { } sspMG( DSP_SANITY_CMD ); delay( 200 ); if( 0 != readMG( &dspData ) ) { if( (uchar)dspData == (uchar)RSP_ACK_CMD ) { delay( 200 ); if( 0 != readMG( &dspData ) ) { if( (uchar)dspData == (uchar)RSP_SANITY_CMD ) { if( 0 != readMG( &dspData ) ) { if( 0 != (dspData & 0x04) ) { qunlock(&spiLock); tsleep( &up->sleep, return0, 0, 500 ); goto loop1; } } } } } } spiDead = 1; spiDSPreset(); qunlock(&spiLock); pexit( 0, 0 ); } static void spiIntr(Ureg *, void *) { spi->sscr1 = 0; spiROR |= spi->sssr & SSP_ROR; wakeup(&spiRendez); } static int spiReady(void *) { return(spi->sscr1 == 0); } static void shandspreset( void ) { // For black screen message to hang up handset extern int consoleprint; int saved_consoleprint; int OK_to_show_error_msg = 1; uchar dspData; clkper = US2TMR(5); clkper1 = US2TMR(1); //shared resource qlock(&spiLock); initSPI(); delay( 500 ); //empty the fifo loop2: while( 0 != readMG( &dspData ) ) { } //get the saved time memset(dspTime.array, 0, sizeof dspTime.array); sspMG( DSP_GETIME_CMD ); delay( 200 ); if( 0 != readMG( &dspData ) ) { readMG( &dspTime.array[0] ); readMG( &dspTime.array[1] ); readMG( &dspTime.array[2] ); readMG( &dspTime.array[3] ); //print("\n%lx",dspTime.ltime); } while( 0 != readMG( &dspData ) ) { } sspMG( DSP_SANITY_CMD ); delay( 200 ); if( 0 != readMG( &dspData ) ) { if( (uchar)dspData == (uchar)RSP_ACK_CMD ) { delay( 200 ); if( 0 != readMG( &dspData ) ) { if( (uchar)dspData == (uchar)RSP_SANITY_CMD ) { if( 0 != readMG( &dspData ) ) { if( 0 != (dspData & 0x04) ) { // Display the error message 1 time, and wait // for user to hang up the set if ( OK_to_show_error_msg ) { saved_consoleprint = consoleprint; consoleprint = 1; print ( " \n \n \n AC Power has returned. " ); print ( " \n Your IS2630 will return to normal operation after the receiver is hung up. " ); consoleprint = saved_consoleprint; OK_to_show_error_msg = 0; } //spiDead = 0; goto loop2; } } } } } } if( 0 != spiDead ) { spiDSPreset(); } gpioPinIntrEnable(SPI_DIN_v, &dinRendez); qunlock(&spiLock); } static void prtDSPcnts(Rune); static void togglespiflag(Rune) { spiFlag = !spiFlag; } static void shandspinit( void ) { //construct the queues dspQueue = qopen( 256, 0, 0, 0 ); if( dspQueue == 0 ) panic( "shandspinit" ); tel2limQueue = qopen( 256, 0, 0, 0 ); if( tel2limQueue == 0 ) panic( "shandspinit" ); tel2stdQueue = qopen( 256, 0, 0, 0 ); if( tel2stdQueue == 0 ) panic( "shandspinit" ); permQueue = qopen( 512, 0, 0, 0 ); if( permQueue == 0 ) panic( "shandspinit" ); cwiQueue = qopen( 256, 0, 0, 0 ); if( cwiQueue == 0 ) panic( "shandspinit" ); //create the tasks telInit(); kproc( "dspEgressTask", dspEgressTask, 0 , 0); kproc( "dspIngressTask", dspIngressTask, 0 , 0); kproc( "periodicTask", periodicTask, 0 , 0); if( 0 == spiDead )kproc( "spiBusyTest", spiBusyTest, 0, 0 ); debugkey('M', "spiFlag", togglespiflag, 0); debugkey('D', "dsp counts", prtDSPcnts, 0); } static Chan* shandspattach( char *spec ) { return( devattach( 'S', spec ) ); } static int shandspwalk( Chan *c, char *name ) { return( devwalk( c, name, spiDirtab, nelem( spiDirtab ), devgen ) ); } static void shandspstat( Chan *c, char *name ) { devstat( c, name, spiDirtab, nelem( spiDirtab ), devgen ); } static Chan * shandspopen( Chan* c, int omode ) { return( devopen( c, omode, spiDirtab, nelem( spiDirtab ), devgen ) ); } static void shandspclose( Chan* c ) { USED( c ); error( Eperm ); } static long shandspread( Chan* c, void* a, long n, ulong offset ) { USED( offset ); //check what is being read switch( c->qid.path & ~CHDIR ) { case Qdir: //directory n = devdirread( c, a, n, spiDirtab, nelem( spiDirtab ), devgen ); break; case Qdata1: //file n = qread( tel2limQueue, a, 64 ); break; case Qdata3: //file if( 0 == qcanread( tel2stdQueue ) ) { //nothing to read n = 0; } else { //there is queued data n = qread( tel2stdQueue, a, 64 ); } break; case Qdata4: //file n = qread( permQueue, a, 500 ); break; default: n = 0; break; } return n; } extern Rendez spkrRendez; extern int spkrBusy; static long shandspwrite( Chan* c, void *a, long n, ulong offset ) { USED( offset ); //check where the write is going switch( c->qid.path & ~CHDIR ) { case Qdata2: //file telCommand(a, n); break; case Qdata4: //file if( n == 500 )memmove( permBuffer, a, n ); break; case Qdata5: if( 0 == memcmp( "pulse", (char *)a, n ) ) { dialCont = PULSE_DIAL; dialStop = PULSE_DIAL; } else if( 0 == memcmp( "dtmf", (char *)a, n ) ) { dialCont = DTMF_CONTU; dialStop = DTMF_BURST; } else if( 0 == memcmp( "AutomaticSpeakerPhoneOn", (char *)a, n ) ) { speakerOption = 1; } else if( 0 == memcmp( "AutomaticSpeakerPhoneOff", (char *)a, n ) ) { speakerOption = 0; } else if( 0 == memcmp( "cidType2On", (char *)a, n ) ) { cidOption = ALL_CID_OPTS_ON; cidControl( 2, SET_CID_OPTIONS, cidOption ); } else if( 0 == memcmp( "cidType2Off", (char *)a, n ) ) { cidOption = ALL_CID_OPTS_MINUS_CIDCW; cidControl( 2, SET_CID_OPTIONS, cidOption ); } else if( 0 == memcmp( "clearStutter", (char *)a, n ) ) { extern uchar fskPresent; fskPresent = 0; } else if( 0 == memcmp( "clearMWI", (char *)a, n ) ) { extern uchar fskPresent; uchar foo[2]; foo[0] = RSP_CID_CMD; if( 0 != fskPresent ) { foo[1] = FSK_MSG_OFF; telMessage( foo, 2 ); } else { foo[1] = STUTTER_MSG_OFF; telMessage( foo, 2 ); } } else { } break; case Qdata6: if( 0 == memcmp( "beep", (char *)a, n ) ) { uchar foo[3]; //fire off a tone foo[0] = DSP_TONE_CMD; foo[1] = DEST_SP; foo[2] = SHORT_BEEP; qwrite( dspQueue, foo, 3 ); } else if( 0 == memcmp( "confirmation", (char *)a, n ) ) { uchar foo[3]; //fire off a tone foo[0] = DSP_TONE_CMD; foo[1] = DEST_SP; foo[2] = HAPPY_TONE; qwrite( dspQueue, foo, 3 ); } else if( 0 == memcmp( "error", (char *)a, n ) ) { uchar foo[3]; //fire off a tone foo[0] = DSP_TONE_CMD; foo[1] = DEST_SP; foo[2] = SAD_TONE; qwrite( dspQueue, foo, 3 ); } else { } break; default: error( Ebadusefd ); break; } return n; } void spiDSPreset( void ) { // Give DSP a resets. // Remove CTS. gpio->pinclear = DSP_RESET | SPI_CTS; // Hold reset low if (up) tsleep(&up->sleep, return0, 0, 500); else delay( 500 ); // Remove reset gpio->pinset = DSP_RESET; // Give DSP time to wake if (up) tsleep(&up->sleep, return0, 0, 500); else delay( 500 ); } static uchar sspMG( uchar val ) { uchar rd_val = 0; int ii; ulong t; gpio->pinclear = WHEATIES; // Select Device. // get some setup time if (val & 0x80) gpio->pinset = SPI_DOUT; else gpio->pinclear = SPI_DOUT; t = timer_start() + clkper1; for (ii = 8; ii > 0; ii--) { int x; rd_val <<= 1; while ((int)(t - timer_start()) > 0); x = splhi(); gpio->pinset = SPI_SCLK; t = timer_start() + clkper; if (gpio->pinlevel & SPI_DIN) rd_val |= 1; splx(x); while ((int)(t - timer_start()) > 0); x = splhi(); val <<= 1; if (val & 0x80) gpio->pinset = SPI_DOUT; else gpio->pinclear = SPI_DOUT; gpio->pinclear = SPI_SCLK; t = timer_start() + clkper; splx(x); } gpio->pinset = WHEATIES; // Remove CS. return( rd_val ); } static void gpioPinIntrEnable(int v, GpioRendez *gr) { gr->v = 1 << v; intrenable(v, gpioPinIntr, gr, BusGPIO); } static void gpioPinIntr(Ureg *ur, void *a) { ulong v = ((GpioRendez *)a)->v; USED(ur); gpio->pineds = v; gpio->pinred &= ~v; gpio->pinfed &= ~v; wakeup (a); } static int gpioPinUp(void *a) { return ((gpio->pinlevel & (ulong)a) != 0); } static int gpioPinDown(void *a) { return ((gpio->pinlevel & (ulong)a) == 0); } /* * Drive the SPI pins through the GPIO register at the best possible rate. * Send or receive up to 'n'=32 bits per call. Can also send more than 32 * bits (0's), but only the last 32 bits received can be returned. * * 'out' must be left justified. */ static ulong sspSFio(ulong out, int n) { ulong in; // get some setup time if ((int)out < 0) gpio->pinset = SPI_DOUT; else gpio->pinclear = SPI_DOUT; in = 0; /* * Each interation cycles through a period of SCLK. * Interrupts are inhibited briefly to synchronize * data with clock. */ for ( ; n > 0; n--) { int x; in <<= 1; x = splhi(); spi_tsync(); gpio->pinset = SPI_SCLK; if (gpio->pinlevel & SPI_DIN) in |= 1; splx(x); out <<= 1; x = splhi(); spi_tsync(); if ((long)out < 0) gpio->pinset = SPI_DOUT; else gpio->pinclear = SPI_DOUT; gpio->pinclear = SPI_SCLK; splx(x); } return(in); } /* * Check status of the serial flash. Return 1 when non-busy. * Wait up to one second. */ static int waitSF(int cmp) { int ret; qlock(&spiLock); // Select Device. gpio->pinclear = EE_CS; if (waserror()) { // Remove CS. gpio->pinset = EE_CS; qunlock(&spiLock); nexterror(); } // Send out for the status register sspSFio(0x57<<24, 9); /* * Serial flash is now updating our SPI input pin with its * status. Wait for it to go high (non-busy). */ if ( !(gpio->pinlevel & SPI_DIN) ) { int x; /* * Enable interrupt and wait for it to wake us. */ x = splhi(); gpio->pinred |= SPI_DIN; gpio->pineds &= ~SPI_DIN; splx(x); tsleep( &dinRendez, gpioPinUp, (void *)dinRendez.v, 1000); ret = (gpio->pinlevel & SPI_DIN) != 0; } else ret = 1; if (ret && cmp) { /* check "COMP" in the status register */ ret = ((sspSFio(0, 8) & 0x40) == 0); } poperror(); // Remove CS. gpio->pinset = EE_CS; qunlock(&spiLock); return(ret); } static int sspSFcmd( uchar cmd, int pga, int bya, void *a, int cnt ) { int i, j, returnValue, kerr; ulong word1; uchar *data = (uchar *)a; if ( ((ulong)pga >= SFLASH_NUM_SECTORS) || ((ulong)bya >= SFLASH_SECTOR_SIZE) || ((ulong)(bya + cnt) > SFLASH_SECTOR_SIZE) ) { print("Bad SF params: %d %d %d\n", pga, bya, cnt); return(0); } qlock(&spiLock); // Select Device. gpio->pinclear = EE_CS; if (waserror()) { // Remove CS. gpio->pinset = EE_CS; qunlock(&spiLock); nexterror(); } word1 = (cmd << 24) | (pga << 9) | bya; switch( cmd ) { case MAIN_MEMORY_PAGE_READ: case BUFFER1_READ: case BUFFER2_READ: sspSFio(word1, 32); /* * Send the "don't care" bits. */ if (cmd == MAIN_MEMORY_PAGE_READ) sspSFio(0, 32 + 1); else sspSFio(0, 8 + 1); /* * Transfer the bulk of the data using the spi fifos */ spi->sscr0 = SPICTRL0_SF; gpio->altfunc |= SPI_CTRL; // SPI to drive gpio i = 0; j = 0; kerr = 0; while (waserror()) /* for sleep() below */ kerr = 1; while (1) { int sssr; sssr = spi->sssr; if (sssr & SSP_RNE) { //receive buffer has data data[j] = spi->ssdr; if (++j >= cnt) break; } else if ( (sssr & SSP_TNF) && i < cnt ) { //transfer buffer is not full spi->ssdr = 0; i++; } else if (i < cnt) { // enable interrupts spi->sscr1 = 0x3; sleep(&spiRendez, spiReady, 0); } } spi->sscr0 = 0; gpio->altfunc &= ~SPI_CTRL; // SPI not to drive gpio poperror(); if (kerr) nexterror(); returnValue = j; break; case MAIN_PROGRAM_ERASE: case BUFFER1_WRITE: case BUFFER2_WRITE: // Send out the write command sspSFio(word1, 32); /* * Transfer the bulk of the data using the spi fifos */ spi->sscr0 = SPICTRL0_SF; gpio->altfunc |= SPI_CTRL; // SPI to drive gpio i = 0; j = 0; kerr = 0; while (waserror()) /* for sleep() below */ kerr = 1; while (1) { int sssr; sssr = spi->sssr; if (sssr & SSP_RNE) { int rd_val; //receive buffer has data rd_val = spi->ssdr; USED(rd_val); if (++j >= cnt) break; } else if ( (sssr & SSP_TNF) && i < cnt ) { //transfer buffer is not full spi->ssdr = data[i++] << 8; } else if (i < cnt) { // enable interrupts spi->sscr1 = 0x3; sleep(&spiRendez, spiReady, 0); } } spi->sscr0 = 0; gpio->altfunc &= ~SPI_CTRL; // SPI not to drive gpio poperror(); if (kerr) nexterror(); returnValue = j; break; case COMPARE_BUFFER1: case COMPARE_BUFFER2: // Send the command - someone else waits for result case BUFFER1_PROGRAM_ERASE: case BUFFER2_PROGRAM_ERASE: case BUFFER1_PROGRAM_NOERASE: case BUFFER2_PROGRAM_NOERASE: case BUFFER1_AUTO_REWRITE: case BUFFER2_AUTO_REWRITE: case MAIN_PAGE_TO_BUFFER1: case MAIN_PAGE_TO_BUFFER2: // Send the command sspSFio(word1, 32); returnValue = 1; break; default: if( spiFlag )print("\nsspSF COMMAND FAIL"); returnValue = 0; break; } poperror(); // Remove CS. gpio->pinset = EE_CS; qunlock(&spiLock); return( returnValue ); } int sspSF( uchar cmd, int pga, int bya, void *data, int cnt ) { int returnValue; int pri; pri = setpri(PriLocodec); qlock(&SFLock); if (waserror()) { qunlock(&SFLock); setpri(pri); nexterror(); } if (!waitSF(0)) { poperror(); qunlock(&SFLock); setpri(pri); return(0); } returnValue = sspSFcmd(cmd, pga, bya, data, cnt); if (returnValue > 0) { switch(cmd) { case MAIN_PROGRAM_ERASE: if (!waitSF(0)) returnValue = 0; break; case COMPARE_BUFFER1: case COMPARE_BUFFER2: if (!waitSF(1)) returnValue = 0; break; } } poperror(); qunlock(&SFLock); setpri(pri); return(returnValue); } static int prtMGlen = 0; static void prtMGio(uchar c, int to) { prtMGlen += print( to ? ",%ux" : "'%ux", c); if (prtMGlen >= 70) { prtMGlen = 0; print("\n"); } } static void prtMGeom(int to) { prtMGlen += print(to ? "/" : "\\"); if (prtMGlen > 10) { prtMGlen = 0; print("\n"); } } static int readMG( uchar *Rd_Dat ) { //test for no data if( 0 != (gpio->pinlevel & SPI_RTSN) ) return( 0 ); // Generate CTS. gpio->pinset = SPI_CTS; // Wait for RTS ack. if(timer_devwait(&gpio->pinlevel, SPI_RTSN, SPI_RTSN, MS2TMR(2)) < 0) { spiDSPreset(); print("readMG: reset\n"); return( 0 ); } // Get data. *Rd_Dat = sspMG( 0x00 ); //delay microdelay( 200 ); // Remove CTS. gpio->pinclear = SPI_CTS; /* * Delay until next word of message appears, but no longer * than 500 microseconds. * THIS IS UNRELIABLE -- since another task or interrupt * routine could take us away before 500usec elapses. * Not knowing that, we might think we are getting more * of the same message. * (this should be handled by parsing messages in the future) * * Actually, this has now been changed so that it will check * the device one more time after the timeout, to see if * there is a message waiting (timer_devwait does this) * -- can the return value of timer_devwait be used to * handle this situation properly, since -1 means timeout, * and 0 means the device responded (and interruptions * are properly handled) */ timer_devwait(&gpio->pinlevel, SPI_RTSN, 0, US2TMR(500)); return( 1 ); } static void initSPI(void) { ulong tmp; tmp = gpio->pindir; tmp |= (CODEC_RESET | DSP_RESET); // Make reset lines outputs. tmp |= SPI_CTRL; // Set Control directions. tmp |= (SPI_CS | SPI_CTS); // Make CS and CTS pins outputs. tmp &= ~(SPI_RTSN | SPI_DIN); // RTS and DIN are inputs. gpio->altfunc &= ~SPI_CTRL; // SPI not to drive gpio gpio->pinclear = SPI_CTRL; // SPI signals all low gpio->pinset = SPI_CS; // Remove all serial CS's. gpio->pinclear = SPI_CTS; // CTS idles low. gpio->pindir = tmp; gpio->pinset = DSP_RESET; ppc->ppar = (1 << PPC_V_SPR); // SPI out alternate pins. spi->sscr0 = 0; spi->sscr1 = 0; mcp->mccr = MCPCTRL; // Turn on the MCP because of chip bug. intrenable(19, spiIntr, 0, BusCPU); } static int dspCmdcnt[256]; static int dspRspcnt[256]; static int dspRsplen[256]; static void prtDSPcnts(Rune) { int ll; int ii; int total; ll = print("cmds:"); total = 0; for (ii = 0; ii < 256; ii++) { int cnt; if ((cnt = dspCmdcnt[ii]) != 0) { total += cnt; ll += print(" %X:%d", ii, cnt); if (ll > 70) { print("\n"); ll = print(" "); } } } print(" total: %d\n", total); ll = print("resps:"); total = 0; for (ii = 0; ii < 256; ii++) { int len, cnt; len = dspRsplen[ii]; if ((cnt = dspRspcnt[ii]) != 0) { int avg, rem; total += cnt; ll += print(" %X:%d", ii, cnt); avg = (len+cnt/20)/cnt; rem = len%cnt; ll += print("/%d", avg); if (rem) ll += print(".%d", ((10*rem+cnt/2)/cnt)%10); if (ll > 64) { print("\n"); ll = print(" "); } } } print(" total: %d\n", total); } static void dspIngressTask( void *a ) { uchar foo[64]; int i; int expect; USED( a ); gpioPinIntrEnable(SPI_RTSN_v, &rtsRendez); //set a higher priority setpri(PriLocodec); for(;;) { if( spiFlag > 1 )print("\nspiTask rts = %lux ", (gpio->pinlevel & SPI_RTSN)); //test for data coming from mongoose if( 0 == (gpio->pinlevel & SPI_RTSN) ) { if( spiFlag > 1 )print("\nspiTask rts"); qlock(&spiLock); //extract the message xxx: expect = 64; for( i = 0; i < expect; i++ ) { //check for data if( 0 == readMG( &foo[i] ) ) { break; } if ( spiFlag ) prtMGio(foo[i], 0); //look at first byte if( i == 0 ) { //determine actual length switch( foo[0] ) { //filter out these responses case RSP_ACK_CMD: case RSP_BAD_CMD: dspRspcnt[foo[0]]++; dspRsplen[foo[0]]++; goto xxx; case SPKPH_STATES_DATA: case SPKPH_PARAMS_DATA: expect = 22; break; case 0x01: case KEYPRESS: case RSP_PARALLEL_SET_DETECT_CMD: case RSP_BUSY_CMD: case RSP_LIU_CMD: case 0xf7: expect = 2; break; case RSP_SANITY_CMD: case RSP_BATT_STATUS_CMD: expect = 3; break; default: break; } } } qunlock(&spiLock); if ( spiFlag ) prtMGeom(0); //test for null message if( 0 != i && 0 != spiDead ) { dspRspcnt[foo[0]]++; dspRsplen[foo[0]] += i; telMessage(foo, i); } } else { int x; x = splhi(); gpio->pinfed |= SPI_RTSN; splx(x); //wait for rts to happen sleep( &rtsRendez, gpioPinDown, (void *)rtsRendez.v); } } } static void dspEgressTask( void *a ) { uchar foo[64]; long byteCount; USED( a ); //set a higher priority setpri(PriLocodec); for(;;) { int i; //get a command to be sent to the dsp byteCount = qread( dspQueue, foo, sizeof(foo) ); dspCmdcnt[foo[0]]++; //configure for mongoose //////////////////////// qlock(&spiLock); //feed the mongoose one byte at a time for (i = 0; i < byteCount; i++) { if( spiFlag )prtMGio(foo[i], 1); // Send data. if( 0 != spiDead ) sspMG( foo[i] ); //Give DSP time between bytes. microdelay( 100 ); } qunlock(&spiLock); if ( spiFlag ) prtMGeom(1); //Give DSP time between commands. if ( (byteCount > 0) && ( (foo[0] == READ_STATES_DATA_CMD) || (foo[0] == READ_PARAMS_DATA_CMD) ) ) { /* wait until speaker data flow is done */ tsleep(&spkrRendez, returnBusy, &spkrBusy, 1000); } else { tsleep( &up->sleep, return0, 0, 10); } } } static void periodicTask(void *a) { int timeCorrection = 0; uchar foo[1]; int batteryLoop = 540; //set a higher priority setpri(PriLocodec); USED(a); for(;;) { //time correction if( timeCorrection++ == 5 ) { foo[0] = DSP_GETIME_CMD; qwrite( dspQueue, foo, 1 ); } // Check if user has manually changed the time if ( inferno_time_has_changed ) { dspClockSet(); inferno_time_has_changed = 0; } //test if battery should be checked // Note - time is in seconds, so this is around 10 minutes if( ++batteryLoop > 600 ) { //battery test batteryLoop = 0; foo[0] = DSP_BATT_STATUS_CMD; } else { // ESD/EMI fix for codec corruption // Just keep "refreshing" the brightness whenever // we send a sanity message. ushort current_brightness; current_brightness = getbrightness(); setbrightness(current_brightness); //do a sanity update foo[0] = DSP_SANITY_CMD; } qwrite( dspQueue, foo, 1 ); //test if there were any responses if( ++sanityCounter > 15 ) { //dsp is not responding sanityCounter = 0; if( 0 != spiDead )spiDSPreset(); } tsleep( &up->sleep, return0, 0, 1000 ); } } Dev shandspdevtab = { 'S', "spi", shandspreset, shandspinit, shandspattach, devdetach, devclone, shandspwalk, shandspstat, shandspopen, devcreate, shandspclose, shandspread, devbread, shandspwrite, devbwrite, devremove, devwstat, };