Blob Blame History Raw
From df5b67509d2b27114695d5bb3d32da22f9d0a0ef Mon Sep 17 00:00:00 2001
From: ClockSelect <mrrraow@gmail.com>
Date: Sun, 21 Aug 2016 05:44:39 +0200
Subject: [PATCH 1/7] New commands for myevic

---
 README.rst        |  30 ++++++++++
 evic/cli.py       | 164 +++++++++++++++++++++++++++++++++++++++++++-----------
 evic/dataflash.py |   7 +++
 evic/device.py    |  88 ++++++++++++++++++++---------
 4 files changed, 230 insertions(+), 59 deletions(-)

diff --git a/README.rst b/README.rst
index 344ddbf..fec0c37 100644
--- a/README.rst
+++ b/README.rst
@@ -124,3 +124,33 @@ Use  ``--no-verify`` to disable verification for APROM or data flash. To disable
 ::
 
     $ evic-usb upload --no-verify aprom --no-verify dataflash firmware.bin
+
+Reset the device:
+
+::
+
+    $ evic-usb reset
+
+Dump any part of the flash memory:
+
+::
+
+    $ evic-usb fmc-read -o out.bin -s startaddr -l length
+
+Example to read the parameters flash memory:
+
+::
+
+    $ evic-usb fmc-read -o out.bin -s 122880 -l 4096
+
+Setup date and time of the device to the current time:
+
+::
+
+    $ evic-usb time
+
+Take a screenshot of the device display:
+
+::
+
+    $ evic-usb screenshot -o outfile.[png|jpg|...]
diff --git a/evic/cli.py b/evic/cli.py
index 9d81038..8d0acdc 100644
--- a/evic/cli.py
+++ b/evic/cli.py
@@ -24,12 +24,13 @@
 import struct
 from time import sleep
 from contextlib import contextmanager
+from datetime import datetime
+from PIL import Image
 
 import click
 
 import evic
 
-from .device import DeviceInfo
 
 @contextmanager
 def handle_exceptions(*exceptions):
@@ -108,17 +109,39 @@ def read_dataflash(dev, verify):
     return dataflash
 
 
-def print_device_info(device_info, dataflash):
+def fmc_read(dev, start, len):
+    """Reads the device data flash.
+
+    Args:
+        dev: evic.HIDTransfer object.
+
+    Returns:
+        evic.DataFlash object containing the device data flash.
+    """
+
+    # Read the data flash
+    with handle_exceptions(IOError):
+        click.echo("Reading data flash...", nl=False)
+        fmemory = dev.fmc_read(start, len)
+
+    return fmemory
+
+
+def print_device_info(dev, dataflash):
     """Prints the device information found from data flash.
 
     Args:
-        device_info: device.DeviceInfo tuple.
+        dev: evic.HIDTransfer object.
         dataflash: evic.DataFlash object.
     """
 
+    # Find the product name
+    product_name = dev.product_names.get(dataflash.product_id,
+                                         "Unknown device")
+
     # Print out the information
     click.echo("\tDevice name: ", nl=False)
-    click.secho(device_info.name, bold=True)
+    click.secho(product_name, bold=True)
     click.echo("\tFirmware version: ", nl=False)
     click.secho("{0:.2f}".format(dataflash.fw_version / 100.0), bold=True)
     click.echo("\tHardware version: ", nl=False)
@@ -167,12 +190,8 @@ def upload(inputfile, encrypted, dataflashfile, noverify):
     dataflash = read_dataflash(dev, verify)
     dataflash_original = copy.deepcopy(dataflash)
 
-    # Get the device info
-    device_info = dev.devices.get(dataflash.product_id,
-                                  DeviceInfo("Unknown device", None, None))
-
     # Print the device information
-    print_device_info(device_info, dataflash)
+    print_device_info(dev, dataflash)
 
     # Read the APROM image
     aprom = evic.APROM(inputfile.read())
@@ -183,12 +202,9 @@ def upload(inputfile, encrypted, dataflashfile, noverify):
     if 'aprom' not in noverify:
         with handle_exceptions(evic.APROMError):
             click.echo("Verifying APROM...", nl=False)
-
-            supported_product_ids = [dataflash.product_id]
-            if device_info.supported_product_ids:
-                supported_product_ids.extend(device_info.supported_product_ids)
-
-            aprom.verify(supported_product_ids, dataflash.hw_version)
+            aprom.verify(
+                dev.supported_product_ids[dataflash.product_id],
+                dataflash.hw_version)
 
     # Are we using a data flash file?
     if dataflashfile:
@@ -237,6 +253,53 @@ def upload(inputfile, encrypted, dataflashfile, noverify):
         dev.write_aprom(aprom)
 
 
