Linux內核編程(二十一)USB驅動開發

一、驅動類型

????????USB 驅動開發主要分為兩種:主機側的驅動程序和設備側的驅動程序。一般我們編寫的都是主機側的USB驅動程序。

????????主機側驅動程序用于控制插入到主機中的 USB 設備,而設備側驅動程序則負責控制 USB 設備如何與主機通信。由于設備側驅動程序通常與設備功能緊密相關,因此常常被稱為 USB gadget 驅動程序。USB gadget 驅動程序的作用是定義如何通過 USB 協議棧與主機端進行通信,確保設備能夠正確響應主機的請求。

????????在 USB 系統中,主機側和設備側有各自不同的控制器。主機側使用的是主機控制器(Host Controller),它負責在主機和設備之間建立數據傳輸連接,管理設備的連接和斷開。設備側則使用 USB 設備控制器(UDC),它用于控制設備如何響應主機的請求和進行數據傳輸。主機控制器和設備控制器分別在各自的系統中充當著至關重要的角色,確保 USB 設備和主機之間的有效通信。

????????這兩種驅動程序通過操作系統中的 USB 子系統進行協同工作,提供了廣泛的設備支持,從鍵盤、鼠標等簡單外設到復雜的存儲設備、音頻設備等多種類型的 USB 設備。

二、USB傳輸介質-URB

????????USB通信和IIC類似,都要先構建數據包,然后使用對應的API函數進行傳輸。URB就是USB傳輸的介質。URB(USB請求塊)是Linux內核中用于管理USB數據傳輸的結構體。在Linux中,USB數據傳輸的核心就是通過URB來進行的,它相當于I2C中的數據包封裝,承擔著數據傳輸的“容器”角色。URB用于描述一次USB傳輸的請求,包括傳輸方向、數據長度、目標端點等信息。

1. 根據數據傳輸的方式和協議類型,URB有不同的類型。

(1)控制傳輸URB:用于管理設備控制請求,如設備的初始化、配置等。使用usb_fill_control_urb()來填充控制傳輸的URB。

(2)批量傳輸URB:用于較大數據量的傳輸,通常用于數據的讀取和寫入。使用usb_fill_bulk_urb()來填充批量傳輸URB。

(3)等時傳輸URB:用于對實時性要求較高的傳輸,如音頻、視頻流等。使用usb_fill_int_urb()來填充等時傳輸URB。

(4)中斷傳輸URB:用于短數據的周期性傳輸,如鍵盤、鼠標等設備。使用usb_fill_int_urb()來填充中斷傳輸URB。

2. 結構體如下所示。

struct urb {struct list_head urb_list;          // 用于管理URB隊列的鏈表unsigned int pipe;                  // 傳輸通道,即端點void *context;                      // 用戶定義的上下文,用于回調函數中傳遞信息unsigned char *transfer_buffer;     // 數據緩沖區指針,指向傳輸數據的內存dma_addr_t transfer_dma;            // 用于DMA傳輸的物理地址unsigned int transfer_flags;        // 標志,指示URB的某些屬性(例如同步、異步等)unsigned int status;                // 傳輸狀態,成功或失敗unsigned int actual_length;         // 實際傳輸的字節數unsigned int number_of_packets;     // 分包傳輸時的數據包數unsigned int timeout;               // 傳輸超時時間(毫秒)unsigned int start_frame;           // 傳輸開始的幀號unsigned int interval;              // 傳輸間隔時間(僅對中斷傳輸有效)unsigned char *setup_packet;        // 指向控制傳輸的請求包(如果是控制傳輸時)struct urb *next;                   // 下一個URB,供鏈表使用unsigned int transfer_buffer_length; // 緩沖區的長度(即傳輸數據的最大字節數)struct usb_device *dev;             // USB設備指針,指向傳輸目標設備void (*complete)(struct urb *urb);   // 傳輸完成后的回調函數struct mutex *lock;                 // 用于同步URB訪問的互斥鎖unsigned int pipe_flags;            // 端點標志,描述端點的類型和特性
};

3. 相關API。

(1)構建URB對象。

????????返回一個指向分配的URB對象的指針。如果分配失敗,返回 NULL

struct urb *usb_alloc_urb(int iso_packets, gfp_t mem_flags);
/*
iso_packets:表示如果URB是用于等時傳輸,這個參數指定URB包含的數據包數量。如果不是等時傳輸,此參數應為0。
mem_flags:內存分配標志,通常使用 GFP_KERNEL 來分配內存。
*/

