📄 dvb_ca_en50221.c
字号:
/* 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 = file->private_data; struct dvb_ca_private *ca = 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 = 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 = 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 = file->private_data; struct dvb_ca_private *ca = 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)) { /* check the CAM hasn't been removed/reset in the meantime */ if (ca->slot_info[slot].slot_state != DVB_CA_SLOTSTATE_RUNNING) { status = -EIO; goto exit; } status = dvb_ca_en50221_write_data(ca, slot, fragbuf, fraglen + 2); if (status == (fraglen + 2)) { written = 1; break; } if (status != -EAGAIN) goto exit; msleep(1); } if (!written) { status = -EIO; goto exit; } fragpos += fraglen; } status = count + 2;exit: return status;}/** * Condition for waking up in dvb_ca_en50221_io_read_condition */static int 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]; slot = ca->next_read_slot; while ((slot_count < ca->slot_count) && (!found)) { if (ca->slot_info[slot].slot_state != DVB_CA_SLOTSTATE_RUNNING) goto nextslot; if (ca->slot_info[slot].rx_buffer.data == NULL) { return 0; } idx = dvb_ringbuffer_pkt_next(&ca->slot_info[slot].rx_buffer, -1, &fraglen); while (idx != -1) { dvb_ringbuffer_pkt_read(&ca->slot_info[slot].rx_buffer, idx, 0, hdr, 2, 0); if (connection_id == -1) connection_id = hdr[0]; if ((hdr[0] == connection_id) && ((hdr[1] & 0x80) == 0)) { *_slot = slot; found = 1; break; } idx = dvb_ringbuffer_pkt_next(&ca->slot_info[slot].rx_buffer, idx, &fraglen); }nextslot: slot = (slot + 1) % ca->slot_count; slot_count++; } ca->next_read_slot = slot; return found;}/** * Implementation of read() syscall. * * @param file File structure. * @param buf Destination buffer. * @param count Size of destination buffer. * @param 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; 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", __FUNCTION__); /* Outgoing packet has a 2 byte header. hdr[0] = slot_id, hdr[1] = connection_id */ if (count < 2) return -EINVAL; /* wait for some data */ if ((status = dvb_ca_en50221_io_read_condition(ca, &result, &slot)) == 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; } idx = dvb_ringbuffer_pkt_next(&ca->slot_info[slot].rx_buffer, -1, &fraglen); pktlen = 2; do { if (idx == -1) { printk("dvb_ca adapter %d: BUG: read packet ended before last_fragment encountered\n", ca->dvbdev->adapter->num); status = -EIO; goto exit; } dvb_ringbuffer_pkt_read(&ca->slot_info[slot].rx_buffer, idx, 0, hdr, 2, 0); 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; } if ((status = dvb_ringbuffer_pkt_read(&ca->slot_info[slot].rx_buffer, idx, 2, buf + pktlen, fraglen, 1)) < 0) { goto exit; } pktlen += fraglen; } if ((hdr[1] & 0x80) == 0) last_fragment = 1; dispose = 1; } idx2 = dvb_ringbuffer_pkt_next(&ca->slot_info[slot].rx_buffer, idx, &fraglen); if (dispose) dvb_ringbuffer_pkt_dispose(&ca->slot_info[slot].rx_buffer, idx); idx = idx2; dispose = 0; } while (!last_fragment); hdr[0] = slot; hdr[1] = connection_id; if ((status = copy_to_user(buf, hdr, 2)) != 0) goto exit; status = pktlen;exit: return status;}/** * Implementation of file open syscall. * * @param inode Inode concerned. * @param file File concerned. * * @return 0 on success, <0 on failure. */static int dvb_ca_en50221_io_open(struct inode *inode, struct file *file){ struct dvb_device *dvbdev = file->private_data; struct dvb_ca_private *ca = dvbdev->priv; int err; int i; dprintk("%s\n", __FUNCTION__); if (!try_module_get(ca->pub->owner)) return -EIO; err = dvb_generic_open(inode, file); if (err < 0) return err; for (i = 0; i < ca->slot_count; i++) { if (ca->slot_info[i].slot_state == DVB_CA_SLOTSTATE_RUNNING) { if (ca->slot_info[i].rx_buffer.data != NULL) { /* it is safe to call this here without locks because * ca->open == 0. Data is not read in this case */ dvb_ringbuffer_flush(&ca->slot_info[i].rx_buffer); } } } ca->open = 1; dvb_ca_en50221_thread_update_delay(ca); dvb_ca_en50221_thread_wakeup(ca); return 0;}/** * Implementation of file close syscall. * * @param inode Inode concerned. * @param file File concerned. * * @return 0 on success, <0 on failure. */static int dvb_ca_en50221_io_release(struct inode *inode, struct file *file){ struct dvb_device *dvbdev = file->private_data; struct dvb_ca_private *ca = dvbdev->priv; int err = 0; dprintk("%s\n", __FUNCTION__); /* mark the CA device as closed */ ca->open = 0; dvb_ca_en50221_thread_update_delay(ca); err = dvb_generic_release(inode, file); module_put(ca->pub->owner); return 0;}/** * Implementation of poll() syscall. * * @param file File concerned. * @param wait poll wait table. * * @return Standard poll mask. */static unsigned int dvb_ca_en50221_io_poll(struct file *file, poll_table * wait){ struct dvb_device *dvbdev = file->private_data; struct dvb_ca_private *ca = dvbdev->priv; unsigned int mask = 0; int slot; int result = 0; dprintk("%s\n", __FUNCTION__); if (dvb_ca_en50221_io_read_condition(ca, &result, &slot) == 1) { mask |= POLLIN; } /* if there is something, return now */ if (mask) return mask; /* wait for something to happen */ poll_wait(file, &ca->wait_queue, wait); if (dvb_ca_en50221_io_read_condition(ca, &result, &slot) == 1) { mask |= POLLIN; } return mask;}EXPORT_SYMBOL(dvb_ca_en50221_init);static struct file_operations dvb_ca_fops = { .owner = THIS_MODULE, .read = dvb_ca_en50221_io_read, .write = dvb_ca_en50221_io_write, .ioctl = dvb_ca_en50221_io_ioctl, .open = dvb_ca_en50221_io_open, .release = dvb_ca_en50221_io_release, .poll = dvb_ca_en50221_io_poll,};static struct dvb_device dvbdev_ca = { .priv = NULL, .users = 1, .readers = 1, .writers = 1, .fops = &dvb_ca_fops,};/* ******************************************************************************** *//* Initialisation/shutdown functions *//** * Initialise a new DVB CA EN50221 interface device. * * @param dvb_adapter DVB adapter to attach the new CA device to. * @param ca The dvb_ca instance. * @param flags Flags describing the CA device (DVB_CA_FLAG_*). * @param 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", __FUNCTION__); if (slot_count < 1) return -EINVAL; /* initialise the system data */ if ((ca = (struct dvb_ca_private *) kmalloc(sizeof(struct dvb_ca_private), GFP_KERNEL)) == NULL) { ret = -ENOMEM; goto error; } memset(ca, 0, sizeof(struct dvb_ca_private)); ca->pub = pubca; ca->flags = flags; ca->slot_count = slot_count; if ((ca->slot_info = kmalloc(sizeof(struct dvb_ca_slot) * slot_count, GFP_KERNEL)) == NULL) { ret = -ENOMEM; goto error; } memset(ca->slot_info, 0, sizeof(struct dvb_ca_slot) * slot_count); init_waitqueue_head(&ca->wait_queue); ca->thread_pid = 0; init_waitqueue_head(&ca->thread_queue); ca->exit = 0; 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); if (ret) goto error; /* now initialise each slot */ for (i = 0; i < slot_count; i++) { memset(&ca->slot_info[i], 0, sizeof(struct dvb_ca_slot)); ca->slot_info[i].slot_state = DVB_CA_SLOTSTATE_NONE; atomic_set(&ca->slot_info[i].camchange_count, 0); ca->slot_info[i].camchange_type = DVB_CA_EN50221_CAMCHANGE_REMOVED; } if (signal_pending(current)) { ret = -EINTR; goto error; } mb(); /* create a kthread for monitoring this CA device */ ret = kernel_thread(dvb_ca_en50221_thread, ca, 0); if (ret < 0) { printk("dvb_ca_init: failed to start kernel_thread (%d)\n", ret); goto error; } ca->thread_pid = ret; return 0;error: if (ca != NULL) { if (ca->dvbdev != NULL) dvb_unregister_device(ca->dvbdev); kfree(ca->slot_info); kfree(ca); } pubca->private = NULL; return ret;}EXPORT_SYMBOL(dvb_ca_en50221_release);/** * Release a DVB CA EN50221 interface device. * * @param ca_dev The dvb_device_t instance for the CA device. * @param ca The associated dvb_ca instance. */void dvb_ca_en50221_release(struct dvb_ca_en50221 *pubca){ struct dvb_ca_private *ca = pubca->private; int i; dprintk("%s\n", __FUNCTION__); /* shutdown the thread if there was one */ if (ca->thread_pid) { if (kill_proc(ca->thread_pid, 0, 1) == -ESRCH) { printk("dvb_ca_release adapter %d: thread PID %d already died\n", ca->dvbdev->adapter->num, ca->thread_pid); } else { ca->exit = 1; mb(); dvb_ca_en50221_thread_wakeup(ca); wait_event_interruptible(ca->thread_queue, ca->thread_pid == 0); } } for (i = 0; i < ca->slot_count; i++) { dvb_ca_en50221_slot_shutdown(ca, i); if (ca->slot_info[i].rx_buffer.data != NULL) { vfree(ca->slot_info[i].rx_buffer.data); } } kfree(ca->slot_info); dvb_unregister_device(ca->dvbdev); kfree(ca); pubca->private = NULL;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -