📄 dvb_ca_en50221.c
字号:
break; case DVB_CA_SLOTSTATE_RUNNING: if (ca->open) dvb_ca_en50221_read_data(ca, slot, NULL, 0); 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 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) { 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) { 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; } 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; } down_write(&ca->slot_info[slot].sem); dvb_ringbuffer_init(&ca->slot_info[slot].rx_buffer, rxbuf, RX_BUFFER_SIZE); up_write(&ca->slot_info[slot].sem); 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; // no need to poll if the CAM supports IRQs if (ca->slot_info[slot].da_irq_supported) break; // poll mode 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; } } } /* 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 __user * 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)) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -