/* $Id$ G N O K I I A Linux/Unix toolset and driver for Nokia mobile phones. Copyright (C) 1999, 2000 Hugh Blemings & Pavel Janík ml. Released under the terms of the GNU GPL, see file COPYING for more details. This code contains the main part of the 5160/6160 code. $Log$ Revision 1.1.1.1 2001/11/25 21:59:05 short :pserver:cvs@pserver.samba.org:/cvsroot - gnokii - Sun Nov 25 22:56 CET 2001 Revision 1.29 2001/08/20 23:27:37 pkot Add hardware shakehand to the link layer (Manfred Jonsson) Revision 1.28 2001/06/28 00:28:45 pkot Small docs updates (Pawel Kot) */ #ifndef WIN32 #define __mbus_6160_c /* "Turn on" prototypes in mbus-6160.h */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "misc.h" #include "gsm-common.h" #include "mbus-6160.h" #include "device.h" #include "phones/nokia.h" #define WRITEPHONE(a, b, c) device_write(b, c) //#define DEBUG /* Global variables used by code in gsm-api.c to expose the functions supported by this model of phone. */ bool MB61_LinkOK; GSM_Functions MB61_Functions = { MB61_Initialise, MB61_Terminate, MB61_GetMemoryLocation, MB61_WritePhonebookLocation, UNIMPLEMENTED, UNIMPLEMENTED, UNIMPLEMENTED, UNIMPLEMENTED, UNIMPLEMENTED, UNIMPLEMENTED, UNIMPLEMENTED, UNIMPLEMENTED, UNIMPLEMENTED, UNIMPLEMENTED, UNIMPLEMENTED, UNIMPLEMENTED, UNIMPLEMENTED, UNIMPLEMENTED, UNIMPLEMENTED, UNIMPLEMENTED, UNIMPLEMENTED, UNIMPLEMENTED, UNIMPLEMENTED, PNOK_GetManufacturer, UNIMPLEMENTED, UNIMPLEMENTED, UNIMPLEMENTED, UNIMPLEMENTED, UNIMPLEMENTED, UNIMPLEMENTED, UNIMPLEMENTED, UNIMPLEMENTED, UNIMPLEMENTED, UNIMPLEMENTED, UNIMPLEMENTED, UNIMPLEMENTED, UNIMPLEMENTED, UNIMPLEMENTED, UNIMPLEMENTED, UNIMPLEMENTED, UNIMPLEMENTED, UNIMPLEMENTED, UNIMPLEMENTED, UNIMPLEMENTED, MB61_SendRLPFrame, UNIMPLEMENTED, UNIMPLEMENTED, UNIMPLEMENTED, UNIMPLEMENTED, UNIMPLEMENTED, UNIMPLEMENTED, UNIMPLEMENTED, UNIMPLEMENTED, UNIMPLEMENTED }; /* FIXME - these are guesses only... */ GSM_Information MB61_Information = { "5160|6160|6185", /* Models */ 4, /* Max RF Level */ 0, /* Min RF Level */ GRF_Arbitrary, /* RF level units */ 4, /* Max Battery Level */ 0, /* Min Battery Level */ GBU_Arbitrary, /* Battery level units */ GDT_None, /* No date/time support */ GDT_None, /* No alarm support */ 0, /* Max alarms = 0 */ 0, 0, /* Startup logo size */ 0, 0, /* Op logo size */ 0, 0 /* Caller logo size */ }; /* Local variables */ pthread_t Thread; bool RequestTerminate; bool MB61_LinkOK; char PortDevice[GSM_MAX_DEVICE_NAME_LENGTH]; u8 RequestSequenceNumber; /* 2-63 */ int PortFD; bool GotInitResponse; bool GotIDResponse; enum MB61_RX_States RX_State; enum MB61_Models ModelIdentified; enum MB61_Responses ExpectedResponse; enum MB61_Responses LatestResponse; GSM_PhonebookEntry *CurrentPhonebookEntry; GSM_Error CurrentPhonebookError; int MessageLength; u8 MessageDestination; u8 MessageSource; u8 MessageCommand; u8 MessageBuffer[MB61_MAX_RECEIVE_LENGTH]; u8 MessageCSum; u8 MessageSequenceNumber; int BufferCount; u8 CalculatedCSum; int ChecksumFails; /* The following functions are made visible to gsm-api.c and friends. */ /* Initialise variables and state machine. */ GSM_Error MB61_Initialise(char *port_device, char *initlength, GSM_ConnectionType connection, void (*rlp_callback)(RLP_F96Frame *frame)) { int rtn; RequestTerminate = false; MB61_LinkOK = false; ModelIdentified = MB61_ModelUnknown; ExpectedResponse = MB61_Response_Unknown; CurrentPhonebookEntry = NULL; CurrentPhonebookError = GE_NONE; strncpy (PortDevice, port_device, GSM_MAX_DEVICE_NAME_LENGTH); /* Create and start thread, */ rtn = pthread_create(&Thread, NULL, (void *) MB61_ThreadLoop, (void *)NULL); if (rtn == EAGAIN || rtn == EINVAL) { return (GE_INTERNALERROR); } return (GE_NONE); } /* Applications should call MB61_Terminate to shut down the MB61 thread and close the serial port. */ void MB61_Terminate(void) { /* Request termination of thread */ RequestTerminate = true; /* Now wait for thread to terminate. */ pthread_join(Thread, NULL); /* Close serial port. */ } /* Routine to get specifed phone book location. Designed to be called by application. Will block until location is retrieved or a timeout/error occurs. */ GSM_Error MB61_GetMemoryLocation(GSM_PhonebookEntry *entry) { int timeout; if (entry->MemoryType != GMT_ME) { return (GE_INVALIDMEMORYTYPE); } timeout = 20; /* 2 seconds for command to complete */ /* Return if no link has been established. */ if (!MB61_LinkOK) { return GE_NOLINK; } /* Process depending on model identified */ switch (ModelIdentified) { case MB61_Model5160: if (entry->Location >= MAX_5160_PHONEBOOK_ENTRIES) { return (GE_INVALIDPHBOOKLOCATION); } CurrentPhonebookEntry = entry; CurrentPhonebookError = GE_BUSY; MB61_SetExpectedResponse(MB61_Response_0x40_PhonebookRead); MB61_TX_SendPhonebookReadRequest(entry->Location); break; case MB61_Model6160: if (entry->Location >= MAX_6160_PHONEBOOK_ENTRIES) { return (GE_INVALIDPHBOOKLOCATION); } CurrentPhonebookEntry = entry; CurrentPhonebookError = GE_BUSY; MB61_SetExpectedResponse(MB61_Response_0x40_PhonebookRead); MB61_TX_SendPhonebookReadRequest(entry->Location); break; case MB61_Model6185: if (entry->Location >= MAX_6185_PHONEBOOK_ENTRIES) { return (GE_INVALIDPHBOOKLOCATION); } CurrentPhonebookEntry = entry; CurrentPhonebookError = GE_BUSY; MB61_SetExpectedResponse(MB61_Response_0x40_LongPhonebookRead); MB61_TX_SendLongPhonebookReadRequest(entry->Location); break; default: return(GE_NOTIMPLEMENTED); } /* When response is received, data is copied into entry by handler code or if error has occured, CurrentPhonebookEntry is set accordingly. */ if (MB61_WaitForExpectedResponse(2000) != true) { return (GE_INTERNALERROR); } return (CurrentPhonebookError); } /* Routine to write phonebook location in phone. Designed to be called by application code. Will block until location is written or timeout occurs. */ GSM_Error MB61_WritePhonebookLocation(GSM_PhonebookEntry *entry) { if (entry->MemoryType != GMT_ME) { return (GE_INVALIDMEMORYTYPE); } /* Return if no link has been established. */ if (!MB61_LinkOK) { return GE_NOLINK; } /* Process depending on model identified */ switch (ModelIdentified) { case MB61_Model5160: if (entry->Location >= MAX_5160_PHONEBOOK_ENTRIES) { return (GE_INVALIDPHBOOKLOCATION); } if (strlen(entry->Name) > MAX_5160_PHONEBOOK_NAME_LENGTH) { return (GE_PHBOOKNAMETOOLONG); } if (strlen(entry->Name) > MAX_5160_PHONEBOOK_NUMBER_LENGTH) { return (GE_PHBOOKNAMETOOLONG); } CurrentPhonebookError = GE_BUSY; MB61_SetExpectedResponse(MB61_Response_0x40_WriteAcknowledge); MB61_TX_SendPhonebookWriteRequest(entry); break; case MB61_Model6160: if (entry->Location >= MAX_6160_PHONEBOOK_ENTRIES) { return (GE_INVALIDPHBOOKLOCATION); } if (strlen(entry->Name) > MAX_616X_PHONEBOOK_NAME_LENGTH) { return (GE_PHBOOKNAMETOOLONG); } if (strlen(entry->Name) > MAX_616X_PHONEBOOK_NUMBER_LENGTH) { return (GE_PHBOOKNAMETOOLONG); } CurrentPhonebookError = GE_BUSY; MB61_SetExpectedResponse(MB61_Response_0x40_WriteAcknowledge); MB61_TX_SendPhonebookWriteRequest(entry); break; case MB61_Model6185: return (GE_NOTIMPLEMENTED); break; default: return(GE_NOTIMPLEMENTED); } /* When response is received, data is copied into entry by handler code or if error has occured, CurrentPhonebookEntry is set accordingly. */ if (MB61_WaitForExpectedResponse(2000) != true) { return (GE_INTERNALERROR); } return (CurrentPhonebookError); } bool MB61_SendRLPFrame(RLP_F96Frame *frame, bool out_dtx) { return (false); } /* Everything from here down is internal to 6160 code. */ /* This is the main loop for the MB61 functions. When MB61_Initialise is called a thread is created to run this loop. This loop is exited when the application calls the MB61_Terminate function. */ void MB61_ThreadLoop(void) { int idle_timer; /* Initialise RX state machine. */ BufferCount = 0; RX_State = MB61_RX_Sync; idle_timer = 0; /* Try to open serial port, if we fail we sit here and don't proceed to the main loop. */ if (MB61_OpenSerial() != true) { MB61_LinkOK = false; while (!RequestTerminate) { usleep (100000); } return; } /* Do initialisation sequence, sit here if it fails. */ if (MB61_InitialiseLink() != true) { MB61_LinkOK = false; while (!RequestTerminate) { usleep (100000); } return; } /* Link is up OK so sit here twiddling our thumbs until told to terminate. */ while (!RequestTerminate) { if (idle_timer == 0) { idle_timer = 20; } else { idle_timer --; /*fprintf(stdout, "."); fflush(stdout);*/ } usleep(100000); /* Avoid becoming a "busy" loop. */ } /* Drop DTR and RTS lines before exiting */ device_setdtrrts(0, 0); } bool MB61_InitialiseLink(void) { unsigned char init_char[1] = {0x04}; fprintf(stdout, "Sending init...\n"); /* Need to "toggle" the dtr/rts lines in the right sequence it seems for the interface to work. Base time value is units of 50ms it seems */ #define BASE_TIME (50000) /* Default state */ device_setdtrrts(0, 1); sleep(1); /* RTS low for 250ms */ device_setdtrrts(0, 0); usleep(5 * BASE_TIME); /* RTS high, DTR high for 50ms */ device_setdtrrts(1, 1); usleep(BASE_TIME); /* RTS low, DTR high for 50ms */ device_setdtrrts(1, 0); usleep(BASE_TIME); /* RTS high, DTR high for 50ms */ device_setdtrrts(1, 1); usleep(BASE_TIME); /* RTS low, DTR high for 50ms */ device_setdtrrts(1, 0); usleep(BASE_TIME); /* RTS low, DTR low for 50ms */ device_setdtrrts(0, 0); usleep(BASE_TIME); /* RTS low, DTR high for 50ms */ device_setdtrrts(1, 0); usleep(BASE_TIME); /* RTS high, DTR high for 50ms */ device_setdtrrts(1, 1); usleep(BASE_TIME); /* RTS low, DTR low for 50ms */ device_setdtrrts(0, 0); usleep(BASE_TIME); /* leave RTS high, DTR low for duration of session. */ usleep(BASE_TIME); device_setdtrrts(0, 1); sleep(1); /* Initialise sequence number used when sending messages to phone. */ /* Send Initialisation message to phone. */ MB61_SetExpectedResponse(MB61_Response_0xD0_Init); RequestSequenceNumber = 0x02; MB61_TX_SendMessage(MSG_ADDR_PHONE, MSG_ADDR_PC, 0xd0, RequestSequenceNumber, 1, init_char); /* We've now finished initialising things so sit in the loop until told to do otherwise. Loop doesn't do much other than send periodic keepalive messages to phone. This loop will become more involved once we start doing fax/data calls. */ fprintf(stdout, "Waiting for first response...\n"); fflush(stdout); if(MB61_WaitForExpectedResponse(100) == false) { return false; } MB61_SetExpectedResponse(MB61_Response_0xD0_Init); RequestSequenceNumber ++; MB61_TX_SendMessage(MSG_ADDR_PHONE, MSG_ADDR_PC, 0xd0, RequestSequenceNumber, 1, init_char); fprintf(stdout, "Waiting for second response...\n"); fflush(stdout); if(MB61_WaitForExpectedResponse(100) == false) { return false; } MB61_SetExpectedResponse(MB61_Response_0xD0_Init); MB61_TX_SendPhoneIDRequest(); if(MB61_WaitForExpectedResponse(300) == false) { return false; } MB61_LinkOK = true; return (true); } /* MB61_SetExpectedResponse Used in combination with MB61_WaitForExpectedResponse, these functions allow the MB61 code to specify what message it is expecting a response to. This becomes important as it appears that there is no standard or unique character to identify incoming messages from the phone. A good example is the ID and Version responses which differ only in their length and one of the bytes in the data portion of the message. It may be that once we understand more of the MBUS protocol we can confirm that messages are unique... */ void MB61_SetExpectedResponse(enum MB61_Responses response) { LatestResponse = MB61_Response_Unknown; ExpectedResponse = response; } /* MB61_WaitForExpectedResponse Allows code to wait a specified number of msecs for the requested response before timing out. Returns true if expected response has been recieved, false in case of timeout */ bool MB61_WaitForExpectedResponse(int timeout) { int count; count = timeout; while ((count > 0) && (LatestResponse != ExpectedResponse)) { count --; usleep(1000); } if (LatestResponse == ExpectedResponse) { return (true); } return (false); } /* MB61_RX_DispatchMessage Once we've received a message from the phone, the command/message type byte is used to call an appropriate handler routine or simply acknowledge the message as required. */ enum MB61_RX_States MB61_RX_DispatchMessage(void) { /* If the message is from ADDR_PC ignore and don't process further. */ if (MessageSource == MSG_ADDR_PC) { return MB61_RX_Sync; } /* Leave this uncommented if you want all messages in raw form. */ //MB61_RX_DisplayMessage(); /* Switch on the basis of the message type byte */ switch (MessageCommand) { case 0x40: if (MB61_TX_SendStandardAcknowledge(MessageSequenceNumber) != true) { fprintf(stderr, _("Standard Ack write (0x40) failed!")); } if (ExpectedResponse == MB61_Response_0x40_PhonebookRead) { MB61_RX_Handle0x40_PhonebookRead(); LatestResponse = MB61_Response_0x40_PhonebookRead; break; } if (ExpectedResponse == MB61_Response_0x40_WriteAcknowledge) { LatestResponse = MB61_Response_0x40_WriteAcknowledge; CurrentPhonebookError = GE_NONE; break; } break; /* 0xd0 messages are the response to initialisation requests. */ case 0xd0: if (ExpectedResponse == MB61_Response_0xD0_Init) { LatestResponse = MB61_Response_0xD0_Init; } break; case 0xd2: if (MB61_TX_SendStandardAcknowledge(MessageSequenceNumber) != true) { fprintf(stderr, _("Standard Ack write (0xd2) failed!")); } if (ExpectedResponse == MB61_Response_0xD2_ID) { MB61_RX_Handle0xD2_ID(); LatestResponse = MB61_Response_0xD2_ID; } if (ExpectedResponse == MB61_Response_0xD2_Version) { MB61_RX_Handle0xD2_Version(); LatestResponse = MB61_Response_0xD2_Version; } break; /* Incoming 0x7f's are acks for commands we've sent. */ case 0x7f: break; /* Here we attempt to acknowledge and display messages we don't understand fully... The phone will send the same message several (3-4) times before giving up if no ack is received. */ default: MB61_RX_DisplayMessage(); if (MB61_TX_SendStandardAcknowledge(MessageSequenceNumber) != true) { fprintf(stderr, _("Standard Ack write failed!")); } fprintf(stdout, "Sent standard Ack for unknown %02x\n", MessageCommand); break; } return MB61_RX_Sync; } /* "Short" phonebook reads have 8 bytes of data (unknown/unstudied) then a null terminated string for the number and then a null terminated string which is the name. */ void MB61_RX_Handle0x40_PhonebookRead(void) { int i, j; bool got_null; if (CurrentPhonebookEntry == NULL) { CurrentPhonebookError = GE_INTERNALERROR; return; } /* First do number */ i = 8; got_null = false; j = 0; while ((i < MessageLength) && (!got_null) && (j < GSM_MAX_PHONEBOOK_NUMBER_LENGTH)) { CurrentPhonebookEntry->Number[j] = MessageBuffer[i]; i++; j++; if (MessageBuffer[i] == 0) { got_null = true; } } CurrentPhonebookEntry->Number[j] = 0; /* Now name */ got_null = false; j = 0; i ++; while ((i < MessageLength) && (!got_null) && (j < GSM_MAX_PHONEBOOK_NAME_LENGTH)) { CurrentPhonebookEntry->Name[j] = MessageBuffer[i]; i++; j++; if (MessageBuffer[i] == 0) { got_null = true; } } CurrentPhonebookEntry->Name[j] = 0; if ((strlen(CurrentPhonebookEntry->Number) != 0) || (strlen(CurrentPhonebookEntry->Name) != 0)) { CurrentPhonebookEntry->Empty = false; } else { CurrentPhonebookEntry->Empty = true; } CurrentPhonebookEntry->Group = GSM_GROUPS_NOT_SUPPORTED; /* Done */ CurrentPhonebookError = GE_NONE; } void MB61_RX_Handle0x40_LongPhonebookRead(void) { } /* When we get an ID response back, we use it to set model information for later and if in debug mode print it out. */ void MB61_RX_Handle0xD2_ID(void) { #ifdef DEBUG int i; #endif if (strstr(MessageBuffer + 4, "NSW-1") != NULL) { ModelIdentified = MB61_Model5160; #ifdef DEBUG fprintf(stdout, "Identified as 5160\n"); #endif } else { if (strstr(MessageBuffer + 4, "NSW-3") != NULL) { ModelIdentified = MB61_Model6160; #ifdef DEBUG fprintf(stdout, "Identified as 6160\n"); #endif } else { if (strstr(MessageBuffer + 4, "NSD-3") != NULL) { ModelIdentified = MB61_Model6185; #ifdef DEBUG fprintf(stdout, "Identified as 6185\n"); #endif } else { #ifdef DEBUG fprintf(stdout, "Unknown model - please report dump below to hugh@linuxcare.com\n"); #endif } } } #ifdef DEBUG for (i = 4; i < MessageLength; i++) { if (isprint(MessageBuffer[i])) { fprintf(stdout, "[%02x%c]", MessageBuffer[i], MessageBuffer[i]); } else { fprintf(stdout, "[%02x ]", MessageBuffer[i]); } if (((i - 3) % 16) == 0) { fprintf(stdout, "\n"); } } #endif #ifdef DEBUG fflush(stdout); #endif } void MB61_RX_Handle0xD2_Version(void) { } void MB61_RX_DisplayMessage(void) { int i; fprintf(stdout, "Dest:%02x Src:%02x Cmd:%02x Len:%d Seq:%02x Csum:%02x\n", MessageDestination, MessageSource, MessageCommand, MessageLength, MessageSequenceNumber, MessageCSum); if (MessageLength == 0) { return; } else { fprintf(stdout, "Data: "); } for (i = 0; i < MessageLength; i++) { if (isprint(MessageBuffer[i])) { fprintf(stdout, "[%02x%c]", MessageBuffer[i], MessageBuffer[i]); } else { fprintf(stdout, "[%02x ]", MessageBuffer[i]); } if (((i + 1) % 8) == 0) { fprintf(stdout, "\n "); } } fprintf(stdout, "\n"); } /* Higher level code does bounds checks for length of name/number as well as entry number. */ GSM_Error MB61_TX_SendPhonebookWriteRequest(GSM_PhonebookEntry *entry) { /* 7 - header and null terminators, 17 - number length, 17 - name length. */ u8 message[7 + 17 + 17]; int name_length; int number_length; int message_length; name_length = strlen(entry->Name); number_length = strlen(entry->Number); /* Header plus two terminating nulls plus name/number themselves */ message_length = 7 + name_length + 1 + number_length + 1; message[0] = 0x00; /* Header bytes, purpose not investigated */ message[1] = 0x01; message[2] = 0x1f; message[3] = 0x01; message[4] = 0x04; message[5] = 0x87; message[6] = entry->Location; strncpy(message + 7, entry->Number, 16); strncpy(message + 8 + number_length, entry->Name, 16); MB61_UpdateSequenceNumber(); MB61_TX_SendMessage(MSG_ADDR_PHONE, MSG_ADDR_PC, 0x40, RequestSequenceNumber, message_length, message); return (GE_NONE); } bool MB61_TX_SendPhonebookReadRequest(u8 entry) { u8 message[7] = {0x00, 0x01, 0x1f, 0x01, 0x04, 0x86, 0x01}; message[6] = entry; MB61_UpdateSequenceNumber(); MB61_TX_SendMessage(MSG_ADDR_PHONE, MSG_ADDR_PC, 0x40, RequestSequenceNumber, 7, message); ExpectedResponse = MB61_Response_0x40_PhonebookRead; return (true); } /* 6185 requires a different phone book request apparently */ bool MB61_TX_SendLongPhonebookReadRequest(u8 entry) { u8 message[8] = {0x00, 0x00, 0x07, 0x11, 0x00, 0x10, 0x00, 0x00}; message[7] = entry; MB61_UpdateSequenceNumber(); MB61_TX_SendMessage(MSG_ADDR_PHONE, MSG_ADDR_PC, 0x40, RequestSequenceNumber, 8, message); ExpectedResponse = MB61_Response_0x40_PhonebookRead; return (true); } void MB61_TX_SendPhoneIDRequest(void) { u8 message[5] = {0x00, 0x01, 0x00, 0x03, 0x00}; MB61_UpdateSequenceNumber(); MB61_TX_SendMessage(MSG_ADDR_PHONE, MSG_ADDR_PC, 0xd1, RequestSequenceNumber, 5, message); ExpectedResponse = MB61_Response_0xD2_ID; } void MB61_UpdateSequenceNumber(void) { RequestSequenceNumber ++; if (RequestSequenceNumber > 63) { RequestSequenceNumber = 2; } } /* Not totally happy with this but it works for now. - HAB 20000602 */ bool MB61_TX_SendStandardAcknowledge(u8 sequence_number) { u8 out_buffer[6]; u8 checksum; int count; out_buffer[0] = 0x1f; out_buffer[1] = MSG_ADDR_PHONE; out_buffer[2] = MSG_ADDR_PC; out_buffer[3] = 0x7f; out_buffer[4] = sequence_number; /* Now calculate checksum over entire message and append to message. */ checksum = 0; for (count = 0; count < 5; count ++) { checksum ^= out_buffer[count]; } out_buffer[5] = checksum; /* Send it out... */ if (WRITEPHONE(PortFD, out_buffer, 6) != 6) { perror(_("TX_SendMessage - write:")); return (false); } #ifdef DEBUG fprintf(stdout, "(Ack %02x) ", sequence_number); #endif return (true); } /* RX_State machine for receive handling. Called once for each character received from the phone/phone. */ void MB61_RX_StateMachine(char rx_byte) { #ifdef DEBUG fprintf(stdout, "(%d)", RX_State); #endif switch (RX_State) { /* Messages on the MBUS start with 0x1f. We use this to "synchronise" with the incoming data stream. */ case MB61_RX_Sync: if (rx_byte == 0x1f) { BufferCount = 0; CalculatedCSum = rx_byte; RX_State = MB61_RX_GetDestination; } break; /* Next byte is the destination of the message. */ case MB61_RX_GetDestination: MessageDestination = rx_byte; CalculatedCSum ^= rx_byte; RX_State = MB61_RX_GetSource; break; /* Next byte is the source of the message. */ case MB61_RX_GetSource: MessageSource = rx_byte; CalculatedCSum ^= rx_byte; /* Sanity check these. We make sure that the Source and destination are either PC/PHONE or PHONE/PC */ if (((MessageSource == MSG_ADDR_PC) && (MessageDestination == MSG_ADDR_PHONE)) || ((MessageSource == MSG_ADDR_PHONE) && (MessageDestination == MSG_ADDR_PC))) { RX_State = MB61_RX_GetCommand; } else { RX_State = MB61_RX_Sync; } break; /* Next byte is the command type. */ case MB61_RX_GetCommand: MessageCommand = rx_byte; CalculatedCSum ^= rx_byte; /* Command type 0x7f is an ack and is handled differently in that it's length is known a priori */ if (MessageCommand != 0x7f) { RX_State = MB61_RX_GetLengthMSB; break; } else { MessageLength = 0; RX_State = MB61_RX_GetMessage; break; } /* Next is the most significant byte of message length. */ case MB61_RX_GetLengthMSB: MessageLength = rx_byte * 256; CalculatedCSum ^= rx_byte; RX_State = MB61_RX_GetLengthLSB; break; /* Next is the most significant byte of message length. */ case MB61_RX_GetLengthLSB: MessageLength += rx_byte; CalculatedCSum ^= rx_byte; RX_State = MB61_RX_GetMessage; break; /* Get each byte of the message. We deliberately get one too many bytes so we get the sequence byte here as well. */ case MB61_RX_GetMessage: CalculatedCSum ^= rx_byte; MessageBuffer[BufferCount] = rx_byte; BufferCount ++; if (BufferCount >= MB61_MAX_RECEIVE_LENGTH) { RX_State = MB61_RX_Sync; /* Should be PANIC */ } /* If this is the last byte, it's the checksum */ if (BufferCount > MessageLength) { MessageSequenceNumber = rx_byte; RX_State = MB61_RX_GetCSum; } break; /* Get checksum and if valid hand over to dispatch message function */ case MB61_RX_GetCSum: MessageCSum = rx_byte; /* Compare against calculated checksum. */ if (MessageCSum == CalculatedCSum) { /* Got checksum, matches calculated one so now pass to appropriate dispatch handler. */ RX_State = MB61_RX_DispatchMessage(); } /* Checksum didn't match so ignore. */ else { ChecksumFails ++; fprintf(stderr, _("CS Fail %02x != %02x"), MessageCSum, CalculatedCSum); MB61_RX_DisplayMessage(); fflush(stderr); RX_State = MB61_RX_Sync; } CalculatedCSum ^= rx_byte; break; default: } } /* Called by initialisation code to open comm port in asynchronous mode. */ bool MB61_OpenSerial(void) { int result; struct sigaction sig_io; /* Set up and install handler before enabling async IO on port. */ sig_io.sa_handler = MB61_SigHandler; sig_io.sa_flags = 0; sigaction (SIGIO, &sig_io, NULL); /* Open device MBUS uses 9600,O,1 */ result = device_open(PortDevice, true, true, false, GCT_Serial); if (!result) { perror(_("Couldn't open MB61 device: ")); return (false); } fprintf(stdout, "Opened MB61 device\n"); device_changespeed(9600); return (true); } /* Handler called when characters received from serial port. calls state machine code to process it. */ void MB61_SigHandler(int status) { unsigned char buffer[255]; int count,res; res = device_read(buffer, 255); for (count = 0; count < res ; count ++) { MB61_RX_StateMachine(buffer[count]); #ifdef DEBUG if (isprint(buffer[count])) { fprintf(stdout, "<%02x%c>", buffer[count], buffer[count]); } else { fprintf(stdout, "<%02x >", buffer[count]); } #endif } fflush(stdout); } /* Prepares the message header and sends it, prepends the message start byte (0x01) and other values according the value specified when called. Calculates checksum and then sends the lot down the pipe... */ int MB61_TX_SendMessage(u8 destination, u8 source, u8 command, u8 sequence_byte, int message_length, u8 *buffer) { u8 out_buffer[MB61_MAX_TRANSMIT_LENGTH + 7]; int count; unsigned char checksum; /* Check message isn't too long, once the necessary header and trailer bytes are included. */ if ((message_length + 7) > MB61_MAX_TRANSMIT_LENGTH) { fprintf(stderr, _("TX_SendMessage - message too long!\n")); return (false); } //RX_State = MB61_RX_Sync; Hack. /* Now construct the message header. */ out_buffer[0] = 0x1f; /* Start of message indicator */ out_buffer[1] = destination; out_buffer[2] = source; out_buffer[3] = command; out_buffer[4] = message_length >> 8; out_buffer[5] = message_length & 0xff; /* Copy in data if any. */ if (message_length != 0) { memcpy(out_buffer + 6, buffer, message_length); } /* Copy in sequence number */ out_buffer[message_length + 6] = sequence_byte; /* Now calculate checksum over entire message and append to message. */ checksum = 0; for (count = 0; count < message_length + 7; count ++) { checksum ^= out_buffer[count]; } out_buffer[message_length + 7] = checksum; /* Send it out... */ if (WRITEPHONE(PortFD, out_buffer, message_length + 8) != message_length + 8) { perror(_("TX_SendMessage - write:")); return (false); } #ifdef DEBUG for (count = 0; count < message_length + 8; count++) { if (isprint(out_buffer[count])) { fprintf(stdout, "{%02x%c}", out_buffer[count], out_buffer[count]); } else { fprintf(stdout, "{%02x }", out_buffer[count]); } if (((count + 1) % 16) == 0) { fprintf(stdout, "\n"); } } fflush(stdout); #endif return (true); } #endif