summaryrefslogblamecommitdiffstats
path: root/sw/ttyartnet.c
blob: 544f773755300d115a32d57a9767843c1e3a4497 (plain) (tree)










































































































































































































































                                                                                                                                                
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
#include <time.h>
#include <arpa/inet.h>
#include <string.h>
#include <sys/ioctl.h>
#include <termios.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/udp.h>
#include <poll.h>
#include <sys/types.h>
#include <error.h>
#include <fcntl.h>
#define termios asmtermios
#define winsize asmwinsize
#define termio asmtermio
#include <asm/termios.h>
#undef termios
#undef winsize
#undef termio
#define S0(x) (x ? x : "")
// start bit: low, 2 stop bits high
struct artnet {
	char name[8]; // Art-Net\0
	char opcode[2]; // 0x5000 in LE: { 0x00, 0x50 }
	uint8_t version_hi; // 0
	uint8_t version_li; // 14
	uint8_t sequence; // set to 0 to disable sequencing
	uint8_t physical; // original universe
	uint8_t sub_uni;
	uint8_t net;
	uint8_t length_hi;
	uint8_t length_lo;
	unsigned char data[512];
	
} __attribute__((packed));
int samomor = 0;
void handle_me (int s __attribute__((unused))) {
	samomor++;
}
int rate (int what_rate, int uart) {
	int r = 0;
	struct termios2 uartattr2;
	if (ioctl(uart, TCGETS2, &uartattr2) == -1) {
		error_at_line(0, errno, __FILE__, __LINE__, "ioctl TCGETS2, fd: %d", uart);
		r = 1;
		goto r;
	}
	uartattr2.c_cflag &= ~CBAUD;
	uartattr2.c_cflag |= BOTHER;
	uartattr2.c_ispeed = what_rate;
	uartattr2.c_ospeed = what_rate;
	if (ioctl(uart, TCSETS2, &uartattr2) == -1) {
		error_at_line(0, errno, __FILE__, __LINE__, "ioctl TCSETS2");
		r = 2;
		goto r;
	}
r:
	return r;
}
int main (int argc, char ** argv) {
	int r = 0;
	if (argc != 1+1)
		error_at_line(1, 0, __FILE__, __LINE__, "usage: %s /dev/ttyUSB0", S0(argv[0]));
	struct sigaction act = {
		.sa_handler = handle_me,
		.sa_flags = SA_RESTART
	};
	if (sigaction(SIGINT, &act, NULL) == -1)
		error_at_line(2, errno, __FILE__, __LINE__, "sigaction");
	int sock = -1;
	int uart = open(argv[1], O_WRONLY | O_NOCTTY | O_NDELAY | O_CLOEXEC);
	if (uart == -1)
		error_at_line(3, errno, __FILE__, __LINE__, "open");
	struct termios uartattr;
	if (tcgetattr(uart, &uartattr) == -1) {
		error_at_line(0, errno, __FILE__, __LINE__, "tcgetattr");
		r = 4;
		goto r;
	}
	uartattr.c_iflag = 0;
	uartattr.c_oflag = 0;
	uartattr.c_cflag = CS8 | CSTOPB;
	uartattr.c_lflag = 0;
	if (tcflush(uart, TCIOFLUSH) == -1) {
		error_at_line(0, errno, __FILE__, __LINE__, "tcflush");
		r = 5;
		goto r;
	}
	if (tcsetattr(uart, TCSANOW, &uartattr) == -1) {
		error_at_line(0, errno, __FILE__, __LINE__, "tcsetattr");
		r = 6;
		goto r;
	}
	if ((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) {
		error_at_line(0, errno, __FILE__, __LINE__, "socket");
		r = 7;
		goto r;
	}
	int z = 1;
	if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &z, sizeof z) == -1) {
		error_at_line(0, errno, __FILE__, __LINE__, "setsockopt");
		r = 8;
		goto r;
	}
	struct sockaddr_in bind_address = {
		.sin_family = AF_INET,
		.sin_port = htons(6454),
		.sin_addr = {
			.s_addr = INADDR_ANY
		}
	};
	if (bind(sock, (struct sockaddr *) &bind_address, sizeof bind_address) == -1) {
		error_at_line(0, errno, __FILE__, __LINE__, "bind");
		r = 9;
		goto r;
	}
	long long last_dmx_burst = 0;
	struct artnet udp;
	while (!samomor) {
		struct pollfd pollfds[2];
		pollfds[0].fd = sock;
		pollfds[0].events = POLLIN | POLLERR | POLLHUP | POLLNVAL;
		// pollfds[1].fd = uart;
		// pollfds[1].events = POLLOUT | POLLERR | POLLHUP | POLLNVAL;
		int poll_return;
fprintf(stderr, "before poll\n");
		if ((poll_return = poll(pollfds, 1 /* corr: I don't care about uart */, -1)) == -1) {
			error_at_line(0, errno, __FILE__, __LINE__, "poll");
			r = 10;
			goto r;
		}
fprintf(stderr, "after poll\n");
		if (!poll_return) { // this can't happen, timeout is -1
			error_at_line(0, 0, __FILE__, __LINE__, "this can't happen, poll returned 0 and timeout was -1\n");
			r = 11;
			goto r;
		}
		int both_events = pollfds[0].revents /* | pollfds[1].revents */;
		if ((both_events & POLLERR) | (both_events & POLLHUP) | (both_events & POLLNVAL)) {
			error_at_line(0, 0, __FILE__, __LINE__, "(both_events & POLLERR) | (both_events & POLLHUP) | (both_events & POLLNVAL)");
			r = 12;
			goto r;
		}
		struct sockaddr_in sender;
		socklen_t sender_len = sizeof sender;
		ssize_t bytes = recvfrom(sock, &udp, sizeof udp, MSG_DONTWAIT, (struct sockaddr *) &sender, &sender_len);
		if (bytes == -1) {
			error_at_line(0, errno, __FILE__, __LINE__, "recvfrom");
			r = 13;
			goto r;
		} else {
			fprintf(stderr, "received %ld bytes from %s:%d\n", bytes, inet_ntoa(sender.sin_addr), ntohs(sender.sin_port));
		}
		if (udp.opcode[0] != 0x00 || udp.opcode[1] != 0x50)
			continue;
		// if (bytes < 530)
		//	raise(SIGINT);
		for (int i = 0; i < 512; i++)
			if (udp.data[i] != 0)
				fprintf(stderr, "\tDMX channel %d is %u\n", i+1, udp.data[i]);
		struct timespec current_time;
		if (clock_gettime(CLOCK_MONOTONIC, &current_time) == -1) {
			error_at_line(0, errno, __FILE__, __LINE__, "clock_gettime");
			r = 14;
			goto r;
		}
		long long current_time_ms = current_time.tv_sec*1000+current_time.tv_nsec/1000000;
		if (current_time_ms >= last_dmx_burst+50) {
			last_dmx_burst = current_time_ms;
		} else
			continue;
		if (tcflush(uart, TCIOFLUSH) == -1) {
			error_at_line(0, errno, __FILE__, __LINE__, "tcflush");
			r = 15;
			goto r;
		}
		switch (rate(10000, uart)) {
			case 1:
				r = 16;
				goto r;
				break;
			case 2:
				r = 17;
				goto r;
				break;
			default:
				break;
		}
		if (tcflush(uart, TCIOFLUSH) == -1) {
			error_at_line(0, errno, __FILE__, __LINE__, "tcflush");
			r = 18;
			goto r;
		}
		usleep(10000);
		if (write(uart, "\0", 1) == -1) {
			error_at_line(0, errno, __FILE__, __LINE__, "write packet start sequence");
			r = 19;
			goto r;
		}
		usleep(1000);
		switch (rate(250000, uart)) {
			case 1:
				r = 20;
				goto r;
				break;
			case 2:
				r = 21;
				goto r;
				break;
			default:
				break;
		}
		udp.data[-1] = '\0';
#pragma GCC diagnostic ignored "-Wstringop-overread"
		if (write(uart, udp.data-1, 513) == -1) {
			error_at_line(0, errno, __FILE__, __LINE__, "write packet data");
			r = 21;
			goto r;
		}
#pragma GCC diagnostic pop
	}
r:
	fprintf(stderr, "closing fds and returning\n");
	if (close(uart) == -1)
		error_at_line(22, errno, __FILE__, __LINE__, "close(uart)");
	if (sock != -1)
		if (close(sock) == -1)
			error_at_line(23, errno, __FILE__, __LINE__, "close(sock)");
	return r;
}