dvbci.cpp: fix segfault on channel change
[vuplus_dvbapp] / lib / dvb_ci / dvbci_session.cpp
1 /* DVB CI Transport Connection */
2
3 #include <lib/base/eerror.h>
4 #include <lib/dvb_ci/dvbci_session.h>
5 #include <lib/dvb_ci/dvbci_resmgr.h>
6 #include <lib/dvb_ci/dvbci_appmgr.h>
7 #include <lib/dvb_ci/dvbci_camgr.h>
8 #include <lib/dvb_ci/dvbci_datetimemgr.h>
9 #include <lib/dvb_ci/dvbci_mmi.h>
10
11 DEFINE_REF(eDVBCISession);
12
13 ePtr<eDVBCISession> eDVBCISession::sessions[SLMS];
14
15 int eDVBCISession::buildLengthField(unsigned char *pkt, int len)
16 {
17         if (len < 127)
18         {
19                 *pkt++=len;
20                 return 1;
21         } else if (len < 256)
22         {
23                 *pkt++=0x81;
24                 *pkt++=len;
25                 return 2;
26         } else if (len < 65535)
27         {
28                 *pkt++=0x82;
29                 *pkt++=len>>8;
30                 *pkt++=len;
31                 return 3;
32         } else
33         {
34                 eDebug("too big length");
35                 exit(0);
36         }
37 }
38
39 int eDVBCISession::parseLengthField(const unsigned char *pkt, int &len)
40 {
41         len=0;
42         if (!(*pkt&0x80)) 
43         {
44                 len = *pkt;
45                 return 1;
46         }
47         for (int i=0; i<(pkt[0]&0x7F); ++i)
48         {
49                 len <<= 8;
50                 len |= pkt[i + 1];
51         }
52         return (pkt[0] & 0x7F) + 1;
53 }
54
55 void eDVBCISession::sendAPDU(const unsigned char *tag, const void *data, int len)
56 {
57         unsigned char pkt[len+3+4];
58         int l;
59         memcpy(pkt, tag, 3);
60         l=buildLengthField(pkt+3, len);
61         if (data)
62                 memcpy(pkt+3+l, data, len);
63         sendSPDU(0x90, 0, 0, pkt, len+3+l);
64 }
65
66 void eDVBCISession::sendSPDU(unsigned char tag, const void *data, int len, const void *apdu, int alen)
67 {
68         sendSPDU(slot, tag, data, len, session_nb, apdu, alen);
69 }
70
71 void eDVBCISession::sendSPDU(eDVBCISlot *slot, unsigned char tag, const void *data, int len, unsigned short session_nb, const void *apdu,int alen)
72 {
73         unsigned char pkt[4096];
74         unsigned char *ptr=pkt;
75         *ptr++=tag;
76         ptr+=buildLengthField(ptr, len+2);
77         if (data)
78                 memcpy(ptr, data, len);
79         ptr+=len;
80         *ptr++=session_nb>>8;
81         *ptr++=session_nb;
82
83         if (apdu)
84                 memcpy(ptr, apdu, alen);
85
86         ptr+=alen;
87         slot->send(pkt, ptr - pkt);
88 }
89
90 void eDVBCISession::sendOpenSessionResponse(eDVBCISlot *slot, unsigned char session_status, const unsigned char *resource_identifier, unsigned short session_nb)
91 {
92         char pkt[6];
93         pkt[0]=session_status;
94         eDebug("sendOpenSessionResponse");
95         memcpy(pkt + 1, resource_identifier, 4);
96         sendSPDU(slot, 0x92, pkt, 5, session_nb);
97 }
98
99 void eDVBCISession::recvCreateSessionResponse(const unsigned char *data)
100 {
101         status = data[0];
102         state = stateStarted;
103         action = 1;
104         eDebug("create Session Response, status %x", status);
105 }
106
107 void eDVBCISession::recvCloseSessionRequest(const unsigned char *data)
108 {
109         state = stateInDeletion;
110         action = 1;
111         eDebug("close Session Request");
112 }
113
114 void eDVBCISession::deleteSessions(const eDVBCISlot *slot)
115 {
116         ePtr<eDVBCISession> ptr;
117         for (unsigned short session_nb=0; session_nb < SLMS; ++session_nb)
118         {
119                 ptr = sessions[session_nb];
120                 if (ptr && ptr->slot == slot)
121                         sessions[session_nb]=0;
122         }
123 }
124
125 void eDVBCISession::createSession(eDVBCISlot *slot, const unsigned char *resource_identifier, unsigned char &status, ePtr<eDVBCISession> &session)
126 {
127         unsigned long tag;
128         unsigned short session_nb;
129
130         for (session_nb=1; session_nb < SLMS; ++session_nb)
131                 if (!sessions[session_nb-1])
132                         break;
133         if (session_nb == SLMS)
134         {
135                 status=0xF3;
136                 return;
137         }
138
139         tag = resource_identifier[0] << 24;
140         tag|= resource_identifier[1] << 16;
141         tag|= resource_identifier[2] << 8;
142         tag|= resource_identifier[3];
143
144         switch (tag)
145         {
146         case 0x00010041:
147                 session=new eDVBCIResourceManagerSession;
148                 eDebug("RESOURCE MANAGER");
149                 break;
150         case 0x00020041:
151                 session=new eDVBCIApplicationManagerSession(slot);
152                 eDebug("APPLICATION MANAGER");
153                 break;
154         case 0x00030041:
155                 session = new eDVBCICAManagerSession(slot);
156                 eDebug("CA MANAGER");
157                 break;
158         case 0x00240041:
159                 session=new eDVBCIDateTimeSession;
160                 eDebug("DATE-TIME");
161                 break;
162         case 0x00400041:
163                 session = new eDVBCIMMISession(slot);
164                 eDebug("MMI - create session");
165                 break;
166         case 0x00100041:
167 //              session=new eDVBCIAuthSession;
168                 eDebug("AuthSession");
169 //              break;
170         case 0x00200041:
171         default:
172                 eDebug("unknown resource type %02x %02x %02x %02x", resource_identifier[0], resource_identifier[1], resource_identifier[2],resource_identifier[3]);
173                 session=0;
174                 status=0xF0;
175         }
176
177         if (!session)
178         {
179                 eDebug("unknown session.. expect crash");
180                 return;
181         }
182
183         eDebug("new session nb %d %p", session_nb, &(*session));
184         session->session_nb = session_nb;
185
186         if (session)
187         {
188                 sessions[session_nb - 1] = session;
189                 session->slot = slot;
190                 status = 0;
191         }
192         session->state = stateInCreation;
193 }
194
195 void eDVBCISession::handleClose()
196 {
197         unsigned char data[1]={0x00};
198         sendSPDU(0x96, data, 1, 0, 0);
199 }
200
201 int eDVBCISession::pollAll()
202 {
203         for (int session_nb=1; session_nb < SLMS; ++session_nb)
204                 if (sessions[session_nb-1])
205                 {
206                         int r;
207
208                         if (sessions[session_nb-1]->state == stateInDeletion)
209                         {
210                                 sessions[session_nb-1]->handleClose();
211                                 sessions[session_nb-1]=0;
212                                 r=1;
213                         } else
214                                 r=sessions[session_nb-1]->poll();
215
216                         if (r)
217                                 return 1;
218                 }
219         return 0;
220 }
221
222 void eDVBCISession::receiveData(eDVBCISlot *slot, const unsigned char *ptr, size_t len)
223 {
224         const unsigned char *pkt = (const unsigned char*)ptr;
225         unsigned char tag = *pkt++;
226         int llen, hlen;
227
228         eDebug("slot: %p",slot);
229
230         for(unsigned int i=0;i<len;i++)
231                 eDebugNoNewLine("%02x ",ptr[i]);
232         eDebug("");
233         
234         llen = parseLengthField(pkt, hlen);
235         pkt += llen;
236         
237         ePtr<eDVBCISession> session;
238         
239         if(tag == 0x91)
240         {
241                 unsigned char status;
242                 createSession(slot, pkt, status, session);
243                 sendOpenSessionResponse(slot, status, pkt, session?session->session_nb:0);
244                 
245                 if (session)
246                 {
247                         session->state=stateStarted;
248                         session->action=1;
249                 }
250         }
251         else
252         {
253                 unsigned session_nb;
254                 session_nb=pkt[hlen-2]<<8;
255                 session_nb|=pkt[hlen-1]&0xFF;
256                 
257                 if ((!session_nb) || (session_nb >= SLMS))
258                 {
259                         eDebug("PROTOCOL: illegal session number %x", session_nb);
260                         return;
261                 }
262                 
263                 session=sessions[session_nb-1];
264                 if (!session)
265                 {
266                         eDebug("PROTOCOL: data on closed session %x", session_nb);
267                         return;
268                 }
269
270                 switch (tag)
271                 {
272                 case 0x90:
273                         break;
274                 case 0x94:
275                         session->recvCreateSessionResponse(pkt);
276                         break;
277                 case 0x95:
278                         eDebug("recvCloseSessionRequest");
279                         session->recvCloseSessionRequest(pkt);
280                         break;
281                 default:
282                         eDebug("INTERNAL: nyi, tag %02x.", tag);
283                         return;
284                 }
285         }
286         
287         hlen += llen + 1; // lengthfield and tag
288
289         pkt = ((const unsigned char*)ptr) + hlen;
290         len -= hlen;
291
292         if (session)
293                 while (len > 0)
294                 {
295                         int alen;
296                         const unsigned char *tag=pkt;
297                         pkt+=3; // tag
298                         len-=3;
299                         hlen=parseLengthField(pkt, alen);
300                         pkt+=hlen;
301                         len-=hlen;
302
303                         //if (eDVBCIModule::getInstance()->workarounds_active & eDVBCIModule::workaroundMagicAPDULength)
304                         {
305                                 if (((len-alen) > 0) && ((len - alen) < 3))
306                                 {
307                                         eDebug("WORKAROUND: applying work around MagicAPDULength");
308                                         alen=len;
309                                 }
310                         }
311                         if (session->receivedAPDU(tag, pkt, alen))
312                                 session->action = 1;
313                         pkt+=alen;
314                         len-=alen;
315                 }
316                 
317         if (len)
318                 eDebug("PROTOCOL: warning, TL-Data has invalid length");
319 }
320
321 eDVBCISession::~eDVBCISession()
322 {
323 //      eDebug("destroy %p", this);
324 }
325