Improvements to picoLCD driver Infra-Red processing

Martin Tharby Jones <[email protected]> Fri, 13 Sep 2013 20:35:56 +0100
Newsgroups gmane.comp.sysutils.lcdproc
Message-ID <[email protected]>
This is a multi-part message in MIME format.
--------------080202020907090900050006
Content-Type: multipart/alternative;
 boundary="------------040805050407000501050005"


--------------040805050407000501050005
Content-Type: text/plain; charset=ISO-8859-1; format=flowed
Content-Transfer-Encoding: 7bit

Hello,

I have attached a patch with improvements to the picoLCD IR code.
There are two areas that have been changed:

 1. Use multiple USB transfer buffers so that data cannot be lost if the
    system is busy.
 2. Allow the IR timing data to be sent in microseconds or jiffies
    (1/16384s). Using the smaller timing resolution improves the ability
    of irrecord to determine the timing of the IR signal.

Martin


--------------040805050407000501050005
Content-Type: text/html; charset=ISO-8859-1
Content-Transfer-Encoding: 7bit

<html>
  <head>

    <meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">
  </head>
  <body bgcolor="#FFFFFF" text="#000000">
    Hello,<br>
    <br>
    I have attached a patch with improvements to the picoLCD IR code.<br>
    There are two areas that have been changed:<br>
    <ol>
      <li>Use multiple USB transfer buffers so that data cannot be lost
        if the system is busy.</li>
      <li>Allow the IR timing data to be sent in microseconds or jiffies
        (1/16384s). Using the smaller timing resolution improves the
        ability of irrecord to determine the timing of the IR signal.</li>
    </ol>
    <p>Martin<br>
      <br>
    </p>
  </body>
</html>

--------------040805050407000501050005--

--------------080202020907090900050006
Content-Type: text/x-patch;
 name="picoLCD_LIRC.patch"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename="picoLCD_LIRC.patch"

Index: LCDd.conf
===================================================================
RCS file: /cvsroot/lcdproc/lcdproc/LCDd.conf,v
retrieving revision 1.169
diff -u -r1.169 LCDd.conf
--- LCDd.conf	8 May 2013 19:19:55 -0000	1.169
+++ LCDd.conf	13 Sep 2013 19:13:59 -0000
@@ -35,6 +35,7 @@
 #            function properly.
 # NOTE: Always place a slash as last character !
 DriverPath=server/drivers/
+# DriverPath=/usr/lib/lcdproc/
 
 # Tells the server to load the given drivers. Multiple lines can be given.
 # The name of the driver is case sensitive and determines the section
@@ -50,10 +51,12 @@
 #   mdm166a, ms6931, mtc_s16209x, MtxOrb, mx5000, NoritakeVFD, picolcd,
 #   pyramid, sdeclcd, sed1330, sed1520, serialPOS, serialVFD, shuttleVFD, sli,
 #   stv5730, svga, t6963, text, tyan, ula200, vlsys_m428, xosd
-Driver=curses
+#Driver=curses
+Driver=picolcd
+Driver=lirc
 
 # Tells the driver to bind to the given interface. [default: 127.0.0.1]
-Bind=127.0.0.1
+Bind=LiFi.local
 
 # Listen on this specified port. [default: 13666]
 Port=13666
@@ -116,8 +119,8 @@
 ToggleRotateKey=Enter
 PrevScreenKey=Left
 NextScreenKey=Right
-#ScrollUpKey=Up
-#ScrollDownKey=Down
+ScrollUpKey=Up
+ScrollDownKey=Down
 
 
 ## The menu section. The menu is an internal LCDproc client. ##
@@ -128,12 +131,13 @@
 # Up to six keys are supported. The MenuKey (to enter and exit the menu), the
 # EnterKey (to select values) and at least one movement keys are required.
 # These are the default key assignments:
-MenuKey=Escape
+#MenuKey=Escape
+MenuKey=Minus+Plus
 EnterKey=Enter
 UpKey=Up
 DownKey=Down
-#LeftKey=Left
-#RightKey=Right
+LeftKey=Left
+RightKey=Right
 
 
 ### Driver sections are below this line, in alphabetical order  ###
@@ -802,10 +806,10 @@
 [lirc]
 
 # Specify an alternative location of the lircrc file [default: ~/.lircrc]
-#lircrc=/etc/lircrc.lcdproc
+lircrc=/etc/lirc/lircrc
 
 # Must be the same as in your lircrc
-#prog=lcdd
+prog=lcdd
 
 
 
