blob: 758e861d5a677c8ac00190e5c150cb77bffda3eb [file] [log] [blame]
// Extension routines
#include <stdio.h>
#include <stdlib.h>
#include <poll.h>
#include <fcntl.h>
#define _GNU_SOURCE
#define __USE_GNU
#include <string.h>
#include <termios.h>
#include <sys/ioctl.h>
#ifdef __APPLE__
#include <stdlib.h>
#endif
#include <glob.h>
#include <unistd.h>
#include "forth.h"
#include "sha256.h"
#include <sys/types.h>
#include <sys/stat.h>
#ifdef OPENGL
#ifdef USE_GLEW
#include <GL/glew.h>
#else
void glewInit(void) { }
#endif
#include <GLFW/glfw3.h>
#endif
// #define BUFFERED_READ
#ifdef BUFFERED_READ
char buf[100];
char *bufp;
int nbuf;
#endif
#ifdef __APPLE__
const char * port_nameish = "/dev/cu.usb*";
#else
const char * port_nameish = "/dev/ttyUSB*";
#endif
#include "com-ops.c"
cell posix_set_parity(cell comfid, cell parity) // 'e', 'o', 'n'
{
struct termios kstate;
tcgetattr(comfid,&kstate);
switch (parity) {
case 'n':
kstate.c_cflag &= ~PARENB;
break;
case 'o':
kstate.c_iflag |= IGNPAR;
kstate.c_iflag &= INPCK;
kstate.c_cflag |= PARENB;
kstate.c_cflag |= PARODD;
break;
case 'e':
kstate.c_iflag |= IGNPAR;
kstate.c_iflag &= INPCK;
kstate.c_cflag |= PARENB;
kstate.c_cflag &= ~PARODD;
break;
}
tcsetattr(comfid, TCSAFLUSH, &kstate);
return 0;
}
cell posix_set_modem_control(cell comfid, cell dtr, cell rts)
{
int modemstat, modemstatold;
ioctl(comfid, TIOCMGET, &modemstat);
modemstatold = modemstat;
modemstat &= ~ (TIOCM_DTR | TIOCM_RTS);
if (dtr)
modemstat |= TIOCM_DTR;
if (rts)
modemstat |= TIOCM_RTS;
ioctl(comfid, TIOCMSET, &modemstat);
return modemstatold;
}
cell posix_get_modem_control(cell comfid)
{
int modemstat;
ioctl(comfid, TIOCMGET, &modemstat);
return modemstat;
}
cell posix_set_baud(cell comfid, cell baudrate)
{
int baudcode;
switch(baudrate) {
case 115200: baudcode = B115200; break;
case 38400: baudcode = B38400; break;
case 19200: baudcode = B19200; break;
case 9600: baudcode = B9600; break;
case 4800: baudcode = B4800; break;
case 2400: baudcode = B2400; break;
case 1200: baudcode = B1200; break;
case 300: baudcode = B300; break;
case 110: baudcode = B110; break;
default: baudcode = B115200; break;
}
struct termios kstate;
tcgetattr(comfid,&kstate);
cfsetospeed(&kstate, baudcode);
cfsetispeed(&kstate, baudcode);
tcsetattr(comfid, TCSANOW, &kstate);
return 0;
}
cell posix_timed_read(cell handle, cell ms, cell len, cell buffer)
{
int actual;
struct pollfd pollfd;
#ifdef BUFFERED_READ
if (nbuf) {
actual = len > nbuf ? nbuf : len;
memcpy((char *)buffer, bufp, actual);
nbuf -= actual;
bufp += actual;
return actual;
}
#endif
pollfd.fd = handle;
pollfd.events = POLLIN;
pollfd.revents = 0;
actual = poll(&pollfd, 1, ms);
#ifdef BUFFERED_READ
if (actual > 0) {
nbuf = read((int)handle, (void *)buf, 100);
bufp = buf;
if (nbuf < 0)
nbuf = 0;
}
if (nbuf) {
actual = len > nbuf ? nbuf : len;
memcpy((char *)buffer, bufp, actual);
nbuf -= actual;
bufp += actual;
return actual;
}
return actual;
#else
if (actual > 0) {
actual = read((int)handle, (void *)buffer, (size_t)len);
}
#endif
return actual;
}
cell posix_write(cell handle, cell len, cell buf)
{
return (cell)write((int)handle, (void *)buf, (int)len);
}
cell open_posix_com(char *comname)
{
struct termios kstate;
int comfid;
printf("%s\n",comname);
comfid = open(comname, O_RDWR, O_EXCL);
if (comfid < 0) {
return (cell)comfid;
}
tcgetattr(comfid,&kstate);
cfmakeraw(&kstate);
cfsetospeed(&kstate, B115200);
cfsetispeed(&kstate, B115200);
kstate.c_cflag |= CLOCAL;
kstate.c_cc[VTIME] = 1; /* Try for 1/10 second */
kstate.c_cc[VMIN] = 0; /* Poll for character */
tcsetattr(comfid, TCSANOW, &kstate);
#ifdef BUFFERED_READ
nbuf = 0;
#endif
com_ops_t *ops = malloc(sizeof(com_ops_t));
ops->handle = (cell)comfid;
ops->close = (cell (*)(cell))close;
ops->get_modem_control = posix_get_modem_control;
ops->set_modem_control = posix_set_modem_control;
ops->set_parity = posix_set_parity;
ops->set_baud = posix_set_baud;
ops->write = posix_write;
ops->timed_read = posix_timed_read;
return (cell)ops;
}
#ifdef USE_FTDI
#include <libusb.h>
#include "extend-libftdi.c"
#else
cell ft_open_serial(cell portnum, cell pid) { return 0; }
cell ft_get_errno() { return -9999; }
cell ft_setbits(cell ops, unsigned char bits) { return -1; }
cell ft_getbits(cell ops) { return -1; }
#endif
int get_nth_port(cell portnum, char ** resbuf)
{
glob_t g;
int retval = 0;
retval = glob(port_nameish, 0, NULL, &g);
if (retval !=0 )
return -1;
if (portnum >= g.gl_pathc)
return -1;
char * result = g.gl_pathv[portnum];
size_t ressize = strlen(result);
*resbuf = malloc(ressize+1);
if (*resbuf == 0)
{
fprintf(stderr,"Out of memory in get_nth_port\n");
return -1;
}
strcpy(*resbuf, result);
globfree(&g);
return 0;
}
// The argument can be either
// a) A small positive integer like 5 for /dev/ttyUSB5
// b) A small negative integer like -2 for the nth system dependent serial port
// c) A null-terminated string like "/dev/ttyFAKE" to name a specific device
// This assumes that string addresses will never be small integers
cell open_com(cell portnum) // Open COM port
{
cell res;
res = ft_open_serial(portnum, 0x4e4c); // Nod Ring
if (res)
return res;
res = ft_open_serial(portnum, 0x4e4d); // Nod Backspin
if (res)
return res;
char *comname;
char comname_buf[32];
if (portnum < 0 && portnum > -100) {
// ports numbered -1, -2, -3...
int res = get_nth_port(-1 - portnum, &comname);
if (res < 0) {
fprintf(stderr, "Unable to find a port with name like %s\n", port_nameish);
return (cell)res;
}
} else if ((u_cell)portnum < 200) {
snprintf(comname_buf, 31, "/dev/ttyUSB%ld", portnum);
comname = comname_buf;
} else {
comname = (char *)portnum;
}
return open_posix_com(comname);
}
extern char *expand_name(char *name);
cell open_file(cell stradr) // Open file
{
char *name = (char *)stradr;
int fid;
fid = open(expand_name(name), O_RDWR, 0);
return (cell)fid;
}
void non_blocking(cell fid)
{
fcntl((int)fid, F_SETFL, fcntl((int)fid, F_GETFL) | O_NONBLOCK);
}
void rename_file(cell new, cell old)
{
rename((char *)old, (char *)new);
}
void close_file(cell fid)
{
close((int)fid);
}
cell write_file(cell handle, cell len, cell buffer)
{
size_t actual;
actual = write((int)handle, (void *)buffer, (size_t)len);
return actual;
}
cell read_file(cell handle, cell len, cell buffer)
{
int actual;
actual = read((int)handle, (void *)buffer, (size_t)len);
return actual;
}
cell file_date(cell stradr)
{
struct stat buf;
char *name = (char *)stradr;
stat(expand_name(name), &buf);
return (cell)buf.st_mtime;
}
void ms(cell nms)
{
usleep(nms*1000); // nanosleep(timespec) would be better
}
void us(cell nus)
{
usleep(nus); // nanosleep(timespec) would be better
}
#include <sys/socket.h>
#include <netdb.h>
int stream_connect(char *host, char *port, int timeout)
{
struct addrinfo hints, *res, *res0;
int error;
int s;
const char *cause = NULL;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
error = getaddrinfo(host, port, &hints, &res0);
if (error) {
perror("getaddrinfo");
return -1;
}
s = -1;
for (res = res0; res; res = res->ai_next) {
s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if (s < 0) {
cause = "socket";
continue;
}
if (connect(s, res->ai_addr, res->ai_addrlen) < 0) {
cause = "connect";
close(s);
s = -1;
continue;
}
break; /* okay we got one */
}
if (s < 0) {
printf("%s", cause);
return -2;
}
freeaddrinfo(res0);
const struct timeval recv_timeout = {.tv_sec=timeout, .tv_usec=0};
error = setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &recv_timeout, sizeof(recv_timeout));
if (error) {
perror("unable to set receive timeout.");
return -3;
}
return s;
}
void dummy(void) { }
cell open_sha256()
{
SHA256Context * sc;
sc = (SHA256Context *)malloc(sizeof(*sc));
SHA256Init(sc);
return (cell)sc;
}
void close_sha256(SHA256Context *sc, uint8_t *hash)
{
SHA256Final(sc, hash);
free(sc);
}
#include <sys/mman.h>
cell memmap(cell fd, cell len, cell off)
{
return (long)mmap((void *)0, (size_t)len, PROT_READ|PROT_WRITE, MAP_SHARED, (int)fd, (off_t)off);
}
cell memunmap(cell len, cell adr)
{
return munmap((void *) adr, (size_t) len);
}
#include <sys/time.h>
cell get_msecs(void)
{
struct timeval tv;
unsigned int msecs;
gettimeofday(&tv, NULL);
msecs = (tv.tv_usec / 1000) + (tv.tv_sec * 1000);
return (cell)msecs;
}
#include <errno.h>
cell errno_val(void) { return (cell)errno; }
#ifdef __APPLE__
cell open_scanner() { return -1; }
#else
#include <linux/input.h>
cell open_scanner()
{
int i;
int fid;
int ret;
char devname[20];
char scannername[128];
for (i = 0; i<20; i++) {
sprintf(devname, "/dev/input/event%d", i);
fid = open(devname, O_RDONLY, 0);
if (fid > 0) {
ret = ioctl(fid, EVIOCGNAME(sizeof(scannername)), scannername);
if (ret >= 0) {
if (strcasestr(scannername, "scanner")) {
ret = ioctl(fid, EVIOCGRAB, 1); // Exclusive
printf("Grabbed device %d as fid %d, exc %d named %s.\n",i, fid, ret, scannername);
return fid;
}
// The Taotronics scanner identifies itself thusly.
if (strcmp(scannername, "HID Keyboard Device HID Keyboard Device") == 0) {
ret = ioctl(fid, EVIOCGRAB, 1); // Exclusive
printf("Grabbed device %d as fid %d, exc %d named %s.\n",i, fid, ret, scannername);
return fid;
}
fprintf(stdout, "Rejecting input device %d with name %s.\n", i, scannername);
}
close(fid);
}
}
return -1;
}
#endif
#ifdef OPENGL
void error_callback(int error, const char* description)
{
fputs(description, stderr);
}
void set_error_callback(void)
{
glfwSetErrorCallback(error_callback);
}
#endif
cell ((* const ccalls[])()) = {
// OS-independent functions
C(ms) //c ms { i.ms -- }
C(get_msecs) //c get-msecs { -- i.ms }
C(us) //c us { i.microseconds -- }
C(open_file) //c h-open-file { $.name -- i.handle }
C(close_file) //c h-close-handle { i.handle -- }
C(write_file) //c h-write-file { a.buf i.len i.handle -- i.actual }
C(read_file) //c h-read-file { a.buf i.len i.handle -- i.actual }
C(rename_file) //c rename-file { $.old $.new -- }
C(file_date) //c file-date { $.name -- i.unixtime }
// Posix-specific I/O
C(errno_val) //c errno { -- i.errno }
C(strerror) //c strerror { i.errno -- $.msg }
C(ioctl) //c ioctl { a.data i.code i.fd -- i.error }
C(non_blocking) //c non-blocking { i.fd -- }
C(poll) //c poll { i.timeout i.nfds a.pfds -- i.nfds }
C(select) //c select { a.tv a.exceptfds a.writefds a.readfds i.handle -- i.error }
C(fcntl) //c fcntl { i.flags i.cmd i.handle -- i.error }
C(memmap) //c mmap { a.phys i.len i.fd -- a.virt }
C(memunmap) //c munmap { a.virt i.len -- i.error }
C(lseek) //c lseek { i.whence i.offset i.fd -- i.offset }
// Posix sockets
C(socket) //c socket { i.proto i.type i.family -- i.handle }
C(bind) //c bind { i.len a.addr i.handle -- i.error }
C(setsockopt) //c setsockopt { i.len a.addr i.optname i.level i.handle -- i.error }
C(getsockopt) //c getsockopt { i.len a.addr i.optname i.level i.handle -- i.error }
C(connect) //c connect { i.len a.adr i.handle -- i.error }
C(stream_connect) //c stream-connect { i.timeout $.portname $.hostname -- i.handle }
// Serial port interfaces
C(open_com) //c open-com { i.port# -- i.handle }
C(close_com) //c close-com { i.handle -- }
C(timed_read_com) //c timed-read-com { a.buf i.len i.ms i.handle -- i.actual }
C(write_com) //c write-com { a.buf i.len i.handle -- i.actual }
C(set_modem_control) //c set-modem { i.rts i.dtr i.handle -- }
C(get_modem_control) //c get-modem { i.handle -- i.modemstat }
C(set_com_parity) //c set-parity { i.parity i.handle -- }
C(set_baud) //c baud { i.baudrate i.fd -- }
// SHA routines; pure code, no I/O
C(open_sha256) //c sha256-open { -- a.context }
C(SHA256Update) //c sha256-update { i.len a.data a.context -- }
C(close_sha256) //c sha256-close { a.hash a.context -- }
// Miscellaneous
C(open_scanner) //c open-scanner { -- i.fd }
C(system) //c system { $ -- }
#ifdef USE_FTDI
// FTDI bit-banging
C(ft_open_serial) //c ft-open-com { i.pid i.index -- i.handle }
C(ft_get_errno) //c ft-errno { -- i.err }
C(ft_setbits) //c ft-setbits { i.mask i.handle -- i.status }
C(ft_getbits) //c ft-getbits { i.handle -- i.bits }
C(libusb_init) //c libusb_init { a.'ctx -- i.err }
C(libusb_exit) //c libusb_exit { a.ctx -- }
C(libusb_set_debug) //c libusb_set_debug { i.level a.ctx -- }
C(libusb_get_device_list) //c libusb_get_device_list { a.''list a.ctx -- h.len }
C(libusb_free_device_list) //c libusb_free_device_list { i.unref a.'list -- }
C(libusb_get_bus_number) //c libusb_get_bus_number { a.dev -- h.bus# }
C(libusb_get_port_number) //c libusb_get_port_number { a.dev -- h.port# }
C(libusb_get_port_numbers) //c libusb_get_port_numbers { i.nport a.port#s a.dev -- h.n }
C(libusb_get_parent) //c libusb_get_parent { a.dev -- a.parent }
C(libusb_get_device_address) //c libusb_get_device_address { a.dev -- h.adr }
C(libusb_get_device_speed) //c libusb_get_device_speed { a.dev -- h.speed }
C(libusb_get_max_packet_size) //c libusb_get_max_packet_size { i.ep a.dev -- h.size }
C(libusb_get_max_iso_packet_size) //c libusb_get_max_iso_packet_size { i.ep a.dev -- h.size }
C(libusb_ref_device) //c libusb_ref_device { a.dev -- a.dev }
C(libusb_unref_device) //c libusb_unref_device { a.dev -- }
C(libusb_open) //c libusb_open { a.handle a.dev -- h.err }
C(libusb_open_device_with_vid_pid) //c libusb_open_device_with_vid_pid { i.pid i.vid a.ctx -- a.handle }
C(libusb_close) //c libusb_close { a.dev -- }
C(libusb_get_device) //c libusb_get_device { a.handle -- a.dev }
C(libusb_get_configuration) //c libusb_get_configuration { a.config a.handle -- h.err }
C(libusb_set_configuration) //c libusb_set_configuration { i.config a.handle -- h.err }
C(libusb_claim_interface) //c libusb_claim_interface { i.ifce a.handle -- h.err }
C(libusb_release_interface) //c libusb_release_interface { i.ifce a.handle -- h.err }
C(libusb_set_interface_alt_setting) //c libusb_set_interface_alt { i.alt i.ifce a.handle -- h.err }
C(libusb_clear_halt) //c libusb_clear_halt { i.ep a.handle -- h.err }
C(libusb_reset_device) //c libusb_reset_device { a.handle -- h.err }
C(libusb_kernel_driver_active) //c libusb_kernel_driver_active { i.ifce a.handle -- h.stat }
C(libusb_detach_kernel_driver) //c libusb_detach_kernel_driver { i.ifce a.handle -- h.err }
C(libusb_attach_kernel_driver) //c libusb_attach_kernel_driver { i.ifce a.handle -- h.err }
C(libusb_set_auto_detach_kernel_driver) //c libusb_set_auto_detach { i.enable a.handle -- h.err }
C(libusb_get_device_descriptor) //c libusb_get_device_descriptor { a.desc a.dev -- h.err }
C(libusb_get_config_descriptor) //c libusb_get_config_descriptor { a.'desc i.index a.dev -- h.err }
C(libusb_get_string_descriptor_ascii) //c libusb_get_string_descr_ascii { i.len a.string i.index a.dev -- h.err }
C(libusb_get_descriptor) //c libusb_get_descriptor { i.len a.desc i.index i.type a.handle -- h.err }
C(libusb_free_config_descriptor) //c libusb_free_config_descriptor { a.desc -- }
C(libusb_control_transfer) //c libusb_control_transfer { i.timeout i.len a.data i.windex i.wvalue i.request i.reqtype a.handle -- h.nbytes }
C(libusb_bulk_transfer) //c libusb_bulk_transfer { i.timeout a.actual i.len a.data i.ep a.handle -- h.err }
C(libusb_interrupt_transfer) //c libusb_interrupt_transfer { i.timeout a.actual i.len a.data i.ep a.handle -- h.err }
#endif
#ifdef OPENGL
C(glfwInit) //c glfwInit { -- h.okay }
C(glfwTerminate) //c glfwTerminate { -- }
C(set_error_callback)//c set-error-callback { -- }
C(glfwCreateWindow) //c glfwCreateWindow { a.share a.monitor $name i.h i.w -- a.window }
C(glfwMakeContextCurrent) //c glfwMakeContextCurrent { a.window -- }
C(glfwWindowShouldClose) //c glfwWindowShouldClose { a.window -- h.close? }
C(glfwGetFramebufferSize) //c glfwGetFramebufferSize { a.height a.width a.window -- }
C(glfwSwapBuffers) //c glfwSwapBuffers { a.window -- }
C(glfwSwapInterval) //c glfwSwapInterval { i.interval -- }
C(glfwPollEvents) //c glfwPollEvents { -- }
C(glfwWindowHint) //c glfwWindowHint { i.value i.hint# -- }
C(glfwSetInputMode) //c glfwSetInputMode { i.value i.param# a.window -- }
C(glfwGetKey) //c glfwGetKey { i.key a.window -- h.state }
C(glewInit) //c glewInit { -- }
#if 0
C(glViewport) //x gl-viewport { i.height i.width i.y i.x -- }
C(glClear) //x gl-clear { i.bits -- }
C(glMatrixMode) //x gl-matrix-mode { i.mode -- }
C(glLoadIdentity) //x gl-load-identity { -- }
#endif
#endif
};