Linux攝像頭驅動學習之:(五)UVC-分析設備描述符


  linux系統上插上USB攝像頭設備后,內存就會有相應的設備描述符信息,后期可以根據這些信息進一步寫驅動程序。

流程:Device(設備) -> Configuration(配置) -> IAD I/F(接口聯合體描述符-對接口的管理,比如數量和調用順序等)

查看UVC 1.5 Cloass Specification 規范手冊
框架調用流程:IT(01) -> PU(03) -> EU(04) -> OT(02)

<Video Control Interface> 處理函數 parse_videocontrol_interface()--------------------------------------------

VideoControl Interface的自定義描述符:
extra buffer of interface 0:
extra desc 0: 0d 24 01 00 01 4d 00 80 c3 c9 01 01 01
VC_HEADER
extra desc 1: 12 24 02 01 01 02 00 00 00 00 00 00 00 00 03 0e 00 00
VC_INPUT_TERMINAL ID
extra desc 2: 09 24 03 02 01 01 00 04 00
VC_OUTPUT_TERMINAL ID wTerminalType bAssocTerminal bSourceID
extra desc 3: 0b 24 05 03 01 00 00 02 7f 14 00
VC_PROCESSING_UNIT ID bSourceID wMaxMultiplier bControlSize bmControls
extra desc 4: 1a 24 06 04 ad cc b1 c2 f6 ab b8 48 8e 37 32 d4 f3 a3 fe ec 08 01 03 01 3f 00
VC_EXTENSION_UNIT ID GUID bNumControls bNrInPins baSourceID

VC_DESCRIPTOR_UNDEFINED 0x00
VC_HEADER 0x01
VC_INPUT_TERMINAL 0x02
VC_OUTPUT_TERMINAL 0x03
VC_SELECTOR_UNIT 0x04
VC_PROCESSING_UNIT 0x05
VC_EXTENSION_UNIT 0x06
VC_ENCODING_UNIT 0x07

<Video Streaming Interface> 處理函數 parse_videostreaming_interface()--------------------------------------

VideoStreaming Interface的自定義描述符:
extra buffer of interface 1:
extra desc 0: 0e 24 01 01 df 00 81 00 02 02 01 01 01 00
VS_INPUT_HEADER bNumFormats
extra desc 1: 1b 24 04 01 05 59 55 59 32 00 00 10 00 80 00 00 aa 00 38 9b 71 10 01 00 00 00 00
VS_FORMAT_UNCOMPRESSED bFormatIndex bNumFrameDescriptors GUID bBitsPerPixel
extra desc 2: 1e 24 05 01 00 80 02 e0 01 00 00 ca 08 00 00 ca 08 00 60 09 00 15 16 05 00 01 15 16 05 00
VS_FRAME_UNCOMPRESSED bFrameIndex bmCapabilities wWidth wHeight
640x480
extra desc 3: 1e 24 05 02 00 60 01 20 01 00 80 e6 02 00 80 e6 02 00 18 03 00 15 16 05 00 01 15 16 05 00
VS_FRAME_UNCOMPRESSED
extra desc 4: 1e 24 05 03 00 40 01 f0 00 00 80 32 02 00 80 32 02 00 58 02 00 15 16 05 00 01 15 16 05 00
extra desc 5: 1e 24 05 04 00 b0 00 90 00 00 a0 b9 00 00 a0 b9 00 00 c6 00 00 15 16 05 00 01 15 16 05 00
extra desc 6: 1e 24 05 05 00 a0 00 78 00 00 a0 8c 00 00 a0 8c 00 00 96 00 00 15 16 05 00 01 15 16 05 00

extra desc 7: 1a 24 03 00 05 80 02 e0 01 60 01 20 01 40 01 f0 00 b0 00 90 00 a0 00 78 00 00
VS_STILL_IMAGE_FRAME
extra desc 8: 06 24 0d 01 01 04

VS_INPUT_HEADER 0x01
VS_STILL_IMAGE_FRAME 0x03
VS_FORMAT_UNCOMPRESSED 0x04
VS_FRAME_UNCOMPRESSED 0x05
VS_COLORFORMAT 0x0D

 

 

