extractedLnx/linux-2.5.54/drivers/media/video/zr36067.c_do_zoran_ioctl.c
static int do_zoran_ioctl(struct zoran *zr, unsigned int cmd,
void *arg)
{
switch (cmd) {
case VIDIOCGCAP:
{
struct video_capability b;
DEBUG2(printk("%s: ioctl VIDIOCGCAP\n", zr->name));
strncpy(b.name, zr->video_dev.name,
sizeof(b.name));
b.type =
VID_TYPE_CAPTURE | VID_TYPE_OVERLAY |
VID_TYPE_CLIPPING | VID_TYPE_FRAMERAM |
VID_TYPE_SCALES;
/* theoretically we could also flag VID_TYPE_SUBCAPTURE
but this is not even implemented in the BTTV driver */
if (zr->card == DC10 || zr->card == DC10plus) {
b.channels = 3; /* composite, svhs, internal */
} else {
b.channels = 2; /* composite, svhs */
}
b.audios = 0;
b.maxwidth = BUZ_MAX_WIDTH;
b.maxheight = BUZ_MAX_HEIGHT;
b.minwidth = BUZ_MIN_WIDTH;
b.minheight = BUZ_MIN_HEIGHT;
if (copy_to_user(arg, &b, sizeof(b))) {
return -EFAULT;
}
return 0;
}
break;
case VIDIOCGCHAN:
{
struct video_channel v;
if (copy_from_user(&v, arg, sizeof(v))) {
return -EFAULT;
}
DEBUG2(printk
("%s: ioctl VIDIOCGCHAN for channel %d\n",
zr->name, v.channel));
switch (v.channel) {
case 0:
strcpy(v.name, "Composite");
break;
case 1:
strcpy(v.name, "SVHS");
break;
case 2:
if (zr->card == DC10
|| zr->card == DC10plus) {
strcpy(v.name, "Internal/comp");
break;
}
default:
DEBUG1(printk(KERN_ERR
"%s: VIDIOCGCHAN on not existing channel %d\n",
zr->name, v.channel));
return -EINVAL;
}
v.tuners = 0;
v.flags = 0;
v.type = VIDEO_TYPE_CAMERA;
v.norm = zr->params.norm;
if (copy_to_user(arg, &v, sizeof(v))) {
return -EFAULT;
}
return 0;
}
break;
/* RJ: the documentation at http://roadrunner.swansea.linux.org.uk/v4lapi.shtml says:
* "The VIDIOCSCHAN ioctl takes an integer argument and switches the capture to this input."
* ^^^^^^^
* The famos BTTV driver has it implemented with a struct video_channel argument
* and we follow it for compatibility reasons
*
* BTW: this is the only way the user can set the norm!
*/
case VIDIOCSCHAN:
{
struct video_channel v;
int input;
int on, res;
int encoder_norm;
if (copy_from_user(&v, arg, sizeof(v))) {
return -EFAULT;
}
if (zr->codec_mode != BUZ_MODE_IDLE) {
if (v.norm != zr->params.norm
|| v.channel != zr->params.input) {
DEBUG1(printk(KERN_ERR
"%s: VIDIOCSCHAN called while the card in capture/playback mode\n",
zr->name));
return -EINVAL;
} else {
DEBUG1(printk(BUZ_WARNING
"%s: Warning: VIDIOCSCHAN called while the card in capture/playback mode\n",
zr->name));
}
}
DEBUG2(printk
("%s: ioctl VIDIOCSCHAN: channel=%d, norm=%d\n",
zr->name, v.channel, v.norm));
switch (v.channel) {
case 0:
if (zr->card == BUZ)
input = 3;
else
input = 0;
break;
case 1:
input = 7;
break;
case 2:
if (zr->card == DC10
|| zr->card == DC10plus) {
input = 5;
break;
}
default:
DEBUG1(printk(KERN_ERR
"%s: VIDIOCSCHAN on not existing channel %d\n",
zr->name, v.channel));
return -EINVAL;
break;
}
if (lock_norm && v.norm != zr->params.norm) {
if (lock_norm > 1) {
DEBUG1(printk(KERN_WARNING
"%s: VIDIOCSCHAN: TV standard is locked, can not switch norm.\n",
zr->name));
return -EINVAL;
} else {
DEBUG1(printk(KERN_WARNING
"%s: VIDIOCSCHAN: TV standard is locked, norm was not changed.\n",
zr->name));
v.norm = zr->params.norm;
}
}
if(v.norm >= 2)
return -EINVAL;
if (!cardnorms[zr->card][v.norm]) {
DEBUG1(printk(KERN_ERR
"%s: VIDIOCSCHAN with not supported norm %d\n",
zr->name, v.norm));
return -EOPNOTSUPP;
break;
}
encoder_norm = v.norm;
zr->params.norm = v.norm;
zr->params.input = v.channel;
zr->timing = cardnorms[zr->card][zr->params.norm];
/* We switch overlay off and on since a change in the norm
needs different VFE settings */
on = zr->v4l_overlay_active
&& !zr->v4l_memgrab_active;
if (on)
zr36057_overlay(zr, 0);
set_videobus_enable(zr, 0);
i2c_control_device(&zr->i2c,
I2C_DRIVERID_VIDEODECODER,
DECODER_SET_INPUT, &input);
i2c_control_device(&zr->i2c,
I2C_DRIVERID_VIDEODECODER,
DECODER_SET_NORM,
&zr->params.norm);
i2c_control_device(&zr->i2c,
I2C_DRIVERID_VIDEOENCODER,
ENCODER_SET_NORM,
&encoder_norm);
set_videobus_enable(zr, 1);
if (on)
zr36057_overlay(zr, 1);
/* Make sure the changes come into effect */
res = wait_grab_pending(zr);
if (res)
return res;
return 0;
}
break;
case VIDIOCGTUNER:
{
DEBUG1(printk(KERN_ERR
"%s: ioctl VIDIOCGTUNER not supported\n",
zr->name));
return -EINVAL;
}
break;
case VIDIOCSTUNER:
{
DEBUG1(printk(KERN_ERR
"%s: ioctl VIDIOCSTUNER not supported\n",
zr->name));
return -EINVAL;
}
break;
case VIDIOCGPICT:
{
struct video_picture p = zr->picture;
DEBUG2(printk
("%s: ioctl VIDIOCGPICT\n", zr->name));
p.depth = zr->buffer.depth;
switch (zr->buffer.depth) {
case 15:
p.palette = VIDEO_PALETTE_RGB555;
break;
case 16:
p.palette = VIDEO_PALETTE_RGB565;
break;
case 24:
p.palette = VIDEO_PALETTE_RGB24;
break;
case 32:
p.palette = VIDEO_PALETTE_RGB32;
break;
}
if (copy_to_user(arg, &p, sizeof(p))) {
return -EFAULT;
}
return 0;
}
break;
case VIDIOCSPICT:
{
struct video_picture p;
if (copy_from_user(&p, arg, sizeof(p))) {
return -EFAULT;
}
i2c_control_device(&zr->i2c,
I2C_DRIVERID_VIDEODECODER,
DECODER_SET_PICTURE, &p);
DEBUG2(printk
("%s: ioctl VIDIOCSPICT bri=%d hue=%d col=%d con=%d dep=%d pal=%d\n",
zr->name, p.brightness, p.hue, p.colour,
p.contrast, p.depth, p.palette));
/* The depth and palette values have no meaning to us,
should we return -EINVAL if they don't fit ? */
zr->picture = p;
return 0;
}
break;
case VIDIOCCAPTURE:
{
int v, res;
if (copy_from_user(&v, arg, sizeof(v))) {
return -EFAULT;
}
DEBUG2(printk
("%s: ioctl VIDIOCCAPTURE: %d\n", zr->name,
v));
/* If there is nothing to do, return immediatly */
if ((v && zr->v4l_overlay_active)
|| (!v && !zr->v4l_overlay_active))
return 0;
if (v == 0) {
zr->v4l_overlay_active = 0;
if (!zr->v4l_memgrab_active)
zr36057_overlay(zr, 0);
/* When a grab is running, the video simply won't be switched on any more */
} else {
if (!zr->buffer_set || !zr->window_set) {
DEBUG1(printk(KERN_ERR
"%s: VIDIOCCAPTURE: buffer or window not set\n",
zr->name));
return -EINVAL;
}
zr->v4l_overlay_active = 1;
if (!zr->v4l_memgrab_active)
zr36057_overlay(zr, 1);
/* When a grab is running, the video will be switched on when grab is finished */
}
/* Make sure the changes come into effect */
res = wait_grab_pending(zr);
if (res)
return res;
return 0;
}
break;
case VIDIOCGWIN:
{
DEBUG2(printk("%s: ioctl VIDIOCGWIN\n", zr->name));
if (copy_to_user
(arg, &zr->window, sizeof(zr->window))) {
return -EFAULT;
}
return 0;
}
break;
case VIDIOCSWIN:
{
struct video_clip *vcp;
struct video_window vw;
struct tvnorm *tvn;
int on, end, res, Wa, Ha;
tvn = zr->timing;
Wa = tvn->Wa;
Ha = tvn->Ha;
if (copy_from_user(&vw, arg, sizeof(vw))) {
return -EFAULT;
}
DEBUG2(printk
("%s: ioctl VIDIOCSWIN: x=%d y=%d w=%d h=%d clipcount=%d\n",
zr->name, vw.x, vw.y, vw.width, vw.height,
vw.clipcount));
if (!zr->buffer_set) {
DEBUG1(printk(KERN_ERR
"%s: VIDIOCSWIN: frame buffer has to be set first\n",
zr->name));
return -EINVAL;
}
/*
* The video front end needs 4-byte alinged line sizes, we correct that
* silently here if necessary
*/
if (zr->buffer.depth == 15
|| zr->buffer.depth == 16) {
end = (vw.x + vw.width) & ~1; /* round down */
vw.x = (vw.x + 1) & ~1; /* round up */
vw.width = end - vw.x;
}
if (zr->buffer.depth == 24) {
end = (vw.x + vw.width) & ~3; /* round down */
vw.x = (vw.x + 3) & ~3; /* round up */
vw.width = end - vw.x;
}
if (vw.width > Wa)
vw.width = Wa;
if (vw.height > Ha)
vw.height = Ha;
/* Check for vaild parameters */
if (vw.width < BUZ_MIN_WIDTH
|| vw.height < BUZ_MIN_HEIGHT
|| vw.width > BUZ_MAX_WIDTH
|| vw.height > BUZ_MAX_HEIGHT) {
DEBUG1(printk(KERN_ERR
"%s: VIDIOCSWIN: width = %d or height = %d invalid\n",
zr->name, vw.width, vw.height));
return -EINVAL;
}
zr->window.x = vw.x;
zr->window.y = vw.y;
zr->window.width = vw.width;
zr->window.height = vw.height;
zr->window.chromakey = 0;
zr->window.flags = 0; // RJ: Is this intended for interlace on/off ?
zr->window.clips = NULL;
/*
* If an overlay is running, we have to switch it off
* and switch it on again in order to get the new settings in effect.
*
* We also want to avoid that the overlay mask is written
* when an overlay is running.
*/
on = zr->v4l_overlay_active
&& !zr->v4l_memgrab_active;
if (on)
zr36057_overlay(zr, 0);
/*
* Write the overlay mask if clips are wanted.
*/
if (vw.clipcount < 0 || vw.clipcount > 2048)
return -EINVAL;
if (vw.clipcount) {
vcp =
vmalloc(sizeof(struct video_clip) *
(vw.clipcount + 4));
if (vcp == NULL) {
printk(KERN_ERR
"%s: zoran_ioctl: Alloc of clip mask failed\n",
zr->name);
return -ENOMEM;
}
if (copy_from_user
(vcp, vw.clips,
sizeof(struct video_clip) *
vw.clipcount)) {
vfree(vcp);
return -EFAULT;
}
write_overlay_mask(zr, vcp, vw.clipcount);
vfree(vcp);
}
zr->window.clipcount = vw.clipcount;
if (on)
zr36057_overlay(zr, 1);
zr->window_set = 1;
/* Make sure the changes come into effect */
res = wait_grab_pending(zr);
if (res)
return res;
return 0;
}
break;
case VIDIOCGFBUF:
{
DEBUG2(printk
("%s: ioctl VIDIOCGFBUF\n", zr->name));
if (copy_to_user
(arg, &zr->buffer, sizeof(zr->buffer))) {
return -EFAULT;
}
return 0;
}
break;
case VIDIOCSFBUF:
{
struct video_buffer v;
/* RJ: Isn't this too restrictive? As long as the user doesn't set
the base address it shouldn't be too dangerous */
if (!capable(CAP_SYS_ADMIN)) {
DEBUG1(printk(KERN_ERR
"%s: Only the superuser may issue VIDIOCSFBUF ioctl\n",
zr->name));
return -EPERM;
}
if (copy_from_user(&v, arg, sizeof(v))) {
return -EFAULT;
}
DEBUG2(printk
("%s: ioctl VIDIOCSFBUF: base=0x%x w=%d h=%d depth=%d bpl=%d\n",
zr->name, (u32) v.base, v.width, v.height,
v.depth, v.bytesperline));
if (zr->v4l_overlay_active) {
/* Has the user gotten crazy ... ? */
DEBUG1(printk(KERN_ERR
"%s: VIDIOCSFBUF not allowed when overlay active\n",
zr->name));
return -EINVAL;
}
if (v.depth != 15 && v.depth != 16 && v.depth != 24
&& v.depth != 32) {
DEBUG1(printk(KERN_ERR
"%s: VIDIOCSFBUF: depth=%d not supported\n",
zr->name, v.depth));
return -EINVAL;
}
if (v.height <= 0 || v.width <= 0
|| v.bytesperline <= 0) {
DEBUG1(printk(KERN_ERR
"%s: VIDIOCSFBUF: invalid height/width/bpl value\n",
zr->name));
return -EINVAL;
}
if (v.bytesperline & 3) {
DEBUG1(printk(KERN_ERR
"%s: VIDIOCSFBUF: bytesperline must be 4-byte aligned\n",
zr->name));
return -EINVAL;
}
if (v.base) {
zr->buffer.base =
(void *) ((unsigned long) v.base & ~3);
}
zr->buffer.height = v.height;
zr->buffer.width = v.width;
zr->buffer.depth = v.depth;
zr->buffer.bytesperline = v.bytesperline;
if (zr->buffer.base)
zr->buffer_set = 1;
zr->window_set = 0; /* The user should set new window parameters */
return 0;
}
break;
/* RJ: what is VIDIOCKEY intended to do ??? */
case VIDIOCKEY:
{
/* Will be handled higher up .. */
DEBUG2(printk("%s: ioctl VIDIOCKEY\n", zr->name));
return 0;
}
break;
case VIDIOCGFREQ:
{
DEBUG1(printk(KERN_ERR
"%s: ioctl VIDIOCGFREQ not supported\n",
zr->name));
return -EINVAL;
}
break;
case VIDIOCSFREQ:
{
DEBUG1(printk(KERN_ERR
"%s: ioctl VIDIOCSFREQ not supported\n",
zr->name));
return -EINVAL;
}
break;
case VIDIOCGAUDIO:
{
DEBUG1(printk(KERN_ERR
"%s: ioctl VIDIOCGAUDIO not supported\n",
zr->name));
return -EINVAL;
}
break;
case VIDIOCSAUDIO:
{
DEBUG1(printk(KERN_ERR
"%s: ioctl VIDIOCSAUDIO not supported\n",
zr->name));
return -EINVAL;
}
break;
case VIDIOCSYNC:
{
int v;
if (copy_from_user(&v, arg, sizeof(v))) {
return -EFAULT;
}
DEBUG3(printk
("%s: ioctl VIDIOCSYNC %d\n", zr->name, v));
return v4l_sync(zr, v);
}
break;
case VIDIOCMCAPTURE:
{
struct video_mmap vm;
if (copy_from_user
((void *) &vm, (void *) arg, sizeof(vm))) {
return -EFAULT;
}
DEBUG2(printk
("%s: ioctl VIDIOCMCAPTURE frame=%d geom=%dx%d fmt=%d\n",
zr->name, vm.frame, vm.width, vm.height,
vm.format));
return v4l_grab(zr, &vm);
}
break;
case VIDIOCGMBUF:
{
struct video_mbuf vm;
int i;
DEBUG2(printk
("%s: ioctl VIDIOCGMBUF\n", zr->name));
vm.size = v4l_nbufs * v4l_bufsize;
vm.frames = v4l_nbufs;
for (i = 0; i < v4l_nbufs; i++) {
vm.offsets[i] = i * v4l_bufsize;
}
/* The next mmap will map the V4L buffers */
zr->map_mjpeg_buffers = 0;
if (copy_to_user(arg, &vm, sizeof(vm))) {
return -EFAULT;
}
return 0;
}
break;
case VIDIOCGUNIT:
{
struct video_unit vu;
DEBUG2(printk
("%s: ioctl VIDIOCGUNIT\n", zr->name));
vu.video = zr->video_dev.minor;
vu.vbi = VIDEO_NO_UNIT;
vu.radio = VIDEO_NO_UNIT;
vu.audio = VIDEO_NO_UNIT;
vu.teletext = VIDEO_NO_UNIT;
if (copy_to_user(arg, &vu, sizeof(vu))) {
return -EFAULT;
}
return 0;
}
break;
/*
* RJ: In principal we could support subcaptures for V4L grabbing.
* Not even the famous BTTV driver has them, however.
* If there should be a strong demand, one could consider
* to implement them.
*/
case VIDIOCGCAPTURE:
{
DEBUG1(printk(KERN_ERR
"%s: ioctl VIDIOCGCAPTURE not supported\n",
zr->name));
return -EINVAL;
}
break;
case VIDIOCSCAPTURE:
{
DEBUG1(printk(KERN_ERR
"%s: ioctl VIDIOCSCAPTURE not supported\n",
zr->name));
return -EINVAL;
}
break;
case BUZIOC_G_PARAMS:
{
DEBUG2(printk
("%s: ioctl BUZIOC_G_PARAMS\n", zr->name));
if (copy_to_user
(arg, &(zr->params), sizeof(zr->params))) {
return -EFAULT;
}
return 0;
}
break;
case BUZIOC_S_PARAMS:
{
struct zoran_params bp;
/* int input, on; */
if (zr->codec_mode != BUZ_MODE_IDLE) {
DEBUG1(printk(KERN_ERR
"%s: BUZIOC_S_PARAMS called but Buz in capture/playback mode\n",
zr->name));
return -EINVAL;
}
if (copy_from_user(&bp, arg, sizeof(bp))) {
return -EFAULT;
}
DEBUG2(printk
("%s: ioctl BUZIOC_S_PARAMS\n", zr->name));
/* Check the params first before overwriting our internal values */
if (zoran_check_params(zr, &bp))
return -EINVAL;
zr->params = bp;
/* Make changes of input and norm go into effect immediatly */
/* We switch overlay off and on since a change in the norm
needs different VFE settings */
if (copy_to_user(arg, &bp, sizeof(bp))) {
return -EFAULT;
}
return 0;
}
break;
case BUZIOC_REQBUFS:
{
struct zoran_requestbuffers br;
if (zr->jpg_buffers_allocated) {
DEBUG1(printk(KERN_ERR
"%s: BUZIOC_REQBUFS: buffers allready allocated\n",
zr->name));
return -EINVAL;
}
if (copy_from_user(&br, arg, sizeof(br))) {
return -EFAULT;
}
DEBUG2(printk
("%s: ioctl BUZIOC_REQBUFS count = %lu size=%lu\n",
zr->name, br.count, br.size));
/* Enforce reasonable lower and upper limits */
if (br.count < 4)
br.count = 4; /* Could be choosen smaller */
if (br.count > BUZ_MAX_FRAME)
br.count = BUZ_MAX_FRAME;
br.size = PAGE_ALIGN(br.size);
if (br.size < 8192)
br.size = 8192; /* Arbitrary */
/* br.size is limited by 1 page for the stat_com tables to a Maximum of 2 MB */
if (br.size > (512 * 1024))
br.size = (512 * 1024); /* 512 K should be enough */
if (zr->need_contiguous
&& br.size > MAX_KMALLOC_MEM)
br.size = MAX_KMALLOC_MEM;
zr->jpg_nbufs = br.count;
zr->jpg_bufsize = br.size;
if (jpg_fbuffer_alloc(zr))
return -ENOMEM;
/* The next mmap will map the MJPEG buffers */
zr->map_mjpeg_buffers = 1;
if (copy_to_user(arg, &br, sizeof(br))) {
return -EFAULT;
}
return 0;
}
break;
case BUZIOC_QBUF_CAPT:
{
int nb;
if (copy_from_user
((void *) &nb, (void *) arg, sizeof(int))) {
return -EFAULT;
}
DEBUG4(printk
("%s: ioctl BUZIOC_QBUF_CAPT %d\n",
zr->name, nb));
return jpg_qbuf(zr, nb, BUZ_MODE_MOTION_COMPRESS);
}
break;
case BUZIOC_QBUF_PLAY:
{
int nb;
if (copy_from_user
((void *) &nb, (void *) arg, sizeof(int))) {
return -EFAULT;
}
DEBUG4(printk
("%s: ioctl BUZIOC_QBUF_PLAY %d\n",
zr->name, nb));
return jpg_qbuf(zr, nb,
BUZ_MODE_MOTION_DECOMPRESS);
}
break;
case BUZIOC_SYNC:
{
struct zoran_sync bs;
int res;
DEBUG4(printk
("%s: ioctl BUZIOC_SYNC\n", zr->name));
res = jpg_sync(zr, &bs);
if (copy_to_user(arg, &bs, sizeof(bs))) {
return -EFAULT;
}
return res;
}
break;
case BUZIOC_G_STATUS:
{
struct zoran_status bs;
int norm, input, status;
unsigned long timeout;
if (zr->codec_mode != BUZ_MODE_IDLE) {
DEBUG1(printk(KERN_ERR
"%s: BUZIOC_G_STATUS called but Buz in capture/playback mode\n",
zr->name));
return -EINVAL;
}
if (copy_from_user(&bs, arg, sizeof(bs))) {
return -EFAULT;
}
DEBUG2(printk
("%s: ioctl BUZIOC_G_STATUS\n", zr->name));
switch (bs.input) {
case 0:
if (zr->card == BUZ)
input = 3;
else
input = 0;
break;
case 1:
input = 7;
break;
default:
DEBUG1(printk(KERN_ERR
"%s: BUZIOC_G_STATUS on not existing input %d\n",
zr->name, bs.input));
return -EINVAL;
}
/* Set video norm to VIDEO_MODE_AUTO */
norm = VIDEO_MODE_AUTO;
set_videobus_enable(zr, 0);
i2c_control_device(&zr->i2c,
I2C_DRIVERID_VIDEODECODER,
DECODER_SET_INPUT, &input);
i2c_control_device(&zr->i2c,
I2C_DRIVERID_VIDEODECODER,
DECODER_SET_NORM, &norm);
set_videobus_enable(zr, 1);
/* sleep 1 second */
timeout = jiffies + 1 * HZ;
while (jiffies < timeout)
schedule();
/* Get status of video decoder */
i2c_control_device(&zr->i2c,
I2C_DRIVERID_VIDEODECODER,
DECODER_GET_STATUS, &status);
bs.signal = (status & DECODER_STATUS_GOOD) ? 1 : 0;
if (status & DECODER_STATUS_NTSC)
bs.norm = VIDEO_MODE_NTSC;
else if (status & DECODER_STATUS_SECAM)
bs.norm = VIDEO_MODE_SECAM;
else
bs.norm = VIDEO_MODE_PAL;
bs.color = (status & DECODER_STATUS_COLOR) ? 1 : 0;
/* restore previous input and norm */
if (zr->card == BUZ)
input = zr->params.input == 0 ? 3 : 7;
else
input = zr->params.input == 0 ? 0 : 7;
set_videobus_enable(zr, 0);
i2c_control_device(&zr->i2c,
I2C_DRIVERID_VIDEODECODER,
DECODER_SET_INPUT, &input);
i2c_control_device(&zr->i2c,
I2C_DRIVERID_VIDEODECODER,
DECODER_SET_NORM,
&zr->params.norm);
set_videobus_enable(zr, 1);
if (copy_to_user(arg, &bs, sizeof(bs))) {
return -EFAULT;
}
return 0;
}
break;
default:
DEBUG1(printk
("%s: UNKNOWN ioctl cmd: 0x%x\n", zr->name, cmd));
return -ENOIOCTLCMD;
}
return 0;
}
Generated by GNU enscript 1.6.4.