1 /*
2 * libzvbi -- VBI proxy client
3 *
4 * Copyright (C) 2003, 2004 Tom Zoerner
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
15 *
16 * You should have received a copy of the GNU Library General Public
17 * License along with this library; if not, write to the
18 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301 USA.
20 */
21
22 /* $Id: proxy-client.c,v 1.18 2008/02/19 00:35:21 mschimek Exp $ */
23
24 static const char rcsid[] =
25 "$Id: proxy-client.c,v 1.18 2008/02/19 00:35:21 mschimek Exp $";
26
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
30
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <math.h>
35 #include <errno.h>
36 #include <fcntl.h>
37 #include <unistd.h>
38 #include <assert.h>
39 #include <sys/time.h>
40 #include <sys/types.h>
41
42 #include "vbi.h"
43 #include "io.h"
44 #include "bcd.h"
45 #include "misc.h"
46
47 #include "proxy-msg.h"
48 #include "proxy-client.h"
49
50 #ifdef ENABLE_PROXY
51
52 #define dprintf1(fmt, arg...) do {if (vpc->trace >= 1) fprintf(stderr, "proxy-client: " fmt, ## arg);} while(0)
53 #define dprintf2(fmt, arg...) do {if (vpc->trace >= 2) fprintf(stderr, "proxy-client: " fmt, ## arg);} while(0)
54
55 /* ----------------------------------------------------------------------------
56 ** Declaration of types of internal state variables
57 */
58 typedef enum
59 {
60 CLNT_STATE_NULL,
61 CLNT_STATE_ERROR,
62 CLNT_STATE_WAIT_CON_CNF,
63 CLNT_STATE_WAIT_IDLE,
64 CLNT_STATE_WAIT_SRV_CNF,
65 CLNT_STATE_WAIT_RPC_REPLY,
66 CLNT_STATE_CAPTURING,
67 } PROXY_CLIENT_STATE;
68
69 struct vbi_proxy_client
70 {
71 unsigned int services;
72 int strict;
73 int buffer_count;
74 int scanning;
75 unsigned int trace;
76 VBI_PROXY_CLIENT_FLAGS client_flags;
77 VBI_PROXY_DAEMON_FLAGS daemon_flags;
78 VBI_DRIVER_API_REV vbi_api_revision;
79 vbi_raw_decoder dec;
80
81 int chn_scanning;
82 int chn_prio;
83 vbi_bool has_token;
84
85 vbi_bool sliced_ind;
86 vbi_capture_buffer raw_buf;
87 vbi_capture_buffer slice_buf;
88 vbi_capture capt_api;
89
90 VBI_PROXY_EV_TYPE ev_mask;
91
92 PROXY_CLIENT_STATE state;
93 VBIPROXY_MSG_STATE io;
94 VBIPROXY_MSG * p_client_msg;
95 int max_client_msg_size;
96 vbi_bool endianSwap;
97 unsigned long rxTotal;
98 unsigned long rxStartTime;
99 char * p_srv_host;
100 char * p_srv_port;
101 char * p_client_name;
102 char * p_errorstr;
103
104 VBI_PROXY_CLIENT_CALLBACK * p_callback_func;
105 void * p_callback_data;
106 };
107
108 /* timeout for RPC to proxy daemon (for parameter changes) */
109 #define RPC_TIMEOUT_MSECS 5000
110 /* timeout for waiting until ongoing read is completed
111 ** used to "free" the socket before sending parameter requests */
112 #define IDLE_TIMEOUT_MSECS 2000
113
114 /* helper macro */
115 #define VBI_RAW_SERVICES(SRV) (((SRV) & (VBI_SLICED_VBI_625 | VBI_SLICED_VBI_525)) != 0)
116
117 /* ----------------------------------------------------------------------------
118 ** Open client connection
119 ** - automatically chooses the optimum transport: TCP/IP or pipe for local
120 ** - since the socket is made non-blocking, the result of the connect is not
121 ** yet available when the function finishes; the caller has to wait for
122 ** completion with select() and then query the socket error status
123 */
proxy_client_connect_server(vbi_proxy_client * vpc)124 static vbi_bool proxy_client_connect_server( vbi_proxy_client * vpc )
125 {
126 vbi_bool use_tcp_ip;
127 int sock_fd;
128 vbi_bool result = FALSE;
129
130 use_tcp_ip = FALSE;
131
132 /* check if a server address has been configured */
133 if ( ((vpc->p_srv_host != NULL) || (use_tcp_ip == FALSE)) &&
134 (vpc->p_srv_port != NULL))
135 {
136 sock_fd = vbi_proxy_msg_connect_to_server(use_tcp_ip, vpc->p_srv_host, vpc->p_srv_port, &vpc->p_errorstr);
137 if (sock_fd != -1)
138 {
139 /* initialize IO state */
140 memset(&vpc->io, 0, sizeof(vpc->io));
141 vpc->io.sock_fd = sock_fd;
142 vpc->io.lastIoTime = time(NULL);
143 vpc->rxStartTime = vpc->io.lastIoTime;
144 vpc->rxTotal = 0;
145
146 result = TRUE;
147 }
148 }
149 else
150 {
151 dprintf1("connect_server: hostname or port not configured\n");
152 if (use_tcp_ip && (vpc->p_srv_host == NULL))
153 asprintf(&vpc->p_errorstr, _("Server hostname not configured."));
154 else if (vpc->p_srv_port == NULL)
155 asprintf(&vpc->p_errorstr, _("Server port not configured."));
156 }
157 return result;
158 }
159
160 /* ----------------------------------------------------------------------------
161 ** Allocate buffer for client/servier message exchange
162 ** - buffer is allocated statically, large enough for all expected messages
163 */
proxy_client_alloc_msg_buf(vbi_proxy_client * vpc)164 static vbi_bool proxy_client_alloc_msg_buf( vbi_proxy_client * vpc )
165 {
166 vbi_bool result;
167 size_t msg_size;
168
169 msg_size = sizeof(VBIPROXY_MSG_BODY);
170
171 if ( (vpc->state == CLNT_STATE_CAPTURING) &&
172 (vpc->services != 0) )
173 {
174 /* XXX TODO allow both raw and sliced */
175 if (VBI_RAW_SERVICES(vpc->services))
176 msg_size = VBIPROXY_SLICED_IND_SIZE(0, vpc->dec.count[0] + vpc->dec.count[1]);
177 else
178 msg_size = VBIPROXY_SLICED_IND_SIZE(vpc->dec.count[0] + vpc->dec.count[1], 0);
179
180 if (msg_size < sizeof(VBIPROXY_MSG_BODY))
181 msg_size = sizeof(VBIPROXY_MSG_BODY);
182 }
183 else
184 msg_size = sizeof(VBIPROXY_MSG_BODY);
185
186 msg_size += VBIPROXY_MSG_BODY_OFFSET;
187
188 if (((int) msg_size != vpc->max_client_msg_size)
189 || (vpc->p_client_msg == NULL))
190 {
191 if (vpc->p_client_msg != NULL)
192 free(vpc->p_client_msg);
193
194 dprintf2("alloc_msg_buf: allocate buffer for "
195 "max. %lu bytes\n", (unsigned long) msg_size);
196 vpc->max_client_msg_size = msg_size;
197 vpc->p_client_msg = malloc(msg_size);
198
199 if (vpc->p_client_msg == NULL)
200 {
201 asprintf(&vpc->p_errorstr, _("Virtual memory exhausted."));
202 result = FALSE;
203 }
204 else
205 result = TRUE;
206 }
207 else
208 result = TRUE;
209
210 return result;
211 }
212
213 /* ----------------------------------------------------------------------------
214 ** Checks the size of a message from server to client
215 */
proxy_client_check_msg(vbi_proxy_client * vpc,uint len,VBIPROXY_MSG * pMsg)216 static vbi_bool proxy_client_check_msg( vbi_proxy_client * vpc, uint len,
217 VBIPROXY_MSG * pMsg )
218 {
219 VBIPROXY_MSG_HEADER * pHead = &pMsg->head;
220 VBIPROXY_MSG_BODY * pBody = &pMsg->body;
221 vbi_bool result = FALSE;
222
223 /*if (vpc->p_client_msg->head.type != MSG_TYPE_SLICED_IND) */
224 dprintf2("check_msg: recv msg type %d, len %d (%s)\n", pHead->type, pHead->len, vbi_proxy_msg_debug_get_type_str(pHead->type));
225
226 switch (pHead->type)
227 {
228 case MSG_TYPE_CONNECT_CNF:
229 if ( (len == sizeof(VBIPROXY_MSG_HEADER) + sizeof(pBody->connect_cnf)) &&
230 (memcmp(pBody->connect_cnf.magics.protocol_magic, VBIPROXY_MAGIC_STR, VBIPROXY_MAGIC_LEN) == 0) )
231 {
232 if (pBody->connect_cnf.magics.endian_magic == VBIPROXY_ENDIAN_MAGIC)
233 { /* endian type matches -> no swapping required */
234 vpc->endianSwap = FALSE;
235 }
236 else if (pBody->connect_cnf.magics.endian_magic == VBIPROXY_ENDIAN_MISMATCH)
237 { /* endian type does not match -> convert "endianess" of all msg elements > 1 byte */
238 /* enable byte swapping for all following messages */
239 vpc->endianSwap = TRUE;
240 }
241 result = TRUE;
242 }
243 break;
244
245 case MSG_TYPE_CONNECT_REJ:
246 result = ( (len == sizeof(VBIPROXY_MSG_HEADER) + sizeof(pBody->connect_rej)) &&
247 (memcmp(pBody->connect_rej.magics.protocol_magic, VBIPROXY_MAGIC_STR, VBIPROXY_MAGIC_LEN) == 0) );
248 break;
249
250 case MSG_TYPE_SLICED_IND:
251 result = (len == sizeof(VBIPROXY_MSG_HEADER) +
252 VBIPROXY_SLICED_IND_SIZE(pBody->sliced_ind.sliced_lines, pBody->sliced_ind.raw_lines));
253 break;
254
255 case MSG_TYPE_SERVICE_CNF:
256 result = (len == sizeof(VBIPROXY_MSG_HEADER) + sizeof(pBody->service_cnf));
257 break;
258
259 case MSG_TYPE_SERVICE_REJ:
260 result = (len == sizeof(VBIPROXY_MSG_HEADER) + sizeof(pBody->service_rej));
261 break;
262
263 case MSG_TYPE_CLOSE_REQ:
264 result = (len == sizeof(VBIPROXY_MSG_HEADER));
265 break;
266
267 case MSG_TYPE_CHN_TOKEN_CNF:
268 result = (len == sizeof(VBIPROXY_MSG_HEADER) + sizeof(pBody->chn_token_cnf));
269 break;
270
271 case MSG_TYPE_CHN_TOKEN_IND:
272 result = (len == sizeof(VBIPROXY_MSG_HEADER) + sizeof(pBody->chn_token_ind));
273 break;
274
275 case MSG_TYPE_CHN_NOTIFY_CNF:
276 result = (len == sizeof(VBIPROXY_MSG_HEADER) + sizeof(pBody->chn_notify_cnf));
277 break;
278
279 case MSG_TYPE_CHN_SUSPEND_CNF:
280 result = (len == sizeof(VBIPROXY_MSG_HEADER) + sizeof(pBody->chn_suspend_cnf));
281 break;
282
283 case MSG_TYPE_CHN_SUSPEND_REJ:
284 result = (len == sizeof(VBIPROXY_MSG_HEADER) + sizeof(pBody->chn_suspend_rej));
285 break;
286
287 case MSG_TYPE_CHN_IOCTL_CNF:
288 result = (len == sizeof(VBIPROXY_MSG_HEADER) +
289 VBIPROXY_CHN_IOCTL_CNF_SIZE(pBody->chn_ioctl_cnf.arg_size));
290 break;
291
292 case MSG_TYPE_CHN_IOCTL_REJ:
293 result = (len == sizeof(VBIPROXY_MSG_HEADER) + sizeof(pBody->chn_ioctl_rej));
294 break;
295
296 case MSG_TYPE_CHN_RECLAIM_REQ:
297 result = (len == sizeof(VBIPROXY_MSG_HEADER) + sizeof(pBody->chn_reclaim_req));
298 break;
299
300 case MSG_TYPE_CHN_CHANGE_IND:
301 result = (len == sizeof(VBIPROXY_MSG_HEADER) + sizeof(pBody->chn_change_ind));
302 break;
303
304 case MSG_TYPE_CONNECT_REQ:
305 case MSG_TYPE_SERVICE_REQ:
306 case MSG_TYPE_CHN_TOKEN_REQ:
307 case MSG_TYPE_CHN_RECLAIM_CNF:
308 case MSG_TYPE_CHN_NOTIFY_REQ:
309 case MSG_TYPE_CHN_SUSPEND_REQ:
310 case MSG_TYPE_CHN_IOCTL_REQ:
311 case MSG_TYPE_DAEMON_PID_REQ:
312 case MSG_TYPE_DAEMON_PID_CNF:
313 dprintf1("check_msg: recv server msg type %d (%s)\n", pHead->type, vbi_proxy_msg_debug_get_type_str(pHead->type));
314 result = FALSE;
315 break;
316 default:
317 dprintf1("check_msg: unknown msg type %d\n", pHead->type);
318 result = FALSE;
319 break;
320 }
321
322 if (result == FALSE)
323 {
324 dprintf1("check_msg: illegal msg len %d for type %d (%s)\n", len, pHead->type, vbi_proxy_msg_debug_get_type_str(pHead->type));
325 errno = EMSGSIZE;
326 }
327
328 return result;
329 }
330
331 /* ----------------------------------------------------------------------------
332 ** Handle asynchronous messages from server
333 */
proxy_client_take_message(vbi_proxy_client * vpc)334 static vbi_bool proxy_client_take_message( vbi_proxy_client * vpc )
335 {
336 VBIPROXY_MSG_BODY * pMsg = &vpc->p_client_msg->body;
337 vbi_bool result = FALSE;
338
339 switch (vpc->p_client_msg->head.type)
340 {
341 case MSG_TYPE_SLICED_IND:
342 if (vpc->state == CLNT_STATE_CAPTURING)
343 {
344 /* XXX TODO check raw */
345 if ((int) pMsg->sliced_ind.sliced_lines > vpc->dec.count[0] + vpc->dec.count[1])
346 { /* more lines than req. for service -> would overflow the allocated slicer buffer
347 ** -> discard extra lines (should never happen; proxy checks for line counts) */
348 dprintf1("take_message: SLICED_IND: too many lines: %d > %d\n", pMsg->sliced_ind.sliced_lines, vpc->dec.count[0] + vpc->dec.count[1]);
349 pMsg->sliced_ind.sliced_lines = vpc->dec.count[0] + vpc->dec.count[1];
350 }
351 /*assert(vpc->sliced_ind == FALSE);*/
352 vpc->sliced_ind = TRUE;
353 result = TRUE;
354 }
355 else if ( (vpc->state == CLNT_STATE_WAIT_IDLE) ||
356 (vpc->state == CLNT_STATE_WAIT_SRV_CNF) ||
357 (vpc->state == CLNT_STATE_WAIT_RPC_REPLY) )
358 {
359 /* discard incoming data during service changes */
360 result = TRUE;
361 }
362 break;
363
364 case MSG_TYPE_CHN_TOKEN_IND:
365 if ( (vpc->state == CLNT_STATE_CAPTURING) ||
366 (vpc->state == CLNT_STATE_WAIT_IDLE) ||
367 (vpc->state == CLNT_STATE_WAIT_RPC_REPLY) )
368 {
369 /* XXX check if we're currently waiting for CNF for chn param change? */
370 vpc->has_token = TRUE;
371 vpc->ev_mask |= VBI_PROXY_EV_CHN_GRANTED;
372 result = TRUE;
373 }
374 break;
375
376 case MSG_TYPE_CHN_RECLAIM_REQ:
377 if (vpc->state >= CLNT_STATE_WAIT_IDLE)
378 {
379 /* XXX FIXME: if no callback registered reply immediately */
380 /* XXX FIXME? handle "has_token == FALSE": reply immediately? */
381 vpc->ev_mask |= VBI_PROXY_EV_CHN_RECLAIMED;
382 vpc->ev_mask &= ~VBI_PROXY_EV_CHN_GRANTED;
383 result = TRUE;
384 }
385 break;
386
387 case MSG_TYPE_CHN_CHANGE_IND:
388 dprintf1("channel change indication: new scanning %d\n", pMsg->chn_change_ind.scanning);
389 vpc->chn_scanning = pMsg->chn_change_ind.scanning;
390 /* schedule callback to be invoked for this event */
391 if ((pMsg->chn_change_ind.notify_flags & VBI_PROXY_CHN_FLUSH) != 0)
392 vpc->ev_mask |= VBI_PROXY_EV_CHN_CHANGED;
393 if ((pMsg->chn_change_ind.notify_flags & VBI_PROXY_CHN_NORM) != 0)
394 vpc->ev_mask |= VBI_PROXY_EV_NORM_CHANGED;
395 result = TRUE;
396 break;
397
398 case MSG_TYPE_CLOSE_REQ:
399 result = FALSE;
400 break;
401
402 case MSG_TYPE_CONNECT_CNF:
403 case MSG_TYPE_CONNECT_REJ:
404 case MSG_TYPE_SERVICE_CNF:
405 case MSG_TYPE_SERVICE_REJ:
406 case MSG_TYPE_CHN_TOKEN_CNF:
407 case MSG_TYPE_CHN_NOTIFY_CNF:
408 case MSG_TYPE_CHN_SUSPEND_CNF:
409 case MSG_TYPE_CHN_SUSPEND_REJ:
410 case MSG_TYPE_CHN_IOCTL_CNF:
411 case MSG_TYPE_CHN_IOCTL_REJ:
412 /* synchronous message - internal error */
413 dprintf1("take_message: error: handler called for RPC message reply %d (%s)\n", vpc->p_client_msg->head.type, vbi_proxy_msg_debug_get_type_str(vpc->p_client_msg->head.type));
414 result = FALSE;
415 break;
416
417 default:
418 break;
419 }
420
421 if ((result == FALSE) && (vpc->p_errorstr == NULL))
422 {
423 dprintf1("take_message: message type %d (len %d) not expected in state %d\n", vpc->p_client_msg->head.type, vpc->p_client_msg->head.len, vpc->state);
424 asprintf(&vpc->p_errorstr, _("Protocol error (unexpected message)."));
425 }
426
427 return result;
428 }
429
430 /* ----------------------------------------------------------------------------
431 ** Close client connection
432 */
proxy_client_close(vbi_proxy_client * vpc)433 static void proxy_client_close( vbi_proxy_client * vpc )
434 {
435 int save_errno;
436
437 if (vpc != NULL)
438 {
439 save_errno = errno;
440 vbi_proxy_msg_close_io(&vpc->io);
441
442 memset(&vpc->io, 0, sizeof(vpc->io));
443 vpc->io.sock_fd = -1;
444 vpc->io.lastIoTime = time(NULL);
445
446 if (vpc->state != CLNT_STATE_NULL)
447 {
448 vpc->state = CLNT_STATE_ERROR;
449 }
450 errno = save_errno;
451 }
452 else
453 dprintf1("proxy_client-close: illegal NULL ptr param");
454 }
455
456 /* ----------------------------------------------------------------------------
457 ** Wait for I/O event on socket with the given timeout
458 */
proxy_client_wait_select(vbi_proxy_client * vpc,struct timeval * timeout)459 static int proxy_client_wait_select( vbi_proxy_client * vpc, struct timeval * timeout )
460 {
461 struct timeval tv_start;
462 struct timeval tv;
463 fd_set fd_rd;
464 fd_set fd_wr;
465 int ret;
466
467 if (vpc->io.sock_fd != -1)
468 {
469 do
470 {
471 #ifdef HAVE_LIBPTHREAD
472 pthread_testcancel();
473 #endif
474
475 FD_ZERO(&fd_rd);
476 FD_ZERO(&fd_wr);
477
478 if (vpc->io.writeLen > 0)
479 FD_SET(vpc->io.sock_fd, &fd_wr);
480 else
481 FD_SET(vpc->io.sock_fd, &fd_rd);
482
483 if ( ((vpc->client_flags & VBI_PROXY_CLIENT_NO_TIMEOUTS) == 0) &&
484 ((vpc->daemon_flags & VBI_PROXY_DAEMON_NO_TIMEOUTS) == 0) )
485 {
486 tv = *timeout; /* Linux kernel overwrites this */
487 gettimeofday(&tv_start, NULL);
488
489 ret = select(vpc->io.sock_fd + 1, &fd_rd, &fd_wr, NULL, &tv);
490
491 vbi_capture_io_update_timeout(timeout, &tv_start);
492 }
493 else
494 ret = select(vpc->io.sock_fd + 1, &fd_rd, &fd_wr, NULL, NULL);
495
496 } while ((ret < 0) && (errno == EINTR));
497
498 if (ret > 0) {
499 dprintf2("wait_select: waited for %c -> sock r/w %d/%d\n",
500 (vpc->io.writeLen > 0) ? 'w':'r',
501 (int) FD_ISSET(vpc->io.sock_fd, &fd_rd),
502 (int) FD_ISSET(vpc->io.sock_fd, &fd_wr));
503 } else if (ret == 0) {
504 dprintf1("wait_select: timeout\n");
505 } else {
506 dprintf1("wait_select: error %d (%s)\n", errno, strerror(errno));
507 }
508 }
509 else
510 {
511 dprintf1("wait_select: socket not open\n");
512 ret = -1;
513 }
514
515 return ret;
516 }
517
518 /* ----------------------------------------------------------------------------
519 ** Call remote procedure, i.e. write message then wait for reply
520 ** - message must already have been written prior to calling this function
521 ** - this is a synchronous message exchange with the daemon, i.e. the function
522 ** does not return until a reply is available or a timeout occured (in which
523 ** case the connection is dropped.)
524 */
proxy_client_rpc(vbi_proxy_client * vpc,VBIPROXY_MSG_TYPE reply1,VBIPROXY_MSG_TYPE reply2)525 static vbi_bool proxy_client_rpc( vbi_proxy_client * vpc,
526 VBIPROXY_MSG_TYPE reply1, VBIPROXY_MSG_TYPE reply2 )
527 {
528 struct timeval tv;
529 vbi_bool io_blocked;
530
531 assert (vpc->state != CLNT_STATE_ERROR);
532 assert (vpc->io.sock_fd != -1);
533
534 tv.tv_sec = RPC_TIMEOUT_MSECS / 1000;
535 tv.tv_usec = (RPC_TIMEOUT_MSECS % 1000) * 1000;
536
537 /* wait for write to finish */
538 do
539 {
540 if (proxy_client_wait_select(vpc, &tv) <= 0)
541 goto failure;
542
543 if (vbi_proxy_msg_handle_write(&vpc->io, &io_blocked) == FALSE)
544 goto failure;
545
546 } while (vpc->io.writeLen > 0);
547
548 /* wait for reply message */
549 while (1)
550 {
551 assert (vbi_proxy_msg_is_idle(&vpc->io));
552
553 do
554 {
555 if (proxy_client_wait_select(vpc, &tv) <= 0)
556 goto failure;
557
558 if (vbi_proxy_msg_handle_read(&vpc->io, &io_blocked, TRUE, vpc->p_client_msg, vpc->max_client_msg_size) == FALSE)
559 goto failure;
560
561 } while ((vpc->io.readOff == 0) || (vpc->io.readOff < vpc->io.readLen));
562
563 /* perform security checks on received message */
564 if (proxy_client_check_msg(vpc, vpc->io.readLen, vpc->p_client_msg) == FALSE)
565 goto failure;
566
567 vpc->rxTotal += vpc->p_client_msg->head.len;
568 vbi_proxy_msg_close_read(&vpc->io);
569
570 /* if it's the expected reply, we're finished */
571 if ( (vpc->p_client_msg->head.type != reply1) &&
572 (vpc->p_client_msg->head.type != reply2) )
573 {
574 /* process asynchronous message (e.g. slicer data or another IND message) */
575 if (proxy_client_take_message(vpc) == FALSE)
576 goto failure;
577 }
578 else
579 break;
580 }
581
582 return TRUE;
583
584 failure:
585 asprintf(&vpc->p_errorstr, _("Connection lost due to I/O error."));
586 return FALSE;
587 }
588
589 /* ----------------------------------------------------------------------------
590 ** Read a message from the socket
591 ** - if no data is available in the socket buffer the function blocks;
592 ** when the timeout is reached the function returns 0
593 */
proxy_client_read_message(vbi_proxy_client * vpc,struct timeval * p_timeout)594 static int proxy_client_read_message( vbi_proxy_client * vpc,
595 struct timeval * p_timeout )
596 {
597 vbi_bool io_blocked;
598 int ret;
599
600 /* simultaneous read and write is not supported */
601 assert (vpc->io.writeLen == 0);
602 assert ((vpc->io.readOff == 0) || (vpc->io.readLen < vpc->io.readOff));
603
604 if (proxy_client_alloc_msg_buf(vpc) == FALSE)
605 goto failure;
606
607 do
608 {
609 ret = proxy_client_wait_select(vpc, p_timeout);
610 if (ret < 0)
611 goto failure;
612 if (ret == 0)
613 break;
614
615 if (vbi_proxy_msg_handle_read(&vpc->io, &io_blocked, TRUE, vpc->p_client_msg, vpc->max_client_msg_size) == FALSE)
616 goto failure;
617
618 } while (vpc->io.readOff < vpc->io.readLen);
619
620 if (ret > 0)
621 {
622 /* perform security checks on received message */
623 if (proxy_client_check_msg(vpc, vpc->io.readLen, vpc->p_client_msg) == FALSE)
624 goto failure;
625
626 vpc->rxTotal += vpc->p_client_msg->head.len;
627 vbi_proxy_msg_close_read(&vpc->io);
628
629 /* process the message - frees the buffer if neccessary */
630 if (proxy_client_take_message(vpc) == FALSE)
631 goto failure;
632 }
633
634 return ret;
635
636 failure:
637 asprintf(&vpc->p_errorstr, _("Connection lost due to I/O error."));
638 proxy_client_close(vpc);
639 return -1;
640 }
641
642 /* ----------------------------------------------------------------------------
643 ** Wait until ongoing read is finished
644 ** - incoming data is discarded
645 */
proxy_client_wait_idle(vbi_proxy_client * vpc)646 static vbi_bool proxy_client_wait_idle( vbi_proxy_client * vpc )
647 {
648 PROXY_CLIENT_STATE old_state;
649 struct timeval tv;
650 vbi_bool io_blocked;
651
652 assert (vpc->io.writeLen == 0);
653
654 if (vpc->io.readOff > 0)
655 {
656 /* set intermediate state so that incoming data is discarded in the handler */
657 tv.tv_sec = IDLE_TIMEOUT_MSECS / 1000;
658 tv.tv_usec = IDLE_TIMEOUT_MSECS * 1000;
659
660 while (vpc->io.readOff < vpc->io.readLen)
661 {
662 if (proxy_client_wait_select(vpc, &tv) <= 0)
663 goto failure;
664
665 if (vbi_proxy_msg_handle_read(&vpc->io, &io_blocked, TRUE, vpc->p_client_msg, vpc->max_client_msg_size) == FALSE)
666 goto failure;
667 }
668
669 /* perform security checks on received message */
670 if (proxy_client_check_msg(vpc, vpc->io.readLen, vpc->p_client_msg) == FALSE)
671 goto failure;
672
673 vpc->rxTotal += vpc->p_client_msg->head.len;
674 vbi_proxy_msg_close_read(&vpc->io);
675
676 old_state = vpc->state;
677 vpc->state = CLNT_STATE_WAIT_IDLE;
678
679 if (proxy_client_take_message(vpc) == FALSE)
680 goto failure;
681
682 vpc->state = old_state;
683 }
684
685 return TRUE;
686
687 failure:
688 return FALSE;
689 }
690
691 /* ----------------------------------------------------------------------------
692 ** Start VBI acquisition, i.e. open connection to proxy daemon
693 */
proxy_client_start_acq(vbi_proxy_client * vpc)694 static vbi_bool proxy_client_start_acq( vbi_proxy_client * vpc )
695 {
696 VBIPROXY_CONNECT_REQ * p_req_msg;
697 VBIPROXY_CONNECT_CNF * p_cnf_msg;
698 VBIPROXY_CONNECT_REJ * p_rej_msg;
699 struct timeval tv;
700
701 assert(vpc->state == CLNT_STATE_NULL);
702
703 if (proxy_client_connect_server(vpc) == FALSE)
704 goto failure;
705
706 /* fake write request: make select to wait for socket to become writable */
707 vpc->io.writeLen = 1;
708 tv.tv_sec = 4;
709 tv.tv_usec = 0;
710
711 /* wait for socket to reach connected state */
712 if (proxy_client_wait_select(vpc, &tv) <= 0)
713 goto failure;
714
715 vpc->io.writeLen = 0;
716
717 if (vbi_proxy_msg_finish_connect(vpc->io.sock_fd, &vpc->p_errorstr) == FALSE)
718 goto failure;
719
720 if (proxy_client_alloc_msg_buf(vpc) == FALSE)
721 goto failure;
722
723 /* write service request parameters */
724 p_req_msg = &vpc->p_client_msg->body.connect_req;
725 vbi_proxy_msg_fill_magics(&p_req_msg->magics);
726
727 strlcpy((char *) p_req_msg->client_name, vpc->p_client_name, VBIPROXY_CLIENT_NAME_MAX_LENGTH);
728 p_req_msg->client_name[VBIPROXY_CLIENT_NAME_MAX_LENGTH - 1] = 0;
729 p_req_msg->pid = getpid();
730
731 p_req_msg->client_flags = vpc->client_flags;
732 p_req_msg->scanning = vpc->scanning;
733 p_req_msg->services = vpc->services;
734 p_req_msg->strict = vpc->strict;
735 p_req_msg->buffer_count = vpc->buffer_count;
736
737 /* send the connect request message to the proxy server */
738 vbi_proxy_msg_write(&vpc->io, MSG_TYPE_CONNECT_REQ, sizeof(p_req_msg[0]),
739 vpc->p_client_msg, FALSE);
740
741 vpc->state = CLNT_STATE_WAIT_CON_CNF;
742
743 /* send message and wait for reply */
744 if (proxy_client_rpc(vpc, MSG_TYPE_CONNECT_CNF, MSG_TYPE_CONNECT_REJ) == FALSE)
745 goto failure;
746
747 if (vpc->p_client_msg->head.type == MSG_TYPE_CONNECT_CNF)
748 {
749 p_cnf_msg = &vpc->p_client_msg->body.connect_cnf;
750
751 /* first server message received: contains version info */
752 /* note: nxtvepg and endian magics are already checked */
753 if (p_cnf_msg->magics.protocol_compat_version != VBIPROXY_COMPAT_VERSION)
754 {
755 dprintf1("take_message: CONNECT_CNF: reply version %x, protocol %x\n", p_cnf_msg->magics.protocol_version, p_cnf_msg->magics.protocol_compat_version);
756
757 asprintf (&vpc->p_errorstr,
758 _("Incompatible server version %u.%u.%u."),
759 ((p_cnf_msg->magics.protocol_compat_version >> 16) & 0xff),
760 ((p_cnf_msg->magics.protocol_compat_version >> 8) & 0xff),
761 ((p_cnf_msg->magics.protocol_compat_version ) & 0xff));
762 goto failure;
763 }
764 else if (vpc->endianSwap)
765 { /* endian swapping currently unsupported */
766 asprintf(&vpc->p_errorstr, _("Incompatible server architecture (endianess mismatch)."));
767 goto failure;
768 }
769 else
770 { /* version ok -> request block forwarding */
771 dprintf1("Successfully connected to proxy (version %x.%x.%x, protocol %x.%x.%x)\n",
772 (p_cnf_msg->magics.protocol_version >> 16) & 0xff,
773 (p_cnf_msg->magics.protocol_version >> 8) & 0xff,
774 (p_cnf_msg->magics.protocol_version) & 0xff,
775 (p_cnf_msg->magics.protocol_compat_version >> 16) & 0xff,
776 (p_cnf_msg->magics.protocol_compat_version >> 8) & 0xff,
777 (p_cnf_msg->magics.protocol_compat_version) & 0xff);
778
779 vpc->dec = p_cnf_msg->dec;
780 vpc->services = p_cnf_msg->services;
781 vpc->daemon_flags = p_cnf_msg->daemon_flags;
782 vpc->vbi_api_revision = p_cnf_msg->vbi_api_revision;
783
784 vpc->state = CLNT_STATE_CAPTURING;
785 }
786 }
787 else
788 {
789 p_rej_msg = &vpc->p_client_msg->body.connect_rej;
790 dprintf2("take_message: CONNECT_REJ: reply version %x, protocol %x\n", p_rej_msg->magics.protocol_version, p_rej_msg->magics.protocol_compat_version);
791 if (vpc->p_errorstr != NULL)
792 {
793 free(vpc->p_errorstr);
794 vpc->p_errorstr = NULL;
795 }
796 if (p_rej_msg->errorstr[0] != 0)
797 vpc->p_errorstr = strdup((char *) p_rej_msg->errorstr);
798
799 goto failure;
800 }
801
802 return TRUE;
803
804 failure:
805 /* failed to establish a connection to the server */
806 proxy_client_close(vpc);
807 return FALSE;
808 }
809
810 /* ----------------------------------------------------------------------------
811 ** Stop acquisition, i.e. close connection
812 */
proxy_client_stop_acq(vbi_proxy_client * vpc)813 static void proxy_client_stop_acq( vbi_proxy_client * vpc )
814 {
815 if (vpc->state != CLNT_STATE_NULL)
816 {
817 /* note: set the new state first to prevent callback from close function */
818 vpc->state = CLNT_STATE_NULL;
819
820 proxy_client_close(vpc);
821 }
822 else
823 dprintf1("stop_acq: acq not enabled\n");
824 }
825
826 /* ----------------------------------------------------------------------------
827 ** Process pending callbacks
828 ** - returns FALSE if caller should return from loop
829 */
830 static void
vbi_proxy_process_callbacks(vbi_proxy_client * vpc)831 vbi_proxy_process_callbacks( vbi_proxy_client * vpc )
832 {
833 VBI_PROXY_EV_TYPE ev_mask;
834
835 if (vpc->ev_mask != VBI_PROXY_EV_NONE)
836 {
837 ev_mask = vpc->ev_mask;
838 vpc->ev_mask = VBI_PROXY_EV_NONE;
839
840 if (vpc->p_callback_func != NULL)
841 {
842 vpc->p_callback_func(vpc->p_callback_data, ev_mask);
843 }
844 else
845 {
846 if (ev_mask & VBI_PROXY_EV_CHN_RECLAIMED)
847 {
848 }
849 }
850 }
851 }
852
853 /* ----------------------------------------------------------------------------
854 ** E X P O R T E D F U N C T I O N S
855 ** --------------------------------------------------------------------------*/
856
857 /* document below */
858 int
vbi_proxy_client_channel_request(vbi_proxy_client * vpc,VBI_CHN_PRIO chn_prio,vbi_channel_profile * p_chn_profile)859 vbi_proxy_client_channel_request( vbi_proxy_client * vpc,
860 VBI_CHN_PRIO chn_prio,
861 vbi_channel_profile * p_chn_profile )
862 {
863 VBIPROXY_CHN_TOKEN_REQ * p_req;
864 int result;
865
866 if (vpc != NULL)
867 {
868 if (vpc->state == CLNT_STATE_ERROR)
869 return -1;
870
871 dprintf1("Request for channel token: prio=%d\n", chn_prio);
872 assert(vpc->state == CLNT_STATE_CAPTURING);
873
874 if (proxy_client_alloc_msg_buf(vpc) == FALSE)
875 goto failure;
876
877 /* wait for ongoing read to complete (XXX FIXME: don't discard messages) */
878 if (proxy_client_wait_idle(vpc) == FALSE)
879 goto failure;
880
881 /* reset token in any case because prio or profile may have changed */
882 vpc->has_token = FALSE;
883 vpc->ev_mask &= ~VBI_PROXY_EV_CHN_GRANTED;
884 vpc->chn_prio = chn_prio;
885
886 vpc->state = CLNT_STATE_WAIT_RPC_REPLY;
887
888 /* send channel change request to proxy daemon */
889 p_req = &vpc->p_client_msg->body.chn_token_req;
890 memset(p_req, 0, sizeof(p_req[0]));
891 p_req->chn_prio = chn_prio;
892 p_req->chn_profile = *p_chn_profile;
893
894 vbi_proxy_msg_write(&vpc->io, MSG_TYPE_CHN_TOKEN_REQ, sizeof(p_req[0]),
895 vpc->p_client_msg, FALSE);
896
897 /* send message and wait for reply */
898 if (proxy_client_rpc(vpc, MSG_TYPE_CHN_TOKEN_CNF, -1) == FALSE)
899 goto failure;
900
901 /* process reply message */
902 vpc->has_token = vpc->p_client_msg->body.chn_token_cnf.token_ind;
903 if (vpc->has_token)
904 {
905 vpc->ev_mask |= VBI_PROXY_EV_CHN_GRANTED;
906 }
907
908 vpc->state = CLNT_STATE_CAPTURING;
909 result = (vpc->has_token ? 1 : 0);
910
911 /* invoke callback in case TOKEN_IND was piggy-backed */
912 vbi_proxy_process_callbacks(vpc);
913
914 return result;
915 }
916
917 failure:
918 proxy_client_close(vpc);
919 return -1;
920 }
921
922
923 /* document below */
924 int
vbi_proxy_client_channel_notify(vbi_proxy_client * vpc,VBI_PROXY_CHN_FLAGS notify_flags,unsigned int scanning)925 vbi_proxy_client_channel_notify( vbi_proxy_client * vpc,
926 VBI_PROXY_CHN_FLAGS notify_flags,
927 unsigned int scanning )
928 {
929 VBIPROXY_CHN_NOTIFY_REQ * p_msg;
930
931 if (vpc != NULL)
932 {
933 if (vpc->state == CLNT_STATE_ERROR)
934 return -1;
935
936 assert(vpc->state == CLNT_STATE_CAPTURING);
937
938 if (proxy_client_alloc_msg_buf(vpc) == FALSE)
939 goto failure;
940
941 /* wait for ongoing read to complete (XXX FIXME: don't discard messages) */
942 if (proxy_client_wait_idle(vpc) == FALSE)
943 goto failure;
944
945 dprintf1("Send channel notification: flags 0x%X, scanning %d (prio=%d, has_token=%d)\n", notify_flags, scanning, vpc->chn_prio, vpc->has_token);
946
947 memset(vpc->p_client_msg, 0, sizeof(vpc->p_client_msg[0]));
948 p_msg = &vpc->p_client_msg->body.chn_notify_req;
949
950 p_msg->notify_flags = notify_flags;
951 p_msg->scanning = scanning;
952
953 vbi_proxy_msg_write(&vpc->io, MSG_TYPE_CHN_NOTIFY_REQ, sizeof(p_msg[0]),
954 vpc->p_client_msg, FALSE);
955
956 vpc->state = CLNT_STATE_WAIT_RPC_REPLY;
957
958 /* send message and wait for reply */
959 if (proxy_client_rpc(vpc, MSG_TYPE_CHN_NOTIFY_CNF, -1) == FALSE)
960 goto failure;
961
962 /* process reply message */
963 /* XXX TODO */
964
965 vpc->state = CLNT_STATE_CAPTURING;
966 }
967
968 /* invoke callback in case TOKEN_IND was piggy-backed */
969 vbi_proxy_process_callbacks(vpc);
970
971 return 0;
972
973 failure:
974 proxy_client_close(vpc);
975 return -1;
976 }
977
978
979 /* document below */
980 int
vbi_proxy_client_channel_suspend(vbi_proxy_client * vpc,VBI_PROXY_SUSPEND cmd)981 vbi_proxy_client_channel_suspend( vbi_proxy_client * vpc,
982 VBI_PROXY_SUSPEND cmd )
983 {
984 /* XXX TODO */
985
986 vpc = vpc;
987 cmd = cmd;
988
989 return -1;
990 }
991
992
993 /* document below */
994 int
vbi_proxy_client_device_ioctl(vbi_proxy_client * vpc,int request,void * p_arg)995 vbi_proxy_client_device_ioctl( vbi_proxy_client * vpc, int request, void * p_arg )
996 {
997 VBIPROXY_MSG * p_msg;
998 vbi_bool req_perm;
999 int size;
1000 int result = -1;
1001
1002 if (vpc != NULL)
1003 {
1004 if (vpc->state == CLNT_STATE_CAPTURING)
1005 {
1006 /* determine size of the argument */
1007 size = vbi_proxy_msg_check_ioctl(vpc->vbi_api_revision, request, p_arg, &req_perm);
1008 if (size >= 0)
1009 {
1010 /* XXX TODO: for GET type calls on v4l2 use local device */
1011
1012 if ( (req_perm == FALSE) ||
1013 (vpc->chn_prio > VBI_CHN_PRIO_BACKGROUND) || vpc->has_token )
1014 {
1015 /* wait for ongoing read to complete (XXX FIXME: don't discard messages) */
1016 if (proxy_client_wait_idle(vpc) == FALSE)
1017 goto failure;
1018
1019 dprintf1("Forwarding ioctl: 0x%X, argp=0x%lX\n", request, (long)p_arg);
1020
1021 p_msg = malloc(VBIPROXY_MSG_BODY_OFFSET + VBIPROXY_CHN_IOCTL_REQ_SIZE(size));
1022 if (p_msg == NULL)
1023 goto failure;
1024
1025 p_msg->body.chn_ioctl_req.request = request;
1026 p_msg->body.chn_ioctl_req.arg_size = size;
1027 if (size > 0)
1028 memcpy(p_msg->body.chn_ioctl_req.arg_data, p_arg, size);
1029
1030 vbi_proxy_msg_write(&vpc->io, MSG_TYPE_CHN_IOCTL_REQ,
1031 VBIPROXY_CHN_IOCTL_REQ_SIZE(size), p_msg, TRUE);
1032
1033 /* send message and wait for reply */
1034 if (proxy_client_rpc(vpc, MSG_TYPE_CHN_IOCTL_CNF, MSG_TYPE_CHN_IOCTL_REJ) == FALSE)
1035 goto failure;
1036
1037 /* process reply message */
1038 if (vpc->p_client_msg->head.type == MSG_TYPE_CHN_IOCTL_CNF)
1039 {
1040 if (size > 0)
1041 memcpy(p_arg, vpc->p_client_msg->body.chn_ioctl_req.arg_data, size);
1042 result = vpc->p_client_msg->body.chn_ioctl_cnf.result;
1043 errno = vpc->p_client_msg->body.chn_ioctl_cnf.errcode;
1044 }
1045 else
1046 {
1047 errno = EBUSY;
1048 result = -1;
1049 }
1050 vpc->state = CLNT_STATE_CAPTURING;
1051 }
1052 else
1053 {
1054 dprintf1("vbi_proxy-client_ioctl: request not allowed without obtaining token first\n");
1055 errno = EBUSY;
1056 }
1057 }
1058 else
1059 {
1060 dprintf1("vbi_proxy-client_ioctl: unknown or not allowed request: 0x%X\n", request);
1061 errno = EINVAL;
1062 }
1063 }
1064 else
1065 dprintf1("vbi_proxy-client_ioctl: client in invalid state %d\n", vpc->state);
1066
1067 vbi_proxy_process_callbacks(vpc);
1068 }
1069 else
1070 dprintf1("vbi_proxy-client_ioctl: invalid NULL ptr param\n");
1071
1072 failure:
1073 return result;
1074 }
1075
1076
1077 /* document below */
1078 int
vbi_proxy_client_get_channel_desc(vbi_proxy_client * vpc,unsigned int * p_scanning,vbi_bool * p_granted)1079 vbi_proxy_client_get_channel_desc( vbi_proxy_client * vpc,
1080 unsigned int * p_scanning,
1081 vbi_bool * p_granted )
1082 {
1083 if (vpc != NULL)
1084 {
1085 if (p_scanning != NULL)
1086 *p_scanning = vpc->scanning;
1087 if (p_granted != NULL)
1088 *p_granted = vpc->has_token;
1089
1090 return 0;
1091 }
1092 else
1093 return -1;
1094 }
1095
1096
1097 /* document below */
1098 vbi_bool
vbi_proxy_client_has_channel_control(vbi_proxy_client * vpc)1099 vbi_proxy_client_has_channel_control( vbi_proxy_client * vpc )
1100 {
1101 if (vpc != NULL)
1102 {
1103 return (vpc->has_token);
1104 }
1105 else
1106 {
1107 dprintf1("vbi_proxy_client-has_channel_token: NULL client param");
1108 return FALSE;
1109 }
1110 }
1111
1112
1113 /* document below */
1114 VBI_DRIVER_API_REV
vbi_proxy_client_get_driver_api(vbi_proxy_client * vpc)1115 vbi_proxy_client_get_driver_api( vbi_proxy_client * vpc )
1116 {
1117 if (vpc != NULL)
1118 {
1119 return vpc->vbi_api_revision;
1120 }
1121 else
1122 return VBI_API_UNKNOWN;
1123 }
1124
1125
1126 /* document below */
1127 VBI_PROXY_CLIENT_CALLBACK *
vbi_proxy_client_set_callback(vbi_proxy_client * vpc,VBI_PROXY_CLIENT_CALLBACK * p_callback,void * p_data)1128 vbi_proxy_client_set_callback( vbi_proxy_client * vpc,
1129 VBI_PROXY_CLIENT_CALLBACK * p_callback, void * p_data )
1130 {
1131 VBI_PROXY_CLIENT_CALLBACK * p_prev_cb = NULL;
1132
1133 if (vpc != NULL)
1134 {
1135 p_prev_cb = vpc->p_callback_func;
1136
1137 vpc->p_callback_func = p_callback;
1138 vpc->p_callback_data = p_data;
1139 }
1140 else
1141 dprintf1("vbi_proxy_client-set_callback: invalid pointer arg\n");
1142
1143 return p_prev_cb;
1144 }
1145
1146
1147 /* document below */
1148 vbi_capture *
vbi_proxy_client_get_capture_if(vbi_proxy_client * vpc)1149 vbi_proxy_client_get_capture_if( vbi_proxy_client * vpc )
1150 {
1151 if (vpc != NULL)
1152 {
1153 return &vpc->capt_api;
1154 }
1155 else
1156 return NULL;
1157 }
1158
1159 /* ----------------------------------------------------------------------------
1160 ** D E V I C E C A P T U R E A P I
1161 ** --------------------------------------------------------------------------*/
1162
1163 /**
1164 * @internal
1165 *
1166 * @param vpc Pointer to initialized proxy client context
1167 *
1168 * @return
1169 * Pointer to a vbi_raw_decoder structure, read only. Returns @c NULL
1170 * upon error (i.e. if the client is not connected to the daemon)
1171 */
1172 static vbi_raw_decoder *
vbi_proxy_client_get_dec_params(vbi_capture * vc)1173 vbi_proxy_client_get_dec_params( vbi_capture * vc )
1174 {
1175 vbi_proxy_client * vpc = PARENT(vc, vbi_proxy_client, capt_api);
1176
1177 if (vc != NULL)
1178 return &vpc->dec;
1179 else
1180 return NULL;
1181 }
1182
1183
1184 /**
1185 * @internal
1186 *
1187 * @param vpc Pointer to initialized proxy client context
1188 *
1189 * @return
1190 * File descriptor of the socket used to connect to the proxy daemon or
1191 * -1 upon error (i.e. if the client is not connected to the daemon)
1192 * The descriptor can only be used for select() by caller, i.e. not for
1193 * read/write and must never be closed (call the close function instead)
1194 */
1195 static int
vbi_proxy_client_get_fd(vbi_capture * vc)1196 vbi_proxy_client_get_fd( vbi_capture * vc )
1197 {
1198 vbi_proxy_client * vpc = PARENT(vc, vbi_proxy_client, capt_api);
1199
1200 if (vc != NULL)
1201 {
1202 return vpc->io.sock_fd;
1203 }
1204 else
1205 return -1;
1206 }
1207
1208
1209 /**
1210 * @internal
1211 *
1212 * Read one frame's worth of VBI data. If asynchronous events occur,
1213 * the callback is invoked before the call returns.
1214 *
1215 * Note: This function may indicate a timeout (i.e. return 0) even
1216 * if a previous select indicated readability. This will occur when
1217 * asynchronous messages (e.g. channel change indications) arrive.
1218 * Proxy clients should be prepared for this. Channel change
1219 * indications can be supressed with VBI_PROXY_CLIENT_NO_STATUS_IND
1220 * in client flags during creation of the proxy, but there may still
1221 * be asynchronous messages when a token is granted.
1222 */
1223 static int
vbi_proxy_client_read(vbi_capture * vc,struct vbi_capture_buffer ** pp_raw_buf,struct vbi_capture_buffer ** pp_slice_buf,const struct timeval * p_timeout)1224 vbi_proxy_client_read( vbi_capture * vc,
1225 struct vbi_capture_buffer **pp_raw_buf,
1226 struct vbi_capture_buffer **pp_slice_buf,
1227 const struct timeval * p_timeout )
1228 {
1229 vbi_proxy_client * vpc = PARENT(vc, vbi_proxy_client, capt_api);
1230 struct timeval timeout = *p_timeout;
1231 int lines;
1232 int result;
1233
1234 if ((vc != NULL) && (vpc->state == CLNT_STATE_CAPTURING))
1235 {
1236 vpc->sliced_ind = FALSE;
1237
1238 /* wait for message & read it (note: may also be some status ind) */
1239 result = proxy_client_read_message(vpc, &timeout);
1240
1241 if (result > 0)
1242 {
1243 if (vpc->sliced_ind != FALSE)
1244 {
1245 if (pp_raw_buf != NULL)
1246 {
1247 lines = vpc->p_client_msg->body.sliced_ind.raw_lines;
1248
1249 if (*pp_raw_buf != NULL)
1250 {
1251 /* XXX optimization possible: read sliced msg into buffer to avoid memcpy */
1252 memcpy( (*pp_raw_buf)->data,
1253 vpc->p_client_msg->body.sliced_ind.u.raw,
1254 lines * VBIPROXY_RAW_LINE_SIZE );
1255 }
1256 else
1257 {
1258 *pp_raw_buf = &vpc->raw_buf;
1259 (*pp_raw_buf)->data = vpc->p_client_msg->body.sliced_ind.u.raw;
1260 }
1261 (*pp_raw_buf)->size = lines * VBIPROXY_RAW_LINE_SIZE;
1262 (*pp_raw_buf)->timestamp = vpc->p_client_msg->body.sliced_ind.timestamp;
1263 }
1264
1265 if (pp_slice_buf != NULL)
1266 {
1267 lines = vpc->p_client_msg->body.sliced_ind.sliced_lines;
1268
1269 if (*pp_slice_buf != NULL)
1270 {
1271 /* XXX optimization possible: read sliced msg into buffer to avoid memcpy */
1272 memcpy( (*pp_slice_buf)->data,
1273 vpc->p_client_msg->body.sliced_ind.u.sliced,
1274 lines * sizeof(vbi_sliced) );
1275 }
1276 else
1277 {
1278 *pp_slice_buf = &vpc->slice_buf;
1279 (*pp_slice_buf)->data = vpc->p_client_msg->body.sliced_ind.u.sliced;
1280 }
1281
1282 (*pp_slice_buf)->size = lines * sizeof(vbi_sliced);
1283 (*pp_slice_buf)->timestamp = vpc->p_client_msg->body.sliced_ind.timestamp;
1284 }
1285 }
1286 else
1287 { /* not a slicer data unit */
1288 result = 0;
1289 }
1290 vbi_proxy_process_callbacks(vpc);
1291 }
1292 return result;
1293 }
1294 errno = EBADF;
1295 return -1;
1296 }
1297
1298
1299 /**
1300 * @internal
1301 *
1302 * Add and/or remove one or more services to an already initialized
1303 * capture context.
1304 *
1305 * Note the "commit" parameter is currently not applicable to proxy clients.
1306 */
1307 static unsigned int
vbi_proxy_client_update_services(vbi_capture * vc,vbi_bool reset,vbi_bool commit,unsigned int services,int strict,char ** pp_errorstr)1308 vbi_proxy_client_update_services( vbi_capture * vc,
1309 vbi_bool reset, vbi_bool commit,
1310 unsigned int services, int strict,
1311 char ** pp_errorstr )
1312 {
1313 vbi_proxy_client * vpc = PARENT(vc, vbi_proxy_client, capt_api);
1314
1315 if (vc != NULL)
1316 {
1317 if (vpc->state == CLNT_STATE_ERROR)
1318 return 0;
1319
1320 assert(vpc->state == CLNT_STATE_CAPTURING);
1321
1322 if (proxy_client_alloc_msg_buf(vpc) == FALSE)
1323 goto failure;
1324
1325 /* wait for ongoing read to complete */
1326 if (proxy_client_wait_idle(vpc) == FALSE)
1327 goto failure;
1328
1329 vpc->state = CLNT_STATE_WAIT_SRV_CNF;
1330
1331 dprintf1("update_services: send service req: srv %d, strict %d\n", services, strict);
1332
1333 /* send service request to proxy daemon */
1334 vpc->p_client_msg->body.service_req.reset = reset;
1335 vpc->p_client_msg->body.service_req.commit = commit;
1336 vpc->p_client_msg->body.service_req.services = services;
1337 vpc->p_client_msg->body.service_req.strict = strict;
1338 vbi_proxy_msg_write(&vpc->io, MSG_TYPE_SERVICE_REQ, sizeof(vpc->p_client_msg->body.service_req),
1339 vpc->p_client_msg, FALSE);
1340
1341 /* send message and wait for reply */
1342 if (proxy_client_rpc(vpc, MSG_TYPE_SERVICE_CNF, MSG_TYPE_SERVICE_REJ) == FALSE)
1343 goto failure;
1344
1345 if (vpc->p_client_msg->head.type == MSG_TYPE_SERVICE_CNF)
1346 {
1347 memset(&vpc->dec, 0, sizeof(vpc->dec));
1348
1349 vpc->services = vpc->p_client_msg->body.service_cnf.services;
1350 memcpy(&vpc->dec, &vpc->p_client_msg->body.service_cnf.dec, sizeof(vpc->dec));
1351 dprintf1("service cnf: granted service %d\n", vpc->dec.services);
1352 }
1353 else
1354 {
1355 /* process the message */
1356 if ( (vpc->p_client_msg->body.service_rej.errorstr[0] != 0) &&
1357 (pp_errorstr != NULL) )
1358 {
1359 *pp_errorstr = strdup((char *) vpc->p_client_msg->body.service_rej.errorstr);
1360 }
1361 }
1362 vpc->state = CLNT_STATE_CAPTURING;
1363
1364 return services & vpc->services;
1365 }
1366
1367 failure:
1368 if (vpc->p_errorstr != NULL)
1369 {
1370 if (pp_errorstr != NULL)
1371 *pp_errorstr = vpc->p_errorstr;
1372 else
1373 free(vpc->p_errorstr);
1374 vpc->p_errorstr = NULL;
1375 }
1376 proxy_client_close(vpc);
1377 return 0;
1378 }
1379
1380
1381 /**
1382 * @internal
1383 *
1384 * Note this function is only present because it's part of the capture
1385 * device API. Proxy-aware clients should use the proxy client API
1386 * function vbi_proxy_client_channel_notify() instead of this one, because
1387 * it allows to return the channel control "token" at the same time.
1388 */
1389 static void
vbi_proxy_client_flush(vbi_capture * vc)1390 vbi_proxy_client_flush( vbi_capture * vc )
1391 {
1392 vbi_proxy_client * vpc = PARENT(vc, vbi_proxy_client, capt_api);
1393
1394 if (vc != NULL)
1395 {
1396 vbi_proxy_client_channel_notify(vpc, VBI_PROXY_CHN_FLUSH, 0);
1397 }
1398 }
1399
1400
1401 /**
1402 * @internal
1403 *
1404 * @param vpc Pointer to initialized proxy client context
1405 *
1406 * Queries properties of the exported "capture device" file handle.
1407 */
1408 static VBI_CAPTURE_FD_FLAGS
vbi_proxy_client_get_fd_flags(vbi_capture * vc)1409 vbi_proxy_client_get_fd_flags(vbi_capture *vc)
1410 {
1411 vc = vc;
1412
1413 return VBI_FD_HAS_SELECT;
1414 }
1415
1416 /**
1417 * @internal
1418 *
1419 * @param vpc Pointer to initialized proxy client context
1420 *
1421 * Close connection to the proxy daemon. The proxy client context
1422 * can be re-used for another connection later.
1423 */
1424 static void
vbi_proxy_client_stop(vbi_capture * vc)1425 vbi_proxy_client_stop( vbi_capture * vc )
1426 {
1427 vbi_proxy_client * vpc = PARENT(vc, vbi_proxy_client, capt_api);
1428
1429 if (vc != NULL)
1430 {
1431 proxy_client_stop_acq(vpc);
1432 }
1433 }
1434
1435 /* document below */
1436 vbi_capture *
vbi_capture_proxy_new(struct vbi_proxy_client * vpc,int buffers,int scanning,unsigned int * p_services,int strict,char ** pp_errorstr)1437 vbi_capture_proxy_new( struct vbi_proxy_client * vpc,
1438 int buffers, int scanning,
1439 unsigned int *p_services, int strict,
1440 char **pp_errorstr )
1441 {
1442 if (vpc != NULL)
1443 {
1444 if ( (vpc->state == CLNT_STATE_NULL) ||
1445 (vpc->state == CLNT_STATE_ERROR) )
1446 {
1447 if (scanning != 525 && scanning != 625)
1448 scanning = 0;
1449
1450 if (buffers < 1)
1451 buffers = 1;
1452
1453 if (strict < -1)
1454 strict = -1;
1455 else if (strict > 2)
1456 strict = 2;
1457
1458 /* check and copy parameters into state struct */
1459 assert((p_services == NULL) || (*p_services != 0));
1460
1461 vpc->buffer_count = buffers;
1462 vpc->scanning = scanning,
1463 vpc->services = ((p_services != NULL) ? *p_services : 0);
1464 vpc->strict = strict;
1465
1466 /* reset state if in error state (e.g. previous connect failed) */
1467 vpc->state = CLNT_STATE_NULL;
1468
1469 /* send params to daemon and wait for reply */
1470 if ( proxy_client_start_acq(vpc) )
1471 {
1472 assert(vpc->state == CLNT_STATE_CAPTURING);
1473 assert((p_services == NULL) || (vpc->services != 0));
1474
1475 if (p_services != NULL)
1476 *p_services = vpc->services;
1477
1478 return &vpc->capt_api;
1479 }
1480 }
1481 else
1482 dprintf1("vbi_proxy-client_start: illegal state %d for start\n", vpc->state);
1483 }
1484 else
1485 dprintf1("vbi_proxy-client_start: illegal NULL ptr param\n");
1486
1487 if (pp_errorstr != NULL)
1488 *pp_errorstr = vpc->p_errorstr;
1489 else
1490 free(vpc->p_errorstr);
1491 vpc->p_errorstr = NULL;
1492
1493 return NULL;
1494 }
1495
1496 void
vbi_proxy_client_destroy(vbi_proxy_client * vpc)1497 vbi_proxy_client_destroy( vbi_proxy_client * vpc )
1498 {
1499 if (vpc != NULL)
1500 {
1501 /* close the connection (during normal shutdown it should already be closed) */
1502 if (vpc->state != CLNT_STATE_NULL)
1503 proxy_client_stop_acq(vpc);
1504
1505 if (vpc->p_srv_host != NULL)
1506 free(vpc->p_srv_host);
1507
1508 if (vpc->p_srv_port != NULL)
1509 free(vpc->p_srv_port);
1510
1511 if (vpc->p_client_msg != NULL)
1512 free(vpc->p_client_msg);
1513
1514 if (vpc->p_errorstr != NULL)
1515 free(vpc->p_errorstr);
1516
1517 free(vpc);
1518 }
1519 }
1520
1521 /* document below */
1522 vbi_proxy_client *
vbi_proxy_client_create(const char * p_dev_name,const char * p_client_name,VBI_PROXY_CLIENT_FLAGS client_flags,char ** pp_errorstr,int trace_level)1523 vbi_proxy_client_create( const char *p_dev_name, const char *p_client_name,
1524 VBI_PROXY_CLIENT_FLAGS client_flags,
1525 char **pp_errorstr, int trace_level )
1526 {
1527 vbi_proxy_client * vpc;
1528
1529 if (trace_level)
1530 {
1531 fprintf(stderr, "Creating vbi proxy client, rev.\n%s\n", rcsid);
1532 vbi_proxy_msg_set_debug_level(trace_level);
1533 }
1534
1535 vpc = (vbi_proxy_client *) calloc(1, sizeof(vpc[0]));
1536 if (vpc != NULL)
1537 {
1538 /* fill capture interface struct */
1539 vpc->capt_api.parameters = vbi_proxy_client_get_dec_params;
1540 vpc->capt_api._delete = vbi_proxy_client_stop;
1541 vpc->capt_api.get_fd = vbi_proxy_client_get_fd;
1542 vpc->capt_api.get_fd_flags = vbi_proxy_client_get_fd_flags;
1543 vpc->capt_api.read = vbi_proxy_client_read;
1544 vpc->capt_api.update_services = vbi_proxy_client_update_services;
1545 vpc->capt_api.flush = vbi_proxy_client_flush;
1546
1547 /* initialize client state with given parameters */
1548 vpc->p_client_name = strdup(p_client_name);
1549 vpc->client_flags = client_flags;
1550 vpc->p_srv_port = vbi_proxy_msg_get_socket_name(p_dev_name);
1551 vpc->p_srv_host = NULL;
1552 vpc->trace = trace_level;
1553
1554 vpc->state = CLNT_STATE_NULL;
1555 vpc->io.sock_fd = -1;
1556 }
1557 else
1558 {
1559 asprintf(pp_errorstr, _("Virtual memory exhausted."));
1560 }
1561 return vpc;
1562 }
1563
1564 #else /* !ENABLE_PROXY */
1565
1566 /**
1567 * @addtogroup Proxy VBI capture proxy interface
1568 * @ingroup Raw
1569 * @brief Receiving sliced or raw data from VBI proxy daemon
1570 *
1571 * Using the VBI proxy daemon instead of capturing directly from a
1572 * VBI device allows multiple clients to capture concurrently, e.g.
1573 * to decode multiple data services.
1574 */
1575
1576 /**
1577 * @param vpc Pointer to initialized proxy client context
1578 * @param chn_prio Channel change priority level. If there are other clients
1579 * with higher priority the client will be refused any channel changes.
1580 * @param p_chn_profile Channel profile for scheduling at background
1581 * priority level.
1582 *
1583 * This function is used to request permission to switch channels or norm.
1584 * Since the VBI device can be shared with other proxy clients, clients should
1585 * wait for permission, so that the proxy daemon can fairly schedule channel
1586 * requests.
1587 *
1588 * Scheduling differs at the 3 priority levels. For an explanation of
1589 * priorities see enum VBI_CHN_PRIO. At background level channel changes
1590 * are coordinated by introduction of a virtual token: only the
1591 * one client which holds the token is allowed to switch channels. The daemon
1592 * will wait for the token to be returned before it's granted to another
1593 * client. This way conflicting channel changes are avoided.
1594 *
1595 * At the upper level the latest request always wins. To avoid interference
1596 * the application still might wait until it gets indicated that the token
1597 * has been returned to the daemon.
1598 *
1599 * The token may be granted right away or at a later time, e.g. when it has
1600 * to be reclaimed from another client first, or if there are other clients
1601 * with higher priority. If a callback has been registered, it will be
1602 * invoked when the token arrives; otherwise
1603 * vbi_proxy_client_has_channel_control()
1604 * can be used to poll for it.
1605 *
1606 * Note: to set the priority level to "background" only without requesting
1607 * a channel, set the is_valid member in the profile to @c FALSE.
1608 *
1609 * @return
1610 * 1 if change is allowed, 0 if not allowed,
1611 * -1 on error, examine @c errno for details.
1612 *
1613 * @since 0.2.9
1614 */
1615 /* XXX TODO improve description */
1616 int
vbi_proxy_client_channel_request(vbi_proxy_client * vpc,VBI_CHN_PRIO chn_prio,vbi_channel_profile * p_chn_profile)1617 vbi_proxy_client_channel_request( vbi_proxy_client * vpc,
1618 VBI_CHN_PRIO chn_prio,
1619 vbi_channel_profile * p_chn_profile )
1620 {
1621 errno = 0;
1622 return -1;
1623 }
1624
1625
1626 /**
1627 * @param vpc Pointer to initialized proxy client context
1628 * @param notify_flags Combination of event notification bits
1629 * @param scanning New norm, if norm event bit is set
1630 *
1631 * Send channel control request to proxy daemon.
1632 * See description of the flags for details.
1633 *
1634 * @return
1635 * 0 upon success, -1 on error, examine @c errno for details.
1636 *
1637 * @since 0.2.9
1638 */
1639 int
vbi_proxy_client_channel_notify(vbi_proxy_client * vpc,VBI_PROXY_CHN_FLAGS notify_flags,unsigned int scanning)1640 vbi_proxy_client_channel_notify( vbi_proxy_client * vpc,
1641 VBI_PROXY_CHN_FLAGS notify_flags,
1642 unsigned int scanning )
1643 {
1644 return -1;
1645 }
1646
1647
1648 /**
1649 * @param vpc Pointer to initialized proxy client context
1650 * @param cmd Control command
1651 *
1652 * Request to temporarily suspend capturing
1653 *
1654 * @return
1655 * 0 upon success, -1 on error, examine @c errno for details.
1656 *
1657 * @since 0.2.9
1658 */
1659 int
vbi_proxy_client_channel_suspend(vbi_proxy_client * vpc,VBI_PROXY_SUSPEND cmd)1660 vbi_proxy_client_channel_suspend( vbi_proxy_client * vpc,
1661 VBI_PROXY_SUSPEND cmd )
1662 {
1663 return -1;
1664 }
1665
1666
1667 /**
1668 * @param vpc Pointer to initialized proxy client context
1669 * @param request Ioctl request code to be passed to driver
1670 * @param p_arg Ioctl argument buffer to be passed to driver.
1671 * For ioctls which return data, the buffer will by modified by
1672 * the call (i.e. same as if the ioctl had ben called directly)
1673 * Note the required buffer size depends on the request code.
1674 *
1675 * @brief Wrapper for ioctl requests on the VBI device
1676 *
1677 * This function allows to manipulate parameters of the underlying
1678 * VBI device. Not all ioctls are allowed here. It's mainly intended
1679 * to be used for channel enumeration and channel/norm changes.
1680 * The request codes and parameters are the same as for the actual device.
1681 * The caller has to query the driver API first and use the respective
1682 * ioctl codes, same as if the device would be used directly.
1683 *
1684 * @return
1685 * Same as for the ioctl, i.e. -1 on error and errno set appropriately.
1686 * The funtion also will fail with errno @c EBUSY if the client doesn't
1687 * have permission to control the channel.
1688 *
1689 * @since 0.2.9
1690 */
1691 int
vbi_proxy_client_device_ioctl(vbi_proxy_client * vpc,int request,void * p_arg)1692 vbi_proxy_client_device_ioctl( vbi_proxy_client * vpc,
1693 int request, void * p_arg )
1694 {
1695 return -1;
1696 }
1697
1698 /**
1699 * @param vpc Pointer to initialized proxy client context
1700 * @param p_scanning Returns new scanning after channel change
1701 * @param p_granted Returns@c TRUE if client is currently allowed to
1702 * switch channels
1703 *
1704 * Retrieve info sent by the proxy daemon in a channel change indication.
1705 *
1706 * @return
1707 * 0 upon success, -1 on error.
1708 *
1709 * @since 0.2.9
1710 */
1711 int
vbi_proxy_client_get_channel_desc(vbi_proxy_client * vpc,unsigned int * p_scanning,vbi_bool * p_granted)1712 vbi_proxy_client_get_channel_desc( vbi_proxy_client * vpc,
1713 unsigned int * p_scanning,
1714 vbi_bool * p_granted )
1715 {
1716 return -1;
1717 }
1718
1719 /**
1720 * @param vpc Pointer to initialized proxy client context
1721 *
1722 * @brief Query if the client is currently allowed to switch channels
1723 *
1724 * @return
1725 * Returns @c TRUE if client is currently allowed to switch channels.
1726 *
1727 * @since 0.2.9
1728 */
1729 vbi_bool
vbi_proxy_client_has_channel_control(vbi_proxy_client * vpc)1730 vbi_proxy_client_has_channel_control( vbi_proxy_client * vpc )
1731 {
1732 return FALSE;
1733 }
1734
1735 /**
1736 * @param vpc Pointer to initialized proxy client context
1737 *
1738 * @brief Returns the driver type behind the actual capture device
1739 *
1740 * This function can be used to query which driver is behind the
1741 * device which is currently opened by the VBI proxy daemon.
1742 * Applications which use libzvbi's capture API only need not
1743 * care about this. The information is only relevant to applications
1744 * which need to change channels or norms.
1745 *
1746 * The function will fail if the client is currently not connected
1747 * to the daemon, i.e. VPI capture has to be started first.
1748 *
1749 * @return
1750 * Driver type or -1 on error.
1751 *
1752 * @since 0.2.9
1753 */
1754 VBI_DRIVER_API_REV
vbi_proxy_client_get_driver_api(vbi_proxy_client * vpc)1755 vbi_proxy_client_get_driver_api( vbi_proxy_client * vpc )
1756 {
1757 return VBI_API_UNKNOWN;
1758 }
1759
1760 /**
1761 * @param vpc Pointer to initialized proxy client context
1762 * @param p_callback Pointer to callback function
1763 * @param p_data Void pointer which will be passed through to the
1764 * callback function unmodified.
1765 *
1766 * @brief Installs callback function for asynchronous events
1767 *
1768 * This function installs a callback function which will be invoked
1769 * upon asynchronous events (e.g. channel changes by other clients.)
1770 * Since the proxy client has no "life" on it's own (i.e.
1771 * it's not using an internal thread or process) callbacks will only
1772 * occur from inside other proxy client function calls. The client's
1773 * file description will become readable when an asynchronous message
1774 * has arrived from the daemon. Typically the application then will
1775 * call read to obtain sliced data and the callback will be invoked
1776 * from inside the read function. Usually in this case the read call
1777 * will return zero, i.e. indicate an timeout since no actual sliced
1778 * data has arrived.
1779 *
1780 * Note for channel requests the callback to grant channel control may
1781 * be invoked before the request function returns.
1782 * Note you can call any interface function from inside the callback,
1783 * including the destroy operator.
1784 *
1785 * @return
1786 * Returns pointer to the previous callback or @c NULL if none.
1787 *
1788 * @since 0.2.9
1789 */
1790 VBI_PROXY_CLIENT_CALLBACK *
vbi_proxy_client_set_callback(vbi_proxy_client * vpc,VBI_PROXY_CLIENT_CALLBACK * p_callback,void * p_data)1791 vbi_proxy_client_set_callback( vbi_proxy_client * vpc,
1792 VBI_PROXY_CLIENT_CALLBACK * p_callback,
1793 void * p_data )
1794 {
1795 return NULL;
1796 }
1797
1798 /**
1799 * @param vpc Pointer to initialized and active proxy client context
1800 *
1801 * @brief Returns capture interface for an initialized proxy client
1802 *
1803 * This function is for convenience only: it returns the same pointer
1804 * as the previous call to vbi_capture_proxy_new(), so that the client
1805 * need not store it. This pointer is required for function calls
1806 * through the capture device API (e.g. reading raw or sliced data)
1807 *
1808 * @return
1809 * Pointer to a vbi_capture structure, should be treated as void * by
1810 * caller, i.e. acessed neither for read nor write. Returns @c NULL
1811 * upon error (i.e. if the client is not connected to the daemon)
1812 *
1813 * @since 0.2.9
1814 */
1815 vbi_capture *
vbi_proxy_client_get_capture_if(vbi_proxy_client * vpc)1816 vbi_proxy_client_get_capture_if( vbi_proxy_client * vpc )
1817 {
1818 return NULL;
1819 }
1820
1821 /**
1822 * @ingroup Device
1823 *
1824 * @param p_proxy_client Reference to an initialized proxy client
1825 * context.
1826 * @param buffers Number of intermediate buffers on server side
1827 * of the proxy socket connection. (Note this is not related to the
1828 * device buffer count parameter of @a v4l2_new et.al.)
1829 * @param scanning This indicates the current norm: 625 for PAL and
1830 * 525 for NTSC; set to 0 if you don't know (you should not attempt
1831 * to query the device for the norm, as this parameter is only required
1832 * for v4l1 drivers which don't support video standard query ioctls)
1833 * @param p_services This must point to a set of @ref VBI_SLICED_
1834 * symbols describing the
1835 * data services to be decoded. On return the services actually
1836 * decodable will be stored here. See vbi_raw_decoder_add()
1837 * for details. If you want to capture raw data only, set to
1838 * @c VBI_SLICED_VBI_525, @c VBI_SLICED_VBI_625 or both.
1839 * If this parameter is @c NULL, no services will be installed.
1840 * You can do so later with vbi_capture_update_services(); note the
1841 * reset parameter must be set to @c TRUE in this case.
1842 * @param strict Will be passed to vbi_raw_decoder_add().
1843 * @param pp_errorstr If not @c NULL this function stores a pointer to an error
1844 * description here. You must free() this string when no longer needed.
1845 *
1846 * Open a new connection to a VBI proxy to open a VBI device for the
1847 * given services. On side of the proxy daemon, one of the regular
1848 * capture context creation functions (e.g. v4l2_new) is invoked.
1849 * If the creation succeeds, and any of the requested services are
1850 * available, capturing is started and all captured data is forwarded
1851 * transparently to the client.
1852 *
1853 * Whenever possible the proxy should be used instead of opening the device
1854 * directly, since it allows the user to start multiple VBI clients in
1855 * parallel. When this function fails (usually because the user hasn't
1856 * started the proxy daemon) applications should automatically fall back
1857 * to opening the device directly.
1858 *
1859 * @return
1860 * Initialized vbi_capture context, @c NULL on failure.
1861 *
1862 * @since 0.2.9
1863 */
1864 vbi_capture *
vbi_capture_proxy_new(struct vbi_proxy_client * p_proxy_client,int buffers,int scanning,unsigned int * p_services,int strict,char ** pp_errorstr)1865 vbi_capture_proxy_new( struct vbi_proxy_client *p_proxy_client,
1866 int buffers, int scanning,
1867 unsigned int *p_services, int strict,
1868 char **pp_errorstr )
1869 {
1870 pthread_once (&vbi_init_once, vbi_init);
1871 asprintf(pp_errorstr, _("Proxy client interface not compiled."));
1872 return NULL;
1873 }
1874
1875 /**
1876 * @param vpc Pointer to initialized proxy client context
1877 *
1878 * This function closes the connection to the proxy daemon and frees
1879 * all resources. The given context must no longer be used after this
1880 * function was called. If the context was used via the capture device
1881 * interface, the vbi_capture context must be destroyed first.
1882 *
1883 * @since 0.2.9
1884 */
1885 void
vbi_proxy_client_destroy(vbi_proxy_client * vpc)1886 vbi_proxy_client_destroy( vbi_proxy_client * vpc )
1887 {
1888 vpc = vpc;
1889 }
1890
1891 /**
1892 * @param p_dev_name Name of the device to open, usually one of
1893 * @c /dev/vbi or @c /dev/vbi0 and up. Note: should be the same path as
1894 * used by the proxy daemon, else the client may not be able to connect.
1895 * @param p_client_name Name of the client application, typically identical
1896 * to argv[0] (without the path though) Can be used by the proxy daemon
1897 * to fine-tune scheduling or to present the user with a list of
1898 * currently connected applications.
1899 * @param client_flags Can contain one or more members of
1900 * VBI_PROXY_CLIENT_FLAGS
1901 * @param pp_errorstr If not @c NULL this function stores a pointer to an error
1902 * description here. You must free() this string when no longer needed.
1903 * @param trace_level Enable debug output to stderr if non-zero.
1904 * Larger values produce more output.
1905 *
1906 * This function initializes a proxy daemon client context with the given
1907 * parameters. (Note this function does not yet connect the daemon.)
1908 *
1909 * @return
1910 * Initialized proxy client context, @c NULL on failure
1911 *
1912 * @since 0.2.9
1913 */
1914 vbi_proxy_client *
vbi_proxy_client_create(const char * p_dev_name,const char * p_client_name,VBI_PROXY_CLIENT_FLAGS client_flags,char ** pp_errorstr,int trace_level)1915 vbi_proxy_client_create(const char *p_dev_name, const char *p_client_name,
1916 VBI_PROXY_CLIENT_FLAGS client_flags,
1917 char **pp_errorstr, int trace_level)
1918 {
1919 asprintf(pp_errorstr, _("Proxy client interface not compiled."));
1920 return NULL;
1921 }
1922
1923 #endif /* !ENABLE_PROXY */
1924
1925 /*
1926 Local variables:
1927 c-set-style: K&R
1928 c-basic-offset: 8
1929 End:
1930 */
1931