linux 驅動沒有設備id,linux不同總線的設備和驅動的匹配過程分析

摘自:

前幾日讀書會,談到linux中driver和device的匹配問題,我認為是通過設備名來匹配的,因為我之前看過platform的驅動,它就是通過設備name和驅動name來進行匹配,所以我確信linux里邊所有的驅動和設備都是這樣匹配的。但師兄A提出了反對意見,并舉pci設備和pci驅動的匹配過程為例。我深信自己的理解是正確的,下來以后分秒必爭的查看了內核源代碼,結果發現:我們的理解都是正確的,但又都是不正確的!

先以pci設備和pci驅動的匹配過程為例:

static struct pci_driver serial_pci_driver = {

.name?????????? = "serial",

.probe????????? = pciserial_init_one,

.remove???????? = __devexit_p(pciserial_remove_one),

#ifdef CONFIG_PM

.suspend??????? = pciserial_suspend_one,

.resume???????? = pciserial_resume_one,

#endif

.id_table?????? = serial_pci_tbl,

};

static int __init serial8250_pci_init(void)

{

return pci_register_driver(&serial_pci_driver);

}

分析:

#define pci_register_driver(driver)???????????? \

__pci_register_driver(driver, THIS_MODULE, KBUILD_MODNAME)

int __pci_register_driver(struct pci_driver *drv, struct module *owner,

const char *mod_name)

{

...

drv->driver.bus = &pci_bus_type;

error = driver_register(&drv->driver);

...

}

僅僅抓取兩句關鍵代碼,后面會用到

driver_register ->? bus_add_driver ->? driver_attach(&driver->driver);

int driver_attach(struct device_driver *drv)

{

return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);

//遍歷總線上的所有設備,每遍歷一個設備,就調用一次__driver_attach來和驅動進行匹配

}

static int __driver_attach(struct device *dev, void *data)

{

struct device_driver *drv = data;

/*

* Lock device and try to bind to it. We drop the error

* here and always return 0, because we need to keep trying

* to bind to devices and some drivers will return an error

* simply if it didn't support the device.

*

* driver_probe_device() will spit a warning if there

* is an error.

*/

if (drv->bus->match && !drv->bus->match(dev, drv))

return 0;

if (dev->parent)??????? /* Needed for USB */

down(&dev->parent->sem);

down(&dev->sem);

if (!dev->driver)

driver_probe_device(drv, dev);

up(&dev->sem);

if (dev->parent)

up(&dev->parent->sem);

return 0;

}

我最關注的是這句代碼:

if (drv->bus->match && !drv->bus->match(dev, drv))

return 0;

這句代碼揭露了匹配的過程。

這是一種面向對像的思想,你是pci設備,那就調用pci總線的match函數;你是其他總線的設備

那就調用相應的其他的match函數。由于我們是pci的驅動(我們注冊驅動使用的函數是pci_register_driver)并且在前面有:

drv->driver.bus = &pci_bus_type;讓我們看看pci_bus_type是和角色:

struct bus_type pci_bus_type = {

.name?????????? = "pci",

.match????????? = pci_bus_match,

.uevent???????? = pci_uevent,

.probe????????? = pci_device_probe,

.remove???????? = pci_device_remove,

.shutdown?????? = pci_device_shutdown,

.dev_attrs????? = pci_dev_attrs,

.pm???????????? = PCI_PM_OPS_PTR,

};

很明顯,我們將進入pci_bus_match

static int pci_bus_match(struct device *dev, struct device_driver *drv)

{

struct pci_dev *pci_dev = to_pci_dev(dev);

struct pci_driver *pci_drv = to_pci_driver(drv);

const struct pci_device_id *found_id;

found_id = pci_match_device(pci_drv, pci_dev);//對pci驅動和設備進行匹配

if (found_id)

return 1;

return 0;

}

static const struct pci_device_id *pci_match_device(struct pci_driver *drv,

struct pci_dev *dev)

{

struct pci_dynid *dynid;

/* Look at the dynamic ids first, before the static ones */

spin_lock(&drv->dynids.lock);

list_for_each_entry(dynid, &drv->dynids.list, node) {

if (pci_match_one_device(&dynid->id, dev)) {

spin_unlock(&drv->dynids.lock);

return &dynid->id;

}

}

spin_unlock(&drv->dynids.lock);

return pci_match_id(drv->id_table, dev);

}

static inline const struct pci_device_id *

pci_match_one_device(const struct pci_device_id *id, const struct pci_dev *dev)

{

if ((id->vendor == PCI_ANY_ID || id->vendor == dev->vendor) &&

(id->device == PCI_ANY_ID || id->device == dev->device) &&

(id->subvendor == PCI_ANY_ID || id->subvendor == dev->subsystem_vendor) &&

(id->subdevice == PCI_ANY_ID || id->subdevice == dev->subsystem_device) &&

!((id->class ^ dev->class) & id->class_mask))

return id;

return NULL;

}

這就是具體的匹配過程,這個過程大家看代碼應該可以明白。

假如匹配,那么會返回一個pci_device_id類型的指針。