(2)釋放一個URB對象。?

void usb_free_urb(struct urb *urb);

(3)填充控制傳輸URB。

void usb_fill_control_urb(struct urb *urb, struct usb_device *dev,unsigned int pipe, unsigned char *setup_packet,void *transfer_buffer, int buffer_length,usb_complete_t complete_fn, void *context);
/*urb:要填充的URB對象。dev:目標USB設備。pipe:USB管道(端點),可以通過usb_sndctrlpipe()和usb_rcvctrlpipe()等函數獲取。setup_packet:指向控制請求的setup包指針,包含請求的控制信息(如請求碼、值、索引等)。transfer_buffer:指向傳輸數據緩沖區的指針。如果是控制傳輸的輸入數據,數據會存儲在這            里;輸出數據也通過此緩沖區發送。buffer_length:傳輸緩沖區的長度。complete_fn:傳輸完成后的回調函數。context:用于回調函數的用戶定義的上下文數據。可以在回調函數中使用。
*/

?(4)填充批量傳輸URB。

void usb_fill_bulk_urb(struct urb *urb, struct usb_device *dev,unsigned int pipe, void *transfer_buffer,int buffer_length, usb_complete_t complete_fn,void *context);
/*urb:要填充的URB對象。dev:目標USB設備。pipe:USB管道(端點),可以通過usb_sndbulkpipe()和usb_rcvbulkpipe()等函數獲取。transfer_buffer:指向傳輸數據緩沖區的指針。buffer_length:傳輸緩沖區的長度。complete_fn:傳輸完成后的回調函數。context:用于回調函數的用戶定義的上下文數據。
*/

?(5)填充中斷傳輸URB。

void usb_fill_int_urb(struct urb *urb, struct usb_device *dev,unsigned int pipe, void *transfer_buffer,int buffer_length, usb_complete_t complete_fn,void *context, unsigned int interval);
/*urb:要填充的URB對象。dev:目標USB設備。pipe:USB管道(端點),可以通過usb_sndintpipe()和usb_rcvintpipe()等函數獲取。transfer_buffer:指向傳輸數據緩沖區的指針。buffer_length:傳輸緩沖區的長度。complete_fn:傳輸完成后的回調函數。context:用于回調函數的用戶定義的上下文數據。interval:傳輸間隔,單位為幀(適用于中斷傳輸)。
*/

?(6)提交URB進行傳輸。

????????如果提交成功,返回 0;如果失敗,返回一個負數錯誤碼。

int usb_submit_urb(struct urb *urb, gfp_t mem_flags);
/*urb:要提交的URB對象。mem_flags:內存分配標志,通常使用 GFP_KERNEL。
*/

(7)取消一個URB的傳輸。

void usb_kill_urb(struct urb *urb);

三、主機側驅動開發框架

1. 操作流程。

(1)編寫USB驅動框架。

(2)完善probe函數。

(3)在退出函數中釋放掉內存以及相關注銷。

2. 編寫USB驅動框架。

①在函數入口函數中注冊usb驅動,在出口函數中注銷usb驅動。

②填充usb驅動信息,例如名稱、probe函數等。

#include <linux/module.h>
#include <linux/usb.h>
#include <linux/init.h>
//設備連接時執行
static int my_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) {return 0;
}//設備斷開時執行
static void my_usb_disconnect(struct usb_interface *intf) {}static const struct usb_device_id my_usb_id_table[] = {{ USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT, USB_INTERFACE_PROTOCOL_KEYBOARD) },{ }, // 空結構體,表示數組結束
};static struct usb_driver my_usb_driver = {.name = "my_usb",.probe = my_usb_probe,.disconnect = my_usb_disconnect,.id_table = my_usb_id_table,
};static int my_usb_init(void) {int ret = usb_register(&my_usb_driver);return 0;
}static void my_usb_exit(void) {usb_deregister(&my_usb_driver);
}module_init(my_usb_init);
module_exit(my_usb_exit);
MODULE_LICENSE("GPL");

3. 完善probe函數。

①由于鍵盤屬于輸入設備,則在probe函數中先為輸入子系統開辟空間,并填充信息。

②設置鍵盤的按鍵事件以及鍵值。

③注冊輸入子系統到驅動。

④構建URB對象,開辟空間并填充中斷傳輸URB函數中的參數。

⑤提交URB對象,用于數據傳輸。

⑥在填充URB的回調函數中,上報事件。

⑦在退出函數中釋放內存。

