/*(C) copyright 2008, Steven Snyder, All Rights Reserved Steven T. Snyder, http://www.steventsnyder.com LICENSING INFORMATION: This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include #include #include #include #include // for command line parsing #include #include #include // for memcpy #include #include "linuxaldl.h" #include "sts_serial.h" #include "linuxaldl_definitions.h" #include "linuxaldl_gui.h" // global variables // ================================================= linuxaldl_settings aldl_settings = { .aldl_definition_table = aldl_definition_table, .scan_interval = 150, .scan_timeout = 100, }; // ============================================================================ // // linuxaldl // main () // // ============================================================================ int main(int argc, const char* argv[]){ int res; // temporary storage for function results int guimode = 0; // ======================================================================== // COMMAND LINE OPTION PARSING // ======================================================================== poptContext popt_aldl; // popt option table // ----------------- struct poptOption aldl_opt_table[] = { { "serial",'\0', POPT_ARG_STRING | POPT_ARGFLAG_ONEDASH,&aldl_settings.aldlportname,0, "Serial port the aldl interface is on", "/dev/ttyUSB0" }, { "mask",'\0', POPT_ARG_STRING | POPT_ARGFLAG_ONEDASH,&aldl_settings.aldldefname,0, "ALDL code definition to use", "DF"}, POPT_AUTOHELP { NULL, 0, 0, NULL, 0, 0, NULL} }; // popt context popt_aldl = poptGetContext(NULL, argc, argv,aldl_opt_table,0); poptSetOtherOptionHelp(popt_aldl,"[logfile.log]\nTo use GUI: linuxaldl [-serial=/dev/ttyUSB0]"); if (argc < 2) { poptPrintUsage(popt_aldl,stderr,0); return 1; } res = poptGetNextOpt(popt_aldl); // parse the command line arguments // if no serial port selected, print usage instructions and exit if (aldl_settings.aldlportname == NULL) { poptPrintUsage(popt_aldl,stderr,0); return 1; } aldl_settings.logfilename = (char*)poptGetArg(popt_aldl); // if no log file specified or no definition file specified if (aldl_settings.logfilename == NULL || poptPeekArg(popt_aldl) != NULL || aldl_settings.aldldefname == NULL) { guimode = 1; } else { // In command line mode, choose definition using the -mask=DEF argument // get the aldl definition address aldl_settings.definition = aldl_get_definition(NULL, aldl_settings.aldldefname); // if no definition by that name exists, exit if (aldl_settings.definition == NULL) { fprintf(stderr,"Error: No definition with mask \"%s\" found.", aldl_settings.aldldefname); fprintf(stderr,"Consult the documentation.\n"); fprintf(stderr," Note: definition names are case sensitive.\n"); return -1; } else { fprintf(stderr, "Definition \"%s\" selected.\n", aldl_settings.definition->name); } } poptFreeContext(popt_aldl); // free the popt context // ======================================================= // CONNECT / VERIFY ALDL INTERFACE // ======================================================= // Establish a connection to the aldl // =================================== printf("Trying to connect to ALDL interface on %s...\n", aldl_settings.aldlportname); // connect to the device and set the serial port settings aldl_settings.faldl = serial_connect(aldl_settings.aldlportname, O_RDWR | O_NOCTTY | O_NONBLOCK, aldl_settings.definition->basic_baudrate); if (aldl_settings.faldl == -1) { fprintf(stderr," Couldn't open to %s\n",aldl_settings.aldlportname); return -1; } /* Set custom baud rate: ALDL has two variants; 1) 160 baud with non-RS232 framing. Clock up to 1600 baud and take one byte from each bit to get us the 8 data bits plus one start and one stop bit. 2) 8192 baud with RS232 framing. Prefer to set exact baud rate if possible, but they are non-standard PC datarates.. 8192 should work at 9600; the packets are short enough that framing errors should be minimal and checksums will catch them anyway. */ aldl_settings.customrate = 1; if (set_custom_baud_rate(aldl_settings.faldl, aldl_settings.definition->ideal_baudrate)!=0) { fprintf(stderr," Couldn't set baud rate to %d. Using standard rate (%d).\n", aldl_settings.definition->ideal_baudrate, aldl_settings.definition->basic_baudrate); fprintf(stderr," There may be framing errors.\n"); aldl_settings.customrate = 0; } // verify the aldl if (verifyaldl()<0) { fprintf(stderr," ALDL verification failure. No response from ECM.\n"); tcflush(aldl_settings.faldl, TCIOFLUSH); close(aldl_settings.faldl); return -1; } // GUI mode if no .log file specified at the command line if (guimode) { // GUI mode linuxaldl_gui(argc, (char**)argv); } else { // Open the .log file for writing, create if it doesnt exist // ------------------------------------------------------------ aldl_settings.flogfile = open(aldl_settings.logfilename,O_RDWR | O_CREAT, 0666); if (aldl_settings.flogfile == -1) { fprintf(stderr,"Unable to open/create %s for writing.\n",aldl_settings.logfilename); return 0; } printf("Opened %s for writing.\n",aldl_settings.logfilename); // Read from the aldl // ------------------ res = aldl_scan_and_log(aldl_settings.flogfile); if (res==-1) fprintf(stderr,"Error: Fatal read error occured. log file may be corrupted.\n"); else printf("Received %d bytes from device and wrote to file: %s.\n", res,aldl_settings.logfilename); close(aldl_settings.flogfile); } // ============================================================ // CLEANUP (FLUSH SERIAL LINE, CLOSE PORT) // ============================================================ // discard any unwritten data tcflush(aldl_settings.faldl, TCIOFLUSH); // close the port close(aldl_settings.faldl); printf("Connection closed.\n"); return 0; } // ============================================================ // // linuxaldl general function definitions // // ============================================================ // (mostly used for the command line interface but also main()) // wake up / verify the aldl // This function should check that the aldl interface is working by listening // for ECM chatter, or some other operation to determine whether there is // a line connected to the serial port. int verifyaldl() { //XXX NOT IMPLEMENTED return 0; } int aldl_scan_and_log(int fd) { //XXX NOT IMPLEMENTED return 0; } // sends an artibtrary aldl message contained in the buffer msg_buf. // the checksum must be set in the buffer by the caller. // the following macros can be used as arguments: // _ALDL_MESSAGE_MODE8 // _ALDL_MESSAGE_MODE9 // which use the mode 8 and mode 9 message definitions from the // current aldl definition. // returns 0 on success. int send_aldl_message(char* msg_buf, unsigned int size) { int res; #ifdef _LINUXALDL_DEBUG printf("Sending sequence: "); fprinthex(stdout,msg_buf,size); printf("\n"); #endif res = write(aldl_settings.faldl,&msg_buf,size); if (res <= 0) return -1; else if ((unsigned)resmode1_response_length; if (size < mode1_len) { printf("Read buffer must be at least %d bytes.\n",mode1_len); return -1; } /* Enter Mode1 if required */ if (def->mode1_request_length) { // put the mode 1 request message and checksum in the output buffer memcpy(outbuffer, def->mode1_request, def->mode1_request_length-1); outbuffer[def->mode1_request_length-1] = get_checksum(outbuffer, def->mode1_request_length-1); // form the response message start sequence char seq[] = { def->mode1_request[0], 0x52+def->mode1_response_length, 0x01}; // flush the serial receive buffer tcflush(aldl_settings.faldl,TCIFLUSH); // write the request to the serial interface write(aldl_settings.faldl,&outbuffer,def->mode1_request_length); // wait for the bytes to be written tcdrain(aldl_settings.faldl); // wait for response from ECM // read sequence, 50msec timeout res=read_sequence(aldl_settings.faldl, inbuffer, mode1_len, seq, 3, 0, aldl_settings.scan_timeout*1000); } else { /* Wait for Framing START ? */ // XXX what sequence to match for 160baud rate? } if (res<0) { fprintf(stderr,"Error receiving mode1 message: %s\n",strerror(errno)); return -1; } if ((unsigned)resmask)) break; if (defname && !strcmp(defname,result->name)) break; result = aldl_definition_table[++index]; } return result; } // updates data_set_floats and/or data_set_strings using the current data_set_raw bytes. // if the flags argument is ALDL_UPDATE_STRINGS then only sets will be updated. // if flags is ALDL_UPDATE_FLOATS then only floats will be updated, and the data_set_strings // array will not be modified in any way. // if flags is ALDL_UPDATE_FLOATS|ALDL_UPDATE_STRINGS, then both will be updated. // if the aldl_settings.data_set_floats or aldl_settings.data_set_strings arrays have // not yet been initialized (e.g. are NULL pointers) then they will not be modified. void aldl_update_sets(int flags) { unsigned int i=0; byte_def_t* defs = aldl_settings.definition->mode1_def; byte_def_t* cur_def; for (i = 0 ; defs[i].label ; i++) { cur_def = defs+i; switch (cur_def->operation) { case ALDL_OP_SCALAR: { float converted_val; char *new_data_string = NULL; if (cur_def->bits == 8) { converted_val = (((float)aldl_settings.data_set_raw[cur_def->byte_offset-1]) * cur_def->op_factor) + cur_def->op_offset; } else if (cur_def->bits == 16) { converted_val = ((float)aldl_settings.data_set_raw[cur_def->byte_offset-1]) * 256 + ((float)aldl_settings.data_set_raw[cur_def->byte_offset]); converted_val = (converted_val*cur_def->op_factor) + cur_def->op_offset; } else { // XXX log some sort of error for non 8/16bpp? continue; } if ((flags & ALDL_UPDATE_FLOATS) && aldl_settings.data_set_floats != NULL) aldl_settings.data_set_floats[i] = converted_val; // convert the result to a string if ((flags & ALDL_UPDATE_STRINGS) && aldl_settings.data_set_strings != NULL) { new_data_string=malloc(10); // allocate ten bytes for the string snprintf(new_data_string,10,"%.1f",converted_val); // convert the floating point value to a string // if there is currently a string registered, free it if (aldl_settings.data_set_strings[i] != NULL) free(aldl_settings.data_set_strings[i]); // register the new string in the table aldl_settings.data_set_strings[i] = new_data_string; } break; } case ALDL_OP_MAP: { float converted_val; char *new_data_string = NULL; if (cur_def->bits == 8) { converted_val = cur_def->map[(unsigned char)aldl_settings.data_set_raw[cur_def->byte_offset-1]]; } else { // XXX log some sort of error for non-8bpp? continue; } if ((flags & ALDL_UPDATE_FLOATS) && aldl_settings.data_set_floats != NULL) aldl_settings.data_set_floats[i] = converted_val; // convert the result to a string if ((flags & ALDL_UPDATE_STRINGS) && aldl_settings.data_set_strings != NULL) { new_data_string=malloc(10); // allocate ten bytes for the string snprintf(new_data_string,10,"%.1f",converted_val); // convert the floating point value to a string // if there is currently a string registered, free it if (aldl_settings.data_set_strings[i] != NULL) free(aldl_settings.data_set_strings[i]); // register the new string in the table aldl_settings.data_set_strings[i] = new_data_string; } break; } case ALDL_OP_BIT: if (flags & ALDL_UPDATE_FLOATS) { aldl_settings.data_set_floats[i] = (aldl_settings.data_set_raw[cur_def->byte_offset-1] & cur_def->b_bit) ? 1 : 0; } if (flags & ALDL_UPDATE_STRINGS) { // if there is currently a string registered, free it if (aldl_settings.data_set_strings[i] != NULL) free(aldl_settings.data_set_strings[i]); // register the new string in the table aldl_settings.data_set_strings[i] = strdup((aldl_settings.data_set_raw[cur_def->byte_offset-1] & cur_def->b_bit) ? cur_def->b_set : cur_def->b_unset); } break; case ALDL_OP_SEPERATOR: break; /* Deliberately ignore this */ default: // XXX log some sort of error here? continue; } //fprintf(stderr,"Updating element %s\n",cur_def->label); } }