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 *****************************************************************************/
24 #ifdef HAVE_ASI_SUPPORT
32 #include <sys/types.h>
37 #include <sys/ioctl.h>
38 #include <sys/socket.h>
39 #include <netinet/in.h>
40 #include <arpa/inet.h>
45 #include <bitstream/common.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 /*****************************************************************************
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 */
68 static struct ev_io asi_watcher
;
69 static struct ev_timer mute_watcher
;
71 static uint8_t p_pid_filter
[8192 / 8];
72 static bool b_sync
= false;
74 /*****************************************************************************
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 /*****************************************************************************
82 *****************************************************************************/
85 static int ReadULSysfs( const char *psz_fmt
, unsigned int i_link
)
87 char psz_file
[MAXLEN
], psz_data
[MAXLEN
];
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 )
99 i_ret
= read( i_fd
, psz_data
, sizeof(psz_data
) );
105 i_data
= strtoul( psz_data
, &psz_tmp
, 0 );
106 if ( *psz_tmp
!= '\n' )
112 static ssize_t
WriteULSysfs( const char *psz_fmt
, unsigned int i_link
,
115 char psz_file
[MAXLEN
], psz_data
[MAXLEN
];
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 )
128 i_ret
= write( i_fd
, psz_data
, strlen(psz_data
) + 1 );
133 /*****************************************************************************
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
,
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
);
154 if ( i_bufsize
% TS_SIZE
)
156 msg_Err( NULL
, ASI_BUFSIZE_FILE
" must be a multiple of 188",
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
) );
170 #ifdef USE_HARDWARE_FILTERING
171 memset( p_pid_filter
, 0x0, sizeof(p_pid_filter
) );
173 memset( p_pid_filter
, 0xff, sizeof(p_pid_filter
) );
175 if ( ioctl( i_handle
, ASI_IOC_RXSETPF
, p_pid_filter
) < 0 )
177 msg_Warn( NULL
, "couldn't filter padding" );
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 /*****************************************************************************
191 *****************************************************************************/
192 static void asi_Read(struct ev_loop
*loop
, struct ev_io
*w
, int revents
)
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
;
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
) );
236 msg_Info( NULL
, "frontend has acquired lock" );
237 switch (i_print_type
) {
239 fprintf(print_fh
, "<STATUS type=\"lock\" status=\"1\"/>\n");
242 fprintf(print_fh
, "lock status: 1\n");
251 ev_timer_again(loop
, &mute_watcher
);
255 while ( i_len
&& *pp_current
)
257 pp_current
= &(*pp_current
)->p_next
;
262 msg_Dbg( NULL
, "partial buffer received" );
263 block_DeleteChain( *pp_current
);
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
) {
276 fprintf(print_fh
, "<STATUS type=\"lock\" status=\"0\"/>\n");
279 fprintf(print_fh
, "lock status: 0\n" );
286 /*****************************************************************************
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
);
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
);
314 /*****************************************************************************
316 *****************************************************************************/
317 void asi_Reset( void )
319 msg_Warn( NULL
, "asi_Reset() do nothing" );