Zephyrのdriverを作ってみよう①~写経開始~
はじめに
Zephyrのdriverを作る方法を学びます。 まずは、qemu_cortex_m3をターゲットとした場合の、UART device driverを写経します。
|> zephyr/drivers/serial/uart_stellaris.c
全体で700行くらいなので、全てを単純に写経する、というよりポイントを押さえて行きたいと思います。
進め方
qemu_cortex_m3
のボード定義をコピーして、UARTだけ、自作driverに差し替えます。
テスト用アプリケーションプロジェクトの準備
まずは、テスト用のアプリケーションプロジェクトとして、hello_world
をベースとします。
| zephyr/samples/hello_world
レポジトリのboardファイルを直接編集したくないため、プロジェクト内にboardファイルを作成します。
# at ${ZEPHYR_BASE}/samples directory cp -r hello_world hello_from_my_uart cd hello_from_my_uart mkdir -p boards/arm cp -r ${ZEPHYR_BASE}/boards/arm/qemu_cortex_m3 boards/arm/
CMakeLists.txt
を編集して、プロジェクト下のボードファイルを見に行くようにします。
$ head -n 5 CMakeLists.txt cmake_minimum_required(VERSION 3.8.2) # Re-direct the directory where the 'boards' directory is found from # $ZEPHYR_BASE to this directory. set(BOARD_ROOT ${CMAKE_CURRENT_LIST_DIR}) set(BOARD qemu_cortex_m3)
defconfigの修正
qemu_cortex_m3をターゲットにすると、UARTのdriverは、stellarisというデバイスのdriverを使います。 これを、自作のmy_uart driverに変更していきます。
|> ${ZEPHYR_BASE}/samples/hello_from_my_uart/boards/arm/qemu_cortex_m3_defconfig
# CONFIG_UART_STELLARIS=y CONFIG_UART_MY_UART=y
これで、存在しない機能を有効化したため、ビルドが通らなくなるはず。一応試しておきます。
$ mkdir build && cd $_ $ cmake -GNinja -DBOARD=qemu_cortex_m3 .. -- Selected BOARD qemu_cortex_m3 ... ${ZEPHYR_BASE}/samples/hello_from_my_uart/boards/arm/qemu_cortex_m3/qemu_cortex_m3_defconfig:10: warning: attempt to assign the value "y" to the undefined symbol UART_MY_UART ...
期待通り、UART_MY_UART
がundefined symbolとなり、ビルドが通りません。
Kconfigの追加
ビルドが通るように、自作UARTのKconfigを追加します。まずは、シリアルデバイスのroot Kconfigです。
|> ${ZEPHYR_BASE}/drivers/serial/Kconfig
# Kconfig - serial driver configuration options ... source "drivers/serial/Kconfig.imx" source "drivers/serial/Kconfig.stellaris" source "drivers/serial/Kconfig.native_posix" ... # 追加 source "drivers/serial/Kconfig.my_uart"
ベースとするstellarisのKconfigをコピーします。
# at ${ZEPHYR_BASE}/drivers/serial/ cp Kconfig.stellaris Kconfig.my_uart
Kconfig.my_uartを編集します。STELLARIS
となっている部分を、MY_UART
に置換します。
- menuconfig UART_STELLARIS + menuconfig UART_MY_UART ...
これで、UART_MY_UART
のundefined symbolが解決されます。ビルドプロセスが少し先に進み、次のエラーが発生します。
CMake Error at ../../CMakeLists.txt:678 (message): The Zephyr library 'drivers__serial' was created without source files. Empty (non-imported) libraries are not supported. Either make sure that the library has the sources it should have, or make sure it is not created when it has no source files.
my_uartに、何らかのソースファイルを紐づける必要があるようです。とりあえず、stellarisのソースファイルを紐づけるとして、どこでソースファイルの紐づけを行っているか調べます。
|> ${ZEPHYR_BASE}/drivers/serial/CMakeLists.txt
zephyr_library() zephyr_library_sources_ifdef(CONFIG_UART_ALTERA_JTAG uart_altera_jtag_hal.c) zephyr_library_sources_if_kconfig(uart_imx.c) zephyr_library_sources_if_kconfig(uart_cc32xx.c) zephyr_library_sources_if_kconfig(uart_cmsdk_apb.c) ... zephyr_library_sources_if_kconfig(usart_sam.c) zephyr_library_sources_if_kconfig(uart_stellaris.c) zephyr_library_sources_if_kconfig(uart_stm32.c) ...
CONFIG_XXXX
のXXXX
部分と対応した名前のソースコードを指定します。
uart_stellaris.c
をコピーしてuart_my_uart.c
を作成し、次の1行を追加します。
zephyr_library_sources_if_kconfig(uart_my_uart.c)
これで、cmakeが通るようになります。
$ cmake -GNinja -DBOARD=qemu_cortex_m3 .. ... -- Configuring done -- Generating done -- Build files have been written to: ${ZEPHYR_BASE}/samples/hello_from_my_uart/build
寄り道 (zephyr_library_sources_if_kconfig )
|> ${ZEPHYR_BASE}/cmake/extensions.cmake
function(zephyr_library_sources_if_kconfig item) get_filename_component(item_basename ${item} NAME_WE) string(TOUPPER CONFIG_${item_basename} UPPER_CASE_CONFIG) zephyr_library_sources_ifdef(${UPPER_CASE_CONFIG} ${item}) endfunction()
zephyr_library_sources_if_kconfig(uart_my_uart.c)
と書いた場合、CONFIG_UART_MY_UART
が定義されていれば、ビルド対象になるようです。
難しい…。
動作確認
ビルドできるようになったので、動作確認してみます。
# at ${ZEPHYR_BASE}/samples/hello_from_my_uart $ ninja run [70/101] Building C object zephyr/driv...drivers__serial.dir/uart_my_uart.c.obj ${ZEPHYR_BASE}/drivers/serial/uart_my_uart.c:263:12: warning: ‘uart_stellaris_init’ defined but not used [-Wunused-function] static int uart_stellaris_init(struct device *dev) ^~~~~~~~~~~~~~~~~~~ ${ZEPHYR_BASE}/drivers/serial/uart_my_uart.c:604:37: warning: ‘uart_stellaris_driver_api’ defined but not used [-Wunused-const-variable=] static const struct uart_driver_api uart_stellaris_driver_api = { ^~~~~~~~~~~~~~~~~~~~~~~~~ [95/101] Linking C executable zephyr/zephyr_prebuilt.elf Memory region Used Size Region Size %age Used FLASH: 7288 B 256 KB 2.78% SRAM: 3952 B 64 KB 6.03% IDT_LIST: 8 B 2 KB 0.39% [101/101] To exit from QEMU enter: 'CTRL+a, x'[QEMU] CPU: cortex-m3 qemu-system-arm: warning: nic stellaris_enet.0 has no peer qemu: fatal: Lockup: can't escalate 3 to HardFault (current priority -1) R00=00000000 R01=0000002a R02=0000189b R03=4f480688 R04=0000002a R05=20000000 R06=000001fd R07=20000e10 R08=00000000 R09=ffffffff R10=00000000 R11=00000000 R12=00000000 R13=20000db4 R14=0000056f R15=4f480688 XPSR=20000003 --C- A handler FPSCR: 00000000 Aborted (core dumped)
むむむ…。driverのソースコードを確認します。
|> ${ZEPHYR_BASE}/drivers/serial/uart_my_uart.c
# line 626 #ifdef CONFIG_UART_STELLARIS_PORT_0 // Kconfigで名前を変えた #ifdef CONFIG_UART_INTERRUPT_DRIVEN static void irq_config_func_0(struct device *port); #endif ... DEVICE_AND_API_INIT(uart_stellaris0, DT_TI_STELLARIS_UART_4000C000_LABEL, &uart_stellaris_init, &uart_stellaris_dev_data_0, &uart_stellaris_dev_cfg_0, PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, &uart_stellaris_driver_api); ... #endif /* CONFIG_UART_STELLARIS_PORT_0 */
Kconfigで変更したシンボルが、ソースコードで反映されていないので、初期化が走っていないようです。
- #ifdef CONFIG_UART_STELLARIS_PORT_0 + #ifdef CONFIG_UART_MY_UART_PORT_0
修正して、これでもダメ!
元々、CONFIG_UART_STELLARIS_PORT_0
を有効化している場所を探してみると、soc
ディレクトリにあるdefconfigでした。
|> soc/arm/ti_lm3s6965/Kconfig.defconfig
... if UART_STELLARIS config UART_STELLARIS_PORT_0 default y ...
ということで、少しアドホックな対応ですが、boardファイルを少し修正します。
|> ${ZEPHYR_BASE}/samples/hello_from_my_uart/boards/arm/qemu_cortex_m3/Kconfig.defconfig
if UART_MY_UART config UART_MY_UART_PORT_0 default y endif # UART_MY_UART
さて、気を取り直して
$ ninja ${ZEPHYR_BASE}/drivers/serial/uart_my_uart.c:636:18: error: ‘DT_UART_STELLARIS_CLK_FREQ’ undeclared here (not in a function) .sys_clk_freq = DT_UART_STELLARIS_CLK_FREQ, ^~~~~~~~~~~~~~~~~~~~~~~~~~ ...
うげぇ~。
# at ${ZEPHYR_BASE} $ grep DT_UART_STELLARIS_CLK_FREQ soc/arm/ti_lm3s6965/* soc/arm/ti_lm3s6965/soc.h:#define DT_UART_STELLARIS_CLK_FREQ SYSCLK_DEFAULT_IOSC_HZ
なんかありますねぇ…。
/* uart configuration settings */ #if defined(CONFIG_UART_STELLARIS) #define DT_UART_STELLARIS_CLK_FREQ SYSCLK_DEFAULT_IOSC_HZ #endif /* CONFIG_UART_STELLARIS */
ああ、そういう…。泣く泣く、上記ヘッダファイルに追加します。
+ #if defined(CONFIG_UART_MY_UART) + + #define DT_UART_STELLARIS_CLK_FREQ SYSCLK_DEFAULT_IOSC_HZ + + #endif /* CONFIG_UART_MY_UART */
$ ninja run [2/6] Linking C executable zephyr/zephyr_prebuilt.elf Memory region Used Size Region Size %age Used FLASH: 7464 B 256 KB 2.85% SRAM: 3968 B 64 KB 6.05% IDT_LIST: 8 B 2 KB 0.39% [6/6] To exit from QEMU enter: 'CTRL+a, x'[QEMU] CPU: cortex-m3 qemu-system-arm: warning: nic stellaris_enet.0 has no peer ***** Booting Zephyr OS 1.13.99 ***** Hello World! qemu_cortex_m3
動きました!