Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Прикрутить стандарный драйвер к новой шине
Форум разработчиков электроники ELECTRONIX.ru > Cистемный уровень проектирования > Операционные системы > Linux
Hoodwin
Вот примерно такая задачка нарисовалась. Имеется ПЛИС, подключенная к процессору через интерфейс VLYNQ. И хотя исходно VLYNQ предназначен для peer-to-peer соединения, наличие ПЛИС позволяет реализовать несколько подсистем. Ну, для простоты пусть это будут UART 16550, несколько штук. Вопрос заключается в том, можно ли подцепить стандартные драйверы системы и не писать ничего заново?

Сам по себе интерфейс VLYNQ, если отбросить его инициализацию, представляет собой аналогию шины: он прозрачно транслирует чтение/запись в определенном окне адресного пространства в пакетные обращения по шине, плюс еще умеет делать некие конфигурационные обращения. В общем, структурно даже несколько на PCI похоже, только без hotplug. То есть, после того, как сам VLYNQ поднят, можно вычислить базовый адрес регистров UART 16550 и он ничем не будет отличаться от такого же UART, встроенного в процессор. То есть, казалось бы, можно как-то приживить к работе с таким UART-ом обычные драйвера, вроде serial8250.

Теоретически, правда, есть пара нюансов:
1. Tсли линк у VLYNQ пропадет, то чтение через VLYNQ завешивает систему. То есть, по хорошему перед чтением чего-то нужно проверять линк на исправность (статус в локальном регистре контроллера VLYNQ). Но вот на практике никогда у нас линк не падал пока.
2. Адресное пространство шины VLYNQ вообще-то тоже 32-битное. Но так как оно доступно через окошко в адресном пространстве процессора, то единовременно видно только около 64М адресов, которые мапятся через регистр страниц. То есть, по хорошему, обращение драйверов к регистрам устройства должно вестись через spinlock, ассоциированный с драйвером шины VLYNQ.
3. Прерывания по VLYNQ идут как специальный пакетик между пакетами данных. Хотя контроллер VLYNQ поддерживает 32 бита флагов прерываний и умеет сам выделять наиболее приоритетный флаг, процессору он показывает только одно прерывание, собственно от контроллера VLYNQ. То есть, нужно как-то уметь воткнуть между обработчиком прерывания стандартного драйвера UART и собственно UARTом обработчик прерывания VLYNQ.

Почитав linux device drivers 3-го издания я так и не смог до конца уяснить, требует ли новая шина написания новых драйверов, если устройства имеют стандартный интерфейс управления. Точнее говоря, из повествования следует, что требует, если я устройство зарегистрирую именно на этой шине. С другой стороны, теоретически же можно сам драйвер шины написать так, что он найдет устройства UART на шине VLYNQ, но зарегистриует их как простые platform_device. И тогда они окажутся на шине platfrom_bus, где подбор драйверов идет просто по имени. Так делать можно, или это грабли?

По поводу прерывания. В ядре 2.6.37 есть исходничек drivers/vlynq/vlynq.c. К сожалению, он нам не подходит целиком, так ка заточен на одно устройство peer-to-peer, да и применялся он, судя по всему, только для arch/mips/ar7. Но вот там прерывания интересно сделаны. 32 прерывания от VLYNQ просто замаплены на диапазон номеров прерываний выше контроллера прерываний.
Код
static irqreturn_t vlynq_irq(int irq, void *dev_id)
{
        struct vlynq_device *dev = dev_id;
        u32 status;
        int virq = 0;

        status = readl(&dev->local->int_status);
        writel(status, &dev->local->int_status);

        if (unlikely(!status))
                spurious_interrupt();

        while (status) {
                if (status & 1)
                        do_IRQ(dev->irq_start + virq);
                status >>= 1;
                virq++;
        }

        return IRQ_HANDLED;
}

Короче, обработчик прерывания VLYNQ, похоже, дергает сам обработчики виртуальных прерываний. То есть, можно зарегистрировать несколько platform_device, в ресурсах которых заказать прерывания с номером, соответствующим диапазону vlynq, и обработчик основного прерывания от vlynq сам передаст прерывание драйверу UART. И тогда, по идее, стандартный драйвер должен отработать.

Еще есть вопрос, можно ли иным способом повторно использовать код обычного драйвера UART? Ну, например, написать обертки ко всем его функциям, чтобы учитывать специфику шины (spinlock'и, проверки линка и пр).
SM
по поводу vlynq - тут не подскажу.

а вот по поводу уарта - все это одним мановением левого пальца - если это "8250.c" - в структуре plat_serial8250_port порта есть пойнтеры на ф-ции serial_in и serial_out, определив которые (на соотв. ф-ции нижележащего драйвера шины VLYNQ), драйвер уарта будет обращаться к портам уарта через них (при этом неважно, какой там UPIO_xxx стоит, эти ф-ции пересиливают флаги UPIO_). Тоже касается IRQ - передается в этой же структуре, и передать именно виртуаьный IRQ, который дернет драйвер VLYNQ.
Hoodwin
Ну, допустим. Но ведь чтобы переопределенные serial_in, serial-out заработали, придется все равно регистрировать устройство как platform_device, так? Система то должна к нему драйвер поискать.
SM
Цитата(Hoodwin @ Jan 28 2013, 16:46) *
Ну, допустим. Но ведь чтобы переопределенные serial_in, serial-out заработали, придется все равно регистрировать устройство как platform_device, так? Система то должна к нему драйвер поискать.


Это забота драйвера шины. Гляньте, например, на i2c - i2c_register_board_info(...), или как драйвера SPI делают - ну а переопределять уж Ваша забота, при заполнении platform_data на каждый уарт, который передастся через драйвер шины драйверу уарта, когда тот его регистрировать будет. Вам по аналогии с I2C надо писать некий vlynq_register_board_info
Hoodwin
Поглядел два примера:
1) i2c_register_board_info -- drivers/i2c/{core.c, board_info.c}
2) mdio_bus -- drivers/net/phy/{mdio_bus.c, phy.c, phy_device.c}