+@usb.command('reset')
+def reset():
+    """Resets the device."""
+
+    dev = evic.HIDTransfer()
+
+    # Connect the device
+    connect(dev)
+
+    # Restart
+    click.echo("Restarting the device...", nl=False)
+    dev.reset()
+    sleep(2)
+    click.secho("OK", fg='green', nl=False, bold=True)
+
+
+@usb.command('time')
+def time():
+    """Sets the device date/time to now."""
+
+    dev = evic.HIDTransfer()
+    
+    # Connect the device
+    connect(dev)
+    
+    # Read the data flash
+    dataflash = read_dataflash(dev, 1)
+    
+    # Print the device information
+    print_device_info(dev, dataflash)
+    
+    dt = datetime.now()
+    dataflash.df_year = dt.year
+    dataflash.df_month = dt.month
+    dataflash.df_day = dt.day
+    dataflash.df_hour = dt.hour
+    dataflash.df_minute = dt.minute
+    dataflash.df_second = dt.second
+
+    # Write data flash to the device
+    with handle_exceptions(IOError):
+        click.echo("Writing data flash...", nl=False)
+        sleep(0.1)
+        dev.write_dataflash(dataflash)
+        click.secho("OK", fg='green', bold=True)
+
+
 @usb.command('upload-logo')
 @click.argument('inputfile', type=click.File('rb'))
 @click.option('--invert', '-i', is_flag=True,
@@ -258,27 +321,22 @@ def uploadlogo(inputfile, invert, noverify):
     dataflash = read_dataflash(dev, noverify)
     dataflash_original = copy.deepcopy(dataflash)
 
-    # Get the device info
-    device_info = dev.devices.get(dataflash.product_id,
-                                  DeviceInfo("Unknown device", None, None))
-
     # Print the device information
-    print_device_info(device_info, dataflash)
+    print_device_info(dev, dataflash)
 
     # Convert the image
     with handle_exceptions(evic.LogoConversionError):
         click.echo("Converting logo...", nl=False)
-
-        # Check supported logo dimensions
-        logo_dimensions = device_info.logo_dimensions
-        if not logo_dimensions:
+        # Check supported logo size
+        try:
+            logosize = dev.supported_logo_size[dataflash.product_id]
+        except KeyError:
             raise evic.LogoConversionError("Device doesn't support logos.")
-
         # Perform the actual conversion
         logo = evic.logo.fromimage(inputfile, invert)
-        if (logo.width, logo.height) != logo_dimensions:
+        if (logo.width, logo.height) != logosize:
             raise evic.LogoConversionError("Device only supports {}x{} logos."
-                                           .format(*logo_dimensions))
+                                           .format(*logosize))
 
     # We want to boot to LDROM on restart
     if not dev.ldrom:
@@ -325,12 +383,8 @@ def dumpdataflash(output, noverify):
     # Read the data flash
     dataflash = read_dataflash(dev, noverify)
 
-    # Get the device info
-    device_info = dev.devices.get(dataflash.product_id,
-                                  DeviceInfo("Unknown device", None, None))
-
     # Print the device information
-    print_device_info(device_info, dataflash)
+    print_device_info(dev, dataflash)
 
     # Write the data flash to the file
     with handle_exceptions(IOError):
@@ -338,6 +392,52 @@ def dumpdataflash(output, noverify):
         output.write(dataflash.array)
 
 
+@usb.command('fmcread')
+@click.option('--output', '-o', type=click.File('wb'), required=True)
+@click.option('--start', '-s', type=click.INT, required=True)
+@click.option('--length', '-l', type=click.INT, required=True)
+def fmcread(output, start, length):
+    """Write device flash memory to a file."""
+
+    dev = evic.HIDTransfer()
+
+    # Connect the device
+    connect(dev)
+
+    # Print the USB info of the device
+    print_usb_info(dev)
+
+    # Read the data flash
+    fmemory = fmc_read(dev, start, length)
+
+    # Write the data flash to the file
+    with handle_exceptions(IOError):
+        click.echo("Writing flash memory to the file...", nl=False)
+        output.write(fmemory)
+
+
+@usb.command('screenshot')
+@click.option('--output', '-o', type=click.File('wb'), required=True)
+def screenshot(output):
+    """Take a screenshot."""
+
+    dev = evic.HIDTransfer()
+
+    # Connect the device
+    connect(dev)
+
+    # Read the screen data
+    data = dev.read_screen()
+
+    # create the image from screen data
+    im = Image.fromstring("1",(64,128),bytes(data))
+    
+    # Write the image to the file
+    with handle_exceptions(IOError):
+        click.echo("Writing image to the file...", nl=False)
+        im.save(output,"PNG")
+
+
 @usb.command('reset-dataflash')
 def resetdataflash():
     """Reset device data flash."""
