The RPi module is now supported in hmsniff and flash-ota.
hmsniff
hmsniff.d
hmsniff.o
+hmuartlgw.d
+hmuartlgw.o
util.d
util.o
*.enc
CC=gcc
HMLAN_OBJS=hmcfgusb.o hmland.o util.o
-HMSNIFF_OBJS=hmcfgusb.o hmsniff.o
+HMSNIFF_OBJS=hmcfgusb.o hmuartlgw.o hmsniff.o
FLASH_HMCFGUSB_OBJS=hmcfgusb.o firmware.o util.o flash-hmcfgusb.o
-FLASH_OTA_OBJS=hmcfgusb.o culfw.o firmware.o util.o flash-ota.o hm.o aes.o
+FLASH_OTA_OBJS=hmcfgusb.o culfw.o hmuartlgw.o firmware.o util.o flash-ota.o hm.o aes.o
OBJS=$(HMLAN_OBJS) $(HMSNIFF_OBJS) $(FLASH_HMCFGUSB_OBJS) $(FLASH_OTA_OBJS)
#include "version.h"
#include "hmcfgusb.h"
#include "culfw.h"
+#include "hmuartlgw.h"
#include "util.h"
#define MAX_RETRIES 5
/* Maximum payloadlen supported by IO */
uint32_t max_payloadlen = NORMAL_MAX_PAYLOAD;
-enum device_type {
- DEVICE_TYPE_HMCFGUSB,
- DEVICE_TYPE_CULFW,
-};
-
-struct ota_dev {
- int type;
- struct hmcfgusb_dev *hmcfgusb;
- struct culfw_dev *culfw;
-};
-
enum message_type {
MESSAGE_TYPE_E = 1,
MESSAGE_TYPE_R = 2,
};
+enum hmuartlgw_state {
+ HMUARTLGW_STATE_GET_HMID,
+ HMUARTLGW_STATE_GET_FIRMWARE,
+ HMUARTLGW_STATE_GET_CREDITS,
+ HMUARTLGW_STATE_DONE,
+ HMUARTLGW_STATE_WAIT_APP,
+ HMUARTLGW_STATE_ACK_APP,
+};
+
struct recv_data {
uint8_t message[64];
enum message_type message_type;
int speed;
uint16_t version;
uint8_t credits;
+ enum hmuartlgw_state uartlgw_state;
+ uint8_t uartlgw_version[3];
};
static int parse_hmcfgusb(uint8_t *buf, int buf_len, void *data)
return 1;
}
-int send_hm_message(struct ota_dev *dev, struct recv_data *rdata, uint8_t *msg)
+static int parse_hmuartlgw(enum hmuartlgw_dst dst, uint8_t *buf, int buf_len, void *data)
+{
+ struct recv_data *rdata = data;
+
+ if (dst == HMUARTLGW_OS) {
+ switch (rdata->uartlgw_state) {
+ case HMUARTLGW_STATE_GET_FIRMWARE:
+ if (buf[0] == HMUARTLGW_OS_ACK) {
+ rdata->uartlgw_version[0] = buf[5];
+ rdata->uartlgw_version[1] = buf[6];
+ rdata->uartlgw_version[2] = buf[7];
+ rdata->uartlgw_state = HMUARTLGW_STATE_DONE;
+ }
+ break;
+ case HMUARTLGW_STATE_GET_CREDITS:
+ if (buf[0] == HMUARTLGW_OS_ACK) {
+ rdata->credits = buf[2] / 2;
+ rdata->uartlgw_state = HMUARTLGW_STATE_DONE;
+ }
+ break;
+ default:
+ break;
+ }
+ return 0;
+ }
+
+ switch(buf[0]) {
+ case HMUARTLGW_APP_ACK:
+ if (rdata->uartlgw_state == HMUARTLGW_STATE_GET_HMID) {
+ my_hmid = (buf[4] << 16) | (buf[5] << 8) | buf[6];
+ }
+
+ rdata->status = buf[1];
+ rdata->message_type = MESSAGE_TYPE_R;
+ rdata->uartlgw_state = HMUARTLGW_STATE_ACK_APP;
+#if 0
+ hexdump(buf, buf_len, "ACK Status: ");
+#endif
+
+ break;
+ case HMUARTLGW_APP_RECV:
+ if ((!hmid) ||
+ ((buf[7] == ((hmid >> 16) & 0xff)) &&
+ (buf[8] == ((hmid >> 8) & 0xff)) &&
+ (buf[9] == (hmid & 0xff)))) {
+ memset(rdata->message, 0, sizeof(rdata->message));
+ memcpy(rdata->message + 1, buf + 4, buf_len - 4);
+ rdata->message[LEN] = buf_len - 4;
+ rdata->message_type = MESSAGE_TYPE_E;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return 1;
+}
+
+int send_hm_message(struct hm_dev *dev, struct recv_data *rdata, uint8_t *msg)
{
static uint32_t id = 1;
struct timeval tv;
}
}
break;
+ case DEVICE_TYPE_HMUARTLGW:
+ memset(out, 0, sizeof(out));
+
+ out[0] = HMUARTLGW_APP_SEND;
+ out[1] = 0x00;
+ out[2] = 0x00;
+ out[3] = (msg[CTL] & 0x10) ? 0x01 : 0x00; /* Burst?! */
+ memcpy(&out[4], &msg[1], msg[0]);
+
+ memset(rdata, 0, sizeof(struct recv_data));
+ hmuartlgw_send(dev->hmuartlgw, out, msg[0] + 4, HMUARTLGW_APP);
+
+ while (1) {
+ if (rdata->message_type == MESSAGE_TYPE_R) {
+ if ((rdata->status == 0x02) ||
+ (rdata->status == 0x03) ||
+ (rdata->status == 0x0c)) {
+ break;
+ } else {
+ if (rdata->status == 0x0d) {
+ fprintf(stderr, "\nAES handshake failed!\n");
+ } else if (rdata->status == 0x04 || rdata->status == 0x06) {
+ fprintf(stderr, "\nMissing ACK!\n");
+ } else {
+ fprintf(stderr, "\nInvalid status: %04x\n", rdata->status);
+ }
+ return 0;
+ }
+ }
+ errno = 0;
+ pfd = hmuartlgw_poll(dev->hmuartlgw, 1000);
+ if ((pfd < 0) && errno) {
+ if (errno != ETIMEDOUT) {
+ perror("\n\nhmcfgusb_poll");
+ exit(EXIT_FAILURE);
+ }
+ }
+ }
+ break;
}
id++;
return 1;
}
-static int switch_speed(struct ota_dev *dev, struct recv_data *rdata, uint8_t speed)
+static int switch_speed(struct hm_dev *dev, struct recv_data *rdata, uint8_t speed)
{
uint8_t out[0x40];
int pfd;
return culfw_send(dev->culfw, "Ar\r\n", 4);
}
break;
+ case DEVICE_TYPE_HMUARTLGW:
+ if (speed == 100) {
+ out[0] = HMUARTLGW_OS_UPDATE_MODE;
+ out[1] = 0xe9;
+ out[2] = 0xca;
+ hmuartlgw_send(dev->hmuartlgw, out, 3, HMUARTLGW_OS);
+ } else {
+ out[0] = HMUARTLGW_OS_NORMAL_MODE;
+ hmuartlgw_send(dev->hmuartlgw, out, 1, HMUARTLGW_OS);
+ }
+ break;
}
return 1;
fprintf(stderr, "\t-b bps\t\tuse CUL with speed \"bps\" (default: %u)\n", DEFAULT_CUL_BPS);
fprintf(stderr, "\t-l\t\tlower payloadlen (required for devices with little RAM, e.g. CUL v2 and CUL v4)\n");
fprintf(stderr, "\t-S serial\tuse HM-CFG-USB with given serial\n");
+ fprintf(stderr, "\t-U device\tuse HM-MOD-UART on given device\n");
fprintf(stderr, "\t-h\t\tthis help\n");
fprintf(stderr, "\nOptional parameters for automatically sending device to bootloader\n");
fprintf(stderr, "\t-C\t\tHMID of central (3 hex-bytes, no prefix, e.g. ABCDEF)\n");
char *culfw_dev = NULL;
char *endptr = NULL;
unsigned int bps = DEFAULT_CUL_BPS;
- struct ota_dev dev;
+ struct hm_dev dev;
struct recv_data rdata;
uint8_t out[0x40];
uint8_t *pos;
uint16_t len;
struct firmware *fw;
char *hmcfgusb_serial = NULL;
+ char *uart = NULL;
int block;
int pfd;
int debug = 0;
printf("HomeMatic OTA flasher version " VERSION "\n\n");
- while((opt = getopt(argc, argv, "b:c:f:hls:C:D:K:S:")) != -1) {
+ while((opt = getopt(argc, argv, "b:c:f:hls:C:D:K:S:U:")) != -1) {
switch (opt) {
case 'b':
bps = atoi(optarg);
case 'S':
hmcfgusb_serial = optarg;
break;
+ case 'U':
+ uart = optarg;
+ break;
case 'h':
case ':':
case '?':
exit(EXIT_FAILURE);
memset(&rdata, 0, sizeof(rdata));
- memset(&dev, 0, sizeof(struct ota_dev));
+ memset(&dev, 0, sizeof(struct hm_dev));
if (culfw_dev) {
printf("Opening culfw-device at path %s with speed %u\n", culfw_dev, bps);
fprintf(stderr, "\nThis version does _not_ support firmware upgrade mode, you need at least 1.58!\n");
exit(EXIT_FAILURE);
}
+ } else if (uart) {
+ uint32_t new_hmid = my_hmid;
+
+ hmuartlgw_set_debug(debug);
+
+ dev.hmuartlgw = hmuart_init(uart, parse_hmuartlgw, &rdata);
+ if (!dev.hmuartlgw) {
+ fprintf(stderr, "Can't initialize HM-MOD-UART\n");
+ exit(EXIT_FAILURE);
+ }
+ dev.type = DEVICE_TYPE_HMUARTLGW;
+
+ out[0] = HMUARTLGW_APP_GET_HMID;
+ do {
+ rdata.uartlgw_state = HMUARTLGW_STATE_GET_HMID;
+ hmuartlgw_send(dev.hmuartlgw, out, 1, HMUARTLGW_APP);
+ do { hmuartlgw_poll(dev.hmuartlgw, 500); } while (rdata.uartlgw_state != HMUARTLGW_STATE_ACK_APP);
+ } while (rdata.status == 0x08);
+
+ out[0] = HMUARTLGW_OS_GET_FIRMWARE;
+ do {
+ rdata.uartlgw_state = HMUARTLGW_STATE_GET_FIRMWARE;
+ hmuartlgw_send(dev.hmuartlgw, out, 1, HMUARTLGW_OS);
+ do { hmuartlgw_poll(dev.hmuartlgw, 500); } while (rdata.uartlgw_state != HMUARTLGW_STATE_DONE);
+ } while (rdata.status == 0x08);
+
+ out[0] = HMUARTLGW_OS_GET_CREDITS;
+ do {
+ rdata.uartlgw_state = HMUARTLGW_STATE_GET_CREDITS;
+ hmuartlgw_send(dev.hmuartlgw, out, 1, HMUARTLGW_OS);
+ do { hmuartlgw_poll(dev.hmuartlgw, 500); } while (rdata.uartlgw_state != HMUARTLGW_STATE_DONE);
+ } while (rdata.status == 0x08);
+
+ printf("HM-MOD-UART firmware version: %u.%u.%u, used credits: %u%%\n",
+ rdata.uartlgw_version[0],
+ rdata.uartlgw_version[1],
+ rdata.uartlgw_version[2],
+ rdata.credits);
+
+ if (rdata.credits >= 40) {
+ printf("\nRebooting HM-MOD-UART to avoid running out of credits\n");
+
+ hmuartlgw_enter_bootloader(dev.hmuartlgw);
+ hmuartlgw_enter_app(dev.hmuartlgw);
+ }
+
+ printf("\nHM-MOD-UART opened\n\n");
+
+ if (new_hmid && (my_hmid != new_hmid)) {
+ printf("Changing hmid from %06x to %06x\n", my_hmid, new_hmid);
+
+ out[0] = HMUARTLGW_APP_SET_HMID;
+ out[1] = (new_hmid >> 16) & 0xff;
+ out[2] = (new_hmid >> 8) & 0xff;
+ out[3] = new_hmid & 0xff;
+ do {
+ rdata.uartlgw_state = HMUARTLGW_STATE_WAIT_APP;
+ hmuartlgw_send(dev.hmuartlgw, out, 4, HMUARTLGW_APP);
+ do { hmuartlgw_poll(dev.hmuartlgw, 500); } while (rdata.uartlgw_state != HMUARTLGW_STATE_ACK_APP);
+ } while (rdata.status == 0x08);
+
+ my_hmid = new_hmid;
+ }
+
+ if (kNo > 0) {
+ printf("Setting AES-key\n");
+
+ memset(out, 0, sizeof(out));
+ out[0] = HMUARTLGW_APP_SET_CURRENT_KEY;
+ memcpy(&(out[1]), key, 16);
+ out[17] = kNo;
+
+ do {
+ rdata.uartlgw_state = HMUARTLGW_STATE_WAIT_APP;
+ hmuartlgw_send(dev.hmuartlgw, out, 18, HMUARTLGW_APP);
+ do { hmuartlgw_poll(dev.hmuartlgw, 500); } while (rdata.uartlgw_state != HMUARTLGW_STATE_ACK_APP);
+ } while (rdata.status == 0x08);
+
+ memset(out, 0, sizeof(out));
+ out[0] = HMUARTLGW_APP_SET_OLD_KEY;
+ memcpy(&(out[1]), key, 16);
+ out[17] = kNo;
+
+ do {
+ rdata.uartlgw_state = HMUARTLGW_STATE_WAIT_APP;
+ hmuartlgw_send(dev.hmuartlgw, out, 18, HMUARTLGW_APP);
+ do { hmuartlgw_poll(dev.hmuartlgw, 500); } while (rdata.uartlgw_state != HMUARTLGW_STATE_ACK_APP);
+ } while (rdata.status == 0x08);
+ }
} else {
uint32_t new_hmid = my_hmid;
}
if (hmid && my_hmid) {
+ switch (dev.type) {
+ case DEVICE_TYPE_HMCFGUSB:
+ printf("Adding HMID\n");
+
+ memset(out, 0, sizeof(out));
+ out[0] = '+';
+ out[1] = (hmid >> 16) & 0xff;
+ out[2] = (hmid >> 8) & 0xff;
+ out[3] = hmid & 0xff;
+
+ hmcfgusb_send(dev.hmcfgusb, out, sizeof(out), 1);
+ break;
+ case DEVICE_TYPE_HMUARTLGW:
+ printf("Adding HMID\n");
+
+ memset(out, 0, sizeof(out));
+ out[0] = HMUARTLGW_APP_ADD_PEER;
+ out[1] = (hmid >> 16) & 0xff;
+ out[2] = (hmid >> 8) & 0xff;
+ out[3] = hmid & 0xff;
+ out[4] = (kNo > 0) ? kNo : 0x00; /* KeyIndex */
+ out[5] = 0x00; /* WakeUp? */
+ out[6] = 0x00; /* WakeUp? */
+
+ do {
+ rdata.uartlgw_state = HMUARTLGW_STATE_WAIT_APP;
+ hmuartlgw_send(dev.hmuartlgw, out, 7, HMUARTLGW_APP);
+ do { hmuartlgw_poll(dev.hmuartlgw, 500); } while (rdata.uartlgw_state != HMUARTLGW_STATE_ACK_APP);
+ } while (rdata.status == 0x08);
+
+ break;
+ }
printf("Sending device with hmid %06x to bootloader\n", hmid);
out[CTL] = 0x30;
out[TYPE] = 0x11;
pfd = culfw_poll(dev.culfw, 1000);
break;
case DEVICE_TYPE_HMCFGUSB:
- default:
pfd = hmcfgusb_poll(dev.hmcfgusb, 1000);
break;
+ case DEVICE_TYPE_HMUARTLGW:
+ pfd = hmuartlgw_poll(dev.hmuartlgw, 1000);
+ break;
+ default:
+ pfd = -1;
+ break;
}
if ((pfd < 0) && errno) {
printf("Device with serial %s (HMID: %06x) entered firmware-update-mode\n", serial, hmid);
- if (dev.type == DEVICE_TYPE_HMCFGUSB) {
- printf("Adding HMID\n");
+ switch (dev.type) {
+ case DEVICE_TYPE_HMCFGUSB:
+ printf("Adding HMID\n");
- memset(out, 0, sizeof(out));
- out[0] = '+';
- out[1] = (hmid >> 16) & 0xff;
- out[2] = (hmid >> 8) & 0xff;
- out[3] = hmid & 0xff;
+ memset(out, 0, sizeof(out));
+ out[0] = '+';
+ out[1] = (hmid >> 16) & 0xff;
+ out[2] = (hmid >> 8) & 0xff;
+ out[3] = hmid & 0xff;
- hmcfgusb_send(dev.hmcfgusb, out, sizeof(out), 1);
+ hmcfgusb_send(dev.hmcfgusb, out, sizeof(out), 1);
+ break;
+ case DEVICE_TYPE_HMUARTLGW:
+ printf("Adding HMID\n");
+
+ memset(out, 0, sizeof(out));
+ out[0] = HMUARTLGW_APP_ADD_PEER;
+ out[1] = (hmid >> 16) & 0xff;
+ out[2] = (hmid >> 8) & 0xff;
+ out[3] = hmid & 0xff;
+ out[4] = 0x00; /* KeyIndex */
+ out[5] = 0x00; /* WakeUp? */
+ out[6] = 0x00; /* WakeUp? */
+
+ do {
+ rdata.uartlgw_state = HMUARTLGW_STATE_WAIT_APP;
+ hmuartlgw_send(dev.hmuartlgw, out, 7, HMUARTLGW_APP);
+ do { hmuartlgw_poll(dev.hmuartlgw, 500); } while (rdata.uartlgw_state != HMUARTLGW_STATE_ACK_APP);
+ } while (rdata.status == 0x08);
+
+ break;
}
switchcnt = 3;
}
printf("Waiting for device to reboot\n");
+ rdata.message_type = MESSAGE_TYPE_R;
cnt = 10;
+ if (dev.type == DEVICE_TYPE_HMUARTLGW)
+ cnt = 200; /* FIXME */
do {
errno = 0;
switch(dev.type) {
pfd = culfw_poll(dev.culfw, 1000);
break;
case DEVICE_TYPE_HMCFGUSB:
- default:
pfd = hmcfgusb_poll(dev.hmcfgusb, 1000);
break;
+ case DEVICE_TYPE_HMUARTLGW:
+ pfd = hmuartlgw_poll(dev.hmuartlgw, 1000);
+ break;
+ default:
+ pfd = -1;
+ break;
}
if ((pfd < 0) && errno) {
if (errno != ETIMEDOUT) {
#define SET_LEN_FROM_PAYLOADLEN(buf, payloadlen) do { buf[0x00] = payloadlen + 0x09; } while(0)
#define PAYLOADLEN(buf) (buf[0x00] - 0x09)
+enum device_type {
+ DEVICE_TYPE_HMCFGUSB,
+ DEVICE_TYPE_CULFW,
+ DEVICE_TYPE_HMUARTLGW,
+};
+
+struct hm_dev {
+ int type;
+ struct hmcfgusb_dev *hmcfgusb;
+ struct culfw_dev *culfw;
+ struct hmuartlgw_dev *hmuartlgw;
+};
+
uint8_t* hm_sign(uint8_t *key, uint8_t *challenge, uint8_t *m_frame, uint8_t *exp_auth, uint8_t *resp);
#include "version.h"
#include "hexdump.h"
#include "hmcfgusb.h"
+#include "hmuartlgw.h"
+#include "hm.h"
static int verbose = 0;
return 1;
}
+static int parse_hmuartlgw(enum hmuartlgw_dst dst, uint8_t *buf, int buf_len, void *data)
+{
+ if (dst == HMUARTLGW_OS) {
+ if ((buf[0] != HMUARTLGW_OS_ACK) ||
+ (buf[1] != 0x01)) {
+ hexdump(buf, buf_len, "OS> ");
+ }
+ return 0;
+ }
+
+ if (dst != HMUARTLGW_APP) {
+ return 0;
+ }
+
+ switch(buf[0]) {
+ case HMUARTLGW_APP_RECV:
+ dissect_hm(buf + 3, buf_len - 3);
+ case HMUARTLGW_APP_ACK:
+ break;
+ default:
+ hexdump(buf, buf_len, "Unknown> ");
+ }
+
+ return 1;
+}
+
void hmsniff_syntax(char *prog)
{
fprintf(stderr, "Syntax: %s options\n\n", prog);
fprintf(stderr, "Possible options:\n");
fprintf(stderr, "\t-f\t\tfast (100k/firmware update) mode\n");
fprintf(stderr, "\t-S serial\tuse HM-CFG-USB with given serial\n");
+ fprintf(stderr, "\t-U device\tuse HM-MOD-UART on given device\n");
fprintf(stderr, "\t-v\t\tverbose mode\n");
fprintf(stderr, "\t-V\t\tshow version (" VERSION ")\n");
int main(int argc, char **argv)
{
- struct hmcfgusb_dev *dev;
+ struct hm_dev dev = { 0 };
struct recv_data rdata;
char *serial = NULL;
+ char *uart = NULL;
int quit = 0;
int speed = 10;
- uint8_t speed_buf[2];
+ uint8_t buf[32];
int opt;
- while((opt = getopt(argc, argv, "fS:vV")) != -1) {
+ dev.type = DEVICE_TYPE_HMCFGUSB;
+
+ while((opt = getopt(argc, argv, "fS:U:vV")) != -1) {
switch (opt) {
case 'f':
speed = 100;
case 'S':
serial = optarg;
break;
+ case 'U':
+ uart = optarg;
+ dev.type = DEVICE_TYPE_HMUARTLGW;
+ break;
case 'v':
verbose = 1;
break;
}
}
- hmcfgusb_set_debug(0);
+ if (dev.type == DEVICE_TYPE_HMCFGUSB) {
+ hmcfgusb_set_debug(0);
+ } else {
+ hmuartlgw_set_debug(0);
+ }
do {
memset(&rdata, 0, sizeof(rdata));
rdata.wrong_hmid = 0;
- dev = hmcfgusb_init(parse_hmcfgusb, &rdata, serial);
- if (!dev) {
- fprintf(stderr, "Can't initialize HM-CFG-USB, retrying in 1s...\n");
- sleep(1);
- continue;
- }
- printf("HM-CFG-USB opened!\n");
+ if (dev.type == DEVICE_TYPE_HMCFGUSB) {
+ dev.hmcfgusb = hmcfgusb_init(parse_hmcfgusb, &rdata, serial);
+ if (!dev.hmcfgusb) {
+ fprintf(stderr, "Can't initialize HM-CFG-USB, retrying in 1s...\n");
+ sleep(1);
+ continue;
+ }
+ printf("HM-CFG-USB opened!\n");
- hmcfgusb_send_null_frame(dev, 1);
- hmcfgusb_send(dev, (unsigned char*)"K", 1, 1);
+ hmcfgusb_send_null_frame(dev.hmcfgusb, 1);
+ hmcfgusb_send(dev.hmcfgusb, (unsigned char*)"K", 1, 1);
- hmcfgusb_send_null_frame(dev, 1);
- speed_buf[0] = 'G';
- speed_buf[1] = speed;
- hmcfgusb_send(dev, speed_buf, 2, 1);
+ hmcfgusb_send_null_frame(dev.hmcfgusb, 1);
+ buf[0] = 'G';
+ buf[1] = speed;
+ hmcfgusb_send(dev.hmcfgusb, buf, 2, 1);
+ } else {
+ dev.hmuartlgw = hmuart_init(uart, parse_hmuartlgw, &rdata);
+ if (!dev.hmuartlgw) {
+ fprintf(stderr, "Can't initialize HM-MOD-UART!\n");
+ exit(1);
+ }
+ printf("HM-MOD-UART opened!\n");
+
+ buf[0] = HMUARTLGW_APP_SET_HMID;
+ buf[1] = 0x00;
+ buf[2] = 0x00;
+ buf[3] = 0x00;
+ hmuartlgw_send(dev.hmuartlgw, buf, 4, HMUARTLGW_APP);
+ do { hmuartlgw_poll(dev.hmuartlgw, 500); } while (errno != ETIMEDOUT);
+ if (speed == 100) {
+ buf[0] = HMUARTLGW_OS_UPDATE_MODE;
+ buf[1] = 0xe9;
+ buf[2] = 0xca;
+ hmuartlgw_send(dev.hmuartlgw, buf, 3, HMUARTLGW_OS);
+ } else {
+ buf[0] = HMUARTLGW_OS_NORMAL_MODE;
+ hmuartlgw_send(dev.hmuartlgw, buf, 1, HMUARTLGW_OS);
+ }
+ }
while(!quit) {
int fd;
if (rdata.wrong_hmid) {
printf("changing hmId to 000000, this might reboot the device!\n");
- hmcfgusb_send(dev, (unsigned char*)"A\00\00\00", 4, 1);
- rdata.wrong_hmid = 0;
- hmcfgusb_send(dev, (unsigned char*)"K", 1, 1);
+ if (dev.type == DEVICE_TYPE_HMCFGUSB) {
+ hmcfgusb_send(dev.hmcfgusb, (unsigned char*)"A\00\00\00", 4, 1);
+ rdata.wrong_hmid = 0;
+ hmcfgusb_send(dev.hmcfgusb, (unsigned char*)"K", 1, 1);
+ } else {
+ buf[0] = HMUARTLGW_APP_SET_HMID;
+ buf[1] = 0x00;
+ buf[2] = 0x00;
+ buf[3] = 0x00;
+ hmuartlgw_send(dev.hmuartlgw, buf, 4, HMUARTLGW_APP);
+ }
+ }
+ if (dev.type == DEVICE_TYPE_HMCFGUSB) {
+ fd = hmcfgusb_poll(dev.hmcfgusb, 1000);
+ } else {
+ fd = hmuartlgw_poll(dev.hmuartlgw, 60000);
}
- fd = hmcfgusb_poll(dev, 1000);
if (fd >= 0) {
fprintf(stderr, "activity on unknown fd %d!\n", fd);
continue;
} else if (fd == -1) {
if (errno) {
if (errno != ETIMEDOUT) {
- perror("hmcfgusb_poll");
+ perror("hmsniff_poll");
break;
} else {
/* periodically wakeup the device */
- hmcfgusb_send_null_frame(dev, 1);
+ if (dev.type == DEVICE_TYPE_HMCFGUSB) {
+ hmcfgusb_send_null_frame(dev.hmcfgusb, 1);
+ }
}
}
}
}
- hmcfgusb_close(dev);
+ if (dev.type == DEVICE_TYPE_HMCFGUSB) {
+ if (dev.hmcfgusb)
+ hmcfgusb_close(dev.hmcfgusb);
+ } else {
+ if (dev.hmuartlgw)
+ hmuartlgw_close(dev.hmuartlgw);
+ }
} while (!quit);
- hmcfgusb_exit();
+ if (dev.type == DEVICE_TYPE_HMCFGUSB) {
+ hmcfgusb_exit();
+ }
return EXIT_SUCCESS;
}
--- /dev/null
+/* HM-MOD-UART/HM-LGW-O-TW-W-EU driver
+ *
+ * Copyright (c) 2016 Michael Gernoth <michael@gernoth.net>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <poll.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "hexdump.h"
+#include "hmuartlgw.h"
+
+#define HMUARTLGW_INIT_TIMEOUT 10000
+
+#define HMUARTLGW_SETTLE_TIME 2
+
+static int debug = 0;
+
+enum hmuartlgw_state {
+ HMUARTLGW_QUERY_APPSTATE,
+ HMUARTLGW_ENTER_BOOTLOADER,
+ HMUARTLGW_ENTER_BOOTLOADER_ACK,
+ HMUARTLGW_BOOTLOADER,
+ HMUARTLGW_ENTER_APPLICATION,
+ HMUARTLGW_ENTER_APPLICATION_ACK,
+ HMUARTLGW_APPLICATION,
+};
+
+struct recv_data {
+ enum hmuartlgw_state state;
+};
+
+
+#define CRC16_POLY 0x8005
+
+static uint16_t crc16(uint8_t* buf, int length)
+{
+ uint16_t crc = 0xd77f;
+ int i;
+
+ while (length--) {
+ crc ^= *buf++ << 8;
+ for (i = 0; i < 8; i++) {
+ if (crc & 0x8000) {
+ crc <<= 1;
+ crc ^= CRC16_POLY;
+ } else {
+ crc <<= 1;
+ }
+ }
+ }
+
+ return crc;
+}
+
+static int hmuartlgw_init_parse(enum hmuartlgw_dst dst, uint8_t *buf, int buf_len, void *data)
+{
+ struct recv_data *rdata = data;
+
+#if 0
+ if (debug) {
+ printf("Length: %d\n", buf_len);
+ hexdump(buf, buf_len, "INIT > ");
+ }
+#endif
+
+ if (dst != HMUARTLGW_OS)
+ return 0;
+
+ if ((buf_len == 10) && (buf[0] == 0x00) && !strncmp(((char*)buf)+1, "Co_CPU_BL", 9)) {
+ rdata->state = HMUARTLGW_BOOTLOADER;
+ return 1;
+ }
+
+ if ((buf_len == 11) && (buf[0] == 0x00) && !strncmp(((char*)buf)+1, "Co_CPU_App", 10)) {
+ rdata->state = HMUARTLGW_APPLICATION;
+ return 1;
+ }
+
+ switch(rdata->state) {
+ case HMUARTLGW_QUERY_APPSTATE:
+ if ((buf[0] == 0x04) && (buf[1] == 0x02)) {
+ if (!strncmp(((char*)buf)+2, "Co_CPU_BL", 9)) {
+ rdata->state = HMUARTLGW_BOOTLOADER;
+ } else if (!strncmp(((char*)buf)+2, "Co_CPU_App", 10)) {
+ rdata->state = HMUARTLGW_APPLICATION;
+ }
+ }
+ break;
+ case HMUARTLGW_ENTER_BOOTLOADER:
+ if ((buf_len == 2) &&
+ (buf[0] == 0x04) &&
+ (buf[1] == 0x01)) {
+ rdata->state = HMUARTLGW_ENTER_BOOTLOADER_ACK;
+ }
+ break;
+ case HMUARTLGW_ENTER_BOOTLOADER_ACK:
+ rdata->state = HMUARTLGW_ENTER_BOOTLOADER;
+ break;
+ case HMUARTLGW_ENTER_APPLICATION:
+ if ((buf_len == 2) &&
+ (buf[0] == 0x04) &&
+ (buf[1] == 0x01)) {
+ rdata->state = HMUARTLGW_ENTER_APPLICATION_ACK;
+ }
+ break;
+ case HMUARTLGW_ENTER_APPLICATION_ACK:
+ rdata->state = HMUARTLGW_ENTER_APPLICATION;
+ break;
+ default:
+ return 0;
+ break;
+ }
+
+ return 1;
+}
+
+struct hmuartlgw_dev *hmuart_init(char *device, hmuartlgw_cb_fn cb, void *data)
+{
+ struct hmuartlgw_dev *dev = NULL;
+ struct termios oldtio, tio;
+
+ dev = malloc(sizeof(struct hmuartlgw_dev));
+ if (dev == NULL) {
+ perror("malloc(struct hmuartlgw_dev)");
+ return NULL;
+ }
+
+ memset(dev, 0, sizeof(struct hmuartlgw_dev));
+
+ dev->fd = open(device, O_RDWR | O_NOCTTY);
+ if (dev->fd < 0) {
+ perror("open(hmuartlgw)");
+ goto out;
+ }
+
+ if (debug) {
+ fprintf(stderr, "%s opened\n", device);
+ }
+
+ if (tcgetattr(dev->fd, &oldtio) == -1) {
+ perror("tcgetattr");
+ goto out2;
+ }
+
+ memset(&tio, 0, sizeof(tio));
+
+ tio.c_cflag = B115200 | CS8 | CLOCAL | CREAD;
+ tio.c_iflag = IGNPAR;
+ tio.c_oflag = 0;
+ tio.c_lflag = 0;
+ tio.c_cc[VTIME] = 0;
+ tio.c_cc[VMIN] = 1;
+
+ tcflush(dev->fd, TCIFLUSH);
+ if (tcsetattr(dev->fd, TCSANOW, &tio) == -1) {
+ perror("tcsetattr");
+ goto out2;
+ }
+
+ if (debug) {
+ fprintf(stderr, "serial parameters set\n");
+ }
+
+ hmuartlgw_flush(dev);
+
+ hmuartlgw_enter_app(dev);
+
+ dev->cb = cb;
+ dev->cb_data = data;
+
+ return dev;
+
+out2:
+ close(dev->fd);
+out:
+ free(dev);
+ return NULL;
+}
+
+struct hmuartlgw_dev *hmlgw_init(char *device, hmuartlgw_cb_fn cb, void *data)
+{
+ struct hmuartlgw_dev *dev = NULL;
+
+ return dev;
+}
+
+void hmuartlgw_enter_bootloader(struct hmuartlgw_dev *dev)
+{
+ hmuartlgw_cb_fn cb_old = dev->cb;
+ void *cb_data_old = dev->cb_data;
+ struct recv_data rdata = { 0 };
+ uint8_t buf[128] = { 0 };
+
+ if (debug) {
+ fprintf(stderr, "Entering bootloader\n");
+ }
+
+ buf[0] = HMUARTLGW_OS_CHANGE_APP;
+
+ dev->cb = hmuartlgw_init_parse;
+ dev->cb_data = &rdata;
+
+ rdata.state = HMUARTLGW_QUERY_APPSTATE;
+ buf[0] = HMUARTLGW_OS_GET_APP;
+ hmuartlgw_send(dev, buf, 1, HMUARTLGW_OS);
+ do {
+ hmuartlgw_poll(dev, HMUARTLGW_INIT_TIMEOUT);
+ } while (rdata.state == HMUARTLGW_QUERY_APPSTATE);
+
+ if (rdata.state != HMUARTLGW_BOOTLOADER) {
+ rdata.state = HMUARTLGW_ENTER_BOOTLOADER;
+ buf[0] = HMUARTLGW_OS_CHANGE_APP;
+ hmuartlgw_send(dev, buf, 1, HMUARTLGW_OS);
+ do {
+ hmuartlgw_poll(dev, HMUARTLGW_INIT_TIMEOUT);
+ } while (rdata.state != HMUARTLGW_BOOTLOADER);
+
+ printf("Waiting for bootloader to settle...\n");
+ sleep(HMUARTLGW_SETTLE_TIME);
+ }
+
+ dev->cb = cb_old;
+ dev->cb_data = cb_data_old;
+}
+
+void hmuartlgw_enter_app(struct hmuartlgw_dev *dev)
+{
+ hmuartlgw_cb_fn cb_old = dev->cb;
+ void *cb_data_old = dev->cb_data;
+ struct recv_data rdata = { 0 };
+ uint8_t buf[128] = { 0 };
+
+ if (debug) {
+ fprintf(stderr, "Entering application\n");
+ }
+
+ dev->cb = hmuartlgw_init_parse;
+ dev->cb_data = &rdata;
+
+ rdata.state = HMUARTLGW_QUERY_APPSTATE;
+ buf[0] = HMUARTLGW_OS_GET_APP;
+ hmuartlgw_send(dev, buf, 1, HMUARTLGW_OS);
+ do {
+ hmuartlgw_poll(dev, HMUARTLGW_INIT_TIMEOUT);
+ } while (rdata.state == HMUARTLGW_QUERY_APPSTATE);
+
+ if (rdata.state != HMUARTLGW_APPLICATION) {
+ rdata.state = HMUARTLGW_ENTER_APPLICATION;
+ buf[0] = HMUARTLGW_OS_CHANGE_APP;
+ hmuartlgw_send(dev, buf, 1, HMUARTLGW_OS);
+ do {
+ hmuartlgw_poll(dev, HMUARTLGW_INIT_TIMEOUT);
+ } while (rdata.state != HMUARTLGW_APPLICATION);
+
+ printf("Waiting for application to settle...\n");
+ sleep(HMUARTLGW_SETTLE_TIME);
+ }
+
+ dev->cb = cb_old;
+ dev->cb_data = cb_data_old;
+}
+
+static int hmuartlgw_escape(uint8_t *frame, int framelen)
+{
+ int i;
+
+ for (i = 1; i < framelen; i++) {
+ if (frame[i] == 0xfc || frame[i] == 0xfd) {
+ memmove(frame + i + 1, frame + i, framelen - i);
+ frame[i++] = 0xfc;
+ frame[i] &= 0x7f;
+ framelen++;
+ }
+ }
+ return framelen;
+}
+
+int hmuartlgw_send_raw(struct hmuartlgw_dev *dev, uint8_t *frame, int framelen)
+{
+ int w = 0;
+ int ret;
+
+ if (debug) {
+ hexdump(frame, framelen, "UARTLGW < ");
+ }
+
+ framelen = hmuartlgw_escape(frame, framelen);
+
+ do {
+ ret = write(dev->fd, frame + w, framelen - w);
+ if (ret < 0) {
+ perror("write");
+ return 0;
+ }
+ w += ret;
+ } while (w < framelen);
+
+ return 1;
+}
+
+int hmuartlgw_send(struct hmuartlgw_dev *dev, uint8_t *cmd, int cmdlen, enum hmuartlgw_dst dst)
+{
+ static uint8_t cnt = 0;
+ uint8_t frame[1024] = { 0 };
+ uint16_t crc;
+
+ frame[0] = 0xfd;
+ frame[1] = ((cmdlen + 2) >> 8) & 0xff;
+ frame[2] = (cmdlen + 2) & 0xff;
+ frame[3] = dst;
+ dev->last_send_cnt = cnt;
+ frame[4] = cnt++;
+ memcpy(&(frame[5]), cmd, cmdlen);
+ crc = crc16(frame, cmdlen + 5);
+ frame[cmdlen + 5] = (crc >> 8) & 0xff;
+ frame[cmdlen + 6] = crc & 0xff;
+
+ return hmuartlgw_send_raw(dev, frame, cmdlen + 7);
+}
+
+int hmuartlgw_poll(struct hmuartlgw_dev *dev, int timeout)
+{
+ struct pollfd pfds[1];
+ int ret;
+ int r = 0;
+ uint16_t crc;
+
+ errno = 0;
+
+ memset(pfds, 0, sizeof(struct pollfd) * 1);
+
+ pfds[0].fd = dev->fd;
+ pfds[0].events = POLLIN;
+
+ ret = poll(pfds, 1, timeout);
+ if (ret == -1)
+ return -1;
+
+ errno = 0;
+ if (ret == 0) {
+ errno = ETIMEDOUT;
+ return -1;
+ }
+
+ if (!(pfds[0].revents & POLLIN)) {
+ errno = EIO;
+ return -1;
+ }
+
+ r = read(dev->fd, dev->buf+dev->pos, 1);
+ if (r < 0)
+ return -1;
+
+ if (r == 0) {
+ errno = EOF;
+ return -1;
+ }
+
+ dev->pos += r;
+
+ if (dev->buf[0] != 0xfd) {
+ memset(dev->buf, 0, sizeof(dev->buf));
+ dev->pos = 0;
+ dev->unescape_next = 0;
+ return -1;
+ }
+
+ if (dev->unescape_next) {
+ dev->buf[dev->pos-1] |= 0x80;
+ dev->unescape_next = 0;
+ } else if (dev->buf[dev->pos-1] == 0xfc) {
+ dev->unescape_next = 1;
+ dev->pos--;
+ return -1;
+ }
+
+ if (dev->pos >= 3) {
+ uint16_t len;
+
+ len = ((dev->buf[1] << 8) & 0xff00) | (dev->buf[2] & 0xff);
+
+ if (dev->pos < len + 5)
+ return -1;
+ } else {
+ return -1;
+ }
+
+ crc = crc16(dev->buf, dev->pos - 2);
+ if ((((crc >> 8) & 0xff) == dev->buf[dev->pos - 2]) &&
+ ((crc & 0xff) == dev->buf[dev->pos - 1])) {
+
+ if (debug)
+ hexdump(dev->buf, dev->pos, "UARTLGW > ");
+
+ dev->cb(dev->buf[3], dev->buf + 5 , dev->pos - 7, dev->cb_data);
+
+ memset(dev->buf, 0, sizeof(dev->buf));
+ dev->pos = 0;
+ dev->unescape_next = 0;
+ } else {
+ fprintf(stderr, "Invalid checksum received!\n");
+ hexdump(dev->buf, dev->pos, "ERR> ");
+ printf("calculated: %04x\n", crc);
+
+ memset(dev->buf, 0, sizeof(dev->buf));
+ dev->pos = 0;
+ dev->unescape_next = 0;
+ }
+
+ errno = 0;
+ return -1;
+}
+
+void hmuartlgw_close(struct hmuartlgw_dev *dev)
+{
+ close(dev->fd);
+}
+
+void hmuartlgw_flush(struct hmuartlgw_dev *dev)
+{
+ struct pollfd pfds[1];
+ int ret;
+ int r = 0;
+ uint8_t buf[1024];
+
+ tcflush(dev->fd, TCIOFLUSH);
+
+ while(1) {
+ memset(pfds, 0, sizeof(struct pollfd) * 1);
+
+ pfds[0].fd = dev->fd;
+ pfds[0].events = POLLIN;
+
+ ret = poll(pfds, 1, 100);
+ if (ret <= 0)
+ break;
+
+ if (!(pfds[0].revents & POLLIN))
+ break;
+
+ memset(buf, 0, sizeof(buf));
+ r = read(dev->fd, buf, sizeof(buf));
+ if (r <= 0)
+ break;
+ }
+
+ return;
+}
+
+void hmuartlgw_set_debug(int d)
+{
+ debug = d;
+}
--- /dev/null
+/* HM-MOD-UART/HM-LGW-O-TW-W-EU driver
+ *
+ * Copyright (c) 2016 Michael Gernoth <michael@gernoth.net>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#define HMUARTLGW_OS_GET_APP 0x00
+#define HMUARTLGW_OS_GET_FIRMWARE 0x02
+#define HMUARTLGW_OS_CHANGE_APP 0x03
+#define HMUARTLGW_OS_ACK 0x04
+#define HMUARTLGW_OS_UNSOL_CREDITS 0x05
+#define HMUARTLGW_OS_NORMAL_MODE 0x06
+#define HMUARTLGW_OS_UPDATE_MODE 0x07
+#define HMUARTLGW_OS_GET_CREDITS 0x08
+#define HMUARTLGW_OS_GET_SERIAL 0x0B
+#define HMUARTLGW_OS_SET_TIME 0x0E
+
+#define HMUARTLGW_APP_SET_HMID 0x00
+#define HMUARTLGW_APP_GET_HMID 0x01
+#define HMUARTLGW_APP_SEND 0x02
+#define HMUARTLGW_APP_SET_CURRENT_KEY 0x03 /* key index */
+#define HMUARTLGW_APP_ACK 0x04
+#define HMUARTLGW_APP_RECV 0x05
+#define HMUARTLGW_APP_ADD_PEER 0x06
+#define HMUARTLGW_APP_REMOVE_PEER 0x07
+#define HMUARTLGW_APP_PEER_ADD_AES 0x09
+#define HMUARTLGW_APP_PEER_REMOVE_AES 0x0A
+#define HMUARTLGW_APP_SET_OLD_KEY 0x0F /* key index */
+#define HMUARTLGW_APP_DEFAULT_HMID 0x10
+
+enum hmuartlgw_dst {
+ HMUARTLGW_OS = 0,
+ HMUARTLGW_APP = 1,
+};
+
+typedef int (*hmuartlgw_cb_fn)(enum hmuartlgw_dst dst, uint8_t *buf, int buf_len, void *data);
+
+struct hmuartlgw_dev {
+ int fd;
+ hmuartlgw_cb_fn cb;
+ void *cb_data;
+ uint8_t last_send_cnt;
+ uint8_t buf[1024];
+ int pos;
+ int unescape_next;
+};
+
+struct hmuartlgw_dev *hmuart_init(char *device, hmuartlgw_cb_fn cb, void *data);
+struct hmuartlgw_dev *hmlgw_init(char *device, hmuartlgw_cb_fn cb, void *data);
+int hmuartlgw_send_raw(struct hmuartlgw_dev *dev, uint8_t *frame, int framelen);
+int hmuartlgw_send(struct hmuartlgw_dev *dev, uint8_t *cmd, int cmdlen, enum hmuartlgw_dst dst);
+int hmuartlgw_poll(struct hmuartlgw_dev *dev, int timeout);
+void hmuartlgw_close(struct hmuartlgw_dev *dev);
+void hmuartlgw_flush(struct hmuartlgw_dev *dev);
+void hmuartlgw_enter_bootloader(struct hmuartlgw_dev *dev);
+void hmuartlgw_enter_app(struct hmuartlgw_dev *dev);
+void hmuartlgw_set_debug(int d);
--- /dev/null
+#!/bin/bash
+
+if [ "`id -u`" != "0" ]; then
+ exec sudo "${0}"
+fi
+
+if [ ! -d "/sys/class/gpio/gpio18" ]; then
+ echo 18 >/sys/class/gpio/export
+fi
+
+echo out >/sys/class/gpio/gpio18/direction
+echo 0 >/sys/class/gpio/gpio18/value
+sleep 0.2
+echo 1 >/sys/class/gpio/gpio18/value
+sleep 2