aboutsummaryrefslogtreecommitdiffstats
path: root/drv_ShuttleVFD.c
diff options
context:
space:
mode:
Diffstat (limited to 'drv_ShuttleVFD.c')
-rw-r--r--drv_ShuttleVFD.c429
1 files changed, 429 insertions, 0 deletions
diff --git a/drv_ShuttleVFD.c b/drv_ShuttleVFD.c
new file mode 100644
index 0000000..24fc434
--- /dev/null
+++ b/drv_ShuttleVFD.c
@@ -0,0 +1,429 @@
+/* $Id: drv_Sample.c 975 2009-01-18 11:16:20Z michael $
+ * $URL: https://ssl.bulix.org/svn/lcd4linux/trunk/drv_Sample.c $
+ *
+ * Shuttle SG33G5M VFD lcd4linux driver
+ *
+ * Copyright (C) 2009 Matthieu Crapet <mcrapet@gmail.com>
+ * based on the USBLCD driver.
+ *
+ * Shuttle SG33G5M VFD (20x1 character display. Each character cell is 5x8 pixels)
+ * - The display is driven by Princeton Technologies PT6314 VFD controller
+ * - Cypress CY7C63723C (receives USB commands and talk to VFD controller)
+ *
+ * LCD "prococol" : each message has a length of 8 bytes
+ * - 1 nibble: command (0x1, 0x3, 0x7, 0x9, 0xD)
+ * - 0x1 : clear text and icons (len=1)
+ * - 0x7 : icons (len=4)
+ * - 0x9 : text (len=7)
+ * - 0xD : set clock data (len=7)
+ * - 0x3 : display clock (internal feature) (len=1)
+ * - 1 nibble: message length (0-7)
+ * - 7 bytes : message data
+ *
+ * This file is part of LCD4Linux.
+ *
+ * LCD4Linux is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * LCD4Linux is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/*
+ *
+ * exported fuctions:
+ *
+ * struct DRIVER drv_ShuttleVFD
+ *
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+
+#ifdef HAVE_USB_H
+#include <usb.h>
+#else
+#error "ShuttleVFD: libusb required"
+#endif
+
+#include "debug.h"
+#include "cfg.h"
+#include "qprintf.h"
+#include "udelay.h"
+#include "plugin.h"
+#include "widget.h"
+#include "widget_text.h"
+#include "widget_bar.h" // for DIRECTION
+#include "drv.h"
+#include "drv_generic_text.h"
+#include "drv_generic_gpio.h"
+
+
+/*
+ * Some hardware definitions
+ */
+// VFD USB properties
+#define SHUTTLE_VFD_VENDOR_ID 0x051C
+#define SHUTTLE_VFD_PRODUCT_ID1 0x0003
+#define SHUTTLE_VFD_PRODUCT_ID2 0x0005 // IR-receiver included
+#define SHUTTLE_VFD_INTERFACE_NUM 1
+
+// VFD physical dimensions
+#define SHUTTLE_VFD_WIDTH 20
+#define SHUTTLE_VFD_HEIGHT 1
+
+// VFD USB control message
+#define SHUTTLE_VFD_PACKET_SIZE 8
+#define SHUTTLE_VFD_DATA_SIZE (SHUTTLE_VFD_PACKET_SIZE-1)
+#define SHUTTLE_VFD_SUCCESS_SLEEP_USEC 25600
+
+
+/* Global static data */
+static char Name[] = "ShuttleVFD";
+static usb_dev_handle *lcd;
+static unsigned char buffer[SHUTTLE_VFD_PACKET_SIZE];
+
+/* Issues with the display module:
+ * - Can't set cursor position. Must save full buffer here.
+ * - Can't get icons status (on or off). Must save status here.
+ * - Clear command also clear text AND icons.
+ */
+static unsigned char fb[SHUTTLE_VFD_WIDTH * SHUTTLE_VFD_HEIGHT];
+static unsigned icons;
+
+
+/****************************************/
+/*** hardware dependant functions ***/
+/****************************************/
+
+/* look for device on USB bus */
+static int drv_ShuttleVFD_open(void)
+{
+ struct usb_bus *bus;
+ struct usb_device *dev;
+
+ int vendor_id = SHUTTLE_VFD_VENDOR_ID;
+ int interface = SHUTTLE_VFD_INTERFACE_NUM;
+
+ lcd = NULL;
+
+ usb_init();
+ usb_find_busses();
+ usb_find_devices();
+
+ for (bus = usb_get_busses(); bus != NULL; bus = bus->next) {
+ for (dev = bus->devices; dev != NULL; dev = dev->next) {
+ if (dev->descriptor.idVendor == vendor_id && ((dev->descriptor.idProduct == SHUTTLE_VFD_PRODUCT_ID1) ||
+ (dev->descriptor.idProduct == SHUTTLE_VFD_PRODUCT_ID2))) {
+
+ unsigned int v = dev->descriptor.bcdDevice;
+
+ info("%s: found ShuttleVFD V%1d%1d.%1d%1d on bus %s device %s", Name,
+ (v & 0xF000) >> 12, (v & 0xF00) >> 8, (v & 0xF0) >> 4, (v & 0xF), bus->dirname, dev->filename);
+
+ lcd = usb_open(dev);
+ }
+ }
+ }
+
+ if (lcd != NULL) {
+ if (usb_claim_interface(lcd, interface) < 0) {
+ usb_close(lcd);
+ error("%s: usb_claim_interface() failed!", Name);
+ error("%s: root permissions maybe required?", Name);
+ return -1;
+ }
+ } else {
+ error("%s: could not find ShuttleVFD", Name);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int drv_ShuttleVFD_close(void)
+{
+ int interface = SHUTTLE_VFD_INTERFACE_NUM;
+
+ usb_release_interface(lcd, interface);
+ usb_close(lcd);
+ return 0;
+}
+
+
+static void drv_ShuttleVFD_send(unsigned char packet[SHUTTLE_VFD_PACKET_SIZE])
+{
+ if (usb_control_msg(lcd, 0x21, // requesttype
+ 0x09, // request
+ 0x0200, // value
+ 0x0001, // index
+ (char *) packet, SHUTTLE_VFD_PACKET_SIZE, 100) == SHUTTLE_VFD_PACKET_SIZE) {
+
+ udelay(SHUTTLE_VFD_SUCCESS_SLEEP_USEC);
+ } else {
+ debug("usb_control_msg failed");
+ }
+}
+
+
+/* Clear full display and icons. */
+static void drv_ShuttleVFD_clear(void)
+{
+ // Update local framebuffer mirror
+ memset(fb, ' ', SHUTTLE_VFD_HEIGHT * SHUTTLE_VFD_WIDTH);
+
+ buffer[0] = (1 << 4) + 1;
+ buffer[1] = 0x1;
+ drv_ShuttleVFD_send(buffer);
+}
+
+
+static void drv_ShuttleVFD_reset_cursor(void)
+{
+ buffer[0] = (1 << 4) + 1;
+ buffer[1] = 0x2;
+ drv_ShuttleVFD_send(buffer);
+}
+
+
+/* text mode displays only */
+static void drv_ShuttleVFD_write(const int row, const int col, const char *data, int len)
+{
+ unsigned char *p;
+ int i;
+
+ // Update local framebuffer mirror
+ memcpy(fb + (row * SHUTTLE_VFD_WIDTH) + col, data, len);
+
+ p = fb;
+ len = SHUTTLE_VFD_WIDTH;
+
+ drv_ShuttleVFD_reset_cursor();
+
+ while (len > 0) {
+ if (len > 7)
+ buffer[0] = (9 << 4) + 7;
+ else
+ buffer[0] = (9 << 4) + len;
+
+ for (i = 0; i < 7 && len--; i++) {
+ buffer[i + 1] = *p++;
+ }
+
+ drv_ShuttleVFD_send(buffer);
+ }
+}
+
+
+static void drv_ShuttleVFD_defchar(const int ascii, const unsigned char *matrix)
+{
+ (void) matrix;
+ debug("%s: not available (ascii=%d)", Name, ascii);
+}
+
+
+static int drv_ShuttleVFD_start(const char *section)
+{
+ char *port;
+
+ port = cfg_get(section, "Port", NULL);
+
+ if (port == NULL || *port == '\0') {
+ error("%s: no '%s.Port' entry from %s", Name, section, cfg_source());
+ return -1;
+ }
+
+ if (strcasecmp(port, "libusb") != 0) {
+ error("%s: libusb expected", Name);
+ error("%s: compile lcd4linux with libusb support!", Name);
+ return -1;
+ }
+
+ DROWS = SHUTTLE_VFD_HEIGHT;
+ DCOLS = SHUTTLE_VFD_WIDTH;
+
+ /* open communication with the display */
+ if (drv_ShuttleVFD_open() < 0) {
+ return -1;
+ }
+
+ drv_ShuttleVFD_clear(); /* clear display */
+ return 0;
+}
+
+
+/* VFD Icons. Add +1 in lcd4linux.conf (GPO1..GPIO27)
+ * 0: television
+ * 1: cd/dvd
+ * 2: music
+ * 3: radio
+ * 4: clock
+ * 5: pause
+ * 6: play
+ * 7: record
+ * 8: rewind
+ * 9: camera
+ * 10: mute
+ * 11: repeat
+ * 12: reverse
+ * 13: fastforward
+ * 14: stop
+ * 15: volume 1
+ * 16: volume 2
+ * ...
+ * 25: volume 11
+ * 26: volume 12
+ */
+static int drv_ShuttleVFD_icons_set(const int num, const int val)
+{
+ unsigned long value;
+
+ if (num < 0 || num >= 27) {
+ info("%s: num %d out of range (1..27)", Name, num);
+ return -1;
+ }
+ // Special case for volume (icon n°16)
+ if (num >= 15)
+ value = (num - 15 + 1) << 15;
+ else
+ value = 1 << num;
+
+ if (val > 0)
+ icons |= value;
+ else
+ icons &= ~value;
+
+ buffer[0] = (7 << 4) + 4;
+ buffer[1] = (value >> 15) & 0x1F;
+ buffer[2] = (value >> 10) & 0x1F;
+ buffer[3] = (value >> 5) & 0x1F;
+ buffer[4] = value & 0x1F; // each data byte is stored on 5 bits
+ drv_ShuttleVFD_send(buffer);
+
+ return 0;
+}
+
+
+/****************************************/
+/*** plugins ***/
+/****************************************/
+
+/* none yet ! */
+
+
+/****************************************/
+/*** widget callbacks ***/
+/****************************************/
+
+/* using drv_generic_text_draw(W) */
+/* using drv_generic_gpio_draw(W) */
+
+
+/****************************************/
+/*** exported functions ***/
+/****************************************/
+
+
+/* supported Shuttle models */
+int drv_ShuttleVFD_list(void)
+{
+ printf("Shuttle SG33G5M, Shuttle PF27 upgrade kit");
+ return 0;
+}
+
+
+/* initialize driver & text display */
+int drv_ShuttleVFD_init(const char *section, const int quiet)
+{
+ WIDGET_CLASS wc;
+ int ret;
+
+ info("%s: %s", Name, "$Rev: 975 $");
+
+ /* display preferences */
+ XRES = 5; /* pixel width of one char */
+ YRES = 8; /* pixel height of one char */
+ CHARS = 0; /* number of user-defineable characters */
+ CHAR0 = 0; /* ASCII of first user-defineable char */
+ GOTO_COST = 2; /* number of bytes a goto command requires */
+ GPOS = 15 + 12; /* Fancy icons on top of display */
+
+ /* real worker functions */
+ drv_generic_text_real_write = drv_ShuttleVFD_write;
+ drv_generic_text_real_defchar = drv_ShuttleVFD_defchar;
+ drv_generic_gpio_real_set = drv_ShuttleVFD_icons_set;
+
+ /* start display */
+ if ((ret = drv_ShuttleVFD_start(section)) != 0)
+ return ret;
+
+ if (!quiet) {
+ char buffer[40];
+ qprintf(buffer, sizeof(buffer), "%s %dx%d", Name, DCOLS, DROWS);
+ if (drv_generic_text_greet(buffer, "Shuttle")) {
+ sleep(3);
+ drv_ShuttleVFD_clear();
+ }
+ }
+
+ /* initialize generic text driver */
+ if ((ret = drv_generic_text_init(section, Name)) != 0)
+ return ret;
+
+ /* initialize generic GPIO driver */
+ if ((ret = drv_generic_gpio_init(section, Name)) != 0)
+ return ret;
+
+ /* register text widget */
+ wc = Widget_Text;
+ wc.draw = drv_generic_text_draw;
+ widget_register(&wc);
+
+ return 0;
+}
+
+
+/* close driver & text display */
+int drv_ShuttleVFD_quit(const int quiet)
+{
+ info("%s: shutting down.", Name);
+
+ /* clear display */
+ drv_ShuttleVFD_clear();
+
+ /* say goodbye... */
+ if (!quiet) {
+ drv_generic_text_greet("goodbye!", NULL);
+ }
+
+ drv_generic_text_quit();
+ drv_generic_gpio_quit();
+
+ debug("closing connection");
+ drv_ShuttleVFD_close();
+
+ return (0);
+}
+
+
+DRIVER drv_ShuttleVFD = {
+ .name = Name,
+ .list = drv_ShuttleVFD_list,
+ .init = drv_ShuttleVFD_init,
+ .quit = drv_ShuttleVFD_quit,
+};