diff --git a/evic/dataflash.py b/evic/dataflash.py
index 31abb2a..0c61651 100644
--- a/evic/dataflash.py
+++ b/evic/dataflash.py
@@ -46,6 +46,13 @@ class DataFlash(binstruct.StructTemplate):
     fw_version = binstruct.Int32Field(256)
     ldrom_version = binstruct.Int32Field(260)
 
+    df_year = binstruct.Int16Field(320)
+    df_month = binstruct.Int8Field(322)
+    df_day = binstruct.Int8Field(323)
+    df_hour = binstruct.Int8Field(324)
+    df_minute = binstruct.Int8Field(325)
+    df_second = binstruct.Int8Field(326)
+
     def verify(self, checksum):
         """Verifies the data flash against given checksum.
 
diff --git a/evic/device.py b/evic/device.py
index 0d94545..a5be4af 100644
--- a/evic/device.py
+++ b/evic/device.py
@@ -18,7 +18,6 @@
 """
 
 import struct
-from collections import namedtuple
 
 try:
     import hid
@@ -28,8 +27,6 @@
 
 from .dataflash import DataFlash
 
-DeviceInfo = namedtuple('DeviceInfo',
-                        'name supported_product_ids logo_dimensions')
 
 class HIDTransfer(object):
     """Generic Nuvoton HID Transfer device class.
@@ -37,7 +34,13 @@ class HIDTransfer(object):
     Attributes:
         vid: USB vendor ID.
         pid: USB product ID.
-        devices: A dictionary mapping product IDs to DeviceInfo tuples.
+        product_names: A dictionary mapping product IDs to
+                       product name strings.
+        supported_product_ids: A dictionary mapping product ID to a list of
+                               strings containing the IDs of the products
+                               with compatible firmware.
+        supported_logo_size: A dictionary mapping product ID to the allowed
+                             logo resolution as a (width, height) tuple.
         hid_signature: A bytearray containing the HID command signature
                        (4 bytes).
         device: A HIDAPI device.
@@ -49,29 +52,32 @@ class HIDTransfer(object):
 
     vid = 0x0416
     pid = 0x5020
-    devices = {'E043': DeviceInfo("eVic VTwo", None, (64, 40)),
-               'E052': DeviceInfo("eVic-VTC Mini", ['W007'], (64, 40)),
-               'E056': DeviceInfo("CUBOID MINI", None, (64, 40)),
-               'E060': DeviceInfo("Cuboid", None, (64, 40)),
-               'E083': DeviceInfo("eGrip II", None, (64, 40)),
-               'E092': DeviceInfo("eVic AIO", None, (64, 40)),
-               'E115': DeviceInfo("eVic VTwo mini", None, (64, 40)),
-               'E150': DeviceInfo("eVic Basic", None, (64, 40)),
-               'M011': DeviceInfo("iStick TC100W", None, None),
-               'M037': DeviceInfo("ASTER", None, (96, 16)),
-               'M041': DeviceInfo("iStick Pico", None, (96, 16)),
-               'M045': DeviceInfo("iStick Pico Mega", None, (96, 16)),
-               'M046': DeviceInfo("iPower", None, (96, 16)),
-               'W007': DeviceInfo("Presa TC75W", ['E052'], None),
-               'W010': DeviceInfo("Classic", None, None),
-               'W011': DeviceInfo("Lite", None, None),
-               'W013': DeviceInfo("Stout", None, None),
-               'W014': DeviceInfo("Reuleaux RX200", None, None),
-               'W016': DeviceInfo("CENTURION", None, None),
-               'W018': DeviceInfo("Reuleaux RX2/3", None, (64, 48)),
-               'W033': DeviceInfo("Reuleaux RX200S", None, None)
-              }
-
+    product_names = {'E052': "eVic-VTC Mini",
+                     'E056': "CUBOID MINI",
+                     'E060': "Cuboid",
+                     'E083': "eGrip II",
+                     'M011': "iStick TC100W",
+                     'M041': "iStick Pico",
+                     'W007': "Presa TC75W",
+                     'W010': "Classic",
+                     'W011': "Lite",
+                     'W013': "Stout",
+                     'W014': "Reuleaux RX200"}
+    supported_product_ids = {'E052': ['E052', 'W007'],
+                             'E056': ['E056'],
+                             'E060': ['E060'],
+                             'M011': ['M011'],
+                             'M041': ['M041'],
+                             'W007': ['W007', 'E052'],
+                             'W010': ['W010'],
+                             'W011': ['W011'],
+                             'W013': ['W013'],
+                             'W014': ['W014']}
+    supported_logo_size = {'E052': (64, 40),
+                           'E056': (64, 40),
+                           'E060': (64, 40),
+                           'E083': (64, 40),
+                           'M041': (96, 16)}
     # 0x43444948
     hid_signature = bytearray(b'HIDC')
 
@@ -161,6 +167,34 @@ def read_dataflash(self):
 
         return (dataflash, checksum)
 
+    def fmc_read(self, start, length):
+        """Reads the device flash memory.
+
+        Returns:
+            An array containing the data flash memory content.
+        """
+
+        # Send the command for reading the data flash
+        self.send_command(0xC0, start, length)
+
+        # Read the dataflash
+        buf = self.read(length)
+        return (buf)
+
+    def read_screen(self):
+        """Reads the screen memory.
+
+        Returns:
+            An array containing the screen.
+        """
+
+        # Send the command for reading the screen buffer
+        self.send_command(0xC1, 0, 0x400)
+
+        # Read the data
+        buf = self.read(0x400)
+        return (buf)
+
     def write(self, data):
         """Writes data to the device.
 

From 1cf91b1cd8165b86df373d6feb273ec37ec19141 Mon Sep 17 00:00:00 2001
From: ClockSelect <mrrraow@gmail.com>
Date: Sun, 21 Aug 2016 06:27:00 +0200
Subject: [PATCH 2/7] Integrated changes from master

---
 evic/cli.py    | 52 +++++++++++++++++++++++++++++++++------------------
 evic/device.py | 59 ++++++++++++++++++++++++++--------------------------------
 2 files changed, 60 insertions(+), 51 deletions(-)

diff --git a/evic/cli.py b/evic/cli.py
index 8d0acdc..b613bfb 100644
--- a/evic/cli.py
+++ b/evic/cli.py
@@ -30,6 +30,7 @@
 import click
 
 import evic
+from .device import DeviceInfo
 
 
 @contextmanager
@@ -127,21 +128,17 @@ def fmc_read(dev, start, len):
     return fmemory
 
 
-def print_device_info(dev, dataflash):
+def print_device_info(device_info, dataflash):
     """Prints the device information found from data flash.
 
     Args:
-        dev: evic.HIDTransfer object.
+        device_info: device.DeviceInfo tuple.
         dataflash: evic.DataFlash object.
     """
 
-    # Find the product name
-    product_name = dev.product_names.get(dataflash.product_id,
-                                         "Unknown device")
-
     # Print out the information
     click.echo("\tDevice name: ", nl=False)
-    click.secho(product_name, bold=True)
+    click.secho(device_info.name, bold=True)
     click.echo("\tFirmware version: ", nl=False)
     click.secho("{0:.2f}".format(dataflash.fw_version / 100.0), bold=True)
     click.echo("\tHardware version: ", nl=False)
@@ -190,8 +187,12 @@ def upload(inputfile, encrypted, dataflashfile, noverify):
     dataflash = read_dataflash(dev, verify)
     dataflash_original = copy.deepcopy(dataflash)
 
+    # Get the device info
+    device_info = dev.devices.get(dataflash.product_id,		
+                                  DeviceInfo("Unknown device", None, None))		
+		
     # Print the device information
-    print_device_info(dev, dataflash)
+    print_device_info(device_info, dataflash)
 
     # Read the APROM image
     aprom = evic.APROM(inputfile.read())
@@ -202,9 +203,12 @@ def upload(inputfile, encrypted, dataflashfile, noverify):
     if 'aprom' not in noverify:
         with handle_exceptions(evic.APROMError):
             click.echo("Verifying APROM...", nl=False)
-            aprom.verify(
-                dev.supported_product_ids[dataflash.product_id],
-                dataflash.hw_version)
+
+            supported_product_ids = [dataflash.product_id]
+            if device_info.supported_product_ids:
+                supported_product_ids.extend(device_info.supported_product_ids)
+		
+            aprom.verify(supported_product_ids, dataflash.hw_version)
 
     # Are we using a data flash file?
     if dataflashfile:
@@ -281,8 +285,12 @@ def time():
     # Read the data flash
     dataflash = read_dataflash(dev, 1)
     
+    # Get the device info
+    device_info = dev.devices.get(dataflash.product_id,		
+                                  DeviceInfo("Unknown device", None, None))		
+		
     # Print the device information
-    print_device_info(dev, dataflash)
+    print_device_info(device_info, dataflash)
     
     dt = datetime.now()
     dataflash.df_year = dt.year
@@ -321,22 +329,25 @@ def uploadlogo(inputfile, invert, noverify):
     dataflash = read_dataflash(dev, noverify)
     dataflash_original = copy.deepcopy(dataflash)
 
+    # Get the device info		
+    device_info = dev.devices.get(dataflash.product_id,		
+                                  DeviceInfo("Unknown device", None, None))		
+
     # Print the device information
-    print_device_info(dev, dataflash)
+    print_device_info(device_info, dataflash)
 
     # Convert the image
     with handle_exceptions(evic.LogoConversionError):
         click.echo("Converting logo...", nl=False)
         # Check supported logo size
