Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
linux
Commits
7679ba7b
Commit
7679ba7b
authored
Apr 25, 2004
by
Michael Hunold
Committed by
Linus Torvalds
Apr 25, 2004
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[PATCH] DVB: Add EN50221 cam support to dvb-core
- [DVB] add generic functions for EN50221 CAM interfaces
parent
2b7f4a04
Changes
6
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
1931 additions
and
5 deletions
+1931
-5
drivers/media/dvb/dvb-core/Makefile
drivers/media/dvb/dvb-core/Makefile
+2
-1
drivers/media/dvb/dvb-core/dvb_ca_en50221.c
drivers/media/dvb/dvb-core/dvb_ca_en50221.c
+1628
-0
drivers/media/dvb/dvb-core/dvb_ca_en50221.h
drivers/media/dvb/dvb-core/dvb_ca_en50221.h
+128
-0
drivers/media/dvb/dvb-core/dvb_ksyms.c
drivers/media/dvb/dvb-core/dvb_ksyms.c
+6
-0
drivers/media/dvb/dvb-core/dvb_ringbuffer.c
drivers/media/dvb/dvb-core/dvb_ringbuffer.c
+114
-2
drivers/media/dvb/dvb-core/dvb_ringbuffer.h
drivers/media/dvb/dvb-core/dvb_ringbuffer.h
+53
-2
No files found.
drivers/media/dvb/dvb-core/Makefile
View file @
7679ba7b
...
...
@@ -3,6 +3,7 @@
#
dvb-core-objs
=
dvbdev.o dmxdev.o dvb_demux.o dvb_filter.o
\
dvb_functions.o dvb_frontend.o dvb_i2c.o dvb_net.o dvb_ksyms.o dvb_ringbuffer.o
dvb_ca_en50221.o dvb_functions.o dvb_frontend.o
\
dvb_i2c.o dvb_net.o dvb_ksyms.o dvb_ringbuffer.o
obj-$(CONFIG_DVB_CORE)
+=
dvb-core.o
drivers/media/dvb/dvb-core/dvb_ca_en50221.c
0 → 100644
View file @
7679ba7b
/*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
* Or, point your browser to http://www.gnu.org/copyleft/gpl.html
*/
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/vmalloc.h>
#include <linux/delay.h>
#include <asm/semaphore.h>
#include <asm/atomic.h>
#include "dvb_ca_en50221.h"
#include "dvb_functions.h"
#include "dvb_ringbuffer.h"
static
int
dvb_ca_en50221_debug
=
0
;
#define dprintk if (dvb_ca_en50221_debug) printk
#define INIT_TIMEOUT_SECS 5
#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
;
/* 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
;
/* semaphore for syncing access to slot structure */
struct
semaphore
sem
;
/* 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
{
/* 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 */
pid_t
thread_pid
;
/* Wait queue used when shutting thread down */
wait_queue_head_t
thread_queue
;
/* Flag indicating when thread should exit */
int
exit
:
1
;
/* Flag indicating if the CA device is open */
int
open
:
1
;
/* Flag indicating the thread should wake up now */
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
;
};
static
void
dvb_ca_en50221_thread_wakeup
(
struct
dvb_ca_private
*
ca
);
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
);
/**
* Safely find needle in haystack.
*
* @param haystack Buffer to look in.
* @param hlen Number of bytes in haystack.
* @param needle Buffer to find.
* @param nlen Number of bytes in needle.
* @return Pointer into haystack needle was found at, or NULL if not found.
*/
static
u8
*
findstr
(
u8
*
haystack
,
int
hlen
,
u8
*
needle
,
int
nlen
)
{
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 */
/**
* Check CAM status.
*/
static
int
dvb_ca_en50221_check_camstatus
(
struct
dvb_ca_private
*
ca
,
int
slot
)
{
int
slot_status
;
int
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 */
if
((
status
=
down_interruptible
(
&
ca
->
slot_info
[
slot
].
sem
))
!=
0
)
return
status
;
slot_status
=
ca
->
pub
->
poll_slot_status
(
ca
->
pub
,
slot
);
up
(
&
ca
->
slot_info
[
slot
].
sem
);
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
;
}
/**
* Wait for flags to become set on the STATUS register on a CAM interface,
* checking for errors and timeout.
*
* @param ca CA instance.
* @param slot Slot on interface.
* @param waitfor Flags to wait for.
* @param timeout_ms Timeout in milliseconds.
*
* @return 0 on success, nonzero on error.
*/
static
int
dvb_ca_en50221_wait_if_status
(
struct
dvb_ca_private
*
ca
,
int
slot
,
u8
waitfor
,
int
timeout_hz
)
{
unsigned
long
timeout
;
unsigned
long
start
;
dprintk
(
"%s
\n
"
,
__FUNCTION__
);
/* 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
)
{
dprintk
(
"%s succeeded timeout:%lu
\n
"
,
__FUNCTION__
,
jiffies
-
start
);
return
0
;
}
/* check for timeout */
if
(
time_after
(
jiffies
,
timeout
))
{
break
;
}
/* wait for a bit */
dvb_delay
(
1
);
}
dprintk
(
"%s failed timeout:%lu
\n
"
,
__FUNCTION__
,
jiffies
-
start
);
/* if we get here, we've timed out */
return
-
ETIMEDOUT
;
}
/**
* Initialise the link layer connection to a CAM.
*
* @param ca CA instance.
* @param slot Slot id.
*
* @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
];
dprintk
(
"%s
\n
"
,
__FUNCTION__
);
/* we'll be determining these during this function */
ca
->
slot_info
[
slot
].
da_irq_supported
=
0
;
/* reset the link interface. Note CAM IRQs are disabled */
if
((
ret
=
ca
->
pub
->
write_cam_control
(
ca
->
pub
,
slot
,
CTRLIF_COMMAND
,
CMDREG_RS
))
!=
0
)
return
ret
;
if
((
ret
=
dvb_ca_en50221_wait_if_status
(
ca
,
slot
,
STATUSREG_FR
,
HZ
/
10
))
!=
0
)
return
ret
;
/* set the host link buffer size temporarily. it will be overwritten with the
* real negotiated size later. */
ca
->
slot_info
[
slot
].
link_buf_size
=
2
;
/* read the buffer size from the CAM */
if
((
ret
=
ca
->
pub
->
write_cam_control
(
ca
->
pub
,
slot
,
CTRLIF_COMMAND
,
IRQEN
|
CMDREG_SR
))
!=
0
)
return
ret
;
if
((
ret
=
dvb_ca_en50221_wait_if_status
(
ca
,
slot
,
STATUSREG_DA
,
HZ
/
10
))
!=
0
)
return
ret
;
if
((
ret
=
dvb_ca_en50221_read_data
(
ca
,
slot
,
buf
,
2
))
!=
2
)
return
-
EIO
;
if
((
ret
=
ca
->
pub
->
write_cam_control
(
ca
->
pub
,
slot
,
CTRLIF_COMMAND
,
IRQEN
))
!=
0
)
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 */
if
((
ret
=
ca
->
pub
->
write_cam_control
(
ca
->
pub
,
slot
,
CTRLIF_COMMAND
,
IRQEN
|
CMDREG_SW
))
!=
0
)
return
ret
;
if
((
ret
=
dvb_ca_en50221_wait_if_status
(
ca
,
slot
,
STATUSREG_FR
,
HZ
/
10
))
!=
0
)
return
ret
;
if
((
ret
=
dvb_ca_en50221_write_data
(
ca
,
slot
,
buf
,
2
))
!=
2
)
return
-
EIO
;
if
((
ret
=
ca
->
pub
->
write_cam_control
(
ca
->
pub
,
slot
,
CTRLIF_COMMAND
,
IRQEN
))
!=
0
)
return
ret
;
/* success */
return
0
;
}
/**
* Read a tuple from attribute memory.
*
* @param ca CA instance.
* @param slot Slot id.
* @param address Address to read from. Updated.
* @param tupleType Tuple id byte. Updated.
* @param tupleLength Tuple length. Updated.
* @param tuple Dest buffer for tuple (must be 256 bytes). Updated.
*
* @return 0 on success, nonzero on error.
*/
static
int
dvb_ca_en50221_read_tuple
(
struct
dvb_ca_private
*
ca
,
int
slot
,
int
*
address
,
int
*
tupleType
,
int
*
tupleLength
,
u8
*
tuple
)
{
int
i
;
int
_tupleType
;
int
_tupleLength
;
int
_address
=
*
address
;
/* grab the next tuple length and type */
if
((
_tupleType
=
ca
->
pub
->
read_attribute_mem
(
ca
->
pub
,
slot
,
_address
))
<
0
)
return
_tupleType
;
if
((
_tupleLength
=
ca
->
pub
->
read_attribute_mem
(
ca
->
pub
,
slot
,
_address
+
2
))
<
0
)
return
_tupleLength
;
_address
+=
4
;
dprintk
(
"TUPLE type:0x%x length:%i
\n
"
,
_tupleType
,
_tupleLength
);
/* read in the whole tuple */
for
(
i
=
0
;
i
<
_tupleLength
;
i
++
)
{
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
]
:
'.'
);
}
_address
+=
(
_tupleLength
*
2
);
// success
*
tupleType
=
_tupleType
;
*
tupleLength
=
_tupleLength
;
*
address
=
_address
;
return
0
;
}
/**
* Parse attribute memory of a CAM module, extracting Config register, and checking
* it is a DVB CAM module.
*
* @param ca CA instance.
* @param slot Slot id.
*
* @return 0 on success, <0 on failure.
*/
static
int
dvb_ca_en50221_parse_attributes
(
struct
dvb_ca_private
*
ca
,
int
slot
)
{
int
address
=
0
;
int
tupleLength
;
int
tupleType
;
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
if
((
status
=
dvb_ca_en50221_read_tuple
(
ca
,
slot
,
&
address
,
&
tupleType
,
&
tupleLength
,
tuple
))
<
0
)
return
status
;
if
(
tupleType
!=
0x1D
)
return
-
EINVAL
;
// CISTPL_DEVICE_0C
if
((
status
=
dvb_ca_en50221_read_tuple
(
ca
,
slot
,
&
address
,
&
tupleType
,
&
tupleLength
,
tuple
))
<
0
)
return
status
;
if
(
tupleType
!=
0x1C
)
return
-
EINVAL
;
// CISTPL_VERS_1
if
((
status
=
dvb_ca_en50221_read_tuple
(
ca
,
slot
,
&
address
,
&
tupleType
,
&
tupleLength
,
tuple
))
<
0
)
return
status
;
if
(
tupleType
!=
0x15
)
return
-
EINVAL
;
// CISTPL_MANFID
if
((
status
=
dvb_ca_en50221_read_tuple
(
ca
,
slot
,
&
address
,
&
tupleType
,
&
tupleLength
,
tuple
))
<
0
)
return
status
;
if
(
tupleType
!=
0x20
)
return
-
EINVAL
;
if
(
tupleLength
!=
4
)
return
-
EINVAL
;
manfid
=
(
tuple
[
1
]
<<
8
)
|
tuple
[
0
];
devid
=
(
tuple
[
3
]
<<
8
)
|
tuple
[
2
];
// CISTPL_CONFIG
if
((
status
=
dvb_ca_en50221_read_tuple
(
ca
,
slot
,
&
address
,
&
tupleType
,
&
tupleLength
,
tuple
))
<
0
)
return
status
;
if
(
tupleType
!=
0x1A
)
return
-
EINVAL
;
if
(
tupleLength
<
3
)
return
-
EINVAL
;
/* extract the configbase */
rasz
=
tuple
[
0
]
&
3
;
if
(
tupleLength
<
(
3
+
rasz
+
14
))
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 */
dvb_str
=
findstr
(
tuple
,
tupleLength
,
"DVB_CI_V"
,
8
);
if
(
dvb_str
==
NULL
)
return
-
EINVAL
;
if
(
tupleLength
<
((
dvb_str
-
(
char
*
)
tuple
)
+
12
))
return
-
EINVAL
;
/* is it a version we support? */
if
(
strncmp
(
dvb_str
+
8
,
"1.00"
,
4
))
{
printk
(
"dvb_ca: Unsupported DVB CAM module version %c%c%c%c
\n
"
,
dvb_str
[
8
],
dvb_str
[
9
],
dvb_str
[
10
],
dvb_str
[
11
]);
return
-
EINVAL
;
}
/* process the CFTABLE_ENTRY tuples, and any after those */
while
((
!
end_chain
)
&&
(
address
<
0x1000
))
{
if
((
status
=
dvb_ca_en50221_read_tuple
(
ca
,
slot
,
&
address
,
&
tupleType
,
&
tupleLength
,
tuple
))
<
0
)
return
status
;
switch
(
tupleType
)
{
case
0x1B
:
// CISTPL_CFTABLE_ENTRY
if
(
tupleLength
<
(
2
+
11
+
17
))
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 */
if
((
findstr
(
tuple
,
tupleLength
,
"DVB_HOST"
,
8
)
==
NULL
)
||
(
findstr
(
tuple
,
tupleLength
,
"DVB_CI_MODULE"
,
13
)
==
NULL
))
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 */
dprintk
(
"dvb_ca: Skipping unknown tuple type:0x%x length:0x%x
\n
"
,
tupleType
,
tupleLength
);
break
;
}
}
if
((
address
>
0x1000
)
||
(
!
got_cftableentry
))
return
-
EINVAL
;
dprintk
(
"Valid DVB CAM detected MANID:%x DEVID:%x CONFIGBASE:0x%x CONFIGOPTION:0x%x
\n
"
,
manfid
,
devid
,
ca
->
slot_info
[
slot
].
config_base
,
ca
->
slot_info
[
slot
].
config_option
);
// success!
return
0
;
}
/**
* Set CAM's configoption correctly.
*
* @param ca CA instance.
* @param slot Slot containing the CAM.
*/
static
int
dvb_ca_en50221_set_configoption
(
struct
dvb_ca_private
*
ca
,
int
slot
)
{
int
configoption
;
dprintk
(
"%s
\n
"
,
__FUNCTION__
);
/* 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
;
}
/**
* 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.
*
* @param ca CA instance.
* @param slot Slot to read from.
* @param ebuf If non-NULL, the data will be written to this buffer. If NULL,
* the data will be added into the buffering system as a normal fragment.
* @param ecount Size of ebuf. Ignored if ebuf is NULL.
*
* @return Number of bytes read, or < 0 on error
*/
static
int
dvb_ca_en50221_read_data
(
struct
dvb_ca_private
*
ca
,
int
slot
,
u8
*
ebuf
,
int
ecount
)
{
int
bytes_read
;
int
status
;
u8
buf
[
HOST_LINK_BUF_SIZE
];
int
i
;
dprintk
(
"%s
\n
"
,
__FUNCTION__
);
/* acquire the slot */
if
((
status
=
down_interruptible
(
&
ca
->
slot_info
[
slot
].
sem
))
!=
0
)
return
status
;
/* check if we have space for a link buf in the rx_buffer */
if
(
ebuf
==
NULL
)
{
if
(
dvb_ringbuffer_free
(
&
ca
->
slot_info
[
slot
].
rx_buffer
)
<
(
ca
->
slot_info
[
slot
].
link_buf_size
+
DVB_RINGBUFFER_PKTHDRSIZE
))
{
status
=
-
EAGAIN
;
goto
exit
;
}
}
/* reset the interface if there's been a tx error */
if
((
status
=
ca
->
pub
->
read_cam_control
(
ca
->
pub
,
slot
,
CTRLIF_STATUS
))
<
0
)
goto
exit
;
if
(
status
&
STATUSREG_TXERR
)
{
ca
->
slot_info
[
slot
].
slot_state
=
DVB_CA_SLOTSTATE_LINKINIT
;
status
=
-
EIO
;
goto
exit
;
}
if
(
!
(
status
&
STATUSREG_DA
))
{
/* no data */
status
=
0
;
goto
exit
;
}
/* read the amount of data */
if
((
status
=
ca
->
pub
->
read_cam_control
(
ca
->
pub
,
slot
,
CTRLIF_SIZE_HIGH
))
<
0
)
goto
exit
;
bytes_read
=
status
<<
8
;
if
((
status
=
ca
->
pub
->
read_cam_control
(
ca
->
pub
,
slot
,
CTRLIF_SIZE_LOW
))
<
0
)
goto
exit
;
bytes_read
|=
status
;
/* check it will fit */
if
(
ebuf
==
NULL
)
{
if
(
bytes_read
>
ca
->
slot_info
[
slot
].
link_buf_size
)
{
printk
(
"dvb_ca: CAM tried to send a buffer larger than the link buffer size!
\n
"
);
ca
->
slot_info
[
slot
].
slot_state
=
DVB_CA_SLOTSTATE_LINKINIT
;
status
=
-
EIO
;
goto
exit
;
}
if
(
bytes_read
<
2
)
{
printk
(
"dvb_ca: CAM sent a buffer that was less than 2 bytes!
\n
"
);
ca
->
slot_info
[
slot
].
slot_state
=
DVB_CA_SLOTSTATE_LINKINIT
;
status
=
-
EIO
;
goto
exit
;
}
}
else
{
if
(
bytes_read
>
ecount
)
{
printk
(
"dvb_ca: CAM tried to send a buffer larger than the ecount size!
\n
"
);
status
=
-
EIO
;
goto
exit
;
}
}
/* fill the buffer */
for
(
i
=
0
;
i
<
bytes_read
;
i
++
)
{
/* read byte and check */
if
((
status
=
ca
->
pub
->
read_cam_control
(
ca
->
pub
,
slot
,
CTRLIF_DATA
))
<
0
)
goto
exit
;
/* OK, store it in the buffer */
buf
[
i
]
=
status
;
}
/* check for read error (RE should now go to 0) */
if
((
status
=
ca
->
pub
->
read_cam_control
(
ca
->
pub
,
slot
,
CTRLIF_STATUS
))
<
0
)
goto
exit
;
if
(
status
&
STATUSREG_RE
)
{
status
=
-
EIO
;
goto
exit
;
}
/* OK, add it to the receive buffer, or copy into external buffer if supplied */
if
(
ebuf
==
NULL
)
{
dvb_ringbuffer_pkt_write
(
&
ca
->
slot_info
[
slot
].
rx_buffer
,
buf
,
bytes_read
,
0
);
}
else
{
memcpy
(
ebuf
,
buf
,
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:
up
(
&
ca
->
slot_info
[
slot
].
sem
);
return
status
;
}
/**
* This function talks to an EN50221 CAM control interface. It writes a buffer of data
* to a CAM.
*
* @param ca CA instance.
* @param slot Slot to write to.
* @param ebuf The data in this buffer is treated as a complete link-level packet to
* be written.
* @param count Size of ebuf.
*
* @return Number of bytes written, or < 0 on error.
*/
static
int
dvb_ca_en50221_write_data
(
struct
dvb_ca_private
*
ca
,
int
slot
,
u8
*
buf
,
int
bytes_write
)
{
int
status
;
int
i
;
dprintk
(
"%s
\n
"
,
__FUNCTION__
);
// sanity check
if
(
bytes_write
>
ca
->
slot_info
[
slot
].
link_buf_size
)
return
-
EINVAL
;
/* acquire the slot */
if
((
status
=
down_interruptible
(
&
ca
->
slot_info
[
slot
].
sem
))
!=
0
)
return
status
;
/* reset the interface if there's been a tx error */
if
((
status
=
ca
->
pub
->
read_cam_control
(
ca
->
pub
,
slot
,
CTRLIF_STATUS
))
<
0
)
goto
exitnowrite
;
if
(
status
&
STATUSREG_TXERR
)
{
ca
->
slot_info
[
slot
].
slot_state
=
DVB_CA_SLOTSTATE_LINKINIT
;
status
=
-
EIO
;
goto
exitnowrite
;
}
/* check if interface is actually waiting for us to read from it */
if
(
status
&
STATUSREG_DA
)
{
status
=
-
EAGAIN
;
goto
exitnowrite
;
}
/* OK, set HC bit */
if
((
status
=
ca
->
pub
->
write_cam_control
(
ca
->
pub
,
slot
,
CTRLIF_COMMAND
,
IRQEN
|
CMDREG_HC
))
!=
0
)
goto
exit
;
/* check if interface is still free */
if
((
status
=
ca
->
pub
->
read_cam_control
(
ca
->
pub
,
slot
,
CTRLIF_STATUS
))
<
0
)
goto
exit
;
if
(
!
(
status
&
STATUSREG_FR
))
{
/* it wasn't free => try again later */
status
=
-
EAGAIN
;
goto
exit
;
}
/* send the amount of data */
if
((
status
=
ca
->
pub
->
write_cam_control
(
ca
->
pub
,
slot
,
CTRLIF_SIZE_HIGH
,
bytes_write
>>
8
))
!=
0
)
goto
exit
;
if
((
status
=
ca
->
pub
->
write_cam_control
(
ca
->
pub
,
slot
,
CTRLIF_SIZE_LOW
,
bytes_write
&
0xff
))
!=
0
)
goto
exit
;
/* send the buffer */
for
(
i
=
0
;
i
<
bytes_write
;
i
++
)
{
if
((
status
=
ca
->
pub
->
write_cam_control
(
ca
->
pub
,
slot
,
CTRLIF_DATA
,
buf
[
i
]))
!=
0
)
goto
exit
;
}
/* check for write error (WE should now be 0) */
if
((
status
=
ca
->
pub
->
read_cam_control
(
ca
->
pub
,
slot
,
CTRLIF_STATUS
))
<
0
)
goto
exit
;
if
(
status
&
STATUSREG_WE
)
{
status
=
-
EIO
;
goto
exit
;
}
status
=
bytes_write
;
exit:
ca
->
pub
->
write_cam_control
(
ca
->
pub
,
slot
,
CTRLIF_COMMAND
,
IRQEN
);
exitnowrite:
up
(
&
ca
->
slot_info
[
slot
].
sem
);
return
status
;
}
/* ******************************************************************************** */
/* EN50221 higher level functions */
/**
* A CAM has been removed => shut it down.
*
* @param ca CA instance.
* @param slot Slot to shut down.
*/
static
int
dvb_ca_en50221_slot_shutdown
(
struct
dvb_ca_private
*
ca
,
int
slot
)
{
int
status
;
dprintk
(
"%s
\n
"
,
__FUNCTION__
);
if
((
status
=
down_interruptible
(
&
ca
->
slot_info
[
slot
].
sem
))
!=
0
)
return
status
;
ca
->
pub
->
slot_shutdown
(
ca
->
pub
,
slot
);
ca
->
slot_info
[
slot
].
slot_state
=
DVB_CA_SLOTSTATE_NONE
;
if
(
ca
->
slot_info
[
slot
].
rx_buffer
.
data
)
vfree
(
ca
->
slot_info
[
slot
].
rx_buffer
.
data
);
ca
->
slot_info
[
slot
].
rx_buffer
.
data
=
NULL
;
up
(
&
ca
->
slot_info
[
slot
].
sem
);
/* need to wake up all processes to check if they're now
trying to write to a defunct CAM */
wake_up_interruptible
(
&
ca
->
wait_queue
);
dprintk
(
"Slot %i shutdown
\n
"
,
slot
);
/* success */
return
0
;
}
/**
* A CAMCHANGE IRQ has occurred.
*
* @param ca CA instance.
* @param slot Slot concerned.
* @param change_type One of the DVB_CA_CAMCHANGE_* values.
*/
void
dvb_ca_en50221_camchange_irq
(
struct
dvb_ca_en50221
*
pubca
,
int
slot
,
int
change_type
)
{
struct
dvb_ca_private
*
ca
=
(
struct
dvb_ca_private
*
)
pubca
->
private
;
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
);
}
/**
* A CAMREADY IRQ has occurred.
*
* @param ca CA instance.
* @param slot Slot concerned.
*/
void
dvb_ca_en50221_camready_irq
(
struct
dvb_ca_en50221
*
pubca
,
int
slot
)
{
struct
dvb_ca_private
*
ca
=
(
struct
dvb_ca_private
*
)
pubca
->
private
;
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
);
}
}
/**
* An FR or DA IRQ has occurred.
*
* @param ca CA instance.
* @param slot Slot concerned.
*/
void
dvb_ca_en50221_frda_irq
(
struct
dvb_ca_en50221
*
pubca
,
int
slot
)
{
struct
dvb_ca_private
*
ca
=
(
struct
dvb_ca_private
*
)
pubca
->
private
;
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
:
flags
=
ca
->
pub
->
read_cam_control
(
pubca
,
slot
,
CTRLIF_STATUS
);
if
(
flags
&
STATUSREG_DA
)
{
dvb_ca_en50221_thread_wakeup
(
ca
);
}
break
;
}
}
/* ******************************************************************************** */
/* EN50221 thread functions */
/**
* Wake up the DVB CA thread
*
* @param ca CA instance.
*/
static
void
dvb_ca_en50221_thread_wakeup
(
struct
dvb_ca_private
*
ca
)
{
dprintk
(
"%s
\n
"
,
__FUNCTION__
);
ca
->
wakeup
=
1
;
mb
();
wake_up_interruptible
(
&
ca
->
thread_queue
);
}
/**
* Used by the CA thread to determine if an early wakeup is necessary
*
* @param ca CA instance.
*/
static
int
dvb_ca_en50221_thread_should_wakeup
(
struct
dvb_ca_private
*
ca
)
{
if
(
ca
->
wakeup
)
{
ca
->
wakeup
=
0
;
return
1
;
}
if
(
ca
->
exit
)
return
1
;
return
0
;
}
/**
* Update the delay used by the thread.
*
* @param ca CA instance.
*/
static
void
dvb_ca_en50221_thread_update_delay
(
struct
dvb_ca_private
*
ca
)
{
int
delay
;
int
curdelay
=
100000000
;
int
slot
;
for
(
slot
=
0
;
slot
<
ca
->
slot_count
;
slot
++
)
{
switch
(
ca
->
slot_info
[
slot
].
slot_state
)
{
default:
case
DVB_CA_SLOTSTATE_NONE
:
case
DVB_CA_SLOTSTATE_INVALID
:
delay
=
HZ
*
60
;
if
(
!
(
ca
->
flags
&
DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE
))
{
delay
=
HZ
/
10
;
}
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
:
delay
=
HZ
/
10
;
break
;
case
DVB_CA_SLOTSTATE_RUNNING
:
delay
=
HZ
*
60
;
if
(
!
(
ca
->
flags
&
DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE
))
{
delay
=
HZ
/
10
;
}
if
(
ca
->
open
)
{
if
((
!
ca
->
slot_info
[
slot
].
da_irq_supported
)
||
(
!
(
ca
->
flags
&
DVB_CA_EN50221_FLAG_IRQ_DA
)))
{
delay
=
HZ
/
100
;
}
}
break
;
}
if
(
delay
<
curdelay
)
curdelay
=
delay
;
}
ca
->
delay
=
curdelay
;
}
/**
* 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
=
(
struct
dvb_ca_private
*
)
data
;
char
name
[
15
];
int
slot
;
int
flags
;
int
pktcount
;
void
*
rxbuf
;
dprintk
(
"%s
\n
"
,
__FUNCTION__
);
/* setup kernel thread */
snprintf
(
name
,
sizeof
(
name
),
"kdvb-ca-%i:%i"
,
ca
->
dvbdev
->
adapter
->
num
,
ca
->
dvbdev
->
id
);
dvb_kernel_thread_setup
(
name
);
/* choose the correct initial delay */
dvb_ca_en50221_thread_update_delay
(
ca
);
/* main loop */
while
(
!
ca
->
exit
)
{
/* sleep for a bit */
if
(
!
ca
->
wakeup
)
{
flags
=
wait_event_interruptible_timeout
(
ca
->
thread_queue
,
dvb_ca_en50221_thread_should_wakeup
(
ca
),
ca
->
delay
);
if
((
flags
==
-
ERESTARTSYS
)
||
ca
->
exit
)
{
/* got signal or quitting */
break
;
}
}
ca
->
wakeup
=
0
;
/* go through all the slots processing them */
for
(
slot
=
0
;
slot
<
ca
->
slot_count
;
slot
++
)
{
// check the cam status + deal with CAMCHANGEs
while
(
dvb_ca_en50221_check_camstatus
(
ca
,
slot
))
{
/* clear down an old CI slot if necessary */
if
(
ca
->
slot_info
[
slot
].
slot_state
!=
DVB_CA_SLOTSTATE_NONE
)
dvb_ca_en50221_slot_shutdown
(
ca
,
slot
);
/* if a CAM is NOW present, initialise it */
if
(
ca
->
slot_info
[
slot
].
camchange_type
==
DVB_CA_EN50221_CAMCHANGE_INSERTED
)
{
ca
->
slot_info
[
slot
].
slot_state
=
DVB_CA_SLOTSTATE_UNINITIALISED
;
}
/* we've handled one CAMCHANGE */
dvb_ca_en50221_thread_update_delay
(
ca
);
atomic_dec
(
&
ca
->
slot_info
[
slot
].
camchange_count
);
}
// CAM state machine
switch
(
ca
->
slot_info
[
slot
].
slot_state
)
{
case
DVB_CA_SLOTSTATE_NONE
:
case
DVB_CA_SLOTSTATE_INVALID
:
// no action needed
break
;
case
DVB_CA_SLOTSTATE_UNINITIALISED
:
ca
->
slot_info
[
slot
].
slot_state
=
DVB_CA_SLOTSTATE_WAITREADY
;
ca
->
pub
->
slot_reset
(
ca
->
pub
,
slot
);
ca
->
slot_info
[
slot
].
timeout
=
jiffies
+
(
INIT_TIMEOUT_SECS
*
HZ
);
break
;
case
DVB_CA_SLOTSTATE_WAITREADY
:
if
(
time_after
(
jiffies
,
ca
->
slot_info
[
slot
].
timeout
))
{
printk
(
"dvb_ca: PC card did not respond :(
\n
"
);
ca
->
slot_info
[
slot
].
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
;
case
DVB_CA_SLOTSTATE_VALIDATE
:
if
(
dvb_ca_en50221_parse_attributes
(
ca
,
slot
)
!=
0
)
{
printk
(
"dvb_ca: Invalid PC card inserted :(
\n
"
);
ca
->
slot_info
[
slot
].
slot_state
=
DVB_CA_SLOTSTATE_INVALID
;
dvb_ca_en50221_thread_update_delay
(
ca
);
break
;
}
if
(
dvb_ca_en50221_set_configoption
(
ca
,
slot
)
!=
0
)
{
printk
(
"dvb_ca: Unable to initialise CAM :(
\n
"
);
ca
->
slot_info
[
slot
].
slot_state
=
DVB_CA_SLOTSTATE_INVALID
;
dvb_ca_en50221_thread_update_delay
(
ca
);
break
;
}
dprintk
(
"DVB CAM validated successfully
\n
"
);
ca
->
slot_info
[
slot
].
timeout
=
jiffies
+
(
INIT_TIMEOUT_SECS
*
HZ
);
ca
->
slot_info
[
slot
].
slot_state
=
DVB_CA_SLOTSTATE_WAITFR
;
ca
->
wakeup
=
1
;
break
;
case
DVB_CA_SLOTSTATE_WAITFR
:
if
(
time_after
(
jiffies
,
ca
->
slot_info
[
slot
].
timeout
))
{
printk
(
"dvb_ca: DVB CAM did not respond :(
\n
"
);
ca
->
slot_info
[
slot
].
slot_state
=
DVB_CA_SLOTSTATE_INVALID
;
dvb_ca_en50221_thread_update_delay
(
ca
);
break
;
}
flags
=
ca
->
pub
->
read_cam_control
(
ca
->
pub
,
slot
,
CTRLIF_STATUS
);
if
(
flags
&
STATUSREG_FR
)
{
ca
->
slot_info
[
slot
].
slot_state
=
DVB_CA_SLOTSTATE_LINKINIT
;
ca
->
wakeup
=
1
;
}
break
;
case
DVB_CA_SLOTSTATE_LINKINIT
:
if
(
dvb_ca_en50221_link_init
(
ca
,
slot
)
!=
0
)
{
printk
(
"dvb_ca: DVB CAM link initialisation failed :(
\n
"
);
ca
->
slot_info
[
slot
].
slot_state
=
DVB_CA_SLOTSTATE_INVALID
;
dvb_ca_en50221_thread_update_delay
(
ca
);
break
;
}
rxbuf
=
vmalloc
(
RX_BUFFER_SIZE
);
if
(
rxbuf
==
NULL
)
{
printk
(
"dvb_ca: Unable to allocate CAM rx buffer :(
\n
"
);
ca
->
slot_info
[
slot
].
slot_state
=
DVB_CA_SLOTSTATE_INVALID
;
dvb_ca_en50221_thread_update_delay
(
ca
);
break
;
}
dvb_ringbuffer_init
(
&
ca
->
slot_info
[
slot
].
rx_buffer
,
rxbuf
,
RX_BUFFER_SIZE
);
ca
->
pub
->
slot_ts_enable
(
ca
->
pub
,
slot
);
ca
->
slot_info
[
slot
].
slot_state
=
DVB_CA_SLOTSTATE_RUNNING
;
dvb_ca_en50221_thread_update_delay
(
ca
);
printk
(
"dvb_ca: DVB CAM detected and initialised successfully
\n
"
);
break
;
case
DVB_CA_SLOTSTATE_RUNNING
:
if
(
!
ca
->
open
)
break
;
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
;
break
;
}
/* 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
;
}
}
break
;
}
}
}
/* completed */
ca
->
thread_pid
=
0
;
mb
();
wake_up_interruptible
(
&
ca
->
thread_queue
);
return
0
;
}
/* ******************************************************************************** */
/* EN50221 IO interface functions */
/**
* Real ioctl implementation.
* NOTE: CA_SEND_MSG/CA_GET_MSG ioctls have userspace buffers passed to them.
*
* @param inode Inode concerned.
* @param file File concerned.
* @param cmd IOCTL command.
* @param arg Associated argument.
*
* @return 0 on success, <0 on error.
*/
static
int
dvb_ca_en50221_io_do_ioctl
(
struct
inode
*
inode
,
struct
file
*
file
,
unsigned
int
cmd
,
void
*
parg
)
{
struct
dvb_device
*
dvbdev
=
(
struct
dvb_device
*
)
file
->
private_data
;
struct
dvb_ca_private
*
ca
=
(
struct
dvb_ca_private
*
)
dvbdev
->
priv
;
int
err
=
0
;
int
slot
;
dprintk
(
"%s
\n
"
,
__FUNCTION__
);
switch
(
cmd
)
{
case
CA_RESET
:
for
(
slot
=
0
;
slot
<
ca
->
slot_count
;
slot
++
)
{
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
);
}
}
ca
->
next_read_slot
=
0
;
dvb_ca_en50221_thread_wakeup
(
ca
);
break
;
case
CA_GET_CAP
:
{
struct
ca_caps
*
caps
=
(
struct
ca_caps
*
)
parg
;
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
:
{
struct
ca_slot_info
*
info
=
(
struct
ca_slot_info
*
)
parg
;
if
((
info
->
num
>
ca
->
slot_count
)
||
(
info
->
num
<
0
))
return
-
EINVAL
;
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
;
}
return
err
;
}
/**
* Wrapper for ioctl implementation.
*
* @param inode Inode concerned.
* @param file File concerned.
* @param cmd IOCTL command.
* @param arg Associated argument.
*
* @return 0 on success, <0 on error.
*/
static
int
dvb_ca_en50221_io_ioctl
(
struct
inode
*
inode
,
struct
file
*
file
,
unsigned
int
cmd
,
unsigned
long
arg
)
{
return
dvb_usercopy
(
inode
,
file
,
cmd
,
arg
,
dvb_ca_en50221_io_do_ioctl
);
}
/**
* Implementation of write() syscall.
*
* @param file File structure.
* @param buf Source buffer.
* @param count Size of source buffer.
* @param ppos Position in file (ignored).
*
* @return Number of bytes read, or <0 on error.
*/
static
ssize_t
dvb_ca_en50221_io_write
(
struct
file
*
file
,
const
char
*
buf
,
size_t
count
,
loff_t
*
ppos
)
{
struct
dvb_device
*
dvbdev
=
(
struct
dvb_device
*
)
file
->
private_data
;
struct
dvb_ca_private
*
ca
=
(
struct
dvb_ca_private
*
)
dvbdev
->
priv
;
u8
slot
,
connection_id
;
int
status
;
char
fragbuf
[
HOST_LINK_BUF_SIZE
];
int
fragpos
=
0
;
int
fraglen
;
unsigned
long
timeout
;
int
written
;
dprintk
(
"%s
\n
"
,
__FUNCTION__
);
/* 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
;
if
((
count
-
fragpos
)
<
fraglen
)
fraglen
=
count
-
fragpos
;
fragbuf
[
0
]
=
connection_id
;
fragbuf
[
1
]
=
((
fragpos
+
fraglen
)
<
count
)
?
0x80
:
0x00
;
if
((
status
=
copy_from_user
(
fragbuf
+
2
,
buf
+
fragpos
,
fraglen
))
!=
0
)
goto
exit
;
timeout
=
jiffies
+
HZ
/
2
;
written
=
0
;
while
(
!
time_after
(
jiffies
,
timeout
))
{
status
=
dvb_ca_en50221_write_data
(
ca
,
slot
,
fragbuf
,
fraglen
+
2
);
if
(
status
==
(
fraglen
+
2
))
{
written
=
1
;
break
;
}
if
(
status
!=
-
EAGAIN
)
goto
exit
;
dvb_delay
(
1
);
}
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
*/
static
int
dvb_ca_en50221_io_read_condition
(
struct
dvb_ca_private
*
ca
,
int
*
result
,
int
*
_slot
)
{
int
slot
;
int
slot_count
=
0
;
int
idx
;
int
fraglen
;
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
((
*
result
=
down_interruptible
(
&
ca
->
slot_info
[
slot
].
sem
))
!=
0
)
return
1
;
idx
=
dvb_ringbuffer_pkt_next
(
&
ca
->
slot_info
[
slot
].
rx_buffer
,
-
1
,
&
fraglen
);
while
(
idx
!=
-
1
)
{
dvb_ringbuffer_pkt_read
(
&
ca
->
slot_info
[
slot
].
rx_buffer
,
idx
,
0
,
hdr
,
2
,
0
);
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
);
}
if
(
!
found
)
up
(
&
ca
->
slot_info
[
slot
].
sem
);
nextslot:
slot
=
(
slot
+
1
)
%
ca
->
slot_count
;
slot_count
++
;
}
ca
->
next_read_slot
=
slot
;
return
found
;
}
/**
* Implementation of read() syscall.
*
* @param file File structure.
* @param buf Destination buffer.
* @param count Size of destination buffer.
* @param ppos Position in file (ignored).
*
* @return Number of bytes read, or <0 on error.
*/
static
ssize_t
dvb_ca_en50221_io_read
(
struct
file
*
file
,
char
*
buf
,
size_t
count
,
loff_t
*
ppos
)
{
struct
dvb_device
*
dvbdev
=
(
struct
dvb_device
*
)
file
->
private_data
;
struct
dvb_ca_private
*
ca
=
(
struct
dvb_ca_private
*
)
dvbdev
->
priv
;
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
;
dprintk
(
"%s
\n
"
,
__FUNCTION__
);
/* Outgoing packet has a 2 byte header. hdr[0] = slot_id, hdr[1] = connection_id */
if
(
count
<
2
)
return
-
EINVAL
;
/* wait for some data */
if
((
status
=
dvb_ca_en50221_io_read_condition
(
ca
,
&
result
,
&
slot
))
==
0
)
{
/* 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
)
{
printk
(
"dvb_ca: BUG: read packet ended before last_fragment encountered
\n
"
);
status
=
-
EIO
;
goto
exit
;
}
dvb_ringbuffer_pkt_read
(
&
ca
->
slot_info
[
slot
].
rx_buffer
,
idx
,
0
,
hdr
,
2
,
0
);
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
;
}
if
((
status
=
dvb_ringbuffer_pkt_read
(
&
ca
->
slot_info
[
slot
].
rx_buffer
,
idx
,
2
,
buf
+
pktlen
,
fraglen
,
1
))
<
0
)
{
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
;
if
((
status
=
copy_to_user
(
buf
,
hdr
,
2
))
!=
0
)
goto
exit
;
status
=
pktlen
;
exit:
up
(
&
ca
->
slot_info
[
slot
].
sem
);
return
status
;
}
/**
* Implementation of file open syscall.
*
* @param inode Inode concerned.
* @param file File concerned.
*
* @return 0 on success, <0 on failure.
*/
static
int
dvb_ca_en50221_io_open
(
struct
inode
*
inode
,
struct
file
*
file
)
{
struct
dvb_device
*
dvbdev
=
(
struct
dvb_device
*
)
file
->
private_data
;
struct
dvb_ca_private
*
ca
=
(
struct
dvb_ca_private
*
)
dvbdev
->
priv
;
int
err
;
int
i
;
dprintk
(
"%s
\n
"
,
__FUNCTION__
);
err
=
dvb_generic_open
(
inode
,
file
);
if
(
err
<
0
)
return
err
;
for
(
i
=
0
;
i
<
ca
->
slot_count
;
i
++
)
{
if
(
ca
->
slot_info
[
i
].
slot_state
==
DVB_CA_SLOTSTATE_RUNNING
)
{
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
);
return
0
;
}
/**
* Implementation of file close syscall.
*
* @param inode Inode concerned.
* @param file File concerned.
*
* @return 0 on success, <0 on failure.
*/
static
int
dvb_ca_en50221_io_release
(
struct
inode
*
inode
,
struct
file
*
file
)
{
struct
dvb_device
*
dvbdev
=
(
struct
dvb_device
*
)
file
->
private_data
;
struct
dvb_ca_private
*
ca
=
(
struct
dvb_ca_private
*
)
dvbdev
->
priv
;
int
err
;
dprintk
(
"%s
\n
"
,
__FUNCTION__
);
/* mark the CA device as closed */
ca
->
open
=
0
;
dvb_ca_en50221_thread_update_delay
(
ca
);
err
=
dvb_generic_release
(
inode
,
file
);
if
(
err
<
0
)
return
err
;
return
0
;
}
/**
* Implementation of poll() syscall.
*
* @param file File concerned.
* @param wait poll wait table.
*
* @return Standard poll mask.
*/
static
unsigned
int
dvb_ca_en50221_io_poll
(
struct
file
*
file
,
poll_table
*
wait
)
{
struct
dvb_device
*
dvbdev
=
(
struct
dvb_device
*
)
file
->
private_data
;
struct
dvb_ca_private
*
ca
=
(
struct
dvb_ca_private
*
)
dvbdev
->
priv
;
unsigned
int
mask
=
0
;
int
slot
;
int
result
=
0
;
dprintk
(
"%s
\n
"
,
__FUNCTION__
);
if
(
dvb_ca_en50221_io_read_condition
(
ca
,
&
result
,
&
slot
)
==
1
)
{
up
(
&
ca
->
slot_info
[
slot
].
sem
);
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
)
{
up
(
&
ca
->
slot_info
[
slot
].
sem
);
mask
|=
POLLIN
;
}
return
mask
;
}
static
struct
file_operations
dvb_ca_fops
=
{
owner:
THIS_MODULE
,
read:
dvb_ca_en50221_io_read
,
write:
dvb_ca_en50221_io_write
,
ioctl:
dvb_ca_en50221_io_ioctl
,
open:
dvb_ca_en50221_io_open
,
release:
dvb_ca_en50221_io_release
,
poll:
dvb_ca_en50221_io_poll
,
};
static
struct
dvb_device
dvbdev_ca
=
{
priv:
0
,
users:
1
,
readers:
1
,
writers:
1
,
fops:
&
dvb_ca_fops
,
};
/* ******************************************************************************** */
/* Initialisation/shutdown functions */
/**
* Initialise a new DVB CA EN50221 interface device.
*
* @param dvb_adapter DVB adapter to attach the new CA device to.
* @param ca The dvb_ca instance.
* @param flags Flags describing the CA device (DVB_CA_FLAG_*).
* @param slot_count Number of slots supported.
*
* @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
;
dprintk
(
"%s
\n
"
,
__FUNCTION__
);
if
(
slot_count
<
1
)
return
-
EINVAL
;
/* initialise the system data */
if
((
ca
=
(
struct
dvb_ca_private
*
)
kmalloc
(
sizeof
(
struct
dvb_ca_private
),
GFP_KERNEL
))
==
NULL
)
{
ret
=
-
ENOMEM
;
goto
error
;
}
memset
(
ca
,
0
,
sizeof
(
struct
dvb_ca_private
));
ca
->
pub
=
pubca
;
ca
->
flags
=
flags
;
ca
->
slot_count
=
slot_count
;
if
((
ca
->
slot_info
=
kmalloc
(
sizeof
(
struct
dvb_ca_slot
)
*
slot_count
,
GFP_KERNEL
))
==
NULL
)
{
ret
=
-
ENOMEM
;
goto
error
;
}
memset
(
ca
->
slot_info
,
0
,
sizeof
(
struct
dvb_ca_slot
)
*
slot_count
);
init_waitqueue_head
(
&
ca
->
wait_queue
);
ca
->
thread_pid
=
0
;
init_waitqueue_head
(
&
ca
->
thread_queue
);
ca
->
exit
=
0
;
ca
->
open
=
0
;
ca
->
wakeup
=
0
;
ca
->
next_read_slot
=
0
;
pubca
->
private
=
ca
;
/* register the DVB device */
ret
=
dvb_register_device
(
dvb_adapter
,
&
ca
->
dvbdev
,
&
dvbdev_ca
,
ca
,
DVB_DEVICE_CA
);
if
(
ret
)
goto
error
;
/* 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
;
init_MUTEX
(
&
ca
->
slot_info
[
i
].
sem
);
}
if
(
signal_pending
(
current
))
{
ret
=
-
EINTR
;
goto
error
;
}
mb
();
/* create a kthread for monitoring this CA device */
ret
=
kernel_thread
(
dvb_ca_en50221_thread
,
ca
,
0
);
if
(
ret
<
0
)
{
printk
(
"dvb_ca_init: failed to start kernel_thread (%d)
\n
"
,
ret
);
goto
error
;
}
ca
->
thread_pid
=
ret
;
return
0
;
error:
if
(
ca
!=
NULL
)
{
if
(
ca
->
dvbdev
!=
NULL
)
dvb_unregister_device
(
ca
->
dvbdev
);
if
(
ca
->
slot_info
!=
NULL
)
kfree
(
ca
->
slot_info
);
kfree
(
ca
);
}
pubca
->
private
=
NULL
;
return
ret
;
}
/**
* Release a DVB CA EN50221 interface device.
*
* @param ca_dev The dvb_device_t instance for the CA device.
* @param ca The associated dvb_ca instance.
*/
void
dvb_ca_en50221_release
(
struct
dvb_ca_en50221
*
pubca
)
{
struct
dvb_ca_private
*
ca
=
(
struct
dvb_ca_private
*
)
pubca
->
private
;
int
i
;
dprintk
(
"%s
\n
"
,
__FUNCTION__
);
/* shutdown the thread if there was one */
if
(
ca
->
thread_pid
)
{
if
(
kill_proc
(
ca
->
thread_pid
,
0
,
1
)
==
-
ESRCH
)
{
printk
(
"dvb_ca_release: thread PID %d already died
\n
"
,
ca
->
thread_pid
);
}
else
{
ca
->
exit
=
1
;
mb
();
dvb_ca_en50221_thread_wakeup
(
ca
);
wait_event_interruptible
(
ca
->
thread_queue
,
ca
->
thread_pid
==
0
);
}
}
for
(
i
=
0
;
i
<
ca
->
slot_count
;
i
++
)
{
dvb_ca_en50221_slot_shutdown
(
ca
,
i
);
}
kfree
(
ca
->
slot_info
);
dvb_unregister_device
(
ca
->
dvbdev
);
kfree
(
ca
);
pubca
->
private
=
NULL
;
}
MODULE_PARM
(
dvb_ca_en50221_debug
,
"i"
);
MODULE_PARM_DESC
(
dvb_ca_en50221_debug
,
"enable verbose debug messages"
);
drivers/media/dvb/dvb-core/dvb_ca_en50221.h
0 → 100644
View file @
7679ba7b
/*
* dvb_ca.h: generic DVB functions for EN50221 CA interfaces
*
* Copyright (C) 2004 Andrew de Quincey
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef _DVB_CA_EN50221_H_
#define _DVB_CA_EN50221_H_
#include <linux/list.h>
#include <linux/dvb/ca.h>
#include "dvbdev.h"
#define DVB_CA_EN50221_POLL_CAM_PRESENT 1
#define DVB_CA_EN50221_POLL_CAM_CHANGED 2
#define DVB_CA_EN50221_POLL_CAM_READY 4
#define DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE 1
#define DVB_CA_EN50221_FLAG_IRQ_FR 2
#define DVB_CA_EN50221_FLAG_IRQ_DA 4
#define DVB_CA_EN50221_CAMCHANGE_REMOVED 0
#define DVB_CA_EN50221_CAMCHANGE_INSERTED 1
/* Structure describing a CA interface */
struct
dvb_ca_en50221
{
/* functions for accessing attribute memory on the CAM */
int
(
*
read_attribute_mem
)(
struct
dvb_ca_en50221
*
ca
,
int
slot
,
int
address
);
int
(
*
write_attribute_mem
)(
struct
dvb_ca_en50221
*
ca
,
int
slot
,
int
address
,
u8
value
);
/* functions for accessing the control interface on the CAM */
int
(
*
read_cam_control
)(
struct
dvb_ca_en50221
*
ca
,
int
slot
,
u8
address
);
int
(
*
write_cam_control
)(
struct
dvb_ca_en50221
*
ca
,
int
slot
,
u8
address
,
u8
value
);
/* Functions for controlling slots */
int
(
*
slot_reset
)(
struct
dvb_ca_en50221
*
ca
,
int
slot
);
int
(
*
slot_shutdown
)(
struct
dvb_ca_en50221
*
ca
,
int
slot
);
int
(
*
slot_ts_enable
)(
struct
dvb_ca_en50221
*
ca
,
int
slot
);
/*
* Poll slot status.
* Only necessary if DVB_CA_FLAG_EN50221_IRQ_CAMCHANGE is not set
*/
int
(
*
poll_slot_status
)(
struct
dvb_ca_en50221
*
ca
,
int
slot
);
/* private data, used by caller */
void
*
data
;
/* Opaque data used by the dvb_ca core. Do not modify! */
void
*
private
;
};
/* ******************************************************************************** */
/* Functions for reporting IRQ events */
/**
* A CAMCHANGE IRQ has occurred.
*
* @param ca CA instance.
* @param slot Slot concerned.
* @param change_type One of the DVB_CA_CAMCHANGE_* values
*/
void
dvb_ca_en50221_camchange_irq
(
struct
dvb_ca_en50221
*
pubca
,
int
slot
,
int
change_type
);
/**
* A CAMREADY IRQ has occurred.
*
* @param ca CA instance.
* @param slot Slot concerned.
*/
void
dvb_ca_en50221_camready_irq
(
struct
dvb_ca_en50221
*
pubca
,
int
slot
);
/**
* An FR or a DA IRQ has occurred.
*
* @param ca CA instance.
* @param slot Slot concerned.
*/
void
dvb_ca_en50221_frda_irq
(
struct
dvb_ca_en50221
*
ca
,
int
slot
);
/* ******************************************************************************** */
/* Initialisation/shutdown functions */
/**
* Initialise a new DVB CA device.
*
* @param dvb_adapter DVB adapter to attach the new CA device to.
* @param ca The dvb_ca instance.
* @param flags Flags describing the CA device (DVB_CA_EN50221_FLAG_*).
* @param slot_count Number of slots supported.
*
* @return 0 on success, nonzero on failure
*/
extern
int
dvb_ca_en50221_init
(
struct
dvb_adapter
*
dvb_adapter
,
struct
dvb_ca_en50221
*
ca
,
int
flags
,
int
slot_count
);
/**
* Release a DVB CA device.
*
* @param ca The associated dvb_ca instance.
*/
extern
void
dvb_ca_en50221_release
(
struct
dvb_ca_en50221
*
ca
);
#endif
drivers/media/dvb/dvb-core/dvb_ksyms.c
View file @
7679ba7b
...
...
@@ -10,6 +10,7 @@
#include "dvb_frontend.h"
#include "dvb_net.h"
#include "dvb_filter.h"
#include "dvb_ca_en50221.h"
EXPORT_SYMBOL
(
dvb_dmxdev_init
);
EXPORT_SYMBOL
(
dvb_dmxdev_release
);
...
...
@@ -49,3 +50,8 @@ EXPORT_SYMBOL(dvb_filter_pes2ts_init);
EXPORT_SYMBOL
(
dvb_filter_pes2ts
);
EXPORT_SYMBOL
(
dvb_filter_get_ac3info
);
EXPORT_SYMBOL
(
dvb_ca_en50221_init
);
EXPORT_SYMBOL
(
dvb_ca_en50221_release
);
EXPORT_SYMBOL
(
dvb_ca_en50221_frda_irq
);
EXPORT_SYMBOL
(
dvb_ca_en50221_camchange_irq
);
EXPORT_SYMBOL
(
dvb_ca_en50221_camready_irq
);
drivers/media/dvb/dvb-core/dvb_ringbuffer.c
View file @
7679ba7b
...
...
@@ -3,9 +3,10 @@
* dvb_ringbuffer.c: ring buffer implementation for the dvb driver
*
* Copyright (C) 2003 Oliver Endriss
* Copyright (C) 2004 Andrew de Quincey
*
* based on code originally found in av7110.c:
* Copyright (C) 1999-200
2 Ralph Metzler
* based on code originally found in av7110.c
& dvb_ci.c
:
* Copyright (C) 1999-200
3 Ralph Metzler
* & Marcus Metzler for convergence integrated media GmbH
*
* This program is free software; you can redistribute it and/or
...
...
@@ -24,6 +25,8 @@
*/
#define __KERNEL_SYSCALLS__
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/module.h>
...
...
@@ -33,6 +36,8 @@
#include "dvb_ringbuffer.h"
#define PKT_READY 0
#define PKT_DISPOSED 1
void
dvb_ringbuffer_init
(
struct
dvb_ringbuffer
*
rbuf
,
void
*
data
,
size_t
len
)
...
...
@@ -158,6 +163,109 @@ ssize_t dvb_ringbuffer_write(struct dvb_ringbuffer *rbuf, const u8 *buf,
return
len
;
}
ssize_t
dvb_ringbuffer_pkt_write
(
struct
dvb_ringbuffer
*
rbuf
,
u8
*
buf
,
size_t
len
,
int
usermem
)
{
int
status
;
ssize_t
oldpwrite
=
rbuf
->
pwrite
;
DVB_RINGBUFFER_WRITE_BYTE
(
rbuf
,
len
>>
8
);
DVB_RINGBUFFER_WRITE_BYTE
(
rbuf
,
len
&
0xff
);
DVB_RINGBUFFER_WRITE_BYTE
(
rbuf
,
PKT_READY
);
status
=
dvb_ringbuffer_write
(
rbuf
,
buf
,
len
,
usermem
);
if
(
status
<
0
)
rbuf
->
pwrite
=
oldpwrite
;
return
status
;
}
ssize_t
dvb_ringbuffer_pkt_read
(
struct
dvb_ringbuffer
*
rbuf
,
size_t
idx
,
int
offset
,
u8
*
buf
,
size_t
len
,
int
usermem
)
{
size_t
todo
;
size_t
split
;
size_t
pktlen
;
pktlen
=
rbuf
->
data
[
idx
]
<<
8
;
pktlen
|=
rbuf
->
data
[(
idx
+
1
)
%
rbuf
->
size
];
if
(
offset
>
pktlen
)
return
-
EINVAL
;
if
((
offset
+
len
)
>
pktlen
)
len
=
pktlen
-
offset
;
idx
=
(
idx
+
DVB_RINGBUFFER_PKTHDRSIZE
+
offset
)
%
rbuf
->
size
;
todo
=
len
;
split
=
((
idx
+
len
)
>
rbuf
->
size
)
?
rbuf
->
size
-
idx
:
0
;
if
(
split
>
0
)
{
if
(
!
usermem
)
memcpy
(
buf
,
rbuf
->
data
+
idx
,
split
);
else
if
(
copy_to_user
(
buf
,
rbuf
->
data
+
idx
,
split
))
return
-
EFAULT
;
buf
+=
split
;
todo
-=
split
;
idx
=
0
;
}
if
(
!
usermem
)
memcpy
(
buf
,
rbuf
->
data
+
idx
,
todo
);
else
if
(
copy_to_user
(
buf
,
rbuf
->
data
+
idx
,
todo
))
return
-
EFAULT
;
return
len
;
}
void
dvb_ringbuffer_pkt_dispose
(
struct
dvb_ringbuffer
*
rbuf
,
size_t
idx
)
{
size_t
pktlen
;
rbuf
->
data
[(
idx
+
2
)
%
rbuf
->
size
]
=
PKT_DISPOSED
;
// clean up disposed packets
while
(
dvb_ringbuffer_avail
(
rbuf
)
>
DVB_RINGBUFFER_PKTHDRSIZE
)
{
if
(
DVB_RINGBUFFER_PEEK
(
rbuf
,
2
)
==
PKT_DISPOSED
)
{
pktlen
=
DVB_RINGBUFFER_PEEK
(
rbuf
,
0
)
<<
8
;
pktlen
|=
DVB_RINGBUFFER_PEEK
(
rbuf
,
1
);
DVB_RINGBUFFER_SKIP
(
rbuf
,
pktlen
+
DVB_RINGBUFFER_PKTHDRSIZE
);
}
else
{
// first packet is not disposed, so we stop cleaning now
break
;
}
}
}
ssize_t
dvb_ringbuffer_pkt_next
(
struct
dvb_ringbuffer
*
rbuf
,
size_t
idx
,
size_t
*
pktlen
)
{
int
consumed
;
int
curpktlen
;
int
curpktstatus
;
if
(
idx
==
-
1
)
{
idx
=
rbuf
->
pread
;
}
else
{
curpktlen
=
rbuf
->
data
[
idx
]
<<
8
;
curpktlen
|=
rbuf
->
data
[(
idx
+
1
)
%
rbuf
->
size
];
idx
=
(
idx
+
curpktlen
+
DVB_RINGBUFFER_PKTHDRSIZE
)
%
rbuf
->
size
;
}
consumed
=
(
idx
-
rbuf
->
pread
)
%
rbuf
->
size
;
while
((
dvb_ringbuffer_avail
(
rbuf
)
-
consumed
)
>
DVB_RINGBUFFER_PKTHDRSIZE
)
{
curpktlen
=
rbuf
->
data
[
idx
]
<<
8
;
curpktlen
|=
rbuf
->
data
[(
idx
+
1
)
%
rbuf
->
size
];
curpktstatus
=
rbuf
->
data
[(
idx
+
2
)
%
rbuf
->
size
];
if
(
curpktstatus
==
PKT_READY
)
{
*
pktlen
=
curpktlen
;
return
idx
;
}
consumed
+=
curpktlen
+
DVB_RINGBUFFER_PKTHDRSIZE
;
idx
=
(
idx
+
curpktlen
+
DVB_RINGBUFFER_PKTHDRSIZE
)
%
rbuf
->
size
;
}
// no packets available
return
-
1
;
}
EXPORT_SYMBOL
(
dvb_ringbuffer_init
);
EXPORT_SYMBOL
(
dvb_ringbuffer_empty
);
...
...
@@ -167,3 +275,7 @@ EXPORT_SYMBOL(dvb_ringbuffer_flush);
EXPORT_SYMBOL
(
dvb_ringbuffer_flush_spinlock_wakeup
);
EXPORT_SYMBOL
(
dvb_ringbuffer_read
);
EXPORT_SYMBOL
(
dvb_ringbuffer_write
);
EXPORT_SYMBOL
(
dvb_ringbuffer_pkt_write
);
EXPORT_SYMBOL
(
dvb_ringbuffer_pkt_read
);
EXPORT_SYMBOL
(
dvb_ringbuffer_pkt_dispose
);
EXPORT_SYMBOL
(
dvb_ringbuffer_pkt_next
);
drivers/media/dvb/dvb-core/dvb_ringbuffer.h
View file @
7679ba7b
...
...
@@ -3,9 +3,10 @@
* dvb_ringbuffer.h: ring buffer implementation for the dvb driver
*
* Copyright (C) 2003 Oliver Endriss
* Copyright (C) 2004 Andrew de Quincey
*
* based on code originally found in av7110.c:
* Copyright (C) 1999-200
2
Ralph Metzler & Marcus Metzler
* based on code originally found in av7110.c
& dvb_ci.c
:
* Copyright (C) 1999-200
3
Ralph Metzler & Marcus Metzler
* for convergence integrated media GmbH
*
* This program is free software; you can redistribute it and/or
...
...
@@ -39,6 +40,8 @@ struct dvb_ringbuffer {
spinlock_t
lock
;
};
#define DVB_RINGBUFFER_PKTHDRSIZE 3
/*
** Notes:
...
...
@@ -120,4 +123,52 @@ extern ssize_t dvb_ringbuffer_read(struct dvb_ringbuffer *rbuf, u8 *buf,
extern
ssize_t
dvb_ringbuffer_write
(
struct
dvb_ringbuffer
*
rbuf
,
const
u8
*
buf
,
size_t
len
,
int
usermem
);
/**
* Write a packet into the ringbuffer.
*
* <rbuf> Ringbuffer to write to.
* <buf> Buffer to write.
* <len> Length of buffer (currently limited to 65535 bytes max).
* <usermem> Set to 1 if <buf> is in userspace.
* returns Number of bytes written, or -EFAULT, -ENOMEM, -EVINAL.
*/
extern
ssize_t
dvb_ringbuffer_pkt_write
(
struct
dvb_ringbuffer
*
rbuf
,
u8
*
buf
,
size_t
len
,
int
usermem
);
/**
* Read from a packet in the ringbuffer. Note: unlike dvb_ringbuffer_read(), this
* does NOT update the read pointer in the ringbuffer. You must use
* dvb_ringbuffer_pkt_dispose() to mark a packet as no longer required.
*
* <rbuf> Ringbuffer concerned.
* <idx> Packet index as returned by dvb_ringbuffer_pkt_next().
* <offset> Offset into packet to read from.
* <buf> Destination buffer for data.
* <len> Size of destination buffer.
* <usermem> Set to 1 if <buf> is in userspace.
* returns Number of bytes read, or -EFAULT.
*/
extern
ssize_t
dvb_ringbuffer_pkt_read
(
struct
dvb_ringbuffer
*
rbuf
,
size_t
idx
,
int
offset
,
u8
*
buf
,
size_t
len
,
int
usermem
);
/**
* Dispose of a packet in the ring buffer.
*
* <rbuf> Ring buffer concerned.
* <idx> Packet index as returned by dvb_ringbuffer_pkt_next().
*/
extern
void
dvb_ringbuffer_pkt_dispose
(
struct
dvb_ringbuffer
*
rbuf
,
size_t
idx
);
/**
* Get the index of the next packet in a ringbuffer.
*
* <rbuf> Ringbuffer concerned.
* <idx> Previous packet index, or -1 to return the first packet index.
* <pktlen> On success, will be updated to contain the length of the packet in bytes.
* returns Packet index (if >=0), or -1 if no packets available.
*/
extern
ssize_t
dvb_ringbuffer_pkt_next
(
struct
dvb_ringbuffer
*
rbuf
,
size_t
idx
,
size_t
*
pktlen
);
#endif
/* _DVB_RINGBUFFER_H_ */
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment