📄 dvb_ca_en50221.c
字号:
int status; u8 buf[HOST_LINK_BUF_SIZE]; int i; dprintk("%s\n", __FUNCTION__); /* check if we have space for a link buf in the rx_buffer */ if (ebuf == NULL) { int buf_free; if (ca->slot_info[slot].rx_buffer.data == NULL) { status = -EIO; goto exit; } buf_free = dvb_ringbuffer_free(&ca->slot_info[slot].rx_buffer); if (buf_free < (ca->slot_info[slot].link_buf_size + DVB_RINGBUFFER_PKTHDRSIZE)) { status = -EAGAIN; goto exit; } } /* check if there is data available */ if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS)) < 0) 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 adapter %d: CAM tried to send a buffer larger than the link buffer size (%i > %i)!\n", ca->dvbdev->adapter->num, bytes_read, ca->slot_info[slot].link_buf_size); ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_LINKINIT; status = -EIO; goto exit; } if (bytes_read < 2) { printk("dvb_ca adapter %d: CAM sent a buffer that was less than 2 bytes!\n", ca->dvbdev->adapter->num); ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_LINKINIT; status = -EIO; goto exit; } } else { if (bytes_read > ecount) { printk("dvb_ca adapter %d: CAM tried to send a buffer larger than the ecount size!\n", ca->dvbdev->adapter->num); 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 be 0) */ if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS)) < 0) goto exit; if (status & STATUSREG_RE) { ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_LINKINIT; status = -EIO; goto exit; } /* OK, add it to the receive buffer, or copy into external buffer if supplied */ if (ebuf == NULL) { if (ca->slot_info[slot].rx_buffer.data == NULL) { status = -EIO; goto exit; } dvb_ringbuffer_pkt_write(&ca->slot_info[slot].rx_buffer, buf, bytes_read); } else { memcpy(ebuf, buf, bytes_read); } dprintk("Received CA packet for slot %i connection id 0x%x last_frag:%i size:0x%x\n", slot, buf[0], (buf[1] & 0x80) == 0, bytes_read); /* wake up readers when a last_fragment is received */ if ((buf[1] & 0x80) == 0x00) { wake_up_interruptible(&ca->wait_queue); } status = bytes_read;exit: return status;}/** * 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; /* check if interface is actually waiting for us to read from it, or if a read is in progress */ if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS)) < 0) goto exitnowrite; if (status & (STATUSREG_DA | STATUSREG_RE)) { 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) { ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_LINKINIT; status = -EIO; goto exit; } status = bytes_write; dprintk("Wrote CA packet for slot %i, connection id 0x%x last_frag:%i size:0x%x\n", slot, buf[0], (buf[1] & 0x80) == 0, bytes_write);exit: ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, IRQEN);exitnowrite: return status;}EXPORT_SYMBOL(dvb_ca_en50221_camchange_irq);/* ******************************************************************************** *//* 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){ dprintk("%s\n", __FUNCTION__); ca->pub->slot_shutdown(ca->pub, slot); ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_NONE; /* 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;}EXPORT_SYMBOL(dvb_ca_en50221_camready_irq);/** * 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 = 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);}EXPORT_SYMBOL(dvb_ca_en50221_frda_irq);/** * 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 = 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 = 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: if (ca->open) 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 / 10; } } 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 = data; char name[15]; int slot; int flags; int status; 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); lock_kernel(); daemonize(name); sigfillset(¤t->blocked); unlock_kernel(); /* 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 adaptor %d: PC card did not respond :(\n", ca->dvbdev->adapter->num); 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) { /* 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)) { int 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); break; } } printk("dvb_ca adapter %d: Invalid PC card inserted :(\n", ca->dvbdev->adapter->num); 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 adapter %d: Unable to initialise CAM :(\n", ca->dvbdev->adapter->num); ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_INVALID; dvb_ca_en50221_thread_update_delay(ca); break; } if (ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, CMDREG_RS) != 0) { printk("dvb_ca adapter %d: Unable to reset CAM IF\n", ca->dvbdev->adapter->num); 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 adapter %d: DVB CAM did not respond :(\n", ca->dvbdev->adapter->num); 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) { /* 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)) { int 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); break; } } printk("dvb_ca adapter %d: DVB CAM link initialisation failed :(\n", ca->dvbdev->adapter->num); ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_INVALID; dvb_ca_en50221_thread_update_delay(ca); break; } if (ca->slot_info[slot].rx_buffer.data == NULL) { rxbuf = vmalloc(RX_BUFFER_SIZE); if (rxbuf == NULL) { printk("dvb_ca adapter %d: Unable to allocate CAM rx buffer :(\n", ca->dvbdev->adapter->num); 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 adapter %d: DVB CAM detected and initialised successfully\n", ca->dvbdev->adapter->num); break; case DVB_CA_SLOTSTATE_RUNNING: if (!ca->open) continue; // poll slots for data pktcount = 0; while ((status = 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; } } }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -