📄 dvb_ca.c
字号:
if ((status = ca->read_cam_control(ca, slot, CTRLIF_STATUS)) < 0) goto exit; if (!(status & STATUSREG_FR)) { /* it wasn't free => try again later */ res = -EAGAIN; goto exit; } /* grab some data to send */ bytes_write = dvb_ca_buf_copy_to_cam(ca, buf + 2, ca->slot_info[i].link_buf_size - 2, &last_fragment, slot, connection_id); if (bytes_write <= 0) { res = bytes_write; goto exit; } /* setup the buffer header */ buf[0] = connection_id; buf[1] = last_fragment ? 0 : 0x80; bytes_write += 2; // for the header /* send the amount of data */ if (status = ca->write_cam_control(ca, slot, CTRLIF_SIZE_HIGH, bytes_write >> 8)) goto exit; if (status = ca->write_cam_control(ca, slot, CTRLIF_SIZE_LOW, bytes_write & 0xff)) goto exit; /* send the buffer */ for(i=0; i < bytes_read; i++) { if (status = ca->write_cam_control(ca, slot, CTRLIF_DATA, ca->slot_info[slot].link_buf[i])) goto exit; /* At this point, WE=1, FR=0 during the transfer */ } /* check for write error (WE should now be 0) */ if ((status = ca->read_cam_control(ca, slot, CTRLIF_STATUS)) < 0) goto exit; if (status & STATUSREG_WE) { res = -EIO; goto exit; } /* return the number of bytes read */ status = bytes_write;exit: ca->write_cam_control(ca, slot, CTRLIF_COMMAND, 0); dvb_ca_slot_release(ca, slot); return status;}/** * A CAM has been inserted => initialise it. * * @param ca CA instance. * @param slot Slot containing the CAM to initialise. */static int dvb_ca_en50221_slot_init(struct dvb_ca* ca, int slot) { int status; dprintk ("%s\n", __FUNCTION__); /* acquire the slot exclusively */ if (status = dvb_ca_slot_acquire_exclusive(ca, slot)) return status; /* reset slot */ ca->slot_reset(ca, slot); /* check it is a DVB cam and get hardware info */ if (status = dvb_ca_en50221_parse_attributes(ca, slot)) { dvb_ca_slot_release_exclusive(ca, slot); return status; } /* setup CAM hardware */ if (status = dvb_ca_en50221_init(ca, slot)) { dvb_ca_slot_release_exclusive(ca, slot); return status; } /* perform CAM link protocol init */ if (status = dvb_ca_en50221_link_init(ca, slot)) { dvb_ca_slot_release_exclusive(ca, slot); return status; } /* OK, slot is now active */ INIT_LIST_HEAD(&ca->slot[info].connections); init_MUTEX(&ca->slot_info[slot].task_sem); ca->slot_info[slot].tasks = 0; ca->slot_info[slot].cam_present = 1; /* success */ dvb_ca_slot_release_exclusive(ca, slot); return 0;}/** * 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* ca, int slot) { struct dvb_ca_connection* cacon; struct dvb_ca_connection* tmp; int status; dprintk ("%s\n", __FUNCTION__); /* acquire the slot exclusively */ if (status = dvb_ca_slot_acquire_exclusive(ca, slot)) return status; /* there is no longer a CAM present in this slot */ ca->slot_info[slot].cam_present = 0; /* bypass the TS feed to that slot, then reset it */ ca->slot_ts_bypass(ca, slot, 1); ca->slot_reset(ca, slot); /* flush the buffers on this slot */ /* cannot use dvb_ca_buf_flush_buffers() here because it locks exclusively as well */ list_for_each_entry_safe(cacon, tmp, &ca->slot_info[slot].connections, next) { dvb_ca_connection_destroy(cacon, 1); } /* finished modifying slot structures */ dvb_ca_slot_release_exclusive(ca, slot); /* need to wake up all read and write processes to check if they're now trying to write to a defunct CAM */ wake_up_interruptible(&ca->read_queue); wake_up_interruptible(&ca->write_queue); /* success */ return 0;}/* ******************************************************************************** *//* Monitoring thread *//** * Kernel thread which monitors CA slots for CAM changes, and performs data transfers. */static int dvb_ca_thread(void* data) { struct dvb_ca *ca = (struct dvb_ca*) data; struct dvb_ca_connection* cacon; struct dvb_ca_connection* tmp; char name[15]; int slot; unsigned long scan_connections = jiffies + (60*HZ); dprintk ("%s\n", __FUNCTION__); /* setup kernel thread */ snprintf(name, sizeof(name), "kdvb-ca"); dvb_kernel_thread_setup(name); /* main loop */ while(!ca->exit) { up (&ca->thread_sem); /* is locked when we enter the thread... */ /* sleep for a bit */ interruptible_sleep_on_timeout(&ca->thread_queue, HZ/10); if (signal_pending(current)) break; if (down_interruptible(&ca->thread_sem)) break; if (ca->exit) break; /* go through all the slots looking for CAM changes */ for(slot=0; slot < ca->slot_count; slot++) { /* poll for CAM changes if there aren't any IRQs for that * OR: if we've had a CAM_CHANGE task set */ if ((!(ca->flags & DVB_CA_FLAG_EN50221_IRQ_CAMCHANGE)) || (ca->slot_info[slot].tasks & DVB_CA_TASK_CAM_CHANGED)) { /* determine the hardware status of the CAM */ int slot_status = ca->slot_status(ca, slot); int cam_present = (slot_status & DVB_CA_SLOT_CAM_PRESENT) ? 1: 0; int cam_changed = (slot_status & DVB_CA_SLOT_CAM_CHANGED) ? 1: 0; if (!cam_changed) { cam_changed = (cam_present != ca->slot_info[slot].cam_present); } /* if cam status has changed => update */ if (cam_changed) { if (cam_present) dvb_ca_en50221_cam_slot_init(ca, slot); else dvb_ca_en50221_cam_slot_shutdown(ca, slot); } } /* do any reading and writing necessary */ if (ca->open && (ca->slot_info[slot].cam_present)) { // FIXME: sort it out! /* if the CAM or the CI interface itself doesn't support IRQs, need to poll for reads */ if ((!ca->slot_info[slot].irq_supported) || (!ca->flags & DVB_CA_FLAG_EN50221_IRQ_READ)) { /* if there is data to be read, read it! */ while(dvb_ca_cam_read_data(ca, slot) > 0); } } /* tidy up any defunct connection structures every 60 seconds */ if ((ca->slot_info[slot].cam_present) && (time_after(jiffies, scan_connections))) { if (status = dvb_ca_slot_acquire_exclusive(ca, slot)) break; list_for_each_entry_safe(cacon, tmp, &ca->slot_info[slot].connections, next) { /* tear down a connection structure if it hasn't been used for 120 seconds */ unsigned long timeout = cacon->last_used + (120 * HZ); if (time_after(jiffies, timeout)) { dvb_ca_connection_destroy(cacon, 1); } } dvb_ca_slot_release_exclusive(ca, slot); } } } up(&ca->thread_sem); mb(); /* completed */ wake_up_interruptible (&ca->thread_queue); return(0);}/* ******************************************************************************** *//* Packet buffer functions *//** * Flush buffers associated with a particular CA slot. * * @param ca CA instance. * @param slot Slot whose buffers are to be to flushed. * * @return 0 on success, <0 on failure. */int dvb_ca_buf_flush_buffers(struct dvb_ca* ca, int slot) { struct dvb_ca_connection* cacon; struct dvb_ca_connection* tmp; int status; if (status = dvb_ca_slot_acquire_exclusive(ca, slot)) return status; /* just destroy all the connections for this slot */ list_for_each_entry_safe(cacon, tmp, &ca->slot_info[slot].connections, next) { dvb_ca_connection_destroy(cacon, 1); } dvb_ca_slot_release_exclusive(ca, slot); return 0;}/** * This function does not talk to a CAM; it is a utility function for grabbing data from the * connection buffers in chunks suitable for use by hardware-dependent code. * * Copy data to be sent to the CAM from the cache into the * supplied buffer. This call is for lower layers to get data still to * be sent to the CAM. The buffer is filled by the layers which receive data * from userspace. * * @param ca CA instance. * @param dest Where to put the data. * @param len Size of destination buffer. * @param last_fragment. This will be set to 1 if this is the last fragment of a packet, * or 0 if there is still more data in this packet to be transmitted. * @param slot Slot concerned. * @param connection_id Connection id concerned. * * @return Number of bytes copied to dest, or <0 on error. */int dvb_ca_buf_copy_to_cam(dvb_ca* ca, u8 *dest, int len, u8* last_fragment, u8 slot, u8 connection_id){ struct dvb_ca_connection* cacon; int status; /* in the buffer, each packet has a 3 byte header as follows: 0: High byte of total packet size. 1: Low byte of total packet size. 2: Flag byte. 0=> packet incomplete. 1=> packet complete. These are all updated as data fragments are appended to the packet. */ /* acquire the slot */ if (status = dvb_ca_slot_acquire(ca, slot)) return status; /* get the connection struct for this slot/connection id */ cacon = dvb_ca_connection_get(ca, slot, connection_id, 0); /* if we didn't find a connection struct, there is no data! */ if (cacon == NULL) { dvb_ca_slot_release(ca, slot); *last_fragment = 1; return 0; } /* if there is no data, there is no point going on */ if (dvb_ringbuffer_avail(&cacon->tx_buffer) == 0) { dvb_ca_slot_release(ca, slot); *last_fragment = 1; return 0; } /* if we're not in the middle of sending a packet... */ if (cacon->tx_partial_pkt_size == -1); /* ensure there is actually enough data for a header */ if (dvb_ringbuffer_avail(&cacon->tx_buffer) < 3) { dvb_ca_slot_release(ca, slot); *last_fragment = 1; return 0; } /* if the packet data is not available yet, exit straight away */ if (DVB_RINGBUFFER_PEEK(&cacon->tx_buffer, 2) != 1) { dvb_ca_slot_release(ca, slot); *last_fragment = 1; return 0; } /* get the size of the next packet */ cacon->tx_partial_pkt_size = DVB_RINGBUFFER_PEEK(&cacon->tx_buffer, 0) << 8; cacon->tx_partial_pkt_size |= DVB_RINGBUFFER_PEEK(&cacon->tx_buffer, 1); DVB_RINGBUFFER_SKIP(&cacon->tx_buffer, 3); } /* don't copy more data than is actually available */ if (cacon->tx_partial_pkt_size < len) len = cacon->partial_pkt_size; /* copy the data into dest */ dvb_ringbuffer_read(&cacon->tx_buffer, dest, len, 0); /* update the amount of consumed data */ cacon->tx_partial_pkt_size -= len; if (cacon->tx_partial_pkt_size == 0) { /* whole packet consumed => move to next */ cacon->tx_partial_pkt_size = -1; /* wake up the wait queues now writing is completed */ wake_up_interruptible(&ca->read_queue); wake_up_interruptible(&ca->write_queue); } /* ok, return the amount of data copied this time */ *last_fragment = (cacon->tx_partial_pkt_size == -1) ? 1 : 0; dvb_ca_slot_release(ca, slot); return len;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -