Add HDHomeRun OTA tuner support

Change-Id: Ief7fdb5a223529596f4981c081d2466ad18c891c
diff --git a/hdhomerun_plugin.c b/hdhomerun_plugin.c
old mode 100755
new mode 100644
index 573edf1..8cca2da
--- a/hdhomerun_plugin.c
+++ b/hdhomerun_plugin.c
@@ -45,9 +45,11 @@
 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) );
+  struct device_list *devices;
+
+  sage_log((_LOG_TRACE, 3, "hdhr:hdhomerun device init"));
+
+  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) );
@@ -59,15 +61,14 @@
     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 );
+      snprintf(devices->hdhrs[i].tuner_name,
+               sizeof(devices->hdhrs[i].tuner_name), "%s-%s",
+               devices->hdhrs[i].model, 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":"" ));
+      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;
@@ -105,7 +106,6 @@
   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;
 }
@@ -134,8 +134,9 @@
     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 ) {
+    /* HDHomeRun PRIME */
     if ( strstr( channel, "vchan:" ) == NULL ) {
       char vchan[32];
       snprintf( vchan, sizeof(vchan), "vchan:%d", atoi(channel) );
@@ -143,11 +144,31 @@
     } 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;
+    /* HDHomeRun US */
+    char tune_str[32], map_str[32], prog_str[8];
+    int ch = -1, prog = -1, vch_major = -1, vch_minor = -1;
     char *ps, *pe;
     map_str[0] = 0x0;
+
+    /*
+     * channel format: xx-yy-zz
+     * xx=physical channel, yy=virtual major channel, zz=virtual minor channel
+     */
+    if (sscanf(channel, "%d-%d-%d", &ch, &vch_major, &vch_minor) == 3) {
+      ret = tune_vchannel(hdhr->hd, ch, vch_major, vch_minor, stream_tar);
+      goto out;
+    }
+
+    /*
+     * channel format: xx-yy
+     * xx=physical channel, yy=virtual major channel
+     */
+    if (sscanf(channel, "%d-%d", &ch, &vch_major) == 2) {
+      vch_minor = 0;
+      ret = tune_vchannel(hdhr->hd, ch, vch_major, vch_minor, stream_tar);
+      goto out;
+    }
+
     //if channel in format "map:xxx-yy|auto:zzz prog:ppp"
     if ( ( ps = strstr( channel, "map:" ))!= NULL &&
        ( pe = strchr( ps+4, '|' )) != NULL ) {
@@ -164,8 +185,7 @@
     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" );
+        snprintf( map_str, sizeof(map_str), "map:us-bcast" );
       }
     }
     if ( map_str[0] && ch > 0 && prog > 0 ) {
@@ -177,6 +197,8 @@
       return 0;
     }
   }
+
+out:
   sage_log(( _LOG_TRACE, 3, "hdhr:hdhomerun tune %s %s.", (char*)channel, ret>0?"successful":"failed" ));
   return ret > 0;
 }
@@ -224,7 +246,8 @@
       *cl_p = cl;
     return ret;
   } else {
-    //TODO
+    sage_log((_LOG_TRACE, 3, "hdhr:hdhomerun ATSC dev_scan not implemented %s.",
+              (char*)hdhr->name));
   }
 
   return 0;
@@ -256,8 +279,9 @@
     }
     release_vchan_tbl( vt );
     return cl;
-  } else { //TODO
-
+  } else {
+    sage_log((_LOG_TRACE, 3, "hdhr:hdhomerun ATSC build_hdhr_channel_table not implemented %s.",
+              (char*)hdhr->name));
   }
   return NULL;
 }
diff --git a/hdhomerun_tuner.c b/hdhomerun_tuner.c
index ae837b9..3dd1a0d 100644
--- a/hdhomerun_tuner.c
+++ b/hdhomerun_tuner.c
@@ -50,6 +50,7 @@
   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;
@@ -65,7 +66,6 @@
   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 ) {
@@ -88,11 +88,6 @@
       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 );
@@ -111,17 +106,128 @@
   }
 
   char target[64];
-  snprintf( target, sizeof(target), "%s ttl-2",stream_target );
+  snprintf( target, sizeof(target), "%s ttl=2", 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;
 }
 
