PCI设备驱动初探(仅仅是内核部分,不是具体设备驱动)

在操作系统中,声卡、网卡之类的设备驱动并不像硬盘、鼠标、键盘等等驱动直接编写就行了。它们是建立在内核PCI驱动基础上的,也就是说这类设备通过PCI总线与系统通信。所以要编写这类的驱动首先要构造一个PCI设备的内核驱动,这样我们才能继续正常的使用连接在PCI总线的设备。

关于PCI设备的详细介绍,还是请大家参考网上诸位大侠的文章,笔者这里就不班门弄斧了。这中间的原因当然是笔者根本就看不懂,主要是PCI设备相较于普通的设备复杂了很多,不仔细研究个十天八天的真的是弄不懂啊。

那么笔者既然不懂,又是怎么编写出的PCI驱动程序呢。起始笔者还真没有那个本事。这次笔者是又一次发挥了复制粘贴的本领。干脆直接把linux内核的PCI驱动粘贴进自己的代码中,本来是只是抱着试试看的想法,没想真的奏效了,看来这些日子的运气还是不错的。

https://elixir.bootlin.com/

通过上图大家看到了吧。笔者这次居然敢去碰linux内核了。在这里就是PCI设备驱动的内核部分。而且笔者还可以把网页翻译成了中文。

关于文件的详情,笔者这里也没完全弄懂,所以还是请大家自主观看吧。这里要告诉大家的是其实linux为我们展示了3种PCI访问方式。笔者只是拿来了一种作为测试代码。前两种是通过输入输出指令直接探测设备,而第3种是调用BIOS的代码来实现的。哦对了,起始PCI设备也不是专为intel处理器设计的,所以我们参考的代码是与体系结构相关的,也就是i386架构的。

上图具体定位了真正的激活PCI设备的函数,由于linux代码的架构性太强,笔者有些代码根本就看不懂。好在笔者还是比较善于改编的,这不笔者就通过少量的修改,形成了自己系统的代码。

unsigned int tmp;

void out8_(unsigned char data, unsigned short port);
void out16_(unsigned short data, unsigned short port);
void out32_(unsigned int data, unsigned short port);
unsigned char in8_(unsigned short port);
unsigned short in16_(unsigned short port);
unsigned int in32_(unsigned short port);

#define inb in8_
#define inw in16_
#define inl in32_
#define outb out8_
#define outw out16_
#define outl out32_
    

#define CONFIG_CMD(bus, device_fn, where)   (0x80000000 | (bus << 16) \
                                            | (device_fn << 8) | (where & ~3))
#define PCIBIOS_SUCCESSFUL        0x00

int pci_conf1_read_config_byte(unsigned char bus, unsigned char device_fn,
                   unsigned char where, unsigned char *value)
{
    outl(CONFIG_CMD(bus,device_fn,where), 0xCF8);
    *value = inb(0xCFC + (where&3));
    return PCIBIOS_SUCCESSFUL;
}

int pci_conf1_read_config_word (unsigned char bus,
    unsigned char device_fn, unsigned char where, unsigned short *value)
{
    outl(CONFIG_CMD(bus,device_fn,where), 0xCF8);    
    *value = inw(0xCFC + (where&2));
    return PCIBIOS_SUCCESSFUL;    
}

int pci_conf1_read_config_dword (unsigned char bus, unsigned char device_fn, 
                 unsigned char where, unsigned int *value)
{
    outl(CONFIG_CMD(bus,device_fn,where), 0xCF8);
    *value = inl(0xCFC);
    return PCIBIOS_SUCCESSFUL;    
}

int pci_conf1_write_config_byte (unsigned char bus, unsigned char device_fn, 
                 unsigned char where, unsigned char value)
{
    outl(CONFIG_CMD(bus,device_fn,where), 0xCF8);    
    outb(value, 0xCFC + (where&3));
    return PCIBIOS_SUCCESSFUL;
}