#include <linux/init.h>
#include <linux/module.h>
#include <linux/usb.h>
#include <linux/usb/input.h>
#include <linux/hid.h>
#include <linux/slab.h>struct input_dev *myusb_inputdev = NULL;  // 輸入設備
struct urb *myusb_urb = NULL;  // USB請求塊(URB)
unsigned char *myusb_buf = NULL;  // 數據緩沖區
int myusb_size = 0;  // 緩沖區大小
dma_addr_t myusb_dma;  // DMA地址// 鍵盤的鍵碼表,包含標準鍵盤的按鍵映射
static const unsigned char usb_keyboardcode[256] = {0, 0, 30, 48, 46, 32, 18, 33, 34, 35, 23, 36, 37, 38, 0, 50,49, 24, 25, 16, 19, 31, 20, 22, 47, 17, 45, 21, 44, 2, 3, 5,6, 7, 8, 9, 10, 11, 28, 1, 14, 15, 57, 12, 13, 26, 4, 27,43, 43, 39, 40, 41, 51, 52, 53, 58, 59, 60, 61, 62, 63, 64, 65,66, 67, 68, 87, 88, 99, 70, 119, 110, 102, 104, 111, 107, 109, 106, 105,108, 103, 69, 98, 55, 74, 78, 96, 79, 80, 81, 75, 76, 77, 71, 72,73, 82, 83, 86, 127, 116, 117, 183, 184, 185, 186, 187, 188, 189, 190, 191,192, 193, 194, 134, 138, 130, 132, 128, 129, 131, 137, 133, 135, 136, 113, 115,114, 0, 0, 0, 121, 0, 89, 93, 124, 92, 94, 95, 0, 0, 6, 0,122, 123, 90, 91, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};//回調函數執行的條件是 URB傳輸完成,無論是成功還是失敗。
static void myusb_func(struct urb *urb)  //填充中斷URB的回調函數
{if (urb->status) {pr_err("URB transfer failed with status %d\n", urb->status);} else {pr_info("URB transfer completed successfully\n");}
}// 設備連接時執行
static int my_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) {int i;int ret;struct usb_device *myusb_dev = interface_to_usbdev(intf); // 獲取USB設備指針struct usb_endpoint_descriptor *endpoint;// 1. 為輸入設備分配內存myusb_inputdev = input_allocate_device();if (!myusb_inputdev) {pr_err("Failed to allocate input device\n");return -ENOMEM;}myusb_inputdev->name = "myusb_input";   // 設置設備名稱// 2. 設置事件類型:按鍵事件和重復事件set_bit(EV_KEY, myusb_inputdev->evbit);set_bit(EV_REP, myusb_inputdev->evbit);for (i = 0; i < 255; i++) {  // 設置按鍵位圖set_bit(usb_keyboardcode[i], myusb_inputdev->keybit);}clear_bit(0, myusb_inputdev->keybit);  // 清除無效的按鍵位// 3. 注冊輸入設備ret = input_register_device(myusb_inputdev);if (ret) {input_free_device(myusb_inputdev);return ret;}
// 4. URB分配內存myusb_urb = usb_alloc_urb(0, GFP_KERNEL); // 分配一個URB,ISO數據包數為0,內存分配標志為GFP_KERNEL// 獲取端點描述符并獲取端點最大數據包大小endpoint = &intf->cur_altsetting->endpoint[0].desc;myusb_size = endpoint->wMaxPacketSize;// 分配一致性內存緩沖區用于數據傳輸myusb_buf = usb_alloc_coherent(myusb_dev, myusb_size, GFP_ATOMIC, &myusb_dma);// 獲取接收中斷管道unsigned int pipe = usb_rcvintpipe(myusb_dev, endpoint->bEndpointAddress);// 填充URBusb_fill_int_urb(myusb_urb, myusb_dev, pipe, myusb_buf, myusb_size, myusb_func, 0, endpoint->bInterval);// 5. 提交URB進行數據傳輸ret = usb_submit_urb(myusb_urb, GFP_KERNEL);mykbd_urb->transfer_dma = mykbd_dma;  // 設置URB的DMA地址mykbd_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; // 設置URB的標志:不使用DMA映射return 0;
}// 設備斷開時執行
void myusb_disconnect(struct usb_interface *intf) {usb_kill_urb(myusb_urb);  // 取消URB傳輸usb_free_urb(myusb_urb);  // 釋放URBusb_free_coherent(myusb_dev, myusb_size, myusb_buf, myusb_dma);  // 釋放一致性內存input_unregister_device(myusb_inputdev);    // 注銷輸入設備input_free_device(myusb_inputdev);    // 釋放輸入設備
}

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

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

