All pastes #956033 Raw Edit

Untitled

public c v1 · immutable
#956033 ·published 2008-03-25 00:06 UTC
rendered paste body
/* * This is a driver for the Insteon USB powerline controller V2. * * "Insteon" is a registered trademark. It is appropriate to use "Insteon" * to refer to products that are licensed to use that brand by SmartLabs, * such as the Insteon devices sold by Smarthome. You should use another * name to refer to Open Source software. My own software for driving * Insteon devices is called "Ion". * * Written by Bruce Perens <bruce@perens.com>. * Copyright (C) 2006 Sourcelabs. * * This is Free Software under the GNU General Public License version * 2.1 or any later version. If you wish another license, please write * to the author. * * Sourcelabs pays me to spend half of my work time on Open Source * projects of my choice. Thus, they sponsored this development. *  * Currently, this software writes the ROM version command and dumps the * result, then monitors the PLC output until interrupted. *  * Obviously, I have more plans for it. - Bruce */#include <hid.h>#include <stdlib.h>#include <stdio.h>#include <unistd.h>#include <string.h>#define ION_READ_BUFFER_SIZE    8#define ION_WRITE_BUFFER_SIZE   1024/* IBIOS Commands */#define IBIOS_DOWNLOAD			0x40#define IBIOS_FL_MESSAGE		0x41#define IBIOS_UPLOAD			0x42#define IBIOS_VL_MESSAGE		0x43#define IBIOS_GET_CHECKSUM		0x44#define IBIOS_EVENT_REPORT		0x45#define IBIOS_MASK			0x46#define IBIOS_SIMULATED_EVENT		0x47#define IBIOS_GET_VERSION		0x48#define IBIOS_DEBUG_REPORT		0x49#define IBIOS_X10_BYTE_RECEIVED		0x4A#define IBIOS_INSTEON_RECEIVED		0x4Fstruct _IonPLC {  char *                r_head;  char *                r_tail;  char *                w_head;  char *                w_tail;  HIDInterface *        hid;  unsigned int          got_ack:1;  char                  r_data[ION_READ_BUFFER_SIZE];  char                  w_data[ION_WRITE_BUFFER_SIZE];};typedef struct _IonPLC *        IonPLC;/* * ReceiveCount is a table of the length of fixed-length packets. * For variable-length packets, it specifies the length of the header data. * * Labels for PLC packet bytes: * * A2   Address (high byte, then low byte). * ACK  Acknowledge (0x06) or Negative Acknowledge (0x15). * AND  AND mask. NOT Bits to clear. * C2   Checksum (high byte, then low byte). * D2   Device type (high byte, then low byte). * EN   Event number. * FR   Firmware revision number. * I3   Insteon address (three bytes, high first). * L    Length (one byte). * L2   Length (high byte, then low byte). * MF	Message flags. * OR   OR mask. Bits to set. * TV   Timer value. * XT   X10 type: 0 for address, 1 for data. * dN   Data bytes. N indicates how many. */static const int ReceiveCount[] = {        5,      /* 0x40 Write to IBIOS: A2 L2 ACK */        1,      /* 0x41 IBIOS message: L dL. */        7,      /* 0x42 Read from IBIOS: A2 L2 C2 ACK */        0,      /* 0x43 ETX-terminated IBIOS message: d(variable) ETX (3) */        7,      /* 0x44 Get Checksim: A2 L2 C2 ACK */        1,      /* 0x45 Event report: EN */        5,      /* 0x46 Mask (bit-manipulation): A2 OR AND ACK */        3,      /* 0x47 Simiulated event: EN TV ACK */        7,      /* 0x48 Get version: I3 D2 FR */        2,      /* 0x49 Debug reporting next instruction to execute: A2 */        2,      /* 0x4a X10 recieved: XT d1 */        -1,     /* 0x4b */        -1,     /* 0x4c */        -1,     /* 0x4d */        -1,     /* 0x4e */        10,     /* 0x4f Insteon packet: EN I3(source) I3(dest) MF d2 [d14] ACK*/};static const char * const	EventExplanations[] = {	"SALad initialized",	/* 0 */	"Received the first message in a hop sequence addressed to me",	/* 1 */	"Received the first message in a hop sequence not addressed to me",/*2*/	"Received a duplicate message",	/* 3 */	"Recieved an ACK to my direct message",	/* 4 */	"Did not get an ACK to my direct message, even after 5 retries",/* 5 */ 	"Received a message to an unknown address, and censored it",	/* 6 */	"Received a response to my join-me message",			/* 7 */	"Received an X10 byte",						/* 8 */	"Received an X10 extended-message byte",			/* 9 */	"A SET button tap sequence has begun",			/* a */	"The SET button is being pressed",				/* b */	"The SET button has been released",				/* c */	"The tick counter has expired",					/* d */	"An alarm tripped",						/* e */	"The dedicated midnight alarm tripped",				/* f */	"The dedicated 2:00 AM alarm tripped",				/* 10 */	"Received a serial byte for SALad processing",			/* 11 */	"Received an unknown IBIOS serial command",			/* 12 */	"Received an interrupt from my daughter card",			/* 13 */	"The load was turned on",					/* 14 */	"The load was turned off",					/* 15 */};static intfill(IonPLC b){  hid_return    r;  r = hid_interrupt_read(b->hid, USB_ENDPOINT_IN+1, (char *)b->r_data, ION_READ_BUFFER_SIZE, 100);  if ( r == HID_RET_SUCCESS ) {    unsigned char       length = b->r_data[0] & 0x0f;    if ( b->r_data[0] & 0x80 ) {      printf("Got CTS\n");    }    if ( length > 7 ) {      fprintf(stderr, "Invalid PLC message length %d.\n", length);      return 0;    }    b->r_tail = (b->r_head = b->r_data + 1) + length;    return (int)length;  }  else    return 0;}inline static intgetByte(IonPLC b){  int   c;  if ( b->r_head == b->r_tail && fill(b) == 0 )    return -1;  c = *(b->r_head)++ & 0xff;  return c;}voidIonPLCClose(IonPLC b){  hid_close(b->hid);  hid_delete_HIDInterface(&(b->hid));  hid_cleanup();  free(b);}inline static intgetBytes(IonPLC b, unsigned char * r_data, int length, int * copied, int real_length){  int   i;  int   c;  int   cp = *copied;  for ( i = 0; i < real_length; i++ ) {    if ( (c = getByte(b)) < 0 )      return 0;    if ( cp < length )      r_data[cp++] = c & 0xff;  }  *copied = cp;  return cp;}intIonPLCRead(IonPLC b, unsigned char * data, int length){    int c;    int copied = 0;    while ( (c = getByte(b)) >= 0 && c != 2 ) {      fprintf(stderr, "Discarding unsynchronized byte %02x\n", c);    }    if ( c < 0 )      return 0;    c = getByte(b);    if ( c >= 0x40 && c <= 0x4f ) {      int   get = ReceiveCount[c & 0x0f];      if ( copied < length )        data[0] = c & 0xff;      if ( get > 0 )        getBytes(b, &(data[1]), length - copied, &copied, get);      else {        fprintf(stderr, "Don't know what to do with command %x\n", c);        return copied;      }      switch ( c ) {      case 0x41:        get = data[1] & 0xff;        break;      case 0x42:        get = ((data[3] & 0xff) << 8) | (data[4] & 0xff);        break;      case 0x4f:        if ( data[8] & 0x10 ) {	/* Extended message */          fprintf(stderr, "Got extended message.\n");          get = 14;          break;        }        /* Fall through */      default:	  return copied;      }      getBytes(b, &data[copied], length - copied, &copied, get);          }    else if ( c >= 0 ) {      fprintf(stderr, "Don't know what to do with received command %02x.\n",       c);    }    return 0;}IonPLCIonPLCOpenUSB(){  static const HIDInterfaceMatcher matcher = { 0x10bf, 0x4, NULL, NULL, 0 };  hid_return    r;  IonPLC        b = malloc(sizeof(struct _IonPLC));  if ( b == 0 || hid_init() != HID_RET_SUCCESS   || (b->hid = hid_new_HIDInterface()) == 0 ) {    fprintf(stderr, "Failed to initialize libhid.\n");    return 0;  }  b->r_head = b->r_tail = b->r_data;  b->w_head = b->w_tail = b->w_data;  if ( (r = hid_force_open(b->hid, 0, &matcher, 3)) != HID_RET_SUCCESS ) {    fprintf(stderr, "hid_force_open failed with return code %d\n", r);    return 0;  }  return b;}int main(void){  IonPLC        b = IonPLCOpenUSB();  hid_return    r;  unsigned char msg[] = { 0x02, 0x02, 0x48 };   /* Get ROM version */  char          empty = 0;  int           written = 0;  if ( b == 0 )    return 1;    /*   * For some reason, the first write just disappears.   * I don't know if this is a libhid or PLC issue.   * So, I just do an empty write here.   */  hid_interrupt_write(b->hid, USB_ENDPOINT_OUT+1, &empty, 0, 100);  for ( ; ; ) {    unsigned char       p[1024];    int                 length;    unsigned char	event;    if ( (length = IonPLCRead(b, p, sizeof(p))) > 0 ) {      switch (p[0]) {      case IBIOS_EVENT_REPORT:        event = p[1];        printf("Event %x", event);        if ( event < (sizeof(EventExplanations)/sizeof(*EventExplanations)) )          printf(": %s", EventExplanations[event]);        printf("\n");        break;      case IBIOS_GET_VERSION:        printf("PLC address %x.%x.%x, Decice type %x subtype %x, Firmware version %x.\n"        ,p[1], p[2] ,p[3] ,p[4] ,p[5] ,p[6] ,p[7]);        break;      case IBIOS_INSTEON_RECEIVED:        printf("Insteon Message: Event %x, %x.%x.%x %x.%x.%x %x %x %x.\n"        ,p[1] ,p[2] ,p[3] ,p[4] ,p[5] ,p[6] ,p[7] ,p[8] ,p[9] ,p[10]);        break;      default:        printf("Got command %x.\n", p[0]);      }      fflush(stdout);    }    if ( written < 1 ) {      r = hid_interrupt_write(b->hid, USB_ENDPOINT_OUT+1, (char *)msg, sizeof(msg), 100);      if ( r != 0 )        fprintf(stderr, "Write returned %d.\n", r);      written++;    }  }  IonPLCClose(b);  return 0;}