| // 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; |
| 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)); |
| 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++) { |
| devices->hdhrs[i].tuner_id = index++; |
| 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" : "")); |
| } |
| 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->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; |
| } |
| |
| if (hdhr->cc_tuner) { |
| // HDHomeRun PRIME |
| 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 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) { |
| 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) { |
| snprintf(map_str, sizeof(map_str), "map:us-bcast"); |
| } |
| } |
| 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; |
| } |
| } |
| |
| out: |
| 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 { |
| sage_log((_LOG_TRACE, 3, "hdhr:hdhomerun ATSC dev_scan not implemented %s.", |
| (char *)hdhr->name)); |
| } |
| |
| 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 { |
| sage_log( |
| (_LOG_TRACE, 3, |
| "hdhr:hdhomerun ATSC build_hdhr_channel_table not implemented %s.", |
| (char *)hdhr->name)); |
| } |
| 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; |
| } |