1. Remove connection type header files and put prototypes locally and in glcd-drivers.h. Don't fall into the pitfall of too many header files again. 2. Create a separate frame buffer structure (this allows easier re-use of the frame buffer functions). 3. Add a function to retrieve pixel values from framebuffer. 4. Add the 'serdisplib' connection type. 5. Support display width that are not a multiple of 8 (e.g. 122x32).
218 lines
6.1 KiB
C
218 lines
6.1 KiB
C
/** \file server/drivers/glcd-serdisp.c
|
|
* Connection type using serdisplib for output (bridge driver).
|
|
*/
|
|
|
|
/*-
|
|
* Copyright (c) 2010 Bernhard Walle <bernhard@bwalle.de>
|
|
* 2011 Markus Dolze <bsdfan@nurfuerspam.de>
|
|
*
|
|
* This file is released under the GNU General Public License. Refer to the
|
|
* COPYING file distributed with this package.
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
# include "config.h"
|
|
#endif
|
|
|
|
#include <stdlib.h>
|
|
#include <strings.h>
|
|
#include <string.h>
|
|
#include <limits.h>
|
|
#include <serdisplib/serdisp.h>
|
|
|
|
#include "lcd.h"
|
|
#include "report.h"
|
|
#include "glcd-low.h"
|
|
|
|
#define SERDISPLIB_MAX_DISPLAYNAME 32
|
|
#define SERDISPLIB_MAX_DEVICENAME PATH_MAX
|
|
|
|
/* Prototypes */
|
|
void glcd_serdisp_blit(PrivateData *p);
|
|
void glcd_serdisp_close(PrivateData *p);
|
|
void glcd_serdisp_backlight(PrivateData *p, int state);
|
|
|
|
/** Private data for the serdisplib connection type */
|
|
typedef struct glcd_serdisp_data {
|
|
/** the name of the display driver in serdisplib, e.g. 'ctinclud' */
|
|
char display_name[SERDISPLIB_MAX_DISPLAYNAME];
|
|
|
|
/** the name of the device in serdisplib, e.g. /dev/parport0 */
|
|
char display_device[SERDISPLIB_MAX_DEVICENAME];
|
|
|
|
/** the serdisplib connection handle */
|
|
serdisp_CONN_t *serdisplib_conn;
|
|
|
|
/** the serdisplib handle */
|
|
serdisp_t *disp;
|
|
|
|
/** framebuffer for incremental updates */
|
|
struct glcd_framebuf bsbuf;
|
|
} CT_serdisp_data;
|
|
|
|
/**
|
|
* API: Initialize the connection type driver.
|
|
* \param drvthis Pointer to driver structure.
|
|
* \retval 0 Success.
|
|
* \retval <0 Error.
|
|
*/
|
|
int
|
|
glcd_serdisp_init(Driver *drvthis)
|
|
{
|
|
PrivateData *p = (PrivateData *)drvthis->private_data;
|
|
CT_serdisp_data *ct_data;
|
|
const char *s;
|
|
|
|
report(RPT_INFO, "glcd/serdisplib: intializing...");
|
|
|
|
/* Set up connection type low-level functions */
|
|
p->glcd_functions->blit = glcd_serdisp_blit;
|
|
p->glcd_functions->close = glcd_serdisp_close;
|
|
p->glcd_functions->set_backlight = glcd_serdisp_backlight;
|
|
|
|
/* Allocate memory structures */
|
|
ct_data = (CT_serdisp_data *) calloc(1, sizeof(CT_serdisp_data));
|
|
if (ct_data == NULL) {
|
|
report(RPT_ERR, "%s: error allocating connection data",
|
|
drvthis->name);
|
|
return -1;
|
|
}
|
|
p->ct_data = ct_data;
|
|
|
|
/* get the display name */
|
|
s = drvthis->config_get_string(drvthis->name, "serdisp_name", 0, NULL);
|
|
if (s == NULL) {
|
|
report(RPT_ERR, "%s: \'serdisp_name\' missing in configuration",
|
|
drvthis->name);
|
|
goto err_out;
|
|
}
|
|
strncpy(ct_data->display_name, s, SERDISPLIB_MAX_DISPLAYNAME);
|
|
ct_data->display_name[SERDISPLIB_MAX_DISPLAYNAME - 1] = '\0';
|
|
|
|
/* get the display device */
|
|
s = drvthis->config_get_string(drvthis->name, "serdisp_device", 0, NULL);
|
|
if (s == NULL) {
|
|
report(RPT_ERR, "%s: \'serdisp_device\' missing in configuration",
|
|
drvthis->name);
|
|
goto err_out;
|
|
}
|
|
strncpy(ct_data->display_device, s, SERDISPLIB_MAX_DEVICENAME);
|
|
ct_data->display_device[SERDISPLIB_MAX_DEVICENAME - 1] = '\0';
|
|
|
|
/* open the output device */
|
|
ct_data->serdisplib_conn = SDCONN_open(ct_data->display_device);
|
|
if (ct_data->serdisplib_conn == NULL) {
|
|
report(RPT_ERR, "Could not open %s: %s", ct_data->display_device,
|
|
sd_geterrormsg());
|
|
goto err_out;
|
|
}
|
|
|
|
/* open and initialize the display with options */
|
|
s = drvthis->config_get_string(drvthis->name, "serdisp_options", 0, "");
|
|
debug(RPT_INFO, "%s: Using serdisp options: %s", drvthis->name, s);
|
|
ct_data->disp = serdisp_init(ct_data->serdisplib_conn, ct_data->display_name, s);
|
|
if (ct_data->disp == NULL) {
|
|
report(RPT_ERR, "Error opening display %s: %s\n",
|
|
ct_data->display_name, sd_geterrormsg());
|
|
goto err_out;
|
|
}
|
|
|
|
/* set size overriding anything set in serdisp_options */
|
|
serdisp_setoption(ct_data->disp, "WIDTH", p->framebuf.px_width);
|
|
serdisp_setoption(ct_data->disp, "HEIGHT", p->framebuf.px_height);
|
|
|
|
/* allocate backing store based on frame buffer */
|
|
ct_data->bsbuf.px_width = p->framebuf.px_width;
|
|
ct_data->bsbuf.px_height = p->framebuf.px_height;
|
|
ct_data->bsbuf.bytesPerLine = p->framebuf.bytesPerLine;
|
|
ct_data->bsbuf.data = malloc(ct_data->bsbuf.px_height * ct_data->bsbuf.bytesPerLine);
|
|
if (ct_data->bsbuf.data == NULL) {
|
|
report(RPT_ERR, "%s: error allocating backing store",
|
|
drvthis->name);
|
|
}
|
|
memset(ct_data->bsbuf.data, 0x00, ct_data->bsbuf.px_height * ct_data->bsbuf.bytesPerLine);
|
|
|
|
serdisp_clearbuffer(ct_data->disp);
|
|
|
|
debug(RPT_INFO, "glcd/serdisplib: finished");
|
|
return 0;
|
|
|
|
err_out:
|
|
glcd_serdisp_close(p);
|
|
return -1;
|
|
}
|
|
|
|
|
|
/**
|
|
* API: Release low-level resources.
|
|
* \param p Pointer to glcd driver's private date structure.
|
|
*/
|
|
void
|
|
glcd_serdisp_close(PrivateData *p)
|
|
{
|
|
if (p->ct_data != NULL) {
|
|
CT_serdisp_data *ct_data = (CT_serdisp_data *) p->ct_data;
|
|
|
|
if (ct_data->serdisplib_conn && !ct_data->disp)
|
|
SDCONN_close(ct_data->serdisplib_conn);
|
|
if (ct_data->disp)
|
|
serdisp_quit(ct_data->disp);
|
|
if (ct_data->bsbuf.data) {
|
|
free(ct_data->bsbuf.data);
|
|
ct_data->bsbuf.data = NULL;
|
|
}
|
|
|
|
free(p->ct_data);
|
|
p->ct_data = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* API: Write the framebuffer to the display
|
|
* \param p Pointer to glcd driver's private date structure.
|
|
*/
|
|
void
|
|
glcd_serdisp_blit(PrivateData *p)
|
|
{
|
|
CT_serdisp_data *ct_data = (CT_serdisp_data *) p->ct_data;
|
|
int px, py;
|
|
int pixel_new, pixel_old;
|
|
|
|
/*
|
|
* Update method: go through the whole framebuffer line by line and
|
|
* compare each pixel with the one in the backing store. If different
|
|
* draw to serdisplib.
|
|
*/
|
|
for (py = 0; py < p->framebuf.px_height; py++) {
|
|
for (px = 0; px < p->framebuf.px_width; px++) {
|
|
pixel_old = fb_get_pixel(&(ct_data->bsbuf), px, py);
|
|
pixel_new = fb_get_pixel(&(p->framebuf), px, py);
|
|
if (pixel_old != pixel_new) {
|
|
serdisp_setcolour(ct_data->disp, px, py,
|
|
(pixel_new == FB_BLACK) ? SD_COL_BLACK : SD_COL_WHITE);
|
|
fb_draw_pixel(&(ct_data->bsbuf), px, py, pixel_new);
|
|
}
|
|
}
|
|
}
|
|
serdisp_update(ct_data->disp);
|
|
}
|
|
|
|
|
|
/**
|
|
* API: Turn the backlight on or off. The serdisp library does not support
|
|
* backlight levels. Any level set in glcd driver is ignored.
|
|
* \param p
|
|
* \param state
|
|
*/
|
|
void
|
|
glcd_serdisp_backlight(PrivateData *p, int state)
|
|
{
|
|
CT_serdisp_data *ct_data = (CT_serdisp_data *) p->ct_data;
|
|
|
|
if (state == BACKLIGHT_ON)
|
|
serdisp_setoption(ct_data->disp, "BACKLIGHT", SD_OPTION_YES);
|
|
else
|
|
serdisp_setoption(ct_data->disp, "BACKLIGHT", SD_OPTION_NO);
|
|
}
|