Показаны сообщения с ярлыком hardware. Показать все сообщения
Показаны сообщения с ярлыком hardware. Показать все сообщения

понедельник, 24 января 2011 г.

ftd2xx library on wine

Сегодня рассмотрим решение проблемы запуска windows-приложений, написанных для различных девайсов и использующих библиотеку ftd2xx (libftd2xx) для связи с ними, под wine. Вначале я кратко расскажу про мотивацию к такой экзотике, потом о принципе реализации и граблях, на которые удалось наступить, и в конце дам ссылочку на патч.

И так, исходные данные, автокоррелятор от Avesta project, подключающийся к ПК по USB, и программа Irtac для работы с прибором. В приборе USB аппаратно реализовано на микросхеме ftdi, что можно увидеть по логам при подключении устройства (dmesg или /var/log/messages). Программа Irtac собрана под windows с использованием коммерческой (закрытой) версии библиотеки ftd2xx. Реализации под linux нет и ждать не приходится, а перегружаться в винду ради одного прибора очень не хочется, тем более, что реально он часто нужен не один. Есть вариант с использованием виртуальной машины. Неопенсорсный virtualbox умеет прокидывать USB в систему, и, например, поляриметр от thorlabs так заставить работать удалось. Ситуацию осложняет то, что такой трюк оборачивается существенными тормозами в работе виртуалки, и Irtac тупо отваливался по таймауту. Под wine сама программа хоть и запускается, но работать с прибором не хочет. Оно и понятно, если я ей подсуну windows-версию библиотеки, то она понятия не имеет как работать с USB  в linux, а linux-версию программа, собранная под винду, по ясным причинам подцепить не сможет. И так, выход один, надо разобраться, как wine реализует свои библиотеки, и написать обёртку (fake) для linux-версии libftd2xx.
Немного рутины - необходимо достать исходники установленного у вас wine, распаковать их, потом наложить прилагаемый патчик, запустить configure и make, а после скопировать ftd2xx.dll.so и ftd2xx.dll.fake в /usr/lib/wine/ftd2xx.dll.so и /usr/lib/wine/fakedlls/ftd2xx.dll (или куда там установлен штатный wine) соответственно. Как получился этот патч? Сначала всё казалось проще пареной репы: у нас есть две библиотеки с идентичными функциями, значит, нам даже толком не надо ничего писать, а просто "связать" вызов функции из программы с той же функцией в нативной библиотеке. Сказано - сделано. Но было бы наивно предполагать, что Irtac сходу заработает, тем более что мы не знаем функции, которые он использует. Для тестирования я взял примеры из libftd2xx1.0.2 и собрал их как под linux, так и под винду (через mingw). Тут количество используемых функций и порядок их вывода строго определён, и можно отследить на каком месте что у нас порушилось. На первом этапе я никак не мог понять, как сделать этот самый вызов функций с одним и тем же названием и метался от простой сборки wine-библиотеки статиком с libftd2xx.a до написания обёртки с другим именем, из которой бы цеплялась нативная библиотека, а сама обёртка использовалась бы нашей fake-либой, и тем самым, мы избежали бы конфликта имён. Однако, найти правильный путь помогла wine-библиотека glu32. Здесь реализовано фактически именно то, что нам необходимо. А вся изюминка кроется в том, что заголовки функций для windows- и для linux-версии ftd2xx немного различаются. Это обстоятельство учтено в ftd2xx.h, и правильные заголовки определяются при сборке define-ами. Нам же необходимо подставить define-ы, которые использовались бы при сборке под виндой (чтобы прога могла найти функции), хотя фактически, собираем мы всё это дело под пингвином. Однако, после этого прозрения вылезла ещё одна проблема, и заключается она вот в чём. Функции, которые мы хотим отдать wine-у на экспорт, перечисляются в ftd2xx.spec, где первый параметр - некий номер функции... Я не понимаю, что это за номер, но на практике, этот номер как-то связан с тем, где именно находится данная конкретная функция. И если мы правильно пишем обёртку, но вместо номера ставим "@", что в соответствии с http://linux.die.net/man/1/winebuild означает автоматическое присвоение номера, то не факт, что этот номер совпадёт с реальным. И при попутке запустить программу получим что-то типа:
wine: Call from 0x7ef918f0 to unimplemented function FTD2XX.dll.32, aborting
wine: Unimplemented function FTD2XX.dll.32 called at address 0x7ef918f0 (thread 0009), starting debugger...
Но это и ключ к разгадке! Если мы знаем, какую именно функцию дергает приложение, а это можно установить по исходникам, то данный вывод означает, что эта функция должна находится по номеру 32, но её там почему-то нет. Ставим этот номер вместо собаки в ftd2xx.spec, и двигаемся дальше...

