picolcdgfx driver

Scott Meharg <[email protected]> Tue, 29 Jan 2013 18:59:21 -0600
Newsgroups gmane.comp.sysutils.lcdproc
Message-ID <CAOgbcjQvV8HBY=kOKy71cfYO28ZH6NVWo4TyZBcWzh+zaY6B0w@mail.gmail.com>
Attached is the patch and file for Mini-box.com's PicoLCDGraphics LCD.

It uses glcd driver with connect type picolcdgfx.

_______________________________________________
LCDproc mailing list
[email protected]
http://lists.omnipotent.net/mailman/listinfo/lcdproc
glcd-picolcdgfx.c (text/x-csrc, 9.4 KB)
/** \file server/drivers/glcd-picolcdgfx.c
 * Driver for the picoLCD Graphics 256x64 from mini-box.com.  Based on lcd4linux
 * driver by Nicu Pavel.
 */

/*-
 * Copyright (c) 2009 Nicu Pavel <[email protected]>
 * Copyright (c) 2012 Scott Meahrg <[email protected]>
 *
 * This file is released under the GNU General Public License. Refer to the
 * COPYING file distributed with this package.
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <stdlib.h>
#include <errno.h>
#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <usb.h>

#include "lcd.h"
#include "report.h"
#include "glcd-low.h"

#define picoLCDGfx_VENDOR  0x04d8
#define picoLCDGfx_DEVICE  0xc002

/* picoLCD Graphics Commands */
/* These were taken from picolcd.h */
#define PICOLCDGFX_IN_KEY_STATE		0x11

#define PICOLCDGFX_OUT_GPO		0x81

#define PICOLCDGFX_OUT_BACKLIGHT	0x91
#define PICOLCDGFX_OUT_CONTRAST		0x92
#define PICOLCDGFX_OUT_INIT_DISPLAY	0x93
#define PICOLCDGFX_OUT_CMD		0x94
#define PICOLCDGFX_OUT_DATA		0x95
#define PICOLCDGFX_OUT_CMD_DATA		0x96

#define PICOLCDGFX_MAX_DATA_LEN 24

#define PICOLCDGFX_DEF_KEYTIMEOUT	125
#define PICOLCDGFX_DEF_INVERTED		0

/** Private data for the X11 connection type */
typedef struct glcd_picolcdgfx_data {
	usb_dev_handle *lcd;
	unsigned char inverted;
	int keytimeout;

	unsigned char *backingstore;
} CT_picolcdgfx_data;

/* Prototypes */
void glcd_picolcdgfx_blit(PrivateData *p);
void glcd_picolcdgfx_close(PrivateData *p);
unsigned char glcd_picolcdgfx_pollkeys(PrivateData *p);
void glcd_picolcdgfx_set_backlight(PrivateData *p, int state);
void glcd_picolcdgfx_set_contrast(PrivateData *p, int value);

int
picolcdgfx_read(usb_dev_handle *lcd, unsigned char *data, int size, int timeout)
{
    return usb_interrupt_read(lcd, USB_ENDPOINT_IN + 1, (char *) data, size, timeout);
}

void
picolcdgfx_write(usb_dev_handle *lcd, unsigned char *data, int size)
{
    int __attribute__ ((unused)) ret;
    ret = usb_interrupt_write(lcd, USB_ENDPOINT_OUT + 1, (char *) data, size, 1000);
}

/**
 * API: Initialize the connection type driver.
 * \param drvthis  Pointer to driver structure.
 * \retval 0       Success.
 * \retval <0      Error.
 */
