Bugfix RC-5 for picoLCD 20x2 and change to dynamic IR sync.

Use consistent indent style.
This commit is contained in:
mmdolze
2009-11-01 15:08:33 +00:00
parent 6540908435
commit 33d1f3ce96
5 changed files with 163 additions and 178 deletions
+1
View File
@@ -12,6 +12,7 @@ v.0.5dev (ongoing development)
* Include <errno.h> instead of <sys/errno.h> everywhere (found by S. Klauer)
* configure: Fix de-selecting of drivers
* Build system: Add a check testing if SA_RESTART flag is available.
* picolcd driver: Fix RC-5 for picoLCD 20x2 (A. van Schie)
v0.5.3
+ lcdexec: notification when called program finishes
+3 -7
View File
@@ -864,13 +864,9 @@ Key5Light=on
# 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
# Threshold in jiffies of synthesized gap that triggers flushing the IR data to lirc
# [default: 100 (6.1ms); 0 to suppress]
LircFlushThreshold=100
+6 -20
View File
@@ -264,30 +264,16 @@
<varlistentry>
<term>
<property>LircSync</property> =
<property>LircFlushThreshold</property> =
<parameter><replaceable>DURATION</replaceable></parameter>
</term>
<listitem>
<para>
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
<literal>64</literal> jiffies (3.9ms).
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<property>LircLength</property> =
<parameter><replaceable>DURATION</replaceable></parameter>
</term>
<listitem>
<para>
This value determines the length in jiffies (1/16384 seconds) of the entire LIRC code for a single remote
property. It depends on the notion that all propertys 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).
This value is the length in jiffies (1/16384 seconds) of the synthesized sync space that will
trigger sending the queued IR data to LIRC. Values up to 32767 (2s) are permitted, values lower
than 16 will suppress the flushing IR data during processing. The default is
<literal>100</literal> jiffies (6.1ms). It's should only be needed to change this value when
when lircd.conf has a gap &lt; 6100 or the samples &gt; 6300.
</para>
</listitem>
</varlistentry>
+152 -149
View File
@@ -12,6 +12,10 @@
* (c) 2008 Jack Cleaver - add LIRC connection
* (c) 2008 Mini-Box.com Nicu Pavel <npavel@mini-box.com>
* - Added support for 4x20 picoLCD
* (c) 2009 Andries van Schie - Bugfix RC-5 for picoLCD 20x2
* - Changed to dynamic IR sync(space) injection, by timing time between end and start pulse.
* - Queueing IR data to prevent timeouts by LIRC (sending by timeout)
* - Removed usb_clear_halt, because it breaks picoLCD 20x2 (1.57) communication
* License: GPL (same as usblcd and lcdPROC)
*
* picoLCD: http://www.mini-box.com/picoLCD-20x2-OEM
@@ -45,6 +49,7 @@
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/time.h>
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
@@ -93,9 +98,10 @@ typedef struct picolcd_private_data {
struct sockaddr_in lircserver;
/* IR transcode results */
unsigned char result[512];
int sync;
int preset_gap;
int gap;
unsigned char* resptr;
struct timeval lastmsg;
int lastval;
int flush_threshold;
} PrivateData;
/* Private function definitions */
@@ -106,8 +112,8 @@ static void picolcd_20x2_set_char (Driver *drvthis, int n, unsigned char *dat);
static void picolcd_20x4_set_char (Driver *drvthis, int n, unsigned char *dat);
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);
static void picolcd_lircsend(Driver *drvthis);
static void ir_transcode(Driver *drvthis, unsigned char *data, unsigned int cbdata);
picolcd_device picolcd_device_ids[] = {
{
@@ -290,19 +296,28 @@ MODULE_EXPORT int picoLCD_init(Driver *drvthis)
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->flush_threshold = drvthis->config_get_int(drvthis->name, "LircFlushThreshold", 0,
DEFAULT_FLUSH_THRESHOLD_JIFFY);
if (p->flush_threshold < 16) /* Prevent small 'foolish' values these will disable the check also! */
p->flush_threshold = 0x8000; /* Disabled, send check will always fail! */
p->IRenabled = (lirchost != NULL && *lirchost != '\0') ? 1 : 0;
/* Simulate that the last value send was a PULSE,
* so we start with sending a SPACE to make LIRC happy
*/
p->lastval = 0;
p->resptr = p->result;
gettimeofday(&p->lastmsg, NULL);
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 (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;
@@ -311,11 +326,11 @@ MODULE_EXPORT int picoLCD_init(Driver *drvthis)
/* 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_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: IR events will be sent to LIRC on %s:%d, with flush threshold=%d",
drvthis->name, lirchost, lircport, p->flush_threshold);
}
report(RPT_INFO, "%s: init complete", drvthis->name);
@@ -650,12 +665,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:
p->ccmode = custom;
p->ccmode = custom;
picoLCD_set_char(drvthis, 7, heart_filled);
picoLCD_chr(drvthis, x, y, 7);
break;
case ICON_HEART_OPEN:
p->ccmode = custom;
p->ccmode = custom;
picoLCD_set_char(drvthis, 7, heart_open);
picoLCD_chr(drvthis, x, y, 7);
break;
@@ -684,38 +699,29 @@ MODULE_EXPORT int picoLCD_icon (Driver *drvthis, int x, int y, int icon)
MODULE_EXPORT char *picoLCD_get_key(Driver *drvthis)
{
PrivateData *p = drvthis->private_data;
lcd_packet *keydata;
lcd_packet keydata;
char *keystr = NULL;
int keys_read = 0;
int key_pass = 0;
int two_keys = 0;
int ret;
debug(RPT_DEBUG, "%s: get_key start (timeout %d)",
drvthis->name, p->key_timeout);
keydata = malloc(sizeof(lcd_packet));
if (keydata == NULL) {
report(RPT_ERR, "%s: get_key keydata malloc(%d) failed",
drvthis->name, sizeof(lcd_packet));
return NULL;
}
while (! keys_read) {
get_key_event(p->lcd, keydata, p->key_timeout);
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) {
if (keydata.type == IN_REPORT_KEY_STATE) {
if (! keydata.data[1] && key_pass) {
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) {
else if (! keydata.data[2] && ! two_keys) {
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 = p->device->keymap[keydata->data[1]];
keystr = p->device->keymap[keydata.data[1]];
}
else {
/* We got two keys */
@@ -723,56 +729,33 @@ MODULE_EXPORT char *picoLCD_get_key(Driver *drvthis)
debug(RPT_DEBUG, "%s: get_key got two keys", drvthis->name);
two_keys++;
sprintf(keybuf, "%s+%s", p->device->keymap[keydata->data[1]],
p->device->keymap[keydata->data[2]]);
sprintf(keybuf, "%s+%s", p->device->keymap[keydata.data[1]],
p->device->keymap[keydata.data[2]]);
keystr = keybuf;
}
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;
else if (p->IRenabled && keydata.type == IN_REPORT_IR_DATA) {
debug(RPT_NOTICE, "%s: get_key irdata, length=%d bytes",
drvthis->name, keydata->data[1]);
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);
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);
}
}
/* transcoded data is queued and send when complete or by a timeout */
ir_transcode(drvthis, keydata.data + 2, keydata.data[1]);
}
else {
debug(RPT_DEBUG, "%s: get_key got non-key data or timeout", drvthis->name);
debug(RPT_DEBUG, "%s: get_key got non-key/ir data or timeout", drvthis->name);
if (p->result < p->resptr) {
debug(RPT_INFO, "picolcd: timeout %d send lirc data now", p->key_timeout);
/* Send data maybe is enhough for LIRC */
picolcd_lircsend(drvthis);
}
/* We got IR or otherwise bad data */
free(keydata);
return NULL;
}
}
free(keydata);
debug(RPT_DEBUG, "%s: get_key complete (%s)", drvthis->name, keystr);
if ((keystr != NULL) && (strlen(keystr) > 0))
@@ -970,113 +953,140 @@ MODULE_EXPORT char *picoLCD_get_info(Driver *drvthis)
* \param drvthis Pointer to driver structure [used for debug() and report()].
* \param data Buffer of integers to be transcoded.
* \param cbdata Buffer of integers to be transcoded.
* \param result Buffer to receive the transcoded values.
* \param cbresult Buffer to receive the transcoded values.
* \return Number of bytes placed in result buffer.
*
* \note The picoLCD introduces two issues:
* 1. Every read contains a maximum of 10 samples (20 bytes),
* sending the converted samples direct to LIRC will lead to timeouts,
* in LIRC while we are still waiting for the rest of the samples.
* To fix this I queue the samples and send it when a sync is detected or by a timeout.
* 2. The sync (long space) are not send by the picoLCD.
* To fix this we look for a pulse at the end of the last message and a pulse at the
* begin new message, we then flush the queue and start with a (sync) space, with
* the duration of the time between the last and current message.
*
* \note To make LIRC happy I send the queued samples with the sync space a the begin,
* and not at the end (the next 'calcutated' sync is put at the begin of the next message),
* this is because LIRC requires a space at the begin but will solves the missing space
* with a timeout at the end.
*/
static int ir_transcode(Driver *drvthis, unsigned char *data, unsigned int cbdata,
unsigned char *result, int cbresult)
static void ir_transcode(Driver *drvthis, unsigned char* data, unsigned int cbdata)
{
PrivateData *p = drvthis->private_data;
int i;
int cIntervals = cbdata >> 1;
int resptr = 0;
long w = (data[1] << 8) + data[0];
long w = (data[1] << 8) | data[0];
struct timeval now;
//Check for odd buffer length (invalid buffer)
/* Check for odd buffer length (invalid buffer) */
if (cbdata & 1) {
return -1;
return;
}
/*
* 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;
/* Get time needed to calculate the time between 2 ir data messages */
gettimeofday(&now, 0);
/* Check for a missing SPACE since the last message */
debug(RPT_INFO, "picolcd: last 0x04x first %04x", p->lastval, (-w & 0xFFFF));
if (((p->lastval & 0x8000) == 0) && ((-w & 0x8000) == 0)) {
/* Calculate the time passed from the last ir message to now
* and use that time for the missing space (sync) */
int secs = now.tv_sec - p->lastmsg.tv_sec;
int gap = 0x7FFF;
/* previous message is complete send it, without the added space */
debug(RPT_INFO, "picolcd: missing sync detected, flushing queue before adding sync");
picolcd_lircsend(drvthis);
/* Prevent the overflow (2 secs = 32678 jiffies), but allow 2.99 seconds to reach the max */
if (secs <= 2) {
/* microseconds to jiffies (same as (16384/1000000) but no possible int32 overflow) */
gap = ((now.tv_usec - p->lastmsg.tv_usec + secs * 1000000) * 256) / 15625;
/* Check overflow */
if (gap >= 0x8000) {
gap = 0x7FFF;
}
p->gap = p->preset_gap;
debug(RPT_DEBUG, "%s: preset gap, length=%d jiffies",
drvthis->name, p->gap);
}
/* Make it a space */
gap |= 0x8000;
debug(RPT_INFO, "picolcd: injecting space %04hx between %04hx and %04hx",
gap, p->lastval, -w & 0xFFFF);
*p->resptr++ = (unsigned char)(gap & 0xff);
*p->resptr++ = (unsigned char)((gap >> 8) & 0xff);
}
/* Check if there is enhough space left in buffer to store all new samples */
else if (cbdata >= (&p->result[sizeof(p->result)] - p->resptr)) {
/* This should never happen but just to be sure. */
debug(RPT_INFO, "picolcd: buffer almost full send lirc data now");
picolcd_lircsend(drvthis);
}
for (i = 0; i < cIntervals; i++) {
long w = (data[i*2 + 1] << 8) + data[i*2];
w = *data++;
w |= *data++ << 8;
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;
w = (w * 16384/ 1000000) & 0xFFFF;
}
else {
//Scale.
w = w * 16384 / 1000000;
if (w >= p->flush_threshold) {
report(RPT_INFO, "picolcd: detected sync space sending lirc data now");
picolcd_lircsend(drvthis);
}
//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;
}
*p->resptr++ = (unsigned char)(w & 0xff);
*p->resptr++ = (unsigned char)((w >> 8) & 0xff);
}
/*
* 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;
p->lastval = w;
p->lastmsg = now;
/* Look for a short buffer (a full buffer has 10 samples) with a terminal PULSE */
if ((cIntervals < 10) && ((w & 0x8000) == 0)) {
debug(RPT_INFO, "picolcd: IR data end detected sending lirc data now");
picolcd_lircsend(drvthis);
}
}
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);
}
/**
* Send any queued IR samples to LIRC
* \param drvthis Pointer to driver structure.
*/
static void picolcd_lircsend(Driver *drvthis)
{
PrivateData *p = drvthis->private_data;
int len = p->resptr - p->result;
if (len > 0) {
#ifdef DEBUG
debug(RPT_INFO, "picolcd: sending LIRCD %d samples", len/2);
{
unsigned char *ptr = p->result;
unsigned char *endptr = ptr + len;
char logbuf[sizeof(p->result) * 3]; /* every 2 bytes become 5 bytes " xxxx" */
char* logptr = logbuf;
while (ptr < endptr) {
unsigned int val = *ptr++;
val |= *ptr++ << 8;
logptr += sprintf(logptr, " %04x", val);
}
else {
debug(RPT_DEBUG, "%s: not appending gap because it would be negative (length=%d jiffies)", drvthis->name, p->gap);
debug(RPT_DEBUG, "picolcd: data:%s", logbuf);
}
#endif
if (sendto(p->lircsock, p->result, len, 0,
(struct sockaddr *) &(p->lircserver), sizeof(p->lircserver)) == -1) {
/* Ignore not connected errors when lirc has gone away */
if (errno != ECONNREFUSED) {
report(RPT_WARNING, "picolcd: failed to send IR data, reason: %s", strerror(errno));
}
} else {
debug(RPT_DEBUG, "picolcd: send %d bytes to lirc(udp)", len);
}
else {
//Result buffer would overflow
resptr = -1;
}
p->resptr = p->result;
}
else {
debug(RPT_DEBUG, "%s: cIntervals=%d; not appending gap (length=%d jiffies)",
drvthis->name, cIntervals, p->gap);
}
return resptr;
}
@@ -1197,13 +1207,6 @@ static void get_key_event(usb_dev_handle *lcd, lcd_packet *packet, int timeout)
} break;
case IN_REPORT_IR_DATA: {
packet->type = IN_REPORT_IR_DATA;
/*
* clears the halt status on the usb endpoint
* picoLCD 20x4 keeps last ir state without clearing the
* status on endpoint, meaning that we will get same IR data
* over and over till we clear the status manually.
*/
usb_clear_halt(lcd, USB_ENDPOINT_IN + 1);
} break;
default: {
packet->type = 0;
+1 -2
View File
@@ -47,8 +47,7 @@
#define PICOLCD_MAX_DATA_LEN 24
#define DEFAULT_LIRCPORT 8765
#define DEFAULT_SYNC_JIFFY 64
#define DEFAULT_LENGTH_JIFFY 2048
#define DEFAULT_FLUSH_THRESHOLD_JIFFY 100 /* 6.1 millisec */
typedef struct _lcd_packet {