diff options
author | Mauro Carvalho Chehab <mchehab@redhat.com> | 2012-06-14 16:35:52 -0300 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2012-08-13 23:02:38 -0300 |
commit | 5bc3cb743bbab408792c1b4ef31adf6268aa4b7e (patch) | |
tree | 94faf3260c80a9626b450a6472780828cdf03b26 /drivers/media/video | |
parent | 2ea4b442589b30210a166b9630c2547ebbe2cb82 (diff) |
[media] v4l: move v4l2 core into a separate directory
Currently, the v4l2 core is mixed together with other non-core drivers.
Move them into a separate directory.
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media/video')
24 files changed, 0 insertions, 17455 deletions
diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index a5e3bd044085..068e8daa6b7b 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -2,32 +2,6 @@ # Generic video config states # -config VIDEO_V4L2 - tristate - depends on VIDEO_DEV && VIDEO_V4L2_COMMON - default y - -config VIDEOBUF_GEN - tristate - -config VIDEOBUF_DMA_SG - depends on HAS_DMA - select VIDEOBUF_GEN - tristate - -config VIDEOBUF_VMALLOC - select VIDEOBUF_GEN - tristate - -config VIDEOBUF_DMA_CONTIG - depends on HAS_DMA - select VIDEOBUF_GEN - tristate - -config VIDEOBUF_DVB - tristate - select VIDEOBUF_GEN - config VIDEO_BTCX depends on PCI tristate @@ -36,36 +10,6 @@ config VIDEO_TVEEPROM tristate depends on I2C -config VIDEO_TUNER - tristate - depends on MEDIA_TUNER - -config V4L2_MEM2MEM_DEV - tristate - depends on VIDEOBUF2_CORE - -config VIDEOBUF2_CORE - tristate - -config VIDEOBUF2_MEMOPS - tristate - -config VIDEOBUF2_DMA_CONTIG - select VIDEOBUF2_CORE - select VIDEOBUF2_MEMOPS - tristate - -config VIDEOBUF2_VMALLOC - select VIDEOBUF2_CORE - select VIDEOBUF2_MEMOPS - tristate - - -config VIDEOBUF2_DMA_SG - #depends on HAS_DMA - select VIDEOBUF2_CORE - select VIDEOBUF2_MEMOPS - tristate # # Multimedia Video device configuration # diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index 12cad1206148..839e2c982376 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -2,32 +2,18 @@ # Makefile for the video capture/playback device drivers. # -tuner-objs := tuner-core.o - msp3400-objs := msp3400-driver.o msp3400-kthreads.o stkwebcam-objs := stk-webcam.o stk-sensor.o omap2cam-objs := omap24xxcam.o omap24xxcam-dma.o -videodev-objs := v4l2-dev.o v4l2-ioctl.o v4l2-device.o v4l2-fh.o \ - v4l2-event.o v4l2-ctrls.o v4l2-subdev.o -ifeq ($(CONFIG_COMPAT),y) - videodev-objs += v4l2-compat-ioctl32.o -endif - -# V4L2 core modules - -obj-$(CONFIG_VIDEO_DEV) += videodev.o v4l2-int-device.o -obj-$(CONFIG_VIDEO_V4L2_COMMON) += v4l2-common.o - # Helper modules obj-$(CONFIG_VIDEO_APTINA_PLL) += aptina-pll.o # All i2c modules must come first: -obj-$(CONFIG_VIDEO_TUNER) += tuner.o obj-$(CONFIG_VIDEO_TVAUDIO) += tvaudio.o obj-$(CONFIG_VIDEO_TDA7432) += tda7432.o obj-$(CONFIG_VIDEO_SAA6588) += saa6588.o @@ -128,21 +114,8 @@ obj-$(CONFIG_STA2X11_VIP) += sta2x11_vip.o obj-$(CONFIG_VIDEO_TIMBERDALE) += timblogiw.o obj-$(CONFIG_VIDEO_STK1160) += stk1160/ -obj-$(CONFIG_VIDEOBUF_GEN) += videobuf-core.o -obj-$(CONFIG_VIDEOBUF_DMA_SG) += videobuf-dma-sg.o -obj-$(CONFIG_VIDEOBUF_DMA_CONTIG) += videobuf-dma-contig.o -obj-$(CONFIG_VIDEOBUF_VMALLOC) += videobuf-vmalloc.o -obj-$(CONFIG_VIDEOBUF_DVB) += videobuf-dvb.o obj-$(CONFIG_VIDEO_BTCX) += btcx-risc.o -obj-$(CONFIG_VIDEOBUF2_CORE) += videobuf2-core.o -obj-$(CONFIG_VIDEOBUF2_MEMOPS) += videobuf2-memops.o -obj-$(CONFIG_VIDEOBUF2_VMALLOC) += videobuf2-vmalloc.o -obj-$(CONFIG_VIDEOBUF2_DMA_CONTIG) += videobuf2-dma-contig.o -obj-$(CONFIG_VIDEOBUF2_DMA_SG) += videobuf2-dma-sg.o - -obj-$(CONFIG_V4L2_MEM2MEM_DEV) += v4l2-mem2mem.o - obj-$(CONFIG_VIDEO_M32R_AR_M64278) += arv.o obj-$(CONFIG_VIDEO_CX2341X) += cx2341x.o diff --git a/drivers/media/video/tuner-core.c b/drivers/media/video/tuner-core.c deleted file mode 100644 index b5a819af2b8c..000000000000 --- a/drivers/media/video/tuner-core.c +++ /dev/null @@ -1,1354 +0,0 @@ -/* - * i2c tv tuner chip device driver - * core core, i.e. kernel interfaces, registering and so on - * - * Copyright(c) by Ralph Metzler, Gerd Knorr, Gunther Mayer - * - * Copyright(c) 2005-2011 by Mauro Carvalho Chehab - * - Added support for a separate Radio tuner - * - Major rework and cleanups at the code - * - * This driver supports many devices and the idea is to let the driver - * detect which device is present. So rather than listing all supported - * devices here, we pretend to support a single, fake device type that will - * handle both radio and analog TV tuning. - */ - -#include <linux/module.h> -#include <linux/kernel.h> -#include <linux/string.h> -#include <linux/timer.h> -#include <linux/delay.h> -#include <linux/errno.h> -#include <linux/slab.h> -#include <linux/poll.h> -#include <linux/i2c.h> -#include <linux/types.h> -#include <linux/init.h> -#include <linux/videodev2.h> -#include <media/tuner.h> -#include <media/tuner-types.h> -#include <media/v4l2-device.h> -#include <media/v4l2-ioctl.h> -#include "mt20xx.h" -#include "tda8290.h" -#include "tea5761.h" -#include "tea5767.h" -#include "tuner-xc2028.h" -#include "tuner-simple.h" -#include "tda9887.h" -#include "xc5000.h" -#include "tda18271.h" -#include "xc4000.h" - -#define UNSET (-1U) - -#define PREFIX (t->i2c->driver->driver.name) - -/* - * Driver modprobe parameters - */ - -/* insmod options used at init time => read/only */ -static unsigned int addr; -static unsigned int no_autodetect; -static unsigned int show_i2c; - -module_param(addr, int, 0444); -module_param(no_autodetect, int, 0444); -module_param(show_i2c, int, 0444); - -/* insmod options used at runtime => read/write */ -static int tuner_debug; -static unsigned int tv_range[2] = { 44, 958 }; -static unsigned int radio_range[2] = { 65, 108 }; -static char pal[] = "--"; -static char secam[] = "--"; -static char ntsc[] = "-"; - -module_param_named(debug, tuner_debug, int, 0644); -module_param_array(tv_range, int, NULL, 0644); -module_param_array(radio_range, int, NULL, 0644); -module_param_string(pal, pal, sizeof(pal), 0644); -module_param_string(secam, secam, sizeof(secam), 0644); -module_param_string(ntsc, ntsc, sizeof(ntsc), 0644); - -/* - * Static vars - */ - -static LIST_HEAD(tuner_list); -static const struct v4l2_subdev_ops tuner_ops; - -/* - * Debug macros - */ - -#define tuner_warn(fmt, arg...) do { \ - printk(KERN_WARNING "%s %d-%04x: " fmt, PREFIX, \ - i2c_adapter_id(t->i2c->adapter), \ - t->i2c->addr, ##arg); \ - } while (0) - -#define tuner_info(fmt, arg...) do { \ - printk(KERN_INFO "%s %d-%04x: " fmt, PREFIX, \ - i2c_adapter_id(t->i2c->adapter), \ - t->i2c->addr, ##arg); \ - } while (0) - -#define tuner_err(fmt, arg...) do { \ - printk(KERN_ERR "%s %d-%04x: " fmt, PREFIX, \ - i2c_adapter_id(t->i2c->adapter), \ - t->i2c->addr, ##arg); \ - } while (0) - -#define tuner_dbg(fmt, arg...) do { \ - if (tuner_debug) \ - printk(KERN_DEBUG "%s %d-%04x: " fmt, PREFIX, \ - i2c_adapter_id(t->i2c->adapter), \ - t->i2c->addr, ##arg); \ - } while (0) - -/* - * Internal struct used inside the driver - */ - -struct tuner { - /* device */ - struct dvb_frontend fe; - struct i2c_client *i2c; - struct v4l2_subdev sd; - struct list_head list; - - /* keep track of the current settings */ - v4l2_std_id std; - unsigned int tv_freq; - unsigned int radio_freq; - unsigned int audmode; - - enum v4l2_tuner_type mode; - unsigned int mode_mask; /* Combination of allowable modes */ - - bool standby; /* Standby mode */ - - unsigned int type; /* chip type id */ - unsigned int config; - const char *name; -}; - -/* - * Function prototypes - */ - -static void set_tv_freq(struct i2c_client *c, unsigned int freq); -static void set_radio_freq(struct i2c_client *c, unsigned int freq); - -/* - * tuner attach/detach logic - */ - -/* This macro allows us to probe dynamically, avoiding static links */ -#ifdef CONFIG_MEDIA_ATTACH -#define tuner_symbol_probe(FUNCTION, ARGS...) ({ \ - int __r = -EINVAL; \ - typeof(&FUNCTION) __a = symbol_request(FUNCTION); \ - if (__a) { \ - __r = (int) __a(ARGS); \ - symbol_put(FUNCTION); \ - } else { \ - printk(KERN_ERR "TUNER: Unable to find " \ - "symbol "#FUNCTION"()\n"); \ - } \ - __r; \ -}) - -static void tuner_detach(struct dvb_frontend *fe) -{ - if (fe->ops.tuner_ops.release) { - fe->ops.tuner_ops.release(fe); - symbol_put_addr(fe->ops.tuner_ops.release); - } - if (fe->ops.analog_ops.release) { - fe->ops.analog_ops.release(fe); - symbol_put_addr(fe->ops.analog_ops.release); - } -} -#else -#define tuner_symbol_probe(FUNCTION, ARGS...) ({ \ - FUNCTION(ARGS); \ -}) - -static void tuner_detach(struct dvb_frontend *fe) -{ - if (fe->ops.tuner_ops.release) - fe->ops.tuner_ops.release(fe); - if (fe->ops.analog_ops.release) - fe->ops.analog_ops.release(fe); -} -#endif - - -static inline struct tuner *to_tuner(struct v4l2_subdev *sd) -{ - return container_of(sd, struct tuner, sd); -} - -/* - * struct analog_demod_ops callbacks - */ - -static void fe_set_params(struct dvb_frontend *fe, - struct analog_parameters *params) -{ - struct dvb_tuner_ops *fe_tuner_ops = &fe->ops.tuner_ops; - struct tuner *t = fe->analog_demod_priv; - - if (NULL == fe_tuner_ops->set_analog_params) { - tuner_warn("Tuner frontend module has no way to set freq\n"); - return; - } - fe_tuner_ops->set_analog_params(fe, params); -} - -static void fe_standby(struct dvb_frontend *fe) -{ - struct dvb_tuner_ops *fe_tuner_ops = &fe->ops.tuner_ops; - - if (fe_tuner_ops->sleep) - fe_tuner_ops->sleep(fe); -} - -static int fe_has_signal(struct dvb_frontend *fe) -{ - u16 strength = 0; - - if (fe->ops.tuner_ops.get_rf_strength) - fe->ops.tuner_ops.get_rf_strength(fe, &strength); - - return strength; -} - -static int fe_get_afc(struct dvb_frontend *fe) -{ - s32 afc = 0; - - if (fe->ops.tuner_ops.get_afc) - fe->ops.tuner_ops.get_afc(fe, &afc); - - return 0; -} - -static int fe_set_config(struct dvb_frontend *fe, void *priv_cfg) -{ - struct dvb_tuner_ops *fe_tuner_ops = &fe->ops.tuner_ops; - struct tuner *t = fe->analog_demod_priv; - - if (fe_tuner_ops->set_config) - return fe_tuner_ops->set_config(fe, priv_cfg); - - tuner_warn("Tuner frontend module has no way to set config\n"); - - return 0; -} - -static void tuner_status(struct dvb_frontend *fe); - -static struct analog_demod_ops tuner_analog_ops = { - .set_params = fe_set_params, - .standby = fe_standby, - .has_signal = fe_has_signal, - .get_afc = fe_get_afc, - .set_config = fe_set_config, - .tuner_status = tuner_status -}; - -/* - * Functions to select between radio and TV and tuner probe/remove functions - */ - -/** - * set_type - Sets the tuner type for a given device - * - * @c: i2c_client descriptoy - * @type: type of the tuner (e. g. tuner number) - * @new_mode_mask: Indicates if tuner supports TV and/or Radio - * @new_config: an optional parameter ranging from 0-255 used by - a few tuners to adjust an internal parameter, - like LNA mode - * @tuner_callback: an optional function to be called when switching - * to analog mode - * - * This function applys the tuner config to tuner specified - * by tun_setup structure. It contains several per-tuner initialization "magic" - */ -static void set_type(struct i2c_client *c, unsigned int type, - unsigned int new_mode_mask, unsigned int new_config, - int (*tuner_callback) (void *dev, int component, int cmd, int arg)) -{ - struct tuner *t = to_tuner(i2c_get_clientdata(c)); - struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops; - struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops; - unsigned char buffer[4]; - int tune_now = 1; - - if (type == UNSET || type == TUNER_ABSENT) { - tuner_dbg("tuner 0x%02x: Tuner type absent\n", c->addr); - return; - } - - t->type = type; - /* prevent invalid config values */ - t->config = new_config < 256 ? new_config : 0; - if (tuner_callback != NULL) { - tuner_dbg("defining GPIO callback\n"); - t->fe.callback = tuner_callback; - } - - /* discard private data, in case set_type() was previously called */ - tuner_detach(&t->fe); - t->fe.analog_demod_priv = NULL; - - switch (t->type) { - case TUNER_MT2032: - if (!dvb_attach(microtune_attach, - &t->fe, t->i2c->adapter, t->i2c->addr)) - goto attach_failed; - break; - case TUNER_PHILIPS_TDA8290: - { - struct tda829x_config cfg = { - .lna_cfg = t->config, - }; - if (!dvb_attach(tda829x_attach, &t->fe, t->i2c->adapter, - t->i2c->addr, &cfg)) - goto attach_failed; - break; - } - case TUNER_TEA5767: - if (!dvb_attach(tea5767_attach, &t->fe, - t->i2c->adapter, t->i2c->addr)) - goto attach_failed; - t->mode_mask = T_RADIO; - break; - case TUNER_TEA5761: - if (!dvb_attach(tea5761_attach, &t->fe, - t->i2c->adapter, t->i2c->addr)) - goto attach_failed; - t->mode_mask = T_RADIO; - break; - case TUNER_PHILIPS_FMD1216ME_MK3: - case TUNER_PHILIPS_FMD1216MEX_MK3: - buffer[0] = 0x0b; - buffer[1] = 0xdc; - buffer[2] = 0x9c; - buffer[3] = 0x60; - i2c_master_send(c, buffer, 4); - mdelay(1); - buffer[2] = 0x86; - buffer[3] = 0x54; - i2c_master_send(c, buffer, 4); - if (!dvb_attach(simple_tuner_attach, &t->fe, - t->i2c->adapter, t->i2c->addr, t->type)) - goto attach_failed; - break; - case TUNER_PHILIPS_TD1316: - buffer[0] = 0x0b; - buffer[1] = 0xdc; - buffer[2] = 0x86; - buffer[3] = 0xa4; - i2c_master_send(c, buffer, 4); - if (!dvb_attach(simple_tuner_attach, &t->fe, - t->i2c->adapter, t->i2c->addr, t->type)) - goto attach_failed; - break; - case TUNER_XC2028: - { - struct xc2028_config cfg = { - .i2c_adap = t->i2c->adapter, - .i2c_addr = t->i2c->addr, - }; - if (!dvb_attach(xc2028_attach, &t->fe, &cfg)) - goto attach_failed; - tune_now = 0; - break; - } - case TUNER_TDA9887: - if (!dvb_attach(tda9887_attach, - &t->fe, t->i2c->adapter, t->i2c->addr)) - goto attach_failed; - break; - case TUNER_XC5000: - { - struct xc5000_config xc5000_cfg = { - .i2c_address = t->i2c->addr, - /* if_khz will be set at dvb_attach() */ - .if_khz = 0, - }; - - if (!dvb_attach(xc5000_attach, - &t->fe, t->i2c->adapter, &xc5000_cfg)) - goto attach_failed; - tune_now = 0; - break; - } - case TUNER_XC5000C: - { - struct xc5000_config xc5000c_cfg = { - .i2c_address = t->i2c->addr, - /* if_khz will be set at dvb_attach() */ - .if_khz = 0, - .chip_id = XC5000C, - }; - - if (!dvb_attach(xc5000_attach, - &t->fe, t->i2c->adapter, &xc5000c_cfg)) - goto attach_failed; - tune_now = 0; - break; - } - case TUNER_NXP_TDA18271: - { - struct tda18271_config cfg = { - .config = t->config, - .small_i2c = TDA18271_03_BYTE_CHUNK_INIT, - }; - - if (!dvb_attach(tda18271_attach, &t->fe, t->i2c->addr, - t->i2c->adapter, &cfg)) - goto attach_failed; - tune_now = 0; - break; - } - case TUNER_XC4000: - { - struct xc4000_config xc4000_cfg = { - .i2c_address = t->i2c->addr, - /* FIXME: the correct parameters will be set */ - /* only when the digital dvb_attach() occurs */ - .default_pm = 0, - .dvb_amplitude = 0, - .set_smoothedcvbs = 0, - .if_khz = 0 - }; - if (!dvb_attach(xc4000_attach, - &t->fe, t->i2c->adapter, &xc4000_cfg)) - goto attach_failed; - tune_now = 0; - break; - } - default: - if (!dvb_attach(simple_tuner_attach, &t->fe, - t->i2c->adapter, t->i2c->addr, t->type)) - goto attach_failed; - - break; - } - - if ((NULL == analog_ops->set_params) && - (fe_tuner_ops->set_analog_params)) { - - t->name = fe_tuner_ops->info.name; - - t->fe.analog_demod_priv = t; - memcpy(analog_ops, &tuner_analog_ops, - sizeof(struct analog_demod_ops)); - - } else { - t->name = analog_ops->info.name; - } - - tuner_dbg("type set to %s\n", t->name); - - t->mode_mask = new_mode_mask; - - /* Some tuners require more initialization setup before use, - such as firmware download or device calibration. - trying to set a frequency here will just fail - FIXME: better to move set_freq to the tuner code. This is needed - on analog tuners for PLL to properly work - */ - if (tune_now) { - if (V4L2_TUNER_RADIO == t->mode) - set_radio_freq(c, t->radio_freq); - else - set_tv_freq(c, t->tv_freq); - } - - tuner_dbg("%s %s I2C addr 0x%02x with type %d used for 0x%02x\n", - c->adapter->name, c->driver->driver.name, c->addr << 1, type, - t->mode_mask); - return; - -attach_failed: - tuner_dbg("Tuner attach for type = %d failed.\n", t->type); - t->type = TUNER_ABSENT; - - return; -} - -/** - * tuner_s_type_addr - Sets the tuner type for a device - * - * @sd: subdev descriptor - * @tun_setup: type to be associated to a given tuner i2c address - * - * This function applys the tuner config to tuner specified - * by tun_setup structure. - * If tuner I2C address is UNSET, then it will only set the device - * if the tuner supports the mode specified in the call. - * If the address is specified, the change will be applied only if - * tuner I2C address matches. - * The call can change the tuner number and the tuner mode. - */ -static int tuner_s_type_addr(struct v4l2_subdev *sd, - struct tuner_setup *tun_setup) -{ - struct tuner *t = to_tuner(sd); - struct i2c_client *c = v4l2_get_subdevdata(sd); - - tuner_dbg("Calling set_type_addr for type=%d, addr=0x%02x, mode=0x%02x, config=0x%02x\n", - tun_setup->type, - tun_setup->addr, - tun_setup->mode_mask, - tun_setup->config); - - if ((t->type == UNSET && ((tun_setup->addr == ADDR_UNSET) && - (t->mode_mask & tun_setup->mode_mask))) || - (tun_setup->addr == c->addr)) { - set_type(c, tun_setup->type, tun_setup->mode_mask, - tun_setup->config, tun_setup->tuner_callback); - } else - tuner_dbg("set addr discarded for type %i, mask %x. " - "Asked to change tuner at addr 0x%02x, with mask %x\n", - t->type, t->mode_mask, - tun_setup->addr, tun_setup->mode_mask); - - return 0; -} - -/** - * tuner_s_config - Sets tuner configuration - * - * @sd: subdev descriptor - * @cfg: tuner configuration - * - * Calls tuner set_config() private function to set some tuner-internal - * parameters - */ -static int tuner_s_config(struct v4l2_subdev *sd, - const struct v4l2_priv_tun_config *cfg) -{ - struct tuner *t = to_tuner(sd); - struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops; - - if (t->type != cfg->tuner) - return 0; - - if (analog_ops->set_config) { - analog_ops->set_config(&t->fe, cfg->priv); - return 0; - } - - tuner_dbg("Tuner frontend module has no way to set config\n"); - return 0; -} - -/** - * tuner_lookup - Seek for tuner adapters - * - * @adap: i2c_adapter struct - * @radio: pointer to be filled if the adapter is radio - * @tv: pointer to be filled if the adapter is TV - * - * Search for existing radio and/or TV tuners on the given I2C adapter, - * discarding demod-only adapters (tda9887). - * - * Note that when this function is called from tuner_probe you can be - * certain no other devices will be added/deleted at the same time, I2C - * core protects against that. - */ -static void tuner_lookup(struct i2c_adapter *adap, - struct tuner **radio, struct tuner **tv) -{ - struct tuner *pos; - - *radio = NULL; - *tv = NULL; - - list_for_each_entry(pos, &tuner_list, list) { - int mode_mask; - - if (pos->i2c->adapter != adap || - strcmp(pos->i2c->driver->driver.name, "tuner")) - continue; - - mode_mask = pos->mode_mask; - if (*radio == NULL && mode_mask == T_RADIO) - *radio = pos; - /* Note: currently TDA9887 is the only demod-only - device. If other devices appear then we need to - make this test more general. */ - else if (*tv == NULL && pos->type != TUNER_TDA9887 && - (pos->mode_mask & T_ANALOG_TV)) - *tv = pos; - } -} - -/** - *tuner_probe - Probes the existing tuners on an I2C bus - * - * @client: i2c_client descriptor - * @id: not used - * - * This routine probes for tuners at the expected I2C addresses. On most - * cases, if a device answers to a given I2C address, it assumes that the - * device is a tuner. On a few cases, however, an additional logic is needed - * to double check if the device is really a tuner, or to identify the tuner - * type, like on tea5767/5761 devices. - * - * During client attach, set_type is called by adapter's attach_inform callback. - * set_type must then be completed by tuner_probe. - */ -static int tuner_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct tuner *t; - struct tuner *radio; - struct tuner *tv; - - t = kzalloc(sizeof(struct tuner), GFP_KERNEL); - if (NULL == t) - return -ENOMEM; - v4l2_i2c_subdev_init(&t->sd, client, &tuner_ops); - t->i2c = client; - t->name = "(tuner unset)"; - t->type = UNSET; - t->audmode = V4L2_TUNER_MODE_STEREO; - t->standby = 1; - t->radio_freq = 87.5 * 16000; /* Initial freq range */ - t->tv_freq = 400 * 16; /* Sets freq to VHF High - needed for some PLL's to properly start */ - - if (show_i2c) { - unsigned char buffer[16]; - int i, rc; - - memset(buffer, 0, sizeof(buffer)); - rc = i2c_master_recv(client, buffer, sizeof(buffer)); - tuner_info("I2C RECV = "); - for (i = 0; i < rc; i++) - printk(KERN_CONT "%02x ", buffer[i]); - printk("\n"); - } - - /* autodetection code based on the i2c addr */ - if (!no_autodetect) { - switch (client->addr) { - case 0x10: - if (tuner_symbol_probe(tea5761_autodetection, - t->i2c->adapter, - t->i2c->addr) >= 0) { - t->type = TUNER_TEA5761; - t->mode_mask = T_RADIO; - tuner_lookup(t->i2c->adapter, &radio, &tv); - if (tv) - tv->mode_mask &= ~T_RADIO; - - goto register_client; - } - kfree(t); - return -ENODEV; - case 0x42: - case 0x43: - case 0x4a: - case 0x4b: - /* If chip is not tda8290, don't register. - since it can be tda9887*/ - if (tuner_symbol_probe(tda829x_probe, t->i2c->adapter, - t->i2c->addr) >= 0) { - tuner_dbg("tda829x detected\n"); - } else { - /* Default is being tda9887 */ - t->type = TUNER_TDA9887; - t->mode_mask = T_RADIO | T_ANALOG_TV; - goto register_client; - } - break; - case 0x60: - if (tuner_symbol_probe(tea5767_autodetection, - t->i2c->adapter, t->i2c->addr) - >= 0) { - t->type = TUNER_TEA5767; - t->mode_mask = T_RADIO; - /* Sets freq to FM range */ - tuner_lookup(t->i2c->adapter, &radio, &tv); - if (tv) - tv->mode_mask &= ~T_RADIO; - - goto register_client; - } - break; - } - } - - /* Initializes only the first TV tuner on this adapter. Why only the - first? Because there are some devices (notably the ones with TI - tuners) that have more than one i2c address for the *same* device. - Experience shows that, except for just one case, the first - address is the right one. The exception is a Russian tuner - (ACORP_Y878F). So, the desired behavior is just to enable the - first found TV tuner. */ - tuner_lookup(t->i2c->adapter, &radio, &tv); - if (tv == NULL) { - t->mode_mask = T_ANALOG_TV; - if (radio == NULL) - t->mode_mask |= T_RADIO; - tuner_dbg("Setting mode_mask to 0x%02x\n", t->mode_mask); - } - - /* Should be just before return */ -register_client: - /* Sets a default mode */ - if (t->mode_mask & T_ANALOG_TV) - t->mode = V4L2_TUNER_ANALOG_TV; - else - t->mode = V4L2_TUNER_RADIO; - set_type(client, t->type, t->mode_mask, t->config, t->fe.callback); - list_add_tail(&t->list, &tuner_list); - - tuner_info("Tuner %d found with type(s)%s%s.\n", - t->type, - t->mode_mask & T_RADIO ? " Radio" : "", - t->mode_mask & T_ANALOG_TV ? " TV" : ""); - return 0; -} - -/** - * tuner_remove - detaches a tuner - * - * @client: i2c_client descriptor - */ - -static int tuner_remove(struct i2c_client *client) -{ - struct tuner *t = to_tuner(i2c_get_clientdata(client)); - - v4l2_device_unregister_subdev(&t->sd); - tuner_detach(&t->fe); - t->fe.analog_demod_priv = NULL; - - list_del(&t->list); - kfree(t); - return 0; -} - -/* - * Functions to switch between Radio and TV - * - * A few cards have a separate I2C tuner for radio. Those routines - * take care of switching between TV/Radio mode, filtering only the - * commands that apply to the Radio or TV tuner. - */ - -/** - * check_mode - Verify if tuner supports the requested mode - * @t: a pointer to the module's internal struct_tuner - * - * This function checks if the tuner is capable of tuning analog TV, - * digital TV or radio, depending on what the caller wants. If the - * tuner can't support that mode, it returns -EINVAL. Otherwise, it - * returns 0. - * This function is needed for boards that have a separate tuner for - * radio (like devices with tea5767). - * NOTE: mt20xx uses V4L2_TUNER_DIGITAL_TV and calls set_tv_freq to - * select a TV frequency. So, t_mode = T_ANALOG_TV could actually - * be used to represent a Digital TV too. - */ -static inline int check_mode(struct tuner *t, enum v4l2_tuner_type mode) -{ - int t_mode; - if (mode == V4L2_TUNER_RADIO) - t_mode = T_RADIO; - else - t_mode = T_ANALOG_TV; - - if ((t_mode & t->mode_mask) == 0) - return -EINVAL; - - return 0; -} - -/** - * set_mode - Switch tuner to other mode. - * @t: a pointer to the module's internal struct_tuner - * @mode: enum v4l2_type (radio or TV) - * - * If tuner doesn't support the needed mode (radio or TV), prints a - * debug message and returns -EINVAL, changing its state to standby. - * Otherwise, changes the mode and returns 0. - */ -static int set_mode(struct tuner *t, enum v4l2_tuner_type mode) -{ - struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops; - - if (mode != t->mode) { - if (check_mode(t, mode) == -EINVAL) { - tuner_dbg("Tuner doesn't support mode %d. " - "Putting tuner to sleep\n", mode); - t->standby = true; - if (analog_ops->standby) - analog_ops->standby(&t->fe); - return -EINVAL; - } - t->mode = mode; - tuner_dbg("Changing to mode %d\n", mode); - } - return 0; -} - -/** - * set_freq - Set the tuner to the desired frequency. - * @t: a pointer to the module's internal struct_tuner - * @freq: frequency to set (0 means to use the current frequency) - */ -static void set_freq(struct tuner *t, unsigned int freq) -{ - struct i2c_client *client = v4l2_get_subdevdata(&t->sd); - - if (t->mode == V4L2_TUNER_RADIO) { - if (!freq) - freq = t->radio_freq; - set_radio_freq(client, freq); - } else { - if (!freq) - freq = t->tv_freq; - set_tv_freq(client, freq); - } -} - -/* - * Functions that are specific for TV mode - */ - -/** - * set_tv_freq - Set tuner frequency, freq in Units of 62.5 kHz = 1/16MHz - * - * @c: i2c_client descriptor - * @freq: frequency - */ -static void set_tv_freq(struct i2c_client *c, unsigned int freq) -{ - struct tuner *t = to_tuner(i2c_get_clientdata(c)); - struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops; - - struct analog_parameters params = { - .mode = t->mode, - .audmode = t->audmode, - .std = t->std - }; - - if (t->type == UNSET) { - tuner_warn("tuner type not set\n"); - return; - } - if (NULL == analog_ops->set_params) { - tuner_warn("Tuner has no way to set tv freq\n"); - return; - } - if (freq < tv_range[0] * 16 || freq > tv_range[1] * 16) { - tuner_dbg("TV freq (%d.%02d) out of range (%d-%d)\n", - freq / 16, freq % 16 * 100 / 16, tv_range[0], - tv_range[1]); - /* V4L2 spec: if the freq is not possible then the closest - possible value should be selected */ - if (freq < tv_range[0] * 16) - freq = tv_range[0] * 16; - else - freq = tv_range[1] * 16; - } - params.frequency = freq; - tuner_dbg("tv freq set to %d.%02d\n", - freq / 16, freq % 16 * 100 / 16); - t->tv_freq = freq; - t->standby = false; - - analog_ops->set_params(&t->fe, ¶ms); -} - -/** - * tuner_fixup_std - force a given video standard variant - * - * @t: tuner internal struct - * @std: TV standard - * - * A few devices or drivers have problem to detect some standard variations. - * On other operational systems, the drivers generally have a per-country - * code, and some logic to apply per-country hacks. V4L2 API doesn't provide - * such hacks. Instead, it relies on a proper video standard selection from - * the userspace application. However, as some apps are buggy, not allowing - * to distinguish all video standard variations, a modprobe parameter can - * be used to force a video standard match. - */ -static v4l2_std_id tuner_fixup_std(struct tuner *t, v4l2_std_id std) -{ - if (pal[0] != '-' && (std & V4L2_STD_PAL) == V4L2_STD_PAL) { - switch (pal[0]) { - case '6': - return V4L2_STD_PAL_60; - case 'b': - case 'B': - case 'g': - case 'G': - return V4L2_STD_PAL_BG; - case 'i': - case 'I': - return V4L2_STD_PAL_I; - case 'd': - case 'D': - case 'k': - case 'K': - return V4L2_STD_PAL_DK; - case 'M': - case 'm': - return V4L2_STD_PAL_M; - case 'N': - case 'n': - if (pal[1] == 'c' || pal[1] == 'C') - return V4L2_STD_PAL_Nc; - return V4L2_STD_PAL_N; - default: - tuner_warn("pal= argument not recognised\n"); - break; - } - } - if (secam[0] != '-' && (std & V4L2_STD_SECAM) == V4L2_STD_SECAM) { - switch (secam[0]) { - case 'b': - case 'B': - case 'g': - case 'G': - case 'h': - case 'H': - return V4L2_STD_SECAM_B | - V4L2_STD_SECAM_G | - V4L2_STD_SECAM_H; - case 'd': - case 'D': - case 'k': - case 'K': - return V4L2_STD_SECAM_DK; - case 'l': - case 'L': - if ((secam[1] == 'C') || (secam[1] == 'c')) - return V4L2_STD_SECAM_LC; - return V4L2_STD_SECAM_L; - default: - tuner_warn("secam= argument not recognised\n"); - break; - } - } - - if (ntsc[0] != '-' && (std & V4L2_STD_NTSC) == V4L2_STD_NTSC) { - switch (ntsc[0]) { - case 'm': - case 'M': - return V4L2_STD_NTSC_M; - case 'j': - case 'J': - return V4L2_STD_NTSC_M_JP; - case 'k': - case 'K': - return V4L2_STD_NTSC_M_KR; - default: - tuner_info("ntsc= argument not recognised\n"); - break; - } - } - return std; -} - -/* - * Functions that are specific for Radio mode - */ - -/** - * set_radio_freq - Set tuner frequency, freq in Units of 62.5 Hz = 1/16kHz - * - * @c: i2c_client descriptor - * @freq: frequency - */ -static void set_radio_freq(struct i2c_client *c, unsigned int freq) -{ - struct tuner *t = to_tuner(i2c_get_clientdata(c)); - struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops; - - struct analog_parameters params = { - .mode = t->mode, - .audmode = t->audmode, - .std = t->std - }; - - if (t->type == UNSET) { - tuner_warn("tuner type not set\n"); - return; - } - if (NULL == analog_ops->set_params) { - tuner_warn("tuner has no way to set radio frequency\n"); - return; - } - if (freq < radio_range[0] * 16000 || freq > radio_range[1] * 16000) { - tuner_dbg("radio freq (%d.%02d) out of range (%d-%d)\n", - freq / 16000, freq % 16000 * 100 / 16000, - radio_range[0], radio_range[1]); - /* V4L2 spec: if the freq is not possible then the closest - possible value should be selected */ - if (freq < radio_range[0] * 16000) - freq = radio_range[0] * 16000; - else - freq = radio_range[1] * 16000; - } - params.frequency = freq; - tuner_dbg("radio freq set to %d.%02d\n", - freq / 16000, freq % 16000 * 100 / 16000); - t->radio_freq = freq; - t->standby = false; - - analog_ops->set_params(&t->fe, ¶ms); -} - -/* - * Debug function for reporting tuner status to userspace - */ - -/** - * tuner_status - Dumps the current tuner status at dmesg - * @fe: pointer to struct dvb_frontend - * - * This callback is used only for driver debug purposes, answering to - * VIDIOC_LOG_STATUS. No changes should happen on this call. - */ -static void tuner_status(struct dvb_frontend *fe) -{ - struct tuner *t = fe->analog_demod_priv; - unsigned long freq, freq_fraction; - struct dvb_tuner_ops *fe_tuner_ops = &fe->ops.tuner_ops; - struct analog_demod_ops *analog_ops = &fe->ops.analog_ops; - const char *p; - - switch (t->mode) { - case V4L2_TUNER_RADIO: - p = "radio"; - break; - case V4L2_TUNER_DIGITAL_TV: /* Used by mt20xx */ - p = "digital TV"; - break; - case V4L2_TUNER_ANALOG_TV: - default: - p = "analog TV"; - break; - } - if (t->mode == V4L2_TUNER_RADIO) { - freq = t->radio_freq / 16000; - freq_fraction = (t->radio_freq % 16000) * 100 / 16000; - } else { - freq = t->tv_freq / 16; - freq_fraction = (t->tv_freq % 16) * 100 / 16; - } - tuner_info("Tuner mode: %s%s\n", p, - t->standby ? " on standby mode" : ""); - tuner_info("Frequency: %lu.%02lu MHz\n", freq, freq_fraction); - tuner_info("Standard: 0x%08lx\n", (unsigned long)t->std); - if (t->mode != V4L2_TUNER_RADIO) - return; - if (fe_tuner_ops->get_status) { - u32 tuner_status; - - fe_tuner_ops->get_status(&t->fe, &tuner_status); - if (tuner_status & TUNER_STATUS_LOCKED) - tuner_info("Tuner is locked.\n"); - if (tuner_status & TUNER_STATUS_STEREO) - tuner_info("Stereo: yes\n"); - } - if (analog_ops->has_signal) - tuner_info("Signal strength: %d\n", - analog_ops->has_signal(fe)); -} - -/* - * Function to splicitly change mode to radio. Probably not needed anymore - */ - -static int tuner_s_radio(struct v4l2_subdev *sd) -{ - struct tuner *t = to_tuner(sd); - - if (set_mode(t, V4L2_TUNER_RADIO) == 0) - set_freq(t, 0); - return 0; -} - -/* - * Tuner callbacks to handle userspace ioctl's - */ - -/** - * tuner_s_power - controls the power state of the tuner - * @sd: pointer to struct v4l2_subdev - * @on: a zero value puts the tuner to sleep, non-zero wakes it up - */ -static int tuner_s_power(struct v4l2_subdev *sd, int on) -{ - struct tuner *t = to_tuner(sd); - struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops; - - if (on) { - if (t->standby && set_mode(t, t->mode) == 0) { - tuner_dbg("Waking up tuner\n"); - set_freq(t, 0); - } - return 0; - } - - tuner_dbg("Putting tuner to sleep\n"); - t->standby = true; - if (analog_ops->standby) - analog_ops->standby(&t->fe); - return 0; -} - -static int tuner_s_std(struct v4l2_subdev *sd, v4l2_std_id std) -{ - struct tuner *t = to_tuner(sd); - - if (set_mode(t, V4L2_TUNER_ANALOG_TV)) - return 0; - - t->std = tuner_fixup_std(t, std); - if (t->std != std) - tuner_dbg("Fixup standard %llx to %llx\n", std, t->std); - set_freq(t, 0); - return 0; -} - -static int tuner_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *f) -{ - struct tuner *t = to_tuner(sd); - - if (set_mode(t, f->type) == 0) - set_freq(t, f->frequency); - return 0; -} - -/** - * tuner_g_frequency - Get the tuned frequency for the tuner - * @sd: pointer to struct v4l2_subdev - * @f: pointer to struct v4l2_frequency - * - * At return, the structure f will be filled with tuner frequency - * if the tuner matches the f->type. - * Note: f->type should be initialized before calling it. - * This is done by either video_ioctl2 or by the bridge driver. - */ -static int tuner_g_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *f) -{ - struct tuner *t = to_tuner(sd); - struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops; - - if (check_mode(t, f->type) == -EINVAL) - return 0; - if (f->type == t->mode && fe_tuner_ops->get_frequency && !t->standby) { - u32 abs_freq; - - fe_tuner_ops->get_frequency(&t->fe, &abs_freq); - f->frequency = (V4L2_TUNER_RADIO == t->mode) ? - DIV_ROUND_CLOSEST(abs_freq * 2, 125) : - DIV_ROUND_CLOSEST(abs_freq, 62500); - } else { - f->frequency = (V4L2_TUNER_RADIO == f->type) ? - t->radio_freq : t->tv_freq; - } - return 0; -} - -/** - * tuner_g_tuner - Fill in tuner information - * @sd: pointer to struct v4l2_subdev - * @vt: pointer to struct v4l2_tuner - * - * At return, the structure vt will be filled with tuner information - * if the tuner matches vt->type. - * Note: vt->type should be initialized before calling it. - * This is done by either video_ioctl2 or by the bridge driver. - */ -static int tuner_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) -{ - struct tuner *t = to_tuner(sd); - struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops; - struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops; - - if (check_mode(t, vt->type) == -EINVAL) - return 0; - if (vt->type == t->mode && analog_ops->get_afc) - vt->afc = analog_ops->get_afc(&t->fe); - if (analog_ops->has_signal) - vt->signal = analog_ops->has_signal(&t->fe); - if (vt->type != V4L2_TUNER_RADIO) { - vt->capability |= V4L2_TUNER_CAP_NORM; - vt->rangelow = tv_range[0] * 16; - vt->rangehigh = tv_range[1] * 16; - return 0; - } - - /* radio mode */ - if (vt->type == t->mode) { - vt->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO; - if (fe_tuner_ops->get_status) { - u32 tuner_status; - - fe_tuner_ops->get_status(&t->fe, &tuner_status); - vt->rxsubchans = - (tuner_status & TUNER_STATUS_STEREO) ? - V4L2_TUNER_SUB_STEREO : - V4L2_TUNER_SUB_MONO; - } - vt->audmode = t->audmode; - } - vt->capability |= V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO; - vt->rangelow = radio_range[0] * 16000; - vt->rangehigh = radio_range[1] * 16000; - - return 0; -} - -/** - * tuner_s_tuner - Set the tuner's audio mode - * @sd: pointer to struct v4l2_subdev - * @vt: pointer to struct v4l2_tuner - * - * Sets the audio mode if the tuner matches vt->type. - * Note: vt->type should be initialized before calling it. - * This is done by either video_ioctl2 or by the bridge driver. - */ -static int tuner_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) -{ - struct tuner *t = to_tuner(sd); - - if (set_mode(t, vt->type)) - return 0; - - if (t->mode == V4L2_TUNER_RADIO) - t->audmode = vt->audmode; - set_freq(t, 0); - - return 0; -} - -static int tuner_log_status(struct v4l2_subdev *sd) -{ - struct tuner *t = to_tuner(sd); - struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops; - - if (analog_ops->tuner_status) - analog_ops->tuner_status(&t->fe); - return 0; -} - -#ifdef CONFIG_PM_SLEEP -static int tuner_suspend(struct device *dev) -{ - struct i2c_client *c = to_i2c_client(dev); - struct tuner *t = to_tuner(i2c_get_clientdata(c)); - struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops; - - tuner_dbg("suspend\n"); - - if (!t->standby && analog_ops->standby) - analog_ops->standby(&t->fe); - - return 0; -} - -static int tuner_resume(struct device *dev) -{ - struct i2c_client *c = to_i2c_client(dev); - struct tuner *t = to_tuner(i2c_get_clientdata(c)); - - tuner_dbg("resume\n"); - - if (!t->standby) - if (set_mode(t, t->mode) == 0) - set_freq(t, 0); - - return 0; -} -#endif - -static int tuner_command(struct i2c_client *client, unsigned cmd, void *arg) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - - /* TUNER_SET_CONFIG is still called by tuner-simple.c, so we have - to handle it here. - There must be a better way of doing this... */ - switch (cmd) { - case TUNER_SET_CONFIG: - return tuner_s_config(sd, arg); - } - return -ENOIOCTLCMD; -} - -/* - * Callback structs - */ - -static const struct v4l2_subdev_core_ops tuner_core_ops = { - .log_status = tuner_log_status, - .s_std = tuner_s_std, - .s_power = tuner_s_power, -}; - -static const struct v4l2_subdev_tuner_ops tuner_tuner_ops = { - .s_radio = tuner_s_radio, - .g_tuner = tuner_g_tuner, - .s_tuner = tuner_s_tuner, - .s_frequency = tuner_s_frequency, - .g_frequency = tuner_g_frequency, - .s_type_addr = tuner_s_type_addr, - .s_config = tuner_s_config, -}; - -static const struct v4l2_subdev_ops tuner_ops = { - .core = &tuner_core_ops, - .tuner = &tuner_tuner_ops, -}; - -/* - * I2C structs and module init functions - */ - -static const struct dev_pm_ops tuner_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(tuner_suspend, tuner_resume) -}; - -static const struct i2c_device_id tuner_id[] = { - { "tuner", }, /* autodetect */ - { } -}; -MODULE_DEVICE_TABLE(i2c, tuner_id); - -static struct i2c_driver tuner_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "tuner", - .pm = &tuner_pm_ops, - }, - .probe = tuner_probe, - .remove = tuner_remove, - .command = tuner_command, - .id_table = tuner_id, -}; - -module_i2c_driver(tuner_driver); - -MODULE_DESCRIPTION("device driver for various TV and TV+FM radio tuners"); -MODULE_AUTHOR("Ralph Metzler, Gerd Knorr, Gunther Mayer"); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/v4l2-common.c b/drivers/media/video/v4l2-common.c deleted file mode 100644 index 105f88cdb9d6..000000000000 --- a/drivers/media/video/v4l2-common.c +++ /dev/null @@ -1,623 +0,0 @@ -/* - * Video for Linux Two - * - * A generic video device interface for the LINUX operating system - * using a set of device structures/vectors for low level operations. - * - * This file replaces the videodev.c file that comes with the - * regular kernel distribution. - * - * 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. - * - * Author: Bill Dirks <bill@thedirks.org> - * based on code by Alan Cox, <alan@cymru.net> - * - */ - -/* - * Video capture interface for Linux - * - * A generic video device interface for the LINUX operating system - * using a set of device structures/vectors for low level operations. - * - * 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. - * - * Author: Alan Cox, <alan@lxorguk.ukuu.org.uk> - * - * Fixes: - */ - -/* - * Video4linux 1/2 integration by Justin Schoeman - * <justin@suntiger.ee.up.ac.za> - * 2.4 PROCFS support ported from 2.4 kernels by - * Iñaki GarcÃa Etxebarria <garetxe@euskalnet.net> - * Makefile fix by "W. Michael Petullo" <mike@flyn.org> - * 2.4 devfs support ported from 2.4 kernels by - * Dan Merillat <dan@merillat.org> - * Added Gerd Knorrs v4l1 enhancements (Justin Schoeman) - */ - -#include <linux/module.h> -#include <linux/types.h> -#include <linux/kernel.h> -#include <linux/mm.h> -#include <linux/string.h> -#include <linux/errno.h> -#include <linux/i2c.h> -#if defined(CONFIG_SPI) -#include <linux/spi/spi.h> -#endif -#include <asm/uaccess.h> -#include <asm/pgtable.h> -#include <asm/io.h> -#include <asm/div64.h> -#include <media/v4l2-common.h> -#include <media/v4l2-device.h> -#include <media/v4l2-ctrls.h> -#include <media/v4l2-chip-ident.h> - -#include <linux/videodev2.h> - -MODULE_AUTHOR("Bill Dirks, Justin Schoeman, Gerd Knorr"); -MODULE_DESCRIPTION("misc helper functions for v4l2 device drivers"); -MODULE_LICENSE("GPL"); - -/* - * - * V 4 L 2 D R I V E R H E L P E R A P I - * - */ - -/* - * Video Standard Operations (contributed by Michael Schimek) - */ - -/* Helper functions for control handling */ - -/* Check for correctness of the ctrl's value based on the data from - struct v4l2_queryctrl and the available menu items. Note that - menu_items may be NULL, in that case it is ignored. */ -int v4l2_ctrl_check(struct v4l2_ext_control *ctrl, struct v4l2_queryctrl *qctrl, - const char * const *menu_items) -{ - if (qctrl->flags & V4L2_CTRL_FLAG_DISABLED) - return -EINVAL; - if (qctrl->flags & V4L2_CTRL_FLAG_GRABBED) - return -EBUSY; - if (qctrl->type == V4L2_CTRL_TYPE_STRING) - return 0; - if (qctrl->type == V4L2_CTRL_TYPE_BUTTON || - qctrl->type == V4L2_CTRL_TYPE_INTEGER64 || - qctrl->type == V4L2_CTRL_TYPE_CTRL_CLASS) - return 0; - if (ctrl->value < qctrl->minimum || ctrl->value > qctrl->maximum) - return -ERANGE; - if (qctrl->type == V4L2_CTRL_TYPE_MENU && menu_items != NULL) { - if (menu_items[ctrl->value] == NULL || - menu_items[ctrl->value][0] == '\0') - return -EINVAL; - } - if (qctrl->type == V4L2_CTRL_TYPE_BITMASK && - (ctrl->value & ~qctrl->maximum)) - return -ERANGE; - return 0; -} -EXPORT_SYMBOL(v4l2_ctrl_check); - -/* Fill in a struct v4l2_queryctrl */ -int v4l2_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 max, s32 step, s32 def) -{ - const char *name; - - v4l2_ctrl_fill(qctrl->id, &name, &qctrl->type, - &min, &max, &step, &def, &qctrl->flags); - - if (name == NULL) - return -EINVAL; - - qctrl->minimum = min; - qctrl->maximum = max; - qctrl->step = step; - qctrl->default_value = def; - qctrl->reserved[0] = qctrl->reserved[1] = 0; - strlcpy(qctrl->name, name, sizeof(qctrl->name)); - return 0; -} -EXPORT_SYMBOL(v4l2_ctrl_query_fill); - -/* Fill in a struct v4l2_querymenu based on the struct v4l2_queryctrl and - the menu. The qctrl pointer may be NULL, in which case it is ignored. - If menu_items is NULL, then the menu items are retrieved using - v4l2_ctrl_get_menu. */ -int v4l2_ctrl_query_menu(struct v4l2_querymenu *qmenu, struct v4l2_queryctrl *qctrl, - const char * const *menu_items) -{ - int i; - - qmenu->reserved = 0; - if (menu_items == NULL) - menu_items = v4l2_ctrl_get_menu(qmenu->id); - if (menu_items == NULL || - (qctrl && (qmenu->index < qctrl->minimum || qmenu->index > qctrl->maximum))) - return -EINVAL; - for (i = 0; i < qmenu->index && menu_items[i]; i++) ; - if (menu_items[i] == NULL || menu_items[i][0] == '\0') - return -EINVAL; - strlcpy(qmenu->name, menu_items[qmenu->index], sizeof(qmenu->name)); - return 0; -} -EXPORT_SYMBOL(v4l2_ctrl_query_menu); - -/* Fill in a struct v4l2_querymenu based on the specified array of valid - menu items (terminated by V4L2_CTRL_MENU_IDS_END). - Use this if there are 'holes' in the list of valid menu items. */ -int v4l2_ctrl_query_menu_valid_items(struct v4l2_querymenu *qmenu, const u32 *ids) -{ - const char * const *menu_items = v4l2_ctrl_get_menu(qmenu->id); - - qmenu->reserved = 0; - if (menu_items == NULL || ids == NULL) - return -EINVAL; - while (*ids != V4L2_CTRL_MENU_IDS_END) { - if (*ids++ == qmenu->index) { - strlcpy(qmenu->name, menu_items[qmenu->index], - sizeof(qmenu->name)); - return 0; - } - } - return -EINVAL; -} -EXPORT_SYMBOL(v4l2_ctrl_query_menu_valid_items); - -/* ctrl_classes points to an array of u32 pointers, the last element is - a NULL pointer. Each u32 array is a 0-terminated array of control IDs. - Each array must be sorted low to high and belong to the same control - class. The array of u32 pointers must also be sorted, from low class IDs - to high class IDs. - - This function returns the first ID that follows after the given ID. - When no more controls are available 0 is returned. */ -u32 v4l2_ctrl_next(const u32 * const * ctrl_classes, u32 id) -{ - u32 ctrl_class = V4L2_CTRL_ID2CLASS(id); - const u32 *pctrl; - - if (ctrl_classes == NULL) - return 0; - - /* if no query is desired, then check if the ID is part of ctrl_classes */ - if ((id & V4L2_CTRL_FLAG_NEXT_CTRL) == 0) { - /* find class */ - while (*ctrl_classes && V4L2_CTRL_ID2CLASS(**ctrl_classes) != ctrl_class) - ctrl_classes++; - if (*ctrl_classes == NULL) - return 0; - pctrl = *ctrl_classes; - /* find control ID */ - while (*pctrl && *pctrl != id) pctrl++; - return *pctrl ? id : 0; - } - id &= V4L2_CTRL_ID_MASK; - id++; /* select next control */ - /* find first class that matches (or is greater than) the class of - the ID */ - while (*ctrl_classes && V4L2_CTRL_ID2CLASS(**ctrl_classes) < ctrl_class) - ctrl_classes++; - /* no more classes */ - if (*ctrl_classes == NULL) - return 0; - pctrl = *ctrl_classes; - /* find first ctrl within the class that is >= ID */ - while (*pctrl && *pctrl < id) pctrl++; - if (*pctrl) - return *pctrl; - /* we are at the end of the controls of the current class. */ - /* continue with next class if available */ - ctrl_classes++; - if (*ctrl_classes == NULL) - return 0; - return **ctrl_classes; -} -EXPORT_SYMBOL(v4l2_ctrl_next); - -int v4l2_chip_match_host(const struct v4l2_dbg_match *match) -{ - switch (match->type) { - case V4L2_CHIP_MATCH_HOST: - return match->addr == 0; - default: - return 0; - } -} -EXPORT_SYMBOL(v4l2_chip_match_host); - -#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE)) -int v4l2_chip_match_i2c_client(struct i2c_client *c, const struct v4l2_dbg_match *match) -{ - int len; - - if (c == NULL || match == NULL) - return 0; - - switch (match->type) { - case V4L2_CHIP_MATCH_I2C_DRIVER: - if (c->driver == NULL || c->driver->driver.name == NULL) - return 0; - len = strlen(c->driver->driver.name); - /* legacy drivers have a ' suffix, don't try to match that */ - if (len && c->driver->driver.name[len - 1] == '\'') - len--; - return len && !strncmp(c->driver->driver.name, match->name, len); - case V4L2_CHIP_MATCH_I2C_ADDR: - return c->addr == match->addr; - default: - return 0; - } -} -EXPORT_SYMBOL(v4l2_chip_match_i2c_client); - -int v4l2_chip_ident_i2c_client(struct i2c_client *c, struct v4l2_dbg_chip_ident *chip, - u32 ident, u32 revision) -{ - if (!v4l2_chip_match_i2c_client(c, &chip->match)) - return 0; - if (chip->ident == V4L2_IDENT_NONE) { - chip->ident = ident; - chip->revision = revision; - } - else { - chip->ident = V4L2_IDENT_AMBIGUOUS; - chip->revision = 0; - } - return 0; -} -EXPORT_SYMBOL(v4l2_chip_ident_i2c_client); - -/* ----------------------------------------------------------------- */ - -/* I2C Helper functions */ - - -void v4l2_i2c_subdev_init(struct v4l2_subdev *sd, struct i2c_client *client, - const struct v4l2_subdev_ops *ops) -{ - v4l2_subdev_init(sd, ops); - sd->flags |= V4L2_SUBDEV_FL_IS_I2C; - /* the owner is the same as the i2c_client's driver owner */ - sd->owner = client->driver->driver.owner; - /* i2c_client and v4l2_subdev point to one another */ - v4l2_set_subdevdata(sd, client); - i2c_set_clientdata(client, sd); - /* initialize name */ - snprintf(sd->name, sizeof(sd->name), "%s %d-%04x", - client->driver->driver.name, i2c_adapter_id(client->adapter), - client->addr); -} -EXPORT_SYMBOL_GPL(v4l2_i2c_subdev_init); - - - -/* Load an i2c sub-device. */ -struct v4l2_subdev *v4l2_i2c_new_subdev_board(struct v4l2_device *v4l2_dev, - struct i2c_adapter *adapter, struct i2c_board_info *info, - const unsigned short *probe_addrs) -{ - struct v4l2_subdev *sd = NULL; - struct i2c_client *client; - - BUG_ON(!v4l2_dev); - - request_module(I2C_MODULE_PREFIX "%s", info->type); - - /* Create the i2c client */ - if (info->addr == 0 && probe_addrs) - client = i2c_new_probed_device(adapter, info, probe_addrs, - NULL); - else - client = i2c_new_device(adapter, info); - - /* Note: by loading the module first we are certain that c->driver - will be set if the driver was found. If the module was not loaded - first, then the i2c core tries to delay-load the module for us, - and then c->driver is still NULL until the module is finally - loaded. This delay-load mechanism doesn't work if other drivers - want to use the i2c device, so explicitly loading the module - is the best alternative. */ - if (client == NULL || client->driver == NULL) - goto error; - - /* Lock the module so we can safely get the v4l2_subdev pointer */ - if (!try_module_get(client->driver->driver.owner)) - goto error; - sd = i2c_get_clientdata(client); - - /* Register with the v4l2_device which increases the module's - use count as well. */ - if (v4l2_device_register_subdev(v4l2_dev, sd)) - sd = NULL; - /* Decrease the module use count to match the first try_module_get. */ - module_put(client->driver->driver.owner); - -error: - /* If we have a client but no subdev, then something went wrong and - we must unregister the client. */ - if (client && sd == NULL) - i2c_unregister_device(client); - return sd; -} -EXPORT_SYMBOL_GPL(v4l2_i2c_new_subdev_board); - -struct v4l2_subdev *v4l2_i2c_new_subdev(struct v4l2_device *v4l2_dev, - struct i2c_adapter *adapter, const char *client_type, - u8 addr, const unsigned short *probe_addrs) -{ - struct i2c_board_info info; - - /* Setup the i2c board info with the device type and - the device address. */ - memset(&info, 0, sizeof(info)); - strlcpy(info.type, client_type, sizeof(info.type)); - info.addr = addr; - - return v4l2_i2c_new_subdev_board(v4l2_dev, adapter, &info, probe_addrs); -} -EXPORT_SYMBOL_GPL(v4l2_i2c_new_subdev); - -/* Return i2c client address of v4l2_subdev. */ -unsigned short v4l2_i2c_subdev_addr(struct v4l2_subdev *sd) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return client ? client->addr : I2C_CLIENT_END; -} -EXPORT_SYMBOL_GPL(v4l2_i2c_subdev_addr); - -/* Return a list of I2C tuner addresses to probe. Use only if the tuner - addresses are unknown. */ -const unsigned short *v4l2_i2c_tuner_addrs(enum v4l2_i2c_tuner_type type) -{ - static const unsigned short radio_addrs[] = { -#if defined(CONFIG_MEDIA_TUNER_TEA5761) || defined(CONFIG_MEDIA_TUNER_TEA5761_MODULE) - 0x10, -#endif - 0x60, - I2C_CLIENT_END - }; - static const unsigned short demod_addrs[] = { - 0x42, 0x43, 0x4a, 0x4b, - I2C_CLIENT_END - }; - static const unsigned short tv_addrs[] = { - 0x42, 0x43, 0x4a, 0x4b, /* tda8290 */ - 0x60, 0x61, 0x62, 0x63, 0x64, - I2C_CLIENT_END - }; - - switch (type) { - case ADDRS_RADIO: - return radio_addrs; - case ADDRS_DEMOD: - return demod_addrs; - case ADDRS_TV: - return tv_addrs; - case ADDRS_TV_WITH_DEMOD: - return tv_addrs + 4; - } - return NULL; -} -EXPORT_SYMBOL_GPL(v4l2_i2c_tuner_addrs); - -#endif /* defined(CONFIG_I2C) */ - -#if defined(CONFIG_SPI) - -/* Load an spi sub-device. */ - -void v4l2_spi_subdev_init(struct v4l2_subdev *sd, struct spi_device *spi, - const struct v4l2_subdev_ops *ops) -{ - v4l2_subdev_init(sd, ops); - sd->flags |= V4L2_SUBDEV_FL_IS_SPI; - /* the owner is the same as the spi_device's driver owner */ - sd->owner = spi->dev.driver->owner; - /* spi_device and v4l2_subdev point to one another */ - v4l2_set_subdevdata(sd, spi); - spi_set_drvdata(spi, sd); - /* initialize name */ - strlcpy(sd->name, spi->dev.driver->name, sizeof(sd->name)); -} -EXPORT_SYMBOL_GPL(v4l2_spi_subdev_init); - -struct v4l2_subdev *v4l2_spi_new_subdev(struct v4l2_device *v4l2_dev, - struct spi_master *master, struct spi_board_info *info) -{ - struct v4l2_subdev *sd = NULL; - struct spi_device *spi = NULL; - - BUG_ON(!v4l2_dev); - - if (info->modalias) - request_module(info->modalias); - - spi = spi_new_device(master, info); - - if (spi == NULL || spi->dev.driver == NULL) - goto error; - - if (!try_module_get(spi->dev.driver->owner)) - goto error; - - sd = spi_get_drvdata(spi); - - /* Register with the v4l2_device which increases the module's - use count as well. */ - if (v4l2_device_register_subdev(v4l2_dev, sd)) - sd = NULL; - - /* Decrease the module use count to match the first try_module_get. */ - module_put(spi->dev.driver->owner); - -error: - /* If we have a client but no subdev, then something went wrong and - we must unregister the client. */ - if (spi && sd == NULL) - spi_unregister_device(spi); - - return sd; -} -EXPORT_SYMBOL_GPL(v4l2_spi_new_subdev); - -#endif /* defined(CONFIG_SPI) */ - -/* Clamp x to be between min and max, aligned to a multiple of 2^align. min - * and max don't have to be aligned, but there must be at least one valid - * value. E.g., min=17,max=31,align=4 is not allowed as there are no multiples - * of 16 between 17 and 31. */ -static unsigned int clamp_align(unsigned int x, unsigned int min, - unsigned int max, unsigned int align) -{ - /* Bits that must be zero to be aligned */ - unsigned int mask = ~((1 << align) - 1); - - /* Round to nearest aligned value */ - if (align) - x = (x + (1 << (align - 1))) & mask; - - /* Clamp to aligned value of min and max */ - if (x < min) - x = (min + ~mask) & mask; - else if (x > max) - x = max & mask; - - return x; -} - -/* Bound an image to have a width between wmin and wmax, and height between - * hmin and hmax, inclusive. Additionally, the width will be a multiple of - * 2^walign, the height will be a multiple of 2^halign, and the overall size - * (width*height) will be a multiple of 2^salign. The image may be shrunk - * or enlarged to fit the alignment constraints. - * - * The width or height maximum must not be smaller than the corresponding - * minimum. The alignments must not be so high there are no possible image - * sizes within the allowed bounds. wmin and hmin must be at least 1 - * (don't use 0). If you don't care about a certain alignment, specify 0, - * as 2^0 is 1 and one byte alignment is equivalent to no alignment. If - * you only want to adjust downward, specify a maximum that's the same as - * the initial value. - */ -void v4l_bound_align_image(u32 *w, unsigned int wmin, unsigned int wmax, - unsigned int walign, - u32 *h, unsigned int hmin, unsigned int hmax, - unsigned int halign, unsigned int salign) -{ - *w = clamp_align(*w, wmin, wmax, walign); - *h = clamp_align(*h, hmin, hmax, halign); - - /* Usually we don't need to align the size and are done now. */ - if (!salign) - return; - - /* How much alignment do we have? */ - walign = __ffs(*w); - halign = __ffs(*h); - /* Enough to satisfy the image alignment? */ - if (walign + halign < salign) { - /* Max walign where there is still a valid width */ - unsigned int wmaxa = __fls(wmax ^ (wmin - 1)); - /* Max halign where there is still a valid height */ - unsigned int hmaxa = __fls(hmax ^ (hmin - 1)); - - /* up the smaller alignment until we have enough */ - do { - if (halign >= hmaxa || - (walign <= halign && walign < wmaxa)) { - *w = clamp_align(*w, wmin, wmax, walign + 1); - walign = __ffs(*w); - } else { - *h = clamp_align(*h, hmin, hmax, halign + 1); - halign = __ffs(*h); - } - } while (halign + walign < salign); - } -} -EXPORT_SYMBOL_GPL(v4l_bound_align_image); - -/** - * v4l_fill_dv_preset_info - fill description of a digital video preset - * @preset - preset value - * @info - pointer to struct v4l2_dv_enum_preset - * - * drivers can use this helper function to fill description of dv preset - * in info. - */ -int v4l_fill_dv_preset_info(u32 preset, struct v4l2_dv_enum_preset *info) -{ - static const struct v4l2_dv_preset_info { - u16 width; - u16 height; - const char *name; - } dv_presets[] = { - { 0, 0, "Invalid" }, /* V4L2_DV_INVALID */ - { 720, 480, "480p@59.94" }, /* V4L2_DV_480P59_94 */ - { 720, 576, "576p@50" }, /* V4L2_DV_576P50 */ - { 1280, 720, "720p@24" }, /* V4L2_DV_720P24 */ - { 1280, 720, "720p@25" }, /* V4L2_DV_720P25 */ - { 1280, 720, "720p@30" }, /* V4L2_DV_720P30 */ - { 1280, 720, "720p@50" }, /* V4L2_DV_720P50 */ - { 1280, 720, "720p@59.94" }, /* V4L2_DV_720P59_94 */ - { 1280, 720, "720p@60" }, /* V4L2_DV_720P60 */ - { 1920, 1080, "1080i@29.97" }, /* V4L2_DV_1080I29_97 */ - { 1920, 1080, "1080i@30" }, /* V4L2_DV_1080I30 */ - { 1920, 1080, "1080i@25" }, /* V4L2_DV_1080I25 */ - { 1920, 1080, "1080i@50" }, /* V4L2_DV_1080I50 */ - { 1920, 1080, "1080i@60" }, /* V4L2_DV_1080I60 */ - { 1920, 1080, "1080p@24" }, /* V4L2_DV_1080P24 */ - { 1920, 1080, "1080p@25" }, /* V4L2_DV_1080P25 */ - { 1920, 1080, "1080p@30" }, /* V4L2_DV_1080P30 */ - { 1920, 1080, "1080p@50" }, /* V4L2_DV_1080P50 */ - { 1920, 1080, "1080p@60" }, /* V4L2_DV_1080P60 */ - }; - - if (info == NULL || preset >= ARRAY_SIZE(dv_presets)) - return -EINVAL; - - info->preset = preset; - info->width = dv_presets[preset].width; - info->height = dv_presets[preset].height; - strlcpy(info->name, dv_presets[preset].name, sizeof(info->name)); - return 0; -} -EXPORT_SYMBOL_GPL(v4l_fill_dv_preset_info); - -const struct v4l2_frmsize_discrete *v4l2_find_nearest_format( - const struct v4l2_discrete_probe *probe, - s32 width, s32 height) -{ - int i; - u32 error, min_error = UINT_MAX; - const struct v4l2_frmsize_discrete *size, *best = NULL; - - if (!probe) - return best; - - for (i = 0, size = probe->sizes; i < probe->num_sizes; i++, size++) { - error = abs(size->width - width) + abs(size->height - height); - if (error < min_error) { - min_error = error; - best = size; - } - if (!error) - break; - } - - return best; -} -EXPORT_SYMBOL_GPL(v4l2_find_nearest_format); diff --git a/drivers/media/video/v4l2-compat-ioctl32.c b/drivers/media/video/v4l2-compat-ioctl32.c deleted file mode 100644 index 9ebd5c540d10..000000000000 --- a/drivers/media/video/v4l2-compat-ioctl32.c +++ /dev/null @@ -1,1045 +0,0 @@ -/* - * ioctl32.c: Conversion between 32bit and 64bit native ioctls. - * Separated from fs stuff by Arnd Bergmann <arnd@arndb.de> - * - * Copyright (C) 1997-2000 Jakub Jelinek (jakub@redhat.com) - * Copyright (C) 1998 Eddie C. Dost (ecd@skynet.be) - * Copyright (C) 2001,2002 Andi Kleen, SuSE Labs - * Copyright (C) 2003 Pavel Machek (pavel@ucw.cz) - * Copyright (C) 2005 Philippe De Muyter (phdm@macqel.be) - * Copyright (C) 2008 Hans Verkuil <hverkuil@xs4all.nl> - * - * These routines maintain argument size conversion between 32bit and 64bit - * ioctls. - */ - -#include <linux/compat.h> -#include <linux/module.h> -#include <linux/videodev2.h> -#include <media/v4l2-dev.h> -#include <media/v4l2-ioctl.h> - -static long native_ioctl(struct file *file, unsigned int cmd, unsigned long arg) -{ - long ret = -ENOIOCTLCMD; - - if (file->f_op->unlocked_ioctl) - ret = file->f_op->unlocked_ioctl(file, cmd, arg); - - return ret; -} - - -struct v4l2_clip32 { - struct v4l2_rect c; - compat_caddr_t next; -}; - -struct v4l2_window32 { - struct v4l2_rect w; - __u32 field; /* enum v4l2_field */ - __u32 chromakey; - compat_caddr_t clips; /* actually struct v4l2_clip32 * */ - __u32 clipcount; - compat_caddr_t bitmap; -}; - -static int get_v4l2_window32(struct v4l2_window *kp, struct v4l2_window32 __user *up) -{ - if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_window32)) || - copy_from_user(&kp->w, &up->w, sizeof(up->w)) || - get_user(kp->field, &up->field) || - get_user(kp->chromakey, &up->chromakey) || - get_user(kp->clipcount, &up->clipcount)) - return -EFAULT; - if (kp->clipcount > 2048) - return -EINVAL; - if (kp->clipcount) { - struct v4l2_clip32 __user *uclips; - struct v4l2_clip __user *kclips; - int n = kp->clipcount; - compat_caddr_t p; - - if (get_user(p, &up->clips)) - return -EFAULT; - uclips = compat_ptr(p); - kclips = compat_alloc_user_space(n * sizeof(struct v4l2_clip)); - kp->clips = kclips; - while (--n >= 0) { - if (copy_in_user(&kclips->c, &uclips->c, sizeof(uclips->c))) - return -EFAULT; - if (put_user(n ? kclips + 1 : NULL, &kclips->next)) - return -EFAULT; - uclips += 1; - kclips += 1; - } - } else - kp->clips = NULL; - return 0; -} - -static int put_v4l2_window32(struct v4l2_window *kp, struct v4l2_window32 __user *up) -{ - if (copy_to_user(&up->w, &kp->w, sizeof(kp->w)) || - put_user(kp->field, &up->field) || - put_user(kp->chromakey, &up->chromakey) || - put_user(kp->clipcount, &up->clipcount)) - return -EFAULT; - return 0; -} - -static inline int get_v4l2_pix_format(struct v4l2_pix_format *kp, struct v4l2_pix_format __user *up) -{ - if (copy_from_user(kp, up, sizeof(struct v4l2_pix_format))) - return -EFAULT; - return 0; -} - -static inline int get_v4l2_pix_format_mplane(struct v4l2_pix_format_mplane *kp, - struct v4l2_pix_format_mplane __user *up) -{ - if (copy_from_user(kp, up, sizeof(struct v4l2_pix_format_mplane))) - return -EFAULT; - return 0; -} - -static inline int put_v4l2_pix_format(struct v4l2_pix_format *kp, struct v4l2_pix_format __user *up) -{ - if (copy_to_user(up, kp, sizeof(struct v4l2_pix_format))) - return -EFAULT; - return 0; -} - -static inline int put_v4l2_pix_format_mplane(struct v4l2_pix_format_mplane *kp, - struct v4l2_pix_format_mplane __user *up) -{ - if (copy_to_user(up, kp, sizeof(struct v4l2_pix_format_mplane))) - return -EFAULT; - return 0; -} - -static inline int get_v4l2_vbi_format(struct v4l2_vbi_format *kp, struct v4l2_vbi_format __user *up) -{ - if (copy_from_user(kp, up, sizeof(struct v4l2_vbi_format))) - return -EFAULT; - return 0; -} - -static inline int put_v4l2_vbi_format(struct v4l2_vbi_format *kp, struct v4l2_vbi_format __user *up) -{ - if (copy_to_user(up, kp, sizeof(struct v4l2_vbi_format))) - return -EFAULT; - return 0; -} - -static inline int get_v4l2_sliced_vbi_format(struct v4l2_sliced_vbi_format *kp, struct v4l2_sliced_vbi_format __user *up) -{ - if (copy_from_user(kp, up, sizeof(struct v4l2_sliced_vbi_format))) - return -EFAULT; - return 0; -} - -static inline int put_v4l2_sliced_vbi_format(struct v4l2_sliced_vbi_format *kp, struct v4l2_sliced_vbi_format __user *up) -{ - if (copy_to_user(up, kp, sizeof(struct v4l2_sliced_vbi_format))) - return -EFAULT; - return 0; -} - -struct v4l2_format32 { - __u32 type; /* enum v4l2_buf_type */ - union { - struct v4l2_pix_format pix; - struct v4l2_pix_format_mplane pix_mp; - struct v4l2_window32 win; - struct v4l2_vbi_format vbi; - struct v4l2_sliced_vbi_format sliced; - __u8 raw_data[200]; /* user-defined */ - } fmt; -}; - -/** - * struct v4l2_create_buffers32 - VIDIOC_CREATE_BUFS32 argument - * @index: on return, index of the first created buffer - * @count: entry: number of requested buffers, - * return: number of created buffers - * @memory: buffer memory type - * @format: frame format, for which buffers are requested - * @reserved: future extensions - */ -struct v4l2_create_buffers32 { - __u32 index; - __u32 count; - __u32 memory; /* enum v4l2_memory */ - struct v4l2_format32 format; - __u32 reserved[8]; -}; - -static int __get_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user *up) -{ - switch (kp->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - case V4L2_BUF_TYPE_VIDEO_OUTPUT: - return get_v4l2_pix_format(&kp->fmt.pix, &up->fmt.pix); - case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: - case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: - return get_v4l2_pix_format_mplane(&kp->fmt.pix_mp, - &up->fmt.pix_mp); - case V4L2_BUF_TYPE_VIDEO_OVERLAY: - case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: - return get_v4l2_window32(&kp->fmt.win, &up->fmt.win); - case V4L2_BUF_TYPE_VBI_CAPTURE: - case V4L2_BUF_TYPE_VBI_OUTPUT: - return get_v4l2_vbi_format(&kp->fmt.vbi, &up->fmt.vbi); - case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: - case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: - return get_v4l2_sliced_vbi_format(&kp->fmt.sliced, &up->fmt.sliced); - case V4L2_BUF_TYPE_PRIVATE: - if (copy_from_user(kp, up, sizeof(kp->fmt.raw_data))) - return -EFAULT; - return 0; - default: - printk(KERN_INFO "compat_ioctl32: unexpected VIDIOC_FMT type %d\n", - kp->type); - return -EINVAL; - } -} - -static int get_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user *up) -{ - if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_format32)) || - get_user(kp->type, &up->type)) - return -EFAULT; - return __get_v4l2_format32(kp, up); -} - -static int get_v4l2_create32(struct v4l2_create_buffers *kp, struct v4l2_create_buffers32 __user *up) -{ - if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_create_buffers32)) || - copy_from_user(kp, up, offsetof(struct v4l2_create_buffers32, format.fmt))) - return -EFAULT; - return __get_v4l2_format32(&kp->format, &up->format); -} - -static int __put_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user *up) -{ - switch (kp->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - case V4L2_BUF_TYPE_VIDEO_OUTPUT: - return put_v4l2_pix_format(&kp->fmt.pix, &up->fmt.pix); - case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: - case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: - return put_v4l2_pix_format_mplane(&kp->fmt.pix_mp, - &up->fmt.pix_mp); - case V4L2_BUF_TYPE_VIDEO_OVERLAY: - case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: - return put_v4l2_window32(&kp->fmt.win, &up->fmt.win); - case V4L2_BUF_TYPE_VBI_CAPTURE: - case V4L2_BUF_TYPE_VBI_OUTPUT: - return put_v4l2_vbi_format(&kp->fmt.vbi, &up->fmt.vbi); - case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: - case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: - return put_v4l2_sliced_vbi_format(&kp->fmt.sliced, &up->fmt.sliced); - case V4L2_BUF_TYPE_PRIVATE: - if (copy_to_user(up, kp, sizeof(up->fmt.raw_data))) - return -EFAULT; - return 0; - default: - printk(KERN_INFO "compat_ioctl32: unexpected VIDIOC_FMT type %d\n", - kp->type); - return -EINVAL; - } -} - -static int put_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user *up) -{ - if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_format32)) || - put_user(kp->type, &up->type)) - return -EFAULT; - return __put_v4l2_format32(kp, up); -} - -static int put_v4l2_create32(struct v4l2_create_buffers *kp, struct v4l2_create_buffers32 __user *up) -{ - if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_create_buffers32)) || - copy_to_user(up, kp, offsetof(struct v4l2_create_buffers32, format.fmt))) - return -EFAULT; - return __put_v4l2_format32(&kp->format, &up->format); -} - -struct v4l2_standard32 { - __u32 index; - __u32 id[2]; /* __u64 would get the alignment wrong */ - __u8 name[24]; - struct v4l2_fract frameperiod; /* Frames, not fields */ - __u32 framelines; - __u32 reserved[4]; -}; - -static int get_v4l2_standard32(struct v4l2_standard *kp, struct v4l2_standard32 __user *up) -{ - /* other fields are not set by the user, nor used by the driver */ - if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_standard32)) || - get_user(kp->index, &up->index)) - return -EFAULT; - return 0; -} - -static int put_v4l2_standard32(struct v4l2_standard *kp, struct v4l2_standard32 __user *up) -{ - if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_standard32)) || - put_user(kp->index, &up->index) || - copy_to_user(up->id, &kp->id, sizeof(__u64)) || - copy_to_user(up->name, kp->name, 24) || - copy_to_user(&up->frameperiod, &kp->frameperiod, sizeof(kp->frameperiod)) || - put_user(kp->framelines, &up->framelines) || - copy_to_user(up->reserved, kp->reserved, 4 * sizeof(__u32))) - return -EFAULT; - return 0; -} - -struct v4l2_plane32 { - __u32 bytesused; - __u32 length; - union { - __u32 mem_offset; - compat_long_t userptr; - } m; - __u32 data_offset; - __u32 reserved[11]; -}; - -struct v4l2_buffer32 { - __u32 index; - __u32 type; /* enum v4l2_buf_type */ - __u32 bytesused; - __u32 flags; - __u32 field; /* enum v4l2_field */ - struct compat_timeval timestamp; - struct v4l2_timecode timecode; - __u32 sequence; - - /* memory location */ - __u32 memory; /* enum v4l2_memory */ - union { - __u32 offset; - compat_long_t userptr; - compat_caddr_t planes; - } m; - __u32 length; - __u32 reserved2; - __u32 reserved; -}; - -static int get_v4l2_plane32(struct v4l2_plane *up, struct v4l2_plane32 *up32, - enum v4l2_memory memory) -{ - void __user *up_pln; - compat_long_t p; - - if (copy_in_user(up, up32, 2 * sizeof(__u32)) || - copy_in_user(&up->data_offset, &up32->data_offset, - sizeof(__u32))) - return -EFAULT; - - if (memory == V4L2_MEMORY_USERPTR) { - if (get_user(p, &up32->m.userptr)) - return -EFAULT; - up_pln = compat_ptr(p); - if (put_user((unsigned long)up_pln, &up->m.userptr)) - return -EFAULT; - } else { - if (copy_in_user(&up->m.mem_offset, &up32->m.mem_offset, - sizeof(__u32))) - return -EFAULT; - } - - return 0; -} - -static int put_v4l2_plane32(struct v4l2_plane *up, struct v4l2_plane32 *up32, - enum v4l2_memory memory) -{ - if (copy_in_user(up32, up, 2 * sizeof(__u32)) || - copy_in_user(&up32->data_offset, &up->data_offset, - sizeof(__u32))) - return -EFAULT; - - /* For MMAP, driver might've set up the offset, so copy it back. - * USERPTR stays the same (was userspace-provided), so no copying. */ - if (memory == V4L2_MEMORY_MMAP) - if (copy_in_user(&up32->m.mem_offset, &up->m.mem_offset, - sizeof(__u32))) - return -EFAULT; - - return 0; -} - -static int get_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user *up) -{ - struct v4l2_plane32 __user *uplane32; - struct v4l2_plane __user *uplane; - compat_caddr_t p; - int num_planes; - int ret; - - if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_buffer32)) || - get_user(kp->index, &up->index) || - get_user(kp->type, &up->type) || - get_user(kp->flags, &up->flags) || - get_user(kp->memory, &up->memory)) - return -EFAULT; - - if (V4L2_TYPE_IS_OUTPUT(kp->type)) - if (get_user(kp->bytesused, &up->bytesused) || - get_user(kp->field, &up->field) || - get_user(kp->timestamp.tv_sec, &up->timestamp.tv_sec) || - get_user(kp->timestamp.tv_usec, - &up->timestamp.tv_usec)) - return -EFAULT; - - if (V4L2_TYPE_IS_MULTIPLANAR(kp->type)) { - if (get_user(kp->length, &up->length)) - return -EFAULT; - - num_planes = kp->length; - if (num_planes == 0) { - kp->m.planes = NULL; - /* num_planes == 0 is legal, e.g. when userspace doesn't - * need planes array on DQBUF*/ - return 0; - } - - if (get_user(p, &up->m.planes)) - return -EFAULT; - - uplane32 = compat_ptr(p); - if (!access_ok(VERIFY_READ, uplane32, - num_planes * sizeof(struct v4l2_plane32))) - return -EFAULT; - - /* We don't really care if userspace decides to kill itself - * by passing a very big num_planes value */ - uplane = compat_alloc_user_space(num_planes * - sizeof(struct v4l2_plane)); - kp->m.planes = uplane; - - while (--num_planes >= 0) { - ret = get_v4l2_plane32(uplane, uplane32, kp->memory); - if (ret) - return ret; - ++uplane; - ++uplane32; - } - } else { - switch (kp->memory) { - case V4L2_MEMORY_MMAP: - if (get_user(kp->length, &up->length) || - get_user(kp->m.offset, &up->m.offset)) - return -EFAULT; - break; - case V4L2_MEMORY_USERPTR: - { - compat_long_t tmp; - - if (get_user(kp->length, &up->length) || - get_user(tmp, &up->m.userptr)) - return -EFAULT; - - kp->m.userptr = (unsigned long)compat_ptr(tmp); - } - break; - case V4L2_MEMORY_OVERLAY: - if (get_user(kp->m.offset, &up->m.offset)) - return -EFAULT; - break; - } - } - - return 0; -} - -static int put_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user *up) -{ - struct v4l2_plane32 __user *uplane32; - struct v4l2_plane __user *uplane; - compat_caddr_t p; - int num_planes; - int ret; - - if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_buffer32)) || - put_user(kp->index, &up->index) || - put_user(kp->type, &up->type) || - put_user(kp->flags, &up->flags) || - put_user(kp->memory, &up->memory)) - return -EFAULT; - - if (put_user(kp->bytesused, &up->bytesused) || - put_user(kp->field, &up->field) || - put_user(kp->timestamp.tv_sec, &up->timestamp.tv_sec) || - put_user(kp->timestamp.tv_usec, &up->timestamp.tv_usec) || - copy_to_user(&up->timecode, &kp->timecode, sizeof(struct v4l2_timecode)) || - put_user(kp->sequence, &up->sequence) || - put_user(kp->reserved2, &up->reserved2) || - put_user(kp->reserved, &up->reserved)) - return -EFAULT; - - if (V4L2_TYPE_IS_MULTIPLANAR(kp->type)) { - num_planes = kp->length; - if (num_planes == 0) - return 0; - - uplane = kp->m.planes; - if (get_user(p, &up->m.planes)) - return -EFAULT; - uplane32 = compat_ptr(p); - - while (--num_planes >= 0) { - ret = put_v4l2_plane32(uplane, uplane32, kp->memory); - if (ret) - return ret; - ++uplane; - ++uplane32; - } - } else { - switch (kp->memory) { - case V4L2_MEMORY_MMAP: - if (put_user(kp->length, &up->length) || - put_user(kp->m.offset, &up->m.offset)) - return -EFAULT; - break; - case V4L2_MEMORY_USERPTR: - if (put_user(kp->length, &up->length) || - put_user(kp->m.userptr, &up->m.userptr)) - return -EFAULT; - break; - case V4L2_MEMORY_OVERLAY: - if (put_user(kp->m.offset, &up->m.offset)) - return -EFAULT; - break; - } - } - - return 0; -} - -struct v4l2_framebuffer32 { - __u32 capability; - __u32 flags; - compat_caddr_t base; - struct v4l2_pix_format fmt; -}; - -static int get_v4l2_framebuffer32(struct v4l2_framebuffer *kp, struct v4l2_framebuffer32 __user *up) -{ - u32 tmp; - - if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_framebuffer32)) || - get_user(tmp, &up->base) || - get_user(kp->capability, &up->capability) || - get_user(kp->flags, &up->flags)) - return -EFAULT; - kp->base = compat_ptr(tmp); - get_v4l2_pix_format(&kp->fmt, &up->fmt); - return 0; -} - -static int put_v4l2_framebuffer32(struct v4l2_framebuffer *kp, struct v4l2_framebuffer32 __user *up) -{ - u32 tmp = (u32)((unsigned long)kp->base); - - if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_framebuffer32)) || - put_user(tmp, &up->base) || - put_user(kp->capability, &up->capability) || - put_user(kp->flags, &up->flags)) - return -EFAULT; - put_v4l2_pix_format(&kp->fmt, &up->fmt); - return 0; -} - -struct v4l2_input32 { - __u32 index; /* Which input */ - __u8 name[32]; /* Label */ - __u32 type; /* Type of input */ - __u32 audioset; /* Associated audios (bitfield) */ - __u32 tuner; /* Associated tuner */ - v4l2_std_id std; - __u32 status; - __u32 reserved[4]; -} __attribute__ ((packed)); - -/* The 64-bit v4l2_input struct has extra padding at the end of the struct. - Otherwise it is identical to the 32-bit version. */ -static inline int get_v4l2_input32(struct v4l2_input *kp, struct v4l2_input32 __user *up) -{ - if (copy_from_user(kp, up, sizeof(struct v4l2_input32))) - return -EFAULT; - return 0; -} - -static inline int put_v4l2_input32(struct v4l2_input *kp, struct v4l2_input32 __user *up) -{ - if (copy_to_user(up, kp, sizeof(struct v4l2_input32))) - return -EFAULT; - return 0; -} - -struct v4l2_ext_controls32 { - __u32 ctrl_class; - __u32 count; - __u32 error_idx; - __u32 reserved[2]; - compat_caddr_t controls; /* actually struct v4l2_ext_control32 * */ -}; - -struct v4l2_ext_control32 { - __u32 id; - __u32 size; - __u32 reserved2[1]; - union { - __s32 value; - __s64 value64; - compat_caddr_t string; /* actually char * */ - }; -} __attribute__ ((packed)); - -/* The following function really belong in v4l2-common, but that causes - a circular dependency between modules. We need to think about this, but - for now this will do. */ - -/* Return non-zero if this control is a pointer type. Currently only - type STRING is a pointer type. */ -static inline int ctrl_is_pointer(u32 id) -{ - switch (id) { - case V4L2_CID_RDS_TX_PS_NAME: - case V4L2_CID_RDS_TX_RADIO_TEXT: - return 1; - default: - return 0; - } -} - -static int get_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext_controls32 __user *up) -{ - struct v4l2_ext_control32 __user *ucontrols; - struct v4l2_ext_control __user *kcontrols; - int n; - compat_caddr_t p; - - if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_ext_controls32)) || - get_user(kp->ctrl_class, &up->ctrl_class) || - get_user(kp->count, &up->count) || - get_user(kp->error_idx, &up->error_idx) || - copy_from_user(kp->reserved, up->reserved, sizeof(kp->reserved))) - return -EFAULT; - n = kp->count; - if (n == 0) { - kp->controls = NULL; - return 0; - } - if (get_user(p, &up->controls)) - return -EFAULT; - ucontrols = compat_ptr(p); - if (!access_ok(VERIFY_READ, ucontrols, - n * sizeof(struct v4l2_ext_control32))) - return -EFAULT; - kcontrols = compat_alloc_user_space(n * sizeof(struct v4l2_ext_control)); - kp->controls = kcontrols; - while (--n >= 0) { - if (copy_in_user(kcontrols, ucontrols, sizeof(*ucontrols))) - return -EFAULT; - if (ctrl_is_pointer(kcontrols->id)) { - void __user *s; - - if (get_user(p, &ucontrols->string)) - return -EFAULT; - s = compat_ptr(p); - if (put_user(s, &kcontrols->string)) - return -EFAULT; - } - ucontrols++; - kcontrols++; - } - return 0; -} - -static int put_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext_controls32 __user *up) -{ - struct v4l2_ext_control32 __user *ucontrols; - struct v4l2_ext_control __user *kcontrols = kp->controls; - int n = kp->count; - compat_caddr_t p; - - if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_ext_controls32)) || - put_user(kp->ctrl_class, &up->ctrl_class) || - put_user(kp->count, &up->count) || - put_user(kp->error_idx, &up->error_idx) || - copy_to_user(up->reserved, kp->reserved, sizeof(up->reserved))) - return -EFAULT; - if (!kp->count) - return 0; - - if (get_user(p, &up->controls)) - return -EFAULT; - ucontrols = compat_ptr(p); - if (!access_ok(VERIFY_WRITE, ucontrols, - n * sizeof(struct v4l2_ext_control32))) - return -EFAULT; - - while (--n >= 0) { - unsigned size = sizeof(*ucontrols); - - /* Do not modify the pointer when copying a pointer control. - The contents of the pointer was changed, not the pointer - itself. */ - if (ctrl_is_pointer(kcontrols->id)) - size -= sizeof(ucontrols->value64); - if (copy_in_user(ucontrols, kcontrols, size)) - return -EFAULT; - ucontrols++; - kcontrols++; - } - return 0; -} - -struct v4l2_event32 { - __u32 type; - union { - __u8 data[64]; - } u; - __u32 pending; - __u32 sequence; - struct compat_timespec timestamp; - __u32 id; - __u32 reserved[8]; -}; - -static int put_v4l2_event32(struct v4l2_event *kp, struct v4l2_event32 __user *up) -{ - if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_event32)) || - put_user(kp->type, &up->type) || - copy_to_user(&up->u, &kp->u, sizeof(kp->u)) || - put_user(kp->pending, &up->pending) || - put_user(kp->sequence, &up->sequence) || - put_compat_timespec(&kp->timestamp, &up->timestamp) || - put_user(kp->id, &up->id) || - copy_to_user(up->reserved, kp->reserved, 8 * sizeof(__u32))) - return -EFAULT; - return 0; -} - -#define VIDIOC_G_FMT32 _IOWR('V', 4, struct v4l2_format32) -#define VIDIOC_S_FMT32 _IOWR('V', 5, struct v4l2_format32) -#define VIDIOC_QUERYBUF32 _IOWR('V', 9, struct v4l2_buffer32) -#define VIDIOC_G_FBUF32 _IOR ('V', 10, struct v4l2_framebuffer32) -#define VIDIOC_S_FBUF32 _IOW ('V', 11, struct v4l2_framebuffer32) -#define VIDIOC_QBUF32 _IOWR('V', 15, struct v4l2_buffer32) -#define VIDIOC_DQBUF32 _IOWR('V', 17, struct v4l2_buffer32) -#define VIDIOC_ENUMSTD32 _IOWR('V', 25, struct v4l2_standard32) -#define VIDIOC_ENUMINPUT32 _IOWR('V', 26, struct v4l2_input32) -#define VIDIOC_TRY_FMT32 _IOWR('V', 64, struct v4l2_format32) -#define VIDIOC_G_EXT_CTRLS32 _IOWR('V', 71, struct v4l2_ext_controls32) -#define VIDIOC_S_EXT_CTRLS32 _IOWR('V', 72, struct v4l2_ext_controls32) -#define VIDIOC_TRY_EXT_CTRLS32 _IOWR('V', 73, struct v4l2_ext_controls32) -#define VIDIOC_DQEVENT32 _IOR ('V', 89, struct v4l2_event32) -#define VIDIOC_CREATE_BUFS32 _IOWR('V', 92, struct v4l2_create_buffers32) -#define VIDIOC_PREPARE_BUF32 _IOWR('V', 93, struct v4l2_buffer32) - -#define VIDIOC_OVERLAY32 _IOW ('V', 14, s32) -#define VIDIOC_STREAMON32 _IOW ('V', 18, s32) -#define VIDIOC_STREAMOFF32 _IOW ('V', 19, s32) -#define VIDIOC_G_INPUT32 _IOR ('V', 38, s32) -#define VIDIOC_S_INPUT32 _IOWR('V', 39, s32) -#define VIDIOC_G_OUTPUT32 _IOR ('V', 46, s32) -#define VIDIOC_S_OUTPUT32 _IOWR('V', 47, s32) - -static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long arg) -{ - union { - struct v4l2_format v2f; - struct v4l2_buffer v2b; - struct v4l2_framebuffer v2fb; - struct v4l2_input v2i; - struct v4l2_standard v2s; - struct v4l2_ext_controls v2ecs; - struct v4l2_event v2ev; - struct v4l2_create_buffers v2crt; - unsigned long vx; - int vi; - } karg; - void __user *up = compat_ptr(arg); - int compatible_arg = 1; - long err = 0; - - /* First, convert the command. */ - switch (cmd) { - case VIDIOC_G_FMT32: cmd = VIDIOC_G_FMT; break; - case VIDIOC_S_FMT32: cmd = VIDIOC_S_FMT; break; - case VIDIOC_QUERYBUF32: cmd = VIDIOC_QUERYBUF; break; - case VIDIOC_G_FBUF32: cmd = VIDIOC_G_FBUF; break; - case VIDIOC_S_FBUF32: cmd = VIDIOC_S_FBUF; break; - case VIDIOC_QBUF32: cmd = VIDIOC_QBUF; break; - case VIDIOC_DQBUF32: cmd = VIDIOC_DQBUF; break; - case VIDIOC_ENUMSTD32: cmd = VIDIOC_ENUMSTD; break; - case VIDIOC_ENUMINPUT32: cmd = VIDIOC_ENUMINPUT; break; - case VIDIOC_TRY_FMT32: cmd = VIDIOC_TRY_FMT; break; - case VIDIOC_G_EXT_CTRLS32: cmd = VIDIOC_G_EXT_CTRLS; break; - case VIDIOC_S_EXT_CTRLS32: cmd = VIDIOC_S_EXT_CTRLS; break; - case VIDIOC_TRY_EXT_CTRLS32: cmd = VIDIOC_TRY_EXT_CTRLS; break; - case VIDIOC_DQEVENT32: cmd = VIDIOC_DQEVENT; break; - case VIDIOC_OVERLAY32: cmd = VIDIOC_OVERLAY; break; - case VIDIOC_STREAMON32: cmd = VIDIOC_STREAMON; break; - case VIDIOC_STREAMOFF32: cmd = VIDIOC_STREAMOFF; break; - case VIDIOC_G_INPUT32: cmd = VIDIOC_G_INPUT; break; - case VIDIOC_S_INPUT32: cmd = VIDIOC_S_INPUT; break; - case VIDIOC_G_OUTPUT32: cmd = VIDIOC_G_OUTPUT; break; - case VIDIOC_S_OUTPUT32: cmd = VIDIOC_S_OUTPUT; break; - case VIDIOC_CREATE_BUFS32: cmd = VIDIOC_CREATE_BUFS; break; - case VIDIOC_PREPARE_BUF32: cmd = VIDIOC_PREPARE_BUF; break; - } - - switch (cmd) { - case VIDIOC_OVERLAY: - case VIDIOC_STREAMON: - case VIDIOC_STREAMOFF: - case VIDIOC_S_INPUT: - case VIDIOC_S_OUTPUT: - err = get_user(karg.vi, (s32 __user *)up); - compatible_arg = 0; - break; - - case VIDIOC_G_INPUT: - case VIDIOC_G_OUTPUT: - compatible_arg = 0; - break; - - case VIDIOC_G_FMT: - case VIDIOC_S_FMT: - case VIDIOC_TRY_FMT: - err = get_v4l2_format32(&karg.v2f, up); - compatible_arg = 0; - break; - - case VIDIOC_CREATE_BUFS: - err = get_v4l2_create32(&karg.v2crt, up); - compatible_arg = 0; - break; - - case VIDIOC_PREPARE_BUF: - case VIDIOC_QUERYBUF: - case VIDIOC_QBUF: - case VIDIOC_DQBUF: - err = get_v4l2_buffer32(&karg.v2b, up); - compatible_arg = 0; - break; - - case VIDIOC_S_FBUF: - err = get_v4l2_framebuffer32(&karg.v2fb, up); - compatible_arg = 0; - break; - - case VIDIOC_G_FBUF: - compatible_arg = 0; - break; - - case VIDIOC_ENUMSTD: - err = get_v4l2_standard32(&karg.v2s, up); - compatible_arg = 0; - break; - - case VIDIOC_ENUMINPUT: - err = get_v4l2_input32(&karg.v2i, up); - compatible_arg = 0; - break; - - case VIDIOC_G_EXT_CTRLS: - case VIDIOC_S_EXT_CTRLS: - case VIDIOC_TRY_EXT_CTRLS: - err = get_v4l2_ext_controls32(&karg.v2ecs, up); - compatible_arg = 0; - break; - case VIDIOC_DQEVENT: - compatible_arg = 0; - break; - } - if (err) - return err; - - if (compatible_arg) - err = native_ioctl(file, cmd, (unsigned long)up); - else { - mm_segment_t old_fs = get_fs(); - - set_fs(KERNEL_DS); - err = native_ioctl(file, cmd, (unsigned long)&karg); - set_fs(old_fs); - } - - /* Special case: even after an error we need to put the - results back for these ioctls since the error_idx will - contain information on which control failed. */ - switch (cmd) { - case VIDIOC_G_EXT_CTRLS: - case VIDIOC_S_EXT_CTRLS: - case VIDIOC_TRY_EXT_CTRLS: - if (put_v4l2_ext_controls32(&karg.v2ecs, up)) - err = -EFAULT; - break; - } - if (err) - return err; - - switch (cmd) { - case VIDIOC_S_INPUT: - case VIDIOC_S_OUTPUT: - case VIDIOC_G_INPUT: - case VIDIOC_G_OUTPUT: - err = put_user(((s32)karg.vi), (s32 __user *)up); - break; - - case VIDIOC_G_FBUF: - err = put_v4l2_framebuffer32(&karg.v2fb, up); - break; - - case VIDIOC_DQEVENT: - err = put_v4l2_event32(&karg.v2ev, up); - break; - - case VIDIOC_G_FMT: - case VIDIOC_S_FMT: - case VIDIOC_TRY_FMT: - err = put_v4l2_format32(&karg.v2f, up); - break; - - case VIDIOC_CREATE_BUFS: - err = put_v4l2_create32(&karg.v2crt, up); - break; - - case VIDIOC_QUERYBUF: - case VIDIOC_QBUF: - case VIDIOC_DQBUF: - err = put_v4l2_buffer32(&karg.v2b, up); - break; - - case VIDIOC_ENUMSTD: - err = put_v4l2_standard32(&karg.v2s, up); - break; - - case VIDIOC_ENUMINPUT: - err = put_v4l2_input32(&karg.v2i, up); - break; - } - return err; -} - -long v4l2_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg) -{ - struct video_device *vdev = video_devdata(file); - long ret = -ENOIOCTLCMD; - - if (!file->f_op->unlocked_ioctl) - return ret; - - switch (cmd) { - case VIDIOC_QUERYCAP: - case VIDIOC_RESERVED: - case VIDIOC_ENUM_FMT: - case VIDIOC_G_FMT32: - case VIDIOC_S_FMT32: - case VIDIOC_REQBUFS: - case VIDIOC_QUERYBUF32: - case VIDIOC_G_FBUF32: - case VIDIOC_S_FBUF32: - case VIDIOC_OVERLAY32: - case VIDIOC_QBUF32: - case VIDIOC_DQBUF32: - case VIDIOC_STREAMON32: - case VIDIOC_STREAMOFF32: - case VIDIOC_G_PARM: - case VIDIOC_S_PARM: - case VIDIOC_G_STD: - case VIDIOC_S_STD: - case VIDIOC_ENUMSTD32: - case VIDIOC_ENUMINPUT32: - case VIDIOC_G_CTRL: - case VIDIOC_S_CTRL: - case VIDIOC_G_TUNER: - case VIDIOC_S_TUNER: - case VIDIOC_G_AUDIO: - case VIDIOC_S_AUDIO: - case VIDIOC_QUERYCTRL: - case VIDIOC_QUERYMENU: - case VIDIOC_G_INPUT32: - case VIDIOC_S_INPUT32: - case VIDIOC_G_OUTPUT32: - case VIDIOC_S_OUTPUT32: - case VIDIOC_ENUMOUTPUT: - case VIDIOC_G_AUDOUT: - case VIDIOC_S_AUDOUT: - case VIDIOC_G_MODULATOR: - case VIDIOC_S_MODULATOR: - case VIDIOC_S_FREQUENCY: - case VIDIOC_G_FREQUENCY: - case VIDIOC_CROPCAP: - case VIDIOC_G_CROP: - case VIDIOC_S_CROP: - case VIDIOC_G_SELECTION: - case VIDIOC_S_SELECTION: - case VIDIOC_G_JPEGCOMP: - case VIDIOC_S_JPEGCOMP: - case VIDIOC_QUERYSTD: - case VIDIOC_TRY_FMT32: - case VIDIOC_ENUMAUDIO: - case VIDIOC_ENUMAUDOUT: - case VIDIOC_G_PRIORITY: - case VIDIOC_S_PRIORITY: - case VIDIOC_G_SLICED_VBI_CAP: - case VIDIOC_LOG_STATUS: - case VIDIOC_G_EXT_CTRLS32: - case VIDIOC_S_EXT_CTRLS32: - case VIDIOC_TRY_EXT_CTRLS32: - case VIDIOC_ENUM_FRAMESIZES: - case VIDIOC_ENUM_FRAMEINTERVALS: - case VIDIOC_G_ENC_INDEX: - case VIDIOC_ENCODER_CMD: - case VIDIOC_TRY_ENCODER_CMD: - case VIDIOC_DECODER_CMD: - case VIDIOC_TRY_DECODER_CMD: - case VIDIOC_DBG_S_REGISTER: - case VIDIOC_DBG_G_REGISTER: - case VIDIOC_DBG_G_CHIP_IDENT: - case VIDIOC_S_HW_FREQ_SEEK: - case VIDIOC_ENUM_DV_PRESETS: - case VIDIOC_S_DV_PRESET: - case VIDIOC_G_DV_PRESET: - case VIDIOC_QUERY_DV_PRESET: - case VIDIOC_S_DV_TIMINGS: - case VIDIOC_G_DV_TIMINGS: - case VIDIOC_DQEVENT: - case VIDIOC_DQEVENT32: - case VIDIOC_SUBSCRIBE_EVENT: - case VIDIOC_UNSUBSCRIBE_EVENT: - case VIDIOC_CREATE_BUFS32: - case VIDIOC_PREPARE_BUF32: - case VIDIOC_ENUM_DV_TIMINGS: - case VIDIOC_QUERY_DV_TIMINGS: - case VIDIOC_DV_TIMINGS_CAP: - case VIDIOC_ENUM_FREQ_BANDS: - ret = do_video_ioctl(file, cmd, arg); - break; - - default: - if (vdev->fops->compat_ioctl32) - ret = vdev->fops->compat_ioctl32(file, cmd, arg); - - if (ret == -ENOIOCTLCMD) - printk(KERN_WARNING "compat_ioctl32: " - "unknown ioctl '%c', dir=%d, #%d (0x%08x)\n", - _IOC_TYPE(cmd), _IOC_DIR(cmd), _IOC_NR(cmd), - cmd); - break; - } - return ret; -} -EXPORT_SYMBOL_GPL(v4l2_compat_ioctl32); diff --git a/drivers/media/video/v4l2-ctrls.c b/drivers/media/video/v4l2-ctrls.c deleted file mode 100644 index b6a2ee71e5c3..000000000000 --- a/drivers/media/video/v4l2-ctrls.c +++ /dev/null @@ -1,2651 +0,0 @@ -/* - V4L2 controls framework implementation. - - Copyright (C) 2010 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 <linux/ctype.h> -#include <linux/slab.h> -#include <linux/export.h> -#include <media/v4l2-ioctl.h> -#include <media/v4l2-device.h> -#include <media/v4l2-ctrls.h> -#include <media/v4l2-event.h> -#include <media/v4l2-dev.h> - -#define has_op(master, op) \ - (master->ops && master->ops->op) -#define call_op(master, op) \ - (has_op(master, op) ? master->ops->op(master) : 0) - -/* Internal temporary helper struct, one for each v4l2_ext_control */ -struct v4l2_ctrl_helper { - /* Pointer to the control reference of the master control */ - struct v4l2_ctrl_ref *mref; - /* The control corresponding to the v4l2_ext_control ID field. */ - struct v4l2_ctrl *ctrl; - /* v4l2_ext_control index of the next control belonging to the - same cluster, or 0 if there isn't any. */ - u32 next; -}; - -/* Small helper function to determine if the autocluster is set to manual - mode. */ -static bool is_cur_manual(const struct v4l2_ctrl *master) -{ - return master->is_auto && master->cur.val == master->manual_mode_value; -} - -/* Same as above, but this checks the against the new value instead of the - current value. */ -static bool is_new_manual(const struct v4l2_ctrl *master) -{ - return master->is_auto && master->val == master->manual_mode_value; -} - -/* Returns NULL or a character pointer array containing the menu for - the given control ID. The pointer array ends with a NULL pointer. - An empty string signifies a menu entry that is invalid. This allows - drivers to disable certain options if it is not supported. */ -const char * const *v4l2_ctrl_get_menu(u32 id) -{ - static const char * const mpeg_audio_sampling_freq[] = { - "44.1 kHz", - "48 kHz", - "32 kHz", - NULL - }; - static const char * const mpeg_audio_encoding[] = { - "MPEG-1/2 Layer I", - "MPEG-1/2 Layer II", - "MPEG-1/2 Layer III", - "MPEG-2/4 AAC", - "AC-3", - NULL - }; - static const char * const mpeg_audio_l1_bitrate[] = { - "32 kbps", - "64 kbps", - "96 kbps", - "128 kbps", - "160 kbps", - "192 kbps", - "224 kbps", - "256 kbps", - "288 kbps", - "320 kbps", - "352 kbps", - "384 kbps", - "416 kbps", - "448 kbps", - NULL - }; - static const char * const mpeg_audio_l2_bitrate[] = { - "32 kbps", - "48 kbps", - "56 kbps", - "64 kbps", - "80 kbps", - "96 kbps", - "112 kbps", - "128 kbps", - "160 kbps", - "192 kbps", - "224 kbps", - "256 kbps", - "320 kbps", - "384 kbps", - NULL - }; - static const char * const mpeg_audio_l3_bitrate[] = { - "32 kbps", - "40 kbps", - "48 kbps", - "56 kbps", - "64 kbps", - "80 kbps", - "96 kbps", - "112 kbps", - "128 kbps", - "160 kbps", - "192 kbps", - "224 kbps", - "256 kbps", - "320 kbps", - NULL - }; - static const char * const mpeg_audio_ac3_bitrate[] = { - "32 kbps", - "40 kbps", - "48 kbps", - "56 kbps", - "64 kbps", - "80 kbps", - "96 kbps", - "112 kbps", - "128 kbps", - "160 kbps", - "192 kbps", - "224 kbps", - "256 kbps", - "320 kbps", - "384 kbps", - "448 kbps", - "512 kbps", - "576 kbps", - "640 kbps", - NULL - }; - static const char * const mpeg_audio_mode[] = { - "Stereo", - "Joint Stereo", - "Dual", - "Mono", - NULL - }; - static const char * const mpeg_audio_mode_extension[] = { - "Bound 4", - "Bound 8", - "Bound 12", - "Bound 16", - NULL - }; - static const char * const mpeg_audio_emphasis[] = { - "No Emphasis", - "50/15 us", - "CCITT J17", - NULL - }; - static const char * const mpeg_audio_crc[] = { - "No CRC", - "16-bit CRC", - NULL - }; - static const char * const mpeg_audio_dec_playback[] = { - "Auto", - "Stereo", - "Left", - "Right", - "Mono", - "Swapped Stereo", - NULL - }; - static const char * const mpeg_video_encoding[] = { - "MPEG-1", - "MPEG-2", - "MPEG-4 AVC", - NULL - }; - static const char * const mpeg_video_aspect[] = { - "1x1", - "4x3", - "16x9", - "2.21x1", - NULL - }; - static const char * const mpeg_video_bitrate_mode[] = { - "Variable Bitrate", - "Constant Bitrate", - NULL - }; - static const char * const mpeg_stream_type[] = { - "MPEG-2 Program Stream", - "MPEG-2 Transport Stream", - "MPEG-1 System Stream", - "MPEG-2 DVD-compatible Stream", - "MPEG-1 VCD-compatible Stream", - "MPEG-2 SVCD-compatible Stream", - NULL - }; - static const char * const mpeg_stream_vbi_fmt[] = { - "No VBI", - "Private Packet, IVTV Format", - NULL - }; - static const char * const camera_power_line_frequency[] = { - "Disabled", - "50 Hz", - "60 Hz", - "Auto", - NULL - }; - static const char * const camera_exposure_auto[] = { - "Auto Mode", - "Manual Mode", - "Shutter Priority Mode", - "Aperture Priority Mode", - NULL - }; - static const char * const camera_exposure_metering[] = { - "Average", - "Center Weighted", - "Spot", - NULL - }; - static const char * const camera_auto_focus_range[] = { - "Auto", - "Normal", - "Macro", - "Infinity", - NULL - }; - static const char * const colorfx[] = { - "None", - "Black & White", - "Sepia", - "Negative", - "Emboss", - "Sketch", - "Sky Blue", - "Grass Green", - "Skin Whiten", - "Vivid", - "Aqua", - "Art Freeze", - "Silhouette", - "Solarization", - "Antique", - "Set Cb/Cr", - NULL - }; - static const char * const auto_n_preset_white_balance[] = { - "Manual", - "Auto", - "Incandescent", - "Fluorescent", - "Fluorescent H", - "Horizon", - "Daylight", - "Flash", - "Cloudy", - "Shade", - NULL, - }; - static const char * const camera_iso_sensitivity_auto[] = { - "Manual", - "Auto", - NULL - }; - static const char * const scene_mode[] = { - "None", - "Backlight", - "Beach/Snow", - "Candle Light", - "Dusk/Dawn", - "Fall Colors", - "Fireworks", - "Landscape", - "Night", - "Party/Indoor", - "Portrait", - "Sports", - "Sunset", - "Text", - NULL - }; - static const char * const tune_preemphasis[] = { - "No Preemphasis", - "50 Microseconds", - "75 Microseconds", - NULL, - }; - static const char * const header_mode[] = { - "Separate Buffer", - "Joined With 1st Frame", - NULL, - }; - static const char * const multi_slice[] = { - "Single", - "Max Macroblocks", - "Max Bytes", - NULL, - }; - static const char * const entropy_mode[] = { - "CAVLC", - "CABAC", - NULL, - }; - static const char * const mpeg_h264_level[] = { - "1", - "1b", - "1.1", - "1.2", - "1.3", - "2", - "2.1", - "2.2", - "3", - "3.1", - "3.2", - "4", - "4.1", - "4.2", - "5", - "5.1", - NULL, - }; - static const char * const h264_loop_filter[] = { - "Enabled", - "Disabled", - "Disabled at Slice Boundary", - NULL, - }; - static const char * const h264_profile[] = { - "Baseline", - "Constrained Baseline", - "Main", - "Extended", - "High", - "High 10", - "High 422", - "High 444 Predictive", - "High 10 Intra", - "High 422 Intra", - "High 444 Intra", - "CAVLC 444 Intra", - "Scalable Baseline", - "Scalable High", - "Scalable High Intra", - "Multiview High", - NULL, - }; - static const char * const vui_sar_idc[] = { - "Unspecified", - "1:1", - "12:11", - "10:11", - "16:11", - "40:33", - "24:11", - "20:11", - "32:11", - "80:33", - "18:11", - "15:11", - "64:33", - "160:99", - "4:3", - "3:2", - "2:1", - "Extended SAR", - NULL, - }; - static const char * const mpeg_mpeg4_level[] = { - "0", - "0b", - "1", - "2", - "3", - "3b", - "4", - "5", - NULL, - }; - static const char * const mpeg4_profile[] = { - "Simple", - "Advanced Simple", - "Core", - "Simple Scalable", - "Advanced Coding Efficency", - NULL, - }; - - static const char * const flash_led_mode[] = { - "Off", - "Flash", - "Torch", - NULL, - }; - static const char * const flash_strobe_source[] = { - "Software", - "External", - NULL, - }; - - static const char * const jpeg_chroma_subsampling[] = { - "4:4:4", - "4:2:2", - "4:2:0", - "4:1:1", - "4:1:0", - "Gray", - NULL, - }; - - switch (id) { - case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: - return mpeg_audio_sampling_freq; - case V4L2_CID_MPEG_AUDIO_ENCODING: - return mpeg_audio_encoding; - case V4L2_CID_MPEG_AUDIO_L1_BITRATE: - return mpeg_audio_l1_bitrate; - case V4L2_CID_MPEG_AUDIO_L2_BITRATE: - return mpeg_audio_l2_bitrate; - case V4L2_CID_MPEG_AUDIO_L3_BITRATE: - return mpeg_audio_l3_bitrate; - case V4L2_CID_MPEG_AUDIO_AC3_BITRATE: - return mpeg_audio_ac3_bitrate; - case V4L2_CID_MPEG_AUDIO_MODE: - return mpeg_audio_mode; - case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION: - return mpeg_audio_mode_extension; - case V4L2_CID_MPEG_AUDIO_EMPHASIS: - return mpeg_audio_emphasis; - case V4L2_CID_MPEG_AUDIO_CRC: - return mpeg_audio_crc; - case V4L2_CID_MPEG_AUDIO_DEC_PLAYBACK: - case V4L2_CID_MPEG_AUDIO_DEC_MULTILINGUAL_PLAYBACK: - return mpeg_audio_dec_playback; - case V4L2_CID_MPEG_VIDEO_ENCODING: - return mpeg_video_encoding; - case V4L2_CID_MPEG_VIDEO_ASPECT: - return mpeg_video_aspect; - case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: - return mpeg_video_bitrate_mode; - case V4L2_CID_MPEG_STREAM_TYPE: - return mpeg_stream_type; - case V4L2_CID_MPEG_STREAM_VBI_FMT: - return mpeg_stream_vbi_fmt; - case V4L2_CID_POWER_LINE_FREQUENCY: - return camera_power_line_frequency; - case V4L2_CID_EXPOSURE_AUTO: - return camera_exposure_auto; - case V4L2_CID_EXPOSURE_METERING: - return camera_exposure_metering; - case V4L2_CID_AUTO_FOCUS_RANGE: - return camera_auto_focus_range; - case V4L2_CID_COLORFX: - return colorfx; - case V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE: - return auto_n_preset_white_balance; - case V4L2_CID_ISO_SENSITIVITY_AUTO: - return camera_iso_sensitivity_auto; - case V4L2_CID_SCENE_MODE: - return scene_mode; - case V4L2_CID_TUNE_PREEMPHASIS: - return tune_preemphasis; - case V4L2_CID_FLASH_LED_MODE: - return flash_led_mode; - case V4L2_CID_FLASH_STROBE_SOURCE: - return flash_strobe_source; - case V4L2_CID_MPEG_VIDEO_HEADER_MODE: - return header_mode; - case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE: - return multi_slice; - case V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE: - return entropy_mode; - case V4L2_CID_MPEG_VIDEO_H264_LEVEL: - return mpeg_h264_level; - case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE: - return h264_loop_filter; - case V4L2_CID_MPEG_VIDEO_H264_PROFILE: - return h264_profile; - case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_IDC: - return vui_sar_idc; - case V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL: - return mpeg_mpeg4_level; - case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE: - return mpeg4_profile; - case V4L2_CID_JPEG_CHROMA_SUBSAMPLING: - return jpeg_chroma_subsampling; - - default: - return NULL; - } -} -EXPORT_SYMBOL(v4l2_ctrl_get_menu); - -/* Return the control name. */ -const char *v4l2_ctrl_get_name(u32 id) -{ - switch (id) { - /* USER controls */ - /* Keep the order of the 'case's the same as in videodev2.h! */ - case V4L2_CID_USER_CLASS: return "User Controls"; - case V4L2_CID_BRIGHTNESS: return "Brightness"; - case V4L2_CID_CONTRAST: return "Contrast"; - case V4L2_CID_SATURATION: return "Saturation"; - case V4L2_CID_HUE: return "Hue"; - case V4L2_CID_AUDIO_VOLUME: return "Volume"; - case V4L2_CID_AUDIO_BALANCE: return "Balance"; - case V4L2_CID_AUDIO_BASS: return "Bass"; - case V4L2_CID_AUDIO_TREBLE: return "Treble"; - case V4L2_CID_AUDIO_MUTE: return "Mute"; - case V4L2_CID_AUDIO_LOUDNESS: return "Loudness"; - case V4L2_CID_BLACK_LEVEL: return "Black Level"; - case V4L2_CID_AUTO_WHITE_BALANCE: return "White Balance, Automatic"; - case V4L2_CID_DO_WHITE_BALANCE: return "Do White Balance"; - case V4L2_CID_RED_BALANCE: return "Red Balance"; - case V4L2_CID_BLUE_BALANCE: return "Blue Balance"; - case V4L2_CID_GAMMA: return "Gamma"; - case V4L2_CID_EXPOSURE: return "Exposure"; - case V4L2_CID_AUTOGAIN: return "Gain, Automatic"; - case V4L2_CID_GAIN: return "Gain"; - case V4L2_CID_HFLIP: return "Horizontal Flip"; - case V4L2_CID_VFLIP: return "Vertical Flip"; - case V4L2_CID_HCENTER: return "Horizontal Center"; - case V4L2_CID_VCENTER: return "Vertical Center"; - case V4L2_CID_POWER_LINE_FREQUENCY: return "Power Line Frequency"; - case V4L2_CID_HUE_AUTO: return "Hue, Automatic"; - case V4L2_CID_WHITE_BALANCE_TEMPERATURE: return "White Balance Temperature"; - case V4L2_CID_SHARPNESS: return "Sharpness"; - case V4L2_CID_BACKLIGHT_COMPENSATION: return "Backlight Compensation"; - case V4L2_CID_CHROMA_AGC: return "Chroma AGC"; - case V4L2_CID_COLOR_KILLER: return "Color Killer"; - case V4L2_CID_COLORFX: return "Color Effects"; - case V4L2_CID_AUTOBRIGHTNESS: return "Brightness, Automatic"; - case V4L2_CID_BAND_STOP_FILTER: return "Band-Stop Filter"; - case V4L2_CID_ROTATE: return "Rotate"; - case V4L2_CID_BG_COLOR: return "Background Color"; - case V4L2_CID_CHROMA_GAIN: return "Chroma Gain"; - case V4L2_CID_ILLUMINATORS_1: return "Illuminator 1"; - case V4L2_CID_ILLUMINATORS_2: return "Illuminator 2"; - case V4L2_CID_MIN_BUFFERS_FOR_CAPTURE: return "Min Number of Capture Buffers"; - case V4L2_CID_MIN_BUFFERS_FOR_OUTPUT: return "Min Number of Output Buffers"; - case V4L2_CID_ALPHA_COMPONENT: return "Alpha Component"; - case V4L2_CID_COLORFX_CBCR: return "Color Effects, CbCr"; - - /* MPEG controls */ - /* Keep the order of the 'case's the same as in videodev2.h! */ - case V4L2_CID_MPEG_CLASS: return "MPEG Encoder Controls"; - case V4L2_CID_MPEG_STREAM_TYPE: return "Stream Type"; - case V4L2_CID_MPEG_STREAM_PID_PMT: return "Stream PMT Program ID"; - case V4L2_CID_MPEG_STREAM_PID_AUDIO: return "Stream Audio Program ID"; - case V4L2_CID_MPEG_STREAM_PID_VIDEO: return "Stream Video Program ID"; - case V4L2_CID_MPEG_STREAM_PID_PCR: return "Stream PCR Program ID"; - case V4L2_CID_MPEG_STREAM_PES_ID_AUDIO: return "Stream PES Audio ID"; - case V4L2_CID_MPEG_STREAM_PES_ID_VIDEO: return "Stream PES Video ID"; - case V4L2_CID_MPEG_STREAM_VBI_FMT: return "Stream VBI Format"; - case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: return "Audio Sampling Frequency"; - case V4L2_CID_MPEG_AUDIO_ENCODING: return "Audio Encoding"; - case V4L2_CID_MPEG_AUDIO_L1_BITRATE: return "Audio Layer I Bitrate"; - case V4L2_CID_MPEG_AUDIO_L2_BITRATE: return "Audio Layer II Bitrate"; - case V4L2_CID_MPEG_AUDIO_L3_BITRATE: return "Audio Layer III Bitrate"; - case V4L2_CID_MPEG_AUDIO_MODE: return "Audio Stereo Mode"; - case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION: return "Audio Stereo Mode Extension"; - case V4L2_CID_MPEG_AUDIO_EMPHASIS: return "Audio Emphasis"; - case V4L2_CID_MPEG_AUDIO_CRC: return "Audio CRC"; - case V4L2_CID_MPEG_AUDIO_MUTE: return "Audio Mute"; - case V4L2_CID_MPEG_AUDIO_AAC_BITRATE: return "Audio AAC Bitrate"; - case V4L2_CID_MPEG_AUDIO_AC3_BITRATE: return "Audio AC-3 Bitrate"; - case V4L2_CID_MPEG_AUDIO_DEC_PLAYBACK: return "Audio Playback"; - case V4L2_CID_MPEG_AUDIO_DEC_MULTILINGUAL_PLAYBACK: return "Audio Multilingual Playback"; - case V4L2_CID_MPEG_VIDEO_ENCODING: return "Video Encoding"; - case V4L2_CID_MPEG_VIDEO_ASPECT: return "Video Aspect"; - case V4L2_CID_MPEG_VIDEO_B_FRAMES: return "Video B Frames"; - case V4L2_CID_MPEG_VIDEO_GOP_SIZE: return "Video GOP Size"; - case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE: return "Video GOP Closure"; - case V4L2_CID_MPEG_VIDEO_PULLDOWN: return "Video Pulldown"; - case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: return "Video Bitrate Mode"; - case V4L2_CID_MPEG_VIDEO_BITRATE: return "Video Bitrate"; - case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: return "Video Peak Bitrate"; - case V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION: return "Video Temporal Decimation"; - case V4L2_CID_MPEG_VIDEO_MUTE: return "Video Mute"; - case V4L2_CID_MPEG_VIDEO_MUTE_YUV: return "Video Mute YUV"; - case V4L2_CID_MPEG_VIDEO_DECODER_SLICE_INTERFACE: return "Decoder Slice Interface"; - case V4L2_CID_MPEG_VIDEO_DECODER_MPEG4_DEBLOCK_FILTER: return "MPEG4 Loop Filter Enable"; - case V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB: return "Number of Intra Refresh MBs"; - case V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE: return "Frame Level Rate Control Enable"; - case V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE: return "H264 MB Level Rate Control"; - case V4L2_CID_MPEG_VIDEO_HEADER_MODE: return "Sequence Header Mode"; - case V4L2_CID_MPEG_VIDEO_MAX_REF_PIC: return "Max Number of Reference Pics"; - case V4L2_CID_MPEG_VIDEO_H263_I_FRAME_QP: return "H263 I-Frame QP Value"; - case V4L2_CID_MPEG_VIDEO_H263_P_FRAME_QP: return "H263 P-Frame QP Value"; - case V4L2_CID_MPEG_VIDEO_H263_B_FRAME_QP: return "H263 B-Frame QP Value"; - case V4L2_CID_MPEG_VIDEO_H263_MIN_QP: return "H263 Minimum QP Value"; - case V4L2_CID_MPEG_VIDEO_H263_MAX_QP: return "H263 Maximum QP Value"; - case V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP: return "H264 I-Frame QP Value"; - case V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP: return "H264 P-Frame QP Value"; - case V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP: return "H264 B-Frame QP Value"; - case V4L2_CID_MPEG_VIDEO_H264_MAX_QP: return "H264 Maximum QP Value"; - case V4L2_CID_MPEG_VIDEO_H264_MIN_QP: return "H264 Minimum QP Value"; - case V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM: return "H264 8x8 Transform Enable"; - case V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE: return "H264 CPB Buffer Size"; - case V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE: return "H264 Entropy Mode"; - case V4L2_CID_MPEG_VIDEO_H264_I_PERIOD: return "H264 I-Frame Period"; - case V4L2_CID_MPEG_VIDEO_H264_LEVEL: return "H264 Level"; - case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA: return "H264 Loop Filter Alpha Offset"; - case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA: return "H264 Loop Filter Beta Offset"; - case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE: return "H264 Loop Filter Mode"; - case V4L2_CID_MPEG_VIDEO_H264_PROFILE: return "H264 Profile"; - case V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_HEIGHT: return "Vertical Size of SAR"; - case V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_WIDTH: return "Horizontal Size of SAR"; - case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_ENABLE: return "Aspect Ratio VUI Enable"; - case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_IDC: return "VUI Aspect Ratio IDC"; - case V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP: return "MPEG4 I-Frame QP Value"; - case V4L2_CID_MPEG_VIDEO_MPEG4_P_FRAME_QP: return "MPEG4 P-Frame QP Value"; - case V4L2_CID_MPEG_VIDEO_MPEG4_B_FRAME_QP: return "MPEG4 B-Frame QP Value"; - case V4L2_CID_MPEG_VIDEO_MPEG4_MIN_QP: return "MPEG4 Minimum QP Value"; - case V4L2_CID_MPEG_VIDEO_MPEG4_MAX_QP: return "MPEG4 Maximum QP Value"; - case V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL: return "MPEG4 Level"; - case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE: return "MPEG4 Profile"; - case V4L2_CID_MPEG_VIDEO_MPEG4_QPEL: return "Quarter Pixel Search Enable"; - case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES: return "Maximum Bytes in a Slice"; - case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB: return "Number of MBs in a Slice"; - case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE: return "Slice Partitioning Method"; - case V4L2_CID_MPEG_VIDEO_VBV_SIZE: return "VBV Buffer Size"; - case V4L2_CID_MPEG_VIDEO_DEC_PTS: return "Video Decoder PTS"; - case V4L2_CID_MPEG_VIDEO_DEC_FRAME: return "Video Decoder Frame Count"; - - /* CAMERA controls */ - /* Keep the order of the 'case's the same as in videodev2.h! */ - case V4L2_CID_CAMERA_CLASS: return "Camera Controls"; - case V4L2_CID_EXPOSURE_AUTO: return "Auto Exposure"; - case V4L2_CID_EXPOSURE_ABSOLUTE: return "Exposure Time, Absolute"; - case V4L2_CID_EXPOSURE_AUTO_PRIORITY: return "Exposure, Dynamic Framerate"; - case V4L2_CID_PAN_RELATIVE: return "Pan, Relative"; - case V4L2_CID_TILT_RELATIVE: return "Tilt, Relative"; - case V4L2_CID_PAN_RESET: return "Pan, Reset"; - case V4L2_CID_TILT_RESET: return "Tilt, Reset"; - case V4L2_CID_PAN_ABSOLUTE: return "Pan, Absolute"; - case V4L2_CID_TILT_ABSOLUTE: return "Tilt, Absolute"; - case V4L2_CID_FOCUS_ABSOLUTE: return "Focus, Absolute"; - case V4L2_CID_FOCUS_RELATIVE: return "Focus, Relative"; - case V4L2_CID_FOCUS_AUTO: return "Focus, Automatic Continuous"; - case V4L2_CID_ZOOM_ABSOLUTE: return "Zoom, Absolute"; - case V4L2_CID_ZOOM_RELATIVE: return "Zoom, Relative"; - case V4L2_CID_ZOOM_CONTINUOUS: return "Zoom, Continuous"; - case V4L2_CID_PRIVACY: return "Privacy"; - case V4L2_CID_IRIS_ABSOLUTE: return "Iris, Absolute"; - case V4L2_CID_IRIS_RELATIVE: return "Iris, Relative"; - case V4L2_CID_AUTO_EXPOSURE_BIAS: return "Auto Exposure, Bias"; - case V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE: return "White Balance, Auto & Preset"; - case V4L2_CID_WIDE_DYNAMIC_RANGE: return "Wide Dynamic Range"; - case V4L2_CID_IMAGE_STABILIZATION: return "Image Stabilization"; - case V4L2_CID_ISO_SENSITIVITY: return "ISO Sensitivity"; - case V4L2_CID_ISO_SENSITIVITY_AUTO: return "ISO Sensitivity, Auto"; - case V4L2_CID_EXPOSURE_METERING: return "Exposure, Metering Mode"; - case V4L2_CID_SCENE_MODE: return "Scene Mode"; - case V4L2_CID_3A_LOCK: return "3A Lock"; - case V4L2_CID_AUTO_FOCUS_START: return "Auto Focus, Start"; - case V4L2_CID_AUTO_FOCUS_STOP: return "Auto Focus, Stop"; - case V4L2_CID_AUTO_FOCUS_STATUS: return "Auto Focus, Status"; - case V4L2_CID_AUTO_FOCUS_RANGE: return "Auto Focus, Range"; - - /* FM Radio Modulator control */ - /* Keep the order of the 'case's the same as in videodev2.h! */ - case V4L2_CID_FM_TX_CLASS: return "FM Radio Modulator Controls"; - case V4L2_CID_RDS_TX_DEVIATION: return "RDS Signal Deviation"; - case V4L2_CID_RDS_TX_PI: return "RDS Program ID"; - case V4L2_CID_RDS_TX_PTY: return "RDS Program Type"; - case V4L2_CID_RDS_TX_PS_NAME: return "RDS PS Name"; - case V4L2_CID_RDS_TX_RADIO_TEXT: return "RDS Radio Text"; - case V4L2_CID_AUDIO_LIMITER_ENABLED: return "Audio Limiter Feature Enabled"; - case V4L2_CID_AUDIO_LIMITER_RELEASE_TIME: return "Audio Limiter Release Time"; - case V4L2_CID_AUDIO_LIMITER_DEVIATION: return "Audio Limiter Deviation"; - case V4L2_CID_AUDIO_COMPRESSION_ENABLED: return "Audio Compression Enabled"; - case V4L2_CID_AUDIO_COMPRESSION_GAIN: return "Audio Compression Gain"; - case V4L2_CID_AUDIO_COMPRESSION_THRESHOLD: return "Audio Compression Threshold"; - case V4L2_CID_AUDIO_COMPRESSION_ATTACK_TIME: return "Audio Compression Attack Time"; - case V4L2_CID_AUDIO_COMPRESSION_RELEASE_TIME: return "Audio Compression Release Time"; - case V4L2_CID_PILOT_TONE_ENABLED: return "Pilot Tone Feature Enabled"; - case V4L2_CID_PILOT_TONE_DEVIATION: return "Pilot Tone Deviation"; - case V4L2_CID_PILOT_TONE_FREQUENCY: return "Pilot Tone Frequency"; - case V4L2_CID_TUNE_PREEMPHASIS: return "Pre-Emphasis"; - case V4L2_CID_TUNE_POWER_LEVEL: return "Tune Power Level"; - case V4L2_CID_TUNE_ANTENNA_CAPACITOR: return "Tune Antenna Capacitor"; - - /* Flash controls */ - case V4L2_CID_FLASH_CLASS: return "Flash Controls"; - case V4L2_CID_FLASH_LED_MODE: return "LED Mode"; - case V4L2_CID_FLASH_STROBE_SOURCE: return "Strobe Source"; - case V4L2_CID_FLASH_STROBE: return "Strobe"; - case V4L2_CID_FLASH_STROBE_STOP: return "Stop Strobe"; - case V4L2_CID_FLASH_STROBE_STATUS: return "Strobe Status"; - case V4L2_CID_FLASH_TIMEOUT: return "Strobe Timeout"; - case V4L2_CID_FLASH_INTENSITY: return "Intensity, Flash Mode"; - case V4L2_CID_FLASH_TORCH_INTENSITY: return "Intensity, Torch Mode"; - case V4L2_CID_FLASH_INDICATOR_INTENSITY: return "Intensity, Indicator"; - case V4L2_CID_FLASH_FAULT: return "Faults"; - case V4L2_CID_FLASH_CHARGE: return "Charge"; - case V4L2_CID_FLASH_READY: return "Ready to Strobe"; - - /* JPEG encoder controls */ - /* Keep the order of the 'case's the same as in videodev2.h! */ - case V4L2_CID_JPEG_CLASS: return "JPEG Compression Controls"; - case V4L2_CID_JPEG_CHROMA_SUBSAMPLING: return "Chroma Subsampling"; - case V4L2_CID_JPEG_RESTART_INTERVAL: return "Restart Interval"; - case V4L2_CID_JPEG_COMPRESSION_QUALITY: return "Compression Quality"; - case V4L2_CID_JPEG_ACTIVE_MARKER: return "Active Markers"; - - /* Image source controls */ - case V4L2_CID_IMAGE_SOURCE_CLASS: return "Image Source Controls"; - case V4L2_CID_VBLANK: return "Vertical Blanking"; - case V4L2_CID_HBLANK: return "Horizontal Blanking"; - case V4L2_CID_ANALOGUE_GAIN: return "Analogue Gain"; - - /* Image processing controls */ - case V4L2_CID_IMAGE_PROC_CLASS: return "Image Processing Controls"; - case V4L2_CID_LINK_FREQ: return "Link Frequency"; - case V4L2_CID_PIXEL_RATE: return "Pixel Rate"; - - default: - return NULL; - } -} -EXPORT_SYMBOL(v4l2_ctrl_get_name); - -void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, - s32 *min, s32 *max, s32 *step, s32 *def, u32 *flags) -{ - *name = v4l2_ctrl_get_name(id); - *flags = 0; - - switch (id) { - case V4L2_CID_AUDIO_MUTE: - case V4L2_CID_AUDIO_LOUDNESS: - case V4L2_CID_AUTO_WHITE_BALANCE: - case V4L2_CID_AUTOGAIN: - case V4L2_CID_HFLIP: - case V4L2_CID_VFLIP: - case V4L2_CID_HUE_AUTO: - case V4L2_CID_CHROMA_AGC: - case V4L2_CID_COLOR_KILLER: - case V4L2_CID_AUTOBRIGHTNESS: - case V4L2_CID_MPEG_AUDIO_MUTE: - case V4L2_CID_MPEG_VIDEO_MUTE: - case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE: - case V4L2_CID_MPEG_VIDEO_PULLDOWN: - case V4L2_CID_EXPOSURE_AUTO_PRIORITY: - case V4L2_CID_FOCUS_AUTO: - case V4L2_CID_PRIVACY: - case V4L2_CID_AUDIO_LIMITER_ENABLED: - case V4L2_CID_AUDIO_COMPRESSION_ENABLED: - case V4L2_CID_PILOT_TONE_ENABLED: - case V4L2_CID_ILLUMINATORS_1: - case V4L2_CID_ILLUMINATORS_2: - case V4L2_CID_FLASH_STROBE_STATUS: - case V4L2_CID_FLASH_CHARGE: - case V4L2_CID_FLASH_READY: - case V4L2_CID_MPEG_VIDEO_DECODER_MPEG4_DEBLOCK_FILTER: - case V4L2_CID_MPEG_VIDEO_DECODER_SLICE_INTERFACE: - case V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE: - case V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE: - case V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM: - case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_ENABLE: - case V4L2_CID_MPEG_VIDEO_MPEG4_QPEL: - case V4L2_CID_WIDE_DYNAMIC_RANGE: - case V4L2_CID_IMAGE_STABILIZATION: - *type = V4L2_CTRL_TYPE_BOOLEAN; - *min = 0; - *max = *step = 1; - break; - case V4L2_CID_PAN_RESET: - case V4L2_CID_TILT_RESET: - case V4L2_CID_FLASH_STROBE: - case V4L2_CID_FLASH_STROBE_STOP: - case V4L2_CID_AUTO_FOCUS_START: - case V4L2_CID_AUTO_FOCUS_STOP: - *type = V4L2_CTRL_TYPE_BUTTON; - *flags |= V4L2_CTRL_FLAG_WRITE_ONLY; - *min = *max = *step = *def = 0; - break; - case V4L2_CID_POWER_LINE_FREQUENCY: - case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: - case V4L2_CID_MPEG_AUDIO_ENCODING: - case V4L2_CID_MPEG_AUDIO_L1_BITRATE: - case V4L2_CID_MPEG_AUDIO_L2_BITRATE: - case V4L2_CID_MPEG_AUDIO_L3_BITRATE: - case V4L2_CID_MPEG_AUDIO_AC3_BITRATE: - case V4L2_CID_MPEG_AUDIO_MODE: - case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION: - case V4L2_CID_MPEG_AUDIO_EMPHASIS: - case V4L2_CID_MPEG_AUDIO_CRC: - case V4L2_CID_MPEG_AUDIO_DEC_PLAYBACK: - case V4L2_CID_MPEG_AUDIO_DEC_MULTILINGUAL_PLAYBACK: - case V4L2_CID_MPEG_VIDEO_ENCODING: - case V4L2_CID_MPEG_VIDEO_ASPECT: - case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: - case V4L2_CID_MPEG_STREAM_TYPE: - case V4L2_CID_MPEG_STREAM_VBI_FMT: - case V4L2_CID_EXPOSURE_AUTO: - case V4L2_CID_AUTO_FOCUS_RANGE: - case V4L2_CID_COLORFX: - case V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE: - case V4L2_CID_TUNE_PREEMPHASIS: - case V4L2_CID_FLASH_LED_MODE: - case V4L2_CID_FLASH_STROBE_SOURCE: - case V4L2_CID_MPEG_VIDEO_HEADER_MODE: - case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE: - case V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE: - case V4L2_CID_MPEG_VIDEO_H264_LEVEL: - case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE: - case V4L2_CID_MPEG_VIDEO_H264_PROFILE: - case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_IDC: - case V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL: - case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE: - case V4L2_CID_JPEG_CHROMA_SUBSAMPLING: - case V4L2_CID_ISO_SENSITIVITY_AUTO: - case V4L2_CID_EXPOSURE_METERING: - case V4L2_CID_SCENE_MODE: - *type = V4L2_CTRL_TYPE_MENU; - break; - case V4L2_CID_LINK_FREQ: - *type = V4L2_CTRL_TYPE_INTEGER_MENU; - break; - case V4L2_CID_RDS_TX_PS_NAME: - case V4L2_CID_RDS_TX_RADIO_TEXT: - *type = V4L2_CTRL_TYPE_STRING; - break; - case V4L2_CID_ISO_SENSITIVITY: - case V4L2_CID_AUTO_EXPOSURE_BIAS: - *type = V4L2_CTRL_TYPE_INTEGER_MENU; - break; - case V4L2_CID_USER_CLASS: - case V4L2_CID_CAMERA_CLASS: - case V4L2_CID_MPEG_CLASS: - case V4L2_CID_FM_TX_CLASS: - case V4L2_CID_FLASH_CLASS: - case V4L2_CID_JPEG_CLASS: - case V4L2_CID_IMAGE_SOURCE_CLASS: - case V4L2_CID_IMAGE_PROC_CLASS: - *type = V4L2_CTRL_TYPE_CTRL_CLASS; - /* You can neither read not write these */ - *flags |= V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_WRITE_ONLY; - *min = *max = *step = *def = 0; - break; - case V4L2_CID_BG_COLOR: - *type = V4L2_CTRL_TYPE_INTEGER; - *step = 1; - *min = 0; - /* Max is calculated as RGB888 that is 2^24 */ - *max = 0xFFFFFF; - break; - case V4L2_CID_FLASH_FAULT: - case V4L2_CID_JPEG_ACTIVE_MARKER: - case V4L2_CID_3A_LOCK: - case V4L2_CID_AUTO_FOCUS_STATUS: - *type = V4L2_CTRL_TYPE_BITMASK; - break; - case V4L2_CID_MIN_BUFFERS_FOR_CAPTURE: - case V4L2_CID_MIN_BUFFERS_FOR_OUTPUT: - *type = V4L2_CTRL_TYPE_INTEGER; - *flags |= V4L2_CTRL_FLAG_READ_ONLY; - break; - case V4L2_CID_MPEG_VIDEO_DEC_FRAME: - case V4L2_CID_MPEG_VIDEO_DEC_PTS: - *flags |= V4L2_CTRL_FLAG_VOLATILE; - /* Fall through */ - case V4L2_CID_PIXEL_RATE: - *type = V4L2_CTRL_TYPE_INTEGER64; - *flags |= V4L2_CTRL_FLAG_READ_ONLY; - *min = *max = *step = *def = 0; - break; - default: - *type = V4L2_CTRL_TYPE_INTEGER; - break; - } - switch (id) { - case V4L2_CID_MPEG_AUDIO_ENCODING: - case V4L2_CID_MPEG_AUDIO_MODE: - case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: - case V4L2_CID_MPEG_VIDEO_B_FRAMES: - case V4L2_CID_MPEG_STREAM_TYPE: - *flags |= V4L2_CTRL_FLAG_UPDATE; - break; - case V4L2_CID_AUDIO_VOLUME: - case V4L2_CID_AUDIO_BALANCE: - case V4L2_CID_AUDIO_BASS: - case V4L2_CID_AUDIO_TREBLE: - case V4L2_CID_BRIGHTNESS: - case V4L2_CID_CONTRAST: - case V4L2_CID_SATURATION: - case V4L2_CID_HUE: - case V4L2_CID_RED_BALANCE: - case V4L2_CID_BLUE_BALANCE: - case V4L2_CID_GAMMA: - case V4L2_CID_SHARPNESS: - case V4L2_CID_CHROMA_GAIN: - case V4L2_CID_RDS_TX_DEVIATION: - case V4L2_CID_AUDIO_LIMITER_RELEASE_TIME: - case V4L2_CID_AUDIO_LIMITER_DEVIATION: - case V4L2_CID_AUDIO_COMPRESSION_GAIN: - case V4L2_CID_AUDIO_COMPRESSION_THRESHOLD: - case V4L2_CID_AUDIO_COMPRESSION_ATTACK_TIME: - case V4L2_CID_AUDIO_COMPRESSION_RELEASE_TIME: - case V4L2_CID_PILOT_TONE_DEVIATION: - case V4L2_CID_PILOT_TONE_FREQUENCY: - case V4L2_CID_TUNE_POWER_LEVEL: - case V4L2_CID_TUNE_ANTENNA_CAPACITOR: - *flags |= V4L2_CTRL_FLAG_SLIDER; - break; - case V4L2_CID_PAN_RELATIVE: - case V4L2_CID_TILT_RELATIVE: - case V4L2_CID_FOCUS_RELATIVE: - case V4L2_CID_IRIS_RELATIVE: - case V4L2_CID_ZOOM_RELATIVE: - *flags |= V4L2_CTRL_FLAG_WRITE_ONLY; - break; - case V4L2_CID_FLASH_STROBE_STATUS: - case V4L2_CID_AUTO_FOCUS_STATUS: - case V4L2_CID_FLASH_READY: - *flags |= V4L2_CTRL_FLAG_READ_ONLY; - break; - } -} -EXPORT_SYMBOL(v4l2_ctrl_fill); - -/* Helper function to determine whether the control type is compatible with - VIDIOC_G/S_CTRL. */ -static bool type_is_int(const struct v4l2_ctrl *ctrl) -{ - switch (ctrl->type) { - case V4L2_CTRL_TYPE_INTEGER64: - case V4L2_CTRL_TYPE_STRING: - /* Nope, these need v4l2_ext_control */ - return false; - default: - return true; - } -} - -static void fill_event(struct v4l2_event *ev, struct v4l2_ctrl *ctrl, u32 changes) -{ - memset(ev->reserved, 0, sizeof(ev->reserved)); - ev->type = V4L2_EVENT_CTRL; - ev->id = ctrl->id; - ev->u.ctrl.changes = changes; - ev->u.ctrl.type = ctrl->type; - ev->u.ctrl.flags = ctrl->flags; - if (ctrl->type == V4L2_CTRL_TYPE_STRING) - ev->u.ctrl.value64 = 0; - else - ev->u.ctrl.value64 = ctrl->cur.val64; - ev->u.ctrl.minimum = ctrl->minimum; - ev->u.ctrl.maximum = ctrl->maximum; - if (ctrl->type == V4L2_CTRL_TYPE_MENU - || ctrl->type == V4L2_CTRL_TYPE_INTEGER_MENU) - ev->u.ctrl.step = 1; - else - ev->u.ctrl.step = ctrl->step; - ev->u.ctrl.default_value = ctrl->default_value; -} - -static void send_event(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, u32 changes) -{ - struct v4l2_event ev; - struct v4l2_subscribed_event *sev; - - if (list_empty(&ctrl->ev_subs)) - return; - fill_event(&ev, ctrl, changes); - - list_for_each_entry(sev, &ctrl->ev_subs, node) - if (sev->fh != fh || - (sev->flags & V4L2_EVENT_SUB_FL_ALLOW_FEEDBACK)) - v4l2_event_queue_fh(sev->fh, &ev); -} - -/* Helper function: copy the current control value back to the caller */ -static int cur_to_user(struct v4l2_ext_control *c, - struct v4l2_ctrl *ctrl) -{ - u32 len; - - switch (ctrl->type) { - case V4L2_CTRL_TYPE_STRING: - len = strlen(ctrl->cur.string); - if (c->size < len + 1) { - c->size = len + 1; - return -ENOSPC; - } - return copy_to_user(c->string, ctrl->cur.string, - len + 1) ? -EFAULT : 0; - case V4L2_CTRL_TYPE_INTEGER64: - c->value64 = ctrl->cur.val64; - break; - default: - c->value = ctrl->cur.val; - break; - } - return 0; -} - -/* Helper function: copy the caller-provider value as the new control value */ -static int user_to_new(struct v4l2_ext_control *c, - struct v4l2_ctrl *ctrl) -{ - int ret; - u32 size; - - ctrl->is_new = 1; - switch (ctrl->type) { - case V4L2_CTRL_TYPE_INTEGER64: - ctrl->val64 = c->value64; - break; - case V4L2_CTRL_TYPE_STRING: - size = c->size; - if (size == 0) - return -ERANGE; - if (size > ctrl->maximum + 1) - size = ctrl->maximum + 1; - ret = copy_from_user(ctrl->string, c->string, size); - if (!ret) { - char last = ctrl->string[size - 1]; - - ctrl->string[size - 1] = 0; - /* If the string was longer than ctrl->maximum, - then return an error. */ - if (strlen(ctrl->string) == ctrl->maximum && last) - return -ERANGE; - } - return ret ? -EFAULT : 0; - default: - ctrl->val = c->value; - break; - } - return 0; -} - -/* Helper function: copy the new control value back to the caller */ -static int new_to_user(struct v4l2_ext_control *c, - struct v4l2_ctrl *ctrl) -{ - u32 len; - - switch (ctrl->type) { - case V4L2_CTRL_TYPE_STRING: - len = strlen(ctrl->string); - if (c->size < len + 1) { - c->size = ctrl->maximum + 1; - return -ENOSPC; - } - return copy_to_user(c->string, ctrl->string, - len + 1) ? -EFAULT : 0; - case V4L2_CTRL_TYPE_INTEGER64: - c->value64 = ctrl->val64; - break; - default: - c->value = ctrl->val; - break; - } - return 0; -} - -/* Copy the new value to the current value. */ -static void new_to_cur(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, - bool update_inactive) -{ - bool changed = false; - - if (ctrl == NULL) - return; - switch (ctrl->type) { - case V4L2_CTRL_TYPE_BUTTON: - changed = true; - break; - case V4L2_CTRL_TYPE_STRING: - /* strings are always 0-terminated */ - changed = strcmp(ctrl->string, ctrl->cur.string); - strcpy(ctrl->cur.string, ctrl->string); - break; - case V4L2_CTRL_TYPE_INTEGER64: - changed = ctrl->val64 != ctrl->cur.val64; - ctrl->cur.val64 = ctrl->val64; - break; - default: - changed = ctrl->val != ctrl->cur.val; - ctrl->cur.val = ctrl->val; - break; - } - if (update_inactive) { - /* Note: update_inactive can only be true for auto clusters. */ - ctrl->flags &= - ~(V4L2_CTRL_FLAG_INACTIVE | V4L2_CTRL_FLAG_VOLATILE); - if (!is_cur_manual(ctrl->cluster[0])) { - ctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; - if (ctrl->cluster[0]->has_volatiles) - ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE; - } - fh = NULL; - } - if (changed || update_inactive) { - /* If a control was changed that was not one of the controls - modified by the application, then send the event to all. */ - if (!ctrl->is_new) - fh = NULL; - send_event(fh, ctrl, - (changed ? V4L2_EVENT_CTRL_CH_VALUE : 0) | - (update_inactive ? V4L2_EVENT_CTRL_CH_FLAGS : 0)); - } -} - -/* Copy the current value to the new value */ -static void cur_to_new(struct v4l2_ctrl *ctrl) -{ - if (ctrl == NULL) - return; - switch (ctrl->type) { - case V4L2_CTRL_TYPE_STRING: - /* strings are always 0-terminated */ - strcpy(ctrl->string, ctrl->cur.string); - break; - case V4L2_CTRL_TYPE_INTEGER64: - ctrl->val64 = ctrl->cur.val64; - break; - default: - ctrl->val = ctrl->cur.val; - break; - } -} - -/* Return non-zero if one or more of the controls in the cluster has a new - value that differs from the current value. */ -static int cluster_changed(struct v4l2_ctrl *master) -{ - int diff = 0; - int i; - - for (i = 0; !diff && i < master->ncontrols; i++) { - struct v4l2_ctrl *ctrl = master->cluster[i]; - - if (ctrl == NULL) - continue; - switch (ctrl->type) { - case V4L2_CTRL_TYPE_BUTTON: - /* Button controls are always 'different' */ - return 1; - case V4L2_CTRL_TYPE_STRING: - /* strings are always 0-terminated */ - diff = strcmp(ctrl->string, ctrl->cur.string); - break; - case V4L2_CTRL_TYPE_INTEGER64: - diff = ctrl->val64 != ctrl->cur.val64; - break; - default: - diff = ctrl->val != ctrl->cur.val; - break; - } - } - return diff; -} - -/* Validate integer-type control */ -static int validate_new_int(const struct v4l2_ctrl *ctrl, s32 *pval) -{ - s32 val = *pval; - u32 offset; - - switch (ctrl->type) { - case V4L2_CTRL_TYPE_INTEGER: - /* Round towards the closest legal value */ - val += ctrl->step / 2; - if (val < ctrl->minimum) - val = ctrl->minimum; - if (val > ctrl->maximum) - val = ctrl->maximum; - offset = val - ctrl->minimum; - offset = ctrl->step * (offset / ctrl->step); - val = ctrl->minimum + offset; - *pval = val; - return 0; - - case V4L2_CTRL_TYPE_BOOLEAN: - *pval = !!val; - return 0; - - case V4L2_CTRL_TYPE_MENU: - case V4L2_CTRL_TYPE_INTEGER_MENU: - if (val < ctrl->minimum || val > ctrl->maximum) - return -ERANGE; - if (ctrl->menu_skip_mask & (1 << val)) - return -EINVAL; - if (ctrl->type == V4L2_CTRL_TYPE_MENU && - ctrl->qmenu[val][0] == '\0') - return -EINVAL; - return 0; - - case V4L2_CTRL_TYPE_BITMASK: - *pval &= ctrl->maximum; - return 0; - - case V4L2_CTRL_TYPE_BUTTON: - case V4L2_CTRL_TYPE_CTRL_CLASS: - *pval = 0; - return 0; - - default: - return -EINVAL; - } -} - -/* Validate a new control */ -static int validate_new(const struct v4l2_ctrl *ctrl, struct v4l2_ext_control *c) -{ - char *s = c->string; - size_t len; - - switch (ctrl->type) { - case V4L2_CTRL_TYPE_INTEGER: - case V4L2_CTRL_TYPE_BOOLEAN: - case V4L2_CTRL_TYPE_MENU: - case V4L2_CTRL_TYPE_INTEGER_MENU: - case V4L2_CTRL_TYPE_BITMASK: - case V4L2_CTRL_TYPE_BUTTON: - case V4L2_CTRL_TYPE_CTRL_CLASS: - return validate_new_int(ctrl, &c->value); - - case V4L2_CTRL_TYPE_INTEGER64: - return 0; - - case V4L2_CTRL_TYPE_STRING: - len = strlen(s); - if (len < ctrl->minimum) - return -ERANGE; - if ((len - ctrl->minimum) % ctrl->step) - return -ERANGE; - return 0; - - default: - return -EINVAL; - } -} - -static inline u32 node2id(struct list_head *node) -{ - return list_entry(node, struct v4l2_ctrl_ref, node)->ctrl->id; -} - -/* Set the handler's error code if it wasn't set earlier already */ -static inline int handler_set_err(struct v4l2_ctrl_handler *hdl, int err) -{ - if (hdl->error == 0) - hdl->error = err; - return err; -} - -/* Initialize the handler */ -int v4l2_ctrl_handler_init(struct v4l2_ctrl_handler *hdl, - unsigned nr_of_controls_hint) -{ - hdl->lock = &hdl->_lock; - mutex_init(hdl->lock); - INIT_LIST_HEAD(&hdl->ctrls); - INIT_LIST_HEAD(&hdl->ctrl_refs); - hdl->nr_of_buckets = 1 + nr_of_controls_hint / 8; - hdl->buckets = kcalloc(hdl->nr_of_buckets, sizeof(hdl->buckets[0]), - GFP_KERNEL); - hdl->error = hdl->buckets ? 0 : -ENOMEM; - return hdl->error; -} -EXPORT_SYMBOL(v4l2_ctrl_handler_init); - -/* Free all controls and control refs */ -void v4l2_ctrl_handler_free(struct v4l2_ctrl_handler *hdl) -{ - struct v4l2_ctrl_ref *ref, *next_ref; - struct v4l2_ctrl *ctrl, *next_ctrl; - struct v4l2_subscribed_event *sev, *next_sev; - - if (hdl == NULL || hdl->buckets == NULL) - return; - - mutex_lock(hdl->lock); - /* Free all nodes */ - list_for_each_entry_safe(ref, next_ref, &hdl->ctrl_refs, node) { - list_del(&ref->node); - kfree(ref); - } - /* Free all controls owned by the handler */ - list_for_each_entry_safe(ctrl, next_ctrl, &hdl->ctrls, node) { - list_del(&ctrl->node); - list_for_each_entry_safe(sev, next_sev, &ctrl->ev_subs, node) - list_del(&sev->node); - kfree(ctrl); - } - kfree(hdl->buckets); - hdl->buckets = NULL; - hdl->cached = NULL; - hdl->error = 0; - mutex_unlock(hdl->lock); -} -EXPORT_SYMBOL(v4l2_ctrl_handler_free); - -/* For backwards compatibility: V4L2_CID_PRIVATE_BASE should no longer - be used except in G_CTRL, S_CTRL, QUERYCTRL and QUERYMENU when dealing - with applications that do not use the NEXT_CTRL flag. - - We just find the n-th private user control. It's O(N), but that should not - be an issue in this particular case. */ -static struct v4l2_ctrl_ref *find_private_ref( - struct v4l2_ctrl_handler *hdl, u32 id) -{ - struct v4l2_ctrl_ref *ref; - - id -= V4L2_CID_PRIVATE_BASE; - list_for_each_entry(ref, &hdl->ctrl_refs, node) { - /* Search for private user controls that are compatible with - VIDIOC_G/S_CTRL. */ - if (V4L2_CTRL_ID2CLASS(ref->ctrl->id) == V4L2_CTRL_CLASS_USER && - V4L2_CTRL_DRIVER_PRIV(ref->ctrl->id)) { - if (!type_is_int(ref->ctrl)) - continue; - if (id == 0) - return ref; - id--; - } - } - return NULL; -} - -/* Find a control with the given ID. */ -static struct v4l2_ctrl_ref *find_ref(struct v4l2_ctrl_handler *hdl, u32 id) -{ - struct v4l2_ctrl_ref *ref; - int bucket; - - id &= V4L2_CTRL_ID_MASK; - - /* Old-style private controls need special handling */ - if (id >= V4L2_CID_PRIVATE_BASE) - return find_private_ref(hdl, id); - bucket = id % hdl->nr_of_buckets; - - /* Simple optimization: cache the last control found */ - if (hdl->cached && hdl->cached->ctrl->id == id) - return hdl->cached; - - /* Not in cache, search the hash */ - ref = hdl->buckets ? hdl->buckets[bucket] : NULL; - while (ref && ref->ctrl->id != id) - ref = ref->next; - - if (ref) - hdl->cached = ref; /* cache it! */ - return ref; -} - -/* Find a control with the given ID. Take the handler's lock first. */ -static struct v4l2_ctrl_ref *find_ref_lock( - struct v4l2_ctrl_handler *hdl, u32 id) -{ - struct v4l2_ctrl_ref *ref = NULL; - - if (hdl) { - mutex_lock(hdl->lock); - ref = find_ref(hdl, id); - mutex_unlock(hdl->lock); - } - return ref; -} - -/* Find a control with the given ID. */ -struct v4l2_ctrl *v4l2_ctrl_find(struct v4l2_ctrl_handler *hdl, u32 id) -{ - struct v4l2_ctrl_ref *ref = find_ref_lock(hdl, id); - - return ref ? ref->ctrl : NULL; -} -EXPORT_SYMBOL(v4l2_ctrl_find); - -/* Allocate a new v4l2_ctrl_ref and hook it into the handler. */ -static int handler_new_ref(struct v4l2_ctrl_handler *hdl, - struct v4l2_ctrl *ctrl) -{ - struct v4l2_ctrl_ref *ref; - struct v4l2_ctrl_ref *new_ref; - u32 id = ctrl->id; - u32 class_ctrl = V4L2_CTRL_ID2CLASS(id) | 1; - int bucket = id % hdl->nr_of_buckets; /* which bucket to use */ - - /* Automatically add the control class if it is not yet present. */ - if (id != class_ctrl && find_ref_lock(hdl, class_ctrl) == NULL) - if (!v4l2_ctrl_new_std(hdl, NULL, class_ctrl, 0, 0, 0, 0)) - return hdl->error; - - if (hdl->error) - return hdl->error; - - new_ref = kzalloc(sizeof(*new_ref), GFP_KERNEL); - if (!new_ref) - return handler_set_err(hdl, -ENOMEM); - new_ref->ctrl = ctrl; - if (ctrl->handler == hdl) { - /* By default each control starts in a cluster of its own. - new_ref->ctrl is basically a cluster array with one - element, so that's perfect to use as the cluster pointer. - But only do this for the handler that owns the control. */ - ctrl->cluster = &new_ref->ctrl; - ctrl->ncontrols = 1; - } - - INIT_LIST_HEAD(&new_ref->node); - - mutex_lock(hdl->lock); - - /* Add immediately at the end of the list if the list is empty, or if - the last element in the list has a lower ID. - This ensures that when elements are added in ascending order the - insertion is an O(1) operation. */ - if (list_empty(&hdl->ctrl_refs) || id > node2id(hdl->ctrl_refs.prev)) { - list_add_tail(&new_ref->node, &hdl->ctrl_refs); - goto insert_in_hash; - } - - /* Find insert position in sorted list */ - list_for_each_entry(ref, &hdl->ctrl_refs, node) { - if (ref->ctrl->id < id) - continue; - /* Don't add duplicates */ - if (ref->ctrl->id == id) { - kfree(new_ref); - goto unlock; - } - list_add(&new_ref->node, ref->node.prev); - break; - } - -insert_in_hash: - /* Insert the control node in the hash */ - new_ref->next = hdl->buckets[bucket]; - hdl->buckets[bucket] = new_ref; - -unlock: - mutex_unlock(hdl->lock); - return 0; -} - -/* Add a new control */ -static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, - const struct v4l2_ctrl_ops *ops, - u32 id, const char *name, enum v4l2_ctrl_type type, - s32 min, s32 max, u32 step, s32 def, - u32 flags, const char * const *qmenu, - const s64 *qmenu_int, void *priv) -{ - struct v4l2_ctrl *ctrl; - unsigned sz_extra = 0; - - if (hdl->error) - return NULL; - - /* Sanity checks */ - if (id == 0 || name == NULL || id >= V4L2_CID_PRIVATE_BASE || - (type == V4L2_CTRL_TYPE_INTEGER && step == 0) || - (type == V4L2_CTRL_TYPE_BITMASK && max == 0) || - (type == V4L2_CTRL_TYPE_MENU && qmenu == NULL) || - (type == V4L2_CTRL_TYPE_INTEGER_MENU && qmenu_int == NULL) || - (type == V4L2_CTRL_TYPE_STRING && max == 0)) { - handler_set_err(hdl, -ERANGE); - return NULL; - } - if (type != V4L2_CTRL_TYPE_BITMASK && max < min) { - handler_set_err(hdl, -ERANGE); - return NULL; - } - if ((type == V4L2_CTRL_TYPE_INTEGER || - type == V4L2_CTRL_TYPE_MENU || - type == V4L2_CTRL_TYPE_INTEGER_MENU || - type == V4L2_CTRL_TYPE_BOOLEAN) && - (def < min || def > max)) { - handler_set_err(hdl, -ERANGE); - return NULL; - } - if (type == V4L2_CTRL_TYPE_BITMASK && ((def & ~max) || min || step)) { - handler_set_err(hdl, -ERANGE); - return NULL; - } - - if (type == V4L2_CTRL_TYPE_BUTTON) - flags |= V4L2_CTRL_FLAG_WRITE_ONLY; - else if (type == V4L2_CTRL_TYPE_CTRL_CLASS) - flags |= V4L2_CTRL_FLAG_READ_ONLY; - else if (type == V4L2_CTRL_TYPE_STRING) - sz_extra += 2 * (max + 1); - - ctrl = kzalloc(sizeof(*ctrl) + sz_extra, GFP_KERNEL); - if (ctrl == NULL) { - handler_set_err(hdl, -ENOMEM); - return NULL; - } - - INIT_LIST_HEAD(&ctrl->node); - INIT_LIST_HEAD(&ctrl->ev_subs); - ctrl->handler = hdl; - ctrl->ops = ops; - ctrl->id = id; - ctrl->name = name; - ctrl->type = type; - ctrl->flags = flags; - ctrl->minimum = min; - ctrl->maximum = max; - ctrl->step = step; - if (type == V4L2_CTRL_TYPE_MENU) - ctrl->qmenu = qmenu; - else if (type == V4L2_CTRL_TYPE_INTEGER_MENU) - ctrl->qmenu_int = qmenu_int; - ctrl->priv = priv; - ctrl->cur.val = ctrl->val = ctrl->default_value = def; - - if (ctrl->type == V4L2_CTRL_TYPE_STRING) { - ctrl->cur.string = (char *)&ctrl[1] + sz_extra - (max + 1); - ctrl->string = (char *)&ctrl[1] + sz_extra - 2 * (max + 1); - if (ctrl->minimum) - memset(ctrl->cur.string, ' ', ctrl->minimum); - } - if (handler_new_ref(hdl, ctrl)) { - kfree(ctrl); - return NULL; - } - mutex_lock(hdl->lock); - list_add_tail(&ctrl->node, &hdl->ctrls); - mutex_unlock(hdl->lock); - return ctrl; -} - -struct v4l2_ctrl *v4l2_ctrl_new_custom(struct v4l2_ctrl_handler *hdl, - const struct v4l2_ctrl_config *cfg, void *priv) -{ - bool is_menu; - struct v4l2_ctrl *ctrl; - const char *name = cfg->name; - const char * const *qmenu = cfg->qmenu; - const s64 *qmenu_int = cfg->qmenu_int; - enum v4l2_ctrl_type type = cfg->type; - u32 flags = cfg->flags; - s32 min = cfg->min; - s32 max = cfg->max; - u32 step = cfg->step; - s32 def = cfg->def; - - if (name == NULL) - v4l2_ctrl_fill(cfg->id, &name, &type, &min, &max, &step, - &def, &flags); - - is_menu = (cfg->type == V4L2_CTRL_TYPE_MENU || - cfg->type == V4L2_CTRL_TYPE_INTEGER_MENU); - if (is_menu) - WARN_ON(step); - else - WARN_ON(cfg->menu_skip_mask); - if (cfg->type == V4L2_CTRL_TYPE_MENU && qmenu == NULL) - qmenu = v4l2_ctrl_get_menu(cfg->id); - else if (cfg->type == V4L2_CTRL_TYPE_INTEGER_MENU && - qmenu_int == NULL) { - handler_set_err(hdl, -EINVAL); - return NULL; - } - - ctrl = v4l2_ctrl_new(hdl, cfg->ops, cfg->id, name, - type, min, max, - is_menu ? cfg->menu_skip_mask : step, - def, flags, qmenu, qmenu_int, priv); - if (ctrl) - ctrl->is_private = cfg->is_private; - return ctrl; -} -EXPORT_SYMBOL(v4l2_ctrl_new_custom); - -/* Helper function for standard non-menu controls */ -struct v4l2_ctrl *v4l2_ctrl_new_std(struct v4l2_ctrl_handler *hdl, - const struct v4l2_ctrl_ops *ops, - u32 id, s32 min, s32 max, u32 step, s32 def) -{ - const char *name; - enum v4l2_ctrl_type type; - u32 flags; - - v4l2_ctrl_fill(id, &name, &type, &min, &max, &step, &def, &flags); - if (type == V4L2_CTRL_TYPE_MENU - || type == V4L2_CTRL_TYPE_INTEGER_MENU) { - handler_set_err(hdl, -EINVAL); - return NULL; - } - return v4l2_ctrl_new(hdl, ops, id, name, type, - min, max, step, def, flags, NULL, NULL, NULL); -} -EXPORT_SYMBOL(v4l2_ctrl_new_std); - -/* Helper function for standard menu controls */ -struct v4l2_ctrl *v4l2_ctrl_new_std_menu(struct v4l2_ctrl_handler *hdl, - const struct v4l2_ctrl_ops *ops, - u32 id, s32 max, s32 mask, s32 def) -{ - const char * const *qmenu = v4l2_ctrl_get_menu(id); - const char *name; - enum v4l2_ctrl_type type; - s32 min; - s32 step; - u32 flags; - - v4l2_ctrl_fill(id, &name, &type, &min, &max, &step, &def, &flags); - if (type != V4L2_CTRL_TYPE_MENU) { - handler_set_err(hdl, -EINVAL); - return NULL; - } - return v4l2_ctrl_new(hdl, ops, id, name, type, - 0, max, mask, def, flags, qmenu, NULL, NULL); -} -EXPORT_SYMBOL(v4l2_ctrl_new_std_menu); - -/* Helper function for standard integer menu controls */ -struct v4l2_ctrl *v4l2_ctrl_new_int_menu(struct v4l2_ctrl_handler *hdl, - const struct v4l2_ctrl_ops *ops, - u32 id, s32 max, s32 def, const s64 *qmenu_int) -{ - const char *name; - enum v4l2_ctrl_type type; - s32 min; - s32 step; - u32 flags; - - v4l2_ctrl_fill(id, &name, &type, &min, &max, &step, &def, &flags); - if (type != V4L2_CTRL_TYPE_INTEGER_MENU) { - handler_set_err(hdl, -EINVAL); - return NULL; - } - return v4l2_ctrl_new(hdl, ops, id, name, type, - 0, max, 0, def, flags, NULL, qmenu_int, NULL); -} -EXPORT_SYMBOL(v4l2_ctrl_new_int_menu); - -/* Add a control from another handler to this handler */ -struct v4l2_ctrl *v4l2_ctrl_add_ctrl(struct v4l2_ctrl_handler *hdl, - struct v4l2_ctrl *ctrl) -{ - if (hdl == NULL || hdl->error) - return NULL; - if (ctrl == NULL) { - handler_set_err(hdl, -EINVAL); - return NULL; - } - if (ctrl->handler == hdl) - return ctrl; - return handler_new_ref(hdl, ctrl) ? NULL : ctrl; -} -EXPORT_SYMBOL(v4l2_ctrl_add_ctrl); - -/* Add the controls from another handler to our own. */ -int v4l2_ctrl_add_handler(struct v4l2_ctrl_handler *hdl, - struct v4l2_ctrl_handler *add) -{ - struct v4l2_ctrl_ref *ref; - int ret = 0; - - /* Do nothing if either handler is NULL or if they are the same */ - if (!hdl || !add || hdl == add) - return 0; - if (hdl->error) - return hdl->error; - mutex_lock(add->lock); - list_for_each_entry(ref, &add->ctrl_refs, node) { - struct v4l2_ctrl *ctrl = ref->ctrl; - - /* Skip handler-private controls. */ - if (ctrl->is_private) - continue; - /* And control classes */ - if (ctrl->type == V4L2_CTRL_TYPE_CTRL_CLASS) - continue; - ret = handler_new_ref(hdl, ctrl); - if (ret) - break; - } - mutex_unlock(add->lock); - return ret; -} -EXPORT_SYMBOL(v4l2_ctrl_add_handler); - -/* Cluster controls */ -void v4l2_ctrl_cluster(unsigned ncontrols, struct v4l2_ctrl **controls) -{ - bool has_volatiles = false; - int i; - - /* The first control is the master control and it must not be NULL */ - BUG_ON(ncontrols == 0 || controls[0] == NULL); - - for (i = 0; i < ncontrols; i++) { - if (controls[i]) { - controls[i]->cluster = controls; - controls[i]->ncontrols = ncontrols; - if (controls[i]->flags & V4L2_CTRL_FLAG_VOLATILE) - has_volatiles = true; - } - } - controls[0]->has_volatiles = has_volatiles; -} -EXPORT_SYMBOL(v4l2_ctrl_cluster); - -void v4l2_ctrl_auto_cluster(unsigned ncontrols, struct v4l2_ctrl **controls, - u8 manual_val, bool set_volatile) -{ - struct v4l2_ctrl *master = controls[0]; - u32 flag = 0; - int i; - - v4l2_ctrl_cluster(ncontrols, controls); - WARN_ON(ncontrols <= 1); - WARN_ON(manual_val < master->minimum || manual_val > master->maximum); - WARN_ON(set_volatile && !has_op(master, g_volatile_ctrl)); - master->is_auto = true; - master->has_volatiles = set_volatile; - master->manual_mode_value = manual_val; - master->flags |= V4L2_CTRL_FLAG_UPDATE; - - if (!is_cur_manual(master)) - flag = V4L2_CTRL_FLAG_INACTIVE | - (set_volatile ? V4L2_CTRL_FLAG_VOLATILE : 0); - - for (i = 1; i < ncontrols; i++) - if (controls[i]) - controls[i]->flags |= flag; -} -EXPORT_SYMBOL(v4l2_ctrl_auto_cluster); - -/* Activate/deactivate a control. */ -void v4l2_ctrl_activate(struct v4l2_ctrl *ctrl, bool active) -{ - /* invert since the actual flag is called 'inactive' */ - bool inactive = !active; - bool old; - - if (ctrl == NULL) - return; - - if (inactive) - /* set V4L2_CTRL_FLAG_INACTIVE */ - old = test_and_set_bit(4, &ctrl->flags); - else - /* clear V4L2_CTRL_FLAG_INACTIVE */ - old = test_and_clear_bit(4, &ctrl->flags); - if (old != inactive) - send_event(NULL, ctrl, V4L2_EVENT_CTRL_CH_FLAGS); -} -EXPORT_SYMBOL(v4l2_ctrl_activate); - -/* Grab/ungrab a control. - Typically used when streaming starts and you want to grab controls, - preventing the user from changing them. - - Just call this and the framework will block any attempts to change - these controls. */ -void v4l2_ctrl_grab(struct v4l2_ctrl *ctrl, bool grabbed) -{ - bool old; - - if (ctrl == NULL) - return; - - v4l2_ctrl_lock(ctrl); - if (grabbed) - /* set V4L2_CTRL_FLAG_GRABBED */ - old = test_and_set_bit(1, &ctrl->flags); - else - /* clear V4L2_CTRL_FLAG_GRABBED */ - old = test_and_clear_bit(1, &ctrl->flags); - if (old != grabbed) - send_event(NULL, ctrl, V4L2_EVENT_CTRL_CH_FLAGS); - v4l2_ctrl_unlock(ctrl); -} -EXPORT_SYMBOL(v4l2_ctrl_grab); - -/* Log the control name and value */ -static void log_ctrl(const struct v4l2_ctrl *ctrl, - const char *prefix, const char *colon) -{ - if (ctrl->flags & (V4L2_CTRL_FLAG_DISABLED | V4L2_CTRL_FLAG_WRITE_ONLY)) - return; - if (ctrl->type == V4L2_CTRL_TYPE_CTRL_CLASS) - return; - - printk(KERN_INFO "%s%s%s: ", prefix, colon, ctrl->name); - - switch (ctrl->type) { - case V4L2_CTRL_TYPE_INTEGER: - printk(KERN_CONT "%d", ctrl->cur.val); - break; - case V4L2_CTRL_TYPE_BOOLEAN: - printk(KERN_CONT "%s", ctrl->cur.val ? "true" : "false"); - break; - case V4L2_CTRL_TYPE_MENU: - printk(KERN_CONT "%s", ctrl->qmenu[ctrl->cur.val]); - break; - case V4L2_CTRL_TYPE_INTEGER_MENU: - printk(KERN_CONT "%lld", ctrl->qmenu_int[ctrl->cur.val]); - break; - case V4L2_CTRL_TYPE_BITMASK: - printk(KERN_CONT "0x%08x", ctrl->cur.val); - break; - case V4L2_CTRL_TYPE_INTEGER64: - printk(KERN_CONT "%lld", ctrl->cur.val64); - break; - case V4L2_CTRL_TYPE_STRING: - printk(KERN_CONT "%s", ctrl->cur.string); - break; - default: - printk(KERN_CONT "unknown type %d", ctrl->type); - break; - } - if (ctrl->flags & (V4L2_CTRL_FLAG_INACTIVE | - V4L2_CTRL_FLAG_GRABBED | - V4L2_CTRL_FLAG_VOLATILE)) { - if (ctrl->flags & V4L2_CTRL_FLAG_INACTIVE) - printk(KERN_CONT " inactive"); - if (ctrl->flags & V4L2_CTRL_FLAG_GRABBED) - printk(KERN_CONT " grabbed"); - if (ctrl->flags & V4L2_CTRL_FLAG_VOLATILE) - printk(KERN_CONT " volatile"); - } - printk(KERN_CONT "\n"); -} - -/* Log all controls owned by the handler */ -void v4l2_ctrl_handler_log_status(struct v4l2_ctrl_handler *hdl, - const char *prefix) -{ - struct v4l2_ctrl *ctrl; - const char *colon = ""; - int len; - - if (hdl == NULL) - return; - if (prefix == NULL) - prefix = ""; - len = strlen(prefix); - if (len && prefix[len - 1] != ' ') - colon = ": "; - mutex_lock(hdl->lock); - list_for_each_entry(ctrl, &hdl->ctrls, node) - if (!(ctrl->flags & V4L2_CTRL_FLAG_DISABLED)) - log_ctrl(ctrl, prefix, colon); - mutex_unlock(hdl->lock); -} -EXPORT_SYMBOL(v4l2_ctrl_handler_log_status); - -/* Call s_ctrl for all controls owned by the handler */ -int v4l2_ctrl_handler_setup(struct v4l2_ctrl_handler *hdl) -{ - struct v4l2_ctrl *ctrl; - int ret = 0; - - if (hdl == NULL) - return 0; - mutex_lock(hdl->lock); - list_for_each_entry(ctrl, &hdl->ctrls, node) - ctrl->done = false; - - list_for_each_entry(ctrl, &hdl->ctrls, node) { - struct v4l2_ctrl *master = ctrl->cluster[0]; - int i; - - /* Skip if this control was already handled by a cluster. */ - /* Skip button controls and read-only controls. */ - if (ctrl->done || ctrl->type == V4L2_CTRL_TYPE_BUTTON || - (ctrl->flags & V4L2_CTRL_FLAG_READ_ONLY)) - continue; - - for (i = 0; i < master->ncontrols; i++) { - if (master->cluster[i]) { - cur_to_new(master->cluster[i]); - master->cluster[i]->is_new = 1; - master->cluster[i]->done = true; - } - } - ret = call_op(master, s_ctrl); - if (ret) - break; - } - mutex_unlock(hdl->lock); - return ret; -} -EXPORT_SYMBOL(v4l2_ctrl_handler_setup); - -/* Implement VIDIOC_QUERYCTRL */ -int v4l2_queryctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_queryctrl *qc) -{ - u32 id = qc->id & V4L2_CTRL_ID_MASK; - struct v4l2_ctrl_ref *ref; - struct v4l2_ctrl *ctrl; - - if (hdl == NULL) - return -EINVAL; - - mutex_lock(hdl->lock); - - /* Try to find it */ - ref = find_ref(hdl, id); - - if ((qc->id & V4L2_CTRL_FLAG_NEXT_CTRL) && !list_empty(&hdl->ctrl_refs)) { - /* Find the next control with ID > qc->id */ - - /* Did we reach the end of the control list? */ - if (id >= node2id(hdl->ctrl_refs.prev)) { - ref = NULL; /* Yes, so there is no next control */ - } else if (ref) { - /* We found a control with the given ID, so just get - the next one in the list. */ - ref = list_entry(ref->node.next, typeof(*ref), node); - } else { - /* No control with the given ID exists, so start - searching for the next largest ID. We know there - is one, otherwise the first 'if' above would have - been true. */ - list_for_each_entry(ref, &hdl->ctrl_refs, node) - if (id < ref->ctrl->id) - break; - } - } - mutex_unlock(hdl->lock); - if (!ref) - return -EINVAL; - - ctrl = ref->ctrl; - memset(qc, 0, sizeof(*qc)); - if (id >= V4L2_CID_PRIVATE_BASE) - qc->id = id; - else - qc->id = ctrl->id; - strlcpy(qc->name, ctrl->name, sizeof(qc->name)); - qc->minimum = ctrl->minimum; - qc->maximum = ctrl->maximum; - qc->default_value = ctrl->default_value; - if (ctrl->type == V4L2_CTRL_TYPE_MENU - || ctrl->type == V4L2_CTRL_TYPE_INTEGER_MENU) - qc->step = 1; - else - qc->step = ctrl->step; - qc->flags = ctrl->flags; - qc->type = ctrl->type; - return 0; -} -EXPORT_SYMBOL(v4l2_queryctrl); - -int v4l2_subdev_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) -{ - if (qc->id & V4L2_CTRL_FLAG_NEXT_CTRL) - return -EINVAL; - return v4l2_queryctrl(sd->ctrl_handler, qc); -} -EXPORT_SYMBOL(v4l2_subdev_queryctrl); - -/* Implement VIDIOC_QUERYMENU */ -int v4l2_querymenu(struct v4l2_ctrl_handler *hdl, struct v4l2_querymenu *qm) -{ - struct v4l2_ctrl *ctrl; - u32 i = qm->index; - - ctrl = v4l2_ctrl_find(hdl, qm->id); - if (!ctrl) - return -EINVAL; - - qm->reserved = 0; - /* Sanity checks */ - switch (ctrl->type) { - case V4L2_CTRL_TYPE_MENU: - if (ctrl->qmenu == NULL) - return -EINVAL; - break; - case V4L2_CTRL_TYPE_INTEGER_MENU: - if (ctrl->qmenu_int == NULL) - return -EINVAL; - break; - default: - return -EINVAL; - } - - if (i < ctrl->minimum || i > ctrl->maximum) - return -EINVAL; - - /* Use mask to see if this menu item should be skipped */ - if (ctrl->menu_skip_mask & (1 << i)) - return -EINVAL; - /* Empty menu items should also be skipped */ - if (ctrl->type == V4L2_CTRL_TYPE_MENU) { - if (ctrl->qmenu[i] == NULL || ctrl->qmenu[i][0] == '\0') - return -EINVAL; - strlcpy(qm->name, ctrl->qmenu[i], sizeof(qm->name)); - } else { - qm->value = ctrl->qmenu_int[i]; - } - return 0; -} -EXPORT_SYMBOL(v4l2_querymenu); - -int v4l2_subdev_querymenu(struct v4l2_subdev *sd, struct v4l2_querymenu *qm) -{ - return v4l2_querymenu(sd->ctrl_handler, qm); -} -EXPORT_SYMBOL(v4l2_subdev_querymenu); - - - -/* Some general notes on the atomic requirements of VIDIOC_G/TRY/S_EXT_CTRLS: - - It is not a fully atomic operation, just best-effort only. After all, if - multiple controls have to be set through multiple i2c writes (for example) - then some initial writes may succeed while others fail. Thus leaving the - system in an inconsistent state. The question is how much effort you are - willing to spend on trying to make something atomic that really isn't. - - From the point of view of an application the main requirement is that - when you call VIDIOC_S_EXT_CTRLS and some values are invalid then an - error should be returned without actually affecting any controls. - - If all the values are correct, then it is acceptable to just give up - in case of low-level errors. - - It is important though that the application can tell when only a partial - configuration was done. The way we do that is through the error_idx field - of struct v4l2_ext_controls: if that is equal to the count field then no - controls were affected. Otherwise all controls before that index were - successful in performing their 'get' or 'set' operation, the control at - the given index failed, and you don't know what happened with the controls - after the failed one. Since if they were part of a control cluster they - could have been successfully processed (if a cluster member was encountered - at index < error_idx), they could have failed (if a cluster member was at - error_idx), or they may not have been processed yet (if the first cluster - member appeared after error_idx). - - It is all fairly theoretical, though. In practice all you can do is to - bail out. If error_idx == count, then it is an application bug. If - error_idx < count then it is only an application bug if the error code was - EBUSY. That usually means that something started streaming just when you - tried to set the controls. In all other cases it is a driver/hardware - problem and all you can do is to retry or bail out. - - Note that these rules do not apply to VIDIOC_TRY_EXT_CTRLS: since that - never modifies controls the error_idx is just set to whatever control - has an invalid value. - */ - -/* Prepare for the extended g/s/try functions. - Find the controls in the control array and do some basic checks. */ -static int prepare_ext_ctrls(struct v4l2_ctrl_handler *hdl, - struct v4l2_ext_controls *cs, - struct v4l2_ctrl_helper *helpers) -{ - struct v4l2_ctrl_helper *h; - bool have_clusters = false; - u32 i; - - for (i = 0, h = helpers; i < cs->count; i++, h++) { - struct v4l2_ext_control *c = &cs->controls[i]; - struct v4l2_ctrl_ref *ref; - struct v4l2_ctrl *ctrl; - u32 id = c->id & V4L2_CTRL_ID_MASK; - - cs->error_idx = i; - - if (cs->ctrl_class && V4L2_CTRL_ID2CLASS(id) != cs->ctrl_class) - return -EINVAL; - - /* Old-style private controls are not allowed for - extended controls */ - if (id >= V4L2_CID_PRIVATE_BASE) - return -EINVAL; - ref = find_ref_lock(hdl, id); - if (ref == NULL) - return -EINVAL; - ctrl = ref->ctrl; - if (ctrl->flags & V4L2_CTRL_FLAG_DISABLED) - return -EINVAL; - - if (ctrl->cluster[0]->ncontrols > 1) - have_clusters = true; - if (ctrl->cluster[0] != ctrl) - ref = find_ref_lock(hdl, ctrl->cluster[0]->id); - /* Store the ref to the master control of the cluster */ - h->mref = ref; - h->ctrl = ctrl; - /* Initially set next to 0, meaning that there is no other - control in this helper array belonging to the same - cluster */ - h->next = 0; - } - - /* We are done if there were no controls that belong to a multi- - control cluster. */ - if (!have_clusters) - return 0; - - /* The code below figures out in O(n) time which controls in the list - belong to the same cluster. */ - - /* This has to be done with the handler lock taken. */ - mutex_lock(hdl->lock); - - /* First zero the helper field in the master control references */ - for (i = 0; i < cs->count; i++) - helpers[i].mref->helper = NULL; - for (i = 0, h = helpers; i < cs->count; i++, h++) { - struct v4l2_ctrl_ref *mref = h->mref; - - /* If the mref->helper is set, then it points to an earlier - helper that belongs to the same cluster. */ - if (mref->helper) { - /* Set the next field of mref->helper to the current - index: this means that that earlier helper now - points to the next helper in the same cluster. */ - mref->helper->next = i; - /* mref should be set only for the first helper in the - cluster, clear the others. */ - h->mref = NULL; - } - /* Point the mref helper to the current helper struct. */ - mref->helper = h; - } - mutex_unlock(hdl->lock); - return 0; -} - -/* Handles the corner case where cs->count == 0. It checks whether the - specified control class exists. If that class ID is 0, then it checks - whether there are any controls at all. */ -static int class_check(struct v4l2_ctrl_handler *hdl, u32 ctrl_class) -{ - if (ctrl_class == 0) - return list_empty(&hdl->ctrl_refs) ? -EINVAL : 0; - return find_ref_lock(hdl, ctrl_class | 1) ? 0 : -EINVAL; -} - - - -/* Get extended controls. Allocates the helpers array if needed. */ -int v4l2_g_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *cs) -{ - struct v4l2_ctrl_helper helper[4]; - struct v4l2_ctrl_helper *helpers = helper; - int ret; - int i, j; - - cs->error_idx = cs->count; - cs->ctrl_class = V4L2_CTRL_ID2CLASS(cs->ctrl_class); - - if (hdl == NULL) - return -EINVAL; - - if (cs->count == 0) - return class_check(hdl, cs->ctrl_class); - - if (cs->count > ARRAY_SIZE(helper)) { - helpers = kmalloc_array(cs->count, sizeof(helper[0]), - GFP_KERNEL); - if (helpers == NULL) - return -ENOMEM; - } - - ret = prepare_ext_ctrls(hdl, cs, helpers); - cs->error_idx = cs->count; - - for (i = 0; !ret && i < cs->count; i++) - if (helpers[i].ctrl->flags & V4L2_CTRL_FLAG_WRITE_ONLY) - ret = -EACCES; - - for (i = 0; !ret && i < cs->count; i++) { - int (*ctrl_to_user)(struct v4l2_ext_control *c, - struct v4l2_ctrl *ctrl) = cur_to_user; - struct v4l2_ctrl *master; - - if (helpers[i].mref == NULL) - continue; - - master = helpers[i].mref->ctrl; - cs->error_idx = i; - - v4l2_ctrl_lock(master); - - /* g_volatile_ctrl will update the new control values */ - if ((master->flags & V4L2_CTRL_FLAG_VOLATILE) || - (master->has_volatiles && !is_cur_manual(master))) { - for (j = 0; j < master->ncontrols; j++) - cur_to_new(master->cluster[j]); - ret = call_op(master, g_volatile_ctrl); - ctrl_to_user = new_to_user; - } - /* If OK, then copy the current (for non-volatile controls) - or the new (for volatile controls) control values to the - caller */ - if (!ret) { - u32 idx = i; - - do { - ret = ctrl_to_user(cs->controls + idx, - helpers[idx].ctrl); - idx = helpers[idx].next; - } while (!ret && idx); - } - v4l2_ctrl_unlock(master); - } - - if (cs->count > ARRAY_SIZE(helper)) - kfree(helpers); - return ret; -} -EXPORT_SYMBOL(v4l2_g_ext_ctrls); - -int v4l2_subdev_g_ext_ctrls(struct v4l2_subdev *sd, struct v4l2_ext_controls *cs) -{ - return v4l2_g_ext_ctrls(sd->ctrl_handler, cs); -} -EXPORT_SYMBOL(v4l2_subdev_g_ext_ctrls); - -/* Helper function to get a single control */ -static int get_ctrl(struct v4l2_ctrl *ctrl, s32 *val) -{ - struct v4l2_ctrl *master = ctrl->cluster[0]; - int ret = 0; - int i; - - if (ctrl->flags & V4L2_CTRL_FLAG_WRITE_ONLY) - return -EACCES; - - v4l2_ctrl_lock(master); - /* g_volatile_ctrl will update the current control values */ - if (ctrl->flags & V4L2_CTRL_FLAG_VOLATILE) { - for (i = 0; i < master->ncontrols; i++) - cur_to_new(master->cluster[i]); - ret = call_op(master, g_volatile_ctrl); - *val = ctrl->val; - } else { - *val = ctrl->cur.val; - } - v4l2_ctrl_unlock(master); - return ret; -} - -int v4l2_g_ctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_control *control) -{ - struct v4l2_ctrl *ctrl = v4l2_ctrl_find(hdl, control->id); - - if (ctrl == NULL || !type_is_int(ctrl)) - return -EINVAL; - return get_ctrl(ctrl, &control->value); -} -EXPORT_SYMBOL(v4l2_g_ctrl); - -int v4l2_subdev_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *control) -{ - return v4l2_g_ctrl(sd->ctrl_handler, control); -} -EXPORT_SYMBOL(v4l2_subdev_g_ctrl); - -s32 v4l2_ctrl_g_ctrl(struct v4l2_ctrl *ctrl) -{ - s32 val = 0; - - /* It's a driver bug if this happens. */ - WARN_ON(!type_is_int(ctrl)); - get_ctrl(ctrl, &val); - return val; -} -EXPORT_SYMBOL(v4l2_ctrl_g_ctrl); - - -/* Core function that calls try/s_ctrl and ensures that the new value is - copied to the current value on a set. - Must be called with ctrl->handler->lock held. */ -static int try_or_set_cluster(struct v4l2_fh *fh, - struct v4l2_ctrl *master, bool set) -{ - bool update_flag; - int ret; - int i; - - /* Go through the cluster and either validate the new value or - (if no new value was set), copy the current value to the new - value, ensuring a consistent view for the control ops when - called. */ - for (i = 0; i < master->ncontrols; i++) { - struct v4l2_ctrl *ctrl = master->cluster[i]; - - if (ctrl == NULL) - continue; - - if (!ctrl->is_new) { - cur_to_new(ctrl); - continue; - } - /* Check again: it may have changed since the - previous check in try_or_set_ext_ctrls(). */ - if (set && (ctrl->flags & V4L2_CTRL_FLAG_GRABBED)) - return -EBUSY; - } - - ret = call_op(master, try_ctrl); - - /* Don't set if there is no change */ - if (ret || !set || !cluster_changed(master)) - return ret; - ret = call_op(master, s_ctrl); - if (ret) - return ret; - - /* If OK, then make the new values permanent. */ - update_flag = is_cur_manual(master) != is_new_manual(master); - for (i = 0; i < master->ncontrols; i++) - new_to_cur(fh, master->cluster[i], update_flag && i > 0); - return 0; -} - -/* Validate controls. */ -static int validate_ctrls(struct v4l2_ext_controls *cs, - struct v4l2_ctrl_helper *helpers, bool set) -{ - unsigned i; - int ret = 0; - - cs->error_idx = cs->count; - for (i = 0; i < cs->count; i++) { - struct v4l2_ctrl *ctrl = helpers[i].ctrl; - - cs->error_idx = i; - - if (ctrl->flags & V4L2_CTRL_FLAG_READ_ONLY) - return -EACCES; - /* This test is also done in try_set_control_cluster() which - is called in atomic context, so that has the final say, - but it makes sense to do an up-front check as well. Once - an error occurs in try_set_control_cluster() some other - controls may have been set already and we want to do a - best-effort to avoid that. */ - if (set && (ctrl->flags & V4L2_CTRL_FLAG_GRABBED)) - return -EBUSY; - ret = validate_new(ctrl, &cs->controls[i]); - if (ret) - return ret; - } - return 0; -} - -/* Obtain the current volatile values of an autocluster and mark them - as new. */ -static void update_from_auto_cluster(struct v4l2_ctrl *master) -{ - int i; - - for (i = 0; i < master->ncontrols; i++) - cur_to_new(master->cluster[i]); - if (!call_op(master, g_volatile_ctrl)) - for (i = 1; i < master->ncontrols; i++) - if (master->cluster[i]) - master->cluster[i]->is_new = 1; -} - -/* Try or try-and-set controls */ -static int try_set_ext_ctrls(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl, - struct v4l2_ext_controls *cs, - bool set) -{ - struct v4l2_ctrl_helper helper[4]; - struct v4l2_ctrl_helper *helpers = helper; - unsigned i, j; - int ret; - - cs->error_idx = cs->count; - cs->ctrl_class = V4L2_CTRL_ID2CLASS(cs->ctrl_class); - - if (hdl == NULL) - return -EINVAL; - - if (cs->count == 0) - return class_check(hdl, cs->ctrl_class); - - if (cs->count > ARRAY_SIZE(helper)) { - helpers = kmalloc_array(cs->count, sizeof(helper[0]), - GFP_KERNEL); - if (!helpers) - return -ENOMEM; - } - ret = prepare_ext_ctrls(hdl, cs, helpers); - if (!ret) - ret = validate_ctrls(cs, helpers, set); - if (ret && set) - cs->error_idx = cs->count; - for (i = 0; !ret && i < cs->count; i++) { - struct v4l2_ctrl *master; - u32 idx = i; - - if (helpers[i].mref == NULL) - continue; - - cs->error_idx = i; - master = helpers[i].mref->ctrl; - v4l2_ctrl_lock(master); - - /* Reset the 'is_new' flags of the cluster */ - for (j = 0; j < master->ncontrols; j++) - if (master->cluster[j]) - master->cluster[j]->is_new = 0; - - /* For volatile autoclusters that are currently in auto mode - we need to discover if it will be set to manual mode. - If so, then we have to copy the current volatile values - first since those will become the new manual values (which - may be overwritten by explicit new values from this set - of controls). */ - if (master->is_auto && master->has_volatiles && - !is_cur_manual(master)) { - /* Pick an initial non-manual value */ - s32 new_auto_val = master->manual_mode_value + 1; - u32 tmp_idx = idx; - - do { - /* Check if the auto control is part of the - list, and remember the new value. */ - if (helpers[tmp_idx].ctrl == master) - new_auto_val = cs->controls[tmp_idx].value; - tmp_idx = helpers[tmp_idx].next; - } while (tmp_idx); - /* If the new value == the manual value, then copy - the current volatile values. */ - if (new_auto_val == master->manual_mode_value) - update_from_auto_cluster(master); - } - - /* Copy the new caller-supplied control values. - user_to_new() sets 'is_new' to 1. */ - do { - ret = user_to_new(cs->controls + idx, helpers[idx].ctrl); - idx = helpers[idx].next; - } while (!ret && idx); - - if (!ret) - ret = try_or_set_cluster(fh, master, set); - - /* Copy the new values back to userspace. */ - if (!ret) { - idx = i; - do { - ret = new_to_user(cs->controls + idx, - helpers[idx].ctrl); - idx = helpers[idx].next; - } while (!ret && idx); - } - v4l2_ctrl_unlock(master); - } - - if (cs->count > ARRAY_SIZE(helper)) - kfree(helpers); - return ret; -} - -int v4l2_try_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *cs) -{ - return try_set_ext_ctrls(NULL, hdl, cs, false); -} -EXPORT_SYMBOL(v4l2_try_ext_ctrls); - -int v4l2_s_ext_ctrls(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl, - struct v4l2_ext_controls *cs) -{ - return try_set_ext_ctrls(fh, hdl, cs, true); -} -EXPORT_SYMBOL(v4l2_s_ext_ctrls); - -int v4l2_subdev_try_ext_ctrls(struct v4l2_subdev *sd, struct v4l2_ext_controls *cs) -{ - return try_set_ext_ctrls(NULL, sd->ctrl_handler, cs, false); -} -EXPORT_SYMBOL(v4l2_subdev_try_ext_ctrls); - -int v4l2_subdev_s_ext_ctrls(struct v4l2_subdev *sd, struct v4l2_ext_controls *cs) -{ - return try_set_ext_ctrls(NULL, sd->ctrl_handler, cs, true); -} -EXPORT_SYMBOL(v4l2_subdev_s_ext_ctrls); - -/* Helper function for VIDIOC_S_CTRL compatibility */ -static int set_ctrl(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, s32 *val) -{ - struct v4l2_ctrl *master = ctrl->cluster[0]; - int ret; - int i; - - ret = validate_new_int(ctrl, val); - if (ret) - return ret; - - v4l2_ctrl_lock(ctrl); - - /* Reset the 'is_new' flags of the cluster */ - for (i = 0; i < master->ncontrols; i++) - if (master->cluster[i]) - master->cluster[i]->is_new = 0; - - /* For autoclusters with volatiles that are switched from auto to - manual mode we have to update the current volatile values since - those will become the initial manual values after such a switch. */ - if (master->is_auto && master->has_volatiles && ctrl == master && - !is_cur_manual(master) && *val == master->manual_mode_value) - update_from_auto_cluster(master); - ctrl->val = *val; - ctrl->is_new = 1; - ret = try_or_set_cluster(fh, master, true); - *val = ctrl->cur.val; - v4l2_ctrl_unlock(ctrl); - return ret; -} - -int v4l2_s_ctrl(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl, - struct v4l2_control *control) -{ - struct v4l2_ctrl *ctrl = v4l2_ctrl_find(hdl, control->id); - - if (ctrl == NULL || !type_is_int(ctrl)) - return -EINVAL; - - if (ctrl->flags & V4L2_CTRL_FLAG_READ_ONLY) - return -EACCES; - - return set_ctrl(fh, ctrl, &control->value); -} -EXPORT_SYMBOL(v4l2_s_ctrl); - -int v4l2_subdev_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *control) -{ - return v4l2_s_ctrl(NULL, sd->ctrl_handler, control); -} -EXPORT_SYMBOL(v4l2_subdev_s_ctrl); - -int v4l2_ctrl_s_ctrl(struct v4l2_ctrl *ctrl, s32 val) -{ - /* It's a driver bug if this happens. */ - WARN_ON(!type_is_int(ctrl)); - return set_ctrl(NULL, ctrl, &val); -} -EXPORT_SYMBOL(v4l2_ctrl_s_ctrl); - -static int v4l2_ctrl_add_event(struct v4l2_subscribed_event *sev, unsigned elems) -{ - struct v4l2_ctrl *ctrl = v4l2_ctrl_find(sev->fh->ctrl_handler, sev->id); - - if (ctrl == NULL) - return -EINVAL; - - v4l2_ctrl_lock(ctrl); - list_add_tail(&sev->node, &ctrl->ev_subs); - if (ctrl->type != V4L2_CTRL_TYPE_CTRL_CLASS && - (sev->flags & V4L2_EVENT_SUB_FL_SEND_INITIAL)) { - struct v4l2_event ev; - u32 changes = V4L2_EVENT_CTRL_CH_FLAGS; - - if (!(ctrl->flags & V4L2_CTRL_FLAG_WRITE_ONLY)) - changes |= V4L2_EVENT_CTRL_CH_VALUE; - fill_event(&ev, ctrl, changes); - /* Mark the queue as active, allowing this initial - event to be accepted. */ - sev->elems = elems; - v4l2_event_queue_fh(sev->fh, &ev); - } - v4l2_ctrl_unlock(ctrl); - return 0; -} - -static void v4l2_ctrl_del_event(struct v4l2_subscribed_event *sev) -{ - struct v4l2_ctrl *ctrl = v4l2_ctrl_find(sev->fh->ctrl_handler, sev->id); - - v4l2_ctrl_lock(ctrl); - list_del(&sev->node); - v4l2_ctrl_unlock(ctrl); -} - -void v4l2_ctrl_replace(struct v4l2_event *old, const struct v4l2_event *new) -{ - u32 old_changes = old->u.ctrl.changes; - - old->u.ctrl = new->u.ctrl; - old->u.ctrl.changes |= old_changes; -} -EXPORT_SYMBOL(v4l2_ctrl_replace); - -void v4l2_ctrl_merge(const struct v4l2_event *old, struct v4l2_event *new) -{ - new->u.ctrl.changes |= old->u.ctrl.changes; -} -EXPORT_SYMBOL(v4l2_ctrl_merge); - -const struct v4l2_subscribed_event_ops v4l2_ctrl_sub_ev_ops = { - .add = v4l2_ctrl_add_event, - .del = v4l2_ctrl_del_event, - .replace = v4l2_ctrl_replace, - .merge = v4l2_ctrl_merge, -}; -EXPORT_SYMBOL(v4l2_ctrl_sub_ev_ops); - -int v4l2_ctrl_log_status(struct file *file, void *fh) -{ - struct video_device *vfd = video_devdata(file); - struct v4l2_fh *vfh = file->private_data; - - if (test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags) && vfd->v4l2_dev) - v4l2_ctrl_handler_log_status(vfh->ctrl_handler, - vfd->v4l2_dev->name); - return 0; -} -EXPORT_SYMBOL(v4l2_ctrl_log_status); - -int v4l2_ctrl_subscribe_event(struct v4l2_fh *fh, - struct v4l2_event_subscription *sub) -{ - if (sub->type == V4L2_EVENT_CTRL) - return v4l2_event_subscribe(fh, sub, 0, &v4l2_ctrl_sub_ev_ops); - return -EINVAL; -} -EXPORT_SYMBOL(v4l2_ctrl_subscribe_event); - -unsigned int v4l2_ctrl_poll(struct file *file, struct poll_table_struct *wait) -{ - struct v4l2_fh *fh = file->private_data; - - if (v4l2_event_pending(fh)) - return POLLPRI; - poll_wait(file, &fh->wait, wait); - return 0; -} -EXPORT_SYMBOL(v4l2_ctrl_poll); diff --git a/drivers/media/video/v4l2-dev.c b/drivers/media/video/v4l2-dev.c deleted file mode 100644 index 71237f5f85f4..000000000000 --- a/drivers/media/video/v4l2-dev.c +++ /dev/null @@ -1,1003 +0,0 @@ -/* - * Video capture interface for Linux version 2 - * - * A generic video device interface for the LINUX operating system - * using a set of device structures/vectors for low level operations. - * - * 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. - * - * Authors: Alan Cox, <alan@lxorguk.ukuu.org.uk> (version 1) - * Mauro Carvalho Chehab <mchehab@infradead.org> (version 2) - * - * Fixes: 20000516 Claudio Matsuoka <claudio@conectiva.com> - * - Added procfs support - */ - -#include <linux/module.h> -#include <linux/types.h> -#include <linux/kernel.h> -#include <linux/mm.h> -#include <linux/string.h> -#include <linux/errno.h> -#include <linux/init.h> -#include <linux/kmod.h> -#include <linux/slab.h> -#include <asm/uaccess.h> - -#include <media/v4l2-common.h> -#include <media/v4l2-device.h> -#include <media/v4l2-ioctl.h> - -#define VIDEO_NUM_DEVICES 256 -#define VIDEO_NAME "video4linux" - -/* - * sysfs stuff - */ - -static ssize_t show_index(struct device *cd, - struct device_attribute *attr, char *buf) -{ - struct video_device *vdev = to_video_device(cd); - - return sprintf(buf, "%i\n", vdev->index); -} - -static ssize_t show_debug(struct device *cd, - struct device_attribute *attr, char *buf) -{ - struct video_device *vdev = to_video_device(cd); - - return sprintf(buf, "%i\n", vdev->debug); -} - -static ssize_t set_debug(struct device *cd, struct device_attribute *attr, - const char *buf, size_t len) -{ - struct video_device *vdev = to_video_device(cd); - int res = 0; - u16 value; - - res = kstrtou16(buf, 0, &value); - if (res) - return res; - - vdev->debug = value; - return len; -} - -static ssize_t show_name(struct device *cd, - struct device_attribute *attr, char *buf) -{ - struct video_device *vdev = to_video_device(cd); - - return sprintf(buf, "%.*s\n", (int)sizeof(vdev->name), vdev->name); -} - -static struct device_attribute video_device_attrs[] = { - __ATTR(name, S_IRUGO, show_name, NULL), - __ATTR(debug, 0644, show_debug, set_debug), - __ATTR(index, S_IRUGO, show_index, NULL), - __ATTR_NULL -}; - -/* - * Active devices - */ -static struct video_device *video_device[VIDEO_NUM_DEVICES]; -static DEFINE_MUTEX(videodev_lock); -static DECLARE_BITMAP(devnode_nums[VFL_TYPE_MAX], VIDEO_NUM_DEVICES); - -/* Device node utility functions */ - -/* Note: these utility functions all assume that vfl_type is in the range - [0, VFL_TYPE_MAX-1]. */ - -#ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES -/* Return the bitmap corresponding to vfl_type. */ -static inline unsigned long *devnode_bits(int vfl_type) -{ - /* Any types not assigned to fixed minor ranges must be mapped to - one single bitmap for the purposes of finding a free node number - since all those unassigned types use the same minor range. */ - int idx = (vfl_type > VFL_TYPE_RADIO) ? VFL_TYPE_MAX - 1 : vfl_type; - - return devnode_nums[idx]; -} -#else -/* Return the bitmap corresponding to vfl_type. */ -static inline unsigned long *devnode_bits(int vfl_type) -{ - return devnode_nums[vfl_type]; -} -#endif - -/* Mark device node number vdev->num as used */ -static inline void devnode_set(struct video_device *vdev) -{ - set_bit(vdev->num, devnode_bits(vdev->vfl_type)); -} - -/* Mark device node number vdev->num as unused */ -static inline void devnode_clear(struct video_device *vdev) -{ - clear_bit(vdev->num, devnode_bits(vdev->vfl_type)); -} - -/* Try to find a free device node number in the range [from, to> */ -static inline int devnode_find(struct video_device *vdev, int from, int to) -{ - return find_next_zero_bit(devnode_bits(vdev->vfl_type), to, from); -} - -struct video_device *video_device_alloc(void) -{ - return kzalloc(sizeof(struct video_device), GFP_KERNEL); -} -EXPORT_SYMBOL(video_device_alloc); - -void video_device_release(struct video_device *vdev) -{ - kfree(vdev); -} -EXPORT_SYMBOL(video_device_release); - -void video_device_release_empty(struct video_device *vdev) -{ - /* Do nothing */ - /* Only valid when the video_device struct is a static. */ -} -EXPORT_SYMBOL(video_device_release_empty); - -static inline void video_get(struct video_device *vdev) -{ - get_device(&vdev->dev); -} - -static inline void video_put(struct video_device *vdev) -{ - put_device(&vdev->dev); -} - -/* Called when the last user of the video device exits. */ -static void v4l2_device_release(struct device *cd) -{ - struct video_device *vdev = to_video_device(cd); - struct v4l2_device *v4l2_dev = vdev->v4l2_dev; - - mutex_lock(&videodev_lock); - if (WARN_ON(video_device[vdev->minor] != vdev)) { - /* should not happen */ - mutex_unlock(&videodev_lock); - return; - } - - /* Free up this device for reuse */ - video_device[vdev->minor] = NULL; - - /* Delete the cdev on this minor as well */ - cdev_del(vdev->cdev); - /* Just in case some driver tries to access this from - the release() callback. */ - vdev->cdev = NULL; - - /* Mark device node number as free */ - devnode_clear(vdev); - - mutex_unlock(&videodev_lock); - -#if defined(CONFIG_MEDIA_CONTROLLER) - if (v4l2_dev && v4l2_dev->mdev && - vdev->vfl_type != VFL_TYPE_SUBDEV) - media_device_unregister_entity(&vdev->entity); -#endif - - /* Do not call v4l2_device_put if there is no release callback set. - * Drivers that have no v4l2_device release callback might free the - * v4l2_dev instance in the video_device release callback below, so we - * must perform this check here. - * - * TODO: In the long run all drivers that use v4l2_device should use the - * v4l2_device release callback. This check will then be unnecessary. - */ - if (v4l2_dev && v4l2_dev->release == NULL) - v4l2_dev = NULL; - - /* Release video_device and perform other - cleanups as needed. */ - vdev->release(vdev); - - /* Decrease v4l2_device refcount */ - if (v4l2_dev) - v4l2_device_put(v4l2_dev); -} - -static struct class video_class = { - .name = VIDEO_NAME, - .dev_attrs = video_device_attrs, -}; - -struct video_device *video_devdata(struct file *file) -{ - return video_device[iminor(file->f_path.dentry->d_inode)]; -} -EXPORT_SYMBOL(video_devdata); - - -/* Priority handling */ - -static inline bool prio_is_valid(enum v4l2_priority prio) -{ - return prio == V4L2_PRIORITY_BACKGROUND || - prio == V4L2_PRIORITY_INTERACTIVE || - prio == V4L2_PRIORITY_RECORD; -} - -void v4l2_prio_init(struct v4l2_prio_state *global) -{ - memset(global, 0, sizeof(*global)); -} -EXPORT_SYMBOL(v4l2_prio_init); - -int v4l2_prio_change(struct v4l2_prio_state *global, enum v4l2_priority *local, - enum v4l2_priority new) -{ - if (!prio_is_valid(new)) - return -EINVAL; - if (*local == new) - return 0; - - atomic_inc(&global->prios[new]); - if (prio_is_valid(*local)) - atomic_dec(&global->prios[*local]); - *local = new; - return 0; -} -EXPORT_SYMBOL(v4l2_prio_change); - -void v4l2_prio_open(struct v4l2_prio_state *global, enum v4l2_priority *local) -{ - v4l2_prio_change(global, local, V4L2_PRIORITY_DEFAULT); -} -EXPORT_SYMBOL(v4l2_prio_open); - -void v4l2_prio_close(struct v4l2_prio_state *global, enum v4l2_priority local) -{ - if (prio_is_valid(local)) - atomic_dec(&global->prios[local]); -} -EXPORT_SYMBOL(v4l2_prio_close); - -enum v4l2_priority v4l2_prio_max(struct v4l2_prio_state *global) -{ - if (atomic_read(&global->prios[V4L2_PRIORITY_RECORD]) > 0) - return V4L2_PRIORITY_RECORD; - if (atomic_read(&global->prios[V4L2_PRIORITY_INTERACTIVE]) > 0) - return V4L2_PRIORITY_INTERACTIVE; - if (atomic_read(&global->prios[V4L2_PRIORITY_BACKGROUND]) > 0) - return V4L2_PRIORITY_BACKGROUND; - return V4L2_PRIORITY_UNSET; -} -EXPORT_SYMBOL(v4l2_prio_max); - -int v4l2_prio_check(struct v4l2_prio_state *global, enum v4l2_priority local) -{ - return (local < v4l2_prio_max(global)) ? -EBUSY : 0; -} -EXPORT_SYMBOL(v4l2_prio_check); - - -static ssize_t v4l2_read(struct file *filp, char __user *buf, - size_t sz, loff_t *off) -{ - struct video_device *vdev = video_devdata(filp); - int ret = -ENODEV; - - if (!vdev->fops->read) - return -EINVAL; - if (video_is_registered(vdev)) - ret = vdev->fops->read(filp, buf, sz, off); - if (vdev->debug) - printk(KERN_DEBUG "%s: read: %zd (%d)\n", - video_device_node_name(vdev), sz, ret); - return ret; -} - -static ssize_t v4l2_write(struct file *filp, const char __user *buf, - size_t sz, loff_t *off) -{ - struct video_device *vdev = video_devdata(filp); - int ret = -ENODEV; - - if (!vdev->fops->write) - return -EINVAL; - if (video_is_registered(vdev)) - ret = vdev->fops->write(filp, buf, sz, off); - if (vdev->debug) - printk(KERN_DEBUG "%s: write: %zd (%d)\n", - video_device_node_name(vdev), sz, ret); - return ret; -} - -static unsigned int v4l2_poll(struct file *filp, struct poll_table_struct *poll) -{ - struct video_device *vdev = video_devdata(filp); - unsigned int res = POLLERR | POLLHUP; - - if (!vdev->fops->poll) - return DEFAULT_POLLMASK; - if (video_is_registered(vdev)) - res = vdev->fops->poll(filp, poll); - if (vdev->debug) - printk(KERN_DEBUG "%s: poll: %08x\n", - video_device_node_name(vdev), res); - return res; -} - -static long v4l2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) -{ - struct video_device *vdev = video_devdata(filp); - int ret = -ENODEV; - - if (vdev->fops->unlocked_ioctl) { - struct mutex *lock = v4l2_ioctl_get_lock(vdev, cmd); - - if (lock && mutex_lock_interruptible(lock)) - return -ERESTARTSYS; - if (video_is_registered(vdev)) - ret = vdev->fops->unlocked_ioctl(filp, cmd, arg); - if (lock) - mutex_unlock(lock); - } else if (vdev->fops->ioctl) { - /* This code path is a replacement for the BKL. It is a major - * hack but it will have to do for those drivers that are not - * yet converted to use unlocked_ioctl. - * - * There are two options: if the driver implements struct - * v4l2_device, then the lock defined there is used to - * serialize the ioctls. Otherwise the v4l2 core lock defined - * below is used. This lock is really bad since it serializes - * completely independent devices. - * - * Both variants suffer from the same problem: if the driver - * sleeps, then it blocks all ioctls since the lock is still - * held. This is very common for VIDIOC_DQBUF since that - * normally waits for a frame to arrive. As a result any other - * ioctl calls will proceed very, very slowly since each call - * will have to wait for the VIDIOC_QBUF to finish. Things that - * should take 0.01s may now take 10-20 seconds. - * - * The workaround is to *not* take the lock for VIDIOC_DQBUF. - * This actually works OK for videobuf-based drivers, since - * videobuf will take its own internal lock. - */ - static DEFINE_MUTEX(v4l2_ioctl_mutex); - struct mutex *m = vdev->v4l2_dev ? - &vdev->v4l2_dev->ioctl_lock : &v4l2_ioctl_mutex; - - if (cmd != VIDIOC_DQBUF && mutex_lock_interruptible(m)) - return -ERESTARTSYS; - if (video_is_registered(vdev)) - ret = vdev->fops->ioctl(filp, cmd, arg); - if (cmd != VIDIOC_DQBUF) - mutex_unlock(m); - } else - ret = -ENOTTY; - - return ret; -} - -#ifdef CONFIG_MMU -#define v4l2_get_unmapped_area NULL -#else -static unsigned long v4l2_get_unmapped_area(struct file *filp, - unsigned long addr, unsigned long len, unsigned long pgoff, - unsigned long flags) -{ - struct video_device *vdev = video_devdata(filp); - int ret; - - if (!vdev->fops->get_unmapped_area) - return -ENOSYS; - if (!video_is_registered(vdev)) - return -ENODEV; - ret = vdev->fops->get_unmapped_area(filp, addr, len, pgoff, flags); - if (vdev->debug) - printk(KERN_DEBUG "%s: get_unmapped_area (%d)\n", - video_device_node_name(vdev), ret); - return ret; -} -#endif - -static int v4l2_mmap(struct file *filp, struct vm_area_struct *vm) -{ - struct video_device *vdev = video_devdata(filp); - int ret = -ENODEV; - - if (!vdev->fops->mmap) - return -ENODEV; - if (video_is_registered(vdev)) - ret = vdev->fops->mmap(filp, vm); - if (vdev->debug) - printk(KERN_DEBUG "%s: mmap (%d)\n", - video_device_node_name(vdev), ret); - return ret; -} - -/* Override for the open function */ -static int v4l2_open(struct inode *inode, struct file *filp) -{ - struct video_device *vdev; - int ret = 0; - - /* Check if the video device is available */ - mutex_lock(&videodev_lock); - vdev = video_devdata(filp); - /* return ENODEV if the video device has already been removed. */ - if (vdev == NULL || !video_is_registered(vdev)) { - mutex_unlock(&videodev_lock); - return -ENODEV; - } - /* and increase the device refcount */ - video_get(vdev); - mutex_unlock(&videodev_lock); - if (vdev->fops->open) { - if (video_is_registered(vdev)) - ret = vdev->fops->open(filp); - else - ret = -ENODEV; - } - - if (vdev->debug) - printk(KERN_DEBUG "%s: open (%d)\n", - video_device_node_name(vdev), ret); - /* decrease the refcount in case of an error */ - if (ret) - video_put(vdev); - return ret; -} - -/* Override for the release function */ -static int v4l2_release(struct inode *inode, struct file *filp) -{ - struct video_device *vdev = video_devdata(filp); - int ret = 0; - - if (vdev->fops->release) - ret = vdev->fops->release(filp); - if (vdev->debug) - printk(KERN_DEBUG "%s: release\n", - video_device_node_name(vdev)); - - /* decrease the refcount unconditionally since the release() - return value is ignored. */ - video_put(vdev); - return ret; -} - -static const struct file_operations v4l2_fops = { - .owner = THIS_MODULE, - .read = v4l2_read, - .write = v4l2_write, - .open = v4l2_open, - .get_unmapped_area = v4l2_get_unmapped_area, - .mmap = v4l2_mmap, - .unlocked_ioctl = v4l2_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = v4l2_compat_ioctl32, -#endif - .release = v4l2_release, - .poll = v4l2_poll, - .llseek = no_llseek, -}; - -/** - * get_index - assign stream index number based on parent device - * @vdev: video_device to assign index number to, vdev->parent should be assigned - * - * Note that when this is called the new device has not yet been registered - * in the video_device array, but it was able to obtain a minor number. - * - * This means that we can always obtain a free stream index number since - * the worst case scenario is that there are VIDEO_NUM_DEVICES - 1 slots in - * use of the video_device array. - * - * Returns a free index number. - */ -static int get_index(struct video_device *vdev) -{ - /* This can be static since this function is called with the global - videodev_lock held. */ - static DECLARE_BITMAP(used, VIDEO_NUM_DEVICES); - int i; - - /* Some drivers do not set the parent. In that case always return 0. */ - if (vdev->parent == NULL) - return 0; - - bitmap_zero(used, VIDEO_NUM_DEVICES); - - for (i = 0; i < VIDEO_NUM_DEVICES; i++) { - if (video_device[i] != NULL && - video_device[i]->parent == vdev->parent) { - set_bit(video_device[i]->index, used); - } - } - - return find_first_zero_bit(used, VIDEO_NUM_DEVICES); -} - -#define SET_VALID_IOCTL(ops, cmd, op) \ - if (ops->op) \ - set_bit(_IOC_NR(cmd), valid_ioctls) - -/* This determines which ioctls are actually implemented in the driver. - It's a one-time thing which simplifies video_ioctl2 as it can just do - a bit test. - - Note that drivers can override this by setting bits to 1 in - vdev->valid_ioctls. If an ioctl is marked as 1 when this function is - called, then that ioctl will actually be marked as unimplemented. - - It does that by first setting up the local valid_ioctls bitmap, and - at the end do a: - - vdev->valid_ioctls = valid_ioctls & ~(vdev->valid_ioctls) - */ -static void determine_valid_ioctls(struct video_device *vdev) -{ - DECLARE_BITMAP(valid_ioctls, BASE_VIDIOC_PRIVATE); - const struct v4l2_ioctl_ops *ops = vdev->ioctl_ops; - - bitmap_zero(valid_ioctls, BASE_VIDIOC_PRIVATE); - - SET_VALID_IOCTL(ops, VIDIOC_QUERYCAP, vidioc_querycap); - if (ops->vidioc_g_priority || - test_bit(V4L2_FL_USE_FH_PRIO, &vdev->flags)) - set_bit(_IOC_NR(VIDIOC_G_PRIORITY), valid_ioctls); - if (ops->vidioc_s_priority || - test_bit(V4L2_FL_USE_FH_PRIO, &vdev->flags)) - set_bit(_IOC_NR(VIDIOC_S_PRIORITY), valid_ioctls); - if (ops->vidioc_enum_fmt_vid_cap || - ops->vidioc_enum_fmt_vid_out || - ops->vidioc_enum_fmt_vid_cap_mplane || - ops->vidioc_enum_fmt_vid_out_mplane || - ops->vidioc_enum_fmt_vid_overlay || - ops->vidioc_enum_fmt_type_private) - set_bit(_IOC_NR(VIDIOC_ENUM_FMT), valid_ioctls); - if (ops->vidioc_g_fmt_vid_cap || - ops->vidioc_g_fmt_vid_out || - ops->vidioc_g_fmt_vid_cap_mplane || - ops->vidioc_g_fmt_vid_out_mplane || - ops->vidioc_g_fmt_vid_overlay || - ops->vidioc_g_fmt_vbi_cap || - ops->vidioc_g_fmt_vid_out_overlay || - ops->vidioc_g_fmt_vbi_out || - ops->vidioc_g_fmt_sliced_vbi_cap || - ops->vidioc_g_fmt_sliced_vbi_out || - ops->vidioc_g_fmt_type_private) - set_bit(_IOC_NR(VIDIOC_G_FMT), valid_ioctls); - if (ops->vidioc_s_fmt_vid_cap || - ops->vidioc_s_fmt_vid_out || - ops->vidioc_s_fmt_vid_cap_mplane || - ops->vidioc_s_fmt_vid_out_mplane || - ops->vidioc_s_fmt_vid_overlay || - ops->vidioc_s_fmt_vbi_cap || - ops->vidioc_s_fmt_vid_out_overlay || - ops->vidioc_s_fmt_vbi_out || - ops->vidioc_s_fmt_sliced_vbi_cap || - ops->vidioc_s_fmt_sliced_vbi_out || - ops->vidioc_s_fmt_type_private) - set_bit(_IOC_NR(VIDIOC_S_FMT), valid_ioctls); - if (ops->vidioc_try_fmt_vid_cap || - ops->vidioc_try_fmt_vid_out || - ops->vidioc_try_fmt_vid_cap_mplane || - ops->vidioc_try_fmt_vid_out_mplane || - ops->vidioc_try_fmt_vid_overlay || - ops->vidioc_try_fmt_vbi_cap || - ops->vidioc_try_fmt_vid_out_overlay || - ops->vidioc_try_fmt_vbi_out || - ops->vidioc_try_fmt_sliced_vbi_cap || - ops->vidioc_try_fmt_sliced_vbi_out || - ops->vidioc_try_fmt_type_private) - set_bit(_IOC_NR(VIDIOC_TRY_FMT), valid_ioctls); - SET_VALID_IOCTL(ops, VIDIOC_REQBUFS, vidioc_reqbufs); - SET_VALID_IOCTL(ops, VIDIOC_QUERYBUF, vidioc_querybuf); - SET_VALID_IOCTL(ops, VIDIOC_QBUF, vidioc_qbuf); - SET_VALID_IOCTL(ops, VIDIOC_DQBUF, vidioc_dqbuf); - SET_VALID_IOCTL(ops, VIDIOC_OVERLAY, vidioc_overlay); - SET_VALID_IOCTL(ops, VIDIOC_G_FBUF, vidioc_g_fbuf); - SET_VALID_IOCTL(ops, VIDIOC_S_FBUF, vidioc_s_fbuf); - SET_VALID_IOCTL(ops, VIDIOC_STREAMON, vidioc_streamon); - SET_VALID_IOCTL(ops, VIDIOC_STREAMOFF, vidioc_streamoff); - if (vdev->tvnorms) - set_bit(_IOC_NR(VIDIOC_ENUMSTD), valid_ioctls); - if (ops->vidioc_g_std || vdev->current_norm) - set_bit(_IOC_NR(VIDIOC_G_STD), valid_ioctls); - SET_VALID_IOCTL(ops, VIDIOC_S_STD, vidioc_s_std); - SET_VALID_IOCTL(ops, VIDIOC_QUERYSTD, vidioc_querystd); - SET_VALID_IOCTL(ops, VIDIOC_ENUMINPUT, vidioc_enum_input); - SET_VALID_IOCTL(ops, VIDIOC_G_INPUT, vidioc_g_input); - SET_VALID_IOCTL(ops, VIDIOC_S_INPUT, vidioc_s_input); - SET_VALID_IOCTL(ops, VIDIOC_ENUMOUTPUT, vidioc_enum_output); - SET_VALID_IOCTL(ops, VIDIOC_G_OUTPUT, vidioc_g_output); - SET_VALID_IOCTL(ops, VIDIOC_S_OUTPUT, vidioc_s_output); - /* Note: the control handler can also be passed through the filehandle, - and that can't be tested here. If the bit for these control ioctls - is set, then the ioctl is valid. But if it is 0, then it can still - be valid if the filehandle passed the control handler. */ - if (vdev->ctrl_handler || ops->vidioc_queryctrl) - set_bit(_IOC_NR(VIDIOC_QUERYCTRL), valid_ioctls); - if (vdev->ctrl_handler || ops->vidioc_g_ctrl || ops->vidioc_g_ext_ctrls) - set_bit(_IOC_NR(VIDIOC_G_CTRL), valid_ioctls); - if (vdev->ctrl_handler || ops->vidioc_s_ctrl || ops->vidioc_s_ext_ctrls) - set_bit(_IOC_NR(VIDIOC_S_CTRL), valid_ioctls); - if (vdev->ctrl_handler || ops->vidioc_g_ext_ctrls) - set_bit(_IOC_NR(VIDIOC_G_EXT_CTRLS), valid_ioctls); - if (vdev->ctrl_handler || ops->vidioc_s_ext_ctrls) - set_bit(_IOC_NR(VIDIOC_S_EXT_CTRLS), valid_ioctls); - if (vdev->ctrl_handler || ops->vidioc_try_ext_ctrls) - set_bit(_IOC_NR(VIDIOC_TRY_EXT_CTRLS), valid_ioctls); - if (vdev->ctrl_handler || ops->vidioc_querymenu) - set_bit(_IOC_NR(VIDIOC_QUERYMENU), valid_ioctls); - SET_VALID_IOCTL(ops, VIDIOC_ENUMAUDIO, vidioc_enumaudio); - SET_VALID_IOCTL(ops, VIDIOC_G_AUDIO, vidioc_g_audio); - SET_VALID_IOCTL(ops, VIDIOC_S_AUDIO, vidioc_s_audio); - SET_VALID_IOCTL(ops, VIDIOC_ENUMAUDOUT, vidioc_enumaudout); - SET_VALID_IOCTL(ops, VIDIOC_G_AUDOUT, vidioc_g_audout); - SET_VALID_IOCTL(ops, VIDIOC_S_AUDOUT, vidioc_s_audout); - SET_VALID_IOCTL(ops, VIDIOC_G_MODULATOR, vidioc_g_modulator); - SET_VALID_IOCTL(ops, VIDIOC_S_MODULATOR, vidioc_s_modulator); - if (ops->vidioc_g_crop || ops->vidioc_g_selection) - set_bit(_IOC_NR(VIDIOC_G_CROP), valid_ioctls); - if (ops->vidioc_s_crop || ops->vidioc_s_selection) - set_bit(_IOC_NR(VIDIOC_S_CROP), valid_ioctls); - SET_VALID_IOCTL(ops, VIDIOC_G_SELECTION, vidioc_g_selection); - SET_VALID_IOCTL(ops, VIDIOC_S_SELECTION, vidioc_s_selection); - if (ops->vidioc_cropcap || ops->vidioc_g_selection) - set_bit(_IOC_NR(VIDIOC_CROPCAP), valid_ioctls); - SET_VALID_IOCTL(ops, VIDIOC_G_JPEGCOMP, vidioc_g_jpegcomp); - SET_VALID_IOCTL(ops, VIDIOC_S_JPEGCOMP, vidioc_s_jpegcomp); - SET_VALID_IOCTL(ops, VIDIOC_G_ENC_INDEX, vidioc_g_enc_index); - SET_VALID_IOCTL(ops, VIDIOC_ENCODER_CMD, vidioc_encoder_cmd); - SET_VALID_IOCTL(ops, VIDIOC_TRY_ENCODER_CMD, vidioc_try_encoder_cmd); - SET_VALID_IOCTL(ops, VIDIOC_DECODER_CMD, vidioc_decoder_cmd); - SET_VALID_IOCTL(ops, VIDIOC_TRY_DECODER_CMD, vidioc_try_decoder_cmd); - if (ops->vidioc_g_parm || (vdev->vfl_type == VFL_TYPE_GRABBER && - (ops->vidioc_g_std || vdev->tvnorms))) - set_bit(_IOC_NR(VIDIOC_G_PARM), valid_ioctls); - SET_VALID_IOCTL(ops, VIDIOC_S_PARM, vidioc_s_parm); - SET_VALID_IOCTL(ops, VIDIOC_G_TUNER, vidioc_g_tuner); - SET_VALID_IOCTL(ops, VIDIOC_S_TUNER, vidioc_s_tuner); - SET_VALID_IOCTL(ops, VIDIOC_G_FREQUENCY, vidioc_g_frequency); - SET_VALID_IOCTL(ops, VIDIOC_S_FREQUENCY, vidioc_s_frequency); - SET_VALID_IOCTL(ops, VIDIOC_G_SLICED_VBI_CAP, vidioc_g_sliced_vbi_cap); - SET_VALID_IOCTL(ops, VIDIOC_LOG_STATUS, vidioc_log_status); -#ifdef CONFIG_VIDEO_ADV_DEBUG - SET_VALID_IOCTL(ops, VIDIOC_DBG_G_REGISTER, vidioc_g_register); - SET_VALID_IOCTL(ops, VIDIOC_DBG_S_REGISTER, vidioc_s_register); -#endif - SET_VALID_IOCTL(ops, VIDIOC_DBG_G_CHIP_IDENT, vidioc_g_chip_ident); - SET_VALID_IOCTL(ops, VIDIOC_S_HW_FREQ_SEEK, vidioc_s_hw_freq_seek); - SET_VALID_IOCTL(ops, VIDIOC_ENUM_FRAMESIZES, vidioc_enum_framesizes); - SET_VALID_IOCTL(ops, VIDIOC_ENUM_FRAMEINTERVALS, vidioc_enum_frameintervals); - SET_VALID_IOCTL(ops, VIDIOC_ENUM_DV_PRESETS, vidioc_enum_dv_presets); - SET_VALID_IOCTL(ops, VIDIOC_S_DV_PRESET, vidioc_s_dv_preset); - SET_VALID_IOCTL(ops, VIDIOC_G_DV_PRESET, vidioc_g_dv_preset); - SET_VALID_IOCTL(ops, VIDIOC_QUERY_DV_PRESET, vidioc_query_dv_preset); - SET_VALID_IOCTL(ops, VIDIOC_S_DV_TIMINGS, vidioc_s_dv_timings); - SET_VALID_IOCTL(ops, VIDIOC_G_DV_TIMINGS, vidioc_g_dv_timings); - SET_VALID_IOCTL(ops, VIDIOC_ENUM_DV_TIMINGS, vidioc_enum_dv_timings); - SET_VALID_IOCTL(ops, VIDIOC_QUERY_DV_TIMINGS, vidioc_query_dv_timings); - SET_VALID_IOCTL(ops, VIDIOC_DV_TIMINGS_CAP, vidioc_dv_timings_cap); - /* yes, really vidioc_subscribe_event */ - SET_VALID_IOCTL(ops, VIDIOC_DQEVENT, vidioc_subscribe_event); - SET_VALID_IOCTL(ops, VIDIOC_SUBSCRIBE_EVENT, vidioc_subscribe_event); - SET_VALID_IOCTL(ops, VIDIOC_UNSUBSCRIBE_EVENT, vidioc_unsubscribe_event); - SET_VALID_IOCTL(ops, VIDIOC_CREATE_BUFS, vidioc_create_bufs); - SET_VALID_IOCTL(ops, VIDIOC_PREPARE_BUF, vidioc_prepare_buf); - if (ops->vidioc_enum_freq_bands || ops->vidioc_g_tuner || ops->vidioc_g_modulator) - set_bit(_IOC_NR(VIDIOC_ENUM_FREQ_BANDS), valid_ioctls); - bitmap_andnot(vdev->valid_ioctls, valid_ioctls, vdev->valid_ioctls, - BASE_VIDIOC_PRIVATE); -} - -/** - * __video_register_device - register video4linux devices - * @vdev: video device structure we want to register - * @type: type of device to register - * @nr: which device node number (0 == /dev/video0, 1 == /dev/video1, ... - * -1 == first free) - * @warn_if_nr_in_use: warn if the desired device node number - * was already in use and another number was chosen instead. - * @owner: module that owns the video device node - * - * The registration code assigns minor numbers and device node numbers - * based on the requested type and registers the new device node with - * the kernel. - * - * This function assumes that struct video_device was zeroed when it - * was allocated and does not contain any stale date. - * - * An error is returned if no free minor or device node number could be - * found, or if the registration of the device node failed. - * - * Zero is returned on success. - * - * Valid types are - * - * %VFL_TYPE_GRABBER - A frame grabber - * - * %VFL_TYPE_VBI - Vertical blank data (undecoded) - * - * %VFL_TYPE_RADIO - A radio card - * - * %VFL_TYPE_SUBDEV - A subdevice - */ -int __video_register_device(struct video_device *vdev, int type, int nr, - int warn_if_nr_in_use, struct module *owner) -{ - int i = 0; - int ret; - int minor_offset = 0; - int minor_cnt = VIDEO_NUM_DEVICES; - const char *name_base; - - /* A minor value of -1 marks this video device as never - having been registered */ - vdev->minor = -1; - - /* the release callback MUST be present */ - if (WARN_ON(!vdev->release)) - return -EINVAL; - - /* v4l2_fh support */ - spin_lock_init(&vdev->fh_lock); - INIT_LIST_HEAD(&vdev->fh_list); - - /* Part 1: check device type */ - switch (type) { - case VFL_TYPE_GRABBER: - name_base = "video"; - break; - case VFL_TYPE_VBI: - name_base = "vbi"; - break; - case VFL_TYPE_RADIO: - name_base = "radio"; - break; - case VFL_TYPE_SUBDEV: - name_base = "v4l-subdev"; - break; - default: - printk(KERN_ERR "%s called with unknown type: %d\n", - __func__, type); - return -EINVAL; - } - - vdev->vfl_type = type; - vdev->cdev = NULL; - if (vdev->v4l2_dev) { - if (vdev->v4l2_dev->dev) - vdev->parent = vdev->v4l2_dev->dev; - if (vdev->ctrl_handler == NULL) - vdev->ctrl_handler = vdev->v4l2_dev->ctrl_handler; - /* If the prio state pointer is NULL, then use the v4l2_device - prio state. */ - if (vdev->prio == NULL) - vdev->prio = &vdev->v4l2_dev->prio; - } - - /* Part 2: find a free minor, device node number and device index. */ -#ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES - /* Keep the ranges for the first four types for historical - * reasons. - * Newer devices (not yet in place) should use the range - * of 128-191 and just pick the first free minor there - * (new style). */ - switch (type) { - case VFL_TYPE_GRABBER: - minor_offset = 0; - minor_cnt = 64; - break; - case VFL_TYPE_RADIO: - minor_offset = 64; - minor_cnt = 64; - break; - case VFL_TYPE_VBI: - minor_offset = 224; - minor_cnt = 32; - break; - default: - minor_offset = 128; - minor_cnt = 64; - break; - } -#endif - - /* Pick a device node number */ - mutex_lock(&videodev_lock); - nr = devnode_find(vdev, nr == -1 ? 0 : nr, minor_cnt); - if (nr == minor_cnt) - nr = devnode_find(vdev, 0, minor_cnt); - if (nr == minor_cnt) { - printk(KERN_ERR "could not get a free device node number\n"); - mutex_unlock(&videodev_lock); - return -ENFILE; - } -#ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES - /* 1-on-1 mapping of device node number to minor number */ - i = nr; -#else - /* The device node number and minor numbers are independent, so - we just find the first free minor number. */ - for (i = 0; i < VIDEO_NUM_DEVICES; i++) - if (video_device[i] == NULL) - break; - if (i == VIDEO_NUM_DEVICES) { - mutex_unlock(&videodev_lock); - printk(KERN_ERR "could not get a free minor\n"); - return -ENFILE; - } -#endif - vdev->minor = i + minor_offset; - vdev->num = nr; - devnode_set(vdev); - - /* Should not happen since we thought this minor was free */ - WARN_ON(video_device[vdev->minor] != NULL); - vdev->index = get_index(vdev); - mutex_unlock(&videodev_lock); - - if (vdev->ioctl_ops) - determine_valid_ioctls(vdev); - - /* Part 3: Initialize the character device */ - vdev->cdev = cdev_alloc(); - if (vdev->cdev == NULL) { - ret = -ENOMEM; - goto cleanup; - } - vdev->cdev->ops = &v4l2_fops; - vdev->cdev->owner = owner; - ret = cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1); - if (ret < 0) { - printk(KERN_ERR "%s: cdev_add failed\n", __func__); - kfree(vdev->cdev); - vdev->cdev = NULL; - goto cleanup; - } - - /* Part 4: register the device with sysfs */ - vdev->dev.class = &video_class; - vdev->dev.devt = MKDEV(VIDEO_MAJOR, vdev->minor); - if (vdev->parent) - vdev->dev.parent = vdev->parent; - dev_set_name(&vdev->dev, "%s%d", name_base, vdev->num); - ret = device_register(&vdev->dev); - if (ret < 0) { - printk(KERN_ERR "%s: device_register failed\n", __func__); - goto cleanup; - } - /* Register the release callback that will be called when the last - reference to the device goes away. */ - vdev->dev.release = v4l2_device_release; - - if (nr != -1 && nr != vdev->num && warn_if_nr_in_use) - printk(KERN_WARNING "%s: requested %s%d, got %s\n", __func__, - name_base, nr, video_device_node_name(vdev)); - - /* Increase v4l2_device refcount */ - if (vdev->v4l2_dev) - v4l2_device_get(vdev->v4l2_dev); - -#if defined(CONFIG_MEDIA_CONTROLLER) - /* Part 5: Register the entity. */ - if (vdev->v4l2_dev && vdev->v4l2_dev->mdev && - vdev->vfl_type != VFL_TYPE_SUBDEV) { - vdev->entity.type = MEDIA_ENT_T_DEVNODE_V4L; - vdev->entity.name = vdev->name; - vdev->entity.info.v4l.major = VIDEO_MAJOR; - vdev->entity.info.v4l.minor = vdev->minor; - ret = media_device_register_entity(vdev->v4l2_dev->mdev, - &vdev->entity); - if (ret < 0) - printk(KERN_WARNING - "%s: media_device_register_entity failed\n", - __func__); - } -#endif - /* Part 6: Activate this minor. The char device can now be used. */ - set_bit(V4L2_FL_REGISTERED, &vdev->flags); - mutex_lock(&videodev_lock); - video_device[vdev->minor] = vdev; - mutex_unlock(&videodev_lock); - - return 0; - -cleanup: - mutex_lock(&videodev_lock); - if (vdev->cdev) - cdev_del(vdev->cdev); - devnode_clear(vdev); - mutex_unlock(&videodev_lock); - /* Mark this video device as never having been registered. */ - vdev->minor = -1; - return ret; -} -EXPORT_SYMBOL(__video_register_device); - -/** - * video_unregister_device - unregister a video4linux device - * @vdev: the device to unregister - * - * This unregisters the passed device. Future open calls will - * be met with errors. - */ -void video_unregister_device(struct video_device *vdev) -{ - /* Check if vdev was ever registered at all */ - if (!vdev || !video_is_registered(vdev)) - return; - - mutex_lock(&videodev_lock); - /* This must be in a critical section to prevent a race with v4l2_open. - * Once this bit has been cleared video_get may never be called again. - */ - clear_bit(V4L2_FL_REGISTERED, &vdev->flags); - mutex_unlock(&videodev_lock); - device_unregister(&vdev->dev); -} -EXPORT_SYMBOL(video_unregister_device); - -/* - * Initialise video for linux - */ -static int __init videodev_init(void) -{ - dev_t dev = MKDEV(VIDEO_MAJOR, 0); - int ret; - - printk(KERN_INFO "Linux video capture interface: v2.00\n"); - ret = register_chrdev_region(dev, VIDEO_NUM_DEVICES, VIDEO_NAME); - if (ret < 0) { - printk(KERN_WARNING "videodev: unable to get major %d\n", - VIDEO_MAJOR); - return ret; - } - - ret = class_register(&video_class); - if (ret < 0) { - unregister_chrdev_region(dev, VIDEO_NUM_DEVICES); - printk(KERN_WARNING "video_dev: class_register failed\n"); - return -EIO; - } - - return 0; -} - -static void __exit videodev_exit(void) -{ - dev_t dev = MKDEV(VIDEO_MAJOR, 0); - - class_unregister(&video_class); - unregister_chrdev_region(dev, VIDEO_NUM_DEVICES); -} - -subsys_initcall(videodev_init); -module_exit(videodev_exit) - -MODULE_AUTHOR("Alan Cox, Mauro Carvalho Chehab <mchehab@infradead.org>"); -MODULE_DESCRIPTION("Device registrar for Video4Linux drivers v2"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_CHARDEV_MAJOR(VIDEO_MAJOR); - - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/video/v4l2-device.c b/drivers/media/video/v4l2-device.c deleted file mode 100644 index 1f203b85a637..000000000000 --- a/drivers/media/video/v4l2-device.c +++ /dev/null @@ -1,280 +0,0 @@ -/* - V4L2 device support. - - Copyright (C) 2008 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 <linux/types.h> -#include <linux/ioctl.h> -#include <linux/module.h> -#include <linux/i2c.h> -#include <linux/slab.h> -#if defined(CONFIG_SPI) -#include <linux/spi/spi.h> -#endif -#include <linux/videodev2.h> -#include <media/v4l2-device.h> -#include <media/v4l2-ctrls.h> - -int v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev) -{ - if (v4l2_dev == NULL) - return -EINVAL; - - INIT_LIST_HEAD(&v4l2_dev->subdevs); - spin_lock_init(&v4l2_dev->lock); - mutex_init(&v4l2_dev->ioctl_lock); - v4l2_prio_init(&v4l2_dev->prio); - kref_init(&v4l2_dev->ref); - get_device(dev); - v4l2_dev->dev = dev; - if (dev == NULL) { - /* If dev == NULL, then name must be filled in by the caller */ - WARN_ON(!v4l2_dev->name[0]); - return 0; - } - - /* Set name to driver name + device name if it is empty. */ - if (!v4l2_dev->name[0]) - snprintf(v4l2_dev->name, sizeof(v4l2_dev->name), "%s %s", - dev->driver->name, dev_name(dev)); - if (!dev_get_drvdata(dev)) - dev_set_drvdata(dev, v4l2_dev); - return 0; -} -EXPORT_SYMBOL_GPL(v4l2_device_register); - -static void v4l2_device_release(struct kref *ref) -{ - struct v4l2_device *v4l2_dev = - container_of(ref, struct v4l2_device, ref); - - if (v4l2_dev->release) - v4l2_dev->release(v4l2_dev); -} - -int v4l2_device_put(struct v4l2_device *v4l2_dev) -{ - return kref_put(&v4l2_dev->ref, v4l2_device_release); -} -EXPORT_SYMBOL_GPL(v4l2_device_put); - -int v4l2_device_set_name(struct v4l2_device *v4l2_dev, const char *basename, - atomic_t *instance) -{ - int num = atomic_inc_return(instance) - 1; - int len = strlen(basename); - - if (basename[len - 1] >= '0' && basename[len - 1] <= '9') - snprintf(v4l2_dev->name, sizeof(v4l2_dev->name), - "%s-%d", basename, num); - else - snprintf(v4l2_dev->name, sizeof(v4l2_dev->name), - "%s%d", basename, num); - return num; -} -EXPORT_SYMBOL_GPL(v4l2_device_set_name); - -void v4l2_device_disconnect(struct v4l2_device *v4l2_dev) -{ - if (v4l2_dev->dev == NULL) - return; - - if (dev_get_drvdata(v4l2_dev->dev) == v4l2_dev) - dev_set_drvdata(v4l2_dev->dev, NULL); - put_device(v4l2_dev->dev); - v4l2_dev->dev = NULL; -} -EXPORT_SYMBOL_GPL(v4l2_device_disconnect); - -void v4l2_device_unregister(struct v4l2_device *v4l2_dev) -{ - struct v4l2_subdev *sd, *next; - - if (v4l2_dev == NULL) - return; - v4l2_device_disconnect(v4l2_dev); - - /* Unregister subdevs */ - list_for_each_entry_safe(sd, next, &v4l2_dev->subdevs, list) { - v4l2_device_unregister_subdev(sd); -#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE)) - if (sd->flags & V4L2_SUBDEV_FL_IS_I2C) { - struct i2c_client *client = v4l2_get_subdevdata(sd); - - /* We need to unregister the i2c client explicitly. - We cannot rely on i2c_del_adapter to always - unregister clients for us, since if the i2c bus - is a platform bus, then it is never deleted. */ - if (client) - i2c_unregister_device(client); - continue; - } -#endif -#if defined(CONFIG_SPI) - if (sd->flags & V4L2_SUBDEV_FL_IS_SPI) { - struct spi_device *spi = v4l2_get_subdevdata(sd); - - if (spi) - spi_unregister_device(spi); - continue; - } -#endif - } -} -EXPORT_SYMBOL_GPL(v4l2_device_unregister); - -int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev, - struct v4l2_subdev *sd) -{ -#if defined(CONFIG_MEDIA_CONTROLLER) - struct media_entity *entity = &sd->entity; -#endif - int err; - - /* Check for valid input */ - if (v4l2_dev == NULL || sd == NULL || !sd->name[0]) - return -EINVAL; - - /* Warn if we apparently re-register a subdev */ - WARN_ON(sd->v4l2_dev != NULL); - - if (!try_module_get(sd->owner)) - return -ENODEV; - - sd->v4l2_dev = v4l2_dev; - if (sd->internal_ops && sd->internal_ops->registered) { - err = sd->internal_ops->registered(sd); - if (err) { - module_put(sd->owner); - return err; - } - } - - /* This just returns 0 if either of the two args is NULL */ - err = v4l2_ctrl_add_handler(v4l2_dev->ctrl_handler, sd->ctrl_handler); - if (err) { - if (sd->internal_ops && sd->internal_ops->unregistered) - sd->internal_ops->unregistered(sd); - module_put(sd->owner); - return err; - } - -#if defined(CONFIG_MEDIA_CONTROLLER) - /* Register the entity. */ - if (v4l2_dev->mdev) { - err = media_device_register_entity(v4l2_dev->mdev, entity); - if (err < 0) { - if (sd->internal_ops && sd->internal_ops->unregistered) - sd->internal_ops->unregistered(sd); - module_put(sd->owner); - return err; - } - } -#endif - - spin_lock(&v4l2_dev->lock); - list_add_tail(&sd->list, &v4l2_dev->subdevs); - spin_unlock(&v4l2_dev->lock); - - return 0; -} -EXPORT_SYMBOL_GPL(v4l2_device_register_subdev); - -static void v4l2_device_release_subdev_node(struct video_device *vdev) -{ - struct v4l2_subdev *sd = video_get_drvdata(vdev); - sd->devnode = NULL; - kfree(vdev); -} - -int v4l2_device_register_subdev_nodes(struct v4l2_device *v4l2_dev) -{ - struct video_device *vdev; - struct v4l2_subdev *sd; - int err; - - /* Register a device node for every subdev marked with the - * V4L2_SUBDEV_FL_HAS_DEVNODE flag. - */ - list_for_each_entry(sd, &v4l2_dev->subdevs, list) { - if (!(sd->flags & V4L2_SUBDEV_FL_HAS_DEVNODE)) - continue; - - vdev = kzalloc(sizeof(*vdev), GFP_KERNEL); - if (!vdev) { - err = -ENOMEM; - goto clean_up; - } - - video_set_drvdata(vdev, sd); - strlcpy(vdev->name, sd->name, sizeof(vdev->name)); - vdev->v4l2_dev = v4l2_dev; - vdev->fops = &v4l2_subdev_fops; - vdev->release = v4l2_device_release_subdev_node; - vdev->ctrl_handler = sd->ctrl_handler; - err = __video_register_device(vdev, VFL_TYPE_SUBDEV, -1, 1, - sd->owner); - if (err < 0) { - kfree(vdev); - goto clean_up; - } -#if defined(CONFIG_MEDIA_CONTROLLER) - sd->entity.info.v4l.major = VIDEO_MAJOR; - sd->entity.info.v4l.minor = vdev->minor; -#endif - sd->devnode = vdev; - } - return 0; - -clean_up: - list_for_each_entry(sd, &v4l2_dev->subdevs, list) { - if (!sd->devnode) - break; - video_unregister_device(sd->devnode); - } - - return err; -} -EXPORT_SYMBOL_GPL(v4l2_device_register_subdev_nodes); - -void v4l2_device_unregister_subdev(struct v4l2_subdev *sd) -{ - struct v4l2_device *v4l2_dev; - - /* return if it isn't registered */ - if (sd == NULL || sd->v4l2_dev == NULL) - return; - - v4l2_dev = sd->v4l2_dev; - - spin_lock(&v4l2_dev->lock); - list_del(&sd->list); - spin_unlock(&v4l2_dev->lock); - - if (sd->internal_ops && sd->internal_ops->unregistered) - sd->internal_ops->unregistered(sd); - sd->v4l2_dev = NULL; - -#if defined(CONFIG_MEDIA_CONTROLLER) - if (v4l2_dev->mdev) - media_device_unregister_entity(&sd->entity); -#endif - video_unregister_device(sd->devnode); - module_put(sd->owner); -} -EXPORT_SYMBOL_GPL(v4l2_device_unregister_subdev); diff --git a/drivers/media/video/v4l2-event.c b/drivers/media/video/v4l2-event.c deleted file mode 100644 index ef2a33c94045..000000000000 --- a/drivers/media/video/v4l2-event.c +++ /dev/null @@ -1,313 +0,0 @@ -/* - * v4l2-event.c - * - * V4L2 events. - * - * Copyright (C) 2009--2010 Nokia Corporation. - * - * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * 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., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - */ - -#include <media/v4l2-dev.h> -#include <media/v4l2-fh.h> -#include <media/v4l2-event.h> - -#include <linux/sched.h> -#include <linux/slab.h> -#include <linux/export.h> - -static unsigned sev_pos(const struct v4l2_subscribed_event *sev, unsigned idx) -{ - idx += sev->first; - return idx >= sev->elems ? idx - sev->elems : idx; -} - -static int __v4l2_event_dequeue(struct v4l2_fh *fh, struct v4l2_event *event) -{ - struct v4l2_kevent *kev; - unsigned long flags; - - spin_lock_irqsave(&fh->vdev->fh_lock, flags); - - if (list_empty(&fh->available)) { - spin_unlock_irqrestore(&fh->vdev->fh_lock, flags); - return -ENOENT; - } - - WARN_ON(fh->navailable == 0); - - kev = list_first_entry(&fh->available, struct v4l2_kevent, list); - list_del(&kev->list); - fh->navailable--; - - kev->event.pending = fh->navailable; - *event = kev->event; - kev->sev->first = sev_pos(kev->sev, 1); - kev->sev->in_use--; - - spin_unlock_irqrestore(&fh->vdev->fh_lock, flags); - - return 0; -} - -int v4l2_event_dequeue(struct v4l2_fh *fh, struct v4l2_event *event, - int nonblocking) -{ - int ret; - - if (nonblocking) - return __v4l2_event_dequeue(fh, event); - - /* Release the vdev lock while waiting */ - if (fh->vdev->lock) - mutex_unlock(fh->vdev->lock); - - do { - ret = wait_event_interruptible(fh->wait, - fh->navailable != 0); - if (ret < 0) - break; - - ret = __v4l2_event_dequeue(fh, event); - } while (ret == -ENOENT); - - if (fh->vdev->lock) - mutex_lock(fh->vdev->lock); - - return ret; -} -EXPORT_SYMBOL_GPL(v4l2_event_dequeue); - -/* Caller must hold fh->vdev->fh_lock! */ -static struct v4l2_subscribed_event *v4l2_event_subscribed( - struct v4l2_fh *fh, u32 type, u32 id) -{ - struct v4l2_subscribed_event *sev; - - assert_spin_locked(&fh->vdev->fh_lock); - - list_for_each_entry(sev, &fh->subscribed, list) - if (sev->type == type && sev->id == id) - return sev; - - return NULL; -} - -static void __v4l2_event_queue_fh(struct v4l2_fh *fh, const struct v4l2_event *ev, - const struct timespec *ts) -{ - struct v4l2_subscribed_event *sev; - struct v4l2_kevent *kev; - bool copy_payload = true; - - /* Are we subscribed? */ - sev = v4l2_event_subscribed(fh, ev->type, ev->id); - if (sev == NULL) - return; - - /* - * If the event has been added to the fh->subscribed list, but its - * add op has not completed yet elems will be 0, treat this as - * not being subscribed. - */ - if (!sev->elems) - return; - - /* Increase event sequence number on fh. */ - fh->sequence++; - - /* Do we have any free events? */ - if (sev->in_use == sev->elems) { - /* no, remove the oldest one */ - kev = sev->events + sev_pos(sev, 0); - list_del(&kev->list); - sev->in_use--; - sev->first = sev_pos(sev, 1); - fh->navailable--; - if (sev->elems == 1) { - if (sev->ops && sev->ops->replace) { - sev->ops->replace(&kev->event, ev); - copy_payload = false; - } - } else if (sev->ops && sev->ops->merge) { - struct v4l2_kevent *second_oldest = - sev->events + sev_pos(sev, 0); - sev->ops->merge(&kev->event, &second_oldest->event); - } - } - - /* Take one and fill it. */ - kev = sev->events + sev_pos(sev, sev->in_use); - kev->event.type = ev->type; - if (copy_payload) - kev->event.u = ev->u; - kev->event.id = ev->id; - kev->event.timestamp = *ts; - kev->event.sequence = fh->sequence; - sev->in_use++; - list_add_tail(&kev->list, &fh->available); - - fh->navailable++; - - wake_up_all(&fh->wait); -} - -void v4l2_event_queue(struct video_device *vdev, const struct v4l2_event *ev) -{ - struct v4l2_fh *fh; - unsigned long flags; - struct timespec timestamp; - - ktime_get_ts(×tamp); - - spin_lock_irqsave(&vdev->fh_lock, flags); - - list_for_each_entry(fh, &vdev->fh_list, list) - __v4l2_event_queue_fh(fh, ev, ×tamp); - - spin_unlock_irqrestore(&vdev->fh_lock, flags); -} -EXPORT_SYMBOL_GPL(v4l2_event_queue); - -void v4l2_event_queue_fh(struct v4l2_fh *fh, const struct v4l2_event *ev) -{ - unsigned long flags; - struct timespec timestamp; - - ktime_get_ts(×tamp); - - spin_lock_irqsave(&fh->vdev->fh_lock, flags); - __v4l2_event_queue_fh(fh, ev, ×tamp); - spin_unlock_irqrestore(&fh->vdev->fh_lock, flags); -} -EXPORT_SYMBOL_GPL(v4l2_event_queue_fh); - -int v4l2_event_pending(struct v4l2_fh *fh) -{ - return fh->navailable; -} -EXPORT_SYMBOL_GPL(v4l2_event_pending); - -int v4l2_event_subscribe(struct v4l2_fh *fh, - struct v4l2_event_subscription *sub, unsigned elems, - const struct v4l2_subscribed_event_ops *ops) -{ - struct v4l2_subscribed_event *sev, *found_ev; - unsigned long flags; - unsigned i; - - if (sub->type == V4L2_EVENT_ALL) - return -EINVAL; - - if (elems < 1) - elems = 1; - - sev = kzalloc(sizeof(*sev) + sizeof(struct v4l2_kevent) * elems, GFP_KERNEL); - if (!sev) - return -ENOMEM; - for (i = 0; i < elems; i++) - sev->events[i].sev = sev; - sev->type = sub->type; - sev->id = sub->id; - sev->flags = sub->flags; - sev->fh = fh; - sev->ops = ops; - - spin_lock_irqsave(&fh->vdev->fh_lock, flags); - found_ev = v4l2_event_subscribed(fh, sub->type, sub->id); - if (!found_ev) - list_add(&sev->list, &fh->subscribed); - spin_unlock_irqrestore(&fh->vdev->fh_lock, flags); - - if (found_ev) { - kfree(sev); - return 0; /* Already listening */ - } - - if (sev->ops && sev->ops->add) { - int ret = sev->ops->add(sev, elems); - if (ret) { - sev->ops = NULL; - v4l2_event_unsubscribe(fh, sub); - return ret; - } - } - - /* Mark as ready for use */ - sev->elems = elems; - - return 0; -} -EXPORT_SYMBOL_GPL(v4l2_event_subscribe); - -void v4l2_event_unsubscribe_all(struct v4l2_fh *fh) -{ - struct v4l2_event_subscription sub; - struct v4l2_subscribed_event *sev; - unsigned long flags; - - do { - sev = NULL; - - spin_lock_irqsave(&fh->vdev->fh_lock, flags); - if (!list_empty(&fh->subscribed)) { - sev = list_first_entry(&fh->subscribed, - struct v4l2_subscribed_event, list); - sub.type = sev->type; - sub.id = sev->id; - } - spin_unlock_irqrestore(&fh->vdev->fh_lock, flags); - if (sev) - v4l2_event_unsubscribe(fh, &sub); - } while (sev); -} -EXPORT_SYMBOL_GPL(v4l2_event_unsubscribe_all); - -int v4l2_event_unsubscribe(struct v4l2_fh *fh, - struct v4l2_event_subscription *sub) -{ - struct v4l2_subscribed_event *sev; - unsigned long flags; - int i; - - if (sub->type == V4L2_EVENT_ALL) { - v4l2_event_unsubscribe_all(fh); - return 0; - } - - spin_lock_irqsave(&fh->vdev->fh_lock, flags); - - sev = v4l2_event_subscribed(fh, sub->type, sub->id); - if (sev != NULL) { - /* Remove any pending events for this subscription */ - for (i = 0; i < sev->in_use; i++) { - list_del(&sev->events[sev_pos(sev, i)].list); - fh->navailable--; - } - list_del(&sev->list); - } - - spin_unlock_irqrestore(&fh->vdev->fh_lock, flags); - - if (sev && sev->ops && sev->ops->del) - sev->ops->del(sev); - - kfree(sev); - - return 0; -} -EXPORT_SYMBOL_GPL(v4l2_event_unsubscribe); diff --git a/drivers/media/video/v4l2-fh.c b/drivers/media/video/v4l2-fh.c deleted file mode 100644 index 9e3fc040ea20..000000000000 --- a/drivers/media/video/v4l2-fh.c +++ /dev/null @@ -1,120 +0,0 @@ -/* - * v4l2-fh.c - * - * V4L2 file handles. - * - * Copyright (C) 2009--2010 Nokia Corporation. - * - * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * 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., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - */ - -#include <linux/bitops.h> -#include <linux/slab.h> -#include <linux/export.h> -#include <media/v4l2-dev.h> -#include <media/v4l2-fh.h> -#include <media/v4l2-event.h> -#include <media/v4l2-ioctl.h> - -void v4l2_fh_init(struct v4l2_fh *fh, struct video_device *vdev) -{ - fh->vdev = vdev; - /* Inherit from video_device. May be overridden by the driver. */ - fh->ctrl_handler = vdev->ctrl_handler; - INIT_LIST_HEAD(&fh->list); - set_bit(V4L2_FL_USES_V4L2_FH, &fh->vdev->flags); - fh->prio = V4L2_PRIORITY_UNSET; - init_waitqueue_head(&fh->wait); - INIT_LIST_HEAD(&fh->available); - INIT_LIST_HEAD(&fh->subscribed); - fh->sequence = -1; -} -EXPORT_SYMBOL_GPL(v4l2_fh_init); - -void v4l2_fh_add(struct v4l2_fh *fh) -{ - unsigned long flags; - - if (test_bit(V4L2_FL_USE_FH_PRIO, &fh->vdev->flags)) - v4l2_prio_open(fh->vdev->prio, &fh->prio); - spin_lock_irqsave(&fh->vdev->fh_lock, flags); - list_add(&fh->list, &fh->vdev->fh_list); - spin_unlock_irqrestore(&fh->vdev->fh_lock, flags); -} -EXPORT_SYMBOL_GPL(v4l2_fh_add); - -int v4l2_fh_open(struct file *filp) -{ - struct video_device *vdev = video_devdata(filp); - struct v4l2_fh *fh = kzalloc(sizeof(*fh), GFP_KERNEL); - - filp->private_data = fh; - if (fh == NULL) - return -ENOMEM; - v4l2_fh_init(fh, vdev); - v4l2_fh_add(fh); - return 0; -} -EXPORT_SYMBOL_GPL(v4l2_fh_open); - -void v4l2_fh_del(struct v4l2_fh *fh) -{ - unsigned long flags; - - spin_lock_irqsave(&fh->vdev->fh_lock, flags); - list_del_init(&fh->list); - spin_unlock_irqrestore(&fh->vdev->fh_lock, flags); - if (test_bit(V4L2_FL_USE_FH_PRIO, &fh->vdev->flags)) - v4l2_prio_close(fh->vdev->prio, fh->prio); -} -EXPORT_SYMBOL_GPL(v4l2_fh_del); - -void v4l2_fh_exit(struct v4l2_fh *fh) -{ - if (fh->vdev == NULL) - return; - v4l2_event_unsubscribe_all(fh); - fh->vdev = NULL; -} -EXPORT_SYMBOL_GPL(v4l2_fh_exit); - -int v4l2_fh_release(struct file *filp) -{ - struct v4l2_fh *fh = filp->private_data; - - if (fh) { - v4l2_fh_del(fh); - v4l2_fh_exit(fh); - kfree(fh); - } - return 0; -} -EXPORT_SYMBOL_GPL(v4l2_fh_release); - -int v4l2_fh_is_singular(struct v4l2_fh *fh) -{ - unsigned long flags; - int is_singular; - - if (fh == NULL || fh->vdev == NULL) - return 0; - spin_lock_irqsave(&fh->vdev->fh_lock, flags); - is_singular = list_is_singular(&fh->list); - spin_unlock_irqrestore(&fh->vdev->fh_lock, flags); - return is_singular; -} -EXPORT_SYMBOL_GPL(v4l2_fh_is_singular); diff --git a/drivers/media/video/v4l2-int-device.c b/drivers/media/video/v4l2-int-device.c deleted file mode 100644 index f4473494af7a..000000000000 --- a/drivers/media/video/v4l2-int-device.c +++ /dev/null @@ -1,164 +0,0 @@ -/* - * drivers/media/video/v4l2-int-device.c - * - * V4L2 internal ioctl interface. - * - * Copyright (C) 2007 Nokia Corporation. - * - * Contact: Sakari Ailus <sakari.ailus@nokia.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * 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., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - */ - -#include <linux/kernel.h> -#include <linux/list.h> -#include <linux/sort.h> -#include <linux/string.h> -#include <linux/module.h> - -#include <media/v4l2-int-device.h> - -static DEFINE_MUTEX(mutex); -static LIST_HEAD(int_list); - -void v4l2_int_device_try_attach_all(void) -{ - struct v4l2_int_device *m, *s; - - list_for_each_entry(m, &int_list, head) { - if (m->type != v4l2_int_type_master) - continue; - - list_for_each_entry(s, &int_list, head) { - if (s->type != v4l2_int_type_slave) - continue; - - /* Slave is connected? */ - if (s->u.slave->master) - continue; - - /* Slave wants to attach to master? */ - if (s->u.slave->attach_to[0] != 0 - && strncmp(m->name, s->u.slave->attach_to, - V4L2NAMESIZE)) - continue; - - if (!try_module_get(m->module)) - continue; - - s->u.slave->master = m; - if (m->u.master->attach(s)) { - s->u.slave->master = NULL; - module_put(m->module); - continue; - } - } - } -} -EXPORT_SYMBOL_GPL(v4l2_int_device_try_attach_all); - -static int ioctl_sort_cmp(const void *a, const void *b) -{ - const struct v4l2_int_ioctl_desc *d1 = a, *d2 = b; - - if (d1->num > d2->num) - return 1; - - if (d1->num < d2->num) - return -1; - - return 0; -} - -int v4l2_int_device_register(struct v4l2_int_device *d) -{ - if (d->type == v4l2_int_type_slave) - sort(d->u.slave->ioctls, d->u.slave->num_ioctls, - sizeof(struct v4l2_int_ioctl_desc), - &ioctl_sort_cmp, NULL); - mutex_lock(&mutex); - list_add(&d->head, &int_list); - v4l2_int_device_try_attach_all(); - mutex_unlock(&mutex); - - return 0; -} -EXPORT_SYMBOL_GPL(v4l2_int_device_register); - -void v4l2_int_device_unregister(struct v4l2_int_device *d) -{ - mutex_lock(&mutex); - list_del(&d->head); - if (d->type == v4l2_int_type_slave - && d->u.slave->master != NULL) { - d->u.slave->master->u.master->detach(d); - module_put(d->u.slave->master->module); - d->u.slave->master = NULL; - } - mutex_unlock(&mutex); -} -EXPORT_SYMBOL_GPL(v4l2_int_device_unregister); - -/* Adapted from search_extable in extable.c. */ -static v4l2_int_ioctl_func *find_ioctl(struct v4l2_int_slave *slave, int cmd, - v4l2_int_ioctl_func *no_such_ioctl) -{ - const struct v4l2_int_ioctl_desc *first = slave->ioctls; - const struct v4l2_int_ioctl_desc *last = - first + slave->num_ioctls - 1; - - while (first <= last) { - const struct v4l2_int_ioctl_desc *mid; - - mid = (last - first) / 2 + first; - - if (mid->num < cmd) - first = mid + 1; - else if (mid->num > cmd) - last = mid - 1; - else - return mid->func; - } - - return no_such_ioctl; -} - -static int no_such_ioctl_0(struct v4l2_int_device *d) -{ - return -ENOIOCTLCMD; -} - -int v4l2_int_ioctl_0(struct v4l2_int_device *d, int cmd) -{ - return ((v4l2_int_ioctl_func_0 *) - find_ioctl(d->u.slave, cmd, - (v4l2_int_ioctl_func *)no_such_ioctl_0))(d); -} -EXPORT_SYMBOL_GPL(v4l2_int_ioctl_0); - -static int no_such_ioctl_1(struct v4l2_int_device *d, void *arg) -{ - return -ENOIOCTLCMD; -} - -int v4l2_int_ioctl_1(struct v4l2_int_device *d, int cmd, void *arg) -{ - return ((v4l2_int_ioctl_func_1 *) - find_ioctl(d->u.slave, cmd, - (v4l2_int_ioctl_func *)no_such_ioctl_1))(d, arg); -} -EXPORT_SYMBOL_GPL(v4l2_int_ioctl_1); - -MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/v4l2-ioctl.c b/drivers/media/video/v4l2-ioctl.c deleted file mode 100644 index c3b7b5f59b32..000000000000 --- a/drivers/media/video/v4l2-ioctl.c +++ /dev/null @@ -1,2324 +0,0 @@ -/* - * Video capture interface for Linux version 2 - * - * A generic framework to process V4L2 ioctl commands. - * - * 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. - * - * Authors: Alan Cox, <alan@lxorguk.ukuu.org.uk> (version 1) - * Mauro Carvalho Chehab <mchehab@infradead.org> (version 2) - */ - -#include <linux/module.h> -#include <linux/slab.h> -#include <linux/types.h> -#include <linux/kernel.h> -#include <linux/version.h> - -#include <linux/videodev2.h> - -#include <media/v4l2-common.h> -#include <media/v4l2-ioctl.h> -#include <media/v4l2-ctrls.h> -#include <media/v4l2-fh.h> -#include <media/v4l2-event.h> -#include <media/v4l2-device.h> -#include <media/v4l2-chip-ident.h> -#include <media/videobuf2-core.h> - -/* Zero out the end of the struct pointed to by p. Everything after, but - * not including, the specified field is cleared. */ -#define CLEAR_AFTER_FIELD(p, field) \ - memset((u8 *)(p) + offsetof(typeof(*(p)), field) + sizeof((p)->field), \ - 0, sizeof(*(p)) - offsetof(typeof(*(p)), field) - sizeof((p)->field)) - -struct std_descr { - v4l2_std_id std; - const char *descr; -}; - -static const struct std_descr standards[] = { - { V4L2_STD_NTSC, "NTSC" }, - { V4L2_STD_NTSC_M, "NTSC-M" }, - { V4L2_STD_NTSC_M_JP, "NTSC-M-JP" }, - { V4L2_STD_NTSC_M_KR, "NTSC-M-KR" }, - { V4L2_STD_NTSC_443, "NTSC-443" }, - { V4L2_STD_PAL, "PAL" }, - { V4L2_STD_PAL_BG, "PAL-BG" }, - { V4L2_STD_PAL_B, "PAL-B" }, - { V4L2_STD_PAL_B1, "PAL-B1" }, - { V4L2_STD_PAL_G, "PAL-G" }, - { V4L2_STD_PAL_H, "PAL-H" }, - { V4L2_STD_PAL_I, "PAL-I" }, - { V4L2_STD_PAL_DK, "PAL-DK" }, - { V4L2_STD_PAL_D, "PAL-D" }, - { V4L2_STD_PAL_D1, "PAL-D1" }, - { V4L2_STD_PAL_K, "PAL-K" }, - { V4L2_STD_PAL_M, "PAL-M" }, - { V4L2_STD_PAL_N, "PAL-N" }, - { V4L2_STD_PAL_Nc, "PAL-Nc" }, - { V4L2_STD_PAL_60, "PAL-60" }, - { V4L2_STD_SECAM, "SECAM" }, - { V4L2_STD_SECAM_B, "SECAM-B" }, - { V4L2_STD_SECAM_G, "SECAM-G" }, - { V4L2_STD_SECAM_H, "SECAM-H" }, - { V4L2_STD_SECAM_DK, "SECAM-DK" }, - { V4L2_STD_SECAM_D, "SECAM-D" }, - { V4L2_STD_SECAM_K, "SECAM-K" }, - { V4L2_STD_SECAM_K1, "SECAM-K1" }, - { V4L2_STD_SECAM_L, "SECAM-L" }, - { V4L2_STD_SECAM_LC, "SECAM-Lc" }, - { 0, "Unknown" } -}; - -/* video4linux standard ID conversion to standard name - */ -const char *v4l2_norm_to_name(v4l2_std_id id) -{ - u32 myid = id; - int i; - - /* HACK: ppc32 architecture doesn't have __ucmpdi2 function to handle - 64 bit comparations. So, on that architecture, with some gcc - variants, compilation fails. Currently, the max value is 30bit wide. - */ - BUG_ON(myid != id); - - for (i = 0; standards[i].std; i++) - if (myid == standards[i].std) - break; - return standards[i].descr; -} -EXPORT_SYMBOL(v4l2_norm_to_name); - -/* Returns frame period for the given standard */ -void v4l2_video_std_frame_period(int id, struct v4l2_fract *frameperiod) -{ - if (id & V4L2_STD_525_60) { - frameperiod->numerator = 1001; - frameperiod->denominator = 30000; - } else { - frameperiod->numerator = 1; - frameperiod->denominator = 25; - } -} -EXPORT_SYMBOL(v4l2_video_std_frame_period); - -/* Fill in the fields of a v4l2_standard structure according to the - 'id' and 'transmission' parameters. Returns negative on error. */ -int v4l2_video_std_construct(struct v4l2_standard *vs, - int id, const char *name) -{ - vs->id = id; - v4l2_video_std_frame_period(id, &vs->frameperiod); - vs->framelines = (id & V4L2_STD_525_60) ? 525 : 625; - strlcpy(vs->name, name, sizeof(vs->name)); - return 0; -} -EXPORT_SYMBOL(v4l2_video_std_construct); - -/* ----------------------------------------------------------------- */ -/* some arrays for pretty-printing debug messages of enum types */ - -const char *v4l2_field_names[] = { - [V4L2_FIELD_ANY] = "any", - [V4L2_FIELD_NONE] = "none", - [V4L2_FIELD_TOP] = "top", - [V4L2_FIELD_BOTTOM] = "bottom", - [V4L2_FIELD_INTERLACED] = "interlaced", - [V4L2_FIELD_SEQ_TB] = "seq-tb", - [V4L2_FIELD_SEQ_BT] = "seq-bt", - [V4L2_FIELD_ALTERNATE] = "alternate", - [V4L2_FIELD_INTERLACED_TB] = "interlaced-tb", - [V4L2_FIELD_INTERLACED_BT] = "interlaced-bt", -}; -EXPORT_SYMBOL(v4l2_field_names); - -const char *v4l2_type_names[] = { - [V4L2_BUF_TYPE_VIDEO_CAPTURE] = "vid-cap", - [V4L2_BUF_TYPE_VIDEO_OVERLAY] = "vid-overlay", - [V4L2_BUF_TYPE_VIDEO_OUTPUT] = "vid-out", - [V4L2_BUF_TYPE_VBI_CAPTURE] = "vbi-cap", - [V4L2_BUF_TYPE_VBI_OUTPUT] = "vbi-out", - [V4L2_BUF_TYPE_SLICED_VBI_CAPTURE] = "sliced-vbi-cap", - [V4L2_BUF_TYPE_SLICED_VBI_OUTPUT] = "sliced-vbi-out", - [V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY] = "vid-out-overlay", - [V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE] = "vid-cap-mplane", - [V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE] = "vid-out-mplane", -}; -EXPORT_SYMBOL(v4l2_type_names); - -static const char *v4l2_memory_names[] = { - [V4L2_MEMORY_MMAP] = "mmap", - [V4L2_MEMORY_USERPTR] = "userptr", - [V4L2_MEMORY_OVERLAY] = "overlay", -}; - -#define prt_names(a, arr) ((((a) >= 0) && ((a) < ARRAY_SIZE(arr))) ? \ - arr[a] : "unknown") - -/* ------------------------------------------------------------------ */ -/* debug help functions */ - -static void v4l_print_querycap(const void *arg, bool write_only) -{ - const struct v4l2_capability *p = arg; - - pr_cont("driver=%s, card=%s, bus=%s, version=0x%08x, " - "capabilities=0x%08x, device_caps=0x%08x\n", - p->driver, p->card, p->bus_info, - p->version, p->capabilities, p->device_caps); -} - -static void v4l_print_enuminput(const void *arg, bool write_only) -{ - const struct v4l2_input *p = arg; - - pr_cont("index=%u, name=%s, type=%u, audioset=0x%x, tuner=%u, " - "std=0x%08Lx, status=0x%x, capabilities=0x%x\n", - p->index, p->name, p->type, p->audioset, p->tuner, - (unsigned long long)p->std, p->status, p->capabilities); -} - -static void v4l_print_enumoutput(const void *arg, bool write_only) -{ - const struct v4l2_output *p = arg; - - pr_cont("index=%u, name=%s, type=%u, audioset=0x%x, " - "modulator=%u, std=0x%08Lx, capabilities=0x%x\n", - p->index, p->name, p->type, p->audioset, p->modulator, - (unsigned long long)p->std, p->capabilities); -} - -static void v4l_print_audio(const void *arg, bool write_only) -{ - const struct v4l2_audio *p = arg; - - if (write_only) - pr_cont("index=%u, mode=0x%x\n", p->index, p->mode); - else - pr_cont("index=%u, name=%s, capability=0x%x, mode=0x%x\n", - p->index, p->name, p->capability, p->mode); -} - -static void v4l_print_audioout(const void *arg, bool write_only) -{ - const struct v4l2_audioout *p = arg; - - if (write_only) - pr_cont("index=%u\n", p->index); - else - pr_cont("index=%u, name=%s, capability=0x%x, mode=0x%x\n", - p->index, p->name, p->capability, p->mode); -} - -static void v4l_print_fmtdesc(const void *arg, bool write_only) -{ - const struct v4l2_fmtdesc *p = arg; - - pr_cont("index=%u, type=%s, flags=0x%x, pixelformat=%c%c%c%c, description='%s'\n", - p->index, prt_names(p->type, v4l2_type_names), - p->flags, (p->pixelformat & 0xff), - (p->pixelformat >> 8) & 0xff, - (p->pixelformat >> 16) & 0xff, - (p->pixelformat >> 24) & 0xff, - p->description); -} - -static void v4l_print_format(const void *arg, bool write_only) -{ - const struct v4l2_format *p = arg; - const struct v4l2_pix_format *pix; - const struct v4l2_pix_format_mplane *mp; - const struct v4l2_vbi_format *vbi; - const struct v4l2_sliced_vbi_format *sliced; - const struct v4l2_window *win; - const struct v4l2_clip *clip; - unsigned i; - - pr_cont("type=%s", prt_names(p->type, v4l2_type_names)); - switch (p->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - case V4L2_BUF_TYPE_VIDEO_OUTPUT: - pix = &p->fmt.pix; - pr_cont(", width=%u, height=%u, " - "pixelformat=%c%c%c%c, field=%s, " - "bytesperline=%u sizeimage=%u, colorspace=%d\n", - pix->width, pix->height, - (pix->pixelformat & 0xff), - (pix->pixelformat >> 8) & 0xff, - (pix->pixelformat >> 16) & 0xff, - (pix->pixelformat >> 24) & 0xff, - prt_names(pix->field, v4l2_field_names), - pix->bytesperline, pix->sizeimage, - pix->colorspace); - break; - case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: - case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: - mp = &p->fmt.pix_mp; - pr_cont(", width=%u, height=%u, " - "format=%c%c%c%c, field=%s, " - "colorspace=%d, num_planes=%u\n", - mp->width, mp->height, - (mp->pixelformat & 0xff), - (mp->pixelformat >> 8) & 0xff, - (mp->pixelformat >> 16) & 0xff, - (mp->pixelformat >> 24) & 0xff, - prt_names(mp->field, v4l2_field_names), - mp->colorspace, mp->num_planes); - for (i = 0; i < mp->num_planes; i++) - printk(KERN_DEBUG "plane %u: bytesperline=%u sizeimage=%u\n", i, - mp->plane_fmt[i].bytesperline, - mp->plane_fmt[i].sizeimage); - break; - case V4L2_BUF_TYPE_VIDEO_OVERLAY: - case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: - win = &p->fmt.win; - pr_cont(", wxh=%dx%d, x,y=%d,%d, field=%s, " - "chromakey=0x%08x, bitmap=%p, " - "global_alpha=0x%02x\n", - win->w.width, win->w.height, - win->w.left, win->w.top, - prt_names(win->field, v4l2_field_names), - win->chromakey, win->bitmap, win->global_alpha); - clip = win->clips; - for (i = 0; i < win->clipcount; i++) { - printk(KERN_DEBUG "clip %u: wxh=%dx%d, x,y=%d,%d\n", - i, clip->c.width, clip->c.height, - clip->c.left, clip->c.top); - clip = clip->next; - } - break; - case V4L2_BUF_TYPE_VBI_CAPTURE: - case V4L2_BUF_TYPE_VBI_OUTPUT: - vbi = &p->fmt.vbi; - pr_cont(", sampling_rate=%u, offset=%u, samples_per_line=%u, " - "sample_format=%c%c%c%c, start=%u,%u, count=%u,%u\n", - vbi->sampling_rate, vbi->offset, - vbi->samples_per_line, - (vbi->sample_format & 0xff), - (vbi->sample_format >> 8) & 0xff, - (vbi->sample_format >> 16) & 0xff, - (vbi->sample_format >> 24) & 0xff, - vbi->start[0], vbi->start[1], - vbi->count[0], vbi->count[1]); - break; - case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: - case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: - sliced = &p->fmt.sliced; - pr_cont(", service_set=0x%08x, io_size=%d\n", - sliced->service_set, sliced->io_size); - for (i = 0; i < 24; i++) - printk(KERN_DEBUG "line[%02u]=0x%04x, 0x%04x\n", i, - sliced->service_lines[0][i], - sliced->service_lines[1][i]); - break; - case V4L2_BUF_TYPE_PRIVATE: - pr_cont("\n"); - break; - } -} - -static void v4l_print_framebuffer(const void *arg, bool write_only) -{ - const struct v4l2_framebuffer *p = arg; - - pr_cont("capability=0x%x, flags=0x%x, base=0x%p, width=%u, " - "height=%u, pixelformat=%c%c%c%c, " - "bytesperline=%u sizeimage=%u, colorspace=%d\n", - p->capability, p->flags, p->base, - p->fmt.width, p->fmt.height, - (p->fmt.pixelformat & 0xff), - (p->fmt.pixelformat >> 8) & 0xff, - (p->fmt.pixelformat >> 16) & 0xff, - (p->fmt.pixelformat >> 24) & 0xff, - p->fmt.bytesperline, p->fmt.sizeimage, - p->fmt.colorspace); -} - -static void v4l_print_buftype(const void *arg, bool write_only) -{ - pr_cont("type=%s\n", prt_names(*(u32 *)arg, v4l2_type_names)); -} - -static void v4l_print_modulator(const void *arg, bool write_only) -{ - const struct v4l2_modulator *p = arg; - - if (write_only) - pr_cont("index=%u, txsubchans=0x%x", p->index, p->txsubchans); - else - pr_cont("index=%u, name=%s, capability=0x%x, " - "rangelow=%u, rangehigh=%u, txsubchans=0x%x\n", - p->index, p->name, p->capability, - p->rangelow, p->rangehigh, p->txsubchans); -} - -static void v4l_print_tuner(const void *arg, bool write_only) -{ - const struct v4l2_tuner *p = arg; - - if (write_only) - pr_cont("index=%u, audmode=%u\n", p->index, p->audmode); - else - pr_cont("index=%u, name=%s, type=%u, capability=0x%x, " - "rangelow=%u, rangehigh=%u, signal=%u, afc=%d, " - "rxsubchans=0x%x, audmode=%u\n", - p->index, p->name, p->type, - p->capability, p->rangelow, - p->rangehigh, p->signal, p->afc, - p->rxsubchans, p->audmode); -} - -static void v4l_print_frequency(const void *arg, bool write_only) -{ - const struct v4l2_frequency *p = arg; - - pr_cont("tuner=%u, type=%u, frequency=%u\n", - p->tuner, p->type, p->frequency); -} - -static void v4l_print_standard(const void *arg, bool write_only) -{ - const struct v4l2_standard *p = arg; - - pr_cont("index=%u, id=0x%Lx, name=%s, fps=%u/%u, " - "framelines=%u\n", p->index, - (unsigned long long)p->id, p->name, - p->frameperiod.numerator, - p->frameperiod.denominator, - p->framelines); -} - -static void v4l_print_std(const void *arg, bool write_only) -{ - pr_cont("std=0x%08Lx\n", *(const long long unsigned *)arg); -} - -static void v4l_print_hw_freq_seek(const void *arg, bool write_only) -{ - const struct v4l2_hw_freq_seek *p = arg; - - pr_cont("tuner=%u, type=%u, seek_upward=%u, wrap_around=%u, spacing=%u\n", - p->tuner, p->type, p->seek_upward, p->wrap_around, p->spacing); -} - -static void v4l_print_requestbuffers(const void *arg, bool write_only) -{ - const struct v4l2_requestbuffers *p = arg; - - pr_cont("count=%d, type=%s, memory=%s\n", - p->count, - prt_names(p->type, v4l2_type_names), - prt_names(p->memory, v4l2_memory_names)); -} - -static void v4l_print_buffer(const void *arg, bool write_only) -{ - const struct v4l2_buffer *p = arg; - const struct v4l2_timecode *tc = &p->timecode; - const struct v4l2_plane *plane; - int i; - - pr_cont("%02ld:%02d:%02d.%08ld index=%d, type=%s, " - "flags=0x%08x, field=%s, sequence=%d, memory=%s", - p->timestamp.tv_sec / 3600, - (int)(p->timestamp.tv_sec / 60) % 60, - (int)(p->timestamp.tv_sec % 60), - (long)p->timestamp.tv_usec, - p->index, - prt_names(p->type, v4l2_type_names), - p->flags, prt_names(p->field, v4l2_field_names), - p->sequence, prt_names(p->memory, v4l2_memory_names)); - - if (V4L2_TYPE_IS_MULTIPLANAR(p->type) && p->m.planes) { - pr_cont("\n"); - for (i = 0; i < p->length; ++i) { - plane = &p->m.planes[i]; - printk(KERN_DEBUG - "plane %d: bytesused=%d, data_offset=0x%08x " - "offset/userptr=0x%lx, length=%d\n", - i, plane->bytesused, plane->data_offset, - plane->m.userptr, plane->length); - } - } else { - pr_cont("bytesused=%d, offset/userptr=0x%lx, length=%d\n", - p->bytesused, p->m.userptr, p->length); - } - - printk(KERN_DEBUG "timecode=%02d:%02d:%02d type=%d, " - "flags=0x%08x, frames=%d, userbits=0x%08x\n", - tc->hours, tc->minutes, tc->seconds, - tc->type, tc->flags, tc->frames, *(__u32 *)tc->userbits); -} - -static void v4l_print_create_buffers(const void *arg, bool write_only) -{ - const struct v4l2_create_buffers *p = arg; - - pr_cont("index=%d, count=%d, memory=%s, ", - p->index, p->count, - prt_names(p->memory, v4l2_memory_names)); - v4l_print_format(&p->format, write_only); -} - -static void v4l_print_streamparm(const void *arg, bool write_only) -{ - const struct v4l2_streamparm *p = arg; - - pr_cont("type=%s", prt_names(p->type, v4l2_type_names)); - - if (p->type == V4L2_BUF_TYPE_VIDEO_CAPTURE || - p->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { - const struct v4l2_captureparm *c = &p->parm.capture; - - pr_cont(", capability=0x%x, capturemode=0x%x, timeperframe=%d/%d, " - "extendedmode=%d, readbuffers=%d\n", - c->capability, c->capturemode, - c->timeperframe.numerator, c->timeperframe.denominator, - c->extendedmode, c->readbuffers); - } else if (p->type == V4L2_BUF_TYPE_VIDEO_OUTPUT || - p->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { - const struct v4l2_outputparm *c = &p->parm.output; - - pr_cont(", capability=0x%x, outputmode=0x%x, timeperframe=%d/%d, " - "extendedmode=%d, writebuffers=%d\n", - c->capability, c->outputmode, - c->timeperframe.numerator, c->timeperframe.denominator, - c->extendedmode, c->writebuffers); - } -} - -static void v4l_print_queryctrl(const void *arg, bool write_only) -{ - const struct v4l2_queryctrl *p = arg; - - pr_cont("id=0x%x, type=%d, name=%s, min/max=%d/%d, " - "step=%d, default=%d, flags=0x%08x\n", - p->id, p->type, p->name, - p->minimum, p->maximum, - p->step, p->default_value, p->flags); -} - -static void v4l_print_querymenu(const void *arg, bool write_only) -{ - const struct v4l2_querymenu *p = arg; - - pr_cont("id=0x%x, index=%d\n", p->id, p->index); -} - -static void v4l_print_control(const void *arg, bool write_only) -{ - const struct v4l2_control *p = arg; - - pr_cont("id=0x%x, value=%d\n", p->id, p->value); -} - -static void v4l_print_ext_controls(const void *arg, bool write_only) -{ - const struct v4l2_ext_controls *p = arg; - int i; - - pr_cont("class=0x%x, count=%d, error_idx=%d", - p->ctrl_class, p->count, p->error_idx); - for (i = 0; i < p->count; i++) { - if (p->controls[i].size) - pr_cont(", id/val=0x%x/0x%x", - p->controls[i].id, p->controls[i].value); - else - pr_cont(", id/size=0x%x/%u", - p->controls[i].id, p->controls[i].size); - } - pr_cont("\n"); -} - -static void v4l_print_cropcap(const void *arg, bool write_only) -{ - const struct v4l2_cropcap *p = arg; - - pr_cont("type=%s, bounds wxh=%dx%d, x,y=%d,%d, " - "defrect wxh=%dx%d, x,y=%d,%d\n, " - "pixelaspect %d/%d\n", - prt_names(p->type, v4l2_type_names), - p->bounds.width, p->bounds.height, - p->bounds.left, p->bounds.top, - p->defrect.width, p->defrect.height, - p->defrect.left, p->defrect.top, - p->pixelaspect.numerator, p->pixelaspect.denominator); -} - -static void v4l_print_crop(const void *arg, bool write_only) -{ - const struct v4l2_crop *p = arg; - - pr_cont("type=%s, wxh=%dx%d, x,y=%d,%d\n", - prt_names(p->type, v4l2_type_names), - p->c.width, p->c.height, - p->c.left, p->c.top); -} - -static void v4l_print_selection(const void *arg, bool write_only) -{ - const struct v4l2_selection *p = arg; - - pr_cont("type=%s, target=%d, flags=0x%x, wxh=%dx%d, x,y=%d,%d\n", - prt_names(p->type, v4l2_type_names), - p->target, p->flags, - p->r.width, p->r.height, p->r.left, p->r.top); -} - -static void v4l_print_jpegcompression(const void *arg, bool write_only) -{ - const struct v4l2_jpegcompression *p = arg; - - pr_cont("quality=%d, APPn=%d, APP_len=%d, " - "COM_len=%d, jpeg_markers=0x%x\n", - p->quality, p->APPn, p->APP_len, - p->COM_len, p->jpeg_markers); -} - -static void v4l_print_enc_idx(const void *arg, bool write_only) -{ - const struct v4l2_enc_idx *p = arg; - - pr_cont("entries=%d, entries_cap=%d\n", - p->entries, p->entries_cap); -} - -static void v4l_print_encoder_cmd(const void *arg, bool write_only) -{ - const struct v4l2_encoder_cmd *p = arg; - - pr_cont("cmd=%d, flags=0x%x\n", - p->cmd, p->flags); -} - -static void v4l_print_decoder_cmd(const void *arg, bool write_only) -{ - const struct v4l2_decoder_cmd *p = arg; - - pr_cont("cmd=%d, flags=0x%x\n", p->cmd, p->flags); - - if (p->cmd == V4L2_DEC_CMD_START) - pr_info("speed=%d, format=%u\n", - p->start.speed, p->start.format); - else if (p->cmd == V4L2_DEC_CMD_STOP) - pr_info("pts=%llu\n", p->stop.pts); -} - -static void v4l_print_dbg_chip_ident(const void *arg, bool write_only) -{ - const struct v4l2_dbg_chip_ident *p = arg; - - pr_cont("type=%u, ", p->match.type); - if (p->match.type == V4L2_CHIP_MATCH_I2C_DRIVER) - pr_cont("name=%s, ", p->match.name); - else - pr_cont("addr=%u, ", p->match.addr); - pr_cont("chip_ident=%u, revision=0x%x\n", - p->ident, p->revision); -} - -static void v4l_print_dbg_register(const void *arg, bool write_only) -{ - const struct v4l2_dbg_register *p = arg; - - pr_cont("type=%u, ", p->match.type); - if (p->match.type == V4L2_CHIP_MATCH_I2C_DRIVER) - pr_cont("name=%s, ", p->match.name); - else - pr_cont("addr=%u, ", p->match.addr); - pr_cont("reg=0x%llx, val=0x%llx\n", - p->reg, p->val); -} - -static void v4l_print_dv_enum_presets(const void *arg, bool write_only) -{ - const struct v4l2_dv_enum_preset *p = arg; - - pr_cont("index=%u, preset=%u, name=%s, width=%u, height=%u\n", - p->index, p->preset, p->name, p->width, p->height); -} - -static void v4l_print_dv_preset(const void *arg, bool write_only) -{ - const struct v4l2_dv_preset *p = arg; - - pr_cont("preset=%u\n", p->preset); -} - -static void v4l_print_dv_timings(const void *arg, bool write_only) -{ - const struct v4l2_dv_timings *p = arg; - - switch (p->type) { - case V4L2_DV_BT_656_1120: - pr_cont("type=bt-656/1120, interlaced=%u, " - "pixelclock=%llu, " - "width=%u, height=%u, polarities=0x%x, " - "hfrontporch=%u, hsync=%u, " - "hbackporch=%u, vfrontporch=%u, " - "vsync=%u, vbackporch=%u, " - "il_vfrontporch=%u, il_vsync=%u, " - "il_vbackporch=%u, standards=0x%x, flags=0x%x\n", - p->bt.interlaced, p->bt.pixelclock, - p->bt.width, p->bt.height, - p->bt.polarities, p->bt.hfrontporch, - p->bt.hsync, p->bt.hbackporch, - p->bt.vfrontporch, p->bt.vsync, - p->bt.vbackporch, p->bt.il_vfrontporch, - p->bt.il_vsync, p->bt.il_vbackporch, - p->bt.standards, p->bt.flags); - break; - default: - pr_cont("type=%d\n", p->type); - break; - } -} - -static void v4l_print_enum_dv_timings(const void *arg, bool write_only) -{ - const struct v4l2_enum_dv_timings *p = arg; - - pr_cont("index=%u, ", p->index); - v4l_print_dv_timings(&p->timings, write_only); -} - -static void v4l_print_dv_timings_cap(const void *arg, bool write_only) -{ - const struct v4l2_dv_timings_cap *p = arg; - - switch (p->type) { - case V4L2_DV_BT_656_1120: - pr_cont("type=bt-656/1120, width=%u-%u, height=%u-%u, " - "pixelclock=%llu-%llu, standards=0x%x, capabilities=0x%x\n", - p->bt.min_width, p->bt.max_width, - p->bt.min_height, p->bt.max_height, - p->bt.min_pixelclock, p->bt.max_pixelclock, - p->bt.standards, p->bt.capabilities); - break; - default: - pr_cont("type=%u\n", p->type); - break; - } -} - -static void v4l_print_frmsizeenum(const void *arg, bool write_only) -{ - const struct v4l2_frmsizeenum *p = arg; - - pr_cont("index=%u, pixelformat=%c%c%c%c, type=%u", - p->index, - (p->pixel_format & 0xff), - (p->pixel_format >> 8) & 0xff, - (p->pixel_format >> 16) & 0xff, - (p->pixel_format >> 24) & 0xff, - p->type); - switch (p->type) { - case V4L2_FRMSIZE_TYPE_DISCRETE: - pr_cont(" wxh=%ux%u\n", - p->discrete.width, p->discrete.height); - break; - case V4L2_FRMSIZE_TYPE_STEPWISE: - pr_cont(" min=%ux%u, max=%ux%u, step=%ux%u\n", - p->stepwise.min_width, p->stepwise.min_height, - p->stepwise.step_width, p->stepwise.step_height, - p->stepwise.max_width, p->stepwise.max_height); - break; - case V4L2_FRMSIZE_TYPE_CONTINUOUS: - /* fall through */ - default: - pr_cont("\n"); - break; - } -} - -static void v4l_print_frmivalenum(const void *arg, bool write_only) -{ - const struct v4l2_frmivalenum *p = arg; - - pr_cont("index=%u, pixelformat=%c%c%c%c, wxh=%ux%u, type=%u", - p->index, - (p->pixel_format & 0xff), - (p->pixel_format >> 8) & 0xff, - (p->pixel_format >> 16) & 0xff, - (p->pixel_format >> 24) & 0xff, - p->width, p->height, p->type); - switch (p->type) { - case V4L2_FRMIVAL_TYPE_DISCRETE: - pr_cont(" fps=%d/%d\n", - p->discrete.numerator, - p->discrete.denominator); - break; - case V4L2_FRMIVAL_TYPE_STEPWISE: - pr_cont(" min=%d/%d, max=%d/%d, step=%d/%d\n", - p->stepwise.min.numerator, - p->stepwise.min.denominator, - p->stepwise.max.numerator, - p->stepwise.max.denominator, - p->stepwise.step.numerator, - p->stepwise.step.denominator); - break; - case V4L2_FRMIVAL_TYPE_CONTINUOUS: - /* fall through */ - default: - pr_cont("\n"); - break; - } -} - -static void v4l_print_event(const void *arg, bool write_only) -{ - const struct v4l2_event *p = arg; - const struct v4l2_event_ctrl *c; - - pr_cont("type=0x%x, pending=%u, sequence=%u, id=%u, " - "timestamp=%lu.%9.9lu\n", - p->type, p->pending, p->sequence, p->id, - p->timestamp.tv_sec, p->timestamp.tv_nsec); - switch (p->type) { - case V4L2_EVENT_VSYNC: - printk(KERN_DEBUG "field=%s\n", - prt_names(p->u.vsync.field, v4l2_field_names)); - break; - case V4L2_EVENT_CTRL: - c = &p->u.ctrl; - printk(KERN_DEBUG "changes=0x%x, type=%u, ", - c->changes, c->type); - if (c->type == V4L2_CTRL_TYPE_INTEGER64) - pr_cont("value64=%lld, ", c->value64); - else - pr_cont("value=%d, ", c->value); - pr_cont("flags=0x%x, minimum=%d, maximum=%d, step=%d," - " default_value=%d\n", - c->flags, c->minimum, c->maximum, - c->step, c->default_value); - break; - case V4L2_EVENT_FRAME_SYNC: - pr_cont("frame_sequence=%u\n", - p->u.frame_sync.frame_sequence); - break; - } -} - -static void v4l_print_event_subscription(const void *arg, bool write_only) -{ - const struct v4l2_event_subscription *p = arg; - - pr_cont("type=0x%x, id=0x%x, flags=0x%x\n", - p->type, p->id, p->flags); -} - -static void v4l_print_sliced_vbi_cap(const void *arg, bool write_only) -{ - const struct v4l2_sliced_vbi_cap *p = arg; - int i; - - pr_cont("type=%s, service_set=0x%08x\n", - prt_names(p->type, v4l2_type_names), p->service_set); - for (i = 0; i < 24; i++) - printk(KERN_DEBUG "line[%02u]=0x%04x, 0x%04x\n", i, - p->service_lines[0][i], - p->service_lines[1][i]); -} - -static void v4l_print_freq_band(const void *arg, bool write_only) -{ - const struct v4l2_frequency_band *p = arg; - - pr_cont("tuner=%u, type=%u, index=%u, capability=0x%x, " - "rangelow=%u, rangehigh=%u, modulation=0x%x\n", - p->tuner, p->type, p->index, - p->capability, p->rangelow, - p->rangehigh, p->modulation); -} - -static void v4l_print_u32(const void *arg, bool write_only) -{ - pr_cont("value=%u\n", *(const u32 *)arg); -} - -static void v4l_print_newline(const void *arg, bool write_only) -{ - pr_cont("\n"); -} - -static void v4l_print_default(const void *arg, bool write_only) -{ - pr_cont("driver-specific ioctl\n"); -} - -static int check_ext_ctrls(struct v4l2_ext_controls *c, int allow_priv) -{ - __u32 i; - - /* zero the reserved fields */ - c->reserved[0] = c->reserved[1] = 0; - for (i = 0; i < c->count; i++) - c->controls[i].reserved2[0] = 0; - - /* V4L2_CID_PRIVATE_BASE cannot be used as control class - when using extended controls. - Only when passed in through VIDIOC_G_CTRL and VIDIOC_S_CTRL - is it allowed for backwards compatibility. - */ - if (!allow_priv && c->ctrl_class == V4L2_CID_PRIVATE_BASE) - return 0; - /* Check that all controls are from the same control class. */ - for (i = 0; i < c->count; i++) { - if (V4L2_CTRL_ID2CLASS(c->controls[i].id) != c->ctrl_class) { - c->error_idx = i; - return 0; - } - } - return 1; -} - -static int check_fmt(const struct v4l2_ioctl_ops *ops, enum v4l2_buf_type type) -{ - if (ops == NULL) - return -EINVAL; - - switch (type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - if (ops->vidioc_g_fmt_vid_cap || - ops->vidioc_g_fmt_vid_cap_mplane) - return 0; - break; - case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: - if (ops->vidioc_g_fmt_vid_cap_mplane) - return 0; - break; - case V4L2_BUF_TYPE_VIDEO_OVERLAY: - if (ops->vidioc_g_fmt_vid_overlay) - return 0; - break; - case V4L2_BUF_TYPE_VIDEO_OUTPUT: - if (ops->vidioc_g_fmt_vid_out || - ops->vidioc_g_fmt_vid_out_mplane) - return 0; - break; - case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: - if (ops->vidioc_g_fmt_vid_out_mplane) - return 0; - break; - case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: - if (ops->vidioc_g_fmt_vid_out_overlay) - return 0; - break; - case V4L2_BUF_TYPE_VBI_CAPTURE: - if (ops->vidioc_g_fmt_vbi_cap) - return 0; - break; - case V4L2_BUF_TYPE_VBI_OUTPUT: - if (ops->vidioc_g_fmt_vbi_out) - return 0; - break; - case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: - if (ops->vidioc_g_fmt_sliced_vbi_cap) - return 0; - break; - case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: - if (ops->vidioc_g_fmt_sliced_vbi_out) - return 0; - break; - case V4L2_BUF_TYPE_PRIVATE: - if (ops->vidioc_g_fmt_type_private) - return 0; - break; - } - return -EINVAL; -} - -static int v4l_querycap(const struct v4l2_ioctl_ops *ops, - struct file *file, void *fh, void *arg) -{ - struct v4l2_capability *cap = (struct v4l2_capability *)arg; - - cap->version = LINUX_VERSION_CODE; - return ops->vidioc_querycap(file, fh, cap); -} - -static int v4l_s_input(const struct v4l2_ioctl_ops *ops, - struct file *file, void *fh, void *arg) -{ - return ops->vidioc_s_input(file, fh, *(unsigned int *)arg); -} - -static int v4l_s_output(const struct v4l2_ioctl_ops *ops, - struct file *file, void *fh, void *arg) -{ - return ops->vidioc_s_output(file, fh, *(unsigned int *)arg); -} - -static int v4l_g_priority(const struct v4l2_ioctl_ops *ops, - struct file *file, void *fh, void *arg) -{ - struct video_device *vfd; - u32 *p = arg; - - if (ops->vidioc_g_priority) - return ops->vidioc_g_priority(file, fh, arg); - vfd = video_devdata(file); - *p = v4l2_prio_max(&vfd->v4l2_dev->prio); - return 0; -} - -static int v4l_s_priority(const struct v4l2_ioctl_ops *ops, - struct file *file, void *fh, void *arg) -{ - struct video_device *vfd; - struct v4l2_fh *vfh; - u32 *p = arg; - - if (ops->vidioc_s_priority) - return ops->vidioc_s_priority(file, fh, *p); - vfd = video_devdata(file); - vfh = file->private_data; - return v4l2_prio_change(&vfd->v4l2_dev->prio, &vfh->prio, *p); -} - -static int v4l_enuminput(const struct v4l2_ioctl_ops *ops, - struct file *file, void *fh, void *arg) -{ - struct v4l2_input *p = arg; - - /* - * We set the flags for CAP_PRESETS, CAP_CUSTOM_TIMINGS & - * CAP_STD here based on ioctl handler provided by the - * driver. If the driver doesn't support these - * for a specific input, it must override these flags. - */ - if (ops->vidioc_s_std) - p->capabilities |= V4L2_IN_CAP_STD; - if (ops->vidioc_s_dv_preset) - p->capabilities |= V4L2_IN_CAP_PRESETS; - if (ops->vidioc_s_dv_timings) - p->capabilities |= V4L2_IN_CAP_CUSTOM_TIMINGS; - - return ops->vidioc_enum_input(file, fh, p); -} - -static int v4l_enumoutput(const struct v4l2_ioctl_ops *ops, - struct file *file, void *fh, void *arg) -{ - struct v4l2_output *p = arg; - - /* - * We set the flags for CAP_PRESETS, CAP_CUSTOM_TIMINGS & - * CAP_STD here based on ioctl handler provided by the - * driver. If the driver doesn't support these - * for a specific output, it must override these flags. - */ - if (ops->vidioc_s_std) - p->capabilities |= V4L2_OUT_CAP_STD; - if (ops->vidioc_s_dv_preset) - p->capabilities |= V4L2_OUT_CAP_PRESETS; - if (ops->vidioc_s_dv_timings) - p->capabilities |= V4L2_OUT_CAP_CUSTOM_TIMINGS; - - return ops->vidioc_enum_output(file, fh, p); -} - -static int v4l_enum_fmt(const struct v4l2_ioctl_ops *ops, - struct file *file, void *fh, void *arg) -{ - struct v4l2_fmtdesc *p = arg; - - switch (p->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - if (unlikely(!ops->vidioc_enum_fmt_vid_cap)) - break; - return ops->vidioc_enum_fmt_vid_cap(file, fh, arg); - case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: - if (unlikely(!ops->vidioc_enum_fmt_vid_cap_mplane)) - break; - return ops->vidioc_enum_fmt_vid_cap_mplane(file, fh, arg); - case V4L2_BUF_TYPE_VIDEO_OVERLAY: - if (unlikely(!ops->vidioc_enum_fmt_vid_overlay)) - break; - return ops->vidioc_enum_fmt_vid_overlay(file, fh, arg); - case V4L2_BUF_TYPE_VIDEO_OUTPUT: - if (unlikely(!ops->vidioc_enum_fmt_vid_out)) - break; - return ops->vidioc_enum_fmt_vid_out(file, fh, arg); - case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: - if (unlikely(!ops->vidioc_enum_fmt_vid_out_mplane)) - break; - return ops->vidioc_enum_fmt_vid_out_mplane(file, fh, arg); - case V4L2_BUF_TYPE_PRIVATE: - if (unlikely(!ops->vidioc_enum_fmt_type_private)) - break; - return ops->vidioc_enum_fmt_type_private(file, fh, arg); - } - return -EINVAL; -} - -static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops, - struct file *file, void *fh, void *arg) -{ - struct v4l2_format *p = arg; - - switch (p->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - if (unlikely(!ops->vidioc_g_fmt_vid_cap)) - break; - return ops->vidioc_g_fmt_vid_cap(file, fh, arg); - case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: - if (unlikely(!ops->vidioc_g_fmt_vid_cap_mplane)) - break; - return ops->vidioc_g_fmt_vid_cap_mplane(file, fh, arg); - case V4L2_BUF_TYPE_VIDEO_OVERLAY: - if (unlikely(!ops->vidioc_g_fmt_vid_overlay)) - break; - return ops->vidioc_g_fmt_vid_overlay(file, fh, arg); - case V4L2_BUF_TYPE_VIDEO_OUTPUT: - if (unlikely(!ops->vidioc_g_fmt_vid_out)) - break; - return ops->vidioc_g_fmt_vid_out(file, fh, arg); - case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: - if (unlikely(!ops->vidioc_g_fmt_vid_out_mplane)) - break; - return ops->vidioc_g_fmt_vid_out_mplane(file, fh, arg); - case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: - if (unlikely(!ops->vidioc_g_fmt_vid_out_overlay)) - break; - return ops->vidioc_g_fmt_vid_out_overlay(file, fh, arg); - case V4L2_BUF_TYPE_VBI_CAPTURE: - if (unlikely(!ops->vidioc_g_fmt_vbi_cap)) - break; - return ops->vidioc_g_fmt_vbi_cap(file, fh, arg); - case V4L2_BUF_TYPE_VBI_OUTPUT: - if (unlikely(!ops->vidioc_g_fmt_vbi_out)) - break; - return ops->vidioc_g_fmt_vbi_out(file, fh, arg); - case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: - if (unlikely(!ops->vidioc_g_fmt_sliced_vbi_cap)) - break; - return ops->vidioc_g_fmt_sliced_vbi_cap(file, fh, arg); - case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: - if (unlikely(!ops->vidioc_g_fmt_sliced_vbi_out)) - break; - return ops->vidioc_g_fmt_sliced_vbi_out(file, fh, arg); - case V4L2_BUF_TYPE_PRIVATE: - if (unlikely(!ops->vidioc_g_fmt_type_private)) - break; - return ops->vidioc_g_fmt_type_private(file, fh, arg); - } - return -EINVAL; -} - -static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops, - struct file *file, void *fh, void *arg) -{ - struct v4l2_format *p = arg; - - switch (p->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - if (unlikely(!ops->vidioc_s_fmt_vid_cap)) - break; - CLEAR_AFTER_FIELD(p, fmt.pix); - return ops->vidioc_s_fmt_vid_cap(file, fh, arg); - case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: - if (unlikely(!ops->vidioc_s_fmt_vid_cap_mplane)) - break; - CLEAR_AFTER_FIELD(p, fmt.pix_mp); - return ops->vidioc_s_fmt_vid_cap_mplane(file, fh, arg); - case V4L2_BUF_TYPE_VIDEO_OVERLAY: - if (unlikely(!ops->vidioc_s_fmt_vid_overlay)) - break; - CLEAR_AFTER_FIELD(p, fmt.win); - return ops->vidioc_s_fmt_vid_overlay(file, fh, arg); - case V4L2_BUF_TYPE_VIDEO_OUTPUT: - if (unlikely(!ops->vidioc_s_fmt_vid_out)) - break; - CLEAR_AFTER_FIELD(p, fmt.pix); - return ops->vidioc_s_fmt_vid_out(file, fh, arg); - case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: - if (unlikely(!ops->vidioc_s_fmt_vid_out_mplane)) - break; - CLEAR_AFTER_FIELD(p, fmt.pix_mp); - return ops->vidioc_s_fmt_vid_out_mplane(file, fh, arg); - case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: - if (unlikely(!ops->vidioc_s_fmt_vid_out_overlay)) - break; - CLEAR_AFTER_FIELD(p, fmt.win); - return ops->vidioc_s_fmt_vid_out_overlay(file, fh, arg); - case V4L2_BUF_TYPE_VBI_CAPTURE: - if (unlikely(!ops->vidioc_s_fmt_vbi_cap)) - break; - CLEAR_AFTER_FIELD(p, fmt.vbi); - return ops->vidioc_s_fmt_vbi_cap(file, fh, arg); - case V4L2_BUF_TYPE_VBI_OUTPUT: - if (unlikely(!ops->vidioc_s_fmt_vbi_out)) - break; - CLEAR_AFTER_FIELD(p, fmt.vbi); - return ops->vidioc_s_fmt_vbi_out(file, fh, arg); - case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: - if (unlikely(!ops->vidioc_s_fmt_sliced_vbi_cap)) - break; - CLEAR_AFTER_FIELD(p, fmt.sliced); - return ops->vidioc_s_fmt_sliced_vbi_cap(file, fh, arg); - case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: - if (unlikely(!ops->vidioc_s_fmt_sliced_vbi_out)) - break; - CLEAR_AFTER_FIELD(p, fmt.sliced); - return ops->vidioc_s_fmt_sliced_vbi_out(file, fh, arg); - case V4L2_BUF_TYPE_PRIVATE: - if (unlikely(!ops->vidioc_s_fmt_type_private)) - break; - return ops->vidioc_s_fmt_type_private(file, fh, arg); - } - return -EINVAL; -} - -static int v4l_try_fmt(const struct v4l2_ioctl_ops *ops, - struct file *file, void *fh, void *arg) -{ - struct v4l2_format *p = arg; - - switch (p->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - if (unlikely(!ops->vidioc_try_fmt_vid_cap)) - break; - CLEAR_AFTER_FIELD(p, fmt.pix); - return ops->vidioc_try_fmt_vid_cap(file, fh, arg); - case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: - if (unlikely(!ops->vidioc_try_fmt_vid_cap_mplane)) - break; - CLEAR_AFTER_FIELD(p, fmt.pix_mp); - return ops->vidioc_try_fmt_vid_cap_mplane(file, fh, arg); - case V4L2_BUF_TYPE_VIDEO_OVERLAY: - if (unlikely(!ops->vidioc_try_fmt_vid_overlay)) - break; - CLEAR_AFTER_FIELD(p, fmt.win); - return ops->vidioc_try_fmt_vid_overlay(file, fh, arg); - case V4L2_BUF_TYPE_VIDEO_OUTPUT: - if (unlikely(!ops->vidioc_try_fmt_vid_out)) - break; - CLEAR_AFTER_FIELD(p, fmt.pix); - return ops->vidioc_try_fmt_vid_out(file, fh, arg); - case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: - if (unlikely(!ops->vidioc_try_fmt_vid_out_mplane)) - break; - CLEAR_AFTER_FIELD(p, fmt.pix_mp); - return ops->vidioc_try_fmt_vid_out_mplane(file, fh, arg); - case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: - if (unlikely(!ops->vidioc_try_fmt_vid_out_overlay)) - break; - CLEAR_AFTER_FIELD(p, fmt.win); - return ops->vidioc_try_fmt_vid_out_overlay(file, fh, arg); - case V4L2_BUF_TYPE_VBI_CAPTURE: - if (unlikely(!ops->vidioc_try_fmt_vbi_cap)) - break; - CLEAR_AFTER_FIELD(p, fmt.vbi); - return ops->vidioc_try_fmt_vbi_cap(file, fh, arg); - case V4L2_BUF_TYPE_VBI_OUTPUT: - if (unlikely(!ops->vidioc_try_fmt_vbi_out)) - break; - CLEAR_AFTER_FIELD(p, fmt.vbi); - return ops->vidioc_try_fmt_vbi_out(file, fh, arg); - case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: - if (unlikely(!ops->vidioc_try_fmt_sliced_vbi_cap)) - break; - CLEAR_AFTER_FIELD(p, fmt.sliced); - return ops->vidioc_try_fmt_sliced_vbi_cap(file, fh, arg); - case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: - if (unlikely(!ops->vidioc_try_fmt_sliced_vbi_out)) - break; - CLEAR_AFTER_FIELD(p, fmt.sliced); - return ops->vidioc_try_fmt_sliced_vbi_out(file, fh, arg); - case V4L2_BUF_TYPE_PRIVATE: - if (unlikely(!ops->vidioc_try_fmt_type_private)) - break; - return ops->vidioc_try_fmt_type_private(file, fh, arg); - } - return -EINVAL; -} - -static int v4l_streamon(const struct v4l2_ioctl_ops *ops, - struct file *file, void *fh, void *arg) -{ - return ops->vidioc_streamon(file, fh, *(unsigned int *)arg); -} - -static int v4l_streamoff(const struct v4l2_ioctl_ops *ops, - struct file *file, void *fh, void *arg) -{ - return ops->vidioc_streamoff(file, fh, *(unsigned int *)arg); -} - -static int v4l_g_tuner(const struct v4l2_ioctl_ops *ops, - struct file *file, void *fh, void *arg) -{ - struct video_device *vfd = video_devdata(file); - struct v4l2_tuner *p = arg; - int err; - - p->type = (vfd->vfl_type == VFL_TYPE_RADIO) ? - V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; - err = ops->vidioc_g_tuner(file, fh, p); - if (!err) - p->capability |= V4L2_TUNER_CAP_FREQ_BANDS; - return err; -} - -static int v4l_s_tuner(const struct v4l2_ioctl_ops *ops, - struct file *file, void *fh, void *arg) -{ - struct video_device *vfd = video_devdata(file); - struct v4l2_tuner *p = arg; - - p->type = (vfd->vfl_type == VFL_TYPE_RADIO) ? - V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; - return ops->vidioc_s_tuner(file, fh, p); -} - -static int v4l_g_modulator(const struct v4l2_ioctl_ops *ops, - struct file *file, void *fh, void *arg) -{ - struct v4l2_modulator *p = arg; - int err; - - err = ops->vidioc_g_modulator(file, fh, p); - if (!err) - p->capability |= V4L2_TUNER_CAP_FREQ_BANDS; - return err; -} - -static int v4l_g_frequency(const struct v4l2_ioctl_ops *ops, - struct file *file, void *fh, void *arg) -{ - struct video_device *vfd = video_devdata(file); - struct v4l2_frequency *p = arg; - - p->type = (vfd->vfl_type == VFL_TYPE_RADIO) ? - V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; - return ops->vidioc_g_frequency(file, fh, p); -} - -static int v4l_s_frequency(const struct v4l2_ioctl_ops *ops, - struct file *file, void *fh, void *arg) -{ - struct video_device *vfd = video_devdata(file); - struct v4l2_frequency *p = arg; - enum v4l2_tuner_type type; - - type = (vfd->vfl_type == VFL_TYPE_RADIO) ? - V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; - if (p->type != type) - return -EINVAL; - return ops->vidioc_s_frequency(file, fh, p); -} - -static int v4l_enumstd(const struct v4l2_ioctl_ops *ops, - struct file *file, void *fh, void *arg) -{ - struct video_device *vfd = video_devdata(file); - struct v4l2_standard *p = arg; - v4l2_std_id id = vfd->tvnorms, curr_id = 0; - unsigned int index = p->index, i, j = 0; - const char *descr = ""; - - /* Return norm array in a canonical way */ - for (i = 0; i <= index && id; i++) { - /* last std value in the standards array is 0, so this - while always ends there since (id & 0) == 0. */ - while ((id & standards[j].std) != standards[j].std) - j++; - curr_id = standards[j].std; - descr = standards[j].descr; - j++; - if (curr_id == 0) - break; - if (curr_id != V4L2_STD_PAL && - curr_id != V4L2_STD_SECAM && - curr_id != V4L2_STD_NTSC) - id &= ~curr_id; - } - if (i <= index) - return -EINVAL; - - v4l2_video_std_construct(p, curr_id, descr); - return 0; -} - -static int v4l_g_std(const struct v4l2_ioctl_ops *ops, - struct file *file, void *fh, void *arg) -{ - struct video_device *vfd = video_devdata(file); - v4l2_std_id *id = arg; - - /* Calls the specific handler */ - if (ops->vidioc_g_std) - return ops->vidioc_g_std(file, fh, arg); - if (vfd->current_norm) { - *id = vfd->current_norm; - return 0; - } - return -ENOTTY; -} - -static int v4l_s_std(const struct v4l2_ioctl_ops *ops, - struct file *file, void *fh, void *arg) -{ - struct video_device *vfd = video_devdata(file); - v4l2_std_id *id = arg, norm; - int ret; - - norm = (*id) & vfd->tvnorms; - if (vfd->tvnorms && !norm) /* Check if std is supported */ - return -EINVAL; - - /* Calls the specific handler */ - ret = ops->vidioc_s_std(file, fh, &norm); - - /* Updates standard information */ - if (ret >= 0) - vfd->current_norm = norm; - return ret; -} - -static int v4l_querystd(const struct v4l2_ioctl_ops *ops, - struct file *file, void *fh, void *arg) -{ - struct video_device *vfd = video_devdata(file); - v4l2_std_id *p = arg; - - /* - * If nothing detected, it should return all supported - * standard. - * Drivers just need to mask the std argument, in order - * to remove the standards that don't apply from the mask. - * This means that tuners, audio and video decoders can join - * their efforts to improve the standards detection. - */ - *p = vfd->tvnorms; - return ops->vidioc_querystd(file, fh, arg); -} - -static int v4l_s_hw_freq_seek(const struct v4l2_ioctl_ops *ops, - struct file *file, void *fh, void *arg) -{ - struct video_device *vfd = video_devdata(file); - struct v4l2_hw_freq_seek *p = arg; - enum v4l2_tuner_type type; - - type = (vfd->vfl_type == VFL_TYPE_RADIO) ? - V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; - if (p->type != type) - return -EINVAL; - return ops->vidioc_s_hw_freq_seek(file, fh, p); -} - -static int v4l_reqbufs(const struct v4l2_ioctl_ops *ops, - struct file *file, void *fh, void *arg) -{ - struct v4l2_requestbuffers *p = arg; - int ret = check_fmt(ops, p->type); - - if (ret) - return ret; - - if (p->type < V4L2_BUF_TYPE_PRIVATE) - CLEAR_AFTER_FIELD(p, memory); - - return ops->vidioc_reqbufs(file, fh, p); -} - -static int v4l_querybuf(const struct v4l2_ioctl_ops *ops, - struct file *file, void *fh, void *arg) -{ - struct v4l2_buffer *p = arg; - int ret = check_fmt(ops, p->type); - - return ret ? ret : ops->vidioc_querybuf(file, fh, p); -} - -static int v4l_qbuf(const struct v4l2_ioctl_ops *ops, - struct file *file, void *fh, void *arg) -{ - struct v4l2_buffer *p = arg; - int ret = check_fmt(ops, p->type); - - return ret ? ret : ops->vidioc_qbuf(file, fh, p); -} - -static int v4l_dqbuf(const struct v4l2_ioctl_ops *ops, - struct file *file, void *fh, void *arg) -{ - struct v4l2_buffer *p = arg; - int ret = check_fmt(ops, p->type); - - return ret ? ret : ops->vidioc_dqbuf(file, fh, p); -} - -static int v4l_create_bufs(const struct v4l2_ioctl_ops *ops, - struct file *file, void *fh, void *arg) -{ - struct v4l2_create_buffers *create = arg; - int ret = check_fmt(ops, create->format.type); - - return ret ? ret : ops->vidioc_create_bufs(file, fh, create); -} - -static int v4l_prepare_buf(const struct v4l2_ioctl_ops *ops, - struct file *file, void *fh, void *arg) -{ - struct v4l2_buffer *b = arg; - int ret = check_fmt(ops, b->type); - - return ret ? ret : ops->vidioc_prepare_buf(file, fh, b); -} - -static int v4l_g_parm(const struct v4l2_ioctl_ops *ops, - struct file *file, void *fh, void *arg) -{ - struct video_device *vfd = video_devdata(file); - struct v4l2_streamparm *p = arg; - v4l2_std_id std; - int ret = check_fmt(ops, p->type); - - if (ret) - return ret; - if (ops->vidioc_g_parm) - return ops->vidioc_g_parm(file, fh, p); - std = vfd->current_norm; - if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && - p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) - return -EINVAL; - p->parm.capture.readbuffers = 2; - if (ops->vidioc_g_std) - ret = ops->vidioc_g_std(file, fh, &std); - if (ret == 0) - v4l2_video_std_frame_period(std, - &p->parm.capture.timeperframe); - return ret; -} - -static int v4l_s_parm(const struct v4l2_ioctl_ops *ops, - struct file *file, void *fh, void *arg) -{ - struct v4l2_streamparm *p = arg; - int ret = check_fmt(ops, p->type); - - return ret ? ret : ops->vidioc_s_parm(file, fh, p); -} - -static int v4l_queryctrl(const struct v4l2_ioctl_ops *ops, - struct file *file, void *fh, void *arg) -{ - struct video_device *vfd = video_devdata(file); - struct v4l2_queryctrl *p = arg; - struct v4l2_fh *vfh = - test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags) ? fh : NULL; - - if (vfh && vfh->ctrl_handler) - return v4l2_queryctrl(vfh->ctrl_handler, p); - if (vfd->ctrl_handler) - return v4l2_queryctrl(vfd->ctrl_handler, p); - if (ops->vidioc_queryctrl) - return ops->vidioc_queryctrl(file, fh, p); - return -ENOTTY; -} - -static int v4l_querymenu(const struct v4l2_ioctl_ops *ops, - struct file *file, void *fh, void *arg) -{ - struct video_device *vfd = video_devdata(file); - struct v4l2_querymenu *p = arg; - struct v4l2_fh *vfh = - test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags) ? fh : NULL; - - if (vfh && vfh->ctrl_handler) - return v4l2_querymenu(vfh->ctrl_handler, p); - if (vfd->ctrl_handler) - return v4l2_querymenu(vfd->ctrl_handler, p); - if (ops->vidioc_querymenu) - return ops->vidioc_querymenu(file, fh, p); - return -ENOTTY; -} - -static int v4l_g_ctrl(const struct v4l2_ioctl_ops *ops, - struct file *file, void *fh, void *arg) -{ - struct video_device *vfd = video_devdata(file); - struct v4l2_control *p = arg; - struct v4l2_fh *vfh = - test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags) ? fh : NULL; - struct v4l2_ext_controls ctrls; - struct v4l2_ext_control ctrl; - - if (vfh && vfh->ctrl_handler) - return v4l2_g_ctrl(vfh->ctrl_handler, p); - if (vfd->ctrl_handler) - return v4l2_g_ctrl(vfd->ctrl_handler, p); - if (ops->vidioc_g_ctrl) - return ops->vidioc_g_ctrl(file, fh, p); - if (ops->vidioc_g_ext_ctrls == NULL) - return -ENOTTY; - - ctrls.ctrl_class = V4L2_CTRL_ID2CLASS(p->id); - ctrls.count = 1; - ctrls.controls = &ctrl; - ctrl.id = p->id; - ctrl.value = p->value; - if (check_ext_ctrls(&ctrls, 1)) { - int ret = ops->vidioc_g_ext_ctrls(file, fh, &ctrls); - - if (ret == 0) - p->value = ctrl.value; - return ret; - } - return -EINVAL; -} - -static int v4l_s_ctrl(const struct v4l2_ioctl_ops *ops, - struct file *file, void *fh, void *arg) -{ - struct video_device *vfd = video_devdata(file); - struct v4l2_control *p = arg; - struct v4l2_fh *vfh = - test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags) ? fh : NULL; - struct v4l2_ext_controls ctrls; - struct v4l2_ext_control ctrl; - - if (vfh && vfh->ctrl_handler) - return v4l2_s_ctrl(vfh, vfh->ctrl_handler, p); - if (vfd->ctrl_handler) - return v4l2_s_ctrl(NULL, vfd->ctrl_handler, p); - if (ops->vidioc_s_ctrl) - return ops->vidioc_s_ctrl(file, fh, p); - if (ops->vidioc_s_ext_ctrls == NULL) - return -ENOTTY; - - ctrls.ctrl_class = V4L2_CTRL_ID2CLASS(p->id); - ctrls.count = 1; - ctrls.controls = &ctrl; - ctrl.id = p->id; - ctrl.value = p->value; - if (check_ext_ctrls(&ctrls, 1)) - return ops->vidioc_s_ext_ctrls(file, fh, &ctrls); - return -EINVAL; -} - -static int v4l_g_ext_ctrls(const struct v4l2_ioctl_ops *ops, - struct file *file, void *fh, void *arg) -{ - struct video_device *vfd = video_devdata(file); - struct v4l2_ext_controls *p = arg; - struct v4l2_fh *vfh = - test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags) ? fh : NULL; - - p->error_idx = p->count; - if (vfh && vfh->ctrl_handler) - return v4l2_g_ext_ctrls(vfh->ctrl_handler, p); - if (vfd->ctrl_handler) - return v4l2_g_ext_ctrls(vfd->ctrl_handler, p); - if (ops->vidioc_g_ext_ctrls == NULL) - return -ENOTTY; - return check_ext_ctrls(p, 0) ? ops->vidioc_g_ext_ctrls(file, fh, p) : - -EINVAL; -} - -static int v4l_s_ext_ctrls(const struct v4l2_ioctl_ops *ops, - struct file *file, void *fh, void *arg) -{ - struct video_device *vfd = video_devdata(file); - struct v4l2_ext_controls *p = arg; - struct v4l2_fh *vfh = - test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags) ? fh : NULL; - - p->error_idx = p->count; - if (vfh && vfh->ctrl_handler) - return v4l2_s_ext_ctrls(vfh, vfh->ctrl_handler, p); - if (vfd->ctrl_handler) - return v4l2_s_ext_ctrls(NULL, vfd->ctrl_handler, p); - if (ops->vidioc_s_ext_ctrls == NULL) - return -ENOTTY; - return check_ext_ctrls(p, 0) ? ops->vidioc_s_ext_ctrls(file, fh, p) : - -EINVAL; -} - -static int v4l_try_ext_ctrls(const struct v4l2_ioctl_ops *ops, - struct file *file, void *fh, void *arg) -{ - struct video_device *vfd = video_devdata(file); - struct v4l2_ext_controls *p = arg; - struct v4l2_fh *vfh = - test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags) ? fh : NULL; - - p->error_idx = p->count; - if (vfh && vfh->ctrl_handler) - return v4l2_try_ext_ctrls(vfh->ctrl_handler, p); - if (vfd->ctrl_handler) - return v4l2_try_ext_ctrls(vfd->ctrl_handler, p); - if (ops->vidioc_try_ext_ctrls == NULL) - return -ENOTTY; - return check_ext_ctrls(p, 0) ? ops->vidioc_try_ext_ctrls(file, fh, p) : - -EINVAL; -} - -static int v4l_g_crop(const struct v4l2_ioctl_ops *ops, - struct file *file, void *fh, void *arg) -{ - struct v4l2_crop *p = arg; - struct v4l2_selection s = { - .type = p->type, - }; - int ret; - - if (ops->vidioc_g_crop) - return ops->vidioc_g_crop(file, fh, p); - /* simulate capture crop using selection api */ - - /* crop means compose for output devices */ - if (V4L2_TYPE_IS_OUTPUT(p->type)) - s.target = V4L2_SEL_TGT_COMPOSE_ACTIVE; - else - s.target = V4L2_SEL_TGT_CROP_ACTIVE; - - ret = ops->vidioc_g_selection(file, fh, &s); - - /* copying results to old structure on success */ - if (!ret) - p->c = s.r; - return ret; -} - -static int v4l_s_crop(const struct v4l2_ioctl_ops *ops, - struct file *file, void *fh, void *arg) -{ - struct v4l2_crop *p = arg; - struct v4l2_selection s = { - .type = p->type, - .r = p->c, - }; - - if (ops->vidioc_s_crop) - return ops->vidioc_s_crop(file, fh, p); - /* simulate capture crop using selection api */ - - /* crop means compose for output devices */ - if (V4L2_TYPE_IS_OUTPUT(p->type)) - s.target = V4L2_SEL_TGT_COMPOSE_ACTIVE; - else - s.target = V4L2_SEL_TGT_CROP_ACTIVE; - - return ops->vidioc_s_selection(file, fh, &s); -} - -static int v4l_cropcap(const struct v4l2_ioctl_ops *ops, - struct file *file, void *fh, void *arg) -{ - struct v4l2_cropcap *p = arg; - struct v4l2_selection s = { .type = p->type }; - int ret; - - if (ops->vidioc_cropcap) - return ops->vidioc_cropcap(file, fh, p); - - /* obtaining bounds */ - if (V4L2_TYPE_IS_OUTPUT(p->type)) - s.target = V4L2_SEL_TGT_COMPOSE_BOUNDS; - else - s.target = V4L2_SEL_TGT_CROP_BOUNDS; - - ret = ops->vidioc_g_selection(file, fh, &s); - if (ret) - return ret; - p->bounds = s.r; - - /* obtaining defrect */ - if (V4L2_TYPE_IS_OUTPUT(p->type)) - s.target = V4L2_SEL_TGT_COMPOSE_DEFAULT; - else - s.target = V4L2_SEL_TGT_CROP_DEFAULT; - - ret = ops->vidioc_g_selection(file, fh, &s); - if (ret) - return ret; - p->defrect = s.r; - - /* setting trivial pixelaspect */ - p->pixelaspect.numerator = 1; - p->pixelaspect.denominator = 1; - return 0; -} - -static int v4l_log_status(const struct v4l2_ioctl_ops *ops, - struct file *file, void *fh, void *arg) -{ - struct video_device *vfd = video_devdata(file); - int ret; - - if (vfd->v4l2_dev) - pr_info("%s: ================= START STATUS =================\n", - vfd->v4l2_dev->name); - ret = ops->vidioc_log_status(file, fh); - if (vfd->v4l2_dev) - pr_info("%s: ================== END STATUS ==================\n", - vfd->v4l2_dev->name); - return ret; -} - -static int v4l_dbg_g_register(const struct v4l2_ioctl_ops *ops, - struct file *file, void *fh, void *arg) -{ -#ifdef CONFIG_VIDEO_ADV_DEBUG - struct v4l2_dbg_register *p = arg; - - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - return ops->vidioc_g_register(file, fh, p); -#else - return -ENOTTY; -#endif -} - -static int v4l_dbg_s_register(const struct v4l2_ioctl_ops *ops, - struct file *file, void *fh, void *arg) -{ -#ifdef CONFIG_VIDEO_ADV_DEBUG - struct v4l2_dbg_register *p = arg; - - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - return ops->vidioc_s_register(file, fh, p); -#else - return -ENOTTY; -#endif -} - -static int v4l_dbg_g_chip_ident(const struct v4l2_ioctl_ops *ops, - struct file *file, void *fh, void *arg) -{ - struct v4l2_dbg_chip_ident *p = arg; - - p->ident = V4L2_IDENT_NONE; - p->revision = 0; - return ops->vidioc_g_chip_ident(file, fh, p); -} - -static int v4l_dqevent(const struct v4l2_ioctl_ops *ops, - struct file *file, void *fh, void *arg) -{ - return v4l2_event_dequeue(fh, arg, file->f_flags & O_NONBLOCK); -} - -static int v4l_subscribe_event(const struct v4l2_ioctl_ops *ops, - struct file *file, void *fh, void *arg) -{ - return ops->vidioc_subscribe_event(fh, arg); -} - -static int v4l_unsubscribe_event(const struct v4l2_ioctl_ops *ops, - struct file *file, void *fh, void *arg) -{ - return ops->vidioc_unsubscribe_event(fh, arg); -} - -static int v4l_g_sliced_vbi_cap(const struct v4l2_ioctl_ops *ops, - struct file *file, void *fh, void *arg) -{ - struct v4l2_sliced_vbi_cap *p = arg; - - /* Clear up to type, everything after type is zeroed already */ - memset(p, 0, offsetof(struct v4l2_sliced_vbi_cap, type)); - - return ops->vidioc_g_sliced_vbi_cap(file, fh, p); -} - -static int v4l_enum_freq_bands(const struct v4l2_ioctl_ops *ops, - struct file *file, void *fh, void *arg) -{ - struct video_device *vfd = video_devdata(file); - struct v4l2_frequency_band *p = arg; - enum v4l2_tuner_type type; - int err; - - type = (vfd->vfl_type == VFL_TYPE_RADIO) ? - V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; - - if (type != p->type) - return -EINVAL; - if (ops->vidioc_enum_freq_bands) - return ops->vidioc_enum_freq_bands(file, fh, p); - if (ops->vidioc_g_tuner) { - struct v4l2_tuner t = { - .index = p->tuner, - .type = type, - }; - - err = ops->vidioc_g_tuner(file, fh, &t); - if (err) - return err; - p->capability = t.capability | V4L2_TUNER_CAP_FREQ_BANDS; - p->rangelow = t.rangelow; - p->rangehigh = t.rangehigh; - p->modulation = (type == V4L2_TUNER_RADIO) ? - V4L2_BAND_MODULATION_FM : V4L2_BAND_MODULATION_VSB; - return 0; - } - if (ops->vidioc_g_modulator) { - struct v4l2_modulator m = { - .index = p->tuner, - }; - - if (type != V4L2_TUNER_RADIO) - return -EINVAL; - err = ops->vidioc_g_modulator(file, fh, &m); - if (err) - return err; - p->capability = m.capability | V4L2_TUNER_CAP_FREQ_BANDS; - p->rangelow = m.rangelow; - p->rangehigh = m.rangehigh; - p->modulation = (type == V4L2_TUNER_RADIO) ? - V4L2_BAND_MODULATION_FM : V4L2_BAND_MODULATION_VSB; - return 0; - } - return -ENOTTY; -} - -struct v4l2_ioctl_info { - unsigned int ioctl; - u32 flags; - const char * const name; - union { - u32 offset; - int (*func)(const struct v4l2_ioctl_ops *ops, - struct file *file, void *fh, void *p); - } u; - void (*debug)(const void *arg, bool write_only); -}; - -/* This control needs a priority check */ -#define INFO_FL_PRIO (1 << 0) -/* This control can be valid if the filehandle passes a control handler. */ -#define INFO_FL_CTRL (1 << 1) -/* This is a standard ioctl, no need for special code */ -#define INFO_FL_STD (1 << 2) -/* This is ioctl has its own function */ -#define INFO_FL_FUNC (1 << 3) -/* Queuing ioctl */ -#define INFO_FL_QUEUE (1 << 4) -/* Zero struct from after the field to the end */ -#define INFO_FL_CLEAR(v4l2_struct, field) \ - ((offsetof(struct v4l2_struct, field) + \ - sizeof(((struct v4l2_struct *)0)->field)) << 16) -#define INFO_FL_CLEAR_MASK (_IOC_SIZEMASK << 16) - -#define IOCTL_INFO_STD(_ioctl, _vidioc, _debug, _flags) \ - [_IOC_NR(_ioctl)] = { \ - .ioctl = _ioctl, \ - .flags = _flags | INFO_FL_STD, \ - .name = #_ioctl, \ - .u.offset = offsetof(struct v4l2_ioctl_ops, _vidioc), \ - .debug = _debug, \ - } - -#define IOCTL_INFO_FNC(_ioctl, _func, _debug, _flags) \ - [_IOC_NR(_ioctl)] = { \ - .ioctl = _ioctl, \ - .flags = _flags | INFO_FL_FUNC, \ - .name = #_ioctl, \ - .u.func = _func, \ - .debug = _debug, \ - } - -static struct v4l2_ioctl_info v4l2_ioctls[] = { - IOCTL_INFO_FNC(VIDIOC_QUERYCAP, v4l_querycap, v4l_print_querycap, 0), - IOCTL_INFO_FNC(VIDIOC_ENUM_FMT, v4l_enum_fmt, v4l_print_fmtdesc, INFO_FL_CLEAR(v4l2_fmtdesc, type)), - IOCTL_INFO_FNC(VIDIOC_G_FMT, v4l_g_fmt, v4l_print_format, INFO_FL_CLEAR(v4l2_format, type)), - IOCTL_INFO_FNC(VIDIOC_S_FMT, v4l_s_fmt, v4l_print_format, INFO_FL_PRIO), - IOCTL_INFO_FNC(VIDIOC_REQBUFS, v4l_reqbufs, v4l_print_requestbuffers, INFO_FL_PRIO | INFO_FL_QUEUE), - IOCTL_INFO_FNC(VIDIOC_QUERYBUF, v4l_querybuf, v4l_print_buffer, INFO_FL_QUEUE | INFO_FL_CLEAR(v4l2_buffer, length)), - IOCTL_INFO_STD(VIDIOC_G_FBUF, vidioc_g_fbuf, v4l_print_framebuffer, 0), - IOCTL_INFO_STD(VIDIOC_S_FBUF, vidioc_s_fbuf, v4l_print_framebuffer, INFO_FL_PRIO), - IOCTL_INFO_STD(VIDIOC_OVERLAY, vidioc_overlay, v4l_print_u32, INFO_FL_PRIO), - IOCTL_INFO_FNC(VIDIOC_QBUF, v4l_qbuf, v4l_print_buffer, INFO_FL_QUEUE), - IOCTL_INFO_FNC(VIDIOC_DQBUF, v4l_dqbuf, v4l_print_buffer, INFO_FL_QUEUE), - IOCTL_INFO_FNC(VIDIOC_STREAMON, v4l_streamon, v4l_print_buftype, INFO_FL_PRIO | INFO_FL_QUEUE), - IOCTL_INFO_FNC(VIDIOC_STREAMOFF, v4l_streamoff, v4l_print_buftype, INFO_FL_PRIO | INFO_FL_QUEUE), - IOCTL_INFO_FNC(VIDIOC_G_PARM, v4l_g_parm, v4l_print_streamparm, INFO_FL_CLEAR(v4l2_streamparm, type)), - IOCTL_INFO_FNC(VIDIOC_S_PARM, v4l_s_parm, v4l_print_streamparm, INFO_FL_PRIO), - IOCTL_INFO_FNC(VIDIOC_G_STD, v4l_g_std, v4l_print_std, 0), - IOCTL_INFO_FNC(VIDIOC_S_STD, v4l_s_std, v4l_print_std, INFO_FL_PRIO), - IOCTL_INFO_FNC(VIDIOC_ENUMSTD, v4l_enumstd, v4l_print_standard, INFO_FL_CLEAR(v4l2_standard, index)), - IOCTL_INFO_FNC(VIDIOC_ENUMINPUT, v4l_enuminput, v4l_print_enuminput, INFO_FL_CLEAR(v4l2_input, index)), - IOCTL_INFO_FNC(VIDIOC_G_CTRL, v4l_g_ctrl, v4l_print_control, INFO_FL_CTRL | INFO_FL_CLEAR(v4l2_control, id)), - IOCTL_INFO_FNC(VIDIOC_S_CTRL, v4l_s_ctrl, v4l_print_control, INFO_FL_PRIO | INFO_FL_CTRL), - IOCTL_INFO_FNC(VIDIOC_G_TUNER, v4l_g_tuner, v4l_print_tuner, INFO_FL_CLEAR(v4l2_tuner, index)), - IOCTL_INFO_FNC(VIDIOC_S_TUNER, v4l_s_tuner, v4l_print_tuner, INFO_FL_PRIO), - IOCTL_INFO_STD(VIDIOC_G_AUDIO, vidioc_g_audio, v4l_print_audio, 0), - IOCTL_INFO_STD(VIDIOC_S_AUDIO, vidioc_s_audio, v4l_print_audio, INFO_FL_PRIO), - IOCTL_INFO_FNC(VIDIOC_QUERYCTRL, v4l_queryctrl, v4l_print_queryctrl, INFO_FL_CTRL | INFO_FL_CLEAR(v4l2_queryctrl, id)), - IOCTL_INFO_FNC(VIDIOC_QUERYMENU, v4l_querymenu, v4l_print_querymenu, INFO_FL_CTRL | INFO_FL_CLEAR(v4l2_querymenu, index)), - IOCTL_INFO_STD(VIDIOC_G_INPUT, vidioc_g_input, v4l_print_u32, 0), - IOCTL_INFO_FNC(VIDIOC_S_INPUT, v4l_s_input, v4l_print_u32, INFO_FL_PRIO), - IOCTL_INFO_STD(VIDIOC_G_OUTPUT, vidioc_g_output, v4l_print_u32, 0), - IOCTL_INFO_FNC(VIDIOC_S_OUTPUT, v4l_s_output, v4l_print_u32, INFO_FL_PRIO), - IOCTL_INFO_FNC(VIDIOC_ENUMOUTPUT, v4l_enumoutput, v4l_print_enumoutput, INFO_FL_CLEAR(v4l2_output, index)), - IOCTL_INFO_STD(VIDIOC_G_AUDOUT, vidioc_g_audout, v4l_print_audioout, 0), - IOCTL_INFO_STD(VIDIOC_S_AUDOUT, vidioc_s_audout, v4l_print_audioout, INFO_FL_PRIO), - IOCTL_INFO_FNC(VIDIOC_G_MODULATOR, v4l_g_modulator, v4l_print_modulator, INFO_FL_CLEAR(v4l2_modulator, index)), - IOCTL_INFO_STD(VIDIOC_S_MODULATOR, vidioc_s_modulator, v4l_print_modulator, INFO_FL_PRIO), - IOCTL_INFO_FNC(VIDIOC_G_FREQUENCY, v4l_g_frequency, v4l_print_frequency, INFO_FL_CLEAR(v4l2_frequency, tuner)), - IOCTL_INFO_FNC(VIDIOC_S_FREQUENCY, v4l_s_frequency, v4l_print_frequency, INFO_FL_PRIO), - IOCTL_INFO_FNC(VIDIOC_CROPCAP, v4l_cropcap, v4l_print_cropcap, INFO_FL_CLEAR(v4l2_cropcap, type)), - IOCTL_INFO_FNC(VIDIOC_G_CROP, v4l_g_crop, v4l_print_crop, INFO_FL_CLEAR(v4l2_crop, type)), - IOCTL_INFO_FNC(VIDIOC_S_CROP, v4l_s_crop, v4l_print_crop, INFO_FL_PRIO), - IOCTL_INFO_STD(VIDIOC_G_SELECTION, vidioc_g_selection, v4l_print_selection, 0), - IOCTL_INFO_STD(VIDIOC_S_SELECTION, vidioc_s_selection, v4l_print_selection, INFO_FL_PRIO), - IOCTL_INFO_STD(VIDIOC_G_JPEGCOMP, vidioc_g_jpegcomp, v4l_print_jpegcompression, 0), - IOCTL_INFO_STD(VIDIOC_S_JPEGCOMP, vidioc_s_jpegcomp, v4l_print_jpegcompression, INFO_FL_PRIO), - IOCTL_INFO_FNC(VIDIOC_QUERYSTD, v4l_querystd, v4l_print_std, 0), - IOCTL_INFO_FNC(VIDIOC_TRY_FMT, v4l_try_fmt, v4l_print_format, 0), - IOCTL_INFO_STD(VIDIOC_ENUMAUDIO, vidioc_enumaudio, v4l_print_audio, INFO_FL_CLEAR(v4l2_audio, index)), - IOCTL_INFO_STD(VIDIOC_ENUMAUDOUT, vidioc_enumaudout, v4l_print_audioout, INFO_FL_CLEAR(v4l2_audioout, index)), - IOCTL_INFO_FNC(VIDIOC_G_PRIORITY, v4l_g_priority, v4l_print_u32, 0), - IOCTL_INFO_FNC(VIDIOC_S_PRIORITY, v4l_s_priority, v4l_print_u32, INFO_FL_PRIO), - IOCTL_INFO_FNC(VIDIOC_G_SLICED_VBI_CAP, v4l_g_sliced_vbi_cap, v4l_print_sliced_vbi_cap, INFO_FL_CLEAR(v4l2_sliced_vbi_cap, type)), - IOCTL_INFO_FNC(VIDIOC_LOG_STATUS, v4l_log_status, v4l_print_newline, 0), - IOCTL_INFO_FNC(VIDIOC_G_EXT_CTRLS, v4l_g_ext_ctrls, v4l_print_ext_controls, INFO_FL_CTRL), - IOCTL_INFO_FNC(VIDIOC_S_EXT_CTRLS, v4l_s_ext_ctrls, v4l_print_ext_controls, INFO_FL_PRIO | INFO_FL_CTRL), - IOCTL_INFO_FNC(VIDIOC_TRY_EXT_CTRLS, v4l_try_ext_ctrls, v4l_print_ext_controls, INFO_FL_CTRL), - IOCTL_INFO_STD(VIDIOC_ENUM_FRAMESIZES, vidioc_enum_framesizes, v4l_print_frmsizeenum, INFO_FL_CLEAR(v4l2_frmsizeenum, pixel_format)), - IOCTL_INFO_STD(VIDIOC_ENUM_FRAMEINTERVALS, vidioc_enum_frameintervals, v4l_print_frmivalenum, INFO_FL_CLEAR(v4l2_frmivalenum, height)), - IOCTL_INFO_STD(VIDIOC_G_ENC_INDEX, vidioc_g_enc_index, v4l_print_enc_idx, 0), - IOCTL_INFO_STD(VIDIOC_ENCODER_CMD, vidioc_encoder_cmd, v4l_print_encoder_cmd, INFO_FL_PRIO | INFO_FL_CLEAR(v4l2_encoder_cmd, flags)), - IOCTL_INFO_STD(VIDIOC_TRY_ENCODER_CMD, vidioc_try_encoder_cmd, v4l_print_encoder_cmd, INFO_FL_CLEAR(v4l2_encoder_cmd, flags)), - IOCTL_INFO_STD(VIDIOC_DECODER_CMD, vidioc_decoder_cmd, v4l_print_decoder_cmd, INFO_FL_PRIO), - IOCTL_INFO_STD(VIDIOC_TRY_DECODER_CMD, vidioc_try_decoder_cmd, v4l_print_decoder_cmd, 0), - IOCTL_INFO_FNC(VIDIOC_DBG_S_REGISTER, v4l_dbg_s_register, v4l_print_dbg_register, 0), - IOCTL_INFO_FNC(VIDIOC_DBG_G_REGISTER, v4l_dbg_g_register, v4l_print_dbg_register, 0), - IOCTL_INFO_FNC(VIDIOC_DBG_G_CHIP_IDENT, v4l_dbg_g_chip_ident, v4l_print_dbg_chip_ident, 0), - IOCTL_INFO_FNC(VIDIOC_S_HW_FREQ_SEEK, v4l_s_hw_freq_seek, v4l_print_hw_freq_seek, INFO_FL_PRIO), - IOCTL_INFO_STD(VIDIOC_ENUM_DV_PRESETS, vidioc_enum_dv_presets, v4l_print_dv_enum_presets, 0), - IOCTL_INFO_STD(VIDIOC_S_DV_PRESET, vidioc_s_dv_preset, v4l_print_dv_preset, INFO_FL_PRIO), - IOCTL_INFO_STD(VIDIOC_G_DV_PRESET, vidioc_g_dv_preset, v4l_print_dv_preset, 0), - IOCTL_INFO_STD(VIDIOC_QUERY_DV_PRESET, vidioc_query_dv_preset, v4l_print_dv_preset, 0), - IOCTL_INFO_STD(VIDIOC_S_DV_TIMINGS, vidioc_s_dv_timings, v4l_print_dv_timings, INFO_FL_PRIO), - IOCTL_INFO_STD(VIDIOC_G_DV_TIMINGS, vidioc_g_dv_timings, v4l_print_dv_timings, 0), - IOCTL_INFO_FNC(VIDIOC_DQEVENT, v4l_dqevent, v4l_print_event, 0), - IOCTL_INFO_FNC(VIDIOC_SUBSCRIBE_EVENT, v4l_subscribe_event, v4l_print_event_subscription, 0), - IOCTL_INFO_FNC(VIDIOC_UNSUBSCRIBE_EVENT, v4l_unsubscribe_event, v4l_print_event_subscription, 0), - IOCTL_INFO_FNC(VIDIOC_CREATE_BUFS, v4l_create_bufs, v4l_print_create_buffers, INFO_FL_PRIO | INFO_FL_QUEUE), - IOCTL_INFO_FNC(VIDIOC_PREPARE_BUF, v4l_prepare_buf, v4l_print_buffer, INFO_FL_QUEUE), - IOCTL_INFO_STD(VIDIOC_ENUM_DV_TIMINGS, vidioc_enum_dv_timings, v4l_print_enum_dv_timings, 0), - IOCTL_INFO_STD(VIDIOC_QUERY_DV_TIMINGS, vidioc_query_dv_timings, v4l_print_dv_timings, 0), - IOCTL_INFO_STD(VIDIOC_DV_TIMINGS_CAP, vidioc_dv_timings_cap, v4l_print_dv_timings_cap, INFO_FL_CLEAR(v4l2_dv_timings_cap, type)), - IOCTL_INFO_FNC(VIDIOC_ENUM_FREQ_BANDS, v4l_enum_freq_bands, v4l_print_freq_band, 0), -}; -#define V4L2_IOCTLS ARRAY_SIZE(v4l2_ioctls) - -bool v4l2_is_known_ioctl(unsigned int cmd) -{ - if (_IOC_NR(cmd) >= V4L2_IOCTLS) - return false; - return v4l2_ioctls[_IOC_NR(cmd)].ioctl == cmd; -} - -struct mutex *v4l2_ioctl_get_lock(struct video_device *vdev, unsigned cmd) -{ - if (_IOC_NR(cmd) >= V4L2_IOCTLS) - return vdev->lock; - if (test_bit(_IOC_NR(cmd), vdev->disable_locking)) - return NULL; - if (vdev->queue && vdev->queue->lock && - (v4l2_ioctls[_IOC_NR(cmd)].flags & INFO_FL_QUEUE)) - return vdev->queue->lock; - return vdev->lock; -} - -/* Common ioctl debug function. This function can be used by - external ioctl messages as well as internal V4L ioctl */ -void v4l_printk_ioctl(const char *prefix, unsigned int cmd) -{ - const char *dir, *type; - - if (prefix) - printk(KERN_DEBUG "%s: ", prefix); - - switch (_IOC_TYPE(cmd)) { - case 'd': - type = "v4l2_int"; - break; - case 'V': - if (_IOC_NR(cmd) >= V4L2_IOCTLS) { - type = "v4l2"; - break; - } - pr_cont("%s", v4l2_ioctls[_IOC_NR(cmd)].name); - return; - default: - type = "unknown"; - break; - } - - switch (_IOC_DIR(cmd)) { - case _IOC_NONE: dir = "--"; break; - case _IOC_READ: dir = "r-"; break; - case _IOC_WRITE: dir = "-w"; break; - case _IOC_READ | _IOC_WRITE: dir = "rw"; break; - default: dir = "*ERR*"; break; - } - pr_cont("%s ioctl '%c', dir=%s, #%d (0x%08x)", - type, _IOC_TYPE(cmd), dir, _IOC_NR(cmd), cmd); -} -EXPORT_SYMBOL(v4l_printk_ioctl); - -static long __video_do_ioctl(struct file *file, - unsigned int cmd, void *arg) -{ - struct video_device *vfd = video_devdata(file); - const struct v4l2_ioctl_ops *ops = vfd->ioctl_ops; - bool write_only = false; - struct v4l2_ioctl_info default_info; - const struct v4l2_ioctl_info *info; - void *fh = file->private_data; - struct v4l2_fh *vfh = NULL; - int use_fh_prio = 0; - int debug = vfd->debug; - long ret = -ENOTTY; - - if (ops == NULL) { - pr_warn("%s: has no ioctl_ops.\n", - video_device_node_name(vfd)); - return ret; - } - - if (test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags)) { - vfh = file->private_data; - use_fh_prio = test_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags); - } - - if (v4l2_is_known_ioctl(cmd)) { - info = &v4l2_ioctls[_IOC_NR(cmd)]; - - if (!test_bit(_IOC_NR(cmd), vfd->valid_ioctls) && - !((info->flags & INFO_FL_CTRL) && vfh && vfh->ctrl_handler)) - goto done; - - if (use_fh_prio && (info->flags & INFO_FL_PRIO)) { - ret = v4l2_prio_check(vfd->prio, vfh->prio); - if (ret) - goto done; - } - } else { - default_info.ioctl = cmd; - default_info.flags = 0; - default_info.debug = v4l_print_default; - info = &default_info; - } - - write_only = _IOC_DIR(cmd) == _IOC_WRITE; - if (write_only && debug > V4L2_DEBUG_IOCTL) { - v4l_printk_ioctl(video_device_node_name(vfd), cmd); - pr_cont(": "); - info->debug(arg, write_only); - } - if (info->flags & INFO_FL_STD) { - typedef int (*vidioc_op)(struct file *file, void *fh, void *p); - const void *p = vfd->ioctl_ops; - const vidioc_op *vidioc = p + info->u.offset; - - ret = (*vidioc)(file, fh, arg); - } else if (info->flags & INFO_FL_FUNC) { - ret = info->u.func(ops, file, fh, arg); - } else if (!ops->vidioc_default) { - ret = -ENOTTY; - } else { - ret = ops->vidioc_default(file, fh, - use_fh_prio ? v4l2_prio_check(vfd->prio, vfh->prio) >= 0 : 0, - cmd, arg); - } - -done: - if (debug) { - if (write_only && debug > V4L2_DEBUG_IOCTL) { - if (ret < 0) - printk(KERN_DEBUG "%s: error %ld\n", - video_device_node_name(vfd), ret); - return ret; - } - v4l_printk_ioctl(video_device_node_name(vfd), cmd); - if (ret < 0) - pr_cont(": error %ld\n", ret); - else if (debug == V4L2_DEBUG_IOCTL) - pr_cont("\n"); - else if (_IOC_DIR(cmd) == _IOC_NONE) - info->debug(arg, write_only); - else { - pr_cont(": "); - info->debug(arg, write_only); - } - } - - return ret; -} - -static int check_array_args(unsigned int cmd, void *parg, size_t *array_size, - void * __user *user_ptr, void ***kernel_ptr) -{ - int ret = 0; - - switch (cmd) { - case VIDIOC_QUERYBUF: - case VIDIOC_QBUF: - case VIDIOC_DQBUF: { - struct v4l2_buffer *buf = parg; - - if (V4L2_TYPE_IS_MULTIPLANAR(buf->type) && buf->length > 0) { - if (buf->length > VIDEO_MAX_PLANES) { - ret = -EINVAL; - break; - } - *user_ptr = (void __user *)buf->m.planes; - *kernel_ptr = (void *)&buf->m.planes; - *array_size = sizeof(struct v4l2_plane) * buf->length; - ret = 1; - } - break; - } - - case VIDIOC_S_EXT_CTRLS: - case VIDIOC_G_EXT_CTRLS: - case VIDIOC_TRY_EXT_CTRLS: { - struct v4l2_ext_controls *ctrls = parg; - - if (ctrls->count != 0) { - if (ctrls->count > V4L2_CID_MAX_CTRLS) { - ret = -EINVAL; - break; - } - *user_ptr = (void __user *)ctrls->controls; - *kernel_ptr = (void *)&ctrls->controls; - *array_size = sizeof(struct v4l2_ext_control) - * ctrls->count; - ret = 1; - } - break; - } - } - - return ret; -} - -long -video_usercopy(struct file *file, unsigned int cmd, unsigned long arg, - v4l2_kioctl func) -{ - char sbuf[128]; - void *mbuf = NULL; - void *parg = (void *)arg; - long err = -EINVAL; - bool has_array_args; - size_t array_size = 0; - void __user *user_ptr = NULL; - void **kernel_ptr = NULL; - - /* Copy arguments into temp kernel buffer */ - if (_IOC_DIR(cmd) != _IOC_NONE) { - if (_IOC_SIZE(cmd) <= sizeof(sbuf)) { - parg = sbuf; - } else { - /* too big to allocate from stack */ - mbuf = kmalloc(_IOC_SIZE(cmd), GFP_KERNEL); - if (NULL == mbuf) - return -ENOMEM; - parg = mbuf; - } - - err = -EFAULT; - if (_IOC_DIR(cmd) & _IOC_WRITE) { - unsigned int n = _IOC_SIZE(cmd); - - /* - * In some cases, only a few fields are used as input, - * i.e. when the app sets "index" and then the driver - * fills in the rest of the structure for the thing - * with that index. We only need to copy up the first - * non-input field. - */ - if (v4l2_is_known_ioctl(cmd)) { - u32 flags = v4l2_ioctls[_IOC_NR(cmd)].flags; - if (flags & INFO_FL_CLEAR_MASK) - n = (flags & INFO_FL_CLEAR_MASK) >> 16; - } - - if (copy_from_user(parg, (void __user *)arg, n)) - goto out; - - /* zero out anything we don't copy from userspace */ - if (n < _IOC_SIZE(cmd)) - memset((u8 *)parg + n, 0, _IOC_SIZE(cmd) - n); - } else { - /* read-only ioctl */ - memset(parg, 0, _IOC_SIZE(cmd)); - } - } - - err = check_array_args(cmd, parg, &array_size, &user_ptr, &kernel_ptr); - if (err < 0) - goto out; - has_array_args = err; - - if (has_array_args) { - /* - * When adding new types of array args, make sure that the - * parent argument to ioctl (which contains the pointer to the - * array) fits into sbuf (so that mbuf will still remain - * unused up to here). - */ - mbuf = kmalloc(array_size, GFP_KERNEL); - err = -ENOMEM; - if (NULL == mbuf) - goto out_array_args; - err = -EFAULT; - if (copy_from_user(mbuf, user_ptr, array_size)) - goto out_array_args; - *kernel_ptr = mbuf; - } - - /* Handles IOCTL */ - err = func(file, cmd, parg); - if (err == -ENOIOCTLCMD) - err = -ENOTTY; - - if (has_array_args) { - *kernel_ptr = user_ptr; - if (copy_to_user(user_ptr, mbuf, array_size)) - err = -EFAULT; - goto out_array_args; - } - /* VIDIOC_QUERY_DV_TIMINGS can return an error, but still have valid - results that must be returned. */ - if (err < 0 && cmd != VIDIOC_QUERY_DV_TIMINGS) - goto out; - -out_array_args: - /* Copy results into user buffer */ - switch (_IOC_DIR(cmd)) { - case _IOC_READ: - case (_IOC_WRITE | _IOC_READ): - if (copy_to_user((void __user *)arg, parg, _IOC_SIZE(cmd))) - err = -EFAULT; - break; - } - -out: - kfree(mbuf); - return err; -} -EXPORT_SYMBOL(video_usercopy); - -long video_ioctl2(struct file *file, - unsigned int cmd, unsigned long arg) -{ - return video_usercopy(file, cmd, arg, __video_do_ioctl); -} -EXPORT_SYMBOL(video_ioctl2); diff --git a/drivers/media/video/v4l2-mem2mem.c b/drivers/media/video/v4l2-mem2mem.c deleted file mode 100644 index 97b48318aee1..000000000000 --- a/drivers/media/video/v4l2-mem2mem.c +++ /dev/null @@ -1,647 +0,0 @@ -/* - * Memory-to-memory device framework for Video for Linux 2 and videobuf. - * - * Helper functions for devices that use videobuf buffers for both their - * source and destination. - * - * Copyright (c) 2009-2010 Samsung Electronics Co., Ltd. - * Pawel Osciak, <pawel@osciak.com> - * Marek Szyprowski, <m.szyprowski@samsung.com> - * - * 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. - */ -#include <linux/module.h> -#include <linux/sched.h> -#include <linux/slab.h> - -#include <media/videobuf2-core.h> -#include <media/v4l2-mem2mem.h> -#include <media/v4l2-dev.h> -#include <media/v4l2-fh.h> -#include <media/v4l2-event.h> - -MODULE_DESCRIPTION("Mem to mem device framework for videobuf"); -MODULE_AUTHOR("Pawel Osciak, <pawel@osciak.com>"); -MODULE_LICENSE("GPL"); - -static bool debug; -module_param(debug, bool, 0644); - -#define dprintk(fmt, arg...) \ - do { \ - if (debug) \ - printk(KERN_DEBUG "%s: " fmt, __func__, ## arg);\ - } while (0) - - -/* Instance is already queued on the job_queue */ -#define TRANS_QUEUED (1 << 0) -/* Instance is currently running in hardware */ -#define TRANS_RUNNING (1 << 1) - - -/* Offset base for buffers on the destination queue - used to distinguish - * between source and destination buffers when mmapping - they receive the same - * offsets but for different queues */ -#define DST_QUEUE_OFF_BASE (1 << 30) - - -/** - * struct v4l2_m2m_dev - per-device context - * @curr_ctx: currently running instance - * @job_queue: instances queued to run - * @job_spinlock: protects job_queue - * @m2m_ops: driver callbacks - */ -struct v4l2_m2m_dev { - struct v4l2_m2m_ctx *curr_ctx; - - struct list_head job_queue; - spinlock_t job_spinlock; - - struct v4l2_m2m_ops *m2m_ops; -}; - -static struct v4l2_m2m_queue_ctx *get_queue_ctx(struct v4l2_m2m_ctx *m2m_ctx, - enum v4l2_buf_type type) -{ - if (V4L2_TYPE_IS_OUTPUT(type)) - return &m2m_ctx->out_q_ctx; - else - return &m2m_ctx->cap_q_ctx; -} - -/** - * v4l2_m2m_get_vq() - return vb2_queue for the given type - */ -struct vb2_queue *v4l2_m2m_get_vq(struct v4l2_m2m_ctx *m2m_ctx, - enum v4l2_buf_type type) -{ - struct v4l2_m2m_queue_ctx *q_ctx; - - q_ctx = get_queue_ctx(m2m_ctx, type); - if (!q_ctx) - return NULL; - - return &q_ctx->q; -} -EXPORT_SYMBOL(v4l2_m2m_get_vq); - -/** - * v4l2_m2m_next_buf() - return next buffer from the list of ready buffers - */ -void *v4l2_m2m_next_buf(struct v4l2_m2m_queue_ctx *q_ctx) -{ - struct v4l2_m2m_buffer *b = NULL; - unsigned long flags; - - spin_lock_irqsave(&q_ctx->rdy_spinlock, flags); - - if (list_empty(&q_ctx->rdy_queue)) { - spin_unlock_irqrestore(&q_ctx->rdy_spinlock, flags); - return NULL; - } - - b = list_entry(q_ctx->rdy_queue.next, struct v4l2_m2m_buffer, list); - spin_unlock_irqrestore(&q_ctx->rdy_spinlock, flags); - return &b->vb; -} -EXPORT_SYMBOL_GPL(v4l2_m2m_next_buf); - -/** - * v4l2_m2m_buf_remove() - take off a buffer from the list of ready buffers and - * return it - */ -void *v4l2_m2m_buf_remove(struct v4l2_m2m_queue_ctx *q_ctx) -{ - struct v4l2_m2m_buffer *b = NULL; - unsigned long flags; - - spin_lock_irqsave(&q_ctx->rdy_spinlock, flags); - if (list_empty(&q_ctx->rdy_queue)) { - spin_unlock_irqrestore(&q_ctx->rdy_spinlock, flags); - return NULL; - } - b = list_entry(q_ctx->rdy_queue.next, struct v4l2_m2m_buffer, list); - list_del(&b->list); - q_ctx->num_rdy--; - spin_unlock_irqrestore(&q_ctx->rdy_spinlock, flags); - - return &b->vb; -} -EXPORT_SYMBOL_GPL(v4l2_m2m_buf_remove); - -/* - * Scheduling handlers - */ - -/** - * v4l2_m2m_get_curr_priv() - return driver private data for the currently - * running instance or NULL if no instance is running - */ -void *v4l2_m2m_get_curr_priv(struct v4l2_m2m_dev *m2m_dev) -{ - unsigned long flags; - void *ret = NULL; - - spin_lock_irqsave(&m2m_dev->job_spinlock, flags); - if (m2m_dev->curr_ctx) - ret = m2m_dev->curr_ctx->priv; - spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags); - - return ret; -} -EXPORT_SYMBOL(v4l2_m2m_get_curr_priv); - -/** - * v4l2_m2m_try_run() - select next job to perform and run it if possible - * - * Get next transaction (if present) from the waiting jobs list and run it. - */ -static void v4l2_m2m_try_run(struct v4l2_m2m_dev *m2m_dev) -{ - unsigned long flags; - - spin_lock_irqsave(&m2m_dev->job_spinlock, flags); - if (NULL != m2m_dev->curr_ctx) { - spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags); - dprintk("Another instance is running, won't run now\n"); - return; - } - - if (list_empty(&m2m_dev->job_queue)) { - spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags); - dprintk("No job pending\n"); - return; - } - - m2m_dev->curr_ctx = list_entry(m2m_dev->job_queue.next, - struct v4l2_m2m_ctx, queue); - m2m_dev->curr_ctx->job_flags |= TRANS_RUNNING; - spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags); - - m2m_dev->m2m_ops->device_run(m2m_dev->curr_ctx->priv); -} - -/** - * v4l2_m2m_try_schedule() - check whether an instance is ready to be added to - * the pending job queue and add it if so. - * @m2m_ctx: m2m context assigned to the instance to be checked - * - * There are three basic requirements an instance has to meet to be able to run: - * 1) at least one source buffer has to be queued, - * 2) at least one destination buffer has to be queued, - * 3) streaming has to be on. - * - * There may also be additional, custom requirements. In such case the driver - * should supply a custom callback (job_ready in v4l2_m2m_ops) that should - * return 1 if the instance is ready. - * An example of the above could be an instance that requires more than one - * src/dst buffer per transaction. - */ -static void v4l2_m2m_try_schedule(struct v4l2_m2m_ctx *m2m_ctx) -{ - struct v4l2_m2m_dev *m2m_dev; - unsigned long flags_job, flags; - - m2m_dev = m2m_ctx->m2m_dev; - dprintk("Trying to schedule a job for m2m_ctx: %p\n", m2m_ctx); - - if (!m2m_ctx->out_q_ctx.q.streaming - || !m2m_ctx->cap_q_ctx.q.streaming) { - dprintk("Streaming needs to be on for both queues\n"); - return; - } - - spin_lock_irqsave(&m2m_dev->job_spinlock, flags_job); - if (m2m_ctx->job_flags & TRANS_QUEUED) { - spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags_job); - dprintk("On job queue already\n"); - return; - } - - spin_lock_irqsave(&m2m_ctx->out_q_ctx.rdy_spinlock, flags); - if (list_empty(&m2m_ctx->out_q_ctx.rdy_queue)) { - spin_unlock_irqrestore(&m2m_ctx->out_q_ctx.rdy_spinlock, flags); - spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags_job); - dprintk("No input buffers available\n"); - return; - } - if (list_empty(&m2m_ctx->cap_q_ctx.rdy_queue)) { - spin_unlock_irqrestore(&m2m_ctx->out_q_ctx.rdy_spinlock, flags); - spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags_job); - dprintk("No output buffers available\n"); - return; - } - spin_unlock_irqrestore(&m2m_ctx->out_q_ctx.rdy_spinlock, flags); - - if (m2m_dev->m2m_ops->job_ready - && (!m2m_dev->m2m_ops->job_ready(m2m_ctx->priv))) { - spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags_job); - dprintk("Driver not ready\n"); - return; - } - - list_add_tail(&m2m_ctx->queue, &m2m_dev->job_queue); - m2m_ctx->job_flags |= TRANS_QUEUED; - - spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags_job); - - v4l2_m2m_try_run(m2m_dev); -} - -/** - * v4l2_m2m_job_finish() - inform the framework that a job has been finished - * and have it clean up - * - * Called by a driver to yield back the device after it has finished with it. - * Should be called as soon as possible after reaching a state which allows - * other instances to take control of the device. - * - * This function has to be called only after device_run() callback has been - * called on the driver. To prevent recursion, it should not be called directly - * from the device_run() callback though. - */ -void v4l2_m2m_job_finish(struct v4l2_m2m_dev *m2m_dev, - struct v4l2_m2m_ctx *m2m_ctx) -{ - unsigned long flags; - - spin_lock_irqsave(&m2m_dev->job_spinlock, flags); - if (!m2m_dev->curr_ctx || m2m_dev->curr_ctx != m2m_ctx) { - spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags); - dprintk("Called by an instance not currently running\n"); - return; - } - - list_del(&m2m_dev->curr_ctx->queue); - m2m_dev->curr_ctx->job_flags &= ~(TRANS_QUEUED | TRANS_RUNNING); - wake_up(&m2m_dev->curr_ctx->finished); - m2m_dev->curr_ctx = NULL; - - spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags); - - /* This instance might have more buffers ready, but since we do not - * allow more than one job on the job_queue per instance, each has - * to be scheduled separately after the previous one finishes. */ - v4l2_m2m_try_schedule(m2m_ctx); - v4l2_m2m_try_run(m2m_dev); -} -EXPORT_SYMBOL(v4l2_m2m_job_finish); - -/** - * v4l2_m2m_reqbufs() - multi-queue-aware REQBUFS multiplexer - */ -int v4l2_m2m_reqbufs(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, - struct v4l2_requestbuffers *reqbufs) -{ - struct vb2_queue *vq; - - vq = v4l2_m2m_get_vq(m2m_ctx, reqbufs->type); - return vb2_reqbufs(vq, reqbufs); -} -EXPORT_SYMBOL_GPL(v4l2_m2m_reqbufs); - -/** - * v4l2_m2m_querybuf() - multi-queue-aware QUERYBUF multiplexer - * - * See v4l2_m2m_mmap() documentation for details. - */ -int v4l2_m2m_querybuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, - struct v4l2_buffer *buf) -{ - struct vb2_queue *vq; - int ret = 0; - unsigned int i; - - vq = v4l2_m2m_get_vq(m2m_ctx, buf->type); - ret = vb2_querybuf(vq, buf); - - /* Adjust MMAP memory offsets for the CAPTURE queue */ - if (buf->memory == V4L2_MEMORY_MMAP && !V4L2_TYPE_IS_OUTPUT(vq->type)) { - if (V4L2_TYPE_IS_MULTIPLANAR(vq->type)) { - for (i = 0; i < buf->length; ++i) - buf->m.planes[i].m.mem_offset - += DST_QUEUE_OFF_BASE; - } else { - buf->m.offset += DST_QUEUE_OFF_BASE; - } - } - - return ret; -} -EXPORT_SYMBOL_GPL(v4l2_m2m_querybuf); - -/** - * v4l2_m2m_qbuf() - enqueue a source or destination buffer, depending on - * the type - */ -int v4l2_m2m_qbuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, - struct v4l2_buffer *buf) -{ - struct vb2_queue *vq; - int ret; - - vq = v4l2_m2m_get_vq(m2m_ctx, buf->type); - ret = vb2_qbuf(vq, buf); - if (!ret) - v4l2_m2m_try_schedule(m2m_ctx); - - return ret; -} -EXPORT_SYMBOL_GPL(v4l2_m2m_qbuf); - -/** - * v4l2_m2m_dqbuf() - dequeue a source or destination buffer, depending on - * the type - */ -int v4l2_m2m_dqbuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, - struct v4l2_buffer *buf) -{ - struct vb2_queue *vq; - - vq = v4l2_m2m_get_vq(m2m_ctx, buf->type); - return vb2_dqbuf(vq, buf, file->f_flags & O_NONBLOCK); -} -EXPORT_SYMBOL_GPL(v4l2_m2m_dqbuf); - -/** - * v4l2_m2m_streamon() - turn on streaming for a video queue - */ -int v4l2_m2m_streamon(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, - enum v4l2_buf_type type) -{ - struct vb2_queue *vq; - int ret; - - vq = v4l2_m2m_get_vq(m2m_ctx, type); - ret = vb2_streamon(vq, type); - if (!ret) - v4l2_m2m_try_schedule(m2m_ctx); - - return ret; -} -EXPORT_SYMBOL_GPL(v4l2_m2m_streamon); - -/** - * v4l2_m2m_streamoff() - turn off streaming for a video queue - */ -int v4l2_m2m_streamoff(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, - enum v4l2_buf_type type) -{ - struct vb2_queue *vq; - - vq = v4l2_m2m_get_vq(m2m_ctx, type); - return vb2_streamoff(vq, type); -} -EXPORT_SYMBOL_GPL(v4l2_m2m_streamoff); - -/** - * v4l2_m2m_poll() - poll replacement, for destination buffers only - * - * Call from the driver's poll() function. Will poll both queues. If a buffer - * is available to dequeue (with dqbuf) from the source queue, this will - * indicate that a non-blocking write can be performed, while read will be - * returned in case of the destination queue. - */ -unsigned int v4l2_m2m_poll(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, - struct poll_table_struct *wait) -{ - struct video_device *vfd = video_devdata(file); - unsigned long req_events = poll_requested_events(wait); - struct vb2_queue *src_q, *dst_q; - struct vb2_buffer *src_vb = NULL, *dst_vb = NULL; - unsigned int rc = 0; - unsigned long flags; - - if (test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags)) { - struct v4l2_fh *fh = file->private_data; - - if (v4l2_event_pending(fh)) - rc = POLLPRI; - else if (req_events & POLLPRI) - poll_wait(file, &fh->wait, wait); - if (!(req_events & (POLLOUT | POLLWRNORM | POLLIN | POLLRDNORM))) - return rc; - } - - src_q = v4l2_m2m_get_src_vq(m2m_ctx); - dst_q = v4l2_m2m_get_dst_vq(m2m_ctx); - - /* - * There has to be at least one buffer queued on each queued_list, which - * means either in driver already or waiting for driver to claim it - * and start processing. - */ - if ((!src_q->streaming || list_empty(&src_q->queued_list)) - && (!dst_q->streaming || list_empty(&dst_q->queued_list))) { - rc |= POLLERR; - goto end; - } - - if (m2m_ctx->m2m_dev->m2m_ops->unlock) - m2m_ctx->m2m_dev->m2m_ops->unlock(m2m_ctx->priv); - - poll_wait(file, &src_q->done_wq, wait); - poll_wait(file, &dst_q->done_wq, wait); - - if (m2m_ctx->m2m_dev->m2m_ops->lock) - m2m_ctx->m2m_dev->m2m_ops->lock(m2m_ctx->priv); - - spin_lock_irqsave(&src_q->done_lock, flags); - if (!list_empty(&src_q->done_list)) - src_vb = list_first_entry(&src_q->done_list, struct vb2_buffer, - done_entry); - if (src_vb && (src_vb->state == VB2_BUF_STATE_DONE - || src_vb->state == VB2_BUF_STATE_ERROR)) - rc |= POLLOUT | POLLWRNORM; - spin_unlock_irqrestore(&src_q->done_lock, flags); - - spin_lock_irqsave(&dst_q->done_lock, flags); - if (!list_empty(&dst_q->done_list)) - dst_vb = list_first_entry(&dst_q->done_list, struct vb2_buffer, - done_entry); - if (dst_vb && (dst_vb->state == VB2_BUF_STATE_DONE - || dst_vb->state == VB2_BUF_STATE_ERROR)) - rc |= POLLIN | POLLRDNORM; - spin_unlock_irqrestore(&dst_q->done_lock, flags); - -end: - return rc; -} -EXPORT_SYMBOL_GPL(v4l2_m2m_poll); - -/** - * v4l2_m2m_mmap() - source and destination queues-aware mmap multiplexer - * - * Call from driver's mmap() function. Will handle mmap() for both queues - * seamlessly for videobuffer, which will receive normal per-queue offsets and - * proper videobuf queue pointers. The differentiation is made outside videobuf - * by adding a predefined offset to buffers from one of the queues and - * subtracting it before passing it back to videobuf. Only drivers (and - * thus applications) receive modified offsets. - */ -int v4l2_m2m_mmap(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, - struct vm_area_struct *vma) -{ - unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; - struct vb2_queue *vq; - - if (offset < DST_QUEUE_OFF_BASE) { - vq = v4l2_m2m_get_src_vq(m2m_ctx); - } else { - vq = v4l2_m2m_get_dst_vq(m2m_ctx); - vma->vm_pgoff -= (DST_QUEUE_OFF_BASE >> PAGE_SHIFT); - } - - return vb2_mmap(vq, vma); -} -EXPORT_SYMBOL(v4l2_m2m_mmap); - -/** - * v4l2_m2m_init() - initialize per-driver m2m data - * - * Usually called from driver's probe() function. - */ -struct v4l2_m2m_dev *v4l2_m2m_init(struct v4l2_m2m_ops *m2m_ops) -{ - struct v4l2_m2m_dev *m2m_dev; - - if (!m2m_ops) - return ERR_PTR(-EINVAL); - - BUG_ON(!m2m_ops->device_run); - BUG_ON(!m2m_ops->job_abort); - - m2m_dev = kzalloc(sizeof *m2m_dev, GFP_KERNEL); - if (!m2m_dev) - return ERR_PTR(-ENOMEM); - - m2m_dev->curr_ctx = NULL; - m2m_dev->m2m_ops = m2m_ops; - INIT_LIST_HEAD(&m2m_dev->job_queue); - spin_lock_init(&m2m_dev->job_spinlock); - - return m2m_dev; -} -EXPORT_SYMBOL_GPL(v4l2_m2m_init); - -/** - * v4l2_m2m_release() - cleans up and frees a m2m_dev structure - * - * Usually called from driver's remove() function. - */ -void v4l2_m2m_release(struct v4l2_m2m_dev *m2m_dev) -{ - kfree(m2m_dev); -} -EXPORT_SYMBOL_GPL(v4l2_m2m_release); - -/** - * v4l2_m2m_ctx_init() - allocate and initialize a m2m context - * @priv - driver's instance private data - * @m2m_dev - a previously initialized m2m_dev struct - * @vq_init - a callback for queue type-specific initialization function to be - * used for initializing videobuf_queues - * - * Usually called from driver's open() function. - */ -struct v4l2_m2m_ctx *v4l2_m2m_ctx_init(struct v4l2_m2m_dev *m2m_dev, - void *drv_priv, - int (*queue_init)(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq)) -{ - struct v4l2_m2m_ctx *m2m_ctx; - struct v4l2_m2m_queue_ctx *out_q_ctx, *cap_q_ctx; - int ret; - - m2m_ctx = kzalloc(sizeof *m2m_ctx, GFP_KERNEL); - if (!m2m_ctx) - return ERR_PTR(-ENOMEM); - - m2m_ctx->priv = drv_priv; - m2m_ctx->m2m_dev = m2m_dev; - init_waitqueue_head(&m2m_ctx->finished); - - out_q_ctx = &m2m_ctx->out_q_ctx; - cap_q_ctx = &m2m_ctx->cap_q_ctx; - - INIT_LIST_HEAD(&out_q_ctx->rdy_queue); - INIT_LIST_HEAD(&cap_q_ctx->rdy_queue); - spin_lock_init(&out_q_ctx->rdy_spinlock); - spin_lock_init(&cap_q_ctx->rdy_spinlock); - - INIT_LIST_HEAD(&m2m_ctx->queue); - - ret = queue_init(drv_priv, &out_q_ctx->q, &cap_q_ctx->q); - - if (ret) - goto err; - - return m2m_ctx; -err: - kfree(m2m_ctx); - return ERR_PTR(ret); -} -EXPORT_SYMBOL_GPL(v4l2_m2m_ctx_init); - -/** - * v4l2_m2m_ctx_release() - release m2m context - * - * Usually called from driver's release() function. - */ -void v4l2_m2m_ctx_release(struct v4l2_m2m_ctx *m2m_ctx) -{ - struct v4l2_m2m_dev *m2m_dev; - unsigned long flags; - - m2m_dev = m2m_ctx->m2m_dev; - - spin_lock_irqsave(&m2m_dev->job_spinlock, flags); - if (m2m_ctx->job_flags & TRANS_RUNNING) { - spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags); - m2m_dev->m2m_ops->job_abort(m2m_ctx->priv); - dprintk("m2m_ctx %p running, will wait to complete", m2m_ctx); - wait_event(m2m_ctx->finished, !(m2m_ctx->job_flags & TRANS_RUNNING)); - } else if (m2m_ctx->job_flags & TRANS_QUEUED) { - list_del(&m2m_ctx->queue); - m2m_ctx->job_flags &= ~(TRANS_QUEUED | TRANS_RUNNING); - spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags); - dprintk("m2m_ctx: %p had been on queue and was removed\n", - m2m_ctx); - } else { - /* Do nothing, was not on queue/running */ - spin_unlock_irqrestore(&m2m_dev->job_spinlock, flags); - } - - vb2_queue_release(&m2m_ctx->cap_q_ctx.q); - vb2_queue_release(&m2m_ctx->out_q_ctx.q); - - kfree(m2m_ctx); -} -EXPORT_SYMBOL_GPL(v4l2_m2m_ctx_release); - -/** - * v4l2_m2m_buf_queue() - add a buffer to the proper ready buffers list. - * - * Call from buf_queue(), videobuf_queue_ops callback. - */ -void v4l2_m2m_buf_queue(struct v4l2_m2m_ctx *m2m_ctx, struct vb2_buffer *vb) -{ - struct v4l2_m2m_buffer *b = container_of(vb, struct v4l2_m2m_buffer, vb); - struct v4l2_m2m_queue_ctx *q_ctx; - unsigned long flags; - - q_ctx = get_queue_ctx(m2m_ctx, vb->vb2_queue->type); - if (!q_ctx) - return; - - spin_lock_irqsave(&q_ctx->rdy_spinlock, flags); - list_add_tail(&b->list, &q_ctx->rdy_queue); - q_ctx->num_rdy++; - spin_unlock_irqrestore(&q_ctx->rdy_spinlock, flags); -} -EXPORT_SYMBOL_GPL(v4l2_m2m_buf_queue); - diff --git a/drivers/media/video/v4l2-subdev.c b/drivers/media/video/v4l2-subdev.c deleted file mode 100644 index 9182f81deb5b..000000000000 --- a/drivers/media/video/v4l2-subdev.c +++ /dev/null @@ -1,470 +0,0 @@ -/* - * V4L2 sub-device - * - * Copyright (C) 2010 Nokia Corporation - * - * Contact: Laurent Pinchart <laurent.pinchart@ideasonboard.com> - * Sakari Ailus <sakari.ailus@iki.fi> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * 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 <linux/ioctl.h> -#include <linux/slab.h> -#include <linux/types.h> -#include <linux/videodev2.h> -#include <linux/export.h> - -#include <media/v4l2-ctrls.h> -#include <media/v4l2-device.h> -#include <media/v4l2-ioctl.h> -#include <media/v4l2-fh.h> -#include <media/v4l2-event.h> - -static int subdev_fh_init(struct v4l2_subdev_fh *fh, struct v4l2_subdev *sd) -{ -#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) - fh->pad = kzalloc(sizeof(*fh->pad) * sd->entity.num_pads, GFP_KERNEL); - if (fh->pad == NULL) - return -ENOMEM; -#endif - return 0; -} - -static void subdev_fh_free(struct v4l2_subdev_fh *fh) -{ -#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) - kfree(fh->pad); - fh->pad = NULL; -#endif -} - -static int subdev_open(struct file *file) -{ - struct video_device *vdev = video_devdata(file); - struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev); - struct v4l2_subdev_fh *subdev_fh; -#if defined(CONFIG_MEDIA_CONTROLLER) - struct media_entity *entity = NULL; -#endif - int ret; - - subdev_fh = kzalloc(sizeof(*subdev_fh), GFP_KERNEL); - if (subdev_fh == NULL) - return -ENOMEM; - - ret = subdev_fh_init(subdev_fh, sd); - if (ret) { - kfree(subdev_fh); - return ret; - } - - v4l2_fh_init(&subdev_fh->vfh, vdev); - v4l2_fh_add(&subdev_fh->vfh); - file->private_data = &subdev_fh->vfh; -#if defined(CONFIG_MEDIA_CONTROLLER) - if (sd->v4l2_dev->mdev) { - entity = media_entity_get(&sd->entity); - if (!entity) { - ret = -EBUSY; - goto err; - } - } -#endif - - if (sd->internal_ops && sd->internal_ops->open) { - ret = sd->internal_ops->open(sd, subdev_fh); - if (ret < 0) - goto err; - } - - return 0; - -err: -#if defined(CONFIG_MEDIA_CONTROLLER) - if (entity) - media_entity_put(entity); -#endif - v4l2_fh_del(&subdev_fh->vfh); - v4l2_fh_exit(&subdev_fh->vfh); - subdev_fh_free(subdev_fh); - kfree(subdev_fh); - - return ret; -} - -static int subdev_close(struct file *file) -{ - struct video_device *vdev = video_devdata(file); - struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev); - struct v4l2_fh *vfh = file->private_data; - struct v4l2_subdev_fh *subdev_fh = to_v4l2_subdev_fh(vfh); - - if (sd->internal_ops && sd->internal_ops->close) - sd->internal_ops->close(sd, subdev_fh); -#if defined(CONFIG_MEDIA_CONTROLLER) - if (sd->v4l2_dev->mdev) - media_entity_put(&sd->entity); -#endif - v4l2_fh_del(vfh); - v4l2_fh_exit(vfh); - subdev_fh_free(subdev_fh); - kfree(subdev_fh); - file->private_data = NULL; - - return 0; -} - -static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) -{ - struct video_device *vdev = video_devdata(file); - struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev); - struct v4l2_fh *vfh = file->private_data; -#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) - struct v4l2_subdev_fh *subdev_fh = to_v4l2_subdev_fh(vfh); -#endif - - switch (cmd) { - case VIDIOC_QUERYCTRL: - return v4l2_queryctrl(vfh->ctrl_handler, arg); - - case VIDIOC_QUERYMENU: - return v4l2_querymenu(vfh->ctrl_handler, arg); - - case VIDIOC_G_CTRL: - return v4l2_g_ctrl(vfh->ctrl_handler, arg); - - case VIDIOC_S_CTRL: - return v4l2_s_ctrl(vfh, vfh->ctrl_handler, arg); - - case VIDIOC_G_EXT_CTRLS: - return v4l2_g_ext_ctrls(vfh->ctrl_handler, arg); - - case VIDIOC_S_EXT_CTRLS: - return v4l2_s_ext_ctrls(vfh, vfh->ctrl_handler, arg); - - case VIDIOC_TRY_EXT_CTRLS: - return v4l2_try_ext_ctrls(vfh->ctrl_handler, arg); - - case VIDIOC_DQEVENT: - if (!(sd->flags & V4L2_SUBDEV_FL_HAS_EVENTS)) - return -ENOIOCTLCMD; - - return v4l2_event_dequeue(vfh, arg, file->f_flags & O_NONBLOCK); - - case VIDIOC_SUBSCRIBE_EVENT: - return v4l2_subdev_call(sd, core, subscribe_event, vfh, arg); - - case VIDIOC_UNSUBSCRIBE_EVENT: - return v4l2_subdev_call(sd, core, unsubscribe_event, vfh, arg); - -#ifdef CONFIG_VIDEO_ADV_DEBUG - case VIDIOC_DBG_G_REGISTER: - { - struct v4l2_dbg_register *p = arg; - - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - return v4l2_subdev_call(sd, core, g_register, p); - } - case VIDIOC_DBG_S_REGISTER: - { - struct v4l2_dbg_register *p = arg; - - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - return v4l2_subdev_call(sd, core, s_register, p); - } -#endif - - case VIDIOC_LOG_STATUS: { - int ret; - - pr_info("%s: ================= START STATUS =================\n", - sd->name); - ret = v4l2_subdev_call(sd, core, log_status); - pr_info("%s: ================== END STATUS ==================\n", - sd->name); - return ret; - } - -#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) - case VIDIOC_SUBDEV_G_FMT: { - struct v4l2_subdev_format *format = arg; - - if (format->which != V4L2_SUBDEV_FORMAT_TRY && - format->which != V4L2_SUBDEV_FORMAT_ACTIVE) - return -EINVAL; - - if (format->pad >= sd->entity.num_pads) - return -EINVAL; - - return v4l2_subdev_call(sd, pad, get_fmt, subdev_fh, format); - } - - case VIDIOC_SUBDEV_S_FMT: { - struct v4l2_subdev_format *format = arg; - - if (format->which != V4L2_SUBDEV_FORMAT_TRY && - format->which != V4L2_SUBDEV_FORMAT_ACTIVE) - return -EINVAL; - - if (format->pad >= sd->entity.num_pads) - return -EINVAL; - - return v4l2_subdev_call(sd, pad, set_fmt, subdev_fh, format); - } - - case VIDIOC_SUBDEV_G_CROP: { - struct v4l2_subdev_crop *crop = arg; - struct v4l2_subdev_selection sel; - int rval; - - if (crop->which != V4L2_SUBDEV_FORMAT_TRY && - crop->which != V4L2_SUBDEV_FORMAT_ACTIVE) - return -EINVAL; - - if (crop->pad >= sd->entity.num_pads) - return -EINVAL; - - rval = v4l2_subdev_call(sd, pad, get_crop, subdev_fh, crop); - if (rval != -ENOIOCTLCMD) - return rval; - - memset(&sel, 0, sizeof(sel)); - sel.which = crop->which; - sel.pad = crop->pad; - sel.target = V4L2_SEL_TGT_CROP; - - rval = v4l2_subdev_call( - sd, pad, get_selection, subdev_fh, &sel); - - crop->rect = sel.r; - - return rval; - } - - case VIDIOC_SUBDEV_S_CROP: { - struct v4l2_subdev_crop *crop = arg; - struct v4l2_subdev_selection sel; - int rval; - - if (crop->which != V4L2_SUBDEV_FORMAT_TRY && - crop->which != V4L2_SUBDEV_FORMAT_ACTIVE) - return -EINVAL; - - if (crop->pad >= sd->entity.num_pads) - return -EINVAL; - - rval = v4l2_subdev_call(sd, pad, set_crop, subdev_fh, crop); - if (rval != -ENOIOCTLCMD) - return rval; - - memset(&sel, 0, sizeof(sel)); - sel.which = crop->which; - sel.pad = crop->pad; - sel.target = V4L2_SEL_TGT_CROP; - sel.r = crop->rect; - - rval = v4l2_subdev_call( - sd, pad, set_selection, subdev_fh, &sel); - - crop->rect = sel.r; - - return rval; - } - - case VIDIOC_SUBDEV_ENUM_MBUS_CODE: { - struct v4l2_subdev_mbus_code_enum *code = arg; - - if (code->pad >= sd->entity.num_pads) - return -EINVAL; - - return v4l2_subdev_call(sd, pad, enum_mbus_code, subdev_fh, - code); - } - - case VIDIOC_SUBDEV_ENUM_FRAME_SIZE: { - struct v4l2_subdev_frame_size_enum *fse = arg; - - if (fse->pad >= sd->entity.num_pads) - return -EINVAL; - - return v4l2_subdev_call(sd, pad, enum_frame_size, subdev_fh, - fse); - } - - case VIDIOC_SUBDEV_G_FRAME_INTERVAL: - return v4l2_subdev_call(sd, video, g_frame_interval, arg); - - case VIDIOC_SUBDEV_S_FRAME_INTERVAL: - return v4l2_subdev_call(sd, video, s_frame_interval, arg); - - case VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL: { - struct v4l2_subdev_frame_interval_enum *fie = arg; - - if (fie->pad >= sd->entity.num_pads) - return -EINVAL; - - return v4l2_subdev_call(sd, pad, enum_frame_interval, subdev_fh, - fie); - } - - case VIDIOC_SUBDEV_G_SELECTION: { - struct v4l2_subdev_selection *sel = arg; - - if (sel->which != V4L2_SUBDEV_FORMAT_TRY && - sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) - return -EINVAL; - - if (sel->pad >= sd->entity.num_pads) - return -EINVAL; - - return v4l2_subdev_call( - sd, pad, get_selection, subdev_fh, sel); - } - - case VIDIOC_SUBDEV_S_SELECTION: { - struct v4l2_subdev_selection *sel = arg; - - if (sel->which != V4L2_SUBDEV_FORMAT_TRY && - sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) - return -EINVAL; - - if (sel->pad >= sd->entity.num_pads) - return -EINVAL; - - return v4l2_subdev_call( - sd, pad, set_selection, subdev_fh, sel); - } -#endif - default: - return v4l2_subdev_call(sd, core, ioctl, cmd, arg); - } - - return 0; -} - -static long subdev_ioctl(struct file *file, unsigned int cmd, - unsigned long arg) -{ - return video_usercopy(file, cmd, arg, subdev_do_ioctl); -} - -static unsigned int subdev_poll(struct file *file, poll_table *wait) -{ - struct video_device *vdev = video_devdata(file); - struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev); - struct v4l2_fh *fh = file->private_data; - - if (!(sd->flags & V4L2_SUBDEV_FL_HAS_EVENTS)) - return POLLERR; - - poll_wait(file, &fh->wait, wait); - - if (v4l2_event_pending(fh)) - return POLLPRI; - - return 0; -} - -const struct v4l2_file_operations v4l2_subdev_fops = { - .owner = THIS_MODULE, - .open = subdev_open, - .unlocked_ioctl = subdev_ioctl, - .release = subdev_close, - .poll = subdev_poll, -}; - -#ifdef CONFIG_MEDIA_CONTROLLER -int v4l2_subdev_link_validate_default(struct v4l2_subdev *sd, - struct media_link *link, - struct v4l2_subdev_format *source_fmt, - struct v4l2_subdev_format *sink_fmt) -{ - if (source_fmt->format.width != sink_fmt->format.width - || source_fmt->format.height != sink_fmt->format.height - || source_fmt->format.code != sink_fmt->format.code) - return -EINVAL; - - return 0; -} -EXPORT_SYMBOL_GPL(v4l2_subdev_link_validate_default); - -static int -v4l2_subdev_link_validate_get_format(struct media_pad *pad, - struct v4l2_subdev_format *fmt) -{ - switch (media_entity_type(pad->entity)) { - case MEDIA_ENT_T_V4L2_SUBDEV: - fmt->which = V4L2_SUBDEV_FORMAT_ACTIVE; - fmt->pad = pad->index; - return v4l2_subdev_call(media_entity_to_v4l2_subdev( - pad->entity), - pad, get_fmt, NULL, fmt); - default: - WARN(1, "Driver bug! Wrong media entity type %d, entity %s\n", - media_entity_type(pad->entity), pad->entity->name); - /* Fall through */ - case MEDIA_ENT_T_DEVNODE_V4L: - return -EINVAL; - } -} - -int v4l2_subdev_link_validate(struct media_link *link) -{ - struct v4l2_subdev *sink; - struct v4l2_subdev_format sink_fmt, source_fmt; - int rval; - - rval = v4l2_subdev_link_validate_get_format( - link->source, &source_fmt); - if (rval < 0) - return 0; - - rval = v4l2_subdev_link_validate_get_format( - link->sink, &sink_fmt); - if (rval < 0) - return 0; - - sink = media_entity_to_v4l2_subdev(link->sink->entity); - - rval = v4l2_subdev_call(sink, pad, link_validate, link, - &source_fmt, &sink_fmt); - if (rval != -ENOIOCTLCMD) - return rval; - - return v4l2_subdev_link_validate_default( - sink, link, &source_fmt, &sink_fmt); -} -EXPORT_SYMBOL_GPL(v4l2_subdev_link_validate); -#endif /* CONFIG_MEDIA_CONTROLLER */ - -void v4l2_subdev_init(struct v4l2_subdev *sd, const struct v4l2_subdev_ops *ops) -{ - INIT_LIST_HEAD(&sd->list); - BUG_ON(!ops); - sd->ops = ops; - sd->v4l2_dev = NULL; - sd->flags = 0; - sd->name[0] = '\0'; - sd->grp_id = 0; - sd->dev_priv = NULL; - sd->host_priv = NULL; -#if defined(CONFIG_MEDIA_CONTROLLER) - sd->entity.name = sd->name; - sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV; -#endif -} -EXPORT_SYMBOL(v4l2_subdev_init); diff --git a/drivers/media/video/videobuf-core.c b/drivers/media/video/videobuf-core.c deleted file mode 100644 index bf7a326b1cdc..000000000000 --- a/drivers/media/video/videobuf-core.c +++ /dev/null @@ -1,1189 +0,0 @@ -/* - * generic helper functions for handling video4linux capture buffers - * - * (c) 2007 Mauro Carvalho Chehab, <mchehab@infradead.org> - * - * Highly based on video-buf written originally by: - * (c) 2001,02 Gerd Knorr <kraxel@bytesex.org> - * (c) 2006 Mauro Carvalho Chehab, <mchehab@infradead.org> - * (c) 2006 Ted Walther and John Sokol - * - * 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 - */ - -#include <linux/init.h> -#include <linux/module.h> -#include <linux/moduleparam.h> -#include <linux/mm.h> -#include <linux/sched.h> -#include <linux/slab.h> -#include <linux/interrupt.h> - -#include <media/videobuf-core.h> - -#define MAGIC_BUFFER 0x20070728 -#define MAGIC_CHECK(is, should) \ - do { \ - if (unlikely((is) != (should))) { \ - printk(KERN_ERR \ - "magic mismatch: %x (expected %x)\n", \ - is, should); \ - BUG(); \ - } \ - } while (0) - -static int debug; -module_param(debug, int, 0644); - -MODULE_DESCRIPTION("helper module to manage video4linux buffers"); -MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>"); -MODULE_LICENSE("GPL"); - -#define dprintk(level, fmt, arg...) \ - do { \ - if (debug >= level) \ - printk(KERN_DEBUG "vbuf: " fmt, ## arg); \ - } while (0) - -/* --------------------------------------------------------------------- */ - -#define CALL(q, f, arg...) \ - ((q->int_ops->f) ? q->int_ops->f(arg) : 0) - -struct videobuf_buffer *videobuf_alloc_vb(struct videobuf_queue *q) -{ - struct videobuf_buffer *vb; - - BUG_ON(q->msize < sizeof(*vb)); - - if (!q->int_ops || !q->int_ops->alloc_vb) { - printk(KERN_ERR "No specific ops defined!\n"); - BUG(); - } - - vb = q->int_ops->alloc_vb(q->msize); - if (NULL != vb) { - init_waitqueue_head(&vb->done); - vb->magic = MAGIC_BUFFER; - } - - return vb; -} -EXPORT_SYMBOL_GPL(videobuf_alloc_vb); - -static int is_state_active_or_queued(struct videobuf_queue *q, struct videobuf_buffer *vb) -{ - unsigned long flags; - bool rc; - - spin_lock_irqsave(q->irqlock, flags); - rc = vb->state != VIDEOBUF_ACTIVE && vb->state != VIDEOBUF_QUEUED; - spin_unlock_irqrestore(q->irqlock, flags); - return rc; -}; - -int videobuf_waiton(struct videobuf_queue *q, struct videobuf_buffer *vb, - int non_blocking, int intr) -{ - bool is_ext_locked; - int ret = 0; - - MAGIC_CHECK(vb->magic, MAGIC_BUFFER); - - if (non_blocking) { - if (is_state_active_or_queued(q, vb)) - return 0; - return -EAGAIN; - } - - is_ext_locked = q->ext_lock && mutex_is_locked(q->ext_lock); - - /* Release vdev lock to prevent this wait from blocking outside access to - the device. */ - if (is_ext_locked) - mutex_unlock(q->ext_lock); - if (intr) - ret = wait_event_interruptible(vb->done, is_state_active_or_queued(q, vb)); - else - wait_event(vb->done, is_state_active_or_queued(q, vb)); - /* Relock */ - if (is_ext_locked) - mutex_lock(q->ext_lock); - - return ret; -} -EXPORT_SYMBOL_GPL(videobuf_waiton); - -int videobuf_iolock(struct videobuf_queue *q, struct videobuf_buffer *vb, - struct v4l2_framebuffer *fbuf) -{ - MAGIC_CHECK(vb->magic, MAGIC_BUFFER); - MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS); - - return CALL(q, iolock, q, vb, fbuf); -} -EXPORT_SYMBOL_GPL(videobuf_iolock); - -void *videobuf_queue_to_vaddr(struct videobuf_queue *q, - struct videobuf_buffer *buf) -{ - if (q->int_ops->vaddr) - return q->int_ops->vaddr(buf); - return NULL; -} -EXPORT_SYMBOL_GPL(videobuf_queue_to_vaddr); - -/* --------------------------------------------------------------------- */ - - -void videobuf_queue_core_init(struct videobuf_queue *q, - const struct videobuf_queue_ops *ops, - struct device *dev, - spinlock_t *irqlock, - enum v4l2_buf_type type, - enum v4l2_field field, - unsigned int msize, - void *priv, - struct videobuf_qtype_ops *int_ops, - struct mutex *ext_lock) -{ - BUG_ON(!q); - memset(q, 0, sizeof(*q)); - q->irqlock = irqlock; - q->ext_lock = ext_lock; - q->dev = dev; - q->type = type; - q->field = field; - q->msize = msize; - q->ops = ops; - q->priv_data = priv; - q->int_ops = int_ops; - - /* All buffer operations are mandatory */ - BUG_ON(!q->ops->buf_setup); - BUG_ON(!q->ops->buf_prepare); - BUG_ON(!q->ops->buf_queue); - BUG_ON(!q->ops->buf_release); - - /* Lock is mandatory for queue_cancel to work */ - BUG_ON(!irqlock); - - /* Having implementations for abstract methods are mandatory */ - BUG_ON(!q->int_ops); - - mutex_init(&q->vb_lock); - init_waitqueue_head(&q->wait); - INIT_LIST_HEAD(&q->stream); -} -EXPORT_SYMBOL_GPL(videobuf_queue_core_init); - -/* Locking: Only usage in bttv unsafe find way to remove */ -int videobuf_queue_is_busy(struct videobuf_queue *q) -{ - int i; - - MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS); - - if (q->streaming) { - dprintk(1, "busy: streaming active\n"); - return 1; - } - if (q->reading) { - dprintk(1, "busy: pending read #1\n"); - return 1; - } - if (q->read_buf) { - dprintk(1, "busy: pending read #2\n"); - return 1; - } - for (i = 0; i < VIDEO_MAX_FRAME; i++) { - if (NULL == q->bufs[i]) - continue; - if (q->bufs[i]->map) { - dprintk(1, "busy: buffer #%d mapped\n", i); - return 1; - } - if (q->bufs[i]->state == VIDEOBUF_QUEUED) { - dprintk(1, "busy: buffer #%d queued\n", i); - return 1; - } - if (q->bufs[i]->state == VIDEOBUF_ACTIVE) { - dprintk(1, "busy: buffer #%d avtive\n", i); - return 1; - } - } - return 0; -} -EXPORT_SYMBOL_GPL(videobuf_queue_is_busy); - -/** - * __videobuf_free() - free all the buffers and their control structures - * - * This function can only be called if streaming/reading is off, i.e. no buffers - * are under control of the driver. - */ -/* Locking: Caller holds q->vb_lock */ -static int __videobuf_free(struct videobuf_queue *q) -{ - int i; - - dprintk(1, "%s\n", __func__); - if (!q) - return 0; - - if (q->streaming || q->reading) { - dprintk(1, "Cannot free buffers when streaming or reading\n"); - return -EBUSY; - } - - MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS); - - for (i = 0; i < VIDEO_MAX_FRAME; i++) - if (q->bufs[i] && q->bufs[i]->map) { - dprintk(1, "Cannot free mmapped buffers\n"); - return -EBUSY; - } - - for (i = 0; i < VIDEO_MAX_FRAME; i++) { - if (NULL == q->bufs[i]) - continue; - q->ops->buf_release(q, q->bufs[i]); - kfree(q->bufs[i]); - q->bufs[i] = NULL; - } - - return 0; -} - -/* Locking: Caller holds q->vb_lock */ -void videobuf_queue_cancel(struct videobuf_queue *q) -{ - unsigned long flags = 0; - int i; - - q->streaming = 0; - q->reading = 0; - wake_up_interruptible_sync(&q->wait); - - /* remove queued buffers from list */ - spin_lock_irqsave(q->irqlock, flags); - for (i = 0; i < VIDEO_MAX_FRAME; i++) { - if (NULL == q->bufs[i]) - continue; - if (q->bufs[i]->state == VIDEOBUF_QUEUED) { - list_del(&q->bufs[i]->queue); - q->bufs[i]->state = VIDEOBUF_ERROR; - wake_up_all(&q->bufs[i]->done); - } - } - spin_unlock_irqrestore(q->irqlock, flags); - - /* free all buffers + clear queue */ - for (i = 0; i < VIDEO_MAX_FRAME; i++) { - if (NULL == q->bufs[i]) - continue; - q->ops->buf_release(q, q->bufs[i]); - } - INIT_LIST_HEAD(&q->stream); -} -EXPORT_SYMBOL_GPL(videobuf_queue_cancel); - -/* --------------------------------------------------------------------- */ - -/* Locking: Caller holds q->vb_lock */ -enum v4l2_field videobuf_next_field(struct videobuf_queue *q) -{ - enum v4l2_field field = q->field; - - BUG_ON(V4L2_FIELD_ANY == field); - - if (V4L2_FIELD_ALTERNATE == field) { - if (V4L2_FIELD_TOP == q->last) { - field = V4L2_FIELD_BOTTOM; - q->last = V4L2_FIELD_BOTTOM; - } else { - field = V4L2_FIELD_TOP; - q->last = V4L2_FIELD_TOP; - } - } - return field; -} -EXPORT_SYMBOL_GPL(videobuf_next_field); - -/* Locking: Caller holds q->vb_lock */ -static void videobuf_status(struct videobuf_queue *q, struct v4l2_buffer *b, - struct videobuf_buffer *vb, enum v4l2_buf_type type) -{ - MAGIC_CHECK(vb->magic, MAGIC_BUFFER); - MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS); - - b->index = vb->i; - b->type = type; - - b->memory = vb->memory; - switch (b->memory) { - case V4L2_MEMORY_MMAP: - b->m.offset = vb->boff; - b->length = vb->bsize; - break; - case V4L2_MEMORY_USERPTR: - b->m.userptr = vb->baddr; - b->length = vb->bsize; - break; - case V4L2_MEMORY_OVERLAY: - b->m.offset = vb->boff; - break; - } - - b->flags = 0; - if (vb->map) - b->flags |= V4L2_BUF_FLAG_MAPPED; - - switch (vb->state) { - case VIDEOBUF_PREPARED: - case VIDEOBUF_QUEUED: - case VIDEOBUF_ACTIVE: - b->flags |= V4L2_BUF_FLAG_QUEUED; - break; - case VIDEOBUF_ERROR: - b->flags |= V4L2_BUF_FLAG_ERROR; - /* fall through */ - case VIDEOBUF_DONE: - b->flags |= V4L2_BUF_FLAG_DONE; - break; - case VIDEOBUF_NEEDS_INIT: - case VIDEOBUF_IDLE: - /* nothing */ - break; - } - - b->field = vb->field; - b->timestamp = vb->ts; - b->bytesused = vb->size; - b->sequence = vb->field_count >> 1; -} - -int videobuf_mmap_free(struct videobuf_queue *q) -{ - int ret; - videobuf_queue_lock(q); - ret = __videobuf_free(q); - videobuf_queue_unlock(q); - return ret; -} -EXPORT_SYMBOL_GPL(videobuf_mmap_free); - -/* Locking: Caller holds q->vb_lock */ -int __videobuf_mmap_setup(struct videobuf_queue *q, - unsigned int bcount, unsigned int bsize, - enum v4l2_memory memory) -{ - unsigned int i; - int err; - - MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS); - - err = __videobuf_free(q); - if (0 != err) - return err; - - /* Allocate and initialize buffers */ - for (i = 0; i < bcount; i++) { - q->bufs[i] = videobuf_alloc_vb(q); - - if (NULL == q->bufs[i]) - break; - - q->bufs[i]->i = i; - q->bufs[i]->memory = memory; - q->bufs[i]->bsize = bsize; - switch (memory) { - case V4L2_MEMORY_MMAP: - q->bufs[i]->boff = PAGE_ALIGN(bsize) * i; - break; - case V4L2_MEMORY_USERPTR: - case V4L2_MEMORY_OVERLAY: - /* nothing */ - break; - } - } - - if (!i) - return -ENOMEM; - - dprintk(1, "mmap setup: %d buffers, %d bytes each\n", i, bsize); - - return i; -} -EXPORT_SYMBOL_GPL(__videobuf_mmap_setup); - -int videobuf_mmap_setup(struct videobuf_queue *q, - unsigned int bcount, unsigned int bsize, - enum v4l2_memory memory) -{ - int ret; - videobuf_queue_lock(q); - ret = __videobuf_mmap_setup(q, bcount, bsize, memory); - videobuf_queue_unlock(q); - return ret; -} -EXPORT_SYMBOL_GPL(videobuf_mmap_setup); - -int videobuf_reqbufs(struct videobuf_queue *q, - struct v4l2_requestbuffers *req) -{ - unsigned int size, count; - int retval; - - if (req->count < 1) { - dprintk(1, "reqbufs: count invalid (%d)\n", req->count); - return -EINVAL; - } - - if (req->memory != V4L2_MEMORY_MMAP && - req->memory != V4L2_MEMORY_USERPTR && - req->memory != V4L2_MEMORY_OVERLAY) { - dprintk(1, "reqbufs: memory type invalid\n"); - return -EINVAL; - } - - videobuf_queue_lock(q); - if (req->type != q->type) { - dprintk(1, "reqbufs: queue type invalid\n"); - retval = -EINVAL; - goto done; - } - - if (q->streaming) { - dprintk(1, "reqbufs: streaming already exists\n"); - retval = -EBUSY; - goto done; - } - if (!list_empty(&q->stream)) { - dprintk(1, "reqbufs: stream running\n"); - retval = -EBUSY; - goto done; - } - - count = req->count; - if (count > VIDEO_MAX_FRAME) - count = VIDEO_MAX_FRAME; - size = 0; - q->ops->buf_setup(q, &count, &size); - dprintk(1, "reqbufs: bufs=%d, size=0x%x [%u pages total]\n", - count, size, - (unsigned int)((count * PAGE_ALIGN(size)) >> PAGE_SHIFT)); - - retval = __videobuf_mmap_setup(q, count, size, req->memory); - if (retval < 0) { - dprintk(1, "reqbufs: mmap setup returned %d\n", retval); - goto done; - } - - req->count = retval; - retval = 0; - - done: - videobuf_queue_unlock(q); - return retval; -} -EXPORT_SYMBOL_GPL(videobuf_reqbufs); - -int videobuf_querybuf(struct videobuf_queue *q, struct v4l2_buffer *b) -{ - int ret = -EINVAL; - - videobuf_queue_lock(q); - if (unlikely(b->type != q->type)) { - dprintk(1, "querybuf: Wrong type.\n"); - goto done; - } - if (unlikely(b->index >= VIDEO_MAX_FRAME)) { - dprintk(1, "querybuf: index out of range.\n"); - goto done; - } - if (unlikely(NULL == q->bufs[b->index])) { - dprintk(1, "querybuf: buffer is null.\n"); - goto done; - } - - videobuf_status(q, b, q->bufs[b->index], q->type); - - ret = 0; -done: - videobuf_queue_unlock(q); - return ret; -} -EXPORT_SYMBOL_GPL(videobuf_querybuf); - -int videobuf_qbuf(struct videobuf_queue *q, struct v4l2_buffer *b) -{ - struct videobuf_buffer *buf; - enum v4l2_field field; - unsigned long flags = 0; - int retval; - - MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS); - - if (b->memory == V4L2_MEMORY_MMAP) - down_read(¤t->mm->mmap_sem); - - videobuf_queue_lock(q); - retval = -EBUSY; - if (q->reading) { - dprintk(1, "qbuf: Reading running...\n"); - goto done; - } - retval = -EINVAL; - if (b->type != q->type) { - dprintk(1, "qbuf: Wrong type.\n"); - goto done; - } - if (b->index >= VIDEO_MAX_FRAME) { - dprintk(1, "qbuf: index out of range.\n"); - goto done; - } - buf = q->bufs[b->index]; - if (NULL == buf) { - dprintk(1, "qbuf: buffer is null.\n"); - goto done; - } - MAGIC_CHECK(buf->magic, MAGIC_BUFFER); - if (buf->memory != b->memory) { - dprintk(1, "qbuf: memory type is wrong.\n"); - goto done; - } - if (buf->state != VIDEOBUF_NEEDS_INIT && buf->state != VIDEOBUF_IDLE) { - dprintk(1, "qbuf: buffer is already queued or active.\n"); - goto done; - } - - switch (b->memory) { - case V4L2_MEMORY_MMAP: - if (0 == buf->baddr) { - dprintk(1, "qbuf: mmap requested " - "but buffer addr is zero!\n"); - goto done; - } - if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT - || q->type == V4L2_BUF_TYPE_VBI_OUTPUT - || q->type == V4L2_BUF_TYPE_SLICED_VBI_OUTPUT) { - buf->size = b->bytesused; - buf->field = b->field; - buf->ts = b->timestamp; - } - break; - case V4L2_MEMORY_USERPTR: - if (b->length < buf->bsize) { - dprintk(1, "qbuf: buffer length is not enough\n"); - goto done; - } - if (VIDEOBUF_NEEDS_INIT != buf->state && - buf->baddr != b->m.userptr) - q->ops->buf_release(q, buf); - buf->baddr = b->m.userptr; - break; - case V4L2_MEMORY_OVERLAY: - buf->boff = b->m.offset; - break; - default: - dprintk(1, "qbuf: wrong memory type\n"); - goto done; - } - - dprintk(1, "qbuf: requesting next field\n"); - field = videobuf_next_field(q); - retval = q->ops->buf_prepare(q, buf, field); - if (0 != retval) { - dprintk(1, "qbuf: buffer_prepare returned %d\n", retval); - goto done; - } - - list_add_tail(&buf->stream, &q->stream); - if (q->streaming) { - spin_lock_irqsave(q->irqlock, flags); - q->ops->buf_queue(q, buf); - spin_unlock_irqrestore(q->irqlock, flags); - } - dprintk(1, "qbuf: succeeded\n"); - retval = 0; - wake_up_interruptible_sync(&q->wait); - -done: - videobuf_queue_unlock(q); - - if (b->memory == V4L2_MEMORY_MMAP) - up_read(¤t->mm->mmap_sem); - - return retval; -} -EXPORT_SYMBOL_GPL(videobuf_qbuf); - -/* Locking: Caller holds q->vb_lock */ -static int stream_next_buffer_check_queue(struct videobuf_queue *q, int noblock) -{ - int retval; - -checks: - if (!q->streaming) { - dprintk(1, "next_buffer: Not streaming\n"); - retval = -EINVAL; - goto done; - } - - if (list_empty(&q->stream)) { - if (noblock) { - retval = -EAGAIN; - dprintk(2, "next_buffer: no buffers to dequeue\n"); - goto done; - } else { - dprintk(2, "next_buffer: waiting on buffer\n"); - - /* Drop lock to avoid deadlock with qbuf */ - videobuf_queue_unlock(q); - - /* Checking list_empty and streaming is safe without - * locks because we goto checks to validate while - * holding locks before proceeding */ - retval = wait_event_interruptible(q->wait, - !list_empty(&q->stream) || !q->streaming); - videobuf_queue_lock(q); - - if (retval) - goto done; - - goto checks; - } - } - - retval = 0; - -done: - return retval; -} - -/* Locking: Caller holds q->vb_lock */ -static int stream_next_buffer(struct videobuf_queue *q, - struct videobuf_buffer **vb, int nonblocking) -{ - int retval; - struct videobuf_buffer *buf = NULL; - - retval = stream_next_buffer_check_queue(q, nonblocking); - if (retval) - goto done; - - buf = list_entry(q->stream.next, struct videobuf_buffer, stream); - retval = videobuf_waiton(q, buf, nonblocking, 1); - if (retval < 0) - goto done; - - *vb = buf; -done: - return retval; -} - -int videobuf_dqbuf(struct videobuf_queue *q, - struct v4l2_buffer *b, int nonblocking) -{ - struct videobuf_buffer *buf = NULL; - int retval; - - MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS); - - memset(b, 0, sizeof(*b)); - videobuf_queue_lock(q); - - retval = stream_next_buffer(q, &buf, nonblocking); - if (retval < 0) { - dprintk(1, "dqbuf: next_buffer error: %i\n", retval); - goto done; - } - - switch (buf->state) { - case VIDEOBUF_ERROR: - dprintk(1, "dqbuf: state is error\n"); - break; - case VIDEOBUF_DONE: - dprintk(1, "dqbuf: state is done\n"); - break; - default: - dprintk(1, "dqbuf: state invalid\n"); - retval = -EINVAL; - goto done; - } - CALL(q, sync, q, buf); - videobuf_status(q, b, buf, q->type); - list_del(&buf->stream); - buf->state = VIDEOBUF_IDLE; - b->flags &= ~V4L2_BUF_FLAG_DONE; -done: - videobuf_queue_unlock(q); - return retval; -} -EXPORT_SYMBOL_GPL(videobuf_dqbuf); - -int videobuf_streamon(struct videobuf_queue *q) -{ - struct videobuf_buffer *buf; - unsigned long flags = 0; - int retval; - - videobuf_queue_lock(q); - retval = -EBUSY; - if (q->reading) - goto done; - retval = 0; - if (q->streaming) - goto done; - q->streaming = 1; - spin_lock_irqsave(q->irqlock, flags); - list_for_each_entry(buf, &q->stream, stream) - if (buf->state == VIDEOBUF_PREPARED) - q->ops->buf_queue(q, buf); - spin_unlock_irqrestore(q->irqlock, flags); - - wake_up_interruptible_sync(&q->wait); -done: - videobuf_queue_unlock(q); - return retval; -} -EXPORT_SYMBOL_GPL(videobuf_streamon); - -/* Locking: Caller holds q->vb_lock */ -static int __videobuf_streamoff(struct videobuf_queue *q) -{ - if (!q->streaming) - return -EINVAL; - - videobuf_queue_cancel(q); - - return 0; -} - -int videobuf_streamoff(struct videobuf_queue *q) -{ - int retval; - - videobuf_queue_lock(q); - retval = __videobuf_streamoff(q); - videobuf_queue_unlock(q); - - return retval; -} -EXPORT_SYMBOL_GPL(videobuf_streamoff); - -/* Locking: Caller holds q->vb_lock */ -static ssize_t videobuf_read_zerocopy(struct videobuf_queue *q, - char __user *data, - size_t count, loff_t *ppos) -{ - enum v4l2_field field; - unsigned long flags = 0; - int retval; - - MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS); - - /* setup stuff */ - q->read_buf = videobuf_alloc_vb(q); - if (NULL == q->read_buf) - return -ENOMEM; - - q->read_buf->memory = V4L2_MEMORY_USERPTR; - q->read_buf->baddr = (unsigned long)data; - q->read_buf->bsize = count; - - field = videobuf_next_field(q); - retval = q->ops->buf_prepare(q, q->read_buf, field); - if (0 != retval) - goto done; - - /* start capture & wait */ - spin_lock_irqsave(q->irqlock, flags); - q->ops->buf_queue(q, q->read_buf); - spin_unlock_irqrestore(q->irqlock, flags); - retval = videobuf_waiton(q, q->read_buf, 0, 0); - if (0 == retval) { - CALL(q, sync, q, q->read_buf); - if (VIDEOBUF_ERROR == q->read_buf->state) - retval = -EIO; - else - retval = q->read_buf->size; - } - -done: - /* cleanup */ - q->ops->buf_release(q, q->read_buf); - kfree(q->read_buf); - q->read_buf = NULL; - return retval; -} - -static int __videobuf_copy_to_user(struct videobuf_queue *q, - struct videobuf_buffer *buf, - char __user *data, size_t count, - int nonblocking) -{ - void *vaddr = CALL(q, vaddr, buf); - - /* copy to userspace */ - if (count > buf->size - q->read_off) - count = buf->size - q->read_off; - - if (copy_to_user(data, vaddr + q->read_off, count)) - return -EFAULT; - - return count; -} - -static int __videobuf_copy_stream(struct videobuf_queue *q, - struct videobuf_buffer *buf, - char __user *data, size_t count, size_t pos, - int vbihack, int nonblocking) -{ - unsigned int *fc = CALL(q, vaddr, buf); - - if (vbihack) { - /* dirty, undocumented hack -- pass the frame counter - * within the last four bytes of each vbi data block. - * We need that one to maintain backward compatibility - * to all vbi decoding software out there ... */ - fc += (buf->size >> 2) - 1; - *fc = buf->field_count >> 1; - dprintk(1, "vbihack: %d\n", *fc); - } - - /* copy stuff using the common method */ - count = __videobuf_copy_to_user(q, buf, data, count, nonblocking); - - if ((count == -EFAULT) && (pos == 0)) - return -EFAULT; - - return count; -} - -ssize_t videobuf_read_one(struct videobuf_queue *q, - char __user *data, size_t count, loff_t *ppos, - int nonblocking) -{ - enum v4l2_field field; - unsigned long flags = 0; - unsigned size = 0, nbufs = 1; - int retval; - - MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS); - - videobuf_queue_lock(q); - - q->ops->buf_setup(q, &nbufs, &size); - - if (NULL == q->read_buf && - count >= size && - !nonblocking) { - retval = videobuf_read_zerocopy(q, data, count, ppos); - if (retval >= 0 || retval == -EIO) - /* ok, all done */ - goto done; - /* fallback to kernel bounce buffer on failures */ - } - - if (NULL == q->read_buf) { - /* need to capture a new frame */ - retval = -ENOMEM; - q->read_buf = videobuf_alloc_vb(q); - - dprintk(1, "video alloc=0x%p\n", q->read_buf); - if (NULL == q->read_buf) - goto done; - q->read_buf->memory = V4L2_MEMORY_USERPTR; - q->read_buf->bsize = count; /* preferred size */ - field = videobuf_next_field(q); - retval = q->ops->buf_prepare(q, q->read_buf, field); - - if (0 != retval) { - kfree(q->read_buf); - q->read_buf = NULL; - goto done; - } - - spin_lock_irqsave(q->irqlock, flags); - q->ops->buf_queue(q, q->read_buf); - spin_unlock_irqrestore(q->irqlock, flags); - - q->read_off = 0; - } - - /* wait until capture is done */ - retval = videobuf_waiton(q, q->read_buf, nonblocking, 1); - if (0 != retval) - goto done; - - CALL(q, sync, q, q->read_buf); - - if (VIDEOBUF_ERROR == q->read_buf->state) { - /* catch I/O errors */ - q->ops->buf_release(q, q->read_buf); - kfree(q->read_buf); - q->read_buf = NULL; - retval = -EIO; - goto done; - } - - /* Copy to userspace */ - retval = __videobuf_copy_to_user(q, q->read_buf, data, count, nonblocking); - if (retval < 0) - goto done; - - q->read_off += retval; - if (q->read_off == q->read_buf->size) { - /* all data copied, cleanup */ - q->ops->buf_release(q, q->read_buf); - kfree(q->read_buf); - q->read_buf = NULL; - } - -done: - videobuf_queue_unlock(q); - return retval; -} -EXPORT_SYMBOL_GPL(videobuf_read_one); - -/* Locking: Caller holds q->vb_lock */ -static int __videobuf_read_start(struct videobuf_queue *q) -{ - enum v4l2_field field; - unsigned long flags = 0; - unsigned int count = 0, size = 0; - int err, i; - - q->ops->buf_setup(q, &count, &size); - if (count < 2) - count = 2; - if (count > VIDEO_MAX_FRAME) - count = VIDEO_MAX_FRAME; - size = PAGE_ALIGN(size); - - err = __videobuf_mmap_setup(q, count, size, V4L2_MEMORY_USERPTR); - if (err < 0) - return err; - - count = err; - - for (i = 0; i < count; i++) { - field = videobuf_next_field(q); - err = q->ops->buf_prepare(q, q->bufs[i], field); - if (err) - return err; - list_add_tail(&q->bufs[i]->stream, &q->stream); - } - spin_lock_irqsave(q->irqlock, flags); - for (i = 0; i < count; i++) - q->ops->buf_queue(q, q->bufs[i]); - spin_unlock_irqrestore(q->irqlock, flags); - q->reading = 1; - return 0; -} - -static void __videobuf_read_stop(struct videobuf_queue *q) -{ - int i; - - videobuf_queue_cancel(q); - __videobuf_free(q); - INIT_LIST_HEAD(&q->stream); - for (i = 0; i < VIDEO_MAX_FRAME; i++) { - if (NULL == q->bufs[i]) - continue; - kfree(q->bufs[i]); - q->bufs[i] = NULL; - } - q->read_buf = NULL; -} - -int videobuf_read_start(struct videobuf_queue *q) -{ - int rc; - - videobuf_queue_lock(q); - rc = __videobuf_read_start(q); - videobuf_queue_unlock(q); - - return rc; -} -EXPORT_SYMBOL_GPL(videobuf_read_start); - -void videobuf_read_stop(struct videobuf_queue *q) -{ - videobuf_queue_lock(q); - __videobuf_read_stop(q); - videobuf_queue_unlock(q); -} -EXPORT_SYMBOL_GPL(videobuf_read_stop); - -void videobuf_stop(struct videobuf_queue *q) -{ - videobuf_queue_lock(q); - - if (q->streaming) - __videobuf_streamoff(q); - - if (q->reading) - __videobuf_read_stop(q); - - videobuf_queue_unlock(q); -} -EXPORT_SYMBOL_GPL(videobuf_stop); - -ssize_t videobuf_read_stream(struct videobuf_queue *q, - char __user *data, size_t count, loff_t *ppos, - int vbihack, int nonblocking) -{ - int rc, retval; - unsigned long flags = 0; - - MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS); - - dprintk(2, "%s\n", __func__); - videobuf_queue_lock(q); - retval = -EBUSY; - if (q->streaming) - goto done; - if (!q->reading) { - retval = __videobuf_read_start(q); - if (retval < 0) - goto done; - } - - retval = 0; - while (count > 0) { - /* get / wait for data */ - if (NULL == q->read_buf) { - q->read_buf = list_entry(q->stream.next, - struct videobuf_buffer, - stream); - list_del(&q->read_buf->stream); - q->read_off = 0; - } - rc = videobuf_waiton(q, q->read_buf, nonblocking, 1); - if (rc < 0) { - if (0 == retval) - retval = rc; - break; - } - - if (q->read_buf->state == VIDEOBUF_DONE) { - rc = __videobuf_copy_stream(q, q->read_buf, data + retval, count, - retval, vbihack, nonblocking); - if (rc < 0) { - retval = rc; - break; - } - retval += rc; - count -= rc; - q->read_off += rc; - } else { - /* some error */ - q->read_off = q->read_buf->size; - if (0 == retval) - retval = -EIO; - } - - /* requeue buffer when done with copying */ - if (q->read_off == q->read_buf->size) { - list_add_tail(&q->read_buf->stream, - &q->stream); - spin_lock_irqsave(q->irqlock, flags); - q->ops->buf_queue(q, q->read_buf); - spin_unlock_irqrestore(q->irqlock, flags); - q->read_buf = NULL; - } - if (retval < 0) - break; - } - -done: - videobuf_queue_unlock(q); - return retval; -} -EXPORT_SYMBOL_GPL(videobuf_read_stream); - -unsigned int videobuf_poll_stream(struct file *file, - struct videobuf_queue *q, - poll_table *wait) -{ - unsigned long req_events = poll_requested_events(wait); - struct videobuf_buffer *buf = NULL; - unsigned int rc = 0; - - videobuf_queue_lock(q); - if (q->streaming) { - if (!list_empty(&q->stream)) - buf = list_entry(q->stream.next, - struct videobuf_buffer, stream); - } else if (req_events & (POLLIN | POLLRDNORM)) { - if (!q->reading) - __videobuf_read_start(q); - if (!q->reading) { - rc = POLLERR; - } else if (NULL == q->read_buf) { - q->read_buf = list_entry(q->stream.next, - struct videobuf_buffer, - stream); - list_del(&q->read_buf->stream); - q->read_off = 0; - } - buf = q->read_buf; - } - if (!buf) - rc = POLLERR; - - if (0 == rc) { - poll_wait(file, &buf->done, wait); - if (buf->state == VIDEOBUF_DONE || - buf->state == VIDEOBUF_ERROR) { - switch (q->type) { - case V4L2_BUF_TYPE_VIDEO_OUTPUT: - case V4L2_BUF_TYPE_VBI_OUTPUT: - case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: - rc = POLLOUT | POLLWRNORM; - break; - default: - rc = POLLIN | POLLRDNORM; - break; - } - } - } - videobuf_queue_unlock(q); - return rc; -} -EXPORT_SYMBOL_GPL(videobuf_poll_stream); - -int videobuf_mmap_mapper(struct videobuf_queue *q, struct vm_area_struct *vma) -{ - int rc = -EINVAL; - int i; - - MAGIC_CHECK(q->int_ops->magic, MAGIC_QTYPE_OPS); - - if (!(vma->vm_flags & VM_WRITE) || !(vma->vm_flags & VM_SHARED)) { - dprintk(1, "mmap appl bug: PROT_WRITE and MAP_SHARED are required\n"); - return -EINVAL; - } - - videobuf_queue_lock(q); - for (i = 0; i < VIDEO_MAX_FRAME; i++) { - struct videobuf_buffer *buf = q->bufs[i]; - - if (buf && buf->memory == V4L2_MEMORY_MMAP && - buf->boff == (vma->vm_pgoff << PAGE_SHIFT)) { - rc = CALL(q, mmap_mapper, q, buf, vma); - break; - } - } - videobuf_queue_unlock(q); - - return rc; -} -EXPORT_SYMBOL_GPL(videobuf_mmap_mapper); diff --git a/drivers/media/video/videobuf-dma-contig.c b/drivers/media/video/videobuf-dma-contig.c deleted file mode 100644 index 3a43ba0959bf..000000000000 --- a/drivers/media/video/videobuf-dma-contig.c +++ /dev/null @@ -1,510 +0,0 @@ -/* - * helper functions for physically contiguous capture buffers - * - * The functions support hardware lacking scatter gather support - * (i.e. the buffers must be linear in physical memory) - * - * Copyright (c) 2008 Magnus Damm - * - * Based on videobuf-vmalloc.c, - * (c) 2007 Mauro Carvalho Chehab, <mchehab@infradead.org> - * - * 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 - */ - -#include <linux/init.h> -#include <linux/module.h> -#include <linux/mm.h> -#include <linux/pagemap.h> -#include <linux/dma-mapping.h> -#include <linux/sched.h> -#include <linux/slab.h> -#include <media/videobuf-dma-contig.h> - -struct videobuf_dma_contig_memory { - u32 magic; - void *vaddr; - dma_addr_t dma_handle; - bool cached; - unsigned long size; -}; - -#define MAGIC_DC_MEM 0x0733ac61 -#define MAGIC_CHECK(is, should) \ - if (unlikely((is) != (should))) { \ - pr_err("magic mismatch: %x expected %x\n", (is), (should)); \ - BUG(); \ - } - -static int __videobuf_dc_alloc(struct device *dev, - struct videobuf_dma_contig_memory *mem, - unsigned long size, gfp_t flags) -{ - mem->size = size; - if (mem->cached) { - mem->vaddr = alloc_pages_exact(mem->size, flags | GFP_DMA); - if (mem->vaddr) { - int err; - - mem->dma_handle = dma_map_single(dev, mem->vaddr, - mem->size, - DMA_FROM_DEVICE); - err = dma_mapping_error(dev, mem->dma_handle); - if (err) { - dev_err(dev, "dma_map_single failed\n"); - - free_pages_exact(mem->vaddr, mem->size); - mem->vaddr = NULL; - return err; - } - } - } else - mem->vaddr = dma_alloc_coherent(dev, mem->size, - &mem->dma_handle, flags); - - if (!mem->vaddr) { - dev_err(dev, "memory alloc size %ld failed\n", mem->size); - return -ENOMEM; - } - - dev_dbg(dev, "dma mapped data is at %p (%ld)\n", mem->vaddr, mem->size); - - return 0; -} - -static void __videobuf_dc_free(struct device *dev, - struct videobuf_dma_contig_memory *mem) -{ - if (mem->cached) { - if (!mem->vaddr) - return; - dma_unmap_single(dev, mem->dma_handle, mem->size, - DMA_FROM_DEVICE); - free_pages_exact(mem->vaddr, mem->size); - } else - dma_free_coherent(dev, mem->size, mem->vaddr, mem->dma_handle); - - mem->vaddr = NULL; -} - -static void videobuf_vm_open(struct vm_area_struct *vma) -{ - struct videobuf_mapping *map = vma->vm_private_data; - - dev_dbg(map->q->dev, "vm_open %p [count=%u,vma=%08lx-%08lx]\n", - map, map->count, vma->vm_start, vma->vm_end); - - map->count++; -} - -static void videobuf_vm_close(struct vm_area_struct *vma) -{ - struct videobuf_mapping *map = vma->vm_private_data; - struct videobuf_queue *q = map->q; - int i; - - dev_dbg(q->dev, "vm_close %p [count=%u,vma=%08lx-%08lx]\n", - map, map->count, vma->vm_start, vma->vm_end); - - map->count--; - if (0 == map->count) { - struct videobuf_dma_contig_memory *mem; - - dev_dbg(q->dev, "munmap %p q=%p\n", map, q); - videobuf_queue_lock(q); - - /* We need first to cancel streams, before unmapping */ - if (q->streaming) - videobuf_queue_cancel(q); - - for (i = 0; i < VIDEO_MAX_FRAME; i++) { - if (NULL == q->bufs[i]) - continue; - - if (q->bufs[i]->map != map) - continue; - - mem = q->bufs[i]->priv; - if (mem) { - /* This callback is called only if kernel has - allocated memory and this memory is mmapped. - In this case, memory should be freed, - in order to do memory unmap. - */ - - MAGIC_CHECK(mem->magic, MAGIC_DC_MEM); - - /* vfree is not atomic - can't be - called with IRQ's disabled - */ - dev_dbg(q->dev, "buf[%d] freeing %p\n", - i, mem->vaddr); - - __videobuf_dc_free(q->dev, mem); - mem->vaddr = NULL; - } - - q->bufs[i]->map = NULL; - q->bufs[i]->baddr = 0; - } - - kfree(map); - - videobuf_queue_unlock(q); - } -} - -static const struct vm_operations_struct videobuf_vm_ops = { - .open = videobuf_vm_open, - .close = videobuf_vm_close, -}; - -/** - * videobuf_dma_contig_user_put() - reset pointer to user space buffer - * @mem: per-buffer private videobuf-dma-contig data - * - * This function resets the user space pointer - */ -static void videobuf_dma_contig_user_put(struct videobuf_dma_contig_memory *mem) -{ - mem->dma_handle = 0; - mem->size = 0; -} - -/** - * videobuf_dma_contig_user_get() - setup user space memory pointer - * @mem: per-buffer private videobuf-dma-contig data - * @vb: video buffer to map - * - * This function validates and sets up a pointer to user space memory. - * Only physically contiguous pfn-mapped memory is accepted. - * - * Returns 0 if successful. - */ -static int videobuf_dma_contig_user_get(struct videobuf_dma_contig_memory *mem, - struct videobuf_buffer *vb) -{ - struct mm_struct *mm = current->mm; - struct vm_area_struct *vma; - unsigned long prev_pfn, this_pfn; - unsigned long pages_done, user_address; - unsigned int offset; - int ret; - - offset = vb->baddr & ~PAGE_MASK; - mem->size = PAGE_ALIGN(vb->size + offset); - ret = -EINVAL; - - down_read(&mm->mmap_sem); - - vma = find_vma(mm, vb->baddr); - if (!vma) - goto out_up; - - if ((vb->baddr + mem->size) > vma->vm_end) - goto out_up; - - pages_done = 0; - prev_pfn = 0; /* kill warning */ - user_address = vb->baddr; - - while (pages_done < (mem->size >> PAGE_SHIFT)) { - ret = follow_pfn(vma, user_address, &this_pfn); - if (ret) - break; - - if (pages_done == 0) - mem->dma_handle = (this_pfn << PAGE_SHIFT) + offset; - else if (this_pfn != (prev_pfn + 1)) - ret = -EFAULT; - - if (ret) - break; - - prev_pfn = this_pfn; - user_address += PAGE_SIZE; - pages_done++; - } - -out_up: - up_read(¤t->mm->mmap_sem); - - return ret; -} - -static struct videobuf_buffer *__videobuf_alloc_vb(size_t size, bool cached) -{ - struct videobuf_dma_contig_memory *mem; - struct videobuf_buffer *vb; - - vb = kzalloc(size + sizeof(*mem), GFP_KERNEL); - if (vb) { - vb->priv = ((char *)vb) + size; - mem = vb->priv; - mem->magic = MAGIC_DC_MEM; - mem->cached = cached; - } - - return vb; -} - -static struct videobuf_buffer *__videobuf_alloc_uncached(size_t size) -{ - return __videobuf_alloc_vb(size, false); -} - -static struct videobuf_buffer *__videobuf_alloc_cached(size_t size) -{ - return __videobuf_alloc_vb(size, true); -} - -static void *__videobuf_to_vaddr(struct videobuf_buffer *buf) -{ - struct videobuf_dma_contig_memory *mem = buf->priv; - - BUG_ON(!mem); - MAGIC_CHECK(mem->magic, MAGIC_DC_MEM); - - return mem->vaddr; -} - -static int __videobuf_iolock(struct videobuf_queue *q, - struct videobuf_buffer *vb, - struct v4l2_framebuffer *fbuf) -{ - struct videobuf_dma_contig_memory *mem = vb->priv; - - BUG_ON(!mem); - MAGIC_CHECK(mem->magic, MAGIC_DC_MEM); - - switch (vb->memory) { - case V4L2_MEMORY_MMAP: - dev_dbg(q->dev, "%s memory method MMAP\n", __func__); - - /* All handling should be done by __videobuf_mmap_mapper() */ - if (!mem->vaddr) { - dev_err(q->dev, "memory is not alloced/mmapped.\n"); - return -EINVAL; - } - break; - case V4L2_MEMORY_USERPTR: - dev_dbg(q->dev, "%s memory method USERPTR\n", __func__); - - /* handle pointer from user space */ - if (vb->baddr) - return videobuf_dma_contig_user_get(mem, vb); - - /* allocate memory for the read() method */ - if (__videobuf_dc_alloc(q->dev, mem, PAGE_ALIGN(vb->size), - GFP_KERNEL)) - return -ENOMEM; - break; - case V4L2_MEMORY_OVERLAY: - default: - dev_dbg(q->dev, "%s memory method OVERLAY/unknown\n", __func__); - return -EINVAL; - } - - return 0; -} - -static int __videobuf_sync(struct videobuf_queue *q, - struct videobuf_buffer *buf) -{ - struct videobuf_dma_contig_memory *mem = buf->priv; - BUG_ON(!mem); - MAGIC_CHECK(mem->magic, MAGIC_DC_MEM); - - dma_sync_single_for_cpu(q->dev, mem->dma_handle, mem->size, - DMA_FROM_DEVICE); - - return 0; -} - -static int __videobuf_mmap_mapper(struct videobuf_queue *q, - struct videobuf_buffer *buf, - struct vm_area_struct *vma) -{ - struct videobuf_dma_contig_memory *mem; - struct videobuf_mapping *map; - int retval; - unsigned long size; - unsigned long pos, start = vma->vm_start; - struct page *page; - - dev_dbg(q->dev, "%s\n", __func__); - - /* create mapping + update buffer list */ - map = kzalloc(sizeof(struct videobuf_mapping), GFP_KERNEL); - if (!map) - return -ENOMEM; - - buf->map = map; - map->q = q; - - buf->baddr = vma->vm_start; - - mem = buf->priv; - BUG_ON(!mem); - MAGIC_CHECK(mem->magic, MAGIC_DC_MEM); - - if (__videobuf_dc_alloc(q->dev, mem, PAGE_ALIGN(buf->bsize), - GFP_KERNEL | __GFP_COMP)) - goto error; - - /* Try to remap memory */ - - size = vma->vm_end - vma->vm_start; - size = (size < mem->size) ? size : mem->size; - - if (!mem->cached) { - vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); - retval = remap_pfn_range(vma, vma->vm_start, - mem->dma_handle >> PAGE_SHIFT, - size, vma->vm_page_prot); - if (retval) { - dev_err(q->dev, "mmap: remap failed with error %d. ", - retval); - dma_free_coherent(q->dev, mem->size, - mem->vaddr, mem->dma_handle); - goto error; - } - } else { - pos = (unsigned long)mem->vaddr; - - while (size > 0) { - page = virt_to_page((void *)pos); - if (NULL == page) { - dev_err(q->dev, "mmap: virt_to_page failed\n"); - __videobuf_dc_free(q->dev, mem); - goto error; - } - retval = vm_insert_page(vma, start, page); - if (retval) { - dev_err(q->dev, "mmap: insert failed with error %d\n", - retval); - __videobuf_dc_free(q->dev, mem); - goto error; - } - start += PAGE_SIZE; - pos += PAGE_SIZE; - - if (size > PAGE_SIZE) - size -= PAGE_SIZE; - else - size = 0; - } - } - - vma->vm_ops = &videobuf_vm_ops; - vma->vm_flags |= VM_DONTEXPAND; - vma->vm_private_data = map; - - dev_dbg(q->dev, "mmap %p: q=%p %08lx-%08lx (%lx) pgoff %08lx buf %d\n", - map, q, vma->vm_start, vma->vm_end, - (long int)buf->bsize, vma->vm_pgoff, buf->i); - - videobuf_vm_open(vma); - - return 0; - -error: - kfree(map); - return -ENOMEM; -} - -static struct videobuf_qtype_ops qops = { - .magic = MAGIC_QTYPE_OPS, - .alloc_vb = __videobuf_alloc_uncached, - .iolock = __videobuf_iolock, - .mmap_mapper = __videobuf_mmap_mapper, - .vaddr = __videobuf_to_vaddr, -}; - -static struct videobuf_qtype_ops qops_cached = { - .magic = MAGIC_QTYPE_OPS, - .alloc_vb = __videobuf_alloc_cached, - .iolock = __videobuf_iolock, - .sync = __videobuf_sync, - .mmap_mapper = __videobuf_mmap_mapper, - .vaddr = __videobuf_to_vaddr, -}; - -void videobuf_queue_dma_contig_init(struct videobuf_queue *q, - const struct videobuf_queue_ops *ops, - struct device *dev, - spinlock_t *irqlock, - enum v4l2_buf_type type, - enum v4l2_field field, - unsigned int msize, - void *priv, - struct mutex *ext_lock) -{ - videobuf_queue_core_init(q, ops, dev, irqlock, type, field, msize, - priv, &qops, ext_lock); -} -EXPORT_SYMBOL_GPL(videobuf_queue_dma_contig_init); - -void videobuf_queue_dma_contig_init_cached(struct videobuf_queue *q, - const struct videobuf_queue_ops *ops, - struct device *dev, - spinlock_t *irqlock, - enum v4l2_buf_type type, - enum v4l2_field field, - unsigned int msize, - void *priv, struct mutex *ext_lock) -{ - videobuf_queue_core_init(q, ops, dev, irqlock, type, field, msize, - priv, &qops_cached, ext_lock); -} -EXPORT_SYMBOL_GPL(videobuf_queue_dma_contig_init_cached); - -dma_addr_t videobuf_to_dma_contig(struct videobuf_buffer *buf) -{ - struct videobuf_dma_contig_memory *mem = buf->priv; - - BUG_ON(!mem); - MAGIC_CHECK(mem->magic, MAGIC_DC_MEM); - - return mem->dma_handle; -} -EXPORT_SYMBOL_GPL(videobuf_to_dma_contig); - -void videobuf_dma_contig_free(struct videobuf_queue *q, - struct videobuf_buffer *buf) -{ - struct videobuf_dma_contig_memory *mem = buf->priv; - - /* mmapped memory can't be freed here, otherwise mmapped region - would be released, while still needed. In this case, the memory - release should happen inside videobuf_vm_close(). - So, it should free memory only if the memory were allocated for - read() operation. - */ - if (buf->memory != V4L2_MEMORY_USERPTR) - return; - - if (!mem) - return; - - MAGIC_CHECK(mem->magic, MAGIC_DC_MEM); - - /* handle user space pointer case */ - if (buf->baddr) { - videobuf_dma_contig_user_put(mem); - return; - } - - /* read() method */ - if (mem->vaddr) { - __videobuf_dc_free(q->dev, mem); - mem->vaddr = NULL; - } -} -EXPORT_SYMBOL_GPL(videobuf_dma_contig_free); - -MODULE_DESCRIPTION("helper module to manage video4linux dma contig buffers"); -MODULE_AUTHOR("Magnus Damm"); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/videobuf-dma-sg.c b/drivers/media/video/videobuf-dma-sg.c deleted file mode 100644 index f300deafd268..000000000000 --- a/drivers/media/video/videobuf-dma-sg.c +++ /dev/null @@ -1,633 +0,0 @@ -/* - * helper functions for SG DMA video4linux capture buffers - * - * The functions expect the hardware being able to scatter gather - * (i.e. the buffers are not linear in physical memory, but fragmented - * into PAGE_SIZE chunks). They also assume the driver does not need - * to touch the video data. - * - * (c) 2007 Mauro Carvalho Chehab, <mchehab@infradead.org> - * - * Highly based on video-buf written originally by: - * (c) 2001,02 Gerd Knorr <kraxel@bytesex.org> - * (c) 2006 Mauro Carvalho Chehab, <mchehab@infradead.org> - * (c) 2006 Ted Walther and John Sokol - * - * 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 - */ - -#include <linux/init.h> -#include <linux/module.h> -#include <linux/moduleparam.h> -#include <linux/sched.h> -#include <linux/slab.h> -#include <linux/interrupt.h> - -#include <linux/dma-mapping.h> -#include <linux/vmalloc.h> -#include <linux/pagemap.h> -#include <linux/scatterlist.h> -#include <asm/page.h> -#include <asm/pgtable.h> - -#include <media/videobuf-dma-sg.h> - -#define MAGIC_DMABUF 0x19721112 -#define MAGIC_SG_MEM 0x17890714 - -#define MAGIC_CHECK(is, should) \ - if (unlikely((is) != (should))) { \ - printk(KERN_ERR "magic mismatch: %x (expected %x)\n", \ - is, should); \ - BUG(); \ - } - -static int debug; -module_param(debug, int, 0644); - -MODULE_DESCRIPTION("helper module to manage video4linux dma sg buffers"); -MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>"); -MODULE_LICENSE("GPL"); - -#define dprintk(level, fmt, arg...) \ - if (debug >= level) \ - printk(KERN_DEBUG "vbuf-sg: " fmt , ## arg) - -/* --------------------------------------------------------------------- */ - -/* - * Return a scatterlist for some page-aligned vmalloc()'ed memory - * block (NULL on errors). Memory for the scatterlist is allocated - * using kmalloc. The caller must free the memory. - */ -static struct scatterlist *videobuf_vmalloc_to_sg(unsigned char *virt, - int nr_pages) -{ - struct scatterlist *sglist; - struct page *pg; - int i; - - sglist = vzalloc(nr_pages * sizeof(*sglist)); - if (NULL == sglist) - return NULL; - sg_init_table(sglist, nr_pages); - for (i = 0; i < nr_pages; i++, virt += PAGE_SIZE) { - pg = vmalloc_to_page(virt); - if (NULL == pg) - goto err; - BUG_ON(PageHighMem(pg)); - sg_set_page(&sglist[i], pg, PAGE_SIZE, 0); - } - return sglist; - -err: - vfree(sglist); - return NULL; -} - -/* - * Return a scatterlist for a an array of userpages (NULL on errors). - * Memory for the scatterlist is allocated using kmalloc. The caller - * must free the memory. - */ -static struct scatterlist *videobuf_pages_to_sg(struct page **pages, - int nr_pages, int offset, size_t size) -{ - struct scatterlist *sglist; - int i; - - if (NULL == pages[0]) - return NULL; - sglist = vmalloc(nr_pages * sizeof(*sglist)); - if (NULL == sglist) - return NULL; - sg_init_table(sglist, nr_pages); - - if (PageHighMem(pages[0])) - /* DMA to highmem pages might not work */ - goto highmem; - sg_set_page(&sglist[0], pages[0], - min_t(size_t, PAGE_SIZE - offset, size), offset); - size -= min_t(size_t, PAGE_SIZE - offset, size); - for (i = 1; i < nr_pages; i++) { - if (NULL == pages[i]) - goto nopage; - if (PageHighMem(pages[i])) - goto highmem; - sg_set_page(&sglist[i], pages[i], min_t(size_t, PAGE_SIZE, size), 0); - size -= min_t(size_t, PAGE_SIZE, size); - } - return sglist; - -nopage: - dprintk(2, "sgl: oops - no page\n"); - vfree(sglist); - return NULL; - -highmem: - dprintk(2, "sgl: oops - highmem page\n"); - vfree(sglist); - return NULL; -} - -/* --------------------------------------------------------------------- */ - -struct videobuf_dmabuf *videobuf_to_dma(struct videobuf_buffer *buf) -{ - struct videobuf_dma_sg_memory *mem = buf->priv; - BUG_ON(!mem); - - MAGIC_CHECK(mem->magic, MAGIC_SG_MEM); - - return &mem->dma; -} -EXPORT_SYMBOL_GPL(videobuf_to_dma); - -void videobuf_dma_init(struct videobuf_dmabuf *dma) -{ - memset(dma, 0, sizeof(*dma)); - dma->magic = MAGIC_DMABUF; -} -EXPORT_SYMBOL_GPL(videobuf_dma_init); - -static int videobuf_dma_init_user_locked(struct videobuf_dmabuf *dma, - int direction, unsigned long data, unsigned long size) -{ - unsigned long first, last; - int err, rw = 0; - - dma->direction = direction; - switch (dma->direction) { - case DMA_FROM_DEVICE: - rw = READ; - break; - case DMA_TO_DEVICE: - rw = WRITE; - break; - default: - BUG(); - } - - first = (data & PAGE_MASK) >> PAGE_SHIFT; - last = ((data+size-1) & PAGE_MASK) >> PAGE_SHIFT; - dma->offset = data & ~PAGE_MASK; - dma->size = size; - dma->nr_pages = last-first+1; - dma->pages = kmalloc(dma->nr_pages * sizeof(struct page *), GFP_KERNEL); - if (NULL == dma->pages) - return -ENOMEM; - - dprintk(1, "init user [0x%lx+0x%lx => %d pages]\n", - data, size, dma->nr_pages); - - err = get_user_pages(current, current->mm, - data & PAGE_MASK, dma->nr_pages, - rw == READ, 1, /* force */ - dma->pages, NULL); - - if (err != dma->nr_pages) { - dma->nr_pages = (err >= 0) ? err : 0; - dprintk(1, "get_user_pages: err=%d [%d]\n", err, dma->nr_pages); - return err < 0 ? err : -EINVAL; - } - return 0; -} - -int videobuf_dma_init_user(struct videobuf_dmabuf *dma, int direction, - unsigned long data, unsigned long size) -{ - int ret; - - down_read(¤t->mm->mmap_sem); - ret = videobuf_dma_init_user_locked(dma, direction, data, size); - up_read(¤t->mm->mmap_sem); - - return ret; -} -EXPORT_SYMBOL_GPL(videobuf_dma_init_user); - -int videobuf_dma_init_kernel(struct videobuf_dmabuf *dma, int direction, - int nr_pages) -{ - dprintk(1, "init kernel [%d pages]\n", nr_pages); - - dma->direction = direction; - dma->vaddr = vmalloc_32(nr_pages << PAGE_SHIFT); - if (NULL == dma->vaddr) { - dprintk(1, "vmalloc_32(%d pages) failed\n", nr_pages); - return -ENOMEM; - } - - dprintk(1, "vmalloc is at addr 0x%08lx, size=%d\n", - (unsigned long)dma->vaddr, - nr_pages << PAGE_SHIFT); - - memset(dma->vaddr, 0, nr_pages << PAGE_SHIFT); - dma->nr_pages = nr_pages; - - return 0; -} -EXPORT_SYMBOL_GPL(videobuf_dma_init_kernel); - -int videobuf_dma_init_overlay(struct videobuf_dmabuf *dma, int direction, - dma_addr_t addr, int nr_pages) -{ - dprintk(1, "init overlay [%d pages @ bus 0x%lx]\n", - nr_pages, (unsigned long)addr); - dma->direction = direction; - - if (0 == addr) - return -EINVAL; - - dma->bus_addr = addr; - dma->nr_pages = nr_pages; - - return 0; -} -EXPORT_SYMBOL_GPL(videobuf_dma_init_overlay); - -int videobuf_dma_map(struct device *dev, struct videobuf_dmabuf *dma) -{ - MAGIC_CHECK(dma->magic, MAGIC_DMABUF); - BUG_ON(0 == dma->nr_pages); - - if (dma->pages) { - dma->sglist = videobuf_pages_to_sg(dma->pages, dma->nr_pages, - dma->offset, dma->size); - } - if (dma->vaddr) { - dma->sglist = videobuf_vmalloc_to_sg(dma->vaddr, - dma->nr_pages); - } - if (dma->bus_addr) { - dma->sglist = vmalloc(sizeof(*dma->sglist)); - if (NULL != dma->sglist) { - dma->sglen = 1; - sg_dma_address(&dma->sglist[0]) = dma->bus_addr - & PAGE_MASK; - dma->sglist[0].offset = dma->bus_addr & ~PAGE_MASK; - sg_dma_len(&dma->sglist[0]) = dma->nr_pages * PAGE_SIZE; - } - } - if (NULL == dma->sglist) { - dprintk(1, "scatterlist is NULL\n"); - return -ENOMEM; - } - if (!dma->bus_addr) { - dma->sglen = dma_map_sg(dev, dma->sglist, - dma->nr_pages, dma->direction); - if (0 == dma->sglen) { - printk(KERN_WARNING - "%s: videobuf_map_sg failed\n", __func__); - vfree(dma->sglist); - dma->sglist = NULL; - dma->sglen = 0; - return -ENOMEM; - } - } - - return 0; -} -EXPORT_SYMBOL_GPL(videobuf_dma_map); - -int videobuf_dma_unmap(struct device *dev, struct videobuf_dmabuf *dma) -{ - MAGIC_CHECK(dma->magic, MAGIC_DMABUF); - - if (!dma->sglen) - return 0; - - dma_unmap_sg(dev, dma->sglist, dma->sglen, dma->direction); - - vfree(dma->sglist); - dma->sglist = NULL; - dma->sglen = 0; - - return 0; -} -EXPORT_SYMBOL_GPL(videobuf_dma_unmap); - -int videobuf_dma_free(struct videobuf_dmabuf *dma) -{ - int i; - MAGIC_CHECK(dma->magic, MAGIC_DMABUF); - BUG_ON(dma->sglen); - - if (dma->pages) { - for (i = 0; i < dma->nr_pages; i++) - page_cache_release(dma->pages[i]); - kfree(dma->pages); - dma->pages = NULL; - } - - vfree(dma->vaddr); - dma->vaddr = NULL; - - if (dma->bus_addr) - dma->bus_addr = 0; - dma->direction = DMA_NONE; - - return 0; -} -EXPORT_SYMBOL_GPL(videobuf_dma_free); - -/* --------------------------------------------------------------------- */ - -static void videobuf_vm_open(struct vm_area_struct *vma) -{ - struct videobuf_mapping *map = vma->vm_private_data; - - dprintk(2, "vm_open %p [count=%d,vma=%08lx-%08lx]\n", map, - map->count, vma->vm_start, vma->vm_end); - - map->count++; -} - -static void videobuf_vm_close(struct vm_area_struct *vma) -{ - struct videobuf_mapping *map = vma->vm_private_data; - struct videobuf_queue *q = map->q; - struct videobuf_dma_sg_memory *mem; - int i; - - dprintk(2, "vm_close %p [count=%d,vma=%08lx-%08lx]\n", map, - map->count, vma->vm_start, vma->vm_end); - - map->count--; - if (0 == map->count) { - dprintk(1, "munmap %p q=%p\n", map, q); - videobuf_queue_lock(q); - for (i = 0; i < VIDEO_MAX_FRAME; i++) { - if (NULL == q->bufs[i]) - continue; - mem = q->bufs[i]->priv; - if (!mem) - continue; - - MAGIC_CHECK(mem->magic, MAGIC_SG_MEM); - - if (q->bufs[i]->map != map) - continue; - q->bufs[i]->map = NULL; - q->bufs[i]->baddr = 0; - q->ops->buf_release(q, q->bufs[i]); - } - videobuf_queue_unlock(q); - kfree(map); - } - return; -} - -/* - * Get a anonymous page for the mapping. Make sure we can DMA to that - * memory location with 32bit PCI devices (i.e. don't use highmem for - * now ...). Bounce buffers don't work very well for the data rates - * video capture has. - */ -static int videobuf_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf) -{ - struct page *page; - - dprintk(3, "fault: fault @ %08lx [vma %08lx-%08lx]\n", - (unsigned long)vmf->virtual_address, - vma->vm_start, vma->vm_end); - - page = alloc_page(GFP_USER | __GFP_DMA32); - if (!page) - return VM_FAULT_OOM; - clear_user_highpage(page, (unsigned long)vmf->virtual_address); - vmf->page = page; - - return 0; -} - -static const struct vm_operations_struct videobuf_vm_ops = { - .open = videobuf_vm_open, - .close = videobuf_vm_close, - .fault = videobuf_vm_fault, -}; - -/* --------------------------------------------------------------------- - * SG handlers for the generic methods - */ - -/* Allocated area consists on 3 parts: - struct video_buffer - struct <driver>_buffer (cx88_buffer, saa7134_buf, ...) - struct videobuf_dma_sg_memory - */ - -static struct videobuf_buffer *__videobuf_alloc_vb(size_t size) -{ - struct videobuf_dma_sg_memory *mem; - struct videobuf_buffer *vb; - - vb = kzalloc(size + sizeof(*mem), GFP_KERNEL); - if (!vb) - return vb; - - mem = vb->priv = ((char *)vb) + size; - mem->magic = MAGIC_SG_MEM; - - videobuf_dma_init(&mem->dma); - - dprintk(1, "%s: allocated at %p(%ld+%ld) & %p(%ld)\n", - __func__, vb, (long)sizeof(*vb), (long)size - sizeof(*vb), - mem, (long)sizeof(*mem)); - - return vb; -} - -static void *__videobuf_to_vaddr(struct videobuf_buffer *buf) -{ - struct videobuf_dma_sg_memory *mem = buf->priv; - BUG_ON(!mem); - - MAGIC_CHECK(mem->magic, MAGIC_SG_MEM); - - return mem->dma.vaddr; -} - -static int __videobuf_iolock(struct videobuf_queue *q, - struct videobuf_buffer *vb, - struct v4l2_framebuffer *fbuf) -{ - int err, pages; - dma_addr_t bus; - struct videobuf_dma_sg_memory *mem = vb->priv; - BUG_ON(!mem); - - MAGIC_CHECK(mem->magic, MAGIC_SG_MEM); - - switch (vb->memory) { - case V4L2_MEMORY_MMAP: - case V4L2_MEMORY_USERPTR: - if (0 == vb->baddr) { - /* no userspace addr -- kernel bounce buffer */ - pages = PAGE_ALIGN(vb->size) >> PAGE_SHIFT; - err = videobuf_dma_init_kernel(&mem->dma, - DMA_FROM_DEVICE, - pages); - if (0 != err) - return err; - } else if (vb->memory == V4L2_MEMORY_USERPTR) { - /* dma directly to userspace */ - err = videobuf_dma_init_user(&mem->dma, - DMA_FROM_DEVICE, - vb->baddr, vb->bsize); - if (0 != err) - return err; - } else { - /* NOTE: HACK: videobuf_iolock on V4L2_MEMORY_MMAP - buffers can only be called from videobuf_qbuf - we take current->mm->mmap_sem there, to prevent - locking inversion, so don't take it here */ - - err = videobuf_dma_init_user_locked(&mem->dma, - DMA_FROM_DEVICE, - vb->baddr, vb->bsize); - if (0 != err) - return err; - } - break; - case V4L2_MEMORY_OVERLAY: - if (NULL == fbuf) - return -EINVAL; - /* FIXME: need sanity checks for vb->boff */ - /* - * Using a double cast to avoid compiler warnings when - * building for PAE. Compiler doesn't like direct casting - * of a 32 bit ptr to 64 bit integer. - */ - bus = (dma_addr_t)(unsigned long)fbuf->base + vb->boff; - pages = PAGE_ALIGN(vb->size) >> PAGE_SHIFT; - err = videobuf_dma_init_overlay(&mem->dma, DMA_FROM_DEVICE, - bus, pages); - if (0 != err) - return err; - break; - default: - BUG(); - } - err = videobuf_dma_map(q->dev, &mem->dma); - if (0 != err) - return err; - - return 0; -} - -static int __videobuf_sync(struct videobuf_queue *q, - struct videobuf_buffer *buf) -{ - struct videobuf_dma_sg_memory *mem = buf->priv; - BUG_ON(!mem || !mem->dma.sglen); - - MAGIC_CHECK(mem->magic, MAGIC_SG_MEM); - MAGIC_CHECK(mem->dma.magic, MAGIC_DMABUF); - - dma_sync_sg_for_cpu(q->dev, mem->dma.sglist, - mem->dma.sglen, mem->dma.direction); - - return 0; -} - -static int __videobuf_mmap_mapper(struct videobuf_queue *q, - struct videobuf_buffer *buf, - struct vm_area_struct *vma) -{ - struct videobuf_dma_sg_memory *mem = buf->priv; - struct videobuf_mapping *map; - unsigned int first, last, size = 0, i; - int retval; - - retval = -EINVAL; - - BUG_ON(!mem); - MAGIC_CHECK(mem->magic, MAGIC_SG_MEM); - - /* look for first buffer to map */ - for (first = 0; first < VIDEO_MAX_FRAME; first++) { - if (buf == q->bufs[first]) { - size = PAGE_ALIGN(q->bufs[first]->bsize); - break; - } - } - - /* paranoia, should never happen since buf is always valid. */ - if (!size) { - dprintk(1, "mmap app bug: offset invalid [offset=0x%lx]\n", - (vma->vm_pgoff << PAGE_SHIFT)); - goto done; - } - - last = first; - - /* create mapping + update buffer list */ - retval = -ENOMEM; - map = kmalloc(sizeof(struct videobuf_mapping), GFP_KERNEL); - if (NULL == map) - goto done; - - size = 0; - for (i = first; i <= last; i++) { - if (NULL == q->bufs[i]) - continue; - q->bufs[i]->map = map; - q->bufs[i]->baddr = vma->vm_start + size; - size += PAGE_ALIGN(q->bufs[i]->bsize); - } - - map->count = 1; - map->q = q; - vma->vm_ops = &videobuf_vm_ops; - vma->vm_flags |= VM_DONTEXPAND | VM_RESERVED; - vma->vm_flags &= ~VM_IO; /* using shared anonymous pages */ - vma->vm_private_data = map; - dprintk(1, "mmap %p: q=%p %08lx-%08lx pgoff %08lx bufs %d-%d\n", - map, q, vma->vm_start, vma->vm_end, vma->vm_pgoff, first, last); - retval = 0; - -done: - return retval; -} - -static struct videobuf_qtype_ops sg_ops = { - .magic = MAGIC_QTYPE_OPS, - - .alloc_vb = __videobuf_alloc_vb, - .iolock = __videobuf_iolock, - .sync = __videobuf_sync, - .mmap_mapper = __videobuf_mmap_mapper, - .vaddr = __videobuf_to_vaddr, -}; - -void *videobuf_sg_alloc(size_t size) -{ - struct videobuf_queue q; - - /* Required to make generic handler to call __videobuf_alloc */ - q.int_ops = &sg_ops; - - q.msize = size; - - return videobuf_alloc_vb(&q); -} -EXPORT_SYMBOL_GPL(videobuf_sg_alloc); - -void videobuf_queue_sg_init(struct videobuf_queue *q, - const struct videobuf_queue_ops *ops, - struct device *dev, - spinlock_t *irqlock, - enum v4l2_buf_type type, - enum v4l2_field field, - unsigned int msize, - void *priv, - struct mutex *ext_lock) -{ - videobuf_queue_core_init(q, ops, dev, irqlock, type, field, msize, - priv, &sg_ops, ext_lock); -} -EXPORT_SYMBOL_GPL(videobuf_queue_sg_init); - diff --git a/drivers/media/video/videobuf-dvb.c b/drivers/media/video/videobuf-dvb.c deleted file mode 100644 index b7efa4516d36..000000000000 --- a/drivers/media/video/videobuf-dvb.c +++ /dev/null @@ -1,398 +0,0 @@ -/* - * - * some helper function for simple DVB cards which simply DMA the - * complete transport stream and let the computer sort everything else - * (i.e. we are using the software demux, ...). Also uses the - * video-buf to manage DMA buffers. - * - * (c) 2004 Gerd Knorr <kraxel@bytesex.org> [SUSE Labs] - * - * 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. - */ - -#include <linux/module.h> -#include <linux/init.h> -#include <linux/device.h> -#include <linux/fs.h> -#include <linux/kthread.h> -#include <linux/file.h> -#include <linux/slab.h> - -#include <linux/freezer.h> - -#include <media/videobuf-core.h> -#include <media/videobuf-dvb.h> - -/* ------------------------------------------------------------------ */ - -MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]"); -MODULE_LICENSE("GPL"); - -static unsigned int debug; -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug,"enable debug messages"); - -#define dprintk(fmt, arg...) if (debug) \ - printk(KERN_DEBUG "%s/dvb: " fmt, dvb->name , ## arg) - -/* ------------------------------------------------------------------ */ - -static int videobuf_dvb_thread(void *data) -{ - struct videobuf_dvb *dvb = data; - struct videobuf_buffer *buf; - unsigned long flags; - void *outp; - - dprintk("dvb thread started\n"); - set_freezable(); - videobuf_read_start(&dvb->dvbq); - - for (;;) { - /* fetch next buffer */ - buf = list_entry(dvb->dvbq.stream.next, - struct videobuf_buffer, stream); - list_del(&buf->stream); - videobuf_waiton(&dvb->dvbq, buf, 0, 1); - - /* no more feeds left or stop_feed() asked us to quit */ - if (0 == dvb->nfeeds) - break; - if (kthread_should_stop()) - break; - try_to_freeze(); - - /* feed buffer data to demux */ - outp = videobuf_queue_to_vaddr(&dvb->dvbq, buf); - - if (buf->state == VIDEOBUF_DONE) - dvb_dmx_swfilter(&dvb->demux, outp, - buf->size); - - /* requeue buffer */ - list_add_tail(&buf->stream,&dvb->dvbq.stream); - spin_lock_irqsave(dvb->dvbq.irqlock,flags); - dvb->dvbq.ops->buf_queue(&dvb->dvbq,buf); - spin_unlock_irqrestore(dvb->dvbq.irqlock,flags); - } - - videobuf_read_stop(&dvb->dvbq); - dprintk("dvb thread stopped\n"); - - /* Hmm, linux becomes *very* unhappy without this ... */ - while (!kthread_should_stop()) { - set_current_state(TASK_INTERRUPTIBLE); - schedule(); - } - return 0; -} - -static int videobuf_dvb_start_feed(struct dvb_demux_feed *feed) -{ - struct dvb_demux *demux = feed->demux; - struct videobuf_dvb *dvb = demux->priv; - int rc; - - if (!demux->dmx.frontend) - return -EINVAL; - - mutex_lock(&dvb->lock); - dvb->nfeeds++; - rc = dvb->nfeeds; - - if (NULL != dvb->thread) - goto out; - dvb->thread = kthread_run(videobuf_dvb_thread, - dvb, "%s dvb", dvb->name); - if (IS_ERR(dvb->thread)) { - rc = PTR_ERR(dvb->thread); - dvb->thread = NULL; - } - -out: - mutex_unlock(&dvb->lock); - return rc; -} - -static int videobuf_dvb_stop_feed(struct dvb_demux_feed *feed) -{ - struct dvb_demux *demux = feed->demux; - struct videobuf_dvb *dvb = demux->priv; - int err = 0; - - mutex_lock(&dvb->lock); - dvb->nfeeds--; - if (0 == dvb->nfeeds && NULL != dvb->thread) { - err = kthread_stop(dvb->thread); - dvb->thread = NULL; - } - mutex_unlock(&dvb->lock); - return err; -} - -static int videobuf_dvb_register_adapter(struct videobuf_dvb_frontends *fe, - struct module *module, - void *adapter_priv, - struct device *device, - char *adapter_name, - short *adapter_nr, - int mfe_shared) -{ - int result; - - mutex_init(&fe->lock); - - /* register adapter */ - result = dvb_register_adapter(&fe->adapter, adapter_name, module, - device, adapter_nr); - if (result < 0) { - printk(KERN_WARNING "%s: dvb_register_adapter failed (errno = %d)\n", - adapter_name, result); - } - fe->adapter.priv = adapter_priv; - fe->adapter.mfe_shared = mfe_shared; - - return result; -} - -static int videobuf_dvb_register_frontend(struct dvb_adapter *adapter, - struct videobuf_dvb *dvb) -{ - int result; - - /* register frontend */ - result = dvb_register_frontend(adapter, dvb->frontend); - if (result < 0) { - printk(KERN_WARNING "%s: dvb_register_frontend failed (errno = %d)\n", - dvb->name, result); - goto fail_frontend; - } - - /* register demux stuff */ - dvb->demux.dmx.capabilities = - DMX_TS_FILTERING | DMX_SECTION_FILTERING | - DMX_MEMORY_BASED_FILTERING; - dvb->demux.priv = dvb; - dvb->demux.filternum = 256; - dvb->demux.feednum = 256; - dvb->demux.start_feed = videobuf_dvb_start_feed; - dvb->demux.stop_feed = videobuf_dvb_stop_feed; - result = dvb_dmx_init(&dvb->demux); - if (result < 0) { - printk(KERN_WARNING "%s: dvb_dmx_init failed (errno = %d)\n", - dvb->name, result); - goto fail_dmx; - } - - dvb->dmxdev.filternum = 256; - dvb->dmxdev.demux = &dvb->demux.dmx; - dvb->dmxdev.capabilities = 0; - result = dvb_dmxdev_init(&dvb->dmxdev, adapter); - - if (result < 0) { - printk(KERN_WARNING "%s: dvb_dmxdev_init failed (errno = %d)\n", - dvb->name, result); - goto fail_dmxdev; - } - - dvb->fe_hw.source = DMX_FRONTEND_0; - result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_hw); - if (result < 0) { - printk(KERN_WARNING "%s: add_frontend failed (DMX_FRONTEND_0, errno = %d)\n", - dvb->name, result); - goto fail_fe_hw; - } - - dvb->fe_mem.source = DMX_MEMORY_FE; - result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_mem); - if (result < 0) { - printk(KERN_WARNING "%s: add_frontend failed (DMX_MEMORY_FE, errno = %d)\n", - dvb->name, result); - goto fail_fe_mem; - } - - result = dvb->demux.dmx.connect_frontend(&dvb->demux.dmx, &dvb->fe_hw); - if (result < 0) { - printk(KERN_WARNING "%s: connect_frontend failed (errno = %d)\n", - dvb->name, result); - goto fail_fe_conn; - } - - /* register network adapter */ - result = dvb_net_init(adapter, &dvb->net, &dvb->demux.dmx); - if (result < 0) { - printk(KERN_WARNING "%s: dvb_net_init failed (errno = %d)\n", - dvb->name, result); - goto fail_fe_conn; - } - return 0; - -fail_fe_conn: - dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem); -fail_fe_mem: - dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw); -fail_fe_hw: - dvb_dmxdev_release(&dvb->dmxdev); -fail_dmxdev: - dvb_dmx_release(&dvb->demux); -fail_dmx: - dvb_unregister_frontend(dvb->frontend); -fail_frontend: - dvb_frontend_detach(dvb->frontend); - dvb->frontend = NULL; - - return result; -} - -/* ------------------------------------------------------------------ */ -/* Register a single adapter and one or more frontends */ -int videobuf_dvb_register_bus(struct videobuf_dvb_frontends *f, - struct module *module, - void *adapter_priv, - struct device *device, - short *adapter_nr, - int mfe_shared) -{ - struct list_head *list, *q; - struct videobuf_dvb_frontend *fe; - int res; - - fe = videobuf_dvb_get_frontend(f, 1); - if (!fe) { - printk(KERN_WARNING "Unable to register the adapter which has no frontends\n"); - return -EINVAL; - } - - /* Bring up the adapter */ - res = videobuf_dvb_register_adapter(f, module, adapter_priv, device, - fe->dvb.name, adapter_nr, mfe_shared); - if (res < 0) { - printk(KERN_WARNING "videobuf_dvb_register_adapter failed (errno = %d)\n", res); - return res; - } - - /* Attach all of the frontends to the adapter */ - mutex_lock(&f->lock); - list_for_each_safe(list, q, &f->felist) { - fe = list_entry(list, struct videobuf_dvb_frontend, felist); - res = videobuf_dvb_register_frontend(&f->adapter, &fe->dvb); - if (res < 0) { - printk(KERN_WARNING "%s: videobuf_dvb_register_frontend failed (errno = %d)\n", - fe->dvb.name, res); - goto err; - } - } - mutex_unlock(&f->lock); - return 0; - -err: - mutex_unlock(&f->lock); - videobuf_dvb_unregister_bus(f); - return res; -} -EXPORT_SYMBOL(videobuf_dvb_register_bus); - -void videobuf_dvb_unregister_bus(struct videobuf_dvb_frontends *f) -{ - videobuf_dvb_dealloc_frontends(f); - - dvb_unregister_adapter(&f->adapter); -} -EXPORT_SYMBOL(videobuf_dvb_unregister_bus); - -struct videobuf_dvb_frontend *videobuf_dvb_get_frontend( - struct videobuf_dvb_frontends *f, int id) -{ - struct list_head *list, *q; - struct videobuf_dvb_frontend *fe, *ret = NULL; - - mutex_lock(&f->lock); - - list_for_each_safe(list, q, &f->felist) { - fe = list_entry(list, struct videobuf_dvb_frontend, felist); - if (fe->id == id) { - ret = fe; - break; - } - } - - mutex_unlock(&f->lock); - - return ret; -} -EXPORT_SYMBOL(videobuf_dvb_get_frontend); - -int videobuf_dvb_find_frontend(struct videobuf_dvb_frontends *f, - struct dvb_frontend *p) -{ - struct list_head *list, *q; - struct videobuf_dvb_frontend *fe = NULL; - int ret = 0; - - mutex_lock(&f->lock); - - list_for_each_safe(list, q, &f->felist) { - fe = list_entry(list, struct videobuf_dvb_frontend, felist); - if (fe->dvb.frontend == p) { - ret = fe->id; - break; - } - } - - mutex_unlock(&f->lock); - - return ret; -} -EXPORT_SYMBOL(videobuf_dvb_find_frontend); - -struct videobuf_dvb_frontend *videobuf_dvb_alloc_frontend( - struct videobuf_dvb_frontends *f, int id) -{ - struct videobuf_dvb_frontend *fe; - - fe = kzalloc(sizeof(struct videobuf_dvb_frontend), GFP_KERNEL); - if (fe == NULL) - goto fail_alloc; - - fe->id = id; - mutex_init(&fe->dvb.lock); - - mutex_lock(&f->lock); - list_add_tail(&fe->felist, &f->felist); - mutex_unlock(&f->lock); - -fail_alloc: - return fe; -} -EXPORT_SYMBOL(videobuf_dvb_alloc_frontend); - -void videobuf_dvb_dealloc_frontends(struct videobuf_dvb_frontends *f) -{ - struct list_head *list, *q; - struct videobuf_dvb_frontend *fe; - - mutex_lock(&f->lock); - list_for_each_safe(list, q, &f->felist) { - fe = list_entry(list, struct videobuf_dvb_frontend, felist); - if (fe->dvb.net.dvbdev) { - dvb_net_release(&fe->dvb.net); - fe->dvb.demux.dmx.remove_frontend(&fe->dvb.demux.dmx, - &fe->dvb.fe_mem); - fe->dvb.demux.dmx.remove_frontend(&fe->dvb.demux.dmx, - &fe->dvb.fe_hw); - dvb_dmxdev_release(&fe->dvb.dmxdev); - dvb_dmx_release(&fe->dvb.demux); - dvb_unregister_frontend(fe->dvb.frontend); - } - if (fe->dvb.frontend) - /* always allocated, may have been reset */ - dvb_frontend_detach(fe->dvb.frontend); - list_del(list); /* remove list entry */ - kfree(fe); /* free frontend allocation */ - } - mutex_unlock(&f->lock); -} -EXPORT_SYMBOL(videobuf_dvb_dealloc_frontends); diff --git a/drivers/media/video/videobuf-vmalloc.c b/drivers/media/video/videobuf-vmalloc.c deleted file mode 100644 index df142580e44c..000000000000 --- a/drivers/media/video/videobuf-vmalloc.c +++ /dev/null @@ -1,349 +0,0 @@ -/* - * helper functions for vmalloc video4linux capture buffers - * - * The functions expect the hardware being able to scatter gather - * (i.e. the buffers are not linear in physical memory, but fragmented - * into PAGE_SIZE chunks). They also assume the driver does not need - * to touch the video data. - * - * (c) 2007 Mauro Carvalho Chehab, <mchehab@infradead.org> - * - * 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 - */ - -#include <linux/init.h> -#include <linux/module.h> -#include <linux/moduleparam.h> -#include <linux/slab.h> -#include <linux/interrupt.h> - -#include <linux/pci.h> -#include <linux/vmalloc.h> -#include <linux/pagemap.h> -#include <asm/page.h> -#include <asm/pgtable.h> - -#include <media/videobuf-vmalloc.h> - -#define MAGIC_DMABUF 0x17760309 -#define MAGIC_VMAL_MEM 0x18221223 - -#define MAGIC_CHECK(is, should) \ - if (unlikely((is) != (should))) { \ - printk(KERN_ERR "magic mismatch: %x (expected %x)\n", \ - is, should); \ - BUG(); \ - } - -static int debug; -module_param(debug, int, 0644); - -MODULE_DESCRIPTION("helper module to manage video4linux vmalloc buffers"); -MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>"); -MODULE_LICENSE("GPL"); - -#define dprintk(level, fmt, arg...) \ - if (debug >= level) \ - printk(KERN_DEBUG "vbuf-vmalloc: " fmt , ## arg) - - -/***************************************************************************/ - -static void videobuf_vm_open(struct vm_area_struct *vma) -{ - struct videobuf_mapping *map = vma->vm_private_data; - - dprintk(2, "vm_open %p [count=%u,vma=%08lx-%08lx]\n", map, - map->count, vma->vm_start, vma->vm_end); - - map->count++; -} - -static void videobuf_vm_close(struct vm_area_struct *vma) -{ - struct videobuf_mapping *map = vma->vm_private_data; - struct videobuf_queue *q = map->q; - int i; - - dprintk(2, "vm_close %p [count=%u,vma=%08lx-%08lx]\n", map, - map->count, vma->vm_start, vma->vm_end); - - map->count--; - if (0 == map->count) { - struct videobuf_vmalloc_memory *mem; - - dprintk(1, "munmap %p q=%p\n", map, q); - videobuf_queue_lock(q); - - /* We need first to cancel streams, before unmapping */ - if (q->streaming) - videobuf_queue_cancel(q); - - for (i = 0; i < VIDEO_MAX_FRAME; i++) { - if (NULL == q->bufs[i]) - continue; - - if (q->bufs[i]->map != map) - continue; - - mem = q->bufs[i]->priv; - if (mem) { - /* This callback is called only if kernel has - allocated memory and this memory is mmapped. - In this case, memory should be freed, - in order to do memory unmap. - */ - - MAGIC_CHECK(mem->magic, MAGIC_VMAL_MEM); - - /* vfree is not atomic - can't be - called with IRQ's disabled - */ - dprintk(1, "%s: buf[%d] freeing (%p)\n", - __func__, i, mem->vaddr); - - vfree(mem->vaddr); - mem->vaddr = NULL; - } - - q->bufs[i]->map = NULL; - q->bufs[i]->baddr = 0; - } - - kfree(map); - - videobuf_queue_unlock(q); - } - - return; -} - -static const struct vm_operations_struct videobuf_vm_ops = { - .open = videobuf_vm_open, - .close = videobuf_vm_close, -}; - -/* --------------------------------------------------------------------- - * vmalloc handlers for the generic methods - */ - -/* Allocated area consists on 3 parts: - struct video_buffer - struct <driver>_buffer (cx88_buffer, saa7134_buf, ...) - struct videobuf_dma_sg_memory - */ - -static struct videobuf_buffer *__videobuf_alloc_vb(size_t size) -{ - struct videobuf_vmalloc_memory *mem; - struct videobuf_buffer *vb; - - vb = kzalloc(size + sizeof(*mem), GFP_KERNEL); - if (!vb) - return vb; - - mem = vb->priv = ((char *)vb) + size; - mem->magic = MAGIC_VMAL_MEM; - - dprintk(1, "%s: allocated at %p(%ld+%ld) & %p(%ld)\n", - __func__, vb, (long)sizeof(*vb), (long)size - sizeof(*vb), - mem, (long)sizeof(*mem)); - - return vb; -} - -static int __videobuf_iolock(struct videobuf_queue *q, - struct videobuf_buffer *vb, - struct v4l2_framebuffer *fbuf) -{ - struct videobuf_vmalloc_memory *mem = vb->priv; - int pages; - - BUG_ON(!mem); - - MAGIC_CHECK(mem->magic, MAGIC_VMAL_MEM); - - switch (vb->memory) { - case V4L2_MEMORY_MMAP: - dprintk(1, "%s memory method MMAP\n", __func__); - - /* All handling should be done by __videobuf_mmap_mapper() */ - if (!mem->vaddr) { - printk(KERN_ERR "memory is not alloced/mmapped.\n"); - return -EINVAL; - } - break; - case V4L2_MEMORY_USERPTR: - pages = PAGE_ALIGN(vb->size); - - dprintk(1, "%s memory method USERPTR\n", __func__); - - if (vb->baddr) { - printk(KERN_ERR "USERPTR is currently not supported\n"); - return -EINVAL; - } - - /* The only USERPTR currently supported is the one needed for - * read() method. - */ - - mem->vaddr = vmalloc_user(pages); - if (!mem->vaddr) { - printk(KERN_ERR "vmalloc (%d pages) failed\n", pages); - return -ENOMEM; - } - dprintk(1, "vmalloc is at addr %p (%d pages)\n", - mem->vaddr, pages); - -#if 0 - int rc; - /* Kernel userptr is used also by read() method. In this case, - there's no need to remap, since data will be copied to user - */ - if (!vb->baddr) - return 0; - - /* FIXME: to properly support USERPTR, remap should occur. - The code below won't work, since mem->vma = NULL - */ - /* Try to remap memory */ - rc = remap_vmalloc_range(mem->vma, (void *)vb->baddr, 0); - if (rc < 0) { - printk(KERN_ERR "mmap: remap failed with error %d", rc); - return -ENOMEM; - } -#endif - - break; - case V4L2_MEMORY_OVERLAY: - default: - dprintk(1, "%s memory method OVERLAY/unknown\n", __func__); - - /* Currently, doesn't support V4L2_MEMORY_OVERLAY */ - printk(KERN_ERR "Memory method currently unsupported.\n"); - return -EINVAL; - } - - return 0; -} - -static int __videobuf_mmap_mapper(struct videobuf_queue *q, - struct videobuf_buffer *buf, - struct vm_area_struct *vma) -{ - struct videobuf_vmalloc_memory *mem; - struct videobuf_mapping *map; - int retval, pages; - - dprintk(1, "%s\n", __func__); - - /* create mapping + update buffer list */ - map = kzalloc(sizeof(struct videobuf_mapping), GFP_KERNEL); - if (NULL == map) - return -ENOMEM; - - buf->map = map; - map->q = q; - - buf->baddr = vma->vm_start; - - mem = buf->priv; - BUG_ON(!mem); - MAGIC_CHECK(mem->magic, MAGIC_VMAL_MEM); - - pages = PAGE_ALIGN(vma->vm_end - vma->vm_start); - mem->vaddr = vmalloc_user(pages); - if (!mem->vaddr) { - printk(KERN_ERR "vmalloc (%d pages) failed\n", pages); - goto error; - } - dprintk(1, "vmalloc is at addr %p (%d pages)\n", mem->vaddr, pages); - - /* Try to remap memory */ - retval = remap_vmalloc_range(vma, mem->vaddr, 0); - if (retval < 0) { - printk(KERN_ERR "mmap: remap failed with error %d. ", retval); - vfree(mem->vaddr); - goto error; - } - - vma->vm_ops = &videobuf_vm_ops; - vma->vm_flags |= VM_DONTEXPAND | VM_RESERVED; - vma->vm_private_data = map; - - dprintk(1, "mmap %p: q=%p %08lx-%08lx (%lx) pgoff %08lx buf %d\n", - map, q, vma->vm_start, vma->vm_end, - (long int)buf->bsize, - vma->vm_pgoff, buf->i); - - videobuf_vm_open(vma); - - return 0; - -error: - mem = NULL; - kfree(map); - return -ENOMEM; -} - -static struct videobuf_qtype_ops qops = { - .magic = MAGIC_QTYPE_OPS, - - .alloc_vb = __videobuf_alloc_vb, - .iolock = __videobuf_iolock, - .mmap_mapper = __videobuf_mmap_mapper, - .vaddr = videobuf_to_vmalloc, -}; - -void videobuf_queue_vmalloc_init(struct videobuf_queue *q, - const struct videobuf_queue_ops *ops, - struct device *dev, - spinlock_t *irqlock, - enum v4l2_buf_type type, - enum v4l2_field field, - unsigned int msize, - void *priv, - struct mutex *ext_lock) -{ - videobuf_queue_core_init(q, ops, dev, irqlock, type, field, msize, - priv, &qops, ext_lock); -} -EXPORT_SYMBOL_GPL(videobuf_queue_vmalloc_init); - -void *videobuf_to_vmalloc(struct videobuf_buffer *buf) -{ - struct videobuf_vmalloc_memory *mem = buf->priv; - BUG_ON(!mem); - MAGIC_CHECK(mem->magic, MAGIC_VMAL_MEM); - - return mem->vaddr; -} -EXPORT_SYMBOL_GPL(videobuf_to_vmalloc); - -void videobuf_vmalloc_free(struct videobuf_buffer *buf) -{ - struct videobuf_vmalloc_memory *mem = buf->priv; - - /* mmapped memory can't be freed here, otherwise mmapped region - would be released, while still needed. In this case, the memory - release should happen inside videobuf_vm_close(). - So, it should free memory only if the memory were allocated for - read() operation. - */ - if ((buf->memory != V4L2_MEMORY_USERPTR) || buf->baddr) - return; - - if (!mem) - return; - - MAGIC_CHECK(mem->magic, MAGIC_VMAL_MEM); - - vfree(mem->vaddr); - mem->vaddr = NULL; - - return; -} -EXPORT_SYMBOL_GPL(videobuf_vmalloc_free); - diff --git a/drivers/media/video/videobuf2-core.c b/drivers/media/video/videobuf2-core.c deleted file mode 100644 index 4da3df61901f..000000000000 --- a/drivers/media/video/videobuf2-core.c +++ /dev/null @@ -1,2380 +0,0 @@ -/* - * videobuf2-core.c - V4L2 driver helper framework - * - * Copyright (C) 2010 Samsung Electronics - * - * Author: Pawel Osciak <pawel@osciak.com> - * Marek Szyprowski <m.szyprowski@samsung.com> - * - * 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. - */ - -#include <linux/err.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/mm.h> -#include <linux/poll.h> -#include <linux/slab.h> -#include <linux/sched.h> - -#include <media/v4l2-dev.h> -#include <media/v4l2-fh.h> -#include <media/v4l2-event.h> -#include <media/videobuf2-core.h> - -static int debug; -module_param(debug, int, 0644); - -#define dprintk(level, fmt, arg...) \ - do { \ - if (debug >= level) \ - printk(KERN_DEBUG "vb2: " fmt, ## arg); \ - } while (0) - -#define call_memop(q, op, args...) \ - (((q)->mem_ops->op) ? \ - ((q)->mem_ops->op(args)) : 0) - -#define call_qop(q, op, args...) \ - (((q)->ops->op) ? ((q)->ops->op(args)) : 0) - -#define V4L2_BUFFER_STATE_FLAGS (V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_QUEUED | \ - V4L2_BUF_FLAG_DONE | V4L2_BUF_FLAG_ERROR | \ - V4L2_BUF_FLAG_PREPARED) - -/** - * __vb2_buf_mem_alloc() - allocate video memory for the given buffer - */ -static int __vb2_buf_mem_alloc(struct vb2_buffer *vb) -{ - struct vb2_queue *q = vb->vb2_queue; - void *mem_priv; - int plane; - - /* Allocate memory for all planes in this buffer */ - for (plane = 0; plane < vb->num_planes; ++plane) { - mem_priv = call_memop(q, alloc, q->alloc_ctx[plane], - q->plane_sizes[plane]); - if (IS_ERR_OR_NULL(mem_priv)) - goto free; - - /* Associate allocator private data with this plane */ - vb->planes[plane].mem_priv = mem_priv; - vb->v4l2_planes[plane].length = q->plane_sizes[plane]; - } - - return 0; -free: - /* Free already allocated memory if one of the allocations failed */ - for (; plane > 0; --plane) { - call_memop(q, put, vb->planes[plane - 1].mem_priv); - vb->planes[plane - 1].mem_priv = NULL; - } - - return -ENOMEM; -} - -/** - * __vb2_buf_mem_free() - free memory of the given buffer - */ -static void __vb2_buf_mem_free(struct vb2_buffer *vb) -{ - struct vb2_queue *q = vb->vb2_queue; - unsigned int plane; - - for (plane = 0; plane < vb->num_planes; ++plane) { - call_memop(q, put, vb->planes[plane].mem_priv); - vb->planes[plane].mem_priv = NULL; - dprintk(3, "Freed plane %d of buffer %d\n", plane, - vb->v4l2_buf.index); - } -} - -/** - * __vb2_buf_userptr_put() - release userspace memory associated with - * a USERPTR buffer - */ -static void __vb2_buf_userptr_put(struct vb2_buffer *vb) -{ - struct vb2_queue *q = vb->vb2_queue; - unsigned int plane; - - for (plane = 0; plane < vb->num_planes; ++plane) { - if (vb->planes[plane].mem_priv) - call_memop(q, put_userptr, vb->planes[plane].mem_priv); - vb->planes[plane].mem_priv = NULL; - } -} - -/** - * __setup_offsets() - setup unique offsets ("cookies") for every plane in - * every buffer on the queue - */ -static void __setup_offsets(struct vb2_queue *q, unsigned int n) -{ - unsigned int buffer, plane; - struct vb2_buffer *vb; - unsigned long off; - - if (q->num_buffers) { - struct v4l2_plane *p; - vb = q->bufs[q->num_buffers - 1]; - p = &vb->v4l2_planes[vb->num_planes - 1]; - off = PAGE_ALIGN(p->m.mem_offset + p->length); - } else { - off = 0; - } - - for (buffer = q->num_buffers; buffer < q->num_buffers + n; ++buffer) { - vb = q->bufs[buffer]; - if (!vb) - continue; - - for (plane = 0; plane < vb->num_planes; ++plane) { - vb->v4l2_planes[plane].length = q->plane_sizes[plane]; - vb->v4l2_planes[plane].m.mem_offset = off; - - dprintk(3, "Buffer %d, plane %d offset 0x%08lx\n", - buffer, plane, off); - - off += vb->v4l2_planes[plane].length; - off = PAGE_ALIGN(off); - } - } -} - -/** - * __vb2_queue_alloc() - allocate videobuf buffer structures and (for MMAP type) - * video buffer memory for all buffers/planes on the queue and initializes the - * queue - * - * Returns the number of buffers successfully allocated. - */ -static int __vb2_queue_alloc(struct vb2_queue *q, enum v4l2_memory memory, - unsigned int num_buffers, unsigned int num_planes) -{ - unsigned int buffer; - struct vb2_buffer *vb; - int ret; - - for (buffer = 0; buffer < num_buffers; ++buffer) { - /* Allocate videobuf buffer structures */ - vb = kzalloc(q->buf_struct_size, GFP_KERNEL); - if (!vb) { - dprintk(1, "Memory alloc for buffer struct failed\n"); - break; - } - - /* Length stores number of planes for multiplanar buffers */ - if (V4L2_TYPE_IS_MULTIPLANAR(q->type)) - vb->v4l2_buf.length = num_planes; - - vb->state = VB2_BUF_STATE_DEQUEUED; - vb->vb2_queue = q; - vb->num_planes = num_planes; - vb->v4l2_buf.index = q->num_buffers + buffer; - vb->v4l2_buf.type = q->type; - vb->v4l2_buf.memory = memory; - - /* Allocate video buffer memory for the MMAP type */ - if (memory == V4L2_MEMORY_MMAP) { - ret = __vb2_buf_mem_alloc(vb); - if (ret) { - dprintk(1, "Failed allocating memory for " - "buffer %d\n", buffer); - kfree(vb); - break; - } - /* - * Call the driver-provided buffer initialization - * callback, if given. An error in initialization - * results in queue setup failure. - */ - ret = call_qop(q, buf_init, vb); - if (ret) { - dprintk(1, "Buffer %d %p initialization" - " failed\n", buffer, vb); - __vb2_buf_mem_free(vb); - kfree(vb); - break; - } - } - - q->bufs[q->num_buffers + buffer] = vb; - } - - __setup_offsets(q, buffer); - - dprintk(1, "Allocated %d buffers, %d plane(s) each\n", - buffer, num_planes); - - return buffer; -} - -/** - * __vb2_free_mem() - release all video buffer memory for a given queue - */ -static void __vb2_free_mem(struct vb2_queue *q, unsigned int buffers) -{ - unsigned int buffer; - struct vb2_buffer *vb; - - for (buffer = q->num_buffers - buffers; buffer < q->num_buffers; - ++buffer) { - vb = q->bufs[buffer]; - if (!vb) - continue; - - /* Free MMAP buffers or release USERPTR buffers */ - if (q->memory == V4L2_MEMORY_MMAP) - __vb2_buf_mem_free(vb); - else - __vb2_buf_userptr_put(vb); - } -} - -/** - * __vb2_queue_free() - free buffers at the end of the queue - video memory and - * related information, if no buffers are left return the queue to an - * uninitialized state. Might be called even if the queue has already been freed. - */ -static void __vb2_queue_free(struct vb2_queue *q, unsigned int buffers) -{ - unsigned int buffer; - - /* Call driver-provided cleanup function for each buffer, if provided */ - if (q->ops->buf_cleanup) { - for (buffer = q->num_buffers - buffers; buffer < q->num_buffers; - ++buffer) { - if (NULL == q->bufs[buffer]) - continue; - q->ops->buf_cleanup(q->bufs[buffer]); - } - } - - /* Release video buffer memory */ - __vb2_free_mem(q, buffers); - - /* Free videobuf buffers */ - for (buffer = q->num_buffers - buffers; buffer < q->num_buffers; - ++buffer) { - kfree(q->bufs[buffer]); - q->bufs[buffer] = NULL; - } - - q->num_buffers -= buffers; - if (!q->num_buffers) - q->memory = 0; - INIT_LIST_HEAD(&q->queued_list); -} - -/** - * __verify_planes_array() - verify that the planes array passed in struct - * v4l2_buffer from userspace can be safely used - */ -static int __verify_planes_array(struct vb2_buffer *vb, const struct v4l2_buffer *b) -{ - /* Is memory for copying plane information present? */ - if (NULL == b->m.planes) { - dprintk(1, "Multi-planar buffer passed but " - "planes array not provided\n"); - return -EINVAL; - } - - if (b->length < vb->num_planes || b->length > VIDEO_MAX_PLANES) { - dprintk(1, "Incorrect planes array length, " - "expected %d, got %d\n", vb->num_planes, b->length); - return -EINVAL; - } - - return 0; -} - -/** - * __buffer_in_use() - return true if the buffer is in use and - * the queue cannot be freed (by the means of REQBUFS(0)) call - */ -static bool __buffer_in_use(struct vb2_queue *q, struct vb2_buffer *vb) -{ - unsigned int plane; - for (plane = 0; plane < vb->num_planes; ++plane) { - void *mem_priv = vb->planes[plane].mem_priv; - /* - * If num_users() has not been provided, call_memop - * will return 0, apparently nobody cares about this - * case anyway. If num_users() returns more than 1, - * we are not the only user of the plane's memory. - */ - if (mem_priv && call_memop(q, num_users, mem_priv) > 1) - return true; - } - return false; -} - -/** - * __buffers_in_use() - return true if any buffers on the queue are in use and - * the queue cannot be freed (by the means of REQBUFS(0)) call - */ -static bool __buffers_in_use(struct vb2_queue *q) -{ - unsigned int buffer; - for (buffer = 0; buffer < q->num_buffers; ++buffer) { - if (__buffer_in_use(q, q->bufs[buffer])) - return true; - } - return false; -} - -/** - * __fill_v4l2_buffer() - fill in a struct v4l2_buffer with information to be - * returned to userspace - */ -static int __fill_v4l2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b) -{ - struct vb2_queue *q = vb->vb2_queue; - int ret; - - /* Copy back data such as timestamp, flags, etc. */ - memcpy(b, &vb->v4l2_buf, offsetof(struct v4l2_buffer, m)); - b->reserved2 = vb->v4l2_buf.reserved2; - b->reserved = vb->v4l2_buf.reserved; - - if (V4L2_TYPE_IS_MULTIPLANAR(q->type)) { - ret = __verify_planes_array(vb, b); - if (ret) - return ret; - - /* - * Fill in plane-related data if userspace provided an array - * for it. The memory and size is verified above. - */ - memcpy(b->m.planes, vb->v4l2_planes, - b->length * sizeof(struct v4l2_plane)); - } else { - /* - * We use length and offset in v4l2_planes array even for - * single-planar buffers, but userspace does not. - */ - b->length = vb->v4l2_planes[0].length; - b->bytesused = vb->v4l2_planes[0].bytesused; - if (q->memory == V4L2_MEMORY_MMAP) - b->m.offset = vb->v4l2_planes[0].m.mem_offset; - else if (q->memory == V4L2_MEMORY_USERPTR) - b->m.userptr = vb->v4l2_planes[0].m.userptr; - } - - /* - * Clear any buffer state related flags. - */ - b->flags &= ~V4L2_BUFFER_STATE_FLAGS; - - switch (vb->state) { - case VB2_BUF_STATE_QUEUED: - case VB2_BUF_STATE_ACTIVE: - b->flags |= V4L2_BUF_FLAG_QUEUED; - break; - case VB2_BUF_STATE_ERROR: - b->flags |= V4L2_BUF_FLAG_ERROR; - /* fall through */ - case VB2_BUF_STATE_DONE: - b->flags |= V4L2_BUF_FLAG_DONE; - break; - case VB2_BUF_STATE_PREPARED: - b->flags |= V4L2_BUF_FLAG_PREPARED; - break; - case VB2_BUF_STATE_DEQUEUED: - /* nothing */ - break; - } - - if (__buffer_in_use(q, vb)) - b->flags |= V4L2_BUF_FLAG_MAPPED; - - return 0; -} - -/** - * vb2_querybuf() - query video buffer information - * @q: videobuf queue - * @b: buffer struct passed from userspace to vidioc_querybuf handler - * in driver - * - * Should be called from vidioc_querybuf ioctl handler in driver. - * This function will verify the passed v4l2_buffer structure and fill the - * relevant information for the userspace. - * - * The return values from this function are intended to be directly returned - * from vidioc_querybuf handler in driver. - */ -int vb2_querybuf(struct vb2_queue *q, struct v4l2_buffer *b) -{ - struct vb2_buffer *vb; - - if (b->type != q->type) { - dprintk(1, "querybuf: wrong buffer type\n"); - return -EINVAL; - } - - if (b->index >= q->num_buffers) { - dprintk(1, "querybuf: buffer index out of range\n"); - return -EINVAL; - } - vb = q->bufs[b->index]; - - return __fill_v4l2_buffer(vb, b); -} -EXPORT_SYMBOL(vb2_querybuf); - -/** - * __verify_userptr_ops() - verify that all memory operations required for - * USERPTR queue type have been provided - */ -static int __verify_userptr_ops(struct vb2_queue *q) -{ - if (!(q->io_modes & VB2_USERPTR) || !q->mem_ops->get_userptr || - !q->mem_ops->put_userptr) - return -EINVAL; - - return 0; -} - -/** - * __verify_mmap_ops() - verify that all memory operations required for - * MMAP queue type have been provided - */ -static int __verify_mmap_ops(struct vb2_queue *q) -{ - if (!(q->io_modes & VB2_MMAP) || !q->mem_ops->alloc || - !q->mem_ops->put || !q->mem_ops->mmap) - return -EINVAL; - - return 0; -} - -/** - * __verify_memory_type() - Check whether the memory type and buffer type - * passed to a buffer operation are compatible with the queue. - */ -static int __verify_memory_type(struct vb2_queue *q, - enum v4l2_memory memory, enum v4l2_buf_type type) -{ - if (memory != V4L2_MEMORY_MMAP && memory != V4L2_MEMORY_USERPTR) { - dprintk(1, "reqbufs: unsupported memory type\n"); - return -EINVAL; - } - - if (type != q->type) { - dprintk(1, "reqbufs: requested type is incorrect\n"); - return -EINVAL; - } - - /* - * Make sure all the required memory ops for given memory type - * are available. - */ - if (memory == V4L2_MEMORY_MMAP && __verify_mmap_ops(q)) { - dprintk(1, "reqbufs: MMAP for current setup unsupported\n"); - return -EINVAL; - } - - if (memory == V4L2_MEMORY_USERPTR && __verify_userptr_ops(q)) { - dprintk(1, "reqbufs: USERPTR for current setup unsupported\n"); - return -EINVAL; - } - - /* - * Place the busy tests at the end: -EBUSY can be ignored when - * create_bufs is called with count == 0, but count == 0 should still - * do the memory and type validation. - */ - if (q->fileio) { - dprintk(1, "reqbufs: file io in progress\n"); - return -EBUSY; - } - return 0; -} - -/** - * __reqbufs() - Initiate streaming - * @q: videobuf2 queue - * @req: struct passed from userspace to vidioc_reqbufs handler in driver - * - * Should be called from vidioc_reqbufs ioctl handler of a driver. - * This function: - * 1) verifies streaming parameters passed from the userspace, - * 2) sets up the queue, - * 3) negotiates number of buffers and planes per buffer with the driver - * to be used during streaming, - * 4) allocates internal buffer structures (struct vb2_buffer), according to - * the agreed parameters, - * 5) for MMAP memory type, allocates actual video memory, using the - * memory handling/allocation routines provided during queue initialization - * - * If req->count is 0, all the memory will be freed instead. - * If the queue has been allocated previously (by a previous vb2_reqbufs) call - * and the queue is not busy, memory will be reallocated. - * - * The return values from this function are intended to be directly returned - * from vidioc_reqbufs handler in driver. - */ -static int __reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req) -{ - unsigned int num_buffers, allocated_buffers, num_planes = 0; - int ret; - - if (q->streaming) { - dprintk(1, "reqbufs: streaming active\n"); - return -EBUSY; - } - - if (req->count == 0 || q->num_buffers != 0 || q->memory != req->memory) { - /* - * We already have buffers allocated, so first check if they - * are not in use and can be freed. - */ - if (q->memory == V4L2_MEMORY_MMAP && __buffers_in_use(q)) { - dprintk(1, "reqbufs: memory in use, cannot free\n"); - return -EBUSY; - } - - __vb2_queue_free(q, q->num_buffers); - - /* - * In case of REQBUFS(0) return immediately without calling - * driver's queue_setup() callback and allocating resources. - */ - if (req->count == 0) - return 0; - } - - /* - * Make sure the requested values and current defaults are sane. - */ - num_buffers = min_t(unsigned int, req->count, VIDEO_MAX_FRAME); - memset(q->plane_sizes, 0, sizeof(q->plane_sizes)); - memset(q->alloc_ctx, 0, sizeof(q->alloc_ctx)); - q->memory = req->memory; - - /* - * Ask the driver how many buffers and planes per buffer it requires. - * Driver also sets the size and allocator context for each plane. - */ - ret = call_qop(q, queue_setup, q, NULL, &num_buffers, &num_planes, - q->plane_sizes, q->alloc_ctx); - if (ret) - return ret; - - /* Finally, allocate buffers and video memory */ - ret = __vb2_queue_alloc(q, req->memory, num_buffers, num_planes); - if (ret == 0) { - dprintk(1, "Memory allocation failed\n"); - return -ENOMEM; - } - - allocated_buffers = ret; - - /* - * Check if driver can handle the allocated number of buffers. - */ - if (allocated_buffers < num_buffers) { - num_buffers = allocated_buffers; - - ret = call_qop(q, queue_setup, q, NULL, &num_buffers, - &num_planes, q->plane_sizes, q->alloc_ctx); - - if (!ret && allocated_buffers < num_buffers) - ret = -ENOMEM; - - /* - * Either the driver has accepted a smaller number of buffers, - * or .queue_setup() returned an error - */ - } - - q->num_buffers = allocated_buffers; - - if (ret < 0) { - __vb2_queue_free(q, allocated_buffers); - return ret; - } - - /* - * Return the number of successfully allocated buffers - * to the userspace. - */ - req->count = allocated_buffers; - - return 0; -} - -/** - * vb2_reqbufs() - Wrapper for __reqbufs() that also verifies the memory and - * type values. - * @q: videobuf2 queue - * @req: struct passed from userspace to vidioc_reqbufs handler in driver - */ -int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req) -{ - int ret = __verify_memory_type(q, req->memory, req->type); - - return ret ? ret : __reqbufs(q, req); -} -EXPORT_SYMBOL_GPL(vb2_reqbufs); - -/** - * __create_bufs() - Allocate buffers and any required auxiliary structs - * @q: videobuf2 queue - * @create: creation parameters, passed from userspace to vidioc_create_bufs - * handler in driver - * - * Should be called from vidioc_create_bufs ioctl handler of a driver. - * This function: - * 1) verifies parameter sanity - * 2) calls the .queue_setup() queue operation - * 3) performs any necessary memory allocations - * - * The return values from this function are intended to be directly returned - * from vidioc_create_bufs handler in driver. - */ -static int __create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create) -{ - unsigned int num_planes = 0, num_buffers, allocated_buffers; - int ret; - - if (q->num_buffers == VIDEO_MAX_FRAME) { - dprintk(1, "%s(): maximum number of buffers already allocated\n", - __func__); - return -ENOBUFS; - } - - if (!q->num_buffers) { - memset(q->plane_sizes, 0, sizeof(q->plane_sizes)); - memset(q->alloc_ctx, 0, sizeof(q->alloc_ctx)); - q->memory = create->memory; - } - - num_buffers = min(create->count, VIDEO_MAX_FRAME - q->num_buffers); - - /* - * Ask the driver, whether the requested number of buffers, planes per - * buffer and their sizes are acceptable - */ - ret = call_qop(q, queue_setup, q, &create->format, &num_buffers, - &num_planes, q->plane_sizes, q->alloc_ctx); - if (ret) - return ret; - - /* Finally, allocate buffers and video memory */ - ret = __vb2_queue_alloc(q, create->memory, num_buffers, - num_planes); - if (ret == 0) { - dprintk(1, "Memory allocation failed\n"); - return -ENOMEM; - } - - allocated_buffers = ret; - - /* - * Check if driver can handle the so far allocated number of buffers. - */ - if (ret < num_buffers) { - num_buffers = ret; - - /* - * q->num_buffers contains the total number of buffers, that the - * queue driver has set up - */ - ret = call_qop(q, queue_setup, q, &create->format, &num_buffers, - &num_planes, q->plane_sizes, q->alloc_ctx); - - if (!ret && allocated_buffers < num_buffers) - ret = -ENOMEM; - - /* - * Either the driver has accepted a smaller number of buffers, - * or .queue_setup() returned an error - */ - } - - q->num_buffers += allocated_buffers; - - if (ret < 0) { - __vb2_queue_free(q, allocated_buffers); - return -ENOMEM; - } - - /* - * Return the number of successfully allocated buffers - * to the userspace. - */ - create->count = allocated_buffers; - - return 0; -} - -/** - * vb2_create_bufs() - Wrapper for __create_bufs() that also verifies the - * memory and type values. - * @q: videobuf2 queue - * @create: creation parameters, passed from userspace to vidioc_create_bufs - * handler in driver - */ -int vb2_create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create) -{ - int ret = __verify_memory_type(q, create->memory, create->format.type); - - create->index = q->num_buffers; - if (create->count == 0) - return ret != -EBUSY ? ret : 0; - return ret ? ret : __create_bufs(q, create); -} -EXPORT_SYMBOL_GPL(vb2_create_bufs); - -/** - * vb2_plane_vaddr() - Return a kernel virtual address of a given plane - * @vb: vb2_buffer to which the plane in question belongs to - * @plane_no: plane number for which the address is to be returned - * - * This function returns a kernel virtual address of a given plane if - * such a mapping exist, NULL otherwise. - */ -void *vb2_plane_vaddr(struct vb2_buffer *vb, unsigned int plane_no) -{ - struct vb2_queue *q = vb->vb2_queue; - - if (plane_no > vb->num_planes || !vb->planes[plane_no].mem_priv) - return NULL; - - return call_memop(q, vaddr, vb->planes[plane_no].mem_priv); - -} -EXPORT_SYMBOL_GPL(vb2_plane_vaddr); - -/** - * vb2_plane_cookie() - Return allocator specific cookie for the given plane - * @vb: vb2_buffer to which the plane in question belongs to - * @plane_no: plane number for which the cookie is to be returned - * - * This function returns an allocator specific cookie for a given plane if - * available, NULL otherwise. The allocator should provide some simple static - * inline function, which would convert this cookie to the allocator specific - * type that can be used directly by the driver to access the buffer. This can - * be for example physical address, pointer to scatter list or IOMMU mapping. - */ -void *vb2_plane_cookie(struct vb2_buffer *vb, unsigned int plane_no) -{ - struct vb2_queue *q = vb->vb2_queue; - - if (plane_no > vb->num_planes || !vb->planes[plane_no].mem_priv) - return NULL; - - return call_memop(q, cookie, vb->planes[plane_no].mem_priv); -} -EXPORT_SYMBOL_GPL(vb2_plane_cookie); - -/** - * vb2_buffer_done() - inform videobuf that an operation on a buffer is finished - * @vb: vb2_buffer returned from the driver - * @state: either VB2_BUF_STATE_DONE if the operation finished successfully - * or VB2_BUF_STATE_ERROR if the operation finished with an error - * - * This function should be called by the driver after a hardware operation on - * a buffer is finished and the buffer may be returned to userspace. The driver - * cannot use this buffer anymore until it is queued back to it by videobuf - * by the means of buf_queue callback. Only buffers previously queued to the - * driver by buf_queue can be passed to this function. - */ -void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state) -{ - struct vb2_queue *q = vb->vb2_queue; - unsigned long flags; - - if (vb->state != VB2_BUF_STATE_ACTIVE) - return; - - if (state != VB2_BUF_STATE_DONE && state != VB2_BUF_STATE_ERROR) - return; - - dprintk(4, "Done processing on buffer %d, state: %d\n", - vb->v4l2_buf.index, vb->state); - - /* Add the buffer to the done buffers list */ - spin_lock_irqsave(&q->done_lock, flags); - vb->state = state; - list_add_tail(&vb->done_entry, &q->done_list); - atomic_dec(&q->queued_count); - spin_unlock_irqrestore(&q->done_lock, flags); - - /* Inform any processes that may be waiting for buffers */ - wake_up(&q->done_wq); -} -EXPORT_SYMBOL_GPL(vb2_buffer_done); - -/** - * __fill_vb2_buffer() - fill a vb2_buffer with information provided in - * a v4l2_buffer by the userspace - */ -static int __fill_vb2_buffer(struct vb2_buffer *vb, const struct v4l2_buffer *b, - struct v4l2_plane *v4l2_planes) -{ - unsigned int plane; - int ret; - - if (V4L2_TYPE_IS_MULTIPLANAR(b->type)) { - /* - * Verify that the userspace gave us a valid array for - * plane information. - */ - ret = __verify_planes_array(vb, b); - if (ret) - return ret; - - /* Fill in driver-provided information for OUTPUT types */ - if (V4L2_TYPE_IS_OUTPUT(b->type)) { - /* - * Will have to go up to b->length when API starts - * accepting variable number of planes. - */ - for (plane = 0; plane < vb->num_planes; ++plane) { - v4l2_planes[plane].bytesused = - b->m.planes[plane].bytesused; - v4l2_planes[plane].data_offset = - b->m.planes[plane].data_offset; - } - } - - if (b->memory == V4L2_MEMORY_USERPTR) { - for (plane = 0; plane < vb->num_planes; ++plane) { - v4l2_planes[plane].m.userptr = - b->m.planes[plane].m.userptr; - v4l2_planes[plane].length = - b->m.planes[plane].length; - } - } - } else { - /* - * Single-planar buffers do not use planes array, - * so fill in relevant v4l2_buffer struct fields instead. - * In videobuf we use our internal V4l2_planes struct for - * single-planar buffers as well, for simplicity. - */ - if (V4L2_TYPE_IS_OUTPUT(b->type)) - v4l2_planes[0].bytesused = b->bytesused; - - if (b->memory == V4L2_MEMORY_USERPTR) { - v4l2_planes[0].m.userptr = b->m.userptr; - v4l2_planes[0].length = b->length; - } - } - - vb->v4l2_buf.field = b->field; - vb->v4l2_buf.timestamp = b->timestamp; - vb->v4l2_buf.flags = b->flags & ~V4L2_BUFFER_STATE_FLAGS; - - return 0; -} - -/** - * __qbuf_userptr() - handle qbuf of a USERPTR buffer - */ -static int __qbuf_userptr(struct vb2_buffer *vb, const struct v4l2_buffer *b) -{ - struct v4l2_plane planes[VIDEO_MAX_PLANES]; - struct vb2_queue *q = vb->vb2_queue; - void *mem_priv; - unsigned int plane; - int ret; - int write = !V4L2_TYPE_IS_OUTPUT(q->type); - - /* Verify and copy relevant information provided by the userspace */ - ret = __fill_vb2_buffer(vb, b, planes); - if (ret) - return ret; - - for (plane = 0; plane < vb->num_planes; ++plane) { - /* Skip the plane if already verified */ - if (vb->v4l2_planes[plane].m.userptr && - vb->v4l2_planes[plane].m.userptr == planes[plane].m.userptr - && vb->v4l2_planes[plane].length == planes[plane].length) - continue; - - dprintk(3, "qbuf: userspace address for plane %d changed, " - "reacquiring memory\n", plane); - - /* Check if the provided plane buffer is large enough */ - if (planes[plane].length < q->plane_sizes[plane]) { - ret = -EINVAL; - goto err; - } - - /* Release previously acquired memory if present */ - if (vb->planes[plane].mem_priv) - call_memop(q, put_userptr, vb->planes[plane].mem_priv); - - vb->planes[plane].mem_priv = NULL; - vb->v4l2_planes[plane].m.userptr = 0; - vb->v4l2_planes[plane].length = 0; - - /* Acquire each plane's memory */ - mem_priv = call_memop(q, get_userptr, q->alloc_ctx[plane], - planes[plane].m.userptr, - planes[plane].length, write); - if (IS_ERR_OR_NULL(mem_priv)) { - dprintk(1, "qbuf: failed acquiring userspace " - "memory for plane %d\n", plane); - ret = mem_priv ? PTR_ERR(mem_priv) : -EINVAL; - goto err; - } - vb->planes[plane].mem_priv = mem_priv; - } - - /* - * Call driver-specific initialization on the newly acquired buffer, - * if provided. - */ - ret = call_qop(q, buf_init, vb); - if (ret) { - dprintk(1, "qbuf: buffer initialization failed\n"); - goto err; - } - - /* - * Now that everything is in order, copy relevant information - * provided by userspace. - */ - for (plane = 0; plane < vb->num_planes; ++plane) - vb->v4l2_planes[plane] = planes[plane]; - - return 0; -err: - /* In case of errors, release planes that were already acquired */ - for (plane = 0; plane < vb->num_planes; ++plane) { - if (vb->planes[plane].mem_priv) - call_memop(q, put_userptr, vb->planes[plane].mem_priv); - vb->planes[plane].mem_priv = NULL; - vb->v4l2_planes[plane].m.userptr = 0; - vb->v4l2_planes[plane].length = 0; - } - - return ret; -} - -/** - * __qbuf_mmap() - handle qbuf of an MMAP buffer - */ -static int __qbuf_mmap(struct vb2_buffer *vb, const struct v4l2_buffer *b) -{ - return __fill_vb2_buffer(vb, b, vb->v4l2_planes); -} - -/** - * __enqueue_in_driver() - enqueue a vb2_buffer in driver for processing - */ -static void __enqueue_in_driver(struct vb2_buffer *vb) -{ - struct vb2_queue *q = vb->vb2_queue; - - vb->state = VB2_BUF_STATE_ACTIVE; - atomic_inc(&q->queued_count); - q->ops->buf_queue(vb); -} - -static int __buf_prepare(struct vb2_buffer *vb, const struct v4l2_buffer *b) -{ - struct vb2_queue *q = vb->vb2_queue; - int ret; - - switch (q->memory) { - case V4L2_MEMORY_MMAP: - ret = __qbuf_mmap(vb, b); - break; - case V4L2_MEMORY_USERPTR: - ret = __qbuf_userptr(vb, b); - break; - default: - WARN(1, "Invalid queue type\n"); - ret = -EINVAL; - } - - if (!ret) - ret = call_qop(q, buf_prepare, vb); - if (ret) - dprintk(1, "qbuf: buffer preparation failed: %d\n", ret); - else - vb->state = VB2_BUF_STATE_PREPARED; - - return ret; -} - -/** - * vb2_prepare_buf() - Pass ownership of a buffer from userspace to the kernel - * @q: videobuf2 queue - * @b: buffer structure passed from userspace to vidioc_prepare_buf - * handler in driver - * - * Should be called from vidioc_prepare_buf ioctl handler of a driver. - * This function: - * 1) verifies the passed buffer, - * 2) calls buf_prepare callback in the driver (if provided), in which - * driver-specific buffer initialization can be performed, - * - * The return values from this function are intended to be directly returned - * from vidioc_prepare_buf handler in driver. - */ -int vb2_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b) -{ - struct vb2_buffer *vb; - int ret; - - if (q->fileio) { - dprintk(1, "%s(): file io in progress\n", __func__); - return -EBUSY; - } - - if (b->type != q->type) { - dprintk(1, "%s(): invalid buffer type\n", __func__); - return -EINVAL; - } - - if (b->index >= q->num_buffers) { - dprintk(1, "%s(): buffer index out of range\n", __func__); - return -EINVAL; - } - - vb = q->bufs[b->index]; - if (NULL == vb) { - /* Should never happen */ - dprintk(1, "%s(): buffer is NULL\n", __func__); - return -EINVAL; - } - - if (b->memory != q->memory) { - dprintk(1, "%s(): invalid memory type\n", __func__); - return -EINVAL; - } - - if (vb->state != VB2_BUF_STATE_DEQUEUED) { - dprintk(1, "%s(): invalid buffer state %d\n", __func__, vb->state); - return -EINVAL; - } - - ret = __buf_prepare(vb, b); - if (ret < 0) - return ret; - - __fill_v4l2_buffer(vb, b); - - return 0; -} -EXPORT_SYMBOL_GPL(vb2_prepare_buf); - -/** - * vb2_qbuf() - Queue a buffer from userspace - * @q: videobuf2 queue - * @b: buffer structure passed from userspace to vidioc_qbuf handler - * in driver - * - * Should be called from vidioc_qbuf ioctl handler of a driver. - * This function: - * 1) verifies the passed buffer, - * 2) if necessary, calls buf_prepare callback in the driver (if provided), in - * which driver-specific buffer initialization can be performed, - * 3) if streaming is on, queues the buffer in driver by the means of buf_queue - * callback for processing. - * - * The return values from this function are intended to be directly returned - * from vidioc_qbuf handler in driver. - */ -int vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b) -{ - struct rw_semaphore *mmap_sem = NULL; - struct vb2_buffer *vb; - int ret = 0; - - /* - * In case of user pointer buffers vb2 allocator needs to get direct - * access to userspace pages. This requires getting read access on - * mmap semaphore in the current process structure. The same - * semaphore is taken before calling mmap operation, while both mmap - * and qbuf are called by the driver or v4l2 core with driver's lock - * held. To avoid a AB-BA deadlock (mmap_sem then driver's lock in - * mmap and driver's lock then mmap_sem in qbuf) the videobuf2 core - * release driver's lock, takes mmap_sem and then takes again driver's - * lock. - * - * To avoid race with other vb2 calls, which might be called after - * releasing driver's lock, this operation is performed at the - * beggining of qbuf processing. This way the queue status is - * consistent after getting driver's lock back. - */ - if (q->memory == V4L2_MEMORY_USERPTR) { - mmap_sem = ¤t->mm->mmap_sem; - call_qop(q, wait_prepare, q); - down_read(mmap_sem); - call_qop(q, wait_finish, q); - } - - if (q->fileio) { - dprintk(1, "qbuf: file io in progress\n"); - ret = -EBUSY; - goto unlock; - } - - if (b->type != q->type) { - dprintk(1, "qbuf: invalid buffer type\n"); - ret = -EINVAL; - goto unlock; - } - - if (b->index >= q->num_buffers) { - dprintk(1, "qbuf: buffer index out of range\n"); - ret = -EINVAL; - goto unlock; - } - - vb = q->bufs[b->index]; - if (NULL == vb) { - /* Should never happen */ - dprintk(1, "qbuf: buffer is NULL\n"); - ret = -EINVAL; - goto unlock; - } - - if (b->memory != q->memory) { - dprintk(1, "qbuf: invalid memory type\n"); - ret = -EINVAL; - goto unlock; - } - - switch (vb->state) { - case VB2_BUF_STATE_DEQUEUED: - ret = __buf_prepare(vb, b); - if (ret) - goto unlock; - case VB2_BUF_STATE_PREPARED: - break; - default: - dprintk(1, "qbuf: buffer already in use\n"); - ret = -EINVAL; - goto unlock; - } - - /* - * Add to the queued buffers list, a buffer will stay on it until - * dequeued in dqbuf. - */ - list_add_tail(&vb->queued_entry, &q->queued_list); - vb->state = VB2_BUF_STATE_QUEUED; - - /* - * If already streaming, give the buffer to driver for processing. - * If not, the buffer will be given to driver on next streamon. - */ - if (q->streaming) - __enqueue_in_driver(vb); - - /* Fill buffer information for the userspace */ - __fill_v4l2_buffer(vb, b); - - dprintk(1, "qbuf of buffer %d succeeded\n", vb->v4l2_buf.index); -unlock: - if (mmap_sem) - up_read(mmap_sem); - return ret; -} -EXPORT_SYMBOL_GPL(vb2_qbuf); - -/** - * __vb2_wait_for_done_vb() - wait for a buffer to become available - * for dequeuing - * - * Will sleep if required for nonblocking == false. - */ -static int __vb2_wait_for_done_vb(struct vb2_queue *q, int nonblocking) -{ - /* - * All operations on vb_done_list are performed under done_lock - * spinlock protection. However, buffers may be removed from - * it and returned to userspace only while holding both driver's - * lock and the done_lock spinlock. Thus we can be sure that as - * long as we hold the driver's lock, the list will remain not - * empty if list_empty() check succeeds. - */ - - for (;;) { - int ret; - - if (!q->streaming) { - dprintk(1, "Streaming off, will not wait for buffers\n"); - return -EINVAL; - } - - if (!list_empty(&q->done_list)) { - /* - * Found a buffer that we were waiting for. - */ - break; - } - - if (nonblocking) { - dprintk(1, "Nonblocking and no buffers to dequeue, " - "will not wait\n"); - return -EAGAIN; - } - - /* - * We are streaming and blocking, wait for another buffer to - * become ready or for streamoff. Driver's lock is released to - * allow streamoff or qbuf to be called while waiting. - */ - call_qop(q, wait_prepare, q); - - /* - * All locks have been released, it is safe to sleep now. - */ - dprintk(3, "Will sleep waiting for buffers\n"); - ret = wait_event_interruptible(q->done_wq, - !list_empty(&q->done_list) || !q->streaming); - - /* - * We need to reevaluate both conditions again after reacquiring - * the locks or return an error if one occurred. - */ - call_qop(q, wait_finish, q); - if (ret) - return ret; - } - return 0; -} - -/** - * __vb2_get_done_vb() - get a buffer ready for dequeuing - * - * Will sleep if required for nonblocking == false. - */ -static int __vb2_get_done_vb(struct vb2_queue *q, struct vb2_buffer **vb, - int nonblocking) -{ - unsigned long flags; - int ret; - - /* - * Wait for at least one buffer to become available on the done_list. - */ - ret = __vb2_wait_for_done_vb(q, nonblocking); - if (ret) - return ret; - - /* - * Driver's lock has been held since we last verified that done_list - * is not empty, so no need for another list_empty(done_list) check. - */ - spin_lock_irqsave(&q->done_lock, flags); - *vb = list_first_entry(&q->done_list, struct vb2_buffer, done_entry); - list_del(&(*vb)->done_entry); - spin_unlock_irqrestore(&q->done_lock, flags); - - return 0; -} - -/** - * vb2_wait_for_all_buffers() - wait until all buffers are given back to vb2 - * @q: videobuf2 queue - * - * This function will wait until all buffers that have been given to the driver - * by buf_queue() are given back to vb2 with vb2_buffer_done(). It doesn't call - * wait_prepare, wait_finish pair. It is intended to be called with all locks - * taken, for example from stop_streaming() callback. - */ -int vb2_wait_for_all_buffers(struct vb2_queue *q) -{ - if (!q->streaming) { - dprintk(1, "Streaming off, will not wait for buffers\n"); - return -EINVAL; - } - - wait_event(q->done_wq, !atomic_read(&q->queued_count)); - return 0; -} -EXPORT_SYMBOL_GPL(vb2_wait_for_all_buffers); - -/** - * vb2_dqbuf() - Dequeue a buffer to the userspace - * @q: videobuf2 queue - * @b: buffer structure passed from userspace to vidioc_dqbuf handler - * in driver - * @nonblocking: if true, this call will not sleep waiting for a buffer if no - * buffers ready for dequeuing are present. Normally the driver - * would be passing (file->f_flags & O_NONBLOCK) here - * - * Should be called from vidioc_dqbuf ioctl handler of a driver. - * This function: - * 1) verifies the passed buffer, - * 2) calls buf_finish callback in the driver (if provided), in which - * driver can perform any additional operations that may be required before - * returning the buffer to userspace, such as cache sync, - * 3) the buffer struct members are filled with relevant information for - * the userspace. - * - * The return values from this function are intended to be directly returned - * from vidioc_dqbuf handler in driver. - */ -int vb2_dqbuf(struct vb2_queue *q, struct v4l2_buffer *b, bool nonblocking) -{ - struct vb2_buffer *vb = NULL; - int ret; - - if (q->fileio) { - dprintk(1, "dqbuf: file io in progress\n"); - return -EBUSY; - } - - if (b->type != q->type) { - dprintk(1, "dqbuf: invalid buffer type\n"); - return -EINVAL; - } - - ret = __vb2_get_done_vb(q, &vb, nonblocking); - if (ret < 0) { - dprintk(1, "dqbuf: error getting next done buffer\n"); - return ret; - } - - ret = call_qop(q, buf_finish, vb); - if (ret) { - dprintk(1, "dqbuf: buffer finish failed\n"); - return ret; - } - - switch (vb->state) { - case VB2_BUF_STATE_DONE: - dprintk(3, "dqbuf: Returning done buffer\n"); - break; - case VB2_BUF_STATE_ERROR: - dprintk(3, "dqbuf: Returning done buffer with errors\n"); - break; - default: - dprintk(1, "dqbuf: Invalid buffer state\n"); - return -EINVAL; - } - - /* Fill buffer information for the userspace */ - __fill_v4l2_buffer(vb, b); - /* Remove from videobuf queue */ - list_del(&vb->queued_entry); - - dprintk(1, "dqbuf of buffer %d, with state %d\n", - vb->v4l2_buf.index, vb->state); - - vb->state = VB2_BUF_STATE_DEQUEUED; - return 0; -} -EXPORT_SYMBOL_GPL(vb2_dqbuf); - -/** - * __vb2_queue_cancel() - cancel and stop (pause) streaming - * - * Removes all queued buffers from driver's queue and all buffers queued by - * userspace from videobuf's queue. Returns to state after reqbufs. - */ -static void __vb2_queue_cancel(struct vb2_queue *q) -{ - unsigned int i; - - /* - * Tell driver to stop all transactions and release all queued - * buffers. - */ - if (q->streaming) - call_qop(q, stop_streaming, q); - q->streaming = 0; - - /* - * Remove all buffers from videobuf's list... - */ - INIT_LIST_HEAD(&q->queued_list); - /* - * ...and done list; userspace will not receive any buffers it - * has not already dequeued before initiating cancel. - */ - INIT_LIST_HEAD(&q->done_list); - atomic_set(&q->queued_count, 0); - wake_up_all(&q->done_wq); - - /* - * Reinitialize all buffers for next use. - */ - for (i = 0; i < q->num_buffers; ++i) - q->bufs[i]->state = VB2_BUF_STATE_DEQUEUED; -} - -/** - * vb2_streamon - start streaming - * @q: videobuf2 queue - * @type: type argument passed from userspace to vidioc_streamon handler - * - * Should be called from vidioc_streamon handler of a driver. - * This function: - * 1) verifies current state - * 2) passes any previously queued buffers to the driver and starts streaming - * - * The return values from this function are intended to be directly returned - * from vidioc_streamon handler in the driver. - */ -int vb2_streamon(struct vb2_queue *q, enum v4l2_buf_type type) -{ - struct vb2_buffer *vb; - int ret; - - if (q->fileio) { - dprintk(1, "streamon: file io in progress\n"); - return -EBUSY; - } - - if (type != q->type) { - dprintk(1, "streamon: invalid stream type\n"); - return -EINVAL; - } - - if (q->streaming) { - dprintk(1, "streamon: already streaming\n"); - return -EBUSY; - } - - /* - * If any buffers were queued before streamon, - * we can now pass them to driver for processing. - */ - list_for_each_entry(vb, &q->queued_list, queued_entry) - __enqueue_in_driver(vb); - - /* - * Let driver notice that streaming state has been enabled. - */ - ret = call_qop(q, start_streaming, q, atomic_read(&q->queued_count)); - if (ret) { - dprintk(1, "streamon: driver refused to start streaming\n"); - __vb2_queue_cancel(q); - return ret; - } - - q->streaming = 1; - - dprintk(3, "Streamon successful\n"); - return 0; -} -EXPORT_SYMBOL_GPL(vb2_streamon); - - -/** - * vb2_streamoff - stop streaming - * @q: videobuf2 queue - * @type: type argument passed from userspace to vidioc_streamoff handler - * - * Should be called from vidioc_streamoff handler of a driver. - * This function: - * 1) verifies current state, - * 2) stop streaming and dequeues any queued buffers, including those previously - * passed to the driver (after waiting for the driver to finish). - * - * This call can be used for pausing playback. - * The return values from this function are intended to be directly returned - * from vidioc_streamoff handler in the driver - */ -int vb2_streamoff(struct vb2_queue *q, enum v4l2_buf_type type) -{ - if (q->fileio) { - dprintk(1, "streamoff: file io in progress\n"); - return -EBUSY; - } - - if (type != q->type) { - dprintk(1, "streamoff: invalid stream type\n"); - return -EINVAL; - } - - if (!q->streaming) { - dprintk(1, "streamoff: not streaming\n"); - return -EINVAL; - } - - /* - * Cancel will pause streaming and remove all buffers from the driver - * and videobuf, effectively returning control over them to userspace. - */ - __vb2_queue_cancel(q); - - dprintk(3, "Streamoff successful\n"); - return 0; -} -EXPORT_SYMBOL_GPL(vb2_streamoff); - -/** - * __find_plane_by_offset() - find plane associated with the given offset off - */ -static int __find_plane_by_offset(struct vb2_queue *q, unsigned long off, - unsigned int *_buffer, unsigned int *_plane) -{ - struct vb2_buffer *vb; - unsigned int buffer, plane; - - /* - * Go over all buffers and their planes, comparing the given offset - * with an offset assigned to each plane. If a match is found, - * return its buffer and plane numbers. - */ - for (buffer = 0; buffer < q->num_buffers; ++buffer) { - vb = q->bufs[buffer]; - - for (plane = 0; plane < vb->num_planes; ++plane) { - if (vb->v4l2_planes[plane].m.mem_offset == off) { - *_buffer = buffer; - *_plane = plane; - return 0; - } - } - } - - return -EINVAL; -} - -/** - * vb2_mmap() - map video buffers into application address space - * @q: videobuf2 queue - * @vma: vma passed to the mmap file operation handler in the driver - * - * Should be called from mmap file operation handler of a driver. - * This function maps one plane of one of the available video buffers to - * userspace. To map whole video memory allocated on reqbufs, this function - * has to be called once per each plane per each buffer previously allocated. - * - * When the userspace application calls mmap, it passes to it an offset returned - * to it earlier by the means of vidioc_querybuf handler. That offset acts as - * a "cookie", which is then used to identify the plane to be mapped. - * This function finds a plane with a matching offset and a mapping is performed - * by the means of a provided memory operation. - * - * The return values from this function are intended to be directly returned - * from the mmap handler in driver. - */ -int vb2_mmap(struct vb2_queue *q, struct vm_area_struct *vma) -{ - unsigned long off = vma->vm_pgoff << PAGE_SHIFT; - struct vb2_buffer *vb; - unsigned int buffer, plane; - int ret; - - if (q->memory != V4L2_MEMORY_MMAP) { - dprintk(1, "Queue is not currently set up for mmap\n"); - return -EINVAL; - } - - /* - * Check memory area access mode. - */ - if (!(vma->vm_flags & VM_SHARED)) { - dprintk(1, "Invalid vma flags, VM_SHARED needed\n"); - return -EINVAL; - } - if (V4L2_TYPE_IS_OUTPUT(q->type)) { - if (!(vma->vm_flags & VM_WRITE)) { - dprintk(1, "Invalid vma flags, VM_WRITE needed\n"); - return -EINVAL; - } - } else { - if (!(vma->vm_flags & VM_READ)) { - dprintk(1, "Invalid vma flags, VM_READ needed\n"); - return -EINVAL; - } - } - - /* - * Find the plane corresponding to the offset passed by userspace. - */ - ret = __find_plane_by_offset(q, off, &buffer, &plane); - if (ret) - return ret; - - vb = q->bufs[buffer]; - - ret = call_memop(q, mmap, vb->planes[plane].mem_priv, vma); - if (ret) - return ret; - - dprintk(3, "Buffer %d, plane %d successfully mapped\n", buffer, plane); - return 0; -} -EXPORT_SYMBOL_GPL(vb2_mmap); - -#ifndef CONFIG_MMU -unsigned long vb2_get_unmapped_area(struct vb2_queue *q, - unsigned long addr, - unsigned long len, - unsigned long pgoff, - unsigned long flags) -{ - unsigned long off = pgoff << PAGE_SHIFT; - struct vb2_buffer *vb; - unsigned int buffer, plane; - int ret; - - if (q->memory != V4L2_MEMORY_MMAP) { - dprintk(1, "Queue is not currently set up for mmap\n"); - return -EINVAL; - } - - /* - * Find the plane corresponding to the offset passed by userspace. - */ - ret = __find_plane_by_offset(q, off, &buffer, &plane); - if (ret) - return ret; - - vb = q->bufs[buffer]; - - return (unsigned long)vb2_plane_vaddr(vb, plane); -} -EXPORT_SYMBOL_GPL(vb2_get_unmapped_area); -#endif - -static int __vb2_init_fileio(struct vb2_queue *q, int read); -static int __vb2_cleanup_fileio(struct vb2_queue *q); - -/** - * vb2_poll() - implements poll userspace operation - * @q: videobuf2 queue - * @file: file argument passed to the poll file operation handler - * @wait: wait argument passed to the poll file operation handler - * - * This function implements poll file operation handler for a driver. - * For CAPTURE queues, if a buffer is ready to be dequeued, the userspace will - * be informed that the file descriptor of a video device is available for - * reading. - * For OUTPUT queues, if a buffer is ready to be dequeued, the file descriptor - * will be reported as available for writing. - * - * If the driver uses struct v4l2_fh, then vb2_poll() will also check for any - * pending events. - * - * The return values from this function are intended to be directly returned - * from poll handler in driver. - */ -unsigned int vb2_poll(struct vb2_queue *q, struct file *file, poll_table *wait) -{ - struct video_device *vfd = video_devdata(file); - unsigned long req_events = poll_requested_events(wait); - struct vb2_buffer *vb = NULL; - unsigned int res = 0; - unsigned long flags; - - if (test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags)) { - struct v4l2_fh *fh = file->private_data; - - if (v4l2_event_pending(fh)) - res = POLLPRI; - else if (req_events & POLLPRI) - poll_wait(file, &fh->wait, wait); - } - - /* - * Start file I/O emulator only if streaming API has not been used yet. - */ - if (q->num_buffers == 0 && q->fileio == NULL) { - if (!V4L2_TYPE_IS_OUTPUT(q->type) && (q->io_modes & VB2_READ) && - (req_events & (POLLIN | POLLRDNORM))) { - if (__vb2_init_fileio(q, 1)) - return res | POLLERR; - } - if (V4L2_TYPE_IS_OUTPUT(q->type) && (q->io_modes & VB2_WRITE) && - (req_events & (POLLOUT | POLLWRNORM))) { - if (__vb2_init_fileio(q, 0)) - return res | POLLERR; - /* - * Write to OUTPUT queue can be done immediately. - */ - return res | POLLOUT | POLLWRNORM; - } - } - - /* - * There is nothing to wait for if no buffers have already been queued. - */ - if (list_empty(&q->queued_list)) - return res | POLLERR; - - poll_wait(file, &q->done_wq, wait); - - /* - * Take first buffer available for dequeuing. - */ - spin_lock_irqsave(&q->done_lock, flags); - if (!list_empty(&q->done_list)) - vb = list_first_entry(&q->done_list, struct vb2_buffer, - done_entry); - spin_unlock_irqrestore(&q->done_lock, flags); - - if (vb && (vb->state == VB2_BUF_STATE_DONE - || vb->state == VB2_BUF_STATE_ERROR)) { - return (V4L2_TYPE_IS_OUTPUT(q->type)) ? - res | POLLOUT | POLLWRNORM : - res | POLLIN | POLLRDNORM; - } - return res; -} -EXPORT_SYMBOL_GPL(vb2_poll); - -/** - * vb2_queue_init() - initialize a videobuf2 queue - * @q: videobuf2 queue; this structure should be allocated in driver - * - * The vb2_queue structure should be allocated by the driver. The driver is - * responsible of clearing it's content and setting initial values for some - * required entries before calling this function. - * q->ops, q->mem_ops, q->type and q->io_modes are mandatory. Please refer - * to the struct vb2_queue description in include/media/videobuf2-core.h - * for more information. - */ -int vb2_queue_init(struct vb2_queue *q) -{ - BUG_ON(!q); - BUG_ON(!q->ops); - BUG_ON(!q->mem_ops); - BUG_ON(!q->type); - BUG_ON(!q->io_modes); - - BUG_ON(!q->ops->queue_setup); - BUG_ON(!q->ops->buf_queue); - - INIT_LIST_HEAD(&q->queued_list); - INIT_LIST_HEAD(&q->done_list); - spin_lock_init(&q->done_lock); - init_waitqueue_head(&q->done_wq); - - if (q->buf_struct_size == 0) - q->buf_struct_size = sizeof(struct vb2_buffer); - - return 0; -} -EXPORT_SYMBOL_GPL(vb2_queue_init); - -/** - * vb2_queue_release() - stop streaming, release the queue and free memory - * @q: videobuf2 queue - * - * This function stops streaming and performs necessary clean ups, including - * freeing video buffer memory. The driver is responsible for freeing - * the vb2_queue structure itself. - */ -void vb2_queue_release(struct vb2_queue *q) -{ - __vb2_cleanup_fileio(q); - __vb2_queue_cancel(q); - __vb2_queue_free(q, q->num_buffers); -} -EXPORT_SYMBOL_GPL(vb2_queue_release); - -/** - * struct vb2_fileio_buf - buffer context used by file io emulator - * - * vb2 provides a compatibility layer and emulator of file io (read and - * write) calls on top of streaming API. This structure is used for - * tracking context related to the buffers. - */ -struct vb2_fileio_buf { - void *vaddr; - unsigned int size; - unsigned int pos; - unsigned int queued:1; -}; - -/** - * struct vb2_fileio_data - queue context used by file io emulator - * - * vb2 provides a compatibility layer and emulator of file io (read and - * write) calls on top of streaming API. For proper operation it required - * this structure to save the driver state between each call of the read - * or write function. - */ -struct vb2_fileio_data { - struct v4l2_requestbuffers req; - struct v4l2_buffer b; - struct vb2_fileio_buf bufs[VIDEO_MAX_FRAME]; - unsigned int index; - unsigned int q_count; - unsigned int dq_count; - unsigned int flags; -}; - -/** - * __vb2_init_fileio() - initialize file io emulator - * @q: videobuf2 queue - * @read: mode selector (1 means read, 0 means write) - */ -static int __vb2_init_fileio(struct vb2_queue *q, int read) -{ - struct vb2_fileio_data *fileio; - int i, ret; - unsigned int count = 0; - - /* - * Sanity check - */ - if ((read && !(q->io_modes & VB2_READ)) || - (!read && !(q->io_modes & VB2_WRITE))) - BUG(); - - /* - * Check if device supports mapping buffers to kernel virtual space. - */ - if (!q->mem_ops->vaddr) - return -EBUSY; - - /* - * Check if streaming api has not been already activated. - */ - if (q->streaming || q->num_buffers > 0) - return -EBUSY; - - /* - * Start with count 1, driver can increase it in queue_setup() - */ - count = 1; - - dprintk(3, "setting up file io: mode %s, count %d, flags %08x\n", - (read) ? "read" : "write", count, q->io_flags); - - fileio = kzalloc(sizeof(struct vb2_fileio_data), GFP_KERNEL); - if (fileio == NULL) - return -ENOMEM; - - fileio->flags = q->io_flags; - - /* - * Request buffers and use MMAP type to force driver - * to allocate buffers by itself. - */ - fileio->req.count = count; - fileio->req.memory = V4L2_MEMORY_MMAP; - fileio->req.type = q->type; - ret = vb2_reqbufs(q, &fileio->req); - if (ret) - goto err_kfree; - - /* - * Check if plane_count is correct - * (multiplane buffers are not supported). - */ - if (q->bufs[0]->num_planes != 1) { - ret = -EBUSY; - goto err_reqbufs; - } - - /* - * Get kernel address of each buffer. - */ - for (i = 0; i < q->num_buffers; i++) { - fileio->bufs[i].vaddr = vb2_plane_vaddr(q->bufs[i], 0); - if (fileio->bufs[i].vaddr == NULL) - goto err_reqbufs; - fileio->bufs[i].size = vb2_plane_size(q->bufs[i], 0); - } - - /* - * Read mode requires pre queuing of all buffers. - */ - if (read) { - /* - * Queue all buffers. - */ - for (i = 0; i < q->num_buffers; i++) { - struct v4l2_buffer *b = &fileio->b; - memset(b, 0, sizeof(*b)); - b->type = q->type; - b->memory = q->memory; - b->index = i; - ret = vb2_qbuf(q, b); - if (ret) - goto err_reqbufs; - fileio->bufs[i].queued = 1; - } - - /* - * Start streaming. - */ - ret = vb2_streamon(q, q->type); - if (ret) - goto err_reqbufs; - } - - q->fileio = fileio; - - return ret; - -err_reqbufs: - fileio->req.count = 0; - vb2_reqbufs(q, &fileio->req); - -err_kfree: - kfree(fileio); - return ret; -} - -/** - * __vb2_cleanup_fileio() - free resourced used by file io emulator - * @q: videobuf2 queue - */ -static int __vb2_cleanup_fileio(struct vb2_queue *q) -{ - struct vb2_fileio_data *fileio = q->fileio; - - if (fileio) { - /* - * Hack fileio context to enable direct calls to vb2 ioctl - * interface. - */ - q->fileio = NULL; - - vb2_streamoff(q, q->type); - fileio->req.count = 0; - vb2_reqbufs(q, &fileio->req); - kfree(fileio); - dprintk(3, "file io emulator closed\n"); - } - return 0; -} - -/** - * __vb2_perform_fileio() - perform a single file io (read or write) operation - * @q: videobuf2 queue - * @data: pointed to target userspace buffer - * @count: number of bytes to read or write - * @ppos: file handle position tracking pointer - * @nonblock: mode selector (1 means blocking calls, 0 means nonblocking) - * @read: access mode selector (1 means read, 0 means write) - */ -static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_t count, - loff_t *ppos, int nonblock, int read) -{ - struct vb2_fileio_data *fileio; - struct vb2_fileio_buf *buf; - int ret, index; - - dprintk(3, "file io: mode %s, offset %ld, count %zd, %sblocking\n", - read ? "read" : "write", (long)*ppos, count, - nonblock ? "non" : ""); - - if (!data) - return -EINVAL; - - /* - * Initialize emulator on first call. - */ - if (!q->fileio) { - ret = __vb2_init_fileio(q, read); - dprintk(3, "file io: vb2_init_fileio result: %d\n", ret); - if (ret) - return ret; - } - fileio = q->fileio; - - /* - * Hack fileio context to enable direct calls to vb2 ioctl interface. - * The pointer will be restored before returning from this function. - */ - q->fileio = NULL; - - index = fileio->index; - buf = &fileio->bufs[index]; - - /* - * Check if we need to dequeue the buffer. - */ - if (buf->queued) { - struct vb2_buffer *vb; - - /* - * Call vb2_dqbuf to get buffer back. - */ - memset(&fileio->b, 0, sizeof(fileio->b)); - fileio->b.type = q->type; - fileio->b.memory = q->memory; - fileio->b.index = index; - ret = vb2_dqbuf(q, &fileio->b, nonblock); - dprintk(5, "file io: vb2_dqbuf result: %d\n", ret); - if (ret) - goto end; - fileio->dq_count += 1; - - /* - * Get number of bytes filled by the driver - */ - vb = q->bufs[index]; - buf->size = vb2_get_plane_payload(vb, 0); - buf->queued = 0; - } - - /* - * Limit count on last few bytes of the buffer. - */ - if (buf->pos + count > buf->size) { - count = buf->size - buf->pos; - dprintk(5, "reducing read count: %zd\n", count); - } - - /* - * Transfer data to userspace. - */ - dprintk(3, "file io: copying %zd bytes - buffer %d, offset %u\n", - count, index, buf->pos); - if (read) - ret = copy_to_user(data, buf->vaddr + buf->pos, count); - else - ret = copy_from_user(buf->vaddr + buf->pos, data, count); - if (ret) { - dprintk(3, "file io: error copying data\n"); - ret = -EFAULT; - goto end; - } - - /* - * Update counters. - */ - buf->pos += count; - *ppos += count; - - /* - * Queue next buffer if required. - */ - if (buf->pos == buf->size || - (!read && (fileio->flags & VB2_FILEIO_WRITE_IMMEDIATELY))) { - /* - * Check if this is the last buffer to read. - */ - if (read && (fileio->flags & VB2_FILEIO_READ_ONCE) && - fileio->dq_count == 1) { - dprintk(3, "file io: read limit reached\n"); - /* - * Restore fileio pointer and release the context. - */ - q->fileio = fileio; - return __vb2_cleanup_fileio(q); - } - - /* - * Call vb2_qbuf and give buffer to the driver. - */ - memset(&fileio->b, 0, sizeof(fileio->b)); - fileio->b.type = q->type; - fileio->b.memory = q->memory; - fileio->b.index = index; - fileio->b.bytesused = buf->pos; - ret = vb2_qbuf(q, &fileio->b); - dprintk(5, "file io: vb2_dbuf result: %d\n", ret); - if (ret) - goto end; - - /* - * Buffer has been queued, update the status - */ - buf->pos = 0; - buf->queued = 1; - buf->size = q->bufs[0]->v4l2_planes[0].length; - fileio->q_count += 1; - - /* - * Switch to the next buffer - */ - fileio->index = (index + 1) % q->num_buffers; - - /* - * Start streaming if required. - */ - if (!read && !q->streaming) { - ret = vb2_streamon(q, q->type); - if (ret) - goto end; - } - } - - /* - * Return proper number of bytes processed. - */ - if (ret == 0) - ret = count; -end: - /* - * Restore the fileio context and block vb2 ioctl interface. - */ - q->fileio = fileio; - return ret; -} - -size_t vb2_read(struct vb2_queue *q, char __user *data, size_t count, - loff_t *ppos, int nonblocking) -{ - return __vb2_perform_fileio(q, data, count, ppos, nonblocking, 1); -} -EXPORT_SYMBOL_GPL(vb2_read); - -size_t vb2_write(struct vb2_queue *q, char __user *data, size_t count, - loff_t *ppos, int nonblocking) -{ - return __vb2_perform_fileio(q, data, count, ppos, nonblocking, 0); -} -EXPORT_SYMBOL_GPL(vb2_write); - - -/* - * The following functions are not part of the vb2 core API, but are helper - * functions that plug into struct v4l2_ioctl_ops, struct v4l2_file_operations - * and struct vb2_ops. - * They contain boilerplate code that most if not all drivers have to do - * and so they simplify the driver code. - */ - -/* The queue is busy if there is a owner and you are not that owner. */ -static inline bool vb2_queue_is_busy(struct video_device *vdev, struct file *file) -{ - return vdev->queue->owner && vdev->queue->owner != file->private_data; -} - -/* vb2 ioctl helpers */ - -int vb2_ioctl_reqbufs(struct file *file, void *priv, - struct v4l2_requestbuffers *p) -{ - struct video_device *vdev = video_devdata(file); - int res = __verify_memory_type(vdev->queue, p->memory, p->type); - - if (res) - return res; - if (vb2_queue_is_busy(vdev, file)) - return -EBUSY; - res = __reqbufs(vdev->queue, p); - /* If count == 0, then the owner has released all buffers and he - is no longer owner of the queue. Otherwise we have a new owner. */ - if (res == 0) - vdev->queue->owner = p->count ? file->private_data : NULL; - return res; -} -EXPORT_SYMBOL_GPL(vb2_ioctl_reqbufs); - -int vb2_ioctl_create_bufs(struct file *file, void *priv, - struct v4l2_create_buffers *p) -{ - struct video_device *vdev = video_devdata(file); - int res = __verify_memory_type(vdev->queue, p->memory, p->format.type); - - p->index = vdev->queue->num_buffers; - /* If count == 0, then just check if memory and type are valid. - Any -EBUSY result from __verify_memory_type can be mapped to 0. */ - if (p->count == 0) - return res != -EBUSY ? res : 0; - if (res) - return res; - if (vb2_queue_is_busy(vdev, file)) - return -EBUSY; - res = __create_bufs(vdev->queue, p); - if (res == 0) - vdev->queue->owner = file->private_data; - return res; -} -EXPORT_SYMBOL_GPL(vb2_ioctl_create_bufs); - -int vb2_ioctl_prepare_buf(struct file *file, void *priv, - struct v4l2_buffer *p) -{ - struct video_device *vdev = video_devdata(file); - - if (vb2_queue_is_busy(vdev, file)) - return -EBUSY; - return vb2_prepare_buf(vdev->queue, p); -} -EXPORT_SYMBOL_GPL(vb2_ioctl_prepare_buf); - -int vb2_ioctl_querybuf(struct file *file, void *priv, struct v4l2_buffer *p) -{ - struct video_device *vdev = video_devdata(file); - - /* No need to call vb2_queue_is_busy(), anyone can query buffers. */ - return vb2_querybuf(vdev->queue, p); -} -EXPORT_SYMBOL_GPL(vb2_ioctl_querybuf); - -int vb2_ioctl_qbuf(struct file *file, void *priv, struct v4l2_buffer *p) -{ - struct video_device *vdev = video_devdata(file); - - if (vb2_queue_is_busy(vdev, file)) - return -EBUSY; - return vb2_qbuf(vdev->queue, p); -} -EXPORT_SYMBOL_GPL(vb2_ioctl_qbuf); - -int vb2_ioctl_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p) -{ - struct video_device *vdev = video_devdata(file); - - if (vb2_queue_is_busy(vdev, file)) - return -EBUSY; - return vb2_dqbuf(vdev->queue, p, file->f_flags & O_NONBLOCK); -} -EXPORT_SYMBOL_GPL(vb2_ioctl_dqbuf); - -int vb2_ioctl_streamon(struct file *file, void *priv, enum v4l2_buf_type i) -{ - struct video_device *vdev = video_devdata(file); - - if (vb2_queue_is_busy(vdev, file)) - return -EBUSY; - return vb2_streamon(vdev->queue, i); -} -EXPORT_SYMBOL_GPL(vb2_ioctl_streamon); - -int vb2_ioctl_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) -{ - struct video_device *vdev = video_devdata(file); - - if (vb2_queue_is_busy(vdev, file)) - return -EBUSY; - return vb2_streamoff(vdev->queue, i); -} -EXPORT_SYMBOL_GPL(vb2_ioctl_streamoff); - -/* v4l2_file_operations helpers */ - -int vb2_fop_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct video_device *vdev = video_devdata(file); - - return vb2_mmap(vdev->queue, vma); -} -EXPORT_SYMBOL_GPL(vb2_fop_mmap); - -int vb2_fop_release(struct file *file) -{ - struct video_device *vdev = video_devdata(file); - - if (file->private_data == vdev->queue->owner) { - vb2_queue_release(vdev->queue); - vdev->queue->owner = NULL; - } - return v4l2_fh_release(file); -} -EXPORT_SYMBOL_GPL(vb2_fop_release); - -ssize_t vb2_fop_write(struct file *file, char __user *buf, - size_t count, loff_t *ppos) -{ - struct video_device *vdev = video_devdata(file); - struct mutex *lock = vdev->queue->lock ? vdev->queue->lock : vdev->lock; - int err = -EBUSY; - - if (lock && mutex_lock_interruptible(lock)) - return -ERESTARTSYS; - if (vb2_queue_is_busy(vdev, file)) - goto exit; - err = vb2_write(vdev->queue, buf, count, ppos, - file->f_flags & O_NONBLOCK); - if (err >= 0) - vdev->queue->owner = file->private_data; -exit: - if (lock) - mutex_unlock(lock); - return err; -} -EXPORT_SYMBOL_GPL(vb2_fop_write); - -ssize_t vb2_fop_read(struct file *file, char __user *buf, - size_t count, loff_t *ppos) -{ - struct video_device *vdev = video_devdata(file); - struct mutex *lock = vdev->queue->lock ? vdev->queue->lock : vdev->lock; - int err = -EBUSY; - - if (lock && mutex_lock_interruptible(lock)) - return -ERESTARTSYS; - if (vb2_queue_is_busy(vdev, file)) - goto exit; - err = vb2_read(vdev->queue, buf, count, ppos, - file->f_flags & O_NONBLOCK); - if (err >= 0) - vdev->queue->owner = file->private_data; -exit: - if (lock) - mutex_unlock(lock); - return err; -} -EXPORT_SYMBOL_GPL(vb2_fop_read); - -unsigned int vb2_fop_poll(struct file *file, poll_table *wait) -{ - struct video_device *vdev = video_devdata(file); - struct vb2_queue *q = vdev->queue; - struct mutex *lock = q->lock ? q->lock : vdev->lock; - unsigned long req_events = poll_requested_events(wait); - unsigned res; - void *fileio; - bool must_lock = false; - - /* Try to be smart: only lock if polling might start fileio, - otherwise locking will only introduce unwanted delays. */ - if (q->num_buffers == 0 && q->fileio == NULL) { - if (!V4L2_TYPE_IS_OUTPUT(q->type) && (q->io_modes & VB2_READ) && - (req_events & (POLLIN | POLLRDNORM))) - must_lock = true; - else if (V4L2_TYPE_IS_OUTPUT(q->type) && (q->io_modes & VB2_WRITE) && - (req_events & (POLLOUT | POLLWRNORM))) - must_lock = true; - } - - /* If locking is needed, but this helper doesn't know how, then you - shouldn't be using this helper but you should write your own. */ - WARN_ON(must_lock && !lock); - - if (must_lock && lock && mutex_lock_interruptible(lock)) - return POLLERR; - - fileio = q->fileio; - - res = vb2_poll(vdev->queue, file, wait); - - /* If fileio was started, then we have a new queue owner. */ - if (must_lock && !fileio && q->fileio) - q->owner = file->private_data; - if (must_lock && lock) - mutex_unlock(lock); - return res; -} -EXPORT_SYMBOL_GPL(vb2_fop_poll); - -#ifndef CONFIG_MMU -unsigned long vb2_fop_get_unmapped_area(struct file *file, unsigned long addr, - unsigned long len, unsigned long pgoff, unsigned long flags) -{ - struct video_device *vdev = video_devdata(file); - - return vb2_get_unmapped_area(vdev->queue, addr, len, pgoff, flags); -} -EXPORT_SYMBOL_GPL(vb2_fop_get_unmapped_area); -#endif - -/* vb2_ops helpers. Only use if vq->lock is non-NULL. */ - -void vb2_ops_wait_prepare(struct vb2_queue *vq) -{ - mutex_unlock(vq->lock); -} -EXPORT_SYMBOL_GPL(vb2_ops_wait_prepare); - -void vb2_ops_wait_finish(struct vb2_queue *vq) -{ - mutex_lock(vq->lock); -} -EXPORT_SYMBOL_GPL(vb2_ops_wait_finish); - -MODULE_DESCRIPTION("Driver helper framework for Video for Linux 2"); -MODULE_AUTHOR("Pawel Osciak <pawel@osciak.com>, Marek Szyprowski"); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/videobuf2-dma-contig.c b/drivers/media/video/videobuf2-dma-contig.c deleted file mode 100644 index 4b7132660a93..000000000000 --- a/drivers/media/video/videobuf2-dma-contig.c +++ /dev/null @@ -1,186 +0,0 @@ -/* - * videobuf2-dma-contig.c - DMA contig memory allocator for videobuf2 - * - * Copyright (C) 2010 Samsung Electronics - * - * Author: Pawel Osciak <pawel@osciak.com> - * - * 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. - */ - -#include <linux/module.h> -#include <linux/slab.h> -#include <linux/dma-mapping.h> - -#include <media/videobuf2-core.h> -#include <media/videobuf2-dma-contig.h> -#include <media/videobuf2-memops.h> - -struct vb2_dc_conf { - struct device *dev; -}; - -struct vb2_dc_buf { - struct vb2_dc_conf *conf; - void *vaddr; - dma_addr_t dma_addr; - unsigned long size; - struct vm_area_struct *vma; - atomic_t refcount; - struct vb2_vmarea_handler handler; -}; - -static void vb2_dma_contig_put(void *buf_priv); - -static void *vb2_dma_contig_alloc(void *alloc_ctx, unsigned long size) -{ - struct vb2_dc_conf *conf = alloc_ctx; - struct vb2_dc_buf *buf; - - buf = kzalloc(sizeof *buf, GFP_KERNEL); - if (!buf) - return ERR_PTR(-ENOMEM); - - buf->vaddr = dma_alloc_coherent(conf->dev, size, &buf->dma_addr, - GFP_KERNEL); - if (!buf->vaddr) { - dev_err(conf->dev, "dma_alloc_coherent of size %ld failed\n", - size); - kfree(buf); - return ERR_PTR(-ENOMEM); - } - - buf->conf = conf; - buf->size = size; - - buf->handler.refcount = &buf->refcount; - buf->handler.put = vb2_dma_contig_put; - buf->handler.arg = buf; - - atomic_inc(&buf->refcount); - - return buf; -} - -static void vb2_dma_contig_put(void *buf_priv) -{ - struct vb2_dc_buf *buf = buf_priv; - - if (atomic_dec_and_test(&buf->refcount)) { - dma_free_coherent(buf->conf->dev, buf->size, buf->vaddr, - buf->dma_addr); - kfree(buf); - } -} - -static void *vb2_dma_contig_cookie(void *buf_priv) -{ - struct vb2_dc_buf *buf = buf_priv; - - return &buf->dma_addr; -} - -static void *vb2_dma_contig_vaddr(void *buf_priv) -{ - struct vb2_dc_buf *buf = buf_priv; - if (!buf) - return NULL; - - return buf->vaddr; -} - -static unsigned int vb2_dma_contig_num_users(void *buf_priv) -{ - struct vb2_dc_buf *buf = buf_priv; - - return atomic_read(&buf->refcount); -} - -static int vb2_dma_contig_mmap(void *buf_priv, struct vm_area_struct *vma) -{ - struct vb2_dc_buf *buf = buf_priv; - - if (!buf) { - printk(KERN_ERR "No buffer to map\n"); - return -EINVAL; - } - - return vb2_mmap_pfn_range(vma, buf->dma_addr, buf->size, - &vb2_common_vm_ops, &buf->handler); -} - -static void *vb2_dma_contig_get_userptr(void *alloc_ctx, unsigned long vaddr, - unsigned long size, int write) -{ - struct vb2_dc_buf *buf; - struct vm_area_struct *vma; - dma_addr_t dma_addr = 0; - int ret; - - buf = kzalloc(sizeof *buf, GFP_KERNEL); - if (!buf) - return ERR_PTR(-ENOMEM); - - ret = vb2_get_contig_userptr(vaddr, size, &vma, &dma_addr); - if (ret) { - printk(KERN_ERR "Failed acquiring VMA for vaddr 0x%08lx\n", - vaddr); - kfree(buf); - return ERR_PTR(ret); - } - - buf->size = size; - buf->dma_addr = dma_addr; - buf->vma = vma; - - return buf; -} - -static void vb2_dma_contig_put_userptr(void *mem_priv) -{ - struct vb2_dc_buf *buf = mem_priv; - - if (!buf) - return; - - vb2_put_vma(buf->vma); - kfree(buf); -} - -const struct vb2_mem_ops vb2_dma_contig_memops = { - .alloc = vb2_dma_contig_alloc, - .put = vb2_dma_contig_put, - .cookie = vb2_dma_contig_cookie, - .vaddr = vb2_dma_contig_vaddr, - .mmap = vb2_dma_contig_mmap, - .get_userptr = vb2_dma_contig_get_userptr, - .put_userptr = vb2_dma_contig_put_userptr, - .num_users = vb2_dma_contig_num_users, -}; -EXPORT_SYMBOL_GPL(vb2_dma_contig_memops); - -void *vb2_dma_contig_init_ctx(struct device *dev) -{ - struct vb2_dc_conf *conf; - - conf = kzalloc(sizeof *conf, GFP_KERNEL); - if (!conf) - return ERR_PTR(-ENOMEM); - - conf->dev = dev; - - return conf; -} -EXPORT_SYMBOL_GPL(vb2_dma_contig_init_ctx); - -void vb2_dma_contig_cleanup_ctx(void *alloc_ctx) -{ - kfree(alloc_ctx); -} -EXPORT_SYMBOL_GPL(vb2_dma_contig_cleanup_ctx); - -MODULE_DESCRIPTION("DMA-contig memory handling routines for videobuf2"); -MODULE_AUTHOR("Pawel Osciak <pawel@osciak.com>"); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/videobuf2-dma-sg.c b/drivers/media/video/videobuf2-dma-sg.c deleted file mode 100644 index 25c3b360e1ad..000000000000 --- a/drivers/media/video/videobuf2-dma-sg.c +++ /dev/null @@ -1,283 +0,0 @@ -/* - * videobuf2-dma-sg.c - dma scatter/gather memory allocator for videobuf2 - * - * Copyright (C) 2010 Samsung Electronics - * - * Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com> - * - * 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. - */ - -#include <linux/module.h> -#include <linux/mm.h> -#include <linux/scatterlist.h> -#include <linux/sched.h> -#include <linux/slab.h> -#include <linux/vmalloc.h> - -#include <media/videobuf2-core.h> -#include <media/videobuf2-memops.h> -#include <media/videobuf2-dma-sg.h> - -struct vb2_dma_sg_buf { - void *vaddr; - struct page **pages; - int write; - int offset; - struct vb2_dma_sg_desc sg_desc; - atomic_t refcount; - struct vb2_vmarea_handler handler; -}; - -static void vb2_dma_sg_put(void *buf_priv); - -static void *vb2_dma_sg_alloc(void *alloc_ctx, unsigned long size) -{ - struct vb2_dma_sg_buf *buf; - int i; - - buf = kzalloc(sizeof *buf, GFP_KERNEL); - if (!buf) - return NULL; - - buf->vaddr = NULL; - buf->write = 0; - buf->offset = 0; - buf->sg_desc.size = size; - buf->sg_desc.num_pages = (size + PAGE_SIZE - 1) >> PAGE_SHIFT; - - buf->sg_desc.sglist = vzalloc(buf->sg_desc.num_pages * - sizeof(*buf->sg_desc.sglist)); - if (!buf->sg_desc.sglist) - goto fail_sglist_alloc; - sg_init_table(buf->sg_desc.sglist, buf->sg_desc.num_pages); - - buf->pages = kzalloc(buf->sg_desc.num_pages * sizeof(struct page *), - GFP_KERNEL); - if (!buf->pages) - goto fail_pages_array_alloc; - - for (i = 0; i < buf->sg_desc.num_pages; ++i) { - buf->pages[i] = alloc_page(GFP_KERNEL | __GFP_ZERO | __GFP_NOWARN); - if (NULL == buf->pages[i]) - goto fail_pages_alloc; - sg_set_page(&buf->sg_desc.sglist[i], - buf->pages[i], PAGE_SIZE, 0); - } - - buf->handler.refcount = &buf->refcount; - buf->handler.put = vb2_dma_sg_put; - buf->handler.arg = buf; - - atomic_inc(&buf->refcount); - - printk(KERN_DEBUG "%s: Allocated buffer of %d pages\n", - __func__, buf->sg_desc.num_pages); - return buf; - -fail_pages_alloc: - while (--i >= 0) - __free_page(buf->pages[i]); - kfree(buf->pages); - -fail_pages_array_alloc: - vfree(buf->sg_desc.sglist); - -fail_sglist_alloc: - kfree(buf); - return NULL; -} - -static void vb2_dma_sg_put(void *buf_priv) -{ - struct vb2_dma_sg_buf *buf = buf_priv; - int i = buf->sg_desc.num_pages; - - if (atomic_dec_and_test(&buf->refcount)) { - printk(KERN_DEBUG "%s: Freeing buffer of %d pages\n", __func__, - buf->sg_desc.num_pages); - if (buf->vaddr) - vm_unmap_ram(buf->vaddr, buf->sg_desc.num_pages); - vfree(buf->sg_desc.sglist); - while (--i >= 0) - __free_page(buf->pages[i]); - kfree(buf->pages); - kfree(buf); - } -} - -static void *vb2_dma_sg_get_userptr(void *alloc_ctx, unsigned long vaddr, - unsigned long size, int write) -{ - struct vb2_dma_sg_buf *buf; - unsigned long first, last; - int num_pages_from_user, i; - - buf = kzalloc(sizeof *buf, GFP_KERNEL); - if (!buf) - return NULL; - - buf->vaddr = NULL; - buf->write = write; - buf->offset = vaddr & ~PAGE_MASK; - buf->sg_desc.size = size; - - first = (vaddr & PAGE_MASK) >> PAGE_SHIFT; - last = ((vaddr + size - 1) & PAGE_MASK) >> PAGE_SHIFT; - buf->sg_desc.num_pages = last - first + 1; - - buf->sg_desc.sglist = vzalloc( - buf->sg_desc.num_pages * sizeof(*buf->sg_desc.sglist)); - if (!buf->sg_desc.sglist) - goto userptr_fail_sglist_alloc; - - sg_init_table(buf->sg_desc.sglist, buf->sg_desc.num_pages); - - buf->pages = kzalloc(buf->sg_desc.num_pages * sizeof(struct page *), - GFP_KERNEL); - if (!buf->pages) - goto userptr_fail_pages_array_alloc; - - num_pages_from_user = get_user_pages(current, current->mm, - vaddr & PAGE_MASK, - buf->sg_desc.num_pages, - write, - 1, /* force */ - buf->pages, - NULL); - - if (num_pages_from_user != buf->sg_desc.num_pages) - goto userptr_fail_get_user_pages; - - sg_set_page(&buf->sg_desc.sglist[0], buf->pages[0], - PAGE_SIZE - buf->offset, buf->offset); - size -= PAGE_SIZE - buf->offset; - for (i = 1; i < buf->sg_desc.num_pages; ++i) { - sg_set_page(&buf->sg_desc.sglist[i], buf->pages[i], - min_t(size_t, PAGE_SIZE, size), 0); - size -= min_t(size_t, PAGE_SIZE, size); - } - return buf; - -userptr_fail_get_user_pages: - printk(KERN_DEBUG "get_user_pages requested/got: %d/%d]\n", - num_pages_from_user, buf->sg_desc.num_pages); - while (--num_pages_from_user >= 0) - put_page(buf->pages[num_pages_from_user]); - kfree(buf->pages); - -userptr_fail_pages_array_alloc: - vfree(buf->sg_desc.sglist); - -userptr_fail_sglist_alloc: - kfree(buf); - return NULL; -} - -/* - * @put_userptr: inform the allocator that a USERPTR buffer will no longer - * be used - */ -static void vb2_dma_sg_put_userptr(void *buf_priv) -{ - struct vb2_dma_sg_buf *buf = buf_priv; - int i = buf->sg_desc.num_pages; - - printk(KERN_DEBUG "%s: Releasing userspace buffer of %d pages\n", - __func__, buf->sg_desc.num_pages); - if (buf->vaddr) - vm_unmap_ram(buf->vaddr, buf->sg_desc.num_pages); - while (--i >= 0) { - if (buf->write) - set_page_dirty_lock(buf->pages[i]); - put_page(buf->pages[i]); - } - vfree(buf->sg_desc.sglist); - kfree(buf->pages); - kfree(buf); -} - -static void *vb2_dma_sg_vaddr(void *buf_priv) -{ - struct vb2_dma_sg_buf *buf = buf_priv; - - BUG_ON(!buf); - - if (!buf->vaddr) - buf->vaddr = vm_map_ram(buf->pages, - buf->sg_desc.num_pages, - -1, - PAGE_KERNEL); - - /* add offset in case userptr is not page-aligned */ - return buf->vaddr + buf->offset; -} - -static unsigned int vb2_dma_sg_num_users(void *buf_priv) -{ - struct vb2_dma_sg_buf *buf = buf_priv; - - return atomic_read(&buf->refcount); -} - -static int vb2_dma_sg_mmap(void *buf_priv, struct vm_area_struct *vma) -{ - struct vb2_dma_sg_buf *buf = buf_priv; - unsigned long uaddr = vma->vm_start; - unsigned long usize = vma->vm_end - vma->vm_start; - int i = 0; - - if (!buf) { - printk(KERN_ERR "No memory to map\n"); - return -EINVAL; - } - - do { - int ret; - - ret = vm_insert_page(vma, uaddr, buf->pages[i++]); - if (ret) { - printk(KERN_ERR "Remapping memory, error: %d\n", ret); - return ret; - } - - uaddr += PAGE_SIZE; - usize -= PAGE_SIZE; - } while (usize > 0); - - - /* - * Use common vm_area operations to track buffer refcount. - */ - vma->vm_private_data = &buf->handler; - vma->vm_ops = &vb2_common_vm_ops; - - vma->vm_ops->open(vma); - - return 0; -} - -static void *vb2_dma_sg_cookie(void *buf_priv) -{ - struct vb2_dma_sg_buf *buf = buf_priv; - - return &buf->sg_desc; -} - -const struct vb2_mem_ops vb2_dma_sg_memops = { - .alloc = vb2_dma_sg_alloc, - .put = vb2_dma_sg_put, - .get_userptr = vb2_dma_sg_get_userptr, - .put_userptr = vb2_dma_sg_put_userptr, - .vaddr = vb2_dma_sg_vaddr, - .mmap = vb2_dma_sg_mmap, - .num_users = vb2_dma_sg_num_users, - .cookie = vb2_dma_sg_cookie, -}; -EXPORT_SYMBOL_GPL(vb2_dma_sg_memops); - -MODULE_DESCRIPTION("dma scatter/gather memory handling routines for videobuf2"); -MODULE_AUTHOR("Andrzej Pietrasiewicz"); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/videobuf2-memops.c b/drivers/media/video/videobuf2-memops.c deleted file mode 100644 index 504cd4cbe29e..000000000000 --- a/drivers/media/video/videobuf2-memops.c +++ /dev/null @@ -1,227 +0,0 @@ -/* - * videobuf2-memops.c - generic memory handling routines for videobuf2 - * - * Copyright (C) 2010 Samsung Electronics - * - * Author: Pawel Osciak <pawel@osciak.com> - * Marek Szyprowski <m.szyprowski@samsung.com> - * - * 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. - */ - -#include <linux/slab.h> -#include <linux/module.h> -#include <linux/dma-mapping.h> -#include <linux/vmalloc.h> -#include <linux/mm.h> -#include <linux/sched.h> -#include <linux/file.h> - -#include <media/videobuf2-core.h> -#include <media/videobuf2-memops.h> - -/** - * vb2_get_vma() - acquire and lock the virtual memory area - * @vma: given virtual memory area - * - * This function attempts to acquire an area mapped in the userspace for - * the duration of a hardware operation. The area is "locked" by performing - * the same set of operation that are done when process calls fork() and - * memory areas are duplicated. - * - * Returns a copy of a virtual memory region on success or NULL. - */ -struct vm_area_struct *vb2_get_vma(struct vm_area_struct *vma) -{ - struct vm_area_struct *vma_copy; - - vma_copy = kmalloc(sizeof(*vma_copy), GFP_KERNEL); - if (vma_copy == NULL) - return NULL; - - if (vma->vm_ops && vma->vm_ops->open) - vma->vm_ops->open(vma); - - if (vma->vm_file) - get_file(vma->vm_file); - - memcpy(vma_copy, vma, sizeof(*vma)); - - vma_copy->vm_mm = NULL; - vma_copy->vm_next = NULL; - vma_copy->vm_prev = NULL; - - return vma_copy; -} -EXPORT_SYMBOL_GPL(vb2_get_vma); - -/** - * vb2_put_userptr() - release a userspace virtual memory area - * @vma: virtual memory region associated with the area to be released - * - * This function releases the previously acquired memory area after a hardware - * operation. - */ -void vb2_put_vma(struct vm_area_struct *vma) -{ - if (!vma) - return; - - if (vma->vm_ops && vma->vm_ops->close) - vma->vm_ops->close(vma); - - if (vma->vm_file) - fput(vma->vm_file); - - kfree(vma); -} -EXPORT_SYMBOL_GPL(vb2_put_vma); - -/** - * vb2_get_contig_userptr() - lock physically contiguous userspace mapped memory - * @vaddr: starting virtual address of the area to be verified - * @size: size of the area - * @res_paddr: will return physical address for the given vaddr - * @res_vma: will return locked copy of struct vm_area for the given area - * - * This function will go through memory area of size @size mapped at @vaddr and - * verify that the underlying physical pages are contiguous. If they are - * contiguous the virtual memory area is locked and a @res_vma is filled with - * the copy and @res_pa set to the physical address of the buffer. - * - * Returns 0 on success. - */ -int vb2_get_contig_userptr(unsigned long vaddr, unsigned long size, - struct vm_area_struct **res_vma, dma_addr_t *res_pa) -{ - struct mm_struct *mm = current->mm; - struct vm_area_struct *vma; - unsigned long offset, start, end; - unsigned long this_pfn, prev_pfn; - dma_addr_t pa = 0; - - start = vaddr; - offset = start & ~PAGE_MASK; - end = start + size; - - vma = find_vma(mm, start); - - if (vma == NULL || vma->vm_end < end) - return -EFAULT; - - for (prev_pfn = 0; start < end; start += PAGE_SIZE) { - int ret = follow_pfn(vma, start, &this_pfn); - if (ret) - return ret; - - if (prev_pfn == 0) - pa = this_pfn << PAGE_SHIFT; - else if (this_pfn != prev_pfn + 1) - return -EFAULT; - - prev_pfn = this_pfn; - } - - /* - * Memory is contigous, lock vma and return to the caller - */ - *res_vma = vb2_get_vma(vma); - if (*res_vma == NULL) - return -ENOMEM; - - *res_pa = pa + offset; - return 0; -} -EXPORT_SYMBOL_GPL(vb2_get_contig_userptr); - -/** - * vb2_mmap_pfn_range() - map physical pages to userspace - * @vma: virtual memory region for the mapping - * @paddr: starting physical address of the memory to be mapped - * @size: size of the memory to be mapped - * @vm_ops: vm operations to be assigned to the created area - * @priv: private data to be associated with the area - * - * Returns 0 on success. - */ -int vb2_mmap_pfn_range(struct vm_area_struct *vma, unsigned long paddr, - unsigned long size, - const struct vm_operations_struct *vm_ops, - void *priv) -{ - int ret; - - size = min_t(unsigned long, vma->vm_end - vma->vm_start, size); - - vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); - ret = remap_pfn_range(vma, vma->vm_start, paddr >> PAGE_SHIFT, - size, vma->vm_page_prot); - if (ret) { - printk(KERN_ERR "Remapping memory failed, error: %d\n", ret); - return ret; - } - - vma->vm_flags |= VM_DONTEXPAND | VM_RESERVED; - vma->vm_private_data = priv; - vma->vm_ops = vm_ops; - - vma->vm_ops->open(vma); - - pr_debug("%s: mapped paddr 0x%08lx at 0x%08lx, size %ld\n", - __func__, paddr, vma->vm_start, size); - - return 0; -} -EXPORT_SYMBOL_GPL(vb2_mmap_pfn_range); - -/** - * vb2_common_vm_open() - increase refcount of the vma - * @vma: virtual memory region for the mapping - * - * This function adds another user to the provided vma. It expects - * struct vb2_vmarea_handler pointer in vma->vm_private_data. - */ -static void vb2_common_vm_open(struct vm_area_struct *vma) -{ - struct vb2_vmarea_handler *h = vma->vm_private_data; - - pr_debug("%s: %p, refcount: %d, vma: %08lx-%08lx\n", - __func__, h, atomic_read(h->refcount), vma->vm_start, - vma->vm_end); - - atomic_inc(h->refcount); -} - -/** - * vb2_common_vm_close() - decrease refcount of the vma - * @vma: virtual memory region for the mapping - * - * This function releases the user from the provided vma. It expects - * struct vb2_vmarea_handler pointer in vma->vm_private_data. - */ -static void vb2_common_vm_close(struct vm_area_struct *vma) -{ - struct vb2_vmarea_handler *h = vma->vm_private_data; - - pr_debug("%s: %p, refcount: %d, vma: %08lx-%08lx\n", - __func__, h, atomic_read(h->refcount), vma->vm_start, - vma->vm_end); - - h->put(h->arg); -} - -/** - * vb2_common_vm_ops - common vm_ops used for tracking refcount of mmaped - * video buffers - */ -const struct vm_operations_struct vb2_common_vm_ops = { - .open = vb2_common_vm_open, - .close = vb2_common_vm_close, -}; -EXPORT_SYMBOL_GPL(vb2_common_vm_ops); - -MODULE_DESCRIPTION("common memory handling routines for videobuf2"); -MODULE_AUTHOR("Pawel Osciak <pawel@osciak.com>"); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/videobuf2-vmalloc.c b/drivers/media/video/videobuf2-vmalloc.c deleted file mode 100644 index 94efa04d8d55..000000000000 --- a/drivers/media/video/videobuf2-vmalloc.c +++ /dev/null @@ -1,223 +0,0 @@ -/* - * videobuf2-vmalloc.c - vmalloc memory allocator for videobuf2 - * - * Copyright (C) 2010 Samsung Electronics - * - * Author: Pawel Osciak <pawel@osciak.com> - * - * 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. - */ - -#include <linux/io.h> -#include <linux/module.h> -#include <linux/mm.h> -#include <linux/sched.h> -#include <linux/slab.h> -#include <linux/vmalloc.h> - -#include <media/videobuf2-core.h> -#include <media/videobuf2-vmalloc.h> -#include <media/videobuf2-memops.h> - -struct vb2_vmalloc_buf { - void *vaddr; - struct page **pages; - struct vm_area_struct *vma; - int write; - unsigned long size; - unsigned int n_pages; - atomic_t refcount; - struct vb2_vmarea_handler handler; -}; - -static void vb2_vmalloc_put(void *buf_priv); - -static void *vb2_vmalloc_alloc(void *alloc_ctx, unsigned long size) -{ - struct vb2_vmalloc_buf *buf; - - buf = kzalloc(sizeof(*buf), GFP_KERNEL); - if (!buf) - return NULL; - - buf->size = size; - buf->vaddr = vmalloc_user(buf->size); - buf->handler.refcount = &buf->refcount; - buf->handler.put = vb2_vmalloc_put; - buf->handler.arg = buf; - - if (!buf->vaddr) { - pr_debug("vmalloc of size %ld failed\n", buf->size); - kfree(buf); - return NULL; - } - - atomic_inc(&buf->refcount); - return buf; -} - -static void vb2_vmalloc_put(void *buf_priv) -{ - struct vb2_vmalloc_buf *buf = buf_priv; - - if (atomic_dec_and_test(&buf->refcount)) { - vfree(buf->vaddr); - kfree(buf); - } -} - -static void *vb2_vmalloc_get_userptr(void *alloc_ctx, unsigned long vaddr, - unsigned long size, int write) -{ - struct vb2_vmalloc_buf *buf; - unsigned long first, last; - int n_pages, offset; - struct vm_area_struct *vma; - dma_addr_t physp; - - buf = kzalloc(sizeof(*buf), GFP_KERNEL); - if (!buf) - return NULL; - - buf->write = write; - offset = vaddr & ~PAGE_MASK; - buf->size = size; - - - vma = find_vma(current->mm, vaddr); - if (vma && (vma->vm_flags & VM_PFNMAP) && (vma->vm_pgoff)) { - if (vb2_get_contig_userptr(vaddr, size, &vma, &physp)) - goto fail_pages_array_alloc; - buf->vma = vma; - buf->vaddr = ioremap_nocache(physp, size); - if (!buf->vaddr) - goto fail_pages_array_alloc; - } else { - first = vaddr >> PAGE_SHIFT; - last = (vaddr + size - 1) >> PAGE_SHIFT; - buf->n_pages = last - first + 1; - buf->pages = kzalloc(buf->n_pages * sizeof(struct page *), - GFP_KERNEL); - if (!buf->pages) - goto fail_pages_array_alloc; - - /* current->mm->mmap_sem is taken by videobuf2 core */ - n_pages = get_user_pages(current, current->mm, - vaddr & PAGE_MASK, buf->n_pages, - write, 1, /* force */ - buf->pages, NULL); - if (n_pages != buf->n_pages) - goto fail_get_user_pages; - - buf->vaddr = vm_map_ram(buf->pages, buf->n_pages, -1, - PAGE_KERNEL); - if (!buf->vaddr) - goto fail_get_user_pages; - } - - buf->vaddr += offset; - return buf; - -fail_get_user_pages: - pr_debug("get_user_pages requested/got: %d/%d]\n", n_pages, - buf->n_pages); - while (--n_pages >= 0) - put_page(buf->pages[n_pages]); - kfree(buf->pages); - -fail_pages_array_alloc: - kfree(buf); - - return NULL; -} - -static void vb2_vmalloc_put_userptr(void *buf_priv) -{ - struct vb2_vmalloc_buf *buf = buf_priv; - unsigned long vaddr = (unsigned long)buf->vaddr & PAGE_MASK; - unsigned int i; - - if (buf->pages) { - if (vaddr) - vm_unmap_ram((void *)vaddr, buf->n_pages); - for (i = 0; i < buf->n_pages; ++i) { - if (buf->write) - set_page_dirty_lock(buf->pages[i]); - put_page(buf->pages[i]); - } - kfree(buf->pages); - } else { - if (buf->vma) - vb2_put_vma(buf->vma); - iounmap(buf->vaddr); - } - kfree(buf); -} - -static void *vb2_vmalloc_vaddr(void *buf_priv) -{ - struct vb2_vmalloc_buf *buf = buf_priv; - - if (!buf->vaddr) { - pr_err("Address of an unallocated plane requested " - "or cannot map user pointer\n"); - return NULL; - } - - return buf->vaddr; -} - -static unsigned int vb2_vmalloc_num_users(void *buf_priv) -{ - struct vb2_vmalloc_buf *buf = buf_priv; - return atomic_read(&buf->refcount); -} - -static int vb2_vmalloc_mmap(void *buf_priv, struct vm_area_struct *vma) -{ - struct vb2_vmalloc_buf *buf = buf_priv; - int ret; - - if (!buf) { - pr_err("No memory to map\n"); - return -EINVAL; - } - - ret = remap_vmalloc_range(vma, buf->vaddr, 0); - if (ret) { - pr_err("Remapping vmalloc memory, error: %d\n", ret); - return ret; - } - - /* - * Make sure that vm_areas for 2 buffers won't be merged together - */ - vma->vm_flags |= VM_DONTEXPAND; - - /* - * Use common vm_area operations to track buffer refcount. - */ - vma->vm_private_data = &buf->handler; - vma->vm_ops = &vb2_common_vm_ops; - - vma->vm_ops->open(vma); - - return 0; -} - -const struct vb2_mem_ops vb2_vmalloc_memops = { - .alloc = vb2_vmalloc_alloc, - .put = vb2_vmalloc_put, - .get_userptr = vb2_vmalloc_get_userptr, - .put_userptr = vb2_vmalloc_put_userptr, - .vaddr = vb2_vmalloc_vaddr, - .mmap = vb2_vmalloc_mmap, - .num_users = vb2_vmalloc_num_users, -}; -EXPORT_SYMBOL_GPL(vb2_vmalloc_memops); - -MODULE_DESCRIPTION("vmalloc memory handling routines for videobuf2"); -MODULE_AUTHOR("Pawel Osciak <pawel@osciak.com>"); -MODULE_LICENSE("GPL"); |