From 7d4dd740ebb5e476555f619ba8ef98def10225a5 Mon Sep 17 00:00:00 2001 From: mmdolze Date: Sat, 3 Dec 2011 20:10:40 +0000 Subject: [PATCH] Add key pad support to the glcd driver. --- LCDd.conf | 15 ++++ server/drivers/glcd-glcd2usb.c | 46 ++++++++++-- server/drivers/glcd-low.h | 15 ++++ server/drivers/glcd_drv.c | 126 ++++++++++++++++++++++++++++++++- server/drivers/glcd_drv.h | 1 + server/drivers/hd44780.c | 2 +- 6 files changed, 195 insertions(+), 10 deletions(-) diff --git a/LCDd.conf b/LCDd.conf index b3b6962..9876a95 100644 --- a/LCDd.conf +++ b/LCDd.conf @@ -390,6 +390,21 @@ ConnectionType=t6963 # to zero to completely turn off the backlight. [default: 100; legal: 0 - 1000] #OffBrightness=0 +# Time (ms) from first key report to first repeat. Set to 0 to disable repeated +# key reports. [default: 500; legal: 0 - 3000] +#KeyRepeatDelay=500 + +# Time (ms) between repeated key reports. Ignored if KeyRepeatDelay is disabled +# (set to zero). [default: 300; legal: 0 - 3000] +#KeyRepeatInterval=300 + +# Assign key strings to keys. There may be up to 16 keys numbered 'A' to 'Z'. +# By default keys 'A' to 'F' are assigned Up, Down, Left, Right, Enter, Escape. +KeyMap_A=Up +KeyMap_B=Down +KeyMap_C=Enter +KeyMap_D=Escape + # t6963: Parallel port to use [default: 0x378; legal: 0x200 - 0x400] #Port=0x378 diff --git a/server/drivers/glcd-glcd2usb.c b/server/drivers/glcd-glcd2usb.c index 1ad8ec8..d403736 100644 --- a/server/drivers/glcd-glcd2usb.c +++ b/server/drivers/glcd-glcd2usb.c @@ -4,7 +4,7 @@ * * \note This connection type uses the screen size as reported by the glcd2usb * device. Any size configured in LCDd.conf will be ignored. - * \note The glcd2usb with KS108 firmware uses a vertival (aka paged) frame + * \note The glcd2usb with KS108 firmware uses a vertical (aka paged) frame * buffer layout. Other layouts are not supported by this driver. * * \todo Keypad input @@ -33,7 +33,7 @@ #define GLCD2USB_VID 0x1c40 #define GLCD2USB_PID 0x0525 -/* Some useful sortcuts */ +/* Some useful shortcuts */ #define PAGES (p->framebuf.px_height / 8) #define PAGED_SIZE (p->framebuf.px_width * PAGES) @@ -56,7 +56,7 @@ typedef struct glcd_glcd2usb_data { * Note: The glcd2usb device implements a HID device. My first try to use * libhid failed, because glcd2usb uses report descriptors that totally confuse * libhid's parser. Therefor we need to roll our own. The code below has been - * copied from the lcd4linux implementation of this driver. + * copied from the LCD4Linux implementation of this driver. */ /* Error codes */ @@ -123,7 +123,7 @@ usbSetReport(usb_dev_handle * device, int reportType, unsigned char *buffer, int /** * Sends a 'get report' request to the default control end point of glcd2usb - * device. The amout of data read is stored in parameter len. + * device. The amount of data read is stored in parameter len. * * \param device USB device handle of the device to talk to * \param reportType A HID report type (input / output / feature) @@ -132,7 +132,7 @@ usbSetReport(usb_dev_handle * device, int reportType, unsigned char *buffer, int * be allocated beforehand and be at least of 'len' size. * \param len Pointer to an integer with the number of bytes to read * at most. Upon return, this hold the number of bytes - * actually read or -1 if an error occured. + * actually read or -1 if an error occurred. * \return 0 on success, USB_ERROR_IO otherwise */ static int @@ -318,6 +318,37 @@ glcd2usb_blit(PrivateData *p) } +/** + * API: Poll for any pressed keys. Converts the bitmap of keys pressed into a + * scancode (1-4) for each pressed key. + * \note Only single key presses are detected. + */ +unsigned char +glcd2usb_poll_keys(PrivateData *p) +{ + CT_glcd2usb_data *ctd = (CT_glcd2usb_data *) p->ct_data; + unsigned char keycode = 0; + int err = 0, len = 2; + int i; + + if ((err = usbGetReport(ctd->device, USB_HID_REPORT_TYPE_FEATURE, + GLCD2USB_RID_GET_BUTTONS, ctd->tx_buffer.bytes, &len)) != 0) { + p->glcd_functions->drv_report(RPT_ERR, "glcd2usb_poll_keys: Error getting button state: %s", + usbErrorMessage(err)); + return 0; + } + + for (i = 0; i < 4; i++) { + if (ctd->tx_buffer.bytes[1] & (1 << i)) { + keycode = i + 1; + break; + } + } + + return keycode; +} + + /** * API: Initialize glcd2usb connection type. */ @@ -339,6 +370,7 @@ glcd2usb_init(Driver *drvthis) p->glcd_functions->blit = glcd2usb_blit; p->glcd_functions->close = glcd2usb_close; p->glcd_functions->set_backlight = glcd2usb_backlight; + p->glcd_functions->poll_keys = glcd2usb_poll_keys; /* Allocate memory structures */ ctd = (CT_glcd2usb_data *) calloc(1, sizeof(CT_glcd2usb_data)); @@ -407,7 +439,7 @@ found_dev: /* * now try to claim the interface and detach the kernel HID driver on - * linux and other operating systems which support the call. + * 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 @@ -463,7 +495,7 @@ found_dev: ctd->tx_buffer.display_info.width, ctd->tx_buffer.display_info.height); } - /* Allocate the display (turn off the 'screensaver') */ + /* Allocate the display (turn off the 'whirl') */ ctd->tx_buffer.bytes[0] = GLCD2USB_RID_SET_ALLOC; ctd->tx_buffer.bytes[1] = 1; if ((err = usbSetReport(ctd->device, USB_HID_REPORT_TYPE_FEATURE, ctd->tx_buffer.bytes, 2)) != 0) { diff --git a/server/drivers/glcd-low.h b/server/drivers/glcd-low.h index 638da5f..8b15a43 100644 --- a/server/drivers/glcd-low.h +++ b/server/drivers/glcd-low.h @@ -16,6 +16,9 @@ #define GLCD_DEFAULT_CONTRAST 600 #define GLCD_DEFAULT_BRIGHTNESS 800 #define GLCD_DEFAULT_OFFBRIGHTNESS 100 +#define GLCD_KEYPAD_MAX 26 +#define GLCD_DEFAULT_REPEAT_DELAY 500 /* milliseconds */ +#define GLCD_DEFAULT_REPEAT_INTERVAL 300 /* milliseconds */ /** The framebuffer and its properties */ struct glcd_framebuf { @@ -27,11 +30,13 @@ struct glcd_framebuf { /** private data for the \c glcd driver */ typedef struct glcd_private_data { + /* framebuffer and size settings */ struct glcd_framebuf framebuf; /**< the main framebuffer */ int cellwidth; /**< character cell width */ int cellheight; /**< character cell height */ int width; /**< display width in characters */ int height; /**< display height in characters */ + /* low-level hardware-related stuff */ int contrast; /**< current contrast */ int brightness; /**< current brightness (for backlight on) */ int offbrightness; /**< current brightness (for backlight off) */ @@ -39,8 +44,15 @@ typedef struct glcd_private_data { int backlightstate; /**< state of backlight currently used */ struct hwDependentFns *glcd_functions; /**< pointers to low-level functions */ void *ct_data; /**< Connection type specific data */ + /* Renderer settings */ void *render_config; /**< Settings for the font renderer */ char use_ft2; /**< (Not) use FreeType if available */ + /* Keypad related settings */ + char *keyMap[GLCD_KEYPAD_MAX]; /**< Maps scancode number to key strings */ + char *pressed_key; /**< The last key string */ + struct timeval *key_wait_time; /**< Time until key auto repeat */ + int key_repeat_delay; /**< Time until first key repeat */ + int key_repeat_interval; /**< Time between auto repeated keys */ } PrivateData; /** Structure holding pointers to display specific functions */ @@ -61,6 +73,9 @@ typedef struct hwDependentFns { /* Output "data" to output latch if there is one */ void (*output)(PrivateData *p, int data); + /* Poll for key presses */ + unsigned char (*poll_keys)(PrivateData *p); + /* Close the interface on shutdown */ void (*close)(PrivateData *p); } GLCD_functions; diff --git a/server/drivers/glcd_drv.c b/server/drivers/glcd_drv.c index 0a4970c..32583b7 100644 --- a/server/drivers/glcd_drv.c +++ b/server/drivers/glcd_drv.c @@ -52,6 +52,7 @@ #include "report.h" #include "glcd-render.h" #include "shared/defines.h" +#include "timing.h" /* Vars for the server core */ MODULE_EXPORT char *api_version = API_VERSION; @@ -59,6 +60,8 @@ MODULE_EXPORT int stay_in_foreground = 0; MODULE_EXPORT int supports_multiple = 0; MODULE_EXPORT char *symbol_prefix = "glcd_"; +static char *defaultKeyMap[GLCD_KEYPAD_MAX] = {"Up", "Down", "Left", "Right", "Enter", "Escape"}; + /** * Initialize the driver. (Required) * \param drvthis Pointer to driver structure. @@ -113,6 +116,7 @@ glcd_init(Driver *drvthis) p->glcd_functions->set_contrast = NULL; p->glcd_functions->set_backlight = NULL; p->glcd_functions->output = NULL; + p->glcd_functions->poll_keys = NULL; /* Read display size in pixels */ strncpy(size, drvthis->config_get_string(drvthis->name, "Size", 0, GLCD_DEFAULT_SIZE), sizeof(size)); @@ -200,6 +204,52 @@ glcd_init(Driver *drvthis) p->height = p->framebuf.px_height / p->cellheight; debug(RPT_INFO, "%s: Screen size (final) = %dx%d", drvthis->name, p->width, p->height); + /* Initialize key map */ + for (i = 0; i < GLCD_KEYPAD_MAX; i++) { + char buf[40]; + + /* First fill with default value */ + p->keyMap[i] = defaultKeyMap[i]; + + /* Read config value */ + sprintf(buf, "KeyMap_%c", i + 'A'); + s = drvthis->config_get_string(drvthis->name, buf, 0, NULL); + + /* Was a key specified in the config file ? */ + if (s) { + p->keyMap[i] = strdup(s); + debug(RPT_INFO, "%s: Key '%c' = \"%s\"", drvthis->name, i + 'A', s); + } + } + + /* Initialize delay */ + if ((p->key_wait_time = malloc(sizeof(struct timeval))) == NULL) { + report(RPT_ERR, "%s: error allocating memory", drvthis->name); + return -1; + } + timerclear(p->key_wait_time); + + /* Get key auto repeat delay */ + tmp = drvthis->config_get_int(drvthis->name, "KeyRepeatDelay", 0, GLCD_DEFAULT_REPEAT_DELAY); + if (tmp < 0 || tmp > 3000) { + report(RPT_WARNING, "%s: KeyRepeatDelay must be between 0-3000; using default %d", + drvthis->name, GLCD_DEFAULT_REPEAT_DELAY); + tmp = GLCD_DEFAULT_REPEAT_DELAY; + } + p->key_repeat_delay = tmp; + + /* Get key auto repeat interval */ + tmp = drvthis->config_get_int(drvthis->name, "KeyRepeatInterval", 0, GLCD_DEFAULT_REPEAT_INTERVAL); + if (tmp < 0 || tmp > 3000) { + report(RPT_WARNING, "%s: KeyRepeatInterval must be between 0-3000; using default %d", + drvthis->name, GLCD_DEFAULT_REPEAT_INTERVAL); + tmp = GLCD_DEFAULT_REPEAT_INTERVAL; + } + p->key_repeat_interval = tmp; + + debug(RPT_INFO, "%s: Key repeat: delay = %d, interval = %d", + drvthis->name, p->key_repeat_delay, p->key_repeat_interval); + glcd_clear(drvthis); return 0; @@ -556,7 +606,7 @@ glcd_get_brightness(Driver *drvthis, int state) { PrivateData *p = drvthis->private_data; - debug(RPT_INFO, "%s(%i)", __FUNCTION__, state); + debug(RPT_DEBUG, "%s(%i)", __FUNCTION__, state); return (state == BACKLIGHT_ON) ? p->brightness : p->offbrightness; } @@ -573,7 +623,7 @@ glcd_set_brightness(Driver *drvthis, int state, int promille) { PrivateData *p = drvthis->private_data; - debug(RPT_INFO, "%s(%i,%i)", __FUNCTION__, state, promille); + debug(RPT_DEBUG, "%s(%i,%i)", __FUNCTION__, state, promille); /* Check it */ if (promille < 0 || promille > 1000) @@ -634,3 +684,75 @@ glcd_output(Driver *drvthis, int value) if (p->glcd_functions->output != NULL) p->glcd_functions->output(p, value); } + + +/** + * Get key from the key panel connected to the display. + * \param drvthis Pointer to driver structure. + * \return String representation of the key; + * \c NULL if nothing available / unmapped key. + */ +MODULE_EXPORT const char * +glcd_get_key(Driver *drvthis) +{ + PrivateData *p = (PrivateData *) drvthis->private_data; + unsigned char scancode; + char *keystr = NULL; + struct timeval current_time, delay_time; + + /* return "no key pressed" if required functions missing */ + if (p->glcd_functions->poll_keys == NULL) + return NULL; + + /* + * scancode is either '0' (no key pressed) or an index into the key + * string mapping table ('1-26'). + */ + scancode = p->glcd_functions->poll_keys(p); + if (scancode != '\0') { + if (scancode > GLCD_KEYPAD_MAX) + return NULL; + keystr = p->keyMap[scancode - 1]; + } + + /* + * If a key has been pressed and it is not the same as in the previous + * call to this function return that key string and start a timer. If + * it is the same, check if the timer has passed. If not (or the timer + * has been disabled) return no key string. Otherwise set the repeat + * interval timer and return that key. + */ + if (keystr != NULL) { + if (keystr == p->pressed_key) { + if (timerisset(p->key_wait_time)) { + gettimeofday(¤t_time, NULL); + if (timercmp(¤t_time, p->key_wait_time, >)) { + /* Set timer for next key */ + delay_time.tv_sec = p->key_repeat_interval / 1000; + delay_time.tv_usec = (p->key_repeat_interval % 1000) * 1000; + timeradd(¤t_time, &delay_time, p->key_wait_time); + } + else { + return NULL; + } + } + else { + return NULL; + } + } + else { + /* Set the time for repeated key press if enabled */ + if (p->key_repeat_delay > 0) { + gettimeofday(¤t_time, NULL); + delay_time.tv_sec = p->key_repeat_interval / 1000; + delay_time.tv_usec = (p->key_repeat_interval % 1000) * 1000; + timeradd(¤t_time, &delay_time, p->key_wait_time); + } + report(RPT_DEBUG, "%s: New key pressed: %s", drvthis->name, keystr); + } + } + + p->pressed_key = keystr; + + return keystr; +} diff --git a/server/drivers/glcd_drv.h b/server/drivers/glcd_drv.h index 7e2c46a..be3717d 100644 --- a/server/drivers/glcd_drv.h +++ b/server/drivers/glcd_drv.h @@ -26,4 +26,5 @@ MODULE_EXPORT void glcd_set_brightness (Driver *drvthis, int state, int promille MODULE_EXPORT void glcd_backlight (Driver *drvthis, int promille); MODULE_EXPORT void glcd_output(Driver *drvthis, int value); +MODULE_EXPORT const char *glcd_get_key(Driver *drvthis); #endif diff --git a/server/drivers/hd44780.c b/server/drivers/hd44780.c index 26f7aaf..0b80a99 100644 --- a/server/drivers/hd44780.c +++ b/server/drivers/hd44780.c @@ -1233,7 +1233,7 @@ HD44780_get_key(Driver *drvthis) char *keystr = NULL; struct timeval curr_time, time_diff; - /* return "no key pressed" if required functions mission or input disabled */ + /* return "no key pressed" if required function is missing or input disabled */ if ((!p->have_keypad) || (p->hd44780_functions->scankeypad == NULL)) return NULL;