From 915154c4eb39dfa4a593d946c3a443a1812cc4ff Mon Sep 17 00:00:00 2001 From: michael Date: Sat, 28 Feb 2009 06:19:46 +0000 Subject: GLCD2USB driver from Till Harbaum git-svn-id: https://ssl.bulix.org/svn/lcd4linux/trunk@987 3ae390bd-cb1e-0410-b409-cd5a39f66f1f --- drv_GLCD2USB.c | 562 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 562 insertions(+) create mode 100644 drv_GLCD2USB.c (limited to 'drv_GLCD2USB.c') diff --git a/drv_GLCD2USB.c b/drv_GLCD2USB.c new file mode 100644 index 0000000..38a6b31 --- /dev/null +++ b/drv_GLCD2USB.c @@ -0,0 +1,562 @@ +/* $Id$ + * $URL$ + * + * GLCD2USB driver for LCD4Linux + * (see http://www.harbaum.org/till/glcd2usb for hardware) + * + * Copyright (C) 2007 Till Harbaum + * + * This file is part of LCD4Linux. + * + * LCD4Linux 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; either version 2, or (at your option) + * any later version. + * + * LCD4Linux is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/* + * + * exported fuctions: + * + * struct DRIVER drv_GLCD2USB + * + */ + +/* + * Options: + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "debug.h" +#include "cfg.h" +#include "qprintf.h" +#include "plugin.h" +#include "widget.h" +#include "widget_text.h" +#include "widget_icon.h" +#include "widget_bar.h" +#include "widget_keypad.h" +#include "drv.h" +#include "drv_generic_graphic.h" +#include "drv_generic_keypad.h" + +#define usbDevice usb_dev_handle /* use libusb's device structure */ +#include "usbcalls.h" + +#include "glcd2usb.h" + +/* ------------------------------------------------------------------------- */ + +#define USBRQ_HID_GET_REPORT 0x01 +#define USBRQ_HID_SET_REPORT 0x09 + +usbDevice_t *dev = NULL; + +/* USB message buffer */ +static union { + unsigned char bytes[132]; + display_info_t display_info; +} buffer; + +/* ------------------------------------------------------------------------- */ + + +#define IDENT_VENDOR_NUM 0x0403 +#define IDENT_VENDOR_STRING "www.harbaum.org/till/glcd2usb" +#define IDENT_PRODUCT_NUM 0xc634 +#define IDENT_PRODUCT_STRING "GLCD2USB" + +static char Name[] = IDENT_PRODUCT_STRING; + +/* ------------------------------------------------------------------------- */ + +static int usbGetString(usb_dev_handle * dev, int index, 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, 0x0409, buffer, sizeof(buffer), 1000)) < 0) + return rval; + + /* not a string */ + if (buffer[1] != USB_DT_STRING) + return 0; + + /* string would have been longer than buffer is */ + if ((unsigned char) buffer[0] < rval) + rval = (unsigned char) buffer[0]; + + /* 16 bit unicode -> 8 bit ascii */ + 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] = '?'; + } + + /* terminate string */ + buf[i - 1] = 0; + + return i - 1; +} + +/* ------------------------------------------------------------------------- */ + +int usbOpenDevice(usbDevice_t ** 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) { + usb_init(); + didUsbInit = 1; + } + + 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; + error("%s Warning: cannot open USB device: %s", Name, usb_strerror()); + continue; + } + if (vendorName == NULL && productName == NULL) { /* name does not matter */ + break; + } + /* now check whether the names match: */ + len = usbGetString(handle, dev->descriptor.iManufacturer, string, sizeof(string)); + if (len < 0) { + errorCode = USB_ERROR_IO; + error("%s: Cannot query manufacturer for device: %s", Name, usb_strerror()); + } else { + errorCode = USB_ERROR_NOTFOUND; + if (strcmp(string, vendorName) == 0) { + len = usbGetString(handle, dev->descriptor.iProduct, 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; + if (strcmp(string, productName) == 0) + break; + } + } + } + usb_close(handle); + handle = NULL; + } + } + if (handle) + break; + } + + if (handle != NULL) { + int rval, retries = 3; + if (usb_set_configuration(handle, 1)) { + fprintf(stderr, "Warning: could not set configuration: %s\n", usb_strerror()); + } + /* now try to claim the interface and detach the kernel HID driver on + * linux and other operating systems which support the call. + */ + while ((rval = usb_claim_interface(handle, 0)) != 0 && retries-- > 0) { +#ifdef LIBUSB_HAS_DETACH_KERNEL_DRIVER_NP + if (usb_detach_kernel_driver_np(handle, 0) < 0) { + fprintf(stderr, "Warning: could not detach kernel HID driver: %s\n", usb_strerror()); + } +#endif + } +#ifndef __APPLE__ + if (rval != 0) + fprintf(stderr, "Warning: could not claim interface\n"); +#endif + /* Continue anyway, even if we could not claim the interface. Control transfers + * should still work. + */ + errorCode = 0; + *device = handle; + } + return errorCode; +} + +/* ------------------------------------------------------------------------- */ + +void usbCloseDevice(usbDevice_t * device) +{ + if (device != NULL) + usb_close(device); +} + +/* ------------------------------------------------------------------------- */ + +int usbSetReport(usbDevice_t * device, int reportType, unsigned char *buffer, int len) +{ + int bytesSent; + + /* the write command needs some tweaking regarding allowed report lengths */ + if (buffer[0] == GLCD2USB_RID_WRITE) { + int i = 0, allowed_lengths[] = { 4 + 4, 8 + 4, 16 + 4, 32 + 4, 64 + 4, 128 + 4 }; + + if (len > 128 + 4) + error("%s: %d bytes usb report is too long \n", Name, len); + + while (allowed_lengths[i] != (128 + 4) && allowed_lengths[i] < len) + i++; + + len = allowed_lengths[i]; + buffer[0] = GLCD2USB_RID_WRITE + i; + } + + bytesSent = usb_control_msg(device, USB_TYPE_CLASS | USB_RECIP_INTERFACE | + USB_ENDPOINT_OUT, USBRQ_HID_SET_REPORT, + reportType << 8 | buffer[0], 0, (char *) buffer, len, 1000); + if (bytesSent != len) { + if (bytesSent < 0) + error("%s: Error sending message: %s", Name, usb_strerror()); + return USB_ERROR_IO; + } + return 0; +} + +/* ------------------------------------------------------------------------- */ + +int usbGetReport(usbDevice_t * device, int reportType, int reportNumber, unsigned char *buffer, int *len) +{ + *len = usb_control_msg(device, USB_TYPE_CLASS | USB_RECIP_INTERFACE | + USB_ENDPOINT_IN, USBRQ_HID_GET_REPORT, + reportType << 8 | reportNumber, 0, (char *) buffer, *len, 1000); + if (*len < 0) { + error("%s: Error sending message: %s", Name, usb_strerror()); + return USB_ERROR_IO; + } + return 0; +} + +char *usbErrorMessage(int errCode) +{ + static char buffer[80]; + + switch (errCode) { + case USB_ERROR_ACCESS: + return "Access to device denied"; + case USB_ERROR_NOTFOUND: + return "The specified device was not found"; + case USB_ERROR_BUSY: + return "The device is used by another application"; + case USB_ERROR_IO: + return "Communication error with device"; + default: + sprintf(buffer, "Unknown USB error %d", errCode); + return buffer; + } + return NULL; /* not reached */ +} + +static char *video_buffer = NULL; +static char *dirty_buffer = NULL; + +static void drv_GLCD2USB_blit(const int row, const int col, const int height, const int width) +{ + int r, c, err, i, j; + + /* update offscreen buffer */ + for (r = row; r < row + height; r++) { + for (c = col; c < col + width; c++) { + int x, y, bit; + + /* these assignments are display layout dependent */ + x = c; + y = r / 8; + bit = r % 8; + + i = video_buffer[x + DCOLS * y]; + + if (drv_generic_graphic_black(r, c)) + video_buffer[x + DCOLS * y] |= 1 << bit; + else + video_buffer[x + DCOLS * y] &= ~(1 << bit); + + if (video_buffer[x + DCOLS * y] != i) + dirty_buffer[x + DCOLS * y] |= 1 << bit; + } + } + +#if 0 + /* display what's in the buffer (for debugging) */ + for (r = 0; r < DROWS; r++) { + for (c = 0; c < DCOLS; c++) { + if (video_buffer[c + DCOLS * (r / 8)] & (1 << (r % 8))) + putchar('#'); + else + putchar(' '); + } + putchar('\n'); + } +#endif + + /* short gaps of unchanged bytes in fact increase the communication */ + /* overhead. so we eliminate them here */ + for (j = -1, i = 0; i < DROWS * DCOLS / 8; i++) { + if (dirty_buffer[i] && j >= 0 && i - j <= 4) { + /* found a clean gap <= 4 bytes: mark it dirty */ + for (r = j; r < i; r++) + dirty_buffer[r] = 1; + } + + /* if this is dirty, drop the saved position */ + if (dirty_buffer[i]) + j = -1; + + /* save position of this clean entry if there's no position saved yet */ + if (!dirty_buffer[i] && j < 0) + j = i; + } + + /* and do the actual data transmission */ + buffer.bytes[0] = 0; + for (i = 0; i < DROWS * DCOLS / 8; i++) { + if (dirty_buffer[i]) { + /* starting a new run? */ + if (!buffer.bytes[0]) { + buffer.bytes[0] = GLCD2USB_RID_WRITE; + buffer.bytes[1] = i % 256; // offset + buffer.bytes[2] = i / 256; + buffer.bytes[3] = 0; // length + } + buffer.bytes[4 + buffer.bytes[3]++] = video_buffer[i]; + } + + /* this part of the buffer is not dirty or we are at end */ + /* of buffer or the buffer is fill: send data then */ + if ((!dirty_buffer[i]) || (i == DROWS * DCOLS / 8 - 1) || (buffer.bytes[3] == 128)) { + /* is there data to be sent in the buffer? */ + if (buffer.bytes[0] && buffer.bytes[3]) { + if ((err = usbSetReport(dev, USB_HID_REPORT_TYPE_FEATURE, buffer.bytes, buffer.bytes[3] + 4)) != 0) + error("%s: Error sending display contents: %s", Name, usbErrorMessage(err)); + + buffer.bytes[0] = 0; + } + } + + /* this entry isn't dirty anymore */ + dirty_buffer[i] = 0; + } +} + +static int drv_GLCD2USB_start(const char *section) +{ + char *s; + int err = 0, len; + + if (sscanf(s = cfg_get(section, "font", "6x8"), "%dx%d", &XRES, &YRES) != 2 || XRES < 1 || YRES < 1) { + error("%s: bad %s.Font '%s' from %s", Name, section, s, cfg_source()); + free(s); + return -1; + } + free(s); + + if ((err = usbOpenDevice(&dev, IDENT_VENDOR_NUM, IDENT_VENDOR_STRING, + IDENT_PRODUCT_NUM, IDENT_PRODUCT_STRING)) != 0) { + error("%s: opening GLCD2USB device: %s", Name, usbErrorMessage(err)); + return -1; + } + + info("%s: Found device", Name); + + /* query display parameters */ + memset(&buffer, 0, sizeof(buffer)); + + len = sizeof(display_info_t); + if ((err = usbGetReport(dev, USB_HID_REPORT_TYPE_FEATURE, GLCD2USB_RID_GET_INFO, buffer.bytes, &len)) != 0) { + + error("%s: query display parameters: %s", Name, usbErrorMessage(err)); + usbCloseDevice(dev); + return -1; + } + + if (len < (int) sizeof(buffer.display_info)) { + error("%s: Not enough bytes in display info report (%d instead of %d)", + Name, len, (int) sizeof(buffer.display_info)); + usbCloseDevice(dev); + return -1; + } + + info("%s: display name = %s", Name, buffer.display_info.name); + info("%s: display resolution = %d * %d", Name, buffer.display_info.width, buffer.display_info.height); + info("%s: display flags: %x", Name, buffer.display_info.flags); + + /* TODO: check for supported features */ + + + /* save display size */ + DCOLS = buffer.display_info.width; + DROWS = buffer.display_info.height; + + /* allocate a offscreen buffer */ + video_buffer = malloc(DCOLS * DROWS / 8); + dirty_buffer = malloc(DCOLS * DROWS / 8); + memset(video_buffer, 0, DCOLS * DROWS / 8); + memset(dirty_buffer, 0, DCOLS * DROWS / 8); + + /* get access to display */ + buffer.bytes[0] = GLCD2USB_RID_SET_ALLOC; + buffer.bytes[1] = 1; /* 1=alloc, 0=free */ + if ((err = usbSetReport(dev, USB_HID_REPORT_TYPE_FEATURE, buffer.bytes, 2)) != 0) { + error("%s: Error allocating display: %s", Name, usbErrorMessage(err)); + usbCloseDevice(dev); + return -1; + } + + return 0; +} + + + +/****************************************/ +/*** plugins ***/ +/****************************************/ + +/* none at the moment... */ + + +/****************************************/ +/*** widget callbacks ***/ +/****************************************/ + + +/* using drv_generic_graphic_draw(W) */ +/* using drv_generic_graphic_icon_draw(W) */ +/* using drv_generic_graphic_bar_draw(W) */ + + +/****************************************/ +/*** exported functions ***/ +/****************************************/ + + +/* list models */ +int drv_GLCD2USB_list(void) +{ + printf("generic"); + return 0; +} + +static int drv_GLCD2USB_keypad(const int num) +{ + int val = 0; +#if 0 + /* check for key press event */ + if (num & 0x80) + val = WIDGET_KEY_PRESSED; + else + val = WIDGET_KEY_RELEASED; + + if ((num & 0x7f) == 0) + val += WIDGET_KEY_UP; + + if ((num & 0x7f) == 1) + val += WIDGET_KEY_DOWN; +#endif + return val; +} + +/* initialize driver & display */ +int drv_GLCD2USB_init(const char *section, const __attribute__ ((unused)) + int quiet) +{ + int ret; + + info("%s: %s", Name, "$Rev$"); + + /* real worker functions */ + drv_generic_graphic_real_blit = drv_GLCD2USB_blit; + drv_generic_keypad_real_press = drv_GLCD2USB_keypad; + + /* start display */ + if ((ret = drv_GLCD2USB_start(section)) != 0) + return ret; + + /* initialize generic graphic driver */ + if ((ret = drv_generic_graphic_init(section, Name)) != 0) + return ret; + + /* register plugins */ + /* none at the moment... */ + + + return 0; +} + + +/* close driver & display */ +int drv_GLCD2USB_quit(const __attribute__ ((unused)) + int quiet) +{ + int err; + + info("%s: shutting down.", Name); + drv_generic_graphic_quit(); + + /* release access to display */ + + buffer.bytes[0] = GLCD2USB_RID_SET_ALLOC; + buffer.bytes[1] = 0; /* 1=alloc, 0=free */ + if ((err = usbSetReport(dev, USB_HID_REPORT_TYPE_FEATURE, buffer.bytes, 2)) != 0) { + error("%s Error freeing display: %s", Name, usbErrorMessage(err)); + } + + /* clean up */ + if (dev != NULL) + usbCloseDevice(dev); + + if (video_buffer != NULL) { + free(video_buffer); + free(dirty_buffer); + } + + return (0); +} + + +DRIVER drv_GLCD2USB = { + .name = Name, + .list = drv_GLCD2USB_list, + .init = drv_GLCD2USB_init, + .quit = drv_GLCD2USB_quit, +}; -- cgit v1.2.3