По итогам получается следующее:
1) платформа регистрирует ресурсы устройства (i2c-adapter, mdio_bus)
2) драйверу дергается функция probe
3) драйвер проверяет, что периферийное устройство есть, и регистрирует само устройство в системе.
4) Функция регистрации запускает сканирование шины, либо сканирование ресурсов, статически привязанных к шине (board_info)
5) Появление новых ресурсов снова дает поиск драйверов, прилепленных к шине, вызов для них probe и т.д.

То есть, получается, драйвер VLYNQ должен определить, что за устройство реально в него воткнуто, а затем самостоятельно зарегистрировать ресурсы новых устройств. Вопросы:
1) В контексте какого процесса вызываются функции probe драйвера? Правильно ли я понимаю, что это может быть либо в контексте инициализации ядра до вызова /init, либо в контексте вызова утилиты insmod, если драйвер собран в модуль, либо еще как-то там через udev если он будет по hotplug'у подцеплен?
2) Можно ли регистрировать устройство на шине platform_bus, если реально ресурсы устройства принадлежат шине vlynq? Не несет ли это в себе какие-то подковерные проблемы? Или системе вообще пофигу на подчинение шин?

Например, если я хочу поднять простой UART с помощью драйвера serial8250, то мне нужно либо зарегистрировать именно platform_device с именем serial8250, либо писать драйвер порта для работы с шиной VLYNQ. Второе, может даже и правильнее, однако дольше в реализации.
Если же выбрать первое, то устройству UART придется ткнуть в ресурсы, которые целиком в ведении контроллера VLYNQ.
SM
А зачем все эти трудности? Вам реально PnP нужен на VLYNQ?

Сделайте проще. По аналогии с I2C. в board_xxxx.c создаете структурку, в которой список всего того, что на Вашей шине висит. Его отдаете драйверу vlynq (пишете соотв. vlynq_register_board_info) - оно по списку вызывает все probe для всех девайсов. Далее, делаете в драйвере VLYNQ функции типа vlynq_dev_iowrite и vlynq_dev_ioread. Которые обеспечивают доступ к регистрам того, что на шине висит. Их статически, через инициализационную структуру, передаете в platform_data (поля serial_in и serial_out) драйверу 8250, после чего драйвер 8250 работает с своим 8250 функциями драйвера шины, что корерктно с точки зрения того, кому чьи ресурсы даны. И драйвер 8250 не требует никакой доработки, только реализовать пару ф-ций в драйвере vlynq.

Ну а если еще и модулем грузить.... И PnP... Вам шашечки, или ехать?

Если хотите совсем просто и быстро - вообще драйвер vlynq не трогайте и не подключайте, напишите прямо в инициализации борды получение ресурсов VLYNQ, и свои ф-ции serial_in и serial_out, работающие через VLYNQ, там же рядом в файле борды. И регистрируйте сразу драйвер 8250, с передачей ф-ций доступа через platform_data ему. Но тут с reuse потом будут проблемы sm.gif
Hoodwin
Да дело не в PNP. Я хочу, собственно, двух вещей:
1) минимальную защиту от дурака сделать. То есть, что бы при изменении прошивки ПЛИС для другого проекта с другим набором периферии система ругнулась и не подняла старые устройства на VLYNQ.
2) Чтобы не надо было собирать ядро по всяком мало-мальски пустяковому поводу. Проблема, естественно, не в том, чтобы его собрать, а в том, что система поддержки изделий после поставки должна быть как-то унифицирована. И переделка всего ядра - это формально слишком большое изменение, которое потребует тестирования всей системы. При замене только стандартного модуля можно говорить о тестировании подсистемы.

Про шашечки изречение я не понял, я пока спросил про то, как устроен стандартный путь в линукс, когда требуется повторно использовать уже имеющийся код драйвера с новой шиной. Или стандартный путь - это шашечки и есть? sm.gif

Что касается структур типа board_info, и, если смотреть глубже, вообще встраивания описания железа платы непосредственно в ядро, то налицо тенденция отказа от этой практики и замены ее на передачу описателя device_tree в виде внешней структуры как параметра запуска ядра. Придумано именно для унификации ядер и упрощения себе и пользователю жизни впоследствии. Пока что это не наш путь, так как еще пока не доделаны ядра для прочих кристаллов семейства, а все те, что есть, конфигурируются в рантайм. Слишком много работы, и это задача не первого этапа.

