Add the PID for the Touch to the list of MH PIDs.
Add the Touch's Skin/Arch to the remote_info structs.
Add support for handling the Touch's non-standard serial number, and provide
a libconcord call to expose this information.
Don't try to read the /cfg/usercfg on the Link/Touch.
Remove set_configuration - doesn't seem to be needed and causes problems for
Harmony Touch.
Add new API methods so that MHGUI can perform a sync operation.
Fix WriteFile() so that it reads and responds to the periodic acks.
Implement SetTime() for the Touch.

Signed-off-by: Scott Talbert <s...@techie.net>
---
 concordance/concordance.c                |  12 ++-
 libconcord/bindings/python/libconcord.py |  27 +++++++
 libconcord/libconcord.cpp                |  23 ++++++
 libconcord/libconcord.h                  |   5 ++
 libconcord/libusbhid.cpp                 |   5 --
 libconcord/remote.h                      |   2 +
 libconcord/remote_info.h                 |  15 +++-
 libconcord/remote_mh.cpp                 | 126 +++++++++++++++++++++----------
 8 files changed, 168 insertions(+), 47 deletions(-)

diff --git a/concordance/concordance.c b/concordance/concordance.c
index 4471697..860af34 100644
--- a/concordance/concordance.c
+++ b/concordance/concordance.c
@@ -900,8 +900,16 @@ int print_version_info(struct options_t *options)
         printf("  USB PID: %04X\n", get_usb_pid());
         printf("  USB Ver: %04X\n", get_usb_bcd());
 
-        printf("  Serial Number: %s\n\t%s\n\t%s\n", get_serial(1),
-               get_serial(2), get_serial(3));
+       /*
+        * Certain MH remotes (but not all) such as the Harmony Touch use a
+        * different serial number format.  In this case, mh_get_serial() will
+        * return a non-zero-length string which will have the correct serial.
+        */
+        if (strlen(mh_get_serial()) != 0)
+            printf("  Serial Number: %s\n", mh_get_serial());
+        else
+            printf("  Serial Number: %s\n\t%s\n\t%s\n", get_serial(1),
+                   get_serial(2), get_serial(3));
     }
 
     used = get_config_bytes_used();
diff --git a/libconcord/bindings/python/libconcord.py 
b/libconcord/bindings/python/libconcord.py
index 718fc0e..491471a 100644
--- a/libconcord/bindings/python/libconcord.py
+++ b/libconcord/bindings/python/libconcord.py
@@ -973,3 +973,30 @@ mh_set_wifi_config = _create_func(
     _ret_lc_concord(),
     _in('config', POINTER(mh_wifi_config))
 )
+
+# const char *mh_get_serial();
+mh_get_serial = _create_func(
+    'mh_get_serial',
+    c_char_p
+)
+
+#int mh_read_file(const char *filename, uint8_t *buffer, const uint32_t buflen,
+#                 uint32_t *data_read);
+mh_read_file = _create_func(
+    'mh_read_file',
+    _ret_lc_concord(),
+    _in('filename', c_char_p),
+    _in('buffer', POINTER(c_ubyte)),
+    _in('buflen', c_uint),
+    _out('data_read', c_uint)
+)
+
+#int mh_write_file(const char *filename, uint8_t *buffer,
+#                  const uint32_t buflen);
+mh_write_file = _create_func(
+    'mh_write_file',
+    _ret_lc_concord(),
+    _in('filename', c_char_p),
+    _in('buffer', POINTER(c_ubyte)),
+    _in('buflen', c_uint)
+)
diff --git a/libconcord/libconcord.cpp b/libconcord/libconcord.cpp
index be54ab5..a335b6c 100644
--- a/libconcord/libconcord.cpp
+++ b/libconcord/libconcord.cpp
@@ -229,6 +229,8 @@ int is_mh_pid(unsigned int pid)
         case 0xC124: /* Harmony 300 */
         case 0xC125: /* Harmony 200 */
         case 0xC126: /* Harmony Link */
+        case 0xC129: /* Harmony Hub */
+        case 0xC12B: /* Harmony Touch/Ultimate */
             return 1;
         default:
             return 0;
@@ -1942,6 +1944,27 @@ int mh_set_wifi_config(const struct mh_wifi_config 
*config)
     return err;
 }
 
