IOC mediator: support CBC signal services

This patch implements the IOC CBC signal serivces including single signal,
multi-signal and group signal. All of them are used for CAN devices
communication and IOC on-board peripherals control.

Signed-off-by: Liu Yuan <yuan1.liu@intel.com>
Reviewed-by: Wang Yu <yu1.wang@intel.com>
Reviewed-by: Liu Shuo <shuo.a.liu@intel.com>
Reviewed-by: Zhao Yakui <yakui.zhao@intel.com>
Acked-by: Eddie Dong <eddie.dong@intel.com>
This commit is contained in:
Liu Yuan 2018-04-13 15:18:45 +08:00 committed by lijinxia
parent 0ad5243a6c
commit 9cbe34e555

View File

@ -33,6 +33,7 @@
*/
#include <stdio.h>
#include <stdbool.h>
#include "ioc.h"
@ -44,6 +45,8 @@ static int ioc_cbc_debug;
do { if (ioc_cbc_debug) printf(fmt, ##args); } while (0)
#define WPRINTF(fmt, args...) printf(fmt, ##args)
static void cbc_send_pkt(struct cbc_pkt *pkt);
/*
* Buffer bytes of reading from the virtual UART, because the bytes maybe can
* not generate one complete CBC frame.
@ -235,6 +238,197 @@ cbc_unpack_link(struct ioc_dev *ioc)
}
}
/*
* Find a CBC signal from CBC signal table.
*/
static inline struct cbc_signal *
cbc_find_signal(uint16_t id, struct cbc_signal *table, size_t size)
{
int i;
for (i = 0; i < size; i++) {
if (id == table[i].id)
return &table[i];
}
return NULL;
}
/*
* Find a CBC signal group from CBC signal group table.
*/
static inline struct cbc_group *
cbc_find_signal_group(uint16_t id, struct cbc_group *table, size_t size)
{
int i;
for (i = 0; i < size; i++) {
if (id == table[i].id)
return &table[i];
}
return NULL;
}
/*
* Signal length unit is bit in signal definition not byte,
* if the length is 3 bits then return 1 byte,
* if the length is 10 bits then return 2 bytes.
*/
static int
cbc_get_signal_len(uint16_t id, struct cbc_signal *table, size_t size)
{
struct cbc_signal *p;
p = cbc_find_signal(id, table, size);
return (p == NULL ? 0 : (p->len + 7)/8);
}
/*
* Set signal flag to inactive.
*/
static void
cbc_disable_signal(uint16_t id, struct cbc_signal *table, size_t size)
{
struct cbc_signal *p;
p = cbc_find_signal(id, table, size);
if (p)
p->flag = CBC_INACTIVE;
}
/*
* Set signal group flag to inactive.
*/
static void
cbc_disable_signal_group(uint16_t id, struct cbc_group *table, size_t size)
{
struct cbc_group *p;
p = cbc_find_signal_group(id, table, size);
if (p)
p->flag = CBC_INACTIVE;
}
/*
* Whitelist verification for a signal.
*/
static int
wlist_verify_signal(uint16_t id, struct wlist_signal *list, size_t size)
{
/* TODO: implementation */
return 0;
}
/*
* Whiltelist verification for a signal group.
*/
static int
wlist_verify_group(uint16_t id, struct wlist_group *list, size_t size)
{
/* TODO: implementation */
return 0;
}
/*
* CBC invalidates signals/groups.
*/
static void
cbc_set_invalidation(struct cbc_pkt *pkt, int type)
{
int i;
uint8_t *payload;
uint8_t num;
uint16_t id;
payload = pkt->req->buf + CBC_PAYLOAD_POS;
/* Number of signals or groups */
num = payload[1];
/*
* Safty check.
* Each signal/group id length is 2 bytes and 2 bytes of service header,
* the service length should less than the maximum service size.
*/
if ((num * 2 + 2) >= CBC_MAX_SERVICE_SIZE) {
DPRINTF("ioc cbc group number is invalid, number is %d\r\n",
num);
return;
}
for (i = 0; i < num; i++) {
id = payload[i * 2 + 2] | payload[i * 2 + 3] << 8;
if (type == CBC_INVAL_T_SIGNAL)
cbc_disable_signal(id, pkt->cfg->cbc_sig_tbl,
pkt->cfg->cbc_sig_num);
else if (type == CBC_INVAL_T_GROUP)
cbc_disable_signal_group(id, pkt->cfg->cbc_grp_tbl,
pkt->cfg->cbc_grp_num);
else
DPRINTF("%s", "ioc invalidation is not defined\r\n");
}
}
/*
* CBC multi-signal data process.
* Forwarding signal should be in whitelist, otherwise abandon the signal.
*/
static void
cbc_forward_signals(struct cbc_pkt *pkt)
{
int i, j;
int offset = 1;
uint8_t *payload = pkt->req->buf + CBC_PAYLOAD_POS;
uint8_t num = 0;
uint16_t id;
int signal_len;
int valids = 1;
for (i = 0; i < payload[0]; i++) {
id = payload[offset] | payload[offset + 1] << 8;
/* The length includes two bytes of signal ID occupation */
signal_len = cbc_get_signal_len(id, pkt->cfg->cbc_sig_tbl,
pkt->cfg->cbc_sig_num) + 2;
/* Whitelist verification */
if (wlist_verify_signal(id, pkt->cfg->wlist_sig_tbl,
pkt->cfg->wlist_sig_num) == 0) {
num++;
if (valids < offset) {
for (j = 0; j < signal_len; j++, valids++)
payload[valids] = payload[offset + j];
} else
valids += signal_len;
}
offset += signal_len;
/* Safty check */
if (offset + 1 > CBC_MAX_SERVICE_SIZE) {
DPRINTF("ioc offset=%d is error in forward signal\r\n",
offset);
return;
}
}
/* Send permitted signals */
if (num > 0) {
/*
* Set permitted signal numbers
*/
payload[0] = num;
/*
* Set multi-signal value for CBC service layer header,
* one service frame is generated completely.
*/
pkt->req->buf[CBC_SRV_POS] = CBC_SD_MULTI_SIGNAL;
pkt->req->srv_len = valids + CBC_SRV_HDR_SIZE;
/* Send the CBC packet */
cbc_send_pkt(pkt);
}
}
/*
* Pack CBC link header includes SOF value, extension bits, frame length bits,
* tx sequence bits, link alignment paddings and checksum byte.
@ -457,6 +651,87 @@ cbc_process_lifecycle(struct cbc_pkt *pkt)
static void
cbc_process_signal(struct cbc_pkt *pkt)
{
/*
* TODO: put the is_active into pkt structure instead local static
* variable when the cbc_process_signal is seperated.
*/
static bool is_active;
uint8_t cmd;
uint8_t *payload;
uint16_t id;
payload = pkt->req->buf + CBC_PAYLOAD_POS;
cmd = pkt->req->buf[CBC_SRV_POS];
/*
* FIXME:seperate the logic in two functions
* link_len is 0 means the packet is transmitted to PTY(UART DM)
* if the signal channel is not active, do not transmit it to PTY
* to CBC cdevs, always forward the signals because signal channel
* status only for UOS
*/
if (pkt->req->link_len == 0 && is_active == false &&
(cmd == CBC_SD_SINGLE_SIGNAL ||
cmd == CBC_SD_MULTI_SIGNAL ||
cmd == CBC_SD_GROUP_SIGNAL))
return;
switch (cmd) {
/* Bidirectional command */
case CBC_SD_SINGLE_SIGNAL:
id = payload[0] | payload[1] << 8;
if (wlist_verify_signal(id, pkt->cfg->wlist_sig_tbl,
pkt->cfg->wlist_sig_num) == 0)
cbc_send_pkt(pkt);
break;
/* Bidirectional command */
case CBC_SD_MULTI_SIGNAL:
cbc_forward_signals(pkt);
break;
/* Bidirectional command */
case CBC_SD_GROUP_SIGNAL:
id = payload[0] | payload[1] << 8;
if (wlist_verify_group(id, pkt->cfg->wlist_grp_tbl,
pkt->cfg->wlist_grp_num) == 0)
cbc_send_pkt(pkt);
break;
/* Bidirectional command */
case CBC_SD_INVAL_SSIG:
id = payload[0] | payload[1] << 8;
cbc_disable_signal(id, pkt->cfg->cbc_sig_tbl,
pkt->cfg->cbc_sig_num);
break;
/* Bidirectional command */
case CBC_SD_INVAL_MSIG:
cbc_set_invalidation(pkt, CBC_INVAL_T_SIGNAL);
break;
/* Bidirectional command */
case CBC_SD_INVAL_SGRP:
id = payload[0] | payload[1] << 8;
cbc_disable_signal_group(id, pkt->cfg->cbc_grp_tbl,
pkt->cfg->cbc_grp_num);
break;
/* Bidirectional command */
case CBC_SD_INVAL_MGRP:
cbc_set_invalidation(pkt, CBC_INVAL_T_GROUP);
break;
/*
* FIXME: seperate into rx signal process
* Open/reset/close are not bidirectional operations
* only for IOC rx thread
*/
case CBC_SD_OPEN_CHANNEL:
case CBC_SD_RESET_CHANNEL:
is_active = true;
break;
case CBC_SD_CLOSE_CHANNEL:
is_active = false;
break;
default:
DPRINTF("ioc got an new operation of signal channel=%d\r\n",
cmd);
break;
}
}
/*