2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* dvb_ca.c: generic DVB functions for EN50221 CAM interfaces
|
|
|
|
*
|
|
|
|
* Copyright (C) 2004 Andrew de Quincey
|
|
|
|
*
|
|
|
|
* Parts of this file were based on sources as follows:
|
|
|
|
*
|
|
|
|
* Copyright (C) 2003 Ralph Metzler <rjkm@metzlerbros.de>
|
|
|
|
*
|
|
|
|
* based on code:
|
|
|
|
*
|
|
|
|
* Copyright (C) 1999-2002 Ralph Metzler
|
|
|
|
* & Marcus Metzler for convergence integrated media GmbH
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
* as published by the Free Software Foundation; either version 2
|
|
|
|
* of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
2016-10-28 19:31:20 +08:00
|
|
|
* To obtain the license, point your browser to
|
|
|
|
* http://www.gnu.org/copyleft/gpl.html
|
2005-04-17 06:20:36 +08:00
|
|
|
*/
|
|
|
|
|
2016-10-13 17:47:54 +08:00
|
|
|
#define pr_fmt(fmt) "dvb_ca_en50221: " fmt
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <linux/errno.h>
|
|
|
|
#include <linux/slab.h>
|
|
|
|
#include <linux/list.h>
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/vmalloc.h>
|
|
|
|
#include <linux/delay.h>
|
2005-12-01 16:51:49 +08:00
|
|
|
#include <linux/spinlock.h>
|
2017-02-03 02:15:33 +08:00
|
|
|
#include <linux/sched/signal.h>
|
2007-10-03 22:23:01 +08:00
|
|
|
#include <linux/kthread.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
#include "dvb_ca_en50221.h"
|
|
|
|
#include "dvb_ringbuffer.h"
|
|
|
|
|
|
|
|
static int dvb_ca_en50221_debug;
|
|
|
|
|
|
|
|
module_param_named(cam_debug, dvb_ca_en50221_debug, int, 0644);
|
|
|
|
MODULE_PARM_DESC(cam_debug, "enable verbose debug messages");
|
|
|
|
|
2016-10-13 17:47:54 +08:00
|
|
|
#define dprintk(fmt, arg...) do { \
|
|
|
|
if (dvb_ca_en50221_debug) \
|
|
|
|
printk(KERN_DEBUG pr_fmt("%s: " fmt), __func__, ##arg);\
|
|
|
|
} while (0)
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2005-09-10 04:02:27 +08:00
|
|
|
#define INIT_TIMEOUT_SECS 10
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
#define HOST_LINK_BUF_SIZE 0x200
|
|
|
|
|
|
|
|
#define RX_BUFFER_SIZE 65535
|
|
|
|
|
|
|
|
#define MAX_RX_PACKETS_PER_ITERATION 10
|
|
|
|
|
|
|
|
#define CTRLIF_DATA 0
|
|
|
|
#define CTRLIF_COMMAND 1
|
|
|
|
#define CTRLIF_STATUS 1
|
|
|
|
#define CTRLIF_SIZE_LOW 2
|
|
|
|
#define CTRLIF_SIZE_HIGH 3
|
|
|
|
|
|
|
|
#define CMDREG_HC 1 /* Host control */
|
|
|
|
#define CMDREG_SW 2 /* Size write */
|
|
|
|
#define CMDREG_SR 4 /* Size read */
|
|
|
|
#define CMDREG_RS 8 /* Reset interface */
|
|
|
|
#define CMDREG_FRIE 0x40 /* Enable FR interrupt */
|
|
|
|
#define CMDREG_DAIE 0x80 /* Enable DA interrupt */
|
|
|
|
#define IRQEN (CMDREG_DAIE)
|
|
|
|
|
|
|
|
#define STATUSREG_RE 1 /* read error */
|
|
|
|
#define STATUSREG_WE 2 /* write error */
|
|
|
|
#define STATUSREG_FR 0x40 /* module free */
|
|
|
|
#define STATUSREG_DA 0x80 /* data available */
|
|
|
|
#define STATUSREG_TXERR (STATUSREG_RE|STATUSREG_WE) /* general transfer error */
|
|
|
|
|
|
|
|
|
|
|
|
#define DVB_CA_SLOTSTATE_NONE 0
|
|
|
|
#define DVB_CA_SLOTSTATE_UNINITIALISED 1
|
|
|
|
#define DVB_CA_SLOTSTATE_RUNNING 2
|
|
|
|
#define DVB_CA_SLOTSTATE_INVALID 3
|
|
|
|
#define DVB_CA_SLOTSTATE_WAITREADY 4
|
|
|
|
#define DVB_CA_SLOTSTATE_VALIDATE 5
|
|
|
|
#define DVB_CA_SLOTSTATE_WAITFR 6
|
|
|
|
#define DVB_CA_SLOTSTATE_LINKINIT 7
|
|
|
|
|
|
|
|
|
|
|
|
/* Information on a CA slot */
|
|
|
|
struct dvb_ca_slot {
|
|
|
|
|
|
|
|
/* current state of the CAM */
|
|
|
|
int slot_state;
|
|
|
|
|
2008-09-26 17:29:03 +08:00
|
|
|
/* mutex used for serializing access to one CI slot */
|
|
|
|
struct mutex slot_lock;
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/* Number of CAMCHANGES that have occurred since last processing */
|
|
|
|
atomic_t camchange_count;
|
|
|
|
|
|
|
|
/* Type of last CAMCHANGE */
|
|
|
|
int camchange_type;
|
|
|
|
|
|
|
|
/* base address of CAM config */
|
|
|
|
u32 config_base;
|
|
|
|
|
|
|
|
/* value to write into Config Control register */
|
|
|
|
u8 config_option;
|
|
|
|
|
|
|
|
/* if 1, the CAM supports DA IRQs */
|
|
|
|
u8 da_irq_supported:1;
|
|
|
|
|
|
|
|
/* size of the buffer to use when talking to the CAM */
|
|
|
|
int link_buf_size;
|
|
|
|
|
|
|
|
/* buffer for incoming packets */
|
|
|
|
struct dvb_ringbuffer rx_buffer;
|
|
|
|
|
|
|
|
/* timer used during various states of the slot */
|
|
|
|
unsigned long timeout;
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Private CA-interface information */
|
|
|
|
struct dvb_ca_private {
|
[media] dvb-core/en50221: use kref to manage struct dvb_ca_private
Don't free the object until the file handle has been closed. Fixes
use-after-free bug which occurs when I disconnect my DVB-S received
while VDR is running.
This is a crash dump of such a use-after-free:
general protection fault: 0000 [#1] SMP
CPU: 0 PID: 2541 Comm: CI adapter on d Not tainted 4.7.0-rc1-hosting+ #49
Hardware name: Bochs Bochs, BIOS Bochs 01/01/2011
task: ffff880027d7ce00 ti: ffff88003d8f8000 task.ti: ffff88003d8f8000
RIP: 0010:[<ffffffff812f3d1f>] [<ffffffff812f3d1f>] dvb_ca_en50221_io_read_condition.isra.7+0x6f/0x150
RSP: 0018:ffff88003d8fba98 EFLAGS: 00010206
RAX: 0000000059534255 RBX: 000000753d470f90 RCX: ffff88003c74d181
RDX: 00000001bea04ba9 RSI: ffff88003d8fbaf4 RDI: 3a3030a56d763fc0
RBP: ffff88003d8fbae0 R08: ffff88003c74d180 R09: 0000000000000000
R10: 0000000000000001 R11: 0000000000000000 R12: ffff88003c480e00
R13: 00000000ffffffff R14: 0000000059534255 R15: 0000000000000000
FS: 00007fb4209b4700(0000) GS:ffff88003fc00000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007f06445f4078 CR3: 000000003c55b000 CR4: 00000000000006b0
Stack:
ffff88003d8fbaf4 000000003c2170c0 0000000000004000 0000000000000000
ffff88003c480e00 ffff88003d8fbc80 ffff88003c74d180 ffff88003d8fbb8c
0000000000000000 ffff88003d8fbb10 ffffffff812f3e37 ffff88003d8fbb00
Call Trace:
[<ffffffff812f3e37>] dvb_ca_en50221_io_poll+0x37/0xa0
[<ffffffff8113109b>] do_sys_poll+0x2db/0x520
This is a backtrace of the kernel attempting to lock a freed mutex:
#0 0xffffffff81083d40 in rep_nop () at ./arch/x86/include/asm/processor.h:569
#1 cpu_relax () at ./arch/x86/include/asm/processor.h:574
#2 virt_spin_lock (lock=<optimized out>) at ./arch/x86/include/asm/qspinlock.h:57
#3 native_queued_spin_lock_slowpath (lock=0xffff88003c480e90, val=761492029) at kernel/locking/qspinlock.c:304
#4 0xffffffff810d1a06 in pv_queued_spin_lock_slowpath (val=<optimized out>, lock=<optimized out>) at ./arch/x86/include/asm/paravirt.h:669
#5 queued_spin_lock_slowpath (val=<optimized out>, lock=<optimized out>) at ./arch/x86/include/asm/qspinlock.h:28
#6 queued_spin_lock (lock=<optimized out>) at include/asm-generic/qspinlock.h:107
#7 __mutex_lock_common (use_ww_ctx=<optimized out>, ww_ctx=<optimized out>, ip=<optimized out>, nest_lock=<optimized out>, subclass=<optimized out>,
state=<optimized out>, lock=<optimized out>) at kernel/locking/mutex.c:526
#8 mutex_lock_interruptible_nested (lock=0xffff88003c480e88, subclass=<optimized out>) at kernel/locking/mutex.c:647
#9 0xffffffff812f49fe in dvb_ca_en50221_io_do_ioctl (file=<optimized out>, cmd=761492029, parg=0x1 <irq_stack_union+1>)
at drivers/media/dvb-core/dvb_ca_en50221.c:1210
#10 0xffffffff812ee660 in dvb_usercopy (file=<optimized out>, cmd=761492029, arg=<optimized out>, func=<optimized out>) at drivers/media/dvb-core/dvbdev.c:883
#11 0xffffffff812f3410 in dvb_ca_en50221_io_ioctl (file=<optimized out>, cmd=<optimized out>, arg=<optimized out>) at drivers/media/dvb-core/dvb_ca_en50221.c:1284
#12 0xffffffff8112eddd in vfs_ioctl (arg=<optimized out>, cmd=<optimized out>, filp=<optimized out>) at fs/ioctl.c:43
#13 do_vfs_ioctl (filp=0xffff88003c480e90, fd=<optimized out>, cmd=<optimized out>, arg=<optimized out>) at fs/ioctl.c:674
#14 0xffffffff8112f30c in SYSC_ioctl (arg=<optimized out>, cmd=<optimized out>, fd=<optimized out>) at fs/ioctl.c:689
#15 SyS_ioctl (fd=6, cmd=2148298626, arg=140734533693696) at fs/ioctl.c:680
#16 0xffffffff8103feb2 in entry_SYSCALL_64 () at arch/x86/entry/entry_64.S:207
Signed-off-by: Max Kellermann <max@duempel.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
2016-07-04 20:08:45 +08:00
|
|
|
struct kref refcount;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/* pointer back to the public data structure */
|
|
|
|
struct dvb_ca_en50221 *pub;
|
|
|
|
|
|
|
|
/* the DVB device */
|
|
|
|
struct dvb_device *dvbdev;
|
|
|
|
|
|
|
|
/* Flags describing the interface (DVB_CA_FLAG_*) */
|
|
|
|
u32 flags;
|
|
|
|
|
|
|
|
/* number of slots supported by this CA interface */
|
|
|
|
unsigned int slot_count;
|
|
|
|
|
|
|
|
/* information on each slot */
|
|
|
|
struct dvb_ca_slot *slot_info;
|
|
|
|
|
|
|
|
/* wait queues for read() and write() operations */
|
|
|
|
wait_queue_head_t wait_queue;
|
|
|
|
|
|
|
|
/* PID of the monitoring thread */
|
2007-10-03 22:23:01 +08:00
|
|
|
struct task_struct *thread;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/* Flag indicating if the CA device is open */
|
|
|
|
unsigned int open:1;
|
|
|
|
|
|
|
|
/* Flag indicating the thread should wake up now */
|
|
|
|
unsigned int wakeup:1;
|
|
|
|
|
|
|
|
/* Delay the main thread should use */
|
|
|
|
unsigned long delay;
|
|
|
|
|
|
|
|
/* Slot to start looking for data to read from in the next user-space read operation */
|
|
|
|
int next_read_slot;
|
2012-12-24 05:49:07 +08:00
|
|
|
|
|
|
|
/* mutex serializing ioctls */
|
|
|
|
struct mutex ioctl_mutex;
|
2005-04-17 06:20:36 +08:00
|
|
|
};
|
|
|
|
|
2016-03-21 21:30:17 +08:00
|
|
|
static void dvb_ca_private_free(struct dvb_ca_private *ca)
|
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
|
2016-08-10 05:32:16 +08:00
|
|
|
dvb_free_device(ca->dvbdev);
|
2016-03-21 21:30:17 +08:00
|
|
|
for (i = 0; i < ca->slot_count; i++)
|
|
|
|
vfree(ca->slot_info[i].rx_buffer.data);
|
|
|
|
|
|
|
|
kfree(ca->slot_info);
|
|
|
|
kfree(ca);
|
|
|
|
}
|
|
|
|
|
[media] dvb-core/en50221: use kref to manage struct dvb_ca_private
Don't free the object until the file handle has been closed. Fixes
use-after-free bug which occurs when I disconnect my DVB-S received
while VDR is running.
This is a crash dump of such a use-after-free:
general protection fault: 0000 [#1] SMP
CPU: 0 PID: 2541 Comm: CI adapter on d Not tainted 4.7.0-rc1-hosting+ #49
Hardware name: Bochs Bochs, BIOS Bochs 01/01/2011
task: ffff880027d7ce00 ti: ffff88003d8f8000 task.ti: ffff88003d8f8000
RIP: 0010:[<ffffffff812f3d1f>] [<ffffffff812f3d1f>] dvb_ca_en50221_io_read_condition.isra.7+0x6f/0x150
RSP: 0018:ffff88003d8fba98 EFLAGS: 00010206
RAX: 0000000059534255 RBX: 000000753d470f90 RCX: ffff88003c74d181
RDX: 00000001bea04ba9 RSI: ffff88003d8fbaf4 RDI: 3a3030a56d763fc0
RBP: ffff88003d8fbae0 R08: ffff88003c74d180 R09: 0000000000000000
R10: 0000000000000001 R11: 0000000000000000 R12: ffff88003c480e00
R13: 00000000ffffffff R14: 0000000059534255 R15: 0000000000000000
FS: 00007fb4209b4700(0000) GS:ffff88003fc00000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007f06445f4078 CR3: 000000003c55b000 CR4: 00000000000006b0
Stack:
ffff88003d8fbaf4 000000003c2170c0 0000000000004000 0000000000000000
ffff88003c480e00 ffff88003d8fbc80 ffff88003c74d180 ffff88003d8fbb8c
0000000000000000 ffff88003d8fbb10 ffffffff812f3e37 ffff88003d8fbb00
Call Trace:
[<ffffffff812f3e37>] dvb_ca_en50221_io_poll+0x37/0xa0
[<ffffffff8113109b>] do_sys_poll+0x2db/0x520
This is a backtrace of the kernel attempting to lock a freed mutex:
#0 0xffffffff81083d40 in rep_nop () at ./arch/x86/include/asm/processor.h:569
#1 cpu_relax () at ./arch/x86/include/asm/processor.h:574
#2 virt_spin_lock (lock=<optimized out>) at ./arch/x86/include/asm/qspinlock.h:57
#3 native_queued_spin_lock_slowpath (lock=0xffff88003c480e90, val=761492029) at kernel/locking/qspinlock.c:304
#4 0xffffffff810d1a06 in pv_queued_spin_lock_slowpath (val=<optimized out>, lock=<optimized out>) at ./arch/x86/include/asm/paravirt.h:669
#5 queued_spin_lock_slowpath (val=<optimized out>, lock=<optimized out>) at ./arch/x86/include/asm/qspinlock.h:28
#6 queued_spin_lock (lock=<optimized out>) at include/asm-generic/qspinlock.h:107
#7 __mutex_lock_common (use_ww_ctx=<optimized out>, ww_ctx=<optimized out>, ip=<optimized out>, nest_lock=<optimized out>, subclass=<optimized out>,
state=<optimized out>, lock=<optimized out>) at kernel/locking/mutex.c:526
#8 mutex_lock_interruptible_nested (lock=0xffff88003c480e88, subclass=<optimized out>) at kernel/locking/mutex.c:647
#9 0xffffffff812f49fe in dvb_ca_en50221_io_do_ioctl (file=<optimized out>, cmd=761492029, parg=0x1 <irq_stack_union+1>)
at drivers/media/dvb-core/dvb_ca_en50221.c:1210
#10 0xffffffff812ee660 in dvb_usercopy (file=<optimized out>, cmd=761492029, arg=<optimized out>, func=<optimized out>) at drivers/media/dvb-core/dvbdev.c:883
#11 0xffffffff812f3410 in dvb_ca_en50221_io_ioctl (file=<optimized out>, cmd=<optimized out>, arg=<optimized out>) at drivers/media/dvb-core/dvb_ca_en50221.c:1284
#12 0xffffffff8112eddd in vfs_ioctl (arg=<optimized out>, cmd=<optimized out>, filp=<optimized out>) at fs/ioctl.c:43
#13 do_vfs_ioctl (filp=0xffff88003c480e90, fd=<optimized out>, cmd=<optimized out>, arg=<optimized out>) at fs/ioctl.c:674
#14 0xffffffff8112f30c in SYSC_ioctl (arg=<optimized out>, cmd=<optimized out>, fd=<optimized out>) at fs/ioctl.c:689
#15 SyS_ioctl (fd=6, cmd=2148298626, arg=140734533693696) at fs/ioctl.c:680
#16 0xffffffff8103feb2 in entry_SYSCALL_64 () at arch/x86/entry/entry_64.S:207
Signed-off-by: Max Kellermann <max@duempel.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
2016-07-04 20:08:45 +08:00
|
|
|
static void dvb_ca_private_release(struct kref *ref)
|
|
|
|
{
|
|
|
|
struct dvb_ca_private *ca = container_of(ref, struct dvb_ca_private, refcount);
|
|
|
|
dvb_ca_private_free(ca);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void dvb_ca_private_get(struct dvb_ca_private *ca)
|
|
|
|
{
|
|
|
|
kref_get(&ca->refcount);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void dvb_ca_private_put(struct dvb_ca_private *ca)
|
|
|
|
{
|
|
|
|
kref_put(&ca->refcount, dvb_ca_private_release);
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
static void dvb_ca_en50221_thread_wakeup(struct dvb_ca_private *ca);
|
2017-05-08 05:23:28 +08:00
|
|
|
static int dvb_ca_en50221_read_data(struct dvb_ca_private *ca, int slot,
|
|
|
|
u8 *ebuf, int ecount);
|
|
|
|
static int dvb_ca_en50221_write_data(struct dvb_ca_private *ca, int slot,
|
|
|
|
u8 *ebuf, int ecount);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Safely find needle in haystack.
|
|
|
|
*
|
2015-08-22 18:09:29 +08:00
|
|
|
* @haystack: Buffer to look in.
|
|
|
|
* @hlen: Number of bytes in haystack.
|
|
|
|
* @needle: Buffer to find.
|
|
|
|
* @nlen: Number of bytes in needle.
|
2005-04-17 06:20:36 +08:00
|
|
|
* @return Pointer into haystack needle was found at, or NULL if not found.
|
|
|
|
*/
|
2017-05-08 05:23:28 +08:00
|
|
|
static char *findstr(char *haystack, int hlen, char *needle, int nlen)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (hlen < nlen)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
for (i = 0; i <= hlen - nlen; i++) {
|
|
|
|
if (!strncmp(haystack + i, needle, nlen))
|
|
|
|
return haystack + i;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* ******************************************************************************** */
|
|
|
|
/* EN50221 physical interface functions */
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2015-08-22 18:09:29 +08:00
|
|
|
* dvb_ca_en50221_check_camstatus - Check CAM status.
|
2005-04-17 06:20:36 +08:00
|
|
|
*/
|
|
|
|
static int dvb_ca_en50221_check_camstatus(struct dvb_ca_private *ca, int slot)
|
|
|
|
{
|
|
|
|
int slot_status;
|
|
|
|
int cam_present_now;
|
|
|
|
int cam_changed;
|
|
|
|
|
|
|
|
/* IRQ mode */
|
|
|
|
if (ca->flags & DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE) {
|
|
|
|
return (atomic_read(&ca->slot_info[slot].camchange_count) != 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* poll mode */
|
|
|
|
slot_status = ca->pub->poll_slot_status(ca->pub, slot, ca->open);
|
|
|
|
|
|
|
|
cam_present_now = (slot_status & DVB_CA_EN50221_POLL_CAM_PRESENT) ? 1 : 0;
|
|
|
|
cam_changed = (slot_status & DVB_CA_EN50221_POLL_CAM_CHANGED) ? 1 : 0;
|
|
|
|
if (!cam_changed) {
|
|
|
|
int cam_present_old = (ca->slot_info[slot].slot_state != DVB_CA_SLOTSTATE_NONE);
|
|
|
|
cam_changed = (cam_present_now != cam_present_old);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cam_changed) {
|
|
|
|
if (!cam_present_now) {
|
|
|
|
ca->slot_info[slot].camchange_type = DVB_CA_EN50221_CAMCHANGE_REMOVED;
|
|
|
|
} else {
|
|
|
|
ca->slot_info[slot].camchange_type = DVB_CA_EN50221_CAMCHANGE_INSERTED;
|
|
|
|
}
|
|
|
|
atomic_set(&ca->slot_info[slot].camchange_count, 1);
|
|
|
|
} else {
|
|
|
|
if ((ca->slot_info[slot].slot_state == DVB_CA_SLOTSTATE_WAITREADY) &&
|
|
|
|
(slot_status & DVB_CA_EN50221_POLL_CAM_READY)) {
|
|
|
|
// move to validate state if reset is completed
|
|
|
|
ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_VALIDATE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return cam_changed;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2015-08-22 18:09:29 +08:00
|
|
|
* dvb_ca_en50221_wait_if_status - Wait for flags to become set on the STATUS
|
|
|
|
* register on a CAM interface, checking for errors and timeout.
|
2005-04-17 06:20:36 +08:00
|
|
|
*
|
2015-08-22 18:09:29 +08:00
|
|
|
* @ca: CA instance.
|
|
|
|
* @slot: Slot on interface.
|
|
|
|
* @waitfor: Flags to wait for.
|
|
|
|
* @timeout_ms: Timeout in milliseconds.
|
2005-04-17 06:20:36 +08:00
|
|
|
*
|
|
|
|
* @return 0 on success, nonzero on error.
|
|
|
|
*/
|
|
|
|
static int dvb_ca_en50221_wait_if_status(struct dvb_ca_private *ca, int slot,
|
|
|
|
u8 waitfor, int timeout_hz)
|
|
|
|
{
|
|
|
|
unsigned long timeout;
|
|
|
|
unsigned long start;
|
|
|
|
|
2008-04-09 10:20:00 +08:00
|
|
|
dprintk("%s\n", __func__);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/* loop until timeout elapsed */
|
|
|
|
start = jiffies;
|
|
|
|
timeout = jiffies + timeout_hz;
|
|
|
|
while (1) {
|
|
|
|
/* read the status and check for error */
|
|
|
|
int res = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS);
|
|
|
|
if (res < 0)
|
|
|
|
return -EIO;
|
|
|
|
|
|
|
|
/* if we got the flags, it was successful! */
|
|
|
|
if (res & waitfor) {
|
2016-10-13 17:47:54 +08:00
|
|
|
dprintk("%s succeeded timeout:%lu\n",
|
|
|
|
__func__, jiffies - start);
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* check for timeout */
|
|
|
|
if (time_after(jiffies, timeout)) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* wait for a bit */
|
2017-07-16 08:43:04 +08:00
|
|
|
usleep_range(1000, 1100);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2008-04-09 10:20:00 +08:00
|
|
|
dprintk("%s failed timeout:%lu\n", __func__, jiffies - start);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/* if we get here, we've timed out */
|
|
|
|
return -ETIMEDOUT;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2015-08-22 18:09:29 +08:00
|
|
|
* dvb_ca_en50221_link_init - Initialise the link layer connection to a CAM.
|
2005-04-17 06:20:36 +08:00
|
|
|
*
|
2015-08-22 18:09:29 +08:00
|
|
|
* @ca: CA instance.
|
|
|
|
* @slot: Slot id.
|
2005-04-17 06:20:36 +08:00
|
|
|
*
|
|
|
|
* @return 0 on success, nonzero on failure.
|
|
|
|
*/
|
|
|
|
static int dvb_ca_en50221_link_init(struct dvb_ca_private *ca, int slot)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
int buf_size;
|
|
|
|
u8 buf[2];
|
|
|
|
|
2008-04-09 10:20:00 +08:00
|
|
|
dprintk("%s\n", __func__);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/* we'll be determining these during this function */
|
|
|
|
ca->slot_info[slot].da_irq_supported = 0;
|
|
|
|
|
2017-07-16 08:43:05 +08:00
|
|
|
/*
|
|
|
|
* set the host link buffer size temporarily. it will be overwritten
|
|
|
|
* with the real negotiated size later.
|
|
|
|
*/
|
2005-04-17 06:20:36 +08:00
|
|
|
ca->slot_info[slot].link_buf_size = 2;
|
|
|
|
|
|
|
|
/* read the buffer size from the CAM */
|
2017-07-16 08:43:06 +08:00
|
|
|
ret = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND,
|
|
|
|
IRQEN | CMDREG_SR);
|
|
|
|
if (ret)
|
2005-04-17 06:20:36 +08:00
|
|
|
return ret;
|
2017-06-26 05:37:06 +08:00
|
|
|
ret = dvb_ca_en50221_wait_if_status(ca, slot, STATUSREG_DA, HZ);
|
2017-07-16 08:43:06 +08:00
|
|
|
if (ret)
|
2005-04-17 06:20:36 +08:00
|
|
|
return ret;
|
2017-07-16 08:43:06 +08:00
|
|
|
ret = dvb_ca_en50221_read_data(ca, slot, buf, 2);
|
|
|
|
if (ret != 2)
|
2005-04-17 06:20:36 +08:00
|
|
|
return -EIO;
|
2017-07-16 08:43:06 +08:00
|
|
|
ret = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, IRQEN);
|
|
|
|
if (ret)
|
2005-04-17 06:20:36 +08:00
|
|
|
return ret;
|
|
|
|
|
|
|
|
/* store it, and choose the minimum of our buffer and the CAM's buffer size */
|
|
|
|
buf_size = (buf[0] << 8) | buf[1];
|
|
|
|
if (buf_size > HOST_LINK_BUF_SIZE)
|
|
|
|
buf_size = HOST_LINK_BUF_SIZE;
|
|
|
|
ca->slot_info[slot].link_buf_size = buf_size;
|
|
|
|
buf[0] = buf_size >> 8;
|
|
|
|
buf[1] = buf_size & 0xff;
|
|
|
|
dprintk("Chosen link buffer size of %i\n", buf_size);
|
|
|
|
|
|
|
|
/* write the buffer size to the CAM */
|
2017-07-16 08:43:06 +08:00
|
|
|
ret = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND,
|
|
|
|
IRQEN | CMDREG_SW);
|
|
|
|
if (ret)
|
2005-04-17 06:20:36 +08:00
|
|
|
return ret;
|
2017-07-16 08:43:06 +08:00
|
|
|
ret = dvb_ca_en50221_wait_if_status(ca, slot, STATUSREG_FR, HZ / 10);
|
|
|
|
if (ret)
|
2005-04-17 06:20:36 +08:00
|
|
|
return ret;
|
2017-07-16 08:43:06 +08:00
|
|
|
ret = dvb_ca_en50221_write_data(ca, slot, buf, 2);
|
|
|
|
if (ret != 2)
|
2005-04-17 06:20:36 +08:00
|
|
|
return -EIO;
|
2017-07-16 08:43:06 +08:00
|
|
|
ret = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, IRQEN);
|
|
|
|
if (ret)
|
2005-04-17 06:20:36 +08:00
|
|
|
return ret;
|
|
|
|
|
|
|
|
/* success */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2015-08-22 18:09:29 +08:00
|
|
|
* dvb_ca_en50221_read_tuple - Read a tuple from attribute memory.
|
2005-04-17 06:20:36 +08:00
|
|
|
*
|
2015-08-22 18:09:29 +08:00
|
|
|
* @ca: CA instance.
|
|
|
|
* @slot: Slot id.
|
|
|
|
* @address: Address to read from. Updated.
|
|
|
|
* @tupleType: Tuple id byte. Updated.
|
|
|
|
* @tupleLength: Tuple length. Updated.
|
|
|
|
* @tuple: Dest buffer for tuple (must be 256 bytes). Updated.
|
2005-04-17 06:20:36 +08:00
|
|
|
*
|
|
|
|
* @return 0 on success, nonzero on error.
|
|
|
|
*/
|
|
|
|
static int dvb_ca_en50221_read_tuple(struct dvb_ca_private *ca, int slot,
|
2017-07-16 08:43:06 +08:00
|
|
|
int *address, int *tuple_type,
|
|
|
|
int *tuple_length, u8 *tuple)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
int i;
|
2017-07-16 08:43:06 +08:00
|
|
|
int _tuple_type;
|
|
|
|
int _tuple_length;
|
2005-04-17 06:20:36 +08:00
|
|
|
int _address = *address;
|
|
|
|
|
|
|
|
/* grab the next tuple length and type */
|
2017-07-16 08:43:06 +08:00
|
|
|
_tuple_type = ca->pub->read_attribute_mem(ca->pub, slot, _address);
|
|
|
|
if (_tuple_type < 0)
|
|
|
|
return _tuple_type;
|
|
|
|
if (_tuple_type == 0xff) {
|
|
|
|
dprintk("END OF CHAIN TUPLE type:0x%x\n", _tuple_type);
|
2005-04-17 06:20:36 +08:00
|
|
|
*address += 2;
|
2017-07-16 08:43:06 +08:00
|
|
|
*tuple_type = _tuple_type;
|
|
|
|
*tuple_length = 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
}
|
2017-07-16 08:43:06 +08:00
|
|
|
_tuple_length = ca->pub->read_attribute_mem(ca->pub, slot,
|
|
|
|
_address + 2);
|
|
|
|
if (_tuple_length < 0)
|
|
|
|
return _tuple_length;
|
2005-04-17 06:20:36 +08:00
|
|
|
_address += 4;
|
|
|
|
|
2017-07-16 08:43:06 +08:00
|
|
|
dprintk("TUPLE type:0x%x length:%i\n", _tuple_type, _tuple_length);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/* read in the whole tuple */
|
2017-07-16 08:43:06 +08:00
|
|
|
for (i = 0; i < _tuple_length; i++) {
|
2005-04-17 06:20:36 +08:00
|
|
|
tuple[i] = ca->pub->read_attribute_mem(ca->pub, slot, _address + (i * 2));
|
|
|
|
dprintk(" 0x%02x: 0x%02x %c\n",
|
|
|
|
i, tuple[i] & 0xff,
|
|
|
|
((tuple[i] > 31) && (tuple[i] < 127)) ? tuple[i] : '.');
|
|
|
|
}
|
2017-07-16 08:43:06 +08:00
|
|
|
_address += (_tuple_length * 2);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
// success
|
2017-07-16 08:43:06 +08:00
|
|
|
*tuple_type = _tuple_type;
|
|
|
|
*tuple_length = _tuple_length;
|
2005-04-17 06:20:36 +08:00
|
|
|
*address = _address;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2015-08-22 18:09:29 +08:00
|
|
|
* dvb_ca_en50221_parse_attributes - Parse attribute memory of a CAM module,
|
|
|
|
* extracting Config register, and checking it is a DVB CAM module.
|
2005-04-17 06:20:36 +08:00
|
|
|
*
|
2015-08-22 18:09:29 +08:00
|
|
|
* @ca: CA instance.
|
|
|
|
* @slot: Slot id.
|
2005-04-17 06:20:36 +08:00
|
|
|
*
|
|
|
|
* @return 0 on success, <0 on failure.
|
|
|
|
*/
|
|
|
|
static int dvb_ca_en50221_parse_attributes(struct dvb_ca_private *ca, int slot)
|
|
|
|
{
|
|
|
|
int address = 0;
|
2017-07-16 08:43:06 +08:00
|
|
|
int tuple_length;
|
|
|
|
int tuple_type;
|
2005-04-17 06:20:36 +08:00
|
|
|
u8 tuple[257];
|
|
|
|
char *dvb_str;
|
|
|
|
int rasz;
|
|
|
|
int status;
|
|
|
|
int got_cftableentry = 0;
|
|
|
|
int end_chain = 0;
|
|
|
|
int i;
|
|
|
|
u16 manfid = 0;
|
|
|
|
u16 devid = 0;
|
|
|
|
|
|
|
|
|
|
|
|
// CISTPL_DEVICE_0A
|
2017-07-16 08:43:06 +08:00
|
|
|
status = dvb_ca_en50221_read_tuple(ca, slot, &address, &tuple_type,
|
|
|
|
&tuple_length, tuple);
|
|
|
|
if (status < 0)
|
2005-04-17 06:20:36 +08:00
|
|
|
return status;
|
2017-07-16 08:43:06 +08:00
|
|
|
if (tuple_type != 0x1D)
|
2005-04-17 06:20:36 +08:00
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// CISTPL_DEVICE_0C
|
2017-07-16 08:43:06 +08:00
|
|
|
status = dvb_ca_en50221_read_tuple(ca, slot, &address, &tuple_type,
|
|
|
|
&tuple_length, tuple);
|
|
|
|
if (status < 0)
|
2005-04-17 06:20:36 +08:00
|
|
|
return status;
|
2017-07-16 08:43:06 +08:00
|
|
|
if (tuple_type != 0x1C)
|
2005-04-17 06:20:36 +08:00
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// CISTPL_VERS_1
|
2017-07-16 08:43:06 +08:00
|
|
|
status = dvb_ca_en50221_read_tuple(ca, slot, &address, &tuple_type,
|
|
|
|
&tuple_length, tuple);
|
|
|
|
if (status < 0)
|
2005-04-17 06:20:36 +08:00
|
|
|
return status;
|
2017-07-16 08:43:06 +08:00
|
|
|
if (tuple_type != 0x15)
|
2005-04-17 06:20:36 +08:00
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// CISTPL_MANFID
|
2017-07-16 08:43:06 +08:00
|
|
|
status = dvb_ca_en50221_read_tuple(ca, slot, &address, &tuple_type,
|
|
|
|
&tuple_length, tuple);
|
|
|
|
if (status < 0)
|
2005-04-17 06:20:36 +08:00
|
|
|
return status;
|
2017-07-16 08:43:06 +08:00
|
|
|
if (tuple_type != 0x20)
|
2005-04-17 06:20:36 +08:00
|
|
|
return -EINVAL;
|
2017-07-16 08:43:06 +08:00
|
|
|
if (tuple_length != 4)
|
2005-04-17 06:20:36 +08:00
|
|
|
return -EINVAL;
|
|
|
|
manfid = (tuple[1] << 8) | tuple[0];
|
|
|
|
devid = (tuple[3] << 8) | tuple[2];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// CISTPL_CONFIG
|
2017-07-16 08:43:06 +08:00
|
|
|
status = dvb_ca_en50221_read_tuple(ca, slot, &address, &tuple_type,
|
|
|
|
&tuple_length, tuple);
|
|
|
|
if (status < 0)
|
2005-04-17 06:20:36 +08:00
|
|
|
return status;
|
2017-07-16 08:43:06 +08:00
|
|
|
if (tuple_type != 0x1A)
|
2005-04-17 06:20:36 +08:00
|
|
|
return -EINVAL;
|
2017-07-16 08:43:06 +08:00
|
|
|
if (tuple_length < 3)
|
2005-04-17 06:20:36 +08:00
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
/* extract the configbase */
|
|
|
|
rasz = tuple[0] & 3;
|
2017-07-16 08:43:06 +08:00
|
|
|
if (tuple_length < (3 + rasz + 14))
|
2005-04-17 06:20:36 +08:00
|
|
|
return -EINVAL;
|
|
|
|
ca->slot_info[slot].config_base = 0;
|
|
|
|
for (i = 0; i < rasz + 1; i++) {
|
|
|
|
ca->slot_info[slot].config_base |= (tuple[2 + i] << (8 * i));
|
|
|
|
}
|
|
|
|
|
|
|
|
/* check it contains the correct DVB string */
|
2017-07-16 08:43:06 +08:00
|
|
|
dvb_str = findstr((char *)tuple, tuple_length, "DVB_CI_V", 8);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (dvb_str == NULL)
|
|
|
|
return -EINVAL;
|
2017-07-16 08:43:06 +08:00
|
|
|
if (tuple_length < ((dvb_str - (char *)tuple) + 12))
|
2005-04-17 06:20:36 +08:00
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
/* is it a version we support? */
|
|
|
|
if (strncmp(dvb_str + 8, "1.00", 4)) {
|
2016-10-13 17:47:54 +08:00
|
|
|
pr_err("dvb_ca adapter %d: Unsupported DVB CAM module version %c%c%c%c\n",
|
|
|
|
ca->dvbdev->adapter->num, dvb_str[8], dvb_str[9],
|
|
|
|
dvb_str[10], dvb_str[11]);
|
2005-04-17 06:20:36 +08:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* process the CFTABLE_ENTRY tuples, and any after those */
|
|
|
|
while ((!end_chain) && (address < 0x1000)) {
|
2017-07-16 08:43:06 +08:00
|
|
|
status = dvb_ca_en50221_read_tuple(ca, slot, &address,
|
|
|
|
&tuple_type, &tuple_length,
|
|
|
|
tuple);
|
|
|
|
if (status < 0)
|
2005-04-17 06:20:36 +08:00
|
|
|
return status;
|
2017-07-16 08:43:06 +08:00
|
|
|
switch (tuple_type) {
|
2005-04-17 06:20:36 +08:00
|
|
|
case 0x1B: // CISTPL_CFTABLE_ENTRY
|
2017-07-16 08:43:06 +08:00
|
|
|
if (tuple_length < (2 + 11 + 17))
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
/* if we've already parsed one, just use it */
|
|
|
|
if (got_cftableentry)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* get the config option */
|
|
|
|
ca->slot_info[slot].config_option = tuple[0] & 0x3f;
|
|
|
|
|
|
|
|
/* OK, check it contains the correct strings */
|
2017-07-16 08:43:06 +08:00
|
|
|
if (!findstr((char *)tuple, tuple_length,
|
|
|
|
"DVB_HOST", 8) ||
|
|
|
|
!findstr((char *)tuple, tuple_length,
|
|
|
|
"DVB_CI_MODULE", 13))
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
got_cftableentry = 1;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x14: // CISTPL_NO_LINK
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0xFF: // CISTPL_END
|
|
|
|
end_chain = 1;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default: /* Unknown tuple type - just skip this tuple and move to the next one */
|
2016-10-13 17:47:54 +08:00
|
|
|
dprintk("dvb_ca: Skipping unknown tuple type:0x%x length:0x%x\n",
|
2017-07-16 08:43:06 +08:00
|
|
|
tuple_type, tuple_length);
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((address > 0x1000) || (!got_cftableentry))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
dprintk("Valid DVB CAM detected MANID:%x DEVID:%x CONFIGBASE:0x%x CONFIGOPTION:0x%x\n",
|
2016-10-13 17:47:54 +08:00
|
|
|
manfid, devid, ca->slot_info[slot].config_base,
|
|
|
|
ca->slot_info[slot].config_option);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
// success!
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2015-08-22 18:09:29 +08:00
|
|
|
* dvb_ca_en50221_set_configoption - Set CAM's configoption correctly.
|
2005-04-17 06:20:36 +08:00
|
|
|
*
|
2015-08-22 18:09:29 +08:00
|
|
|
* @ca: CA instance.
|
|
|
|
* @slot: Slot containing the CAM.
|
2005-04-17 06:20:36 +08:00
|
|
|
*/
|
|
|
|
static int dvb_ca_en50221_set_configoption(struct dvb_ca_private *ca, int slot)
|
|
|
|
{
|
|
|
|
int configoption;
|
|
|
|
|
2008-04-09 10:20:00 +08:00
|
|
|
dprintk("%s\n", __func__);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/* set the config option */
|
|
|
|
ca->pub->write_attribute_mem(ca->pub, slot,
|
|
|
|
ca->slot_info[slot].config_base,
|
|
|
|
ca->slot_info[slot].config_option);
|
|
|
|
|
|
|
|
/* check it */
|
|
|
|
configoption = ca->pub->read_attribute_mem(ca->pub, slot, ca->slot_info[slot].config_base);
|
|
|
|
dprintk("Set configoption 0x%x, read configoption 0x%x\n",
|
|
|
|
ca->slot_info[slot].config_option, configoption & 0x3f);
|
|
|
|
|
|
|
|
/* fine! */
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2015-08-22 18:09:29 +08:00
|
|
|
* dvb_ca_en50221_read_data - This function talks to an EN50221 CAM control
|
|
|
|
* interface. It reads a buffer of data from the CAM. The data can either
|
|
|
|
* be stored in a supplied buffer, or automatically be added to the slot's
|
|
|
|
* rx_buffer.
|
2005-04-17 06:20:36 +08:00
|
|
|
*
|
2015-08-22 18:09:29 +08:00
|
|
|
* @ca: CA instance.
|
|
|
|
* @slot: Slot to read from.
|
|
|
|
* @ebuf: If non-NULL, the data will be written to this buffer. If NULL,
|
2005-04-17 06:20:36 +08:00
|
|
|
* the data will be added into the buffering system as a normal fragment.
|
2015-08-22 18:09:29 +08:00
|
|
|
* @ecount: Size of ebuf. Ignored if ebuf is NULL.
|
2005-04-17 06:20:36 +08:00
|
|
|
*
|
|
|
|
* @return Number of bytes read, or < 0 on error
|
|
|
|
*/
|
2017-05-08 05:23:28 +08:00
|
|
|
static int dvb_ca_en50221_read_data(struct dvb_ca_private *ca, int slot,
|
|
|
|
u8 *ebuf, int ecount)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
int bytes_read;
|
|
|
|
int status;
|
|
|
|
u8 buf[HOST_LINK_BUF_SIZE];
|
|
|
|
int i;
|
|
|
|
|
2008-04-09 10:20:00 +08:00
|
|
|
dprintk("%s\n", __func__);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/* check if we have space for a link buf in the rx_buffer */
|
|
|
|
if (ebuf == NULL) {
|
|
|
|
int buf_free;
|
|
|
|
|
|
|
|
if (ca->slot_info[slot].rx_buffer.data == NULL) {
|
|
|
|
status = -EIO;
|
|
|
|
goto exit;
|
|
|
|
}
|
|
|
|
buf_free = dvb_ringbuffer_free(&ca->slot_info[slot].rx_buffer);
|
|
|
|
|
2017-06-26 05:37:07 +08:00
|
|
|
if (buf_free < (ca->slot_info[slot].link_buf_size +
|
|
|
|
DVB_RINGBUFFER_PKTHDRSIZE)) {
|
2005-04-17 06:20:36 +08:00
|
|
|
status = -EAGAIN;
|
|
|
|
goto exit;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-26 05:37:07 +08:00
|
|
|
if (ca->pub->read_data &&
|
|
|
|
(ca->slot_info[slot].slot_state != DVB_CA_SLOTSTATE_LINKINIT)) {
|
|
|
|
if (ebuf == NULL)
|
|
|
|
status = ca->pub->read_data(ca->pub, slot, buf,
|
|
|
|
sizeof(buf));
|
|
|
|
else
|
|
|
|
status = ca->pub->read_data(ca->pub, slot, buf, ecount);
|
|
|
|
if (status < 0)
|
|
|
|
return status;
|
|
|
|
bytes_read = status;
|
|
|
|
if (status == 0)
|
|
|
|
goto exit;
|
|
|
|
} else {
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2017-06-26 05:37:07 +08:00
|
|
|
/* check if there is data available */
|
|
|
|
status = ca->pub->read_cam_control(ca->pub, slot,
|
|
|
|
CTRLIF_STATUS);
|
|
|
|
if (status < 0)
|
2005-04-17 06:20:36 +08:00
|
|
|
goto exit;
|
2017-06-26 05:37:07 +08:00
|
|
|
if (!(status & STATUSREG_DA)) {
|
|
|
|
/* no data */
|
|
|
|
status = 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
goto exit;
|
|
|
|
}
|
2017-06-26 05:37:07 +08:00
|
|
|
|
|
|
|
/* read the amount of data */
|
|
|
|
status = ca->pub->read_cam_control(ca->pub, slot,
|
|
|
|
CTRLIF_SIZE_HIGH);
|
|
|
|
if (status < 0)
|
|
|
|
goto exit;
|
|
|
|
bytes_read = status << 8;
|
|
|
|
status = ca->pub->read_cam_control(ca->pub, slot,
|
|
|
|
CTRLIF_SIZE_LOW);
|
|
|
|
if (status < 0)
|
2005-04-17 06:20:36 +08:00
|
|
|
goto exit;
|
2017-06-26 05:37:07 +08:00
|
|
|
bytes_read |= status;
|
|
|
|
|
|
|
|
/* check it will fit */
|
|
|
|
if (ebuf == NULL) {
|
|
|
|
if (bytes_read > ca->slot_info[slot].link_buf_size) {
|
|
|
|
pr_err("dvb_ca adapter %d: CAM tried to send a buffer larger than the link buffer size (%i > %i)!\n",
|
|
|
|
ca->dvbdev->adapter->num, bytes_read,
|
|
|
|
ca->slot_info[slot].link_buf_size);
|
|
|
|
ca->slot_info[slot].slot_state =
|
|
|
|
DVB_CA_SLOTSTATE_LINKINIT;
|
|
|
|
status = -EIO;
|
|
|
|
goto exit;
|
|
|
|
}
|
|
|
|
if (bytes_read < 2) {
|
|
|
|
pr_err("dvb_ca adapter %d: CAM sent a buffer that was less than 2 bytes!\n",
|
|
|
|
ca->dvbdev->adapter->num);
|
|
|
|
ca->slot_info[slot].slot_state =
|
|
|
|
DVB_CA_SLOTSTATE_LINKINIT;
|
|
|
|
status = -EIO;
|
|
|
|
goto exit;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (bytes_read > ecount) {
|
|
|
|
pr_err("dvb_ca adapter %d: CAM tried to send a buffer larger than the ecount size!\n",
|
|
|
|
ca->dvbdev->adapter->num);
|
|
|
|
status = -EIO;
|
|
|
|
goto exit;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2017-06-26 05:37:07 +08:00
|
|
|
/* fill the buffer */
|
|
|
|
for (i = 0; i < bytes_read; i++) {
|
|
|
|
/* read byte and check */
|
|
|
|
status = ca->pub->read_cam_control(ca->pub, slot,
|
|
|
|
CTRLIF_DATA);
|
|
|
|
if (status < 0)
|
|
|
|
goto exit;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2017-06-26 05:37:07 +08:00
|
|
|
/* OK, store it in the buffer */
|
|
|
|
buf[i] = status;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2017-06-26 05:37:07 +08:00
|
|
|
/* check for read error (RE should now be 0) */
|
|
|
|
status = ca->pub->read_cam_control(ca->pub, slot,
|
|
|
|
CTRLIF_STATUS);
|
|
|
|
if (status < 0)
|
|
|
|
goto exit;
|
|
|
|
if (status & STATUSREG_RE) {
|
|
|
|
ca->slot_info[slot].slot_state =
|
|
|
|
DVB_CA_SLOTSTATE_LINKINIT;
|
|
|
|
status = -EIO;
|
|
|
|
goto exit;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* OK, add it to the receive buffer, or copy into external buffer if supplied */
|
|
|
|
if (ebuf == NULL) {
|
|
|
|
if (ca->slot_info[slot].rx_buffer.data == NULL) {
|
|
|
|
status = -EIO;
|
|
|
|
goto exit;
|
|
|
|
}
|
|
|
|
dvb_ringbuffer_pkt_write(&ca->slot_info[slot].rx_buffer, buf, bytes_read);
|
|
|
|
} else {
|
|
|
|
memcpy(ebuf, buf, bytes_read);
|
|
|
|
}
|
|
|
|
|
|
|
|
dprintk("Received CA packet for slot %i connection id 0x%x last_frag:%i size:0x%x\n", slot,
|
|
|
|
buf[0], (buf[1] & 0x80) == 0, bytes_read);
|
|
|
|
|
|
|
|
/* wake up readers when a last_fragment is received */
|
|
|
|
if ((buf[1] & 0x80) == 0x00) {
|
|
|
|
wake_up_interruptible(&ca->wait_queue);
|
|
|
|
}
|
|
|
|
status = bytes_read;
|
|
|
|
|
|
|
|
exit:
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2015-08-22 18:09:29 +08:00
|
|
|
* dvb_ca_en50221_write_data - This function talks to an EN50221 CAM control
|
|
|
|
* interface. It writes a buffer of data to a CAM.
|
2005-04-17 06:20:36 +08:00
|
|
|
*
|
2015-08-22 18:09:29 +08:00
|
|
|
* @ca: CA instance.
|
|
|
|
* @slot: Slot to write to.
|
|
|
|
* @ebuf: The data in this buffer is treated as a complete link-level packet to
|
2005-04-17 06:20:36 +08:00
|
|
|
* be written.
|
2015-08-22 18:09:29 +08:00
|
|
|
* @count: Size of ebuf.
|
2005-04-17 06:20:36 +08:00
|
|
|
*
|
|
|
|
* @return Number of bytes written, or < 0 on error.
|
|
|
|
*/
|
2017-05-08 05:23:28 +08:00
|
|
|
static int dvb_ca_en50221_write_data(struct dvb_ca_private *ca, int slot,
|
|
|
|
u8 *buf, int bytes_write)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
int status;
|
|
|
|
int i;
|
|
|
|
|
2008-04-09 10:20:00 +08:00
|
|
|
dprintk("%s\n", __func__);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
|
2008-09-26 17:29:03 +08:00
|
|
|
/* sanity check */
|
2005-04-17 06:20:36 +08:00
|
|
|
if (bytes_write > ca->slot_info[slot].link_buf_size)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2017-06-26 05:37:07 +08:00
|
|
|
if (ca->pub->write_data &&
|
|
|
|
(ca->slot_info[slot].slot_state != DVB_CA_SLOTSTATE_LINKINIT))
|
|
|
|
return ca->pub->write_data(ca->pub, slot, buf, bytes_write);
|
|
|
|
|
2017-07-16 08:43:05 +08:00
|
|
|
/*
|
|
|
|
* it is possible we are dealing with a single buffer implementation,
|
|
|
|
* thus if there is data available for read or if there is even a read
|
|
|
|
* already in progress, we do nothing but awake the kernel thread to
|
|
|
|
* process the data if necessary.
|
|
|
|
*/
|
2017-07-16 08:43:06 +08:00
|
|
|
status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS);
|
|
|
|
if (status < 0)
|
2005-04-17 06:20:36 +08:00
|
|
|
goto exitnowrite;
|
|
|
|
if (status & (STATUSREG_DA | STATUSREG_RE)) {
|
2008-09-26 17:29:03 +08:00
|
|
|
if (status & STATUSREG_DA)
|
|
|
|
dvb_ca_en50221_thread_wakeup(ca);
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
status = -EAGAIN;
|
|
|
|
goto exitnowrite;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* OK, set HC bit */
|
2017-07-16 08:43:06 +08:00
|
|
|
status = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND,
|
|
|
|
IRQEN | CMDREG_HC);
|
|
|
|
if (status)
|
2005-04-17 06:20:36 +08:00
|
|
|
goto exit;
|
|
|
|
|
|
|
|
/* check if interface is still free */
|
2017-07-16 08:43:06 +08:00
|
|
|
status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS);
|
|
|
|
if (status < 0)
|
2005-04-17 06:20:36 +08:00
|
|
|
goto exit;
|
|
|
|
if (!(status & STATUSREG_FR)) {
|
|
|
|
/* it wasn't free => try again later */
|
|
|
|
status = -EAGAIN;
|
|
|
|
goto exit;
|
|
|
|
}
|
|
|
|
|
[media] media/dvb-core: Race condition when writing to CAM
It started with a sporadic message in syslog: "CAM tried to send a
buffer larger than the ecount size" This message is not the fault
itself, but a consecutive fault, after a read error from the CAM. This
happens only on several CAMs, several hardware, and of course sporadic.
It is a consecutive fault, if the last read from the CAM did fail. I
guess this will not happen on all CAMs, but at least it did on mine.
There was a write error to the CAM and during the re-initialization
procedure, the CAM finished the last read, although it got a RS.
The write error to the CAM happened because a race condition between HC
write, checking DA and FR.
This patch added an additional check for DA(RE), just after checking FR.
It is important to read the CAMs status register again, to give the CAM
the necessary time for a proper reaction to HC. Please note the
description within the source code (patch below).
[mchehab@s-opensource.com: make checkpatch happy]
Signed-off-by: Jasmin jessich <jasmin@anw.at>
Tested-by: Ralph Metzler <rjkm@metzlerbros.de>
Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
2017-03-18 10:04:20 +08:00
|
|
|
/*
|
|
|
|
* It may need some time for the CAM to settle down, or there might
|
|
|
|
* be a race condition between the CAM, writing HC and our last
|
|
|
|
* check for DA. This happens, if the CAM asserts DA, just after
|
|
|
|
* checking DA before we are setting HC. In this case it might be
|
|
|
|
* a bug in the CAM to keep the FR bit, the lower layer/HW
|
|
|
|
* communication requires a longer timeout or the CAM needs more
|
|
|
|
* time internally. But this happens in reality!
|
|
|
|
* We need to read the status from the HW again and do the same
|
|
|
|
* we did for the previous check for DA
|
|
|
|
*/
|
|
|
|
status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS);
|
|
|
|
if (status < 0)
|
|
|
|
goto exit;
|
|
|
|
|
|
|
|
if (status & (STATUSREG_DA | STATUSREG_RE)) {
|
|
|
|
if (status & STATUSREG_DA)
|
|
|
|
dvb_ca_en50221_thread_wakeup(ca);
|
|
|
|
|
|
|
|
status = -EAGAIN;
|
|
|
|
goto exit;
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/* send the amount of data */
|
2017-07-16 08:43:06 +08:00
|
|
|
status = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_SIZE_HIGH,
|
|
|
|
bytes_write >> 8);
|
|
|
|
if (status)
|
2005-04-17 06:20:36 +08:00
|
|
|
goto exit;
|
2017-07-16 08:43:06 +08:00
|
|
|
status = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_SIZE_LOW,
|
|
|
|
bytes_write & 0xff);
|
|
|
|
if (status)
|
2005-04-17 06:20:36 +08:00
|
|
|
goto exit;
|
|
|
|
|
|
|
|
/* send the buffer */
|
|
|
|
for (i = 0; i < bytes_write; i++) {
|
2017-07-16 08:43:06 +08:00
|
|
|
status = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_DATA,
|
|
|
|
buf[i]);
|
|
|
|
if (status)
|
2005-04-17 06:20:36 +08:00
|
|
|
goto exit;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* check for write error (WE should now be 0) */
|
2017-07-16 08:43:06 +08:00
|
|
|
status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS);
|
|
|
|
if (status < 0)
|
2005-04-17 06:20:36 +08:00
|
|
|
goto exit;
|
|
|
|
if (status & STATUSREG_WE) {
|
|
|
|
ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_LINKINIT;
|
|
|
|
status = -EIO;
|
|
|
|
goto exit;
|
|
|
|
}
|
|
|
|
status = bytes_write;
|
|
|
|
|
|
|
|
dprintk("Wrote CA packet for slot %i, connection id 0x%x last_frag:%i size:0x%x\n", slot,
|
|
|
|
buf[0], (buf[1] & 0x80) == 0, bytes_write);
|
|
|
|
|
|
|
|
exit:
|
|
|
|
ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, IRQEN);
|
|
|
|
|
|
|
|
exitnowrite:
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* ******************************************************************************** */
|
|
|
|
/* EN50221 higher level functions */
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2017-05-08 05:23:34 +08:00
|
|
|
* dvb_ca_en50221_slot_shutdown - A CAM has been removed => shut it down.
|
2005-04-17 06:20:36 +08:00
|
|
|
*
|
2015-08-22 18:09:29 +08:00
|
|
|
* @ca: CA instance.
|
|
|
|
* @slot: Slot to shut down.
|
2005-04-17 06:20:36 +08:00
|
|
|
*/
|
|
|
|
static int dvb_ca_en50221_slot_shutdown(struct dvb_ca_private *ca, int slot)
|
|
|
|
{
|
2008-04-09 10:20:00 +08:00
|
|
|
dprintk("%s\n", __func__);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
ca->pub->slot_shutdown(ca->pub, slot);
|
|
|
|
ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_NONE;
|
|
|
|
|
2017-07-16 08:43:05 +08:00
|
|
|
/*
|
|
|
|
* need to wake up all processes to check if they're now trying to
|
|
|
|
* write to a defunct CAM
|
|
|
|
*/
|
2005-04-17 06:20:36 +08:00
|
|
|
wake_up_interruptible(&ca->wait_queue);
|
|
|
|
|
|
|
|
dprintk("Slot %i shutdown\n", slot);
|
|
|
|
|
|
|
|
/* success */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2017-05-08 05:23:34 +08:00
|
|
|
* dvb_ca_en50221_camchange_irq - A CAMCHANGE IRQ has occurred.
|
2005-04-17 06:20:36 +08:00
|
|
|
*
|
2015-08-22 18:09:29 +08:00
|
|
|
* @ca: CA instance.
|
|
|
|
* @slot: Slot concerned.
|
|
|
|
* @change_type: One of the DVB_CA_CAMCHANGE_* values.
|
2005-04-17 06:20:36 +08:00
|
|
|
*/
|
|
|
|
void dvb_ca_en50221_camchange_irq(struct dvb_ca_en50221 *pubca, int slot, int change_type)
|
|
|
|
{
|
2005-05-17 12:54:24 +08:00
|
|
|
struct dvb_ca_private *ca = pubca->private;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
dprintk("CAMCHANGE IRQ slot:%i change_type:%i\n", slot, change_type);
|
|
|
|
|
|
|
|
switch (change_type) {
|
|
|
|
case DVB_CA_EN50221_CAMCHANGE_REMOVED:
|
|
|
|
case DVB_CA_EN50221_CAMCHANGE_INSERTED:
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
ca->slot_info[slot].camchange_type = change_type;
|
|
|
|
atomic_inc(&ca->slot_info[slot].camchange_count);
|
|
|
|
dvb_ca_en50221_thread_wakeup(ca);
|
|
|
|
}
|
2017-05-08 05:23:34 +08:00
|
|
|
EXPORT_SYMBOL(dvb_ca_en50221_camchange_irq);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
2015-08-22 18:09:29 +08:00
|
|
|
* dvb_ca_en50221_camready_irq - A CAMREADY IRQ has occurred.
|
2005-04-17 06:20:36 +08:00
|
|
|
*
|
2015-08-22 18:09:29 +08:00
|
|
|
* @ca: CA instance.
|
|
|
|
* @slot: Slot concerned.
|
2005-04-17 06:20:36 +08:00
|
|
|
*/
|
|
|
|
void dvb_ca_en50221_camready_irq(struct dvb_ca_en50221 *pubca, int slot)
|
|
|
|
{
|
2005-05-17 12:54:24 +08:00
|
|
|
struct dvb_ca_private *ca = pubca->private;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
dprintk("CAMREADY IRQ slot:%i\n", slot);
|
|
|
|
|
|
|
|
if (ca->slot_info[slot].slot_state == DVB_CA_SLOTSTATE_WAITREADY) {
|
|
|
|
ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_VALIDATE;
|
|
|
|
dvb_ca_en50221_thread_wakeup(ca);
|
|
|
|
}
|
|
|
|
}
|
2017-05-08 05:23:34 +08:00
|
|
|
EXPORT_SYMBOL(dvb_ca_en50221_camready_irq);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
2017-05-08 05:23:34 +08:00
|
|
|
* dvb_ca_en50221_frda_irq - An FR or DA IRQ has occurred.
|
2005-04-17 06:20:36 +08:00
|
|
|
*
|
2015-08-22 18:09:29 +08:00
|
|
|
* @ca: CA instance.
|
|
|
|
* @slot: Slot concerned.
|
2005-04-17 06:20:36 +08:00
|
|
|
*/
|
|
|
|
void dvb_ca_en50221_frda_irq(struct dvb_ca_en50221 *pubca, int slot)
|
|
|
|
{
|
2005-05-17 12:54:24 +08:00
|
|
|
struct dvb_ca_private *ca = pubca->private;
|
2005-04-17 06:20:36 +08:00
|
|
|
int flags;
|
|
|
|
|
|
|
|
dprintk("FR/DA IRQ slot:%i\n", slot);
|
|
|
|
|
|
|
|
switch (ca->slot_info[slot].slot_state) {
|
|
|
|
case DVB_CA_SLOTSTATE_LINKINIT:
|
|
|
|
flags = ca->pub->read_cam_control(pubca, slot, CTRLIF_STATUS);
|
|
|
|
if (flags & STATUSREG_DA) {
|
|
|
|
dprintk("CAM supports DA IRQ\n");
|
|
|
|
ca->slot_info[slot].da_irq_supported = 1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DVB_CA_SLOTSTATE_RUNNING:
|
|
|
|
if (ca->open)
|
2005-12-01 16:51:49 +08:00
|
|
|
dvb_ca_en50221_thread_wakeup(ca);
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2017-05-08 05:23:34 +08:00
|
|
|
EXPORT_SYMBOL(dvb_ca_en50221_frda_irq);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
|
|
|
|
/* ******************************************************************************** */
|
|
|
|
/* EN50221 thread functions */
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Wake up the DVB CA thread
|
|
|
|
*
|
2015-08-22 18:09:29 +08:00
|
|
|
* @ca: CA instance.
|
2005-04-17 06:20:36 +08:00
|
|
|
*/
|
|
|
|
static void dvb_ca_en50221_thread_wakeup(struct dvb_ca_private *ca)
|
|
|
|
{
|
|
|
|
|
2008-04-09 10:20:00 +08:00
|
|
|
dprintk("%s\n", __func__);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
ca->wakeup = 1;
|
|
|
|
mb();
|
2007-10-03 22:23:01 +08:00
|
|
|
wake_up_process(ca->thread);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Update the delay used by the thread.
|
|
|
|
*
|
2015-08-22 18:09:29 +08:00
|
|
|
* @ca: CA instance.
|
2005-04-17 06:20:36 +08:00
|
|
|
*/
|
|
|
|
static void dvb_ca_en50221_thread_update_delay(struct dvb_ca_private *ca)
|
|
|
|
{
|
|
|
|
int delay;
|
|
|
|
int curdelay = 100000000;
|
|
|
|
int slot;
|
|
|
|
|
2008-05-03 23:58:36 +08:00
|
|
|
/* Beware of too high polling frequency, because one polling
|
|
|
|
* call might take several hundred milliseconds until timeout!
|
|
|
|
*/
|
2005-04-17 06:20:36 +08:00
|
|
|
for (slot = 0; slot < ca->slot_count; slot++) {
|
|
|
|
switch (ca->slot_info[slot].slot_state) {
|
|
|
|
default:
|
|
|
|
case DVB_CA_SLOTSTATE_NONE:
|
2008-05-03 23:58:36 +08:00
|
|
|
delay = HZ * 60; /* 60s */
|
|
|
|
if (!(ca->flags & DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE))
|
|
|
|
delay = HZ * 5; /* 5s */
|
|
|
|
break;
|
2005-04-17 06:20:36 +08:00
|
|
|
case DVB_CA_SLOTSTATE_INVALID:
|
2008-05-03 23:58:36 +08:00
|
|
|
delay = HZ * 60; /* 60s */
|
|
|
|
if (!(ca->flags & DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE))
|
|
|
|
delay = HZ / 10; /* 100ms */
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case DVB_CA_SLOTSTATE_UNINITIALISED:
|
|
|
|
case DVB_CA_SLOTSTATE_WAITREADY:
|
|
|
|
case DVB_CA_SLOTSTATE_VALIDATE:
|
|
|
|
case DVB_CA_SLOTSTATE_WAITFR:
|
|
|
|
case DVB_CA_SLOTSTATE_LINKINIT:
|
2008-05-03 23:58:36 +08:00
|
|
|
delay = HZ / 10; /* 100ms */
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case DVB_CA_SLOTSTATE_RUNNING:
|
2008-05-03 23:58:36 +08:00
|
|
|
delay = HZ * 60; /* 60s */
|
|
|
|
if (!(ca->flags & DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE))
|
|
|
|
delay = HZ / 10; /* 100ms */
|
2005-04-17 06:20:36 +08:00
|
|
|
if (ca->open) {
|
|
|
|
if ((!ca->slot_info[slot].da_irq_supported) ||
|
2008-05-03 23:58:36 +08:00
|
|
|
(!(ca->flags & DVB_CA_EN50221_FLAG_IRQ_DA)))
|
|
|
|
delay = HZ / 10; /* 100ms */
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (delay < curdelay)
|
|
|
|
curdelay = delay;
|
|
|
|
}
|
|
|
|
|
|
|
|
ca->delay = curdelay;
|
|
|
|
}
|
|
|
|
|
2017-07-16 08:43:03 +08:00
|
|
|
/**
|
|
|
|
* Poll if the CAM is gone.
|
|
|
|
*
|
|
|
|
* @ca: CA instance.
|
|
|
|
* @slot: Slot to process.
|
|
|
|
* @return: 0 .. no change
|
|
|
|
* 1 .. CAM state changed
|
|
|
|
*/
|
|
|
|
|
|
|
|
static int dvb_ca_en50221_poll_cam_gone(struct dvb_ca_private *ca, int slot)
|
|
|
|
{
|
|
|
|
int changed = 0;
|
|
|
|
int status;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* we need this extra check for annoying interfaces like the
|
|
|
|
* budget-av
|
|
|
|
*/
|
|
|
|
if ((!(ca->flags & DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE)) &&
|
|
|
|
(ca->pub->poll_slot_status)) {
|
|
|
|
status = ca->pub->poll_slot_status(ca->pub, slot, 0);
|
|
|
|
if (!(status &
|
|
|
|
DVB_CA_EN50221_POLL_CAM_PRESENT)) {
|
|
|
|
ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_NONE;
|
|
|
|
dvb_ca_en50221_thread_update_delay(ca);
|
|
|
|
changed = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return changed;
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/**
|
2017-07-16 08:43:02 +08:00
|
|
|
* Thread state machine for one CA slot to perform the data transfer.
|
|
|
|
*
|
|
|
|
* @ca: CA instance.
|
|
|
|
* @slot: Slot to process.
|
2005-04-17 06:20:36 +08:00
|
|
|
*/
|
2017-07-16 08:43:02 +08:00
|
|
|
static void dvb_ca_en50221_thread_state_machine(struct dvb_ca_private *ca,
|
|
|
|
int slot)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2017-07-16 08:43:02 +08:00
|
|
|
struct dvb_ca_slot *sl = &ca->slot_info[slot];
|
2005-04-17 06:20:36 +08:00
|
|
|
int flags;
|
|
|
|
int pktcount;
|
|
|
|
void *rxbuf;
|
|
|
|
|
2017-07-16 08:43:02 +08:00
|
|
|
mutex_lock(&sl->slot_lock);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2017-07-16 08:43:02 +08:00
|
|
|
/* check the cam status + deal with CAMCHANGEs */
|
|
|
|
while (dvb_ca_en50221_check_camstatus(ca, slot)) {
|
|
|
|
/* clear down an old CI slot if necessary */
|
|
|
|
if (sl->slot_state != DVB_CA_SLOTSTATE_NONE)
|
|
|
|
dvb_ca_en50221_slot_shutdown(ca, slot);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2017-07-16 08:43:02 +08:00
|
|
|
/* if a CAM is NOW present, initialise it */
|
|
|
|
if (sl->camchange_type == DVB_CA_EN50221_CAMCHANGE_INSERTED)
|
|
|
|
sl->slot_state = DVB_CA_SLOTSTATE_UNINITIALISED;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2017-07-16 08:43:02 +08:00
|
|
|
/* we've handled one CAMCHANGE */
|
|
|
|
dvb_ca_en50221_thread_update_delay(ca);
|
|
|
|
atomic_dec(&sl->camchange_count);
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2017-07-16 08:43:02 +08:00
|
|
|
/* CAM state machine */
|
|
|
|
switch (sl->slot_state) {
|
|
|
|
case DVB_CA_SLOTSTATE_NONE:
|
|
|
|
case DVB_CA_SLOTSTATE_INVALID:
|
|
|
|
/* no action needed */
|
|
|
|
break;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2017-07-16 08:43:02 +08:00
|
|
|
case DVB_CA_SLOTSTATE_UNINITIALISED:
|
|
|
|
sl->slot_state = DVB_CA_SLOTSTATE_WAITREADY;
|
|
|
|
ca->pub->slot_reset(ca->pub, slot);
|
|
|
|
sl->timeout = jiffies + (INIT_TIMEOUT_SECS * HZ);
|
|
|
|
break;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2017-07-16 08:43:02 +08:00
|
|
|
case DVB_CA_SLOTSTATE_WAITREADY:
|
|
|
|
if (time_after(jiffies, sl->timeout)) {
|
|
|
|
pr_err("dvb_ca adaptor %d: PC card did not respond :(\n",
|
|
|
|
ca->dvbdev->adapter->num);
|
|
|
|
sl->slot_state = DVB_CA_SLOTSTATE_INVALID;
|
|
|
|
dvb_ca_en50221_thread_update_delay(ca);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* no other action needed; will automatically change state when
|
|
|
|
* ready
|
|
|
|
*/
|
|
|
|
break;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2017-07-16 08:43:02 +08:00
|
|
|
case DVB_CA_SLOTSTATE_VALIDATE:
|
|
|
|
if (dvb_ca_en50221_parse_attributes(ca, slot) != 0) {
|
2017-07-16 08:43:03 +08:00
|
|
|
if (dvb_ca_en50221_poll_cam_gone(ca, slot))
|
|
|
|
break;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2017-07-16 08:43:02 +08:00
|
|
|
pr_err("dvb_ca adapter %d: Invalid PC card inserted :(\n",
|
|
|
|
ca->dvbdev->adapter->num);
|
|
|
|
sl->slot_state = DVB_CA_SLOTSTATE_INVALID;
|
|
|
|
dvb_ca_en50221_thread_update_delay(ca);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (dvb_ca_en50221_set_configoption(ca, slot) != 0) {
|
|
|
|
pr_err("dvb_ca adapter %d: Unable to initialise CAM :(\n",
|
|
|
|
ca->dvbdev->adapter->num);
|
|
|
|
sl->slot_state = DVB_CA_SLOTSTATE_INVALID;
|
|
|
|
dvb_ca_en50221_thread_update_delay(ca);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (ca->pub->write_cam_control(ca->pub, slot,
|
|
|
|
CTRLIF_COMMAND,
|
|
|
|
CMDREG_RS) != 0) {
|
|
|
|
pr_err("dvb_ca adapter %d: Unable to reset CAM IF\n",
|
|
|
|
ca->dvbdev->adapter->num);
|
|
|
|
sl->slot_state = DVB_CA_SLOTSTATE_INVALID;
|
|
|
|
dvb_ca_en50221_thread_update_delay(ca);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
dprintk("DVB CAM validated successfully\n");
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2017-07-16 08:43:02 +08:00
|
|
|
sl->timeout = jiffies + (INIT_TIMEOUT_SECS * HZ);
|
|
|
|
sl->slot_state = DVB_CA_SLOTSTATE_WAITFR;
|
|
|
|
ca->wakeup = 1;
|
|
|
|
break;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2017-07-16 08:43:02 +08:00
|
|
|
case DVB_CA_SLOTSTATE_WAITFR:
|
|
|
|
if (time_after(jiffies, sl->timeout)) {
|
|
|
|
pr_err("dvb_ca adapter %d: DVB CAM did not respond :(\n",
|
|
|
|
ca->dvbdev->adapter->num);
|
|
|
|
sl->slot_state = DVB_CA_SLOTSTATE_INVALID;
|
|
|
|
dvb_ca_en50221_thread_update_delay(ca);
|
|
|
|
break;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2017-07-16 08:43:02 +08:00
|
|
|
flags = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS);
|
|
|
|
if (flags & STATUSREG_FR) {
|
|
|
|
sl->slot_state = DVB_CA_SLOTSTATE_LINKINIT;
|
|
|
|
ca->wakeup = 1;
|
|
|
|
}
|
|
|
|
break;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2017-07-16 08:43:02 +08:00
|
|
|
case DVB_CA_SLOTSTATE_LINKINIT:
|
|
|
|
if (dvb_ca_en50221_link_init(ca, slot) != 0) {
|
2017-07-16 08:43:03 +08:00
|
|
|
if (dvb_ca_en50221_poll_cam_gone(ca, slot))
|
|
|
|
break;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2017-07-16 08:43:02 +08:00
|
|
|
pr_err("dvb_ca adapter %d: DVB CAM link initialisation failed :(\n",
|
|
|
|
ca->dvbdev->adapter->num);
|
|
|
|
sl->slot_state = DVB_CA_SLOTSTATE_UNINITIALISED;
|
|
|
|
dvb_ca_en50221_thread_update_delay(ca);
|
|
|
|
break;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2017-07-16 08:43:02 +08:00
|
|
|
if (!sl->rx_buffer.data) {
|
|
|
|
rxbuf = vmalloc(RX_BUFFER_SIZE);
|
|
|
|
if (!rxbuf) {
|
|
|
|
pr_err("dvb_ca adapter %d: Unable to allocate CAM rx buffer :(\n",
|
2016-10-13 17:47:54 +08:00
|
|
|
ca->dvbdev->adapter->num);
|
2017-07-16 08:43:02 +08:00
|
|
|
sl->slot_state = DVB_CA_SLOTSTATE_INVALID;
|
|
|
|
dvb_ca_en50221_thread_update_delay(ca);
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
2017-07-16 08:43:02 +08:00
|
|
|
}
|
|
|
|
dvb_ringbuffer_init(&sl->rx_buffer, rxbuf,
|
|
|
|
RX_BUFFER_SIZE);
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2017-07-16 08:43:02 +08:00
|
|
|
ca->pub->slot_ts_enable(ca->pub, slot);
|
|
|
|
sl->slot_state = DVB_CA_SLOTSTATE_RUNNING;
|
|
|
|
dvb_ca_en50221_thread_update_delay(ca);
|
|
|
|
pr_err("dvb_ca adapter %d: DVB CAM detected and initialised successfully\n",
|
|
|
|
ca->dvbdev->adapter->num);
|
|
|
|
break;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2017-07-16 08:43:02 +08:00
|
|
|
case DVB_CA_SLOTSTATE_RUNNING:
|
|
|
|
if (!ca->open)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* poll slots for data */
|
|
|
|
pktcount = 0;
|
|
|
|
while (dvb_ca_en50221_read_data(ca, slot, NULL, 0) > 0) {
|
|
|
|
if (!ca->open)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* if a CAMCHANGE occurred at some point, do not do any
|
|
|
|
* more processing of this slot
|
|
|
|
*/
|
|
|
|
if (dvb_ca_en50221_check_camstatus(ca, slot)) {
|
|
|
|
/*
|
|
|
|
* we dont want to sleep on the next iteration
|
|
|
|
* so we can handle the cam change
|
|
|
|
*/
|
|
|
|
ca->wakeup = 1;
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
|
|
|
}
|
2008-09-26 17:29:03 +08:00
|
|
|
|
2017-07-16 08:43:02 +08:00
|
|
|
/* check if we've hit our limit this time */
|
|
|
|
if (++pktcount >= MAX_RX_PACKETS_PER_ITERATION) {
|
|
|
|
/*
|
|
|
|
* dont sleep; there is likely to be more data
|
|
|
|
* to read
|
|
|
|
*/
|
|
|
|
ca->wakeup = 1;
|
|
|
|
break;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2017-07-16 08:43:02 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
mutex_unlock(&sl->slot_lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Kernel thread which monitors CA slots for CAM changes, and performs data
|
|
|
|
* transfers.
|
|
|
|
*/
|
|
|
|
static int dvb_ca_en50221_thread(void *data)
|
|
|
|
{
|
|
|
|
struct dvb_ca_private *ca = data;
|
|
|
|
int slot;
|
|
|
|
|
|
|
|
dprintk("%s\n", __func__);
|
|
|
|
|
|
|
|
/* choose the correct initial delay */
|
|
|
|
dvb_ca_en50221_thread_update_delay(ca);
|
|
|
|
|
|
|
|
/* main loop */
|
|
|
|
while (!kthread_should_stop()) {
|
|
|
|
/* sleep for a bit */
|
|
|
|
if (!ca->wakeup) {
|
|
|
|
set_current_state(TASK_INTERRUPTIBLE);
|
|
|
|
schedule_timeout(ca->delay);
|
|
|
|
if (kthread_should_stop())
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
ca->wakeup = 0;
|
|
|
|
|
|
|
|
/* go through all the slots processing them */
|
|
|
|
for (slot = 0; slot < ca->slot_count; slot++)
|
|
|
|
dvb_ca_en50221_thread_state_machine(ca, slot);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* ******************************************************************************** */
|
|
|
|
/* EN50221 IO interface functions */
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Real ioctl implementation.
|
|
|
|
* NOTE: CA_SEND_MSG/CA_GET_MSG ioctls have userspace buffers passed to them.
|
|
|
|
*
|
2015-08-22 18:09:29 +08:00
|
|
|
* @inode: Inode concerned.
|
|
|
|
* @file: File concerned.
|
|
|
|
* @cmd: IOCTL command.
|
|
|
|
* @arg: Associated argument.
|
2005-04-17 06:20:36 +08:00
|
|
|
*
|
|
|
|
* @return 0 on success, <0 on error.
|
|
|
|
*/
|
2010-04-27 06:24:00 +08:00
|
|
|
static int dvb_ca_en50221_io_do_ioctl(struct file *file,
|
2005-04-17 06:20:36 +08:00
|
|
|
unsigned int cmd, void *parg)
|
|
|
|
{
|
2005-05-17 12:54:24 +08:00
|
|
|
struct dvb_device *dvbdev = file->private_data;
|
|
|
|
struct dvb_ca_private *ca = dvbdev->priv;
|
2005-04-17 06:20:36 +08:00
|
|
|
int err = 0;
|
|
|
|
int slot;
|
|
|
|
|
2008-04-09 10:20:00 +08:00
|
|
|
dprintk("%s\n", __func__);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2012-12-24 05:49:07 +08:00
|
|
|
if (mutex_lock_interruptible(&ca->ioctl_mutex))
|
|
|
|
return -ERESTARTSYS;
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
switch (cmd) {
|
|
|
|
case CA_RESET:
|
|
|
|
for (slot = 0; slot < ca->slot_count; slot++) {
|
2008-09-26 17:29:03 +08:00
|
|
|
mutex_lock(&ca->slot_info[slot].slot_lock);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (ca->slot_info[slot].slot_state != DVB_CA_SLOTSTATE_NONE) {
|
|
|
|
dvb_ca_en50221_slot_shutdown(ca, slot);
|
|
|
|
if (ca->flags & DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE)
|
|
|
|
dvb_ca_en50221_camchange_irq(ca->pub,
|
|
|
|
slot,
|
|
|
|
DVB_CA_EN50221_CAMCHANGE_INSERTED);
|
|
|
|
}
|
2008-09-26 17:29:03 +08:00
|
|
|
mutex_unlock(&ca->slot_info[slot].slot_lock);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
ca->next_read_slot = 0;
|
|
|
|
dvb_ca_en50221_thread_wakeup(ca);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CA_GET_CAP: {
|
2005-05-17 12:54:24 +08:00
|
|
|
struct ca_caps *caps = parg;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
caps->slot_num = ca->slot_count;
|
|
|
|
caps->slot_type = CA_CI_LINK;
|
|
|
|
caps->descr_num = 0;
|
|
|
|
caps->descr_type = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case CA_GET_SLOT_INFO: {
|
2005-05-17 12:54:24 +08:00
|
|
|
struct ca_slot_info *info = parg;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2013-01-05 01:56:02 +08:00
|
|
|
if ((info->num > ca->slot_count) || (info->num < 0)) {
|
|
|
|
err = -EINVAL;
|
|
|
|
goto out_unlock;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
info->type = CA_CI_LINK;
|
|
|
|
info->flags = 0;
|
|
|
|
if ((ca->slot_info[info->num].slot_state != DVB_CA_SLOTSTATE_NONE)
|
|
|
|
&& (ca->slot_info[info->num].slot_state != DVB_CA_SLOTSTATE_INVALID)) {
|
|
|
|
info->flags = CA_CI_MODULE_PRESENT;
|
|
|
|
}
|
|
|
|
if (ca->slot_info[info->num].slot_state == DVB_CA_SLOTSTATE_RUNNING) {
|
|
|
|
info->flags |= CA_CI_MODULE_READY;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
err = -EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2013-01-05 01:56:02 +08:00
|
|
|
out_unlock:
|
2012-12-24 05:49:07 +08:00
|
|
|
mutex_unlock(&ca->ioctl_mutex);
|
2005-04-17 06:20:36 +08:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Wrapper for ioctl implementation.
|
|
|
|
*
|
2015-08-22 18:09:29 +08:00
|
|
|
* @inode: Inode concerned.
|
|
|
|
* @file: File concerned.
|
|
|
|
* @cmd: IOCTL command.
|
|
|
|
* @arg: Associated argument.
|
2005-04-17 06:20:36 +08:00
|
|
|
*
|
|
|
|
* @return 0 on success, <0 on error.
|
|
|
|
*/
|
2010-04-27 06:24:00 +08:00
|
|
|
static long dvb_ca_en50221_io_ioctl(struct file *file,
|
|
|
|
unsigned int cmd, unsigned long arg)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2010-09-12 01:56:45 +08:00
|
|
|
return dvb_usercopy(file, cmd, arg, dvb_ca_en50221_io_do_ioctl);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Implementation of write() syscall.
|
|
|
|
*
|
2015-08-22 18:09:29 +08:00
|
|
|
* @file: File structure.
|
|
|
|
* @buf: Source buffer.
|
|
|
|
* @count: Size of source buffer.
|
|
|
|
* @ppos: Position in file (ignored).
|
2005-04-17 06:20:36 +08:00
|
|
|
*
|
|
|
|
* @return Number of bytes read, or <0 on error.
|
|
|
|
*/
|
|
|
|
static ssize_t dvb_ca_en50221_io_write(struct file *file,
|
2017-05-08 05:23:28 +08:00
|
|
|
const char __user *buf, size_t count,
|
|
|
|
loff_t *ppos)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-05-17 12:54:24 +08:00
|
|
|
struct dvb_device *dvbdev = file->private_data;
|
|
|
|
struct dvb_ca_private *ca = dvbdev->priv;
|
2005-04-17 06:20:36 +08:00
|
|
|
u8 slot, connection_id;
|
|
|
|
int status;
|
2007-07-13 22:54:35 +08:00
|
|
|
u8 fragbuf[HOST_LINK_BUF_SIZE];
|
2005-04-17 06:20:36 +08:00
|
|
|
int fragpos = 0;
|
|
|
|
int fraglen;
|
|
|
|
unsigned long timeout;
|
|
|
|
int written;
|
|
|
|
|
2008-04-09 10:20:00 +08:00
|
|
|
dprintk("%s\n", __func__);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/* Incoming packet has a 2 byte header. hdr[0] = slot_id, hdr[1] = connection_id */
|
|
|
|
if (count < 2)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
/* extract slot & connection id */
|
|
|
|
if (copy_from_user(&slot, buf, 1))
|
|
|
|
return -EFAULT;
|
|
|
|
if (copy_from_user(&connection_id, buf + 1, 1))
|
|
|
|
return -EFAULT;
|
|
|
|
buf += 2;
|
|
|
|
count -= 2;
|
|
|
|
|
|
|
|
/* check if the slot is actually running */
|
|
|
|
if (ca->slot_info[slot].slot_state != DVB_CA_SLOTSTATE_RUNNING)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
/* fragment the packets & store in the buffer */
|
|
|
|
while (fragpos < count) {
|
|
|
|
fraglen = ca->slot_info[slot].link_buf_size - 2;
|
2012-01-11 05:08:53 +08:00
|
|
|
if (fraglen < 0)
|
|
|
|
break;
|
|
|
|
if (fraglen > HOST_LINK_BUF_SIZE - 2)
|
|
|
|
fraglen = HOST_LINK_BUF_SIZE - 2;
|
2005-04-17 06:20:36 +08:00
|
|
|
if ((count - fragpos) < fraglen)
|
|
|
|
fraglen = count - fragpos;
|
|
|
|
|
|
|
|
fragbuf[0] = connection_id;
|
|
|
|
fragbuf[1] = ((fragpos + fraglen) < count) ? 0x80 : 0x00;
|
2010-06-04 23:39:03 +08:00
|
|
|
status = copy_from_user(fragbuf + 2, buf + fragpos, fraglen);
|
|
|
|
if (status) {
|
|
|
|
status = -EFAULT;
|
2005-04-17 06:20:36 +08:00
|
|
|
goto exit;
|
2010-06-04 23:39:03 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
timeout = jiffies + HZ / 2;
|
|
|
|
written = 0;
|
|
|
|
while (!time_after(jiffies, timeout)) {
|
|
|
|
/* check the CAM hasn't been removed/reset in the meantime */
|
|
|
|
if (ca->slot_info[slot].slot_state != DVB_CA_SLOTSTATE_RUNNING) {
|
|
|
|
status = -EIO;
|
|
|
|
goto exit;
|
|
|
|
}
|
|
|
|
|
2008-09-26 17:29:03 +08:00
|
|
|
mutex_lock(&ca->slot_info[slot].slot_lock);
|
2005-04-17 06:20:36 +08:00
|
|
|
status = dvb_ca_en50221_write_data(ca, slot, fragbuf, fraglen + 2);
|
2008-09-26 17:29:03 +08:00
|
|
|
mutex_unlock(&ca->slot_info[slot].slot_lock);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (status == (fraglen + 2)) {
|
|
|
|
written = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (status != -EAGAIN)
|
|
|
|
goto exit;
|
|
|
|
|
2017-07-16 08:43:04 +08:00
|
|
|
usleep_range(1000, 1100);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
if (!written) {
|
|
|
|
status = -EIO;
|
|
|
|
goto exit;
|
|
|
|
}
|
|
|
|
|
|
|
|
fragpos += fraglen;
|
|
|
|
}
|
|
|
|
status = count + 2;
|
|
|
|
|
|
|
|
exit:
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Condition for waking up in dvb_ca_en50221_io_read_condition
|
|
|
|
*/
|
2005-12-01 16:51:49 +08:00
|
|
|
static int dvb_ca_en50221_io_read_condition(struct dvb_ca_private *ca,
|
|
|
|
int *result, int *_slot)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
int slot;
|
|
|
|
int slot_count = 0;
|
|
|
|
int idx;
|
2005-12-01 16:51:49 +08:00
|
|
|
size_t fraglen;
|
2005-04-17 06:20:36 +08:00
|
|
|
int connection_id = -1;
|
|
|
|
int found = 0;
|
|
|
|
u8 hdr[2];
|
|
|
|
|
|
|
|
slot = ca->next_read_slot;
|
|
|
|
while ((slot_count < ca->slot_count) && (!found)) {
|
|
|
|
if (ca->slot_info[slot].slot_state != DVB_CA_SLOTSTATE_RUNNING)
|
|
|
|
goto nextslot;
|
|
|
|
|
|
|
|
if (ca->slot_info[slot].rx_buffer.data == NULL) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
idx = dvb_ringbuffer_pkt_next(&ca->slot_info[slot].rx_buffer, -1, &fraglen);
|
|
|
|
while (idx != -1) {
|
2008-06-23 01:20:29 +08:00
|
|
|
dvb_ringbuffer_pkt_read(&ca->slot_info[slot].rx_buffer, idx, 0, hdr, 2);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (connection_id == -1)
|
|
|
|
connection_id = hdr[0];
|
|
|
|
if ((hdr[0] == connection_id) && ((hdr[1] & 0x80) == 0)) {
|
|
|
|
*_slot = slot;
|
|
|
|
found = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
idx = dvb_ringbuffer_pkt_next(&ca->slot_info[slot].rx_buffer, idx, &fraglen);
|
|
|
|
}
|
|
|
|
|
2005-12-01 16:51:49 +08:00
|
|
|
nextslot:
|
2005-04-17 06:20:36 +08:00
|
|
|
slot = (slot + 1) % ca->slot_count;
|
|
|
|
slot_count++;
|
|
|
|
}
|
|
|
|
|
|
|
|
ca->next_read_slot = slot;
|
|
|
|
return found;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Implementation of read() syscall.
|
|
|
|
*
|
2015-08-22 18:09:29 +08:00
|
|
|
* @file: File structure.
|
|
|
|
* @buf: Destination buffer.
|
|
|
|
* @count: Size of destination buffer.
|
|
|
|
* @ppos: Position in file (ignored).
|
2005-04-17 06:20:36 +08:00
|
|
|
*
|
|
|
|
* @return Number of bytes read, or <0 on error.
|
|
|
|
*/
|
2017-05-08 05:23:28 +08:00
|
|
|
static ssize_t dvb_ca_en50221_io_read(struct file *file, char __user *buf,
|
|
|
|
size_t count, loff_t *ppos)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-05-17 12:54:24 +08:00
|
|
|
struct dvb_device *dvbdev = file->private_data;
|
|
|
|
struct dvb_ca_private *ca = dvbdev->priv;
|
2005-04-17 06:20:36 +08:00
|
|
|
int status;
|
|
|
|
int result = 0;
|
|
|
|
u8 hdr[2];
|
|
|
|
int slot;
|
|
|
|
int connection_id = -1;
|
|
|
|
size_t idx, idx2;
|
|
|
|
int last_fragment = 0;
|
|
|
|
size_t fraglen;
|
|
|
|
int pktlen;
|
|
|
|
int dispose = 0;
|
|
|
|
|
2008-04-09 10:20:00 +08:00
|
|
|
dprintk("%s\n", __func__);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/* Outgoing packet has a 2 byte header. hdr[0] = slot_id, hdr[1] = connection_id */
|
|
|
|
if (count < 2)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
/* wait for some data */
|
2017-07-16 08:43:06 +08:00
|
|
|
status = dvb_ca_en50221_io_read_condition(ca, &result, &slot);
|
|
|
|
if (status == 0) {
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/* if we're in nonblocking mode, exit immediately */
|
|
|
|
if (file->f_flags & O_NONBLOCK)
|
|
|
|
return -EWOULDBLOCK;
|
|
|
|
|
|
|
|
/* wait for some data */
|
|
|
|
status = wait_event_interruptible(ca->wait_queue,
|
|
|
|
dvb_ca_en50221_io_read_condition
|
|
|
|
(ca, &result, &slot));
|
|
|
|
}
|
|
|
|
if ((status < 0) || (result < 0)) {
|
|
|
|
if (result)
|
|
|
|
return result;
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
idx = dvb_ringbuffer_pkt_next(&ca->slot_info[slot].rx_buffer, -1, &fraglen);
|
|
|
|
pktlen = 2;
|
|
|
|
do {
|
|
|
|
if (idx == -1) {
|
2016-10-13 17:47:54 +08:00
|
|
|
pr_err("dvb_ca adapter %d: BUG: read packet ended before last_fragment encountered\n",
|
|
|
|
ca->dvbdev->adapter->num);
|
2005-04-17 06:20:36 +08:00
|
|
|
status = -EIO;
|
|
|
|
goto exit;
|
|
|
|
}
|
|
|
|
|
2008-06-23 01:20:29 +08:00
|
|
|
dvb_ringbuffer_pkt_read(&ca->slot_info[slot].rx_buffer, idx, 0, hdr, 2);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (connection_id == -1)
|
|
|
|
connection_id = hdr[0];
|
|
|
|
if (hdr[0] == connection_id) {
|
|
|
|
if (pktlen < count) {
|
|
|
|
if ((pktlen + fraglen - 2) > count) {
|
|
|
|
fraglen = count - pktlen;
|
|
|
|
} else {
|
|
|
|
fraglen -= 2;
|
|
|
|
}
|
|
|
|
|
2008-06-23 01:20:29 +08:00
|
|
|
if ((status = dvb_ringbuffer_pkt_read_user(&ca->slot_info[slot].rx_buffer, idx, 2,
|
|
|
|
buf + pktlen, fraglen)) < 0) {
|
2005-04-17 06:20:36 +08:00
|
|
|
goto exit;
|
|
|
|
}
|
|
|
|
pktlen += fraglen;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((hdr[1] & 0x80) == 0)
|
|
|
|
last_fragment = 1;
|
|
|
|
dispose = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
idx2 = dvb_ringbuffer_pkt_next(&ca->slot_info[slot].rx_buffer, idx, &fraglen);
|
|
|
|
if (dispose)
|
|
|
|
dvb_ringbuffer_pkt_dispose(&ca->slot_info[slot].rx_buffer, idx);
|
|
|
|
idx = idx2;
|
|
|
|
dispose = 0;
|
|
|
|
} while (!last_fragment);
|
|
|
|
|
|
|
|
hdr[0] = slot;
|
|
|
|
hdr[1] = connection_id;
|
2010-06-04 23:39:03 +08:00
|
|
|
status = copy_to_user(buf, hdr, 2);
|
|
|
|
if (status) {
|
|
|
|
status = -EFAULT;
|
2005-04-17 06:20:36 +08:00
|
|
|
goto exit;
|
2010-06-04 23:39:03 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
status = pktlen;
|
|
|
|
|
2005-12-01 16:51:49 +08:00
|
|
|
exit:
|
2005-04-17 06:20:36 +08:00
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Implementation of file open syscall.
|
|
|
|
*
|
2015-08-22 18:09:29 +08:00
|
|
|
* @inode: Inode concerned.
|
|
|
|
* @file: File concerned.
|
2005-04-17 06:20:36 +08:00
|
|
|
*
|
|
|
|
* @return 0 on success, <0 on failure.
|
|
|
|
*/
|
|
|
|
static int dvb_ca_en50221_io_open(struct inode *inode, struct file *file)
|
|
|
|
{
|
2005-05-17 12:54:24 +08:00
|
|
|
struct dvb_device *dvbdev = file->private_data;
|
|
|
|
struct dvb_ca_private *ca = dvbdev->priv;
|
2005-04-17 06:20:36 +08:00
|
|
|
int err;
|
|
|
|
int i;
|
|
|
|
|
2008-04-09 10:20:00 +08:00
|
|
|
dprintk("%s\n", __func__);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (!try_module_get(ca->pub->owner))
|
|
|
|
return -EIO;
|
|
|
|
|
|
|
|
err = dvb_generic_open(inode, file);
|
2007-08-25 22:46:07 +08:00
|
|
|
if (err < 0) {
|
|
|
|
module_put(ca->pub->owner);
|
2005-04-17 06:20:36 +08:00
|
|
|
return err;
|
2007-08-25 22:46:07 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
for (i = 0; i < ca->slot_count; i++) {
|
|
|
|
|
|
|
|
if (ca->slot_info[i].slot_state == DVB_CA_SLOTSTATE_RUNNING) {
|
|
|
|
if (ca->slot_info[i].rx_buffer.data != NULL) {
|
2017-07-16 08:43:05 +08:00
|
|
|
/*
|
|
|
|
* it is safe to call this here without locks
|
|
|
|
* because ca->open == 0. Data is not read in
|
|
|
|
* this case
|
|
|
|
*/
|
2005-04-17 06:20:36 +08:00
|
|
|
dvb_ringbuffer_flush(&ca->slot_info[i].rx_buffer);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ca->open = 1;
|
|
|
|
dvb_ca_en50221_thread_update_delay(ca);
|
|
|
|
dvb_ca_en50221_thread_wakeup(ca);
|
|
|
|
|
[media] dvb-core/en50221: use kref to manage struct dvb_ca_private
Don't free the object until the file handle has been closed. Fixes
use-after-free bug which occurs when I disconnect my DVB-S received
while VDR is running.
This is a crash dump of such a use-after-free:
general protection fault: 0000 [#1] SMP
CPU: 0 PID: 2541 Comm: CI adapter on d Not tainted 4.7.0-rc1-hosting+ #49
Hardware name: Bochs Bochs, BIOS Bochs 01/01/2011
task: ffff880027d7ce00 ti: ffff88003d8f8000 task.ti: ffff88003d8f8000
RIP: 0010:[<ffffffff812f3d1f>] [<ffffffff812f3d1f>] dvb_ca_en50221_io_read_condition.isra.7+0x6f/0x150
RSP: 0018:ffff88003d8fba98 EFLAGS: 00010206
RAX: 0000000059534255 RBX: 000000753d470f90 RCX: ffff88003c74d181
RDX: 00000001bea04ba9 RSI: ffff88003d8fbaf4 RDI: 3a3030a56d763fc0
RBP: ffff88003d8fbae0 R08: ffff88003c74d180 R09: 0000000000000000
R10: 0000000000000001 R11: 0000000000000000 R12: ffff88003c480e00
R13: 00000000ffffffff R14: 0000000059534255 R15: 0000000000000000
FS: 00007fb4209b4700(0000) GS:ffff88003fc00000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007f06445f4078 CR3: 000000003c55b000 CR4: 00000000000006b0
Stack:
ffff88003d8fbaf4 000000003c2170c0 0000000000004000 0000000000000000
ffff88003c480e00 ffff88003d8fbc80 ffff88003c74d180 ffff88003d8fbb8c
0000000000000000 ffff88003d8fbb10 ffffffff812f3e37 ffff88003d8fbb00
Call Trace:
[<ffffffff812f3e37>] dvb_ca_en50221_io_poll+0x37/0xa0
[<ffffffff8113109b>] do_sys_poll+0x2db/0x520
This is a backtrace of the kernel attempting to lock a freed mutex:
#0 0xffffffff81083d40 in rep_nop () at ./arch/x86/include/asm/processor.h:569
#1 cpu_relax () at ./arch/x86/include/asm/processor.h:574
#2 virt_spin_lock (lock=<optimized out>) at ./arch/x86/include/asm/qspinlock.h:57
#3 native_queued_spin_lock_slowpath (lock=0xffff88003c480e90, val=761492029) at kernel/locking/qspinlock.c:304
#4 0xffffffff810d1a06 in pv_queued_spin_lock_slowpath (val=<optimized out>, lock=<optimized out>) at ./arch/x86/include/asm/paravirt.h:669
#5 queued_spin_lock_slowpath (val=<optimized out>, lock=<optimized out>) at ./arch/x86/include/asm/qspinlock.h:28
#6 queued_spin_lock (lock=<optimized out>) at include/asm-generic/qspinlock.h:107
#7 __mutex_lock_common (use_ww_ctx=<optimized out>, ww_ctx=<optimized out>, ip=<optimized out>, nest_lock=<optimized out>, subclass=<optimized out>,
state=<optimized out>, lock=<optimized out>) at kernel/locking/mutex.c:526
#8 mutex_lock_interruptible_nested (lock=0xffff88003c480e88, subclass=<optimized out>) at kernel/locking/mutex.c:647
#9 0xffffffff812f49fe in dvb_ca_en50221_io_do_ioctl (file=<optimized out>, cmd=761492029, parg=0x1 <irq_stack_union+1>)
at drivers/media/dvb-core/dvb_ca_en50221.c:1210
#10 0xffffffff812ee660 in dvb_usercopy (file=<optimized out>, cmd=761492029, arg=<optimized out>, func=<optimized out>) at drivers/media/dvb-core/dvbdev.c:883
#11 0xffffffff812f3410 in dvb_ca_en50221_io_ioctl (file=<optimized out>, cmd=<optimized out>, arg=<optimized out>) at drivers/media/dvb-core/dvb_ca_en50221.c:1284
#12 0xffffffff8112eddd in vfs_ioctl (arg=<optimized out>, cmd=<optimized out>, filp=<optimized out>) at fs/ioctl.c:43
#13 do_vfs_ioctl (filp=0xffff88003c480e90, fd=<optimized out>, cmd=<optimized out>, arg=<optimized out>) at fs/ioctl.c:674
#14 0xffffffff8112f30c in SYSC_ioctl (arg=<optimized out>, cmd=<optimized out>, fd=<optimized out>) at fs/ioctl.c:689
#15 SyS_ioctl (fd=6, cmd=2148298626, arg=140734533693696) at fs/ioctl.c:680
#16 0xffffffff8103feb2 in entry_SYSCALL_64 () at arch/x86/entry/entry_64.S:207
Signed-off-by: Max Kellermann <max@duempel.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
2016-07-04 20:08:45 +08:00
|
|
|
dvb_ca_private_get(ca);
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Implementation of file close syscall.
|
|
|
|
*
|
2015-08-22 18:09:29 +08:00
|
|
|
* @inode: Inode concerned.
|
|
|
|
* @file: File concerned.
|
2005-04-17 06:20:36 +08:00
|
|
|
*
|
|
|
|
* @return 0 on success, <0 on failure.
|
|
|
|
*/
|
|
|
|
static int dvb_ca_en50221_io_release(struct inode *inode, struct file *file)
|
|
|
|
{
|
2005-05-17 12:54:24 +08:00
|
|
|
struct dvb_device *dvbdev = file->private_data;
|
|
|
|
struct dvb_ca_private *ca = dvbdev->priv;
|
2007-08-25 22:36:39 +08:00
|
|
|
int err;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2008-04-09 10:20:00 +08:00
|
|
|
dprintk("%s\n", __func__);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/* mark the CA device as closed */
|
|
|
|
ca->open = 0;
|
|
|
|
dvb_ca_en50221_thread_update_delay(ca);
|
|
|
|
|
|
|
|
err = dvb_generic_release(inode, file);
|
|
|
|
|
|
|
|
module_put(ca->pub->owner);
|
|
|
|
|
[media] dvb-core/en50221: use kref to manage struct dvb_ca_private
Don't free the object until the file handle has been closed. Fixes
use-after-free bug which occurs when I disconnect my DVB-S received
while VDR is running.
This is a crash dump of such a use-after-free:
general protection fault: 0000 [#1] SMP
CPU: 0 PID: 2541 Comm: CI adapter on d Not tainted 4.7.0-rc1-hosting+ #49
Hardware name: Bochs Bochs, BIOS Bochs 01/01/2011
task: ffff880027d7ce00 ti: ffff88003d8f8000 task.ti: ffff88003d8f8000
RIP: 0010:[<ffffffff812f3d1f>] [<ffffffff812f3d1f>] dvb_ca_en50221_io_read_condition.isra.7+0x6f/0x150
RSP: 0018:ffff88003d8fba98 EFLAGS: 00010206
RAX: 0000000059534255 RBX: 000000753d470f90 RCX: ffff88003c74d181
RDX: 00000001bea04ba9 RSI: ffff88003d8fbaf4 RDI: 3a3030a56d763fc0
RBP: ffff88003d8fbae0 R08: ffff88003c74d180 R09: 0000000000000000
R10: 0000000000000001 R11: 0000000000000000 R12: ffff88003c480e00
R13: 00000000ffffffff R14: 0000000059534255 R15: 0000000000000000
FS: 00007fb4209b4700(0000) GS:ffff88003fc00000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007f06445f4078 CR3: 000000003c55b000 CR4: 00000000000006b0
Stack:
ffff88003d8fbaf4 000000003c2170c0 0000000000004000 0000000000000000
ffff88003c480e00 ffff88003d8fbc80 ffff88003c74d180 ffff88003d8fbb8c
0000000000000000 ffff88003d8fbb10 ffffffff812f3e37 ffff88003d8fbb00
Call Trace:
[<ffffffff812f3e37>] dvb_ca_en50221_io_poll+0x37/0xa0
[<ffffffff8113109b>] do_sys_poll+0x2db/0x520
This is a backtrace of the kernel attempting to lock a freed mutex:
#0 0xffffffff81083d40 in rep_nop () at ./arch/x86/include/asm/processor.h:569
#1 cpu_relax () at ./arch/x86/include/asm/processor.h:574
#2 virt_spin_lock (lock=<optimized out>) at ./arch/x86/include/asm/qspinlock.h:57
#3 native_queued_spin_lock_slowpath (lock=0xffff88003c480e90, val=761492029) at kernel/locking/qspinlock.c:304
#4 0xffffffff810d1a06 in pv_queued_spin_lock_slowpath (val=<optimized out>, lock=<optimized out>) at ./arch/x86/include/asm/paravirt.h:669
#5 queued_spin_lock_slowpath (val=<optimized out>, lock=<optimized out>) at ./arch/x86/include/asm/qspinlock.h:28
#6 queued_spin_lock (lock=<optimized out>) at include/asm-generic/qspinlock.h:107
#7 __mutex_lock_common (use_ww_ctx=<optimized out>, ww_ctx=<optimized out>, ip=<optimized out>, nest_lock=<optimized out>, subclass=<optimized out>,
state=<optimized out>, lock=<optimized out>) at kernel/locking/mutex.c:526
#8 mutex_lock_interruptible_nested (lock=0xffff88003c480e88, subclass=<optimized out>) at kernel/locking/mutex.c:647
#9 0xffffffff812f49fe in dvb_ca_en50221_io_do_ioctl (file=<optimized out>, cmd=761492029, parg=0x1 <irq_stack_union+1>)
at drivers/media/dvb-core/dvb_ca_en50221.c:1210
#10 0xffffffff812ee660 in dvb_usercopy (file=<optimized out>, cmd=761492029, arg=<optimized out>, func=<optimized out>) at drivers/media/dvb-core/dvbdev.c:883
#11 0xffffffff812f3410 in dvb_ca_en50221_io_ioctl (file=<optimized out>, cmd=<optimized out>, arg=<optimized out>) at drivers/media/dvb-core/dvb_ca_en50221.c:1284
#12 0xffffffff8112eddd in vfs_ioctl (arg=<optimized out>, cmd=<optimized out>, filp=<optimized out>) at fs/ioctl.c:43
#13 do_vfs_ioctl (filp=0xffff88003c480e90, fd=<optimized out>, cmd=<optimized out>, arg=<optimized out>) at fs/ioctl.c:674
#14 0xffffffff8112f30c in SYSC_ioctl (arg=<optimized out>, cmd=<optimized out>, fd=<optimized out>) at fs/ioctl.c:689
#15 SyS_ioctl (fd=6, cmd=2148298626, arg=140734533693696) at fs/ioctl.c:680
#16 0xffffffff8103feb2 in entry_SYSCALL_64 () at arch/x86/entry/entry_64.S:207
Signed-off-by: Max Kellermann <max@duempel.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
2016-07-04 20:08:45 +08:00
|
|
|
dvb_ca_private_put(ca);
|
|
|
|
|
2007-08-25 22:36:39 +08:00
|
|
|
return err;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Implementation of poll() syscall.
|
|
|
|
*
|
2015-08-22 18:09:29 +08:00
|
|
|
* @file: File concerned.
|
|
|
|
* @wait: poll wait table.
|
2005-04-17 06:20:36 +08:00
|
|
|
*
|
|
|
|
* @return Standard poll mask.
|
|
|
|
*/
|
2017-05-08 05:23:28 +08:00
|
|
|
static unsigned int dvb_ca_en50221_io_poll(struct file *file, poll_table *wait)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-05-17 12:54:24 +08:00
|
|
|
struct dvb_device *dvbdev = file->private_data;
|
|
|
|
struct dvb_ca_private *ca = dvbdev->priv;
|
2005-04-17 06:20:36 +08:00
|
|
|
unsigned int mask = 0;
|
|
|
|
int slot;
|
|
|
|
int result = 0;
|
|
|
|
|
2008-04-09 10:20:00 +08:00
|
|
|
dprintk("%s\n", __func__);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (dvb_ca_en50221_io_read_condition(ca, &result, &slot) == 1) {
|
|
|
|
mask |= POLLIN;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* if there is something, return now */
|
|
|
|
if (mask)
|
|
|
|
return mask;
|
|
|
|
|
|
|
|
/* wait for something to happen */
|
|
|
|
poll_wait(file, &ca->wait_queue, wait);
|
|
|
|
|
|
|
|
if (dvb_ca_en50221_io_read_condition(ca, &result, &slot) == 1) {
|
|
|
|
mask |= POLLIN;
|
|
|
|
}
|
|
|
|
|
|
|
|
return mask;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(dvb_ca_en50221_init);
|
|
|
|
|
|
|
|
|
2009-01-11 17:12:43 +08:00
|
|
|
static const struct file_operations dvb_ca_fops = {
|
2005-04-17 06:20:36 +08:00
|
|
|
.owner = THIS_MODULE,
|
|
|
|
.read = dvb_ca_en50221_io_read,
|
|
|
|
.write = dvb_ca_en50221_io_write,
|
2010-04-27 06:24:00 +08:00
|
|
|
.unlocked_ioctl = dvb_ca_en50221_io_ioctl,
|
2005-04-17 06:20:36 +08:00
|
|
|
.open = dvb_ca_en50221_io_open,
|
|
|
|
.release = dvb_ca_en50221_io_release,
|
|
|
|
.poll = dvb_ca_en50221_io_poll,
|
llseek: automatically add .llseek fop
All file_operations should get a .llseek operation so we can make
nonseekable_open the default for future file operations without a
.llseek pointer.
The three cases that we can automatically detect are no_llseek, seq_lseek
and default_llseek. For cases where we can we can automatically prove that
the file offset is always ignored, we use noop_llseek, which maintains
the current behavior of not returning an error from a seek.
New drivers should normally not use noop_llseek but instead use no_llseek
and call nonseekable_open at open time. Existing drivers can be converted
to do the same when the maintainer knows for certain that no user code
relies on calling seek on the device file.
The generated code is often incorrectly indented and right now contains
comments that clarify for each added line why a specific variant was
chosen. In the version that gets submitted upstream, the comments will
be gone and I will manually fix the indentation, because there does not
seem to be a way to do that using coccinelle.
Some amount of new code is currently sitting in linux-next that should get
the same modifications, which I will do at the end of the merge window.
Many thanks to Julia Lawall for helping me learn to write a semantic
patch that does all this.
===== begin semantic patch =====
// This adds an llseek= method to all file operations,
// as a preparation for making no_llseek the default.
//
// The rules are
// - use no_llseek explicitly if we do nonseekable_open
// - use seq_lseek for sequential files
// - use default_llseek if we know we access f_pos
// - use noop_llseek if we know we don't access f_pos,
// but we still want to allow users to call lseek
//
@ open1 exists @
identifier nested_open;
@@
nested_open(...)
{
<+...
nonseekable_open(...)
...+>
}
@ open exists@
identifier open_f;
identifier i, f;
identifier open1.nested_open;
@@
int open_f(struct inode *i, struct file *f)
{
<+...
(
nonseekable_open(...)
|
nested_open(...)
)
...+>
}
@ read disable optional_qualifier exists @
identifier read_f;
identifier f, p, s, off;
type ssize_t, size_t, loff_t;
expression E;
identifier func;
@@
ssize_t read_f(struct file *f, char *p, size_t s, loff_t *off)
{
<+...
(
*off = E
|
*off += E
|
func(..., off, ...)
|
E = *off
)
...+>
}
@ read_no_fpos disable optional_qualifier exists @
identifier read_f;
identifier f, p, s, off;
type ssize_t, size_t, loff_t;
@@
ssize_t read_f(struct file *f, char *p, size_t s, loff_t *off)
{
... when != off
}
@ write @
identifier write_f;
identifier f, p, s, off;
type ssize_t, size_t, loff_t;
expression E;
identifier func;
@@
ssize_t write_f(struct file *f, const char *p, size_t s, loff_t *off)
{
<+...
(
*off = E
|
*off += E
|
func(..., off, ...)
|
E = *off
)
...+>
}
@ write_no_fpos @
identifier write_f;
identifier f, p, s, off;
type ssize_t, size_t, loff_t;
@@
ssize_t write_f(struct file *f, const char *p, size_t s, loff_t *off)
{
... when != off
}
@ fops0 @
identifier fops;
@@
struct file_operations fops = {
...
};
@ has_llseek depends on fops0 @
identifier fops0.fops;
identifier llseek_f;
@@
struct file_operations fops = {
...
.llseek = llseek_f,
...
};
@ has_read depends on fops0 @
identifier fops0.fops;
identifier read_f;
@@
struct file_operations fops = {
...
.read = read_f,
...
};
@ has_write depends on fops0 @
identifier fops0.fops;
identifier write_f;
@@
struct file_operations fops = {
...
.write = write_f,
...
};
@ has_open depends on fops0 @
identifier fops0.fops;
identifier open_f;
@@
struct file_operations fops = {
...
.open = open_f,
...
};
// use no_llseek if we call nonseekable_open
////////////////////////////////////////////
@ nonseekable1 depends on !has_llseek && has_open @
identifier fops0.fops;
identifier nso ~= "nonseekable_open";
@@
struct file_operations fops = {
... .open = nso, ...
+.llseek = no_llseek, /* nonseekable */
};
@ nonseekable2 depends on !has_llseek @
identifier fops0.fops;
identifier open.open_f;
@@
struct file_operations fops = {
... .open = open_f, ...
+.llseek = no_llseek, /* open uses nonseekable */
};
// use seq_lseek for sequential files
/////////////////////////////////////
@ seq depends on !has_llseek @
identifier fops0.fops;
identifier sr ~= "seq_read";
@@
struct file_operations fops = {
... .read = sr, ...
+.llseek = seq_lseek, /* we have seq_read */
};
// use default_llseek if there is a readdir
///////////////////////////////////////////
@ fops1 depends on !has_llseek && !nonseekable1 && !nonseekable2 && !seq @
identifier fops0.fops;
identifier readdir_e;
@@
// any other fop is used that changes pos
struct file_operations fops = {
... .readdir = readdir_e, ...
+.llseek = default_llseek, /* readdir is present */
};
// use default_llseek if at least one of read/write touches f_pos
/////////////////////////////////////////////////////////////////
@ fops2 depends on !fops1 && !has_llseek && !nonseekable1 && !nonseekable2 && !seq @
identifier fops0.fops;
identifier read.read_f;
@@
// read fops use offset
struct file_operations fops = {
... .read = read_f, ...
+.llseek = default_llseek, /* read accesses f_pos */
};
@ fops3 depends on !fops1 && !fops2 && !has_llseek && !nonseekable1 && !nonseekable2 && !seq @
identifier fops0.fops;
identifier write.write_f;
@@
// write fops use offset
struct file_operations fops = {
... .write = write_f, ...
+ .llseek = default_llseek, /* write accesses f_pos */
};
// Use noop_llseek if neither read nor write accesses f_pos
///////////////////////////////////////////////////////////
@ fops4 depends on !fops1 && !fops2 && !fops3 && !has_llseek && !nonseekable1 && !nonseekable2 && !seq @
identifier fops0.fops;
identifier read_no_fpos.read_f;
identifier write_no_fpos.write_f;
@@
// write fops use offset
struct file_operations fops = {
...
.write = write_f,
.read = read_f,
...
+.llseek = noop_llseek, /* read and write both use no f_pos */
};
@ depends on has_write && !has_read && !fops1 && !fops2 && !has_llseek && !nonseekable1 && !nonseekable2 && !seq @
identifier fops0.fops;
identifier write_no_fpos.write_f;
@@
struct file_operations fops = {
... .write = write_f, ...
+.llseek = noop_llseek, /* write uses no f_pos */
};
@ depends on has_read && !has_write && !fops1 && !fops2 && !has_llseek && !nonseekable1 && !nonseekable2 && !seq @
identifier fops0.fops;
identifier read_no_fpos.read_f;
@@
struct file_operations fops = {
... .read = read_f, ...
+.llseek = noop_llseek, /* read uses no f_pos */
};
@ depends on !has_read && !has_write && !fops1 && !fops2 && !has_llseek && !nonseekable1 && !nonseekable2 && !seq @
identifier fops0.fops;
@@
struct file_operations fops = {
...
+.llseek = noop_llseek, /* no read or write fn */
};
===== End semantic patch =====
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
Cc: Julia Lawall <julia@diku.dk>
Cc: Christoph Hellwig <hch@infradead.org>
2010-08-16 00:52:59 +08:00
|
|
|
.llseek = noop_llseek,
|
2005-04-17 06:20:36 +08:00
|
|
|
};
|
|
|
|
|
2015-01-03 09:48:17 +08:00
|
|
|
static const struct dvb_device dvbdev_ca = {
|
2005-04-17 06:20:36 +08:00
|
|
|
.priv = NULL,
|
|
|
|
.users = 1,
|
|
|
|
.readers = 1,
|
|
|
|
.writers = 1,
|
2015-01-03 09:48:17 +08:00
|
|
|
#if defined(CONFIG_MEDIA_CONTROLLER_DVB)
|
2015-02-18 23:09:27 +08:00
|
|
|
.name = "dvb-ca-en50221",
|
2015-01-03 09:48:17 +08:00
|
|
|
#endif
|
2005-04-17 06:20:36 +08:00
|
|
|
.fops = &dvb_ca_fops,
|
|
|
|
};
|
|
|
|
|
|
|
|
/* ******************************************************************************** */
|
|
|
|
/* Initialisation/shutdown functions */
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Initialise a new DVB CA EN50221 interface device.
|
|
|
|
*
|
2015-08-22 18:09:29 +08:00
|
|
|
* @dvb_adapter: DVB adapter to attach the new CA device to.
|
|
|
|
* @ca: The dvb_ca instance.
|
|
|
|
* @flags: Flags describing the CA device (DVB_CA_FLAG_*).
|
|
|
|
* @slot_count: Number of slots supported.
|
2005-04-17 06:20:36 +08:00
|
|
|
*
|
|
|
|
* @return 0 on success, nonzero on failure
|
|
|
|
*/
|
|
|
|
int dvb_ca_en50221_init(struct dvb_adapter *dvb_adapter,
|
|
|
|
struct dvb_ca_en50221 *pubca, int flags, int slot_count)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
struct dvb_ca_private *ca = NULL;
|
|
|
|
int i;
|
|
|
|
|
2008-04-09 10:20:00 +08:00
|
|
|
dprintk("%s\n", __func__);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (slot_count < 1)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
/* initialise the system data */
|
2017-07-16 08:43:06 +08:00
|
|
|
ca = kzalloc(sizeof(*ca), GFP_KERNEL);
|
|
|
|
if (!ca) {
|
2005-04-17 06:20:36 +08:00
|
|
|
ret = -ENOMEM;
|
2015-02-03 23:47:48 +08:00
|
|
|
goto exit;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
[media] dvb-core/en50221: use kref to manage struct dvb_ca_private
Don't free the object until the file handle has been closed. Fixes
use-after-free bug which occurs when I disconnect my DVB-S received
while VDR is running.
This is a crash dump of such a use-after-free:
general protection fault: 0000 [#1] SMP
CPU: 0 PID: 2541 Comm: CI adapter on d Not tainted 4.7.0-rc1-hosting+ #49
Hardware name: Bochs Bochs, BIOS Bochs 01/01/2011
task: ffff880027d7ce00 ti: ffff88003d8f8000 task.ti: ffff88003d8f8000
RIP: 0010:[<ffffffff812f3d1f>] [<ffffffff812f3d1f>] dvb_ca_en50221_io_read_condition.isra.7+0x6f/0x150
RSP: 0018:ffff88003d8fba98 EFLAGS: 00010206
RAX: 0000000059534255 RBX: 000000753d470f90 RCX: ffff88003c74d181
RDX: 00000001bea04ba9 RSI: ffff88003d8fbaf4 RDI: 3a3030a56d763fc0
RBP: ffff88003d8fbae0 R08: ffff88003c74d180 R09: 0000000000000000
R10: 0000000000000001 R11: 0000000000000000 R12: ffff88003c480e00
R13: 00000000ffffffff R14: 0000000059534255 R15: 0000000000000000
FS: 00007fb4209b4700(0000) GS:ffff88003fc00000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007f06445f4078 CR3: 000000003c55b000 CR4: 00000000000006b0
Stack:
ffff88003d8fbaf4 000000003c2170c0 0000000000004000 0000000000000000
ffff88003c480e00 ffff88003d8fbc80 ffff88003c74d180 ffff88003d8fbb8c
0000000000000000 ffff88003d8fbb10 ffffffff812f3e37 ffff88003d8fbb00
Call Trace:
[<ffffffff812f3e37>] dvb_ca_en50221_io_poll+0x37/0xa0
[<ffffffff8113109b>] do_sys_poll+0x2db/0x520
This is a backtrace of the kernel attempting to lock a freed mutex:
#0 0xffffffff81083d40 in rep_nop () at ./arch/x86/include/asm/processor.h:569
#1 cpu_relax () at ./arch/x86/include/asm/processor.h:574
#2 virt_spin_lock (lock=<optimized out>) at ./arch/x86/include/asm/qspinlock.h:57
#3 native_queued_spin_lock_slowpath (lock=0xffff88003c480e90, val=761492029) at kernel/locking/qspinlock.c:304
#4 0xffffffff810d1a06 in pv_queued_spin_lock_slowpath (val=<optimized out>, lock=<optimized out>) at ./arch/x86/include/asm/paravirt.h:669
#5 queued_spin_lock_slowpath (val=<optimized out>, lock=<optimized out>) at ./arch/x86/include/asm/qspinlock.h:28
#6 queued_spin_lock (lock=<optimized out>) at include/asm-generic/qspinlock.h:107
#7 __mutex_lock_common (use_ww_ctx=<optimized out>, ww_ctx=<optimized out>, ip=<optimized out>, nest_lock=<optimized out>, subclass=<optimized out>,
state=<optimized out>, lock=<optimized out>) at kernel/locking/mutex.c:526
#8 mutex_lock_interruptible_nested (lock=0xffff88003c480e88, subclass=<optimized out>) at kernel/locking/mutex.c:647
#9 0xffffffff812f49fe in dvb_ca_en50221_io_do_ioctl (file=<optimized out>, cmd=761492029, parg=0x1 <irq_stack_union+1>)
at drivers/media/dvb-core/dvb_ca_en50221.c:1210
#10 0xffffffff812ee660 in dvb_usercopy (file=<optimized out>, cmd=761492029, arg=<optimized out>, func=<optimized out>) at drivers/media/dvb-core/dvbdev.c:883
#11 0xffffffff812f3410 in dvb_ca_en50221_io_ioctl (file=<optimized out>, cmd=<optimized out>, arg=<optimized out>) at drivers/media/dvb-core/dvb_ca_en50221.c:1284
#12 0xffffffff8112eddd in vfs_ioctl (arg=<optimized out>, cmd=<optimized out>, filp=<optimized out>) at fs/ioctl.c:43
#13 do_vfs_ioctl (filp=0xffff88003c480e90, fd=<optimized out>, cmd=<optimized out>, arg=<optimized out>) at fs/ioctl.c:674
#14 0xffffffff8112f30c in SYSC_ioctl (arg=<optimized out>, cmd=<optimized out>, fd=<optimized out>) at fs/ioctl.c:689
#15 SyS_ioctl (fd=6, cmd=2148298626, arg=140734533693696) at fs/ioctl.c:680
#16 0xffffffff8103feb2 in entry_SYSCALL_64 () at arch/x86/entry/entry_64.S:207
Signed-off-by: Max Kellermann <max@duempel.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
2016-07-04 20:08:45 +08:00
|
|
|
kref_init(&ca->refcount);
|
2005-04-17 06:20:36 +08:00
|
|
|
ca->pub = pubca;
|
|
|
|
ca->flags = flags;
|
|
|
|
ca->slot_count = slot_count;
|
2017-07-16 08:43:06 +08:00
|
|
|
ca->slot_info = kcalloc(slot_count, sizeof(struct dvb_ca_slot),
|
|
|
|
GFP_KERNEL);
|
|
|
|
if (!ca->slot_info) {
|
2005-04-17 06:20:36 +08:00
|
|
|
ret = -ENOMEM;
|
2015-02-03 23:47:48 +08:00
|
|
|
goto free_ca;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
init_waitqueue_head(&ca->wait_queue);
|
|
|
|
ca->open = 0;
|
|
|
|
ca->wakeup = 0;
|
|
|
|
ca->next_read_slot = 0;
|
|
|
|
pubca->private = ca;
|
|
|
|
|
|
|
|
/* register the DVB device */
|
2015-08-22 03:18:18 +08:00
|
|
|
ret = dvb_register_device(dvb_adapter, &ca->dvbdev, &dvbdev_ca, ca, DVB_DEVICE_CA, 0);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (ret)
|
2015-02-03 23:47:48 +08:00
|
|
|
goto free_slot_info;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/* now initialise each slot */
|
|
|
|
for (i = 0; i < slot_count; i++) {
|
|
|
|
memset(&ca->slot_info[i], 0, sizeof(struct dvb_ca_slot));
|
|
|
|
ca->slot_info[i].slot_state = DVB_CA_SLOTSTATE_NONE;
|
|
|
|
atomic_set(&ca->slot_info[i].camchange_count, 0);
|
|
|
|
ca->slot_info[i].camchange_type = DVB_CA_EN50221_CAMCHANGE_REMOVED;
|
2008-09-26 17:29:03 +08:00
|
|
|
mutex_init(&ca->slot_info[i].slot_lock);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2012-12-24 05:49:07 +08:00
|
|
|
mutex_init(&ca->ioctl_mutex);
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
if (signal_pending(current)) {
|
|
|
|
ret = -EINTR;
|
2015-02-03 23:47:48 +08:00
|
|
|
goto unregister_device;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
mb();
|
|
|
|
|
|
|
|
/* create a kthread for monitoring this CA device */
|
2007-10-03 22:23:01 +08:00
|
|
|
ca->thread = kthread_run(dvb_ca_en50221_thread, ca, "kdvb-ca-%i:%i",
|
|
|
|
ca->dvbdev->adapter->num, ca->dvbdev->id);
|
|
|
|
if (IS_ERR(ca->thread)) {
|
|
|
|
ret = PTR_ERR(ca->thread);
|
2016-10-13 17:47:54 +08:00
|
|
|
pr_err("dvb_ca_init: failed to start kernel_thread (%d)\n",
|
|
|
|
ret);
|
2015-02-03 23:47:48 +08:00
|
|
|
goto unregister_device;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
|
2015-02-03 23:47:48 +08:00
|
|
|
unregister_device:
|
|
|
|
dvb_unregister_device(ca->dvbdev);
|
|
|
|
free_slot_info:
|
|
|
|
kfree(ca->slot_info);
|
|
|
|
free_ca:
|
|
|
|
kfree(ca);
|
|
|
|
exit:
|
2005-04-17 06:20:36 +08:00
|
|
|
pubca->private = NULL;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(dvb_ca_en50221_release);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Release a DVB CA EN50221 interface device.
|
|
|
|
*
|
2015-08-22 18:09:29 +08:00
|
|
|
* @ca_dev: The dvb_device_t instance for the CA device.
|
|
|
|
* @ca: The associated dvb_ca instance.
|
2005-04-17 06:20:36 +08:00
|
|
|
*/
|
|
|
|
void dvb_ca_en50221_release(struct dvb_ca_en50221 *pubca)
|
|
|
|
{
|
2005-05-17 12:54:24 +08:00
|
|
|
struct dvb_ca_private *ca = pubca->private;
|
2005-04-17 06:20:36 +08:00
|
|
|
int i;
|
|
|
|
|
2008-04-09 10:20:00 +08:00
|
|
|
dprintk("%s\n", __func__);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/* shutdown the thread if there was one */
|
2007-10-03 22:23:01 +08:00
|
|
|
kthread_stop(ca->thread);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
for (i = 0; i < ca->slot_count; i++) {
|
|
|
|
dvb_ca_en50221_slot_shutdown(ca, i);
|
|
|
|
}
|
2016-08-10 05:32:16 +08:00
|
|
|
dvb_remove_device(ca->dvbdev);
|
[media] dvb-core/en50221: use kref to manage struct dvb_ca_private
Don't free the object until the file handle has been closed. Fixes
use-after-free bug which occurs when I disconnect my DVB-S received
while VDR is running.
This is a crash dump of such a use-after-free:
general protection fault: 0000 [#1] SMP
CPU: 0 PID: 2541 Comm: CI adapter on d Not tainted 4.7.0-rc1-hosting+ #49
Hardware name: Bochs Bochs, BIOS Bochs 01/01/2011
task: ffff880027d7ce00 ti: ffff88003d8f8000 task.ti: ffff88003d8f8000
RIP: 0010:[<ffffffff812f3d1f>] [<ffffffff812f3d1f>] dvb_ca_en50221_io_read_condition.isra.7+0x6f/0x150
RSP: 0018:ffff88003d8fba98 EFLAGS: 00010206
RAX: 0000000059534255 RBX: 000000753d470f90 RCX: ffff88003c74d181
RDX: 00000001bea04ba9 RSI: ffff88003d8fbaf4 RDI: 3a3030a56d763fc0
RBP: ffff88003d8fbae0 R08: ffff88003c74d180 R09: 0000000000000000
R10: 0000000000000001 R11: 0000000000000000 R12: ffff88003c480e00
R13: 00000000ffffffff R14: 0000000059534255 R15: 0000000000000000
FS: 00007fb4209b4700(0000) GS:ffff88003fc00000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007f06445f4078 CR3: 000000003c55b000 CR4: 00000000000006b0
Stack:
ffff88003d8fbaf4 000000003c2170c0 0000000000004000 0000000000000000
ffff88003c480e00 ffff88003d8fbc80 ffff88003c74d180 ffff88003d8fbb8c
0000000000000000 ffff88003d8fbb10 ffffffff812f3e37 ffff88003d8fbb00
Call Trace:
[<ffffffff812f3e37>] dvb_ca_en50221_io_poll+0x37/0xa0
[<ffffffff8113109b>] do_sys_poll+0x2db/0x520
This is a backtrace of the kernel attempting to lock a freed mutex:
#0 0xffffffff81083d40 in rep_nop () at ./arch/x86/include/asm/processor.h:569
#1 cpu_relax () at ./arch/x86/include/asm/processor.h:574
#2 virt_spin_lock (lock=<optimized out>) at ./arch/x86/include/asm/qspinlock.h:57
#3 native_queued_spin_lock_slowpath (lock=0xffff88003c480e90, val=761492029) at kernel/locking/qspinlock.c:304
#4 0xffffffff810d1a06 in pv_queued_spin_lock_slowpath (val=<optimized out>, lock=<optimized out>) at ./arch/x86/include/asm/paravirt.h:669
#5 queued_spin_lock_slowpath (val=<optimized out>, lock=<optimized out>) at ./arch/x86/include/asm/qspinlock.h:28
#6 queued_spin_lock (lock=<optimized out>) at include/asm-generic/qspinlock.h:107
#7 __mutex_lock_common (use_ww_ctx=<optimized out>, ww_ctx=<optimized out>, ip=<optimized out>, nest_lock=<optimized out>, subclass=<optimized out>,
state=<optimized out>, lock=<optimized out>) at kernel/locking/mutex.c:526
#8 mutex_lock_interruptible_nested (lock=0xffff88003c480e88, subclass=<optimized out>) at kernel/locking/mutex.c:647
#9 0xffffffff812f49fe in dvb_ca_en50221_io_do_ioctl (file=<optimized out>, cmd=761492029, parg=0x1 <irq_stack_union+1>)
at drivers/media/dvb-core/dvb_ca_en50221.c:1210
#10 0xffffffff812ee660 in dvb_usercopy (file=<optimized out>, cmd=761492029, arg=<optimized out>, func=<optimized out>) at drivers/media/dvb-core/dvbdev.c:883
#11 0xffffffff812f3410 in dvb_ca_en50221_io_ioctl (file=<optimized out>, cmd=<optimized out>, arg=<optimized out>) at drivers/media/dvb-core/dvb_ca_en50221.c:1284
#12 0xffffffff8112eddd in vfs_ioctl (arg=<optimized out>, cmd=<optimized out>, filp=<optimized out>) at fs/ioctl.c:43
#13 do_vfs_ioctl (filp=0xffff88003c480e90, fd=<optimized out>, cmd=<optimized out>, arg=<optimized out>) at fs/ioctl.c:674
#14 0xffffffff8112f30c in SYSC_ioctl (arg=<optimized out>, cmd=<optimized out>, fd=<optimized out>) at fs/ioctl.c:689
#15 SyS_ioctl (fd=6, cmd=2148298626, arg=140734533693696) at fs/ioctl.c:680
#16 0xffffffff8103feb2 in entry_SYSCALL_64 () at arch/x86/entry/entry_64.S:207
Signed-off-by: Max Kellermann <max@duempel.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
2016-07-04 20:08:45 +08:00
|
|
|
dvb_ca_private_put(ca);
|
2005-04-17 06:20:36 +08:00
|
|
|
pubca->private = NULL;
|
|
|
|
}
|