Serial driver and basic KISS interface implemented

This commit is contained in:
Mark Qvist 2020-05-25 16:31:55 +02:00
parent a91ab8e5ea
commit f81f856a66
10 changed files with 305 additions and 3 deletions

1
.gitignore vendored
View File

@ -1,3 +1,4 @@
tncattach
# Prerequisites # Prerequisites
*.d *.d

69
KISS.c Normal file
View File

@ -0,0 +1,69 @@
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include "KISS.h"
#include "Serial.h"
int frame_len;
bool IN_FRAME;
bool ESCAPE;
uint8_t kiss_command = CMD_UNKNOWN;
uint8_t frame_buffer[MAX_PAYLOAD];
uint8_t write_buffer[MAX_PAYLOAD*2+3];
void kiss_frame_received(int frame_len) {
}
void kiss_serial_read(uint8_t sbyte) {
if (IN_FRAME && sbyte == FEND && kiss_command == CMD_DATA) {
IN_FRAME = false;
kiss_frame_received(frame_len);
} else if (sbyte == FEND) {
IN_FRAME = true;
kiss_command = CMD_UNKNOWN;
frame_len = 0;
} else if (IN_FRAME && frame_len < MAX_PAYLOAD) {
// Have a look at the command byte first
if (frame_len == 0 && kiss_command == CMD_UNKNOWN) {
// Strip of port nibble
kiss_command = sbyte & 0x0F;
} else if (kiss_command == CMD_DATA) {
if (sbyte == FESC) {
ESCAPE = true;
} else {
if (ESCAPE) {
if (sbyte == TFEND) sbyte = FEND;
if (sbyte == TFESC) sbyte = FESC;
ESCAPE = false;
}
if (frame_len < MAX_PAYLOAD) {
frame_buffer[frame_len++] = sbyte;
}
}
}
}
}
int kiss_write_frame(int serial_port, uint8_t* buffer, int frame_len) {
int write_len = 0;
write_buffer[write_len++] = FEND;
write_buffer[write_len++] = CMD_DATA;
for (int i = 0; i < frame_len; i++) {
uint8_t byte = buffer[i];
if (byte == FEND) {
write_buffer[write_len++] = FESC;
write_buffer[write_len++] = TFEND;
} else if (byte == FESC) {
write_buffer[write_len++] = FESC;
write_buffer[write_len++] = TFESC;
} else {
write_buffer[write_len++] = byte;
}
}
write_buffer[write_len++] = FEND;
return write(serial_port, write_buffer, write_len);
}

7
KISS.h
View File

@ -10,4 +10,9 @@
#define CMD_SLOTTIME 0x03 #define CMD_SLOTTIME 0x03
#define CMD_TXTAIL 0x04 #define CMD_TXTAIL 0x04
#define CMD_FULLDUPLEX 0x05 #define CMD_FULLDUPLEX 0x05
#define CMD_SETHARDWARE 0x06 #define CMD_SETHARDWARE 0x06
#define MAX_PAYLOAD 1522
void kiss_serial_read(uint8_t sbyte);
int kiss_write_frame(int serial_port, uint8_t* buffer, int frame_len);

View File

@ -0,0 +1,3 @@
TNC Attach
==========
Attach KISS TNC devices as network interfaces in Linux and macOS.

168
Serial.c Normal file
View File

