⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 dvb_ca.c

📁 linux_dvb的驱动程序:linuxtv-dvb-1.1.1.rar
💻 C
📖 第 1 页 / 共 5 页
字号:
}/** * This function does not talk to a CAM; it is a utility function for caching data retrieved * from a CAM by some hardware-dependent method and storing it in the connection buffers. * * Copy data recieved from the CAM into the cache. This call is for lower layers * to cache data received from the CAM. The buffer is emptied by the higher layers * which send data to userspace. * * @param ca CA instance. * @param data Buffer containing data. * @param len Number of bytes. * @param last_fragment 1 if this is the last fragment of a packet, or 0 if not. * @param slot Slot the data was received from. * @param connection_id Connection id the data was received from. * * @return 0 on success, or -1 if there was not enough space. * If -1 is returned, the packet will be automatically discarded. Any further data written * for this packet (last_fragment==0) will also be discarded until the next packet (i.e. the * write AFTER the write with last_fragment==1). */int dvb_ca_buf_copy_from_cam(dvb_ca* ca, u8 *data, int len, u8 last_fragment, u8 slot, u8 connection_id){        struct dvb_ca_connection* cacon;        int total_length = 0;        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, 1);        if (cacon == NULL) {                dvb_ca_slot_release(ca, slot);                return -1;        }        /* check if we're discarding the current packet */        if (cacon->rx_partial_pkt == -2) {                /* if it is the last_fragment of a packet, set the flags appropriately */                if (last_fragment) {                        /* we're NOT in the middle of a partial any more */                        cacon->rx_partial_pkt = -1;                }                /* indicate we're discarding this bit */                dvb_ca_slot_release(ca, slot);                return -1;        } else if (cacon->rx_partial_pkt == -1) {                /* check we've got enough space for the data + 3 byte header */                if (dvb_ringbuffer_free(&cacon->rx_buffer) < (len + 3)) {                        /* if it is not the last fragment, we need to discard any other data written for this packet */                        if (!last_fragment) {                                /* set the rx_partial_pkt to indicate discarding in progress */                                cacon->rx_partial_pkt = -2;                        }                        /* indicate we're discarding */                        dvb_ca_slot_release(ca, slot);                        return -1;                }                /* record position partial packet starts at */                cacon->rx_partial_pkt = cacon->rx_buffer.pwrite - cacon->rx_buffer.data;                /* build the dummy (for the moment) packet header */                DVB_RINGBUFFER_WRITE_BYTE(&cacon->rx_buffer, 0);                DVB_RINGBUFFER_WRITE_BYTE(&cacon->rx_buffer, 0);                DVB_RINGBUFFER_WRITE_BYTE(&cacon->rx_buffer, 0);        } else {                /* check we've got enough space for this fragment, if not, need to discard the current packet */                if (dvb_ringbuffer_free(&cacon->rx_buffer) < len) {                        /* roll the write pointer back to the start of the packet being discarded */                        cacon->rx_buffer.pwrite = cacon->rx_buffer.data + cacon->rx_partial_pkt;                        /* mark the current packet to be discarded */                        cacon->rx_partial_pkt = -2;                        /* if it is the last_fragment of a packet, set the flags appropriately */                        if (last_fragment) {                                /* we're NOT in the middle of a packet */                                cacon->rx_partial_pkt = -1;                        }                        dvb_ca_slot_release(ca, slot);                        return -1;                }                /* get the current length of the partial packet */                total_length = cacon->rx_buffer.data[(cacon->rx_partial_pkt + 0) % cacon->rx_buffer.size] << 8;                total_length |= cacon->rx_buffer.data[(cacon->rx_partial_pkt + 1) % cacon->rx_buffer.size];        }        /* store the data */        dvb_ringbuffer_write(&cacon->rx_buffer, data, len, 0);        /* update the length of the packet in the packet header */        total_length += len;        cacon->rx_buffer.data[(cacon->rx_partial_pkt + 0) % cacon->rx_buffer.size] = total_length >> 8;        cacon->rx_buffer.data[(cacon->rx_partial_pkt + 1) % cacon->rx_buffer.size] = (total_length & 0xff);        /* if this is the last fragment, tidy up */        if (last_fragment) {                /* mark the packet as complete */                cacon->rx_buffer.data[(cacon->rx_partial_pkt + 2) % cacon->rx_buffer.size] = 1;                /* we no longer have a partial packet */                cacon->rx_partial_pkt = -1;                /* ok, wake up everything now reading is complete */                wake_up_interruptible(&ca->read_queue);                wake_up_interruptible(&ca->write_queue);        }        /* success */        dvb_ca_slot_release(ca, slot);        return 0;}#define DVB_CA_BUF_COPY_FROM_USER_CONDITION                                                   \({                                                                                            \        int __status;                                                                         \                                                                                              \        if ((__status = dvb_ca_slot_acquire(ca, slot)) == 0) {                                \                cacon = dvb_ca_connection_get(ca, slot, connection_id, 1);                    \                if (cacon == NULL) {                                                          \                        __status = -EIO;                                                      \                        dvb_ca_slot_release(ca, slot);                                        \                } else {                                                                      \                        __status = dvb_ringbuffer_free(&cacon->tx_buffer) >= (count+3)        \                        if (!__status) dvb_ca_slot_release(ca, slot);                         \                }                                                                             \        }                                                                                     \        __status;                                                                             \})/** * Function to copy a packet supplied by the user into the appropriate slot/connection's tx_buffer. * * @param ca CA instance. * @param file Associated file. * @param buf Source buffer (userspace). * @param count Size of buffer. * @param ppos Position in file (ignored). * @param slot Slot to write to. * @param connection_id Id of connection to write to. * * @return Number of bytes copied, or <0 on error. */static ssize_t dvb_ca_buf_copy_from_user(dvb_ca* ca, struct file *file, const char *buf, size_t count, loff_t *ppos, int slot, int connection_id) {        dvb_ca_connection* cacon;        int status;        int write_pos;        /* Packets in the buffer have a 3 byte header as follows:           0: MSB of total packet size.           1: LSB 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;        /* locate the connection to write to */        cacon = dvb_ca_connection_get(ca, slot, connection_id, 1);        if (cacon == NULL) {                dvb_ca_slot_release(ca, slot);                return -EIO;        }        /* check if the packet is WAY too big */        if ((count + 3) > cacon->tx_buffer.size) {                dvb_ca_slot_release(ca, slot);                return -EINVAL;        }        /* OK, block if there isn't enough space at the moment */        if (dvb_ringbuffer_free(&cacon->tx_buffer) < (count + 3)) {                /* if we're in non blocking mode... */                if (file->f_flags & O_NONBLOCK) {                        dvb_ca_slot_release(ca, slot);                        return -EWOULDBLOCK;                }                /* wait for some space */                dvb_ca_slot_release(ca, slot);                if ((status = dvb_ca_wait_event_interruptible(ca->write_queue, DVB_CA_BUF_COPY_FROM_USER_CONDITION)) < 0) {                        /* slot is already released if the above failed */                        return status;                }                /* the wait was successful => slot is now reacquired */        }        /* write the packet header */        write_pos = cacon->tx_buffer.pwrite - cacon->tx_buffer.data;        DVB_RINGBUFFER_WRITE_BYTE(&cacon->tx_buffer, count >> 8);        DVB_RINGBUFFER_WRITE_BYTE(&cacon->tx_buffer, count & 0xff);        DVB_RINGBUFFER_WRITE_BYTE(&cacon->tx_buffer, 0);        /* write the packet data to the buffer */        if (dvb_ringbuffer_write(&cacon->tx_buffer, buf+3, count, 1) != count) {                /* roll the write pointer back to the start of the packet being discarded */                cacon->tx_buffer.pwrite = cacon->tx_buffer.data + write_pos;                /* tell userspace */                dvb_ca_slot_release(ca, slot);                return -EFAULT;        }        /* mark the packet as available */        cacon->tx_buffer.data[(write_pos + 2) % cacon->tx_buffer.size] = 1;        /* return number of bytes written */        dvb_ca_slot_release(ca, slot);        return count;}// TODO: we should maybe remember the previous slot/connection and the next time start// at the one after, to give them equal priority.#define DVB_CA_BUF_COPY_TO_USER_CONDITION                                                       \({                                                                                              \        int __status;                                                                           \                                                                                                \        for(slot=0; slot < ca->slot_count; slot++) {                                            \                                                                                                \                if (dvb_ca_slot_acquire(ca, slot) == 0) {                                       \                                                                                                \                        /* go through each connection id to see if there is data available */   \                        list_for_each_entry(cacon, &ca->slot_info[slot].connections, next) {    \                                __status = dvb_ringbuffer_avail(&cacon->rx_buffer) >= 3;        \                                if (__status) goto completed;                                   \                        }                                                                       \                        dvb_ca_slot_release(ca, slot);                                          \                }                                                                               \         }                                                                                      \    completed:                                                                                  \         __status;                                                                              \})/** * Function to copy a packet received from the CAM to a buffer supplied by the user. * * @param ca CA instance. * @param file Associated file. * @param buf Destination buffer (userspace). * @param count Size of buffer. * @param ppos Position in file (ignored). * @param _slot Updated to contain the slot received from. * @param connection_id Updated to contain the connection_id received from. * * @return Number of bytes copied, or <0 on error. */static ssize_t dvb_ca_buf_copy_to_user(dvb_ca* ca, struct file *file, char *buf, size_t count, loff_t *ppos, u8* _slot, u8* connection_id) {        dvb_ca_connection* cacon;        int slot;        int packet_size;        int split;        int status;        /* Packets in the buffer have a 3 byte header as follows:           0: MSB of total packet size.           1: LSB 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.        */        /* check there is some space in the user-supplied destination buffer */        if (count == 0) return 0;        /* get a slot/connection to read from */        if ((status = DVB_CA_BUF_COPY_TO_USER_CONDITION) == 0) {                /* if we're in nonblocking mode, exit immediately */                if (file->f_flags & O_NONBLOCK)                        return -EWOULDBLOCK;                /* wait for some data */                if ((status = dvb_ca_wait_event_interruptible(ca->read_queue, DVB_CA_BUF_COPY_TO_USER_CONDITION)) < 0) {                        /* check for errors. No need to relase slot, as we won't have one */                        return status;                }                /* the wait was successful => slot is now acquired */        }        /* check for errors. No need to relase slot, as we won't have one */        if (status < 0) return status;        /* is the packet marked complete? */        if (DVB_RINGBUFFER_PEEK(&cacon->rx_buffer, 2) != 1) {                dvb_ca_slot_release(ca, slot);                return 0;        }        /* get the packet's size */        packet_size = DVB_RINGBUFFER_PEEK(&cacon->rx_buffer,0) << 8;        packet_size |= DVB_RINGBUFFER_PEEK(&cacon->rx_buffer,1);        /* is there enough space in the destination buffer */        if (count < packet_size) {                dvb_ca_slot_release(ca, slot);                return -EINVAL;        }        /* Copy the data into the user buffer */        /* Cannot use dvb_ringbuffer_read() here because we have to ignore the         * three byte packet header. We can't just skip it beforehand, because         * the read COULD -EFAULT, and we'd have to roll back. In the meantime,         * something could have overwritten the packet header we skipped. */        if ((cacon->rx_buffer.pread + 3 + packet_size) > cacon->rx_buffer.size) {                split = cacon->rx_buffer.size - (cacon->rx_buffer.pread + 3);        } else {                split = 0;        }        if (split > 0) {                if (copy_to_user(buf, cacon->rx_buffer.data + cacon->rx_buffer.pread + 3, split)) {                        dvb_ca_slot_release(ca, slot);                        return -EFAULT;                }                if (copy_to_user(buf+split, cacon->rx_buffer.data, packet_size - split)) {                        dvb_ca_slot_release(ca, slot);                        return -EFAULT;                }        } else {                if (copy_to_user(buf, cacon->rx_buffer.data + cacon->rx_buffer.pread + 3, packet_size)) {                        dvb_ca_slot_release(ca, slot);                        return -EFAULT;                }        }        /* the copy was successful => skip the packet header and the packet data */        DVB_RINGBUFFER_SKIP(&cacon->rx_buffer, 3 + packet_size);        /* return number of bytes read */        *_slot = slot;        *connection_id = cacon->connection_id;        dvb_ca_slot_release(ca, slot);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -