4.4 Пример драйвера MTD для NOR Flash |
Предыдущая Содержание Следующая |
Погрузимся в подробности драйвера памяти NOR флеш для Linux. Файл mtd.c содержит код для простой NOR Flash, основанный на следующих предположениях:
▪Устройство флеш-памяти имеет один регион стирания, так что все секторы имеют одинаковый размер. (Регион стирания определяется как площадь микросхемы, которая содержит секторы одного и того же размера.) ▪Обращение к микросхеме флеш-памяти происходит с помощью 4-х байтовой шины. ▪Функциональность блокировки, разблокировки, приостановления и возобновления работы не поддерживается.
Для простоты будем считать, что у нас в виде макросов или функций имеется следующая информация:
▪DUMMY_FLASH_ERASE_SIZE: размер сектора стирания флеш-памяти ▪DUMMY_FLASH_SIZE: размер флеш-памяти ▪PROBE_FLASH(): функция, которая проверяет, присутствует ли NOR Flash по указанному адресу ▪WRITE_FLASH_ONE_WORD: функция/макрос для записи слова по указанному адресу ▪ERASE_FLASH_SECTOR: функция для стирания заданного сектора ▪DUMMY_FLASH_ERASE_TIME: время стирания одного сектора в тиках (jiffies)
Сначала составим список всех заголовочных файлов, которые потребуются для нашего драйвера флеш-памяти.
/* mtd.c */ #include <linux/kernel.h> #include <linux/module.h> #include <linux/types.h> #include <linux/sched.h> #include <linux/errno.h> #include <linux/interrupt.h> #include <linux/mtd/map.h> #include <linux/mtd/mtd.h> #include <linux/mtd/cfi.h> #include <linux/delay.h>
Теперь определим все API/макросы, которые, как мы ожидаем, определит пользователь.
#define DUMMY_FLASH_ERASE_SIZE #define PROBE_FLASH(map) #define WRITE_FLASH_ONE_WORD(map, start, addr, data) #define ERASE_FLASH_SECTOR(map, start, addr) #define DUMMY_FLASH_ERASE_TIME #define DUMMY_FLASH_SIZE
Краткое описание аргументов, которые передаются вышеописанному API:
▪map: это указатель на структуру map_info, объявленную в заголовочном файле include/linux/mtd/map.h. Эта структура более подробно объясняется в Разделе 4.5. ▪start: это начальный адрес микросхемы NOR флеш. Этот адрес обычно используется для программирования флеш-памяти с помощью команды стирания или записи данных. ▪addr: это смещение от начального адреса микросхемы, куда должны быть записаны данные или где должен быть стёрт сектор. ▪data: этот аргумент для API записи представляет собой 32-х разрядное слово, которое определяет, что должно быть записано по указанному адресу.
Далее мы определяем структуру, которая содержит информацию, относящуюся именно к этой флеш-памяти.
struct dummy_private_info_struct { int number_of_chips; /* Количество микросхем флеш-памяти */ int chipshift; /* Размер каждой флеш-памяти */ struct flchip *chips; } ;
Краткое описание каждого из полей этой структуры:
▪number_of_chips: как следует из названия, оно указывает, сколько всего микросхем можно найти по адресу probe. Это число коду драйвера должен вернуть API PROBE_FLASH(). ▪chipshift: это общее количество разрядов адреса для устройства, которое используется для вычисления адреса смещения, и общего числа байтов, которое вмещает устройство. ▪chips: struct flchip можно найти в include/linux/mtd/flashchip.h. Дополнительное объяснение находится в функции dummy_probe().
Далее идёт список статических функций, которые должны быть объявлены.
static struct mtd_info * dummy_probe(struct map_info *); static void dummy_destroy(struct mtd_info *); static int dummy_flash_read(struct mtd_info *, loff_t , size_t , size_t *, u_char *); static int dummy_flash_erase(struct mtd_info *, struct erase_info *); static int dummy_flash_write(struct mtd_info *, loff_t , size_t , size_t *, const u_char *); static void dummy_flash_sync(struct mtd_info *);
Структура mtd_chip_driver используется процедурой инициализации dummy_flash_init() и функцией выхода dummy_flash_exit(). Наиболее важным полем является .probe, которое вызывается, чтобы определить, присутствует ли на плате по указанному адресу флеш-память указанного типа. Уровень MTD хранит список таких структур. Обращение к процедурам probe происходит, когда драйвер связи с флеш-памятью вызывает процедуру do_map_probe().
static struct mtd_chip_driver dummy_chipdrv = { .probe = dummy_probe, .destroy = dummy_destroy, .name = "dummy_probe", .module = THIS_MODULE };
Теперь определим процедуру probe. Эта функция проверяет, может ли флеш-память быть найдена по адресу map->virt, который заполняется драйвером связи с флеш-памятью. Если он в состоянии обнаружить флеш-память, то он выделяет память для структуры mtd и структуры dummy_private_info. Структура mtd заполнена различными процедурами драйвера, такими как read, write, erase и так далее. Структура dummy_private_info заполнена информации о данной флеш-памяти. Реализацию процедуры probe можно посмотреть в Распечатке 4.1.
Наиболее интересными структурами данных, которые инициализируются в функции probe, являются очередь ожидания и мьютекс. Они используются для предотвращения одновременного доступа к флеш-памяти, что является необходимым условием почти для всех устройств флеш-памяти. Таким образом, когда должны быть выполнены такие операции, как чтение или запись, драйвер должен проверить, не используется ли флеш-память. Это выполняется с помощью поля state, которое установлено во время инициализации в FL_READY. Если флеш-память используется, то процесс должен заблокироваться на очереди ожидания и ждать пробуждения. Мьютекс (спин-блокировка) используется для предотвращения проблем с состояниями гонок на машинах с SMP (симметричной многопроцессорной обработкой) или в случае, если разрешено вытеснение.
Перейдём к процедуре read. Процедурой чтения, зарегистрированной в ядре MTD, является dummy_flash_read(), которая вызывается, чтобы прочитать len байтов из флеш-памяти со смещения from. Поскольку процедуры записи могут охватывать несколько микросхем, для чтения данных из одной микросхемы внутри вызывается функция dummy_flash_read_one_chip(). Реализацию можно посмотреть в Распечатке 4.2.
Теперь перейдём к процедуре записи. Процедурой, зарегистрированной в ядре MTD, является dummy_flash_write(). Поскольку запись может начинаться с невыровненного адреса, данная функция гарантирует, что она забуферирует данные в таких случаях и, в свою очередь, вызовет функцию dummy_flash_write_oneword() для записи 32-х разрядных данных по выровненным 32-х разрядным адресам. Реализацию можно посмотреть в Распечатке 4.3.
Функцией стирания, зарегистрированной в ядре MTD, является dummy_flash_erase(). Эта функция должна убедиться, что указанный адрес стирания является выровненным по сектору и количество байт, которое будет стираться, кратно размеру сектора. Внутри вызывается функция dummy_flash_erase_one_block(); она стирает один сектор по указанному адресу. Поскольку стирание сектора занимает много времени, эта функция вытесняет вызывающую задачу, отправляя её в сон на DUMMY_FLASH_ERASE_TIME тиков. По окончании стирания ядро MTD сигнализирует, что стирание завершено, устанавливая состояние стирания в MTD_ERASE_DONE, а затем перед возвращением выполняются все зарегистрированные обратные вызовы. Реализацию можно посмотреть в Распечатке 4.4.
Когда устройство флеш-памяти закрывается, вызывается функция sync. Эта функция должна убедиться, что ни одна из микросхем флеш-памяти не используется на момент закрытия; если же они используются, функция заставляет вызывающий процесс ждать, пока все микросхемы не перейдут в неиспользуемое состояние. Реализацию имитации функции sync можно посмотреть в Распечатке 4.5.
В случае, если драйвер флеш-памяти загружен как модуль, вызывается функция dummy_destroy. Функции dummy_destroy() выполняет все очистку при выгрузке модуля.
static void dummy_destroy(struct mtd_info *mtd) { struct dummy_private_info_struct *priv = ((struct map_info *)mtd->priv)->fldrv_priv; kfree(priv->chips); }
Следующими являются функции инициализации и выхода.
int __init dummy_flash_init(void) { register_mtd_chip_driver(&dummy_chipdrv); return 0; } void __exit dummy_flash_exit(void) { unregister_mtd_chip_driver(&dummy_chipdrv); }
module_init(dummy_flash_init); module_exit(dummy_flash_exit);
MODULE_LICENSE("GPL"); MODULE_AUTHOR("Embedded Linux book"); MODULE_DESCRIPTION("Sample MTD driver");
|
Предыдущая Содержание Следующая |