📄 dvb_ca.c
字号:
}/** * 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 + -