diff --git a/tools/jn516x/JennicModuleProgrammer b/tools/jn516x/JennicModuleProgrammer new file mode 100644 index 000000000..eef29c284 Binary files /dev/null and b/tools/jn516x/JennicModuleProgrammer differ diff --git a/tools/jn516x/Makefile b/tools/jn516x/Makefile new file mode 100644 index 000000000..be3caa44b --- /dev/null +++ b/tools/jn516x/Makefile @@ -0,0 +1,25 @@ +ifndef HOST_OS + ifeq ($(OS),Windows_NT) + HOST_OS := Windows + else + HOST_OS := $(shell uname) + endif +endif + +ifeq ($(HOST_OS),Windows) + SERIALDUMP = serialdump-windows +endif + +ifeq ($(HOST_OS),Darwin) + SERIALDUMP = serialdump-macos +endif + +ifndef SERIALDUMP + # Assume Linux + SERIALDUMP = serialdump-linux +endif + +all: $(SERIALDUMP) + +$(SERIALDUMP): serialdump.c + $(CC) -O2 -o $@ $< diff --git a/tools/jn516x/mote-list.py b/tools/jn516x/mote-list.py new file mode 100644 index 000000000..170b04fdb --- /dev/null +++ b/tools/jn516x/mote-list.py @@ -0,0 +1,94 @@ +#!/usr/bin/python + +# Copyright (c) 2015, SICS Swedish ICT +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the Institute nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# This file is part of the Contiki operating system. +# + +import sys, os, platform +import multiprocessing + +# detect the operating system +sysname = platform.system() +if "Linux" in sysname: + IS_WINDOWS = False + FLASH_PROGRAMMER_DEFAULT_PATH = "/usr/jn-toolchain/tools/flashprogrammer/JennicModuleProgrammer" + import motelist_lib.linux_motelist_impl as motelist_impl # @UnusedImport + +elif ("Win" in sysname) or ("NT" in sysname): + IS_WINDOWS = True + FLASH_PROGRAMMER_DEFAULT_PATH = 'C:\\NXP\\bstudio_nxp\\sdk\\JN-SW-4163\\Tools\\flashprogrammer\\FlashCLI.exe' + import motelist_lib.windows_motelist_impl as motelist_impl # @Reimport @UnusedImport + +else: + print ("OS ('{}') is not supported".format(os.name)) + + +def main(): + # use the default location + flash_programmer = FLASH_PROGRAMMER_DEFAULT_PATH + if len(sys.argv) > 2: + flash_programmer=sys.argv[1] + + serial_dumper = "" + if len(sys.argv) > 3: + serial_dumper=sys.argv[3] + + motes = motelist_impl.list_motes(flash_programmer) + if motes: + motes.sort() + print 'Found %d JN516X motes at:' %(len(motes)) + motes_str = '' + for m in motes: + motes_str += "%s " %(str(m)) + print motes_str + + firmware_file='#' + if len(sys.argv) > 2: + firmware_file = sys.argv[2] + elif len(sys.argv) > 1: + firmware_file = sys.argv[1] + + if firmware_file[0] == '\\': + firmware_file = firmware_file[1:] + + if firmware_file not in ['#', '!', '?', '%']: + print '\nBatch programming all connected motes...\n' + motelist_impl.program_motes(flash_programmer, motes, firmware_file) + elif firmware_file == '?' or firmware_file == '!': + should_display_mac_list = (firmware_file == '!') + motelist_impl.print_info(flash_programmer, motes, should_display_mac_list) + elif firmware_file == '%': + print '\nLogging from all connected motes...\n' + motelist_impl.serialdump_ports(flash_programmer, serial_dumper, motes) + else: + print '\nNo firmware file specified.\n' + +if __name__ == '__main__': + multiprocessing.freeze_support() + main() diff --git a/tools/jn516x/motelist_lib/__init__.py b/tools/jn516x/motelist_lib/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tools/jn516x/motelist_lib/linux_motelist_impl.py b/tools/jn516x/motelist_lib/linux_motelist_impl.py new file mode 100644 index 000000000..231c7814f --- /dev/null +++ b/tools/jn516x/motelist_lib/linux_motelist_impl.py @@ -0,0 +1,220 @@ +# Copyright (c) 2015, SICS Swedish ICT +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the Institute nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# This file is part of the Contiki operating system. +# +# Author(s): +# Janis Judvaitis +# Atis Elsts + +import os, glob, re +import multiprocessing, subprocess + +FTDI_VENDOR_ID = "0403" +FTDI_PRODUCT_ID = "6001" + +doPrintVendorID = False + +def read_line(filename): + """helper function to read a single line from a file""" + line = "Unknown" + try: + with open(filename) as f: + line = f.readline().strip() + finally: + return line + +# try to extract descriptions from sysfs. this was done by experimenting, +# no guarantee that it works for all devices or in the future... + +def usb_sysfs_hw_string(sysfs_path): + """given a path to a usb device in sysfs, return a string describing it""" + snr = read_line(sysfs_path + '/serial') + if snr: + snr_txt = '%s' % (snr,) + else: + snr_txt = '' + if doPrintVendorID: + return 'USB VID:PID=%s:%s SNR=%s' % ( + read_line(sysfs_path + '/idVendor'), + read_line(sysfs_path + '/idProduct'), + snr_txt + ) + else: + return snr_txt + +def usb_string(sysfs_path): + # Get dir name in /sys/bus/usb/drivers/usb for current usb dev + dev = os.path.basename(os.path.realpath(sysfs_path)) + dev_dir = os.path.join("/sys/bus/usb/drivers/usb", dev) + + try: + # Go to usb dev directory + product = read_line(os.path.join(dev_dir, "product")) + manufacturer = read_line(os.path.join(dev_dir, "manufacturer")) + result = product + " by " + manufacturer + except: + result = "Unknown device" + + return result + +def describe(device): + """ + Get a human readable description. + For USB-Serial devices try to run lsusb to get a human readable description. + For USB-CDC devices read the description from sysfs. + """ + base = os.path.basename(device) + # USB-Serial devices + sys_dev_path = '/sys/class/tty/%s/device/driver/%s' % (base, base) + if os.path.exists(sys_dev_path): + sys_usb = os.path.dirname(os.path.dirname(os.path.realpath(sys_dev_path))) + return usb_string(sys_usb) + + # Arduino wants special handling + sys_dev_path = '/sys/class/tty/%s/device/driver/' % (base) + for x in os.listdir(sys_dev_path): + # Driver directory's name contains device ID in /sys/bus/usb/drivers/usb + temp = x.split(":") + if len(temp) == 2: + # No Arduino adds, need to save space! + return usb_string(temp[0]).replace("(www.arduino.cc)", "").strip() + + # USB-CDC devices + sys_dev_path = '/sys/class/tty/%s/device/interface' % (base,) + if os.path.exists(sys_dev_path): + return read_line(sys_dev_path) + + return base + +def hwinfo(device): + """Try to get a HW identification using sysfs""" + base = os.path.basename(device) + if os.path.exists('/sys/class/tty/%s/device' % (base,)): + # PCI based devices + sys_id_path = '/sys/class/tty/%s/device/id' % (base,) + if os.path.exists(sys_id_path): + return read_line(sys_id_path) + # USB-Serial devices + sys_dev_path = '/sys/class/tty/%s/device/driver/%s' % (base, base) + if os.path.exists(sys_dev_path): + sys_usb = os.path.dirname(os.path.dirname(os.path.realpath(sys_dev_path))) + return usb_sysfs_hw_string(sys_usb) + # USB-CDC devices + if base.startswith('ttyACM'): + sys_dev_path = '/sys/class/tty/%s/device' % (base,) + if os.path.exists(sys_dev_path): + return usb_sysfs_hw_string(sys_dev_path + '/..') + return 'n/a' # XXX directly remove these from the list? + +####################################### + +def is_nxp_mote(device): + base = os.path.basename(device) + # USB-Serial device? + sys_dev_path = '/sys/class/tty/%s/device/driver/%s' % (base, base) + if not os.path.exists(sys_dev_path): + return False + + path_usb = os.path.dirname(os.path.dirname(os.path.realpath(sys_dev_path))) + + dev = os.path.basename(os.path.realpath(path_usb)) + dev_dir = os.path.join("/sys/bus/usb/drivers/usb", dev) + + try: + idProduct = read_line(os.path.join(dev_dir, "idProduct")) + idVendor = read_line(os.path.join(dev_dir, "idVendor")) + if idVendor != FTDI_VENDOR_ID or idProduct != FTDI_PRODUCT_ID: + return False + product = read_line(os.path.join(dev_dir, "product")) + manufacturer = read_line(os.path.join(dev_dir, "manufacturer")) + if manufacturer != "NXP": + return False + except: + return False + return True + + +def list_motes(flash_programmer): + devices = glob.glob('/dev/ttyUSB*')# + glob.glob('/dev/ttyACM*') + return [d for d in devices if is_nxp_mote(d)] + + +def extract_information(port, stdout_value): + mac_str='Unknown' # not supported on Linux + info='' # not properly supported on Linux + is_program_success='' + + info = describe(port) + ", SerialID: " + hwinfo(port) + + res = re.compile('(Success)').search(stdout_value) + if res: + is_program_success = str(res.group(1)) + else: + res = re.compile('(Error .*)\n').search(stdout_value) + if res: + is_program_success = str(res.group(1)) + + return [mac_str, info, is_program_success] + + +def program_motes(flash_programmer, motes, firmware_file): + for m in motes: + cmd = [flash_programmer, '-v', '-s', m, '-I', '38400', '-P', '1000000', '-f', firmware_file] + cmd = " ".join(cmd) + proc = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE,) + stdout_value, stderr_value = proc.communicate('through stdin to stdout') + [mac_str, info, is_program_success] = extract_information(m, stdout_value) + print m, is_program_success + + errors = (stderr_value) + if errors != '': + print 'Errors:', errors + + +def print_info(flash_programmer, motes, do_mac_only): + if do_mac_only: + print "Listing Mac addresses (not supported on Linux):" + else: + print "Listing mote info:" + + for m in motes: + [mac_str, info, is_program_success] = extract_information(m, '') + if do_mac_only: + print m, mac_str + else: + print m, '\n', info, '\n' + +def serialdump(args): + port_name = args[0] + serial_dumper = args[1] + rv = subprocess.call(serial_dumper + ' -b1000000 ' + port_name, shell=True) + +def serialdump_ports(flash_programmer, serial_dumper, ports): + p = multiprocessing.Pool() + p.map(serialdump, zip(ports, [serial_dumper] * len(ports))) + p.close() diff --git a/tools/jn516x/motelist_lib/windows_motelist_impl.py b/tools/jn516x/motelist_lib/windows_motelist_impl.py new file mode 100644 index 000000000..a4528484b --- /dev/null +++ b/tools/jn516x/motelist_lib/windows_motelist_impl.py @@ -0,0 +1,142 @@ +# Copyright (c) 2015, SICS Swedish ICT +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the Institute nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# This file is part of the Contiki operating system. +# +# Author(s): +# Simon Duquennoy +# Atis Elsts + +import os, re, subprocess, multiprocessing + +def list_motes(flash_programmer): + #There is no COM0 in windows. We use this to trigger an error message that lists all valid COM ports + cmd = [flash_programmer, '-c', 'COM0'] + proc = subprocess.Popen(cmd, shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE,) + stdout_value, stderr_value = proc.communicate('through stdin to stdout') + com_str = (stderr_value) + #print '\tpass through:', repr(stdout_value) + #print '\tstderr :', com_str + + ## Extract COM ports from output: + ## Example com_str: "Available ports: ['COM15', 'COM10']" + res = re.compile('\[((?:\'COM\d+\'.?.?)+)\]').search(com_str) + + ports = [] + if res: + port_str=str(res.group(1)) + ports=port_str.replace('\'', '').replace(',', '').split() + return ports + +def extract_information(port, stdout_value): + mac_str='' + info='' + is_program_success='' + + #print 'output: ', stdout_value + +# res = re.compile('Connecting to device on (COM\d+)').search(stdout_value) +# if res: +# port_str = str(res.group(1)) + + ### extracting the following information + ''' + Devicelabel: JN516x, BL 0x00080006 + FlashLabel: Internal Flash (256K) + Memory: 0x00008000 bytes RAM, 0x00040000 bytes Flash + ChipPartNo: 8 + ChipRevNo: 1 + ROM Version: 0x00080006 + MAC Address: 00:15:8D:00:00:35:DD:FB + ZB License: 0x00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00 + User Data: 00:00:00:00:00:00:00:00 + FlashMID: 0xCC + FlashDID: 0xEE + MacLocation: 0x00000010 + Sector Length: 0x08000 + Bootloader Version: 0x00080006 + ''' + + res = re.compile('(Devicelabel.*\sFlashLabel.*\sMemory.*\sChipPartNo.*\sChipRevNo.*\sROM Version.*\sMAC Address.*\sZB License.*\sUser Data.*\sFlashMID.*\sFlashDID.*\sMacLocation.*\sSector Length.*\sBootloader Version\:\s+0x\w{8})').search(stdout_value) + if res: + info = str(res.group(1)) + + res = re.compile('MAC Address\:\s+((?:\w{2}\:?){8})').search(stdout_value) + if res: + mac_str = str(res.group(1)) + + res = re.compile('(Program\ssuccessfully\swritten\sto\sflash)').search(stdout_value) + if res: + is_program_success = str(res.group(1)) + + return [mac_str, info, is_program_success] + +def program_motes(flash_programmer, motes, firmware_file): + for m in motes: + cmd = [flash_programmer, '-c', m, '-B', '1000000', '-s', '-w', '-f', firmware_file] + proc = subprocess.Popen(cmd, shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE,) + stdout_value, stderr_value = proc.communicate('through stdin to stdout') + [mac_str, info, is_program_success] = extract_information(m, stdout_value) + print m, mac_str, is_program_success + + errors = (stderr_value) + if errors != '': + print 'Errors:', errors + +def print_info(flash_programmer, motes, do_mac_only): + if do_mac_only: + print "Listing Mac addresses:" + else: + print "Listing mote info:" + for m in motes: + cmd=[flash_programmer, '-c', m, '-B', '1000000'] + proc = subprocess.Popen(cmd, shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE,) + stdout_value, stderr_value = proc.communicate('through stdin to stdout') + [mac_str, info, is_program_success] = extract_information(m, stdout_value) + + errors = (stderr_value) + + if do_mac_only: + print m, mac_str + else: + print m, '\n', info, '\n' + + if errors != '': + print 'Errors:', errors + +def serialdump(args): + port_name = args[0] + serial_dumper = args[1] + cmd = [serial_dumper, '-b1000000', "/dev/" + port_name.lower()] + if os.name == "posix" or os.name == "cygwin": + cmd = " ".join(cmd) + rv = subprocess.call(cmd, shell=True) + +def serialdump_ports(flash_programmer, serial_dumper, ports): + p = multiprocessing.Pool() + p.map(serialdump, zip(ports, [serial_dumper] * len(ports))) + p.close() diff --git a/tools/jn516x/serialdump.c b/tools/jn516x/serialdump.c new file mode 100644 index 000000000..26b0884e2 --- /dev/null +++ b/tools/jn516x/serialdump.c @@ -0,0 +1,396 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define BAUDRATE B115200 +#define BAUDRATE_S "115200" +#ifdef linux +#define MODEMDEVICE "/dev/ttyS0" +#else +#define MODEMDEVICE "/dev/com1" +#endif /* linux */ + +#define SLIP_END 0300 +#define SLIP_ESC 0333 +#define SLIP_ESC_END 0334 +#define SLIP_ESC_ESC 0335 + +#define CSNA_INIT 0x01 + +#define BUFSIZE 40 +#define HCOLS 20 +#define ICOLS 18 + +#define MODE_START_DATE 0 +#define MODE_DATE 1 +#define MODE_START_TEXT 2 +#define MODE_TEXT 3 +#define MODE_INT 4 +#define MODE_HEX 5 +#define MODE_SLIP_AUTO 6 +#define MODE_SLIP 7 +#define MODE_SLIP_HIDE 8 + +static unsigned char rxbuf[2048]; + +static int +usage(int result) +{ + printf("Usage: serialdump [-x] [-s[on]] [-i] [-bSPEED] [SERIALDEVICE]\n"); + printf(" -x for hexadecimal output\n"); + printf(" -i for decimal output\n"); + printf(" -s for automatic SLIP mode\n"); + printf(" -so for SLIP only mode (all data is SLIP packets)\n"); + printf(" -sn to hide SLIP packages\n"); + printf(" -T[format] to add time for each text line\n"); + printf(" (see man page for strftime() for format description)\n"); + return result; +} + +static void +print_hex_line(unsigned char *prefix, unsigned char *outbuf, int index) +{ + int i; + + printf("\r%s", prefix); + for(i = 0; i < index; i++) { + if((i % 4) == 0) { + printf(" "); + } + printf("%02X", outbuf[i] & 0xFF); + } + printf(" "); + for(i = index; i < HCOLS; i++) { + if((i % 4) == 0) { + printf(" "); + } + printf(" "); + } + for(i = 0; i < index; i++) { + if(outbuf[i] < 30 || outbuf[i] > 126) { + printf("."); + } else { + printf("%c", outbuf[i]); + } + } +} + +int +main(int argc, char **argv) +{ + struct termios options; + fd_set mask, smask; + int fd; + speed_t speed = BAUDRATE; + char *speedname = BAUDRATE_S; + char *device = MODEMDEVICE; + char *timeformat = NULL; + unsigned char buf[BUFSIZE], outbuf[HCOLS]; + unsigned char mode = MODE_START_TEXT; + int nfound, flags = 0; + unsigned char lastc = '\0'; + + int index = 1; + while(index < argc) { + if(argv[index][0] == '-') { + switch(argv[index][1]) { + case 'b': + /* set speed */ + if(strcmp(&argv[index][2], "38400") == 0) { + speed = B38400; + speedname = "38400"; + } else if(strcmp(&argv[index][2], "19200") == 0) { + speed = B19200; + speedname = "19200"; + } else if(strcmp(&argv[index][2], "57600") == 0) { + speed = B57600; + speedname = "57600"; + } else if(strcmp(&argv[index][2], "115200") == 0) { + speed = B115200; + speedname = "115200"; + } else if(strcmp(&argv[index][2], "230400") == 0) { + speed = B230400; + speedname = "230400"; + } else if(strcmp(&argv[index][2], "460800") == 0) { + speed = B460800; + speedname = "460800"; + } else if(strcmp(&argv[index][2], "500000") == 0) { + speed = B500000; + speedname = "500000"; + } else if(strcmp(&argv[index][2], "576000") == 0) { + speed = B576000; + speedname = "576000"; + } else if(strcmp(&argv[index][2], "921600") == 0) { + speed = B921600; + speedname = "921600"; + } else if(strcmp(&argv[index][2], "1000000") == 0) { + speed = B1000000; + speedname = "1000000"; + } else { + fprintf(stderr, "unsupported speed: %s\n", &argv[index][2]); + return usage(1); + } + break; + case 'x': + mode = MODE_HEX; + break; + case 'i': + mode = MODE_INT; + break; + case 's': + switch(argv[index][2]) { + case 'n': + mode = MODE_SLIP_HIDE; + break; + case 'o': + mode = MODE_SLIP; + break; + default: + mode = MODE_SLIP_AUTO; + break; + } + break; + case 'T': + if(strlen(&argv[index][2]) == 0) { + timeformat = "%Y-%m-%d %H:%M:%S"; + } else { + timeformat = &argv[index][2]; + } + mode = MODE_START_DATE; + break; + case 'h': + return usage(0); + default: + fprintf(stderr, "unknown option '%c'\n", argv[index][1]); + return usage(1); + } + index++; + } else { + device = argv[index++]; + if(index < argc) { + fprintf(stderr, "too many arguments\n"); + return usage(1); + } + } + } + fprintf(stderr, "connecting to %s (%s)", device, speedname); + +#ifdef O_SYNC + fd = open(device, O_RDWR | O_NOCTTY | O_NDELAY /*| O_DIRECT*/ | O_SYNC); + if(fd < 0 && errno == EINVAL){ // O_SYNC not supported (e.g. raspberian) + fd = open(device, O_RDWR | O_NOCTTY | O_NDELAY | O_DIRECT); + } +#else + fd = open(device, O_RDWR | O_NOCTTY | O_NDELAY | O_SYNC ); +#endif + if(fd < 0) { + fprintf(stderr, "\n"); + perror("open"); + exit(-1); + } + fprintf(stderr, " [OK]\n"); + + if(fcntl(fd, F_SETFL, 0) < 0) { + perror("could not set fcntl"); + exit(-1); + } + + if(tcgetattr(fd, &options) < 0) { + perror("could not get options"); + exit(-1); + } + /* fprintf(stderr, "serial options set\n"); */ + cfsetispeed(&options, speed); + cfsetospeed(&options, speed); + /* Enable the receiver and set local mode */ + options.c_cflag |= (CLOCAL | CREAD); + /* Mask the character size bits and turn off (odd) parity */ + options.c_cflag &= ~(CSIZE | PARENB | PARODD); + /* Select 8 data bits */ + options.c_cflag |= CS8; + + /* Raw input */ + options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); + /* Raw output */ + options.c_oflag &= ~OPOST; + + if(tcsetattr(fd, TCSANOW, &options) < 0) { + perror("could not set options"); + exit(-1); + } + + /* Make read() return immediately */ + /* if (fcntl(fd, F_SETFL, FNDELAY) < 0) { */ + /* perror("\ncould not set fcntl"); */ + /* exit(-1); */ + /* } */ + + FD_ZERO(&mask); + FD_SET(fd, &mask); + FD_SET(fileno(stdin), &mask); + + index = 0; + for(;;) { + smask = mask; + nfound = select(FD_SETSIZE, &smask, (fd_set *) 0, (fd_set *) 0, (struct timeval *) 0); + if(nfound < 0) { + if(errno == EINTR) { + fprintf(stderr, "interrupted system call\n"); + continue; + } + /* something is very wrong! */ + perror("select"); + exit(1); + } + + if(FD_ISSET(fileno(stdin), &smask)) { + /* data from standard in */ + int n = read(fileno(stdin), buf, sizeof(buf)); + if(n < 0) { + perror("could not read"); + exit(-1); + } else if(n > 0) { + /* because commands might need parameters, lines needs to be + separated which means the terminating LF must be sent */ + /* while(n > 0 && buf[n - 1] < 32) { */ + /* n--; */ + /* } */ + if(n > 0) { + int i; + /* fprintf(stderr, "SEND %d bytes\n", n);*/ + /* write slowly */ + for(i = 0; i < n; i++) { + if(write(fd, &buf[i], 1) <= 0) { + perror("write"); + exit(1); + } else { + fflush(NULL); + usleep(6000); + } + } + } + } else { + /* End of input, exit. */ + exit(0); + } + } + + if(FD_ISSET(fd, &smask)) { + int i, j, n = read(fd, buf, sizeof(buf)); + if(n < 0) { + perror("could not read"); + exit(-1); + } + + for(i = 0; i < n; i++) { + switch(mode) { + case MODE_START_TEXT: + case MODE_TEXT: + printf("%c", buf[i]); + break; + case MODE_START_DATE: { + time_t t; + t = time(&t); + strftime(outbuf, HCOLS, timeformat, localtime(&t)); + printf("%s|", outbuf); + mode = MODE_DATE; + } + /* continue into the MODE_DATE */ + case MODE_DATE: + printf("%c", buf[i]); + if(buf[i] == '\n') { + mode = MODE_START_DATE; + } + break; + case MODE_INT: + printf("%03d ", buf[i]); + if(++index >= ICOLS) { + index = 0; + printf("\n"); + } + break; + case MODE_HEX: + rxbuf[index++] = buf[i]; + if(index >= HCOLS) { + print_hex_line("", rxbuf, index); + index = 0; + printf("\n"); + } + break; + + case MODE_SLIP_AUTO: + case MODE_SLIP_HIDE: + if(!flags && (buf[i] != SLIP_END)) { + /* Not a SLIP packet? */ + printf("%c", buf[i]); + break; + } + /* continue to slip only mode */ + case MODE_SLIP: + switch(buf[i]) { + case SLIP_ESC: + lastc = SLIP_ESC; + break; + + case SLIP_END: + if(index > 0) { + if(flags != 2 && mode != MODE_SLIP_HIDE) { + /* not overflowed: show packet */ + print_hex_line("SLIP: ", rxbuf, index > HCOLS ? HCOLS : index); + printf("\n"); + } + lastc = '\0'; + index = 0; + flags = 0; + } else { + flags = !flags; + } + break; + + default: + if(lastc == SLIP_ESC) { + lastc = '\0'; + + /* Previous read byte was an escape byte, so this byte will be + interpreted differently from others. */ + switch(buf[i]) { + case SLIP_ESC_END: + buf[i] = SLIP_END; + break; + case SLIP_ESC_ESC: + buf[i] = SLIP_ESC; + break; + } + } + + rxbuf[index++] = buf[i]; + if(index >= sizeof(rxbuf)) { + fprintf(stderr, "**** slip overflow\n"); + index = 0; + flags = 2; + } + break; + } + break; + } + } + + /* after processing for some output modes */ + if(index > 0) { + switch(mode) { + case MODE_HEX: + print_hex_line("", rxbuf, index); + break; + } + } + fflush(stdout); + } + } +}