/* * * This file is part of Maemo Mapper. * * Maemo Mapper 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. * * Maemo Mapper 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 Maemo Mapper. If not, see . * * * Parts of this code have been ported from Xastir by Rob Williams (10 Aug 2008): * * * XASTIR, Amateur Station Tracking and Information Reporting * Copyright (C) 1999,2000 Frank Giannandrea * Copyright (C) 2000-2007 The Xastir Group * */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #ifdef INCLUDE_APRS #include "aprs_kiss.h" #include "aprs.h" #include "defines.h" #include "data.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Moved ahead of inet.h as reports of some *BSD's not // including this as they should. #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #define DBUS_API_SUBJECT_TO_CHANGE #include #define DISABLE_SETUID_PRIVILEGE do { \ seteuid(getuid()); \ setegid(getgid()); \ } while(0) #define ENABLE_SETUID_PRIVILEGE do { \ seteuid(euid); \ setegid(egid); \ } while(0) #define MAX_INPUT_QUEUE 1000 gint decode_ax25_line(gchar *line, TAprsPort port); typedef struct { pthread_mutex_t lock; pthread_t threadID; } xastir_mutex; xastir_mutex connect_lock; // Protects port_data[].thread_status and port_data[].connect_status // Read/write pointers for the circular input queue /* static int incoming_read_ptr = 0; static int incoming_write_ptr = 0; static int queue_depth = 0; static int push_count = 0; static int pop_count = 0; */ uid_t euid; gid_t egid; iface port_data; // shared port data //WE7U2 // We feed a raw 7-byte string into this routine. It decodes the // callsign-SSID and tells us whether there are more callsigns after // this. If the "asterisk" input parameter is nonzero it'll add an // asterisk to the callsign if it has been digipeated. This // function is called by the decode_ax25_header() function. // // Inputs: string Raw input string // asterisk 1 = add "digipeated" asterisk // // Outputs: callsign Processed string // returned int 1=more callsigns follow, 0=end of address field // gint decode_ax25_address(gchar *string, gchar *callsign, gint asterisk) { gint i,j; gchar ssid; gchar t; gint more = 0; gint digipeated = 0; // Shift each of the six callsign characters right one bit to // convert to ASCII. We also get rid of the extra spaces here. j = 0; for (i = 0; i < 6; i++) { t = ((unsigned char)string[i] >> 1) & 0x7f; if (t != ' ') { callsign[j++] = t; } } // Snag out the SSID byte to play with. We need more than just // the 4 SSID bits out of it. ssid = (unsigned char)string[6]; // Check the digipeat bit if ( (ssid & 0x80) && asterisk) digipeated++; // Has been digipeated // Check whether it is the end of the address field if ( !(ssid & 0x01) ) more++; // More callsigns to come after this one // Snag the four SSID bits ssid = (ssid >> 1) & 0x0f; // Construct the SSID number and add it to the end of the // callsign if non-zero. If it's zero we don't add it. if (ssid) { callsign[j++] = '-'; if (ssid > 9) { callsign[j++] = '1'; } ssid = ssid % 10; callsign[j++] = '0' + ssid; } // Add an asterisk if the packet has been digipeated through // this callsign if (digipeated) callsign[j++] = '*'; // Terminate the string callsign[j] = '\0'; return(more); } // Function which receives raw AX.25 packets from a KISS interface and // converts them to a printable TAPR-2 (more or less) style string. // We receive the packet with a KISS Frame End character at the // beginning and a "\0" character at the end. We can end up with // multiple asterisks, one for each callsign that the packet was // digipeated through. A few other TNC's put out this same sort of // format. // // Note about KISS & CRC's: The TNC checks the CRC. If bad, it // drops the packet. If good, it sends it to the computer WITHOUT // the CRC bytes. There's no way at the computer end to check // whether the packet was corrupted over the serial channel between // the TNC and the computer. Upon sending a KISS packet to the TNC, // the TNC itself adds the CRC bytes back on before sending it over // the air. In Xastir we can just assume that we're getting // error-free packets from the TNC, ignoring possible corruption // over the serial line. // // Some versions of KISS can encode the radio channel (for // multi-port TNC's) in the command byte. How do we know we're // running those versions of KISS though? Here are the KISS // variants that I've been able to discover to date: // // KISS No CRC, one radio port // // SMACK 16-bit CRC, multiport TNC's // // KISS-CRC // // 6-PACK // // KISS Multi-drop (Kantronics) 8-bit XOR Checksum, multiport TNC's (AGWPE compatible) // BPQKISS (Multi-drop) 8-bit XOR Checksum, multiport TNC's // XKISS (Kantronics) 8-bit XOR Checksum, multiport TNC's // // JKISS (AGWPE and BPQ32 compatible) // // MKISS Linux driver which supports KISS/BPQ and // hardware handshaking? Also Paccomm command to // immediately enter KISS mode. // // FlexKISS -, // FlexCRC -|-- These are all the same! // RMNC-KISS -| // CRC-RMNC -' // // // It appears that none of the above protocols implement any form of // hardware flow control. // // // Compare this function with interface.c:process_ax25_packet() to // see if we're missing anything important. // // // Inputs: data_string Raw string (must be MAX_LINE_SIZE or bigger) // length Length of raw string (may get changed here) // // Outputs: int 0 if it is a bad packet, // 1 if it is good // data_string Processed string // gint decode_ax25_header( unsigned char *data_string, gint *length) { gchar temp[20]; gchar result[MAX_LINE_SIZE+100]; gchar dest[15]; gint i, ptr; gchar callsign[15]; gchar more; gchar num_digis = 0; // Do we have a string at all? if (data_string == NULL) return(0); // Drop the packet if it is too long. Note that for KISS packets // we can't use strlen() as there can be 0x00 bytes in the // data itself. if (*length > 1024) { data_string[0] = '\0'; *length = 0; return(0); } // Start with an empty string for the result result[0] = '\0'; ptr = 0; // Process the destination address for (i = 0; i < 7; i++) temp[i] = data_string[ptr++]; temp[7] = '\0'; more = decode_ax25_address(temp, callsign, 0); // No asterisk snprintf(dest,sizeof(dest),"%s",callsign); // Process the source address for (i = 0; i < 7; i++) temp[i] = data_string[ptr++]; temp[7] = '\0'; more = decode_ax25_address(temp, callsign, 0); // No asterisk // Store the two callsigns we have into "result" in the correct // order snprintf(result,sizeof(result),"%s>%s",callsign,dest); // Process the digipeater addresses (if any) num_digis = 0; while (more && num_digis < 8) { for (i = 0; i < 7; i++) temp[i] = data_string[ptr++]; temp[7] = '\0'; more = decode_ax25_address(temp, callsign, 1); // Add asterisk strncat(result, ",", sizeof(result) - strlen(result)); strncat(result, callsign, sizeof(result) - strlen(result)); num_digis++; } strncat(result, ":", sizeof(result) - strlen(result)); // Check the Control and PID bytes and toss packets that are // AX.25 connect/disconnect or information packets. We only // want to process UI packets in Xastir. // Control byte should be 0x03 (UI Frame). Strip the poll-bit // from the PID byte before doing the comparison. if ( (data_string[ptr++] & (~0x10)) != 0x03) { return(0); } // PID byte should be 0xf0 (normal AX.25 text) if (data_string[ptr++] != 0xf0) return(0); // WE7U: We get multiple concatenated KISS packets sometimes. Look // for that here and flag when it happens (so we know about it and // can fix it someplace earlier in the process). Correct the // current packet so we don't get the extra garbage tacked onto the // end. for (i = ptr; i < *length; i++) { if (data_string[i] == KISS_FEND) { fprintf(stderr,"***Found concatenated KISS packets:***\n"); data_string[i] = '\0'; // Truncate the string break; } } // Add the Info field to the decoded header info strncat(result, (char *)(&data_string[ptr]), sizeof(result) - strlen(result)); // Copy the result onto the top of the input data. Note that // the length can sometimes be longer than the input string, so // we can't just use the "length" variable here or we'll // truncate our string. Make sure the data_string variable is // MAX_LINE_SIZE or bigger. // snprintf((char *)data_string, MAX_LINE_SIZE, "%s", result); // Write out the new length *length = strlen(result); //fprintf(stderr,"%s\n",data_string); return(1); } // Added by KB6MER for KAM XL(SERIAL_TNC_AUX_GPS) support // buf is a null terminated string // returns buf as a null terminated string after cleaning. // Currently: // removes leading 'cmd:' prompts from TNC if needed // Can be used to add any additional data cleaning functions desired. // Currently only called for SERIAL_TNC_AUX_GPS, but could be added // to other device routines to improve packet decode on other devices. // // Note that the length of "buf" can be up to MAX_DEVICE_BUFFER, // which is currently set to 4096. // void tnc_data_clean(gchar *buf) { while (!strncmp(buf,"cmd:",4)) { int ii; // We're _shortening_ the string here, so we don't need to // know the length of the buffer unless it has no '\0' // terminator to begin with! In that one case we could run // off the end of the string and get a segfault or cause // other problems. for (ii = 0; ; ii++) { buf[ii] = buf[ii+4]; if (buf[ii] == '\0') break; } } } static gboolean aprs_parse_tty_packet(gchar *packet) { decode_ax25_line(packet, APRS_PORT_TTY); g_free(packet); return FALSE; } static gboolean kiss_parse_packet(unsigned char *data_string, gint data_length) { //fprintf(stderr, "Parse: %s\n", data_string); gint devicetype = port_data.device_type; switch(devicetype) { case DEVICE_SERIAL_KISS_TNC: case DEVICE_SERIAL_MKISS_TNC: if ( !decode_ax25_header( data_string, &data_length ) ) { // Had a problem decoding it. Drop // it on the floor. break; } else { // Good decode. Drop through to the // next block to log and decode the // packet. } case DEVICE_SERIAL_TNC: tnc_data_clean((char *)data_string); case DEVICE_AX25_TNC: //fprintf(stderr, "Decoded kiss: %s\n", data_string); g_idle_add((GSourceFunc)aprs_parse_tty_packet, data_string); // decode_ax25_line(data_string, "T", 0); break; default: break; } return FALSE; } // Add one record to the circular queue. Returns 1 if queue is // full, 0 if successful. // /* int push_incoming_data(unsigned char *data_string, int length) { int next_write_ptr = (incoming_write_ptr + 1) % MAX_INPUT_QUEUE; // Check whether queue is full if (incoming_read_ptr == next_write_ptr) { // Yep, it's full! return(1); } // Advance the write pointer incoming_write_ptr = next_write_ptr; incoming_data_queue[incoming_write_ptr].length = length; // incoming_data_queue[incoming_write_ptr].port = port; snprintf((char *)incoming_data_queue[incoming_write_ptr].data, (length < MAX_LINE_SIZE) ? length : MAX_LINE_SIZE, "%s", data_string); queue_depth++; push_count++; return(0); } */ //*********************************************************** // channel_data() // // Takes data read in from a port and adds it to the // incoming_data_queue. If queue is full, waits for queue to have // space before continuing. // // port # // string is the string of data // length is the length of the string. If 0 then use strlen() // on the string itself to determine the length. // // Note that decode_ax25_header() and perhaps other routines may // increase the length of the string while processing. We need to // send a COPY of our input string off to the decoding routines for // this reason, and the size of the buffer must be MAX_LINE_SIZE // for this reason also. //*********************************************************** void channel_data(unsigned char *string, int length) { int max; // struct timeval tmv; // Some messiness necessary because we're using xastir_mutex's // instead of pthread_mutex_t's. int process_it = 0; //fprintf(stderr,"channel_data: %x %d\n",string[0],length); max = 0; if (string == NULL) { return; } if (string[0] == '\0') { return; } if (length == 0) { // Compute length of string including terminator length = strlen((const char *)string) + 1; } // Check for excessively long packets. These might be TCP/IP // packets or concatenated APRS packets. In any case it's some // kind of garbage that we don't want to try to parse. // Note that for binary data (WX stations and KISS packets), the // strlen() function may not work correctly. if (length > MAX_LINE_SIZE) { // Too long! // fprintf(stderr, "Too long"); string[0] = '\0'; // Truncate it to zero length return; } // This protects channel_data from being run by more than one // thread at the same time. if (length > 0) { // Install the cleanup routine for the case where this // thread gets killed while the mutex is locked. The // cleanup routine initiates an unlock before the thread // dies. We must be in deferred cancellation mode for the // thread to have this work properly. We must first get the // pthread_mutex_t address. // If it's any of three types of GPS ports and is a GPRMC or // GPGGA string, just stick it in one of two global // variables for holding such strings. UpdateTime() can // come along and process/clear-out those strings at the // gps_time interval. // process_it++; // Remove the cleanup routine for the case where this thread // gets killed while the mutex is locked. The cleanup // routine initiates an unlock before the thread dies. We // must be in deferred cancellation mode for the thread to // have this work properly. // pthread_cleanup_pop(0); //fprintf(stderr,"Channel data on Port [%s]\n",(char *)string); if (process_it) { // Wait for empty space in queue //fprintf(stderr,"\n== %s", string); /* while (push_incoming_data(string, length) && max < 5400) { sched_yield(); // Yield to other threads tmv.tv_sec = 0; tmv.tv_usec = 2; // 2 usec (void)select(0,NULL,NULL,NULL,&tmv); max++; } */ kiss_parse_packet(g_strdup(string), length); //g_idle_add((GSourceFunc)kiss_parse_packet, g_strdup(string)); } // else // { // fprintf(stderr,"Channel data on Port [%s]\n",(char *)string); // } } } //**************************************************************** // get device name only (the portion at the end of the full path) // device_name current full name of device //**************************************************************** char *get_device_name_only(char *device_name) { int i,len,done; if (device_name == NULL) return(NULL); done = 0; len = (int)strlen(device_name); for(i = len; i > 0 && !done; i--){ if(device_name[i] == '/'){ device_name += (i+1); done = 1; } } return(device_name); } int filethere(char *fn) { FILE *f; int ret; ret =0; f=fopen(fn,"r"); if (f != NULL) { ret=1; (void)fclose(f); } return(ret); } /* * Close the serial port * */ gint serial_detach() { int ok; ok = -1; if (port_data.active == DEVICE_IN_USE && port_data.status == DEVICE_UP) { // Close port first (void)tcsetattr(port_data.channel, TCSANOW, &port_data.t_old); if (close(port_data.channel) == 0) { port_data.status = DEVICE_DOWN; usleep(200); port_data.active = DEVICE_NOT_IN_USE; ok = 1; } else { fprintf(stderr,"Could not close port %s\n",port_data.device_name); port_data.status = DEVICE_DOWN; usleep(200); port_data.active = DEVICE_NOT_IN_USE; } } return(ok); } typedef struct { char *adapter; /* do not free this, it is freed somewhere else */ char *bonding; /* allocated from heap, you must free this */ } bonding_t; static inline DBusGConnection *get_dbus_gconn(GError **error) { DBusGConnection *conn; conn = dbus_g_bus_get(DBUS_BUS_SYSTEM, error); return conn; } void close_tnc_port() { if (port_data.device_name != NULL) { fprintf(stderr, "in close_tnc_port()\n"); serial_detach(); int skip_dbus = 0; DBusGConnection *bus = NULL; DBusGProxy *proxy = NULL; GError *error = NULL; bus = get_dbus_gconn(&error); if (!bus) { errno = ECONNREFUSED; /* close enough :) */ skip_dbus = 1; } if (!skip_dbus) { /* Disconnect the device */ proxy = dbus_g_proxy_new_for_name(bus, BTCOND_DBUS, BTCOND_PATH, BTCOND_INTERFACE); error = NULL; if(!dbus_g_proxy_call(proxy, BTCOND_DISCONNECT, &error, G_TYPE_STRING, port_data.device_name, G_TYPE_INVALID, G_TYPE_INVALID) || error){ // PDEBUG("Cannot send msg (service=%s, object=%s, interface=%s, " // "method=%s) [%s]\n", // BTCOND_DBUS, // BTCOND_PATH, // BTCOND_INTERFACE, // BTCOND_DISCONNECT, // error->message ? error->message : ""); } g_object_unref(proxy); } free(port_data.device_name); port_data.device_name[0]=0; if (bus) { dbus_g_connection_unref(bus); } } } gboolean open_bluetooth_tty_connection(gchar *bda, gchar **aprs_bt_port) { gint i, st, num_bondings = 0, num_rfcomms = 0, num_posdev = 0; GError *error = NULL; DBusGConnection *bus = NULL; DBusGProxy *proxy = NULL; gchar **rfcomms = 0; bonding_t *bondings = 0; /* points to array of bonding_t */ bonding_t *posdev = 0; /* bondings with positioning bit on */ gchar *tmp; const gchar const *spp="SPP"; /* Use the dbus interface to get the BT information */ #if (__GNUC__ > 2) && ((__GNUC__ > 3) || (__GNUC_MINOR__ > 2)) #define ERRSTR(fmt, args...) \ if (error_buf && error_buf_max_len>0) { \ set_error_msg(error_buf, error_buf_max_len, fmt, args); \ } else { \ PDEBUG(fmt, args); \ } #else #define ERRSTR(fmt, args...) \ if (error_buf && error_buf_max_len>0) { \ set_error_msg(error_buf, error_buf_max_len, fmt, ##args); \ } else { \ PDEBUG(fmt, ##args); \ } #endif error = NULL; bus = dbus_g_bus_get(DBUS_BUS_SYSTEM, &error); if (error) { st = -1; errno = ECONNREFUSED; /* close enough :) */ //ERRSTR("%s", error->message); //PDEBUG("Cannot get reply message [%s]\n", error->message); goto OUT; } /* We need BT information only if the caller does not specify * the BT address. If address is defined, it is assumed that * it is already bonded and we just create RFCOMM connection * to it. */ if (!bda) { // May want to support auto detect for serial ports? } else { /* if (!bda) */ /* Caller supplied BT address so use it */ num_posdev = 1; posdev = calloc(1, sizeof(bonding_t *)); if (!posdev) { st = -1; errno = ENOMEM; goto OUT; } posdev[0].bonding = strdup(bda); /* Adapter information is not needed */ posdev[0].adapter = ""; } /* For each bondend BT GPS device, try to create rfcomm */ for (i=0; icode : -1, error ? error->message : "", tmp ? tmp : ""); */ /* No error if already connected */ if (error && !strstr(error->message, "com.nokia.btcond.error.connected")) { ERROR: fprintf(stderr, "Cannot send msg (service=%s, object=%s, interface=%s, " "method=%s) [%s]\n", BTCOND_DBUS, BTCOND_PATH, BTCOND_INTERFACE, BTCOND_CONNECT, error->message ? error->message : ""); continue; } else if(!tmp || !*tmp) { /* hack: rfcommX device name is at the end of error message */ char *last_space = strstr(error->message, " rfcomm"); if (!last_space) { goto ERROR; } g_free(tmp); tmp = g_strdup_printf("/dev/%s", last_space+1); } } g_object_unref(proxy); if (tmp && tmp[0]) { rfcomms = (char **)realloc(rfcomms, (num_rfcomms+1)*sizeof(char *)); if (!rfcomms) { st = -1; errno = ENOMEM; goto OUT; } rfcomms[num_rfcomms] = tmp; num_rfcomms++; fprintf(stderr, "BT addr=%s, RFCOMM %s now exists (adapter=%s)\n", posdev[i].bonding, tmp, posdev[i].adapter); tmp = NULL; } else { g_free(tmp); } } if (num_rfcomms==0) { /* serial device creation failed */ fprintf(stderr, "No rfcomm created\n"); st = -1; errno = EINVAL; } else { /* Add null at the end */ rfcomms = (char **)realloc(rfcomms, (num_rfcomms+1)*sizeof(char *)); if (!rfcomms) { st = -1; errno = ENOMEM; } else { rfcomms[num_rfcomms] = NULL; /* Just start the beast (to be done if everything is ok) */ st = 0; *aprs_bt_port = g_strdup_printf("%s",rfcomms[0]); } } OUT: /* if (adapters) { g_strfreev(adapters); } */ if (posdev) { for (i=0; i-1; } //*********************************************************** // Serial port INIT //*********************************************************** void update_aprs_tty_status() { /* _aprs_tty_enable = (_aprs_tty_state == RCVR_UP); gtk_check_menu_item_set_active( GTK_CHECK_MENU_ITEM(_menu_enable_aprs_tty_item), _aprs_tty_enable); */ } int serial_init () { int speed; euid = geteuid(); egid = getegid(); fprintf(stderr, "in serial_init\n"); // clear port_channel port_data.channel = -1; // clear port active port_data.active = DEVICE_NOT_IN_USE; // clear port status port_data.status = DEVICE_DOWN; //set_aprs_tty_conn_state(RCVR_DOWN); update_aprs_tty_status(); //gw-obex.h if(_aprs_tnc_method == TNC_CONNECTION_BT) { // Bluetooth connection gchar * aprs_bt_port = NULL; //fprintf(stderr, "Connecting to BT device...\n"); if(!open_bluetooth_tty_connection(_aprs_tnc_bt_mac, &aprs_bt_port) || aprs_bt_port == NULL) { fprintf(stderr, "Failed to connect to BT device\n"); // Failed to connect return -1; } snprintf(port_data.device_name, MAX_DEVICE_NAME, aprs_bt_port); g_free(aprs_bt_port); fprintf(stderr, "BT Port: %s\n", port_data.device_name); } else { snprintf(port_data.device_name, MAX_DEVICE_NAME, _aprs_tty_port); } // TODO - make these configurable port_data.device_type = DEVICE_SERIAL_KISS_TNC; port_data.sp = B9600; // check for lock file // Try to open the serial port now ENABLE_SETUID_PRIVILEGE; port_data.channel = open(port_data.device_name, O_RDWR|O_NOCTTY); DISABLE_SETUID_PRIVILEGE; if (port_data.channel == -1){ fprintf(stderr,"Could not open channel on port!\n"); return (-1); } // get port attributes for new and old if (tcgetattr(port_data.channel, &port_data.t) != 0) { fprintf(stderr,"Could not get t port attributes for port!\n"); // Here we should close the port and remove the lock. serial_detach(); return (-1); } if (tcgetattr(port_data.channel, &port_data.t_old) != 0) { fprintf(stderr,"Could not get t_old port attributes for port!\n"); // Here we should close the port and remove the lock. serial_detach(); return (-1); } // set time outs port_data.t.c_cc[VMIN] = (cc_t)0; port_data.t.c_cc[VTIME] = (cc_t)20; // set port flags port_data.t.c_iflag &= ~(BRKINT | IGNPAR | PARMRK | INPCK | ISTRIP | INLCR | IGNCR | ICRNL | IXON); port_data.t.c_iflag = (tcflag_t)(IGNBRK | IGNPAR); port_data.t.c_oflag = (0); port_data.t.c_lflag = (0); #ifdef CBAUD speed = (int)(port_data.t.c_cflag & CBAUD); #else // CBAUD speed = 0; #endif // CBAUD port_data.t.c_cflag = (tcflag_t)(HUPCL|CLOCAL|CREAD); port_data.t.c_cflag &= ~PARENB; switch (port_data.style){ case(0): // No parity (8N1) port_data.t.c_cflag &= ~CSTOPB; port_data.t.c_cflag &= ~CSIZE; port_data.t.c_cflag |= CS8; break; case(1): // Even parity (7E1) port_data.t.c_cflag &= ~PARODD; port_data.t.c_cflag &= ~CSTOPB; port_data.t.c_cflag &= ~CSIZE; port_data.t.c_cflag |= CS7; break; case(2): // Odd parity (7O1): port_data.t.c_cflag |= PARODD; port_data.t.c_cflag &= ~CSTOPB; port_data.t.c_cflag &= ~CSIZE; port_data.t.c_cflag |= CS7; break; default: break; } port_data.t.c_cflag |= speed; // set input and out put speed if (cfsetispeed(&port_data.t, port_data.sp) == -1) { fprintf(stderr,"Could not set port input speed for port!\n"); // Here we should close the port and remove the lock. serial_detach(); return (-1); } if (cfsetospeed(&port_data.t, port_data.sp) == -1) { fprintf(stderr,"Could not set port output speed for port!\n"); // Here we should close the port and remove the lock. serial_detach(); return (-1); } if (tcflush(port_data.channel, TCIFLUSH) == -1) { fprintf(stderr,"Could not flush data for port!\n"); // Here we should close the port and remove the lock. serial_detach(); return (-1); } if (tcsetattr(port_data.channel,TCSANOW, &port_data.t) == -1) { fprintf(stderr,"Could not set port attributes for port!\n"); // Here we should close the port and remove the lock. serial_detach(); return (-1); } // clear port active port_data.active = DEVICE_IN_USE; // clear port status port_data.status = DEVICE_UP; set_aprs_tty_conn_state(RCVR_UP); // Show the latest status in the interface control dialog update_aprs_tty_status(); // Ensure we are in KISS mode if(port_data.device_type == DEVICE_SERIAL_KISS_TNC) { // Send KISS init string gchar * cmd = g_strdup("\nINT KISS\nRESTART\n"); port_write_string(cmd, strlen(cmd), APRS_PORT_TTY); } // return good condition return (1); } gboolean read_port_data() { unsigned char cin, last; gint i; // struct timeval tmv; // fd_set rd; cin = (unsigned char)0; last = (unsigned char)0; int skip = 0; // Handle all EXCEPT AX25_TNC interfaces here // Get one character //fprintf(stderr,"waiting for tty in... "); port_data.scan = (int)read(port_data.channel,&cin,1); if(port_data.scan == 0) return TRUE; else if(port_data.scan < 0) return FALSE; //fprintf(stderr,"%02x \n",cin); // Below is code for ALL types of interfaces if (port_data.scan > 0 && port_data.status == DEVICE_UP ) { if (port_data.device_type != DEVICE_AX25_TNC) port_data.bytes_input += port_data.scan; // Add character to read buffer // Handle all EXCEPT AX25_TNC interfaces here if (port_data.device_type != DEVICE_AX25_TNC){ // Do special KISS packet processing here. // We save the last character in // port_data.channel2, as it is // otherwise only used for AX.25 ports. if ( (port_data.device_type == DEVICE_SERIAL_KISS_TNC) || (port_data.device_type == DEVICE_SERIAL_MKISS_TNC) ) { if (port_data.channel2 == KISS_FESC) { // Frame Escape char if (cin == KISS_TFEND) { // Transposed Frame End char // Save this char for next time // around port_data.channel2 = cin; cin = KISS_FEND; } else if (cin == KISS_TFESC) { // Transposed Frame Escape char // Save this char for next time // around port_data.channel2 = cin; cin = KISS_FESC; } else { port_data.channel2 = cin; } } else if (port_data.channel2 == KISS_FEND) { // Frame End char // Frame start or frame end. Drop // the next character which should // either be another frame end or a // type byte. // Note this "type" byte is where it specifies which KISS interface // the packet came from. We may want to use this later for // multi-drop KISS or other types of KISS protocols. // Save this char for next time // around port_data.channel2 = cin; skip++; } else if (cin == KISS_FESC) { // Frame Escape char port_data.channel2 = cin; skip++; } else { port_data.channel2 = cin; } } // End of first special KISS processing // We shouldn't see any AX.25 flag // characters on a KISS interface because // they are stripped out by the KISS code. // What we should see though are KISS_FEND // characters at the beginning of each // packet. These characters are where we // should break the data apart in order to // send strings to the decode routines. It // may be just fine to still break it on \r // or \n chars, as the KISS_FEND should // appear immediately afterwards in // properly formed packets. if ( (!skip) && (cin == (unsigned char)'\r' || cin == (unsigned char)'\n' || port_data.read_in_pos >= (MAX_DEVICE_BUFFER - 1) || ( (cin == KISS_FEND) && (port_data.device_type == DEVICE_SERIAL_KISS_TNC) ) || ( (cin == KISS_FEND) && (port_data.device_type == DEVICE_SERIAL_MKISS_TNC) ) ) && port_data.data_type == 0) { // If end-of-line // End serial/net type data send it to the decoder Put a terminating // zero at the end of the read-in data port_data.device_read_buffer[port_data.read_in_pos] = (char)0; if (port_data.status == DEVICE_UP && port_data.read_in_pos > 0) { int length; // Compute length of string in // circular queue //fprintf(stderr,"%d\t%d\n",port_data.read_in_pos,port_data.read_out_pos); // KISS TNC sends binary data if ( (port_data.device_type == DEVICE_SERIAL_KISS_TNC) || (port_data.device_type == DEVICE_SERIAL_MKISS_TNC) ) { length = port_data.read_in_pos - port_data.read_out_pos; if (length < 0) length = (length + MAX_DEVICE_BUFFER) % MAX_DEVICE_BUFFER; length++; } else { // ASCII data length = 0; } channel_data( (unsigned char *)port_data.device_read_buffer, length); // Length of string } for (i = 0; i <= port_data.read_in_pos; i++) port_data.device_read_buffer[i] = (char)0; port_data.read_in_pos = 0; } else if (!skip) { // Check for binary WX station data if (cin == '\0') // OWW WX daemon sends 0x00's! cin = '\n'; if (port_data.read_in_pos < (MAX_DEVICE_BUFFER - 1) ) { port_data.device_read_buffer[port_data.read_in_pos] = (char)cin; port_data.read_in_pos++; port_data.device_read_buffer[port_data.read_in_pos] = (char)0; } else { port_data.read_in_pos = 0; } } } // End of non-AX.25 interface code block return TRUE; } // else if (port_data.status == DEVICE_UP) { /* error or close on read */ // // cause re-connect // // // } return TRUE; } #endif //INCLUDE_APRS