LINUX.ORG.RU

Интерфейс /dev/mem забагован?

 , ,


0

1

В общем, стоит задача программно реализовать (в userspace) один аппаратный интерфейс через дерганье gpio-выводами. Вариант через sysfs даже не рассматривался - медленно до безобразия (взять хотя бы то, что у интерфейса есть параллельная двунаправленная шина, и придётся побитово загонять в неё данные и побитово же переключать направление). Напрашивается прямой доступ к регистрам контроллера через /dev/mem.

Уж сколько дней ломаю голову, ситуация следующая. Через sysfs все 11 нужных пинов работают (во всех режимах). И если предварительно через sysfs настроить пин на выход (испробовал на трёх), то и через /dev/mem получается им помигать/прочитать. А вот переключить направление пина (вход или выход) прямой записью в регистр не удаётся никак - значение записывается, но как будто не в регистр, а в какой-то кэш. И остаётся в этом кэше даже после переоткрытия memory mapping и даже при следующем запуске программки. Но стоит поменять направление порта через sysfs, как значение в этом регистре обновляется тем, что туда записалось через sysfs.

Думал, может, я чего-то не так делаю с этим отображением. Поставил из репозиториев утилиту devmem2. С ней всё то же самое. Далее предположил, что, быть может, направление пина не так просто переключается. Или подумал, вдруг направление порта хранится в поле структуры и не синхронизируется со значением регистра. Но нет, в структурах только указатели на функции, которые обращаются к этим регистрам через readl/writel.

А теперь самое главное. Решил пока вырубить полностью gpio controller через device tree на всём порту, с которым работаю. Пробую опять помигать через devmem2. Теперь получается и мигнуть, и попереключать направление… но только младшими 8-ю битами. В остальные 24 бита значения пишутся (и считываются), но нужного действия не оказывают. Пока такое подозрение, что запись uint32 в отображение инициирует побайтовое копирование, регистры же могут быть перезаписаны только целиком по 4 байта.

Кто что может посоветовать, кроме как свой модуль ядра писать? Куда копать, как быть?

P. S. Платформа freescale imx6dl, ядро 3.14.


Напиши уже модуль для ведра, потому что если ты лезешь из userspace напрямую в регистры, которые уже управляются каким-то драйвером, происходить может всякое непредсказуемое.

Если тебе религия не позволяет делать закрытый драйвер, сделай прослойку, которая будет делать claim на нужные gpio и принимать нечто из userspace и дёргать в нужном порядке gpio плюс слушать что там валится из шины.

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

Так ведь я уже отключил драйвер (через device tree), ответственный за нужный мне порт, и регистры конкретно этого порта теперь никем не управляются (смотрю по /proc/iomem). И всё равно ничего не работает.

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

Значит кумарь даташит ещё раз, ты что-то делаешь не так.

Dark_SavanT ★★★★★ ()

bit-banging в юзерспейсе ИМХО очень плохая идея, пригодная разве что для микроскопических объемов данных на инерфейсе, некритичном к таймингам (т.к. юзерспейс процесс вполне может уйти поспать на несколько сот миллисекунд скажем, никого не предупредив - ну, там, своппинг случится, или еще какое-то сверхважное мероприятие у ядра).

NiTr0 ★★★★★ ()

По поводу младших 8-и бит был неправ, как оказалось. И через devmem2 всё работает. И ещё прояснилось, что дело действительно в кэшировании. Записываемое в регистр направления порта (вход-выход) значение записывалось в некий кэш и вообще не попадало в регистр, даже после закрытия memory mapping и завершения процесса (WTF?). Зато если сразу после записи в этот регистр вызвать msync с параметром MS_ASYNC (пробовал ранее с параметром MS_SYNC - возвращало EINVAL), то всё работает. Как это понимать? Неоднократно встречались наставления, что в случае отображения в /dev/mem msync вообще не нужно вызывать. И почему кэшируется именно регистр направления порта, а стоящие впритык по соседним адресам регистры данных (их два по обе стороны, второй показывает фактическое значение сигнала на пинах) - никогда?

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

Совершенно непонятные вещи происходят. Как оказалось, msync помогает далеко не всегда, закономерность мне так и не удалось выявить. Не давал покоя тот факт, что ручное манипулирование портами через утилиту devmem2 работало стабильно. Взял её исходники, поковырял, и оказалось, что совершенно по-разному себя ведёт обращение к типизированному (volatile uint32_t * ) и нетипизированному, но динамически кастуемому (volatile void * ) указателю. В первом случае ничего не работает (запись в регистр не происходит, но изменённое извне значение (допустим, путём вызова system("devmem2 <…>") ) читается), во втором - работает. WTF?

Причём полученные ключом -s ассемблерные листинги идентичны. Компилятор arm-linux-gnueabihf-gcc v4.9.3.

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