aboutsummaryrefslogtreecommitdiffstats
path: root/dvb-t/auto-With167kHzOffsets
blob: 5dfbd2eea1aeb8b1d2c51f827cadc71004be4cbb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
# for ($freq = 177500000; $freq <= 226500000; $freq += 7000000) {
# 	print "T $freq 7MHz AUTO NONE AUTO AUTO AUTO NONE\n";
# }
# for ($freq = 474000000; $freq <= 858000000; $freq += 8000000) {
# 	print "T $freq 8MHz AUTO NONE AUTO AUTO AUTO NONE\n";
# 	print "T ", $freq - 167000, " 8MHz AUTO NONE AUTO AUTO AUTO NONE\n";
# 	print "T ", $freq + 167000, " 8MHz AUTO NONE AUTO AUTO AUTO NONE\n";
# }
#
# T freq bw fec_hi fec_lo mod transmission-mode guard-interval hierarchy
T 177500000 7MHz AUTO NONE AUTO AUTO AUTO NONE
T 184500000 7MHz AUTO NONE AUTO AUTO AUTO NONE
T 191500000 7MHz AUTO NONE AUTO AUTO AUTO NONE
T 198500000 7MHz AUTO NONE AUTO AUTO AUTO NONE
T 205500000 7MHz AUTO NONE AUTO AUTO AUTO NONE
T 212500000 7MHz AUTO NONE AUTO AUTO AUTO NONE
T 219500000 7MHz AUTO NONE AUTO AUTO AUTO NONE
T 226500000 7MHz AUTO NONE AUTO AUTO AUTO NONE
T 474000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 473833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 474167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 482000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 481833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 482167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 490000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 489833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 490167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 498000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 497833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 498167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 506000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 505833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 506167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 514000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 513833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 514167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 522000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 521833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 522167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 530000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 529833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 530167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 538000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 537833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 538167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 546000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 545833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 546167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 554000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 553833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 554167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 562000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 561833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 562167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 570000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 569833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 570167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 578000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 577833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 578167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 586000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 585833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 586167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 594000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 593833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 594167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 602000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 601833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 602167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 610000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 609833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 610167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 618000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 617833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 618167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 626000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 625833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 626167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 634000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 633833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 634167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 642000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 641833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 642167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 650000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 649833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 650167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 658000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 657833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 658167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 666000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 665833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 666167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 674000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 673833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 674167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 682000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 681833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 682167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 690000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 689833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 690167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 698000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 697833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 698167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 706000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 705833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 706167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 714000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 713833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 714167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 722000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 721833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 722167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 730000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 729833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 730167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 738000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 737833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 738167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 746000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 745833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 746167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 754000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 753833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 754167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 762000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 761833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 762167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 770000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 769833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 770167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 778000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 777833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 778167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 786000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 785833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 786167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 794000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 793833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 794167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 802000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 801833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 802167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 810000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 809833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 810167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 818000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 817833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 818167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 826000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 825833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 826167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 834000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 833833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 834167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 842000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 841833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 842167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 850000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 849833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 850167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 858000000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 857833000 8MHz AUTO NONE AUTO AUTO AUTO NONE
T 858167000 8MHz AUTO NONE AUTO AUTO AUTO NONE
Name.Class */ .highlight .no { color: #003366; font-weight: bold } /* Name.Constant */ .highlight .nd { color: #555555 } /* Name.Decorator */ .highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */ .highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */ .highlight .nl { color: #336699; font-style: italic } /* Name.Label */ .highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */ .highlight .py { color: #336699; font-weight: bold } /* Name.Property */ .highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */ .highlight .nv { color: #336699 } /* Name.Variable */ .highlight .ow { color: #008800 } /* Operator.Word */ .highlight .w { color: #bbbbbb } /* Text.Whitespace */ .highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */ .highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */ .highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */ .highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */ .highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */ .highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */ .highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */ .highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */ .highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */ .highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */ .highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */ .highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */ .highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */ .highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */ .highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */ .highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */ .highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */ .highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */ .highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */ .highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */ .highlight .vc { color: #336699 } /* Name.Variable.Class */ .highlight .vg { color: #dd7700 } /* Name.Variable.Global */ .highlight .vi { color: #3333bb } /* Name.Variable.Instance */ .highlight .vm { color: #336699 } /* Name.Variable.Magic */ .highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
/* $Id$
 * $URL$
 *
 * driver for picoLCD displays from mini-box.com
 *
 * Copyright (C) 2005 Michael Reinelt <michael@reinelt.co.at>
 * Copyright (C) 2005, 2006, 2007 The LCD4Linux Team <lcd4linux-devel@users.sourceforge.net>
 *
 * Copyright (C) 2007 Nicu Pavel, Mini-Box.com <npavel@mini-box.com>
 *
 * 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_picoLCD
 *
 */

#include "config.h"

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <termios.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/time.h>

#include <usb.h>

#include "debug.h"
#include "cfg.h"
#include "qprintf.h"
#include "udelay.h"
#include "plugin.h"
#include "widget.h"
#include "widget_text.h"
#include "widget_icon.h"
#include "widget_bar.h"
#include "drv.h"
#include "drv_generic_text.h"
#include "drv_generic_gpio.h"
#include "drv_generic_keypad.h"



#define picoLCD_VENDOR  0x04d8
#define picoLCD_DEVICE  0x0002

static char Name[] = "picoLCD";

static unsigned int gpo = 0;

static char *Buffer;
static char *BufPtr;

static usb_dev_handle *lcd;



/****************************************/
/***  hardware dependant functions    ***/
/****************************************/

static int drv_pL_open(void)
{
    struct usb_bus *busses, *bus;
    struct usb_device *dev;
    char driver[1024];
    char product[1024];
    char manufacturer[1024];
    char serialnumber[1024];
    int ret;

    lcd = NULL;

    info("%s: scanning for picoLCD...", Name);

    usb_set_debug(0);

    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 == picoLCD_VENDOR) && (dev->descriptor.idProduct == picoLCD_DEVICE)) {

		info("%s: found picoLCD on bus %s device %s", Name, bus->dirname, dev->filename);

		lcd = usb_open(dev);

		ret = usb_get_driver_np(lcd, 0, driver, sizeof(driver));

		if (ret == 0) {
		    info("%s: interface 0 already claimed by '%s'", Name, driver);
		    info("%s: attempting to detach driver...", Name);
		    if (usb_detach_kernel_driver_np(lcd, 0) < 0) {
			error("%s: usb_detach_kernel_driver_np() failed!", Name);
			return -1;
		    }
		}

		usb_set_configuration(lcd, 1);
		usleep(100);

		if (usb_claim_interface(lcd, 0) < 0) {
		    error("%s: usb_claim_interface() failed!", Name);
		    return -1;
		}

		usb_set_altinterface(lcd, 0);

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

		info("%s: Manufacturer='%s' Product='%s' SerialNumber='%s'", Name, manufacturer, product, serialnumber);

		return 0;
	    }
	}
    }
    error("%s: could not find a picoLCD", Name);
    return -1;
}


static int drv_pL_close(void)
{
    usb_release_interface(lcd, 0);
    usb_close(lcd);

    return 0;
}


static void drv_pL_send(unsigned char *data, int size)
{
    usb_interrupt_write(lcd, USB_ENDPOINT_OUT + 1, (char *) data, size, 1000);
}

static int drv_pL_read(unsigned char *data, int size)
{
    return usb_interrupt_read(lcd, USB_ENDPOINT_OUT + 1, (char *) data, size, 1000);
}



static void drv_pL_clear(void)
{
    unsigned char cmd[1] = { 0x94 };	/* clear display */
    drv_pL_send(cmd, 1);
}

static int drv_pL_contrast(int contrast)
{
    unsigned char cmd[2] = { 0x92 };	/* set contrast */

    if (contrast < 0)
	contrast = 0;
    if (contrast > 255)
	contrast = 255;

    cmd[1] = contrast;
    drv_pL_send(cmd, 2);

    return contrast;
}


static int drv_pL_backlight(int backlight)
{
    unsigned char cmd[2] = { 0x91 };	/* set backlight */

    if (backlight < 0)
	backlight = 0;
    if (backlight > 1)
	backlight = 1;

    cmd[1] = backlight;
    drv_pL_send(cmd, 2);

    return backlight;
}

#define _USBLCD_MAX_DATA_LEN          24
#define IN_REPORT_KEY_STATE           0x11
static int drv_pL_gpi( __attribute__ ((unused))
		      int num)
{
    int ret;
    unsigned char read_packet[_USBLCD_MAX_DATA_LEN];
    ret = drv_pL_read(read_packet, _USBLCD_MAX_DATA_LEN);
    if ((ret > 0) && (read_packet[0] == IN_REPORT_KEY_STATE)) {
	debug("picoLCD: pressed key= 0x%02x\n", read_packet[1]);
	return read_packet[1];
    }
    return 0;
}

static int drv_pL_gpo(int num, int val)
{
    unsigned char cmd[2] = { 0x81 };	/* set GPO */

    if (num < 0)
	num = 0;
    if (num > 7)
	num = 7;

    if (val < 0)
	val = 0;
    if (val > 1)
	val = 1;

    /* set led bit to 1 or 0 */
    if (val)
	gpo |= 1 << num;
    else
	gpo &= ~(1 << num);

    cmd[1] = gpo;
    drv_pL_send(cmd, 2);

    return val;
}


static void drv_pL_write(const int row, const int col, const char *data, int len)
{
    unsigned char cmd[64];
    int i;

    cmd[0] = 0x98;		/* goto/write */
    cmd[1] = row;
    cmd[2] = col;
    cmd[3] = len;

    i = 4;
    while (len--) {
	cmd[i++] = *data++;
    }

    drv_pL_send(cmd, i);
}

static void drv_pL_defchar(const int ascii, const unsigned char *matrix)
{
    unsigned char cmd[10] = { 0x9c };	/* define character */
    int i;

    cmd[1] = ascii;
    for (i = 0; i < 8; i++) {
	cmd[i + 2] = *matrix++ & 0x1f;
    }

    drv_pL_send(cmd, 10);
}


static int drv_pL_start(const char *section, const int quiet)
{
    int rows = -1, cols = -1;
    int value;
    char *s;

    s = cfg_get(section, "Size", NULL);
    if (s == NULL || *s == '\0') {
	error("%s: no '%s.Size' entry from %s", Name, section, cfg_source());
	return -1;
    }
    if (sscanf(s, "%dx%d", &cols, &rows) != 2 || rows < 1 || cols < 1) {
	error("%s: bad %s.Size '%s' from %s", Name, section, s, cfg_source());
	free(s);
	return -1;
    }

    DROWS = rows;
    DCOLS = cols;

    if (drv_pL_open() < 0) {
	return -1;
    }

    /* Init the command buffer */
    Buffer = (char *) malloc(1024);
    if (Buffer == NULL) {
	error("%s: coommand buffer could not be allocated: malloc() failed", Name);
	return -1;
    }
    BufPtr = Buffer;

    if (cfg_number(section, "Contrast", 0, 0, 255, &value) > 0) {
	info("Setting contrast to %d", value);
	drv_pL_contrast(value);
    }

    if (cfg_number(section, "Backlight", 0, 0, 1, &value) > 0) {
	info("Setting backlight to %d", value);
	drv_pL_backlight(value);
    }

    drv_pL_clear();		/* clear display */

    if (!quiet) {
	char buffer[40];
	qprintf(buffer, sizeof(buffer), "%s %dx%d", Name, DCOLS, DROWS);
	if (drv_generic_text_greet(buffer, "http://www.picolcd.com")) {
	    sleep(3);
	    drv_pL_clear();
	}
    }

    return 0;
}


/****************************************/
/***            plugins               ***/
/****************************************/

static void plugin_contrast(RESULT * result, RESULT * arg1)
{
    double contrast;

    contrast = drv_pL_contrast(R2N(arg1));
    SetResult(&result, R_NUMBER, &contrast);
}

static void plugin_backlight(RESULT * result, RESULT * arg1)
{
    double backlight;

    backlight = drv_pL_backlight(R2N(arg1));
    SetResult(&result, R_NUMBER, &backlight);
}

static void plugin_gpo(RESULT * result, RESULT * argv[])
{
    double gpo;
    gpo = drv_pL_gpo(R2N(argv[0]), R2N(argv[1]));
    SetResult(&result, R_NUMBER, &gpo);
}

/****************************************/
/***        widget callbacks          ***/
/****************************************/


/* using drv_generic_text_draw(W) */
/* using drv_generic_text_icon_draw(W) */
/* using drv_generic_text_bar_draw(W) */


/****************************************/
/***        exported functions        ***/
/****************************************/


/* list models */
int drv_pL_list(void)
{
    printf("picoLCD 20x2 Text LCD");
    return 0;
}


/* initialize driver & display */
int drv_pL_init(const char *section, const int quiet)
{
    WIDGET_CLASS wc;
    int ret;

    info("%s: %s", Name, "$Rev$");

    /* display preferences */
    XRES = 5;			/* pixel width of one char  */
    YRES = 8;			/* pixel height of one char  */
    CHARS = 8;			/* number of user-defineable characters */
    CHAR0 = 0;			/* ASCII of first user-defineable char */
    GPOS = 8;
    GPIS = 1;
    INVALIDATE = 1;
    GOTO_COST = 2;		/* number of bytes a goto command requires */

    /* real worker functions */
    drv_generic_text_real_write = drv_pL_write;
    drv_generic_text_real_defchar = drv_pL_defchar;
    drv_generic_gpio_real_set = drv_pL_gpo;
    drv_generic_gpio_real_get = drv_pL_gpi;

    /* start display */
    if ((ret = drv_pL_start(section, quiet)) != 0)
	return ret;

    /* initialize generic text driver */
    if ((ret = drv_generic_text_init(section, Name)) != 0)
	return ret;

    /* initialize generic icon driver */
    if ((ret = drv_generic_text_icon_init()) != 0)
	return ret;

    /* initialize generic bar driver */
    if ((ret = drv_generic_text_bar_init(0)) != 0)
	return ret;

    drv_generic_text_bar_add_segment(0, 0, 255, 32);

    /* GPO's init */
    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);

    /* register icon widget */
    wc = Widget_Icon;
    wc.draw = drv_generic_text_icon_draw;
    widget_register(&wc);

    /* register bar widget */
    wc = Widget_Bar;
    wc.draw = drv_generic_text_bar_draw;
    widget_register(&wc);

    /* register plugins */
    AddFunction("LCD::contrast", -1, plugin_contrast);
    AddFunction("LCD::backlight", -1, plugin_backlight);
    AddFunction("LCD::gpo", -1, plugin_gpo);

    return 0;
}


/* close driver & display */
int drv_pL_quit(const int quiet)
{

    info("%s: shutting down.", Name);

    drv_generic_text_quit();

    /* clear display */
    drv_pL_clear();

    /* say goodbye... */
    if (!quiet) {
	drv_generic_text_greet("goodbye!", NULL);
    }

    drv_pL_close();

    if (Buffer) {
	free(Buffer);
	Buffer = NULL;
	BufPtr = Buffer;
    }

    return (0);
}


DRIVER drv_picoLCD = {
    .name = Name,
    .list = drv_pL_list,
    .init = drv_pL_init,
    .quit = drv_pL_quit,
};