/* * targavfd plugin for VDR (C++) * * (C) 2010-2011 Andreas Brachold * This targavfd plugin 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, version 3 of the License. * * See the files README and COPYING for details. * */ #include #include #include #include #include #include #include "setup.h" #include "ffont.h" #include "vfd.h" // Values for transaction's data packet. static const int CONTROL_REQUEST_TYPE_OUT = LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE; // From the HID spec: static const int HID_SET_REPORT = 0x09; static const int HID_REPORT_TYPE_OUTPUT = 0x02; static const int unsigned MAX_CONTROL_OUT_TRANSFER_SIZE = 63; static const int INTERFACE_NUMBER = 0; static const int TIMEOUT_MS = 5000; // Defines from display datasheet static const int VENDOR_ID = 0x019c2; static const int PRODUCT_ID = 0x06a11; static const unsigned char ICON_PLAY = 0x00; //Play static const unsigned char ICON_PAUSE = 0x01; //Pause static const unsigned char ICON_RECORD = 0x02; //Record static const unsigned char ICON_MESSAGE = 0x03; //Message symbol (without the inner @) static const unsigned char ICON_MSGAT = 0x04; //Message @ static const unsigned char ICON_MUTE = 0x05; //Mute static const unsigned char ICON_WLAN1 = 0x06; //WLAN (tower base) static const unsigned char ICON_WLAN2 = 0x07; //WLAN strength (1 of 3) static const unsigned char ICON_WLAN3 = 0x08; //WLAN strength (2 of 3) static const unsigned char ICON_WLAN4 = 0x09; //WLAN strength (3 of 3) static const unsigned char ICON_VOLUME = 0x0A; //Volume (the word) static const unsigned char ICON_VOL1 = 0x0B; //Volume level 1 of 14 static const unsigned char ICON_VOL2 = 0x0C; //Volume level 2 of 14 static const unsigned char ICON_VOL3 = 0x0D; //Volume level 3 of 14 static const unsigned char ICON_VOL4 = 0x0E; //Volume level 4 of 14 static const unsigned char ICON_VOL5 = 0x0F; //Volume level 5 of 14 static const unsigned char ICON_VOL6 = 0x10; //Volume level 6 of 14 static const unsigned char ICON_VOL7 = 0x11; //Volume level 7 of 14 static const unsigned char ICON_VOL8 = 0x12; //Volume level 8 of 14 static const unsigned char ICON_VOL9 = 0x13; //Volume level 9 of 14 static const unsigned char ICON_VOL10 = 0x14; //Volume level 10 of 14 static const unsigned char ICON_VOL11 = 0x15; //Volume level 11 of 14 static const unsigned char ICON_VOL12 = 0x16; //Volume level 12 of 14 static const unsigned char ICON_VOL13 = 0x17; //Volume level 13 of 14 static const unsigned char ICON_VOL14 = 0x18; //Volume level 14 of 14 static const unsigned char STATE_OFF = 0x00; //Symbol off static const unsigned char STATE_ON = 0x01; //Symbol on static const unsigned char STATE_ONHIGH = 0x02; //Symbol on, high intensity, can only be used with the volume symbols static const unsigned char CMD_PREFIX = 0x1b; static const unsigned char CMD_SETCLOCK = 0x00; //Actualize the time of the display static const unsigned char CMD_SMALLCLOCK = 0x01; //Display small clock on display static const unsigned char CMD_BIGCLOCK = 0x02; //Display big clock on display static const unsigned char CMD_SETSYMBOL = 0x30; //Enable or disable symbol static const unsigned char CMD_SETDIMM = 0x40; //Set the dimming level of the display static const unsigned char CMD_RESET = 0x50; //Reset all configuration data to default and clear static const unsigned char CMD_SETRAM = 0x60; //Set the actual graphics RAM offset for next data write static const unsigned char CMD_SETPIXEL = 0x70; //Write pixel data to RAM of the display static const unsigned char CMD_TEST1 = 0xf0; //Show vertical test pattern static const unsigned char CMD_TEST2 = 0xf1; //Show horizontal test pattern static const unsigned char TIME_12 = 0x00; //12 hours format static const unsigned char TIME_24 = 0x01; //24 hours format static const unsigned char BRIGHT_OFF = 0x00; //Display off static const unsigned char BRIGHT_DIMM = 0x01; //Display dimmed static const unsigned char BRIGHT_FULL = 0x02; //Display full brightness cVFDQueue::cVFDQueue() { devh = NULL; bInit = false; } cVFDQueue::~cVFDQueue() { cVFDQueue::close(); } bool cVFDQueue::open() { int result; bool ready = false; dsyslog("targaVFD: scanning for Futaba MDM166A..."); bInit = true; //Initialize libusb result = libusb_init(NULL); if (result >= 0) { devh = libusb_open_device_with_vid_pid(NULL, VENDOR_ID, PRODUCT_ID); if (devh != NULL) { // a targavfd has been detected. // Detach the hidusb driver from the HID to enable using libusb. libusb_detach_kernel_driver(devh, INTERFACE_NUMBER); { result = libusb_claim_interface(devh, INTERFACE_NUMBER); if (result >= 0) { ready = true; } else { esyslog("targaVFD: libusb_claim_interface error! %s (%d)",usberror(result),result); } } } else { esyslog("targaVFD: Unable to find the device!"); } } else { esyslog("targaVFD: Unable to initialize libusb! %s (%d)",usberror(result),result); } if(!ready) { if(devh) libusb_release_interface(devh, 0); devh = NULL; } while (!empty()) { pop(); } return ready; } void cVFDQueue::close() { if (devh != NULL) { int result = libusb_release_interface(devh, 0); if (result < 0) { esyslog("targaVFD: libusb_release_interface failed! %s (%d)",usberror(result),result); } libusb_close(devh); devh = NULL; } if(bInit) { // Deinitialize libusb libusb_exit(NULL); bInit = false; } } void cVFDQueue::QueueCmd(const unsigned char & cmd) { this->push(CMD_PREFIX); this->push(cmd); } void cVFDQueue::QueueData(const unsigned char & data) { this->push(data); } bool cVFDQueue::QueueFlush() { if(empty()) return true; if(!isopen()) { return false; } int bytes; unsigned char buf[MAX_CONTROL_OUT_TRANSFER_SIZE+1]; while (!empty()) { buf[0] = (unsigned char) std::min((size_t)MAX_CONTROL_OUT_TRANSFER_SIZE,size()); for(unsigned int i = 0;i < MAX_CONTROL_OUT_TRANSFER_SIZE && !empty();++i) { buf[i+1] = (char) front(); //the first element in the queue pop(); //remove the first element of the queue } bytes = libusb_control_transfer( devh, CONTROL_REQUEST_TYPE_OUT , HID_SET_REPORT, (HID_REPORT_TYPE_OUTPUT<<8)|0x00, INTERFACE_NUMBER, buf, (((int)buf[0]) + 1), TIMEOUT_MS); if (bytes <= 0) { esyslog("targaVFD: libusb_control_transfer failed : %s (%d)",usberror(bytes),bytes); while (!empty()) { pop(); } cVFDQueue::close(); return false; } } return true; } const char *cVFDQueue::usberror(int ret) const { switch(ret) { case LIBUSB_SUCCESS: return "Success (no error)."; case LIBUSB_ERROR_IO: return "Input/output error."; case LIBUSB_ERROR_INVALID_PARAM: return "Invalid parameter."; case LIBUSB_ERROR_ACCESS: return "Access denied (insufficient permissions)."; case LIBUSB_ERROR_NO_DEVICE: return "No such device (it may have been disconnected)."; case LIBUSB_ERROR_NOT_FOUND: return "Entity not found."; case LIBUSB_ERROR_BUSY: return "Resource busy."; case LIBUSB_ERROR_TIMEOUT: return "Operation timed out."; case LIBUSB_ERROR_OVERFLOW: return "Overflow."; case LIBUSB_ERROR_PIPE: return "Pipe error."; case LIBUSB_ERROR_INTERRUPTED: return "System call interrupted (perhaps due to signal)."; case LIBUSB_ERROR_NO_MEM: return "Insufficient memory."; case LIBUSB_ERROR_NOT_SUPPORTED: return "Operation not supported or unimplemented on this platform."; case LIBUSB_ERROR_OTHER: return "Other error. "; } return "unknown error"; } cVFD::cVFD() { pFont = NULL; lastIconState = 0; framebuf = NULL; backingstore = NULL; } cVFD::~cVFD() { this->close(); } /** * Initialize the driver. * \retval 0 Success. * \retval <0 Error. */ bool cVFD::open() { if(!SetFont(theSetup.m_szFont, theSetup.m_nRenderMode == eRenderMode_DualLine, theSetup.m_nBigFontHeight, theSetup.m_nSmallFontHeight)) { return false; } if(!cVFDQueue::open()) { return false; } isyslog("targaVFD: open Device successful"); /* Make sure the frame buffer is there... */ this->framebuf = new cVFDBitmap(theSetup.m_cWidth,theSetup.m_cHeight); if (this->framebuf == NULL) { esyslog("targaVFD: unable to allocate framebuffer"); return false; } m_iSizeYb = ((theSetup.m_cHeight + 7) / 8); /* Make sure the framebuffer backing store is there... */ this->backingstore = new unsigned char[theSetup.m_cWidth * m_iSizeYb]; if (this->backingstore == NULL) { esyslog("targaVFD: unable to create framebuffer backing store"); return false; } this->lastIconState = 0; QueueCmd(CMD_RESET); //Brightness(theSetup.m_nBrightness); if(QueueFlush()) { dsyslog("targaVFD: init() done"); return true; } return false; } /* * turning display off */ bool cVFD::SendCmdShutdown() { QueueCmd(CMD_RESET); return QueueFlush(); } inline unsigned char toBCD(int x){ return (unsigned char)(((x) / 10 * 16) + ((x) % 10)); } /* * Show the big clock. We need to set it to the current time, then it just * keeps counting automatically. */ bool cVFD::SendCmdClock() { time_t tt; struct tm l; tt = time(NULL); localtime_r(&tt, &l); // Set time QueueCmd(CMD_SETCLOCK); QueueData(toBCD(l.tm_min)); QueueData(toBCD(l.tm_hour)); // Show it QueueCmd(CMD_BIGCLOCK); QueueData(TIME_24); return QueueFlush(); } /** * Close the driver (do necessary clean-up). */ void cVFD::close() { cVFDQueue::close(); if(pFont) { delete pFont; pFont = NULL; } if(framebuf) { delete framebuf; framebuf = NULL; } if(backingstore) { delete[] backingstore; backingstore = NULL; } dsyslog("targaVFD: close() done"); } /** * Clear the screen. */ void cVFD::clear() { if(framebuf) framebuf->clear(); } /** * Flush cached bitmap data and submit changes rows to the Display. */ bool cVFD::flush(bool refreshAll) { unsigned int n, x, yb; if (!backingstore || !framebuf) return false; const uchar* fb = framebuf->getBitmap(); const unsigned int width = framebuf->Width(); bool doRefresh = false; unsigned int minX = width; unsigned int maxX = 0; for (yb = 0; yb < m_iSizeYb; ++yb) for (x = 0; x < width; ++x) { n = x + (yb * width); if (*(fb + n) != *(backingstore + n)) { *(backingstore + n) = *(fb + n); minX = min(minX, x); maxX = max(maxX, x + 1); doRefresh = true; } } if (refreshAll || doRefresh) { if (refreshAll) { minX = 0; maxX = width; } maxX = min(maxX, width); unsigned int nData = (maxX-minX) * m_iSizeYb; if(nData) { // send data to display, controller QueueCmd(CMD_SETRAM); QueueData(minX*m_iSizeYb); QueueCmd(CMD_SETPIXEL); QueueData(nData); for (x = minX; x < maxX; ++x) for (yb = 0; yb < m_iSizeYb; ++yb) { n = x + (yb * width); QueueData((*(backingstore + n))); } } } return QueueFlush(); } /** * Print a string on the screen at position (x,y). * The upper-left corner is (1,1), the lower-right corner is (this->width, this->height). * \param x Horizontal character position (column). * \param y Vertical character position (row). * \param string String that gets written. */ int cVFD::DrawText(int x, int y, const char* string) { if(pFont && framebuf) return pFont->DrawText(framebuf, x, y, string, 1024); return -1; } /** * Height of framebuffer from current device. */ int cVFD::Height() const { if(framebuf) return framebuf->Height(); return 0; } /** * Width of framebuffer from current device. */ int cVFD::Width() const { if(framebuf) return framebuf->Width(); return 0; } /** * Draw a rectangle on framebuffer on device. * * \param x1 First horizontal corner (column). * \param y1 First vertical corner (row). * \param x2 Second horizontal corner (column). * \param y2 Second vertical corner (row). * \param filled drawing of rectangle should be filled. */ bool cVFD::Rectangle(int x1, int y1, int x2, int y2, bool filled) { if(framebuf) return framebuf->Rectangle(x1, y1, x2, y2, filled); return false; } /** * Sets the "icons state" for the device. We use this to control the icons * around the outside the display. * * \param state This symbols to display. */ void cVFD::icons(unsigned int state) { for(unsigned i = 0; i <= 0x18; i++) { if((state & (1 << i)) != (lastIconState & (1 << i))) { QueueCmd(CMD_SETSYMBOL); QueueData(i); QueueData((state & (1 << i)) ? STATE_ON : STATE_OFF ); } } lastIconState = state; } /** * Sets the brightness of the display. * * \param nBrightness The value the brightness (0 = off * 1 = half brightness; 2 = highest brightness). */ void cVFD::Brightness(int nBrightness) { if (nBrightness < 0) { nBrightness = 0; } else if (nBrightness > 2) { nBrightness = 2; } this->QueueCmd(CMD_SETDIMM); this->QueueData((unsigned char) (nBrightness)); } bool cVFD::SetFont(const char *szFont, bool bTwoLineMode, int nBigFontHeight, int nSmallFontHeight) { cVFDFont* tmpFont = NULL; cString sFileName = cFont::GetFontFileName(szFont); if(!isempty(sFileName)) { if (bTwoLineMode) { tmpFont = new cVFDFont(sFileName,nSmallFontHeight); } else { tmpFont = new cVFDFont(sFileName,nBigFontHeight); } } else { esyslog("targaVFD: unable to find font '%s'",szFont); } if(tmpFont) { if(pFont) { delete pFont; } pFont = tmpFont; return true; } return false; }