summaryrefslogtreecommitdiffstats
path: root/userland
diff options
context:
space:
mode:
authorJames McKenzie <git@madingley.org>2014-12-15 12:05:41 +0000
committerJames McKenzie <git@madingley.org>2014-12-15 12:05:41 +0000
commit373bb32332b117236720af0ff971769fc5930ba6 (patch)
treec1a1e58218a9ce1fed33b7e5330069f856c209cc /userland
downloadvalve-373bb32332b117236720af0ff971769fc5930ba6.tar.gz
valve-373bb32332b117236720af0ff971769fc5930ba6.tar.bz2
valve-373bb32332b117236720af0ff971769fc5930ba6.zip
Diffstat (limited to 'userland')
-rw-r--r--userland/Makefile20
-rw-r--r--userland/cp210x.c534
-rw-r--r--userland/cp210x.h97
-rw-r--r--userland/cp210x_int.h82
-rw-r--r--userland/libradiator.c364
-rw-r--r--userland/program_radiator.c23
-rw-r--r--userland/radiator.c34
-rw-r--r--userland/radiator.h21
8 files changed, 1175 insertions, 0 deletions
diff --git a/userland/Makefile b/userland/Makefile
new file mode 100644
index 0000000..d45447c
--- /dev/null
+++ b/userland/Makefile
@@ -0,0 +1,20 @@
+
+PROGS=radiator program_radiator
+CSRCS=libradiator.c cp210x.c
+
+USBINC=$(shell pkg-config --cflags libusb-1.0)
+USBLIB=$(shell pkg-config --libs libusb-1.0)
+
+CFLAGS=${USBINC} -g -Wall -Wno-unused
+LIBS=${USBLIB} -g
+
+OBJS=${CSRCS:%.c=%.o}
+
+default:${PROGS}
+
+${PROGS}: %:%.o ${OBJS}
+ ${CC} ${CFLAGS} ${LDFLAGS} -o $@ ${OBJS} $@.o ${LIBS}
+
+
+clean:
+ /bin/rm -f core ${OBJS} ${PROGS} ${PROGS:%=%.o}
diff --git a/userland/cp210x.c b/userland/cp210x.c
new file mode 100644
index 0000000..2c1892b
--- /dev/null
+++ b/userland/cp210x.c
@@ -0,0 +1,534 @@
+#include <stdio.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <errno.h>
+#include <string.h>
+#include <malloc.h>
+#include <time.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#include <endian.h>
+
+#include "cp210x_int.h"
+#include "cp210x.h"
+
+
+#define cpu_to_le32(a) htole32(a)
+#define le32_to_cpu(a) le32toh(a)
+
+#define cpu_to_be16(a) htobe16(a)
+#define be16_to_cpu(a) be16toh(a)
+
+#define TIMEOUT 300
+
+
+static int
+cp210x_ctlmsg (CP210X * c, uint8_t request,
+ uint8_t requestype, uint16_t value, uint16_t index, void *data,
+ uint16_t size)
+{
+ uint8_t *tbuf;
+ int ret;
+
+ if (!(tbuf = malloc (size)))
+ return -ENOMEM;
+ if (requestype & 0x80)
+ {
+ ret = libusb_control_transfer (c->handle, requestype,
+ request, value, index, tbuf, size,
+ TIMEOUT);
+ if (ret > 0 && size)
+ memcpy (data, tbuf, size);
+ }
+ else
+ {
+ if (size)
+ memcpy (tbuf, data, size);
+ ret = libusb_control_transfer (c->handle, requestype,
+ request, value, index, tbuf, size,
+ TIMEOUT);
+ }
+ free (tbuf);
+
+ if (ret < 0 && ret != -EPIPE)
+ {
+ fprintf (stderr, "cp210x: control failed cmd rqt %u "
+ "rq %u len %u ret %d\n", requestype, request, size, ret);
+ }
+ return ret;
+}
+
+
+
+static int
+cp210x_get_config (CP210X * c, uint8_t request, unsigned int *data, int size)
+{
+ uint32_t *buf;
+ int result, i, length;
+
+ /* Number of integers required to contain the array */
+ length = (((size - 1) | 3) + 1) / 4;
+
+ buf = calloc (length, sizeof (uint32_t));
+ if (!buf)
+ {
+ fprintf (stderr, "%s - out of memory.\n", __FUNCTION__);
+ return -ENOMEM;
+ }
+
+ /* Issue the request, attempting to read 'size' bytes */
+ result = libusb_control_transfer (c->handle,
+ REQTYPE_INTERFACE_TO_HOST, request,
+ 0x0000, c->interface, (uint8_t *) buf,
+ size, TIMEOUT);
+
+ /* Convert data into an array of integers */
+ for (i = 0; i < length; i++)
+ data[i] = le32_to_cpu (buf[i]);
+
+ free (buf);
+
+ if (result != size)
+ {
+ fprintf (stderr,
+ "%s - Unable to send config request, request=0x%x size=%d result=%d\n",
+ __FUNCTION__, request, size, result);
+ if (result > 0)
+ result = -EPROTO;
+
+ return result;
+ }
+
+ return 0;
+}
+
+
+int
+cp210x_set_config (CP210X * c, uint8_t request, unsigned int *data, int size)
+{
+ uint32_t *buf;
+ int result, i, length;
+
+ /* Number of integers required to contain the array */
+ length = (((size - 1) | 3) + 1) / 4;
+
+ buf = malloc (length * sizeof (uint32_t));
+ if (!buf)
+ {
+ fprintf (stderr, "%s - out of memory.\n", __FUNCTION__);
+ return -ENOMEM;
+ }
+
+ /* Array of integers into bytes */
+ for (i = 0; i < length; i++)
+ buf[i] = cpu_to_le32 (data[i]);
+
+ if (size > 2)
+ {
+ result = libusb_control_transfer (c->handle,
+ REQTYPE_HOST_TO_INTERFACE, request,
+ 0x0000, c->interface, (uint8_t *) buf,
+ size, TIMEOUT);
+ }
+ else
+ {
+ result = libusb_control_transfer (c->handle,
+ REQTYPE_HOST_TO_INTERFACE, request,
+ data[0], c->interface, NULL, 0,
+ TIMEOUT);
+ }
+
+ free (buf);
+
+ if ((size > 2 && result != size) || result < 0)
+ {
+ fprintf (stderr,
+ "%s - Unable to send request, request=0x%x size=%d result=%d\n",
+ __FUNCTION__, request, size, result);
+ if (result > 0)
+ result = -EPROTO;
+
+ return result;
+ }
+
+ return 0;
+}
+
+
+static int
+cp210x_set_config_single (CP210X * c, uint8_t request, unsigned int data)
+{
+ return cp210x_set_config (c, request, &data, 2);
+}
+
+static int
+cp210x_set_u16 (CP210X * c, int cmd, unsigned int value)
+{
+ return cp210x_ctlmsg (c, 0xff, 0x40, 0x3700 | (cmd & 0xff), value, 0, 0);
+}
+
+static int
+cp210x_set_string (CP210X * c, int cmd, uint8_t * usbstr, size_t len,
+ size_t maxlen)
+{
+
+ int ret;
+
+ if (len > maxlen)
+ return -EMSGSIZE;
+
+
+ ret = cp210x_ctlmsg (c, 0xff, 0x40, 0x3700 | (cmd & 0xff), 0, usbstr, len);
+
+ fprintf (stderr, "%s - cmd 0x%02x len %d ret %d %x %x %x %x %x %x\n",
+ __FUNCTION__, cmd, (int) len, ret, usbstr[0], usbstr[1], usbstr[2],
+ usbstr[3], usbstr[4], usbstr[5]);
+
+ return ret;
+}
+
+
+int
+cp210x_set_gpio (CP210X * c, uint8_t gpio)
+{
+ return cp210x_ctlmsg (c, 0xff, REQTYPE_HOST_TO_DEVICE, 0x37e1,
+ ((uint16_t) gpio << 8) | CP_GPIO_MASK, NULL, 0);
+}
+
+
+int
+cp210x_set_portconf (CP210X * c, struct cp210x_port_config *config)
+{
+ int ret;
+ struct cp210x_port_config be_config;
+
+ memset (&be_config, 0, sizeof (be_config));
+
+ be_config.reset.mode = cpu_to_be16 (config->reset.mode);
+ be_config.reset.low_power = 0; // cpu_to_be16(config->reset.low_power);
+ be_config.reset.latch = cpu_to_be16 (config->reset.latch);
+
+ be_config.suspend.mode = cpu_to_be16 (config->suspend.mode);
+ be_config.suspend.low_power = 0; // cpu_to_be16(config->suspend.low_power);
+ be_config.suspend.latch = cpu_to_be16 (config->suspend.latch);
+
+ be_config.enhanced_fxn = config->enhanced_fxn;
+
+ ret =
+ cp210x_ctlmsg (c, 0xff, REQTYPE_HOST_TO_DEVICE, 0x370c, 0, &be_config,
+ sizeof (be_config));
+ if (ret == sizeof (be_config))
+ return 0;
+ else if (ret >= 0)
+ return -EPROTO;
+ else
+ return ret;
+}
+
+
+int
+cp210x_get_portconf (CP210X * c, struct cp210x_port_config *config)
+{
+ int ret;
+
+ ret =
+ cp210x_ctlmsg (c, 0xff, REQTYPE_DEVICE_TO_HOST, 0x370c, 0, config,
+ sizeof (*config));
+
+ if (ret != sizeof (*config))
+ return (ret >= 0) ? -EPROTO : ret;
+
+ /* Words from cp2103 are MSB */
+
+ config->reset.mode = be16_to_cpu (config->reset.mode);
+ config->reset.low_power = be16_to_cpu (config->reset.low_power);
+ config->reset.latch = be16_to_cpu (config->reset.latch);
+
+ config->suspend.mode = be16_to_cpu (config->suspend.mode);
+ config->suspend.low_power = be16_to_cpu (config->suspend.low_power);
+ config->suspend.latch = be16_to_cpu (config->suspend.latch);
+
+ /* apparently not implemented yet */
+ config->reset.low_power = 0;
+ config->suspend.low_power = 0;
+
+ return 0;
+}
+
+
+
+int
+cp210x_set_dtr (CP210X * c, int on)
+{
+ unsigned int control;
+ control = on ? CONTROL_DTR : 0;
+ control |= CONTROL_WRITE_DTR;
+
+ return cp210x_set_config (c, CP210X_SET_MHS, &control, 2);
+}
+
+int
+cp210x_set_vid (CP210X * c, uint16_t vid)
+{
+ return cp210x_set_u16 (c, 1, vid);
+}
+
+int
+cp210x_set_pid (CP210X * c, uint16_t pid)
+{
+ return cp210x_set_u16 (c, 2, pid);
+}
+
+
+int
+cp210x_set_dev_ver (CP210X * c, uint16_t ver)
+{
+ return cp210x_set_u16 (c, 7, ver);
+}
+
+
+ssize_t
+cp210x_read (CP210X * c, void *buf, size_t len, unsigned int timeout)
+{
+ int red = 0;
+ int err;
+
+ timeout /= 1000;
+
+ err = libusb_bulk_transfer (c->handle, 0x81, buf, len, &red, timeout);
+
+ switch (err)
+ {
+ case 0:
+ break;
+ case LIBUSB_ERROR_TIMEOUT:
+ return 0;
+ default:
+ return -1;
+ }
+
+ return red;
+}
+
+
+
+int
+cp210x_set_mfg (CP210X * c, uint8_t *s,int len)
+{
+ return cp210x_set_string (c, 0x0, s, len,
+ CP210X_MAX_MFG_STRLEN);
+}
+
+
+int
+cp210x_set_product (CP210X * c, uint8_t *s,int len)
+{
+
+ return cp210x_set_string (c, 0x3, s, len,
+ CP210X_MAX_PRODUCT_STRLEN);
+}
+
+
+int
+cp210x_set_serial (CP210X * c, uint8_t *s,int len)
+{
+
+ return cp210x_set_string (c, 0x4, s, len,
+ CP210X_MAX_SERIAL_STRLEN);
+}
+
+
+
+
+int
+cp210x_setup (CP210X * c)
+{
+
+ unsigned int bits;
+ unsigned int modem_ctl[4];
+ uint32_t baud;
+
+
+ if (cp210x_set_config_single (c, CP210X_IFC_ENABLE, UART_ENABLE))
+ {
+
+ fprintf (stderr, "failed to start uart\n");
+ return -1;
+ }
+
+
+ baud = 115200;
+
+ if (cp210x_set_config (c, CP210X_SET_BAUDRATE, &baud, sizeof (baud)))
+ {
+ fprintf (stderr, "failed to set baud rate to %u\n", baud);
+ return -1;
+ }
+
+
+
+ cp210x_get_config (c, CP210X_GET_LINE_CTL, &bits, 2);
+
+ bits &= ~BITS_DATA_MASK;
+ bits |= BITS_DATA_8;
+ if (cp210x_set_config (c, CP210X_SET_LINE_CTL, &bits, 2))
+ {
+ fprintf (stderr,
+ "Number of data bits requested not supported by device\n");
+ return -1;
+ }
+
+
+ cp210x_get_config (c, CP210X_GET_LINE_CTL, &bits, 2);
+ bits &= ~BITS_PARITY_MASK;
+ bits |= BITS_PARITY_SPACE;
+ if (cp210x_set_config (c, CP210X_SET_LINE_CTL, &bits, 2))
+ {
+ fprintf (stderr, "Parity mode not supported by device\n");
+ return -1;
+ }
+
+ cp210x_get_config (c, CP210X_GET_LINE_CTL, &bits, 2);
+ bits &= ~BITS_STOP_MASK;
+ bits |= BITS_STOP_1;
+ if (cp210x_set_config (c, CP210X_SET_LINE_CTL, &bits, 2))
+ {
+ fprintf (stderr,
+ "Number of stop bits requested not supported by device\n");
+ return -1;
+ }
+
+
+ cp210x_get_config (c, CP210X_GET_FLOW, modem_ctl, 16);
+ modem_ctl[0] &= ~0x7B;
+ modem_ctl[0] |= 0x01;
+ modem_ctl[1] |= 0x40;
+
+ if (cp210x_set_config (c, CP210X_SET_FLOW, modem_ctl, 16))
+ {
+ fprintf (stderr, "Couldn't disable modem control\n");
+ return -1;
+ }
+
+
+ return 0;
+}
+
+
+
+static libusb_device *
+find_device (void)
+{
+ libusb_device **list;
+ libusb_device *found = NULL;
+ ssize_t cnt;
+ ssize_t i = 0;
+
+ libusb_init (NULL);
+
+ cnt = libusb_get_device_list (NULL, &list);
+
+ if (cnt < 0)
+ return NULL;
+
+
+ for (i = 0; i < cnt; i++)
+ {
+ struct libusb_device_descriptor desc = { 0 };
+ libusb_device *device = list[i];
+
+ libusb_get_device_descriptor (device, &desc);
+
+
+ if (((desc.idVendor == 0x10c4) && (desc.idProduct == 0xea60))
+ ||((desc.idVendor == 0x413c) && (desc.idProduct == 0x9500)))
+ {
+ libusb_ref_device (device);
+ libusb_free_device_list (list, 1);
+
+ return device;
+ break;
+ }
+ }
+ libusb_free_device_list (list, 1);
+ return NULL;
+}
+
+
+
+int
+cp210x_reset (CP210X * c)
+{
+ libusb_reset_device (c->handle);
+
+ if (libusb_set_configuration (c->handle, 1) < 0 ||
+ libusb_claim_interface (c->handle, 0) < 0)
+ return -1;
+
+
+ if (cp210x_setup (c))
+ return -1;
+
+
+ return 0;
+}
+
+CP210X *
+cp210x_open (void)
+{
+ CP210X *c = malloc (sizeof (CP210X));
+
+ c->device = find_device ();
+ c->handle = NULL;
+
+ if (!c->device)
+ {
+ free (c);
+ return NULL;
+ }
+ libusb_open (c->device, &c->handle);
+
+ if (!c->handle)
+ {
+ free (c);
+ return NULL;
+ }
+
+ libusb_detach_kernel_driver (c->handle, 0);
+ libusb_detach_kernel_driver (c->handle, 1);
+
+ libusb_reset_device (c->handle);
+
+ c->interface = 0;
+ if (libusb_set_configuration (c->handle, 1) < 0 ||
+ libusb_claim_interface (c->handle, 0) < 0)
+ {
+
+ libusb_close (c->handle);
+ libusb_unref_device (c->device);
+ free (c);
+ return NULL;
+
+
+ }
+
+
+ if (cp210x_setup (c))
+ {
+ libusb_close (c->handle);
+ libusb_unref_device (c->device);
+ free (c);
+ return NULL;
+ }
+
+ return c;
+}
+
+void
+cp210x_close (CP210X * c)
+{
+ libusb_close (c->handle);
+ libusb_unref_device (c->device);
+ free (c);
+}
diff --git a/userland/cp210x.h b/userland/cp210x.h
new file mode 100644
index 0000000..4b9beff
--- /dev/null
+++ b/userland/cp210x.h
@@ -0,0 +1,97 @@
+#ifndef _CP210X_H_
+#define _CP210X_H_
+
+#include <libusb.h>
+typedef struct {
+ libusb_device *device;
+ libusb_device_handle *handle;
+ int interface;
+} CP210X;
+
+struct cp210x_port_state {
+ uint16_t mode; /* push-pull = 1, open-drain = 0 */
+ uint16_t low_power; /* 1 = ground the pin, and disable */
+ uint16_t latch;
+} __attribute__((packed));
+
+/* LOPWR MODE LATCH */
+/* 0 0 0 Pin drivers are totem pole, and inital config sinks current to GND */
+/* 0 0 1 Pin drivers are totem pole, and inital config sources current from VIO */
+/* 0 1 0 Pin drivers are open drain, and inital config sinks current to GND */
+/* 0 1 1 Pin drivers are open drain, and inital config leaves pin HI-Z (you want this for inputs) */
+/* 1 X X Pin drivers are disabled, and pin sinks current to GND */
+
+struct cp210x_port_config {
+ struct cp210x_port_state reset;
+ struct cp210x_port_state suspend;
+ uint8_t enhanced_fxn;
+} __attribute__((packed));
+
+
+#define CP_EFXN_GPIO_0_IS_TXLED (1 << 0)
+#define CP_EFXN_GPIO_1_IS_RXLED (1 << 1)
+#define CP_EFXN_GPIO_2_IS_RS485_TX (1 << 2)
+#define CP_EFXN_UNUSED_1 (1 << 3) /* Set to zero */
+#define CP_EFXN_ENABLE_WPU (1 << 4)
+#define CP_EFXN_UNUSED_2 (1 << 5) /* Set to zero */
+#define CP_EFXN_SERIAL_AUTOOFF (1 << 6)
+#define CP_EFXN_GPIOL_AUTOOFF (1 << 7)
+
+
+#define CP_PIN_RI (1 << 0)
+#define CP_PIN_DCD (1 << 1)
+#define CP_PIN_DTR (1 << 2)
+#define CP_PIN_DSR (1 << 3)
+#define CP_PIN_TXD (1 << 4)
+#define CP_PIN_RXD (1 << 5)
+#define CP_PIN_RTS (1 << 6)
+#define CP_PIN_CTS (1 << 7)
+#define CP_PIN_GPIO_0 (1 << 8)
+#define CP_PIN_GPIO_1 (1 << 9)
+#define CP_PIN_GPIO_2 (1 << 10)
+#define CP_PIN_GPIO_3 (1 << 11)
+#define CP_PIN_UNUSED_1 (1 << 12)
+#define CP_PIN_UNUSED_2 (1 << 13)
+#define CP_PIN_GPIO_SUSPEND (1 << 14)
+#define CP_PIN_GPIO_NSUSPEND (1 << 15)
+
+#define CP_INPUT_PINS (CP_PIN_RXD|CP_PIN_CTS|CP_PIN_DSR|CP_PIN_RI|CP_PIN_DCD)
+
+
+
+
+
+
+/* CP2103 GPIO */
+#define CP_GPIO_0 0x01
+#define CP_GPIO_1 0x02
+#define CP_GPIO_2 0x04
+#define CP_GPIO_3 0x08
+#define CP_GPIO_MASK (CP_GPIO_0|CP_GPIO_1|CP_GPIO_2|CP_GPIO_3)
+
+
+#define CP210X_MAX_PRODUCT_STRLEN 0x7d
+#define CP210X_MAX_SERIAL_STRLEN 0x3f
+#define CP210X_MAX_MFG_STRLEN 0x18
+#define CP210X_MAX_MAXPOWER 250
+
+
+
+
+extern int cp210x_set_config(CP210X *c, uint8_t request, unsigned int *data, int size);
+extern int cp210x_set_gpio(CP210X *c, uint8_t gpio);
+extern int cp210x_set_portconf(CP210X *c, struct cp210x_port_config *config);
+extern int cp210x_get_portconf(CP210X *c, struct cp210x_port_config *config);
+extern int cp210x_set_dtr(CP210X *c, int on);
+extern int cp210x_set_vid(CP210X *c, uint16_t vid);
+extern int cp210x_set_pid(CP210X *c, uint16_t pid);
+extern int cp210x_set_dev_ver(CP210X *c, uint16_t ver);
+extern ssize_t cp210x_read(CP210X *c, void *buf, size_t len, unsigned int timeout);
+extern int cp210x_set_mfg(CP210X *c, uint8_t *s, int len);
+extern int cp210x_set_product(CP210X *c, uint8_t *s, int len);
+extern int cp210x_set_serial(CP210X *c, uint8_t *s, int len);
+extern int cp210x_setup(CP210X *c);
+extern int cp210x_reset(CP210X *c);
+extern CP210X *cp210x_open(void);
+extern void cp210x_close(CP210X *c);
+#endif
diff --git a/userland/cp210x_int.h b/userland/cp210x_int.h
new file mode 100644
index 0000000..5a5ee3a
--- /dev/null
+++ b/userland/cp210x_int.h
@@ -0,0 +1,82 @@
+#ifndef _CPIO210X_H
+#define _CPIO210X_H
+
+/* Config request types */
+#define REQTYPE_HOST_TO_INTERFACE 0x41
+#define REQTYPE_INTERFACE_TO_HOST 0xc1
+#define REQTYPE_HOST_TO_DEVICE 0x40
+#define REQTYPE_DEVICE_TO_HOST 0xc0
+
+/* Config request codes */
+#define CP210X_IFC_ENABLE 0x00
+#define CP210X_SET_BAUDDIV 0x01
+#define CP210X_GET_BAUDDIV 0x02
+#define CP210X_SET_LINE_CTL 0x03
+#define CP210X_GET_LINE_CTL 0x04
+#define CP210X_SET_BREAK 0x05
+#define CP210X_IMM_CHAR 0x06
+#define CP210X_SET_MHS 0x07
+#define CP210X_GET_MDMSTS 0x08
+#define CP210X_SET_XON 0x09
+#define CP210X_SET_XOFF 0x0A
+#define CP210X_SET_EVENTMASK 0x0B
+#define CP210X_GET_EVENTMASK 0x0C
+#define CP210X_SET_CHAR 0x0D
+#define CP210X_GET_CHARS 0x0E
+#define CP210X_GET_PROPS 0x0F
+#define CP210X_GET_COMM_STATUS 0x10
+#define CP210X_RESET 0x11
+#define CP210X_PURGE 0x12
+#define CP210X_SET_FLOW 0x13
+#define CP210X_GET_FLOW 0x14
+#define CP210X_EMBED_EVENTS 0x15
+#define CP210X_GET_EVENTSTATE 0x16
+#define CP210X_SET_CHARS 0x19
+#define CP210X_GET_BAUDRATE 0x1D
+#define CP210X_SET_BAUDRATE 0x1E
+
+/* CP210X_IFC_ENABLE */
+#define UART_ENABLE 0x0001
+#define UART_DISABLE 0x0000
+
+/* CP210X_(SET|GET)_BAUDDIV */
+#define BAUD_RATE_GEN_FREQ 0x384000
+
+/* CP210X_(SET|GET)_LINE_CTL */
+#define BITS_DATA_MASK 0X0f00
+#define BITS_DATA_5 0X0500
+#define BITS_DATA_6 0X0600
+#define BITS_DATA_7 0X0700
+#define BITS_DATA_8 0X0800
+#define BITS_DATA_9 0X0900
+
+#define BITS_PARITY_MASK 0x00f0
+#define BITS_PARITY_NONE 0x0000
+#define BITS_PARITY_ODD 0x0010
+#define BITS_PARITY_EVEN 0x0020
+#define BITS_PARITY_MARK 0x0030
+#define BITS_PARITY_SPACE 0x0040
+
+#define BITS_STOP_MASK 0x000f
+#define BITS_STOP_1 0x0000
+#define BITS_STOP_1_5 0x0001
+#define BITS_STOP_2 0x0002
+
+/* CP210X_SET_BREAK */
+#define BREAK_ON 0x0001
+#define BREAK_OFF 0x0000
+
+/* CP210X_(SET_MHS|GET_MDMSTS) */
+#define CONTROL_DTR 0x0001
+#define CONTROL_RTS 0x0002
+#define CONTROL_CTS 0x0010
+#define CONTROL_DSR 0x0020
+#define CONTROL_RING 0x0040
+#define CONTROL_DCD 0x0080
+#define CONTROL_WRITE_DTR 0x0100
+#define CONTROL_WRITE_RTS 0x0200
+
+
+#endif /* _CPIO210X_H */
+
+
diff --git a/userland/libradiator.c b/userland/libradiator.c
new file mode 100644
index 0000000..15d2e21
--- /dev/null
+++ b/userland/libradiator.c
@@ -0,0 +1,364 @@
+#include <stdio.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <errno.h>
+#include <string.h>
+#include <malloc.h>
+#include <time.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+#include "radiator.h"
+
+
+
+
+/*
+ * bit 0: 1 left leg NPN base
+ * bit 1: 2 left leg PNP base
+ * bit 2: 4 right leg PNP base
+ * bit 3: 8 right leg NPN base
+ */
+
+#define LEFT_NPN_OFF 0
+#define LEFT_NPN_ON 1
+#define LEFT_PNP_OFF 2
+#define LEFT_PNP_ON 0
+
+#define LEFT_LOW (LEFT_NPN_ON | LEFT_PNP_OFF)
+#define LEFT_HI (LEFT_NPN_OFF | LEFT_PNP_ON)
+#define LEFT_OFF (LEFT_NPN_OFF | LEFT_PNP_OFF)
+
+#define RIGHT_NPN_OFF 0
+#define RIGHT_NPN_ON 8
+#define RIGHT_PNP_OFF 4
+#define RIGHT_PNP_ON 0
+
+
+#define RIGHT_LOW (RIGHT_NPN_ON | RIGHT_PNP_OFF)
+#define RIGHT_HI (RIGHT_NPN_OFF | RIGHT_PNP_ON)
+#define RIGHT_OFF (RIGHT_NPN_OFF | RIGHT_PNP_OFF)
+
+#define BRAKE (RIGHT_LOW | LEFT_LOW)
+#define FORWARDS (RIGHT_LOW | LEFT_HI)
+#define BACKWARDS (RIGHT_HI | LEFT_LOW)
+#define OFF (RIGHT_OFF| LEFT_OFF)
+
+#define TIMEOUT 750000 /*75ms */
+#define RUN_IN 50
+#define RELEASE_TENSION 10
+
+
+static int brake_motor(Radiator *r)
+{
+ int i=BRAKE;
+ printf (" Motor braking\n");
+ return cp210x_set_gpio(r->cp210x,i);
+}
+
+static int
+drive_motor (Radiator*r, int dir)
+{
+ int i;
+
+ if (dir > 0)
+ {
+ printf (" Motor forwards\n");
+ i = FORWARDS;
+ }
+ else if (dir < 0)
+ {
+ printf (" Motor backwards\n");
+ i = BACKWARDS;
+ }
+ else
+ {
+ printf (" Motor off\n");
+ i = OFF;
+ }
+
+ return cp210x_set_gpio(r->cp210x,i);
+}
+
+
+static int
+sensor_led_power (Radiator *r, int on)
+{
+
+unsigned int control;
+
+printf (" Rotation sensor power %s\n", on ? "on" : "off");
+
+return cp210x_set_dtr(r->cp210x,on);
+
+}
+
+
+static int count_steps (Radiator *r, int count)
+{
+ int counted = 0;
+ struct timeval tv;
+ char buf[128];
+ int i;
+ fd_set rfds;
+
+ printf (" Counting %d steps", count);
+ fflush(stdout);
+
+
+ while (count)
+ {
+ i=cp210x_read(r->cp210x,buf,sizeof(buf),TIMEOUT);
+
+ if (i==0) break;
+
+ if (i<0) continue;
+
+ counted+=i;
+ if (count>0) {
+ count-=i;
+ if (count<0) count=0;
+ }
+
+ printf (".");
+ fflush (stdout);
+ }
+
+ printf ("%d steps\n", counted);
+ return counted;
+}
+
+
+
+
+static int
+drive_motor_count (Radiator * r, int dir, int steps)
+{
+ int s1, s2, ret;
+
+ sensor_led_power (r, 1);
+ drive_motor (r, dir);
+ s1 = count_steps (r, steps);
+ if (s1 != steps) {
+ printf (" Hit end stop\n");
+ drive_motor (r, 0);
+ } else {
+ brake_motor(r);
+ }
+ s2 = count_steps (r, -1);
+ sensor_led_power (r, 0);
+ drive_motor (r, 0);
+
+ /*did we hit an end stop? */
+ if (s1 != steps)
+ ret = s1 - s2;
+ else
+ ret = s1 + s2;
+
+ printf (" %d steps under power (of %d), %d steps coasting -> %d steps\n",
+ s1, steps, s2, ret);
+
+ r->pos += ret * dir;
+
+ /*Recalibrate our notion of ends */
+
+ if (s1 != steps)
+ {
+ if (dir == -1)
+ {
+ r->pos = 0;
+ }
+ else
+ {
+ r->max = r->pos;
+ }
+ }
+
+ return ret;
+}
+
+
+
+
+int
+radiator_set_pos (Radiator * r, int wanted)
+{
+ int guard, delta;
+
+ delta = wanted - r->pos;
+ printf ("Now at %d want %d, delta %d\n", r->pos, wanted, delta);
+
+ if (wanted == 0)
+ {
+ drive_motor_count (r, -1, -1);
+ return 0;
+ }
+ else if (r->max && (wanted >= r->max))
+ {
+ drive_motor_count (r, 1, -1);
+ return 0;
+ }
+
+
+ guard = r->overshoot * 3;
+
+ if ((delta > -guard) && (delta < guard))
+ {
+ printf ("Too close\n");
+
+ drive_motor_count (r, (r->pos > r->half) ? -1 : 1, RUN_IN);
+
+ delta = wanted - r->pos;
+ printf ("Now at %d want %d, delta %d\n", r->pos, wanted, delta);
+ }
+
+
+ if (delta < 0)
+ {
+ drive_motor_count (r, -1, (-delta) - r->overshoot);
+ }
+ else if (delta > 0)
+ {
+ drive_motor_count (r, 1, delta - r->overshoot);
+ }
+
+ printf ("Wanted %d, got %d\n", wanted, r->pos);
+
+ return 0;
+}
+
+void
+radiator_calibrate (Radiator * r)
+{
+ radiator_set_pos (r, 0);
+
+ drive_motor_count (r, 1, RUN_IN);
+
+ r->overshoot = r->pos - RUN_IN;
+}
+
+
+
+static int utf16(uint8_t *dst,uint8_t *src)
+{
+int len=0;
+
+
+while (*src) {
+ *(dst++)=*(src++);
+ len+=2;
+ *(dst++)=0;
+}
+len+=2;
+
+return len;
+}
+
+
+static int make_usb_descriptor(uint8_t *dst,char *src)
+{
+int len;
+len=utf16(dst+2,(uint8_t *) src);
+//memcpy(dst+2,src,len);
+dst[0]=len+2;
+dst[1]=0x3;
+return len+2;
+}
+
+
+int
+radiator_program (Radiator * r)
+{
+ uint8_t buf[256];
+ struct cp210x_port_config config;
+ memset (&config, 0, sizeof (config));
+ uint16_t vid,pid;
+ int len;
+
+
+ cp210x_get_portconf(r->cp210x,&config);
+
+ printf
+ ("config was reset=(%x,%x,%x), suspend=(%x,%x,%x), enhanced_fxn=%x\n",
+ config.reset.mode, config.reset.low_power, config.reset.latch,
+ config.suspend.mode, config.suspend.low_power, config.suspend.latch,
+ config.enhanced_fxn);
+
+
+ memset (&config, 0, sizeof (config));
+
+ config.reset.mode = ~CP_INPUT_PINS;
+ config.reset.low_power = 0;
+ config.reset.latch = CP_INPUT_PINS | CP_PIN_GPIO_1 | CP_PIN_GPIO_2;
+ config.suspend.mode = ~CP_INPUT_PINS;
+ config.suspend.low_power = 0;
+ config.suspend.latch = CP_INPUT_PINS | CP_PIN_GPIO_1 | CP_PIN_GPIO_2;
+ config.enhanced_fxn = CP_EFXN_ENABLE_WPU;
+
+
+ cp210x_set_portconf(r->cp210x,&config);
+
+ memset (&config, 0, sizeof (config));
+ cp210x_get_portconf(r->cp210x,&config);
+
+
+ printf
+ ("config is reset=(%x,%x,%x), suspend=(%x,%x,%x), enhanced_fxn=%x\n",
+ config.reset.mode, config.reset.low_power, config.reset.latch,
+ config.suspend.mode, config.suspend.low_power, config.suspend.latch,
+ config.enhanced_fxn);
+
+ memset(buf,0,sizeof(buf));
+
+vid=0x413c;
+
+ cp210x_set_vid(r->cp210x,vid);
+
+pid=0x9500;
+ cp210x_set_pid(r->cp210x,pid);
+
+ len=make_usb_descriptor(buf,"USB Radiator Valve");
+ cp210x_set_product(r->cp210x, buf,len);
+
+ len=make_usb_descriptor(buf,"000001");
+ cp210x_set_serial (r->cp210x,buf,len);
+
+ len=make_usb_descriptor(buf,"Global Panaceas");
+ cp210x_set_mfg (r->cp210x,buf,len);
+
+ cp210x_reset(r->cp210x);
+
+ return 0;
+}
+
+Radiator *
+radiator_open (char *path, int quiet)
+{
+ Radiator *r = malloc (sizeof (Radiator));
+
+
+ r->cp210x=cp210x_open();
+
+ if (!r->cp210x) {
+ free(r);
+ return NULL;
+ }
+
+
+ if (!quiet)
+ radiator_calibrate (r);
+
+ return r;
+}
+
+
+void
+radiator_close (Radiator * r)
+{
+ cp210x_close(r->cp210x);
+ free (r);
+}
+
+
+
diff --git a/userland/program_radiator.c b/userland/program_radiator.c
new file mode 100644
index 0000000..75077da
--- /dev/null
+++ b/userland/program_radiator.c
@@ -0,0 +1,23 @@
+#include <stdio.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include "radiator.h"
+
+int
+main (int argc, char *argv[])
+{
+ Radiator *r;
+ int pos;
+ char buf[1024];
+
+
+ r = radiator_open (argv[1],1);
+
+ if (!r)
+ return -1;
+
+ radiator_program(r);
+
+ return 0;
+}
diff --git a/userland/radiator.c b/userland/radiator.c
new file mode 100644
index 0000000..2d594da
--- /dev/null
+++ b/userland/radiator.c
@@ -0,0 +1,34 @@
+#include <stdio.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include "radiator.h"
+
+int
+main (int argc, char *argv[])
+{
+ Radiator *r;
+ int pos;
+ char buf[1024];
+
+
+ r = radiator_open (argv[1],0);
+
+ if (!r)
+ return -1;
+
+
+ for (;;)
+ {
+ printf ("Current position %d (end stop at %d, overshoots by %d)\n",
+ r->pos, r->max, r->overshoot);
+ printf ("Enter new>");
+ fflush (stdout);
+ buf[sizeof (buf) - 1] = 0;
+ fgets (buf, sizeof (buf) - 1, stdin);
+ radiator_set_pos (r, atoi (buf));
+ }
+
+
+ return 0;
+}
diff --git a/userland/radiator.h b/userland/radiator.h
new file mode 100644
index 0000000..da912c0
--- /dev/null
+++ b/userland/radiator.h
@@ -0,0 +1,21 @@
+#ifndef _RADIATOR_H_
+#define _RADIATOR_H_
+
+#include "cp210x.h"
+
+typedef struct
+{
+ CP210X *cp210x;
+ int pos;
+ int half;
+ int overshoot;
+ int max;
+} Radiator;
+
+int radiator_set_pos (Radiator * r, int wanted);
+void radiator_calibrate (Radiator * r);
+int radiator_program (Radiator * r);
+Radiator *radiator_open (char *s, int quiet);
+void radiator_close (Radiator * r);
+
+#endif /* _RADIATOR_H_ */