+ return 0;
+}
+// end static methods
+
+#define CA_REPLY_DEBUG
+#define MAX_LENGTH_BYTES 4
+#define MIN_LENGTH_BYTES 1
+
+void eDVBCAService::socketCB(int what)
+{
+ if (what & (eSocketNotifier::Read | eSocketNotifier::Priority))
+ {
+ char msgbuffer[4096];
+ ssize_t length = read(m_sock, msgbuffer, sizeof(msgbuffer));
+ if (length == -1)
+ {
+ if (errno != EAGAIN && errno != EINTR && errno != EBUSY)
+ {
+ eDebug("[eSocketMMIHandler] read (%m)");
+ what |= eSocketNotifier::Error;
+ }
+ } else if (length == 0)
+ {
+ what |= eSocketNotifier::Hungup;
+ } else
+ {
+ int len = length;
+ unsigned char *data = (unsigned char*)msgbuffer;
+ int clear = 1;
+ // If a new message starts, then the previous message
+ // should already have been processed. Otherwise the
+ // previous message was incomplete and should therefore
+ // be deleted.
+ if ((len >= 1) && ((data[0] & 0xFF) != 0x9f))
+ clear = 0;
+ if ((len >= 2) && ((data[1] & 0x80) != 0x80))
+ clear = 0;
+ if ((len >= 3) && ((data[2] & 0x80) != 0x00))
+ clear = 0;
+ if (clear)
+ {
+ m_buffer.clear();
+#ifdef CA_REPLY_DEBUG
+ eDebug("clear buffer");
+#endif
+ }
+#ifdef CA_REPLY_DEBUG
+ eDebug("Put to buffer:");
+ for (int i=0; i < len; ++i)
+ eDebugNoNewLine("%02x ", data[i]);
+ eDebug("\n--------");
+#endif
+ m_buffer.write( data, len );
+
+ while ( m_buffer.size() >= (3 + MIN_LENGTH_BYTES) )
+ {
+ unsigned char tmp[3+MAX_LENGTH_BYTES];
+ m_buffer.peek(tmp, 3+MIN_LENGTH_BYTES);
+ if (((tmp[0] & 0xFF) != 0x9f) || ((tmp[1] & 0x80) != 0x80) || ((tmp[2] & 0x80) != 0x00))
+ {
+ m_buffer.skip(1);
+#ifdef CA_REPLY_DEBUG
+ eDebug("skip %02x", tmp[0]);
+#endif
+ continue;
+ }
+ if (tmp[3] & 0x80)
+ {
+ int peekLength = (tmp[3] & 0x7f) + 4;
+ if (m_buffer.size() < peekLength)
+ continue;
+ m_buffer.peek(tmp, peekLength);
+ }
+ int size=0;
+ int LengthBytes=eDVBCISession::parseLengthField(tmp+3, size);
+ int messageLength = 3+LengthBytes+size;
+ if ( m_buffer.size() >= messageLength )
+ {
+ unsigned char dest[messageLength];
+ m_buffer.read(dest, messageLength);
+#ifdef CA_REPLY_DEBUG
+ eDebug("dump ca reply:");
+ for (int i=0; i < messageLength; ++i)
+ eDebugNoNewLine("%02x ", dest[i]);
+ eDebug("\n--------");
+#endif
+// /*emit*/ mmi_progress(0, dest, (const void*)(dest+3+LengthBytes), messageLength-3-LengthBytes);
+ }
+ }
+ }
+ }
+ if (what & eSocketNotifier::Hungup) {
+ /*eDebug("[eDVBCAService] connection closed")*/;
+ m_sendstate=1;
+ sendCAPMT();
+ }
+ if (what & eSocketNotifier::Error)
+ eDebug("[eDVBCAService] connection error");
+}
+
+void eDVBCAService::Connect()
+{
+ m_sn=0;
+ memset(&m_servaddr, 0, sizeof(struct sockaddr_un));
+ m_servaddr.sun_family = AF_UNIX;
+ strcpy(m_servaddr.sun_path, "/tmp/camd.socket");
+ m_clilen = sizeof(m_servaddr.sun_family) + strlen(m_servaddr.sun_path);
+ m_sock = socket(PF_UNIX, SOCK_STREAM, 0);
+ if (m_sock != -1)
+ {
+ if (!connect(m_sock, (struct sockaddr *) &m_servaddr, m_clilen))
+ {
+ int val=1;
+ fcntl(m_sock, F_SETFL, O_NONBLOCK);
+ setsockopt(m_sock, SOL_SOCKET, SO_REUSEADDR, &val, 4);
+ m_sn = eSocketNotifier::create(eApp, m_sock,
+ eSocketNotifier::Read|eSocketNotifier::Priority|eSocketNotifier::Error|eSocketNotifier::Hungup);
+ CONNECT(m_sn->activated, eDVBCAService::socketCB);
+
+ }
+// else
+// eDebug("[eDVBCAService] connect failed %m");
+ }
+ else
+ eDebug("[eDVBCAService] create socket failed %m");
+}
+
+void eDVBCAService::buildCAPMT(eTable<ProgramMapSection> *ptr)
+{
+ if (!ptr)
+ return;
+
+ eDVBTableSpec table_spec;
+ ptr->getSpec(table_spec);
+
+ int pmtpid = table_spec.pid,
+ pmt_version = table_spec.version;
+
+ uint8_t demux_mask = 0;
+ int data_demux = -1;
+
+ int iter=0, max_demux_slots = sizeof(m_used_demux);
+ while ( iter < max_demux_slots )
+ {
+ if ( m_used_demux[iter] != 0xFF )
+ {
+ if ( m_used_demux[iter] > data_demux )
+ data_demux = m_used_demux[iter];
+ demux_mask |= (1 << m_used_demux[iter]);
+ }
+ ++iter;
+ }
+
+ if ( data_demux == -1 )
+ {
+ eDebug("[eDVBCAService] no data demux found for service %s", m_service.toString().c_str() );
+ return;
+ }
+
+ eDebug("demux %d mask %02x prevhash %08x", data_demux, demux_mask, m_prev_build_hash);
+
+ unsigned int build_hash = ( pmtpid << 16);
+ build_hash |= (demux_mask << 8);
+ build_hash |= (pmt_version&0xFF);
+
+ if ( build_hash == m_prev_build_hash )
+ {
+ eDebug("[eDVBCAService] don't build/send the same CA PMT twice");
+ return;
+ }
+
+ std::vector<ProgramMapSection*>::const_iterator i=ptr->getSections().begin();
+ if ( i != ptr->getSections().end() )
+ {
+ CaProgramMapSection capmt(*i++, m_prev_build_hash ? 0x05 /*update*/ : 0x03 /*only*/, 0x01 );
+
+ while( i != ptr->getSections().end() )
+ {
+// eDebug("append");
+ capmt.append(*i++);
+ }
+
+ // add our private descriptors to capmt
+ uint8_t tmp[10];
+
+ tmp[0]=0x84; // pmt pid
+ tmp[1]=0x02;
+ tmp[2]=pmtpid>>8;
+ tmp[3]=pmtpid&0xFF;
+ capmt.injectDescriptor(tmp, false);
+
+ tmp[0] = 0x82; // demux
+ tmp[1] = 0x02;
+ tmp[2] = demux_mask; // descramble bitmask
+ tmp[3] = data_demux&0xFF; // read section data from demux number
+ capmt.injectDescriptor(tmp, false);
+
+ tmp[0] = 0x81; // dvbnamespace
+ tmp[1] = 0x08;
+ tmp[2] = m_service.getDVBNamespace().get()>>24;
+ tmp[3]=(m_service.getDVBNamespace().get()>>16)&0xFF;
+ tmp[4]=(m_service.getDVBNamespace().get()>>8)&0xFF;
+ tmp[5]=m_service.getDVBNamespace().get()&0xFF;
+ tmp[6]=m_service.getTransportStreamID().get()>>8;
+ tmp[7]=m_service.getTransportStreamID().get()&0xFF;
+ tmp[8]=m_service.getOriginalNetworkID().get()>>8;
+ tmp[9]=m_service.getOriginalNetworkID().get()&0xFF;
+ capmt.injectDescriptor(tmp, false);
+
+ capmt.writeToBuffer(m_capmt);
+ }
+
+ m_prev_build_hash = build_hash;
+
+ if ( m_sendstate != 0xFFFFFFFF )
+ m_sendstate=0;
+ sendCAPMT();
+}
+
+void eDVBCAService::sendCAPMT()
+{
+ if ( m_sendstate && m_sendstate != 0xFFFFFFFF ) // broken pipe retry
+ {
+ ::close(m_sock);
+ Connect();
+ }
+
+ int wp=0;
+ if ( m_capmt[3] & 0x80 )
+ {
+ int i=0;
+ int lenbytes = m_capmt[3] & ~0x80;
+ while(i < lenbytes)
+ wp = (wp << 8) | m_capmt[4 + i++];
+ wp+=4;
+ wp+=lenbytes;
+ }
+ else
+ {
+ wp = m_capmt[3];
+ wp+=4;
+ }
+
+ if ( write(m_sock, m_capmt, wp) == wp )
+ {
+ m_sendstate=0xFFFFFFFF;
+ eDebug("[eDVBCAService] send %d bytes",wp);
+ eDVBChannelID chid;
+ m_service.getChannelID(chid);
+ channel_data *data = getChannelData(chid);
+ if (data)
+ {
+ int lenbytes = m_capmt[3] & 0x80 ? m_capmt[3] & ~0x80 : 0;
+ data->m_dataDemux = m_capmt[24+lenbytes];
+ }
+#if 1
+ for(int i=0;i<wp;i++)
+ eDebugNoNewLine("%02x ", m_capmt[i]);
+ eDebug("");
+#endif
+ }
+ else
+ {
+ switch(m_sendstate)
+ {
+ case 0xFFFFFFFF:
+ ++m_sendstate;
+ m_retryTimer->start(0,true);
+// eDebug("[eDVBCAService] send failed .. immediate retry");
+ break;
+ default:
+ m_retryTimer->start(5000,true);
+// eDebug("[eDVBCAService] send failed .. retry in 5 sec");
+ break;
+ }
+ ++m_sendstate;
+ }
+}
+
+static PyObject *createTuple(int pid, const char *type)
+{
+ PyObject *r = PyTuple_New(2);
+ PyTuple_SetItem(r, 0, PyInt_FromLong(pid));
+ PyTuple_SetItem(r, 1, PyString_FromString(type));
+ return r;
+}
+
+static inline void PyList_AppendSteal(PyObject *list, PyObject *item)
+{
+ PyList_Append(list, item);
+ Py_DECREF(item);
+}
+
+PyObject *eDVBServicePMTHandler::program::createPythonObject()
+{
+ PyObject *r = PyDict_New();
+
+ PyObject *l = PyList_New(0);