4.4 Пример драйвера MTD для NOR Flash

Предыдущая  Содержание  Следующая V*D*V

Погрузимся в подробности драйвера памяти 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");

 

Предыдущая  Содержание  Следующая