⌨ Labor omnia vincit ☮

Yet another method to find PCI vendor and device ID

Posted in Linux Kernel by anaumov on 17.11.2012

Первым шагом написания драйвера для PCI устройства является определение его Vendor ID и Device ID. Существует много способов найти их. Кто-то советует смотреть документацию к устройству, более распространенным способом является просто заглянуть в файл include/linux/pci_ids.h. Ок, если я кернел-разработчик, то у меня естественно есть этот файл, а также я могу сделать что-то типа

> /sbin/lspci -n -s 02:00.0
02:00.0 0200: 11ab:4364 (rev 12)

…но что делать, если мне понадобился Vendor ID и Device ID на какой-нибудь, возможно даже не моей машине, где во-первых нет этого файла, а во-вторых мне также нужны Class code, BAR0-BAR5, Subvendor ID и Subdevice ID, которых lspci с ключами -n и -s не показывает?

Информацию об этом можно вытянуть из sysfs (рассматривается случай, если устройство уже зарегистрированно). Пусть это будет, к примеру, Ethernet-карта; cмотрим её адрес:

> /sbin/lspci | grep Ethernet
02:00.0 Ethernet controller: Marvell Technology Group Ltd. 88E8056 PCI-E Gigabit Ethernet Controller
05:04.0 Ethernet controller: Marvell Technology Group Ltd. 88E8001 Gigabit Ethernet Controller

Ок, карт две, с адресами 02:00.0 и 05:04.0 соответсвенно. Более подробно PCI-дерево можно увидеть через тот же lscpi, но с ключем -t (tree).

> /sbin/lspci -t
-[0000:00]-+-00.0
           +-01.0-[01]--
           +-1a.0
           +-1a.1
           +-1a.7
           +-1b.0
           +-1c.0-[04]----00.0
           +-1c.4-[03]--+-00.0
           |            \-00.1
           +-1c.5-[02]----00.0
           +-1d.0
           +-1d.1
           +-1d.2
           +-1d.7
           +-1e.0-[05]--+-03.0
           |            \-04.0
           +-1f.0
           +-1f.2
           +-1f.3
           \-1f.5

Я показываю этот вывод для того, чтобы вам легче было отследить откуда я узнал адрес /sys/devices/pci0000:00/0000:00:1c.5/0000:02:00.0/:

> l /sys/devices/pci0000\:00/0000\:00\:1c.5/0000\:02\:00.0/driver
lrwxrwxrwx 1 root root 0 Nov 16 22:04 /sys/devices/pci0000:00/0000:00:1c.5/0000:02:00.0/driver \
-> ../../../../bus/pci/drivers/sky2/

Да, это то устройство, и работает оно под управлением модуля sky2.
Второе висит на skge:

> l /sys/devices/pci0000\:00/0000\:00\:1e.0/0000\:05\:04.0/driver
lrwxrwxrwx 1 root root 0 Nov 17 00:12 /sys/devices/pci0000:00/0000:00:1e.0/0000:05:04.0/driver \
-> ../../../../bus/pci/drivers/skge/

Каждое PCI устройство имеет область памяти (256-байт), которая содержит его конфигурацию. Эта информация является ключевым для определения марки и возможности PCI-карт. Находится она в config-файле, в том же каталоге. Прочитать её дамп можно с помощью od:

> od -x /sys/devices/pci0000\:00/0000\:00\:1c.5/0000\:02\:00.0/config

0000000 11ab 4364 0407 0010 0012 0200 0008 0000
0000020 c004 fa7f 0000 0000 8801 0000 0000 0000
0000040 0000 0000 0000 0000 0000 0000 1043 81f8
0000060 0000 fa7c 0048 0000 0000 0000 010a 0000
0000100 0000 01f0 8000 01a0 5001 fe03 2000 1300
0000120 5c03 83fc ffff ffff 0000 0100 e005 0081
0000140 300c fee0 0000 0000 41a1 0000 0000 0000
0000160 0200 0000 0000 0000 0000 0000 0000 0000
0000200 0000 0000 7000 0000 0000 0000 a882 00e8
0000220 0000 0000 0000 0000 0000 0000 0000 0000
*

Первые два бита – это Vendor ID [11ab]. Дальше (смещение 2 бита) – Device ID [4364]. Class code находится со смещением 10 бит [0200]. BAR0 – BAR5 (base address register) со смещением с 16 по 39 бит [c004…0000]. Subvendor ID имеет смещение 44 [1043], и наконец Subdevice ID имеет смещение 46 бит [81f8].

Кстати, зная Vendor и Device ID, мы можем заглянуть на pcidatabase.com и попробовать узнать побольше об этом устройстве:

Пройдя дальше по ссылке я узнал немного о производителе и других его разработках.

check point

Один важный момент, который я хотел бы рассмотреть в конце – это отсутствие информации об устройстве с device ID 4364 в include/linux/pci_ids.h. Как вы видете, там есть лишь vendor ID 11ab. Значит ли это, что карта с этим ID не поддерживается ядром? Нет, карта работает отлично, и ядро находит устройство без каких-либо проблем. Проверить это можно с помощью API ядра: pci_get_device(), которая находит (или не находит) устройство и pci-enable-device(), которая включает его.

#include <linux/init.h>
#include <linux/module.h>
#include <linux/pci.h>

#define VENDOR_ID       0x11ab
#define DEVICE_ID       0x4364

static int __init pci_start(void)
{
        struct pci_dev *pdev;
        pdev = pci_get_device(VENDOR_ID, DEVICE_ID, NULL);

        if(!pdev)
                printk("Device not found\n");
        else {
                printk("Device found\n");

                if(pci_enable_device(pdev)) {
                        printk("Could not enable the device\n");
                        return NULL;
                }
                else
                        printk("Device enabled\n");
        }
        return 0;
}

static void __exit pci_exit(void)
{
        printk("Bye bye");
}

module_init(pci_start);
module_exit(pci_exit);

MODULE_LICENSE("GPL");

Почему так происходит, я не знаю. Возможно вы, уважаемые мои читатели, сможете подсказать мне это?😉

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: