videohubctrl can be used to control Blackmagic Design Videohub SDI router device over the network.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

cmd.c 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355
  1. /*
  2. * === Commands processing ===
  3. *
  4. * Blackmagic Design Videohub control application
  5. * Copyright (C) 2014 Unix Solutions Ltd.
  6. * Written by Georgi Chorbadzhiyski
  7. *
  8. * Released under MIT license.
  9. * See LICENSE-MIT.txt for license terms.
  10. *
  11. */
  12. #include <stdio.h>
  13. #include <stdlib.h>
  14. #include <string.h>
  15. #include "data.h"
  16. #include "cmd.h"
  17. #include "util.h"
  18. enum cmd_flags {
  19. PARSE_NONE = (1 << 0), /* The result if this command needs no parsing */
  20. PARSE_CUSTOM = (1 << 1), /* Use custom parser for this command */
  21. PARSE_SLOT_TXT = (1 << 2), /* Parse [slot_num] [slot_text] */
  22. PARSE_SLOT_DEST = (1 << 3), /* Parse [slot_num] [dest_slot] */
  23. };
  24. static struct videohub_commands {
  25. enum vcmd cmd;
  26. const char *txt;
  27. unsigned int flags;
  28. } videohub_commands[] = {
  29. { CMD_PROTOCOL_PREAMBLE, "PROTOCOL PREAMBLE", PARSE_CUSTOM },
  30. { CMD_VIDEOHUB_DEVICE, "VIDEOHUB DEVICE", PARSE_CUSTOM },
  31. { CMD_INPUT_LABELS, "INPUT LABELS", PARSE_SLOT_TXT },
  32. { CMD_OUTPUT_LABELS, "OUTPUT LABELS", PARSE_SLOT_TXT },
  33. { CMD_VIDEO_OUTPUT_LOCKS, "VIDEO OUTPUT LOCKS", PARSE_SLOT_TXT },
  34. { CMD_VIDEO_OUTPUT_ROUTING, "VIDEO OUTPUT ROUTING", PARSE_SLOT_DEST },
  35. { CMD_VIDEO_INPUT_STATUS, "VIDEO INPUT STATUS", PARSE_SLOT_TXT },
  36. { CMD_VIDEO_OUTPUT_STATUS, "VIDEO OUTPUT STATUS", PARSE_SLOT_TXT },
  37. { CMD_PING, "PING", PARSE_NONE },
  38. { CMD_ACK, "ACK", PARSE_NONE },
  39. { CMD_NAK, "NAK", PARSE_NONE },
  40. };
  41. static const char *get_cmd_text(enum vcmd cmd) {
  42. unsigned int i;
  43. for (i = 0; i < ARRAY_SIZE(videohub_commands); i++) {
  44. if (videohub_commands[i].cmd == cmd)
  45. return videohub_commands[i].txt;
  46. }
  47. return "";
  48. }
  49. static char *parse_text(char *line, char *cmd) {
  50. char *parsed_text = strstr(line, cmd);
  51. if (parsed_text == line) {
  52. return parsed_text + strlen(cmd);
  53. }
  54. return NULL;
  55. }
  56. bool parse_command(struct videohub_data *data, char *cmd) {
  57. unsigned int i;
  58. bool ret = true;
  59. if (!strlen(cmd))
  60. return false;
  61. struct videohub_commands *v = NULL;
  62. for (i = 0; i < ARRAY_SIZE(videohub_commands); i++) {
  63. if (!videohub_commands[i].txt)
  64. continue;
  65. if (strstr(cmd, videohub_commands[i].txt) == cmd) {
  66. v = &videohub_commands[i];
  67. break;
  68. }
  69. }
  70. if (!v) {
  71. q("WARNING: Videohub sent unknown command!\n");
  72. q(" Please report this command to author's email: georgi@unixsol.org\n");
  73. q(" You may use -q or --quiet to suppress the message.\n");
  74. q("---------8<-----------8<----------- cut here ---------8<------------8<---------\n");
  75. q("%s\n", cmd);
  76. q("---------8<-----------8<----------- cut here ---------8<------------8<---------\n");
  77. return false;
  78. }
  79. d("debug: Got '%s' command.\n", v->txt);
  80. if (debug > 1)
  81. d("----\n%s\n----\n", cmd);
  82. char *p, *cmd_data = xstrdup( cmd + strlen(v->txt) + 2 ); // +2 to compensate for :\n at the end of the command
  83. // Split line by line
  84. char *line, *saveptr = NULL;
  85. for(i = 0, line = strtok_r(cmd_data, "\n", &saveptr); line; line = strtok_r(NULL, "\n", &saveptr), i++) {
  86. // Parse command data response looking like that: "[slot_pos] [slot_data]"
  87. char *slot_data = NULL;
  88. unsigned int slot_pos = 0, dest_pos = 0;
  89. if (v->flags & (PARSE_SLOT_TXT | PARSE_SLOT_DEST)) {
  90. slot_data = strchr(line, ' ');
  91. if (!slot_data)
  92. continue;
  93. slot_data[0] = '\0'; // Separate slot_pos from slot_data
  94. slot_data++;
  95. slot_pos = strtoul(line, NULL, 10);
  96. if (slot_pos + 1 > data->outputs.num) {
  97. q("WARNING: %s - invalid slot %u\n", v->txt, slot_pos);
  98. continue;
  99. }
  100. }
  101. if (v->flags & PARSE_SLOT_DEST) {
  102. dest_pos = strtoul(slot_data, NULL, 10);
  103. if (dest_pos + 1 > data->inputs.num) {
  104. q("WARNING: %s - invalid dest %u\n", v->txt, dest_pos);
  105. continue;
  106. }
  107. }
  108. // Parse commands
  109. switch (v->cmd) {
  110. case CMD_PROTOCOL_PREAMBLE:
  111. if ((p = parse_text(line, "Version: ")))
  112. snprintf(data->device.protocol_ver, sizeof(data->device.protocol_ver), "%s", p);
  113. break;
  114. case CMD_VIDEOHUB_DEVICE:
  115. if ((p = parse_text(line, "Device present: "))) {
  116. data->device.dev_present = streq(p, "true");
  117. data->device.needs_fw_update = streq(p, "needs_update");
  118. }
  119. if ((p = parse_text(line, "Model name: ")))
  120. snprintf(data->device.model_name, sizeof(data->device.model_name), "%s", p);
  121. if ((p = parse_text(line, "Unique ID: ")))
  122. snprintf(data->device.unique_id, sizeof(data->device.unique_id) , "%s", p);
  123. if ((p = parse_text(line, "Video inputs: ")))
  124. data->inputs.num = strtoul(p, NULL, 10);
  125. if ((p = parse_text(line, "Video processing units: ")))
  126. data->device.num_video_processing_units = strtoul(p, NULL, 10);
  127. if ((p = parse_text(line, "Video outputs: ")))
  128. data->outputs.num = strtoul(p, NULL, 10);
  129. if ((p = parse_text(line, "Video monitoring output: ")))
  130. data->device.num_video_monitoring_outputs = strtoul(p, NULL, 10);
  131. if ((p = parse_text(line, "Serial ports: ")))
  132. data->device.num_serial_ports = strtoul(p, NULL, 10);
  133. break;
  134. case CMD_INPUT_LABELS:
  135. snprintf(data->inputs.port[slot_pos].name, sizeof(data->inputs.port[slot_pos].name), "%s", slot_data);
  136. break;
  137. case CMD_OUTPUT_LABELS:
  138. snprintf(data->outputs.port[slot_pos].name, sizeof(data->outputs.port[slot_pos].name), "%s", slot_data);
  139. break;
  140. case CMD_VIDEO_INPUT_STATUS:
  141. snprintf(data->inputs.port[slot_pos].status, sizeof(data->inputs.port[slot_pos].status), "%s", slot_data);
  142. break;
  143. case CMD_VIDEO_OUTPUT_STATUS:
  144. snprintf(data->outputs.port[slot_pos].status, sizeof(data->outputs.port[slot_pos].status), "%s", slot_data);
  145. break;
  146. case CMD_VIDEO_OUTPUT_LOCKS:
  147. switch (slot_data[0]) {
  148. case 'O': data->outputs.port[slot_pos].lock = PORT_LOCKED; break;
  149. case 'L': data->outputs.port[slot_pos].lock = PORT_LOCKED_OTHER; break;
  150. default : data->outputs.port[slot_pos].lock = PORT_UNLOCKED; break;
  151. }
  152. break;
  153. case CMD_VIDEO_OUTPUT_ROUTING:
  154. data->outputs.port[slot_pos].routed_to = dest_pos;
  155. case CMD_PING:
  156. case CMD_ACK:
  157. // Do nothing
  158. break;
  159. case CMD_NAK:
  160. ret = false;
  161. break;
  162. }
  163. }
  164. free(cmd_data);
  165. return ret;
  166. }
  167. int parse_text_buffer(struct videohub_data *data, char *cmd_buffer) {
  168. // The buffer contains only one command, no splitting is needed
  169. if (!strstr(cmd_buffer, "\n\n"))
  170. return parse_command(data, cmd_buffer);
  171. // Split commands and parse them one by one
  172. int ok_commands = 0;
  173. char *buf_copy = xstrdup(cmd_buffer);
  174. char *newcmd, *cmd = buf_copy;
  175. while(1) {
  176. newcmd = strstr(cmd, "\n\n"); // Find next command
  177. if (!newcmd) {
  178. if (parse_command(data, cmd)) // Parse current command
  179. ok_commands++;
  180. break;
  181. }
  182. newcmd[0] = '\0'; // Terminate previous command
  183. if (parse_command(data, cmd)) // Parse previous command
  184. ok_commands++;
  185. cmd = newcmd + 2; // Advance cmd to the next command
  186. }
  187. free(buf_copy);
  188. return ok_commands;
  189. }
  190. // Try to find port with certain name, return 0 on not found, pos + 1 is found
  191. static int get_port_by_name(struct port_set *p, char *name) {
  192. unsigned int i;
  193. for(i = 0; i < p->num; i++) {
  194. if (streq(name, p->port[i].name)) {
  195. return i + 1;
  196. }
  197. }
  198. return 0;
  199. }
  200. // Return 0 on error, number otherwise if it can parse the whole input
  201. static unsigned int my_atoi(char *txt) {
  202. char *endptr = NULL;
  203. if (!txt)
  204. return 0;
  205. unsigned int ret = strtoul(txt, &endptr, 10);
  206. if (endptr == txt || *endptr)
  207. return 0;
  208. return ret;
  209. }
  210. void prepare_cmd_entry(struct videohub_data *d, struct vcmd_entry *e) {
  211. e->port_no1 = my_atoi(e->param1);
  212. e->port_no2 = my_atoi(e->param2);
  213. switch (e->cmd) {
  214. case CMD_INPUT_LABELS:
  215. if (e->port_no1 == 0 || e->port_no1 > d->inputs.num) {
  216. e->port_no1 = get_port_by_name(&d->inputs, e->param1);
  217. if (!e->port_no1)
  218. die("Unknown input port number/name: %s", e->param1);
  219. }
  220. break;
  221. case CMD_OUTPUT_LABELS:
  222. case CMD_VIDEO_OUTPUT_LOCKS:
  223. if (e->port_no1 == 0 || e->port_no1 > d->outputs.num) {
  224. e->port_no1 = get_port_by_name(&d->outputs, e->param1);
  225. if (!e->port_no1)
  226. die("Unknown output port number/name: %s", e->param1);
  227. }
  228. e->lock = d->outputs.port[e->port_no1 - 1].lock;
  229. break;
  230. case CMD_VIDEO_OUTPUT_ROUTING:
  231. if (e->port_no1 == 0 || e->port_no1 > d->outputs.num) {
  232. e->port_no1 = get_port_by_name(&d->outputs, e->param1);
  233. if (!e->port_no1)
  234. die("Unknown output port number/name: %s", e->param1);
  235. }
  236. if (e->port_no2 == 0 || e->port_no2 > d->inputs.num) {
  237. e->port_no2 = get_port_by_name(&d->inputs, e->param2);
  238. if (!e->port_no2)
  239. die("Unknown input port number/name: %s", e->param2);
  240. }
  241. break;
  242. case CMD_VIDEO_INPUT_STATUS:
  243. case CMD_VIDEO_OUTPUT_STATUS:
  244. case CMD_PROTOCOL_PREAMBLE:
  245. case CMD_VIDEOHUB_DEVICE:
  246. case CMD_PING:
  247. case CMD_ACK:
  248. case CMD_NAK:
  249. break;
  250. }
  251. }
  252. void format_cmd_text(struct vcmd_entry *e, char *buf, unsigned int bufsz) {
  253. switch (e->cmd) {
  254. case CMD_INPUT_LABELS:
  255. snprintf(buf, bufsz, "%s:\n%u %s\n\n", get_cmd_text(e->cmd),
  256. e->port_no1 - 1, e->param2);
  257. break;
  258. case CMD_OUTPUT_LABELS:
  259. snprintf(buf, bufsz, "%s:\n%u %s\n\n", get_cmd_text(e->cmd),
  260. e->port_no1 - 1, e->param2);
  261. break;
  262. case CMD_VIDEO_OUTPUT_LOCKS:
  263. snprintf(buf, bufsz, "%s:\n%u %s\n\n", get_cmd_text(e->cmd),
  264. e->port_no1 - 1, e->do_lock ? "O" : (e->lock == PORT_LOCKED_OTHER ? "F" : "U"));
  265. break;
  266. case CMD_VIDEO_OUTPUT_ROUTING:
  267. snprintf(buf, bufsz, "%s:\n%u %u\n\n", get_cmd_text(e->cmd),
  268. e->port_no1 - 1, e->port_no2 - 1);
  269. break;
  270. case CMD_VIDEO_INPUT_STATUS:
  271. case CMD_VIDEO_OUTPUT_STATUS:
  272. case CMD_PROTOCOL_PREAMBLE:
  273. case CMD_VIDEOHUB_DEVICE:
  274. case CMD_PING:
  275. case CMD_ACK:
  276. case CMD_NAK:
  277. break;
  278. }
  279. }
  280. void show_cmd(struct videohub_data *d, struct vcmd_entry *e) {
  281. const char *prefix = "videohub: ";
  282. switch (e->cmd) {
  283. case CMD_INPUT_LABELS:
  284. printf("%srename video input %d - \"%s\" to \"%s\"\n",
  285. prefix,
  286. e->port_no1, d->inputs.port[e->port_no1 - 1].name,
  287. e->param2
  288. );
  289. break;
  290. case CMD_OUTPUT_LABELS:
  291. printf("%srename video output %d - \"%s\" to \"%s\"\n",
  292. prefix,
  293. e->port_no1, d->outputs.port[e->port_no1 - 1].name,
  294. e->param2
  295. );
  296. break;
  297. case CMD_VIDEO_OUTPUT_LOCKS:
  298. printf("%s%s video output %d - \"%s\"\n",
  299. prefix,
  300. e->do_lock ? "lock" : (e->lock == PORT_LOCKED_OTHER ? "force unlock" : "unlock"),
  301. e->port_no1, d->outputs.port[e->port_no1 - 1].name
  302. );
  303. break;
  304. case CMD_VIDEO_OUTPUT_ROUTING:
  305. printf("%sset video output %d \"%s\" to read from input %d \"%s\"\n",
  306. prefix,
  307. e->port_no1, d->outputs.port[e->port_no1 - 1].name,
  308. e->port_no2, d->inputs.port [e->port_no2 - 1].name
  309. );
  310. break;
  311. case CMD_VIDEO_INPUT_STATUS:
  312. case CMD_VIDEO_OUTPUT_STATUS:
  313. case CMD_PROTOCOL_PREAMBLE:
  314. case CMD_VIDEOHUB_DEVICE:
  315. case CMD_PING:
  316. case CMD_ACK:
  317. case CMD_NAK:
  318. break;
  319. }
  320. }