From 181cec4348da40331b3e8ab365732c025ec149b2 Mon Sep 17 00:00:00 2001 From: Reinhard Tartler Date: Wed, 27 Apr 2011 19:24:15 +0200 Subject: Import upstream version 0.11.0~svn1143 --- drv_G15.c | 676 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 676 insertions(+) create mode 100644 drv_G15.c (limited to 'drv_G15.c') diff --git a/drv_G15.c b/drv_G15.c new file mode 100644 index 0000000..98fe120 --- /dev/null +++ b/drv_G15.c @@ -0,0 +1,676 @@ +/* $Id: drv_G15.c 1117 2010-04-18 11:47:58Z mzuther $ + * $URL: https://ssl.bulix.org/svn/lcd4linux/trunk/drv_G15.c $ + * + * Driver for Logitech G-15 keyboard LCD screen + * + * Copyright (C) 2006 Dave Ingram + * Copyright (C) 2005 Michael Reinelt + * Copyright (C) 2005 The LCD4Linux Team + * + * 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_G15 + * + */ + +#include "config.h" + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include "debug.h" +#include "cfg.h" +#include "qprintf.h" +#include "udelay.h" +#include "plugin.h" +#include "drv.h" +#include "drv_generic_graphic.h" +#include "thread.h" + +/* Logitech: VendorID 0x046d */ +#define G15_VENDOR 0x046d +/* Logitech Keyboard G15: ProductID 0xc222, 0xc227 */ +/* Logitech Speaker Z10: ProductID 0x0a07 */ +#define G15_DEVICE 0xc222 +#define G15_DEVICE2 0xc227 +#define Z10_DEVICE 0x0a07 +#define M1730_DEVICE 0xc251 + +#if 0 +#define DEBUG(x) debug("%s(): %s", __FUNCTION__, x); +#else +#define DEBUG(x) +#endif + +#define KB_UPDOWN_PRESS + +static char Name[] = "G-15"; + +static usb_dev_handle *g15_lcd; + +static unsigned char *g15_image; + +unsigned char g_key_states[18]; +unsigned char m_key_states[4]; +unsigned char l_key_states[5]; + +static int uinput_fd; +static int kb_mutex; +static int kb_thread_pid; +static int kb_single_keypress = 0; + +static int usb_endpoint = 0; + + +/****************************************/ +/*** hardware dependant functions ***/ +/****************************************/ + + +void drv_G15_keyDown(unsigned char scancode) +{ + struct input_event event; + memset(&event, 0, sizeof(event)); + + event.type = EV_KEY; + event.code = scancode; + event.value = 1; + write(uinput_fd, &event, sizeof(event)); +} + +void drv_G15_keyUp(unsigned char scancode) +{ + struct input_event event; + memset(&event, 0, sizeof(event)); + + event.type = EV_KEY; + event.code = scancode; + event.value = 0; + write(uinput_fd, &event, sizeof(event)); +} + +void drv_G15_keyDownUp(unsigned char scancode) +{ + drv_G15_keyDown(scancode); + drv_G15_keyUp(scancode); + +} + +inline unsigned char drv_G15_evalScanCode(int key) +{ + /* first 12 G keys produce F1 - F12, thats 0x3a + key */ + if (key < 12) { + return 0x3a + key; + } + + /* the other keys produce Key '1' (above letters) + key, thats 0x1e + key */ + else { + return 0x1e + key - 12; /* sigh, half an hour to find -12 .... */ + } +} + +void drv_G15_processKeyEvent(unsigned char *buffer) +{ + const int g_scancode_offset = 167; + const int m_scancode_offset = 187; + const int l_scancode_offset = 191; + int i; + int is_set; + unsigned char m_key_new_states[4]; + unsigned char l_key_new_states[5]; + unsigned char orig_scancode; + +#if 0 + printf("%hhx %hhx %hhx %hhx %hhx %hhx %hhx %hhx %hhx \n\n", buffer[0], buffer[1], buffer[2], buffer[3], buffer[4], + buffer[5], buffer[6], buffer[7], buffer[8]); + usleep(100); +#endif + + if (buffer[0] == 0x01) { + DEBUG("Checking keys: "); + + for (i = 0; i < 18; ++i) { + orig_scancode = drv_G15_evalScanCode(i); + is_set = 0; + + if (buffer[1] == orig_scancode || buffer[2] == orig_scancode || buffer[3] == orig_scancode || + buffer[4] == orig_scancode || buffer[5] == orig_scancode) + is_set = 1; + + if (!is_set && g_key_states[i] != 0) { + /* key was pressed but is no more */ + if (!kb_single_keypress) + drv_G15_keyUp(g_scancode_offset + i); + g_key_states[i] = 0; + debug("G%d going up", (i + 1)); + } else if (is_set && g_key_states[i] == 0) { + if (!kb_single_keypress) + drv_G15_keyDown(g_scancode_offset + i); + else + drv_G15_keyDownUp(g_scancode_offset + i); + + g_key_states[i] = 1; + debug("G%d going down", (i + 1)); + } + } + } else { + if (buffer[0] == 0x02) { + memset(m_key_new_states, 0, sizeof(m_key_new_states)); + + if (buffer[6] & 0x01) + m_key_new_states[0] = 1; + if (buffer[7] & 0x02) + m_key_new_states[1] = 1; + if (buffer[8] & 0x04) + m_key_new_states[2] = 1; + if (buffer[7] & 0x40) + m_key_new_states[3] = 1; + + for (i = 0; i < 4; ++i) { + if (!m_key_new_states[i] && m_key_states[i] != 0) { + /* key was pressed but is no more */ + if (!kb_single_keypress) + drv_G15_keyUp(m_scancode_offset + i); + m_key_states[i] = 0; + debug("M%d going up", (i + 1)); + } else if (m_key_new_states[i] && m_key_states[i] == 0) { + if (!kb_single_keypress) + drv_G15_keyDown(m_scancode_offset + i); + else + drv_G15_keyDownUp(m_scancode_offset + i); + m_key_states[i] = 1; + debug("M%d going down", (i + 1)); + } + } + + memset(l_key_new_states, 0, sizeof(l_key_new_states)); + if (buffer[8] & 0x80) + l_key_new_states[0] = 1; + if (buffer[2] & 0x80) + l_key_new_states[1] = 1; + if (buffer[3] & 0x80) + l_key_new_states[2] = 1; + if (buffer[4] & 0x80) + l_key_new_states[3] = 1; + if (buffer[5] & 0x80) + l_key_new_states[4] = 1; + + for (i = 0; i < 5; ++i) { + if (!l_key_new_states[i] && l_key_states[i] != 0) { + /* key was pressed but is no more */ + if (!kb_single_keypress) + drv_G15_keyUp(l_scancode_offset + i); + l_key_states[i] = 0; + debug("L%d going up", (i + 1)); + } else if (l_key_new_states[i] && l_key_states[i] == 0) { + if (!kb_single_keypress) + drv_G15_keyDown(l_scancode_offset + i); + else + drv_G15_keyDownUp(l_scancode_offset + i); + l_key_states[i] = 1; + debug("L%d going down", (i + 1)); + } + } + + } + } +} + +void drv_G15_closeUIDevice() +{ + DEBUG("closing device"); + ioctl(uinput_fd, UI_DEV_DESTROY); + close(uinput_fd); +} + + +void drv_G15_initKeyHandling(char *device_filename) +{ + struct uinput_user_dev device; + int i; + DEBUG("Key Handling init") + uinput_fd = open(device_filename, O_RDWR); + + if (uinput_fd < 0) { + info("Error, could not open the uinput device"); + info("Compile your kernel for uinput, calling it a day now"); + info("mknod uinput c 10 223"); + abort(); + } + memset(&device, 0, sizeof(device)); + strncpy(device.name, "G15 Keys", UINPUT_MAX_NAME_SIZE); + device.id.bustype = BUS_USB; + device.id.version = 4; + + ioctl(uinput_fd, UI_SET_EVBIT, EV_KEY); + + for (i = 0; i < 256; ++i) + ioctl(uinput_fd, UI_SET_KEYBIT, i); + + write(uinput_fd, &device, sizeof(device)); + + if (ioctl(uinput_fd, UI_DEV_CREATE)) { + info("Failed to create input device"); + abort(); + } + +/* atexit(&closeDevice); */ + + memset(g_key_states, 0, sizeof(g_key_states)); + memset(m_key_states, 0, sizeof(m_key_states)); + memset(l_key_states, 0, sizeof(l_key_states)); +} + + +static void drv_G15_KBThread(void __attribute__ ((unused)) * notused) +{ + unsigned char buffer[9]; + int ret; + while (1) { + mutex_lock(kb_mutex); + ret = usb_bulk_read(g15_lcd, 0x81, (char *) buffer, 9, 10); +/* ret = usb_interrupt_read(g15_lcd, 0x81, (char*)buffer, 9, 10); */ + mutex_unlock(kb_mutex); + if (ret == 9) { + drv_G15_processKeyEvent(buffer); + } + } +} + +static int drv_G15_open() +{ + struct usb_bus *bus; + struct usb_device *dev; + char dname[32] = { 0 }; + int interf = -1; + int config = 1; + int retval; + + g15_lcd = NULL; + + info("%s: Scanning USB for G-15 keyboard or Z-10 speaker ...", Name); + + usb_init(); + usb_set_debug(0); // 0: no, 1 error, 2 warn, 3 info + debug("%s: found %d USB busses", Name, usb_find_busses()); + debug("%s: found %d USB devices", Name, usb_find_devices()); + + for (bus = usb_get_busses(); bus; bus = bus->next) { + for (dev = bus->devices; dev; dev = dev->next) { + debug("%s: open %s/%s/%s", Name, bus->dirname, dev->bus->dirname, dev->filename); + if (dev->descriptor.idVendor == G15_VENDOR) { + if ((g15_lcd = usb_open(dev))) { + // get vendor name if possible + if (dev->descriptor.iManufacturer) { + retval = usb_get_string_simple(g15_lcd, dev->descriptor.iManufacturer, dname, sizeof(dname)); + if (retval <= 0) { + snprintf(dname, sizeof(dname), "(unknown)"); + } + } + debug("%s: Found USB vendor ID 0x%x (%s), checking productID 0x%x...", + Name, G15_VENDOR, dname, dev->descriptor.idProduct); + switch (dev->descriptor.idProduct) { + case G15_DEVICE: + case G15_DEVICE2: + case M1730_DEVICE: + { + info("%s: Found Logitech G-15 or Dell M1730 Keyboard", Name); + interf = 0; + config = 1; + usb_endpoint = 0x02; + break; + } + case Z10_DEVICE: + { + info("%s: Found Logitech Z-10 Speaker", Name); + interf = 2; + usb_endpoint = 0x03; + break; + } + default: + debug("%s: Don't found USB product IDs 0x%x|0x%x/0x%x for G-15/M1730 or 0x%x for Z10", + Name, G15_DEVICE, G15_DEVICE2, M1730_DEVICE, Z10_DEVICE); + usb_close(g15_lcd); + } + + if (interf >= 0) { + debug("%s: Vendor 0x%x Product 0x%x found", + Name, dev->descriptor.idVendor, dev->descriptor.idProduct); + +#ifdef LIBUSB_HAS_GET_DRIVER_NP + /* detach from the kernel if we need to */ + retval = usb_get_driver_np(g15_lcd, interf, dname, 31); + debug("%s: Ret %i from usb_get_driver_np(interf.%d), Drivername %s", + Name, retval, interf, dname); + switch (retval) { + case -EPERM: + error("%s: Permission denied! eUID of this process is %i %s", + Name, geteuid(), geteuid() != 0 ? "(not root)" : ""); + //return -1; + break; + case -ENODATA: + error("%s: No data available! Device switched off?", Name); + //return -1; + break; + } +#ifdef LIBUSB_HAS_DETACH_KERNEL_DRIVER_NP + if (retval == 0 && strcmp(dname, "usbhid") == 0) { + debug("%s: detaching...", Name); + usb_detach_kernel_driver_np(g15_lcd, interf); + } +#endif // detach_kernel_driver_np +#endif // get_driver_np + + retval = usb_set_configuration(g15_lcd, config); + debug("%s: Ret %d from usb_set_configuration(%d)", Name, retval, config); + switch (retval) { + case -EPERM: + error("%s: Permission denied! eUID of this process is %i %s", + Name, geteuid(), geteuid() != 0 ? "(not root)" : ""); + return -1; + break; + case -EBUSY: + error("%s: Device or resource busy! Device switched off?", Name); + return -1; + break; + } + usleep(100); + retval = usb_claim_interface(g15_lcd, interf); + debug("%s: Ret %i from usb_claim_interface(%d)", Name, retval, interf); + return retval; + } + + } + } // G15_Vendor + } // all devices + } // all busses + + return -1; +} + + +static int drv_G15_close(void) +{ + usb_release_interface(g15_lcd, 0); + if (g15_lcd) + usb_close(g15_lcd); + + return 0; +} + + +static void drv_G15_update_img() +{ + int i, j, k; + unsigned char *output = malloc(DCOLS * DROWS * sizeof(unsigned char)); + int retval; + + DEBUG("entered"); + if (!output) + return; + + DEBUG("memory allocated"); + memset(output, 0, DCOLS * DROWS); + DEBUG("memory set"); + output[0] = 0x03; + DEBUG("first output set"); + + for (k = 0; k < 6; k++) { + for (i = 0; i < DCOLS; i++) { + int maxj = (k == 5) ? 3 : 8; + for (j = 0; j < maxj; j++) { + if (g15_image[(k * 1280) + i + (j * DCOLS)]) + output[32 + i + (k * DCOLS)] |= (1 << j); + } + } + } + + DEBUG("output array prepared"); + mutex_lock(kb_mutex); + retval = usb_interrupt_write(g15_lcd, usb_endpoint, (char *) output, 992, 1000); + //info("%s: Ret %i from usb_interrupt_write(endpoint %d)", Name, retval, usb_endpoint); + mutex_unlock(kb_mutex); + usleep(300); + + DEBUG("data written to LCD"); + + free(output); + + DEBUG("memory freed"); + DEBUG("left"); +} + + + +/* for graphic displays only */ +static void drv_G15_blit(const int row, const int col, const int height, const int width) +{ + int r, c; + + DEBUG("entered"); + + for (r = row; r < row + height; r++) { + for (c = col; c < col + width; c++) { + g15_image[r * DCOLS + c] = drv_generic_graphic_black(r, c); + } + } + + DEBUG("updating image"); + + drv_G15_update_img(); + + DEBUG("left"); +} + + +/* start graphic display */ +static int drv_G15_start(const char *section) +{ + char *s; + + DEBUG("entered"); + + /* read display size from config */ + DROWS = 43; + DCOLS = 160; + + DEBUG("display size set"); + + s = cfg_get(section, "Font", "6x8"); + if (s == NULL || *s == '\0') { + error("%s: no '%s.Font' entry from %s", Name, section, cfg_source()); + return -1; + } + + XRES = -1; + YRES = -1; + if (sscanf(s, "%dx%d", &XRES, &YRES) != 2 || XRES < 1 || YRES < 1) { + error("%s: bad Font '%s' from %s", Name, s, cfg_source()); + return -1; + } + + /* Fixme: provider other fonts someday... */ + if (XRES != 6 && YRES != 8) { + error("%s: bad Font '%s' from %s (only 6x8 at the moment)", Name, s, cfg_source()); + return -1; + } + + DEBUG("Finished config stuff"); + + DEBUG("allocating image buffer"); + /* you surely want to allocate a framebuffer or something... */ + g15_image = malloc(DCOLS * DROWS * sizeof(unsigned char)); + if (!g15_image) + return -1; + DEBUG("allocated"); + memset(g15_image, 0, DCOLS * DROWS); + DEBUG("zeroed"); + + /* open communication with the display */ + DEBUG("opening display..."); + if (drv_G15_open() < 0) { + DEBUG("opening failed"); + return -1; + } + DEBUG("display open"); + + /* reset & initialize display */ + DEBUG("clearing display"); + drv_G15_update_img(); + DEBUG("done"); + + /* + if (cfg_number(section, "Contrast", 0, 0, 255, &contrast) > 0) { + drv_G15_contrast(contrast); + } + */ + s = cfg_get(section, "Uinput", ""); + if (s != NULL && *s != '\0') { + cfg_number(section, "SingleKeyPress", 0, 0, 1, &kb_single_keypress); + drv_G15_initKeyHandling(s); + + DEBUG("creating thread for keyboard"); + kb_mutex = mutex_create(); + kb_thread_pid = thread_create("G15_KBThread", drv_G15_KBThread, NULL); + + DEBUG("done"); + } + DEBUG("left"); + + return 0; +} + + +/****************************************/ +/*** plugins ***/ +/****************************************/ + +/* none */ + + +/****************************************/ +/*** exported functions ***/ +/****************************************/ + + +/* list models */ +int drv_G15_list(void) +{ + printf("Logitech G-15 or Z-10 / Dell M1730"); + return 0; +} + + +/* initialize driver & display */ +int drv_G15_init(const char *section, const int quiet) +{ + int ret; + + info("%s: %s", Name, "$Rev: 1117 $"); + + DEBUG("entered"); + + /* real worker functions */ + drv_generic_graphic_real_blit = drv_G15_blit; + + /* start display */ + if ((ret = drv_G15_start(section)) != 0) + return ret; + + /* initialize generic graphic driver */ + if ((ret = drv_generic_graphic_init(section, Name)) != 0) + return ret; + + if (!quiet) { + char buffer[40]; + + qprintf(buffer, sizeof(buffer), "%s %dx%d", Name, DCOLS, DROWS); + if (drv_generic_graphic_greet(buffer, NULL)) { + sleep(3); + drv_generic_graphic_clear(); + } + } + + /* register plugins */ + /* none at the moment... */ + + + DEBUG("left"); + + return 0; +} + + + +/* close driver & display */ +int drv_G15_quit(const int quiet) +{ + info("%s: shutting down.", Name); + + DEBUG("clearing display"); + /* clear display */ + drv_generic_graphic_clear(); + + DEBUG("saying goodbye"); + /* say goodbye... */ + if (!quiet) { + drv_generic_graphic_greet("goodbye!", NULL); + } + + DEBUG("generic_graphic_quit()"); + drv_generic_graphic_quit(); + + mutex_destroy(kb_mutex); + usleep(10 * 1000); + thread_destroy(kb_thread_pid); + + drv_G15_closeUIDevice(); + DEBUG("closing UInputDev"); + + + DEBUG("closing connection"); + drv_G15_close(); + + DEBUG("freeing image alloc"); + free(g15_image); + + return (0); +} + + +DRIVER drv_G15 = { + .name = Name, + .list = drv_G15_list, + .init = drv_G15_init, + .quit = drv_G15_quit, +}; -- cgit v1.2.3