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