Linux|基于USB3 Vision协议的libusb库的usb设备的发现与连接

参考:
libusb常用函数 (https://blog.csdn.net/weixin_41466668/article/details/78898543)
libusb枚举设备的过程
libusb学习笔记(5)
Linux libusb开发教程<二> API编程接口介绍
USB描述符
usb驱动的基本结构和函数简介
libusb实现读写USB设备

简介

USB3 Vision是一个基于USB 3.0接口的机器视觉通信协议。它由自动化成像协会 (AIA)开发,用于支持高速、高带宽的视频流和相机控制。
在Linux环境下,使用USB3 Vision协议与USB相机设备进行连接和通信,通常需要以下步骤:

设备发现

首先,你需要发现并识别连接到系统的USB3 Vision设备。这通常通过读取设备的描述符和属性来完成。

		libusb_device** devices;
		libusb_device* device = NULL;
		libusb_context *context = NULL;
		ssize_t device_count;
		int i = 0;
		// 初始化 libusb。 必须在调用任何其他 libusb 函数之前调用此函数。
		int result = libusb_init(&context);
		if (result < 0) {
			std::cerr << "Failed to initialize libusb: " << libusb_error_name(result) << std::endl;
			return 1;
		}
		// 返回当前连接到系统的 USB 设备列表。 这是查找要操作的 USB 设备的入口点。
		device_count = libusb_get_device_list(context, &devices);
		if (device_count < 0)
		{
			std::cerr << "Failed to get device list: " << libusb_error_name(device_count) << std::endl;
			libusb_exit(context);
			return 1;
		}
		// 遍历所有usb可连接设备 筛选出需要连接的设备 并记录ID(可以自定义修改)
		while ((device = devices[i++]) != NULL)
		{
			unsigned long long ID = 0;
			struct libusb_device_descriptor desc;
			result = libusb_get_device_descriptor(device, &desc);
			if (result < 0) {
				std::cerr << "Failed to get device descriptor: " << libusb_error_name(result) << std::endl;
				continue;
			}
			// 筛选相机usb设备 !根据你自己需要连接的设备进行修改 记录ID
			if (desc.bDeviceClass == 0xFF && desc.bDeviceSubClass == 0xFF && desc.bDeviceProtocol == 0xFF)
			{
				uint8_t portID = libusb_get_port_number(device);
				unsigned long long DevID = desc.idVendor << 16 | desc.idProduct;
				ID = DevID << 32 | portID;
				vecId.push_back(ID);
			}

		}
		// 释放先前使用 libusb_get_device_list() 发现的设备列表。 如果设置了 unref_devices 参数,则列表中每个设备的引用计数减 1。
		libusb_free_device_list(devices, 1);
		libusb_exit(context);

设备打开

一旦设备被识别,你需要打开设备以进行进一步的通信。这通常涉及到获取设备的句柄和初始化设备。

// 获取设备和设备句柄 打开设备
libusb_device_handle* m_usbHandle;
libusb_device* m_usbDev;
libusb_device** devs;
libusb_device* dev;
ssize_t cnt;
// 根据初始化时获取的相机列表获取出相关相机
// 返回当前连接到系统的 USB 设备列表。 这是查找要操作的 USB 设备的入口点。
cnt = libusb_get_device_list(NULL, &devs);
if (cnt < 0)
{
	libusb_exit(NULL);
	return 0;
}
while ((dev = devs[i++]) != NULL)
{
	struct libusb_device_descriptor desc;
	libusb_get_device_descriptor(dev, &desc);
	uint8_t portID = libusb_get_port_number(dev);
	if (desc.idVendor == vid && desc.idProduct == pid && portID == portid)
	{
		break;
	}
}
int result = libusb_open(dev, &m_usbHandle); 
if (result < 0) {
	std::cerr << "Failed to open device: " << libusb_error_name(result) << std::endl;
}
m_usbDev = libusb_get_device(m_usbHandle);
if (m_usbDev == NULL) {
	std::cerr << "Failed to libusb_get_device. " << std::endl;
}
libusb_free_device_list(devs, 1);

// 获取设备标准描述符 用于通信
struct libusb_device_descriptor desc;
// libusb_get_device_descriptor获取指定设备的USB设备描述符
libusb_get_device_descriptor(m_usbDev, &desc);
bool specFlag = false;
if (m_usbDev)
{
	// 遍历所有可能的配置数
	for (int i = 0; i < desc.bNumConfigurations; i++)
	{
		// usb设备配置描述结构体 
		struct libusb_config_descriptor* config = NULL;
		//libusb_get_config_descriptor获取配置描述符、接口描述符、端点描述符信息。
		libusb_get_config_descriptor(m_usbDev, i, &config);
		// 配置所支持的接口数,指该配置配备的接口数量,也表示该配置下接口描述符数量。
		for (int j = 0; (config != NULL && j < config->bNumInterfaces); j++)
		{
			// 给当前设备绑定接口到全局变量
			struct libusb_interface interface = config->interface[j];
			// interface.num_altsetting:备用设置数
			for (int k = 0; k < interface.num_altsetting; k++)
			{
				// libusb_interface_descriptor
				// usb描述符,主要有四种usb描述符,设备描述符,配置描述符,接口描述符和端点描述符,
				// 协议里规定一个usb设备是必须支持这四大描述符的
				struct libusb_interface_descriptor intfDesc = interface.altsetting[k];
				// intfDesc.bInterfaceProtocol:接口协议
				if (intfDesc.bInterfaceProtocol == 0)
				{
					// intfDesc.bInterfaceNumber:接口号
					unsigned char m_ctrlInterface = static_cast<unsigned char>(intfDesc.bInterfaceNumber);
					// intfDesc.bNumEndpoints:接口拥有的端点数量
					for (int h = 0; h < intfDesc.bNumEndpoints; h++)
					{
						// libusb端点描述符
						struct libusb_endpoint_descriptor endDesc = intfDesc.endpoint[h];
						// __u8 bEndpointAddress;
						// 它的bits 0~3表示的就是端点号,USB_ENDPOINT_NUMBER_MASK;bit7是方向,USB_ENDPOINT_DIR_MASK
						// 判断是输入端点还是输出端点
						if (GET_BIT(endDesc.bEndpointAddress, 7))
						{
							unsigned char m_ctrlInEp = static_cast<int>(endDesc.bEndpointAddress);
						}
						else
						{
							unsigned char m_ctrlOutEp = static_cast<int>(endDesc.bEndpointAddress);
						}
					}
				}
				else if (intfDesc.bInterfaceProtocol == 1)
				{
					unsigned char m_eventInterface = static_cast<unsigned char>(intfDesc.bInterfaceNumber);
					for (int h = 0; h < intfDesc.bNumEndpoints; h++)
					{
						struct libusb_endpoint_descriptor endDesc = intfDesc.endpoint[h];
						unsigned char m_eventEp = static_cast<int>(endDesc.bEndpointAddress);
					}
				}
				else if (intfDesc.bInterfaceProtocol == 2)
				{
					unsigned char m_streamInterface = static_cast<unsigned char>(intfDesc.bInterfaceNumber);
					for (int h = 0; h < intfDesc.bNumEndpoints; h++)
					{
						struct libusb_endpoint_descriptor endDesc = intfDesc.endpoint[h];
						unsigned char m_streamEp = static_cast<int>(endDesc.bEndpointAddress);
					}
				}
			}
		}
	}
}

if (m_usbHandle)
{
	// 内核驱动激活与分离
	// libusb_kernel_active获取驱动是否被加载
	if (1 == libusb_kernel_driver_active(m_usbHandle, m_ctrlInterface))
	{
		if (0 == libusb_detach_kernel_driver(m_usbHandle, m_ctrlInterface))
		{
			printf("Kernel Driver Detached\n");
		}
	}
	// 声明接口
	int rtn = libusb_claim_interface(m_usbHandle, m_ctrlInterface);
	if (1 == libusb_kernel_driver_active(m_usbHandle, m_eventInterface))
	{
		if (0 == libusb_detach_kernel_driver(m_usbHandle, m_eventInterface))
		{
			printf("Kernel Driver Detached\n");
		}
	}
	rtn = libusb_claim_interface(m_usbHandle, m_eventInterface);
	if (1 == libusb_kernel_driver_active(m_usbHandle, m_streamInterface))
	{
		if (0 == libusb_detach_kernel_driver(m_usbHandle, m_streamInterface))
		{
			printf("Kernel Driver Detached\n");
		}
	}
	// 注册一个通信接口
	// 返回值:成功0;EBUSY接口无效,无法注册;ENOMEM内存不足。
	rtn = libusb_claim_interface(m_usbHandle, m_streamInterface);
}

设备配置

在设备打开后,你需要配置设备的参数,如分辨率、帧率、曝光时间等。这通常通过发送控制传输请求来完成。

int ReadMemory(unsigned long long startAddr, unsigned char* readBuff, unsigned int length)
{
	unsigned int len = 4294967295; // 根据自己需求修改
	auto cmdBuffPtr = std::unique_ptr<char[]>(new char[len / sizeof(char)]);
	unsigned int maxReadLength = 1024;	// 根据自己需求修改

	unsigned int bytes2Read = length;
	unsigned long long startAddrOnce = startAddr;
	unsigned char* pBuff = readBuff;
	while (bytes2Read)
	{
		unsigned int readOnce = maxReadLength;
		if (bytes2Read < maxReadLength)
		{
			readOnce = bytes2Read;
		}
		unsigned short sendReqId = m_reqId;
		std::lock_guard < std::mutex> lock(m_mutex);
		// 拷贝内存
		new (cmdBuffPtr.get()) GENCP_CMD_HEADER(GENCP_CMD::GENCP_READMEM_CMD, sizeof(READ_CMD_DATA), sendReqId);
		GENCP_CMD_ADDRESS address;
		memcpy(&address, &startAddrOnce, sizeof(GENCP_CMD_ADDRESS));
		READ_CMD_DATA cmdData = { address,0 ,readOnce };
		memcpy(cmdBuffPtr.get() + sizeof(GENCP_CMD_HEADER), &cmdData, sizeof(READ_CMD_DATA));
		auto ackBuffPtr = std::unique_ptr<char[]>(new char[m_maxAckTransLength / sizeof(char)]);

		//printf("%s:%d \n", __FILE__, __LINE__);
		int rtn = TransceiveData(reinterpret_cast<char*>(cmdBuffPtr.get()),
			len,
			readOnce,
			enumType(GENCP_CMD::GENCP_READMEM_ACK),
			reinterpret_cast<char*>(ackBuffPtr.get()),
			m_maxAckTransLength);

		//printf("%s:%d \n", __FILE__, __LINE__);
		if (rtn < 0)
		{
			printf("%s:%d ERR_INVALID_DATA_RECEIVED\n", __FILE__, __LINE__);
			return ERR_INVALID_DATA_RECEIVED;
		}
		GENCP_CMD_HEADER* pData = reinterpret_cast<GENCP_CMD_HEADER*>(ackBuffPtr.get());

		unsigned int recvReqId = pData->req_id;

		if (recvReqId != sendReqId)
		{
			printf("%s:%d ERR_INVALID_DATA_RECEIVED\n", __FILE__, __LINE__);
			return ERR_INVALID_DATA_RECEIVED;
		}
		memcpy(pBuff, ackBuffPtr.get() + sizeof(GENCP_CMD_HEADER), readOnce);
		startAddrOnce += readOnce;
		pBuff += readOnce;
		bytes2Read -= readOnce;
	}

	//printf("%s:%d \n", __FILE__, __LINE__);
	return length;
}
  1. 数据流控制: 配置好设备后,你需要启动数据流以开始接收视频数据。这通常涉及到分配和提交传输缓 冲区,然后启动数据流。
  2. 数据接收:一旦数据流启动,你就可以开始接收视频数据了。这通常通过异步或同步的方式来完成,取决于你的应用需求。
  3. 设备关闭: 最后,当你完成所有操作后,你需要关闭设备以释放资源
    在每个步骤中,你都可能需要发送和接收各种类型的信息,包括设备描述符、设备属性、控制传输请求、数据流命令等。具体的信息类型和格式取决于USB3 Vision协议的规范和你的设备的实现。
    注意,要在Linux环境下使用USB3 Vision协议,你可能需要一个支持该协议的库,如libusb或者专门的USB3 Vision SDK。这些库通常会提供一套API,使你能够更容易地进行上述步骤。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

奇树谦

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值