Add additional plugin code.
Change-Id: I99d4fae11900055782b7832e99a8267b20144393
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..3f3ea2b
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,50 @@
+# Copyright 2011 Google Inc. All Rights Reserved.
+# Author: qianzhang@google.com (ken Zhang)
+#MAKEFILE for cross platform
+
+DEBUG=-g
+ifdef DEBUG
+ DEBUG_CFLAGS= -g $(DEBUG)
+else
+ DEBUG_CFLAGS= -O3
+endif
+
+ifdef TARGET
+ CROSS_PREFIX:=$(TARGET)
+else
+ CROSS_PREFIX:=
+endif
+
+CC:=$(CROSS_PREFIX)gcc
+LD:=$(CROSS_PREFIX)ld
+AR:=$(CROSS_PREFIX)ar
+RANLIB:=$(CROSS_PREFIX)ranlib
+STRIP:=$(CROSS_PREFIX)strip
+
+CFLAGS= -Wall -fPIC -O2 -Wpointer-arith
+
+FILES=sagelog.c hdhomerun_tuner.c hdhomerun_http.c hdhomerun_dev.c utility.c hdhomerun_plugin.c
+INC= sagelog.h hdhomerun_tuner.h hdhomerun_http.h hdhomerun_dev.h utility.h
+EXTRA_INCS= ../tvstreamparser
+
+#all:hdhomerun
+all:hdhr_plugin.so
+dep_make:hdrun_dep
+
+hdhr_plugin.so:dep_make $(FILES) $(INC)
+ $(CC) $(CFLAGS) $(DEBUG_CFLAGS) $(FILES) -shared -o hdhr_plugin.so -I$(EXTRA_INCS) -L. -lhdhomerun -lpthread
+
+hdhomerun:dep_make $(FILES) $(INC)
+ $(CC) $(CFLAGS) $(DEBUG_CFLAGS) $(FILES) -DTEST_APP -o hdhomerun -I$(EXTRA_INCS) -L. -lhdhomerun -lpthread
+
+hdrun_dep:
+ make -C libhdhomerun
+ cp libhdhomerun/libhdhomerun.so .
+
+clean:
+ rm -f $(ALL) hdrun libhdhomerun.so hdhr_plugin.so get_http *~
+ make clean -C libhdhomerun
+
+get_http:hdhomerun_http.c hdhomerun_http.h
+ $(CC) $(CFLAGS) $(DEBUG_CFLAGS) -DHTTP_GET_APP hdhomerun_http.c -o get_http
+
diff --git a/channel_buffer.c b/channel_buffer.c
new file mode 100644
index 0000000..85c82bc
--- /dev/null
+++ b/channel_buffer.c
@@ -0,0 +1,458 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+// Author: qianzhang@google.com (Qiann Zhang)
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <netdb.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <assert.h>
+#include <signal.h>
+#include <sys/errno.h>
+#include <features.h>
+#include <sys/time.h>
+#include <math.h>
+#include <time.h>
+#include <sys/timeb.h>
+#include <sys/un.h>
+#include <poll.h>
+#include <pthread.h>
+#include <dirent.h>
+#include <dlfcn.h>
+#include <stdarg.h>
+#include <inttypes.h>
+#include "channel_buffer.h"
+
+#define sage_log(x) _sagelog x
+void _sagelog( int type, int level, const char* cstr, ... );
+
+static const char* delimiter = " \t,;\r\n:=|'\"#";
+
+static int is_delimiter( char ch )
+{
+ int i;
+ if ( ch == 0 ) return 1;
+ for ( i = 0; delimiter[i]; i++ )
+ if ( ch == delimiter[i] ) return 1;
+ return 0;
+}
+
+//format name:zzz (or name=zzzz)
+int get_string( char*p, const char* name, char* string, int max_len, char **next )
+{
+ char *s, *e;
+ e = p;
+
+ while ( e != NULL && *e ) {
+ s = strstr( e, name );
+ if ( s == NULL )
+ return 0;
+ //is a token name
+ if ( ((s > p && is_delimiter(*(s-1))) || s == p ) && is_delimiter( *(s+strlen(name)) ) ) {
+ s += (int)strlen(name);
+ while( *s && ( *s == ' ' || *s == '\t' ) )
+ s++; //skip white space
+ if ( *s == '=' || *s == ':' ) {
+ s++;
+ while( *s && ( *s == ' ' || *s == '\t' ) )
+ s++; //skip white space
+ if ( *s ) {
+ int i=0;
+ while ( i++<max_len && *s && *s != ' ' && *s != '\t' && *s != '\n' && *s != '\r')
+ *string++ = *s++;
+
+ *string = 0x0; //terminator
+ if ( next != NULL ) {
+ while ( *s && *s != ' ' && *s != '\t' && *s != '\n' && *s != '\r')
+ s++;
+ *next = s;
+ }
+ return 1;
+ }
+ }
+ }
+ e = ++s;
+ }
+ return 0;
+}
+
+//format name:"zzz" (or name="zzzz")
+int get_string_with_quote( char*p, const char* name, char* string, int max_len, char **next )
+{
+ char *s, *e;
+ e = p;
+
+ while ( e != NULL && *e ) {
+ s = strstr( e, name );
+ if ( s == NULL ) return 0;
+ //is a token name
+ if ( ((s > p && is_delimiter(*(s-1))) || s == p ) && is_delimiter( *(s+strlen(name)) ) ) {
+ s += (int)strlen(name);
+ while( *s && ( *s == ' ' || *s == '\t' ) )
+ s++; //skip white space
+ if ( *s == '=' || *s == ':' ) {
+ s++;
+ while( *s && ( *s == ' ' || *s == '\t' ) )
+ s++; //skip white space
+ if ( *s == '\"' ) {
+ int i=0;
+ s++;
+ while ( i++<max_len && *s && *s != '\"' )
+ *string++ = *s++;
+
+ *string = 0x0; //terminator
+
+ if ( next != NULL) {
+ while ( *s && *s != '\"' )
+ s++;
+ *next = s+1;
+ }
+ return 1;
+ } else
+ return 0;
+ }
+ }
+ e = ++s;
+ }
+ return 0;
+}
+
+int get_string_token( char*p, int num, char* string, int max_len, char **next )
+{
+ int count = 0;
+ char *s;
+ s = p;
+ if ( s == NULL )
+ return 0;
+ while ( *s ) {
+ while( *s && ( *s == ' ' || *s == '\t' || *s == '\r' || *s == '\n' ) )
+ s++; //skip white space
+ if ( *s == 0 )
+ break;
+
+ if ( count++ != num ) {
+ while( *s && !( *s == ' ' || *s == '\t' || *s == ';' || *s == ',' || *s == '\r' || *s == '\n' ) )
+ s++; //skip a token
+ } else {
+ if ( *s ) {
+ int i=0;
+ while ( i++<max_len && *s && !( *s == ' ' || *s == '\t' || *s == ';' || *s == ',' || *s == '\r' || *s == '\n' ) )
+ *string++ = *s++;
+
+ *string = 0x0; //terminator
+ if ( next != NULL ) {
+ while ( *s && !( *s == ' ' || *s == '\t' || *s == '\r' || *s == '\n' ) )
+ s++;
+ *next = s;
+ }
+ return 1;
+ }
+ }
+ if ( *s )
+ s++;
+ }
+ return 0;
+}
+
+int get_int_val( char*p, const char* name, int* val, char** next )
+{
+ char *s, *e;
+ e = p;
+ while( e != NULL && *e ) {
+ s = strstr( e, name );
+ if ( s == NULL )
+ return 0;
+ //is a token name
+ if ( ( (s > p && is_delimiter(*(s-1)) ) || s == p ) && is_delimiter( *(s+strlen(name)) ) ) {
+ s += strlen(name);
+ while( *s && ( *s == ' ' || *s == '\t' ) )
+ s++; //skip white space
+ if ( *s == '=' || *s == ':' ) {
+ s++;
+ while( *s && ( *s == ' ' || *s == '\t' ) )
+ s++; //skip white space
+ if ( *s ) {
+ *val = atoi( s );
+ if (*val < 0 && *s == '-' )
+ s++;
+ while( *s >= '0' && *s <= '9')
+ s++;
+ if ( next != NULL )
+ *next = s;
+ return 1;
+ }
+ }
+ }
+ e = ++s;
+ }
+ return 0;
+}
+
+//format: name val; ext
+int get_cfg_val( char* string, char* name, int max_name_lenn, int* Val, char* ext, int max_ext_len )
+{
+ const char* delima = " \t,;\r\n";
+ char *token, *saved_ptr;
+ if ( name == NULL || ext == NULL || max_name_lenn == 0 || max_ext_len == 0 )
+ return 0;
+
+ name[0] = 0;
+ *Val = 0;
+ ext[0] = 0;;
+ token = (char*)strtok_r( string, delima, &saved_ptr );
+ if ( token != NULL )
+ strncpy( name, token, max_name_lenn );
+ token = (char*)strtok_r( saved_ptr, delima, &saved_ptr );
+ if ( token != NULL ) {
+ *Val = atoi( token); strncpy( ext, token, max_ext_len ); }
+ token = (char*)strtok_r( saved_ptr, delima, &saved_ptr );
+ if ( token != NULL )
+ strncpy( ext, token, max_ext_len );
+ return 1;
+}
+
+int make_channel_entry_buffer( struct channel_entry_t *ce, char *buf, int size )
+{
+ int pos = 0;
+ pos += snprintf( buf+pos, size-pos, "ch:%03d state:%d ", ce->ch, ce->state );
+ if ( ce->ctrl_bits )
+ pos += snprintf( buf+pos, size-pos, "ctrl:%d ", ce->ctrl_bits );
+ if ( ce->ch_name[0] )
+ pos += snprintf( buf+pos, size-pos, "name:%s ", ce->ch_name );
+ if ( ce->src_ip[0] )
+ pos += snprintf( buf+pos, size-pos, "ip:%s port:%d ", ce->src_ip, ce->src_port);
+ if ( ce->epg_id[0] )
+ pos += snprintf( buf+pos, size-pos, "epg:%s ", ce->epg_id );
+ if ( ce->dev_name[0] )
+ pos += snprintf( buf+pos, size-pos, "dev:%s ", ce->dev_name );
+ if ( ce->dev_tuning[0] )
+ pos += snprintf( buf+pos, size-pos, "tune:%s ", ce->dev_tuning );
+ if ( ce->av_inf[0] )
+ pos += snprintf( buf+pos, size-pos, "av:\"%s\" ", ce->av_inf );
+ if ( ce->ch_desc[0] )
+ pos += snprintf( buf+pos, size-pos, "desc:%s ", ce->ch_desc );
+ pos += snprintf( buf+pos, size-pos, "\n" );
+ return pos;
+}
+
+int parse_channel_entry_t_buffer( struct channel_entry_t *ce, char* buf )
+{
+ int item = 0;
+ char *p = buf;
+ if ( !get_int_val( p, "ch", &ce->ch, NULL ) )
+ ce->ch = -1;
+ else
+ if ( !get_int_val( p, "state", &ce->state, NULL ) )
+ ce->ch = 1;
+ else
+ item++;
+ if ( !get_int_val( p, "ctrl", &ce->ctrl_bits, NULL ) )
+ ce->ctrl_bits = 0;
+ else
+ item++;
+ if ( !get_string( p, "name", ce->ch_name, sizeof(ce->ch_name)-1, NULL ) )
+ ce->ch_name[0] = 0x0;
+ else
+ item++;
+ if ( !get_string( p, "desc", ce->ch_desc, sizeof(ce->ch_desc)-1, NULL ) )
+ ce->ch_desc[0] = 0x0;
+ else
+ item++;
+ if ( !get_string( p, "ip", ce->src_ip, sizeof(ce->src_ip)-1, NULL ) )
+ ce->src_ip[0] = 0x0;
+ else
+ item++;
+ if ( !get_int_val( p, "port", &ce->src_port, NULL ) )
+ ce->src_port = -1;
+ else
+ item++;
+ if ( !get_string( p, "epg", ce->epg_id, sizeof(ce->epg_id)-1, NULL ) )
+ ce->epg_id[0] = 0x0;
+ else
+ item++;
+ if ( !get_string( p, "dev", ce->dev_name, sizeof(ce->dev_name)-1, NULL ) )
+ ce->dev_name[0] = 0x0;
+ else
+ item++;
+ if ( !get_string( p, "tuning", ce->dev_tuning, sizeof(ce->dev_tuning)-1, NULL ) )
+ ce->dev_tuning[0] = 0x0;
+ else
+ item++;
+ if ( !get_string_with_quote( p, "av", ce->av_inf, sizeof(ce->av_inf)-1, NULL ) )
+ ce->av_inf[0] = 0x0;
+ else
+ item++;
+ if ( !get_string_with_quote( p, "desc", ce->ch_desc, sizeof(ce->ch_desc)-1, NULL ) )
+ ce->ch_desc[0] = 0x0;
+ else
+ item++;
+
+ return item;
+}
+
+struct channel_list_t* create_channel_list( int num )
+{
+ if ( num <= 0 )
+ return NULL;
+ struct channel_list_t *cl = malloc( sizeof( struct channel_list_t));
+ if ( cl == NULL )
+ return NULL;
+ cl->num = num;
+ cl->channel_num = 0;
+ cl->ce = malloc( sizeof(struct channel_entry_t)*num );
+ if ( cl->ce == NULL ) {
+ free( cl );
+ return NULL;
+ }
+ memset( cl->ce, 0x0, sizeof(struct channel_entry_t)*num );
+ return cl;
+}
+
+void release_channel_list( struct channel_list_t *cl )
+{
+ if ( cl ) {
+ if ( cl->ce )
+ free( cl->ce );
+ free( cl );
+ }
+}
+
+struct channel_list_t* join_channel_list( struct channel_list_t* cl1, struct channel_list_t* cl2 )
+{
+ int i, n, channel_num;
+ if ( cl1 == NULL )
+ return cl2;
+ if ( cl2 == NULL )
+ return cl1;
+
+ channel_num = cl1->channel_num + cl2->channel_num;
+ if ( channel_num < cl1->num ) {
+ n = 0;
+ for ( i = cl1->channel_num; i<channel_num; i++ ) {
+ cl1->ce[i] = cl2->ce[n++];
+ cl1->ce[i].ch = i;
+ }
+ cl2->channel_num = channel_num;
+ release_channel_list( cl2 );
+ return cl1;
+ } else if ( channel_num < cl2->num ) {
+ n = 0;
+ for ( i = cl2->channel_num; i<channel_num; i++ ) {
+ cl2->ce[i] = cl1->ce[n++];
+ cl2->ce[i].ch = i;
+ }
+ cl2->channel_num = channel_num;
+ release_channel_list( cl1 );
+ return cl2;
+ } else {
+ struct channel_list_t* cl = create_channel_list( channel_num );
+ for ( i = 0; i<cl1->channel_num; i++ ) {
+ cl->ce[i] = cl1->ce[i];
+ cl->ce[i].ch = i;
+ }
+ for ( n = 0; n<cl2->channel_num; n++, i++ ) {
+ cl->ce[i] = cl2->ce[n];
+ cl->ce[i].ch = i;
+ }
+ release_channel_list( cl1 );
+ release_channel_list( cl2 );
+ cl->channel_num = channel_num;
+ return cl;
+ }
+ return NULL;
+}
+
+
+struct channel_list_t* load_channel_entry( char* file_name )
+{
+ int item;
+ int count = 0;
+ char buf[2048]={0};
+ struct channel_list_t *cl;
+ FILE *fp;
+ fp = fopen( file_name, "r" );
+ if ( fp == NULL ) {
+ //printf( "Can't open file %s to load channel table, errno:%d\n", file_name, errno );
+ return NULL;
+ }
+ //count entry number
+ while ( !feof(fp) ) {
+ char* p = fgets( buf, sizeof(buf)-1, fp );
+ if ( p == NULL )
+ break;
+ if ( buf[0] != '#' && strlen(buf) > 4 )
+ count++;
+ }
+ fseek( fp, 0, SEEK_SET );
+ cl = create_channel_list( count );
+ while ( !feof(fp) && cl->channel_num < cl->num ) {
+ char *p = fgets( buf, sizeof(buf)-1, fp );
+ if ( p == NULL )
+ break;
+
+ //skip comments
+ if ( buf[0] == '#' )
+ continue;
+
+ item = parse_channel_entry_t_buffer( &cl->ce[cl->channel_num], buf );
+ if ( item > 0 ) {
+ cl->channel_num++;
+ }
+ }
+ fclose( fp );
+ return cl;
+}
+
+int save_channel_entry( char* file_name, struct channel_list_t *cl )
+{
+ int i;
+ char buf[2048];
+ FILE *fp;
+ if ( cl == NULL || cl->channel_num == 0 )
+ return 0;
+
+ fp = fopen( file_name, "w" );
+ if ( fp == NULL ) {
+ //printf( "Can't open file %s to save channel table, errno:%d\n", file_name, errno );
+ return -1;
+ }
+ fputs( "#Google sagetv channel table ver 1.0., maxium 2048 bytes per line\n", fp );
+ fprintf( fp, "total_channel:%d\n", cl->channel_num );
+ for ( i = 0; i<cl->channel_num; i++ ) {
+ make_channel_entry_buffer( &cl->ce[i], buf, sizeof(buf) );
+ fputs( buf, fp );
+ }
+ fclose( fp );
+ return 1;
+}
+
+char* join_alloc_string( char* str1, char* str2 )
+{
+ int len, len1, len2;
+ char* str;
+ if ( str1 == NULL )
+ return str2;
+ if ( str2 == NULL )
+ return str1;
+ len1 = strlen( str1 );
+ len2 = strlen( str2 );
+ len = len1 + len2;
+ str = (char*)malloc( len+1 );
+ strncpy( str, str1, len1 );
+ strncpy( str+len1, str2, len2 );
+ str[len] = 0x0;
+ free( str1 );
+ free( str2 );
+ return str;
+}
+
diff --git a/channel_buffer.h b/channel_buffer.h
new file mode 100644
index 0000000..d586915
--- /dev/null
+++ b/channel_buffer.h
@@ -0,0 +1,50 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+// Author: qianzhang@google.com (ken Zhang)
+
+#ifndef _CHANNEL_BUFFER_H_
+#define _CHANNEL_BUFFER_H_
+
+#ifndef MAX
+#define MAX( x, y ) (( x)>(y) ? (x):(y) )
+#endif
+#ifndef MIN
+#define MIN( x, y ) (( x)<(y) ? (x):(y) )
+#endif
+
+struct channel_entry_t {
+ int ch;
+ int state;
+ int ctrl_bits; //0x01 fix muticast IP/port
+ char ch_name[20];
+ char ch_desc[32];
+ char src_ip[16];
+ int src_port;
+ char epg_id[8];
+ char dev_name[32];
+ char dev_tuning[16];
+ char av_inf[1024];
+ int frq;
+};
+
+struct channel_list_t {
+ int num;
+ int channel_num;
+ struct channel_entry_t *ce;
+};
+
+char* join_alloc_string( char* str1, char* str2 );
+int get_string( char*p, const char* name, char* string, int max_len, char **next );
+int get_string_with_quote( char*p, const char* name, char* string, int max_len, char **next );
+int get_string_token( char*p, int num, char* string, int max_len, char **next );
+int get_int_val( char*p, const char* name, int* val, char** next );
+
+struct channel_list_t* create_channel_list( int num );
+void release_channel_list( struct channel_list_t *cl );
+struct channel_list_t* join_channel_list( struct channel_list_t* cl1, struct channel_list_t* cl2 );
+
+int make_channel_entry_buffer( struct channel_entry_t *ce, char *buf, int size );
+int parse_channel_entry_buffer( struct channel_entry_t *ce, char* buf );
+struct channel_list_t* load_channel_entry( char* file_name );
+int save_channel_entry( char* file_name, struct channel_list_t *cl );
+
+#endif
diff --git a/hdhomerun_dev.c b/hdhomerun_dev.c
new file mode 100644
index 0000000..3d384c8
--- /dev/null
+++ b/hdhomerun_dev.c
@@ -0,0 +1,124 @@
+
+#include <stdio.h>
+ #include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include "utility.h"
+#include "hdhomerun_tuner.h"
+#include "hdhomerun_http.h"
+#include "hdhomerun_dev.h"
+#include "sagelog.h"
+
+#define _MIN_(x,y) ((x)>(y)? (y):(x))
+
+static int read_elemnt( char* xml, char* tag, char* buf, int size )
+{
+ char open_tag[256], close_tag[256];
+ char *start, *end;
+ snprintf( open_tag, sizeof(open_tag), "<%s>", tag );
+ snprintf( close_tag, sizeof(open_tag), "</%s>", tag );
+
+ start = strstr( xml, open_tag );
+ if ( start != NULL ) {
+ start += strlen( open_tag );
+ end = strstr( start, close_tag );
+ if ( end != NULL ) {
+ int len = _MIN_( end - start, size );
+ memcpy( buf, start, len );
+ buf[len] = 0;
+ end += strlen( close_tag );
+ return end - xml;
+ }
+ }
+ return 0;
+}
+
+static void html_txt_converison( char* txt )
+{
+ char *p;
+ do {
+ //remove &
+ p = strstr( txt, "&" );
+ if ( p )
+ strcpy( p+1, p+1+4 );
+ } while ( p );
+}
+
+//remove tag and trim space
+static void cleanup_txt( char* txt )
+{
+ int n = strlen( txt );
+ while ( n > 0 ) {
+ if ( txt[n] == ' ' || txt[n] == '<' || txt[n] == '\r' || txt[n] == '\n' )
+ txt[n] = 0x0;
+ n--;
+ }
+}
+
+static int program_parser( void* context, char* buf, int bytes )
+{
+ struct vchan_tbl_t *vt = (struct vchan_tbl_t*)context;
+ int pos, used_bytes;
+ char guide_num[16], guide_name[64];;
+ pos = 0;
+ while ( pos < bytes ) {
+ used_bytes = read_elemnt( buf+pos, "GuideNumber", guide_num, sizeof(guide_num) );
+ if ( used_bytes == 0 )
+ break;
+ pos += used_bytes;
+ //printf( "%s ", guide_num );
+ used_bytes = read_elemnt( buf+pos, "GuideName", guide_name, sizeof(guide_name) );
+ if ( used_bytes == 0 )
+ break;
+ html_txt_converison( guide_name );
+ if ( vt->channel_num >= vt->channel_size )
+ grow_vhcan_tbl( vt );
+ if ( vt->channel_num < vt->channel_size ) {
+ int i = vt->channel_num;
+ vt->vchan[i].guide_id = atoi( guide_num );
+ snprintf( vt->vchan[i].guide_name, sizeof(vt->vchan[i].guide_name), "\"%s\"", guide_name );
+ vt->channel_num++;
+ }
+ pos += used_bytes;
+ //printf( "%s;\n", guide_name );
+ }
+ return pos;
+}
+
+static int hdhomerun_parser( void* context, char* buf, int bytes )
+{
+ char *p;
+ struct hdhr_tuner_t *ht = (struct hdhr_tuner_t *)context;
+ p = strstr( buf, "Model:" );
+ if ( p != NULL ) {
+ if ( strlen( p+6 )<10 )
+ return p-buf;
+ if ( get_string_by_name( p, "Model", ht->model, sizeof(ht->model), NULL ) ) {
+ cleanup_txt( ht->model );
+ }
+ }
+
+ if ( strstr( buf, "CableCARD Menu" ) != NULL ) {
+ ht->cc_tuner = 1;
+ }
+ return bytes;
+}
+
+int get_dev_model( struct hdhr_tuner_t *ht )
+{
+ int ret = parse_http_page( ht->ip, "/", hdhomerun_parser, ht );
+ return ret;
+}
+
+int get_vchannel_list( struct vchan_tbl_t *vt )
+{
+ int ret = parse_http_page( vt->ht.ip, "/lineup.xml", program_parser, vt );
+ return ret;
+}
+
+ //parse_http_page( "192.168.1.168", "/", hdhomerun_parser, NULL );
+ //parse_http_page( "192.168.1.168", "/lineup.xml", program_parser, NULL );
+
diff --git a/hdhomerun_dev.h b/hdhomerun_dev.h
new file mode 100644
index 0000000..452e25a
--- /dev/null
+++ b/hdhomerun_dev.h
@@ -0,0 +1,9 @@
+
+#ifndef _HDHOMERUN_DEV_H_
+#define _HDHOMERUN_DEV_H_
+
+int get_dev_model( struct hdhr_tuner_t *ht );
+int get_vchannel_list( struct vchan_tbl_t *vt );
+
+#endif
+
diff --git a/hdhomerun_http.c b/hdhomerun_http.c
new file mode 100644
index 0000000..9ee9fca
--- /dev/null
+++ b/hdhomerun_http.c
@@ -0,0 +1,148 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <string.h>
+#include "hdhomerun_http.h"
+
+#define HOST "google.com"
+#define PAGE "/"
+#define PORT 80
+#define USERAGENT "SAGETV 1.0"
+
+#define _MIN_(x,y) ((x)>(y)? (y):(x))
+static char *get_ip(char *host, char* buf, int size)
+{
+ struct hostent *hent;
+ char *ip = buf;
+ memset(ip, 0, size);
+ if((hent = gethostbyname(host)) == NULL)
+ {
+ herror("Can't get IP");
+ exit(1);
+ }
+ if(inet_ntop(AF_INET, (void *)hent->h_addr_list[0], ip, size) == NULL)
+ {
+ perror("Can't resolve host");
+ exit(1);
+ }
+ return ip;
+}
+
+static char *build_get_query(char *host, char *page)
+{
+ char *query;
+ char *getpage = page;
+ char *tpl = "GET /%s HTTP/1.0\r\nHost: %s\r\nUser-Agent: %s\r\n\r\n";
+ if(getpage[0] == '/'){
+ getpage = getpage + 1;
+ }
+ // -5 is to consider the %s %s %s in tpl and the ending \0
+ query = (char *)malloc(strlen(host)+strlen(getpage)+strlen(USERAGENT)+strlen(tpl)-5);
+ sprintf(query, tpl, getpage, host, USERAGENT);
+ return query;
+}
+
+int parse_http_page( char* host, char* page, parser_f parser, void* context )
+{
+ struct sockaddr_in *remote;
+ int ret;
+ char buf[BUFSIZ+1];
+ int sock;
+ char *get;
+ char ip[32];
+ if((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0){
+ perror("Can't create TCP socket");
+ return -1;
+ }
+
+ get_ip( host, ip, sizeof( ip ) );
+ remote = (struct sockaddr_in *)malloc(sizeof(struct sockaddr_in *));
+ remote->sin_family = AF_INET;
+ ret = inet_pton(AF_INET, ip, (void *)(&(remote->sin_addr.s_addr)));
+ if( ret < 0) {
+ perror("Can't set remote->sin_addr.s_addr");
+ return -1;
+ }else if(ret == 0) {
+ fprintf(stderr, "%s is not a valid IP address\n", ip);
+ return -1;
+ }
+ remote->sin_port = htons(PORT);
+
+ if(connect(sock, (struct sockaddr *)remote, sizeof(struct sockaddr)) < 0){
+ perror("Could not connect");
+ return -1;
+ }
+ get = build_get_query(host, page);
+
+ //send the query to the server
+ int sent = 0;
+ while(sent < strlen(get))
+ {
+ ret = send(sock, get+sent, strlen(get)-sent, 0);
+ if(ret == -1){
+ perror("Can't send query");
+ break;
+ }
+ sent += ret;
+ }
+
+ //recieve page
+ int remain = 0;
+ while( (ret = recv(sock, buf+remain, BUFSIZ-remain, 0)) > 0 ) {
+ int used_bytes = 0;
+ if ( parser != NULL && ret >= 0 ) {
+ *(buf+remain+ret) = 0x0;
+ used_bytes = parser( context, buf, ret+remain );
+ remain = ret+remain - used_bytes;
+ if ( remain > 0 )
+ memcpy( buf, buf+used_bytes, remain );
+ }
+ if ( used_bytes < 0 )
+ break;
+ }
+ free(get);
+ free(remote);
+ close(sock);
+ return 1;
+}
+
+#ifdef HTTP_GET_APP
+static void usage()
+{
+ fprintf(stderr, "USAGE: htmlget host [page]\n\
+\thost: the website hostname. ex: coding.debuntu.org\n\
+\tpage: the page to retrieve. ex: index.html, default: /\n");
+}
+
+static int noop_parser( void* context, char* buf, int bytes )
+{
+ printf( "%s", buf );
+ return bytes;
+}
+
+int main(int argc, char **argv)
+{
+ char *host;
+ char *page;
+
+ if(argc == 1){
+ usage();
+ exit(2);
+ }
+ host = argv[1];
+ if(argc > 2){
+ page = argv[2];
+ }else{
+ page = PAGE;
+ }
+
+ parse_http_page( host, page, noop_parser, NULL );
+ return 0;
+}
+
+#endif
+
diff --git a/hdhomerun_http.h b/hdhomerun_http.h
new file mode 100644
index 0000000..0708ff8
--- /dev/null
+++ b/hdhomerun_http.h
@@ -0,0 +1,8 @@
+
+#ifndef _HDHOMERUN_HTTP_H_
+#define _HDHOMERUN_HTTP_H_
+
+typedef int (*parser_f)( void*, char*, int );
+int parse_http_page( char* host, char* page, parser_f parser, void* context );
+
+#endif
diff --git a/hdhomerun_plugin.c b/hdhomerun_plugin.c
new file mode 100644
index 0000000..b32abd5
--- /dev/null
+++ b/hdhomerun_plugin.c
@@ -0,0 +1,303 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+// Author: qianzhang@google.com (Qian Zhang)
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <assert.h>
+#include "utility.h"
+//#include "../channel_srv/channel_device.h"
+//#include "../channel_srv/channel_buffer.h"
+#include "hdhomerun_plugin.h"
+#include "libhdhomerun/hdhomerun.h"
+#include "hdhomerun_tuner.h"
+#include "hdhomerun_dev.h"
+#include "hdhomerun_tuner.h"
+#include "channel_buffer.h"
+#include "sagelog.h"
+
+#define MAX_TUNER_NUM 16
+
+struct device_list {
+ int tuner_num;
+ int list_num;
+ struct hdhr_tuner_t *hdhrs;
+};
+
+static int discover_hdhrs( struct hdhr_tuner_t *ht, int max_ht_num )
+{
+ int i, ret = 0 ;
+ ret = discover_device( ht, max_ht_num );
+ if ( ret > 0 ) {
+ for ( i = 0; i<ret; i++ ) {
+ //if a unit shares a source input in a device, copy data.
+ if ( i> 0 && tuner_input_sharing( &ht[i-1], &ht[i] ) ) {
+ ht[i].cc_tuner = ht[i-1].cc_tuner;
+ strncpy( ht[i].model, ht[i-1].model, sizeof(ht[i].model) );
+ } else
+ get_dev_model( &ht[i] );
+ }
+ return i;
+ }
+ return 0;
+}
+
+void* dev_init( int *num )
+{
+ int i, index = 0, tuner_num;
+ char *type_name[2] ={ "NR", "CC" };
+ sage_log(( _LOG_TRACE, 3, "hdhr::hdhomerun device init" ));
+ struct device_list *devices = (struct device_list *)malloc( sizeof(struct device_list) );
+ if ( devices != NULL ) {
+ devices->hdhrs = (struct hdhr_tuner_t *)malloc(sizeof(struct hdhr_tuner_t) * MAX_TUNER_NUM );
+ memset( devices->hdhrs, 0, sizeof(sizeof(struct hdhr_tuner_t) * MAX_TUNER_NUM) );
+ if ( devices->hdhrs == NULL ) {
+ free( devices);
+ return 0;
+ }
+ devices->list_num = MAX_TUNER_NUM;
+ devices->tuner_num = 0;
+ tuner_num = discover_hdhrs( devices->hdhrs, MAX_TUNER_NUM );
+ for ( i = 0; i<tuner_num; i++ ) {
+ int type = 0;
+ devices->hdhrs[i].tuner_id = index++;
+ if ( devices->hdhrs[i].cc_tuner )
+ type = 1;
+ else
+ type = 0;
+ snprintf( devices->hdhrs[i].tuner_name, sizeof(devices->hdhrs[i].tuner_name), "HDHR-%s-%s", type_name[type], devices->hdhrs[i].name );
+ devices->tuner_num++;
+ sage_log(( _LOG_TRACE, 3, "hdhr:%d %s %s %s", i, devices->hdhrs[i].tuner_name, devices->hdhrs[i].model, devices->hdhrs[i].cc_tuner?"CableCard":"" ));
+ }
+ devices->tuner_num = tuner_num;
+ *num = tuner_num;
+ return devices;
+ }
+ return NULL;
+}
+
+void dev_release( void* handle )
+{
+ struct device_list *devices = (struct device_list *)handle;
+ if ( devices != NULL ) {
+ if ( devices->hdhrs != NULL )
+ free( devices->hdhrs );
+ free( devices );
+ }
+ sage_log(( _LOG_TRACE, 3, "hdhr:hdhomerun device release." ));
+}
+
+int get_dev_name( void* handle, int index, char* name, int max_name_len )
+{
+ struct device_list *devices = (struct device_list *)handle;
+ if ( devices != NULL && index < devices->tuner_num ) {
+ struct hdhr_tuner_t* hdhr = &devices->hdhrs[index];
+ strncpy( name, hdhr->tuner_name, max_name_len );
+ return 1;
+ }
+ return 0;
+}
+
+void* dev_open( void* handle, int index, int attr )
+{
+ struct device_list *devices = (struct device_list *)handle;
+ struct hdhr_tuner_t* hdhr;
+ if ( index >= devices->tuner_num )
+ return NULL;
+ hdhr = &devices->hdhrs[index];
+ hdhr->type = attr; //1:atsc 2:qam
+ hdhr->hd = open_hdhr( hdhr->name );
+ return hdhr;
+}
+
+void dev_close( void* dev )
+{
+ struct hdhr_tuner_t* hdhr = (struct hdhr_tuner_t*)dev;
+ if ( hdhr == NULL ) {
+ sage_log(( _LOG_TRACE, 3, "hdhr:ERROR: invalid device handle." ));
+ return;
+ }
+ stop_channel( hdhr->hd );
+ close_hdhr( hdhr->hd );
+}
+
+int dev_tune( void* dev, char* channel, char* stream_tar )
+{
+ int ret;
+ struct hdhr_tuner_t* hdhr = (struct hdhr_tuner_t*)dev;
+ if ( hdhr == NULL ) {
+ sage_log(( _LOG_TRACE, 3, "hdhr:ERROR: invalid device handle." ));
+ return 0;
+ }
+ sage_log(( _LOG_TRACE, 3, "hdhr:hdhomerun tune %s %s.", hdhr->name, (char*)channel ));
+ if ( strstr( stream_tar, "udp://" ) == NULL ) {
+ sage_log(( _LOG_TRACE, 3, "hdhr:ERROR: stream target \"%s\" is wrong.", stream_tar ));
+ return 0;
+ }
+ //cablecard tuner:HDHomerun prime unit
+ if ( hdhr->cc_tuner ) {
+ if ( strstr( channel, "vchan:" ) == NULL ) {
+ char vchan[32];
+ snprintf( vchan, sizeof(vchan), "vchan:%d", atoi(channel) );
+ ret = tune_channel( hdhr->hd, vchan, "", stream_tar );
+ } else
+ ret = tune_channel( hdhr->hd, channel, "", stream_tar );
+ } else {
+ //HDHomeRun unit
+ char tune_str[32], map_str[8], prog_str[8];
+ int ch = -1, prog = -1;
+ char *ps, *pe;
+ map_str[0] = 0x0;
+ //if channel in format "map:xxx-yy|auto:zzz prog:ppp"
+ if ( ( ps = strstr( channel, "map:" ))!= NULL &&
+ ( pe = strchr( ps+4, '|' )) != NULL ) {
+ strncpy( map_str, ps+4, pe-(ps+4) );
+ map_str[pe-(ps+4)] = 0x0;
+ }
+ get_int_val_by_name( channel, "auto", &ch, NULL );
+ get_int_val_by_name( channel, "prog", &prog, NULL );
+ if ( prog == -1 ) {
+ sage_log(( _LOG_TRACE, 3, "hdhr:ERROR: invalid channel string %s", channel ));
+ return 0;
+ }
+ //if channel in format "nnn prog:mmm", based on dev to guess map
+ if ( map_str[0] == 0x0 && prog > 0 && ch == -1 ) {
+ ch=atoi( channel );
+ if ( ch > 0 ) {
+ int atsc = ( hdhr->type == 1 ); //TODO dvb
+ snprintf( map_str, sizeof( map_str), "map:us-%s", atsc? "atsc":"cable" );
+ }
+ }
+ if ( map_str[0] && ch > 0 && prog > 0 ) {
+ snprintf( tune_str, sizeof(tune_str), "%s|auto:%d", map_str, ch );
+ snprintf( prog_str, sizeof(prog), "%d", prog );
+ ret = tune_channel( hdhr->hd, tune_str, prog_str, stream_tar );
+ } else {
+ sage_log(( _LOG_ERROR, 3, "hdhr:ERROR: invalid channel string %s", channel ));
+ return 0;
+ }
+ }
+ sage_log(( _LOG_TRACE, 3, "hdhr:hdhomerun tune %s %s.", (char*)channel, ret>0?"successful":"failed" ));
+ return ret > 0;
+}
+
+int dev_stop( void* dev )
+{
+ struct hdhr_tuner_t* hdhr = (struct hdhr_tuner_t*)dev;
+ if ( hdhr == NULL ) {
+ sage_log(( _LOG_TRACE, 3, "hdhr:ERROR: invalid device handle." ));
+ return 0;
+ }
+ sage_log(( _LOG_TRACE, 3, "hdhr:hdhomerun stop %s.", (char*)hdhr->name ));
+ int ret = stop_channel( hdhr->hd );
+ return ret > 0;
+}
+
+int dev_scan( void* dev, char* tuner_name, char* channel, struct channel_list_t **cl_p )
+{
+ struct hdhr_tuner_t* hdhr = (struct hdhr_tuner_t*)dev;
+ if ( hdhr == NULL ) {
+ sage_log(( _LOG_TRACE, 3, "hdhr:ERROR: invalid device handle." ));
+ return 0;
+ }
+ sage_log(( _LOG_TRACE, 3, "hdhr:hdhomerun scan %s.", (char*)hdhr->name ));
+ if ( hdhr->cc_tuner ) {
+ int vchannel;
+ char *p;
+ if ( ( p = strstr( channel, "vchan:" ) ) == NULL )
+ vchannel = atoi(channel) ;
+ else
+ vchannel = atoi(p+6);
+ if ( vchannel == 0 )
+ return 0;
+
+ struct channel_list_t *cl = _create_channel_list( 1 );
+ int ret = scan_vchannel( hdhr->hd, vchannel, cl->ce );
+ char unit_name[16];
+ snprintf( cl->ce[0].dev_name, sizeof(cl->ce[0].dev_name)-1, "HDHR.%s", get_unit_name( hdhr, unit_name, sizeof(unit_name) ));
+ cl->channel_num = ret;
+ if ( ret == 0 ) {
+ _release_channel_list( cl );
+ cl = NULL;
+ }
+ if ( cl_p != NULL )
+ *cl_p = cl;
+ return ret;
+ } else {
+ //TODO
+ }
+
+ return 0;
+}
+
+static struct channel_list_t * build_hdhr_channel_table( struct hdhr_tuner_t* hdhr )
+{
+ if ( hdhr->cc_tuner ) {
+ struct vchan_tbl_t* vt = create_vchan_tbl( hdhr );
+ struct channel_list_t *cl = NULL;
+ if ( get_vchannel_list( vt ) > 0 ) {
+ int num = vt->channel_num;
+ cl = _create_channel_list( num );
+ int i;
+ for ( i = 0; i<num; i++ ) {
+ char unit_name[16];
+ cl->ce[i].state = 1;
+ snprintf( cl->ce[i].dev_tuning, sizeof(cl->ce[i].dev_tuning)-1, "vchan:%d", vt->vchan[i].guide_id );
+ snprintf( cl->ce[i].ch_name, sizeof(cl->ce[i].ch_name)-1, "%d", vt->vchan[i].guide_id );
+ strncpy( cl->ce[i].ch_desc, vt->vchan[i].guide_name, sizeof(cl->ce[i].ch_desc)-1 );
+ cl->ce[i].src_ip[0] = 0x0;
+ cl->ce[i].src_port = 0; //dynamic assigning
+ snprintf( cl->ce[i].dev_name, sizeof(cl->ce[i].dev_name)-1, "HDHR.%s", get_unit_name( hdhr, unit_name, sizeof(unit_name) ));
+ cl->ce[i].ch = vt->vchan[i].guide_id;
+ cl->ce[i].frq = 0;
+ cl->ce[i].av_inf[0] = 0;
+ }
+ cl->channel_num = i;
+ }
+ release_vchan_tbl( vt );
+ return cl;
+ } else { //TODO
+
+ }
+ return NULL;
+}
+
+int dev_build_channel_table( void* dev, char* option, struct channel_list_t **cl_p )
+{
+ struct hdhr_tuner_t* hdhr = (struct hdhr_tuner_t*)dev;
+ if ( hdhr == NULL ) {
+ sage_log(( _LOG_TRACE, 3, "hdhr:ERROR: invalid device handle." ));
+ return 0;
+ }
+ sage_log(( _LOG_TRACE, 3, "hdhr:hdhomerun dev_build_channel_table %s.", (char*)hdhr->name ));
+ struct channel_list_t *cl= build_hdhr_channel_table( hdhr );
+ if ( cl_p != NULL ) {
+ *cl_p = cl;
+ if ( cl == NULL )
+ return 0;
+ return cl->channel_num;
+ }
+ return 0;
+}
+
+int dev_release_channel_table( void* dev )
+{
+ struct hdhr_tuner_t* hdhr = (struct hdhr_tuner_t*)dev;
+ if ( hdhr == NULL ) {
+ sage_log(( _LOG_TRACE, 3, "hdhr:ERROR: invalid device handle." ));
+ return 0;
+ }
+ sage_log(( _LOG_TRACE, 3, "hdhr:hdhomerun dev_release_channel_table %s.", (char*)hdhr->name ));
+ if ( hdhr->channel_table != NULL ) {
+ free( hdhr->channel_table );
+ hdhr->channel_table = NULL;
+ return 1;
+ } else { //TODO
+
+ }
+ return 0;
+}
+
+
diff --git a/hdhomerun_plugin.h b/hdhomerun_plugin.h
new file mode 100644
index 0000000..9928358
--- /dev/null
+++ b/hdhomerun_plugin.h
@@ -0,0 +1,16 @@
+
+#ifndef _HDHOMERUN_PLUGIN_H_
+#define _HDHOMERUN_PLUGIN_H_
+
+void* dev_init( int *num ); //return tuner handle
+void dev_release( void* handle ); //release tuner
+int get_dev_name( void* handle, int index, char* name, int max_name_len );
+void* dev_open( void* handle, int index, int attr );
+void dev_close( void* dev );
+int dev_tune( void* dev, char* channel, char* stream_tar );
+int dev_stop( void* dev );
+int dev_scan( void* dev, char* tuner_name, char* channel, struct channel_list_t **cl_p );
+int dev_build_channel_table( void* dev, char* option, struct channel_list_t **cl_p );
+int dev_release_channel_table( void* dev );
+
+#endif
diff --git a/hdhomerun_tuner.c b/hdhomerun_tuner.c
new file mode 100644
index 0000000..cae211c
--- /dev/null
+++ b/hdhomerun_tuner.c
@@ -0,0 +1,648 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+// Author: qianzhang@google.com (Qian Zhang)
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include "utility.h"
+#include "channel_buffer.h"
+#include "libhdhomerun/hdhomerun.h"
+#include "hdhomerun_tuner.h"
+#include "hdhomerun_dev.h"
+#include "sagelog.h"
+
+#define PARSE_TIMEOUT 2
+#define CHANNE_SCAN_SRC "227.0.0.1"
+#define CHANNEL_SCAN_PORT 6069
+static volatile sig_atomic_t sigabort_flag = FALSE;
+static volatile sig_atomic_t siginfo_flag = FALSE;
+static const char* map_tbl[] ={ "us-bcast", "us-cable", "us-hrc", "us-irc", "au-bcast", "tw-bcast", "eu-bcast", 0x0 };
+
+static void sigabort_handler(int arg)
+{
+ sigabort_flag = TRUE;
+}
+
+static void siginfo_handler(int arg)
+{
+ siginfo_flag = TRUE;
+}
+
+static void register_signal_handlers(sig_t sigpipe_handler, sig_t sigint_handler, sig_t siginfo_handler)
+{
+#if defined(SIGPIPE)
+ signal(SIGPIPE, sigpipe_handler);
+#endif
+#if defined(SIGINT)
+ signal(SIGINT, sigint_handler);
+#endif
+#if defined(SIGINFO)
+ signal(SIGINFO, siginfo_handler);
+#endif
+}
+
+//channel format: map:us-cable,auto:68
+void* open_hdhr( char* name )
+{
+ struct hdhomerun_device_t *hd;
+ hd = hdhomerun_device_create_from_str( name, NULL);
+ return hd;
+}
+void close_hdhr( void* device )
+{
+ struct hdhomerun_device_t *hd =(struct hdhomerun_device_t *)device;
+ if ( hd ) {
+ hdhomerun_device_stream_stop( hd );
+ hdhomerun_device_destroy( hd );
+ }
+}
+
+int tune_channel( void* device, char *channel, char *program, char* stream_target )
+{
+ char channel_str[32]="auto:";
+ char vchannel_str[16];
+ char channel_map[32];
+ struct hdhomerun_device_t *hd =(struct hdhomerun_device_t *)device;
+ struct hdhomerun_channel_list_t *channel_list=NULL;
+ int ret;
+
+ if ( hd == NULL ) {
+ printf( "hdhomerun invalid handle\n");
+ return -1;
+ }
+
+ if ( !get_string_by_name( channel, "vchan", vchannel_str, sizeof(vchannel_str), NULL ) ) {
+ vchannel_str[0] = 0x0;
+
+ if ( !get_string_by_name( channel, "auto", channel_str+5, sizeof(channel_str)-5, NULL ) ) {
+ printf("hdhomerun %p invalid channel %s\n", device, channel );
+ return -1;
+ }
+ if ( !get_string_by_name( channel, "map", channel_map, sizeof(channel_map), NULL ) ) {
+ printf("hdhomerun %p invalid channel map %s\n", device, channel );
+ strncpy( channel_map, "us-cable", sizeof(channel_map) );
+ }
+ if ( program[0] == 0x0 ) {
+ printf("hdhomerun %p invalid channel program %s\n", device, program );
+ return -1;
+ }
+ channel_list = hdhomerun_channel_list_create( channel_map );
+ if ( channel_list == NULL ) {
+ printf( "hdhomerun failed to create channel map\n");
+ return -1;
+ }
+ ret = hdhomerun_device_set_tuner_channelmap( hd, channel_map );
+ if ( ret < 0 )
+ printf( "hdhomerun failed to set tuner channel map %s\n", channel_map );
+
+ ret = hdhomerun_device_set_tuner_channel( hd, channel_str );
+ if ( ret < 0 )
+ printf("hdhomerun tuning failed:%s ret:%d\n", channel_str, ret );
+
+ ret = hdhomerun_device_set_tuner_program( hd, program );
+ if ( ret <= 0 )
+ printf( "hdhomerun failed set program:%s ret:%d\n", program, ret );
+ } else {
+ ret = hdhomerun_device_set_tuner_vchannel( hd, vchannel_str );
+ if ( ret < 0 )
+ printf("hdhomerun tuning failed: vchannel:%s ret:%d\n", vchannel_str, ret );
+ }
+
+ char target[64];
+ snprintf( target, sizeof(target), "%s ttl-16",stream_target );
+ ret = hdhomerun_device_set_tuner_target( hd, target );
+ if ( ret < 0 ) {
+ printf( "hdhomerun failed set target %s ret:%d\n", target, ret );
+ }
+ if ( channel_list )
+ hdhomerun_channel_list_destroy( channel_list );
+
+ return ret;
+}
+
+//channel format: map:us-cable,auto:68
+int stop_channel( void* device )
+{
+ int ret;
+ struct hdhomerun_device_t *hd =(struct hdhomerun_device_t *)device;
+ if ( hd == NULL ) {
+ printf( "hdhomerun invalid handle\n");
+ return -1;
+ }
+
+ ret = hdhomerun_device_set_tuner_channel( hd, "none" );
+ if ( ret < 0 ) {
+ printf( "hdhomerun failed set channel none to stop streaming ret:%d\n", ret );
+ }
+
+ ret = hdhomerun_device_set_tuner_vchannel( hd, "none" );
+ if ( ret < 0 ) {
+ printf( "hdhomerun failed set vchannel none to stop streaming ret:%d\n", ret );
+ }
+
+ ret = hdhomerun_device_set_tuner_target( hd, "none" );
+ if ( ret < 0 ) {
+ printf( "hdhomerun failed set target none to stop streaming ret:%d\n", ret );
+ }
+
+ return ret;
+}
+
+char* get_unit_name( struct hdhr_tuner_t *ht, char* name, int size )
+{
+ char* p = strchr( ht->name, '-' );
+ if ( p != NULL ) {
+ int len = p - ht->name;
+ strncpy( name, ht->name, len );
+ name[len] = 0x0;
+ return name;
+ }
+ name[ 0 ] = 0x0;
+ return name;
+}
+
+static char* ip4_address( int ip4, char *buf, int size )
+{
+ snprintf( buf, size, "%u.%u.%u.%u",
+ (unsigned int)(ip4 >> 24) & 0xFF, (unsigned int)(ip4 >> 16) & 0xFF,
+ (unsigned int)(ip4 >> 8) & 0xFF, (unsigned int)(ip4 >> 0) & 0xFF );
+ return buf;
+}
+
+int discover_device( struct hdhr_tuner_t *ht, int max_ht_num )
+{
+ int i, k, num = 0;
+ struct hdhomerun_discover_device_t *device_info = malloc( max_ht_num * sizeof(struct hdhomerun_discover_device_t) );
+ int ret = hdhomerun_discover_find_devices_custom(0, HDHOMERUN_DEVICE_TYPE_TUNER, -1, device_info, 16);
+ if ( ret <= 0 ) {
+ free( device_info );
+ return 0;
+ }
+ //printf( "found %d hdhomerun device\n", ret );
+ for ( i = 0; i<ret; i++ ) {
+ //printf( "ip:%x type:%x id:%x num:%d\n", device_info[i].ip_addr, device_info[i].device_type, device_info[i].device_id,
+ // device_info[i].tuner_count );
+ for ( k = 0; k<device_info[i].tuner_count && num < max_ht_num; k++ ) {
+ snprintf( ht[num].name, sizeof(ht[num].name), "%X-%d", device_info[i].device_id, k );
+ ip4_address( device_info[i].ip_addr, ht[num].ip, sizeof(ht[num].ip) );
+ ht[num].type = device_info[i].device_type;
+ ht[num].cc_tuner = 0;
+ ht[num].model[0] = 0;
+ num++;
+ }
+ }
+ free( device_info );
+ return num;
+}
+
+static char *get_tune_string( char* result, char* tune_str, int tune_str_len )
+{
+ int i = 0;
+ int ch;
+ while ( map_tbl[i][0] ) {
+ if ( get_int_val_by_name( result, map_tbl[i], &ch, NULL ) > 0 ) {
+ snprintf( tune_str, tune_str_len, "map:%s|auto:%d", map_tbl[i], ch );
+ return tune_str;
+ }
+ i++;
+ }
+ return "";
+}
+
+static int get_tune_channel( char* result )
+{
+ int i = 0;
+ int ch;
+ while ( map_tbl[i][0] ) {
+ if ( get_int_val_by_name( result, map_tbl[i], &ch, NULL ) > 0 ) {
+ return ch;
+ }
+ i++;
+ }
+ return 0;
+}
+
+int scan_all( void* device, struct channel_entry_t *ce, int ce_num )
+{
+ int found_channel = 0;
+ struct hdhomerun_device_t *hd =(struct hdhomerun_device_t *)device;
+ if ( hd == NULL ) {
+ printf( "hdhomerun invalid handle\n");
+ return -1;
+ }
+
+ char *ret_error;
+ if (hdhomerun_device_tuner_lockkey_request(hd, &ret_error) <= 0) {
+ fprintf(stderr, "failed to lock tuner\n");
+ if (ret_error) {
+ fprintf(stderr, "%s\n", ret_error);
+ }
+ return -1;
+ }
+
+ hdhomerun_device_set_tuner_target(hd, "none");
+
+ char *channelmap;
+ if (hdhomerun_device_get_tuner_channelmap(hd, &channelmap) <= 0) {
+ fprintf(stderr, "failed to query channelmap from device\n");
+ return -1;
+ }
+
+ const char *channelmap_scan_group = hdhomerun_channelmap_get_channelmap_scan_group(channelmap);
+ if (!channelmap_scan_group) {
+ fprintf(stderr, "unknown channelmap '%s'\n", channelmap);
+ return -1;
+ }
+
+ if (hdhomerun_device_channelscan_init(hd, channelmap_scan_group) <= 0) {
+ fprintf(stderr, "failed to initialize channel scan\n");
+ return -1;
+ }
+
+ register_signal_handlers(sigabort_handler, sigabort_handler, siginfo_handler);
+
+ int ret = 0;
+ while (!sigabort_flag && found_channel < ce_num ) {
+ struct hdhomerun_channelscan_result_t result;
+ char tune_str[32];
+ char channel_name[32];
+ int channel_num;
+ ret = hdhomerun_device_channelscan_advance( hd, &result );
+ if (ret <= 0) {
+ break;
+ }
+
+ ret = hdhomerun_device_channelscan_detect( hd, &result );
+ if (ret < 0) {
+ break;
+ }
+ if (ret == 0) {
+ continue;
+ }
+ if ( 0 )
+ printf("LOCK: %s %s (ss=%u snq=%u seq=%u)\n", get_tune_string( result.channel_str, tune_str, sizeof(tune_str)),
+ result.status.lock_str, result.status.signal_strength,
+ result.status.signal_to_noise_quality, result.status.symbol_error_quality
+ );
+
+ channel_num = get_tune_channel( result.channel_str );
+ int i;
+ for (i = 0; i < result.program_count; i++) {
+ struct hdhomerun_channelscan_program_t *program = &result.programs[i];
+ if ( strstr( program->program_str, "encrypted" ) == NULL ) {
+ int program_id = atoi( program->program_str );
+
+ if ( !get_string_by_token( program->program_str, 2, channel_name, sizeof(channel_name), NULL ) ) {
+ if ( !get_string_by_token( program->program_str, 1, channel_name, sizeof(channel_name), NULL ) )
+ snprintf( channel_name, sizeof(channel_name), "%d.%d", channel_num, i );
+ else {
+ if ( !strcmp(channel_name, "0" ) )
+ snprintf( channel_name, sizeof(channel_name), "%d.%d", channel_num, i );
+ }
+ }
+ snprintf( ce->dev_tuning, sizeof(ce->dev_tuning), "%s prog:%d", get_tune_string( result.channel_str, tune_str, sizeof(tune_str) ),
+ program_id );
+ strncpy( ce->ch_name, channel_name, sizeof(ce->ch_name) );
+ strncpy( ce->dev_name, device, sizeof(ce->dev_name) );
+ ce->ch = found_channel;
+ ce->frq = result.frequency;
+ ce->av_inf[0] = 0x0;
+ ce++;
+ if ( ++found_channel >= ce_num )
+ break;
+
+ printf("o");
+ }
+ }
+
+ printf( "." ); fflush( stdout );
+ }
+
+ hdhomerun_device_tuner_lockkey_release(hd);
+
+ if (ret < 0) {
+ fprintf(stderr, "communication error sending request to hdhomerun device\n");
+ }
+ return found_channel;
+}
+
+
+struct vchan_tbl_t* create_vchan_tbl( struct hdhr_tuner_t *ht )
+{
+ struct vchan_tbl_t *vt = malloc( sizeof( struct vchan_tbl_t ));
+ vt->ht = *ht;
+ vt->channel_size = 500;
+ vt->channel_num = 0;
+ vt->vchan = malloc( sizeof(struct vchan_t )*vt->channel_size );
+ memset( vt->vchan, 0x0, sizeof(struct vchan_t )*vt->channel_size );
+ return vt;
+}
+
+void release_vchan_tbl( struct vchan_tbl_t *vt )
+{
+ if ( vt->vchan );
+ free( vt->vchan );
+ free( vt );
+}
+
+int grow_vhcan_tbl( struct vchan_tbl_t *vt )
+{
+ int new_channel_size = vt->channel_size + 100;
+ struct vchan_t * new_vchan_list = malloc( sizeof(struct vchan_t)*new_channel_size );
+ if ( new_vchan_list == NULL )
+ return 0;
+ memcpy( new_vchan_list, vt->vchan, vt->channel_size*sizeof(struct vchan_t) );
+ free( vt->vchan );
+ vt->vchan = new_vchan_list;
+ vt->channel_size = new_channel_size;
+ return 1;
+}
+
+int tuner_input_sharing( struct hdhr_tuner_t *ht1, struct hdhr_tuner_t *ht2 )
+{
+ if ( ht1->cc_tuner == 1 && !strcmp( ht1->ip, ht2->ip ) )
+ return 1;
+ return 0;
+}
+
+
+#ifdef TEST_APP
+static int scan_channel( void* device, int index, struct channel_entry_t *ce, int ce_channel_num, int ce_num )
+{
+ int found_channel = 0;
+ struct hdhomerun_device_t *hd =(struct hdhomerun_device_t *)device;
+ if ( hd == NULL ) {
+ printf( "hdhomerun invalid handle\n");
+ return -1;
+ }
+
+ char *ret_error;
+ if (hdhomerun_device_tuner_lockkey_request(hd, &ret_error) <= 0) {
+ fprintf(stderr, "failed to lock tuner\n");
+ if (ret_error) {
+ fprintf(stderr, "%s\n", ret_error);
+ }
+ return -1;
+ }
+
+ hdhomerun_device_set_tuner_target(hd, "none");
+
+ char *channelmap;
+ if (hdhomerun_device_get_tuner_channelmap(hd, &channelmap) <= 0) {
+ fprintf(stderr, "failed to query channelmap from device\n");
+ return -1;
+ }
+
+ const char *channelmap_scan_group = hdhomerun_channelmap_get_channelmap_scan_group(channelmap);
+ if (!channelmap_scan_group) {
+ fprintf(stderr, "unknown channelmap '%s'\n", channelmap);
+ return -1;
+ }
+
+ if (hdhomerun_device_channelscan_init(hd, channelmap_scan_group) <= 0) {
+ fprintf(stderr, "failed to initialize channel scan\n");
+ return -1;
+ }
+
+ register_signal_handlers(sigabort_handler, sigabort_handler, siginfo_handler);
+
+ int ret = 0, i;
+ do {
+ struct hdhomerun_channelscan_result_t result;
+ char tune_str[32];
+ char channel_name[32];
+ int channel_num;
+ ret = hdhomerun_device_channelscan_at( hd, index, &result );
+ //ret = hdhomerun_device_channelscan_advance( hd, &result );
+ if (ret <= 0) {
+ break;
+ }
+ /* skip dumplicated channel*/
+ for ( i = 0; i<ce_channel_num; i++ ) {
+ if ( ce->frq == result.frequency )
+ break;
+ }
+ if ( ce->frq == result.frequency )
+ break;
+ ret = hdhomerun_device_channelscan_detect( hd, &result );
+ if (ret < 0) {
+ break;
+ }
+ if (ret == 0) {
+ continue;
+ }
+
+ if ( 0 )
+ printf("LOCK: %s %s (ss=%u snq=%u seq=%u)\n", get_tune_string( result.channel_str, tune_str, sizeof(tune_str)),
+ result.status.lock_str, result.status.signal_strength,
+ result.status.signal_to_noise_quality, result.status.symbol_error_quality
+ );
+
+ channel_num = get_tune_channel( result.channel_str );
+ for ( i = 0; i < result.program_count; i++) {
+ struct hdhomerun_channelscan_program_t *program = &result.programs[i];
+ if ( strstr( program->program_str, "encrypted" ) == NULL ) {
+ int program_id = atoi( program->program_str );
+
+ if ( !get_string_by_token( program->program_str, 2, channel_name, sizeof(channel_name), NULL ) ) {
+ if ( !get_string_by_token( program->program_str, 1, channel_name, sizeof(channel_name), NULL ) )
+ snprintf( channel_name, sizeof(channel_name), "%d.%d", channel_num, i );
+ else {
+ if ( !strcmp(channel_name, "0" ) )
+ snprintf( channel_name, sizeof(channel_name), "%d.%d", channel_num, i );
+ }
+ }
+ struct channel_entry_t *ce_p = &ce[ce_channel_num];
+ snprintf( ce->dev_tuning, sizeof(ce->dev_tuning), "%s prog:%d", get_tune_string( result.channel_str, tune_str, sizeof(tune_str) ),
+ program_id );
+ strncpy( ce_p->ch_name, channel_name, sizeof(ce_p->ch_name) );
+ strncpy( ce_p->dev_name, device, sizeof(ce_p->dev_name) );
+ ce_p->ch = found_channel+ce_channel_num;
+ ce_p->frq = result.frequency;
+ ce_p->av_inf[0] = 0x0;
+ char stream_tar[32];
+ snprintf( stream_tar, sizeof(stream_tar), "udp://%s:%u", CHANNE_SCAN_SRC, CHANNEL_SCAN_PORT );
+ ret = tune_channel( hd, ce_p->dev_tuning, "", stream_tar );
+ if ( ret > 0 )
+ ret = parse_avinf( CHANNE_SCAN_SRC, CHANNEL_SCAN_PORT, ce_p->av_inf, sizeof(ce_p->av_inf), PARSE_TIMEOUT );
+ if ( ret > 0 )
+ ret = strstr( ce_p->av_inf, "ENCRYPTED" )==NULL && strstr( ce_p->av_inf, "NO-DATA" )==NULL ;
+ if ( ret )
+ if ( ++ce_channel_num > ce_num )
+ break;
+ }
+ }
+ } while ( 0 );
+
+ hdhomerun_device_tuner_lockkey_release(hd);
+
+ if (ret < 0) {
+ fprintf(stderr, "communication error sending request to hdhomerun device\n");
+ }
+ return found_channel;
+}
+
+int scan_vchannel( void* device, int vchannel, struct channel_entry_t *ce )
+{
+ int ret;
+ struct hdhomerun_device_t *hd =(struct hdhomerun_device_t *)device;
+ if ( hd == NULL ) {
+ sage_log(( _LOG_ERROR, 3, "hdhomerun invalid handle." ));
+ return -1;
+ }
+
+ ce->state = 1;
+ ce->ch = vchannel;
+ ce->frq = 0;
+ ce->av_inf[0] = 0x0;
+ char stream_tar[32];
+ snprintf( ce->ch_name, sizeof(ce->ch_name), "%d", vchannel );
+ snprintf( ce->dev_tuning, sizeof(ce->dev_tuning), "vchan:%d", vchannel );
+ snprintf( stream_tar, sizeof(stream_tar), "udp://%s:%u", CHANNE_SCAN_SRC, CHANNEL_SCAN_PORT );
+ ret = tune_channel( device, ce->dev_tuning, "", stream_tar );
+ if ( ret > 0 )
+ ret = parse_avinf( CHANNE_SCAN_SRC, CHANNEL_SCAN_PORT, ce->av_inf, sizeof(ce->av_inf), PARSE_TIMEOUT );
+ if ( ret > 0 )
+ ret = strstr( ce->av_inf, "ENCRYPTED" )==NULL && strstr( ce->av_inf, "NO-DATA" )==NULL ;
+ if ( ret <= 0 ) {
+ ce->state = 0;
+ return 0;
+ }
+
+ return 1;
+}
+
+static int _make_channel_entry_buffer( struct channel_entry_t *ce, char *buf, int size )
+{
+ int pos = 0;
+ pos += snprintf( buf+pos, size-pos, "ch:%03d ", ce->ch );
+ if ( ce->ch_name[0] )
+ pos += snprintf( buf+pos, size-pos, "name:%s ", ce->ch_name );
+ if ( ce->src_ip[0] && ce->src_port )
+ pos += snprintf( buf+pos, size-pos, "ip:%s port:%d ", ce->src_ip, ce->src_port);
+ if ( ce->epg_id[0] )
+ pos += snprintf( buf+pos, size-pos, "epg:%s ", ce->epg_id );
+ if ( ce->dev_name[0] )
+ pos += snprintf( buf+pos, size-pos, "dev:%s ", ce->dev_name );
+ if ( ce->dev_tuning[0] )
+ pos += snprintf( buf+pos, size-pos, "tune:%s ", ce->dev_tuning );
+ if ( ce->av_inf[0] )
+ pos += snprintf( buf+pos, size-pos, "av:\"%s\" ", ce->av_inf );
+ pos += snprintf( buf+pos, size-pos, "\n" );
+ return pos;
+}
+
+static int _save_channel_entry( char* file_name, struct channel_entry_t *ce, int num )
+{
+ int i;
+ char buf[2048];
+ FILE *fp;
+ fp = fopen( file_name, "w" );
+ if ( fp == NULL ) {
+ //printf( "Can't open file %s to save channel table, errno:%d\n", file_name, errno );
+ return -1;
+ }
+ fputs( "#Google sagetv channel table ver 1.0., maxium 2048 bytes per line\n", fp );
+ fprintf( fp, "total_channel:%d\n", num );
+ for ( i = 0; i<num; i++ ) {
+ _make_channel_entry_buffer( ce, buf, sizeof(buf) );
+ fputs( buf, fp );
+ ce++;
+ }
+ fclose( fp );
+ return 1;
+}
+
+int main( int argc, char* argv[] )
+{
+ int i, ret = 0 ;
+ int tuner_count = 0;
+ struct hdhr_tuner_t ht[16];
+
+ ret = discover_device( ht, 16 );
+ printf( "found tuners:%d\n", ret );
+ if ( ret > 0 ) {
+ for ( i = 0; i<ret; i++ ) {
+ //if a unit shares a source input in a device, copy data.
+ if ( i> 0 && tuner_input_sharing( &ht[i-1], &ht[i] ) ) {
+ ht[i].cc_tuner = ht[i-1].cc_tuner;
+ strncpy( ht[i].model, ht[i-1].model, sizeof(ht[i].model) );
+ } else
+ get_dev_model( &ht[i] );
+
+ printf( "%d tuner:%s ip:%s type:%d cc:%d model:%s\n", i, ht[i].name, ht[i].ip, ht[i].type, ht[i].cc_tuner, ht[i].model );
+ }
+ tuner_count = ret;
+ }
+
+ // test channel scan
+ if ( 0 ) {
+ time_t t0 = time( NULL );
+ struct channel_entry_t ce[400];
+ int channel_num = 0;
+ //channel_num = scan_all( "101007FD-0", ce, 400 );
+ for ( i = 60; i<=70; i++ ) {
+ ret = scan_channel( "101007FD-1", i, ce, channel_num, 400 );
+ if ( ret < 0 || sigabort_flag )
+ break;
+ printf( "find channe:%d\n", ret );
+ channel_num += ret;
+ }
+ printf( "time:%ld\n", time( NULL) - t0 );
+ _save_channel_entry( "channel-scan.txt", ce, channel_num );
+ }
+
+ /*
+ //test vchannel scan
+ if ( 1 ) {
+ struct hdhr_tuner_t *cc_ht = NULL;
+ for ( i = 0; i<tuner_count; i++ ) {
+ if ( ht[i].cc_tuner ) {
+ cc_ht = &ht[i];
+ break;
+ }
+ }
+
+
+ if ( cc_ht != NULL ) {
+ time_t t0 = time( NULL );
+ struct vchan_tbl_t * vt = create_vchan_tbl( cc_ht );
+ ret = get_vchannel_list( vt );
+ if ( ret > 0 ) {
+ int n;
+ int channel_num = 0;
+ struct channel_list_t *cl = create_channel_list( ret );
+ for ( n = 1; n<vt->channel_num && channel_num < 500 ; n++ ) {
+ printf( "TRACE %d %s\n", n, vt->vchan[n].guide_name );
+ ret = scan_vchannel( vt, n, cl );
+ if ( ret > 0 ) {
+ struct channel_entry_t *ce_p = &cl->ce[channel_num];
+ snprintf( ce_p->dev_name, sizeof(ce_p->dev_name), "HDHR.%s", device, sizeof(ce_p->dev_name) );
+ channel_num++;
+ }
+ printf( "TRACE channel %d, %d\n", channel_num, n );
+ }
+ _save_channel_entry( "channel-scan.txt", cl );
+ }
+ release_vchan_tbl( vt );
+ printf( "time:%ld\n", time( NULL) - t0 );
+ }
+ }
+ */
+
+ //test tune
+ if ( 1 ) {
+ //printf( "tuning %s \n", device[i] );
+ char *dev_name = "1311A273-0"; //"101007FD-1";
+ //ret = tune_channel( dev_name, "map:us-cable|auto:68", "343", "udp://226.0.0.1:4096" );
+ ret = tune_channel( dev_name, "vchan:600", "", "udp://226.0.0.1:4096" );
+ printf( "hit enter to stop\n" );
+ getchar();
+ ret = stop_channel( dev_name );
+ }
+
+ return ret;
+}
+#endif
diff --git a/hdhomerun_tuner.h b/hdhomerun_tuner.h
new file mode 100644
index 0000000..2ffe6db
--- /dev/null
+++ b/hdhomerun_tuner.h
@@ -0,0 +1,46 @@
+
+
+#ifndef _HDHOMERUN_TUNER_H_
+#define _HDHOMERUN_TUNER_H_
+
+struct hdhr_tuner_t {
+ void* hd;
+ int tuner_id; //unique id in a channel service
+ char tuner_name[20]; //external name used by plugin in a channel service
+ char name[16]; //internal used by hdhomerun device
+ char ip[16]; //device unit ip
+ char model[16]; //device model
+ int type; //device type 1:atsc 2:qam 3:dvb
+ int cc_tuner; //cable card tuner
+ void* channel_table; //channel scan private data
+};
+
+struct vchan_t {
+ int guide_id;
+ char guide_name[32];
+};
+
+struct vchan_tbl_t{
+ struct hdhr_tuner_t ht;
+ int channel_num;
+ int channel_size;
+ struct vchan_t *vchan;
+};
+
+struct channel_entry_t;
+int discover_device( struct hdhr_tuner_t *ht, int max_ht_num );
+//get unit name of a tuner, one unit contains 1-6 tuners
+void* open_hdhr( char* name );
+void close_hdhr( void* device );
+char* get_unit_name( struct hdhr_tuner_t *ht, char* name, int size );
+int scan_all( void* device, struct channel_entry_t *ce, int ce_num );
+int tuner_input_sharing( struct hdhr_tuner_t *ht1, struct hdhr_tuner_t *ht2 );
+int tune_channel( void* device, char *channel, char *program, char* stream_target );
+int stop_channel( void* device );
+int scan_vchannel( void* device, int vchannel, struct channel_entry_t *ce );
+int tuner_input_sharing( struct hdhr_tuner_t *ht1, struct hdhr_tuner_t *ht2 );
+struct vchan_tbl_t* create_vchan_tbl( struct hdhr_tuner_t *ht );
+void release_vchan_tbl( struct vchan_tbl_t *vt );
+int grow_vhcan_tbl( struct vchan_tbl_t *vt );
+
+#endif
diff --git a/sagelog.c b/sagelog.c
new file mode 100644
index 0000000..101d412
--- /dev/null
+++ b/sagelog.c
@@ -0,0 +1,105 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+// Author: qianzhang@google.com (ken Zhang)
+
+#include <stdio.h>
+#include <time.h>
+#include <stdlib.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include "sagelog.h"
+
+int sagelog_enabled=1;
+int console_enabled = 1;
+int trace_level = 3;
+char log_filename[128]="/tmp/SageEncoder.log";
+
+void _enable_native_log( )
+{
+ sagelog_enabled=1;
+}
+void _disable_native_log( )
+{
+ sagelog_enabled=0;
+}
+
+void _set_native_log( int level )
+{
+ if ( level == 0 ) {
+ sagelog_enabled = 0;
+ printf( "sagelog is disabled." );
+ }
+ else
+ {
+ sagelog_enabled = 1;
+ trace_level = level;
+ printf( "sagelog is enabled, \"%s\"\n", log_filename );
+ }
+}
+
+void _sagelog( int type, int level, const char* cstr, ... )
+{
+ time_t ct;
+ struct tm ltm;
+ va_list args;
+ char szInfo[1024*3];
+ va_list va;
+ FILE* fp;
+
+ if ( !sagelog_enabled ) return;
+ if ( type == _LOG_TRACE && level > trace_level ) return;
+
+ fp = fopen( log_filename, "a" );
+ if ( fp == NULL ) return;
+ time(&ct); localtime_r( &ct, <m );
+
+
+ va_start(va, cstr);
+
+ snprintf( szInfo, sizeof(szInfo)-3, TEXT("%02d/%02d/%d %02d:%02d:%02d "), ltm.tm_mon+1, ltm.tm_mday, ltm.tm_year+1900,
+ ltm.tm_hour, ltm.tm_min, ltm.tm_sec );
+
+ vsnprintf(szInfo + lstrlen(szInfo), (int)sizeof(szInfo)-lstrlen(szInfo)-3, cstr, va);
+ lstrcat(szInfo, TEXT("\r\n"));
+ fwrite( (char*)szInfo, 1, lstrlen(szInfo), fp );
+
+ if ( console_enabled )
+ printf( "%s", szInfo );
+
+ va_end(args);
+ fclose( fp );
+}
+
+void _flog_setup( char* filename, int enable )
+{
+ if ( filename != NULL && filename[0] )
+ strncpy( log_filename, filename, sizeof( log_filename ) );
+
+ sagelog_enabled = enable;
+}
+
+char* _get_flog_path( )
+{
+ return log_filename;
+}
+
+int _get_flog_state( )
+{
+ return sagelog_enabled;
+}
+
+int _flog_check()
+{
+ FILE* fp = fopen( "NATIVE_LOG.ENABLE", "r" );
+ if ( fp != NULL )
+ {
+ sagelog_enabled = 1;
+ fclose( fp );
+ return 1;
+ }
+ return 0;
+}
+
+
+
+
diff --git a/sagelog.h b/sagelog.h
new file mode 100644
index 0000000..f482629
--- /dev/null
+++ b/sagelog.h
@@ -0,0 +1,52 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+// Author: qianzhang@google.com (ken Zhang)
+
+
+#ifndef _SAGELOG_H_
+#define _SAGELOG_H_
+
+#include <assert.h>
+
+enum { _LOG_TIMING = 0x01, // Timing and performance measurements
+ _LOG_TRACE = 0x02, // General step point call tracing
+ _LOG_MEMORY = 0x04, // Memory and object allocation/destruction
+ _LOG_LOCKING = 0x08, // Locking/unlocking of critical sections
+ _LOG_ERROR = 0x10, // Debug error notification
+ _LOG_CUSTOM1 = 0x20,
+ _LOG_CUSTOM2 = 0x40,
+};
+
+#define _MIN(x,y) ((x)>(y)? (y):(x))
+#define _MAX(x,y) ((x)>(y)? (x):(y))
+
+
+#ifndef TEXT
+#define TEXT( x ) x
+#endif
+
+#define snprintf snprintf
+#define vsnprintf vsnprintf
+
+#ifndef lstrlen
+#define lstrlen strlen
+#endif
+
+#ifndef lstrcat
+#define lstrcat strcat
+#endif
+
+#ifndef ASSERT
+#define ASSERT assert
+#endif
+
+#define sage_log( x ) _sagelog x
+
+void _sagelog( int type, int level, const char* cstr, ... );
+void _flog_setup( char* filename, int enable );
+char* _get_flog_path( );
+int _get_flog_state( );
+int _flog_check();
+void _set_native_log( int level );
+void _enable_native_log( );
+void _disable_native_log();
+#endif /* SAGELOG_H_ */
diff --git a/utility.c b/utility.c
new file mode 100644
index 0000000..24f77e2
--- /dev/null
+++ b/utility.c
@@ -0,0 +1,241 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+// Author: qianzhang@google.com (ken Zhang)
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "channel_buffer.h"
+
+#include "utility.h"
+#define sage_log(x) _sagelog x
+void _sagelog( int type, int level, const char* cstr, ... );
+
+static const char* delimiter = " \t,;\r\n:=|'\"#";
+
+static int is_delimiter( char ch )
+{
+ int i;
+ if ( ch == 0 ) return 1;
+ for ( i = 0; delimiter[i]; i++ )
+ if ( ch == delimiter[i] ) return 1;
+ return 0;
+}
+
+//format name:zzz (or name=zzzz)
+int get_string_by_name( char*p, const char* name, char* string, int max_len, char **next )
+{
+ char *s, *e;
+ e = p;
+
+ while ( e != NULL && *e ) {
+ s = strstr( e, name );
+ if ( s == NULL )
+ return 0;
+ //is a token name
+ if ( ((s > p && is_delimiter(*(s-1))) || s == p ) && is_delimiter( *(s+strlen(name)) ) ) {
+ s += (int)strlen(name);
+ while( *s && ( *s == ' ' || *s == '\t' ) )
+ s++; //skip white space
+ if ( *s == '=' || *s == ':' ) {
+ s++;
+ while( *s && ( *s == ' ' || *s == '\t' ) )
+ s++; //skip white space
+ if ( *s ) {
+ int i=0;
+ while ( i++<max_len && *s && *s != ' ' && *s != '\t' && *s != '\n' && *s != '\r')
+ *string++ = *s++;
+
+ *string = 0x0; //terminator
+ if ( next != NULL ) {
+ while ( *s && *s != ' ' && *s != '\t' && *s != '\n' && *s != '\r')
+ s++;
+ *next = s;
+ }
+ return 1;
+ }
+ }
+ }
+ e = ++s;
+ }
+ return 0;
+}
+
+//format name:zzz (or name=zzzz)
+int get_string_by_token( char*p, int num, char* string, int max_len, char **next )
+{
+ int count = 0;
+ char *s;
+ s = p;
+ if ( s == NULL )
+ return 0;
+ while ( *s ) {
+ while( *s && ( *s == ' ' || *s == '\t' || *s == '\r' || *s == '\n' ) )
+ s++; //skip white space
+ if ( *s == 0 )
+ break;
+
+ if ( count++ != num ) {
+ while( *s && !( *s == ' ' || *s == '\t' || *s == ';' || *s == ',' || *s == '\r' || *s == '\n' ) )
+ s++; //skip a token
+ } else {
+ if ( *s ) {
+ int i=0;
+ while ( i++<max_len && *s && !( *s == ' ' || *s == '\t' || *s == ';' || *s == ',' || *s == '\r' || *s == '\n' ) )
+ *string++ = *s++;
+
+ *string = 0x0; //terminator
+ if ( next != NULL ) {
+ while ( *s && !( *s == ' ' || *s == '\t' || *s == '\r' || *s == '\n' ) )
+ s++;
+ *next = s;
+ }
+ return 1;
+ }
+ }
+ if ( *s )
+ s++;
+ }
+ return 0;
+}
+
+//format name:"zzz" (or name="zzzz")
+int get_quote_string_by_name( char*p, const char* name, char* string, int max_len, char **next )
+{
+ char *s, *e;
+ e = p;
+
+ while ( e != NULL && *e ) {
+ s = strstr( e, name );
+ if ( s == NULL ) return 0;
+ //is a token name
+ if ( ((s > p && is_delimiter(*(s-1))) || s == p ) && is_delimiter( *(s+strlen(name)) ) ) {
+ s += (int)strlen(name);
+ while( *s && ( *s == ' ' || *s == '\t' ) )
+ s++; //skip white space
+ if ( *s == '=' || *s == ':' ) {
+ s++;
+ while( *s && ( *s == ' ' || *s == '\t' ) )
+ s++; //skip white space
+ if ( *s == '\"' ) {
+ int i=0;
+ s++;
+ while ( i++<max_len && *s && *s != '\"' )
+ *string++ = *s++;
+
+ *string = 0x0; //terminator
+
+ if ( next != NULL) {
+ while ( *s && *s != '\"' )
+ s++;
+ *next = s+1;
+ }
+ return 1;
+ } else
+ return 0;
+ }
+ }
+ e = ++s;
+ }
+ return 0;
+}
+
+int get_int_val_by_name( char*p, const char* name, int* val, char** next )
+{
+ char *s, *e;
+ e = p;
+ while( e != NULL && *e ) {
+ s = strstr( e, name );
+ if ( s == NULL )
+ return 0;
+ //is a token name
+ if ( ( (s > p && is_delimiter(*(s-1)) ) || s == p ) && is_delimiter( *(s+strlen(name)) ) ) {
+ s += strlen(name);
+ while( *s && ( *s == ' ' || *s == '\t' ) )
+ s++; //skip white space
+ if ( *s == '=' || *s == ':' ) {
+ s++;
+ while( *s && ( *s == ' ' || *s == '\t' ) )
+ s++; //skip white space
+ if ( *s ) {
+ *val = atoi( s );
+ if (*val < 0 && *s == '-' )
+ s++;
+ while( *s >= '0' && *s <= '9')
+ s++;
+ if ( next != NULL )
+ *next = s;
+ return 1;
+ }
+ }
+ }
+ e = ++s;
+ }
+ return 0;
+}
+struct channel_list_t* _create_channel_list( int num )
+{
+ if ( num <= 0 )
+ return NULL;
+ struct channel_list_t *cl = malloc( sizeof( struct channel_list_t));
+ if ( cl == NULL )
+ return NULL;
+ cl->num = num;
+ cl->channel_num = 0;
+ cl->ce = malloc( sizeof(struct channel_entry_t)*num );
+ if ( cl->ce == NULL ) {
+ free( cl );
+ return NULL;
+ }
+ memset( cl->ce, 0x0, sizeof(struct channel_entry_t)*num );
+ return cl;
+}
+
+void _release_channel_list( struct channel_list_t *cl )
+{
+ if ( cl ) {
+ if ( cl->ce )
+ free( cl->ce );
+ free( cl );
+ }
+}
+
+struct channel_list_t* _joint_channel_list( struct channel_list_t* cl1, struct channel_list_t* cl2 )
+{
+ int i, n, channel_num;
+ if ( cl1 == NULL )
+ return cl2;
+ if ( cl2 == NULL )
+ return cl1;
+
+ channel_num = cl1->channel_num + cl2->channel_num;
+ if ( channel_num < cl1->num ) {
+ n = 0;
+ for ( i = cl1->channel_num; i<channel_num; i++ )
+ cl1->ce[i] = cl2->ce[n++];
+ cl2->channel_num = channel_num;
+ _release_channel_list( cl2 );
+ return cl1;
+ } else if ( channel_num < cl2->num ) {
+ n = 0;
+ for ( i = cl2->channel_num; i<channel_num; i++ )
+ cl2->ce[i] = cl1->ce[n++];
+ cl2->channel_num = channel_num;
+ _release_channel_list( cl1 );
+ return cl2;
+ } else {
+ struct channel_list_t* cl = _create_channel_list( channel_num );
+ for ( i = 0; i<cl1->channel_num; i++ )
+ cl->ce[i] = cl1->ce[i];
+ for ( n = 0; n<cl2->channel_num; n++ )
+ cl->ce[i++] = cl2->ce[n];
+ _release_channel_list( cl1 );
+ _release_channel_list( cl2 );
+ cl->channel_num = channel_num;
+ return cl;
+ }
+ return NULL;
+}
+
+
+
diff --git a/utility.h b/utility.h
new file mode 100644
index 0000000..5ce0b67
--- /dev/null
+++ b/utility.h
@@ -0,0 +1,15 @@
+
+
+#ifndef _UTILITY_H_
+#define _UTILITY_H_
+
+int get_string_by_name( char*p, const char* name, char* string, int max_len, char **next );
+int get_string_by_token( char*p, int num, char* string, int max_len, char **next );
+int get_quote_string_by_name( char*p, const char* name, char* string, int max_len, char **next );
+int get_int_val_by_name( char*p, const char* name, int* val, char** next );
+
+struct channel_list_t* _create_channel_list( int num );
+void _release_channel_list( struct channel_list_t *cl );
+struct channel_list_t* _joint_channel_list( struct channel_list_t* cl1, struct channel_list_t* cl2 );
+
+#endif