В результате, я написал обёртки для всех функций из ftd2xx.h кроме тех, где есть префикс W32 (просто я не знаю как уложить их параметры в концепцию wine). Получилось 49 штук. Из них я подобрал номера к 16-ти. Для этой процедуры я использовал примеры  BitMode, EEPROM/read, LargeRead, Simple и Timeouts. Вывод данных программ был идентичен про запуске в linux, и при запуске под wine-ом. В качестве "прибора" использовалась заглушка в виде микросхемы FT232RL с замкнутыми Rx/Tx и питанием от USB-порта. В ближайшее время буду пробовать на реальном железе.
Использование данного материала, а также поправки и исправления неточностей только приветствуются.
Патчик можно найти тут.

upd: Оказывается, кое-что всё-таки "уже украдено до нас". Так, вот тут автор ещё в 2005 году сделал вызов основных функций через libusb. Кроме того, он приводит spec, в котором уже указаны адреса для всех функций.. Что-то мне подсказывает, что их узнать можно гораздо проще, чем я описал выше...

upd2: Поразительно, но мне действительно удалось заставить эту прогу корректно работать с прибором! Однако, обнаружилось пара нюансов:
1. Значения Timeout-ов и LatencyTimer пришлось увеличить в 10 раз возможно, хватило бы и пары, но взял с запасом, и оно попёрло)
2. Если функция FT_ListDevices используется с флагом FT_LIST_BY_INDEX, то перед вызовом нативной функции стоит вызвать FT_CreateDeviceInfoList, т.к. без этого всё или виснет или crash-ится.
Обновлённый патчик со всеми этими изменениями, а также с некоторым количеством отладочной информации для wine-1.2_rc7 можно найти тут.

понедельник, 26 июля 2010 г.

MacrOS: Simply RTOS on Macroses

При всей бесперспективности повторного изобретательства велосипедов порой от этого праздного занятия просто невозможно удержаться. И вот сейчас, зачем нужна ещё одна пародия на RTOS, когда их и так полно, и тут есть несколько соображений. Во-первых, как ни крути, а своя рубашка ближе к телу. И во-вторых, после некоторого опыта писания прошивок под наши приборы я осознал, что для полного счастья мне необходимо совсем не много. Это немного и является отличительной особенностью этого произведения.
1. Таймеры. Программные, с характерной длительностью в 10-тки миллисекунд. Неблокирующие ожидания таймаутов. Wait-ы внутри задачи.
2. Кооперативная многозадачность. В топку приоритеты, вытеснения, жесткое распределение времени. Специфика такая, что прошивка состоит из множества небольших задач, которые не несут сложных расчётов, а большую часть времени ждут срабатывания какого-нить флага или исключения, или запускаются периодически, чтобы собрать данные.
3. Быстрое определение необходимости выполнения задачи, и переход на следующую в противном случае. Переключение контекста - дорогостоящая операция, поэтому хотелось бы понять, чего ждёт задача и проверить это условие не входя в контекст задачи.
Вот, пожалуй, три вещи необходимые для счастья.
Про ОС как таковую тут можно говорить вообще с большой натяжкой. По сути нам необходим лишь планировщик, который бы в цикле перебирал все задачки. Прототипом был планировщик из какого-то примера с www.atmel.com. Уж очень мне нравится фирма, и даташиты толковые, и контроллеры функциональные, производительные и не обременённые ненужными сложностями, и примеры есть, и gcc умеет компилить под avr. Да, изначально MacrOS написана под AVR архитектуру, но для переноса на другой контроллер требуется лишь переписать код для миллисекундного таймера, т.к. всё остальное написано на C. Это в теории. на практике надо переписывать всё, что касается прерываний, а это usart, spi и т.д. Но вернёмся к планировщику. К сожалению, он не умел управлять контекстом, а наличие такой фичи сильно облегчает написание каждой отдельной задачи.
Спасение я нашел в функции setjmp. Но её использование весьма нетривиально, и именно тогда родилась идея, "а почему  бы не обернуть ей вызов в макрос?" Сказано - сделано. Заводим на каждую задачку структуру, типа task, в которой храним контекст и ещё немного инфы, о которой скажу позже. Пишем макрос MOS_Scheduler(), который сохраняет контекст текущей задачи и делает longjmp в контекст планировщика. Всё просто! Планировщик, получив управление, переходит к следующей задаче, сохраняет свой контекст и делает longjmp в следующую task-у, и так по кругу. Но это слишком медленно, учитывая что часто надо проверить лишь состояние флага. Делаем финт ушами, в структуру задачи добавляем ссылку на фнкцию checker и пару переменных, которые будем передавать этой функции как параметры. Одна - указатель, вторая - значение которое надо проверить. После в планировщике вместо перехода в задачу сначала делаем вызов checker-а, а по его результатам определяем, требуется ли переход в задачу. И тут Остапа понесло: можно сделать checker-ы разного типа, которые будут проверять налицие флага, отсутствие, делать anr или or со значением в указателе. В общем, практически всё, что может понадобится для отслеживания нажатия на кнопку или отслаживания аппаратного флага.
Теперь ввести таймеры не составит большого труда. Ставим условие, что проверка на таймаут должна быть быстрой, а старт можно сделать и дольше. Создаём массив с таймерами. Заводим глобальный счётчик, который инктементируется от аппаратного таймера. При старте высчитываем время, когда таймер закончится, и сбрасываем флаг таймаута. В прерывании от аппаратного таймера пробегаемся по всему массиву, и выставляем флаги для тех таймеров, чьи значения меньше текущего. Значения этих флагов позже проверятся в задаче, которая ждёт таймаута.
На этом пока всё, минимально-необходимое окружение есть. Надеюсь, что это окружение будет полезно не только мне, и велосипед приобретёт статус ноу-хау. Исходники имеются и будут выложены, как я приведу их в божеский вид. Нетерпеливым просьба писать на мыло.

