Zephyr×Rustのインテグレーションにチャレンジ!⑫~Driver APIリファクタリングの旅1~
はじめに
ZephyrとRustのインテグレーションに挑戦しています。
Rustで最低限動作するZephyrのUART Driverを作りました。 力技で無理矢理実装したところも多いです。負債を返済するときが来ました。
デバイス登録
まずは、デバイスおよびデバイスドライバをZephyr kernelに認識させるために、次のように実装していました。
#[link_section = ".init_POST_KERNEL40"] static __DEVICE_MY_DEVICE: Device = Device { config: &__CONFIG_MY_DEVICE, driver_api: &UART_API, driver_data: 0 }; #[link_section = ".devconfig.init"] static __CONFIG_MY_DEVICE: DeviceConfig = DeviceConfig { name: zephyr::CONFIG_UART_CONSOLE_ON_DEV_NAME, init: my_init, config_info: 0 };
特定のセクションに、Device / DeviceConfig構造体のオブジェクトを配置しています。
とりあえずで解決したい問題は2つです。
- 使い方が面倒
- 型がUART Driver固定
使い方が面倒
Device
/ DeviceConfig
構造体を知っていなければならないのと、それ以上にリンカセクションを意識しなければならないのがダメダメです。
マクロで簡素化
雑にマクロを書いて、もう少し簡単に書けるようにします。 次のように、必要な情報をマクロの引数に渡すと、上述したものと同等になるようにします。
device_config!( __CONFIG_MY_DEVICE, zephyr::CONFIG_UART_CONSOLE_ON_DEV_NAME, my_init, 0 ); device_init!(__DEVICE_MY_DEVICE, &__CONFIG_MY_DEVICE, &UART_API, 0);
少しAPIを簡単にするだけなので、マクロもそれほど複雑になりません。
与えられた引数から、Device
/ DeviceConfig
構造体の静的なオブジェクトを作成し、特定のリンカセクションを指定します。
#[macro_export] macro_rules! device_config { ($dev_name:ident, $name:expr, $init:expr, $info:expr) => { #[link_section = ".devconfig.init"] static $dev_name: DeviceConfig = DeviceConfig { name: $name, init: $init, config_info: $info }; } } #[macro_export] macro_rules! device_init { ($dev_name:ident, $config:expr, $api:expr, $data:expr) => { #[link_section = ".init_POST_KERNEL40"] static $dev_name: Device = Device { config: $config, driver_api: $api, driver_data: $data }; } }
現状、device_init!
の方は、.init_POST_KERNEL40
と固定になっています。
おそらく、これ以上は手続きマクロが必要なので、これから勉強します。
いずれにせよ、これで少し使うのが楽になりました。
型がUART Driver固定
Device
構造体のdriver_api
の型がuart_driver_api
固定になっています。これではUARTでしか使えません。
#[repr(C)] #[derive(Debug, Copy, Clone)] pub struct Device { pub config: &'static DeviceConfig, pub driver_api: &'static zephyr::uart_driver_api, pub driver_data: usize, }
ジェネリクスで汎化!
driver_api
の型をジェネリックにします。
pub struct Device<T: 'static + DriverApi> { pub config: &'static DeviceConfig<T>, pub driver_api: &'static T, pub driver_data: usize, }
単純にジェネリックにするだけでは、どんな型も指定できてしまうため、DriverApi
traitの実装を制約にします。
DriverApi
traitは、ただのマーカーです。何か必要になったら追加しましょう。
pub trait DriverApi {} impl DriverApi for bindings::uart_driver_api {}
これで、少しはマシになりました。 が、まだまだ負債はたくさんあるので、少しずつ解消していきます。