在__driver_attach函數里邊繼續往下看,有一句:

if (!dev->driver)

driver_probe_device(drv, dev);

假如 這個設備還沒有“名花有主”,那么調用driver_probe_device

int driver_probe_device(struct device_driver *drv, struct device *dev)

{

int ret = 0;

if (!device_is_registered(dev))

return -ENODEV;

if (drv->bus->match && !drv->bus->match(dev, drv))

goto done;

pr_debug("bus: '%s': %s: matched device %s with driver %s\n",

drv->bus->name, __func__, dev_name(dev), drv->name);

ret = really_probe(dev, drv);

done:

return ret;

}

static int really_probe(struct device *dev, struct device_driver *drv)

{

...

if (dev->bus->probe) {

ret = dev->bus->probe(dev);

if (ret)

goto probe_failed;

} else if (drv->probe) {

ret = drv->probe(dev);????????? //終于進入了我們注冊的驅動的probe函數

if (ret)

goto probe_failed;

}

...

}

總結一下,pci驅動和pci設備的匹配不是通過名字來進行匹配的,具體過程上面已經說清楚了。而且,通過上面的分析,大家可以看出來

內核使用的是面向對象的思想,不同的總線它的匹配策略是不一樣的。

附:

1.platform總線的匹配策略:

static int platform_match(struct device *dev, struct device_driver *drv)

{

struct platform_device *pdev;

pdev = container_of(dev, struct platform_device, dev);

return (strcmp(pdev->name, drv->name) == 0);

}

可見platform總線設備和驅動的匹配策略很簡單,僅僅是比較name域是否相同

2.usb總線的匹配策略:

static int usb_device_match(struct device *dev, struct device_driver *drv)

{

/* devices and interfaces are handled separately */

if (is_usb_device(dev)) {

/* interface drivers never match devices */

if (!is_usb_device_driver(drv))

return 0;

/* TODO: Add real matching code */

return 1;

} else {

struct usb_interface *intf;

struct usb_driver *usb_drv;

const struct usb_device_id *id;

/* device drivers never match interfaces */

if (is_usb_device_driver(drv))

return 0;

intf = to_usb_interface(dev);

usb_drv = to_usb_driver(drv);

id = usb_match_id(intf, usb_drv->id_table);

if (id)

return 1;

id = usb_match_dynamic_id(intf, usb_drv);

if (id)

return 1;

}

return 0;

}

usb總線設備和驅動的匹配策略則比較復雜。

摘自:

閱讀(2861) | 評論(0) | 轉發(0) |

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

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

相關文章

理解Flight框架核心

看到了這篇分析flight的文章還不錯,就轉過來了,地址:https://blog.csdn.net/sky_zhe/article/details/38906689 Flight框架(官網)是一個微型的PHP框架,它簡單,快速,可擴展。借助Flig…

安裝ISO系統(原版系統)系統終極方法

首先進入PE,在PE下找到你的系統ISO鏡像,解壓縮,然后將鏡像里的boot文件夾、sources文件夾和bootmgr文件提取出來,然后復制到你要安裝的分區(比如c盤),接下來拔下U盤,重新啟動計算機&…

intel i218v千兆網卡 linux驅動,適用于英特爾? 千兆位以太網網絡連接的 Linux* 基礎驅動程序...

適用于英特爾 千兆位以太網網絡連接的 Linux* igb* 基礎驅動程序安裝說明Linux* igb 驅動程序支持所有基于 82575、82576、82580,I350,I354 和 I210/I211 的英特爾 千兆位以太網網絡連接。有關驅動程序配置的詳細信息,請參閱下載中心中的自述…

Linux下如何抓取串口碼流,linux alsa音頻中采樣率fs、比特率BCLK 、主時鐘MCLK關系...

轉:https://blog.csdn.net/lugandong/article/details/72468831一、拿512fs說話:看圖知道采樣的位深是32bit(位),左右聲道各占了8*32BCLK,那一個完整的LRCLK一共8*32*2512BCLK。其實xxxfs就是這么算出來的,也是固定的&…

第 39 章 ThinkPHP--CURD 操作

學習ThinkPHP 模型中的 CURD 操作,也就是增刪改查。通過 CURD, 我們可以方便快速的對數據庫進行操作。 1.數據創建 2.數據寫入 3.數據讀取 4.數據更新 5.數據刪除 一.數據創建 在數據庫添加等操作之前,我們首先需要對數據進行創建…

洛谷 P1529 回家 Bessie Come Home Label:Dijkstra最短路 亂搞

題目描述 現在是晚餐時間,而母牛們在外面分散的牧場中。 農民約翰按響了電鈴,所以她們開始向谷倉走去。 你的工作是要指出哪只母牛會最先到達谷倉(在給出的測試數據中,總會有且只有一只最快的母牛)。 在擠奶的時候(晚餐前),每只母牛都在她自己的牧場上,一些牧場上可能沒有母牛。…

