From e15158ea962e769e4beb46d34d5779e2f8fa5ef7 Mon Sep 17 00:00:00 2001 From: Joseph Coffland Date: Fri, 17 May 2019 14:21:47 -0700 Subject: [PATCH] Boost HDMI signal, Increased AVR serial and command buffers, Changed AVR serial interrupt priorites, Fix planner process shutdown, Use systemd serivce instead of init.d, Fixed log rotating, Rewrote RPi serial driver, added support for Nowforever VFDs. --- .gitignore | 2 + CHANGELOG.md | 8 + MANIFEST.in | 2 + Makefile | 7 +- docs/cross_compile_rpi_kernel_module.md | 68 +++ package.json | 2 +- scripts/bbctrl.init.d | 59 --- scripts/bbctrl.service | 15 + scripts/demo-chroot | 17 + scripts/install.sh | 69 +-- scripts/rc.local | 5 + src/avr/src/command.c | 4 +- src/avr/src/config.h | 2 +- src/avr/src/ringbuf.def | 8 + src/avr/src/usart.c | 8 +- src/avr/src/usart.h | 5 +- src/bbserial/Makefile | 10 + src/bbserial/bbserial.c | 558 ++++++++++++++++++++++++ src/pug/templates/tool-view.pug | 42 +- src/py/bbctrl/AVR.py | 5 +- src/py/bbctrl/AVREmu.py | 76 ++-- src/py/bbctrl/Camera.py | 2 +- src/py/bbctrl/Log.py | 25 +- src/py/bbctrl/Preplanner.py | 8 +- src/py/bbctrl/__init__.py | 4 +- 25 files changed, 870 insertions(+), 141 deletions(-) create mode 100644 docs/cross_compile_rpi_kernel_module.md delete mode 100755 scripts/bbctrl.init.d create mode 100644 scripts/bbctrl.service create mode 100755 scripts/demo-chroot create mode 100644 src/bbserial/Makefile create mode 100644 src/bbserial/bbserial.c diff --git a/.gitignore b/.gitignore index ba12fc8..2157929 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,8 @@ /crap /pkg /mnt +/demo +/bbctrl-* node_modules *~ \#* diff --git a/CHANGELOG.md b/CHANGELOG.md index 472d9ae..8f744fd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,14 @@ Buildbotics CNC Controller Firmware Changelog ============================================= +## v0.4.8 + - Fixed log rotating. + - Use systemd serivce instead of init.d. + - Fix planner terminate. + - Changed AVR serial interrupt priorites. + - Increased AVR serial and command buffers. + - Boost HDMI signal. + ## v0.4.7 - Fix homing switch to motor channel mapping with non-standard axis order. - Added ``switch-debounce`` and ``switch-lockout`` config options. diff --git a/MANIFEST.in b/MANIFEST.in index 9dbe3de..32d4570 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,9 +1,11 @@ recursive-include src/py/bbctrl/http * include package.json README.md scripts/install.sh include src/avr/bbctrl-avr-firmware.hex +include src/bbserial/bbserial.ko include scripts/avr109-flash.py include scripts/buildbotics.gc include scripts/xinitrc include scripts/rc.local +include scripts/bbctrl.service recursive-include src/py/camotics * global-exclude .gitignore diff --git a/Makefile b/Makefile index 31d1049..0aed58e 100644 --- a/Makefile +++ b/Makefile @@ -42,12 +42,15 @@ endif all: $(HTML) $(RESOURCES) @for SUB in $(SUBPROJECTS); do $(MAKE) -C src/$$SUB; done -pkg: all $(AVR_FIRMWARE) +pkg: all $(AVR_FIRMWARE) bbserial ./setup.py sdist beta-pkg: pkg cp dist/$(PKG_NAME).tar.bz2 dist/$(BETA_PKG_NAME).tar.bz2 +bbserial: + $(MAKE) -C src/bbserial + gplan: $(GPLAN_TARGET) $(GPLAN_TARGET): $(GPLAN_MOD) @@ -136,4 +139,4 @@ clean: tidy dist-clean: clean rm -rf node_modules -.PHONY: all install clean tidy pkg gplan lint pylint jshint +.PHONY: all install clean tidy pkg gplan lint pylint jshint bbserial diff --git a/docs/cross_compile_rpi_kernel_module.md b/docs/cross_compile_rpi_kernel_module.md new file mode 100644 index 0000000..2b04dd3 --- /dev/null +++ b/docs/cross_compile_rpi_kernel_module.md @@ -0,0 +1,68 @@ +# Install the cross compiler + + sudo apt-get update + sudo apt-get install -y gcc-arm-linux-gnueabihf + +# Determine the correct kernel version on the target pi + + dpkg-query -l raspberrypi-kernel + +# Get the kernel source for the correct kernel version + + wget https://github.com/raspberrypi/linux/archive/raspberrypi-kernel_1.20171029-1.tar.gz + tar xf raspberrypi-kernel_1.20171029-1.tar.gz + cd linux-raspberrypi-kernel_1.20171029-1 + +# Prep the kernel source + + KERNEL=kernel7 + make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- bcm2709_defconfig + make -j 8 ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- modules_prepare + +# Create hello.c module + + #include + #include + + MODULE_LICENSE("GPL"); + + static int hello_init(void) { + printk(KERN_ALERT "Hello world!\n"); + return 0; + } + + static void hello_exit(void) { + printk(KERN_ALERT "Goodbye cruel world!\n"); + } + + module_init(hello_init); + module_exit(hello_exit); + +# Create a Makefile + + CROSS := arm-linux-gnueabihf- + DIR:=$(shell dirname $(realpath $(lastword $(MAKEFILE_LIST)))) + obj-m := hello.o + + all: + $(MAKE) ARCH=arm CROSS_COMPILE=$(CROSS) -C kernel M=$(DIR) modules + + clean: + $(MAKE) ARCH=arm CROSS_COMPILE=$(CROSS) -C kernel M=$(DIR) clean + +# Compile the module + + ln -sf path/to/rpi-kernel kernel + make + +# Copy module to rpi + + scp hello.ko pi.local: + +# Load it on the pi + + sudo insmod hello.ko + +# Look for message in syslog + + tail /var/log/syslog diff --git a/package.json b/package.json index e936b17..326139f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bbctrl", - "version": "0.4.7", + "version": "0.4.8", "homepage": "http://buildbotics.com/", "repository": "https://github.com/buildbotics/bbctrl-firmware", "license": "GPL-3.0+", diff --git a/scripts/bbctrl.init.d b/scripts/bbctrl.init.d deleted file mode 100755 index 54d4366..0000000 --- a/scripts/bbctrl.init.d +++ /dev/null @@ -1,59 +0,0 @@ -#!/bin/bash - -### BEGIN INIT INFO -# Provides: bbctl -# Required-Start: $local_fs $network -# Required-Stop: $local_fs $network -# Default-Start: 2 3 4 5 -# Default-Stop: 0 1 6 -# Short-Description: Buildbotics Controller Web service -# Description: Buildbotics Controller Web service -### END INIT INFO - - -DAEMON_NAME=bbctrl -DAEMON=/usr/local/bin/$DAEMON_NAME -DAEMON_OPTS="" -DAEMON_USER=root -DAEMON_DIR=/var/lib/$DAEMON_NAME -PIDFILE=/var/run/$DAEMON_NAME.pid -DEFAULTS=/etc/default/$DAEMON_NAME - - -. /lib/lsb/init-functions - - -if [ -e $DEFAULTS ]; then - . $DEFAULTS -fi - - -do_start () { - log_daemon_msg "Starting system $DAEMON_NAME daemon" - mkdir -p $DAEMON_DIR && - start-stop-daemon --start --background --pidfile $PIDFILE --make-pidfile \ - --user $DAEMON_USER --chuid $DAEMON_USER --chdir $DAEMON_DIR \ - --startas $DAEMON -- $DAEMON_OPTS -l /var/log/$DAEMON_NAME.log - log_end_msg $? -} - - -do_stop () { - log_daemon_msg "Stopping system $DAEMON_NAME daemon" - start-stop-daemon --stop --pidfile $PIDFILE --retry 10 - log_end_msg $? -} - - -case "$1" in - start|stop) do_${1} ;; - restart|reload|force-reload) do_stop; do_start ;; - status) - status_of_proc "$DAEMON_NAME" "$DAEMON" && exit 0 || exit $? - ;; - - *) - echo "Usage: /etc/init.d/$DAEMON_NAME {start|stop|restart|status}" - exit 1 - ;; -esac diff --git a/scripts/bbctrl.service b/scripts/bbctrl.service new file mode 100644 index 0000000..8b22cc7 --- /dev/null +++ b/scripts/bbctrl.service @@ -0,0 +1,15 @@ +[Unit] +Description=Buildbotics Controller +After=network.target + +[Service] +User=root +ExecStart=/usr/local/bin/bbctrl -l /var/log/bbctrl.log +WorkingDirectory=/var/lib/bbctrl +Restart=always +StandardOutput=null +Nice=-10 +KillMode=process + +[Install] +WantedBy=multi-user.target diff --git a/scripts/demo-chroot b/scripts/demo-chroot new file mode 100755 index 0000000..75e7578 --- /dev/null +++ b/scripts/demo-chroot @@ -0,0 +1,17 @@ +#!/bin/bash -ex + +ROOT="$PWD/demo" + +# Clean up on EXIT +function cleanup { + umount "$ROOT"/{dev/pts,dev,sys,proc} 2>/dev/null || true +} +trap cleanup EXIT + +# mount binds +mount --bind /dev "$ROOT/dev/" +mount --bind /sys "$ROOT/sys/" +mount --bind /proc "$ROOT/proc/" +mount --bind /dev/pts "$ROOT/dev/pts" + +chroot "$ROOT" "$@" diff --git a/scripts/install.sh b/scripts/install.sh index 49965e8..86f796e 100755 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -13,36 +13,42 @@ while [ $# -gt 0 ]; do done -if $UPDATE_PY; then - if [ -e /var/run/bbctrl.pid ]; then - service bbctrl stop +function update_config_txt() { + MATCH="$1" + CONFIG="$2" + + grep "$MATCH" /boot/config.txt + + if [ $? -ne 0 ]; then + mount -o remount,rw /boot && + echo "$CONFIG" >> /boot/config.txt + mount -o remount,ro /boot fi + +} + + +if $UPDATE_PY; then + service bbctrl stop + + # Update service + rm -f /etc/init.d/bbctrl + cp scripts/bbctrl.service /etc/systemd/system/ + systemctl daemon-reload + systemctl enable bbctrl fi if $UPDATE_AVR; then ./scripts/avr109-flash.py src/avr/bbctrl-avr-firmware.hex fi -# Increase USB current -grep max_usb_current /boot/config.txt >/dev/null -if [ $? -ne 0 ]; then - mount -o remount,rw /boot && - echo max_usb_current=1 >> /boot/config.txt - mount -o remount,ro /boot -fi - +# Update config.txt +update_config_txt ^max_usb_current max_usb_current=1 +update_config_txt ^config_hdmi_boost config_hdmi_boost=8 # TODO Enable GPU -#grep ^dtoverlay=vc4-kms-v3d /boot/config.txt >/dev/null -#if [ $? -ne 0 ]; then -# mount -o remount,rw /boot && -# ( -# echo -# echo dtoverlay=vc4-kms-v3d -# echo gpu_mem=16 -# ) >> /boot/config.txt -# mount -o remount,ro /boot -#fi +#update_config_txt dtoverlay=vc4-kms "dtoverlay=vc4-kms-v3d" +#update_config_txt gpu_mem "gpu_mem=16" #chmod ug+s /usr/lib/xorg/Xorg # Fix camera @@ -68,17 +74,6 @@ if [ -e /etc/init.d/hawkeye ]; then apt-get remove --purge -y hawkeye fi -# Enable USB audio -if [ ! -e /etc/asound.conf ]; then - ( - echo "pcm.!default {" - echo " type asym" - echo " playback.pcm \"plug:hw:0\"" - echo " capture.pcm \"plug:dsnoop:1\"" - echo "}" - ) > etc/asound.conf -fi - # Decrease boot delay sed -i 's/^TimeoutStartSec=.*$/TimeoutStartSec=1/' \ /etc/systemd/system/network-online.target.wants/networking.service @@ -118,6 +113,16 @@ cp scripts/xinitrc ~pi/.xinitrc chmod +x ~pi/.xinitrc chown pi:pi ~pi/.xinitrc +# Install bbserial +MODSRC=src/bbserial/bbserial.ko +MODDST=/lib/modules/$(uname -r)/kernel/drivers/tty/serial/bbserial.ko +diff -q $MODSRC $MODDST 2>/dev/null >/dev/null +if [ $? -ne 0 ]; then + cp $MODSRC $MODDST + depmod + REBOOT=true +fi + # Install rc.local cp scripts/rc.local /etc/ diff --git a/scripts/rc.local b/scripts/rc.local index 1270715..aecc57f 100755 --- a/scripts/rc.local +++ b/scripts/rc.local @@ -3,6 +3,11 @@ # Mount /boot read only mount -o remount,ro /boot +# Load bbserial +echo 3f201000.serial > /sys/bus/amba/drivers/uart-pl011/unbind +modprobe -r bbserial +modprobe bbserial + # Set SPI GPIO mode gpio mode 27 alt3 diff --git a/src/avr/src/command.c b/src/avr/src/command.c index 17aee9c..916c388 100644 --- a/src/avr/src/command.c +++ b/src/avr/src/command.c @@ -49,13 +49,11 @@ #include -#define RING_BUF_ATOMIC_COPY(TO, FROM) \ - ATOMIC_BLOCK(ATOMIC_RESTORESTATE) TO = FROM - #define RING_BUF_NAME sync_q #define RING_BUF_TYPE uint8_t #define RING_BUF_INDEX_TYPE volatile uint16_t #define RING_BUF_SIZE SYNC_QUEUE_SIZE +#define RING_BUF_ATOMIC_COPY 1 #include "ringbuf.def" diff --git a/src/avr/src/config.h b/src/avr/src/config.h index a99be90..512b3f3 100644 --- a/src/avr/src/config.h +++ b/src/avr/src/config.h @@ -219,7 +219,7 @@ enum { #define VELOCITY_MULTIPLIER 1000.0 #define ACCEL_MULTIPLIER 1000000.0 #define JERK_MULTIPLIER 1000000.0 -#define SYNC_QUEUE_SIZE 2048 +#define SYNC_QUEUE_SIZE 4096 #define EXEC_FILL_TARGET 8 #define EXEC_DELAY 250 // ms #define JOG_STOPPING_UNDERSHOOT 1 // % of stopping distance diff --git a/src/avr/src/ringbuf.def b/src/avr/src/ringbuf.def index 1f444a0..9fff964 100644 --- a/src/avr/src/ringbuf.def +++ b/src/avr/src/ringbuf.def @@ -53,6 +53,9 @@ #include #include +#include + + #ifndef RING_BUF_NAME #error Must define RING_BUF_NAME #endif @@ -106,6 +109,11 @@ RING_BUF_FUNC void CONCAT(RING_BUF_NAME, _init)() { #ifdef RING_BUF_ATOMIC_COPY +#undef RING_BUF_ATOMIC_COPY +#define RING_BUF_ATOMIC_COPY(TO, FROM) \ + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) TO = FROM + + #define RING_BUF_ATOMIC_READ_INDEX(INDEX) \ RING_BUF_FUNC RING_BUF_INDEX_TYPE CONCAT(RING_BUF_NAME, _read_##INDEX)() { \ RING_BUF_INDEX_TYPE index; \ diff --git a/src/avr/src/usart.c b/src/avr/src/usart.c index 17dba80..f364332 100644 --- a/src/avr/src/usart.c +++ b/src/avr/src/usart.c @@ -38,12 +38,16 @@ // Ring buffers +#define RING_BUF_INDEX_TYPE volatile uint16_t #define RING_BUF_NAME tx_buf #define RING_BUF_SIZE USART_TX_RING_BUF_SIZE +#define RING_BUF_ATOMIC_COPY 1 #include "ringbuf.def" +#define RING_BUF_INDEX_TYPE volatile uint16_t #define RING_BUF_NAME rx_buf #define RING_BUF_SIZE USART_RX_RING_BUF_SIZE +#define RING_BUF_ATOMIC_COPY 1 #include "ringbuf.def" static bool _flush = false; @@ -60,9 +64,9 @@ static void _set_rxc_interrupt(bool enable) { if (SERIAL_CTS_THRESH <= rx_buf_space()) OUTCLR_PIN(SERIAL_CTS_PIN); // CTS Lo (enable) - SERIAL_PORT.CTRLA |= USART_RXCINTLVL_MED_gc; + SERIAL_PORT.CTRLA |= USART_RXCINTLVL_HI_gc; - } else SERIAL_PORT.CTRLA &= ~USART_RXCINTLVL_MED_gc; + } else SERIAL_PORT.CTRLA &= ~USART_RXCINTLVL_HI_gc; } diff --git a/src/avr/src/usart.h b/src/avr/src/usart.h index 727c697..b59f4ed 100644 --- a/src/avr/src/usart.h +++ b/src/avr/src/usart.h @@ -33,8 +33,9 @@ #include -#define USART_TX_RING_BUF_SIZE 256 -#define USART_RX_RING_BUF_SIZE 256 +// NOTE, RING_BUF_INDEX_TYPE must be be large enough to cover the buffer +#define USART_TX_RING_BUF_SIZE 1024 +#define USART_RX_RING_BUF_SIZE 1024 typedef enum { diff --git a/src/bbserial/Makefile b/src/bbserial/Makefile new file mode 100644 index 0000000..6482bfc --- /dev/null +++ b/src/bbserial/Makefile @@ -0,0 +1,10 @@ +CROSS := arm-linux-gnueabihf- +DIR:=$(shell dirname $(realpath $(lastword $(MAKEFILE_LIST)))) +obj-m := bbserial.o +ccflags-y := -std=gnu99 -Wno-declaration-after-statement + +all: + $(MAKE) ARCH=arm CROSS_COMPILE=$(CROSS) -C kernel M=$(DIR) modules + +clean: + $(MAKE) ARCH=arm CROSS_COMPILE=$(CROSS) -C kernel M=$(DIR) clean diff --git a/src/bbserial/bbserial.c b/src/bbserial/bbserial.c new file mode 100644 index 0000000..8a5745f --- /dev/null +++ b/src/bbserial/bbserial.c @@ -0,0 +1,558 @@ +/******************************************************************************\ + + This file is part of the Buildbotics firmware. + + Copyright (c) 2015 - 2019, Buildbotics LLC + All rights reserved. + + This file ("the software") is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License, + version 2 as published by the Free Software Foundation. You should + have received a copy of the GNU General Public License, version 2 + along with the software. If not, see . + + The software 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the software. If not, see + . + + For information regarding this software email: + "Joseph Coffland" + +\******************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Joseph Coffland"); +MODULE_DESCRIPTION("Buildbotics controller serial port driver"); +MODULE_VERSION("0.2"); + + +#define DEVICE_NAME "ttyBB0" +#define BUF_SIZE (1 << 16) + + +enum { + REG_DR, + REG_ST_DMAWM, + REG_ST_TIMEOUT, + REG_FR, + REG_LCRH_RX, + REG_LCRH_TX, + REG_IBRD, + REG_FBRD, + REG_CR, + REG_IFLS, + REG_IMSC, + REG_RIS, + REG_MIS, + REG_ICR, + REG_DMACR, + REG_ST_XFCR, + REG_ST_XON1, + REG_ST_XON2, + REG_ST_XOFF1, + REG_ST_XOFF2, + REG_ST_ITCR, + REG_ST_ITIP, + REG_ST_ABCR, + REG_ST_ABIMSC, + REG_ARRAY_SIZE, +}; + + +static u16 _offsets[REG_ARRAY_SIZE] = { + [REG_DR] = UART01x_DR, + [REG_FR] = UART01x_FR, + [REG_LCRH_RX] = UART011_LCRH, + [REG_LCRH_TX] = UART011_LCRH, + [REG_IBRD] = UART011_IBRD, + [REG_FBRD] = UART011_FBRD, + [REG_CR] = UART011_CR, + [REG_IFLS] = UART011_IFLS, + [REG_IMSC] = UART011_IMSC, + [REG_RIS] = UART011_RIS, + [REG_MIS] = UART011_MIS, + [REG_ICR] = UART011_ICR, + [REG_DMACR] = UART011_DMACR, +}; + + +static int _debug = 0; + +struct ring_buf { + unsigned char *buf; + unsigned head; + unsigned tail; +}; + +static struct { + struct clk *clk; + struct ring_buf tx_buf; + struct ring_buf rx_buf; + spinlock_t lock; + unsigned open; + unsigned char __iomem *base; + wait_queue_head_t wait; + unsigned long req_events; + unsigned irq; + unsigned im; // interrupt mask + + unsigned rx_bytes; + unsigned tx_bytes; + + unsigned brk_errs; + unsigned parity_errs; + unsigned frame_errs; + unsigned overruns; + + int major; + struct class *class; + struct device *dev; +} _port; + + +#define RING_BUF_INC(BUF, INDEX) \ + do {(BUF).INDEX = ((BUF).INDEX + 1) & (BUF_SIZE - 1);} while (0) + +#define RING_BUF_POP(BUF) RING_BUF_INC(BUF, tail) + +#define RING_BUF_PUSH(BUF, C) \ + do { \ + (BUF).buf[(BUF).head] = C; \ + RING_BUF_INC(BUF, head); \ + } while (0) + +#define RING_BUF_POKE(BUF) (BUF).buf[(BUF).head] +#define RING_BUF_PEEK(BUF) (BUF).buf[(BUF).tail] + +#define RING_BUF_MOVE(SRC, DST) \ + do { \ + RING_BUF_PUSH(DST, RING_BUF_PEEK(SRC)); \ + RING_BUF_POP(SRC); \ + } while (0) + + +#define RING_BUF_SPACE(BUF) ((((BUF).tail) - ((BUF).head + 1)) & (BUF_SIZE - 1)) +#define RING_BUF_FILL(BUF) ((((BUF).head) - ((BUF).tail)) & (BUF_SIZE - 1)) + + +static unsigned _read(unsigned reg) { + return readw_relaxed(_port.base + _offsets[reg]); +} + + +static void _write(unsigned val, unsigned reg) { + writew_relaxed(val, _port.base + _offsets[reg]); +} + + +static unsigned _tx_chars(void) { + unsigned bytes = 0; + unsigned fill = RING_BUF_FILL(_port.tx_buf); + + for (int i = 0; i < fill; i++) { + // Check if UART FIFO full + if (_read(REG_FR) & UART01x_FR_TXFF) break; + + _write(RING_BUF_PEEK(_port.tx_buf), REG_DR); + RING_BUF_POP(_port.tx_buf); + mb(); + _port.tx_bytes++; + bytes++; + } + + // Stop TX when buffer is empty + if (!RING_BUF_FILL(_port.tx_buf)) { + _port.im &= ~UART011_TXIM; + _write(_port.im, REG_IMSC); + } + + return bytes; +} + + +static unsigned _rx_chars(void) { + unsigned bytes = 0; + unsigned space = RING_BUF_SPACE(_port.rx_buf); + + for (int i = 0; i < space; i++) { + // Check if UART FIFO empty + unsigned status = _read(REG_FR); + if (status & UART01x_FR_RXFE) break; + + // Read char from FIFO and update status + unsigned ch = _read(REG_DR); + _port.rx_bytes++; + + // Record errors + if (ch & UART011_DR_BE) _port.brk_errs++; + if (ch & UART011_DR_PE) _port.parity_errs++; + if (ch & UART011_DR_FE) _port.frame_errs++; + if (ch & UART011_DR_OE) _port.overruns++; + + // Queue char + RING_BUF_PUSH(_port.rx_buf, ch); + bytes++; + } + + // Stop RX interrupts when buffer is full + if (!RING_BUF_SPACE(_port.rx_buf)) { + _port.im &= ~(UART011_RXIM | UART011_RTIM | UART011_FEIM | UART011_PEIM | + UART011_BEIM | UART011_OEIM); + _write(_port.im, REG_IMSC); + } + + return bytes; +} + + +static irqreturn_t _interrupt(int irq, void *id) { + unsigned long flags; + spin_lock_irqsave(&_port.lock, flags); + + unsigned rBytes = 0; + unsigned wBytes = 0; + + for (int pass = 0; pass < 256; pass++) { + unsigned status = _read(REG_MIS); + if (!status) break; + + // Clear interrupt status + _write(status, REG_ICR); + + // Read and/or write + if (status & (UART011_RTIS | UART011_RXIS)) rBytes += _rx_chars(); + if (status & UART011_TXIS) wBytes += _tx_chars(); + } + + spin_unlock_irqrestore(&_port.lock, flags); + + // Notify pollers + if ((rBytes && (_port.req_events & POLLIN)) || + (wBytes && (_port.req_events & POLLOUT))) + wake_up_interruptible(&_port.wait); + + return IRQ_HANDLED; +} + + +static void _enable_tx(void) { + unsigned long flags; + spin_lock_irqsave(&_port.lock, flags); + + _port.im |= UART011_TXIM; + _write(_port.im, REG_IMSC); + _tx_chars(); // Must prime the pump + + spin_unlock_irqrestore(&_port.lock, flags); +} + + +static void _enable_rx(void) { + unsigned long flags; + spin_lock_irqsave(&_port.lock, flags); + + _port.im |= UART011_RTIM | UART011_RXIM; + _write(_port.im, REG_IMSC); + + spin_unlock_irqrestore(&_port.lock, flags); +} + + +static int _dev_open(struct inode *inodep, struct file *filep) { + if (_debug) printk(KERN_INFO "bbserial: open()\n"); + if (_port.open) return -EBUSY; + _port.open = 1; + return 0; +} + + +static ssize_t _dev_read(struct file *filep, char *buffer, size_t len, + loff_t *offset) { + if (_debug) printk(KERN_INFO "bbserial: read() len=%d\n", len); + ssize_t bytes = 0; + + // TODO read whole blocks + while (len && RING_BUF_FILL(_port.rx_buf)) { + put_user(RING_BUF_PEEK(_port.rx_buf), buffer++); + RING_BUF_POP(_port.rx_buf); + len--; + bytes++; + } + + if (bytes) _enable_rx(); + + return bytes ? bytes : -EAGAIN; +} + + +static ssize_t _dev_write(struct file *filep, const char *buffer, size_t len, + loff_t *offset) { + if (_debug) + printk(KERN_INFO "bbserial: write() len=%d tx=%d rx=%d\n", + len, RING_BUF_FILL(_port.tx_buf), RING_BUF_FILL(_port.rx_buf)); + + ssize_t bytes = 0; + + // TODO write whole blocks + while (len && RING_BUF_SPACE(_port.tx_buf)) { + get_user(RING_BUF_POKE(_port.tx_buf), buffer++); + RING_BUF_INC(_port.tx_buf, head); + len--; + bytes++; + } + + if (bytes) _enable_tx(); + + return bytes ? bytes : -EAGAIN; +} + + +static int _dev_release(struct inode *inodep, struct file *filep) { + printk(KERN_INFO "bbserial: release()\n"); + _port.open = 0; + return 0; +} + + +static unsigned _dev_poll(struct file *file, poll_table *wait) { + if (_debug) printk(KERN_INFO "bbserial: poll(tx=%d rx=%d)\n", + RING_BUF_FILL(_port.tx_buf), RING_BUF_FILL(_port.rx_buf)); + + _port.req_events = poll_requested_events(wait); + poll_wait(file, &_port.wait, wait); + _port.req_events = 0; + + unsigned ret = 0; + if (RING_BUF_SPACE(_port.tx_buf)) ret |= POLLOUT | POLLWRNORM; + if (RING_BUF_FILL(_port.rx_buf)) ret |= POLLIN | POLLRDNORM; + + return ret; +} + + +static long _dev_ioctl(struct file *file, unsigned cmd, unsigned long arg) { + if (_debug) printk(KERN_INFO "bbserial: ioctl() cmd=%d arg=%lu\n", cmd, arg); + + int __user *ptr = (int __user *)arg; + + switch (cmd) { + case TCGETS: // TODO Get serial port settings + case TCSETS: // TODO Set serial port settings + case TIOCMBIS: // TODO Set modem control state + return 0; + + case TCFLSH: return 0; + case FIONREAD: return put_user(RING_BUF_FILL(_port.rx_buf), ptr); + default: return -ENOIOCTLCMD; + } + + return 0; +} + + +static struct file_operations _ops = { + .owner = THIS_MODULE, + .open = _dev_open, + .read = _dev_read, + .write = _dev_write, + .release = _dev_release, + .poll = _dev_poll, + .unlocked_ioctl = _dev_ioctl, +}; + + +static int _probe(struct amba_device *dev, const struct amba_id *id) { + if (_debug) printk(KERN_INFO "bbserial: probing\n"); + + // Allocate buffers + _port.tx_buf.buf = devm_kzalloc(&dev->dev, BUF_SIZE, GFP_KERNEL); + _port.rx_buf.buf = devm_kzalloc(&dev->dev, BUF_SIZE, GFP_KERNEL); + if (!_port.tx_buf.buf || !_port.rx_buf.buf) return -ENOMEM; + + // Map IO memory + _port.base = devm_ioremap_resource(&dev->dev, &dev->res); + if (IS_ERR(_port.base)) { + dev_err(&dev->dev, "bbserial: failed to map IO memory\n"); + return PTR_ERR(_port.base); + } + + // Get and enable clock + _port.clk = devm_clk_get(&dev->dev, 0); + if (IS_ERR(_port.clk)) { + dev_err(&dev->dev, "bbserial: failed to get clock\n"); + return PTR_ERR(_port.clk); + } + + int ret = clk_prepare_enable(_port.clk); + if (ret) { + dev_err(&dev->dev, "bbserial: clock prepare failed\n"); + return ret; + } + + // Disable UART and mask interrupts + _write(0, REG_CR); + _write(0, REG_IMSC); + + // Set baud rate + const unsigned baud = 230400; + unsigned brd = clk_get_rate(_port.clk) * 16 / baud; + if ((brd & 3) == 3) brd = (brd >> 2) + 1; + else brd >>= 2; + _write(brd & 0x3f, REG_FBRD); + _write(brd >> 6, REG_IBRD); + + // N81 & enable FIFOs + _write(UART01x_LCRH_WLEN_8 | UART01x_LCRH_FEN, REG_LCRH_RX); + + // Enable, TX, RX, RTS, DTR & CTS + unsigned cr = UART01x_CR_UARTEN | UART011_CR_RXE | UART011_CR_TXE | + UART011_CR_RTS | UART011_CR_DTR | UART011_CR_CTSEN; + _write(cr, REG_CR); + + // Set interrupt FIFO trigger levels + _write(UART011_IFLS_RX2_8 | UART011_IFLS_TX6_8, REG_IFLS); + + // Clear pending interrupts + _write(0x7ff, REG_ICR); + + // Enable read interrupts + _port.im = _read(REG_IMSC); + _enable_rx(); + + // Allocate IRQ + _port.irq = dev->irq[0]; + ret = request_irq(_port.irq, _interrupt, 0, "bbserial", &_port); + + if (ret) { + dev_err(&dev->dev, "bbserial: request for IRQ failed\n"); + clk_disable_unprepare(_port.clk); + return ret; + } + + // Dynamically allocate device major number + _port.major = register_chrdev(0, DEVICE_NAME, &_ops); + if (_port.major < 0) { + clk_disable_unprepare(_port.clk); + dev_err(&dev->dev, "bbserial: failed to register a major number\n"); + return _port.major; + } + + // Register device class + _port.class = class_create(THIS_MODULE, "bbs"); + if (IS_ERR(_port.class)) { + unregister_chrdev(_port.major, DEVICE_NAME); + clk_disable_unprepare(_port.clk); + dev_err(&dev->dev, "bbserial: ailed to register device class\n"); + return PTR_ERR(_port.class); + } + + // Register device driver + _port.dev = + device_create(_port.class, 0, MKDEV(_port.major, 0), 0, DEVICE_NAME); + if (IS_ERR(_port.dev)) { + class_destroy(_port.class); + unregister_chrdev(_port.major, DEVICE_NAME); + dev_err(&dev->dev, "bbserial: failed to create the device\n"); + return PTR_ERR(_port.dev); + } + + return 0; +} + + +static int _remove(struct amba_device *dev) { + if (_debug) printk(KERN_INFO "bbserial: removing\n"); + + unsigned long flags; + spin_lock_irqsave(&_port.lock, flags); + + // Mask and clear interrupts + _write(0, REG_IMSC); + _write(0x7ff, REG_ICR); + + // Disable UART + _write(0, REG_CR); + + spin_unlock_irqrestore(&_port.lock, flags); + + // Free IRQ + free_irq(_port.irq, &_port); + synchronize_irq(_port.irq); + + // Shut down the clock producer + clk_disable_unprepare(_port.clk); + + // Unload char dev + device_destroy(_port.class, MKDEV(_port.major, 0)); + class_unregister(_port.class); + class_destroy(_port.class); + unregister_chrdev(_port.major, DEVICE_NAME); + + return 0; +} + + +static struct amba_id _ids[] = { + { + .id = 0x00041011, + .mask = 0x000fffff, + .data = 0, + }, + {0, 0}, +}; + + +MODULE_DEVICE_TABLE(amba, _ids); + + +static struct amba_driver _driver = { + .drv = {.name = "bbserial"}, + .id_table = _ids, + .probe = _probe, + .remove = _remove, +}; + + +static int __init bbserial_init(void) { + printk(KERN_INFO "bbserial: loaded\n"); + + // Clear memory + memset(&_port, 0, sizeof(_port)); + + // Init lock + spin_lock_init(&_port.lock); + + // Init wait queues + init_waitqueue_head(&_port.wait); + + return amba_driver_register(&_driver); +} + + +static void __exit bbserial_exit(void) { + amba_driver_unregister(&_driver); + printk(KERN_INFO "bbserial: unloaded\n"); +} + + +module_init(bbserial_init); +module_exit(bbserial_exit); diff --git a/src/pug/templates/tool-view.pug b/src/pug/templates/tool-view.pug index 3ef7bf5..3ce29b9 100644 --- a/src/pug/templates/tool-view.pug +++ b/src/pug/templates/tool-view.pug @@ -58,7 +58,7 @@ script#tool-view-template(type="text/x-template") fieldset.modbus-program( v-if="is_modbus && this.tool_type != 'HUANYANG VFD'") h2 Active Modbus Program - p + p(v-if="$root.modified") | (Click #[tt(class="save") Save] to activate the selected | #[b tool-type].) table.modbus-regs.fixed-regs @@ -146,6 +146,46 @@ script#tool-view-template(type="text/x-template") | | and spindle type. + .notes(v-if="tool_type.startsWith('NOWFOREVER VFD')") + h2 Notes + p Set the following using the VFD's frontpanel. + table.modbus-regs.fixed-regs + tr + th Address + th Value + th Meaning + th Description + tr + td.reg-addr P0-000 + td.reg-value 2 + td Modbus communication + td Command source + tr + td.reg-addr P0-001 + td.reg-value 0 + td Main frequence X + td Select frequency source + tr + td.reg-addr P0-002 + td.reg-value 6 + td Modbus communication + td Main frequency X + tr + td.reg-addr P0-055 + td.reg-value 1 + td Modbus ID + td Must match #[tt bus-id] above + tr + td.reg-addr P0-056 + td.reg-value 2 + td 9600 baud + td Must match #[tt baud] above + tr + td.reg-addr P0-057 + td.reg-value 0 + td 1 start, 8 data, no parity, 1 stop + td Must match #[tt parity] above + .notes(v-if="tool_type.startsWith('DELTA VFD015M21A')") h2 Notes p Set the following using the VFD's frontpanel. diff --git a/src/py/bbctrl/AVR.py b/src/py/bbctrl/AVR.py index 110e513..6ef0db7 100644 --- a/src/py/bbctrl/AVR.py +++ b/src/py/bbctrl/AVR.py @@ -65,7 +65,7 @@ def serial_set_low_latency(sp): ss = serial_struct() fcntl.ioctl(sp, termios.TIOCGSERIAL, ss) - ss.flags |= 1 << ASYNCB_LOW_LATENCY + ss.flags |= 1 << ASYNCB_LOW_LATENCY # pylint: disable=no-member fcntl.ioctl(sp, termios.TIOCSSERIAL, ss) @@ -88,7 +88,7 @@ class AVR(object): self.sp = serial.Serial(self.ctrl.args.serial, self.ctrl.args.baud, rtscts = 1, timeout = 0, write_timeout = 0) self.sp.nonblocking() - serial_set_low_latency(self.sp) + #serial_set_low_latency(self.sp) except Exception as e: self.sp = None @@ -122,6 +122,7 @@ class AVR(object): def _serial_read(self): try: + data = '' data = self.sp.read(self.sp.in_waiting) self.read_cb(data) diff --git a/src/py/bbctrl/AVREmu.py b/src/py/bbctrl/AVREmu.py index 495e746..7f6be71 100644 --- a/src/py/bbctrl/AVREmu.py +++ b/src/py/bbctrl/AVREmu.py @@ -39,66 +39,83 @@ class AVREmu(object): self.ctrl = ctrl self.log = ctrl.log.get('AVREmu') - self.avrOut = None - self.avrIn = None - self.i2cOut = None - self.read_cb = None + self.avrOut = None + self.avrIn = None + self.i2cOut = None + self.read_cb = None self.write_cb = None - self.pid = None + self.pid = None def close(self): - if self.pid is None: return - os.kill(self.pid, signal.SIGKILL) - os.waitpid(self.pid, 0) - self.pid = None + # Close pipes + def _close(fd, withHandle): + if fd is None: return + try: + if withHandle: self.ctrl.ioloop.remove_handler(fd) + except: pass + try: + os.close(fd) + except: pass + + _close(self.avrOut, True) + _close(self.avrIn, True) + _close(self.i2cOut, False) + + self.avrOut, self.avrIn, self.i2cOut = None, None, None + + # Kill process and wait for it + if self.pid is not None: + os.kill(self.pid, signal.SIGKILL) + os.waitpid(self.pid, 0) + self.pid = None def _start(self): try: self.close() - if self.avrOut is not None: - self.ctrl.ioloop.remove_handler(self.avrOut) - os.close(self.avrOut) - - if self.avrIn is not None: - self.ctrl.ioloop.remove_handler(self.avrIn) - os.close(self.avrIn) - - if self.i2cOut is not None: os.close(self.i2cOut) - - stdinFDs = os.pipe() + # Create pipes + stdinFDs = os.pipe() stdoutFDs = os.pipe() - i2cFDs = os.pipe() + i2cFDs = os.pipe() + self.pid = os.fork() - if not self.pid: # Child - os.dup2(stdinFDs[0], 0) + if not self.pid: + # Dup child ends + os.dup2(stdinFDs[0], 0) os.dup2(stdoutFDs[1], 1) - os.dup2(i2cFDs[0], 3) + os.dup2(i2cFDs[0], 3) + # Close orig fds os.close(stdinFDs[0]) os.close(stdoutFDs[1]) os.close(i2cFDs[0]) + # Close parent ends + os.close(stdinFDs[1]) + os.close(stdoutFDs[0]) + os.close(i2cFDs[1]) + cmd = ['bbemu'] if self.ctrl.args.fast_emu: cmd.append('--fast') os.execvp(cmd[0], cmd) os._exit(1) # In case of failure - # Parent + # Parent, close child ends os.close(stdinFDs[0]) os.close(stdoutFDs[1]) os.close(i2cFDs[0]) - os.set_blocking(stdinFDs[1], False) + # Non-blocking IO + os.set_blocking(stdinFDs[1], False) os.set_blocking(stdoutFDs[0], False) - os.set_blocking(i2cFDs[1], False) + os.set_blocking(i2cFDs[1], False) self.avrOut = stdinFDs[1] - self.avrIn = stdoutFDs[0] + self.avrIn = stdoutFDs[0] self.i2cOut = i2cFDs[1] ioloop = self.ctrl.ioloop @@ -110,8 +127,7 @@ class AVREmu(object): self.write_enabled = True except Exception: - self.pid = None - self.avrOut, self.avrIn, self.i2cOut = None, None, None + self.close() self.log.exception('Failed to start bbemu') diff --git a/src/py/bbctrl/Camera.py b/src/py/bbctrl/Camera.py index 589ee40..a5f0fcd 100755 --- a/src/py/bbctrl/Camera.py +++ b/src/py/bbctrl/Camera.py @@ -19,7 +19,7 @@ # # # You should have received a copy of the GNU Lesser General Public # # License along with the software. If not, see # -# . # +# . # # # # For information regarding this software email: # # "Joseph Coffland" # diff --git a/src/py/bbctrl/Log.py b/src/py/bbctrl/Log.py index cea8558..090be36 100644 --- a/src/py/bbctrl/Log.py +++ b/src/py/bbctrl/Log.py @@ -87,7 +87,9 @@ class Log(object): self.level = DEBUG if args.verbose else INFO - self.f = None if self.path is None else open(self.path, 'w') + # Open log, rotate if necessary + self.f = None + self._open() # Log header version = pkg_resources.require('bbctrl')[0].version @@ -123,8 +125,10 @@ class Log(object): s = hdr + ('\n' + hdr).join(msg.split('\n')) if self.f is not None: + if 1e22 <= self.bytes_written + len(s) + 1: self._open() self.f.write(s + '\n') self.f.flush() + self.bytes_written += len(s) + 1 print(s) @@ -135,3 +139,22 @@ class Log(object): if where is not None: msg['where'] = where self.broadcast(dict(log = msg)) + + + def _open(self): + if self.path is None: return + if self.f is not None: self.f.close() + self._rotate(self.path) + self.f = open(self.path, 'w') + self.bytes_written = 0 + + + def _rotate(self, path, n = None): + fullpath = '%s.%d' % (path, n) if n is not None else path + nextN = (0 if n is None else n) + 1 + + if os.path.exists(fullpath): + if n == 16: os.unlink(fullpath) + else: self._rotate(path, nextN) + + os.rename(fullpath, '%s.%d' % (path, nextN)) diff --git a/src/py/bbctrl/Preplanner.py b/src/py/bbctrl/Preplanner.py index 2e25179..d961d78 100644 --- a/src/py/bbctrl/Preplanner.py +++ b/src/py/bbctrl/Preplanner.py @@ -31,6 +31,7 @@ import json import hashlib import glob import tempfile +import signal from concurrent.futures import Future from tornado import gen, process, iostream import bbctrl @@ -91,7 +92,10 @@ class Plan(object): def terminate(self): if self.cancel: return self.cancel = True - if self.pid is not None: os.kill(self.pid) + if self.pid is not None: + try: + os.kill(self.pid, signal.SIGKILL) + except: pass def delete(self): @@ -226,7 +230,7 @@ class Preplanner(object): def invalidate(self, filename): if filename in self.plans: - self.plans[filename].terinate() + self.plans[filename].terminate() del self.plans[filename] diff --git a/src/py/bbctrl/__init__.py b/src/py/bbctrl/__init__.py index 4035c91..b8eed91 100644 --- a/src/py/bbctrl/__init__.py +++ b/src/py/bbctrl/__init__.py @@ -80,7 +80,7 @@ def on_exit(sig = 0, func = None): ctrl.close() ctrl = None - sys.exit(1) + sys.exit(0) def time_str(): @@ -125,7 +125,7 @@ def parse_args(): type = int, help = 'HTTP port') parser.add_argument('-a', '--addr', metavar = 'IP', default = '0.0.0.0', help = 'HTTP address to bind') - parser.add_argument('-s', '--serial', default = '/dev/ttyAMA0', + parser.add_argument('-s', '--serial', default = '/dev/ttyBB0', help = 'Serial device') parser.add_argument('-b', '--baud', default = 230400, type = int, help = 'Serial baud rate') -- 2.27.0