int
glcd_picolcdgfx_init(Driver *drvthis)
{
	PrivateData *p = (PrivateData *)drvthis->private_data;
	CT_picolcdgfx_data *ct_data;
	
	struct usb_bus *busses, *bus;
	struct usb_device *dev;
	char driver[1024];
	char product[1024];
	char manufacturer[1024];
	char serialnumber[1024];
	int ret;
    
	report(RPT_INFO, "GLCD/picolcdgfx: intializing");

	/* Set up connection type low-level functions */
	p->glcd_functions->blit = glcd_picolcdgfx_blit;
	p->glcd_functions->close = glcd_picolcdgfx_close;
	p->glcd_functions->poll_keys = glcd_picolcdgfx_pollkeys;
	p->glcd_functions->set_backlight = glcd_picolcdgfx_set_backlight;
	p->glcd_functions->set_contrast = glcd_picolcdgfx_set_contrast;

	/* Allocate memory structures */
	ct_data = (CT_picolcdgfx_data *) calloc(1, sizeof(CT_picolcdgfx_data));
	if (ct_data == NULL) {
		report(RPT_ERR, "GLCD/picolcdgfx: error allocating connection data");
		return -1;
	}
	p->ct_data = ct_data;
	
	/* Fix display size to 256x64 */
	p->framebuf.px_width = 256;
	p->framebuf.px_height = 64;
	
	/* Since the display is fixed to 256x64 we have to recalculate. */
	p->framebuf.bytesPerLine = (p->framebuf.px_width + 7) / 8;
	p->framebuf.size = p->framebuf.bytesPerLine * p->framebuf.px_height;
	
	ct_data->backingstore = malloc(p->framebuf.size);
	if (ct_data->backingstore == NULL) {
		report(RPT_ERR, "GLCD/picolcdgfx: unable to allocate backing store");
		return -1;
	}
	memset(ct_data->backingstore, 0x00, p->framebuf.size);
	
	/* Get keytimeout */
	ct_data->keytimeout = drvthis->config_get_int(drvthis->name,
		"picolcdgfx_KeyTimeout", 0, PICOLCDGFX_DEF_KEYTIMEOUT);
		
	/* Get inverted option */
	ct_data->inverted = drvthis->config_get_bool(drvthis->name,
		"picolcdgfx_Inverted", 0, PICOLCDGFX_DEF_INVERTED);

	ct_data->lcd = NULL;

	report(RPT_DEBUG,"GLCD/picolcdgfx: scanning for picoLCD 256x64...");

	usb_init();
	usb_find_busses();
	usb_find_devices();
	busses = usb_get_busses();

	for (bus = busses; bus; bus = bus->next) {
	for (dev = bus->devices; dev; dev = dev->next) {
		if ((dev->descriptor.idVendor == picoLCDGfx_VENDOR) && (dev->descriptor.idProduct == picoLCDGfx_DEVICE)) {
			report(RPT_DEBUG,"GLCD/picolcdgfx: found picoLCDGraphics on bus %s device %s", bus->dirname, dev->filename);

			ct_data->lcd = usb_open(dev);

			ret = usb_get_driver_np(ct_data->lcd, 0, driver, sizeof(driver));

			if (ret == 0) {
				report(RPT_DEBUG,"GLCD/picolcdgfx: interface 0 already claimed by '%s'", driver);
				report(RPT_DEBUG,"GLCD/picolcdgfx: attempting to detach driver...");
				if (usb_detach_kernel_driver_np(ct_data->lcd, 0) < 0) {
					report(RPT_ERR,"GLCD/picolcdgfx: usb_detach_kernel_driver_np() failed!");
					return -1;
				}
			}

			usb_set_configuration(ct_data->lcd, 1);
			usleep(100);
		
			if (usb_claim_interface(ct_data->lcd, 0) < 0) {
				report(RPT_ERR,"GLCD/picolcdgfx: usb_claim_interface() failed!");
				return -1;
			}

			usb_set_altinterface(ct_data->lcd, 0);
			usb_get_string_simple(ct_data->lcd, dev->descriptor.iProduct, product, sizeof(product));
			usb_get_string_simple(ct_data->lcd, dev->descriptor.iManufacturer, manufacturer, sizeof(manufacturer));
			usb_get_string_simple(ct_data->lcd, dev->descriptor.iSerialNumber, serialnumber, sizeof(serialnumber));

			report(RPT_INFO,"GLCD/picolcdgfx: Manufacturer='%s' Product='%s' SerialNumber='%s'", manufacturer, product, serialnumber);
		
			debug(RPT_DEBUG, "GLCD/picolcdgfx: init() done");

			return 0;
		}
	}
	}
	
	report(RPT_ERR,"GLCD/picolcdgfx: could not find a picoLCDGraphics");
	return -1;	
}

/**
 * API: Write the framebuffer to the display
 * \param p  Pointer to glcd driver's private date structure.
 */
