1a0c7056fSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
23d6c2bc0SMauro Carvalho Chehab /*
33d6c2bc0SMauro Carvalho Chehab  * dvb_ca.c: generic DVB functions for EN50221 CAM interfaces
43d6c2bc0SMauro Carvalho Chehab  *
53d6c2bc0SMauro Carvalho Chehab  * Copyright (C) 2004 Andrew de Quincey
63d6c2bc0SMauro Carvalho Chehab  *
73d6c2bc0SMauro Carvalho Chehab  * Parts of this file were based on sources as follows:
83d6c2bc0SMauro Carvalho Chehab  *
93d6c2bc0SMauro Carvalho Chehab  * Copyright (C) 2003 Ralph Metzler <rjkm@metzlerbros.de>
103d6c2bc0SMauro Carvalho Chehab  *
113d6c2bc0SMauro Carvalho Chehab  * based on code:
123d6c2bc0SMauro Carvalho Chehab  *
133d6c2bc0SMauro Carvalho Chehab  * Copyright (C) 1999-2002 Ralph  Metzler
143d6c2bc0SMauro Carvalho Chehab  *                       & Marcus Metzler for convergence integrated media GmbH
153d6c2bc0SMauro Carvalho Chehab  */
163d6c2bc0SMauro Carvalho Chehab 
17b3ad24d2SMauro Carvalho Chehab #define pr_fmt(fmt) "dvb_ca_en50221: " fmt
18b3ad24d2SMauro Carvalho Chehab 
193d6c2bc0SMauro Carvalho Chehab #include <linux/errno.h>
203d6c2bc0SMauro Carvalho Chehab #include <linux/slab.h>
213d6c2bc0SMauro Carvalho Chehab #include <linux/list.h>
223d6c2bc0SMauro Carvalho Chehab #include <linux/module.h>
234f5ab5d7SMauro Carvalho Chehab #include <linux/nospec.h>
243d6c2bc0SMauro Carvalho Chehab #include <linux/vmalloc.h>
253d6c2bc0SMauro Carvalho Chehab #include <linux/delay.h>
263d6c2bc0SMauro Carvalho Chehab #include <linux/spinlock.h>
27174cd4b1SIngo Molnar #include <linux/sched/signal.h>
283d6c2bc0SMauro Carvalho Chehab #include <linux/kthread.h>
293d6c2bc0SMauro Carvalho Chehab 
30fada1935SMauro Carvalho Chehab #include <media/dvb_ca_en50221.h>
31fada1935SMauro Carvalho Chehab #include <media/dvb_ringbuffer.h>
323d6c2bc0SMauro Carvalho Chehab 
333d6c2bc0SMauro Carvalho Chehab static int dvb_ca_en50221_debug;
343d6c2bc0SMauro Carvalho Chehab 
353d6c2bc0SMauro Carvalho Chehab module_param_named(cam_debug, dvb_ca_en50221_debug, int, 0644);
363d6c2bc0SMauro Carvalho Chehab MODULE_PARM_DESC(cam_debug, "enable verbose debug messages");
373d6c2bc0SMauro Carvalho Chehab 
38b3ad24d2SMauro Carvalho Chehab #define dprintk(fmt, arg...) do {					\
39b3ad24d2SMauro Carvalho Chehab 	if (dvb_ca_en50221_debug)					\
40b3ad24d2SMauro Carvalho Chehab 		printk(KERN_DEBUG pr_fmt("%s: " fmt), __func__, ##arg);\
41b3ad24d2SMauro Carvalho Chehab } while (0)
423d6c2bc0SMauro Carvalho Chehab 
433d6c2bc0SMauro Carvalho Chehab #define INIT_TIMEOUT_SECS 10
443d6c2bc0SMauro Carvalho Chehab 
453d6c2bc0SMauro Carvalho Chehab #define HOST_LINK_BUF_SIZE 0x200
463d6c2bc0SMauro Carvalho Chehab 
473d6c2bc0SMauro Carvalho Chehab #define RX_BUFFER_SIZE 65535
483d6c2bc0SMauro Carvalho Chehab 
493d6c2bc0SMauro Carvalho Chehab #define MAX_RX_PACKETS_PER_ITERATION 10
503d6c2bc0SMauro Carvalho Chehab 
513d6c2bc0SMauro Carvalho Chehab #define CTRLIF_DATA      0
523d6c2bc0SMauro Carvalho Chehab #define CTRLIF_COMMAND   1
533d6c2bc0SMauro Carvalho Chehab #define CTRLIF_STATUS    1
543d6c2bc0SMauro Carvalho Chehab #define CTRLIF_SIZE_LOW  2
553d6c2bc0SMauro Carvalho Chehab #define CTRLIF_SIZE_HIGH 3
563d6c2bc0SMauro Carvalho Chehab 
573d6c2bc0SMauro Carvalho Chehab #define CMDREG_HC        1	/* Host control */
583d6c2bc0SMauro Carvalho Chehab #define CMDREG_SW        2	/* Size write */
593d6c2bc0SMauro Carvalho Chehab #define CMDREG_SR        4	/* Size read */
603d6c2bc0SMauro Carvalho Chehab #define CMDREG_RS        8	/* Reset interface */
613d6c2bc0SMauro Carvalho Chehab #define CMDREG_FRIE   0x40	/* Enable FR interrupt */
623d6c2bc0SMauro Carvalho Chehab #define CMDREG_DAIE   0x80	/* Enable DA interrupt */
633d6c2bc0SMauro Carvalho Chehab #define IRQEN (CMDREG_DAIE)
643d6c2bc0SMauro Carvalho Chehab 
653d6c2bc0SMauro Carvalho Chehab #define STATUSREG_RE     1	/* read error */
663d6c2bc0SMauro Carvalho Chehab #define STATUSREG_WE     2	/* write error */
673d6c2bc0SMauro Carvalho Chehab #define STATUSREG_FR  0x40	/* module free */
683d6c2bc0SMauro Carvalho Chehab #define STATUSREG_DA  0x80	/* data available */
693d6c2bc0SMauro Carvalho Chehab 
703d6c2bc0SMauro Carvalho Chehab #define DVB_CA_SLOTSTATE_NONE           0
713d6c2bc0SMauro Carvalho Chehab #define DVB_CA_SLOTSTATE_UNINITIALISED  1
723d6c2bc0SMauro Carvalho Chehab #define DVB_CA_SLOTSTATE_RUNNING        2
733d6c2bc0SMauro Carvalho Chehab #define DVB_CA_SLOTSTATE_INVALID        3
743d6c2bc0SMauro Carvalho Chehab #define DVB_CA_SLOTSTATE_WAITREADY      4
753d6c2bc0SMauro Carvalho Chehab #define DVB_CA_SLOTSTATE_VALIDATE       5
763d6c2bc0SMauro Carvalho Chehab #define DVB_CA_SLOTSTATE_WAITFR         6
773d6c2bc0SMauro Carvalho Chehab #define DVB_CA_SLOTSTATE_LINKINIT       7
783d6c2bc0SMauro Carvalho Chehab 
793d6c2bc0SMauro Carvalho Chehab /* Information on a CA slot */
803d6c2bc0SMauro Carvalho Chehab struct dvb_ca_slot {
813d6c2bc0SMauro Carvalho Chehab 	/* current state of the CAM */
823d6c2bc0SMauro Carvalho Chehab 	int slot_state;
833d6c2bc0SMauro Carvalho Chehab 
843d6c2bc0SMauro Carvalho Chehab 	/* mutex used for serializing access to one CI slot */
853d6c2bc0SMauro Carvalho Chehab 	struct mutex slot_lock;
863d6c2bc0SMauro Carvalho Chehab 
873d6c2bc0SMauro Carvalho Chehab 	/* Number of CAMCHANGES that have occurred since last processing */
883d6c2bc0SMauro Carvalho Chehab 	atomic_t camchange_count;
893d6c2bc0SMauro Carvalho Chehab 
903d6c2bc0SMauro Carvalho Chehab 	/* Type of last CAMCHANGE */
913d6c2bc0SMauro Carvalho Chehab 	int camchange_type;
923d6c2bc0SMauro Carvalho Chehab 
933d6c2bc0SMauro Carvalho Chehab 	/* base address of CAM config */
943d6c2bc0SMauro Carvalho Chehab 	u32 config_base;
953d6c2bc0SMauro Carvalho Chehab 
963d6c2bc0SMauro Carvalho Chehab 	/* value to write into Config Control register */
973d6c2bc0SMauro Carvalho Chehab 	u8 config_option;
983d6c2bc0SMauro Carvalho Chehab 
993d6c2bc0SMauro Carvalho Chehab 	/* if 1, the CAM supports DA IRQs */
1003d6c2bc0SMauro Carvalho Chehab 	u8 da_irq_supported:1;
1013d6c2bc0SMauro Carvalho Chehab 
1023d6c2bc0SMauro Carvalho Chehab 	/* size of the buffer to use when talking to the CAM */
1033d6c2bc0SMauro Carvalho Chehab 	int link_buf_size;
1043d6c2bc0SMauro Carvalho Chehab 
1053d6c2bc0SMauro Carvalho Chehab 	/* buffer for incoming packets */
1063d6c2bc0SMauro Carvalho Chehab 	struct dvb_ringbuffer rx_buffer;
1073d6c2bc0SMauro Carvalho Chehab 
1083d6c2bc0SMauro Carvalho Chehab 	/* timer used during various states of the slot */
1093d6c2bc0SMauro Carvalho Chehab 	unsigned long timeout;
1103d6c2bc0SMauro Carvalho Chehab };
1113d6c2bc0SMauro Carvalho Chehab 
1123d6c2bc0SMauro Carvalho Chehab /* Private CA-interface information */
1133d6c2bc0SMauro Carvalho Chehab struct dvb_ca_private {
114da677fe1SMax Kellermann 	struct kref refcount;
1153d6c2bc0SMauro Carvalho Chehab 
1163d6c2bc0SMauro Carvalho Chehab 	/* pointer back to the public data structure */
1173d6c2bc0SMauro Carvalho Chehab 	struct dvb_ca_en50221 *pub;
1183d6c2bc0SMauro Carvalho Chehab 
1193d6c2bc0SMauro Carvalho Chehab 	/* the DVB device */
1203d6c2bc0SMauro Carvalho Chehab 	struct dvb_device *dvbdev;
1213d6c2bc0SMauro Carvalho Chehab 
1223d6c2bc0SMauro Carvalho Chehab 	/* Flags describing the interface (DVB_CA_FLAG_*) */
1233d6c2bc0SMauro Carvalho Chehab 	u32 flags;
1243d6c2bc0SMauro Carvalho Chehab 
1253d6c2bc0SMauro Carvalho Chehab 	/* number of slots supported by this CA interface */
1263d6c2bc0SMauro Carvalho Chehab 	unsigned int slot_count;
1273d6c2bc0SMauro Carvalho Chehab 
1283d6c2bc0SMauro Carvalho Chehab 	/* information on each slot */
1293d6c2bc0SMauro Carvalho Chehab 	struct dvb_ca_slot *slot_info;
1303d6c2bc0SMauro Carvalho Chehab 
1313d6c2bc0SMauro Carvalho Chehab 	/* wait queues for read() and write() operations */
1323d6c2bc0SMauro Carvalho Chehab 	wait_queue_head_t wait_queue;
1333d6c2bc0SMauro Carvalho Chehab 
1343d6c2bc0SMauro Carvalho Chehab 	/* PID of the monitoring thread */
1353d6c2bc0SMauro Carvalho Chehab 	struct task_struct *thread;
1363d6c2bc0SMauro Carvalho Chehab 
1373d6c2bc0SMauro Carvalho Chehab 	/* Flag indicating if the CA device is open */
1383d6c2bc0SMauro Carvalho Chehab 	unsigned int open:1;
1393d6c2bc0SMauro Carvalho Chehab 
1403d6c2bc0SMauro Carvalho Chehab 	/* Flag indicating the thread should wake up now */
1413d6c2bc0SMauro Carvalho Chehab 	unsigned int wakeup:1;
1423d6c2bc0SMauro Carvalho Chehab 
1433d6c2bc0SMauro Carvalho Chehab 	/* Delay the main thread should use */
1443d6c2bc0SMauro Carvalho Chehab 	unsigned long delay;
1453d6c2bc0SMauro Carvalho Chehab 
14696375b7aSJasmin Jessich 	/*
14796375b7aSJasmin Jessich 	 * Slot to start looking for data to read from in the next user-space
14896375b7aSJasmin Jessich 	 * read operation
14996375b7aSJasmin Jessich 	 */
1503d6c2bc0SMauro Carvalho Chehab 	int next_read_slot;
15130ad64b8SNikolaus Schulz 
15230ad64b8SNikolaus Schulz 	/* mutex serializing ioctls */
15330ad64b8SNikolaus Schulz 	struct mutex ioctl_mutex;
154*280a8ab8SHyunwoo Kim 
155*280a8ab8SHyunwoo Kim 	/* A mutex used when a device is disconnected */
156*280a8ab8SHyunwoo Kim 	struct mutex remove_mutex;
157*280a8ab8SHyunwoo Kim 
158*280a8ab8SHyunwoo Kim 	/* Whether the device is disconnected */
159*280a8ab8SHyunwoo Kim 	int exit;
1603d6c2bc0SMauro Carvalho Chehab };
1613d6c2bc0SMauro Carvalho Chehab 
dvb_ca_private_free(struct dvb_ca_private * ca)162bd3df3c5SMax Kellermann static void dvb_ca_private_free(struct dvb_ca_private *ca)
163bd3df3c5SMax Kellermann {
164bd3df3c5SMax Kellermann 	unsigned int i;
165bd3df3c5SMax Kellermann 
1660fc044b2SLin Ma 	dvb_device_put(ca->dvbdev);
167bd3df3c5SMax Kellermann 	for (i = 0; i < ca->slot_count; i++)
168bd3df3c5SMax Kellermann 		vfree(ca->slot_info[i].rx_buffer.data);
169bd3df3c5SMax Kellermann 
170bd3df3c5SMax Kellermann 	kfree(ca->slot_info);
171bd3df3c5SMax Kellermann 	kfree(ca);
172bd3df3c5SMax Kellermann }
173bd3df3c5SMax Kellermann 
dvb_ca_private_release(struct kref * ref)174da677fe1SMax Kellermann static void dvb_ca_private_release(struct kref *ref)
175da677fe1SMax Kellermann {
176fbabbdddSJasmin Jessich 	struct dvb_ca_private *ca;
177fbabbdddSJasmin Jessich 
178fbabbdddSJasmin Jessich 	ca = container_of(ref, struct dvb_ca_private, refcount);
179da677fe1SMax Kellermann 	dvb_ca_private_free(ca);
180da677fe1SMax Kellermann }
181da677fe1SMax Kellermann 
dvb_ca_private_get(struct dvb_ca_private * ca)182da677fe1SMax Kellermann static void dvb_ca_private_get(struct dvb_ca_private *ca)
183da677fe1SMax Kellermann {
184da677fe1SMax Kellermann 	kref_get(&ca->refcount);
185da677fe1SMax Kellermann }
186da677fe1SMax Kellermann 
dvb_ca_private_put(struct dvb_ca_private * ca)187da677fe1SMax Kellermann static void dvb_ca_private_put(struct dvb_ca_private *ca)
188da677fe1SMax Kellermann {
189da677fe1SMax Kellermann 	kref_put(&ca->refcount, dvb_ca_private_release);
190da677fe1SMax Kellermann }
191da677fe1SMax Kellermann 
1923d6c2bc0SMauro Carvalho Chehab static void dvb_ca_en50221_thread_wakeup(struct dvb_ca_private *ca);
1938ec6107aSJasmin Jessich static int dvb_ca_en50221_read_data(struct dvb_ca_private *ca, int slot,
1948ec6107aSJasmin Jessich 				    u8 *ebuf, int ecount);
1958ec6107aSJasmin Jessich static int dvb_ca_en50221_write_data(struct dvb_ca_private *ca, int slot,
196a4315e5bSYongSu Yoo 				     u8 *ebuf, int ecount, int size_write_flag);
1973d6c2bc0SMauro Carvalho Chehab 
1983d6c2bc0SMauro Carvalho Chehab /**
199a4184b4fSHans Verkuil  * findstr - Safely find needle in haystack.
2003d6c2bc0SMauro Carvalho Chehab  *
201fbefb1a8SMauro Carvalho Chehab  * @haystack: Buffer to look in.
202fbefb1a8SMauro Carvalho Chehab  * @hlen: Number of bytes in haystack.
203fbefb1a8SMauro Carvalho Chehab  * @needle: Buffer to find.
204fbefb1a8SMauro Carvalho Chehab  * @nlen: Number of bytes in needle.
20546e42a30SMauro Carvalho Chehab  * return: Pointer into haystack needle was found at, or NULL if not found.
2063d6c2bc0SMauro Carvalho Chehab  */
findstr(char * haystack,int hlen,char * needle,int nlen)2073d6c2bc0SMauro Carvalho Chehab static char *findstr(char *haystack, int hlen, char *needle, int nlen)
2083d6c2bc0SMauro Carvalho Chehab {
2093d6c2bc0SMauro Carvalho Chehab 	int i;
2103d6c2bc0SMauro Carvalho Chehab 
2113d6c2bc0SMauro Carvalho Chehab 	if (hlen < nlen)
2123d6c2bc0SMauro Carvalho Chehab 		return NULL;
2133d6c2bc0SMauro Carvalho Chehab 
2143d6c2bc0SMauro Carvalho Chehab 	for (i = 0; i <= hlen - nlen; i++) {
2153d6c2bc0SMauro Carvalho Chehab 		if (!strncmp(haystack + i, needle, nlen))
2163d6c2bc0SMauro Carvalho Chehab 			return haystack + i;
2173d6c2bc0SMauro Carvalho Chehab 	}
2183d6c2bc0SMauro Carvalho Chehab 
2193d6c2bc0SMauro Carvalho Chehab 	return NULL;
2203d6c2bc0SMauro Carvalho Chehab }
2213d6c2bc0SMauro Carvalho Chehab 
22296375b7aSJasmin Jessich /* ************************************************************************** */
2233d6c2bc0SMauro Carvalho Chehab /* EN50221 physical interface functions */
2243d6c2bc0SMauro Carvalho Chehab 
22546e42a30SMauro Carvalho Chehab /*
226fbefb1a8SMauro Carvalho Chehab  * dvb_ca_en50221_check_camstatus - Check CAM status.
2273d6c2bc0SMauro Carvalho Chehab  */
dvb_ca_en50221_check_camstatus(struct dvb_ca_private * ca,int slot)2283d6c2bc0SMauro Carvalho Chehab static int dvb_ca_en50221_check_camstatus(struct dvb_ca_private *ca, int slot)
2293d6c2bc0SMauro Carvalho Chehab {
230a75aa90cSJasmin Jessich 	struct dvb_ca_slot *sl = &ca->slot_info[slot];
2313d6c2bc0SMauro Carvalho Chehab 	int slot_status;
2323d6c2bc0SMauro Carvalho Chehab 	int cam_present_now;
2333d6c2bc0SMauro Carvalho Chehab 	int cam_changed;
2343d6c2bc0SMauro Carvalho Chehab 
2353d6c2bc0SMauro Carvalho Chehab 	/* IRQ mode */
2367bd8cc8fSJasmin Jessich 	if (ca->flags & DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE)
237a75aa90cSJasmin Jessich 		return (atomic_read(&sl->camchange_count) != 0);
2383d6c2bc0SMauro Carvalho Chehab 
2393d6c2bc0SMauro Carvalho Chehab 	/* poll mode */
2403d6c2bc0SMauro Carvalho Chehab 	slot_status = ca->pub->poll_slot_status(ca->pub, slot, ca->open);
2413d6c2bc0SMauro Carvalho Chehab 
2423d6c2bc0SMauro Carvalho Chehab 	cam_present_now = (slot_status & DVB_CA_EN50221_POLL_CAM_PRESENT) ? 1 : 0;
2433d6c2bc0SMauro Carvalho Chehab 	cam_changed = (slot_status & DVB_CA_EN50221_POLL_CAM_CHANGED) ? 1 : 0;
2443d6c2bc0SMauro Carvalho Chehab 	if (!cam_changed) {
245a75aa90cSJasmin Jessich 		int cam_present_old = (sl->slot_state != DVB_CA_SLOTSTATE_NONE);
246a75aa90cSJasmin Jessich 
2473d6c2bc0SMauro Carvalho Chehab 		cam_changed = (cam_present_now != cam_present_old);
2483d6c2bc0SMauro Carvalho Chehab 	}
2493d6c2bc0SMauro Carvalho Chehab 
2503d6c2bc0SMauro Carvalho Chehab 	if (cam_changed) {
2517bd8cc8fSJasmin Jessich 		if (!cam_present_now)
252a75aa90cSJasmin Jessich 			sl->camchange_type = DVB_CA_EN50221_CAMCHANGE_REMOVED;
2537bd8cc8fSJasmin Jessich 		else
254a75aa90cSJasmin Jessich 			sl->camchange_type = DVB_CA_EN50221_CAMCHANGE_INSERTED;
255a75aa90cSJasmin Jessich 		atomic_set(&sl->camchange_count, 1);
2563d6c2bc0SMauro Carvalho Chehab 	} else {
257a75aa90cSJasmin Jessich 		if ((sl->slot_state == DVB_CA_SLOTSTATE_WAITREADY) &&
2583d6c2bc0SMauro Carvalho Chehab 		    (slot_status & DVB_CA_EN50221_POLL_CAM_READY)) {
259224457a9SJasmin Jessich 			/* move to validate state if reset is completed */
260a75aa90cSJasmin Jessich 			sl->slot_state = DVB_CA_SLOTSTATE_VALIDATE;
2613d6c2bc0SMauro Carvalho Chehab 		}
2623d6c2bc0SMauro Carvalho Chehab 	}
2633d6c2bc0SMauro Carvalho Chehab 
2643d6c2bc0SMauro Carvalho Chehab 	return cam_changed;
2653d6c2bc0SMauro Carvalho Chehab }
2663d6c2bc0SMauro Carvalho Chehab 
2673d6c2bc0SMauro Carvalho Chehab /**
268fbefb1a8SMauro Carvalho Chehab  * dvb_ca_en50221_wait_if_status - Wait for flags to become set on the STATUS
269fbefb1a8SMauro Carvalho Chehab  *	 register on a CAM interface, checking for errors and timeout.
2703d6c2bc0SMauro Carvalho Chehab  *
271fbefb1a8SMauro Carvalho Chehab  * @ca: CA instance.
272fbefb1a8SMauro Carvalho Chehab  * @slot: Slot on interface.
273fbefb1a8SMauro Carvalho Chehab  * @waitfor: Flags to wait for.
27446e42a30SMauro Carvalho Chehab  * @timeout_hz: Timeout in milliseconds.
2753d6c2bc0SMauro Carvalho Chehab  *
27646e42a30SMauro Carvalho Chehab  * return: 0 on success, nonzero on error.
2773d6c2bc0SMauro Carvalho Chehab  */
dvb_ca_en50221_wait_if_status(struct dvb_ca_private * ca,int slot,u8 waitfor,int timeout_hz)2783d6c2bc0SMauro Carvalho Chehab static int dvb_ca_en50221_wait_if_status(struct dvb_ca_private *ca, int slot,
2793d6c2bc0SMauro Carvalho Chehab 					 u8 waitfor, int timeout_hz)
2803d6c2bc0SMauro Carvalho Chehab {
2813d6c2bc0SMauro Carvalho Chehab 	unsigned long timeout;
2823d6c2bc0SMauro Carvalho Chehab 	unsigned long start;
2833d6c2bc0SMauro Carvalho Chehab 
2843d6c2bc0SMauro Carvalho Chehab 	dprintk("%s\n", __func__);
2853d6c2bc0SMauro Carvalho Chehab 
2863d6c2bc0SMauro Carvalho Chehab 	/* loop until timeout elapsed */
2873d6c2bc0SMauro Carvalho Chehab 	start = jiffies;
2883d6c2bc0SMauro Carvalho Chehab 	timeout = jiffies + timeout_hz;
2893d6c2bc0SMauro Carvalho Chehab 	while (1) {
290fbabbdddSJasmin Jessich 		int res;
291fbabbdddSJasmin Jessich 
2923d6c2bc0SMauro Carvalho Chehab 		/* read the status and check for error */
293fbabbdddSJasmin Jessich 		res = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS);
2943d6c2bc0SMauro Carvalho Chehab 		if (res < 0)
2953d6c2bc0SMauro Carvalho Chehab 			return -EIO;
2963d6c2bc0SMauro Carvalho Chehab 
2973d6c2bc0SMauro Carvalho Chehab 		/* if we got the flags, it was successful! */
2983d6c2bc0SMauro Carvalho Chehab 		if (res & waitfor) {
299b3ad24d2SMauro Carvalho Chehab 			dprintk("%s succeeded timeout:%lu\n",
300b3ad24d2SMauro Carvalho Chehab 				__func__, jiffies - start);
3013d6c2bc0SMauro Carvalho Chehab 			return 0;
3023d6c2bc0SMauro Carvalho Chehab 		}
3033d6c2bc0SMauro Carvalho Chehab 
3043d6c2bc0SMauro Carvalho Chehab 		/* check for timeout */
3057bd8cc8fSJasmin Jessich 		if (time_after(jiffies, timeout))
3063d6c2bc0SMauro Carvalho Chehab 			break;
3073d6c2bc0SMauro Carvalho Chehab 
3083d6c2bc0SMauro Carvalho Chehab 		/* wait for a bit */
309b9af29e1SJasmin Jessich 		usleep_range(1000, 1100);
3103d6c2bc0SMauro Carvalho Chehab 	}
3113d6c2bc0SMauro Carvalho Chehab 
3123d6c2bc0SMauro Carvalho Chehab 	dprintk("%s failed timeout:%lu\n", __func__, jiffies - start);
3133d6c2bc0SMauro Carvalho Chehab 
3143d6c2bc0SMauro Carvalho Chehab 	/* if we get here, we've timed out */
3153d6c2bc0SMauro Carvalho Chehab 	return -ETIMEDOUT;
3163d6c2bc0SMauro Carvalho Chehab }
3173d6c2bc0SMauro Carvalho Chehab 
3183d6c2bc0SMauro Carvalho Chehab /**
319fbefb1a8SMauro Carvalho Chehab  * dvb_ca_en50221_link_init - Initialise the link layer connection to a CAM.
3203d6c2bc0SMauro Carvalho Chehab  *
321fbefb1a8SMauro Carvalho Chehab  * @ca: CA instance.
322fbefb1a8SMauro Carvalho Chehab  * @slot: Slot id.
3233d6c2bc0SMauro Carvalho Chehab  *
32446e42a30SMauro Carvalho Chehab  * return: 0 on success, nonzero on failure.
3253d6c2bc0SMauro Carvalho Chehab  */
dvb_ca_en50221_link_init(struct dvb_ca_private * ca,int slot)3263d6c2bc0SMauro Carvalho Chehab static int dvb_ca_en50221_link_init(struct dvb_ca_private *ca, int slot)
3273d6c2bc0SMauro Carvalho Chehab {
328a75aa90cSJasmin Jessich 	struct dvb_ca_slot *sl = &ca->slot_info[slot];
3293d6c2bc0SMauro Carvalho Chehab 	int ret;
3303d6c2bc0SMauro Carvalho Chehab 	int buf_size;
3313d6c2bc0SMauro Carvalho Chehab 	u8 buf[2];
3323d6c2bc0SMauro Carvalho Chehab 
3333d6c2bc0SMauro Carvalho Chehab 	dprintk("%s\n", __func__);
3343d6c2bc0SMauro Carvalho Chehab 
3353d6c2bc0SMauro Carvalho Chehab 	/* we'll be determining these during this function */
336a75aa90cSJasmin Jessich 	sl->da_irq_supported = 0;
3373d6c2bc0SMauro Carvalho Chehab 
338a1ca23d1SJasmin Jessich 	/*
339a1ca23d1SJasmin Jessich 	 * set the host link buffer size temporarily. it will be overwritten
340a1ca23d1SJasmin Jessich 	 * with the real negotiated size later.
341a1ca23d1SJasmin Jessich 	 */
342a75aa90cSJasmin Jessich 	sl->link_buf_size = 2;
3433d6c2bc0SMauro Carvalho Chehab 
3443d6c2bc0SMauro Carvalho Chehab 	/* read the buffer size from the CAM */
345bacba9e5SJasmin Jessich 	ret = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND,
346bacba9e5SJasmin Jessich 					 IRQEN | CMDREG_SR);
347bacba9e5SJasmin Jessich 	if (ret)
3483d6c2bc0SMauro Carvalho Chehab 		return ret;
3495dbddc99SRalph Metzler 	ret = dvb_ca_en50221_wait_if_status(ca, slot, STATUSREG_DA, HZ);
350bacba9e5SJasmin Jessich 	if (ret)
3513d6c2bc0SMauro Carvalho Chehab 		return ret;
352bacba9e5SJasmin Jessich 	ret = dvb_ca_en50221_read_data(ca, slot, buf, 2);
353bacba9e5SJasmin Jessich 	if (ret != 2)
3543d6c2bc0SMauro Carvalho Chehab 		return -EIO;
355bacba9e5SJasmin Jessich 	ret = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, IRQEN);
356bacba9e5SJasmin Jessich 	if (ret)
3573d6c2bc0SMauro Carvalho Chehab 		return ret;
3583d6c2bc0SMauro Carvalho Chehab 
35996375b7aSJasmin Jessich 	/*
36096375b7aSJasmin Jessich 	 * store it, and choose the minimum of our buffer and the CAM's buffer
36196375b7aSJasmin Jessich 	 * size
36296375b7aSJasmin Jessich 	 */
3633d6c2bc0SMauro Carvalho Chehab 	buf_size = (buf[0] << 8) | buf[1];
3643d6c2bc0SMauro Carvalho Chehab 	if (buf_size > HOST_LINK_BUF_SIZE)
3653d6c2bc0SMauro Carvalho Chehab 		buf_size = HOST_LINK_BUF_SIZE;
366a75aa90cSJasmin Jessich 	sl->link_buf_size = buf_size;
3673d6c2bc0SMauro Carvalho Chehab 	buf[0] = buf_size >> 8;
3683d6c2bc0SMauro Carvalho Chehab 	buf[1] = buf_size & 0xff;
3693d6c2bc0SMauro Carvalho Chehab 	dprintk("Chosen link buffer size of %i\n", buf_size);
3703d6c2bc0SMauro Carvalho Chehab 
3713d6c2bc0SMauro Carvalho Chehab 	/* write the buffer size to the CAM */
372bacba9e5SJasmin Jessich 	ret = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND,
373bacba9e5SJasmin Jessich 					 IRQEN | CMDREG_SW);
374bacba9e5SJasmin Jessich 	if (ret)
3753d6c2bc0SMauro Carvalho Chehab 		return ret;
376bacba9e5SJasmin Jessich 	ret = dvb_ca_en50221_wait_if_status(ca, slot, STATUSREG_FR, HZ / 10);
377bacba9e5SJasmin Jessich 	if (ret)
3783d6c2bc0SMauro Carvalho Chehab 		return ret;
379a4315e5bSYongSu Yoo 	ret = dvb_ca_en50221_write_data(ca, slot, buf, 2, CMDREG_SW);
380bacba9e5SJasmin Jessich 	if (ret != 2)
3813d6c2bc0SMauro Carvalho Chehab 		return -EIO;
382bacba9e5SJasmin Jessich 	ret = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, IRQEN);
383bacba9e5SJasmin Jessich 	if (ret)
3843d6c2bc0SMauro Carvalho Chehab 		return ret;
3853d6c2bc0SMauro Carvalho Chehab 
3863d6c2bc0SMauro Carvalho Chehab 	/* success */
3873d6c2bc0SMauro Carvalho Chehab 	return 0;
3883d6c2bc0SMauro Carvalho Chehab }
3893d6c2bc0SMauro Carvalho Chehab 
3903d6c2bc0SMauro Carvalho Chehab /**
391fbefb1a8SMauro Carvalho Chehab  * dvb_ca_en50221_read_tuple - Read a tuple from attribute memory.
3923d6c2bc0SMauro Carvalho Chehab  *
393fbefb1a8SMauro Carvalho Chehab  * @ca: CA instance.
394fbefb1a8SMauro Carvalho Chehab  * @slot: Slot id.
395fbefb1a8SMauro Carvalho Chehab  * @address: Address to read from. Updated.
39646e42a30SMauro Carvalho Chehab  * @tuple_type: Tuple id byte. Updated.
39746e42a30SMauro Carvalho Chehab  * @tuple_length: Tuple length. Updated.
398fbefb1a8SMauro Carvalho Chehab  * @tuple: Dest buffer for tuple (must be 256 bytes). Updated.
3993d6c2bc0SMauro Carvalho Chehab  *
40046e42a30SMauro Carvalho Chehab  * return: 0 on success, nonzero on error.
4013d6c2bc0SMauro Carvalho Chehab  */
dvb_ca_en50221_read_tuple(struct dvb_ca_private * ca,int slot,int * address,int * tuple_type,int * tuple_length,u8 * tuple)4023d6c2bc0SMauro Carvalho Chehab static int dvb_ca_en50221_read_tuple(struct dvb_ca_private *ca, int slot,
403bacba9e5SJasmin Jessich 				     int *address, int *tuple_type,
404bacba9e5SJasmin Jessich 				     int *tuple_length, u8 *tuple)
4053d6c2bc0SMauro Carvalho Chehab {
4063d6c2bc0SMauro Carvalho Chehab 	int i;
407bacba9e5SJasmin Jessich 	int _tuple_type;
408bacba9e5SJasmin Jessich 	int _tuple_length;
4093d6c2bc0SMauro Carvalho Chehab 	int _address = *address;
4103d6c2bc0SMauro Carvalho Chehab 
4113d6c2bc0SMauro Carvalho Chehab 	/* grab the next tuple length and type */
412bacba9e5SJasmin Jessich 	_tuple_type = ca->pub->read_attribute_mem(ca->pub, slot, _address);
413bacba9e5SJasmin Jessich 	if (_tuple_type < 0)
414bacba9e5SJasmin Jessich 		return _tuple_type;
415bacba9e5SJasmin Jessich 	if (_tuple_type == 0xff) {
416bacba9e5SJasmin Jessich 		dprintk("END OF CHAIN TUPLE type:0x%x\n", _tuple_type);
4173d6c2bc0SMauro Carvalho Chehab 		*address += 2;
418bacba9e5SJasmin Jessich 		*tuple_type = _tuple_type;
419bacba9e5SJasmin Jessich 		*tuple_length = 0;
4203d6c2bc0SMauro Carvalho Chehab 		return 0;
4213d6c2bc0SMauro Carvalho Chehab 	}
422bacba9e5SJasmin Jessich 	_tuple_length = ca->pub->read_attribute_mem(ca->pub, slot,
423bacba9e5SJasmin Jessich 						    _address + 2);
424bacba9e5SJasmin Jessich 	if (_tuple_length < 0)
425bacba9e5SJasmin Jessich 		return _tuple_length;
4263d6c2bc0SMauro Carvalho Chehab 	_address += 4;
4273d6c2bc0SMauro Carvalho Chehab 
428bacba9e5SJasmin Jessich 	dprintk("TUPLE type:0x%x length:%i\n", _tuple_type, _tuple_length);
4293d6c2bc0SMauro Carvalho Chehab 
4303d6c2bc0SMauro Carvalho Chehab 	/* read in the whole tuple */
431bacba9e5SJasmin Jessich 	for (i = 0; i < _tuple_length; i++) {
43296375b7aSJasmin Jessich 		tuple[i] = ca->pub->read_attribute_mem(ca->pub, slot,
43396375b7aSJasmin Jessich 						       _address + (i * 2));
4343d6c2bc0SMauro Carvalho Chehab 		dprintk("  0x%02x: 0x%02x %c\n",
4353d6c2bc0SMauro Carvalho Chehab 			i, tuple[i] & 0xff,
4363d6c2bc0SMauro Carvalho Chehab 			((tuple[i] > 31) && (tuple[i] < 127)) ? tuple[i] : '.');
4373d6c2bc0SMauro Carvalho Chehab 	}
438bacba9e5SJasmin Jessich 	_address += (_tuple_length * 2);
4393d6c2bc0SMauro Carvalho Chehab 
440224457a9SJasmin Jessich 	/* success */
441bacba9e5SJasmin Jessich 	*tuple_type = _tuple_type;
442bacba9e5SJasmin Jessich 	*tuple_length = _tuple_length;
4433d6c2bc0SMauro Carvalho Chehab 	*address = _address;
4443d6c2bc0SMauro Carvalho Chehab 	return 0;
4453d6c2bc0SMauro Carvalho Chehab }
4463d6c2bc0SMauro Carvalho Chehab 
4473d6c2bc0SMauro Carvalho Chehab /**
448fbefb1a8SMauro Carvalho Chehab  * dvb_ca_en50221_parse_attributes - Parse attribute memory of a CAM module,
449fbefb1a8SMauro Carvalho Chehab  *	extracting Config register, and checking it is a DVB CAM module.
4503d6c2bc0SMauro Carvalho Chehab  *
451fbefb1a8SMauro Carvalho Chehab  * @ca: CA instance.
452fbefb1a8SMauro Carvalho Chehab  * @slot: Slot id.
4533d6c2bc0SMauro Carvalho Chehab  *
45446e42a30SMauro Carvalho Chehab  * return: 0 on success, <0 on failure.
4553d6c2bc0SMauro Carvalho Chehab  */
dvb_ca_en50221_parse_attributes(struct dvb_ca_private * ca,int slot)4563d6c2bc0SMauro Carvalho Chehab static int dvb_ca_en50221_parse_attributes(struct dvb_ca_private *ca, int slot)
4573d6c2bc0SMauro Carvalho Chehab {
458a75aa90cSJasmin Jessich 	struct dvb_ca_slot *sl;
4593d6c2bc0SMauro Carvalho Chehab 	int address = 0;
460bacba9e5SJasmin Jessich 	int tuple_length;
461bacba9e5SJasmin Jessich 	int tuple_type;
4623d6c2bc0SMauro Carvalho Chehab 	u8 tuple[257];
4633d6c2bc0SMauro Carvalho Chehab 	char *dvb_str;
4643d6c2bc0SMauro Carvalho Chehab 	int rasz;
4653d6c2bc0SMauro Carvalho Chehab 	int status;
4663d6c2bc0SMauro Carvalho Chehab 	int got_cftableentry = 0;
4673d6c2bc0SMauro Carvalho Chehab 	int end_chain = 0;
4683d6c2bc0SMauro Carvalho Chehab 	int i;
4693d6c2bc0SMauro Carvalho Chehab 	u16 manfid = 0;
4703d6c2bc0SMauro Carvalho Chehab 	u16 devid = 0;
4713d6c2bc0SMauro Carvalho Chehab 
472224457a9SJasmin Jessich 	/* CISTPL_DEVICE_0A */
473bacba9e5SJasmin Jessich 	status = dvb_ca_en50221_read_tuple(ca, slot, &address, &tuple_type,
474bacba9e5SJasmin Jessich 					   &tuple_length, tuple);
475bacba9e5SJasmin Jessich 	if (status < 0)
4763d6c2bc0SMauro Carvalho Chehab 		return status;
477bacba9e5SJasmin Jessich 	if (tuple_type != 0x1D)
4783d6c2bc0SMauro Carvalho Chehab 		return -EINVAL;
4793d6c2bc0SMauro Carvalho Chehab 
480224457a9SJasmin Jessich 	/* CISTPL_DEVICE_0C */
481bacba9e5SJasmin Jessich 	status = dvb_ca_en50221_read_tuple(ca, slot, &address, &tuple_type,
482bacba9e5SJasmin Jessich 					   &tuple_length, tuple);
483bacba9e5SJasmin Jessich 	if (status < 0)
4843d6c2bc0SMauro Carvalho Chehab 		return status;
485bacba9e5SJasmin Jessich 	if (tuple_type != 0x1C)
4863d6c2bc0SMauro Carvalho Chehab 		return -EINVAL;
4873d6c2bc0SMauro Carvalho Chehab 
488224457a9SJasmin Jessich 	/* CISTPL_VERS_1 */
489bacba9e5SJasmin Jessich 	status = dvb_ca_en50221_read_tuple(ca, slot, &address, &tuple_type,
490bacba9e5SJasmin Jessich 					   &tuple_length, tuple);
491bacba9e5SJasmin Jessich 	if (status < 0)
4923d6c2bc0SMauro Carvalho Chehab 		return status;
493bacba9e5SJasmin Jessich 	if (tuple_type != 0x15)
4943d6c2bc0SMauro Carvalho Chehab 		return -EINVAL;
4953d6c2bc0SMauro Carvalho Chehab 
496224457a9SJasmin Jessich 	/* CISTPL_MANFID */
497bacba9e5SJasmin Jessich 	status = dvb_ca_en50221_read_tuple(ca, slot, &address, &tuple_type,
498bacba9e5SJasmin Jessich 					   &tuple_length, tuple);
499bacba9e5SJasmin Jessich 	if (status < 0)
5003d6c2bc0SMauro Carvalho Chehab 		return status;
501bacba9e5SJasmin Jessich 	if (tuple_type != 0x20)
5023d6c2bc0SMauro Carvalho Chehab 		return -EINVAL;
503bacba9e5SJasmin Jessich 	if (tuple_length != 4)
5043d6c2bc0SMauro Carvalho Chehab 		return -EINVAL;
5053d6c2bc0SMauro Carvalho Chehab 	manfid = (tuple[1] << 8) | tuple[0];
5063d6c2bc0SMauro Carvalho Chehab 	devid = (tuple[3] << 8) | tuple[2];
5073d6c2bc0SMauro Carvalho Chehab 
508224457a9SJasmin Jessich 	/* CISTPL_CONFIG */
509bacba9e5SJasmin Jessich 	status = dvb_ca_en50221_read_tuple(ca, slot, &address, &tuple_type,
510bacba9e5SJasmin Jessich 					   &tuple_length, tuple);
511bacba9e5SJasmin Jessich 	if (status < 0)
5123d6c2bc0SMauro Carvalho Chehab 		return status;
513bacba9e5SJasmin Jessich 	if (tuple_type != 0x1A)
5143d6c2bc0SMauro Carvalho Chehab 		return -EINVAL;
515bacba9e5SJasmin Jessich 	if (tuple_length < 3)
5163d6c2bc0SMauro Carvalho Chehab 		return -EINVAL;
5173d6c2bc0SMauro Carvalho Chehab 
5183d6c2bc0SMauro Carvalho Chehab 	/* extract the configbase */
5193d6c2bc0SMauro Carvalho Chehab 	rasz = tuple[0] & 3;
520bacba9e5SJasmin Jessich 	if (tuple_length < (3 + rasz + 14))
5213d6c2bc0SMauro Carvalho Chehab 		return -EINVAL;
522a75aa90cSJasmin Jessich 	sl = &ca->slot_info[slot];
523a75aa90cSJasmin Jessich 	sl->config_base = 0;
524a75aa90cSJasmin Jessich 	for (i = 0; i < rasz + 1; i++)
525a75aa90cSJasmin Jessich 		sl->config_base |= (tuple[2 + i] << (8 * i));
5263d6c2bc0SMauro Carvalho Chehab 
5273d6c2bc0SMauro Carvalho Chehab 	/* check it contains the correct DVB string */
528bacba9e5SJasmin Jessich 	dvb_str = findstr((char *)tuple, tuple_length, "DVB_CI_V", 8);
52913b51648SJasmin Jessich 	if (!dvb_str)
5303d6c2bc0SMauro Carvalho Chehab 		return -EINVAL;
531bacba9e5SJasmin Jessich 	if (tuple_length < ((dvb_str - (char *)tuple) + 12))
5323d6c2bc0SMauro Carvalho Chehab 		return -EINVAL;
5333d6c2bc0SMauro Carvalho Chehab 
5343d6c2bc0SMauro Carvalho Chehab 	/* is it a version we support? */
5353d6c2bc0SMauro Carvalho Chehab 	if (strncmp(dvb_str + 8, "1.00", 4)) {
536b3ad24d2SMauro Carvalho Chehab 		pr_err("dvb_ca adapter %d: Unsupported DVB CAM module version %c%c%c%c\n",
537b3ad24d2SMauro Carvalho Chehab 		       ca->dvbdev->adapter->num, dvb_str[8], dvb_str[9],
538b3ad24d2SMauro Carvalho Chehab 		       dvb_str[10], dvb_str[11]);
5393d6c2bc0SMauro Carvalho Chehab 		return -EINVAL;
5403d6c2bc0SMauro Carvalho Chehab 	}
5413d6c2bc0SMauro Carvalho Chehab 
5423d6c2bc0SMauro Carvalho Chehab 	/* process the CFTABLE_ENTRY tuples, and any after those */
5433d6c2bc0SMauro Carvalho Chehab 	while ((!end_chain) && (address < 0x1000)) {
544bacba9e5SJasmin Jessich 		status = dvb_ca_en50221_read_tuple(ca, slot, &address,
545bacba9e5SJasmin Jessich 						   &tuple_type, &tuple_length,
546bacba9e5SJasmin Jessich 						   tuple);
547bacba9e5SJasmin Jessich 		if (status < 0)
5483d6c2bc0SMauro Carvalho Chehab 			return status;
549bacba9e5SJasmin Jessich 		switch (tuple_type) {
550224457a9SJasmin Jessich 		case 0x1B:	/* CISTPL_CFTABLE_ENTRY */
551bacba9e5SJasmin Jessich 			if (tuple_length < (2 + 11 + 17))
5523d6c2bc0SMauro Carvalho Chehab 				break;
5533d6c2bc0SMauro Carvalho Chehab 
5543d6c2bc0SMauro Carvalho Chehab 			/* if we've already parsed one, just use it */
5553d6c2bc0SMauro Carvalho Chehab 			if (got_cftableentry)
5563d6c2bc0SMauro Carvalho Chehab 				break;
5573d6c2bc0SMauro Carvalho Chehab 
5583d6c2bc0SMauro Carvalho Chehab 			/* get the config option */
559a75aa90cSJasmin Jessich 			sl->config_option = tuple[0] & 0x3f;
5603d6c2bc0SMauro Carvalho Chehab 
5613d6c2bc0SMauro Carvalho Chehab 			/* OK, check it contains the correct strings */
562bacba9e5SJasmin Jessich 			if (!findstr((char *)tuple, tuple_length,
563bacba9e5SJasmin Jessich 				     "DVB_HOST", 8) ||
564bacba9e5SJasmin Jessich 			    !findstr((char *)tuple, tuple_length,
565bacba9e5SJasmin Jessich 				     "DVB_CI_MODULE", 13))
5663d6c2bc0SMauro Carvalho Chehab 				break;
5673d6c2bc0SMauro Carvalho Chehab 
5683d6c2bc0SMauro Carvalho Chehab 			got_cftableentry = 1;
5693d6c2bc0SMauro Carvalho Chehab 			break;
5703d6c2bc0SMauro Carvalho Chehab 
571224457a9SJasmin Jessich 		case 0x14:	/* CISTPL_NO_LINK */
5723d6c2bc0SMauro Carvalho Chehab 			break;
5733d6c2bc0SMauro Carvalho Chehab 
574224457a9SJasmin Jessich 		case 0xFF:	/* CISTPL_END */
5753d6c2bc0SMauro Carvalho Chehab 			end_chain = 1;
5763d6c2bc0SMauro Carvalho Chehab 			break;
5773d6c2bc0SMauro Carvalho Chehab 
57896375b7aSJasmin Jessich 		default:	/* Unknown tuple type - just skip this tuple */
579b3ad24d2SMauro Carvalho Chehab 			dprintk("dvb_ca: Skipping unknown tuple type:0x%x length:0x%x\n",
580bacba9e5SJasmin Jessich 				tuple_type, tuple_length);
5813d6c2bc0SMauro Carvalho Chehab 			break;
5823d6c2bc0SMauro Carvalho Chehab 		}
5833d6c2bc0SMauro Carvalho Chehab 	}
5843d6c2bc0SMauro Carvalho Chehab 
5853d6c2bc0SMauro Carvalho Chehab 	if ((address > 0x1000) || (!got_cftableentry))
5863d6c2bc0SMauro Carvalho Chehab 		return -EINVAL;
5873d6c2bc0SMauro Carvalho Chehab 
5883d6c2bc0SMauro Carvalho Chehab 	dprintk("Valid DVB CAM detected MANID:%x DEVID:%x CONFIGBASE:0x%x CONFIGOPTION:0x%x\n",
589a75aa90cSJasmin Jessich 		manfid, devid, sl->config_base, sl->config_option);
5903d6c2bc0SMauro Carvalho Chehab 
591224457a9SJasmin Jessich 	/* success! */
5923d6c2bc0SMauro Carvalho Chehab 	return 0;
5933d6c2bc0SMauro Carvalho Chehab }
5943d6c2bc0SMauro Carvalho Chehab 
5953d6c2bc0SMauro Carvalho Chehab /**
596fbefb1a8SMauro Carvalho Chehab  * dvb_ca_en50221_set_configoption - Set CAM's configoption correctly.
5973d6c2bc0SMauro Carvalho Chehab  *
598fbefb1a8SMauro Carvalho Chehab  * @ca: CA instance.
599fbefb1a8SMauro Carvalho Chehab  * @slot: Slot containing the CAM.
6003d6c2bc0SMauro Carvalho Chehab  */
dvb_ca_en50221_set_configoption(struct dvb_ca_private * ca,int slot)6013d6c2bc0SMauro Carvalho Chehab static int dvb_ca_en50221_set_configoption(struct dvb_ca_private *ca, int slot)
6023d6c2bc0SMauro Carvalho Chehab {
603a75aa90cSJasmin Jessich 	struct dvb_ca_slot *sl = &ca->slot_info[slot];
6043d6c2bc0SMauro Carvalho Chehab 	int configoption;
6053d6c2bc0SMauro Carvalho Chehab 
6063d6c2bc0SMauro Carvalho Chehab 	dprintk("%s\n", __func__);
6073d6c2bc0SMauro Carvalho Chehab 
6083d6c2bc0SMauro Carvalho Chehab 	/* set the config option */
609a75aa90cSJasmin Jessich 	ca->pub->write_attribute_mem(ca->pub, slot, sl->config_base,
610a75aa90cSJasmin Jessich 				     sl->config_option);
6113d6c2bc0SMauro Carvalho Chehab 
6123d6c2bc0SMauro Carvalho Chehab 	/* check it */
613a75aa90cSJasmin Jessich 	configoption = ca->pub->read_attribute_mem(ca->pub, slot,
614a75aa90cSJasmin Jessich 						   sl->config_base);
6153d6c2bc0SMauro Carvalho Chehab 	dprintk("Set configoption 0x%x, read configoption 0x%x\n",
616a75aa90cSJasmin Jessich 		sl->config_option, configoption & 0x3f);
6173d6c2bc0SMauro Carvalho Chehab 
6183d6c2bc0SMauro Carvalho Chehab 	/* fine! */
6193d6c2bc0SMauro Carvalho Chehab 	return 0;
6203d6c2bc0SMauro Carvalho Chehab }
6213d6c2bc0SMauro Carvalho Chehab 
6223d6c2bc0SMauro Carvalho Chehab /**
623fbefb1a8SMauro Carvalho Chehab  * dvb_ca_en50221_read_data - This function talks to an EN50221 CAM control
624fbefb1a8SMauro Carvalho Chehab  *	interface. It reads a buffer of data from the CAM. The data can either
625fbefb1a8SMauro Carvalho Chehab  *	be stored in a supplied buffer, or automatically be added to the slot's
626fbefb1a8SMauro Carvalho Chehab  *	rx_buffer.
6273d6c2bc0SMauro Carvalho Chehab  *
628fbefb1a8SMauro Carvalho Chehab  * @ca: CA instance.
629fbefb1a8SMauro Carvalho Chehab  * @slot: Slot to read from.
630fbefb1a8SMauro Carvalho Chehab  * @ebuf: If non-NULL, the data will be written to this buffer. If NULL,
63146e42a30SMauro Carvalho Chehab  *	  the data will be added into the buffering system as a normal
63246e42a30SMauro Carvalho Chehab  *	  fragment.
633fbefb1a8SMauro Carvalho Chehab  * @ecount: Size of ebuf. Ignored if ebuf is NULL.
6343d6c2bc0SMauro Carvalho Chehab  *
63546e42a30SMauro Carvalho Chehab  * return: Number of bytes read, or < 0 on error
6363d6c2bc0SMauro Carvalho Chehab  */
dvb_ca_en50221_read_data(struct dvb_ca_private * ca,int slot,u8 * ebuf,int ecount)6378ec6107aSJasmin Jessich static int dvb_ca_en50221_read_data(struct dvb_ca_private *ca, int slot,
6388ec6107aSJasmin Jessich 				    u8 *ebuf, int ecount)
6393d6c2bc0SMauro Carvalho Chehab {
640a75aa90cSJasmin Jessich 	struct dvb_ca_slot *sl = &ca->slot_info[slot];
6413d6c2bc0SMauro Carvalho Chehab 	int bytes_read;
6423d6c2bc0SMauro Carvalho Chehab 	int status;
6433d6c2bc0SMauro Carvalho Chehab 	u8 buf[HOST_LINK_BUF_SIZE];
6443d6c2bc0SMauro Carvalho Chehab 	int i;
6453d6c2bc0SMauro Carvalho Chehab 
6463d6c2bc0SMauro Carvalho Chehab 	dprintk("%s\n", __func__);
6473d6c2bc0SMauro Carvalho Chehab 
6483d6c2bc0SMauro Carvalho Chehab 	/* check if we have space for a link buf in the rx_buffer */
64913b51648SJasmin Jessich 	if (!ebuf) {
6503d6c2bc0SMauro Carvalho Chehab 		int buf_free;
6513d6c2bc0SMauro Carvalho Chehab 
652a75aa90cSJasmin Jessich 		if (!sl->rx_buffer.data) {
6533d6c2bc0SMauro Carvalho Chehab 			status = -EIO;
6543d6c2bc0SMauro Carvalho Chehab 			goto exit;
6553d6c2bc0SMauro Carvalho Chehab 		}
656a75aa90cSJasmin Jessich 		buf_free = dvb_ringbuffer_free(&sl->rx_buffer);
6573d6c2bc0SMauro Carvalho Chehab 
658a75aa90cSJasmin Jessich 		if (buf_free < (sl->link_buf_size +
659f894165cSRalph Metzler 				DVB_RINGBUFFER_PKTHDRSIZE)) {
6603d6c2bc0SMauro Carvalho Chehab 			status = -EAGAIN;
6613d6c2bc0SMauro Carvalho Chehab 			goto exit;
6623d6c2bc0SMauro Carvalho Chehab 		}
6633d6c2bc0SMauro Carvalho Chehab 	}
6643d6c2bc0SMauro Carvalho Chehab 
665f894165cSRalph Metzler 	if (ca->pub->read_data &&
666a75aa90cSJasmin Jessich 	    (sl->slot_state != DVB_CA_SLOTSTATE_LINKINIT)) {
66713b51648SJasmin Jessich 		if (!ebuf)
668f894165cSRalph Metzler 			status = ca->pub->read_data(ca->pub, slot, buf,
669f894165cSRalph Metzler 						    sizeof(buf));
670f894165cSRalph Metzler 		else
671f894165cSRalph Metzler 			status = ca->pub->read_data(ca->pub, slot, buf, ecount);
672f894165cSRalph Metzler 		if (status < 0)
673f894165cSRalph Metzler 			return status;
674f894165cSRalph Metzler 		bytes_read =  status;
675f894165cSRalph Metzler 		if (status == 0)
676f894165cSRalph Metzler 			goto exit;
677f894165cSRalph Metzler 	} else {
6783d6c2bc0SMauro Carvalho Chehab 		/* check if there is data available */
679f894165cSRalph Metzler 		status = ca->pub->read_cam_control(ca->pub, slot,
680f894165cSRalph Metzler 						   CTRLIF_STATUS);
681f894165cSRalph Metzler 		if (status < 0)
6823d6c2bc0SMauro Carvalho Chehab 			goto exit;
6833d6c2bc0SMauro Carvalho Chehab 		if (!(status & STATUSREG_DA)) {
6843d6c2bc0SMauro Carvalho Chehab 			/* no data */
6853d6c2bc0SMauro Carvalho Chehab 			status = 0;
6863d6c2bc0SMauro Carvalho Chehab 			goto exit;
6873d6c2bc0SMauro Carvalho Chehab 		}
6883d6c2bc0SMauro Carvalho Chehab 
6893d6c2bc0SMauro Carvalho Chehab 		/* read the amount of data */
690f894165cSRalph Metzler 		status = ca->pub->read_cam_control(ca->pub, slot,
691f894165cSRalph Metzler 						   CTRLIF_SIZE_HIGH);
692f894165cSRalph Metzler 		if (status < 0)
6933d6c2bc0SMauro Carvalho Chehab 			goto exit;
6943d6c2bc0SMauro Carvalho Chehab 		bytes_read = status << 8;
695f894165cSRalph Metzler 		status = ca->pub->read_cam_control(ca->pub, slot,
696f894165cSRalph Metzler 						   CTRLIF_SIZE_LOW);
697f894165cSRalph Metzler 		if (status < 0)
6983d6c2bc0SMauro Carvalho Chehab 			goto exit;
6993d6c2bc0SMauro Carvalho Chehab 		bytes_read |= status;
7003d6c2bc0SMauro Carvalho Chehab 
7013d6c2bc0SMauro Carvalho Chehab 		/* check it will fit */
70213b51648SJasmin Jessich 		if (!ebuf) {
703a75aa90cSJasmin Jessich 			if (bytes_read > sl->link_buf_size) {
704b3ad24d2SMauro Carvalho Chehab 				pr_err("dvb_ca adapter %d: CAM tried to send a buffer larger than the link buffer size (%i > %i)!\n",
705b3ad24d2SMauro Carvalho Chehab 				       ca->dvbdev->adapter->num, bytes_read,
706a75aa90cSJasmin Jessich 				       sl->link_buf_size);
707a75aa90cSJasmin Jessich 				sl->slot_state = DVB_CA_SLOTSTATE_LINKINIT;
7083d6c2bc0SMauro Carvalho Chehab 				status = -EIO;
7093d6c2bc0SMauro Carvalho Chehab 				goto exit;
7103d6c2bc0SMauro Carvalho Chehab 			}
7113d6c2bc0SMauro Carvalho Chehab 			if (bytes_read < 2) {
712b3ad24d2SMauro Carvalho Chehab 				pr_err("dvb_ca adapter %d: CAM sent a buffer that was less than 2 bytes!\n",
7133d6c2bc0SMauro Carvalho Chehab 				       ca->dvbdev->adapter->num);
714a75aa90cSJasmin Jessich 				sl->slot_state = DVB_CA_SLOTSTATE_LINKINIT;
7153d6c2bc0SMauro Carvalho Chehab 				status = -EIO;
7163d6c2bc0SMauro Carvalho Chehab 				goto exit;
7173d6c2bc0SMauro Carvalho Chehab 			}
7183d6c2bc0SMauro Carvalho Chehab 		} else {
7193d6c2bc0SMauro Carvalho Chehab 			if (bytes_read > ecount) {
720b3ad24d2SMauro Carvalho Chehab 				pr_err("dvb_ca adapter %d: CAM tried to send a buffer larger than the ecount size!\n",
7213d6c2bc0SMauro Carvalho Chehab 				       ca->dvbdev->adapter->num);
7223d6c2bc0SMauro Carvalho Chehab 				status = -EIO;
7233d6c2bc0SMauro Carvalho Chehab 				goto exit;
7243d6c2bc0SMauro Carvalho Chehab 			}
7253d6c2bc0SMauro Carvalho Chehab 		}
7263d6c2bc0SMauro Carvalho Chehab 
7273d6c2bc0SMauro Carvalho Chehab 		/* fill the buffer */
7283d6c2bc0SMauro Carvalho Chehab 		for (i = 0; i < bytes_read; i++) {
7293d6c2bc0SMauro Carvalho Chehab 			/* read byte and check */
730f894165cSRalph Metzler 			status = ca->pub->read_cam_control(ca->pub, slot,
731f894165cSRalph Metzler 							   CTRLIF_DATA);
732f894165cSRalph Metzler 			if (status < 0)
7333d6c2bc0SMauro Carvalho Chehab 				goto exit;
7343d6c2bc0SMauro Carvalho Chehab 
7353d6c2bc0SMauro Carvalho Chehab 			/* OK, store it in the buffer */
7363d6c2bc0SMauro Carvalho Chehab 			buf[i] = status;
7373d6c2bc0SMauro Carvalho Chehab 		}
7383d6c2bc0SMauro Carvalho Chehab 
7393d6c2bc0SMauro Carvalho Chehab 		/* check for read error (RE should now be 0) */
740f894165cSRalph Metzler 		status = ca->pub->read_cam_control(ca->pub, slot,
741f894165cSRalph Metzler 						   CTRLIF_STATUS);
742f894165cSRalph Metzler 		if (status < 0)
7433d6c2bc0SMauro Carvalho Chehab 			goto exit;
7443d6c2bc0SMauro Carvalho Chehab 		if (status & STATUSREG_RE) {
745a75aa90cSJasmin Jessich 			sl->slot_state = DVB_CA_SLOTSTATE_LINKINIT;
7463d6c2bc0SMauro Carvalho Chehab 			status = -EIO;
7473d6c2bc0SMauro Carvalho Chehab 			goto exit;
7483d6c2bc0SMauro Carvalho Chehab 		}
749f894165cSRalph Metzler 	}
7503d6c2bc0SMauro Carvalho Chehab 
75196375b7aSJasmin Jessich 	/*
75296375b7aSJasmin Jessich 	 * OK, add it to the receive buffer, or copy into external buffer if
75396375b7aSJasmin Jessich 	 * supplied
75496375b7aSJasmin Jessich 	 */
75513b51648SJasmin Jessich 	if (!ebuf) {
756a75aa90cSJasmin Jessich 		if (!sl->rx_buffer.data) {
7573d6c2bc0SMauro Carvalho Chehab 			status = -EIO;
7583d6c2bc0SMauro Carvalho Chehab 			goto exit;
7593d6c2bc0SMauro Carvalho Chehab 		}
760a75aa90cSJasmin Jessich 		dvb_ringbuffer_pkt_write(&sl->rx_buffer, buf, bytes_read);
7613d6c2bc0SMauro Carvalho Chehab 	} else {
7623d6c2bc0SMauro Carvalho Chehab 		memcpy(ebuf, buf, bytes_read);
7633d6c2bc0SMauro Carvalho Chehab 	}
7643d6c2bc0SMauro Carvalho Chehab 
7653d6c2bc0SMauro Carvalho Chehab 	dprintk("Received CA packet for slot %i connection id 0x%x last_frag:%i size:0x%x\n", slot,
7663d6c2bc0SMauro Carvalho Chehab 		buf[0], (buf[1] & 0x80) == 0, bytes_read);
7673d6c2bc0SMauro Carvalho Chehab 
7683d6c2bc0SMauro Carvalho Chehab 	/* wake up readers when a last_fragment is received */
7697bd8cc8fSJasmin Jessich 	if ((buf[1] & 0x80) == 0x00)
7703d6c2bc0SMauro Carvalho Chehab 		wake_up_interruptible(&ca->wait_queue);
7717bd8cc8fSJasmin Jessich 
7723d6c2bc0SMauro Carvalho Chehab 	status = bytes_read;
7733d6c2bc0SMauro Carvalho Chehab 
7743d6c2bc0SMauro Carvalho Chehab exit:
7753d6c2bc0SMauro Carvalho Chehab 	return status;
7763d6c2bc0SMauro Carvalho Chehab }
7773d6c2bc0SMauro Carvalho Chehab 
7783d6c2bc0SMauro Carvalho Chehab /**
779fbefb1a8SMauro Carvalho Chehab  * dvb_ca_en50221_write_data - This function talks to an EN50221 CAM control
780fbefb1a8SMauro Carvalho Chehab  *				interface. It writes a buffer of data to a CAM.
7813d6c2bc0SMauro Carvalho Chehab  *
782fbefb1a8SMauro Carvalho Chehab  * @ca: CA instance.
783fbefb1a8SMauro Carvalho Chehab  * @slot: Slot to write to.
78446e42a30SMauro Carvalho Chehab  * @buf: The data in this buffer is treated as a complete link-level packet to
7853d6c2bc0SMauro Carvalho Chehab  *	 be written.
78646e42a30SMauro Carvalho Chehab  * @bytes_write: Size of ebuf.
787a4315e5bSYongSu Yoo  * @size_write_flag: A flag on Command Register which says whether the link size
788a4315e5bSYongSu Yoo  * information will be writen or not.
7893d6c2bc0SMauro Carvalho Chehab  *
79046e42a30SMauro Carvalho Chehab  * return: Number of bytes written, or < 0 on error.
7913d6c2bc0SMauro Carvalho Chehab  */
dvb_ca_en50221_write_data(struct dvb_ca_private * ca,int slot,u8 * buf,int bytes_write,int size_write_flag)7928ec6107aSJasmin Jessich static int dvb_ca_en50221_write_data(struct dvb_ca_private *ca, int slot,
793a4315e5bSYongSu Yoo 				     u8 *buf, int bytes_write, int size_write_flag)
7943d6c2bc0SMauro Carvalho Chehab {
795a75aa90cSJasmin Jessich 	struct dvb_ca_slot *sl = &ca->slot_info[slot];
7963d6c2bc0SMauro Carvalho Chehab 	int status;
7973d6c2bc0SMauro Carvalho Chehab 	int i;
7983d6c2bc0SMauro Carvalho Chehab 
7993d6c2bc0SMauro Carvalho Chehab 	dprintk("%s\n", __func__);
8003d6c2bc0SMauro Carvalho Chehab 
8013d6c2bc0SMauro Carvalho Chehab 	/* sanity check */
802a75aa90cSJasmin Jessich 	if (bytes_write > sl->link_buf_size)
8033d6c2bc0SMauro Carvalho Chehab 		return -EINVAL;
8043d6c2bc0SMauro Carvalho Chehab 
805f894165cSRalph Metzler 	if (ca->pub->write_data &&
806a75aa90cSJasmin Jessich 	    (sl->slot_state != DVB_CA_SLOTSTATE_LINKINIT))
807f894165cSRalph Metzler 		return ca->pub->write_data(ca->pub, slot, buf, bytes_write);
808f894165cSRalph Metzler 
809a1ca23d1SJasmin Jessich 	/*
810a1ca23d1SJasmin Jessich 	 * it is possible we are dealing with a single buffer implementation,
811a1ca23d1SJasmin Jessich 	 * thus if there is data available for read or if there is even a read
812a1ca23d1SJasmin Jessich 	 * already in progress, we do nothing but awake the kernel thread to
813a1ca23d1SJasmin Jessich 	 * process the data if necessary.
814a1ca23d1SJasmin Jessich 	 */
815bacba9e5SJasmin Jessich 	status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS);
816bacba9e5SJasmin Jessich 	if (status < 0)
8173d6c2bc0SMauro Carvalho Chehab 		goto exitnowrite;
8183d6c2bc0SMauro Carvalho Chehab 	if (status & (STATUSREG_DA | STATUSREG_RE)) {
8193d6c2bc0SMauro Carvalho Chehab 		if (status & STATUSREG_DA)
8203d6c2bc0SMauro Carvalho Chehab 			dvb_ca_en50221_thread_wakeup(ca);
8213d6c2bc0SMauro Carvalho Chehab 
8223d6c2bc0SMauro Carvalho Chehab 		status = -EAGAIN;
8233d6c2bc0SMauro Carvalho Chehab 		goto exitnowrite;
8243d6c2bc0SMauro Carvalho Chehab 	}
8253d6c2bc0SMauro Carvalho Chehab 
8263d6c2bc0SMauro Carvalho Chehab 	/* OK, set HC bit */
827bacba9e5SJasmin Jessich 	status = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND,
828a4315e5bSYongSu Yoo 					    IRQEN | CMDREG_HC | size_write_flag);
829bacba9e5SJasmin Jessich 	if (status)
8303d6c2bc0SMauro Carvalho Chehab 		goto exit;
8313d6c2bc0SMauro Carvalho Chehab 
8323d6c2bc0SMauro Carvalho Chehab 	/* check if interface is still free */
833bacba9e5SJasmin Jessich 	status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS);
834bacba9e5SJasmin Jessich 	if (status < 0)
8353d6c2bc0SMauro Carvalho Chehab 		goto exit;
8363d6c2bc0SMauro Carvalho Chehab 	if (!(status & STATUSREG_FR)) {
8373d6c2bc0SMauro Carvalho Chehab 		/* it wasn't free => try again later */
8383d6c2bc0SMauro Carvalho Chehab 		status = -EAGAIN;
8393d6c2bc0SMauro Carvalho Chehab 		goto exit;
8403d6c2bc0SMauro Carvalho Chehab 	}
8413d6c2bc0SMauro Carvalho Chehab 
842e7080d44SJasmin J 	/*
843e7080d44SJasmin J 	 * It may need some time for the CAM to settle down, or there might
844e7080d44SJasmin J 	 * be a race condition between the CAM, writing HC and our last
845e7080d44SJasmin J 	 * check for DA. This happens, if the CAM asserts DA, just after
846e7080d44SJasmin J 	 * checking DA before we are setting HC. In this case it might be
847e7080d44SJasmin J 	 * a bug in the CAM to keep the FR bit, the lower layer/HW
848e7080d44SJasmin J 	 * communication requires a longer timeout or the CAM needs more
849e7080d44SJasmin J 	 * time internally. But this happens in reality!
850e7080d44SJasmin J 	 * We need to read the status from the HW again and do the same
851e7080d44SJasmin J 	 * we did for the previous check for DA
852e7080d44SJasmin J 	 */
853e7080d44SJasmin J 	status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS);
854e7080d44SJasmin J 	if (status < 0)
855e7080d44SJasmin J 		goto exit;
856e7080d44SJasmin J 
857e7080d44SJasmin J 	if (status & (STATUSREG_DA | STATUSREG_RE)) {
858e7080d44SJasmin J 		if (status & STATUSREG_DA)
859e7080d44SJasmin J 			dvb_ca_en50221_thread_wakeup(ca);
860e7080d44SJasmin J 
861e7080d44SJasmin J 		status = -EAGAIN;
862e7080d44SJasmin J 		goto exit;
863e7080d44SJasmin J 	}
864e7080d44SJasmin J 
8653d6c2bc0SMauro Carvalho Chehab 	/* send the amount of data */
866bacba9e5SJasmin Jessich 	status = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_SIZE_HIGH,
867bacba9e5SJasmin Jessich 					    bytes_write >> 8);
868bacba9e5SJasmin Jessich 	if (status)
8693d6c2bc0SMauro Carvalho Chehab 		goto exit;
870bacba9e5SJasmin Jessich 	status = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_SIZE_LOW,
871bacba9e5SJasmin Jessich 					    bytes_write & 0xff);
872bacba9e5SJasmin Jessich 	if (status)
8733d6c2bc0SMauro Carvalho Chehab 		goto exit;
8743d6c2bc0SMauro Carvalho Chehab 
8753d6c2bc0SMauro Carvalho Chehab 	/* send the buffer */
8763d6c2bc0SMauro Carvalho Chehab 	for (i = 0; i < bytes_write; i++) {
877bacba9e5SJasmin Jessich 		status = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_DATA,
878bacba9e5SJasmin Jessich 						    buf[i]);
879bacba9e5SJasmin Jessich 		if (status)
8803d6c2bc0SMauro Carvalho Chehab 			goto exit;
8813d6c2bc0SMauro Carvalho Chehab 	}
8823d6c2bc0SMauro Carvalho Chehab 
8833d6c2bc0SMauro Carvalho Chehab 	/* check for write error (WE should now be 0) */
884bacba9e5SJasmin Jessich 	status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS);
885bacba9e5SJasmin Jessich 	if (status < 0)
8863d6c2bc0SMauro Carvalho Chehab 		goto exit;
8873d6c2bc0SMauro Carvalho Chehab 	if (status & STATUSREG_WE) {
888a75aa90cSJasmin Jessich 		sl->slot_state = DVB_CA_SLOTSTATE_LINKINIT;
8893d6c2bc0SMauro Carvalho Chehab 		status = -EIO;
8903d6c2bc0SMauro Carvalho Chehab 		goto exit;
8913d6c2bc0SMauro Carvalho Chehab 	}
8923d6c2bc0SMauro Carvalho Chehab 	status = bytes_write;
8933d6c2bc0SMauro Carvalho Chehab 
8943d6c2bc0SMauro Carvalho Chehab 	dprintk("Wrote CA packet for slot %i, connection id 0x%x last_frag:%i size:0x%x\n", slot,
8953d6c2bc0SMauro Carvalho Chehab 		buf[0], (buf[1] & 0x80) == 0, bytes_write);
8963d6c2bc0SMauro Carvalho Chehab 
8973d6c2bc0SMauro Carvalho Chehab exit:
8983d6c2bc0SMauro Carvalho Chehab 	ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, IRQEN);
8993d6c2bc0SMauro Carvalho Chehab 
9003d6c2bc0SMauro Carvalho Chehab exitnowrite:
9013d6c2bc0SMauro Carvalho Chehab 	return status;
9023d6c2bc0SMauro Carvalho Chehab }
9033d6c2bc0SMauro Carvalho Chehab 
90496375b7aSJasmin Jessich /* ************************************************************************** */
9053d6c2bc0SMauro Carvalho Chehab /* EN50221 higher level functions */
9063d6c2bc0SMauro Carvalho Chehab 
9073d6c2bc0SMauro Carvalho Chehab /**
90897adc2b3SJasmin Jessich  * dvb_ca_en50221_slot_shutdown - A CAM has been removed => shut it down.
9093d6c2bc0SMauro Carvalho Chehab  *
910fbefb1a8SMauro Carvalho Chehab  * @ca: CA instance.
911fbefb1a8SMauro Carvalho Chehab  * @slot: Slot to shut down.
9123d6c2bc0SMauro Carvalho Chehab  */
dvb_ca_en50221_slot_shutdown(struct dvb_ca_private * ca,int slot)9133d6c2bc0SMauro Carvalho Chehab static int dvb_ca_en50221_slot_shutdown(struct dvb_ca_private *ca, int slot)
9143d6c2bc0SMauro Carvalho Chehab {
9153d6c2bc0SMauro Carvalho Chehab 	dprintk("%s\n", __func__);
9163d6c2bc0SMauro Carvalho Chehab 
9173d6c2bc0SMauro Carvalho Chehab 	ca->pub->slot_shutdown(ca->pub, slot);
9183d6c2bc0SMauro Carvalho Chehab 	ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_NONE;
9193d6c2bc0SMauro Carvalho Chehab 
920a1ca23d1SJasmin Jessich 	/*
921a1ca23d1SJasmin Jessich 	 * need to wake up all processes to check if they're now trying to
922a1ca23d1SJasmin Jessich 	 * write to a defunct CAM
923a1ca23d1SJasmin Jessich 	 */
9243d6c2bc0SMauro Carvalho Chehab 	wake_up_interruptible(&ca->wait_queue);
9253d6c2bc0SMauro Carvalho Chehab 
9263d6c2bc0SMauro Carvalho Chehab 	dprintk("Slot %i shutdown\n", slot);
9273d6c2bc0SMauro Carvalho Chehab 
9283d6c2bc0SMauro Carvalho Chehab 	/* success */
9293d6c2bc0SMauro Carvalho Chehab 	return 0;
9303d6c2bc0SMauro Carvalho Chehab }
9313d6c2bc0SMauro Carvalho Chehab 
9323d6c2bc0SMauro Carvalho Chehab /**
93397adc2b3SJasmin Jessich  * dvb_ca_en50221_camchange_irq - A CAMCHANGE IRQ has occurred.
9343d6c2bc0SMauro Carvalho Chehab  *
93546e42a30SMauro Carvalho Chehab  * @pubca: CA instance.
936fbefb1a8SMauro Carvalho Chehab  * @slot: Slot concerned.
937fbefb1a8SMauro Carvalho Chehab  * @change_type: One of the DVB_CA_CAMCHANGE_* values.
9383d6c2bc0SMauro Carvalho Chehab  */
dvb_ca_en50221_camchange_irq(struct dvb_ca_en50221 * pubca,int slot,int change_type)93996375b7aSJasmin Jessich void dvb_ca_en50221_camchange_irq(struct dvb_ca_en50221 *pubca, int slot,
94096375b7aSJasmin Jessich 				  int change_type)
9413d6c2bc0SMauro Carvalho Chehab {
9423d6c2bc0SMauro Carvalho Chehab 	struct dvb_ca_private *ca = pubca->private;
943a75aa90cSJasmin Jessich 	struct dvb_ca_slot *sl = &ca->slot_info[slot];
9443d6c2bc0SMauro Carvalho Chehab 
9453d6c2bc0SMauro Carvalho Chehab 	dprintk("CAMCHANGE IRQ slot:%i change_type:%i\n", slot, change_type);
9463d6c2bc0SMauro Carvalho Chehab 
9473d6c2bc0SMauro Carvalho Chehab 	switch (change_type) {
9483d6c2bc0SMauro Carvalho Chehab 	case DVB_CA_EN50221_CAMCHANGE_REMOVED:
9493d6c2bc0SMauro Carvalho Chehab 	case DVB_CA_EN50221_CAMCHANGE_INSERTED:
9503d6c2bc0SMauro Carvalho Chehab 		break;
9513d6c2bc0SMauro Carvalho Chehab 
9523d6c2bc0SMauro Carvalho Chehab 	default:
9533d6c2bc0SMauro Carvalho Chehab 		return;
9543d6c2bc0SMauro Carvalho Chehab 	}
9553d6c2bc0SMauro Carvalho Chehab 
956a75aa90cSJasmin Jessich 	sl->camchange_type = change_type;
957a75aa90cSJasmin Jessich 	atomic_inc(&sl->camchange_count);
9583d6c2bc0SMauro Carvalho Chehab 	dvb_ca_en50221_thread_wakeup(ca);
9593d6c2bc0SMauro Carvalho Chehab }
96097adc2b3SJasmin Jessich EXPORT_SYMBOL(dvb_ca_en50221_camchange_irq);
9613d6c2bc0SMauro Carvalho Chehab 
9623d6c2bc0SMauro Carvalho Chehab /**
963fbefb1a8SMauro Carvalho Chehab  * dvb_ca_en50221_camready_irq - A CAMREADY IRQ has occurred.
9643d6c2bc0SMauro Carvalho Chehab  *
96546e42a30SMauro Carvalho Chehab  * @pubca: CA instance.
966fbefb1a8SMauro Carvalho Chehab  * @slot: Slot concerned.
9673d6c2bc0SMauro Carvalho Chehab  */
dvb_ca_en50221_camready_irq(struct dvb_ca_en50221 * pubca,int slot)9683d6c2bc0SMauro Carvalho Chehab void dvb_ca_en50221_camready_irq(struct dvb_ca_en50221 *pubca, int slot)
9693d6c2bc0SMauro Carvalho Chehab {
9703d6c2bc0SMauro Carvalho Chehab 	struct dvb_ca_private *ca = pubca->private;
971a75aa90cSJasmin Jessich 	struct dvb_ca_slot *sl = &ca->slot_info[slot];
9723d6c2bc0SMauro Carvalho Chehab 
9733d6c2bc0SMauro Carvalho Chehab 	dprintk("CAMREADY IRQ slot:%i\n", slot);
9743d6c2bc0SMauro Carvalho Chehab 
975a75aa90cSJasmin Jessich 	if (sl->slot_state == DVB_CA_SLOTSTATE_WAITREADY) {
976a75aa90cSJasmin Jessich 		sl->slot_state = DVB_CA_SLOTSTATE_VALIDATE;
9773d6c2bc0SMauro Carvalho Chehab 		dvb_ca_en50221_thread_wakeup(ca);
9783d6c2bc0SMauro Carvalho Chehab 	}
9793d6c2bc0SMauro Carvalho Chehab }
98097adc2b3SJasmin Jessich EXPORT_SYMBOL(dvb_ca_en50221_camready_irq);
9813d6c2bc0SMauro Carvalho Chehab 
9823d6c2bc0SMauro Carvalho Chehab /**
98397adc2b3SJasmin Jessich  * dvb_ca_en50221_frda_irq - An FR or DA IRQ has occurred.
9843d6c2bc0SMauro Carvalho Chehab  *
98546e42a30SMauro Carvalho Chehab  * @pubca: CA instance.
986fbefb1a8SMauro Carvalho Chehab  * @slot: Slot concerned.
9873d6c2bc0SMauro Carvalho Chehab  */
dvb_ca_en50221_frda_irq(struct dvb_ca_en50221 * pubca,int slot)9883d6c2bc0SMauro Carvalho Chehab void dvb_ca_en50221_frda_irq(struct dvb_ca_en50221 *pubca, int slot)
9893d6c2bc0SMauro Carvalho Chehab {
9903d6c2bc0SMauro Carvalho Chehab 	struct dvb_ca_private *ca = pubca->private;
991a75aa90cSJasmin Jessich 	struct dvb_ca_slot *sl = &ca->slot_info[slot];
9923d6c2bc0SMauro Carvalho Chehab 	int flags;
9933d6c2bc0SMauro Carvalho Chehab 
9943d6c2bc0SMauro Carvalho Chehab 	dprintk("FR/DA IRQ slot:%i\n", slot);
9953d6c2bc0SMauro Carvalho Chehab 
996a75aa90cSJasmin Jessich 	switch (sl->slot_state) {
9973d6c2bc0SMauro Carvalho Chehab 	case DVB_CA_SLOTSTATE_LINKINIT:
9983d6c2bc0SMauro Carvalho Chehab 		flags = ca->pub->read_cam_control(pubca, slot, CTRLIF_STATUS);
9993d6c2bc0SMauro Carvalho Chehab 		if (flags & STATUSREG_DA) {
10003d6c2bc0SMauro Carvalho Chehab 			dprintk("CAM supports DA IRQ\n");
1001a75aa90cSJasmin Jessich 			sl->da_irq_supported = 1;
10023d6c2bc0SMauro Carvalho Chehab 		}
10033d6c2bc0SMauro Carvalho Chehab 		break;
10043d6c2bc0SMauro Carvalho Chehab 
10053d6c2bc0SMauro Carvalho Chehab 	case DVB_CA_SLOTSTATE_RUNNING:
10063d6c2bc0SMauro Carvalho Chehab 		if (ca->open)
10073d6c2bc0SMauro Carvalho Chehab 			dvb_ca_en50221_thread_wakeup(ca);
10083d6c2bc0SMauro Carvalho Chehab 		break;
10093d6c2bc0SMauro Carvalho Chehab 	}
10103d6c2bc0SMauro Carvalho Chehab }
101197adc2b3SJasmin Jessich EXPORT_SYMBOL(dvb_ca_en50221_frda_irq);
10123d6c2bc0SMauro Carvalho Chehab 
101396375b7aSJasmin Jessich /* ************************************************************************** */
10143d6c2bc0SMauro Carvalho Chehab /* EN50221 thread functions */
10153d6c2bc0SMauro Carvalho Chehab 
10163d6c2bc0SMauro Carvalho Chehab /**
1017a4184b4fSHans Verkuil  * dvb_ca_en50221_thread_wakeup - Wake up the DVB CA thread
10183d6c2bc0SMauro Carvalho Chehab  *
1019fbefb1a8SMauro Carvalho Chehab  * @ca: CA instance.
10203d6c2bc0SMauro Carvalho Chehab  */
dvb_ca_en50221_thread_wakeup(struct dvb_ca_private * ca)10213d6c2bc0SMauro Carvalho Chehab static void dvb_ca_en50221_thread_wakeup(struct dvb_ca_private *ca)
10223d6c2bc0SMauro Carvalho Chehab {
10233d6c2bc0SMauro Carvalho Chehab 	dprintk("%s\n", __func__);
10243d6c2bc0SMauro Carvalho Chehab 
10253d6c2bc0SMauro Carvalho Chehab 	ca->wakeup = 1;
10263d6c2bc0SMauro Carvalho Chehab 	mb();
10273d6c2bc0SMauro Carvalho Chehab 	wake_up_process(ca->thread);
10283d6c2bc0SMauro Carvalho Chehab }
10293d6c2bc0SMauro Carvalho Chehab 
10303d6c2bc0SMauro Carvalho Chehab /**
1031a4184b4fSHans Verkuil  * dvb_ca_en50221_thread_update_delay - Update the delay used by the thread.
10323d6c2bc0SMauro Carvalho Chehab  *
1033fbefb1a8SMauro Carvalho Chehab  * @ca: CA instance.
10343d6c2bc0SMauro Carvalho Chehab  */
dvb_ca_en50221_thread_update_delay(struct dvb_ca_private * ca)10353d6c2bc0SMauro Carvalho Chehab static void dvb_ca_en50221_thread_update_delay(struct dvb_ca_private *ca)
10363d6c2bc0SMauro Carvalho Chehab {
10373d6c2bc0SMauro Carvalho Chehab 	int delay;
10383d6c2bc0SMauro Carvalho Chehab 	int curdelay = 100000000;
10393d6c2bc0SMauro Carvalho Chehab 	int slot;
10403d6c2bc0SMauro Carvalho Chehab 
1041dc32f324SJasmin Jessich 	/*
1042dc32f324SJasmin Jessich 	 * Beware of too high polling frequency, because one polling
10433d6c2bc0SMauro Carvalho Chehab 	 * call might take several hundred milliseconds until timeout!
10443d6c2bc0SMauro Carvalho Chehab 	 */
10453d6c2bc0SMauro Carvalho Chehab 	for (slot = 0; slot < ca->slot_count; slot++) {
1046a75aa90cSJasmin Jessich 		struct dvb_ca_slot *sl = &ca->slot_info[slot];
1047a75aa90cSJasmin Jessich 
1048a75aa90cSJasmin Jessich 		switch (sl->slot_state) {
10493d6c2bc0SMauro Carvalho Chehab 		default:
10503d6c2bc0SMauro Carvalho Chehab 		case DVB_CA_SLOTSTATE_NONE:
10513d6c2bc0SMauro Carvalho Chehab 			delay = HZ * 60;  /* 60s */
10523d6c2bc0SMauro Carvalho Chehab 			if (!(ca->flags & DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE))
10533d6c2bc0SMauro Carvalho Chehab 				delay = HZ * 5;  /* 5s */
10543d6c2bc0SMauro Carvalho Chehab 			break;
10553d6c2bc0SMauro Carvalho Chehab 		case DVB_CA_SLOTSTATE_INVALID:
10563d6c2bc0SMauro Carvalho Chehab 			delay = HZ * 60;  /* 60s */
10573d6c2bc0SMauro Carvalho Chehab 			if (!(ca->flags & DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE))
10583d6c2bc0SMauro Carvalho Chehab 				delay = HZ / 10;  /* 100ms */
10593d6c2bc0SMauro Carvalho Chehab 			break;
10603d6c2bc0SMauro Carvalho Chehab 
10613d6c2bc0SMauro Carvalho Chehab 		case DVB_CA_SLOTSTATE_UNINITIALISED:
10623d6c2bc0SMauro Carvalho Chehab 		case DVB_CA_SLOTSTATE_WAITREADY:
10633d6c2bc0SMauro Carvalho Chehab 		case DVB_CA_SLOTSTATE_VALIDATE:
10643d6c2bc0SMauro Carvalho Chehab 		case DVB_CA_SLOTSTATE_WAITFR:
10653d6c2bc0SMauro Carvalho Chehab 		case DVB_CA_SLOTSTATE_LINKINIT:
10663d6c2bc0SMauro Carvalho Chehab 			delay = HZ / 10;  /* 100ms */
10673d6c2bc0SMauro Carvalho Chehab 			break;
10683d6c2bc0SMauro Carvalho Chehab 
10693d6c2bc0SMauro Carvalho Chehab 		case DVB_CA_SLOTSTATE_RUNNING:
10703d6c2bc0SMauro Carvalho Chehab 			delay = HZ * 60;  /* 60s */
10713d6c2bc0SMauro Carvalho Chehab 			if (!(ca->flags & DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE))
10723d6c2bc0SMauro Carvalho Chehab 				delay = HZ / 10;  /* 100ms */
10733d6c2bc0SMauro Carvalho Chehab 			if (ca->open) {
1074a75aa90cSJasmin Jessich 				if ((!sl->da_irq_supported) ||
10753d6c2bc0SMauro Carvalho Chehab 				    (!(ca->flags & DVB_CA_EN50221_FLAG_IRQ_DA)))
10763d6c2bc0SMauro Carvalho Chehab 					delay = HZ / 10;  /* 100ms */
10773d6c2bc0SMauro Carvalho Chehab 			}
10783d6c2bc0SMauro Carvalho Chehab 			break;
10793d6c2bc0SMauro Carvalho Chehab 		}
10803d6c2bc0SMauro Carvalho Chehab 
10813d6c2bc0SMauro Carvalho Chehab 		if (delay < curdelay)
10823d6c2bc0SMauro Carvalho Chehab 			curdelay = delay;
10833d6c2bc0SMauro Carvalho Chehab 	}
10843d6c2bc0SMauro Carvalho Chehab 
10853d6c2bc0SMauro Carvalho Chehab 	ca->delay = curdelay;
10863d6c2bc0SMauro Carvalho Chehab }
10873d6c2bc0SMauro Carvalho Chehab 
1088a004b70eSJasmin Jessich /**
1089a4184b4fSHans Verkuil  * dvb_ca_en50221_poll_cam_gone - Poll if the CAM is gone.
10905d023252SJasmin Jessich  *
10915d023252SJasmin Jessich  * @ca: CA instance.
10925d023252SJasmin Jessich  * @slot: Slot to process.
109346e42a30SMauro Carvalho Chehab  * return:: 0 .. no change
10945d023252SJasmin Jessich  *          1 .. CAM state changed
10955d023252SJasmin Jessich  */
10965d023252SJasmin Jessich 
dvb_ca_en50221_poll_cam_gone(struct dvb_ca_private * ca,int slot)10975d023252SJasmin Jessich static int dvb_ca_en50221_poll_cam_gone(struct dvb_ca_private *ca, int slot)
10985d023252SJasmin Jessich {
10995d023252SJasmin Jessich 	int changed = 0;
11005d023252SJasmin Jessich 	int status;
11015d023252SJasmin Jessich 
11025d023252SJasmin Jessich 	/*
11035d023252SJasmin Jessich 	 * we need this extra check for annoying interfaces like the
11045d023252SJasmin Jessich 	 * budget-av
11055d023252SJasmin Jessich 	 */
11065d023252SJasmin Jessich 	if ((!(ca->flags & DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE)) &&
11075d023252SJasmin Jessich 	    (ca->pub->poll_slot_status)) {
11085d023252SJasmin Jessich 		status = ca->pub->poll_slot_status(ca->pub, slot, 0);
11095d023252SJasmin Jessich 		if (!(status &
11105d023252SJasmin Jessich 			DVB_CA_EN50221_POLL_CAM_PRESENT)) {
11115d023252SJasmin Jessich 			ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_NONE;
11125d023252SJasmin Jessich 			dvb_ca_en50221_thread_update_delay(ca);
11135d023252SJasmin Jessich 			changed = 1;
11145d023252SJasmin Jessich 		}
11155d023252SJasmin Jessich 	}
11165d023252SJasmin Jessich 	return changed;
11175d023252SJasmin Jessich }
11185d023252SJasmin Jessich 
11195d023252SJasmin Jessich /**
1120a4184b4fSHans Verkuil  * dvb_ca_en50221_thread_state_machine - Thread state machine for one CA slot
1121a4184b4fSHans Verkuil  *	to perform the data transfer.
1122a004b70eSJasmin Jessich  *
1123a004b70eSJasmin Jessich  * @ca: CA instance.
1124a004b70eSJasmin Jessich  * @slot: Slot to process.
1125a004b70eSJasmin Jessich  */
dvb_ca_en50221_thread_state_machine(struct dvb_ca_private * ca,int slot)1126a004b70eSJasmin Jessich static void dvb_ca_en50221_thread_state_machine(struct dvb_ca_private *ca,
1127a004b70eSJasmin Jessich 						int slot)
1128a004b70eSJasmin Jessich {
1129a004b70eSJasmin Jessich 	struct dvb_ca_slot *sl = &ca->slot_info[slot];
1130a004b70eSJasmin Jessich 	int flags;
1131a004b70eSJasmin Jessich 	int pktcount;
1132a004b70eSJasmin Jessich 	void *rxbuf;
11333d6c2bc0SMauro Carvalho Chehab 
1134a004b70eSJasmin Jessich 	mutex_lock(&sl->slot_lock);
1135a004b70eSJasmin Jessich 
1136a004b70eSJasmin Jessich 	/* check the cam status + deal with CAMCHANGEs */
1137a004b70eSJasmin Jessich 	while (dvb_ca_en50221_check_camstatus(ca, slot)) {
1138a004b70eSJasmin Jessich 		/* clear down an old CI slot if necessary */
1139a004b70eSJasmin Jessich 		if (sl->slot_state != DVB_CA_SLOTSTATE_NONE)
1140a004b70eSJasmin Jessich 			dvb_ca_en50221_slot_shutdown(ca, slot);
1141a004b70eSJasmin Jessich 
1142a004b70eSJasmin Jessich 		/* if a CAM is NOW present, initialise it */
1143a004b70eSJasmin Jessich 		if (sl->camchange_type == DVB_CA_EN50221_CAMCHANGE_INSERTED)
1144a004b70eSJasmin Jessich 			sl->slot_state = DVB_CA_SLOTSTATE_UNINITIALISED;
1145a004b70eSJasmin Jessich 
1146a004b70eSJasmin Jessich 		/* we've handled one CAMCHANGE */
1147a004b70eSJasmin Jessich 		dvb_ca_en50221_thread_update_delay(ca);
1148a004b70eSJasmin Jessich 		atomic_dec(&sl->camchange_count);
1149a004b70eSJasmin Jessich 	}
1150a004b70eSJasmin Jessich 
1151a004b70eSJasmin Jessich 	/* CAM state machine */
1152a004b70eSJasmin Jessich 	switch (sl->slot_state) {
1153a004b70eSJasmin Jessich 	case DVB_CA_SLOTSTATE_NONE:
1154a004b70eSJasmin Jessich 	case DVB_CA_SLOTSTATE_INVALID:
1155a004b70eSJasmin Jessich 		/* no action needed */
1156a004b70eSJasmin Jessich 		break;
1157a004b70eSJasmin Jessich 
1158a004b70eSJasmin Jessich 	case DVB_CA_SLOTSTATE_UNINITIALISED:
1159a004b70eSJasmin Jessich 		sl->slot_state = DVB_CA_SLOTSTATE_WAITREADY;
1160a004b70eSJasmin Jessich 		ca->pub->slot_reset(ca->pub, slot);
1161a004b70eSJasmin Jessich 		sl->timeout = jiffies + (INIT_TIMEOUT_SECS * HZ);
1162a004b70eSJasmin Jessich 		break;
1163a004b70eSJasmin Jessich 
1164a004b70eSJasmin Jessich 	case DVB_CA_SLOTSTATE_WAITREADY:
1165a004b70eSJasmin Jessich 		if (time_after(jiffies, sl->timeout)) {
1166a004b70eSJasmin Jessich 			pr_err("dvb_ca adaptor %d: PC card did not respond :(\n",
1167a004b70eSJasmin Jessich 			       ca->dvbdev->adapter->num);
1168a004b70eSJasmin Jessich 			sl->slot_state = DVB_CA_SLOTSTATE_INVALID;
1169a004b70eSJasmin Jessich 			dvb_ca_en50221_thread_update_delay(ca);
1170a004b70eSJasmin Jessich 			break;
1171a004b70eSJasmin Jessich 		}
1172a004b70eSJasmin Jessich 		/*
1173a004b70eSJasmin Jessich 		 * no other action needed; will automatically change state when
1174a004b70eSJasmin Jessich 		 * ready
1175a004b70eSJasmin Jessich 		 */
1176a004b70eSJasmin Jessich 		break;
1177a004b70eSJasmin Jessich 
1178a004b70eSJasmin Jessich 	case DVB_CA_SLOTSTATE_VALIDATE:
1179a004b70eSJasmin Jessich 		if (dvb_ca_en50221_parse_attributes(ca, slot) != 0) {
11805d023252SJasmin Jessich 			if (dvb_ca_en50221_poll_cam_gone(ca, slot))
1181a004b70eSJasmin Jessich 				break;
1182a004b70eSJasmin Jessich 
1183a004b70eSJasmin Jessich 			pr_err("dvb_ca adapter %d: Invalid PC card inserted :(\n",
1184a004b70eSJasmin Jessich 			       ca->dvbdev->adapter->num);
1185a004b70eSJasmin Jessich 			sl->slot_state = DVB_CA_SLOTSTATE_INVALID;
1186a004b70eSJasmin Jessich 			dvb_ca_en50221_thread_update_delay(ca);
1187a004b70eSJasmin Jessich 			break;
1188a004b70eSJasmin Jessich 		}
1189a004b70eSJasmin Jessich 		if (dvb_ca_en50221_set_configoption(ca, slot) != 0) {
1190a004b70eSJasmin Jessich 			pr_err("dvb_ca adapter %d: Unable to initialise CAM :(\n",
1191a004b70eSJasmin Jessich 			       ca->dvbdev->adapter->num);
1192a004b70eSJasmin Jessich 			sl->slot_state = DVB_CA_SLOTSTATE_INVALID;
1193a004b70eSJasmin Jessich 			dvb_ca_en50221_thread_update_delay(ca);
1194a004b70eSJasmin Jessich 			break;
1195a004b70eSJasmin Jessich 		}
1196a004b70eSJasmin Jessich 		if (ca->pub->write_cam_control(ca->pub, slot,
1197a004b70eSJasmin Jessich 					       CTRLIF_COMMAND,
1198a004b70eSJasmin Jessich 					       CMDREG_RS) != 0) {
1199a004b70eSJasmin Jessich 			pr_err("dvb_ca adapter %d: Unable to reset CAM IF\n",
1200a004b70eSJasmin Jessich 			       ca->dvbdev->adapter->num);
1201a004b70eSJasmin Jessich 			sl->slot_state = DVB_CA_SLOTSTATE_INVALID;
1202a004b70eSJasmin Jessich 			dvb_ca_en50221_thread_update_delay(ca);
1203a004b70eSJasmin Jessich 			break;
1204a004b70eSJasmin Jessich 		}
1205a004b70eSJasmin Jessich 		dprintk("DVB CAM validated successfully\n");
1206a004b70eSJasmin Jessich 
1207a004b70eSJasmin Jessich 		sl->timeout = jiffies + (INIT_TIMEOUT_SECS * HZ);
1208a004b70eSJasmin Jessich 		sl->slot_state = DVB_CA_SLOTSTATE_WAITFR;
1209a004b70eSJasmin Jessich 		ca->wakeup = 1;
1210a004b70eSJasmin Jessich 		break;
1211a004b70eSJasmin Jessich 
1212a004b70eSJasmin Jessich 	case DVB_CA_SLOTSTATE_WAITFR:
1213a004b70eSJasmin Jessich 		if (time_after(jiffies, sl->timeout)) {
1214a004b70eSJasmin Jessich 			pr_err("dvb_ca adapter %d: DVB CAM did not respond :(\n",
1215a004b70eSJasmin Jessich 			       ca->dvbdev->adapter->num);
1216a004b70eSJasmin Jessich 			sl->slot_state = DVB_CA_SLOTSTATE_INVALID;
1217a004b70eSJasmin Jessich 			dvb_ca_en50221_thread_update_delay(ca);
1218a004b70eSJasmin Jessich 			break;
1219a004b70eSJasmin Jessich 		}
1220a004b70eSJasmin Jessich 
1221a004b70eSJasmin Jessich 		flags = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS);
1222a004b70eSJasmin Jessich 		if (flags & STATUSREG_FR) {
1223a004b70eSJasmin Jessich 			sl->slot_state = DVB_CA_SLOTSTATE_LINKINIT;
1224a004b70eSJasmin Jessich 			ca->wakeup = 1;
1225a004b70eSJasmin Jessich 		}
1226a004b70eSJasmin Jessich 		break;
1227a004b70eSJasmin Jessich 
1228a004b70eSJasmin Jessich 	case DVB_CA_SLOTSTATE_LINKINIT:
1229a004b70eSJasmin Jessich 		if (dvb_ca_en50221_link_init(ca, slot) != 0) {
12305d023252SJasmin Jessich 			if (dvb_ca_en50221_poll_cam_gone(ca, slot))
1231a004b70eSJasmin Jessich 				break;
1232a004b70eSJasmin Jessich 
1233a004b70eSJasmin Jessich 			pr_err("dvb_ca adapter %d: DVB CAM link initialisation failed :(\n",
1234a004b70eSJasmin Jessich 			       ca->dvbdev->adapter->num);
1235a004b70eSJasmin Jessich 			sl->slot_state = DVB_CA_SLOTSTATE_UNINITIALISED;
1236a004b70eSJasmin Jessich 			dvb_ca_en50221_thread_update_delay(ca);
1237a004b70eSJasmin Jessich 			break;
1238a004b70eSJasmin Jessich 		}
1239a004b70eSJasmin Jessich 
1240a004b70eSJasmin Jessich 		if (!sl->rx_buffer.data) {
1241a004b70eSJasmin Jessich 			rxbuf = vmalloc(RX_BUFFER_SIZE);
1242a004b70eSJasmin Jessich 			if (!rxbuf) {
1243a004b70eSJasmin Jessich 				pr_err("dvb_ca adapter %d: Unable to allocate CAM rx buffer :(\n",
1244a004b70eSJasmin Jessich 				       ca->dvbdev->adapter->num);
1245a004b70eSJasmin Jessich 				sl->slot_state = DVB_CA_SLOTSTATE_INVALID;
1246a004b70eSJasmin Jessich 				dvb_ca_en50221_thread_update_delay(ca);
1247a004b70eSJasmin Jessich 				break;
1248a004b70eSJasmin Jessich 			}
1249a004b70eSJasmin Jessich 			dvb_ringbuffer_init(&sl->rx_buffer, rxbuf,
1250a004b70eSJasmin Jessich 					    RX_BUFFER_SIZE);
1251a004b70eSJasmin Jessich 		}
1252a004b70eSJasmin Jessich 
1253a004b70eSJasmin Jessich 		ca->pub->slot_ts_enable(ca->pub, slot);
1254a004b70eSJasmin Jessich 		sl->slot_state = DVB_CA_SLOTSTATE_RUNNING;
1255a004b70eSJasmin Jessich 		dvb_ca_en50221_thread_update_delay(ca);
12561aebed32SDaniel Scheller 		pr_info("dvb_ca adapter %d: DVB CAM detected and initialised successfully\n",
1257a004b70eSJasmin Jessich 			ca->dvbdev->adapter->num);
1258a004b70eSJasmin Jessich 		break;
1259a004b70eSJasmin Jessich 
1260a004b70eSJasmin Jessich 	case DVB_CA_SLOTSTATE_RUNNING:
1261a004b70eSJasmin Jessich 		if (!ca->open)
1262a004b70eSJasmin Jessich 			break;
1263a004b70eSJasmin Jessich 
1264a004b70eSJasmin Jessich 		/* poll slots for data */
1265a004b70eSJasmin Jessich 		pktcount = 0;
1266a004b70eSJasmin Jessich 		while (dvb_ca_en50221_read_data(ca, slot, NULL, 0) > 0) {
1267a004b70eSJasmin Jessich 			if (!ca->open)
1268a004b70eSJasmin Jessich 				break;
1269a004b70eSJasmin Jessich 
1270a004b70eSJasmin Jessich 			/*
1271a004b70eSJasmin Jessich 			 * if a CAMCHANGE occurred at some point, do not do any
1272a004b70eSJasmin Jessich 			 * more processing of this slot
1273a004b70eSJasmin Jessich 			 */
1274a004b70eSJasmin Jessich 			if (dvb_ca_en50221_check_camstatus(ca, slot)) {
1275a004b70eSJasmin Jessich 				/*
12764ecb4bfcSJasmin Jessich 				 * we don't want to sleep on the next iteration
1277a004b70eSJasmin Jessich 				 * so we can handle the cam change
1278a004b70eSJasmin Jessich 				 */
1279a004b70eSJasmin Jessich 				ca->wakeup = 1;
1280a004b70eSJasmin Jessich 				break;
1281a004b70eSJasmin Jessich 			}
1282a004b70eSJasmin Jessich 
1283a004b70eSJasmin Jessich 			/* check if we've hit our limit this time */
1284a004b70eSJasmin Jessich 			if (++pktcount >= MAX_RX_PACKETS_PER_ITERATION) {
1285a004b70eSJasmin Jessich 				/*
12864ecb4bfcSJasmin Jessich 				 * don't sleep; there is likely to be more data
1287a004b70eSJasmin Jessich 				 * to read
1288a004b70eSJasmin Jessich 				 */
1289a004b70eSJasmin Jessich 				ca->wakeup = 1;
1290a004b70eSJasmin Jessich 				break;
1291a004b70eSJasmin Jessich 			}
1292a004b70eSJasmin Jessich 		}
1293a004b70eSJasmin Jessich 		break;
1294a004b70eSJasmin Jessich 	}
1295a004b70eSJasmin Jessich 
1296a004b70eSJasmin Jessich 	mutex_unlock(&sl->slot_lock);
1297a004b70eSJasmin Jessich }
12983d6c2bc0SMauro Carvalho Chehab 
129946e42a30SMauro Carvalho Chehab /*
1300a004b70eSJasmin Jessich  * Kernel thread which monitors CA slots for CAM changes, and performs data
1301a004b70eSJasmin Jessich  * transfers.
13023d6c2bc0SMauro Carvalho Chehab  */
dvb_ca_en50221_thread(void * data)13033d6c2bc0SMauro Carvalho Chehab static int dvb_ca_en50221_thread(void *data)
13043d6c2bc0SMauro Carvalho Chehab {
13053d6c2bc0SMauro Carvalho Chehab 	struct dvb_ca_private *ca = data;
13063d6c2bc0SMauro Carvalho Chehab 	int slot;
13073d6c2bc0SMauro Carvalho Chehab 
13083d6c2bc0SMauro Carvalho Chehab 	dprintk("%s\n", __func__);
13093d6c2bc0SMauro Carvalho Chehab 
13103d6c2bc0SMauro Carvalho Chehab 	/* choose the correct initial delay */
13113d6c2bc0SMauro Carvalho Chehab 	dvb_ca_en50221_thread_update_delay(ca);
13123d6c2bc0SMauro Carvalho Chehab 
13133d6c2bc0SMauro Carvalho Chehab 	/* main loop */
13143d6c2bc0SMauro Carvalho Chehab 	while (!kthread_should_stop()) {
13153d6c2bc0SMauro Carvalho Chehab 		/* sleep for a bit */
13163d6c2bc0SMauro Carvalho Chehab 		if (!ca->wakeup) {
13173d6c2bc0SMauro Carvalho Chehab 			set_current_state(TASK_INTERRUPTIBLE);
13183d6c2bc0SMauro Carvalho Chehab 			schedule_timeout(ca->delay);
13193d6c2bc0SMauro Carvalho Chehab 			if (kthread_should_stop())
13203d6c2bc0SMauro Carvalho Chehab 				return 0;
13213d6c2bc0SMauro Carvalho Chehab 		}
13223d6c2bc0SMauro Carvalho Chehab 		ca->wakeup = 0;
13233d6c2bc0SMauro Carvalho Chehab 
13243d6c2bc0SMauro Carvalho Chehab 		/* go through all the slots processing them */
1325a004b70eSJasmin Jessich 		for (slot = 0; slot < ca->slot_count; slot++)
1326a004b70eSJasmin Jessich 			dvb_ca_en50221_thread_state_machine(ca, slot);
13273d6c2bc0SMauro Carvalho Chehab 	}
13283d6c2bc0SMauro Carvalho Chehab 
13293d6c2bc0SMauro Carvalho Chehab 	return 0;
13303d6c2bc0SMauro Carvalho Chehab }
13313d6c2bc0SMauro Carvalho Chehab 
133296375b7aSJasmin Jessich /* ************************************************************************** */
13333d6c2bc0SMauro Carvalho Chehab /* EN50221 IO interface functions */
13343d6c2bc0SMauro Carvalho Chehab 
13353d6c2bc0SMauro Carvalho Chehab /**
1336a4184b4fSHans Verkuil  * dvb_ca_en50221_io_do_ioctl - Real ioctl implementation.
13373d6c2bc0SMauro Carvalho Chehab  *
1338fbefb1a8SMauro Carvalho Chehab  * @file: File concerned.
1339fbefb1a8SMauro Carvalho Chehab  * @cmd: IOCTL command.
134046e42a30SMauro Carvalho Chehab  * @parg: Associated argument.
13413d6c2bc0SMauro Carvalho Chehab  *
1342a4184b4fSHans Verkuil  * NOTE: CA_SEND_MSG/CA_GET_MSG ioctls have userspace buffers passed to them.
1343a4184b4fSHans Verkuil  *
134446e42a30SMauro Carvalho Chehab  * return: 0 on success, <0 on error.
13453d6c2bc0SMauro Carvalho Chehab  */
dvb_ca_en50221_io_do_ioctl(struct file * file,unsigned int cmd,void * parg)13463d6c2bc0SMauro Carvalho Chehab static int dvb_ca_en50221_io_do_ioctl(struct file *file,
13473d6c2bc0SMauro Carvalho Chehab 				      unsigned int cmd, void *parg)
13483d6c2bc0SMauro Carvalho Chehab {
13493d6c2bc0SMauro Carvalho Chehab 	struct dvb_device *dvbdev = file->private_data;
13503d6c2bc0SMauro Carvalho Chehab 	struct dvb_ca_private *ca = dvbdev->priv;
13513d6c2bc0SMauro Carvalho Chehab 	int err = 0;
13523d6c2bc0SMauro Carvalho Chehab 	int slot;
13533d6c2bc0SMauro Carvalho Chehab 
13543d6c2bc0SMauro Carvalho Chehab 	dprintk("%s\n", __func__);
13553d6c2bc0SMauro Carvalho Chehab 
135630ad64b8SNikolaus Schulz 	if (mutex_lock_interruptible(&ca->ioctl_mutex))
135730ad64b8SNikolaus Schulz 		return -ERESTARTSYS;
135830ad64b8SNikolaus Schulz 
13593d6c2bc0SMauro Carvalho Chehab 	switch (cmd) {
13603d6c2bc0SMauro Carvalho Chehab 	case CA_RESET:
13613d6c2bc0SMauro Carvalho Chehab 		for (slot = 0; slot < ca->slot_count; slot++) {
1362a75aa90cSJasmin Jessich 			struct dvb_ca_slot *sl = &ca->slot_info[slot];
1363a75aa90cSJasmin Jessich 
1364a75aa90cSJasmin Jessich 			mutex_lock(&sl->slot_lock);
1365a75aa90cSJasmin Jessich 			if (sl->slot_state != DVB_CA_SLOTSTATE_NONE) {
13663d6c2bc0SMauro Carvalho Chehab 				dvb_ca_en50221_slot_shutdown(ca, slot);
13673d6c2bc0SMauro Carvalho Chehab 				if (ca->flags & DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE)
13683d6c2bc0SMauro Carvalho Chehab 					dvb_ca_en50221_camchange_irq(ca->pub,
13693d6c2bc0SMauro Carvalho Chehab 								     slot,
13703d6c2bc0SMauro Carvalho Chehab 								     DVB_CA_EN50221_CAMCHANGE_INSERTED);
13713d6c2bc0SMauro Carvalho Chehab 			}
1372a75aa90cSJasmin Jessich 			mutex_unlock(&sl->slot_lock);
13733d6c2bc0SMauro Carvalho Chehab 		}
13743d6c2bc0SMauro Carvalho Chehab 		ca->next_read_slot = 0;
13753d6c2bc0SMauro Carvalho Chehab 		dvb_ca_en50221_thread_wakeup(ca);
13763d6c2bc0SMauro Carvalho Chehab 		break;
13773d6c2bc0SMauro Carvalho Chehab 
13783d6c2bc0SMauro Carvalho Chehab 	case CA_GET_CAP: {
13793d6c2bc0SMauro Carvalho Chehab 		struct ca_caps *caps = parg;
13803d6c2bc0SMauro Carvalho Chehab 
13813d6c2bc0SMauro Carvalho Chehab 		caps->slot_num = ca->slot_count;
13823d6c2bc0SMauro Carvalho Chehab 		caps->slot_type = CA_CI_LINK;
13833d6c2bc0SMauro Carvalho Chehab 		caps->descr_num = 0;
13843d6c2bc0SMauro Carvalho Chehab 		caps->descr_type = 0;
13853d6c2bc0SMauro Carvalho Chehab 		break;
13863d6c2bc0SMauro Carvalho Chehab 	}
13873d6c2bc0SMauro Carvalho Chehab 
13883d6c2bc0SMauro Carvalho Chehab 	case CA_GET_SLOT_INFO: {
13893d6c2bc0SMauro Carvalho Chehab 		struct ca_slot_info *info = parg;
1390a75aa90cSJasmin Jessich 		struct dvb_ca_slot *sl;
13913d6c2bc0SMauro Carvalho Chehab 
1392a75aa90cSJasmin Jessich 		slot = info->num;
13936706fe55SDan Carpenter 		if ((slot >= ca->slot_count) || (slot < 0)) {
1394c4fe29a3SDan Carpenter 			err = -EINVAL;
1395c4fe29a3SDan Carpenter 			goto out_unlock;
1396c4fe29a3SDan Carpenter 		}
1397d382c5beSMauro Carvalho Chehab 		slot = array_index_nospec(slot, ca->slot_count);
13983d6c2bc0SMauro Carvalho Chehab 
13993d6c2bc0SMauro Carvalho Chehab 		info->type = CA_CI_LINK;
14003d6c2bc0SMauro Carvalho Chehab 		info->flags = 0;
1401a75aa90cSJasmin Jessich 		sl = &ca->slot_info[slot];
1402a75aa90cSJasmin Jessich 		if ((sl->slot_state != DVB_CA_SLOTSTATE_NONE) &&
1403a75aa90cSJasmin Jessich 		    (sl->slot_state != DVB_CA_SLOTSTATE_INVALID)) {
14043d6c2bc0SMauro Carvalho Chehab 			info->flags = CA_CI_MODULE_PRESENT;
14053d6c2bc0SMauro Carvalho Chehab 		}
1406a75aa90cSJasmin Jessich 		if (sl->slot_state == DVB_CA_SLOTSTATE_RUNNING)
14073d6c2bc0SMauro Carvalho Chehab 			info->flags |= CA_CI_MODULE_READY;
14083d6c2bc0SMauro Carvalho Chehab 		break;
14093d6c2bc0SMauro Carvalho Chehab 	}
14103d6c2bc0SMauro Carvalho Chehab 
14113d6c2bc0SMauro Carvalho Chehab 	default:
14123d6c2bc0SMauro Carvalho Chehab 		err = -EINVAL;
14133d6c2bc0SMauro Carvalho Chehab 		break;
14143d6c2bc0SMauro Carvalho Chehab 	}
14153d6c2bc0SMauro Carvalho Chehab 
1416c4fe29a3SDan Carpenter out_unlock:
141730ad64b8SNikolaus Schulz 	mutex_unlock(&ca->ioctl_mutex);
14183d6c2bc0SMauro Carvalho Chehab 	return err;
14193d6c2bc0SMauro Carvalho Chehab }
14203d6c2bc0SMauro Carvalho Chehab 
14213d6c2bc0SMauro Carvalho Chehab /**
1422a4184b4fSHans Verkuil  * dvb_ca_en50221_io_ioctl - Wrapper for ioctl implementation.
14233d6c2bc0SMauro Carvalho Chehab  *
1424fbefb1a8SMauro Carvalho Chehab  * @file: File concerned.
1425fbefb1a8SMauro Carvalho Chehab  * @cmd: IOCTL command.
1426fbefb1a8SMauro Carvalho Chehab  * @arg: Associated argument.
14273d6c2bc0SMauro Carvalho Chehab  *
142846e42a30SMauro Carvalho Chehab  * return: 0 on success, <0 on error.
14293d6c2bc0SMauro Carvalho Chehab  */
dvb_ca_en50221_io_ioctl(struct file * file,unsigned int cmd,unsigned long arg)14303d6c2bc0SMauro Carvalho Chehab static long dvb_ca_en50221_io_ioctl(struct file *file,
14313d6c2bc0SMauro Carvalho Chehab 				    unsigned int cmd, unsigned long arg)
14323d6c2bc0SMauro Carvalho Chehab {
14333d6c2bc0SMauro Carvalho Chehab 	return dvb_usercopy(file, cmd, arg, dvb_ca_en50221_io_do_ioctl);
14343d6c2bc0SMauro Carvalho Chehab }
14353d6c2bc0SMauro Carvalho Chehab 
14363d6c2bc0SMauro Carvalho Chehab /**
1437a4184b4fSHans Verkuil  * dvb_ca_en50221_io_write - Implementation of write() syscall.
14383d6c2bc0SMauro Carvalho Chehab  *
1439fbefb1a8SMauro Carvalho Chehab  * @file: File structure.
1440fbefb1a8SMauro Carvalho Chehab  * @buf: Source buffer.
1441fbefb1a8SMauro Carvalho Chehab  * @count: Size of source buffer.
1442fbefb1a8SMauro Carvalho Chehab  * @ppos: Position in file (ignored).
14433d6c2bc0SMauro Carvalho Chehab  *
144446e42a30SMauro Carvalho Chehab  * return: Number of bytes read, or <0 on error.
14453d6c2bc0SMauro Carvalho Chehab  */
dvb_ca_en50221_io_write(struct file * file,const char __user * buf,size_t count,loff_t * ppos)14463d6c2bc0SMauro Carvalho Chehab static ssize_t dvb_ca_en50221_io_write(struct file *file,
14478ec6107aSJasmin Jessich 				       const char __user *buf, size_t count,
14488ec6107aSJasmin Jessich 				       loff_t *ppos)
14493d6c2bc0SMauro Carvalho Chehab {
14503d6c2bc0SMauro Carvalho Chehab 	struct dvb_device *dvbdev = file->private_data;
14513d6c2bc0SMauro Carvalho Chehab 	struct dvb_ca_private *ca = dvbdev->priv;
1452a75aa90cSJasmin Jessich 	struct dvb_ca_slot *sl;
14533d6c2bc0SMauro Carvalho Chehab 	u8 slot, connection_id;
14543d6c2bc0SMauro Carvalho Chehab 	int status;
14553d6c2bc0SMauro Carvalho Chehab 	u8 fragbuf[HOST_LINK_BUF_SIZE];
14563d6c2bc0SMauro Carvalho Chehab 	int fragpos = 0;
14573d6c2bc0SMauro Carvalho Chehab 	int fraglen;
14583d6c2bc0SMauro Carvalho Chehab 	unsigned long timeout;
14593d6c2bc0SMauro Carvalho Chehab 	int written;
14603d6c2bc0SMauro Carvalho Chehab 
14613d6c2bc0SMauro Carvalho Chehab 	dprintk("%s\n", __func__);
14623d6c2bc0SMauro Carvalho Chehab 
146396375b7aSJasmin Jessich 	/*
146496375b7aSJasmin Jessich 	 * Incoming packet has a 2 byte header.
146596375b7aSJasmin Jessich 	 * hdr[0] = slot_id, hdr[1] = connection_id
146696375b7aSJasmin Jessich 	 */
14673d6c2bc0SMauro Carvalho Chehab 	if (count < 2)
14683d6c2bc0SMauro Carvalho Chehab 		return -EINVAL;
14693d6c2bc0SMauro Carvalho Chehab 
14703d6c2bc0SMauro Carvalho Chehab 	/* extract slot & connection id */
14713d6c2bc0SMauro Carvalho Chehab 	if (copy_from_user(&slot, buf, 1))
14723d6c2bc0SMauro Carvalho Chehab 		return -EFAULT;
14733d6c2bc0SMauro Carvalho Chehab 	if (copy_from_user(&connection_id, buf + 1, 1))
14743d6c2bc0SMauro Carvalho Chehab 		return -EFAULT;
14753d6c2bc0SMauro Carvalho Chehab 	buf += 2;
14763d6c2bc0SMauro Carvalho Chehab 	count -= 2;
1477a24e6348SColin Ian King 
1478a24e6348SColin Ian King 	if (slot >= ca->slot_count)
1479a24e6348SColin Ian King 		return -EINVAL;
14804f5ab5d7SMauro Carvalho Chehab 	slot = array_index_nospec(slot, ca->slot_count);
1481a75aa90cSJasmin Jessich 	sl = &ca->slot_info[slot];
14823d6c2bc0SMauro Carvalho Chehab 
14833d6c2bc0SMauro Carvalho Chehab 	/* check if the slot is actually running */
1484a75aa90cSJasmin Jessich 	if (sl->slot_state != DVB_CA_SLOTSTATE_RUNNING)
14853d6c2bc0SMauro Carvalho Chehab 		return -EINVAL;
14863d6c2bc0SMauro Carvalho Chehab 
14873d6c2bc0SMauro Carvalho Chehab 	/* fragment the packets & store in the buffer */
14883d6c2bc0SMauro Carvalho Chehab 	while (fragpos < count) {
1489a75aa90cSJasmin Jessich 		fraglen = sl->link_buf_size - 2;
14903d6c2bc0SMauro Carvalho Chehab 		if (fraglen < 0)
14913d6c2bc0SMauro Carvalho Chehab 			break;
14923d6c2bc0SMauro Carvalho Chehab 		if (fraglen > HOST_LINK_BUF_SIZE - 2)
14933d6c2bc0SMauro Carvalho Chehab 			fraglen = HOST_LINK_BUF_SIZE - 2;
14943d6c2bc0SMauro Carvalho Chehab 		if ((count - fragpos) < fraglen)
14953d6c2bc0SMauro Carvalho Chehab 			fraglen = count - fragpos;
14963d6c2bc0SMauro Carvalho Chehab 
14973d6c2bc0SMauro Carvalho Chehab 		fragbuf[0] = connection_id;
14983d6c2bc0SMauro Carvalho Chehab 		fragbuf[1] = ((fragpos + fraglen) < count) ? 0x80 : 0x00;
14993d6c2bc0SMauro Carvalho Chehab 		status = copy_from_user(fragbuf + 2, buf + fragpos, fraglen);
15003d6c2bc0SMauro Carvalho Chehab 		if (status) {
15013d6c2bc0SMauro Carvalho Chehab 			status = -EFAULT;
15023d6c2bc0SMauro Carvalho Chehab 			goto exit;
15033d6c2bc0SMauro Carvalho Chehab 		}
15043d6c2bc0SMauro Carvalho Chehab 
15053d6c2bc0SMauro Carvalho Chehab 		timeout = jiffies + HZ / 2;
15063d6c2bc0SMauro Carvalho Chehab 		written = 0;
15073d6c2bc0SMauro Carvalho Chehab 		while (!time_after(jiffies, timeout)) {
150896375b7aSJasmin Jessich 			/*
150996375b7aSJasmin Jessich 			 * check the CAM hasn't been removed/reset in the
151096375b7aSJasmin Jessich 			 * meantime
151196375b7aSJasmin Jessich 			 */
1512a75aa90cSJasmin Jessich 			if (sl->slot_state != DVB_CA_SLOTSTATE_RUNNING) {
15133d6c2bc0SMauro Carvalho Chehab 				status = -EIO;
15143d6c2bc0SMauro Carvalho Chehab 				goto exit;
15153d6c2bc0SMauro Carvalho Chehab 			}
15163d6c2bc0SMauro Carvalho Chehab 
1517a75aa90cSJasmin Jessich 			mutex_lock(&sl->slot_lock);
151896375b7aSJasmin Jessich 			status = dvb_ca_en50221_write_data(ca, slot, fragbuf,
1519a4315e5bSYongSu Yoo 							   fraglen + 2, 0);
1520a75aa90cSJasmin Jessich 			mutex_unlock(&sl->slot_lock);
15213d6c2bc0SMauro Carvalho Chehab 			if (status == (fraglen + 2)) {
15223d6c2bc0SMauro Carvalho Chehab 				written = 1;
15233d6c2bc0SMauro Carvalho Chehab 				break;
15243d6c2bc0SMauro Carvalho Chehab 			}
15253d6c2bc0SMauro Carvalho Chehab 			if (status != -EAGAIN)
15263d6c2bc0SMauro Carvalho Chehab 				goto exit;
15273d6c2bc0SMauro Carvalho Chehab 
1528b9af29e1SJasmin Jessich 			usleep_range(1000, 1100);
15293d6c2bc0SMauro Carvalho Chehab 		}
15303d6c2bc0SMauro Carvalho Chehab 		if (!written) {
15313d6c2bc0SMauro Carvalho Chehab 			status = -EIO;
15323d6c2bc0SMauro Carvalho Chehab 			goto exit;
15333d6c2bc0SMauro Carvalho Chehab 		}
15343d6c2bc0SMauro Carvalho Chehab 
15353d6c2bc0SMauro Carvalho Chehab 		fragpos += fraglen;
15363d6c2bc0SMauro Carvalho Chehab 	}
15373d6c2bc0SMauro Carvalho Chehab 	status = count + 2;
15383d6c2bc0SMauro Carvalho Chehab 
15393d6c2bc0SMauro Carvalho Chehab exit:
15403d6c2bc0SMauro Carvalho Chehab 	return status;
15413d6c2bc0SMauro Carvalho Chehab }
15423d6c2bc0SMauro Carvalho Chehab 
154346e42a30SMauro Carvalho Chehab /*
15443d6c2bc0SMauro Carvalho Chehab  * Condition for waking up in dvb_ca_en50221_io_read_condition
15453d6c2bc0SMauro Carvalho Chehab  */
dvb_ca_en50221_io_read_condition(struct dvb_ca_private * ca,int * result,int * _slot)15463d6c2bc0SMauro Carvalho Chehab static int dvb_ca_en50221_io_read_condition(struct dvb_ca_private *ca,
15473d6c2bc0SMauro Carvalho Chehab 					    int *result, int *_slot)
15483d6c2bc0SMauro Carvalho Chehab {
15493d6c2bc0SMauro Carvalho Chehab 	int slot;
15503d6c2bc0SMauro Carvalho Chehab 	int slot_count = 0;
15513d6c2bc0SMauro Carvalho Chehab 	int idx;
15523d6c2bc0SMauro Carvalho Chehab 	size_t fraglen;
15533d6c2bc0SMauro Carvalho Chehab 	int connection_id = -1;
15543d6c2bc0SMauro Carvalho Chehab 	int found = 0;
15553d6c2bc0SMauro Carvalho Chehab 	u8 hdr[2];
15563d6c2bc0SMauro Carvalho Chehab 
15573d6c2bc0SMauro Carvalho Chehab 	slot = ca->next_read_slot;
15583d6c2bc0SMauro Carvalho Chehab 	while ((slot_count < ca->slot_count) && (!found)) {
1559a75aa90cSJasmin Jessich 		struct dvb_ca_slot *sl = &ca->slot_info[slot];
1560a75aa90cSJasmin Jessich 
1561a75aa90cSJasmin Jessich 		if (sl->slot_state != DVB_CA_SLOTSTATE_RUNNING)
15623d6c2bc0SMauro Carvalho Chehab 			goto nextslot;
15633d6c2bc0SMauro Carvalho Chehab 
1564a75aa90cSJasmin Jessich 		if (!sl->rx_buffer.data)
15653d6c2bc0SMauro Carvalho Chehab 			return 0;
15663d6c2bc0SMauro Carvalho Chehab 
1567a75aa90cSJasmin Jessich 		idx = dvb_ringbuffer_pkt_next(&sl->rx_buffer, -1, &fraglen);
15683d6c2bc0SMauro Carvalho Chehab 		while (idx != -1) {
1569a75aa90cSJasmin Jessich 			dvb_ringbuffer_pkt_read(&sl->rx_buffer, idx, 0, hdr, 2);
15703d6c2bc0SMauro Carvalho Chehab 			if (connection_id == -1)
15713d6c2bc0SMauro Carvalho Chehab 				connection_id = hdr[0];
157296375b7aSJasmin Jessich 			if ((hdr[0] == connection_id) &&
157396375b7aSJasmin Jessich 			    ((hdr[1] & 0x80) == 0)) {
15743d6c2bc0SMauro Carvalho Chehab 				*_slot = slot;
15753d6c2bc0SMauro Carvalho Chehab 				found = 1;
15763d6c2bc0SMauro Carvalho Chehab 				break;
15773d6c2bc0SMauro Carvalho Chehab 			}
15783d6c2bc0SMauro Carvalho Chehab 
1579a75aa90cSJasmin Jessich 			idx = dvb_ringbuffer_pkt_next(&sl->rx_buffer, idx,
1580a75aa90cSJasmin Jessich 						      &fraglen);
15813d6c2bc0SMauro Carvalho Chehab 		}
15823d6c2bc0SMauro Carvalho Chehab 
15833d6c2bc0SMauro Carvalho Chehab nextslot:
15843d6c2bc0SMauro Carvalho Chehab 		slot = (slot + 1) % ca->slot_count;
15853d6c2bc0SMauro Carvalho Chehab 		slot_count++;
15863d6c2bc0SMauro Carvalho Chehab 	}
15873d6c2bc0SMauro Carvalho Chehab 
15883d6c2bc0SMauro Carvalho Chehab 	ca->next_read_slot = slot;
15893d6c2bc0SMauro Carvalho Chehab 	return found;
15903d6c2bc0SMauro Carvalho Chehab }
15913d6c2bc0SMauro Carvalho Chehab 
15923d6c2bc0SMauro Carvalho Chehab /**
1593a4184b4fSHans Verkuil  * dvb_ca_en50221_io_read - Implementation of read() syscall.
15943d6c2bc0SMauro Carvalho Chehab  *
1595fbefb1a8SMauro Carvalho Chehab  * @file: File structure.
1596fbefb1a8SMauro Carvalho Chehab  * @buf: Destination buffer.
1597fbefb1a8SMauro Carvalho Chehab  * @count: Size of destination buffer.
1598fbefb1a8SMauro Carvalho Chehab  * @ppos: Position in file (ignored).
15993d6c2bc0SMauro Carvalho Chehab  *
160046e42a30SMauro Carvalho Chehab  * return: Number of bytes read, or <0 on error.
16013d6c2bc0SMauro Carvalho Chehab  */
dvb_ca_en50221_io_read(struct file * file,char __user * buf,size_t count,loff_t * ppos)16023d6c2bc0SMauro Carvalho Chehab static ssize_t dvb_ca_en50221_io_read(struct file *file, char __user *buf,
16033d6c2bc0SMauro Carvalho Chehab 				      size_t count, loff_t *ppos)
16043d6c2bc0SMauro Carvalho Chehab {
16053d6c2bc0SMauro Carvalho Chehab 	struct dvb_device *dvbdev = file->private_data;
16063d6c2bc0SMauro Carvalho Chehab 	struct dvb_ca_private *ca = dvbdev->priv;
1607a75aa90cSJasmin Jessich 	struct dvb_ca_slot *sl;
16083d6c2bc0SMauro Carvalho Chehab 	int status;
16093d6c2bc0SMauro Carvalho Chehab 	int result = 0;
16103d6c2bc0SMauro Carvalho Chehab 	u8 hdr[2];
16113d6c2bc0SMauro Carvalho Chehab 	int slot;
16123d6c2bc0SMauro Carvalho Chehab 	int connection_id = -1;
16133d6c2bc0SMauro Carvalho Chehab 	size_t idx, idx2;
16143d6c2bc0SMauro Carvalho Chehab 	int last_fragment = 0;
16153d6c2bc0SMauro Carvalho Chehab 	size_t fraglen;
16163d6c2bc0SMauro Carvalho Chehab 	int pktlen;
16173d6c2bc0SMauro Carvalho Chehab 	int dispose = 0;
16183d6c2bc0SMauro Carvalho Chehab 
16193d6c2bc0SMauro Carvalho Chehab 	dprintk("%s\n", __func__);
16203d6c2bc0SMauro Carvalho Chehab 
162196375b7aSJasmin Jessich 	/*
162296375b7aSJasmin Jessich 	 * Outgoing packet has a 2 byte header.
162396375b7aSJasmin Jessich 	 * hdr[0] = slot_id, hdr[1] = connection_id
162496375b7aSJasmin Jessich 	 */
16253d6c2bc0SMauro Carvalho Chehab 	if (count < 2)
16263d6c2bc0SMauro Carvalho Chehab 		return -EINVAL;
16273d6c2bc0SMauro Carvalho Chehab 
16283d6c2bc0SMauro Carvalho Chehab 	/* wait for some data */
1629bacba9e5SJasmin Jessich 	status = dvb_ca_en50221_io_read_condition(ca, &result, &slot);
1630bacba9e5SJasmin Jessich 	if (status == 0) {
16313d6c2bc0SMauro Carvalho Chehab 		/* if we're in nonblocking mode, exit immediately */
16323d6c2bc0SMauro Carvalho Chehab 		if (file->f_flags & O_NONBLOCK)
16333d6c2bc0SMauro Carvalho Chehab 			return -EWOULDBLOCK;
16343d6c2bc0SMauro Carvalho Chehab 
16353d6c2bc0SMauro Carvalho Chehab 		/* wait for some data */
16363d6c2bc0SMauro Carvalho Chehab 		status = wait_event_interruptible(ca->wait_queue,
16373d6c2bc0SMauro Carvalho Chehab 						  dvb_ca_en50221_io_read_condition
16383d6c2bc0SMauro Carvalho Chehab 						  (ca, &result, &slot));
16393d6c2bc0SMauro Carvalho Chehab 	}
16403d6c2bc0SMauro Carvalho Chehab 	if ((status < 0) || (result < 0)) {
16413d6c2bc0SMauro Carvalho Chehab 		if (result)
16423d6c2bc0SMauro Carvalho Chehab 			return result;
16433d6c2bc0SMauro Carvalho Chehab 		return status;
16443d6c2bc0SMauro Carvalho Chehab 	}
16453d6c2bc0SMauro Carvalho Chehab 
1646a75aa90cSJasmin Jessich 	sl = &ca->slot_info[slot];
1647a75aa90cSJasmin Jessich 	idx = dvb_ringbuffer_pkt_next(&sl->rx_buffer, -1, &fraglen);
16483d6c2bc0SMauro Carvalho Chehab 	pktlen = 2;
16493d6c2bc0SMauro Carvalho Chehab 	do {
16503d6c2bc0SMauro Carvalho Chehab 		if (idx == -1) {
1651b3ad24d2SMauro Carvalho Chehab 			pr_err("dvb_ca adapter %d: BUG: read packet ended before last_fragment encountered\n",
1652b3ad24d2SMauro Carvalho Chehab 			       ca->dvbdev->adapter->num);
16533d6c2bc0SMauro Carvalho Chehab 			status = -EIO;
16543d6c2bc0SMauro Carvalho Chehab 			goto exit;
16553d6c2bc0SMauro Carvalho Chehab 		}
16563d6c2bc0SMauro Carvalho Chehab 
1657a75aa90cSJasmin Jessich 		dvb_ringbuffer_pkt_read(&sl->rx_buffer, idx, 0, hdr, 2);
16583d6c2bc0SMauro Carvalho Chehab 		if (connection_id == -1)
16593d6c2bc0SMauro Carvalho Chehab 			connection_id = hdr[0];
16603d6c2bc0SMauro Carvalho Chehab 		if (hdr[0] == connection_id) {
16613d6c2bc0SMauro Carvalho Chehab 			if (pktlen < count) {
16627bd8cc8fSJasmin Jessich 				if ((pktlen + fraglen - 2) > count)
16633d6c2bc0SMauro Carvalho Chehab 					fraglen = count - pktlen;
16647bd8cc8fSJasmin Jessich 				else
16653d6c2bc0SMauro Carvalho Chehab 					fraglen -= 2;
16663d6c2bc0SMauro Carvalho Chehab 
1667a75aa90cSJasmin Jessich 				status =
1668a75aa90cSJasmin Jessich 				   dvb_ringbuffer_pkt_read_user(&sl->rx_buffer,
1669a75aa90cSJasmin Jessich 								idx, 2,
1670a75aa90cSJasmin Jessich 								buf + pktlen,
1671a75aa90cSJasmin Jessich 								fraglen);
1672a75aa90cSJasmin Jessich 				if (status < 0)
16733d6c2bc0SMauro Carvalho Chehab 					goto exit;
1674a75aa90cSJasmin Jessich 
16753d6c2bc0SMauro Carvalho Chehab 				pktlen += fraglen;
16763d6c2bc0SMauro Carvalho Chehab 			}
16773d6c2bc0SMauro Carvalho Chehab 
16783d6c2bc0SMauro Carvalho Chehab 			if ((hdr[1] & 0x80) == 0)
16793d6c2bc0SMauro Carvalho Chehab 				last_fragment = 1;
16803d6c2bc0SMauro Carvalho Chehab 			dispose = 1;
16813d6c2bc0SMauro Carvalho Chehab 		}
16823d6c2bc0SMauro Carvalho Chehab 
1683a75aa90cSJasmin Jessich 		idx2 = dvb_ringbuffer_pkt_next(&sl->rx_buffer, idx, &fraglen);
16843d6c2bc0SMauro Carvalho Chehab 		if (dispose)
1685a75aa90cSJasmin Jessich 			dvb_ringbuffer_pkt_dispose(&sl->rx_buffer, idx);
16863d6c2bc0SMauro Carvalho Chehab 		idx = idx2;
16873d6c2bc0SMauro Carvalho Chehab 		dispose = 0;
16883d6c2bc0SMauro Carvalho Chehab 	} while (!last_fragment);
16893d6c2bc0SMauro Carvalho Chehab 
16903d6c2bc0SMauro Carvalho Chehab 	hdr[0] = slot;
16913d6c2bc0SMauro Carvalho Chehab 	hdr[1] = connection_id;
16923d6c2bc0SMauro Carvalho Chehab 	status = copy_to_user(buf, hdr, 2);
16933d6c2bc0SMauro Carvalho Chehab 	if (status) {
16943d6c2bc0SMauro Carvalho Chehab 		status = -EFAULT;
16953d6c2bc0SMauro Carvalho Chehab 		goto exit;
16963d6c2bc0SMauro Carvalho Chehab 	}
16973d6c2bc0SMauro Carvalho Chehab 	status = pktlen;
16983d6c2bc0SMauro Carvalho Chehab 
16993d6c2bc0SMauro Carvalho Chehab exit:
17003d6c2bc0SMauro Carvalho Chehab 	return status;
17013d6c2bc0SMauro Carvalho Chehab }
17023d6c2bc0SMauro Carvalho Chehab 
17033d6c2bc0SMauro Carvalho Chehab /**
1704a4184b4fSHans Verkuil  * dvb_ca_en50221_io_open - Implementation of file open syscall.
17053d6c2bc0SMauro Carvalho Chehab  *
1706fbefb1a8SMauro Carvalho Chehab  * @inode: Inode concerned.
1707fbefb1a8SMauro Carvalho Chehab  * @file: File concerned.
17083d6c2bc0SMauro Carvalho Chehab  *
170946e42a30SMauro Carvalho Chehab  * return: 0 on success, <0 on failure.
17103d6c2bc0SMauro Carvalho Chehab  */
dvb_ca_en50221_io_open(struct inode * inode,struct file * file)17113d6c2bc0SMauro Carvalho Chehab static int dvb_ca_en50221_io_open(struct inode *inode, struct file *file)
17123d6c2bc0SMauro Carvalho Chehab {
17133d6c2bc0SMauro Carvalho Chehab 	struct dvb_device *dvbdev = file->private_data;
17143d6c2bc0SMauro Carvalho Chehab 	struct dvb_ca_private *ca = dvbdev->priv;
17153d6c2bc0SMauro Carvalho Chehab 	int err;
17163d6c2bc0SMauro Carvalho Chehab 	int i;
17173d6c2bc0SMauro Carvalho Chehab 
17183d6c2bc0SMauro Carvalho Chehab 	dprintk("%s\n", __func__);
17193d6c2bc0SMauro Carvalho Chehab 
1720*280a8ab8SHyunwoo Kim 	mutex_lock(&ca->remove_mutex);
1721*280a8ab8SHyunwoo Kim 
1722*280a8ab8SHyunwoo Kim 	if (ca->exit) {
1723*280a8ab8SHyunwoo Kim 		mutex_unlock(&ca->remove_mutex);
1724*280a8ab8SHyunwoo Kim 		return -ENODEV;
1725*280a8ab8SHyunwoo Kim 	}
1726*280a8ab8SHyunwoo Kim 
1727*280a8ab8SHyunwoo Kim 	if (!try_module_get(ca->pub->owner)) {
1728*280a8ab8SHyunwoo Kim 		mutex_unlock(&ca->remove_mutex);
17293d6c2bc0SMauro Carvalho Chehab 		return -EIO;
1730*280a8ab8SHyunwoo Kim 	}
17313d6c2bc0SMauro Carvalho Chehab 
17323d6c2bc0SMauro Carvalho Chehab 	err = dvb_generic_open(inode, file);
17333d6c2bc0SMauro Carvalho Chehab 	if (err < 0) {
17343d6c2bc0SMauro Carvalho Chehab 		module_put(ca->pub->owner);
1735*280a8ab8SHyunwoo Kim 		mutex_unlock(&ca->remove_mutex);
17363d6c2bc0SMauro Carvalho Chehab 		return err;
17373d6c2bc0SMauro Carvalho Chehab 	}
17383d6c2bc0SMauro Carvalho Chehab 
17393d6c2bc0SMauro Carvalho Chehab 	for (i = 0; i < ca->slot_count; i++) {
1740a75aa90cSJasmin Jessich 		struct dvb_ca_slot *sl = &ca->slot_info[i];
17413d6c2bc0SMauro Carvalho Chehab 
1742a75aa90cSJasmin Jessich 		if (sl->slot_state == DVB_CA_SLOTSTATE_RUNNING) {
1743a75aa90cSJasmin Jessich 			if (!sl->rx_buffer.data) {
1744a1ca23d1SJasmin Jessich 				/*
1745a1ca23d1SJasmin Jessich 				 * it is safe to call this here without locks
1746a1ca23d1SJasmin Jessich 				 * because ca->open == 0. Data is not read in
1747a1ca23d1SJasmin Jessich 				 * this case
1748a1ca23d1SJasmin Jessich 				 */
1749a75aa90cSJasmin Jessich 				dvb_ringbuffer_flush(&sl->rx_buffer);
17503d6c2bc0SMauro Carvalho Chehab 			}
17513d6c2bc0SMauro Carvalho Chehab 		}
17523d6c2bc0SMauro Carvalho Chehab 	}
17533d6c2bc0SMauro Carvalho Chehab 
17543d6c2bc0SMauro Carvalho Chehab 	ca->open = 1;
17553d6c2bc0SMauro Carvalho Chehab 	dvb_ca_en50221_thread_update_delay(ca);
17563d6c2bc0SMauro Carvalho Chehab 	dvb_ca_en50221_thread_wakeup(ca);
17573d6c2bc0SMauro Carvalho Chehab 
1758da677fe1SMax Kellermann 	dvb_ca_private_get(ca);
1759da677fe1SMax Kellermann 
1760*280a8ab8SHyunwoo Kim 	mutex_unlock(&ca->remove_mutex);
17613d6c2bc0SMauro Carvalho Chehab 	return 0;
17623d6c2bc0SMauro Carvalho Chehab }
17633d6c2bc0SMauro Carvalho Chehab 
17643d6c2bc0SMauro Carvalho Chehab /**
1765a4184b4fSHans Verkuil  * dvb_ca_en50221_io_release - Implementation of file close syscall.
17663d6c2bc0SMauro Carvalho Chehab  *
1767fbefb1a8SMauro Carvalho Chehab  * @inode: Inode concerned.
1768fbefb1a8SMauro Carvalho Chehab  * @file: File concerned.
17693d6c2bc0SMauro Carvalho Chehab  *
177046e42a30SMauro Carvalho Chehab  * return: 0 on success, <0 on failure.
17713d6c2bc0SMauro Carvalho Chehab  */
dvb_ca_en50221_io_release(struct inode * inode,struct file * file)17723d6c2bc0SMauro Carvalho Chehab static int dvb_ca_en50221_io_release(struct inode *inode, struct file *file)
17733d6c2bc0SMauro Carvalho Chehab {
17743d6c2bc0SMauro Carvalho Chehab 	struct dvb_device *dvbdev = file->private_data;
17753d6c2bc0SMauro Carvalho Chehab 	struct dvb_ca_private *ca = dvbdev->priv;
17763d6c2bc0SMauro Carvalho Chehab 	int err;
17773d6c2bc0SMauro Carvalho Chehab 
17783d6c2bc0SMauro Carvalho Chehab 	dprintk("%s\n", __func__);
17793d6c2bc0SMauro Carvalho Chehab 
1780*280a8ab8SHyunwoo Kim 	mutex_lock(&ca->remove_mutex);
1781*280a8ab8SHyunwoo Kim 
17823d6c2bc0SMauro Carvalho Chehab 	/* mark the CA device as closed */
17833d6c2bc0SMauro Carvalho Chehab 	ca->open = 0;
17843d6c2bc0SMauro Carvalho Chehab 	dvb_ca_en50221_thread_update_delay(ca);
17853d6c2bc0SMauro Carvalho Chehab 
17863d6c2bc0SMauro Carvalho Chehab 	err = dvb_generic_release(inode, file);
17873d6c2bc0SMauro Carvalho Chehab 
17883d6c2bc0SMauro Carvalho Chehab 	module_put(ca->pub->owner);
17893d6c2bc0SMauro Carvalho Chehab 
1790da677fe1SMax Kellermann 	dvb_ca_private_put(ca);
1791da677fe1SMax Kellermann 
1792*280a8ab8SHyunwoo Kim 	if (dvbdev->users == 1 && ca->exit == 1) {
1793*280a8ab8SHyunwoo Kim 		mutex_unlock(&ca->remove_mutex);
1794*280a8ab8SHyunwoo Kim 		wake_up(&dvbdev->wait_queue);
1795*280a8ab8SHyunwoo Kim 	} else {
1796*280a8ab8SHyunwoo Kim 		mutex_unlock(&ca->remove_mutex);
1797*280a8ab8SHyunwoo Kim 	}
1798*280a8ab8SHyunwoo Kim 
17993d6c2bc0SMauro Carvalho Chehab 	return err;
18003d6c2bc0SMauro Carvalho Chehab }
18013d6c2bc0SMauro Carvalho Chehab 
18023d6c2bc0SMauro Carvalho Chehab /**
1803a4184b4fSHans Verkuil  * dvb_ca_en50221_io_poll - Implementation of poll() syscall.
18043d6c2bc0SMauro Carvalho Chehab  *
1805fbefb1a8SMauro Carvalho Chehab  * @file: File concerned.
1806fbefb1a8SMauro Carvalho Chehab  * @wait: poll wait table.
18073d6c2bc0SMauro Carvalho Chehab  *
180846e42a30SMauro Carvalho Chehab  * return: Standard poll mask.
18093d6c2bc0SMauro Carvalho Chehab  */
dvb_ca_en50221_io_poll(struct file * file,poll_table * wait)1810c23e0cb8SAl Viro static __poll_t dvb_ca_en50221_io_poll(struct file *file, poll_table *wait)
18113d6c2bc0SMauro Carvalho Chehab {
18123d6c2bc0SMauro Carvalho Chehab 	struct dvb_device *dvbdev = file->private_data;
18133d6c2bc0SMauro Carvalho Chehab 	struct dvb_ca_private *ca = dvbdev->priv;
1814c23e0cb8SAl Viro 	__poll_t mask = 0;
18153d6c2bc0SMauro Carvalho Chehab 	int slot;
18163d6c2bc0SMauro Carvalho Chehab 	int result = 0;
18173d6c2bc0SMauro Carvalho Chehab 
18183d6c2bc0SMauro Carvalho Chehab 	dprintk("%s\n", __func__);
18193d6c2bc0SMauro Carvalho Chehab 
1820c6f5c7c2SHans Verkuil 	poll_wait(file, &ca->wait_queue, wait);
1821c6f5c7c2SHans Verkuil 
18227bd8cc8fSJasmin Jessich 	if (dvb_ca_en50221_io_read_condition(ca, &result, &slot) == 1)
1823a9a08845SLinus Torvalds 		mask |= EPOLLIN;
18243d6c2bc0SMauro Carvalho Chehab 
18253d6c2bc0SMauro Carvalho Chehab 	/* if there is something, return now */
18263d6c2bc0SMauro Carvalho Chehab 	if (mask)
18273d6c2bc0SMauro Carvalho Chehab 		return mask;
18283d6c2bc0SMauro Carvalho Chehab 
18297bd8cc8fSJasmin Jessich 	if (dvb_ca_en50221_io_read_condition(ca, &result, &slot) == 1)
1830a9a08845SLinus Torvalds 		mask |= EPOLLIN;
18313d6c2bc0SMauro Carvalho Chehab 
18323d6c2bc0SMauro Carvalho Chehab 	return mask;
18333d6c2bc0SMauro Carvalho Chehab }
18343d6c2bc0SMauro Carvalho Chehab 
18353d6c2bc0SMauro Carvalho Chehab static const struct file_operations dvb_ca_fops = {
18363d6c2bc0SMauro Carvalho Chehab 	.owner = THIS_MODULE,
18373d6c2bc0SMauro Carvalho Chehab 	.read = dvb_ca_en50221_io_read,
18383d6c2bc0SMauro Carvalho Chehab 	.write = dvb_ca_en50221_io_write,
18393d6c2bc0SMauro Carvalho Chehab 	.unlocked_ioctl = dvb_ca_en50221_io_ioctl,
18403d6c2bc0SMauro Carvalho Chehab 	.open = dvb_ca_en50221_io_open,
18413d6c2bc0SMauro Carvalho Chehab 	.release = dvb_ca_en50221_io_release,
18423d6c2bc0SMauro Carvalho Chehab 	.poll = dvb_ca_en50221_io_poll,
18433d6c2bc0SMauro Carvalho Chehab 	.llseek = noop_llseek,
18443d6c2bc0SMauro Carvalho Chehab };
18453d6c2bc0SMauro Carvalho Chehab 
1846738a1b1eSMauro Carvalho Chehab static const struct dvb_device dvbdev_ca = {
18473d6c2bc0SMauro Carvalho Chehab 	.priv = NULL,
18483d6c2bc0SMauro Carvalho Chehab 	.users = 1,
18493d6c2bc0SMauro Carvalho Chehab 	.readers = 1,
18503d6c2bc0SMauro Carvalho Chehab 	.writers = 1,
1851738a1b1eSMauro Carvalho Chehab #if defined(CONFIG_MEDIA_CONTROLLER_DVB)
1852e4fd3bc5SMauro Carvalho Chehab 	.name = "dvb-ca-en50221",
1853738a1b1eSMauro Carvalho Chehab #endif
18543d6c2bc0SMauro Carvalho Chehab 	.fops = &dvb_ca_fops,
18553d6c2bc0SMauro Carvalho Chehab };
18563d6c2bc0SMauro Carvalho Chehab 
185796375b7aSJasmin Jessich /* ************************************************************************** */
18583d6c2bc0SMauro Carvalho Chehab /* Initialisation/shutdown functions */
18593d6c2bc0SMauro Carvalho Chehab 
18603d6c2bc0SMauro Carvalho Chehab /**
1861a4184b4fSHans Verkuil  * dvb_ca_en50221_init - Initialise a new DVB CA EN50221 interface device.
18623d6c2bc0SMauro Carvalho Chehab  *
1863fbefb1a8SMauro Carvalho Chehab  * @dvb_adapter: DVB adapter to attach the new CA device to.
186446e42a30SMauro Carvalho Chehab  * @pubca: The dvb_ca instance.
1865fbefb1a8SMauro Carvalho Chehab  * @flags: Flags describing the CA device (DVB_CA_FLAG_*).
1866fbefb1a8SMauro Carvalho Chehab  * @slot_count: Number of slots supported.
18673d6c2bc0SMauro Carvalho Chehab  *
186846e42a30SMauro Carvalho Chehab  * return: 0 on success, nonzero on failure
18693d6c2bc0SMauro Carvalho Chehab  */
dvb_ca_en50221_init(struct dvb_adapter * dvb_adapter,struct dvb_ca_en50221 * pubca,int flags,int slot_count)18703d6c2bc0SMauro Carvalho Chehab int dvb_ca_en50221_init(struct dvb_adapter *dvb_adapter,
18713d6c2bc0SMauro Carvalho Chehab 			struct dvb_ca_en50221 *pubca, int flags, int slot_count)
18723d6c2bc0SMauro Carvalho Chehab {
18733d6c2bc0SMauro Carvalho Chehab 	int ret;
18743d6c2bc0SMauro Carvalho Chehab 	struct dvb_ca_private *ca = NULL;
18753d6c2bc0SMauro Carvalho Chehab 	int i;
18763d6c2bc0SMauro Carvalho Chehab 
18773d6c2bc0SMauro Carvalho Chehab 	dprintk("%s\n", __func__);
18783d6c2bc0SMauro Carvalho Chehab 
18793d6c2bc0SMauro Carvalho Chehab 	if (slot_count < 1)
18803d6c2bc0SMauro Carvalho Chehab 		return -EINVAL;
18813d6c2bc0SMauro Carvalho Chehab 
18823d6c2bc0SMauro Carvalho Chehab 	/* initialise the system data */
1883bacba9e5SJasmin Jessich 	ca = kzalloc(sizeof(*ca), GFP_KERNEL);
1884bacba9e5SJasmin Jessich 	if (!ca) {
18853d6c2bc0SMauro Carvalho Chehab 		ret = -ENOMEM;
1886a2bbf5d0SMarkus Elfring 		goto exit;
18873d6c2bc0SMauro Carvalho Chehab 	}
1888da677fe1SMax Kellermann 	kref_init(&ca->refcount);
18893d6c2bc0SMauro Carvalho Chehab 	ca->pub = pubca;
18903d6c2bc0SMauro Carvalho Chehab 	ca->flags = flags;
18913d6c2bc0SMauro Carvalho Chehab 	ca->slot_count = slot_count;
1892bacba9e5SJasmin Jessich 	ca->slot_info = kcalloc(slot_count, sizeof(struct dvb_ca_slot),
1893bacba9e5SJasmin Jessich 				GFP_KERNEL);
1894bacba9e5SJasmin Jessich 	if (!ca->slot_info) {
18953d6c2bc0SMauro Carvalho Chehab 		ret = -ENOMEM;
1896a2bbf5d0SMarkus Elfring 		goto free_ca;
18973d6c2bc0SMauro Carvalho Chehab 	}
18983d6c2bc0SMauro Carvalho Chehab 	init_waitqueue_head(&ca->wait_queue);
18993d6c2bc0SMauro Carvalho Chehab 	ca->open = 0;
19003d6c2bc0SMauro Carvalho Chehab 	ca->wakeup = 0;
19013d6c2bc0SMauro Carvalho Chehab 	ca->next_read_slot = 0;
19023d6c2bc0SMauro Carvalho Chehab 	pubca->private = ca;
19033d6c2bc0SMauro Carvalho Chehab 
19043d6c2bc0SMauro Carvalho Chehab 	/* register the DVB device */
190596375b7aSJasmin Jessich 	ret = dvb_register_device(dvb_adapter, &ca->dvbdev, &dvbdev_ca, ca,
190696375b7aSJasmin Jessich 				  DVB_DEVICE_CA, 0);
19073d6c2bc0SMauro Carvalho Chehab 	if (ret)
1908a2bbf5d0SMarkus Elfring 		goto free_slot_info;
19093d6c2bc0SMauro Carvalho Chehab 
19103d6c2bc0SMauro Carvalho Chehab 	/* now initialise each slot */
19113d6c2bc0SMauro Carvalho Chehab 	for (i = 0; i < slot_count; i++) {
1912a75aa90cSJasmin Jessich 		struct dvb_ca_slot *sl = &ca->slot_info[i];
1913a75aa90cSJasmin Jessich 
1914a75aa90cSJasmin Jessich 		memset(sl, 0, sizeof(struct dvb_ca_slot));
1915a75aa90cSJasmin Jessich 		sl->slot_state = DVB_CA_SLOTSTATE_NONE;
1916a75aa90cSJasmin Jessich 		atomic_set(&sl->camchange_count, 0);
1917a75aa90cSJasmin Jessich 		sl->camchange_type = DVB_CA_EN50221_CAMCHANGE_REMOVED;
1918a75aa90cSJasmin Jessich 		mutex_init(&sl->slot_lock);
19193d6c2bc0SMauro Carvalho Chehab 	}
19203d6c2bc0SMauro Carvalho Chehab 
192130ad64b8SNikolaus Schulz 	mutex_init(&ca->ioctl_mutex);
1922*280a8ab8SHyunwoo Kim 	mutex_init(&ca->remove_mutex);
192330ad64b8SNikolaus Schulz 
19243d6c2bc0SMauro Carvalho Chehab 	if (signal_pending(current)) {
19253d6c2bc0SMauro Carvalho Chehab 		ret = -EINTR;
1926a2bbf5d0SMarkus Elfring 		goto unregister_device;
19273d6c2bc0SMauro Carvalho Chehab 	}
19283d6c2bc0SMauro Carvalho Chehab 	mb();
19293d6c2bc0SMauro Carvalho Chehab 
19303d6c2bc0SMauro Carvalho Chehab 	/* create a kthread for monitoring this CA device */
19313d6c2bc0SMauro Carvalho Chehab 	ca->thread = kthread_run(dvb_ca_en50221_thread, ca, "kdvb-ca-%i:%i",
19323d6c2bc0SMauro Carvalho Chehab 				 ca->dvbdev->adapter->num, ca->dvbdev->id);
19333d6c2bc0SMauro Carvalho Chehab 	if (IS_ERR(ca->thread)) {
19343d6c2bc0SMauro Carvalho Chehab 		ret = PTR_ERR(ca->thread);
1935b3ad24d2SMauro Carvalho Chehab 		pr_err("dvb_ca_init: failed to start kernel_thread (%d)\n",
19363d6c2bc0SMauro Carvalho Chehab 		       ret);
1937a2bbf5d0SMarkus Elfring 		goto unregister_device;
19383d6c2bc0SMauro Carvalho Chehab 	}
19393d6c2bc0SMauro Carvalho Chehab 	return 0;
19403d6c2bc0SMauro Carvalho Chehab 
1941a2bbf5d0SMarkus Elfring unregister_device:
19423d6c2bc0SMauro Carvalho Chehab 	dvb_unregister_device(ca->dvbdev);
1943a2bbf5d0SMarkus Elfring free_slot_info:
19443d6c2bc0SMauro Carvalho Chehab 	kfree(ca->slot_info);
1945a2bbf5d0SMarkus Elfring free_ca:
19463d6c2bc0SMauro Carvalho Chehab 	kfree(ca);
1947a2bbf5d0SMarkus Elfring exit:
19483d6c2bc0SMauro Carvalho Chehab 	pubca->private = NULL;
19493d6c2bc0SMauro Carvalho Chehab 	return ret;
19503d6c2bc0SMauro Carvalho Chehab }
195182ec19e4SJasmin Jessich EXPORT_SYMBOL(dvb_ca_en50221_init);
19523d6c2bc0SMauro Carvalho Chehab 
19533d6c2bc0SMauro Carvalho Chehab /**
1954a4184b4fSHans Verkuil  * dvb_ca_en50221_release - Release a DVB CA EN50221 interface device.
19553d6c2bc0SMauro Carvalho Chehab  *
195646e42a30SMauro Carvalho Chehab  * @pubca: The associated dvb_ca instance.
19573d6c2bc0SMauro Carvalho Chehab  */
dvb_ca_en50221_release(struct dvb_ca_en50221 * pubca)19583d6c2bc0SMauro Carvalho Chehab void dvb_ca_en50221_release(struct dvb_ca_en50221 *pubca)
19593d6c2bc0SMauro Carvalho Chehab {
19603d6c2bc0SMauro Carvalho Chehab 	struct dvb_ca_private *ca = pubca->private;
19613d6c2bc0SMauro Carvalho Chehab 	int i;
19623d6c2bc0SMauro Carvalho Chehab 
19633d6c2bc0SMauro Carvalho Chehab 	dprintk("%s\n", __func__);
19643d6c2bc0SMauro Carvalho Chehab 
1965*280a8ab8SHyunwoo Kim 	mutex_lock(&ca->remove_mutex);
1966*280a8ab8SHyunwoo Kim 	ca->exit = 1;
1967*280a8ab8SHyunwoo Kim 	mutex_unlock(&ca->remove_mutex);
1968*280a8ab8SHyunwoo Kim 
1969*280a8ab8SHyunwoo Kim 	if (ca->dvbdev->users < 1)
1970*280a8ab8SHyunwoo Kim 		wait_event(ca->dvbdev->wait_queue,
1971*280a8ab8SHyunwoo Kim 				ca->dvbdev->users == 1);
1972*280a8ab8SHyunwoo Kim 
19733d6c2bc0SMauro Carvalho Chehab 	/* shutdown the thread if there was one */
19743d6c2bc0SMauro Carvalho Chehab 	kthread_stop(ca->thread);
19753d6c2bc0SMauro Carvalho Chehab 
19767bd8cc8fSJasmin Jessich 	for (i = 0; i < ca->slot_count; i++)
19773d6c2bc0SMauro Carvalho Chehab 		dvb_ca_en50221_slot_shutdown(ca, i);
19787bd8cc8fSJasmin Jessich 
19794d5030b6SMax Kellermann 	dvb_remove_device(ca->dvbdev);
1980da677fe1SMax Kellermann 	dvb_ca_private_put(ca);
19813d6c2bc0SMauro Carvalho Chehab 	pubca->private = NULL;
19823d6c2bc0SMauro Carvalho Chehab }
198382ec19e4SJasmin Jessich EXPORT_SYMBOL(dvb_ca_en50221_release);
1984