You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Ubuntu 20.04下ioctl调用返回“设备或资源忙”问题的解决方法

问题:Ubuntu 20.04下通过ioctl控制USB相机返回“设备或资源忙”

环境与操作步骤

在Ubuntu 20.04系统中,尝试通过ioctl与USB相机寄存器收发数据,操作流程如下:

  1. 通过lsusb获取设备路径信息:
laptop05:~$ lsusb
Bus 001 Device 006: ID a168:0970 My Camera
  1. 打开设备句柄:
deviceHandle = ::open("/dev/bus/usb/001/006", (O_RDWR | O_NONBLOCK));
  1. 程序关闭时关闭设备句柄:
if(deviceHandle > (-1))
    ::close(deviceHandle);
  1. 发送控制数据的核心代码片段:
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,内容为:
    SUBSYSTEM=="usb", ATTRS{idVendor}=="a168", ATTRS{idProduct}=="0970", MODE="0666"
    
    重新加载udev规则:
    sudo udevadm control --reload-rules
    sudo udevadm trigger
    

内容的提问来源于stack exchange,提问作者Wunian

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 17:35:55