Skip to content

Instantly share code, notes, and snippets.

@lafka
Last active June 12, 2019 05:13
Show Gist options
  • Save lafka/511d162a4a716524df6ef8d28a5e9c50 to your computer and use it in GitHub Desktop.
Save lafka/511d162a4a716524df6ef8d28a5e9c50 to your computer and use it in GitHub Desktop.
, tinysix_g_addr#include "path.h"
#include <sqlite3.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <unistd.h>
// extern unsigned char send_associate(void);
extern unsigned char strResp[1000];
extern int frm_len;
extern int Interrupt_Count;
extern unsigned char Modem_Response[1000];
extern unsigned int send_req(unsigned char[],unsigned int);
extern unsigned char addr[8], root_mac_addr[8];
extern unsigned int tinysix_g_acls_count;
extern uint8_t tinysix_g_acls[50][4];
extern uint8_t tinysix_g_commission_panid[2];
extern uint8_t tinysix_g_commission_channel;
extern uint8_t tinysix_g_commission_type;
extern uint8_t tinysix_g_commission_prefix[8];
extern uint8_t tinysix_g_commission_llsec[16];
int tinysix_g_addr_llocal = 0;
int hex_decode(uint8_t *out, size_t size, const char *in, uint8_t **ptr) {
int pos = size - 1;
int insize = strlen(in);
int first = 0 == insize % 2;
/**
* Loop in reverse to easily fix padding.
*
* The process is like this:
* - First lowercase input by OR'ing with ' ' (a single space) - bit 5
* - check if it's a character or number by AND'ing with '@' - bit 6
* - If `first` is initially 1 we're processing the right most nibble
* - If we encounter a separator character we flip `first` thus making
* the next nibble the first (remember we're using modulo to "guess"
* which nibble we're working on).
*/
for (int i = strlen(in) - 1; i >= 0; i--) {
if (' ' == in[i] || '-' == in[i] || ':' == in[i]) {
/**
* if we've only processed the right most nibble we don't need to
* flip `first` since the next nibble will have same evenness
*/
if (first != i % 2) {
pos--;
continue;
}
first = !first;
continue;
}
//if ( ! (('0' > in[pos] && in[pos] < '0') || ('a >= in[pos] && in[pos] <= 'f'))) {
// return -1;
//}
out[pos] |= ((in[i] | ' ') - ('@' & in[i] ? 0x57 : 0x30)) << (first == i % 2 ? 0 : 4);
if (first != i % 2) {
pos--;
}
}
// If the last processed character was first nibble processed `pos` has not
// been decremented yet
if (NULL != ptr) {
*ptr = &out[pos + (first ? 1 : 0)];
}
return size - pos - (first ? 1 : 0);
}
int retry(unsigned int count, unsigned int delay, int (*fn)(void *), void *ptr) {
int status;
for (int i = 0; i < count; i++) {
printf("retry %d/%d for %p\n", i, count, fn);
status = fn(ptr);
printf(" -> returned %x\n", status);
if (1 == status) {
break;
}
usleep(delay * 1000);
}
return status;
}
/**
* Send an associate request for a specific panid
*
* Takes one uint16_t argument `panid`, sends ASSOCIATE
* and waits 500ms before checking the response.
*
* Returns 1 if OK, 0 if failed
*/
int tinysix_associate(void *panid) {
uint8_t buf[] = {0x2b, 0x07, 0x02, 0x00, 0x02, 0x03, 0x00, 0x00};
memcpy(&buf[6], panid, 2);
frm_len = 0;
Interrupt_Count = 0;
memset(Modem_Response, '\0', sizeof(Modem_Response));
memset(strResp, '\0', sizeof(strResp));
send_req(buf, sizeof(buf));
return 5 <= frm_len && 0 == strResp[5];
}
/**
* Send CONNSTATUS request, wait for 500 ms and check resposne
*
* Returns 1 if connected, 0 otherwise
*/
int tinysix_connstatus(void *none) {
uint8_t buf[] = {0x2b, 0x05, 0x02, 0x00, 0x73, 0x63};
frm_len = 0;
Interrupt_Count = 0;
memset(Modem_Response, '\0', sizeof(Modem_Response));
memset(strResp, '\0', sizeof(strResp));
send_req(buf, sizeof(buf));
return 6 <= frm_len && 0 == strResp[5] && 0x01 == strResp[6];
}
/**
* Send a decommissioning packet to `mac`. Optionally take a second
* parameter `tinysix_g_addr_llocal` in which case the packet will be transmitted
* using link-local prefix.
*
* Uses send req, the response will be put in strResp.
* If no reply returns 0 other wise 1 on success (reply received).
*
* Note that this does not validate that the decommissioning was succesfull
* it only checks if there was any bytes received after sending the decommision
* packet
*/
int tinysix_decommission(void *none) {
uint8_t buf[] = {
0x2b, 0x2f, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x05, 0x00,
0xff, 0xff, 0x01, 0x02, 0x02, 0x02, 0x03, 0xff,
0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x4d, 0x53, 0x45, 0x44, 0x43, 0x4c};
int size;
if (tinysix_g_addr_llocal) {
buf[2] = 0x45;
}
memcpy(&buf[3], tinysix_g_addr, 8);
printf("De-Commission Query[%d] ", (int) sizeof(buf));
for (int i = 0; i < sizeof(buf); i++) {
printf("%02x ", buf[i]);
}
printf("\n");
frm_len=0;
Interrupt_Count = 0;
memset(Modem_Response,'\0',sizeof(Modem_Response));
memset(strResp,'\0',sizeof(strResp));
size = send_req(buf, sizeof(buf));
// @todo 2019-06-11; check that the decommissioning actually
// returned a status packet and that it was successfull
//strResp[size] = 0x00;
return size > 0;
}
/**
* Send a commissioning packet to `mac`
*
* - panid should be set in global `tinysix_g_commission_panid`
* - channel should be set in global `tinysix_g_commission_channel`
* - type should be set in global `tinysix_g_commission_type`
* - prefix should be set in global `tinysix_g_commission_prefix`
* - llsec should be set in global `tinysix_g_commission_llsec`
*/
int tinysix_commission(void *none) {
uint8_t buf[] = {
0x2b, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x05, 0x00,
0xff, 0xff, 0x01, 0x02, 0x02, 0x02, 0x03, 0xff,
0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x4d, 0x53, 0x45, 0x44, 0x43, 0x4c};
int size;
if (tinysix_g_addr_llocal) {
buf[2] = 0x45;
}
memcpy(&buf[3], tinysix_g_addr, 8);
uint8_t *attr_panid = &buf[16];
uint8_t *attr_channel = &buf[19];
uint8_t *attr_type = &buf[21];
uint8_t *attr_prefix = &buf[23];
uint8_t *attr_llsec = &buf[32];
memcpy(attr_panid, tinysix_g_commission_panid, sizeof(tinysix_g_commission_panid));
*attr_channel = tinysix_g_commission_channel;
*attr_type = tinysix_g_commission_type;
memcpy(attr_prefix, tinysix_g_commission_prefix, sizeof(tinysix_g_commission_prefix));
memcpy(attr_llsec, tinysix_g_commission_llsec, sizeof(tinysix_g_commission_llsec));
if (tinysix_g_addr_llocal) {
buf[2] = 0x45;
}
memcpy(&buf[3], tinysix_g_addr, 8);
size = send_req(buf, sizeof(buf));
return size > 0;
}
/**
* Add or remove ACLs from meter at address `mac`
*
* Argument `llocal` control if link-local is used
* Argument `op` MUST be either 0 for ADD or 1 for REM
* Argument `count` defines how many ACLs are in `acls`
* Argument `acls` a pointer to the buffer containing ACLs
*/
static int _change_acls(uint8_t mac[8], int llocal, uint8_t op, int count, uint8_t (*acls)[4]) {
// byte number 16, 17 is Add/Remove (0/1) and ACL count
uint8_t buf[512] = {
0x2b, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x1a,
0x01, 0x00};
int size, len = 18;
if (llocal) {
buf[2] = 0x45;
}
memcpy(&buf[3], mac, 8);
buf[16] = op;
buf[17] = count;
memcpy(&buf[18], acls, 4 * count);
len += 4 * count;
buf[1] = len - 1;
printf("%s-ACL Query[%d] ", op == 1 ? "REM" : "ADD", len);
for (int i = 0; i < len; i++) {
printf("%02x ", buf[i]);
}
printf("\n");
frm_len = 0;
Interrupt_Count = 0;
memset(Modem_Response, '\0', sizeof(Modem_Response));
memset(strResp, '\0', sizeof(strResp));
size = send_req(buf, len);
// @todo 2019-06-11; check that the decommissioning actually
// returned a status packet and that it was successfull
//strResp[size] = 0x00;
return size > 0;
}
/**
* Send REM-ACL command to device specified in the global `addr`
*
* Expects `tinysix_g_acls_count` and `tinysix_g_acls` is globally set
* the first being the number of ACLs the second being a buffer wiht space
* for atleast 4 * `tinysix_g_acls_count`.
*
* Additionally the global flag `tinysix_g_addr_llocal`
*/
int tinysix_rem_acls(void *none) {
return _change_acls(addr, tinysix_g_addr_llocal, 0x01, tinysix_g_acls_count, tinysix_g_acls);
}
/**
* Send ADD-ACL command to device specified in the global `addr`
*
* Expects `tinysix_g_acls_count` and `tinysix_g_acls` is globally set
* the first being the number of ACLs the second being a buffer wiht space
* for atleast 4 * `tinysix_g_acls_count`.
*
* Additionally the global flag `tinysix_g_addr_llocal`
*/
int tinysix_add_acls(void *none) {
return _change_acls(addr, tinysix_g_addr_llocal, 0x00, tinysix_g_acls_count, tinysix_g_acls);
}
/**
* Retrieve the commissionstatus and MAC for a meter
*
* Takes 4 arguments;
* - serialnum, the serialnumber of the device
* - makecode, an optional makecode or NULL if unspecified
* - commstatus, ptr to where value from database will be set. May be NULL
* - mac, optional pointer where value from database will be set. May be NULL
*/
int get_meter_commissionstatus(char *serialnum, char *makecode, char *commstatus, uint8_t mac[8]) {
sqlite3 *db;
sqlite3_stmt *stmt;
int rows = 0, status;
char buf[32];
const char *query = "SELECT macaddress, commissionstatus FROM rfmstr WHERE meterserialnumber = ?1 AND makecode = ?2";
const char *query_nomake = "SELECT macaddress, commissionstatus FROM rfmstr WHERE meterserialnumber = ?1";
sqlite3_open("/home/MRF.db", &db);
if (NULL != makecode) {
sqlite3_prepare_v2(db, query, -1, &stmt, 0);
} else {
sqlite3_prepare_v2(db, query_nomake, -1, &stmt, 0);
}
fprintf(stderr, "query: %s\n", sqlite3_expanded_sql(stmt));
if (NULL != mac) {
strncpy(buf, sqlite3_column_text(stmt, 1), sizeof(buf));
hex_decode(mac, 8, buf, NULL);
}
sqlite3_bind_text(stmt, 2, commstatus, -1, NULL);
while (SQLITE_ROW == (status = sqlite3_step(stmt))) {
strncpy(buf, sqlite3_column_text(stmt, 1), sizeof(buf));
strncpy(buf, sqlite3_column_text(stmt, 2), sizeof(buf));
rows++;
}
if (SQLITE_DONE != status) {
fprintf(stderr, "some error in database: %s\n", sqlite3_errmsg(db));
}
sqlite3_finalize(stmt);
sqlite3_close(db);
if (rows > 1) {
return -1;
} else {
return 0 != rows;
}
}
/**
* Retrieve relevant information about a network for a specific serialnumber
*
* If a network was found the MAC and root will be set in `addr` and `raddr`
* respectively. The PAN of the network will be set in `panid`.
*
* If no network was found, or the meter does not have valid `networkid` set,
* this function will return 0 and none of the arguments will be changed.
* */
int get_meter_networking(char *serialnum, uint8_t addr[8], uint8_t raddr[8], uint16_t *panid) {
sqlite3 *db;
sqlite3_stmt *stmt;
int status;
char buf[32];
const char *query = "\
SELECT meter.macaddress, root.macaddress, panid \n\
FROM rfmstr meter, rfmstr root, network6 net \n\
WHERE meter.meterserialnumber = '100002' \n\
AND meter.networkid = net.networkid \n\
AND root.meterserialnumber = net.rserialnumber \n\
AND root.makecode = net.rmakecode";
sqlite3_open("/home/MRF.db", &db);
sqlite3_prepare_v2(db, query, -1, &stmt, 0);
sqlite3_bind_text(stmt, 1, serialnum, -1, NULL);
printf("query: %s\n", sqlite3_expanded_sql(stmt));
int rc = sqlite3_step(stmt);
if (SQLITE_DONE == rc) {
// nothing found
printf("No networking found for meter %s, device is probably uncommissioned", serialnum);
status = 0;
} else if (SQLITE_ROW == rc) {
// parse hex: meter mac address
strncpy(buf, sqlite3_column_text(stmt, 0), sizeof(buf));
hex_decode(addr, 8, buf, NULL);
// parse hex: root mac address
strncpy(buf, sqlite3_column_text(stmt, 1), sizeof(buf));
hex_decode(raddr, 8, buf, NULL);
// decode panid to big endian
strncpy(buf, sqlite3_column_text(stmt, 2), sizeof(buf));
hex_decode((uint8_t *) panid, 2, buf, NULL);
status = 1;
}
sqlite3_finalize(stmt);
sqlite3_close(db);
return status;
}
int main() {
printf("hello %p\n", tinysix_associate);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment