Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Драйвер
Форум разработчиков электроники ELECTRONIX.ru > Cистемный уровень проектирования > Операционные системы > Linux
Dubov
Стало необходимо написать свой драйвер SPI устройства. Драйвер для работы из пользовательского пространства не годится, так как данные нужно принимать от АЦП по SPI в DMA буфер непрерывно(пропуски в данных недопускаются) со скоростью 100К семплов в секунду, чего нельзя добится из user space. Из пользовательского пространства я планирую только забирать блоками через драйвер.
Для моей платформы есть файлы для работы с spi в папке /drivers/spi/

Вопрос:
файлы в /drivers/spi - это всё что нужно для написания своего драйвера(модуля ядра)?

Просто ранее я писал только standalone приложения и смущает отсутствие в Linux процедур прямого обращения к регистрам (здесь всё через обёртки какие то мудрёные).
В линукс смущает то что нету так привычных мне *.h файлов где через дефайны описаны адреса всех регистров (по мануалу).
Idle
Цитата(Dubov @ Nov 12 2012, 14:21) *
Просто ранее я писал только standalone приложения и смущает отсутствие в Linux процедур прямого обращения к регистрам (здесь всё через обёртки какие то мудрёные).

Это не так, именно через прямое обращение к регистрам SoC в адресном пространстве и происходит взаимодействие с железом.

Цитата(Dubov @ Nov 12 2012, 14:21) *
В линукс смущает то что нету так привычных мне *.h файлов где через дефайны описаны адреса всех регистров (по мануалу).

Они как правило есть, но не всегда на _все_ регистры.

Для написания драйвера _устройства_ подключённого через spi слейвом, обращаться напрямую к железу не надо - это делает драйвер spi контроллера (мастера).
xor.kruger
Сначала ознакомьтесь с принципами функционирования подсистемы SPI в ОС GNU/Linux. Инфа есть в каталоге Documentation/spi исходников ядра или например в книге Essential Linux Device Drivers.
Также для примера можете подсмотреть как соведуют делать для семейства чипов AT91SAM9X link
Dubov
Цитата(Idle @ Nov 12 2012, 15:20) *
Это не так, именно через прямое обращение к регистрам SoC в адресном пространстве и происходит взаимодействие с железом.


Они как правило есть, но не всегда на _все_ регистры.

Для написания драйвера _устройства_ подключённого через spi слейвом, обращаться напрямую к железу не надо - это делает драйвер spi контроллера (мастера).

так вот этот драйвер мастера, насколько я могу понять, не может складывать данные в буфер произвольной длины, а только работает через read() или write(). Таким образом считать данные из ацп в пространстве пользователя нельзя, точнее можно, но данные будут не нерпрерывные, а спотреями, ввиду латентности операционки.

Выход: написать драйвер "с нуля" на уровне ядра.
как? пока сложно себе представляю... увидеть бы пример работы с SPI через DMA в драйвере.

Цитата(xor.kruger @ Nov 12 2012, 15:54) *
Сначала ознакомьтесь с принципами функционирования подсистемы SPI в ОС GNU/Linux. Инфа есть в каталоге Documentation/spi исходников ядра или например в книге Essential Linux Device Drivers.
Также для примера можете подсмотреть как соведуют делать для семейства чипов AT91SAM9X link

подсистема iio - мутная вещь. По моему намного проще обращаться к регистрам как в standalone приложении. Как раз для SAM9260 писал именно "напрямую" через SPI DMA, взяв пример кода из проекта плейера (тоже без ос).
Получилось просто и быстро: standalone процедура превратилась в драйвер уровня ядра. Тем более, в книге Linux Device drivers написано что на уровне ядра к регистрам можно обращаться напрямую.

IIO выдумали якобы для унифицирования, но мне показалось там слишком всё усложнено.Тем более из общения с разработчиками драйверов из AD я так и не смог выяснить зачем они написали софтовое тактирование АЦП из юзерспейс. Это же идиотизм: тактировать промышленный АЦП клоком с непредсказуемым джиттером.

Отвлёкся от темы.
Idle
Цитата(Dubov @ Nov 12 2012, 16:46) *
так вот этот драйвер мастера, насколько я могу понять, не может складывать данные в буфер произвольной длины, а только работает через read() или write()

