Zephyr×Rustのインテグレーションにチャレンジ!⑥~RustでDriverを書く!~
はじめに
ZephyrとRustのインテグレーションに挑戦しています。
これまでで、アプリケーションをRustで書いてきました。 ここからは、Driverを書く方法を調査していきます。
前回までのあらすじ
RustでZephyrのDriverを書くためには、INIT_DEVICEマクロ相当のことをすればよいことがわかりました。
具体的には、.devconfig.initと.init_<priority level>に、device_configとdeviceオブジェクトを配置します。
bindgenで自動されたbindingは次の通りでした。これをグローバル領域に置いていきます。
#[repr(C)] #[derive(Debug, Copy, Clone)] pub struct device_config { pub name: *const cty::c_char, pub init: ::core::option::Option<unsafe extern "C" fn(device: *mut device) -> cty::c_int>, pub config_info: *const cty::c_void, } #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct device { pub config: *mut device_config, pub driver_api: *const cty::c_void, pub driver_data: *mut cty::c_void, }
Syncの洗礼
とりあえず、雑に試してみます。
#[link_section = ".init_POST_KERNEL40"] static __DEVICE_MY_DEVICE: zephyr::device = zephyr::device { config: core::ptr::null_mut(), driver_api: core::ptr::null_mut(), driver_data: core::ptr::null_mut() };
ビルドしてみます。
error[E0277]: `*mut bindings::device_config` cannot be shared between threads safely
--> src/lib.rs:31:1
|
31 | / static __DEVICE_MY_DEVICE: zephyr::device = zephyr::device {
32 | | config: core::ptr::null_mut(),
33 | | driver_api: core::ptr::null_mut(),
34 | | driver_data: core::ptr::null_mut()
35 | | };
| |__^ `*mut bindings::device_config` cannot be shared between threads safely
|
= help: within `bindings::device`, the trait `core::marker::Sync` is not implemented for `*mut bindings::device_config`
= note: required because it appears within the type `bindings::device`
= note: shared static variables must have a type that implements `Sync`
あー、生ポインタ型なので、Syncトレイトが実装されていない、と…。 どちらにしても、bindgenから自動生成した構造体を使うのは無理そうですね。
真面目にやるのであれば、生ポインタを包むNewtype型を作って、Syncトレイトを実装する方法があります。
pub struct MY_PTR(*mut bindings::device_config); unsafe impl Sync for MY_PTR {}
こうなると、自分で構造体を定義する必要があるので、一回動くまでは、楽をします。
device_configおよびdeviceと等価になる構造体を用意します。
#[repr(C)] #[derive(Debug, Copy, Clone)] pub struct DeviceConfig { pub name: usize, // 初期化関数の関数ポインタ pub init: unsafe extern "C" fn(device: *mut Device) -> cty::c_int, pub config_info: usize, } #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct Device { pub config: &'static DeviceConfig, pub driver_api: usize, pub driver_data: usize, } unsafe impl Sync for DeviceConfig {} unsafe impl Sync for Device {}
これでバイナリ上の辻褄は合うはずです。
実装
かなり力技で、褒められた実装ではないですが、次のように実装します。
unsafe extern "C" fn my_init(_device: *mut Device) -> cty::c_int { println!("Hello from My Driver!\n"); 0 } #[link_section = ".devconfig.init"] static __CONFIG_MY_DEVICE: DeviceConfig = DeviceConfig { name: 0, init: my_init, config_info: 0 }; #[link_section = ".init_POST_KERNEL40"] static __DEVICE_MY_DEVICE: Device = Device { config: &__CONFIG_MY_DEVICE, driver_api: 0, driver_data: 0 };
結果
$ ninja run Recompacting log... [1/1] To exit from QEMU enter: 'CTRL+a, x'[QEMU] CPU: cortex-m3 qemu-system-arm: warning: nic stellaris_enet.0 has no peer Hello from My Driver! ***** Booting Zephyr OS v1.13.99-ncs2-10-gdad14f4 ***** Hello from Rust!
Rustで書いたDriverの初期化関数から、Hello from My Driver!が出力されました!