diff options
author | Sebastian Kuzminsky <seb@highlab.com> | 2011-01-12 23:59:59 -0700 |
---|---|---|
committer | Sebastian Kuzminsky <seb@highlab.com> | 2011-01-13 11:32:23 -0700 |
commit | c6118997d8d98ae52fd73d0d016142c5981986a4 (patch) | |
tree | b843fdcb53b7b9f18c4c9f6a17adf4b187990110 | |
parent | 9dcbf97dea9ca7c2761efe4ae424754d0c5ea83e (diff) | |
download | linuxcnc-c6118997d8d98ae52fd73d0d016142c5981986a4.tar.gz linuxcnc-c6118997d8d98ae52fd73d0d016142c5981986a4.zip |
add a driver for the Contour Design ShuttleXpress
-rw-r--r-- | docs/man/man1/shuttlexpress.1 | 41 | ||||
-rw-r--r-- | src/hal/user_comps/Submakefile | 9 | ||||
-rw-r--r-- | src/hal/user_comps/shuttlexpress.c | 359 |
3 files changed, 409 insertions, 0 deletions
diff --git a/docs/man/man1/shuttlexpress.1 b/docs/man/man1/shuttlexpress.1 new file mode 100644 index 000000000..fa76db70c --- /dev/null +++ b/docs/man/man1/shuttlexpress.1 @@ -0,0 +1,41 @@ +.TH SHUTTLEXPRESS "1" "2011-01-13" "EMC Documentation" "HAL User's Manual" +.de TQ +.br +.ns +.TP \\$1 +.. +.SH NAME +shuttlexpress \- control HAL pins with the ShuttleXpress device made by Contour Design +.SH SYNOPSIS +\fIloadusr\fR \fBshuttlexpress\fR \fI[DEVICE ...]\fR +.SH DESCRIPTION +shuttlexpress is a userspace HAL component that interfaces Contour Design's +ShuttleXpress device with EMC2's HAL. The ShuttleXpress has five momentary +buttons, a 10 count/revolution jog wheel with detents, and a 15-position +spring-loaded outer wheel that returns to center when released. +.PP +If it is started without command-line arguments, it will probe all +/dev/hidraw* device files for ShuttleXpress devices, and use all devices +found. If it is started with command-line arguments, only will only +probe the devices specified. +.SH UDEV +The shuttlexpress module needs read permission on the /dev/hidraw* +device files. This can be accomplished by adding a file +\fB/etc/udev/rules.d/99-shuttlexpress.rules\fR, with the following contents: + +SUBSYSTEM=="hidraw", ATTRS{idVendor}=="0b33", ATTRS{idProduct}=="0020", MODE="0444" + +.SH A warning about the Jog Wheel +The ShuttleXpress device has an internal 8-bit counter for the current +jog-wheel position. The shuttlexpress driver can not know this value +until the ShuttleXpress device sends its first event. When the first +event comes into the driver, the driver uses the device's then-current +jog-wheel count as 0. + +This means that if the first event is generated by a jog-wheel move, +that first move will be lost. + +Any user interaction with the ShuttleXpress device will generate an event, +informing the driver of the jog-wheel position. So if you (for example) +push one of the buttons at startup, the jog-wheel will work fine and +notice the first click. diff --git a/src/hal/user_comps/Submakefile b/src/hal/user_comps/Submakefile index 16bfeec08..dd848dca7 100644 --- a/src/hal/user_comps/Submakefile +++ b/src/hal/user_comps/Submakefile @@ -14,13 +14,22 @@ MODBUSLDFLAGS := $(GLIB_LIBS) MODBUSCCFLAGS := $(GLIB_CFLAGS) USERSRCS += $(MODBUSSRCS) +SHUTTLEXPRESS_SRC = hal/user_comps/shuttlexpress.c +USERSRCS += $(SHUTTLEXPRESS_SRC) + $(call TOOBJSDEPS, $(MODBUSSRCS)) : EXTRAFLAGS += $(MODBUSCCFLAGS) + $(call TOOBJSDEPS, $(SHUTTLEXPRESS_SRC)) : ../bin/gs2_vfd: $(call TOOBJS, $(MODBUSSRCS)) ../lib/libemchal.so.0 $(ECHO) Linking $(notdir $@) @$(CC) $(LDFLAGS) $(MODBUSLDFLAGS) -o $@ $^ TARGETS += ../bin/gs2_vfd +../bin/shuttlexpress: $(call TOOBJS, $(SHUTTLEXPRESS_SRC)) ../lib/libemchal.so.0 + $(ECHO) Linking $(notdir $@) + @$(CC) $(LDFLAGS) -o $@ $^ +TARGETS += ../bin/shuttlexpress + ../include/%.h: ./hal/user_comps/%.h cp $^ $@ ../include/%.hh: ./hal/user_comps/%.hh diff --git a/src/hal/user_comps/shuttlexpress.c b/src/hal/user_comps/shuttlexpress.c new file mode 100644 index 000000000..7956ae6de --- /dev/null +++ b/src/hal/user_comps/shuttlexpress.c @@ -0,0 +1,359 @@ + +#include <errno.h> +#include <fcntl.h> +#include <glob.h> +#include <signal.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include <linux/hidraw.h> + +#include "hal.h" + + + + +#define Max(a, b) ((a) > (b) ? (a) : (b)) + + + + +// USB Vendor and Product IDs +#define VENDOR_ID 0x0b33 // Contour Design +#define PRODUCT_ID 0x0020 // ShuttleXpress + + +// each packet from the ShuttleXpress is this many bytes +#define PACKET_LEN 5 + + +// the module name, and prefix for all HAL pins +char *modname = "shuttlexpress"; + + +int hal_comp_id; + + + + +// each ShuttleXpress presents this interface to HAL +struct shuttlexpress_hal { + hal_bit_t *button_0; + hal_bit_t *button_0_not; + hal_bit_t *button_1; + hal_bit_t *button_1_not; + hal_bit_t *button_2; + hal_bit_t *button_2_not; + hal_bit_t *button_3; + hal_bit_t *button_3_not; + hal_bit_t *button_4; + hal_bit_t *button_4_not; + hal_s32_t *count; // accumulated counts from the jog wheel + hal_s32_t *rotation; // current position of the springy outer wheel (FIXME: for lack of a better name...) +}; + + +struct shuttlexpress { + int fd; + char *device_file; + struct shuttlexpress_hal *hal; + int read_first_event; + int prev_count; +}; + + +// this will become an array of all the ShuttleXpress devices we're using +struct shuttlexpress **shuttlexpress = NULL; +int num_devices = 0; + + + + +static void exit_handler(int sig) { + printf("%s: exiting\n", modname); + exit(0); +} + + +static void call_hal_exit(void) { + hal_exit(hal_comp_id); +} + + + + +int read_update(struct shuttlexpress *s) { + int r; + int8_t packet[PACKET_LEN]; + + r = read(s->fd, packet, PACKET_LEN); + if (r < 0) { + fprintf(stderr, "%s: error reading %s: %s\n", modname, s->device_file, strerror(errno)); + return -1; + } else if (r == 0) { + fprintf(stderr, "%s: EOF on %s\n", modname, s->device_file); + return -1; + } + + *s->hal->button_0 = packet[3] & 0x10; + *s->hal->button_0_not = !*s->hal->button_0; + *s->hal->button_1 = packet[3] & 0x20; + *s->hal->button_1_not = !*s->hal->button_1; + *s->hal->button_2 = packet[3] & 0x40; + *s->hal->button_2_not = !*s->hal->button_2; + *s->hal->button_3 = packet[3] & 0x80; + *s->hal->button_3_not = !*s->hal->button_3; + *s->hal->button_4 = packet[4] & 0x01; + *s->hal->button_4_not = !*s->hal->button_4; + + { + int curr_count = packet[1]; + + if (s->read_first_event == 0) { + *s->hal->count = 0; + s->prev_count = curr_count; + s->read_first_event = 1; + } else { + int diff_count = curr_count - s->prev_count; + if (diff_count > 128) diff_count -= 256; + if (diff_count < -128) diff_count += 256; + *s->hal->count += diff_count; + s->prev_count = curr_count; + } + } + + *s->hal->rotation = packet[0]; + + return 0; +} + + +struct shuttlexpress *check_for_shuttlexpress(char *dev_filename) { + struct shuttlexpress *s; + struct hidraw_devinfo devinfo; + char name[100]; + int r; + + printf("%s: checking %s\n", modname, dev_filename); + + s = (struct shuttlexpress *)calloc(1, sizeof(struct shuttlexpress)); + if (s == NULL) { + fprintf(stderr, "%s: out of memory!\n", modname); + return NULL; + } + + s->device_file = dev_filename; + + s->fd = open(s->device_file, O_RDONLY); + if (s->fd < 0) { + fprintf(stderr, "%s: error opening %s: %s\n", modname, s->device_file, strerror(errno)); + if (errno == EACCES) { + fprintf(stderr, "%s: make sure you have read permission on %s, read the shuttlexpress(1) manpage for more info\n", modname, s->device_file); + } + goto fail0; + } + + + r = ioctl(s->fd, HIDIOCGRAWINFO, &devinfo); + if (r < 0) { + fprintf(stderr, "%s: error with ioctl HIDIOCGRAWINFO on %s: %s\n", modname, s->device_file, strerror(errno)); + goto fail1; + } + + if (devinfo.vendor != VENDOR_ID) { + fprintf(stderr, "%s: dev %s has unexpected Vendor ID 0x%04x (expected Contour Design, 0x%04x)\n", modname, s->device_file, devinfo.vendor, VENDOR_ID); + goto fail1; + } + + if (devinfo.product != PRODUCT_ID) { + fprintf(stderr, "%s: dev %s has unexpected Product ID 0x%04x (expected ShuttleXpress, 0x%04x)\n", modname, s->device_file, devinfo.product, PRODUCT_ID); + goto fail1; + } + + r = ioctl(s->fd, HIDIOCGRAWNAME(99), name); + if (r < 0) { + fprintf(stderr, "%s: error with ioctl HIDIOCGRAWNAME on %s: %s\n", modname, s->device_file, strerror(errno)); + goto fail1; + } + printf("%s: found %s on %s\n", modname, name, s->device_file); + + + s->hal = (struct shuttlexpress_hal *)hal_malloc(sizeof(struct shuttlexpress_hal)); + if (s->hal == NULL) { + fprintf(stderr, "%s: ERROR: unable to allocate HAL shared memory\n", modname); + goto fail1; + } + + r = hal_pin_bit_newf(HAL_OUT, &(s->hal->button_0), hal_comp_id, "%s.%d.button-0", modname, num_devices); + if (r != 0) goto fail1; + + r = hal_pin_bit_newf(HAL_OUT, &(s->hal->button_0_not), hal_comp_id, "%s.%d.button-0-not", modname, num_devices); + if (r != 0) goto fail1; + + r = hal_pin_bit_newf(HAL_OUT, &(s->hal->button_1), hal_comp_id, "%s.%d.button-1", modname, num_devices); + if (r != 0) goto fail1; + + r = hal_pin_bit_newf(HAL_OUT, &(s->hal->button_1_not), hal_comp_id, "%s.%d.button-1-not", modname, num_devices); + if (r != 0) goto fail1; + + r = hal_pin_bit_newf(HAL_OUT, &(s->hal->button_2), hal_comp_id, "%s.%d.button-2", modname, num_devices); + if (r != 0) goto fail1; + + r = hal_pin_bit_newf(HAL_OUT, &(s->hal->button_2_not), hal_comp_id, "%s.%d.button-2-not", modname, num_devices); + if (r != 0) goto fail1; + + r = hal_pin_bit_newf(HAL_OUT, &(s->hal->button_3), hal_comp_id, "%s.%d.button-3", modname, num_devices); + if (r != 0) goto fail1; + + r = hal_pin_bit_newf(HAL_OUT, &(s->hal->button_3_not), hal_comp_id, "%s.%d.button-3-not", modname, num_devices); + if (r != 0) goto fail1; + + r = hal_pin_bit_newf(HAL_OUT, &(s->hal->button_4), hal_comp_id, "%s.%d.button-4", modname, num_devices); + if (r != 0) goto fail1; + + r = hal_pin_bit_newf(HAL_OUT, &(s->hal->button_4_not), hal_comp_id, "%s.%d.button-4-not", modname, num_devices); + if (r != 0) goto fail1; + + r = hal_pin_s32_newf(HAL_OUT, &(s->hal->count), hal_comp_id, "%s.%d.count", modname, num_devices); + if (r != 0) goto fail1; + + r = hal_pin_s32_newf(HAL_OUT, &(s->hal->rotation), hal_comp_id, "%s.%d.rotation", modname, num_devices); + if (r != 0) goto fail1; + + *s->hal->button_0 = 0; + *s->hal->button_0_not = 1; + *s->hal->button_1 = 0; + *s->hal->button_1_not = 1; + *s->hal->button_2 = 0; + *s->hal->button_2_not = 1; + *s->hal->button_3 = 0; + *s->hal->button_3_not = 1; + *s->hal->button_4 = 0; + *s->hal->button_4_not = 1; + *s->hal->count = 0; + *s->hal->rotation = 0; + + return s; + + +fail1: + close(s->fd); + +fail0: + free(s); + return NULL; +} + + + + +int main(int argc, char *argv[]) { + int i; + glob_t glob_buffer; + + char **names; + int num_names; + + + hal_comp_id = hal_init(modname); + if (hal_comp_id < 1) { + fprintf(stderr, "%s: ERROR: hal_init failed\n", modname); + exit(1); + } + + signal(SIGINT, exit_handler); + signal(SIGTERM, exit_handler); + atexit(call_hal_exit); + + + // get the list of device filenames to check for ShuttleXpress devices + if (argc > 1) { + // list of devices provided on the command line + names = &argv[1]; + num_names = argc - 1; + } else { + // probe for /dev/hidraw* + int r; + + r = glob("/dev/hidraw*", 0, NULL, &glob_buffer); + if (r == GLOB_NOMATCH) { + fprintf(stderr, "%s: no /dev/hidraw* found, is device plugged in?\n", modname); + exit(1); + } else if (r != 0) { + fprintf(stderr, "%s: error with glob!\n", modname); + exit(1); + } + names = glob_buffer.gl_pathv; + num_names = glob_buffer.gl_pathc; + + // the pathnames we got from glob(3) are used in the shuttlexpress array, so we intentionally dont call globfree(3) + } + + + // probe for ShuttleXpress devices on all those device file names + for (i = 0; i < num_names; i ++) { + struct shuttlexpress *s; + s = check_for_shuttlexpress(names[i]); + if (s == NULL) continue; + + num_devices ++; + shuttlexpress = (struct shuttlexpress **)realloc(shuttlexpress, (num_devices * sizeof(struct shuttlexpress *))); + if (shuttlexpress == NULL) { + fprintf(stderr, "%s: out of memory!\n", modname); + exit(1); + } + shuttlexpress[num_devices - 1] = s; + } + + + if (num_devices == 0) { + fprintf(stderr, "%s: no devices found\n", modname); + exit(1); + } + + + hal_ready(hal_comp_id); + + + // select on all the hidraw devices, process events from the active ones + while (1) { + fd_set readers; + int max_fd; + int i; + int r; + + FD_ZERO(&readers); + max_fd = -1; + + for (i = 0; i < num_devices; i ++) { + FD_SET(shuttlexpress[i]->fd, &readers); + max_fd = Max(max_fd, shuttlexpress[i]->fd); + } + + r = select(max_fd + 1, &readers, NULL, NULL, NULL); + if (r < 0) { + if ((errno == EAGAIN) || (errno == EINTR)) continue; + fprintf(stderr, "%s: error with select!\n", modname); + exit(1); + } + + for (i = 0; i < num_devices; i ++) { + if (FD_ISSET(shuttlexpress[i]->fd, &readers)) { + r = read_update(shuttlexpress[i]); + if (r < 0) { + exit(1); + } + } + } + } + + exit(0); +} + |