LINUX.ORG.RU

PCI драйвер


0

0

Вопрос по структуре PCI драйвера для ядра 2.6.12: Проблема доступа к I/O memory PCI - устройства. Делаю так:

struct pci_driver hppm_driver = { /* .... */ }; int init_module(void) {

pci_register_driver(&hppm_driver);

} int cleanup_module(void) { pci_unregister_driver(&hppm_driver); } module_init(init_module); module_exit(cleanup_module);

Я так понял, что для ядра 2.6.х этого достаточно для инициализации. Далее получаю базовый адрес: int *plbase, *pbase, pci_reg; unsigned long base; base = pci_resource_start(pci_dev, 3); plbase = (unsigned int*)&base; pbase = (unsigned int*)*(plbase+1); //--- получаю базовый адрес pci_reg = *(pbase+REG_OFFSET); И получаю ОШИБКУ СЕГМЕНТАЦИИ. Подскажите. что делаю не так ???

Спасибо.


это не про PCI драйвер, это про язык "C".

Вот это вот:

    base = pci_resource_start(pci_dev, 3);
    plbase = (unsigned int*)&base;
    pbase = (unsigned int*)*(plbase+1);
    pci_reg = *(pbase+REG_OFFSET);

настолько все неверно (и АБСОЛЮТНО непонятно, что вы хотели
сделать), что даже и не знаю, как обьяснить.

P.S. форматируйте код.

idle ★★★★★
()
Ответ на: комментарий от idle

Попробую пояснить, как я это понимаю:

unsigned long base = pci_resource_start(pci_dev, 3);

base - базовый адрес памяти ввода/вывода PCI - устройства.

unsigned int* plbase = (unsigned int*)&base;

unsigned int *pbase = (unsigned int*)*(plbase+1);

А-а-а понял, сейчас (вместо этих двух функций) нужно сделать pbase = mmap(...), а затем уже обращаться к регистрам PCI - устройства.

pci_reg = *(pbase+REG_OFFSET);

Так что с точки зрения "С" все верно, ошибка именно в недопонимании PCI драйвера.

senjy
() автор топика
Ответ на: комментарий от senjy

А по-моему ты ерунду говоришь.Причем в данном случае mmap? И разве в исходниках ядра нет примеров работы с PCI устройствами?

anonymous
()
Ответ на: комментарий от senjy

> Так что с точки зрения "С" все верно,

верно ????

> unsigned int* plbase = (unsigned int*)&base;

берем адрес ЛОКАЛЬНОЙ переменной в kernel-stack

> unsigned int *pbase = (unsigned int*)*(plbase+1);

считываем МУСОР из МУСОРА, это верно ???

вам нужен ioremap_nocache(base)

idle ★★★★★
()
Ответ на: комментарий от anonymous

Вот блин... Я же привел пример, как я это понимаю, не работает, и попросил подсказать, ЕСЛИ КТО ЗНАЕТ!!!, как правильно надо сделать. А что ерунда так это и понятно, так как не работает и сообщать об этом мне не надо. Если сам не знаешь, то не отвечай, а то абы ляпнуть.

Еще раз повторю, может не так объяснил: для инициализации, регистрации (сканирования шины и т.д.) я использовал - pci_register_driver(&hppm_driver);

далее при помощи

unsigned long base = pci_resource_start(pci_dev, 3); получаю из конфигурационного пространства PCI базовый адрес памяти ввода/вывода. Далее этот адрес мне нужно отобразить в адресное пространство моего драйвера чтобы обращаться к регистрам моей PCI-платы, как это сделать в Linux'e я не знаю, ну и навскидку предложил что-нибудь из mmap. Сейчас думаю, может использовать ф-ции:

int check_mem_region(unsigned long start, unsigned long len);

void request_mem_region(unsigned long start, unsigned long len, char *name);

Если, кто знает, и у кого есть время подскажите, как правильно сделать, может я вообще все не правильно делаю... Так ткните носом...

senjy
() автор топика
Ответ на: комментарий от idle

Предыдущий ответ писал, не видя этого сообщения.

Значит при использовании

void *ioremap_nocache(unsigned long phys_addr, unsigned long size);

я получу указатель, по которому уже смогу работать с устройством? больше ни каких манипуляций не требуется?

Т.е.

U32* base_addr = (U32*)ioremap_nocache(base, size);

u32 reg = *(base_addr + REG_OFFSET);

Так?

senjy
() автор топика
Ответ на: комментарий от idle

Ага. Спасибо. А, если ф-цией

base = pci_resource_start(pci_dev, 3);

