static ssize_t cx18_read(struct cx18_stream *s, char __user *ubuf, size_t tot_count, int non_block) { struct cx18 *cx = s->cx; size_t tot_written = 0; int single_frame = 0; if (atomic_read(&cx->ana_capturing) == 0 && s->id == -1) { /* shouldn't happen */ CX18_DEBUG_WARN("Stream %s not initialized before read\n", s->name); return -EIO; } /* Each VBI buffer is one frame, the v4l2 API says that for VBI the frames should arrive one-by-one, so make sure we never output more than one VBI frame at a time */ if (s->type == CX18_ENC_STREAM_TYPE_VBI && !cx18_raw_vbi(cx)) single_frame = 1; for (;;) { struct cx18_buffer *buf; int rc; buf = cx18_get_buffer(s, non_block, &rc); /* if there is no data available... */ if (buf == NULL) { /* if we got data, then return that regardless */ if (tot_written) break; /* EOS condition */ if (rc == 0) { clear_bit(CX18_F_S_STREAMOFF, &s->s_flags); clear_bit(CX18_F_S_APPL_IO, &s->s_flags); cx18_release_stream(s); } /* set errno */ return rc; } rc = cx18_copy_buf_to_user(s, buf, ubuf + tot_written, tot_count - tot_written); if (buf != &cx->vbi.sliced_mpeg_buf) { if (buf->readpos == buf->bytesused) cx18_stream_put_buf_fw(s, buf); else cx18_push(s, buf, &s->q_full); } else if (buf->readpos == buf->bytesused) { int idx = cx->vbi.inserted_frame % CX18_VBI_FRAMES; cx->vbi.sliced_mpeg_size[idx] = 0; cx->vbi.inserted_frame++; cx->vbi_data_inserted += buf->bytesused; } if (rc < 0) return rc; tot_written += rc; if (tot_written == tot_count || single_frame) break; } return tot_written; }
static void epu_dma_done(struct cx18 *cx, struct cx18_epu_work_order *order) { u32 handle, mdl_ack_count, id; struct cx18_mailbox *mb; struct cx18_mdl_ack *mdl_ack; struct cx18_stream *s; struct cx18_buffer *buf; int i; mb = &order->mb; handle = mb->args[0]; s = cx18_handle_to_stream(cx, handle); if (s == NULL) { CX18_WARN("Got DMA done notification for unknown/inactive" " handle %d, %s mailbox seq no %d\n", handle, (order->flags & CX18_F_EWO_MB_STALE_UPON_RECEIPT) ? "stale" : "good", mb->request); return; } mdl_ack_count = mb->args[2]; mdl_ack = order->mdl_ack; for (i = 0; i < mdl_ack_count; i++, mdl_ack++) { id = mdl_ack->id; /* * Simple integrity check for processing a stale (and possibly * inconsistent mailbox): make sure the buffer id is in the * valid range for the stream. * * We go through the trouble of dealing with stale mailboxes * because most of the time, the mailbox data is still valid and * unchanged (and in practice the firmware ping-pongs the * two mdl_ack buffers so mdl_acks are not stale). * * There are occasions when we get a half changed mailbox, * which this check catches for a handle & id mismatch. If the * handle and id do correspond, the worst case is that we * completely lost the old buffer, but pick up the new buffer * early (but the new mdl_ack is guaranteed to be good in this * case as the firmware wouldn't point us to a new mdl_ack until * it's filled in). * * cx18_queue_get buf() will detect the lost buffers * and send them back to q_free for fw rotation eventually. */ if ((order->flags & CX18_F_EWO_MB_STALE_UPON_RECEIPT) && !(id >= s->mdl_offset && id < (s->mdl_offset + s->buffers))) { CX18_WARN("Fell behind! Ignoring stale mailbox with " " inconsistent data. Lost buffer for mailbox " "seq no %d\n", mb->request); break; } buf = cx18_queue_get_buf(s, id, mdl_ack->data_used); CX18_DEBUG_HI_DMA("DMA DONE for %s (buffer %d)\n", s->name, id); if (buf == NULL) { CX18_WARN("Could not find buf %d for stream %s\n", id, s->name); /* Put as many buffers as possible back into fw use */ cx18_stream_load_fw_queue(s); continue; } if (s->type == CX18_ENC_STREAM_TYPE_TS && s->dvb.enabled) { CX18_DEBUG_HI_DMA("TS recv bytesused = %d\n", buf->bytesused); dvb_dmx_swfilter(&s->dvb.demux, buf->buf, buf->bytesused); } /* Put as many buffers as possible back into fw use */ cx18_stream_load_fw_queue(s); /* Put back TS buffer, since it was removed from all queues */ if (s->type == CX18_ENC_STREAM_TYPE_TS) cx18_stream_put_buf_fw(s, buf); } wake_up(&cx->dma_waitq); if (s->id != -1) wake_up(&s->waitq); }
static struct cx18_buffer *cx18_get_buffer(struct cx18_stream *s, int non_block, int *err) { struct cx18 *cx = s->cx; struct cx18_stream *s_vbi = &cx->streams[CX18_ENC_STREAM_TYPE_VBI]; struct cx18_buffer *buf; DEFINE_WAIT(wait); *err = 0; while (1) { if (s->type == CX18_ENC_STREAM_TYPE_MPG) { if (time_after(jiffies, cx->dualwatch_jiffies + msecs_to_jiffies(1000))) { cx->dualwatch_jiffies = jiffies; cx18_dualwatch(cx); } if (test_bit(CX18_F_S_INTERNAL_USE, &s_vbi->s_flags) && !test_bit(CX18_F_S_APPL_IO, &s_vbi->s_flags)) { while ((buf = cx18_dequeue(s_vbi, &s_vbi->q_full))) { /* byteswap and process VBI data */ cx18_process_vbi_data(cx, buf, s_vbi->dma_pts, s_vbi->type); cx18_stream_put_buf_fw(s_vbi, buf); } } buf = &cx->vbi.sliced_mpeg_buf; if (buf->readpos != buf->bytesused) return buf; } /* do we have new data? */ buf = cx18_dequeue(s, &s->q_full); if (buf) { if (!test_and_clear_bit(CX18_F_B_NEED_BUF_SWAP, &buf->b_flags)) return buf; if (s->type == CX18_ENC_STREAM_TYPE_MPG) /* byteswap MPG data */ cx18_buf_swap(buf); else { /* byteswap and process VBI data */ cx18_process_vbi_data(cx, buf, s->dma_pts, s->type); } return buf; } /* return if end of stream */ if (!test_bit(CX18_F_S_STREAMING, &s->s_flags)) { CX18_DEBUG_INFO("EOS %s\n", s->name); return NULL; } /* return if file was opened with O_NONBLOCK */ if (non_block) { *err = -EAGAIN; return NULL; } /* wait for more data to arrive */ prepare_to_wait(&s->waitq, &wait, TASK_INTERRUPTIBLE); /* New buffers might have become available before we were added to the waitqueue */ if (!atomic_read(&s->q_full.buffers)) schedule(); finish_wait(&s->waitq, &wait); if (signal_pending(current)) { /* return if a signal was received */ CX18_DEBUG_INFO("User stopped %s\n", s->name); *err = -EINTR; return NULL; } } }