void
glcd_picolcdgfx_blit(PrivateData *p)
{
	CT_picolcdgfx_data *ct_data = (CT_picolcdgfx_data *) p->ct_data;
		
	/* Check if framebufer has changed. If not there's nothing to do */
	if (memcmp(p->framebuf.data, ct_data->backingstore, p->framebuf.size) == 0)
		return;
		
	unsigned char cmd3[64] = { PICOLCDGFX_OUT_CMD_DATA };	/* send command + data */
	unsigned char cmd4[64] = { PICOLCDGFX_OUT_DATA };	/* send data only */

	int index, bit, x, y;
	unsigned char cs, line;
	unsigned char pixel;

	for (cs = 0; cs < 4; cs++) {
		unsigned char chipsel = (cs << 2);	//chipselect
		for (line = 0; line < 8; line++) {
			cmd3[0] = PICOLCDGFX_OUT_CMD_DATA;
			cmd3[1] = chipsel;
			cmd3[2] = 0x02;
			cmd3[3] = 0x00;
			cmd3[4] = 0x00;
			cmd3[5] = 0xb8 | line;
			cmd3[6] = 0x00;
			cmd3[7] = 0x00;
			cmd3[8] = 0x40;
			cmd3[9] = 0x00;
			cmd3[10] = 0x00;
			cmd3[11] = 32;

			cmd4[0] = PICOLCDGFX_OUT_DATA;
			cmd4[1] = chipsel | 0x01;
			cmd4[2] = 0x00;
			cmd4[3] = 0x00;
			cmd4[4] = 32;

			for (index = 0; index < 32; index++) {
				pixel = 0x00;

				for (bit = 0; bit < 8; bit++) {
					x = cs * 64 + index;
					y = (line * 8 + bit + 0) % p->framebuf.px_height;
		    
					if ((fb_get_pixel(&p->framebuf, x, y) ^ ct_data->inverted) == FB_BLACK)
						pixel |= (1 << bit);
				}

				cmd3[12 + index] = pixel;
			}

			for (index = 32; index < 64; index++) {
				pixel = 0x00;

				for (bit = 0; bit < 8; bit++) {
					x = cs * 64 + index;
					y = (line * 8 + bit + 0) % p->framebuf.px_height;
		    
					if ((fb_get_pixel(&p->framebuf, x, y) ^ ct_data->inverted) == FB_BLACK)
						pixel |= (1 << bit);
				}

				cmd4[5 + (index - 32)] = pixel;
			}

			picolcdgfx_write(ct_data->lcd,cmd3, 44);
			picolcdgfx_write(ct_data->lcd,cmd4, 38);
		}
	}

	memcpy(ct_data->backingstore, p->framebuf.data, p->framebuf.size);
}

/**
 * API: Release low-level resources.
 * \param p  Pointer to glcd driver's private date structure.
 */
void
glcd_picolcdgfx_close(PrivateData *p)
{
	if (p->ct_data != NULL) {
		CT_picolcdgfx_data *ct_data = (CT_picolcdgfx_data *) p->ct_data;
		
		if (ct_data->lcd != NULL) {
			usb_release_interface(ct_data->lcd, 0);
			usb_close(ct_data->lcd);
		}
		
		if (ct_data->backingstore != NULL)
			free(ct_data->backingstore);
		
		free(p->ct_data);
		p->ct_data = NULL;
	}
}

unsigned char
glcd_picolcdgfx_pollkeys(PrivateData *p)
{
	CT_picolcdgfx_data *ct_data = (CT_picolcdgfx_data *) p->ct_data;
	unsigned char rv = 0;

	int ret;
	unsigned char rx_packet[PICOLCDGFX_MAX_DATA_LEN];
	ret = picolcdgfx_read(ct_data->lcd, rx_packet, PICOLCDGFX_MAX_DATA_LEN, ct_data->keytimeout);

	if ((ret > 0) && (rx_packet[0] == PICOLCDGFX_IN_KEY_STATE)) {
		switch (rx_packet[1]) {
			case 0x01:
				rv = 6;
				//keystr = "Escape";
				break;
			case 0x02:
				rv = 3;
				//keystr = "Left";
				break;
			case 0x05:
				rv = 1;
				//keystr = "Up";
				break;
			case 0x06:
				rv = 5;
				//keystr = "Enter";
				break;
			case 0x07:
				rv = 2;
				//keystr = "Down";
				break;
		}
	}

	return rv;
}

void
glcd_picolcdgfx_set_backlight(PrivateData *p, int state)
{
	CT_picolcdgfx_data *ct_data = (CT_picolcdgfx_data *)p->ct_data;
		
	unsigned char cmd[2] = { PICOLCDGFX_OUT_BACKLIGHT }; /* set backlight */
	
	if (state < 0)
		cmd[1] = 0;
	if (state >= 1)
		cmd[1] = 200;

	picolcdgfx_write(ct_data->lcd, cmd, 2);
}

void
glcd_picolcdgfx_set_contrast(PrivateData *p, int value)
{
	CT_picolcdgfx_data *ct_data = (CT_picolcdgfx_data *)p->ct_data;
	unsigned char cmd[2] = { PICOLCDGFX_OUT_CONTRAST };   /* set contrast */

	/* I believe contrast is 200 to 255
	 * with vlaue at 1000 I think 204 is the best. (S. Meharg)
	 */
	
	int val = (((1000 - value) / (float)1000) * 40) + 204;
	
	cmd[1] = val;
	picolcdgfx_write(ct_data->lcd, cmd, 2);

}
glcd-picolcdgfx.patch (application/octet-stream, 5.5 KB)
Index: lcdproc/LCDd.conf
===================================================================
RCS file: /cvsroot/lcdproc/lcdproc/LCDd.conf,v
retrieving revision 1.166
diff -u -r1.166 LCDd.conf
--- lcdproc/LCDd.conf	17 Jan 2013 22:15:45 -0000	1.166
+++ lcdproc/LCDd.conf	30 Jan 2013 00:44:58 -0000
@@ -357,6 +357,7 @@
 
 # Width and height of the display in pixel. The supported sizes may depend on
 # the ConnectionType. [default: 128x64; legal: 1x1 - 640x480]
+# NOTE: For connection type picolcdgfx the size is ignored and fixed at 256x64.
 #Size=128x64
 
 # Width and height of a character cell in pixels. This value is only used if
@@ -431,6 +432,14 @@
 # Important: The value must be quoted as it contains equal signs!
 #serdisp_options="INVERT=1"
 
+# picolcdgfx: Options
+# Time in ms for usb_read to wait on a key press. [default: 125; legal: >0]
+#picolcdgfx_KeyTimeout=125
+
+# Inverted: Inverts the pixels.  [default: no; legal: yes or no]
+#picolcdgfx_Inverted=no
+
+# picolcdgfx: End of options
 
 
 ## glcdlib meta driver for graphical LCDs ##
Index: lcdproc/acinclude.m4
===================================================================
RCS file: /cvsroot/lcdproc/lcdproc/acinclude.m4,v
retrieving revision 1.128
diff -u -r1.128 acinclude.m4
--- lcdproc/acinclude.m4	27 Jan 2013 11:44:43 -0000	1.128
+++ lcdproc/acinclude.m4	30 Jan 2013 00:44:59 -0000
@@ -174,7 +174,7 @@
 				GLCD_DRIVERS="$GLCD_DRIVERS glcd-glcd-png.o"
 			fi
 			if test "$enable_libusb" = yes ; then
