// SPDX-License-Identifier: GPL-2.0-or-later /* * 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
*/
staticvoid dvb_ca_en50221_thread_wakeup(struct dvb_ca_private *ca); staticint dvb_ca_en50221_read_data(struct dvb_ca_private *ca, int slot,
u8 *ebuf, int ecount); staticint dvb_ca_en50221_write_data(struct dvb_ca_private *ca, int slot,
u8 *ebuf, int ecount, int size_write_flag);
/** * findstr - Safely find needle in haystack. * * @haystack: Buffer to look in. * @hlen: Number of bytes in haystack. * @needle: Buffer to find. * @nlen: Number of bytes in needle. * return: Pointer into haystack needle was found at, or NULL if not found.
*/ staticchar *findstr(char *haystack, int hlen, char *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;
}
if (cam_changed) { if (!cam_present_now)
sl->camchange_type = DVB_CA_EN50221_CAMCHANGE_REMOVED; else
sl->camchange_type = DVB_CA_EN50221_CAMCHANGE_INSERTED;
atomic_set(&sl->camchange_count, 1);
} else { if ((sl->slot_state == DVB_CA_SLOTSTATE_WAITREADY) &&
(slot_status & DVB_CA_EN50221_POLL_CAM_READY)) { /* move to validate state if reset is completed */
sl->slot_state = DVB_CA_SLOTSTATE_VALIDATE;
}
}
return cam_changed;
}
/** * 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. * * @ca: CA instance. * @slot: Slot on interface. * @waitfor: Flags to wait for. * @timeout_hz: Timeout in milliseconds. * * return: 0 on success, nonzero on error.
*/ staticint dvb_ca_en50221_wait_if_status(struct dvb_ca_private *ca, int slot,
u8 waitfor, int timeout_hz)
{ unsignedlong timeout; unsignedlong start;
dprintk("%s\n", __func__);
/* loop until timeout elapsed */
start = jiffies;
timeout = jiffies + timeout_hz; while (1) { int res;
/* read the status and check for error */
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",
__func__, jiffies - start); return 0;
}
/* check for timeout */ if (time_after(jiffies, timeout)) break;
/* if we get here, we've timed out */ return -ETIMEDOUT;
}
/** * dvb_ca_en50221_link_init - Initialise the link layer connection to a CAM. * * @ca: CA instance. * @slot: Slot id. * * return: 0 on success, nonzero on failure.
*/ staticint dvb_ca_en50221_link_init(struct dvb_ca_private *ca, int slot)
{ struct dvb_ca_slot *sl = &ca->slot_info[slot]; int ret; int buf_size;
u8 buf[2];
dprintk("%s\n", __func__);
/* we'll be determining these during this function */
sl->da_irq_supported = 0;
/* * set the host link buffer size temporarily. it will be overwritten * with the real negotiated size later.
*/
sl->link_buf_size = 2;
/* read the buffer size from the CAM */
ret = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND,
IRQEN | CMDREG_SR); if (ret) return ret;
ret = dvb_ca_en50221_wait_if_status(ca, slot, STATUSREG_DA, HZ); if (ret) return ret;
ret = dvb_ca_en50221_read_data(ca, slot, buf, 2); if (ret != 2) return -EIO;
ret = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, IRQEN); if (ret) 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;
sl->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 */
ret = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND,
IRQEN | CMDREG_SW); if (ret) return ret;
ret = dvb_ca_en50221_wait_if_status(ca, slot, STATUSREG_FR, HZ / 10); if (ret) return ret;
ret = dvb_ca_en50221_write_data(ca, slot, buf, 2, CMDREG_SW); if (ret != 2) return -EIO;
ret = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, IRQEN); if (ret) return ret;
/* success */ return 0;
}
/** * dvb_ca_en50221_read_tuple - Read a tuple from attribute memory. * * @ca: CA instance. * @slot: Slot id. * @address: Address to read from. Updated. * @tuple_type: Tuple id byte. Updated. * @tuple_length: Tuple length. Updated. * @tuple: Dest buffer for tuple (must be 256 bytes). Updated. * * return: 0 on success, nonzero on error.
*/ staticint dvb_ca_en50221_read_tuple(struct dvb_ca_private *ca, int slot, int *address, int *tuple_type, int *tuple_length, u8 *tuple)
{ int i; int _tuple_type; int _tuple_length; int _address = *address;
/* grab the next tuple length and type */
_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);
*address += 2;
*tuple_type = _tuple_type;
*tuple_length = 0; return 0;
}
_tuple_length = ca->pub->read_attribute_mem(ca->pub, slot,
_address + 2); if (_tuple_length < 0) return _tuple_length;
_address += 4;
/** * dvb_ca_en50221_parse_attributes - Parse attribute memory of a CAM module, * extracting Config register, and checking it is a DVB CAM module. * * @ca: CA instance. * @slot: Slot id. * * return: 0 on success, <0 on failure.
*/ staticint dvb_ca_en50221_parse_attributes(struct dvb_ca_private *ca, int slot)
{ struct dvb_ca_slot *sl; int address = 0; int tuple_length; int tuple_type;
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 */
status = dvb_ca_en50221_read_tuple(ca, slot, &address, &tuple_type,
&tuple_length, tuple); if (status < 0) return status; if (tuple_type != 0x1D) return -EINVAL;
/* CISTPL_DEVICE_0C */
status = dvb_ca_en50221_read_tuple(ca, slot, &address, &tuple_type,
&tuple_length, tuple); if (status < 0) return status; if (tuple_type != 0x1C) return -EINVAL;
/* CISTPL_VERS_1 */
status = dvb_ca_en50221_read_tuple(ca, slot, &address, &tuple_type,
&tuple_length, tuple); if (status < 0) return status; if (tuple_type != 0x15) return -EINVAL;
/* CISTPL_CONFIG */
status = dvb_ca_en50221_read_tuple(ca, slot, &address, &tuple_type,
&tuple_length, tuple); if (status < 0) return status; if (tuple_type != 0x1A) return -EINVAL; if (tuple_length < 3) return -EINVAL;
/* extract the configbase */
rasz = tuple[0] & 3; if (tuple_length < (3 + rasz + 14)) return -EINVAL;
sl = &ca->slot_info[slot];
sl->config_base = 0; for (i = 0; i < rasz + 1; i++)
sl->config_base |= (tuple[2 + i] << (8 * i));
/* check it contains the correct DVB string */
dvb_str = findstr((char *)tuple, tuple_length, "DVB_CI_V", 8); if (!dvb_str) return -EINVAL; if (tuple_length < ((dvb_str - (char *)tuple) + 12)) return -EINVAL;
/* is it a version we support? */ if (strncmp(dvb_str + 8, "1.00", 4)) {
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]); return -EINVAL;
}
/* process the CFTABLE_ENTRY tuples, and any after those */ while ((!end_chain) && (address < 0x1000)) {
status = dvb_ca_en50221_read_tuple(ca, slot, &address,
&tuple_type, &tuple_length,
tuple); if (status < 0) return status; switch (tuple_type) { case 0x1B: /* CISTPL_CFTABLE_ENTRY */ if (tuple_length < (2 + 11 + 17)) break;
/* if we've already parsed one, just use it */ if (got_cftableentry) break;
/* get the config option */
sl->config_option = tuple[0] & 0x3f;
/* OK, check it contains the correct strings */ if (!findstr((char *)tuple, tuple_length, "DVB_HOST", 8) ||
!findstr((char *)tuple, tuple_length, "DVB_CI_MODULE", 13)) 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 */
dprintk("dvb_ca: Skipping unknown tuple type:0x%x length:0x%x\n",
tuple_type, tuple_length); break;
}
}
if ((address > 0x1000) || (!got_cftableentry)) return -EINVAL;
/** * 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. * * @ca: CA instance. * @slot: Slot to read from. * @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. * @ecount: Size of ebuf. Ignored if ebuf is NULL. * * return: Number of bytes read, or < 0 on error
*/ staticint dvb_ca_en50221_read_data(struct dvb_ca_private *ca, int slot,
u8 *ebuf, int ecount)
{ struct dvb_ca_slot *sl = &ca->slot_info[slot]; int bytes_read; int status;
u8 buf[HOST_LINK_BUF_SIZE]; int i;
dprintk("%s\n", __func__);
/* check if we have space for a link buf in the rx_buffer */ if (!ebuf) { int buf_free;
if (!sl->rx_buffer.data) {
status = -EIO; gotoexit;
}
buf_free = dvb_ringbuffer_free(&sl->rx_buffer);
if (buf_free < (sl->link_buf_size +
DVB_RINGBUFFER_PKTHDRSIZE)) {
status = -EAGAIN; gotoexit;
}
}
if (ca->pub->read_data &&
(sl->slot_state != DVB_CA_SLOTSTATE_LINKINIT)) { if (!ebuf)
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) gotoexit;
} else { /* check if there is data available */
status = ca->pub->read_cam_control(ca->pub, slot,
CTRLIF_STATUS); if (status < 0) gotoexit; if (!(status & STATUSREG_DA)) { /* no data */
status = 0; gotoexit;
}
/* read the amount of data */
status = ca->pub->read_cam_control(ca->pub, slot,
CTRLIF_SIZE_HIGH); if (status < 0) gotoexit;
bytes_read = status << 8;
status = ca->pub->read_cam_control(ca->pub, slot,
CTRLIF_SIZE_LOW); if (status < 0) gotoexit;
bytes_read |= status;
/* check it will fit */ if (!ebuf) { if (bytes_read > sl->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,
sl->link_buf_size);
sl->slot_state = DVB_CA_SLOTSTATE_LINKINIT;
status = -EIO; gotoexit;
} 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);
sl->slot_state = DVB_CA_SLOTSTATE_LINKINIT;
status = -EIO; gotoexit;
}
} 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; gotoexit;
}
}
/* 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) gotoexit;
/* OK, store it in the buffer */
buf[i] = status;
}
/* check for read error (RE should now be 0) */
status = ca->pub->read_cam_control(ca->pub, slot,
CTRLIF_STATUS); if (status < 0) gotoexit; if (status & STATUSREG_RE) {
sl->slot_state = DVB_CA_SLOTSTATE_LINKINIT;
status = -EIO; gotoexit;
}
}
/* * OK, add it to the receive buffer, or copy into external buffer if * supplied
*/ if (!ebuf) { if (!sl->rx_buffer.data) {
status = -EIO; gotoexit;
}
dvb_ringbuffer_pkt_write(&sl->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;
}
/** * dvb_ca_en50221_write_data - This function talks to an EN50221 CAM control * interface. It writes a buffer of data to a CAM. * * @ca: CA instance. * @slot: Slot to write to. * @buf: The data in this buffer is treated as a complete link-level packet to * be written. * @bytes_write: Size of ebuf. * @size_write_flag: A flag on Command Register which says whether the link size * information will be writen or not. * * return: Number of bytes written, or < 0 on error.
*/ staticint dvb_ca_en50221_write_data(struct dvb_ca_private *ca, int slot,
u8 *buf, int bytes_write, int size_write_flag)
{ struct dvb_ca_slot *sl = &ca->slot_info[slot]; int status; int i;
dprintk("%s\n", __func__);
/* sanity check */ if (bytes_write > sl->link_buf_size) return -EINVAL;
/* * 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.
*/
status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS); if (status < 0) goto exitnowrite; if (status & (STATUSREG_DA | STATUSREG_RE)) { if (status & STATUSREG_DA)
dvb_ca_en50221_thread_wakeup(ca);
status = -EAGAIN; goto exitnowrite;
}
/* OK, set HC bit */
status = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND,
IRQEN | CMDREG_HC | size_write_flag); if (status) gotoexit;
/* check if interface is still free */
status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS); if (status < 0) gotoexit; if (!(status & STATUSREG_FR)) { /* it wasn't free => try again later */
status = -EAGAIN; gotoexit;
}
/* * 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) gotoexit;
if (status & (STATUSREG_DA | STATUSREG_RE)) { if (status & STATUSREG_DA)
dvb_ca_en50221_thread_wakeup(ca);
status = -EAGAIN; gotoexit;
}
/* send the amount of data */
status = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_SIZE_HIGH,
bytes_write >> 8); if (status) gotoexit;
status = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_SIZE_LOW,
bytes_write & 0xff); if (status) gotoexit;
/* send the buffer */ for (i = 0; i < bytes_write; i++) {
status = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_DATA,
buf[i]); if (status) gotoexit;
}
/* check for write error (WE should now be 0) */
status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS); if (status < 0) gotoexit; if (status & STATUSREG_WE) {
sl->slot_state = DVB_CA_SLOTSTATE_LINKINIT;
status = -EIO; gotoexit;
}
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);
/** * dvb_ca_en50221_slot_shutdown - A CAM has been removed => shut it down. * * @ca: CA instance. * @slot: Slot to shut down.
*/ staticint dvb_ca_en50221_slot_shutdown(struct dvb_ca_private *ca, int slot)
{
dprintk("%s\n", __func__);
/** * dvb_ca_en50221_thread_wakeup - Wake up the DVB CA thread * * @ca: CA instance.
*/ staticvoid dvb_ca_en50221_thread_wakeup(struct dvb_ca_private *ca)
{
dprintk("%s\n", __func__);
/** * dvb_ca_en50221_thread_update_delay - Update the delay used by the thread. * * @ca: CA instance.
*/ staticvoid dvb_ca_en50221_thread_update_delay(struct dvb_ca_private *ca)
{ int delay; int curdelay = 100000000; int slot;
/* * Beware of too high polling frequency, because one polling * call might take several hundred milliseconds until timeout!
*/ for (slot = 0; slot < ca->slot_count; slot++) { struct dvb_ca_slot *sl = &ca->slot_info[slot];
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; /* 100ms */ break;
/** * dvb_ca_en50221_poll_cam_gone - Poll if the CAM is gone. * * @ca: CA instance. * @slot: Slot to process. * return:: 0 .. no change * 1 .. CAM state changed
*/
staticint 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;
}
/** * dvb_ca_en50221_thread_state_machine - Thread state machine for one CA slot * to perform the data transfer. * * @ca: CA instance. * @slot: Slot to process.
*/ staticvoid dvb_ca_en50221_thread_state_machine(struct dvb_ca_private *ca, int slot)
{ struct dvb_ca_slot *sl = &ca->slot_info[slot]; int flags; int pktcount; void *rxbuf;
mutex_lock(&sl->slot_lock);
/* 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);
/* if a CAM is NOW present, initialise it */ if (sl->camchange_type == DVB_CA_EN50221_CAMCHANGE_INSERTED)
sl->slot_state = DVB_CA_SLOTSTATE_UNINITIALISED;
/* we've handled one CAMCHANGE */
dvb_ca_en50221_thread_update_delay(ca);
atomic_dec(&sl->camchange_count);
}
/* CAM state machine */ switch (sl->slot_state) { case DVB_CA_SLOTSTATE_NONE: case DVB_CA_SLOTSTATE_INVALID: /* no action needed */ break;
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;
case DVB_CA_SLOTSTATE_VALIDATE: if (dvb_ca_en50221_parse_attributes(ca, slot) != 0) { if (dvb_ca_en50221_poll_cam_gone(ca, slot)) break;
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 don't 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) { /* * don't sleep; there is likely to be more data * to read
*/
ca->wakeup = 1; break;
}
} break;
}
mutex_unlock(&sl->slot_lock);
}
/* * Kernel thread which monitors CA slots for CAM changes, and performs data * transfers.
*/ staticint 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);
}
timeout = jiffies + HZ / 2;
written = 0; while (!time_after(jiffies, timeout)) { /* * check the CAM hasn't been removed/reset in the * meantime
*/ if (sl->slot_state != DVB_CA_SLOTSTATE_RUNNING) {
status = -EIO; gotoexit;
}
mutex_lock(&sl->slot_lock);
status = dvb_ca_en50221_write_data(ca, slot, fragbuf,
fraglen + 2, 0);
mutex_unlock(&sl->slot_lock); if (status == (fraglen + 2)) {
written = 1; break;
} if (status != -EAGAIN) gotoexit;
usleep_range(1000, 1100);
} if (!written) {
status = -EIO; gotoexit;
}
fragpos += fraglen;
}
status = count + 2;
exit: return status;
}
/* * Condition for waking up in dvb_ca_en50221_io_read_condition
*/ staticint dvb_ca_en50221_io_read_condition(struct dvb_ca_private *ca, int *result, int *_slot)
{ int slot; int slot_count = 0; int idx;
size_t fraglen; int connection_id = -1; int found = 0;
u8 hdr[2];
/** * dvb_ca_en50221_io_read - Implementation of read() syscall. * * @file: File structure. * @buf: Destination buffer. * @count: Size of destination buffer. * @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 __user *buf,
size_t count, loff_t *ppos)
{ struct dvb_device *dvbdev = file->private_data; struct dvb_ca_private *ca = dvbdev->priv; struct dvb_ca_slot *sl; 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", __func__);
/* * Outgoing packet has a 2 byte header. * hdr[0] = slot_id, hdr[1] = connection_id
*/ if (count < 2) return -EINVAL;
/* wait for some data */
status = dvb_ca_en50221_io_read_condition(ca, &result, &slot); if (status == 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;
}
sl = &ca->slot_info[slot];
idx = dvb_ringbuffer_pkt_next(&sl->rx_buffer, -1, &fraglen);
pktlen = 2; do { if (idx == -1) {
pr_err("dvb_ca adapter %d: BUG: read packet ended before last_fragment encountered\n",
ca->dvbdev->adapter->num);
status = -EIO; gotoexit;
}
dvb_ringbuffer_pkt_read(&sl->rx_buffer, idx, 0, hdr, 2); 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;
status =
dvb_ringbuffer_pkt_read_user(&sl->rx_buffer,
idx, 2,
buf + pktlen,
fraglen); if (status < 0) gotoexit;
for (i = 0; i < ca->slot_count; i++) { struct dvb_ca_slot *sl = &ca->slot_info[i];
if (sl->slot_state == DVB_CA_SLOTSTATE_RUNNING) { if (!sl->rx_buffer.data) { /* * it is safe to call this here without locks * because ca->open == 0. Data is not read in * this case
*/
dvb_ringbuffer_flush(&sl->rx_buffer);
}
}
}
/** * dvb_ca_en50221_init - Initialise a new DVB CA EN50221 interface device. * * @dvb_adapter: DVB adapter to attach the new CA device to. * @pubca: The dvb_ca instance. * @flags: Flags describing the CA device (DVB_CA_FLAG_*). * @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", __func__);
if (slot_count < 1) return -EINVAL;
/* initialise the system data */
ca = kzalloc(sizeof(*ca), GFP_KERNEL); if (!ca) {
ret = -ENOMEM; gotoexit;
}
kref_init(&ca->refcount);
ca->pub = pubca;
ca->flags = flags;
ca->slot_count = slot_count;
ca->slot_info = kcalloc(slot_count, sizeof(struct dvb_ca_slot),
GFP_KERNEL); if (!ca->slot_info) {
ret = -ENOMEM; goto free_ca;
}
init_waitqueue_head(&ca->wait_queue);
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, 0); if (ret) goto free_slot_info;
/* now initialise each slot */ for (i = 0; i < slot_count; i++) { struct dvb_ca_slot *sl = &ca->slot_info[i];
if (signal_pending(current)) {
ret = -EINTR; goto unregister_device;
}
mb();
/* create a kthread for monitoring this CA device */
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);
pr_err("dvb_ca_init: failed to start kernel_thread (%d)\n",
ret); goto unregister_device;
} return 0;
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.18Bemerkung:
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.