# HG changeset patch # User Peter Meerwald # Date 1357214919 -3600 # Node ID c6e3b362f9cfeb7aff61f1695c8fa374b327735c import diff -r 000000000000 -r c6e3b362f9cf hid-usb-to-ir.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hid-usb-to-ir.c Thu Jan 03 13:08:39 2013 +0100 @@ -0,0 +1,280 @@ +/* + * Demo program for the Silabs IR Gesture USB Reference Design, + * http://www.silabs.com/products/sensors/Pages/HID-USB-to-IR-Reference-Design.aspx + * + * Reads the part number of the cp2112 bridge, + * http://www.silabs.com/products/interface/usbtouart/Pages/HID-USB-to-SMBus-Bridge.aspx, + * and the part/revision/sequencer version of the si1143 via SMBus/I2C, + * finally lets the four blue LEDs dance :). + * + * Copyright (c) 2012 Peter Meerwald, + * Released under GPLv3 license, see http://www.gnu.org/licenses/gpl-3.0.html. + * + * Depends on the HID API multi-platform library, available from + * http://www.signal11.us/oss/hidapi/. + * + * Build with + * gcc -Wall -g -o hid-usb-to-ir hid-usb-to-ir.c `pkg-config hidapi-hidraw --libs` + * + * May need root permission to access USB device (or appropriate udev rule). + * + * Based upon information from Silabs AN495 v0.2. + */ + +#include +#include +#include +#include +#include "hidapi/hidapi.h" + +#define SI114X_SLAVE_ADDR 0x5a +#define SI114X_HWKEY 0x07 +#define SI114x_PART_ID 0x00 +#define SI114x_REV_ID 0x01 +#define SI114x_SEQ_ID 0x02 + +#define CP2112_GETSET_GPIO_CONFIG 0x02 +#define CP2112_GET_GPIO 0x03 +#define CP2112_SET_GPIO 0x04 +#define CP2122_GET_VERSION_INFO 0x05 +#define CP2112_GETSET_SMBUS_CONFIG 0x06 +#define CP2112_DATA_WRITE_READ_REQ 0x11 +#define CP2112_DATA_READ_RESP 0x13 +#define CP2112_DATA_WRITE 0x14 +#define CP2112_TRANSFER_STATUS_REQ 0x15 +#define CP2112_TRANSFER_STATUS_RESP 0x16 + +#define CP2112_LED_DOWN 0x01 /* LED DS2, GPIO 0 */ +#define CP2112_LED_UP 0x02 /* LED DS3, GPIO 1 */ +#define CP2112_LED_RIGHT 0x08 /* LED DS8, GPIO 3 */ +#define CP2112_LED_LEFT 0x80 /* LED DS4, GPIO 7 */ +#define CP2112_LEDS_MASK (CP2112_LED_DOWN | CP2112_LED_UP | \ + CP2112_LED_RIGHT | CP2112_LED_LEFT) + +/* get cp2112 part number and version */ +static int cp2112_get_version(hid_device *hd, unsigned char data[2]) { + unsigned char buf[3] = { CP2122_GET_VERSION_INFO, }; + + int ret = hid_get_feature_report(hd, buf, sizeof(buf)); + if (ret < 0) { + fprintf(stderr, "hid_get_feature_report() failed: %ls\n", + hid_error(hd)); + return -1; + } + + data[0] = buf[1]; /* part number */ + data[1] = buf[2]; /* version */ + + return 0; +} + +/* configures cp2112 to automatically send read data, see AN495 section 4.6 */ +static int cp2112_set_auto_send_read(hid_device *hd, int on_off) { + unsigned char buf[14] = { CP2112_GETSET_SMBUS_CONFIG, }; + int ret = hid_get_feature_report(hd, buf, sizeof(buf)); + if (ret < 0) { + fprintf(stderr, "hid_get_feature_report() failed: %ls\n", + hid_error(hd)); + return -1; + } + + buf[6] = on_off; + + ret = hid_send_feature_report(hd, buf, sizeof(buf)); + if (ret < 0) { + fprintf(stderr, "hid_send_feature_report() failed: %ls\n", + hid_error(hd)); + return -1; + } + + return 0; +} + +/* see if the cp2112 is idle */ +static int cp2112_is_idle(hid_device *hd) { + unsigned char status_req[2] = { CP2112_TRANSFER_STATUS_REQ, 0x01, }; + if (hid_write(hd, status_req, sizeof(status_req)) < 0) { + fprintf(stderr, "hid_write() failed: %ls\n", + hid_error(hd)); + return -1; + } + + unsigned char status_resp[7] = { 0x00, }; + if (hid_read(hd, status_resp, sizeof(status_resp)) < 0) { + fprintf(stderr, "hid_read() failed: %ls\n", + hid_error(hd)); + return -1; + } + + if (status_resp[0] == CP2112_TRANSFER_STATUS_RESP) { + if (status_resp[1] == 0x00) /* is idle? */ + return 1; + } + + return 0; +} + +/* write a data byte to register reg of the si1143 */ +static int cp2112_write_byte(hid_device *hd, unsigned char reg, unsigned char data) { + /* 0x5a is the 7-bit SMBus slave address of the si1143 */ + unsigned char buf_out[5] = { CP2112_DATA_WRITE, SI114X_SLAVE_ADDR<<1, 0x02, }; + buf_out[3] = reg; + buf_out[4] = data; + int ret = hid_write(hd, buf_out, sizeof(buf_out)); + if (ret < 0) { + fprintf(stderr, "hid_write() failed: %ls\n", + hid_error(hd)); + return -1; + } + + return 0; +} + +/* read a data byte from register reg of the si1143 */ +static int cp2112_read_byte(hid_device *hd, unsigned char reg, unsigned char data[1]) { + /* 0x5a is the 7-bit SMBus slave address of the si1143 */ + unsigned char buf_out[6] = { CP2112_DATA_WRITE_READ_REQ, + SI114X_SLAVE_ADDR<<1, 0x00, 0x01, 0x01, }; + buf_out[5] = reg; + int ret = hid_write(hd, buf_out, sizeof(buf_out)); + if (ret < 0) { + fprintf(stderr, "hid_write() failed: %ls\n", + hid_error(hd)); + return -1; + } + + /* FIXME: may loop forever, error handling needed */ + while (1) { + + unsigned char buf_in[8] = { 0x00, }; + ret = hid_read(hd, buf_in, sizeof(buf_in)); + if (ret < 0) { + fprintf(stderr, "hid_read_() failed: %ls\n", + hid_error(hd)); + return -1; + } + + + if (buf_in[0] == CP2112_TRANSFER_STATUS_REQ) + continue; + + if (buf_in[0] == CP2112_DATA_READ_RESP) { + if (buf_in[1] == 0x02 && buf_in[2] == 0x00) + continue; /* no data yet */ + if (buf_in[1] == 0x02 && buf_in[2] == 0x01) { + data[0] = buf_in[3]; /* data available */ + return 0; + } + } + } + + return -1; +} + +/* configure cp2112 GPIO pins */ +static int cp2112_config_gpio(hid_device *hd) { + unsigned char buf[5] = { CP2112_GETSET_GPIO_CONFIG, }; + int ret = hid_get_feature_report(hd, buf, sizeof(buf)); + if (ret < 0) { + fprintf(stderr, "hid_get_feature_report() failed: %ls\n", + hid_error(hd)); + return -1; + } + + buf[1] = 0x8b; /* output direction for GPIOs 0, 1, 3, 7 */ + buf[2] = 0x8b; /* push-pull for GPIOs 0, 1, 3, 7 */ + buf[3] = 0x00; /* no special functions, i.e. use pins as GPIO */ + + ret = hid_send_feature_report(hd, buf, sizeof(buf)); + if (ret < 0) { + fprintf(stderr, "hid_send_feature_report() failed: %ls\n", + hid_error(hd)); + return -1; + } + + return 0; +} + +/* set cp2112 GPIO pins */ +static int cp2112_set_gpio(hid_device *hd, unsigned char mask, unsigned char value) { + unsigned char buf[3] = { CP2112_SET_GPIO, }; + + buf[1] = ~value; /* set GPIO pins: 0 .. turn LED on, 1 .. off */ + buf[2] = mask; /* mask of pins to set, ignore others */ + + int ret = hid_send_feature_report(hd, buf, sizeof(buf)); + if (ret < 0) { + fprintf(stderr, "hid_send_feature_report() failed: %ls\n", + hid_error(hd)); + return -1; + } + + return 0; +} + +int main() { + if (hid_init() < 0) { + fprintf(stderr, "hid_init() failed, exit.\n"); + exit(EXIT_FAILURE); + } + + /* open Silabs IR Gesture USB reference design by USB product:vendor id */ + hid_device *hd = hid_open(0x10c4, 0xea90, NULL); + if (hd == NULL) { + fprintf(stderr, "hid_open() failed\n"); + exit(EXIT_FAILURE); + } + + unsigned char version_data[2]; + if (cp2112_get_version(hd, version_data) < 0) + exit(EXIT_FAILURE); + printf("cp2112 part number 0x%02x, device version %d\n", + version_data[0], version_data[1]); + + if (cp2112_set_auto_send_read(hd, 1) < 0) + exit(EXIT_FAILURE); + + /* write 0x17 to si114x HW_KEY register as per datasheet */ + if (cp2112_write_byte(hd, SI114X_HWKEY, 0x17) < 0) + exit(EXIT_FAILURE); + + unsigned char part, rev, seq; + if (cp2112_read_byte(hd, SI114x_PART_ID, &part) < 0) + exit(EXIT_FAILURE); + + if (cp2112_read_byte(hd, SI114x_REV_ID, &rev) < 0) + exit(EXIT_FAILURE); + + if (cp2112_read_byte(hd, SI114x_SEQ_ID, &seq) < 0) + exit(EXIT_FAILURE); + printf("si114x part number 0x%02x, revision 0x%02x, sequencer 0x%02x\n", + part, rev, seq); + + cp2112_is_idle(hd); + + if (cp2112_config_gpio(hd) < 0) + exit(EXIT_FAILURE); + + while (1) { + if (cp2112_set_gpio(hd, CP2112_LEDS_MASK, CP2112_LED_DOWN) < 0) + exit(EXIT_FAILURE); + usleep(50*1000); + if (cp2112_set_gpio(hd, CP2112_LEDS_MASK, CP2112_LED_RIGHT) < 0) + exit(EXIT_FAILURE); + usleep(50*1000); + if (cp2112_set_gpio(hd, CP2112_LEDS_MASK, CP2112_LED_UP) < 0) + exit(EXIT_FAILURE); + usleep(50*1000); + if (cp2112_set_gpio(hd, CP2112_LEDS_MASK, CP2112_LED_LEFT) < 0) + exit(EXIT_FAILURE); + usleep(50*1000); + } + + /* never get here */ + printf("done\n"); + + hid_close(hd); + hid_exit(); + + return EXIT_SUCCESS; +}