В отношении vlynq наш путь заключается в том, чтобы создать полноценную инфраструктуру управления шиной и устройствами на ней. Например, для 8250 нужно указать прерывание, но реально VLYNQ имеет свою подсистему прерываний, для управления которой нужно зарегистрировать в ядре еще один irq_chip. То есть в той или иной степени драйвер шины нужен, и "просто и быстро" не сделать. Кроме того, нам нужен еще драйвер собственно главного сопра на ПЛИС, который будет уметь делать трансферы по VLYNQ со скоростями до 35 МБ/с, и это естественно будет уже DMA, а не жалкие там обертки воде ioread/iowrite.

Пока что мне видится такая схема:
1) Регистрируем ресурсы для драйвера vlynq_bus.
2) запускаем драйвер, он регистрирует устройство vlynq_bus типа platform_device, определяет функции чтения-записи vlynq_шины, создает обработчик прерывания для шины.
3) Этот же драйвер сканирует удаленное устройство и регистрирует в системе первое и единственное vlynq_device с dev_id, равное прочитанному из конфигурационного пространства Vlynq удаленного устройства.
4) Мы делаем insmod и втыкаем в систему драйвер в виде модуля. который говорит, что он знает и поддерживает устройство с нужным dev_id.
5) Драйвер просто декодирует dev_id и регистрирует в системе перечень новых ресурсов. Ресурсы UART регистрируются в виде platform_device, ресурсы для собственных драйверов регистрируются как vlynq_device.
6) Система сама подключает драйверы UART с шины platform_bus к новым устройствам UART.
7) Мы делаем insmod и втыкаем в систему драйверы новых устройств для своих прикладных задач. Система связывает их с устройствами, делает probe и стартует драйвер для приложений.
8) Для приложений появляются интерфейсы через /dev, /sys или /proc и прикладные программы начинают работу с ними.

Это правильный путь?
SM
Да, путь в общем правильный. Только я не знаю, как при работе через insmod передать драйверу уарта ссылки на функции чтения/записи через драйвер vlynq шины - там ведь нет platform_data. Я пока не ходил путем с insmod, но, боюсь, что это потребует и патча для собственно драйвера 8250. Поэтому, на мой взгляд, сначала надо сделать драйвер vlynq_bus со статическим описанием периферии, подключенной к шине, без чтения всяких dev_id, собираемый внутрь ядра - и работу непатченного 8250 через него. (а если например через этот уарт загрузочная консоль? Какой такой inbsmod...). Вот это я называю "ехать". И когда оно заработает, уже наводить "шашечки" - делать поддержку insmod, PnP, и прочих рюшечек, облегчающих жизнь при тестировании и т.д.
Hoodwin
Хм. А почему нет? Что мне мешает сделать точно такой-же вызов register_platform_device из драйвера устройства VLYNQ и поместить туда указатель на platform_data именно для serial8250? А сами функции можно сделать прямо в драйвере устройства как обертки для функций, предоставляемых собственно драйвером шины.

Со статическим описанием ресурсов UART я уже сделал. В целом оно дышит, пока немного криво, так как я сам UART пока кривой собрал в ПЛИС, но это к теме не относится sm.gif Ну и пока с прерыванием разбираюсь, какие функции надо сделать для irq_chip, какой тип самим прерываниям сделать, и можно ли динамически выделить еще некоторый набор номеров irq или надо статически под них таблицу расширять?

Сами UART'ы нам нужны для внешних устройств, консолей для них не будет никогда. Для консолей есть два родных UART'а прямо в SOC. Но есть проекты, где нужно до 6 UART, причем иногда в полудуплексном исполнении RS-485. У меня для этого дела написан кор на VHDL, который 1) умеет управлять третьим состоянием выходного драйвера, 2) умеет сигналы DTR и RTS использовать для конфигурации типа порта: полудуплекс или полный дуплекс и ленивый/неленивый TXEN (для полного дуплекса RS-422). В итоге можно прямо через stty рулить конфигурацией драйвера порта.

В общем, программу минимум я уже почти выполнил до рюшечек я уже почти добрался, поэтому озаботился вопросами архитектуры. К сожалению, не хватает бэкграунда в в виде best practices. А просто ковыряя ядро можно всякой муры начитаться из рудиментов прошлого.
SM
Цитата(Hoodwin @ Feb 18 2013, 17:02) *
Хм. А почему нет? Что мне мешает сделать точно такой-же вызов register_platform_device из драйвера устройства VLYNQ и поместить туда указатель на platform_data именно для serial8250? А сами функции можно сделать прямо в драйвере устройства как обертки для функций, предоставляемых собственно драйвером шины.