int pci_conf1_write_config_word (unsigned char bus, unsigned char device_fn, 
                 unsigned char where, unsigned short value)
{
    outl(CONFIG_CMD(bus,device_fn,where), 0xCF8);
    outw(value, 0xCFC + (where&2));
    return PCIBIOS_SUCCESSFUL;
}

int pci_conf1_write_config_dword (unsigned char bus, unsigned char device_fn, 
                  unsigned char where, unsigned int value)
{
    outl(CONFIG_CMD(bus,device_fn,where), 0xCF8);
    outl(value, 0xCFC);
    return PCIBIOS_SUCCESSFUL;
}

    


#define PCI_CLASS_DEVICE        0x0a    /* Device class */
#define PCI_CLASS_DISPLAY_VGA        0x0300
#define  PCI_CLASS_BRIDGE_HOST        0x0600
#define PCI_VENDOR_ID        0x00    /* 16 bits */
#define PCI_VENDOR_ID_INTEL        0x8086
#define PCI_VENDOR_ID_COMPAQ        0x0e11
typedef unsigned short u16;

int pci_sanity_check(void)
{
    u16 dfn, x;
    for(dfn=0; dfn < 0x100; dfn++)
        if ((!pci_conf1_read_config_word(0, dfn, PCI_CLASS_DEVICE, &x) &&
             (x == PCI_CLASS_BRIDGE_HOST || x == PCI_CLASS_DISPLAY_VGA)) ||
            (!pci_conf1_read_config_word(0, dfn, PCI_VENDOR_ID, &x) &&
             (x == PCI_VENDOR_ID_INTEL || x == PCI_VENDOR_ID_COMPAQ)))
            return 1;
    return 0;
}

void pci_check_direct(void)
{
    unsigned int tmp;
    unsigned long flags;
    
    outb (0x01, 0xCFB);
    tmp = inl (0xCF8);
    outl (0x80000000, 0xCF8);
    if (inl (0xCF8) == 0x80000000 && pci_sanity_check()) {
        outl (tmp, 0xCF8);
        return;
    }
    outl (tmp, 0xCF8);
}

    pci_check_direct();


#undef inb
#undef inw
#undef inl
#undef outb
#undef outw
#undef outl

    for(i = 0x28; i < 0x28 + 1; i++) {
        pci_conf1_read_config_dword(0, i, 0, &tmp);
        printf_(" %x %x ", i, tmp);        
    }

    unsigned short nambar, nabmbar;
    
    pci_conf1_read_config_dword(0, 0x28, 0x10, &tmp);
    nambar = tmp & 0xfffe;
    pci_conf1_read_config_dword(0, 0x28, 0x14, &tmp);
    nabmbar = tmp & 0xffc0;
    
    printf_("|NAMBAR=%x, NABMBAR=%x|", nambar, nabmbar);

下边是汇编的部分

global _out8, _out16, _out32, _in8, _in16, _in32
global _out8_, _out16_, _out32_, _in8_, _in16_, _in32_
align 16
_out8: ; void out8(unsigned short port, unsigned char data);
    push edx
    mov dx, [esp + 2 * 4]
    mov al, [esp + 3 * 4]
    out dx, al
    pop edx
    ret
    
align 16
_out16: ; void out16(unsigned short port, unsigned short data);
    push edx
    mov dx, [esp + 2 * 4]
    mov ax, [esp + 3 * 4]
    out dx, ax
    pop edx
    ret
    
align 16
_out32: ; void out32(unsigned short port, unsigned int data);
    push edx
    mov dx, [esp + 2 * 4]
    mov eax, [esp + 3 * 4]
    out dx, eax
    pop edx
    ret

align 16
_out8_: ; void out8_(unsigned char data, unsigned short port);
    push edx
    mov dx, [esp + 3 * 4]
    mov al, [esp + 2 * 4]
    out dx, al
    pop edx
    ret
    
