116 changes: 47 additions & 69 deletions mythtv/libs/libmythhdhomerun/hdhomerun_control.c
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
* hdhomerun_control.c
*
* Copyright © 2006 Silicondust USA Inc. <www.silicondust.com>.
* Copyright © 2006-2010 Silicondust USA Inc. <www.silicondust.com>.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
Expand Down Expand Up @@ -32,29 +32,30 @@

#include "hdhomerun.h"

#define HDHOMERUN_CONTROL_SEND_TIMEOUT 5000
#define HDHOMERUN_CONTROL_RECV_TIMEOUT 5000
#define HDHOMERUN_CONTROL_CONNECT_TIMEOUT 2500
#define HDHOMERUN_CONTROL_SEND_TIMEOUT 2500
#define HDHOMERUN_CONTROL_RECV_TIMEOUT 2500
#define HDHOMERUN_CONTROL_UPGRADE_TIMEOUT 20000

struct hdhomerun_control_sock_t {
uint32_t desired_device_id;
uint32_t desired_device_ip;
uint32_t actual_device_id;
uint32_t actual_device_ip;
int sock;
hdhomerun_sock_t sock;
struct hdhomerun_debug_t *dbg;
struct hdhomerun_pkt_t tx_pkt;
struct hdhomerun_pkt_t rx_pkt;
};

