#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 "shandsp.h" //jrc - MR improvement - use this for frequency of low battery and // ringer off messages extern ushort getbrightness (void); // From screen.c file extern int spiFlag; extern void spiDSPreset( void ); extern Queue *tel2limQueue; extern Queue *tel2stdQueue; extern Queue *dspQueue; extern Queue *cwiQueue; static Queue *telQueue; static Queue *telMsgQueue; Rendez spkrRendez; int spkrBusy; // jrc - Following variable is used to correct a "feature" (i.e., bug) // in DSP when dialing a stream of digits, and a key is pressed. uchar temp_dial_mode; // jrc - Used to control frequency of battery test uchar screen_was_idle = 1; // Initialize to true so that on power up, // low batt message will get displayed // jrc - Used for modem/phone override control uchar modem_override_pending = 0; uchar expected_mode_after_override = ONHOOK; extern uchar dialCont; extern uchar dialStop; extern uchar cidOption; extern uchar speakerOption; extern int sanityCounter; int audioPath = 0; // NOTE - this is a slightly misleading name. This variable is used by // the software to determine state transitions, and is NOT used // with the DSP Audio Path Command (0xA6), which takes a parameter // "mode", which sets up the Audio path. int muteState = 0; int holdState = 0; int modemState = 0; int priority_ring_state = 0; // Default to normal, non-priority ring uchar volSpeaker = 4; // Default speaker volume level ( range is [0,7] ) uchar volRinger = 4; // Default ringer volume level ( range is [0,7] ) uchar volCord = 1; // Default handset volume level ( range is [0,3] ) uchar cidFlag = 0; uchar cidHour = 0; uchar cidMinute = 0; uchar lineInUse = 0; int newCallState = 0; int newNoteState = 0; // // Following variables are used for CO mailbox message detection. The // CO can send a "command" in either audio mode (stutter dial tone) or // in visual mode (fsk). Whenever a "command" is received in fsk mode, // we (the phone) automatically disable stutter detection. // uchar fskPresent = 0; // 0 = stutter mode, 1 = fsk mode int fskState = 0; // 0 = no messages, 1 = message(s) in CO mailbox int stutterState = 0; // 0 = no messages, 1 = message(s) in CO mailbox enum { CLEARALL, RELAY, SPEAKER, MUTE, LINE1, NEWCALL, NEWNOTE, PARALLEL }; enum { RING_CWI, LIU_CWI, LNIU_CWI, DONE_CWI }; struct { // message identifier for limbo application uchar cid; // New for DDN uchar DDN_number; uchar call_qualifier; // date uchar month1; uchar month2; uchar slash; uchar day1; uchar day2; uchar comma1; // time uchar hour1; uchar hour2; uchar colon; uchar minute1; uchar minute2; uchar ampm[2]; uchar comma2; // Before DDN // // NOTE - need to maintain backward compatibility to this format for // sets already in the field // // // number // uchar area[3]; // uchar dash1; // uchar co[3]; // uchar dash2; // uchar num[4]; // // After DDN - need to support up to 12 digits (for DDN), plus // hyphenation. Format is: xx-xxx-xxx-xxxx uchar num[15]; uchar comma3; // name uchar name[16]; } cidStuff; static void dspToneCmd( uchar dest, uchar tone ) { uchar foo[3]; //fire off a tone foo[0] = DSP_TONE_CMD; foo[1] = dest; foo[2] = tone; //DEBUG ONLY //print ( " dsp TONE dest = %X, tone = %X \n ", dest, tone ); qwrite( dspQueue, foo, 3 ); } void beep( void ) { dspToneCmd( DEST_SP, SHORT_BEEP ); } static void dspVolumeCmd( uchar dest, uchar volume ) { uchar foo[2]; //volume foo[0] = DSP_VOL_CMD; foo[1] = dest | volume; //DEBUG ONLY //print ( " VOLUME dest = %X, volume = %X \n ", dest, volume ); qwrite( dspQueue, foo, 2 ); } static void dspDialCmd( uchar mode, uchar digit ) { uchar foo[2]; //dial foo[0] = DSP_DIAL_CMD; foo[1] = mode | digit; // jrc -DEBUG ONLY // print ( " dsp DIAL %X \n ", digit ); qwrite( dspQueue, foo, 2 ); } static void dspLineCtl( uchar mode ) { uchar foo[2]; //on and off hook control foo[0] = DSP_LINE_CTL; foo[1] = mode; qwrite( dspQueue, foo, 2 ); } static void dspAudioCmd( uchar path ) { uchar foo[2]; foo[0] = DSP_AUDIO_CMD; foo[1] = path; // jrc - DEBUG ONLY //print ( "dsp AUDIO path is %X \n ", path ); qwrite( dspQueue, foo, 2 ); } static void getDspSt( void ) { uchar foo[1]; tsleep( &up->sleep, return0, 0, 100 ); foo[0] = READ_STATES_DATA_CMD; // jrc debug only // print ( "\n getDSP 0x1a - GET STATES "); if (spiFlag) print("getDspSt spkrBusy\n"); spkrBusy = 1; qwrite( dspQueue, foo, 1 ); } static void getDspPp( void ) { uchar foo[1]; tsleep( &up->sleep, return0, 0, 100 ); foo[0] = READ_PARAMS_DATA_CMD; // jrc debug only // print (" \n getDSP 0x1b - GET PARAMS "); if (spiFlag) print("getDspPp spkrBusy\n"); spkrBusy = 1; qwrite( dspQueue, foo, 1 ); } static void putDspStPp( void ) { extern char permBuffer[]; int i; char foo[22]; foo[0] = SPKPH_PARAMS_DATA; for( i = 0; i < 5; ++i ) { foo[1] = (char)i; //print("\nputDsp %x %x ",foo[0], foo[1]); memmove(foo+2, permBuffer+20*i, 20); qwrite( dspQueue, foo, 22 ); } foo[0] = SPKPH_STATES_DATA; for( i = 0; i < 20; i++ ) { foo[1] = i; //print("\nputDsp %x %x ",foo[0], foo[1]); memmove(foo+2, permBuffer+100+20*i, 20); qwrite( dspQueue, foo, 22 ); } } static void lineStatus( void ) { uchar foo[1]; foo[0] = DSP_GET_LINE_STAT_CMD; qwrite( dspQueue, foo, 1 ); } static void parallelDetect( void ) { uchar foo[1]; foo[0] = DSP_PARALLEL_SET_DETECT_CMD; qwrite( dspQueue, foo, 1 ); } static void stutterDetectEnable( void ) { uchar foo[2]; foo[0] = DSP_CID_CMD; foo[1] = START_STUTTER_DET; qwrite( dspQueue, foo, 2 ); } static void cwiStateChange( uchar x ) { qwrite( cwiQueue, &x, 1 ); } void cidControl( int len, uchar x1, uchar x2 ) { uchar foo[3]; foo[0] = DSP_CID_CMD; foo[1] = x1; foo[2] = x2; qwrite( dspQueue, foo, (len + 1) ); } static void cidInit( void ) { cidStuff.cid = K2A_INCOMING_CID_MSG; // New for DDN cidStuff.DDN_number = 'N'; // Default to NOT a DDN number cidStuff.call_qualifier = ' '; // Default to NO call qualifier cidStuff.month1 = '0'; cidStuff.month2 = '0'; cidStuff.slash = '/'; cidStuff.day1 = '0'; cidStuff.day2 = '0'; cidStuff.comma1 = ','; cidStuff.hour1 = '0'; cidStuff.hour2 = '0'; cidStuff.colon = ':'; cidStuff.minute1 = '0'; cidStuff.minute2 = '0'; cidStuff.ampm[0] = 'a'; cidStuff.ampm[1] = 'm'; cidStuff.comma2 = ','; // Before DDN // cidStuff.area[0] = '#'; // cidStuff.area[1] = '#'; // cidStuff.area[2] = '#'; // cidStuff.dash1 = '-'; // cidStuff.co[0] = '#'; // cidStuff.co[1] = '#'; // cidStuff.co[2] = '#'; // cidStuff.dash2 = '-'; // cidStuff.num[0] = '#'; // cidStuff.num[1] = '#'; // cidStuff.num[2] = '#'; // cidStuff.num[3] = '#'; // Populate with blanks. Algorithm below will insert digits. memset (cidStuff.num, ' ' , sizeof cidStuff.num); cidStuff.comma3 = ','; // 1 15 memmove(cidStuff.name, "***************", sizeof cidStuff.name - 1); cidStuff.name[15] = 0; } /* END cidInit */ static void cidProcess( uchar type, uchar *hexArray ) { // DDN - handle up to 12 digit numbers from DSP uchar dstring[12]; int i; uchar c; // jrc - debug only // { // int i; // print ( "\n Hex Array is : "); // for ( i = 0; i < 16; i++ ) // print ( "%X ",hexArray[i] ); // } switch( type ) { case CID1: if ( spiFlag )print("\nCID1"); // // NOTE - hexArray contains the 16 data bytes returned by the DSP // containing assorted CID data, shown below. // hexArray[0] - Flags (new for DDN support) // bit 7 - DDN detected // 0 = No DDN detected // 1 = Yes, DDN was detected // bit 6 - Call Qualifier detected // 0 = No Call Qualifier detected (next byte will be all ones // 1 = Yes, Call Qualifier was detected, next byte is the qualifier // hexArray[1] - Call Qualifer - the ASCII letter qualification // 0xFF = No call qualifier // Other Possible values are: // 0x4C = L (long distance call) // hexArray[2] - Special Condition byte - if least significant // bit is set, a parallel set was offhook, and // no CID handshake took place // hexArray[3] - Always 0x00 - Unused - Ignore (call index) // hexArray[4-5] - date (packed BCD) // hexArray[6-7] - time (packed BCD) // hexArray[8-15] - number (packed BCD) //ignore cid data if parallel set is detected if ( 0 != audioPath && 0 != (hexArray[2] & 1) ) { cidInit(); // Send special message to Limbo, which puts up friendly msg memmove (cidStuff.num, "PARALLEL " , sizeof cidStuff.num ); memset (cidStuff.name, ' ' , sizeof cidStuff.name); qwrite( tel2limQueue, &cidStuff.cid, sizeof( cidStuff ) ); // jrc - DEBUG only // { // char s[sizeof(cidStuff) + 1]; // memmove(s, &cidStuff, sizeof (cidStuff) ); // s[sizeof(cidStuff)] = 0; // print(" \n Send CID to Limbo: %s %d ", s, sizeof(cidStuff) ); // } return; } cidControl( 1, GETCID2, 0 ); // New for DDN - check bit 7 for DDN number detected if ( ( hexArray[0] & 0x80) == 0x80 ) { //print ( " \n YES, it is a DDN number "); cidStuff.DDN_number = 'Y'; } else { //print ( " \n NO, it is NOT a DDN number "); cidStuff.DDN_number = 'N'; } // New for DDN - check bit 6 for call qualifier if ( ( hexArray[0] & 0x40) == 0x40 ) { //print ( " \n YES, Call Qualifier is present "); cidStuff.call_qualifier = hexArray[1]; } else { //print ( " \n NO, call qualifer NOT present "); cidStuff.call_qualifier = ' '; } // Extract date and time cidStuff.month1 = '0' + ((hexArray[4] & 0xf0) >>4); cidStuff.month2 = '0' + ((hexArray[4] & 0x0f) >>0); cidStuff.day1 = '0' + ((hexArray[5] & 0xf0) >>4); cidStuff.day2 = '0' + ((hexArray[5] & 0x0f) >>0); cidStuff.hour1 = '0' + ((hexArray[6] & 0xf0) >>4); cidStuff.hour2 = '0' + ((hexArray[6] & 0x0f) >>0); cidStuff.minute1 = '0' + ((hexArray[7] & 0xf0) >>4); cidStuff.minute2 = '0' + ((hexArray[7] & 0x0f) >>0); cidHour = ((cidStuff.hour1 - '0') * 10) + (cidStuff.hour2 - '0'); cidMinute = ((cidStuff.minute1 - '0') * 10) + (cidStuff.minute2 - '0'); //adjust am and pm if ( cidHour == 0 ) { cidStuff.hour1 = '1'; cidStuff.hour2 = '2'; } else if ( cidHour >= 12 ) { cidStuff.ampm[0] = 'p'; if ( cidHour > 12 ) { cidStuff.hour1 = (cidHour - 12) / 10 + '0'; cidStuff.hour2 = (cidHour - 12) % 10 + '0'; } } // Extract the number (convert packed BCD to bytes) for (i = 0; i < 6; i++) { c = hexArray[8+i]; dstring[2*i] = c >> 4; dstring[2*i+1] = c & 0xf; } // Check for Private Number (0x0A by DSP Spec) if ( dstring[0] == 0x0A ) { memmove (cidStuff.num, "PRIVATE NUM " , sizeof cidStuff.num); } // Check for Out of area Number (0x0B by DSP Spec) else if ( dstring[0] == 0x0B ) { memmove (cidStuff.num, "UNKNOWN NUM " , sizeof cidStuff.num); } else { /* Count consecutive digits less than '10' and convert to ascii */ for (i = 0; i < 12; i++) { if (dstring[i] >= 0xa) break; dstring[i] += '0'; } if (i > 0) { // jrc - DEBUG ONLY // int k; // print ( "\n number of digits is %d, data is ", i); // for ( k = 0; k < i; k++ ) print ( "%c", dstring[k] ); // Format a 12 digit number as follows: xx-xxx-xxx-xxxx if ( i <= 4 ) { // Slide digit string to right end of number buffer. memmove(cidStuff.num + sizeof cidStuff.num - i, dstring, i); } else if ( i <= 7 ) { // Move 4 digits, insert a dash, and then get rest of digits memmove(cidStuff.num + sizeof cidStuff.num - 4, dstring + i - 4, 4); cidStuff.num[sizeof cidStuff.num - (4+1)] = '-'; memmove(cidStuff.num + sizeof cidStuff.num - (i+1), dstring, i - 4); } else if ( i <= 10 ) { // Move 4 digits, insert dash, move 3 digits, insert dash, move rest memmove(cidStuff.num + sizeof cidStuff.num - 4, dstring + i - 4, 4); cidStuff.num[sizeof cidStuff.num - (4+1)] = '-'; memmove(cidStuff.num + sizeof cidStuff.num - (7+1), dstring + i - 7, 3); cidStuff.num[sizeof cidStuff.num - (4+1+3+1)] = '-'; memmove(cidStuff.num + sizeof cidStuff.num - (i+2) , dstring, i - 7); } else if ( i <= 12 ) { // Move 4 digits, insert dash, move 3 digits, insert dash, move 3 digits, insert dash, move rest memmove(cidStuff.num + sizeof cidStuff.num - 4, dstring + i - 4, 4); cidStuff.num[sizeof cidStuff.num - (4+1)] = '-'; memmove(cidStuff.num + sizeof cidStuff.num - (7+1), dstring + i - 7, 3); cidStuff.num[sizeof cidStuff.num - (4+1+3+1)] = '-'; memmove(cidStuff.num + sizeof cidStuff.num - (10+2) , dstring + i - 10, 3); cidStuff.num[sizeof cidStuff.num - (4+1+3+1+3+1)] = '-'; memmove(cidStuff.num + sizeof cidStuff.num - (i+3), dstring, i - 10); } } else // i = 0, so no number was given, return appropriate message { //memset (cidStuff.area, ' ' , sizeofcidStuff.num); memmove (cidStuff.num, "NO DATA FROM CO" , sizeof cidStuff.num ); } } break; case CID2: if ( spiFlag )print("\nCID2"); // // NOTE - hexArray contains the 16 data bytes returned by the DSP // containing the CID name. Since the maximum name size is // 15 characters (by BellCore spec), the first data byte // (hexArray[0]) is unused and should be ignored (by DSP // spec). So the name field runs from hexArray[1] thru // hexArray[15]. // Check for Private Name ( P in ASCII table ) if ( hexArray[1] == 0x10 ) { if ( 0 != memcmp(cidStuff.num, "PRIVATE NUM " , sizeof cidStuff.num) ) { // 1.............16 memmove(cidStuff.name, "PRIVATE NAME ", sizeof cidStuff.name); } else // Name and Number are both private, remove duplicate message { memmove (cidStuff.num, "PRIVATE CALL " , sizeof cidStuff.num); memset (cidStuff.name, ' ' , sizeof cidStuff.name); } } // Check for Out of Area Name ( O in ASCII table ) else if ( hexArray[1] == 0x0f ) { if ( 0 != memcmp(cidStuff.num, "UNKNOWN NUM " , sizeof cidStuff.num) ) { // 1.............16 memmove(cidStuff.name, "UNKNOWN NAME ", sizeof cidStuff.name); } else // Name and Number are both unknown, remove duplicate message { memmove (cidStuff.num, "UNKNOWN CALL " , sizeof cidStuff.num); memset (cidStuff.name, ' ' , sizeof cidStuff.name); } } else // Put Name into CID buffer, padding with blanks as necessary. // Terminator is D in ASCII table (i.e., EOT). { i = 0; while ( ( i < 15 ) && ( hexArray[i + 1] != 0x04 ) ) { cidStuff.name[i] = hexArray[i + 1]; i = i + 1; } for ( ; i < 15; i++ ) cidStuff.name[i] = ' '; } qwrite( tel2limQueue, &cidStuff.cid, sizeof( cidStuff ) ); // jrc - DEBUG only // { // char s[sizeof(cidStuff) + 1]; // memmove(s, &cidStuff, sizeof (cidStuff) ); // s[sizeof(cidStuff)] = 0; // print(" \n Send CID to Limbo: %s %d ", s, sizeof(cidStuff) ); // } cidInit(); //fix the clock cidFlag = 1; c = DSP_GETIME_CMD; qwrite( dspQueue, &c, 1 ); break; default: break; } /* END switch(type) */ } /* END cidProcess */ static void ledControl( uchar led, uchar state ) { static struct { uchar command; uchar fandbRate; //flash and blink rate uchar ledcba9; //nil, nil, nil, nil uchar led8765; //nil, nil, newcall1, newcall2 uchar led4321; //speaker, mute, line1, relay uchar line1; //line1 uchar parallel; //parallel } ledBits = {0,0,0,0,0,0,0}; switch( led ) { case CLEARALL: ledBits.ledcba9 = 0; //ledBits.led8765 = 0; ledBits.led4321 &= 0x0c; ledBits.line1 = 0; break; case SPEAKER: ledBits.led4321 = (ledBits.led4321 & 0x3f) | (state <<6); break; case MUTE: ledBits.led4321 = (ledBits.led4321 & 0xcf) | (state <<4); break; case LINE1: ledBits.line1 = state; break; case PARALLEL: ledBits.parallel = state; break; case RELAY: ledBits.led4321 = (ledBits.led4321 & 0xfc) | (state <<0); break; case NEWCALL: ledBits.led8765 = (ledBits.led8765 & 0xf3) | (state <<2); break; default: break; } ledBits.led4321 = (ledBits.led4321 & 0xf3) | (ledBits.line1 <<2); if ( ledBits.line1 == 0 ) { ledBits.led4321 |= ledBits.parallel <<2; } ledBits.command = DSP_LED_CMD; ledBits.fandbRate = (FLASHRATE500 <<4) | BLINKRATE500; //jrc - DEBUG ONLY // print ("\n LED CONTROL is %X, %X, %X, %X, %X ", // ledBits.command, ledBits.fandbRate, ledBits.ledcba9, // ledBits.led8765, ledBits.led4321); qwrite( dspQueue, (uchar *)&ledBits, 5 ); } void setNewcallLed(void) { if ( newNoteState != 0 || fskState != 0 || stutterState != 0 ) { ledControl( NEWCALL, 0x01 ); } else if ( newCallState != 0 ) { ledControl( NEWCALL, 0x03 ); } else { ledControl( NEWCALL, 0x00 ); } } // This routine initializes the DSP clock when the DSP reports that the // time is bad. void dspClockInit ( void ) { uchar foo[9]; extern union { uchar array[8]; ulong ltime; }dspTime; union { uchar b[4]; long l; }t; //get the limbo time t.l = seconds(); // jrc - debug only // print ( "\n In DSP CLOCK INIT, bad DSP time is %X ", dspTime.ltime ); // print ( "\n In DSP CLOCK INIT, default limbo time is %X ", t.l ); if ( dspTime.ltime > t.l ) t.l = dspTime.ltime; // jrc - debug only // print ( "\n In DSP CLOCK INIT, setting time to %X ", t.l ); //set the dsp time foo[0] = DSP_SETIME_CMD; foo[1] = t.b[0]; foo[2] = t.b[1]; foo[3] = t.b[2]; foo[4] = t.b[3]; foo[5] = 0; foo[6] = 0; foo[7] = 0; foo[8] = 0; qwrite( dspQueue, foo, 5 ); } // This routine sets the DSP clock to the value specified by a user. // This is used to coordinate time between Limbo and DSP. Whenever the // Limbo time is updated (by a write to /dev/time - the console device), // the DSP time must also get updated, so that in the event of a power // failure, the DSP will restore the proper time. void dspClockSet ( void ) { uchar foo[5]; union { uchar as_bytes[4]; long as_long; } temp_time; // Get the current time from Limbo, and send it to DSP. // DSP time is LSB first, so need to reorder the bytes temp_time.as_long = seconds(); //print ( "\n In DSP CLOCK SET, sending new time %X to DSP ", temp_time.as_long ); //set the dsp time foo[0] = DSP_SETIME_CMD; foo[1] = temp_time.as_bytes[0]; foo[2] = temp_time.as_bytes[1]; foo[3] = temp_time.as_bytes[2]; foo[4] = temp_time.as_bytes[3]; qwrite( dspQueue, foo, 5 ); } // This routine updates the DSP time based on the received time from // a caller id message. Note that only the CID time is used to update // the DSP time, the CID date is NOT used for time updates, since no // year data is supplied with the CID date. #define SECONDS_IN_A_DAY (60*60*24) void dspClockUpdate( uchar *current_dsp_time ) { extern ulong boottime; uchar foo[5]; union { uchar b[4]; ulong l; }t; //get the dsp time t.b[0] = current_dsp_time[0]; t.b[1] = current_dsp_time[1]; t.b[2] = current_dsp_time[2]; t.b[3] = current_dsp_time[3]; //jrc - DEBUG ONLY //print ( "\n In DSP Clock UPDATE, dsp time is %X ", t.l ); if ( cidFlag == 0 ) { ulong jan1998 = 60 * 60 * 24 * 7 * 52 * 28 + 60 * 60 * 24 * 35; //jrc - DEBUG ONLY //print ( "\n This Update is NOT related to Caller ID" ); //correct the inferno time if ( t.l > jan1998 ) { /* BUG */ if ( seconds() > jan1998 ) { if ( t.l > seconds() ) { boottime = boottime + (t.l - seconds()); } else { boottime = boottime - (seconds() - t.l); } } else { boottime = boottime + (t.l - seconds()); } } else { if ( seconds() > jan1998 ) { t.l = seconds(); } else { /* Bug -- always 1998 */ boottime = jan1998; t.l = seconds(); } } } else { //jrc - DEBUG ONLY //print ( "\n This Update is caused by CID reception" ); //print ( "\n CidHour is %d, CidMinute is %d ", cidHour, cidMinute ); //correct the time // t.l = ((seconds() / 24 / 60 / 60) * 24 * 60 * 60) + 1; // BUG - daylight savings //t.l = ((seconds() / 24 / 60 / 60) * 24 * 60 * 60) + (cidHour * 60 * 60) + (cidMinute * 60); // // Note the following computation. We get the current time // maintained by inferno (which is the number of seconds // elapsed since the epoch 00:00:00 GMT, January 1, 1970). // Performing integer division by seconds in a day, we get // the number of days which have elapsed since the epoch. // Multiplying this number by seconds in a day gives the // time in seconds at precisely zero hundred hours for // this "day", which does have to be the correct date. // (We are only adjusting the time, not the date). // Finally, we add on the number of seconds indicated in // the caller ID message. // t.l = ( ( seconds() / SECONDS_IN_A_DAY) * SECONDS_IN_A_DAY) + (cidHour * 60 * 60) + (cidMinute * 60); //correct the inferno time /* BUG */ if ( t.l > seconds() ) { //jrc - DEBUG ONLY //print ( "\n Adjusting boot time, case 1, forward in time" ); boottime = boottime + (t.l - seconds()); } else { //jrc - DEBUG ONLY //print ( "\n Adjusting boot time, case 2, backward in time" ); boottime = boottime - (seconds() - t.l); } } //set the dsp time foo[0] = DSP_SETIME_CMD; foo[1] = t.b[0]; foo[2] = t.b[1]; foo[3] = t.b[2]; foo[4] = t.b[3]; qwrite( dspQueue, foo, 5 ); cidFlag = 0; } /* ************************************************************************** ** ** FUNCTION NAME : Go_Off_Hook ** ** DESCRIPTION : This routine will perform all the necessary actions, ** to take either the handset or speakerphone off-hook, ** so that a phone call can be made. ** ** PARAMETERS : mode - which goes off hook (handset or speaker) ** ** RETURN VALUE : None ** ** HISTORY : 05/12/1998 - J. Carroll : ** 1) Created - new routine to reduce ** redundant code ** ************************************************************************** */ static void Go_Off_Hook ( uchar mode ) { /* BEGIN Go_Off_Hook */ uchar foo[1]; if ( mode == OFFHOOK_S ) { putDspStPp(); //take line off hook dspLineCtl( mode ); stutterDetectEnable(); //speaker has gone off hook foo[0] = K2A_SPKR_IN_USE_MSG; qwrite( tel2limQueue, foo, 1 ); // Set LEDs and states audioPath = audioPath | DEST_SP; // OR - set speakerphone bit ledControl( SPEAKER, LED_ON ); ledControl( LINE1, LED_ON ); ledControl( MUTE, LED_OFF ); holdState = 0; muteState = 0; //set the volume dspVolumeCmd( VOL_SP, volSpeaker ); //set the audio path tsleep( &up->sleep, return0, 0, 200 ); dspAudioCmd( SPEAKERPHONE ); } else if ( mode == OFFHOOK_H ) { // NOTE - by design: lifting handset disables speakerphone audioPath = audioPath | DEST_CH; // OR - set handset bit audioPath = audioPath & (~DEST_SP); //AND - clear speaker bit //take line off hook dspLineCtl( mode ); stutterDetectEnable(); //handset is off hook foo[0] = K2A_HSET_IN_USE_MSG; qwrite( tel2limQueue, foo, 1 ); // jrc - DEBUG only //print ("\n Driver sent Handset in use message to Limbo . . ."); ledControl( SPEAKER, LED_OFF ); ledControl( MUTE, LED_OFF ); ledControl( LINE1, LED_ON ); holdState = 0; muteState = 0; //set the volume dspVolumeCmd( VOL_HS, volCord ); //set the handset audio path dspAudioCmd( HANDSET ); } } /* END Go_Off_Hook */ /* ************************************************************************** ** ** FUNCTION NAME : Check_Modem_and_Go_Off_Hook ** ** DESCRIPTION : This routine will check if the line is in use by the ** modem. If it is, a message is sent to a limbo appl ** to inform the user, and let the user choose the ** appropriate action. If the modem is not using the ** line, then this routine falls through to the normal ** off-hook sequence. ** ** PARAMETERS : mode - which goes off hook (handset or speaker) ** ** RETURN VALUE : None ** ** HISTORY : 05/12/1998 - J. Carroll : ** 1) Created - new routine ** ************************************************************************** */ static void Check_Modem_and_Go_Off_Hook ( uchar mode ) { /* BEGIN Check_Modem_and_Go_Off_Hook */ uchar foo[1]; // Check for modem override if ( modemState != 0 ) { // It's in use, let the high level appl know. foo[0] = K2A_MODEM_OVERRIDE_REQ_MSG; qwrite( tel2limQueue, foo, 1 ); // Don't do anything to the line, just set appropriate flags and // await for furthur commands from Limbo. modem_override_pending = 1; expected_mode_after_override = mode; //jrc - DEBUG ONLY //print ( " Check_Modem says OVERRIDE . . . \n " ); } else // Modem is idle { Go_Off_Hook ( mode ); //jrc - DEBUG ONLY //print ( " Check_Modem says GO OFF HOOK on %x \n ", mode ); } } /* END Check_Modem_and_Go_Off_Hook */ static void keyPress( uchar mode, uchar type, uchar key ) { int dtmf; char foo[2]; static flashPressed = 0; static uchar key2dtmf[] = { [ KEY_0 ] DTMF_0, [ KEY_1 ] DTMF_1, [ KEY_2 ] DTMF_2, [ KEY_3 ] DTMF_3, [ KEY_4 ] DTMF_4, [ KEY_5 ] DTMF_5, [ KEY_6 ] DTMF_6, [ KEY_7 ] DTMF_7, [ KEY_8 ] DTMF_8, [ KEY_9 ] DTMF_9, [ KEY_ASS ] DTMF_S, [ KEY_PND ] DTMF_P, [ KEY_SPACE ] SPACE, [ KEY_PAUSE ] DTMF_U, }; static uchar dtmf2ascii[] = { [ DTMF_0 ] '0', [ DTMF_1 ] '1', [ DTMF_2 ] '2', [ DTMF_3 ] '3', [ DTMF_4 ] '4', [ DTMF_5 ] '5', [ DTMF_6 ] '6', [ DTMF_7 ] '7', [ DTMF_8 ] '8', [ DTMF_9 ] '9', [ DTMF_S ] '*', [ DTMF_P ] '#', [ SPACE ] ' ', [ DTMF_U ] 'p', }; foo[0] = K2A_KEYPAD_PRESS_MSG; if ( 0 != audioPath ) { foo[0] = K2A_DIGIT_DIALED_MSG; } switch( key ) { case KEY_0: case KEY_1: case KEY_2: case KEY_3: case KEY_4: case KEY_5: case KEY_6: case KEY_7: case KEY_8: case KEY_9: case KEY_ASS: case KEY_PND: if ( mode == PULSE_DIAL || 0 == audioPath )if ( 0 != type )beep(); /* FALLTHROUGH */ case KEY_SPACE: case KEY_PAUSE: dtmf = key2dtmf[key]; foo[1] = dtmf2ascii[dtmf]; qwrite( tel2limQueue, foo, 2 ); if ( 0 != audioPath )dspDialCmd( mode, dtmf ); break; case KEY_LINE1: case KEY_LINE2: dspToneCmd( DEST_SP, SAD_TONE ); break; case KEY_UP: if ( DTMF_CONTU == dialCont ) { if ( 0 != flashPressed ) { flashPressed = 0; } else { dspDialCmd( mode, 0xfe ); } } break; case KEY_SPEAKER: audioPath = audioPath ^ DEST_SP; // XOR - toggle the speaker bit if ( 0 != (audioPath & DEST_SP) || 0 != holdState ) { Check_Modem_and_Go_Off_Hook (OFFHOOK_S); } else { // Do NOT honor the onhook event if the modem is busy, since // the user inadvertently pressed the speakerphone while the // modem was in use. if ( modemState == 0 ) // Modem is idle, handle onhook event { foo[0] = K2A_SPKR_NOT_IN_USE_MSG; qwrite( tel2limQueue, foo, 1 ); if ( 0 != (audioPath & DEST_CH) ) { //resume handset operations dspAudioCmd( HANDSET ); ledControl( SPEAKER, LED_OFF ); // MR - crd2285 //mute was removed muteState = 0; ledControl( MUTE, LED_OFF ); } else { //clear the audio path dspAudioCmd( IDLE ); //speaker has gone on hook ledControl( SPEAKER, LED_OFF ); ledControl( LINE1, LED_OFF ); //put line on hook dspLineCtl( ONHOOK ); //mute was removed muteState = 0; ledControl( MUTE, LED_OFF ); // Reset all modem override flags modem_override_pending = 0; expected_mode_after_override = ONHOOK; //check speaker parameters getDspSt(); } } } break; case KEY_OFFHOOK: // // NOTE - jrc - this is a completely undocumented feature of // the DSP. When the handset is lifted, the DSP // sends the message " 04 88 ", which can be loosely // interpreted as "Key Press - Key offhook ". Likewise // the message " 04 80 ", is sent when the handset is // cradled, and can be interpreted as "Key Press - Key // onhook ". // Check_Modem_and_Go_Off_Hook (OFFHOOK_H); break; case KEY_ONHOOK: // // NOTE - jrc - this is a completely undocumented feature of // the DSP. When the handset is lifted, the DSP // sends the message " 04 88 ", which can be loosely // interpreted as "Key Press - Key offhook ". Likewise // the message " 04 80 ", is sent when the handset is // cradled, and can be interpreted as "Key Press - Key // onhook ". // // Do NOT honor the onhook event if the modem is busy, since // the user inadvertently lifted the handset while the // modem was in use. if ( modemState == 0 ) // Modem is idle, handle onhook event { foo[0] = K2A_HSET_NOT_IN_USE_MSG; qwrite( tel2limQueue, foo, 1 ); // jrc - DEBUG only //print ("\n Driver sent Handset NOT in use message to Limbo . . ."); audioPath &= ~DEST_CH; if ( 0 == audioPath && 0 == holdState ) { //clear the audio path dspAudioCmd( IDLE ); //put line on hook dspLineCtl( ONHOOK ); ledControl( LINE1, LED_OFF ); //mute was removed muteState = 0; ledControl( MUTE, LED_OFF ); // Reset all modem override flags modem_override_pending = 0; expected_mode_after_override = ONHOOK; } } break; case KEY_VOL_UP: if ( audioPath == 0 ) { //ringer volume up if ( volRinger < 7 ) { //set the volume dspVolumeCmd( VOL_RG, ++volRinger ); //fire off a ring dspToneCmd( DEST_SP, RING_ALERT ); } else { dspToneCmd( DEST_SP, DOUBLE_BEEP ); } foo[0] = K2A_RINGER_VOLUME_MSG; foo[1] = '0' + volRinger; qwrite( tel2limQueue, foo, 2 ); } else if ( (audioPath & DEST_SP) == DEST_SP ) { //speaker volume up if ( volSpeaker < 7 ) { //set the volume dspVolumeCmd( VOL_SP, ++volSpeaker ); // jrc - remove this for mr test //beep(); } else { dspToneCmd( DEST_SP, DOUBLE_BEEP ); } foo[0] = K2A_SPKR_VOLUME_MSG; foo[1] = '0' + volSpeaker; qwrite( tel2limQueue, foo, 2 ); } else if ( audioPath == DEST_CH ) { //handset volume up if ( volCord < 3 ) { //set the volume dspVolumeCmd( VOL_HS, ++volCord ); } else { dspToneCmd( DEST_SP, DOUBLE_BEEP ); } foo[0] = K2A_HSET_VOLUME_MSG; foo[1] = '0' + volCord; qwrite( tel2limQueue, foo, 2 ); } break; case KEY_VOL_DN: if ( audioPath == 0 ) { //ringer volume down if ( volRinger > 0 ) { //set the volume dspVolumeCmd( VOL_RG, --volRinger ); //fire off a ring dspToneCmd( DEST_SP, RING_ALERT ); } else { dspToneCmd( DEST_SP, DOUBLE_BEEP ); } foo[0] = K2A_RINGER_VOLUME_MSG; foo[1] = '0' + volRinger; qwrite( tel2limQueue, foo, 2 ); } else if ( (audioPath & DEST_SP) == DEST_SP ) { //speaker volume down if ( volSpeaker > 1 ) { //set the volume dspVolumeCmd( VOL_SP, --volSpeaker ); // jrc - remove this for mr test //beep(); } else { dspToneCmd( DEST_SP, DOUBLE_BEEP ); } foo[0] = K2A_SPKR_VOLUME_MSG; foo[1] = '0' + volSpeaker; qwrite( tel2limQueue, foo, 2 ); } else if ( audioPath == DEST_CH ) { //handset volume down if ( volCord > 0 ) { //set the volume dspVolumeCmd( VOL_HS, --volCord ); } else { dspToneCmd( DEST_SP, DOUBLE_BEEP ); } foo[0] = K2A_HSET_VOLUME_MSG; foo[1] = '0' + volCord; qwrite( tel2limQueue, foo, 2 ); } break; case KEY_FLASH: beep(); // jrc - DEBUG only //print ("\n Flash key pressed, sending message to Limbo . . ."); tsleep( &up->sleep, return0, 0, 250 ); // NOTE - following is not the K2A message, it is the data. The // message is either a Keypress or dialed digit, which was // previously set above - be sure to send it to Limbo. foo[1] = 'f'; qwrite( tel2limQueue, foo, 2 ); // // jrc - crd2356, crd2364: Flash key corrupting line and // modem. // Always tell Limbo the Flash key was pressed (for entry // into directory numbers, etc.). However, do NOT do // anything to the line if either the modem has control // of the line, or the line is not in use. Apparently, // telling the DSP to dial a flash key, even if the // line is not in use, causes the DSP to take the line // off-hook. // if ( ( audioPath != 0 ) && ( modemState == 0 ) ) { // jrc - DEBUG only //print ("\n Sending DSP Dial Flash key command "); dspDialCmd( DTMF_BURST, DTMF_F ); flashPressed = 1; //test hold state if ( 0 != holdState ) { //hold was removed holdState = 0; ledControl( LINE1, LED_ON ); //test audio path if ( (audioPath & DEST_SP) == DEST_SP ) { //speaker phone unmute dspAudioCmd( SPEAKERPHONE ); ledControl( SPEAKER, LED_ON ); } else { //hand set is unmuted dspAudioCmd( HANDSET ); } } //test mute state if ( 0 != muteState ) { //mute was removed muteState = 0; ledControl( MUTE, LED_OFF ); //test audio path if ( (audioPath & DEST_SP) == DEST_SP ) { //speaker phone unmute dspAudioCmd( SPEAKERPHONE ); } else { //hand set is unmuted dspAudioCmd( HANDSET ); } } } break; case KEY_HOLD: if ( 0 != audioPath ) { parallelDetect(); //test hold state if ( 0 != holdState ) { //hold was removed holdState = 0; //jrc - Bug in DSP? Since LED was blinking // on hold, need to turn it off first, // then on, for it to stay on steady. ledControl( LINE1, LED_OFF ); ledControl( LINE1, LED_ON ); beep(); tsleep( &up->sleep, return0, 0, 250 ); //test audio path if ( (audioPath & DEST_SP) == DEST_SP ) { //speaker phone unmute dspAudioCmd( SPEAKERPHONE ); ledControl( SPEAKER, LED_ON ); } else { //hand set is unmuted dspAudioCmd( HANDSET ); } } else { //mute is applied holdState = 1; //mute is removed muteState = 0; ledControl( LINE1, LED_FLASH ); ledControl( SPEAKER, LED_OFF ); //hand set is muted dspAudioCmd( IDLE ); ledControl( MUTE, LED_OFF ); tsleep( &up->sleep, return0, 0, 100 ); beep(); } } else { //hold is not active holdState = 0; beep(); } break; case KEY_MUTE: if ( 0 != audioPath && 0 == holdState ) { //test mute state if ( 0 != muteState ) { //mute was removed muteState = 0; ledControl( MUTE, LED_OFF ); beep(); tsleep( &up->sleep, return0, 0, 250 ); //test audio path if ( (audioPath & DEST_SP) == DEST_SP ) { //speaker phone unmute dspAudioCmd( SPEAKERPHONE ); } else { //hand set is unmuted dspAudioCmd( HANDSET ); } } else { //mute is applied muteState = 1; ledControl( MUTE, LED_ON ); //test audio path if ( (audioPath & DEST_SP) == DEST_SP ) { //speaker phone muted dspAudioCmd( MUTE_SPEAKERPHONE ); } else { //hand set is muted dspAudioCmd( MUTE_HANDSET ); } tsleep( &up->sleep, return0, 0, 100 ); beep(); } } else { //mute is not active muteState = 0; ledControl( MUTE, LED_OFF ); beep(); } break; default: break; } } /* ************************************************************************** ** ** FUNCTION NAME : ringAlert ** ** DESCRIPTION : This routine handles ringing the phone to indicate an ** incoming call to the user. It also handles priority ** ringing. ** ** PARAMETERS : ring_data - status byte received with DSP ring command ** Size : 1 byte ** Bit Layout : bit 7-2, ring count ** bit 1, ring alerter ( 0 = off, 1 = on ) ** bit 0, line ( 0 = line1, 1 = line2 ) ** ** RETURN VALUE : None ** ** HISTORY : 03/10/1998 - J. Carroll : ** 1) Add header and comments ** 2) Don't send tone command to DSP if ringer ** volume is off (inf980574). ** ************************************************************************** */ void ringAlert( uchar ring_data ) { /* BEGIN ringAlert */ static uchar old_ring_data = 0; char foo[1]; // Since ring commands come in about every 128ms, only process the data // when something changes from the previous ring command. if ( ring_data != old_ring_data ) { if ( ( ring_data & 0xfe) == 0x02 ) // Mask out line bit, check if { // ring alerter bit is set, and // ring count = 0 (i.e first ring). cidInit(); priority_ring_state = 0; // Initialize to normal ring. } if ( ( ring_data & 0x03) == 0x02 ) // Mask out ring count, { // check for ring on, and // on line 1. foo[0] = K2A_RINGING_MSG; qwrite( tel2limQueue, foo, 1 ); // jrc - Don't even send the command to DSP if ringer is off if ( volRinger > 0 ) { dspVolumeCmd ( VOL_RG, volRinger ); dspToneCmd ( DEST_SP, RING_ALERT ); } cwiStateChange( RING_CWI ); } else // ring on bit is clear, so kill the alerter { dspToneCmd( DEST_SP, 0 ); // Since ringing has just terminated, now need to check if this // is a priority ring (which occurs at the end of normal ringing). if ( priority_ring_state != 0 ) { // Special case handling - DSP sends a status byte of 00 after // 10 seconds of no ring, to tell FP that ringing has stopped. // Don't want to do priority ring in this case. if ( ( ring_data & 0xfe) != 0x00 ) { dspToneCmd ( DEST_SP, PRIORITY_RING ); } } } // Save the data for comparison with next ring command old_ring_data = ring_data; } } /* END ringAlert */ void farEndHangUp( void ) { uchar foo[1]; foo[0] = K2A_FAR_END_HANGUP_MSG; qwrite( tel2limQueue, foo, 1 ); } void busyDetect( uchar xxx ) { uchar foo[1]; static int report; switch( xxx ) { case BUSY_ON: report = 0; foo[0] = DSP_BUSYON_CMD; qwrite( dspQueue, foo, 1 ); // jrc - debug // print ( " \n Sending DSP Busy ON command "); break; case BUSY_OFF: foo[0] = DSP_BUSYOFF_CMD; qwrite( dspQueue, foo, 1 ); // jrc - debug // print ( " \n Sending DSP Busy OFF command "); if ( report == 0 ) { foo[0] = K2A_CONNECT_MSG; qwrite( tel2stdQueue, foo, 1 ); // jrc - debug // print ( " \n Determined NOT BUSY, sending Limbo CONNECTED msg "); } break; case BUSY_REPORT: foo[0] = K2A_BUSY_MSG; qwrite( tel2stdQueue, foo, 1 ); report = 1; // jrc - debug // print ( " \n Determined BUSY, sending Limbo BUSY msg "); break; default: break; } } // This routine waits for 10 seconds to detect a busy signal. void busyTask( void *a ) { USED( a ); tsleep( &up->sleep, return0, 0, 200 ); busyDetect( BUSY_ON ); tsleep( &up->sleep, return0, 0, 10000 ); busyDetect( BUSY_OFF ); pexit( 0, 0 ); } /* END busyTask */ void dialProcess( uchar *digit_sequence , int sequence_length ) { int i = 0; uchar digit; // If the user has turned OFF Automatic Speakerphone, don't do anything. if ( 0 == speakerOption && 0 == audioPath ) { return; } // DEBUG only // print("dialProcess: %s %d \n", digit_sequence, sequence_length); // Take the speakerphone off hook, if necessary. if ( audioPath == 0 ) { Check_Modem_and_Go_Off_Hook ( OFFHOOK_S ); // If we went off hook, the audio path is now non-zero. // So if the audio path is non-zero, since this is an // auto offhook event, we need to wait for dialtone. if ( audioPath != 0 ) { // jrc - wait 1 additional second (up to 3) since missing first digit // tsleep ( &up->sleep, return0, 0, 1000 ); // FOLLOWING LINES WORKS about 95% of the time //Send the pause command (to wait for dialtone?) //dspDialCmd( DTMF_BURST, DTMF_U ); // Just sleep for 3 seconds, before dialing tsleep ( &up->sleep, return0, 0, 3000 ); } else { // Audio path is still zero, so modem has the line. Do not // pump any digits on the line, just exit. return; } } // jrc - fix for dsp bug - when dialing a string of digits in // burst mode, and then a key is pressed, this causes a // "dial - no tone" command to be sent to DSP when the // key is released. The DSP stops all dialing once this // is received, even if other digits are queued up ahead of it. // NOTE - this is done here because the routine telMessage, // defined below, is invoked from a separate task, and // this routine handles all KeyPad presses, and uses // the current value of dailCont to handle the keypress. // This "patch" guarantees that we stay in DTMF burst // mode until the string has been pumped out onto the line. temp_dial_mode = dialCont; if ( temp_dial_mode != PULSE_DIAL ) { dialCont = DTMF_BURST; } //run through the digits while ( ( i < sequence_length ) && ( digit_sequence[i] != 0 )) { switch ( digit_sequence[i] ) { case '0': digit = KEY_0; break; case '1': digit = KEY_1; break; case '2': digit = KEY_2; break; case '3': digit = KEY_3; break; case '4': digit = KEY_4; break; case '5': digit = KEY_5; break; case '6': digit = KEY_6; break; case '7': digit = KEY_7; break; case '8': digit = KEY_8; break; case '9': digit = KEY_9; break; case 'S': case 's': digit = KEY_SPACE; break; case 'P': case 'p': digit = KEY_PAUSE; break; case 'W': case 'w': digit = KEY_WAIT; break; case 'F': case 'f': digit = KEY_FLASH; break; case '*': digit = KEY_ASS; break; case '#': digit = KEY_PND; break; default: digit = KEY_PAUSE; break; } keyPress( dialStop, 0, digit ); ++i; } // jrc - clean up of DSP bug fix, restore original mode // NOTE - this time may need to increase if some sick user decides // to put PAUSES in between all his numbers. This number // works reasonably well for both handset and speakerphone. tsleep ( &up->sleep, return0, 0, 2000 ); dialCont = temp_dial_mode; } /* END dialProcess */ /* ************************************************************************** ** ** FUNCTION NAME : modemControl ** ** DESCRIPTION : This routine handles taking the modem on and off hook. ** ** PARAMETERS : line_request - desired state of modem ** 0 = put modem on-hook (modem off) ** non-zero = take modem off_hook (modem on) ** ** RETURN VALUE : 0 - modem request was properly serviced ** -1 - modem request was invalid, or failed ** ** HISTORY : 03/11/1998 - J. Carroll : ** 1) Add header and comments ** ************************************************************************** */ int modemControl( int line_request ) { /* BEGIN modemControl */ uchar foo[1]; //test for access to the phone line if ( 0 != line_request ) { //see if in use if ( 0 != audioPath ) { //line is in use return( -1 ); } else { //line is idle dspLineCtl( OFFHOOK_H ); ledControl( RELAY, LED_OFF ); ledControl( LINE1, LED_ON ); modemState = 1; foo[0] = K2A_MODEM_IN_USE_MSG; qwrite( tel2limQueue, foo, 1 ); } } else // request to put modem on-hook (modem off) { if ( 0 != modemState ) { //hangup dspLineCtl( ONHOOK ); ledControl( RELAY, LED_ON ); ledControl( LINE1, LED_OFF ); modemState = 0; foo[0] = K2A_MODEM_NOT_IN_USE_MSG; qwrite( tel2limQueue, foo, 1 ); } else { //modem was not active return( -1 ); } } //return ok return( 0 ); } /* END modemControl */ /* ************************************************************************** ** ** FUNCTION NAME : modemOverride ** ** DESCRIPTION : This routine will perform the necessary actions ** to take the phone off-hook, while the modem was ** using the line. ** ** PARAMETERS : None ** ** RETURN VALUE : None ** ** HISTORY : 05/12/1998 - J. Carroll : ** 1) Created - new routine ** ************************************************************************** */ void modemOverride ( void ) { /* BEGIN modemOverride */ uchar foo[1]; //jrc - debug only //print (" \n In Modem Override ..., about to abort modem " ); // Brute Force Method: // Put the phone on-hook, which will cause the modem to detect a // hang up condition, and then we can get the line back. // This is copied from modemControl - hangup procedure dspLineCtl( ONHOOK ); ledControl( RELAY, LED_ON ); ledControl( LINE1, LED_OFF ); modemState = 0; foo[0] = K2A_MODEM_NOT_IN_USE_MSG; qwrite( tel2limQueue, foo, 1 ); // Allow time for CO to recognize end of previous call, start of new call // (i.e., must be longer than "flash" timing). tsleep( &up->sleep, return0, 0, 2000 ); Go_Off_Hook ( expected_mode_after_override ); // Reset all modem override flags modem_override_pending = 0; expected_mode_after_override = ONHOOK; // jrc - debug only //print (" \n In Modem Override ..., Phone should be off-hook, ready for dialing " ); } /* END modemOverride */ void telCommand( uchar *a, int n) { uchar foo[1]; if ( spiFlag ) print("\ntelCommand"); // jrc - DEBUG only // { // char s[64]; // memmove(s, a, n); // s[n] = 0; // print(" \n telCommand: %s %d ", s, n); // } switch (a[0]) { case A2K_DIAL_MSG: case A2K_MUTE_MSG: case A2K_ENABLE_BUSY_DETECT_MSG: //dial, mute, or busy detect command - asynchronous qwrite( telQueue, a, n ); break; case A2K_HANGUP_MSG: //hangup command dspAudioCmd( IDLE ); ledControl( CLEARALL, 0 ); ledControl( RELAY, LED_ON ); holdState = 0; muteState = 0; dspLineCtl( ONHOOK ); break; case A2K_CHECK_LINE_MSG: foo[0] = K2A_LINE_NOT_IN_USE_MSG; if ( lineInUse != 0 ) { foo[0] = K2A_LINE_IN_USE_MSG; } qwrite( tel2stdQueue, foo, 1 ); break; case A2K_NEW_CALL_MSG: newCallState = a[1] - '0'; setNewcallLed(); break; case A2K_NEW_NOTE_MSG: newNoteState = a[1] - '0'; setNewcallLed(); break; case A2K_PRIORITY_RING_MSG: priority_ring_state = 1; break; case A2K_DO_MODEM_OVERRIDE_MSG: modemOverride(); break; // Used to restore message LED after power fail case A2K_SET_MSG_MODE_MSG: switch ( a[1] - '0') { case FSK_MODE_LED_ON: fskPresent = 1; fskState = 1; stutterState = 0; break; case FSK_MODE_LED_OFF: fskPresent = 1; fskState = 0; stutterState = 0; break; case STUTTER_MODE_LED_ON: fskPresent = 0; fskState = 0; stutterState = 1; break; case STUTTER_MODE_LED_OFF: fskPresent = 0; fskState = 0; stutterState = 0; break; } // Update the multi-purpose LED based on the new settings setNewcallLed(); break; } } static void cwiTask( void *a ) { uchar foo[100]; uchar cwiState; int cwiTimer; char liuCheck; enum { CWI_NULL, CWI_IDLE_CC, CWI_IDLE_UA, CWI_RING, CWI_IGNORE, CWI_DETECT, CWI_LIU }; USED( a ); //initial state cwiState = CWI_NULL; cwiTimer = 0; liuCheck = 0; dspLineCtl( OFFHOOK_S ); dspLineCtl( ONHOOK ); while( 1 ) { //fsk was seen if ( 0 != fskPresent ) { //empty the queue while( 1 ) { //get the state changes qread( cwiQueue, foo, sizeof( foo ) ); } } //increment time tsleep( &up->sleep, return0, 0, 10 ); ++cwiTimer; //see if the timer has crossed a boundary switch( cwiState ) { case CWI_RING: if ( 580 == cwiTimer ) { //ring stopped cwiState = CWI_IDLE_UA; cwiTimer = 0; } break; case CWI_IDLE_UA: if ( stutterState != 0 ) cwiState = CWI_NULL; if ( 23700 == cwiTimer ) { //unanswered call cwiState = CWI_IGNORE; cwiTimer = 0; } break; case CWI_IGNORE: if ( 50 == cwiTimer ) { //start the stutter testing dspLineCtl( OFFHOOK_H ); stutterDetectEnable(); cwiState = CWI_DETECT; cwiTimer = 0; } break; case CWI_DETECT: if ( 210 == cwiTimer ) { //all done dspLineCtl( ONHOOK ); if ( 0 != liuCheck ) { cwiState = CWI_LIU; } else { cwiState = CWI_NULL; } cwiTimer = 0; } break; case CWI_IDLE_CC: if ( cwiTimer == 2700 ) { cwiState = CWI_IGNORE; cwiTimer = 0; } break; case CWI_LIU: if ( cwiTimer == 2000 ) { } break; default: cwiState = CWI_NULL; cwiTimer = 0; break; } //see if there is a state change if ( qcanread( cwiQueue ) ) { //get the state change qread( cwiQueue, foo, sizeof( foo ) ); switch( foo[0] ) { case RING_CWI: cwiTimer = 0; cwiState = CWI_RING; break; case LIU_CWI: liuCheck = 1; if ( cwiState != CWI_DETECT ) { cwiTimer = 0; cwiState = CWI_LIU; } break; case LNIU_CWI: liuCheck = 0; if ( cwiState == CWI_LIU ) { if ( cwiTimer > 2000 ) { cwiTimer = 0; cwiState = CWI_IDLE_CC; } else { cwiState = CWI_NULL; cwiTimer = 0; } } break; case DONE_CWI: if ( cwiState == CWI_DETECT ) { dspLineCtl( ONHOOK ); } break; default: cwiState = CWI_NULL; cwiTimer = 0; break; } } } } static void telTask( void * ) { uchar tmsg[64]; int sz; telQueue = qopen( 64, 0, 0, 0 ); if ( telQueue == 0 ) { panic("telTaskQueue"); } //wait for all the other tasks tsleep( &up->sleep, return0, 0, 2000 ); //create the stutter tone task kproc( "cwiTask", cwiTask, 0 , 0); for (;;) { sz = qread( telQueue, tmsg, sizeof tmsg ); switch ( tmsg[0] ) { case A2K_DIAL_MSG : dialProcess(&tmsg[1], sz - 1); break; case A2K_MUTE_MSG : if ( 0 != (audioPath & DEST_SP) ) { ledControl( SPEAKER, LED_BLINK ); audioPath = 0; dspAudioCmd( MUTE_SPEAKERPHONE ); } break; case A2K_ENABLE_BUSY_DETECT_MSG: //enable busy detect // jrc - debug //print ( " \n LIMBO has commanded busy detection "); kproc( "busy", busyTask, 0, 0); break; default: print("telTask: what is '%c'?\n", tmsg[0]); break; } } } static void doTelMsg( uchar *msg, int msize ) { extern char permBuffer[]; // jrc - increase this by 1 for MR 1501 resolution char foo[2]; //jrc - must maintain this buffer between function calls - make it static static uchar tmpBuffer[500]; //check for autonomous commands loop: // jrc - debug only // if ( msg[0] != 0xdb){int i;print("\nD->m ");for(i=0;i 1 ) { --msize; memmove( &msg[0], &msg[1], msize ); goto loop; } break; case RSP_RING_CMD: ringAlert( msg[1] ); break; case KEYPRESS: // jrc - debug // print ( " \n DSP Keypress: %X %X %X ", msg[0], msg[1], msg[2] ); keyPress( dialCont, 1, msg[1] ); if ( msize > 2 ) { // jrc - debug // print ( " msize > 2, so calling goto loop . . . " ); msize -= 2; memmove( &msg[0], &msg[2], msize ); goto loop; } break; case PWRDN_MSG: if (spiFlag)print("\nPWRDN_MSG"); break; case PWRUP_NO_CALL_MSG: if (spiFlag)print("\nPWRUP_NO_CALL_MSG"); break; case PWRUP_WITH_CALL_MSG: if (spiFlag)print("\nPWRUP_WITH_CALL_MSG"); break; case RSP_PARALLEL_SET_DETECT_CMD : if ( 0 != holdState ) { //drop the call audioPath = 0; dspAudioCmd( IDLE ); ledControl( CLEARALL, 0 ); ledControl( RELAY, LED_ON ); holdState = 0; muteState = 0; dspLineCtl( ONHOOK ); farEndHangUp(); } break; case RSP_BATT_STATUS_CMD: // jrc - this is a workaround to fix how often the battery and ringer // off messages get displayed, based on a "return from idle" // event. Without any help from the toolbar, we can detect // "idle" as the screen brightness being turned off. // // NOTE - do not even send the message if the modem is active, since // this will overwrite any web page the user is viewing, and // is really a nuisance. if ( modemState == 0 ) // Modem is idle { if ( screen_was_idle ) { if ( getbrightness() != 0 ) { // Screen was idle, and screen is now alive, so put up the // message. // Check for low battery condition // jrc - this is supposed to be around 5.3 Volts? if ( (msg[1] & 0x7f) < 0x2c ) { foo[0] = K2A_BATT_LOW_MSG; qwrite ( tel2limQueue, foo, 1 ); } // Check for ringer off condition if ( volRinger == 0 ) { foo[0] = K2A_RINGER_OFF_MSG; qwrite ( tel2limQueue, foo, 1 ); } } } else { // Screen was not idle last time we received this message, // so not a return from idle event, so don't send the message if ( getbrightness() == 0 ) { screen_was_idle = 1; } } } /* END if ( modemState == 0 ) */ // jrc - come back to this when better defined!!! // // Check for good battery condition // NOTE - not bad does not mean good. There is a window between // the low batt threshold and the good batt threshold, and // anything in between should not change the status until // one of these thresholds is reached. NOTE - this value // is arbitrarily chosen right now. // if ( (msg[1] & 0x7f) > 0x30 ) // { // foo[0] = K2A_BATT_AOK_MSG; // qwrite( tel2limQueue, foo, 1 ); // } break; case SPKPH_STATES_DATA: // jrc - debug only // if (msg[1] == 0 ) print(" \n "); // print(" 0x18 %x ",msg[1]); if (msg[1] < 20) memmove(tmpBuffer+100+20*msg[1], msg+2, 20); if ( msg[1] == 19 ) { // jrc - this has to be done here. // Only want to issue this command after all state // blocks have been received, since DSP does some // strange resetting of its block counter if both // "get block" commands were issued before all data // blocks have been received. if (spiFlag) print("getDspSt wakeup\n"); spkrBusy = 0; wakeup(&spkrRendez); getDspPp(); } break; case SPKPH_PARAMS_DATA: // jrc - debug only // if (msg[1] == 0 ) print(" \n "); // print(" 0x19 %x ",msg[1]); if (msg[1] < 5) memmove(tmpBuffer+20*msg[1], msg+2, 20); if ( msg[1] == 4 ) { // jrc - DSP bug fix !!! // // Always insert default bytes, no matter what DSP returned // on the upload (12 words total = 24 bytes ) // NOTE - values copied from shannon.b file // NOTE - Reconstruct the data BEFORE the comparison test static uchar dspdatafix[] = { // Params - Block 2 - last 2 bytes only 0x04, 0x00, // Params - Block 3 - whole block 0x06, 0x00, 0x08, 0x00, 0x0A, 0x00, 0x10, 0x00, 0x12, 0x00, 0x14, 0x00, 0x16, 0x00, 0x1C, 0x00, 0x1E, 0x00, 0x20, 0x00, // Params - Block 4 - first 2 bytes only! 0x22, 0x00, }; // Calculate index into corrupted data in buffer: // 20 bytes (block 0) + 20 bytes (block 1) + 18 bytes (block 2) memmove(tmpBuffer+58, dspdatafix, sizeof dspdatafix); // End of DSP BUG Fix, now do the comparison if ( 0 != memcmp( tmpBuffer, permBuffer, 500 ) ) { static diffTest = 0; extern Queue *permQueue; memmove( permBuffer, tmpBuffer, 500 ); if ( diffTest++ > 10 ) { qwrite( permQueue, tmpBuffer, 500 ); diffTest = 0; } } // else // { // print (" \n Buffer compare is OK " ); // } if (spiFlag) print("getDspPp wakeup\n"); spkrBusy = 0; wakeup(&spkrRendez); } break; default: if (spiFlag)print("default size %d data %x %x %x %x %x",msize,msg[0],msg[1],msg[2],msg[3],msg[4]); break; } } /* * Action (e.g. from dspIngress) deferred to lower priority task */ void telMessage( uchar *msg, int sz ) { qwrite(telMsgQueue, msg, sz); } /* * The task that performs actions from DSP */ static void telMsgTask( void * ) { uchar msg[64]; int sz; // jrc (for billb) - make this queue at least as big as worst case for // all DSP states and params data (25 blocks * 22 bytes) = 550 telMsgQueue = qopen( 600, 0, 0, 0 ); if ( telMsgQueue == 0 ) { panic("telMsgTask"); } while (sz = qread(telMsgQueue, msg, sizeof msg)) doTelMsg(msg, sz); } void telInit( void ) { kproc( "telTask", telTask, 0, 0); kproc( "telMsgTask", telMsgTask, 0, 0); }