а как должно происходить чтение? вот есть, например, spi_w8r16 два байта читает - почему она не подходит?
Dubov
Цитата(Idle @ Nov 12 2012, 17:24) *
а как должно происходить чтение? вот есть, например, spi_w8r16 два байта читает - почему она не подходит?

Вообще планировалось сделать так:
завести на уровне ядра большущий буфер(около 1 МБ), а чтение происходит в самом драйвере, пусть по 2 байта, но складываться всё должно тоже в буфер на уровне ядра (в тот самый большой буфер).
В приложении же отобразить буфер в юзерспейс и читать указатель, который возвращает драйвер.

Иными словами: User mode SPI device driver support - не подходит.

P.S. Возможно я чего-то не понимаю.

Все реализации что я до этого видел предлагают править файл борды и активировать User mode SPI device driver support. Но это не подходит.

Цитата(Idle @ Nov 12 2012, 17:24) *
а как должно происходить чтение? вот есть, например, spi_w8r16 два байта читает - почему она не подходит?

и это как я понял, не DMA чтение
Idle
Цитата(Dubov @ Nov 12 2012, 17:39) *
Вообще планировалось сделать так:
завести на уровне ядра большущий буфер(около 1 МБ), а чтение происходит в самом драйвере, пусть по 2 байта, но складываться всё должно тоже в буфер на уровне ядра (в тот самый большой буфер).

тогда можно выделить в драйвере буфер, периодически дёргать (через таймер, например) чтение по spi, складывать полученные данные в буфер
создать в драйвере chardev и отдавать буфер при чтении из созданного файла
как такой вариант?
Dubov
[quote name='Idle' date='Nov 12 2012, 18:04' post='1110736']
тогда можно выделить в драйвере буфер, периодически дёргать (через таймер, например) чтение по spi, складывать полученные данные в буфер
создать в драйвере chardev и отдавать буфер при чтении из созданного файла
как такой вариант?и(получается никаких spi_write()).
Насколько я понял мне все пытаются объяснить что линукс предоставляет независимость от платформы даже в таких задачах (чтиение зSPI по DMA в модуле ядра).

Все примеры и статьи в интеренете только для вариантов когда пофиг когда данные придут. А мне нужно потоковое накопление в буфере и периодическая отдача в юзерспейс. Со вторым я понимаю как разобраться... с первым - засада.
Idle
Цитата(Dubov @ Nov 12 2012, 18:56) *
Насколько я понял мне все пытаются объяснить что линукс предоставляет независимость от платформы даже в таких задачах (чтиение зSPI по DMA в модуле ядра).

нет, пишу как я бы это делал

Цитата(Dubov @ Nov 12 2012, 18:56) *
А мне нужно потоковое накопление в буфере и периодическая отдача в юзерспейс.

ну вот и накапливайте в буфере периодически читая при помощи дравера мастера, а в чём разница при чтении из полностю своего кода и при использовании драйвера?


upd
я понял - вы хотите зарядить dma на чтение по spi sm.gif
тогда линуксовая подсистема spi тут не нужна совсем, смотрите на реализацию мастера для своего SoC и пишите что нужно
работа с регистрами происходит так же как и на bare metal - они в памяти
работа с dma - через линуксовый api, с ним мне работать не приходилось
Dubov
Цитата(Idle @ Nov 12 2012, 19:25) *
ну вот и накапливайте в буфере периодически читая при помощи дравера мастера, а в чём разница при чтении из полностю своего кода и при использовании


Я думал всегда что в скорости. Если считывать из юзерспейс то можно пропустить данные. Частота дисркетизации АЦП 100kSps.
В модуле же ядра, если считывать по DMA можно напихать большой буфер без пропусков и потом из юзерспеейс потихонечку грести.

Как вариант, можно на ПЛИС сделать буфер, чтобы просто из юзерспейс забирать даные, но это за рамками топика.

Вот и пытаюсь:

1) найти примеры для OMAP DMA SPI без ОС (standalone), чтобы оформить как модуль ядра.
либо
2) какие-то средства линукса (api для DMA на уровне ядра)

Цитата(Idle @ Nov 12 2012, 19:25) *
работа с регистрами происходит так же как и на bare metal - они в памяти

