blob: 95d828475417a13484a2b29473b6bef838c1402e [file] [log] [blame]
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
* Copyright (c) 2019 Amlogic, Inc. All rights reserved.
*/
#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/wait.h>
#include <linux/string.h>
#include <linux/interrupt.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/spinlock.h>
#include <linux/fcntl.h>
#include <asm/irq.h>
#include <linux/uaccess.h>
#include <linux/poll.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <linux/spinlock.h>
#include <linux/string.h>
#include <linux/pinctrl/consumer.h>
#include <linux/reset.h>
#include <linux/amlogic/media/utils/amstream.h>
#include <linux/amlogic/cpu_version.h>
#include <linux/clk.h>
#include <linux/dvb/ca.h>
#include <linux/dvb/dmx.h>
#include <linux/version.h>
#include <linux/amlogic/aml_key.h>
#include <linux/compat.h>
#include "aml_dvb.h"
#include "dmx_log.h"
//#include "sc2_demux/dvb_reg.h"
#include "aml_dsc.h"
#include "am_key.h"
#include "sc2_demux/sc2_control.h"
#define DSC_CHANNEL_NUM 8
#define DSC_STATE_FREE 0
#define DSC_STATE_READY 3
#define DSC_STATE_GO 4
#define DSC_COUNT 8
struct dsc_channel {
struct aml_dsc *dsc;
int state;
unsigned int id;
enum ca_sc2_dsc_type dsc_type;
int index;
int index00;
int sid;
int pid;
char loop;
enum ca_sc2_algo_type algo;
struct dsc_channel *next;
};
#define MAX_DSC_PID_TABLE_NUM (64)
static struct dsc_pid_table dsc_tsn_pid_table[MAX_DSC_PID_TABLE_NUM];
static struct dsc_pid_table dsc_tsd_pid_table[MAX_DSC_PID_TABLE_NUM];
static struct dsc_pid_table dsc_tse_pid_table[MAX_DSC_PID_TABLE_NUM];
#define dprint_i(fmt, args...) \
dprintk(LOG_ERROR, debug_dsc, fmt, ## args)
#define dprint(fmt, args...) \
dprintk(LOG_ERROR, debug_dsc, "dsc:" fmt, ## args)
#define pr_dbg(fmt, args...) \
dprintk(LOG_DBG, debug_dsc, "dsc:" fmt, ## args)
MODULE_PARM_DESC(debug_dsc, "\n\t\t Enable dsc information");
static int debug_dsc;
module_param(debug_dsc, int, 0644);
static int s_init_flag;
static unsigned int gloable_ch_id;
static void _init_per_table(struct dsc_pid_table *head, int n)
{
int i = 0;
memset(head, 0, sizeof(struct dsc_pid_table) * n);
for (i = 0; i < n; i++) {
head->id = i;
head++;
}
}
static void _init_dsc_table(void)
{
if (s_init_flag == 0) {
_init_per_table(dsc_tsn_pid_table, MAX_DSC_PID_TABLE_NUM);
_init_per_table(dsc_tsd_pid_table, MAX_DSC_PID_TABLE_NUM);
_init_per_table(dsc_tse_pid_table, MAX_DSC_PID_TABLE_NUM);
s_init_flag = 1;
}
}
static struct dsc_pid_table *_get_dsc_pid_table(int index, int dsc_type)
{
struct dsc_pid_table *table;
if (index >= MAX_DSC_PID_TABLE_NUM)
return NULL;
if (dsc_type == CA_DSC_COMMON_TYPE)
table = dsc_tsn_pid_table;
else if (dsc_type == CA_DSC_TSD_TYPE)
table = dsc_tsd_pid_table;
else
table = dsc_tse_pid_table;
return &table[index];
}
static int _malloc_dsc_table_index(int dsc_type)
{
int i = 0;
struct dsc_pid_table *table;
if (dsc_type == CA_DSC_COMMON_TYPE)
table = dsc_tsn_pid_table;
else if (dsc_type == CA_DSC_TSD_TYPE)
table = dsc_tsd_pid_table;
else
table = dsc_tse_pid_table;
for (i = 0; i < MAX_DSC_PID_TABLE_NUM; i++) {
if (table[i].used == 0) {
table[i].used = 1;
break;
}
}
if (i == MAX_DSC_PID_TABLE_NUM) {
dprint("%s fail\n", __func__);
return -1;
}
return i;
}
static int _free_dsc_table_index(int index, int dsc_type)
{
struct dsc_pid_table *table;
struct dsc_pid_table *ptmp = NULL;
int id = 0;
pr_dbg("%s enter\n", __func__);
if (index >= MAX_DSC_PID_TABLE_NUM)
return -1;
if (dsc_type == CA_DSC_COMMON_TYPE)
table = dsc_tsn_pid_table;
else if (dsc_type == CA_DSC_TSD_TYPE)
table = dsc_tsd_pid_table;
else
table = dsc_tse_pid_table;
ptmp = &table[index];
id = ptmp->id;
memset(ptmp, 0, sizeof(struct dsc_pid_table));
ptmp->id = id;
dsc_config_pid_table(ptmp, dsc_type);
pr_dbg("%s exit\n", __func__);
return 0;
}
static int _add_chan_to_list(struct aml_dsc *dsc, struct dsc_channel *chan)
{
if (dsc->dsc_channels)
chan->next = dsc->dsc_channels;
dsc->dsc_channels = chan;
return 0;
}
static int _remove_chan_from_list(struct aml_dsc *dsc, struct dsc_channel *chan)
{
struct dsc_channel *ptmp = dsc->dsc_channels;
struct dsc_channel *pretmp = dsc->dsc_channels;
pr_dbg("%s\n", __func__);
while (ptmp) {
if (ptmp == chan) {
if (ptmp == dsc->dsc_channels)
dsc->dsc_channels = dsc->dsc_channels->next;
else
pretmp->next = chan->next;
}
pretmp = ptmp;
ptmp = ptmp->next;
}
return 0;
}
static struct dsc_channel *_get_chan_from_list(struct aml_dsc *dsc, int id)
{
struct dsc_channel *ptmp = dsc->dsc_channels;
pr_dbg("%s\n", __func__);
while (ptmp) {
if (ptmp->id == id)
return ptmp;
ptmp = ptmp->next;
}
return NULL;
}
static int _dsc_chan_alloc(struct aml_dsc *dsc,
unsigned int pid, int algo, int dsc_type,
unsigned int *ca_index, char loop)
{
char index = 0;
struct dsc_channel *ch = vmalloc(sizeof(*ch));
pr_dbg("%s pid:0x%0x, algo:%d, dsc_type:%d\n",
__func__, pid, algo, dsc_type);
if (!ch) {
dprint("%s vmalloc fail\n", __func__);
return -ENOMEM;
}
memset(ch, 0, sizeof(struct dsc_channel));
ch->dsc = dsc;
if (dsc->source == INPUT_DEMOD)
ch->sid = dsc->demod_sid;
else
ch->sid = dsc->local_sid;
if (loop && ch->sid >= 32)
dprint("warn: double descram sid should small than 32\n");
if (loop)
ch->sid = ch->sid >= 32 ? ch->sid : (ch->sid + 32);
ch->loop = loop;
ch->pid = pid;
ch->dsc_type = dsc_type;
ch->algo = algo;
index = _malloc_dsc_table_index(dsc_type);
if (index == -1) {
dprint("%s _malloc_dsc_table_index fail\n", __func__);
return -1;
}
ch->state = DSC_STATE_READY;
ch->index = index;
ch->index00 = -1;
ch->next = NULL;
ch->id = gloable_ch_id;
_add_chan_to_list(dsc, ch);
*ca_index = gloable_ch_id;
gloable_ch_id++;
return 0;
}
static void _dsc_chan_free(struct dsc_channel *ch)
{
struct aml_dsc *dsc = (struct aml_dsc *)ch->dsc;
pr_dbg("%s enter\n", __func__);
if (ch->state == DSC_STATE_FREE)
return;
_remove_chan_from_list(dsc, ch);
_free_dsc_table_index(ch->index, ch->dsc_type);
if (ch->index00 != -1) {
_free_dsc_table_index(ch->index00, ch->dsc_type);
ch->index00 = -1;
}
vfree(ch);
pr_dbg("%s exit\n", __func__);
}
static int _dsc_chan_set_key(struct dsc_channel *ch,
enum ca_sc2_key_type parity, u32 key_index)
{
struct dsc_pid_table *ptmp = NULL;
struct dsc_pid_table *ptmp1 = NULL;
int handle_has_iv = 0;
int type_has_iv = 0;
u32 kte;
#define KTE_MAX_NUM 256
#define IV_FLAG_OFFSET 31
#define HANDLE_TO_KTE(h) ((h) & (KTE_MAX_NUM - 1))
/*
* handle definition
| Name | Bits | Notes |
| --------- | ------ | ------------------------ |
| IV flag | 31 | 0: not IV 1: is IV |
| RFU | [20:8] | Reserved for future |
| key table | [7:0] | the real key table entry |
*/
pr_dbg("%s parity:%d, handle:%#x\n", __func__, parity, key_index);
kte = HANDLE_TO_KTE(key_index);
handle_has_iv = key_index >> IV_FLAG_OFFSET;
if (parity == CA_KEY_ODD_IV_TYPE ||
parity == CA_KEY_EVEN_IV_TYPE || parity == CA_KEY_00_IV_TYPE)
type_has_iv = 1;
if (handle_has_iv != type_has_iv) {
pr_dbg("%s parity:%d, handle:%#x, failed: not match\n",
__func__, parity, key_index);
return -1;
}
if (parity == CA_KEY_00_TYPE || parity == CA_KEY_00_IV_TYPE) {
if (ch->index00 == -1) {
ch->index00 = _malloc_dsc_table_index(ch->dsc_type);
if (ch->index00 == -1) {
dprint("%s _malloc_dsc_table_index fail\n",
__func__);
return -1;
}
}
ptmp = _get_dsc_pid_table(ch->index00, ch->dsc_type);
if (!ptmp) {
dprint("%s _get_dsc_pid_table fail\n", __func__);
return -1;
}
ptmp->scb00 = 1;
ptmp1 = _get_dsc_pid_table(ch->index, ch->dsc_type);
if (ptmp1 && ptmp1->scb_user == 1) {
ptmp->scb_as_is = ptmp1->scb_as_is;
ptmp->scb_out = ptmp1->scb_out;
} else {
ptmp->scb_as_is = 0;
ptmp->scb_out = 0;
}
} else {
ptmp = _get_dsc_pid_table(ch->index, ch->dsc_type);
if (!ptmp) {
dprint("%s _get_dsc_pid_table fail\n", __func__);
return -1;
}
ptmp->scb00 = 0;
if (ptmp->scb_user == 0) {
ptmp->scb_as_is = 0;
ptmp->scb_out = 0;
/*tse, need set scb */
if (ch->dsc_type == CA_DSC_TSE_TYPE) {
if (parity == CA_KEY_EVEN_TYPE)
ptmp->scb_out = 0x2;
if (parity == CA_KEY_ODD_TYPE)
ptmp->scb_out = 0x3;
} else {
ptmp->scb_as_is = 0;
ptmp->scb_out = 0;
}
}
}
ptmp->valid = 1;
ptmp->algo = ch->algo;
ptmp->sid = ch->sid;
ptmp->pid = ch->pid;
if (parity == CA_KEY_EVEN_TYPE)
ptmp->kte_even_00 = kte;
else if (parity == CA_KEY_EVEN_IV_TYPE)
ptmp->even_00_iv = kte;
else if (parity == CA_KEY_ODD_TYPE)
ptmp->kte_odd = kte;
else if (parity == CA_KEY_ODD_IV_TYPE)
ptmp->odd_iv = kte;
else if (parity == CA_KEY_00_IV_TYPE)
ptmp->even_00_iv = kte;
else
ptmp->kte_even_00 = kte;
if (ptmp->algo != (CA_ALGO_UNKNOWN + 1))
dsc_config_pid_table(ptmp, ch->dsc_type);
return 0;
}
static int _dsc_chan_set_scb(struct dsc_channel *ch, u8 scb_out, u8 scb_as_is)
{
struct dsc_pid_table *ptmp = NULL;
ptmp = _get_dsc_pid_table(ch->index, ch->dsc_type);
if (!ptmp) {
dprint("%s _get_dsc_pid_table fail\n", __func__);
return -1;
}
ptmp->scb_as_is = scb_as_is;
ptmp->scb_out = scb_out;
ptmp->scb_user = 1;
if (ptmp->valid)
dsc_config_pid_table(ptmp, ch->dsc_type);
if (ch->index00) {
ptmp = _get_dsc_pid_table(ch->index00, ch->dsc_type);
if (!ptmp) {
dprint("%s _get_dsc_pid_table fail\n", __func__);
return -1;
}
ptmp->scb_as_is = scb_as_is;
ptmp->scb_out = scb_out;
if (ptmp->valid)
dsc_config_pid_table(ptmp, ch->dsc_type);
}
return 0;
}
static int _dsc_chan_set_algo(struct dsc_channel *ch,
enum ca_sc2_algo_type algo)
{
struct dsc_pid_table *ptmp = NULL;
ptmp = _get_dsc_pid_table(ch->index, ch->dsc_type);
if (!ptmp) {
dprint("%s _get_dsc_pid_table fail\n", __func__);
return -1;
}
ptmp->algo = algo;
if (ptmp->valid)
dsc_config_pid_table(ptmp, ch->dsc_type);
return 0;
}
static void _dsc_reset(struct aml_dsc *dsc)
{
dprint("dsc_reset not support\n");
}
static int _dvb_dsc_open(struct inode *inode, struct file *file)
{
int err;
err = dvb_generic_open(inode, file);
if (err < 0)
return err;
return 0;
}
static int handle_desc_ext(struct aml_dsc *dsc, struct ca_sc2_descr_ex *d)
{
int ret = -EINVAL;
switch (d->cmd) {
case CA_ALLOC:{
// pr_dbg("%s CA_ALLOC\n", __func__);
if (d->params.alloc_params.algo > CA_ALGO_UNKNOWN) {
ret = -EINVAL;
break;
}
if (d->params.alloc_params.loop != 1 &&
d->params.alloc_params.loop != 0) {
dprint("alloc_params.loop:%d err\n",
d->params.alloc_params.loop);
ret = -EINVAL;
break;
}
ret = _dsc_chan_alloc(dsc,
d->params.alloc_params.pid & 0x1FFF,
d->params.alloc_params.algo + 1,
d->params.alloc_params.dsc_type,
&d->params.alloc_params.ca_index,
d->params.alloc_params.loop);
pr_dbg("%s CA_ALLOC:%d, loop:%d\n", __func__,
d->params.alloc_params.ca_index,
d->params.alloc_params.loop);
}
break;
case CA_FREE:{
struct dsc_channel *ch;
pr_dbg("%s CA_FREE:%d\n", __func__,
d->params.free_params.ca_index);
ch = _get_chan_from_list(dsc,
d->params.free_params.ca_index);
if (ch)
_dsc_chan_free(ch);
ret = 0;
}
break;
case CA_KEY:{
struct dsc_channel *ch;
// pr_dbg("%s CA_KEY ca_index:%d\n", __func__,
// d->params.alloc_params.ca_index);
ch = _get_chan_from_list(dsc,
d->params.key_params.ca_index);
if (ch) {
pr_dbg("%s KEY parity:%d, index:%d\n",
__func__,
d->params.key_params.parity,
d->params.key_params.key_index);
ret = _dsc_chan_set_key(ch,
d->params.key_params.parity,
d->params.key_params.key_index);
}
}
break;
case CA_GET_STATUS:{
int dsc_type = d->params.key_params.ca_index;
if (dsc_type > CA_DSC_TSE_TYPE) {
d->params.key_params.key_index = 0xffffffff;
ret = -1;
} else {
d->params.key_params.key_index =
dsc_get_status(dsc_type);
ret = 0;
}
}
break;
case CA_SET_SCB:{
struct dsc_channel *ch;
ch = _get_chan_from_list(dsc,
d->params.key_params.ca_index);
if (ch) {
pr_dbg("%s scb:%d, scb_as_is:%d\n",
__func__,
d->params.scb_params.ca_scb,
d->params.scb_params.ca_scb_as_is);
ret = _dsc_chan_set_scb(ch,
d->params.scb_params.ca_scb,
d->params.scb_params.ca_scb_as_is);
}
}
break;
case CA_SET_ALGO:{
struct dsc_channel *ch;
ch = _get_chan_from_list(dsc,
d->params.algo_params.ca_index);
if (ch) {
pr_dbg("%s algo:%d\n",
__func__,
d->params.algo_params.algo);
ret = _dsc_chan_set_algo(ch,
d->params.algo_params.algo + 1);
}
}
break;
default:
break;
}
return ret;
}
static int _dvb_dsc_do_ioctl(struct file *file, unsigned int cmd, void *parg)
{
struct dvb_device *dvbdev = file->private_data;
struct aml_dsc *dsc = dvbdev->priv;
int ret = -EINVAL;
if (mutex_lock_interruptible(&dsc->mutex))
return -ERESTARTSYS;
switch (cmd) {
case CA_RESET:
_dsc_reset(dsc);
break;
case CA_GET_CAP:{
struct ca_caps *cap = parg;
cap->slot_num = 1;
cap->slot_type = CA_DESCR;
cap->descr_num = DSC_CHANNEL_NUM;
cap->descr_type = 0;
break;
}
case CA_GET_SLOT_INFO:{
struct ca_slot_info *slot = parg;
slot->num = 1;
slot->type = CA_DESCR;
slot->flags = 0;
break;
}
case CA_GET_DESCR_INFO:{
struct ca_descr_info *descr = parg;
descr->num = DSC_CHANNEL_NUM;
descr->type = 0;
break;
}
case CA_SC2_SET_DESCR_EX:{
ret =
handle_desc_ext(dsc,
(struct ca_sc2_descr_ex *)parg);
break;
}
}
mutex_unlock(&dsc->mutex);
return ret;
}
static int _dvb_dsc_usercopy(struct file *file,
unsigned int cmd, unsigned long arg,
int (*func)(struct file *file,
unsigned int cmd, void *arg))
{
char sbuf[128];
void *mbuf = NULL;
void *parg = NULL;
int err = -EINVAL;
/* Copy arguments into temp kernel buffer */
switch (_IOC_DIR(cmd)) {
case _IOC_NONE:
/*
* For this command, the pointer is actually an integer
* argument.
*/
parg = (void *)arg;
break;
case _IOC_READ: /* some v4l ioctls are marked wrong ... */
case _IOC_WRITE:
case (_IOC_WRITE | _IOC_READ):
if (_IOC_SIZE(cmd) <= sizeof(sbuf)) {
parg = sbuf;
} else {
/* too big to allocate from stack */
mbuf = kmalloc(_IOC_SIZE(cmd), GFP_KERNEL);
if (!mbuf)
return -ENOMEM;
parg = mbuf;
}
err = -EFAULT;
if (copy_from_user(parg, (void __user *)arg, _IOC_SIZE(cmd)))
goto out;
break;
}
/* call driver */
err = func(file, cmd, parg);
if (err == -ENOIOCTLCMD)
err = -ENOTTY;
if (err < 0)
goto out;
/* Copy results into user buffer */
switch (_IOC_DIR(cmd)) {
case _IOC_READ:
case (_IOC_WRITE | _IOC_READ):
if (copy_to_user((void __user *)arg, parg, _IOC_SIZE(cmd)))
err = -EFAULT;
break;
}
out:
kfree(mbuf);
return err;
}
static long _dvb_dsc_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
return _dvb_dsc_usercopy(file, cmd, arg, _dvb_dsc_do_ioctl);
}
static int _dvb_dsc_release(struct inode *inode, struct file *file)
{
struct dvb_device *dvbdev = file->private_data;
struct aml_dsc *dsc = dvbdev->priv;
struct dsc_channel *ptmp = dsc->dsc_channels;
struct dsc_channel *ch;
if (mutex_lock_interruptible(&dsc->mutex))
return -ERESTARTSYS;
while (ptmp) {
ch = ptmp;
ptmp = ptmp->next;
if (ch)
_dsc_chan_free(ch);
}
mutex_unlock(&dsc->mutex);
dvb_generic_release(inode, file);
return 0;
}
#ifdef CONFIG_COMPAT
static long _dvb_dsc_compat_ioctl(struct file *filp,
unsigned int cmd, unsigned long args)
{
unsigned long ret;
args = (unsigned long)compat_ptr(args);
ret = _dvb_dsc_ioctl(filp, cmd, args);
return ret;
}
#endif
static const struct file_operations dvb_dsc_fops = {
.owner = THIS_MODULE,
.read = NULL,
.write = NULL,
.unlocked_ioctl = _dvb_dsc_ioctl,
.open = _dvb_dsc_open,
.release = _dvb_dsc_release,
.poll = NULL,
#ifdef CONFIG_COMPAT
.compat_ioctl = _dvb_dsc_compat_ioctl,
#endif
};
static struct dvb_device dvbdev_dsc = {
.priv = NULL,
.users = 1,
.readers = 1,
.writers = 1,
.fops = &dvb_dsc_fops,
};
int dsc_init(struct aml_dsc *dsc, struct dvb_adapter *dvb_adapter)
{
_init_dsc_table();
/*Register descrambler device */
return dvb_register_device(dvb_adapter, &dsc->dev,
&dvbdev_dsc, dsc, DVB_DEVICE_CA, 0);
}
void dsc_release(struct aml_dsc *dsc)
{
if (dsc->dev) {
dvb_unregister_device(dsc->dev);
dsc->dev = NULL;
}
}
int dsc_set_source(int id, int source)
{
struct aml_dvb *advb = aml_get_dvb_device();
advb->dsc[id].source = source;
return 0;
}
int dsc_set_sid(int id, int source, int sid)
{
struct aml_dvb *advb = aml_get_dvb_device();
if (source == INPUT_DEMOD)
advb->dsc[id].demod_sid = sid;
else
advb->dsc[id].local_sid = sid;
return 0;
}
static char *get_algo_str(int algo)
{
char *str;
switch (algo) {
case CA_ALGO_AES_ECB_CLR_END:
case CA_ALGO_AES_ECB_CLR_FRONT:
str = "aes_ecb";
break;
case CA_ALGO_AES_CBC_CLR_END:
case CA_ALGO_AES_CBC_IDSA:
str = "aes_cbc";
break;
case CA_ALGO_CSA2:
str = "csa2";
break;
case CA_ALGO_DES_SCTE41:
case CA_ALGO_DES_SCTE52:
str = "des";
break;
case CA_ALGO_TDES_ECB_CLR_END:
str = "tdes";
break;
case CA_ALGO_CPCM_LSA_MDI_CBC:
case CA_ALGO_CPCM_LSA_MDD_CBC:
str = "cpcm";
break;
case CA_ALGO_CSA3:
str = "csa3";
break;
case CA_ALGO_ASA:
case CA_ALGO_ASA_LIGHT:
str = "asa";
break;
case CA_ALGO_S17_ECB_CLR_END:
case CA_ALGO_S17_ECB_CTS:
str = "s17";
break;
default:
str = "none";
break;
}
return str;
}
int dsc_dump_info(char *buf)
{
int r, total = 0;
int i = 0;
struct aml_dvb *dvb = aml_get_dvb_device();
struct aml_dsc *dsc;
struct dsc_channel *chans;
struct dsc_pid_table *ptmp;
int have_show_n = 0;
int have_show_d = 0;
int have_show_e = 0;
unsigned int status;
char kte = 0;
int pid = 0;
char sid = 0;
char err = 0;
r = sprintf(buf, "\n pay attation: dsc connected to %s\n\n",
dvb->dsc_pipeline ? "demod" : "local");
buf += r;
total += r;
for (i = 0; i < DSC_DEV_COUNT; i++) {
dsc = &dvb->dsc[i];
if (!dsc->dev)
continue;
if (mutex_lock_interruptible(&dsc->mutex))
return -ERESTARTSYS;
r = sprintf(buf, "dsc%d source:%s ", i,
dsc->source == INPUT_DEMOD ? "input_demod" :
(dsc->source == INPUT_LOCAL ?
"input_local" : "input_local_sec"));
buf += r;
total += r;
r = sprintf(buf, "demod_sid:0x%0x local_sid:0x%0x\n",
dsc->demod_sid, dsc->local_sid);
buf += r;
total += r;
/*add dsc status*/
chans = dsc->dsc_channels;
while (chans) {
status = dsc_get_status(chans->dsc_type);
kte = status & 0xff;
pid = (status >> 8) & 0x1FFF;
sid = (status >> 24) & 0x3F;
err = (status >> 30) & 0x3;
r = 0;
if (chans->dsc_type == 0 && have_show_n == 0) {
r = sprintf(buf, "tsn status: kte:%d, ", kte);
buf += r;
total += r;
r = sprintf(buf, "pid:0x%0x, sid:0x%0x, err:%d\n",
pid, sid, err);
buf += r;
total += r;
have_show_n = 1;
} else if (chans->dsc_type == 1 && have_show_d == 0) {
r = sprintf(buf, "tsd status: kte:%d, ", kte);
buf += r;
total += r;
r = sprintf(buf, "pid:0x%0x, sid:0x%0x, err:%d\n",
pid, sid, err);
buf += r;
total += r;
have_show_d = 1;
} else if (chans->dsc_type == 2 && have_show_e == 0) {
r = sprintf(buf, "tse status: kte:%d, ", kte);
buf += r;
total += r;
r = sprintf(buf, "pid:0x%0x, sid:0x%0x, err:%d\n",
pid, sid, err);
buf += r;
total += r;
have_show_e = 1;
}
chans = chans->next;
}
chans = dsc->dsc_channels;
while (chans) {
r = sprintf(buf, " chan_id:%d module:ts%s ",
chans->id, (chans->dsc_type == 0 ? "n" :
(chans->dsc_type ==
1 ? "d" : "e")));
buf += r;
total += r;
r = sprintf(buf, "pid:0x%0x algo:%s ",
chans->pid, get_algo_str(chans->algo - 1));
buf += r;
total += r;
r = sprintf(buf, "slot:%d, 00_slot:%d, loop:%d\n",
chans->index, chans->index00, chans->loop);
buf += r;
total += r;
if (chans->index != -1) {
ptmp =
_get_dsc_pid_table(chans->index,
chans->dsc_type);
r = sprintf(buf,
" slot:%d, even:%d, even iv:%d, odd:%d odd iv:%d\n",
chans->index, ptmp->kte_even_00,
ptmp->even_00_iv, ptmp->kte_odd,
ptmp->odd_iv);
buf += r;
total += r;
}
if (chans->index00 != -1) {
ptmp =
_get_dsc_pid_table(chans->index00,
chans->dsc_type);
r = sprintf(buf, " slot:%d, 00:%d, 00 iv:%d\n",
chans->index, ptmp->kte_even_00,
ptmp->even_00_iv);
buf += r;
total += r;
}
chans = chans->next;
}
mutex_unlock(&dvb->dsc[i].mutex);
}
return total;
}