427 lines
15 KiB
C
427 lines
15 KiB
C
|
/**
|
||
|
* \file usb-led-fader.c
|
||
|
* \brief Commandline-tool for the USB-LED-Fader.
|
||
|
* \author Ronald Schaten
|
||
|
* \version $Id: usb-led-fader.c,v 1.1 2006/09/26 18:18:27 rschaten Exp $
|
||
|
*
|
||
|
* License: See documentation.
|
||
|
*/
|
||
|
|
||
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <string.h>
|
||
|
#include <usb.h> /* this is libusb, see http://libusb.sourceforge.net/ */
|
||
|
|
||
|
#include "usbledfader.h"
|
||
|
#include "channels.h"
|
||
|
|
||
|
#define USBDEV_SHARED_VENDOR 0x16C0 /**< VOTI */
|
||
|
#define USBDEV_SHARED_PRODUCT 0x05DC /**< Obdev's free shared PID. Use obdev's generic shared VID/PID pair and follow the rules outlined in firmware/usbdrv/USBID-License.txt. */
|
||
|
|
||
|
/* These are error codes for the communication via USB. */
|
||
|
#define USB_ERROR_NOTFOUND 1 /**< Error code if the device isn't found. */
|
||
|
#define USB_ERROR_ACCESS 2 /**< Error code if the device isn't accessible. */
|
||
|
#define USB_ERROR_IO 3 /**< Error code if errors in the communication with the device occur. */
|
||
|
|
||
|
/**
|
||
|
* Displays usage-informations. This function is called if the parameters
|
||
|
* cannot be parsed.
|
||
|
* \param name The name of this application.
|
||
|
*/
|
||
|
void usage(char *name)
|
||
|
{
|
||
|
fprintf(stderr, "usage:\n");
|
||
|
fprintf(stderr, " %s status\n", name);
|
||
|
fprintf(stderr, " %s set ledId waveId waveformId periodDuration repetitionCount\n", name);
|
||
|
fprintf(stderr, " %s clear ledId\n", name);
|
||
|
fprintf(stderr, " %s reset\n", name);
|
||
|
fprintf(stderr, " %s show waveformId\n", name);
|
||
|
fprintf(stderr, " %s test\n\n", name);
|
||
|
fprintf(stderr, "parameters:\n");
|
||
|
fprintf(stderr, " ledId: ID of the LED (0-%d).\n", CHANNELS - 1);
|
||
|
fprintf(stderr, " waveId: ID of the wave (0-1: constant waves, 2: override).\n");
|
||
|
fprintf(stderr, " waveformId: ID of the waveform (0-31: brightness, 32-37: patterns).\n");
|
||
|
fprintf(stderr, " periodDuration: Time in sec/10 for one repetition of the waveform.\n");
|
||
|
fprintf(stderr, " A value of 0 can be used to reset the wave.\n");
|
||
|
fprintf(stderr, " repetitionCount: Number of repetitions before switching to the next wave.\n");
|
||
|
fprintf(stderr, " A value of 0 can be used to repeat this forever.\n");
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Reads and converts a string from USB. The conversion to ASCII is 'lossy' (unknown characters become '?').
|
||
|
* \param dev Handle of the USB-Device.
|
||
|
* \param index Index of the required data.
|
||
|
* \param langid Index of the expected language.
|
||
|
* \param buf Buffer to contain the return-string.
|
||
|
* \param buflen Length of buf.
|
||
|
* \return Length of the string.
|
||
|
*/
|
||
|
int usbGetStringAscii(usb_dev_handle * dev, int index, int langid, char *buf, int buflen) {
|
||
|
char buffer[256];
|
||
|
int rval, i;
|
||
|
|
||
|
if ((rval = usb_control_msg(dev, USB_ENDPOINT_IN, USB_REQ_GET_DESCRIPTOR, (USB_DT_STRING << 8) + index, langid, buffer, sizeof(buffer), 1000)) < 0) {
|
||
|
return rval;
|
||
|
}
|
||
|
if (buffer[1] != USB_DT_STRING) {
|
||
|
return 0;
|
||
|
}
|
||
|
if ((unsigned char) buffer[0] < rval) {
|
||
|
rval = (unsigned char) buffer[0];
|
||
|
}
|
||
|
rval /= 2;
|
||
|
/* lossy conversion to ISO Latin1 */
|
||
|
for (i = 1; i < rval; i++) {
|
||
|
if (i > buflen) {
|
||
|
/* destination buffer overflow */
|
||
|
break;
|
||
|
}
|
||
|
buf[i - 1] = buffer[2 * i];
|
||
|
if (buffer[2 * i + 1] != 0) {
|
||
|
/* outside of ISO Latin1 range */
|
||
|
buf[i - 1] = '?';
|
||
|
}
|
||
|
}
|
||
|
buf[i - 1] = 0;
|
||
|
return i - 1;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Connect to the USB-device. Loops through all connected USB-Devices and
|
||
|
* searches our counterpart.
|
||
|
* \param device Handle to address the device.
|
||
|
* \param vendor USBDEV_SHARED_VENDOR as defined.
|
||
|
* \param vendorName In our case "www.schatenseite.de".
|
||
|
* \param product USBDEV_SHARED_PRODUCT as defined.
|
||
|
* \param productName In our case "USB-LED-Fader".
|
||
|
* \return Error code.
|
||
|
*/
|
||
|
int usbOpenDevice(usb_dev_handle ** device, int vendor, char *vendorName, int product, char *productName) {
|
||
|
struct usb_bus *bus;
|
||
|
struct usb_device *dev;
|
||
|
usb_dev_handle *handle = NULL;
|
||
|
int errorCode = USB_ERROR_NOTFOUND;
|
||
|
static int didUsbInit = 0;
|
||
|
|
||
|
if (!didUsbInit) {
|
||
|
didUsbInit = 1;
|
||
|
usb_init();
|
||
|
}
|
||
|
usb_find_busses();
|
||
|
usb_find_devices();
|
||
|
for (bus = usb_get_busses(); bus; bus = bus->next) {
|
||
|
for (dev = bus->devices; dev; dev = dev->next) {
|
||
|
if (dev->descriptor.idVendor == vendor && dev->descriptor.idProduct == product) {
|
||
|
char string[256];
|
||
|
int len;
|
||
|
handle = usb_open(dev); /* we need to open the device in order to query strings */
|
||
|
if (!handle) {
|
||
|
errorCode = USB_ERROR_ACCESS;
|
||
|
fprintf(stderr, "Warning: cannot open USB device: %s\n", usb_strerror());
|
||
|
continue;
|
||
|
}
|
||
|
if (vendorName == NULL && productName == NULL) { /* name does not matter */
|
||
|
break;
|
||
|
}
|
||
|
/* now check whether the names match: */
|
||
|
len = usbGetStringAscii(handle, dev->descriptor.iManufacturer, 0x0409, string, sizeof(string));
|
||
|
if (len < 0) {
|
||
|
errorCode = USB_ERROR_IO;
|
||
|
fprintf(stderr, "Warning: cannot query manufacturer for device: %s\n", usb_strerror());
|
||
|
} else {
|
||
|
errorCode = USB_ERROR_NOTFOUND;
|
||
|
/* fprintf(stderr, "seen device from vendor ->%s<-\n", string); */
|
||
|
if (strcmp(string, vendorName) == 0) {
|
||
|
len = usbGetStringAscii(handle, dev->descriptor.iProduct, 0x0409, string, sizeof(string));
|
||
|
if (len < 0) {
|
||
|
errorCode = USB_ERROR_IO;
|
||
|
fprintf(stderr, "Warning: cannot query product for device: %s\n", usb_strerror());
|
||
|
} else {
|
||
|
errorCode = USB_ERROR_NOTFOUND;
|
||
|
/* fprintf(stderr, "seen product ->%s<-\n", string); */
|
||
|
if (strcmp(string, productName) == 0) {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
usb_close(handle);
|
||
|
handle = NULL;
|
||
|
}
|
||
|
}
|
||
|
if (handle) {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if (handle != NULL) {
|
||
|
errorCode = 0;
|
||
|
*device = handle;
|
||
|
}
|
||
|
return errorCode;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Test connection to the device. The test consists of writing 1000 random
|
||
|
* numbers to the device and checking the echo. This should discover systematic
|
||
|
* bit errors (e.g. in bit stuffing).
|
||
|
* \param handle Handle to talk to the device.
|
||
|
* \param argc Number of arguments.
|
||
|
* \param argv Arguments.
|
||
|
*/
|
||
|
void dev_test(usb_dev_handle *handle, int argc, char** argv) {
|
||
|
unsigned char buffer[8];
|
||
|
int nBytes;
|
||
|
int i, v, r;
|
||
|
if (argc != 2) {
|
||
|
usage(argv[0]);
|
||
|
exit(1);
|
||
|
}
|
||
|
for (i = 0; i < 1000; i++) {
|
||
|
v = rand() & 0xffff;
|
||
|
nBytes = usb_control_msg(handle, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_ENDPOINT_IN, CMD_ECHO, v, 0, (char *) buffer, sizeof(buffer), 5000);
|
||
|
if (nBytes < 2) {
|
||
|
if (nBytes < 0) {
|
||
|
fprintf(stderr, "USB error: %s\n", usb_strerror());
|
||
|
}
|
||
|
fprintf(stderr, "only %d bytes received in iteration %d\n", nBytes, i);
|
||
|
exit(1);
|
||
|
}
|
||
|
r = buffer[0] | (buffer[1] << 8);
|
||
|
if (r != v) {
|
||
|
fprintf(stderr, "data error: received 0x%x instead of 0x%x in iteration %d\n", r, v, i);
|
||
|
exit(1);
|
||
|
}
|
||
|
}
|
||
|
printf("test succeeded\n");
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Set waves. It is possible to set any number of waves at once.
|
||
|
* \param handle Handle to talk to the device.
|
||
|
* \param argc Number of arguments.
|
||
|
* \param argv Arguments.
|
||
|
*/
|
||
|
void dev_set(usb_dev_handle *handle, int argc, char** argv) {
|
||
|
unsigned char buffer[8];
|
||
|
int nBytes;
|
||
|
int parameter;
|
||
|
if ((argc < 7) || ((argc - 2) % 5 != 0)) {
|
||
|
usage(argv[0]);
|
||
|
exit(1);
|
||
|
}
|
||
|
for (parameter = 2; (parameter + 4) < argc; parameter += 5) {
|
||
|
int ledId = atoi(argv[parameter + 0]);
|
||
|
if ((ledId < 0) || (ledId > (CHANNELS - 1))) {
|
||
|
fprintf(stderr, "invalid ledId: %d\n", ledId);
|
||
|
exit(1);
|
||
|
}
|
||
|
int waveId = atoi(argv[parameter + 1]);
|
||
|
if ((waveId < 0) || (waveId > 2)) {
|
||
|
fprintf(stderr, "invalid waveId: %d\n", waveId);
|
||
|
exit(1);
|
||
|
}
|
||
|
int waveformId = atoi(argv[parameter + 2]);
|
||
|
if ((waveformId < 0) || (waveformId > 38)) {
|
||
|
fprintf(stderr, "invalid waveformId: %d\n", waveformId);
|
||
|
exit(1);
|
||
|
}
|
||
|
int periodDuration = atoi(argv[parameter + 3]);
|
||
|
if ((periodDuration < 0) || (periodDuration > 255)) {
|
||
|
fprintf(stderr, "invalid periodDuration: %d\n", periodDuration);
|
||
|
exit(1);
|
||
|
}
|
||
|
int repetitionCount = atoi(argv[parameter + 4]);
|
||
|
if ((repetitionCount < 0) || (repetitionCount > 255)) {
|
||
|
fprintf(stderr, "invalid repetitionCount: %d\n", repetitionCount);
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
buffer[0] = CMD_SET;
|
||
|
buffer[1] = ledId;
|
||
|
buffer[2] = waveId;
|
||
|
buffer[3] = waveformId;
|
||
|
buffer[4] = periodDuration;
|
||
|
buffer[5] = repetitionCount;
|
||
|
|
||
|
nBytes = usb_control_msg(handle, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_ENDPOINT_OUT, CMD_SET, ledId, 0, (char *) buffer, sizeof(buffer), 5000);
|
||
|
|
||
|
if (nBytes < 0) {
|
||
|
fprintf(stderr, "USB error: %s\n", usb_strerror());
|
||
|
exit(1);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Clear all waves on one LED.
|
||
|
* \param handle Handle to talk to the device.
|
||
|
* \param argc Number of arguments.
|
||
|
* \param argv Arguments.
|
||
|
*/
|
||
|
void dev_clear(usb_dev_handle *handle, int argc, char** argv) {
|
||
|
unsigned char buffer[8];
|
||
|
int nBytes;
|
||
|
if (argc != 3) {
|
||
|
usage(argv[0]);
|
||
|
exit(1);
|
||
|
}
|
||
|
int ledId = atoi(argv[2]);
|
||
|
if ((ledId < 0) || (ledId > (CHANNELS - 1))) {
|
||
|
fprintf(stderr, "invalid LED: %d\n", ledId);
|
||
|
exit(1);
|
||
|
}
|
||
|
nBytes = usb_control_msg(handle, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_ENDPOINT_OUT, CMD_CLEAR, ledId, 0, (char *) buffer, sizeof(buffer), 5000);
|
||
|
if (nBytes < 0) {
|
||
|
fprintf(stderr, "USB error: %s\n", usb_strerror());
|
||
|
exit(1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get the status of the device. Status information is printed in detail.
|
||
|
* \param handle Handle to talk to the device.
|
||
|
* \param argc Number of arguments.
|
||
|
* \param argv Arguments.
|
||
|
*/
|
||
|
void dev_status(usb_dev_handle *handle, int argc, char** argv) {
|
||
|
int nBytes;
|
||
|
int i, j;
|
||
|
static fade_GlobalData fade_globalData; /* contains the state of all four LEDs. */
|
||
|
if (argc != 2) {
|
||
|
usage(argv[0]);
|
||
|
exit(1);
|
||
|
}
|
||
|
nBytes = usb_control_msg(handle, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_ENDPOINT_IN, CMD_GET, 0, 0, (char *) &fade_globalData, sizeof(fade_globalData), 5000);
|
||
|
if (nBytes < 0) {
|
||
|
fprintf(stderr, "USB error: %s\n", usb_strerror());
|
||
|
exit(1);
|
||
|
}
|
||
|
if (nBytes != sizeof(fade_globalData)) {
|
||
|
fprintf(stderr, "USB oddity: %d bytes received, %d bytes expected.\n", nBytes, sizeof(fade_globalData));
|
||
|
exit(1);
|
||
|
}
|
||
|
for (i = 0; i < CHANNELS; i++) {
|
||
|
printf("LED %d %10s %10s %10s %10s %10s\n", i, "curid", "curvalue", "curpos", "currep", "nextupd");
|
||
|
printf(" %10d %10d %10d %10d %10d\n",
|
||
|
fade_globalData.led[i].waveCurrentId,
|
||
|
fade_globalData.led[i].waveCurrentValue,
|
||
|
fade_globalData.led[i].waveCurrentPosition,
|
||
|
fade_globalData.led[i].waveCurrentRepetition,
|
||
|
fade_globalData.led[i].waveNextUpdate);
|
||
|
printf("%10s %10s %10s %10s %10s %10s\n", "wave", "waveform", "length", "repeat", "duration", "updtime");
|
||
|
for (j = 0; j < 3; j++) {
|
||
|
printf("%10d %10d %10d %10d %10d %10d\n",
|
||
|
j,
|
||
|
fade_globalData.led[i].wave[j].waveformId,
|
||
|
fade_globalData.led[i].wave[j].waveformLength,
|
||
|
fade_globalData.led[i].wave[j].waveformRepetition,
|
||
|
fade_globalData.led[i].wave[j].waveformDuration,
|
||
|
fade_globalData.led[i].wave[j].waveformUpdateTime);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Reset the device.
|
||
|
* \param handle Handle to talk to the device.
|
||
|
* \param argc Number of arguments.
|
||
|
* \param argv Arguments.
|
||
|
*/
|
||
|
void dev_reset(usb_dev_handle *handle, int argc, char** argv) {
|
||
|
unsigned char buffer[8];
|
||
|
int nBytes;
|
||
|
if (argc != 2) {
|
||
|
usage(argv[0]);
|
||
|
exit(1);
|
||
|
}
|
||
|
nBytes = usb_control_msg(handle, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_ENDPOINT_OUT, CMD_RESET, 0, 0, (char *) buffer, sizeof(buffer), 5000);
|
||
|
if (nBytes < 0) {
|
||
|
fprintf(stderr, "USB error: %s\n", usb_strerror());
|
||
|
exit(1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Show a waveform. This will not send a command to the device, the waveform is
|
||
|
* only printed on the screen.
|
||
|
* \param handle Handle to talk to the device (not needed).
|
||
|
* \param argc Number of arguments.
|
||
|
* \param argv Arguments.
|
||
|
*/
|
||
|
int dev_show(int argc, char **argv) {
|
||
|
if (argc != 3) {
|
||
|
usage(argv[0]);
|
||
|
exit(1);
|
||
|
}
|
||
|
int waveformId = atoi(argv[2]);
|
||
|
if ((waveformId < 0) || (waveformId > 38)) {
|
||
|
fprintf(stderr, "invalid waveformId: %d\n", waveformId);
|
||
|
exit(1);
|
||
|
}
|
||
|
int i, j;
|
||
|
int length = fade_calculateWaveform(waveformId, 0);
|
||
|
printf("wave %2d - length %2d\n", waveformId, length);
|
||
|
for (i = 31; i > 0; i--) {
|
||
|
printf("%2d: ", i);
|
||
|
for (j = 1; j <= length; j++) {
|
||
|
if (fade_calculateWaveform(waveformId, j) >= i) {
|
||
|
printf("*");
|
||
|
} else {
|
||
|
printf(" ");
|
||
|
}
|
||
|
}
|
||
|
printf("\n");
|
||
|
}
|
||
|
printf(" ");
|
||
|
for (j = 1; j <= length; j++) {
|
||
|
printf("=");
|
||
|
}
|
||
|
printf("\n");
|
||
|
exit(0);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Main function. Initializes the USB-device, parses commandline-parameters and
|
||
|
* calls the functions that communicate with the device.
|
||
|
* \param argc Number of arguments.
|
||
|
* \param argv Arguments.
|
||
|
* \return Error code.
|
||
|
*/
|
||
|
int main(int argc, char **argv)
|
||
|
{
|
||
|
usb_dev_handle *handle = NULL;
|
||
|
|
||
|
if (argc < 2) {
|
||
|
usage(argv[0]);
|
||
|
exit(1);
|
||
|
}
|
||
|
usb_init();
|
||
|
if (usbOpenDevice (&handle, USBDEV_SHARED_VENDOR, "www.schatenseite.de", USBDEV_SHARED_PRODUCT, "USB-LED-Fader") != 0) {
|
||
|
fprintf(stderr, "Could not find USB device \"USB-LED-Fader\" with vid=0x%x pid=0x%x\n", USBDEV_SHARED_VENDOR, USBDEV_SHARED_PRODUCT);
|
||
|
exit(1);
|
||
|
}
|
||
|
/* We have searched all devices on all busses for our USB device above. Now
|
||
|
* try to open it and perform the vendor specific control operations for the
|
||
|
* function requested by the user.
|
||
|
*/
|
||
|
if (strcmp(argv[1], "test") == 0) {
|
||
|
dev_test(handle, argc, argv);
|
||
|
} else if (strcmp(argv[1], "set") == 0) {
|
||
|
dev_set(handle, argc, argv);
|
||
|
} else if (strcmp(argv[1], "clear") == 0) {
|
||
|
dev_clear(handle, argc, argv);
|
||
|
} else if (strcmp(argv[1], "status") == 0) {
|
||
|
dev_status(handle, argc, argv);
|
||
|
} else if (strcmp(argv[1], "reset") == 0) {
|
||
|
dev_reset(handle, argc, argv);
|
||
|
} else if (strcmp(argv[1], "show") == 0) {
|
||
|
dev_reset(handle, argc, argv);
|
||
|
} else {
|
||
|
usage(argv[0]);
|
||
|
exit(1);
|
||
|
}
|
||
|
usb_close(handle);
|
||
|
return 0;
|
||
|
}
|
||
|
|