diff --git a/libx52/Makefile b/libx52/Makefile index fe3f265..d0de21f 100644 --- a/libx52/Makefile +++ b/libx52/Makefile @@ -1,6 +1,8 @@ +SRCS := $(wildcard *.c) +HEADERS := $(wildcard *.h) -test_x52: x52control.c x52control.h - gcc -I /usr/include/libusb-1.0/ x52control.c -lusb-1.0 -o $@ +test_x52: ${SRCS} ${HEADERS} + gcc -I /usr/include/libusb-1.0/ ${SRCS} -lusb-1.0 -o $@ .PHONY: clean clean: diff --git a/libx52/libx52.h b/libx52/libx52.h index 03e683b..a40a38f 100644 --- a/libx52/libx52.h +++ b/libx52/libx52.h @@ -1,7 +1,7 @@ /* * Saitek X52 Pro MFD & LED driver * - * Copyright (C) 2012 Nirenjan Krishnan (nirenjan@nirenjan.org) + * Copyright (C) 2012-2015 Nirenjan Krishnan (nirenjan@nirenjan.org) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -12,7 +12,61 @@ #ifndef LIBX52_H #define LIBX52_H +#include +#include + struct libx52_device; typedef struct libx52_device libx52_device; +/** + * @brief Initialize the X52 library + * + * Sets up any internal data structures to access the joystick, all + * calls to libx52 use the returned pointer to control the device. + * + * @returns a pointer to the detected \ref libx52_device + */ +libx52_device * libx52_init(void); + +/** + * @brief Exit the library and free up any resources used + * + * @param[in] dev Pointer to the device + * @returns None + */ +void libx52_exit(libx52_device *dev); + +int libx52_control_transfer(libx52_device *x52, uint16_t index, uint16_t value); +int libx52_set_text(libx52_device *x52, uint8_t line, const char *text, uint8_t length); +int libx52_set_led(libx52_device *x52, uint8_t led, uint8_t state); +int libx52_set_date(libx52_device *x52, uint8_t date, uint8_t month, uint8_t year, uint8_t format); +int libx52_set_time(libx52_device *x52, uint8_t hour, uint8_t minute, uint8_t format); +int libx52_set_clock(libx52_device *x52, time_t time); +int libx52_set_brightness(libx52_device *x52, uint8_t mfd, uint16_t brightness); +int libx52_set_shift(libx52_device *x52, uint8_t state); +int libx52_set_blink(libx52_device *x52, uint8_t state); +int libx52_update(libx52_device *x52); + +/* LED indices */ +#define X52_LED_FIRE 0x01 +#define X52_LED_A_RED 0x02 +#define X52_LED_A_GREEN 0x03 +#define X52_LED_B_RED 0x04 +#define X52_LED_B_GREEN 0x05 +#define X52_LED_D_RED 0x06 +#define X52_LED_D_GREEN 0x07 +#define X52_LED_E_RED 0x08 +#define X52_LED_E_GREEN 0x09 +#define X52_LED_T1_RED 0x0a +#define X52_LED_T1_GREEN 0x0b +#define X52_LED_T2_RED 0x0c +#define X52_LED_T2_GREEN 0x0d +#define X52_LED_T3_RED 0x0e +#define X52_LED_T3_GREEN 0x0f +#define X52_LED_POV_RED 0x10 +#define X52_LED_POV_GREEN 0x11 +#define X52_LED_I_RED 0x12 +#define X52_LED_I_GREEN 0x13 +#define X52_LED_THROTTLE 0x14 + #endif /* !defined LIBX52_H */ diff --git a/libx52/x52_common.h b/libx52/x52_common.h index dc7301a..ee3f997 100644 --- a/libx52/x52_common.h +++ b/libx52/x52_common.h @@ -1,7 +1,7 @@ /* * Saitek X52 Pro MFD & LED driver * - * Copyright (C) 2012 Nirenjan Krishnan (nirenjan@nirenjan.org) + * Copyright (C) 2012-2015 Nirenjan Krishnan (nirenjan@nirenjan.org) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -57,6 +57,7 @@ struct x52_mfd_offs { }; struct libx52_device { + libusb_context *ctx; libusb_device_handle *hdl; struct libusb_transfer *in_xfer; struct libusb_transfer *ctrl_xfer; @@ -65,6 +66,8 @@ struct libx52_device { uint8_t input_buffer[16]; uint32_t led_mask; + uint16_t mfd_brightness; + uint16_t led_brightness; struct x52_mfd_line line[X52_MFD_LINES]; struct x52_mfd_date date; @@ -72,6 +75,7 @@ struct libx52_device { struct x52_mfd_offs offs[2]; }; +/** Indicator bits for update mask */ #define X52_BIT_SHIFT 0 #define X52_BIT_LED_FIRE 1 #define X52_BIT_LED_A_RED 2 diff --git a/libx52/x52control.c b/libx52/x52control.c index b722e37..79c7612 100644 --- a/libx52/x52control.c +++ b/libx52/x52control.c @@ -1,7 +1,7 @@ /* * Saitek X52 Pro MFD & LED driver * - * Copyright (C) 2012 Nirenjan Krishnan (nirenjan@nirenjan.org) + * Copyright (C) 2012-2015 Nirenjan Krishnan (nirenjan@nirenjan.org) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -21,24 +21,14 @@ #include "x52control.h" #include "x52_common.h" -static libusb_context *libx52_ctx; - -#define VENDOR_SAITEK 0x06a3 -#define X52_PROD_X52PRO 0x0762 - -static int libx52_check_product(uint16_t idVendor, uint16_t idProduct) +int libx52_control_transfer(libx52_device *x52, uint16_t index, uint16_t value) { - if (idVendor == VENDOR_SAITEK) { - switch (idProduct) { - case X52_PROD_X52PRO: - return 1; - } - } - - return 0; + return libusb_control_transfer(x52->hdl, + LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, + X52_VENDOR_REQUEST, value, index, NULL, 0, 1000); } -inline void set_bit(uint32_t *value, int bit) +inline void set_bit(uint32_t *value, uint32_t bit) { *value |= (1UL << bit); } @@ -53,6 +43,39 @@ inline uint32_t tst_bit(uint32_t *value, uint32_t bit) return (*value & (1UL << bit)); } +static int libx52_write_line(libx52_device *x52, uint8_t line_index) +{ + uint8_t i; + uint16_t value; + int rc; + + const uint16_t line_index_map[X52_MFD_LINES] = { + X52_MFD_LINE1, + X52_MFD_LINE2, + X52_MFD_LINE3, + }; + + /* Clear the line first */ + rc = libx52_control_transfer(x52, + line_index_map[line_index] | X52_MFD_CLEAR_LINE, 0); + if (rc) { + return rc; + } + + for (i = 0; i < x52->line[line_index].length; i += 2) { + value = x52->line[line_index].text[i + 1] << 8 | + x52->line[line_index].text[i]; + + rc = libx52_control_transfer(x52, + line_index_map[line_index] | X52_MFD_WRITE_LINE, value); + if (rc) { + return rc; + } + } + + return rc; +} + int libx52_set_text(libx52_device *x52, uint8_t line, const char *text, uint8_t length) { if (!x52 || !text) { @@ -68,6 +91,7 @@ int libx52_set_text(libx52_device *x52, uint8_t line, const char *text, uint8_t } memcpy(x52->line[line].text, text, length); + x52->line[line].length = length; set_bit(&x52->update_mask, X52_BIT_MFD_LINE1 + line); return 0; @@ -105,96 +129,231 @@ int libx52_set_date(libx52_device *x52, uint8_t date, uint8_t month, uint8_t yea x52->date.format = format; set_bit(&x52->update_mask, X52_BIT_MFD_DATE); + + return 0; } int libx52_set_time(libx52_device *x52, uint8_t hour, uint8_t minute, uint8_t format) { - if (!x52 || format >= x52_mfd_format_max) { + if (!x52) { return -EINVAL; } x52->time.hour = hour; x52->time.minute = minute; - x52->time.h24 = format; + x52->time.h24 = !!format; set_bit(&x52->update_mask, X52_BIT_MFD_TIME); -} -libx52_device* libx52_init(void) -{ - int rc; - ssize_t count; - int i; - libusb_device **list; - libusb_device *device; - libusb_device_handle *hdl; - struct libusb_device_descriptor desc; - char pname[64]; - libx52_device *x52_dev; - - rc = libusb_init(&libx52_ctx); - if (rc) { - fprintf(stderr, "Unable to initialize libusb; rc=%d\n", rc); - return NULL; - } - libusb_set_debug(libx52_ctx, 3); - - count = libusb_get_device_list(libx52_ctx, &list); - for (i = 0; i < count; i++) { - device = list[i]; - printf("Device %d:\n", i); - printf("\tBus: %d\n", libusb_get_bus_number(device)); - printf("\tAddress: %d\n", libusb_get_device_address(device)); - printf("\tSpeed: %d\n", libusb_get_device_speed(device)); - - if (!libusb_get_device_descriptor(device, &desc)) { - printf("\tVID/PID: %04x %04x\n", desc.idVendor, desc.idProduct); - - if (libx52_check_product(desc.idVendor, desc.idProduct)) { - x52_dev = malloc(sizeof(libx52_device)); - if (!x52_dev) { - fprintf(stderr, "Cannot allocate libx52_device\n"); - return NULL; - } - - rc = libusb_open(device, &hdl); - if (rc) { - free(x52_dev); - } - - x52_dev->hdl = hdl; - - if(libusb_get_string_descriptor_ascii(hdl, desc.iManufacturer, pname, 64) > 0 || 1) { - printf("\tManufacturer: %s\n", pname); - } - - if(libusb_get_string_descriptor_ascii(hdl, desc.iProduct, pname, 64) > 0 || 1) { - printf("\tProduct: %s\n", pname); - } - } - } - printf("\n"); - } - libusb_free_device_list(list, 1); - - return x52_dev; -} - -void libx52_exit(libx52_device *dev) -{ - libusb_close(dev->hdl); - free(dev); - libusb_exit(libx52_ctx); -} - -int main() -{ - int rc; - ssize_t count; - int i; - libx52_device *dev; - - dev = libx52_init(); - libx52_exit(dev); return 0; } + +static int libx52_write_date(libx52_device *x52) +{ + uint16_t value1; //dd-mm + uint16_t value2; //yy + int rc; + + switch (x52->date.format) { + case x52_mfd_format_yymmdd: + value1 = x52->date.month << 8 | + x52->date.year; + value2 = x52->date.day; + break; + + case x52_mfd_format_mmddyy: + value1 = x52->date.day << 8 | + x52->date.month; + value2 = x52->date.year; + break; + + case x52_mfd_format_ddmmyy: + value1 = x52->date.month << 8 | + x52->date.day; + value2 = x52->date.year; + break; + + default: + return -EINVAL; + } + + rc = libx52_control_transfer(x52, X52_DATE_DDMM, value1); + if (rc == 0) { + rc = libx52_control_transfer(x52, X52_DATE_YEAR, value2); + } + + return rc; +} + +int libx52_set_brightness(libx52_device *x52, uint8_t mfd, uint16_t brightness) +{ + if (!x52) { + return -EINVAL; + } + + if (mfd) { + x52->mfd_brightness = brightness; + set_bit(&x52->update_mask, X52_BIT_BRI_MFD); + } else { + x52->led_brightness = brightness; + set_bit(&x52->update_mask, X52_BIT_BRI_LED); + } + + return 0; +} + +int libx52_set_shift(libx52_device *x52, uint8_t state) +{ + if (!x52) { + return -EINVAL; + } + + if (state) { + set_bit(&x52->led_mask, X52_BIT_SHIFT); + } else { + clr_bit(&x52->led_mask, X52_BIT_SHIFT); + } + + set_bit(&x52->update_mask, X52_BIT_SHIFT); + return 0; +} + +int libx52_set_blink(libx52_device *x52, uint8_t state) +{ + if (!x52) { + return -EINVAL; + } + + if (state) { + set_bit(&x52->led_mask, X52_BIT_POV_BLINK); + } else { + clr_bit(&x52->led_mask, X52_BIT_POV_BLINK); + } + + set_bit(&x52->update_mask, X52_BIT_POV_BLINK); + return 0; +} + +int libx52_set_clock(libx52_device *x52, time_t time) +{ + struct tm timeval; + if (!x52) { + return -EINVAL; + } + + timeval = *localtime(&time); + + x52->date.day = timeval.tm_mday; + x52->date.month = timeval.tm_mon + 1; + x52->date.year = timeval.tm_year % 100; + x52->time.hour = timeval.tm_hour; + x52->time.minute = timeval.tm_min; + + set_bit(&x52->update_mask, X52_BIT_MFD_DATE); + set_bit(&x52->update_mask, X52_BIT_MFD_TIME); + return 0; +} + +int libx52_update(libx52_device *x52) +{ + unsigned int i; + uint32_t update_mask; + uint16_t value; + int rc; + + /* Save the update mask */ + update_mask = x52->update_mask; + /* Reset the device update mask to 0 */ + x52->update_mask = 0; + + for (i = 0; i < 32; i++) { + if (tst_bit(&update_mask, i)) { + switch (i) { + case X52_BIT_SHIFT: + value = tst_bit(&x52->led_mask, X52_BIT_SHIFT) ? X52_SHIFT_ON : X52_SHIFT_OFF; + rc = libx52_control_transfer(x52, X52_SHIFT_INDICATOR, value); + break; + + case X52_BIT_LED_FIRE: + case X52_BIT_LED_A_RED: + case X52_BIT_LED_A_GREEN: + case X52_BIT_LED_B_RED: + case X52_BIT_LED_B_GREEN: + case X52_BIT_LED_D_RED: + case X52_BIT_LED_D_GREEN: + case X52_BIT_LED_E_RED: + case X52_BIT_LED_E_GREEN: + case X52_BIT_LED_T1_RED: + case X52_BIT_LED_T1_GREEN: + case X52_BIT_LED_T2_RED: + case X52_BIT_LED_T2_GREEN: + case X52_BIT_LED_T3_RED: + case X52_BIT_LED_T3_GREEN: + case X52_BIT_LED_POV_RED: + case X52_BIT_LED_POV_GREEN: + case X52_BIT_LED_I_RED: + case X52_BIT_LED_I_GREEN: + case X52_BIT_LED_THROTTLE: + /* The bits correspond exactly to the LED identifiers */ + value = tst_bit(&x52->led_mask, i) ? 1 : 0; + rc = libx52_control_transfer(x52, X52_LED, value | (i << 8)); + break; + + case X52_BIT_MFD_LINE1: + rc = libx52_write_line(x52, 0); + break; + + case X52_BIT_MFD_LINE2: + rc = libx52_write_line(x52, 1); + break; + + case X52_BIT_MFD_LINE3: + rc = libx52_write_line(x52, 2); + break; + + case X52_BIT_POV_BLINK: + value = tst_bit(&x52->led_mask, X52_BIT_POV_BLINK) ? X52_BLINK_ON : X52_BLINK_OFF; + rc = libx52_control_transfer(x52, X52_BLINK_INDICATOR, value); + break; + + case X52_BIT_BRI_MFD: + rc = libx52_control_transfer(x52, X52_MFD_BRIGHTNESS, + x52->mfd_brightness); + break; + + case X52_BIT_BRI_LED: + rc = libx52_control_transfer(x52, X52_LED_BRIGHTNESS, + x52->led_brightness); + break; + + case X52_BIT_MFD_DATE: + rc = libx52_write_date(x52); + break; + + case X52_BIT_MFD_TIME: + value = x52->time.h24 << 15 | + x52->time.hour << 8 | + x52->time.minute; + rc = libx52_control_transfer(x52, X52_TIME_CLOCK1, value); + break; + + case X52_BIT_MFD_OFFS1: + case X52_BIT_MFD_OFFS2: + default: + /* Ignore any spurious bits */ + rc = 0; + break; + } + + if (rc == 0) { + clr_bit(&update_mask, i); + } else { + /* Last transfer failed - reset the update mask */ + x52->update_mask = update_mask; + break; + } + } + } + + return rc; +} diff --git a/libx52/x52control.h b/libx52/x52control.h index d8396bc..45adec8 100644 --- a/libx52/x52control.h +++ b/libx52/x52control.h @@ -1,7 +1,7 @@ /* * Saitek X52 Pro MFD & LED driver * - * Copyright (C) 2012 Nirenjan Krishnan (nirenjan@nirenjan.org) + * Copyright (C) 2012-2015 Nirenjan Krishnan (nirenjan@nirenjan.org) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -50,26 +50,4 @@ #define X52_BLINK_ON 0x51 #define X52_BLINK_OFF 0x50 -/* LED indices */ -#define X52_LED_FIRE 0x01 -#define X52_LED_A_RED 0x02 -#define X52_LED_A_GREEN 0x03 -#define X52_LED_B_RED 0x04 -#define X52_LED_B_GREEN 0x05 -#define X52_LED_D_RED 0x06 -#define X52_LED_D_GREEN 0x07 -#define X52_LED_E_RED 0x08 -#define X52_LED_E_GREEN 0x09 -#define X52_LED_T1_RED 0x0a -#define X52_LED_T1_GREEN 0x0b -#define X52_LED_T2_RED 0x0c -#define X52_LED_T2_GREEN 0x0d -#define X52_LED_T3_RED 0x0e -#define X52_LED_T3_GREEN 0x0f -#define X52_LED_POV_RED 0x10 -#define X52_LED_POV_GREEN 0x11 -#define X52_LED_I_RED 0x12 -#define X52_LED_I_GREEN 0x13 -#define X52_LED_THROTTLE 0x14 - #endif /* !defined X52JOY_COMMANDS_H */ diff --git a/libx52/x52core.c b/libx52/x52core.c new file mode 100644 index 0000000..93d5fe1 --- /dev/null +++ b/libx52/x52core.c @@ -0,0 +1,94 @@ +/* + * Saitek X52 Pro MFD & LED driver + * + * Copyright (C) 2012-2015 Nirenjan Krishnan (nirenjan@nirenjan.org) + * + * 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. + * + */ + +#include +#include +#include +#include +#include + +#include + +#include "libx52.h" +#include "x52control.h" +#include "x52_common.h" + +#define VENDOR_SAITEK 0x06a3 +#define X52_PROD_X52PRO 0x0762 + +/* Check if the USB device is supported by this library */ +static int libx52_check_product(uint16_t idVendor, uint16_t idProduct) +{ + if (idVendor == VENDOR_SAITEK) { + switch (idProduct) { + case X52_PROD_X52PRO: + return 1; + } + } + + return 0; +} + +libx52_device* libx52_init(void) +{ + int rc; + ssize_t count; + int i; + libusb_device **list; + libusb_device *device; + libusb_device_handle *hdl; + struct libusb_device_descriptor desc; + libx52_device *x52_dev; + + /* Allocate memory for the library's data structures */ + x52_dev = malloc(sizeof(libx52_device)); + if (!x52_dev) { + errno = ENOMEM; + return NULL; + } + + rc = libusb_init(&(x52_dev->ctx)); + if (rc) { + errno = ELIBACC; + goto err_recovery; + } + libusb_set_debug(x52_dev->ctx, 3); + + count = libusb_get_device_list(x52_dev->ctx, &list); + for (i = 0; i < count; i++) { + device = list[i]; + if (!libusb_get_device_descriptor(device, &desc)) { + if (libx52_check_product(desc.idVendor, desc.idProduct)) { + rc = libusb_open(device, &hdl); + if (rc) { + goto err_recovery; + } + + x52_dev->hdl = hdl; + break; + } + } + } + libusb_free_device_list(list, 1); + + return x52_dev; +err_recovery: + free(x52_dev); + return NULL; +} + +void libx52_exit(libx52_device *dev) +{ + libusb_close(dev->hdl); + libusb_exit(dev->ctx); + free(dev); +} + diff --git a/libx52/x52test.c b/libx52/x52test.c new file mode 100644 index 0000000..9226fc6 --- /dev/null +++ b/libx52/x52test.c @@ -0,0 +1,51 @@ +#include +#include +#include +#include +#include +#include + +#include "libx52.h" + +int main() +{ + uint32_t i; + uint32_t j; + char str[16]; + char c; + + libx52_device *dev; + + dev = libx52_init(); + + libx52_set_brightness(dev, 1, 127); + libx52_set_brightness(dev, 0, 127); + + libx52_set_led(dev, X52_LED_FIRE, 1); + + libx52_set_text(dev, 1, "0123456789ABCDEF", 16); + + for (i = 0; i < 256; i+=16) { + c = i >> 4; + c += (c < 10) ? 0x30 : 0x37; + for (j = 0; j < 16; j++) { + str[j] = c; + } + libx52_set_text(dev, 0, str, 16); + + for (j = 0; j < 16; j++) { + str[j] = i + j; + } + libx52_set_text(dev, 2, str, 16); + libx52_update(dev); + sleep(1); + } + + libx52_set_text(dev, 0, " Saitek X52 Pro ", 16); + libx52_set_text(dev, 1, " Flight ", 16); + libx52_set_text(dev, 2, " Control System ", 16); + libx52_update(dev); + + libx52_exit(dev); + return 0; +}