Merge branch 'nto-signal-stats'
[dvblast.git] / asi.c
blobadc9783c251dc2b6a05fd171aaf56a11cb451250
1 /*****************************************************************************
2 * asi.c: support for Computer Modules ASI cards
3 *****************************************************************************
4 * Copyright (C) 2004, 2009, 2015 VideoLAN
6 * Authors: Christophe Massiot <massiot@via.ecp.fr>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
21 *****************************************************************************/
22 #include "config.h"
24 #ifdef HAVE_ASI_SUPPORT
26 #include <stdlib.h>
27 #include <stdio.h>
28 #include <unistd.h>
29 #include <stdint.h>
30 #include <stdbool.h>
31 #include <string.h>
32 #include <sys/types.h>
33 #include <sys/stat.h>
34 #include <fcntl.h>
35 #include <sys/uio.h>
36 #include <sys/poll.h>
37 #include <sys/ioctl.h>
38 #include <sys/socket.h>
39 #include <netinet/in.h>
40 #include <arpa/inet.h>
41 #include <errno.h>
43 #include <ev.h>
45 #include <bitstream/common.h>
47 #include "asi.h"
49 #include "dvblast.h"
52 * The problem with hardware filtering is that on startup, when you only
53 * set a filter on PID 0, it can take a very long time for a large buffer
54 * (typically ~100 TS packets) to fill up. And the buffer size cannot be
55 * adjusted afer startup. --Meuuh
57 //#define USE_HARDWARE_FILTERING
59 /*****************************************************************************
60 * Local declarations
61 *****************************************************************************/
62 #define ASI_DEVICE "/dev/asirx%u"
63 #define ASI_TIMESTAMPS_FILE "/sys/class/asi/asirx%u/timestamps"
64 #define ASI_BUFSIZE_FILE "/sys/class/asi/asirx%u/bufsize"
65 #define ASI_LOCK_TIMEOUT 5000000 /* 5 s */
67 static int i_handle;
68 static struct ev_io asi_watcher;
69 static struct ev_timer mute_watcher;
70 static int i_bufsize;
71 static uint8_t p_pid_filter[8192 / 8];
72 static bool b_sync = false;
74 /*****************************************************************************
75 * Local prototypes
76 *****************************************************************************/
77 static void asi_Read(struct ev_loop *loop, struct ev_io *w, int revents);
78 static void asi_MuteCb(struct ev_loop *loop, struct ev_timer *w, int revents);
80 /*****************************************************************************
81 * Local helpers
82 *****************************************************************************/
83 #define MAXLEN 256
85 static int ReadULSysfs( const char *psz_fmt, unsigned int i_link )
87 char psz_file[MAXLEN], psz_data[MAXLEN];
88 char *psz_tmp;
89 int i_fd;
90 ssize_t i_ret;
91 unsigned int i_data;
93 snprintf( psz_file, sizeof(psz_file), psz_fmt, i_link );
94 psz_file[sizeof(psz_file) - 1] = '\0';
96 if ( (i_fd = open( psz_file, O_RDONLY )) < 0 )
97 return i_fd;
99 i_ret = read( i_fd, psz_data, sizeof(psz_data) );
100 close( i_fd );
102 if ( i_ret < 0 )
103 return i_ret;
105 i_data = strtoul( psz_data, &psz_tmp, 0 );
106 if ( *psz_tmp != '\n' )
107 return -1;
109 return i_data;
112 static ssize_t WriteULSysfs( const char *psz_fmt, unsigned int i_link,
113 unsigned int i_buf )
115 char psz_file[MAXLEN], psz_data[MAXLEN];
116 int i_fd;
117 ssize_t i_ret;
119 snprintf( psz_file, sizeof(psz_file), psz_fmt, i_link );
120 psz_file[sizeof(psz_file) - 1] = '\0';
122 snprintf( psz_data, sizeof(psz_data), "%u\n", i_buf );
123 psz_file[sizeof(psz_data) - 1] = '\0';
125 if ( (i_fd = open( psz_file, O_WRONLY )) < 0 )
126 return i_fd;
128 i_ret = write( i_fd, psz_data, strlen(psz_data) + 1 );
129 close( i_fd );
130 return i_ret;
133 /*****************************************************************************
134 * asi_Open
135 *****************************************************************************/
136 void asi_Open( void )
138 char psz_dev[MAXLEN];
140 /* No timestamp - we wouldn't know what to do with them */
141 if ( WriteULSysfs( ASI_TIMESTAMPS_FILE, i_asi_adapter, 0 ) < 0 )
143 msg_Err( NULL, "couldn't write file " ASI_TIMESTAMPS_FILE,
144 i_asi_adapter );
145 exit(EXIT_FAILURE);
148 if ( (i_bufsize = ReadULSysfs( ASI_BUFSIZE_FILE, i_asi_adapter )) < 0 )
150 msg_Err( NULL, "couldn't read file " ASI_BUFSIZE_FILE, i_asi_adapter );
151 exit(EXIT_FAILURE);
154 if ( i_bufsize % TS_SIZE )
156 msg_Err( NULL, ASI_BUFSIZE_FILE " must be a multiple of 188",
157 i_asi_adapter );
158 exit(EXIT_FAILURE);
161 snprintf( psz_dev, sizeof(psz_dev), ASI_DEVICE, i_asi_adapter );
162 psz_dev[sizeof(psz_dev) - 1] = '\0';
163 if ( (i_handle = open( psz_dev, O_RDONLY, 0 )) < 0 )
165 msg_Err( NULL, "couldn't open device " ASI_DEVICE " (%s)",
166 i_asi_adapter, strerror(errno) );
167 exit(EXIT_FAILURE);
170 #ifdef USE_HARDWARE_FILTERING
171 memset( p_pid_filter, 0x0, sizeof(p_pid_filter) );
172 #else
173 memset( p_pid_filter, 0xff, sizeof(p_pid_filter) );
174 #endif
175 if ( ioctl( i_handle, ASI_IOC_RXSETPF, p_pid_filter ) < 0 )
177 msg_Warn( NULL, "couldn't filter padding" );
180 fsync( i_handle );
182 ev_io_init(&asi_watcher, asi_Read, i_handle, EV_READ);
183 ev_io_start(event_loop, &asi_watcher);
185 ev_timer_init(&mute_watcher, asi_MuteCb,
186 ASI_LOCK_TIMEOUT / 1000000., ASI_LOCK_TIMEOUT / 1000000.);
189 /*****************************************************************************
190 * ASI events
191 *****************************************************************************/
192 static void asi_Read(struct ev_loop *loop, struct ev_io *w, int revents)
194 unsigned int i_val;
196 if ( ioctl(i_handle, ASI_IOC_RXGETEVENTS, &i_val) == 0 )
198 if ( i_val & ASI_EVENT_RX_BUFFER )
199 msg_Warn( NULL, "driver receive buffer queue overrun" );
200 if ( i_val & ASI_EVENT_RX_FIFO )
201 msg_Warn( NULL, "onboard receive FIFO overrun" );
202 if ( i_val & ASI_EVENT_RX_CARRIER )
203 msg_Warn( NULL, "carrier status change" );
204 if ( i_val & ASI_EVENT_RX_LOS )
205 msg_Warn( NULL, "loss of packet synchronization" );
206 if ( i_val & ASI_EVENT_RX_AOS )
207 msg_Warn( NULL, "acquisition of packet synchronization" );
208 if ( i_val & ASI_EVENT_RX_DATA )
209 msg_Warn( NULL, "receive data status change" );
212 struct iovec p_iov[i_bufsize / TS_SIZE];
213 block_t *p_ts, **pp_current = &p_ts;
214 int i, i_len;
216 for ( i = 0; i < i_bufsize / TS_SIZE; i++ )
218 *pp_current = block_New();
219 p_iov[i].iov_base = (*pp_current)->p_ts;
220 p_iov[i].iov_len = TS_SIZE;
221 pp_current = &(*pp_current)->p_next;
224 if ( (i_len = readv(i_handle, p_iov, i_bufsize / TS_SIZE)) < 0 )
226 msg_Err( NULL, "couldn't read from device " ASI_DEVICE " (%s)",
227 i_asi_adapter, strerror(errno) );
228 i_len = 0;
230 i_len /= TS_SIZE;
232 if ( i_len )
234 if ( !b_sync )
236 msg_Info( NULL, "frontend has acquired lock" );
237 switch (i_print_type) {
238 case PRINT_XML:
239 fprintf(print_fh, "<STATUS type=\"lock\" status=\"1\"/>\n");
240 break;
241 case PRINT_TEXT:
242 fprintf(print_fh, "lock status: 1\n");
243 break;
244 default:
245 break;
248 b_sync = true;
251 ev_timer_again(loop, &mute_watcher);
254 pp_current = &p_ts;
255 while ( i_len && *pp_current )
257 pp_current = &(*pp_current)->p_next;
258 i_len--;
261 if ( *pp_current )
262 msg_Dbg( NULL, "partial buffer received" );
263 block_DeleteChain( *pp_current );
264 *pp_current = NULL;
266 demux_Run( p_ts );
269 static void asi_MuteCb(struct ev_loop *loop, struct ev_timer *w, int revents)
271 msg_Warn( NULL, "frontend has lost lock" );
272 ev_timer_stop(loop, w);
274 switch (i_print_type) {
275 case PRINT_XML:
276 fprintf(print_fh, "<STATUS type=\"lock\" status=\"0\"/>\n");
277 break;
278 case PRINT_TEXT:
279 fprintf(print_fh, "lock status: 0\n" );
280 break;
281 default:
282 break;
286 /*****************************************************************************
287 * asi_SetFilter
288 *****************************************************************************/
289 int asi_SetFilter( uint16_t i_pid )
291 #ifdef USE_HARDWARE_FILTERING
292 p_pid_filter[ i_pid / 8 ] |= (0x01 << (i_pid % 8));
293 if ( ioctl( i_handle, ASI_IOC_RXSETPF, p_pid_filter ) < 0 )
294 msg_Warn( NULL, "couldn't add filter on PID %u", i_pid );
296 return 1;
297 #else
298 return -1;
299 #endif
302 /*****************************************************************************
303 * asi_UnsetFilter: normally never called
304 *****************************************************************************/
305 void asi_UnsetFilter( int i_fd, uint16_t i_pid )
307 #ifdef USE_HARDWARE_FILTERING
308 p_pid_filter[ i_pid / 8 ] &= ~(0x01 << (i_pid % 8));
309 if ( ioctl( i_handle, ASI_IOC_RXSETPF, p_pid_filter ) < 0 )
310 msg_Warn( NULL, "couldn't remove filter on PID %u", i_pid );
311 #endif
314 /*****************************************************************************
315 * asi_Reset
316 *****************************************************************************/
317 void asi_Reset( void )
319 msg_Warn( NULL, "asi_Reset() do nothing" );
322 #endif