я пока даже *.h-файл описания регистров для OMAP не нашёл. Настолько всё запутано в исходниках.
Эх, у Atmela в этом было много проще и нагляднее (AT91SAM9260.h - и всё)
alx2
Цитата(Dubov @ Nov 13 2012, 00:17) *
Я думал всегда что в скорости. Если считывать из юзерспейс то можно пропустить данные. Частота дисркетизации АЦП 100kSps.
В модуле же ядра, если считывать по DMA можно напихать большой буфер без пропусков и потом из юзерспеейс потихонечку грести.

Вам Idle так и предлагает - создать большой ядерный буфер и пихать туда данные непрерывно без пропусков. А потом из юзерспейса через read() накопленные в ядерном буфере данные выгребать. Вы говорили, что Вам не нравится получать данные путем чтения файла устройства. Почему? Фактически, такое чтение - просто копирование данных из одного участка памяти в другой...
Dubov
Цитата(alx2 @ Nov 13 2012, 16:03) *
Вам Idle так и предлагает - создать большой ядерный буфер и пихать туда данные непрерывно без пропусков. А потом из юзерспейса через read() накопленные в ядерном буфере данные выгребать. Вы говорили, что Вам не нравится получать данные путем чтения файла устройства. Почему? Фактически, такое чтение - просто копирование данных из одного участка памяти в другой...

хорошо, чтение - это просто метод забрать данные.

Мне нужен метод положить данные в буфер. Причём в большой буфер.
пока нашёл вот что: spi-omap2-mcspi.c

