學(xué)習(xí)Linux源碼要先從大方向去把握,掌握好代碼的整體框架,設(shè)計(jì)思想以后再去探究細(xì)節(jié)上的東西。這樣可以讓整個(gè)學(xué)習(xí)事半功倍,有效的提升學(xué)習(xí)的效率。分析這個(gè)代碼我們也采用這樣的方式,先從大方向入手,了解了整個(gè)脈絡(luò)了以后,再針對(duì)細(xì)枝末節(jié)一個(gè)個(gè)解析。
static struct pci_driver lpc_sich_driver = {
.name = "lpc_sich",
.id_table = lpc_sich_ids,
.probe = lpc_sich_probe,
.remove = lpc_sich_remove,
};
首先是驅(qū)動(dòng)結(jié)構(gòu)體,大體上所有驅(qū)動(dòng)都需要這么一個(gè)結(jié)構(gòu)體,module_pci_driver會(huì)將這個(gè)驅(qū)動(dòng)注冊(cè)進(jìn)內(nèi)核。
name就是這個(gè)驅(qū)動(dòng)的名稱,用一個(gè)字符串表示;id_table是使用到這個(gè)驅(qū)動(dòng)的設(shè)備列表;
probe很重要,是匹配設(shè)備和驅(qū)動(dòng)的關(guān)鍵,沒有它設(shè)備無法正常工作起來; remove則是在刪除驅(qū)動(dòng)時(shí)用的上的東西。
// id_table代碼
static DEFINE_PCI_DEVICE_TABLE(lpc_sich_ids) = {
// PCI_VENDOR_ID_JN 廠商號(hào)
// PCI_DEVICE_ID_SICH_LPC 設(shè)備ID,本文件是配置的lpc驅(qū)動(dòng)
{ PCI_DEVICE(PCI_VENDOR_ID_JN, PCI_DEVICE_ID_SICH_LPC) },
{ 0, }
};
接著解析lpc_sich_probe,即probe函數(shù)。 probe函數(shù)在設(shè)備驅(qū)動(dòng)注冊(cè)最后收尾工作,當(dāng)設(shè)備的device 和其對(duì)應(yīng)的driver 在總線上完成配對(duì)之后,系統(tǒng)就調(diào)用函數(shù)以及其他相關(guān)工作。
文件作者在這里使用了一個(gè)自定義的結(jié)構(gòu)體
struct lpc_sich_adapter {
void __iomem *hst_regs; // 驅(qū)動(dòng)的基地址
struct device *dev; // 設(shè)備信息
int irq; // 驅(qū)動(dòng)
struct irq_chip_generic *gc; // 中斷片信息
unsigned features; // 使用到的技術(shù)
};
注冊(cè)過程開始前首先就為結(jié)構(gòu)體分配內(nèi)存
lpc_adapter = kzalloc(sizeof(*lpc_adapter), GFP_KERNEL);
lpc_sich_adapter內(nèi)存分配成功后,調(diào)用pci_enable_device(pdev)使能設(shè)備。
調(diào)用pci_request_selected_regions(pdev, 1, KBUILD_MODNAME)為設(shè)備申請(qǐng)內(nèi)存.
// 為lpc_adaper變量賦值
lpc_adapter->dev = &pdev->dev;
lpc_adapter->hst_regs = pci_iomap(pdev, LPC_HST_BAR, 0); // hst是High-Speed Transfer
lpc_adapter->features = 0;
// 設(shè)置主設(shè)備
pci_set_master(pdev);
// todo
set_bit(LPC_USE_MSI, &lpc_adapter->features);
ret = pci_enable_msi(pdev);
irq_chip_generic結(jié)構(gòu)體是用來描述驅(qū)動(dòng)中斷的結(jié)構(gòu)體,該結(jié)構(gòu)體需要進(jìn)行很多設(shè)置,最主要的是ct屬性,也就是chip_type。
c = irq_alloc_generic_chip("LPC_SICH", num_ct, irq_base,
lpc_adapter->hst_regs,
handle_level_irq);
ct = gc->chip_types;
ct->regs.mask = LPC_IRQ_MASK;
ct->regs.ack = LPC_IRQ;
ct->chip.irq_mask = irq_gc_mask_set_bit;
ct->chip.irq_unmask = irq_gc_mask_clr_bit;
ct->chip.irq_ack = irq_gc_ack_set_bit;
irq_setup_generic_chip(gc, IRQ_MSK(LPC_NR_IRQS), 0, 0, IRQ_NOPROBE | IRQ_LEVEL);
后續(xù)會(huì)調(diào)用一些函數(shù)對(duì)細(xì)節(jié)上進(jìn)行一些設(shè)置,irq_set_handler_data用來設(shè)置中斷,
lpc_enable自定義函數(shù),通過修改寄存器的方式使能設(shè)備
lpc_mem_flash_init/lpc_fw_flash_init 這兩個(gè)函數(shù)初始化內(nèi)存資源,因?yàn)槭峭ㄟ^pci進(jìn)行連接的,所以直接走的總線。
irq_set_handler_data(lpc_adapter->irq, lpc_adapter);
irq_set_chained_handler(lpc_adapter->irq, (irq_flow_handler_t) lpc_irq_handler_mfd);
pci_read_config_word(pdev, PCI_COMMAND, &pci_command);
pci_command |= PCI_COMMAND_IO;
pci_write_config_word(pdev, PCI_COMMAND, pci_command);
pci_read_config_word(pdev, PCI_COMMAND, &pci_command);
dev_info(&pdev->dev, "pci command = %#x\n", pci_command);
lpc_enable(lpc_adapter);
lpc_mem_flash_init(pdev,lpc_adapter);
lpc_fw_flash_init(pdev,lpc_adapter);
下面這段話需要注意下,因?yàn)閘pc也屬于mfd設(shè)備(mutilfunction device), 因此需要調(diào)用mfd_add_devices
lpc_sich_cells, ARRAY_SIZE(lpc_sich_cells), NULL,
0, NULL);
后續(xù)再使能設(shè)備中斷, 將pdev寫入適配器,就基本上完成了probe函數(shù)
lpc_enable_irqs(lpc_adapter);
pci_set_drvdata(pdev, lpc_adapter);
一路分析下來, 實(shí)際上整個(gè)文件都是在圍繞著probe這個(gè)點(diǎn)做文章,在probe中需要對(duì)各個(gè)結(jié)構(gòu)體做大量設(shè)置,以及調(diào)用若干函數(shù),使得設(shè)備和驅(qū)動(dòng)在總線上做匹配, 這些都需再以后做大量的積累。只有多去學(xué)習(xí),理解,多去寫,才能做到看到驅(qū)動(dòng)也不發(fā)慌。