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.
955 lines
34 KiB
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;
|
|
}
|