align 16
_out16_: ; void out16_(unsigned short data, unsigned short port);
    push edx
    mov dx, [esp + 3 * 4]
    mov ax, [esp + 2 * 4]
    out dx, ax
    pop edx
    ret
    
align 16
_out32_: ; void out32_(unsigned int data, unsigned short port);
    push edx
    mov dx, [esp + 3 * 4]
    mov eax, [esp + 2 * 4]
    out dx, eax
    pop edx
    ret

align 16
_in8_:
_in8: ; unsigned char in8(unsigned short port);
    push edx
    mov dx, [esp + 2 * 4]
    in al, dx
    pop edx
    ret

align 16
_in16_:
_in16: ; unsigned short in16(unsigned short port);
    push edx
    mov dx, [esp + 2 * 4]
    in ax, dx
    pop edx
    ret

align 16
_in32_:
_in32: ; unsigned int in32(unsigned short port);
    push edx
    mov dx, [esp + 2 * 4]
    in eax, dx
    pop edx
    ret

下边是探测到的ICH ac97声卡的设备号和音频混音器的寄存器的端口基地址以及音频总线主控制器的端口基地址。当然在Snail OS中为了看清探测的结果,已经关闭了所有中断。

结合虚拟机的调试信息大家可以知道,探测的设备信息完全正确。

VBoxDbg> info pci

00:00.0 i440FX: 8086-1237 PIIX3

Class base/sub: 0600 (bridge device)

Command: 0000, Status: 0000

Bus master: No

00:01.0 PIIX3: 8086-7000 PIIX3

Class base/sub: 0601 (bridge device)

Command: 0007, Status: 0200

Bus master: Yes

00:01.1 piix3ide: 8086-7111 PIIX3

Class base/sub: 0101 (mass storage controller)

IO region #4: d000..d00f

Command: 0007, Status: 0000

Bus master: Yes

00:02.0 vga: 80ee-beef PIIX3 IRQ10 (INTA#->IRQ18)

Class base/sub: 0300 (display controller)

MMIO32 PREFETCH region #0: e0000000..e07fffff

Command: 0003, Status: 0000

Bus master: No

00:03.0 pcnet: 1022-2000 PIIX3 IRQ9 (INTA#->IRQ19)

Class base/sub: 0200 (network controller)

IO region #0: d020..d03f

MMIO32 region #1: f0000000..f0000fff

Command: 0007, Status: 0280

Bus master: Yes

00:04.0 VMMDev: 80ee-cafe PIIX3 IRQ11 (INTA#->IRQ20)

Class base/sub: 0880 (base system peripherals)

IO region #0: d040..d05f

MMIO32 region #1: f0400000..f07fffff

MMIO32 PREFETCH region #2: f0800000..f0803fff

Command: 0003, Status: 0000

Bus master: No

00:05.0 ichac97: 8086-2415 PIIX3 IRQ11 (INTA#->IRQ21)

Class base/sub: 0401 (multimedia controller)

IO region #0: d100..d1ff

IO region #1: d200..d23f

Command: 0001, Status: 0280

Bus master: No

00:06.0 usb-ohci: 106b-003f PIIX3 IRQ10 (INTA#->IRQ22)

Class base/sub: 0c03 (serial bus controllers)

MMIO32 region #0: f0804000..f0804fff

Command: 0002, Status: 0010

Bus master: No

00:07.0 acpi: 8086-7113 PIIX3 IRQ9 (INTA#->IRQ23)

Class base/sub: 0680 (bridge device)

Command: 0001, Status: 0280

Bus master: No

VBoxDbg>

总线上PCI设备可接256种之多,所以大家只要把代码中的for语句从0号设备开始循环查询就能够看到所有设备的全貌了。

下图是笔者探测的前64个,不知道大家是否能和调试信息对号入座吗?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

weixin_39410618

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值