четверг, 21 января 2010 г.

Atmel AVR USI TWI library

Довелось на днях разбираться с тем, что предоставляет нам Atmel для взаимодействия с устройствами по протоколу i2c. Производители решили, что реализовывать раздельно spi и i2c много чести, и предоставили нам набор "рассыпухи", из которой мы сами можем соорудить один из интерфейсов, или придумать свой. Основные составляющие этого набора это сдвиговый регистр для обмена данными, 4-х битный счётчик для подсчёта количества пришедших бит, и различные переключалки, для выбора типа протокола, тактового генератора и пр плюс кое-какие прерывания под это дело. Всего три 8-ми битных регистра.
Первым делом глянул в гугл на предмет готовых реализаций i2c на этом железе. Однако, может я плохо смотрел, но нашел только исходники на аппаратный twi. Про TWI на USI были упоминания рядом с IAR (навороченная среда разработки под МК), но что тянуть палёный и непонятный код. Было решено написать "библиотечку" на основе исходников под аппаратный twi.
Собственно, оттуда я взял только названия функций. Посылка на старт, стоп и клоки реализуется программно, т.о. функции у нас получились блокирующие, и во время пересылки мы не сможем больше ничего сделать. Но поскольку twi мне нужен только для начальной конфигурации пары девайсов, то такая не совсем оптимальная реализация меня устраивает.
Во время внимательного чтения даташита на предмет управления с этими тремя регистрами, и попытки оживить всё это дело, обнаружилась пара совершенно тонких и не очевидных нюансов, ради которых и я пишу эту заметку. В документации о них написано весьма вскользь, и, как мне кажется зря.

1. Во первых, нет чёткого разделения на мастера и слейва. И логика устроена так, что при опознавании на начала передачи выставляется флаг USISIF в регистре USISR и одновременно с этим блокируется линия SCL. Особенность в том, что флаг срабатывает и тогда, когда старт формирует сам девайс, и, т.о. чтобы дальше была возможность контролировать SCL необходимо этот флаг сбросить.

2. Передача данных из USIDR на порт. Этот момент менее описан в доках, по этому за правильность не ручаюсь, но когда я сначала выставлял на порт SDA нуль, а после писал данные в USIDR и пытался их оправить, то линия никак на мои действия не реагировала. Однако, когда я сначала выставил на SDA единичку, после чего, чтобы таки выставить его в нуль для посылки start, записал 0x00 в USIRD, и только после этого записал адрес устройства и попытался его отправить, то всё заработало. Эврика, как говорится :) Итого, рецепт такой. Пишем в PORT на пины SDA и SCL по единичке, потом делаем их выходами, а после рулим через USIDR и флаг USITC из USICR.

Исходники можно взять по адресу http://galilley.at.nsu.ru/usitwi.tar.bz2, но есть нюанс, проверена работоспособность только функций на запись, функции на чтение не отлажены, хотя теоретически должны работать.

среда, 20 февраля 2008 г.

Спецификация USB, заметки по сути.

USB - универсальная последовательная шина, позволяющая подключать до 127 устройств самого различного плана и имеющая пропускную способность до 480Мбит/сек (USB 2.0), бла-бла и бла-бла... Это все знают, это можно легко прочитать на той же вики и интересно это может быть лишь для общего развития. ИМХО куда интересней собрать какую-нить свою поделку взаимодействущую с ПК через эту шину.

Задача:

Организовать soft-usb на МК AVR серии ATmega.

Но перво-наперво надо понять как в принципе работает эта шина.

Пойдем путём настоящих джедаев и скачаем с www.usb.org полную версию спецификации 2.0...

Признаться, стиль в котором там всё составлено мне совершенно не понравился, уж слишком много воды, повторений и пр.. Можно было, конечно, написать тоже более лаконично и логично, но признаем что это просто адский труд и скажем спасибо разработчикам за то, что имеем. И так, постараюсь выделить самую суть, которую просто необходимо понимать для того чтобы работать с usb на самом низком уровне.

  1. Все транзакции определяются хостом: устройство должно принять данные по запросу хоста, устройство может передать данные только после того как хост спросит, нет ли данных на отправку и пр.
  2. Каждое устройство при подключении проходит стадию конфигурирования: получает от хоста уникальный адрес, передаёт информацию о типе, о производителе (об этом чуть позже).
  3. Хост идентифицирует каждое устройство по его адресу на шине, поле адреса занимает 7 байт, отсюда максимальное число устройств на шине - 127 (нулевой используется при конфигурации и не может быть назначен в качестве рабочего)
  4. Данные по шине передаются пакетами, каждый пакет начинается с поля синхронизации, затем идёт поле PID, определяющее тип пакета, дальнейший формат определяется типом пакета. Особо стоит обратить внимание на типы Token, DATA, Handshake. (В спецификации описание взаимодействия конечного устройства с ПК переплетается с описанием взаимодействия ПК с USB-хабом, функционал хаба мы не предусматриваем и не рассматриваем всё что его касается)
  5. Для контроля правильности передачи в каждом пакете предусмотрено поле CRC.
  6. Для кодирования данных в линии используется формат NRZI в котором постоянство логического уровня в линии соответствует единице, а изменение - нулю.
  7. Процесс передачи/получения данных выглядит следующим образом: хост посылает token с адресом устройства и указанием чего хочет - либо передать данные, либо принять их. В первом случае после token-а следует пакет с данными, после которого устройство должно ответить ASK в случае успеха или NAK в случае невозможности принять данные, ошибок CRC и пр. Во втором случае устройство само передаёт пакет данных и ожидает ASK от хоста, а с случае неудачи повторяет попытку.
  8. Процесс конфигурирования. ИМХО самая сложная стадия... Есть token-пакет типа SETUP. После него устройство должно ожидать пакета DATA0 в котором в поле данных особым образом сформирован запрос хоста (request), стандартные запросы описаны в секции Standart Device Request спецификации. Наше устройство должно уметь распознавать все типы запросов. Кроме прочего в запросе есть информация о том, куда должен быть направлен следующий пакет данных - в хост или в устройство.
  9. Более того, устройство должно уметь формировать ответ на запросы - должно сообщить хосту о своих свойствах через так называемые дескрипторы (Standart USB Descriptor). Полное их описание также довольно хорошо представлено в спецификации. Более того, именно этим разделам стоит уделить особое внимание. Отмечу лишь что через дескрипторы устройство сообщает свои Class, SubClass, Protocol, которые уже активно используются на уровне ОС и драйверов.

P.S. в дальнейшем тема обязательно будет продолжена, но уже с точки зрения железа и подготовки прошивки для МК.