]> git.webhop.me Git - lcd4linux.git/commitdiff
DPF patch by superelchi
authormichael <michael@3ae390bd-cb1e-0410-b409-cd5a39f66f1f>
Thu, 23 May 2013 03:05:50 +0000 (03:05 +0000)
committermichael <michael@3ae390bd-cb1e-0410-b409-cd5a39f66f1f>
Thu, 23 May 2013 03:05:50 +0000 (03:05 +0000)
git-svn-id: https://ssl.bulix.org/svn/lcd4linux/trunk@1198 3ae390bd-cb1e-0410-b409-cd5a39f66f1f

configure.in
drivers.m4
drv_dpf.c

index e6fb646fda2755c217551bfd436b90aff4e4ab7d..477c5ae5220a136c6eee2585bde319d4a5c990ed 100644 (file)
@@ -87,9 +87,6 @@ AC_CHECK_HEADERS(serdisplib/serdisp.h, [has_serdisplib="true"], [has_serdisplib=
 # check for st2205 libs
 AC_CHECK_HEADERS(st2205.h, [has_st2205="true"], [has_st2205="false"])
 
-# check for libdpf libs
-AC_CHECK_HEADERS(libdpf/libdpf.h, [has_libdpf="true"], [has_libdpf="false"])
-
 # check for vncserver libs
 AC_CHECK_HEADERS(rfb/rfb.h, [has_vncserverlib="true"], [has_vncserverlib="false"])
 
index a692bf23bf0e23c66c79b834f5bacb7eee1d036b..c3b9d46c152a9b9efbc3a60cde7b02cbf14a70a5 100644 (file)
@@ -378,14 +378,10 @@ if test "$D4D" = "yes"; then
 fi
 
 if test "$DPF" = "yes"; then
-   if test "$has_libdpf" = "true"; then
-      GRAPHIC="yes"
-      DRIVERS="$DRIVERS drv_dpf.o"
-      DRVLIBS="$DRVLIBS -Llibdpf -ldpf -lusb"
-      AC_DEFINE(WITH_DPF,1,[DPF driver])
-   else
-      AC_MSG_WARN(libdpf.h not found: DPF driver disabled)
-   fi
+   GRAPHIC="yes"
+   DRIVERS="$DRIVERS drv_dpf.o"
+   LIBUSB="yes"
+   AC_DEFINE(WITH_DPF,1,[DPF driver])
 fi
 
 if test "$EA232graphic" = "yes"; then
index 39e1708f1fbe5ba5e17a5da4dce70df13fb3d66e..eff490ec394c1f1483ed531573f39f0ff73d9066 100644 (file)
--- a/drv_dpf.c
+++ b/drv_dpf.c
@@ -1,16 +1,17 @@
 /* $Id: drv_dpf.c 980 2009-01-28 21:18:52Z michux $
  * $URL: https://ssl.bulix.org/svn/lcd4linux/trunk/drv_dpf.c $
  *
- * Very basic hacked picture frame driver. Uses external libdpf.
- * This is a first working approach for AX206 based DPFs. In future,
- * more DPFs might be covered by that library. Work in progress.
+ * Driver for hacked digital picture frames. Uses *NO* external libraries.
+ * See http://dpf-ax.sourceforge.net/ for more information.
  *
- * See http://picframe.spritesserver.nl/ for more info.
- * 
  * Copyright (C) 2008 Jeroen Domburg <picframe@spritesmods.com>
  * Modified from sample code by:
  * Copyright (C) 2005 Michael Reinelt <michael@reinelt.co.at>
  * Copyright (C) 2005, 2006, 2007 The LCD4Linux Team <lcd4linux-devel@users.sourceforge.net>
+ * 
+ * Mods by <hackfin@section5.ch>
+ * Complete rewrite 05/2013 by superelchi <superelchi AT wolke7.net>
+ *
  *
  * This file is part of LCD4Linux.
  *
@@ -46,8 +47,6 @@
 #include <string.h>
 #include <errno.h>
 
-#include <libdpf/libdpf.h>
-
 #include "debug.h"
 #include "cfg.h"
 #include "qprintf.h"
 
 #include "drv_generic_graphic.h"
 
+//###################################################################
+// Start dpfcore4driver.h
+// See http://dpf-ax.sourceforge.net/
+//###################################################################
+
+#define DPFAXHANDLE void *     // Handle needed for dpf_ax access
+#define DPF_BPP 2              //bpp for dfp-ax is currently always 2!
+
+/**
+ * Open DPF device.
+ * 
+ * Device must be string in the form "usbX" or "dpfX", with X = 0 .. number of connected dpfs.
+ * The open function will scan the USB bus and return a handle to access dpf #X.
+ * If dpf #X is not found, returns NULL.
+ *
+ * \param dev  device name to open
+ * \return             device handle or NULL
+ */
+DPFAXHANDLE dpf_ax_open(const char *device);
+
+/**
+ *  Close DPF device.
+ */
+void dpf_ax_close(DPFAXHANDLE h);
+
+/** Blit data to screen.
+ *
+ * \param buf     buffer to 16 bpp RGB 565 image data
+ * \param rect    rectangle tuple: [x0, y0, x1, y1]
+ */
+void dpf_ax_screen_blit(DPFAXHANDLE h, const unsigned char *buf, short rect[4]);
+
+/** Set backlight brightness.
+ * 
+ * \param value       Backlight value 0..7 (0 = off, 7 = max brightness)
+ */
+void dpf_ax_setbacklight(DPFAXHANDLE h, int value);
+
+/** Get screen width.
+ * 
+ * \return width in pixel
+ */
+int dpf_ax_getwidth(DPFAXHANDLE h);
+
+/** Get screen height.
+ * 
+ * \return height in pixel
+ */
+int dpf_ax_getheight(DPFAXHANDLE h);
+
+//###################################################################
+// End dpfcore4driver.h
+//###################################################################
+
+
 static char Name[] = "DPF";
 
-static DPFContext *g_h;
 
-/* Display data */
-static unsigned char *g_fb;
+/*
+ * Dpf status
+ */
+static struct {
+    unsigned char *lcdBuf;         // Display data buffer
+    unsigned char *xferBuf;    // USB transfer buffer
+    DPFAXHANDLE dpfh;          // Handle for dpf access
+    int pwidth;                                // Physical display width
+    int pheight;                       // Physical display height
 
-static int drv_dpf_open(const char *section)
-{
-    int error;
-    char *dev;
+    // Flags to translate logical to physical orientation
+    int isPortrait;
+    int rotate90;
+    int flip;
 
-    // Currently, the Port specification is unused.
+    // Current dirty rectangle
+    int minx, maxx;
+    int miny, maxy;
 
-    dev = cfg_get(section, "Port", NULL);
-    if (dev == NULL || *dev == '\0') {
-       error("dpf: no '%s.Port' entry from %s", section, cfg_source());
-       return -1;
-    }
+    // Config properties
+    int orientation;
+    int backlight;
+} dpf;
 
-    error = dpf_open(NULL, &g_h);
-    if (error < 0) {
-       error("dpf: cannot open dpf device %s", dev);
-       return -1;
-    }
 
-    return 0;
-}
+// Convert RGBA pixel to RGB565 pixel(s)
 
+#define _RGB565_0(p) (( ((p.R) & 0xf8)      ) | (((p.G) & 0xe0) >> 5))
+#define _RGB565_1(p) (( ((p.G) & 0x1c) << 3 ) | (((p.B) & 0xf8) >> 3))
 
-static int drv_dpf_close(void)
+/*
+ * Set one pixel in lcdBuf.
+ * 
+ * Respects orientation and updates dirty rectangle.
+ *
+ * in: x, y - pixel coordinates
+ *     pix  - RGBA pixel value
+ * out:        -
+ */
+static void drv_set_pixel(int x, int y, RGBA pix)
 {
-    dpf_close(g_h);
+    int changed = 0;
+    
+    int sx = DCOLS;
+    int sy = DROWS;
+    int lx = x % sx;
+    int ly = y % sy;
+
+    if (dpf.flip) {
+       // upside down orientation
+        lx = DCOLS - 1 - lx;
+        ly = DROWS - 1 - ly;
+    }
 
-    return 0;
-}
+    if (dpf.rotate90) {
+        // wrong Orientation, rotate
+        int i = ly;
+        ly = dpf.pheight - 1 - lx;
+        lx = i;
+    }
+
+    if (lx < 0 || lx >= (int) dpf.pwidth || ly < 0 || ly >= (int) dpf.pheight)
+    {
+        error("dpf: x/y out of bounds (x=%d, y=%d, rot=%d, flip=%d, lx=%d, ly=%d)\n", x, y, dpf.rotate90, dpf.flip, lx, ly);
+        return;
+    }
 
-#define _RGB565_0(p) \
-       (( ((p.R) & 0xf8)      ) | (((p.G) & 0xe0) >> 5))
-#define _RGB565_1(p) \
-       (( ((p.G) & 0x1c) << 3 ) | (((p.B) & 0xf8) >> 3))
+    unsigned char c1 = _RGB565_0(pix);
+    unsigned char c2 = _RGB565_1(pix);
+    unsigned int i = (ly * dpf.pwidth + lx) * DPF_BPP;
+    if (dpf.lcdBuf[i] != c1 || dpf.lcdBuf[i+1] != c2)
+    {
+       dpf.lcdBuf[i]   = c1;
+       dpf.lcdBuf[i+1] = c2;
+       changed = 1;
+    }
 
+    if (changed)
+    {
+        if (lx < dpf.minx) dpf.minx = lx;
+        if (lx > dpf.maxx) dpf.maxx = lx;
+        if (ly < dpf.miny) dpf.miny = ly;
+        if (ly > dpf.maxy) dpf.maxy = ly;
+    }
+}
+
+/*
+ * Send pixel data to dpf
+ */
 static void drv_dpf_blit(const int row, const int col, const int height, const int width)
 {
-    int r, c;
-    short rect[4];
-    unsigned long i;
-    RGBA p;
-    unsigned char *pix;
-
-    pix = g_fb;
-    for (r = row; r < row + height; r++) {
-       for (c = col; c < col + width; c++) {
-           p = drv_generic_graphic_rgb(r, c);
-           *pix++ = _RGB565_0(p);
-           *pix++ = _RGB565_1(p);
-       }
+    int x, y;
+
+    // Set pixels one by one
+    // Note: here is room for optimization :-)
+    for (y = row; y < row + height; y++)
+       for (x = col; x < col + width; x++)
+           drv_set_pixel(x, y, drv_generic_graphic_rgb(y, x));
+
+    // If nothing has changed, skip transfer
+    if (dpf.minx > dpf.maxx || dpf.miny > dpf.maxy)
+       return;
+
+    // Copy data in dirty rectangle from data buffer to temp transfer buffer
+    unsigned int cpylength = (dpf.maxx - dpf.minx + 1) * DPF_BPP;
+    unsigned char *ps = dpf.lcdBuf + (dpf.miny * dpf.pwidth + dpf.minx) * DPF_BPP;
+    unsigned char *pd = dpf.xferBuf;
+    for (y = dpf.miny; y <= dpf.maxy; y++) {
+       memcpy(pd, ps, cpylength);
+       ps += dpf.pwidth * DPF_BPP;
+       pd += cpylength;
     }
-    rect[0] = col;
-    rect[1] = row;
-    rect[2] = col + width;
-    rect[3] = row + height;
-    dpf_screen_blit(g_h, g_fb, rect);
+
+    // Send the buffer
+    short rect[4];
+    rect[0] = dpf.minx;
+    rect[1] = dpf.miny;
+    rect[2] = dpf.maxx + 1;
+    rect[3] = dpf.maxy + 1;
+    dpf_ax_screen_blit(dpf.dpfh, dpf.xferBuf, rect);
+
+    // Reset dirty rectangle
+    dpf.minx = dpf.pwidth - 1;
+    dpf.maxx = 0;
+    dpf.miny = dpf.pheight - 1;
+    dpf.maxy = 0;
 }
 
 
 /* start graphic display */
-static int drv_dpf_start2(const char *section)
+static int drv_dpf_start(const char *section)
 {
+    int i;
+    char *dev;
     char *s;
 
+    // Check if config is valid
+
+    // Get the device
+    dev = cfg_get(section, "Port", NULL);
+    if (dev == NULL || *dev == '\0') {
+        error("dpf: no '%s.Port' entry from %s", section, cfg_source());
+        return -1;
+    }
+
+    // Get font
     s = cfg_get(section, "Font", "6x8");
     if (s == NULL || *s == '\0') {
-       error("%s: no '%s.Font' entry from %s", Name, section, cfg_source());
-       return -1;
+        error("%s: no '%s.Font' entry from %s", Name, section, cfg_source());
+        return -1;
     }
 
     XRES = -1;
     YRES = -1;
     if (sscanf(s, "%dx%d", &XRES, &YRES) != 2 || XRES < 1 || YRES < 1) {
-       error("%s: bad Font '%s' from %s", Name, s, cfg_source());
-       return -1;
+        error("%s: bad Font '%s' from %s", Name, s, cfg_source());
+        return -1;
     }
 
-    /* Fixme: provider other fonts someday... */
-    /* Overridden - we have scaled the textout drawing */
-/*  if (XRES != 6 && YRES != 8) {
-       error("%s: bad Font '%s' from %s (only 6x8 at the moment)", Name, s, cfg_source());
-       return -1;
-    } */
-
     /* we dont want fonts below 6 width */
     if (XRES < 6) {
-       error("%s: bad Font '%s' width '%d' using minimum of 6)", Name, s, XRES);
-       XRES = 6;
+        error("%s: bad Font '%s' width '%d' using minimum of 6)", Name, s, XRES);
+        XRES = 6;
     }
 
     /* we dont want fonts below 8 height */
     if (YRES < 8) {
-       error("%s: bad Font '%s' height '%d' using minimum of 8)", Name, s, YRES);
-       YRES = 8;
+        error("%s: bad Font '%s' height '%d' using minimum of 8)", Name, s, YRES);
+        YRES = 8;
     }
 
+    // Get the logical orientation (0 = landscape, 1 = portrait, 2 = reverse landscape, 3 = reverse portrait)
+    if (cfg_number(section, "Orientation", 0, 0, 3, &i) > 0)
+        dpf.orientation = i;
+    else
+        dpf.orientation = 0;
+
+    // Get the backlight value (0 = off, 7 = max brightness)
+    if (cfg_number(section, "Backlight", 0, 0, 7, &i) > 0)
+        dpf.backlight = i;
+    else
+        dpf.backlight = 7;
+
     /* open communication with the display */
-    if (drv_dpf_open(section) < 0) {
-       return -1;
+    dpf.dpfh = dpf_ax_open(dev);
+    if (dpf.dpfh == NULL) {
+        error("dpf: cannot open dpf device %s", dev);
+        return -1;
     }
 
-    /* you surely want to allocate a framebuffer or something... */
-    g_fb = malloc(g_h->height * g_h->width * g_h->bpp);
-
-    /* set width/height from dpf firmware specs */
-    DROWS = g_h->height;
-    DCOLS = g_h->width;
+    // Get dpfs physical dimensions
+    dpf.pwidth = dpf_ax_getwidth(dpf.dpfh);
+    dpf.pheight = dpf_ax_getheight(dpf.dpfh);
+    
 
+    // See, if we have to rotate the display
+    dpf.isPortrait = dpf.pwidth < dpf.pheight;
+    if (dpf.isPortrait) {
+       if (dpf.orientation == 0 || dpf.orientation == 2)
+           dpf.rotate90 = 1;
+    }
+    else
+       if (dpf.orientation == 1 || dpf.orientation == 3)
+           dpf.rotate90 = 1;
+    dpf.flip = (!dpf.isPortrait && dpf.rotate90);      // adjust to make rotate por/land = physical por/land
+    if (dpf.orientation > 1) dpf.flip = !dpf.flip;
+
+    // allocate display buffer + temp transfer buffer
+    dpf.lcdBuf = malloc(dpf.pwidth * dpf.pheight * DPF_BPP);
+    dpf.xferBuf = malloc(dpf.pwidth * dpf.pheight * DPF_BPP);
+
+    // clear display buffer + set it to "dirty"
+    memset(dpf.lcdBuf, 0, dpf.pwidth * dpf.pheight * DPF_BPP);       //Black
+    dpf.minx = 0;
+    dpf.maxx = dpf.pwidth - 1;
+    dpf.miny = 0;
+    dpf.maxy = dpf.pheight - 1;
+
+    // set the logical width/height for lcd4linux
+    DCOLS = ((!dpf.rotate90) ? dpf.pwidth : dpf.pheight);
+    DROWS = ((!dpf.rotate90) ? dpf.pheight : dpf.pwidth);
+
+    // Set backlight (brightness)
+    dpf_ax_setbacklight(dpf.dpfh, dpf.backlight);
+    
     return 0;
 }
 
+
 /****************************************/
 /***            plugins               ***/
 /****************************************/
 
 static void plugin_backlight(RESULT * result, RESULT * arg1)
 {
-    int bl_on;
-    bl_on = (R2N(arg1) == 0 ? 0 : 1);
-    dpf_backlight(g_h, bl_on);
-    SetResult(&result, R_NUMBER, &bl_on);
+    int b = R2N(arg1);
+    if (b < 0) b = 0;
+    if (b > 7) b = 7;
+    
+    dpf_ax_setbacklight(dpf.dpfh, b);
+    SetResult(&result, R_NUMBER, &b);
 }
 
 
-/****************************************/
-/***        widget callbacks          ***/
-/****************************************/
-
-
-/* using drv_generic_text_draw(W) */
-/* using drv_generic_text_icon_draw(W) */
-/* using drv_generic_text_bar_draw(W) */
-/* using drv_generic_gpio_draw(W) */
-
-
 /****************************************/
 /***        exported functions        ***/
 /****************************************/
@@ -211,13 +370,13 @@ static void plugin_backlight(RESULT * result, RESULT * arg1)
 /* list models */
 int drv_dpf_list(void)
 {
-    printf("generic hacked photo frame");
+    printf("Hacked dpf-ax digital photo frame");
     return 0;
 }
 
 
 /* initialize driver & display */
-int drv_dpf_init2(const char *section, const int quiet)
+int drv_dpf_init(const char *section, const int quiet)
 {
     int ret;
 
@@ -225,7 +384,7 @@ int drv_dpf_init2(const char *section, const int quiet)
     drv_generic_graphic_real_blit = drv_dpf_blit;
 
     /* start display */
-    if ((ret = drv_dpf_start2(section)) != 0)
+    if ((ret = drv_dpf_start(section)) != 0)
        return ret;
 
     /* initialize generic graphic driver */
@@ -249,7 +408,7 @@ int drv_dpf_init2(const char *section, const int quiet)
 
 
 /* close driver & display */
-int drv_dpf_quit2(const int quiet)
+int drv_dpf_quit(const int quiet)
 {
     info("%s: shutting down.", Name);
 
@@ -264,7 +423,7 @@ int drv_dpf_quit2(const int quiet)
     drv_generic_graphic_quit();
 
     debug("closing connection");
-    drv_dpf_close();
+    dpf_ax_close(dpf.dpfh);
 
     return (0);
 }
@@ -273,6 +432,293 @@ int drv_dpf_quit2(const int quiet)
 DRIVER drv_DPF = {
     .name = Name,
     .list = drv_dpf_list,
-    .init = drv_dpf_init2,
-    .quit = drv_dpf_quit2,
+    .init = drv_dpf_init,
+    .quit = drv_dpf_quit,
+};
+
+//###################################################################
+// Start dpfcore4driver.c
+// See http://dpf-ax.sourceforge.net/
+//###################################################################
+
+#include <usb.h>
+
+#define AX206_VID 0x1908               // Hacked frames USB Vendor ID
+#define AX206_PID 0x0102               // Hacked frames USB Product ID
+
+#define USBCMD_SETPROPERTY  0x01       // USB command: Set property
+#define USBCMD_BLIT         0x12       // USB command: Blit to screen
+
+/* Generic SCSI device stuff */
+
+#define DIR_IN  0
+#define DIR_OUT 1
+
+/* The DPF context structure */
+typedef
+struct dpf_context {
+       usb_dev_handle *udev;
+       unsigned int width;
+       unsigned int height;
+} DPFContext;
+
+static int wrap_scsi(DPFContext *h, unsigned char *cmd, int cmdlen, char out,
+       unsigned char *data, unsigned long block_len);
+
+/**
+ * Open DPF device.
+ * 
+ * Device must be string in the form "usbX" or "dpfX", with X = 0 .. number of connected dpfs.
+ * The open function will scan the USB bus and return a handle to access dpf #X.
+ * If dpf #X is not found, returns NULL.
+ *
+ * \param dev  device name to open
+ * \return             device handle or NULL
+ */
+DPFAXHANDLE dpf_ax_open(const char *dev)
+{
+       DPFContext *dpf;
+       int index = -1;
+       usb_dev_handle *u;
+
+       if (dev && strlen(dev) == 4 && (strncmp(dev, "usb", 3) == 0 || strncmp(dev, "dpf", 3) == 0))
+               index = dev[3] - '0';
+               
+       if (index < 0 || index > 9) {
+               fprintf(stderr, "dpf_ax_open: wrong device '%s'. Please specify a string like 'usb0'\n", dev);
+               return NULL;
+       }
+
+       usb_init();
+       usb_find_busses();
+       usb_find_devices();
+
+       struct usb_bus *b = usb_get_busses();
+       struct usb_device *d = NULL;
+       int enumeration = 0;
+       int found = 0;
+
+       while (b && !found) {
+               d = b->devices;
+               while (d) {
+                       if ((d->descriptor.idVendor == AX206_VID) && (d->descriptor.idProduct == AX206_PID)) { 
+                               fprintf(stderr, "dpf_ax_open: found AX206 #%d\n", enumeration+1);
+                               if (enumeration == index) {
+                                       found = 1;
+                                       break;
+                               }
+                               else enumeration++;
+                       }
+                       d = d->next;
+               }
+               b = b->next;
+       }
+
+       if (!d) {
+               fprintf(stderr,"dpf_ax_open: no matching USB device '%s' found!\n", dev);
+               return NULL;
+       }
+
+       dpf = (DPFContext *) malloc(sizeof(DPFContext));
+       if (!dpf) {
+               fprintf(stderr, "dpf_ax_open: error allocation memory.\n");
+               return NULL;
+       }
+
+       u = usb_open(d);
+       if (u == NULL) {
+               fprintf(stderr,"dpf_ax_open: failed to open usb device '%s'!\n", dev);
+               free(dpf);
+               return NULL;
+       }
+
+       if (usb_claim_interface(u, 0) < 0) {
+               fprintf(stderr,"dpf_ax_open: failed to claim usb device!\n");
+               usb_close(u);
+               free(dpf);
+               return NULL;
+       }
+
+       dpf->udev = u;
+
+       static unsigned char buf[5];
+       static unsigned char cmd[16] = {
+               0xcd, 0, 0, 0,
+                  0, 2, 0, 0,
+                  0, 0, 0, 0,
+                  0, 0, 0, 0
+       };
+       cmd[5] = 2; // get LCD parameters
+       if (wrap_scsi(dpf, cmd, sizeof(cmd), DIR_IN, buf, 5) == 0) {
+               dpf->width = (buf[0]) | (buf[1] << 8);
+               dpf->height = (buf[2]) | (buf[3] << 8);
+               fprintf(stderr, "dpf_ax_open: got LCD dimensions: %dx%d\n", dpf->width, dpf->height);
+       }
+       else {
+               fprintf(stderr, "dpf_ax_open: error reading LCD dimensions!\n");
+               dpf_ax_close(dpf);
+               return NULL;
+       }
+       return (DPFAXHANDLE) dpf;
+}
+
+/**
+ *  Close DPF device
+ */
+
+void dpf_ax_close(DPFAXHANDLE h)
+{
+       DPFContext *dpf = (DPFContext *) h;
+       
+       usb_release_interface(dpf->udev, 0);
+       usb_close(dpf->udev);
+       free(dpf);
+}
+
+/** Get screen width.
+ * 
+ * \return width in pixel
+ */
+int dpf_ax_getwidth(DPFAXHANDLE h)
+{
+       return ((DPFContext *) h)->width;
+}
+
+/** Get screen height.
+ * 
+ * \return height in pixel
+ */
+int dpf_ax_getheight(DPFAXHANDLE h)
+{
+       return ((DPFContext *) h)->height;
+}
+
+static
+unsigned char g_excmd[16] = {
+       0xcd, 0, 0, 0,
+          0, 6, 0, 0,
+          0, 0, 0, 0,
+          0, 0, 0, 0
 };
+
+/** Blit data to screen.
+ *
+ * \param buf     buffer to 16 bpp RGB 565 image data
+ * \param rect    rectangle tuple: [x0, y0, x1, y1]
+ */
+void dpf_ax_screen_blit(DPFAXHANDLE h, const unsigned char *buf, short rect[4])
+{
+       unsigned long len = (rect[2] - rect[0]) * (rect[3] - rect[1]);
+       len <<= 1;
+       unsigned char *cmd = g_excmd;
+
+       cmd[6] = USBCMD_BLIT;
+       cmd[7] = rect[0];
+       cmd[8] = rect[0] >> 8;
+       cmd[9] = rect[1];
+       cmd[10] = rect[1] >> 8;
+       cmd[11] = rect[2] - 1;
+       cmd[12] = (rect[2] - 1) >> 8;
+       cmd[13] = rect[3] - 1;
+       cmd[14] = (rect[3] - 1) >> 8;
+       cmd[15] = 0;
+
+       wrap_scsi((DPFContext *) h, cmd, sizeof(g_excmd), DIR_OUT, (unsigned char*) buf, len);
+}
+
+/** Set backlight
+ * 
+ * \param value       Backlight value 0..7 (0 = off, 7 = max brightness)
+ */
+void dpf_ax_setbacklight(DPFAXHANDLE h, int b)
+{
+       unsigned char *cmd = g_excmd;
+
+       if (b < 0) b = 0;
+    if (b > 7) b = 7;
+
+       cmd[6] = USBCMD_SETPROPERTY;
+       cmd[7] = 0x01;          // PROPERTY_BRIGHTNESS
+       cmd[8] = 0x00;      //PROPERTY_BRIGHTNESS >> 8;
+       cmd[9] = b;
+       cmd[10] = b >> 8;
+
+       wrap_scsi((DPFContext *) h, cmd, sizeof(g_excmd), DIR_OUT, NULL, 0);
+}
+
+
+static unsigned char g_buf[] = {
+       0x55, 0x53, 0x42, 0x43, // dCBWSignature
+       0xde, 0xad, 0xbe, 0xef, // dCBWTag
+       0x00, 0x80, 0x00, 0x00, // dCBWLength
+       0x00, // bmCBWFlags: 0x80: data in (dev to host), 0x00: Data out
+       0x00, // bCBWLUN
+       0x10, // bCBWCBLength
+
+       // SCSI cmd:
+       0xcd, 0x00, 0x00, 0x00,
+       0x00, 0x06, 0x11, 0xf8,
+       0x70, 0x00, 0x40, 0x00,
+       0x00, 0x00, 0x00, 0x00,
+};
+
+#define ENDPT_OUT 1
+#define ENDPT_IN 0x81
+
+static int wrap_scsi(DPFContext *h, unsigned char *cmd, int cmdlen, char out,
+       unsigned char *data, unsigned long block_len)
+{
+       int len;
+       int ret;
+       static unsigned char ansbuf[13]; // Do not change size.
+
+       g_buf[14] = cmdlen;
+       memcpy(&g_buf[15], cmd, cmdlen);
+
+       g_buf[8] = block_len;
+       g_buf[9] = block_len >> 8;
+       g_buf[10] = block_len >> 16;
+       g_buf[11] = block_len >> 24;
+
+       ret = usb_bulk_write(h->udev, ENDPT_OUT, (const char *)g_buf, sizeof(g_buf), 1000);
+       if (ret < 0) return ret;
+
+       if (out == DIR_OUT) {
+               if (data) {
+                       ret = usb_bulk_write(h->udev, ENDPT_OUT, (const char* )data, block_len, 3000);
+                       if (ret != (int) block_len) {
+                               fprintf(stderr, "dpf_ax ERROR: bulk write.\n");
+                               return ret;
+                       }
+               }
+       } else if (data) {
+               ret = usb_bulk_read(h->udev, ENDPT_IN, (char *) data, block_len, 4000);
+               if (ret != (int) block_len) {
+                       fprintf(stderr, "dpf_ax ERROR: bulk read.\n");
+                       return ret;
+               }
+       }
+       // get ACK:
+       len = sizeof(ansbuf);
+       int retry = 0;
+       int timeout = 0;
+       do {
+               timeout = 0;
+               ret = usb_bulk_read(h->udev, ENDPT_IN, (char *) ansbuf, len, 5000);
+               if (ret != len) {
+                       fprintf(stderr, "dpf_ax ERROR: bulk ACK read.\n");
+                       timeout = 1;
+               }
+               retry++;
+       } while (timeout && retry < 5);
+       if (strncmp((char *) ansbuf, "USBS", 4)) {
+               fprintf(stderr, "dpf_ax ERROR: got invalid reply\n.");
+               return -1;
+       }
+       // pass back return code set by peer:
+       return ansbuf[12];
+}
+
+//###################################################################
+// End dpfcore4driver.c
+//###################################################################