Open wang723-stack opened 11 months ago
好奇你的libusb demo是怎麼寫的? 因為Official OpenOCD也有類似問題! 目前測試是因為libusb_get_device_list不會返回對應的設備清單,會讓開啟的過程中找不到該裝置。 libusb官網是有提到可能會有macOS kernel extension占用裝置(Ref: https://github.com/libusb/libusb/wiki/FAQ#how-can-i-run-libusb-applications-under-mac-os-x-if-there-is-already-a-kernel-extension-installed-for-the-device-and-claim-exclusive-access ),不過目前還沒走到那邊就會因為找不到裝置而結束,詳細原因還未知!
最下面的代码这个 demo 的全部代码,这个 demo 几乎是 libusb api 最基础的用法,其中,get_interface_no 函数是为了找到支持 bulk transfer 的接口,用到了 libusb_get_active_config_descriptor 函数;这个 demo 运行下来似乎表明,对于 libusb,list usb设备,得到一个 handle 并且 claim 一个接口的过程中需要用到的 api 都不存在权限问题并且能正常运行
程序流程大概是:
利用 libusb 打开 usb 设备并通信应该都是这个流程?还是说 ICEman 用到了其他的方式。
这是运行截图:
这是用 lldb 运行 ICEman 的截图,这似乎提示在调用 libusb_get_active_config_descriptor 时出现了问题?
#include <stdio.h>
#include <stdlib.h>
#include <libusb-1.0/libusb.h>
// #include <mcheck.h>
typedef struct libusb_interface_descriptor interface_t;
typedef struct libusb_endpoint_descriptor endpoint_t;
libusb_device **devList = NULL;
ssize_t devNum = 0;
libusb_device_handle *devHandle = NULL;
typedef struct {
uint8_t endpoint_address_in;
uint8_t endpoint_address_out;
unsigned int interface_no;
} usb_config;
static int get_interface_no(libusb_device *dev, usb_config *usb) {
struct libusb_config_descriptor *config;
const interface_t *interface;
const endpoint_t *endpoint;
int count = 0;
int ret = -1;
libusb_get_active_config_descriptor(dev, &config);
interface = config->interface->altsetting;
for(int i = 0; i < config->interface->num_altsetting; i++) {
count = 0;
endpoint = interface[i].endpoint;
for(int j = 0; j < interface[i].bNumEndpoints; j++) {
uint8_t type = endpoint[j].bmAttributes;
// printf("bmAttributes : %02x\n", type);
uint8_t direct = endpoint[j].bEndpointAddress;
// printf("bEndpointAddress : %02x\n", direct);
if((type & (0x0f >> (4 - 2))) == LIBUSB_TRANSFER_TYPE_BULK) {
if((direct & (0xf0 << (4 - 1))) == LIBUSB_ENDPOINT_OUT) {
usb->endpoint_address_out = endpoint[j].bEndpointAddress;
count++;
if(count == 2) {
usb->interface_no = i;
break;
}
}
else {
usb->endpoint_address_in = endpoint[j].bEndpointAddress;
count++;
if(count == 2) {
usb->interface_no = i;
break;
}
}
}
}
if(count == 2) {
usb->interface_no = i;
ret = 0;
break;
}
}
libusb_free_config_descriptor(config);
// printf("out : %02x\nin : %02x\nno : %02x\n", usb->endpoint_address_out, usb->endpoint_address_in,
// usb->interface_no);
return ret;
}
int main(int argc, char **argv) {
// setenv("MALLOC_TRACE", "output", 1);
// mtrace();
usb_config *usbConfig = (usb_config *)malloc(sizeof(usb_config));
int ret = libusb_init(NULL);
if(ret < 0) {
printf("libusb init fail : ret : %d\n", ret);
return -1;
}
int index = 0;
int dev_n = 0, bus_n = 0;
char Manufacturer[128] = {0};
char Product[128] = {0};
libusb_device_handle *handle = NULL;
devNum = libusb_get_device_list(NULL, &devList);
for(int i = 0; i < devNum; i++) {
libusb_open(devList[i], &handle);
struct libusb_device_descriptor desc;
libusb_get_device_descriptor(devList[i], &desc);
bus_n = libusb_get_bus_number(devList[i]);
dev_n = libusb_get_device_address(devList[i]);
if(handle != NULL) {
libusb_get_string_descriptor_ascii(handle, desc.iManufacturer, Manufacturer, 128);
libusb_get_string_descriptor_ascii(handle, desc.iProduct, Product, 128);
}
printf("USB Device[%d] : ", i + 1);
printf("Bus %03d Device %03d: ID %04x:%04x %s %s\n",
bus_n, dev_n, desc.idVendor, desc.idProduct, Manufacturer, Product);
if(handle != NULL)
libusb_close(handle);
}
printf("Input the index of usb that you wanna use (0 to exit) : ");
scanf("%d", &index);
if(index > (devNum - 1) || index == 0) {
goto exit;
}
ret = libusb_open(devList[index - 1], &devHandle);
if(ret != 0) {
printf("libusb open fail : ret : %d\n", ret);
goto exit;
}
if(libusb_kernel_driver_active(devHandle, 0) == 1) {
// printf("tlk open : Kernel Driver Active\n");
libusb_detach_kernel_driver(devHandle, 0);
}
ret = get_interface_no(devList[index - 1], usbConfig);
if(ret != 0) {
printf("get end addr fail\n");
goto exit;
}
ret = libusb_claim_interface(devHandle, usbConfig->interface_no);
if(ret < 0) {
printf("Cannot Claim Interface : ret : %d\n", ret);
libusb_attach_kernel_driver(devHandle, 0);
libusb_close(devHandle);
goto exit;
}
printf("Get usb handle and Claim Interface!\n");
libusb_close(devHandle);
exit:
libusb_free_device_list(devList, 1);
libusb_exit(NULL);
// muntrace();
return 0;
}
@wujiheng 对了,这是在我的 macOS 中运行时 ICEman,不使用 sudo 情况下的 log 信息
@wujiheng 您好,由于 ICEman 的代码量有些大,之前没有接触过 openocd 类似的项目,可以请教一下 ICEman 程序从启动到找到并打开 AICE USB 设备的具体流程吗,就是具体的函数调用顺序;我也需要测试一下为什么 libusb 的接口会工作异常,因为在我本地的其他程序测试中是没有异常的,十分感谢!
@YuraGakki 抱歉沒看到這則訊息,如果需要參考FTDI 打開裝置的流程,可以參考這邊 mpsse.c中的open_matching_device(),下面是簡單的流程:
大概是這樣
@wujiheng 谢谢,我利用 lldb 也追踪到了这个流程,不过我的测试中,libusb_get_device_list 这个接口是可以正常工作的,程序可以正确的找到指定 vid pid 的设备;不过有一个很明显的事情是,在没有 sudo 权限的情况下,libusb_detach_kernel_driver() 这个接口几乎肯定会返回 LIBUSB_ERROR_ACCESS 的错误码(没有权限),而 ICEman 中对于这个函数没能成功执行的处理是直接 continue,这相当于直接忽略了该设备,哪怕它能够被成功打开,这也就导致没有 sudo 的情况下,几乎所有的设备都被忽略;所以我把这句 continue 注释掉,发现 ICEman 在没有 sudo的情况下也可以正常工作了。具体测试是 ICEman 配合 riscv32-elf-gdb ,可是实现对 mcu 的 remote debug,这应该可以说明 在没有 detach kernel 的情况下 ICEman 和 USB 设备也可以正常通信,这可能是因为我的环境中,除了 ICEman 应该没有别的进程会试图和 Andes 的设备通信。不知道这能否成为你们测试该问题时的参考。
另外,我不清楚 _NDS_V5ONLY 这个宏在哪里被定义,但至少根据 build.sh 编译出的程序中,这个宏应该是存在的。上面说的 continue 的处理,只在 #if _NDS_V5ONLY 成立时是这么执行的,在 #if _NDS_V5ONLY 不成立时,代码中也调用了 libusb_detach_kernel_driver(),但此时即使失败也没有 continue,不知道这么区分是出于哪一种考虑?不过这是否意味着 libusb_detach_kernel_driver() 不是一定要成功执行才可以?如果是的话,看起来去掉 mpsse.c 第 280 行的 continue 似乎能够解决问题。
當初會這樣設計是基於Linux中的ftdi_sio會占用FTDI相關的adapter,所以在程式中先呼叫libusb_detach_kernel_driver將相關的kernel module退掉,當退出失敗後,libusb_claim_interface會直接失敗,不確定是否為Red Hat才有的問題還是其他distribution也會遇到,所以才改flow成這樣的狀況。
不過更奇怪的是,只要跑過一次你提供的demo,ICEman也就不需要sudo,這個現象我到現在還沒有想明白,不是很熟macOS底層的操作
macOS 下的 ICEman 需要 sudo 权限才能打开设备(linux 也是),但奇怪的是,在 macOS 下,我自己写的 libusb demo 可以打开 andes 设备,没有使用 sudo 权限,这说明该设备的控制权限是被释放给普通用户的,ICEman 控制设备不也是使用的 libusb 吗,这个问题可能出在哪里