📄 dvb_ca_en50221.c
字号:
/* * dvb_ca.c: generic DVB functions for EN50221 CAM interfaces * * Copyright (C) 2004 Andrew de Quincey * * Parts of this file were based on sources as follows: * * Copyright (C) 2003 Ralph Metzler <rjkm@metzlerbros.de> * * based on code: * * Copyright (C) 1999-2002 Ralph Metzler * & Marcus Metzler for convergence integrated media GmbH * * 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, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * Or, point your browser to http://www.gnu.org/copyleft/gpl.html */#include <linux/errno.h>#include <linux/slab.h>#include <linux/list.h>#include <linux/module.h>#include <linux/moduleparam.h>#include <linux/vmalloc.h>#include <linux/delay.h>#include <linux/spinlock.h>#include <linux/sched.h>#include "dvb_ca_en50221.h"#include "dvb_ringbuffer.h"static int dvb_ca_en50221_debug;module_param_named(cam_debug, dvb_ca_en50221_debug, int, 0644);MODULE_PARM_DESC(cam_debug, "enable verbose debug messages");#define dprintk if (dvb_ca_en50221_debug) printk#define INIT_TIMEOUT_SECS 10#define HOST_LINK_BUF_SIZE 0x200#define RX_BUFFER_SIZE 65535#define MAX_RX_PACKETS_PER_ITERATION 10#define CTRLIF_DATA 0#define CTRLIF_COMMAND 1#define CTRLIF_STATUS 1#define CTRLIF_SIZE_LOW 2#define CTRLIF_SIZE_HIGH 3#define CMDREG_HC 1 /* Host control */#define CMDREG_SW 2 /* Size write */#define CMDREG_SR 4 /* Size read */#define CMDREG_RS 8 /* Reset interface */#define CMDREG_FRIE 0x40 /* Enable FR interrupt */#define CMDREG_DAIE 0x80 /* Enable DA interrupt */#define IRQEN (CMDREG_DAIE)#define STATUSREG_RE 1 /* read error */#define STATUSREG_WE 2 /* write error */#define STATUSREG_FR 0x40 /* module free */#define STATUSREG_DA 0x80 /* data available */#define STATUSREG_TXERR (STATUSREG_RE|STATUSREG_WE) /* general transfer error */#define DVB_CA_SLOTSTATE_NONE 0#define DVB_CA_SLOTSTATE_UNINITIALISED 1#define DVB_CA_SLOTSTATE_RUNNING 2#define DVB_CA_SLOTSTATE_INVALID 3#define DVB_CA_SLOTSTATE_WAITREADY 4#define DVB_CA_SLOTSTATE_VALIDATE 5#define DVB_CA_SLOTSTATE_WAITFR 6#define DVB_CA_SLOTSTATE_LINKINIT 7/* Information on a CA slot */struct dvb_ca_slot { /* current state of the CAM */ int slot_state; /* Number of CAMCHANGES that have occurred since last processing */ atomic_t camchange_count; /* Type of last CAMCHANGE */ int camchange_type; /* base address of CAM config */ u32 config_base; /* value to write into Config Control register */ u8 config_option; /* if 1, the CAM supports DA IRQs */ u8 da_irq_supported:1; /* size of the buffer to use when talking to the CAM */ int link_buf_size; /* buffer for incoming packets */ struct dvb_ringbuffer rx_buffer; /* timer used during various states of the slot */ unsigned long timeout;};/* Private CA-interface information */struct dvb_ca_private { /* pointer back to the public data structure */ struct dvb_ca_en50221 *pub; /* the DVB device */ struct dvb_device *dvbdev; /* Flags describing the interface (DVB_CA_FLAG_*) */ u32 flags; /* number of slots supported by this CA interface */ unsigned int slot_count; /* information on each slot */ struct dvb_ca_slot *slot_info; /* wait queues for read() and write() operations */ wait_queue_head_t wait_queue; /* PID of the monitoring thread */ pid_t thread_pid; /* Wait queue used when shutting thread down */ wait_queue_head_t thread_queue; /* Flag indicating when thread should exit */ unsigned int exit:1; /* Flag indicating if the CA device is open */ unsigned int open:1; /* Flag indicating the thread should wake up now */ unsigned int wakeup:1; /* Delay the main thread should use */ unsigned long delay; /* Slot to start looking for data to read from in the next user-space read operation */ int next_read_slot;};static void dvb_ca_en50221_thread_wakeup(struct dvb_ca_private *ca);static int dvb_ca_en50221_read_data(struct dvb_ca_private *ca, int slot, u8 * ebuf, int ecount);static int dvb_ca_en50221_write_data(struct dvb_ca_private *ca, int slot, u8 * ebuf, int ecount);/** * Safely find needle in haystack. * * @param haystack Buffer to look in. * @param hlen Number of bytes in haystack. * @param needle Buffer to find. * @param nlen Number of bytes in needle. * @return Pointer into haystack needle was found at, or NULL if not found. */static u8 *findstr(u8 * haystack, int hlen, u8 * needle, int nlen){ int i; if (hlen < nlen) return NULL; for (i = 0; i <= hlen - nlen; i++) { if (!strncmp(haystack + i, needle, nlen)) return haystack + i; } return NULL;}/* ******************************************************************************** *//* EN50221 physical interface functions *//** * Check CAM status. */static int dvb_ca_en50221_check_camstatus(struct dvb_ca_private *ca, int slot){ int slot_status; int cam_present_now; int cam_changed; /* IRQ mode */ if (ca->flags & DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE) { return (atomic_read(&ca->slot_info[slot].camchange_count) != 0); } /* poll mode */ slot_status = ca->pub->poll_slot_status(ca->pub, slot, ca->open); cam_present_now = (slot_status & DVB_CA_EN50221_POLL_CAM_PRESENT) ? 1 : 0; cam_changed = (slot_status & DVB_CA_EN50221_POLL_CAM_CHANGED) ? 1 : 0; if (!cam_changed) { int cam_present_old = (ca->slot_info[slot].slot_state != DVB_CA_SLOTSTATE_NONE); cam_changed = (cam_present_now != cam_present_old); } if (cam_changed) { if (!cam_present_now) { ca->slot_info[slot].camchange_type = DVB_CA_EN50221_CAMCHANGE_REMOVED; } else { ca->slot_info[slot].camchange_type = DVB_CA_EN50221_CAMCHANGE_INSERTED; } atomic_set(&ca->slot_info[slot].camchange_count, 1); } else { if ((ca->slot_info[slot].slot_state == DVB_CA_SLOTSTATE_WAITREADY) && (slot_status & DVB_CA_EN50221_POLL_CAM_READY)) { // move to validate state if reset is completed ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_VALIDATE; } } return cam_changed;}/** * Wait for flags to become set on the STATUS register on a CAM interface, * checking for errors and timeout. * * @param ca CA instance. * @param slot Slot on interface. * @param waitfor Flags to wait for. * @param timeout_ms Timeout in milliseconds. * * @return 0 on success, nonzero on error. */static int dvb_ca_en50221_wait_if_status(struct dvb_ca_private *ca, int slot, u8 waitfor, int timeout_hz){ unsigned long timeout; unsigned long start; dprintk("%s\n", __FUNCTION__); /* loop until timeout elapsed */ start = jiffies; timeout = jiffies + timeout_hz; while (1) { /* read the status and check for error */ int res = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS); if (res < 0) return -EIO; /* if we got the flags, it was successful! */ if (res & waitfor) { dprintk("%s succeeded timeout:%lu\n", __FUNCTION__, jiffies - start); return 0; } /* check for timeout */ if (time_after(jiffies, timeout)) { break; } /* wait for a bit */ msleep(1); } dprintk("%s failed timeout:%lu\n", __FUNCTION__, jiffies - start); /* if we get here, we've timed out */ return -ETIMEDOUT;}/** * Initialise the link layer connection to a CAM. * * @param ca CA instance. * @param slot Slot id. * * @return 0 on success, nonzero on failure. */static int dvb_ca_en50221_link_init(struct dvb_ca_private *ca, int slot){ int ret; int buf_size; u8 buf[2]; dprintk("%s\n", __FUNCTION__); /* we'll be determining these during this function */ ca->slot_info[slot].da_irq_supported = 0; /* set the host link buffer size temporarily. it will be overwritten with the * real negotiated size later. */ ca->slot_info[slot].link_buf_size = 2; /* read the buffer size from the CAM */ if ((ret = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, IRQEN | CMDREG_SR)) != 0) return ret; if ((ret = dvb_ca_en50221_wait_if_status(ca, slot, STATUSREG_DA, HZ / 10)) != 0) return ret; if ((ret = dvb_ca_en50221_read_data(ca, slot, buf, 2)) != 2) return -EIO; if ((ret = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, IRQEN)) != 0) return ret; /* store it, and choose the minimum of our buffer and the CAM's buffer size */ buf_size = (buf[0] << 8) | buf[1]; if (buf_size > HOST_LINK_BUF_SIZE) buf_size = HOST_LINK_BUF_SIZE; ca->slot_info[slot].link_buf_size = buf_size; buf[0] = buf_size >> 8; buf[1] = buf_size & 0xff; dprintk("Chosen link buffer size of %i\n", buf_size); /* write the buffer size to the CAM */ if ((ret = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, IRQEN | CMDREG_SW)) != 0) return ret; if ((ret = dvb_ca_en50221_wait_if_status(ca, slot, STATUSREG_FR, HZ / 10)) != 0) return ret; if ((ret = dvb_ca_en50221_write_data(ca, slot, buf, 2)) != 2) return -EIO; if ((ret = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, IRQEN)) != 0) return ret; /* success */ return 0;}/** * Read a tuple from attribute memory. * * @param ca CA instance. * @param slot Slot id. * @param address Address to read from. Updated. * @param tupleType Tuple id byte. Updated. * @param tupleLength Tuple length. Updated. * @param tuple Dest buffer for tuple (must be 256 bytes). Updated. * * @return 0 on success, nonzero on error. */static int dvb_ca_en50221_read_tuple(struct dvb_ca_private *ca, int slot, int *address, int *tupleType, int *tupleLength, u8 * tuple){ int i; int _tupleType; int _tupleLength; int _address = *address; /* grab the next tuple length and type */ if ((_tupleType = ca->pub->read_attribute_mem(ca->pub, slot, _address)) < 0) return _tupleType; if (_tupleType == 0xff) { dprintk("END OF CHAIN TUPLE type:0x%x\n", _tupleType); *address += 2; *tupleType = _tupleType; *tupleLength = 0; return 0; } if ((_tupleLength = ca->pub->read_attribute_mem(ca->pub, slot, _address + 2)) < 0) return _tupleLength; _address += 4; dprintk("TUPLE type:0x%x length:%i\n", _tupleType, _tupleLength); /* read in the whole tuple */ for (i = 0; i < _tupleLength; i++) { tuple[i] = ca->pub->read_attribute_mem(ca->pub, slot, _address + (i * 2)); dprintk(" 0x%02x: 0x%02x %c\n", i, tuple[i] & 0xff, ((tuple[i] > 31) && (tuple[i] < 127)) ? tuple[i] : '.'); } _address += (_tupleLength * 2); // success *tupleType = _tupleType; *tupleLength = _tupleLength; *address = _address; return 0;}/** * Parse attribute memory of a CAM module, extracting Config register, and checking * it is a DVB CAM module. * * @param ca CA instance. * @param slot Slot id. * * @return 0 on success, <0 on failure. */static int dvb_ca_en50221_parse_attributes(struct dvb_ca_private *ca, int slot){ int address = 0; int tupleLength; int tupleType; u8 tuple[257]; char *dvb_str; int rasz; int status; int got_cftableentry = 0; int end_chain = 0; int i; u16 manfid = 0; u16 devid = 0; // CISTPL_DEVICE_0A if ((status = dvb_ca_en50221_read_tuple(ca, slot, &address, &tupleType, &tupleLength, tuple)) < 0) return status; if (tupleType != 0x1D) return -EINVAL; // CISTPL_DEVICE_0C if ((status = dvb_ca_en50221_read_tuple(ca, slot, &address, &tupleType, &tupleLength, tuple)) < 0) return status; if (tupleType != 0x1C) return -EINVAL; // CISTPL_VERS_1 if ((status = dvb_ca_en50221_read_tuple(ca, slot, &address, &tupleType, &tupleLength, tuple)) < 0) return status; if (tupleType != 0x15) return -EINVAL; // CISTPL_MANFID if ((status = dvb_ca_en50221_read_tuple(ca, slot, &address, &tupleType, &tupleLength, tuple)) < 0) return status; if (tupleType != 0x20) return -EINVAL; if (tupleLength != 4) return -EINVAL; manfid = (tuple[1] << 8) | tuple[0]; devid = (tuple[3] << 8) | tuple[2]; // CISTPL_CONFIG if ((status = dvb_ca_en50221_read_tuple(ca, slot, &address, &tupleType, &tupleLength, tuple)) < 0) return status; if (tupleType != 0x1A) return -EINVAL; if (tupleLength < 3) return -EINVAL; /* extract the configbase */ rasz = tuple[0] & 3; if (tupleLength < (3 + rasz + 14)) return -EINVAL; ca->slot_info[slot].config_base = 0; for (i = 0; i < rasz + 1; i++) { ca->slot_info[slot].config_base |= (tuple[2 + i] << (8 * i)); } /* check it contains the correct DVB string */ dvb_str = findstr(tuple, tupleLength, "DVB_CI_V", 8); if (dvb_str == NULL) return -EINVAL; if (tupleLength < ((dvb_str - (char *) tuple) + 12)) return -EINVAL; /* is it a version we support? */ if (strncmp(dvb_str + 8, "1.00", 4)) { printk("dvb_ca adapter %d: Unsupported DVB CAM module version %c%c%c%c\n", ca->dvbdev->adapter->num, dvb_str[8], dvb_str[9], dvb_str[10], dvb_str[11]); return -EINVAL; } /* process the CFTABLE_ENTRY tuples, and any after those */ while ((!end_chain) && (address < 0x1000)) { if ((status = dvb_ca_en50221_read_tuple(ca, slot, &address, &tupleType, &tupleLength, tuple)) < 0) return status; switch (tupleType) { case 0x1B: // CISTPL_CFTABLE_ENTRY if (tupleLength < (2 + 11 + 17)) break; /* if we've already parsed one, just use it */ if (got_cftableentry) break; /* get the config option */ ca->slot_info[slot].config_option = tuple[0] & 0x3f; /* OK, check it contains the correct strings */ if ((findstr(tuple, tupleLength, "DVB_HOST", 8) == NULL) || (findstr(tuple, tupleLength, "DVB_CI_MODULE", 13) == NULL)) break; got_cftableentry = 1; break; case 0x14: // CISTPL_NO_LINK break; case 0xFF: // CISTPL_END end_chain = 1; break; default: /* Unknown tuple type - just skip this tuple and move to the next one */ dprintk("dvb_ca: Skipping unknown tuple type:0x%x length:0x%x\n", tupleType, tupleLength); break; } } if ((address > 0x1000) || (!got_cftableentry)) return -EINVAL; dprintk("Valid DVB CAM detected MANID:%x DEVID:%x CONFIGBASE:0x%x CONFIGOPTION:0x%x\n", manfid, devid, ca->slot_info[slot].config_base, ca->slot_info[slot].config_option); // success! return 0;}/** * Set CAM's configoption correctly. * * @param ca CA instance. * @param slot Slot containing the CAM. */static int dvb_ca_en50221_set_configoption(struct dvb_ca_private *ca, int slot){ int configoption; dprintk("%s\n", __FUNCTION__); /* set the config option */ ca->pub->write_attribute_mem(ca->pub, slot, ca->slot_info[slot].config_base, ca->slot_info[slot].config_option); /* check it */ configoption = ca->pub->read_attribute_mem(ca->pub, slot, ca->slot_info[slot].config_base); dprintk("Set configoption 0x%x, read configoption 0x%x\n", ca->slot_info[slot].config_option, configoption & 0x3f); /* fine! */ return 0;}/** * This function talks to an EN50221 CAM control interface. It reads a buffer of * data from the CAM. The data can either be stored in a supplied buffer, or * automatically be added to the slot's rx_buffer. * * @param ca CA instance. * @param slot Slot to read from. * @param ebuf If non-NULL, the data will be written to this buffer. If NULL, * the data will be added into the buffering system as a normal fragment. * @param ecount Size of ebuf. Ignored if ebuf is NULL. * * @return Number of bytes read, or < 0 on error */static int dvb_ca_en50221_read_data(struct dvb_ca_private *ca, int slot, u8 * ebuf, int ecount){ int bytes_read;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -