刪除百度收錄網(wǎng)站百度灰色關(guān)鍵詞排名
文章目錄
- 1、聲明
- 2、HID協(xié)議
- 2.1、描述符
- 2.2、鼠標(biāo)數(shù)據(jù)格式
- 3、應(yīng)用程序
- 4、編譯應(yīng)用程序
- 5、測試
1、聲明
本文是在學(xué)習(xí)韋東山《驅(qū)動(dòng)大全》USB子系統(tǒng)時(shí),為梳理知識(shí)點(diǎn)和自己回看而記錄,全部內(nèi)容高度復(fù)制粘貼。
韋老師的《驅(qū)動(dòng)大全》:商品詳情
其對(duì)應(yīng)的講義資料:https://e.coding.net/weidongshan/linux/doc_and_source_for_drivers.git
libusb api:https://libusb.sourceforge.io/api-1.0/libusb_api.html
2、HID協(xié)議
HID:Human Interface Devices, 人類用來跟計(jì)算機(jī)交互的設(shè)備。就是鼠標(biāo)、鍵盤、游戲手柄等設(shè)備。對(duì)于USB接口的HID設(shè)備,有一套協(xié)議。
2.1、描述符
HID設(shè)備有如下描述符:
- HID設(shè)備的"設(shè)備描述符"并無實(shí)際意義,沒有使用"設(shè)備描述符"來表示自己是HID設(shè)備。
- HID設(shè)備只有一個(gè)配置,所以只有一個(gè)配置描述符。
- 接口描述符:
- bInterfaceClass為3,表示它是HID設(shè)備。
- bInterfaceSubClass是0或1,1表示它支持"Boot Interface"(表示PC的BIOS能識(shí)別、使用它),0表示必須等操作系統(tǒng)啟動(dòng)后通過驅(qū)動(dòng)程序來使用它。
- bInterfaceProtocol:0-None, 1-鍵盤, 2-鼠標(biāo)。
- 端點(diǎn)描述符:HID設(shè)備有一個(gè)控制端點(diǎn)、一個(gè)中斷端點(diǎn)。
對(duì)于鼠標(biāo),HOST可以通過中斷端點(diǎn)讀到數(shù)據(jù)。
2.2、鼠標(biāo)數(shù)據(jù)格式
通過中斷傳輸可以讀到鼠標(biāo)數(shù)據(jù),它是8字節(jié)的數(shù)據(jù),格式如下:
偏移 | 大小 | 描述 |
---|---|---|
0 | 1字節(jié) | |
1 | 1字節(jié) | 按鍵狀態(tài) |
2 | 2字節(jié) | X位移 |
4 | 2字節(jié) | Y位移 |
6 | 1字節(jié)或2字節(jié) | 滾輪 |
按鍵狀態(tài)里,每一位對(duì)應(yīng)鼠標(biāo)的一個(gè)按鍵,等1時(shí)表示對(duì)應(yīng)按鍵被點(diǎn)擊了,格式如下:
位 | 長度 | 描述 |
---|---|---|
0 | 1 | 鼠標(biāo)的左鍵 |
1 | 1 | 鼠標(biāo)的右鍵 |
2 | 1 | 鼠標(biāo)的中間鍵 |
3 | 5 | 保留,設(shè)備自己定義bit3: 鼠標(biāo)的側(cè)邊按鍵bit4: |
X位移、Y位移都是8位的有符號(hào)數(shù)。對(duì)于X位移,負(fù)數(shù)表示鼠標(biāo)向左移動(dòng),正數(shù)表示鼠標(biāo)向右移動(dòng),移動(dòng)的幅度就使用這個(gè)8位數(shù)據(jù)表示。對(duì)于Y位移,負(fù)數(shù)表示鼠標(biāo)向上移動(dòng),正數(shù)表示鼠標(biāo)向下移動(dòng),移動(dòng)的幅度就使用這個(gè)8位數(shù)據(jù)表示。
3、應(yīng)用程序
本次應(yīng)用程序是使用同步接口讀取鼠標(biāo)數(shù)據(jù)。
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>#include <libusb-1.0/libusb.h>int main(int argc, char **argv)
{int err;libusb_device *dev, **devs;int num_devices;int endpoint;int interface_num;int transferred;int count = 0;unsigned char buffer[8];struct libusb_config_descriptor *config_desc;struct libusb_device_handle *dev_handle = NULL;int found = 0;/* libusb init */err = libusb_init(NULL);if (err < 0) {fprintf(stderr, "failed to initialise libusb %d - %s\n", err, libusb_strerror(err));exit(1);}/* get device list */if ((num_devices = libusb_get_device_list(NULL, &devs)) < 0) // 獲取設(shè)備描述符列表(函數(shù)返回設(shè)備描述符數(shù)量){fprintf(stderr, "libusb_get_device_list() failed\n");libusb_exit(NULL);exit(1);} fprintf(stdout, "libusb_get_device_list() ok\n");/* for each device, get config descriptor */for (int i = 0; i < num_devices; i++){dev = devs[i];err = libusb_get_config_descriptor(dev, 0, &config_desc); // 獲取配置描述符if (err) {fprintf(stderr, "could not get configuration descriptor\n");continue;}fprintf(stdout, "libusb_get_config_descriptor() ok\n");/* parse interface descriptor, find usb mouse */for (int interface = 0; interface < config_desc->bNumInterfaces; interface++) // 枚舉所有接口描述符{const struct libusb_interface_descriptor *intf_desc = &config_desc->interface[interface].altsetting[0]; // 獲取配置描述符里的第一個(gè)接口描述符interface_num = intf_desc->bInterfaceNumber; // 記錄該接口描述符的編號(hào)(編號(hào)是從0開始)if (intf_desc->bInterfaceClass != 3 || intf_desc->bInterfaceProtocol != 2) // 判斷是否是HID設(shè)備和是否是鼠標(biāo)協(xié)議continue;/* 找到了USB鼠標(biāo) */fprintf(stdout, "find usb mouse ok\n");for (int ep = 0; ep < intf_desc->bNumEndpoints; ep++) // 枚舉所有端點(diǎn)描述符{// 判斷是否是中斷傳輸,是否是輸入端點(diǎn)(輸入輸出都是以USB Host來討論,所以該端點(diǎn)是USB Device輸出到USB Host)if ((intf_desc->endpoint[ep].bmAttributes & 3) == LIBUSB_TRANSFER_TYPE_INTERRUPT || (intf_desc->endpoint[ep].bEndpointAddress & 0x80) == LIBUSB_ENDPOINT_IN){/* 找到了輸入的中斷端點(diǎn) */fprintf(stdout, "find in int endpoint\n");endpoint = intf_desc->endpoint[ep].bEndpointAddress;found = 1;break;}}if (found)break;}libusb_free_config_descriptor(config_desc);if (found)break; }if (!found){/* free device list */libusb_free_device_list(devs, 1);libusb_exit(NULL);exit(1);}/* libusb open */if (found){err = libusb_open(dev, &dev_handle);if (err){fprintf(stderr, "failed to open usb mouse\n");exit(1);}fprintf(stdout, "libusb_open ok\n");}/* free device list */libusb_free_device_list(devs, 1);/* claim interface */libusb_set_auto_detach_kernel_driver(dev_handle, 1); err = libusb_claim_interface(dev_handle, interface_num);if (err){fprintf(stderr, "failed to libusb_claim_interface\n");exit(1);}fprintf(stdout, "libusb_claim_interface ok\n");/* libusb interrupt transfer */while (1){err = libusb_interrupt_transfer(dev_handle, endpoint, buffer, 8, &transferred, 5000); // 發(fā)起中斷傳輸,阻塞等待,5s超時(shí)時(shí)間if (!err) {/* parser data */printf("%04d datas: ", count++);printf("recv datas len = %d\n", transferred);for (int i = 0; i < transferred; i++){printf("%02x ", buffer[i]);}printf("\n");} else if (err == LIBUSB_ERROR_TIMEOUT){fprintf(stderr, "libusb_interrupt_transfer timout\n");} else {const char *errname = libusb_error_name(err);fprintf(stderr, "libusb_interrupt_transfer err : %d, %s\n", err, errname);//exit(1);}}/* libusb close */libusb_release_interface(dev_handle, interface_num);libusb_close(dev_handle);libusb_exit(NULL);
}
4、編譯應(yīng)用程序
假設(shè)你的開發(fā)板是ubuntu系統(tǒng):
# 安裝libusb庫
$ sudo apt install libusb-1.0-0-dev# 編譯程序
$ gcc -o readmouse readmouse.c -lusb-1.0
5、測試
將usb鼠標(biāo)插入開發(fā)板:
執(zhí)行程序:
$ sudo ./readmouse
移動(dòng)鼠標(biāo):
滾輪滑動(dòng):
按鍵狀態(tài):
另外,每個(gè)鼠標(biāo)的數(shù)據(jù)格式是不一樣的。以上測試結(jié)果只是我使用的鼠標(biāo)。