USB子系統學習(四)用戶態下使用libusb讀取鼠標數據

文章目錄

  • 1、聲明
  • 2、HID協議
    • 2.1、描述符
    • 2.2、鼠標數據格式
  • 3、應用程序
  • 4、編譯應用程序
  • 5、測試
  • 6、其它

1、聲明

本文是在學習韋東山《驅動大全》USB子系統時,為梳理知識點和自己回看而記錄,全部內容高度復制粘貼。

韋老師的《驅動大全》:商品詳情

其對應的講義資料: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協議

HID:Human Interface Devices, 人類用來跟計算機交互的設備。就是鼠標、鍵盤、游戲手柄等設備。對于USB接口的HID設備,有一套協議。

2.1、描述符

HID設備有如下描述符:

  • HID設備的"設備描述符"并無實際意義,沒有使用"設備描述符"來表示自己是HID設備。
  • HID設備只有一個配置,所以只有一個配置描述符。
  • 接口描述符:
    • bInterfaceClass為3,表示它是HID設備。
    • bInterfaceSubClass是0或1,1表示它支持"Boot Interface"(表示PC的BIOS能識別、使用它),0表示必須等操作系統啟動后通過驅動程序來使用它。
    • bInterfaceProtocol:0-None, 1-鍵盤, 2-鼠標。
  • 端點描述符:HID設備有一個控制端點、一個中斷端點。

對于鼠標,HOST可以通過中斷端點讀到數據。

2.2、鼠標數據格式

通過中斷傳輸可以讀到鼠標數據,它是8字節的數據,格式如下:

偏移大小描述
01字節
11字節按鍵狀態
22字節X位移
42字節Y位移
61字節或2字節滾輪

按鍵狀態里,每一位對應鼠標的一個按鍵,等1時表示對應按鍵被點擊了,格式如下:

長度描述
01鼠標的左鍵
11鼠標的右鍵
21鼠標的中間鍵
35保留,設備自己定義bit3: 鼠標的側邊按鍵bit4:

X位移、Y位移都是8位的有符號數。對于X位移,負數表示鼠標向左移動,正數表示鼠標向右移動,移動的幅度就使用這個8位數據表示。對于Y位移,負數表示鼠標向上移動,正數表示鼠標向下移動,移動的幅度就使用這個8位數據表示。

3、應用程序

本次應用程序使用的是同步接口讀取鼠標數據。

#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)    // 獲取設備描述符列表(函數返回設備描述符數量){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];     // 獲取配置描述符里的第一個接口描述符interface_num = intf_desc->bInterfaceNumber;        // 記錄該接口描述符的編號(編號是從0開始)if (intf_desc->bInterfaceClass != 3 || intf_desc->bInterfaceProtocol != 2)  // 判斷是否是HID設備和是否是鼠標協議continue;/* 找到了USB鼠標 */fprintf(stdout, "find usb mouse ok\n");for (int ep = 0; ep < intf_desc->bNumEndpoints; ep++)   // 枚舉所有端點描述符{// 判斷是否是中斷傳輸,是否是輸入端點(輸入輸出都是以USB Host來討論,所以該端點是USB Device輸出到USB Host)if ((intf_desc->endpoint[ep].bmAttributes & 3) == LIBUSB_TRANSFER_TYPE_INTERRUPT || (intf_desc->endpoint[ep].bEndpointAddress & 0x80) == LIBUSB_ENDPOINT_IN){/* 找到了輸入的中斷端點 */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);		// 發起中斷傳輸,阻塞等待,5s超時時間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、編譯應用程序

假設你的開發板是ubuntu系統:

# 安裝libusb庫
$ sudo apt install libusb-1.0-0-dev# 編譯程序
$ gcc -o readmouse readmouse.c -lusb-1.0

5、測試

將usb鼠標插入開發板:

執行程序:

$ sudo ./readmouse

移動鼠標:

滾輪滑動:

按鍵狀態:

另外,每個鼠標的數據格式是不一樣的。以上測試結果只是我使用的鼠標。

6、其它

以下是使用異步接口讀取鼠標數據的測試程序。