@ -0,0 +1,168 @@
#include "Serial.h"
int open_port(char* port) {
int fd;
fd = open(port, O_RDWR | O_NOCTTY | O_SYNC | O_NDELAY);
if (fd == -1) {
perror("The serial port could not be opened");
} else {
fcntl(fd, F_SETFL, 0);
}
return fd;
}
int close_port(int fd) {
return close(fd);
}
void set_speed(void *tty_s, int speed) {
cfsetospeed(tty_s, speed);
cfsetispeed(tty_s, speed);
}
bool setup_port(int fd, int speed) {
struct termios tty;
if (tcgetattr(fd, &tty) != 0) {
perror("Error setting port speed, could not read port parameters");
return false;
}
switch (speed) {
case 0:
set_speed(&tty, B0);
break;
case 50:
set_speed(&tty, B50);
break;
case 75:
set_speed(&tty, B75);
break;
case 110:
set_speed(&tty, B110);
break;
case 134:
set_speed(&tty, B134);
break;
case 150:
set_speed(&tty, B150);
break;
case 200:
set_speed(&tty, B200);
break;
case 300:
set_speed(&tty, B300);
break;
case 600:
set_speed(&tty, B600);
break;
case 1200:
set_speed(&tty, B1200);
break;
case 2400:
set_speed(&tty, B2400);
break;
case 4800:
set_speed(&tty, B4800);
break;
case 9600:
set_speed(&tty, B9600);
break;
case 19200:
set_speed(&tty, B19200);
break;
case 38400:
set_speed(&tty, B38400);
break;
case 57600:
set_speed(&tty, B57600);
break;
case 115200:
set_speed(&tty, B115200);
break;
case 230400:
set_speed(&tty, B230400);
break;
default:
printf("Error: Invalid port speed %d specified", speed);
return false;
}
// Set 8-bit characters, no parity, one stop bit
tty.c_cflag |= CS8;
tty.c_cflag &= ~PARENB;
tty.c_cflag &= ~CSTOPB;
// Disable hardware flow control
tty.c_cflag &= ~CRTSCTS;
// Enable reading and ignore modem
// control lines
tty.c_cflag |= CREAD | CLOCAL;
// Disable canonical mode, echo
// and signal characters.
tty.c_lflag &= ~ICANON;
tty.c_lflag &= ~ECHO;
tty.c_lflag &= ~ECHOE;
tty.c_lflag &= ~ECHONL;
tty.c_lflag &= ~ISIG;
// Disable processing of input,
// just pass the raw data.
tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL);
// Disable XON/XOFF software flow control.
tty.c_iflag &= ~(IXON | IXOFF | IXANY);
// Disable processing output bytes
// and new line conversions
tty.c_oflag &= ~OPOST;
tty.c_oflag &= ~ONLCR;
// Block forever until at least one byte is read.
tty.c_cc[VMIN] = 1;
tty.c_cc[VTIME] = 0;
// TODO: Check these
// Prevent conversion of tabs to spaces (NOT PRESENT IN LINUX)
// tty.c_oflag &= ~OXTABS;
// Prevent removal of C-d chars (0x004) in output (NOT PRESENT IN LINUX)
// tty.c_oflag &= ~ONOEOT;
if (tcsetattr(fd, TCSANOW, &tty) != 0) {
perror("Could not configure serial port parameters");
return false;
} else {
return true;
}
}
bool set_port_blocking(int fd, bool should_block) {
struct termios tty;
memset(&tty, 0, sizeof tty);
if (tcgetattr(fd, &tty) != 0) {
perror("Error configuring port blocking behaviour, could not read port parameters");
return false;
} else {
// TODO: Implement this correctly
if (should_block) {
// Block forever until at least one byte is read.
tty.c_cc[VMIN] = 1;
tty.c_cc[VTIME] = 0;
} else {
// Never block, always return immediately with
// whatever is available.
tty.c_cc[VMIN] = 0;
tty.c_cc[VTIME] = 0;
}
if (tcsetattr(fd, TCSANOW, &tty) != 0) {
perror("Could not set port parameters while configuring blocking behaviour");
return false;
} else {
return true;
}
}
}

12
Serial.h Normal file
View File

@ -0,0 +1,12 @@
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <termios.h>
int open_port(char* port);
int close_port(int fd);
bool setup_port(int fs, int speed);
bool set_port_blocking(int fd, bool should_block);

3
TAP.c Normal file
View File

@ -0,0 +1,3 @@
#include "TAP.h"
char tap_name[IFNAMSIZ];

0
TAP.h Normal file
View File

View File

@ -1,10 +1,11 @@
.DEFAULT_GOAL := all .DEFAULT_GOAL := all
.PHONY: all clean install .PHONY: all clean install tncattach
compiler = gcc compiler = gcc
flags = -lm flags = -lm
all: tncattach all: tncattach
rebuild: clean all
clean: clean:
@echo "Cleaning tncattach build..." @echo "Cleaning tncattach build..."
@ -13,7 +14,7 @@ clean:
tncattach: tncattach:
@echo "Making tncattach..." @echo "Making tncattach..."
@echo "Compiling with: ${compiler}" @echo "Compiling with: ${compiler}"
${compiler} ${flags} tncattach.c -o tncattach ${compiler} ${flags} tncattach.c Serial.c KISS.c TAP.c -o tncattach -Wall
install: all install: all
@echo "Installing tncattach..." @echo "Installing tncattach..."

View File

@ -1,5 +1,45 @@
#include <stdlib.h>
#include <stdbool.h>
#include <signal.h>
#include "Serial.h"
#include "KISS.h" #include "KISS.h"
#include "TAP.h"
#define SERIAL_BUFFER_SIZE 512
int attached_port;
uint8_t serial_buffer[512];
void signal_handler(int signal) {
printf("\r\nClosing serial port...\r\n");
close_port(attached_port);
exit(0);
}
void read_loop(tnc) {
bool should_continue = true;
while (should_continue) {
int len = read(attached_port, serial_buffer, sizeof(serial_buffer));
if (len > 0) {
for (int i = 0; i < len; i++) {
kiss_serial_read(serial_buffer[i]);
}
} else {
printf("Error: Could not read from serial port, exiting now\r\n");
close_port(attached_port);
exit(1);
}
}
}
int main() { int main() {
signal(SIGINT, signal_handler);
attached_port = open_port("/dev/tty.usbserial-");
if (setup_port(attached_port, 115200)) {
printf("Port open\r\n");
read_loop(attached_port);
}
return 0; return 0;
} }