convert JPEG stream data to bitmap, BaseJpegDecode example program

Dependencies:   BaseJpegDecode BaseUsbHost FATFileSystem mbed-rtos mbed

JPEGデコードのサンプルプログラムです。
JPEGのMCU単位で逐次デコード出力していますので少ないRAMメモリで動かすことが出来ます。

#include "USBHostMSD.h"
#include "SimpleJpegDecode.h"
#include "bmp24.h"

const char* INPUT_FILE  = "/usb/input.jpg";
const char* OUTPUT_FILE = "/usb/output.bmp";

bmp24 bmp;
RawSerial pc(USBTX, USBRX);

void callbackRGB(int x, int y, uint8_t* rgb) {
    bmp.point(x, y, rgb);
    pc.printf("x=%d, y=%d, RGB=(0x%02x,0x%02x,0x%02x)\n", x, y, rgb[0], rgb[1], rgb[2]);
}

int main() {
    pc.baud(115200);
    
    USBHostMSD* msd = new USBHostMSD("usb");
    if (!msd->connect()) {
        error("USB Flash drive not found.\n");
    } 
    SimpleJpegDecode* decode = new SimpleJpegDecode(RGB24);

    decode->setOnResult(callbackRGB);
    decode->clear();
    pc.printf("input: %s\n", INPUT_FILE);
    FILE* fp = fopen(INPUT_FILE, "rb");
    if (fp == NULL) {
         error("open error\n");
    }
    while(1) {
        int c = fgetc(fp);
        if (c == EOF) {
            break;
        }
        decode->input(c);
    }
    fclose(fp);
    pc.printf("output: %s\n", OUTPUT_FILE);
    if (!bmp.writeFile(OUTPUT_FILE)) {
        error("write error\n");
    }
    exit(1);     
}

Files at this revision

API Documentation at this revision

Comitter:
va009039
Date:
Sat Feb 02 01:25:25 2013 +0000
Commit message:
first commit

Changed in this revision

