Skip to content

Commit

Permalink
Initial ash_serial commit.
Browse files Browse the repository at this point in the history
  • Loading branch information
samuelventura committed Dec 29, 2022
1 parent 26cb320 commit 373de8f
Show file tree
Hide file tree
Showing 13 changed files with 403 additions and 1 deletion.
4 changes: 4 additions & 0 deletions .formatter.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Used by "mix format"
[
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
]
29 changes: 29 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# The directory Mix will write compiled artifacts to.
/_build/

# If you run "mix test --cover", coverage assets end up here.
/cover/

# The directory Mix downloads your dependencies sources to.
/deps/

# Where third-party dependencies like ExDoc output generated docs.
/doc/

# Ignore .fetch files in case you like to edit your project deps locally.
/.fetch

# If the VM crashes, it generates a dump, let's ignore it too.
erl_crash.dump

# Also ignore archive artifacts (built via "mix archive.build").
*.ez

# Ignore package tarball (built via "mix hex.build").
ash_serial-*.tar

# Temporary files, for example, from tests.
/tmp/

priv
Makefile.*.env
40 changes: 40 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@

MIX_ENV ?= dev
MIX_TARGET ?= host
UNAME := $(shell uname -s | tr '[:upper:]' '[:lower:]')

SRCDIR = src
PRIVDIR = priv
OUTDIR = $(PRIVDIR)/$(MIX_TARGET)
LOCBIN = ~/.local/bin

