5

UEFI开发探索89- YIE002USB开发板(12 Linux编程)

 3 years ago
source link: http://yiiyee.cn/blog/2021/05/13/uefi%e5%bc%80%e5%8f%91%e6%8e%a2%e7%b4%a289-yie002usb%e5%bc%80%e5%8f%91%e6%9d%bf%ef%bc%8812-linux%e7%bc%96%e7%a8%8b%ef%bc%89/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

UEFI开发探索89- YIE002USB开发板(12 Linux编程)

请保留-> 【原文:  https://blog.csdn.net/luobing4365 和 http://yiiyee.cn/blog/author/luobing/】

对于USB HID的编程,在YIE002USB开篇时,就已经讨论过:

  1. 下位机是使用YIE002制作的USB HID设备,这是嵌入式开发;
  2. 上位机包括了Windows系统、UEFI系统和Linux系统。

上位机软件中,没有把Mac OSX包含进来,主要是因为我较少在苹果的系统上开发,而且也可以将其等同于Linux系统看待(同属于类Unix系)。

至今为止,Windows系统和UEFI系统,我们都已经完成了上位机的软件编写,下面该进入Linux的USB HID上位机软件开发了。

对于Linux下的HID,网站“The Linux Kernel”上有非常详细的描述,值得仔细阅读,网址为:
https://www.kernel.org/doc/html/latest/hid/index.html。

我们所要访问的设备,是USB HID设备。在Linux系统中,常用的USB开源库,是libusb。这是一个用C语言开发的,跨平台的USB设备访问接口库。

在Github上找到这个开源库后,通读了一遍其文档。理论上使用它,可以完整地实现我们所需要的功能。不过,文档中也建议,如果是访问HID设备的话,可以使用hidapi。

hidapi是一个开源的操作HID设备的C语言库,适用于Windows、Linux和Mac OSX平台。它是针对HID设备的,对于其他USB设备不合适。

其源码仓库为:https://github.com/libusb/hidapi,主要目录如下:

hidapi: 头文件(所有平台共用一份头文件)hidapi.h
libusb:Linux系统实现源码文件hid.c,使用libusb库实现的方式
linux:Linux系统实现源码文件hid.c,使用内核接口(hidraw)实现方式
windows:Windows系统实现源码文件hid.c
mac:Mac OSX 系统实现源码文件hid.c
hidtest:测试代码 test.c

目前我们要开发的,是linux下的USB HID访问程序。从其代码结构可以看出,有两种方式可以使用,也即hidraw和libusb库可以使用。

由于hidapi封装得比较好,两种开发方式没有太大的区别。我大部分的工作,都是在考虑如何编写编译用的Makefile文件。

本篇主要hidraw的方式来开发Linux下的访问代码。

2 hidapi的准备

在开发过程中,我使用了虚拟机(Ubuntu 16.04)和实际机器(Ubuntu 18.04)来进行实验。前者用来开发hidraw方式的代码,后者用来开发libusb方式的代码。

使用两种开发环境,是因为我发现libusb方式的代码,在虚拟机上运行有各种问题,具体原因未知。不过,两种方式开发的代码,在实际机器上,都运行得很好。

2.1 安装必要组件

下载hidapi库到本地:

robin@robin-virtual-machine:~$ git clone https://github.com/libusb/hidapi.git

hidapi库比较小,应该能下载下来。如果还是有问题的话,建议在gitee上找到对应的库,用同样的方法git到本地。

安装必要的组件:

robin@robin-virtual-machine:~$ sudo apt-get install libudev-dev libusb-1.0-0-dev

所需要的组件,可以参考hidapi的文档中的要求。开发过程中,不需要使用GUI,因此,libfox-1.6-dev不需要安装。

如果编译所需的工具没有安装的话,也需要安装:

robin@robin-virtual-machine:~$ sudo apt-get install  build-essential pkg-config

build-essential包含了编译所需的工具,包括gcc、make等。pkg-config是linux下的一个命令,用于获得某个库/模块的所有编译相关的信息,在编译过程中需要用到。

2.2 hidapi主要接口

初始化和退出:

hid_init():初始化函数,无参数。
hid_exit():退出,实际上是销毁结构体等,在完成所有工作后必须调用,否则会造成内存泄漏。
hid_enumerate():枚举设备,返回的是hid_device_info链表。一般使用hid_enumerate(0, 0)枚举所有设备。枚举一般用于获取设备ID或者设备路径。如果提前知道这些信息,亦可不用枚举。
hid_free_enumeration():释放枚举所用到的链表。

设备打开与关闭:

hid_open():打开指定 VID 和 PID 设备,返回设备结构体指针,如 hid_device *handle; handle = hid_open(0x8765, 0x4321, NULL)。设备读写和关闭函数时,均需要该结构体指针。
hid_open_path():根据设备路径打开设备,设备路径由hid_enumerate获取。如Linux下的 /dev/hidraw1。
hid_close():关闭设备。

Feature Report 收发:

hid_get_feature_report():获取 Feature report,也即采用Feature报告进行通信。
hid_send_feature_report():发送 Feature report。

读写函数:

hid_read():读取数据。
hid_write():写数据。

需要指出的是,网上不少文章,把hid_read()和hid_write()认为是读取Input report和发送Output report。这种理解是有问题的,从后面的实验可以看出,hid_read()和hid_write()并不是走的Input report和Output report的通信方式,至少这种理解不全面。

从实验结果来看,这两个函数,类似于Windows系统的ReadFile()和WriteFile()方式,而下位机对应的,是端点通信方式(参见第85篇博客)。也即在有多个端点(包括必有的端点0和其他端点)的情况下,走的是端点通信;只有端点0时,才会走Input report和Output report的通信方式。

实际上,在hidapi的源码中(inux文件夹下的hid.c),很明确地说到,hidraw方式没有实现Input report的获取。

当然,如何设计,还是取决于下位机的固件了。

3 使用hidapi的hidraw方式的示例

在了解了上述背景知识后,去实现需要的代码,就相对比较容易了。

hidapi提供了测试用的程序,位于hidtest文件夹下的test.c。linux版本的编译,可以在文件夹linux下,使用Makefile文件进行编译:

robin@robin-virtual-machine:~/hidapi/linux$ make -f Makefile-manual

编译之后,将会生成hidtest-hidraw执行文件。我们要在test.c的基础上,实现需要的功能。

3.1 准备开发目录

为方便后续的开发,需要自己建立开发用的文件夹,把需要的源文件和头文件包含进来。

新建文件夹hidraw,把hidapi的几个文件拷贝到此处:

linux/hid.c
hidapi/hidapi.h
hidtest/test.c

在文件夹linux下,有编译用的文件Makefile-manual。由于文件目录改变了,不能直接使用。可以在hidraw下新建Makefile,内容如下:

all: hidtest-hidraw
CC       ?= gcc
CFLAGS   ?= -Wall -g -fpic

LDFLAGS  ?= -Wall -g

COBJS     = hid.o test.o
OBJS      = $(COBJS)
LIBS_UDEV = `pkg-config libudev --libs` -lrt
LIBS      = $(LIBS_UDEV)
INCLUDES ?= `pkg-config libusb-1.0 --cflags`


# Console Test Program
hidtest-hidraw: $(COBJS)
	$(CC) $(LDFLAGS) $^ $(LIBS_UDEV) -o $@

# Objects
$(COBJS): %.o: %.c
	$(CC) $(CFLAGS) -c $(INCLUDES) $< -o $@

clean:
	rm -f $(OBJS) hidtest-hidraw $(COBJS)

.PHONY: clean libs

在hidraw文件夹下打开终端,输入make进行编译:

robin@robin-virtual-machine:~/luotest/hidapi$ make
cc -Wall -g -fpic -c `pkg-config libusb-1.0 --cflags` hid.c -o hid.o
cc -Wall -g -fpic -c `pkg-config libusb-1.0 --cflags` test.c -o test.o
cc -Wall -g hid.o test.o `pkg-config libudev --libs` -lrt -o hidtest-hidraw

