/* $Id: drv_dpf.c 980 2009-01-28 21:18:52Z michux $ * $URL: https://ssl.bulix.org/svn/lcd4linux/trunk/drv_dpf.c $ * * Driver for hacked digital picture frames. Uses *NO* external libraries. * See http://dpf-ax.sourceforge.net/ for more information. * * Copyright (C) 2008 Jeroen Domburg * Modified from sample code by: * Copyright (C) 2005 Michael Reinelt * Copyright (C) 2005, 2006, 2007 The LCD4Linux Team * * Mods by * Complete rewrite 05/2013 by superelchi * * * This file is part of LCD4Linux. * * LCD4Linux 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, or (at your option) * any later version. * * LCD4Linux 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., 675 Mass Ave, Cambridge, MA 02139, USA. * */ /* * * exported fuctions: * * struct DRIVER drv_DPF * */ #include "config.h" #include #include #include #include #include #include "debug.h" #include "cfg.h" #include "qprintf.h" #include "udelay.h" #include "plugin.h" #include "widget.h" #include "widget_text.h" #include "widget_icon.h" #include "widget_bar.h" #include "drv.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"; /* * 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 // Flags to translate logical to physical orientation int isPortrait; int rotate90; int flip; // Current dirty rectangle int minx, maxx; int miny, maxy; // Config properties int orientation; int backlight; } dpf; // 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)) /* * 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) { 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; } 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; } 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 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; } // 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_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; } 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; } /* 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; } /* 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; } // 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 */ dpf.dpfh = dpf_ax_open(dev); if (dpf.dpfh == NULL) { error("dpf: cannot open dpf device %s", dev); return -1; } // 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 b = R2N(arg1); if (b < 0) b = 0; if (b > 7) b = 7; dpf_ax_setbacklight(dpf.dpfh, b); SetResult(&result, R_NUMBER, &b); } /****************************************/ /*** exported functions ***/ /****************************************/ /* list models */ int drv_dpf_list(void) { printf("Hacked dpf-ax digital photo frame"); return 0; } /* initialize driver & display */ int drv_dpf_init(const char *section, const int quiet) { int ret; /* real worker functions */ drv_generic_graphic_real_blit = drv_dpf_blit; /* start display */ if ((ret = drv_dpf_start(section)) != 0) return ret; /* initialize generic graphic driver */ if ((ret = drv_generic_graphic_init(section, Name)) != 0) return ret; if (!quiet) { char buffer[40]; qprintf(buffer, sizeof(buffer), "%s %dx%d", Name, DCOLS, DROWS); if (drv_generic_graphic_greet(buffer, NULL)) { sleep(3); drv_generic_graphic_clear(); } } /* register plugins */ AddFunction("LCD::backlight", 1, plugin_backlight); return 0; } /* close driver & display */ int drv_dpf_quit(const int quiet) { info("%s: shutting down.", Name); /* clear display */ drv_generic_graphic_clear(); /* say goodbye... */ if (!quiet) { drv_generic_graphic_greet("goodbye!", NULL); } drv_generic_graphic_quit(); debug("closing connection"); dpf_ax_close(dpf.dpfh); return (0); } DRIVER drv_DPF = { .name = Name, .list = drv_dpf_list, .init = drv_dpf_init, .quit = drv_dpf_quit, }; //################################################################### // Start dpfcore4driver.c // See http://dpf-ax.sourceforge.net/ //################################################################### #include #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 //###################################################################