1 /*
2     en50221 encoder An implementation for libdvb
3     an implementation for the en50221 transport layer
4 
5     Copyright (C) 2004, 2005 Manu Abraham <abraham.manu@gmail.com>
6     Copyright (C) 2005 Julian Scheel (julian at jusst dot de)
7     Copyright (C) 2006 Andrew de Quincey (adq_dvb@lidskialf.net)
8 
9     This library is free software; you can redistribute it and/or modify
10     it under the terms of the GNU Lesser General Public License as
11     published by the Free Software Foundation; either version 2.1 of
12     the License, or (at your option) any later version.
13 
14     This program is distributed in the hope that it will be useful,
15     but WITHOUT ANY WARRANTY; without even the implied warranty of
16     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17     GNU Lesser General Public License for more details.
18 
19     You should have received a copy of the GNU Lesser General Public
20     License along with this library; if not, write to the Free Software
21     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
22 */
23 
24 #include <stdio.h>
25 #include <unistd.h>
26 #include <string.h>
27 #include <fcntl.h>
28 #include <sys/ioctl.h>
29 #include <time.h>
30 #include <libdvbmisc/dvbmisc.h>
31 #include <sys/uio.h>
32 #include <pthread.h>
33 #include "en50221_transport.h"
34 #include "en50221_session.h"
35 #include "en50221_errno.h"
36 #include "asn_1.h"
37 
38 
39 // these are the possible session statuses
40 #define S_STATUS_OPEN                    0x00	// session is opened
41 #define S_STATUS_CLOSE_NO_RES            0xF0	// could not open session, no proper resource available
42 #define S_STATUS_CLOSE_RES_UNAVAILABLE   0xF1	// could not open session, resource unavailable
43 #define S_STATUS_CLOSE_RES_LOW_VERSION   0xF2	// could not open session, resource version too low
44 #define S_STATUS_CLOSE_RES_BUSY          0xF3	// could not open session, resource is busy
45 
46 #define ST_OPEN_SESSION_REQ     0x91	// h<--m
47 #define ST_OPEN_SESSION_RES     0x92	// h-->m
48 #define ST_CREATE_SESSION       0x93	// h-->m
49 #define ST_CREATE_SESSION_RES   0x94	// h<--m
50 #define ST_CLOSE_SESSION_REQ    0x95	// h<->m
51 #define ST_CLOSE_SESSION_RES    0x96	// h<->m
52 #define ST_SESSION_NUMBER       0x90	// h<->m
53 
54 #define S_STATE_IDLE            0x01	// this session is not in use
55 #define S_STATE_ACTIVE          0x02	// this session is in use
56 #define S_STATE_IN_CREATION     0x04	// this session waits for a ST_CREATE_SESSION_RES to become active
57 #define S_STATE_IN_DELETION     0x08	// this session waits for ST_CLOSE_SESSION_RES to become idle again
58 
59 
60 // for each session we store its identifier, the resource-id
61 // it is linked to and the callback of the specific resource
62 struct en50221_session {
63 	uint8_t state;
64 	uint32_t resource_id;
65 	uint8_t slot_id;
66 	uint8_t connection_id;
67 
68 	en50221_sl_resource_callback callback;
69 	void *callback_arg;
70 
71 	pthread_mutex_t session_lock;
72 };
73 
74 struct en50221_session_layer {
75 	uint32_t max_sessions;
76 	struct en50221_transport_layer *tl;
77 
78 	en50221_sl_lookup_callback lookup;
79 	void *lookup_arg;
80 
81 	en50221_sl_session_callback session;
82 	void *session_arg;
83 
84 	pthread_mutex_t global_lock;
85 	pthread_mutex_t setcallback_lock;
86 
87 	int error;
88 
89 	struct en50221_session *sessions;
90 };
91 
92 static void en50221_sl_transport_callback(void *arg, int reason,
93 					  uint8_t * data,
94 					  uint32_t data_length,
95 					  uint8_t slot_id,
96 					  uint8_t connection_id);
97 static int en50221_sl_alloc_new_session(struct en50221_session_layer *sl,
98 					uint32_t resource_id,
99 					uint8_t slot_id,
100 					uint8_t connection_id,
101 					en50221_sl_resource_callback
102 					callback, void *arg);
103 
104 
105 
106 
en50221_sl_create(struct en50221_transport_layer * tl,uint32_t max_sessions)107 struct en50221_session_layer *en50221_sl_create(struct en50221_transport_layer *tl,
108 						uint32_t max_sessions)
109 {
110 	struct en50221_session_layer *sl = NULL;
111 	uint32_t i;
112 
113 	// setup structure
114 	sl = (struct en50221_session_layer *)
115 	    malloc(sizeof(struct en50221_session_layer));
116 	if (sl == NULL)
117 		goto error_exit;
118 	sl->max_sessions = max_sessions;
119 	sl->lookup = NULL;
120 	sl->session = NULL;
121 	sl->tl = tl;
122 	sl->error = 0;
123 
124 	// init the mutex
125 	pthread_mutex_init(&sl->global_lock, NULL);
126 	pthread_mutex_init(&sl->setcallback_lock, NULL);
127 
128 	// create the slots
129 	sl->sessions = malloc(sizeof(struct en50221_session) * max_sessions);
130 	if (sl->sessions == NULL)
131 		goto error_exit;
132 
133 	// set them up
134 	for (i = 0; i < max_sessions; i++) {
135 		sl->sessions[i].state = S_STATE_IDLE;
136 		sl->sessions[i].callback = NULL;
137 
138 		pthread_mutex_init(&sl->sessions[i].session_lock, NULL);
139 	}
140 
141 	// register ourselves with the transport layer
142 	en50221_tl_register_callback(tl, en50221_sl_transport_callback, sl);
143 
144 	return sl;
145 
146 error_exit:
147 	en50221_sl_destroy(sl);
148 	return NULL;
149 }
150 
en50221_sl_destroy(struct en50221_session_layer * sl)151 void en50221_sl_destroy(struct en50221_session_layer *sl)
152 {
153 	uint32_t i;
154 
155 	if (sl) {
156 		if (sl->sessions) {
157 			for (i = 0; i < sl->max_sessions; i++) {
158 				pthread_mutex_destroy(&sl->sessions[i].session_lock);
159 			}
160 			free(sl->sessions);
161 		}
162 
163 		pthread_mutex_destroy(&sl->setcallback_lock);
164 		pthread_mutex_destroy(&sl->global_lock);
165 
166 		free(sl);
167 	}
168 }
169 
en50221_sl_get_error(struct en50221_session_layer * sl)170 int en50221_sl_get_error(struct en50221_session_layer *sl)
171 {
172 	return sl->error;
173 }
174 
en50221_sl_register_lookup_callback(struct en50221_session_layer * sl,en50221_sl_lookup_callback callback,void * arg)175 void en50221_sl_register_lookup_callback(struct en50221_session_layer *sl,
176 					 en50221_sl_lookup_callback
177 					 callback, void *arg)
178 {
179 	pthread_mutex_lock(&sl->setcallback_lock);
180 	sl->lookup = callback;
181 	sl->lookup_arg = arg;
182 	pthread_mutex_unlock(&sl->setcallback_lock);
183 }
184 
en50221_sl_register_session_callback(struct en50221_session_layer * sl,en50221_sl_session_callback callback,void * arg)185 void en50221_sl_register_session_callback(struct en50221_session_layer *sl,
186 					  en50221_sl_session_callback
187 					  callback, void *arg)
188 {
189 	pthread_mutex_lock(&sl->setcallback_lock);
190 	sl->session = callback;
191 	sl->session_arg = arg;
192 	pthread_mutex_unlock(&sl->setcallback_lock);
193 }
194 
en50221_sl_create_session(struct en50221_session_layer * sl,int slot_id,uint8_t connection_id,uint32_t resource_id,en50221_sl_resource_callback callback,void * arg)195 int en50221_sl_create_session(struct en50221_session_layer *sl,
196 			      int slot_id, uint8_t connection_id,
197 			      uint32_t resource_id,
198 			      en50221_sl_resource_callback callback,
199 			      void *arg)
200 {
201 	// lookup next free session_id:
202 	pthread_mutex_lock(&sl->global_lock);
203 	int session_number =
204 	    en50221_sl_alloc_new_session(sl, resource_id, slot_id,
205 					 connection_id, callback, arg);
206 	if (session_number == -1) {
207 		pthread_mutex_unlock(&sl->global_lock);
208 		return -1;
209 	}
210 	pthread_mutex_unlock(&sl->global_lock);
211 
212 	// make up the header
213 	uint8_t hdr[8];
214 	hdr[0] = ST_CREATE_SESSION;
215 	hdr[1] = 6;
216 	hdr[2] = resource_id >> 24;
217 	hdr[3] = resource_id >> 16;
218 	hdr[4] = resource_id >> 8;
219 	hdr[5] = resource_id;
220 	hdr[6] = session_number >> 8;
221 	hdr[7] = session_number;
222 
223 	// send this command
224 	if (en50221_tl_send_data(sl->tl, slot_id, connection_id, hdr, 8)) {
225 		pthread_mutex_lock(&sl->sessions[session_number].session_lock);
226 		if (sl->sessions[session_number].state == S_STATE_IN_CREATION) {
227 			sl->sessions[session_number].state = S_STATE_IDLE;
228 		}
229 		pthread_mutex_unlock(&sl->sessions[session_number].session_lock);
230 
231 		sl->error = en50221_tl_get_error(sl->tl);
232 		return -1;
233 	}
234 	// ok.
235 	return session_number;
236 }
237 
en50221_sl_destroy_session(struct en50221_session_layer * sl,uint16_t session_number)238 int en50221_sl_destroy_session(struct en50221_session_layer *sl,
239 			       uint16_t session_number)
240 {
241 	if (session_number >= sl->max_sessions) {
242 		sl->error = EN50221ERR_BADSESSIONNUMBER;
243 		return -1;
244 	}
245 
246 	pthread_mutex_lock(&sl->sessions[session_number].session_lock);
247 	if (!(sl->sessions[session_number].state & (S_STATE_ACTIVE | S_STATE_IN_DELETION))) {
248 		sl->error = EN50221ERR_BADSESSIONNUMBER;
249 		pthread_mutex_unlock(&sl->sessions[session_number].session_lock);
250 		return -1;
251 	}
252 	// set the state
253 	sl->sessions[session_number].state = S_STATE_IN_DELETION;
254 
255 	// get essential details
256 	uint8_t slot_id = sl->sessions[session_number].slot_id;
257 	uint8_t connection_id = sl->sessions[session_number].connection_id;
258 	pthread_mutex_unlock(&sl->sessions[session_number].session_lock);
259 
260 	//  sendit
261 	uint8_t hdr[4];
262 	hdr[0] = ST_CLOSE_SESSION_REQ;
263 	hdr[1] = 2;
264 	hdr[2] = session_number >> 8;
265 	hdr[3] = session_number;
266 	if (en50221_tl_send_data(sl->tl, slot_id, connection_id, hdr, 4)) {
267 		pthread_mutex_lock(&sl->sessions[session_number].session_lock);
268 		if (sl->sessions[session_number].state == S_STATE_IN_DELETION) {
269 			sl->sessions[session_number].state = S_STATE_IDLE;
270 		}
271 		pthread_mutex_unlock(&sl->sessions[session_number].session_lock);
272 
273 		sl->error = en50221_tl_get_error(sl->tl);
274 		return -1;
275 	}
276 
277 	return 0;
278 }
279 
en50221_sl_send_data(struct en50221_session_layer * sl,uint16_t session_number,uint8_t * data,uint16_t data_length)280 int en50221_sl_send_data(struct en50221_session_layer *sl,
281 			 uint16_t session_number,
282 			 uint8_t *data,
283 			 uint16_t data_length)
284 {
285 	if (session_number >= sl->max_sessions) {
286 		sl->error = EN50221ERR_BADSESSIONNUMBER;
287 		return -1;
288 	}
289 
290 	pthread_mutex_lock(&sl->sessions[session_number].session_lock);
291 	if (sl->sessions[session_number].state != S_STATE_ACTIVE) {
292 		sl->error = EN50221ERR_BADSESSIONNUMBER;
293 		pthread_mutex_unlock(&sl->sessions[session_number].session_lock);
294 		return -1;
295 	}
296 	// get essential details
297 	uint8_t slot_id = sl->sessions[session_number].slot_id;
298 	uint8_t connection_id = sl->sessions[session_number].connection_id;
299 	pthread_mutex_unlock(&sl->sessions[session_number].session_lock);
300 
301 	// sendit
302 	struct iovec iov[2];
303 	uint8_t hdr[4];
304 	hdr[0] = ST_SESSION_NUMBER;
305 	hdr[1] = 2;
306 	hdr[2] = session_number >> 8;
307 	hdr[3] = session_number;
308 	iov[0].iov_base = hdr;
309 	iov[0].iov_len = 4;
310 	iov[1].iov_base = data;
311 	iov[1].iov_len = data_length;
312 	if (en50221_tl_send_datav(sl->tl, slot_id, connection_id, iov, 2)) {
313 		sl->error = en50221_tl_get_error(sl->tl);
314 		return -1;
315 	}
316 
317 	return 0;
318 }
319 
en50221_sl_send_datav(struct en50221_session_layer * sl,uint16_t session_number,struct iovec * vector,int iov_count)320 int en50221_sl_send_datav(struct en50221_session_layer *sl,
321 			  uint16_t session_number,
322 			  struct iovec *vector,
323 			  int iov_count)
324 {
325 	if (session_number >= sl->max_sessions) {
326 		sl->error = EN50221ERR_BADSESSIONNUMBER;
327 		return -1;
328 	}
329 
330 	pthread_mutex_lock(&sl->sessions[session_number].session_lock);
331 	if (sl->sessions[session_number].state != S_STATE_ACTIVE) {
332 		sl->error = EN50221ERR_BADSESSIONNUMBER;
333 		pthread_mutex_unlock(&sl->sessions[session_number].session_lock);
334 		return -1;
335 	}
336 	if (iov_count > 9) {
337 		sl->error = EN50221ERR_IOVLIMIT;
338 		pthread_mutex_unlock(&sl->sessions[session_number].session_lock);
339 		return -1;
340 	}
341 	uint8_t slot_id = sl->sessions[session_number].slot_id;
342 	uint8_t connection_id = sl->sessions[session_number].connection_id;
343 	pthread_mutex_unlock(&sl->sessions[session_number].session_lock);
344 
345 	// make up the header
346 	struct iovec out_iov[10];
347 	uint8_t hdr[4];
348 	hdr[0] = ST_SESSION_NUMBER;
349 	hdr[1] = 2;
350 	hdr[2] = session_number >> 8;
351 	hdr[3] = session_number;
352 	out_iov[0].iov_base = hdr;
353 	out_iov[0].iov_len = 4;
354 
355 	// make up the data
356 	memcpy(&out_iov[1], vector, iov_count * sizeof(struct iovec));
357 
358 	// send this command
359 	if (en50221_tl_send_datav(sl->tl, slot_id, connection_id, out_iov, iov_count + 1)) {
360 		sl->error = en50221_tl_get_error(sl->tl);
361 		return -1;
362 	}
363 	return 0;
364 }
365 
en50221_sl_broadcast_data(struct en50221_session_layer * sl,int slot_id,uint32_t resource_id,uint8_t * data,uint16_t data_length)366 int en50221_sl_broadcast_data(struct en50221_session_layer *sl,
367 			      int slot_id, uint32_t resource_id,
368 			      uint8_t *data, uint16_t data_length)
369 {
370 	uint32_t i;
371 
372 	for (i = 0; i < sl->max_sessions; i++) {
373 		pthread_mutex_lock(&sl->sessions[i].session_lock);
374 
375 		if (sl->sessions[i].state != S_STATE_ACTIVE) {
376 			pthread_mutex_unlock(&sl->sessions[i].session_lock);
377 			continue;
378 		}
379 		if ((slot_id != -1)
380 		    && (slot_id != sl->sessions[i].slot_id)) {
381 			pthread_mutex_unlock(&sl->sessions[i].session_lock);
382 			continue;
383 		}
384 
385 		if (sl->sessions[i].resource_id == resource_id) {
386 			pthread_mutex_unlock(&sl->sessions[i].session_lock);
387 			en50221_sl_send_data(sl, i, data, data_length);
388 		} else {
389 			pthread_mutex_unlock(&sl->sessions[i].session_lock);
390 		}
391 	}
392 
393 	return 0;
394 }
395 
396 
397 
en50221_sl_handle_open_session_request(struct en50221_session_layer * sl,uint8_t * data,uint32_t data_length,uint8_t slot_id,uint8_t connection_id)398 static void en50221_sl_handle_open_session_request(struct en50221_session_layer *sl,
399 						   uint8_t *data,
400 						   uint32_t data_length,
401 						   uint8_t slot_id,
402 						   uint8_t connection_id)
403 {
404 	// check
405 	if (data_length < 5) {
406 		print(LOG_LEVEL, ERROR, 1,
407 		      "Received data with invalid length from module on slot %02x\n",
408 		      slot_id);
409 		return;
410 	}
411 	if (data[0] != 4) {
412 		print(LOG_LEVEL, ERROR, 1,
413 		      "Received data with invalid length from module on slot %02x\n",
414 		      slot_id);
415 		return;
416 	}
417 	// get the resource id
418 	uint32_t requested_resource_id =
419 	    (data[1] << 24) | (data[2] << 16) | (data[3] << 8) | data[4];
420 
421 	// get lookup callback details
422 	pthread_mutex_lock(&sl->setcallback_lock);
423 	en50221_sl_lookup_callback lcb = sl->lookup;
424 	void *lcb_arg = sl->lookup_arg;
425 	pthread_mutex_unlock(&sl->setcallback_lock);
426 
427 	// first of all, lookup this resource id
428 	int status = S_STATUS_CLOSE_NO_RES;
429 	en50221_sl_resource_callback resource_callback = NULL;
430 	void *resource_arg = NULL;
431 	uint32_t connected_resource_id;
432 	if (lcb) {
433 		status =
434 		    lcb(lcb_arg, slot_id, requested_resource_id,
435 			&resource_callback, &resource_arg,
436 			&connected_resource_id);
437 		switch (status) {
438 		case 0:
439 			status = S_STATUS_OPEN;
440 			break;
441 
442 		case -1:
443 			status = S_STATUS_CLOSE_NO_RES;
444 			break;
445 
446 		case -2:
447 			status = S_STATUS_CLOSE_RES_LOW_VERSION;
448 			break;
449 
450 		case -3:
451 			status = S_STATUS_CLOSE_RES_UNAVAILABLE;
452 			break;
453 		}
454 	}
455 	// if we found it, get a new session for it
456 	int session_number = -1;
457 	if (status == S_STATUS_OPEN) {
458 		// lookup next free session_id:
459 		pthread_mutex_lock(&sl->global_lock);
460 		session_number =
461 		    en50221_sl_alloc_new_session(sl, connected_resource_id,
462 						 slot_id, connection_id,
463 						 resource_callback,
464 						 resource_arg);
465 		pthread_mutex_unlock(&sl->global_lock);
466 
467 		if (session_number == -1) {
468 			status = S_STATUS_CLOSE_NO_RES;
469 		} else {
470 			// inform upper layers/ check availability
471 			pthread_mutex_lock(&sl->setcallback_lock);
472 			en50221_sl_session_callback cb = sl->session;
473 			void *cb_arg = sl->session_arg;
474 			pthread_mutex_unlock(&sl->setcallback_lock);
475 			if (cb) {
476 				if (cb(cb_arg, S_SCALLBACK_REASON_CAMCONNECTING,
477 				       slot_id, session_number,
478 				       connected_resource_id)) {
479 					status = S_STATUS_CLOSE_RES_BUSY;
480 				}
481 			} else {
482 				status = S_STATUS_CLOSE_RES_UNAVAILABLE;
483 			}
484 		}
485 	}
486 	// send response
487 	uint8_t hdr[9];
488 	hdr[0] = ST_OPEN_SESSION_RES;
489 	hdr[1] = 7;
490 	hdr[2] = status;
491 	hdr[3] = connected_resource_id >> 24;
492 	hdr[4] = connected_resource_id >> 16;
493 	hdr[5] = connected_resource_id >> 8;
494 	hdr[6] = connected_resource_id;
495 	hdr[7] = session_number >> 8;
496 	hdr[8] = session_number;
497 	if (en50221_tl_send_data(sl->tl, slot_id, connection_id, hdr, 9)) {
498 		print(LOG_LEVEL, ERROR, 1,
499 		      "Transport layer error %i occurred\n",
500 		      en50221_tl_get_error(sl->tl));
501 		status = S_STATUS_CLOSE_NO_RES;
502 		// fallthrough
503 	}
504 	// inform upper layers what happened
505 	if (session_number != -1) {
506 		// setup session state apppropriately from upper layer response
507 		pthread_mutex_lock(&sl->sessions[session_number].session_lock);
508 		if (status != S_STATUS_OPEN) {
509 			sl->sessions[session_number].state = S_STATE_IDLE;
510 		} else {
511 			sl->sessions[session_number].state = S_STATE_ACTIVE;
512 		}
513 		pthread_mutex_unlock(&sl->sessions[session_number].session_lock);
514 
515 		// tell upper layers
516 		if (sl->sessions[session_number].state == S_STATE_ACTIVE) {
517 			pthread_mutex_lock(&sl->setcallback_lock);
518 			en50221_sl_session_callback cb = sl->session;
519 			void *cb_arg = sl->session_arg;
520 			pthread_mutex_unlock(&sl->setcallback_lock);
521 
522 			if (status == S_STATUS_OPEN) {
523 				if (cb)
524 					cb(cb_arg,
525 					   S_SCALLBACK_REASON_CAMCONNECTED,
526 					   slot_id, session_number,
527 					   connected_resource_id);
528 			} else {
529 				sl->sessions[session_number].state =
530 				    S_STATE_IDLE;
531 				if (cb)
532 					cb(cb_arg,
533 					   S_SCALLBACK_REASON_CAMCONNECTFAIL,
534 					   slot_id, session_number,
535 					   connected_resource_id);
536 			}
537 		}
538 	}
539 }
540 
en50221_sl_handle_close_session_request(struct en50221_session_layer * sl,uint8_t * data,uint32_t data_length,uint8_t slot_id,uint8_t connection_id)541 static void en50221_sl_handle_close_session_request(struct en50221_session_layer *sl,
542 						    uint8_t * data,
543 						    uint32_t data_length,
544 						    uint8_t slot_id,
545 						    uint8_t connection_id)
546 {
547 	// check
548 	if (data_length < 3) {
549 		print(LOG_LEVEL, ERROR, 1,
550 		      "Received data with invalid length from module on slot %02x\n",
551 		      slot_id);
552 		return;
553 	}
554 	if (data[0] != 2) {
555 		print(LOG_LEVEL, ERROR, 1,
556 		      "Received data with invalid length from module on slot %02x\n",
557 		      slot_id);
558 		return;
559 	}
560 	// extract session number
561 	uint16_t session_number = (data[1] << 8) | data[2];
562 
563 	// check session number is ok
564 	uint8_t code = 0x00;
565 	uint32_t resource_id = 0;
566 	if (session_number >= sl->max_sessions) {
567 		code = 0xF0;	// session close error
568 		print(LOG_LEVEL, ERROR, 1, "Received bad session id %i\n",
569 		      slot_id);
570 	} else {
571 		pthread_mutex_lock(&sl->sessions[session_number].
572 				   session_lock);
573 		if (slot_id != sl->sessions[session_number].slot_id) {
574 			print(LOG_LEVEL, ERROR, 1,
575 			      "Received unexpected session on invalid slot %i\n",
576 			      slot_id);
577 			code = 0xF0;	// session close error
578 		}
579 		if (connection_id != sl->sessions[session_number].connection_id) {
580 			print(LOG_LEVEL, ERROR, 1,
581 			      "Received unexpected session on invalid slot %i\n",
582 			      slot_id);
583 			code = 0xF0;	// session close error
584 		}
585 		if (!(sl->sessions[session_number].state & (S_STATE_ACTIVE | S_STATE_IN_DELETION))) {
586 			print(LOG_LEVEL, ERROR, 1,
587 			      "Received unexpected session on invalid slot %i\n",
588 			      slot_id);
589 			code = 0xF0;	// session close error
590 		}
591 
592 		if (code == 0x00) {
593 			sl->sessions[session_number].state = S_STATE_IDLE;
594 			code = 0x00;	// close ok
595 		}
596 		resource_id = sl->sessions[session_number].resource_id;
597 		pthread_mutex_unlock(&sl->sessions[session_number].session_lock);
598 	}
599 
600 	// make up the response
601 	uint8_t hdr[5];
602 	hdr[0] = ST_CLOSE_SESSION_RES;
603 	hdr[1] = 3;
604 	hdr[2] = code;
605 	hdr[3] = session_number >> 8;
606 	hdr[4] = session_number;
607 
608 	// sendit
609 	if (en50221_tl_send_data(sl->tl, slot_id, connection_id, hdr, 5)) {
610 		print(LOG_LEVEL, ERROR, 1,
611 		      "Transport layer reports error %i on slot %i\n",
612 		      en50221_tl_get_error(sl->tl), slot_id);
613 	}
614 	// callback to announce destruction to resource if it was ok
615 	if (code == 0x00) {
616 		pthread_mutex_lock(&sl->setcallback_lock);
617 		en50221_sl_session_callback cb = sl->session;
618 		void *cb_arg = sl->session_arg;
619 		pthread_mutex_unlock(&sl->setcallback_lock);
620 
621 		if (cb)
622 			cb(cb_arg, S_SCALLBACK_REASON_CLOSE, slot_id,
623 			   session_number, resource_id);
624 	}
625 }
626 
en50221_sl_handle_create_session_response(struct en50221_session_layer * sl,uint8_t * data,uint32_t data_length,uint8_t slot_id,uint8_t connection_id)627 static void en50221_sl_handle_create_session_response(struct en50221_session_layer *sl,
628 						      uint8_t * data,
629 						      uint32_t data_length,
630 						      uint8_t slot_id,
631 						      uint8_t connection_id)
632 {
633 	// check
634 	if (data_length < 8) {
635 		print(LOG_LEVEL, ERROR, 1,
636 		      "Received data with invalid length from module on slot %02x\n",
637 		      slot_id);
638 		return;
639 	}
640 	if (data[0] != 7) {
641 		print(LOG_LEVEL, ERROR, 1,
642 		      "Received data with invalid length from module on slot %02x\n",
643 		      slot_id);
644 		return;
645 	}
646 	// extract session number
647 	uint16_t session_number = (data[5] << 8) | data[6];
648 
649 	// check session number is ok
650 	if (session_number >= sl->max_sessions) {
651 		print(LOG_LEVEL, ERROR, 1, "Received bad session id %i\n",
652 		      slot_id);
653 		return;
654 	}
655 
656 	pthread_mutex_lock(&sl->sessions[session_number].session_lock);
657 	if (slot_id != sl->sessions[session_number].slot_id) {
658 		print(LOG_LEVEL, ERROR, 1,
659 		      "Received unexpected session on invalid slot %i\n",
660 		      slot_id);
661 		pthread_mutex_unlock(&sl->sessions[session_number].session_lock);
662 		return;
663 	}
664 	if (connection_id != sl->sessions[session_number].connection_id) {
665 		print(LOG_LEVEL, ERROR, 1,
666 		      "Received unexpected session on invalid slot %i\n",
667 		      slot_id);
668 		pthread_mutex_unlock(&sl->sessions[session_number].session_lock);
669 		return;
670 	}
671 	if (sl->sessions[session_number].state != S_STATE_IN_CREATION) {
672 		print(LOG_LEVEL, ERROR, 1,
673 		      "Received unexpected session on invalid slot %i\n",
674 		      slot_id);
675 		pthread_mutex_unlock(&sl->sessions[session_number].session_lock);
676 		return;
677 	}
678 	// extract status
679 	if (data[1] != S_STATUS_OPEN) {
680 		print(LOG_LEVEL, ERROR, 1,
681 		      "Session creation failed 0x%02x\n", data[1]);
682 		sl->sessions[session_number].state = S_STATE_IDLE;
683 		pthread_mutex_unlock(&sl->sessions[session_number].session_lock);
684 
685 		// inform upper layers
686 		pthread_mutex_lock(&sl->setcallback_lock);
687 		en50221_sl_session_callback cb = sl->session;
688 		void *cb_arg = sl->session_arg;
689 		pthread_mutex_unlock(&sl->setcallback_lock);
690 		if (cb)
691 			cb(cb_arg, S_SCALLBACK_REASON_CONNECTFAIL, slot_id,
692 			   session_number,
693 			   sl->sessions[session_number].resource_id);
694 		return;
695 	}
696 	// set it active
697 	sl->sessions[session_number].state = S_STATE_ACTIVE;
698 	pthread_mutex_unlock(&sl->sessions[session_number].session_lock);
699 
700 	// inform upper layers
701 	pthread_mutex_lock(&sl->setcallback_lock);
702 	en50221_sl_session_callback cb = sl->session;
703 	void *cb_arg = sl->session_arg;
704 	pthread_mutex_unlock(&sl->setcallback_lock);
705 	if (cb)
706 		cb(cb_arg, S_SCALLBACK_REASON_CONNECTED, slot_id,
707 		   session_number,
708 		   sl->sessions[session_number].resource_id);
709 }
710 
en50221_sl_handle_close_session_response(struct en50221_session_layer * sl,uint8_t * data,uint32_t data_length,uint8_t slot_id,uint8_t connection_id)711 static void en50221_sl_handle_close_session_response(struct en50221_session_layer *sl,
712 						     uint8_t *data,
713 						     uint32_t data_length,
714 						     uint8_t slot_id,
715 						     uint8_t connection_id)
716 {
717 	// check
718 	if (data_length < 5) {
719 		print(LOG_LEVEL, ERROR, 1,
720 		      "Received data with invalid length from module on slot %02x\n",
721 		      slot_id);
722 		return;
723 	}
724 	if (data[0] != 4) {
725 		print(LOG_LEVEL, ERROR, 1,
726 		      "Received data with invalid length from module on slot %02x\n",
727 		      slot_id);
728 		return;
729 	}
730 	// extract session number
731 	uint16_t session_number = (data[2] << 8) | data[3];
732 
733 	// check session number is ok
734 	if (session_number >= sl->max_sessions) {
735 		print(LOG_LEVEL, ERROR, 1, "Received bad session id %i\n", slot_id);
736 		return;
737 	}
738 
739 	pthread_mutex_lock(&sl->sessions[session_number].session_lock);
740 	if (slot_id != sl->sessions[session_number].slot_id) {
741 		print(LOG_LEVEL, ERROR, 1,
742 		      "Received unexpected session on invalid slot %i\n",
743 		      slot_id);
744 		pthread_mutex_unlock(&sl->sessions[session_number].session_lock);
745 		return;
746 	}
747 	if (connection_id != sl->sessions[session_number].connection_id) {
748 		print(LOG_LEVEL, ERROR, 1,
749 		      "Received unexpected session on invalid slot %i\n",
750 		      slot_id);
751 		pthread_mutex_unlock(&sl->sessions[session_number].session_lock);
752 		return;
753 	}
754 	if (sl->sessions[session_number].state != S_STATE_IN_DELETION) {
755 		print(LOG_LEVEL, ERROR, 1,
756 		      "Received unexpected session on invalid slot %i\n",
757 		      slot_id);
758 		pthread_mutex_unlock(&sl->sessions[session_number].session_lock);
759 		return;
760 	}
761 	// extract status
762 	if (data[1] != 0x00) {
763 		print(LOG_LEVEL, ERROR, 1, "Session close failed 0x%02x\n", data[1]);
764 		// just fallthrough anyway
765 	}
766 	// completed
767 	sl->sessions[session_number].state = S_STATE_IDLE;
768 	pthread_mutex_unlock(&sl->sessions[session_number].session_lock);
769 }
770 
en50221_sl_handle_session_package(struct en50221_session_layer * sl,uint8_t * data,uint32_t data_length,uint8_t slot_id,uint8_t connection_id)771 static void en50221_sl_handle_session_package(struct en50221_session_layer *sl,
772 					      uint8_t *data,
773 					      uint32_t data_length,
774 					      uint8_t slot_id,
775 					      uint8_t connection_id)
776 {
777 	// check
778 	if (data_length < 3) {
779 		print(LOG_LEVEL, ERROR, 1,
780 		      "Received data with invalid length from module on slot %i\n",
781 		      slot_id);
782 		return;
783 	}
784 	if (data[0] != 2) {
785 		print(LOG_LEVEL, ERROR, 1,
786 		      "Received data with invalid length from module on slot %i\n",
787 		      slot_id);
788 		return;
789 	}
790 	// get session number
791 	uint16_t session_number = (data[1] << 8) | data[2];
792 
793 	// check it
794 	if (session_number >= sl->max_sessions) {
795 		print(LOG_LEVEL, ERROR, 1,
796 		      "Received data with bad session_number from module on slot %i\n",
797 		      slot_id);
798 		return;
799 	}
800 
801 	pthread_mutex_lock(&sl->sessions[session_number].session_lock);
802 	if (slot_id != sl->sessions[session_number].slot_id) {
803 		print(LOG_LEVEL, ERROR, 1,
804 		      "Received unexpected session on invalid slot %i\n",
805 		      slot_id);
806 		pthread_mutex_unlock(&sl->sessions[session_number].session_lock);
807 		return;
808 	}
809 	if (connection_id != sl->sessions[session_number].connection_id) {
810 		print(LOG_LEVEL, ERROR, 1,
811 		      "Received unexpected session on invalid slot %i\n",
812 		      slot_id);
813 		pthread_mutex_unlock(&sl->sessions[session_number].session_lock);
814 		return;
815 	}
816 	if (sl->sessions[session_number].state != S_STATE_ACTIVE) {
817 		print(LOG_LEVEL, ERROR, 1,
818 		      "Received data with bad session_number from module on slot %i\n",
819 		      slot_id);
820 		pthread_mutex_unlock(&sl->sessions[session_number].session_lock);
821 		return;
822 	}
823 
824 	en50221_sl_resource_callback cb = sl->sessions[session_number].callback;
825 	void *cb_arg = sl->sessions[session_number].callback_arg;
826 	uint32_t resource_id = sl->sessions[session_number].resource_id;
827 	pthread_mutex_unlock(&sl->sessions[session_number].session_lock);
828 
829 	// there can be > 1 APDU following the package - all for the same session/resource_id tho.
830 	data += 3;
831 	data_length -= 3;
832 	while (data_length) {
833 		// check length field
834 		if (data_length < 3) {
835 			print(LOG_LEVEL, ERROR, 1,
836 			      "Received invalid sized session package from slot %i\n",
837 			      slot_id);
838 			return;
839 		}
840 		// parse the APDU's length field
841 		int length_field_len;
842 		uint16_t asn_data_length;
843 		if ((length_field_len = asn_1_decode(&asn_data_length, data + 3, data_length - 3)) < 0) {
844 			print(LOG_LEVEL, ERROR, 1,
845 			      "Received invalid sized session package from slot %i\n",
846 			      slot_id);
847 			return;
848 		}
849 		uint32_t apdu_length = 3 + length_field_len + asn_data_length;
850 
851 		// check there is enough data
852 		if (apdu_length > data_length) {
853 			print(LOG_LEVEL, ERROR, 1,
854 			      "Received invalid sized session package from slot %i\n",
855 			      slot_id);
856 			return;
857 		}
858 		// pass the APDU up to the higher layers
859 		if (cb)
860 			cb(cb_arg, slot_id, session_number, resource_id, data, apdu_length);
861 
862 		// next!
863 		data += apdu_length;
864 		data_length -= apdu_length;
865 	}
866 
867 }
868 
en50221_sl_transport_callback(void * arg,int reason,uint8_t * data,uint32_t data_length,uint8_t slot_id,uint8_t connection_id)869 static void en50221_sl_transport_callback(void *arg, int reason,
870 					  uint8_t *data,
871 					  uint32_t data_length,
872 					  uint8_t slot_id,
873 					  uint8_t connection_id)
874 {
875 	struct en50221_session_layer *sl =
876 	    (struct en50221_session_layer *) arg;
877 	uint32_t i;
878 
879 	// deal with the reason for this callback
880 	switch (reason) {
881 	case T_CALLBACK_REASON_DATA:
882 		// fallthrough into rest of this function
883 		break;
884 
885 	case T_CALLBACK_REASON_CONNECTIONOPEN:
886 	{
887 		pthread_mutex_lock(&sl->setcallback_lock);
888 		en50221_sl_session_callback cb = sl->session;
889 		void *cb_arg = sl->session_arg;
890 		pthread_mutex_unlock(&sl->setcallback_lock);
891 
892 		if (cb)
893 			cb(cb_arg, S_SCALLBACK_REASON_TC_CONNECT,
894 				slot_id, connection_id, 0);
895 		return;
896 	}
897 
898 	case T_CALLBACK_REASON_CAMCONNECTIONOPEN:
899 	{
900 		pthread_mutex_lock(&sl->setcallback_lock);
901 		en50221_sl_session_callback cb = sl->session;
902 		void *cb_arg = sl->session_arg;
903 		pthread_mutex_unlock(&sl->setcallback_lock);
904 
905 		if (cb)
906 			cb(cb_arg,
907 				S_SCALLBACK_REASON_TC_CAMCONNECT,
908 				slot_id, connection_id, 0);
909 		return;
910 	}
911 
912 	case T_CALLBACK_REASON_CONNECTIONCLOSE:
913 	{
914 		pthread_mutex_lock(&sl->setcallback_lock);
915 		en50221_sl_session_callback cb = sl->session;
916 		void *cb_arg = sl->session_arg;
917 		pthread_mutex_unlock(&sl->setcallback_lock);
918 
919 		for (i = 0; i < sl->max_sessions; i++) {
920 			pthread_mutex_lock(&sl->sessions[i].session_lock);
921 
922 			if (sl->sessions[i].state == S_STATE_IDLE) {
923 				pthread_mutex_unlock(&sl->sessions[i].session_lock);
924 				continue;
925 			}
926 			if (sl->sessions[i].connection_id != connection_id) {
927 				pthread_mutex_unlock(&sl->sessions[i].session_lock);
928 				continue;
929 			}
930 
931 			sl->sessions[i].state = S_STATE_IDLE;
932 
933 			uint8_t _slot_id = sl->sessions[i].slot_id;
934 			uint32_t resource_id = sl->sessions[i].resource_id;
935 			pthread_mutex_unlock(&sl->sessions[i].session_lock);
936 
937 			if (cb)
938 				cb(cb_arg, S_SCALLBACK_REASON_CLOSE, _slot_id, i, resource_id);
939 		}
940 		return;
941 	}
942 
943 	case T_CALLBACK_REASON_SLOTCLOSE:
944 	{
945 		pthread_mutex_lock(&sl->setcallback_lock);
946 		en50221_sl_session_callback cb = sl->session;
947 		void *cb_arg = sl->session_arg;
948 		pthread_mutex_unlock(&sl->setcallback_lock);
949 
950 		for (i = 0; i < sl->max_sessions; i++) {
951 			pthread_mutex_lock(&sl->sessions[i].session_lock);
952 
953 			if (sl->sessions[i].state == S_STATE_IDLE) {
954 				pthread_mutex_unlock(&sl->sessions[i].session_lock);
955 				continue;
956 			}
957 			if (sl->sessions[i].slot_id != slot_id) {
958 				pthread_mutex_unlock(&sl->sessions[i].session_lock);
959 				continue;
960 			}
961 			sl->sessions[i].state = S_STATE_IDLE;
962 
963 			uint32_t resource_id = sl->sessions[i].resource_id;
964 			pthread_mutex_unlock(&sl->sessions[i].session_lock);
965 
966 			if (cb)
967 				cb(cb_arg, S_SCALLBACK_REASON_CLOSE, slot_id, i, resource_id);
968 
969 		}
970 		return;
971 	}
972 	}
973 
974 	// sanity check data length
975 	if (data_length < 1) {
976 		print(LOG_LEVEL, ERROR, 1,
977 		      "Received data with invalid length from module on slot %i\n",
978 		      slot_id);
979 		return;
980 	}
981 	// deal with the data
982 	uint8_t spdu_tag = data[0];
983 	switch (spdu_tag) {
984 	case ST_OPEN_SESSION_REQ:
985 		en50221_sl_handle_open_session_request(sl, data + 1,
986 						       data_length - 1,
987 						       slot_id,
988 						       connection_id);
989 		break;
990 
991 	case ST_CLOSE_SESSION_REQ:
992 		en50221_sl_handle_close_session_request(sl, data + 1,
993 							data_length - 1,
994 							slot_id,
995 							connection_id);
996 		break;
997 
998 	case ST_SESSION_NUMBER:
999 		en50221_sl_handle_session_package(sl, data + 1,
1000 						  data_length - 1, slot_id,
1001 						  connection_id);
1002 		break;
1003 
1004 	case ST_CREATE_SESSION_RES:
1005 		en50221_sl_handle_create_session_response(sl, data + 1,
1006 							  data_length - 1,
1007 							  slot_id,
1008 							  connection_id);
1009 		break;
1010 
1011 	case ST_CLOSE_SESSION_RES:
1012 		en50221_sl_handle_close_session_response(sl, data + 1,
1013 							 data_length - 1,
1014 							 slot_id,
1015 							 connection_id);
1016 		break;
1017 
1018 	default:
1019 		print(LOG_LEVEL, ERROR, 1,
1020 		      "Received unknown session tag %02x from module on slot %i",
1021 		      spdu_tag, slot_id);
1022 		break;
1023 	}
1024 }
1025 
en50221_sl_alloc_new_session(struct en50221_session_layer * sl,uint32_t resource_id,uint8_t slot_id,uint8_t connection_id,en50221_sl_resource_callback callback,void * arg)1026 static int en50221_sl_alloc_new_session(struct en50221_session_layer *sl,
1027 					uint32_t resource_id,
1028 					uint8_t slot_id,
1029 					uint8_t connection_id,
1030 					en50221_sl_resource_callback
1031 					callback, void *arg)
1032 {
1033 	int session_number = -1;
1034 	uint32_t i;
1035 	for (i = 1; i < sl->max_sessions; i++) {
1036 		if (sl->sessions[i].state == S_STATE_IDLE) {
1037 			session_number = i;
1038 			break;
1039 		}
1040 	}
1041 	if (session_number == -1) {
1042 		sl->error = EN50221ERR_OUTOFSESSIONS;
1043 		return -1;
1044 	}
1045 	// setup the session
1046 	sl->sessions[session_number].state = S_STATE_IN_CREATION;
1047 	sl->sessions[session_number].resource_id = resource_id;
1048 	sl->sessions[session_number].slot_id = slot_id;
1049 	sl->sessions[session_number].connection_id = connection_id;
1050 	sl->sessions[session_number].callback = callback;
1051 	sl->sessions[session_number].callback_arg = arg;
1052 
1053 	// ok
1054 	return session_number;
1055 }
1056