summaryrefslogtreecommitdiff
path: root/libs/surfaces/frontier
diff options
context:
space:
mode:
authorJohn Anderson <ardour@semiosix.com>2007-02-14 19:56:16 +0000
committerJohn Anderson <ardour@semiosix.com>2007-02-14 19:56:16 +0000
commite878b365193ad9315e557a8245b767d8a0fe568d (patch)
treed03d6b7bc7bb57c7c55d370010b8c6e6bb6619ae /libs/surfaces/frontier
parent92c09a6d91fa90bbfaec4d94e2c6e6c11f1e49a3 (diff)
merge r1449 from surfaces branch to include mackie surface and tranzport updates. Thanks to Gerd Flaig for the merge command: svn merge svn+ssh://ardoursvn@ardour.org/ardour2/trunk@1449 svn+ssh://ardoursvn@ardour.org/ardour2/branches/surfaces.
git-svn-id: svn://localhost/ardour2/trunk@1460 d708f5d6-7413-0410-9779-e7cbd77b26cf
Diffstat (limited to 'libs/surfaces/frontier')
-rw-r--r--libs/surfaces/frontier/kernel_drivers/BUILD10
-rw-r--r--libs/surfaces/frontier/kernel_drivers/Makefile35
-rw-r--r--libs/surfaces/frontier/kernel_drivers/README16
-rw-r--r--libs/surfaces/frontier/kernel_drivers/doc/keycodes.html35
-rw-r--r--libs/surfaces/frontier/kernel_drivers/tests/Makefile23
-rw-r--r--libs/surfaces/frontier/kernel_drivers/tests/README104
-rw-r--r--libs/surfaces/frontier/kernel_drivers/tests/tranzport.c375
-rw-r--r--libs/surfaces/frontier/kernel_drivers/tests/tranzport_lights.c361
-rwxr-xr-xlibs/surfaces/frontier/kernel_drivers/tests/tranzport_tests.sh27
-rw-r--r--libs/surfaces/frontier/kernel_drivers/tranzport.c1065
-rw-r--r--libs/surfaces/frontier/tests/Makefile17
-rw-r--r--libs/surfaces/frontier/tests/README104
-rw-r--r--libs/surfaces/frontier/tests/tranzport.c347
-rw-r--r--libs/surfaces/frontier/tests/tranzport_lights.c361
-rw-r--r--libs/surfaces/frontier/tranzport/SConscript56
-rw-r--r--libs/surfaces/frontier/tranzport/interface.cc51
-rw-r--r--libs/surfaces/frontier/tranzport/tranzport_control_protocol.cc1950
-rw-r--r--libs/surfaces/frontier/tranzport/tranzport_control_protocol.h320
18 files changed, 5257 insertions, 0 deletions
diff --git a/libs/surfaces/frontier/kernel_drivers/BUILD b/libs/surfaces/frontier/kernel_drivers/BUILD
new file mode 100644
index 0000000000..dc612e20bf
--- /dev/null
+++ b/libs/surfaces/frontier/kernel_drivers/BUILD
@@ -0,0 +1,10 @@
+To build, type make
+
+# make install and run
+ir: install
+ rmmod tranzport
+ modprobe tranzport
+
+# make install, run, and run tests
+irt: ir
+
diff --git a/libs/surfaces/frontier/kernel_drivers/Makefile b/libs/surfaces/frontier/kernel_drivers/Makefile
new file mode 100644
index 0000000000..223fcdb6fc
--- /dev/null
+++ b/libs/surfaces/frontier/kernel_drivers/Makefile
@@ -0,0 +1,35 @@
+ifneq ($(KERNELRELEASE),)
+
+obj-m := tranzport.o
+tranzport-objs :=
+
+else
+
+KDIR := /lib/modules/$(shell uname -r)/build
+PWD := $(shell pwd)
+MODDIR := $(DESTDIR)/lib/modules/$(shell uname -r)/kernel/sound/usb/misc
+BINDIR := $(DESTDIR)/usr/local/bin
+
+default::
+ $(MAKE) -Wall -C $(KDIR) SUBDIRS=$(PWD) modules
+ $(MAKE) -C tests
+
+install-only:: default
+ mkdir -p $(MODDIR) $(BINDIR)
+ cp tranzport.ko $(MODDIR)
+ $(MAKE) -C tests install
+
+install:: install-only
+ /sbin/depmod -a
+ +/sbin/rmmod tranzport
+ /sbin/modprobe tranzport
+
+irt:: install
+ tranzport_tests.sh
+
+clean::
+ rm -f core .*.cmd *.o *.ko *.mod.c Module.symvers *.bak .\#* *~
+ rm -rf .tmp_versions
+ $(MAKE) -C tests clean
+
+endif
diff --git a/libs/surfaces/frontier/kernel_drivers/README b/libs/surfaces/frontier/kernel_drivers/README
new file mode 100644
index 0000000000..51b4af0f2e
--- /dev/null
+++ b/libs/surfaces/frontier/kernel_drivers/README
@@ -0,0 +1,16 @@
+This directory contains the USB Tranzport Kernel driver for Linux.
+
+At present it accepts reads/writes of 8 byte cmds to /dev/tranzport0 to control
+the lights and screen.
+
+Reads are possible. Wheel Compression does not currently account for button changes
+
+It also has some sysfs hooks that are non-functional at the moment.
+
+The API is closely tied to the ardour revision and WILL change.
+
+A sysfs interface is PERFECT for simple userspace apps to do fun things with the
+lights and screen. It's fairly lousy for handling input events and very lousy
+for watching the state of the shuttle wheel.
+
+In the end this is going to be driven by a midi layer
diff --git a/libs/surfaces/frontier/kernel_drivers/doc/keycodes.html b/libs/surfaces/frontier/kernel_drivers/doc/keycodes.html
new file mode 100644
index 0000000000..651517a7f2
--- /dev/null
+++ b/libs/surfaces/frontier/kernel_drivers/doc/keycodes.html
@@ -0,0 +1,35 @@
+<HTML>
+<HEAD>
+<TITLE> TRANZPORT KEYCODES REFERENCE </TITLE>
+</HEAD>
+<BODY>
+<H2> TRANZPORT KEYCODES REFERENCE </H2>
+
+<H3>Footswitch</H3>
+
+At least on every footswitch I've tried, the polarity appears to be wrong, in that the footswitch "up" position results
+in 0100 being OR'd into the result. Pressing it down results in a 0, if no other keys are pressed. Releasing it results in 0100.
+
+Every other key when up results in 0. This odd behavior would hopefully be controllable via a command to the tranzport,
+but I don't have that, so dealing with footswitch events is weird.
+
+So, seeing this bit enabled would be something like "HAVE_FOOTSWITCH INSTALLED", BE SMART ABOUT IT.
+
+
+<H3>Special Key Combinations</H3>
+<p>
+In addition to the normal keycodes generated by the tranzport, it is possible to hit several combinations of keys and get a unique
+result. Some are really weird. Perhaps the following assignments make sense:
+</p><p>
+<table><tr><th>PRESSING</th><th>RESULT</th><th>ASSIGNED TO</th></tr>
+<tr><td>TRACKLEFT+TRACKRIGHT</td><td>TRACKLEFT+TRACKRIGHT</td><td>Master</td></tr>
+<tr><td>SHIFT+TRACKLEFT+TRACKRIGHT</td><td>SHIFT+TRACKLEFT+TRACKRIGHT+UNDO</td><td>Show Bus Only Toggle</td></tr>
+<tr><td>IN+OUT</td><td>IN+OUT</td><td>Zoom 100%</td></tr>
+<tr><td>SHIFT+IN+OUT</td><td>SHIFT+IN+OUT+UNDO</td><td>Max Zoom</td></tr>
+<tr><td>SHIFT+REW+FFW</td><td>SHIFT+REW+FFW+UNDO</td><td></td></tr>
+<tr><td>RECORD+TRACKSOLO+FOOTSWITCHNOTDEPRESSED</td><td>RECORD+TRACKSOLO+BATTERY</td><td></td></tr>
+<tr><td>PLAY+MUTE</td><td>PLAY+MUTE</td><td></td></tr>
+</table>
+</p>
+</body>
+</html>
diff --git a/libs/surfaces/frontier/kernel_drivers/tests/Makefile b/libs/surfaces/frontier/kernel_drivers/tests/Makefile
new file mode 100644
index 0000000000..534bc7da4b
--- /dev/null
+++ b/libs/surfaces/frontier/kernel_drivers/tests/Makefile
@@ -0,0 +1,23 @@
+# Some basic utilities for testing the tranzport's I/O
+# eventually "tranzport" will become a flexible command
+#
+#
+
+FILES:=tranzport tranzport_lights tranzport_tests.sh
+BINDIR ?= $(DESTDIR)/usr/local/bin
+
+all: tranzport tranzport_lights
+
+tranzport: tranzport.c
+ gcc -g -Wall -o tranzport tranzport.c
+
+tranzport_lights: tranzport_lights.c
+ gcc -g -Wall -o tranzport_lights tranzport_lights.c
+
+clean::
+ rm -f core .*.cmd *.o *.ko *.mod.c Module.symvers *.bak .\#* *~
+ rm -rf .tmp_versions tranzport tranzport_lights
+
+install::
+ cp $(FILES) $(BINDIR)
+
diff --git a/libs/surfaces/frontier/kernel_drivers/tests/README b/libs/surfaces/frontier/kernel_drivers/tests/README
new file mode 100644
index 0000000000..f9efd18f69
--- /dev/null
+++ b/libs/surfaces/frontier/kernel_drivers/tests/README
@@ -0,0 +1,104 @@
+tranzport 0.1 <tranzport.sf.net>
+oct 18, 2005
+arthur@artcmusic.com
+---
+
+The Frontier Design Tranzport(tm) (www.frontierdesign.com) is a simple
+wireless USB device. It is not a MIDI device. The document on their web
+site "Tranzport(tm) Native Mode Interface Description" describes the
+Tranzport(tm) as if it were a MIDI device, but this is implemented by their
+Windows and Macintosh software drivers.
+
+This code will allow you to use your Tranzport(tm) at a lower level of
+abstraction. This code relies on libusb, which can be obtained from
+libusb.sourceforge.net.
+
+To compile the program, type "make". You should end up with a executable
+called "tranzport". You'll probably have to run this program as root.
+
+Using the program is straightforward. It will simply tell you which
+buttons are being pressed and what not. If you press one of the buttons
+with a light, the light will turn on. If you hold shift and press one of
+the buttons with a light, the light will turn off. If you take out the
+batteries to the device (or go out of range), it will tell you that the
+device is offline. When you replace the batteries (or come back in
+range), it should tell you it is back online.
+
+Once you understand how everything works, you should be able to
+incorporate it into your own setup however you wish.
+
+This code was developed on a Linux machine, but (theoretically) it
+should work on any system that is supported by libusb, since that is how
+it communicates with the device.
+
+Here are a few more details about the device:
+
+There are two endpoints for communication with the device. All data
+reads and writes are done in 8-byte segments.
+
+One endpoint is for interrupt reads. This is used to read button data
+from the device. It also supplies status information for when the device
+goes out of range and comes back in range, loses power and regains
+power, etc. The format of the data is:
+
+ 00 ss bb bb bb bb dd 00 (hexadecimal)
+
+where:
+
+ ss - status code, 01=online ff=offline
+ bb - button bits
+ dd - data wheel, 01-3f=forward 41-7f=backward
+
+Please refer to the source code for a list of the button bits.
+
+The other endpoint is for interrupt writes. This is used to toggle the
+lights on the device, and to write data to the LCD.
+
+There are 7 lights on the device. To turn a light on, send the following
+sequence of bytes:
+
+ 00 00 nn 01 00 00 00 00 (hexadecimal)
+
+where nn is the light number.
+
+To turn a light off:
+
+ 00 00 nn 00 00 00 00 00 (hexadecimal)
+
+Here is the list of lights:
+
+ 00 Record
+ 01 Track Rec
+ 02 Track Mute
+ 03 Track Solo
+ 04 Any Solo
+ 05 Loop
+ 06 Punch
+
+The size of the LCD is 20x2, and it is split into 10 cells, each cell
+being 4 characters wide. The cells progress across, then down. To write
+to the LCD, send the following sequence of bytes:
+
+ 00 01 cc aa aa aa aa 00 (hexadecimal)
+
+where:
+
+ cc - cell number
+ aa - ASCII code
+
+Here is a list of the cells to clarify:
+
+ 00 row 0, column 0-3
+ 01 row 0, column 4-7
+ 02 row 0, column 8-11
+ 03 row 0, column 12-15
+ 04 row 0, column 16-19
+ 05 row 1, column 0-3
+ 06 row 1, column 4-7
+ 07 row 1, column 8-11
+ 08 row 1, column 12-15
+ 09 row 1, column 16-19
+
+You should refer to the "Tranzport(tm) Native Mode Interface
+Description" document for a listing of the ASCII codes the LCD uses.
+
diff --git a/libs/surfaces/frontier/kernel_drivers/tests/tranzport.c b/libs/surfaces/frontier/kernel_drivers/tests/tranzport.c
new file mode 100644
index 0000000000..2ef5b6c910
--- /dev/null
+++ b/libs/surfaces/frontier/kernel_drivers/tests/tranzport.c
@@ -0,0 +1,375 @@
+/*
+ * tranzport 0.1 <tranzport.sf.net>
+ * oct 18, 2005
+ * arthur@artcmusic.com
+ */
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <malloc.h>
+
+#define VENDORID 0x165b
+#define PRODUCTID 0x8101
+
+#define READ_ENDPOINT 0x81
+#define WRITE_ENDPOINT 0x02
+
+enum {
+ LIGHT_RECORD = 0,
+ LIGHT_TRACKREC,
+ LIGHT_TRACKMUTE,
+ LIGHT_TRACKSOLO,
+ LIGHT_ANYSOLO,
+ LIGHT_LOOP,
+ LIGHT_PUNCH
+};
+
+#define BUTTONMASK_BATTERY 0x00004000
+#define BUTTONMASK_BACKLIGHT 0x00008000
+#define BUTTONMASK_TRACKLEFT 0x04000000
+#define BUTTONMASK_TRACKRIGHT 0x40000000
+#define BUTTONMASK_TRACKREC 0x00040000
+#define BUTTONMASK_TRACKMUTE 0x00400000
+#define BUTTONMASK_TRACKSOLO 0x00000400
+#define BUTTONMASK_UNDO 0x80000000
+#define BUTTONMASK_IN 0x02000000
+#define BUTTONMASK_OUT 0x20000000
+#define BUTTONMASK_PUNCH 0x00800000
+#define BUTTONMASK_LOOP 0x00080000
+#define BUTTONMASK_PREV 0x00020000
+#define BUTTONMASK_ADD 0x00200000
+#define BUTTONMASK_NEXT 0x00000200
+#define BUTTONMASK_REWIND 0x01000000
+#define BUTTONMASK_FASTFORWARD 0x10000000
+#define BUTTONMASK_STOP 0x00010000
+#define BUTTONMASK_PLAY 0x00100000
+#define BUTTONMASK_RECORD 0x00000100
+#define BUTTONMASK_SHIFT 0x08000000
+
+#define STATUS_OFFLINE 0xff
+#define STATUS_ONLINE 0x01
+#define STATUS_OK 0x00
+
+struct tranzport_s {
+ int *dev;
+ int udev;
+};
+
+typedef struct tranzport_s tranzport_t;
+
+void log_entry(FILE *fp, char *format, va_list ap)
+{
+ vfprintf(fp, format, ap);
+ fputc('\n', fp);
+}
+
+void log_error(char *format, ...)
+{
+ va_list ap;
+ va_start(ap, format);
+ log_entry(stderr, format, ap);
+ va_end(ap);
+}
+
+void vlog_error(char *format, va_list ap)
+{
+ log_entry(stderr, format, ap);
+}
+
+void die(char *format, ...)
+{
+ va_list ap;
+ va_start(ap, format);
+ vlog_error(format, ap);
+ va_end(ap);
+ exit(1);
+}
+
+tranzport_t *open_tranzport_core()
+{
+ tranzport_t *z;
+ int val;
+
+ z = malloc(sizeof(tranzport_t));
+ if (!z)
+ die("not enough memory");
+ memset(z, 0, sizeof(tranzport_t));
+
+ z->udev = open("/dev/tranzport0",O_RDWR);
+ if (z->udev < 1)
+ die("unable to open tranzport");
+
+ return z;
+}
+
+tranzport_t *open_tranzport()
+{
+return open_tranzport_core();
+}
+
+void close_tranzport(tranzport_t *z)
+{
+ int val;
+
+ val = close(z->udev);
+ if (val < 0)
+ log_error("unable to release tranzport");
+
+ free(z);
+}
+
+int tranzport_write_core(tranzport_t *z, uint8_t *cmd, int timeout)
+{
+ int val;
+ val = write(z->udev, cmd, 8);
+ if (val < 0)
+ return val;
+ if (val != 8)
+ return -1;
+ return 0;
+}
+
+int tranzport_lcdwrite(tranzport_t *z, uint8_t cell, char *text, int timeout)
+{
+ uint8_t cmd[8];
+
+ if (cell > 9) {
+ return -1;
+ }
+
+ cmd[0] = 0x00;
+ cmd[1] = 0x01;
+ cmd[2] = cell;
+ cmd[3] = text[0];
+ cmd[4] = text[1];
+ cmd[5] = text[2];
+ cmd[6] = text[3];
+ cmd[7] = 0x00;
+
+ return tranzport_write_core(z, cmd, timeout);
+}
+
+int tranzport_lighton(tranzport_t *z, uint8_t light, int timeout)
+{
+ uint8_t cmd[8];
+
+ cmd[0] = 0x00;
+ cmd[1] = 0x00;
+ cmd[2] = light;
+ cmd[3] = 0x01;
+ cmd[4] = 0x00;
+ cmd[5] = 0x00;
+ cmd[6] = 0x00;
+ cmd[7] = 0x00;
+
+ return tranzport_write_core(z, &cmd[0], timeout);
+}
+
+int tranzport_lightoff(tranzport_t *z, uint8_t light, int timeout)
+{
+ uint8_t cmd[8];
+
+ cmd[0] = 0x00;
+ cmd[1] = 0x00;
+ cmd[2] = light;
+ cmd[3] = 0x00;
+ cmd[4] = 0x00;
+ cmd[5] = 0x00;
+ cmd[6] = 0x00;
+ cmd[7] = 0x00;
+
+ return tranzport_write_core(z, &cmd[0], timeout);
+}
+
+int tranzport_read(tranzport_t *z, uint8_t *status, uint32_t *buttons, uint8_t *datawheel, int timeout)
+{
+ uint8_t buf[8];
+ int val;
+
+ memset(buf, 0xff, 8);
+ val = read(z->udev, buf, 8);
+ if (val < 0) {
+ printf("errno: %d\n",errno);
+ switch(errno) {
+ case ENOENT: ;
+ case ECONNRESET: ;
+ case ESHUTDOWN: printf("dying\n"); exit(1); break;
+ }
+ return val;
+ }
+ if (val != 8)
+ return -1;
+
+ /*printf("read: %02x %02x %02x %02x %02x %02x %02x %02x\n", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]);*/
+
+ *status = buf[1];
+
+ *buttons = 0;
+ *buttons |= buf[2] << 24;
+ *buttons |= buf[3] << 16;
+ *buttons |= buf[4] << 8;
+ *buttons |= buf[5];
+
+ *datawheel = buf[6];
+
+ return 0;
+}
+
+void lights_core(tranzport_t *z, uint32_t buttons, uint32_t buttonmask, uint8_t light)
+{
+ if (buttons & buttonmask) {
+ if (buttons & BUTTONMASK_SHIFT) {
+ tranzport_lightoff(z, light, 10);
+ } else {
+ tranzport_lighton(z, light, 10);
+ }
+ }
+}
+
+void do_lights(tranzport_t *z, uint32_t buttons)
+{
+ lights_core(z, buttons, BUTTONMASK_RECORD, LIGHT_RECORD);
+ lights_core(z, buttons, BUTTONMASK_TRACKREC, LIGHT_TRACKREC);
+ lights_core(z, buttons, BUTTONMASK_TRACKMUTE, LIGHT_TRACKMUTE);
+ lights_core(z, buttons, BUTTONMASK_TRACKSOLO, LIGHT_TRACKSOLO);
+ lights_core(z, buttons, BUTTONMASK_TRACKSOLO, LIGHT_ANYSOLO);
+ lights_core(z, buttons, BUTTONMASK_PUNCH, LIGHT_PUNCH);
+ lights_core(z, buttons, BUTTONMASK_LOOP, LIGHT_LOOP);
+}
+
+void buttons_core(tranzport_t *z, uint32_t buttons, uint32_t buttonmask, char *str)
+{
+ if (buttons & buttonmask)
+ printf(" %s", str);
+}
+
+void do_buttons(tranzport_t *z, uint32_t buttons, uint8_t datawheel)
+{
+ printf("buttons: %x ", buttons);
+ buttons_core(z, buttons, BUTTONMASK_BATTERY, "battery");
+ buttons_core(z, buttons, BUTTONMASK_BACKLIGHT, "backlight");
+ buttons_core(z, buttons, BUTTONMASK_TRACKLEFT, "trackleft");
+ buttons_core(z, buttons, BUTTONMASK_TRACKRIGHT, "trackright");
+ buttons_core(z, buttons, BUTTONMASK_TRACKREC, "trackrec");
+ buttons_core(z, buttons, BUTTONMASK_TRACKMUTE, "trackmute");
+ buttons_core(z, buttons, BUTTONMASK_TRACKSOLO, "tracksolo");
+ buttons_core(z, buttons, BUTTONMASK_UNDO, "undo");
+ buttons_core(z, buttons, BUTTONMASK_IN, "in");
+ buttons_core(z, buttons, BUTTONMASK_OUT, "out");
+ buttons_core(z, buttons, BUTTONMASK_PUNCH, "punch");
+ buttons_core(z, buttons, BUTTONMASK_LOOP, "loop");
+ buttons_core(z, buttons, BUTTONMASK_PREV, "prev");
+ buttons_core(z, buttons, BUTTONMASK_ADD, "add");
+ buttons_core(z, buttons, BUTTONMASK_NEXT, "next");
+ buttons_core(z, buttons, BUTTONMASK_REWIND, "rewind");
+ buttons_core(z, buttons, BUTTONMASK_FASTFORWARD, "fastforward");
+ buttons_core(z, buttons, BUTTONMASK_STOP, "stop");
+ buttons_core(z, buttons, BUTTONMASK_PLAY, "play");
+ buttons_core(z, buttons, BUTTONMASK_RECORD, "record");
+ buttons_core(z, buttons, BUTTONMASK_SHIFT, "shift");
+ if (datawheel)
+ printf(" datawheel=%02x", datawheel);
+ printf("\n");
+}
+
+void do_lcd(tranzport_t *z)
+{
+ tranzport_lcdwrite(z, 0, " ", 10);
+ tranzport_lcdwrite(z, 1, "DISL", 10);
+ tranzport_lcdwrite(z, 2, "EXIA", 10);
+ tranzport_lcdwrite(z, 3, " FOR", 10);
+ tranzport_lcdwrite(z, 4, " ", 10);
+
+ tranzport_lcdwrite(z, 5, " ", 10);
+ tranzport_lcdwrite(z, 6, " CUR", 10);
+ tranzport_lcdwrite(z, 7, "E FO", 10);
+ tranzport_lcdwrite(z, 8, "UND ", 10);
+ tranzport_lcdwrite(z, 9, " ", 10);
+}
+
+void do_lcd2(tranzport_t *z)
+{
+ tranzport_lcdwrite(z, 0, "THE ", 10);
+ tranzport_lcdwrite(z, 1, "TRAN", 10);
+ tranzport_lcdwrite(z, 2, "ZPOR", 10);
+ tranzport_lcdwrite(z, 3, "T RO", 10);
+ tranzport_lcdwrite(z, 4, " KS", 10);
+
+ tranzport_lcdwrite(z, 5, "AWES", 10);
+ tranzport_lcdwrite(z, 6, "OMEE", 10);
+ tranzport_lcdwrite(z, 7, "LEEE", 10);
+ tranzport_lcdwrite(z, 8, "UND ", 10);
+ tranzport_lcdwrite(z, 9, "GROK", 10);
+}
+
+int lights_off(tranzport_t *z) {
+ static int i = 0;
+ int j = 0;
+ for(;j<2; j++,i = (i+1) % 7) {
+ tranzport_lightoff(z, i, 10);
+ }
+return 0;
+}
+
+int lights_on(tranzport_t *z) {
+ static int i = 0;
+ int j = 0;
+ for(;j<2; j++,i = (i+1) % 7) {
+ tranzport_lighton(z, i, 10);
+ }
+return 0;
+}
+
+int main()
+{
+ tranzport_t *z;
+ uint8_t status;
+ uint32_t buttons;
+ uint8_t datawheel;
+ int val;
+
+ z = open_tranzport();
+
+ do_lcd(z);
+
+ for(;;) {
+
+ // do_lcd(z);
+ lights_on(z);
+ // do_lcd2(z);
+
+ val = tranzport_read(z, &status, &buttons, &datawheel, 60000);
+ if (val < 0)
+ continue;
+
+ if (status == STATUS_OFFLINE) {
+ printf("offline: ");
+ continue;
+ }
+
+ if (status == STATUS_ONLINE) {
+ printf("online: ");
+ do_lcd(z);
+ }
+
+ if (status == STATUS_OK) {
+ printf("OK: ");
+ do_lcd(z);
+ }
+
+// do_lights(z, buttons);
+ do_buttons(z, buttons, datawheel);
+ lights_off(z);
+ }
+
+ close_tranzport(z);
+
+ return 0;
+}
+
diff --git a/libs/surfaces/frontier/kernel_drivers/tests/tranzport_lights.c b/libs/surfaces/frontier/kernel_drivers/tests/tranzport_lights.c
new file mode 100644
index 0000000000..4096ee680d
--- /dev/null
+++ b/libs/surfaces/frontier/kernel_drivers/tests/tranzport_lights.c
@@ -0,0 +1,361 @@
+/*
+ * tranzport 0.1 <tranzport.sf.net>
+ * oct 18, 2005
+ * arthur@artcmusic.com
+ */
+
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <malloc.h>
+
+#define VENDORID 0x165b
+#define PRODUCTID 0x8101
+
+#define READ_ENDPOINT 0x81
+#define WRITE_ENDPOINT 0x02
+
+enum {
+ LIGHT_RECORD = 0,
+ LIGHT_TRACKREC,
+ LIGHT_TRACKMUTE,
+ LIGHT_TRACKSOLO,
+ LIGHT_ANYSOLO,
+ LIGHT_LOOP,
+ LIGHT_PUNCH
+};
+
+#define BUTTONMASK_BATTERY 0x00004000
+#define BUTTONMASK_BACKLIGHT 0x00008000
+#define BUTTONMASK_TRACKLEFT 0x04000000
+#define BUTTONMASK_TRACKRIGHT 0x40000000
+#define BUTTONMASK_TRACKREC 0x00040000
+#define BUTTONMASK_TRACKMUTE 0x00400000
+#define BUTTONMASK_TRACKSOLO 0x00000400
+#define BUTTONMASK_UNDO 0x80000000
+#define BUTTONMASK_IN 0x02000000
+#define BUTTONMASK_OUT 0x20000000
+#define BUTTONMASK_PUNCH 0x00800000
+#define BUTTONMASK_LOOP 0x00080000
+#define BUTTONMASK_PREV 0x00020000
+#define BUTTONMASK_ADD 0x00200000
+#define BUTTONMASK_NEXT 0x00000200
+#define BUTTONMASK_REWIND 0x01000000
+#define BUTTONMASK_FASTFORWARD 0x10000000
+#define BUTTONMASK_STOP 0x00010000
+#define BUTTONMASK_PLAY 0x00100000
+#define BUTTONMASK_RECORD 0x00000100
+#define BUTTONMASK_SHIFT 0x08000000
+
+#define STATUS_OFFLINE 0xff
+#define STATUS_ONLINE 0x01
+#define STATUS_OK 0x00
+
+struct tranzport_s {
+ int *dev;
+ int udev;
+};
+
+typedef struct tranzport_s tranzport_t;
+
+void log_entry(FILE *fp, char *format, va_list ap)
+{
+ vfprintf(fp, format, ap);
+ fputc('\n', fp);
+}
+
+void log_error(char *format, ...)
+{
+ va_list ap;
+ va_start(ap, format);
+ log_entry(stderr, format, ap);
+ va_end(ap);
+}
+
+void vlog_error(char *format, va_list ap)
+{
+ log_entry(stderr, format, ap);
+}
+
+void die(char *format, ...)
+{
+ va_list ap;
+ va_start(ap, format);
+ vlog_error(format, ap);
+ va_end(ap);
+ exit(1);
+}
+
+tranzport_t *open_tranzport_core()
+{
+ tranzport_t *z;
+ int val;
+
+ z = malloc(sizeof(tranzport_t));
+ if (!z)
+ die("not enough memory");
+ memset(z, 0, sizeof(tranzport_t));
+
+ z->udev = open("/dev/tranzport0",O_RDWR);
+ if (!z->udev)
+ die("unable to open tranzport");
+
+ return z;
+}
+
+tranzport_t *open_tranzport()
+{
+return open_tranzport_core();
+}
+
+void close_tranzport(tranzport_t *z)
+{
+ int val;
+
+ val = close(z->udev);
+ if (val < 0)
+ log_error("unable to release tranzport");
+
+ free(z);
+}
+
+int tranzport_write_core(tranzport_t *z, uint8_t *cmd, int timeout)
+{
+ int val;
+ val = write(z->udev, cmd, 8);
+ if (val < 0)
+ return val;
+ if (val != 8)
+ return -1;
+ return 0;
+}
+
+int tranzport_lcdwrite(tranzport_t *z, uint8_t cell, char *text, int timeout)
+{
+ uint8_t cmd[8];
+
+ if (cell > 9) {
+ return -1;
+ }
+
+ cmd[0] = 0x00;
+ cmd[1] = 0x01;
+ cmd[2] = cell;
+ cmd[3] = text[0];
+ cmd[4] = text[1];
+ cmd[5] = text[2];
+ cmd[6] = text[3];
+ cmd[7] = 0x00;
+
+ return tranzport_write_core(z, cmd, timeout);
+}
+
+int tranzport_lighton(tranzport_t *z, uint8_t light, int timeout)
+{
+ uint8_t cmd[8];
+
+ cmd[0] = 0x00;
+ cmd[1] = 0x00;
+ cmd[2] = light;
+ cmd[3] = 0x01;
+ cmd[4] = 0x00;
+ cmd[5] = 0x00;
+ cmd[6] = 0x00;
+ cmd[7] = 0x00;
+
+ return tranzport_write_core(z, &cmd[0], timeout);
+}
+
+int tranzport_lightoff(tranzport_t *z, uint8_t light, int timeout)
+{
+ uint8_t cmd[8];
+
+ cmd[0] = 0x00;
+ cmd[1] = 0x00;
+ cmd[2] = light;
+ cmd[3] = 0x00;
+ cmd[4] = 0x00;
+ cmd[5] = 0x00;
+ cmd[6] = 0x00;
+ cmd[7] = 0x00;
+
+ return tranzport_write_core(z, &cmd[0], timeout);
+}
+
+int tranzport_read(tranzport_t *z, uint8_t *status, uint32_t *buttons, uint8_t *datawheel, int timeout)
+{
+ uint8_t buf[8];
+ int val;
+
+ memset(buf, 0xff, 8);
+ val = read(z->udev, buf, 8);
+ if (val < 0) {
+ // printf("errno: %d\n",errno);
+ return val;
+ }
+ if (val != 8)
+ return -1;
+
+ /*printf("read: %02x %02x %02x %02x %02x %02x %02x %02x\n", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]);*/
+
+ *status = buf[1];
+
+ *buttons = 0;
+ *buttons |= buf[2] << 24;
+ *buttons |= buf[3] << 16;
+ *buttons |= buf[4] << 8;
+ *buttons |= buf[5];
+
+ *datawheel = buf[6];
+
+ return 0;
+}
+
+void lights_core(tranzport_t *z, uint32_t buttons, uint32_t buttonmask, uint8_t light)
+{
+ if (buttons & buttonmask) {
+ if (buttons & BUTTONMASK_SHIFT) {
+ tranzport_lightoff(z, light, 1000);
+ } else {
+ tranzport_lighton(z, light, 1000);
+ }
+ }
+}
+
+void do_lights(tranzport_t *z, uint32_t buttons)
+{
+ lights_core(z, buttons, BUTTONMASK_RECORD, LIGHT_RECORD);
+ lights_core(z, buttons, BUTTONMASK_TRACKREC, LIGHT_TRACKREC);
+ lights_core(z, buttons, BUTTONMASK_TRACKMUTE, LIGHT_TRACKMUTE);
+ lights_core(z, buttons, BUTTONMASK_TRACKSOLO, LIGHT_TRACKSOLO);
+ lights_core(z, buttons, BUTTONMASK_TRACKSOLO, LIGHT_ANYSOLO);
+ lights_core(z, buttons, BUTTONMASK_PUNCH, LIGHT_PUNCH);
+ lights_core(z, buttons, BUTTONMASK_LOOP, LIGHT_LOOP);
+}
+
+void buttons_core(tranzport_t *z, uint32_t buttons, uint32_t buttonmask, char *str)
+{
+ if (buttons & buttonmask)
+ printf(" %s", str);
+}
+
+void do_buttons(tranzport_t *z, uint32_t buttons, uint8_t datawheel)
+{
+ printf("buttons: %x ", buttons);
+ buttons_core(z, buttons, BUTTONMASK_BATTERY, "battery");
+ buttons_core(z, buttons, BUTTONMASK_BACKLIGHT, "backlight");
+ buttons_core(z, buttons, BUTTONMASK_TRACKLEFT, "trackleft");
+ buttons_core(z, buttons, BUTTONMASK_TRACKRIGHT, "trackright");
+ buttons_core(z, buttons, BUTTONMASK_TRACKREC, "trackrec");
+ buttons_core(z, buttons, BUTTONMASK_TRACKMUTE, "trackmute");
+ buttons_core(z, buttons, BUTTONMASK_TRACKSOLO, "tracksolo");
+ buttons_core(z, buttons, BUTTONMASK_UNDO, "undo");
+ buttons_core(z, buttons, BUTTONMASK_IN, "in");
+ buttons_core(z, buttons, BUTTONMASK_OUT, "out");
+ buttons_core(z, buttons, BUTTONMASK_PUNCH, "punch");
+ buttons_core(z, buttons, BUTTONMASK_LOOP, "loop");
+ buttons_core(z, buttons, BUTTONMASK_PREV, "prev");
+ buttons_core(z, buttons, BUTTONMASK_ADD, "add");
+ buttons_core(z, buttons, BUTTONMASK_NEXT, "next");
+ buttons_core(z, buttons, BUTTONMASK_REWIND, "rewind");
+ buttons_core(z, buttons, BUTTONMASK_FASTFORWARD, "fastforward");
+ buttons_core(z, buttons, BUTTONMASK_STOP, "stop");
+ buttons_core(z, buttons, BUTTONMASK_PLAY, "play");
+ buttons_core(z, buttons, BUTTONMASK_RECORD, "record");
+ buttons_core(z, buttons, BUTTONMASK_SHIFT, "shift");
+ if (datawheel)
+ printf(" datawheel=%02x", datawheel);
+ printf("\n");
+}
+
+void do_lcd(tranzport_t *z)
+{
+ tranzport_lcdwrite(z, 0, " ", 1000);
+ tranzport_lcdwrite(z, 1, "DISL", 1000);
+ tranzport_lcdwrite(z, 2, "EXIA", 1000);
+ tranzport_lcdwrite(z, 3, " FOR", 1000);
+ tranzport_lcdwrite(z, 4, " ", 1000);
+
+ tranzport_lcdwrite(z, 5, " ", 1000);
+ tranzport_lcdwrite(z, 6, " CUR", 1000);
+ tranzport_lcdwrite(z, 7, "E FO", 1000);
+ tranzport_lcdwrite(z, 8, "UND ", 1000);
+ tranzport_lcdwrite(z, 9, " ", 1000);
+}
+
+void do_lcd2(tranzport_t *z)
+{
+ tranzport_lcdwrite(z, 0, "THE ", 1000);
+ tranzport_lcdwrite(z, 1, "TRAN", 1000);
+ tranzport_lcdwrite(z, 2, "ZPOR", 1000);
+ tranzport_lcdwrite(z, 3, "T RO", 1000);
+ tranzport_lcdwrite(z, 4, "KS ", 1000);
+
+ tranzport_lcdwrite(z, 5, "AWES", 1000);
+ tranzport_lcdwrite(z, 6, "OMEE", 1000);
+ tranzport_lcdwrite(z, 7, "LEEE", 1000);
+ tranzport_lcdwrite(z, 8, "WITH", 1000);
+ tranzport_lcdwrite(z, 9, "ARDO", 1000);
+}
+
+lights_off(tranzport_t *z) {
+int i;
+ for(i=0;i<7;i++) {
+ tranzport_lightoff(z, i, 1000);
+ }
+}
+
+lights_on(tranzport_t *z) {
+int i;
+ for(i=0;i<7;i++) {
+ tranzport_lighton(z, i, 1000);
+ }
+}
+
+int main()
+{
+ tranzport_t *z;
+ uint8_t status;
+ uint32_t buttons;
+ uint8_t datawheel;
+ int val;
+
+ z = open_tranzport();
+
+ do_lcd(z);
+
+ for(;;) {
+
+ do_lcd(z);
+ lights_on(z);
+ do_lcd2(z);
+ lights_off(z);
+
+// val = tranzport_read(z, &status, &buttons, &datawheel, 60000);
+ val = -1;
+ if (val < 0)
+ continue;
+
+ if (status == STATUS_OFFLINE) {
+ printf("offline: ");
+ continue;
+ }
+
+ if (status == STATUS_ONLINE) {
+ printf("online: ");
+ do_lcd(z);
+ }
+
+ do_lights(z, buttons);
+ do_buttons(z, buttons, datawheel);
+ }
+
+ close_tranzport(z);
+
+ return 0;
+}
+
diff --git a/libs/surfaces/frontier/kernel_drivers/tests/tranzport_tests.sh b/libs/surfaces/frontier/kernel_drivers/tests/tranzport_tests.sh
new file mode 100755
index 0000000000..540c62fe16
--- /dev/null
+++ b/libs/surfaces/frontier/kernel_drivers/tests/tranzport_tests.sh
@@ -0,0 +1,27 @@
+#!/bin/sh
+
+echo "Testing lights"
+tranzport_lights &
+A=$!
+sleep 30
+kill $A
+echo "Testing interleaved_reads/writes"
+tranzport &
+A=$!
+sleep 30
+kill $A
+
+exit 0
+
+# not done yet
+echo "Testing_screen"
+tranzport_screen &
+A=$!
+sleep 30
+kill $A
+echo "Testing_reads"
+tranzport_read &
+A=$!
+sleep 30
+kill $A
+
diff --git a/libs/surfaces/frontier/kernel_drivers/tranzport.c b/libs/surfaces/frontier/kernel_drivers/tranzport.c
new file mode 100644
index 0000000000..6893f66921
--- /dev/null
+++ b/libs/surfaces/frontier/kernel_drivers/tranzport.c
@@ -0,0 +1,1065 @@
+/*
+ * Frontier Designs Tranzport driver
+ *
+ * Copyright (C) 2007 Michael Taht (m@taht.net)
+ *
+ * Based on the usbled driver and ldusb drivers by
+ *
+ * Copyright (C) 2004 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2005 Michael Hund <mhund@ld-didactic.de>
+ *
+ * The ldusb driver was, in turn, derived from Lego USB Tower driver
+ * Copyright (C) 2003 David Glance <advidgsf@sourceforge.net>
+ * 2001-2004 Juergen Stuber <starblue@users.sourceforge.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ *
+ */
+
+/**
+ * This driver uses a ring buffer for time critical reading of
+ * interrupt in reports and provides read and write methods for
+ * raw interrupt reports.
+ */
+
+/* Note: this currently uses a dumb ringbuffer for reads and writes.
+ * A more optimal driver would cache and kill off outstanding urbs that are
+ * now invalid, and ignore ones that already were in the queue but valid
+ * as we only have 17 commands for the tranzport. In particular this is
+ * key for getting lights to flash in time as otherwise many commands
+ * can be buffered up before the light change makes it to the interface.
+*/
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/version.h>
+
+#include <asm/uaccess.h>
+#include <linux/input.h>
+#include <linux/usb.h>
+#include <linux/poll.h>
+
+/* Define these values to match your devices */
+#define VENDOR_ID 0x165b
+#define PRODUCT_ID 0x8101
+
+#ifdef CONFIG_USB_DYNAMIC_MINORS
+#define USB_TRANZPORT_MINOR_BASE 0
+#else
+// FIXME 176 - is the ldusb driver's minor - apply for that
+#define USB_TRANZPORT_MINOR_BASE 176
+#endif
+
+/* table of devices that work with this driver */
+static struct usb_device_id usb_tranzport_table [] = {
+ { USB_DEVICE(VENDOR_ID, PRODUCT_ID) },
+ { } /* Terminating entry */
+};
+MODULE_DEVICE_TABLE(usb, usb_tranzport_table);
+MODULE_VERSION("0.29");
+MODULE_AUTHOR("Mike Taht <m@taht.net>");
+MODULE_DESCRIPTION("Tranzport USB Driver");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("Frontier Designs Control Surface");
+
+
+/* make this work on older kernel versions */
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
+
+/**
+ * usb_endpoint_dir_out - check if the endpoint has OUT direction
+ * @epd: endpoint to be checked
+ *
+ * Returns true if the endpoint is of type OUT, otherwise it returns false.
+ */
+
+static inline int usb_endpoint_dir_out(const struct usb_endpoint_descriptor *epd)
+{
+ return ((epd->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT);
+}
+
+static inline int usb_endpoint_dir_in(const struct usb_endpoint_descriptor *epd)
+{
+ return ((epd->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN);
+}
+
+
+/**
+ * usb_endpoint_xfer_int - check if the endpoint has interrupt transfer type
+ * @epd: endpoint to be checked
+ *
+ * Returns true if the endpoint is of type interrupt, otherwise it returns
+ * false.
+ */
+static inline int usb_endpoint_xfer_int(const struct usb_endpoint_descriptor *epd)
+{
+ return ((epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
+ USB_ENDPOINT_XFER_INT);
+}
+
+
+/**
+ * usb_endpoint_is_int_in - check if the endpoint is interrupt IN
+ * @epd: endpoint to be checked
+ *
+ * Returns true if the endpoint has interrupt transfer type and IN direction,
+ * otherwise it returns false.
+ */
+
+static inline int usb_endpoint_is_int_in(const struct usb_endpoint_descriptor *epd)
+{
+ return (usb_endpoint_xfer_int(epd) && usb_endpoint_dir_in(epd));
+}
+
+/**
+ * usb_endpoint_is_int_out - check if the endpoint is interrupt OUT
+ * @epd: endpoint to be checked
+ *
+ * Returns true if the endpoint has interrupt transfer type and OUT direction,
+ * otherwise it returns false.
+ */
+
+static inline int usb_endpoint_is_int_out(const struct usb_endpoint_descriptor *epd)
+{
+ return (usb_endpoint_xfer_int(epd) && usb_endpoint_dir_out(epd));
+}
+
+#endif /* older kernel versions */
+
+/* These two aren't done yet */
+
+#define SUPPRESS_EXTRA_ONLINE_EVENTS 0
+#define BUFFERED_WRITES 0
+
+#define SUPPRESS_EXTRA_OFFLINE_EVENTS 1
+#define COMPRESS_WHEEL_EVENTS 1
+#define BUFFERED_READS 1
+#define RING_BUFFER_SIZE 1000
+#define WRITE_BUFFER_SIZE 34
+#define TRANZPORT_USB_TIMEOUT 10
+
+
+static int debug = 0;
+
+/* Use our own dbg macro */
+#define dbg_info(dev, format, arg...) do { if (debug) dev_info(dev , format , ## arg); } while (0)
+
+/* Module parameters */
+
+module_param(debug, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug enabled or not");
+
+/* All interrupt in transfers are collected in a ring buffer to
+ * avoid racing conditions and get better performance of the driver.
+ */
+
+static int ring_buffer_size = RING_BUFFER_SIZE;
+
+module_param(ring_buffer_size, int, S_IRUGO);
+MODULE_PARM_DESC(ring_buffer_size, "Read ring buffer size in reports");
+
+/* The write_buffer can one day contain more than one interrupt out transfer.
+ */
+static int write_buffer_size = WRITE_BUFFER_SIZE;
+module_param(write_buffer_size, int, S_IRUGO);
+MODULE_PARM_DESC(write_buffer_size, "Write buffer size");
+
+/*
+ * Increase the interval for debugging purposes.
+ * or set to 1 to use the standard interval from the endpoint descriptors.
+ */
+
+static int min_interrupt_in_interval = TRANZPORT_USB_TIMEOUT;
+module_param(min_interrupt_in_interval, int, 0);
+MODULE_PARM_DESC(min_interrupt_in_interval, "Minimum interrupt in interval in ms");
+
+static int min_interrupt_out_interval = TRANZPORT_USB_TIMEOUT;
+module_param(min_interrupt_out_interval, int, 0);
+MODULE_PARM_DESC(min_interrupt_out_interval, "Minimum interrupt out interval in ms");
+
+struct tranzport_cmd {
+ unsigned char cmd[8];
+};
+
+/* Structure to hold all of our device specific stuff */
+struct usb_tranzport {
+ struct semaphore sem; /* locks this structure */
+ struct usb_interface* intf; /* save off the usb interface pointer */
+
+ int open_count; /* number of times this port has been opened */
+
+ struct tranzport_cmd (*ring_buffer)[RING_BUFFER_SIZE]; /* just make c happy */
+ unsigned int ring_head;
+ unsigned int ring_tail;
+
+ wait_queue_head_t read_wait;
+ wait_queue_head_t write_wait;
+
+ unsigned char* interrupt_in_buffer;
+ struct usb_endpoint_descriptor* interrupt_in_endpoint;
+ struct urb* interrupt_in_urb;
+ int interrupt_in_interval;
+ size_t interrupt_in_endpoint_size;
+ int interrupt_in_running;
+ int interrupt_in_done;
+
+ char* interrupt_out_buffer;
+ struct usb_endpoint_descriptor* interrupt_out_endpoint;
+ struct urb* interrupt_out_urb;
+ int interrupt_out_interval;
+ size_t interrupt_out_endpoint_size;
+ int interrupt_out_busy;
+
+ /* Sysfs support - most of these are not hooked up yet */
+
+ int event; /* alternate interface to events */
+ int wheel; /* - for negative, 0 for none, + for positive */
+ int lights;
+ unsigned char dump_state; /* 0 if disabled 1 if enabled */
+ unsigned char enable; /* 0 if disabled 1 if enabled */
+ unsigned char offline; /* if the device is out of range or asleep */
+ unsigned char compress_wheel; /* flag to compress wheel events */
+ unsigned char LightRecord;
+ unsigned char LightTrackrec;
+ unsigned char LightTrackmute;
+ unsigned char LightTracksolo;
+ unsigned char LightAnysolo;
+ unsigned char LightLoop;
+ unsigned char LightPunch;
+ unsigned char last_cmd[8];
+ unsigned char screen[40]; // We'll also have cells
+
+};
+
+/* prevent races between open() and disconnect() */
+static DEFINE_MUTEX(disconnect_mutex);
+
+static struct usb_driver usb_tranzport_driver;
+
+/**
+ * usb_tranzport_abort_transfers
+ * aborts transfers and frees associated data structures
+ */
+static void usb_tranzport_abort_transfers(struct usb_tranzport *dev)
+{
+ /* shutdown transfer */
+ if (dev->interrupt_in_running) {
+ dev->interrupt_in_running = 0;
+ if (dev->intf)
+ usb_kill_urb(dev->interrupt_in_urb);
+ }
+ if (dev->interrupt_out_busy)
+ if (dev->intf)
+ usb_kill_urb(dev->interrupt_out_urb);
+}
+
+#define show_set_light(value) \
+static ssize_t show_##value(struct device *dev, struct device_attribute *attr, char *buf) \
+{ \
+ struct usb_interface *intf = to_usb_interface(dev); \
+ struct usb_tranzport *t = usb_get_intfdata(intf); \
+ \
+ return sprintf(buf, "%d\n", t->value); \
+} \
+static ssize_t set_##value(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) \
+{ \
+ struct usb_interface *intf = to_usb_interface(dev); \
+ struct usb_tranzport *t = usb_get_intfdata(intf); \
+ int temp = simple_strtoul(buf, NULL, 10); \
+ \
+ t->value = temp; \
+ return count; \
+} \
+static DEVICE_ATTR(value, S_IWUGO | S_IRUGO, show_##value, set_##value);
+
+show_set_light(LightRecord);
+show_set_light(LightTrackrec);
+show_set_light(LightTrackmute);
+show_set_light(LightTracksolo);
+show_set_light(LightAnysolo);
+show_set_light(LightLoop);
+show_set_light(LightPunch);
+
+show_set_light(enable);
+show_set_light(offline);
+show_set_light(compress_wheel);
+show_set_light(dump_state);
+
+#define show_set_int(value) \
+static ssize_t show_##value(struct device *dev, struct device_attribute *attr, char *buf) \
+{ \
+ struct usb_interface *intf = to_usb_interface(dev); \
+ struct usb_tranzport *t = usb_get_intfdata(intf); \
+ \
+ return sprintf(buf, "%d\n", t->value); \
+} \
+static ssize_t set_##value(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) \
+{ \
+ struct usb_interface *intf = to_usb_interface(dev); \
+ struct usb_tranzport *t = usb_get_intfdata(intf); \
+ int temp = simple_strtoul(buf, NULL, 10); \
+ \
+ t->value = temp; \
+ return count; \
+} \
+static DEVICE_ATTR(value, S_IWUGO | S_IRUGO, show_##value, set_##value);
+
+show_set_int(wheel);
+show_set_int(event);
+
+#define show_set_cmd(value) \
+static ssize_t show_##value(struct device *dev, struct device_attribute *attr, char *buf) \
+{ \
+ struct usb_interface *intf = to_usb_interface(dev); \
+ struct usb_tranzport *t = usb_get_intfdata(intf); \
+ \
+ return sprintf(buf, "%d\n", t->value); \
+} \
+static ssize_t set_##value(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) \
+{ \
+ struct usb_interface *intf = to_usb_interface(dev); \
+ struct usb_tranzport *t = usb_get_intfdata(intf); \
+ int temp = simple_strtoul(buf, NULL, 10); \
+ \
+ t->value = temp; \
+ return count; \
+} \
+static DEVICE_ATTR(value, S_IWUGO | S_IRUGO, show_##value, set_##value);
+
+
+
+
+/**
+ * usb_tranzport_delete
+ */
+static void usb_tranzport_delete(struct usb_tranzport *dev)
+{
+ usb_tranzport_abort_transfers(dev);
+ /* This is just too twisted to be correct */
+ if(dev->intf != NULL) {
+ device_remove_file(&dev->intf->dev, &dev_attr_LightRecord);
+ device_remove_file(&dev->intf->dev, &dev_attr_LightTrackrec);
+ device_remove_file(&dev->intf->dev, &dev_attr_LightTrackmute);
+ device_remove_file(&dev->intf->dev, &dev_attr_LightTracksolo);
+ device_remove_file(&dev->intf->dev, &dev_attr_LightTrackmute);
+ device_remove_file(&dev->intf->dev, &dev_attr_LightAnysolo);
+ device_remove_file(&dev->intf->dev, &dev_attr_LightLoop);
+ device_remove_file(&dev->intf->dev, &dev_attr_LightPunch);
+ device_remove_file(&dev->intf->dev, &dev_attr_wheel);
+ device_remove_file(&dev->intf->dev, &dev_attr_enable);
+ device_remove_file(&dev->intf->dev, &dev_attr_event);
+ device_remove_file(&dev->intf->dev, &dev_attr_offline);
+ device_remove_file(&dev->intf->dev, &dev_attr_compress_wheel);
+
+ device_remove_file(&dev->intf->dev, &dev_attr_dump_state);
+ }
+
+ /* free data structures */
+ usb_free_urb(dev->interrupt_in_urb);
+ usb_free_urb(dev->interrupt_out_urb);
+ kfree(dev->ring_buffer);
+ kfree(dev->interrupt_in_buffer);
+ kfree(dev->interrupt_out_buffer);
+ kfree(dev);
+}
+
+/**
+ * usb_tranzport_interrupt_in_callback
+ */
+
+static void usb_tranzport_interrupt_in_callback(struct urb *urb)
+{
+ struct usb_tranzport *dev = urb->context;
+ unsigned int next_ring_head;
+ int retval = -1;
+
+ if (urb->status) {
+ if (urb->status == -ENOENT ||
+ urb->status == -ECONNRESET ||
+ urb->status == -ESHUTDOWN) {
+ goto exit;
+ } else {
+ dbg_info(&dev->intf->dev, "%s: nonzero status received: %d\n",
+ __FUNCTION__, urb->status);
+ goto resubmit; /* maybe we can recover */
+ }
+ }
+
+ if (urb->actual_length != 8) {
+ dev_warn(&dev->intf->dev,
+ "Urb length was %d bytes!! Do something intelligent \n", urb->actual_length);
+ } else {
+ dbg_info(&dev->intf->dev, "%s: received: %02x%02x%02x%02x%02x%02x%02x%02x\n",
+ __FUNCTION__, dev->interrupt_in_buffer[0],dev->interrupt_in_buffer[1],dev->interrupt_in_buffer[2],dev->interrupt_in_buffer[3],dev->interrupt_in_buffer[4],dev->interrupt_in_buffer[5],dev->interrupt_in_buffer[6],dev->interrupt_in_buffer[7]);
+#if SUPPRESS_EXTRA_OFFLINE_EVENTS
+ if(dev->offline == 2 && dev->interrupt_in_buffer[1] == 0xff) { goto resubmit; }
+ if(dev->offline == 1 && dev->interrupt_in_buffer[1] == 0xff) { dev->offline = 2; goto resubmit; }
+
+/* Always pass one offline event up the stack */
+ if(dev->offline > 0 && dev->interrupt_in_buffer[1] != 0xff) { dev->offline = 0; }
+ if(dev->offline == 0 && dev->interrupt_in_buffer[1] == 0xff) { dev->offline = 1; }
+
+#endif
+ dbg_info(&dev->intf->dev, "%s: head, tail are %x, %x\n", __FUNCTION__,dev->ring_head,dev->ring_tail);
+
+ next_ring_head = (dev->ring_head+1) % ring_buffer_size;
+
+ if (next_ring_head != dev->ring_tail) {
+ memcpy(&((*dev->ring_buffer)[dev->ring_head]), dev->interrupt_in_buffer, urb->actual_length);
+ dev->ring_head = next_ring_head;
+ retval = 0;
+ memset(dev->interrupt_in_buffer, 0, urb->actual_length);
+ } else {
+ dev_warn(&dev->intf->dev,
+ "Ring buffer overflow, %d bytes dropped\n",
+ urb->actual_length);
+ memset(dev->interrupt_in_buffer, 0, urb->actual_length);
+ }
+ }
+
+resubmit:
+ /* resubmit if we're still running */
+ if (dev->interrupt_in_running && dev->intf) {
+ retval = usb_submit_urb(dev->interrupt_in_urb, GFP_ATOMIC);
+ if (retval)
+ dev_err(&dev->intf->dev,
+ "usb_submit_urb failed (%d)\n", retval);
+ }
+
+exit:
+ dev->interrupt_in_done = 1;
+ wake_up_interruptible(&dev->read_wait);
+}
+
+/**
+ * usb_tranzport_interrupt_out_callback
+ */
+static void usb_tranzport_interrupt_out_callback(struct urb *urb)
+{
+ struct usb_tranzport *dev = urb->context;
+
+ /* sync/async unlink faults aren't errors */
+ if (urb->status && !(urb->status == -ENOENT ||
+ urb->status == -ECONNRESET ||
+ urb->status == -ESHUTDOWN))
+ dbg_info(&dev->intf->dev,
+ "%s - nonzero write interrupt status received: %d\n",
+ __FUNCTION__, urb->status);
+
+ dev->interrupt_out_busy = 0;
+ wake_up_interruptible(&dev->write_wait);
+}
+
+/**
+ * usb_tranzport_open
+ */
+static int usb_tranzport_open(struct inode *inode, struct file *file)
+{
+ struct usb_tranzport *dev;
+ int subminor;
+ int retval = 0;
+ struct usb_interface *interface;
+
+ nonseekable_open(inode, file);
+ subminor = iminor(inode);
+
+ mutex_lock(&disconnect_mutex);
+
+ interface = usb_find_interface(&usb_tranzport_driver, subminor);
+
+ if (!interface) {
+ err("%s - error, can't find device for minor %d\n",
+ __FUNCTION__, subminor);
+ retval = -ENODEV;
+ goto unlock_disconnect_exit;
+ }
+
+ dev = usb_get_intfdata(interface);
+
+ if (!dev) {
+ retval = -ENODEV;
+ goto unlock_disconnect_exit;
+ }
+
+ /* lock this device */
+ if (down_interruptible(&dev->sem)) {
+ retval = -ERESTARTSYS;
+ goto unlock_disconnect_exit;
+ }
+
+ /* allow opening only once */
+ if (dev->open_count) {
+ retval = -EBUSY;
+ goto unlock_exit;
+ }
+ dev->open_count = 1;
+
+ /* initialize in direction */
+ dev->ring_head = 0;
+ dev->ring_tail = 0;
+ usb_fill_int_urb(dev->interrupt_in_urb,
+ interface_to_usbdev(interface),
+ usb_rcvintpipe(interface_to_usbdev(interface),
+ dev->interrupt_in_endpoint->bEndpointAddress),
+ dev->interrupt_in_buffer,
+ dev->interrupt_in_endpoint_size,
+ usb_tranzport_interrupt_in_callback,
+ dev,
+ dev->interrupt_in_interval);
+
+ dev->interrupt_in_running = 1;
+ dev->interrupt_in_done = 0;
+ dev->enable = 1;
+ dev->offline = 0;
+ dev->compress_wheel = 1;
+
+ retval = usb_submit_urb(dev->interrupt_in_urb, GFP_KERNEL);
+ if (retval) {
+ dev_err(&interface->dev, "Couldn't submit interrupt_in_urb %d\n", retval);
+ dev->interrupt_in_running = 0;
+ dev->open_count = 0;
+ goto unlock_exit;
+ }
+
+ /* save device in the file's private structure */
+ file->private_data = dev;
+
+
+unlock_exit:
+ up(&dev->sem);
+
+unlock_disconnect_exit:
+ mutex_unlock(&disconnect_mutex);
+
+ return retval;
+}
+
+/**
+ * usb_tranzport_release
+ */
+static int usb_tranzport_release(struct inode *inode, struct file *file)
+{
+ struct usb_tranzport *dev;
+ int retval = 0;
+
+ dev = file->private_data;
+
+ if (dev == NULL) {
+ retval = -ENODEV;
+ goto exit;
+ }
+
+ if (down_interruptible(&dev->sem)) {
+ retval = -ERESTARTSYS;
+ goto exit;
+ }
+
+ if (dev->open_count != 1) {
+ retval = -ENODEV;
+ goto unlock_exit;
+ }
+
+ if (dev->intf == NULL) {
+ /* the device was unplugged before the file was released */
+ up(&dev->sem);
+ /* unlock here as usb_tranzport_delete frees dev */
+ usb_tranzport_delete(dev);
+ retval = -ENODEV;
+ goto exit;
+ }
+
+ /* wait until write transfer is finished */
+ if (dev->interrupt_out_busy)
+ wait_event_interruptible_timeout(dev->write_wait, !dev->interrupt_out_busy, 2 * HZ);
+ usb_tranzport_abort_transfers(dev);
+ dev->open_count = 0;
+
+unlock_exit:
+ up(&dev->sem);
+
+exit:
+ return retval;
+}
+
+/**
+ * usb_tranzport_poll
+ */
+static unsigned int usb_tranzport_poll(struct file *file, poll_table *wait)
+{
+ struct usb_tranzport *dev;
+ unsigned int mask = 0;
+
+ dev = file->private_data;
+
+ poll_wait(file, &dev->read_wait, wait);
+ poll_wait(file, &dev->write_wait, wait);
+
+ if (dev->ring_head != dev->ring_tail)
+ mask |= POLLIN | POLLRDNORM;
+ if (!dev->interrupt_out_busy)
+ mask |= POLLOUT | POLLWRNORM;
+
+ return mask;
+}
+
+/**
+ * usb_tranzport_read
+ */
+static ssize_t usb_tranzport_read(struct file *file, char __user *buffer, size_t count,
+ loff_t *ppos)
+{
+ struct usb_tranzport *dev;
+ size_t bytes_to_read;
+ int retval = 0;
+
+#if BUFFERED_READS
+ int c = 0;
+#endif
+
+#if COMPRESS_WHEEL_EVENTS
+ signed char oldwheel;
+ signed char newwheel;
+ int cancompress = 1;
+ int next_tail;
+#endif
+
+/* do I have such a thing as a null event? */
+
+ dev = file->private_data;
+
+ /* verify that we actually have some data to read */
+ if (count == 0)
+ goto exit;
+
+ /* lock this object */
+ if (down_interruptible(&dev->sem)) {
+ retval = -ERESTARTSYS;
+ goto exit;
+ }
+
+ /* verify that the device wasn't unplugged */
+ if (dev->intf == NULL) {
+ retval = -ENODEV;
+ err("No device or device unplugged %d\n", retval);
+ goto unlock_exit;
+ }
+
+ while (dev->ring_head == dev->ring_tail) {
+
+ if (file->f_flags & O_NONBLOCK) {
+ retval = -EAGAIN;
+ goto unlock_exit;
+ }
+ // atomic_cmp_exchange(&dev->interrupt_in_done,0,0);
+ dev->interrupt_in_done = 0 ; /* tiny race - FIXME: make atomic? */
+ retval = wait_event_interruptible(dev->read_wait, dev->interrupt_in_done);
+ if (retval < 0) {
+ goto unlock_exit;
+ }
+ }
+
+ dbg_info(&dev->intf->dev, "%s: copying to userspace: %02x%02x%02x%02x%02x%02x%02x%02x\n",
+ __FUNCTION__, (*dev->ring_buffer)[dev->ring_tail].cmd[0],(*dev->ring_buffer)[dev->ring_tail].cmd[1],(*dev->ring_buffer)[dev->ring_tail].cmd[2],(*dev->ring_buffer)[dev->ring_tail].cmd[3],(*dev->ring_buffer)[dev->ring_tail].cmd[4],(*dev->ring_buffer)[dev->ring_tail].cmd[5],(*dev->ring_buffer)[dev->ring_tail].cmd[6],(*dev->ring_buffer)[dev->ring_tail].cmd[7]);
+
+#if BUFFERED_READS
+ c = 0;
+ while((c < count) && (dev->ring_tail != dev->ring_head)) {
+
+/* This started off in the lower level service routine, and I moved it here. Then my brain died. Not done yet. */
+#if COMPRESS_WHEEL_EVENTS
+ next_tail = (dev->ring_tail+1) % ring_buffer_size;
+ if(dev->compress_wheel) cancompress = 1;
+ while(dev->ring_head != next_tail && cancompress == 1 ) {
+ newwheel = (*dev->ring_buffer)[next_tail].cmd[6];
+ oldwheel = (*dev->ring_buffer)[dev->ring_tail].cmd[6];
+ // if both are wheel events, and no buttons have changes (FIXME, do I have to check?),
+ // and we are the same sign, we can compress +- 7F
+ // FIXME: saner check for overflow! - max of +- 7F
+ // FIXME the math is wrong for going in reverse, actually, as the midi spec doesn't allow signed chars
+
+ dbg_info(&dev->intf->dev, "%s: trying to compress: %02x%02x%02x%02x%02x %02x %02x %02x\n",
+ __FUNCTION__, (*dev->ring_buffer)[dev->ring_tail].cmd[0],(*dev->ring_buffer)[dev->ring_tail].cmd[1],(*dev->ring_buffer)[dev->ring_tail].cmd[2],(*dev->ring_buffer)[dev->ring_tail].cmd[3],(*dev->ring_buffer)[dev->ring_tail].cmd[4],(*dev->ring_buffer)[dev->ring_tail].cmd[5],(*dev->ring_buffer)[dev->ring_tail].cmd[6],(*dev->ring_buffer)[dev->ring_tail].cmd[7]);
+
+
+ if(((*dev->ring_buffer)[dev->ring_tail].cmd[6] != 0 &&
+ (*dev->ring_buffer)[next_tail].cmd[6] != 0 ) &&
+ ((newwheel > 0 && oldwheel > 0) ||
+ (newwheel < 0 && oldwheel < 0)) &&
+ ((*dev->ring_buffer)[dev->ring_tail].cmd[2] == (*dev->ring_buffer)[next_tail].cmd[2]) &&
+ ((*dev->ring_buffer)[dev->ring_tail].cmd[3] == (*dev->ring_buffer)[next_tail].cmd[3]) &&
+ ((*dev->ring_buffer)[dev->ring_tail].cmd[4] == (*dev->ring_buffer)[next_tail].cmd[4]) &&
+ ((*dev->ring_buffer)[dev->ring_tail].cmd[5] == (*dev->ring_buffer)[next_tail].cmd[5]))
+ {
+ dbg_info(&dev->intf->dev, "%s: should compress: %02x%02x%02x%02x%02x%02x%02x%02x\n",
+ __FUNCTION__, (*dev->ring_buffer)[dev->ring_tail].cmd[0],(*dev->ring_buffer)[dev->ring_tail].cmd[1],(*dev->ring_buffer)[dev->ring_tail].cmd[2],(*dev->ring_buffer)[dev->ring_tail].cmd[3],(*dev->ring_buffer)[dev->ring_tail].cmd[4],(*dev->ring_buffer)[dev->ring_tail].cmd[5],(*dev->ring_buffer)[dev->ring_tail].cmd[6],(*dev->ring_buffer)[dev->ring_tail].cmd[7]);
+
+ newwheel += oldwheel;
+ if(oldwheel > 0 && !(newwheel > 0)) {
+ newwheel = 0x7f;
+ cancompress = 0;
+ }
+ if(oldwheel < 0 && !(newwheel < 0)) {
+ newwheel = 0x80;
+ cancompress = 0;
+ }
+
+ (*dev->ring_buffer)[next_tail].cmd[6] = newwheel;
+ dev->ring_tail = next_tail;
+ next_tail = (dev->ring_tail+1) % ring_buffer_size;
+ } else {
+ cancompress = 0;
+ }
+ }
+#endif /* COMPRESS_WHEEL_EVENTS */
+
+ if (copy_to_user(&buffer[c], &(*dev->ring_buffer)[dev->ring_tail], 8)) {
+ retval = -EFAULT;
+ goto unlock_exit;
+ }
+
+ dev->ring_tail = (dev->ring_tail+1) % ring_buffer_size;
+ c+=8;
+ dbg_info(&dev->intf->dev, "%s: head, tail are %x, %x\n", __FUNCTION__,dev->ring_head,dev->ring_tail);
+ }
+ retval = c;
+
+#else
+ if (copy_to_user(buffer, &(*dev->ring_buffer)[dev->ring_tail], 8)) {
+ retval = -EFAULT;
+ goto unlock_exit;
+ }
+
+ dev->ring_tail = (dev->ring_tail+1) % ring_buffer_size;
+ dbg_info(&dev->intf->dev, "%s: head, tail are %x, %x\n", __FUNCTION__,dev->ring_head,dev->ring_tail);
+
+ retval = 8;
+#endif /* BUFFERED_READS */
+
+unlock_exit:
+ /* unlock the device */
+ up(&dev->sem);
+
+exit:
+ return retval;
+}
+
+/**
+ * usb_tranzport_write
+ */
+static ssize_t usb_tranzport_write(struct file *file, const char __user *buffer,
+ size_t count, loff_t *ppos)
+{
+ struct usb_tranzport *dev;
+ size_t bytes_to_write;
+ int retval = 0;
+
+ dev = file->private_data;
+
+ /* verify that we actually have some data to write */
+ if (count == 0)
+ goto exit;
+
+ /* lock this object */
+ if (down_interruptible(&dev->sem)) {
+ retval = -ERESTARTSYS;
+ goto exit;
+ }
+
+ /* verify that the device wasn't unplugged */
+ if (dev->intf == NULL) {
+ retval = -ENODEV;
+ err("No device or device unplugged %d\n", retval);
+ goto unlock_exit;
+ }
+
+ /* wait until previous transfer is finished */
+ if (dev->interrupt_out_busy) {
+ if (file->f_flags & O_NONBLOCK) {
+ retval = -EAGAIN;
+ goto unlock_exit;
+ }
+ retval = wait_event_interruptible(dev->write_wait, !dev->interrupt_out_busy);
+ if (retval < 0) {
+ goto unlock_exit;
+ }
+ }
+
+ /* write the data into interrupt_out_buffer from userspace */
+ bytes_to_write = min(count, write_buffer_size*dev->interrupt_out_endpoint_size);
+ if (bytes_to_write < count)
+ dev_warn(&dev->intf->dev, "Write buffer overflow, %zd bytes dropped\n",count-bytes_to_write);
+
+ dbg_info(&dev->intf->dev, "%s: count = %zd, bytes_to_write = %zd\n", __FUNCTION__, count, bytes_to_write);
+
+ if (copy_from_user(dev->interrupt_out_buffer, buffer, bytes_to_write)) {
+ retval = -EFAULT;
+ goto unlock_exit;
+ }
+
+ if (dev->interrupt_out_endpoint == NULL) {
+ err("Endpoint should not be be null! \n");
+ goto unlock_exit;
+ }
+
+ /* send off the urb */
+ usb_fill_int_urb(dev->interrupt_out_urb,
+ interface_to_usbdev(dev->intf),
+ usb_sndintpipe(interface_to_usbdev(dev->intf),
+ dev->interrupt_out_endpoint->bEndpointAddress),
+ dev->interrupt_out_buffer,
+ bytes_to_write,
+ usb_tranzport_interrupt_out_callback,
+ dev,
+ dev->interrupt_out_interval);
+
+ dev->interrupt_out_busy = 1;
+ wmb();
+
+ retval = usb_submit_urb(dev->interrupt_out_urb, GFP_KERNEL);
+ if (retval) {
+ dev->interrupt_out_busy = 0;
+ err("Couldn't submit interrupt_out_urb %d\n", retval);
+ goto unlock_exit;
+ }
+ retval = bytes_to_write;
+
+unlock_exit:
+ /* unlock the device */
+ up(&dev->sem);
+
+exit:
+ return retval;
+}
+
+/* file operations needed when we register this driver */
+static const struct file_operations usb_tranzport_fops = {
+ .owner = THIS_MODULE,
+ .read = usb_tranzport_read,
+ .write = usb_tranzport_write,
+ .open = usb_tranzport_open,
+ .release = usb_tranzport_release,
+ .poll = usb_tranzport_poll,
+};
+
+/*
+ * usb class driver info in order to get a minor number from the usb core,
+ * and to have the device registered with the driver core
+ */
+static struct usb_class_driver usb_tranzport_class = {
+ .name = "tranzport%d",
+ .fops = &usb_tranzport_fops,
+ .minor_base = USB_TRANZPORT_MINOR_BASE,
+};
+
+
+/**
+ * usb_tranzport_probe
+ *
+ * Called by the usb core when a new device is connected that it thinks
+ * this driver might be interested in.
+ */
+static int usb_tranzport_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+ struct usb_device *udev = interface_to_usbdev(intf);
+ struct usb_tranzport *dev = NULL;
+ struct usb_host_interface *iface_desc;
+ struct usb_endpoint_descriptor *endpoint;
+ int i;
+ int true_size;
+ int retval = -ENOMEM;
+
+ /* allocate memory for our device state and intialize it */
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (dev == NULL) {
+ dev_err(&intf->dev, "Out of memory\n");
+ goto exit;
+ }
+ init_MUTEX(&dev->sem);
+ dev->intf = intf;
+ init_waitqueue_head(&dev->read_wait);
+ init_waitqueue_head(&dev->write_wait);
+
+ iface_desc = intf->cur_altsetting;
+
+ /* set up the endpoint information */
+ for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
+ endpoint = &iface_desc->endpoint[i].desc;
+
+ if (usb_endpoint_is_int_in(endpoint))
+ dev->interrupt_in_endpoint = endpoint;
+
+ if (usb_endpoint_is_int_out(endpoint))
+ dev->interrupt_out_endpoint = endpoint;
+ }
+ if (dev->interrupt_in_endpoint == NULL) {
+ dev_err(&intf->dev, "Interrupt in endpoint not found\n");
+ goto error;
+ }
+ if (dev->interrupt_out_endpoint == NULL)
+ dev_warn(&intf->dev, "Interrupt out endpoint not found (using control endpoint instead)\n");
+
+
+ dev->interrupt_in_endpoint_size = le16_to_cpu(dev->interrupt_in_endpoint->wMaxPacketSize);
+
+ if (dev->interrupt_in_endpoint_size != 8)
+ dev_warn(&intf->dev, "Interrupt in endpoint size is not 8!\n");
+
+ if(ring_buffer_size == 0) { ring_buffer_size = RING_BUFFER_SIZE; }
+ true_size = min(ring_buffer_size,RING_BUFFER_SIZE);
+ /* FIXME - there are more usb_alloc routines for dma correctness. Needed? */
+
+ dev->ring_buffer = kmalloc((true_size*sizeof(struct tranzport_cmd))+8, GFP_KERNEL);
+
+ if (!dev->ring_buffer) {
+ dev_err(&intf->dev, "Couldn't allocate ring_buffer of size %d\n",true_size);
+ goto error;
+ }
+ dev->interrupt_in_buffer = kmalloc(dev->interrupt_in_endpoint_size, GFP_KERNEL);
+ if (!dev->interrupt_in_buffer) {
+ dev_err(&intf->dev, "Couldn't allocate interrupt_in_buffer\n");
+ goto error;
+ }
+ dev->interrupt_in_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!dev->interrupt_in_urb) {
+ dev_err(&intf->dev, "Couldn't allocate interrupt_in_urb\n");
+ goto error;
+ }
+ dev->interrupt_out_endpoint_size = dev->interrupt_out_endpoint ? le16_to_cpu(dev->interrupt_out_endpoint->wMaxPacketSize) :
+ udev->descriptor.bMaxPacketSize0;
+
+ if (dev->interrupt_out_endpoint_size !=8)
+ dev_warn(&intf->dev, "Interrupt out endpoint size is not 8!)\n");
+
+ dev->interrupt_out_buffer = kmalloc(write_buffer_size*dev->interrupt_out_endpoint_size, GFP_KERNEL);
+ if (!dev->interrupt_out_buffer) {
+ dev_err(&intf->dev, "Couldn't allocate interrupt_out_buffer\n");
+ goto error;
+ }
+ dev->interrupt_out_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!dev->interrupt_out_urb) {
+ dev_err(&intf->dev, "Couldn't allocate interrupt_out_urb\n");
+ goto error;
+ }
+ dev->interrupt_in_interval = min_interrupt_in_interval > dev->interrupt_in_endpoint->bInterval ? min_interrupt_in_interval : dev->interrupt_in_endpoint->bInterval;
+ if (dev->interrupt_out_endpoint)
+ dev->interrupt_out_interval = min_interrupt_out_interval > dev->interrupt_out_endpoint->bInterval ? min_interrupt_out_interval : dev->interrupt_out_endpoint->bInterval;
+
+ /* we can register the device now, as it is ready */
+ usb_set_intfdata(intf, dev);
+
+ retval = usb_register_dev(intf, &usb_tranzport_class);
+ if (retval) {
+ /* something prevented us from registering this driver */
+ dev_err(&intf->dev, "Not able to get a minor for this device.\n");
+ usb_set_intfdata(intf, NULL);
+ goto error;
+ }
+
+ if((retval = device_create_file(&intf->dev, &dev_attr_LightRecord))) goto error;
+ if((retval = device_create_file(&intf->dev, &dev_attr_LightTrackrec))) goto error;
+ if((retval = device_create_file(&intf->dev, &dev_attr_LightTrackmute))) goto error;
+ if((retval = device_create_file(&intf->dev, &dev_attr_LightTracksolo))) goto error;
+ if((retval = device_create_file(&intf->dev, &dev_attr_LightAnysolo))) goto error;
+ if((retval = device_create_file(&intf->dev, &dev_attr_LightLoop))) goto error;
+ if((retval = device_create_file(&intf->dev, &dev_attr_LightPunch))) goto error;
+ if((retval = device_create_file(&intf->dev, &dev_attr_wheel))) goto error;
+ if((retval = device_create_file(&intf->dev, &dev_attr_event))) goto error;
+ if((retval = device_create_file(&intf->dev, &dev_attr_dump_state))) goto error;
+ if((retval = device_create_file(&intf->dev, &dev_attr_compress_wheel))) goto error;
+ if((retval = device_create_file(&intf->dev, &dev_attr_enable))) goto error;
+ if((retval = device_create_file(&intf->dev, &dev_attr_offline))) goto error;
+
+ /* let the user know what node this device is now attached to */
+ dev_info(&intf->dev, "Tranzport Device #%d now attached to major %d minor %d\n",
+ (intf->minor - USB_TRANZPORT_MINOR_BASE), USB_MAJOR, intf->minor);
+
+exit:
+ return retval;
+
+error:
+ usb_tranzport_delete(dev);
+
+ return retval;
+}
+
+/**
+ * usb_tranzport_disconnect
+ *
+ * Called by the usb core when the device is removed from the system.
+ */
+static void usb_tranzport_disconnect(struct usb_interface *intf)
+{
+ struct usb_tranzport *dev;
+ int minor;
+
+ /* FIXME: The skel code calls lock_kernel here, doesn't use a mutex, needed? */
+ mutex_lock(&disconnect_mutex);
+
+ dev = usb_get_intfdata(intf);
+ usb_set_intfdata(intf, NULL);
+
+ down(&dev->sem);
+
+ minor = intf->minor;
+
+ /* give back our minor */
+ usb_deregister_dev(intf, &usb_tranzport_class);
+
+ /* if the device is not opened, then we clean up right now */
+ if (!dev->open_count) {
+ up(&dev->sem);
+ usb_tranzport_delete(dev);
+ } else {
+ dev->intf = NULL;
+ up(&dev->sem);
+ }
+
+ mutex_unlock(&disconnect_mutex);
+
+ dev_info(&intf->dev, "Tranzport Surface #%d now disconnected\n",
+ (minor - USB_TRANZPORT_MINOR_BASE));
+}
+
+/* usb specific object needed to register this driver with the usb subsystem */
+static struct usb_driver usb_tranzport_driver = {
+ .name = "tranzport",
+ .probe = usb_tranzport_probe,
+ .disconnect = usb_tranzport_disconnect,
+ .id_table = usb_tranzport_table,
+};
+
+/**
+ * usb_tranzport_init
+ */
+static int __init usb_tranzport_init(void)
+{
+ int retval;
+
+ /* register this driver with the USB subsystem */
+ retval = usb_register(&usb_tranzport_driver);
+ if (retval)
+ err("usb_register failed for the "__FILE__" driver. Error number %d\n", retval);
+
+ return retval;
+}
+
+/**
+ * usb_tranzport_exit
+ */
+static void __exit usb_tranzport_exit(void)
+{
+ /* deregister this driver with the USB subsystem */
+ usb_deregister(&usb_tranzport_driver);
+}
+
+module_init(usb_tranzport_init);
+module_exit(usb_tranzport_exit);
+
diff --git a/libs/surfaces/frontier/tests/Makefile b/libs/surfaces/frontier/tests/Makefile
new file mode 100644
index 0000000000..aafb9aaa57
--- /dev/null
+++ b/libs/surfaces/frontier/tests/Makefile
@@ -0,0 +1,17 @@
+# Some basic utilities for testing the tranzport's I/O
+# eventually "tranzport" will become a flexible command
+#
+#
+all: tranzport tranzport_lights
+
+tranzport: tranzport.c
+ gcc -g -Wall -o tranzport tranzport.c
+
+tranzport_lights: tranzport_lights.c
+ gcc -g -Wall -o tranzport_lights tranzport_lights.c
+
+clean::
+ rm -f core .*.cmd *.o *.ko *.mod.c Module.symvers *.bak .\#* *~
+ rm -rf .tmp_versions tranzport tranzport_lights
+
+
diff --git a/libs/surfaces/frontier/tests/README b/libs/surfaces/frontier/tests/README
new file mode 100644
index 0000000000..f9efd18f69
--- /dev/null
+++ b/libs/surfaces/frontier/tests/README
@@ -0,0 +1,104 @@
+tranzport 0.1 <tranzport.sf.net>
+oct 18, 2005
+arthur@artcmusic.com
+---
+
+The Frontier Design Tranzport(tm) (www.frontierdesign.com) is a simple
+wireless USB device. It is not a MIDI device. The document on their web
+site "Tranzport(tm) Native Mode Interface Description" describes the
+Tranzport(tm) as if it were a MIDI device, but this is implemented by their
+Windows and Macintosh software drivers.
+
+This code will allow you to use your Tranzport(tm) at a lower level of
+abstraction. This code relies on libusb, which can be obtained from
+libusb.sourceforge.net.
+
+To compile the program, type "make". You should end up with a executable
+called "tranzport". You'll probably have to run this program as root.
+
+Using the program is straightforward. It will simply tell you which
+buttons are being pressed and what not. If you press one of the buttons
+with a light, the light will turn on. If you hold shift and press one of
+the buttons with a light, the light will turn off. If you take out the
+batteries to the device (or go out of range), it will tell you that the
+device is offline. When you replace the batteries (or come back in
+range), it should tell you it is back online.
+
+Once you understand how everything works, you should be able to
+incorporate it into your own setup however you wish.
+
+This code was developed on a Linux machine, but (theoretically) it
+should work on any system that is supported by libusb, since that is how
+it communicates with the device.
+
+Here are a few more details about the device:
+
+There are two endpoints for communication with the device. All data
+reads and writes are done in 8-byte segments.
+
+One endpoint is for interrupt reads. This is used to read button data
+from the device. It also supplies status information for when the device
+goes out of range and comes back in range, loses power and regains
+power, etc. The format of the data is:
+
+ 00 ss bb bb bb bb dd 00 (hexadecimal)
+
+where:
+
+ ss - status code, 01=online ff=offline
+ bb - button bits
+ dd - data wheel, 01-3f=forward 41-7f=backward
+
+Please refer to the source code for a list of the button bits.
+
+The other endpoint is for interrupt writes. This is used to toggle the
+lights on the device, and to write data to the LCD.
+
+There are 7 lights on the device. To turn a light on, send the following
+sequence of bytes:
+
+ 00 00 nn 01 00 00 00 00 (hexadecimal)
+
+where nn is the light number.
+
+To turn a light off:
+
+ 00 00 nn 00 00 00 00 00 (hexadecimal)
+
+Here is the list of lights:
+
+ 00 Record
+ 01 Track Rec
+ 02 Track Mute
+ 03 Track Solo
+ 04 Any Solo
+ 05 Loop
+ 06 Punch
+
+The size of the LCD is 20x2, and it is split into 10 cells, each cell
+being 4 characters wide. The cells progress across, then down. To write
+to the LCD, send the following sequence of bytes:
+
+ 00 01 cc aa aa aa aa 00 (hexadecimal)
+
+where:
+
+ cc - cell number
+ aa - ASCII code
+
+Here is a list of the cells to clarify:
+
+ 00 row 0, column 0-3
+ 01 row 0, column 4-7
+ 02 row 0, column 8-11
+ 03 row 0, column 12-15
+ 04 row 0, column 16-19
+ 05 row 1, column 0-3
+ 06 row 1, column 4-7
+ 07 row 1, column 8-11
+ 08 row 1, column 12-15
+ 09 row 1, column 16-19
+
+You should refer to the "Tranzport(tm) Native Mode Interface
+Description" document for a listing of the ASCII codes the LCD uses.
+
diff --git a/libs/surfaces/frontier/tests/tranzport.c b/libs/surfaces/frontier/tests/tranzport.c
new file mode 100644
index 0000000000..1eeacd6578
--- /dev/null
+++ b/libs/surfaces/frontier/tests/tranzport.c
@@ -0,0 +1,347 @@
+/*
+ * tranzport 0.1 <tranzport.sf.net>
+ * oct 18, 2005
+ * arthur@artcmusic.com
+ */
+
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <usb.h>
+
+#define VENDORID 0x165b
+#define PRODUCTID 0x8101
+
+#define READ_ENDPOINT 0x81
+#define WRITE_ENDPOINT 0x02
+
+enum {
+ LIGHT_RECORD = 0,
+ LIGHT_TRACKREC,
+ LIGHT_TRACKMUTE,
+ LIGHT_TRACKSOLO,
+ LIGHT_ANYSOLO,
+ LIGHT_LOOP,
+ LIGHT_PUNCH
+};
+
+#define BUTTONMASK_BATTERY 0x00004000
+#define BUTTONMASK_BACKLIGHT 0x00008000
+#define BUTTONMASK_TRACKLEFT 0x04000000
+#define BUTTONMASK_TRACKRIGHT 0x40000000
+#define BUTTONMASK_TRACKREC 0x00040000
+#define BUTTONMASK_TRACKMUTE 0x00400000
+#define BUTTONMASK_TRACKSOLO 0x00000400
+#define BUTTONMASK_UNDO 0x80000000
+#define BUTTONMASK_IN 0x02000000
+#define BUTTONMASK_OUT 0x20000000
+#define BUTTONMASK_PUNCH 0x00800000
+#define BUTTONMASK_LOOP 0x00080000
+#define BUTTONMASK_PREV 0x00020000
+#define BUTTONMASK_ADD 0x00200000
+#define BUTTONMASK_NEXT 0x00000200
+#define BUTTONMASK_REWIND 0x01000000
+#define BUTTONMASK_FASTFORWARD 0x10000000
+#define BUTTONMASK_STOP 0x00010000
+#define BUTTONMASK_PLAY 0x00100000
+#define BUTTONMASK_RECORD 0x00000100
+#define BUTTONMASK_SHIFT 0x08000000
+
+#define STATUS_OFFLINE 0xff
+#define STATUS_ONLINE 0x01
+
+struct tranzport_s {
+ struct usb_device *dev;
+ usb_dev_handle *udev;
+};
+
+typedef struct tranzport_s tranzport_t;
+
+void log_entry(FILE *fp, char *format, va_list ap)
+{
+ vfprintf(fp, format, ap);
+ fputc('\n', fp);
+}
+
+void log_error(char *format, ...)
+{
+ va_list ap;
+ va_start(ap, format);
+ log_entry(stderr, format, ap);
+ va_end(ap);
+}
+
+void vlog_error(char *format, va_list ap)
+{
+ log_entry(stderr, format, ap);
+}
+
+void die(char *format, ...)
+{
+ va_list ap;
+ va_start(ap, format);
+ vlog_error(format, ap);
+ va_end(ap);
+ exit(1);
+}
+
+tranzport_t *open_tranzport_core(struct usb_device *dev)
+{
+ tranzport_t *z;
+ int val;
+
+ z = malloc(sizeof(tranzport_t));
+ if (!z)
+ die("not enough memory");
+ memset(z, 0, sizeof(tranzport_t));
+
+ z->dev = dev;
+ z->udev = usb_open(z->dev);
+ if (!z->udev)
+ die("unable to open tranzport");
+
+ val = usb_claim_interface(z->udev, 0);
+ if (val < 0)
+ die("unable to claim tranzport");
+
+ return z;
+}
+
+tranzport_t *open_tranzport()
+{
+ struct usb_bus *bus;
+ struct usb_device *dev;
+
+ usb_init();
+ usb_find_busses();
+ usb_find_devices();
+
+ for(bus=usb_busses; bus; bus=bus->next) {
+ for(dev=bus->devices; dev; dev=dev->next) {
+ if (dev->descriptor.idVendor != VENDORID)
+ continue;
+ if (dev->descriptor.idProduct != PRODUCTID)
+ continue;
+
+ return open_tranzport_core(dev);
+ }
+ }
+
+ die("can't find tranzport");
+ return 0;
+}
+
+void close_tranzport(tranzport_t *z)
+{
+ int val;
+
+ val = usb_release_interface(z->udev, 0);
+ if (val < 0)
+ log_error("unable to release tranzport");
+
+ val = usb_close(z->udev);
+ if (val < 0)
+ log_error("unable to close tranzport");
+
+ free(z);
+}
+
+int tranzport_write_core(tranzport_t *z, uint8_t *cmd, int timeout)
+{
+ int val;
+ val = usb_interrupt_write(z->udev, WRITE_ENDPOINT, cmd, 8, timeout);
+ if (val < 0)
+ return val;
+ if (val != 8)
+ return -1;
+ return 0;
+}
+
+int tranzport_lcdwrite(tranzport_t *z, uint8_t cell, char *text, int timeout)
+{
+ uint8_t cmd[8];
+
+ if (cell > 9) {
+ return -1;
+ }
+
+ cmd[0] = 0x00;
+ cmd[1] = 0x01;
+ cmd[2] = cell;
+ cmd[3] = text[0];
+ cmd[4] = text[1];
+ cmd[5] = text[2];
+ cmd[6] = text[3];
+ cmd[7] = 0x00;
+
+ return tranzport_write_core(z, cmd, timeout);
+}
+
+int tranzport_lighton(tranzport_t *z, uint8_t light, int timeout)
+{
+ uint8_t cmd[8];
+
+ cmd[0] = 0x00;
+ cmd[1] = 0x00;
+ cmd[2] = light;
+ cmd[3] = 0x01;
+ cmd[4] = 0x00;
+ cmd[5] = 0x00;
+ cmd[6] = 0x00;
+ cmd[7] = 0x00;
+
+ return tranzport_write_core(z, cmd, timeout);
+}
+
+int tranzport_lightoff(tranzport_t *z, uint8_t light, int timeout)
+{
+ uint8_t cmd[8];
+
+ cmd[0] = 0x00;
+ cmd[1] = 0x00;
+ cmd[2] = light;
+ cmd[3] = 0x00;
+ cmd[4] = 0x00;
+ cmd[5] = 0x00;
+ cmd[6] = 0x00;
+ cmd[7] = 0x00;
+
+ return tranzport_write_core(z, cmd, timeout);
+}
+
+int tranzport_read(tranzport_t *z, uint8_t *status, uint32_t *buttons, uint8_t *datawheel, int timeout)
+{
+ uint8_t buf[8];
+ int val;
+
+ memset(buf, 0, 8);
+ val = usb_interrupt_read(z->udev, READ_ENDPOINT, buf, 8, timeout);
+ if (val < 0)
+ return val;
+ if (val != 8)
+ return -1;
+
+ /*printf("read: %02x %02x %02x %02x %02x %02x %02x %02x\n", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]);*/
+
+ *status = buf[1];
+
+ *buttons = 0;
+ *buttons |= buf[2] << 24;
+ *buttons |= buf[3] << 16;
+ *buttons |= buf[4] << 8;
+ *buttons |= buf[5];
+
+ *datawheel = buf[6];
+
+ return 0;
+}
+
+void lights_core(tranzport_t *z, uint32_t buttons, uint32_t buttonmask, uint8_t light)
+{
+ if (buttons & buttonmask) {
+ if (buttons & BUTTONMASK_SHIFT) {
+ tranzport_lightoff(z, light, 1000);
+ } else {
+ tranzport_lighton(z, light, 1000);
+ }
+ }
+}
+
+void do_lights(tranzport_t *z, uint32_t buttons)
+{
+ lights_core(z, buttons, BUTTONMASK_RECORD, LIGHT_RECORD);
+ lights_core(z, buttons, BUTTONMASK_TRACKREC, LIGHT_TRACKREC);
+ lights_core(z, buttons, BUTTONMASK_TRACKMUTE, LIGHT_TRACKMUTE);
+ lights_core(z, buttons, BUTTONMASK_TRACKSOLO, LIGHT_TRACKSOLO);
+ lights_core(z, buttons, BUTTONMASK_TRACKSOLO, LIGHT_ANYSOLO);
+ lights_core(z, buttons, BUTTONMASK_PUNCH, LIGHT_PUNCH);
+ lights_core(z, buttons, BUTTONMASK_LOOP, LIGHT_LOOP);
+}
+
+void buttons_core(tranzport_t *z, uint32_t buttons, uint32_t buttonmask, char *str)
+{
+ if (buttons & buttonmask)
+ printf(" %s", str);
+}
+
+void do_buttons(tranzport_t *z, uint32_t buttons, uint8_t datawheel)
+{
+ printf("buttons:");
+ buttons_core(z, buttons, BUTTONMASK_BATTERY, "battery");
+ buttons_core(z, buttons, BUTTONMASK_BACKLIGHT, "backlight");
+ buttons_core(z, buttons, BUTTONMASK_TRACKLEFT, "trackleft");
+ buttons_core(z, buttons, BUTTONMASK_TRACKRIGHT, "trackright");
+ buttons_core(z, buttons, BUTTONMASK_TRACKREC, "trackrec");
+ buttons_core(z, buttons, BUTTONMASK_TRACKMUTE, "trackmute");
+ buttons_core(z, buttons, BUTTONMASK_TRACKSOLO, "tracksolo");
+ buttons_core(z, buttons, BUTTONMASK_UNDO, "undo");
+ buttons_core(z, buttons, BUTTONMASK_IN, "in");
+ buttons_core(z, buttons, BUTTONMASK_OUT, "out");
+ buttons_core(z, buttons, BUTTONMASK_PUNCH, "punch");
+ buttons_core(z, buttons, BUTTONMASK_LOOP, "loop");
+ buttons_core(z, buttons, BUTTONMASK_PREV, "prev");
+ buttons_core(z, buttons, BUTTONMASK_ADD, "add");
+ buttons_core(z, buttons, BUTTONMASK_NEXT, "next");
+ buttons_core(z, buttons, BUTTONMASK_REWIND, "rewind");
+ buttons_core(z, buttons, BUTTONMASK_FASTFORWARD, "fastforward");
+ buttons_core(z, buttons, BUTTONMASK_STOP, "stop");
+ buttons_core(z, buttons, BUTTONMASK_PLAY, "play");
+ buttons_core(z, buttons, BUTTONMASK_RECORD, "record");
+ buttons_core(z, buttons, BUTTONMASK_SHIFT, "shift");
+ if (datawheel)
+ printf(" datawheel=%02x", datawheel);
+ printf("\n");
+}
+
+void do_lcd(tranzport_t *z)
+{
+ tranzport_lcdwrite(z, 0, " ", 1000);
+ tranzport_lcdwrite(z, 1, "DISL", 1000);
+ tranzport_lcdwrite(z, 2, "EXIA", 1000);
+ tranzport_lcdwrite(z, 3, " FOR", 1000);
+ tranzport_lcdwrite(z, 4, " ", 1000);
+
+ tranzport_lcdwrite(z, 5, " ", 1000);
+ tranzport_lcdwrite(z, 6, " CUR", 1000);
+ tranzport_lcdwrite(z, 7, "E FO", 1000);
+ tranzport_lcdwrite(z, 8, "UND ", 1000);
+ tranzport_lcdwrite(z, 9, " ", 1000);
+}
+
+int main()
+{
+ tranzport_t *z;
+ uint8_t status;
+ uint32_t buttons;
+ uint8_t datawheel;
+ int val;
+
+ z = open_tranzport();
+
+ do_lcd(z);
+
+ for(;;) {
+ val = tranzport_read(z, &status, &buttons, &datawheel, 60000);
+ if (val < 0)
+ continue;
+
+ if (status == STATUS_OFFLINE) {
+ printf("offline\n");
+ continue;
+ }
+
+ if (status == STATUS_ONLINE) {
+ printf("online\n");
+ do_lcd(z);
+ }
+
+ do_lights(z, buttons);
+ do_buttons(z, buttons, datawheel);
+ }
+
+ close_tranzport(z);
+
+ return 0;
+}
+
diff --git a/libs/surfaces/frontier/tests/tranzport_lights.c b/libs/surfaces/frontier/tests/tranzport_lights.c
new file mode 100644
index 0000000000..28a8462d84
--- /dev/null
+++ b/libs/surfaces/frontier/tests/tranzport_lights.c
@@ -0,0 +1,361 @@
+/*
+ * tranzport 0.1 <tranzport.sf.net>
+ * oct 18, 2005
+ * arthur@artcmusic.com
+ */
+
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <malloc.h>
+
+#define VENDORID 0x165b
+#define PRODUCTID 0x8101
+
+#define READ_ENDPOINT 0x81
+#define WRITE_ENDPOINT 0x02
+
+enum {
+ LIGHT_RECORD = 0,
+ LIGHT_TRACKREC,
+ LIGHT_TRACKMUTE,
+ LIGHT_TRACKSOLO,
+ LIGHT_ANYSOLO,
+ LIGHT_LOOP,
+ LIGHT_PUNCH
+};
+
+#define BUTTONMASK_BATTERY 0x00004000
+#define BUTTONMASK_BACKLIGHT 0x00008000
+#define BUTTONMASK_TRACKLEFT 0x04000000
+#define BUTTONMASK_TRACKRIGHT 0x40000000
+#define BUTTONMASK_TRACKREC 0x00040000
+#define BUTTONMASK_TRACKMUTE 0x00400000
+#define BUTTONMASK_TRACKSOLO 0x00000400
+#define BUTTONMASK_UNDO 0x80000000
+#define BUTTONMASK_IN 0x02000000
+#define BUTTONMASK_OUT 0x20000000
+#define BUTTONMASK_PUNCH 0x00800000
+#define BUTTONMASK_LOOP 0x00080000
+#define BUTTONMASK_PREV 0x00020000
+#define BUTTONMASK_ADD 0x00200000
+#define BUTTONMASK_NEXT 0x00000200
+#define BUTTONMASK_REWIND 0x01000000
+#define BUTTONMASK_FASTFORWARD 0x10000000
+#define BUTTONMASK_STOP 0x00010000
+#define BUTTONMASK_PLAY 0x00100000
+#define BUTTONMASK_RECORD 0x00000100
+#define BUTTONMASK_SHIFT 0x08000000
+
+#define STATUS_OFFLINE 0xff
+#define STATUS_ONLINE 0x01
+#define STATUS_OK 0x00
+
+struct tranzport_s {
+ int *dev;
+ int udev;
+};
+
+typedef struct tranzport_s tranzport_t;
+
+void log_entry(FILE *fp, char *format, va_list ap)
+{
+ vfprintf(fp, format, ap);
+ fputc('\n', fp);
+}
+
+void log_error(char *format, ...)
+{
+ va_list ap;
+ va_start(ap, format);
+ log_entry(stderr, format, ap);
+ va_end(ap);
+}
+
+void vlog_error(char *format, va_list ap)
+{
+ log_entry(stderr, format, ap);
+}
+
+void die(char *format, ...)
+{
+ va_list ap;
+ va_start(ap, format);
+ vlog_error(format, ap);
+ va_end(ap);
+ exit(1);
+}
+
+tranzport_t *open_tranzport_core()
+{
+ tranzport_t *z;
+ int val;
+
+ z = malloc(sizeof(tranzport_t));
+ if (!z)
+ die("not enough memory");
+ memset(z, 0, sizeof(tranzport_t));
+
+ z->udev = open("/dev/tranzport0",O_RDWR);
+ if (!z->udev)
+ die("unable to open tranzport");
+
+ return z;
+}
+
+tranzport_t *open_tranzport()
+{
+return open_tranzport_core();
+}
+
+void close_tranzport(tranzport_t *z)
+{
+ int val;
+
+ val = close(z->udev);
+ if (val < 0)
+ log_error("unable to release tranzport");
+
+ free(z);
+}
+
+int tranzport_write_core(tranzport_t *z, uint8_t *cmd, int timeout)
+{
+ int val;
+ val = write(z->udev, cmd, 8);
+ if (val < 0)
+ return val;
+ if (val != 8)
+ return -1;
+ return 0;
+}
+
+int tranzport_lcdwrite(tranzport_t *z, uint8_t cell, char *text, int timeout)
+{
+ uint8_t cmd[8];
+
+ if (cell > 9) {
+ return -1;
+ }
+
+ cmd[0] = 0x00;
+ cmd[1] = 0x01;
+ cmd[2] = cell;
+ cmd[3] = text[0];
+ cmd[4] = text[1];
+ cmd[5] = text[2];
+ cmd[6] = text[3];
+ cmd[7] = 0x00;
+
+ return tranzport_write_core(z, cmd, timeout);
+}
+
+int tranzport_lighton(tranzport_t *z, uint8_t light, int timeout)
+{
+ uint8_t cmd[8];
+
+ cmd[0] = 0x00;
+ cmd[1] = 0x00;
+ cmd[2] = light;
+ cmd[3] = 0x01;
+ cmd[4] = 0x00;
+ cmd[5] = 0x00;
+ cmd[6] = 0x00;
+ cmd[7] = 0x00;
+
+ return tranzport_write_core(z, &cmd[0], timeout);
+}
+
+int tranzport_lightoff(tranzport_t *z, uint8_t light, int timeout)
+{
+ uint8_t cmd[8];
+
+ cmd[0] = 0x00;
+ cmd[1] = 0x00;
+ cmd[2] = light;
+ cmd[3] = 0x00;
+ cmd[4] = 0x00;
+ cmd[5] = 0x00;
+ cmd[6] = 0x00;
+ cmd[7] = 0x00;
+
+ return tranzport_write_core(z, &cmd[0], timeout);
+}
+
+int tranzport_read(tranzport_t *z, uint8_t *status, uint32_t *buttons, uint8_t *datawheel, int timeout)
+{
+ uint8_t buf[8];
+ int val;
+
+ memset(buf, 0xff, 8);
+ val = read(z->udev, buf, 8);
+ if (val < 0) {
+ // printf("errno: %d\n",errno);
+ return val;
+ }
+ if (val != 8)
+ return -1;
+
+ /*printf("read: %02x %02x %02x %02x %02x %02x %02x %02x\n", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]);*/
+
+ *status = buf[1];
+
+ *buttons = 0;
+ *buttons |= buf[2] << 24;
+ *buttons |= buf[3] << 16;
+ *buttons |= buf[4] << 8;
+ *buttons |= buf[5];
+
+ *datawheel = buf[6];
+
+ return 0;
+}
+
+void lights_core(tranzport_t *z, uint32_t buttons, uint32_t buttonmask, uint8_t light)
+{
+ if (buttons & buttonmask) {
+ if (buttons & BUTTONMASK_SHIFT) {
+ tranzport_lightoff(z, light, 1000);
+ } else {
+ tranzport_lighton(z, light, 1000);
+ }
+ }
+}
+
+void do_lights(tranzport_t *z, uint32_t buttons)
+{
+ lights_core(z, buttons, BUTTONMASK_RECORD, LIGHT_RECORD);
+ lights_core(z, buttons, BUTTONMASK_TRACKREC, LIGHT_TRACKREC);
+ lights_core(z, buttons, BUTTONMASK_TRACKMUTE, LIGHT_TRACKMUTE);
+ lights_core(z, buttons, BUTTONMASK_TRACKSOLO, LIGHT_TRACKSOLO);
+ lights_core(z, buttons, BUTTONMASK_TRACKSOLO, LIGHT_ANYSOLO);
+ lights_core(z, buttons, BUTTONMASK_PUNCH, LIGHT_PUNCH);
+ lights_core(z, buttons, BUTTONMASK_LOOP, LIGHT_LOOP);
+}
+
+void buttons_core(tranzport_t *z, uint32_t buttons, uint32_t buttonmask, char *str)
+{
+ if (buttons & buttonmask)
+ printf(" %s", str);
+}
+
+void do_buttons(tranzport_t *z, uint32_t buttons, uint8_t datawheel)
+{
+ printf("buttons: %x ", buttons);
+ buttons_core(z, buttons, BUTTONMASK_BATTERY, "battery");
+ buttons_core(z, buttons, BUTTONMASK_BACKLIGHT, "backlight");
+ buttons_core(z, buttons, BUTTONMASK_TRACKLEFT, "trackleft");
+ buttons_core(z, buttons, BUTTONMASK_TRACKRIGHT, "trackright");
+ buttons_core(z, buttons, BUTTONMASK_TRACKREC, "trackrec");
+ buttons_core(z, buttons, BUTTONMASK_TRACKMUTE, "trackmute");
+ buttons_core(z, buttons, BUTTONMASK_TRACKSOLO, "tracksolo");
+ buttons_core(z, buttons, BUTTONMASK_UNDO, "undo");
+ buttons_core(z, buttons, BUTTONMASK_IN, "in");
+ buttons_core(z, buttons, BUTTONMASK_OUT, "out");
+ buttons_core(z, buttons, BUTTONMASK_PUNCH, "punch");
+ buttons_core(z, buttons, BUTTONMASK_LOOP, "loop");
+ buttons_core(z, buttons, BUTTONMASK_PREV, "prev");
+ buttons_core(z, buttons, BUTTONMASK_ADD, "add");
+ buttons_core(z, buttons, BUTTONMASK_NEXT, "next");
+ buttons_core(z, buttons, BUTTONMASK_REWIND, "rewind");
+ buttons_core(z, buttons, BUTTONMASK_FASTFORWARD, "fastforward");
+ buttons_core(z, buttons, BUTTONMASK_STOP, "stop");
+ buttons_core(z, buttons, BUTTONMASK_PLAY, "play");
+ buttons_core(z, buttons, BUTTONMASK_RECORD, "record");
+ buttons_core(z, buttons, BUTTONMASK_SHIFT, "shift");
+ if (datawheel)
+ printf(" datawheel=%02x", datawheel);
+ printf("\n");
+}
+
+void do_lcd(tranzport_t *z)
+{
+ tranzport_lcdwrite(z, 0, " ", 1000);
+ tranzport_lcdwrite(z, 1, "DISL", 1000);
+ tranzport_lcdwrite(z, 2, "EXIA", 1000);
+ tranzport_lcdwrite(z, 3, " FOR", 1000);
+ tranzport_lcdwrite(z, 4, " ", 1000);
+
+ tranzport_lcdwrite(z, 5, " ", 1000);
+ tranzport_lcdwrite(z, 6, " CUR", 1000);
+ tranzport_lcdwrite(z, 7, "E FO", 1000);
+ tranzport_lcdwrite(z, 8, "UND ", 1000);
+ tranzport_lcdwrite(z, 9, " ", 1000);
+}
+
+void do_lcd2(tranzport_t *z)
+{
+ tranzport_lcdwrite(z, 0, "THE ", 1000);
+ tranzport_lcdwrite(z, 1, "TRAN", 1000);
+ tranzport_lcdwrite(z, 2, "ZPOR", 1000);
+ tranzport_lcdwrite(z, 3, "T RO", 1000);
+ tranzport_lcdwrite(z, 4, " KS", 1000);
+
+ tranzport_lcdwrite(z, 5, "AWES", 1000);
+ tranzport_lcdwrite(z, 6, "OMEE", 1000);
+ tranzport_lcdwrite(z, 7, "LEEE", 1000);
+ tranzport_lcdwrite(z, 8, "UND ", 1000);
+ tranzport_lcdwrite(z, 9, "GROK", 1000);
+}
+
+lights_off(tranzport_t *z) {
+int i;
+ for(i=0;i<7;i++) {
+ tranzport_lightoff(z, i, 1000);
+ }
+}
+
+lights_on(tranzport_t *z) {
+int i;
+ for(i=0;i<7;i++) {
+ tranzport_lighton(z, i, 1000);
+ }
+}
+
+int main()
+{
+ tranzport_t *z;
+ uint8_t status;
+ uint32_t buttons;
+ uint8_t datawheel;
+ int val;
+
+ z = open_tranzport();
+
+ do_lcd(z);
+
+ for(;;) {
+
+ do_lcd(z);
+ lights_on(z);
+ do_lcd2(z);
+ lights_off(z);
+
+// val = tranzport_read(z, &status, &buttons, &datawheel, 60000);
+ val = -1;
+ if (val < 0)
+ continue;
+
+ if (status == STATUS_OFFLINE) {
+ printf("offline: ");
+ continue;
+ }
+
+ if (status == STATUS_ONLINE) {
+ printf("online: ");
+ do_lcd(z);
+ }
+
+ do_lights(z, buttons);
+ do_buttons(z, buttons, datawheel);
+ }
+
+ close_tranzport(z);
+
+ return 0;
+}
+
diff --git a/libs/surfaces/frontier/tranzport/SConscript b/libs/surfaces/frontier/tranzport/SConscript
new file mode 100644
index 0000000000..5d390f3e2f
--- /dev/null
+++ b/libs/surfaces/frontier/tranzport/SConscript
@@ -0,0 +1,56 @@
+# -*- python -*-
+
+import os
+import os.path
+import glob
+
+Import('env final_prefix install_prefix final_config_prefix libraries i18n')
+
+tranzport = env.Copy()
+
+#
+# this defines the version number of libardour_tranzport
+#
+
+domain = 'ardour_tranzport'
+
+tranzport.Append(DOMAIN = domain, MAJOR = 1, MINOR = 0, MICRO = 0)
+tranzport.Append(CXXFLAGS = "-DPACKAGE=\\\"" + domain + "\\\"")
+tranzport.Append(CXXFLAGS="-DLIBSIGC_DISABLE_DEPRECATED")
+tranzport.Append(PACKAGE = domain)
+tranzport.Append(POTFILE = domain + '.pot')
+
+tranzport_files=Split("""
+interface.cc
+tranzport_control_protocol.cc
+""")
+
+tranzport.Append(CCFLAGS="-D_REENTRANT -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE")
+tranzport.Append(CXXFLAGS="-DDATA_DIR=\\\""+final_prefix+"/share\\\"")
+tranzport.Append(CXXFLAGS="-DCONFIG_DIR=\\\""+final_config_prefix+"\\\"")
+tranzport.Append(CXXFLAGS="-DLOCALEDIR=\\\""+final_prefix+"/share/locale\\\"")
+
+tranzport.Merge ([
+ libraries['ardour'],
+ libraries['ardour_cp'],
+ libraries['sigc2'],
+ libraries['pbd'],
+ libraries['midi++2'],
+ libraries['xml'],
+ libraries['usb'],
+ libraries['glib2'],
+ libraries['glibmm2']
+ ])
+
+libardour_tranzport = tranzport.SharedLibrary('ardour_tranzport', tranzport_files)
+
+if tranzport['TRANZPORT']:
+ Default(libardour_tranzport)
+ if env['NLS']:
+ i18n (tranzport, tranzport_files, env)
+ env.Alias('install', env.Install(os.path.join(install_prefix, env['LIBDIR'], 'ardour2', 'surfaces'), libardour_tranzport))
+
+env.Alias('tarball', env.Distribute (env['DISTTREE'],
+ [ 'SConscript' ] +
+ tranzport_files +
+ glob.glob('po/*.po') + glob.glob('*.h')))
diff --git a/libs/surfaces/frontier/tranzport/interface.cc b/libs/surfaces/frontier/tranzport/interface.cc
new file mode 100644
index 0000000000..f6d0dc8206
--- /dev/null
+++ b/libs/surfaces/frontier/tranzport/interface.cc
@@ -0,0 +1,51 @@
+#include <control_protocol/control_protocol.h>
+#include "tranzport_control_protocol.h"
+
+using namespace ARDOUR;
+
+ControlProtocol*
+new_tranzport_protocol (ControlProtocolDescriptor* descriptor, Session* s)
+{
+ TranzportControlProtocol* tcp = new TranzportControlProtocol (*s);
+
+ if (tcp->set_active (true)) {
+ delete tcp;
+ return 0;
+ }
+
+ return tcp;
+
+}
+
+void
+delete_tranzport_protocol (ControlProtocolDescriptor* descriptor, ControlProtocol* cp)
+{
+ delete cp;
+}
+
+bool
+probe_tranzport_protocol (ControlProtocolDescriptor* descriptor)
+{
+ return TranzportControlProtocol::probe();
+}
+
+static ControlProtocolDescriptor tranzport_descriptor = {
+ name : "Tranzport",
+ id : "uri://ardour.org/surfaces/tranzport:0",
+ ptr : 0,
+ module : 0,
+ mandatory : 0,
+ supports_feedback : false,
+ probe : probe_tranzport_protocol,
+ initialize : new_tranzport_protocol,
+ destroy : delete_tranzport_protocol
+};
+
+
+extern "C" {
+ControlProtocolDescriptor*
+protocol_descriptor () {
+ return &tranzport_descriptor;
+}
+}
+
diff --git a/libs/surfaces/frontier/tranzport/tranzport_control_protocol.cc b/libs/surfaces/frontier/tranzport/tranzport_control_protocol.cc
new file mode 100644
index 0000000000..b0c03fb71e
--- /dev/null
+++ b/libs/surfaces/frontier/tranzport/tranzport_control_protocol.cc
@@ -0,0 +1,1950 @@
+/*
+ Copyright (C) 2006 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ $Id: tranzport_control_protocol.cc 1252 2006-12-29 19:13:18Z sampo $
+*/
+
+/* Design notes: The tranzport is a unique device, basically a
+ 20 lcd gui with 22 shift keys and 8 blinking lights.
+
+ As such it has several unique constraints. The device exerts flow control
+ by having a usb write fail. It is pointless to retry madly at that point,
+ the device is busy, and it's not going to become unbusy very quickly.
+
+ So writes need to be either "mandatory" or "unreliable", and therein
+ lies the rub, as the kernel can also drop writes, and missing an
+ interrupt in userspace is also generally bad.
+
+ It will be good one day, to break the gui, keyboard, and blinking light
+ components into separate parts, but for now, this remains monolithic.
+
+ A more complex surface might have hundreds of lights and several displays.
+
+ mike.taht@gmail.com
+ */
+
+#define DEFAULT_USB_TIMEOUT 10
+#define MAX_RETRY 1
+#define MAX_TRANZPORT_INFLIGHT 4
+#define DEBUG_TRANZPORT 0
+#define HAVE_TRANZPORT_KERNEL_DRIVER 0
+
+#include <iostream>
+#include <algorithm>
+#include <cmath>
+
+#define __STDC_FORMAT_MACROS
+#include <inttypes.h>
+#include <float.h>
+#include <sys/time.h>
+#include <errno.h>
+
+#include <pbd/pthread_utils.h>
+
+#include <ardour/route.h>
+#include <ardour/audio_track.h>
+#include <ardour/session.h>
+#include <ardour/tempo.h>
+#include <ardour/location.h>
+#include <ardour/dB.h>
+
+#include "tranzport_control_protocol.h"
+
+using namespace ARDOUR;
+using namespace std;
+using namespace sigc;
+using namespace PBD;
+
+#include "i18n.h"
+
+#include <pbd/abstract_ui.cc>
+
+BaseUI::RequestType LEDChange = BaseUI::new_request_type ();
+BaseUI::RequestType Print = BaseUI::new_request_type ();
+BaseUI::RequestType SetCurrentTrack = BaseUI::new_request_type ();
+
+/* Base Tranzport cmd strings */
+
+static const uint8_t cmd_light_on[] = { 0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00 };
+static const uint8_t cmd_light_off[] = { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 };
+static const uint8_t cmd_write_screen[] = { 0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00 };
+
+static inline double
+gain_to_slider_position (ARDOUR::gain_t g)
+{
+ if (g == 0) return 0;
+ return pow((6.0*log(g)/log(2.0)+192.0)/198.0, 8.0);
+
+}
+
+static inline ARDOUR::gain_t
+slider_position_to_gain (double pos)
+{
+ /* XXX Marcus writes: this doesn't seem right to me. but i don't have a better answer ... */
+ if (pos == 0.0) return 0;
+ return pow (2.0,(sqrt(sqrt(sqrt(pos)))*198.0-192.0)/6.0);
+}
+
+
+TranzportControlProtocol::TranzportControlProtocol (Session& s)
+ : ControlProtocol (s, X_("Tranzport"))
+{
+ /* tranzport controls one track at a time */
+
+ set_route_table_size (1);
+ timeout = 6000; // what is this for?
+ buttonmask = 0;
+ _datawheel = 0;
+ _device_status = STATUS_OFFLINE;
+ udev = 0;
+ current_track_id = 0;
+ last_where = max_frames;
+ wheel_mode = WheelTimeline;
+ wheel_shift_mode = WheelShiftGain;
+ wheel_increment = WheelIncrScreen;
+ bling_mode = BlingOff;
+ timerclear (&last_wheel_motion);
+ last_wheel_dir = 1;
+ last_track_gain = FLT_MAX;
+ display_mode = DisplayNormal;
+ gain_fraction = 0.0;
+ invalidate();
+ screen_init();
+ lights_init();
+ print(0,0,"!!Welcome to Ardour!!");
+ print(1,0,"!Peace through Music!");
+}
+
+void TranzportControlProtocol::light_validate (LightID light)
+{
+ lights_invalid[light] = 0;
+}
+
+void TranzportControlProtocol::light_invalidate (LightID light)
+{
+ lights_invalid[light] = 1;
+}
+
+void TranzportControlProtocol::lights_validate ()
+{
+ memset (lights_invalid, 0, sizeof (lights_invalid));
+}
+
+void TranzportControlProtocol::lights_invalidate ()
+{
+ memset (lights_invalid, 1, sizeof (lights_invalid));
+}
+
+void TranzportControlProtocol::lights_init()
+{
+ for (uint32_t i = 0; i < sizeof(lights_current)/sizeof(lights_current[0]); i++) {
+ lights_invalid[i] = lights_current[i] =
+ lights_pending[i] = lights_flash[i] = false;
+ }
+}
+
+
+
+int
+TranzportControlProtocol::lights_flush ()
+{
+ if ( _device_status == STATUS_OFFLINE) { return (0); }
+
+ // Figure out iterators one day soon
+ // for (LightID i = i.start(), i = i.end(); i++) {
+ // if (lights_pending[i] != lights_current[i] || lights_invalid[i]) {
+ // if (light_set(i, lights_pending[i])) {
+ // return i-1;
+ // }
+ // }
+ //}
+ if ((lights_pending[LightRecord] != lights_current[LightRecord]) || lights_invalid[LightRecord]) {
+ if (light_set(LightRecord,lights_pending[LightRecord])) {
+ return 1;
+ }
+ }
+ if ((lights_pending[LightTrackrec] != lights_current[LightTrackrec]) || lights_invalid[LightTrackrec]) {
+ if (light_set(LightTrackrec,lights_pending[LightTrackrec])) {
+ return 1;
+ }
+ }
+
+ if ((lights_pending[LightTrackmute] != lights_current[LightTrackmute]) || lights_invalid[LightTrackmute]) {
+ if (light_set(LightTrackmute,lights_pending[LightTrackmute])) {
+ return 1;
+ }
+ }
+
+ if ((lights_pending[LightTracksolo] != lights_current[LightTracksolo]) || lights_invalid[LightTracksolo]) {
+ if (light_set(LightTracksolo,lights_pending[LightTracksolo])) {
+ return 1;
+ }
+ }
+ if ((lights_pending[LightAnysolo] != lights_current[LightAnysolo]) || lights_invalid[LightAnysolo]) {
+ if (light_set(LightAnysolo,lights_pending[LightAnysolo])) {
+ return 1;
+ }
+ }
+ if ((lights_pending[LightLoop] != lights_current[LightLoop]) || lights_invalid[LightLoop]) {
+ if (light_set(LightLoop,lights_pending[LightLoop])) {
+ return 1;
+ }
+ }
+ if ((lights_pending[LightPunch] != lights_current[LightPunch]) || lights_invalid[LightPunch]) {
+ if (light_set(LightPunch,lights_pending[LightPunch])) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+// Screen specific commands
+
+void
+TranzportControlProtocol::screen_clear ()
+{
+ const char *blank = " ";
+ print(0,0,blank);
+ print(1,0,blank);
+}
+
+void TranzportControlProtocol::screen_invalidate ()
+{
+ for(int row = 0; row < 2; row++) {
+ for(int col = 0; col < 20; col++) {
+ screen_invalid[row][col] = true;
+ screen_current[row][col] = 0x7f;
+ screen_pending[row][col] = ' ';
+ // screen_flash[row][col] = ' ';
+ }
+ }
+ // memset (&screen_invalid, 1, sizeof(screen_invalid));
+ // memset (&screen_current, 0x7F, sizeof (screen_current)); // fill cache with a character we otherwise never use
+}
+
+void TranzportControlProtocol::screen_validate ()
+{
+}
+
+void TranzportControlProtocol::screen_init ()
+{
+ screen_invalidate();
+}
+
+int
+TranzportControlProtocol::screen_flush ()
+{
+ int cell = 0, row, col_base, col, pending = 0;
+ if ( _device_status == STATUS_OFFLINE) { return (-1); }
+
+ for (row = 0; row < 2 && pending == 0; row++) {
+ for (col_base = 0, col = 0; col < 20 && pending == 0; ) {
+ if ((screen_pending[row][col] != screen_current[row][col])
+ || screen_invalid[row][col]) {
+
+ /* something in this cell is different, so dump the cell to the device. */
+
+ uint8_t cmd[8];
+ cmd[0] = 0x00;
+ cmd[1] = 0x01;
+ cmd[2] = cell;
+ cmd[3] = screen_pending[row][col_base];
+ cmd[4] = screen_pending[row][col_base+1];
+ cmd[5] = screen_pending[row][col_base+2];
+ cmd[6] = screen_pending[row][col_base+3];
+ cmd[7] = 0x00;
+
+ if(write(cmd) != 0) {
+ /* try to update this cell on the next go-round */
+#if DEBUG_TRANZPORT > 4
+ printf("usb screen update failed for some reason... why? \ncmd and data were %02x %02x %02x %02x %02x %02x %02x %02x\n",
+ cmd[0],cmd[1],cmd[2], cmd[3], cmd[4], cmd[5],cmd[6],cmd[7]);
+#endif
+ pending += 1;
+ // Shouldn't need to do this
+ // screen_invalid[row][col_base] = screen_invalid[row][col_base+1] =
+ // screen_invalid[row][col_base+2] = screen_invalid[row][col_base+3] = true;
+
+ } else {
+ /* successful write: copy to current cached display */
+ screen_invalid[row][col_base] = screen_invalid[row][col_base+1] =
+ screen_invalid[row][col_base+2] = screen_invalid[row][col_base+3] = false;
+ memcpy (&screen_current[row][col_base], &screen_pending[row][col_base], 4);
+ }
+
+ /* skip the rest of the 4 character cell since we wrote+copied it already */
+
+ col_base += 4;
+ col = col_base;
+ cell++;
+
+ } else {
+
+ col++;
+
+ if (col && col % 4 == 0) {
+ cell++;
+ col_base += 4;
+ }
+ }
+ }
+ }
+ return pending;
+}
+
+
+// Tranzport specific
+
+void TranzportControlProtocol::invalidate()
+{
+ lcd_damage(); lights_invalidate(); screen_invalidate(); // one of these days lcds can be fine but screens not
+}
+
+TranzportControlProtocol::~TranzportControlProtocol ()
+{
+ set_active (false);
+}
+
+
+int
+TranzportControlProtocol::set_active (bool yn)
+{
+ if (yn != _active) {
+
+ if (yn) {
+
+ if (open ()) {
+ return -1;
+ }
+
+ if (pthread_create_and_store (X_("tranzport monitor"), &thread, 0, _monitor_work, this) == 0) {
+ _active = true;
+ } else {
+ return -1;
+ }
+
+ } else {
+ cerr << "Begin tranzport shutdown\n";
+ screen_clear ();
+ lcd_damage();
+ lights_off ();
+ for(int x = 0; x < 10 && flush(); x++) { usleep(1000); }
+ pthread_cancel_one (thread);
+ cerr << "Tranzport Thread dead\n";
+ close ();
+ _active = false;
+ cerr << "End tranzport shutdown\n";
+ }
+ }
+
+ return 0;
+}
+
+void
+TranzportControlProtocol::show_track_gain ()
+{
+ if (route_table[0]) {
+ gain_t g = route_get_gain (0);
+ if ((g != last_track_gain) || lcd_isdamaged(0,9,8)) {
+ char buf[16];
+ snprintf (buf, sizeof (buf), "%6.1fdB", coefficient_to_dB (route_get_effective_gain (0)));
+ print (0, 9, buf);
+ last_track_gain = g;
+ }
+ } else {
+ print (0, 9, " ");
+ }
+}
+
+void
+TranzportControlProtocol::normal_update ()
+{
+ show_current_track ();
+ show_transport_time ();
+ show_track_gain ();
+ show_wheel_mode ();
+}
+
+void
+TranzportControlProtocol::next_display_mode ()
+{
+ switch (display_mode) {
+
+ case DisplayNormal:
+ enter_big_meter_mode();
+ break;
+
+ case DisplayBigMeter:
+ enter_normal_display_mode();
+ break;
+
+ case DisplayRecording:
+ enter_normal_display_mode();
+ break;
+
+ case DisplayRecordingMeter:
+ enter_big_meter_mode();
+ break;
+
+ case DisplayConfig:
+ case DisplayBling:
+ case DisplayBlingMeter:
+ enter_normal_display_mode();
+ break;
+ }
+}
+
+// FIXME, these 3 aren't done yet
+
+void
+TranzportControlProtocol::enter_recording_mode ()
+{
+ lcd_damage(); // excessive
+ screen_clear ();
+ lights_off ();
+ display_mode = DisplayRecording;
+}
+
+void
+TranzportControlProtocol::enter_bling_mode ()
+{
+ lcd_damage();
+ screen_clear ();
+ lights_off ();
+ display_mode = DisplayBling;
+}
+
+void
+TranzportControlProtocol::enter_config_mode ()
+{
+ lcd_damage();
+ screen_clear ();
+ lights_off ();
+ display_mode = DisplayConfig;
+}
+
+
+void
+TranzportControlProtocol::enter_big_meter_mode ()
+{
+ screen_clear ();
+ lcd_damage();
+ lights_off ();
+ last_meter_fill = 0;
+ display_mode = DisplayBigMeter;
+}
+
+void
+TranzportControlProtocol::enter_normal_display_mode ()
+{
+ screen_clear ();
+ lcd_damage();
+ lights_off ();
+ display_mode = DisplayNormal;
+ // normal_update();
+}
+
+
+float
+log_meter (float db)
+{
+ float def = 0.0f; /* Meter deflection %age */
+
+ if (db < -70.0f) return 0.0f;
+ if (db > 6.0f) return 1.0f;
+
+ if (db < -60.0f) {
+ def = (db + 70.0f) * 0.25f;
+ } else if (db < -50.0f) {
+ def = (db + 60.0f) * 0.5f + 2.5f;
+ } else if (db < -40.0f) {
+ def = (db + 50.0f) * 0.75f + 7.5f;
+ } else if (db < -30.0f) {
+ def = (db + 40.0f) * 1.5f + 15.0f;
+ } else if (db < -20.0f) {
+ def = (db + 30.0f) * 2.0f + 30.0f;
+ } else if (db < 6.0f) {
+ def = (db + 20.0f) * 2.5f + 50.0f;
+ }
+
+ /* 115 is the deflection %age that would be
+ when db=6.0. this is an arbitrary
+ endpoint for our scaling.
+ */
+
+ return def/115.0f;
+}
+
+void
+TranzportControlProtocol::show_meter ()
+{
+ // you only seem to get a route_table[0] on moving forward - bug elsewhere
+ if (route_table[0] == 0) {
+ // Principle of least surprise
+ print (0, 0, "No audio to meter!!!");
+ print (1, 0, "Select another track");
+ return;
+ }
+
+ float level = route_get_peak_input_power (0, 0);
+ float fraction = log_meter (level);
+
+ /* Someday add a peak bar*/
+
+ /* we draw using a choice of a sort of double colon-like character ("::") or a single, left-aligned ":".
+ the screen is 20 chars wide, so we can display 40 different levels. compute the level,
+ then figure out how many "::" to fill. if the answer is odd, make the last one a ":"
+ */
+
+ uint32_t fill = (uint32_t) floor (fraction * 40);
+ char buf[21];
+ uint32_t i;
+
+ if (fill == last_meter_fill) {
+ /* nothing to do */
+ return;
+ }
+
+ last_meter_fill = fill;
+
+ bool add_single_level = (fill % 2 != 0);
+ fill /= 2;
+
+ if (fraction > 0.98) {
+ light_on (LightAnysolo);
+ }
+
+ /* add all full steps */
+
+ for (i = 0; i < fill; ++i) {
+ buf[i] = 0x07; /* tranzport special code for 4 quadrant LCD block */
+ }
+
+ /* add a possible half-step */
+
+ if (i < 20 && add_single_level) {
+ buf[i] = 0x03; /* tranzport special code for 2 left quadrant LCD block */
+ ++i;
+ }
+
+ /* fill rest with space */
+
+ for (; i < 20; ++i) {
+ buf[i] = ' ';
+ }
+
+ /* print() requires this */
+
+ buf[21] = '\0';
+
+ print (0, 0, buf);
+ print (1, 0, buf);
+}
+
+void
+TranzportControlProtocol::show_bbt (nframes_t where)
+{
+ if ((where != last_where) || lcd_isdamaged(1,9,8)) {
+ char buf[16];
+ BBT_Time bbt;
+ session->tempo_map().bbt_time (where, bbt);
+ sprintf (buf, "%03" PRIu32 "|%02" PRIu32 "|%04" PRIu32, bbt.bars,bbt.beats,bbt.ticks);
+ last_bars = bbt.bars;
+ last_beats = bbt.beats;
+ last_ticks = bbt.ticks;
+ last_where = where;
+
+ if(last_ticks < 1960) { print (1, 9, buf); } // save a write so we can do leds
+
+ // if displaymode is recordmode show beats but not yet
+ lights_pending[LightRecord] = false;
+ lights_pending[LightAnysolo] = false;
+ switch(last_beats) {
+ case 1: if(last_ticks < 500 || last_ticks > 1960) lights_pending[LightRecord] = true; break;
+ default: if(last_ticks < 250) lights_pending[LightAnysolo] = true;
+ }
+
+ // update lights for tempo one day
+ // if (bbt_upper_info_label) {
+ // TempoMap::Metric m (session->tempo_map().metric_at (when));
+ // sprintf (buf, "%-5.2f", m.tempo().beats_per_minute());
+ // bbt_lower_info_label->set_text (buf);
+ // sprintf (buf, "%g|%g", m.meter().beats_per_bar(), m.meter().note_divisor());
+ // bbt_upper_info_label->set_text (buf);
+ }
+ }
+
+
+void
+TranzportControlProtocol::show_transport_time ()
+{
+ nframes_t where = session->transport_frame();
+ show_bbt(where);
+}
+
+void
+TranzportControlProtocol::show_smpte (nframes_t where)
+{
+ if ((where != last_where) || lcd_isdamaged(1,9,10)) {
+
+ char buf[5];
+ SMPTE::Time smpte;
+
+ session->smpte_time (where, smpte);
+
+ if (smpte.negative) {
+ sprintf (buf, "-%02" PRIu32 ":", smpte.hours);
+ } else {
+ sprintf (buf, " %02" PRIu32 ":", smpte.hours);
+ }
+ print (1, 8, buf);
+
+ sprintf (buf, "%02" PRIu32 ":", smpte.minutes);
+ print (1, 12, buf);
+
+ sprintf (buf, "%02" PRIu32 ":", smpte.seconds);
+ print (1, 15, buf);
+
+ sprintf (buf, "%02" PRIu32, smpte.frames);
+ print_noretry (1, 18, buf);
+
+ last_where = where;
+ }
+}
+
+void*
+TranzportControlProtocol::_monitor_work (void* arg)
+{
+ return static_cast<TranzportControlProtocol*>(arg)->monitor_work ();
+}
+
+// I note that these usb specific open, close, probe, read routines are basically
+// pure boilerplate and could easily be abstracted elsewhere
+
+#if !HAVE_TRANZPORT_KERNEL_DRIVER
+
+bool
+TranzportControlProtocol::probe ()
+{
+ struct usb_bus *bus;
+ struct usb_device *dev;
+
+ usb_init();
+ usb_find_busses();
+ usb_find_devices();
+
+ for (bus = usb_busses; bus; bus = bus->next) {
+
+ for(dev = bus->devices; dev; dev = dev->next) {
+ if (dev->descriptor.idVendor == VENDORID && dev->descriptor.idProduct == PRODUCTID) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+int
+TranzportControlProtocol::open ()
+{
+ struct usb_bus *bus;
+ struct usb_device *dev;
+
+ usb_init();
+ usb_find_busses();
+ usb_find_devices();
+
+ for (bus = usb_busses; bus; bus = bus->next) {
+
+ for(dev = bus->devices; dev; dev = dev->next) {
+ if (dev->descriptor.idVendor != VENDORID)
+ continue;
+ if (dev->descriptor.idProduct != PRODUCTID)
+ continue;
+ return open_core (dev);
+ }
+ }
+
+ error << _("Tranzport: no device detected") << endmsg;
+ return -1;
+}
+
+int
+TranzportControlProtocol::open_core (struct usb_device* dev)
+{
+ if (!(udev = usb_open (dev))) {
+ error << _("Tranzport: cannot open USB transport") << endmsg;
+ return -1;
+ }
+
+ if (usb_claim_interface (udev, 0) < 0) {
+ error << _("Tranzport: cannot claim USB interface") << endmsg;
+ usb_close (udev);
+ udev = 0;
+ return -1;
+ }
+
+ if (usb_set_configuration (udev, 1) < 0) {
+ cerr << _("Tranzport: cannot configure USB interface") << endmsg;
+ }
+
+ return 0;
+}
+
+int
+TranzportControlProtocol::close ()
+{
+ int ret = 0;
+
+ if (udev == 0) {
+ return 0;
+ }
+
+ if (usb_release_interface (udev, 0) < 0) {
+ error << _("Tranzport: cannot release interface") << endmsg;
+ ret = -1;
+ }
+
+ if (usb_close (udev)) {
+ error << _("Tranzport: cannot close device") << endmsg;
+ udev = 0;
+ ret = 0;
+ }
+
+ return ret;
+}
+
+int TranzportControlProtocol::read(uint8_t *buf, uint32_t timeout_override)
+{
+ int val;
+ // Get smarter about handling usb errors soon. Like disconnect
+ // pthread_testcancel();
+ val = usb_interrupt_read (udev, READ_ENDPOINT, (char *) buf, 8, 10);
+ // pthread_testcancel();
+ return val;
+}
+
+
+int
+TranzportControlProtocol::write_noretry (uint8_t* cmd, uint32_t timeout_override)
+{
+ int val;
+ if(inflight > MAX_TRANZPORT_INFLIGHT) { return (-1); }
+ val = usb_interrupt_write (udev, WRITE_ENDPOINT, (char*) cmd, 8, timeout_override ? timeout_override : timeout);
+
+ if (val < 0) {
+#if DEBUG_TRANZPORT
+ printf("usb_interrupt_write failed: %d\n", val);
+#endif
+ return val;
+ }
+
+ if (val != 8) {
+#if DEBUG_TRANZPORT
+ printf("usb_interrupt_write failed: %d\n", val);
+#endif
+ return -1;
+ }
+ ++inflight;
+
+ return 0;
+
+}
+
+int
+TranzportControlProtocol::write (uint8_t* cmd, uint32_t timeout_override)
+{
+#if MAX_RETRY > 1
+ int val;
+ int retry = 0;
+ if(inflight > MAX_TRANZPORT_INFLIGHT) { return (-1); }
+
+ while((val = usb_interrupt_write (udev, WRITE_ENDPOINT, (char*) cmd, 8, timeout_override ? timeout_override : timeout))!=8 && retry++ < MAX_RETRY) {
+ printf("usb_interrupt_write failed, retrying: %d\n", val);
+ }
+
+ if (retry == MAX_RETRY) {
+ printf("Too many retries on a tranzport write, aborting\n");
+ }
+
+ if (val < 0) {
+ printf("usb_interrupt_write failed: %d\n", val);
+ return val;
+ }
+ if (val != 8) {
+ printf("usb_interrupt_write failed: %d\n", val);
+ return -1;
+ }
+ ++inflight;
+ return 0;
+#else
+ return (write_noretry(cmd,timeout_override));
+#endif
+
+}
+
+#else
+#error Kernel API not defined yet for Tranzport
+// Something like open(/dev/surface/tranzport/event) for reading and raw for writing)
+#endif
+
+// We have a state "Unknown" - STOP USING SPACES FOR IT - switching to arrow character
+// We have another state - no_retry. Misleading, as we still retry on the next pass
+// I think it's pointless to keep no_retry and instead we should throttle writes
+// We have an "displayed" screen
+// We always draw into the pending screen, which could be any of several screens
+// We have an active screen
+// Print arg - we have
+// setactive
+// so someday I think we need a screen object.
+
+/*
+screen_flash.clear();
+screen_flash.print(0,0,"Undone:"); // Someday pull the undo stack from somewhere
+screen_flash.print(1,0,"Nextup:");
+
+if(flash_messages && lcd.getactive() != screen_flash) lcd.setactive(screen_flash,2000);
+
+screen::setactive(screen_name,duration); // duration in ms
+screen::getactive();
+*/
+
+
+int
+TranzportControlProtocol::flush ()
+{
+ int pending = 0;
+ if(!(pending = lights_flush())) {
+ pending = screen_flush();
+ }
+ return pending;
+}
+
+// doing these functions made me realize that screen_invalid should be lcd_isdamaged FIXME soon
+
+bool TranzportControlProtocol::lcd_damage()
+{
+ screen_invalidate();
+ return true;
+}
+
+bool TranzportControlProtocol::lcd_damage (int row, int col, int length)
+{
+ bool result = false;
+ int endcol = col+length-1;
+ if((endcol > 19)) { endcol = 19; }
+ if((row >= 0 && row < 2) && (col >=0 && col < 20)) {
+ for(int c = col; c < endcol; c++) {
+ screen_invalid[row][c] = true;
+ }
+ result = true;
+ }
+ return result;
+}
+
+// Gotta switch to bitfields, this is collossally dumb
+// Still working on the layering, arguably screen_invalid should be lcd_invalid
+
+bool TranzportControlProtocol::lcd_isdamaged ()
+{
+ for(int r = 0; r < 2; r++) {
+ for(int c = 0; c < 20; c++) {
+ if(screen_invalid[r][c]) {
+#if DEBUG_TRANZPORT > 5
+ printf("row: %d,col: %d is damaged, should redraw it\n", r,c);
+#endif
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+bool TranzportControlProtocol::lcd_isdamaged (int row, int col, int length)
+{
+ bool result = 0;
+ int endcol = col+length;
+ if((endcol > 19)) { endcol = 19; }
+ if((row >= 0 && row < 2) && (col >=0 && col < 20)) {
+ for(int c = col; c < endcol; c++) {
+ if(screen_invalid[row][c]) {
+#if DEBUG_TRANZPORT > 5
+ printf("row: %d,col: %d is damaged, should redraw it\n", row,c);
+#endif
+ return true;
+ }
+ }
+ }
+ return result;
+}
+
+// lcd_clear would be a separate function for a smart display
+// here it does nothing, but for the sake of completeness it should
+// probably write the lcd, and while I'm on the topic it should probably
+// take a row, col, length argument....
+
+void
+TranzportControlProtocol::lcd_clear ()
+{
+
+}
+
+// These lcd commands are not universally used yet and may drop out of the api
+
+int
+TranzportControlProtocol::lcd_flush ()
+{
+ return 0;
+}
+
+int
+TranzportControlProtocol::lcd_write(uint8_t* cmd, uint32_t timeout_override)
+{
+ return write(cmd,timeout_override);
+}
+
+void
+TranzportControlProtocol::lcd_fill (uint8_t fill_char)
+{
+}
+
+void
+TranzportControlProtocol::lcd_print (int row, int col, const char* text)
+{
+ print(row,col,text);
+}
+
+void TranzportControlProtocol::lcd_print_noretry (int row, int col, const char* text)
+{
+ print(row,col,text);
+}
+
+// Lights are buffered
+
+void
+TranzportControlProtocol::lights_on ()
+{
+ lights_pending[LightRecord] = lights_pending[LightTrackrec] =
+ lights_pending[LightTrackmute] = lights_pending[LightTracksolo] =
+ lights_pending[LightAnysolo] = lights_pending[LightLoop] =
+ lights_pending[LightPunch] = true;
+}
+
+void
+TranzportControlProtocol::lights_off ()
+{
+ lights_pending[LightRecord] = lights_pending[LightTrackrec] =
+ lights_pending[LightTrackmute] = lights_pending[LightTracksolo] =
+ lights_pending[LightAnysolo] = lights_pending[LightLoop] =
+ lights_pending[LightPunch] = false;
+}
+
+int
+TranzportControlProtocol::light_on (LightID light)
+{
+ lights_pending[light] = true;
+ return 0;
+}
+
+int
+TranzportControlProtocol::light_off (LightID light)
+{
+ lights_pending[light] = false;
+ return 0;
+}
+
+int
+TranzportControlProtocol::light_set (LightID light, bool offon)
+{
+ uint8_t cmd[8];
+ cmd[0] = 0x00; cmd[1] = 0x00; cmd[2] = light; cmd[3] = offon;
+ cmd[4] = 0x00; cmd[5] = 0x00; cmd[6] = 0x00; cmd[7] = 0x00;
+
+ if (write (cmd) == 0) {
+ lights_current[light] = offon;
+ lights_invalid[light] = false;
+ return 0;
+ } else {
+ return -1;
+ }
+}
+
+int TranzportControlProtocol::rtpriority_set(int priority)
+{
+ struct sched_param rtparam;
+ int err;
+ // preallocate and memlock some stack with memlock?
+ char *a = (char*) alloca(4096*2); a[0] = 'a'; a[4096] = 'b';
+ memset (&rtparam, 0, sizeof (rtparam));
+ rtparam.sched_priority = priority; /* XXX should be relative to audio (JACK) thread */
+ // Note - try SCHED_RR with a low limit
+ // - we don't care if we can't write everything this ms
+ // and it will help if we lose the device
+ if ((err = pthread_setschedparam (pthread_self(), SCHED_FIFO, &rtparam)) != 0) {
+ PBD::info << string_compose (_("%1: thread not running with realtime scheduling (%2)"), name(), strerror (errno)) << endmsg;
+ return 1;
+ }
+ return 0;
+}
+
+// Running with realtime privs is bad when you have problems
+
+int TranzportControlProtocol::rtpriority_unset(int priority)
+{
+ struct sched_param rtparam;
+ int err;
+ memset (&rtparam, 0, sizeof (rtparam));
+ rtparam.sched_priority = priority;
+ if ((err = pthread_setschedparam (pthread_self(), SCHED_FIFO, &rtparam)) != 0) {
+ PBD::info << string_compose (_("%1: can't stop realtime scheduling (%2)"), name(), strerror (errno)) << endmsg;
+ return 1;
+ }
+ PBD::info << string_compose (_("%1: realtime scheduling stopped (%2)"), name(), strerror (errno)) << endmsg;
+ return 0;
+}
+
+// Slowly breaking this into where I can make usb processing it's own thread.
+
+void*
+TranzportControlProtocol::monitor_work ()
+{
+ uint8_t buf[8];
+ int val = 0, pending = 0;
+ bool first_time = true;
+ uint8_t offline = 0;
+
+
+ PBD::ThreadCreated (pthread_self(), X_("Tranzport"));
+ pthread_setcancelstate (PTHREAD_CANCEL_ENABLE, 0);
+ pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, 0);
+ next_track ();
+ rtpriority_set();
+ inflight=0;
+ flush();
+
+ while (true) {
+
+ /* bInterval for this beastie is 10ms */
+
+ if (_device_status == STATUS_OFFLINE) {
+ first_time = true;
+ if(offline++ == 1) {
+ cerr << "Transport has gone offline\n";
+ }
+ } else {
+ offline = 0; // hate writing this
+ }
+
+ val = read(buf);
+
+ if (val == 8) {
+ process (buf);
+ }
+
+#if DEBUG_TRANZPORT > 2
+ if(inflight > 1) printf("Inflight: %d\n", inflight);
+#endif
+
+
+ if (_device_status != STATUS_OFFLINE) {
+ if (first_time) {
+ invalidate();
+ lcd_clear ();
+ lights_off ();
+ first_time = false;
+ offline = 0;
+ pending = 3; // Give some time for the device to recover
+ }
+ /* update whatever needs updating */
+ update_state ();
+
+ /* still struggling with a good means of exerting flow control */
+ // pending = flush();
+
+ if(pending == 0) {
+ pending = flush();
+ } else {
+ if(inflight > 0) {
+ pending = --inflight; // we just did a whole bunch of writes so wait
+ } else {
+ pending = 0;
+ }
+ }
+ // pending = 0;
+ }
+ }
+
+ return (void*) 0;
+}
+
+int TranzportControlProtocol::lights_show_recording()
+{
+ // FIXME, flash recording light when recording and transport is moving
+ return lights_show_normal();
+}
+
+// gotta do bling next!
+
+int TranzportControlProtocol::lights_show_bling()
+{
+ switch (bling_mode) {
+ case BlingOff: break;
+ case BlingKit: break; // rotate rec/mute/solo/any solo back and forth
+ case BlingRotating: break; // switch between lights
+ case BlingPairs: break; // Show pairs of lights
+ case BlingRows: break; // light each row in sequence
+ case BlingFlashAll: break; // Flash everything randomly
+ }
+ return 0;
+}
+
+int TranzportControlProtocol::lights_show_normal()
+{
+ /* Track only */
+
+ if (route_table[0]) {
+ boost::shared_ptr<AudioTrack> at = boost::dynamic_pointer_cast<AudioTrack> (route_table[0]);
+ lights_pending[LightTrackrec] = at && at->record_enabled();
+ lights_pending[LightTrackmute] = route_get_muted(0);
+ lights_pending[LightTracksolo] = route_get_soloed(0);
+ } else {
+ lights_pending[LightTrackrec] = false;
+ lights_pending[LightTracksolo] = false;
+ lights_pending[LightTrackmute] = false;
+ }
+
+ /* Global settings */
+
+ lights_pending[LightLoop] = session->get_play_loop();
+ lights_pending[LightPunch] = Config->get_punch_in() || Config->get_punch_out();
+ lights_pending[LightRecord] = session->get_record_enabled();
+ lights_pending[LightAnysolo] = session->soloing();
+
+ return 0;
+}
+
+int TranzportControlProtocol::lights_show_tempo()
+{
+ // someday soon fiddle with the lights based on the tempo
+ return lights_show_normal();
+}
+
+int
+TranzportControlProtocol::update_state ()
+{
+ /* do the text and light updates */
+
+ switch (display_mode) {
+ case DisplayBigMeter:
+ lights_show_tempo();
+ show_meter ();
+ break;
+
+ case DisplayNormal:
+ lights_show_normal();
+ normal_update ();
+ break;
+
+ case DisplayConfig:
+ break;
+
+ case DisplayRecording:
+ lights_show_recording();
+ normal_update();
+ break;
+
+ case DisplayRecordingMeter:
+ lights_show_recording();
+ show_meter();
+ break;
+
+ case DisplayBling:
+ lights_show_bling();
+ normal_update();
+ break;
+
+ case DisplayBlingMeter:
+ lights_show_bling();
+ show_meter();
+ break;
+ }
+ return 0;
+
+}
+
+#define TRANZPORT_BUTTON_HANDLER(callback, button_arg) if (button_changes & button_arg) { \
+ if (buttonmask & button_arg) { \
+ callback##_press (buttonmask&ButtonShift); } else { callback##_release (buttonmask&ButtonShift); } }
+
+int
+TranzportControlProtocol::process (uint8_t* buf)
+{
+ // printf("read: %02x %02x %02x %02x %02x %02x %02x %02x\n", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]);
+
+ uint32_t this_button_mask;
+ uint32_t button_changes;
+
+ _device_status = buf[1];
+
+ this_button_mask = 0;
+ this_button_mask |= buf[2] << 24;
+ this_button_mask |= buf[3] << 16;
+ this_button_mask |= buf[4] << 8;
+ this_button_mask |= buf[5];
+ _datawheel = buf[6];
+
+ button_changes = (this_button_mask ^ buttonmask);
+ buttonmask = this_button_mask;
+
+ if (_datawheel) {
+ datawheel ();
+ }
+
+ // SHIFT + STOP + PLAY for bling mode?
+ // if (button_changes & ButtonPlay & ButtonStop) {
+ // bling_mode_toggle();
+ // } or something like that
+
+ TRANZPORT_BUTTON_HANDLER(button_event_battery,ButtonBattery);
+ TRANZPORT_BUTTON_HANDLER(button_event_backlight,ButtonBacklight);
+ TRANZPORT_BUTTON_HANDLER(button_event_trackleft,ButtonTrackLeft);
+ TRANZPORT_BUTTON_HANDLER(button_event_trackright,ButtonTrackRight);
+ TRANZPORT_BUTTON_HANDLER(button_event_trackrec,ButtonTrackRec);
+ TRANZPORT_BUTTON_HANDLER(button_event_trackmute,ButtonTrackMute);
+ TRANZPORT_BUTTON_HANDLER(button_event_tracksolo,ButtonTrackSolo);
+ TRANZPORT_BUTTON_HANDLER(button_event_undo,ButtonUndo);
+ TRANZPORT_BUTTON_HANDLER(button_event_in,ButtonIn);
+ TRANZPORT_BUTTON_HANDLER(button_event_out,ButtonOut);
+ TRANZPORT_BUTTON_HANDLER(button_event_punch,ButtonPunch);
+ TRANZPORT_BUTTON_HANDLER(button_event_loop,ButtonLoop);
+ TRANZPORT_BUTTON_HANDLER(button_event_prev,ButtonPrev);
+ TRANZPORT_BUTTON_HANDLER(button_event_add,ButtonAdd);
+ TRANZPORT_BUTTON_HANDLER(button_event_next,ButtonNext);
+ TRANZPORT_BUTTON_HANDLER(button_event_rewind,ButtonRewind);
+ TRANZPORT_BUTTON_HANDLER(button_event_fastforward,ButtonFastForward);
+ TRANZPORT_BUTTON_HANDLER(button_event_stop,ButtonStop);
+ TRANZPORT_BUTTON_HANDLER(button_event_play,ButtonPlay);
+ TRANZPORT_BUTTON_HANDLER(button_event_record,ButtonRecord);
+ return 0;
+}
+
+void
+TranzportControlProtocol::show_current_track ()
+{
+ char pad[11];
+ char *v;
+ int len;
+ if (route_table[0] == 0) {
+ print (0, 0, "----------");
+ last_track_gain = FLT_MAX;
+ } else {
+ strcpy(pad," ");
+ v = (char *)route_get_name (0).substr (0, 10).c_str();
+ if((len = strlen(v)) > 0) {
+ strncpy(pad,(char *)v,len);
+ }
+ print (0, 0, pad);
+ }
+}
+
+void
+TranzportControlProtocol::button_event_battery_press (bool shifted)
+{
+}
+
+void
+TranzportControlProtocol::button_event_battery_release (bool shifted)
+{
+}
+
+void
+TranzportControlProtocol::button_event_backlight_press (bool shifted)
+{
+#if DEBUG_TRANZPORT
+ printf("backlight pressed\n");
+#endif
+}
+
+void
+TranzportControlProtocol::button_event_backlight_release (bool shifted)
+{
+#if DEBUG_TRANZPORT
+ printf("backlight released\n\n");
+#endif
+ if (shifted) {
+ lcd_damage();
+ lcd_clear();
+ last_where += 1; /* force time redisplay */
+ last_track_gain = FLT_MAX;
+ normal_update(); // redraw_screen();
+ }
+}
+
+void
+TranzportControlProtocol::button_event_trackleft_press (bool shifted)
+{
+ prev_track ();
+}
+
+void
+TranzportControlProtocol::button_event_trackleft_release (bool shifted)
+{
+}
+
+void
+TranzportControlProtocol::button_event_trackright_press (bool shifted)
+{
+ next_track ();
+}
+
+void
+TranzportControlProtocol::button_event_trackright_release (bool shifted)
+{
+}
+
+void
+TranzportControlProtocol::button_event_trackrec_press (bool shifted)
+{
+ if (shifted) {
+ toggle_all_rec_enables ();
+ } else {
+ route_set_rec_enable (0, !route_get_rec_enable (0));
+ }
+}
+
+void
+TranzportControlProtocol::button_event_trackrec_release (bool shifted)
+{
+}
+
+void
+TranzportControlProtocol::button_event_trackmute_press (bool shifted)
+{
+ if (shifted) {
+ // Mute ALL? Something useful when a phone call comes in. Mute master?
+ } else {
+ route_set_muted (0, !route_get_muted (0));
+ }
+}
+
+void
+TranzportControlProtocol::button_event_trackmute_release (bool shifted)
+{
+}
+
+void
+TranzportControlProtocol::button_event_tracksolo_press (bool shifted)
+{
+#if DEBUG_TRANZPORT
+ printf("solo pressed\n");
+#endif
+ if (display_mode == DisplayBigMeter) {
+ light_off (LightAnysolo);
+ return;
+ }
+
+ if (shifted) {
+ session->set_all_solo (!session->soloing());
+ } else {
+ route_set_soloed (0, !route_get_soloed (0));
+ }
+}
+
+void
+TranzportControlProtocol::button_event_tracksolo_release (bool shifted)
+{
+#if DEBUG_TRANZPORT
+ printf("solo released\n");
+#endif
+}
+
+void
+TranzportControlProtocol::button_event_undo_press (bool shifted)
+{
+ if (shifted) {
+ redo (); // someday flash the screen with what was redone
+ } else {
+ undo (); // someday flash the screen with what was undone
+ }
+}
+
+void
+TranzportControlProtocol::button_event_undo_release (bool shifted)
+{
+}
+
+void
+TranzportControlProtocol::button_event_in_press (bool shifted)
+{
+ if (shifted) {
+ toggle_punch_in ();
+ } else {
+ ControlProtocol::ZoomIn (); /* EMIT SIGNAL */
+ }
+}
+
+void
+TranzportControlProtocol::button_event_in_release (bool shifted)
+{
+}
+
+void
+TranzportControlProtocol::button_event_out_press (bool shifted)
+{
+ if (shifted) {
+ toggle_punch_out ();
+ } else {
+ ControlProtocol::ZoomOut (); /* EMIT SIGNAL */
+ }
+}
+
+void
+TranzportControlProtocol::button_event_out_release (bool shifted)
+{
+}
+
+void
+TranzportControlProtocol::button_event_punch_press (bool shifted)
+{
+}
+
+void
+TranzportControlProtocol::button_event_punch_release (bool shifted)
+{
+}
+
+void
+TranzportControlProtocol::button_event_loop_press (bool shifted)
+{
+ if (shifted) {
+ next_wheel_shift_mode ();
+ } else {
+ loop_toggle ();
+ }
+}
+
+void
+TranzportControlProtocol::button_event_loop_release (bool shifted)
+{
+}
+
+void
+TranzportControlProtocol::button_event_prev_press (bool shifted)
+{
+ if (shifted) {
+ ControlProtocol::ZoomToSession (); /* EMIT SIGNAL */
+ } else {
+ prev_marker ();
+ }
+}
+
+void
+TranzportControlProtocol::button_event_prev_release (bool shifted)
+{
+}
+
+void
+TranzportControlProtocol::button_event_add_press (bool shifted)
+{
+ add_marker ();
+}
+
+void
+TranzportControlProtocol::button_event_add_release (bool shifted)
+{
+}
+
+void
+TranzportControlProtocol::button_event_next_press (bool shifted)
+{
+ if (shifted) {
+ next_wheel_mode ();
+ } else {
+ next_marker ();
+ }
+}
+
+void
+TranzportControlProtocol::button_event_next_release (bool shifted)
+{
+}
+
+void
+TranzportControlProtocol::button_event_rewind_press (bool shifted)
+{
+ if (shifted) {
+ goto_start ();
+ } else {
+ rewind ();
+ }
+}
+
+void
+TranzportControlProtocol::button_event_rewind_release (bool shifted)
+{
+}
+
+void
+TranzportControlProtocol::button_event_fastforward_press (bool shifted)
+{
+ if (shifted) {
+ goto_end ();
+ } else {
+ ffwd ();
+ }
+}
+
+void
+TranzportControlProtocol::button_event_fastforward_release (bool shifted)
+{
+}
+
+void
+TranzportControlProtocol::button_event_stop_press (bool shifted)
+{
+ if (shifted) {
+ next_display_mode ();
+ } else {
+ transport_stop ();
+ }
+}
+
+void
+TranzportControlProtocol::button_event_stop_release (bool shifted)
+{
+}
+
+void
+TranzportControlProtocol::button_event_play_press (bool shifted)
+{
+ if (shifted) {
+ set_transport_speed (1.0f);
+ } else {
+ transport_play ();
+ }
+}
+
+void
+TranzportControlProtocol::button_event_play_release (bool shifted)
+{
+}
+
+void
+TranzportControlProtocol::button_event_record_press (bool shifted)
+{
+ if (shifted) {
+ save_state ();
+ } else {
+ rec_enable_toggle ();
+ }
+}
+
+void
+TranzportControlProtocol::button_event_record_release (bool shifted)
+{
+}
+
+void button_event_mute (bool pressed, bool shifted)
+{
+ static int was_pressed = 0;
+ // if(pressed) { }
+}
+
+void
+TranzportControlProtocol::datawheel ()
+{
+ if ((buttonmask & ButtonTrackRight) || (buttonmask & ButtonTrackLeft)) {
+
+ /* track scrolling */
+
+ if (_datawheel < WheelDirectionThreshold) {
+ next_track ();
+ } else {
+ prev_track ();
+ }
+
+ timerclear (&last_wheel_motion);
+
+ } else if ((buttonmask & ButtonPrev) || (buttonmask & ButtonNext)) {
+
+ if (_datawheel < WheelDirectionThreshold) {
+ next_marker ();
+ } else {
+ prev_marker ();
+ }
+
+ timerclear (&last_wheel_motion);
+
+ } else if (buttonmask & ButtonShift) {
+
+ /* parameter control */
+
+ if (route_table[0]) {
+ switch (wheel_shift_mode) {
+ case WheelShiftGain:
+ if (_datawheel < WheelDirectionThreshold) {
+ step_gain_up ();
+ } else {
+ step_gain_down ();
+ }
+ break;
+ case WheelShiftPan:
+ if (_datawheel < WheelDirectionThreshold) {
+ step_pan_right ();
+ } else {
+ step_pan_left ();
+ }
+ break;
+
+ case WheelShiftMarker:
+ break;
+
+ case WheelShiftMaster:
+ break;
+
+ }
+ }
+
+ timerclear (&last_wheel_motion);
+
+ } else {
+
+ switch (wheel_mode) {
+ case WheelTimeline:
+ scroll ();
+ break;
+
+ case WheelScrub:
+ scrub ();
+ break;
+
+ case WheelShuttle:
+ shuttle ();
+ break;
+ }
+ }
+}
+
+void
+TranzportControlProtocol::scroll ()
+{
+ float m = 1.0;
+ if (_datawheel < WheelDirectionThreshold) {
+ m = 1.0;
+ } else {
+ m = -1.0;
+ }
+ switch(wheel_increment) {
+ case WheelIncrScreen: ScrollTimeline (0.2*m); break;
+ default: break; // other modes unimplemented as yet
+ }
+}
+
+void
+TranzportControlProtocol::scrub ()
+{
+ float speed;
+ struct timeval now;
+ struct timeval delta;
+ int dir;
+
+ gettimeofday (&now, 0);
+
+ if (_datawheel < WheelDirectionThreshold) {
+ dir = 1;
+ } else {
+ dir = -1;
+ }
+
+ if (dir != last_wheel_dir) {
+ /* changed direction, start over */
+ speed = 0.1f;
+ } else {
+ if (timerisset (&last_wheel_motion)) {
+
+ timersub (&now, &last_wheel_motion, &delta);
+
+ /* 10 clicks per second => speed == 1.0 */
+
+ speed = 100000.0f / (delta.tv_sec * 1000000 + delta.tv_usec);
+
+ } else {
+
+ /* start at half-speed and see where we go from there */
+
+ speed = 0.5f;
+ }
+ }
+
+ last_wheel_motion = now;
+ last_wheel_dir = dir;
+
+ set_transport_speed (speed * dir);
+}
+
+void
+TranzportControlProtocol::config ()
+{
+ // FIXME
+}
+
+void
+TranzportControlProtocol::shuttle ()
+{
+ if (_datawheel < WheelDirectionThreshold) {
+ if (session->transport_speed() < 0) {
+ session->request_transport_speed (1.0);
+ } else {
+ session->request_transport_speed (session->transport_speed() + 0.1);
+ }
+ } else {
+ if (session->transport_speed() > 0) {
+ session->request_transport_speed (-1.0);
+ } else {
+ session->request_transport_speed (session->transport_speed() - 0.1);
+ }
+ }
+}
+
+void
+TranzportControlProtocol::step_gain_up ()
+{
+ if (buttonmask & ButtonStop) {
+ gain_fraction += 0.001;
+ } else {
+ gain_fraction += 0.01;
+ }
+
+ if (gain_fraction > 2.0) {
+ gain_fraction = 2.0;
+ }
+
+ route_set_gain (0, slider_position_to_gain (gain_fraction));
+}
+
+void
+TranzportControlProtocol::step_gain_down ()
+{
+ if (buttonmask & ButtonStop) {
+ gain_fraction -= 0.001;
+ } else {
+ gain_fraction -= 0.01;
+ }
+
+ if (gain_fraction < 0.0) {
+ gain_fraction = 0.0;
+ }
+
+ route_set_gain (0, slider_position_to_gain (gain_fraction));
+}
+
+void
+TranzportControlProtocol::step_pan_right ()
+{
+}
+
+void
+TranzportControlProtocol::step_pan_left ()
+{
+}
+
+void
+TranzportControlProtocol::next_wheel_shift_mode ()
+{
+ switch (wheel_shift_mode) {
+ case WheelShiftGain:
+ wheel_shift_mode = WheelShiftPan;
+ break;
+ case WheelShiftPan:
+ wheel_shift_mode = WheelShiftMaster;
+ break;
+ case WheelShiftMaster:
+ wheel_shift_mode = WheelShiftGain;
+ break;
+ case WheelShiftMarker: // Not done yet, disabled
+ wheel_shift_mode = WheelShiftGain;
+ break;
+ }
+
+ show_wheel_mode ();
+}
+
+void
+TranzportControlProtocol::next_wheel_mode ()
+{
+ switch (wheel_mode) {
+ case WheelTimeline:
+ wheel_mode = WheelScrub;
+ break;
+ case WheelScrub:
+ wheel_mode = WheelShuttle;
+ break;
+ case WheelShuttle:
+ wheel_mode = WheelTimeline;
+ }
+
+ show_wheel_mode ();
+}
+
+void
+TranzportControlProtocol::next_track ()
+{
+ ControlProtocol::next_track (current_track_id);
+ gain_fraction = gain_to_slider_position (route_get_effective_gain (0));
+}
+
+void
+TranzportControlProtocol::prev_track ()
+{
+ ControlProtocol::prev_track (current_track_id);
+ gain_fraction = gain_to_slider_position (route_get_effective_gain (0));
+}
+
+void
+TranzportControlProtocol::show_wheel_mode ()
+{
+ string text;
+
+ switch (wheel_mode) {
+ case WheelTimeline:
+ text = "Time";
+ break;
+ case WheelScrub:
+ text = "Scrb";
+ break;
+ case WheelShuttle:
+ text = "Shtl";
+ break;
+ }
+
+ switch (wheel_shift_mode) {
+ case WheelShiftGain:
+ text += ":Gain";
+ break;
+
+ case WheelShiftPan:
+ text += ":Pan ";
+ break;
+
+ case WheelShiftMaster:
+ text += ":Mstr";
+ break;
+
+ case WheelShiftMarker:
+ text += ":Mrkr";
+ break;
+ }
+
+ print (1, 0, text.c_str());
+}
+
+// Was going to keep state around saying to retry or not
+// haven't got to it yet, still not sure it's a good idea
+
+void
+TranzportControlProtocol::print (int row, int col, const char *text) {
+ print_noretry(row,col,text);
+}
+
+void
+TranzportControlProtocol::print_noretry (int row, int col, const char *text)
+{
+ int cell;
+ uint32_t left = strlen (text);
+ char tmp[5];
+ int base_col;
+
+ if (row < 0 || row > 1) {
+ return;
+ }
+
+ if (col < 0 || col > 19) {
+ return;
+ }
+
+ while (left) {
+
+ if (col >= 0 && col < 4) {
+ cell = 0;
+ base_col = 0;
+ } else if (col >= 4 && col < 8) {
+ cell = 1;
+ base_col = 4;
+ } else if (col >= 8 && col < 12) {
+ cell = 2;
+ base_col = 8;
+ } else if (col >= 12 && col < 16) {
+ cell = 3;
+ base_col = 12;
+ } else if (col >= 16 && col < 20) {
+ cell = 4;
+ base_col = 16;
+ } else {
+ return;
+ }
+
+ int offset = col % 4;
+
+ /* copy current cell contents into tmp */
+
+ memcpy (tmp, &screen_pending[row][base_col], 4);
+
+ /* overwrite with new text */
+
+ uint32_t tocopy = min ((4U - offset), left);
+
+ memcpy (tmp+offset, text, tocopy);
+
+ /* copy it back to pending */
+
+ memcpy (&screen_pending[row][base_col], tmp, 4);
+
+ text += tocopy;
+ left -= tocopy;
+ col += tocopy;
+ }
+}
+
+XMLNode&
+TranzportControlProtocol::get_state ()
+{
+ XMLNode* node = new XMLNode (X_("Protocol"));
+ node->add_property (X_("name"), _name);
+ return *node;
+}
+
+int
+TranzportControlProtocol::set_state (const XMLNode& node)
+{
+ return 0;
+}
+
+int
+TranzportControlProtocol::save (char *name)
+{
+ // Presently unimplemented
+ return 0;
+}
+
+int
+TranzportControlProtocol::load (char *name)
+{
+ // Presently unimplemented
+ return 0;
+}
diff --git a/libs/surfaces/frontier/tranzport/tranzport_control_protocol.h b/libs/surfaces/frontier/tranzport/tranzport_control_protocol.h
new file mode 100644
index 0000000000..f13e4a3a44
--- /dev/null
+++ b/libs/surfaces/frontier/tranzport/tranzport_control_protocol.h
@@ -0,0 +1,320 @@
+
+#ifndef ardour_tranzport_control_protocol_h
+#define ardour_tranzport_control_protocol_h
+
+#include <vector>
+
+#include <sys/time.h>
+#include <pthread.h>
+#include <usb.h>
+
+#include <glibmm/thread.h>
+
+#include <ardour/types.h>
+
+#include <control_protocol/control_protocol.h>
+
+class TranzportControlProtocol : public ARDOUR::ControlProtocol
+{
+ public:
+ TranzportControlProtocol (ARDOUR::Session&);
+ virtual ~TranzportControlProtocol();
+
+ int set_active (bool yn);
+
+ static bool probe ();
+
+ XMLNode& get_state ();
+ int set_state (const XMLNode&);
+
+ private:
+ static const int VENDORID = 0x165b;
+ static const int PRODUCTID = 0x8101;
+ static const int READ_ENDPOINT = 0x81;
+ static const int WRITE_ENDPOINT = 0x02;
+ const static int STATUS_OFFLINE = 0xff;
+ const static int STATUS_ONLINE = 0x01;
+ const static uint8_t WheelDirectionThreshold = 0x3f;
+
+ enum LightID {
+ LightRecord = 0,
+ LightTrackrec,
+ LightTrackmute,
+ LightTracksolo,
+ LightAnysolo,
+ LightLoop,
+ LightPunch
+ };
+
+ enum ButtonID {
+ ButtonBattery = 0x00004000,
+ ButtonBacklight = 0x00008000,
+ ButtonTrackLeft = 0x04000000,
+ ButtonTrackRight = 0x40000000,
+ ButtonTrackRec = 0x00040000,
+ ButtonTrackMute = 0x00400000,
+ ButtonTrackSolo = 0x00000400,
+ ButtonUndo = 0x80000000,
+ ButtonIn = 0x02000000,
+ ButtonOut = 0x20000000,
+ ButtonPunch = 0x00800000,
+ ButtonLoop = 0x00080000,
+ ButtonPrev = 0x00020000,
+ ButtonAdd = 0x00200000,
+ ButtonNext = 0x00000200,
+ ButtonRewind = 0x01000000,
+ ButtonFastForward = 0x10000000,
+ ButtonStop = 0x00010000,
+ ButtonPlay = 0x00100000,
+ ButtonRecord = 0x00000100,
+ ButtonShift = 0x08000000
+ };
+
+ enum WheelShiftMode {
+ WheelShiftGain,
+ WheelShiftPan,
+ WheelShiftMaster,
+ WheelShiftMarker
+ };
+
+ enum WheelMode {
+ WheelTimeline,
+ WheelScrub,
+ WheelShuttle
+ };
+
+ // FIXME - look at gtk2_ardour for snap settings
+
+ enum WheelIncrement {
+ WheelIncrSlave,
+ WheelIncrScreen,
+ WheelIncrSample,
+ WheelIncrBeat,
+ WheelIncrBar,
+ WheelIncrSecond,
+ WheelIncrMinute
+ };
+
+ enum DisplayMode {
+ DisplayNormal,
+ DisplayRecording,
+ DisplayRecordingMeter,
+ DisplayBigMeter,
+ DisplayConfig,
+ DisplayBling,
+ DisplayBlingMeter
+ };
+
+ enum BlingMode {
+ BlingOff,
+ BlingKit,
+ BlingRotating,
+ BlingPairs,
+ BlingRows,
+ BlingFlashAll
+ };
+
+ pthread_t thread;
+ uint32_t buttonmask;
+ uint32_t timeout;
+ uint32_t inflight;
+ uint8_t _datawheel;
+ uint8_t _device_status;
+ uint32_t current_track_id;
+ WheelMode wheel_mode;
+ WheelShiftMode wheel_shift_mode;
+ DisplayMode display_mode;
+ BlingMode bling_mode;
+ WheelIncrement wheel_increment;
+ usb_dev_handle* udev;
+
+ ARDOUR::gain_t gain_fraction;
+
+ Glib::Mutex update_lock;
+
+ bool screen_invalid[2][20];
+ char screen_current[2][20];
+ char screen_pending[2][20];
+ char screen_flash[2][20];
+
+ bool lights_invalid[7];
+ bool lights_current[7];
+ bool lights_pending[7];
+ bool lights_flash[7];
+
+ uint32_t last_bars;
+ uint32_t last_beats;
+ uint32_t last_ticks;
+
+ bool last_negative;
+ uint32_t last_hrs;
+ uint32_t last_mins;
+ uint32_t last_secs;
+ uint32_t last_frames;
+ nframes_t last_where;
+ ARDOUR::gain_t last_track_gain;
+ uint32_t last_meter_fill;
+ struct timeval last_wheel_motion;
+ int last_wheel_dir;
+
+ Glib::Mutex io_lock;
+
+ int open ();
+ int read (uint8_t *buf,uint32_t timeout_override = 0);
+ int write (uint8_t* cmd, uint32_t timeout_override = 0);
+ int write_noretry (uint8_t* cmd, uint32_t timeout_override = 0);
+ int close ();
+ int save(char *name = "default");
+ int load(char *name = "default");
+ void print (int row, int col, const char* text);
+ void print_noretry (int row, int col, const char* text);
+
+ int rtpriority_set(int priority = 52);
+ int rtpriority_unset(int priority = 0);
+
+ int open_core (struct usb_device*);
+
+ static void* _monitor_work (void* arg);
+ void* monitor_work ();
+
+ int process (uint8_t *);
+ int update_state();
+ void invalidate();
+ int flush();
+ // bool isuptodate(); // think on this. It seems futile to update more than 30/sec
+
+ // A screen is a cache of what should be on the lcd
+
+ void screen_init();
+ void screen_validate();
+ void screen_invalidate();
+ int screen_flush();
+ void screen_clear();
+ // bool screen_isuptodate(); // think on this -
+
+ // Commands to write to the lcd
+
+ int lcd_init();
+ bool lcd_damage();
+ bool lcd_isdamaged();
+
+ bool lcd_damage(int row, int col = 0, int length = 20);
+ bool lcd_isdamaged(int row, int col = 0, int length = 20);
+
+ int lcd_flush();
+ int lcd_write(uint8_t* cmd, uint32_t timeout_override = 0); // pedantic alias for write
+ void lcd_fill (uint8_t fill_char);
+ void lcd_clear ();
+ void lcd_print (int row, int col, const char* text);
+ void lcd_print_noretry (int row, int col, const char* text);
+
+ // Commands to write to the lights
+ // FIXME - on some devices lights can have intensity and colors
+
+ void lights_init();
+ void lights_validate();
+ void lights_invalidate();
+ void light_validate(LightID light);
+ void light_invalidate(LightID light);
+ int lights_flush();
+ int lights_write(uint8_t* cmd,uint32_t timeout_override = 0); // pedantic alias to write
+
+ // a cache of what should be lit
+
+ void lights_off ();
+ void lights_on ();
+ int light_set(LightID, bool offon = true);
+ int light_on (LightID);
+ int light_off (LightID);
+
+ // some modes for the lights, should probably be renamed
+
+ int lights_show_normal();
+ int lights_show_recording();
+ int lights_show_tempo();
+ int lights_show_bling();
+
+ void enter_big_meter_mode ();
+ void enter_normal_display_mode ();
+ void enter_config_mode();
+ void enter_recording_mode();
+ void enter_bling_mode();
+
+ void next_display_mode ();
+ void normal_update ();
+
+ void show_current_track ();
+ void show_track_gain ();
+ void show_transport_time ();
+ void show_bbt (nframes_t where);
+ void show_smpte (nframes_t where);
+ void show_wheel_mode ();
+ void show_gain ();
+ void show_pan ();
+ void show_meter ();
+
+ void datawheel ();
+ void scrub ();
+ void scroll ();
+ void shuttle ();
+ void config ();
+
+ void next_wheel_mode ();
+ void next_wheel_shift_mode ();
+
+ void set_current_track (ARDOUR::Route*);
+ void next_track ();
+ void prev_track ();
+ void step_gain_up ();
+ void step_gain_down ();
+ void step_pan_right ();
+ void step_pan_left ();
+
+
+ void button_event_battery_press (bool shifted);
+ void button_event_battery_release (bool shifted);
+ void button_event_backlight_press (bool shifted);
+ void button_event_backlight_release (bool shifted);
+ void button_event_trackleft_press (bool shifted);
+ void button_event_trackleft_release (bool shifted);
+ void button_event_trackright_press (bool shifted);
+ void button_event_trackright_release (bool shifted);
+ void button_event_trackrec_press (bool shifted);
+ void button_event_trackrec_release (bool shifted);
+ void button_event_trackmute_press (bool shifted);
+ void button_event_trackmute_release (bool shifted);
+ void button_event_tracksolo_press (bool shifted);
+ void button_event_tracksolo_release (bool shifted);
+ void button_event_undo_press (bool shifted);
+ void button_event_undo_release (bool shifted);
+ void button_event_in_press (bool shifted);
+ void button_event_in_release (bool shifted);
+ void button_event_out_press (bool shifted);
+ void button_event_out_release (bool shifted);
+ void button_event_punch_press (bool shifted);
+ void button_event_punch_release (bool shifted);
+ void button_event_loop_press (bool shifted);
+ void button_event_loop_release (bool shifted);
+ void button_event_prev_press (bool shifted);
+ void button_event_prev_release (bool shifted);
+ void button_event_add_press (bool shifted);
+ void button_event_add_release (bool shifted);
+ void button_event_next_press (bool shifted);
+ void button_event_next_release (bool shifted);
+ void button_event_rewind_press (bool shifted);
+ void button_event_rewind_release (bool shifted);
+ void button_event_fastforward_press (bool shifted);
+ void button_event_fastforward_release (bool shifted);
+ void button_event_stop_press (bool shifted);
+ void button_event_stop_release (bool shifted);
+ void button_event_play_press (bool shifted);
+ void button_event_play_release (bool shifted);
+ void button_event_record_press (bool shifted);
+ void button_event_record_release (bool shifted);
+
+ // new api
+ void button_event_mute (bool pressed, bool shifted);
+};
+
+
+#endif // ardour_tranzport_control_protocol_h