Как я понимаю, в принципе на шине могут быть не только уарты, в разные девайсы, и платформ-даты у них по формату разные. Поэтому надо, чтобы каждый из таких драйверов сам знал, что если он нашине vlynq, и в конфиге есть шина vlynq вообще (по #if, чтобы исходник собирался и без vlynq вообще для других систем) то должен использовать функции работы через vlynq. Это для insmod. А статически проще конечно, когда все платформ-даты статически заданы.

Цитата(Hoodwin @ Feb 18 2013, 17:02) *
Ну и пока с прерыванием разбираюсь, какие функции надо сделать для irq_chip, какой тип самим прерываниям сделать, и можно ли динамически выделить еще некоторый набор номеров irq или надо статически под них таблицу расширять?

если это вопрос, и я его правильно понял, то он решен в дровах gpio, там этих irq немерено объявляется. То есть, по аналогии. Или это просто размышления вcлух по теме sm.gif
Hoodwin
Цитата
Как я понимаю, в принципе на шине могут быть не только уарты, в разные девайсы, и платформ-даты у них по формату разные.

Могут. Но вот для platform_bus определена такая функция match, которая подбирает драйвер по имени устройства:
Код
static const struct platform_device_id *platform_match_id(
                        const struct platform_device_id *id,
                        struct platform_device *pdev)
{
        while (id->name[0]) {
                if (strcmp(pdev->name, id->name) == 0) {
                        pdev->id_entry = id;
                        return id;
                }
                id++;
        }
        return NULL;
}

/**
* platform_match - bind platform device to platform driver.
* @dev: device.
* @drv: driver.
*
* Platform device IDs are assumed to be encoded like this:
* "<name><instance>", where <name> is a short description of the type of
* device, like "pci" or "floppy", and <instance> is the enumerated
* instance of the device, like '0' or '42'.  Driver IDs are simply
* "<name>".  So, extract the <name> from the platform_device structure,
* and compare it against the name of the driver. Return whether they match
* or not.
*/
static int platform_match(struct device *dev, struct device_driver *drv)
{
        struct platform_device *pdev = to_platform_device(dev);
        struct platform_driver *pdrv = to_platform_driver(drv);

        /* match against the id table first */
        if (pdrv->id_table)
                return platform_match_id(pdrv->id_table, pdev) != NULL;

        /* fall-back to driver name match */
        return (strcmp(pdev->name, drv->name) == 0);
}


Ну то есть platform_data подбирается соответственно устройству и драйвер, который по имени подойдет, будет знать о структуре этих данных. так что проблем быть не должно, и я могу хоть динамически выделить память и настроить все структуры.

Цитата
если это вопрос, и я его правильно понял, то он решен в дровах gpio, там этих irq немерено объявляется. То есть, по аналогии. Или это просто размышления вcлух по теме


Ага, поглядим. На самом деле уже почти все понял, вопрос только про выделение диапазона свободных номеров irq, для которых система будет регистрировать новые обработчики.
SM
Цитата(Hoodwin @ Feb 18 2013, 18:58) *
Ну то есть platform_data подбирается соответственно устройству и драйвер, который по имени подойдет, будет знать о структуре этих данных.

Драйвер, который подойдет, ясное дело, что знает о формате своего "platform_data"... А вот откуда будет знать драйвер шины, когда будет регистрировать драйвер найденного на ней девайса, по какому смещению в этой структуре, абсолютно уникальной для каждого драйвера (она и обзывается то <driver_name>_board_info обычно, и опредена в хедере каждого драйвера), находятся места для указателей на read и write функции? И вообще, в 8250 они есть, а в других драйверах их может не быть, даже обычно нет. Это тогда уже надо систему PnP во всей красе поднимать...
Hoodwin
Что-то пока накопал, что все прерывания живут в большой таблице дескрипторов от 0 до NR_IRQS-1. И для того, чтобы подвесить обработчик, нужно заранее зарезервировать в таблице дополнительные номера прерываний. Я то как-то надеялся, что номера можно выделять в рантайме.



Цитата
А вот откуда будет знать драйвер шины, когда будет регистрировать драйвер найденного на ней девайса, по какому смещению в этой структуре, абсолютно уникальной для каждого драйвера (она и обзывается то <driver_name>_board_info обычно, и опредена в хедере каждого драйвера), находятся места для указателей на read и write функции?


Не понял. Драйвер шины регистрирует устройство _своей_ шины (VLYNQ) и цепляет к ней устройство с dev_id, какой прочтет из самого устройства (FPGA). Дальше драйвер FPGA, вставленный insmod'ом, увидит свое устройство (match вернет 0), и когда будет делать probe, сообразит, что таком-то dev_id, соответствуют, скажем, два UART'а. Для них драйвер создаст структуры platform_device, platform_data, заполнит их и дернет register_platform_device. А указатели в них он вставит на свои собственные функции-обертки, которые будут просто ссылаться на подобные функции драйвера шины vlynq_bus. А драйвер шины эти функции объявит как экспортируемые модулем драйвера шины. Вот как-то так. Или это уже и есть PnP во всей красе?
SM
Ну, то есть, Вы хотите для всех-всех устройств, которые теоретически можно повесить на vlynq, и которые сейчас поддержаны в ядре линукса (имеют драйвера), иметь некий список сопоставления их dev_id с какими то функциями по заполеннию их board_info (которые для каждого драйвера устройства, а НЕ ШИНЫ, свои)? Короче, чтобы драйвер vlynq заранее знал о всех драйверах всех устройств, которые могут воткнуть в эту шину? Это не PnP... Это какое то новое веяние в линуксостроении... Получается, что для поддержки нового устройства на шине надо добавлять информацию о нем и о том, что он хочет в своем board_info в драйвер шины. Но это не логично - для добавления нового устройства должно быть достаточно написать новый драйвер, а драйвер шины не трогаит.

А "PnP во всей красе" - это 8250_pnp.c, это например в rtc/rtc_cmos.c, и сами его недра в drivers/pnp.

Цитата(Hoodwin @ Feb 18 2013, 21:14) *
Для них драйвер создаст структуры platform_device, platform_data, заполнит их и дернет register_platform_device.

Вот как Вы себе это представляете в реализации? Откуда драйвер шины узнает, что для UARTа эта структура называется "struct plat_serial8250_port" и имеет по смещению XXX от начала поля serial_in и serial_out, где надо передать ф-ции обмена с девайсом через шину, а какой нибудь дядя вася из ЮАР через пару лет напишет драйвер своего супердевайса, который тоже может работать с VLYNQ, и у него структура будет называться "struct super_device_board_info" и там будут поля write_reg и read_reg, аналогичные по назначению serial_in/out у 8250, но совсем по другому смещению. То есть, по-Вашему, этот дядя вася будет вынужден еще и драйвер шины пропатчить, вписав в список свой драйвер и добавив туда функцию заполнения board_info для себя любимого? Ну или хотя бы этот список как-то передавать через platform_data самому драйверу шины... Если, конечно, он тоже не по insmod подключается.

или вообще это на уровне udev решать. Драйвер шины ему послал "обнаружено то, то и это", а udev сам решит, что с ними делать и как.
Hoodwin
Цитата
Ну, то есть, Вы хотите для всех-всех устройств, которые теоретически можно повесить на vlynq, и которые сейчас поддержаны в ядре линукса (имеют драйвера)


Снова не понимаю Вас. LDM подразумевает, что:
1) Устройства регистрируются в системе на определенной шине.
2) Драйверы регистрируются в системе на определенной шине.
3) Автоматическое связывание устройства и драйвера делается в рамках шины одного типа.
То есть, для того, чтобы UART завелся на шине VLYNQ, должен быть написан специальный драйвер, который зарегистрируется на шине VLYNQ и найдет там свое устройство. Исходный вопрос как раз и сводился к тому, есть ли какой-нибудь стандартный способ связывания имеющихся драйверов от старых шин с новыми шинами.