-        try:
-            logosize = dev.supported_logo_size[dataflash.product_id]
-        except KeyError:
+        logo_dimensions = device_info.logo_dimensions
+        if not logo_dimensions:
             raise evic.LogoConversionError("Device doesn't support logos.")
         # Perform the actual conversion
         logo = evic.logo.fromimage(inputfile, invert)
         if (logo.width, logo.height) != logosize:
             raise evic.LogoConversionError("Device only supports {}x{} logos."
-                                           .format(*logosize))
+                                           .format(*logo_dimensions))
 
     # We want to boot to LDROM on restart
     if not dev.ldrom:
@@ -383,8 +394,13 @@ def dumpdataflash(output, noverify):
     # Read the data flash
     dataflash = read_dataflash(dev, noverify)
 
+    # Get the device info		
+    device_info = dev.devices.get(dataflash.product_id,		
+                                  DeviceInfo("Unknown device", None, None))		
+		
+
     # Print the device information
-    print_device_info(dev, dataflash)
+    print_device_info(device_info, dataflash)
 
     # Write the data flash to the file
     with handle_exceptions(IOError):
diff --git a/evic/device.py b/evic/device.py
index a5be4af..e3f6a7c 100644
--- a/evic/device.py
+++ b/evic/device.py
@@ -18,6 +18,7 @@
 """
 
 import struct
+from collections import namedtuple
 
 try:
     import hid
@@ -27,6 +28,8 @@
 
 from .dataflash import DataFlash
 
+DeviceInfo = namedtuple('DeviceInfo',		
+                        'name supported_product_ids logo_dimensions')
 
 class HIDTransfer(object):
     """Generic Nuvoton HID Transfer device class.
@@ -34,13 +37,7 @@ class HIDTransfer(object):
     Attributes:
         vid: USB vendor ID.
         pid: USB product ID.
-        product_names: A dictionary mapping product IDs to
-                       product name strings.
-        supported_product_ids: A dictionary mapping product ID to a list of
-                               strings containing the IDs of the products
-                               with compatible firmware.
-        supported_logo_size: A dictionary mapping product ID to the allowed
-                             logo resolution as a (width, height) tuple.
+        devices: A dictionary mapping product IDs to DeviceInfo tuples.		
         hid_signature: A bytearray containing the HID command signature
                        (4 bytes).
         device: A HIDAPI device.
@@ -52,32 +49,28 @@ class HIDTransfer(object):
 
     vid = 0x0416
     pid = 0x5020
-    product_names = {'E052': "eVic-VTC Mini",
-                     'E056': "CUBOID MINI",
-                     'E060': "Cuboid",
-                     'E083': "eGrip II",
-                     'M011': "iStick TC100W",
-                     'M041': "iStick Pico",
-                     'W007': "Presa TC75W",
-                     'W010': "Classic",
-                     'W011': "Lite",
-                     'W013': "Stout",
-                     'W014': "Reuleaux RX200"}
-    supported_product_ids = {'E052': ['E052', 'W007'],
-                             'E056': ['E056'],
-                             'E060': ['E060'],
-                             'M011': ['M011'],
-                             'M041': ['M041'],
-                             'W007': ['W007', 'E052'],
-                             'W010': ['W010'],
-                             'W011': ['W011'],
-                             'W013': ['W013'],
-                             'W014': ['W014']}
-    supported_logo_size = {'E052': (64, 40),
-                           'E056': (64, 40),
-                           'E060': (64, 40),
-                           'E083': (64, 40),
-                           'M041': (96, 16)}
+    devices = {'E043': DeviceInfo("eVic VTwo", None, (64, 40)),
+               'E052': DeviceInfo("eVic-VTC Mini", ['W007'], (64, 40)),
+               'E056': DeviceInfo("CUBOID MINI", None, (64, 40)),
+               'E060': DeviceInfo("Cuboid", None, (64, 40)),
+               'E083': DeviceInfo("eGrip II", None, (64, 40)),
+               'E092': DeviceInfo("eVic AIO", None, (64, 40)),
+               'E115': DeviceInfo("eVic VTwo mini", None, (64, 40)),
+               'E150': DeviceInfo("eVic Basic", None, (64, 40)),
+               'M011': DeviceInfo("iStick TC100W", None, None),
+               'M037': DeviceInfo("ASTER", None, (96, 16)),
+               'M041': DeviceInfo("iStick Pico", None, (96, 16)),
+               'M045': DeviceInfo("iStick Pico Mega", None, (96, 16)),
+               'M046': DeviceInfo("iPower", None, (96, 16)),
+               'W007': DeviceInfo("Presa TC75W", ['E052'], None),
+               'W010': DeviceInfo("Classic", None, None),
+               'W011': DeviceInfo("Lite", None, None),
+               'W013': DeviceInfo("Stout", None, None),
+               'W014': DeviceInfo("Reuleaux RX200", None, None),
+               'W016': DeviceInfo("CENTURION", None, None),
+               'W018': DeviceInfo("Reuleaux RX2/3", None, (64, 48)),
+               'W033': DeviceInfo("Reuleaux RX200S", None, None)
+              }
     # 0x43444948
     hid_signature = bytearray(b'HIDC')
 