BaseJpegDecode.lib Show annotated file Show diff for this revision Revisions of this file
BaseUsbHost.lib Show annotated file Show diff for this revision Revisions of this file
FATFileSystem.lib Show annotated file Show diff for this revision Revisions of this file
SimpleJpegDecode/SimpleJpegDecode.cpp Show annotated file Show diff for this revision Revisions of this file
SimpleJpegDecode/SimpleJpegDecode.h Show annotated file Show diff for this revision Revisions of this file
UsbFlashDrive/UsbFlashDrive.cpp Show annotated file Show diff for this revision Revisions of this file
UsbFlashDrive/UsbFlashDrive.h Show annotated file Show diff for this revision Revisions of this file
bmp24.h Show annotated file Show diff for this revision Revisions of this file
main.cpp Show annotated file Show diff for this revision Revisions of this file
mbed-rtos.lib Show annotated file Show diff for this revision Revisions of this file
mbed.bld Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/BaseJpegDecode.lib	Sat Feb 02 01:25:25 2013 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/va009039/code/BaseJpegDecode/#d7ee458cacd1
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/BaseUsbHost.lib	Sat Feb 02 01:25:25 2013 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/va009039/code/BaseUsbHost/#d931d24c2f81
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/FATFileSystem.lib	Sat Feb 02 01:25:25 2013 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/va009039/code/FATFileSystem/#86638c0f65b0
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SimpleJpegDecode/SimpleJpegDecode.cpp	Sat Feb 02 01:25:25 2013 +0000
@@ -0,0 +1,161 @@
+#include "SimpleJpegDecode.h"
+
+#define DBG(...) do{fprintf(stderr,"[%s@%d] ",__PRETTY_FUNCTION__,__LINE__);fprintf(stderr,__VA_ARGS__);} while(0);
+#define ASSERT(A) while(!(A)){fprintf(stderr,"\n\n%s@%d %s ASSERT!\n\n",__PRETTY_FUNCTION__,__LINE__,#A);exit(1);};
+
+
+#define _1_4020 45
+#define _0_3441 11
+#define _0_7139 23
+#define _1_7718 57
+#define _0_0012 0
+
+int adjust(int r) {
+    if (r >= 0) {
+        if (r <= 255) {
+            return r;
+        } else {
+            return 255;
+        }
+    } else {
+        return 0;
+    }
+}
+    
+void convYUVtoRGB(uint8_t rgb[], int y, int u, int v)
+{
+    rgb[0] = adjust((y*32             + v*_1_4020)/32 + 128);
+    rgb[1] = adjust((y*32 - u*_0_3441 - v*_0_7139)/32 + 128);
+    rgb[2] = adjust((y*32 + u*_1_7718 - v*_0_0012)/32 + 128);
+}
+
+SimpleJpegDecode::SimpleJpegDecode(uint8_t output_mode)
+{
+    m_output_mode = output_mode;
+    clearOnResult();
+}
+
+
+void SimpleJpegDecode::output(int mcu, int block, int scan, int value)
+{
+    int sc = (block < yblock) ? 0 : 1;
+    inputBLOCK(mcu, block, scan, value * qt[sc][scan]);
+}
+
+void SimpleJpegDecode::outputDC(int mcu, int block, int value)
+{
+    output(mcu, block, 0, value);
+    DC_count++;
+}
+
+void SimpleJpegDecode::outputAC(int mcu, int block, int scan, int value)
+{
+    output(mcu, block, scan, value);
+    AC_count++;
+}
+
+void SimpleJpegDecode::outputMARK(uint8_t c)
+{
+}
+
+void SimpleJpegDecode::format_YUV(int mcu, int block, int8_t* values)
+{
+    if (block < yblock+1) {
+        memcpy(m_block_data[block], values, 64);
+        return;
+    }
+    int mcu_x_count = (width+15)/16;
+    int mcu_x = mcu % mcu_x_count;
+    int mcu_y = mcu / mcu_x_count;
+    uint8_t yuv[3];
+    if (yblock == 2) {
+        for(int y = 0; y < 8; y++) {
+            for(int x = 0; x < 16; x++) {
+                yuv[0] = m_block_data[x/8][y*8+x%8] + 128;
+                yuv[1] = m_block_data[2][y*8+x/2] + 128;
+                yuv[2] = values[y*8+x/2] + 128;
+                onResult(mcu_x * 16 + x, mcu_y * 8 + y, yuv);
+            }
+        }
+    } else if (yblock == 4) {
+        for(int y = 0; y < 16; y++) {
+            for(int x = 0; x < 16; x++) {
+                yuv[0] = m_block_data[(y/8)*2+x/8][(y%8)*8+x%8] + 128;
+                yuv[1] = m_block_data[4][(y/2)*8+x/2] + 128;
+                yuv[2] = values[(y/2)*8+x/2] + 128;
+                onResult(mcu_x * 16 + x, mcu_y * 16 + y, yuv);
+            }
+        }
+    } else {
+        ASSERT(yblock == 2 || yblock == 4);
+    }    
+}
+
+void SimpleJpegDecode::format_RGB24(int mcu, int block, int8_t* values)
+{
+    if (block < yblock+1) {
+        memcpy(m_block_data[block], values, 64);
+        return;
+    }
+    int mcu_x_count = (width+15)/16;
+    int mcu_x = mcu % mcu_x_count;
+    int mcu_y = mcu / mcu_x_count;
+    uint8_t rgb[3];
+    if (yblock == 2) {
+        for(int y = 0; y < 8; y++) {
+            for(int x = 0; x < 16; x++) {
+                int8_t yuv_y = m_block_data[x/8][y*8+x%8];
+                int8_t yuv_u = m_block_data[2][y*8+x/2];
+                int8_t yuv_v = values[y*8+x/2];
+                convYUVtoRGB(rgb, yuv_y, yuv_u, yuv_v);
+                onResult(mcu_x * 16 + x, mcu_y * 8 + y, rgb);
+            }
+        }
+    } else if (yblock == 4) {
+        for(int y = 0; y < 16; y++) {
+            for(int x = 0; x < 16; x++) {
+                int8_t yuv_y = m_block_data[(y/8)*2+x/8][(y%8)*8+x%8];
+                int8_t yuv_u = m_block_data[4][(y/2)*8+x/2];
+                int8_t yuv_v = values[(y/2)*8+x/2];
+                convYUVtoRGB(rgb, yuv_y, yuv_u, yuv_v);
+                onResult(mcu_x * 16 + x, mcu_y * 16 + y, rgb);
+            }
+        }
+    } else {
+        ASSERT(yblock == 2 || yblock == 4);
+    }    
+}
+
+void SimpleJpegDecode::outputBLOCK(int mcu, int block, int8_t* values)
+{
+    BLOCK_count++;
+    if (m_output_mode == YUV) {
+        format_YUV(mcu, block, values);
+    } else if (m_output_mode == RGB24) {
+        format_RGB24(mcu, block, values);
+    } else {
+        ASSERT(m_output_mode == YUV || m_output_mode == RGB24);
+    }    
+}
+
+void SimpleJpegDecode::onResult(int x, int y, uint8_t* yuv)
+{
+  if(m_pCbItem && m_pCbMeth)
+    (m_pCbItem->*m_pCbMeth)(x, y, yuv);
+  else if(m_pCb)
+    m_pCb(x, y, yuv);
+}
+
+void SimpleJpegDecode::setOnResult( void (*pMethod)(int, int, uint8_t*) )
+{
+  m_pCb = pMethod;
+  m_pCbItem = NULL;
+  m_pCbMeth = NULL;
+}
+
+void SimpleJpegDecode::clearOnResult()
+{
+  m_pCb = NULL;
+  m_pCbItem = NULL;
+  m_pCbMeth = NULL;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SimpleJpegDecode/SimpleJpegDecode.h	Sat Feb 02 01:25:25 2013 +0000
@@ -0,0 +1,56 @@
+#ifndef SIMPLE_JPEG_DECODE_H
+#define SIMPLE_JPEG_DECODE_H
+
+#include "BaseJpegDecode.h"
+#include "inverseDCT.h"
+
+#define YUV   0
+#define RGB24 1
+
+class SimpleJpegDecode : public BaseJpegDecode, public inverseDCT {
+public:
+    SimpleJpegDecode(uint8_t output_mode=RGB24);
+
+    void format_YUV(int mcu, int block, int8_t* values);
+    void format_RGB24(int mcu, int block, int8_t* values);
+
+    void output(int mcu, int block, int scan, int value);
+    virtual void outputDC(int mcu, int block, int value);
+    virtual void outputAC(int mcu, int block, int scan, int value);
+    virtual void outputMARK(uint8_t c);
+    virtual void outputBLOCK(int muc, int block, int8_t* values); // iDCT
+
+    int8_t m_block_data[5][64];
+    int DC_count;
+    int AC_count;
+    int BLOCK_count;
+
+    ///Setups the result callback
+    /**
+     @param pMethod : callback function
+     */
+    void setOnResult( void (*pMethod)(int, int, uint8_t*) );
+  
+    ///Setups the result callback
+    /**
+    @param pItem : instance of class on which to execute the callback method
+    @param pMethod : callback method
+    */
+    class CDummy;
+    template<class T> 
+    void setOnResult( T* pItem, void (T::*pMethod)(int, int, uint8_t*) )
+    {
+        m_pCb = NULL;
+        m_pCbItem = (CDummy*) pItem;
+        m_pCbMeth = (void (CDummy::*)(int, int, uint8_t*)) pMethod;
+    }
+    void clearOnResult();
+protected:
+    void onResult(int x, int y, uint8_t* yuv);
+    CDummy* m_pCbItem;
+    void (CDummy::*m_pCbMeth)(int, int, uint8_t*);
+    void (*m_pCb)(int, int, uint8_t*);
+    uint8_t m_output_mode;
+};
+
+#endif // SIMPLE_JPEG_DECODE_H
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/UsbFlashDrive/UsbFlashDrive.cpp	Sat Feb 02 01:25:25 2013 +0000
@@ -0,0 +1,406 @@
+// UsbFlashDrive.cpp 2013/1/25
+#include "mbed.h"
+#include "rtos.h"
+#include "BaseUsbHost.h"
+//#define DEBUG
+#include "BaseUsbHostDebug.h"
+#define TEST
+#include "BaseUsbHostTest.h"
+#include "UsbFlashDrive.h"
+
+//#define WRITE_PROTECT
+
+
+uint32_t BE32(uint8_t* d)
+{
+    return (d[0] << 24) | (d[1] << 16) | (d[2] << 8) | d[3];
+}
+
+void BE16(uint32_t n, uint8_t* d)
+{
+    d[0] = (uint8_t)(n >> 8);
+    d[1] = (uint8_t)n;
+}
+
+void BE32(uint32_t n, uint8_t* d)
+{
+    d[0] = (uint8_t)(n >> 24);
+    d[1] = (uint8_t)(n >> 16);
+    d[2] = (uint8_t)(n >> 8);
+    d[3] = (uint8_t)n;
+}
+
+UsbFlashDrive::UsbFlashDrive(const char* name, ControlEp* ctlEp): FATFileSystem(name)
+{
+    m_name = name;
+    
+    if (ctlEp == NULL) { // root hub
+        DBG_OHCI(LPC_USB->HcRhPortStatus1);
+        TEST_ASSERT_FALSE(LPC_USB->HcRhPortStatus1 & 0x200);
+        ctlEp = new ControlEp();
+        TEST_ASSERT_TRUE(ctlEp);
+    }
+
+    CTASSERT(sizeof(CBW) == 31);
+    CTASSERT(sizeof(CSW) == 13);
+    TEST_ASSERT(sizeof(CBW) == 31);
+    TEST_ASSERT(sizeof(CSW) == 13);
+
+    m_numBlocks = 0;
+    m_BlockSize = 0;
+    m_lun = 0;
+    m_interface = 0;
+    m_pEpBulkIn = NULL;
+    m_pEpBulkOut = NULL;
+    
+    ParseConfiguration(ctlEp);
+    int rc = ctlEp->SetConfiguration(1);
+    TEST_ASSERT_EQUAL(rc, USB_OK);
+    GetMaxLUN(ctlEp);
+    setup(ctlEp);
+}
+
+bool UsbFlashDrive::check(ControlEp* ctlEp)
+{
+    if (ctlEp == NULL) {
+        return false;
+    }
+    CTASSERT(sizeof(StandardDeviceDescriptor) == 18);
+    CTASSERT(sizeof(StandardConfigurationDescriptor) == 9);
+    CTASSERT(sizeof(StandardInterfaceDescriptor) == 9);
+    TEST_ASSERT(sizeof(StandardDeviceDescriptor) == 18);
+    TEST_ASSERT(sizeof(StandardConfigurationDescriptor) == 9);
+    TEST_ASSERT(sizeof(StandardInterfaceDescriptor) == 9);
+
+    StandardDeviceDescriptor desc;
+    int rc = ctlEp->GetDescriptor(USB_DESCRIPTOR_TYPE_DEVICE, 0, reinterpret_cast<uint8_t*>(&desc), sizeof(StandardDeviceDescriptor));
+    if (rc != USB_OK) {
+        return false;
+    }
+    if (desc.bDeviceClass == 8) {
+        return true;
+    } else if (desc.bDeviceClass != 0x00) {
+        return false;
+    }
+    uint8_t temp[4];
+    rc = ctlEp->GetDescriptor(USB_DESCRIPTOR_TYPE_CONFIGURATION, 0, temp, sizeof(temp));
+    if (rc != USB_OK) {
+        return false;
+    }
+    StandardConfigurationDescriptor* cfg = reinterpret_cast<StandardConfigurationDescriptor*>(temp);
+    uint8_t* buf = new uint8_t[cfg->wTotalLength];
+    
+    rc = ctlEp->GetDescriptor(USB_DESCRIPTOR_TYPE_CONFIGURATION, 0, buf, cfg->wTotalLength);
+    if (rc != USB_OK) {
+        return false;
+    }
+    DBG_HEX(buf, cfg->wTotalLength);
+    bool ret = false;
+    for(int pos = 0; pos < cfg->wTotalLength; pos += buf[pos]) {
+        StandardInterfaceDescriptor* desc = reinterpret_cast<StandardInterfaceDescriptor*>(buf+pos);
+        if (desc->bDescriptorType == 4) { // interface ?
+            if (desc->bInterfaceClass == 8 && desc->bInterfaceSubClass == 6 && desc->bInterfaceProtocol == 0x50) {
+                ret = true;
+            }
+            break;
+        }
+    }
+    delete[] buf;
+    return ret;
+}
+
+int UsbFlashDrive::disk_initialize()
+{
+    //DBG("m_BlockSize=%d\n", m_BlockSize);
+    if (m_BlockSize != 512) {
+        return 1;
+    }
+    return 0;    
+}
+
+int UsbFlashDrive::disk_write(const uint8_t* buffer, uint64_t sector)
+{
+    m_report_disk_write++;
+    //DBG("buffer=%p block_number=%d\n", buffer, sector);
+    int ret = MS_BulkSend(sector, 1, buffer);
+    if (ret >= 0) {
+        return 0;
+    }
+    return 1;
+}
+
+int UsbFlashDrive::disk_read(uint8_t* buffer, uint64_t sector)
+{
+    m_report_disk_read++;
+    //DBG("buffer=%p block_number=%d\n", buffer, sector);
+    int ret = MS_BulkRecv(sector, 1, buffer);
+    if (ret >= 0) {
+        return 0;
+    }
+    return 1;
+}    
+
+int UsbFlashDrive::disk_status()
+{
+    m_report_disk_status++;
+    return 0;
+}
+
+int UsbFlashDrive::disk_sync()
+{
+    m_report_disk_sync++;
+    return 0;
+}
+
+uint64_t UsbFlashDrive::disk_sectors()
+{
+    DBG("m_numBlocks=%d\n", m_numBlocks);
+    return m_numBlocks;
+}
+
+int UsbFlashDrive::setup(ControlEp* ctlEp, int timeout)
+{
+
+    int retry = 0;
+    Timer t;
+    t.start();
+    t.reset();
+    while(t.read_ms() < timeout) {
+        DBG("retry=%d t=%d\n", retry, t.read_ms());
+        if (retry > 80) {
+            return -1;
+        }
+        int rc = TestUnitReady();
+        DBG("TestUnitReady(): %d\n", rc);
+        if (rc == USB_OK) {
+            DBG("m_CSW.bCSWStatus: %02X\n", m_CSW.bCSWStatus);
+            if (m_CSW.bCSWStatus == 0x00) {
+                break;
+            }
+        }
+        GetSenseInfo();
+        retry++;
+        wait_ms(50);
+    }
+    if (t.read_ms() >= timeout) {
+        return -1;
+    }
+    ReadCapacity();
+    Inquire();
+    return 0;
+}
+
+int UsbFlashDrive::ParseConfiguration(ControlEp* ctlEp)
+{
+    TEST_ASSERT(ctlEp);
+    TEST_ASSERT(sizeof(StandardEndpointDescriptor) == 7);
+    uint8_t temp[4];
+    int rc = ctlEp->GetDescriptor(USB_DESCRIPTOR_TYPE_CONFIGURATION, 0, temp, sizeof(temp));
+    if (rc != USB_OK) {
+        return rc;
+    }
+    StandardConfigurationDescriptor* cfg = reinterpret_cast<StandardConfigurationDescriptor*>(temp);
+    uint8_t* buf = new uint8_t[cfg->wTotalLength];
+    rc = ctlEp->GetDescriptor(USB_DESCRIPTOR_TYPE_CONFIGURATION, 0, buf, cfg->wTotalLength);
+    if (rc != USB_OK) {
+        return rc;
+    }
+    DBG_HEX(buf, cfg->wTotalLength);
+    for(int pos = 0; pos < cfg->wTotalLength; pos += buf[pos]) {
+        StandardEndpointDescriptor* desc = reinterpret_cast<StandardEndpointDescriptor*>(buf+pos);
+        if (desc->bDescriptorType == USB_DESCRIPTOR_TYPE_ENDPOINT) {
+            if (desc->bmAttributes == 2) { // bulk
+                BulkEp* pEp = new BulkEp(ctlEp->GetAddr(), desc->bEndpointAddress, desc->wMaxPacketSize);
+                if (desc->bEndpointAddress & 0x80) {
+                    m_pEpBulkIn = pEp;
+                } else {
+                    m_pEpBulkOut = pEp;
+                }
+            }
+        }
+    }
+    delete[] buf;
+    if (m_pEpBulkIn && m_pEpBulkOut) {
+        return USB_OK;
+    }
+    return USB_ERROR;    
+}
+
+int UsbFlashDrive::BulkOnlyMassStorageReset(ControlEp* ctlEp)
+{
+    TEST_ASSERT(ctlEp);
+    int rc = ctlEp->controlReceive(0x21, 0xff, 0x0000, m_interface, NULL, 0); 
+    TEST_ASSERT(rc == USB_OK);
+    return rc;
+}
+
+int UsbFlashDrive::GetMaxLUN(ControlEp* ctlEp)
+{
+    TEST_ASSERT(ctlEp);
+    TEST_ASSERT(m_interface == 0);
+    uint8_t temp[1];
+    int rc = ctlEp->controlReceive(0xa1, 0xfe, 0x0000, m_interface, temp, sizeof(temp)); 
+    TEST_ASSERT(rc == USB_OK);
+    DBG_BYTES("GetMaxLUN", temp, sizeof(temp));
+    m_MaxLUN = temp[0];
+    TEST_ASSERT(m_MaxLUN <= 15);
+    return rc;
+}
+
+
+int UsbFlashDrive::TestUnitReady()
+{
+    const uint8_t cdb[6] = {SCSI_CMD_TEST_UNIT_READY, 0x00, 0x00, 0x00, 0x00, 0x00};
+    m_CBW.dCBWDataTraansferLength = 0;
+    m_CBW.bmCBWFlags = 0x00;
+    CommandTransport(cdb, sizeof(cdb));
+    StatusTransport();
+    return 0;
+}
+
+int UsbFlashDrive::GetSenseInfo()
+{
+    const uint8_t cdb[6] = {SCSI_CMD_REQUEST_SENSE, 0x00, 0x00, 0x00, 18, 0x00};
+    m_CBW.dCBWDataTraansferLength = 18;
+    m_CBW.bmCBWFlags = 0x80; // data In
+    CommandTransport(cdb, sizeof(cdb));
+
+    uint8_t buf[18];
+    _bulkRecv(buf, sizeof(buf));
+    DBG_HEX(buf, sizeof(buf));
+
+    StatusTransport();
+    TEST_ASSERT(m_CSW.bCSWStatus == 0x00);
+    return 0;
+}
+
+int UsbFlashDrive::ReadCapacity()
+{
+    const uint8_t cdb[10] = {SCSI_CMD_READ_CAPACITY, 0x00, 0x00, 0x00, 0x00, 
+                                               0x00, 0x00, 0x00, 0x00, 0x00};
+    m_CBW.dCBWDataTraansferLength = 8;
+    m_CBW.bmCBWFlags = 0x80; // data In
+    CommandTransport(cdb, sizeof(cdb));
+
+    uint8_t buf[8];
+    int rc = _bulkRecv(buf, sizeof(buf));
+    TEST_ASSERT(rc >= 0);
+    DBG_HEX(buf, sizeof(buf));
+
+    StatusTransport();
+    TEST_ASSERT(m_CSW.bCSWStatus == 0x00);
+    
+    m_numBlocks = BE32(buf);
+    m_BlockSize = BE32(buf+4);
+    DBG("m_numBlocks=%d m_BlockSize=%d\n", m_numBlocks, m_BlockSize);
+    TEST_ASSERT(m_BlockSize == 512);
+    TEST_ASSERT(m_numBlocks > 0);
+    return 0;
+}
+
+int UsbFlashDrive::Inquire()
+{
+    const uint8_t cdb[6] = {SCSI_CMD_INQUIRY, 0x00, 0x00, 0x00, 36, 0x00};
+    m_CBW.dCBWDataTraansferLength = 36;
+    m_CBW.bmCBWFlags = 0x80; // data In
+    CommandTransport(cdb, sizeof(cdb));
+
+    uint8_t buf[36];
+    int rc = _bulkRecv(buf, sizeof(buf));
+    TEST_ASSERT(rc >= 0);
+    DBG_HEX(buf, sizeof(buf));
+
+    StatusTransport();
+    return 0;
+}
+
+int UsbFlashDrive::MS_BulkRecv(uint32_t block_number, int num_blocks, uint8_t* user_buffer)
+{
+    TEST_ASSERT(m_BlockSize == 512);
+    TEST_ASSERT(num_blocks == 1);
+    TEST_ASSERT(user_buffer);
+    uint8_t cdb[10] = {SCSI_CMD_READ_10, 0x00, 0x00, 0x00, 0x00, 
+                                   0x00, 0x00, 0x00, 0x00, 0x00};
+    BE32(block_number, cdb+2);
+    BE16(num_blocks, cdb+7);
+    uint32_t len = m_BlockSize * num_blocks;
+    TEST_ASSERT(len <= 512);
+    m_CBW.dCBWDataTraansferLength = len;
+    m_CBW.bmCBWFlags = 0x80; // data In
+    CommandTransport(cdb, sizeof(cdb));
+
+    int ret = _bulkRecv(user_buffer, len);
+    //DBG_HEX(user_buffer, len);
+
+    StatusTransport();
+    TEST_ASSERT(m_CSW.bCSWStatus == 0x00);
+    return ret;
+}
+
+int UsbFlashDrive::MS_BulkSend(uint32_t block_number, int num_blocks, const uint8_t* user_buffer)
+{
+#ifdef WRITE_PROTECT
+    return 0;
+#else
+    TEST_ASSERT(num_blocks == 1);
+    TEST_ASSERT(user_buffer);
+    uint8_t cdb[10] = {SCSI_CMD_WRITE_10, 0x00, 0x00, 0x00, 0x00, 
+                                    0x00, 0x00, 0x00, 0x00, 0x00};
+    BE32(block_number, cdb+2);
+    BE16(num_blocks, cdb+7);
+    uint32_t len = m_BlockSize * num_blocks;
+    TEST_ASSERT(len <= 512);
+    m_CBW.dCBWDataTraansferLength = len;
+    m_CBW.bmCBWFlags = 0x00; // data Out
+    CommandTransport(cdb, sizeof(cdb));
+
+    int ret = _bulkSend(user_buffer, len);
+    //DBG_HEX(user_buffer, len);
+
+    StatusTransport();
+    TEST_ASSERT(m_CSW.bCSWStatus == 0x00);
+    return ret;
+#endif //WRITE_PROTECT    
+}
+
+int UsbFlashDrive::CommandTransport(const uint8_t* cdb, int size)
+{
+    TEST_ASSERT(cdb);
+    TEST_ASSERT(size >= 6);
+    TEST_ASSERT(size <= 16);
+    m_CBW.bCBWLUN = m_lun;
+    m_CBW.bCBWCBLength = size;
+    memcpy(m_CBW.CBWCB, cdb, size);
+
+    m_CBW.dCBWSignature = 0x43425355;
+    m_CBW.dCBWTag = m_tag++;
+    m_CBW.bCBWLUN = 0;
+    //DBG_HEX((uint8_t*)&m_CBW, sizeof(CBW));
+    int rc = _bulkSend((uint8_t*)&m_CBW, sizeof(CBW));
+    return rc;
+}
+
+int UsbFlashDrive::StatusTransport()
+{
+    TEST_ASSERT(sizeof(CSW) == 13);
+    int rc = _bulkRecv((uint8_t*)&m_CSW, sizeof(CSW));
+    //DBG_HEX((uint8_t*)&m_CSW, sizeof(CSW));
+    TEST_ASSERT(m_CSW.dCSWSignature == 0x53425355);
+    TEST_ASSERT(m_CSW.dCSWTag == m_CBW.dCBWTag);
+    TEST_ASSERT(m_CSW.dCSWDataResidue == 0);
+    return rc;
+}
+
+int UsbFlashDrive::_bulkRecv(uint8_t* buf, int size)
+{
+    TEST_ASSERT(m_pEpBulkIn);
+    int ret = m_pEpBulkIn->bulkReceive(buf, size);
+    return ret;
+}
+
+int UsbFlashDrive::_bulkSend(const uint8_t* buf, int size)
+{
+    TEST_ASSERT(m_pEpBulkOut);
+    int ret = m_pEpBulkOut->bulkSend(buf, size);
+    return ret;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/UsbFlashDrive/UsbFlashDrive.h	Sat Feb 02 01:25:25 2013 +0000
@@ -0,0 +1,75 @@
+// usbFlashDrive.h 2013/1/8
+#pragma once
+
+#include "FATFileSystem.h"
+
+#define  SCSI_CMD_REQUEST_SENSE      0x03
+#define  SCSI_CMD_TEST_UNIT_READY    0x00
+#define  SCSI_CMD_INQUIRY            0x12
+#define  SCSI_CMD_READ_10            0x28
+#define  SCSI_CMD_READ_CAPACITY      0x25
+#define  SCSI_CMD_WRITE_10           0x2A
+
+#pragma pack(push,1)
+struct CBW {
+    uint32_t dCBWSignature;
+    uint32_t dCBWTag;
+    uint32_t dCBWDataTraansferLength;
+    uint8_t bmCBWFlags;
+    uint8_t bCBWLUN;
+    uint8_t bCBWCBLength;
+    uint8_t CBWCB[16];
+};
+
+struct CSW {
+    uint32_t dCSWSignature;
+    uint32_t dCSWTag;
+    uint32_t dCSWDataResidue;
+    uint8_t  bCSWStatus;
+};
+#pragma pack(pop)
+
+class UsbFlashDrive : public FATFileSystem {
+public:
+    UsbFlashDrive(const char* name, ControlEp* ctlEp = NULL);
+    static bool check(ControlEp* ctlEp);
+    virtual int disk_initialize();
+    virtual int disk_write(const uint8_t* buffer, uint64_t sector);
+    virtual int disk_read(uint8_t* buffer, uint64_t sector);    
+    virtual int disk_status();
+    virtual int disk_sync();
+    virtual uint64_t disk_sectors();
+private:
+    int setup(ControlEp* ctlEp, int timeout = 9000);
+    int ParseConfiguration(ControlEp* ctlEp);
+    int BulkOnlyMassStorageReset(ControlEp* ctlEp);
+    int GetMaxLUN(ControlEp* ctlEp);
+    int ReadCapacity();
+    int GetSenseInfo();
+    int TestUnitReady();
+    int Inquire();
+    int MS_BulkRecv(uint32_t block_number, int num_blocks, uint8_t* user_buffer);
+    int MS_BulkSend(uint32_t block_number, int num_blocks, const uint8_t* user_buffer);
+    int CommandTransport(const uint8_t* cdb, int size);
+    int StatusTransport();
+    int _bulkRecv(uint8_t* buf, int size);
+    int _bulkSend(const uint8_t* buf, int size);
+    const char* m_name;
+    int m_drive;
+    uint32_t m_numBlocks;
+    int m_BlockSize;
+    int m_lun;
+    int m_MaxLUN;
+    int m_interface;
+    uint32_t m_tag;
+    CBW m_CBW;
+    CSW m_CSW;
+    // endpoint
+    BulkEp* m_pEpBulkIn;
+    BulkEp* m_pEpBulkOut;
+    // report
+    uint32_t m_report_disk_write;
+    uint32_t m_report_disk_read;
+    uint32_t m_report_disk_status;
+    uint32_t m_report_disk_sync;
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bmp24.h	Sat Feb 02 01:25:25 2013 +0000
@@ -0,0 +1,66 @@
+#ifndef BMP24_H
+#define BMP24_H
+
+#define BMP24_WIDTH  (16*4)
+#define BMP24_HEIGHT (16*3)
+
+class bmp24 {
+    uint8_t m_bitmap[BMP24_WIDTH*BMP24_HEIGHT*3];
+public:
+    int width;
+    int height;
+
+    bmp24() {
+        width = BMP24_WIDTH;
+        height = BMP24_HEIGHT;
+    }
+    
+    void clear() {
+        memset(m_bitmap, 0, sizeof(m_bitmap));
+    }
+    
+    void point(int x, int y, uint8_t* rgb) {
+        if (x >= 0 && x < width && y >= 0 && y < height) {
+            int pos = y*width*3+x*3;
+            m_bitmap[pos++] = rgb[0];
+            m_bitmap[pos++] = rgb[1];
+            m_bitmap[pos]   = rgb[2];
+        }
+    }
+    
+    void LE32write(uint8_t* buf, int value) {
+        *buf++ = value & 0xff;
+        *buf++ = (value>>8) & 0xff;
+        *buf++ = (value>>16) & 0xff;
+        *buf   = (value>>24) & 0xff;
+    }
+    
+    bool writeFile(const char *path) {
+        FILE *fp = fopen(path, "wb");
+        if (fp == NULL) {
+            return false;
+        }
+        uint8_t header[] = {
+0x42,0x4d,0x36,0xe1,0x00,0x00,0x00,0x00,0x00,0x00,0x36,0x00,0x00,0x00,0x28,0x00,
+0x00,0x00,0xa0,0x00,0x00,0x00,0x78,0x00,0x00,0x00,0x01,0x00,0x18,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00};
+        int file_size = sizeof(header) + sizeof(m_bitmap);
+        LE32write(header+2, file_size);
+        LE32write(header+18, width);
+        LE32write(header+22, height);
+        
+        fwrite(header, 1, sizeof(header), fp);
+        for(int y = height-1; y >=0; y--) {
+            for(int x = 0; x < width; x++) {
+                fputc(m_bitmap[y*width*3+x*3+2], fp);
+                fputc(m_bitmap[y*width*3+x*3+1], fp);
+                fputc(m_bitmap[y*width*3+x*3+0], fp);
+            }
+        }    
+        fclose(fp);
+        return true;
+    }
+};
+
+#endif // BMP24_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp	Sat Feb 02 01:25:25 2013 +0000
@@ -0,0 +1,57 @@
+// SimpleJpegDecode_example/main.cpp 2013/2/2
+// convert jpeg to bmp
+//
+#include "mbed.h"
+#include "rtos.h"
+#include "BaseUsbHost.h"
+#include "SimpleJpegDecode.h"
+#include "UsbFlashDrive.h"
+#include "bmp24.h"
+
+#define INPUT_FILE  "/usb/input.jpg"
+#define OUTPUT_FILE "/usb/output.bmp"
+
+Serial pc(USBTX, USBRX);
+
+SimpleJpegDecode decode;
+bmp24 bmp;
+
+void callbackRGB(int x, int y, uint8_t* rgb)
+{
+    bmp.point(x, y, rgb);
+}
+
+int main() {
+    pc.baud(921600);
+    printf("%s\n", __FILE__);
+
+    BaseUsbHost* usbHost = new BaseUsbHost();
+    ControlEp* ctlEp = new ControlEp; // root hub
+    if (UsbHub::check(ctlEp)) {
+        UsbHub* hub = new UsbHub(ctlEp);
+        ctlEp = hub->search<UsbFlashDrive>();
+    }
+    if (!UsbFlashDrive::check(ctlEp)) {
+        error("USB flash drive is not connected.\n");
+    }
+    UsbFlashDrive* drive = new UsbFlashDrive("usb", ctlEp);
+
+    bmp.clear();
+    decode.setOnResult(callbackRGB);
+    decode.clear();
+    printf("input: %s\n", INPUT_FILE);
+    FILE* fp = fopen(INPUT_FILE, "rb");
+    if (fp == NULL) {
+         error("open error\n");
+    }
+    while(!feof(fp)) {
+        int c = fgetc(fp);
+        decode.input(c);
+    }
+    fclose(fp);
+    printf("output: %s\n", OUTPUT_FILE);
+    if (!bmp.writeFile(OUTPUT_FILE)) {
+        error("write error\n");
+    }
+    exit(1);     
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed-rtos.lib	Sat Feb 02 01:25:25 2013 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/mbed_official/code/mbed-rtos/#53e6cccd8782
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed.bld	Sat Feb 02 01:25:25 2013 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/mbed_official/code/mbed/builds/0954ebd79f59
\ No newline at end of file