| // 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 "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; |
| } |
| |
| |