@@ -998,7 +1002,7 @@
 
 # Sets the initial state of the backlight upon start-up.
 # [default: on; legal: on, off]
-#Backlight=on
+Backlight=on
 
 # Set the initial brightness [default: 1000; legal: 0 - 1000]. Works only
 # with the 20x4 device
@@ -1012,7 +1016,7 @@
 Contrast=1000
 
 # Link the key lights to the backlight? [default: on; legal: on, off]
-#LinkLights=off
+LinkLights=off
 
 # Light the keys? [default: on; legal: on, off]
 Keylights=on
@@ -1030,15 +1034,21 @@
 
 # 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.
-#LircHost=127.0.0.1
+LircHost=127.0.0.1
 
 # 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
 
 
 
Index: docs/lcdproc-user/drivers/picolcd.docbook
===================================================================
RCS file: /cvsroot/lcdproc/lcdproc/docs/lcdproc-user/drivers/picolcd.docbook,v
retrieving revision 1.14
diff -u -r1.14 picolcd.docbook
--- docs/lcdproc-user/drivers/picolcd.docbook	10 Dec 2011 15:39:47 -0000	1.14
+++ docs/lcdproc-user/drivers/picolcd.docbook	13 Sep 2013 19:13:59 -0000
@@ -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
@@ -347,16 +347,58 @@
 
 <varlistentry>
   <term>
+    <property>LircTime_us</property> = &parameters.yesdefno;
+  </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 approximatly 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 intested in adding IR support to the kernal.
+	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> =
     <parameter><replaceable>DURATION</replaceable></parameter>
   </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 &lt; 6100 or the samples &gt; 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>
 
Index: server/drivers/picolcd.c
===================================================================
RCS file: /cvsroot/lcdproc/lcdproc/server/drivers/picolcd.c,v
retrieving revision 1.25
diff -u -r1.25 picolcd.c
--- server/drivers/picolcd.c	24 Mar 2013 13:59:22 -0000	1.25
+++ server/drivers/picolcd.c	13 Sep 2013 19:14:00 -0000
@@ -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 colour to make (IR) debug easer 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,37 @@
  */
 #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 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 +142,13 @@
 	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 +172,7 @@
 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
@@ -215,6 +252,7 @@
 	const char *lirchost;
 	int lircport;
 	int id;
+	int i;
 	int tmp;
 
 	p = (PrivateData *) malloc(sizeof(PrivateData));
@@ -242,8 +280,6 @@
 	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;
 
@@ -298,19 +334,36 @@
 	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 +553,41 @@
 	/* 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! */
-	}
-	else if (p->flush_threshold > 0x7FFF) {
-		report(RPT_WARNING, "%s: flush threshold to large, using default");
-		tmp = DEFAULT_FLUSH_THRESHOLD_JIFFY;
+	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;
+	}
+	if (p->lirc_time_us) {
+		/*
+		 * Values greater than 32.767ms will disable the flush.
+		 */
+		if (32727 < tmp) {
+			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 +619,8 @@
 		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 +641,7 @@
 #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 +729,7 @@
 
 
 /**
- *  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 +775,23 @@
  * \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.
+ *
+ * (The HD44780UA02 provides a European font: ASCII, accented characters
+ * and some useful icons.)
  */
 MODULE_EXPORT void
 picoLCD_string(Driver *drvthis, int x, int y, unsigned char string[])
@@ -1215,8 +1291,9 @@
 	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 @@
  * 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 @@
  * \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 @@
 
 	/* 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 @@
 
 		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 (16 <= time_gap.tv_sec) {
+				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 (0x7FFF < gap) {
+					/* 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 (2 <= time_gap.tv_sec) {
+				/*
+				 * 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 @@
 	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");
@@ -1943,6 +2094,45 @@
 
 #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 +2175,15 @@
 		"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 +2194,18 @@
 		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
 
Index: server/drivers/picolcd.h
===================================================================
RCS file: /cvsroot/lcdproc/lcdproc/server/drivers/picolcd.h,v
retrieving revision 1.14
diff -u -r1.14 picolcd.h
--- server/drivers/picolcd.h	10 Dec 2011 15:39:47 -0000	1.14
+++ server/drivers/picolcd.h	13 Sep 2013 19:14:00 -0000
@@ -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 */

--------------080202020907090900050006
Content-Type: text/plain; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Content-Disposition: inline

_______________________________________________
LCDproc mailing list
[email protected]
http://lists.omnipotent.net/mailman/listinfo/lcdproc

--------------080202020907090900050006--