Цитата
иметь некий список сопоставления их dev_id с какими то функциями по заполеннию их board_info (которые для каждого драйвера устройства, а НЕ ШИНЫ, свои)?

Вот посмотрите, и в 8250_pci.c, и в 8250_pnp.c большая часть текста посвящена перечислению поддерживаемых конфигураций а-ля board_info. Например, в 8250_pci.c все бы хорошо, и UART в общем-то почти всегда стандартный, однако же отличаются количество портов в одном BAR'е, смещение между соседними базовыми адресами, частота бодового генератора. И в итоге - длиннющая простыня настроек.
Так что в линуксостроении полно таких приемчиков. Но я то как раз и не так собирался делать. Попробую еще раз.

1) Для VLYNQ пишется драйвер шины. Он ничего не знает о конкретных устройствах на шине, но знает, что у каждого есть свой ID. Для лучшего понимания его архитектуры мыслите его как шину PCI с одним единственным слотом.
2) При старте драйвер шины просто поднимает шину, видит там одно устройство и регистрирует его на шине VLYNQ. И все.
3) Для этого ID пишется маленький мини-драйвер, который находит на ней свое устройство по ID, опознает его, и регистрирует ресурсы всех коров внутри ПЛИС как некие platform_device, например. Именно в этом драйвере сидят все аналоги board_info, а не в драйвере шины и не в ядре. Если пересобрать прошивку для ПЛИС и сменить там ID, но система просто не подберет драйвер, в системе не появятся ресурсы. В этом и есть "защита от дурака" и уход от ненужной сборки всего ядра.
4) Как только система видит новые устройства, добавленные мини-драйвером на шину platform_bus, она сама находит нужные драйверы и запускает их. При этом мини-драйвер может создать любые нужные platftrofm_data или другие структуры, необходимые для правильной настройки устройства.
SM
Цитата(Hoodwin @ Feb 19 2013, 00:57) *
3) Автоматическое связывание устройства и драйвера делается в рамках шины одного типа.
То есть, для того, чтобы UART завелся на шине VLYNQ, должен быть написан специальный драйвер, который зарегистрируется на шине VLYNQ и найдет там свое устройство. Исходный вопрос как раз и сводился к тому, есть ли какой-нибудь стандартный способ связывания имеющихся драйверов от старых шин с новыми шинами.

Ну в общем я не так понял исходные данные. Я понял вопрос в том, чтобы поднимать на шине стандартные драйверы, написав ТОЛЬКО драйвер шины, и не трогая код "стандартных" драйверов девайсов, и не делая новых драйверов девайсов (и это основное в вопросе - не трогать ничего, связанного с девайсами, а писать только драйвер шины). Поэтому предложил статическое объявление, которое это позволяет, по крайней мере с драйвером уарта. А так да, выход один, писать некие "врапперы" на стандартные драйверы, которые пробуют себя на шине VLYNQ, зная, как с этой шиной работать, и патчить те драйверы, которые не умеют работать через функции ввода-вывода, заданные снаружи, чтобы они этому научились.
Hoodwin
Да я думаю, что написать маленький минидрайвер не будет большой проблемой.

