EFSL: добавление поддержки новой аппаратуры носителя данных |
![]() |
Добавил(а) microsin |
Перевод раздела "6.3 Adding support for a new endpoint (0.2)", стр. 36, документации EFSL Embedded Filesystems Library - 0.3, 2005 г. [6.3 Добавление поддержки новой аппаратуры (new endpoint)] Эта секция дает пошаговые инструкции, как сделать портирование EFSL на другую аппаратуру носителя данных. Это может потребоваться в том случае, если разработчик не сможет найти подходящей реализации обращения к своей аппаратуре хранения данных. Как Вы можете видеть, мы сделали линейную модель взаимодействия объектов, которая довольно проста. Файл (File) в файловой системе (Filesystem) работает в соответствии с её специфичной организацией. Ниже мы найдем объект раздела (Partition), который отвечает за трансляцию адресации относительно раздела (partition relative addressing) в адресацию относительно диска (disc-based LBA addressing, LBA переводится как Logical Block Addressing, см. [1]). Объект диска (Disc) хранит в себе таблицу разделов (partition table), и имеет прямое взаимодействие с менеджером кэша IOMan (см. [2]). Внутри IOMan все запросы к секторам диска объединяются друг с другом. IOMan проверяет, какие сектора нужно прочитать непосредственно с диска, а какие - из памяти кэша (если эти сектора ранее уже были прочитаны с диска), и какие сектора должны быть синхронизированы с диском (записаны обратно на диск). Если требуется, то при чтении/записи диска запросы переводятся на самый нижний уровень - объект аппаратного интерфейса (hwInterface). Аппаратный интерфейс hwInterface отвечает за 3 функции: Все запросы базируются на основе адреса сектора, где сектор - часть диска, блок из 512 байт, который выровнен на 512-байтную границу байтового адреса. В этом примере мы создадим для EFSL новый порт и добавим поддержку переноса данных на примере аппаратного интерфейса "pigeon carrier" ("голубиная почта"). Инициализация аппаратуры состоит в том, что нужно накормить голубя и сказать ему, где находятся данные. Чтение/запись повлечет за собой предоставление птице сектора и возможности полететь. Выполним следующие шаги: 1. Выбор имени для нашего порта. Нам понадобится имя, чтобы создать нужные определения в исходном коде. В нашем примере было выбрано имя PIGEON_CARRIER (ГОЛУБИНАЯ_ПОЧТА). Соответствующее конечное имя для аппаратуры получилось HW_ENDPOINT_PIGEON_CARRIER. 2. Проверка размерности целых чисел. Откройте файл inc/types.h и создайте новую запись для нашей голубиной почты. Возможно, что нас уже устроит существующий набор типов, и можно сделать простое копирование. 3. Добавление нашего порта в файл interface.h. Найдите файл interface.h в папке inc/. Добавьте секцию для голубиной почты (выше секции #else ... NO_INTERFACE_DEFINED).
4. Выберите Ваш порт в файле conf/config.h (определением HWENDPOINT_PIGEON_CARRIER). 5. Создайте файлы исходного кода. Создайте файл заголовка (*.h) в папке inc/ и файл модуля (*.c) в папке src/interfaces. Для нашего примера это будут файлы pigeon.h и pigeon.c соответственно. 6. Добавьте компиляцию и линковку нового модуля в Makefile, либо настройте это в Вашей среде разработки. Базовая работа проделана, теперь осталось написать код, который будет выполнять реальную работу. [6.3.1 hwInterface] Эта структура представляет образ нижележащего аппаратного обеспечения. В ней имеется отдельное поле, которое обязательно должно присутствовать (так как EFSL использует его), однако в него Вы можете добавить и другие поля, которые могут потребоваться для доступа Вашего драйвера к аппаратуре. Основное правило для встраиваемых систем - рекомендуется иметь эту структуру как можно меньше по размеру. Пример:
[6.3.2 if_initInterface] Эта функция будет вызвана только один раз, когда объект аппаратуры будет инициализирован вызовом efs_init(). Код в процедуре if_initInterface переводит аппаратуру в состояние готовности к использованию. Прототип функции if_initInterface:
Не обязательно, однако желательно, чтобы Вы заполнили поле hw->sectorCount количеством секторов на носителе данных. Это поле используется для проверки корректности запросов к секторам. В случае успешной инициализации функция if_initInterface должна вернуть 0. Пример функции if_initInterface:
[6.3.3 if_readBuf] Эта функция отвечает за чтение сектора с диска и сохранение его данных в буфере пользователя. На входе функции - объект аппаратуры, адрес и указатель на память, где находится буфер сектора размером в 512 байт. Пожалуйста, будьте особо внимательны к границам буферов, так как обычно функцию if_readBuf будет вызывать IOMan, и если у Вас случится переполнение буфера, то может повредиться содержимое кэша, что наверняка повлечет за собой трудно отлавливаемые ошибки и непредсказуемое поведение системы. Прототип функции if_readBuf следующий:
Параметр address задается в адресации LBA, относительно начала диска. Это означает, что если представить весь диск как огромный байтовый массив с побайтовой адресацией byte_address, то второй параметр address функции должен быть вычислен по формуле: address = byte_address / 512; Если Вы обращаетесь к старому диску, или к аппаратуре, которая имеет другую систему адресации данных, то нужно будет пересчитать адрес, чтобы привести его к нужной схеме адресации. Имейте в виду, что нет поддержки для секторов, которые имеют размер не равный 512 байт. В случае успеха функция if_readBuf должна возвратить 0. Пример простейшей реализации функции чтения if_readBuf:
[6.3.4 if_writeBuf] Функция if_writeBuf отвечает за запись данных, и работает точно по такому же принципу, как и if_readBuf. Параметры функции if_writeBuf те же самые, просто третий параметр указывает на буфер размером 512 байт, откуда должны браться данные для записи на диск. [Ссылки] 1. Описание LBA на Википедии. |