я получаю адрес памяти ввода/вывода (3-ий BAR), а не портов ввода/вывода все равно readl()/writel() использовать надо. Напрямую никак чтоли ?

senjy
() автор топика
Ответ на: комментарий от senjy

> я получаю адрес памяти ввода/вывода (3-ий BAR),
> а не портов ввода/вывода

для портов у нас inl/outl, не путайте.

> Напрямую никак чтоли ?

на x86 можно (умеючи), но не надо.

idle ★★★★★
()
Ответ на: комментарий от idle

Понятно. Спасибо большое за помощь.

Результаты использования

reg = readl(base_addr+4);

reg = *(base_addr+4);

оказались одинаковы, просто пишут, что второй вариант не на всех архетиктурах пройдет.

senjy
() автор топика
Ответ на: комментарий от senjy

> просто пишут, что второй вариант не на всех
> архетиктурах пройдет.

он и на x86 не всегда пройдет, пример:

   *cmd_reg_addr = CMD_ВЫПУСТИТЬ_ШАССИ;
   *cmd_reg_addr = CMD_ПРИЗЕМЛЯЦЦА;

не удивляйтесь, если посадка будет "на брюхо" :)

если уж так хочется руками, не забывайте про
"volatile".

idle ★★★★★
()
Ответ на: комментарий от idle

Это понятно, я про саму возможность, что при желании можно... Просто в принципе пофиг как...

Вот попробовал 3 варианта:

static struct pci_dev dev, *pdev; 1. static int __init drv_init(void) { static int rg_result, *base_addr;

pdev = pci_find_device (VENDOR_ID, DEVICE_ID, NULL);

rg_result = pci_enable_device(pdev);

rg_result = pci_read_config_dword(pdev, 0, base_addr); Выдает ошибку сегментации. }

2. static const struct pci_device_id ids[] = {

{PCI_DEVICE(VENDOR_ID, DEVICE_ID)},

{ 0, },

};

MODULE_DEVICE_TABLE(pci, ids);

static struct pci_driver pci_driver = { .name = "tst_pci", .id_table = ids, };

static int __init drv_init(void) { static int rg_result, *base_addr, ; static unsigned long base_addr0;

rg_result = pci_register_driver(&pci_driver);

base_addr0 = pci_resource_start(pdev, 3);

base_addr = (unsigned int*)ioremap_nocache(base_addr0, 256*4);

rg_result = readl(base_addr+4); } Выдает не правильное значение.

3. static int __init drv_init(void) { static int rg_result, *base_addr; static unsigned long base_addr0;

pdev = pci_find_device (VENDOR_ID, DEVICE_ID, NULL);

rg_result = pci_enable_device(pdev);

base_addr0 = pci_resource_start(pdev, 3);

base_addr = (unsigned int*)ioremap_nocache(base_addr0, 256*4);

rg_result = readl(base_addr+4);

Правильный результат!!! }

Вот и не понятно, что тут не так. Пишут, что первый вариант устаревший (для старых ядер) и так делать не рекомендуют, а необходимо заменить все эти pci_find_device(), pci_find_class () и т.д. на pci_register_driver(). Заменил, однако, что-то не то см п.2. Чего-то значит не доделоваю. Почему-то работает пример 3, где смесь двух вариантов. Правильно или нет я определяю по вычитыанию регистра со смещением +4, где находится известное мне значение. Подскажите, что делаю не так.

senjy
() автор топика
Ответ на: комментарий от senjy

Форматируйте код, если хотите, чтобы я хотя бы попытался его
прочитать.

у вас тут смесь какая-то дикая, вам нужен pci_enable_device().

pci_register_driver - это хорошо и надо, но не из той оперы.

idle ★★★★★
()
Ответ на: комментарий от idle

Вот кусок кода, который вроде бы пока :) работает:

pdev = pci_find_device (VENDOR_ID, DEVICE_ID, NULL);
rg_result = pci_enable_device(pdev);
base_addr0 = pci_resource_start(pdev, 3);
base_addr = (unsigned int*)ioremap_nocache(base_addr0, 256*4);
rg_pci = readl(base_addr+4);

В rg_pci - правильное значение регистра со смещением +4.
Вроде так?
А для чего тогда pci_register_driver? Получается, что для горячей замены только (CardBus -устройств)?

senjy
() автор топика
Ответ на: комментарий от senjy

pci_enable_device() - работает с устройством

pci_register_driver() - ну не знаю, даже как
обьяснить, регистрирует ваш драйвер, в sysfs,
к примеру. смотрите код.

idle ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.