相關文章

論文筆記-arXiv2025-A survey about Cold Start Recommendation

論文筆記-arXiv2025-Cold-Start Recommendation towards the Era of Large Language Models: A Comprehensive Survey and Roadmap 面向大語言模型&#xff08;LLMs&#xff09;時代的冷啟動推薦&#xff1a;全面調研與路線圖1.引言2.前言3.內容特征3.1數據不完整學習3.1.1魯棒…

C#使用WMI獲取控制面板中安裝的所有程序列表

C#使用WMI獲取控制面板中安裝的所有程序列表 WMI 全稱Windows Management Instrumentation,Windows Management Instrumentation是Windows中用于提供共同的界面和對象模式以便訪問有關操作系統、設備、應用程序和服務的管理信息。如果此服務被終止&#xff0c;多數基于 Windo…

風光并網對電網電能質量影響的matlab/simulink仿真建模

這個課題早在一幾年的時候比較熱門&#xff0c;之前作電科院配電網的一個項目中也有所涉及&#xff0c;我把其中一部分經典仿真模型思路分享給大家&#xff0c;電能質量影響這部分&#xff0c;我在模型中主要體現的就是不同容量的光伏、風電接入&#xff0c;對并網點的電壓影響…

Ubuntu 24.04 LTS linux 文件權限

Ubuntu 24.04 LTS 文件權限 讀權限 &#xff1a;允許查看文件的內容。寫權限 (w)&#xff1a;允許修改文件的內容。執行權限 (x)&#xff1a;允許執行文件&#xff08;對于目錄來說&#xff0c;是進入目錄的權限&#xff09;。 文件權限通常與三類用戶相關聯&#xff1a; 文…

第13章:Python TDD完善貨幣加法運算(二)

寫在前面 這本書是我們老板推薦過的&#xff0c;我在《價值心法》的推薦書單里也看到了它。用了一段時間 Cursor 軟件后&#xff0c;我突然思考&#xff0c;對于測試開發工程師來說&#xff0c;什么才更有價值呢&#xff1f;如何讓 AI 工具更好地輔助自己寫代碼&#xff0c;或許…

.Net Core微服務入門全紀錄(六)——EventBus-事件總線

系列文章目錄 1、.Net Core微服務入門系列&#xff08;一&#xff09;——項目搭建 2、.Net Core微服務入門全紀錄&#xff08;二&#xff09;——Consul-服務注冊與發現&#xff08;上&#xff09; 3、.Net Core微服務入門全紀錄&#xff08;三&#xff09;——Consul-服務注…

C#防止重復提交

C#防止重復提交 文章目錄 C#防止重復提交前言防止重復提交的思路Web API 防止重復提交代碼實現代碼講解使用方法 MVC防止重復提交總結 前言 當用戶在前端進行提交數據時&#xff0c;如果網絡出現卡頓和前端沒有給出響應的話顧客通常都會狂點提交按鈕&#xff0c;這樣就很容易導…

python學opencv|讀取圖像(三十九 )閾值處理Otsu方法

【1】引言 前序學習了5種閾值處理方法&#xff0c;包括(反)閾值處理、(反)零值處理和截斷處理&#xff0c;還學習了一種自適應處理方法&#xff0c;相關文章鏈接為&#xff1a; python學opencv|讀取圖像&#xff08;三十三&#xff09;閾值處理-灰度圖像-CSDN博客 python學o…

嵌入式硬件篇---PID控制

文章目錄 前言第一部分&#xff1a;連續PID1.比例&#xff08;Proportional&#xff0c;P&#xff09;控制2.積分&#xff08;Integral&#xff0c;I&#xff09;控制3.微分&#xff08;Derivative&#xff0c;D&#xff09;控制4.PID的工作原理5..實質6.分析7.各種PID控制器P控…

日志收集Day001

1.ElasticSearch 作用&#xff1a;日志存儲和檢索 2.單點部署Elasticsearch與基礎配置 rpm -ivh elasticsearch-7.17.5-x86_64.rpm 查看配置文件yy /etc/elasticsearch/elasticsearch.yml&#xff08;這里yy做了別名&#xff0c;過濾掉空行和注釋行&#xff09; yy /etc/el…

集合帖:前綴和及差分模板題 ← 一維及二維

