uvc攝像頭程式碼解析6
阿新 • • 發佈:2019-02-05
10.掃描視訊裝置鏈和註冊視訊裝置
10.1 uvc視訊鏈
struct uvc_video_chain { //uvc視訊鏈 struct uvc_device *dev; //uvc裝置 struct list_head list; //uvc視訊鏈連結串列頭 struct list_head entities; //uvc實體連結串列頭 struct uvc_entity *processing; //處理Unit實體 struct uvc_entity *selector; //選擇器Unit實體 struct mutex ctrl_mutex; /* Protects ctrl.info */ };
10.2 uvc掃描裝置
static int uvc_scan_device(struct uvc_device *dev) { struct uvc_video_chain *chain; //uvc視訊鏈 struct uvc_entity *term; //uvc實體 list_for_each_entry(term, &dev->entities, list) { //遍歷全域性實體連結串列 if (!UVC_ENTITY_IS_OTERM(term)) //獲取實體連結串列中的輸出Terminal實體 continue; if (term->chain.next || term->chain.prev) //已經新增到uvc視訊鏈中了 continue; chain = kzalloc(sizeof(*chain), GFP_KERNEL); //分配uvc視訊鏈記憶體(有多少個輸入Terminal就有多少個uvc_video_chain) if (chain == NULL) return -ENOMEM; INIT_LIST_HEAD(&chain->entities); //初始化視訊鏈entities(實體)連結串列 mutex_init(&chain->ctrl_mutex); chain->dev = dev; //捆綁uvc視訊鏈和uvc裝置 if (uvc_scan_chain(chain, term) < 0) { //掃描uvc視訊鏈(處理所有相關的輸入pin) kfree(chain); continue; } uvc_trace(UVC_TRACE_PROBE, "Found a valid video chain (%s).\n",uvc_print_chain(chain)); list_add_tail(&chain->list, &dev->chains); //新增到uvc裝置的uvc視訊鏈連結串列 } if (list_empty(&dev->chains)) { uvc_printk(KERN_INFO, "No valid video chain found.\n"); return -1; } return 0; }
10.3 uvc掃描視訊鏈
static int uvc_scan_chain(struct uvc_video_chain *chain,struct uvc_entity *term) { struct uvc_entity *entity, *prev; uvc_trace(UVC_TRACE_PROBE, "Scanning UVC chain:"); entity = term; //獲取實體 prev = NULL; //前一個實體 while (entity != NULL) { /* Entity must not be part of an existing chain */ if (entity->chain.next || entity->chain.prev) { //已經新增到uvc視訊鏈中了 uvc_trace(UVC_TRACE_DESCR, "Found reference to entity %d already in chain.\n", entity->id); return -EINVAL; } /* Process entity */ if (uvc_scan_chain_entity(chain, entity) < 0) //掃描當前實體 return -EINVAL; /* Forward scan */ if (uvc_scan_chain_forward(chain, entity, prev) < 0) //向前掃描實體 return -EINVAL; /* Backward scan */ prev = entity; //當前實體作為下一次while迴圈的前一個實體 if (uvc_scan_chain_backward(chain, &entity) < 0) //向後掃描實體 return -EINVAL; } return 0; }
將uvc視訊鏈的輸入實體新增到uvc視訊鏈的entities連結串列中
將uvc視訊鏈新增到uvc裝置的chains連結串列中
10.3.1 掃描當前實體
static int uvc_scan_chain_entity(struct uvc_video_chain *chain,struct uvc_entity *entity)
{
switch (UVC_ENTITY_TYPE(entity)) {
case UVC_VC_EXTENSION_UNIT: //擴充套件Unit
if (uvc_trace_param & UVC_TRACE_PROBE)
printk(" <- XU %d", entity->id);
if (entity->bNrInPins != 1) {
uvc_trace(UVC_TRACE_DESCR, "Extension unit %d has more than 1 input pin.\n", entity->id);
return -1;
}
break;
case UVC_VC_PROCESSING_UNIT: //處理Unit
if (uvc_trace_param & UVC_TRACE_PROBE)
printk(" <- PU %d", entity->id);
if (chain->processing != NULL) {
uvc_trace(UVC_TRACE_DESCR, "Found multiple Processing Units in chain.\n");
return -1;
}
chain->processing = entity; //如果是處理Unit則設定其為uvc視訊鏈的processing物件
break;
case UVC_VC_SELECTOR_UNIT: //選擇器Unit
if (uvc_trace_param & UVC_TRACE_PROBE)
printk(" <- SU %d", entity->id);
/* Single-input selector units are ignored. */
if (entity->bNrInPins == 1)
break;
if (chain->selector != NULL) {
uvc_trace(UVC_TRACE_DESCR, "Found multiple Selector Units in chain.\n");
return -1;
}
chain->selector = entity; //如果是選擇器Unit則設定其為uvc視訊鏈的selector物件
break;
case UVC_ITT_VENDOR_SPECIFIC: //廠商特殊
case UVC_ITT_CAMERA: //輸入Terminal camera
case UVC_ITT_MEDIA_TRANSPORT_INPUT: //輸入Terminal Media transport
if (uvc_trace_param & UVC_TRACE_PROBE)
printk(" <- IT %d\n", entity->id);
break;
case UVC_TT_STREAMING: //輸入Terminal stream
if (UVC_ENTITY_IS_ITERM(entity)) {
if (uvc_trace_param & UVC_TRACE_PROBE)
printk(" <- IT %d\n", entity->id);
}
else {
if (uvc_trace_param & UVC_TRACE_PROBE)
printk(" OT %d", entity->id);
}
break;
default:
uvc_trace(UVC_TRACE_DESCR, "Unsupported entity type 0x%04x found in chain.\n", UVC_ENTITY_TYPE(entity));
return -1;
}
list_add_tail(&entity->chain, &chain->entities); //新增到uvc視訊鏈的實體連結串列
return 0;
}
10.3.2 向前掃描實體
static int uvc_scan_chain_forward(struct uvc_video_chain *chain,struct uvc_entity *entity, struct uvc_entity *prev)
{
struct uvc_entity *forward;
int found;
/* Forward scan */
forward = NULL;
found = 0;
while (1) { //獲取實體前面的所以實體處理直到前面的實體forward=NULL為止跳出死迴圈
forward = uvc_entity_by_reference(chain->dev, entity->id,forward); //獲取前一個實體
if (forward == NULL)
break;
if (forward == prev)
continue;
switch (UVC_ENTITY_TYPE(forward)) {
case UVC_VC_EXTENSION_UNIT: //擴充套件Unit
if (forward->bNrInPins != 1) {
uvc_trace(UVC_TRACE_DESCR, "Extension unit %d has more than 1 input pin.\n",entity->id);
return -EINVAL;
}
list_add_tail(&forward->chain, &chain->entities); //新增uvc實體到uvc視訊鏈的entities中
if (uvc_trace_param & UVC_TRACE_PROBE) {
if (!found)
printk(" (->");
printk(" XU %d", forward->id);
found = 1;
}
break;
case UVC_OTT_VENDOR_SPECIFIC: //廠商特殊
case UVC_OTT_DISPLAY: //輸出Termianl display
case UVC_OTT_MEDIA_TRANSPORT_OUTPUT: //輸出Terminal media transport
case UVC_TT_STREAMING: //輸出Terminal stream
if (UVC_ENTITY_IS_ITERM(forward)) {
uvc_trace(UVC_TRACE_DESCR, "Unsupported input terminal %u.\n", forward->id);
return -EINVAL;
}
list_add_tail(&forward->chain, &chain->entities); //新增uvc實體到uvc視訊鏈的entities中
if (uvc_trace_param & UVC_TRACE_PROBE) {
if (!found)
printk(" (->");
printk(" OT %d", forward->id);
found = 1;
}
break;
}
}
if (found)
printk(")");
return 0;
}
10.3.3 向後掃描實體
static int uvc_scan_chain_backward(struct uvc_video_chain *chain,struct uvc_entity **_entity)
{
struct uvc_entity *entity = *_entity;
struct uvc_entity *term;
int id = -EINVAL, i;
switch (UVC_ENTITY_TYPE(entity)) {
case UVC_VC_EXTENSION_UNIT: //擴充套件Unit
case UVC_VC_PROCESSING_UNIT: //處理Unit處理Unit的輸入Terminal個數只能為1
id = entity->baSourceID[0]; //獲取輸入pin(Unit/Terminal)的ID
break;
case UVC_VC_SELECTOR_UNIT: //選擇器實體
/* Single-input selector units are ignored. */
if (entity->bNrInPins == 1) { //若輸入pin個數為1
id = entity->baSourceID[0]; //獲取輸入in(Unit/Terminal)的ID
break;
}
if (uvc_trace_param & UVC_TRACE_PROBE)
printk(" <- IT");
chain->selector = entity; //uvc視訊鏈的selector物件指向uvc實體
for (i = 0; i < entity->bNrInPins; ++i) { //總共有多少個輸入pin
id = entity->baSourceID[i]; //獲取輸入in(Unit/Terminal)的ID
term = uvc_entity_by_id(chain->dev, id); //獲取對應的輸入pin實體
if (term == NULL || !UVC_ENTITY_IS_ITERM(term)) {
uvc_trace(UVC_TRACE_DESCR, "Selector unit %d input %d isn't connected to an input terminal\n", entity->id, i);
return -1;
}
if (uvc_trace_param & UVC_TRACE_PROBE)
printk(" %d", term->id);
list_add_tail(&term->chain, &chain->entities); //新增uvc實體到uvc視訊鏈的entities連結串列
uvc_scan_chain_forward(chain, term, entity); //向前掃描實體
}
if (uvc_trace_param & UVC_TRACE_PROBE)
printk("\n");
id = 0;
break;
case UVC_ITT_VENDOR_SPECIFIC:
case UVC_ITT_CAMERA:
case UVC_ITT_MEDIA_TRANSPORT_INPUT:
case UVC_OTT_VENDOR_SPECIFIC:
case UVC_OTT_DISPLAY:
case UVC_OTT_MEDIA_TRANSPORT_OUTPUT:
case UVC_TT_STREAMING:
id = UVC_ENTITY_IS_OTERM(entity) ? entity->baSourceID[0] : 0;
break;
}
if (id <= 0) {
*_entity = NULL;
return id;
}
entity = uvc_entity_by_id(chain->dev, id);
if (entity == NULL) {
uvc_trace(UVC_TRACE_DESCR, "Found reference to unknown entity %d.\n", id);
return -EINVAL;
}
*_entity = entity;
return 0;
}
注意到trace列印的語句會發現有一條
uvcvideo: Scanning UVC chain: OT 2 <- XU 5 <- XU 4 <- PU 3 <- IT 1
可以看到這些Unit和Terminal是如何組建起來的
這裡補充一下:
1.開啟trace:echo 0xffff > /sys/module/uvcvideo/par 訊息用dmesg檢視,清除dmesg資訊帶上-c引數就行
2.留意之前lsusb打印出來的描述符表,對應的bTerminalID就是trace列印資訊中對應的Unit或Terminal的數字,而baSourceID則是它的前一級Unit或Terminal的ID號
10.3.4 新增連結串列
list_add_tail(&entity->chain, &chain->entities);
11.註冊uvc視訊鏈
11.1 uvc註冊視訊鏈
static int uvc_register_chains(struct uvc_device *dev)
{
struct uvc_video_chain *chain;
int ret;
list_for_each_entry(chain, &dev->chains, list) { //遍歷uvc裝置的uvc視訊鏈連結串列
ret = uvc_register_terms(dev, chain); //註冊uvc視訊鏈
if (ret < 0)
return ret;
}
return 0;
}
11.2 uvc註冊實體
static int uvc_register_terms(struct uvc_device *dev,struct uvc_video_chain *chain)
{
struct uvc_streaming *stream;
struct uvc_entity *term;
int ret;
list_for_each_entry(term, &chain->entities, chain) { //遍歷uvc視訊鏈的uvc實體連結串列
if (UVC_ENTITY_TYPE(term) != UVC_TT_STREAMING) //不是輸入Terminal streaming型別
continue;
stream = uvc_stream_by_id(dev, term->id); //獲取uvc視訊流
if (stream == NULL) {
uvc_printk(KERN_INFO, "No streaming interface found for terminal %u.", term->id);
continue;
}
stream->chain = chain; //捆綁uvc視訊流和uvc視訊鏈
ret = uvc_register_video(dev, stream); //註冊uvc視訊流
if (ret < 0)
return ret;
}
return 0;
}
11.3 uvc註冊視訊
static int uvc_register_video(struct uvc_device *dev,struct uvc_streaming *stream)
{
struct video_device *vdev;
int ret;
/* Initialize the streaming interface with default streaming parameters.*/
ret = uvc_video_init(stream); //13.uvc視訊初始化
if (ret < 0) {
uvc_printk(KERN_ERR, "Failed to initialize the device (%d).\n", ret);
return ret;
}
/* Register the device with V4L. */
vdev = video_device_alloc(); //分配v4l2裝置記憶體
if (vdev == NULL) {
uvc_printk(KERN_ERR, "Failed to allocate video device (%d).\n",ret);
return -ENOMEM;
}
vdev->parent = &dev->intf->dev; //v4l2裝置的父裝置為usb介面裝置
vdev->fops = &uvc_fops; //v4l2操作函式集
vdev->release = uvc_release; //釋放方法
strlcpy(vdev->name, dev->name, sizeof vdev->name); //設定名字
stream->vdev = vdev; //捆綁uvc視訊流和v4l2裝置
video_set_drvdata(vdev, stream); //將uvc視訊流作為v4l2裝置的驅動資料
ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); //註冊v4l2裝置
if (ret < 0) {
uvc_printk(KERN_ERR, "Failed to register video device (%d).\n",ret);
stream->vdev = NULL;
video_device_release(vdev);
return ret;
}
atomic_inc(&dev->nstreams);
return 0;
}
12.uvc裝置狀態初始化
uvc狀態的處理由中斷端點來控制處理
int uvc_status_init(struct uvc_device *dev)
{
struct usb_host_endpoint *ep = dev->int_ep; //獲取usb_host_endpoint
unsigned int pipe;
int interval;
if (ep == NULL)
return 0;
uvc_input_init(dev); //初始化uvc輸入裝置
dev->status = kzalloc(UVC_MAX_STATUS_SIZE, GFP_KERNEL); //分配uvc裝置狀態記憶體
if (dev->status == NULL)
return -ENOMEM;
dev->int_urb = usb_alloc_urb(0, GFP_KERNEL); //分配urb
if (dev->int_urb == NULL) {
kfree(dev->status);
return -ENOMEM;
}
pipe = usb_rcvintpipe(dev->udev, ep->desc.bEndpointAddress); //中斷輸入端點
interval = ep->desc.bInterval; //獲取間隔
if (interval > 16 && dev->udev->speed == USB_SPEED_HIGH &&(dev->quirks & UVC_QUIRK_STATUS_INTERVAL)) //高速裝置
interval = fls(interval) - 1;
usb_fill_int_urb(dev->int_urb, dev->udev, pipe,dev->status, UVC_MAX_STATUS_SIZE, uvc_status_complete,dev, interval); //填充中斷urb
return 0;
}
這裡只填充了urb資訊,urb的提交請參看14.2.2.1 uvc_status_start啟動狀態,在開啟uvc的V4L2裝置方法時呼叫
12.1 初始化uvc輸入事件
static int uvc_input_init(struct uvc_device *dev)
{
struct input_dev *input;
int ret;
input = input_allocate_device(); //分配input裝置記憶體
if (input == NULL)
return -ENOMEM;
usb_make_path(dev->udev, dev->input_phys, sizeof(dev->input_phys)); //裝置節點路徑
strlcat(dev->input_phys, "/button", sizeof(dev->input_phys));
input->name = dev->name; //輸入裝置名
input->phys = dev->input_phys; //輸入裝置節點路徑
usb_to_input_id(dev->udev, &input->id);
input->dev.parent = &dev->intf->dev; //輸入裝置的父裝置為usb介面裝置
__set_bit(EV_KEY, input->evbit); //設定輸入事件型別
__set_bit(KEY_CAMERA, input->keybit); //設定按鈕
if ((ret = input_register_device(input)) < 0) //註冊input裝置
goto error;
dev->input = input; //uvc裝置捆綁輸入裝置
return 0;
error:
input_free_device(input);
return ret;
}
12.2 urb回撥函式
static void uvc_status_complete(struct urb *urb)
{
struct uvc_device *dev = urb->context;
int len, ret;
switch (urb->status) {
case 0:
break;
case -ENOENT: /* usb_kill_urb() called. */
case -ECONNRESET: /* usb_unlink_urb() called. */
case -ESHUTDOWN: /* The endpoint is being disabled. */
case -EPROTO: /* Device is disconnected (reported by some host controller). */
return;
default:
uvc_printk(KERN_WARNING, "Non-zero status (%d) in status completion handler.\n", urb->status);
return;
}
len = urb->actual_length;
if (len > 0) {
switch (dev->status[0] & 0x0f) {
case UVC_STATUS_TYPE_CONTROL: //VC事件
uvc_event_control(dev, dev->status, len); //Table 2-2 Status Packet Format (VideoControl Interface as the Originator)
break;
case UVC_STATUS_TYPE_STREAMING: //VS事件
uvc_event_streaming(dev, dev->status, len); //Table 2-3 Status Packet Format (VideoStreaming Interface as the Originator)
break;
default
uvc_trace(UVC_TRACE_STATUS, "Unknown status event type %u.\n", dev->status[0]);
break;
}
}
/* Resubmit the URB. */
urb->interval = dev->int_ep->desc.bInterval;
if ((ret = usb_submit_urb(urb, GFP_ATOMIC)) < 0) { //提交urb
uvc_printk(KERN_ERR, "Failed to resubmit status URB (%d).\n",ret);
}
}
12.2.1 VC狀態變化事件處理
static void uvc_event_control(struct uvc_device *dev, __u8 *data, int len)
{
char *attrs[3] = { "value", "info", "failure" };
if (len < 6 || data[2] != 0 || data[4] > 2) {//長度應該為6,且data[2](bEvent)為0表示(Control Change),data[4]大於2部分為保留值
uvc_trace(UVC_TRACE_STATUS, "Invalid control status event received.\n");
return;
}
uvc_trace(UVC_TRACE_STATUS, "Control %u/%u %s change len %d.\n",data[1], data[3], attrs[data[4]], len);
}
12.2.2 VS狀態變化事件處理
static void uvc_event_streaming(struct uvc_device *dev, __u8 *data, int len)
{
if (len < 3) { //長度等於4
uvc_trace(UVC_TRACE_STATUS, "Invalid streaming status event received.\n");
return;
}
if (data[2] == 0) { //data[2](bevent)--0x00(Button Press)攝像頭上的按鈕按下
if (len < 4)
return;
uvc_trace(UVC_TRACE_STATUS, "Button (intf %u) %s len %d\n",data[1], data[3] ? "pressed" : "released", len);
uvc_input_report_key(dev, KEY_CAMERA, data[3]); //上報按鍵事件
}
else {
uvc_trace(UVC_TRACE_STATUS, "Stream %u error event %02x %02x ""len %d.\n", data[1], data[2], data[3], len);
}
}