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