📄 dvb_ca.c
字号:
/* * dvb_ca.c: generic DVB CA functions * * Copyright (C) 2004 Andrew de Quincey * * 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 *//* * In the following, public functions always do locking of slots, private functions do not * necessarily do locking of slots - they may expect the caller to have locked */#include <linux/errno.h>#include <linux/slab.h>#include <linux/list.h>#include <linux/module.h>#include <asm/semaphore.h>#include "dvb_ca.h"#include "dvb_functions.h"static int dvb_ca_debug = 0;#define dprintk if (dvb_ca_debug) printk#define HOST_LINK_BUF_SIZE 0x200#define RX_BUFFER_SIZE 65540#define TX_BUFFER_SIZE 65540#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 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 */static int dvb_ca_en50221_link_init(struct dvb_ca* ca, int slot);static u8* findstr(u8* haystack, int hlen, u8* needle, int nlen) { int i; for(i=0; i<= hlen - nlen; i++) { if (!strncmp(haystack+i, needle, nlen)) return haystack+i; } return NULL;}/* ******************************************************************************** *//* Modified version of wait_event_interruptible so we can check for errors */#define __dvb_ca_wait_event_interruptible(wq, condition, ret) \do { \ wait_queue_t __wait; \ init_waitqueue_entry(&__wait, current); \ \ add_wait_queue(&wq, &__wait); \ for (;;) { \ set_current_state(TASK_INTERRUPTIBLE); \ if (ret = condition) \ break; \ if (!signal_pending(current)) { \ schedule(); \ continue; \ } \ ret = -ERESTARTSYS; \ break; \ } \ current->state = TASK_RUNNING; \ remove_wait_queue(&wq, &__wait); \} while (0)#define dvb_ca_wait_event_interruptible(wq, condition) \({ \ int __ret = 0; \ if (!(condition)) \ __dvb_ca_wait_event_interruptible(wq, condition, __ret); \ __ret; \})/* ******************************************************************************** *//* Functions for controlling access to slots *//** * Safely increment the usage counter for a CA slot. * * @param ca CA instance. * @param slot Slot concerned. * * @return 0 on success, <0 on failure. * */int dvb_ca_slot_acquire(struct dvb_ca* ca, int slot) { int status; if (status = down_interruptible(&ca->slot_info[slot].sem)) return status; if (!ca->slot_info[slot].cam_present) { up(&ca->slot_info[slot].sem); return -EIO; } ca->slot_info[slot].usage_counter++; up(&ca->slot_info[slot].sem); return 0;}/** * Safely decrement the usage counter for a CA slot. * * @param ca CA instance. * @param slot Slot concerned. * * @return 0 on success, <0 on failure. * */int dvb_ca_slot_release(struct dvb_ca* ca, int slot) { int status; if (status = down_interruptible(&ca->slot_info[slot].sem)) return status; ca->slot_info[slot].usage_counter--; up(&ca->slot_info[slot].sem); return(0);}/** * Acquire a slot exclusively. The slot semaphore will be left locked on successful * exit of this function. * * @param ca CA instance. * @param slot Slot concerned. * * @return 0 on success, <0 on failure. * */int dvb_ca_slot_acquire_exclusive(struct dvb_ca* ca, int slot) { int status; while(1) { /* lock the slot */ if (status = down_interruptible(&ca->slot_info[slot].sem)) return status; /* if there is no CAM, exit straight away */ if (!ca->slot_info[slot].cam_present) { up(&ca->slot_info[slot].sem); return -EIO; } /* if there are no other users of the slot, we've finished */ if (ca->slot_info[slot].usage_counter == 0) { ca->slot_info[slot].usage_counter++; break; } /* sleep a bit and try again */ up(&ca->slot_info[slot].sem); dvb_delay(1); } /* DO NOT UNLOCK! */ return 0;}/** * Release an exclusively owned slot. The slot semaphore will be unlocked by this function. * * @param ca CA instance. * @param slot Slot concerned. * * @return 0 on success, <0 on failure. * */int dvb_ca_slot_release_exclusive(struct dvb_ca* ca, int slot) { ca->slot_info[slot].usage_counter--; up(&ca->slot_info[slot].sem); return(0);}/* ******************************************************************************** *//* Functions for link level connection_ids *//** * Internal function to destroy and unlink a dvb_ca_connection structure. * * @param cacon The structure to destroy. * @param unlink If 1, it will be unlinked from the list. */static void dvb_ca_connection_destroy(struct dvb_ca_connection* cacon, int unlink) { /* unlink it from the list. */ if (unlink) list_del(&cacon->connection); /* destroy buffers etc */ if (cacon->rx_buffer.data) vfree(cacon->rx_buffer.data); if (cacon->tx_buffer.data) vfree(cacon->tx_buffer.data); vfree(cacon);}/** * Get or create a dvb_ca_connection structure for a particular slot/connection_id. * * @param ca CA device instance. * @param slot Slot id. * @param connection_id Connection id to retrieve structure for. * @param create If 1, and a connection struct was not found, a new one will be created. * * @return A dvb_ca_connection structure, or NULL. */static struct dvb_ca_connection* dvb_ca_connection_get(struct dvb_ca* ca, int slot, int connection_id, int create) { struct dvb_ca_connection* cacon; u8* mem; /* is there already a record for this connection_id */ list_for_each_entry(cacon, &ca->slot_info[slot].connections, next) { /* if we found it, return it immediately */ if (cacon->connection_id == connection_id) { cacon->last_used = jiffies; return cacon; } } /* if we get here, and we've been asked not to create a structure, just return NULL */ if (!create) { return NULL; } /* did not find it => create a new connection */ cacon = vmalloc(sizeof(struct dvb_ca_connection)); if (cacon == NULL) { goto error; } /* setup the structure */ memset(cacon, 0, sizeof(struct dvb_ca_connection)); cacon->connection_id = connection_id; cacon->rx_partial_pkt = -1; cacon->tx_partial_pkt_size = -1; cacon->last_used = jiffies; if (!(mem = vmalloc(RX_BUFFER_SIZE))) { goto error; } dvb_ringbuffer_init(&cacon->rx_buffer, mem, RX_BUFFER_SIZE); if (!(mem = vmalloc(TX_BUFFER_SIZE))) { goto error; } dvb_ringbuffer_init(&cacon->tx_buffer, mem, TX_BUFFER_SIZE); /* success */ list_add_tail(&cacon->next, &ca->slot_info[slot].connections); return cacon;error: if (cacon != NULL) { dvb_ca_connection_destroy(cacon, 0); } return NULL;}/* ******************************************************************************** *//* EN50221 physical interface functions *//** * 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* ca, int slot, u8 waitfor, int timeout_ms) { unsigned long timeout; /* loop until timeout elapsed */ timeout = jiffies + ((HZ * timeout_ms) / 1000); while(1) { /* read the status and check for error */ int res = ca->read_cam_control(ca, slot, CTRLIF_STATUS); if (res < 0) return -EIO; /* if we got the flags, it was successful! */ if (res & waitfor) { return 0; } /* check for timeout */ if (time_after(jiffies,timeout)) { break; } /* wait for a bit */ mdelay(100); // FIXME: is this timeout good? need to investigate } /* if we get here, we've timed out */ return -ETIMEDOUT;}/** * Reset the CAM control interface. * * @param ca CA instance. * @param slot Slot id. * * @return 0 on success, nonzero on failure. */static int dvb_ca_en50221_reset_if(struct dvb_ca* ca, int slot) { int ret; /* reset the interface and wait for FR to be set */ if (ret = ca->write_cam_control(ca, slot, CTRLIF_COMMAND, CMDREG_RS)) return ret; if (ret = dvb_ca_cam_wait_if_status(ca, slot, STATUSREG_FR, 2)) return ret;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -