|
MythTV
0.26-pre
|
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 }
1.7.6.1