Використання пам’яті введення

Попередня Зміст Наступна

Незважаючи на популярність портів вводу/виводу у світі x86, основним механізмом, який використовується для обміну даними з пристроями, є пов'язані з пам'яттю регістри та пам'ять пристрою. Обидва називаються пам'яттю вводу/виводу, оскільки різниця між регістрами та пам'яттю для програмного забезпечення не видно.

Спосіб доступу до пам'яті введення/виводу залежить від комп'ютерної архітектури, шини та пристрою, хоча принципи скрізь однакові. Обговорення в цьому розділі стосується переважно пам'яті ISA та PCI, а також намагається передати загальну інформацію. Хоча доступ до пам'яті PCI тут, ретельне обговорення PCI відкладається до Глави 12.

Тому, хоча розіменування покажчика працює (зараз) на платформах x86, невдале використання належних макросів перешкоджає переносимості та читання драйвера.

Отримання пам'яті введення/виводу та відображення

Області пам'яті вводу/виводу мають бути виділені до початку використання. Інтерфейсом для отримання областей пам'яті (визначеним в) є:

struct resource *request_mem_region(unsigned long start, unsigned long len, char *name);

Ця функція виділяє область пам'яті len байт, починаючи з start. Якщо все йде добре, повертається не покажчик NULL; в іншому випадку повертається значення NULL. Вся виділена для введення/виведення пам'ять перерахована в /proc/iomem.

Коли більше не потрібні області пам'яті повинні звільнятися:

void release_mem_region(unsigned long start, unsigned long len);

Існує також стара функція перевірки на наявність області пам'яті вводу/вывода:

int check_mem_region(unsigned long start, unsigned long len);

Але,як і check_region, ця функція є небезпечною і її слід уникати.

Функції викликаються відповідно до наступного визначення:

void *ioremap(unsigned long phys_addr, unsigned long size);

void *ioremap_nocache(unsigned long phys_addr, unsigned long size);

void iounmap(void * addr);

Доступ до пам'яті вводу/виводу

На деяких платформах ви можете здійснити використання ioremap, що повертається, як покажчика. Таке використання не переноситься і розробники ядра ведуть роботу з усунення будь-якого такого використання все більше і більше. Правильним способом доступу до пам'яті введення/виведення є набір функцій (визначених за допомогою ), передбачених для цієї мети.

Для читання з пам'яті введення/виводу скористайтеся однією з наступних:

unsigned int ioread8(void *addr);

unsigned int ioread16(void *addr);

unsigned int ioread32(void *addr);

Існує аналогічний набір функцій для запису пам'яті введення/виведення:

void iowrite8(u8 value, void *addr);

void iowrite16(u16 value, void *addr);

void iowrite32(u32 value, void *addr);

void ioread8_rep(void *addr, void *buf, unsigned long count);

void ioread16_rep(void *addr, void *buf, unsigned long count);

void ioread32_rep(void *addr, void *buf, unsigned long count);

void iowrite8_rep(void *addr, const void *buf, unsigned long count);

void iowrite16_rep(void *addr, const void *buf, unsigned long count);

void iowrite32_rep (void addr, const void buf, unsigned long count);

Функції ioread читають count значень із даного addr у заданий buf , а функції iowrite записують count значень із даного buf по заданому addr . Зверніть увагу, щоcount виражається у розмірі записуваних даних; ioread32_rep зчитує count 32-х розрядних значень, починаючи з buf.

Всі вищеописані функції виконують введення/виведення за заданим addr . Замість цього, якщо потрібно працювати з блоком пам'яті вводу/виводу, можна використовувати одну з таких:

void memset_io(void *addr, u8 value, unsigned int count);

void memcpy_fromio(void *dest, void *source, unsigned int count);

void memcpy_toio(void *dest, void *source, unsigned int count);

Ці функції поводяться подібно до їх аналогів бібліотеки Сі.

Якщо ви почитаєте вихідний код ядра, ви побачите при використанні пам'яті введення/виводу багато дзвінків старого набору функцій. Ці функції, як і раніше, працюють, але їх застосування в новому коді не рекомендується. Серед іншого, вони менш безпечними, оскільки вони не виконують деякі види перевірки типу. Тим не менш, ми представляємо їх тут:

Ці макроси використовуються для отримання 8-розрядного, 16-розрядного і 32-розрядного значень даних з пам'яті вводу/виводу.

void writeb(unsigned value, address);

void writew(unsigned value, address);

void writel(unsigned value, address);

Як і попередні функції, ці функції (макроси) використовуються для запису 8-розрядних, 16-розрядних і 32-розрядних елементів даних.

Деякі 64-розрядні платформи також пропонують readq і writeq для операцій пам'яті з 4 словом (8 байт) на шині PCI. Специфікація вченого слова історично залишилася з часу, коли всі реальні процесори мали 16-розрядні слова. Насправді, ім'я L, яке використовується для 32-х розрядних значень, також стало неправильним, але перейменування всього переплутало б навіть більше.

Порти як пам'ятьвведення/виводу

Деяке обладнання має цікаву особливість: деякі версії використовують порти вводу/виводу, тоді як інші використовують пам'ять вводу/виводу. Регістри, які експортуються в процесор, є в будь-якому випадку однаковими, але метод доступу відрізняється. Як спосіб зробити життя простіше для драйверів, що мають справу з таким обладнанням, і як спосіб мінімізації очевидних відмінностей між портом вводу/виводу і доступом до пам'яті, ядро ​​версії 2.6 надає функцію, названу ioport_map :

void *ioport_map(unsigned long port, unsigned int count);

Коли більше не потрібно, це відображення має бути скасовано:

void ioport_unmap (void * addr);

Ці функції роблять порти введення/виведення виглядають подібно до пам'яті. Зауважте, що порти вводу/виводу все ще мають бути виділені request_region , перш ніж вони можуть бути перепризначені цим способом.

Повторне використання short для пам'яті введення/введення

Наприклад, ось як ми використовували short для запалювання налагоджувальних світлодіодів на налагоджувальній платі MIPS:

mips.root# ./short_load use_mem=1 base=0xb7ffffc0

mips.root# echo -n 7 > /dev/short0

Використання short для пам'яті введення/виводу таке саме, як і для портів введення/виводу.

Зверніть увагу на використання бар'єру пам'яті. Оскільки iowrite8 , ймовірно, перетворюється на багатьох архітектур у пряме присвоєння, необхідний бар'єр пам'яті, щоб забезпечити запис в очікуваному порядку.

short використовує inb і outb, щоб показати, як це робиться. Однак простою вправою для читача було б змінити short для перепризначення портів вводу/виводу за допомогою ioport_map та значного спрощення решти коду.

ISA пам'ять нижче 1 Мб

Однією з самихвідомих областей пам'яті введення/виводу є область ISA, що є на персональних комп'ютерах. Це область пам'яті між 640 Кб (0xA0000) та 1 Мб (0x100000). Таким чином, вона знаходиться прямо в середині звичайної оперативної пам'яті системи. Це позиціонування може здатися трохи дивним; це артефакт рішення, прийнятого на початку 1980-х, коли 640 Кб пам'яті здавалося більшим, ніж будь-хто коли-небудь зможе використати.

Хоча ISA пам'ять введення/виводу існує тільки на комп'ютерах класу x86, ми думаємо, що вона коштує кілька слів і приклад драйвера на ній.

Щоб продемонструвати доступ до ISA пам'яті, ми використовуємо ще один невеликий дурний модуль (частина вихідних прикладів). По суті, він називається silly (дурним), як скорочення від Simple Tool for Unloading and Printing ISA Data (простого інструменту для розвантаження та друку даних ISA), або щось подібне.

#define ISA_BASE 0xA0000

#define ISA_MAX 0x100000 /* для доступу до звичайної пам'яті */

/* цей рядок з'являється в silly_init */

io_base = ioremap(ISA_BASE, ISA_MAX - ISA_BASE);

ioremap повертає покажчик, який може бути використаний з ioread8 та іншими функціями, поясненими в розділі "Доступ до пам'яті вводу/виводу".

Наступними двома пристроями є /dev/sillyw (молодший номер 1) та /dev/sillyl (молодший номер 2). Вони працюють подібно /dev/sillyb, тільки вони використовують 16-розрядні і 32-розрядні функції. Ось реалізація write для sillyl, знову частина перемикача:

iowrite32(*(u32 *)ptr, add);

Останнім пристроєм є /dev/sillycp (молодший номер 3), який для виконання тієї ж задачі використовує функції memcpy_*io. Ось суть його реалізації.

memcpy_fromio(ptr, add, count);

ОскількиДля забезпечення доступу до області пам'яті ISA було використано ioremap , silly повинен викликати iounmap при розвантаженні модуля:

isa_readb та друзі

Погляд на вихідні коди ядра підніме ще один набір процедур із такими іменами, як isa_readb . Насправді кожна з щойно описаних функцій має isa_ еквівалент. Ці функції забезпечують доступ до пам'яті ISA без необхідності окремого кроку ioremap. Однак, розробники ядра говорять, що ці функції призначені бути тимчасовими допоміжними засобами портування драйверів і що вони можуть піти у майбутньому. Таким чином, необхідно уникати їх використання.