С драйвером UART вляпался в дурацкую проблему. Прерывания в VLYNQ передаются пакетом из одного 32-битного слова, которое предлагается писать в регистр установки флагов прерывания. То есть, реально можно только взвести прерывание, если соответствующий ему бит принятого слова установлен. Сейчас у меня слово передается только если произошло изменение в каком-либо сигнале прерывания внутри ПЛИС. Но реально работают только единички, они взводят биты в регистре VLYNQ контроллера внутри SOC. А драйвер UART 8250.c написан так, что он не пытается разгрести все прерывания порта за один раз. Он отрабатывает определенную последовательность действий и выходит, видимо рассчитывая, что если она не отобьет прерывание, то он снова зайдет в обработчик и его обработает. В итоге получается, что после первого захода он все не отрабатывает, прерывание от кора UART внутри ПЛИС не снимается, кор vlynq не видит изменения статуса и не шлет пакет в SOC. В итоге второго прерывания уже не возникает. Ну и много интересных эффектов возникает на этой почве... Теперь вот думаю, как это залечить наиболее грамотно. То ли прерывание от UART передергивать после чтения регистра статуса прерывания, чтобы фронт второй возник, то ли думать, как научить VLYNQ транслировать именно состояние линии прерывания.
SM
Цитата(Hoodwin @ Feb 19 2013, 13:14) *
То ли прерывание от UART передергивать после чтения регистра статуса прерывания, чтобы фронт второй возник, то ли думать, как научить VLYNQ транслировать именно состояние линии прерывания.

Ну я в похожем случае когда-то именно на передергивании остановился, как на меньшей крови (это был процессор TMS 55xx, а у него нет физически прерываний по уровню)
Hoodwin
А я извратился, сделал регулярную трансляцию всех флагов прерывания по VLYNQ. Вроде даже ничего получилось, только маскировать прерывание может только драйвер конкретного устройства, драйвер шины не может. Но я пока не вижу, чем это особенно плохо. В общем, работает UART, который подымается на старте системы через статическую регистрацию platform_device. Буду тогда потихоньку переводить на модульный вариант.
Hoodwin
А вот еще совсем простой вопрос. Если я хочу собирать модуль для ядра, но в отдельном проекте, в отдельном каталоге (не в ядре). Но при этому у меня сам проект ядра есть уже развернутый. Как наиболее правильно настроить среду для сборки модуля? Ну, например, нужно прикрутить kernel headers и какие-то объектные модули ядра, которые линкуются в модуль .ko. Как правильно указать ссылку на это все? Просто тупо в каталоге проекта сделать симлинк, или можно как-нибудь "официально" разместить (экспортировать) все эти штуки в какой-нибудь общий путь рядом с кросс-компилятором? Подозреваю, что можно сделать и так и так, лишь бы Makefile правильно под это написать, но хочется сделать это так, чтобы можно было в одной машине в SCM положить, на другой достать и собрать. А из этого следует, что патчить ручками содержимое Makefile-ов (путей там всяких) - неправильно. Есть у кого-нибудь опыт подобного?
SM
Цитата(Hoodwin @ Feb 20 2013, 20:45) *
А из этого следует, что патчить ручками содержимое Makefile-ов (путей там всяких) - неправильно. Есть у кого-нибудь опыт подобного?

Ну у TI опыт есть... на примере graphics SDK для powervr - там дрова собираются модулями отдельно. там именно в makefile первой строчкой идут пути к хидерам ядра. А чтобы это стало официальным - об этом надо написать в README

у nvidia тоже опыт есть. у них скрипт сборки, а пути опцией указываются.
sasamy
Цитата(Hoodwin @ Feb 20 2013, 20:45) *
Как наиболее правильно настроить среду для сборки модуля?


http://www.kernel.org/doc/Documentation/kbuild/modules.txt

=== 2. How to Build External Modules
Idle
Цитата(Hoodwin @ Feb 20 2013, 20:45) *
какие-то объектные модули ядра, которые линкуются в модуль .ko

это какие и зачем?

Цитата(Hoodwin @ Feb 20 2013, 20:45) *
Есть у кого-нибудь опыт подобного?

external modules, редко у кого нет - не патчами же держать
Hoodwin
SM
Цитата
там именно в makefile первой строчкой идут пути к хидерам ядра. А чтобы это стало официальным - об этом надо написать в README

Ну, так именно этого то хочется избежать. Хочется сделать так, чтобы в тех файлах, что попадут в архив (source control), не было никаких путей. У одного пользователя они одни, у другого другие. Нужно, чтобы в README было написано: настройте N переменных окружения: путь к кросс-компилятору, путь к ядру, путь, куда его собирать, путь к ..., и все. Дальше просто берем из репозитория срез и делаем make.

sasamy
Спасибо, почитал. Попробую-что-то слепить. К сожалению, сборка ядра от linux-c6x несколько отличается от того, что там написано. Несколько драйверов из родного дерева я собирал в виде модулей. Ни разу нет ничего похожего на /lib/modules/'uname -r'/extra. Зато нашел их примерно тут:
Код
./Build/rootfs/kernel-modules-c6x/lib/modules/2.6.34-MORS8.el-20120628-jffs2/build

