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);