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.
kaffeine/kaffeine/src/input/dvb/lib/libdvben50221/en50221_session.c

955 lines
34 KiB

/*
en50221 encoder An implementation for libdvb
an implementation for the en50221 transport layer
Copyright (C) 2004, 2005 Manu Abraham (manu@kromtek.com)
Copyright (C) 2005 Julian Scheel (julian at jusst dot de)
Copyright (C) 2006 Andrew de Quincey (adq_dvb@lidskialf.net)
This library is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <time.h>
#include <libdvbmisc/dvbmisc.h>
#include <sys/uio.h>
#include <pthread.h>
#include "en50221_transport.h"
#include "en50221_session.h"
#include "en50221_errno.h"
#include "asn_1.h"
// these are the possible session statuses
#define S_STATUS_OPEN 0x00 // session is opened
#define S_STATUS_CLOSE_NO_RES 0xF0 // could not open session, no proper resource available
#define S_STATUS_CLOSE_RES_UNAVAILABLE 0xF1 // could not open session, resource unavailable
#define S_STATUS_CLOSE_RES_LOW_VERSION 0xF2 // could not open session, resource version too low
#define S_STATUS_CLOSE_RES_BUSY 0xF3 // could not open session, resource is busy
#define ST_OPEN_SESSION_REQ 0x91 // h<--m
#define ST_OPEN_SESSION_RES 0x92 // h-->m
#define ST_CREATE_SESSION 0x93 // h-->m
#define ST_CREATE_SESSION_RES 0x94 // h<--m
#define ST_CLOSE_SESSION_REQ 0x95 // h<->m
#define ST_CLOSE_SESSION_RES 0x96 // h<->m
#define ST_SESSION_NUMBER 0x90 // h<->m
#define S_STATE_IDLE 0x01 // this session is not in use
#define S_STATE_ACTIVE 0x02 // this session is in use
#define S_STATE_IN_CREATION 0x04 // this session waits for a ST_CREATE_SESSION_RES to become active
#define S_STATE_IN_DELETION 0x08 // this session waits for ST_CLOSE_SESSION_RES to become idle again
// for each session we store its identifier, the resource-id
// it is linked to and the callback of the specific resource
struct en50221_session {
uint8_t state;
uint32_t resource_id;
uint8_t slot_id;
uint8_t connection_id;
en50221_sl_resource_callback callback;
void *callback_arg;
pthread_mutex_t session_lock;
};
struct en50221_session_layer_private
{
uint32_t max_sessions;
en50221_transport_layer tl;
en50221_sl_lookup_callback lookup;
void *lookup_arg;
en50221_sl_session_callback session;
void *session_arg;
pthread_mutex_t global_lock;
pthread_mutex_t setcallback_lock;
int error;
struct en50221_session *sessions;
};
static void en50221_sl_transport_callback(void *arg, int reason, uint8_t *data, uint32_t data_length,
uint8_t slot_id, uint8_t connection_id);
static int en50221_sl_alloc_new_session(struct en50221_session_layer_private *private,
uint32_t resource_id,
uint8_t slot_id,
uint8_t connection_id,
en50221_sl_resource_callback callback, void* arg);
en50221_session_layer en50221_sl_create(en50221_transport_layer tl,
uint32_t max_sessions)
{
struct en50221_session_layer_private *private = NULL;
uint32_t i;
// setup structure
private = (struct en50221_session_layer_private*) malloc(sizeof(struct en50221_session_layer_private));
if (private == NULL)
goto error_exit;
private->max_sessions = max_sessions;
private->lookup = NULL;
private->session = NULL;
private->tl = tl;
private->error = 0;
// init the mutex
pthread_mutex_init(&private->global_lock, NULL);
pthread_mutex_init(&private->setcallback_lock, NULL);
// create the slots
private->sessions = malloc(sizeof(struct en50221_session) * max_sessions);
if (private->sessions == NULL)
goto error_exit;
// set them up
for(i=0; i< max_sessions; i++) {
private->sessions[i].state = S_STATE_IDLE;
private->sessions[i].callback = NULL;
pthread_mutex_init(&private->sessions[i].session_lock, NULL);
}
// register ourselves with the transport layer
en50221_tl_register_callback(tl, en50221_sl_transport_callback, private);
return private;
error_exit:
en50221_sl_destroy(private);
return NULL;
}
void en50221_sl_destroy(en50221_session_layer sl)
{
struct en50221_session_layer_private *private = (struct en50221_session_layer_private *) sl;
uint32_t i;
if (private) {
if (private->sessions) {
for(i=0; i< private->max_sessions; i++) {
pthread_mutex_destroy(&private->sessions[i].session_lock);
}
free(private->sessions);
}
pthread_mutex_destroy(&private->setcallback_lock);
pthread_mutex_destroy(&private->global_lock);
free(private);
}
}
int en50221_sl_get_error(en50221_session_layer tl)
{
struct en50221_session_layer_private *private = (struct en50221_session_layer_private *) tl;
return private->error;
}
void en50221_sl_register_lookup_callback(en50221_session_layer sl, en50221_sl_lookup_callback callback, void *arg)
{
struct en50221_session_layer_private *private = (struct en50221_session_layer_private *) sl;
pthread_mutex_lock(&private->setcallback_lock);
private->lookup = callback;
private->lookup_arg = arg;
pthread_mutex_unlock(&private->setcallback_lock);
}
void en50221_sl_register_session_callback(en50221_session_layer sl,
en50221_sl_session_callback callback, void *arg)
{
struct en50221_session_layer_private *private = (struct en50221_session_layer_private *) sl;
pthread_mutex_lock(&private->setcallback_lock);
private->session = callback;
private->session_arg = arg;
pthread_mutex_unlock(&private->setcallback_lock);
}
int en50221_sl_create_session(en50221_session_layer sl, int slot_id, uint8_t connection_id, uint32_t resource_id,
en50221_sl_resource_callback callback, void* arg)
{
struct en50221_session_layer_private *private = (struct en50221_session_layer_private *) sl;
// lookup next free session_id:
pthread_mutex_lock(&private->global_lock);
int session_number = en50221_sl_alloc_new_session(private, resource_id, slot_id, connection_id, callback, arg);
if (session_number == -1) {
pthread_mutex_unlock(&private->global_lock);
return -1;
}
pthread_mutex_unlock(&private->global_lock);
// make up the header
uint8_t hdr[8];
hdr[0] = ST_CREATE_SESSION;
hdr[1] = 6;
hdr[2] = resource_id >> 24;
hdr[3] = resource_id >> 16;
hdr[4] = resource_id >> 8;
hdr[5] = resource_id;
hdr[6] = session_number >> 8;
hdr[7] = session_number;
// send this command
if (en50221_tl_send_data(private->tl, slot_id, connection_id, hdr, 8)) {
pthread_mutex_lock(&private->sessions[session_number].session_lock);
if (private->sessions[session_number].state == S_STATE_IN_CREATION) {
private->sessions[session_number].state = S_STATE_IDLE;
}
pthread_mutex_unlock(&private->sessions[session_number].session_lock);
private->error = en50221_tl_get_error(private->tl);
return -1;
}
// ok.
return session_number;
}
int en50221_sl_destroy_session(en50221_session_layer sl, uint16_t session_number)
{
struct en50221_session_layer_private *private = (struct en50221_session_layer_private *) sl;
if (session_number >= private->max_sessions) {
private->error = EN50221ERR_BADSESSIONNUMBER;
return -1;
}
pthread_mutex_lock(&private->sessions[session_number].session_lock);
if (!(private->sessions[session_number].state & (S_STATE_ACTIVE|S_STATE_IN_DELETION))) {
private->error = EN50221ERR_BADSESSIONNUMBER;
pthread_mutex_unlock(&private->sessions[session_number].session_lock);
return -1;
}
// set the state
private->sessions[session_number].state = S_STATE_IN_DELETION;
// get essential details
uint8_t slot_id = private->sessions[session_number].slot_id;
uint8_t connection_id = private->sessions[session_number].connection_id;
pthread_mutex_unlock(&private->sessions[session_number].session_lock);
// sendit
uint8_t hdr[4];
hdr[0] = ST_CLOSE_SESSION_REQ;
hdr[1] = 2;
hdr[2] = session_number >> 8;
hdr[3] = session_number;
if (en50221_tl_send_data(private->tl, slot_id, connection_id, hdr, 4)) {
pthread_mutex_lock(&private->sessions[session_number].session_lock);
if (private->sessions[session_number].state == S_STATE_IN_DELETION) {
private->sessions[session_number].state = S_STATE_IDLE;
}
pthread_mutex_unlock(&private->sessions[session_number].session_lock);
private->error = en50221_tl_get_error(private->tl);
return -1;
}
return 0;
}
int en50221_sl_send_data(en50221_session_layer sl, uint16_t session_number, uint8_t *data, uint16_t data_length)
{
struct en50221_session_layer_private *private = (struct en50221_session_layer_private *) sl;
if (session_number >= private->max_sessions) {
private->error = EN50221ERR_BADSESSIONNUMBER;
return -1;
}
pthread_mutex_lock(&private->sessions[session_number].session_lock);
if (private->sessions[session_number].state != S_STATE_ACTIVE) {
private->error = EN50221ERR_BADSESSIONNUMBER;
pthread_mutex_unlock(&private->sessions[session_number].session_lock);
return -1;
}
// get essential details
uint8_t slot_id = private->sessions[session_number].slot_id;
uint8_t connection_id = private->sessions[session_number].connection_id;
pthread_mutex_unlock(&private->sessions[session_number].session_lock);
// sendit
struct iovec iov[2];
uint8_t hdr[4];
hdr[0] = ST_SESSION_NUMBER;
hdr[1] = 2;
hdr[2] = session_number >> 8;
hdr[3] = session_number;
iov[0].iov_base = hdr;
iov[0].iov_len = 4;
iov[1].iov_base = data;
iov[1].iov_len = data_length;
if (en50221_tl_send_datav(private->tl, slot_id, connection_id, iov, 2)) {
private->error = en50221_tl_get_error(private->tl);
return -1;
}
return 0;
}
int en50221_sl_send_datav(en50221_session_layer sl, uint16_t session_number,
struct iovec *vector, int iov_count)
{
struct en50221_session_layer_private *private = (struct en50221_session_layer_private *) sl;
if (session_number >= private->max_sessions) {
private->error = EN50221ERR_BADSESSIONNUMBER;
return -1;
}
pthread_mutex_lock(&private->sessions[session_number].session_lock);
if (private->sessions[session_number].state != S_STATE_ACTIVE) {
private->error = EN50221ERR_BADSESSIONNUMBER;
pthread_mutex_unlock(&private->sessions[session_number].session_lock);
return -1;
}
if (iov_count > 9) {
private->error = EN50221ERR_IOVLIMIT;
pthread_mutex_unlock(&private->sessions[session_number].session_lock);
return -1;
}
uint8_t slot_id = private->sessions[session_number].slot_id;
uint8_t connection_id = private->sessions[session_number].connection_id;
pthread_mutex_unlock(&private->sessions[session_number].session_lock);
// make up the header
struct iovec out_iov[10];
uint8_t hdr[4];
hdr[0] = ST_SESSION_NUMBER;
hdr[1] = 2;
hdr[2] = session_number >> 8;
hdr[3] = session_number;
out_iov[0].iov_base = hdr;
out_iov[0].iov_len = 4;
// make up the data
memcpy(&out_iov[1], vector, iov_count * sizeof(struct iovec));
// send this command
if (en50221_tl_send_datav(private->tl, slot_id, connection_id, out_iov, iov_count+1)) {
private->error = en50221_tl_get_error(private->tl);
return -1;
}
return 0;
}
int en50221_sl_broadcast_data(en50221_session_layer sl, int slot_id, uint32_t resource_id,
uint8_t *data, uint16_t data_length)
{
struct en50221_session_layer_private *private = (struct en50221_session_layer_private *) sl;
uint32_t i;
for(i = 0; i < private->max_sessions; i++)
{
pthread_mutex_lock(&private->sessions[i].session_lock);
if (private->sessions[i].state != S_STATE_ACTIVE) {
pthread_mutex_unlock(&private->sessions[i].session_lock);
continue;
}
if ((slot_id != -1) && (slot_id != private->sessions[i].slot_id)) {
pthread_mutex_unlock(&private->sessions[i].session_lock);
continue;
}
if (private->sessions[i].resource_id == resource_id) {
pthread_mutex_unlock(&private->sessions[i].session_lock);
en50221_sl_send_data(sl, i, data, data_length);
} else {
pthread_mutex_unlock(&private->sessions[i].session_lock);
}
}
return 0;
}
static void en50221_sl_handle_open_session_request(struct en50221_session_layer_private *private,
uint8_t *data, uint32_t data_length, uint8_t slot_id, uint8_t connection_id)
{
// check
if (data_length < 5) {
print(LOG_LEVEL, ERROR, 1, "Received data with invalid length from module on slot %02x\n", slot_id);
return;
}
if (data[0] != 4) {
print(LOG_LEVEL, ERROR, 1, "Received data with invalid length from module on slot %02x\n", slot_id);
return;
}
// get the resource id
uint32_t requested_resource_id = (data[1] << 24) | (data[2] << 16) | (data[3] << 8) | data[4];
// get lookup callback details
pthread_mutex_lock(&private->setcallback_lock);
en50221_sl_lookup_callback lcb = private->lookup;
void *lcb_arg = private->lookup_arg;
pthread_mutex_unlock(&private->setcallback_lock);
// first of all, lookup this resource id
int status = S_STATUS_CLOSE_NO_RES;
en50221_sl_resource_callback resource_callback = NULL;
void *resource_arg = NULL;
uint32_t connected_resource_id;
if (lcb) {
status = lcb(lcb_arg, slot_id, requested_resource_id, &resource_callback, &resource_arg, &connected_resource_id);
switch(status) {
case 0:
status = S_STATUS_OPEN;
break;
case -1:
status = S_STATUS_CLOSE_NO_RES;
break;
case -2:
status = S_STATUS_CLOSE_RES_LOW_VERSION;
break;
case -3:
status = S_STATUS_CLOSE_RES_UNAVAILABLE;
break;
}
}
// if we found it, get a new session for it
int session_number = -1;
if (status == S_STATUS_OPEN) {
// lookup next free session_id:
pthread_mutex_lock(&private->global_lock);
session_number = en50221_sl_alloc_new_session(private, connected_resource_id, slot_id, connection_id,
resource_callback, resource_arg);
pthread_mutex_unlock(&private->global_lock);
if (session_number == -1) {
status = S_STATUS_CLOSE_NO_RES;
} else {
// inform upper layers/ check availability
pthread_mutex_lock(&private->setcallback_lock);
en50221_sl_session_callback cb = private->session;
void *cb_arg = private->session_arg;
pthread_mutex_unlock(&private->setcallback_lock);
if (cb) {
if (cb(cb_arg, S_SCALLBACK_REASON_CAMCONNECTING, slot_id, session_number, connected_resource_id)) {
status = S_STATUS_CLOSE_RES_BUSY;
}
} else {
status = S_STATUS_CLOSE_RES_UNAVAILABLE;
}
}
}
// send response
uint8_t hdr[9];
hdr[0] = ST_OPEN_SESSION_RES;
hdr[1] = 7;
hdr[2] = status;
hdr[3] = connected_resource_id >> 24;
hdr[4] = connected_resource_id >> 16;
hdr[5] = connected_resource_id >> 8;
hdr[6] = connected_resource_id;
hdr[7] = session_number >> 8;
hdr[8] = session_number;
if (en50221_tl_send_data(private->tl, slot_id, connection_id, hdr, 9)) {
print(LOG_LEVEL, ERROR, 1, "Transport layer error %i occurred\n", en50221_tl_get_error(private->tl));
status = S_STATUS_CLOSE_NO_RES;
// fallthrough
}
// inform upper layers what happened
if (session_number != -1) {
// setup session state apppropriately from upper layer response
pthread_mutex_lock(&private->sessions[session_number].session_lock);
if (status != S_STATUS_OPEN) {
private->sessions[session_number].state = S_STATE_IDLE;
} else {
private->sessions[session_number].state = S_STATE_ACTIVE;
}
pthread_mutex_unlock(&private->sessions[session_number].session_lock);
// tell upper layers
if (private->sessions[session_number].state == S_STATE_ACTIVE) {
pthread_mutex_lock(&private->setcallback_lock);
en50221_sl_session_callback cb = private->session;
void *cb_arg = private->session_arg;
pthread_mutex_unlock(&private->setcallback_lock);
if (status == S_STATUS_OPEN) {
if (cb)
cb(cb_arg, S_SCALLBACK_REASON_CAMCONNECTED, slot_id, session_number, connected_resource_id);
} else {
private->sessions[session_number].state = S_STATE_IDLE;
if (cb)
cb(cb_arg, S_SCALLBACK_REASON_CAMCONNECTFAIL, slot_id, session_number, connected_resource_id);
}
}
}
}
static void en50221_sl_handle_close_session_request(struct en50221_session_layer_private *private,
uint8_t *data, uint32_t data_length, uint8_t slot_id, uint8_t connection_id)
{
// check
if (data_length < 3) {
print(LOG_LEVEL, ERROR, 1, "Received data with invalid length from module on slot %02x\n", slot_id);
return;
}
if (data[0] != 2) {
print(LOG_LEVEL, ERROR, 1, "Received data with invalid length from module on slot %02x\n", slot_id);
return;
}
// extract session number
uint16_t session_number = (data[1] << 8) | data[2];
// check session number is ok
uint8_t code = 0x00;
uint32_t resource_id = 0;
if (session_number >= private->max_sessions) {
code = 0xF0; // session close error
print(LOG_LEVEL, ERROR, 1, "Received bad session id %i\n", slot_id);
} else {
pthread_mutex_lock(&private->sessions[session_number].session_lock);
if (slot_id != private->sessions[session_number].slot_id) {
print(LOG_LEVEL, ERROR, 1, "Received unexpected session on invalid slot %i\n", slot_id);
code = 0xF0; // session close error
}
if (connection_id != private->sessions[session_number].connection_id) {
print(LOG_LEVEL, ERROR, 1, "Received unexpected session on invalid slot %i\n", slot_id);
code = 0xF0; // session close error
}
if (!(private->sessions[session_number].state & (S_STATE_ACTIVE|S_STATE_IN_DELETION))) {
print(LOG_LEVEL, ERROR, 1, "Received unexpected session on invalid slot %i\n", slot_id);
code = 0xF0; // session close error
}
if (code == 0x00) {
private->sessions[session_number].state = S_STATE_IDLE;
code = 0x00; // close ok
}
resource_id = private->sessions[session_number].resource_id;
pthread_mutex_unlock(&private->sessions[session_number].session_lock);
}
// make up the response
uint8_t hdr[5];
hdr[0] = ST_CLOSE_SESSION_RES;
hdr[1] = 3;
hdr[2] = code;
hdr[3] = session_number >> 8;
hdr[4] = session_number;
// sendit
if (en50221_tl_send_data(private->tl, slot_id, connection_id, hdr, 5)) {
print(LOG_LEVEL, ERROR, 1, "Transport layer reports error %i on slot %i\n",
en50221_tl_get_error(private->tl), slot_id);
}
// callback to announce destruction to resource if it was ok
if (code == 0x00) {
pthread_mutex_lock(&private->setcallback_lock);
en50221_sl_session_callback cb = private->session;
void *cb_arg = private->session_arg;
pthread_mutex_unlock(&private->setcallback_lock);
if (cb)
cb(cb_arg, S_SCALLBACK_REASON_CLOSE, slot_id, session_number, resource_id);
}
}
static void en50221_sl_handle_create_session_response(struct en50221_session_layer_private *private,
uint8_t *data, uint32_t data_length, uint8_t slot_id, uint8_t connection_id)
{
// check
if (data_length < 8) {
print(LOG_LEVEL, ERROR, 1, "Received data with invalid length from module on slot %02x\n", slot_id);
return;
}
if (data[0] != 7) {
print(LOG_LEVEL, ERROR, 1, "Received data with invalid length from module on slot %02x\n", slot_id);
return;
}
// extract session number
uint16_t session_number = (data[5] << 8) | data[6];
// check session number is ok
if (session_number >= private->max_sessions) {
print(LOG_LEVEL, ERROR, 1, "Received bad session id %i\n", slot_id);
return;
}
pthread_mutex_lock(&private->sessions[session_number].session_lock);
if (slot_id != private->sessions[session_number].slot_id) {
print(LOG_LEVEL, ERROR, 1, "Received unexpected session on invalid slot %i\n", slot_id);
pthread_mutex_unlock(&private->sessions[session_number].session_lock);
return;
}
if (connection_id != private->sessions[session_number].connection_id) {
print(LOG_LEVEL, ERROR, 1, "Received unexpected session on invalid slot %i\n", slot_id);
pthread_mutex_unlock(&private->sessions[session_number].session_lock);
return;
}
if (private->sessions[session_number].state != S_STATE_IN_CREATION) {
print(LOG_LEVEL, ERROR, 1, "Received unexpected session on invalid slot %i\n", slot_id);
pthread_mutex_unlock(&private->sessions[session_number].session_lock);
return;
}
// extract status
if (data[1] != S_STATUS_OPEN) {
print(LOG_LEVEL, ERROR, 1, "Session creation failed 0x%02x\n", data[1]);
private->sessions[session_number].state = S_STATE_IDLE;
pthread_mutex_unlock(&private->sessions[session_number].session_lock);
// inform upper layers
pthread_mutex_lock(&private->setcallback_lock);
en50221_sl_session_callback cb = private->session;
void *cb_arg = private->session_arg;
pthread_mutex_unlock(&private->setcallback_lock);
if (cb)
cb(cb_arg, S_SCALLBACK_REASON_CONNECTFAIL, slot_id, session_number,
private->sessions[session_number].resource_id);
return;
}
// set it active
private->sessions[session_number].state = S_STATE_ACTIVE;
pthread_mutex_unlock(&private->sessions[session_number].session_lock);
// inform upper layers
pthread_mutex_lock(&private->setcallback_lock);
en50221_sl_session_callback cb = private->session;
void *cb_arg = private->session_arg;
pthread_mutex_unlock(&private->setcallback_lock);
if (cb)
cb(cb_arg, S_SCALLBACK_REASON_CONNECTED, slot_id, session_number,
private->sessions[session_number].resource_id);
}
static void en50221_sl_handle_close_session_response(struct en50221_session_layer_private *private,
uint8_t *data, uint32_t data_length, uint8_t slot_id, uint8_t connection_id)
{
// check
if (data_length < 5) {
print(LOG_LEVEL, ERROR, 1, "Received data with invalid length from module on slot %02x\n", slot_id);
return;
}
if (data[0] != 4) {
print(LOG_LEVEL, ERROR, 1, "Received data with invalid length from module on slot %02x\n", slot_id);
return;
}
// extract session number
uint16_t session_number = (data[2] << 8) | data[3];
// check session number is ok
if (session_number >= private->max_sessions) {
print(LOG_LEVEL, ERROR, 1, "Received bad session id %i\n", slot_id);
return;
}
pthread_mutex_lock(&private->sessions[session_number].session_lock);
if (slot_id != private->sessions[session_number].slot_id) {
print(LOG_LEVEL, ERROR, 1, "Received unexpected session on invalid slot %i\n", slot_id);
pthread_mutex_unlock(&private->sessions[session_number].session_lock);
return;
}
if (connection_id != private->sessions[session_number].connection_id) {
print(LOG_LEVEL, ERROR, 1, "Received unexpected session on invalid slot %i\n", slot_id);
pthread_mutex_unlock(&private->sessions[session_number].session_lock);
return;
}
if (private->sessions[session_number].state != S_STATE_IN_DELETION) {
print(LOG_LEVEL, ERROR, 1, "Received unexpected session on invalid slot %i\n", slot_id);
pthread_mutex_unlock(&private->sessions[session_number].session_lock);
return;
}
// extract status
if (data[1] != 0x00) {
print(LOG_LEVEL, ERROR, 1, "Session close failed 0x%02x\n", data[1]);
// just fallthrough anyway
}
// completed
private->sessions[session_number].state = S_STATE_IDLE;
pthread_mutex_unlock(&private->sessions[session_number].session_lock);
}
static void en50221_sl_handle_session_package(struct en50221_session_layer_private *private,
uint8_t *data, uint32_t data_length,
uint8_t slot_id, uint8_t connection_id)
{
// check
if (data_length < 3) {
print(LOG_LEVEL, ERROR, 1, "Received data with invalid length from module on slot %i\n", slot_id);
return;
}
if (data[0] != 2) {
print(LOG_LEVEL, ERROR, 1, "Received data with invalid length from module on slot %i\n", slot_id);
return;
}
// get session number
uint16_t session_number = (data[1] << 8) | data[2];
// check it
if (session_number >= private->max_sessions) {
print(LOG_LEVEL, ERROR, 1, "Received data with bad session_number from module on slot %i\n", slot_id);
return;
}
pthread_mutex_lock(&private->sessions[session_number].session_lock);
if (slot_id != private->sessions[session_number].slot_id) {
print(LOG_LEVEL, ERROR, 1, "Received unexpected session on invalid slot %i\n", slot_id);
pthread_mutex_unlock(&private->sessions[session_number].session_lock);
return;
}
if (connection_id != private->sessions[session_number].connection_id) {
print(LOG_LEVEL, ERROR, 1, "Received unexpected session on invalid slot %i\n", slot_id);
pthread_mutex_unlock(&private->sessions[session_number].session_lock);
return;
}
if (private->sessions[session_number].state != S_STATE_ACTIVE) {
print(LOG_LEVEL, ERROR, 1, "Received data with bad session_number from module on slot %i\n", slot_id);
pthread_mutex_unlock(&private->sessions[session_number].session_lock);
return;
}
en50221_sl_resource_callback cb = private->sessions[session_number].callback;
void *cb_arg = private->sessions[session_number].callback_arg;
uint32_t resource_id = private->sessions[session_number].resource_id;
pthread_mutex_unlock(&private->sessions[session_number].session_lock);
// there can be > 1 APDU following the package - all for the same session/resource_id tho.
data += 3;
data_length -= 3;
while(data_length) {
// check length field
if (data_length < 3) {
print(LOG_LEVEL, ERROR, 1, "Received invalid sized session package from slot %i\n", slot_id);
return;
}
// parse the APDU's length field
int length_field_len;
uint16_t asn_data_length;
if ((length_field_len = asn_1_decode(&asn_data_length, data+3, data_length-3)) < 0) {
print(LOG_LEVEL, ERROR, 1, "Received invalid sized session package from slot %i\n", slot_id);
return;
}
uint32_t apdu_length = 3 + length_field_len + asn_data_length;
// check there is enough data
if (apdu_length > data_length) {
print(LOG_LEVEL, ERROR, 1, "Received invalid sized session package from slot %i\n", slot_id);
return;
}
// pass the APDU up to the higher layers
if (cb)
cb(cb_arg, slot_id, session_number, resource_id, data, apdu_length);
// next!
data += apdu_length;
data_length -= apdu_length;
}
}
static void en50221_sl_transport_callback(void *arg, int reason, uint8_t *data, uint32_t data_length,
uint8_t slot_id, uint8_t connection_id)
{
struct en50221_session_layer_private *private = (struct en50221_session_layer_private *) arg;
uint32_t i;
// deal with the reason for this callback
switch(reason) {
case T_CALLBACK_REASON_DATA:
// fallthrough into rest of this function
break;
case T_CALLBACK_REASON_CONNECTIONOPEN:
{
pthread_mutex_lock(&private->setcallback_lock);
en50221_sl_session_callback cb = private->session;
void *cb_arg = private->session_arg;
pthread_mutex_unlock(&private->setcallback_lock);
if (cb)
cb(cb_arg, S_SCALLBACK_REASON_TC_CONNECT, slot_id, connection_id, 0);
return;
}
case T_CALLBACK_REASON_CAMCONNECTIONOPEN:
{
pthread_mutex_lock(&private->setcallback_lock);
en50221_sl_session_callback cb = private->session;
void *cb_arg = private->session_arg;
pthread_mutex_unlock(&private->setcallback_lock);
if (cb)
cb(cb_arg, S_SCALLBACK_REASON_TC_CAMCONNECT, slot_id, connection_id, 0);
return;
}
case T_CALLBACK_REASON_CONNECTIONCLOSE:
{
pthread_mutex_lock(&private->setcallback_lock);
en50221_sl_session_callback cb = private->session;
void *cb_arg = private->session_arg;
pthread_mutex_unlock(&private->setcallback_lock);
for(i=0; i< private->max_sessions; i++) {
pthread_mutex_lock(&private->sessions[i].session_lock);
if (private->sessions[i].state == S_STATE_IDLE) {
pthread_mutex_unlock(&private->sessions[i].session_lock);
continue;
}
if (private->sessions[i].connection_id != connection_id) {
pthread_mutex_unlock(&private->sessions[i].session_lock);
continue;
}
private->sessions[i].state = S_STATE_IDLE;
uint8_t slot_id = private->sessions[i].slot_id;
uint32_t resource_id = private->sessions[i].resource_id;
pthread_mutex_unlock(&private->sessions[i].session_lock);
if (cb)
cb(cb_arg, S_SCALLBACK_REASON_CLOSE, slot_id, i, resource_id);
}
return;
}
case T_CALLBACK_REASON_SLOTCLOSE:
{
pthread_mutex_lock(&private->setcallback_lock);
en50221_sl_session_callback cb = private->session;
void *cb_arg = private->session_arg;
pthread_mutex_unlock(&private->setcallback_lock);
for(i=0; i< private->max_sessions; i++) {
pthread_mutex_lock(&private->sessions[i].session_lock);
if (private->sessions[i].state == S_STATE_IDLE) {
pthread_mutex_unlock(&private->sessions[i].session_lock);
continue;
}
if (private->sessions[i].slot_id != slot_id) {
pthread_mutex_unlock(&private->sessions[i].session_lock);
continue;
}
private->sessions[i].state = S_STATE_IDLE;
uint32_t resource_id = private->sessions[i].resource_id;
pthread_mutex_unlock(&private->sessions[i].session_lock);
if (cb)
cb(cb_arg, S_SCALLBACK_REASON_CLOSE, slot_id, i, resource_id);
}
return;
}
}
// sanity check data length
if (data_length < 1) {
print(LOG_LEVEL, ERROR, 1, "Received data with invalid length from module on slot %i\n", slot_id);
return;
}
// deal with the data
uint8_t spdu_tag = data[0];
switch(spdu_tag)
{
case ST_OPEN_SESSION_REQ:
en50221_sl_handle_open_session_request(private, data+1, data_length-1, slot_id, connection_id);
break;
case ST_CLOSE_SESSION_REQ:
en50221_sl_handle_close_session_request(private, data+1, data_length-1, slot_id, connection_id);
break;
case ST_SESSION_NUMBER:
en50221_sl_handle_session_package(private, data+1, data_length-1, slot_id, connection_id);
break;
case ST_CREATE_SESSION_RES:
en50221_sl_handle_create_session_response(private, data+1, data_length-1, slot_id, connection_id);
break;
case ST_CLOSE_SESSION_RES:
en50221_sl_handle_close_session_response(private, data+1, data_length-1, slot_id, connection_id);
break;
default:
print(LOG_LEVEL, ERROR, 1, "Received unknown session tag %02x from module on slot %i", spdu_tag, slot_id);
break;
}
}
static int en50221_sl_alloc_new_session(struct en50221_session_layer_private *private,
uint32_t resource_id,
uint8_t slot_id,
uint8_t connection_id,
en50221_sl_resource_callback callback, void* arg)
{
int session_number = -1;
uint32_t i;
for(i = 1; i < private->max_sessions; i++) {
if (private->sessions[i].state == S_STATE_IDLE) {
session_number = i;
break;
}
}
if (session_number == -1) {
private->error = EN50221ERR_OUTOFSESSIONS;
return -1;
}
// setup the session
private->sessions[session_number].state = S_STATE_IN_CREATION;
private->sessions[session_number].resource_id = resource_id;
private->sessions[session_number].slot_id = slot_id;
private->sessions[session_number].connection_id = connection_id;
private->sessions[session_number].callback = callback;
private->sessions[session_number].callback_arg = arg;
// ok
return session_number;
}