【一維前綴和及一維差分知識點】 ● 一維“前綴和數組”預處理過程&#xff1a;cin>>a[i], sum[i]sum[i-1]a[i] 或者 cin>>sum[i], sum[i]sum[i-1] &#xff08;1≤i≤n&#xff09; ● 一維“區間和”計算過程&#xff1a;sum[y]-sum[x-1] &#xff08;y…

《offer 來了:Java 面試核心知識點精講 -- 框架篇》(附資源)

繼上篇文章介紹了《offer 來了&#xff1a;Java 面試核心知識點精講 -- 原理篇》書后&#xff0c;本文章再給大家推薦兄弟篇 《offer來了&#xff1a;Java面試核心知識點精講--框架篇》&#xff0c; 簡直就是為Java開發者量身定制的面試神器。 本書是對Java程序員面試中常見的…

Low-Level 大一統:如何使用Diffusion Models完成視頻超分、去雨、去霧、降噪等所有Low-Level 任務?

Diffusion Models專欄文章匯總:入門與實戰 前言:視頻在傳輸過程中常常因為各種因素(如惡劣天氣、噪聲、壓縮和傳感器分辨率限制)而出現質量下降,這會嚴重影響計算機視覺任務(如目標檢測和視頻監控)的性能。現有的視頻修復方法雖然取得了一些進展,但通常只能針對特定的退…

Video-RAG:一種將視頻RAG新框架

1. 摘要及主要貢獻點 摘要&#xff1a; 檢索增強生成&#xff08;RAG&#xff09;是一種強大的策略&#xff0c;通過檢索與查詢相關的外部知識并將其整合到生成過程中&#xff0c;以解決基礎模型生成事實性錯誤輸出的問題。然而&#xff0c;現有的RAG方法主要集中于文本信息&…

Docker Load后存儲的鏡像及更改鏡像存儲目錄的方法

Docker Load后存儲的鏡像及更改鏡像存儲目錄的方法 Docker Load后存儲的鏡像更改鏡像存儲目錄的方法腳本說明注意事項Docker作為一種開源的應用容器引擎,已經廣泛應用于軟件開發、測試和生產環境中。通過Docker,開發者可以將應用打包成鏡像,輕松地進行分發和運行。而在某些場…

【零基礎入門unity游戲開發——unity通用篇37】鼠標指針(光標)修改隱藏鎖定(基于unity6開發介紹)

考慮到每個人基礎可能不一樣,且并不是所有人都有同時做2D、3D開發的需求,所以我把 【零基礎入門unity游戲開發】 分為成了C#篇、unity通用篇、unity3D篇、unity2D篇。 【C#篇】:主要講解C#的基礎語法,包括變量、數據類型、運算符、流程控制、面向對象等,適合沒有編程基礎的…

親測解決Latex File ended while scanning use of \@@BOOKMARK

這個問題只要刪除了.tex后綴文件外的其他同名文件&#xff0c;再次編譯即可。 環境 Win11 MikTex 問題原因 編譯的時候用了好幾種編譯器&#xff0c;然后編譯出現了錯誤。生成了不能使用的引用。 解決方法 刪除.tex后綴文件外的其他同名文件后&#xff0c;再次編譯。 筆…

Amazon MSK 開啟 Public 訪問 SASL 配置的方法

1. 開啟 MSK Public 1.1 配置 MSK 參數 進入 MSK 控制臺頁面&#xff0c;點擊左側菜單 Cluster configuration。選擇已有配置&#xff0c;或者創建新配置。在配置中添加參數 allow.everyone.if.no.acl.foundfalse修改集群配置&#xff0c;選擇到新添加的配置。 1.2 開啟 Pu…

Windows FileZila Server共享電腦文件夾 映射21端口外網連接

我有這樣一個使用場景&#xff0c;在外部網絡環境下&#xff0c;通過手機便捷地讀取存儲在電腦上的視頻文件。比如在外出旅行、出差&#xff0c;身邊沒有攜帶電腦&#xff0c;僅依靠手機設備&#xff0c;就能隨時獲取電腦里存儲的各類視頻&#xff0c;無論是學習資料視頻、工作…

MySQL 實戰 4 種將數據同步到ES方案

文章目錄 1. 前言2. 數據同步方案 2.1 同步雙寫2.2 異步雙寫2.3 定時更新2.4 基于 Binlog 實時同步 3. 數據遷移工具選型 3.1 Canal3.2 阿里云 DTS3.3 Databus3.4 Databus和Canal對比3.4 其它 4. 后記 上周聽到公司新同事分享 MySQL 同步數據到 ES 的方案&#xff0c;發現很有…