как настроить на приём по DMA ума не приложу(
Idle
Цитата(alx2 @ Nov 13 2012, 16:03) *
Вам Idle так и предлагает - создать большой ядерный буфер и пихать туда данные непрерывно без пропусков. А потом из юзерспейса через read() накопленные в ядерном буфере данные выгребать. Вы говорили, что Вам не нравится получать данные путем чтения файла устройства. Почему? Фактически, такое чтение - просто копирование данных из одного участка памяти в другой...

ему надо опрашивать ацп со скоростью 100KS/s т.е. каждые 10 мкс
программно через таймеры или delayed work это не получится т.к. слишком часто
вопрос тут - как написать свой драйвер spi мастера для конкретного железа под эти нужды
Dubov
Цитата(Idle @ Nov 13 2012, 20:14) *
ему надо опрашивать ацп со скоростью 100KS/s т.е. каждые 10 мкс
программно через таймеры или delayed work это не получится т.к. слишком часто
вопрос тут - как написать свой драйвер spi мастера для конкретного железа под эти нужды

Именно так.
Например подобное делал для SAM9, взяв пример из проекта wav-плейера. Там по DMA с флешки по SPI считывание происходило.

CODE
//------------------------------------------------------------------------------
/// Use PDC for SPI data transfer.
/// Return 0 if no error, otherwise return error status.
/// \param pSdSpi Pointer to a SdSpi instance.
/// \param pData Data pointer.
/// \param size Data transfer byte count.
//------------------------------------------------------------------------------
unsigned char SDSPI_PDC_read(SdSpi *pSdSpi, unsigned char *pData, unsigned int size)
{
AT91PS_SPI pSpiHw = pSdSpi->pSpiHw;
unsigned int spiIer;

// Enable the SPI clock
AT91C_BASE_PMC->PMC_PCER = (1 << pSdSpi->spiId);

// Disable transmitter and receiver
pSpiHw->SPI_PTCR = AT91C_PDC_RXTDIS | AT91C_PDC_TXTDIS;

// Receive Pointer Register
pSpiHw->SPI_RPR = (unsigned long)pData;
// Receive Counter Register
pSpiHw->SPI_RCR = size;
// Transmit Pointer Register
pSpiHw->SPI_TPR = (unsigned long)dummy_ff_block;
// Transmit Counter Register
pSpiHw->SPI_TCR = size;

// spiIer = AT91C_SPI_RXBUFF;

// Enable transmitter and receiver
pSpiHw->SPI_PTCR = AT91C_PDC_RXTEN | AT91C_PDC_TXTEN;

while(! (pSpiHw->AT91C_SPI_SR & AT91C_SPI_ENDRX));
pSpiHw->AT91C_SPI_PTCR = AT91C_PDC_RXTDIS;
pSpiHw->AT91C_SPI_PTCR = AT91C_PDC_TXTDIS;
// Interrupt enable shall be done after PDC TXTEN and RXTEN
// pSpiHw->SPI_IER = spiIer;

return 0;
}


вобщем там свелось всё к умелому обращение с регистрами SPI_RPR, SPI_RCR, SPI_TPR, SPI_TCR.
Для OMAP не думаю что сложнее, но, зараза, не могу понять...


Другой вопрос: почему в Linux драйверах всё так сложно? никогда не увидишь имён регистров прямых, всё через структуры структур структур и т.д. ?
безопасность? универсальность?
vshemm
Простейший способ "в лоб" - это char device, внутри которого 2 буфера, в один пишем, другой готов к чтению
из приложения через read(). При заполнении одного буфера DMA перезапускается на другой буфер. Либо "дмашить"
в кольцевой буфер. Как сделать лучше - см. спеки на железо.

Есть нюансы sm.gif Во-первых, вам нужно перезапустить DMA за 10мкс (по прерыванию, например). Не факт, что вы
успеете, т.к. если обращение к регистру занимает (к примеру) 1мкс, а вам нужно перезаписать 5шт - это уже 10мкс.
Некоторые DMA умны настолько, что могут сами брать дескрипторы из памяти и закольцовываться по их списку.
В этом случае перезапускать DMA нет необходимости. Если это не так и терять данные нельзя вообще - то вы
близки к теоретическому пределу железа - опять же, см. спеки на железо.
Во-вторых, при использовании своего char dev нужно убедиться, что никто через линуксовый SPI-фреймворк не
обращается к SPI контроллеру (и через DMA фреймворк тоже).

Что касается "сложности" драйверов в linux - это нормально sm.gif На самом деле там все довольно просто - почти
все разбито на слои и фреймворки для облегчения портируемости и расширения. Фактически, там используется ООП
подход, со всеми вытекающими. В итоге вход в это хозяйство усложняется (и кажется странным для людей
работавших до этого с МК осями/bare metal), но иначе никак. Вообще, такой подход характерен для всех
не-tiny мультиплатформенных ОС.

P.S. Есть фреймворк comedi, все еще стандарт де-факто для data acquisition. Но 100К семплов/с - немалая величина,
и с ней оверхед фреймворка может стать неприемлемым.
alx2
Цитата(Idle @ Nov 13 2012, 21:14) *
ему надо опрашивать ацп со скоростью 100KS/s т.е. каждые 10 мкс
программно через таймеры или delayed work это не получится т.к. слишком часто

Опросы с нужной скоростью может обеспечить контроллер SPI. Если размер сэмпла, допустим, 16 бит, то установив скорость шины SPI 1600 кбит/с, автоматически получим скорость опроса 100 kS/s.
Насколько я понял, проблема в том, что не допускается появление джиттера. А это IMHO скорее железо-специфичная чем linux-специфичная проблема. vshemm об этом уже все сказал. Если требуется за 10 мкс "вручную" по прерыванию перезаряжать DMA, то напрашивается мысль о реализации своего SPI-контроллера со встроенной буферизацией на ПЛИС...
Dubov
Цитата(vshemm @ Nov 14 2012, 00:00) *
Простейший способ "в лоб" - это char device, внутри которого 2 буфера, в один пишем, другой готов к чтению
из приложения через read(). При заполнении одного буфера DMA перезапускается на другой буфер. Либо "дмашить"
в кольцевой буфер.

так я и хотел делать! как работать с DMA на OMAP?! Ищу примеры, а пока курю даташит, но это путь через пустыню...

Цитата(alx2 @ Nov 14 2012, 08:50) *
Опросы с нужной скоростью может обеспечить контроллер SPI. Если размер сэмпла, допустим, 16 бит, то установив скорость шины SPI 1600 кбит/с, автоматически получим скорость опроса 100 kS/s.
Насколько я понял, проблема в том, что не допускается появление джиттера. А это IMHO скорее железо-специфичная чем linux-специфичная проблема. vshemm об этом уже все сказал. Если требуется за 10 мкс "вручную" по прерыванию перезаряжать DMA, то напрашивается мысль о реализации своего SPI-контроллера со встроенной буферизацией на ПЛИС...

А вот и нет. Без Linux в standalone всё делается просто и быстро. Из изерспейс просто нельзя так быстро дёргать SPI. Из юерспейс можно, например, неспешно опрашивать датчик температуры.


ГОСПОДА!
Прошу посмотреть намётаным глазом статью: http://habrahabr.ru/post/123266/
там в разделе "4.Разработка протокольного SPI драйвера уровня ядра" упоминается DMA. Никак не могу понять. Они применяют какой-то DMA API?
неужели то что мне надо...
Idle
Цитата(Dubov @ Nov 14 2012, 09:37) *
А вот и нет. Без Linux в standalone всё делается просто и быстро. Из изерспейс просто нельзя так быстро дёргать SPI. Из юерспейс можно, например, неспешно опрашивать датчик температуры.

Тут дело не в юзерспейс, из ядра (kernel space) тоже нельзя так быстро дёргать программно. Просто в ядре можно настроить dma на приём.


Цитата(Dubov @ Nov 14 2012, 09:37) *
Прошу посмотреть намётаным глазом статью: http://habrahabr.ru/post/123266/
там в разделе "4.Разработка протокольного SPI драйвера уровня ядра" упоминается DMA. Никак не могу понять. Они применяют какой-то DMA API?
неужели то что мне надо...

нет, там описана разравботка драйвера устройства, подключённого к spi слейвом
этот драйвер использует драйвер мастера через стандартный набор функций

я не думаю, что вы найдёте похожий пример т.к. то, что вам надо к линуксовой подсистеме spi отношения не имеет
посмотрите в вашем драйвере spi omap работу с регистрами и dma (если используется) - а дальше сами
Dubov
Цитата(Idle @ Nov 14 2012, 10:31) *
посмотрите в вашем драйвере spi omap работу с регистрами и dma (если используется)

гдеж эти примеры взять crying.gif
яж толкьо с SAM9 работал
Dubov
Господа! Вопрос на засыпку:
если 100кГц частота дискретизации АЦП для Linux - слишком быстро, то как понять такое:
http://wiki.analog.com/resources/tools-sof.../iio-adc/ad7998
как они собираются успевать данные забирать?
Idle
Цитата(Dubov @ Nov 14 2012, 10:53) *
гдеж эти примеры взять crying.gif

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

Цитата(Dubov @ Nov 14 2012, 15:01) *
Господа! Вопрос на засыпку:
если 100кГц частота дискретизации АЦП для Linux - слишком быстро, то как понять такое:
http://wiki.analog.com/resources/tools-sof.../iio-adc/ad7998
как они собираются успевать данные забирать?

я так понят тут вот чего - есть т.н. software triggers и hardware triggers
последние(timer counter или внешний источник) вызывают прерывания с нужной частотой и успеть можно
частота софтовых триггеров ограничена
vshemm
Цитата(Dubov @ Nov 14 2012, 09:37) *
так я и хотел делать! как работать с DMA на OMAP?! Ищу примеры, а пока курю даташит, но это путь через пустыню...

Совершенно нормальный путь sm.gif

Цитата(Dubov @ Nov 14 2012, 09:37) *
А вот и нет. Без Linux в standalone всё делается просто и быстро. Из изерспейс просто нельзя так быстро дёргать SPI. Из юерспейс можно, например, неспешно опрашивать датчик температуры.

Дергать SPI не надо, нужно запускать DMA как уже сказал Idle.

Цитата(Dubov @ Nov 14 2012, 15:01) *
Господа! Вопрос на засыпку:
если 100кГц частота дискретизации АЦП для Linux - слишком быстро, то как понять такое:
...

100КГц быстро не для линукса, а для железа, если данные читать процессором ("дергать SPI"). Если же есть нормальный DMA,
то тот же linux держит десятки и сотни МГц через iio или comedi.
Zelepuk
Цитата(vshemm @ Nov 15 2012, 00:53) *
100КГц быстро не для линукса, а для железа, если данные читать процессором ("дергать SPI"). Если же есть нормальный DMA,
то тот же linux держит десятки и сотни МГц через iio или comedi.

как он может тянуть сотни мегагерц, если через IIO я запускал родной драйвер от AnalogDevices и при частоте 100кГц загрузка процессора была более 50%.
А ещё они зачем-то software trigger придумали (дураки чтоли?), который, например, при обновлении экрана файлового менеджера MC просто переставал генерить меандр. Ну и нафиг тако тригер нужен спрашивается?
Железный другое дело, но всё равно прерывания 100 000 раз в секунду - слишком много для Linux.
Для AT91 был вариант без прерываний (на двух таймерах) на DMA - другое дело.
Dron_Gus
Неужели сложно сделать grep dma -R ./drivers/spi/ ?
Посмотрите, как другие драйвера SPI используют стандартный API DMA и сделайте так же.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.