blob: 9edf07643d867f57dc3866817f600833b12c9df4 [file] [log] [blame]
// 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;
};
void *dev_init(int *num)
{
int i;
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);
if (devices->hdhrs == NULL) {
free(devices);
return NULL;
}
memset(devices->hdhrs, 0,
sizeof(sizeof(struct hdhr_tuner_t) * MAX_TUNER_NUM));
devices->list_num = MAX_TUNER_NUM;
devices->tuner_num = discover_devices(devices->hdhrs, MAX_TUNER_NUM);
for (i = 0; i < devices->tuner_num; i++) {
sage_log((_LOG_TRACE, 3, "hdhr:%d %s %s %s %s", i,
devices->hdhrs[i].tuner_name, devices->hdhrs[i].model,
devices->hdhrs[i].cc_tuner ? "CableCard" : "ATSC",
devices->hdhrs[i].firmware));
}
*num = devices->tuner_num;
}
return devices;
}
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
int ch = -1;
// channel format: xx
// xx=Comcast virtual channel
if (sscanf(channel, "%d", &ch) == 1) {
ret = tune_cable_vchannel(hdhr->hd, ch, stream_tar);
goto out;
}
// other channel formats
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_atsc_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_atsc_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;
}