-				GLCD_DRIVERS="$GLCD_DRIVERS glcd-glcd-glcd2usb.o"
+				GLCD_DRIVERS="$GLCD_DRIVERS glcd-glcd-glcd2usb.o glcd-glcd-picolcdgfx.o"
 			fi
 			AC_CHECK_HEADERS([serdisplib/serdisp.h],[
 				AC_CHECK_LIB(serdisp, serdisp_nextdisplaydescription,[
Index: lcdproc/docs/lcdproc-user/drivers/glcd.docbook
===================================================================
RCS file: /cvsroot/lcdproc/lcdproc/docs/lcdproc-user/drivers/glcd.docbook,v
retrieving revision 1.3
diff -u -r1.3 glcd.docbook
--- lcdproc/docs/lcdproc-user/drivers/glcd.docbook	4 Dec 2011 10:14:22 -0000	1.3
+++ lcdproc/docs/lcdproc-user/drivers/glcd.docbook	30 Jan 2013 00:44:59 -0000
@@ -80,6 +80,13 @@
 </para>
 </sect3>
 
+<sect3 id="glcd-ct-picolcdgfx">
+<title>Connection type picolcdgfx</title>
+<para>
+Support for the picoLCD Graphics 256x64 display from mini-box.com.
+</para>
+</sect3>
+
 <!--
 <sect3 id="glcd-ct-xyz">
 <title>Connection type xyz</title>
@@ -107,6 +114,7 @@
     <parameter><literal>glcd2usb</literal></parameter> |
     <parameter><literal>png</literal></parameter> |
     <parameter><literal>serdisplib</literal></parameter>
+    <parameter><literal>picolcdgfx</literal></parameter>
     }
   </term>
   <listitem><para>
@@ -419,6 +427,27 @@
 </varlistentry>
 </variablelist>
 
+<variablelist>
+<title>Settings for the picolcdgfx connection type</title>
+<varlistentry>
+  <term>
+    <property>picolcdgfx_KeyTimeout</property>
+  </term>
+  <listitem><para>
+    The time in milliseconds to wait for a key press before assuming no key was
+    pressed.  This settings is the usb_read timeout.  Default 125.
+  </para></listitem>
+</varlistentry>
+<varlistentry>
+  <term>
+    <property>picolcdgfx_Inverted</property>
+  </term>
+  <listitem><para>
+    When set to yes, the pixel will be inverted.  Default no.
+  </para></listitem>
+</varlistentry>
+</variablelist>
+
 </sect3>
 </sect2>
 </sect1>
Index: lcdproc/server/drivers/Makefile.am
===================================================================
RCS file: /cvsroot/lcdproc/lcdproc/server/drivers/Makefile.am,v
retrieving revision 1.110
diff -u -r1.110 Makefile.am
--- lcdproc/server/drivers/Makefile.am	27 Jan 2013 11:44:43 -0000	1.110
+++ lcdproc/server/drivers/Makefile.am	30 Jan 2013 00:44:59 -0000
@@ -91,7 +91,7 @@
 EyeboxOne_SOURCES =  lcd.h lcd_lib.h EyeboxOne.c EyeboxOne.h report.h
 g15_SOURCES =        lcd.h lcd_lib.h g15.h g15-num.c g15.c report.h
 glcd_SOURCES =       lcd.h report.h glcd_drv.c glcd_drv.h glcd-low.h glcd-drivers.h glcd-render.c glcd-render.h
-EXTRA_glcd_SOURCES = glcd-t6963.c t6963_low.c t6963_low.h glcd-png.c glcd-serdisp.c glcd-glcd2usb.c glcd-glcd2usb.h
+EXTRA_glcd_SOURCES = glcd-t6963.c t6963_low.c t6963_low.h glcd-png.c glcd-serdisp.c glcd-glcd2usb.c glcd-glcd2usb.h glcd-picolcdgfx.c
 glcdlib_SOURCES =    lcd.h lcd_lib.h glcdlib.h glcdlib.c report.h
 glk_SOURCES =        lcd.h glk.c glk.h glkproto.c glkproto.h report.h
 hd44780_SOURCES =    lcd.h lcd_lib.h hd44780.h hd44780.c hd44780-drivers.h hd44780-low.h hd44780-charmap.h report.h adv_bignum.h
Index: lcdproc/server/drivers/glcd-drivers.h
===================================================================
RCS file: /cvsroot/lcdproc/lcdproc/server/drivers/glcd-drivers.h,v
retrieving revision 1.4
diff -u -r1.4 glcd-drivers.h
--- lcdproc/server/drivers/glcd-drivers.h	27 Nov 2011 10:36:51 -0000	1.4
+++ lcdproc/server/drivers/glcd-drivers.h	30 Jan 2013 00:44:59 -0000
@@ -24,6 +24,7 @@
 #endif
 #ifdef HAVE_LIBUSB
 int glcd2usb_init(Driver *drvthis);
+int glcd_picolcdgfx_init(Driver *drvthis);
 #endif
 
 /* symbolic names for connection types */
@@ -32,6 +33,7 @@
 #define GLCD_CT_PNG		2
 #define GLCD_CT_SERDISP		3
 #define GLCD_CT_GLCD2USB	4
+#define GLCD_CT_PICOLCDGFX	6
 
 /** Structure linking symbolic names to initialization routines */
 typedef struct ConnectionMapping {
@@ -58,6 +60,7 @@
 #endif
 #ifdef HAVE_LIBUSB
 	{"glcd2usb", GLCD_CT_GLCD2USB, glcd2usb_init},
+	{"picolcdgfx", GLCD_CT_PICOLCDGFX, glcd_picolcdgfx_init},
 #endif
 	/* default, end of structure element (do not delete) */
 	{NULL, GLCD_CT_UNKNOWN, NULL}