Ubuntu 20.04下ioctl调用返回“设备或资源忙”问题的解决方法
问题:Ubuntu 20.04下通过ioctl控制USB相机返回“设备或资源忙”
环境与操作步骤
在Ubuntu 20.04系统中,尝试通过ioctl与USB相机寄存器收发数据,操作流程如下:
- 通过
lsusb获取设备路径信息:
laptop05:~$ lsusb Bus 001 Device 006: ID a168:0970 My Camera
- 打开设备句柄:
deviceHandle = ::open("/dev/bus/usb/001/006", (O_RDWR | O_NONBLOCK));
- 程序关闭时关闭设备句柄:
if(deviceHandle > (-1)) ::close(deviceHandle);
- 发送控制数据的核心代码片段:
struct usbdevfs_ctrltransfer ctrl; const int tm = 20; const int tm_out = 1000; //0; const int buf_len = 2; unsigned char tx_buf[buf_len] = { 0, 0 }; tx_buf[0] = 0xF2; tx_buf[1] = 0x01; ctrl.bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT; // 0x21; ctrl.bRequest = 0x01; ctrl.wValue = 0x200; ctrl.wIndex = 0x400; ctrl.wLength = buf_len; ctrl.timeout = tm_out; ctrl.data = (void*)tx_buf; int ans = ioctl(deviceHandle, USBDEVFS_CONTROL, &ctrl); QThread::msleep(tm); if(ans < 0) { qDebug() << "sending 0xF201 is failed."; qDebug() << "ioctl failed and returned errno = " << strerror(errno); }
执行后,ioctl始终返回device or resource is busy(设备或资源忙),无法成功发送控制指令。
完整终端测试代码如下:
#include <fcntl.h> #include <stdio.h> #include <unistd.h> #include <sys/ioctl.h> #include <sys/types.h> #include <linux/ioctl.h> #include <linux/usbdevice_fs.h> // USB方向定义 #define USB_DIR_OUT 0 /* 到设备 */ #define USB_DIR_IN 0x80 /* 到主机 */ // USB类型定义 #define USB_TYPE_MASK (0x03 << 5) #define USB_TYPE_STANDARD (0x00 << 5) #define USB_TYPE_CLASS (0x01 << 5) #define USB_TYPE_VENDOR (0x02 << 5) #define USB_TYPE_RESERVED (0x03 << 5) // USB接收方定义 #define USB_RECIP_MASK 0x1f #define USB_RECIP_DEVICE 0x00 #define USB_RECIP_INTERFACE 0x01 #define USB_RECIP_ENDPOINT 0x02 #define USB_RECIP_OTHER 0x03 #define USB_RECIP_PORT 0x04 #define USB_RECIP_RPIPE 0x05 void turnOnLight(const int deviceHandle) { if(deviceHandle < 0) return; struct usbdevfs_ctrltransfer ctrl; const int tm_out = 1000; //0; const int buf_len = 2; unsigned char tx_buf[buf_len] = { 0, 0 }; //case eAllLeds: // F201, F300. tx_buf[0] = 0xF2; tx_buf[1] = 0x01; ctrl.bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT; // 0x21; ctrl.bRequest = 0x01; ctrl.wValue = 0x200; ctrl.wIndex = 0x400; ctrl.wLength = buf_len; ctrl.timeout = tm_out; ctrl.data = (void*)tx_buf; int ans = ::ioctl(deviceHandle, USBDEVFS_CONTROL, &ctrl); if(ans < 0) perror("sending 0xF201 is failed, perror output:"); tx_buf[0] = 0xF3; tx_buf[1] = 0x00; ctrl.bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT; //0x21; ctrl.bRequest = 0x01; ctrl.wValue = 0x200; ctrl.wIndex = 0x400; ctrl.wLength = buf_len; ctrl.timeout = tm_out; ctrl.data = (void*)tx_buf; ans = ::ioctl(deviceHandle, USBDEVFS_CONTROL, &ctrl); if(ans < 0) perror("sending 0xF300 is failed, perror output:"); } void turnOffLight(const int deviceHandle) { if(deviceHandle < 0) return; struct usbdevfs_ctrltransfer ctrl; const int tm_out = 1000; //0; const int buf_len = 2; unsigned char tx_buf[buf_len] = { 0, 0 }; //case eLedOff: // F200, F300. tx_buf[0] = 0xF2; tx_buf[1] = 0x00; ctrl.bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT; //0x21; ctrl.bRequest = 0x01; ctrl.wValue = 0x200; ctrl.wIndex = 0x400; ctrl.wLength = buf_len; ctrl.timeout = tm_out; ctrl.data = (void*)tx_buf; int ans = ::ioctl(deviceHandle, USBDEVFS_CONTROL, &ctrl); if(ans < 0) perror("sending 0xF200 is failed, perror output:"); tx_buf[0] = 0xF3; tx_buf[1] = 0x00; ctrl.bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT; //0x21; ctrl.bRequest = 0x01; ctrl.wValue = 0x200; ctrl.wIndex = 0x400; ctrl.wLength = buf_len; ctrl.timeout = tm_out; ctrl.data = (void*)tx_buf; ans = ::ioctl(deviceHandle, USBDEVFS_CONTROL, &ctrl); if(ans < 0) perror("sending 0xF300 is failed, perror output:"); } int main(void) { int deviceHandle = ::open("/dev/bus/usb/001/006", (O_RDWR | O_NONBLOCK)); if(deviceHandle < 0) { printf("Opening device is failed.\n"); return 0; } #if 1 turnOnLight(deviceHandle); #else turnOffLight(deviceHandle); #endif if(deviceHandle > (-1)) ::close(deviceHandle); return 0; }
解决方案
1. 排查设备占用情况
“设备或资源忙”的核心原因是设备已被其他进程或驱动占用,可通过以下命令确认:
- 查看占用设备的进程:
lsof /dev/bus/usb/001/006 - 检查是否被V4L2驱动接管:
v4l2-ctl --list-devices,如果相机出现在列表中,说明已被视频驱动占用。
找到占用进程后关闭它,再重新测试程序。
2. 临时卸载冲突内核驱动
如果相机被uvcvideo这类通用USB视频驱动占用,可临时卸载:
sudo rmmod uvcvideo
完成控制指令发送后,可重新加载驱动恢复相机成像功能:
sudo modprobe uvcvideo
3. 修改设备打开模式
尝试去掉O_NONBLOCK标志,以阻塞模式打开设备:
deviceHandle = ::open("/dev/bus/usb/001/006", O_RDWR);
部分USB设备在非阻塞模式下不支持控制传输,阻塞模式能保证指令同步执行。
4. 验证USB控制参数正确性
检查usbdevfs_ctrltransfer的参数是否匹配相机厂商规范:
wIndex:当前设置为0x400,对应接口编号0(低8位为0),可通过lsusb -v -d a168:0970查看设备接口描述符,确认目标控制接口的真实编号。bRequestType:如果相机使用厂商自定义指令,需将USB_TYPE_CLASS改为USB_TYPE_VENDOR。wValue、bRequest:对照相机厂商提供的控制协议文档,确认这些值是否正确。
5. 配置设备访问权限
确保程序有足够权限访问USB设备:
- 直接以root身份运行:
sudo ./your_program - 或添加udev规则给普通用户授权:
创建文件/etc/udev/rules.d/99-my-camera.rules,内容为:
重新加载udev规则:SUBSYSTEM=="usb", ATTRS{idVendor}=="a168", ATTRS{idProduct}=="0970", MODE="0666"sudo udevadm control --reload-rules sudo udevadm trigger
内容的提问来源于stack exchange,提问作者Wunian
相关产品推荐
相关产品推荐