+int vchan_to_prog_num(char *streaminfo, unsigned int vchan_major,
+                      unsigned int vchan_minor, unsigned int *prog_num)
+{
+  char *line;
+  char *next_line;
+  unsigned int program_number;
+  unsigned int virtual_major, virtual_minor;
+
+  next_line = streaminfo;
+
+  while (1) {
+    line = next_line;
+    next_line = strchr(line, '\n');
+    if (!next_line) {
+      break;
+    }
+    *next_line++ = 0;
+
+    if (sscanf(line, "%u: %u.%u", &program_number, &virtual_major, &virtual_minor) != 3) {
+      if (sscanf(line, "%u: %u", &program_number, &virtual_major) != 2) {
+         continue;
+      }
+      virtual_minor = 0;
+    }
+
+    if (vchan_major == virtual_major && vchan_minor == virtual_minor) {
+      *prog_num = program_number;
+      return 1;
+    }
+  }
+
+  return 0;
+}
+
+int tune_vchannel(void *device, unsigned int channel,
+                  unsigned int vchannel_major, unsigned int vchannel_minor,
+                  char *stream_target)
+{
+  char channel_map[16];
+  char channel_str[16];
+  char program_str[16];
+  char target_str[64];
+  struct hdhomerun_device_t *hd;
+  struct hdhomerun_tuner_status_t ts;
+  char *streaminfo;
+  unsigned int program;
+  int i, ret;
+
+  hd = (struct hdhomerun_device_t *)device;
+
+  /* Set tuner channel map to US broadcast */
+  snprintf(channel_map, sizeof(channel_map), "us-bcast");
+  ret = hdhomerun_device_set_tuner_channelmap(hd, channel_map);
+  if (ret < 0) {
+    printf("hdhr:ERROR: set tuner channel map %s\n", channel_map);
+    goto out;
+  }
+
+  /* Tune to physical channel */
+  snprintf(channel_str, sizeof(channel_str), "auto:%u", channel);
+  ret = hdhomerun_device_set_tuner_channel(hd, channel_str);
+  if (ret <= 0) {
+    printf("hdhr:ERROR: set tuner channel %s\n", channel_str);
+    goto out;
+  }
+
+  /* Wait for SER to hit 100% (max 3 secs) */
+  usleep(500000);
+  for (i = 0; i < 10; i++) {
+    ret = hdhomerun_device_get_tuner_status(hd, NULL, &ts);
+    if (ret > 0 && ts.symbol_error_quality >= 100) {
+      break;
+    }
+    usleep(250000);
+  }
+
+  /* Get MPEG2-TS stream info */
+  ret = hdhomerun_device_get_tuner_streaminfo(hd, &streaminfo);
+  if (ret <= 0) {
+    printf("hdhr:ERROR: get tuner streaminfo\n");
+    goto out;
+  }
+  printf("hdhr:hdhomerun streaminfo:\n%s", streaminfo);
+
+  /* Translate virtual channel (major.minor) to program number */
+  ret = vchan_to_prog_num(streaminfo, vchannel_major, vchannel_minor, &program);
+  if (ret <= 0) {
+    printf("hdhr:ERROR: vchannel %d.%d not found in streaminfo\n",
+           vchannel_major, vchannel_minor);
+    goto out;
+  }
+  printf("hdhr:hdhomerun vchannel %u.%u -> program %u\n",
+         vchannel_major, vchannel_minor, program);
+
+  /* Set tuner program */
+  snprintf(program_str, sizeof(program_str), "%u", program);
+  ret = hdhomerun_device_set_tuner_program(hd, program_str);
+  if (ret <= 0) {
+    printf("hdhr:ERROR: set tuner program %s\n", program_str);
+    goto out;
+  }
+
+  /* Set tuner stream target */
+  snprintf(target_str, sizeof(target_str), "%s ttl=2", stream_target);
+  ret = hdhomerun_device_set_tuner_target(hd, target_str);
+  if (ret <= 0) {
+    printf("hdhr:ERROR: set tuner target %s\n", target_str);
+  }
+
+out:
+  return ret;
+}
+
 //channel format: map:us-cable,auto:68
 int stop_channel( void* device )
 {
diff --git a/hdhomerun_tuner.h b/hdhomerun_tuner.h
index 2ffe6db..0a3a5ef 100644
--- a/hdhomerun_tuner.h
+++ b/hdhomerun_tuner.h
@@ -36,6 +36,9 @@
 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 tune_vchannel(void *device, unsigned int channel,
+                  unsigned int vchannel_major, unsigned int vchannel_minor,
+                  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 );