В статье приведён обзор общего подхода к разработке драйверов данного типа
В состав ЗОСРВ «Нейтрино» входят несколько готовых драйверов файловых систем флеш-памяти, которые предназначены для конкретных встраиваемых систем. Эти драйверы находятся в каталоге ${KPDA_TARGET}/${CPU}/sbin
. Драйверы файловых систем флеш-памяти имеют схему именования devf-*.
Если в ЗОСРВ «Нейтрино» отсутствует драйвер для конкретной целевой встраиваемой системы, следует сначала опробовать «общий» драйвер ( devf-generic), который достаточно часто (но не всегда) штатно работает со стандартной флеш-памятью. Для работы этому драйверу необходима поддержка подсистемы MTD (Memory Technology Driver, драйвер запоминающего устройства) и линейной адресации памяти.
Если ни один из наших драйверов не подходит для вашего устройства, вам необходимо разработать драйвер самостоятельно. В составе BSP обычно предоставляется исходный код, достаточный для разработки производного драйвера файловой системы флеш-памяти. Отправной точкой является каталог рабочий_каталог_bsp/src/hardware/flash/boards
.
Помимо каталога boards
, информация о платах/драйверах, которые поддерживаются на текущий момент, содержится в следующих источниках:
Обратите внимание, что в настоящее время мы поддерживаем только модификацию драйверов для систем со встроенной флеш-памятью (которая также называется резидентным массивом флеш-памяти — Resident Flash Array, RFA). Если вам требуется поддержка съемных накопителей (например, устройств PCMCIA, компактных или миниатюрных карт памяти), следует обратиться в адрес нашей технической поддержки.
Каждый драйвер файловой системы флеш-памяти состоит из следующих компонентов:
При адаптации драйвера файловой системы флеш-памяти вносятся изменения в его функцию main() и реализуются службы сокетов. Остальные компоненты реализованы в виде библиотек, которые компонуются с драйвером.
Как и все менеджеры устройств ЗОСРВ «Нейтрино», файловая система флеш-памяти использует стандартный интерфейс resmgr*() / iofunc*() и принимает стандартный набор сообщений для менеджеров ресурсов. Файловая система реагирует на эти сообщения выполнением операций чтения, записи и удаления данных из флеш-памяти.
Например, сообщение open() активизирует код, который считывает структуры данных файловой системы флеш-накопителя и обнаруживает требуемый файл. Последующее сообщение write() приводит к изменению содержимого файла на флеш-накопителе. Специальные функции, такие как стирание данных флеш-устройства, реализованы с помощью сообщений devctl().
Файловая система флеш-памяти является основным компонентом драйвера. Она содержит весь код, который обрабатывает запросы к файловой системе флеш-устройства и управляет ей. Для доступа к флеш-устройству файловая система использует службы сокетов и флеш-памяти.
Код файловой системы флеш-памяти не зависит от платформы и находится в библиотеке libfs-flash3.a
.
Службы сокетов выполняют системно-зависимую инициализацию флеш-устройств при запуске и обеспечивают их адресуемость (в основном это касается флеш-интерфейсов с окнами).
Перед выполнением операций чтения/записи над флеш-устройством другие компоненты проверяют доступность требуемого диапазона адресов с помощью служб сокетов. Если флеш-устройство отображено в линейное адресное пространство процессора, можно обеспечить его адресуемость; в системах, где флеш-память реализована в виде устройства с переключением банков или скрыта за другим интерфейсом (например, PCMCIA), эта задача несколько усложняется.
Службы сокетов являются самым трудоемким компонентом с точки зрения адаптации к встраиваемой системе.
Службы флеш-памяти содержат код, который записывает и стирает данные на конкретном флеш-устройстве. Этот компонент также называется драйвером запоминающего устройства (Memory Technology Driver, MTD).
Каталог ${KPDA_TARGET}/${CPU}/lib
содержит библиотеку MTD с именем libmtd-flash.a
, которая работает с поддерживаемыми флеш-устройствами.
![]() | Исходный код библиотеки libmtd-flash.a обычно размещен в каталоге рабочий_каталог_bsp/src/hardware/flash/mtd-flash . |
Функция сканирования определяет размер массива флеш-памяти с помощью специального алгоритма. Поскольку ее исходный код открыт, разработчик может беспрепятственно обнаруживать ошибки, связанные с некорректным определением объема флеш-накопителей.
Перед разработкой собственного драйвера файловой системы флеш-памяти следует изучить исходный код примеров драйверов. Скорее всего, один из существующих драйверов можно легко адаптировать для конкретной встраиваемой системы. В противном случае можно взять за основу исходный код менеджера devf-ram.
Исходный код файловых систем флеш-памяти имеет следующую структуру:
рабочий_каталог_bsp/src/hardware/ |--> ipl/ |--> startup/ `--> flash/ |--> boards/ | |--> generic/ | |--> ram/ | `--> ... | `--> mtd-flash/ |--> amd/ |--> fujitsu/ |--> hynix/ |--> intel/ |--> numonyx/ |--> rom/ |--> sharp/ |--> sram/ `--> ...
Следующие каталоги также относятся к файловым системам флеш-памяти:
${KPDA_TARGET}/usr/include/sys
f3s_mtd.h
. ${KPDA_TARGET}/usr/include/fs
f3s_api.h
, f3s_socket.h
и f3s_flash.h
. ${KPDA_TARGET}/${CPU}/lib
рабочий_каталог_bsp/src/hardware/flash/boards
рабочий_каталог_bsp/src/hardware/flash/mtd-flash
Перед внесением изменений в исходный код следует:
рабочий_каталог_bsp/src/hardware/flash/boards
Например, чтобы создать драйвер myboard на основе примера для платы 800FADS, выполните следующие команды:
cd рабочий_каталог_bsp/hardware/flash/boards mkdir myboard cp -cRv 800fads myboard cd myboard make clean
Команда cp с параметром -R (рекурсивное копирование) копирует все файлы, которые находятся в каталоге исходного кода, в том числе подкаталог процессора, для которого требуется создать драйвер. В вышеуказанном примере каталог 800fads содержит подкаталог ppc, где выполняется сборка драйвера myboard для процессора PowerPC.
При сборке нового драйвера файловой системы флеш-памяти не требуется вносить изменения в Makefile. Его рекурсивная структура обеспечивает корректную компоновку библиотек.
Сборка драйвера выполняется командой:
make F3S_VER=3 MTD_VER=2
Функция main() драйвера находится в файле main.c в подкаталогах примеров. С нее начинается адаптация драйвера для конкретной встраиваемой системы. Рассмотрим файл main.c
для платы 800FADS:
/** Файл: main.c для платы 800FADS*/#include <sys/f3s_mtd.h>#include "f3s_800fads.h"int main( int argc, char **argv ){int error;static f3s_service_t service[] = { { sizeof( f3s_service_t ),f3s_800fads_open,f3s_800fads_page,f3s_800fads_status,f3s_800fads_close },{ /* обязательная последняя запись */0, 0, 0, 0, 0 } };static f3s_flash_v2_t flash[] = { { sizeof( f3s_flash_v2_t ),f3s_a29f040_ident, /* общее обнаружение */f3s_a29f040_reset, /* общий сброс *//* v1 чтение/запись/стирание/приостановка/возобновление/синхронизация* (не используются) */NULL, NULL, NULL, NULL, NULL, NULL,NULL, /* v2 чтение(по умолчанию) */f3s_a29f040_v2write, /* v2 запись */f3s_a29f040_v2erase, /* v2 стирание */f3s_a29f040_v2suspend, /* v2 приостановка */f3s_a29f040_v2resume, /* v2 возобновление */f3s_a29f040_v2sync, /* v2 синхронизация *//* v2 проверка блокировки/блокировка/разблокировка/полная разблокировка* (не поддерживаются) */NULL, NULL, NULL, NULL },{ /* обязательная последняя запись */0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } };/* инициализация f3s */f3s_init( argc, argv, flash );/* запуск f3s драйвера */error = f3s_start( service, flash );return error;}
Массив service содержит одну или несколько структур f3s_service_t в зависимости от количества различных сокетов, которые должен поддерживать драйвер. Структура f3s_service_t определена в заголовочном файле <fs/f3s_socket.h>
и содержит указатели на функции служб сокетов.
Массив flash содержит одну или несколько структур f3s_flash_t в зависимости от количества типов флеш-устройств, которые должен поддерживать драйвер. Структура f3s_flash_t определена в заголовочном файле <fs/f3s_flash.h>
и содержит указатели на функции служб флеш-памяти.
Функции f3s_init() и f3s_start() определены в заголовочном файле <fs/f3s_api.h>
.
![]() | Не используйте заголовочные файлы <fs/f3s_socket.h> , <fs/f3s_flash.h> и <fs/f3s_api.h> непосредственно. Следует подключать в коде драйвера заголовочный файл <sys/f3s_mtd.h> для обеспечения прямой и обратной совместимости. |
В файле main.c
необходимо задавать:
Если во встраиваемой системе имеется ровно один сокет с одинаковыми устройствами флеш-памяти, оба массива должны состоять из единственного элемента.
Интерфейс служб сокетов определен в заголовочном файле <fs/f3s_socket.h>
и включает в себя следующие функции:
и callback-и структуры f3s_service_t:
Параметр socket в обработчиках используется для передачи аргументов службам сокетов, возврата результатов их работы и хранения информации о каждом сокете. Для взаимодействия со сложными интерфейсами, такими как PCMCIA, определена структура, в которой можно указывать несколько сокетов с несколькими окнами. Обычный массив линейной флеш-памяти имеет один сокет без окон.
В службах сокетов следует анализировать все применимые параметры перед инициализацией флеш-устройств с помощью функции f3s_*_open(). Для этого используются две функции:
С помощью этих параметров службы сокетов получают информацию обо флеш-устройствах системы, которая собрана программой запуска и размещена на системной странице.
Интерфейс служб флеш-памяти определен в заголовочном файле <fs/f3s_flash.h>
и включает в себя следующие функции:
Изучая перечень реализаций данных callback-функций, представленный в заголовочном файле sys/f3s_mtd.h
, в подавляющем числе случаев не требуется разрабатывать собственные, сосредоточившись на выборе наиболее подходящих реализаций из состава библиотеки libmtd-flash.a
(см. следующий параграф).
![]() | Значения параметра flags определены в заголовочном файле <fs/s3s_flash.h> . Самым важным является флаг F3S_VERIFY_WRITE — если он установлен, функция должна проверять успешность записи посредством контрольного чтения. Тем не менее, даже этот механизм иногда не обнаруживает ошибки при записи. |
Имеется несколько вариантов реализации основных служб флеш-памяти для различных устройств:
Существует две версии API MTD: f3s_flash_t и f3s_flash_v2_t. Причем, более новая версия интерфейса обратно совместима с исходной. Детали реализации можно узнать на страницах описания указанных типов. Для каждой callback-функции, присутствующей в дескрипторе службы флеш-памяти, существуют штатные библиотечные реализации, подходящие в подавляющем большинстве случаев. Они предоставляются потребителю в составе библиотеки libmtd-flash.a
.
В идеале должна существовать таблица соответствия библиотечных реализаций callout-функций и конкретных экземпляров оборудования/флеш-памяти. Так, например, чтобы воспользоваться функцией f3s_*_v2erase() на 16-разрядном устройстве Intel, следует вызвать функцию f3s_iCFI_v2erase(). Поскольку в общем случае эта задача не реалистичная, в данном параграфе приводятся общие рекомендации по выбору конкретных реализаций. Для этого требуется:
В стандартной конфигурации флеш-память должна располагаться по непрерывным адресам физической памяти, которые напрямую доступен драйверу. Чипы могут чередоваться по соображениям производительности/ширины шины.
Примеры нестандартных конфигураций:
SIGBUS
или SIGSEGV
) при чтении.
Полный исходный код библиотеки драйверов MTD может поставляться в составе BSP. В этом случае он доступен в каталоге рабочий_каталог_bsp/libs/src/hardware/flash/mtd-flash
. Callback-функции сгруппированы по производителям (например, intel/iCFI_write.c
). Каждый из этих файлов содержит комментарии, описывающие тип флеш-памяти, с которой они работают.
Поскольку исходный код организован по производителям, следующим шагом будет определение производителя. Ознакомьтесь с технической документацией на плату и описание используемой флеш-памяти. Компоненты флеш-памяти разных производителей обычно несовместимы друг с другом.
Определение callback-обработчика f3s_*_read()
В большинстве случаев достаточно установить для указателя в f3s_flash_t значение NULL
. Это заставляет библиотеку использовать memcpy() для чтения непосредственно из флеш-памяти.
Пример:
#include <sys/f3s_mtd.h>int32_t f3s_mtd_read( f3s_dbase_t *dbase, f3s_access_t *access, uint32_t flags,uint32_t offset, int32_t size, uint8_t *buffer ){uint8_t *memory = NULL;/* Установка корректной страницы сокета */memory = (uint8_t *)access->service->page( &access->socket, F3S_POWER_ALL, offset, NULL );if ( memory == NULL ) {fprintf( stderr, "%s: %d page() returns NULL\n", __func__, __LINE__ );return (-1);}/* (!!!) Код, который может потребоваться переопределить в нетипичных сценариях */memcpy( buffer, memory, size );return (size);}
![]() | В составе BSP файл <sys/f3s_mtd.h> находится в каталоге рабочий_каталог_bsp/src/hardware/flash/mtd-flash/public/sys/f3s_mtd.h . |
Определение callback-обработчика f3s_*_ident()
В настоящее время существует две основных реализации вызова f3s_*_ident(): для CFI (Common Flash Interface) и остальных. Большинство современных флеш-чипов поддерживают спецификацию CFI, а также общий метод идентификации флеш-чипа и его возможностей. Если ваш чип поддерживает спецификацию, следует использовать обработчик специфичный для CFI. Альтернативой является использование жестко закодированной таблицы распознаваемых флеш-идентификаторов. Использовать их следует лишь в крайнем случае.
Определение callback-обработчика f3s_*_write()
Память может быть записана либо словами (по 8 или 16 бит), либо буферами (несколько слов за раз). Все чипы поддерживают запись по одному слову, поэтому этот выбор всегда возможен, но не всегда оптимален. Более сложные чипы поддерживают буферизованную запись, которая значительно быстрее. К последним можно отнести Intel StrataFlash и AMD MirrorBit.
Просматривая документацию на память AMD, не путайте режим записи "Unlock Bypass" с буферизованной записью. Этот режим устраняет только дополнительное подтверждение после записи каждого слова. Реальные режимы буферизации собирают несколько слов во внутренний буфер и обслуживают их параллельно.
Определение callback-обработчика f3s_*_erase()
Обычно существует единственный вариант.
Определение callback-обработчика f3s_*_sync()
Для MTDv1 ( f3s_flash_t) обычно существует два типа обработчиков: для флеш-памяти загрузочного модуля и для обычной флеш-памяти. Первый тип имеет два разных размера для блоков стирания. Такому вызову необходимо знать размер блока, который он хочет стереть. Обычный обработчик не нуждается в дополнительной логике. Если вы не уверены, выберите обработчик для флеш-памяти загрузочного модуля — он несколько медленнее, но универсален для любого типа флеш-памяти.
Если вам нужно использовать данный callback-обработчик, обычно вариант выбора только один.
Определение callback-обработчиков f3s_*_suspend() и f3s_*_resume()
Флеш-память либо поддерживают данные вызовы, либо нет. Это обычно становится очевидным после изучения их документации.
Определение callback-обработчиков f3s_*_islock(), f3s_*_lock(), f3s_*_unlock() и f3s_*_unlockall()
Блокировки являются возможностью лишь нового API библиотеки MTD (MTDv2, см. f3s_flash_v2_t). Поддержка блокировок становится очевидной из документации на флеш-память. Для таких микросхем, поддерживающих защиту от записи на уровне блоков, существует две разные реализации: постоянная и энергозависимая.
В этом драйвере файловая система флеш-памяти хранится в основной памяти, а не на флеш-накопителе. По этой причине файловая система не является постоянной — при перезагрузке системы или удалении каталога /dev/shmem/fs0
все данные теряются. Этот драйвер используется преимущественно для тестирования.
В функции main() объявляется один массив services для функций служб сокетов и нулевая запись для функций служб флеш-памяти.
/** Файл: f3s_ram_main.c* Описание: файл содержит функцию main для файловой системы флеш-памяти f3s*/#include "f3s_ram.h"int main( int argc, char **argv ){int error;static f3s_service_t service[] = { { sizeof( f3s_service_t ),f3s_ram_open,f3s_ram_page,f3s_ram_status,f3s_ram_close },{ /* обязательная последняя запись */0, 0, 0, 0, 0 } };static f3s_flash_v2_t flash[] = { { sizeof( f3s_flash_v2_t ),f3s_sram_ident, /* общее обнаружение */f3s_sram_reset, /* общий сброс */NULL, /* v1 чтение (устаревшая функция) */NULL, /* v1 запись (устаревшая функция) */NULL, /* v1 стирание (устаревшая функция) */NULL, /* v1 приостановка (устаревшая функция) */NULL, /* v1 возобновление (устаревшая функция) */NULL, /* v1 синхронизация (устаревшая функция) */NULL, /* v2 чтение (по умолчанию) */f3s_sram_v2write, /* v2 запись */f3s_sram_v2erase, /* v2 стирание */NULL, /* v2 приостановка (не используется) */NULL, /* v2 возобновление (не используется) */f3s_sram_v2sync, /* v2 синхронизация */f3s_sram_v2islock, /* v2 проверка наличия блокировки */f3s_sram_v2lock, /* v2 блокировка */f3s_sram_v2unlock, /* v2 снятие блокировки */f3s_sram_v2unlockall /* v2 всеобщее снятие блокировки */ },{ /* обязательная последняя запись */0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } };/* инициализация f3s */f3s_init( argc, argv, (f3s_flash_t *)flash );/* запуск f3s драйвера */error = f3s_start( service, (f3s_flash_t *)flash );return (error);}
В функции open() служб сокетов присваивается имя драйверу, а затем обрабатываются переданные ему параметры. При отсутствии параметров задается размер по умолчанию и выделяется память для (виртуального) флеш-накопителя.
/** Файл: f3s_ram_open.c* Описание: файл содержит функцию открытия для библиотеки ram*/#include "f3s_ram.h"int32_t f3s_ram_open( f3s_socket_t *socket, uint32_t flags ){static void *memory;char name[8];int fd;int flag;/* выполнена ли инициализация */if ( !memory ){/* получение привилегий ввода/вывода */ThreadCtl( _NTO_TCTL_IO, NULL );/* присвоение имени сокету */socket->name = "RAM (имитация флеш-устройства)";/* заданы ли параметры сокета */if ( f3s_socket_option( socket ) )socket->window_size = 1024 * 1024;/* выбран ли размер массива */if ( !socket->array_size )socket->array_size = socket->window_size;/* задан ли размер массива */if ( !socket->array_size )return (ENXIO);/* присвоение имени общей памяти */sprintf( name, "/fs%X", socket->socket_index );/* открытие общей памяти */fd = shm_open( name, O_CREAT | O_RDWR, 0777 );if ( fd < 0 )return (errno);/* присвоение размера общей памяти */flag = ftruncate( fd, socket->array_size );if ( flag ){close( fd );return (errno);}/* отображение физического адреса в память */memory = mmap( NULL, socket->array_size, PROT_READ | PROT_WRITE,MAP_SHARED, fd, socket->address );if ( !memory ){close( fd );return (errno);}/* копирование дескриптора сокета */socket->socket_handle = (void *)fd;}/* присвоение ранее инициализированного значения указателю на память сокета */socket->memory = memory;return (EOK);}
В функции page() служб сокетов мы сначала проверяем, что заданное значение offset находится в пределах выделенной памяти, а затем при необходимости присваиваем значение size размеру окна. Эта функция возвращает адрес смещения по модулю размера окна.
/** Файл: f3s_ram_page.c* Описание: файл содержит функцию доступа к странице для библиотеки ram*/#include "f3s_ram.h"uint8_t * f3s_ram_page( f3s_socket_t *socket, uint32_t flags, uint32_t offset, int32_t *size ){/* находится ли смещение в пределах массива */if ( offset >= socket->window_size ){errno = ERANGE;return (NULL);}/* выбор подходящей страницы */socket->window_offset = offset & ~(socket->window_size - 1);/* задание корректного размера */*size = min( (offset & ~(socket->window_size - 1)) +socket->window_size - offset, *size );/* возврат указателя */return (socket->memory + offset);}
Функции status() и close() этого драйвера не выполняют каких-либо заслуживающих внимания действий.
Предыдущий раздел: Библиотеки разработки драйверов флеш-памяти