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