From 5c4b3239fc7d0eb344f5a7e99c4ce10d9ebae98f Mon Sep 17 00:00:00 2001 From: mmdolze Date: Wed, 22 Jan 2014 22:15:39 +0000 Subject: [PATCH] Improvements to picoLCD driver IR processing (by M. T. Jones). --- LCDd.conf | 14 +- docs/lcdproc-user/drivers/picolcd.docbook | 75 ++++- server/drivers/picolcd.c | 390 ++++++++++++++++------ server/drivers/picolcd.h | 3 +- 4 files changed, 368 insertions(+), 114 deletions(-) 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 */