本人在RK3308 / RK3399 平台上尝试做一个音频hid设备。按照linux官方库的源码 kernel/driver/usb/gadget/function/f_hid.c 实现了一个hid设备,但是在使用过程中发现了一个问题,就是每次我的hid设备进行一次热插拔以后,或者我通过主机上的 "UsbTreeView" Restart Device 或者 Restart Port 后。开发板上hid驱动程序内的 usb_ep_queue(ep, req_buf, GFP_ATOMIC) 就没法通过hid设备的端点发送数据了,并且 usb_ep_queue(ep, req_buf, GFP_ATOMIC) 会返回值 -108 ,以及 req->status 的值会变为 -ESHUTDOWN,git仓库内有我实现hid驱动的源码和配置,恳请各位大佬们能帮你看一下指导一二,给个解决这个问题的方向。本人已经持续努力了很长一段时间,但是一直找不到解决问题的办法,想放弃改用legacy实现,但是又十分不甘心!!!
最后希望能尽可能够使用function 这种方式实现这个hid设备,因为我在这个方向上已经花费大量的时间和精力,特别想要获取一个答案。
-
方法一:应用层使用文件系统读写失败,时,可动态操作节点,实现热插拔,重新加载sd卡设备
方法二:硬件上增加一个sd卡检测引脚,拔掉卡时,判断,比方法一有更快的时效性和准确性
你遇到的这个问题可能与HID设备的状态有关,具体而言,可能是USB传输协议中的USB协议栈和HID设备的事件处理程序之间发生了状态不匹配。这可能是由于你的HID设备在进行热插拔时没有正确地向USB主机通知状态更改或恢复设备状态。这可能会导致USB协议栈和HID设备的状态不匹配,进而导致无法正常传输数据。
解决此问题可能需要在你的HID设备驱动程序中更改一些代码,特别是在处理设备状态的代码中。以下是一些可能有用的建议:
确保你的HID设备驱动程序正确地实现了必要的USB设备状态管理代码。在你的驱动程序中,你需要监听HID设备的状态,并在状态更改时向USB主机发送应答指令以告知主机设备状态。尤其需要关注设备的挂起状态和恢复状态。
确认你的HID设备驱动程序实现了正确的异步通信机制,确保它正确地使用异步请求以确保在传输数据时不会阻塞。
确认你的HID设备驱动程序正确地实现了USB HID 传输规范,同时确保你的HID设备与主机之间的HID报告通信是完全匹配和正确的。如果有任何问题,请检查你的HID驱动程序和HID设备固件,以确保所有使用的协议和数据格式都是正确的。
当你的设备发生错误时,记得记录错误信息。你可以使用Linux内核提供的调试工具,例如Kprobes或tracepoint,或者使用GDB和其他调试工具来调试你的驱动程序。
如果你的USB HID驱动程序仍然不能正常工作,请考虑使用标准的USB HID驱动程序库或其他兼容的API,以确保你的硬件与USB HID传输规范匹配。
以上是一些可能的建议,可以帮助你解决HID设备驱动程序的问题。在解决问题之前,你可能需要更仔细地审查你的驱动程序代码,并参考其他可用的资源,例如Linux内核源代码和其他驱动程序的参考实现,来确定你的驱动程序是否有问题。如果你无法解决问题,建议你向相关论坛寻求帮助,与更专业的开发者们交流并寻求解决方案。
对内核进行了配置修改吗?还是只做了驱动的修改~
可以借鉴下,基于chitgpt部分引用
在Linux环境下,USB Function驱动程序是一个内核模块,它提供了一种将Linux设备作为USB设备暴露给USB主机的方法。当USB主机连接到USB设备时,USB Function驱动程序将负责控制数据传输。但是,USB Function驱动程序不支持热插拔,因为该模块在内核中启动时就已经加载了。如果要支持热插拔,您需要使用USB Gadget框架,该框架支持动态加载和卸载USB驱动程序。
对于您的问题,如果需要支持热插拔,您需要使用USB Gadget框架来实现驱动程序。您可以参考以下步骤:
首先,您需要在内核配置中启用USB Gadget框架。可以通过make menuconfig命令进入内核配置界面,找到USB Gadget支持选项并启用。
在代码中使用USB Gadget API来实现驱动程序。您可以使用libcomposite库来简化这个过程。该库提供了一些函数,可以让您创建一个虚拟USB设备,该设备可以被主机识别为HID设备。例如,您可以使用函数usb_f_hid_setup来创建一个HID设备。
在驱动程序代码中实现热插拔功能。当USB设备插入或拔出时,您需要检测到此事件并相应地处理。可以使用函数usb_gadget_register_driver和usb_gadget_unregister_driver来注册和注销驱动程序。
这个问题可能是由于驱动没有正确处理热插拔事件导致的。如果设备被拔掉或者重启后,驱动仍然试图发送数据到一个已经不存在的端点上,就会出现这种错误。
解决这个问题需要在驱动中添加对热插拔事件的处理逻辑,当设备被拔掉或者重新插入时,驱动应该立即将相关的端点关闭并释放资源。具体可以参考Linux内核中其他USB gadget驱动的实现方式。
另外,您可以尝试使用USB设备模拟器工具(如VirtualHere)来模拟USB设备进行测试,以避免频繁的热插拔操作对硬件造成的损害。
新补充一些debug收获,发现设备从主机拔掉以后,设备会打印这些信息 “ [ 22.139272] dwc2 ff400000.usb: dwc2_hsotg_ep_stop_xfr: timeout DIEPINT.NAKEFF
[ 22.139492] dwc2 ff400000.usb: dwc2_hsotg_ep_stop_xfr: timeout DOEPCTL.EPDisable
[ 282.874836] dwc2 ff400000.usb: dwc2_hsotg_ep_stop_xfr: timeout DOEPCTL.EPDisable“
我想图片中的那样向设备发送设置接口的信号,也会打印出上面这些信息。然后我的设备就无法向我的主机发送/接收数据了。奇怪的是他这个对uac没有影响,并且向uac驱动发送设置接口的命令也不会出现上面那些打印信息。现在可以知道的就是出现上面的打印信息后,我的hid设备就失效了。
基于ChatGPT4与博主叶秋学长的回答,望采纳!!!有其他问题也可以询问我哦💕:
你遇到的问题可能是由于热插拔后系统重新枚举设备导致的。在重新枚举设备时,旧的设备文件描述符失效,所以调用 ioctl 或 write 等操作时会返回错误。
为了处理这个问题,你可以在驱动程序中使用 USB Gadget 驱动提供的必要函数来检测设备的连接状态。当设备被拔出时,你可以释放资源并清除记录在内核空间中的数据结构。当设备被插入时,你可以重新初始化设备并分配新的资源。
具体实现过程可能会涉及到以下步骤:
在驱动程序中注册 USB 设备控制器,并将 HID 作为一个子设备注册到它上面;
实现一个工作队列或者延迟工作队列,在队列的回调函数中检查设备的连接状态,并根据需要进行设备重置和资源分配;
当设备被拔出时,通过调用 USB Gadget 驱动提供的接口函数完成设备的资源释放和清理工作;
当设备被插入时,通过调用 USB Gadget 驱动提供的接口函数完成设备的资源分配和初始化工作。
希望这些信息对你有所帮助,如果你仍然遇到困难,请告诉我具体错误信息和调试方法,我会尽力帮助你。
仅供参考:
在Linux环境下,使用USB Function实现的HID设备驱动通常不支持热插拔。这是因为USB Function驱动程序是在系统启动时加载的,并且与USB HID设备通信的方式通常是通过USB端口进行的。
当插入或拔出USB设备时,USB端口的状态会发生变化,这可能会导致USB Function驱动程序无法正确处理USB设备的事件。此外,由于USB Function驱动程序是静态链接到系统中的,因此在插入或拔出USB设备时,它将被卸载和加载,这可能会导致系统性能下降。
为了解决这个问题,可以使用动态链接库(如libusb-1.0)来实现HID设备驱动程序。这些库允许您在运行时加载和卸载驱动程序,从而使您的应用程序能够更好地处理USB设备的热插拔。
在Linux环境下,如果使用USB Function实现HID设备驱动,可能会存在不支持热插拔的问题。这是因为在Linux中,USB设备的热插拔支持需要在内核中启用相应的模块。如果USB Function驱动没有启用该模块,就无法支持热插拔。
解决这个问题的方法是,在编译内核时启用相应的模块,或者在运行时加载相应的模块。具体的步骤可以参考Linux内核文档和相关的论坛帖子。另外,还可以考虑使用udev等工具来管理USB设备的插拔,从而避免热插拔的问题。
udev:用来监听硬件设备是否发生改变,并可以给硬件设备命名 ,也可以在硬件发生改变之后执行脚本
使用udev检测显示器是否发生变化,然后执行脚本,解决linux显示器热插拔问题
先补充一点:
[root@localhost ~]# cat /sys/class/drm/card0-VGA-1/status
connected
connectd 表示VGA口处于连接状态
拔掉则显示disconnected 脚本里面可以通过这来判断显示器是否连接
vim /etc/udev/rules.d/99-monitor-watch.rules
ACTION=="change", SUBSYSTEM=="drm", RUN+="/usr/local/bin/watch_monitor.sh" #当显示器发生改变的时候执行脚本
vim /usr/local/bin/watch_monitor.sh
```c
#!/bin/bash
DEVICES=$(find /sys/class/drm/*/status)
while read l
do
dir=$(dirname $l);
status=$(cat $l);
dev=$(echo $dir | cut -d\- -f 2-);
if [ $(expr match $dev "HDMI") != "0" ]
then
#REMOVE THE -X- part from HDMI-X-n
dev=HDMI${dev#HDMI-?-}
else
dev=$(echo $dev | tr -d '-')
fi
if [ "connected" == "$status" ]
then
echo $dev "connected"
DP1=$dev
fi
done <<< "$DEVICES"
function dp_connect(){
ps aux |grep Xorg|grep tty1|awk '{print $2}'|xargs -x kill
# xrandr --output $DP1 --auto
echo "dp_connect"
}
function dp_disconnect(){
echo "dp_disconnect"
}
test $DP1 &> /dev/null && dp_connect || dp_disconnect
chmod +x /usr/local/bin/watch_monitor.sh
缺点:每次插上显示器 都会回到登录页面,
2,使用xrandr的方式实现热插拔
vim /etc/udev/rules.d/99-monitor-watch.rules
KERNEL=="card0", SUBSYSTEM=="drm", ENV{DISPLAY}=":0", RUN+="/usr/bin/xrandr --output VGA1 --auto"
udevadm control --reload-rules
可以使用xrandr的方式来显示。这样就不会回到登录页面
二: 使用udev解决usb自动挂载
vim /etc/udev/rules.d/100-mountsda.rules
1 KERNEL=="sd[b-z]?",SUBSYSTEM=="block",RUN+="/usr/local/src/mountusb.sh %k $env{ACTION}"
2 KERNEL=="sd[b-z]",SUBSYSTEM=="block",RUN+="/usr/local/src/mountusb.sh %k $env{ACTION}"
因为本地磁盘是sda 所以从b开始
支持ntfs格式需要安装ntfs-3g 包存在扩展源,所以要先安装epel-release,然后编写自动挂载脚本
yum install epel-release -y
2 yum install ntfs-3g -y
3
4 vim /usr/local/src/mountusb.sh
5
6 #!/bin/sh
7 DEV_NODE="/dev/"$1
8 MOUNT_NODE="/mnt/"$1
9 echo -e "$(date) \n $2 " >> /tmp/mount.txt
10 if [ $2 == "add" ]; then
11 test ! -e $MOUNT_NODE && mkdir $MOUNT_NODE
12 ntfs-3g -o sync $DEV_NODE $MOUNT_NODE || mount -o sync -o iocharset=utf8 $DEV_NODE $MOUNT_NODE
13 #fi
14 elif [ $2 == "remove" ]; then
15 umount -l $MOUNT_NODE
16 rm -fr $MOUNT_NODE
17 fi
```
这个问题可能是由于热插拔时驱动程序没有正确处理请求,或者是一些手动释放内存资源的原因导致的。具体的解决方法如下:
1.在驱动程序中正确处理请求,对于热插拔后的请求,可以重新分配内存资源,并重新初始化相应的请求结构体。可以在驱动程序的 probe 和 disconnect 函数中进行相应的处理。
2.确认内存资源的释放是否正确,可以在文件系统下运行 mount 命令,然后在 disconnect 函数中卸载文件系统并释放相关资源。
3.可以使用内核提供的带调试信息的版本进行调试,找到具体的错误原因。
希望以上方法能够帮助你解决问题。
试一试通过编写udev规则,让Linux系统在检测到设备插入或拔出时自动加载或卸载驱动程序。可以在/etc/udev/rules.d/目录中编写一条规则文件,例如:
SUBSYSTEM=="usb", ATTRS{idVendor}=="1234", ATTRS{idProduct}=="5678", RUN+="/sbin/modprobe my_hid_driver"
SUBSYSTEM=="usb", ATTRS{idVendor}=="1234", ATTRS{idProduct}=="5678", RUN+="/bin/sh -c 'echo 1 > /sys/bus/usb/devices/%k/autosuspend'"
其中,idVendor和idProduct应该替换为实际设备的Vendor ID和Product ID,my_hid_driver应该替换为实际的驱动程序名。
Linux 系统默认会将 USB HID 设备驱动绑定到特定的 USB 接口号上,导致热插拔功能失效。可以使用 udev 规则添加一个自定义规则,使系统动态地加载和卸载 USB HID 设备驱动,以支持热插拔
我查阅了你提供的GitHub链接,这个仓库是Linux的gadget function HID(Human Interface Device)驱动实现【9†source】。根据我所能获取的信息,这个驱动实现主要包括了以下部分:
另外,我还查阅了其中的 "u_hid.h" 文件,这个文件主要包括了HID function的一些实用定义,比如 "f_hid_opts" 结构体和 "ghid_setup" 和 "ghid_cleanup" 函数【17†source】。
关于你的问题,即Linux环境通过usb function实现的hid设备驱动不支持热插拔,我尝试搜索相关信息,但未能找到足够的信息来帮助解决这个问题。根据你的描述,我理解你的问题可能是在热插拔USB设备时,HID设备驱动未能正确响应。这可能涉及到设备的初始化和清理过程,也可能涉及到设备的电源管理或者是USB接口的状态管理等。
热插拔问题通常涉及到底层的硬件处理和操作系统的设备管理,这可能需要你深入理解USB协议,HID设备的工作原理,以及Linux的USB和设备驱动框架。你可能需要检查你的驱动代码,特别是设备的初始化和清理部分,以及任何与设备状态相关的部分。
另外,你可能需要使用一些工具来调试这个问题,比如dmesg命令可以用来查看内核的消息,lsusb命令可以用来列出当前连接的USB设备,usbmon工具可以用来监控USB设备的活动。
我建议你可以尝试以下几点:
如果你能提供更多的详细信息,比如你的驱动代码,设备的具体型号,以及在设备插入和拔出时的内核消息等,我可能能提供更具体的帮助。