support to send IR commands to LIRC in the picolcd driver (Jack Cleaver)

This commit is contained in:
marschap
2008-09-19 17:16:49 +00:00
parent 84bd6f8645
commit 695eb1ec8f
6 changed files with 495 additions and 167 deletions
+2
View File
@@ -248,6 +248,8 @@ Christian Jodar
Mariusz Bialonczyk
- ethlcd connection type for HD44780
Jack Cleaver
- LIRC support in picolcd
Further developers in the sourceforge lcdproc team:
+1
View File
@@ -54,6 +54,7 @@ v.0.5dev (ongoing development)
* lcdproc: unbreak compilation on OS X 1.5 and higher (Topher Fangio)
* server: fix interpretation of numeric priorities (Andrew M. Bishop)
+ hd44780 driver: new ConnectionType ethlcd (Mariusz Bialonczyk)
+ picolcd driver: support to send IR commands to LIRC (Jack Cleaver)
v.0.5.2
* fix switching on/off the Load screen in lcdproc client using the menu
+15
View File
@@ -803,6 +803,21 @@ Key3Light=on
Key4Light=on
Key5Light=on
# 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.
#LircAddress=127.0.0.1
# UDP port on which LIRC is listening [default: 8765; legal: 1 - 65535]
LircPort=8765
# Duration in jiffies of the synthesized sync pulse [default: 64; 0 to suppress]
LircSync=64
# Duration in jiffies of the synthesized gap; 0 to suppress, default 2048.
# This is really the fixed duration of an entire IR command; individual pulse/space
# durations are deducted from this to determine the duration of the trailing gap.
LircLength=2048
## Pyramid LCD driver ##
+80 -2
View File
@@ -186,6 +186,66 @@
Larger values make key presses more reliable if somewhat slow to take effect.
</para></listitem>
</varlistentry>
<varlistentry>
<term>
<command>LircHost=</command>
<arg choice="plain"><replaceable>HOSTNAME_OR_IP-ADDRESS</replaceable></arg>
</term>
<listitem>
<para>
Set the hostname or IP address to which the driver will send IR data from the sensor.
If not set or set to an empty value, IR support for LIRC will be disabled.
</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
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
anything, because UDP is a connectionless protocol.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<command>LircPort=</command>
<arg choice="plain"><replaceable>PORTNUM</replaceable></arg>
</term>
<listitem>
<para>
This value determines the UDP port to which the driver will send IR data from the sensor. It
defaults to <literal>8765</literal>, which is also the default port on which LIRC will listen.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<command>LircSync=</command>
<arg choice="plain"><replaceable>DURATION</replaceable></arg>
</term>
<listitem>
<para>
This value determines the length in jiffies (1/16384 seconds) of the synthesized sync pulse added
to the beginning of the UDP packet sent to LIRC. Values up to 255 are permitted. Setting a length
of zero will suppress the synthesized pulse (I can't make it work without it, though). The default is
<literal>64</literal> jiffies (3.9ms).
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<command>LircLength=</command>
<arg choice="plain"><replaceable>DURATION</replaceable></arg>
</term>
<listitem>
<para>
This value determines the length in jiffies (1/16384 seconds) of the entire LIRC code for a single remote
command. It depends on the notion that all commands are of the same duration, and are padded with a trailing
gap (space) to make up the time. No such gap is emitted by the picolcd hardware. Values up to MAXINT are permitted.
Setting a length of zero will suppress the synthesized gap. The default is 2048 jiffies (125ms).
</para>
</listitem>
</varlistentry>
</variablelist>
</sect3>
@@ -222,13 +282,31 @@
touchy and both combo key-down and key-up actions may be reported as mutiple events if
the user is more than a tenth of a second (maybe less?) off in motions.
</para>
<sect3 id="picolcd-ir-status">
<title>Infrared sensor status</title>
<para>
LIRC expects sensor data that starts with a longish 'sync' space, denoting the start of
a command; followed by the code data, a sequence of mark/space pairs; sometimes followed by
a 'gap', which should be a space long enough to make the entire command up to a preset
duration in milliseconds. The 'sync' and the 'gap' are absent from the data
emitted by the picolcd hardware. I found that LIRC configuration files for remotes similar to the
ones I tested all used such a fixed-duration encoding, and as that was the only way I could get it
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.
</para>
<para>
This code could therefore use more testing, more research, and more work.
</para>
</sect3>
</sect2>
<sect2 id="picolcd-copy">
<title>Copyright</title>
<para>
The lcdproc picolcd driver was written by Gatewood Green (woody@nitrosecurity.com)
or (woody@linif.org) and paid for by NitroSecurity, Inc (nitrosecurity.com).
The lcdproc picolcd driver originally was written by Gatewood Green (woody@nitrosecurity.com)
or (woody@linif.org) and paid for by NitroSecurity, Inc (nitrosecurity.com),
but has been extended with code from various contributors since then.
</para>
</sect2>
+391 -165
View File
@@ -3,10 +3,11 @@
*
* (c) 2007 NitroSecurity, Inc.
* Written by Gatewood Green <woody@nitrosecurity.com> or <woody@linif.org>
* (c) 2007 Peter Marschall - adapted coding style and reporting to LCDproc
* (c) 2007-2008 Peter Marschall - adapted coding style and reporting to LCDproc
* (c) 2007 Mini-Box.com, Nicu Pavel <npavel@ituner.com>
* - removed libusblcd and hid dependency
* - added vbar, hbar, custom char, bignum support
* (c) 2008 Jack Cleaver - add LIRC connection
*
* License: GPL (same as usblcd and lcdPROC)
*
@@ -43,6 +44,10 @@
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
/* 12 keys plus a 0 placeholder */
#define KEYPAD_MAX 13
@@ -83,6 +88,16 @@ typedef struct pd {
char *info;
unsigned char *framebuf;
unsigned char *lstframe;
int IRenabled;
//For communicating with LIRC
int lircsock;
struct sockaddr_in lircserver;
/* IR transcode results */
unsigned char result[512];
int sync;
int preset_gap;
int gap;
} PrivateData;
static char * keymap[KEYPAD_MAX] = {
@@ -106,6 +121,8 @@ static void picolcd_send(usb_dev_handle *lcd, unsigned char *data, int size);
static void picolcd_write(usb_dev_handle *lcd, const int row, const int col, const unsigned char *data);
static void get_key_event (usb_dev_handle *lcd, lcd_packet *packet, int timeout);
static void set_key_lights (usb_dev_handle *lcd, int keys[], int state);
static int ir_transcode(Driver *drvthis, unsigned char *data, unsigned int cbdata,
unsigned char *result, int cbresult);
/* lcd_logical_driver Variables */
MODULE_EXPORT char *api_version = API_VERSION;
@@ -115,17 +132,18 @@ MODULE_EXPORT char *symbol_prefix = "picoLCD_";
/* lcd_logical_driver mandatory functions */
MODULE_EXPORT int picoLCD_init(Driver *drvthis) {
PrivateData *pd;
PrivateData *p;
int x;
struct usb_bus *bus;
struct usb_device *dev;
const char *lirchost;
int lircport;
pd = (PrivateData *) malloc(sizeof(PrivateData));
if (! pd)
p = (PrivateData *) malloc(sizeof(PrivateData));
if (p == NULL)
return -1;
if (drvthis->store_private_ptr(drvthis, pd))
if (drvthis->store_private_ptr(drvthis, p))
return -1;
/* Try to find picolcd device */
@@ -133,223 +151,270 @@ MODULE_EXPORT int picoLCD_init(Driver *drvthis) {
usb_find_busses();
usb_find_devices();
pd->lcd = NULL;
p->lcd = NULL;
for (bus = usb_get_busses(); bus != NULL; bus = bus->next) {
for (dev = bus->devices; dev != NULL; dev = dev->next) {
if ((dev->descriptor.idVendor == picoLCD_VENDOR) &&
(dev->descriptor.idProduct == picoLCD_DEVICE)) {
report(RPT_INFO, "Found picoLCD on bus %s device %s", bus->dirname, dev->filename);
pd->lcd = usb_open(dev);
p->lcd = usb_open(dev);
goto done;
}
}
}
done:
if (pd->lcd != NULL) {
if (p->lcd != NULL) {
debug(RPT_DEBUG, "%s: opening device succeeded", drvthis->name);
if (usb_set_configuration(pd->lcd, 0) < 0) {
usb_close(pd->lcd);
if (usb_set_configuration(p->lcd, 0) < 0) {
usb_close(p->lcd);
report(RPT_ERR, "%s: unable to set configuration", drvthis->name);
return -1;
}
usleep(100);
if (usb_claim_interface(pd->lcd, 0) < 0) {
if (usb_claim_interface(p->lcd, 0) < 0) {
#ifdef LIBUSB_HAS_DETACH_KERNEL_DRIVER_NP
if ((usb_detach_kernel_driver_np(pd->lcd, 0) < 0) ||
(usb_claim_interface(pd->lcd, 0) < 0)) {
if ((usb_detach_kernel_driver_np(p->lcd, 0) < 0) ||
(usb_claim_interface(p->lcd, 0) < 0)) {
#ifdef LIBUSB_HAS_GET_DRIVER_NP
char driver[1024];
if (usb_get_driver_np(pd->lcd, 0, driver, sizeof(driver)) == 0)
if (usb_get_driver_np(p->lcd, 0, driver, sizeof(driver)) == 0)
report(RPT_WARNING, "Interface 0 already claimed by '%s'", driver);
#endif
usb_close(pd->lcd);
usb_close(p->lcd);
report(RPT_ERR, "%s: unable to re-claim interface", drvthis->name);
return -1;
}
#else
report(RPT_ERR, "%s: failed to claim interface", drvthis->name);
usb_close(pd->lcd);
usb_close(p->lcd);
return -1;
#endif
}
if (usb_set_altinterface(pd->lcd, 0) < 0)
if (usb_set_altinterface(p->lcd, 0) < 0)
report(RPT_WARNING, "%s: unable to set alternate configuration", drvthis->name);
} else {
report(RPT_ERR, "%s: no device found", drvthis->name);
return -1;
}
pd->width = 20; /* hard coded (mfg spec) */
pd->height = 2; /* hard coded (mfg spec) */
pd->info = "picoLCD: Supports the LCD as installed on the M300 (http://www.mini-box.com/Mini-Box-M300-LCD) ";
pd->cellwidth = LCD_DEFAULT_CELLWIDTH;
pd->cellheight = LCD_DEFAULT_CELLHEIGHT;
pd->ccmode = standard;
p->width = 20; /* hard coded (mfg spec) */
p->height = 2; /* hard coded (mfg spec) */
p->info = "picoLCD: Supports the LCD as installed on the M300 (http://www.mini-box.com/Mini-Box-M300-LCD) ";
p->cellwidth = LCD_DEFAULT_CELLWIDTH;
p->cellheight = LCD_DEFAULT_CELLHEIGHT;
p->ccmode = standard;
for (x = 0; x < KEYPAD_LIGHTS; x++)
pd->key_light[x] = 1; /* individual lights on */
p->key_light[x] = 1; /* individual lights on */
pd->contrast = drvthis->config_get_int( drvthis->name, "Contrast", 0, DEFAULT_CONTRAST );
pd->backlight = drvthis->config_get_bool(drvthis->name, "BackLight", 0, DEFAULT_BACKLIGHT);
pd->keylights = drvthis->config_get_bool(drvthis->name, "KeyLights", 0, DEFAULT_KEYLIGHTS); /* key lights with LCD Backlight? */
pd->key_timeout = drvthis->config_get_int( drvthis->name, "KeyTimeout", 0, DEFAULT_TIMEOUT );
p->contrast = drvthis->config_get_int( drvthis->name, "Contrast", 0, DEFAULT_CONTRAST );
p->backlight = drvthis->config_get_bool(drvthis->name, "BackLight", 0, DEFAULT_BACKLIGHT);
p->keylights = drvthis->config_get_bool(drvthis->name, "KeyLights", 0, DEFAULT_KEYLIGHTS); /* key lights with LCD Backlight? */
p->key_timeout = drvthis->config_get_int( drvthis->name, "KeyTimeout", 0, DEFAULT_TIMEOUT );
/* These allow individual lights to be disabled */
pd->key_light[0] = drvthis->config_get_bool(drvthis->name, "Key0Light", 0, 1); /* Directional PAD */
pd->key_light[1] = drvthis->config_get_bool(drvthis->name, "Key1Light", 0, 1); /* F1 */
pd->key_light[2] = drvthis->config_get_bool(drvthis->name, "Key2Light", 0, 1); /* F2 */
pd->key_light[3] = drvthis->config_get_bool(drvthis->name, "Key3Light", 0, 1); /* F3 */
pd->key_light[4] = drvthis->config_get_bool(drvthis->name, "Key4Light", 0, 1); /* F4 */
pd->key_light[5] = drvthis->config_get_bool(drvthis->name, "Key5Light", 0, 1); /* F5 */
p->key_light[0] = drvthis->config_get_bool(drvthis->name, "Key0Light", 0, 1); /* Directional PAD */
p->key_light[1] = drvthis->config_get_bool(drvthis->name, "Key1Light", 0, 1); /* F1 */
p->key_light[2] = drvthis->config_get_bool(drvthis->name, "Key2Light", 0, 1); /* F2 */
p->key_light[3] = drvthis->config_get_bool(drvthis->name, "Key3Light", 0, 1); /* F3 */
p->key_light[4] = drvthis->config_get_bool(drvthis->name, "Key4Light", 0, 1); /* F4 */
p->key_light[5] = drvthis->config_get_bool(drvthis->name, "Key5Light", 0, 1); /* F5 */
for (x = 0; x < KEYPAD_MAX; x++)
pd->key_matrix[x] = keymap[x];
p->key_matrix[x] = keymap[x];
pd->framebuf = (unsigned char *) malloc(pd->width * pd->height + 1);
if (pd->framebuf == NULL) {
report(RPT_ERR, "%s: unable to create framebuf", __FUNCTION__);
p->framebuf = (unsigned char *) malloc(p->width * p->height + 1);
if (p->framebuf == NULL) {
report(RPT_ERR, "%s: unable to create framebuf", drvthis->name);
return -1;
}
memset(pd->framebuf, ' ', pd->width * pd->height);
pd->framebuf[pd->width * pd->height] = 0;
memset(p->framebuf, ' ', p->width * p->height);
p->framebuf[p->width * p->height] = '\0';
pd->lstframe = (unsigned char *) malloc(pd->width * pd->height + 1);
if (pd->lstframe == NULL) {
report(RPT_ERR, "%s: unable to create lstframe", __FUNCTION__);
p->lstframe = (unsigned char *) malloc(p->width * p->height + 1);
if (p->lstframe == NULL) {
report(RPT_ERR, "%s: unable to create lstframe", drvthis->name);
return -1;
}
memset(pd->lstframe, ' ', pd->width * pd->height);
pd->lstframe[pd->width * pd->height] = 0;
memset(p->lstframe, ' ', p->width * p->height);
p->lstframe[p->width * p->height] = '\0';
if (pd->backlight)
if (p->backlight)
picoLCD_backlight(drvthis, 1);
if (! pd->keylights)
set_key_lights(pd->lcd, pd->key_light, 0);
if (! p->keylights)
set_key_lights(p->lcd, p->key_light, 0);
else
picoLCD_backlight(drvthis, 0);
picoLCD_set_contrast(drvthis, pd->contrast);
picoLCD_set_contrast(drvthis, p->contrast);
report(RPT_INFO, "picolcd: init complete", __FUNCTION__);
lirchost = drvthis->config_get_string(drvthis->name, "LircHost", 0, NULL);
lircport = drvthis->config_get_int(drvthis->name, "LircPort", 0, DEFAULT_LIRCPORT);
p->sync = drvthis->config_get_int(drvthis->name, "LircSync", 0, DEFAULT_SYNC_JIFFY);
p->preset_gap = drvthis->config_get_int(drvthis->name, "LircLength", 0, DEFAULT_LENGTH_JIFFY);
p->IRenabled = (lirchost != NULL && *lirchost != '\0') ? 1 : 0;
if (p->IRenabled) {
/* Initialize communication with LIRC */
struct hostent *hostinfo = gethostbyname(lirchost);
if (hostinfo == NULL) {
report (RPT_ERR, "%s: unknown LIRC host %s", drvthis->name, lirchost);
return -1;
}
if ((p->lircsock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
report(RPT_ERR, "%s: failed to create socket to send data to LIRC", drvthis->name);
return -1;
}
/* Construct the server sockaddr_in structure */
memset(&p->lircserver, 0, sizeof(p->lircserver)); /* Clear struct */
p->lircserver.sin_family = AF_INET; /* Internet/IP */
p->lircserver.sin_addr = *(struct in_addr *) hostinfo->h_addr; /* IP address */
p->lircserver.sin_port = htons(lircport); /* server port */
report(RPT_INFO, "%s: IR events will be sent to LIRC on %s:%d, with sync=%d and length=%d",
drvthis->name, lirchost, lircport, p->sync, p->preset_gap);
}
report(RPT_INFO, "%s: init complete", drvthis->name);
return 0;
}
MODULE_EXPORT void picoLCD_close(Driver *drvthis) {
PrivateData *pd = drvthis->private_data;
usb_release_interface(pd->lcd, 0);
usb_close(pd->lcd);
MODULE_EXPORT void picoLCD_close(Driver *drvthis)
{
PrivateData *p = drvthis->private_data;
debug(RPT_DEBUG, "picolcd: close complete");
usb_release_interface(p->lcd, 0);
usb_close(p->lcd);
debug(RPT_DEBUG, "%s: close complete", drvthis->name);
}
/* lcd_logical_driver Essential output functions */
MODULE_EXPORT int picoLCD_width(Driver *drvthis) {
PrivateData *pd = drvthis->private_data;
MODULE_EXPORT int picoLCD_width(Driver *drvthis)
{
PrivateData *p = drvthis->private_data;
return pd->width;
return p->width;
}
MODULE_EXPORT int picoLCD_height(Driver *drvthis) {
PrivateData *pd = drvthis->private_data;
return pd->height;
MODULE_EXPORT int picoLCD_height(Driver *drvthis)
{
PrivateData *p = drvthis->private_data;
return p->height;
}
MODULE_EXPORT void picoLCD_clear(Driver *drvthis) {
PrivateData *pd = drvthis->private_data;
memset(pd->framebuf, ' ', pd->width * pd->height);
pd->ccmode = standard;
MODULE_EXPORT void picoLCD_clear(Driver *drvthis)
{
PrivateData *p = drvthis->private_data;
debug(RPT_DEBUG, "picolcd: clear complete");
memset(p->framebuf, ' ', p->width * p->height);
p->ccmode = standard;
debug(RPT_DEBUG, "%s: clear complete", drvthis->name);
}
MODULE_EXPORT void picoLCD_flush(Driver *drvthis) {
PrivateData *pd = drvthis->private_data;
unsigned char *fb = pd->framebuf;
unsigned char *lf = pd->lstframe;
MODULE_EXPORT void picoLCD_flush(Driver *drvthis)
{
PrivateData *p = drvthis->private_data;
unsigned char *fb = p->framebuf;
unsigned char *lf = p->lstframe;
static unsigned char text[48];
int i, line, offset;
debug(RPT_DEBUG, "picolcd: flush started");
debug(RPT_DEBUG, "%s: flush started", drvthis->name);
for (line = 0; line < pd->height; line++) {
memset(text, 0, 48);
offset = line * pd->width;
fb = pd->framebuf + offset;
lf = pd->lstframe + offset;
for (line = 0; line < p->height; line++) {
memset(text, 0, sizeof(text));
offset = line * p->width;
fb = p->framebuf + offset;
lf = p->lstframe + offset;
for (i = 0; i < pd->width; i++) {
for (i = 0; i < p->width; i++) {
if (*fb++ != *lf++) {
strncpy((char *)text, (char *)pd->framebuf + offset, pd->width);
picolcd_write(pd->lcd, line, 0, text);
memcpy(pd->lstframe + offset, pd->framebuf + offset, pd->width);
strncpy((char *)text, (char *)p->framebuf + offset, p->width);
picolcd_write(p->lcd, line, 0, text);
memcpy(p->lstframe + offset, p->framebuf + offset, p->width);
debug(RPT_DEBUG, "picolcd: flush wrote line %d (%s)", line + 1, text);
debug(RPT_DEBUG, "%s: flush wrote line %d (%s)",
drvthis->name, line + 1, text);
break;
}
}
}
debug(RPT_DEBUG, "picolcd: flush complete\n\t(%s)\n\t(%s)", pd->framebuf, pd->lstframe);
debug(RPT_DEBUG, "%s: flush complete\n\t(%s)\n\t(%s)",
drvthis->name, p->framebuf, p->lstframe);
}
MODULE_EXPORT void picoLCD_string(Driver *drvthis, int x, int y, unsigned char *str) {
PrivateData *pd = drvthis->private_data;
MODULE_EXPORT void picoLCD_string(Driver *drvthis, int x, int y, unsigned char *str)
{
PrivateData *p = drvthis->private_data;
unsigned char *dest;
int len;
debug(RPT_DEBUG, "picolcd: string start (%s)", str);
debug(RPT_DEBUG, "%s: string start (%s)", drvthis->name, str);
if (y < 1 || y > pd->height)
if (y < 1 || y > p->height)
return;
if (x < 1 || x > pd->width)
if (x < 1 || x > p->width)
return;
len = strlen((char *)str);
if (len + x > pd->width) {
debug(RPT_DEBUG, "picolcd: string overlength (>%d). Start: %d Length: %d (%s)", pd->width, x, len ,str);
if (len + x > p->width) {
debug(RPT_DEBUG, "%s: string overlength (>%d). Start: %d Length: %d (%s)",
drvthis->name, p->width, x, len ,str);
len = pd->width - x; /* Copy what we can */
len = p->width - x; /* Copy what we can */
}
x--; y--; /* Convert 1-based to 0-based */
dest = pd->framebuf + (y * pd->width + x);
dest = p->framebuf + (y * p->width + x);
memcpy(dest, str, len * sizeof(char));
debug(RPT_DEBUG, "picolcd: string complete (%s)", str);
debug(RPT_DEBUG, "%s: string complete (%s)", drvthis->name, str);
}
MODULE_EXPORT void picoLCD_chr(Driver *drvthis, int x, int y, unsigned char chr) {
PrivateData *pd = drvthis->private_data;
unsigned char *dest;
debug(RPT_DEBUG, "picolcd: chr start (%c)", chr);
if (y < 1 || y > pd->height)
MODULE_EXPORT void picoLCD_chr(Driver *drvthis, int x, int y, unsigned char chr)
{
PrivateData *p = drvthis->private_data;
unsigned char *dest;
debug(RPT_DEBUG, "%s: chr start (%c)", drvthis->name, chr);
if (y < 1 || y > p->height)
return;
if (x < 1 || x > pd->width)
if (x < 1 || x > p->width)
return;
x--; y--; /* Convert 1-based to 0-based */
dest = pd->framebuf + (y * pd->width + x);
dest = p->framebuf + (y * p->width + x);
memcpy(dest, &chr, sizeof(char));
debug(RPT_DEBUG, "picolcd: chr complete (%c)", chr);
debug(RPT_DEBUG, "%s: chr complete (%c)", drvthis->name, chr);
}
MODULE_EXPORT void picoLCD_set_char (Driver *drvthis, int n, unsigned char *dat)
{
PrivateData *pd = drvthis->private_data;
PrivateData *p = drvthis->private_data;
unsigned char packet[10] = { 0x9c }; /* define character */
unsigned char mask = (1 << pd->cellwidth) - 1;
unsigned char mask = (1 << p->cellwidth) - 1;
int row;
if ((n < 0) || (n >= NUM_CCs))
@@ -359,13 +424,14 @@ MODULE_EXPORT void picoLCD_set_char (Driver *drvthis, int n, unsigned char *dat)
packet[1] = n; /* Custom char to define. */
for (row = 0; row < pd->cellheight; row++) {
for (row = 0; row < p->cellheight; row++) {
packet[row + 2] = dat[row] & mask;
}
picolcd_send(pd->lcd, packet, 10);
picolcd_send(p->lcd, packet, 10);
}
MODULE_EXPORT int picoLCD_get_free_chars (Driver *drvthis)
{
return NUM_CCs;
@@ -374,80 +440,80 @@ MODULE_EXPORT int picoLCD_get_free_chars (Driver *drvthis)
MODULE_EXPORT void picoLCD_vbar (Driver *drvthis, int x, int y, int len, int promille, int options)
{
PrivateData *pd = drvthis->private_data;
PrivateData *p = drvthis->private_data;
if (pd->ccmode != vbar) {
unsigned char vBar[pd->cellheight];
if (p->ccmode != vbar) {
unsigned char vBar[p->cellheight];
int i;
if (pd->ccmode != standard) {
if (p->ccmode != standard) {
/* Not supported(yet) */
report(RPT_WARNING, "%s: vbar: cannot combine two modes using user-defined characters",
drvthis->name);
return;
}
pd->ccmode = vbar;
p->ccmode = vbar;
memset(vBar, 0x00, sizeof(vBar));
for (i = 1; i < pd->cellheight; i++) {
for (i = 1; i < p->cellheight; i++) {
// add pixel line per pixel line ...
vBar[pd->cellheight - i] = 0xFF;
vBar[p->cellheight - i] = 0xFF;
picoLCD_set_char(drvthis, i, vBar);
}
}
lib_vbar_static(drvthis, x, y, len, promille, options, pd->cellheight, 0);
lib_vbar_static(drvthis, x, y, len, promille, options, p->cellheight, 0);
}
MODULE_EXPORT void picoLCD_hbar (Driver *drvthis, int x, int y, int len, int promille, int options)
{
PrivateData *pd = drvthis->private_data;
PrivateData *p = drvthis->private_data;
if (pd->ccmode != hbar) {
unsigned char hBar[pd->cellheight];
if (p->ccmode != hbar) {
unsigned char hBar[p->cellheight];
int i;
if (pd->ccmode != standard) {
if (p->ccmode != standard) {
/* Not supported(yet) */
report(RPT_WARNING, "%s: hbar: cannot combine two modes using user-defined characters",
drvthis->name);
return;
}
pd->ccmode = hbar;
p->ccmode = hbar;
memset(hBar, 0x00, sizeof(hBar));
for (i = 1; i <= pd->cellwidth; i++) {
for (i = 1; i <= p->cellwidth; i++) {
// fill pixel columns from left to right.
memset(hBar, 0xFF & ~((1 << (pd->cellwidth - i)) - 1), sizeof(hBar)-1);
memset(hBar, 0xFF & ~((1 << (p->cellwidth - i)) - 1), sizeof(hBar)-1);
picoLCD_set_char(drvthis, i, hBar);
}
}
lib_hbar_static(drvthis, x, y, len, promille, options, pd->cellwidth, 0);
lib_hbar_static(drvthis, x, y, len, promille, options, p->cellwidth, 0);
}
MODULE_EXPORT void picoLCD_num (Driver *drvthis, int x, int num)
{
PrivateData *pd = drvthis->private_data;
PrivateData *p = drvthis->private_data;
int do_init = 0;
if ((num < 0) || (num > 10))
return;
if (pd->ccmode != bignum) {
if (pd->ccmode != standard) {
if (p->ccmode != bignum) {
if (p->ccmode != standard) {
/* Not supported (yet) */
report(RPT_WARNING, "%s: num: cannot combine two modes using user-defined characters",
drvthis->name);
return;
}
pd->ccmode = bignum;
p->ccmode = bignum;
do_init = 1;
}
@@ -459,7 +525,7 @@ MODULE_EXPORT void picoLCD_num (Driver *drvthis, int x, int num)
MODULE_EXPORT int picoLCD_icon (Driver *drvthis, int x, int y, int icon)
{
PrivateData *pd = drvthis->private_data;
PrivateData *p = drvthis->private_data;
/* 8x5 icons each number represents one row in binary */
@@ -475,12 +541,12 @@ MODULE_EXPORT int picoLCD_icon (Driver *drvthis, int x, int y, int icon)
picoLCD_chr(drvthis, x, y, 255);
break;
case ICON_HEART_FILLED:
pd->ccmode = custom;
p->ccmode = custom;
picoLCD_set_char(drvthis, 7, heart_filled);
picoLCD_chr(drvthis, x, y, 7);
break;
case ICON_HEART_OPEN:
pd->ccmode = custom;
p->ccmode = custom;
picoLCD_set_char(drvthis, 7, heart_open);
picoLCD_chr(drvthis, x, y, 7);
break;
@@ -497,43 +563,72 @@ MODULE_EXPORT int picoLCD_icon (Driver *drvthis, int x, int y, int icon)
return 0;
}
/* lcd_logical_driver Essential input functions */
/* lcd_logical_driver Essential input functions */
MODULE_EXPORT char *picoLCD_get_key(Driver *drvthis) {
PrivateData *pd = drvthis->private_data;
PrivateData *p = drvthis->private_data;
lcd_packet *keydata;
char *keystr = NULL;
int keys_read = 0;
int key_pass = 0;
int two_keys = 0;
debug(RPT_DEBUG, "picolcd: get_key start (timeout %d)", pd->key_timeout);
debug(RPT_DEBUG, "%s: get_key start (timeout %d)",
drvthis->name, p->key_timeout);
keydata = malloc(sizeof(lcd_packet));
while (! keys_read) {
get_key_event(pd->lcd, keydata, pd->key_timeout);
debug(RPT_DEBUG, "picolcd: get_key got an event");
get_key_event(p->lcd, keydata, p->key_timeout);
debug(RPT_DEBUG, "%s: get_key got an event", drvthis->name);
if (keydata->type == IN_REPORT_KEY_STATE) {
if (! keydata->data[1] && key_pass) {
debug(RPT_DEBUG, "picolcd: get_key got all clear");
debug(RPT_DEBUG, "%s: get_key got all clear", drvthis->name);
/* Got a <0, 0> key-up event after reading a valid key press event */
keys_read++; /* All clear */
} else if (! keydata->data[2] && ! two_keys) {
debug(RPT_DEBUG, "picolcd: get_key got one key");
debug(RPT_DEBUG, "%s: get_key got one key", drvthis->name);
/* We got one key (but not after a two key event and before and all clear) */
keystr = pd->key_matrix[keydata->data[1]];
keystr = p->key_matrix[keydata->data[1]];
} else {
/* We got two keys */
debug(RPT_DEBUG, "picolcd: get_key got two keys");
debug(RPT_DEBUG, "%s: get_key got two keys", drvthis->name);
two_keys++;
sprintf(keystr, "%s+%s", pd->key_matrix[keydata->data[1]], pd->key_matrix[keydata->data[2]]);
sprintf(keystr, "%s+%s", p->key_matrix[keydata->data[1]], p->key_matrix[keydata->data[2]]);
}
key_pass++; /* This hack allows us to deal with receiving left over <0,0> first */
} else if (p->IRenabled && keydata->type == IN_REPORT_IR_DATA) {
int cbres;
debug(RPT_NOTICE, "%s: get_key irdata, length=%d bytes",
drvthis->name, keydata->data[1]);
cbres = ir_transcode(drvthis, (keydata->data)+2, keydata->data[1], p->result, sizeof(p->result));
debug(RPT_NOTICE, "%s: get_key irdata transcoded, count=%d bytes",
drvthis->name, cbres);
if (cbres < 0) {
report(RPT_ERR, "%s: could not transcode buffer, length=%d",
drvthis->name, keydata->data[1]);
} else {
debug(RPT_NOTICE, "%s: sending packet to lirc, length=%d",
drvthis->name, cbres);
int ret = sendto(p->lircsock, p->result, cbres, 0,
(struct sockaddr *) &(p->lircserver), sizeof(p->lircserver));
if (ret == -1) {
report(RPT_ERR, "%s: error sending UDP packet, errno=%d",
drvthis->name, errno);
} else if (ret != cbres) {
report(RPT_ERR, "%s: mismatch in number of bytes sent (%d!=%d)",
drvthis->name, cbres, ret);
} else {
debug(RPT_NOTICE, "%s: packet sent to lirc.", drvthis->name);
}
}
} else {
debug(RPT_DEBUG, "picolcd: get_key got non-key data or timeout");
debug(RPT_DEBUG, "%s: get_key got non-key data or timeout", drvthis->name);
/* We got IR or otherwise bad data */
free(keydata);
return NULL;
@@ -543,7 +638,7 @@ MODULE_EXPORT char *picoLCD_get_key(Driver *drvthis) {
free(keydata);
debug(RPT_DEBUG, "picolcd: get_key complete (%s)", keystr);
debug(RPT_DEBUG, "%s: get_key complete (%s)", drvthis->name, keystr);
if ((keystr != NULL) && (strlen(keystr) > 0))
return keystr;
@@ -591,18 +686,22 @@ MODULE_EXPORT char *picoLCD_get_key(Driver *drvthis) {
}
/* lcd_logical_driver Extended output functions */
/* lcd_logical_driver User-defined character functions */
/* lcd_logical_driver Hardware functions */
/*MODULE_EXPORT int picoLCD_get_contrast(Driver *drvthis) {
PrivateData *pd = drvthis->private_data;
/*MODULE_EXPORT int picoLCD_get_contrast(Driver *drvthis)
{
PrivateData *p = drvthis->private_data;
}*/
MODULE_EXPORT int picoLCD_set_contrast(Driver *drvthis, int promille) {
PrivateData *pd = drvthis->private_data;
MODULE_EXPORT int picoLCD_set_contrast(Driver *drvthis, int promille)
{
PrivateData *p = drvthis->private_data;
int inv; /* The hardware seems to go dark on higher values, so we turn it around */
unsigned char packet[2] = { 0x92 }; /* set contrast id */
@@ -617,38 +716,43 @@ MODULE_EXPORT int picoLCD_set_contrast(Driver *drvthis, int promille) {
return -1;
}
picolcd_send(pd->lcd, packet, 2);
picolcd_send(p->lcd, packet, 2);
return 0;
}
/*MODULE_EXPORT int picoLCD_get_brightness(Driver *drvthis, int state) {
PrivateData *pd = drvthis->private_data;
/*MODULE_EXPORT int picoLCD_get_brightness(Driver *drvthis, int state)
{
PrivateData *p = drvthis->private_data;
}*/
/*MODULE_EXPORT int picoLCD_set_brightness(Driver *drvthis, int state, int promille) {
PrivateData *pd = drvthis->private_data;
/*MODULE_EXPORT int picoLCD_set_brightness(Driver *drvthis, int state, int promille)
{
PrivateData *p = drvthis->private_data;
}*/
MODULE_EXPORT void picoLCD_backlight(Driver *drvthis, int state) {
PrivateData *pd = drvthis->private_data;
MODULE_EXPORT void picoLCD_backlight(Driver *drvthis, int state)
{
PrivateData *p = drvthis->private_data;
unsigned char packet[2] = { 0x91 }; /* set backlight id */
if (state == 0) {
packet[1] = state;
picolcd_send(pd->lcd, packet, 2);
set_key_lights(pd->lcd, pd->key_light, state);
picolcd_send(p->lcd, packet, 2);
set_key_lights(p->lcd, p->key_light, state);
return;
}
if (state == 1) {
packet[1] = state;
picolcd_send(pd->lcd, packet, 2);
if (pd->keylights)
set_key_lights(pd->lcd, pd->key_light, state);
picolcd_send(p->lcd, packet, 2);
if (p->keylights)
set_key_lights(p->lcd, p->key_light, state);
return;
}
@@ -656,24 +760,143 @@ MODULE_EXPORT void picoLCD_backlight(Driver *drvthis, int state) {
}
/*MODULE_EXPORT int picoLCD_output(Driver *drvthis, int state) {
PrivateData *pd = drvthis->private_data;
PrivateData *p = drvthis->private_data;
}*/
/* lcd_logical_driver Informational functions */
MODULE_EXPORT char *picoLCD_get_info(Driver *drvthis) {
PrivateData *pd = drvthis->private_data;
PrivateData *p = drvthis->private_data;
return pd->info;
return p->info;
}
/* Private functions */
/*
* Transcode from picoLCD USB format to LIRC UDP format.
* 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.
* 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.
*
* drvthis: driver data (used for debug() and report()).
* data, cbdata: buffer of integers to be transcoded.
* result,
* cbresult: buffer to receive the transcoded values.
* @return: number of bytes placed in result buffer.
*/
static int ir_transcode(Driver *drvthis, unsigned char *data, unsigned int cbdata,
unsigned char *result, int cbresult)
{
PrivateData *p = drvthis->private_data;
int i;
int cIntervals = cbdata >> 1;
int resptr = 0;
long w = (data[1] << 8) + data[0];
//Check for odd buffer length (invalid buffer)
if (cbdata & 1) {
return -1;
}
/*
* Look for an initial long PULSE, and frig it for LIRC's benefit:
* add a sync SPACE in front.
*/
if (w & 0x8000) { /* SPACE */
w = 65536 - w;
if (w > 8000) {
/*
* The signal was a space longer than 8000 ms, i.e. probably a sync. We
* now expect from picoLCD either a repeat-code, or a long pulse followed
* by a full code.
* Lirc expects a sync (SPACE) followed by either a header (pulse/space)
* or a repeat-code.
* Lirc is also expecting active-low signalling. So we emit the required
* sync, then the signals from picoLCD with arity inverted.
* Emit sync space to LIRC (0x8040, = 64 jiffies, = 3900usec).
*/
if (p->sync) {
result[resptr++] = p->sync;
result[resptr++] = 0x80;
}
p->gap = p->preset_gap;
debug(RPT_DEBUG, "%s: preset gap, length=%d jiffies",
drvthis->name, p->gap);
}
}
for (i = 0; i < cIntervals; i++) {
long w = (data[i*2 + 1] << 8) + data[i*2];
if (w & 0x8000) {
//IF w is negative THEN negate. E.g. 0xDCA1 (-9055) -> 9055.
w = 0x10000 - w;
//scale: orig is usec, new is jiffy. E.g. 9055usec = 148 jiffy.
w = (w * 16384 /1000000) & 0xFFFF;
p->gap -= w;
} else {
//Scale.
w = w * 16384 / 1000000;
//Set the space bit.
p->gap -= w;
w |= 0x8000;
}
if (resptr + 2 < cbresult) {
result[resptr++] = w & 0xFF;
result[resptr++] = (w >> 8) & 0xFF;
} else {
resptr = -1;
break;
}
}
/*
* Look for a short buffer with a terminal PULSE, and frig it for LIRC's benefit:
* add a gap SPACE after. This is an ugly gash; it won't work if the code ends with
* a buffer containing exactly ten intervals.
*/
if (resptr > 0 && cIntervals < 10) {
if (resptr + 2 < cbresult) {
int last = cIntervals -1;
w = (data[last*2 + 1] << 8) + data[last*2];
if (p->gap > 0) {
if (w & 0x8000) {
//terminal pulse
debug(RPT_DEBUG, "%s: appending gap, length=%d jiffies",
drvthis->name, p->gap);
p->gap |= 0x8000;
result[resptr++] = p->gap & 0xFF;
result[resptr++] = (p->gap >>8) & 0xFF;
} else {
debug(RPT_DEBUG, "%s: terminal space=[%04x]; not appending gap (length=%d jiffies)", (unsigned int)(w & 0xffff), drvthis->name, p->gap);
}
} else {
debug(RPT_DEBUG, "%s: not appending gap because it would be negative (length=%d jiffies)", drvthis->name, p->gap);
}
} else {
//Result buffer would overflow
resptr = -1;
}
} else {
debug(RPT_DEBUG, "%s: cIntervals=%d; not appending gap (length=%d jiffies)",
drvthis->name, cIntervals, p->gap);
}
return resptr;
}
static void picolcd_send(usb_dev_handle *lcd, unsigned char *data, int size)
{
usb_interrupt_write(lcd, USB_ENDPOINT_OUT + 1, (char *) data, size, 1000);
}
static void picolcd_write(usb_dev_handle *lcd, const int row, const int col, const unsigned char *data)
{
unsigned char packet[64];
@@ -688,7 +911,7 @@ static void picolcd_write(usb_dev_handle *lcd, const int row, const int col, con
packet[3] = len;
i = 4;
while (len--) {
while (len--) {
packet[i++] = *data++;
}
@@ -696,7 +919,8 @@ static void picolcd_write(usb_dev_handle *lcd, const int row, const int col, con
}
void get_key_event (usb_dev_handle *lcd, lcd_packet *packet, int timeout) {
static void get_key_event(usb_dev_handle *lcd, lcd_packet *packet, int timeout)
{
int ret;
memset(packet->data, 0, 255);
@@ -717,8 +941,9 @@ void get_key_event (usb_dev_handle *lcd, lcd_packet *packet, int timeout) {
}
}
void set_key_lights (usb_dev_handle *lcd, int keys[], int state) {
static void set_key_lights(usb_dev_handle *lcd, int keys[], int state)
{
unsigned char packet[2] = { 0x81 }; /* set led */
unsigned int leds = 0;
int i;
@@ -738,3 +963,4 @@ void set_key_lights (usb_dev_handle *lcd, int keys[], int state) {
packet[1] = leds;
picolcd_send(lcd, packet, 2);
}
+6
View File
@@ -34,13 +34,19 @@
#define PICOLCD_MAX_DATA_LEN 24
#define DEFAULT_LIRCPORT 8765
#define DEFAULT_SYNC_JIFFY 64
#define DEFAULT_LENGTH_JIFFY 2048
typedef struct _lcd_packet lcd_packet;
struct _lcd_packet {
unsigned char data[255];
unsigned int type;
};
MODULE_EXPORT int picoLCD_init(Driver *drvthis);
MODULE_EXPORT void picoLCD_close(Driver *drvthis);
MODULE_EXPORT int picoLCD_width(Driver *drvthis);