diff --git a/CREDITS b/CREDITS index 13e87fd..bd6637e 100644 --- a/CREDITS +++ b/CREDITS @@ -248,6 +248,8 @@ Christian Jodar Mariusz Bialonczyk - ethlcd connection type for HD44780 +Jack Cleaver + - LIRC support in picolcd Further developers in the sourceforge lcdproc team: diff --git a/ChangeLog b/ChangeLog index 43f6f94..f8adefe 100644 --- a/ChangeLog +++ b/ChangeLog @@ -54,6 +54,7 @@ v.0.5dev (ongoing development) * lcdproc: unbreak compilation on OS X 1.5 and higher (Topher Fangio) * server: fix interpretation of numeric priorities (Andrew M. Bishop) + hd44780 driver: new ConnectionType ethlcd (Mariusz Bialonczyk) + + picolcd driver: support to send IR commands to LIRC (Jack Cleaver) v.0.5.2 * fix switching on/off the Load screen in lcdproc client using the menu diff --git a/LCDd.conf b/LCDd.conf index 7b69d89..67fb1f9 100644 --- a/LCDd.conf +++ b/LCDd.conf @@ -803,6 +803,21 @@ Key3Light=on Key4Light=on Key5Light=on +# Host name or IP address of the LIRC instance that is to receive IR codes +# If not set, or set to an empty value, IR support is disabled. +#LircAddress=127.0.0.1 + +# UDP port on which LIRC is listening [default: 8765; legal: 1 - 65535] +LircPort=8765 + +# Duration in jiffies of the synthesized sync pulse [default: 64; 0 to suppress] +LircSync=64 + +# Duration in jiffies of the synthesized gap; 0 to suppress, default 2048. +# This is really the fixed duration of an entire IR command; individual pulse/space +# durations are deducted from this to determine the duration of the trailing gap. +LircLength=2048 + ## Pyramid LCD driver ## diff --git a/docs/lcdproc-user/drivers/picolcd.docbook b/docs/lcdproc-user/drivers/picolcd.docbook index e565d39..f48b5ce 100644 --- a/docs/lcdproc-user/drivers/picolcd.docbook +++ b/docs/lcdproc-user/drivers/picolcd.docbook @@ -186,6 +186,66 @@ Larger values make key presses more reliable if somewhat slow to take effect. + + + LircHost= + HOSTNAME_OR_IP-ADDRESS + + + + Set the hostname or IP address to which the driver will send IR data from the sensor. + If not set or set to an empty value, IR support for LIRC will be disabled. + + + LIRC should be configured to use the driver "udp", which will cause it to listen on some + UDP port for packets containing a series of integers, representing pulse and mark + intervals from the sensor. It doesn't matter whether LCDd or LIRC is started first; if LIRC + isn't listening, the packets from LCDd will be discarded. When LIRC comes back, it will + start picking up the packets. Similarly, LCDd can be stopped and restarted without affecting + anything, because UDP is a connectionless protocol. + + + + + + LircPort= + PORTNUM + + + + This value determines the UDP port to which the driver will send IR data from the sensor. It + defaults to 8765, which is also the default port on which LIRC will listen. + + + + + + LircSync= + DURATION + + + + This value determines the length in jiffies (1/16384 seconds) of the synthesized sync pulse added + to the beginning of the UDP packet sent to LIRC. Values up to 255 are permitted. Setting a length + of zero will suppress the synthesized pulse (I can't make it work without it, though). The default is + 64 jiffies (3.9ms). + + + + + + LircLength= + DURATION + + + + This value determines the length in jiffies (1/16384 seconds) of the entire LIRC code for a single remote + command. It depends on the notion that all commands are of the same duration, and are padded with a trailing + gap (space) to make up the time. No such gap is emitted by the picolcd hardware. Values up to MAXINT are permitted. + Setting a length of zero will suppress the synthesized gap. The default is 2048 jiffies (125ms). + + + @@ -222,13 +282,31 @@ touchy and both combo key-down and key-up actions may be reported as mutiple events if the user is more than a tenth of a second (maybe less?) off in motions. + + Infrared sensor status + + LIRC expects sensor data that starts with a longish 'sync' space, denoting the start of + a command; followed by the code data, a sequence of mark/space pairs; sometimes followed by + a 'gap', which should be a space long enough to make the entire command up to a preset + duration in milliseconds. The 'sync' and the 'gap' are absent from the data + emitted by the picolcd hardware. I found that LIRC configuration files for remotes similar to the + ones I tested all used such a fixed-duration encoding, and as that was the only way I could get it + working, this driver by default adds the gap as well as the sync. However I have + still had trouble getting irrecord to work; you need at least + to feed it a template configuration containing sync and gap data. + + + This code could therefore use more testing, more research, and more work. + + Copyright - The lcdproc picolcd driver was written by Gatewood Green (woody@nitrosecurity.com) - or (woody@linif.org) and paid for by NitroSecurity, Inc (nitrosecurity.com). + The lcdproc picolcd driver originally was written by Gatewood Green (woody@nitrosecurity.com) + or (woody@linif.org) and paid for by NitroSecurity, Inc (nitrosecurity.com), + but has been extended with code from various contributors since then. diff --git a/server/drivers/picolcd.c b/server/drivers/picolcd.c index 4953db0..c8b07cf 100644 --- a/server/drivers/picolcd.c +++ b/server/drivers/picolcd.c @@ -3,10 +3,11 @@ * * (c) 2007 NitroSecurity, Inc. * Written by Gatewood Green or - * (c) 2007 Peter Marschall - adapted coding style and reporting to LCDproc + * (c) 2007-2008 Peter Marschall - adapted coding style and reporting to LCDproc * (c) 2007 Mini-Box.com, Nicu Pavel * - removed libusblcd and hid dependency * - added vbar, hbar, custom char, bignum support + * (c) 2008 Jack Cleaver - add LIRC connection * * License: GPL (same as usblcd and lcdPROC) * @@ -43,6 +44,10 @@ #include #include #include +#include +#include +#include +#include /* 12 keys plus a 0 placeholder */ #define KEYPAD_MAX 13 @@ -83,6 +88,16 @@ typedef struct pd { char *info; unsigned char *framebuf; unsigned char *lstframe; + + int IRenabled; + //For communicating with LIRC + int lircsock; + struct sockaddr_in lircserver; + /* IR transcode results */ + unsigned char result[512]; + int sync; + int preset_gap; + int gap; } PrivateData; static char * keymap[KEYPAD_MAX] = { @@ -106,6 +121,8 @@ static void picolcd_send(usb_dev_handle *lcd, unsigned char *data, int size); static void picolcd_write(usb_dev_handle *lcd, const int row, const int col, const unsigned char *data); static void get_key_event (usb_dev_handle *lcd, lcd_packet *packet, int timeout); static void set_key_lights (usb_dev_handle *lcd, int keys[], int state); +static int ir_transcode(Driver *drvthis, unsigned char *data, unsigned int cbdata, + unsigned char *result, int cbresult); /* lcd_logical_driver Variables */ MODULE_EXPORT char *api_version = API_VERSION; @@ -115,17 +132,18 @@ MODULE_EXPORT char *symbol_prefix = "picoLCD_"; /* lcd_logical_driver mandatory functions */ MODULE_EXPORT int picoLCD_init(Driver *drvthis) { - PrivateData *pd; + PrivateData *p; int x; struct usb_bus *bus; struct usb_device *dev; + const char *lirchost; + int lircport; - pd = (PrivateData *) malloc(sizeof(PrivateData)); - - if (! pd) + p = (PrivateData *) malloc(sizeof(PrivateData)); + if (p == NULL) return -1; - if (drvthis->store_private_ptr(drvthis, pd)) + if (drvthis->store_private_ptr(drvthis, p)) return -1; /* Try to find picolcd device */ @@ -133,223 +151,270 @@ MODULE_EXPORT int picoLCD_init(Driver *drvthis) { usb_find_busses(); usb_find_devices(); - pd->lcd = NULL; + p->lcd = NULL; for (bus = usb_get_busses(); bus != NULL; bus = bus->next) { for (dev = bus->devices; dev != NULL; dev = dev->next) { if ((dev->descriptor.idVendor == picoLCD_VENDOR) && (dev->descriptor.idProduct == picoLCD_DEVICE)) { report(RPT_INFO, "Found picoLCD on bus %s device %s", bus->dirname, dev->filename); - pd->lcd = usb_open(dev); + p->lcd = usb_open(dev); goto done; } } } done: - if (pd->lcd != NULL) { + if (p->lcd != NULL) { debug(RPT_DEBUG, "%s: opening device succeeded", drvthis->name); - if (usb_set_configuration(pd->lcd, 0) < 0) { - usb_close(pd->lcd); + if (usb_set_configuration(p->lcd, 0) < 0) { + usb_close(p->lcd); report(RPT_ERR, "%s: unable to set configuration", drvthis->name); return -1; } usleep(100); - if (usb_claim_interface(pd->lcd, 0) < 0) { + if (usb_claim_interface(p->lcd, 0) < 0) { #ifdef LIBUSB_HAS_DETACH_KERNEL_DRIVER_NP - if ((usb_detach_kernel_driver_np(pd->lcd, 0) < 0) || - (usb_claim_interface(pd->lcd, 0) < 0)) { + if ((usb_detach_kernel_driver_np(p->lcd, 0) < 0) || + (usb_claim_interface(p->lcd, 0) < 0)) { #ifdef LIBUSB_HAS_GET_DRIVER_NP char driver[1024]; - if (usb_get_driver_np(pd->lcd, 0, driver, sizeof(driver)) == 0) + if (usb_get_driver_np(p->lcd, 0, driver, sizeof(driver)) == 0) report(RPT_WARNING, "Interface 0 already claimed by '%s'", driver); #endif - usb_close(pd->lcd); + usb_close(p->lcd); report(RPT_ERR, "%s: unable to re-claim interface", drvthis->name); return -1; } #else report(RPT_ERR, "%s: failed to claim interface", drvthis->name); - usb_close(pd->lcd); + usb_close(p->lcd); return -1; #endif } - if (usb_set_altinterface(pd->lcd, 0) < 0) + if (usb_set_altinterface(p->lcd, 0) < 0) report(RPT_WARNING, "%s: unable to set alternate configuration", drvthis->name); } else { report(RPT_ERR, "%s: no device found", drvthis->name); return -1; } - pd->width = 20; /* hard coded (mfg spec) */ - pd->height = 2; /* hard coded (mfg spec) */ - pd->info = "picoLCD: Supports the LCD as installed on the M300 (http://www.mini-box.com/Mini-Box-M300-LCD) "; - pd->cellwidth = LCD_DEFAULT_CELLWIDTH; - pd->cellheight = LCD_DEFAULT_CELLHEIGHT; - pd->ccmode = standard; + p->width = 20; /* hard coded (mfg spec) */ + p->height = 2; /* hard coded (mfg spec) */ + p->info = "picoLCD: Supports the LCD as installed on the M300 (http://www.mini-box.com/Mini-Box-M300-LCD) "; + p->cellwidth = LCD_DEFAULT_CELLWIDTH; + p->cellheight = LCD_DEFAULT_CELLHEIGHT; + p->ccmode = standard; for (x = 0; x < KEYPAD_LIGHTS; x++) - pd->key_light[x] = 1; /* individual lights on */ + p->key_light[x] = 1; /* individual lights on */ - pd->contrast = drvthis->config_get_int( drvthis->name, "Contrast", 0, DEFAULT_CONTRAST ); - pd->backlight = drvthis->config_get_bool(drvthis->name, "BackLight", 0, DEFAULT_BACKLIGHT); - pd->keylights = drvthis->config_get_bool(drvthis->name, "KeyLights", 0, DEFAULT_KEYLIGHTS); /* key lights with LCD Backlight? */ - pd->key_timeout = drvthis->config_get_int( drvthis->name, "KeyTimeout", 0, DEFAULT_TIMEOUT ); + p->contrast = drvthis->config_get_int( drvthis->name, "Contrast", 0, DEFAULT_CONTRAST ); + p->backlight = drvthis->config_get_bool(drvthis->name, "BackLight", 0, DEFAULT_BACKLIGHT); + p->keylights = drvthis->config_get_bool(drvthis->name, "KeyLights", 0, DEFAULT_KEYLIGHTS); /* key lights with LCD Backlight? */ + p->key_timeout = drvthis->config_get_int( drvthis->name, "KeyTimeout", 0, DEFAULT_TIMEOUT ); /* These allow individual lights to be disabled */ - pd->key_light[0] = drvthis->config_get_bool(drvthis->name, "Key0Light", 0, 1); /* Directional PAD */ - pd->key_light[1] = drvthis->config_get_bool(drvthis->name, "Key1Light", 0, 1); /* F1 */ - pd->key_light[2] = drvthis->config_get_bool(drvthis->name, "Key2Light", 0, 1); /* F2 */ - pd->key_light[3] = drvthis->config_get_bool(drvthis->name, "Key3Light", 0, 1); /* F3 */ - pd->key_light[4] = drvthis->config_get_bool(drvthis->name, "Key4Light", 0, 1); /* F4 */ - pd->key_light[5] = drvthis->config_get_bool(drvthis->name, "Key5Light", 0, 1); /* F5 */ + p->key_light[0] = drvthis->config_get_bool(drvthis->name, "Key0Light", 0, 1); /* Directional PAD */ + p->key_light[1] = drvthis->config_get_bool(drvthis->name, "Key1Light", 0, 1); /* F1 */ + p->key_light[2] = drvthis->config_get_bool(drvthis->name, "Key2Light", 0, 1); /* F2 */ + p->key_light[3] = drvthis->config_get_bool(drvthis->name, "Key3Light", 0, 1); /* F3 */ + p->key_light[4] = drvthis->config_get_bool(drvthis->name, "Key4Light", 0, 1); /* F4 */ + p->key_light[5] = drvthis->config_get_bool(drvthis->name, "Key5Light", 0, 1); /* F5 */ for (x = 0; x < KEYPAD_MAX; x++) - pd->key_matrix[x] = keymap[x]; + p->key_matrix[x] = keymap[x]; - pd->framebuf = (unsigned char *) malloc(pd->width * pd->height + 1); - if (pd->framebuf == NULL) { - report(RPT_ERR, "%s: unable to create framebuf", __FUNCTION__); + p->framebuf = (unsigned char *) malloc(p->width * p->height + 1); + if (p->framebuf == NULL) { + report(RPT_ERR, "%s: unable to create framebuf", drvthis->name); return -1; } - memset(pd->framebuf, ' ', pd->width * pd->height); - pd->framebuf[pd->width * pd->height] = 0; + memset(p->framebuf, ' ', p->width * p->height); + p->framebuf[p->width * p->height] = '\0'; - pd->lstframe = (unsigned char *) malloc(pd->width * pd->height + 1); - if (pd->lstframe == NULL) { - report(RPT_ERR, "%s: unable to create lstframe", __FUNCTION__); + p->lstframe = (unsigned char *) malloc(p->width * p->height + 1); + if (p->lstframe == NULL) { + report(RPT_ERR, "%s: unable to create lstframe", drvthis->name); return -1; } - memset(pd->lstframe, ' ', pd->width * pd->height); - pd->lstframe[pd->width * pd->height] = 0; + memset(p->lstframe, ' ', p->width * p->height); + p->lstframe[p->width * p->height] = '\0'; - if (pd->backlight) + if (p->backlight) picoLCD_backlight(drvthis, 1); - if (! pd->keylights) - set_key_lights(pd->lcd, pd->key_light, 0); + if (! p->keylights) + set_key_lights(p->lcd, p->key_light, 0); else picoLCD_backlight(drvthis, 0); - picoLCD_set_contrast(drvthis, pd->contrast); + picoLCD_set_contrast(drvthis, p->contrast); - report(RPT_INFO, "picolcd: init complete", __FUNCTION__); + lirchost = drvthis->config_get_string(drvthis->name, "LircHost", 0, NULL); + lircport = drvthis->config_get_int(drvthis->name, "LircPort", 0, DEFAULT_LIRCPORT); + p->sync = drvthis->config_get_int(drvthis->name, "LircSync", 0, DEFAULT_SYNC_JIFFY); + p->preset_gap = drvthis->config_get_int(drvthis->name, "LircLength", 0, DEFAULT_LENGTH_JIFFY); + + p->IRenabled = (lirchost != NULL && *lirchost != '\0') ? 1 : 0; + + if (p->IRenabled) { + /* Initialize communication with LIRC */ + struct hostent *hostinfo = gethostbyname(lirchost); + + if (hostinfo == NULL) { + report (RPT_ERR, "%s: unknown LIRC host %s", drvthis->name, lirchost); + return -1; + } + if ((p->lircsock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) { + report(RPT_ERR, "%s: failed to create socket to send data to LIRC", drvthis->name); + return -1; + } + + /* Construct the server sockaddr_in structure */ + memset(&p->lircserver, 0, sizeof(p->lircserver)); /* Clear struct */ + p->lircserver.sin_family = AF_INET; /* Internet/IP */ + p->lircserver.sin_addr = *(struct in_addr *) hostinfo->h_addr; /* IP address */ + p->lircserver.sin_port = htons(lircport); /* server port */ + + report(RPT_INFO, "%s: IR events will be sent to LIRC on %s:%d, with sync=%d and length=%d", + drvthis->name, lirchost, lircport, p->sync, p->preset_gap); + } + + report(RPT_INFO, "%s: init complete", drvthis->name); return 0; } -MODULE_EXPORT void picoLCD_close(Driver *drvthis) { - PrivateData *pd = drvthis->private_data; - usb_release_interface(pd->lcd, 0); - usb_close(pd->lcd); +MODULE_EXPORT void picoLCD_close(Driver *drvthis) +{ + PrivateData *p = drvthis->private_data; - debug(RPT_DEBUG, "picolcd: close complete"); + usb_release_interface(p->lcd, 0); + usb_close(p->lcd); + + debug(RPT_DEBUG, "%s: close complete", drvthis->name); } + /* lcd_logical_driver Essential output functions */ -MODULE_EXPORT int picoLCD_width(Driver *drvthis) { - PrivateData *pd = drvthis->private_data; +MODULE_EXPORT int picoLCD_width(Driver *drvthis) +{ + PrivateData *p = drvthis->private_data; - return pd->width; + return p->width; } -MODULE_EXPORT int picoLCD_height(Driver *drvthis) { - PrivateData *pd = drvthis->private_data; - return pd->height; +MODULE_EXPORT int picoLCD_height(Driver *drvthis) +{ + PrivateData *p = drvthis->private_data; + + return p->height; } -MODULE_EXPORT void picoLCD_clear(Driver *drvthis) { - PrivateData *pd = drvthis->private_data; - memset(pd->framebuf, ' ', pd->width * pd->height); - pd->ccmode = standard; +MODULE_EXPORT void picoLCD_clear(Driver *drvthis) +{ + PrivateData *p = drvthis->private_data; - debug(RPT_DEBUG, "picolcd: clear complete"); + memset(p->framebuf, ' ', p->width * p->height); + p->ccmode = standard; + + debug(RPT_DEBUG, "%s: clear complete", drvthis->name); } -MODULE_EXPORT void picoLCD_flush(Driver *drvthis) { - PrivateData *pd = drvthis->private_data; - unsigned char *fb = pd->framebuf; - unsigned char *lf = pd->lstframe; + +MODULE_EXPORT void picoLCD_flush(Driver *drvthis) +{ + PrivateData *p = drvthis->private_data; + unsigned char *fb = p->framebuf; + unsigned char *lf = p->lstframe; static unsigned char text[48]; int i, line, offset; - debug(RPT_DEBUG, "picolcd: flush started"); + debug(RPT_DEBUG, "%s: flush started", drvthis->name); - for (line = 0; line < pd->height; line++) { - memset(text, 0, 48); - offset = line * pd->width; - fb = pd->framebuf + offset; - lf = pd->lstframe + offset; + for (line = 0; line < p->height; line++) { + memset(text, 0, sizeof(text)); + offset = line * p->width; + fb = p->framebuf + offset; + lf = p->lstframe + offset; - for (i = 0; i < pd->width; i++) { + for (i = 0; i < p->width; i++) { if (*fb++ != *lf++) { - strncpy((char *)text, (char *)pd->framebuf + offset, pd->width); - picolcd_write(pd->lcd, line, 0, text); - memcpy(pd->lstframe + offset, pd->framebuf + offset, pd->width); + strncpy((char *)text, (char *)p->framebuf + offset, p->width); + picolcd_write(p->lcd, line, 0, text); + memcpy(p->lstframe + offset, p->framebuf + offset, p->width); - debug(RPT_DEBUG, "picolcd: flush wrote line %d (%s)", line + 1, text); + debug(RPT_DEBUG, "%s: flush wrote line %d (%s)", + drvthis->name, line + 1, text); break; } } } - debug(RPT_DEBUG, "picolcd: flush complete\n\t(%s)\n\t(%s)", pd->framebuf, pd->lstframe); + debug(RPT_DEBUG, "%s: flush complete\n\t(%s)\n\t(%s)", + drvthis->name, p->framebuf, p->lstframe); } -MODULE_EXPORT void picoLCD_string(Driver *drvthis, int x, int y, unsigned char *str) { - PrivateData *pd = drvthis->private_data; + +MODULE_EXPORT void picoLCD_string(Driver *drvthis, int x, int y, unsigned char *str) +{ + PrivateData *p = drvthis->private_data; unsigned char *dest; int len; - debug(RPT_DEBUG, "picolcd: string start (%s)", str); + debug(RPT_DEBUG, "%s: string start (%s)", drvthis->name, str); - if (y < 1 || y > pd->height) + if (y < 1 || y > p->height) return; - if (x < 1 || x > pd->width) + if (x < 1 || x > p->width) return; len = strlen((char *)str); - if (len + x > pd->width) { - debug(RPT_DEBUG, "picolcd: string overlength (>%d). Start: %d Length: %d (%s)", pd->width, x, len ,str); + if (len + x > p->width) { + debug(RPT_DEBUG, "%s: string overlength (>%d). Start: %d Length: %d (%s)", + drvthis->name, p->width, x, len ,str); - len = pd->width - x; /* Copy what we can */ + len = p->width - x; /* Copy what we can */ } x--; y--; /* Convert 1-based to 0-based */ - dest = pd->framebuf + (y * pd->width + x); + dest = p->framebuf + (y * p->width + x); memcpy(dest, str, len * sizeof(char)); - debug(RPT_DEBUG, "picolcd: string complete (%s)", str); + debug(RPT_DEBUG, "%s: string complete (%s)", drvthis->name, str); } -MODULE_EXPORT void picoLCD_chr(Driver *drvthis, int x, int y, unsigned char chr) { - PrivateData *pd = drvthis->private_data; - unsigned char *dest; - debug(RPT_DEBUG, "picolcd: chr start (%c)", chr); - if (y < 1 || y > pd->height) +MODULE_EXPORT void picoLCD_chr(Driver *drvthis, int x, int y, unsigned char chr) +{ + PrivateData *p = drvthis->private_data; + unsigned char *dest; + debug(RPT_DEBUG, "%s: chr start (%c)", drvthis->name, chr); + + if (y < 1 || y > p->height) return; - if (x < 1 || x > pd->width) + if (x < 1 || x > p->width) return; x--; y--; /* Convert 1-based to 0-based */ - dest = pd->framebuf + (y * pd->width + x); + dest = p->framebuf + (y * p->width + x); memcpy(dest, &chr, sizeof(char)); - debug(RPT_DEBUG, "picolcd: chr complete (%c)", chr); + debug(RPT_DEBUG, "%s: chr complete (%c)", drvthis->name, chr); } MODULE_EXPORT void picoLCD_set_char (Driver *drvthis, int n, unsigned char *dat) { - PrivateData *pd = drvthis->private_data; + PrivateData *p = drvthis->private_data; unsigned char packet[10] = { 0x9c }; /* define character */ - unsigned char mask = (1 << pd->cellwidth) - 1; + unsigned char mask = (1 << p->cellwidth) - 1; int row; if ((n < 0) || (n >= NUM_CCs)) @@ -359,13 +424,14 @@ MODULE_EXPORT void picoLCD_set_char (Driver *drvthis, int n, unsigned char *dat) packet[1] = n; /* Custom char to define. */ - for (row = 0; row < pd->cellheight; row++) { + for (row = 0; row < p->cellheight; row++) { packet[row + 2] = dat[row] & mask; } - picolcd_send(pd->lcd, packet, 10); + picolcd_send(p->lcd, packet, 10); } + MODULE_EXPORT int picoLCD_get_free_chars (Driver *drvthis) { return NUM_CCs; @@ -374,80 +440,80 @@ MODULE_EXPORT int picoLCD_get_free_chars (Driver *drvthis) MODULE_EXPORT void picoLCD_vbar (Driver *drvthis, int x, int y, int len, int promille, int options) { - PrivateData *pd = drvthis->private_data; + PrivateData *p = drvthis->private_data; - if (pd->ccmode != vbar) { - unsigned char vBar[pd->cellheight]; + if (p->ccmode != vbar) { + unsigned char vBar[p->cellheight]; int i; - if (pd->ccmode != standard) { + if (p->ccmode != standard) { /* Not supported(yet) */ report(RPT_WARNING, "%s: vbar: cannot combine two modes using user-defined characters", drvthis->name); return; } - pd->ccmode = vbar; + p->ccmode = vbar; memset(vBar, 0x00, sizeof(vBar)); - for (i = 1; i < pd->cellheight; i++) { + for (i = 1; i < p->cellheight; i++) { // add pixel line per pixel line ... - vBar[pd->cellheight - i] = 0xFF; + vBar[p->cellheight - i] = 0xFF; picoLCD_set_char(drvthis, i, vBar); } } - lib_vbar_static(drvthis, x, y, len, promille, options, pd->cellheight, 0); + lib_vbar_static(drvthis, x, y, len, promille, options, p->cellheight, 0); } - MODULE_EXPORT void picoLCD_hbar (Driver *drvthis, int x, int y, int len, int promille, int options) { - PrivateData *pd = drvthis->private_data; + PrivateData *p = drvthis->private_data; - if (pd->ccmode != hbar) { - unsigned char hBar[pd->cellheight]; + if (p->ccmode != hbar) { + unsigned char hBar[p->cellheight]; int i; - if (pd->ccmode != standard) { + if (p->ccmode != standard) { /* Not supported(yet) */ report(RPT_WARNING, "%s: hbar: cannot combine two modes using user-defined characters", drvthis->name); return; } - pd->ccmode = hbar; + p->ccmode = hbar; memset(hBar, 0x00, sizeof(hBar)); - for (i = 1; i <= pd->cellwidth; i++) { + for (i = 1; i <= p->cellwidth; i++) { // fill pixel columns from left to right. - memset(hBar, 0xFF & ~((1 << (pd->cellwidth - i)) - 1), sizeof(hBar)-1); + memset(hBar, 0xFF & ~((1 << (p->cellwidth - i)) - 1), sizeof(hBar)-1); picoLCD_set_char(drvthis, i, hBar); } } - lib_hbar_static(drvthis, x, y, len, promille, options, pd->cellwidth, 0); + lib_hbar_static(drvthis, x, y, len, promille, options, p->cellwidth, 0); } + MODULE_EXPORT void picoLCD_num (Driver *drvthis, int x, int num) { - PrivateData *pd = drvthis->private_data; + PrivateData *p = drvthis->private_data; int do_init = 0; if ((num < 0) || (num > 10)) return; - if (pd->ccmode != bignum) { - if (pd->ccmode != standard) { + if (p->ccmode != bignum) { + if (p->ccmode != standard) { /* Not supported (yet) */ report(RPT_WARNING, "%s: num: cannot combine two modes using user-defined characters", drvthis->name); return; } - pd->ccmode = bignum; + p->ccmode = bignum; do_init = 1; } @@ -459,7 +525,7 @@ MODULE_EXPORT void picoLCD_num (Driver *drvthis, int x, int num) MODULE_EXPORT int picoLCD_icon (Driver *drvthis, int x, int y, int icon) { - PrivateData *pd = drvthis->private_data; + PrivateData *p = drvthis->private_data; /* 8x5 icons each number represents one row in binary */ @@ -475,12 +541,12 @@ MODULE_EXPORT int picoLCD_icon (Driver *drvthis, int x, int y, int icon) picoLCD_chr(drvthis, x, y, 255); break; case ICON_HEART_FILLED: - pd->ccmode = custom; + p->ccmode = custom; picoLCD_set_char(drvthis, 7, heart_filled); picoLCD_chr(drvthis, x, y, 7); break; case ICON_HEART_OPEN: - pd->ccmode = custom; + p->ccmode = custom; picoLCD_set_char(drvthis, 7, heart_open); picoLCD_chr(drvthis, x, y, 7); break; @@ -497,43 +563,72 @@ MODULE_EXPORT int picoLCD_icon (Driver *drvthis, int x, int y, int icon) return 0; } -/* lcd_logical_driver Essential input functions */ +/* lcd_logical_driver Essential input functions */ MODULE_EXPORT char *picoLCD_get_key(Driver *drvthis) { - PrivateData *pd = drvthis->private_data; + PrivateData *p = drvthis->private_data; lcd_packet *keydata; char *keystr = NULL; int keys_read = 0; int key_pass = 0; int two_keys = 0; - debug(RPT_DEBUG, "picolcd: get_key start (timeout %d)", pd->key_timeout); + debug(RPT_DEBUG, "%s: get_key start (timeout %d)", + drvthis->name, p->key_timeout); keydata = malloc(sizeof(lcd_packet)); while (! keys_read) { - get_key_event(pd->lcd, keydata, pd->key_timeout); - debug(RPT_DEBUG, "picolcd: get_key got an event"); + get_key_event(p->lcd, keydata, p->key_timeout); + debug(RPT_DEBUG, "%s: get_key got an event", drvthis->name); if (keydata->type == IN_REPORT_KEY_STATE) { if (! keydata->data[1] && key_pass) { - debug(RPT_DEBUG, "picolcd: get_key got all clear"); + debug(RPT_DEBUG, "%s: get_key got all clear", drvthis->name); /* Got a <0, 0> key-up event after reading a valid key press event */ keys_read++; /* All clear */ } else if (! keydata->data[2] && ! two_keys) { - debug(RPT_DEBUG, "picolcd: get_key got one key"); + debug(RPT_DEBUG, "%s: get_key got one key", drvthis->name); /* We got one key (but not after a two key event and before and all clear) */ - keystr = pd->key_matrix[keydata->data[1]]; + keystr = p->key_matrix[keydata->data[1]]; } else { /* We got two keys */ - debug(RPT_DEBUG, "picolcd: get_key got two keys"); + debug(RPT_DEBUG, "%s: get_key got two keys", drvthis->name); two_keys++; - sprintf(keystr, "%s+%s", pd->key_matrix[keydata->data[1]], pd->key_matrix[keydata->data[2]]); + sprintf(keystr, "%s+%s", p->key_matrix[keydata->data[1]], p->key_matrix[keydata->data[2]]); } key_pass++; /* This hack allows us to deal with receiving left over <0,0> first */ + } else if (p->IRenabled && keydata->type == IN_REPORT_IR_DATA) { + int cbres; + + debug(RPT_NOTICE, "%s: get_key irdata, length=%d bytes", + drvthis->name, keydata->data[1]); + + cbres = ir_transcode(drvthis, (keydata->data)+2, keydata->data[1], p->result, sizeof(p->result)); + debug(RPT_NOTICE, "%s: get_key irdata transcoded, count=%d bytes", + drvthis->name, cbres); + + if (cbres < 0) { + report(RPT_ERR, "%s: could not transcode buffer, length=%d", + drvthis->name, keydata->data[1]); + } else { + debug(RPT_NOTICE, "%s: sending packet to lirc, length=%d", + drvthis->name, cbres); + int ret = sendto(p->lircsock, p->result, cbres, 0, + (struct sockaddr *) &(p->lircserver), sizeof(p->lircserver)); + if (ret == -1) { + report(RPT_ERR, "%s: error sending UDP packet, errno=%d", + drvthis->name, errno); + } else if (ret != cbres) { + report(RPT_ERR, "%s: mismatch in number of bytes sent (%d!=%d)", + drvthis->name, cbres, ret); + } else { + debug(RPT_NOTICE, "%s: packet sent to lirc.", drvthis->name); + } + } } else { - debug(RPT_DEBUG, "picolcd: get_key got non-key data or timeout"); + debug(RPT_DEBUG, "%s: get_key got non-key data or timeout", drvthis->name); /* We got IR or otherwise bad data */ free(keydata); return NULL; @@ -543,7 +638,7 @@ MODULE_EXPORT char *picoLCD_get_key(Driver *drvthis) { free(keydata); - debug(RPT_DEBUG, "picolcd: get_key complete (%s)", keystr); + debug(RPT_DEBUG, "%s: get_key complete (%s)", drvthis->name, keystr); if ((keystr != NULL) && (strlen(keystr) > 0)) return keystr; @@ -591,18 +686,22 @@ MODULE_EXPORT char *picoLCD_get_key(Driver *drvthis) { } + /* lcd_logical_driver Extended output functions */ /* lcd_logical_driver User-defined character functions */ /* lcd_logical_driver Hardware functions */ -/*MODULE_EXPORT int picoLCD_get_contrast(Driver *drvthis) { - PrivateData *pd = drvthis->private_data; +/*MODULE_EXPORT int picoLCD_get_contrast(Driver *drvthis) +{ + PrivateData *p = drvthis->private_data; }*/ -MODULE_EXPORT int picoLCD_set_contrast(Driver *drvthis, int promille) { - PrivateData *pd = drvthis->private_data; + +MODULE_EXPORT int picoLCD_set_contrast(Driver *drvthis, int promille) +{ + PrivateData *p = drvthis->private_data; int inv; /* The hardware seems to go dark on higher values, so we turn it around */ unsigned char packet[2] = { 0x92 }; /* set contrast id */ @@ -617,38 +716,43 @@ MODULE_EXPORT int picoLCD_set_contrast(Driver *drvthis, int promille) { return -1; } - picolcd_send(pd->lcd, packet, 2); + picolcd_send(p->lcd, packet, 2); + return 0; - - } -/*MODULE_EXPORT int picoLCD_get_brightness(Driver *drvthis, int state) { - PrivateData *pd = drvthis->private_data; + +/*MODULE_EXPORT int picoLCD_get_brightness(Driver *drvthis, int state) +{ + PrivateData *p = drvthis->private_data; }*/ -/*MODULE_EXPORT int picoLCD_set_brightness(Driver *drvthis, int state, int promille) { - PrivateData *pd = drvthis->private_data; + +/*MODULE_EXPORT int picoLCD_set_brightness(Driver *drvthis, int state, int promille) +{ + PrivateData *p = drvthis->private_data; }*/ -MODULE_EXPORT void picoLCD_backlight(Driver *drvthis, int state) { - PrivateData *pd = drvthis->private_data; + +MODULE_EXPORT void picoLCD_backlight(Driver *drvthis, int state) +{ + PrivateData *p = drvthis->private_data; unsigned char packet[2] = { 0x91 }; /* set backlight id */ if (state == 0) { packet[1] = state; - picolcd_send(pd->lcd, packet, 2); - set_key_lights(pd->lcd, pd->key_light, state); + picolcd_send(p->lcd, packet, 2); + set_key_lights(p->lcd, p->key_light, state); return; } if (state == 1) { packet[1] = state; - picolcd_send(pd->lcd, packet, 2); - if (pd->keylights) - set_key_lights(pd->lcd, pd->key_light, state); + picolcd_send(p->lcd, packet, 2); + if (p->keylights) + set_key_lights(p->lcd, p->key_light, state); return; } @@ -656,24 +760,143 @@ MODULE_EXPORT void picoLCD_backlight(Driver *drvthis, int state) { } /*MODULE_EXPORT int picoLCD_output(Driver *drvthis, int state) { - PrivateData *pd = drvthis->private_data; + PrivateData *p = drvthis->private_data; }*/ + /* lcd_logical_driver Informational functions */ MODULE_EXPORT char *picoLCD_get_info(Driver *drvthis) { - PrivateData *pd = drvthis->private_data; + PrivateData *p = drvthis->private_data; - return pd->info; + return p->info; } + /* Private functions */ +/* + * Transcode from picoLCD USB format to LIRC UDP format. + * LIRC UDP packets expect 16-bit intervals, with MSB set for space. + * Intervals are measured in jiffies (1/16384 s). + * PicoLCD USB packets contain 16-bit intervals, with value negated + * for space. Intervals are in microseconds. + * PicoLCD presents the bytes in network order, and they must be put back + * in that order for transmission via UDP. + * One jiffy == 61 us. 537 us == 9j. + * + * drvthis: driver data (used for debug() and report()). + * data, cbdata: buffer of integers to be transcoded. + * result, + * cbresult: buffer to receive the transcoded values. + * @return: number of bytes placed in result buffer. + */ +static int ir_transcode(Driver *drvthis, unsigned char *data, unsigned int cbdata, + unsigned char *result, int cbresult) +{ + PrivateData *p = drvthis->private_data; + int i; + int cIntervals = cbdata >> 1; + int resptr = 0; + long w = (data[1] << 8) + data[0]; + + //Check for odd buffer length (invalid buffer) + if (cbdata & 1) { + return -1; + } + + /* + * Look for an initial long PULSE, and frig it for LIRC's benefit: + * add a sync SPACE in front. + */ + if (w & 0x8000) { /* SPACE */ + w = 65536 - w; + if (w > 8000) { + /* + * The signal was a space longer than 8000 ms, i.e. probably a sync. We + * now expect from picoLCD either a repeat-code, or a long pulse followed + * by a full code. + * Lirc expects a sync (SPACE) followed by either a header (pulse/space) + * or a repeat-code. + * Lirc is also expecting active-low signalling. So we emit the required + * sync, then the signals from picoLCD with arity inverted. + * Emit sync space to LIRC (0x8040, = 64 jiffies, = 3900usec). + */ + if (p->sync) { + result[resptr++] = p->sync; + result[resptr++] = 0x80; + } + p->gap = p->preset_gap; + debug(RPT_DEBUG, "%s: preset gap, length=%d jiffies", + drvthis->name, p->gap); + } + } + for (i = 0; i < cIntervals; i++) { + long w = (data[i*2 + 1] << 8) + data[i*2]; + + if (w & 0x8000) { + //IF w is negative THEN negate. E.g. 0xDCA1 (-9055) -> 9055. + w = 0x10000 - w; + //scale: orig is usec, new is jiffy. E.g. 9055usec = 148 jiffy. + w = (w * 16384 /1000000) & 0xFFFF; + p->gap -= w; + } else { + //Scale. + w = w * 16384 / 1000000; + //Set the space bit. + p->gap -= w; + w |= 0x8000; + } + if (resptr + 2 < cbresult) { + result[resptr++] = w & 0xFF; + result[resptr++] = (w >> 8) & 0xFF; + } else { + resptr = -1; + break; + } + } + /* + * Look for a short buffer with a terminal PULSE, and frig it for LIRC's benefit: + * add a gap SPACE after. This is an ugly gash; it won't work if the code ends with + * a buffer containing exactly ten intervals. + */ + if (resptr > 0 && cIntervals < 10) { + if (resptr + 2 < cbresult) { + int last = cIntervals -1; + + w = (data[last*2 + 1] << 8) + data[last*2]; + if (p->gap > 0) { + if (w & 0x8000) { + //terminal pulse + debug(RPT_DEBUG, "%s: appending gap, length=%d jiffies", + drvthis->name, p->gap); + p->gap |= 0x8000; + result[resptr++] = p->gap & 0xFF; + result[resptr++] = (p->gap >>8) & 0xFF; + } else { + debug(RPT_DEBUG, "%s: terminal space=[%04x]; not appending gap (length=%d jiffies)", (unsigned int)(w & 0xffff), drvthis->name, p->gap); + } + } else { + debug(RPT_DEBUG, "%s: not appending gap because it would be negative (length=%d jiffies)", drvthis->name, p->gap); + } + } else { + //Result buffer would overflow + resptr = -1; + } + } else { + debug(RPT_DEBUG, "%s: cIntervals=%d; not appending gap (length=%d jiffies)", + drvthis->name, cIntervals, p->gap); + } + return resptr; +} + + static void picolcd_send(usb_dev_handle *lcd, unsigned char *data, int size) { usb_interrupt_write(lcd, USB_ENDPOINT_OUT + 1, (char *) data, size, 1000); } + static void picolcd_write(usb_dev_handle *lcd, const int row, const int col, const unsigned char *data) { unsigned char packet[64]; @@ -688,7 +911,7 @@ static void picolcd_write(usb_dev_handle *lcd, const int row, const int col, con packet[3] = len; i = 4; - while (len--) { + while (len--) { packet[i++] = *data++; } @@ -696,7 +919,8 @@ static void picolcd_write(usb_dev_handle *lcd, const int row, const int col, con } -void get_key_event (usb_dev_handle *lcd, lcd_packet *packet, int timeout) { +static void get_key_event(usb_dev_handle *lcd, lcd_packet *packet, int timeout) +{ int ret; memset(packet->data, 0, 255); @@ -717,8 +941,9 @@ void get_key_event (usb_dev_handle *lcd, lcd_packet *packet, int timeout) { } } -void set_key_lights (usb_dev_handle *lcd, int keys[], int state) { +static void set_key_lights(usb_dev_handle *lcd, int keys[], int state) +{ unsigned char packet[2] = { 0x81 }; /* set led */ unsigned int leds = 0; int i; @@ -738,3 +963,4 @@ void set_key_lights (usb_dev_handle *lcd, int keys[], int state) { packet[1] = leds; picolcd_send(lcd, packet, 2); } + diff --git a/server/drivers/picolcd.h b/server/drivers/picolcd.h index 059f054..b063ee4 100644 --- a/server/drivers/picolcd.h +++ b/server/drivers/picolcd.h @@ -34,13 +34,19 @@ #define PICOLCD_MAX_DATA_LEN 24 +#define DEFAULT_LIRCPORT 8765 +#define DEFAULT_SYNC_JIFFY 64 +#define DEFAULT_LENGTH_JIFFY 2048 + typedef struct _lcd_packet lcd_packet; + struct _lcd_packet { unsigned char data[255]; unsigned int type; }; + MODULE_EXPORT int picoLCD_init(Driver *drvthis); MODULE_EXPORT void picoLCD_close(Driver *drvthis); MODULE_EXPORT int picoLCD_width(Driver *drvthis);