static void hdhomerun_control_close_sock(struct hdhomerun_control_sock_t *cs)
{
if (cs->sock == -1) {
if (cs->sock == HDHOMERUN_SOCK_INVALID) {
return;
}

close(cs->sock);
cs->sock = -1;
hdhomerun_sock_destroy(cs->sock);
cs->sock = HDHOMERUN_SOCK_INVALID;
}

void hdhomerun_control_set_device(struct hdhomerun_control_sock_t *cs, uint32_t device_id, uint32_t device_ip)
Expand All @@ -76,7 +77,7 @@ struct hdhomerun_control_sock_t *hdhomerun_control_create(uint32_t device_id, ui
}

cs->dbg = dbg;
cs->sock = -1;
cs->sock = HDHOMERUN_SOCK_INVALID;
hdhomerun_control_set_device(cs, device_id, device_ip);

return cs;
Expand All @@ -90,14 +91,18 @@ void hdhomerun_control_destroy(struct hdhomerun_control_sock_t *cs)

static bool_t hdhomerun_control_connect_sock(struct hdhomerun_control_sock_t *cs)
{
if (cs->sock != -1) {
if (cs->sock != HDHOMERUN_SOCK_INVALID) {
return TRUE;
}

if ((cs->desired_device_id == 0) && (cs->desired_device_ip == 0)) {
hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_connect_sock: no device specified\n");
return FALSE;
}
if (hdhomerun_discover_is_ip_multicast(cs->desired_device_ip)) {
hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_connect_sock: cannot use multicast ip address for device operations\n");
return FALSE;
}

/* Find device. */
struct hdhomerun_discover_device_t result;
Expand All @@ -109,24 +114,15 @@ static bool_t hdhomerun_control_connect_sock(struct hdhomerun_control_sock_t *cs
cs->actual_device_id = result.device_id;

/* Create socket. */
cs->sock = (int)socket(AF_INET, SOCK_STREAM, 0);
if (cs->sock == -1) {
hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_connect_sock: failed to create socket (%d)\n", sock_getlasterror);
cs->sock = hdhomerun_sock_create_tcp();
if (cs->sock == HDHOMERUN_SOCK_INVALID) {
hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_connect_sock: failed to create socket (%d)\n", hdhomerun_sock_getlasterror());
return FALSE;
}

/* Set timeouts. */
setsocktimeout(cs->sock, SOL_SOCKET, SO_SNDTIMEO, HDHOMERUN_CONTROL_SEND_TIMEOUT);
setsocktimeout(cs->sock, SOL_SOCKET, SO_RCVTIMEO, HDHOMERUN_CONTROL_RECV_TIMEOUT);

/* Initiate connection. */
struct sockaddr_in sock_addr;
memset(&sock_addr, 0, sizeof(sock_addr));
sock_addr.sin_family = AF_INET;
sock_addr.sin_addr.s_addr = htonl(cs->actual_device_ip);
sock_addr.sin_port = htons(HDHOMERUN_CONTROL_TCP_PORT);
if (connect(cs->sock, (struct sockaddr *)&sock_addr, sizeof(sock_addr)) != 0) {
hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_connect_sock: failed to connect (%d)\n", sock_getlasterror);
if (!hdhomerun_sock_connect(cs->sock, cs->actual_device_ip, HDHOMERUN_CONTROL_TCP_PORT, HDHOMERUN_CONTROL_CONNECT_TIMEOUT)) {
hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_connect_sock: failed to connect (%d)\n", hdhomerun_sock_getlasterror());
hdhomerun_control_close_sock(cs);
return FALSE;
}
Expand Down Expand Up @@ -172,76 +168,58 @@ uint32_t hdhomerun_control_get_local_addr(struct hdhomerun_control_sock_t *cs)
return 0;
}

struct sockaddr_in sock_addr;
socklen_t sockaddr_size = sizeof(sock_addr);
if (getsockname(cs->sock, (struct sockaddr*)&sock_addr, &sockaddr_size) != 0) {
hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_get_local_addr: getsockname failed (%d)\n", sock_getlasterror);
uint32_t addr = hdhomerun_sock_getsockname_addr(cs->sock);
if (addr == 0) {
hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_get_local_addr: getsockname failed (%d)\n", hdhomerun_sock_getlasterror());
return 0;
}

return ntohl(sock_addr.sin_addr.s_addr);
return addr;
}

static int hdhomerun_control_send_sock(struct hdhomerun_control_sock_t *cs, struct hdhomerun_pkt_t *tx_pkt)
static bool_t hdhomerun_control_send_sock(struct hdhomerun_control_sock_t *cs, struct hdhomerun_pkt_t *tx_pkt)
{
int length = (int)(tx_pkt->end - tx_pkt->start);
if (send(cs->sock, (char *)tx_pkt->start, (int)length, 0) != length) {
hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_send_sock: send failed (%d)\n", sock_getlasterror);
if (!hdhomerun_sock_send(cs->sock, tx_pkt->start, tx_pkt->end - tx_pkt->start, HDHOMERUN_CONTROL_SEND_TIMEOUT)) {
hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_send_sock: send failed (%d)\n", hdhomerun_sock_getlasterror());
hdhomerun_control_close_sock(cs);
return -1;
return FALSE;
}

return 1;
return TRUE;
}

static int hdhomerun_control_recv_sock(struct hdhomerun_control_sock_t *cs, struct hdhomerun_pkt_t *rx_pkt, uint16_t *ptype, uint64_t recv_timeout)
static bool_t hdhomerun_control_recv_sock(struct hdhomerun_control_sock_t *cs, struct hdhomerun_pkt_t *rx_pkt, uint16_t *ptype, uint64_t recv_timeout)
{
uint64_t stop_time = getcurrenttime() + recv_timeout;
hdhomerun_pkt_reset(rx_pkt);

while (getcurrenttime() < stop_time) {
struct timeval t;
t.tv_sec = 0;
t.tv_usec = 250000;

fd_set readfds;
FD_ZERO(&readfds);
FD_SET(cs->sock, &readfds);

if (select(cs->sock+1, &readfds, NULL, NULL, &t) < 0) {
hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_recv_sock: select failed (%d)\n", sock_getlasterror);
while (1) {
uint64_t current_time = getcurrenttime();
if (current_time >= stop_time) {
hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_recv_sock: timeout\n");
hdhomerun_control_close_sock(cs);
return -1;
}

if (!FD_ISSET(cs->sock, &readfds)) {
continue;
return FALSE;
}
int rx_length = recv(cs->sock, (char *)rx_pkt->end, (int)(rx_pkt->limit - rx_pkt->end), 0);
if (rx_length <= 0) {
hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_recv_sock: recv failed (%d)\n", sock_getlasterror);

size_t length = rx_pkt->limit - rx_pkt->end;
if (!hdhomerun_sock_recv(cs->sock, rx_pkt->end, &length, stop_time - current_time)) {
hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_recv_sock: recv failed (%d)\n", hdhomerun_sock_getlasterror());
hdhomerun_control_close_sock(cs);
return -1;
return FALSE;
}
rx_pkt->end += rx_length;

rx_pkt->end += length;

int ret = hdhomerun_pkt_open_frame(rx_pkt, ptype);
if (ret < 0) {
hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_recv_sock: frame error\n");
hdhomerun_control_close_sock(cs);
return -1;
return FALSE;
}
if (ret == 0) {
continue;
if (ret > 0) {
return TRUE;
}

return 1;
}

hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_recv_sock: timeout\n");
hdhomerun_control_close_sock(cs);
return -1;
}

static int hdhomerun_control_send_recv_internal(struct hdhomerun_control_sock_t *cs, struct hdhomerun_pkt_t *tx_pkt, struct hdhomerun_pkt_t *rx_pkt, uint16_t type, uint64_t recv_timeout)
Expand All @@ -250,22 +228,22 @@ static int hdhomerun_control_send_recv_internal(struct hdhomerun_control_sock_t

int i;
for (i = 0; i < 2; i++) {
if (cs->sock == -1) {
if (cs->sock == HDHOMERUN_SOCK_INVALID) {
if (!hdhomerun_control_connect_sock(cs)) {
hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_send_recv: connect failed\n");
return -1;
}
}

if (hdhomerun_control_send_sock(cs, tx_pkt) < 0) {
if (!hdhomerun_control_send_sock(cs, tx_pkt)) {
continue;
}
if (!rx_pkt) {
return 1;
}

uint16_t rsp_type;
if (hdhomerun_control_recv_sock(cs, rx_pkt, &rsp_type, recv_timeout) < 0) {
if (!hdhomerun_control_recv_sock(cs, rx_pkt, &rsp_type, recv_timeout)) {
continue;
}
if (rsp_type != type + 1) {
Expand Down
2 changes: 1 addition & 1 deletion mythtv/libs/libmythhdhomerun/hdhomerun_control.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
* hdhomerun_control.h
*
* Copyright © 2006 Silicondust USA Inc. <www.silicondust.com>.
* Copyright © 2006 Silicondust USA Inc. <www.silicondust.com>.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
Expand Down
60 changes: 25 additions & 35 deletions mythtv/libs/libmythhdhomerun/hdhomerun_debug.c
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
* hdhomerun_debug.c
*
* Copyright © 2006 Silicondust USA Inc. <www.silicondust.com>.
* Copyright © 2006-2010 Silicondust USA Inc. <www.silicondust.com>.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
Expand Down Expand Up @@ -44,9 +44,13 @@
#define HDHOMERUN_DEBUG_HOST "debug.silicondust.com"
#endif
#if !defined(HDHOMERUN_DEBUG_PORT)
#define HDHOMERUN_DEBUG_PORT "8002"
#define HDHOMERUN_DEBUG_PORT 8002
#endif

#define HDHOMERUN_DEBUG_CONNECT_RETRY_TIME 30000
#define HDHOMERUN_DEBUG_CONNECT_TIMEOUT 10000
#define HDHOMERUN_DEBUG_SEND_TIMEOUT 10000

struct hdhomerun_debug_message_t
{
struct hdhomerun_debug_message_t *next;
Expand All @@ -73,7 +77,7 @@ struct hdhomerun_debug_t

char *file_name;
FILE *file_fp;
int sock;
hdhomerun_sock_t sock;
};

static THREAD_FUNC_PREFIX hdhomerun_debug_thread_execute(void *arg);
Expand All @@ -85,7 +89,7 @@ struct hdhomerun_debug_t *hdhomerun_debug_create(void)
return NULL;
}

dbg->sock = -1;
dbg->sock = HDHOMERUN_SOCK_INVALID;

pthread_mutex_init(&dbg->print_lock, NULL);
pthread_mutex_init(&dbg->queue_lock, NULL);
Expand Down Expand Up @@ -117,8 +121,8 @@ void hdhomerun_debug_destroy(struct hdhomerun_debug_t *dbg)
if (dbg->file_fp) {
fclose(dbg->file_fp);
}
if (dbg->sock != -1) {
close(dbg->sock);
if (dbg->sock != HDHOMERUN_SOCK_INVALID) {
hdhomerun_sock_destroy(dbg->sock);
}

free(dbg);
Expand All @@ -132,9 +136,9 @@ static void hdhomerun_debug_close_internal(struct hdhomerun_debug_t *dbg)
dbg->file_fp = NULL;
}

if (dbg->sock != -1) {
close(dbg->sock);
dbg->sock = -1;
if (dbg->sock != HDHOMERUN_SOCK_INVALID) {
hdhomerun_sock_destroy(dbg->sock);
dbg->sock = HDHOMERUN_SOCK_INVALID;
}
}

Expand Down Expand Up @@ -251,7 +255,7 @@ void hdhomerun_debug_flush(struct hdhomerun_debug_t *dbg, uint64_t timeout)
return;
}

msleep(10);
msleep_approx(10);
}
}

Expand Down Expand Up @@ -372,54 +376,40 @@ static bool_t hdhomerun_debug_output_message_file(struct hdhomerun_debug_t *dbg,
}

/* Send lock held by caller */
#if defined(__CYGWIN__)
static bool_t hdhomerun_debug_output_message_sock(struct hdhomerun_debug_t *dbg, struct hdhomerun_debug_message_t *message)
{
return TRUE;
}
#else
static bool_t hdhomerun_debug_output_message_sock(struct hdhomerun_debug_t *dbg, struct hdhomerun_debug_message_t *message)
{
if (dbg->sock == -1) {
if (dbg->sock == HDHOMERUN_SOCK_INVALID) {
uint64_t current_time = getcurrenttime();
if (current_time < dbg->connect_delay) {
return FALSE;
}
dbg->connect_delay = current_time + 30*1000;
dbg->connect_delay = current_time + HDHOMERUN_DEBUG_CONNECT_RETRY_TIME;

dbg->sock = (int)socket(AF_INET, SOCK_STREAM, 0);
if (dbg->sock == -1) {
dbg->sock = hdhomerun_sock_create_tcp();
if (dbg->sock == HDHOMERUN_SOCK_INVALID) {
return FALSE;
}

struct addrinfo hints;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;

struct addrinfo *sock_info;
if (getaddrinfo(HDHOMERUN_DEBUG_HOST, HDHOMERUN_DEBUG_PORT, &hints, &sock_info) != 0) {
uint32_t remote_addr = hdhomerun_sock_getaddrinfo_addr(dbg->sock, HDHOMERUN_DEBUG_HOST);
if (remote_addr == 0) {
hdhomerun_debug_close_internal(dbg);
return FALSE;
}
if (connect(dbg->sock, sock_info->ai_addr, (int)sock_info->ai_addrlen) != 0) {
freeaddrinfo(sock_info);

if (!hdhomerun_sock_connect(dbg->sock, remote_addr, HDHOMERUN_DEBUG_PORT, HDHOMERUN_DEBUG_CONNECT_TIMEOUT)) {
hdhomerun_debug_close_internal(dbg);
return FALSE;
}
freeaddrinfo(sock_info);
}

size_t length = strlen(message->buffer);
if (send(dbg->sock, (char *)message->buffer, (int)length, 0) != length) {
if (!hdhomerun_sock_send(dbg->sock, message->buffer, length, HDHOMERUN_DEBUG_SEND_TIMEOUT)) {
hdhomerun_debug_close_internal(dbg);
return FALSE;
}

return TRUE;
}
#endif

static bool_t hdhomerun_debug_output_message(struct hdhomerun_debug_t *dbg, struct hdhomerun_debug_message_t *message)
{
Expand Down Expand Up @@ -466,7 +456,7 @@ static THREAD_FUNC_PREFIX hdhomerun_debug_thread_execute(void *arg)
pthread_mutex_unlock(&dbg->queue_lock);

if (!message) {
msleep(250);
msleep_approx(250);
continue;
}

Expand All @@ -476,7 +466,7 @@ static THREAD_FUNC_PREFIX hdhomerun_debug_thread_execute(void *arg)
}

if (!hdhomerun_debug_output_message(dbg, message)) {
msleep(250);
msleep_approx(250);
continue;
}

Expand Down
2 changes: 1 addition & 1 deletion mythtv/libs/libmythhdhomerun/hdhomerun_debug.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
* hdhomerun_debug.h
*
* Copyright © 2006 Silicondust USA Inc. <www.silicondust.com>.
* Copyright © 2006 Silicondust USA Inc. <www.silicondust.com>.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
Expand Down
470 changes: 382 additions & 88 deletions mythtv/libs/libmythhdhomerun/hdhomerun_device.c

Large diffs are not rendered by default.

31 changes: 9 additions & 22 deletions mythtv/libs/libmythhdhomerun/hdhomerun_device.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
* hdhomerun_device.h
*
* Copyright © 2006-2008 Silicondust USA Inc. <www.silicondust.com>.
* Copyright © 2006-2008 Silicondust USA Inc. <www.silicondust.com>.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
Expand Down Expand Up @@ -51,8 +51,7 @@ extern "C" {
* GUI feedback to the user of the selected tuner might use 5 device objects: 4 for streaming video
* (one per thread) and one for the GUI display that can switch between tuners.
*
* This function will not attempt to connect to the device.
* The connection will be established when first used.
* This function will not attempt to connect to the device. The connection will be established when first used.
*
* uint32_t device_id = 32-bit device id of device. Set to HDHOMERUN_DEVICE_ID_WILDCARD to match any device ID.
* uint32_t device_ip = IP address of device. Set to 0 to auto-detect.
Expand All @@ -68,8 +67,8 @@ extern "C" {
* <device id>
* <device id>-<tuner index>
* <ip address>
* If the tuner index is not included in the device_str then it is set to zero.
* Use hdhomerun_device_set_tuner or hdhomerun_device_set_tuner_from_str to set the tuner.
* If the tuner index is not included in the device_str then it is set to zero. Use hdhomerun_device_set_tuner
* or hdhomerun_device_set_tuner_from_str to set the tuner.
*
* The hdhomerun_device_set_tuner_from_str function sets the tuner from the given tuner_str.
* The tuner_str parameter can be any of the following forms:
Expand All @@ -90,8 +89,8 @@ extern LIBTYPE uint32_t hdhomerun_device_get_device_id_requested(struct hdhomeru
extern LIBTYPE uint32_t hdhomerun_device_get_device_ip_requested(struct hdhomerun_device_t *hd);
extern LIBTYPE unsigned int hdhomerun_device_get_tuner(struct hdhomerun_device_t *hd);

extern LIBTYPE void hdhomerun_device_set_device(struct hdhomerun_device_t *hd, uint32_t device_id, uint32_t device_ip);
extern LIBTYPE void hdhomerun_device_set_tuner(struct hdhomerun_device_t *hd, unsigned int tuner);
extern LIBTYPE int hdhomerun_device_set_device(struct hdhomerun_device_t *hd, uint32_t device_id, uint32_t device_ip);
extern LIBTYPE int hdhomerun_device_set_tuner(struct hdhomerun_device_t *hd, unsigned int tuner);
extern LIBTYPE int hdhomerun_device_set_tuner_from_str(struct hdhomerun_device_t *hd, const char *tuner_str);

/*
Expand Down Expand Up @@ -126,6 +125,7 @@ extern LIBTYPE int hdhomerun_device_get_tuner_lockkey_owner(struct hdhomerun_dev
extern LIBTYPE int hdhomerun_device_get_ir_target(struct hdhomerun_device_t *hd, char **ptarget);
extern LIBTYPE int hdhomerun_device_get_lineup_location(struct hdhomerun_device_t *hd, char **plocation);
extern LIBTYPE int hdhomerun_device_get_version(struct hdhomerun_device_t *hd, char **pversion_str, uint32_t *pversion_num);
extern LIBTYPE int hdhomerun_device_get_supported(struct hdhomerun_device_t *hd, char *prefix, char **pstr);

extern LIBTYPE uint32_t hdhomerun_device_get_tuner_status_ss_color(struct hdhomerun_tuner_status_t *status);
extern LIBTYPE uint32_t hdhomerun_device_get_tuner_status_snq_color(struct hdhomerun_tuner_status_t *status);
Expand All @@ -148,10 +148,9 @@ extern LIBTYPE int hdhomerun_device_set_tuner_filter(struct hdhomerun_device_t *
extern LIBTYPE int hdhomerun_device_set_tuner_filter_by_array(struct hdhomerun_device_t *hd, unsigned char filter_array[0x2000]);
extern LIBTYPE int hdhomerun_device_set_tuner_program(struct hdhomerun_device_t *hd, const char *program);
extern LIBTYPE int hdhomerun_device_set_tuner_target(struct hdhomerun_device_t *hd, const char *target);
extern LIBTYPE int hdhomerun_device_set_tuner_target_to_local_protocol(struct hdhomerun_device_t *hd, const char *protocol);
extern LIBTYPE int hdhomerun_device_set_tuner_target_to_local(struct hdhomerun_device_t *hd);
extern LIBTYPE int hdhomerun_device_set_ir_target(struct hdhomerun_device_t *hd, const char *target);
extern LIBTYPE int hdhomerun_device_set_lineup_location(struct hdhomerun_device_t *hd, const char *location);
extern LIBTYPE int hdhomerun_device_set_sys_dvbc_modulation(struct hdhomerun_device_t *hd, const char *modulation_list);

/*
* Get/set a named control variable on the device.
Expand Down Expand Up @@ -219,12 +218,11 @@ extern LIBTYPE int hdhomerun_device_wait_for_lock(struct hdhomerun_device_t *hd,
* Returns -1 if a communication error occurs.
*
* The hdhomerun_device_stream_recv function should be called periodically to receive the stream data.
* The buffer can losslessly store 1 second of data, however a more typical call rate would be every 64ms.
* The buffer can losslessly store 1 second of data, however a more typical call rate would be every 15ms.
*
* The hdhomerun_device_stream_stop function tells the device to stop streaming data.
*/
extern LIBTYPE int hdhomerun_device_stream_start(struct hdhomerun_device_t *hd);
extern LIBTYPE int hdhomerun_device_stream_refresh_target(struct hdhomerun_device_t *hd);
extern LIBTYPE uint8_t *hdhomerun_device_stream_recv(struct hdhomerun_device_t *hd, size_t max_size, size_t *pactual_size);
extern LIBTYPE void hdhomerun_device_stream_flush(struct hdhomerun_device_t *hd);
extern LIBTYPE void hdhomerun_device_stream_stop(struct hdhomerun_device_t *hd);
Expand All @@ -237,17 +235,6 @@ extern LIBTYPE int hdhomerun_device_channelscan_advance(struct hdhomerun_device_
extern LIBTYPE int hdhomerun_device_channelscan_detect(struct hdhomerun_device_t *hd, struct hdhomerun_channelscan_result_t *result);
extern LIBTYPE uint8_t hdhomerun_device_channelscan_get_progress(struct hdhomerun_device_t *hd);

/*
* Check that the device is running the recommended firmware.
*
* uint32_t features: Reserved for future use. Set to zero.
*
* Returns 1 if the firmware meets the minimum requriements for all operations.
* Returns 0 if th firmware does not meet the minimum requriements for all operations.
* Returns -1 if an error occurs.
*/
extern LIBTYPE int hdhomerun_device_firmware_version_check(struct hdhomerun_device_t *hd, uint32_t features);

/*
* Upload new firmware to the device.
*
Expand Down
36 changes: 19 additions & 17 deletions mythtv/libs/libmythhdhomerun/hdhomerun_device_selector.c
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
* hdhomerun_device_selector.c
*
* Copyright © 2009 Silicondust USA Inc. <www.silicondust.com>.
* Copyright © 2009-2010 Silicondust USA Inc. <www.silicondust.com>.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
Expand Down Expand Up @@ -68,6 +68,11 @@ void hdhomerun_device_selector_destroy(struct hdhomerun_device_selector_t *hds,
free(hds);
}

LIBTYPE int hdhomerun_device_selector_get_device_count(struct hdhomerun_device_selector_t *hds)
{
return (int)hds->hd_count;
}

void hdhomerun_device_selector_add_device(struct hdhomerun_device_selector_t *hds, struct hdhomerun_device_t *hd)
{
size_t index;
Expand Down Expand Up @@ -129,11 +134,11 @@ struct hdhomerun_device_t *hdhomerun_device_selector_find_device(struct hdhomeru
return NULL;
}

void hdhomerun_device_selector_load_from_file(struct hdhomerun_device_selector_t *hds, char *filename)
int hdhomerun_device_selector_load_from_file(struct hdhomerun_device_selector_t *hds, char *filename)
{
FILE *fp = fopen(filename, "r");
if (!fp) {
return;
return 0;
}

while(1) {
Expand All @@ -151,16 +156,17 @@ void hdhomerun_device_selector_load_from_file(struct hdhomerun_device_selector_t
}

fclose(fp);
return (int)hds->hd_count;
}

#if defined(__WINDOWS__)
void hdhomerun_device_selector_load_from_windows_registry(struct hdhomerun_device_selector_t *hds, wchar_t *wsource)
int hdhomerun_device_selector_load_from_windows_registry(struct hdhomerun_device_selector_t *hds, wchar_t *wsource)
{
HKEY tuners_key;
LONG ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Silicondust\\HDHomeRun\\Tuners", 0, KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS, &tuners_key);
if (ret != ERROR_SUCCESS) {
hdhomerun_debug_printf(hds->dbg, "hdhomerun_device_selector_load_from_windows_registry: failed to open tuners registry key (%ld)\n", ret);
return;
hdhomerun_debug_printf(hds->dbg, "hdhomerun_device_selector_load_from_windows_registry: failed to open tuners registry key (%ld)\n", (long)ret);
return 0;
}

DWORD index = 0;
Expand All @@ -177,7 +183,7 @@ void hdhomerun_device_selector_load_from_windows_registry(struct hdhomerun_devic
HKEY device_key;
ret = RegOpenKeyEx(tuners_key, wdevice_name, 0, KEY_QUERY_VALUE, &device_key);
if (ret != ERROR_SUCCESS) {
hdhomerun_debug_printf(hds->dbg, "hdhomerun_device_selector_load_from_windows_registry: failed to open registry key for %S (%ld)\n", wdevice_name, ret);
hdhomerun_debug_printf(hds->dbg, "hdhomerun_device_selector_load_from_windows_registry: failed to open registry key for %S (%ld)\n", wdevice_name, (long)ret);
continue;
}

Expand Down Expand Up @@ -207,6 +213,7 @@ void hdhomerun_device_selector_load_from_windows_registry(struct hdhomerun_devic
}

RegCloseKey(tuners_key);
return (int)hds->hd_count;
}
#endif

Expand Down Expand Up @@ -268,21 +275,16 @@ static bool_t hdhomerun_device_selector_choose_test(struct hdhomerun_device_sele
/*
* Test local port.
*/
int test_sock = (int)socket(AF_INET, SOCK_DGRAM, 0);
if (test_sock == -1) {
hdhomerun_sock_t test_sock = hdhomerun_sock_create_udp();
if (test_sock == HDHOMERUN_SOCK_INVALID) {
hdhomerun_debug_printf(hds->dbg, "hdhomerun_device_selector_choose_test: device %s in use, failed to create test sock\n", name);
return FALSE;
}

struct sockaddr_in sock_addr;
memset(&sock_addr, 0, sizeof(sock_addr));
sock_addr.sin_family = AF_INET;
sock_addr.sin_addr.s_addr = htonl(INADDR_ANY);
sock_addr.sin_port = htons((uint16_t)target_port);
ret = bind(test_sock, (struct sockaddr *)&sock_addr, sizeof(sock_addr));
close(test_sock);
bool_t inuse = (hdhomerun_sock_bind(test_sock, INADDR_ANY, (uint16_t)target_port) == FALSE);
hdhomerun_sock_destroy(test_sock);

if (ret != 0) {
if (inuse) {
hdhomerun_debug_printf(hds->dbg, "hdhomerun_device_selector_choose_test: device %s in use by local machine\n", name);
return FALSE;
}
Expand Down
10 changes: 8 additions & 2 deletions mythtv/libs/libmythhdhomerun/hdhomerun_device_selector.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,18 @@ extern "C" {
extern LIBTYPE struct hdhomerun_device_selector_t *hdhomerun_device_selector_create(struct hdhomerun_debug_t *dbg);
extern LIBTYPE void hdhomerun_device_selector_destroy(struct hdhomerun_device_selector_t *hds, bool_t destroy_devices);

/*
* Get the number of devices in the list.
*/
extern LIBTYPE int hdhomerun_device_selector_get_device_count(struct hdhomerun_device_selector_t *hds);

/*
* Populate device selector with devices from given source.
* Returns the number of devices populated.
*/
extern LIBTYPE void hdhomerun_device_selector_load_from_file(struct hdhomerun_device_selector_t *hds, char *filename);
extern LIBTYPE int hdhomerun_device_selector_load_from_file(struct hdhomerun_device_selector_t *hds, char *filename);
#if defined(__WINDOWS__)
extern LIBTYPE void hdhomerun_device_selector_load_from_windows_registry(struct hdhomerun_device_selector_t *hds, wchar_t *wsource);
extern LIBTYPE int hdhomerun_device_selector_load_from_windows_registry(struct hdhomerun_device_selector_t *hds, wchar_t *wsource);
#endif

/*
Expand Down
252 changes: 0 additions & 252 deletions mythtv/libs/libmythhdhomerun/hdhomerun_dhcp.c
Original file line number Diff line number Diff line change
@@ -1,252 +0,0 @@
/*
* hdhomerun_dhcp.c
*
* Copyright © 2006-2007 Silicondust USA Inc. <www.silicondust.com>.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* As a special exception to the GNU Lesser General Public License,
* you may link, statically or dynamically, an application with a
* publicly distributed version of the Library to produce an
* executable file containing portions of the Library, and
* distribute that executable file under terms of your choice,
* without any of the additional requirements listed in clause 4 of
* the GNU Lesser General Public License.
*
* By "a publicly distributed version of the Library", we mean
* either the unmodified Library as distributed by Silicondust, or a
* modified version of the Library that is distributed under the
* conditions defined in the GNU Lesser General Public License.
*/

#include "hdhomerun.h"
#include "hdhomerun_dhcp.h"

struct dhcp_hdr_t {
uint8_t bootp_message_type;
uint8_t hardware_type;
uint8_t hardware_address_length;
uint8_t hops;
uint32_t transaction_id;
uint16_t seconds_elapsed;
uint16_t bootp_flags;
uint32_t client_ip;
uint32_t your_ip;
uint32_t next_server_ip;
uint32_t relay_agent_ip;
uint8_t client_mac[16];
uint8_t server_host_name[64];
uint8_t boot_file_name[128];
uint32_t magic_cookie;
};

struct hdhomerun_dhcp_t {
int sock;
uint32_t local_address;
pthread_t thread;
volatile bool_t terminate;
};

static THREAD_FUNC_PREFIX hdhomerun_dhcp_thread_execute(void *arg);

struct hdhomerun_dhcp_t *hdhomerun_dhcp_create(uint32_t bind_address)
{
if (bind_address != 0) {
if ((bind_address & 0xFFFF0000) != 0xA9FE0000) {
return NULL;
}
}

/* Create socket. */
int sock = (int)socket(AF_INET, SOCK_DGRAM, 0);
if (sock == -1) {
return NULL;
}

/* Set timeout. */
setsocktimeout(sock, SOL_SOCKET, SO_RCVTIMEO, 1000);

/* Allow broadcast. */
int sock_opt = 1;
setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char *)&sock_opt, sizeof(sock_opt));

/* Allow reuse. */
sock_opt = 1;
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&sock_opt, sizeof(sock_opt));

/* Bind socket. */
struct sockaddr_in sock_addr;
memset(&sock_addr, 0, sizeof(sock_addr));
sock_addr.sin_family = AF_INET;
sock_addr.sin_addr.s_addr = htonl(bind_address);
sock_addr.sin_port = htons(67);
if (bind(sock, (struct sockaddr *)&sock_addr, sizeof(sock_addr)) != 0) {
close(sock);
return NULL;
}

/* Allocate object. */
struct hdhomerun_dhcp_t *dhcp = (struct hdhomerun_dhcp_t *)calloc(1, sizeof(struct hdhomerun_dhcp_t));
if (!dhcp) {
close(sock);
return NULL;
}

dhcp->sock = sock;

if (bind_address != 0) {
dhcp->local_address = bind_address;
} else {
dhcp->local_address = 0xA9FEFFFF;
}

/* Spawn thread. */
if (pthread_create(&dhcp->thread, NULL, &hdhomerun_dhcp_thread_execute, dhcp) != 0) {
close(sock);
free(dhcp);
return NULL;
}

/* Success. */
return dhcp;
}

void hdhomerun_dhcp_destroy(struct hdhomerun_dhcp_t *dhcp)
{
dhcp->terminate = TRUE;
pthread_join(dhcp->thread, NULL);

close(dhcp->sock);
free(dhcp);
}

static void hdhomerun_dhcp_send(struct hdhomerun_dhcp_t *dhcp, uint8_t message_type, struct hdhomerun_pkt_t *pkt)
{
pkt->pos = pkt->start;
struct dhcp_hdr_t *hdr = (struct dhcp_hdr_t *)pkt->pos;
pkt->pos += sizeof(struct dhcp_hdr_t);
pkt->end = pkt->pos;

uint32_t remote_addr = 0xA9FE0000;
remote_addr |= (uint32_t)hdr->client_mac[4] << 8;
remote_addr |= (uint32_t)hdr->client_mac[5] << 0;
if ((remote_addr == 0xA9FE0000) || (remote_addr == 0xA9FEFFFF)) {
remote_addr = 0xA9FE8080;
}

hdr->bootp_message_type = 0x02;
hdr->your_ip = htonl(remote_addr);
hdr->next_server_ip = htonl(0x00000000);

hdhomerun_pkt_write_u8(pkt, 53);
hdhomerun_pkt_write_u8(pkt, 1);
hdhomerun_pkt_write_u8(pkt, message_type);

hdhomerun_pkt_write_u8(pkt, 54);
hdhomerun_pkt_write_u8(pkt, 4);
hdhomerun_pkt_write_u32(pkt, dhcp->local_address);

hdhomerun_pkt_write_u8(pkt, 51);
hdhomerun_pkt_write_u8(pkt, 4);
hdhomerun_pkt_write_u32(pkt, 7*24*60*60);

hdhomerun_pkt_write_u8(pkt, 1);
hdhomerun_pkt_write_u8(pkt, 4);
hdhomerun_pkt_write_u32(pkt, 0xFFFF0000);

hdhomerun_pkt_write_u8(pkt, 0xFF);

while (pkt->pos < pkt->start + 300) {
hdhomerun_pkt_write_u8(pkt, 0x00);
}

struct sockaddr_in sock_addr;
memset(&sock_addr, 0, sizeof(sock_addr));
sock_addr.sin_family = AF_INET;
sock_addr.sin_addr.s_addr = htonl(0xFFFFFFFF);
sock_addr.sin_port = htons(68);

sendto(dhcp->sock, (char *)pkt->start, (int)(pkt->end - pkt->start), 0, (struct sockaddr *)&sock_addr, sizeof(sock_addr));
}

static void hdhomerun_dhcp_recv(struct hdhomerun_dhcp_t *dhcp, struct hdhomerun_pkt_t *pkt)
{
pkt->pos = pkt->start;
struct dhcp_hdr_t *hdr = (struct dhcp_hdr_t *)pkt->pos;
pkt->pos += sizeof(struct dhcp_hdr_t);
if (pkt->pos > pkt->end) {
return;
}

if (ntohl(hdr->magic_cookie) != 0x63825363) {
return;
}

static uint8_t vendor[3] = {0x00, 0x18, 0xDD};
if (memcmp(hdr->client_mac, vendor, 3) != 0) {
return;
}

if (pkt->pos + 3 > pkt->end) {
return;
}
if (hdhomerun_pkt_read_u8(pkt) != 53) {
return;
}
if (hdhomerun_pkt_read_u8(pkt) != 1) {
return;
}
uint8_t message_type_val = hdhomerun_pkt_read_u8(pkt);

switch (message_type_val) {
case 0x01:
hdhomerun_dhcp_send(dhcp, 0x02, pkt);
break;
case 0x03:
hdhomerun_dhcp_send(dhcp, 0x05, pkt);
break;
default:
return;
}
}

static THREAD_FUNC_PREFIX hdhomerun_dhcp_thread_execute(void *arg)
{
struct hdhomerun_dhcp_t *dhcp = (struct hdhomerun_dhcp_t *)arg;
struct hdhomerun_pkt_t pkt_inst;

while (1) {
if (dhcp->terminate) {
return NULL;
}

struct hdhomerun_pkt_t *pkt = &pkt_inst;
hdhomerun_pkt_reset(pkt);

int rx_length = recv(dhcp->sock, (char *)pkt->end, (int)(pkt->limit - pkt->end), 0);
if (rx_length <= 0) {
if (!sock_getlasterror_socktimeout) {
#if defined(__WINDOWS__)
msleep(1000);
#else
sleep(1);
#endif
}
continue;
}
pkt->end += rx_length;

hdhomerun_dhcp_recv(dhcp, pkt);
}
}
44 changes: 0 additions & 44 deletions mythtv/libs/libmythhdhomerun/hdhomerun_dhcp.h
Original file line number Diff line number Diff line change
@@ -1,44 +0,0 @@
/*
* hdhomerun_dhcp.h
*
* Copyright © 2006 Silicondust USA Inc. <www.silicondust.com>.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* As a special exception to the GNU Lesser General Public License,
* you may link, statically or dynamically, an application with a
* publicly distributed version of the Library to produce an
* executable file containing portions of the Library, and
* distribute that executable file under terms of your choice,
* without any of the additional requirements listed in clause 4 of
* the GNU Lesser General Public License.
*
* By "a publicly distributed version of the Library", we mean
* either the unmodified Library as distributed by Silicondust, or a
* modified version of the Library that is distributed under the
* conditions defined in the GNU Lesser General Public License.
*/

#ifdef __cplusplus
extern "C" {
#endif

struct hdhomerun_dhcp_t;

extern LIBTYPE struct hdhomerun_dhcp_t *hdhomerun_dhcp_create(uint32_t bind_address);
extern LIBTYPE void hdhomerun_dhcp_destroy(struct hdhomerun_dhcp_t *dhcp);

#ifdef __cplusplus
}
#endif
255 changes: 120 additions & 135 deletions mythtv/libs/libmythhdhomerun/hdhomerun_discover.c

Large diffs are not rendered by default.

23 changes: 20 additions & 3 deletions mythtv/libs/libmythhdhomerun/hdhomerun_discover.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
* hdhomerun_discover.h
*
* Copyright © 2006-2007 Silicondust USA Inc. <www.silicondust.com>.
* Copyright © 2006-2007 Silicondust USA Inc. <www.silicondust.com>.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
Expand Down Expand Up @@ -44,15 +44,24 @@ struct hdhomerun_discover_device_t {
*
* The device information is stored in caller-supplied array of hdhomerun_discover_device_t vars.
* Multiple attempts are made to find devices.
* Execution time is 1 second.
* Execution time is typically 400ms if max_count is not reached.
*
* Set target_ip to zero to auto-detect IP address.
* Set target_ip to zero to auto-detect the IP address.
* Set device_type to HDHOMERUN_DEVICE_TYPE_TUNER to detect HDHomeRun tuner devices.
* Set device_id to HDHOMERUN_DEVICE_ID_WILDCARD to detect all device ids.
*
* Returns the number of devices found.
* Retruns -1 on error.
*/
extern LIBTYPE int hdhomerun_discover_find_devices_custom(uint32_t target_ip, uint32_t device_type, uint32_t device_id, struct hdhomerun_discover_device_t result_list[], int max_count);

/*
* Optional: persistent discover instance available for discover polling use.
*/
extern LIBTYPE struct hdhomerun_discover_t *hdhomerun_discover_create(void);
extern LIBTYPE void hdhomerun_discover_destroy(struct hdhomerun_discover_t *ds);
extern LIBTYPE int hdhomerun_discover_find_devices(struct hdhomerun_discover_t *ds, uint32_t target_ip, uint32_t device_type, uint32_t device_id, struct hdhomerun_discover_device_t result_list[], int max_count);

/*
* Verify that the device ID given is valid.
*
Expand All @@ -64,6 +73,14 @@ extern LIBTYPE int hdhomerun_discover_find_devices_custom(uint32_t target_ip, ui
*/
extern LIBTYPE bool_t hdhomerun_discover_validate_device_id(uint32_t device_id);

/*
* Detect if an IP address is multicast.
*
* Returns TRUE if multicast.
* Returns FALSE if zero, unicast, expermental, or broadcast.
*/
extern LIBTYPE bool_t hdhomerun_discover_is_ip_multicast(uint32_t ip_addr);

#ifdef __cplusplus
}
#endif
2 changes: 1 addition & 1 deletion mythtv/libs/libmythhdhomerun/hdhomerun_os.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
* hdhomerun_os.h
*
* Copyright © 2006-2008 Silicondust USA Inc. <www.silicondust.com>.
* Copyright © 2006-2008 Silicondust USA Inc. <www.silicondust.com>.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
Expand Down
89 changes: 89 additions & 0 deletions mythtv/libs/libmythhdhomerun/hdhomerun_os_posix.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*
* hdhomerun_os_posix.c
*
* Copyright © 2006-2010 Silicondust USA Inc. <www.silicondust.com>.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* As a special exception to the GNU Lesser General Public License,
* you may link, statically or dynamically, an application with a
* publicly distributed version of the Library to produce an
* executable file containing portions of the Library, and
* distribute that executable file under terms of your choice,
* without any of the additional requirements listed in clause 4 of
* the GNU Lesser General Public License.
*
* By "a publicly distributed version of the Library", we mean
* either the unmodified Library as distributed by Silicondust, or a
* modified version of the Library that is distributed under the
* conditions defined in the GNU Lesser General Public License.
*/

#include "hdhomerun_os.h"

uint64_t getcurrenttime(void)
{
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
static uint64_t result = 0;
static uint64_t previous_time = 0;

pthread_mutex_lock(&lock);

#if defined(CLOCK_MONOTONIC)
struct timespec tp;
clock_gettime(CLOCK_MONOTONIC, &tp);
uint64_t current_time = ((uint64_t)tp.tv_sec * 1000) + (tp.tv_nsec / 1000000);
#else
struct timeval t;
gettimeofday(&t, NULL);
uint64_t current_time = ((uint64_t)t.tv_sec * 1000) + (t.tv_usec / 1000);
#endif

if (current_time > previous_time) {
result += current_time - previous_time;
}

previous_time = current_time;

pthread_mutex_unlock(&lock);
return result;
}

void msleep_approx(uint64_t ms)
{
unsigned int delay_s = ms / 1000;
if (delay_s > 0) {
sleep(delay_s);
ms -= delay_s * 1000;
}

unsigned int delay_us = ms * 1000;
if (delay_us > 0) {
usleep(delay_us);
}
}

void msleep_minimum(uint64_t ms)
{
uint64_t stop_time = getcurrenttime() + ms;

while (1) {
uint64_t current_time = getcurrenttime();
if (current_time >= stop_time) {
return;
}

msleep_approx(stop_time - current_time);
}
}
47 changes: 11 additions & 36 deletions mythtv/libs/libmythhdhomerun/hdhomerun_os_posix.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
* hdhomerun_os_posix.h
*
* Copyright © 2006-2008 Silicondust USA Inc. <www.silicondust.com>.
* Copyright © 2006-2010 Silicondust USA Inc. <www.silicondust.com>.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
Expand Down Expand Up @@ -38,58 +38,33 @@
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/timeb.h>
#include <sys/wait.h>
#include <sys/signal.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <pthread.h>

typedef int bool_t;
typedef void (*sig_t)(int);

#define LIBTYPE
#define sock_getlasterror errno
#define sock_getlasterror_socktimeout (errno == EAGAIN)
#define console_vprintf vprintf
#define console_printf printf
#define THREAD_FUNC_PREFIX void *

static inline uint64_t getcurrenttime(void)
{
struct timeval t;
gettimeofday(&t, NULL);
return ((uint64_t)t.tv_sec * 1000) + (t.tv_usec / 1000);
}

static inline int msleep(unsigned int ms)
{
uint64_t stop_time = getcurrenttime() + ms;

while (1) {
uint64_t current_time = getcurrenttime();
if (current_time >= stop_time) {
return 0;
}
#ifdef __cplusplus
extern "C" {
#endif

uint64_t delay_s = (stop_time - current_time) / 1000;
if (delay_s > 0) {
sleep((unsigned int)delay_s);
continue;
}

uint64_t delay_us = (stop_time - current_time) * 1000;
usleep((unsigned int)delay_us);
}
}
extern LIBTYPE uint64_t getcurrenttime(void);
extern LIBTYPE void msleep_approx(uint64_t ms);
extern LIBTYPE void msleep_minimum(uint64_t ms);

static inline int setsocktimeout(int s, int level, int optname, uint64_t timeout)
{
struct timeval t;
t.tv_sec = timeout / 1000;
t.tv_usec = (timeout % 1000) * 1000;
return setsockopt(s, level, optname, (char *)&t, sizeof(t));
#ifdef __cplusplus
}
#endif
135 changes: 135 additions & 0 deletions mythtv/libs/libmythhdhomerun/hdhomerun_os_windows.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
/*
* hdhomerun_os_windows.c
*
* Copyright © 2006-2010 Silicondust USA Inc. <www.silicondust.com>.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* As a special exception to the GNU Lesser General Public License,
* you may link, statically or dynamically, an application with a
* publicly distributed version of the Library to produce an
* executable file containing portions of the Library, and
* distribute that executable file under terms of your choice,
* without any of the additional requirements listed in clause 4 of
* the GNU Lesser General Public License.
*
* By "a publicly distributed version of the Library", we mean
* either the unmodified Library as distributed by Silicondust, or a
* modified version of the Library that is distributed under the
* conditions defined in the GNU Lesser General Public License.
*/

#include "hdhomerun_os.h"

uint64_t getcurrenttime(void)
{
static pthread_mutex_t lock = INVALID_HANDLE_VALUE;
static uint64_t result = 0;
static uint32_t previous_time = 0;

/* Initialization is not thread safe. */
if (lock == INVALID_HANDLE_VALUE) {
pthread_mutex_init(&lock, NULL);
}

pthread_mutex_lock(&lock);

uint32_t current_time = GetTickCount();

if (current_time > previous_time) {
result += current_time - previous_time;
}

previous_time = current_time;

pthread_mutex_unlock(&lock);
return result;
}

void msleep_approx(uint64_t ms)
{
Sleep((DWORD)ms);
}

void msleep_minimum(uint64_t ms)
{
uint64_t stop_time = getcurrenttime() + ms;

while (1) {
uint64_t current_time = getcurrenttime();
if (current_time >= stop_time) {
return;
}

msleep_approx(stop_time - current_time);
}
}

int pthread_create(pthread_t *tid, void *attr, LPTHREAD_START_ROUTINE start, void *arg)
{
*tid = CreateThread(NULL, 0, start, arg, 0, NULL);
if (!*tid) {
return (int)GetLastError();
}
return 0;
}

int pthread_join(pthread_t tid, void **value_ptr)
{
while (1) {
DWORD ExitCode = 0;
if (!GetExitCodeThread(tid, &ExitCode)) {
return (int)GetLastError();
}
if (ExitCode != STILL_ACTIVE) {
return 0;
}
}
}

void pthread_mutex_init(pthread_mutex_t *mutex, void *attr)
{
*mutex = CreateMutex(NULL, FALSE, NULL);
}

void pthread_mutex_lock(pthread_mutex_t *mutex)
{
WaitForSingleObject(*mutex, INFINITE);
}

void pthread_mutex_unlock(pthread_mutex_t *mutex)
{
ReleaseMutex(*mutex);
}

/*
* The console output format should be set to UTF-8, however in XP and Vista this breaks batch file processing.
* Attempting to restore on exit fails to restore if the program is terminated by the user.
* Solution - set the output format each printf.
*/
void console_vprintf(const char *fmt, va_list ap)
{
UINT cp = GetConsoleOutputCP();
SetConsoleOutputCP(CP_UTF8);
vprintf(fmt, ap);
SetConsoleOutputCP(cp);
}

void console_printf(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
console_vprintf(fmt, ap);
va_end(ap);
}
121 changes: 19 additions & 102 deletions mythtv/libs/libmythhdhomerun/hdhomerun_os_windows.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
* hdhomerun_os_windows.h
*
* Copyright © 2006-2008 Silicondust USA Inc. <www.silicondust.com>.
* Copyright © 2006-2010 Silicondust USA Inc. <www.silicondust.com>.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
Expand Down Expand Up @@ -31,12 +31,10 @@
*/

#define _WINSOCKAPI_
// MinGW lacks wspiapi, so remove dependency by setting minimum WINVER to WinXP
#define WINVER 0x0501
#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
//#include <wspiapi.h>
#include <wspiapi.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
Expand All @@ -55,7 +53,6 @@
#endif

typedef int bool_t;
/* Use MinGW includes instead
typedef signed __int8 int8_t;
typedef signed __int16 int16_t;
typedef signed __int32 int32_t;
Expand All @@ -64,121 +61,41 @@ typedef unsigned __int8 uint8_t;
typedef unsigned __int16 uint16_t;
typedef unsigned __int32 uint32_t;
typedef unsigned __int64 uint64_t;
typedef void (*sig_t)(int);
typedef HANDLE pthread_t;
typedef HANDLE pthread_mutex_t;
*/
#include <stdint.h>
#include <pthread.h>

// Avoid #define conflicts by limiting scope to non-c++
#ifndef __cplusplus
#define socklen_t int
#define close closesocket
#define sock_getlasterror WSAGetLastError()
#define sock_getlasterror_socktimeout (WSAGetLastError() == WSAETIMEDOUT)
//#define va_copy(x, y) x = y
#define va_copy(x, y) x = y
#define atoll _atoi64
#define strdup _strdup
#define strcasecmp _stricmp
#define snprintf _snprintf
#define fseeko _fseeki64
#define ftello _ftelli64
#define THREAD_FUNC_PREFIX DWORD WINAPI
#define SIGPIPE SIGABRT
#endif

static inline uint64_t getcurrenttime(void)
{
struct timeb tb;
ftime(&tb);
return ((uint64_t)tb.time * 1000) + tb.millitm;
}

static inline int msleep(unsigned int ms)
{
uint64_t stop_time = getcurrenttime() + ms;

while (1) {
uint64_t current_time = getcurrenttime();
if (current_time >= stop_time) {
return 0;
}

uint64_t delay_ms = stop_time - current_time;
Sleep((DWORD)delay_ms);
}
}

// Avoid a define conflict
/*static inline int sleep(unsigned int sec)
{
msleep(sec * 1000);
return 0;
}
*/

static inline int setsocktimeout(int s, int level, int optname, uint64_t timeout)
{
int t = (int)timeout;
return setsockopt(s, level, optname, (char *)&t, sizeof(t));
}

/* MythTV uses pthreads lib instead
static inline int pthread_create(pthread_t *tid, void *attr, LPTHREAD_START_ROUTINE start, void *arg)
{
*tid = CreateThread(NULL, 0, start, arg, 0, NULL);
if (!*tid) {
return (int)GetLastError();
}
return 0;
}
static inline int pthread_join(pthread_t tid, void **value_ptr)
{
while (1) {
DWORD ExitCode = 0;
if (!GetExitCodeThread(tid, &ExitCode)) {
return (int)GetLastError();
}
if (ExitCode != STILL_ACTIVE) {
return 0;
}
}
}
static inline void pthread_mutex_init(pthread_mutex_t *mutex, void *attr)
{
*mutex = CreateMutex(NULL, FALSE, NULL);
}
#ifdef __cplusplus
extern "C" {
#endif

static inline void pthread_mutex_lock(pthread_mutex_t *mutex)
{
WaitForSingleObject(*mutex, INFINITE);
}
extern LIBTYPE uint64_t getcurrenttime(void);
extern LIBTYPE void msleep_approx(uint64_t ms);
extern LIBTYPE void msleep_minimum(uint64_t ms);

static inline void pthread_mutex_unlock(pthread_mutex_t *mutex)
{
ReleaseMutex(*mutex);
}
*/
extern LIBTYPE int pthread_create(pthread_t *tid, void *attr, LPTHREAD_START_ROUTINE start, void *arg);
extern LIBTYPE int pthread_join(pthread_t tid, void **value_ptr);
extern LIBTYPE void pthread_mutex_init(pthread_mutex_t *mutex, void *attr);
extern LIBTYPE void pthread_mutex_lock(pthread_mutex_t *mutex);
extern LIBTYPE void pthread_mutex_unlock(pthread_mutex_t *mutex);

/*
* The console output format should be set to UTF-8, however in XP and Vista this breaks batch file processing.
* Attempting to restore on exit fails to restore if the program is terminated by the user.
* Solution - set the output format each printf.
*/
static inline void console_vprintf(const char *fmt, va_list ap)
{
UINT cp = GetConsoleOutputCP();
SetConsoleOutputCP(CP_UTF8);
vprintf(fmt, ap);
SetConsoleOutputCP(cp);
}
extern LIBTYPE void console_vprintf(const char *fmt, va_list ap);
extern LIBTYPE void console_printf(const char *fmt, ...);

static inline void console_printf(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
console_vprintf(fmt, ap);
va_end(ap);
#ifdef __cplusplus
}
#endif
2 changes: 1 addition & 1 deletion mythtv/libs/libmythhdhomerun/hdhomerun_pkt.c
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
* hdhomerun_pkt.c
*
* Copyright © 2005-2006 Silicondust USA Inc. <www.silicondust.com>.
* Copyright © 2005-2006 Silicondust USA Inc. <www.silicondust.com>.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
Expand Down
2 changes: 1 addition & 1 deletion mythtv/libs/libmythhdhomerun/hdhomerun_pkt.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
* hdhomerun_pkt.h
*
* Copyright © 2005-2006 Silicondust USA Inc. <www.silicondust.com>.
* Copyright © 2005-2006 Silicondust USA Inc. <www.silicondust.com>.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
Expand Down
62 changes: 62 additions & 0 deletions mythtv/libs/libmythhdhomerun/hdhomerun_sock.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
* hdhomerun_sock.h
*
* Copyright © 2010 Silicondust USA Inc. <www.silicondust.com>.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* As a special exception to the GNU Lesser General Public License,
* you may link, statically or dynamically, an application with a
* publicly distributed version of the Library to produce an
* executable file containing portions of the Library, and
* distribute that executable file under terms of your choice,
* without any of the additional requirements listed in clause 4 of
* the GNU Lesser General Public License.
*
* By "a publicly distributed version of the Library", we mean
* either the unmodified Library as distributed by Silicondust, or a
* modified version of the Library that is distributed under the
* conditions defined in the GNU Lesser General Public License.
*/
#ifdef __cplusplus
extern "C" {
#endif

#define HDHOMERUN_SOCK_INVALID -1

typedef int hdhomerun_sock_t;

extern LIBTYPE hdhomerun_sock_t hdhomerun_sock_create_udp(void);
extern LIBTYPE hdhomerun_sock_t hdhomerun_sock_create_tcp(void);
extern LIBTYPE void hdhomerun_sock_destroy(hdhomerun_sock_t sock);

extern LIBTYPE int hdhomerun_sock_getlasterror(void);
extern LIBTYPE uint32_t hdhomerun_sock_getsockname_addr(hdhomerun_sock_t sock);
extern LIBTYPE uint16_t hdhomerun_sock_getsockname_port(hdhomerun_sock_t sock);
extern LIBTYPE uint32_t hdhomerun_sock_getpeername_addr(hdhomerun_sock_t sock);
extern LIBTYPE uint32_t hdhomerun_sock_getaddrinfo_addr(hdhomerun_sock_t sock, const char *name);

extern LIBTYPE bool_t hdhomerun_sock_bind(hdhomerun_sock_t sock, uint32_t local_addr, uint16_t local_port);
extern LIBTYPE bool_t hdhomerun_sock_connect(hdhomerun_sock_t sock, uint32_t remote_addr, uint16_t remote_port, uint64_t timeout);

extern LIBTYPE bool_t hdhomerun_sock_send(hdhomerun_sock_t sock, const void *data, size_t length, uint64_t timeout);
extern LIBTYPE bool_t hdhomerun_sock_sendto(hdhomerun_sock_t sock, uint32_t remote_addr, uint16_t remote_port, const void *data, size_t length, uint64_t timeout);

extern LIBTYPE bool_t hdhomerun_sock_recv(hdhomerun_sock_t sock, void *data, size_t *length, uint64_t timeout);
extern LIBTYPE bool_t hdhomerun_sock_recvfrom(hdhomerun_sock_t sock, uint32_t *remote_addr, uint16_t *remote_port, void *data, size_t *length, uint64_t timeout);


#ifdef __cplusplus
}
#endif
414 changes: 414 additions & 0 deletions mythtv/libs/libmythhdhomerun/hdhomerun_sock_posix.c

Large diffs are not rendered by default.

347 changes: 347 additions & 0 deletions mythtv/libs/libmythhdhomerun/hdhomerun_sock_windows.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,347 @@
/*
* hdhomerun_sock_windows.c
*
* Copyright © 2010 Silicondust USA Inc. <www.silicondust.com>.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* As a special exception to the GNU Lesser General Public License,
* you may link, statically or dynamically, an application with a
* publicly distributed version of the Library to produce an
* executable file containing portions of the Library, and
* distribute that executable file under terms of your choice,
* without any of the additional requirements listed in clause 4 of
* the GNU Lesser General Public License.
*
* By "a publicly distributed version of the Library", we mean
* either the unmodified Library as distributed by Silicondust, or a
* modified version of the Library that is distributed under the
* conditions defined in the GNU Lesser General Public License.
*/

/*
* Implementation notes:
*
* API specifies timeout for each operation (or zero for non-blocking).
*
* It is not possible to rely on the OS socket timeout as this will fail to
* detect the command-response situation where data is sent successfully and
* the other end chooses not to send a response (other than the TCP ack).
*
* Windows supports select() however native WSA events are used to:
* - avoid problems with socket numbers above 1024.
* - wait without allowing other events handlers to run (important for use
* with win7 WMC).
*/

#include "hdhomerun.h"

hdhomerun_sock_t hdhomerun_sock_create_udp(void)
{
/* Create socket. */
hdhomerun_sock_t sock = (hdhomerun_sock_t)socket(AF_INET, SOCK_DGRAM, 0);
if (sock == -1) {
return HDHOMERUN_SOCK_INVALID;
}

/* Set non-blocking */
unsigned long mode = 1;
if (ioctlsocket(sock, FIONBIO, &mode) != 0) {
closesocket(sock);
return HDHOMERUN_SOCK_INVALID;
}

/* Allow broadcast. */
int sock_opt = 1;
setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char *)&sock_opt, sizeof(sock_opt));

/* Success. */
return sock;
}

hdhomerun_sock_t hdhomerun_sock_create_tcp(void)
{
/* Create socket. */
hdhomerun_sock_t sock = (hdhomerun_sock_t)socket(AF_INET, SOCK_STREAM, 0);
if (sock == -1) {
return HDHOMERUN_SOCK_INVALID;
}

/* Set non-blocking */
unsigned long mode = 1;
if (ioctlsocket(sock, FIONBIO, &mode) != 0) {
closesocket(sock);
return HDHOMERUN_SOCK_INVALID;
}

/* Success. */
return sock;
}

void hdhomerun_sock_destroy(hdhomerun_sock_t sock)
{
closesocket(sock);
}

int hdhomerun_sock_getlasterror(void)
{
return WSAGetLastError();
}

uint32_t hdhomerun_sock_getsockname_addr(hdhomerun_sock_t sock)
{
struct sockaddr_in sock_addr;
int sockaddr_size = sizeof(sock_addr);

if (getsockname(sock, (struct sockaddr *)&sock_addr, &sockaddr_size) != 0) {
return 0;
}

return ntohl(sock_addr.sin_addr.s_addr);
}

uint16_t hdhomerun_sock_getsockname_port(hdhomerun_sock_t sock)
{
struct sockaddr_in sock_addr;
int sockaddr_size = sizeof(sock_addr);

if (getsockname(sock, (struct sockaddr *)&sock_addr, &sockaddr_size) != 0) {
return 0;
}

return ntohs(sock_addr.sin_port);
}

uint32_t hdhomerun_sock_getpeername_addr(hdhomerun_sock_t sock)
{
struct sockaddr_in sock_addr;
int sockaddr_size = sizeof(sock_addr);

if (getpeername(sock, (struct sockaddr *)&sock_addr, &sockaddr_size) != 0) {
return 0;
}

return ntohl(sock_addr.sin_addr.s_addr);
}

uint32_t hdhomerun_sock_getaddrinfo_addr(hdhomerun_sock_t sock, const char *name)
{
struct addrinfo hints;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;

struct addrinfo *sock_info;
if (getaddrinfo(name, "", &hints, &sock_info) != 0) {
return 0;
}

struct sockaddr_in *sock_addr = (struct sockaddr_in *)sock_info->ai_addr;
uint32_t addr = ntohl(sock_addr->sin_addr.s_addr);

freeaddrinfo(sock_info);
return addr;
}

bool_t hdhomerun_sock_bind(hdhomerun_sock_t sock, uint32_t local_addr, uint16_t local_port)
{
struct sockaddr_in sock_addr;
memset(&sock_addr, 0, sizeof(sock_addr));
sock_addr.sin_family = AF_INET;
sock_addr.sin_addr.s_addr = htonl(local_addr);
sock_addr.sin_port = htons(local_port);

if (bind(sock, (struct sockaddr *)&sock_addr, sizeof(sock_addr)) != 0) {
return FALSE;
}

return TRUE;
}

bool_t hdhomerun_sock_connect(hdhomerun_sock_t sock, uint32_t remote_addr, uint16_t remote_port, uint64_t timeout)
{
WSAEVENT wsa_event = WSACreateEvent();
if (wsa_event == WSA_INVALID_EVENT) {
return FALSE;
}

if (WSAEventSelect(sock, wsa_event, FD_CONNECT) == SOCKET_ERROR) {
WSACloseEvent(wsa_event);
return FALSE;
}

/* Connect (non-blocking). */
struct sockaddr_in sock_addr;
memset(&sock_addr, 0, sizeof(sock_addr));
sock_addr.sin_family = AF_INET;
sock_addr.sin_addr.s_addr = htonl(remote_addr);
sock_addr.sin_port = htons(remote_port);

if (connect(sock, (struct sockaddr *)&sock_addr, sizeof(sock_addr)) != 0) {
if (WSAGetLastError() != WSAEWOULDBLOCK) {
WSACloseEvent(wsa_event);
return FALSE;
}
}

/* Wait for connect to complete (both success and failure will signal). */
DWORD ret = WaitForSingleObjectEx(wsa_event, (DWORD)timeout, FALSE);
WSACloseEvent(wsa_event);

if (ret != WAIT_OBJECT_0) {
return FALSE;
}

/* Detect success/failure. */
int sockaddr_size = sizeof(sock_addr);
if (getpeername(sock, (struct sockaddr *)&sock_addr, &sockaddr_size) != 0) {
return FALSE;
}

return TRUE;
}

static bool_t hdhomerun_sock_wait_for_event(hdhomerun_sock_t sock, long event_type, uint64_t stop_time)
{
uint64_t current_time = getcurrenttime();
if (current_time >= stop_time) {
return FALSE;
}

WSAEVENT wsa_event = WSACreateEvent();
if (wsa_event == WSA_INVALID_EVENT) {
return FALSE;
}

if (WSAEventSelect(sock, wsa_event, event_type) == SOCKET_ERROR) {
WSACloseEvent(wsa_event);
return FALSE;
}

DWORD ret = WaitForSingleObjectEx(wsa_event, (DWORD)(stop_time - current_time), FALSE);
WSACloseEvent(wsa_event);

if (ret != WAIT_OBJECT_0) {
return FALSE;
}

return TRUE;
}

bool_t hdhomerun_sock_send(hdhomerun_sock_t sock, const void *data, size_t length, uint64_t timeout)
{
uint64_t stop_time = getcurrenttime() + timeout;
const uint8_t *ptr = (uint8_t *)data;

while (1) {
int ret = send(sock, (char *)ptr, (int)length, 0);
if (ret >= (int)length) {
return TRUE;
}

if (ret > 0) {
ptr += ret;
length -= ret;
}

if (WSAGetLastError() != WSAEWOULDBLOCK) {
return FALSE;
}

if (!hdhomerun_sock_wait_for_event(sock, FD_WRITE | FD_CLOSE, stop_time)) {
return FALSE;
}
}
}

bool_t hdhomerun_sock_sendto(hdhomerun_sock_t sock, uint32_t remote_addr, uint16_t remote_port, const void *data, size_t length, uint64_t timeout)
{
uint64_t stop_time = getcurrenttime() + timeout;
const uint8_t *ptr = (uint8_t *)data;

while (1) {
struct sockaddr_in sock_addr;
memset(&sock_addr, 0, sizeof(sock_addr));
sock_addr.sin_family = AF_INET;
sock_addr.sin_addr.s_addr = htonl(remote_addr);
sock_addr.sin_port = htons(remote_port);

int ret = sendto(sock, (char *)ptr, (int)length, 0, (struct sockaddr *)&sock_addr, sizeof(sock_addr));
if (ret >= (int)length) {
return TRUE;
}

if (ret > 0) {
ptr += ret;
length -= ret;
}

if (WSAGetLastError() != WSAEWOULDBLOCK) {
return FALSE;
}

if (!hdhomerun_sock_wait_for_event(sock, FD_WRITE | FD_CLOSE, stop_time)) {
return FALSE;
}
}
}

bool_t hdhomerun_sock_recv(hdhomerun_sock_t sock, void *data, size_t *length, uint64_t timeout)
{
uint64_t stop_time = getcurrenttime() + timeout;

while (1) {
int ret = recv(sock, (char *)data, (int)(*length), 0);
if (ret > 0) {
*length = ret;
return TRUE;
}

if (WSAGetLastError() != WSAEWOULDBLOCK) {
return FALSE;
}

if (!hdhomerun_sock_wait_for_event(sock, FD_READ | FD_CLOSE, stop_time)) {
return FALSE;
}
}
}

bool_t hdhomerun_sock_recvfrom(hdhomerun_sock_t sock, uint32_t *remote_addr, uint16_t *remote_port, void *data, size_t *length, uint64_t timeout)
{
uint64_t stop_time = getcurrenttime() + timeout;

while (1) {
struct sockaddr_in sock_addr;
memset(&sock_addr, 0, sizeof(sock_addr));
int sockaddr_size = sizeof(sock_addr);

int ret = recvfrom(sock, (char *)data, (int)(*length), 0, (struct sockaddr *)&sock_addr, &sockaddr_size);
if (ret > 0) {
*remote_addr = ntohl(sock_addr.sin_addr.s_addr);
*remote_port = ntohs(sock_addr.sin_port);
*length = ret;
return TRUE;
}

if (WSAGetLastError() != WSAEWOULDBLOCK) {
return FALSE;
}

if (!hdhomerun_sock_wait_for_event(sock, FD_READ | FD_CLOSE, stop_time)) {
return FALSE;
}
}
}
2 changes: 1 addition & 1 deletion mythtv/libs/libmythhdhomerun/hdhomerun_types.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
* hdhomerun_types.h
*
* Copyright © 2008-2009 Silicondust USA Inc. <www.silicondust.com>.
* Copyright © 2008-2009 Silicondust USA Inc. <www.silicondust.com>.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
Expand Down
173 changes: 107 additions & 66 deletions mythtv/libs/libmythhdhomerun/hdhomerun_video.c
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
* hdhomerun_video.c
*
* Copyright © 2006 Silicondust USA Inc. <www.silicondust.com>.
* Copyright © 2006-2010 Silicondust USA Inc. <www.silicondust.com>.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
Expand Down Expand Up @@ -34,21 +34,27 @@

struct hdhomerun_video_sock_t {
pthread_mutex_t lock;
uint8_t *buffer;
size_t buffer_size;
struct hdhomerun_debug_t *dbg;

hdhomerun_sock_t sock;
uint32_t multicast_ip;

volatile size_t head;
volatile size_t tail;
uint8_t *buffer;
size_t buffer_size;
size_t advance;
volatile bool_t terminate;

pthread_t thread;
int sock;
uint32_t rtp_sequence;
struct hdhomerun_debug_t *dbg;
volatile bool_t terminate;

volatile uint32_t packet_count;
volatile uint32_t transport_error_count;
volatile uint32_t network_error_count;
volatile uint32_t sequence_error_count;
volatile uint32_t overflow_error_count;

volatile uint32_t rtp_sequence;
volatile uint8_t sequence[0x2000];
};

Expand All @@ -64,7 +70,7 @@ struct hdhomerun_video_sock_t *hdhomerun_video_create(uint16_t listen_port, size
}

vs->dbg = dbg;
vs->sock = -1;
vs->sock = HDHOMERUN_SOCK_INVALID;
pthread_mutex_init(&vs->lock, NULL);

/* Reset sequence tracking. */
Expand All @@ -86,8 +92,8 @@ struct hdhomerun_video_sock_t *hdhomerun_video_create(uint16_t listen_port, size
}

/* Create socket. */
vs->sock = (int)socket(AF_INET, SOCK_DGRAM, 0);
if (vs->sock == -1) {
vs->sock = hdhomerun_sock_create_udp();
if (vs->sock == HDHOMERUN_SOCK_INVALID) {
hdhomerun_debug_printf(dbg, "hdhomerun_video_create: failed to allocate socket\n");
goto error;
}
Expand All @@ -96,17 +102,8 @@ struct hdhomerun_video_sock_t *hdhomerun_video_create(uint16_t listen_port, size
int rx_size = 1024 * 1024;
setsockopt(vs->sock, SOL_SOCKET, SO_RCVBUF, (char *)&rx_size, sizeof(rx_size));

/* Set timeouts. */
setsocktimeout(vs->sock, SOL_SOCKET, SO_SNDTIMEO, 1000);
setsocktimeout(vs->sock, SOL_SOCKET, SO_RCVTIMEO, 1000);

/* Bind socket. */
struct sockaddr_in sock_addr;
memset(&sock_addr, 0, sizeof(sock_addr));
sock_addr.sin_family = AF_INET;
sock_addr.sin_addr.s_addr = htonl(INADDR_ANY);
sock_addr.sin_port = htons(listen_port);
if (bind(vs->sock, (struct sockaddr *)&sock_addr, sizeof(sock_addr)) != 0) {
if (!hdhomerun_sock_bind(vs->sock, INADDR_ANY, listen_port)) {
hdhomerun_debug_printf(dbg, "hdhomerun_video_create: failed to bind socket (port %u)\n", listen_port);
goto error;
}
Expand All @@ -121,8 +118,8 @@ struct hdhomerun_video_sock_t *hdhomerun_video_create(uint16_t listen_port, size
return vs;

error:
if (vs->sock != -1) {
close(vs->sock);
if (vs->sock != HDHOMERUN_SOCK_INVALID) {
hdhomerun_sock_destroy(vs->sock);
}
if (vs->buffer) {
free(vs->buffer);
Expand All @@ -136,22 +133,65 @@ void hdhomerun_video_destroy(struct hdhomerun_video_sock_t *vs)
vs->terminate = TRUE;
pthread_join(vs->thread, NULL);

close(vs->sock);
hdhomerun_sock_destroy(vs->sock);
free(vs->buffer);

free(vs);
}

hdhomerun_sock_t hdhomerun_video_get_sock(struct hdhomerun_video_sock_t *vs)
{
return vs->sock;
}

uint16_t hdhomerun_video_get_local_port(struct hdhomerun_video_sock_t *vs)
{
struct sockaddr_in sock_addr;
socklen_t sockaddr_size = sizeof(sock_addr);
if (getsockname(vs->sock, (struct sockaddr*)&sock_addr, &sockaddr_size) != 0) {
hdhomerun_debug_printf(vs->dbg, "hdhomerun_video_get_local_port: getsockname failed (%d)\n", sock_getlasterror);
uint16_t port = hdhomerun_sock_getsockname_port(vs->sock);
if (port == 0) {
hdhomerun_debug_printf(vs->dbg, "hdhomerun_video_get_local_port: getsockname failed (%d)\n", hdhomerun_sock_getlasterror());
return 0;
}

return ntohs(sock_addr.sin_port);
return port;
}

int hdhomerun_video_join_multicast_group(struct hdhomerun_video_sock_t *vs, uint32_t multicast_ip, uint32_t local_ip)
{
if (vs->multicast_ip != 0) {
hdhomerun_video_leave_multicast_group(vs);
}

struct ip_mreq imr;
memset(&imr, 0, sizeof(imr));
imr.imr_multiaddr.s_addr = htonl(multicast_ip);
imr.imr_interface.s_addr = htonl(local_ip);

if (setsockopt(vs->sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (const char *)&imr, sizeof(imr)) != 0) {
hdhomerun_debug_printf(vs->dbg, "hdhomerun_video_join_multicast_group: setsockopt failed (%d)\n", hdhomerun_sock_getlasterror());
return -1;
}

vs->multicast_ip = multicast_ip;
return 1;
}

int hdhomerun_video_leave_multicast_group(struct hdhomerun_video_sock_t *vs)
{
if (vs->multicast_ip == 0) {
return 1;
}

struct ip_mreq imr;
memset(&imr, 0, sizeof(imr));
imr.imr_multiaddr.s_addr = htonl(vs->multicast_ip);
imr.imr_interface.s_addr = htonl(INADDR_ANY);

if (setsockopt(vs->sock, IPPROTO_IP, IP_DROP_MEMBERSHIP, (const char *)&imr, sizeof(imr)) != 0) {
hdhomerun_debug_printf(vs->dbg, "hdhomerun_video_leave_multicast_group: setsockopt failed (%d)\n", hdhomerun_sock_getlasterror());
}

vs->multicast_ip = 0;
return 1;
}

static void hdhomerun_video_stats_ts_pkt(struct hdhomerun_video_sock_t *vs, uint8_t *ptr)
Expand All @@ -168,23 +208,22 @@ static void hdhomerun_video_stats_ts_pkt(struct hdhomerun_video_sock_t *vs, uint
return;
}

uint8_t continuity_counter = ptr[3] & 0x0F;
uint8_t sequence = ptr[3] & 0x0F;

uint8_t previous_sequence = vs->sequence[packet_identifier];
vs->sequence[packet_identifier] = sequence;

if (continuity_counter == ((previous_sequence + 1) & 0x0F)) {
vs->sequence[packet_identifier] = continuity_counter;
if (previous_sequence == 0xFF) {
return;
}
if (previous_sequence == 0xFF) {
vs->sequence[packet_identifier] = continuity_counter;
if (sequence == ((previous_sequence + 1) & 0x0F)) {
return;
}
if (continuity_counter == previous_sequence) {
if (sequence == previous_sequence) {
return;
}

vs->sequence_error_count++;
vs->sequence[packet_identifier] = continuity_counter;
}

static void hdhomerun_video_parse_rtp(struct hdhomerun_video_sock_t *vs, struct hdhomerun_pkt_t *pkt)
Expand All @@ -193,19 +232,27 @@ static void hdhomerun_video_parse_rtp(struct hdhomerun_video_sock_t *vs, struct
uint32_t rtp_sequence = hdhomerun_pkt_read_u16(pkt);
pkt->pos += 8;

if (rtp_sequence != ((vs->rtp_sequence + 1) & 0xFFFF)) {
if (vs->rtp_sequence != 0xFFFFFFFF) {
vs->network_error_count++;
uint32_t previous_rtp_sequence = vs->rtp_sequence;
vs->rtp_sequence = rtp_sequence;

/* restart pid sequence check */
/* can't use memset bcs sequence is volatile */
int i;
for (i = 0; i < sizeof(vs->sequence) / sizeof(uint8_t) ; i++)
vs->sequence[i] = 0xFF;
}
/* Initial case - first packet received. */
if (previous_rtp_sequence == 0xFFFFFFFF) {
return;
}

vs->rtp_sequence = rtp_sequence;
/* Normal case - next sequence number. */
if (rtp_sequence == ((previous_rtp_sequence + 1) & 0xFFFF)) {
return;
}

/* Error case - sequence missed. */
vs->network_error_count++;

/* Restart pid sequence check after packet loss. */
int i;
for (i = 0; i < 0x2000; i++) {
vs->sequence[i] = 0xFF;
}
}

static THREAD_FUNC_PREFIX hdhomerun_video_thread_execute(void *arg)
Expand All @@ -218,7 +265,11 @@ static THREAD_FUNC_PREFIX hdhomerun_video_thread_execute(void *arg)
hdhomerun_pkt_reset(pkt);

/* Receive. */
int length = recv(vs->sock, (char *)pkt->end, VIDEO_RTP_DATA_PACKET_SIZE, 0);
size_t length = VIDEO_RTP_DATA_PACKET_SIZE;
if (!hdhomerun_sock_recv(vs->sock, pkt->end, &length, 25)) {
continue;
}

pkt->end += length;

if (length == VIDEO_RTP_DATA_PACKET_SIZE) {
Expand All @@ -227,16 +278,8 @@ static THREAD_FUNC_PREFIX hdhomerun_video_thread_execute(void *arg)
}

if (length != VIDEO_DATA_PACKET_SIZE) {
if (length > 0) {
/* Data received but not valid - ignore. */
continue;
}
if (sock_getlasterror_socktimeout) {
/* Wait for more data. */
continue;
}
vs->terminate = TRUE;
return NULL;
/* Data received but not valid - ignore. */
continue;
}

pthread_mutex_lock(&vs->lock);
Expand Down Expand Up @@ -269,7 +312,6 @@ static THREAD_FUNC_PREFIX hdhomerun_video_thread_execute(void *arg)
continue;
}

/* Atomic update. */
vs->head = head;

pthread_mutex_unlock(&vs->lock);
Expand All @@ -291,7 +333,6 @@ uint8_t *hdhomerun_video_recv(struct hdhomerun_video_sock_t *vs, size_t max_size
tail -= vs->buffer_size;
}

/* Atomic update. */
vs->tail = tail;
}

Expand Down Expand Up @@ -334,12 +375,12 @@ void hdhomerun_video_flush(struct hdhomerun_video_sock_t *vs)
vs->tail = vs->head;
vs->advance = 0;

/* can't use memset bcs sequence is volatile */
vs->rtp_sequence = 0xFFFFFFFF;

int i;
for (i = 0; i < sizeof(vs->sequence) / sizeof(uint8_t) ; i++)
for (i = 0; i < 0x2000; i++) {
vs->sequence[i] = 0xFF;

vs->rtp_sequence = 0xFFFFFFFF;
}

vs->packet_count = 0;
vs->transport_error_count = 0;
Expand All @@ -355,10 +396,10 @@ void hdhomerun_video_debug_print_stats(struct hdhomerun_video_sock_t *vs)
struct hdhomerun_video_stats_t stats;
hdhomerun_video_get_stats(vs, &stats);

hdhomerun_debug_printf(vs->dbg, "video sock: pkt=%ld net=%ld te=%ld miss=%ld drop=%ld\n",
stats.packet_count, stats.network_error_count,
stats.transport_error_count, stats.sequence_error_count,
stats.overflow_error_count
hdhomerun_debug_printf(vs->dbg, "video sock: pkt=%lu net=%lu te=%lu miss=%lu drop=%lu\n",
(unsigned long)stats.packet_count, (unsigned long)stats.network_error_count,
(unsigned long)stats.transport_error_count, (unsigned long)stats.sequence_error_count,
(unsigned long)stats.overflow_error_count
);
}

Expand Down
13 changes: 12 additions & 1 deletion mythtv/libs/libmythhdhomerun/hdhomerun_video.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
* hdhomerun_video.h
*
* Copyright © 2006 Silicondust USA Inc. <www.silicondust.com>.
* Copyright © 2006 Silicondust USA Inc. <www.silicondust.com>.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
Expand Down Expand Up @@ -70,6 +70,12 @@ extern LIBTYPE void hdhomerun_video_destroy(struct hdhomerun_video_sock_t *vs);
*/
extern LIBTYPE uint16_t hdhomerun_video_get_local_port(struct hdhomerun_video_sock_t *vs);

/*
* Join/leave multicast group.
*/
extern LIBTYPE int hdhomerun_video_join_multicast_group(struct hdhomerun_video_sock_t *vs, uint32_t multicast_ip, uint32_t local_ip);
extern LIBTYPE int hdhomerun_video_leave_multicast_group(struct hdhomerun_video_sock_t *vs);

/*
* Read data from buffer.
*
Expand Down Expand Up @@ -100,6 +106,11 @@ extern LIBTYPE void hdhomerun_video_flush(struct hdhomerun_video_sock_t *vs);
extern LIBTYPE void hdhomerun_video_debug_print_stats(struct hdhomerun_video_sock_t *vs);
extern LIBTYPE void hdhomerun_video_get_stats(struct hdhomerun_video_sock_t *vs, struct hdhomerun_video_stats_t *stats);

/*
* Internal use only.
*/
extern LIBTYPE hdhomerun_sock_t hdhomerun_video_get_sock(struct hdhomerun_video_sock_t *vs);

#ifdef __cplusplus
}
#endif
16 changes: 10 additions & 6 deletions mythtv/libs/libmythhdhomerun/libmythhdhomerun.pro
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,24 @@ INSTALLS = target

QMAKE_CLEAN += $(TARGET) $(TARGETA) $(TARGETD) $(TARGET0) $(TARGET1) $(TARGET2)

HEADERS += hdhomerun.h hdhomerun_os.h hdhomerun_types.h
HEADERS += hdhomerun.h hdhomerun_os.h hdhomerun_sock.h hdhomerun_types.h

HEADERS += hdhomerun_channels.h hdhomerun_channelscan.h hdhomerun_control.h
HEADERS += hdhomerun_debug.h hdhomerun_device.h hdhomerun_dhcp.h
HEADERS += hdhomerun_debug.h hdhomerun_device.h hdhomerun_device_selector.h
HEADERS += hdhomerun_discover.h hdhomerun_pkt.h hdhomerun_video.h
HEADERS += hdhomerun_device_selector.h

SOURCES += hdhomerun_channels.c hdhomerun_channelscan.c hdhomerun_control.c
SOURCES += hdhomerun_debug.c hdhomerun_device.c hdhomerun_dhcp.c
SOURCES += hdhomerun_debug.c hdhomerun_device.c hdhomerun_device_selector.c
SOURCES += hdhomerun_discover.c hdhomerun_pkt.c hdhomerun_video.c
SOURCES += hdhomerun_device_selector.c

unix {
HEADERS += hdhomerun_os_posix.h hdhomerun_sock_posix.h
SOURCES += hdhomerun_os_posix.c hdhomerun_sock_posix.c
}

mingw {
HEADERS += hdhomerun_os_windows.h
HEADERS += hdhomerun_os_windows.h hdhomerun_sock_windows.h
SOURCES += hdhomerun_os_windows.c hdhomerun_sock_windows.c
LIBS += -lws2_32 -liphlpapi -lpthread
}

Expand Down
14 changes: 7 additions & 7 deletions mythtv/libs/libmythtv/hdhrchannel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -181,10 +181,14 @@ bool HDHRChannel::SetChannelByString(const QString &channum)
}
else
{
VERBOSE(VB_IMPORTANT, LOC_ERR +
"dtv_multiplex data is required for tuning");
if (!_stream_handler->TuneVChannel(channum))
{
VERBOSE(VB_IMPORTANT, LOC_ERR +
"dtv_multiplex data is required for tuning");
return false;
}

return false;
SetSIStandard(si_std);
}
}
else if (!ChangeExternalChannel(freqid))
Expand All @@ -201,10 +205,6 @@ bool HDHRChannel::SetChannelByString(const QString &channum)
QString tmpX = m_curchannelname; tmpX.detach();
m_inputs[m_currentInputID]->startChanNum = tmpX;

// Turn on the program filtering if tuning to MPEG stream
if (mpeg_prog_num && (GetTuningMode() == "mpeg"))
_stream_handler->TuneProgram(mpeg_prog_num);

return true;
}

Expand Down
34 changes: 29 additions & 5 deletions mythtv/libs/libmythtv/hdhrstreamhandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,15 @@ bool HDHRStreamHandler::UpdateFiltersFromStreamData(void)

bool HDHRStreamHandler::UpdateFilters(void)
{
if (_tune_mode == hdhrTuneModeFrequency)
_tune_mode = hdhrTuneModeFrequencyPid;

if (_tune_mode != hdhrTuneModeFrequencyPid)
{
VERBOSE(VB_IMPORTANT, LOC_ERR + "UpdateFilters called in wrong tune mode");
return false;
}

#ifdef DEBUG_PID_FILTERS
VERBOSE(VB_RECORD, LOC + "UpdateFilters()");
#endif // DEBUG_PID_FILTERS
Expand All @@ -493,10 +502,6 @@ bool HDHRStreamHandler::UpdateFilters(void)
vector<uint> range_min;
vector<uint> range_max;

// FIXME
// if (_ignore_filters)
// return true;

for (uint i = 0; i < _pid_info.size(); i++)
{
uint pid_min = _pid_info[i];
Expand Down Expand Up @@ -731,8 +736,9 @@ bool HDHRStreamHandler::IsConnected(void) const

bool HDHRStreamHandler::TuneChannel(const QString &chn)
{
QString current = TunerGet("channel");
_tune_mode = hdhrTuneModeFrequency;

QString current = TunerGet("channel");
if (current == chn)
{
VERBOSE(VB_RECORD, QString(LOC + "Not Re-Tuning channel %1").arg(chn));
Expand All @@ -746,7 +752,25 @@ bool HDHRStreamHandler::TuneChannel(const QString &chn)

bool HDHRStreamHandler::TuneProgram(uint mpeg_prog_num)
{
if (_tune_mode == hdhrTuneModeFrequency)
_tune_mode = hdhrTuneModeFrequencyProgram;

if (_tune_mode != hdhrTuneModeFrequencyProgram)
{
VERBOSE(VB_IMPORTANT, LOC_ERR + "TuneProgram called in wrong tune mode");
return false;
}

VERBOSE(VB_RECORD, QString(LOC + "Tuning program %1").arg(mpeg_prog_num));
return !TunerSet(
"program", QString::number(mpeg_prog_num), false).isEmpty();
}

bool HDHRStreamHandler::TuneVChannel(const QString &vchn)
{
_tune_mode = hdhrTuneModeVChannel;

VERBOSE(VB_RECORD, QString(LOC + "Tuning vchannel %1").arg(vchn));
return !TunerSet(
"vchannel", vchn).isEmpty();
}
14 changes: 12 additions & 2 deletions mythtv/libs/libmythtv/hdhrstreamhandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@ class DeviceReadBuffer;
struct hdhomerun_device_t { int dummy; };
#endif

enum HDHRTuneMode {
hdhrTuneModeNone = 0,
hdhrTuneModeFrequency,
hdhrTuneModeFrequencyPid,
hdhrTuneModeFrequencyProgram,
hdhrTuneModeVChannel
};

typedef QMap<uint,int> FilterMap;

//#define RETUNE_TIMEOUT 5000
Expand All @@ -50,6 +58,7 @@ class HDHRStreamHandler : public ReaderPausedCB
// Commands
bool TuneChannel(const QString &chanid);
bool TuneProgram(uint mpeg_prog_num);
bool TuneVChannel(const QString &vchn);
bool EnterPowerSavingMode(void);


Expand Down Expand Up @@ -91,9 +100,10 @@ class HDHRStreamHandler : public ReaderPausedCB

private:
hdhomerun_device_t *_hdhomerun_device;
uint _tuner;
QString _devicename;
uint _tuner;
QString _devicename;
vector<DTVTunerType> _tuner_types;
HDHRTuneMode _tune_mode; // debug self check

mutable QMutex _start_stop_lock;
bool _running;
Expand Down