Пробовал искать по файлам проекта (Makefile искал) слово kernel-modules-c6x, так не нашел. Как так вышло, что они именно в rootfs залетают?
Зато заголовки живут прямо в Build:
Код
./Build/kernel-headers


Idle
Ну, я грешным делом подумал, что для оптимизации таблиц для динамического связывания на каждый вызов внешней функции геренрируется специальный код вида 'jmp <addr>', который статически линкуется с проектом, и в итоге в рантайме нужно править только один адрес. Так во всяком случае раньше делали с динамическими библиотеками. Или с .ko никто подобным не занимается, и тупо нужно править каждый конкретный вызов внешней функции?


Добавление. Не, нашел kernel-modules. В главном Makefile'е:

Код
# install kernel modules here
MOD_DIR = $(BLD)/rootfs/kernel-modules-$(ARCHe)

# install kernel headers here
HDR_DIR = $(BLD)/kernel-headers
KHDR_DIR = $(HDR_DIR)/usr
SM
Цитата(Hoodwin @ Feb 21 2013, 15:18) *
SM

Ну, так именно этого то хочется избежать. Хочется сделать так, чтобы в тех файлах, что попадут в архив (source control), не было никаких путей. У одного пользователя они одни, у другого другие. Нужно, чтобы в README было написано: настройте N переменных окружения: путь к кросс-компилятору, путь к ядру, путь, куда его собирать, путь к ..., и все. Дальше просто берем из репозитория срез и делаем make.


так makefile понимает переменные окружения, ну или типа:

make KERNEL_HEADERS=/xx/xx/xx CROSS_COMPILE=arm-unknown-linux-gnueabi
make install DESTDIR=/куды/впихнуть

хотя это тоже грубо переменные окружения

просто лично я предпочитаю вписать все в макефиле в таком случае, чтобы не вспоминать, что же там за переменные.
sasamy
Цитата(Hoodwin @ Feb 21 2013, 15:18) *
Как так вышло, что они именно в rootfs залетают?


Например так
Код
TOP_DIR ?= $(HOME)
KERN_DIR = $(TOP_DIR)/imx233/linux-2.6.35.3-11.09.01-sk
RFS_DIR = $(TOP_DIR)/imx233/rootfs
ARCH ?=arm
GCC ?=arm-none-linux-gnueabi-

obj-m += tstdrv.o

all:
        $(MAKE) ARCH=$(ARCH) CROSS_COMPILE=$(GCC) -C $(KERN_DIR) M=$(PWD) modules
install:
        $(MAKE) ARCH=$(ARCH) CROSS_COMPILE=$(GCC) -C $(KERN_DIR) M=$(PWD) INSTALL_MOD_PATH=$(RFS_DIR) modules_install
clean:
        $(MAKE) ARCH=$(ARCH) CROSS_COMPILE=$(GCC) -C $(KERN_DIR) M=$(PWD) clean
Idle
Цитата(Hoodwin @ Feb 21 2013, 15:18) *
Ну, я грешным делом подумал, что для оптимизации таблиц для динамического связывания на каждый вызов внешней функции геренрируется специальный код вида 'jmp <addr>', который статически линкуется с проектом, и в итоге в рантайме нужно править только один адрес. Так во всяком случае раньше делали с динамическими библиотеками. Или с .ko никто подобным не занимается, и тупо нужно править каждый конкретный вызов внешней функции?

А, это не - не знаю как работает.
SM
Цитата(Idle @ Feb 21 2013, 17:36) *
А, это не - не знаю как работает.

Там .fixup секция, она архитектурно-зависимая, где хранится весь список релокейшенов и внешних связей. Короче линковка как линковка...
Hoodwin
А еще дурацкий вопрос. Как узнать строчку OS release для текущего собираемого ядра, если я делаю через кросс-компиляцию?
Добрые дяденьки из TI придумали вставлять в название ядра текущую дату сборки, в итоге у меня драйвер собрался с сегодняшней датой, и на вчерашнем ядре работать отказался. Пришлось ядро пересобрать и залить на TFTP. Я, наверное, этот идиотизм порежу и уберу дату из названия ядра. Но все же даже если даты в названии не будет, то как получить то название целиком? uname -r не прокатит, а полное имя там составное, из названия архитектуры, платы, размещения rootfs (initramfs vs jffs2) и endian. При таком раскладе как его узнать?

И еще, KERN_DIR в примере выше - это путь к сборке ядра или к исходникам?

Цитата
Там .fixup секция, она архитектурно-зависимая, где хранится весь список релокейшенов и внешних связей. Короче линковка как линковка...


А как линкер отличает, какие символы считать правильными unresolved external и помещать в .fixup, а какие - нет? Я так понимаю, что отличие .ko от .o только в том и состоит, что в нем должны остаться только правильные unresolved externals, которые объявлены как внешние, линкуемые при загрузке.
SM
Цитата(Hoodwin @ Feb 21 2013, 18:05) *
А как линкер отличает, какие символы считать правильными unresolved external и помещать в .fixup, а какие - нет? Я так понимаю, что отличие .ko от .o только в том и состоит, что в нем должны остаться только правильные unresolved externals, которые объявлены как внешние, линкуемые при загрузке.

