/** \file server/drivers/SureElec.c * * Driver for the LCD modules (actually the controller board) available from * the 'SURE electronics' shop (http://www.sureelectronics.net). * * These devices are PIC based controlled, using a serial communication * protocol with the host. The actual connection to host is done through * USB through a serial <-> USB converter (CP2102 USB to UART Bridge) * integrated on the board. */ /*- * Copyright (C) 2009, Laurent Latil * * This program 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 of the License, or any later * version. * * This program 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., 51 * Franklin Street, Fifth Floor, Boston, MA 02110-1301 */ /* * It should be noted that the communication protocol, while somewhat * documented in the LCD user's manual, is not complete (ie. the * initialization sequence is not described), and inacurate on some points. * This driver is so based on the spying of serial communication using the * officially supported client softwares (LCD Smarties & SureLCD). */ #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "lcd.h" #include "lcd_lib.h" #include "SureElec.h" #include "adv_bignum.h" #include "report.h" /* API: variables for the server core */ MODULE_EXPORT char *api_version = API_VERSION; MODULE_EXPORT int stay_in_foreground = 0; MODULE_EXPORT int supports_multiple = 0; MODULE_EXPORT char *symbol_prefix = "SureElec_"; typedef enum { SURE_ELEC_EDITION1 = 1, SURE_ELEC_EDITION2 = 2, SURE_ELEC_EDITION3 = 3, } SureElec_edition; /** private data for the \c SureElec driver */ typedef struct SureElec_private_data { int fd; /* Serial port file descriptor */ int width, height; /* Screen size */ int cellwidth, cellheight; /* Cell size */ /* framebuffer and buffer for old LCD contents */ unsigned char *framebuf; unsigned char *backingstore; /* definable characters */ CGmode ccmode; int output_state; /**< current output state */ int contrast; /**< current contrast */ int brightness; int offbrightness; int backlight; SureElec_edition edition; } PrivateData; static int open_port(Driver * drvthis, const char *device); static void get_device_screen_size(Driver * drvthis, int *width, int *height); static int write_(Driver * drvthis, const unsigned char *buf, size_t count); static int read_(Driver * drvthis, void *buf, size_t count); /** * Initialize the driver. * \param drvthis Pointer to driver structure. * \retval 0 Success. * \retval <0 Error. * * \todo This function returns at several points without properly freeing * file descriptors and allocated memory. */ MODULE_EXPORT int SureElec_init(Driver * drvthis) { char device[256] = DEFAULT_DEVICE; char size[256] = DEFAULT_SIZE; int tmp; int user_width = -1, user_height = -1; int device_width = -1, device_height = -1; const char *sedition; PrivateData *p; /* Allocate and store private data */ p = (PrivateData *) malloc(sizeof(PrivateData)); if (p == NULL) return -1; if (drvthis->store_private_ptr(drvthis, p)) return -1; /* Initialise the PrivateData structure */ p->fd = -1; p->edition = SURE_ELEC_EDITION2; /* Assume an edition 2 * version */ p->cellwidth = CELL_WIDTH; p->cellheight = CELL_HEIGHT; p->framebuf = NULL; p->backingstore = NULL; p->backlight = 0; debug(RPT_INFO, "SureElec driver: init(%p)", drvthis); /* READ CONFIG FILE */ /* Get serial device to use */ strncpy(device, drvthis->config_get_string(drvthis->name, "Device", 0, DEFAULT_DEVICE), sizeof(device)); device[sizeof(device) - 1] = '\0'; report(RPT_INFO, "%s: using Device %s", drvthis->name, device); if (open_port(drvthis, device) == -1) { return -1; } /* Get edition version */ sedition = drvthis->config_get_string(drvthis->name, "Edition", 0, ""); if (strlen(sedition) != 0) { int n = sedition[0] - '0'; if (strlen(sedition) != 1 || n < 1 || n > 3) { report(RPT_ERR, "%s: unknown edition version %s; must be one of 1, 2, or 3", drvthis->name, sedition); return -1; } p->edition = (SureElec_edition) n; } /* Get the user-specified display size */ strncpy(size, drvthis->config_get_string(drvthis->name, "Size", 0, ""), sizeof(size)); size[sizeof(size) - 1] = '\0'; if (strlen(size) != 0) { if ((sscanf(size, "%dx%d", &user_width, &user_height) != 2) || (user_width <= 0) || (user_width > LCD_MAX_WIDTH) || (user_height <= 0) || (user_height > LCD_MAX_HEIGHT)) { report(RPT_ERR, "%s: invalid screen size: %s", drvthis->name, size); return -1; } } if (p->edition != SURE_ELEC_EDITION1) { /* For edition >1 the device can report screen size */ get_device_screen_size(drvthis, &device_width, &device_height); if (user_width != -1) { if (user_width != device_width || user_height != device_height) { report(RPT_WARNING, "%s: device and config settings disagree for screen size: %d x %d <> %d x %d", drvthis->name, device_width, device_height, user_width, user_height); report(RPT_WARNING, "%s: config specified size applied.", drvthis->name); } device_width = user_width; device_height = user_height; } } else { device_width = user_width; device_height = user_height; } if (device_width == -1) { report(RPT_ERR, "%s: screen size must be specified", drvthis->name); return -1; } /* set screen size */ p->width = device_width; p->height = device_height; report(RPT_INFO, "%s: screen size %d x %d.", drvthis->name, p->width, p->height); /* Get contrast */ tmp = drvthis->config_get_int(drvthis->name, "Contrast", 0, DEFAULT_CONTRAST); debug(RPT_INFO, "%s: Contrast (in config) is '%d'", drvthis->name, tmp); if ((tmp < 0) || (tmp > 1000)) { report(RPT_WARNING, "%s: Contrast value must be between 0 and 1000; using default %d", drvthis->name, DEFAULT_CONTRAST); tmp = DEFAULT_CONTRAST; } SureElec_set_contrast(drvthis, tmp); /* Get backlight */ tmp = drvthis->config_get_int(drvthis->name, "Brightness", 0, DEFAULT_BRIGHTNESS); debug(RPT_INFO, "%s: Brightness (in config) is '%d'", drvthis->name, tmp); if ((tmp < 0) || (tmp > 1000)) { report(RPT_WARNING, "%s: Brightness must be between 0 and 1000; using default %d", drvthis->name, DEFAULT_BRIGHTNESS); tmp = DEFAULT_BRIGHTNESS; } p->brightness = tmp; /* Which backlight-off "brightness" */ tmp = drvthis->config_get_int(drvthis->name, "OffBrightness", 0, DEFAULT_OFFBRIGHTNESS); debug(RPT_INFO, "%s: OffBrightness (in config) is '%d'", __FUNCTION__, tmp); if ((tmp < 0) || (tmp > 1000)) { report(RPT_WARNING, "%s: OffBrightness must be between 0 and 1000; using default %d", drvthis->name, DEFAULT_OFFBRIGHTNESS); tmp = DEFAULT_OFFBRIGHTNESS; } p->offbrightness = tmp; /* Allocate the frame buffer. */ p->framebuf = (unsigned char *)calloc(p->width * p->height, 1); if (p->framebuf == NULL) { report(RPT_ERR, "%s: unable to create framebuffer", drvthis->name); return -1; } memset(p->framebuf, ' ', p->width * p->height); /* Allocate the backing store. */ p->backingstore = (unsigned char *)malloc(p->width * p->height); if (p->backingstore == NULL) { report(RPT_ERR, "%s: unable to create framebuffer backing store", drvthis->name); return -1; } memset(p->backingstore, '#', p->width * p->height); SureElec_clear(drvthis); SureElec_flush(drvthis); report(RPT_DEBUG, "%s: init() done", drvthis->name); return 0; } /** * Close the driver (do necessary clean-up). * \param drvthis Pointer to driver structure. */ MODULE_EXPORT void SureElec_close(Driver * drvthis) { PrivateData *p = drvthis->private_data; if (p != NULL) { if (p->fd >= 0) close(p->fd); /* Free allocated framebuffers */ if (p->framebuf) free(p->framebuf); if (p->backingstore) free(p->backingstore); /* Free our private data */ free(p); } drvthis->store_private_ptr(drvthis, NULL); debug(RPT_DEBUG, "SureElec: closed"); } /** * Return the display width in characters. * \param drvthis Pointer to driver structure. * \return Number of characters the display is wide. */ MODULE_EXPORT int SureElec_width(Driver * drvthis) { PrivateData *p = drvthis->private_data; return p->width; } /** * Return the display height in characters. * \param drvthis Pointer to driver structure. * \return Number of characters the display is high. */ MODULE_EXPORT int SureElec_height(Driver * drvthis) { PrivateData *p = drvthis->private_data; return p->height; } /** * Return the width of a character in pixels. * \param drvthis Pointer to driver structure. * \return Number of pixel columns a character cell is wide. */ MODULE_EXPORT int SureElec_cellwidth(Driver * drvthis) { return CELL_WIDTH; } /** * Return the height of a character in pixels. * \param drvthis Pointer to driver structure. * \return Number of pixel lines a character cell is high. */ MODULE_EXPORT int SureElec_cellheight(Driver * drvthis) { return CELL_HEIGHT; } /** * Print a string on the screen at position (x,y). * The upper-left corner is (1,1), the lower-right corner is (p->width, p->height). * \param drvthis Pointer to driver structure. * \param x Horizontal character position (column). * \param y Vertical character position (row). * \param string String that gets written. */ MODULE_EXPORT void SureElec_string(Driver * drvthis, int x, int y, const char string[]) { PrivateData *p = drvthis->private_data; int i; /* Convert 1-based coords to 0-based... */ x--; y--; /* Check coord. validity */ if ((y < 0) || (y >= p->height)) return; for (i = 0; (string[i] != '\0') && (x < p->width); i++, x++) { p->framebuf[(y * p->width) + x] = string[i]; } debug(RPT_DEBUG, "SureElec: printed string at (%d,%d)", x, y); } /** * Clear the screen. * \param drvthis Pointer to driver structure. */ MODULE_EXPORT void SureElec_clear(Driver * drvthis) { PrivateData *p = drvthis->private_data; /* set hbar/vbar/bignum mode back to normal character display */ p->ccmode = standard; /* replace all chars in framebuf with spaces */ memset(p->framebuf, ' ', (p->width * p->height)); debug(RPT_DEBUG, "SureElec: cleared screen"); } /** * Print a character on the screen at position (x,y). * The upper-left corner is (1,1), the lower-right corner is (p->width, p->height). * \param drvthis Pointer to driver structure. * \param x Horizontal character position (column). * \param y Vertical character position (row). * \param c Character that gets written. */ MODULE_EXPORT void SureElec_chr(Driver * drvthis, int x, int y, char c) { PrivateData *p = drvthis->private_data; /* Convert 1-based coords to 0-based... */ x--; y--; if ((x >= 0) && (y >= 0) && (x < p->width) && (y < p->height)) p->framebuf[(y * p->width) + x] = c; debug(RPT_DEBUG, "SureElec: writing character %02X to position (%d,%d)", c, x, y); } /** * Flush data on screen to the LCD. * \param drvthis Pointer to driver structure. */ MODULE_EXPORT void SureElec_flush(Driver * drvthis) { PrivateData *p = drvthis->private_data; int modified = 0; int i, j; unsigned char cmd[4] = {'\xFE', '\x47', '\x01', /* line */ 0}; for (i = 0; i < p->height; i++) { /* Check if line content changed */ for (j = 0; j < p->width && p->framebuf[p->width * i + j] == p->backingstore[p->width * i + j]; j++) { ; } if (j < p->width) { /* * Line content has been changed, need to output the * line on screen */ cmd[3] = i + 1; if (write_(drvthis, cmd, sizeof(cmd)) == -1 || write_(drvthis, &(p->framebuf[p->width * i]), p->width) == -1) { return; } modified = 1; } } if (modified) { /* If something changed on screen, update the backingstore */ memcpy(p->backingstore, p->framebuf, p->width * p->height); } debug(RPT_DEBUG, "SureElec: frame buffer flushed"); } /** * Get current LCD contrast. * This is only the locally stored contrast, the contrast value * cannot be retrieved from the LCD. * \param drvthis Pointer to driver structure. * \return Stored contrast in promille. */ MODULE_EXPORT int SureElec_get_contrast(Driver * drvthis) { PrivateData *p = drvthis->private_data; return p->contrast; } /** * Set the LCD contrast. * \param drvthis Pointer to driver structure. * \param promille New contrast value in promille. */ MODULE_EXPORT void SureElec_set_contrast(Driver * drvthis, int promille) { unsigned char cmd[3] = {'\xFE', '\x50', 0}; PrivateData *p = drvthis->private_data; int real_contrast; /* Check it */ if ((promille < 0) || (promille > 1000)) return; /* Store it */ p->contrast = promille; real_contrast = (int)((long)promille * MAX_CONTRAST / 1000); /* And do it */ cmd[2] = real_contrast; write_(drvthis, cmd, sizeof(cmd)); } /** * Retrieve brightness. * \param drvthis Pointer to driver structure. * \param state Brightness state (on/off) for which we want the value. * \return Stored brightness in promille. */ MODULE_EXPORT int SureElec_get_brightness(Driver * drvthis, int state) { PrivateData *p = drvthis->private_data; return (state == BACKLIGHT_ON) ? p->brightness : 0; } /** * Set on/off brightness. * \param drvthis Pointer to driver structure. * \param state Brightness state (on/off) for which we want to store the value. * \param promille New brightness in promille. */ MODULE_EXPORT void SureElec_set_brightness(Driver * drvthis, int state, int promille) { PrivateData *p = drvthis->private_data; /* Check value */ if (promille < 0 || promille > 1000) return; /* store the software value */ if (state == BACKLIGHT_ON) p->brightness = promille; else p->offbrightness = promille; } /** * Turn the LCD backlight on or off. * \param drvthis Pointer to driver structure. * \param on New backlight status. */ MODULE_EXPORT void SureElec_backlight(Driver * drvthis, int on) { PrivateData *p = drvthis->private_data; int promille; if (BACKLIGHT_ON == on) { promille = p->brightness; } else { promille = p->offbrightness; } if (promille == 0 && p->backlight) { /* Turn BL off */ unsigned char cmd[2] = {'\xFE', '\x46'}; if (write_(drvthis, cmd, sizeof(cmd)) == -1) { return; } report(RPT_INFO, "SureElec: BL turned off"); p->backlight = 0; } if (promille > 0) { unsigned char cmd_bl[3] = {'\xFE', '\x42', '\x0'}; unsigned char cmd_brightness[3] = {'\xFE', '\x98', 0}; int level; /* Turn BL on & set brightness level */ if (!p->backlight) { if (write_(drvthis, cmd_bl, sizeof(cmd_bl)) == -1) { return; } report(RPT_INFO, "SureElec: BL turned on"); p->backlight = 1; } level = (int)((long)promille * MAX_BACKLIGHT / 1000); cmd_brightness[2] = level; write_(drvthis, cmd_brightness, sizeof(cmd_brightness)); } } /** * 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 SureElec_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 = 1; i < p->cellheight; i++) { /* add pixel line per pixel line */ vBar[p->cellheight - i] = 0xFF; SureElec_set_char(drvthis, i, vBar); } } lib_vbar_static(drvthis, x, y, len, promille, options, p->cellheight, 0); } /** * 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 SureElec_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; memset(hBar, 0x00, sizeof(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)); SureElec_set_char(drvthis, i, hBar); } } lib_hbar_static(drvthis, x, y, len, promille, options, p->cellwidth, 0); } /** * 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 SureElec_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; } /* Lib_adv_bignum does everything needed to show the bignumbers. */ lib_adv_bignum(drvthis, x, num, 0, do_init); } /** * Get total number of custom characters available. * \param drvthis Pointer to driver structure. * \return Number of custom characters (always NUM_CCs). */ MODULE_EXPORT int SureElec_get_free_chars(Driver * drvthis) { return NUM_CC; } /** * 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 SureElec_set_char(Driver * drvthis, int n, unsigned char *dat) { PrivateData *p = drvthis->private_data; unsigned char cmd[11] = {'\xFE', '\x4E', 0, 0, 0, 0, 0, 0, 0, 0, 0}; unsigned char mask = (1 << CELL_WIDTH) - 1; int row; if ((unsigned int) n >= NUM_CC) return; if (!dat) return; cmd[2] = n; /* Custom char to define. */ for (row = 0; row < p->cellheight; row++) { cmd[row + 3] = dat[row] & mask; } write_(drvthis, cmd, sizeof(cmd)); } /** * Place an icon on the screen. * \param drvthis Pointer to driver structure. * \param x Horizontal character position (column). * \param y Vertical character position (row). * \param icon synbolic value representing the icon. * \retval 0 Icon has been successfully defined/written. * \retval <0 Server core shall define/write the icon. */ MODULE_EXPORT int SureElec_icon(Driver * drvthis, int x, int y, int icon) { static unsigned char heart_open[] = { b__XXXXX, b__X_X_X, b_______, b_______, b_______, b__X___X, b__XX_XX, b__XXXXX }; static unsigned char heart_filled[] = { b__XXXXX, b__X_X_X, b___X_X_, b___XXX_, b___XXX_, b__X_X_X, b__XX_XX, b__XXXXX }; static unsigned char arrow_up[] = { b____X__, b___XXX_, b__X_X_X, b____X__, b____X__, b____X__, b____X__, b_______ }; static unsigned char arrow_down[] = { b____X__, b____X__, b____X__, b____X__, b__X_X_X, b___XXX_, b____X__, b_______ }; static unsigned char checkbox_off[] = { b_______, b_______, b__XXXXX, b__X___X, b__X___X, b__X___X, b__XXXXX, b_______ }; static unsigned char checkbox_on[] = { b____X__, b____X__, b__XXX_X, b__X_XX_, b__X_X_X, b__X___X, b__XXXXX, b_______ }; static unsigned char checkbox_gray[] = { b_______, b_______, b__XXXXX, b__X_X_X, b__XX_XX, b__X_X_X, b__XXXXX, b_______ }; unsigned char *pattern = NULL; char charnum = 0; switch (icon) { case ICON_BLOCK_FILLED: charnum = 0xFF; break; case ICON_ARROW_LEFT: charnum = 0x7F; break; case ICON_ARROW_RIGHT: charnum = 0x7E; break; case ICON_ARROW_UP: charnum = 1; pattern = arrow_up; break; case ICON_ARROW_DOWN: charnum = 2; pattern = arrow_down; break; case ICON_HEART_FILLED: pattern = heart_filled; break; case ICON_HEART_OPEN: pattern = heart_open; break; case ICON_CHECKBOX_OFF: charnum = 3; pattern = checkbox_off; break; case ICON_CHECKBOX_ON: charnum = 4; pattern = checkbox_on; break; case ICON_CHECKBOX_GRAY: charnum = 5; pattern = checkbox_gray; break; default: return -1; } if (NULL != pattern) { SureElec_set_char(drvthis, charnum, pattern); } SureElec_chr(drvthis, x, y, charnum); return 0; } /** * Open the communication port and sends the initialization 'magic' sequence * to the device * \param drvthis Pointer to driver structure. * \param device Name of the device to use. * \retval 0 Success * \retval <0 Error opening port or writing init sequence */ static int open_port(Driver * drvthis, const char *device) { PrivateData *p = drvthis->private_data; struct termios portset; unsigned char cmd[3] = {'\xFE', '\x56', 0}; unsigned char init_seq[7] = {'\x54', '\x58', '\x4B', '\x52', '\x44', '\x41', '\x60'}; int i; /* Set up io port correctly, and open it... */ p->fd = open(device, O_RDWR | O_NOCTTY); if (p->fd == -1) { report(RPT_ERR, "%s: open(%s) failed (%s)", drvthis->name, device, strerror(errno)); if (errno == EACCES) report(RPT_ERR, "%s: %s device could not be opened", drvthis->name, device); return -1; } report(RPT_INFO, "%s: opened display on %s", drvthis->name, device); tcgetattr(p->fd, &portset); /* We use RAW mode */ #ifdef HAVE_CFMAKERAW /* The easy way */ cfmakeraw(&portset); #else /* The hard way */ portset.c_iflag &= ~( IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON ); portset.c_oflag &= ~OPOST; portset.c_lflag &= ~( ECHO | ECHONL | ICANON | ISIG | IEXTEN ); portset.c_cflag &= ~( CSIZE | PARENB | CRTSCTS ); portset.c_cflag |= CS8 | CREAD | CLOCAL; #endif /* Set timeouts */ portset.c_cc[VMIN] = 1; portset.c_cc[VTIME] = 3; /* Set port speed */ cfsetospeed(&portset, B9600); cfsetispeed(&portset, B0); /* Do it... */ if (tcsetattr(p->fd, TCSANOW, &portset) == -1) { report(RPT_ERR, "%s: failed to configure port (%s)", drvthis->name, strerror(errno)); return -1; } /* Send initialization sequence */ for (i = 1; i <= 8; i++) { cmd[2] = i; if (write_(drvthis, cmd, sizeof(cmd)) == -1) return -1; } for (i = 0; i < sizeof(init_seq); i++) { if (write_(drvthis, (const unsigned char *)"\x0FE", 1) == -1 || write_(drvthis, &(init_seq[i]), 1) == -1) return -1; } return 0; } /** * Retrieves the display size from the device. * \param drvthis Pointer to driver structure. * \param width Pointer to variable storing display width * \param height Pointer to variable storing display height */ static void get_device_screen_size(Driver * drvthis, int *width, int *height) { unsigned char cmd[2] = {'\xFE', '\x76'}; char result[11]; if (write_(drvthis, cmd, sizeof(cmd)) == -1) return; if (read_(drvthis, result, sizeof(result)) != -1) { *width = (int)(result[0] - '0') * 10 + (int)(result[1] - '0'); *height = (int)(result[2] - '0') * 10 + (int)(result[3] - '0'); } } /** * Writes count bytes from a buffer to the device. * \param drvthis Pointer to driver structure * \param buf Pointer to the buffer * \param count Number of bytes to write * \retval -1 upon error */ static int write_(Driver * drvthis, const unsigned char *buf, size_t count) { int write_count = 0, written; PrivateData *p = drvthis->private_data; while (write_count < count) { written = write(p->fd, &buf[write_count], count - write_count); if (written == -1) { report(RPT_ERR, "SureElec: cannot write to port"); return -1; } write_count += written; } return write_count; } /** * Reads bytes in a buffer from the device. * \param drvthis Pointer to driver structure * \param buf Pointer to the buffer * \param count Number of bytes to read * \retval -1 upon error */ static int read_(Driver * drvthis, void *buf, size_t count) { fd_set rfds; struct timeval tv; PrivateData *p = drvthis->private_data; int read_count; /* Watch fd to see when it has input. */ FD_ZERO(&rfds); FD_SET(p->fd, &rfds); read_count = 0; while (read_count < count) { int retval; /* * For compatibility, better to reset the timeout delay (may * be modified in select() call). */ tv.tv_sec = 1; /* seconds */ tv.tv_usec = 0; /* microseconds */ retval = select(p->fd + 1, &rfds, NULL, NULL, &tv); if (retval) { int read_result = read(p->fd, ((char *)buf) + read_count, count - read_count); if (read_result < 0) { return -1; } else { read_count += read_result; } } else { report(RPT_ERR, "SureElec: No answer from device"); return -1; } } return read_count; }