MythTV  0.26-pre
hdhomerun_control.c
Go to the documentation of this file.
00001 /*
00002  * hdhomerun_control.c
00003  *
00004  * Copyright © 2006-2010 Silicondust USA Inc. <www.silicondust.com>.
00005  *
00006  * This library is free software; you can redistribute it and/or 
00007  * modify it under the terms of the GNU Lesser General Public
00008  * License as published by the Free Software Foundation; either
00009  * version 3 of the License, or (at your option) any later version.
00010  *
00011  * This library is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014  * Lesser General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU Lesser General Public
00017  * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
00018  * 
00019  * As a special exception to the GNU Lesser General Public License,
00020  * you may link, statically or dynamically, an application with a
00021  * publicly distributed version of the Library to produce an
00022  * executable file containing portions of the Library, and
00023  * distribute that executable file under terms of your choice,
00024  * without any of the additional requirements listed in clause 4 of
00025  * the GNU Lesser General Public License.
00026  * 
00027  * By "a publicly distributed version of the Library", we mean
00028  * either the unmodified Library as distributed by Silicondust, or a
00029  * modified version of the Library that is distributed under the
00030  * conditions defined in the GNU Lesser General Public License.
00031  */
00032 
00033 #include "hdhomerun.h"
00034 
00035 #define HDHOMERUN_CONTROL_CONNECT_TIMEOUT 2500
00036 #define HDHOMERUN_CONTROL_SEND_TIMEOUT 2500
00037 #define HDHOMERUN_CONTROL_RECV_TIMEOUT 2500
00038 #define HDHOMERUN_CONTROL_UPGRADE_TIMEOUT 20000
00039 
00040 struct hdhomerun_control_sock_t {
00041         uint32_t desired_device_id;
00042         uint32_t desired_device_ip;
00043         uint32_t actual_device_id;
00044         uint32_t actual_device_ip;
00045         hdhomerun_sock_t sock;
00046         struct hdhomerun_debug_t *dbg;
00047         struct hdhomerun_pkt_t tx_pkt;
00048         struct hdhomerun_pkt_t rx_pkt;
00049 };
00050 
00051 static void hdhomerun_control_close_sock(struct hdhomerun_control_sock_t *cs)
00052 {
00053         if (cs->sock == HDHOMERUN_SOCK_INVALID) {
00054                 return;
00055         }
00056 
00057         hdhomerun_sock_destroy(cs->sock);
00058         cs->sock = HDHOMERUN_SOCK_INVALID;
00059 }
00060 
00061 void hdhomerun_control_set_device(struct hdhomerun_control_sock_t *cs, uint32_t device_id, uint32_t device_ip)
00062 {
00063         hdhomerun_control_close_sock(cs);
00064 
00065         cs->desired_device_id = device_id;
00066         cs->desired_device_ip = device_ip;
00067         cs->actual_device_id = 0;
00068         cs->actual_device_ip = 0;
00069 }
00070 
00071 struct hdhomerun_control_sock_t *hdhomerun_control_create(uint32_t device_id, uint32_t device_ip, struct hdhomerun_debug_t *dbg)
00072 {
00073         struct hdhomerun_control_sock_t *cs = (struct hdhomerun_control_sock_t *)calloc(1, sizeof(struct hdhomerun_control_sock_t));
00074         if (!cs) {
00075                 hdhomerun_debug_printf(dbg, "hdhomerun_control_create: failed to allocate control object\n");
00076                 return NULL;
00077         }
00078 
00079         cs->dbg = dbg;
00080         cs->sock = HDHOMERUN_SOCK_INVALID;
00081         hdhomerun_control_set_device(cs, device_id, device_ip);
00082 
00083         return cs;
00084 }
00085 
00086 void hdhomerun_control_destroy(struct hdhomerun_control_sock_t *cs)
00087 {
00088         hdhomerun_control_close_sock(cs);
00089         free(cs);
00090 }
00091 
00092 static bool_t hdhomerun_control_connect_sock(struct hdhomerun_control_sock_t *cs)
00093 {
00094         if (cs->sock != HDHOMERUN_SOCK_INVALID) {
00095                 return TRUE;
00096         }
00097 
00098         if ((cs->desired_device_id == 0) && (cs->desired_device_ip == 0)) {
00099                 hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_connect_sock: no device specified\n");
00100                 return FALSE;
00101         }
00102         if (hdhomerun_discover_is_ip_multicast(cs->desired_device_ip)) {
00103                 hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_connect_sock: cannot use multicast ip address for device operations\n");
00104                 return FALSE;
00105         }
00106 
00107         /* Find device. */
00108         struct hdhomerun_discover_device_t result;
00109         if (hdhomerun_discover_find_devices_custom(cs->desired_device_ip, HDHOMERUN_DEVICE_TYPE_WILDCARD, cs->desired_device_id, &result, 1) <= 0) {
00110                 hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_connect_sock: device not found\n");
00111                 return FALSE;
00112         }
00113         cs->actual_device_ip = result.ip_addr;
00114         cs->actual_device_id = result.device_id;
00115 
00116         /* Create socket. */
00117         cs->sock = hdhomerun_sock_create_tcp();
00118         if (cs->sock == HDHOMERUN_SOCK_INVALID) {
00119                 hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_connect_sock: failed to create socket (%d)\n", hdhomerun_sock_getlasterror());
00120                 return FALSE;
00121         }
00122 
00123         /* Initiate connection. */
00124         if (!hdhomerun_sock_connect(cs->sock, cs->actual_device_ip, HDHOMERUN_CONTROL_TCP_PORT, HDHOMERUN_CONTROL_CONNECT_TIMEOUT)) {
00125                 hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_connect_sock: failed to connect (%d)\n", hdhomerun_sock_getlasterror());
00126                 hdhomerun_control_close_sock(cs);
00127                 return FALSE;
00128         }
00129 
00130         /* Success. */
00131         return TRUE;
00132 }
00133 
00134 uint32_t hdhomerun_control_get_device_id(struct hdhomerun_control_sock_t *cs)
00135 {
00136         if (!hdhomerun_control_connect_sock(cs)) {
00137                 hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_get_device_id: connect failed\n");
00138                 return 0;
00139         }
00140 
00141         return cs->actual_device_id;
00142 }
00143 
00144 uint32_t hdhomerun_control_get_device_ip(struct hdhomerun_control_sock_t *cs)
00145 {
00146         if (!hdhomerun_control_connect_sock(cs)) {
00147                 hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_get_device_ip: connect failed\n");
00148                 return 0;
00149         }
00150 
00151         return cs->actual_device_ip;
00152 }
00153 
00154 uint32_t hdhomerun_control_get_device_id_requested(struct hdhomerun_control_sock_t *cs)
00155 {
00156         return cs->desired_device_id;
00157 }
00158 
00159 uint32_t hdhomerun_control_get_device_ip_requested(struct hdhomerun_control_sock_t *cs)
00160 {
00161         return cs->desired_device_ip;
00162 }
00163 
00164 uint32_t hdhomerun_control_get_local_addr(struct hdhomerun_control_sock_t *cs)
00165 {
00166         if (!hdhomerun_control_connect_sock(cs)) {
00167                 hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_get_local_addr: connect failed\n");
00168                 return 0;
00169         }
00170 
00171         uint32_t addr = hdhomerun_sock_getsockname_addr(cs->sock);
00172         if (addr == 0) {
00173                 hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_get_local_addr: getsockname failed (%d)\n", hdhomerun_sock_getlasterror());
00174                 return 0;
00175         }
00176 
00177         return addr;
00178 }
00179 
00180 static bool_t hdhomerun_control_send_sock(struct hdhomerun_control_sock_t *cs, struct hdhomerun_pkt_t *tx_pkt)
00181 {
00182         if (!hdhomerun_sock_send(cs->sock, tx_pkt->start, tx_pkt->end - tx_pkt->start, HDHOMERUN_CONTROL_SEND_TIMEOUT)) {
00183                 hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_send_sock: send failed (%d)\n", hdhomerun_sock_getlasterror());
00184                 hdhomerun_control_close_sock(cs);
00185                 return FALSE;
00186         }
00187 
00188         return TRUE;
00189 }
00190 
00191 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)
00192 {
00193         uint64_t stop_time = getcurrenttime() + recv_timeout;
00194         hdhomerun_pkt_reset(rx_pkt);
00195 
00196         while (1) {
00197                 uint64_t current_time = getcurrenttime();
00198                 if (current_time >= stop_time) {
00199                         hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_recv_sock: timeout\n");
00200                         hdhomerun_control_close_sock(cs);
00201                         return FALSE;
00202                 }
00203 
00204                 size_t length = rx_pkt->limit - rx_pkt->end;
00205                 if (!hdhomerun_sock_recv(cs->sock, rx_pkt->end, &length, stop_time - current_time)) {
00206                         hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_recv_sock: recv failed (%d)\n", hdhomerun_sock_getlasterror());
00207                         hdhomerun_control_close_sock(cs);
00208                         return FALSE;
00209                 }
00210 
00211                 rx_pkt->end += length;
00212 
00213                 int ret = hdhomerun_pkt_open_frame(rx_pkt, ptype);
00214                 if (ret < 0) {
00215                         hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_recv_sock: frame error\n");
00216                         hdhomerun_control_close_sock(cs);
00217                         return FALSE;
00218                 }
00219                 if (ret > 0) {
00220                         return TRUE;
00221                 }
00222         }
00223 }
00224 
00225 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)
00226 {
00227         hdhomerun_pkt_seal_frame(tx_pkt, type);
00228 
00229         int i;
00230         for (i = 0; i < 2; i++) {
00231                 if (cs->sock == HDHOMERUN_SOCK_INVALID) {
00232                         if (!hdhomerun_control_connect_sock(cs)) {
00233                                 hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_send_recv: connect failed\n");
00234                                 return -1;
00235                         }
00236                 }
00237 
00238                 if (!hdhomerun_control_send_sock(cs, tx_pkt)) {
00239                         continue;
00240                 }
00241                 if (!rx_pkt) {
00242                         return 1;
00243                 }
00244 
00245                 uint16_t rsp_type;
00246                 if (!hdhomerun_control_recv_sock(cs, rx_pkt, &rsp_type, recv_timeout)) {
00247                         continue;
00248                 }
00249                 if (rsp_type != type + 1) {
00250                         hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_send_recv: unexpected frame type\n");
00251                         hdhomerun_control_close_sock(cs);
00252                         continue;
00253                 }
00254 
00255                 return 1;
00256         }
00257 
00258         hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_send_recv: failed\n");
00259         return -1;
00260 }
00261 
00262 int hdhomerun_control_send_recv(struct hdhomerun_control_sock_t *cs, struct hdhomerun_pkt_t *tx_pkt, struct hdhomerun_pkt_t *rx_pkt, uint16_t type)
00263 {
00264         return hdhomerun_control_send_recv_internal(cs, tx_pkt, rx_pkt, type, HDHOMERUN_CONTROL_RECV_TIMEOUT);
00265 }
00266 
00267 static int hdhomerun_control_get_set(struct hdhomerun_control_sock_t *cs, const char *name, const char *value, uint32_t lockkey, char **pvalue, char **perror)
00268 {
00269         struct hdhomerun_pkt_t *tx_pkt = &cs->tx_pkt;
00270         struct hdhomerun_pkt_t *rx_pkt = &cs->rx_pkt;
00271 
00272         /* Request. */
00273         hdhomerun_pkt_reset(tx_pkt);
00274 
00275         int name_len = (int)strlen(name) + 1;
00276         if (tx_pkt->end + 3 + name_len > tx_pkt->limit) {
00277                 hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_get_set: request too long\n");
00278                 return -1;
00279         }
00280         hdhomerun_pkt_write_u8(tx_pkt, HDHOMERUN_TAG_GETSET_NAME);
00281         hdhomerun_pkt_write_var_length(tx_pkt, name_len);
00282         hdhomerun_pkt_write_mem(tx_pkt, (const void *)name, name_len);
00283 
00284         if (value) {
00285                 int value_len = (int)strlen(value) + 1;
00286                 if (tx_pkt->end + 3 + value_len > tx_pkt->limit) {
00287                         hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_get_set: request too long\n");
00288                         return -1;
00289                 }
00290                 hdhomerun_pkt_write_u8(tx_pkt, HDHOMERUN_TAG_GETSET_VALUE);
00291                 hdhomerun_pkt_write_var_length(tx_pkt, value_len);
00292                 hdhomerun_pkt_write_mem(tx_pkt, (const void *)value, value_len);
00293         }
00294 
00295         if (lockkey != 0) {
00296                 if (tx_pkt->end + 6 > tx_pkt->limit) {
00297                         hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_get_set: request too long\n");
00298                         return -1;
00299                 }
00300                 hdhomerun_pkt_write_u8(tx_pkt, HDHOMERUN_TAG_GETSET_LOCKKEY);
00301                 hdhomerun_pkt_write_var_length(tx_pkt, 4);
00302                 hdhomerun_pkt_write_u32(tx_pkt, lockkey);
00303         }
00304 
00305         /* Send/Recv. */
00306         if (hdhomerun_control_send_recv_internal(cs, tx_pkt, rx_pkt, HDHOMERUN_TYPE_GETSET_REQ, HDHOMERUN_CONTROL_RECV_TIMEOUT) < 0) {
00307                 hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_get_set: send/recv error\n");
00308                 return -1;
00309         }
00310 
00311         /* Response. */
00312         while (1) {
00313                 uint8_t tag;
00314                 size_t len;
00315                 uint8_t *next = hdhomerun_pkt_read_tlv(rx_pkt, &tag, &len);
00316                 if (!next) {
00317                         break;
00318                 }
00319 
00320                 switch (tag) {
00321                 case HDHOMERUN_TAG_GETSET_VALUE:
00322                         if (pvalue) {
00323                                 *pvalue = (char *)rx_pkt->pos;
00324                                 rx_pkt->pos[len] = 0;
00325                         }
00326                         if (perror) {
00327                                 *perror = NULL;
00328                         }
00329                         return 1;
00330 
00331                 case HDHOMERUN_TAG_ERROR_MESSAGE:
00332                         rx_pkt->pos[len] = 0;
00333                         hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_get_set: %s\n", rx_pkt->pos);
00334 
00335                         if (pvalue) {
00336                                 *pvalue = NULL;
00337                         }
00338                         if (perror) {
00339                                 *perror = (char *)rx_pkt->pos;
00340                         }
00341 
00342                         return 0;
00343                 }
00344 
00345                 rx_pkt->pos = next;
00346         }
00347 
00348         hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_get_set: missing response tags\n");
00349         return -1;
00350 }
00351 
00352 int hdhomerun_control_get(struct hdhomerun_control_sock_t *cs, const char *name, char **pvalue, char **perror)
00353 {
00354         return hdhomerun_control_get_set(cs, name, NULL, 0, pvalue, perror);
00355 }
00356 
00357 int hdhomerun_control_set(struct hdhomerun_control_sock_t *cs, const char *name, const char *value, char **pvalue, char **perror)
00358 {
00359         return hdhomerun_control_get_set(cs, name, value, 0, pvalue, perror);
00360 }
00361 
00362 int hdhomerun_control_set_with_lockkey(struct hdhomerun_control_sock_t *cs, const char *name, const char *value, uint32_t lockkey, char **pvalue, char **perror)
00363 {
00364         return hdhomerun_control_get_set(cs, name, value, lockkey, pvalue, perror);
00365 }
00366 
00367 int hdhomerun_control_upgrade(struct hdhomerun_control_sock_t *cs, FILE *upgrade_file)
00368 {
00369         struct hdhomerun_pkt_t *tx_pkt = &cs->tx_pkt;
00370         struct hdhomerun_pkt_t *rx_pkt = &cs->rx_pkt;
00371         uint32_t sequence = 0;
00372 
00373         /* Upload. */
00374         while (1) {
00375                 uint8_t data[256];
00376                 size_t length = fread(data, 1, 256, upgrade_file);
00377                 if (length == 0) {
00378                         break;
00379                 }
00380 
00381                 hdhomerun_pkt_reset(tx_pkt);
00382                 hdhomerun_pkt_write_u32(tx_pkt, sequence);
00383                 hdhomerun_pkt_write_mem(tx_pkt, data, length);
00384 
00385                 if (hdhomerun_control_send_recv_internal(cs, tx_pkt, NULL, HDHOMERUN_TYPE_UPGRADE_REQ, 0) < 0) {
00386                         hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_upgrade: send/recv failed\n");
00387                         return -1;
00388                 }
00389 
00390                 sequence += (uint32_t)length;
00391         }
00392 
00393         if (sequence == 0) {
00394                 /* No data in file. Error, but no need to close connection. */
00395                 hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_upgrade: zero length file\n");
00396                 return 0;
00397         }
00398 
00399         /* Execute upgrade. */
00400         hdhomerun_pkt_reset(tx_pkt);
00401         hdhomerun_pkt_write_u32(tx_pkt, 0xFFFFFFFF);
00402 
00403         if (hdhomerun_control_send_recv_internal(cs, tx_pkt, rx_pkt, HDHOMERUN_TYPE_UPGRADE_REQ, HDHOMERUN_CONTROL_UPGRADE_TIMEOUT) < 0) {
00404                 hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_upgrade: send/recv failed\n");
00405                 return -1;
00406         }
00407 
00408         /* Check response. */
00409         while (1) {
00410                 uint8_t tag;
00411                 size_t len;
00412                 uint8_t *next = hdhomerun_pkt_read_tlv(rx_pkt, &tag, &len);
00413                 if (!next) {
00414                         break;
00415                 }
00416 
00417                 switch (tag) {
00418                 case HDHOMERUN_TAG_ERROR_MESSAGE:
00419                         rx_pkt->pos[len] = 0;
00420                         hdhomerun_debug_printf(cs->dbg, "hdhomerun_control_upgrade: %s\n", (char *)rx_pkt->pos);
00421                         return 0;
00422 
00423                 default:
00424                         break;
00425                 }
00426 
00427                 rx_pkt->pos = next;
00428         }
00429 
00430         return 1;
00431 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends