📄 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/rwsem.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 5#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; /* semaphore for syncing access to slot structure */ struct rw_semaphore sem; /* 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 */ int exit:1; /* Flag indicating if the CA device is open */ int open:1; /* Flag indicating the thread should wake up now */ 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
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -