1562 lines
38 KiB
C
1562 lines
38 KiB
C
/*
|
|
* LIS driver for lcdproc
|
|
* For the L.I.S MCE vacuum fluorescent display from vlsys.co.kr
|
|
* 20x2 display using NEC UPD16314 driver and FTDI FT232RQ USB-to-serial
|
|
* converter.
|
|
*
|
|
* This driver uses the libftdi library to interface to the FTDI chip. Make
|
|
* sure kernel module ftdi_sio doesn't claim the device.
|
|
*
|
|
* Based on: ula200 driver Copyright (C) 2006, Bernhard Walle
|
|
* IOWarrior driver Copyright(C) 2004-2006 Peter Marschall <peter@adpm.de>
|
|
*
|
|
* Copyright (c) 2007, Daryl Fonseca-Holt <wyatt@prairieturtle.ca>
|
|
*
|
|
* This file is released under the GNU General Public License. Refer to the
|
|
* COPYING file distributed with this package.
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*/
|
|
|
|
/*
|
|
* Notes on this driver
|
|
*
|
|
* vlsys is unwilling to provide information about the protocol for this
|
|
* device. This driver was developed entirely by analyzing the USB data
|
|
* flowing between the host and the device on a WinXP machine. As such it
|
|
* probably isn't an optimal driver but it works.
|
|
*
|
|
* Using a dirty buffer/clean buffer approach was useless since I could
|
|
* not determine how to write to specific columns. The only thing selectable
|
|
* was which line would be written at column 1.
|
|
*/
|
|
|
|
/* CHANGES:
|
|
*
|
|
* 2007/05/22 Removed useless Device= config option.
|
|
* Added VendorID= and ProductID= config
|
|
* option parsting. Moved constants to
|
|
* lis.h where they belong. Added
|
|
* lis_cellheight() and lis_cellwidth().
|
|
* Completely commented out lis_test() to
|
|
* save memory.
|
|
* 2007/05/30 Remove set_custom_chars(). Implement
|
|
* lis_set_chars(), lis_vbar(), lis_hbar()
|
|
* and lis_num() using helper functions.
|
|
* 2007/11/01 Change Linux-centric clone() to POSIX
|
|
* threads for portability.
|
|
*/
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
#include <termios.h>
|
|
#include <fcntl.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <syslog.h>
|
|
#include <pthread.h>
|
|
|
|
#include <usb.h>
|
|
#include <ftdi.h>
|
|
|
|
#include <sys/syscall.h>
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
# include "config.h"
|
|
#endif
|
|
|
|
#include "lcd.h"
|
|
#include "lis.h"
|
|
#include "report.h"
|
|
#include "lcd_lib.h"
|
|
#include "adv_bignum.h"
|
|
#include "timing.h"
|
|
|
|
//void lis_test(Driver *drvthis);
|
|
|
|
/* Vars for the server core */
|
|
MODULE_EXPORT char *api_version = API_VERSION;
|
|
MODULE_EXPORT int stay_in_foreground = 0;
|
|
MODULE_EXPORT int supports_multiple = 1;
|
|
MODULE_EXPORT char *symbol_prefix = "lis_";
|
|
|
|
|
|
/*
|
|
* Charactar mapping for UPD16314 device by Daryl Fonseca-Holt
|
|
* <wyatt@prairieturtle.ca> taken from Mark Haemmerling's HD44780 table.
|
|
* Character mapping for HD44780 devices by Mark Haemmerling <mail@markh.de>.
|
|
*
|
|
* Translates ISO 8859-1 to any HD44780 charset.
|
|
*
|
|
* Charmap selector (C) 2006 Pillon Matteo <matteo.pillon@email.it>
|
|
*
|
|
* This file is released under the GNU General Public License.
|
|
* Refer to the COPYING file distributed with this package.
|
|
*/
|
|
|
|
/*
|
|
* UPD16314 table
|
|
*
|
|
*/
|
|
|
|
const unsigned char UPD16314_charmap[] = {
|
|
/* #0 */
|
|
0, 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 */
|
|
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 */
|
|
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, 47, 93, 94, 95,
|
|
/* #96 */
|
|
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 */
|
|
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 */
|
|
160, 33, 236, 237, 164, 92, 124, 167,
|
|
168, 169, 170, 171, 172, 173, 174, 175,
|
|
176, 177, 178, 179, 180, 181, 182, 183,
|
|
184, 185, 186, 187, 188, 189, 190, 191,
|
|
/* #192 */
|
|
192, 193, 194, 195, 196, 197, 198, 199,
|
|
200, 201, 202, 203, 204, 205, 206, 207,
|
|
208, 209, 210, 211, 212, 213, 214, 215,
|
|
216, 217, 218, 219, 220, 221, 223, 224,
|
|
/* #224 */
|
|
224, 225, 226, 227, 228, 229, 230, 231,
|
|
232, 233, 234, 235, 237, 237, 238, 239,
|
|
240, 241, 242, 243, 244, 245, 246, 247,
|
|
248, 249, 250, 251, 252, 253, 254, 1
|
|
};
|
|
|
|
/////////////////////////////////////////
|
|
// Cache standard custom chars
|
|
void
|
|
lis_standard_custom_chars (Driver *drvthis) {
|
|
|
|
PrivateData *p = drvthis->private_data;
|
|
|
|
static unsigned char checkbox_gray[] =
|
|
{ b_______,
|
|
b_______,
|
|
b__XXXXX,
|
|
b__X_X_X,
|
|
b__XX_XX,
|
|
b__X_X_X,
|
|
b__XXXXX,
|
|
b_______ };
|
|
|
|
static unsigned char block_filled[] =
|
|
{ b__XXXXX,
|
|
b__XXXXX,
|
|
b__XXXXX,
|
|
b__XXXXX,
|
|
b__XXXXX,
|
|
b__XXXXX,
|
|
b__XXXXX,
|
|
b__XXXXX };
|
|
|
|
lis_set_char(drvthis, 1, block_filled);
|
|
lis_set_char(drvthis, 2, checkbox_gray);
|
|
|
|
p->ccmode = standard;
|
|
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Write a command to the display.
|
|
//
|
|
// @param p the private data
|
|
// @param data the data bytes
|
|
// @param length the number of bytes in @p data which are valid
|
|
// @return 0 on success, negative value on error
|
|
//
|
|
static int
|
|
lis_ftdi_write_command(Driver *drvthis, unsigned char *data, int length)
|
|
{
|
|
PrivateData *p = (PrivateData *) drvthis->private_data;
|
|
int err;
|
|
|
|
err = ftdi_write_data(&p->ftdic, data, length);
|
|
if (err < 0) {
|
|
report(RPT_WARNING, "%s: ftdi_write_data failed with %d", drvthis->name, err);
|
|
return -1;
|
|
}
|
|
|
|
timing_uPause(16000);
|
|
|
|
return 0;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// Displays a string at line n (n typically 1 or 2)
|
|
//
|
|
static int
|
|
lis_ftdi_line_to_display(Driver *drvthis, int line, unsigned char *string, int len)
|
|
{
|
|
PrivateData *p = (PrivateData *) drvthis->private_data;
|
|
unsigned char buffer[128];
|
|
int err;
|
|
int i;
|
|
|
|
if (len > p->width || line < 1 || line > p->height) {
|
|
return -EINVAL;
|
|
}
|
|
buffer[0] = 0xA0 + line;
|
|
buffer[1] = 0;
|
|
buffer[2] = 0xA7;
|
|
|
|
for (i=0; i < len; i++) {
|
|
buffer[i+3] = UPD16314_charmap[(unsigned char)string[i]];
|
|
}
|
|
buffer[i+3] = 0x00;
|
|
|
|
err = lis_ftdi_write_command(drvthis, buffer, len+4);
|
|
if (err < 0) {
|
|
report(RPT_WARNING, "%s: lis_ftdi_string: "
|
|
"lis_ftdi_write_command() failed", drvthis->name);
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Flush the framebuffer to the display
|
|
//
|
|
MODULE_EXPORT void
|
|
lis_flush(Driver *drvthis)
|
|
{
|
|
PrivateData *p = (PrivateData *) drvthis->private_data;
|
|
|
|
int line, err, i, count;
|
|
unsigned char buffer[65];
|
|
|
|
// see if any custom characters changed
|
|
for (i = 0, count = 0; i < NUM_CCs; i++) {
|
|
if ( ! p->cc[i].clean ) {
|
|
count++;
|
|
p->cc[i].clean++; // mark clean
|
|
}
|
|
}
|
|
if (count) {
|
|
// flush custom characters to device
|
|
buffer[0] = 0xAD; // command to write CGRAM at 0
|
|
for (i = 0; i < NUM_CCs; i++) {
|
|
memcpy(buffer + 1 + (i*CELLHEIGHT), p->cc[i].cache, CELLHEIGHT);
|
|
}
|
|
err = lis_ftdi_write_command(drvthis, buffer, sizeof(buffer));
|
|
if (err < 0) {
|
|
report(RPT_WARNING, "%s: lis_flush(): "
|
|
"lis_ftdi_write_command() failed", drvthis->name);
|
|
}
|
|
report(RPT_DEBUG, "Flushed %d custom chars that changed", count);
|
|
timing_uPause(16000);
|
|
}
|
|
|
|
// write any line that has a change in it
|
|
for (line = 0; line < p->height; line++) {
|
|
if (p->line_flags[line]) {
|
|
report(RPT_DEBUG, "Flushing line %d", line+1);
|
|
lis_ftdi_line_to_display(drvthis, line+1, p->framebuf + (line * p->width), p->width);
|
|
p->line_flags[line] = 0; // clean
|
|
timing_uPause(16000);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// Separate thread to keep a read up on the USB device at all times
|
|
int
|
|
lis_read_thread(void *arg)
|
|
{
|
|
Driver *drvthis;
|
|
PrivateData *p;
|
|
char unsigned buffer[64];
|
|
int size;
|
|
|
|
drvthis = (Driver *)arg;
|
|
p = (PrivateData *) drvthis->private_data;
|
|
|
|
while(! p->child_flag) {
|
|
for (size = ftdi_read_data(&p->ftdic, buffer, 64); size > 0; size = ftdi_read_data(&p->ftdic, buffer, 64))
|
|
;
|
|
if (size < 0) {
|
|
return size;
|
|
p->parent_flag = 0;
|
|
}
|
|
}
|
|
p->parent_flag = 0;
|
|
return 0;
|
|
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Control the display brightness
|
|
//
|
|
MODULE_EXPORT int
|
|
lis_set_brightness(Driver *drvthis, int state, int promille)
|
|
{
|
|
unsigned char buffer[2];
|
|
int err;
|
|
PrivateData *p = (PrivateData *) drvthis->private_data;
|
|
|
|
if (promille < 0 || promille > 1000) {
|
|
report(RPT_WARNING, "%s: invalid brightness %d less then 0 or greater than 1000",
|
|
drvthis->name, promille);
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
buffer[0] = 0xA5;
|
|
if ( promille < 251 )
|
|
buffer[1] = 0x3; // 25%
|
|
else if ( promille < 501 )
|
|
buffer[1] = 0x2; // 50%
|
|
else if ( promille < 751 )
|
|
buffer[1] = 0x1; // 75%
|
|
else
|
|
buffer[1] = 0x0; // 100%
|
|
|
|
err = ftdi_write_data(&p->ftdic, buffer, 2);
|
|
if (err < 0) {
|
|
report(RPT_WARNING, "%s: lis_set_brightness(): ftdi_write_data failed with %d", drvthis->name, err);
|
|
return err;
|
|
}
|
|
else
|
|
p->brightness = promille;
|
|
|
|
report(RPT_DEBUG, "%s: brightness set to %d",
|
|
drvthis->name, promille);
|
|
|
|
return 0;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Get the brightness setting
|
|
MODULE_EXPORT int
|
|
lis_get_brightness(Driver *drvthis, int state)
|
|
{
|
|
PrivateData *p = (PrivateData *) drvthis->private_data;
|
|
|
|
report(RPT_DEBUG, "%s: brightness query returns %d",
|
|
drvthis->name, p->brightness);
|
|
|
|
return p->brightness;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Init the driver and display
|
|
//
|
|
MODULE_EXPORT int
|
|
lis_init(Driver *drvthis)
|
|
{
|
|
PrivateData *p;
|
|
int err;
|
|
const char *s;
|
|
unsigned char buffer[64];
|
|
int count;
|
|
pthread_t read_thread;
|
|
|
|
report(RPT_DEBUG, "%s: Initializing driver",
|
|
drvthis->name);
|
|
// Alocate and store private data
|
|
p = (PrivateData *) malloc( sizeof( PrivateData) );
|
|
if (p == NULL) {
|
|
return -1;
|
|
}
|
|
if (drvthis->store_private_ptr(drvthis, p)) {
|
|
return -1;
|
|
}
|
|
|
|
p->child_flag = 0;
|
|
p->parent_flag = 0;
|
|
p->cellwidth = CELLWIDTH;
|
|
p->cellheight = CELLHEIGHT;
|
|
|
|
// Get and parse size
|
|
s = drvthis->config_get_string( drvthis->name, "size", 0, "20x2");
|
|
if ((sscanf(s, "%dx%d", &(p->width), &(p->height)) != 2)
|
|
|| (p->width <= 0) || (p->width > LCD_MAX_WIDTH)
|
|
|| (p->height <= 0) || (p->height > LCD_MAX_HEIGHT)) {
|
|
report(RPT_ERR, "%s: cannot read Size %s", drvthis->name, s);
|
|
return -1;
|
|
}
|
|
|
|
// set up the dirty flags
|
|
if( ! (p->line_flags = (unsigned int *)malloc(sizeof(int) * p->height))) {
|
|
report(RPT_ERR, "%s: cannot allocate line flags memory", drvthis->name);
|
|
return -1;
|
|
}
|
|
for (count = 0; count < p->height; count++)
|
|
p->line_flags[count] = 1; // dirty!
|
|
|
|
/* Which display brightness */
|
|
count = drvthis->config_get_int(drvthis->name, "Brightness", 0, DEFAULT_BRIGHTNESS);
|
|
if ((count < 0) || (count > 1000)) {
|
|
report(RPT_WARNING, "%s: Brightness must be between 0 and 1000; using default %d",
|
|
drvthis->name, DEFAULT_BRIGHTNESS);
|
|
count = DEFAULT_BRIGHTNESS;
|
|
}
|
|
p->brightness = count;
|
|
|
|
p->VendorID = drvthis->config_get_int(drvthis->name, "VendorID", 0, DISPLAY_VENDOR_ID);
|
|
p->ProductID = drvthis->config_get_int(drvthis->name, "ProductID", 0, DISPLAY_PRODUCT_ID);
|
|
|
|
/* End of config file parsing */
|
|
|
|
|
|
// initialise delay timing
|
|
if (timing_init()) {
|
|
report(RPT_ERR, "%s: cannot initialise delay timing", drvthis->name);
|
|
return -1;
|
|
}
|
|
// Allocate framebuffer
|
|
p->framebuf = (unsigned char *) malloc(p->width * p->height);
|
|
if (p->framebuf == NULL) {
|
|
report(RPT_ERR, "%s: unable to allocate framebuffer", drvthis->name);
|
|
goto err_begin;
|
|
}
|
|
|
|
// open the FTDI library
|
|
ftdi_init(&p->ftdic);
|
|
// (&p->ftdic)->usb_write_timeout = 20;
|
|
// (&p->ftdic)->usb_read_timeout = 20;
|
|
|
|
// open the device
|
|
err = ftdi_usb_open(&p->ftdic, p->VendorID, p->ProductID);
|
|
if (err < 0) {
|
|
report(RPT_ERR, "%s: cannot open USB device %x:%x",
|
|
drvthis->name,
|
|
p->VendorID,
|
|
p->ProductID);
|
|
goto err_framebuf;
|
|
}
|
|
|
|
// set communication parameters
|
|
err = ftdi_set_line_property(&p->ftdic, BITS_7, STOP_BIT_1, EVEN);
|
|
if (err < 0) {
|
|
report(RPT_ERR, "%s: cannot set line properties", drvthis->name);
|
|
goto err_ftdi;
|
|
}
|
|
|
|
// set DTR mask and high
|
|
err = ftdi_setdtr(&p->ftdic, SIO_SET_DTR_MASK | SIO_SET_DTR_HIGH);
|
|
if (err < 0) {
|
|
report(RPT_ERR, "%s: cannot set DTR mask", drvthis->name);
|
|
goto err_ftdi;
|
|
}
|
|
|
|
// set RTS mask and high
|
|
err = ftdi_setrts(&p->ftdic, SIO_SET_RTS_MASK | SIO_SET_RTS_HIGH);
|
|
if (err < 0) {
|
|
report(RPT_ERR, "%s: cannot set RTS mask", drvthis->name);
|
|
goto err_ftdi;
|
|
}
|
|
|
|
// set the baudrate
|
|
err = ftdi_set_baudrate(&p->ftdic, 1200);
|
|
if (err < 0) {
|
|
report(RPT_ERR, "%s: cannot set baudrate", drvthis->name);
|
|
goto err_ftdi;
|
|
}
|
|
|
|
// set RTS high
|
|
err = ftdi_setrts(&p->ftdic, SIO_SET_RTS_HIGH);
|
|
if (err < 0) {
|
|
report(RPT_ERR, "%s: cannot set RTS high", drvthis->name);
|
|
goto err_ftdi;
|
|
}
|
|
|
|
// set DTR high
|
|
err = ftdi_setdtr(&p->ftdic, SIO_SET_DTR_HIGH);
|
|
if (err < 0) {
|
|
report(RPT_ERR, "%s: cannot set DTR high", drvthis->name);
|
|
goto err_ftdi;
|
|
}
|
|
|
|
for (count = 0; count < 6; count++) {
|
|
// reset device
|
|
err = ftdi_usb_reset(&p->ftdic);
|
|
if (err < 0) {
|
|
report(RPT_ERR, "%s: cannot reset device", drvthis->name);
|
|
goto err_ftdi;
|
|
}
|
|
}
|
|
// set latency timer
|
|
err = ftdi_set_latency_timer(&p->ftdic, 16);
|
|
if (err < 0) {
|
|
report(RPT_ERR, "%s: cannot set latency timer", drvthis->name);
|
|
goto err_ftdi;
|
|
}
|
|
|
|
// create a thread to keep a read up on the device
|
|
err = pthread_create( &read_thread,
|
|
NULL,
|
|
(void *) lis_read_thread,
|
|
drvthis
|
|
);
|
|
if (err) {
|
|
report(RPT_ERR, "%s: pthread_create() - %s", drvthis->name, err);
|
|
goto err_framebuf;
|
|
}
|
|
p->parent_flag = 1; // show we're now a happy parent, birth successful.
|
|
|
|
// set communication parameters
|
|
err = ftdi_set_line_property(&p->ftdic, BITS_7, STOP_BIT_1, EVEN);
|
|
if (err < 0) {
|
|
report(RPT_ERR, "%s: cannot set line properties", drvthis->name);
|
|
goto err_ftdi;
|
|
}
|
|
|
|
// set DTR mask and high
|
|
err = ftdi_setdtr(&p->ftdic, SIO_SET_DTR_MASK | SIO_SET_DTR_HIGH);
|
|
if (err < 0) {
|
|
report(RPT_ERR, "%s: cannot set DTR mask", drvthis->name);
|
|
goto err_ftdi;
|
|
}
|
|
|
|
// set RTS mask and high
|
|
err = ftdi_setrts(&p->ftdic, SIO_SET_RTS_MASK | SIO_SET_RTS_HIGH);
|
|
if (err < 0) {
|
|
report(RPT_ERR, "%s: cannot set RTS mask", drvthis->name);
|
|
goto err_ftdi;
|
|
}
|
|
|
|
// disable flow control
|
|
err = ftdi_setflowctrl(&p->ftdic, SIO_DISABLE_FLOW_CTRL);
|
|
if (err < 0) {
|
|
report(RPT_ERR, "%s: cannot disable flow control", drvthis->name);
|
|
goto err_ftdi;
|
|
}
|
|
|
|
// set the baudrate
|
|
err = ftdi_set_baudrate(&p->ftdic, 1200);
|
|
if (err < 0) {
|
|
report(RPT_ERR, "%s: cannot set baudrate", drvthis->name);
|
|
goto err_ftdi;
|
|
}
|
|
|
|
// set RTS mask and high
|
|
err = ftdi_setrts(&p->ftdic, SIO_SET_RTS_MASK | SIO_SET_RTS_HIGH);
|
|
if (err < 0) {
|
|
report(RPT_ERR, "%s: cannot set RTS mask", drvthis->name);
|
|
goto err_ftdi;
|
|
}
|
|
|
|
// set DTR mask and high
|
|
err = ftdi_setdtr(&p->ftdic, SIO_SET_DTR_MASK | SIO_SET_DTR_HIGH);
|
|
if (err < 0) {
|
|
report(RPT_ERR, "%s: cannot set DTR mask", drvthis->name);
|
|
goto err_ftdi;
|
|
}
|
|
|
|
// disable flow control
|
|
err = ftdi_setflowctrl(&p->ftdic, SIO_DISABLE_FLOW_CTRL);
|
|
if (err < 0) {
|
|
report(RPT_ERR, "%s: cannot disable flow control", drvthis->name);
|
|
goto err_ftdi;
|
|
}
|
|
|
|
// set DTR mask and high
|
|
err = ftdi_setdtr(&p->ftdic, SIO_SET_DTR_MASK | SIO_SET_DTR_HIGH);
|
|
if (err < 0) {
|
|
report(RPT_ERR, "%s: cannot set DTR mask", drvthis->name);
|
|
goto err_ftdi;
|
|
}
|
|
|
|
// set RTS high
|
|
err = ftdi_setrts(&p->ftdic, SIO_SET_RTS_HIGH);
|
|
if (err < 0) {
|
|
report(RPT_ERR, "%s: cannot set RTS high", drvthis->name);
|
|
goto err_ftdi;
|
|
}
|
|
|
|
timing_uPause(192000);
|
|
|
|
// set the baudrate
|
|
err = ftdi_set_baudrate(&p->ftdic, 1200);
|
|
if (err < 0) {
|
|
report(RPT_ERR, "%s: cannot set baudrate", drvthis->name);
|
|
goto err_ftdi;
|
|
}
|
|
|
|
// set communication parameters
|
|
err = ftdi_set_line_property(&p->ftdic, BITS_7, STOP_BIT_1, NONE);
|
|
if (err < 0) {
|
|
report(RPT_ERR, "%s: cannot set line properties", drvthis->name);
|
|
goto err_ftdi;
|
|
}
|
|
|
|
// set DTR high
|
|
err = ftdi_setdtr(&p->ftdic, SIO_SET_DTR_HIGH);
|
|
if (err < 0) {
|
|
report(RPT_ERR, "%s: cannot set DTR high", drvthis->name);
|
|
goto err_ftdi;
|
|
}
|
|
|
|
// set RTS high
|
|
err = ftdi_setrts(&p->ftdic, SIO_SET_RTS_HIGH);
|
|
if (err < 0) {
|
|
report(RPT_ERR, "%s: cannot set RTS high", drvthis->name);
|
|
goto err_ftdi;
|
|
}
|
|
|
|
timing_uPause(192000);
|
|
|
|
// set DTR mask and high
|
|
err = ftdi_setdtr(&p->ftdic, SIO_SET_DTR_MASK | SIO_SET_DTR_HIGH);
|
|
if (err < 0) {
|
|
report(RPT_ERR, "%s: cannot set DTR mask", drvthis->name);
|
|
goto err_ftdi;
|
|
}
|
|
|
|
timing_uPause(192000);
|
|
|
|
// set RTS mask and high
|
|
err = ftdi_setrts(&p->ftdic, SIO_SET_RTS_MASK | SIO_SET_RTS_HIGH);
|
|
if (err < 0) {
|
|
report(RPT_ERR, "%s: cannot set RTS mask", drvthis->name);
|
|
goto err_ftdi;
|
|
}
|
|
|
|
timing_uPause(236000);
|
|
|
|
// set DTR high
|
|
err = ftdi_setdtr(&p->ftdic, SIO_SET_DTR_HIGH);
|
|
if (err < 0) {
|
|
report(RPT_ERR, "%s: cannot set DTR high", drvthis->name);
|
|
goto err_ftdi;
|
|
}
|
|
|
|
// set RTS high
|
|
err = ftdi_setrts(&p->ftdic, SIO_SET_RTS_HIGH);
|
|
if (err < 0) {
|
|
report(RPT_ERR, "%s: cannot set RTS high", drvthis->name);
|
|
goto err_ftdi;
|
|
}
|
|
|
|
timing_uPause(192000);
|
|
|
|
// set DTR mask and high
|
|
err = ftdi_setdtr(&p->ftdic, SIO_SET_DTR_MASK | SIO_SET_DTR_HIGH);
|
|
if (err < 0) {
|
|
report(RPT_ERR, "%s: cannot set DTR mask", drvthis->name);
|
|
goto err_ftdi;
|
|
}
|
|
|
|
// set RTS mask and high
|
|
err = ftdi_setrts(&p->ftdic, SIO_SET_RTS_MASK | SIO_SET_RTS_HIGH);
|
|
if (err < 0) {
|
|
report(RPT_ERR, "%s: cannot set RTS mask", drvthis->name);
|
|
goto err_ftdi;
|
|
}
|
|
|
|
timing_uPause(236000);
|
|
|
|
// set DTR mask and high
|
|
err = ftdi_setdtr(&p->ftdic, SIO_SET_DTR_MASK | SIO_SET_DTR_HIGH);
|
|
if (err < 0) {
|
|
report(RPT_ERR, "%s: cannot set DTR mask", drvthis->name);
|
|
goto err_ftdi;
|
|
}
|
|
|
|
// set RTS high
|
|
err = ftdi_setrts(&p->ftdic, SIO_SET_RTS_HIGH);
|
|
if (err < 0) {
|
|
report(RPT_ERR, "%s: cannot set RTS high", drvthis->name);
|
|
goto err_ftdi;
|
|
}
|
|
|
|
timing_uPause(192000);
|
|
|
|
// set the baudrate
|
|
err = ftdi_set_baudrate(&p->ftdic, 1200);
|
|
if (err < 0) {
|
|
report(RPT_ERR, "%s: cannot set baudrate", drvthis->name);
|
|
goto err_ftdi;
|
|
}
|
|
|
|
// set communication parameters
|
|
err = ftdi_set_line_property(&p->ftdic, BITS_7, STOP_BIT_1, NONE);
|
|
if (err < 0) {
|
|
report(RPT_ERR, "%s: cannot set line properties", drvthis->name);
|
|
goto err_ftdi;
|
|
}
|
|
|
|
// set DTR high
|
|
err = ftdi_setdtr(&p->ftdic, SIO_SET_DTR_HIGH);
|
|
if (err < 0) {
|
|
report(RPT_ERR, "%s: cannot set DTR high", drvthis->name);
|
|
goto err_ftdi;
|
|
}
|
|
|
|
// set RTS high
|
|
err = ftdi_setrts(&p->ftdic, SIO_SET_RTS_HIGH);
|
|
if (err < 0) {
|
|
report(RPT_ERR, "%s: cannot set RTS high", drvthis->name);
|
|
goto err_ftdi;
|
|
}
|
|
|
|
timing_uPause(192000);
|
|
|
|
// set DTR mask and high
|
|
err = ftdi_setdtr(&p->ftdic, SIO_SET_DTR_MASK | SIO_SET_DTR_HIGH);
|
|
if (err < 0) {
|
|
report(RPT_ERR, "%s: cannot set DTR mask", drvthis->name);
|
|
goto err_ftdi;
|
|
}
|
|
|
|
timing_uPause(192000);
|
|
|
|
// set RTS mask and high
|
|
err = ftdi_setrts(&p->ftdic, SIO_SET_RTS_MASK | SIO_SET_RTS_HIGH);
|
|
if (err < 0) {
|
|
report(RPT_ERR, "%s: cannot set RTS mask", drvthis->name);
|
|
goto err_ftdi;
|
|
}
|
|
|
|
timing_uPause(236000);
|
|
|
|
// set DTR high
|
|
err = ftdi_setdtr(&p->ftdic, SIO_SET_DTR_HIGH);
|
|
if (err < 0) {
|
|
report(RPT_ERR, "%s: cannot set DTR high", drvthis->name);
|
|
goto err_ftdi;
|
|
}
|
|
|
|
// set RTS high
|
|
err = ftdi_setrts(&p->ftdic, SIO_SET_RTS_HIGH);
|
|
if (err < 0) {
|
|
report(RPT_ERR, "%s: cannot set RTS high", drvthis->name);
|
|
goto err_ftdi;
|
|
}
|
|
|
|
timing_uPause(236000);
|
|
|
|
// set DTR mask and high
|
|
err = ftdi_setdtr(&p->ftdic, SIO_SET_DTR_MASK | SIO_SET_DTR_HIGH);
|
|
if (err < 0) {
|
|
report(RPT_ERR, "%s: cannot set DTR mask", drvthis->name);
|
|
goto err_ftdi;
|
|
}
|
|
|
|
// set RTS high
|
|
err = ftdi_setrts(&p->ftdic, SIO_SET_RTS_HIGH);
|
|
if (err < 0) {
|
|
report(RPT_ERR, "%s: cannot set RTS high", drvthis->name);
|
|
goto err_ftdi;
|
|
}
|
|
|
|
// set the baudrate
|
|
err = ftdi_set_baudrate(&p->ftdic, 1200);
|
|
if (err < 0) {
|
|
report(RPT_ERR, "%s: cannot set baudrate", drvthis->name);
|
|
goto err_ftdi;
|
|
}
|
|
|
|
// set communication parameters
|
|
err = ftdi_set_line_property(&p->ftdic, BITS_7, STOP_BIT_1, NONE);
|
|
if (err < 0) {
|
|
report(RPT_ERR, "%s: cannot set line properties", drvthis->name);
|
|
goto err_ftdi;
|
|
}
|
|
|
|
// set DTR high
|
|
err = ftdi_setdtr(&p->ftdic, SIO_SET_DTR_HIGH);
|
|
if (err < 0) {
|
|
report(RPT_ERR, "%s: cannot set DTR high", drvthis->name);
|
|
goto err_ftdi;
|
|
}
|
|
|
|
// set RTS high
|
|
err = ftdi_setrts(&p->ftdic, SIO_SET_RTS_HIGH);
|
|
if (err < 0) {
|
|
report(RPT_ERR, "%s: cannot set RTS high", drvthis->name);
|
|
goto err_ftdi;
|
|
}
|
|
|
|
timing_uPause(13*16000);
|
|
|
|
// set DTR mask and high
|
|
err = ftdi_setdtr(&p->ftdic, SIO_SET_DTR_MASK | SIO_SET_DTR_HIGH);
|
|
if (err < 0) {
|
|
report(RPT_ERR, "%s: cannot set DTR mask", drvthis->name);
|
|
goto err_ftdi;
|
|
}
|
|
|
|
timing_uPause(13*16000);
|
|
|
|
// set RTS mask and high
|
|
err = ftdi_setrts(&p->ftdic, SIO_SET_RTS_MASK | SIO_SET_RTS_HIGH);
|
|
if (err < 0) {
|
|
report(RPT_ERR, "%s: cannot set RTS mask", drvthis->name);
|
|
goto err_ftdi;
|
|
}
|
|
|
|
timing_uPause(15*16000);
|
|
|
|
// set DTR high
|
|
err = ftdi_setdtr(&p->ftdic, SIO_SET_DTR_HIGH);
|
|
if (err < 0) {
|
|
report(RPT_ERR, "%s: cannot set DTR high", drvthis->name);
|
|
goto err_ftdi;
|
|
}
|
|
|
|
// set RTS high
|
|
err = ftdi_setrts(&p->ftdic, SIO_SET_RTS_HIGH);
|
|
if (err < 0) {
|
|
report(RPT_ERR, "%s: cannot set RTS high", drvthis->name);
|
|
goto err_ftdi;
|
|
}
|
|
|
|
timing_uPause(13*16000);
|
|
|
|
// set DTR mask and high
|
|
err = ftdi_setdtr(&p->ftdic, SIO_SET_DTR_MASK | SIO_SET_DTR_HIGH);
|
|
if (err < 0) {
|
|
report(RPT_ERR, "%s: cannot set DTR mask", drvthis->name);
|
|
goto err_ftdi;
|
|
}
|
|
|
|
// set RTS mask and high
|
|
err = ftdi_setrts(&p->ftdic, SIO_SET_RTS_MASK | SIO_SET_RTS_HIGH);
|
|
if (err < 0) {
|
|
report(RPT_ERR, "%s: cannot set RTS mask", drvthis->name);
|
|
goto err_ftdi;
|
|
}
|
|
|
|
timing_uPause(16*16000);
|
|
|
|
// set DTR mask and high
|
|
err = ftdi_setdtr(&p->ftdic, SIO_SET_DTR_MASK | SIO_SET_DTR_HIGH);
|
|
if (err < 0) {
|
|
report(RPT_ERR, "%s: cannot set DTR mask", drvthis->name);
|
|
goto err_ftdi;
|
|
}
|
|
|
|
// set RTS mask and high
|
|
err = ftdi_setrts(&p->ftdic, SIO_SET_RTS_MASK | SIO_SET_RTS_HIGH);
|
|
if (err < 0) {
|
|
report(RPT_ERR, "%s: cannot set RTS mask", drvthis->name);
|
|
goto err_ftdi;
|
|
}
|
|
|
|
// disable flow control
|
|
err = ftdi_setflowctrl(&p->ftdic, SIO_DISABLE_FLOW_CTRL);
|
|
if (err < 0) {
|
|
report(RPT_ERR, "%s: cannot disable flow control", drvthis->name);
|
|
goto err_ftdi;
|
|
}
|
|
|
|
timing_uPause(16000);
|
|
|
|
// set DTR high
|
|
err = ftdi_setdtr(&p->ftdic, SIO_SET_DTR_HIGH);
|
|
if (err < 0) {
|
|
report(RPT_ERR, "%s: cannot set DTR high", drvthis->name);
|
|
goto err_ftdi;
|
|
}
|
|
|
|
// set RTS high
|
|
err = ftdi_setrts(&p->ftdic, SIO_SET_RTS_HIGH);
|
|
if (err < 0) {
|
|
report(RPT_ERR, "%s: cannot set RTS high", drvthis->name);
|
|
goto err_ftdi;
|
|
}
|
|
|
|
for (count = 0; count < 6; count++) {
|
|
// reset device
|
|
err = ftdi_usb_reset(&p->ftdic);
|
|
if (err < 0) {
|
|
report(RPT_ERR, "%s: cannot reset device", drvthis->name);
|
|
goto err_ftdi;
|
|
}
|
|
}
|
|
|
|
// set latency timer
|
|
err = ftdi_set_latency_timer(&p->ftdic, 16);
|
|
if (err < 0) {
|
|
report(RPT_ERR, "%s: cannot set latency timer", drvthis->name);
|
|
goto err_ftdi;
|
|
}
|
|
|
|
timing_uPause(16000);
|
|
|
|
// set communication parameters
|
|
err = ftdi_set_line_property(&p->ftdic, BITS_7, STOP_BIT_1, NONE);
|
|
if (err < 0) {
|
|
report(RPT_ERR, "%s: cannot set line properties", drvthis->name);
|
|
goto err_ftdi;
|
|
}
|
|
|
|
// set DTR mask and high
|
|
err = ftdi_setdtr(&p->ftdic, SIO_SET_DTR_MASK | SIO_SET_DTR_HIGH);
|
|
if (err < 0) {
|
|
report(RPT_ERR, "%s: cannot set DTR mask", drvthis->name);
|
|
goto err_ftdi;
|
|
}
|
|
|
|
// set RTS mask and high
|
|
err = ftdi_setrts(&p->ftdic, SIO_SET_RTS_MASK | SIO_SET_RTS_HIGH);
|
|
if (err < 0) {
|
|
report(RPT_ERR, "%s: cannot set RTS mask", drvthis->name);
|
|
goto err_ftdi;
|
|
}
|
|
|
|
// disable flow control
|
|
err = ftdi_setflowctrl(&p->ftdic, SIO_DISABLE_FLOW_CTRL);
|
|
if (err < 0) {
|
|
report(RPT_ERR, "%s: cannot disable flow control", drvthis->name);
|
|
goto err_ftdi;
|
|
}
|
|
|
|
// set the baudrate
|
|
err = ftdi_set_baudrate(&p->ftdic, 1200);
|
|
if (err < 0) {
|
|
report(RPT_ERR, "%s: cannot set baudrate", drvthis->name);
|
|
goto err_ftdi;
|
|
}
|
|
|
|
// set the baudrate
|
|
err = ftdi_set_baudrate(&p->ftdic, 38400);
|
|
if (err < 0) {
|
|
report(RPT_ERR, "%s: cannot set baudrate", drvthis->name);
|
|
goto err_ftdi;
|
|
}
|
|
|
|
// set RTS mask and high
|
|
err = ftdi_setrts(&p->ftdic, SIO_SET_RTS_MASK | SIO_SET_RTS_HIGH);
|
|
if (err < 0) {
|
|
report(RPT_ERR, "%s: cannot set RTS mask", drvthis->name);
|
|
goto err_ftdi;
|
|
}
|
|
|
|
// set DTR mask and high
|
|
err = ftdi_setdtr(&p->ftdic, SIO_SET_DTR_MASK | SIO_SET_DTR_HIGH);
|
|
if (err < 0) {
|
|
report(RPT_ERR, "%s: cannot set DTR mask", drvthis->name);
|
|
goto err_ftdi;
|
|
}
|
|
|
|
// set communication parameters N,8,1
|
|
err = ftdi_set_line_property(&p->ftdic, BITS_8, STOP_BIT_1, NONE);
|
|
if (err < 0) {
|
|
report(RPT_ERR, "%s: cannot set line properties N,8,1", drvthis->name);
|
|
goto err_ftdi;
|
|
}
|
|
|
|
// disable flow control
|
|
err = ftdi_setflowctrl(&p->ftdic, SIO_DISABLE_FLOW_CTRL);
|
|
if (err < 0) {
|
|
report(RPT_ERR, "%s: cannot disable flow control", drvthis->name);
|
|
goto err_ftdi;
|
|
}
|
|
|
|
// set DTR high
|
|
err = ftdi_setrts(&p->ftdic, SIO_SET_DTR_HIGH);
|
|
if (err < 0) {
|
|
report(RPT_ERR, "%s: cannot set DTR high", drvthis->name);
|
|
goto err_ftdi;
|
|
}
|
|
|
|
#if 0
|
|
// uncomment this if using a patched version of ftdi
|
|
// set the event character to 0x00
|
|
err = ftdi_set_event_char(&p->ftdic, 0x100);
|
|
if (err < 0) {
|
|
report(RPT_ERR, "%s: cannot set event character ", drvthis->name);
|
|
goto err_ftdi;
|
|
}
|
|
|
|
timing_uPause(16000);
|
|
#endif
|
|
|
|
// don't know what this does exactly
|
|
buffer[0] = 0xA4;
|
|
buffer[1] = 0x7d;
|
|
err = ftdi_write_data(&p->ftdic, buffer, 2);
|
|
if (err < 0) {
|
|
report(RPT_WARNING, "%s: ftdi_write_data failed with %d", drvthis->name, err);
|
|
goto err_ftdi;
|
|
}
|
|
|
|
timing_uPause(3*16000);
|
|
|
|
buffer[0] = 0xAA;
|
|
buffer[1] = 0xAA;
|
|
for(count = 0; count < 10; count++) {
|
|
err = ftdi_write_data(&p->ftdic, buffer, 2);
|
|
if (err < 0) {
|
|
report(RPT_WARNING, "%s: ftdi_write_data failed with %d", drvthis->name, err);
|
|
goto err_ftdi;
|
|
}
|
|
timing_uPause(3*16000);
|
|
}
|
|
|
|
// set the event character to 0x00 (only works with patched libftdi)
|
|
// err = ftdi_set_event_char(&p->ftdic, 0x00);
|
|
// if (err < 0) {
|
|
// report(RPT_ERR, "%s: cannot set event character ", drvthis->name);
|
|
// goto err_ftdi;
|
|
// }
|
|
//
|
|
// timing_uPause(16000);
|
|
|
|
// set DTR high
|
|
err = ftdi_setdtr(&p->ftdic, SIO_SET_DTR_HIGH);
|
|
if (err < 0) {
|
|
report(RPT_ERR, "%s: cannot set DTR high", drvthis->name);
|
|
goto err_ftdi;
|
|
}
|
|
|
|
// set RTS high
|
|
err = ftdi_setrts(&p->ftdic, SIO_SET_RTS_HIGH);
|
|
if (err < 0) {
|
|
report(RPT_ERR, "%s: cannot set RTS high", drvthis->name);
|
|
goto err_ftdi;
|
|
}
|
|
|
|
for (count = 0; count < 6; count++) {
|
|
// reset device
|
|
err = ftdi_usb_reset(&p->ftdic);
|
|
if (err < 0) {
|
|
report(RPT_ERR, "%s: cannot reset device", drvthis->name);
|
|
goto err_ftdi;
|
|
}
|
|
}
|
|
|
|
// set latency timer
|
|
err = ftdi_set_latency_timer(&p->ftdic, 16);
|
|
if (err < 0) {
|
|
report(RPT_ERR, "%s: cannot set latency timer", drvthis->name);
|
|
goto err_ftdi;
|
|
}
|
|
|
|
timing_uPause(16000);
|
|
|
|
// set communication parameters N,8,1
|
|
err = ftdi_set_line_property(&p->ftdic, BITS_8, STOP_BIT_1, NONE);
|
|
if (err < 0) {
|
|
report(RPT_ERR, "%s: cannot set line properties N,8,1", drvthis->name);
|
|
goto err_ftdi;
|
|
}
|
|
|
|
// set DTR mask and high
|
|
err = ftdi_setdtr(&p->ftdic, SIO_SET_DTR_MASK | SIO_SET_DTR_HIGH);
|
|
if (err < 0) {
|
|
report(RPT_ERR, "%s: cannot set DTR mask", drvthis->name);
|
|
goto err_ftdi;
|
|
}
|
|
|
|
// set RTS mask and high
|
|
err = ftdi_setrts(&p->ftdic, SIO_SET_RTS_MASK | SIO_SET_RTS_HIGH);
|
|
if (err < 0) {
|
|
report(RPT_ERR, "%s: cannot set RTS mask", drvthis->name);
|
|
goto err_ftdi;
|
|
}
|
|
|
|
// disable flow control
|
|
err = ftdi_setflowctrl(&p->ftdic, SIO_DISABLE_FLOW_CTRL);
|
|
if (err < 0) {
|
|
report(RPT_ERR, "%s: cannot disable flow control", drvthis->name);
|
|
goto err_ftdi;
|
|
}
|
|
|
|
// set the baudrate
|
|
err = ftdi_set_baudrate(&p->ftdic, 38400);
|
|
if (err < 0) {
|
|
report(RPT_ERR, "%s: cannot set baudrate 38400", drvthis->name);
|
|
goto err_ftdi;
|
|
}
|
|
|
|
// set the baudrate
|
|
err = ftdi_set_baudrate(&p->ftdic, 38400);
|
|
if (err < 0) {
|
|
report(RPT_ERR, "%s: cannot set baudrate 38400", drvthis->name);
|
|
goto err_ftdi;
|
|
}
|
|
|
|
// set RTS mask and high
|
|
err = ftdi_setrts(&p->ftdic, SIO_SET_RTS_MASK | SIO_SET_RTS_HIGH);
|
|
if (err < 0) {
|
|
report(RPT_ERR, "%s: cannot set RTS mask", drvthis->name);
|
|
goto err_ftdi;
|
|
}
|
|
|
|
// set DTR mask and high
|
|
err = ftdi_setdtr(&p->ftdic, SIO_SET_DTR_MASK | SIO_SET_DTR_HIGH);
|
|
if (err < 0) {
|
|
report(RPT_ERR, "%s: cannot set DTR mask", drvthis->name);
|
|
goto err_ftdi;
|
|
}
|
|
|
|
// set communication parameters N,8,1
|
|
err = ftdi_set_line_property(&p->ftdic, BITS_8, STOP_BIT_1, NONE);
|
|
if (err < 0) {
|
|
report(RPT_ERR, "%s: cannot set line properties N,8,1", drvthis->name);
|
|
goto err_ftdi;
|
|
}
|
|
|
|
// disable flow control
|
|
err = ftdi_setflowctrl(&p->ftdic, SIO_DISABLE_FLOW_CTRL);
|
|
if (err < 0) {
|
|
report(RPT_ERR, "%s: cannot disable flow control", drvthis->name);
|
|
goto err_ftdi;
|
|
}
|
|
|
|
// turn off the event character (only works with patched libftdi)
|
|
// err = ftdi_set_event_char(&p->ftdic, 0x00);
|
|
// if (err < 0) {
|
|
// report(RPT_ERR, "%s: cannot turn off event character ", drvthis->name);
|
|
// goto err_ftdi;
|
|
// }
|
|
//
|
|
// timing_uPause(2*16000);
|
|
|
|
// not sure about this
|
|
buffer[0] = 0xA0;
|
|
err = lis_ftdi_write_command(drvthis, buffer, 1);
|
|
if (err < 0) {
|
|
report(RPT_WARNING, "%s: lis_ftdi_clear: "
|
|
"lis_ftdi_write_command failed", drvthis->name);
|
|
}
|
|
|
|
timing_uPause(10*16000);
|
|
|
|
report(RPT_DEBUG, "%s: init() done", drvthis->name);
|
|
|
|
// lis_test(drvthis); /*DEBUG*/
|
|
|
|
return 0;
|
|
|
|
err_ftdi:
|
|
ftdi_usb_close(&p->ftdic);
|
|
ftdi_deinit(&p->ftdic);
|
|
if(p->line_flags)
|
|
free(p->line_flags);
|
|
err_framebuf:
|
|
free(p->framebuf);
|
|
err_begin:
|
|
|
|
if (p->parent_flag) { // terminate the child
|
|
p->child_flag = 1;
|
|
while(p->parent_flag)
|
|
timing_uPause(5*16000);
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Clean-up
|
|
//
|
|
MODULE_EXPORT void
|
|
lis_close(Driver *drvthis)
|
|
{
|
|
PrivateData *p = (PrivateData *) drvthis->private_data;
|
|
|
|
report(RPT_DEBUG, "%s: closing driver",
|
|
drvthis->name);
|
|
if (p != NULL) {
|
|
if (p->parent_flag) { // terminate the child
|
|
p->child_flag = 1;
|
|
while(p->parent_flag)
|
|
timing_uPause(5*16000);
|
|
}
|
|
ftdi_usb_purge_buffers(&p->ftdic);
|
|
ftdi_usb_close(&p->ftdic);
|
|
ftdi_deinit(&p->ftdic);
|
|
|
|
if (p->framebuf != NULL)
|
|
free(p->framebuf);
|
|
|
|
free(p);
|
|
}
|
|
drvthis->store_private_ptr(drvthis, NULL);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Returns the display width
|
|
//
|
|
MODULE_EXPORT int
|
|
lis_width (Driver *drvthis)
|
|
{
|
|
PrivateData *p = (PrivateData *) drvthis->private_data;
|
|
|
|
return p->width;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Returns the display height
|
|
//
|
|
MODULE_EXPORT int
|
|
lis_height (Driver *drvthis)
|
|
{
|
|
PrivateData *p = (PrivateData *) drvthis->private_data;
|
|
|
|
return p->height;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Clear the framebuffer
|
|
//
|
|
MODULE_EXPORT void
|
|
lis_clear (Driver *drvthis)
|
|
{
|
|
PrivateData *p = (PrivateData *) drvthis->private_data;
|
|
int line;
|
|
|
|
report(RPT_DEBUG, "%s: Clearing display",
|
|
drvthis->name);
|
|
for (line = 0; line < p->height; line++) {
|
|
memset(p->framebuf + (line * p->width), ' ', p->width);
|
|
p->line_flags[line] = 1;
|
|
}
|
|
lis_standard_custom_chars(drvthis);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Place a character in the framebuffer
|
|
//
|
|
MODULE_EXPORT void
|
|
lis_chr (Driver *drvthis, int x, int y, unsigned char ch)
|
|
{
|
|
PrivateData *p = (PrivateData *) drvthis->private_data;
|
|
|
|
// ignore out-of-range
|
|
if (y > p->height || x > p->width) {
|
|
report(RPT_WARNING, "%s: Writing char %x at %d,%d"
|
|
" ignored out of range %d,%d",
|
|
drvthis->name, ch, x, y, p->height, p->width);
|
|
return;
|
|
}
|
|
|
|
y--;
|
|
x--;
|
|
|
|
if ( p->framebuf[ (y * p->width) + x] != ch) {
|
|
p->framebuf[ (y * p->width) + x] = ch;
|
|
|
|
p->line_flags[y] = 1;
|
|
report(RPT_DEBUG, "%s: Caching char %x at %d,%d",
|
|
drvthis->name, ch, x, y);
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Place a string in the framebuffer
|
|
//
|
|
MODULE_EXPORT void
|
|
lis_string (Driver *drvthis, int x, int y, char *s)
|
|
{
|
|
PrivateData *p = (PrivateData *) drvthis->private_data;
|
|
int i;
|
|
|
|
x --; // Convert 1-based coords to 0-based
|
|
y --;
|
|
|
|
report(RPT_DEBUG, "%s: Write string to framebuffer %d,%d \"%s\"",
|
|
drvthis->name, x, y, s);
|
|
|
|
for (i = 0; s[i]; i++) {
|
|
// Check for buffer overflows...
|
|
if ((y * p->width) + x + i > (p->width * p->height)) {
|
|
report(RPT_WARNING, "%s: Writing string ignored, out of range",
|
|
drvthis->name, x, y);
|
|
break;
|
|
}
|
|
|
|
if ( p->framebuf[(y*p->width) + x + i] != s[i] ) {
|
|
p->framebuf[(y*p->width) + x + i] = s[i];
|
|
p->line_flags[((y*p->width) + x + i ) / p->width ] = 1; // dirty
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Set default icon into a userdef char
|
|
//
|
|
MODULE_EXPORT int
|
|
lis_icon (Driver *drvthis, int x, int y, int icon)
|
|
{
|
|
PrivateData *p = (PrivateData *) drvthis->private_data;
|
|
unsigned char ch;
|
|
|
|
switch (icon) {
|
|
case ICON_BLOCK_FILLED:
|
|
ch = 0xff;
|
|
break;
|
|
case ICON_HEART_FILLED:
|
|
ch = 0x9D;
|
|
break;
|
|
case ICON_HEART_OPEN:
|
|
ch = 0x9C;
|
|
break;
|
|
case ICON_ARROW_UP:
|
|
ch = 0x18;
|
|
break;
|
|
case ICON_ARROW_DOWN:
|
|
ch = 0x19;
|
|
break;
|
|
case ICON_ARROW_LEFT:
|
|
ch = 0x1B;
|
|
break;
|
|
case ICON_ARROW_RIGHT:
|
|
ch = 0x1A;
|
|
break;
|
|
case ICON_CHECKBOX_OFF:
|
|
ch = 0x6F;
|
|
break;
|
|
case ICON_CHECKBOX_ON:
|
|
ch = 0xC7;
|
|
break;
|
|
case ICON_CHECKBOX_GRAY:
|
|
ch = 2;
|
|
if (p->ccmode != standard)
|
|
lis_standard_custom_chars(drvthis);
|
|
break;
|
|
case ICON_STOP:
|
|
ch = 0x16;
|
|
break;
|
|
case ICON_PAUSE:
|
|
ch = 0xA0;
|
|
break;
|
|
case ICON_PLAY:
|
|
ch = 0x10;
|
|
break;
|
|
case ICON_PLAYR:
|
|
ch = 0x11;
|
|
break;
|
|
case ICON_FF:
|
|
ch = 0xBB;
|
|
break;
|
|
case ICON_FR:
|
|
ch = 0xBC;
|
|
break;
|
|
case ICON_NEXT:
|
|
ch = 0x1D;
|
|
break;
|
|
case ICON_PREV:
|
|
ch = 0x1C;
|
|
break;
|
|
case ICON_REC:
|
|
ch = 0xAE;
|
|
break;
|
|
default:
|
|
return -1; /* Let the core do other icons */
|
|
|
|
}
|
|
report(RPT_DEBUG, "%s: Writing icon #%d (%x) @ %d,%d",
|
|
drvthis->name, icon, ch, x, y);
|
|
lis_chr(drvthis, x, y, ch);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
#if 0
|
|
void
|
|
lis_test(Driver *drvthis)
|
|
{
|
|
int x, y, i;
|
|
|
|
|
|
x = 0;
|
|
y = 1;
|
|
lis_clear(drvthis);
|
|
|
|
for (i = 0; i < 1001; i++) {
|
|
lis_vbar(drvthis, 1, 2, 2, i, 0);
|
|
lis_flush(drvthis);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
/**
|
|
* Define a custom character and write it to the LCD.
|
|
* \param drvthis Pointer to driver structure.
|
|
* \param n Custom character to define [0 - (NUM_CCs-1)].
|
|
* \param dat Array of 8(=cellheight) bytes, each representing a pixel row
|
|
* starting from the top to bottom.
|
|
* The bits in each byte represent the pixels where the LSB
|
|
* (least significant bit) is the rightmost pixel in each pixel row.
|
|
*/
|
|
MODULE_EXPORT void
|
|
lis_set_char(Driver *drvthis, int n, unsigned char *dat)
|
|
{
|
|
PrivateData *p = drvthis->private_data;
|
|
unsigned char mask = (1 << p->cellwidth) - 1;
|
|
int row;
|
|
|
|
|
|
if ((n < 0) || (n >= NUM_CCs))
|
|
return;
|
|
if (dat == NULL)
|
|
return;
|
|
|
|
for (row = 0; row < p->cellheight; row++) {
|
|
int letter = 0;
|
|
|
|
if (p->lastline || (row < p->cellheight - 1))
|
|
letter = dat[row] & mask;
|
|
|
|
if (p->cc[n].cache[row] != letter) {
|
|
p->cc[n].clean = 0; /* only mark dirty if really different */
|
|
}
|
|
p->cc[n].cache[row] = letter;
|
|
}
|
|
report(RPT_DEBUG, "%s: cached custom character #%d",
|
|
drvthis->name, n);
|
|
}
|
|
|
|
|
|
/**
|
|
* Draw a vertical bar bottom-up.
|
|
* \param drvthis Pointer to driver structure.
|
|
* \param x Horizontal character position (column) of the starting point.
|
|
* \param y Vertical character position (row) of the starting point.
|
|
* \param len Number of characters that the bar is high at 100%
|
|
* \param promille Current height level of the bar in promille.
|
|
* \param options Options (currently unused).
|
|
*/
|
|
MODULE_EXPORT void
|
|
lis_vbar(Driver *drvthis, int x, int y, int len, int promille, int options)
|
|
{
|
|
PrivateData *p = drvthis->private_data;
|
|
|
|
if (p->ccmode != vbar) {
|
|
unsigned char vBar[p->cellheight];
|
|
int i;
|
|
|
|
if (p->ccmode != standard) {
|
|
/* Not supported(yet) */
|
|
report(RPT_WARNING, "%s: vbar: cannot combine two modes using user-defined characters",
|
|
drvthis->name);
|
|
return;
|
|
}
|
|
p->ccmode = vbar;
|
|
|
|
memset(vBar, 0x00, sizeof(vBar));
|
|
|
|
for (i = 2; i <= p->cellheight; i++) {
|
|
// add pixel line per pixel line ...
|
|
vBar[p->cellheight - i + 1] = 0x1F;
|
|
lis_set_char(drvthis, i, vBar);
|
|
}
|
|
}
|
|
|
|
report(RPT_DEBUG, "%s: vbar @ %d,%d len %d, %d/1000",
|
|
drvthis->name, x, y, len, promille);
|
|
|
|
lib_vbar_static(drvthis, x, y, len, promille, options, p->cellheight, 2);
|
|
}
|
|
|
|
|
|
/**
|
|
* Draw a horizontal bar to the right.
|
|
* \param drvthis Pointer to driver structure.
|
|
* \param x Horizontal character position (column) of the starting point.
|
|
* \param y Vertical character position (row) of the starting point.
|
|
* \param len Number of characters that the bar is long at 100%
|
|
* \param promille Current length level of the bar in promille.
|
|
* \param options Options (currently unused).
|
|
*/
|
|
MODULE_EXPORT void
|
|
lis_hbar(Driver *drvthis, int x, int y, int len, int promille, int options)
|
|
{
|
|
PrivateData *p = drvthis->private_data;
|
|
|
|
if (p->ccmode != hbar) {
|
|
unsigned char hBar[p->cellheight];
|
|
int i;
|
|
|
|
if (p->ccmode != standard) {
|
|
/* Not supported(yet) */
|
|
report(RPT_WARNING, "%s: hbar: cannot combine two modes using user-defined characters",
|
|
drvthis->name);
|
|
return;
|
|
}
|
|
|
|
p->ccmode = hbar;
|
|
|
|
for (i = 1; i <= p->cellwidth; i++) {
|
|
// fill pixel columns from left to right.
|
|
memset(hBar, 0xFF & ~((1 << (p->cellwidth - i)) - 1), sizeof(hBar));
|
|
lis_set_char(drvthis, i + 2, hBar);
|
|
}
|
|
}
|
|
report(RPT_DEBUG, "%s: hbar @ %d,%d len %d, %d/1000",
|
|
drvthis->name, x, y, len, promille);
|
|
|
|
lib_hbar_static(drvthis, x, y, len, promille, options, p->cellwidth, 2);
|
|
}
|
|
|
|
|
|
/**
|
|
* Write a big number to the screen.
|
|
* \param drvthis Pointer to driver structure.
|
|
* \param x Horizontal character position (column).
|
|
* \param num Character to write (0 - 10 with 10 representing ':')
|
|
*/
|
|
MODULE_EXPORT void
|
|
lis_num(Driver *drvthis, int x, int num)
|
|
{
|
|
PrivateData *p = drvthis->private_data;
|
|
int do_init = 0;
|
|
|
|
if ((num < 0) || (num > 10))
|
|
return;
|
|
|
|
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;
|
|
}
|
|
|
|
p->ccmode = bignum;
|
|
|
|
do_init = 1;
|
|
}
|
|
report(RPT_DEBUG, "%s: big number %d @ %d",
|
|
drvthis->name, x, num);
|
|
|
|
// Lib_adv_bignum does everything needed to show the bignumbers.
|
|
// offset by 2 because driver uses first three custom characters
|
|
lib_adv_bignum(drvthis, x, num, 3, do_init);
|
|
}
|
|
|
|
|
|
/**
|
|
* Get total number of custom characters available.
|
|
* \param drvthis Pointer to driver structure.
|
|
* \return Number of custom characters.
|
|
*/
|
|
MODULE_EXPORT int
|
|
lis_get_free_chars (Driver *drvthis)
|
|
{
|
|
//PrivateData *p = drvthis->private_data;
|
|
|
|
return NUM_CCs - 3; // first three are reserved
|
|
}
|
|
|