/* 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;
/* 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 */ return0;
}
/** *dvb_ca_en50221_read_tuple-Readatuplefromattributememory. * *@ca:CAinstance. *@slot:Slotid. *@address:Addresstoreadfrom.Updated. *@tuple_type:Tupleidbyte.Updated. *@tuple_length:Tuplelength.Updated. *@tuple:Destbufferfortuple(mustbe256bytes).Updated. * *return:0onsuccess,nonzeroonerror.
*/ 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; return0;
}
_tuple_length = ca->pub->read_attribute_mem(ca->pub, slot,
_address + 2); if (_tuple_length < 0) return _tuple_length;
_address += 4;
/* 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) { case0x1B: /* 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;
case0x14: /* CISTPL_NO_LINK */ break;
case0xFF: /* 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-ThisfunctiontalkstoanEN50221CAMcontrol *interface.ItreadsabufferofdatafromtheCAM.Thedatacaneither *bestoredinasuppliedbuffer,orautomaticallybeaddedtotheslot's *rx_buffer. * *@ca:CAinstance. *@slot:Slottoreadfrom. *@ebuf:Ifnon-NULL,thedatawillbewrittentothisbuffer.IfNULL, *thedatawillbeaddedintothebufferingsystemasanormal *fragment. *@ecount:Sizeofebuf.IgnoredifebufisNULL. * *return:Numberofbytesread,or<0onerror
*/ 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,addittothereceivebuffer,orcopyintoexternalbufferif *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-ThisfunctiontalkstoanEN50221CAMcontrol *interface.ItwritesabufferofdatatoaCAM. * *@ca:CAinstance. *@slot:Slottowriteto. *@buf:Thedatainthisbufferistreatedasacompletelink-levelpacketto *bewritten. *@bytes_write:Sizeofebuf. *@size_write_flag:AflagonCommandRegisterwhichsayswhetherthelinksize *informationwillbewritenornot. * *return:Numberofbyteswritten,or<0onerror.
*/ 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;
/* *itispossiblewearedealingwithasinglebufferimplementation, *thusifthereisdataavailableforreadorifthereisevenaread *alreadyinprogress,wedonothingbutawakethekernelthreadto *processthedataifnecessary.
*/
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;
}
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);
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_thread_state_machine-ThreadstatemachineforoneCAslot *toperformthedatatransfer. * *@ca:CAinstance. *@slot:Slottoprocess.
*/ 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;
/* 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()) return0;
}
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)) { /* *checktheCAMhasn'tbeenremoved/resetinthe *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;
}
/* *Conditionforwakingupindvb_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-Implementationofread()syscall. * *@file:Filestructure. *@buf:Destinationbuffer. *@count:Sizeofdestinationbuffer. *@ppos:Positioninfile(ignored). * *return:Numberofbytesread,or<0onerror.
*/ 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__);
/* *Outgoingpackethasa2byteheader. *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;
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;
} return0;
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.