#-pedantic -Wall allow unused during debugging
#-Werror remove to run on macos with lots of warnings
PORT_TARGET = $(OUTDIR)/ash_serial
PORT_SOURCES = $(SRCDIR)/*.c
PORT_HEADERS = $(SRCDIR)/*.h
PORT_CFLAGS = -g0 -O3 -pedantic -Wall -Wextra -D_XOPEN_SOURCE=700
PORT_LDFLAGS = -fPIC -Wl,-rpath,'$$ORIGIN' -L$(OUTDIR) -lpthread

.PHONY: all pre clean reset post

all: pre $(PORT_TARGET) post

pre:
env | sort > Makefile.$(UNAME).$(MIX_TARGET).env
[ -d $(OUTDIR) ] || mkdir -p $(OUTDIR)
echo $(MIX_TARGET) > $(PRIVDIR)/target

post:
rm -fR $(OUTDIR)/*.dSYM

clean:
rm -fr $(PRIVDIR)/target
rm -fr $(OUTDIR)

reset: clean
rm -fr _build deps
rm Makefile.*.env

$(PORT_TARGET): $(PORT_SOURCES) $(PORT_HEADERS) Makefile $(FONTS) $(IMAGES) $(TARGET_CAIRO)
$(CC) $(PORT_CFLAGS) $(PORT_SOURCES) $(PORT_LDFLAGS) -o $@
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
# ash_serial
# Ash.Serial

- https://github.com/samuelventura/sport
- https://github.com/samuelventura/sniff
18 changes: 18 additions & 0 deletions lib/ash_serial.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
defmodule AshSerial do
@moduledoc """
Documentation for `AshSerial`.
"""

@doc """
Hello world.
## Examples
iex> AshSerial.hello()
:world
"""
def hello do
:world
end
end
28 changes: 28 additions & 0 deletions mix.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
defmodule AshSerial.MixProject do
use Mix.Project

def project do
[
app: :ash_serial,
version: "0.1.0",
elixir: "~> 1.13",
make_clean: ["clean"],
# make_args: ["-dn"],
compilers: [:elixir_make | Mix.compilers()],
start_permanent: Mix.env() == :prod,
deps: deps()
]
end

def application do
[
extra_applications: [:logger]
]
end

defp deps do
[
{:elixir_make, "~> 0.6", runtime: false}
]
end
end
13 changes: 13 additions & 0 deletions mix.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
%{
"castore": {:hex, :castore, "0.1.20", "62a0126cbb7cb3e259257827b9190f88316eb7aa3fdac01fd6f2dfd64e7f46e9", [:mix], [], "hexpm", "a020b7650529c986c454a4035b6b13a328e288466986307bea3aadb4c95ac98a"},
"elixir_make": {:hex, :elixir_make, "0.7.3", "c37fdae1b52d2cc51069713a58c2314877c1ad40800a57efb213f77b078a460d", [:mix], [{:castore, "~> 0.1", [hex: :castore, repo: "hexpm", optional: true]}], "hexpm", "24ada3e3996adbed1fa024ca14995ef2ba3d0d17b678b0f3f2b1f66e6ce2b274"},
"nerves": {:hex, :nerves, "1.9.1", "09cfd3589af81c4b1ac35179aee5f6d94bde36548a905c2d4d4efa9ff311222e", [:make, :mix], [{:castore, "~> 0.1", [hex: :castore, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:jason, "~> 1.2", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "011409116923e1dbab250a6dc241ab529790163233c44e374ff352af72d09a56"},
"nerves_system_br": {:hex, :nerves_system_br, "1.20.6", "6622ee4230e968a95716105518bbd7ef569ac82b44e9114cf6a78a1ddca65455", [:mix], [], "hexpm", "ca187a129288156a1e5fb4f34d393aeddb7580091f41aefb82dbed6a4e378748"},
"nerves_system_rpi3": {:hex, :nerves_system_rpi3, "1.20.2", "3a43b8333b733e7a9f19b2eaa984bd0f33aa1fca6b8595f4a439eb32482c5ed0", [:mix], [{:nerves, "~> 1.5.4 or ~> 1.6.0 or ~> 1.7.15 or ~> 1.8", [hex: :nerves, repo: "hexpm", optional: false]}, {:nerves_system_br, "1.20.6", [hex: :nerves_system_br, repo: "hexpm", optional: false]}, {:nerves_toolchain_armv7_nerves_linux_gnueabihf, "~> 1.6.0", [hex: :nerves_toolchain_armv7_nerves_linux_gnueabihf, repo: "hexpm", optional: false]}], "hexpm", "0c3b1526e99e84ed05ddaadab629aeed172f40ed06ef59960a3f199a1f9deb8f"},
"nerves_system_rpi4": {:hex, :nerves_system_rpi4, "1.20.2", "db83189a20bbdb439311308c0a344587ddc9b8f295041b458a558bda0922a694", [:mix], [{:nerves, "~> 1.5.4 or ~> 1.6.0 or ~> 1.7.15 or ~> 1.8", [hex: :nerves, repo: "hexpm", optional: false]}, {:nerves_system_br, "1.20.6", [hex: :nerves_system_br, repo: "hexpm", optional: false]}, {:nerves_toolchain_aarch64_nerves_linux_gnu, "~> 1.6.0", [hex: :nerves_toolchain_aarch64_nerves_linux_gnu, repo: "hexpm", optional: false]}], "hexpm", "942333f7754d9b9a34372cf0f1c92a195cb6fae120770569da64dc65f52ebf68"},
"nerves_system_x86_64": {:hex, :nerves_system_x86_64, "1.20.3", "c011857f01d8659f9aef2f3ef344651f115347fd07a260ff9f5e280a52017155", [:mix], [{:nerves, "~> 1.5.4 or ~> 1.6.0 or ~> 1.7.15 or ~> 1.8", [hex: :nerves, repo: "hexpm", optional: false]}, {:nerves_system_br, "1.20.6", [hex: :nerves_system_br, repo: "hexpm", optional: false]}, {:nerves_toolchain_x86_64_nerves_linux_musl, "~> 1.6.0", [hex: :nerves_toolchain_x86_64_nerves_linux_musl, repo: "hexpm", optional: false]}], "hexpm", "2fc00a72c0f1c61b858c20e006caa32cbc1e26a769f20aabb1444817819251c2"},
"nerves_toolchain_aarch64_nerves_linux_gnu": {:hex, :nerves_toolchain_aarch64_nerves_linux_gnu, "1.6.1", "2ddc1aecb55b55e1cf18b7252b8074418c1b7dd5497e1e6a167b5a9fb40955a9", [:mix], [{:nerves, "~> 1.4", [hex: :nerves, repo: "hexpm", optional: false]}, {:nerves_toolchain_ctng, "~> 1.9.0", [hex: :nerves_toolchain_ctng, repo: "hexpm", optional: false]}], "hexpm", "57ed1c2df89c7425aadfb18c8e7da6b9d1be9ffeafa1d54061cc8abe6e5e0e4c"},
"nerves_toolchain_armv7_nerves_linux_gnueabihf": {:hex, :nerves_toolchain_armv7_nerves_linux_gnueabihf, "1.6.1", "4014bc32142ef980e39af90e1377747095f3f3f8517e41815a23638ef1711702", [:mix], [{:nerves, "~> 1.0", [hex: :nerves, repo: "hexpm", optional: false]}, {:nerves_toolchain_ctng, "~> 1.9.0", [hex: :nerves_toolchain_ctng, repo: "hexpm", optional: false]}], "hexpm", "20a529ce1ab54677f121ac97333cc6b7c32f75ebb4b83cee7e0934867d61bbb5"},
"nerves_toolchain_ctng": {:hex, :nerves_toolchain_ctng, "1.9.3", "60e87fde05988c4264babc8d68a9221c7b8fe5dc195b7d1526f29b8e626c735c", [:mix], [{:nerves, "~> 1.0", [hex: :nerves, repo: "hexpm", optional: false]}], "hexpm", "2b6edb0687b7f78d3fa49958d759f647e351b23c1f59f637c617a6dc179994ae"},
"nerves_toolchain_x86_64_nerves_linux_musl": {:hex, :nerves_toolchain_x86_64_nerves_linux_musl, "1.6.1", "3e10d1c8e848afe4c7ea506d89f0a3ea12d63c4e91c6ea6bbc1e458c0ef6f940", [:mix], [{:nerves, "~> 1.0", [hex: :nerves, repo: "hexpm", optional: false]}, {:nerves_toolchain_ctng, "~> 1.9.0", [hex: :nerves_toolchain_ctng, repo: "hexpm", optional: false]}], "hexpm", "8453041055bc2271279674fe284b9e093a1e8bddf931a8502a88afce43c578b2"},
}
42 changes: 42 additions & 0 deletions src/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#include "main.h"

#define DATA_LEN 256

//ash_serial /dev/ttyUSB0 8N1 9600
int main(int argc, char *argv[])
{
UNUSED(argc);
Serial serial = {0};
serial.path = argv[1];
serial.config = argv[2];
serial.speed = atoi(argv[3]);
serial_open(&serial);
if (serial.error != NULL)
{
ash_crash(serial.error);
}
char* data[DATA_LEN];
struct pollfd fds[2];
fds[0].fd = 0;
fds[0].events = POLLIN;
fds[1].fd = serial.fd;
fds[1].events = POLLIN;
while(1) {
int r = poll(fds, 2, -1);
if (r < 0) ash_crash("events poll");
if (fds[0].revents & POLLIN) {
int rn = read(0, data, DATA_LEN);
if (rn <= 0) break;
int wn = write(serial.fd, data, rn);
if (wn != rn) ash_crash("fds[0] wn %d != rn %d", wn, rn);
}
if (fds[1].revents & POLLIN) {
int rn = read(serial.fd, data, DATA_LEN);
if (rn <= 0) break;
int wn = write(1, data, rn);
if (wn != rn) ash_crash("fds[1] wn %d != rn %d", wn, rn);
}
}
exit(1);
return 0;
}
64 changes: 64 additions & 0 deletions src/main.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
#ifndef __MAIN_H__
#define __MAIN_H__

#include <stdarg.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <math.h>
#include <pthread.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <termios.h>
#include <fcntl.h>
#include <poll.h>

#include <stdbool.h>
#include <stdint.h>

#define UNUSED(x) (void)(x)
#define UNUSED2(x,y) UNUSED(x);UNUSED(y);
#define UNUSED3(x,y,z) UNUSED(x);UNUSED(y);UNUSED(z);
// #define MAX(x, y) ((x)>(y)?(x):(y))

void ash_debug(const char* fmt, ...);
void ash_crash(const char* fmt, ...);

// #define __TRACE_ENABLED__

#ifdef __TRACE_ENABLED__

#define ash_trace(...) ash_debug(__VA_ARGS__)
#define ash_trace_every() ash_trace("%s:%s tid:%ul", __FILE__, __func__, pthread_self());
#define ash_trace_once() static bool _##__func__ = false; \
if (!_##__func__) ash_trace_every(); \
_##__func__ = true;

#else

#define ash_trace(...)
#define ash_trace_every()
#define ash_trace_once()

#endif

typedef struct {
int fd;
int speed;
struct termios fdt;
const char* error;
const char *path;
const char *config; // 8N1 | 7E1 | 7O1
} Serial;

void ash_debug(const char* fmt, ...);
void ash_crash(const char* fmt, ...);

int serial_baud(int speed);
void serial_open(Serial *serial);

#endif
127 changes: 127 additions & 0 deletions src/serial.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
#include "main.h"

int serial_baud(int speed)
{
switch (speed) {
case 50: return B50;
case 75: return B75;
case 110: return B110;
case 134: return B134;
case 150: return B150;
case 200: return B200;
case 300: return B300;
case 600: return B600;
case 1200: return B1200;
case 1800: return B1800;
case 2400: return B2400;
case 4800: return B4800;
case 9600: return B9600;
case 19200: return B19200;
case 38400: return B38400;
case 57600: return B57600;
case 115200: return B115200;
case 230400: return B230400;
case 460800: return B460800;
case 500000: return B500000;
case 576000: return B576000;
case 921600: return B921600;
case 1000000: return B1000000;
case 1152000: return B1152000;
case 1500000: return B1500000;
case 2000000: return B2000000;
case 2500000: return B2500000;
case 3000000: return B3000000;
case 3500000: return B3500000;
case 4000000: return B4000000;
default: return -1;
}
}

void serial_open(Serial *serial)
{
struct termios *fdt = &serial->fdt;
memset(fdt, 0, sizeof(struct termios));
serial->fd = open(serial->path, O_RDWR | O_NOCTTY);
if (serial->fd < 0) {
serial->error = "open failed";
return;
}
if (isatty(serial->fd) < 0) {
serial->error = "isatty failed";
return;
}
if (tcgetattr(serial->fd, fdt) < 0) {
serial->error = "tcgetattr failed";
return;
}

fdt->c_cflag |= CLOCAL | CREAD;
fdt->c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
fdt->c_iflag &= ~(INLCR | IGNCR | ICRNL | IXON | IXOFF | IXANY);
fdt->c_oflag &= ~(ONLCR | OCRNL | OPOST);

int baud = serial_baud(serial->speed);
if (baud > 0) {
cfsetispeed(fdt, baud);
cfsetospeed(fdt, baud);
} else {
serial->error = "Invalid speed";
return;
}

// config {8,7}{N,E,O}{1,2}
switch (serial->config[0]) {
case '8':
fdt->c_cflag &= ~CSIZE;
fdt->c_cflag |= CS8;
break;
case '7':
fdt->c_cflag &= ~CSIZE;
fdt->c_cflag |= CS7;
break;
default:
serial->error = "Invalid databits";
return;
}
switch (serial->config[1]) {
case 'N':
fdt->c_cflag &= ~PARENB;
break;
case 'E':
fdt->c_cflag |= PARENB;
fdt->c_cflag &= ~PARODD;
fdt->c_iflag |= INPCK;
fdt->c_iflag |= ISTRIP;
break;
case 'O':
fdt->c_cflag |= PARENB;
fdt->c_cflag |= PARODD;
fdt->c_iflag |= INPCK;
fdt->c_iflag |= ISTRIP;
break;
default:
serial->error = "Invalid parity";
return;
}
switch (serial->config[2]) {
case '1':
fdt->c_cflag &= ~CSTOPB;
break;
case '2':
fdt->c_cflag |= CSTOPB;
break;
default:
serial->error = "Invalid stopbits";
return;
}

// http://unixwiz.net/techtips/termios-vmin-vtime.html
// blocks until 1 char arrives.
fdt->c_cc[VMIN] = 1;
fdt->c_cc[VTIME] = 0;

if (tcsetattr(serial->fd, TCSANOW, fdt) < 0) {
serial->error = "tcsetattr failed";
return;
}
}
Loading

0 comments on commit 373de8f

Please sign in to comment.