====== C-Code for LANTRONIX remote control ====== Version V3.3:\\ /* * remote tool for PLAs * * platool.c * * 2013 Timo Milosic (t.milosic@gsi.de) * */ #define _POSIX_SOURCE 1 #define _GNU_SOURCE #define _BSD_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define MAX_CHAR 0xFF #define VERBOSE 1 #define SEND_LEN 7 #define RECV_TYP 7 #define RECV_LEN 24 // was 14 #define VERSION "0.3.1" /* look-up table for baud rates */ static uint32_t const lut_baud_rates[][2] = { { B0, 0 }, { B50, 50 }, { B75, 75 }, { B110, 110 }, { B134, 134 }, { B150, 150 }, { B200, 200 }, { B300, 300 }, { B600, 600 }, { B1200, 1200 }, { B1800, 1800 }, { B2400, 2400 }, { B4800, 4800 }, { B9600, 9600 }, { B19200, 19200 }, { B38400, 38400 }, { B57600, 57600 }, { B115200, 115200 } }; /* static const char QUERY_COUNTER_VAL = (1 << 0), SET_COUNTER_VAL = (1 << 1), QUERY_SERIAL_NUM = (1 << 2), SET_SERIAL_NUM = (1 << 3), TURN_ON_DISPLAY = (1 << 4), QUERY_ERROR_STATE = (1 << 5), RESET_ERROR_STATE = (1 << 6); */ static const char QUERY_COUNTER_VAL = 1, SET_COUNTER_VAL = 2, QUERY_SERIAL_NUM = 3, SET_SERIAL_NUM = 4, TURN_ON_DISPLAY = 5, QUERY_ERROR_STATE = 6, RESET_ERROR_STATE = 7, READ_EE_DATA = 8, WRITE_EEPROM = 9; enum DEVICE { RS232, SOCKET }; enum PROTO { TCP, UDP }; /** forward declarations **/ uint32_t get_baud_rate_from_flag(speed_t flag); speed_t get_speed_from_rate(uint32_t baud_rate); int create_socket(void), open_port(char *port); void fetch_device(char *const str), config_serial(void), config_socket(void), socket_connect(void), setup_lantronix_device( char const *const ip, int port); char *byte_to_binary_representation(unsigned char byte), *byte_vec_to_bin_string(unsigned char *bytevec, size_t numbytes); uint8_t *send_data_pattern(uint8_t *buf, size_t len), *receive_data_pattern(ssize_t len); /** make cygwin happy **/ #ifdef __CYGWIN32__ long random(void); void srandom(unsigned); #endif /** globals **/ struct termios *g_term_opt = NULL; struct sockaddr_in *g_lantronix = NULL; bool g_address_specified = false; int g_verbose = 0, g_debug = 0, g_fd_port = 0, g_sock = 0, g_device = -1, g_network_port = -1, g_proto = TCP; uint16_t g_address = 0; // serial number of device uint32_t g_turn_on_display = 0, g_query_counter = 0, g_set_counter = 0, g_query_serial = 0, g_set_serial = 0, g_query_error_state = 0, g_reset_error_state = 0, g_read_ee_data = 0, g_read_ee_data_disp = 0, g_write_eeprom = 0, g_workaround_response = 0, g_baud_rate = B9600; char *g_serial_port = NULL, *g_network_add = NULL, *g_use_password = NULL, *g_reg_file_name = NULL; uint8_t g_vec_send[SEND_LEN], g_vec_recv[RECV_LEN]; /** getopt support **/ static int c = 0; struct option long_options[] = { {"debug", no_argument, NULL, 'd'}, {"toggle-display", no_argument, NULL, 'D'}, {"help", no_argument, NULL, 'h'}, {"debug", no_argument, NULL, 'G'}, {"query-errors", no_argument, NULL, 'e'}, {"query-counter", no_argument, NULL, 'c'}, {"read-ee-data", no_argument, NULL, 'r'}, {"read-ee-data-disp", no_argument, NULL, 'R'}, {"query-serial", no_argument, NULL, 's'}, {"verbose", no_argument, NULL, 'v'}, {"version", no_argument, NULL, 'V'}, {"write-eeprom", no_argument, NULL, 'w'}, {"workaround-response", no_argument, NULL, 'W'}, {"reset-errors", no_argument, NULL, 'E'}, {"set-address", required_argument, NULL, 'a'}, {"set-counter", required_argument, NULL, 'C'}, {"set-serial", required_argument, NULL, 'S'}, {"set-baud-rate", required_argument, NULL, 'b'}, {NULL, 0, NULL, 0} /* required as delimiter by getopt_long() */ }; /* getopt()'s single character map */ const char *optstring = "a:b:cC:dDeEGhQrRS:swWvV"; /** function definitions **/ /* Cygwin >= 1.7 provides /dev/ttySx devices and cfmakeraw() macro */ #if defined(__CYGWIN__SMALLER_17) /* workaround for missing cfmakeraw() in Cygwin */ void cfmakeraw(struct termios *termios_p) { termios_p->c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON); termios_p->c_oflag &= ~OPOST; termios_p->c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN); termios_p->c_cflag &= ~(CSIZE|PARENB); termios_p->c_cflag |= CS8; } #endif /* defined(__CYGWIN__) */ /* convert speed_t value to readable information */ uint32_t get_baud_rate_from_flag(speed_t flag) { int i, dim = sizeof(lut_baud_rates)/sizeof(uint32_t)/2; for (i=0; isin_family = AF_INET; g_lantronix->sin_addr.s_addr = inet_addr(ip); g_lantronix->sin_port = htons(port); fprintf(stdout, "device socket @ %s:%d\n", ip, port); return; } /* set and connect ip socket */ void config_socket(void) { setup_lantronix_device(g_network_add, g_network_port); g_sock = create_socket(); /* attempt to establish connection */ socket_connect(); return; } /* set serial port attributes */ void config_serial(void) { if (!g_fd_port) g_fd_port = open_port(g_serial_port); g_term_opt = (struct termios *)malloc(sizeof(struct termios)); /* acquire port attribute struct */ tcgetattr(g_fd_port, g_term_opt); /* set baud rate */ cfsetispeed(g_term_opt, g_baud_rate); cfsetospeed(g_term_opt, g_baud_rate); if (g_verbose) fprintf(stdout, "g_baud_rate = %d\n", g_baud_rate); /* wrapper function to set */ cfmakeraw(g_term_opt); tcsetattr(g_fd_port, TCSANOW, g_term_opt); speed_t i_speed, o_speed; if (0xFF != (i_speed = get_baud_rate_from_flag(cfgetispeed(g_term_opt)))) fprintf(stdout, "input speed %u Baud\n", i_speed); else fprintf(stdout, "input speed could not be found in lut!\n" "cfgetispeed() returned %u\n", cfgetispeed(g_term_opt)); if (0xFF != (o_speed = get_baud_rate_from_flag(cfgetospeed(g_term_opt)))) fprintf(stdout, "output speed %u Baud\n\n", o_speed); else fprintf(stdout, "output speed could not be found in lut!\n" "cfgetospeed() returned %u\n", cfgetospeed(g_term_opt)); /* we don't need struct termios anymore so let's * delete it to keep valgrind output compact */ free(g_term_opt); g_term_opt = NULL; return; } /* create plain ip socket */ int create_socket(void) { fprintf(stdout, "attempt to create %s socket ... ", g_proto == TCP ? "TCP" : "UDP"); if (g_proto == TCP) { g_sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); } else { g_sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); } if (g_sock < 0) { fprintf(stdout, "failed!\n"); perror("failed to create socket"); exit(-1); } else { fprintf(stdout, "success!\n"); } return g_sock; } /* attach to serial port */ int open_port(char *port) { if (g_fd_port) { fprintf(stdout, "serial port file descriptor already exists\n"); return g_fd_port; } char err_str[256]; int fd; if ((fd = open(port, O_RDWR|O_NOCTTY|O_NDELAY/*|O_NONBLOCK*/)) == -1) { snprintf(err_str, 255, "open_port(): unable to open port %s", port); perror(err_str); exit(-1); } else { fprintf(stdout, "\nsuccessfully attached to port %s ...\n\n", port); fcntl(fd, F_SETFL, 0); } return fd; } /* swap byte order of uint32_t */ void swap_bo_uint32_t(uint32_t *data) { uint8_t tmp_vec[4]; uint8_t *work_vec = (uint8_t *)data; memcpy(tmp_vec, data, sizeof(uint32_t)); for (int i=0; i<4; i++) work_vec[i] = tmp_vec[3-i]; return; } /* send data packet at 'buf' over rs232 with size 'len' */ uint8_t *send_data_pattern( uint8_t *buf, size_t len) { if (g_device == RS232 && !g_fd_port) g_fd_port = open_port(g_serial_port); if (g_device == SOCKET && !g_sock) config_socket(); unsigned int n; switch (g_device) { case RS232: n = write(g_fd_port, buf, len); break; case SOCKET: if (g_proto == TCP && g_use_password) { //FIXME://int passlen = strlen(g_use_password); int passlen = 4; unsigned char *newbuf = (unsigned char *)malloc((len+passlen)*sizeof(unsigned char)); memcpy(newbuf, g_use_password,passlen); memcpy(newbuf+passlen, buf, len); // free(buf); buf = newbuf; len += passlen; } n = send(g_sock, buf, len, 0); break; default: fprintf(stdout, "error in send_data_pattern(): no valid device specified!\n"); exit(-1); } if (n != len) fprintf(stdout, "error: unexpected byte write count!\n" "expected: %d\nread: %d\n", (int)len, n); return g_vec_send; } /* attempt to read 'len' bytes at RS232 port */ uint8_t *receive_data_pattern(ssize_t len) { fprintf(stdout, "awaiting incoming packet of size %d bytes ...\n\n", (int)len); ssize_t n, an = 0; switch (g_device) { case RS232: n = read(g_fd_port, g_vec_recv, len); break; case SOCKET: while(len-an != (n = recv(g_sock, &g_vec_recv[an], len-an, 0))) { an += n; printf("accumulating: socket read %zd/%zd bytes ...\n", an, len); } if (n != len) { an += n; printf("accumulating: socket read %zd/%zd bytes ... success!\n\n", an, len); } else { printf("socket read %zd bytes\n\n", len); } break; default: fprintf(stdout, "error in receive_data_pattern(): no valid device specified!\n"); exit(-1); } return g_vec_recv; } /* convery byte to c-string since there are no binary * output formats available with printf derivatives */ char *byte_to_binary_representation(unsigned char byte) { /* return buffer is allocated on the heap and * needs to be free()'ed manually */ char *bitpat = (char *)malloc(9*sizeof(char)); int i; unsigned char testbit = 0x1; for (i=0; i<8; i++) bitpat[7-i] = byte & (testbit << i) ? '1' : '0'; bitpat[8] = '\0'; /* after all it's a c-string */ return bitpat; } void clear_send_buf() { for (int n=0; n #%02X%02X] " "sent counter value: %02X%02X%02X%02X\n", g_vec_recv[1], g_vec_recv[2], g_vec_recv[2], g_vec_recv[1], g_vec_recv[3], g_vec_recv[4], g_vec_recv[5], g_vec_recv[6]); return; } void set_counter_value( uint16_t addr, uint32_t val) { printf("set counter value --> %X\n", val); send_recv_bufs(SET_COUNTER_VAL, addr, val); fprintf(stdout, "device [(%02X%02X) --> #%02X%02X] " "sent counter value: %02X%02X%02X%02X\n", g_vec_recv[1], g_vec_recv[2], g_vec_recv[2], g_vec_recv[1], g_vec_recv[3], g_vec_recv[4], g_vec_recv[5], g_vec_recv[6]); return; } // MMn nicht durchdacht void query_serial_value(uint16_t addr) { printf("query serial number ...\n"); send_recv_bufs(QUERY_SERIAL_NUM, addr, 0); fprintf(stdout, "device [(%02X%02X) --> #%02X%02X] " "serial number: %02X%02X%02X%02X\n", g_vec_recv[1], g_vec_recv[2], g_vec_recv[2], g_vec_recv[1], g_vec_recv[3], g_vec_recv[4], g_vec_recv[5], g_vec_recv[6]); return; } void set_serial_value( uint16_t addr, uint32_t val) { printf("set serial number --> %X\n", val); send_recv_bufs(SET_SERIAL_NUM, addr, val); fprintf(stdout, "device [(%02X%02X) --> #%02X%02X] " "respond with serial number: %02X%02X%02X%02X\n", g_vec_recv[1], g_vec_recv[2], g_vec_recv[2], g_vec_recv[1], g_vec_recv[3], g_vec_recv[4], g_vec_recv[5], g_vec_recv[6]); return; } void turn_on_display(uint16_t addr) { printf("turning on display ...\n"); send_buf(TURN_ON_DISPLAY, addr, 0); /* test dummy read on socket */ recv_timeout_dummy(); return; } void query_error_state(uint16_t addr) { printf("query error state ...\n"); send_recv_bufs(QUERY_ERROR_STATE, addr, 0); fprintf(stdout, "device [(%02X%02X) --> #%02X%02X] " "respond with error state: %02X%02X%02X%02X\n", g_vec_recv[1], g_vec_recv[2], g_vec_recv[2], g_vec_recv[1], g_vec_recv[3], g_vec_recv[4], g_vec_recv[5], g_vec_recv[6]); return; } void reset_error_state(uint16_t addr) { printf("resetting error state ...\n"); send_buf(RESET_ERROR_STATE, addr, 0); /* test dummy read on socket */ recv_timeout_dummy(); return; } void read_ee_data(uint16_t addr) { printf("query ee data ...\n"); send_recv_bufs(READ_EE_DATA, addr, g_read_ee_data_disp ? 1 : 0); printf("listing 24 bytes:\n\n"); printf("[00] %02X ", g_vec_recv[0]); printf("[01] %02X ", g_vec_recv[1]); printf("[02] %02X ", g_vec_recv[2]); printf("[03] %02X\n", g_vec_recv[3]); printf("[04] %02X ", g_vec_recv[4]); printf("[05] %02X ", g_vec_recv[5]); printf("[06] %02X ", g_vec_recv[6]); printf("[07] %02X\n", g_vec_recv[7]); printf("[08] %02X ", g_vec_recv[8]); printf("[09] %02X ", g_vec_recv[9]); printf("[10] %02X ", g_vec_recv[10]); printf("[11] %02X\n", g_vec_recv[11]); printf("[12] %02X ", g_vec_recv[12]); printf("[13] %02X ", g_vec_recv[13]); printf("[14] %02X ", g_vec_recv[14]); printf("[15] %02X\n", g_vec_recv[15]); printf("[16] %02X ", g_vec_recv[16]); printf("[17] %02X ", g_vec_recv[17]); printf("[18] %02X ", g_vec_recv[18]); printf("[19] %02X\n", g_vec_recv[19]); printf("[20] %02X ", g_vec_recv[20]); printf("[21] %02X ", g_vec_recv[21]); printf("[22] %02X ", g_vec_recv[22]); printf("[23] %02X\n", g_vec_recv[23]); return; } void write_eeprom(uint16_t addr) { printf("dumping data to EEPROM ...\n"); send_buf(WRITE_EEPROM, addr, 0); /* test dummy read on socket */ recv_timeout_dummy(); return; } /* get basename by 'token' (1 char), return char pointer to string */ char *basename_t( char *str, char token) { int i = 0; char *tmpPtr = str; while (*tmpPtr != '\0') { tmpPtr++; i++; /* this might be redundant, since bash already takes care on input */ if (i>MAX_CHAR) { /* MAX_CHAR limits the command line string to scan */ fprintf(stderr, "Error: Command line boundary exceeded at MAX_CHAR (%d).\n" "This does not automatically mean that there is something wrong!\n", MAX_CHAR); exit(-1); } } while (*tmpPtr != token) { if (tmpPtr == str) return str; tmpPtr--; } return ++tmpPtr; } /* who needs a man page, actually..? */ void print_help(char *argv[]) { printf("Test utility for pneumatic-feed counters\n" " 2013 Timo Milosic\n\n"); printf("Usage: %s [OPTION] device\n\n",basename_t(argv[0],'/')); printf("device port can be either a serial port or a network socket\n\n"); printf("Options:\n\n"); printf(" -h, --help\t\t\tdisplay this help and exit\n"); printf(" -V, --version\t\t\tprint version\n"); printf(" -a, --set-address=ADDR\tspecify address of terminal (HEX, eg. 1234)\n"); printf(" -b, --set-baud-rate=RATE\tset baud rate (default 9600)\n\n"); printf(" -W, --workaround-response\tworkaround cumbersome response pattern\n"); printf("\t\t\t\t(LANTRONIX UDS 1100 devices with firmware revision < 6.8.x.x)\n\n"); printf(" -c, --query-counter\t [B1]\tquery counter value\n"); printf(" -C, --set-counter=HEX\t [B2]\tset counter value (HEX, eg. 12345678)\n"); printf(" -s, --query-serial\t [B3]\tquery serial number\n"); printf(" -S, --set-serial=HEX\t [B4]\tset serial number (HEX, eq. 1234)\n"); printf(" -D, --toggle-display\t [B5]\tturn on display\n"); printf(" -e, --query-errors\t [B6]\tquery error counter\n"); printf(" -E, --reset-errors\t [B7]\treset error counter\n"); printf(" -r, --read-ee-data\t [B8]\tread EEPROM data ('-R' for display output)\n"); printf(" -w, --write-eeprom\t [B9]\tdump data to EEPROM\n"); printf("\n"); printf("Examples:\n\n"); printf(" ./%s -a ADDR -b 115200 -q /dev/ttyS0\t\tuse rs232, set it to 115200 and query the counter valus\n",basename_t(argv[0],'/')); printf(" ./%s -a ADDR -S SERIAL 140.181.132.120:10001\tuse TCP/IP and set serial to SERIAL (BCD)\n",basename_t(argv[0],'/')); printf("\n"); return; } int main(int argc, char *argv[]) { /* parse command line with getopt_long() */ while (1) { if (argc == 1) { print_help(argv); return 0; } int option_index = 0; c = getopt_long(argc, argv, optstring, long_options, &option_index); if (c == -1) break; switch (c) { case 0: printf("option %s", long_options[option_index].name); if (optarg) printf(" with arg %s", optarg); printf("\n"); break; case 'a': g_address = strtol(optarg, NULL, 16); // we need this as the address can be '0' too. g_address_specified = true; break; case 'b': // set baud rate get_speed_from_rate(atoi(optarg)); break; case 'c': // query counter g_query_counter = 1; break; case 'C': // set counter g_set_counter = strtol(optarg, NULL, 16); break; case 'D': // turn display on g_turn_on_display = 1; break; case 'e': // query error state g_query_error_state = 1; break; case 'E': // reset error state g_reset_error_state = 1; break; case 'G': // debug mode g_debug = 1; break; case 'h': // print help/usage print_help(argv); exit(0); break; case 'r': // read 24 byte stream g_read_ee_data = 1; break; case 'R': // read 24 byte stream and print on display g_read_ee_data = 1; g_read_ee_data_disp = 1; break; case 's': // query serial g_query_serial = 1; break; case 'S': // set serial g_set_serial = strtol(optarg, NULL, 16); break; case 'v': // verbosity g_verbose = 1; break; case 'V': // print version info and exit fprintf(stdout,"platool %s\n",VERSION); exit(0); break; case 'w': // write to EEPROM g_write_eeprom = 1; break; case 'W': // verbosity g_workaround_response = 1; break; case '?': break; default: printf("?? getopt returned character code 0%o ??\n", c); } } /* only one non-option element? --> rs323 port or IP address */ int resargs = 0; if (((resargs = argc-optind) != 1)) { if (!resargs) fprintf(stderr,"error: no port specified!\n"); if (resargs > 1) fprintf(stderr,"error: more than one port specified!\n"); print_help(argv); exit(-1); } if (argc == 2) fprintf(stdout,"you have to specify an option at least ...\n"); /* some commands require an address */ if ( (g_query_counter || g_set_counter || g_query_serial || g_set_serial || g_read_ee_data || g_query_error_state) && !g_address_specified ) { fprintf(stdout, "you have to specify an address for this command!\n"); exit(-1); } /* fetch device */ fetch_device(argv[optind]); /* config add connect the device specified */ config_device(); if (g_query_counter) query_counter_value(g_address); if (g_set_counter) set_counter_value(g_address, g_set_counter); if (g_query_serial) query_serial_value(g_address); if (g_set_serial) set_serial_value(g_address, g_set_serial); if (g_turn_on_display) turn_on_display(g_address); if (g_query_error_state) query_error_state(g_address); if (g_reset_error_state) reset_error_state(g_address); if (g_read_ee_data) read_ee_data(g_address); if (g_write_eeprom) write_eeprom(g_address); /* close socket if available */ if (g_sock) close(g_sock); return 0; }