diff --git a/LCDd.conf b/LCDd.conf
index 485e324..896198a 100644
--- a/LCDd.conf
+++ b/LCDd.conf
@@ -1035,10 +1035,16 @@ Key5Light=on
# UDP port on which LIRC is listening [default: 8765; legal: 1 - 65535]
LircPort=8765
-# Threshold in jiffies of synthesized gap that triggers flushing the IR data
-# to lirc [default: 100 ; max: 32767 ]
-# 100 means 6.1ms. legal: 16 - 32767; Use 0 to disable.
-LircFlushThreshold=100
+# UDP data time unit for LIRC [default: off; legal: on, off]
+# On: times sent in microseconds (requires LIRC UDP driver that accepts this).
+# Off: times sent in 'jiffies' (1/16384s) (supported by standard LIRC UDP driver).
+LircTime_us=on
+
+# Threshold in microseconds of the gap that triggers flushing the IR data
+# to lirc [default: 8000; legal: 1000 - ]
+# If LircTime_us is on values greater than 32.767ms will disable the flush
+# If LircTime_us is off values greater than 1.999938s will disable the flush
+LircFlushThreshold=10000
diff --git a/docs/lcdproc-user/drivers/picolcd.docbook b/docs/lcdproc-user/drivers/picolcd.docbook
index a6d06d5..0ca80eb 100644
--- a/docs/lcdproc-user/drivers/picolcd.docbook
+++ b/docs/lcdproc-user/drivers/picolcd.docbook
@@ -22,7 +22,7 @@
It is an external USB 2.0 full speed device that comes in a stylish case and
sports a 4 line by 20 character display with white letters
on a blue background, a built-in InfraRed receiver as well as a
- keypad with 8 keys labelled Escape, F1,
+ keypad with 8 keys labeled Escape, F1,
F2, F3, Home,
Up, Down and Enter.
@@ -323,7 +323,7 @@
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
+ UDP port for packets containing a series of integers, representing mark and space
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
@@ -345,6 +345,44 @@
+
+
+ LircTime_us = ¶meters.yesnodef;
+
+
+
+ If LircTime_us is set to on mark and space times
+ are sent to LIRC in microseconds (requires a LIRC UDP driver that
+ accepts this).
+
+
+ If LircTime_us is set to off mark and space times
+ are sent to LIRC in 'jiffies' (1/16384s) (supported by the standard LIRC
+ UDP driver).
+
+
+ Default is off.
+
+
+
+ One 'jiffy' is approximately 61 microseconds about a tenth of typical IR
+ mark and space times. LIRC configuration program irrecord
+ cannot reliably detect the IR data timing when measured in 'jiffies' it
+ works better with microseconds.
+
+
+ I have submitted a patch that modifies the LIRC udp driver to support
+ timing data in microseconds but it has been ignored. The LIRC team
+ currently seems only to be interested in adding IR support to the kernel.
+ You can obtain my modified files from
+ GitHub.
+ The standard UDP driver is satisfactory for most uses but I recommend
+ using the modified driver if you intend to use irrecord
+
+
+
+
+
LircFlushThreshold =
@@ -352,11 +390,15 @@
- 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
- 100 jiffies (6.1ms). It's should only be needed to change this value
- when lircd.conf has a gap < 6100 or the samples > 6300.
+ This value is the length in microseconds of the gap that will trigger
+ sending the queued IR data to LIRC. Values greater than 1000 (1ms) are
+ permitted, lower values will set the default value 8000 (8ms). The
+ maximum depends on the setting of LircTime_us; if LircTime_us is on
+ values greater than 32.767ms will disable the flush, if LircTime_us is
+ off values greater than 1.999938s will disable the flush. The value
+ should be less than the gap times specified in lircd.conf
+ and greater than any space time specified in any header, one, zero, etc.
+ field.
@@ -397,12 +439,27 @@
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.
+ The LIRC configuration program irrecord cannot reliably
+ detect the IR data timing when measured in 'jiffies' it works better
+ with microseconds, see LircTime_us above.
-
+
+
+ The current libusb-1.0 implementation polls at 32Hz to see if any USB
+ processing is required, this is needed when a key has been pressed or
+ some IR data has been received. 32Hz has a period of 31.25ms this is not
+ really fast enough the picoLCD USB transfers can occur every 10ms. This
+ may cause buffer overrun problems for long bursts of IR data. The best
+ solution would be to include USB processing in the main loop select
+ statement this has not been done to avoid major changes to the core
+ code, to work-round this extra USB transfer buffers are allocated. It
+ may also be worth building with PROCESS_FREQ set to 100Hz
+ (in server/main.h).
+
+
-
Copyright
diff --git a/server/drivers/picolcd.c b/server/drivers/picolcd.c
index 5d9d08e..f41229b 100644
--- a/server/drivers/picolcd.c
+++ b/server/drivers/picolcd.c
@@ -48,15 +48,22 @@
#define NUM_CCs 8 /* max. number of custom characters */
#define KEY_BUFFER_SIZE 8 /* size of the key ring buffer */
+#define ANSI_ESCAPES /* Use color to make (IR) debug easier to read */
+#ifdef ANSI_ESCAPES
+#define TEXT_NORMAL "\033[0m"
+#define TEXT_RED "\033[31m"
+#else
+#define TEXT_NORMAL ""
+#define TEXT_RED ""
+#endif
+
#ifdef HAVE_LIBUSB_1_0
/**
- * This structure holds the keys reported in a key event. high_key is set if
- * a single key is pressed or it holds the first key if two keys are pressed.
- * low_key holds the second key pressed (if any).
+ * This structure holds the keys reported in a key event.
*/
typedef struct {
- unsigned char high_key;
- unsigned char low_key;
+ unsigned char high_key; /**< Set if a single key is pressed or it holds the first key if two keys are pressed.*/
+ unsigned char low_key; /**< Holds the second key pressed (if any). */
} keys;
#endif
@@ -75,6 +82,39 @@ typedef struct {
*/
#undef USE_LIBUSB_SINGLE_SELECT
+/**
+ * Multiple buffers are needed to ensure that no USB transfer is missed. Ideally
+ * a single select statement with appropriate timeout should be used, in this
+ * case double buffering is sufficient. When distributed select statements are
+ * used processing of the USB signals is performed by
+ * libusb_handle_events_timeout() in picoLCD_get_key(). This is called at 32Hz
+ * (PROCESS_FREQ) i.e. every 31.25ms which is not really fast enough as the
+ * picoLCD USB transfers can occur every 10ms. This may cause buffer overrun
+ * problems for long bursts of IR data, to avoid problems there must be more
+ * than 3 buffers.
+ */
+#ifdef USE_LIBUSB_SINGLE_SELECT
+#define USB_BUFFERS 2
+#else
+#define USB_BUFFERS 4
+#endif
+
+#ifdef HAVE_LIBUSB_1_0
+/**
+ * This structure holds the data for a USB transfer.
+ */
+typedef struct usb_transfer_data {
+ /** structure for the details of the asynchronous USB transfer */
+ struct libusb_transfer *transfer;
+ /** transfer status */
+ int status;
+ /** Pointer to driver private data */
+ Driver *drvthis;
+ /** data buffer for the USB transfer */
+ unsigned char buffer[PICOLCD_MAX_DATA_LEN];
+}UsbTransferData;
+#endif
+
/** Private data for the picoLCD driver */
typedef struct picolcd_private_data {
USB_DEVICE_HANDLE *lcd;
@@ -104,15 +144,13 @@ typedef struct picolcd_private_data {
unsigned char *resptr;
struct timeval lastmsg;
int lastval;
+ int lirc_time_us;
int flush_threshold;
#ifdef HAVE_LIBUSB_1_0
/* Pointer to libusb 1.0 session */
libusb_context *lib_ctx;
- /* data buffer for the asynchronous USB transfer */
- unsigned char input_buffer[PICOLCD_MAX_DATA_LEN];
- /* structure for the details of the asynchronous USB transfer */
- struct libusb_transfer *input_transfer;
- int libusb_status;
+ /* structure for the details of the USB transfer */
+ UsbTransferData input_transfer[USB_BUFFERS];
/* buffer for the key press data */
keys key_buffer[KEY_BUFFER_SIZE];
int key_read_index; /* Read index in the key_buffer */
@@ -136,6 +174,7 @@ static void set_key_lights(USB_DEVICE_HANDLE *lcd, int keys[], int state);
static void picolcd_lircsend(Driver *drvthis);
static void ir_transcode(Driver *drvthis, unsigned char *data, unsigned int cbdata);
#ifdef HAVE_LIBUSB_1_0
+static void free_usb_transfers(Driver *drvthis);
static void key_buffer_put(Driver *drvthis, unsigned char high_key, unsigned char low_key);
static void usb_cb_input(struct libusb_transfer *transfer);
#else
@@ -231,7 +270,7 @@ picoLCD_init(Driver *drvthis)
#ifndef USE_LIBUSB_SINGLE_SELECT
/*
* When using a single select statement libusb-1.0 should be
- * initialised once for all usb drivers so code equivalent to that
+ * initialised once for all USB drivers so code equivalent to that
* below should be somewhere common, before init_drivers() perhaps.
*/
error = libusb_init(&p->lib_ctx);
@@ -242,8 +281,6 @@ picoLCD_init(Driver *drvthis)
libusb_set_debug(p->lib_ctx, 3);
#endif
- p->libusb_status = LIBUSB_SUCCESS;
- p->input_transfer = NULL;
p->key_read_index = 0;
p->key_write_index = 0;
@@ -270,7 +307,7 @@ picoLCD_init(Driver *drvthis)
return -1;
}
- if (libusb_kernel_driver_active(p->lcd, 0)) {
+ if (libusb_kernel_driver_active(p->lcd, 0) == 1) {
debug(RPT_DEBUG, "%s: libusb_kernel_driver_active returned true", drvthis->name);
error = libusb_detach_kernel_driver(p->lcd, 0);
if (error) {
@@ -281,6 +318,7 @@ picoLCD_init(Driver *drvthis)
else {
debug(RPT_DEBUG, "%s: libusb_kernel_driver_active returned false", drvthis->name);
}
+
error = libusb_claim_interface(p->lcd, 0);
if (error) {
report(RPT_ERR, "%s: libusb_claim_interface error %d", drvthis->name, error);
@@ -298,19 +336,36 @@ picoLCD_init(Driver *drvthis)
if (error) {
report(RPT_WARNING, "%s: libusb_set_interface_alt_setting error %d", drvthis->name, error);
}
- p->input_transfer = libusb_alloc_transfer(0);
- if (p->input_transfer == NULL) {
- report(RPT_ERR, "%s: libusb_alloc_transfer failed", drvthis->name);
- return -1;
- }
- libusb_fill_interrupt_transfer(p->input_transfer, p->lcd, LIBUSB_ENDPOINT_IN + 1, p->input_buffer,
- sizeof(p->input_buffer), usb_cb_input, (void *)drvthis, 0);
- error = libusb_submit_transfer(p->input_transfer);
- if (error) {
- report(RPT_ERR, "%s: libusb_submit_transfer error %d", drvthis->name, error);
- libusb_free_transfer(p->input_transfer);
- p->input_transfer = NULL;
- return -1;
+
+ /* Set-up USB input transfer data structures */
+ for (i = 0; i < USB_BUFFERS; i++)
+ p->input_transfer[i].transfer = NULL;
+ for (i = 0; i < USB_BUFFERS; i++)
+ {
+ UsbTransferData *utdp = &p->input_transfer[i];
+
+ utdp->drvthis = drvthis;
+ utdp->transfer = libusb_alloc_transfer(0);
+ if (utdp->transfer == NULL) {
+ report(RPT_ERR, "%s: libusb_alloc_transfer failed", drvthis->name);
+ free_usb_transfers(drvthis);
+ return -1;
+ }
+ libusb_fill_interrupt_transfer(utdp->transfer,
+ p->lcd,
+ LIBUSB_ENDPOINT_IN + 1,
+ utdp->buffer,
+ sizeof(utdp->buffer),
+ usb_cb_input,
+ (void *)utdp,
+ 0);
+ utdp->status = libusb_submit_transfer(utdp->transfer);
+ if (utdp->status) {
+ report(RPT_ERR, "%s: libusb_submit_transfer error %d",
+ drvthis->name, utdp->status);
+ free_usb_transfers(drvthis);
+ return -1;
+ }
}
#else /* The libusb 0.1 way */
@@ -500,20 +555,42 @@ done:
/* LIRC is only enabled if a hostname is set */
p->IRenabled = (lirchost != NULL && *lirchost != '\0') ? 1 : 0;
- tmp = drvthis->config_get_int(drvthis->name, "LircFlushThreshold", 0, DEFAULT_FLUSH_THRESHOLD_JIFFY);
- /* Prevent small 'foolish' values these will disable the check also! */
- if (p->flush_threshold < 16) {
- report(RPT_WARNING, "%s: flush threshold to small - disabled");
- tmp = 0x8000; /* Disabled, send check will always fail! */
+ p->lirc_time_us = drvthis->config_get_bool(drvthis->name, "LircTime_us", 0, DEFAULT_LIRC_TIME_us);
+
+ tmp = drvthis->config_get_int(drvthis->name, "LircFlushThreshold", 0, DEFAULT_FLUSH_THRESHOLD);
+ /*
+ * Enforce a sensible minimum. Only want to flush on gaps between IR
+ * bursts not the spaces between marks.
+ */
+ if (tmp < 1000) {
+ report(RPT_WARNING, "%s: flush threshold to small (%d) , using default", drvthis->name, tmp);
+ tmp = DEFAULT_FLUSH_THRESHOLD;
}
- else if (p->flush_threshold > 0x7FFF) {
- report(RPT_WARNING, "%s: flush threshold to large, using default");
- tmp = DEFAULT_FLUSH_THRESHOLD_JIFFY;
+ if (p->lirc_time_us) {
+ /*
+ * Values greater than 32.767ms will disable the flush.
+ */
+ if (tmp > 32727) {
+ report(RPT_WARNING, "%s: flush threshold to large (%d), disabled", drvthis->name, tmp);
+ }
+ }
+ else {
+ /*
+ * Scale between microseconds and jiffies (1/16384s)
+ * Values greater than 1999.938ms will disable the flush.
+ */
+ if (0x7FFF * 15625 / 256 < tmp) {
+ report(RPT_WARNING, "%s: flush threshold to large (%d), disabled", drvthis->name, tmp);
+ tmp = 0x8000;
+ }
+ else {
+ tmp = tmp * 256 / 15625;
+ }
}
p->flush_threshold = tmp;
/*
- * Simulate that the last value send was a PULSE, so we start with
+ * Simulate that the last value send was a MARK, so we start with
* sending a SPACE to make LIRC happy
*/
p->lastval = 0;
@@ -545,8 +622,8 @@ done:
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 flush threshold=%d",
- drvthis->name, lirchost, lircport, p->flush_threshold);
+ report(RPT_INFO, "%s: IR events will be sent to LIRC on %s:%d, with flush threshold=%d, time unit: %s",
+ drvthis->name, lirchost, lircport, p->flush_threshold, p->lirc_time_us ? "us" : "1/16384s");
}
report(RPT_INFO, "%s: init complete", drvthis->name);
@@ -567,22 +644,7 @@ picoLCD_close(Driver *drvthis)
#ifdef HAVE_LIBUSB_1_0
int error;
- if (p->input_transfer != NULL) {
- /* Need to cancel transfer before it is freed */
- libusb_cancel_transfer(p->input_transfer);
- while (p->libusb_status != LIBUSB_TRANSFER_CANCELLED) {
- struct timeval timeout;
- /*
- * Wait for the cancellation to complete, the
- * call-back will then have freed the
- * transfer.
- */
- report(RPT_INFO, "%s: waiting for usb transfer to be cancelled", drvthis->name);
- timeout.tv_sec = 1;
- timeout.tv_usec = 0;
- libusb_handle_events_timeout(p->lib_ctx, &timeout);
- }
- }
+ free_usb_transfers(drvthis);
error = libusb_release_interface(p->lcd, 0);
if (error) {
@@ -670,7 +732,7 @@ picoLCD_clear(Driver *drvthis)
/**
- * Flush data on screen to the display.
+ * Flush data in screen buffer to the display.
* \param drvthis Pointer to driver structure.
*/
MODULE_EXPORT void
@@ -716,6 +778,20 @@ picoLCD_flush(Driver *drvthis)
* \param x Horizontal character position (column).
* \param y Vertical character position (row).
* \param string String that gets written.
+ *
+ * The picoLCD uses a HD44780UA00 which provides ASCII and Japanese fonts.
+ *
+ * Characters 0x20-0x7F are ASCII with the following substitutions:
+ * | Index | ASCII | Substitute |
+ * | :---: | :---: | :--------------- |
+ * | 0x5C | \ | Yen |
+ * | 0x7E | ~ | Rightwards Arrow |
+ * | 0x7F | DEL | Leftwards Arrow |
+ *
+ * Characters 0xA0-0xDF are half-width Katakana. They map to
+ * the Unicode block starting at U+FF60 and ending at U+FF9F.
+ *
+ * Characters 0xE0-0xFF are various Greek letters and symbols.
*/
MODULE_EXPORT void
picoLCD_string(Driver *drvthis, int x, int y, unsigned char string[])
@@ -1215,8 +1291,9 @@ picoLCD_get_key(Driver *drvthis)
struct timeval timeout;
/*
* FIXME: It is not efficient to call this at 32Hz, it is only needed
- * if a key has been pressed or IR data has been received. Process
- * any outstanding USB events for our session.
+ * if a key has been pressed or IR data has been received.
+ *
+ * Process any outstanding USB events for our session.
*/
timeout.tv_sec = 0;
timeout.tv_usec = 0;
@@ -1547,7 +1624,7 @@ picoLCD_get_info(Driver *drvthis)
* 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.
+ * for mark. 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.
@@ -1557,15 +1634,18 @@ picoLCD_get_info(Driver *drvthis)
* \param cbdata Length of data to be transcoded.
*
* \note The picoLCD introduces two issues:
+ *
* \note 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.
+ *
* \note 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
+ * look for a mark at the end of the last message and a mark 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 'calculated' sync is put at the
* begin of the next message), this is because LIRC requires a space at
@@ -1582,14 +1662,33 @@ ir_transcode(Driver *drvthis, unsigned char *data, unsigned int cbdata)
/* Check for odd buffer length (invalid buffer) */
if (cbdata & 1) {
+ report(RPT_WARNING, "picolcd: buffer invalid length (%d)", cbdata);
return;
}
/* Get time needed to calculate the time between 2 IR data messages */
gettimeofday(&now, 0);
+#ifdef DEBUG
+ debug(RPT_DEBUG, "picolcd: received %d IR samples", cIntervals);
+ {
+ unsigned char *ptr = data;
+ int c = cIntervals;
+ char logbuf[cbdata * 5];
+ char *logptr = logbuf;
+ while (c--) {
+ unsigned int val = *ptr++;
+ val |= *ptr++ << 8;
+ logptr += sprintf(logptr, " %s%04x",
+ (0x7fff < val) ? TEXT_RED : TEXT_NORMAL,
+ (0x7fff < val) ? 0x10000 - val : val);
+ }
+ debug(RPT_DEBUG, "picolcd: data:%s" TEXT_NORMAL, logbuf);
+ }
+#endif
+
/* Check for a missing SPACE since the last message */
- debug(RPT_INFO, "picolcd: last %04x first %04x", p->lastval, (-w & 0xFFFF));
+ debug(RPT_DEBUG, "picolcd: last %04x 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
@@ -1600,53 +1699,105 @@ ir_transcode(Driver *drvthis, unsigned char *data, unsigned int cbdata)
timersub(&now, &p->lastmsg, &time_gap);
- /* 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);
+ if (p->resptr != p->result) {
+ /* previous message is complete send it without the added space */
+ debug(RPT_INFO, "picolcd: missing space detected, flushing queue before adding sync");
+ picolcd_lircsend(drvthis);
+ }
+ else {
+ debug(RPT_INFO, "picolcd: missing space detected, adding timed space to buffer");
+ }
- /*
- * Prevent the overflow (2 secs = 32678 jiffies), but allow
- * 2.99 seconds to reach the max
- */
- if (2 <= time_gap.tv_sec) {
+ if (p->lirc_time_us) {
/*
- * microseconds to jiffies (same as (16384/1000000)
- * but no possible int32 overflow)
+ * When sending times in microseconds a single word gives a range up
+ * to 32767us. I am not aware of anything that uses IR pulse or space
+ * times as long as 32ms so the pulse and space times can just be
+ * copied from the buffer see below. The gap between transmissions can
+ * be very long so may need special encoding. LIRC processes times in
+ * microseconds using 24 bits so send a zero word as a flag followed
+ * by a three byte time sent as four bytes to keep the buffer length
+ * even. 24 bits gives a range up to 16777215 so we limit to 16s for
+ * simplicity.
*/
- gap = ((time_gap.tv_sec * 1000000 + time_gap.tv_usec) * 256) / 15625;
+ if (time_gap.tv_sec >= 16) {
+ debug(RPT_INFO, "picolcd: IR transmission gap: 8000 00F42400");
+ *p->resptr++ = 0x00; /* zero */
+ *p->resptr++ = 0x80; /* with space bit */
+ *p->resptr++ = 0x00; /* 16s as 24 bit value */
+ *p->resptr++ = 0x24;
+ *p->resptr++ = 0xf4;
+ *p->resptr++ = 0x00;
+ }
+ else {
+ gap = (time_gap.tv_sec * 1000000 + time_gap.tv_usec);
+ if (gap > 0x7FFF) {
+ /* Send as 24 bits */
+ debug(RPT_INFO, "picolcd: IR transmission gap: 8000 %08x", gap);
+ *p->resptr++ = 0x00; /* zero */
+ *p->resptr++ = 0x80; /* with space bit */
+ *p->resptr++ = (unsigned char)(gap & 0xff);
+ *p->resptr++ = (unsigned char)((gap >> 8) & 0xff);
+ *p->resptr++ = (unsigned char)((gap >> 16) & 0xff);
+ *p->resptr++ = 0x00;
+ }
+ else {
+ /* Send a 16 bit space */
+ gap |= 0x8000;
+ debug(RPT_INFO, "picolcd: IR transmission gap: %04x", gap);
+ *p->resptr++ = (unsigned char)(gap & 0xff);
+ *p->resptr++ = (unsigned char)((gap >> 8) & 0xff);
+ }
+ }
}
+ else { /* Intervals measured in jiffies (1/16384 s). */
+ /*
+ * Prevent the overflow (2 secs = 32678 jiffies), but allow
+ * 2.99 seconds to reach the max
+ */
+ if (time_gap.tv_sec >= 2) {
+ /*
+ * microseconds to jiffies (same as (16384/1000000)
+ * but no possible int32 overflow)
+ */
+ gap = ((time_gap.tv_sec * 1000000 + time_gap.tv_usec) * 256) / 15625;
+ }
+ /* Saturate on 15 bit overflow */
+ if (gap >= 0x8000) {
+ gap = 0x7FFF;
+ }
+ /* Make it a space */
+ gap |= 0x8000;
- /* Saturate on 15 bit overflow */
- if (gap >= 0x8000) {
- gap = 0x7FFF;
+ debug(RPT_INFO, "picolcd: injecting space %04x between %04x and %04x",
+ gap, p->lastval, -w & 0xFFFF);
+ *p->resptr++ = (unsigned char)(gap & 0xff);
+ *p->resptr++ = (unsigned char)((gap >> 8) & 0xff);
}
- /* Make it a space */
- gap |= 0x8000;
-
- debug(RPT_INFO, "picolcd: injecting space %04x between %04x and %04x",
- gap, p->lastval, -w & 0xFFFF);
- *p->resptr++ = (unsigned char)(gap & 0xff);
- *p->resptr++ = (unsigned char)((gap >> 8) & 0xff);
}
/* Check if there is enough 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");
+ report(RPT_WARNING, "picolcd: buffer almost full send lirc data now");
picolcd_lircsend(drvthis);
}
for (i = 0; i < cIntervals; i++) {
w = *data++;
w |= *data++ << 8;
- if (w & 0x8000) {
- /* IF w is negative THEN negate. E.g. 0xDCA1 (-9055) -> 9055. */
+ if (w & 0x8000) { /* Mark */
+ /* picoLCD uses negative for mark so 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;
+ if (!p->lirc_time_us) {
+ /* scale: orig is usec, new is jiffy. E.g. 9055usec = 148 jiffy. */
+ w = (w * 16384 / 1000000) & 0xFFFF;
+ }
}
- else {
- /* Scale */
- w = w * 16384 / 1000000;
+ else { /* Space */
+ if (!p->lirc_time_us) {
+ /* Scale */
+ w = w * 16384 / 1000000;
+ }
if (w >= p->flush_threshold) {
report(RPT_INFO, "picolcd: detected sync space sending lirc data now");
picolcd_lircsend(drvthis);
@@ -1661,7 +1812,7 @@ ir_transcode(Driver *drvthis, unsigned char *data, unsigned int cbdata)
p->lastmsg = now;
/*
* Look for a short buffer (a full buffer has 10 samples) with a
- * terminal PULSE
+ * terminal mark
*/
if ((cIntervals < 10) && ((w & 0x8000) == 0)) {
debug(RPT_INFO, "picolcd: IR data end detected sending lirc data now");
@@ -1833,7 +1984,7 @@ picolcd_20x2_set_char(Driver *drvthis, int n, unsigned char *dat)
if ((n < 0) || (n >= NUM_CCs))
return;
- if (!dat)
+ if (dat == NULL)
return;
packet[1] = n; /* Custom char to define. */
@@ -1859,7 +2010,7 @@ picolcd_20x4_set_char(Driver *drvthis, int n, unsigned char *dat)
if ((n < 0) || (n >= NUM_CCs))
return;
- if (!dat)
+ if (dat == NULL)
return;
unsigned char command[6] = {
@@ -1942,6 +2093,43 @@ set_key_lights(USB_DEVICE_HANDLE * lcd, int keys[], int state)
#ifdef HAVE_LIBUSB_1_0
+/**
+ * Free the USB transfer data.
+ *
+ * \param drvthis Pointer to driver structure
+ */
+static void
+free_usb_transfers(Driver *drvthis)
+{
+ PrivateData *p = drvthis->private_data;
+ int i;
+
+ for (i = 0; i < USB_BUFFERS; i++) {
+ if (p->input_transfer[i].transfer != NULL) {
+ if (p->input_transfer[i].status == LIBUSB_SUCCESS) {
+ /* Need to cancel transfer before it is freed */
+ libusb_cancel_transfer(p->input_transfer[i].transfer);
+ while (p->input_transfer[i].status != LIBUSB_TRANSFER_CANCELLED) {
+ struct timeval timeout;
+ /*
+ * Wait for the cancellation to complete, the
+ * call-back will then have freed the
+ * transfer.
+ */
+ report(RPT_INFO, "%s: waiting for usb transfer %d to be cancelled", drvthis->name, i);
+ timeout.tv_sec = 1;
+ timeout.tv_usec = 0;
+ libusb_handle_events_timeout(p->lib_ctx, &timeout);
+ }
+ }
+ else {
+ libusb_free_transfer(p->input_transfer[i].transfer);
+ p->input_transfer[i].transfer = NULL;
+ }
+ }
+ }
+}
+
/**
* Store key press and release events in a buffer ready for the get key function.
* If the buffer is full key codes are discarded.
@@ -1985,14 +2173,15 @@ usb_cb_input(struct libusb_transfer *transfer)
"COMPLETED", "ERROR", "TIMED_OUT", "CANCELLED", "STALL",
"NO_DEVICE", "OVERFLOW"
};
- Driver *drvthis = (Driver *)transfer->user_data;
- PrivateData *p = drvthis->private_data;
+ UsbTransferData *p = (UsbTransferData *)transfer->user_data;
+ Driver *drvthis = p->drvthis;
+ PrivateData *p_data = drvthis->private_data;
if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
report(RPT_ERR, "%s: input transfer status: %s", drvthis->name, status[transfer->status]);
- p->libusb_status = transfer->status;
+ p->status = transfer->status;
libusb_free_transfer(transfer);
- p->input_transfer = NULL;
+ p->transfer = NULL;
return;
}
@@ -2003,17 +2192,18 @@ usb_cb_input(struct libusb_transfer *transfer)
break;
case IN_REPORT_IR_DATA:
debug(RPT_INFO, "%s: USB input call-back IR length %i", drvthis->name, transfer->buffer[1]);
- if (p->IRenabled)
+ if (p_data->IRenabled)
ir_transcode(drvthis, &transfer->buffer[2], transfer->buffer[1]);
break;
default:
report(RPT_ERR, "%s: input transfer unexpected data %d", drvthis->name, transfer->buffer[0]);
+ break;
}
/* Re-transmit the input request transfer */
- p->libusb_status = libusb_submit_transfer(p->input_transfer);
- if (p->libusb_status != LIBUSB_SUCCESS)
- report(RPT_ERR, "%s: input transfer submit status %d", drvthis->name, p->libusb_status);
+ p->status = libusb_submit_transfer(transfer);
+ if (p->status != LIBUSB_SUCCESS)
+ report(RPT_ERR, "%s: input transfer submit status %d", drvthis->name, p->status);
}
#endif
diff --git a/server/drivers/picolcd.h b/server/drivers/picolcd.h
index 4b969f5..2d7293e 100644
--- a/server/drivers/picolcd.h
+++ b/server/drivers/picolcd.h
@@ -36,7 +36,8 @@
#define PICOLCD_MAX_DATA_LEN 24
#define DEFAULT_LIRCPORT 8765
-#define DEFAULT_FLUSH_THRESHOLD_JIFFY 100 /* 6.1 millisec */
+#define DEFAULT_LIRC_TIME_us 0 /* false */
+#define DEFAULT_FLUSH_THRESHOLD 8000 /* microseconds */
#define DEFAULT_CONTRAST 1000 /* Full */
#define DEFAULT_BRIGHTNESS 1000 /* Full */
#define DEFAULT_OFFBRIGHTNESS 0 /* Off */