//參考 lsusb 源碼得知如何去實現獲取設備描述符:
main
  ->list_devices
   ->dumpdev(libusb_device *dev)
     ->dump_device(udev, &desc);
    ->dump_config(udev, config); //打印出配置描述符
       ->for (i = 0 ; i < config->bNumInterfaces ; i++) //每個設置可能對應多個接口
       dump_interface(dev, &config->interface[i]);
        ->for (i = 0; i < interface->num_altsetting; i++) //每個接口設置描述符
        dump_altsetting(dev, &interface->altsetting[i]);


//lsusb 命令打印出設備ID
Bus 002 Device 006: ID 1b3b:2977

//lsusb -v -d 0x1b3b : 命令打印出設備詳細描述符
Bus 002 Device 007: ID 1b3b:2977
Device Descriptor: //設備描述符
bLength 18
bDescriptorType 1
bcdUSB 2.00
bDeviceClass 239 Miscellaneous Device
bDeviceSubClass 2 ?
bDeviceProtocol 1 Interface Association
bMaxPacketSize0 64
idVendor 0x1b3b
idProduct 0x2977
bcdDevice 1.0a
iManufacturer 0
iProduct 0
iSerial 0
bNumConfigurations 1
Configuration Descriptor: //配置描述符
bLength 9
bDescriptorType 2
wTotalLength 492
bNumInterfaces 4
bConfigurationValue 1
iConfiguration 0
bmAttributes 0x80
(Bus Powered)
MaxPower 500mA
Interface Association: //接口
bLength 8
bDescriptorType 11
bFirstInterface 0
bInterfaceCount 2
bFunctionClass 14 Video
bFunctionSubClass 3 Video Interface Collection
bFunctionProtocol 0
iFunction 0
.....................................

/*--------------------------------------------以下代碼實現獲取通用usb攝像頭設備描述符信息-----------------------------------------------------*/

參考:lsusb - 源代碼《libusb-1.0.16-rc10》 《usbutils-006》

//注:本份代碼僅實現設備描述符的獲取和解析
#include <linux/kernel.h> #include <linux/list.h> #include <linux/module.h> #include <linux/usb.h> #include <linux/videodev2.h> #include <linux/vmalloc.h> #include <linux/wait.h> #include <asm/atomic.h> #include <asm/unaligned.h> #include <media/v4l2-common.h> //支持的設備類型信息 static struct usb_device_id sheldon_uvc_ids[] = { /* Generic USB Video Class */ { USB_INTERFACE_INFO(USB_CLASS_VIDEO, 1, 0) },/*1-視頻控制接口*/ { USB_INTERFACE_INFO(USB_CLASS_VIDEO, 2, 0) },/*2-視頻流控制接口(被1包含)*/ {} }; static const char *get_guid(const unsigned char *buf) { static char guid[39]; /* NOTE: see RFC 4122 for more information about GUID/UUID * structure. The first fields fields are historically big * endian numbers, dating from Apollo mc68000 workstations. */ sprintf(guid, "{%02x%02x%02x%02x" "-%02x%02x" "-%02x%02x" "-%02x%02x" "-%02x%02x%02x%02x%02x%02x}", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7], buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14], buf[15]); return guid; } static void parse_videocontrol_interface(struct usb_interface *intf, unsigned char *buf, int buflen) { static const char * const ctrlnames[] = { "Brightness", "Contrast", "Hue", "Saturation", "Sharpness", "Gamma", "White Balance Temperature", "White Balance Component", "Backlight Compensation", "Gain", "Power Line Frequency", "Hue, Auto", "White Balance Temperature, Auto", "White Balance Component, Auto", "Digital Multiplier", "Digital Multiplier Limit", "Analog Video Standard", "Analog Video Lock Status" }; static const char * const camctrlnames[] = { "Scanning Mode", "Auto-Exposure Mode", "Auto-Exposure Priority", "Exposure Time (Absolute)", "Exposure Time (Relative)", "Focus (Absolute)", "Focus (Relative)", "Iris (Absolute)", "Iris (Relative)", "Zoom (Absolute)", "Zoom (Relative)", "PanTilt (Absolute)", "PanTilt (Relative)", "Roll (Absolute)", "Roll (Relative)", "Reserved", "Reserved", "Focus, Auto", "Privacy" }; static const char * const stdnames[] = { "None", "NTSC - 525/60", "PAL - 625/50", "SECAM - 625/50", "NTSC - 625/50", "PAL - 525/60" }; unsigned int i, ctrls, stds, n, p, termt, freq; while (buflen > 0) { if (buf[1] != USB_DT_CS_INTERFACE) printk(" Warning: Invalid descriptor\n"); else if (buf[0] < 3) printk(" Warning: Descriptor too short\n"); printk(" VideoControl Interface Descriptor:\n" " bLength %5u\n" " bDescriptorType %5u\n" " bDescriptorSubtype %5u ", buf[0], buf[1], buf[2]); switch (buf[2]) { case 0x01: /* HEADER */ printk("(HEADER)\n"); n = buf[11]; if (buf[0] < 12+n) printk(" Warning: Descriptor too short\n"); freq = buf[7] | (buf[8] << 8) | (buf[9] << 16) | (buf[10] << 24); printk(" bcdUVC %2x.%02x\n" " wTotalLength %5u\n" " dwClockFrequency %5u.%06uMHz\n" " bInCollection %5u\n", buf[4], buf[3], buf[5] | (buf[6] << 8), freq / 1000000, freq % 1000000, n); for (i = 0; i < n; i++) printk(" baInterfaceNr(%2u) %5u\n", i, buf[12+i]); break; case 0x02: /* INPUT_TERMINAL */ printk("(INPUT_TERMINAL)\n"); termt = buf[4] | (buf[5] << 8); n = termt == 0x0201 ? 7 : 0; if (buf[0] < 8 + n) printk(" Warning: Descriptor too short\n"); printk(" bTerminalID %5u\n" " wTerminalType 0x%04x\n" " bAssocTerminal %5u\n", buf[3], termt, buf[6]); printk(" iTerminal %5u\n", buf[7]); if (termt == 0x0201) { n += buf[14]; printk(" wObjectiveFocalLengthMin %5u\n" " wObjectiveFocalLengthMax %5u\n" " wOcularFocalLength %5u\n" " bControlSize %5u\n", buf[8] | (buf[9] << 8), buf[10] | (buf[11] << 8), buf[12] | (buf[13] << 8), buf[14]); ctrls = 0; for (i = 0; i < 3 && i < buf[14]; i++) ctrls = (ctrls << 8) | buf[8+n-i-1]; printk(" bmControls 0x%08x\n", ctrls); for (i = 0; i < 19; i++) if ((ctrls >> i) & 1) printk(" %s\n", camctrlnames[i]); } break; case 0x03: /* OUTPUT_TERMINAL */ printk("(OUTPUT_TERMINAL)\n"); termt = buf[4] | (buf[5] << 8); if (buf[0] < 9) printk(" Warning: Descriptor too short\n"); printk(" bTerminalID %5u\n" " wTerminalType 0x%04x\n" " bAssocTerminal %5u\n" " bSourceID %5u\n" " iTerminal %5u\n", buf[3], termt, buf[6], buf[7], buf[8]); break; case 0x04: /* SELECTOR_UNIT */ printk("(SELECTOR_UNIT)\n"); p = buf[4]; if (buf[0] < 6+p) printk(" Warning: Descriptor too short\n"); printk(" bUnitID %5u\n" " bNrInPins %5u\n", buf[3], p); for (i = 0; i < p; i++) printk(" baSource(%2u) %5u\n", i, buf[5+i]); printk(" iSelector %5u\n", buf[5+p]); break; case 0x05: /* PROCESSING_UNIT */ printk("(PROCESSING_UNIT)\n"); n = buf[7]; if (buf[0] < 10+n) printk(" Warning: Descriptor too short\n"); printk(" bUnitID %5u\n" " bSourceID %5u\n" " wMaxMultiplier %5u\n" " bControlSize %5u\n", buf[3], buf[4], buf[5] | (buf[6] << 8), n); ctrls = 0; for (i = 0; i < 3 && i < n; i++) ctrls = (ctrls << 8) | buf[8+n-i-1]; printk(" bmControls 0x%08x\n", ctrls); for (i = 0; i < 18; i++) if ((ctrls >> i) & 1) printk(" %s\n", ctrlnames[i]); stds = buf[9+n]; printk(" iProcessing %5u\n" " bmVideoStandards 0x%2x\n", buf[8+n], stds); for (i = 0; i < 6; i++) if ((stds >> i) & 1) printk(" %s\n", stdnames[i]); break; case 0x06: /* EXTENSION_UNIT */ printk("(EXTENSION_UNIT)\n"); p = buf[21]; n = buf[22+p]; if (buf[0] < 24+p+n) printk(" Warning: Descriptor too short\n"); printk(" bUnitID %5u\n" " guidExtensionCode %s\n" " bNumControl %5u\n" " bNrPins %5u\n", buf[3], get_guid(&buf[4]), buf[20], buf[21]); for (i = 0; i < p; i++) printk(" baSourceID(%2u) %5u\n", i, buf[22+i]); printk(" bControlSize %5u\n", buf[22+p]); for (i = 0; i < n; i++) printk(" bmControls(%2u) 0x%02x\n", i, buf[23+p+i]); printk(" iExtension %5u\n", buf[23+p+n]); break; default: printk("(unknown)\n" " Invalid desc subtype:"); break; } buflen -= buf[0]; buf += buf[0]; } } static void parse_videostreaming_interface(struct usb_interface *intf, unsigned char *buf, int buflen) { static const char * const colorPrims[] = { "Unspecified", "BT.709,sRGB", "BT.470-2 (M)", "BT.470-2 (B,G)", "SMPTE 170M", "SMPTE 240M" }; static const char * const transferChars[] = { "Unspecified", "BT.709", "BT.470-2 (M)", "BT.470-2 (B,G)", "SMPTE 170M", "SMPTE 240M", "Linear", "sRGB"}; static const char * const matrixCoeffs[] = { "Unspecified", "BT.709", "FCC", "BT.470-2 (B,G)", "SMPTE 170M (BT.601)", "SMPTE 240M" }; unsigned int i, m, n, p, flags, len; while (buflen > 0) { if (buf[1] != USB_DT_CS_INTERFACE) printk(" Warning: Invalid descriptor\n"); else if (buf[0] < 3) printk(" Warning: Descriptor too short\n"); printk(" VideoStreaming Interface Descriptor:\n" " bLength %5u\n" " bDescriptorType %5u\n" " bDescriptorSubtype %5u ", buf[0], buf[1], buf[2]); switch (buf[2]) { case 0x01: /* INPUT_HEADER */ printk("(INPUT_HEADER)\n"); p = buf[3]; n = buf[12]; if (buf[0] < 13+p*n) printk(" Warning: Descriptor too short\n"); printk(" bNumFormats %5u\n" " wTotalLength %5u\n" " bEndPointAddress %5u\n" " bmInfo %5u\n" " bTerminalLink %5u\n" " bStillCaptureMethod %5u\n" " bTriggerSupport %5u\n" " bTriggerUsage %5u\n" " bControlSize %5u\n", p, buf[4] | (buf[5] << 8), buf[6], buf[7], buf[8], buf[9], buf[10], buf[11], n); for (i = 0; i < p; i++) printk( " bmaControls(%2u) %5u\n", i, buf[13+p*n]); break; case 0x02: /* OUTPUT_HEADER */ printk("(OUTPUT_HEADER)\n"); p = buf[3]; n = buf[8]; if (buf[0] < 9+p*n) printk(" Warning: Descriptor too short\n"); printk(" bNumFormats %5u\n" " wTotalLength %5u\n" " bEndpointAddress %5u\n" " bTerminalLink %5u\n" " bControlSize %5u\n", p, buf[4] | (buf[5] << 8), buf[6], buf[7], n); for (i = 0; i < p; i++) printk( " bmaControls(%2u) %5u\n", i, buf[9+p*n]); break; case 0x03: /* STILL_IMAGE_FRAME */ printk("(STILL_IMAGE_FRAME)\n"); n = buf[4]; m = buf[5+4*n]; if (buf[0] < 6+4*n+m) printk(" Warning: Descriptor too short\n"); printk(" bEndpointAddress %5u\n" " bNumImageSizePatterns %3u\n", buf[3], n); for (i = 0; i < n; i++) printk(" wWidth(%2u) %5u\n" " wHeight(%2u) %5u\n", i, buf[5+4*i] | (buf[6+4*i] << 8), i, buf[7+4*i] | (buf[8+4*i] << 8)); printk(" bNumCompressionPatterns %3u\n", n); for (i = 0; i < m; i++) printk(" bCompression(%2u) %5u\n", i, buf[6+4*n+i]); break; case 0x04: /* FORMAT_UNCOMPRESSED */ case 0x10: /* FORMAT_FRAME_BASED */ if (buf[2] == 0x04) { printk("(FORMAT_UNCOMPRESSED)\n"); len = 27; } else { printk("(FORMAT_FRAME_BASED)\n"); len = 28; } if (buf[0] < len) printk(" Warning: Descriptor too short\n"); flags = buf[25]; printk(" bFormatIndex %5u\n" " bNumFrameDescriptors %5u\n" " guidFormat %s\n" " bBitsPerPixel %5u\n" " bDefaultFrameIndex %5u\n" " bAspectRatioX %5u\n" " bAspectRatioY %5u\n" " bmInterlaceFlags 0x%02x\n", buf[3], buf[4], get_guid(&buf[5]), buf[21], buf[22], buf[23], buf[24], flags); printk(" Interlaced stream or variable: %s\n", (flags & (1 << 0)) ? "Yes" : "No"); printk(" Fields per frame: %u fields\n", (flags & (1 << 1)) ? 1 : 2); printk(" Field 1 first: %s\n", (flags & (1 << 2)) ? "Yes" : "No"); printk(" Field pattern: "); switch ((flags >> 4) & 0x03) { case 0: printk("Field 1 only\n"); break; case 1: printk("Field 2 only\n"); break; case 2: printk("Regular pattern of fields 1 and 2\n"); break; case 3: printk("Random pattern of fields 1 and 2\n"); break; } printk(" bCopyProtect %5u\n", buf[26]); if (buf[2] == 0x10) printk(" bVariableSize %5u\n", buf[27]); break; case 0x05: /* FRAME UNCOMPRESSED */ case 0x07: /* FRAME_MJPEG */ case 0x11: /* FRAME_FRAME_BASED */ if (buf[2] == 0x05) { printk("(FRAME_UNCOMPRESSED)\n"); n = 25; } else if (buf[2] == 0x07) { printk("(FRAME_MJPEG)\n"); n = 25; } else { printk("(FRAME_FRAME_BASED)\n"); n = 21; } len = (buf[n] != 0) ? (26+buf[n]*4) : 38; if (buf[0] < len) printk(" Warning: Descriptor too short\n"); flags = buf[4]; printk(" bFrameIndex %5u\n" " bmCapabilities 0x%02x\n", buf[3], flags); printk(" Still image %ssupported\n", (flags & (1 << 0)) ? "" : "un"); if (flags & (1 << 1)) printk(" Fixed frame-rate\n"); printk(" wWidth %5u\n" " wHeight %5u\n" " dwMinBitRate %9u\n" " dwMaxBitRate %9u\n", buf[5] | (buf[6] << 8), buf[7] | (buf[8] << 8), buf[9] | (buf[10] << 8) | (buf[11] << 16) | (buf[12] << 24), buf[13] | (buf[14] << 8) | (buf[15] << 16) | (buf[16] << 24)); if (buf[2] == 0x11) printk(" dwDefaultFrameInterval %9u\n" " bFrameIntervalType %5u\n" " dwBytesPerLine %9u\n", buf[17] | (buf[18] << 8) | (buf[19] << 16) | (buf[20] << 24), buf[21], buf[22] | (buf[23] << 8) | (buf[24] << 16) | (buf[25] << 24)); else printk(" dwMaxVideoFrameBufferSize %9u\n" " dwDefaultFrameInterval %9u\n" " bFrameIntervalType %5u\n", buf[17] | (buf[18] << 8) | (buf[19] << 16) | (buf[20] << 24), buf[21] | (buf[22] << 8) | (buf[23] << 16) | (buf[24] << 24), buf[25]); if (buf[n] == 0) printk(" dwMinFrameInterval %9u\n" " dwMaxFrameInterval %9u\n" " dwFrameIntervalStep %9u\n", buf[26] | (buf[27] << 8) | (buf[28] << 16) | (buf[29] << 24), buf[30] | (buf[31] << 8) | (buf[32] << 16) | (buf[33] << 24), buf[34] | (buf[35] << 8) | (buf[36] << 16) | (buf[37] << 24)); else for (i = 0; i < buf[n]; i++) printk(" dwFrameInterval(%2u) %9u\n", i, buf[26+4*i] | (buf[27+4*i] << 8) | (buf[28+4*i] << 16) | (buf[29+4*i] << 24)); break; case 0x06: /* FORMAT_MJPEG */ printk("(FORMAT_MJPEG)\n"); if (buf[0] < 11) printk(" Warning: Descriptor too short\n"); flags = buf[5]; printk(" bFormatIndex %5u\n" " bNumFrameDescriptors %5u\n" " bFlags %5u\n", buf[3], buf[4], flags); printk(" Fixed-size samples: %s\n", (flags & (1 << 0)) ? "Yes" : "No"); flags = buf[9]; printk(" bDefaultFrameIndex %5u\n" " bAspectRatioX %5u\n" " bAspectRatioY %5u\n" " bmInterlaceFlags 0x%02x\n", buf[6], buf[7], buf[8], flags); printk(" Interlaced stream or variable: %s\n", (flags & (1 << 0)) ? "Yes" : "No"); printk(" Fields per frame: %u fields\n", (flags & (1 << 1)) ? 2 : 1); printk(" Field 1 first: %s\n", (flags & (1 << 2)) ? "Yes" : "No"); printk(" Field pattern: "); switch ((flags >> 4) & 0x03) { case 0: printk("Field 1 only\n"); break; case 1: printk("Field 2 only\n"); break; case 2: printk("Regular pattern of fields 1 and 2\n"); break; case 3: printk("Random pattern of fields 1 and 2\n"); break; } printk(" bCopyProtect %5u\n", buf[10]); break; case 0x0a: /* FORMAT_MPEG2TS */ printk("(FORMAT_MPEG2TS)\n"); len = buf[0] < 23 ? 7 : 23; if (buf[0] < len) printk(" Warning: Descriptor too short\n"); printk(" bFormatIndex %5u\n" " bDataOffset %5u\n" " bPacketLength %5u\n" " bStrideLength %5u\n", buf[3], buf[4], buf[5], buf[6]); if (len > 7) printk(" guidStrideFormat %s\n", get_guid(&buf[7])); break; case 0x0d: /* COLORFORMAT */ printk("(COLORFORMAT)\n"); if (buf[0] < 6) printk(" Warning: Descriptor too short\n"); printk(" bColorPrimaries %5u (%s)\n", buf[3], (buf[3] <= 5) ? colorPrims[buf[3]] : "Unknown"); printk(" bTransferCharacteristics %5u (%s)\n", buf[4], (buf[4] <= 7) ? transferChars[buf[4]] : "Unknown"); printk(" bMatrixCoefficients %5u (%s)\n", buf[5], (buf[5] <= 5) ? matrixCoeffs[buf[5]] : "Unknown"); break; default: printk(" Invalid desc subtype:"); break; } buflen -= buf[0]; buf += buf[0]; } } //打印端點描述符 static void dump_endpoint(const struct usb_endpoint_descriptor *endpoint) { static const char * const typeattr[] = { "Control", "Isochronous", "Bulk", "Interrupt" }; static const char * const syncattr[] = { "None", "Asynchronous", "Adaptive", "Synchronous" }; static const char * const usage[] = { "Data", "Feedback", "Implicit feedback Data", "(reserved)" }; static const char * const hb[] = { "1x", "2x", "3x", "(?\?)" }; unsigned wmax = le16_to_cpu(endpoint->wMaxPacketSize); printk(" Endpoint Descriptor:\n" " bLength %5u\n" " bDescriptorType %5u\n" " bEndpointAddress 0x%02x EP %u %s\n" " bmAttributes %5u\n" " Transfer Type %s\n" " Synch Type %s\n" " Usage Type %s\n" " wMaxPacketSize 0x%04x %s %d bytes\n" " bInterval %5u\n", endpoint->bLength, endpoint->bDescriptorType, endpoint->bEndpointAddress, endpoint->bEndpointAddress & 0x0f, (endpoint->bEndpointAddress & 0x80) ? "IN" : "OUT", endpoint->bmAttributes, typeattr[endpoint->bmAttributes & 3], syncattr[(endpoint->bmAttributes >> 2) & 3], usage[(endpoint->bmAttributes >> 4) & 3], wmax, hb[(wmax >> 11) & 3], wmax & 0x7ff, endpoint->bInterval); /* only for audio endpoints */ if (endpoint->bLength == 9) printk(" bRefresh %5u\n" " bSynchAddress %5u\n", endpoint->bRefresh, endpoint->bSynchAddress); } //probe處理函數,有匹配usb設備時調用 static int sheldon_uvc_probe(struct usb_interface *intf, const struct usb_device_id *id) { static int cnt; static int i, j ,k ,l ,m; unsigned char *buffer; int buflen; int desc_len; //int desc_cnt; //根據interface結構體獲得usb_device結構體,其中包含了設備描述符 struct usb_device *dev = interface_to_usbdev(intf); //此處需要定義一個描述符結構體 struct usb_device_descriptor *descriptor = &dev->descriptor; //從usb_device結構體中獲得配置描述符相關信息 struct usb_host_config *host_config; struct usb_config_descriptor *config; //端點描述符 struct usb_endpoint_descriptor *endpoint; //定義接口聯合體描述符結構體,獲得 IAD 接口 struct usb_interface_assoc_descriptor *assoc_desc; //定義接口設置信息結構體 struct usb_interface_descriptor *idesc; printk("----sheldon_uvc_probe : cnt = %d----\n",cnt++); //打印第cnt個接口 //打印設備描述符 printk("Device Descriptor:\n" " bLength %5u\n" " bDescriptorType %5u\n" " bcdUSB %2x.%02x\n" " bDeviceClass %5u \n" " bDeviceSubClass %5u \n" " bDeviceProtocol %5u \n" " bMaxPacketSize0 %5u\n" " idVendor 0x%04x \n" " idProduct 0x%04x \n" " bcdDevice %2x.%02x\n" " iManufacturer %5u \n" " iProduct %5u \n" " iSerial %5u \n" " bNumConfigurations %5u\n", descriptor->bLength, descriptor->bDescriptorType, descriptor->bcdUSB >> 8, descriptor->bcdUSB & 0xff, descriptor->bDeviceClass, descriptor->bDeviceSubClass, descriptor->bDeviceProtocol, descriptor->bMaxPacketSize0, descriptor->idVendor, descriptor->idProduct, descriptor->bcdDevice >> 8, descriptor->bcdDevice & 0xff, descriptor->iManufacturer, descriptor->iProduct, descriptor->iSerialNumber, descriptor->bNumConfigurations); //打印配置描述符 for(i = 0; i < descriptor->bNumConfigurations; i++) { host_config = &dev->config[i]; config = &host_config->desc; printk(" Configuration Descriptor Configuration %d :\n" " bLength %5u\n" " bDescriptorType %5u\n" " wTotalLength %5u\n" " bNumInterfaces %5u\n" " bConfigurationValue %5u\n" " iConfiguration %5u \n" " bmAttributes 0x%02x\n", i, config->bLength, config->bDescriptorType, le16_to_cpu(config->wTotalLength), config->bNumInterfaces, config->bConfigurationValue, config->iConfiguration, config->bmAttributes); //打印接口聯合體描述符 assoc_desc = host_config->intf_assoc[0]; printk(" Interface Association: %d\n" " bLength %5u\n" " bDescriptorType %5u\n" " bFirstInterface %5u\n" " bInterfaceCount %5u\n" " bFunctionClass %5u \n" " bFunctionSubClass %5u \n" " bFunctionProtocol %5u \n" " iFunction %5u \n", i, assoc_desc->bLength, assoc_desc->bDescriptorType, assoc_desc->bFirstInterface, assoc_desc->bInterfaceCount, assoc_desc->bFunctionClass, assoc_desc->bFunctionSubClass, assoc_desc->bFunctionProtocol, assoc_desc->iFunction); //打印具體每個接口的描述符 for(j = 0; j < intf->num_altsetting; j++) { idesc = &intf->altsetting[j].desc; printk(" Interface Descriptor: %d \n" " bLength %5u\n" " bDescriptorType %5u\n" " bInterfaceNumber %5u\n" " bAlternateSetting %5u\n" " bNumEndpoints %5u\n" " bInterfaceClass %5u \n" " bInterfaceSubClass %5u \n" " bInterfaceProtocol %5u \n" " iInterface %5u \n", j, idesc->bLength, idesc->bDescriptorType, idesc->bInterfaceNumber, idesc->bAlternateSetting, idesc->bNumEndpoints, idesc->bInterfaceClass, idesc->bInterfaceSubClass, idesc->bInterfaceProtocol, idesc->iInterface); //打印intf接口里的第i個設置的第m個端點的描述符 for (m = 0; m < idesc->bNumEndpoints; m++) { endpoint = &intf->altsetting[j].endpoint[m].desc; dump_endpoint(endpoint); } } //buffer存着設備自定義的描述符(第一個字節描述 長度) buffer = intf->cur_altsetting->extra; //自定義描述符長度 buflen = intf->cur_altsetting->extralen; printk("extra buffer of interface %d \n",cnt-1); //desc_cnt = 0; //第幾個額外的描述符 k = 0; while(k < buflen) //打印描述符 { desc_len = buffer[k]; //從下一個描述符的第一個字節獲得其長度 printk("extra desc %d \n",k); for(l = 0 ; l < desc_len; l++ ,k++) //保證k指向下一個描述符的第一個字節 { printk("%02x ", buffer[k]); } //desc_cnt++; printk("\n"); } idesc = &intf->cur_altsetting->desc; //判斷是CS還是VS, if((buffer[1] == USB_DT_CS_INTERFACE) && (idesc->bInterfaceSubClass == 1)) { parse_videocontrol_interface(intf, buffer, buflen); } if((buffer[1] == USB_DT_CS_INTERFACE) && (idesc->bInterfaceSubClass == 2)) { parse_videostreaming_interface(intf, buffer, buflen); } } return 0; } //disconnect函數,設備斷開時調用 static void sheldon_uvc_disconnect(struct usb_interface *intf) { static int cnt; printk("sheldon_uvc_disconnect : cnt = %d\n",cnt++); } //1.分配usb_driver結構體 //2.設置 static struct usb_driver sheldon_uvc_driver = { .name = "sheldon_uvc", .id_table = sheldon_uvc_ids, .probe = sheldon_uvc_probe, .disconnect = sheldon_uvc_disconnect, }; static int sheldon_uvc_init(void) { //3.注冊 printk("sheldon_uvc_init ~\n"); usb_register(&sheldon_uvc_driver); return 0; } static void sheldon_uvc_exit(void) { printk("sheldon_uvc_exit ~\n"); usb_deregister(&sheldon_uvc_driver); } module_init(sheldon_uvc_init); module_exit(sheldon_uvc_exit); MODULE_LICENSE("GPL");

/*-------------附Makefile-----------*/

KERN_DIR = /usr/src/linux-headers-2.6.31-14-generic/

all:
    make -C $(KERN_DIR) M=`pwd` modules 

clean:
    make -C $(KERN_DIR) M=`pwd` modules clean
    rm -rf modules.order

obj-m    += sheldon_uvc.o

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM