Improvements to picoLCD driver IR processing (by M. T. Jones).
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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 <literal>Escape</literal>, <literal>F1</literal>,
|
||||
keypad with 8 keys labeled <literal>Escape</literal>, <literal>F1</literal>,
|
||||
<literal>F2</literal>, <literal>F3</literal>, <literal>Home</literal>,
|
||||
<literal>Up</literal>, <literal>Down</literal> and <literal>Enter</literal>.
|
||||
</para>
|
||||
@@ -323,7 +323,7 @@
|
||||
</para>
|
||||
<para>
|
||||
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 @@
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term>
|
||||
<property>LircTime_us</property> = ¶meters.yesnodef;
|
||||
</term>
|
||||
<listitem>
|
||||
<para>
|
||||
If <property>LircTime_us</property> is set to on mark and space times
|
||||
are sent to LIRC in microseconds (requires a LIRC UDP driver that
|
||||
accepts this).
|
||||
</para>
|
||||
<para>
|
||||
If <property>LircTime_us</property> is set to off mark and space times
|
||||
are sent to LIRC in 'jiffies' (1/16384s) (supported by the standard LIRC
|
||||
UDP driver).
|
||||
</para>
|
||||
<para>
|
||||
Default is <literal>off</literal>.
|
||||
</para>
|
||||
<note>
|
||||
<para>
|
||||
One 'jiffy' is approximately 61 microseconds about a tenth of typical IR
|
||||
mark and space times. LIRC configuration program <code>irrecord</code>
|
||||
cannot reliably detect the IR data timing when measured in 'jiffies' it
|
||||
works better with microseconds.
|
||||
</para>
|
||||
<para>
|
||||
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
|
||||
<ulink url="https://github.com/emteejay/Patched-LIRC.git">GitHub</ulink>.
|
||||
The standard UDP driver is satisfactory for most uses but I recommend
|
||||
using the modified driver if you intend to use <code>irrecord</code>
|
||||
</para>
|
||||
</note>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term>
|
||||
<property>LircFlushThreshold</property> =
|
||||
@@ -352,11 +390,15 @@
|
||||
</term>
|
||||
<listitem>
|
||||
<para>
|
||||
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 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 <code>lircd.conf</code>
|
||||
and greater than any space time specified in any header, one, zero, etc.
|
||||
field.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
@@ -397,12 +439,27 @@
|
||||
working, this driver by default adds the gap as well as the sync. However I have
|
||||
<emphasis>still</emphasis> had trouble getting <code>irrecord</code> to work; you need at least
|
||||
to feed it a template configuration containing sync and gap data.
|
||||
The LIRC configuration program <code>irrecord</code> cannot reliably
|
||||
detect the IR data timing when measured in 'jiffies' it works better
|
||||
with microseconds, see <code>LircTime_us</code> above.
|
||||
</para>
|
||||
|
||||
<note>
|
||||
<para>
|
||||
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 <code>select</code>
|
||||
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 <code>PROCESS_FREQ</code> set to 100Hz
|
||||
(in <code>server/main.h</code>).
|
||||
</para>
|
||||
</note>
|
||||
</sect3>
|
||||
</sect2>
|
||||
|
||||
|
||||
<sect2 id="picolcd-copy">
|
||||
<title>Copyright</title>
|
||||
|
||||
|
||||
+290
-100
@@ -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
|
||||
|
||||
|
||||
@@ -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 */
|
||||
|
||||
Reference in New Issue
Block a user