11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * dvb_ca.c: generic DVB functions for EN50221 CAM interfaces
31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Copyright (C) 2004 Andrew de Quincey
51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Parts of this file were based on sources as follows:
71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Copyright (C) 2003 Ralph Metzler <rjkm@metzlerbros.de>
91da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * based on code:
111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Copyright (C) 1999-2002 Ralph  Metzler
131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *                       & Marcus Metzler for convergence integrated media GmbH
141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This program is free software; you can redistribute it and/or
161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * modify it under the terms of the GNU General Public License
171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * as published by the Free Software Foundation; either version 2
181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * of the License, or (at your option) any later version.
191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This program is distributed in the hope that it will be useful,
211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * but WITHOUT ANY WARRANTY; without even the implied warranty of
221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * GNU General Public License for more details.
241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * You should have received a copy of the GNU General Public License
261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * along with this program; if not, write to the Free Software
271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/errno.h>
321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/slab.h>
331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/list.h>
341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h>
351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/vmalloc.h>
361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/delay.h>
37ded928468407c8e08dcb6aedb91aaa97b80d5752Andrew de Quincey#include <linux/spinlock.h>
384e57b6817880946a3a78d5d8cad1ace363f7e449Tim Schmielau#include <linux/sched.h>
399320874a3e6aea7044a4a7eedeab13db990424abChristoph Hellwig#include <linux/kthread.h>
401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include "dvb_ca_en50221.h"
421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include "dvb_ringbuffer.h"
431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int dvb_ca_en50221_debug;
451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param_named(cam_debug, dvb_ca_en50221_debug, int, 0644);
471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_PARM_DESC(cam_debug, "enable verbose debug messages");
481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define dprintk if (dvb_ca_en50221_debug) printk
501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5150b447d5b70dc4021ae3b4eaf8ce98932f61a413Dominique Dumont#define INIT_TIMEOUT_SECS 10
521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define HOST_LINK_BUF_SIZE 0x200
541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define RX_BUFFER_SIZE 65535
561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define MAX_RX_PACKETS_PER_ITERATION 10
581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define CTRLIF_DATA      0
601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define CTRLIF_COMMAND   1
611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define CTRLIF_STATUS    1
621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define CTRLIF_SIZE_LOW  2
631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define CTRLIF_SIZE_HIGH 3
641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define CMDREG_HC        1	/* Host control */
661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define CMDREG_SW        2	/* Size write */
671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define CMDREG_SR        4	/* Size read */
681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define CMDREG_RS        8	/* Reset interface */
691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define CMDREG_FRIE   0x40	/* Enable FR interrupt */
701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define CMDREG_DAIE   0x80	/* Enable DA interrupt */
711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define IRQEN (CMDREG_DAIE)
721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define STATUSREG_RE     1	/* read error */
741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define STATUSREG_WE     2	/* write error */
751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define STATUSREG_FR  0x40	/* module free */
761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define STATUSREG_DA  0x80	/* data available */
771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define STATUSREG_TXERR (STATUSREG_RE|STATUSREG_WE)	/* general transfer error */
781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define DVB_CA_SLOTSTATE_NONE           0
811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define DVB_CA_SLOTSTATE_UNINITIALISED  1
821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define DVB_CA_SLOTSTATE_RUNNING        2
831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define DVB_CA_SLOTSTATE_INVALID        3
841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define DVB_CA_SLOTSTATE_WAITREADY      4
851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define DVB_CA_SLOTSTATE_VALIDATE       5
861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define DVB_CA_SLOTSTATE_WAITFR         6
871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define DVB_CA_SLOTSTATE_LINKINIT       7
881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Information on a CA slot */
911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstruct dvb_ca_slot {
921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* current state of the CAM */
941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int slot_state;
951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
96d7e43844e40e07cadc48f1733b9738659f83b38cMatthias Dahl	/* mutex used for serializing access to one CI slot */
97d7e43844e40e07cadc48f1733b9738659f83b38cMatthias Dahl	struct mutex slot_lock;
98d7e43844e40e07cadc48f1733b9738659f83b38cMatthias Dahl
991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Number of CAMCHANGES that have occurred since last processing */
1001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	atomic_t camchange_count;
1011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Type of last CAMCHANGE */
1031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int camchange_type;
1041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* base address of CAM config */
1061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u32 config_base;
1071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* value to write into Config Control register */
1091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u8 config_option;
1101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* if 1, the CAM supports DA IRQs */
1121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u8 da_irq_supported:1;
1131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* size of the buffer to use when talking to the CAM */
1151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int link_buf_size;
1161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* buffer for incoming packets */
1181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct dvb_ringbuffer rx_buffer;
1191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* timer used during various states of the slot */
1211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long timeout;
1221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
1231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Private CA-interface information */
1251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstruct dvb_ca_private {
1261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* pointer back to the public data structure */
1281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct dvb_ca_en50221 *pub;
1291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* the DVB device */
1311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct dvb_device *dvbdev;
1321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Flags describing the interface (DVB_CA_FLAG_*) */
1341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u32 flags;
1351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* number of slots supported by this CA interface */
1371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned int slot_count;
1381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* information on each slot */
1401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct dvb_ca_slot *slot_info;
1411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* wait queues for read() and write() operations */
1431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	wait_queue_head_t wait_queue;
1441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* PID of the monitoring thread */
1469320874a3e6aea7044a4a7eedeab13db990424abChristoph Hellwig	struct task_struct *thread;
1471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Flag indicating if the CA device is open */
1491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned int open:1;
1501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Flag indicating the thread should wake up now */
1521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned int wakeup:1;
1531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Delay the main thread should use */
1551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long delay;
1561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Slot to start looking for data to read from in the next user-space read operation */
1581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int next_read_slot;
1591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
1601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void dvb_ca_en50221_thread_wakeup(struct dvb_ca_private *ca);
1621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int dvb_ca_en50221_read_data(struct dvb_ca_private *ca, int slot, u8 * ebuf, int ecount);
1631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int dvb_ca_en50221_write_data(struct dvb_ca_private *ca, int slot, u8 * ebuf, int ecount);
1641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/**
1671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Safely find needle in haystack.
1681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
1691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @param haystack Buffer to look in.
1701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @param hlen Number of bytes in haystack.
1711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @param needle Buffer to find.
1721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @param nlen Number of bytes in needle.
1731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @return Pointer into haystack needle was found at, or NULL if not found.
1741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
175260f8d7c4cda79b087a182eb03e8574ba41a171eOliver Endrissstatic char *findstr(char * haystack, int hlen, char * needle, int nlen)
1761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i;
1781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (hlen < nlen)
1801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return NULL;
1811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = 0; i <= hlen - nlen; i++) {
1831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!strncmp(haystack + i, needle, nlen))
1841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return haystack + i;
1851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return NULL;
1881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* ******************************************************************************** */
1931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* EN50221 physical interface functions */
1941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/**
1971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Check CAM status.
1981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
1991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int dvb_ca_en50221_check_camstatus(struct dvb_ca_private *ca, int slot)
2001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int slot_status;
2021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int cam_present_now;
2031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int cam_changed;
2041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* IRQ mode */
2061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ca->flags & DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE) {
2071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return (atomic_read(&ca->slot_info[slot].camchange_count) != 0);
2081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* poll mode */
2111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	slot_status = ca->pub->poll_slot_status(ca->pub, slot, ca->open);
2121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	cam_present_now = (slot_status & DVB_CA_EN50221_POLL_CAM_PRESENT) ? 1 : 0;
2141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	cam_changed = (slot_status & DVB_CA_EN50221_POLL_CAM_CHANGED) ? 1 : 0;
2151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!cam_changed) {
2161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		int cam_present_old = (ca->slot_info[slot].slot_state != DVB_CA_SLOTSTATE_NONE);
2171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		cam_changed = (cam_present_now != cam_present_old);
2181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (cam_changed) {
2211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!cam_present_now) {
2221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			ca->slot_info[slot].camchange_type = DVB_CA_EN50221_CAMCHANGE_REMOVED;
2231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		} else {
2241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			ca->slot_info[slot].camchange_type = DVB_CA_EN50221_CAMCHANGE_INSERTED;
2251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
2261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		atomic_set(&ca->slot_info[slot].camchange_count, 1);
2271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else {
2281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if ((ca->slot_info[slot].slot_state == DVB_CA_SLOTSTATE_WAITREADY) &&
2291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		    (slot_status & DVB_CA_EN50221_POLL_CAM_READY)) {
2301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			// move to validate state if reset is completed
2311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_VALIDATE;
2321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
2331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return cam_changed;
2361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/**
2401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Wait for flags to become set on the STATUS register on a CAM interface,
2411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * checking for errors and timeout.
2421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
2431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @param ca CA instance.
2441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @param slot Slot on interface.
2451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @param waitfor Flags to wait for.
2461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @param timeout_ms Timeout in milliseconds.
2471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
2481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @return 0 on success, nonzero on error.
2491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
2501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int dvb_ca_en50221_wait_if_status(struct dvb_ca_private *ca, int slot,
2511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					 u8 waitfor, int timeout_hz)
2521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long timeout;
2541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long start;
2551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
25646b4f7c176a2dd4c60ddb7c80bd09ea2f3220674Harvey Harrison	dprintk("%s\n", __func__);
2571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* loop until timeout elapsed */
2591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	start = jiffies;
2601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	timeout = jiffies + timeout_hz;
2611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	while (1) {
2621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* read the status and check for error */
2631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		int res = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS);
2641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (res < 0)
2651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -EIO;
2661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* if we got the flags, it was successful! */
2681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (res & waitfor) {
26946b4f7c176a2dd4c60ddb7c80bd09ea2f3220674Harvey Harrison			dprintk("%s succeeded timeout:%lu\n", __func__, jiffies - start);
2701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return 0;
2711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
2721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* check for timeout */
2741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (time_after(jiffies, timeout)) {
2751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
2761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
2771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* wait for a bit */
2791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		msleep(1);
2801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
28246b4f7c176a2dd4c60ddb7c80bd09ea2f3220674Harvey Harrison	dprintk("%s failed timeout:%lu\n", __func__, jiffies - start);
2831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* if we get here, we've timed out */
2851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return -ETIMEDOUT;
2861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/**
2901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Initialise the link layer connection to a CAM.
2911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
2921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @param ca CA instance.
2931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @param slot Slot id.
2941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
2951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @return 0 on success, nonzero on failure.
2961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
2971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int dvb_ca_en50221_link_init(struct dvb_ca_private *ca, int slot)
2981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int ret;
3001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int buf_size;
3011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u8 buf[2];
3021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
30346b4f7c176a2dd4c60ddb7c80bd09ea2f3220674Harvey Harrison	dprintk("%s\n", __func__);
3041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* we'll be determining these during this function */
3061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ca->slot_info[slot].da_irq_supported = 0;
3071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* set the host link buffer size temporarily. it will be overwritten with the
3091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * real negotiated size later. */
3101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ca->slot_info[slot].link_buf_size = 2;
3111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* read the buffer size from the CAM */
3131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((ret = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, IRQEN | CMDREG_SR)) != 0)
3141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return ret;
3151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((ret = dvb_ca_en50221_wait_if_status(ca, slot, STATUSREG_DA, HZ / 10)) != 0)
3161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return ret;
3171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((ret = dvb_ca_en50221_read_data(ca, slot, buf, 2)) != 2)
3181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EIO;
3191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((ret = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, IRQEN)) != 0)
3201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return ret;
3211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* store it, and choose the minimum of our buffer and the CAM's buffer size */
3231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	buf_size = (buf[0] << 8) | buf[1];
3241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (buf_size > HOST_LINK_BUF_SIZE)
3251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		buf_size = HOST_LINK_BUF_SIZE;
3261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ca->slot_info[slot].link_buf_size = buf_size;
3271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	buf[0] = buf_size >> 8;
3281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	buf[1] = buf_size & 0xff;
3291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dprintk("Chosen link buffer size of %i\n", buf_size);
3301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* write the buffer size to the CAM */
3321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((ret = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, IRQEN | CMDREG_SW)) != 0)
3331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return ret;
3341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((ret = dvb_ca_en50221_wait_if_status(ca, slot, STATUSREG_FR, HZ / 10)) != 0)
3351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return ret;
3361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((ret = dvb_ca_en50221_write_data(ca, slot, buf, 2)) != 2)
3371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EIO;
3381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((ret = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, IRQEN)) != 0)
3391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return ret;
3401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* success */
3421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
3431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/**
3461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Read a tuple from attribute memory.
3471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
3481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @param ca CA instance.
3491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @param slot Slot id.
3501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @param address Address to read from. Updated.
3511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @param tupleType Tuple id byte. Updated.
3521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @param tupleLength Tuple length. Updated.
3531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @param tuple Dest buffer for tuple (must be 256 bytes). Updated.
3541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
3551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @return 0 on success, nonzero on error.
3561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
3571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int dvb_ca_en50221_read_tuple(struct dvb_ca_private *ca, int slot,
3581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				     int *address, int *tupleType, int *tupleLength, u8 * tuple)
3591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i;
3611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int _tupleType;
3621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int _tupleLength;
3631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int _address = *address;
3641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* grab the next tuple length and type */
3661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((_tupleType = ca->pub->read_attribute_mem(ca->pub, slot, _address)) < 0)
3671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return _tupleType;
3681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (_tupleType == 0xff) {
3691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dprintk("END OF CHAIN TUPLE type:0x%x\n", _tupleType);
3701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		*address += 2;
3711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		*tupleType = _tupleType;
3721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		*tupleLength = 0;
3731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 0;
3741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((_tupleLength = ca->pub->read_attribute_mem(ca->pub, slot, _address + 2)) < 0)
3761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return _tupleLength;
3771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	_address += 4;
3781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dprintk("TUPLE type:0x%x length:%i\n", _tupleType, _tupleLength);
3801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* read in the whole tuple */
3821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = 0; i < _tupleLength; i++) {
3831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		tuple[i] = ca->pub->read_attribute_mem(ca->pub, slot, _address + (i * 2));
3841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dprintk("  0x%02x: 0x%02x %c\n",
3851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			i, tuple[i] & 0xff,
3861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			((tuple[i] > 31) && (tuple[i] < 127)) ? tuple[i] : '.');
3871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	_address += (_tupleLength * 2);
3891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	// success
3911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	*tupleType = _tupleType;
3921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	*tupleLength = _tupleLength;
3931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	*address = _address;
3941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
3951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/**
3991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Parse attribute memory of a CAM module, extracting Config register, and checking
4001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * it is a DVB CAM module.
4011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
4021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @param ca CA instance.
4031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @param slot Slot id.
4041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
4051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @return 0 on success, <0 on failure.
4061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
4071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int dvb_ca_en50221_parse_attributes(struct dvb_ca_private *ca, int slot)
4081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
4091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int address = 0;
4101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int tupleLength;
4111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int tupleType;
4121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u8 tuple[257];
4131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	char *dvb_str;
4141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int rasz;
4151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int status;
4161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int got_cftableentry = 0;
4171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int end_chain = 0;
4181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i;
4191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u16 manfid = 0;
4201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u16 devid = 0;
4211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	// CISTPL_DEVICE_0A
4241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((status =
4251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	     dvb_ca_en50221_read_tuple(ca, slot, &address, &tupleType, &tupleLength, tuple)) < 0)
4261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return status;
4271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (tupleType != 0x1D)
4281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
4291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	// CISTPL_DEVICE_0C
4331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((status =
4341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	     dvb_ca_en50221_read_tuple(ca, slot, &address, &tupleType, &tupleLength, tuple)) < 0)
4351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return status;
4361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (tupleType != 0x1C)
4371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
4381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	// CISTPL_VERS_1
4421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((status =
4431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	     dvb_ca_en50221_read_tuple(ca, slot, &address, &tupleType, &tupleLength, tuple)) < 0)
4441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return status;
4451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (tupleType != 0x15)
4461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
4471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	// CISTPL_MANFID
4511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((status = dvb_ca_en50221_read_tuple(ca, slot, &address, &tupleType,
4521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						&tupleLength, tuple)) < 0)
4531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return status;
4541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (tupleType != 0x20)
4551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
4561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (tupleLength != 4)
4571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
4581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	manfid = (tuple[1] << 8) | tuple[0];
4591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	devid = (tuple[3] << 8) | tuple[2];
4601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	// CISTPL_CONFIG
4641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((status = dvb_ca_en50221_read_tuple(ca, slot, &address, &tupleType,
4651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						&tupleLength, tuple)) < 0)
4661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return status;
4671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (tupleType != 0x1A)
4681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
4691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (tupleLength < 3)
4701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
4711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* extract the configbase */
4731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rasz = tuple[0] & 3;
4741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (tupleLength < (3 + rasz + 14))
4751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
4761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ca->slot_info[slot].config_base = 0;
4771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = 0; i < rasz + 1; i++) {
4781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ca->slot_info[slot].config_base |= (tuple[2 + i] << (8 * i));
4791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* check it contains the correct DVB string */
482260f8d7c4cda79b087a182eb03e8574ba41a171eOliver Endriss	dvb_str = findstr((char *)tuple, tupleLength, "DVB_CI_V", 8);
4831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (dvb_str == NULL)
4841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
4851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (tupleLength < ((dvb_str - (char *) tuple) + 12))
4861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
4871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* is it a version we support? */
4891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (strncmp(dvb_str + 8, "1.00", 4)) {
4901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk("dvb_ca adapter %d: Unsupported DVB CAM module version %c%c%c%c\n",
4911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		       ca->dvbdev->adapter->num, dvb_str[8], dvb_str[9], dvb_str[10], dvb_str[11]);
4921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
4931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* process the CFTABLE_ENTRY tuples, and any after those */
4961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	while ((!end_chain) && (address < 0x1000)) {
4971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if ((status = dvb_ca_en50221_read_tuple(ca, slot, &address, &tupleType,
498afd1a0c9ac281eed3b22b293ccd92af7b0d60889Mauro Carvalho Chehab							&tupleLength, tuple)) < 0)
4991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return status;
5001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		switch (tupleType) {
5011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case 0x1B:	// CISTPL_CFTABLE_ENTRY
5021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (tupleLength < (2 + 11 + 17))
5031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break;
5041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/* if we've already parsed one, just use it */
5061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (got_cftableentry)
5071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break;
5081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/* get the config option */
5101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			ca->slot_info[slot].config_option = tuple[0] & 0x3f;
5111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/* OK, check it contains the correct strings */
513260f8d7c4cda79b087a182eb03e8574ba41a171eOliver Endriss			if ((findstr((char *)tuple, tupleLength, "DVB_HOST", 8) == NULL) ||
514260f8d7c4cda79b087a182eb03e8574ba41a171eOliver Endriss			    (findstr((char *)tuple, tupleLength, "DVB_CI_MODULE", 13) == NULL))
5151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break;
5161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			got_cftableentry = 1;
5181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
5191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case 0x14:	// CISTPL_NO_LINK
5211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
5221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case 0xFF:	// CISTPL_END
5241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			end_chain = 1;
5251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
5261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		default:	/* Unknown tuple type - just skip this tuple and move to the next one */
5281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			dprintk("dvb_ca: Skipping unknown tuple type:0x%x length:0x%x\n", tupleType,
5291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				tupleLength);
5301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
5311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
5321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
5331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((address > 0x1000) || (!got_cftableentry))
5351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
5361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dprintk("Valid DVB CAM detected MANID:%x DEVID:%x CONFIGBASE:0x%x CONFIGOPTION:0x%x\n",
5381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		manfid, devid, ca->slot_info[slot].config_base, ca->slot_info[slot].config_option);
5391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	// success!
5411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
5421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/**
5461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Set CAM's configoption correctly.
5471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
5481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @param ca CA instance.
5491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @param slot Slot containing the CAM.
5501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
5511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int dvb_ca_en50221_set_configoption(struct dvb_ca_private *ca, int slot)
5521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int configoption;
5541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
55546b4f7c176a2dd4c60ddb7c80bd09ea2f3220674Harvey Harrison	dprintk("%s\n", __func__);
5561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* set the config option */
5581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ca->pub->write_attribute_mem(ca->pub, slot,
5591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				     ca->slot_info[slot].config_base,
5601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				     ca->slot_info[slot].config_option);
5611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* check it */
5631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	configoption = ca->pub->read_attribute_mem(ca->pub, slot, ca->slot_info[slot].config_base);
5641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dprintk("Set configoption 0x%x, read configoption 0x%x\n",
5651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ca->slot_info[slot].config_option, configoption & 0x3f);
5661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* fine! */
5681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
5691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/**
5741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This function talks to an EN50221 CAM control interface. It reads a buffer of
5751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * data from the CAM. The data can either be stored in a supplied buffer, or
5761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * automatically be added to the slot's rx_buffer.
5771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
5781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @param ca CA instance.
5791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @param slot Slot to read from.
5801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @param ebuf If non-NULL, the data will be written to this buffer. If NULL,
5811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * the data will be added into the buffering system as a normal fragment.
5821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @param ecount Size of ebuf. Ignored if ebuf is NULL.
5831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
5841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @return Number of bytes read, or < 0 on error
5851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
5861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int dvb_ca_en50221_read_data(struct dvb_ca_private *ca, int slot, u8 * ebuf, int ecount)
5871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int bytes_read;
5891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int status;
5901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u8 buf[HOST_LINK_BUF_SIZE];
5911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i;
5921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
59346b4f7c176a2dd4c60ddb7c80bd09ea2f3220674Harvey Harrison	dprintk("%s\n", __func__);
5941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* check if we have space for a link buf in the rx_buffer */
5961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ebuf == NULL) {
5971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		int buf_free;
5981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (ca->slot_info[slot].rx_buffer.data == NULL) {
6001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			status = -EIO;
6011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto exit;
6021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
6031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		buf_free = dvb_ringbuffer_free(&ca->slot_info[slot].rx_buffer);
6041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (buf_free < (ca->slot_info[slot].link_buf_size + DVB_RINGBUFFER_PKTHDRSIZE)) {
6061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			status = -EAGAIN;
6071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto exit;
6081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
6091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
6101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* check if there is data available */
6121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS)) < 0)
6131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto exit;
6141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!(status & STATUSREG_DA)) {
6151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* no data */
6161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		status = 0;
6171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto exit;
6181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
6191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* read the amount of data */
6211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_SIZE_HIGH)) < 0)
6221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto exit;
6231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	bytes_read = status << 8;
6241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_SIZE_LOW)) < 0)
6251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto exit;
6261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	bytes_read |= status;
6271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* check it will fit */
6291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ebuf == NULL) {
6301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (bytes_read > ca->slot_info[slot].link_buf_size) {
6311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			printk("dvb_ca adapter %d: CAM tried to send a buffer larger than the link buffer size (%i > %i)!\n",
6321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			       ca->dvbdev->adapter->num, bytes_read, ca->slot_info[slot].link_buf_size);
6331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_LINKINIT;
6341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			status = -EIO;
6351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto exit;
6361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
6371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (bytes_read < 2) {
6381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			printk("dvb_ca adapter %d: CAM sent a buffer that was less than 2 bytes!\n",
6391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			       ca->dvbdev->adapter->num);
6401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_LINKINIT;
6411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			status = -EIO;
6421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto exit;
6431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
6441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else {
6451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (bytes_read > ecount) {
6461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			printk("dvb_ca adapter %d: CAM tried to send a buffer larger than the ecount size!\n",
6471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			       ca->dvbdev->adapter->num);
6481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			status = -EIO;
6491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto exit;
6501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
6511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
6521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* fill the buffer */
6541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = 0; i < bytes_read; i++) {
6551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* read byte and check */
6561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_DATA)) < 0)
6571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto exit;
6581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* OK, store it in the buffer */
6601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		buf[i] = status;
6611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
6621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* check for read error (RE should now be 0) */
6641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS)) < 0)
6651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto exit;
6661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (status & STATUSREG_RE) {
6671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_LINKINIT;
6681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		status = -EIO;
6691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto exit;
6701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
6711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* OK, add it to the receive buffer, or copy into external buffer if supplied */
6731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ebuf == NULL) {
6741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (ca->slot_info[slot].rx_buffer.data == NULL) {
6751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			status = -EIO;
6761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto exit;
6771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
6781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dvb_ringbuffer_pkt_write(&ca->slot_info[slot].rx_buffer, buf, bytes_read);
6791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else {
6801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		memcpy(ebuf, buf, bytes_read);
6811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
6821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dprintk("Received CA packet for slot %i connection id 0x%x last_frag:%i size:0x%x\n", slot,
6841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		buf[0], (buf[1] & 0x80) == 0, bytes_read);
6851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* wake up readers when a last_fragment is received */
6871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((buf[1] & 0x80) == 0x00) {
6881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		wake_up_interruptible(&ca->wait_queue);
6891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
6901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	status = bytes_read;
6911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsexit:
6931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return status;
6941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/**
6981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This function talks to an EN50221 CAM control interface. It writes a buffer of data
6991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * to a CAM.
7001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
7011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @param ca CA instance.
7021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @param slot Slot to write to.
7031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @param ebuf The data in this buffer is treated as a complete link-level packet to
7041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * be written.
7051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @param count Size of ebuf.
7061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
7071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @return Number of bytes written, or < 0 on error.
7081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
7091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int dvb_ca_en50221_write_data(struct dvb_ca_private *ca, int slot, u8 * buf, int bytes_write)
7101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int status;
7121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i;
7131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
71446b4f7c176a2dd4c60ddb7c80bd09ea2f3220674Harvey Harrison	dprintk("%s\n", __func__);
7151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
717d7e43844e40e07cadc48f1733b9738659f83b38cMatthias Dahl	/* sanity check */
7181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (bytes_write > ca->slot_info[slot].link_buf_size)
7191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
7201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
721d7e43844e40e07cadc48f1733b9738659f83b38cMatthias Dahl	/* it is possible we are dealing with a single buffer implementation,
722d7e43844e40e07cadc48f1733b9738659f83b38cMatthias Dahl	   thus if there is data available for read or if there is even a read
723d7e43844e40e07cadc48f1733b9738659f83b38cMatthias Dahl	   already in progress, we do nothing but awake the kernel thread to
724d7e43844e40e07cadc48f1733b9738659f83b38cMatthias Dahl	   process the data if necessary. */
7251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS)) < 0)
7261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto exitnowrite;
7271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (status & (STATUSREG_DA | STATUSREG_RE)) {
728d7e43844e40e07cadc48f1733b9738659f83b38cMatthias Dahl		if (status & STATUSREG_DA)
729d7e43844e40e07cadc48f1733b9738659f83b38cMatthias Dahl			dvb_ca_en50221_thread_wakeup(ca);
730d7e43844e40e07cadc48f1733b9738659f83b38cMatthias Dahl
7311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		status = -EAGAIN;
7321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto exitnowrite;
7331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
7341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* OK, set HC bit */
7361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((status = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND,
7371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						 IRQEN | CMDREG_HC)) != 0)
7381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto exit;
7391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* check if interface is still free */
7411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS)) < 0)
7421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto exit;
7431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!(status & STATUSREG_FR)) {
7441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* it wasn't free => try again later */
7451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		status = -EAGAIN;
7461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto exit;
7471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
7481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* send the amount of data */
7501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((status = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_SIZE_HIGH, bytes_write >> 8)) != 0)
7511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto exit;
7521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((status = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_SIZE_LOW,
7531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						 bytes_write & 0xff)) != 0)
7541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto exit;
7551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* send the buffer */
7571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = 0; i < bytes_write; i++) {
7581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if ((status = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_DATA, buf[i])) != 0)
7591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto exit;
7601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
7611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* check for write error (WE should now be 0) */
7631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS)) < 0)
7641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto exit;
7651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (status & STATUSREG_WE) {
7661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_LINKINIT;
7671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		status = -EIO;
7681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto exit;
7691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
7701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	status = bytes_write;
7711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dprintk("Wrote CA packet for slot %i, connection id 0x%x last_frag:%i size:0x%x\n", slot,
7731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		buf[0], (buf[1] & 0x80) == 0, bytes_write);
7741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsexit:
7761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, IRQEN);
7771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsexitnowrite:
7791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return status;
7801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsEXPORT_SYMBOL(dvb_ca_en50221_camchange_irq);
7821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* ******************************************************************************** */
7861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* EN50221 higher level functions */
7871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/**
7901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * A CAM has been removed => shut it down.
7911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
7921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @param ca CA instance.
7931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @param slot Slot to shut down.
7941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
7951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int dvb_ca_en50221_slot_shutdown(struct dvb_ca_private *ca, int slot)
7961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
79746b4f7c176a2dd4c60ddb7c80bd09ea2f3220674Harvey Harrison	dprintk("%s\n", __func__);
7981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ca->pub->slot_shutdown(ca->pub, slot);
8001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_NONE;
8011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* need to wake up all processes to check if they're now
8031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   trying to write to a defunct CAM */
8041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	wake_up_interruptible(&ca->wait_queue);
8051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dprintk("Slot %i shutdown\n", slot);
8071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* success */
8091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
8101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
8111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsEXPORT_SYMBOL(dvb_ca_en50221_camready_irq);
8121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/**
8151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * A CAMCHANGE IRQ has occurred.
8161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
8171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @param ca CA instance.
8181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @param slot Slot concerned.
8191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @param change_type One of the DVB_CA_CAMCHANGE_* values.
8201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
8211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsvoid dvb_ca_en50221_camchange_irq(struct dvb_ca_en50221 *pubca, int slot, int change_type)
8221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
8230c53c70f6afa2d3f4d416d8c0e9a219c2f6b7cb6Johannes Stezenbach	struct dvb_ca_private *ca = pubca->private;
8241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dprintk("CAMCHANGE IRQ slot:%i change_type:%i\n", slot, change_type);
8261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	switch (change_type) {
8281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case DVB_CA_EN50221_CAMCHANGE_REMOVED:
8291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case DVB_CA_EN50221_CAMCHANGE_INSERTED:
8301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
8311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	default:
8331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
8341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
8351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ca->slot_info[slot].camchange_type = change_type;
8371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	atomic_inc(&ca->slot_info[slot].camchange_count);
8381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dvb_ca_en50221_thread_wakeup(ca);
8391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
8401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsEXPORT_SYMBOL(dvb_ca_en50221_frda_irq);
8411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/**
8441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * A CAMREADY IRQ has occurred.
8451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
8461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @param ca CA instance.
8471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @param slot Slot concerned.
8481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
8491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsvoid dvb_ca_en50221_camready_irq(struct dvb_ca_en50221 *pubca, int slot)
8501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
8510c53c70f6afa2d3f4d416d8c0e9a219c2f6b7cb6Johannes Stezenbach	struct dvb_ca_private *ca = pubca->private;
8521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dprintk("CAMREADY IRQ slot:%i\n", slot);
8541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ca->slot_info[slot].slot_state == DVB_CA_SLOTSTATE_WAITREADY) {
8561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_VALIDATE;
8571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dvb_ca_en50221_thread_wakeup(ca);
8581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
8591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
8601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/**
8631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * An FR or DA IRQ has occurred.
8641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
8651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @param ca CA instance.
8661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @param slot Slot concerned.
8671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
8681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsvoid dvb_ca_en50221_frda_irq(struct dvb_ca_en50221 *pubca, int slot)
8691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
8700c53c70f6afa2d3f4d416d8c0e9a219c2f6b7cb6Johannes Stezenbach	struct dvb_ca_private *ca = pubca->private;
8711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int flags;
8721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dprintk("FR/DA IRQ slot:%i\n", slot);
8741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	switch (ca->slot_info[slot].slot_state) {
8761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case DVB_CA_SLOTSTATE_LINKINIT:
8771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		flags = ca->pub->read_cam_control(pubca, slot, CTRLIF_STATUS);
8781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (flags & STATUSREG_DA) {
8791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			dprintk("CAM supports DA IRQ\n");
8801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			ca->slot_info[slot].da_irq_supported = 1;
8811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
8821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
8831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case DVB_CA_SLOTSTATE_RUNNING:
8851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (ca->open)
886ded928468407c8e08dcb6aedb91aaa97b80d5752Andrew de Quincey			dvb_ca_en50221_thread_wakeup(ca);
8871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
8881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
8891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
8901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* ******************************************************************************** */
8941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* EN50221 thread functions */
8951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/**
8971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Wake up the DVB CA thread
8981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
8991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @param ca CA instance.
9001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
9011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void dvb_ca_en50221_thread_wakeup(struct dvb_ca_private *ca)
9021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
9031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
90446b4f7c176a2dd4c60ddb7c80bd09ea2f3220674Harvey Harrison	dprintk("%s\n", __func__);
9051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ca->wakeup = 1;
9071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	mb();
9089320874a3e6aea7044a4a7eedeab13db990424abChristoph Hellwig	wake_up_process(ca->thread);
9091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
9101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/**
9121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Update the delay used by the thread.
9131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
9141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @param ca CA instance.
9151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
9161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void dvb_ca_en50221_thread_update_delay(struct dvb_ca_private *ca)
9171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
9181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int delay;
9191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int curdelay = 100000000;
9201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int slot;
9211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
92271a35fe2a345eb3704e1f1b4da65451d3e2b8c2eRobert Schedel	/* Beware of too high polling frequency, because one polling
92371a35fe2a345eb3704e1f1b4da65451d3e2b8c2eRobert Schedel	 * call might take several hundred milliseconds until timeout!
92471a35fe2a345eb3704e1f1b4da65451d3e2b8c2eRobert Schedel	 */
9251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (slot = 0; slot < ca->slot_count; slot++) {
9261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		switch (ca->slot_info[slot].slot_state) {
9271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		default:
9281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case DVB_CA_SLOTSTATE_NONE:
92971a35fe2a345eb3704e1f1b4da65451d3e2b8c2eRobert Schedel			delay = HZ * 60;  /* 60s */
93071a35fe2a345eb3704e1f1b4da65451d3e2b8c2eRobert Schedel			if (!(ca->flags & DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE))
93171a35fe2a345eb3704e1f1b4da65451d3e2b8c2eRobert Schedel				delay = HZ * 5;  /* 5s */
93271a35fe2a345eb3704e1f1b4da65451d3e2b8c2eRobert Schedel			break;
9331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case DVB_CA_SLOTSTATE_INVALID:
93471a35fe2a345eb3704e1f1b4da65451d3e2b8c2eRobert Schedel			delay = HZ * 60;  /* 60s */
93571a35fe2a345eb3704e1f1b4da65451d3e2b8c2eRobert Schedel			if (!(ca->flags & DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE))
93671a35fe2a345eb3704e1f1b4da65451d3e2b8c2eRobert Schedel				delay = HZ / 10;  /* 100ms */
9371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
9381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case DVB_CA_SLOTSTATE_UNINITIALISED:
9401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case DVB_CA_SLOTSTATE_WAITREADY:
9411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case DVB_CA_SLOTSTATE_VALIDATE:
9421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case DVB_CA_SLOTSTATE_WAITFR:
9431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case DVB_CA_SLOTSTATE_LINKINIT:
94471a35fe2a345eb3704e1f1b4da65451d3e2b8c2eRobert Schedel			delay = HZ / 10;  /* 100ms */
9451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
9461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case DVB_CA_SLOTSTATE_RUNNING:
94871a35fe2a345eb3704e1f1b4da65451d3e2b8c2eRobert Schedel			delay = HZ * 60;  /* 60s */
94971a35fe2a345eb3704e1f1b4da65451d3e2b8c2eRobert Schedel			if (!(ca->flags & DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE))
95071a35fe2a345eb3704e1f1b4da65451d3e2b8c2eRobert Schedel				delay = HZ / 10;  /* 100ms */
9511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (ca->open) {
9521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				if ((!ca->slot_info[slot].da_irq_supported) ||
95371a35fe2a345eb3704e1f1b4da65451d3e2b8c2eRobert Schedel				    (!(ca->flags & DVB_CA_EN50221_FLAG_IRQ_DA)))
95471a35fe2a345eb3704e1f1b4da65451d3e2b8c2eRobert Schedel					delay = HZ / 10;  /* 100ms */
9551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
9561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
9571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
9581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (delay < curdelay)
9601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			curdelay = delay;
9611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
9621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ca->delay = curdelay;
9641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
9651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/**
9691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Kernel thread which monitors CA slots for CAM changes, and performs data transfers.
9701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
9711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int dvb_ca_en50221_thread(void *data)
9721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
9730c53c70f6afa2d3f4d416d8c0e9a219c2f6b7cb6Johannes Stezenbach	struct dvb_ca_private *ca = data;
9741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int slot;
9751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int flags;
9761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int status;
9771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int pktcount;
9781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	void *rxbuf;
9791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
98046b4f7c176a2dd4c60ddb7c80bd09ea2f3220674Harvey Harrison	dprintk("%s\n", __func__);
9811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* choose the correct initial delay */
9831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dvb_ca_en50221_thread_update_delay(ca);
9841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* main loop */
9869320874a3e6aea7044a4a7eedeab13db990424abChristoph Hellwig	while (!kthread_should_stop()) {
9871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* sleep for a bit */
988a39a8ed7beaafe02ce154dfd227f7d734a9f34dcMarco Schluessler		if (!ca->wakeup) {
9899320874a3e6aea7044a4a7eedeab13db990424abChristoph Hellwig			set_current_state(TASK_INTERRUPTIBLE);
9909320874a3e6aea7044a4a7eedeab13db990424abChristoph Hellwig			schedule_timeout(ca->delay);
9919320874a3e6aea7044a4a7eedeab13db990424abChristoph Hellwig			if (kthread_should_stop())
9929320874a3e6aea7044a4a7eedeab13db990424abChristoph Hellwig				return 0;
9931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
9941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ca->wakeup = 0;
9951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* go through all the slots processing them */
9971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		for (slot = 0; slot < ca->slot_count; slot++) {
9981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
999d7e43844e40e07cadc48f1733b9738659f83b38cMatthias Dahl			mutex_lock(&ca->slot_info[slot].slot_lock);
1000d7e43844e40e07cadc48f1733b9738659f83b38cMatthias Dahl
10011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			// check the cam status + deal with CAMCHANGEs
10021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			while (dvb_ca_en50221_check_camstatus(ca, slot)) {
10031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				/* clear down an old CI slot if necessary */
10041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				if (ca->slot_info[slot].slot_state != DVB_CA_SLOTSTATE_NONE)
10051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					dvb_ca_en50221_slot_shutdown(ca, slot);
10061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				/* if a CAM is NOW present, initialise it */
10081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				if (ca->slot_info[slot].camchange_type == DVB_CA_EN50221_CAMCHANGE_INSERTED) {
10091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_UNINITIALISED;
10101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				}
10111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				/* we've handled one CAMCHANGE */
10131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				dvb_ca_en50221_thread_update_delay(ca);
10141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				atomic_dec(&ca->slot_info[slot].camchange_count);
10151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
10161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			// CAM state machine
10181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			switch (ca->slot_info[slot].slot_state) {
10191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			case DVB_CA_SLOTSTATE_NONE:
10201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			case DVB_CA_SLOTSTATE_INVALID:
10211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				// no action needed
10221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break;
10231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			case DVB_CA_SLOTSTATE_UNINITIALISED:
10251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_WAITREADY;
10261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				ca->pub->slot_reset(ca->pub, slot);
10271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				ca->slot_info[slot].timeout = jiffies + (INIT_TIMEOUT_SECS * HZ);
10281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break;
10291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			case DVB_CA_SLOTSTATE_WAITREADY:
10311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				if (time_after(jiffies, ca->slot_info[slot].timeout)) {
10321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					printk("dvb_ca adaptor %d: PC card did not respond :(\n",
10331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					       ca->dvbdev->adapter->num);
10341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_INVALID;
10351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					dvb_ca_en50221_thread_update_delay(ca);
10361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					break;
10371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				}
10381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				// no other action needed; will automatically change state when ready
10391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break;
10401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			case DVB_CA_SLOTSTATE_VALIDATE:
10425c1208ba457a1668c81868060c08496a2d053be0Andrew de Quincey				if (dvb_ca_en50221_parse_attributes(ca, slot) != 0) {
10435c1208ba457a1668c81868060c08496a2d053be0Andrew de Quincey					/* we need this extra check for annoying interfaces like the budget-av */
10445c1208ba457a1668c81868060c08496a2d053be0Andrew de Quincey					if ((!(ca->flags & DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE)) &&
10455c1208ba457a1668c81868060c08496a2d053be0Andrew de Quincey					    (ca->pub->poll_slot_status)) {
1046c6eb8eafdba4ad18b4520a0d28a38bc9e61883eaHans Verkuil						status = ca->pub->poll_slot_status(ca->pub, slot, 0);
10475c1208ba457a1668c81868060c08496a2d053be0Andrew de Quincey						if (!(status & DVB_CA_EN50221_POLL_CAM_PRESENT)) {
10485c1208ba457a1668c81868060c08496a2d053be0Andrew de Quincey							ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_NONE;
10495c1208ba457a1668c81868060c08496a2d053be0Andrew de Quincey							dvb_ca_en50221_thread_update_delay(ca);
10505c1208ba457a1668c81868060c08496a2d053be0Andrew de Quincey							break;
10515c1208ba457a1668c81868060c08496a2d053be0Andrew de Quincey						}
10525c1208ba457a1668c81868060c08496a2d053be0Andrew de Quincey					}
10535c1208ba457a1668c81868060c08496a2d053be0Andrew de Quincey
10541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					printk("dvb_ca adapter %d: Invalid PC card inserted :(\n",
10551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					       ca->dvbdev->adapter->num);
10561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_INVALID;
10571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					dvb_ca_en50221_thread_update_delay(ca);
10581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					break;
10591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				}
10601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				if (dvb_ca_en50221_set_configoption(ca, slot) != 0) {
10611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					printk("dvb_ca adapter %d: Unable to initialise CAM :(\n",
10621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					       ca->dvbdev->adapter->num);
10631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_INVALID;
10641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					dvb_ca_en50221_thread_update_delay(ca);
10651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					break;
10661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				}
10671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				if (ca->pub->write_cam_control(ca->pub, slot,
10681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds							       CTRLIF_COMMAND, CMDREG_RS) != 0) {
10691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					printk("dvb_ca adapter %d: Unable to reset CAM IF\n",
10701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					       ca->dvbdev->adapter->num);
10711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_INVALID;
10721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					dvb_ca_en50221_thread_update_delay(ca);
10731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					break;
10741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				}
10751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				dprintk("DVB CAM validated successfully\n");
10761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				ca->slot_info[slot].timeout = jiffies + (INIT_TIMEOUT_SECS * HZ);
10781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_WAITFR;
10791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				ca->wakeup = 1;
10801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break;
10811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			case DVB_CA_SLOTSTATE_WAITFR:
10831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				if (time_after(jiffies, ca->slot_info[slot].timeout)) {
10841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					printk("dvb_ca adapter %d: DVB CAM did not respond :(\n",
10851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					       ca->dvbdev->adapter->num);
10861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_INVALID;
10871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					dvb_ca_en50221_thread_update_delay(ca);
10881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					break;
10891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				}
10901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				flags = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS);
10921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				if (flags & STATUSREG_FR) {
10931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_LINKINIT;
10941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					ca->wakeup = 1;
10951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				}
10961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break;
10971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			case DVB_CA_SLOTSTATE_LINKINIT:
10991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				if (dvb_ca_en50221_link_init(ca, slot) != 0) {
11005c1208ba457a1668c81868060c08496a2d053be0Andrew de Quincey					/* we need this extra check for annoying interfaces like the budget-av */
11015c1208ba457a1668c81868060c08496a2d053be0Andrew de Quincey					if ((!(ca->flags & DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE)) &&
11025c1208ba457a1668c81868060c08496a2d053be0Andrew de Quincey					    (ca->pub->poll_slot_status)) {
1103c6eb8eafdba4ad18b4520a0d28a38bc9e61883eaHans Verkuil						status = ca->pub->poll_slot_status(ca->pub, slot, 0);
11045c1208ba457a1668c81868060c08496a2d053be0Andrew de Quincey						if (!(status & DVB_CA_EN50221_POLL_CAM_PRESENT)) {
11055c1208ba457a1668c81868060c08496a2d053be0Andrew de Quincey							ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_NONE;
11065c1208ba457a1668c81868060c08496a2d053be0Andrew de Quincey							dvb_ca_en50221_thread_update_delay(ca);
11075c1208ba457a1668c81868060c08496a2d053be0Andrew de Quincey							break;
11085c1208ba457a1668c81868060c08496a2d053be0Andrew de Quincey						}
11095c1208ba457a1668c81868060c08496a2d053be0Andrew de Quincey					}
11105c1208ba457a1668c81868060c08496a2d053be0Andrew de Quincey
11111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					printk("dvb_ca adapter %d: DVB CAM link initialisation failed :(\n", ca->dvbdev->adapter->num);
11121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_INVALID;
11131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					dvb_ca_en50221_thread_update_delay(ca);
11141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					break;
11151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				}
11161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1117ded928468407c8e08dcb6aedb91aaa97b80d5752Andrew de Quincey				if (ca->slot_info[slot].rx_buffer.data == NULL) {
1118ded928468407c8e08dcb6aedb91aaa97b80d5752Andrew de Quincey					rxbuf = vmalloc(RX_BUFFER_SIZE);
1119ded928468407c8e08dcb6aedb91aaa97b80d5752Andrew de Quincey					if (rxbuf == NULL) {
1120ded928468407c8e08dcb6aedb91aaa97b80d5752Andrew de Quincey						printk("dvb_ca adapter %d: Unable to allocate CAM rx buffer :(\n", ca->dvbdev->adapter->num);
1121ded928468407c8e08dcb6aedb91aaa97b80d5752Andrew de Quincey						ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_INVALID;
1122ded928468407c8e08dcb6aedb91aaa97b80d5752Andrew de Quincey						dvb_ca_en50221_thread_update_delay(ca);
1123ded928468407c8e08dcb6aedb91aaa97b80d5752Andrew de Quincey						break;
1124ded928468407c8e08dcb6aedb91aaa97b80d5752Andrew de Quincey					}
1125ded928468407c8e08dcb6aedb91aaa97b80d5752Andrew de Quincey					dvb_ringbuffer_init(&ca->slot_info[slot].rx_buffer, rxbuf, RX_BUFFER_SIZE);
11261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				}
11271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				ca->pub->slot_ts_enable(ca->pub, slot);
11291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_RUNNING;
11301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				dvb_ca_en50221_thread_update_delay(ca);
11311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				printk("dvb_ca adapter %d: DVB CAM detected and initialised successfully\n", ca->dvbdev->adapter->num);
11321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break;
11331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			case DVB_CA_SLOTSTATE_RUNNING:
11351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				if (!ca->open)
1136d7e43844e40e07cadc48f1733b9738659f83b38cMatthias Dahl					break;
11371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1138ded928468407c8e08dcb6aedb91aaa97b80d5752Andrew de Quincey				// poll slots for data
11391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				pktcount = 0;
11401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				while ((status = dvb_ca_en50221_read_data(ca, slot, NULL, 0)) > 0) {
11411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					if (!ca->open)
11421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						break;
11431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					/* if a CAMCHANGE occurred at some point, do not do any more processing of this slot */
11451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					if (dvb_ca_en50221_check_camstatus(ca, slot)) {
11461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						// we dont want to sleep on the next iteration so we can handle the cam change
11471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						ca->wakeup = 1;
11481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						break;
11491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					}
11501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					/* check if we've hit our limit this time */
11521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					if (++pktcount >= MAX_RX_PACKETS_PER_ITERATION) {
11531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						// dont sleep; there is likely to be more data to read
11541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						ca->wakeup = 1;
11551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						break;
11561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					}
11571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				}
11581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break;
11591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
1160d7e43844e40e07cadc48f1733b9738659f83b38cMatthias Dahl
1161d7e43844e40e07cadc48f1733b9738659f83b38cMatthias Dahl			mutex_unlock(&ca->slot_info[slot].slot_lock);
11621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
11631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
11641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
11661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
11671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* ******************************************************************************** */
11711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* EN50221 IO interface functions */
11721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/**
11741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Real ioctl implementation.
11751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * NOTE: CA_SEND_MSG/CA_GET_MSG ioctls have userspace buffers passed to them.
11761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
11771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @param inode Inode concerned.
11781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @param file File concerned.
11791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @param cmd IOCTL command.
11801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @param arg Associated argument.
11811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
11821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @return 0 on success, <0 on error.
11831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
118416ef8def80ea97c3cacdcaa765bdf62b2d94f86dArnd Bergmannstatic int dvb_ca_en50221_io_do_ioctl(struct file *file,
11851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				      unsigned int cmd, void *parg)
11861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
11870c53c70f6afa2d3f4d416d8c0e9a219c2f6b7cb6Johannes Stezenbach	struct dvb_device *dvbdev = file->private_data;
11880c53c70f6afa2d3f4d416d8c0e9a219c2f6b7cb6Johannes Stezenbach	struct dvb_ca_private *ca = dvbdev->priv;
11891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int err = 0;
11901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int slot;
11911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
119246b4f7c176a2dd4c60ddb7c80bd09ea2f3220674Harvey Harrison	dprintk("%s\n", __func__);
11931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	switch (cmd) {
11951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case CA_RESET:
11961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		for (slot = 0; slot < ca->slot_count; slot++) {
1197d7e43844e40e07cadc48f1733b9738659f83b38cMatthias Dahl			mutex_lock(&ca->slot_info[slot].slot_lock);
11981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (ca->slot_info[slot].slot_state != DVB_CA_SLOTSTATE_NONE) {
11991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				dvb_ca_en50221_slot_shutdown(ca, slot);
12001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				if (ca->flags & DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE)
12011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					dvb_ca_en50221_camchange_irq(ca->pub,
12021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds								     slot,
12031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds								     DVB_CA_EN50221_CAMCHANGE_INSERTED);
12041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
1205d7e43844e40e07cadc48f1733b9738659f83b38cMatthias Dahl			mutex_unlock(&ca->slot_info[slot].slot_lock);
12061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
12071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ca->next_read_slot = 0;
12081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dvb_ca_en50221_thread_wakeup(ca);
12091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
12101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case CA_GET_CAP: {
12120c53c70f6afa2d3f4d416d8c0e9a219c2f6b7cb6Johannes Stezenbach		struct ca_caps *caps = parg;
12131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		caps->slot_num = ca->slot_count;
12151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		caps->slot_type = CA_CI_LINK;
12161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		caps->descr_num = 0;
12171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		caps->descr_type = 0;
12181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
12191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
12201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case CA_GET_SLOT_INFO: {
12220c53c70f6afa2d3f4d416d8c0e9a219c2f6b7cb6Johannes Stezenbach		struct ca_slot_info *info = parg;
12231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if ((info->num > ca->slot_count) || (info->num < 0))
12251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -EINVAL;
12261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		info->type = CA_CI_LINK;
12281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		info->flags = 0;
12291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if ((ca->slot_info[info->num].slot_state != DVB_CA_SLOTSTATE_NONE)
12301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			&& (ca->slot_info[info->num].slot_state != DVB_CA_SLOTSTATE_INVALID)) {
12311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			info->flags = CA_CI_MODULE_PRESENT;
12321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
12331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (ca->slot_info[info->num].slot_state == DVB_CA_SLOTSTATE_RUNNING) {
12341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			info->flags |= CA_CI_MODULE_READY;
12351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
12361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
12371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
12381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	default:
12401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		err = -EINVAL;
12411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
12421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
12431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return err;
12451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
12461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/**
12491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Wrapper for ioctl implementation.
12501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
12511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @param inode Inode concerned.
12521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @param file File concerned.
12531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @param cmd IOCTL command.
12541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @param arg Associated argument.
12551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
12561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @return 0 on success, <0 on error.
12571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
125816ef8def80ea97c3cacdcaa765bdf62b2d94f86dArnd Bergmannstatic long dvb_ca_en50221_io_ioctl(struct file *file,
125916ef8def80ea97c3cacdcaa765bdf62b2d94f86dArnd Bergmann				    unsigned int cmd, unsigned long arg)
12601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
126172024f1ec5164a70d84dd8cf4458fe4064a6b692Arnd Bergmann	return dvb_usercopy(file, cmd, arg, dvb_ca_en50221_io_do_ioctl);
12621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
12631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/**
12661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Implementation of write() syscall.
12671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
12681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @param file File structure.
12691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @param buf Source buffer.
12701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @param count Size of source buffer.
12711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @param ppos Position in file (ignored).
12721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
12731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @return Number of bytes read, or <0 on error.
12741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
12751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic ssize_t dvb_ca_en50221_io_write(struct file *file,
12761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				       const char __user * buf, size_t count, loff_t * ppos)
12771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
12780c53c70f6afa2d3f4d416d8c0e9a219c2f6b7cb6Johannes Stezenbach	struct dvb_device *dvbdev = file->private_data;
12790c53c70f6afa2d3f4d416d8c0e9a219c2f6b7cb6Johannes Stezenbach	struct dvb_ca_private *ca = dvbdev->priv;
12801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u8 slot, connection_id;
12811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int status;
1282260f8d7c4cda79b087a182eb03e8574ba41a171eOliver Endriss	u8 fragbuf[HOST_LINK_BUF_SIZE];
12831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int fragpos = 0;
12841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int fraglen;
12851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long timeout;
12861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int written;
12871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
128846b4f7c176a2dd4c60ddb7c80bd09ea2f3220674Harvey Harrison	dprintk("%s\n", __func__);
12891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Incoming packet has a 2 byte header. hdr[0] = slot_id, hdr[1] = connection_id */
12911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (count < 2)
12921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
12931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* extract slot & connection id */
12951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (copy_from_user(&slot, buf, 1))
12961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EFAULT;
12971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (copy_from_user(&connection_id, buf + 1, 1))
12981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EFAULT;
12991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	buf += 2;
13001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	count -= 2;
13011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* check if the slot is actually running */
13031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ca->slot_info[slot].slot_state != DVB_CA_SLOTSTATE_RUNNING)
13041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
13051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* fragment the packets & store in the buffer */
13071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	while (fragpos < count) {
13081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		fraglen = ca->slot_info[slot].link_buf_size - 2;
1309624f0c186978f9cb0ce6582f445922eaaa4a7f4aMauro Carvalho Chehab		if (fraglen < 0)
1310624f0c186978f9cb0ce6582f445922eaaa4a7f4aMauro Carvalho Chehab			break;
1311624f0c186978f9cb0ce6582f445922eaaa4a7f4aMauro Carvalho Chehab		if (fraglen > HOST_LINK_BUF_SIZE - 2)
1312624f0c186978f9cb0ce6582f445922eaaa4a7f4aMauro Carvalho Chehab			fraglen = HOST_LINK_BUF_SIZE - 2;
13131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if ((count - fragpos) < fraglen)
13141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			fraglen = count - fragpos;
13151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		fragbuf[0] = connection_id;
13171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		fragbuf[1] = ((fragpos + fraglen) < count) ? 0x80 : 0x00;
1318e252984c5279dde24fbd6d3efe7fe13dc642e714Dan Carpenter		status = copy_from_user(fragbuf + 2, buf + fragpos, fraglen);
1319e252984c5279dde24fbd6d3efe7fe13dc642e714Dan Carpenter		if (status) {
1320e252984c5279dde24fbd6d3efe7fe13dc642e714Dan Carpenter			status = -EFAULT;
13211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto exit;
1322e252984c5279dde24fbd6d3efe7fe13dc642e714Dan Carpenter		}
13231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		timeout = jiffies + HZ / 2;
13251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		written = 0;
13261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		while (!time_after(jiffies, timeout)) {
13271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/* check the CAM hasn't been removed/reset in the meantime */
13281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (ca->slot_info[slot].slot_state != DVB_CA_SLOTSTATE_RUNNING) {
13291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				status = -EIO;
13301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				goto exit;
13311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
13321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1333d7e43844e40e07cadc48f1733b9738659f83b38cMatthias Dahl			mutex_lock(&ca->slot_info[slot].slot_lock);
13341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			status = dvb_ca_en50221_write_data(ca, slot, fragbuf, fraglen + 2);
1335d7e43844e40e07cadc48f1733b9738659f83b38cMatthias Dahl			mutex_unlock(&ca->slot_info[slot].slot_lock);
13361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (status == (fraglen + 2)) {
13371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				written = 1;
13381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break;
13391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
13401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (status != -EAGAIN)
13411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				goto exit;
13421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			msleep(1);
13441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
13451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!written) {
13461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			status = -EIO;
13471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto exit;
13481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
13491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		fragpos += fraglen;
13511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
13521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	status = count + 2;
13531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsexit:
13551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return status;
13561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
13571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/**
13601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Condition for waking up in dvb_ca_en50221_io_read_condition
13611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
1362ded928468407c8e08dcb6aedb91aaa97b80d5752Andrew de Quinceystatic int dvb_ca_en50221_io_read_condition(struct dvb_ca_private *ca,
1363ded928468407c8e08dcb6aedb91aaa97b80d5752Andrew de Quincey					    int *result, int *_slot)
13641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
13651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int slot;
13661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int slot_count = 0;
13671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int idx;
1368ded928468407c8e08dcb6aedb91aaa97b80d5752Andrew de Quincey	size_t fraglen;
13691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int connection_id = -1;
13701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int found = 0;
13711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u8 hdr[2];
13721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	slot = ca->next_read_slot;
13741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	while ((slot_count < ca->slot_count) && (!found)) {
13751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (ca->slot_info[slot].slot_state != DVB_CA_SLOTSTATE_RUNNING)
13761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto nextslot;
13771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (ca->slot_info[slot].rx_buffer.data == NULL) {
13791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return 0;
13801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
13811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		idx = dvb_ringbuffer_pkt_next(&ca->slot_info[slot].rx_buffer, -1, &fraglen);
13831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		while (idx != -1) {
1384b0ba0e3ab6f452321771325b7b5578f9a804f69eAl Viro			dvb_ringbuffer_pkt_read(&ca->slot_info[slot].rx_buffer, idx, 0, hdr, 2);
13851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (connection_id == -1)
13861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				connection_id = hdr[0];
13871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if ((hdr[0] == connection_id) && ((hdr[1] & 0x80) == 0)) {
13881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				*_slot = slot;
13891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				found = 1;
13901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break;
13911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
13921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			idx = dvb_ringbuffer_pkt_next(&ca->slot_info[slot].rx_buffer, idx, &fraglen);
13941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
13951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1396ded928468407c8e08dcb6aedb91aaa97b80d5752Andrew de Quinceynextslot:
13971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		slot = (slot + 1) % ca->slot_count;
13981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		slot_count++;
13991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
14001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ca->next_read_slot = slot;
14021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return found;
14031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
14041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/**
14071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Implementation of read() syscall.
14081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
14091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @param file File structure.
14101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @param buf Destination buffer.
14111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @param count Size of destination buffer.
14121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @param ppos Position in file (ignored).
14131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
14141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @return Number of bytes read, or <0 on error.
14151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
14161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic ssize_t dvb_ca_en50221_io_read(struct file *file, char __user * buf,
14171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				      size_t count, loff_t * ppos)
14181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
14190c53c70f6afa2d3f4d416d8c0e9a219c2f6b7cb6Johannes Stezenbach	struct dvb_device *dvbdev = file->private_data;
14200c53c70f6afa2d3f4d416d8c0e9a219c2f6b7cb6Johannes Stezenbach	struct dvb_ca_private *ca = dvbdev->priv;
14211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int status;
14221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int result = 0;
14231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u8 hdr[2];
14241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int slot;
14251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int connection_id = -1;
14261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	size_t idx, idx2;
14271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int last_fragment = 0;
14281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	size_t fraglen;
14291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int pktlen;
14301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int dispose = 0;
14311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
143246b4f7c176a2dd4c60ddb7c80bd09ea2f3220674Harvey Harrison	dprintk("%s\n", __func__);
14331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Outgoing packet has a 2 byte header. hdr[0] = slot_id, hdr[1] = connection_id */
14351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (count < 2)
14361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
14371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* wait for some data */
14391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((status = dvb_ca_en50221_io_read_condition(ca, &result, &slot)) == 0) {
14401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* if we're in nonblocking mode, exit immediately */
14421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (file->f_flags & O_NONBLOCK)
14431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -EWOULDBLOCK;
14441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* wait for some data */
14461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		status = wait_event_interruptible(ca->wait_queue,
14471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						  dvb_ca_en50221_io_read_condition
14481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						  (ca, &result, &slot));
14491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
14501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((status < 0) || (result < 0)) {
14511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (result)
14521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return result;
14531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return status;
14541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
14551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	idx = dvb_ringbuffer_pkt_next(&ca->slot_info[slot].rx_buffer, -1, &fraglen);
14571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	pktlen = 2;
14581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	do {
14591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (idx == -1) {
14601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			printk("dvb_ca adapter %d: BUG: read packet ended before last_fragment encountered\n", ca->dvbdev->adapter->num);
14611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			status = -EIO;
14621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto exit;
14631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
14641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1465b0ba0e3ab6f452321771325b7b5578f9a804f69eAl Viro		dvb_ringbuffer_pkt_read(&ca->slot_info[slot].rx_buffer, idx, 0, hdr, 2);
14661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (connection_id == -1)
14671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			connection_id = hdr[0];
14681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (hdr[0] == connection_id) {
14691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (pktlen < count) {
14701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				if ((pktlen + fraglen - 2) > count) {
14711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					fraglen = count - pktlen;
14721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				} else {
14731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					fraglen -= 2;
14741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				}
14751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1476b0ba0e3ab6f452321771325b7b5578f9a804f69eAl Viro				if ((status = dvb_ringbuffer_pkt_read_user(&ca->slot_info[slot].rx_buffer, idx, 2,
1477b0ba0e3ab6f452321771325b7b5578f9a804f69eAl Viro								      buf + pktlen, fraglen)) < 0) {
14781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					goto exit;
14791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				}
14801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				pktlen += fraglen;
14811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
14821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if ((hdr[1] & 0x80) == 0)
14841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				last_fragment = 1;
14851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			dispose = 1;
14861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
14871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		idx2 = dvb_ringbuffer_pkt_next(&ca->slot_info[slot].rx_buffer, idx, &fraglen);
14891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (dispose)
14901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			dvb_ringbuffer_pkt_dispose(&ca->slot_info[slot].rx_buffer, idx);
14911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		idx = idx2;
14921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dispose = 0;
14931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} while (!last_fragment);
14941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	hdr[0] = slot;
14961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	hdr[1] = connection_id;
1497e252984c5279dde24fbd6d3efe7fe13dc642e714Dan Carpenter	status = copy_to_user(buf, hdr, 2);
1498e252984c5279dde24fbd6d3efe7fe13dc642e714Dan Carpenter	if (status) {
1499e252984c5279dde24fbd6d3efe7fe13dc642e714Dan Carpenter		status = -EFAULT;
15001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto exit;
1501e252984c5279dde24fbd6d3efe7fe13dc642e714Dan Carpenter	}
15021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	status = pktlen;
15031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1504ded928468407c8e08dcb6aedb91aaa97b80d5752Andrew de Quinceyexit:
15051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return status;
15061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
15071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/**
15101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Implementation of file open syscall.
15111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
15121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @param inode Inode concerned.
15131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @param file File concerned.
15141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
15151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @return 0 on success, <0 on failure.
15161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
15171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int dvb_ca_en50221_io_open(struct inode *inode, struct file *file)
15181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
15190c53c70f6afa2d3f4d416d8c0e9a219c2f6b7cb6Johannes Stezenbach	struct dvb_device *dvbdev = file->private_data;
15200c53c70f6afa2d3f4d416d8c0e9a219c2f6b7cb6Johannes Stezenbach	struct dvb_ca_private *ca = dvbdev->priv;
15211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int err;
15221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i;
15231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
152446b4f7c176a2dd4c60ddb7c80bd09ea2f3220674Harvey Harrison	dprintk("%s\n", __func__);
15251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!try_module_get(ca->pub->owner))
15271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EIO;
15281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	err = dvb_generic_open(inode, file);
1530226835d7e0a1136bce2a0b923e0832ab47450a30Marco Schluessler	if (err < 0) {
1531226835d7e0a1136bce2a0b923e0832ab47450a30Marco Schluessler		module_put(ca->pub->owner);
15321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return err;
1533226835d7e0a1136bce2a0b923e0832ab47450a30Marco Schluessler	}
15341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = 0; i < ca->slot_count; i++) {
15361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (ca->slot_info[i].slot_state == DVB_CA_SLOTSTATE_RUNNING) {
15381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (ca->slot_info[i].rx_buffer.data != NULL) {
1539ded928468407c8e08dcb6aedb91aaa97b80d5752Andrew de Quincey				/* it is safe to call this here without locks because
1540ded928468407c8e08dcb6aedb91aaa97b80d5752Andrew de Quincey				 * ca->open == 0. Data is not read in this case */
15411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				dvb_ringbuffer_flush(&ca->slot_info[i].rx_buffer);
15421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
15431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
15441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
15451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ca->open = 1;
15471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dvb_ca_en50221_thread_update_delay(ca);
15481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dvb_ca_en50221_thread_wakeup(ca);
15491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
15511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
15521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/**
15551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Implementation of file close syscall.
15561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
15571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @param inode Inode concerned.
15581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @param file File concerned.
15591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
15601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @return 0 on success, <0 on failure.
15611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
15621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int dvb_ca_en50221_io_release(struct inode *inode, struct file *file)
15631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
15640c53c70f6afa2d3f4d416d8c0e9a219c2f6b7cb6Johannes Stezenbach	struct dvb_device *dvbdev = file->private_data;
15650c53c70f6afa2d3f4d416d8c0e9a219c2f6b7cb6Johannes Stezenbach	struct dvb_ca_private *ca = dvbdev->priv;
15660c12c1bfc432477e38ba76f680be4b3f55112a8eMarco Schluessler	int err;
15671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
156846b4f7c176a2dd4c60ddb7c80bd09ea2f3220674Harvey Harrison	dprintk("%s\n", __func__);
15691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* mark the CA device as closed */
15711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ca->open = 0;
15721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dvb_ca_en50221_thread_update_delay(ca);
15731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	err = dvb_generic_release(inode, file);
15751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	module_put(ca->pub->owner);
15771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15780c12c1bfc432477e38ba76f680be4b3f55112a8eMarco Schluessler	return err;
15791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
15801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/**
15831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Implementation of poll() syscall.
15841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
15851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @param file File concerned.
15861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @param wait poll wait table.
15871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
15881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @return Standard poll mask.
15891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
15901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic unsigned int dvb_ca_en50221_io_poll(struct file *file, poll_table * wait)
15911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
15920c53c70f6afa2d3f4d416d8c0e9a219c2f6b7cb6Johannes Stezenbach	struct dvb_device *dvbdev = file->private_data;
15930c53c70f6afa2d3f4d416d8c0e9a219c2f6b7cb6Johannes Stezenbach	struct dvb_ca_private *ca = dvbdev->priv;
15941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned int mask = 0;
15951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int slot;
15961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int result = 0;
15971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
159846b4f7c176a2dd4c60ddb7c80bd09ea2f3220674Harvey Harrison	dprintk("%s\n", __func__);
15991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (dvb_ca_en50221_io_read_condition(ca, &result, &slot) == 1) {
16011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		mask |= POLLIN;
16021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
16031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* if there is something, return now */
16051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (mask)
16061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return mask;
16071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* wait for something to happen */
16091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	poll_wait(file, &ca->wait_queue, wait);
16101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (dvb_ca_en50221_io_read_condition(ca, &result, &slot) == 1) {
16121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		mask |= POLLIN;
16131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
16141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return mask;
16161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
16171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsEXPORT_SYMBOL(dvb_ca_en50221_init);
16181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1620784e29d2031b535637f65a8b81fb0871c7c51b3fJan Engelhardtstatic const struct file_operations dvb_ca_fops = {
16211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.owner = THIS_MODULE,
16221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.read = dvb_ca_en50221_io_read,
16231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.write = dvb_ca_en50221_io_write,
162416ef8def80ea97c3cacdcaa765bdf62b2d94f86dArnd Bergmann	.unlocked_ioctl = dvb_ca_en50221_io_ioctl,
16251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.open = dvb_ca_en50221_io_open,
16261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.release = dvb_ca_en50221_io_release,
16271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.poll = dvb_ca_en50221_io_poll,
16286038f373a3dc1f1c26496e60b6c40b164716f07eArnd Bergmann	.llseek = noop_llseek,
16291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
16301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct dvb_device dvbdev_ca = {
16321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.priv = NULL,
16331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.users = 1,
16341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.readers = 1,
16351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.writers = 1,
16361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.fops = &dvb_ca_fops,
16371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
16381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* ******************************************************************************** */
16411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Initialisation/shutdown functions */
16421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/**
16451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Initialise a new DVB CA EN50221 interface device.
16461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
16471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @param dvb_adapter DVB adapter to attach the new CA device to.
16481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @param ca The dvb_ca instance.
16491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @param flags Flags describing the CA device (DVB_CA_FLAG_*).
16501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @param slot_count Number of slots supported.
16511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
16521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @return 0 on success, nonzero on failure
16531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
16541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsint dvb_ca_en50221_init(struct dvb_adapter *dvb_adapter,
16551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			struct dvb_ca_en50221 *pubca, int flags, int slot_count)
16561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
16571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int ret;
16581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct dvb_ca_private *ca = NULL;
16591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i;
16601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
166146b4f7c176a2dd4c60ddb7c80bd09ea2f3220674Harvey Harrison	dprintk("%s\n", __func__);
16621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (slot_count < 1)
16641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
16651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* initialise the system data */
16677408187d223f63d46a13b6a35b8f96b032c2f623Panagiotis Issaris	if ((ca = kzalloc(sizeof(struct dvb_ca_private), GFP_KERNEL)) == NULL) {
16681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ret = -ENOMEM;
16691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto error;
16701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
16711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ca->pub = pubca;
16721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ca->flags = flags;
16731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ca->slot_count = slot_count;
16747408187d223f63d46a13b6a35b8f96b032c2f623Panagiotis Issaris	if ((ca->slot_info = kcalloc(slot_count, sizeof(struct dvb_ca_slot), GFP_KERNEL)) == NULL) {
16751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ret = -ENOMEM;
16761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto error;
16771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
16781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	init_waitqueue_head(&ca->wait_queue);
16791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ca->open = 0;
16801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ca->wakeup = 0;
16811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ca->next_read_slot = 0;
16821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	pubca->private = ca;
16831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* register the DVB device */
16851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ret = dvb_register_device(dvb_adapter, &ca->dvbdev, &dvbdev_ca, ca, DVB_DEVICE_CA);
16861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ret)
16871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto error;
16881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* now initialise each slot */
16901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = 0; i < slot_count; i++) {
16911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		memset(&ca->slot_info[i], 0, sizeof(struct dvb_ca_slot));
16921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ca->slot_info[i].slot_state = DVB_CA_SLOTSTATE_NONE;
16931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		atomic_set(&ca->slot_info[i].camchange_count, 0);
16941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ca->slot_info[i].camchange_type = DVB_CA_EN50221_CAMCHANGE_REMOVED;
1695d7e43844e40e07cadc48f1733b9738659f83b38cMatthias Dahl		mutex_init(&ca->slot_info[i].slot_lock);
16961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
16971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (signal_pending(current)) {
16991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ret = -EINTR;
17001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto error;
17011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
17021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	mb();
17031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* create a kthread for monitoring this CA device */
17059320874a3e6aea7044a4a7eedeab13db990424abChristoph Hellwig	ca->thread = kthread_run(dvb_ca_en50221_thread, ca, "kdvb-ca-%i:%i",
17069320874a3e6aea7044a4a7eedeab13db990424abChristoph Hellwig				 ca->dvbdev->adapter->num, ca->dvbdev->id);
17079320874a3e6aea7044a4a7eedeab13db990424abChristoph Hellwig	if (IS_ERR(ca->thread)) {
17089320874a3e6aea7044a4a7eedeab13db990424abChristoph Hellwig		ret = PTR_ERR(ca->thread);
17099320874a3e6aea7044a4a7eedeab13db990424abChristoph Hellwig		printk("dvb_ca_init: failed to start kernel_thread (%d)\n",
17109320874a3e6aea7044a4a7eedeab13db990424abChristoph Hellwig			ret);
17111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto error;
17121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
17131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
17141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1715ded928468407c8e08dcb6aedb91aaa97b80d5752Andrew de Quinceyerror:
17161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ca != NULL) {
17171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (ca->dvbdev != NULL)
17181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			dvb_unregister_device(ca->dvbdev);
17191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		kfree(ca->slot_info);
17201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		kfree(ca);
17211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
17221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	pubca->private = NULL;
17231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return ret;
17241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
17251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsEXPORT_SYMBOL(dvb_ca_en50221_release);
17261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/**
17301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Release a DVB CA EN50221 interface device.
17311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
17321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @param ca_dev The dvb_device_t instance for the CA device.
17331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @param ca The associated dvb_ca instance.
17341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
17351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsvoid dvb_ca_en50221_release(struct dvb_ca_en50221 *pubca)
17361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
17370c53c70f6afa2d3f4d416d8c0e9a219c2f6b7cb6Johannes Stezenbach	struct dvb_ca_private *ca = pubca->private;
17381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i;
17391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
174046b4f7c176a2dd4c60ddb7c80bd09ea2f3220674Harvey Harrison	dprintk("%s\n", __func__);
17411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* shutdown the thread if there was one */
17439320874a3e6aea7044a4a7eedeab13db990424abChristoph Hellwig	kthread_stop(ca->thread);
17441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = 0; i < ca->slot_count; i++) {
17461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dvb_ca_en50221_slot_shutdown(ca, i);
174735dc0fefb18eea1b4180a8fafbb83db6a9b7c401Michael Krufky		vfree(ca->slot_info[i].rx_buffer.data);
17481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
17491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree(ca->slot_info);
17501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dvb_unregister_device(ca->dvbdev);
17511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree(ca);
17521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	pubca->private = NULL;
17531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1754