Інтеграція FATFS бібліотеки для організації читання дискового пристрою на iOS

Інтеграція FATFS бібліотеки для організації читання дискового пристрою на iOS

Введення

Стаття присвячена впровадженню open source бібліотеки на iOS для читання/запису даних з MFI дискового пристрою на основі FAT12/FAT16/FAT32/Exfat. Представлено спосіб побудови архітектури програми, на основі FATFS бібліотеки, а також методи налагодження та тестування дротових MFI-пристроїв. Стаття практично не містить коду через дотримання NDA.

Показ завдання

Головною метою було створення якогось універсального SDK (.framework), здатного працювати з виробниками різних пристроїв дискового доступу за протоколом MFI, що використовують єдиний стандарт за API.

На малюнку представлена узагальнена структурна схема такого фреймворку. Залежно від протоколу пристрою вибирається та чи інша бібліотека взаємодії з пристроєм. Залежно від того, яка файлова система (FS) використовується на диску, вибирається відповідний алгоритм роботи з диском: FAT/exFat/Other FS. При цьому до пристрою можуть мати доступ кілька програм (які використовують цей SDK), що знаходяться в пам'яті пристрою, проте запис/читання одноразово може виконувати тільки один. Цей SDK передбачає використання його в багатопоточному додатку c конкурентними завданнями читання і запису.

Пошук рішення

Спочатку в якості базової бібліотеки для реалізації FS FAT32 була обрана реалізація FAT32, створена Apple. Однак, складність інтеграції стандартного API для пристрою доступу внаслідок прив'язки до конкретної платформи змусила піти в напрямку пошуку готового рішення з роздільним абстрактним API для фізичного рівня доступу c MFI пристроєм через бібліотеку виробника. Були розглянуті також EFSL і FATFS open source реалізації FAT32. FATFS була обрана внаслідок декількох причин:

  • поточна підтримка бібліотеки
  • незалежність платформи і простота портування
  • широкий спектр параметрів налаштування
  • підтримка exFAT
  • абстрактний рівень доступу для драйверів

Внаслідок проблем отримання логів роботи програми, враховуючи, що фізично роз'єм був зайнятий і не було можливості підключення до XCode, була підключена бібліотека NSLogger, що дозволяє передавати логи по Wi-Fi майже в real time режимі. Тим не менш, проблема налагодження такої бібліотеки мала місце бути. Було вирішено розробити симулятор MFI пристрою, який звертався до якоїсь області на диску iOS/MAC пристрою і емулював читання/запис по тому ж API, що і реальний пристрій.

Проблеми інтеграції

Портування на Obj-C

FATFS написаний на чистому С, а інтегрувати доводилося в бібліотеку написаної на ObjC. Незважаючи на те, що ObjC безпосередньо підтримує інтеграцію C функцій, виникали проблеми з сигнатурами функцій і з використанням деяких типів змінних.

Підтримка Unicode

Опція Unicode адекватно працювала тільки з параметром кодування Obj-C NSUTF16LittleEndianStringEncoding, незважаючи на те, що згідно із заявленими вимогами підтримувалося додатково UTF-16BE і UTF-8. Всі методи, що працюють з іменами файлів необхідно було зробити потокобезпекою для коректної роботи FAT32.

Thread safe

Опція потокобезпеки, що активується за параметром FS_REENTRANT і реалізується через функції ff_req_grant, ff_rel_grant, ff_del_syncobj зажадала додаткової імплементації через POSIX. Без включеної параметри потокобезпеки спостерігалося пошкодження таблиці файлової системи і як наслідок - втрата даних. Файловий дескриптор pthread_t не відразу вдалося коректно імплементувати внаслідок того, що функції ff _ для синхронізації потоків передавали дані за значенням, а не за посиланням. Після заміни на посилальні значення - проблем з потокобезпекою виконання операцій не виникало.

Кешування