+const char *mh_get_serial()
+{
+    return ri.mh_serial.c_str();
+}
+
+int mh_read_file(const char *filename, uint8_t *buffer, const uint32_t buflen,
+                 uint32_t *data_read)
+{
+    if (!is_mh_remote())
+        return LC_ERROR;
+    return rmt->ReadFile(filename, buffer, buflen, data_read, 0x00, NULL, NULL,
+                         0);
+}
+
+int mh_write_file(const char *filename, uint8_t *buffer, const uint32_t buflen)
+{
+    if (!is_mh_remote())
+        return LC_ERROR;
+    return rmt->WriteFile(filename, buffer, buflen);
+}
+
 /*
  * PRIVATE-SHARED INTERNAL FUNCTIONS
  * These are functions used by the whole library but are NOT part of the API
diff --git a/libconcord/libconcord.h b/libconcord/libconcord.h
index 8e6f7ec..c0d2ba0 100644
--- a/libconcord/libconcord.h
+++ b/libconcord/libconcord.h
@@ -527,6 +527,11 @@ int mh_set_cfg_properties(const struct mh_cfg_properties 
*properties);
 int mh_get_wifi_networks(struct mh_wifi_networks *networks);
 int mh_get_wifi_config(struct mh_wifi_config *config);
 int mh_set_wifi_config(const struct mh_wifi_config *config);
+const char *mh_get_serial();
+int mh_read_file(const char *filename, uint8_t *buffer, const uint32_t buflen,
+                 uint32_t *data_read);
+int mh_write_file(const char *filename, uint8_t *buffer,
+                  const uint32_t buflen);
 
 #ifdef __cplusplus
 }
diff --git a/libconcord/libusbhid.cpp b/libconcord/libusbhid.cpp
index 915c11f..86ba786 100644
--- a/libconcord/libusbhid.cpp
+++ b/libconcord/libusbhid.cpp
@@ -149,11 +149,6 @@ int FindRemote(THIDINFO &hid_info)
 #endif
 
     int err;
-    if ((err = usb_set_configuration(h_hid, 1))) {
-        debug("Failed to set device configuration: %d (%s)", err,
-              usb_strerror());
-        return err;
-    }
 
     if ((err=usb_claim_interface(h_hid, 0))) {
         debug("Failed to claim interface: %d (%s)", err,
diff --git a/libconcord/remote.h b/libconcord/remote.h
index 6f63f62..370cf53 100644
--- a/libconcord/remote.h
+++ b/libconcord/remote.h
@@ -133,6 +133,8 @@ struct TRemoteInfo {
     uint8_t node_id;
     char *tid;
     char *xml_user_rf_setting;
+    /* Special serial number that some MH remotes use */
+    string mh_serial;
 };
 
 struct THarmonyTime {
diff --git a/libconcord/remote_info.h b/libconcord/remote_info.h
index 72d55d7..a0a7d75 100644
--- a/libconcord/remote_info.h
+++ b/libconcord/remote_info.h
@@ -129,7 +129,18 @@ static const TModel ModelList[]={
        { MFG_UNK,      "Unknown",                      NULL },
        { MFG_UNK,      "Unknown",                      NULL },
 // 90
-       { MFG_UNK,      "Unknown",                      NULL }
+       { MFG_UNK,      "Unknown",                      NULL },
+       { MFG_UNK,      "Unknown",                      NULL },
+       { MFG_UNK,      "Unknown",                      NULL },
+       { MFG_UNK,      "Unknown",                      NULL },
+       { MFG_UNK,      "Unknown",                      NULL },
+       { MFG_UNK,      "Unknown",                      NULL },
+       { MFG_UNK,      "Unknown",                      NULL },
+       { MFG_HAR,      "Harmony Hub",                  NULL },
+       { MFG_UNK,      "Unknown",                      NULL },
+       { MFG_HAR,      "Harmony Touch",                NULL },
+// 100
+       { MFG_HAR,      "Harmony Ultimate",             NULL }
 };
 
 static const unsigned int max_model=sizeof(ModelList)/sizeof(TModel)-1;
@@ -500,7 +511,7 @@ static const TArchInfo ArchList[]={
                0,                              // eeprom_size
                "",                             // usb
        },
-       /* arch 17: Link */
+       /* arch 17: Link/Touch */
        {
                0,                              // serial_location
                0,                              // serial_address
diff --git a/libconcord/remote_mh.cpp b/libconcord/remote_mh.cpp
index 88d5b45..22625df 100644
--- a/libconcord/remote_mh.cpp
+++ b/libconcord/remote_mh.cpp
@@ -367,13 +367,15 @@ int CRemoteMH::WriteFile(const char *filename, uint8_t 
*wr,
      */
     const uint8_t param = rsp[5];
 
-    uint8_t pkts_to_send = wrlen / MH_MAX_DATA_SIZE;
+    uint32_t pkts_to_send = wrlen / MH_MAX_DATA_SIZE;
     if ((wrlen % MH_MAX_DATA_SIZE) != 0)
         pkts_to_send++;
     pkts_to_send++; // count is always one more than the actual count
 
     uint8_t msg_ack[MH_MAX_PACKET_SIZE] =
-        { 0xFF, 0x03, get_seq(seq), 0x02, 0x01, param, 0x01, pkts_to_send };
+        { 0xFF, 0x03, get_seq(seq), 0x02, 0x01, param, 0x01, 0x33 };
+    if (pkts_to_send < 0x33)
+        msg_ack[7] = pkts_to_send;
 
     if ((err = HID_WriteReport(msg_ack))) {
         debug("Failed to write to remote");
@@ -386,6 +388,7 @@ int CRemoteMH::WriteFile(const char *filename, uint8_t *wr,
     uint32_t tlen = wrlen;
     uint8_t pkt_len;
     uint8_t tmp_pkt[MH_MAX_PACKET_SIZE];
+    int pkt_count = 0;
     while (tlen) {
         pkt_len = MH_MAX_DATA_SIZE;
         if (tlen < pkt_len) {
@@ -405,6 +408,29 @@ int CRemoteMH::WriteFile(const char *filename, uint8_t *wr,
             return err;
         }
         wr_ptr += pkt_len;
+        pkt_count++;
+        pkts_to_send--;
+
+        /* Every 50 data packets, the remote seems to send us an "ack"
+           of some sort.  Read it and send a response back. */
+        if (pkt_count == 50) {
+            if ((err = HID_ReadReport(rsp, MH_TIMEOUT))) {
+                debug("Failed to read from remote");
+                return LC_ERROR_READ;
+            }
+            debug_print_packet(rsp);
+            /* 3rd byte is the sequence number */
+            msg_ack[2] = get_seq(seq);
+            /* 2nd parameter is the number of packets remaining,
+               plus one */
+            if (pkts_to_send < 0x33)
+                msg_ack[7] = pkts_to_send;
+            if ((err = HID_WriteReport(msg_ack))) {
+                debug("Failed to write to remote");
+                return LC_ERROR_WRITE;
+            }
+            pkt_count = 0;
+        }
     }
 
     /*
@@ -503,38 +529,6 @@ int CRemoteMH::GetIdentity(TRemoteInfo &ri, THIDINFO &hid, 
lc_callback cb,
 
     setup_ri_pointers(ri);
 
-    if (cb) {
-        cb(cb_stage, cb_count++, 1, 2, LC_CB_COUNTER_TYPE_STEPS, cb_arg, NULL);
-    }
-
-    // Send the read config message to find the config bytes used.
-    const uint8_t msg_read_config[MH_MAX_PACKET_SIZE] =
-        { 0xFF, 0x01, 0x00, 0x03, 0x80, '/', 'c', 'f', 'g', '/',
-          'u', 's', 'e', 'r', 'c', 'f', 'g', 0x00, 0x80, 'R', 0x00 };
-    if ((err = HID_WriteReport(msg_read_config))) {
-        debug("Failed to write to remote");
-        return LC_ERROR_WRITE;
-    }
-
-    if ((err = HID_ReadReport(rsp))) {
-        debug("Failed to read from remote");
-        return LC_ERROR_READ;
-    }
-    debug("msg_read_config");
-    debug_print_packet(rsp);
-
-    // In ReadFlash() we add an extra four bytes to the end of the config file
-    // buffer, so we include space for those bytes here.
-    ri.config_bytes_used = (rsp[7] << 24) + (rsp[8] << 16) + (rsp[9] << 8)
-        + rsp[10] + 4;
-    debug("ri.config_bytes_used = %d", ri.config_bytes_used);
-    ri.max_config_size = (ri.flash->size << 10);
-    ri.valid_config = 1;
-
-    if (cb) {
-        cb(cb_stage, cb_count++, 2, 2, LC_CB_COUNTER_TYPE_STEPS, cb_arg, NULL);
-    }
-
     string guid_str = find_value(identity, "guid");
     if (guid_str.length() >= 98) {
         uint8_t guid[48];
@@ -549,9 +543,45 @@ int CRemoteMH::GetIdentity(TRemoteInfo &ri, THIDINFO &hid, 
lc_callback cb,
         }
         make_serial(guid, ri);
     }
+    ri.mh_serial = find_value(identity, "serial_number");
 
-    if ((err = reset_sequence(0x01, 0x06)))
-        return err;
+    if (cb) {
+        cb(cb_stage, cb_count++, 1, 2, LC_CB_COUNTER_TYPE_STEPS, cb_arg, NULL);
+    }
+
+    /* Arch 17 (Link/Touch) don't have the '/cfg/usercfg' so don't read it */
+    if (ri.architecture != 17) {
+        // Send the read config message to find the config bytes used.
+        const uint8_t msg_read_config[MH_MAX_PACKET_SIZE] =
+            { 0xFF, 0x01, 0x00, 0x03, 0x80, '/', 'c', 'f', 'g', '/',
+              'u', 's', 'e', 'r', 'c', 'f', 'g', 0x00, 0x80, 'R', 0x00 };
+        if ((err = HID_WriteReport(msg_read_config))) {
+            debug("Failed to write to remote");
+            return LC_ERROR_WRITE;
+        }
+
+        if ((err = HID_ReadReport(rsp))) {
+            debug("Failed to read from remote");
+            return LC_ERROR_READ;
+        }
+        debug("msg_read_config");
+        debug_print_packet(rsp);
+
+        if ((err = reset_sequence(0x01, 0x06)))
+            return err;
+
+        // In ReadFlash() we add an extra four bytes to the end of the config
+        // file buffer, so we include space for those bytes here.
+        ri.config_bytes_used = (rsp[7] << 24) + (rsp[8] << 16) + (rsp[9] << 8)
+            + rsp[10] + 4;
+        debug("ri.config_bytes_used = %d", ri.config_bytes_used);
+    }
+    ri.max_config_size = (ri.flash->size << 10);
+    ri.valid_config = 1;
+
+    if (cb) {
+        cb(cb_stage, cb_count++, 2, 2, LC_CB_COUNTER_TYPE_STEPS, cb_arg, NULL);
+    }
 
     return 0;
 }
@@ -684,12 +714,32 @@ int CRemoteMH::SetTime(const TRemoteInfo &ri, const 
THarmonyTime &ht,
                        lc_callback cb, void *cb_arg, uint32_t cb_stage)
 {
     /* 
-     * MH remotes do not support SetTime() operations, but we return
+     * Some MH remotes do not support SetTime() operations, but we return
      * success because some higher level operations (for example, update
      * configuration) call SetTime() and thus the whole operation would be
      * declared a failure, which we do not want.
      */
-    return 0;
+    if (ri.architecture != 17) {
+        return 0;
+    } else {
+        /* Yes, the official sw seems to hard code the US-East TZ */
+        const char *tz_str = "EST5EDT,M3.2.0,M11.1.0";
+        size_t tz_str_len = strlen(tz_str);
+        const uint32_t tsv_len = 16 + tz_str_len;
+        uint8_t tsv[tsv_len];
+        tsv[0] = ht.year >> 8;
+        tsv[1] = ht.year;
+        tsv[2] = ht.month;
+        tsv[3] = ht.day;
+        tsv[4] = ht.hour;
+        tsv[5] = ht.minute;
+        tsv[6] = ht.second;
+        tsv[7] = ht.dow;
+        for (int i = 8; i < 16; i++)
+            tsv[i] = 0;
+        memcpy(&tsv[16], tz_str, tz_str_len);
+        return WriteFile("/sys/time", tsv, tsv_len);
+    }
 }
 
 int CRemoteMH::LearnIR(uint32_t *freq, uint32_t **ir_signal,
-- 
2.1.0


------------------------------------------------------------------------------
Dive into the World of Parallel Programming The Go Parallel Website, sponsored
by Intel and developed in partnership with Slashdot Media, is your hub for all
things parallel software development, from weekly thought leadership blogs to
news, videos, case studies, tutorials and more. Take a look and join the 
conversation now. http://goparallel.sourceforge.net/
_______________________________________________
concordance-devel mailing list
concordance-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/concordance-devel

Reply via email to