Как я понимаю, все unresolved external там есть. А правильный он или нет, разбирается insmod, и ругается, если что. А отличие от ".o" лишь в одной букве расширения.

А как это Вы такого добились, чтобы оно даты сравнивало? Я периодически вот собираю TI-шные powervr дрова как модули, никакой привязки. Даже как правило пересборка ядра при незначительных изменениях не приводит к тому, чтобы пересобирать модули. Это наверное какая-то особая доп. фича при сборке модуля.
Hoodwin
Говорю же, добился не я, а дядьки, которые сочиняли проект linux-c6x. sm.gif

В общем итоге, драйвер в виде модуля я собрал, platform_device из него зарегистрировал, устройство (UART внешний) теперь поднимается при insmod и вполне себе живет.
Прикрутил еще vlynq_device на шину, теперь на шине есть device вида /sys/bus/vlynq/devices/<dev-id>/

Что-то только проморгал я, где надо было зарегистрировать и что, чтобы было вида /sys/bus/vlynq/<bus_no>/devices/<dev-id>/.
Но пока это не страшно, так как шина все равно одна. Но правильнее, когда там номер стоит, в некоторых чипах бывают по два кора VLYNQ (кто их видел?).



Цитата
Как я понимаю, все unresolved external там есть. А правильный он или нет, разбирается insmod, и ругается, если что. А отличие от ".o" лишь в одной букве расширения.


Не, на этапе сборки оно тоже разбирается. Сделал просто функцию aaaa, и вот:
Код
__modpost: vmlinux .../Linux/Drivers/m8_hub/m8_hub.o  [.../Linux/Drivers/m8_hub/m8_hub.o FORCE]
  MODPOST 1 modules
WARNING: "aaaa" [.../Linux/Drivers/m8_hub/m8_hub.ko] undefined!


так что умеет оно видеть ссылки, что не в ядре.
SM
Цитата(Hoodwin @ Feb 21 2013, 22:15) *
WARNING: "aaaa" [.../Linux/Drivers/m8_hub/m8_hub.ko] undefined!

так что умеет оно видеть ссылки, что не в ядре.


Уметь то умеет, ясное дело, но на сколько я помню, это всего лишь варнинг, .ko все равно собирается, с этим символом.
Hoodwin
Сейчас вот приделал совсем красиво, чтобы драйвер сам детектировал UART'ы, сам конструировал их platfrom_data, а потом вызывал pltform_device_register. С регистрацией проблем нет. Все поднимается и работает. А вот с rmmod вылезла какая-то бяка:
Код
m8_hub 00:c004: removing serial ports
------------[ cut here ]------------
WARNING: at .../linux-c6x/drivers/base/core.c:131 device_release+0xcc/0xe4()
Device 'serial8250.1' does not have a release() function, it is broken and must be fixed.
Modules linked in: m8_hub(-) [last unloaded: m8_hub]
Stack from 85083d74:
        80398484 800200c4 8033ccb1 80350245 00000083 801e453c 87f4f708 87eb5e00
        87f4f728 80020134 00000014 00000000 00000000 80350281 85083db8 801e453c
        80350281 87eb5780 87f4f7a0 87f4f7a4 87f4f708 80190bec 0000026b 87f4f724
        87f586e8 8706c53c 0000026b 80192508 801ec3c0 85083f68 85083de8 87053d80
        8706c508 87f580a4 87f585e4 87f586da 87069f00 85083f68 801e92e4 87053d80
        87f4f600 8706c508 8706c508 801e9418 0000026b 8706c508 87f586e8 801e9e6c
Call trace: [<800200c4>] warn_slowpath_common+0x84/0xa8
[<801e453c>] device_release+0xcc/0xe4
[<80020134>] warn_slowpath_fmt+0x2c/0x40
[<801e453c>] device_release+0xcc/0xe4
[<80190bec>] kobject_release+0x74/0xf4
[<80192508>] kref_put+0x8c/0x148
[<801ec3c0>] platform_device_unregister+0x1c/0x34
[<87f580a4>] m8_hub_remove+0xa4/0xe0 [m8_hub]
[<801e92e4>] driver_sysfs_remove+0x34/0x6c
[<801e9418>] __device_release_driver+0xfc/0x11c
[<801e9e6c>] driver_detach+0xfc/0x110
[<801e9160>] bus_remove_driver+0xc8/0x168
[<87f58588>] m8_hub_exit+0x48/0x80 [m8_hub]
[<80062e64>] sys_delete_module+0x3b0/0x444
[<8009ac08>] sys_newstat+0x50/0x68
[<80007a3c>] ret_from_syscall_function+0x0/0x4

---[ end trace acd3569ad3b12098 ]---
Module m8_hub unloaded!


В интернете накопал, что это проблема драйвера serial8250. Чего оно хочет то глобально? Чтоб device_release был, хоть пустой но присутствовал. или еще чего?

---
А, вот какая штука. все устройства должны за собой уметь подтирать, а если сами не умеют, то "подтиралки" должны быть определены для типов и классов устройств. Сам драйвер serial8250 за собой подтирать не умеет, так как он не знает, кто именно и как выделял память под структуру описания устройства. В итоге нужно просто зарадить в указатель struct device.releade адрес функции, которая ничего не делает. И тогда все без ругани работает.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.