You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
913 lines
24 KiB
913 lines
24 KiB
/*
|
|
* dvbcam.cpp
|
|
*
|
|
* Copyright (C) 2008 Christophe Thommeret <hftom@free.fr>
|
|
* Copyright (C) 2006 Christoph Pfister <christophpfister@gmail.com>
|
|
*
|
|
* code based on ca_zap (LGPL)
|
|
* Copyright (C) 2004, 2005 Manu Abraham (manu@kromtek.com)
|
|
* Copyright (C) 2006 Andrew de Quincey (adq_dvb@lidskialf.net)
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
|
*/
|
|
|
|
#include <unistd.h>
|
|
#include <stdio.h>
|
|
#include <fcntl.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/poll.h>
|
|
#include <linux/dvb/ca.h>
|
|
|
|
#include <tqapplication.h>
|
|
#include <tqthread.h>
|
|
#include <tqstring.h>
|
|
#include <tqspinbox.h>
|
|
#include <tqlabel.h>
|
|
#include <tqtextbrowser.h>
|
|
#include <tqlineedit.h>
|
|
#include <tqpushbutton.h>
|
|
|
|
#include "dvbcam.h"
|
|
|
|
#define TIMER_EVENT_MMI_OPEN 500
|
|
#define TIMER_EVENT_MMI_CLOSE 501
|
|
#define TIMER_EVENT_CAM_READY 502
|
|
|
|
|
|
|
|
DvbCam::DvbCam(int adapter, int ca_device, int demux_device, int ci_type, int maxService)
|
|
{
|
|
Adapter = adapter;
|
|
CaDevice = ca_device;
|
|
DemuxDevice = demux_device;
|
|
ciType = ci_type;
|
|
CamMaxService = maxService;
|
|
fprintf(stderr, "DvbCam: CamMaxService = %d\n", CamMaxService);
|
|
|
|
stdcam = NULL;
|
|
menuDialog = NULL;
|
|
|
|
sidList.setAutoDelete( true );
|
|
|
|
isRunning = true;
|
|
start();
|
|
}
|
|
|
|
DvbCam::~DvbCam()
|
|
{
|
|
isRunning = false;
|
|
wait();
|
|
sidMutex.lock();
|
|
sidList.clear();
|
|
sidMutex.unlock();
|
|
}
|
|
|
|
int DvbCam::probe( int adapter, int ca_device )
|
|
{
|
|
int fdCa;
|
|
ca_caps_t cap;
|
|
ca_slot_info_t info;
|
|
TQString s = TQString("/dev/dvb/adapter%1/ca%2").tqarg( adapter ).tqarg( ca_device );
|
|
|
|
if ( (fdCa = open( s.ascii(), O_RDWR )) < 0) {
|
|
fprintf(stderr, "DvbCam::probe(): %s:", s.ascii());
|
|
perror(" ");
|
|
return -1;
|
|
}
|
|
|
|
if ( ioctl(fdCa, CA_GET_CAP, &cap) == 0) {
|
|
if ( cap.slot_num>0 ) {
|
|
info.num = 0;
|
|
if ( ioctl(fdCa, CA_GET_SLOT_INFO, &info) == 0) {
|
|
if ( (info.type & CA_CI_LINK)!=0) {
|
|
fprintf(stderr, "DvbCam::probe(): LLCI slot found on %s\n", s.ascii());
|
|
if ( (info.flags & CA_CI_MODULE_PRESENT)!=0 ) {
|
|
fprintf(stderr, "DvbCam::probe(): CA module present on %s\n", s.ascii());
|
|
close (fdCa);
|
|
return CA_CI_LINK;
|
|
}
|
|
else
|
|
fprintf(stderr, "DvbCam::probe(): no CA module present on %s\n", s.ascii());
|
|
}
|
|
else if ( (info.type & CA_CI)!=0 ) {
|
|
fprintf(stderr, "DvbCam::probe(): HLCI slot found on %s\n", s.ascii());
|
|
if ( (info.flags & CA_CI_MODULE_PRESENT)!=0 ) {
|
|
fprintf(stderr, "DvbCam::probe(): CA module present on %s\n", s.ascii());
|
|
close (fdCa);
|
|
return CA_CI;
|
|
}
|
|
else
|
|
fprintf(stderr, "DvbCam::probe(): no CA module present on %s\n", s.ascii());
|
|
}
|
|
else
|
|
fprintf(stderr, "DvbCam::probe(): unsupported CI interface on %s\n", s.ascii());
|
|
}
|
|
else
|
|
fprintf(stderr, "DvbCam::Probe(): ioctl failed on %s\n", s.ascii());
|
|
}
|
|
else
|
|
fprintf(stderr, "DvbCam::Probe(): no CI slot found on %s\n", s.ascii());
|
|
}
|
|
else
|
|
fprintf(stderr, "DvbCam::Probe(): ioctl failed on %s\n", s.ascii());
|
|
|
|
close (fdCa);
|
|
return -1;
|
|
}
|
|
|
|
bool DvbCam::canPlay( ChannelDesc *chan )
|
|
{
|
|
int i, n=0;
|
|
|
|
TQMutexLocker locker( &sidMutex );
|
|
for ( i=0; i<sidList.count(); ++i ) {
|
|
if ( sidList.at(i)->getState()<CamService::Remove )
|
|
n++;
|
|
if ( sidList.at(i)->getChannel().name==chan->name )
|
|
return true;
|
|
}
|
|
return ( n<CamMaxService );
|
|
}
|
|
|
|
void DvbCam::startService( ChannelDesc *chan )
|
|
{
|
|
int i;
|
|
TQMutexLocker locker( &sidMutex );
|
|
for ( i=0; i<sidList.count(); ++i ) {
|
|
if ( sidList.at(i)->getChannel().name==chan->name ) {
|
|
sidList.at(i)->restart();
|
|
return;
|
|
}
|
|
}
|
|
sidList.append( new CamService( Adapter, DemuxDevice, chan, CamMaxService ) );
|
|
}
|
|
|
|
void DvbCam::stopService( ChannelDesc *chan )
|
|
{
|
|
int i;
|
|
TQMutexLocker locker( &sidMutex );
|
|
for ( i=0; i<sidList.count(); ++i ) {
|
|
if ( sidList.at(i)->getChannel().name==chan->name ) {
|
|
sidList.at(i)->setState( CamService::Remove );
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void DvbCam::resendPmts()
|
|
{
|
|
int i;
|
|
TQMutexLocker locker( &sidMutex );
|
|
for ( i=0; i<sidList.count(); ++i ) {
|
|
if ( sidList.at(i)->getState()==CamService::Added ) {
|
|
sidList.at(i)->setState( CamService::Ready );
|
|
}
|
|
}
|
|
}
|
|
|
|
void DvbCam::run()
|
|
{
|
|
int i, reset_loop, query_loop, state_loop;
|
|
bool cam_ready = false;
|
|
|
|
fprintf(stderr, "CamThread: started\n");
|
|
|
|
int ca_fd = dvbca_open(Adapter, CaDevice);
|
|
if ( ca_fd<0 ) {
|
|
fprintf( stderr, "CamThread: [error] opening ca device failed\n" );
|
|
return;
|
|
}
|
|
|
|
//fprintf(stderr, "CamThread: just using the first cam slot\n");
|
|
|
|
reset_loop=0;
|
|
while ( isRunning && reset_loop++<6 && !cam_ready ) {
|
|
if ( ciType!=CA_CI ) { // do not reset HLCI
|
|
if ( dvbca_reset(ca_fd, 0) ) {
|
|
fprintf( stderr, "CamThread: [error] resetting cam slot failed\n" );
|
|
//close( ca_fd );
|
|
//ca_fd = -1;
|
|
//return;
|
|
usleep(1000000);
|
|
continue;
|
|
}
|
|
fprintf( stderr, "CamThread: reset cam slot\n" );
|
|
}
|
|
|
|
state_loop=0;
|
|
query_loop=0;
|
|
while ( isRunning && state_loop++<30 ) {
|
|
switch( dvbca_get_cam_state(ca_fd, 0) ) {
|
|
case DVBCA_CAMSTATE_MISSING: {
|
|
//fprintf(stderr, "CamThread: [error] no cam detected\n");
|
|
//close(ca_fd);
|
|
//return; // FIXME: find a more reliable solution
|
|
break;
|
|
}
|
|
case DVBCA_CAMSTATE_READY: {
|
|
fprintf( stderr, "CamThread: cam 0 is ready\n" );
|
|
cam_ready = true;
|
|
break;
|
|
}
|
|
case DVBCA_CAMSTATE_INITIALISING: {
|
|
fprintf( stderr, "CamThread: cam is initialising\n" );
|
|
if ( ciType==CA_CI ) { // workaround needed for hlci
|
|
fprintf(stderr, "CamThread: cam 0 is ready [hlci workaround]\n");
|
|
cam_ready = true;
|
|
}
|
|
break;
|
|
}
|
|
default: {
|
|
if ( ++query_loop>3 ) {
|
|
fprintf(stderr, "CamThread: [error] querying the cam state failed\n");
|
|
close(ca_fd);
|
|
ca_fd = -1;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
if(cam_ready) {
|
|
break;
|
|
}
|
|
usleep(100000); // 100 ms
|
|
}
|
|
}
|
|
|
|
if ( isRunning ) {
|
|
switch(dvbca_get_interface_type(ca_fd, 0)) {
|
|
case DVBCA_INTERFACE_LINK: {
|
|
fprintf(stderr, "CamThread: LLCI cam slot detected\n");
|
|
break;
|
|
}
|
|
case DVBCA_INTERFACE_HLCI: {
|
|
fprintf(stderr, "CamThread: HLCI cam slot detected\n");
|
|
break;
|
|
}
|
|
default: {
|
|
fprintf(stderr, "CamThread: [error] unknown cam slot type\n");
|
|
close(ca_fd);
|
|
ca_fd = -1;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
close(ca_fd);
|
|
ca_fd = -1;
|
|
|
|
if ( isRunning ) {
|
|
if ( !init() ) {
|
|
fprintf(stderr, "CamThread: [error] cam slot initialisation failed\n");
|
|
return;
|
|
}
|
|
}
|
|
fprintf(stderr, "CamThread: cam slot initialised\n");
|
|
|
|
CamService *cs;
|
|
while ( isRunning ) {
|
|
if ( stdcam->stdcam->poll( stdcam->stdcam )!=EN50221_STDCAM_CAM_OK ) {
|
|
usleep( 100000 );
|
|
continue;
|
|
}
|
|
sidMutex.lock();
|
|
for ( i=0; i<sidList.count(); ++i ) {
|
|
cs = sidList.at(i);
|
|
if ( cs->getState()==CamService::Remove ) {
|
|
if ( sendPmt( cs->caPmt, cs->caPmtSize ) ) {
|
|
fprintf( stderr, "CamThread: %s removed from camlist\n", cs->getChannel().name.ascii() );
|
|
sidList.remove( cs );
|
|
--i;
|
|
}
|
|
else
|
|
fprintf( stderr, "CamThread: %s failed removing from camlist\n", cs->getChannel().name.ascii() );
|
|
stdcam->stdcam->poll( stdcam->stdcam );
|
|
usleep(100000);
|
|
}
|
|
else if ( cs->getState()==CamService::Destroy ) {
|
|
fprintf( stderr, "CamThread: %s service deleted\n", cs->getChannel().name.ascii() );
|
|
sidList.remove( cs );
|
|
--i;
|
|
}
|
|
}
|
|
for ( i=0; i<sidList.count(); ++i ) {
|
|
cs = sidList.at(i);
|
|
if ( cs->getState()==CamService::Ready ) {
|
|
if ( sendPmt( cs->caPmt, cs->caPmtSize ) ) {
|
|
cs->setState( CamService::Added );
|
|
fprintf( stderr, "CamThread: %s pmt sent to cam\n", cs->getChannel().name.ascii() );
|
|
}
|
|
else
|
|
fprintf( stderr, "CamThread: %s pmt failed sending to cam\n", cs->getChannel().name.ascii() );
|
|
stdcam->stdcam->poll( stdcam->stdcam );
|
|
usleep(100000);
|
|
}
|
|
}
|
|
sidMutex.unlock();
|
|
usleep( 10000 ); //sleep a bit
|
|
}
|
|
|
|
fprintf(stderr, "CamThread: stopping requested\n");
|
|
|
|
if ( stdcam ) {
|
|
if (stdcam->stdcam->destroy)
|
|
stdcam->stdcam->destroy(stdcam->stdcam, 1);
|
|
en50221_sl_destroy( SessionLayer );
|
|
en50221_tl_destroy( TransportLayer );
|
|
delete stdcam;
|
|
}
|
|
|
|
fprintf(stderr, "CamThread: stopped\n");
|
|
return;
|
|
}
|
|
|
|
bool DvbCam::init()
|
|
{
|
|
TransportLayer = en50221_tl_create(1, 16);
|
|
if ( TransportLayer==NULL ) {
|
|
fprintf(stderr, "Failed to create transport layer\n");
|
|
return false;
|
|
}
|
|
SessionLayer = en50221_sl_create(TransportLayer, 16);
|
|
if ( SessionLayer==NULL ) {
|
|
fprintf(stderr, "Failed to create session layer\n");
|
|
en50221_tl_destroy( TransportLayer );
|
|
return false;
|
|
}
|
|
en50221_stdcam *sc = en50221_stdcam_create( Adapter, 0, TransportLayer, SessionLayer );
|
|
if ( sc==NULL ) {
|
|
en50221_sl_destroy( SessionLayer );
|
|
en50221_tl_destroy( TransportLayer );
|
|
fprintf(stderr, "Failed to create stdcam\n");
|
|
return false;
|
|
}
|
|
|
|
stdcam = new StandardCam( sc, this );
|
|
|
|
// hook up the AI callbacks
|
|
if ( stdcam->stdcam->ai_resource ) {
|
|
en50221_app_ai_register_callback( stdcam->stdcam->ai_resource, aiCallback, stdcam );
|
|
}
|
|
|
|
// hook up the CA callbacks
|
|
if ( stdcam->stdcam->ca_resource ) {
|
|
en50221_app_ca_register_info_callback( stdcam->stdcam->ca_resource, infoCallback, stdcam );
|
|
}
|
|
|
|
// hook up the MMI callbacks
|
|
if ( stdcam->stdcam->mmi_resource ) {
|
|
en50221_app_mmi_register_close_callback( stdcam->stdcam->mmi_resource, mmi_close_callback, stdcam );
|
|
en50221_app_mmi_register_display_control_callback( stdcam->stdcam->mmi_resource, mmi_display_control_callback, stdcam );
|
|
en50221_app_mmi_register_enq_callback( stdcam->stdcam->mmi_resource, mmi_enq_callback, stdcam );
|
|
en50221_app_mmi_register_menu_callback( stdcam->stdcam->mmi_resource, mmi_menu_callback, stdcam );
|
|
en50221_app_mmi_register_list_callback( stdcam->stdcam->mmi_resource, mmi_menu_callback, stdcam );
|
|
} else {
|
|
fprintf(stderr, "CAM Menus are not supported by this interface hardware\n");
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool DvbCam::sendPmt( unsigned char *pmt_buffer, int size )
|
|
{
|
|
if ( !stdcam )
|
|
return false;
|
|
if ( en50221_app_ca_pmt( stdcam->stdcam->ca_resource, stdcam->stdcam->ca_session_number, pmt_buffer, size) )
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
int DvbCam::infoCallback(void *arg, uint8_t slot_id, uint16_t session_number, uint32_t ca_id_count, uint16_t *ca_ids)
|
|
{
|
|
StandardCam *std = (StandardCam*)arg;
|
|
(void)slot_id;
|
|
(void)session_number;
|
|
|
|
fprintf(stderr, "CAM supports the following ca system ids:\n");
|
|
uint32_t i;
|
|
for ( i=0; i<ca_id_count; i++ ) {
|
|
fprintf( stderr, " 0x%04x\n", ca_ids[i]);
|
|
}
|
|
|
|
TQApplication::postEvent( std->dvbcam, new TQTimerEvent(TIMER_EVENT_CAM_READY ) );
|
|
|
|
return 0;
|
|
}
|
|
|
|
int DvbCam::aiCallback(void *arg, uint8_t slot_id, uint16_t session_number, uint8_t application_type, uint16_t application_manufacturer, uint16_t manufacturer_code, uint8_t menu_string_length, uint8_t *menu_string)
|
|
{
|
|
StandardCam *std = (StandardCam*)arg;
|
|
(void)slot_id;
|
|
(void)session_number;
|
|
|
|
fprintf(stderr, "CAM Application type: %02x\n", application_type);
|
|
std->dvbcam->setAppType( TQString("0x%1").tqarg(application_type, 0, 16 ) );
|
|
fprintf(stderr, "CAM Application manufacturer: %04x\n", application_manufacturer);
|
|
std->dvbcam->setAppManu( TQString("0x%1").tqarg(application_manufacturer, 0, 16 ) );
|
|
fprintf(stderr, "CAM Manufacturer code: %04x\n", manufacturer_code);
|
|
std->dvbcam->setManuCode( TQString("0x%1").tqarg(manufacturer_code, 0, 16 ) );
|
|
fprintf(stderr, "CAM Menu string: %.*s\n", menu_string_length, menu_string);
|
|
TQString s = (const char*)menu_string;
|
|
s.truncate( menu_string_length );
|
|
std->dvbcam->setMenuString( s );
|
|
return 0;
|
|
}
|
|
|
|
int DvbCam::mmi_close_callback(void *arg, uint8_t slot_id, uint16_t session_number, uint8_t cmd_id, uint8_t delay)
|
|
{
|
|
fprintf( stderr,"mmi_close_callback, delay=%d\n",delay);
|
|
StandardCam *std = (StandardCam*)arg;
|
|
(void) slot_id;
|
|
(void) session_number;
|
|
(void) cmd_id;
|
|
(void) delay;
|
|
|
|
// note: not entirely correct as its supposed to delay if asked
|
|
std->mmi_state = MMI_STATE_CLOSED;
|
|
TQApplication::postEvent( std->dvbcam, new TQTimerEvent(TIMER_EVENT_MMI_CLOSE ) );
|
|
return 0;
|
|
}
|
|
|
|
int DvbCam::mmi_display_control_callback(void *arg, uint8_t slot_id, uint16_t session_number, uint8_t cmd_id, uint8_t mmi_mode)
|
|
{
|
|
fprintf( stderr,"mmi_display_control_callback\n");
|
|
struct en50221_app_mmi_display_reply_details reply;
|
|
StandardCam *std = (StandardCam*)arg;
|
|
(void) slot_id;
|
|
|
|
// don't support any commands but set mode
|
|
if ( cmd_id!=MMI_DISPLAY_CONTROL_CMD_ID_SET_MMI_MODE ) {
|
|
en50221_app_mmi_display_reply( std->stdcam->mmi_resource, session_number, MMI_DISPLAY_REPLY_ID_UNKNOWN_CMD_ID, &reply );
|
|
return 0;
|
|
}
|
|
|
|
// we only support high level mode
|
|
if ( mmi_mode!=MMI_MODE_HIGH_LEVEL ) {
|
|
en50221_app_mmi_display_reply( std->stdcam->mmi_resource, session_number, MMI_DISPLAY_REPLY_ID_UNKNOWN_MMI_MODE, &reply );
|
|
return 0;
|
|
}
|
|
|
|
// ack the high level open
|
|
reply.u.mode_ack.mmi_mode = mmi_mode;
|
|
en50221_app_mmi_display_reply( std->stdcam->mmi_resource, session_number, MMI_DISPLAY_REPLY_ID_MMI_MODE_ACK, &reply );
|
|
std->mmi_state = MMI_STATE_OPEN;
|
|
return 0;
|
|
}
|
|
|
|
int DvbCam::mmi_enq_callback(void *arg, uint8_t slot_id, uint16_t session_number, uint8_t blind_answer, uint8_t expected_answer_length,
|
|
uint8_t *text, uint32_t text_size)
|
|
{
|
|
fprintf( stderr,"mmi_enq_callback\n");
|
|
StandardCam *std = (StandardCam*)arg;
|
|
(void) slot_id;
|
|
(void) session_number;
|
|
|
|
TQString s;
|
|
|
|
TQMutexLocker locker( &std->mutex );
|
|
|
|
std->menuList.clear();
|
|
fprintf(stderr, "%.*s: ", text_size, text);
|
|
s = (const char*)text;
|
|
s.truncate( text_size );
|
|
std->menuList.append( s );
|
|
fflush(stdout);
|
|
|
|
std->mmi_enq_blind = blind_answer;
|
|
std->mmi_enq_length = expected_answer_length;
|
|
std->mmi_state = MMI_STATE_ENQ;
|
|
std->menuType = MMI_MENU;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int DvbCam::mmi_menu_callback(void *arg, uint8_t slot_id, uint16_t session_number, struct en50221_app_mmi_text *title,
|
|
struct en50221_app_mmi_text *sub_title, struct en50221_app_mmi_text *bottom, uint32_t item_count,
|
|
struct en50221_app_mmi_text *items, uint32_t item_raw_length, uint8_t *items_raw)
|
|
{
|
|
fprintf( stderr,"mmi_menu_callback, session=%d\n",session_number);
|
|
StandardCam *std = (StandardCam*)arg;
|
|
(void) slot_id;
|
|
(void) session_number;
|
|
(void) item_raw_length;
|
|
(void) items_raw;
|
|
|
|
TQString s;
|
|
|
|
TQMutexLocker locker( &std->mutex );
|
|
std->menuList.clear();
|
|
|
|
fprintf(stderr, "------------------------------\n");
|
|
|
|
if (title->text_length) {
|
|
fprintf(stderr, "%.*s\n", title->text_length, title->text);
|
|
s = (const char*)title->text;
|
|
s.truncate( title->text_length );
|
|
std->menuList.append( s );
|
|
}
|
|
if (sub_title->text_length) {
|
|
fprintf(stderr, "%.*s\n", sub_title->text_length, sub_title->text);
|
|
s = (const char*)sub_title->text;
|
|
s.truncate( sub_title->text_length );
|
|
std->menuList.append( s );
|
|
}
|
|
|
|
uint32_t i;
|
|
fprintf(stderr, "0. Quit menu\n");
|
|
std->menuList.append( "0. Quit menu" );
|
|
for(i=0; i< item_count; i++) {
|
|
fprintf(stderr, "%i. %.*s\n", i+1, items[i].text_length, items[i].text);
|
|
s = (const char*)items[i].text;
|
|
s.truncate( items[i].text_length );
|
|
std->menuList.append( TQString("%1. %2").tqarg(i+1).tqarg(s) );
|
|
|
|
}
|
|
|
|
if (bottom->text_length) {
|
|
fprintf(stderr, "%.*s\n", bottom->text_length, bottom->text);
|
|
s = (const char*)bottom->text;
|
|
s.truncate( bottom->text_length );
|
|
std->menuList.append( s );
|
|
}
|
|
fflush(stdout);
|
|
|
|
std->mmi_state = MMI_STATE_MENU;
|
|
std->menuType = MMI_MENU;
|
|
|
|
TQApplication::postEvent( std->dvbcam, new TQTimerEvent(TIMER_EVENT_MMI_OPEN ) );
|
|
|
|
return 0;
|
|
}
|
|
|
|
void DvbCam::timerEvent( TQTimerEvent *e )
|
|
{
|
|
switch ( e->timerId() ) {
|
|
case TIMER_EVENT_MMI_OPEN:
|
|
showMMI();
|
|
break;
|
|
case TIMER_EVENT_MMI_CLOSE:
|
|
closeMMI();
|
|
break;
|
|
case TIMER_EVENT_CAM_READY:
|
|
resendPmts();
|
|
break;
|
|
}
|
|
}
|
|
|
|
void DvbCam::showMMI()
|
|
{
|
|
if ( !menuDialog && stdcam ) {
|
|
menuDialog = new MCamMenuDialog( stdcam );
|
|
connect( menuDialog, TQT_SIGNAL(enteredResponse(TQString)), this, TQT_SLOT(mmiResponse(TQString)) );
|
|
menuDialog->exec();
|
|
closeMMI();
|
|
}
|
|
}
|
|
|
|
void DvbCam::closeMMI()
|
|
{
|
|
if ( menuDialog ) {
|
|
delete menuDialog;
|
|
menuDialog = 0;
|
|
if ( stdcam && stdcam->stdcam->mmi_resource ) {
|
|
en50221_app_mmi_close( stdcam->stdcam->mmi_resource, stdcam->stdcam->mmi_session_number, MMI_CLOSE_MMI_CMD_ID_IMMEDIATE, 0 );
|
|
stdcam->mmi_state = MMI_STATE_CLOSED;
|
|
}
|
|
}
|
|
}
|
|
|
|
int DvbCam::showCamDialog()
|
|
{
|
|
|
|
CamDialog dlg;
|
|
dlg.maxServiceSpin->setValue( getCamMaxService() );
|
|
dlg.appTypeLab->setText( getAppType() );
|
|
dlg.appManuLab->setText( getAppManu() );
|
|
dlg.manuCodeLab->setText( getManuCode() );
|
|
dlg.menuStringLab->setText( getMenuString() );
|
|
connect( dlg.camMenuBtn, TQT_SIGNAL(clicked()), this, TQT_SLOT(enterMenu()) );
|
|
dlg.exec();
|
|
CamMaxService = dlg.maxServiceSpin->value();
|
|
return CamMaxService;
|
|
}
|
|
|
|
void DvbCam::enterMenu()
|
|
{
|
|
if ( stdcam && stdcam->stdcam->ai_resource )
|
|
en50221_app_ai_entermenu( stdcam->stdcam->ai_resource, stdcam->stdcam->ai_session_number );
|
|
}
|
|
|
|
void DvbCam::mmiResponse( TQString s )
|
|
{
|
|
TQString res = s.stripWhiteSpace();
|
|
|
|
switch( stdcam->mmi_state ) {
|
|
case MMI_STATE_CLOSED:
|
|
case MMI_STATE_OPEN: {
|
|
en50221_app_ai_entermenu(stdcam->stdcam->ai_resource, stdcam->stdcam->ai_session_number);
|
|
fprintf(stderr,"en50221_app_ai_entermenu 2\n");
|
|
break;
|
|
}
|
|
case MMI_STATE_ENQ: {
|
|
if ( res.isEmpty() ) {
|
|
en50221_app_mmi_answ( stdcam->stdcam->mmi_resource, stdcam->stdcam->mmi_session_number, MMI_ANSW_ID_CANCEL, NULL, 0);
|
|
}
|
|
else {
|
|
en50221_app_mmi_answ( stdcam->stdcam->mmi_resource, stdcam->stdcam->mmi_session_number, MMI_ANSW_ID_ANSWER, (uint8_t*)res.ascii(), res.length() );
|
|
}
|
|
stdcam->mmi_state = MMI_STATE_OPEN;
|
|
break;
|
|
}
|
|
case MMI_STATE_MENU: {
|
|
en50221_app_mmi_menu_answ( stdcam->stdcam->mmi_resource, stdcam->stdcam->mmi_session_number, res.toInt() );
|
|
stdcam->mmi_state = MMI_STATE_OPEN;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
MCamMenuDialog::MCamMenuDialog( StandardCam *sc )
|
|
{
|
|
stdcam = sc;
|
|
connect( inputLine, TQT_SIGNAL(returnPressed()), this, TQT_SLOT(validateClicked()) );
|
|
connect( &readTimer, TQT_SIGNAL(timeout()), this, TQT_SLOT(setMenu()) );
|
|
readTimer.start( 500 );
|
|
}
|
|
|
|
void MCamMenuDialog::setMenu()
|
|
{
|
|
if ( !stdcam )
|
|
return;
|
|
TQMutexLocker locker( &stdcam->mutex );
|
|
if ( stdcam->menuType==MMI_MENU )
|
|
menuText->setText( stdcam->menuList.join("\n") );
|
|
}
|
|
|
|
void MCamMenuDialog::validateClicked()
|
|
{
|
|
emit enteredResponse( inputLine->text() );
|
|
inputLine->clear();
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// class CamService
|
|
CamService::CamService( int adapter, int demux_device, ChannelDesc *chan, int maxService )
|
|
{
|
|
Adapter = adapter;
|
|
DemuxDevice = demux_device;
|
|
CamMaxService = maxService;
|
|
channel = *chan;
|
|
parsedPmt = NULL;
|
|
state = NotReady;
|
|
isRunning = true;
|
|
start();
|
|
}
|
|
|
|
CamService::~CamService()
|
|
{
|
|
stop();
|
|
}
|
|
|
|
void CamService::restart()
|
|
{
|
|
setState( NotReady );
|
|
}
|
|
|
|
void CamService::stop()
|
|
{
|
|
isRunning = false;
|
|
wait();
|
|
}
|
|
|
|
int CamService::createSectionFilter(uint16_t pid, uint8_t table_id)
|
|
{
|
|
// open the demuxer
|
|
int demux_fd = dvbdemux_open_demux(Adapter, DemuxDevice, 0);
|
|
if(demux_fd < 0) {
|
|
return -1;
|
|
}
|
|
|
|
// create a section filter
|
|
uint8_t filter[18] = {table_id};
|
|
uint8_t mask[18] = {0xff};
|
|
if(dvbdemux_set_section_filter(demux_fd, pid, filter, mask, 1, 1)) { // crc check on
|
|
close(demux_fd);
|
|
return -1;
|
|
}
|
|
|
|
return demux_fd;
|
|
}
|
|
|
|
int CamService::processPat(int demux_fd)
|
|
{
|
|
// read section
|
|
unsigned char si_buf[4096];
|
|
int size = read( demux_fd, si_buf, sizeof(si_buf) );
|
|
if ( size<0 )
|
|
return -1;
|
|
|
|
// parse section
|
|
section *parsed_section = section_codec( si_buf, size );
|
|
if ( parsed_section==NULL )
|
|
return -1;
|
|
|
|
// parse section_ext
|
|
section_ext *parsed_section_ext = section_ext_decode( parsed_section, 1 ); // crc check on
|
|
if ( parsed_section_ext==NULL )
|
|
return -1;
|
|
|
|
// parse pat
|
|
mpeg_pat_section *parsed_pat = mpeg_pat_section_codec( parsed_section_ext );
|
|
if ( parsed_pat==NULL )
|
|
return -1;
|
|
|
|
// try and find the requested program
|
|
mpeg_pat_program *cur_program;
|
|
mpeg_pat_section_programs_for_each( parsed_pat, cur_program ) {
|
|
if ( cur_program->program_number==channel.sid )
|
|
return cur_program->pid;
|
|
}
|
|
|
|
fprintf( stderr, "CamService (%s): [warning] channel couldn't be found in PAT\n", channel.name.ascii() );
|
|
|
|
return -1;
|
|
}
|
|
|
|
int CamService::processPmt( int demux_fd )
|
|
{
|
|
// read section
|
|
memset( pmtBuffer, 0, sizeof(pmtBuffer) );
|
|
int size = read( demux_fd, pmtBuffer, sizeof(pmtBuffer) );
|
|
if ( size<=0 )
|
|
return -1;
|
|
|
|
// parse section
|
|
section *parsed_section = section_codec( pmtBuffer, size );
|
|
if ( parsed_section==NULL )
|
|
return -1;
|
|
|
|
// parse section_ext
|
|
section_ext *parsed_section_ext = section_ext_decode( parsed_section, 1 ); // crc check on
|
|
if ( parsed_section_ext==NULL )
|
|
return -1;
|
|
|
|
// parse pmt
|
|
struct mpeg_pmt_section *pmt = mpeg_pmt_section_codec( parsed_section_ext );
|
|
if ( pmt==NULL )
|
|
return -1;
|
|
|
|
parsedPmt = pmt;
|
|
return parsedPmt->head.version_number;
|
|
}
|
|
|
|
void CamService::setState( PmtState st )
|
|
{
|
|
TQMutexLocker locker( &mutex );
|
|
switch ( st ) {
|
|
case Ready: {
|
|
int listmgnt;
|
|
if ( CamMaxService==1 )
|
|
listmgnt = CA_LIST_MANAGEMENT_ONLY;
|
|
else
|
|
listmgnt = CA_LIST_MANAGEMENT_ADD;
|
|
if ( state>Ready )
|
|
listmgnt = CA_LIST_MANAGEMENT_UPDATE;
|
|
if ( setCaPmt( listmgnt, CA_PMT_CMD_ID_OK_DESCRAMBLING ) )
|
|
state = st;
|
|
break;
|
|
}
|
|
case Remove: {
|
|
if ( state!=Added )
|
|
state = Destroy;
|
|
else if ( setCaPmt( CA_LIST_MANAGEMENT_UPDATE, CA_PMT_CMD_ID_NOT_SELECTED ) )
|
|
state = st;
|
|
break;
|
|
}
|
|
default: {
|
|
state = st;
|
|
}
|
|
}
|
|
}
|
|
|
|
int CamService::getState()
|
|
{
|
|
TQMutexLocker locker( &mutex );
|
|
return state;
|
|
}
|
|
|
|
bool CamService::setCaPmt( int list_management, int cmd_id )
|
|
{
|
|
if ( !parsedPmt )
|
|
return false;
|
|
|
|
caPmtSize = en50221_ca_format_pmt( parsedPmt, caPmt, sizeof(caPmt), 0, list_management, cmd_id );
|
|
if ( caPmtSize<=0 )
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
void CamService::run()
|
|
{
|
|
int i, pmtVersion=-1;
|
|
int demux_fd=-1;
|
|
|
|
fprintf( stderr, "CamService (%s): started\n", channel.name.ascii() );
|
|
|
|
while ( isRunning ) {
|
|
loopLabel:
|
|
demux_fd = createSectionFilter( TRANSPORT_PAT_PID, stag_mpeg_program_association );
|
|
if ( demux_fd<0 ) {
|
|
fprintf( stderr, "CamService (%s): [error] opening demux device failed\n", channel.name.ascii() );
|
|
demux_fd = -1;
|
|
usleep( 100000 );
|
|
goto loopLabel;
|
|
}
|
|
|
|
pollfd poll_desc;
|
|
poll_desc.fd = demux_fd;
|
|
poll_desc.events = POLLIN | POLLPRI;
|
|
while ( isRunning ) {
|
|
int ret = poll( &poll_desc, 1, 1000 );
|
|
if ( ret<0 ) {
|
|
fprintf( stderr, "CamService (%s): [error] polling demux device failed\n", channel.name.ascii() );
|
|
close( demux_fd );
|
|
demux_fd = -1;
|
|
usleep( 100000 );
|
|
goto loopLabel;
|
|
}
|
|
if ( ret>0 ) {
|
|
int processed_pat = processPat( demux_fd );
|
|
if ( processed_pat>=0 ) {
|
|
close( demux_fd );
|
|
demux_fd = -1;
|
|
demux_fd = createSectionFilter( processed_pat, stag_mpeg_program_map );
|
|
if ( demux_fd<0 ) {
|
|
fprintf( stderr, "CamService (%s): [error] opening demux device failed\n", channel.name.ascii() );
|
|
demux_fd = -1;
|
|
usleep( 100000 );
|
|
goto loopLabel;
|
|
}
|
|
poll_desc.fd = demux_fd;
|
|
break;
|
|
}
|
|
else
|
|
usleep( 10000 );
|
|
}
|
|
else
|
|
usleep( 10000 );
|
|
}
|
|
|
|
while ( isRunning ) {
|
|
int ret = poll( &poll_desc, 1, 1000 );
|
|
if ( ret<0 ) {
|
|
fprintf( stderr, "CamService (%s): [error] polling demux device failed\n", channel.name.ascii() );
|
|
close( demux_fd );
|
|
demux_fd = -1;
|
|
usleep( 100000 );
|
|
goto loopLabel;
|
|
}
|
|
if ( ret>0 ) {
|
|
//fprintf( stderr, "CamService (%s): parsing pmt\n", channel.name.ascii() );
|
|
i = processPmt( demux_fd );
|
|
if ( i==-1 )
|
|
usleep( 10000 );
|
|
else {
|
|
if ( i!=pmtVersion || getState()<Ready ) {
|
|
fprintf( stderr, "CamService (%s): new pmt received\n", channel.name.ascii() );
|
|
setState( Ready );
|
|
pmtVersion = i;
|
|
}
|
|
i = 200;
|
|
while ( i-- && isRunning )
|
|
usleep( 10000 );
|
|
}
|
|
}
|
|
else
|
|
usleep( 10000 );
|
|
}
|
|
}
|
|
|
|
if ( demux_fd != -1 )
|
|
close( demux_fd );
|
|
fprintf( stderr, "CamService (%s): stopped\n", channel.name.ascii() );
|
|
}
|