From 87dc7d1b6c32bfa1c9edb7a5db055a78190a5178 Mon Sep 17 00:00:00 2001
From: ClockSelect <mrrraow@gmail.com>
Date: Sun, 21 Aug 2016 06:30:41 +0200
Subject: [PATCH 3/7] Blanks cleanup

---
 evic/cli.py    | 38 +++++++++++++++++++-------------------
 evic/device.py |  4 ++--
 2 files changed, 21 insertions(+), 21 deletions(-)

diff --git a/evic/cli.py b/evic/cli.py
index b613bfb..dc4c84a 100644
--- a/evic/cli.py
+++ b/evic/cli.py
@@ -188,9 +188,9 @@ def upload(inputfile, encrypted, dataflashfile, noverify):
     dataflash_original = copy.deepcopy(dataflash)
 
     # Get the device info
-    device_info = dev.devices.get(dataflash.product_id,		
-                                  DeviceInfo("Unknown device", None, None))		
-		
+    device_info = dev.devices.get(dataflash.product_id,
+                                  DeviceInfo("Unknown device", None, None))
+
     # Print the device information
     print_device_info(device_info, dataflash)
 
@@ -207,7 +207,7 @@ def upload(inputfile, encrypted, dataflashfile, noverify):
             supported_product_ids = [dataflash.product_id]
             if device_info.supported_product_ids:
                 supported_product_ids.extend(device_info.supported_product_ids)
-		
+
             aprom.verify(supported_product_ids, dataflash.hw_version)
 
     # Are we using a data flash file?
@@ -278,20 +278,20 @@ def time():
     """Sets the device date/time to now."""
 
     dev = evic.HIDTransfer()
-    
+
     # Connect the device
     connect(dev)
-    
+
     # Read the data flash
     dataflash = read_dataflash(dev, 1)
-    
+
     # Get the device info
-    device_info = dev.devices.get(dataflash.product_id,		
-                                  DeviceInfo("Unknown device", None, None))		
-		
+    device_info = dev.devices.get(dataflash.product_id,
+                                  DeviceInfo("Unknown device", None, None))
+
     # Print the device information
     print_device_info(device_info, dataflash)
-    
+
     dt = datetime.now()
     dataflash.df_year = dt.year
     dataflash.df_month = dt.month
@@ -329,9 +329,9 @@ def uploadlogo(inputfile, invert, noverify):
     dataflash = read_dataflash(dev, noverify)
     dataflash_original = copy.deepcopy(dataflash)
 
-    # Get the device info		
-    device_info = dev.devices.get(dataflash.product_id,		
-                                  DeviceInfo("Unknown device", None, None))		
+    # Get the device info
+    device_info = dev.devices.get(dataflash.product_id,
+                                  DeviceInfo("Unknown device", None, None))
 
     # Print the device information
     print_device_info(device_info, dataflash)
@@ -394,10 +394,10 @@ def dumpdataflash(output, noverify):
     # Read the data flash
     dataflash = read_dataflash(dev, noverify)
 
-    # Get the device info		
-    device_info = dev.devices.get(dataflash.product_id,		
-                                  DeviceInfo("Unknown device", None, None))		
-		
+    # Get the device info
+    device_info = dev.devices.get(dataflash.product_id,
+                                  DeviceInfo("Unknown device", None, None))
+
 
     # Print the device information
     print_device_info(device_info, dataflash)
@@ -447,7 +447,7 @@ def screenshot(output):
 
     # create the image from screen data
     im = Image.fromstring("1",(64,128),bytes(data))
-    
+
     # Write the image to the file
     with handle_exceptions(IOError):
         click.echo("Writing image to the file...", nl=False)
diff --git a/evic/device.py b/evic/device.py
index e3f6a7c..2e0f425 100644
--- a/evic/device.py
+++ b/evic/device.py
@@ -28,7 +28,7 @@
 
 from .dataflash import DataFlash
 