可以看出,生成了可执行文件hidtest-hidraw。

下面将在test.c原有代码的基础上,实现USB HID设备的访问。

3.2 USB HID的hidraw方式代码开发

从之前的博客中我们知道,上位机有三种方式与USB HID设备进行通信。目前hidapi的代码中,提供了Feature report和hid_read()&hid_write()两种方式,没有提供Input report&Output report的通信方式。
主要是在Linux的hidraw中,并没有提供相应的接口。

插句题外话,我实在想不通,为什么在hidraw中,不提供Input report&Output report的接口。有知道的朋友,请在博客的评论区留言。

3.2.1 Feature report通信方式

Feature report通信,使用了hid.c中提供的两个函数:hid_send_feature_report()和hid_get_feature_report()。代码如下:

memset(yie_buf,0,sizeof(yie_buf));
	yie_buf[0] = 0x00;
	yie_buf[1] = 0xA0;
	yie_buf[2] = 0x0a;
	yie_buf[3] = 0x0b;
	yie_buf[4] = 0x0c;
	res = hid_send_feature_report(handle, yie_buf, 17);
	if (res < 0) {
		printf("Unable to send a feature report.\n");
	}

	memset(yie_buf,0,sizeof(yie_buf));

	// Read a Feature Report from the device
	// buf[0] = 0x2;
	res = hid_get_feature_report(handle, yie_buf, sizeof(yie_buf));
	if (res < 0) {
		printf("Unable to get a feature report.\n");
		printf("%ls", hid_error(handle));
	}
	else {
		// Print out the returned buffer.
		printf("Feature Report\n   ");
		printf("report number:%d\n   ",yie_buf[0]);
		for (i = 1; i < res; i++)
			printf("%02x ", yie_buf[i]);
		printf("\n");
	}

3.2.2 hid_read()&hid_write()通信方式

hid.c中提供了hid_read()和hid_write()函数,可实现类似Windows的ReadFile()和WriteFile()功能(仅对USB HID设备而言)。实现代码如下:

memset(yie_buf,0,sizeof(yie_buf));
	yie_buf[0] = 0x00;
	yie_buf[1] = 0xA0;
	yie_buf[2] = 0x0a;
	yie_buf[3] = 0x0b;
	yie_buf[4] = 0x0c;

	res = hid_write(handle, yie_buf, 17);
	if (res < 0) {
		printf("Unable to write()\n");
		printf("Error: %ls\n", hid_error(handle));
	}

	memset(yie_buf,0,sizeof(yie_buf));
	res = hid_read(handle, yie_buf, sizeof(yie_buf));
	if (res < 0) {
		printf("Unable to Read data.\n");
		printf("%ls", hid_error(handle));
	}
	else {
		// Print out the returned buffer.
		printf("Read data\n   ");
		for (i = 0; i < res; i++)
			printf("%02x ", yie_buf[i]);
		printf("\n");
	}

修改完后,使用make编译。插上自制的USB HID设备,运行测试代码,测试结果如下:

robin@robin-virtual-machine:~/luotest/hidapi$ sudo ./hidtest-hidraw
……//信息略
Manufacturer String: Robin
Product String: Robin's UEFI Explorer
Serial Number String: (77) My123
Unable to read indexed string 1
Indexed String 1: 
Feature Report
   report number:0
   a0 03 0b 0c 00 00 00 00 00 00 00 00 00 00 00 00 
Read data
   a0 01 0b 0c 00 00 00 00 00 00 00 00 00 00 00 00

注意返回数据的第2个字节,通过之前博客中的内容,可以知道已经实现了三种通信方式中的两种了。

为实现完整的三种通信方式,下一篇将使用libusb库重新构建代码。

Gitee地址:https://gitee.com/luobing4365/uefi-exolorer
项目代码位于:/ 89 hidraw下

169 total views, 1 views today


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK