/** * \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 #include #include #include /* 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; }