-DeviceInfo = namedtuple('DeviceInfo',		
+DeviceInfo = namedtuple('DeviceInfo',
                         'name supported_product_ids logo_dimensions')
 
 class HIDTransfer(object):
@@ -37,7 +37,7 @@ class HIDTransfer(object):
     Attributes:
         vid: USB vendor ID.
         pid: USB product ID.
-        devices: A dictionary mapping product IDs to DeviceInfo tuples.		
+        devices: A dictionary mapping product IDs to DeviceInfo tuples.
         hid_signature: A bytearray containing the HID command signature
                        (4 bytes).
         device: A HIDAPI device.

From 46d35c0c7d2b554ab4a80cea7d7dafacc7f5078a Mon Sep 17 00:00:00 2001
From: ClockSelect <mrrraow@gmail.com>
Date: Sun, 21 Aug 2016 06:37:22 +0200
Subject: [PATCH 4/7] Cleanup

---
 evic/cli.py    | 7 +++++--
 evic/device.py | 1 +
 2 files changed, 6 insertions(+), 2 deletions(-)

diff --git a/evic/cli.py b/evic/cli.py
index dc4c84a..95d02c0 100644
--- a/evic/cli.py
+++ b/evic/cli.py
@@ -30,6 +30,7 @@
 import click
 
 import evic
+
 from .device import DeviceInfo
 
 
@@ -339,13 +340,15 @@ def uploadlogo(inputfile, invert, noverify):
     # Convert the image
     with handle_exceptions(evic.LogoConversionError):
         click.echo("Converting logo...", nl=False)
-        # Check supported logo size
+
+        # Check supported logo dimensions
         logo_dimensions = device_info.logo_dimensions
         if not logo_dimensions:
             raise evic.LogoConversionError("Device doesn't support logos.")
+
         # Perform the actual conversion
         logo = evic.logo.fromimage(inputfile, invert)
-        if (logo.width, logo.height) != logosize:
+        if (logo.width, logo.height) != logo_dimensions:
             raise evic.LogoConversionError("Device only supports {}x{} logos."
                                            .format(*logo_dimensions))
 
diff --git a/evic/device.py b/evic/device.py
index 2e0f425..6ba39a4 100644
--- a/evic/device.py
+++ b/evic/device.py
@@ -71,6 +71,7 @@ class HIDTransfer(object):
                'W018': DeviceInfo("Reuleaux RX2/3", None, (64, 48)),
                'W033': DeviceInfo("Reuleaux RX200S", None, None)
               }
+
     # 0x43444948
     hid_signature = bytearray(b'HIDC')
 

From 136dfd166e87be79bef453e57abbfec8191c3c41 Mon Sep 17 00:00:00 2001
From: ClockSelect <mrrraow@gmail.com>
Date: Sun, 4 Sep 2016 10:53:51 +0200
Subject: [PATCH 5/7] Logo dimensions for Presa 75 running myevic firmware

---
 evic/device.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/evic/device.py b/evic/device.py
index 6ba39a4..33be1b5 100644
--- a/evic/device.py
+++ b/evic/device.py
@@ -62,7 +62,7 @@ class HIDTransfer(object):
                'M041': DeviceInfo("iStick Pico", None, (96, 16)),
                'M045': DeviceInfo("iStick Pico Mega", None, (96, 16)),
                'M046': DeviceInfo("iPower", None, (96, 16)),
-               'W007': DeviceInfo("Presa TC75W", ['E052'], None),
+               'W007': DeviceInfo("Presa TC75W", ['E052'], (64,40)),
                'W010': DeviceInfo("Classic", None, None),
                'W011': DeviceInfo("Lite", None, None),
                'W013': DeviceInfo("Stout", None, None),

From c5235517ae27390175838b3baa65e416a7b21c36 Mon Sep 17 00:00:00 2001
From: ClockSelect <mrrraow@gmail.com>
Date: Wed, 7 Sep 2016 13:05:44 +0200
Subject: [PATCH 6/7] Fix time accuracy

---
 evic/cli.py | 15 +++++++--------
 1 file changed, 7 insertions(+), 8 deletions(-)

diff --git a/evic/cli.py b/evic/cli.py
index 95d02c0..9d774bd 100644
--- a/evic/cli.py
+++ b/evic/cli.py
@@ -293,18 +293,17 @@ def time():
     # Print the device information
     print_device_info(device_info, dataflash)
 
-    dt = datetime.now()
-    dataflash.df_year = dt.year
-    dataflash.df_month = dt.month
-    dataflash.df_day = dt.day
-    dataflash.df_hour = dt.hour
-    dataflash.df_minute = dt.minute
-    dataflash.df_second = dt.second
-
     # Write data flash to the device
     with handle_exceptions(IOError):
         click.echo("Writing data flash...", nl=False)
         sleep(0.1)
+        dt = datetime.now()
+        dataflash.df_year = dt.year
+        dataflash.df_month = dt.month
+        dataflash.df_day = dt.day
+        dataflash.df_hour = dt.hour
+        dataflash.df_minute = dt.minute
+        dataflash.df_second = dt.second
         dev.write_dataflash(dataflash)
         click.secho("OK", fg='green', bold=True)
 

From fa55dda4ea5d3337e4ee4c45ac6f52cd09b58c0e Mon Sep 17 00:00:00 2001
From: ClockSelect <mrrraow@gmail.com>
Date: Sat, 8 Oct 2016 05:03:12 +0200
Subject: [PATCH 7/7] Generic HID input command and LDROM update feature

---
 evic/cli.py    | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 evic/device.py | 34 ++++++++++++++++++++++++++
 2 files changed, 110 insertions(+)

diff --git a/evic/cli.py b/evic/cli.py
index 9d774bd..607e97d 100644
--- a/evic/cli.py
+++ b/evic/cli.py
@@ -129,6 +129,24 @@ def fmc_read(dev, start, len):
     return fmemory
 
 
+def hid_command(dev, cmd, start, len):
+    """Sends a HID command.
+
+    Args:
+        dev: evic.HIDTransfer object.
+
+    Returns:
+        evic.DataFlash object containing the device data flash.
+    """
+
+    # Send the command
+    with handle_exceptions(IOError):
+        click.echo("Sending command...", nl=False)
+        fmemory = dev.hid_command(cmd, start, len)
+
+    return fmemory
+
+
 def print_device_info(device_info, dataflash):
     """Prints the device information found from data flash.
 
@@ -258,6 +276,39 @@ def upload(inputfile, encrypted, dataflashfile, noverify):
         dev.write_aprom(aprom)
 
 
+@usb.command('upload-ldrom')
+@click.argument('inputfile', type=click.File('rb'))
+def upload_ldrom(inputfile):
+    """Upload an LDROM image to the device."""
+
+    dev = evic.HIDTransfer()
+
+    # Connect the device
+    connect(dev)
+
+    # Print the USB info of the device
+    print_usb_info(dev)
+
+    # Read the data flash
+    dataflash = read_dataflash(dev, False)
+
+    # Get the device info
+    device_info = dev.devices.get(dataflash.product_id,
+                                  DeviceInfo("Unknown device", None, None))
+
+    # Print the device information
+    print_device_info(device_info, dataflash)
+
+    # Read the LDROM image
+    ldrom = evic.APROM(inputfile.read())
+
+    # Write data flash to the device
+    with handle_exceptions(IOError):
+        # Write LDROM to the device
+        click.echo("Writing LDROM...", nl=False)
+        dev.write_ldrom(ldrom)
+
+
 @usb.command('reset')
 def reset():
     """Resets the device."""
@@ -434,6 +485,31 @@ def fmcread(output, start, length):
         output.write(fmemory)
 
 
+@usb.command('hidcmd')
+@click.option('--output', '-o', type=click.File('wb'), required=True)
+@click.option('--command', '-c', type=click.INT, required=True)
+@click.option('--start', '-s', type=click.INT, required=True)
+@click.option('--length', '-l', type=click.INT, required=True)
+def hidcmd(command, output, start, length):
+    """Send a HID command to the device."""
+
+    dev = evic.HIDTransfer()
+
+    # Connect the device
+    connect(dev)
+
+    # Print the USB info of the device
+    print_usb_info(dev)
+
+    # Send the command
+    response = hid_command(dev, command, start, length)
+
+    # Write the data flash to the file
+    with handle_exceptions(IOError):
+        click.echo("Writing command response to the file...", nl=False)
+        output.write(response)
+
+
 @usb.command('screenshot')
 @click.option('--output', '-o', type=click.File('wb'), required=True)
 def screenshot(output):
diff --git a/evic/device.py b/evic/device.py
index 33be1b5..7f27e8f 100644
--- a/evic/device.py
+++ b/evic/device.py
@@ -175,6 +175,20 @@ def fmc_read(self, start, length):
         buf = self.read(length)
         return (buf)
 
+    def hid_command(self, cmd, start, length):
+        """Send a HID command to the device.
+
+        Returns:
+            An array containing command response.
+        """
+
+        # Send the command for reading the data flash
+        self.send_command(cmd, start, length)
+
+        # Read the response
+        buf = self.read(length)
+        return (buf)
+
     def read_screen(self):
         """Reads the screen memory.
 
@@ -294,6 +308,17 @@ def write_flash(self, data, start):
 
         self.write(data)
 
+    def write_ldflash(self, data):
+        """Writes data to the flash memory.
+        """
+
+        end = len(data)
+
+        # Send the command for writing the data
+        self.send_command(0x3C, 0x100000, end)
+
+        self.write(data)
+
     def write_aprom(self, aprom):
         """Writes the APROM to the device.
 
@@ -303,6 +328,15 @@ def write_aprom(self, aprom):
 
         self.write_flash(aprom.data, 0)
 
+    def write_ldrom(self, ldrom):
+        """Writes the LDROM to the device.
+
+        Args:
+            aprom: A BinFile object containing an unencrypted LDROM image.
+        """
+
+        self.write_ldflash(ldrom.data)
+
     def write_logo(self, logo):
         """Writes the logo to the the device.