#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>#include <libusb-1.0/libusb.h>struct usb_mouse {struct libusb_device_handle *handle;int interface;int endpoint;unsigned char buf[16];int transferred;struct libusb_transfer *transfer;struct usb_mouse *next;
};static struct usb_mouse *usb_mouse_list;void free_usb_mouses(struct usb_mouse *usb_mouse_list)
{struct usb_mouse *pnext;while (usb_mouse_list){pnext = usb_mouse_list->next;free(usb_mouse_list);usb_mouse_list = pnext;}
}int get_usb_mouse(libusb_device **devs, int num_devices, struct usb_mouse **usb_mouse_list)
{int err;libusb_device *dev;int endpoint;int interface_num;struct libusb_config_descriptor *config_desc;struct libusb_device_handle *dev_handle = NULL;struct usb_mouse *pmouse;struct usb_mouse *list = NULL;int mouse_cnt = 0;/* for each device, get config descriptor */for (int i = 0; i < num_devices; i++) {dev = devs[i];/* parse interface descriptor, find usb mouse */        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");for (int interface = 0; interface < config_desc->bNumInterfaces; interface++) {     // 枚舉所有接口描述符const struct libusb_interface_descriptor *intf_desc = &config_desc->interface[interface].altsetting[0];interface_num = intf_desc->bInterfaceNumber;        // 記錄該接口描述符的編號(編號是從0開始)if (intf_desc->bInterfaceClass != 3 || intf_desc->bInterfaceProtocol != 2)      // 判斷是否是HID設備和是否是鼠標協議    continue;else{/* 找到了USB鼠標 */fprintf(stdout, "find usb mouse ok\n");for (int ep = 0; ep < intf_desc->bNumEndpoints; ep++)   // 枚舉所有端點描述符{// 判斷是否是中斷傳輸,是否是輸入端點(輸入輸出都是以USB Host來討論,所以該端點是USB Device輸出到USB Host)if ((intf_desc->endpoint[ep].bmAttributes & 3) == LIBUSB_TRANSFER_TYPE_INTERRUPT ||(intf_desc->endpoint[ep].bEndpointAddress & 0x80) == LIBUSB_ENDPOINT_IN) {/* 找到了輸入的中斷端點 */fprintf(stdout, "find in int endpoint\n");endpoint = intf_desc->endpoint[ep].bEndpointAddress;/* libusb_open */err = libusb_open(dev, &dev_handle);if (err){fprintf(stderr, "failed to open usb mouse\n");return -1;}fprintf(stdout, "libusb_open ok\n");/* 記錄下來: 放入鏈表 */pmouse = malloc(sizeof(struct usb_mouse));if (!pmouse){fprintf(stderr, "can not malloc\n");return -1;}pmouse->endpoint  = endpoint;pmouse->interface = interface_num;pmouse->handle    = dev_handle;pmouse->next      = NULL;if (!list)list = pmouse;else{pmouse->next = list;list = pmouse;}mouse_cnt++;break;}}}}libusb_free_config_descriptor(config_desc);}*usb_mouse_list = list;return mouse_cnt;
}static void mouse_irq(struct libusb_transfer *transfer)
{static int count = 0;if (transfer->status == LIBUSB_TRANSFER_COMPLETED){/* parser data */printf("%04d datas: ", count++);for (int i = 0; i < transfer->actual_length; i++){printf("%02x ", transfer->buffer[i]);}printf("\n");}if (libusb_submit_transfer(transfer) < 0){fprintf(stderr, "libusb_submit_transfer err\n");}
}int main(int argc, char **argv)
{int err;libusb_device **devs;int num_devices, num_mouse;struct usb_mouse *pmouse;/* 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)    // 獲取設備描述符列表(函數返回設備描述符數量){fprintf(stderr, "libusb_get_device_list() failed\n");libusb_exit(NULL);exit(1);}   fprintf(stdout, "libusb_get_device_list() ok\n");/* get usb mouse */num_mouse = get_usb_mouse(devs, num_devices, &usb_mouse_list);if (num_mouse <= 0){/* free device list */libusb_free_device_list(devs, 1);libusb_exit(NULL);exit(1);}fprintf(stdout, "get %d mouses\n", num_mouse);/* free device list */libusb_free_device_list(devs, 1);/* claim interface */pmouse = usb_mouse_list;while (pmouse){libusb_set_auto_detach_kernel_driver(pmouse->handle, 1);  err = libusb_claim_interface(pmouse->handle, pmouse->interface);if (err){fprintf(stderr, "failed to libusb_claim_interface\n");exit(1);}pmouse = pmouse->next;}fprintf(stdout, "libusb_claim_interface ok\n");/* for each mouse, alloc transfer, fill transfer, submit transfer */pmouse = usb_mouse_list;while (pmouse){/* alloc transfer */pmouse->transfer = libusb_alloc_transfer(0);/* fill transfer */libusb_fill_interrupt_transfer(pmouse->transfer, pmouse->handle, pmouse->endpoint, pmouse->buf,sizeof(pmouse->buf), mouse_irq, pmouse, 0);/* submit transfer */libusb_submit_transfer(pmouse->transfer);pmouse = pmouse->next;}/* handle events */while (1) {struct timeval tv = { 5, 0 };int r;r = libusb_handle_events_timeout(NULL, &tv);if (r < 0) {fprintf(stderr, "libusb_handle_events_timeout err\n");break;}}/* libusb_close */pmouse = usb_mouse_list;while (pmouse){libusb_release_interface(pmouse->handle, pmouse->interface);libusb_close(pmouse->handle);        pmouse = pmouse->next;}free_usb_mouses(usb_mouse_list);libusb_exit(NULL);}

運行程序前,先把多個鼠標插入開發板,然后運行測試程序,移動鼠標,查看打印結果。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/895141.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/895141.shtml
英文地址,請注明出處:http://en.pswp.cn/news/895141.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

2月9日QT

優化登錄框&#xff1a; 當用戶點擊取消按鈕&#xff0c;彈出問題對話框&#xff0c;詢問是否要確定退出登錄&#xff0c;并提供兩個按鈕&#xff0c;yes|No&#xff0c;如果用戶點擊的Yes&#xff0c;則關閉對話框&#xff0c;如果用戶點擊的No&#xff0c;則繼續登錄 當用戶…

安卓路由與aop 以及 Router-api

安卓路由&#xff08;Android Router&#xff09;和AOP&#xff08;面向切面編程&#xff09;是兩個在Android開發中常用的概念。下面我將詳細講解這兩個概念及其在Android開發中的應用。 一、安卓路由 安卓路由主要用于在應用程序中管理不同組件之間的導航和通信。它可以簡化…

大模型賦能網絡安全整體應用流程概述

一、四個階段概述 安全大模型的應用大致可以分為四個階段: 階段一主要基于開源基礎模型訓練安全垂直領域的模型; 階段二主要基于階段一訓練出來的安全大模型開展推理優化、蒸餾等工序,從而打造出不同安全場景的專家模型,比如數據安全領域、安全運營領域、調用郵件識別領…

nexus部署及配置https訪問

1. 使用docker-compose部署nexus docker-compose-nexus.yml version: "3" services:nexus:container_name: my-nexusimage: sonatype/nexus3:3.67.1hostname: my-nexusnetwork_mode: hostports:- 8081:8081deploy:resources:limits:cpus: 4memory: 8192Mreservations…

史上最快 Python版本 Python 3.13 安裝教程

Python3.13安裝和配置 一、Python的下載 1. 網盤下載地址 (下載速度比較快&#xff0c;推薦&#xff09; Python3.13.0下載&#xff1a;Python3.13.0下載地址&#xff08;windows&#xff09;3.13.0下載地址&#xff08;windows&#xff09; 點擊下面的下載鏈接&#xff0c…

Docker從入門到精通- 容器化技術全解析

第一章&#xff1a;Docker 入門 一、什么是 Docker&#xff1f; Docker 就像一個超級厲害的 “打包神器”。它能幫咱們把應用程序和它運行所需要的東東都整整齊齊地打包到一起&#xff0c;形成一個獨立的小盒子&#xff0c;這個小盒子在 Docker 里叫容器。以前呢&#xff0c;…

ProcessingP5js數據可視化

折線圖繪制程序設計說明 可以讀取表格數據&#xff0c;并轉換成折線圖&#xff0c;條形圖和餅狀圖&#xff0c;并設計了銜接動畫效果 1. 功能概述 本程序使用 Processing 讀取 CSV 文件數據&#xff0c;并繪制帶有坐標軸和數據點的折線圖。橫坐標&#xff08;X 軸&#xff09…

使用云計算,企業的數據監管合規問題如何解決?

使用云計算&#xff0c;企業的數據監管合規問題如何解決&#xff1f; 在當今這個信息化、數字化的時代&#xff0c;數據無疑成為了企業最寶貴的資產之一。隨著云計算的普及&#xff0c;企業將大量數據存儲在云端&#xff0c;不僅提升了效率&#xff0c;也帶來了更多靈活性。然…

AWS Fargate

AWS Fargate 是一個由 Amazon Web Services (AWS) 提供的無服務器容器計算引擎。它使開發者能夠運行容器化應用程序&#xff0c;而無需管理底層的服務器或虛擬機。簡而言之&#xff0c;AWS Fargate 讓你只需關注應用的容器本身&#xff0c;而不需要管理運行容器的基礎設施&…

vue3+vite+eslint|prettier+elementplus+國際化+axios封裝+pinia

文章目錄 vue3 vite 創建項目如果創建項目選了 eslint prettier從零教你使用 eslint prettier第一步&#xff0c;下載eslint第二步&#xff0c;創建eslint配置文件&#xff0c;并下載好其他插件第三步&#xff1a;安裝 prettier安裝后配置 eslint (2025/2/7 補充) 第四步&am…

vLLM V1 重磅升級:核心架構全面革新

本文主要是 翻譯簡化個人評讀&#xff0c;原文請參考&#xff1a;vLLM V1: A Major Upgrade to vLLM’s Core Architecture vLLM V1 開發背景 2025年1月27日&#xff0c;vLLM 開發團隊推出 vLLM V1 alpha 版本&#xff0c;這是對框架核心架構的里程碑式升級。基于過去一年半的…

Jupyter Notebook自動保存失敗等問題的解決

一、未生成配置文件 需要在命令行中&#xff0c;執行下面的命令自動生成配置文件 jupyter notebook --generate-config 執行后會在 C:\Users\用戶名\.jupyter目錄中生成文件 jupyter_notebook_config.py 二、在網頁端打開Jupyter Notebook后文件保存失敗&#xff1b;運行代碼…

使用wpa_supplicant和wpa_cli 掃描wifi熱點及配網

一&#xff1a;簡要說明 交叉編譯wpa_supplicant工具后會有wpa_supplicant和wpa_cli兩個程序生產&#xff0c;如果知道需要連接的wifi熱點及密碼的話不需要遍歷及查詢所有wifi熱點的名字及信號強度等信息的話&#xff0c;使用wpa_supplicant即可&#xff0c;否則還需要使用wpa_…

Flink (十七) :Table API SQL (五) 時區

Flink 為日期和時間提供了豐富的數據類型&#xff0c; 包括 DATE&#xff0c; TIME&#xff0c; TIMESTAMP&#xff0c; TIMESTAMP_LTZ&#xff0c; INTERVAL YEAR TO MONTH&#xff0c; INTERVAL DAY TO SECOND 。 Flink 支持在 session &#xff08;會話&#xff09;級別設置…

【真一鍵部署腳本】——一鍵部署deepseek

目錄 deepseek一鍵部署腳本說明 0 必要前提 1 使用方法 1.1 使用默認安裝配置 1.1 .1 使用其它ds模型 1.2 使用自定義安裝 2 附錄&#xff1a;deepseek模型手動下載 3 腳本下載地址 deepseek一鍵部署腳本說明 0 必要前提 linux環境 python>3.10 1 使用方法 1.1 …

5.2Internet及其作用

5.2.1Internet概述 Internet稱為互聯網&#xff0c;又稱英特網&#xff0c;始于1969年的美國ARPANET&#xff08;阿帕網&#xff09;&#xff0c;是全球性的網絡。 互連網指的是兩個或多個不同類型的網絡通過路由器等網絡設備連接起來&#xff0c;形成一個更大的網絡結構。互連…

“圖像識別分割算法:解鎖視覺智能的關鍵技術

嘿&#xff0c;各位朋友&#xff01;今天咱們來聊聊圖像識別分割算法。這可是計算機視覺領域里特別厲害的一項技術&#xff0c;簡單來說&#xff0c;它能讓機器“看懂”圖像中的不同部分&#xff0c;并把它們精準地分出來。想象一下&#xff0c;機器不僅能識別出圖里有貓還是狗…

AJAX項目——數據管理平臺

黑馬程序員視頻地址&#xff1a; 黑馬程序員——數據管理平臺 前言 功能&#xff1a; 1.登錄和權限判斷 2.查看文章內容列表&#xff08;篩選&#xff0c;分頁&#xff09; 3.編輯文章&#xff08;數據回顯&#xff09; 4.刪除文章 5.發布文章&#xff08;圖片上傳&#xff0…

html轉PDF文件最完美的方案(wkhtmltopdf)

目錄 需求 一、方案調研 二、wkhtmltopdf使用 如何使用 文檔簡要說明 三、后端服務 四、前端服務 往期回顧 需求 最近在做報表類的統計項目&#xff0c;其中有很多指標需要匯總&#xff0c;網頁內容有大量的echart圖表&#xff0c;做成一個網頁去瀏覽&#xff0c;同時…

示例:JAVA調用deepseek

近日&#xff0c;國產AI DeepSeek在中國、美國的科技圈受到廣泛關注&#xff0c;甚至被認為是大模型行業的最大“黑馬”。在外網&#xff0c;DeepSeek被不少人稱為“神秘的東方力量”。1月27日&#xff0c;DeepSeek應用登頂蘋果美國地區應用商店免費APP下載排行榜&#xff0c;在…