cx18-fileops.c

来自「trident tm5600的linux驱动」· C语言 代码 · 共 770 行 · 第 1/2 页

C
770
字号
/* *  cx18 file operation functions * *  Derived from ivtv-fileops.c * *  Copyright (C) 2007  Hans Verkuil <hverkuil@xs4all.nl> * *  This program is free software; you can redistribute it and/or modify *  it under the terms of the GNU General Public License as published by *  the Free Software Foundation; either version 2 of the License, or *  (at your option) any later version. * *  This program is distributed in the hope that it will be useful, *  but WITHOUT ANY WARRANTY; without even the implied warranty of *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the *  GNU General Public License for more details. * *  You should have received a copy of the GNU General Public License *  along with this program; if not, write to the Free Software *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA *  02111-1307  USA */#include "cx18-driver.h"#include "cx18-fileops.h"#include "cx18-i2c.h"#include "cx18-queue.h"#include "cx18-vbi.h"#include "cx18-audio.h"#include "cx18-mailbox.h"#include "cx18-scb.h"#include "cx18-streams.h"#include "cx18-controls.h"#include "cx18-ioctl.h"#include "cx18-cards.h"/* This function tries to claim the stream for a specific file descriptor.   If no one else is using this stream then the stream is claimed and   associated VBI streams are also automatically claimed.   Possible error returns: -EBUSY if someone else has claimed   the stream or 0 on success. */static int cx18_claim_stream(struct cx18_open_id *id, int type){	struct cx18 *cx = id->cx;	struct cx18_stream *s = &cx->streams[type];	struct cx18_stream *s_vbi;	int vbi_type;	if (test_and_set_bit(CX18_F_S_CLAIMED, &s->s_flags)) {		/* someone already claimed this stream */		if (s->id == id->open_id) {			/* yes, this file descriptor did. So that's OK. */			return 0;		}		if (s->id == -1 && type == CX18_ENC_STREAM_TYPE_VBI) {			/* VBI is handled already internally, now also assign			   the file descriptor to this stream for external			   reading of the stream. */			s->id = id->open_id;			CX18_DEBUG_INFO("Start Read VBI\n");			return 0;		}		/* someone else is using this stream already */		CX18_DEBUG_INFO("Stream %d is busy\n", type);		return -EBUSY;	}	s->id = id->open_id;	/* CX18_DEC_STREAM_TYPE_MPG needs to claim CX18_DEC_STREAM_TYPE_VBI,	   CX18_ENC_STREAM_TYPE_MPG needs to claim CX18_ENC_STREAM_TYPE_VBI	   (provided VBI insertion is on and sliced VBI is selected), for all	   other streams we're done */	if (type == CX18_ENC_STREAM_TYPE_MPG &&		   cx->vbi.insert_mpeg && cx->vbi.sliced_in->service_set) {		vbi_type = CX18_ENC_STREAM_TYPE_VBI;	} else {		return 0;	}	s_vbi = &cx->streams[vbi_type];	set_bit(CX18_F_S_CLAIMED, &s_vbi->s_flags);	/* mark that it is used internally */	set_bit(CX18_F_S_INTERNAL_USE, &s_vbi->s_flags);	return 0;}/* This function releases a previously claimed stream. It will take into   account associated VBI streams. */static void cx18_release_stream(struct cx18_stream *s){	struct cx18 *cx = s->cx;	struct cx18_stream *s_vbi;	s->id = -1;	if (s->type == CX18_ENC_STREAM_TYPE_VBI &&		test_bit(CX18_F_S_INTERNAL_USE, &s->s_flags)) {		/* this stream is still in use internally */		return;	}	if (!test_and_clear_bit(CX18_F_S_CLAIMED, &s->s_flags)) {		CX18_DEBUG_WARN("Release stream %s not in use!\n", s->name);		return;	}	cx18_flush_queues(s);	/* CX18_ENC_STREAM_TYPE_MPG needs to release CX18_ENC_STREAM_TYPE_VBI,	   for all other streams we're done */	if (s->type == CX18_ENC_STREAM_TYPE_MPG)		s_vbi = &cx->streams[CX18_ENC_STREAM_TYPE_VBI];	else		return;	/* clear internal use flag */	if (!test_and_clear_bit(CX18_F_S_INTERNAL_USE, &s_vbi->s_flags)) {		/* was already cleared */		return;	}	if (s_vbi->id != -1) {		/* VBI stream still claimed by a file descriptor */		return;	}	clear_bit(CX18_F_S_CLAIMED, &s_vbi->s_flags);	cx18_flush_queues(s_vbi);}static void cx18_dualwatch(struct cx18 *cx){	struct v4l2_tuner vt;	u16 new_bitmap;	u16 new_stereo_mode;	const u16 stereo_mask = 0x0300;	const u16 dual = 0x0200;	u32 h;	new_stereo_mode = cx->params.audio_properties & stereo_mask;	memset(&vt, 0, sizeof(vt));	cx18_call_i2c_clients(cx, VIDIOC_G_TUNER, &vt);	if (vt.audmode == V4L2_TUNER_MODE_LANG1_LANG2 &&			(vt.rxsubchans & V4L2_TUNER_SUB_LANG2))		new_stereo_mode = dual;	if (new_stereo_mode == cx->dualwatch_stereo_mode)		return;	new_bitmap = new_stereo_mode			| (cx->params.audio_properties & ~stereo_mask);	CX18_DEBUG_INFO("dualwatch: change stereo flag from 0x%x to 0x%x. "			"new audio_bitmask=0x%ux\n",			cx->dualwatch_stereo_mode, new_stereo_mode, new_bitmap);	h = cx18_find_handle(cx);	if (h == CX18_INVALID_TASK_HANDLE) {		CX18_DEBUG_INFO("dualwatch: can't find valid task handle\n");		return;	}	if (cx18_vapi(cx,		      CX18_CPU_SET_AUDIO_PARAMETERS, 2, h, new_bitmap) == 0) {		cx->dualwatch_stereo_mode = new_stereo_mode;		return;	}	CX18_DEBUG_INFO("dualwatch: changing stereo flag failed\n");}#if 0static void cx18_update_pgm_info(struct cx18 *cx){	u32 wr_idx = (cx18_read_enc(cx, cx->pgm_info_offset)			- cx->pgm_info_offset - 4) / 24;	int cnt;	int i = 0;	if (wr_idx >= cx->pgm_info_num) {		CX18_DEBUG_WARN("Invalid PGM index %d (>= %d)\n", wr_idx, cx->pgm_info_num);		return;	}	cnt = (wr_idx + cx->pgm_info_num - cx->pgm_info_write_idx) % cx->pgm_info_num;	while (i < cnt) {		int idx = (cx->pgm_info_write_idx + i) % cx->pgm_info_num;		struct v4l2_enc_idx_entry *e = cx->pgm_info + idx;		u32 addr = cx->pgm_info_offset + 4 + idx * 24;		const int mapping[] = { V4L2_ENC_IDX_FRAME_P, V4L2_ENC_IDX_FRAME_I, V4L2_ENC_IDX_FRAME_B, 0 };		e->offset = cx18_read_enc(cx, addr + 4)				+ ((u64)cx18_read_enc(cx, addr + 8) << 32);		if (e->offset > cx->mpg_data_received)			break;		e->offset += cx->vbi_data_inserted;		e->length = cx18_read_enc(cx, addr);		e->pts = cx18_read_enc(cx, addr + 16)			 + ((u64)(cx18_read_enc(cx, addr + 20) & 1) << 32);		e->flags = mapping[cx18_read_enc(cx, addr + 12) & 3];		i++;	}	cx->pgm_info_write_idx = (cx->pgm_info_write_idx + i) % cx->pgm_info_num;}#endifstatic 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 0			/* Process pending program info updates and pending			   VBI data */			cx18_update_pgm_info(cx);#endif			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_enqueue(s_vbi, buf, &s_vbi->q_free);				}			}			buf = &cx->vbi.sliced_mpeg_buf;			if (buf->readpos != buf->bytesused)				return buf;		}		/* do we have leftover data? */		buf = cx18_dequeue(s, &s->q_io);		if (buf)			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;		}	}}static void cx18_setup_sliced_vbi_buf(struct cx18 *cx){	int idx = cx->vbi.inserted_frame % CX18_VBI_FRAMES;	cx->vbi.sliced_mpeg_buf.buf = cx->vbi.sliced_mpeg_data[idx];	cx->vbi.sliced_mpeg_buf.bytesused = cx->vbi.sliced_mpeg_size[idx];	cx->vbi.sliced_mpeg_buf.readpos = 0;}static size_t cx18_copy_buf_to_user(struct cx18_stream *s,		struct cx18_buffer *buf, char __user *ubuf, size_t ucount){	struct cx18 *cx = s->cx;	size_t len = buf->bytesused - buf->readpos;	if (len > ucount)		len = ucount;	if (cx->vbi.insert_mpeg && s->type == CX18_ENC_STREAM_TYPE_MPG &&	    cx->vbi.sliced_in->service_set && buf != &cx->vbi.sliced_mpeg_buf) {		const char *start = buf->buf + buf->readpos;		const char *p = start + 1;		const u8 *q;		u8 ch = cx->search_pack_header ? 0xba : 0xe0;		int stuffing, i;		while (start + len > p) {			q = memchr(p, 0, start + len - p);			if (q == NULL)				break;			p = q + 1;			if ((char *)q + 15 >= buf->buf + buf->bytesused ||			    q[1] != 0 || q[2] != 1 || q[3] != ch)				continue;			if (!cx->search_pack_header) {				if ((q[6] & 0xc0) != 0x80)					continue;				if (((q[7] & 0xc0) == 0x80 &&				     (q[9] & 0xf0) == 0x20) ||				    ((q[7] & 0xc0) == 0xc0 &&				     (q[9] & 0xf0) == 0x30)) {					ch = 0xba;					cx->search_pack_header = 1;					p = q + 9;				}				continue;			}			stuffing = q[13] & 7;			/* all stuffing bytes must be 0xff */			for (i = 0; i < stuffing; i++)				if (q[14 + i] != 0xff)					break;			if (i == stuffing &&			    (q[4] & 0xc4) == 0x44 &&			    (q[12] & 3) == 3 &&			    q[14 + stuffing] == 0 &&			    q[15 + stuffing] == 0 &&			    q[16 + stuffing] == 1) {				cx->search_pack_header = 0;				len = (char *)q - start;				cx18_setup_sliced_vbi_buf(cx);				break;			}		}	}	if (copy_to_user(ubuf, (u8 *)buf->buf + buf->readpos, len)) {		CX18_DEBUG_WARN("copy %zd bytes to user failed for %s\n",				len, s->name);		return -EFAULT;	}	buf->readpos += len;	if (s->type == CX18_ENC_STREAM_TYPE_MPG &&	    buf != &cx->vbi.sliced_mpeg_buf)		cx->mpg_data_received += len;	return len;}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 &&	    cx->vbi.sliced_in->service_set)		single_frame = 1;	for (;;) {		struct cx18_buffer *buf;		int rc;

⌨️ 快捷键说明

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