/** \file server/drivers/lis.c * LCDd \c lis driver for the L.I.S MCE VFD by vlsys.co.kr. This is a 20x2 VFD * using NEC UPD16314 driver and FTDI FT232RQ USB-to-serial converter. * * \note This driver uses the libftdi library to interface to the FTDI chip. * Make sure kernel module ftdi_sio doesn't claim the device. */ /*- * Copyright (c) 2007, Daryl Fonseca-Holt * * Based on: * ula200 driver, Copyright (C) 2006, Bernhard Walle * IOWarrior driver, Copyright(C) 2004-2006 Peter Marschall * * 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. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include #include #include #include #include #include #include #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_"; /** * UPD16314 table. Charactar mapping for UPD16314 device by Daryl Fonseca-Holt * taken from Mark Haemmerling's HD44780 table. * Character mapping for HD44780 devices by Mark Haemmerling . * * Translates ISO 8859-1 to any HD44780 charset. */ 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 }; /** * Setup standard custom chars. By default, the full block character is placed * at 0x01 as the UPD16314 has none. */ static 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 drvthis Pointer to Driver * \param data Data bytes * \param length The number of bytes in 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), starting in the first * column. * * \param drvthis Pointer to Driver * \param line The line number (counting from 1) * \param string Pointer to data bytes * \param len The number of bytes in string which are valid * \return 0 on success, negative value on error */ 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; } /** * API: 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) { p->parent_flag = 0; return size; } } p->parent_flag = 0; return 0; } /** * API: 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; } /*** * API: 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; } /** * API: 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); p->lastline = drvthis->config_get_bool(drvthis->name, "lastline", 0, 1); /* 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, strerror(err)); goto err_framebuf; } report(RPT_INFO, "%s: read thread created"); 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; } /** * API: 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); } /** * API: Returns the display width */ MODULE_EXPORT int lis_width (Driver *drvthis) { PrivateData *p = (PrivateData *) drvthis->private_data; return p->width; } /** * API: Returns the display height */ MODULE_EXPORT int lis_height(Driver *drvthis) { PrivateData *p = (PrivateData *) drvthis->private_data; return p->height; } /** * API: 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); } /** * API: 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); } } /** * API: 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 */ } } } /** * API: Place an icon on the screen. The uPD16314 comes with a lot of icons * already in CGROM, so use them. */ 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) { return NUM_CCs - 3; /* first three are reserved */ } /* EOF */