blob: 942663f02c37b5a180747ca98e782eaf5855e6a7 [file] [log] [blame]
/*
* Copyright(c) 2013 FCI Inc. All Rights Reserved
*
* File name : fc8080_demux.c
*
* Description : fc8080 TSIF demux source file
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
* History :
* ----------------------------------------------------------------------
*/
#include <linux/kernel.h>
#include <linux/string.h>
#include "fci_types.h"
#include "fc8080_regs.h"
/* sync byte */
#define SYNC_MASK_FIC 0x80
#define SYNC_MASK_NVIDEO 0xc0
#define SYNC_MASK_VIDEO 0x47
#define SYNC_MASK_VIDEO1 0xb8
/* packet indicator */
#define PKT_IND_NONE 0x00
#define PKT_IND_END 0x20
#define PKT_IND_CONTINUE 0x40
#define PKT_IND_START 0x80
#define PKT_IND_MASK 0xe0
/* data size */
#define FIC_DATA_SIZE (FIC_BUF_LENGTH / 2)
#define VIDEO_DATA_SIZE (CH0_BUF_LENGTH / 2) /* the maximum size */
#define NON_VIDEO_DATA_SIZE (CH0_BUF_LENGTH / 2) /* the maximum size */
#define TS_FRAME_SIZE 188
/* TS service information */
struct TS_FRAME_INFO {
u8 ind;
u16 length; /* current receiving length */
u8 subch_id; /* subch id */
u8 *buffer;
};
/* TS frame header information */
struct TS_FRAME_HDR {
u8 sync;
u8 ind;
u16 length;
u8 *data;
};
static u32 fic_user_data;
static u32 msc_user_data;
static int (*fic_callback)(u32 userdata, u8 *data, int length);
static int (*msc_callback)(u32 userdata, u8 subch_id, u8 *data, int length);
static u8 video_data[2][VIDEO_DATA_SIZE];
static u8 fic_data[FIC_DATA_SIZE];
static u8 non_video_data[4][NON_VIDEO_DATA_SIZE];
struct TS_FRAME_INFO video_frame_info[2] = {
/* Video 0 */
{
0, 0, 0xff, (u8 *) &video_data[0]
},
/* Video 1 */
{
0, 0, 0xff, (u8 *) &video_data[1]
}
};
struct TS_FRAME_INFO fic_frame_info = {
0, 0, 0xff, (u8 *) &fic_data[0]
};
struct TS_FRAME_INFO non_video_frame_info[4] = {
{0, 0, 0xff, (u8 *) non_video_data[0]},
{0, 0, 0xff, (u8 *) non_video_data[1]},
{0, 0, 0xff, (u8 *) non_video_data[2]},
{0, 0, 0xff, (u8 *) non_video_data[3]}
};
/* mapping buf id to subch id */
static u8 video_subch[2] = {
0xff, 0xff
};
/* mapping subch id to buf id */
static u8 non_video_subch[64] = {
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff
};
static int gether_fic(u8 *data)
{
struct TS_FRAME_HDR header;
u16 len;
/*header.sync = data[0];*/
header.ind = data[1];
header.length = (data[2] << 8) | data[3];
header.data = &data[4];
/* current real length */
len = (header.length > 184) ? 184 : header.length;
switch (header.ind) {
case PKT_IND_START:
fic_frame_info.ind = header.ind;
fic_frame_info.length = 0;
memcpy((void *) &fic_frame_info.buffer[0], header.data, len);
fic_frame_info.length = len;
return BBM_OK;
case PKT_IND_CONTINUE:
if (fic_frame_info.ind != PKT_IND_START) {
fic_frame_info.ind = PKT_IND_NONE;
return BBM_E_MUX_INDICATOR;
}
fic_frame_info.ind = header.ind;
memcpy((void *) &fic_frame_info.buffer[fic_frame_info.length],
header.data, len);
fic_frame_info.length += len;
return BBM_OK;
case PKT_IND_END:
if (fic_frame_info.ind != PKT_IND_CONTINUE) {
fic_frame_info.ind = PKT_IND_NONE;
return BBM_E_MUX_INDICATOR;
}
fic_frame_info.ind = header.ind;
memcpy((void *) &fic_frame_info.buffer[fic_frame_info.length],
header.data, len);
fic_frame_info.length += len;
break;
default:
return BBM_E_MUX_INDICATOR;
}
if ((fic_frame_info.length >= FIC_DATA_SIZE) && (fic_frame_info.ind ==
PKT_IND_END)) {
if (fic_callback)
(*fic_callback)(fic_user_data, fic_frame_info.buffer,
fic_frame_info.length);
fic_frame_info.length = 0;
fic_frame_info.ind = PKT_IND_NONE;
}
return BBM_OK;
}
static int gether_non_video(u8 *data)
{
struct TS_FRAME_HDR header;
u16 len;
u8 subch;
u8 buf_id;
u16 data_sz;
header.sync = data[0];
header.ind = data[1];
header.length = (data[2] << 8) | data[3];
header.data = &data[4];
len = (header.length >= 184) ? 184 : header.length;
subch = header.sync & 0x3f;
buf_id = non_video_subch[subch];
if (buf_id == 0xff)
return BBM_E_MUX_SUBCHANNEL;
if (buf_id > 3)
return BBM_E_MUX_SUBCHANNEL;
switch (header.ind) {
case PKT_IND_START:
non_video_frame_info[buf_id].length = 0;
memcpy((void *) &non_video_frame_info[buf_id].buffer[0],
header.data, len);
non_video_frame_info[buf_id].length += len;
if (non_video_frame_info[buf_id].length < header.length)
non_video_frame_info[buf_id].ind = header.ind;
else
non_video_frame_info[buf_id].ind = PKT_IND_END;
break;
case PKT_IND_CONTINUE:
data_sz = non_video_frame_info[buf_id].length;
if (data_sz + len > NON_VIDEO_DATA_SIZE)
return BBM_E_MUX_DATA_SIZE;
memcpy((void *) &non_video_frame_info[buf_id].buffer[data_sz],
header.data, len);
non_video_frame_info[buf_id].length += len;
non_video_frame_info[buf_id].ind = header.ind;
return BBM_OK;
case PKT_IND_END:
data_sz = non_video_frame_info[buf_id].length;
if (data_sz + len > NON_VIDEO_DATA_SIZE)
return BBM_E_MUX_DATA_SIZE;
memcpy((void *) &non_video_frame_info[buf_id].buffer[data_sz],
header.data, len);
non_video_frame_info[buf_id].length += len;
non_video_frame_info[buf_id].ind = header.ind;
break;
default:
return BBM_E_MUX_INDICATOR;
}
if ((header.ind == PKT_IND_END) || (header.ind == PKT_IND_START &&
header.length <= 184)) {
if (msc_callback)
(*msc_callback)(msc_user_data, subch,
&non_video_frame_info[buf_id].buffer[0],
non_video_frame_info[buf_id].length);
non_video_frame_info[buf_id].length = 0;
non_video_frame_info[buf_id].ind = PKT_IND_NONE;
}
return BBM_OK;
}
static int gether_video(u8 *data)
{
u8 subch;
u8 buf_id;
u16 data_sz;
switch (data[0]) {
case SYNC_MASK_VIDEO:
buf_id = 0;
subch = video_subch[0];
break;
case SYNC_MASK_VIDEO1:
buf_id = 1;
data[0] = 0x47;
subch = video_subch[1];
break;
default:
return BBM_E_MUX_DATA_MASK;
}
if (subch == 0xff)
return BBM_E_MUX_SUBCHANNEL;
if (subch > 64)
return BBM_E_MUX_SUBCHANNEL;
data_sz = video_frame_info[buf_id].length;
if (data_sz + TS_FRAME_SIZE > VIDEO_DATA_SIZE)
return BBM_E_MUX_DATA_SIZE;
memcpy((void *) (video_frame_info[buf_id].buffer + data_sz), data, 188);
video_frame_info[buf_id].length += TS_FRAME_SIZE;
if (video_frame_info[buf_id].length >= VIDEO_DATA_SIZE) {
if (msc_callback)
(*msc_callback)(msc_user_data, subch,
video_frame_info[buf_id].buffer,
video_frame_info[buf_id].length);
video_frame_info[buf_id].length = 0;
}
return BBM_OK;
}
int fc8080_demux(u8 *data, u32 length)
{
u32 i;
for (i = 0; i < length; i += TS_FRAME_SIZE) {
if (data[i] == SYNC_MASK_FIC)
gether_fic(&data[i]);
else if ((data[i] == SYNC_MASK_VIDEO) || (data[i] ==
SYNC_MASK_VIDEO1))
gether_video(&data[i]);
else if (((data[i] & 0xc0) == 0xc0) || ((data[i] & 0xc0) ==
0x00))
gether_non_video(&data[i]);
}
return BBM_OK;
}
int fc8080_demux_fic_callback_register(u32 userdata,
int (*callback)(u32 userdata, u8 *data, int length))
{
fic_user_data = userdata;
fic_callback = callback;
return BBM_OK;
}
int fc8080_demux_msc_callback_register(u32 userdata,
int (*callback)(u32 userdata, u8 subch_id, u8 *data, int length))
{
msc_user_data = userdata;
msc_callback = callback;
return BBM_OK;
}
int fc8080_demux_select_video(u8 subch_id, u8 buf_id)
{
video_subch[buf_id] = subch_id & 0x3f;
return BBM_OK;
}
int fc8080_demux_select_channel(u8 subch_id, u8 buf_id)
{
non_video_subch[subch_id & 0x3f] = buf_id;
return BBM_OK;
}
int fc8080_demux_deselect_video(u8 subch_id, u8 buf_id)
{
video_subch[buf_id] = 0xff;
return BBM_OK;
}
int fc8080_demux_deselect_channel(u8 subch_id, u8 buf_id)
{
non_video_subch[subch_id & 0x3f] = 0xff;
return BBM_OK;
}