Щоб уникнути втрати даних під час копіювання та пошкодження таблиці файлів, було застосовано синхронізацію кешу даних записуваного файлу за допомогою функції f_sync. Враховуючи, що розмір дискового простору міг бути більше 16 Гб, деякі базові типи змінних довелося поміняти на «long log». FATFS спочатку проектувалася з розрахунком на вбудовувані пристрої, що характеризуються обмеженим розміром пам'яті і низькою продуктивністю. Як наслідок функції читання/запису виконувалися по одному кластеру дискретно в одиницю часу, що істотно нижче безперервного читання/запису. Подібна проблема виникла при читанні структури каталогів при наявності в директорії більше 100 файлів. Читання/запис одного файла лінійно зростав зі збільшенням кількості файлів, як показано на малюнку. По осі «х» відображено кількість файлів у теці, а по осі «y» - час читання в секундах. Типи кривих на графіку: V2 Sim Fat -симулятор дискового пристрою, V1 Fat файлова система FAT32, для якої відсутня ця проблема (не FATFS), V2 Fat64/128 FATFS c розміром диска 64 і 128 Гб, відповідно.

Структурна схема роботи кешу в такому SDK представлена на малюнку нижче. Таким чином кешувалася не тільки файлова структура, а й сектори для низькорівневих функцій читання/запису медіа пристрою, внаслідок відсутності апаратного кешу.

Унікальний доступ програми до пристрою

Програми з інтегрованим SDK не повинні одночасно мати доступ до зовнішнього дискового пристрою. Правила, яким має підкорятися програма з використовуваним SDK має виглядати як показано нижче на діаграмі станів.

Для встановлення правил доступу до пристрою був імплементований механізм заснований на Darwin Notification Center, що дозволяє за допомогою повідомлень дозволяти або блокувати роботу з пристроєм програми коли з пристроєм працює ще одна програма з даними SDK. Під час першого запуску програми надсилається запит до всіх програм, підписаних на отримання певних повідомлень. Кожен додаток публікує свій стан для інших програм, щоб новий додаток міг перейти в адекватний стан. Якщо пристрій вільний, програма переходить до стану USING. Стан PENDING є проміжним для BUSY і WAIT на той випадок, коли пристрій не доступний або зайнятий. У разі невдачі в комунікаціях використовується стан INVALID. Після завершення використання пристрою програма переходить до стану INACTIVE.

Тестування Фреймворка

Написання Unit Tests для всіх функцій бібліотеки було критично необхідне внаслідок жорстких детермінованих вимог до поведінки API для кінцевих користувачів. Спочатку всі тести виконувалися на симуляторі дискового пристрою. Однак симулятор і бібліотеку для низькорівневого доступу диска писали різні люди, які не мають можливості взаємодії, у зв'язку з чим поведінка симулятора не завжди збігалася з поведінкою драйвера пристрою. Для цього була розроблена схема тестування на реальному пристрої, представлена нижче. Тестований framework інтегрувався в додаток, написаний з використанням Private API, а установка проводилася через Xcode Server Over-the-Air installation. Private API дозволяв виводити програму з Background стану програми з Push Notification і запускати необхідні тести. Результати тестів відправлялися за REST протоколом на сервер статистики, де згодом вони представлялися у вигляді графіків і таблиць. У бібліотеку також включені Activity Tracing функції для отримання більш докладного crash логу.

Ув'язнення

Незважаючи на серйозні недоліки FATFS opensourse бібліотеки, які потребують доопрацювання, іншої альтернативи з можливістю використання Fat32 і ExFat, а також з абстрактним рівнем підтримки media drivers - знайти не вдалося. При коректній модифікації бібліотеки з підключеними додатковими функціями кешування і буферного читання/запису, функції копіювання і читання файлів FAT32 API нижче швидкості копіювання LBA блоків медіа драйвера виробника заліза всього на 10-15%. Для новачків в області розробки та інтеграції файлових систем - FATFS може бути цілком гідним вибором.

Корисні посилання

→ FatFs — Generic FAT File System Module

→ Microsoft EFI FAT32 File System Specification

→ Darwin Notification Concepts

→ About Continuous Integration in Xcode

Image