Перейти до основного вмісту

Додавання типу пристрою

Каталог периферійних пристроїв Sentinel містить апаратні факти. Новий тип пристрою (наприклад, дисплей або послідовний адаптер) потребує типізованого запису, провайдера, який його виявляє, тестів і сторінки в цій теці. Чи підтримує застосунок пристрій, вирішує сам застосунок, а не Sentinel.

1. Визначте запис​

Додайте модуль у src/peripherals/ зі структурою serde для пристрою і додайте її як варіант Peripheral у src/peripherals/mod.rs. Ім’я варіанта стає тегом type у JSON, тож Display(display::Display) серіалізується як {"type": "display", ...}. Розширте Peripheral::id, kind і describe для нового варіанта.

  • id має бути стабільним між повторними підключеннями та перезавантаженнями: виводьте його зі шляху шини, серійного номера або іншого атрибута, що їх переживає, і ніколи — з індексу переліку, як-от номера карти чи /dev/videoN.
  • id також має бути унікальним у межах усього каталогу; робочий потік (worker) цього не перевіряє. Додавайте до нього префікс із типу та провайдера, як це роблять наявні id (camera:v4l2:…, microphone:alsa:…), а решту будуйте з того, що не може бути спільним для двох однакових пристроїв, як-от порт, до якого їх підключено. Самого серійного номера недостатньо: дешеві пристрої часто мають однакові номери.
  • Необов’язкові факти — це поля Option, позначені #[serde(default, skip_serializing_if = "Option::is_none")], як у наявних записах, тож невідомий факт не потрапляє до JSON. Звичайне поле Option серіалізувалося б як null.
  • Повідомляйте факти, які надає ядро або пристрій. Не додавайте полів, що кодують політику, як-от те, чи підтримується режим.

2. Напишіть провайдер​

Реалізуйте трейт Provider:

pub trait Provider: Send {
fn name(&self) -> &'static str; // e.g. "display.drm"
fn subsystems(&self) -> &'static [&'static str]; // uevent subsystems, e.g. ["drm"]
fn discover(&mut self) -> Result<Vec<Peripheral>, ProviderError>;
}

discover виконує одне повне сканування в потоці peripherals. Робочий потік викликає його під час запуску, після uevent від однієї з підсистем subsystems і за запитом на оновлення.

  • Лише читання. Відкривайте вузли пристроїв лише для читання й використовуйте тільки інтерфейси запитів. Ніколи не захоплюйте пристрій у володіння, не налаштовуйте його й не запускайте з нього потік.
  • Обмеженість. Обмежуйте кожен список, який читаєте, і кожен цикл запитів, щоб несправний пристрій не міг загальмувати сканування. Допоміжні функції V4L2 у videodev2.rs демонструють цей підхід.
  • Помилки. Повертайте ProviderError з кодом на кшталт io.permission_denied або io.open (див. sysutil.rs), коли сканування не може завершитися. Тоді каталог зберігає останні справні пристрої цього провайдера й показує помилку в errors. Пристрій, що зникає під час сканування, пропускається, і це не помилка; uevent, який надсилається під час його від’єднання, запускає повторне сканування.
  • Плавна деградація замість збою. Якщо необов’язкового атрибута немає, його поле пропускається. Атрибут, який існує, але не читається, обробляйте відповідно до його призначення:
    • атрибут, потрібний скануванню для пошуку чи ідентифікації пристрою (його id, його ідентичність USB), є помилкою, тож останні справні пристрої провайдера зберігаються;
    • атрибут, який лише додає факт, не заважає опублікувати пристрій без цього факту, із зазначенням причини в записі (так робить поле issues запису мікрофона для нечитабельних метаданих захоплення).

Зареєструйте провайдер у builtin_providers() у src/peripherals/mod.rs.

3. Протестуйте його​

Тести читають дерево фікстур замість живої системи: передайте провайдеру його кореневі каталоги sysfs, procfs і /dev як параметри та підробіть шар ioctl, як це роблять alsa/tests.rs і v4l2/tests.rs. Охопіть кожну відмінність між пристроями класу, а не лише пристрій, який у вас є: кількості, формати, діапазони проти дискретних значень, відсутні необов’язкові поля, кілька однакових пристроїв одночасно та пристрій, що зникає посеред сканування. Перевірте, що два однакові пристрої отримують різні id. Позначайте фікстури, скопійовані з реального обладнання, як реальні знімки.

cargo test --locked має проходити, а cargo fmt --check і cargo clippy --locked --all-targets не повинні повідомляти нічого нового.

4. Задокументуйте його​

Додайте в цю теку сторінку, яка містить ім’я провайдера, підсистеми uevent, спосіб виведення id, кожне поле з умовою його наявності, коди помилок і приклад JSON. Додайте посилання на неї з довідника API та покажчика документації, а потім перегенеруйте переклади (див. розділ Локалізація).