📄 bttv-driver.c
字号:
return 0;
}
case VIDIOCGTUNER:
{
struct video_tuner *v = arg;
if (UNSET == bttv_tvcards[btv->c.type].tuner)
return -EINVAL;
if (v->tuner) /* Only tuner 0 */
return -EINVAL;
strcpy(v->name, "Television");
v->rangelow = 0;
v->rangehigh = 0x7FFFFFFF;
v->flags = VIDEO_TUNER_PAL|VIDEO_TUNER_NTSC|VIDEO_TUNER_SECAM;
v->mode = btv->tvnorm;
v->signal = (btread(BT848_DSTATUS)&BT848_DSTATUS_HLOC) ? 0xFFFF : 0;
bttv_call_i2c_clients(btv,cmd,v);
return 0;
}
case VIDIOCSTUNER:
{
struct video_tuner *v = arg;
if (v->tuner) /* Only tuner 0 */
return -EINVAL;
if (v->mode >= BTTV_TVNORMS)
return -EINVAL;
down(&btv->lock);
set_tvnorm(btv,v->mode);
bttv_call_i2c_clients(btv,cmd,v);
up(&btv->lock);
return 0;
}
case VIDIOCGCHAN:
{
struct video_channel *v = arg;
unsigned int channel = v->channel;
if (channel >= bttv_tvcards[btv->c.type].video_inputs)
return -EINVAL;
v->tuners=0;
v->flags = VIDEO_VC_AUDIO;
v->type = VIDEO_TYPE_CAMERA;
v->norm = btv->tvnorm;
if (channel == bttv_tvcards[btv->c.type].tuner) {
strcpy(v->name,"Television");
v->flags|=VIDEO_VC_TUNER;
v->type=VIDEO_TYPE_TV;
v->tuners=1;
} else if (channel == btv->svhs) {
strcpy(v->name,"S-Video");
} else {
sprintf(v->name,"Composite%d",channel);
}
return 0;
}
case VIDIOCSCHAN:
{
struct video_channel *v = arg;
unsigned int channel = v->channel;
if (channel >= bttv_tvcards[btv->c.type].video_inputs)
return -EINVAL;
if (v->norm >= BTTV_TVNORMS)
return -EINVAL;
down(&btv->lock);
if (channel == btv->input &&
v->norm == btv->tvnorm) {
/* nothing to do */
up(&btv->lock);
return 0;
}
btv->tvnorm = v->norm;
set_input(btv,v->channel);
up(&btv->lock);
return 0;
}
case VIDIOCGAUDIO:
{
struct video_audio *v = arg;
memset(v,0,sizeof(*v));
strcpy(v->name,"Television");
v->flags |= VIDEO_AUDIO_MUTABLE;
v->mode = VIDEO_SOUND_MONO;
down(&btv->lock);
bttv_call_i2c_clients(btv,cmd,v);
/* card specific hooks */
if (btv->audio_hook)
btv->audio_hook(btv,v,0);
up(&btv->lock);
return 0;
}
case VIDIOCSAUDIO:
{
struct video_audio *v = arg;
unsigned int audio = v->audio;
if (audio >= bttv_tvcards[btv->c.type].audio_inputs)
return -EINVAL;
down(&btv->lock);
audio_mux(btv, (v->flags&VIDEO_AUDIO_MUTE) ? AUDIO_MUTE : AUDIO_UNMUTE);
bttv_call_i2c_clients(btv,cmd,v);
/* card specific hooks */
if (btv->audio_hook)
btv->audio_hook(btv,v,1);
up(&btv->lock);
return 0;
}
/* *** v4l2 *** ************************************************ */
case VIDIOC_ENUMSTD:
{
struct v4l2_standard *e = arg;
unsigned int index = e->index;
if (index >= BTTV_TVNORMS)
return -EINVAL;
v4l2_video_std_construct(e, bttv_tvnorms[e->index].v4l2_id,
bttv_tvnorms[e->index].name);
e->index = index;
return 0;
}
case VIDIOC_G_STD:
{
v4l2_std_id *id = arg;
*id = bttv_tvnorms[btv->tvnorm].v4l2_id;
return 0;
}
case VIDIOC_S_STD:
{
v4l2_std_id *id = arg;
unsigned int i;
for (i = 0; i < BTTV_TVNORMS; i++)
if (*id & bttv_tvnorms[i].v4l2_id)
break;
if (i == BTTV_TVNORMS)
return -EINVAL;
down(&btv->lock);
set_tvnorm(btv,i);
i2c_vidiocschan(btv);
up(&btv->lock);
return 0;
}
case VIDIOC_QUERYSTD:
{
v4l2_std_id *id = arg;
if (btread(BT848_DSTATUS) & BT848_DSTATUS_NUML)
*id = V4L2_STD_625_50;
else
*id = V4L2_STD_525_60;
return 0;
}
case VIDIOC_ENUMINPUT:
{
struct v4l2_input *i = arg;
unsigned int n;
n = i->index;
if (n >= bttv_tvcards[btv->c.type].video_inputs)
return -EINVAL;
memset(i,0,sizeof(*i));
i->index = n;
i->type = V4L2_INPUT_TYPE_CAMERA;
i->audioset = 1;
if (i->index == bttv_tvcards[btv->c.type].tuner) {
sprintf(i->name, "Television");
i->type = V4L2_INPUT_TYPE_TUNER;
i->tuner = 0;
} else if (i->index == btv->svhs) {
sprintf(i->name, "S-Video");
} else {
sprintf(i->name,"Composite%d",i->index);
}
if (i->index == btv->input) {
__u32 dstatus = btread(BT848_DSTATUS);
if (0 == (dstatus & BT848_DSTATUS_PRES))
i->status |= V4L2_IN_ST_NO_SIGNAL;
if (0 == (dstatus & BT848_DSTATUS_HLOC))
i->status |= V4L2_IN_ST_NO_H_LOCK;
}
for (n = 0; n < BTTV_TVNORMS; n++)
i->std |= bttv_tvnorms[n].v4l2_id;
return 0;
}
case VIDIOC_G_INPUT:
{
int *i = arg;
*i = btv->input;
return 0;
}
case VIDIOC_S_INPUT:
{
unsigned int *i = arg;
if (*i > bttv_tvcards[btv->c.type].video_inputs)
return -EINVAL;
down(&btv->lock);
set_input(btv,*i);
i2c_vidiocschan(btv);
up(&btv->lock);
return 0;
}
case VIDIOC_G_TUNER:
{
struct v4l2_tuner *t = arg;
if (UNSET == bttv_tvcards[btv->c.type].tuner)
return -EINVAL;
if (0 != t->index)
return -EINVAL;
down(&btv->lock);
memset(t,0,sizeof(*t));
strcpy(t->name, "Television");
t->type = V4L2_TUNER_ANALOG_TV;
t->rangehigh = 0xffffffffUL;
t->capability = V4L2_TUNER_CAP_NORM;
t->rxsubchans = V4L2_TUNER_SUB_MONO;
if (btread(BT848_DSTATUS)&BT848_DSTATUS_HLOC)
t->signal = 0xffff;
{
/* Hmmm ... */
struct video_audio va;
memset(&va, 0, sizeof(struct video_audio));
bttv_call_i2c_clients(btv, VIDIOCGAUDIO, &va);
if (btv->audio_hook)
btv->audio_hook(btv,&va,0);
if(va.mode & VIDEO_SOUND_STEREO) {
t->audmode = V4L2_TUNER_MODE_STEREO;
t->rxsubchans |= V4L2_TUNER_SUB_STEREO;
}
if(va.mode & VIDEO_SOUND_LANG1) {
t->audmode = V4L2_TUNER_MODE_LANG1;
t->rxsubchans = V4L2_TUNER_SUB_LANG1
| V4L2_TUNER_SUB_LANG2;
}
}
/* FIXME: fill capability+audmode */
up(&btv->lock);
return 0;
}
case VIDIOC_S_TUNER:
{
struct v4l2_tuner *t = arg;
if (UNSET == bttv_tvcards[btv->c.type].tuner)
return -EINVAL;
if (0 != t->index)
return -EINVAL;
down(&btv->lock);
{
struct video_audio va;
memset(&va, 0, sizeof(struct video_audio));
bttv_call_i2c_clients(btv, VIDIOCGAUDIO, &va);
if (t->audmode == V4L2_TUNER_MODE_MONO)
va.mode = VIDEO_SOUND_MONO;
else if (t->audmode == V4L2_TUNER_MODE_STEREO)
va.mode = VIDEO_SOUND_STEREO;
else if (t->audmode == V4L2_TUNER_MODE_LANG1)
va.mode = VIDEO_SOUND_LANG1;
else if (t->audmode == V4L2_TUNER_MODE_LANG2)
va.mode = VIDEO_SOUND_LANG2;
bttv_call_i2c_clients(btv, VIDIOCSAUDIO, &va);
if (btv->audio_hook)
btv->audio_hook(btv,&va,1);
}
up(&btv->lock);
return 0;
}
case VIDIOC_G_FREQUENCY:
{
struct v4l2_frequency *f = arg;
memset(f,0,sizeof(*f));
f->type = V4L2_TUNER_ANALOG_TV;
f->frequency = btv->freq;
return 0;
}
case VIDIOC_S_FREQUENCY:
{
struct v4l2_frequency *f = arg;
if (unlikely(f->tuner != 0))
return -EINVAL;
if (unlikely(f->type != V4L2_TUNER_ANALOG_TV))
return -EINVAL;
down(&btv->lock);
btv->freq = f->frequency;
bttv_call_i2c_clients(btv,VIDIOCSFREQ,&btv->freq);
if (btv->has_matchbox && btv->radio_user)
tea5757_set_freq(btv,btv->freq);
up(&btv->lock);
return 0;
}
default:
return -ENOIOCTLCMD;
}
return 0;
}
static int verify_window(const struct bttv_tvnorm *tvn,
struct v4l2_window *win, int fixup)
{
enum v4l2_field field;
int maxw, maxh;
if (win->w.width < 48 || win->w.height < 32)
return -EINVAL;
if (win->clipcount > 2048)
return -EINVAL;
field = win->field;
maxw = tvn->swidth;
maxh = tvn->sheight;
if (V4L2_FIELD_ANY == field) {
field = (win->w.height > maxh/2)
? V4L2_FIELD_INTERLACED
: V4L2_FIELD_TOP;
}
switch (field) {
case V4L2_FIELD_TOP:
case V4L2_FIELD_BOTTOM:
maxh = maxh / 2;
break;
case V4L2_FIELD_INTERLACED:
break;
default:
return -EINVAL;
}
if (!fixup && (win->w.width > maxw || win->w.height > maxh))
return -EINVAL;
if (win->w.width > maxw)
win->w.width = maxw;
if (win->w.height > maxh)
win->w.height = maxh;
win->field = field;
return 0;
}
static int setup_window(struct bttv_fh *fh, struct bttv *btv,
struct v4l2_window *win, int fixup)
{
struct v4l2_clip *clips = NULL;
int n,size,retval = 0;
if (NULL == fh->ovfmt)
return -EINVAL;
retval = verify_window(&bttv_tvnorms[btv->tvnorm],win,fixup);
if (0 != retval)
return retval;
/* copy clips -- luckily v4l1 + v4l2 are binary
compatible here ...*/
n = win->clipcount;
size = sizeof(*clips)*(n+4);
clips = kmalloc(size,GFP_KERNEL);
if (NULL == clips)
return -ENOMEM;
if (n > 0) {
if (copy_from_user(clips,win->clips,sizeof(struct v4l2_clip)*n)) {
kfree(clips);
return -EFAULT;
}
}
/* clip against screen */
if (NULL != btv->fbuf.base)
n = btcx_screen_clips(btv->fbuf.fmt.width, btv->fbuf.fmt.height,
&win->w, clips, n);
btcx_sort_clips(clips,n);
/* 4-byte alignments */
switch (fh->ovfmt->depth) {
case 8:
case 24:
btcx_align(&win->w, clips, n, 3);
break;
case 16:
btcx_align(&win->w, clips, n, 1);
break;
case 32:
/* no alignment fixups needed */
break;
default:
BUG();
}
down(&fh->cap.lock);
if (fh->ov.clips)
kfree(fh->ov.clips);
fh->ov.clips = clips;
fh->ov.nclips = n;
fh->ov.w = win->w;
fh->ov.field = win->field;
fh->ov.setup_ok = 1;
btv->init.ov.w.width = win->w.width;
btv->init.ov.w.height = win->w.height;
btv->init.ov.field = win->field;
/* update overlay if needed */
retval = 0;
if (check_btres(fh, RESOURCE_OVERLAY)) {
struct bttv_buffer *new;
new = videobuf_alloc(sizeof(*new));
bttv_overlay_risc(btv, &fh->ov, fh->ovfmt, new);
retval = bttv_switch_overlay(btv,fh,new);
}
up(&fh->cap.lock);
return retval;
}
/* ----------------------------------------------------------------------- */
static struct videobuf_queue* bttv_queue(struct bttv_fh *fh)
{
struct videobuf_queue* q = NULL;
switch (fh->type) {
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
q = &fh->cap;
break;
case V4L2_BUF_TYPE_VBI_CAPTURE:
q = &fh->vbi;
break;
default:
BUG();
}
return q;
}
static int bttv_resource(struct bttv_fh *fh)
{
int res = 0;
switch (fh->type) {
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
res = RESOURCE_VIDEO;
break;
case V4L2_BUF_TYPE_VBI_CAPTURE:
res = RESOURCE_VBI;
break;
default:
BUG();
}
return res;
}
static int bttv_switch_type(struct bttv_fh *fh, enum v4l2_buf_type type)
{
struct videobuf_queue *q = bttv_queue(fh);
int res = bttv_resource(fh);
if (check_btres(fh,res))
return -EBUSY;
if (videobuf_queue_is_busy(q))
return -EBUSY;
fh->type = type;
return 0;
}
static int bttv_g_fmt(struct bttv_fh *fh, struct v4l2_format *f)
{
switch (f->type) {
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
memset(&f->fmt.pix,0,sizeof(struct v4l2_pix_format));
f->fmt.pix.width = fh->width;
f->fmt.pix.height = fh->height;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -