Closed HelloByeAll closed 1 year ago
Thanks for the feedback.
The issue should be in _ux_host_class_hid_client_search, where linked client should not be used for new hid client. Please try following modification:
ux_host_class_hid_client_search.c, L110:
/* Check if this HID client is registered and not locally used. */
if ((hid_client -> ux_host_class_hid_client_status == UX_USED) &&
(hid_client -> ux_host_class_hid_client_local_instance != UX_NULL))
下
感谢,稍后我会尝试一下这个修改,另外还有一些关于OHCI控制器的问题在 https://github.com/azure-rtos/usbx/issues/51
Thanks for the feedback.
The issue should be in _ux_host_class_hid_client_search, where linked client should not be used for new hid client. Please try following modification:
ux_host_class_hid_client_search.c, L110:
/* Check if this HID client is registered and not locally used. */ if ((hid_client -> ux_host_class_hid_client_status == UX_USED) && (hid_client -> ux_host_class_hid_client_local_instance != UX_NULL))
这里的改法应该是
/* Check if this HID client is registered and not locally used. */
if ((hid_client -> ux_host_class_hid_client_status == UX_USED) &&
(hid_client -> ux_host_class_hid_client_local_instance == UX_NULL))
否则将枚举不成功设备。
另外这并是不一个好的解决方法,有些鼠标本身是鼠标+键盘的复合设备,一旦插入这个鼠标那么新插入的键盘将不会再被成功枚举。 我理解的是这里应该建立一个ux_host_class_hid_client_local_instance 的链表,我们并不阻止相同的设备插入,我们应该让他们共存
没错上文改法有笔误。
另外对于local instance在这个文件需要重置为空:ux_host_class_hid_deactivate.c
/* Call the HID client with a deactivate command if there was a client registered. */
if (hid -> ux_host_class_hid_client != UX_NULL)
{
hid -> ux_host_class_hid_client -> ux_host_class_hid_client_handler(&hid_client_command);
hid -> ux_host_class_hid_client -> ux_host_class_hid_client_local_instance = UX_NULL;
}
这样弹出的hid可以释放hid client 的 local instance并重复利用。
关于复合设备,是不同的interface作为不同的hid存在么?那种对现在的实现是没有问题的,现在的实现每个interface对应一个hid instance,和一个hid client,键盘鼠标会枚举得到两个hid client。
如果同一个interface中包括多种hid client这个实现的确有问题,因为目前每个interface不能关联多个client。但这个实现相对修改会比较大,如果有切实的需求可作为新功能进行开发。
没错上文改法有笔误。
另外对于local instance在这个文件需要重置为空:ux_host_class_hid_deactivate.c
/* Call the HID client with a deactivate command if there was a client registered. */ if (hid -> ux_host_class_hid_client != UX_NULL) { hid -> ux_host_class_hid_client -> ux_host_class_hid_client_handler(&hid_client_command); hid -> ux_host_class_hid_client -> ux_host_class_hid_client_local_instance = UX_NULL; }
这样弹出的hid可以释放hid client 的 local instance并重复利用。
关于复合设备,是不同的interface作为不同的hid存在么?那种对现在的实现是没有问题的,现在的实现每个interface对应一个hid instance,和一个hid client,键盘鼠标会枚举得到两个hid client。
如果同一个interface中包括多种hid client这个实现的确有问题,因为目前每个interface不能关联多个client。但这个实现相对修改会比较大,如果有切实的需求可作为新功能进行开发。
不是,就目前的代码来看,鼠标或者键盘只能各自拥有一个hid instance
, 如果插入2个鼠标的话, 第二个鼠标会因为 hid instance != UX_NULL
导致枚举不上,我认为的改进方法是在这个结构体中维护一个链表 struct UX_HOST_CLASS_HID_CLIENT_STRUCT *ux_host_class_hid_client_next;
在第二个鼠标插入时这个新的 hid instance
应从原来的 hid instance
中继承 name、ux_host_class_hid_client_function
和ux_host_class_hid_client_handler
参数,同时加入ux_host_class_hid_client_nb
参数用来表明当前类型的客户端中hid instance
链表的长度。
typedef struct UX_HOST_CLASS_HID_CLIENT_STRUCT
{
ULONG ux_host_class_hid_client_status;
#if defined(UX_NAME_REFERENCED_BY_POINTER)
UCHAR *ux_host_class_hid_client_name;
#else
UCHAR ux_host_class_hid_client_name[UX_HOST_CLASS_HID_MAX_CLIENT_NAME_LENGTH + 1]; /* "+1" for string null-terminator */
#endif
UINT (*ux_host_class_hid_client_handler) (struct UX_HOST_CLASS_HID_CLIENT_COMMAND_STRUCT *);
VOID *ux_host_class_hid_client_local_instance;
#if defined(UX_HOST_STANDALONE)
VOID (*ux_host_class_hid_client_function)(struct UX_HOST_CLASS_HID_CLIENT_STRUCT *);
#endif
} UX_HOST_CLASS_HID_CLIENT;
typedef struct UX_HOST_CLASS_HID_CLIENT_STRUCT
{
ULONG ux_host_class_hid_client_nb;
ULONG ux_host_class_hid_client_status;
#if defined(UX_NAME_REFERENCED_BY_POINTER)
UCHAR *ux_host_class_hid_client_name;
#else
UCHAR ux_host_class_hid_client_name[UX_HOST_CLASS_HID_MAX_CLIENT_NAME_LENGTH + 1]; /* "+1" for string null-terminator */
#endif
UINT (*ux_host_class_hid_client_handler) (struct UX_HOST_CLASS_HID_CLIENT_COMMAND_STRUCT *);
VOID *ux_host_class_hid_client_local_instance;
struct UX_HOST_CLASS_HID_CLIENT_STRUCT *ux_host_class_hid_client_next;
#if defined(UX_HOST_STANDALONE)
VOID (*ux_host_class_hid_client_function)(struct UX_HOST_CLASS_HID_CLIENT_STRUCT *);
#endif
} UX_HOST_CLASS_HID_CLIENT;
发现问题了,现在hid会链接注册的client,注册的client再链接到client instance,这样如果要支持多个鼠标得注册多个鼠标client,而目前只能注册一个同类型的client……
修改应该可以保持和interface - class处理方式一致,在把注册的client里的local instance移到hid里,hid直接链接到根据注册的client创建的instance,这样注册的client就不会限制到底有多少client instance了
上文的改动可能会影响原有HID API的使用(至少之前的hid report callback结构就得改,不然没法定位到具体的client instance),考虑到兼容性,可能的修改方式是不改变结构的情况下,以原注册的client为模板,在创建instance的同时创建一份拷贝以实际使用,这样在增加不多的运存使用的情况下可以兼顾之前的使用方式。
E.g., mouse modifications:
common/usbx_host_classes/inc/ux_host_class_hid_mouse.h
typedef struct UX_HOST_CLASS_HID_CLIENT_MOUSE_STRUCT
{
UX_HOST_CLASS_HID_MOUSE ux_host_class_hid_client_mouse_mouse;
UX_HOST_CLASS_HID_CLIENT ux_host_class_hid_client_mouse_client;
} UX_HOST_CLASS_HID_CLIENT_MOUSE;
common/usbx_host_classes/src/ux_host_class_hid_mouse_activate.c
UX_HOST_CLASS_HID_CLIENT_MOUSE *client_mouse;
/* Get some memory for both the HID class instance and copy of this client
and for the callback. */
client_mouse = (UX_HOST_CLASS_HID_CLIENT_MOUSE *)
_ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY,
sizeof(UX_HOST_CLASS_HID_CLIENT_MOUSE));
if(client_mouse == UX_NULL)
return(UX_MEMORY_INSUFFICIENT);
/* Get client and mouse instance. */
mouse_instance = &client_mouse -> ux_host_class_hid_client_mouse_mouse;
hid_client = &client_mouse -> ux_host_class_hid_client_mouse_client;
_ux_utility_memory_copy(hid_client, hid -> ux_host_class_hid_client, sizeof(UX_HOST_CLASS_HID_CLIENT));
if (status == UX_SUCCESS)
{
/* Use our copy of client. */
hid -> ux_host_class_hid_client = hid_client;
/* If all is fine and the device is mounted, we may need to inform the application
if a function has been programmed in the system structure. */
if (_ux_system_host -> ux_system_host_change_function != UX_NULL)
{
/* Call system change function. */
_ux_system_host -> ux_system_host_change_function(UX_HID_CLIENT_INSERTION, hid -> ux_host_class_hid_class, (VOID *) hid_client);
}
/* If trace is enabled, insert this event into the trace buffer. */
UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_HID_MOUSE_ACTIVATE, hid, mouse_instance, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0)
/* Return completion status. */
return(status);
}
Fix is done.
USBX HID 设备问题现象:
以鼠标举例, 假设第一个插入的鼠标为1号鼠标, 第二个插入的为2号鼠标,2个鼠标都插入后,将2号鼠标拔出,此时1号鼠标也将不能使用。
更严重的影响:发生内存踩踏
2号鼠标被拔出后,它所使用的内存将被释放,此时1号鼠标动作虽然使用api获取的属性值(x/y坐标、按键值、滚轮值)没有改变,当实际原因是1号鼠标的属性值被更新到2号鼠标原先属性值所在的内存中去了。1号鼠标仍在操作原本已经被释放的2号鼠标的内存。
LOG分析:
鼠标插入:
从LOG上可以看出插入一号鼠标后 一号鼠标的
mouse_instance = 0x2116D830
接着插入2号鼠标mouse_instance = 0x21174140
从下面这2条LOG可以看出 他们所属的hid客户端是同一个,也就是调用
ux_host_class_hid_client_register(_ux_system_host_class_hid_client_mouse_name, ux_host_class_hid_mouse_entry)
注册鼠标客户端时malloc的一片内存。鼠标枚举LOG _ux_host_class_hid_mouse_activate
源码:
从这个.c的源码中也能看出,这里
hid_client -> ux_host_class_hid_client_local_instance = (VOID *) mouse_instance;
的操作会覆盖原本的这个值, 即使这个鼠标枚举失败了hid_client -> ux_host_class_hid_client_local_instance
也没有恢复操作,1号原本可以使用的对象就直接丢失了。(这里就不分析这个情况了,看代码就能理解了)LOG:
2号鼠标拔出:
从LOG中可以很清楚的看到2号鼠标已经被协议栈移除
free hid_client = 0x21163370 instance 0x21174140
并且已经释放了0x21174140这个地址的内存。源码:
LOG:
2号鼠标拔出后移动1号鼠标:
从下面日志可以看到
mouse_instance = 0x21174140
这个地址正是已经被释放了的2号鼠标所拥有的0x21174140
而不是1号本身的0x2116D830
源码中野可以看到,他下面直接对这个地址里的属性值做了修改,此时就发生了内存踩踏源码:
LOG:
hid客户端注册 ux_host_class_hid_client_register
完整日志: