Chinese translated version of Documentation/video4linux/v4l2-framework.txt
If you have any comment or update to the content, please contact the
original document maintainer directly. However, if you have a problem
communicating in English you can also ask the Chinese maintainer for
help. Contact the Chinese maintainer if this translation is outdated
or if there is a problem with the translation.
Maintainer: Mauro Carvalho Chehab mchehab@infradead.org
Chinese maintainer: Fu Wei tekkamanninja@gmail.com
Documentation/video4linux/v4l2-framework.txt çä¸æç¿»è¯
å¦ææ³è¯è®ºææ´æ°æ¬æçå
容ï¼è¯·ç´æ¥èç³»åææ¡£çç»´æ¤è
ãå¦æä½ ä½¿ç¨è±æ
交æµæå°é¾çè¯ï¼ä¹å¯ä»¥åä¸æçç»´æ¤è
æ±å©ãå¦ææ¬ç¿»è¯æ´æ°ä¸åæ¶æè
ç¿»
è¯åå¨é®é¢ï¼è¯·èç³»ä¸æçç»´æ¤è
ã
è±æçç»´æ¤è
ï¼ Mauro Carvalho Chehab mchehab@infradead.org
ä¸æçç»´æ¤è
ï¼ å
ç Fu Wei tekkamanninja@gmail.com
ä¸æçç¿»è¯è
ï¼ å
ç Fu Wei tekkamanninja@gmail.com
ä¸æçæ ¡è¯è
ï¼ å
ç Fu Wei tekkamanninja@gmail.com
以ä¸ä¸ºæ£æ
V4L2 驱å¨æ¡æ¶æ¦è§
æ¬ææ¡£æè¿° V4L2 æ¡æ¶ææä¾çåç§ç»æåå®ä»¬ä¹é´çå ³ç³»ã
ä»ç»
大é¨åç°ä»£ V4L2 设å¤ç±å¤ä¸ª IC ç»æï¼å¨ /dev ä¸å¯¼åºå¤ä¸ªè®¾å¤èç¹ï¼
并åæ¶å建é V4L2 设å¤ï¼å¦ DVBãALSAãFBãI2C å红å¤è¾å
¥è®¾å¤ï¼ã
ç±äºè¿ç§ç¡¬ä»¶çå¤ææ§ï¼V4L2 驱å¨ä¹åå¾é常å¤æã
å°¤å
¶æ¯ V4L2 å¿
é¡»æ¯æ IC å®ç°é³è§é¢çå¤è·¯å¤ç¨åç¼è§£ç ï¼è¿å°±æ´å¢å äºå
¶
å¤ææ§ãé常è¿äº IC éè¿ä¸ä¸ªæå¤ä¸ª I2C æ»çº¿è¿æ¥å°ä¸»æ¡¥é©±å¨å¨ï¼ä½ä¹å¯
使ç¨å
¶ä»æ»çº¿ãè¿äºè®¾å¤ç§°ä¸ºâå设å¤âã
é¿æ以æ¥ï¼è¿ä¸ªæ¡æ¶ä»
éäºéè¿ video_device ç»æä½å建 V4L 设å¤èç¹ï¼
å¹¶ä½¿ç¨ video_buf å¤çè§é¢ç¼å²ï¼æ³¨ï¼æ¬æä¸è®¨è®º video_buf æ¡æ¶ï¼ã
è¿æå³çææ驱å¨å¿
é¡»èªå·±è®¾ç½®è®¾å¤å®ä¾å¹¶è¿æ¥å°å设å¤ãå
¶ä¸ä¸é¨åè¦æ£ç¡®å°
å®ææ¯æ¯è¾å¤æçï¼ä½¿å¾è®¸å¤é©±å¨é½æ²¡ææ£ç¡®å°å®ç°ã
ç±äºæ¡æ¶ç缺失ï¼æå¾å¤éç¨ä»£ç é½ä¸å¯éå¤å©ç¨ã
å æ¤ï¼è¿ä¸ªæ¡æ¶æ建ææ驱å¨é½éè¦çåºæ¬ç»æåï¼èç»ä¸çæ¡æ¶å°ä½¿éç¨ä»£ç
å建æå®ç¨å½æ°å¹¶å¨ææ驱å¨ä¸å
±äº«åå¾æ´å 容æã
驱å¨ç»æ
ææ V4L2 驱å¨é½æå¦ä¸ç»æï¼
æ¯ä¸ªè®¾å¤å®ä¾çç»æä½–å å«å ¶è®¾å¤ç¶æã
åå§ååæ§å¶å设å¤çæ¹æ³ï¼å¦ææï¼ã
å建 V4L2 设å¤èç¹ (/dev/videoXã/dev/vbiX å /dev/radioX)
并è·è¸ªè®¾å¤èç¹çç¹å®æ°æ®ãç¹å®æ件å¥æç»æä½–å å«æ¯ä¸ªæ件å¥æçæ°æ®ã
è§é¢ç¼å²å¤çã
以ä¸æ¯å®ä»¬çåç¥å ³ç³»å¾ï¼
device instancesï¼è®¾å¤å®ä¾ï¼
|
+-sub-device instancesï¼å设å¤å®ä¾ï¼
|
\-V4L2 device nodesï¼V4L2 设å¤èç¹ï¼
|
\-filehandle instancesï¼æ件å¥æå®ä¾ï¼
æ¡æ¶ç»æ
该æ¡æ¶é常类似驱å¨ç»æï¼å®æä¸ä¸ª v4l2_device ç»æç¨äºä¿å设å¤
å®ä¾çæ°æ®ï¼ä¸ä¸ª v4l2_subdev ç»æä½ä»£è¡¨å设å¤å®ä¾ï¼video_device
ç»æä½ä¿å V4L2 设å¤èç¹çæ°æ®ï¼å°æ¥ v4l2_fh ç»æä½å°è·è¸ªæ件å¥æ
å®ä¾ï¼ææªå°æªå®ç°ï¼ã
V4L2 æ¡æ¶ä¹å¯ä¸åªä½æ¡æ¶æ´åï¼å¯éçï¼ãå¦æ驱å¨è®¾ç½®äº v4l2_device
ç»æä½ç mdev åï¼å设å¤åè§é¢èç¹çå
¥å£å°èªå¨åºç°å¨åªä½æ¡æ¶ä¸ã
v4l2_device ç»æä½
æ¯ä¸ªè®¾å¤å®ä¾é½éè¿ v4l2_device (v4l2-device.h)ç»æä½æ¥è¡¨ç¤ºã
ç®å设å¤å¯ä»¥ä»
åé
è¿ä¸ªç»æä½ï¼ä½å¨å¤§å¤æ°æ
åµä¸ï¼é½ä¼å°è¿ä¸ªç»æä½
åµå
¥å°ä¸ä¸ªæ´å¤§çç»æä½ä¸ã
ä½ å¿ é¡»æ³¨åè¿ä¸ªè®¾å¤å®ä¾ï¼
v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev);
注åæä½å°ä¼åå§å v4l2_device ç»æä½ãå¦æ dev->driver_data å
为 NULLï¼å°±å°å
¶æå v4l2_devã
éè¦ä¸åªä½æ¡æ¶æ´åç驱å¨å¿
é¡»æå¨è®¾ç½® dev->driver_dataï¼æåå
å«
v4l2_device ç»æä½å®ä¾ç驱å¨ç¹å®è®¾å¤ç»æä½ãè¿å¯ä»¥å¨æ³¨å V4L2 设å¤
å®ä¾åéè¿ dev_set_drvdata() å½æ°å®æãåæ¶å¿
须设置 v4l2_device
ç»æä½ç mdev åï¼æåéå½çåå§å并注åè¿ç media_device å®ä¾ã
å¦æ v4l2_dev->name 为空ï¼åå®å°è¢«è®¾ç½®ä¸ºä» dev ä¸è¡çåºçå¼ï¼ä¸ºäº
æ´å 精确ï¼å½¢å¼ä¸ºé©±å¨ååè· bus_idï¼ãå¦æä½ å¨è°ç¨ v4l2_device_register
åå·²ç»è®¾ç½®å¥½äºï¼åä¸ä¼è¢«ä¿®æ¹ãå¦æ dev 为 NULLï¼åä½ å¿
é¡»å¨è°ç¨
v4l2_device_register å设置 v4l2_dev->nameã
ä½ å¯ä»¥åºäºé©±å¨åå驱å¨çå
¨å± atomic_t ç±»åçå®ä¾ç¼å·ï¼éè¿
v4l2_device_set_name() 设置 nameãè¿æ ·ä¼çæ类似 ivtv0ãivtv1 ç
ååãè¥é©±å¨å以æ°åç»å°¾ï¼åä¼å¨ç¼å·å驱å¨åé´æå
¥ä¸ä¸ªç ´æå·ï¼å¦ï¼
cx18-0ãcx18-1 çãæ¤å½æ°è¿åå®ä¾ç¼å·ã
第ä¸ä¸ª âdevâ åæ°é常æ¯ä¸ä¸ªæå pci_devãusb_interface æ
platform_device çæéãå¾å°ä½¿å
¶ä¸º NULLï¼é¤éæ¯ä¸ä¸ªISA设å¤æè
å½ä¸ä¸ªè®¾å¤å建äºå¤ä¸ª PCI 设å¤ï¼ä½¿å¾ v4l2_dev æ æ³ä¸ä¸ä¸ªç¹å®çç¶è®¾å¤
å
³èã
ä½ ä¹å¯ä»¥æä¾ä¸ä¸ª notify() åè°ï¼ä½¿å设å¤å¯ä»¥è°ç¨å®å®ç°äºä»¶éç¥ã
ä½è¿ä¸ªè®¾ç½®ä¸å设å¤ç¸å
³ãå设å¤æ¯æçä»»ä½éç¥å¿
é¡»å¨
include/media/
注é v4l2_device 使ç¨å¦ä¸å½æ°ï¼
v4l2_device_unregister(struct v4l2_device *v4l2_dev);
å¦æ dev->driver_data åæå v4l2_devï¼å°ä¼è¢«é置为 NULLã注éåæ¶
ä¼èªå¨ä»è®¾å¤ä¸æ³¨éææå设å¤ã
å¦æä½ æä¸ä¸ªçææ设å¤ï¼å¦USB设å¤ï¼ï¼åå½æå¼åçæ¶ï¼ç¶è®¾å¤å°æ æã
ç±äº v4l2_device æä¸ä¸ªæåç¶è®¾å¤çæéå¿
须被æ¸
é¤ï¼åæ¶æ å¿ç¶è®¾å¤
å·²æ¶å¤±ï¼æ以å¿
é¡»è°ç¨ä»¥ä¸å½æ°ï¼
v4l2_device_disconnect(struct v4l2_device *v4l2_dev);
è¿ä¸ªå½æ°å¹¶ä¸æ³¨éå设å¤ï¼å æ¤ä½ ä¾ç¶è¦è°ç¨ v4l2_device_unregister()
å½æ°ãå¦æä½ ç驱å¨å¨å¹¶éçææçï¼å°±æ²¡æå¿
è¦è°ç¨ v4l2_device_disconnect()ã
ææ¶ä½ éè¦éåææ被ç¹å®é©±å¨æ³¨åç设å¤ãè¿é常åçå¨å¤ä¸ªè®¾å¤é©±å¨ä½¿ç¨
åä¸ä¸ªç¡¬ä»¶çæ
åµä¸ãå¦ï¼ivtvfb 驱å¨æ¯ä¸ä¸ªä½¿ç¨ ivtv 硬件ç帧ç¼å²é©±å¨ï¼
åæ¶ alsa 驱å¨ä¹ä½¿ç¨æ¤ç¡¬ä»¶ã
ä½ å¯ä»¥ä½¿ç¨å¦ä¸ä¾ç¨éåææ注åç设å¤ï¼
static int callback(struct device *dev, void *p)
{
struct v4l2_device *v4l2_dev = dev_get_drvdata(dev);
/* æµè¯è¿ä¸ªè®¾å¤æ¯å¦å·²ç»åå§å */
if (v4l2_dev == NULL)
return 0;
...
return 0;
}
int iterate(void *p)
{
struct device_driver *drv;
int err;
/* å¨PCI æ»çº¿ä¸æ¥æ¾ivtv驱å¨ã
pci_bus_typeæ¯å
¨å±ç. 对äºUSBæ»çº¿ä½¿ç¨usb_bus_typeã */
drv = driver_find("ivtv", &pci_bus_type);
/* éåææçivtv设å¤å®ä¾ */
err = driver_for_each_device(drv, NULL, p, callback);
put_driver(drv);
return err;
}
ææ¶ä½ éè¦ä¸ä¸ªè®¾å¤å®ä¾çè¿è¡è®¡æ°ãè¿ä¸ªé常ç¨äºæ å°ä¸ä¸ªè®¾å¤å®ä¾å°ä¸ä¸ª
模åéæ©æ°ç»çç´¢å¼ã
æ¨èæ¹æ³å¦ä¸ï¼
static atomic_t drv_instance = ATOMIC_INIT(0);
static int drv_probe(struct pci_dev *pdev, const struct pci_device_id *pci_id)
{
…
state->instance = atomic_inc_return(&drv_instance) - 1;
}
å¦æä½ æå¤ä¸ªè®¾å¤èç¹ï¼å¯¹äºçææ设å¤ï¼ç¥éä½æ¶æ³¨é v4l2_device ç»æä½
å°±æ¯è¾å°é¾ãä¸ºæ¤ v4l2_device æå¼ç¨è®¡æ°æ¯æãå½è°ç¨ video_register_device
æ¶å¢å å¼ç¨è®¡æ°ï¼è设å¤èç¹éæ¾æ¶åå°å¼ç¨è®¡æ°ãå½å¼ç¨è®¡æ°ä¸ºé¶ï¼å
v4l2_device çrelease() åè°å°è¢«æ§è¡ãä½ å°±å¯ä»¥å¨æ¤æ¶åæåçæ¸
çå·¥ä½ã
å¦æå建äºå
¶ä»è®¾å¤èç¹ï¼æ¯å¦ ALSAï¼ï¼åä½ å¯ä»¥éè¿ä»¥ä¸å½æ°æå¨å¢å
å¼ç¨è®¡æ°ï¼
void v4l2_device_get(struct v4l2_device *v4l2_dev);
æ:
int v4l2_device_put(struct v4l2_device *v4l2_dev);
ç±äºå¼ç¨ææ¯åå§å为 1 ï¼ä½ ä¹éè¦å¨ disconnect() åè°ï¼å¯¹äº USB 设å¤ï¼ä¸
è°ç¨ v4l2_device_putï¼æè
remove() åè°ï¼ä¾å¦å¯¹äº PCI 设å¤ï¼ï¼å¦å
å¼ç¨è®¡æ°å°æ°¸è¿ä¸ä¼ä¸º 0 ã
v4l2_subdevç»æä½
许å¤é©±å¨éè¦ä¸å设å¤éä¿¡ãè¿äºè®¾å¤å¯ä»¥å®æåç§ä»»å¡ï¼ä½é常ä»ä»¬è´è´£
é³è§é¢å¤ç¨åç¼è§£ç ãå¦ç½ç»æå头çå设å¤é常æ¯ä¼ æå¨åæå头æ§å¶å¨ã
è¿äºä¸è¬ä¸º I2C æ¥å£è®¾å¤ï¼ä½å¹¶ä¸ä¸å®é½æ¯ã为äºç»é©±å¨æä¾è°ç¨å设å¤ç
ç»ä¸æ¥å£ï¼v4l2_subdev ç»æä½ï¼v4l2-subdev.hï¼äº§çäºã
æ¯ä¸ªå设å¤é©±å¨é½å¿
é¡»æä¸ä¸ª v4l2_subdev ç»æä½ãè¿ä¸ªç»æä½å¯ä»¥åç¬
代表ä¸ä¸ªç®åçå设å¤ï¼ä¹å¯ä»¥åµå
¥å°ä¸ä¸ªæ´å¤§çç»æä½ä¸ï¼ä¸æ´å¤è®¾å¤ç¶æ
ä¿¡æ¯ä¿åå¨ä¸èµ·ãé常æä¸ä¸ªä¸çº§è®¾å¤ç»æä½ï¼æ¯å¦ï¼i2c_clientï¼å
å«äº
å
æ ¸å建ç设å¤æ°æ®ãå»ºè®®ä½¿ç¨ v4l2_set_subdevdata() å°è¿ä¸ªç»æä½ç
æéä¿åå¨ v4l2_subdev çç§ææ°æ®å(dev_priv)ä¸ãè¿ä½¿å¾éè¿ v4l2_subdev
æ¾å°å®é
çä½å±æ»çº¿ç¹å®è®¾å¤æ°æ®åå¾å®¹æã
ä½ åæ¶éè¦ä¸ä¸ªä»ä½å±ç»æä½è·å v4l2_subdev æéçæ¹æ³ã对äºå¸¸ç¨ç
i2c_client ç»æä½ï¼i2c_set_clientdata() å½æ°å¯ç¨äºä¿åä¸ä¸ª v4l2_subdev
æéï¼å¯¹äºå
¶ä»æ»çº¿ä½ å¯è½éè¦ä½¿ç¨å
¶ä»ç¸å
³å½æ°ã
桥驱å¨ä¸ä¹åºä¿åæ¯ä¸ªå设å¤çç§ææ°æ®ï¼æ¯å¦ä¸ä¸ªæåç¹å®æ¡¥çå设å¤ç§æ
æ°æ®çæéãä¸ºæ¤ v4l2_subdev ç»æä½æä¾ä¸»æºç§ææ°æ®å(host_priv)ï¼
并å¯éè¿ v4l2_get_subdev_hostdata() å v4l2_set_subdev_hostdata()
访é®ã
ä»æ»çº¿æ¡¥é©±å¨çè§è§ï¼é©±å¨å è½½å设å¤æ¨¡å并以æç§æ¹å¼è·å¾ v4l2_subdev
ç»æä½æéãå¯¹äº i2c æ»çº¿è®¾å¤ç¸å¯¹ç®åï¼è°ç¨ i2c_get_clientdata()ã
对äºå
¶ä»æ»çº¿ä¹éè¦å类似çæä½ãé对 I2C æ»çº¿ä¸çå设å¤è¾
å©å½æ°å¸®ä½
å®æäºå¤§é¨åå¤æçå·¥ä½ã
æ¯ä¸ª v4l2_subdev é½å
å«å设å¤é©±å¨éè¦å®ç°çå½æ°æéï¼å¦æ对æ¤è®¾å¤
ä¸éç¨ï¼å¯ä¸ºNULLï¼ãç±äºå设å¤å¯å®æ许å¤ä¸åçå·¥ä½ï¼èå¨ä¸ä¸ªåºå¤§ç
å½æ°æéç»æä½ä¸é常ä»
æå°æ°æç¨çå½æ°å®ç°å
¶åè½è¯å®ä¸åéãæ以ï¼
å½æ°æéæ ¹æ®å
¶å®ç°çåè½è¢«åç±»ï¼æ¯ä¸ç±»é½æèªå·±çå½æ°æéç»æä½ã
顶å±å½æ°æéç»æä½å
å«äºæååç±»å½æ°æéç»æä½çæéï¼å¦æå设å¤é©±å¨
ä¸æ¯æ该类å½æ°ä¸çä»»ä½ä¸ä¸ªåè½ï¼åæå该类ç»æä½çæé为NULLã
è¿äºç»æä½å®ä¹å¦ä¸ï¼
struct v4l2_subdev_core_ops {
int (*g_chip_ident)(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip);
int (*log_status)(struct v4l2_subdev *sd);
int (*init)(struct v4l2_subdev *sd, u32 val);
…
};
struct v4l2_subdev_tuner_ops {
…
};
struct v4l2_subdev_audio_ops {
…
};
struct v4l2_subdev_video_ops {
…
};
struct v4l2_subdev_pad_ops {
…
};
struct v4l2_subdev_ops {
const struct v4l2_subdev_core_ops *core;
const struct v4l2_subdev_tuner_ops *tuner;
const struct v4l2_subdev_audio_ops *audio;
const struct v4l2_subdev_video_ops *video;
const struct v4l2_subdev_pad_ops *video;
};
å
¶ä¸ coreï¼æ ¸å¿ï¼å½æ°éé常å¯ç¨äºææå设å¤ï¼å
¶ä»ç±»å«çå®ç°ä¾èµäº
å设å¤ãå¦è§é¢è®¾å¤å¯è½ä¸æ¯æé³é¢æä½å½æ°ï¼åä¹äº¦ç¶ã
è¿æ ·ç设置å¨éå¶äºå½æ°æéæ°éçåæ¶ï¼è¿ä½¿å¢å æ°çæä½å½æ°ååç±»
åå¾è¾ä¸ºå®¹æã
å设å¤é©±å¨å¯ä½¿ç¨å¦ä¸å½æ°åå§å v4l2_subdev ç»æä½ï¼
v4l2_subdev_init(sd, &ops);
ç¶åï¼ä½ å¿
é¡»ç¨ä¸ä¸ªå¯ä¸çåååå§å subdev->nameï¼å¹¶åå§å模åç
owner åãè¥ä½¿ç¨ i2c è¾
å©å½æ°ï¼è¿äºé½ä¼å¸®ä½ å¤ç好ã
è¥éååªä½æ¡æ¶æ´åï¼ä½ å¿
é¡»è°ç¨ media_entity_init() åå§å v4l2_subdev
ç»æä½ä¸ç media_entity ç»æä½ï¼entity åï¼ï¼
struct media_pad *pads = &my_sd->pads;
int err;
err = media_entity_init(&sd->entity, npads, pads, 0);
pads æ°ç»å¿
é¡»é¢å
åå§åãæ é¡»æå¨è®¾ç½® media_entity ç type å
name åï¼ä½å¦æå¿
è¦ï¼revision åå¿
é¡»åå§åã
å½ï¼ä»»ä½ï¼å设å¤èç¹è¢«æå¼/å ³éï¼å¯¹ entity çå¼ç¨å°è¢«èªå¨è·å/éæ¾ã
å¨å设å¤è¢«æ³¨éä¹åï¼ä¸è¦å¿è®°æ¸ ç media_entity ç»æä½ï¼
media_entity_cleanup(&sd->entity);
å¦æå设å¤é©±å¨è¶åäºå¤çè§é¢å¹¶æ´åè¿äºåªä½æ¡æ¶ï¼å¿
é¡»ä½¿ç¨ v4l2_subdev_pad_ops
æ¿ä»£ v4l2_subdev_video_ops å®ç°æ ¼å¼ç¸å
³çåè½ã
è¿ç§æ
åµä¸ï¼å设å¤é©±å¨åºè¯¥è®¾ç½® link_validate åï¼ä»¥æä¾å®èªèº«çé¾æ¥
éªè¯å½æ°ãé¾æ¥éªè¯å½æ°åºå¯¹ç®¡éï¼ä¸¤ç«¯é¾æ¥çé½æ¯ V4L2 å设å¤ï¼ä¸çæ¯ä¸ª
é¾æ¥è°ç¨ã驱å¨è¿è¦è´è´£éªè¯å设å¤åè§é¢èç¹é´æ ¼å¼é
ç½®çæ£ç¡®æ§ã
å¦æ link_validate æä½æ²¡æ设置ï¼é»è®¤ç v4l2_subdev_link_validate_default()
å½æ°å°ä¼è¢«è°ç¨ãè¿ä¸ªå½æ°ä¿è¯å®½ãé«ååªä½æ»çº¿åç´ æ ¼å¼å¨é¾æ¥çæ¶å两端
é½ä¸è´ãå设å¤é©±å¨é¤äºå®ä»¬èªå·±çæ£æµå¤ï¼ä¹å¯ä»¥èªç±ä½¿ç¨è¿ä¸ªå½æ°ä»¥æ§è¡
ä¸é¢æå°çæ£æ¥ã
设å¤ï¼æ¡¥ï¼é©±å¨ç¨åºå¿ é¡»å v4l2_device 注å v4l2_subdevï¼
int err = v4l2_device_register_subdev(v4l2_dev, sd);
å¦æå设å¤æ¨¡åå¨å®æ³¨ååæ¶å¤±ï¼è¿ä¸ªæä½å¯è½å¤±è´¥ãå¨è¿ä¸ªå½æ°æåè¿ååï¼
subdev->dev åå°±æåäº v4l2_deviceã
å¦æ v4l2_device ç¶è®¾å¤ç mdev å为é NULL å¼ï¼åå设å¤å®ä½å°è¢«èªå¨
注å为åªä½è®¾å¤ã
注éå设å¤åå¯ç¨å¦ä¸å½æ°ï¼
v4l2_device_unregister_subdev(sd);
æ¤åï¼å设å¤æ¨¡åå°±å¯å¸è½½ï¼ä¸ sd->dev == NULLã
注åä¹è®¾å¤åï¼å¯éè¿ä»¥ä¸æ¹å¼ç´æ¥è°ç¨å ¶æä½å½æ°ï¼
err = sd->ops->core->g_chip_ident(sd, &chip);
ä½ä½¿ç¨å¦ä¸å®ä¼æ¯è¾å®¹æä¸åéï¼
err = v4l2_subdev_call(sd, core, g_chip_ident, &chip);
è¿ä¸ªå®å°ä¼å NULL æéæ£æ¥ï¼å¦æ subdev 为 NULLï¼åè¿å-ENODEVï¼å¦æ
subdev->core æ subdev->core->g_chip_ident 为 NULLï¼åè¿å -ENOIOCTLCMDï¼
å¦åå°è¿å subdev->ops->core->g_chip_ident ops è°ç¨çå®é
ç»æã
ææ¶ä¹å¯è½åæ¶è°ç¨æææä¸ç³»åå设å¤çæ个æä½å½æ°ï¼
v4l2_device_call_all(v4l2_dev, 0, core, g_chip_ident, &chip);
ä»»ä½ä¸æ¯ææ¤æä½çå设å¤é½ä¼è¢«è·³è¿ï¼å¹¶å¿½ç¥é误è¿åå¼ãä½å¦æä½ éè¦
æ£æ¥åºéç ï¼åå¯ä½¿ç¨å¦ä¸å½æ°ï¼
err = v4l2_device_call_until_err(v4l2_dev, 0, core, g_chip_ident, &chip);
é¤ -ENOIOCTLCMD å¤çä»»ä½é误é½ä¼è·³åºå¾ªç¯å¹¶è¿åé误å¼ãå¦æï¼é¤ -ENOIOCTLCMD
å¤ï¼æ²¡æé误åçï¼åè¿å 0ã
对äºä»¥ä¸ä¸¤ä¸ªå½æ°ç第äºä¸ªåæ°ä¸ºç» IDãå¦æ为 0ï¼åææå设å¤é½ä¼æ§è¡
è¿ä¸ªæä½ãå¦æ为é 0 å¼ï¼ååªæé£äºç» ID å¹é
çå设å¤æä¼æ§è¡æ¤æä½ã
å¨æ¡¥é©±å¨æ³¨åä¸ä¸ªå设å¤åï¼å¯ä»¥è®¾ç½® sd->grp_id 为任ä½ææå¼ï¼é»è®¤å¼ä¸º
0ï¼ãè¿ä¸ªå¼å±äºæ¡¥é©±å¨ï¼ä¸å设å¤é©±å¨å°ä¸ä¼ä¿®æ¹å使ç¨å®ã
ç» ID èµäºäºæ¡¥é©±å¨æ´å¤å¯¹äºå¦ä½è°ç¨åè°çæ§å¶ãä¾å¦ï¼çµè·¯æ¿ä¸æå¤ä¸ª
é³é¢è¯çï¼æ¯ä¸ªé½ææ¹åé³éçè½åãä½å½ç¨æ·æ³è¦æ¹åé³éçæ¶åï¼é常
åªæä¸ä¸ªä¼è¢«å®é
使ç¨ãä½ å¯ä»¥å¯¹è¿æ ·çå设å¤è®¾ç½®ç» ID 为ï¼ä¾å¦ AUDIO_CONTROLLERï¼
并å¨è°ç¨ v4l2_device_call_all() æ¶æå®å®ä¸ºç» ID å¼ãè¿å°±ä¿è¯äºåªæ
éè¦çå设å¤æä¼æ§è¡è¿ä¸ªåè°ã
å¦æå设å¤éè¦éç¥å®ç v4l2_device ç¶è®¾å¤ä¸ä¸ªäºä»¶ï¼å¯ä»¥è°ç¨
v4l2_subdev_notify(sd, notification, arg)ãè¿ä¸ªå®æ£æ¥æ¯å¦æä¸ä¸ª
notify() åè°è¢«æ³¨åï¼å¦æ没æï¼è¿å -ENODEVãå¦åè¿å notify() è°ç¨
ç»æã
ä½¿ç¨ v4l2_subdev ç好å¤å¨äºå®æ¯ä¸ä¸ªéç¨ç»æä½ï¼ä¸ä¸å
å«ä»»ä½åºå±ç¡¬ä»¶
ä¿¡æ¯ãææ驱å¨å¯ä»¥å
å«å¤ä¸ª I2C æ»çº¿çå设å¤ï¼ä½ä¹æå设å¤æ¯éè¿ GPIO
æ§å¶ãè¿ä¸ªåºå«ä»
å¨é
置设å¤æ¶æå
³ç³»ï¼ä¸æ¦å设å¤æ³¨åå®æï¼å¯¹äº v4l2
åç³»ç»æ¥è¯´å°±å®å
¨éæäºã
V4L2 å设å¤ç¨æ·ç©ºé´API
é¤äºéè¿ v4l2_subdev_ops ç»æ导åºçå
æ ¸ APIï¼V4L2 å设å¤ä¹å¯ä»¥ç´æ¥
éè¿ç¨æ·ç©ºé´åºç¨ç¨åºæ¥æ§å¶ã
å¯ä»¥å¨ /dev ä¸å建å为 v4l-subdevX 设å¤èç¹ï¼ä»¥éè¿å
¶ç´æ¥è®¿é®å设å¤ã
å¦æå设å¤æ¯æç¨æ·ç©ºé´ç´æ¥é
ç½®ï¼å¿
é¡»å¨æ³¨åå设置 V4L2_SUBDEV_FL_HAS_DEVNODE
æ å¿ã
注åå设å¤ä¹åï¼ v4l2_device 驱å¨ä¼éè¿è°ç¨ v4l2_device_register_subdev_nodes()
å½æ°ä¸ºææ已注åå¹¶è®¾ç½®äº V4L2_SUBDEV_FL_HAS_DEVNODE çå设å¤å建
设å¤èç¹ãè¿äºè®¾å¤èç¹ä¼å¨å设å¤æ³¨éæ¶èªå¨å é¤ã
è¿äºè®¾å¤èç¹å¤ç V4L2 API çä¸ä¸ªåéã
VIDIOC_QUERYCTRL
VIDIOC_QUERYMENU
VIDIOC_G_CTRL
VIDIOC_S_CTRL
VIDIOC_G_EXT_CTRLS
VIDIOC_S_EXT_CTRLS
VIDIOC_TRY_EXT_CTRLS
è¿äº ioctls æ§å¶ä¸ V4L2 ä¸å®ä¹çä¸è´ãä»ä»¬è¡ä¸ºç¸åï¼å¯ä¸ç
ä¸åæ¯ä»ä»¬åªå¤çå设å¤çæ§å¶å®ç°ãæ ¹æ®é©±å¨ç¨åºï¼è¿äºæ§å¶ä¹
å¯ä»¥éè¿ä¸ä¸ªï¼æå¤ä¸ªï¼ V4L2 设å¤èç¹è®¿é®ã
VIDIOC_DQEVENT
VIDIOC_SUBSCRIBE_EVENT
VIDIOC_UNSUBSCRIBE_EVENT
è¿äº ioctls äºä»¶ä¸ V4L2 ä¸å®ä¹çä¸è´ãä»ä»¬è¡ä¸ºç¸åï¼å¯ä¸ç
ä¸åæ¯ä»ä»¬åªå¤çå设å¤äº§ççäºä»¶ãæ ¹æ®é©±å¨ç¨åºï¼è¿äºäºä»¶ä¹
å¯ä»¥éè¿ä¸ä¸ªï¼æå¤ä¸ªï¼ V4L2 设å¤èç¹ä¸æ¥ã
è¦ä½¿ç¨äºä»¶éç¥çå设å¤é©±å¨ï¼å¨æ³¨åå设å¤åå¿
é¡»å¨ v4l2_subdev::flags
ä¸è®¾ç½® V4L2_SUBDEV_USES_EVENTS å¹¶å¨ v4l2_subdev::nevents
ä¸åå§åäºä»¶éå深度ã注åå®æåï¼äºä»¶ä¼å¨ v4l2_subdev::devnode
设å¤èç¹ä¸åé常ä¸æ ·è¢«æéã
为æ£ç¡®æ¯æäºä»¶æºå¶ï¼poll() æ件æä½ä¹åºè¢«å®ç°ã
ç§æ ioctls
ä¸å¨ä»¥ä¸å表ä¸çææ ioctls ä¼éè¿ core::ioctl æä½ç´æ¥ä¼ é
ç»å设å¤é©±å¨ã
I2C å设å¤é©±å¨
ç±äºè¿äºé©±å¨å¾å¸¸è§ï¼æ以å
ç¹æä¾äºç¹å®çè¾
å©å½æ°(v4l2-common.h)让è¿äº
设å¤ç使ç¨æ´å 容æã
æ·»å v4l2_subdev æ¯æçæ¨èæ¹æ³æ¯è®© I2C 驱å¨å° v4l2_subdev ç»æä½
åµå
¥å°ä¸ºæ¯ä¸ª I2C 设å¤å®ä¾å建çç¶æç»æä½ä¸ãèæç®åç设å¤æ²¡æç¶æ
ç»æä½ï¼æ¤æ¶å¯ä»¥ç´æ¥å建ä¸ä¸ª v4l2_subdev ç»æä½ã
ä¸ä¸ªå ¸åçç¶æç»æä½å¦ä¸æ示ï¼âchipnameâç¨è¯çå代æ¿ï¼ï¼
struct chipname_state {
struct v4l2_subdev sd;
… /* éå çç¶æå*/
};
åå§å v4l2_subdev ç»æä½çæ¹æ³å¦ä¸ï¼
v4l2_i2c_subdev_init(&state->sd, client, subdev_ops);
è¿ä¸ªå½æ°å°å¡«å
v4l2_subdev ç»æä½ä¸çææåï¼å¹¶ä¿è¯ v4l2_subdev å
i2c_client é½æåå½¼æ¤ã
åæ¶ï¼ä½ ä¹åºè¯¥ä¸ºä» v4l2_subdev æéæ¾å° chipname_state ç»æä½æé
æ·»å ä¸ä¸ªè¾
å©å
èå½æ°ã
static inline struct chipname_state *to_state(struct v4l2_subdev *sd)
{
return container_of(sd, struct chipname_state, sd);
}
使ç¨ä»¥ä¸å½æ°å¯ä»¥éè¿ v4l2_subdev ç»æä½æéè·å¾ i2c_client ç»æä½
æéï¼
struct i2c_client *client = v4l2_get_subdevdata(sd);
è以ä¸å½æ°åç¸åï¼éè¿ i2c_client ç»æä½æéè·å¾ v4l2_subdev ç»æä½
æéï¼
struct v4l2_subdev *sd = i2c_get_clientdata(client);
å½ remove()å½æ°è¢«è°ç¨åï¼å¿
é¡»ä¿è¯å
è°ç¨ v4l2_device_unregister_subdev(sd)ã
æ¤æä½å°ä¼ä»æ¡¥é©±å¨ä¸æ³¨éå设å¤ãå³ä½¿å设å¤æ²¡æ注åï¼è°ç¨æ¤å½æ°ä¹æ¯
å®å
¨çã
å¿
é¡»è¿æ ·åçåå æ¯ï¼å½æ¡¥é©±å¨æ³¨é i2c éé
å¨æ¶ï¼remove()åè°å½æ°
ä¼è¢«é£ä¸ªéé
å¨ä¸ç i2c 设å¤è°ç¨ãæ¤åï¼ç¸åºç v4l2_subdev ç»æä½
å°±ä¸åå¨äºï¼ææå®ä»¬å¿
é¡»å
被注éãå¨ remove()åè°å½æ°ä¸è°ç¨
v4l2_device_unregister_subdev(sd)ï¼å¯ä»¥ä¿è¯æ§è¡æ»æ¯æ£ç¡®çã
桥驱å¨ä¹æä¸äºè¾ ç»å½æ°å¯ç¨ï¼
struct v4l2_subdev *sd = v4l2_i2c_new_subdev(v4l2_dev, adapter,
“module_foo”, “chipid”, 0x36, NULL);
è¿ä¸ªå½æ°ä¼å è½½ç»å®ç模åï¼å¦æ没æ模åéè¦å è½½ï¼å¯ä»¥ä¸º NULLï¼ï¼
并ç¨ç»å®ç i2c éé
å¨ç»æä½æéï¼i2c_adapterï¼å å¨ä»¶å°åï¼chip/addressï¼
ä½ä¸ºåæ°è°ç¨ i2c_new_device()ãå¦æä¸å顺å©ï¼åå°±å¨ v4l2_device
ä¸æ³¨åäºå设å¤ã
ä½ ä¹å¯ä»¥å©ç¨ v4l2_i2c_new_subdev()çæåä¸ä¸ªåæ°ï¼ä¼ éä¸ä¸ªå¯è½ç
I2C å°åæ°ç»ï¼è®©å½æ°èªå¨æ¢æµãè¿äºæ¢æµå°ååªæå¨åä¸ä¸ªåæ°ä¸º 0 ç
æ
åµä¸ä½¿ç¨ãéé¶åæ°æå³çä½ ç¥éåç¡®ç i2c å°åï¼æ以æ¤æ¶æ é¡»è¿è¡
æ¢æµã
å¦æåºéï¼ä¸¤ä¸ªå½æ°é½è¿å NULLã
注æï¼ä¼ éç» v4l2_i2c_new_subdev()ç chipid é常ä¸æ¨¡ååä¸è´ã
å®å
è®¸ä½ æå®ä¸ä¸ªè¯ççåä½ï¼æ¯å¦âsaa7114âæâsaa7115âãä¸è¬éè¿
i2c 驱å¨èªå¨æ¢æµãchipid ç使ç¨æ¯å¨ä»åéè¦æ·±å
¥äºè§£çäºæ
ãè¿ä¸ªä¸
i2c 驱å¨ä¸åï¼è¾å®¹ææ··æ·ãè¦ç¥éæ¯æåªäºè¯çåä½ï¼ä½ å¯ä»¥æ¥é
i2c
驱å¨ä»£ç ç i2c_device_id 表ï¼ä¸é¢ååºäºææå¯è½æ¯æçè¯çã
è¿æä¸¤ä¸ªè¾ å©å½æ°ï¼
v4l2_i2c_new_subdev_cfgï¼è¿ä¸ªå½æ°æ·»å æ°ç irq å platform_data
åæ°ï¼å¹¶æâaddrâåâprobed_addrsâåæ°ï¼å¦æ addr éé¶ï¼å被使ç¨
ï¼ä¸æ¢æµåä½ï¼ï¼å¦å probed_addrs ä¸çå°åå°ç¨äºèªå¨æ¢æµã
ä¾å¦ï¼ä»¥ä¸ä»£ç å°ä¼æ¢æµå°åï¼0x10ï¼ï¼
struct v4l2_subdev *sd = v4l2_i2c_new_subdev_cfg(v4l2_dev, adapter,
“module_foo”, “chipid”, 0, NULL, 0, I2C_ADDRS(0x10));
v4l2_i2c_new_subdev_board 使ç¨ä¸ä¸ª i2c_board_info ç»æä½ï¼å°å
¶
æ¿ä»£ irqãplatform_data å add råæ°ä¼ éç» i2c 驱å¨ã
å¦æå设å¤æ¯æ s_config æ ¸å¿æä½ï¼è¿ä¸ªæä½ä¼å¨å设å¤é
置好ä¹å以 irq å
platform_data 为åæ°è°ç¨ãæ©æç v4l2_i2c_new_(probed_)subdev å½æ°
åæ ·ä¹ä¼è°ç¨ s_configï¼ä½ä»
å¨ irq 为 0 ä¸ platform_data 为 NULL æ¶ã
video_deviceç»æä½
å¨ /dev ç®å½ä¸çå®é
设å¤èç¹æ ¹æ® video_device ç»æä½(v4l2-dev.h)
å建ãæ¤ç»æä½æ¢å¯ä»¥å¨æåé
ä¹å¯ä»¥åµå
¥å°ä¸ä¸ªæ´å¤§çç»æä½ä¸ã
å¨æåé æ¹æ³å¦ä¸ï¼
struct video_device *vdev = video_device_alloc();
if (vdev == NULL)
return -ENOMEM;
vdev->release = video_device_release;
å¦æå°å ¶åµå ¥å°ä¸ä¸ªå¤§ç»æä½ä¸ï¼åå¿ é¡»èªå·±å®ç° release()åè°ã
struct video_device *vdev = &my_vdev->vdev;
vdev->release = my_vdev_release;
release()åè°å¿
须被设置ï¼ä¸å¨æåä¸ä¸ª video_device ç¨æ·éåºä¹å
被è°ç¨ã
é»è®¤ç video_device_release()åè°åªæ¯è°ç¨ kfree æ¥éæ¾ä¹ååé
ç
å
åã
ä½ åºè¯¥è®¾ç½®è¿äºåï¼
v4l2_dev: 设置为 v4l2_device ç¶è®¾å¤ã
name: 设置为å¯ä¸çæè¿°æ§è®¾å¤åã
fops: 设置为已æç v4l2_file_operations ç»æä½ã
ioctl_ops: å¦æä½ ä½¿ç¨v4l2_ioctl_ops æ¥ç®å ioctl çç»´æ¤
(强ç建议使ç¨ï¼ä¸å°æ¥å¯è½å为强å¶æ§ç!)ï¼ç¶åè®¾ç½®ä½ èªå·±ç
v4l2_ioctl_ops ç»æä½.lock: å¦æä½ è¦å¨é©±å¨ä¸å®ç°ææçéæä½ï¼å设为 NULL ãå¦å
å°±è¦è®¾ç½®ä¸ä¸ªæå struct mutex_lock ç»æä½çæéï¼è¿ä¸ªéå°
å¨ unlocked_ioctl æ件æä½è¢«è°ç¨åç±å æ ¸è·å¾ï¼å¹¶å¨è°ç¨è¿åå
éæ¾ã详è§ä¸ä¸èãprio: ä¿æ对ä¼å 级çè·è¸ªãç¨äºå®ç° VIDIOC_G/S_PRIORITYãå¦æ
设置为 NULLï¼åä¼ä½¿ç¨ v4l2_device ä¸ç v4l2_prio_state ç»æä½ã
å¦æè¦å¯¹æ¯ä¸ªè®¾å¤èç¹ï¼ç»ï¼å®ç°ç¬ç«çä¼å 级ï¼å¯ä»¥å°å ¶æåèªå·±
å®ç°ç v4l2_prio_state ç»æä½ãparent: ä» å¨ä½¿ç¨ NULL ä½ä¸ºç¶è®¾å¤ç»æä½åæ°æ³¨å v4l2_device æ¶
设置æ¤åæ°ãåªæå¨ä¸ä¸ªç¡¬ä»¶è®¾å¤å å«å¤ä¸ä¸ª PCI 设å¤ï¼å ±äº«åä¸ä¸ª
v4l2_device æ ¸å¿æ¶æä¼åçãcx88 驱å¨å°±æ¯ä¸ä¸ªä¾åï¼ä¸ä¸ª v4l2_device ç»æä½æ ¸å¿ï¼è¢«ä¸ä¸ªè£¸ç
è§é¢ PCI 设å¤(cx8800)åä¸ä¸ª MPEG PCI 设å¤(cx8802)å ±ç¨ãç±äº
v4l2_device æ æ³ä¸ç¹å®ç PCI 设å¤å ³èï¼ææ没æ设置ç¶è®¾å¤ãä½å½
video_device é ç½®åï¼å°±ç¥é使ç¨åªä¸ªç¶ PCI 设å¤äºãflagsï¼å¯éãå¦æä½ è¦è®©æ¡æ¶å¤ç设置 VIDIOC_G/S_PRIORITY ioctlsï¼
请设置 V4L2_FL_USE_FH_PRIOãè¿è¦æ±ä½ ä½¿ç¨ v4l2_fh ç»æä½ã
ä¸æ¦ææ驱å¨ä½¿ç¨äºæ ¸å¿çä¼å 级å¤çï¼æç»è¿ä¸ªæ å¿å°æ¶å¤±ãä½ç°å¨å®
å¿ é¡»è¢«æ¾å¼è®¾ç½®ã
å¦æä½ ä½¿ç¨ v4l2_ioctl_opsï¼ååºè¯¥å¨ v4l2_file_operations ç»æä½ä¸
设置 .unlocked_ioctl æå video_ioctl2ã
请å¿ä½¿ç¨ .ioctlï¼å®å·²è¢«åºå¼ï¼ä»åå°æ¶å¤±ã
æäºæ
åµä¸ä½ è¦åè¯æ ¸å¿ï¼ä½ å¨ v4l2_ioctl_ops æå®çæ个å½æ°åºè¢«å¿½ç¥ã
ä½ å¯ä»¥å¨ video_device_register 被è°ç¨åéè¿ä»¥ä¸å½æ°æ è®°è¿ä¸ª ioctlsã
void v4l2_disable_ioctl(struct video_device *vdev, unsigned int cmd);
åºäºå¤é¨å ç´ ï¼ä¾å¦æ个æ¿å¡å·²è¢«ä½¿ç¨ï¼ï¼å¨ä¸å建æ°ç»æä½çæ
åµä¸ï¼ä½ æ³
è¦å
³é v4l2_ioctl_ops ä¸æ个ç¹æ§å¾å¾éè¦è¿ä¸ªæºå¶ã
v4l2_file_operations ç»æä½æ¯ file_operations çä¸ä¸ªåéãå
¶ä¸»è¦
åºå«å¨äºï¼å inode åæ°ä»æªè¢«ä½¿ç¨ï¼å®å°è¢«å¿½ç¥ã
å¦æéè¦ä¸åªä½æ¡æ¶æ´åï¼ä½ å¿
é¡»éè¿è°ç¨ media_entity_init() åå§å
åµå
¥å¨ video_device ç»æä½ä¸ç media_entityï¼entity åï¼ç»æä½ï¼
struct media_pad *pad = &my_vdev->pad;
int err;
err = media_entity_init(&vdev->entity, 1, pad, 0);
pads æ°ç»å¿
é¡»é¢å
åå§åã没æå¿
è¦æå¨è®¾ç½® media_entity ç type å
name åã
å½ï¼ä»»ä½ï¼å设å¤èç¹è¢«æå¼/å ³éï¼å¯¹ entity çå¼ç¨å°è¢«èªå¨è·å/éæ¾ã
v4l2_file_operations ä¸é
ä½ å¯ä»¥å¨ video_device ç»æä½ä¸è®¾ç½®ä¸ä¸ªæå mutex_lock çæéãé常
è¿æ¢å¯æ¯ä¸ä¸ªé¡¶å±äºæ¥éä¹å¯ä¸ºè®¾å¤èç¹èªèº«çäºæ¥éãé»è®¤æ
åµä¸ï¼æ¤é
ç¨äº unlocked_ioctlï¼ä½ä¸ºäºä½¿ç¨ ioctls ä½ éè¿ä»¥ä¸å½æ°å¯ç¦ç¨éå®ï¼
void v4l2_disable_ioctl_locking(struct video_device *vdev, unsigned int cmd);
ä¾å¦: v4l2_disable_ioctl_locking(vdev, VIDIOC_DQBUF);
ä½ å¿ é¡»å¨æ³¨å video_device åè°ç¨è¿ä¸ªå½æ°ã
ç¹å«æ¯å¯¹äº USB 驱å¨ç¨åºï¼æäºå½ä»¤ï¼å¦è®¾ç½®æ§å¶ï¼éè¦å¾é¿çæ¶é´ï¼å¯è½
éè¦èªè¡ä¸ºç¼å²åºéåç ioctls å®ç°éå®ã
å¦æä½ éè¦æ´ç»ç²åº¦çéï¼ä½ å¿
须设置 mutex_lock 为 NULLï¼å¹¶å®å
¨èªå·±å®ç°
éæºå¶ã
è¿å®å
¨ç±é©±å¨å¼åè
å³å®ä½¿ç¨ä½ç§æ¹æ³ãç¶èï¼å¦æä½ ç驱å¨åå¨é¿å»¶æ¶æä½
ï¼ä¾å¦ï¼æ¹å USB æå头çæå
æ¶é´å¯è½éè¦è¾é¿æ¶é´ï¼ï¼èä½ åæ³è®©ç¨æ·
å¨çå¾
é¿å»¶æ¶æä½å®ææé´åå
¶ä»çäºï¼åä½ æ好èªå·±å®ç°éæºå¶ã
å¦ææå®ä¸ä¸ªéï¼åææ ioctl æä½å°å¨è¿ä¸ªéçä½ç¨ä¸ä¸²è¡æ§è¡ãå¦æä½
ä½¿ç¨ videobufï¼åå¿
é¡»å°åä¸ä¸ªéä¼ éç» videobuf éååå§åå½æ°ï¼å¦
videobuf å¿
é¡»çå¾
ä¸å¸§çå°è¾¾ï¼åå¯ä¸´æ¶è§£é并å¨è¿ä¹åéæ°ä¸éãå¦æ驱å¨
ä¹å¨ä»£ç æ§è¡æé´çå¾
ï¼åå¯ååæ ·çå·¥ä½ï¼ä¸´æ¶è§£éï¼åä¸éï¼è®©å
¶ä»è¿ç¨
å¯ä»¥å¨ç¬¬ä¸ä¸ªè¿ç¨é»å¡æ¶è®¿é®è®¾å¤èç¹ã
å¨ä½¿ç¨ videobuf2 çæ
åµä¸ï¼å¿
é¡»å®ç° wait_prepare å wait_finish åè°
å¨éå½çæ¶å解é/å éãè¿ä¸æ¥æ¥è¯´ï¼å¦æä½ å¨ video_device ç»æä½ä¸ä½¿ç¨
éï¼åå¿
é¡»å¨ wait_prepare å wait_finish ä¸å¯¹è¿ä¸ªäºæ¥éè¿è¡è§£é/å éã
çææçæå¼å®ç°ä¹å¿ é¡»å¨è°ç¨ v4l2_device_disconnect åè·å¾éã
video_device注å
æ¥ä¸æ¥ä½ éè¦æ³¨åè§é¢è®¾å¤ï¼è¿ä¼ä¸ºä½ å建ä¸ä¸ªå符设å¤ã
err = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
if (err) {
video_device_release(vdev); /* or kfree(my_vdev); */
return err;
}
å¦æ v4l2_device ç¶è®¾å¤ç mdev å为é NULL å¼ï¼è§é¢è®¾å¤å®ä½å°èªå¨
注å为åªä½è®¾å¤ã
注ååªç§è®¾å¤æ¯æ ¹æ®ç±»åï¼typeï¼åæ°ãåå¨ä»¥ä¸ç±»åï¼
VFL_TYPE_GRABBER: ç¨äºè§é¢è¾å
¥/è¾åºè®¾å¤ç videoX
VFL_TYPE_VBI: ç¨äºåç´æ¶éæ°æ®ç vbiX (ä¾å¦ï¼éèå¼åå¹ï¼å¾æçµè§)
VFL_TYPE_RADIO: ç¨äºå¹¿æè°è°å¨ç radioX
æåä¸ä¸ªåæ°è®©ä½ ç¡®å®ä¸ä¸ªææ§å¶è®¾å¤ç设å¤èç¹å·æ°é(ä¾å¦ videoX ä¸ç X)ã
éå¸¸ä½ å¯ä»¥ä¼ å
¥-1ï¼è®© v4l2 æ¡æ¶èªå·±éæ©ç¬¬ä¸ä¸ªç©ºé²çç¼å·ãä½æ¯ææ¶ç¨æ·
éè¦éæ©ä¸ä¸ªç¹å®çèç¹å·ã驱å¨å
许ç¨æ·éè¿é©±å¨æ¨¡ååæ°éæ©ä¸ä¸ªç¹å®ç
设å¤èç¹å·æ¯å¾æ®éçãè¿ä¸ªç¼å·å°ä¼ä¼ éç»è¿ä¸ªå½æ°ï¼ä¸ video_register_device
å°ä¼è¯å¾éæ©è¿ä¸ªè®¾å¤èç¹å·ãå¦æè¿ä¸ªç¼å·è¢«å ç¨ï¼ä¸ä¸ä¸ªç©ºé²ç设å¤èç¹
ç¼å·å°è¢«éä¸ï¼å¹¶åå
æ ¸æ¥å¿ä¸åéä¸ä¸ªè¦åä¿¡æ¯ã
å¦ä¸ä¸ªä½¿ç¨åºæ¯æ¯å½é©±å¨å建å¤ä¸ªè®¾å¤æ¶ãè¿ç§æ
åµä¸ï¼å¯¹ä¸åçè§é¢è®¾å¤å¨
ç¼å·ä¸ä½¿ç¨ä¸åçèå´æ¯å¾æç¨çãä¾å¦ï¼è§é¢æè·è®¾å¤ä» 0 å¼å§ï¼è§é¢
è¾åºè®¾å¤ä» 16 å¼å§ãæä»¥ä½ å¯ä»¥ä½¿ç¨æåä¸ä¸ªåæ°æ¥æå®è®¾å¤èç¹å·æå°å¼ï¼
è v4l2 æ¡æ¶ä¼è¯å¾éæ©ç¬¬ä¸ä¸ªç空é²ç¼å·ï¼çäºæ大äºä½ æä¾çç¼å·ï¼ã
å¦æ失败ï¼åå®ä¼å°±éæ©ç¬¬ä¸ä¸ªç©ºé²çç¼å·ã
ç±äºè¿ç§æ
åµä¸ï¼ä½ ä¼å¿½ç¥æ æ³éæ©ç¹å®è®¾å¤èç¹å·çè¦åï¼åå¯è°ç¨
video_register_device_no_warn() å½æ°é¿å
è¦åä¿¡æ¯ç产çã
åªè¦è®¾å¤èç¹è¢«å建ï¼ä¸äºå±æ§ä¹ä¼åæ¶å建ãå¨ /sys/class/video4linux
ç®å½ä¸ä½ ä¼æ¾å°è¿äºè®¾å¤ãä¾å¦è¿å
¥å
¶ä¸ç video0 ç®å½ï¼ä½ ä¼çå°ânameâå
âindexâå±æ§ãânameâå±æ§å¼å°±æ¯ video_device ç»æä½ä¸çânameâåã
âindexâå±æ§å¼å°±æ¯è®¾å¤èç¹çç´¢å¼å¼ï¼æ¯æ¬¡è°ç¨ video_register_device()ï¼
ç´¢å¼å¼é½éå¢ 1 ã第ä¸ä¸ªè§é¢è®¾å¤èç¹æ»æ¯ä»ç´¢å¼å¼ 0 å¼å§ã
ç¨æ·å¯ä»¥è®¾ç½® udev è§åï¼å©ç¨ç´¢å¼å±æ§çæè±å¨ç设å¤åï¼ä¾å¦ï¼ç¨âmpegXâ
代表 MPEG è§é¢æè·è®¾å¤èç¹ï¼ã
å¨è®¾å¤æå注ååï¼å°±å¯ä»¥ä½¿ç¨è¿äºåï¼
- vfl_type: ä¼ éç» video_register_device ç设å¤ç±»åã
- minor: å·²ææ´¾ç次设å¤å·ã
- num: 设å¤èç¹ç¼å· (ä¾å¦ videoX ä¸ç X)ã
- index: 设å¤ç´¢å¼å·ã
å¦æ注å失败ï¼ä½ å¿
é¡»è°ç¨ video_device_release() æ¥éæ¾å·²åé
ç
video_device ç»æä½ï¼å¦æ video_device æ¯åµå
¥å¨èªå·±å建çç»æä½ä¸ï¼
ä½ ä¹å¿
é¡»éæ¾å®ãvdev->release() åè°ä¸ä¼å¨æ³¨å失败ä¹å被è°ç¨ï¼
ä½ ä¹ä¸åºè¯å¾å¨æ³¨å失败å注é设å¤ã
video_device 注é
å½è§é¢è®¾å¤èç¹å·²è¢«ç§»é¤ï¼ä¸è®ºæ¯å¸è½½é©±å¨è¿æ¯USB设å¤æå¼ï¼ä½ é½åºæ³¨é
å®ä»¬ï¼
video_unregister_device(vdev);
è¿ä¸ªæä½å°ä» sysfs ä¸ç§»é¤è®¾å¤èç¹ï¼å¯¼è´ udev å°å ¶ä» /dev ä¸ç§»é¤ï¼ã
video_unregister_device() è¿åä¹åï¼å°±æ æ³å®ææå¼æä½ã尽管å¦æ¤ï¼
USB 设å¤çæ
åµåä¸åï¼æäºåºç¨ç¨åºå¯è½ä¾ç¶æå¼çå
¶ä¸ä¸ä¸ªå·²æ³¨é设å¤
èç¹ãæ以å¨æ³¨éä¹åï¼æææ件æä½ï¼å½ç¶é¤äº release ï¼ä¹åºè¿åé误å¼ã
å½æåä¸ä¸ªè§é¢è®¾å¤èç¹çç¨æ·éåºï¼å vdev->release() åè°ä¼è¢«è°ç¨ï¼
并ä¸ä½ å¯ä»¥åæåçæ¸
çæä½ã
ä¸è¦å¿è®°æ¸ çä¸è§é¢è®¾å¤ç¸å ³çåªä½å ¥å£ï¼å¦æ被åå§åè¿ï¼ï¼
media_entity_cleanup(&vdev->entity);
è¿å¯ä»¥å¨ release åè°ä¸å®æã
video_device è¾ å©å½æ°
ä¸äºæç¨çè¾ å©å½æ°å¦ä¸ï¼
- file/video_device ç§ææ°æ®
ä½ å¯ä»¥ç¨ä»¥ä¸å½æ°å¨ video_device ç»æä½ä¸è®¾ç½®/è·å驱å¨ç§ææ°æ®ï¼
void *video_get_drvdata(struct video_device *vdev);
void video_set_drvdata(struct video_device *vdev, void *data);
注æï¼å¨è°ç¨ video_register_device() åæ§è¡ video_set_drvdata()
æ¯å®å
¨çã
è以ä¸å½æ°ï¼
struct video_device *video_devdata(struct file *file);
è¿å file ç»æä½ä¸æ¥æçç video_device æéã
video_drvdata è¾
å©å½æ°ç»åäº video_get_drvdata å video_devdata
çåè½ï¼
void *video_drvdata(struct file *file);
ä½ å¯ä»¥ä½¿ç¨å¦ä¸ä»£ç ä» video_device ç»æä½ä¸è·å v4l2_device ç»æä½
æéï¼
struct v4l2_device *v4l2_dev = vdev->v4l2_dev;
- 设å¤èç¹å
video_device 设å¤èç¹å¨å æ ¸ä¸çå称å¯ä»¥éè¿ä»¥ä¸å½æ°è·å¾
const char *video_device_node_name(struct video_device *vdev);
è¿ä¸ªåå被ç¨æ·ç©ºé´å·¥å
·ï¼ä¾å¦ udevï¼ä½ä¸ºæ示信æ¯ä½¿ç¨ãåºå°½å¯è½ä½¿ç¨
æ¤åè½ï¼èéè®¿é® video_device::num å video_device::minor åã
è§é¢ç¼å²è¾ å©å½æ°
v4l2 æ ¸å¿ API æä¾äºä¸ä¸ªå¤çè§é¢ç¼å²çæ åæ¹æ³(称为âvideobufâ)ã
è¿äºæ¹æ³ä½¿é©±å¨å¯ä»¥éè¿ç»ä¸çæ¹å¼å®ç° read()ãmmap() å overlay()ã
ç®åå¨è®¾å¤ä¸æ¯æè§é¢ç¼å²çæ¹æ³æåæ£/èé DMA(videobuf-dma-sg)ã
çº¿æ§ DMA(videobuf-dma-contig)以å大å¤ç¨äº USB 设å¤çç¨ vmalloc
åé
çç¼å²(videobuf-vmalloc)ã
请åé
Documentation/video4linux/videobufï¼ä»¥è·å¾æ´å¤å
³äº videobuf
å±ç使ç¨ä¿¡æ¯ã
v4l2_fh ç»æä½
v4l2_fh ç»æä½æä¾ä¸ä¸ªä¿åç¨äº V4L2 æ¡æ¶çæ件å¥æç¹å®æ°æ®çç®åæ¹æ³ã
å¦æ video_device ç flag è®¾ç½®äº V4L2_FL_USE_FH_PRIO æ å¿ï¼æ°é©±å¨
å¿
é¡»ä½¿ç¨ v4l2_fh ç»æä½ï¼å 为å®ä¹ç¨äºå®ç°ä¼å
级å¤çï¼VIDIOC_G/S_PRIORITYï¼ã
v4l2_fh çç¨æ·ï¼ä½äº V4l2 æ¡æ¶ä¸ï¼å¹¶é驱å¨ï¼å¯éè¿æµè¯
video_device->flags ä¸ç V4L2_FL_USES_V4L2_FH ä½å¾ç¥é©±å¨æ¯å¦ä½¿ç¨
v4l2_fh ä½ä¸ºä»ç file->private_data æéãè¿ä¸ªä½ä¼å¨è°ç¨ v4l2_fh_init()
æ¶è¢«è®¾ç½®ã
v4l2_fh ç»æä½ä½ä¸ºé©±å¨èªèº«æ件å¥æç»æä½çä¸é¨å被åé
ï¼ä¸é©±å¨å¨
å
¶æå¼å½æ°ä¸å° file->private_data æåå®ã
å¨è®¸å¤æ
åµä¸ï¼v4l2_fh ç»æä½ä¼åµå
¥å°ä¸ä¸ªæ´å¤§çç»æä½ä¸ãè¿éæ
åµä¸ï¼
åºè¯¥å¨ open() ä¸è°ç¨ v4l2_fh_init+v4l2_fh_addï¼å¹¶å¨ release() ä¸
è°ç¨ v4l2_fh_del+v4l2_fh_exitã
驱å¨å¯ä»¥éè¿ä½¿ç¨ container_of å®æåä»ä»¬èªå·±çæ件å¥æç»æä½ãä¾å¦ï¼
struct my_fh {
int blah;
struct v4l2_fh fh;
};
…
int my_open(struct file *file)
{
struct my_fh *my_fh;
struct video_device *vfd;
int ret;
...
my_fh = kzalloc(sizeof(*my_fh), GFP_KERNEL);
...
v4l2_fh_init(&my_fh->fh, vfd);
...
file->private_data = &my_fh->fh;
v4l2_fh_add(&my_fh->fh);
return 0;
}
int my_release(struct file *file)
{
struct v4l2_fh *fh = file->private_data;
struct my_fh *my_fh = container_of(fh, struct my_fh, fh);
...
v4l2_fh_del(&my_fh->fh);
v4l2_fh_exit(&my_fh->fh);
kfree(my_fh);
return 0;
}
以ä¸æ¯ v4l2_fh å½æ°ä½¿ç¨çç®ä»ï¼
void v4l2_fh_init(struct v4l2_fh *fh, struct video_device *vdev)
åå§åæ件å¥æãè¿å¿
é¡»å¨é©±å¨ç v4l2_file_operations->open()
å½æ°ä¸æ§è¡ã
void v4l2_fh_add(struct v4l2_fh *fh)
æ·»å ä¸ä¸ª v4l2_fh å° video_device æ件å¥æå表ãä¸æ¦æ件å¥æ
åå§åå®æå°±å¿
é¡»è°ç¨ã
void v4l2_fh_del(struct v4l2_fh *fh)
ä» video_device() ä¸è§£é¤æ件å¥æçå
³èãæ件å¥æçéåºå½æ°ä¹
å°è¢«è°ç¨ã
void v4l2_fh_exit(struct v4l2_fh *fh)
æ¸ çæ件å¥æãå¨æ¸ çå® v4l2_fh åï¼ç¸å ³å åä¼è¢«éæ¾ã
å¦æ v4l2_fh ä¸æ¯åµå ¥å¨å ¶ä»ç»æä½ä¸çï¼åå¯ä»¥ç¨è¿äºè¾ å©å½æ°ï¼
int v4l2_fh_open(struct file *filp)
åé
ä¸ä¸ª v4l2_fh ç»æä½ç©ºé´ï¼åå§å并å°å
¶æ·»å å° file ç»æä½ç¸å
³ç
video_device ç»æä½ä¸ã
int v4l2_fh_release(struct file *filp)
ä» file ç»æä½ç¸å
³ç video_device ç»æä½ä¸å é¤ v4l2_fh ï¼æ¸
ç
v4l2_fh 并éæ¾ç©ºé´ã
è¿ä¸¤ä¸ªå½æ°å¯ä»¥æå
¥å° v4l2_file_operation ç open() å release()
æä½ä¸ã
æäºé©±å¨éè¦å¨ç¬¬ä¸ä¸ªæ件å¥ææå¼åæåä¸ä¸ªæ件å¥æå
³éçæ¶ååäº
å·¥ä½ãæ以å å
¥äºä¸¤ä¸ªè¾
å©å½æ°ä»¥æ£æ¥ v4l2_fh ç»æä½æ¯å¦æ¯ç¸å
³è®¾å¤
èç¹æå¼çå¯ä¸æ件å¥æã
int v4l2_fh_is_singular(struct v4l2_fh *fh)
å¦ææ¤æ件å¥ææ¯å¯ä¸æå¼çæ件å¥æï¼åè¿å 1 ï¼å¦åè¿å 0 ã
int v4l2_fh_is_singular_file(struct file *filp)
åè½ç¸åï¼ä½éè¿ filp->private_data è°ç¨ v4l2_fh_is_singularã
V4L2 äºä»¶æºå¶
V4L2 äºä»¶æºå¶æä¾äºä¸ä¸ªéç¨çæ¹æ³å°äºä»¶ä¼ éå°ç¨æ·ç©ºé´ã驱å¨å¿
须使ç¨
v4l2_fh æè½æ¯æ V4L2 äºä»¶æºå¶ã
äºä»¶éè¿ä¸ä¸ªç±»ååéæ© ID æ¥å®ä¹ãID 对åºä¸ä¸ª V4L2 对象ï¼ä¾å¦
ä¸ä¸ªæ§å¶ IDãå¦ææªä½¿ç¨ï¼å ID 为 0ã
å½ç¨æ·è®¢é
ä¸ä¸ªäºä»¶ï¼é©±å¨ä¼ä¸ºæ¤åé
ä¸äº kevent ç»æä½ãæ以æ¯ä¸ª
äºä»¶ç»ï¼ç±»åãIDï¼é½ä¼æèªå·±çä¸å¥ kevent ç»æä½ãè¿ä¿è¯äºå¦æ
ä¸ä¸ªé©±å¨çæ¶é´å
产çäºè®¸å¤åç±»äºä»¶ï¼ä¸ä¼è¦çå
¶ä»ç±»åçäºä»¶ã
ä½å¦æä½ æ¶å°çäºä»¶æ°é大äºåç±»äºä»¶ kevent çä¿åæ°éï¼åææ©ç
äºä»¶å°è¢«ä¸¢å¼ï¼å¹¶å å
¥æ°äºä»¶ã
æ¤å¤ï¼v4l2_subscribed_event ç»æä½å
é¨æå¯ä¾é©±å¨è®¾ç½®ç merge() å
replace() åè°ï¼è¿äºåè°ä¼å¨æ°äºä»¶äº§çä¸æ²¡æå¤ä½ç©ºé´çæ¶å被è°ç¨ã
replace() åè°è®©ä½ å¯ä»¥å°æ©æäºä»¶çåè·æ¿æ¢ä¸ºæ°äºä»¶çåè·ï¼å°æ©æ
åè·çç¸å
³æ°æ®å并å°æ¿æ¢è¿æ¥çæ°åè·ä¸ãå½è¯¥ç±»åçäºä»¶ä»
åé
äºä¸ä¸ª
kevent ç»æä½æ¶ï¼å®å°è¢«è°ç¨ãmerge() åè°è®©ä½ å¯ä»¥å并ææ©çäºä»¶åè·
å°å¨å®ä¹åçé£ä¸ªäºä»¶åè·ä¸ãå½è¯¥ç±»åçäºä»¶åé
äºä¸¤ä¸ªææ´å¤ kevent
ç»æä½æ¶ï¼å®å°è¢«è°ç¨ã
è¿ç§æ¹æ³ä¸ä¼æç¶æä¿¡æ¯ä¸¢å¤±ï¼åªä¼å¯¼è´ä¸é´æ¥éª¤ä¿¡æ¯ä¸¢å¤±ã
å
³äº replace/merge åè°çä¸ä¸ªä¸éçä¾åå¨ v4l2-event.c ä¸ï¼ç¨äº
æ§å¶äºä»¶ç ctrls_replace() å ctrls_merge() åè°ã
注æï¼è¿äºåè°å¯ä»¥å¨ä¸æä¸ä¸æä¸è°ç¨ï¼æ以å®ä»¬å¿ 须尽快å®æ并éåºã
æç¨çå½æ°ï¼
void v4l2_event_queue(struct video_device *vdev, const struct v4l2_event *ev)
å°äºä»¶å å
¥è§é¢è®¾å¤çéåã驱å¨ä»
è´è´£å¡«å
type å data åã
å
¶ä»åç± V4L2 å¡«å
ã
int v4l2_event_subscribe(struct v4l2_fh *fh,
struct v4l2_event_subscription *sub, unsigned elems,
const struct v4l2_subscribed_event_ops *ops)
video_device->ioctl_ops->vidioc_subscribe_event å¿
é¡»æ£æµé©±å¨è½
产çç¹å® id çäºä»¶ãç¶åè°ç¨ v4l2_event_subscribe() æ¥è®¢é
该äºä»¶ã
elems åæ°æ¯è¯¥äºä»¶çéå大å°ãè¥ä¸º 0ï¼V4L2 æ¡æ¶å°ä¼ï¼æ ¹æ®äºä»¶ç±»åï¼
å¡«å
é»è®¤å¼ã
ops åæ°å 许驱å¨æå®ä¸ç³»ååè°ï¼
- add: å½æ·»å ä¸ä¸ªæ°çå¬è
æ¶è°ç¨ï¼éå¤è®¢é
åä¸ä¸ªäºä»¶ï¼æ¤åè°
ä» è¢«æ§è¡ä¸æ¬¡ï¼ã
- del: å½ä¸ä¸ªçå¬è åæ¢çå¬æ¶è°ç¨ã
- replace: ç¨âæ°âäºä»¶æ¿æ¢âæ©æâäºä»¶ã
- merge: å°âæ©æâäºä»¶å并å°âæ°âäºä»¶ä¸ã
è¿å个è°ç¨é½æ¯å¯éçï¼å¦æä¸æ³æå®ä»»ä½åè°ï¼å ops å¯ä¸º NULLã
int v4l2_event_unsubscribe(struct v4l2_fh *fh,
struct v4l2_event_subscription *sub)
v4l2_ioctl_ops ç»æä½ä¸ç vidioc_unsubscribe_event åè°å½æ°ã
驱å¨ç¨åºå¯ä»¥ç´æ¥ä½¿ç¨ v4l2_event_unsubscribe() å®ç°é订äºä»¶è¿ç¨ã
ç¹æ®ç V4L2_EVENT_ALL ç±»åï¼å¯ç¨äºé订ææäºä»¶ã驱å¨å¯è½å¨ç¹æ®
æ
åµä¸éè¦åæ¤æä½ã
int v4l2_event_pending(struct v4l2_fh *fh)
è¿åæªå³äºä»¶çæ°éãæå©äºå®ç°è½®è¯¢ï¼pollï¼æä½ã
äºä»¶éè¿ poll ç³»ç»è°ç¨ä¼ éå°ç¨æ·ç©ºé´ã驱å¨å¯ç¨
v4l2_fh->wait (wait_queue_head_t ç±»å)ä½ä¸ºåæ°è°ç¨ poll_wait()ã
äºä»¶å为æ åäºä»¶åç§æäºä»¶ãæ°çæ åäºä»¶å¿
须使ç¨å¯ç¨çæå°äºä»¶ç±»å
ç¼å·ã驱å¨å¿
é¡»ä»ä»ä»¬æ¬ç±»åçç¼å·èµ·å§å¤åé
äºä»¶ãç±»åçç¼å·èµ·å§ä¸º
V4L2_EVENT_PRIVATE_START + n * 1000 ï¼å
¶ä¸ n 为å¯ç¨æå°ç¼å·ãæ¯ä¸ª
ç±»åä¸ç第ä¸ä¸ªäºä»¶ç±»åç¼å·æ¯ä¸ºä»¥åç使ç¨ä¿ççï¼æ以第ä¸ä¸ªå¯ç¨äºä»¶
ç±»åç¼å·æ¯âclass base + 1âã
V4L2 äºä»¶æºå¶ç使ç¨å®ä¾å¯ä»¥å¨ OMAP3 ISP ç驱å¨
(drivers/media/video/omap3isp)ä¸æ¾å°ã