Files
archived-lcd4linux/drv_G15.c
volker f848e286bd additional debug output
git-svn-id: https://ssl.bulix.org/svn/lcd4linux/trunk@1045 3ae390bd-cb1e-0410-b409-cd5a39f66f1f
2009-09-25 12:57:51 +00:00

664 lines
16 KiB
C

/* $Id$
* $URL$
*
* Driver for Logitech G-15 keyboard LCD screen
*
* Copyright (C) 2006 Dave Ingram <dave@partis-project.net>
* Copyright (C) 2005 Michael Reinelt <michael@reinelt.co.at>
* Copyright (C) 2005 The LCD4Linux Team <lcd4linux-devel@users.sourceforge.net>
*
* 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 <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <usb.h>
#include <fcntl.h>
#include <linux/input.h>
#include <linux/uinput.h>
#include <unistd.h>
#include <sys/types.h>
#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 ((g15_lcd = usb_open(dev))) {
if (dev->descriptor.idVendor == G15_VENDOR) {
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:
usb_close(g15_lcd);
}
if (interf >= 0) {
debug("%s: Vendor 0x%x Product 0x%x found",
Name, dev->descriptor.idVendor, dev->descriptor.idProduct);
/* 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;
}
if (retval == 0 && strcmp(dname, "usbhid") == 0) {
debug("%s: detaching...", Name);
usb_detach_kernel_driver_np(g15_lcd, interf);
}
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;
}
}
}
}
}
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(160 * 43 * sizeof(unsigned char));
int retval;
DEBUG("entered");
if (!output)
return;
DEBUG("memory allocated");
memset(output, 0, 160 * 43);
DEBUG("memory set");
output[0] = 0x03;
DEBUG("first output set");
for (k = 0; k < 6; k++) {
for (i = 0; i < 160; i++) {
int maxj = (k == 5) ? 3 : 8;
for (j = 0; j < maxj; j++) {
if (g15_image[(k * 1280) + i + (j * 160)])
output[32 + i + (k * 160)] |= (1 << j);
}
}
}
DEBUG("output array prepared");
mutex_lock(kb_mutex);
//retval = usb_interrupt_write(g15_lcd, 0x02, (char *) output, 992, 1000); // G15
//retval = usb_interrupt_write(g15_lcd, 0x03, (char *) output, 992, 1000); // Z10
retval = usb_interrupt_write(g15_lcd, usb_endpoint, (char *) output, 992, 1000);
//info("%s: Ret %i from usb_interrupt_write(endpoint 2)", Name, retval);
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 * 160 + 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(160 * 43 * sizeof(unsigned char));
if (!g15_image)
return -1;
DEBUG("allocated");
memset(g15_image, 0, 160 * 43);
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$");
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,
};