linux語言的說明順序有哪些,(linux常用頭文件詳解.doc

(linux常用頭文件詳解linux常用頭文件詳解POSIX標準定義的頭文件??????? 目錄項???????? 文件控制??? 文件名匹配類型??? 路徑名模式匹配類型??????? 組文件??? 網絡數據庫操作??????? 口令文件??? 正則表達式??????? TAR歸檔…

第 39 章 ThinkPHP--視圖

學習要點: 1.模版定義 2.賦值和渲染 3.模版地址 4.獲取內容 本節課,我們將要學習一下 ThinkPHP 視圖,視圖是 Web 的可見內容,一般是 HTML 結合 PHP 獲取的數據提供給用戶使用的部分,屬于 MVC 中的 V。 一.模…

mysql日志(介紹 路徑修改 備份)

2019獨角獸企業重金招聘Python工程師標準>>> 環境:senos6 軟件:mysql2.6.20 mysql日志: 錯誤日志 一般查詢日志 慢查詢日志 二進制日志 只記錄DDL,DML等引起數據庫改變的操作都會記錄下來 復制&am…

Sort

<?xml version"1.0" encoding"utf-8"?> SortSort 1 Sort Select sort is the simplest sorting alogrithms. 1.1 IDEA 1.find the smallest element in the rest of array 2.exchange the element with with the i th entry. 3.repeat step1 and s…

a標簽實現不跳轉點擊

<a class"tiao" href"./index.php"></a> JS實現無跳轉a標簽 <script type"text/javascript"> $(".tiao").click(function (){return false; }) </script> 轉載于:https://www.cnblogs.com/wenhainan/p/…

linux下的c語言控制燈閃爍,C語言實現LED燈閃爍控制

原標題&#xff1a;C語言實現LED燈閃爍控制/********* 配套 **********/#include //包含 寄存器的頭文件/****************************************函數功能&#xff1a;延時一段時間*****************************************/void delay(void) //兩個void意思分別為無需返回…

VBA and Access

>>.用vba連接ACESS&#xff1a; Set Conn Server.CreateObject("ADODB.Connection") Conn.ConnectionString"ProviderMicrosoft.Jet.OLEDB.4.0;Data Source" & Server.MapPath("sample.mdb") Conn.Open>>.用vba連接EXCEL,打開EX…

溫州大學c語言作業布置的網站,老師APP上布置作業 三年級娃為刷排名半夜做題_央廣網...

在溫州讀小學三年級的皮皮(化名)&#xff0c;因為學習需要&#xff0c;在媽媽黃女士的手機里安裝了5個APP學習軟件。有數學速算的&#xff0c;英語配音的&#xff0c;還有語文復習的。這些軟件&#xff0c;都是班上的老師推薦安裝的。每天放學回家&#xff0c;皮皮就拿著黃女士…

Algorithm I assignment Collinear

這本來應該是第三周的作業&#xff0c;但是由于其他作業逼近deadline&#xff0c;暫時推后了一周完成。 這周的assignment大大提高了我對這門課的看法&#xff0c;不得不說&#xff0c;Algorithms這門課的assignment部分設計得很好。為什么好&#xff1f;個人認為有以下幾點&am…

vc c語言坐標圖,VC++6.0下C語言畫圖編程問題

復制內容到剪貼板代碼:#include#includevoid CSinusoidView::OnDraw(CDC* pDC){CSinusoidDoc* pDoc GetDocument();ASSERT_VALID(pDoc);// TODO: add draw code for native data here//建立畫筆CPen cpen,pen;pen.CreatePen(PS_SOLID,4,RGB(0,0,0));cpen.CreatePen(PS_SOLID,2…

Java BigDecimal詳解

1.引言 float和double類型的主要設計目標是為了科學計算和工程計算。他們執行二進制浮點運算&#xff0c;這是為了在廣域數值范圍上提供較為精確的快速近似計算而精心設計的。然而&#xff0c;它們沒有提供完全精確的結果&#xff0c;所以不應該被用于要求精確結果的場合。但是…

Erlang庫 -- 有意思的庫匯總

抄自這里 首先&#xff0c;庫存在的目的大致可分為&#xff1a;1、提供便利2、盡可能解決一些痛點首先&#xff0c;我們先明確一下Erlang編程語言的一些痛點&#xff08;偽痛點&#xff09;&#xff1a;1&#xff0c;單進程問題Erlang虛擬機屬于搶占式調度&#xff0c;搶占式調…

windows 串口編程 c語言,windows下C語言版串口發送程序(基于VS2017)

#include "tchar.h"#include int main(){/*****************************打開串口*************************************/HANDLE hCom;//全局變量&#xff0c;串口句柄hCom CreateFile(_T("COM3"),//COM3口GENERIC_READ | GENERIC_WRITE,//允許讀和寫0,/…

scikit-learn決策樹算法類庫使用小結

之前對決策樹的算法原理做了總結&#xff0c;包括決策樹算法原理(上)和決策樹算法原理(下)。今天就從實踐的角度來介紹決策樹算法&#xff0c;主要是講解使用scikit-learn來跑決策樹算法&#xff0c;結果的可視化以及一些參數調參的關鍵點。 1. scikit-learn決策樹算法類庫介紹…