diff --git a/common/beagleplay-sdcard-preparation.tex b/common/beagleplay-sdcard-preparation.tex new file mode 100644 index 0000000000..25cec0419c --- /dev/null +++ b/common/beagleplay-sdcard-preparation.tex @@ -0,0 +1,78 @@ +\section{Prepare the SD card} + +Our SD card needs to be split in two partitions: + +\begin{itemize} + +\item A first partition for the bootloader. It needs to comply with + the requirements of the AM62x SoC so that the ROM code can find + the bootloader in this partition. It should be a FAT32 partition. + We will store the bootloader files (\code{tiboot3.bin}, + \code{tispl.bin_unsigned} and \code{u-boot.img}), the kernel image + (\code{Image}) and the Device Tree + (\code{k3-am625-beagleplay.dtb}). + +\item A second partition for the root filesystem. It can use + whichever filesystem type you want, but for our system, we'll use + {\em ext4}. + +\end{itemize} + +First, let's identify under what name your SD card is identified in +your system: look at the output of \code{cat /proc/partitions} and +find your SD card. In general, if you use the internal SD card reader +of a laptop, it will be \code{mmcblk0}, while if you use an external +USB SD card reader, it will be \code{sdX} (i.e. \code{sdb}, \code{sdc}, +etc.). {\bf Be careful: \code{/dev/sda} is generally the hard drive of + your machine!} + +If your SD card is \code{/dev/mmcblk0}, then the partitions inside the +SD card are named \code{/dev/mmcblk0p1}, \code{/dev/mmcblk0p2}, etc. + +To format your SD card, do the following steps: + +\begin{enumerate} + +\item Unmount all partitions of your SD card (they are generally + automatically mounted by Ubuntu) + +\item Erase the beginning of the SD card to ensure that the existing + partitions are not going to be mistakenly detected:\\ + \code{sudo dd if=/dev/zero of=/dev/mmcblk0 bs=1M count=16}. + +\item Create the two partitions. + + \begin{itemize} + + \item Start the \code{cfdisk} tool for that:\\ + \code{sudo cfdisk /dev/mmcblk0} + + \item Choose the {\em dos} partition table type + + \item Create a first small partition (128 MB), primary, with type + \code{c} ({\em W95 FAT32 (LBA)}) and mark it bootable + + \item Create a second partition, also primary, with the rest of the + available space, with type \code{83} ({\em Linux}). + + \item Exit \code{cfdisk} + + \end{itemize} + +\item Format the first partition as a {\em FAT32} filesystem:\\ + \code{sudo mkfs.vfat -F 32 -n boot /dev/mmcblk0p1} + +\item Format the second partition as an {\em ext4} filesystem:\\ + \code{sudo mkfs.ext4 -L rootfs -E nodiscard /dev/mmcblk0p2} + +\begin{itemize} +\item \code{-L} assigns a volume name to the partition +\item \code{-E nodiscard} disables bad block discarding. While this + should be a useful option for cards with bad blocks, skipping + this step saves long minutes in SD cards. +\end{itemize} +\end{enumerate} + +Remove the SD card and insert it again, the two partitions should be +mounted automatically, in \code{/media/$USER/boot} and +\code{/media/$USER/rootfs}. diff --git a/common/buildroot-beagleplay-labs-vars.tex b/common/buildroot-beagleplay-labs-vars.tex new file mode 100644 index 0000000000..7bdcef2c5f --- /dev/null +++ b/common/buildroot-beagleplay-labs-vars.tex @@ -0,0 +1,2 @@ +\def\board{beagleplay} +\def\boarddescription{BeaglePlay} diff --git a/lab-data/buildroot-beagleplay/buildroot-appdev/myapp.c b/lab-data/buildroot-beagleplay/buildroot-appdev/myapp.c new file mode 100644 index 0000000000..4a3ef3b3c0 --- /dev/null +++ b/lab-data/buildroot-beagleplay/buildroot-appdev/myapp.c @@ -0,0 +1,23 @@ +#include +#include +#include + +int main(void) +{ + config_t cfg; + config_setting_t *setting; + const char *str; + + config_init(&cfg); + + if (config_read_file(&cfg, "myapp.cfg") == CONFIG_FALSE) + { + fprintf(stderr, "Cannot open config file\n"); + config_destroy(&cfg); + exit(1); + } + + printf("Config file successfully opened\n"); + + return 0; +} diff --git a/lab-data/buildroot-beagleplay/buildroot-basic/data/tiboot3.bin b/lab-data/buildroot-beagleplay/buildroot-basic/data/tiboot3.bin new file mode 100644 index 0000000000..e03098d43e Binary files /dev/null and b/lab-data/buildroot-beagleplay/buildroot-basic/data/tiboot3.bin differ diff --git a/lab-data/buildroot-beagleplay/buildroot-basic/data/tispl.bin b/lab-data/buildroot-beagleplay/buildroot-basic/data/tispl.bin new file mode 100644 index 0000000000..1f3eb69813 Binary files /dev/null and b/lab-data/buildroot-beagleplay/buildroot-basic/data/tispl.bin differ diff --git a/lab-data/buildroot-beagleplay/buildroot-new-packages/0001-joystick-support.patch b/lab-data/buildroot-beagleplay/buildroot-new-packages/0001-joystick-support.patch new file mode 100644 index 0000000000..433930fd3b --- /dev/null +++ b/lab-data/buildroot-beagleplay/buildroot-new-packages/0001-joystick-support.patch @@ -0,0 +1,122 @@ +diff --git a/nInvaders.c b/nInvaders.c +index 793139c2e171..8ded1e292455 100644 +--- a/nInvaders.c ++++ b/nInvaders.c +@@ -22,9 +22,12 @@ + */ + + ++#include ++#include + #include + #include + #include ++#include + #include "nInvaders.h" + #include "player.h" + #include "aliens.h" +@@ -35,6 +38,7 @@ + int lives; + long score; + int status; // status handled in timer ++int resend; + + #define GAME_LOOP 1 + #define GAME_NEXTLEVEL 2 +@@ -127,6 +131,58 @@ void drawscore() + statusDisplay(level, score, lives); + } + ++int td() ++{ ++ static struct timespec t0; ++ struct timespec t1; ++ double dt; ++ ++ clock_gettime(CLOCK_MONOTONIC, &t1); ++ dt = t1.tv_nsec - t0.tv_nsec; ++ t0 = t1; ++ ++ return dt > 500000; ++} ++ ++int getjs() ++{ ++ static int fd = -1; ++ struct js_event js; ++ ++ if (fd == -1) { ++ fd = open("/dev/input/js0", O_RDONLY | O_NONBLOCK); ++ if (fd < 0) ++ return -1; ++ } ++ ++ read(fd, &js, sizeof(struct js_event)); ++ ++ switch (js.type & ~JS_EVENT_INIT) { ++ case JS_EVENT_AXIS: ++ if (js.number == 0 && js.value < 0) { ++ resend |= 1 << 0; ++ return KEY_LEFT; ++ } else if (js.number == 0 && js.value > 0) { ++ resend |= 1 << 1; ++ return KEY_RIGHT; ++ } else if (js.number == 0 && js.value == 0) { ++ resend &= ~0x3; ++ } ++ break; ++ case JS_EVENT_BUTTON: ++ if (js.number == 2 && js.value == 1) { ++ resend |= 1 << 2; ++ return ' '; ++ } else if (js.number == 2 && js.value == 0) { ++ resend &= ~0x4; ++ } else if (js.number == 5 && js.value == 1) { ++ return 'p'; ++ } ++ break; ++ } ++ ++ return EOF; ++} + + /** + * reads input from keyboard and do action +@@ -136,7 +192,9 @@ void readInput() + int ch; + static int lastmove; + +- ch = getch(); // get key pressed ++ ch = getch(); ++ if (ch == EOF) ++ ch = getjs(); + + switch (status) { + +@@ -273,6 +331,17 @@ void handleTimer() + if (player_shot_counter++ >= 1) {player_shot_counter=0;} // speed of player shot + if (aliens_move_counter++ >= weite) {aliens_move_counter=0;} // speed of aliend + if (ufo_move_counter++ >= 3) {ufo_move_counter=0;} // speed of ufo ++ ++ ++ if (td()) { ++ if (resend & (1 << 0)) ++ playerMoveLeft(); ++ else if (resend & (1 << 1)) ++ playerMoveRight(); ++ if (resend & (1 << 2)) ++ playerLaunchMissile(); ++ ++ } + + refreshScreen(); + break; +@@ -336,6 +405,8 @@ int main(int argc, char **argv) + + evaluateCommandLine(argc, argv); // evaluate command line parameters + graphicEngineInit(); // initialize graphic engine ++ ++ nodelay(stdscr, TRUE); + + // set up timer/ game handling + setUpTimer(); diff --git a/lab-data/buildroot-beagleplay/buildroot-rootfs/linux/0001-Add-nunchuk-driver.patch b/lab-data/buildroot-beagleplay/buildroot-rootfs/linux/0001-Add-nunchuk-driver.patch new file mode 100644 index 0000000000..c1d937479a --- /dev/null +++ b/lab-data/buildroot-beagleplay/buildroot-rootfs/linux/0001-Add-nunchuk-driver.patch @@ -0,0 +1,464 @@ +From 7c37fae9e0075955966e1b10132d8482477cc097 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Antoine=20T=C3=A9nart?= +Date: Mon, 7 Apr 2014 11:55:31 +0200 +Subject: [PATCH 1/2] Add nunchuk driver +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Antoine Ténart +Signed-off-by: Thomas Petazzoni +--- + drivers/input/joystick/Kconfig | 13 + + drivers/input/joystick/Makefile | 1 + + drivers/input/joystick/wiichuck.c | 403 ++++++++++++++++++++++++++++++ + 3 files changed, 417 insertions(+) + create mode 100644 drivers/input/joystick/wiichuck.c + +diff --git a/drivers/input/joystick/Kconfig b/drivers/input/joystick/Kconfig +index ac6925ce8366..e155f724b789 100644 +--- a/drivers/input/joystick/Kconfig ++++ b/drivers/input/joystick/Kconfig +@@ -207,6 +207,19 @@ config JOYSTICK_TWIDJOY + To compile this driver as a module, choose M here: the + module will be called twidjoy. + ++config JOYSTICK_WIICHUCK ++ tristate "Nintendo Wiimote Extension connector on i2c bus" ++ depends on I2C ++ select INPUT_POLLDEV ++ help ++ Say Y here if you have a Nintendo Wiimote extension connector ++ attached directly to an i2c bus, like the Sparcfun Wiichuck adapter ++ board. This driver supports both the Nunchuk and the Classic ++ Controller extensions. ++ ++ To compile this driver as a module, choose M here: the ++ modules will be called wiichuck. ++ + config JOYSTICK_ZHENHUA + tristate "5-byte Zhenhua RC transmitter" + select SERIO +diff --git a/drivers/input/joystick/Makefile b/drivers/input/joystick/Makefile +index 3937535f0098..7dee2e46303d 100644 +--- a/drivers/input/joystick/Makefile ++++ b/drivers/input/joystick/Makefile +@@ -38,5 +38,6 @@ obj-$(CONFIG_JOYSTICK_TURBOGRAFX) += turbografx.o + obj-$(CONFIG_JOYSTICK_TWIDJOY) += twidjoy.o + obj-$(CONFIG_JOYSTICK_WARRIOR) += warrior.o + obj-$(CONFIG_JOYSTICK_WALKERA0701) += walkera0701.o ++obj-$(CONFIG_JOYSTICK_WIICHUCK) += wiichuck.o + obj-$(CONFIG_JOYSTICK_XPAD) += xpad.o + obj-$(CONFIG_JOYSTICK_ZHENHUA) += zhenhua.o +diff --git a/drivers/input/joystick/wiichuck.c b/drivers/input/joystick/wiichuck.c +new file mode 100644 +index 000000000000..1037e7a29b69 +--- /dev/null ++++ b/drivers/input/joystick/wiichuck.c +@@ -0,0 +1,403 @@ ++/* ++ * i2c Wiichuck driver (Nintendo Wiimote accessory connector) ++ * ++ * This driver supports Nintendo Wiimote accessories like the Nunchuk and ++ * the Classic Controller connected to an i2c bus. ++ * ++ * Copyright (c) 2011 Secret Lab Technologies Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of version 2 of the GNU General Public License as ++ * published by the Free Software Foundation. ++ * ++ * This driver uses the polled input device abstraction to implement an ++ * input driver for Nintendo expansion devices wired up to an i2c bus. ++ * ++ * A state machine implements the protocol handling. It starts in the ++ * DISCONNECTED state initially and polls every second waiting for a ++ * device to get attached, and reading the device id when one does. ++ * If the device is recognized, then the polling period is bumped ++ * to 100ms, and the state machine enters into a loop reading the data ++ * capture reports out of the controller. If at any time the device ++ * is disconnected, then it goes back to DISCONNECTED state and bumps ++ * the polling frequency back to 1 second. ++ * ++ * A callback is implemented for each supported devices, currently the ++ * Nunchuk and the Classic Controller. Wii Motion Plus has yet to be ++ * added. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++MODULE_AUTHOR("Grant Likely "); ++MODULE_DESCRIPTION("Wiichuck (i2c Nintendo Wiimote accessory) driver"); ++MODULE_LICENSE("GPL"); ++ ++#define WIICHUCK_POLL_PERIOD (1000) /* 1 second */ ++#ifdef DEBUG ++#define WIICHUCK_CAPTURE_PERIOD (500) /* 1/2 second for debug */ ++#else ++#define WIICHUCK_CAPTURE_PERIOD (100) /* 100 milliseconds */ ++#endif /* DEBUG */ ++ ++enum wiichuck_state { ++ WIICHUCK_STATE_DISCONNECTED = 0, ++ WIICHUCK_STATE_DATA, ++}; ++ ++struct wiichuck_device { ++ struct input_dev *input_dev; ++ struct i2c_client *i2c_client; ++ int (*process)(struct wiichuck_device *wiichuck); ++ enum wiichuck_state state; ++ ++ /* transfer buffer, aligned to cache line so it can be used with DMA */ ++ uint8_t buf[6] ____cacheline_aligned; ++}; ++ ++static int wiichuck_transfer(struct wiichuck_device *wiichuck, ++ int len, bool read) ++{ ++ struct i2c_client *i2c = wiichuck->i2c_client; ++ struct i2c_msg msg = { ++ .addr = i2c->addr, .len = len, .buf = wiichuck->buf }; ++ ++ if (read) ++ msg.flags = I2C_M_RD; ++ return i2c_transfer(i2c->adapter, &msg, 1); ++} ++ ++static int wiichuck_write(struct wiichuck_device *wiichuck, ++ uint8_t addr, uint8_t val) ++{ ++ wiichuck->buf[0] = addr; ++ wiichuck->buf[1] = val; ++ return wiichuck_transfer(wiichuck, 2, false); ++} ++ ++static inline int wiichuck_setaddr(struct wiichuck_device *wiichuck, ++ uint8_t addr) ++{ ++ wiichuck->buf[0] = addr; ++ return wiichuck_transfer(wiichuck, 1, false); ++} ++ ++static inline int wiichuck_read(struct wiichuck_device *wiichuck, int len) ++{ ++ return wiichuck_transfer(wiichuck, len, true); ++} ++ ++static inline bool WIIBTN(int val, int bit) ++{ ++ return (val & BIT(bit)) == 0; ++} ++ ++static int wiichuck_process_unknown(struct wiichuck_device *wiichuck) ++{ ++ return 0; ++} ++ ++static int wiichuck_process_nunchuk(struct wiichuck_device *wiichuck) ++{ ++ struct input_dev *input_dev = wiichuck->input_dev; ++ uint8_t *b = wiichuck->buf; ++ int ax, ay, az, rc; ++ ++ rc = wiichuck_read(wiichuck, 6); ++ if (rc < 0) ++ return rc; ++ ++ ax = (b[2] << 2) | ((b[5] >> 2) & 0x3); ++ ay = (b[3] << 2) | ((b[5] >> 4) & 0x3); ++ az = (b[4] << 2) | ((b[5] >> 6) & 0x3); ++ ++ input_report_abs(input_dev, ABS_X, b[0]); ++ input_report_abs(input_dev, ABS_Y, b[1]); ++ input_report_abs(input_dev, ABS_RX, ax); ++ input_report_abs(input_dev, ABS_RY, ax); ++ input_report_abs(input_dev, ABS_RZ, ay); ++ input_report_key(input_dev, BTN_C, WIIBTN(b[5], 1)); ++ input_report_key(input_dev, BTN_Z, WIIBTN(b[5], 0)); ++ input_sync(input_dev); ++ ++ dev_dbg(&wiichuck->i2c_client->dev, ++ "nunchuk: j=%.3i,%.3i a=%.3x,%.3x,%.3x c,z=%i,%i\n", ++ b[0],b[1], ax,ay,az, WIIBTN(b[5], 1), WIIBTN(b[5], 0)); ++ return 0; ++}; ++ ++static int wiichuck_process_classic(struct wiichuck_device *wiichuck) ++{ ++ struct input_dev *input_dev = wiichuck->input_dev; ++ uint8_t *b = wiichuck->buf; ++ int lx, ly, lt, rx, ry, rt, rc; ++ ++ rc = wiichuck_read(wiichuck, 6); ++ if (rc < 0) ++ return rc; ++ ++ /* Analog measurements; some values are split between registers */ ++ rx = ((b[0] >> 3) & 0x18) | ((b[1] >> 5) & 6) | ((b[2] >> 7) & 1); ++ ry = b[2] & 0x1f; ++ rt = b[3] & 0x1f; ++ lx = b[0] & 0x3f; ++ ly = b[1] & 0x3f; ++ lt = ((b[2] >> 2) & 0x18) | ((b[3] >> 5) & 7); ++ ++ input_report_abs(input_dev, ABS_HAT0X, lx); /* left joystick */ ++ input_report_abs(input_dev, ABS_HAT0Y, ly); ++ input_report_abs(input_dev, ABS_BRAKE, lt); /* left trigger */ ++ input_report_abs(input_dev, ABS_HAT1X, rx); /* right joystick */ ++ input_report_abs(input_dev, ABS_HAT1Y, ry); ++ input_report_abs(input_dev, ABS_GAS, rt); /* right trigger */ ++ ++ /* D-pad */ ++ input_report_abs(input_dev, ABS_HAT2X, WIIBTN(b[4],7) - WIIBTN(b[5],1)); ++ input_report_abs(input_dev, ABS_HAT2Y, WIIBTN(b[4],6) - WIIBTN(b[5],0)); ++ ++ /* Buttons */ ++ input_report_key(input_dev, BTN_TL, WIIBTN(b[4], 5)); /* left trigger */ ++ input_report_key(input_dev, BTN_SELECT, WIIBTN(b[4], 4)); /* minus */ ++ input_report_key(input_dev, BTN_MODE, WIIBTN(b[4], 3)); /* home */ ++ input_report_key(input_dev, BTN_START, WIIBTN(b[4], 2)); /* plus */ ++ input_report_key(input_dev, BTN_TR, WIIBTN(b[4], 1)); /* right trigger */ ++ ++ input_report_key(input_dev, BTN_TL2, WIIBTN(b[5], 7)); /* left z */ ++ input_report_key(input_dev, BTN_B, WIIBTN(b[5], 6)); ++ input_report_key(input_dev, BTN_Y, WIIBTN(b[5], 5)); ++ input_report_key(input_dev, BTN_A, WIIBTN(b[5], 4)); ++ input_report_key(input_dev, BTN_X, WIIBTN(b[5], 3)); ++ input_report_key(input_dev, BTN_TR2, WIIBTN(b[5], 2)); /* right z */ ++ input_sync(input_dev); ++ ++ dev_dbg(&wiichuck->i2c_client->dev, ++ "classic: r=%.3i,%.3i,%.3i l=%.3x,%.3x,%.3x b=%.2x,%.2x\n", ++ lx,ly,lt, rx,ry,rt, b[4],b[5]); ++ return 0; ++}; ++ ++/** ++ * wiichuck_poll() - Protocol state machine implementation ++ * ++ * A state machine is used here to keep the protocol processing ++ * reentrant. Any of the i2c transactions will indeed sleep because ++ * i2c transactions are slow, but there is a mandatory delay between ++ * triggering the data capture (set address) and reading the data ++ * back. Using a state machine means the poll function can return and ++ * free up the worker thread while waiting for the data. ++ */ ++static void wiichuck_poll(struct input_dev *input_dev) ++{ ++ struct wiichuck_device *wiichuck = input_get_drvdata(input_dev); ++ int id; ++ ++ switch (wiichuck->state) { ++ case WIICHUCK_STATE_DISCONNECTED: ++ /* Disable encryption */ ++ if (wiichuck_write(wiichuck, 0xf0, 0x55) < 0) ++ return; ++ if (wiichuck_write(wiichuck, 0xfb, 0x00) < 0) ++ return; ++ ++ /* Read device id */ ++ if (wiichuck_setaddr(wiichuck, 0xfe) < 0) ++ return; ++ if (wiichuck_read(wiichuck, 2) < 0) ++ return; ++ id = (wiichuck->buf[0] << 8) | wiichuck->buf[1]; ++ ++ switch (id) { ++ case 0x0000: /* Nunchuk */ ++ wiichuck->process = wiichuck_process_nunchuk; ++ dev_info(&wiichuck->i2c_client->dev, ++ "Connected Nunchuk\n"); ++ break; ++ ++ case 0x0101: /* Classic Controller */ ++ wiichuck->process = wiichuck_process_classic; ++ dev_info(&wiichuck->i2c_client->dev, ++ "Connected Classic Controller\n"); ++ break; ++ ++ default: /* No connection or unsupported device */ ++ wiichuck->process = wiichuck_process_unknown; ++ dev_dbg(&wiichuck->i2c_client->dev, ++ "Connected unknown id: %x\n", id); ++ return; ++ } ++ ++ /* Setup the first data transfer */ ++ if (wiichuck_setaddr(wiichuck, 0) < 0) ++ return; ++ ++ wiichuck->state = WIICHUCK_STATE_DATA; ++ input_set_poll_interval(input_dev, WIICHUCK_CAPTURE_PERIOD); ++ break; ++ ++ case WIICHUCK_STATE_DATA: ++ /* Read the input report */ ++ if (wiichuck->process(wiichuck) < 0) ++ goto disconnect; ++ ++ /* Setup the next transfer */ ++ if (wiichuck_setaddr(wiichuck, 0) < 0) ++ goto disconnect; ++ break; ++ ++ default: ++ goto disconnect; ++ } ++ return; ++ ++ disconnect: ++ dev_info(&wiichuck->i2c_client->dev, "disconnected\n"); ++ wiichuck->state = WIICHUCK_STATE_DISCONNECTED; ++ input_set_poll_interval(input_dev, WIICHUCK_POLL_PERIOD); ++} ++ ++/** ++ * wiichuck_open() - Set up initial state machine state ++ * ++ * Called exactly once the first time the device is opened by ++ * user space. Will not be called again unless all users close it ++ * before reopening. This simply clears the state to disconnected and ++ * sets the poll rate back to the slow speed ++ */ ++static int wiichuck_open(struct input_dev *input_dev) ++{ ++ struct wiichuck_device *wiichuck = input_get_drvdata(input_dev); ++ ++ wiichuck->process = wiichuck_process_unknown; ++ wiichuck->state = WIICHUCK_STATE_DISCONNECTED; ++ input_set_poll_interval(input_dev, WIICHUCK_POLL_PERIOD); ++ ++ return 0; ++} ++ ++static int wiichuck_probe(struct i2c_client *client) ++{ ++ struct wiichuck_device *wiichuck; ++ struct input_dev *input_dev; ++ int rc; ++ ++ wiichuck = devm_kzalloc(&client->dev, sizeof(*wiichuck), GFP_KERNEL); ++ if (!wiichuck) ++ return -ENOMEM; ++ ++ input_dev = devm_input_allocate_device(&client->dev); ++ if (!input_dev) ++ return -ENOMEM; ++ ++ wiichuck->i2c_client = client; ++ wiichuck->input_dev = input_dev; ++ ++ input_dev->name = "Wiichuck expansion connector"; ++ input_dev->open = wiichuck_open; ++ input_dev->id.bustype = BUS_I2C; ++ input_dev->dev.parent = &client->dev; ++ ++ /* Declare the events generated by this driver */ ++ __set_bit(EV_ABS, input_dev->evbit); ++ __set_bit(EV_KEY, input_dev->evbit); ++ ++ /* Nunchuk ranges */ ++ __set_bit(ABS_X, input_dev->absbit); /* joystick */ ++ __set_bit(ABS_Y, input_dev->absbit); ++ input_set_abs_params(input_dev, ABS_X, 30, 220, 4, 8); ++ input_set_abs_params(input_dev, ABS_Y, 40, 200, 4, 8); ++ ++ __set_bit(ABS_RX, input_dev->absbit); /* accelerometer */ ++ __set_bit(ABS_RY, input_dev->absbit); ++ __set_bit(ABS_RZ, input_dev->absbit); ++ input_set_abs_params(input_dev, ABS_RX, 0, 0x3ff, 4, 8); ++ input_set_abs_params(input_dev, ABS_RY, 0, 0x3ff, 4, 8); ++ input_set_abs_params(input_dev, ABS_RZ, 0, 0x3ff, 4, 8); ++ ++ /* Nunchuk buttons */ ++ __set_bit(BTN_C, input_dev->keybit); ++ __set_bit(BTN_Z, input_dev->keybit); ++ ++ /* Classic ranges */ ++ __set_bit(ABS_HAT0X, input_dev->absbit); /* Left Joystick */ ++ __set_bit(ABS_HAT0Y, input_dev->absbit); ++ input_set_abs_params(input_dev, ABS_HAT0X, 0, 63, 2, 4); ++ input_set_abs_params(input_dev, ABS_HAT0Y, 0, 63, 2, 4); ++ ++ __set_bit(ABS_HAT1X, input_dev->absbit); /* Right Joystick */ ++ __set_bit(ABS_HAT1Y, input_dev->absbit); ++ input_set_abs_params(input_dev, ABS_HAT1X, 0, 31, 1, 2); ++ input_set_abs_params(input_dev, ABS_HAT1Y, 0, 31, 1, 2); ++ ++ __set_bit(ABS_HAT2X, input_dev->absbit); /* D-pad */ ++ __set_bit(ABS_HAT2Y, input_dev->absbit); ++ input_set_abs_params(input_dev, ABS_HAT2X, -1, 1, 0, 0); ++ input_set_abs_params(input_dev, ABS_HAT2Y, -1, 1, 0, 0); ++ ++ __set_bit(ABS_BRAKE, input_dev->absbit); /* Left trigger */ ++ input_set_abs_params(input_dev, ABS_BRAKE, 0, 31, 1, 2); ++ __set_bit(ABS_GAS, input_dev->absbit); ++ input_set_abs_params(input_dev, ABS_GAS, 0, 31, 1, 2); ++ ++ /* Classic buttons */ ++ __set_bit(BTN_TL, input_dev->keybit); ++ __set_bit(BTN_SELECT, input_dev->keybit); ++ __set_bit(BTN_MODE, input_dev->keybit); ++ __set_bit(BTN_START, input_dev->keybit); ++ __set_bit(BTN_TR, input_dev->keybit); ++ __set_bit(BTN_TL2, input_dev->keybit); ++ __set_bit(BTN_B, input_dev->keybit); ++ __set_bit(BTN_Y, input_dev->keybit); ++ __set_bit(BTN_A, input_dev->keybit); ++ __set_bit(BTN_X, input_dev->keybit); ++ __set_bit(BTN_TR2, input_dev->keybit); ++ ++ i2c_set_clientdata(client, wiichuck); ++ input_set_drvdata(input_dev, wiichuck); ++ ++ rc = input_setup_polling(input_dev, wiichuck_poll); ++ if (rc) { ++ dev_err(&client->dev, "Failed to setup polling\n"); ++ return rc; ++ } ++ ++ /* Register the device; it is 'live' after this point */ ++ rc = input_register_device(input_dev); ++ if (rc) { ++ dev_err(&client->dev, "Failed to register input device\n"); ++ return rc; ++ } ++ ++ return 0; ++} ++ ++static const struct i2c_device_id wiichuck_id[] = { ++ { "wiichuck", 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, wiichuck_id); ++ ++#ifdef CONFIG_OF ++static const struct of_device_id wiichuck_match_table[] = { ++ { .compatible = "nintendo,wiimote-extension", }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, nunchuk_dt_match); ++#endif ++ ++static struct i2c_driver wiichuck_driver = { ++ .driver = { ++ .name = "wiichuck", ++ .owner = THIS_MODULE, ++ .of_match_table = of_match_ptr(wiichuck_match_table), ++ }, ++ .probe = wiichuck_probe, ++ .id_table = wiichuck_id, ++}; ++module_i2c_driver(wiichuck_driver); +-- +2.45.1 + diff --git a/lab-data/buildroot-beagleplay/buildroot-rootfs/linux/0002-Add-nunchuk-node-in-beagleplay-dts.patch b/lab-data/buildroot-beagleplay/buildroot-rootfs/linux/0002-Add-nunchuk-node-in-beagleplay-dts.patch new file mode 100644 index 0000000000..84310bb26c --- /dev/null +++ b/lab-data/buildroot-beagleplay/buildroot-rootfs/linux/0002-Add-nunchuk-node-in-beagleplay-dts.patch @@ -0,0 +1,31 @@ +From 0001000100010001000100010001000100010001 Mon Sep 17 00:00:00 2001 +From: Bootlin +Date: Sat, 1 Jun 2024 10:00:00 +0200 +Subject: [PATCH 2/2] arm64: dts: ti: k3-am625-beagleplay: Add nunchuk on I2C3 + +Declare a Nintendo Wiimote extension (Nunchuk) device on the I2C3 bus, +which is exposed on the mikroBUS connector of the BeaglePlay board. +The Nunchuk uses I2C address 0x52. + +Signed-off-by: Bootlin +--- + arch/arm64/boot/dts/ti/k3-am625-beagleplay.dts | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/arch/arm64/boot/dts/ti/k3-am625-beagleplay.dts b/arch/arm64/boot/dts/ti/k3-am625-beagleplay.dts +--- a/arch/arm64/boot/dts/ti/k3-am625-beagleplay.dts ++++ b/arch/arm64/boot/dts/ti/k3-am625-beagleplay.dts +@@ -633,3 +633,10 @@ + 0 0 0 0 + >; + }; ++ ++&main_i2c3 { ++ nunchuk: joystick@52 { ++ compatible = "nintendo,wiimote-extension"; ++ reg = <0x52>; ++ }; ++}; +-- +2.45.1 + diff --git a/labs/buildroot-advanced-beagleplay/buildroot-advanced-beagleplay.tex b/labs/buildroot-advanced-beagleplay/buildroot-advanced-beagleplay.tex new file mode 100644 index 0000000000..3c0cf37c11 --- /dev/null +++ b/labs/buildroot-advanced-beagleplay/buildroot-advanced-beagleplay.tex @@ -0,0 +1,262 @@ +\subchapter +{Advanced aspects} +{Objectives: + \begin{itemize} + \item Use build time, dependency and filesystem size graphing capabilities + \item Use licensing report generation, and add licensing + information to your own packages + \item Use \code{BR2_EXTERNAL} + \end{itemize} +} + +\section{Build time graphing} + +When your embedded Linux system grows, its build time will also grow, +so it is often interesting to understand where the build time is +spent. + +Since we just did a fresh clean rebuild at the end of the previous +lab, we can analyze the build time. The raw data has been generated by +Buildroot in \code{output/build/build-time.log}, which contains for +each step of each package the start time and end time (in seconds +since Epoch). + +Now, let's get a better visualization of this raw data: + +\begin{bashinput} +make graph-build +\end{bashinput} + +Note: you may need to install \code{python-matplotlib} and \code{graphviz} on your machine. + +The graphs are generated in \code{output/graphs}: + +\begin{itemize} +\item \code{build.hist-build.pdf}, build time of each package, by + build order +\item \code{build.hist-duration.pdf}, build time of each package, by + build duration +\item \code{build.hist-name.pdf}, build time of each package, by + package name +\item \code{build.pie-packages.pdf}, build time of each package, in + proportion of the total build time +\item \code{build.pie-steps.pdf}, build time of each step +\end{itemize} + +Explore those graphs, see which packages and steps are taking the +biggest amount of time. + +Note that when you don't do a clean rebuild, the \code{build-time.log} +file gets appended and appended with all the successful builds, making +the resulting graphs unexploitable. So remember to always do a clean +full rebuild before looking at the build time graphs. + +\section{Dependency graphing} + +Another useful tool to analyze the build is graphing dependencies +between packages. The dependency graph is generated for your current +configuration: depending on the Buildroot configuration, a given +package may have different dependencies. + +To generate the full dependency graph, do: + +\begin{bashinput} +make graph-depends +\end{bashinput} + +The graph is also generated in \code{output/graphs}, under the name +\code{graph-depends.pdf}. On the graph, identify the \code{bar} and +\code{ninvaders} packages you have created, and look at their +dependencies to see if they match your expectations. + +Now, let's draw a graph for a much bigger system. To do this, create a +completely separate Buildroot output directory: + +\begin{bashinput} +mkdir $HOME/__SESSION_NAME__-labs/buildroot-output-test-graph/ +cd $HOME/__SESSION_NAME__-labs/buildroot-output-test-graph/ +\end{bashinput} + +We're going to create a Buildroot configuration, so create a file +named \code{.config} and put the following contents: + +\begin{fileinput} +BR2_TOOLCHAIN_BUILDROOT_GLIBC=y +BR2_TOOLCHAIN_BUILDROOT_CXX=y +BR2_PACKAGE_MESA3D=y +BR2_PACKAGE_MESA3D_GALLIUM_DRIVER_SWRAST=y +BR2_PACKAGE_MESA3D_OPENGL_EGL=y +BR2_PACKAGE_MESA3D_OPENGL_ES=y +BR2_PACKAGE_XORG7=y +BR2_PACKAGE_XSERVER_XORG_SERVER=y +BR2_PACKAGE_LIBGTK3=y +BR2_PACKAGE_WEBKITGTK=y +\end{fileinput} + +It represents a configuration that builds an internal toolchain, with +a X.org graphic server, the Mesa3D OpenGL implementation, the Gtk3 +library, and the Webkit Web rendering engine. We're not going to build +this configuration, as it would take quite a bit of time, but we will +generate the dependency graph for it. + +First, let's run \code{make menuconfig} to expand this minimal +configuration into a full configuration: + +\begin{bashinput} +make -C $HOME/__SESSION_NAME__-labs/buildroot/ O=$(pwd) menuconfig +\end{bashinput} + +Feel free to explore the configuration at this stage. Now, let's +generate the dependency graph: + +\begin{bashinput} +make graph-depends +\end{bashinput} + +Look at \code{graphs/graph-depends.pdf} and how complex it is. Now, +let's look at the dependencies of one specific package, let's say +\code{libgtk3}: + +\begin{bashinput} +make libgtk3-graph-depends +\end{bashinput} + +Now, open the graph generated at +\code{graphs/libgtk3-graph-depends.pdf}. As you can see, it is a lot +more readable. + +Such dependencies graphs are very useful to understand why a package +is being built, and help identifying what you could do to reduce the +number of packages that are part of the build. + +\section{Filesystem size graphing} + +Run \code{make graph-size} and watch the PDF generated at +\code{output/graphs/graph-size.pdf}. You can also look at the CSV +files generated in \code{output/graphs/}. + +\section{Licensing report} + +Go back to our original build directory, in +\code{$HOME/__SESSION_NAME__-labs/buildroot/}. + +As explained during the lectures, Buildroot has a built-in mechanism +to generate a licensing report, describing all the components part of +the generated embedded Linux system, and their corresponding licenses. + +Let's generate this report for our system: + +\begin{bashinput} +make legal-info +\end{bashinput} + +In the output, you can see some interesting messages: + +\begin{verbatim} +WARNING: bar: cannot save license (BAR_LICENSE_FILES not defined) +WARNING: libfoo: cannot save license (LIBFOO_LICENSE_FILES not defined) +WARNING: ninvaders: cannot save license (NINVADERS_LICENSE_FILES not defined) +\end{verbatim} + +So, now update your \code{ninvaders}, \code{libfoo} and \code{bar} +packages to include license information. Run again \code{make + legal-info}. + +Now, explore \code{output/legal-info}, look at the \code{.csv} files, +the \code{.txt} files, and the various directories. Buildroot has +gathered for you most of what is needed to help with licensing +compliance. + +\section{Use {\tt BR2\_EXTERNAL}} + +We should have used \code{BR2_EXTERNAL} since the beginning of the +training, but we were busy learning about so many other things! So +it's finally time to use \code{BR2_EXTERNAL}. + +The whole point of \code{BR2_EXTERNAL} is to allow storing your +project-specific packages, configuration files, root filesystem +overlay or patches outside of the Buildroot tree itself. It makes it +easier to separate the open-source packages from the proprietary ones, +and it makes updating Buildroot itself a lot simpler. + +So, as recommended in the slides, the goal now is to use +\code{BR2_EXTERNAL} to move away from the main Buildroot tree the +following elements: + +\begin{itemize} + +\item The \code{bar} and \code{libfoo} packages. We will keep the + \code{ninvaders} package in the Buildroot tree, since it's a + publicly available open-source package, so it should be submitted to + the official Buildroot rather than kept in a \code{BR2_EXTERNAL} + tree. + +\item The Linux kernel patch and Linux kernel configuration file. + +\item The {\em rootfs overlay} + +\item The {\em post-build script} + +\item The {\em defconfig} + +\end{itemize} + +Your \code{BR2_EXTERNAL} tree should look like this: + +\begin{verbatim} ++-- board/ +| +-- bootlin/ +| +-- beagleplay/ +| +-- linux.config +| +-- post-build.sh +| +-- patches/ +| +-- linux/ +| +-- 0001-Add-nunchuk-driver.patch +| +-- 0002-Add-nunchuk-node-in-beagleplay-dts.patch +| +-- rootfs-overlay/ +| +-- etc +| +-- network +| +-- interfaces ++-- package/ +| +-- bar +| +-- 0001-Fix-missing-libconfig.h-include.patch +| +-- bar.mk +| +-- Config.in +| +-- libfoo +| +-- libfoo.mk +| +-- Config.in ++-- configs +| +-- bootlin_defconfig ++-- Config.in ++-- external.desc ++-- external.mk +\end{verbatim} + +Now, do a full rebuild using your \code{BR2_EXTERNAL} tree, and check +that your system builds and runs fine! + +\section{Going further} + +If you have some time left, let's improve our setup to use {\em + genimage}. This way, we will be able to generate a complete SD card +image, which we can flash on a SD card, without having to manually +create partitions. Follow those steps: + +\begin{itemize} + +\item Change the Buildroot configuration to generate an {\em ext4} + filesystem image + +\item Create your own \code{board/bootlin/beagleplay/genimage.cfg} + file. It should describe a disk image with a FAT32 boot partition + containing \code{tiboot3.bin}, \code{tispl.bin}, + \code{u-boot.img}, \code{Image} and the Device Tree Blob, and an + ext4 root filesystem partition. + +\item Adjust the Buildroot configuration to use the + \code{support/scripts/genimage.sh} script as a {\em post-image} + script, and pass \code{-c board/bootlin/beagleplay/genimage.cfg} + as {\em post-image} script arguments. Make sure to enable + \code{BR2_PACKAGE_HOST_GENIMAGE}. + +\end{itemize} diff --git a/labs/buildroot-advanced-packages-beagleplay/buildroot-advanced-packages-beagleplay.tex b/labs/buildroot-advanced-packages-beagleplay/buildroot-advanced-packages-beagleplay.tex new file mode 100644 index 0000000000..b2b2bc08f6 --- /dev/null +++ b/labs/buildroot-advanced-packages-beagleplay/buildroot-advanced-packages-beagleplay.tex @@ -0,0 +1,217 @@ +\subchapter +{Advanced packaging} +{Objectives: + \begin{itemize} + \item Package an application with a mandatory dependency and an + optional dependency + \item Package a library, hosted on GitHub + \item Use {\em hooks} to tweak packages + \item Add a patch to a package + \end{itemize} +} + +\section{Start packaging application {\tt bar}} + +For the purpose of this training, we have created a completely stupid +and useless application called \code{bar}. Its home page is +\url{https://bootlin.com/~thomas/bar/}, from where you can +download an archive of the application's source code. + +Create an initial package for \code{bar} in \code{package/bar}, with +the necessary code in \code{package/bar/bar.mk} and +\code{package/bar/Config.in}. Don't forget +\code{package/bar/bar.hash}. At this point, your \code{bar.mk} should +only define the \code{_VERSION}, \code{_SOURCE} and +\code{_SITE} variables, and a call to a package infrastructure. + +Enable the \code{bar} package in your Buildroot configuration, and +start the build. It should download \code{bar}, extract it, and start +the configure script. And then it should fail with an error related to +\code{libfoo}. And indeed, as the \code{README} file available in +\code{bar}'s source code says, it has a mandatory dependency on +\code{libfoo}. So let's move on to the next section, and we'll start +packaging \code{libfoo}. + +\section{Packaging {\tt libfoo}: initial packaging} + +According to \code{bar}'s \code{README} file, \code{libfoo} is only +available on {\em GitHub} at +\url{https://github.com/tpetazzoni/libfoo}. + +Create an initial package for \code{libfoo} in \code{package/libfoo}, +with the relevant minimal variables to get \code{libfoo} downloaded +properly. Since it's hosted on {\em GitHub}, remember to use the +\code{github} {\em make} function provided by Buildroot to define +\code{_SITE}. To learn more about this function, \code{grep} for +it in the Buildroot tree, or read the Buildroot reference manual. + +Also, notice that there is a version tagged \code{v0.1} in the GitHub +repository, you should probably use it. + +Enable the \code{libfoo} package and start the build. You should get +an error due to the \code{configure} script being missing. What can +you do about it? Hint: there is one Buildroot variable for {\em +autotools} packages to solve this problem. + +\code{libfoo} should now build fine. Look in +\code{output/target/usr/lib}, the dynamic version of the library +should be installed. However, if you look in \code{output/staging/}, +you will see no sign of \code{libfoo}, neither the library in +\code{output/staging/usr/lib} or the header file in +\code{output/staging/usr/include}. This is an issue because the +compiler will only look in \code{output/staging} for libraries and +headers, so we must change our package so that it also installs to the +{\em staging directory}. Adjust your \code{libfoo.mk} file to achieve +this, restart the build of \code{libfoo}, and make sure that you see +\code{foo.h} in \code{output/staging/usr/include} and \code{libfoo.*} +in \code{output/staging/usr/lib}. + +Now everything looks good, but there are some more improvements we can +do. + +\section{Improvements to {\tt libfoo} packaging} + +If you look in \code{output/target/usr/bin}, you can see a program +called \code{libfoo-example1}. This is just an example program for +\code{libfoo}, it is typically not very useful in a real target +system. So we would like this example program to not be installed. To +achieve this, add a {\em post-install target hook} that removes +\code{libfoo-example1}. Rebuild the \code{libfoo} package and verify +that \code{libfoo-example1} has been properly removed. + +Now, if you go in \code{output/build/libfoo-v0.1}, and run +\code{./configure --help} to see the available options, you should see +an option named \code{--enable-debug-output}, which enables a +debugging feature of \code{libfoo}. Add a sub-option in +\code{package/libfoo/Config.in} to enable the debugging feature, and +the corresponding code in \code{libfoo.mk} to pass +\code{--enable-debug-output} or \code{--disable-debug-output} when +appropriate. + +Enable this new option in \code{menuconfig}, and restart the build of +the package. Verify in the build output that +\code{--enable-debug-output} was properly passed as argument to the +\code{configure} script. + +Now, the packaging of \code{libfoo} seems to be alright, so let's get +back to our \code{bar} application. + +\section{Finalize the packaging of {\tt bar}} + +So, \code{bar} was failing to configure because \code{libfoo} was +missing. Now that \code{libfoo} is available, modify \code{bar} to add +\code{libfoo} as a dependency. Remember that this needs to be done in +two places: \code{Config.in} file and \code{bar.mk} file. + +Restart the build, and it should succeed! Now you can run the +\code{bar} application on your target, and discover how absolutely +useless it is, except for allowing you to learn about Buildroot +packaging! + +\section{{\tt bar} packaging: {\em libconfig} dependency} + +But there's some more things we can do to improve \code{bar}'s +packaging. If you go to \code{output/build/bar-1.1} and run +\code{./configure --help}, you will see that it supports a +\code{--with-libconfig} option. And indeed, \code{bar}'s \code{README} +file also mentions \code{libconfig} as an optional dependency. + +So, change \code{bar.mk} to add {\em libconfig} as an optional +dependency. No need to add a new \code{Config.in} option for that: +just make sure that when {\em libconfig} is enabled in the Buildroot +configuration, \code{--with-libconfig} is passed to \code{bar}'s {\em +configure} script, and that {\em libconfig} is built before +\code{bar}. Also, pass \code{--without-libconfig} when {\em libconfig} +is not enabled. + +Enable \code{libconfig} in your Buildroot configuration, and restart +the build of \code{bar}. What happens? + +It fails to build with messages like \code{error: unknown type name +‘config_t’}. Seems like the author of \code{bar} messed up and forgot +to include the appropriate header file. Let's try to fix this: go to +\code{bar}'s source code in \code{output/build/bar-1.1} and edit +\code{src/main.c}. Right after the \code{#if defined(USE_LIBCONFIG)}, +add a \code{#include }. Save, and restart the build of +\code{bar}. Now it builds fine! + +However, try to rebuild \code{bar} from scratch by doing \code{make +bar-dirclean all}. The build problem happens again. This is because +doing a change directly in \code{output/build/} might be good for +doing a quick test, but not for a permanent solution: everything in +\code{output/} is deleted when doing a \code{make clean}. So instead of +manually changing the package source code, we need to generate a +proper patch for it. + +There are multiple ways to create patches, but we'll simply use Git to +do so. As the \code{bar} project home page indicates, a Git repository +is available on GitHub at \code{https://github.com/tpetazzoni/bar}. + +Start by cloning the Git repository: + +\begin{bashinput} +git clone https://github.com/tpetazzoni/bar.git +\end{bashinput} + +Once the cloning is done, go inside the \code{bar} directory, and +create a new branch named \code{buildroot}, which starts the +\code{v1.1} tag (which matches the \code{bar-1.1.tar.gz} tarball we're +using): + +\begin{bashinput} +git branch buildroot v1.1 +\end{bashinput} + +Move to this newly created branch\footnote{Yes, we can use \code{git +checkout -b} to create the branch and move to it in one command}: + +\begin{bashinput} +git checkout buildroot +\end{bashinput} + +Do the \code{#include } change to \code{src/main.c}, and +commit the result: + +\begin{bashinput} +git commit -a -m "Fix missing include" +\end{bashinput} + +Generate the patch for the last commit (i.e. the one you just created): + +\begin{bashinput} +git format-patch HEAD^ +\end{bashinput} + +and copy the generated \code{0001-*.patch} file to \code{package/bar/} +in the Buildroot sources. + +Now, restart the build with \code{make bar-dirclean all}, it should +built fully successfully! + +You can even check that \code{bar} is linked against +\code{libconfig.so} by doing: + +\begin{bashinput} +./output/host/bin/aarch64-linux-readelf -d output/target/usr/bin/bar +\end{bashinput} + +On the target, test \code{bar}. Then, create a file called +\code{bar.cfg} in the current directory, with the following contents: + +\begin{fileinput} +verbose = "yes" +\end{fileinput} + +And run \code{bar} again, and see what difference it makes. + +Congratulations, you've finished packaging the most useless application +in the world! + +\section{Preparing for the next lab} + +In preparation for the next lab, we need to do a clean full rebuild, +so simply issue: + +\begin{bashinput} +make clean all 2>&1 | tee build.log +\end{bashinput} diff --git a/labs/buildroot-appdev-beagleplay/buildroot-appdev-beagleplay.tex b/labs/buildroot-appdev-beagleplay/buildroot-appdev-beagleplay.tex new file mode 100644 index 0000000000..332caf0dba --- /dev/null +++ b/labs/buildroot-appdev-beagleplay/buildroot-appdev-beagleplay.tex @@ -0,0 +1,232 @@ +\subchapter +{Application development with Buildroot} +{Objectives: + \begin{itemize} + \item Build and run your own application + \item Remote debug your application + \item Create a package for your application + \end{itemize} +} + +\section{Build and run your own application} + +Let's create your own little application that we will use for +demonstration in this lab. Create a folder \code{$HOME/__SESSION_NAME__-labs/myapp}, +and inside this folder a single C file called \code{myapp.c} with the +following contents: + +\begin{fileinput} +#include + +int main(void) { + printf("Hello World\n"); + return 0; +} +\end{fileinput} + +To build this application, we'll use the cross-compiler generated by +Buildroot. To make this easy, let's add the Buildroot host directory +into our PATH: + +\begin{bashinput} +export PATH=$HOME/__SESSION_NAME__-labs/buildroot/output/host/bin:$PATH +\end{bashinput} + +Now you can build your application easily: + +\begin{bashinput} +aarch64-linux-gcc -o myapp myapp.c +\end{bashinput} + +Copy the myapp binary to your target using scp (we use the legacy {\em +SCP} protocol, as we haven't installed a {\em SFTP} server, hence +the \code{-O} option): + +\begin{bashinput} +scp -O myapp root@192.168.0.100: +\end{bashinput} + +And run the \code{myapp} application on your target. + +Now, let's extend the application a little bit more to use a library, +the \code{libconfig} library we've already used in a previous +lab. Change the source code of the application to the one provided in +this lab data directory, \code{myapp.c}. + +If you try to build this application with just: + +\begin{bashinput} +aarch64-linux-gcc -o myapp myapp.c +\end{bashinput} + +It fails to build because it does not link with \code{libconfig}. So +you can manually do: + +\begin{bashinput} +aarch64-linux-gcc -o myapp myapp.c -lconfig +\end{bashinput} + +Since \code{libconfig.so} is in \code{output/staging/usr/lib} and the +compiler is configured to automatically look in \code{output/staging} +as its {\em sysroot}, it works fine. + +However, there's a better solution: using {\em pkg-config}. Buildroot +has installed a special version of \code{pkg-config} in +\code{output/host/bin}, which you can query for libraries +available for the target. Run: + +\begin{bashinput} +pkg-config --list-all +\end{bashinput} + +And check you have \code{libconfig} mentionned. You can query the +compiler and linker flags for \code{libconfig}: + +\begin{bashinput} +pkg-config --cflags --libs libconfig +\end{bashinput} + +And use that to build your application: + +\begin{bashinput} +aarch64-linux-gcc -o myapp myapp.c $(pkg-config --cflags --libs libconfig) +\end{bashinput} + +In the case of \code{libconfig}, it doesn't simplify a lot because the +compiler and linker flags are simple, but for some other libraries, +they are more complicated. + +Copy the new version of \code{myapp} to your target, and run +it. Create a \code{myapp.cfg} config file, and run your application +again. + +\section{Remote debug your application} + +Our application is simple and works, but what if you need to debug it? +So let's set up remote debugging. + +The toolchain is provided with a pre-compiled {\em gdbserver}, so +we'll simply use it. Enable the option +\code{BR2_TOOLCHAIN_EXTERNAL_GDB_SERVER_COPY}, and then force the +re-installation of the toolchain using: + +\begin{bashinput} +make toolchain-external-bootlin-reinstall +\end{bashinput} + +Reflash your system, or alternatively, just copy +\code{output/target/usr/bin/gdbserver} to the target \code{/usr/bin/} +directory using \code{scp}. + +To do some appropriate debugging, we need to have debugging symbols +available. So we need to do two things: + +\begin{enumerate} + +\item Rebuild our application with the \code{-g} flag. + +\item Rebuild the Buildroot system with debugging symbols, so that + shared libraries have debugging symbols. However, since we don't + want to rebuild the entire Buildroot system now, we'll use a trick + and rebuild only the library we need to have the debugging symbols + for: \code{libconfig}. To achieve this, first go to Buildroot + \code{menuconfig}, and in \code{Build options}, enable + \code{build packages with debugging symbols}. Then, do \code{make + libconfig-dirclean all} to force the rebuild of just + \code{libconfig}. + +\end{enumerate} + +Now, on your target, start {\em gdbserver} in multi-process mode, +listening on TCP port 2345: + +\begin{bashinput} +gdbserver --multi localhost:2345 +\end{bashinput} + +Back on the host, run the cross-gdb with the \code{myapp} application +as argument: + +\begin{bashinput} +aarch64-linux-gdb myapp +\end{bashinput} + +We need to tell \code{gdb} where the libraries can be found: + +\begin{bashinput} +(gdb) set sysroot output/staging +\end{bashinput} + +And then connect to the target: + +\begin{bashinput} +(gdb) target extended-remote 192.168.0.100:2345 +\end{bashinput} + +Define which program we want to run on the target: + +\begin{bashinput} +(gdb) set remote exec-file myapp +\end{bashinput} + +Let's put a breakpoint on the \code{main} function, and start the +program: + +\begin{bashinput} +(gdb) break main +(gdb) run +\end{bashinput} + +It stops on the first line of the \code{main} function, which is the +call to \code{config_init}, implemented by the \code{libconfig} +library. If you do the {\em gdb} instruction \code{step}, {\em gdb} +will step into the function, so you can follow what happens. After +having done \code{step} once, you can do \code{backtrace} to see that +you are in the function \code{config_init} called by \code{main}: + +\begin{verbatim} +(gdb) backtrace +#0 config_init (config=0xbefffc3c) at libconfig.c:725 +#1 0x000106f0 in main () at myapp.c:11 +\end{verbatim} + +Note that if you want \code{gdbserver} to stop on the target, you need +to run the {\em gdb} command \code{monitor exit}. + +\section{Create a package for your application} + +Building manually your own application is not desirable, we obviously +want to create a Buildroot package for it. A useful mechanism to +package your own applications is to use the +\code{local} {\em site method}, which tells Buildroot that the source +code of your application is available locally. + +Create a new package called \code{myapp} in your \code{BR2_EXTERNAL} +tree, and by using the \code{local} {\em site method}, make it use +directly the \code{myapp} source code from +\code{$HOME/__SESSION_NAME__-labs/myapp}. Remember that you can use \code{$(TOPDIR)} +to reference the top-level directory of the Buildroot sources. + +For now, directly call \code{gcc} in the build commands. Of course, if +your application becomes more complicated, you should start using a +proper build system (Makefile, autotools, CMake, etc.). + +When the package builds, you should see as the first step being done +that the \code{myapp} source code gets {\em rsynced} from +\code{$(HOME)/bootlin/myapp}: + +\begin{verbatim} +>>> myapp custom Syncing from source dir /home/thomas/bootlin/myapp +\end{verbatim} + +The build should now proceed to the end. Now, make a stupid but +visible change to the source code in \code{myapp.c}. + +Restart the build of \code{myapp} using \code{make myapp-rebuild}, you +will see that Buildroot automatically {\em rsyncs} again the source +code. Then scp the file \code{output/target/usr/bin/myapp} to +\code{192.168.0.100:/usr/bin} and run \code{myapp} again on the target. + +As you can see you can now develop your applications and libraries, +using your normal version control system and relying on Buildroot to +do all the configure, build and install steps for you. diff --git a/labs/buildroot-basic-beagleplay/buildroot-basic-beagleplay.tex b/labs/buildroot-basic-beagleplay/buildroot-basic-beagleplay.tex new file mode 100644 index 0000000000..18f3de9c5d --- /dev/null +++ b/labs/buildroot-basic-beagleplay/buildroot-basic-beagleplay.tex @@ -0,0 +1,349 @@ +\subchapter +{Basic Buildroot usage} +{Objectives: + \begin{itemize} + \item Get Buildroot + \item Configure a minimal system with Buildroot for the BeaglePlay + \item Do the build + \item Prepare the BeaglePlay for usage + \item Flash and test the generated system + \end{itemize} +} + +\section{Setup} + +Go to the \code{$HOME/__SESSION_NAME__-labs/} directory. + +As specified in the Buildroot +manual\footnote{\url{https://buildroot.org/downloads/manual/manual.html\#requirement-mandatory}}, +Buildroot requires a few packages to be installed on your +machine. Let's install them using Ubuntu's package manager: + +\begin{bashinput} +sudo apt install sed make binutils gcc g++ bash patch \ + gzip bzip2 perl tar cpio python3 unzip rsync wget libncurses-dev mtools +\end{bashinput} + +\section{Download Buildroot} + +Since we're going to do Buildroot development, let's clone the +Buildroot source code from its Git repository: + +\begin{bashinput} +git clone https://gitlab.com/buildroot.org/buildroot.git +\end{bashinput} + +Go into the newly created \code{buildroot} directory. + +We're going to start a branch from the {\em 2025.02.6} Buildroot +release, with which this training has been tested. + +\begin{bashinput} +git checkout -b bootlin 2025.02.6 +\end{bashinput} + +\section{Configuring Buildroot} + +If you look under \code{configs/}, you will see that there are +ready-to-use Buildroot configuration files for various platforms. +However, since we want to learn about Buildroot, we'll start our own +configuration from scratch! + +Start the Buildroot configuration utility: + +\begin{bashinput} +make menuconfig +\end{bashinput} + +Of course, you're free to try out the other configuration utilities +\code{nconfig}, \code{xconfig} or \code{gconfig}. + +Now, let's do the configuration: + +\begin{itemize} + +\item \code{Target Options} menu + + \begin{itemize} + + \item The BeaglePlay uses a Texas Instruments AM625x SoC, which + contains four ARM Cortex-A53 cores. This is a 64-bit ARM platform, + so select \code{AArch64 (little endian)} as the target architecture. + + \item Select \code{cortex-A53} as the \code{Target Architecture + Variant}. + + \item The other parameters can be left to their default values. + + \end{itemize} + +\item We don't have anything special to change in the + \code{Build options} menu, but take nonetheless this opportunity to + visit this menu, and look at the available options. Each option has + a help text that tells you more about the option. + +\item \code{Toolchain} menu + + \begin{itemize} + + \item By default, Buildroot builds its own toolchain. This takes + quite a bit of time, and for \code{AArch64} platforms, there is a + pre-built toolchain provided by Bootlin. We'll use it through the + {\em external toolchain} mechanism of Buildroot. Select + \code{External toolchain} as the \code{Toolchain type}. Do not + hesitate however to look at the available options when you select + \code{Buildroot toolchain} as the \code{Toolchain type}. + + \item Select \code{Bootlin toolchains} as the \code{Toolchain}. It + will automatically select the \code{aarch64 glibc bleeding-edge + 2024.05-1} variant, which is fine for our needs. Buildroot can + either use pre-defined toolchains such as the ones provided by ARM + or Bootlin, or custom toolchains (either downloaded from a given + location, or pre-installed on your machine). + + \end{itemize} + +\item \code{System configuration} menu + + \begin{itemize} + + \item For our basic system, we don't need a lot of custom {\em + system configuration} for the moment. So take some time to look + at the available options, and put some custom values for the + \code{System hostname}, \code{System banner} and \code{Root + password}. + + \end{itemize} + +\item \code{Kernel} menu + + \begin{itemize} + + \item We obviously need a Linux kernel to run on our platform, so + enable the \code{Linux kernel} option. + + \item By default, the most recent Linux kernel version available at + the time of the Buildroot release is used. In our case, we want to + use a specific version, to make sure our build is reproducible. So + select \code{Custom version} as the \code{Kernel version}, and + enter \code{6.12.47} in the \code{Kernel version} text field that + appears. + + \item Now, we need to define which kernel configuration to + use. We'll start by using a default configuration provided within + the kernel sources themselves. Select \code{Use the architecture + default configuration} as the \code{Kernel configuration}. On + ARM64, this uses the unified \code{defconfig} which supports all + ARM64 platforms, including the TI AM62x. + + \item The \code{Kernel binary format} is the next option. On AArch64 + platforms, the kernel image format is \code{Image}. Make sure this + option is selected. + + \item On ARM64, all platforms use the {\em Device Tree} to + describe the hardware. The BeaglePlay is in this situation, so + you'll have to enable the \code{Build a Device Tree Blob (DTB)} + option. At + \url{https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/arch/arm64/boot/dts/ti/?id=v6.12}, + you can see the list of Device Tree files for TI ARM64 platforms. + The one for the BeaglePlay is \code{k3-am625-beagleplay.dts}. + Back in Buildroot, enable \code{Build a Device Tree Blob (DTB)} + and type \code{ti/k3-am625-beagleplay} as the \code{In-tree Device + Tree Source file names}. + + \item The kernel configuration for this platform requires having + OpenSSL available on the host machine. To avoid depending on the + OpenSSL development files installed by your host machine Linux + distribution, Buildroot can build its own version: just enable the + \code{Needs host OpenSSL} option. + + \end{itemize} + +\item \code{Target packages} menu. This is probably the most important + menu, as this is the one where you can select amongst the 3000+ + available Buildroot packages which ones should be built and + installed in your system. For our basic system, enabling + \code{BusyBox} is sufficient and is already enabled by default, but + feel free to explore the available packages. We'll have the + opportunity to enable some more packages in the next labs. + +\item \code{Filesystem images} menu. For now, keep only the \code{tar + the root filesystem} option enabled. We'll take care separately of + flashing the root filesystem on the SD card. + +\item \code{Bootloaders} menu. + + \begin{itemize} + + \item We'll use the most popular ARM bootloader, {\em U-Boot}, so + enable it in the configuration. + + \item Select \code{Kconfig} as the \code{Build system}. U-Boot is + transitioning from a situation where all the hardware platforms + were described in C header files to a system where U-Boot re-uses + the Linux kernel configuration logic. Since we are going to use a + recent enough U-Boot version, we are going to use the latter, + called {\em Kconfig}. + + \item Use the custom version of U-Boot \code{2024.04}. + + \item The BeaglePlay uses the TI AM62x SoC from the K3 family. The + U-Boot configuration for the A53 cores is + \code{am62x_beagleplay_a53_defconfig}. So enter + \code{am62x_beagleplay_a53} as the \code{Board defconfig}. + + \item Select \code{u-boot.img} as the \code{U-Boot binary format}. + We only need Buildroot to produce \code{u-boot.img} (the second + stage bootloader). The first stage bootloaders are provided + pre-built (see note below). + + \item In \code{Custom make options}, enter + \code{BINMAN_ALLOW_MISSING=1}. This allows the U-Boot build to + complete even though we are not building the first-stage boot + images (which require additional firmware blobs). + + \end{itemize} + +\item {\bf Note:} The AM62x SoC has a complex multi-stage boot + process involving multiple processor cores. Before the Cortex-A53 + cores can run U-Boot, a Cortex-R5F core must first initialize the + SoC (configure the DDR memory, load TI Foundational Security + firmware, etc.). The first-stage boot chain consists of: + + \begin{itemize} + \item \code{tiboot3.bin}: the R5 U-Boot SPL combined with TI + system firmware, which initializes DDR and starts the A53 cores. + \item \code{tispl.bin}: a FIT image containing the A53 U-Boot SPL, + ARM Trusted Firmware (TF-A), OP-TEE, and the TI Device Manager + firmware. This is loaded by \code{tiboot3.bin} and handles the + secure boot chain before loading \code{u-boot.img}. + \end{itemize} + + Building these first-stage images requires TI's downstream U-Boot + fork, a separate 32-bit ARM toolchain (\code{arm-none-eabi-}), and + TI firmware from \code{ti-linux-firmware}. Since this is outside + the scope of Buildroot, we provide pre-built \code{tiboot3.bin} + and \code{tispl.bin} in the lab data directory at + \code{$HOME/__SESSION_NAME__-labs/buildroot-basic/data/}. + + If you want to learn how to build them yourself, refer to the + Bootlin {\em Embedded Linux} training course, which covers the + full AM62x boot chain in detail. + +\end{itemize} + +You're now done with the configuration! + +\section{Building} + +You could simply run \code{make}, but since we would like to keep a +log of the build, we'll redirect both the standard and error outputs +to a file, as well as the terminal by using the \code{tee} command: + +\begin{bashinput} +make 2>&1 | tee build.log +\end{bashinput} + +While the build is on-going, please go through the following sections +to prepare what will be needed to test the build results. + +\section{Prepare the BeaglePlay} + +\input{beagleplay-prepare.tex} + +\input{beagleplay-sdcard-preparation.tex} + +Now everything should be ready. Hopefully by that time the Buildroot +build should have completed. If not, wait a little bit more. + +\section{Flash the system} + +Once Buildroot has finished building the system, it's time to put it +on the SD card: + +\begin{itemize} + +\item Copy the pre-built \code{tiboot3.bin} and \code{tispl.bin} from + the lab data directory + \code{$HOME/__SESSION_NAME__-labs/buildroot-basic/data/} to the + \code{boot} partition of the SD card. + +\item Copy \code{u-boot.img} from \code{output/images/} to the + \code{boot} partition of the SD card. + +\item Copy the \code{Image} and \code{k3-am625-beagleplay.dtb} files + from \code{output/images/} to the \code{boot} partition of the SD + card. + +\item Extract the \code{rootfs.tar} file to the \code{rootfs} + partition of the SD card, using:\\ + \inlinebash{sudo tar -C /media/$USER/rootfs/ -xf output/images/rootfs.tar} + +\item Create a file named \code{extlinux/extlinux.conf} in the + \code{boot} partition. This file should contain the following lines: + +{\small +\begin{fileinput} +label buildroot + kernel /Image + devicetree /k3-am625-beagleplay.dtb + append console=ttyS2,115200n8 root=/dev/mmcblk1p2 rootwait +\end{fileinput} +} + +These lines teach the U-Boot bootloader how to load the Linux kernel +image and the Device Tree, before booting the kernel. It uses a +standard U-Boot mechanism called {\em distro boot command}, see +\url{https://source.denx.de/u-boot/u-boot/-/raw/master/doc/README.distro} +for more details. + +\end{itemize} + +Cleanly unmount the two SD card partitions, and eject the SD card. + +\section{Boot the system} + +Insert the SD card in the BeaglePlay's microSD slot. Press and hold +the \code{USR} button (located on the side of the board, near the +microSD slot) and power up the board through its USB-C connection. +You can release the \code{USR} button once you see output on the +serial console. Pressing \code{USR} forces the BeaglePlay to boot +from the SD card instead of from the internal eMMC. + +You should see your system booting. Make sure that the U-Boot version +and build dates match with the current date. Do the same check for the +Linux kernel. + +Login as \code{root} on the BeaglePlay, and explore the system. Run +\code{ps} to see which processes are running, and look at what +Buildroot has generated in \code{/bin}, \code{/lib}, \code{/usr} and +\code{/etc}. + +Note: if your system doesn't boot as expected, make sure to reset the +U-Boot environment by running the following U-Boot commands: + +\begin{bashinput} +env default -f -a +saveenv +\end{bashinput} + +and reset. This is needed because the U-Boot loaded from the SD card +may still load the U-Boot environment from the eMMC. Ask your +instructor for additional clarifications if needed. + +\section{Explore the build log} + +Back to your build machine, since we redirected the build output to a +file called \code{build.log}, we can now have a look at it to see what +happened. Since the Buildroot build is quite verbose, Buildroot prints +before each important step a message prefixed by the \code{>>>} +sign. So to get an overall idea of what the build did, you can run: + +\begin{bashinput} +grep ">>>" build.log +\end{bashinput} + +You see the different packages between downloaded, extracted, patched, +configured, built and installed. + +Feel free to explore the \code{output/} directory as well. diff --git a/labs/buildroot-new-packages-beagleplay/buildroot-new-packages-beagleplay.tex b/labs/buildroot-new-packages-beagleplay/buildroot-new-packages-beagleplay.tex new file mode 100644 index 0000000000..3ee0f48527 --- /dev/null +++ b/labs/buildroot-new-packages-beagleplay/buildroot-new-packages-beagleplay.tex @@ -0,0 +1,238 @@ +\subchapter +{New packages in Buildroot} +{Objectives: + \begin{itemize} + \item Create a new package for {\em nInvaders} + \item Understand how to add dependencies + \item Add patches to {\em nInvaders} for {\em Nunchuk} support + \end{itemize} +} + +\section{Preparation} + +After doing a Google search, find the {\em nInvaders} website and +download its source code. Analyze its build system, and conclude which +Buildroot package infrastructure is the most appropriate to create a +package for {\em nInvaders}. + +\section{Minimal package} + +Create a directory for the package in the Buildroot sources, +\code{package/ninvaders}. Create a \code{Config.in} file with one +option to enable this package, and a minimal \code{ninvaders.mk} file +that specifies what is needed just to {\em download} the package. + +For reference, the download URL of the {\em nInvaders} tarball is +\url{https://sourceforge.net/projects/ninvaders/files/ninvaders/0.1.1/}. + +Note: to achieve this, only two variables need to be defined in +\code{.mk} file, plus the call to the appropriate package +infrastructure macro. + +Now, go to \code{menuconfig}, enable {\em nInvaders}, and run +\code{make}. You should see the {\em nInvaders} tarball being +downloaded and extracted. Look in \code{output/build/} to see if it +was properly extracted as expected. + +\section{Make it build!} + +As you have seen in the previous steps, {\em nInvaders} uses a simple +\code{Makefile} for its build process. So you'll have to define the +{\em build commands} variable to trigger the build of {\em + nInvaders}. To do this, you will have to use four variables provided +by Buildroot: + +\begin{itemize} + +\item \code{TARGET_MAKE_ENV}, which should be passed in the + environment when calling \code{make}. + +\item \code{MAKE}, which contains the proper name of the \code{make} + tool with potentially some additional parameters to parallelize the + build. + +\item \code{TARGET_CONFIGURE_OPTS}, which contains the definition of + many variables often used by \code{Makefiles}: \code{CC}, + \code{CFLAGS}, \code{LDFLAGS}, etc. + +\item \code{@D}, which contains the path to the directory where the + {\em nInvaders} source code was extracted. + +\end{itemize} + +When doing Buildroot packages, it is often a good idea to look at how +other packages are doing things. Look for example at the \code{jhead} +package, which is going to be fairly similar to our \code{ninvaders} +package. + +Once you have written the {\em nInvaders} build step, it's time to +test it. However, if you just run \code{make} to start the Buildroot +build, the \code{ninvaders} package will not be rebuilt, because it +has already been built. + +So, let's force Buildroot to rebuild the package by removing its +source directory completely: + +\begin{bashinput} +make ninvaders-dirclean +\end{bashinput} + +And then starting the build: + +\begin{bashinput} +make +\end{bashinput} + +This time, you should see the \code{ninvaders 0.1.1 Building} step +actually doing something, but quickly failing with a message saying +that the \code{ncurses.h} file could not be found. + +Move on to the next section to see how to solve this problem! + +\section{Handling dependencies} + +The \code{ncurses.h} header file is missing, because {\em nInvaders} +depends on the \code{ncurses} library for drawing its interface on a +text-based terminal. So we need to add \code{ncurses} in the +dependencies of {\em nInvaders}. To do this, you need to do two +things: + +\begin{itemize} + +\item Express the dependency in the package \code{Config.in} file. Use + a \code{select} statement to make sure the \code{ncurses} package + option is automatically selected when \code{ninvaders} is + enabled. Check that the \code{ncurses} package does not have itself + some dependencies that need to be propagated up to the + \code{ninvaders} package. + +\item Express the dependency in the package \code{.mk} file. + +\end{itemize} + +Restart again the build of the package by using +\code{make ninvaders-dirclean all} (which is the same as doing +\code{make ninvaders-dirclean} followed by \code{make}). + +Now the package build fails at link time with messages such as +\code{multiple definition of `skill_level'; aliens.o:(.bss+0x674): + first defined here}. + +\section{Customizing CFLAGS} + +The \code{multiple definition} issue is due to the code base of {\em + nInvaders} being quite old, and having multiple compilation units +redefine the same symbols. While this was accepted by older {\em gcc} +versions, since {\em gcc 10} this is no longer accepted by default. + +While we could fix the {\em nInvaders} code base, we will take a +different route: ask {\em gcc} to behave as it did before {\em gcc 10} +and accept such redefinitions. This can be done by passing the +\code{-fcommon} {\em gcc} flag. + +To achieve this, make sure that \code{CFLAGS} is set to +\code{$(TARGET_CFLAGS) -fcommon} in \code{NINVADERS_BUILD_CMDS}. + +Restart the build with \code{make ninvaders-dirclean all}. + +Now the package should build properly! If you look in +\code{output/build/ninvaders-0.1.1/}, you should see a +\code{nInvaders} binary file. Run the \code{file} program with +\code{nInvaders} as argument to verify that it is indeed built for +AArch64. + +However, while \code{nInvaders} has been successfully compiled, it is +not installed in our target root filesystem! + +\section{Installing and testing the program} + +If you study the {\em nInvaders} \code{Makefile}, you can see that +there is no provision for installing the program: there is no +\code{install:} rule. + +So, in \code{ninvaders.mk}, you will have to create the {\em target + installation commands}, and simply manually install the +\code{nInvaders} binary. Use the \code{$(INSTALL)} variable for +that. Again, take example on the \code{jhead} package to know how to +achieve that. + +Rebuild once again the \code{ninvaders} package. This time, you should +see the \code{nInvaders} binary in \code{output/target/usr/bin/}! + +Reflash your root filesystem on the SD card and reboot the +system. {\em nInvaders} will not work very well over the serial port, +so log to your system through \code{ssh}, and play \code{nInvaders} +with the keyboard! + +Note: if you get the error \code{Error opening terminal: + xterm-256color.} when running \code{nInvaders}, issue first the +command \code{export TERM=xterm}. + +\section{Support the Nunchuk} + +Playing with the keyboard is nice, but playing with our Nunchuk would +be even nicer! We have written a patch for {\em nInvaders} that makes +this possible. + +This patch is available in the lab data directory, under the name +\code{0001-joystick-support.patch}. Copy this patch to the right +location so that it gets applied after {\em nInvaders} is extracted by +Buildroot, and before it is built. Rebuild once again the +\code{ninvaders} package. Verify that the patch gets applied at the +\code{ninvaders 0.1.1 Patching} step. + +However, this patch relies on the Linux kernel {\em joystick + interface}, that we need to enable. Go to the Linux kernel +configuration using \code{make linux-menuconfig}, and enable +\kconfig{CONFIG_INPUT_JOYDEV}. Exit, and make sure to save your kernel +configuration safely using \code{make linux-update-defconfig}. Restart +the overall build by running \code{make}. + +Then reflash your kernel image and root filesystem on the SD card, +reboot, and start {\em nInvaders} in a SSH session. You should now be +able to control it using the Nunchuk joystick, and fire with the +\code{C} button. + +\section{Adding a hash file} + +To finalize the package, add the missing {\em hash file}, so that +people building this package can be sure they are building the same +source code. To know the hash, SourceForge provides this information: +go to the {\em nInvaders} download page, and next to the file name, +there is a small information icon that will provide the MD5 and SHA1 +hashes. Add both hashes to the hash file. + +\begin{center} +\includegraphics[width=\textwidth]{labs/buildroot-new-packages/hashes-on-sourceforge.pdf} +\end{center} + +Once the {\em hash file} is added, rebuild the package completely by +doing \code{make ninvaders-dirclean all}. + +Look at the build output, and before the \code{ninvaders 0.1.1 + Extracting} step, you should see a message like this: + +\begin{verbatim} +ninvaders-0.1.1.tar.gz: OK (sha1: ....) +ninvaders-0.1.1.tar.gz: OK (md5: ....) +\end{verbatim} + +\section{Testing package removal} + +Now, to experiment with Buildroot, do the following test: disable the +\code{ninvaders} package in \code{menuconfig} and restart the build +doing \code{make}. Once the build is done (which should be very +quick), looked in \code{output/target/}. Is {\em nInvaders} still +installed? If so, why? + +\section{Sanity checking your package} + +If you want to verify if your package matches the coding style rules +of Buildroot, you can run: + +\begin{bashinput} +make check-package +\end{bashinput} + +While a successful result doesn't mean your package is perfect, it at +least verifies a number of basic requirements. diff --git a/labs/buildroot-rootfs-beagleplay/buildroot-rootfs-beagleplay.tex b/labs/buildroot-rootfs-beagleplay/buildroot-rootfs-beagleplay.tex new file mode 100644 index 0000000000..cd8dc69028 --- /dev/null +++ b/labs/buildroot-rootfs-beagleplay/buildroot-rootfs-beagleplay.tex @@ -0,0 +1,424 @@ +\subchapter +{Root filesystem construction} +{Objectives: + \begin{itemize} + \item Explore the build output + \item Customize the root filesystem using a {\em rootfs overlay} + \item Customize the Linux kernel configuration + \item Use a post-build script + \item Customize the kernel with patches + \item Add more packages + \item Use {\em defconfig} files and {\em out of tree} build + \end{itemize} +} + +\section{Explore the build output} + +Now that we have discussed during the lectures the organization of the +Buildroot {\em output} tree, take some time to look inside +\code{output/} for the different build artefacts. And especially: + +\begin{itemize} + +\item Identify where the cross-compiler has been installed. + +\item Identify where the source code for the different components has + been extracted, and which packages have been built. + +\item Identify where the target root filesystem has been created, and + read the \code{THIS_IS_NOT_YOUR_ROOT_FILESYSTEM} file. + +\item See where the \code{staging} symbolic link is pointing to. + +\end{itemize} + +\section{Use a {\em rootfs overlay} to configure the network} + +The BeaglePlay has an Ethernet port, so we can use a wired network +connection between our embedded system and the development PC. To +achieve this we will need to add a network configuration file that +sets up the Ethernet interface with the appropriate IP address. + +\subsection{IP address configuration} + +By default, Buildroot uses the \code{ifup} program from BusyBox, which +reads the \code{/etc/network/interfaces} file to configure network +interfaces. + +Since this configuration is specific to our project, we will create a +custom directory for our project within the Buildroot sources: +\code{board/bootlin/beagleplay/}. + +Within this directory, create a \code{rootfs-overlay} directory, and +in \code{menuconfig}, specify +\code{board/bootlin/beagleplay/rootfs-overlay} as the {\em rootfs + overlay} (option \code{BR2_ROOTFS_OVERLAY}). + +In \code{board/bootlin/beagleplay/rootfs-overlay}, create a file named +\code{etc/network/interfaces} with the following contents: + +\begin{fileinput} +auto lo +iface lo inet loopback + +auto eth0 +iface eth0 inet static + address 192.168.0.100 + netmask 255.255.255.0 +\end{fileinput} + +Then, rebuild your system by running \code{make}. Here as well, we +don't need to do a full rebuild, since the {\em rootfs overlays} are +applied at the end of each build. You can check in +\code{output/target/etc/network/} if the network configuration file +was properly copied. + +Reflash the root filesystem on the SD card, and boot your BeaglePlay. +It should now have an IP address configured for \code{eth0} by default. + +\section{Configure the network on your host} + +In the next sections of this lab, we will want to interact with the +BeaglePlay over the network. So in this section, we'll configure your +host machine appropriately. + +With a network cable, connect the Ethernet port of your board to the +one of your computer. If your computer already has a wired connection +to the network, your instructor will provide you with a USB Ethernet +adapter. A new network interface should appear on your Linux system. + +Find the name of this interface by typing: +\bashcmd{$ ip a} + +The network interface name is likely to be +\code{enxxx}\footnote{Following the {\em Predictable Network Interface +Names} convention: +\url{https://www.freedesktop.org/wiki/Software/systemd/PredictableNetworkInterfaceNames/}}. +If you have a pluggable Ethernet device, it's easy to identify as it's +the one that shows up after plugging in the device. + +Then configure the host IP address using NetworkManager's command line +interface: + +\begin{bashinput} +nmcli con add type ethernet ifname en... ip4 192.168.0.1/24 +\end{bashinput} + +{\em Note: using \code{ip} in the command line is not +recommended, because Network Manager will unconfigure and +reconfigure the network interface each time the board is rebooted.} + +Once this is done, make sure you can communicate with your target +using \code{ping}. + +\section{Add {\em dropbear} as an SSH server} + +As a first additional package to add to our system, let's add the {\em +dropbear} SSH client/server. The server will be running on the +BeaglePlay, which will allow us to connect over the network to the +BeaglePlay. + +Run \code{make menuconfig}, and enable the \code{dropbear} +package. You can use the search capability of \code{menuconfig} by +typing \code{/}, enter \code{DROPBEAR}. It will give you a list of +results, and each result is associated with a number between +parenthesis, like \code{(1)}. Then simply press \code{1}, and +\code{menuconfig} will jump to the right option. + +After leaving \code{menuconfig}, restart the build by running +\code{make}. + +In this case, we do not need to do a full rebuild, because a simple +\code{make} will notice that the \code{dropbear} package has not been +built, and will therefore trigger the build process. + +Re-extract the root filesystem tarball in the \code{rootfs} partition +of the SD card. Don't forget to replace the entire root filesystem: + +\begin{bashinput} +rm -rf /media/$USER/rootfs/* +sudo tar -C /media/$USER/rootfs/ -xf output/images/rootfs.tar +\end{bashinput} + +Now, boot the new system on the BeaglePlay. You should see a message: + +\begin{verbatim} +Starting dropbear sshd: OK +\end{verbatim} + +Now, from your PC, you can SSH to the board by doing: + +\begin{bashinput} +ssh root@192.168.0.100 +\end{bashinput} + +\section{Use a post-build script} + +Write a shell script that creates a file named \code{/etc/build-id} in +the root filesystem, containing the Git commit id of the Buildroot +sources, as well as the current date. Since this script will be +executed as a post-build script, remember that the first argument +passed to the script is \code{$(TARGET_DIR)}. + +Register this script as a post-build script in your Buildroot +configuration, run a build, and verify that \code{/etc/build-id} is +created as expected. + +\section{Patch the Linux kernel} + +Now, we would like to connect an additional peripheral to our system: +the {\em Wii Nunchuk}. Using this custom peripheral requires adding a +new driver to the Linux kernel, making changes to the Device Tree +describing the hardware, and changing the kernel configuration. This +is the purpose of this section. + +We will first create a new directory to store our kernel patches. It +will sit next to our {\em rootfs overlay} in our project-specific +directory: + +\begin{bashinput} +mkdir board/bootlin/beagleplay/patches/linux/ +\end{bashinput} + +Copy in this directory the two patches that we provided with the data +of this lab, in \code{$HOME/__SESSION_NAME__-labs/buildroot-rootfs/linux/}: + +\begin{bashinput} +cp $HOME/__SESSION_NAME__-labs/buildroot-rootfs/linux/*.patch \ + board/bootlin/beagleplay/patches/linux/ +\end{bashinput} + +The first patch adds the Nunchuk driver, the second patch adjusts the +Device Tree to enable I2C3 (on the mikroBUS connector) and declare the +Nunchuk device on this bus. Feel free to look at them. If you're +interested, you can look at our training course {\em Embedded Linux +kernel driver development}, which precisely covers the development of +this driver. + +Now, we need to tell Buildroot to apply these patches before building +the kernel. To do so, run \code{menuconfig}, go the to the {\em Build + options} menu, and adjust the \code{Global patch directories} option +to \code{board/bootlin/beagleplay/patches/}. + +Let's now clean up completely the \code{linux} package so that its +sources will be re-extracted and our patches applied the next time we +do a build: + +\begin{bashinput} +make linux-dirclean +\end{bashinput} + +If you check in \code{output/build/}, the \code{linux-} +directory will have disappeared. + +Now, we need to adjust our kernel configuration to enable the {\em Wii +Nunchuk} driver. To start the Linux kernel configuration tool, run: + +\begin{bashinput} +make linux-menuconfig +\end{bashinput} + +This will: + +\begin{itemize} +\item Extract the Linux kernel sources +\item Apply our two patches +\item Load the defined kernel configuration, from \code{defconfig} +\item Start the kernel \code{menuconfig} tool +\end{itemize} + +Once in the kernel \code{menuconfig}, enable the option +\kconfig{CONFIG_JOYSTICK_WIICHUCK}, and make sure it is enabled +statically. Also, make sure the \kconfig{CONFIG_INPUT_EVDEV} option is +enabled statically (by default it is enabled as a module). Once those +options are set, leave the kernel \code{menuconfig}. + +Your kernel configuration has now been customized, but those changes +are only saved in \code{output/build/linux-/.config}, which +will be deleted at the next \code{make clean}. So we need to save such +changes persistently. To do so: + +\begin{enumerate} + +\item Run Buildroot \code{menuconfig} + +\item In the \code{Kernel} menu, instead of \code{Using a defconfig}, + chose \code{Using a custom config file}. This will allow us to use + our own custom kernel configuration file, instead of a pre-defined + {\em defconfig} that comes with the kernel sources. + +\item In the \code{Configuration file path}, enter + \code{board/bootlin/beagleplay/linux.config}. + +\item Exit \code{menuconfig} + +\item Run \inlinebash{make linux-update-defconfig}. This will generate + the configuration file in + \code{board/bootlin/beagleplay/linux.config}. It will be a {\em + minimal} configuration file (i.e. a {\em defconfig}). In this + file, verify that the option \kconfig{CONFIG_JOYSTICK_WIICHUCK} is + properly set to \code{y}. + +\end{enumerate} + +You can now restart the build of the kernel: + +\begin{bashinput} +make +\end{bashinput} + +It should hopefully end successfully, and if you look closely at the +build log, you should see the file \code{wiichuck.c} being compiled. + +\section{Connect the Wii Nunchuk} + +Take the nunchuk device provided by your instructor. + +We will connect it to the I2C3 bus of the CPU, which is accessible on +the mikroBUS connector of the BeaglePlay. + +Identify the 4 pins of the nunchuk connector: + +\begin{center} +\includegraphics[width=4cm]{common/nunchuk-pinout.pdf} +\end{center} + +Connect the nunchuk pins to the mikroBUS connector: +\begin{itemize} +\item The \code{GND} pin to \code{GND} on the mikroBUS connector +\item The \code{PWR} pin to \code{+3.3V} on the mikroBUS connector +\item The \code{CLK} pin to \code{SCL} on the mikroBUS connector +\item The \code{DATA} pin to \code{SDA} on the mikroBUS connector +\end{itemize} + +\begin{center} +\includegraphics[width=12cm]{common/beagleplay-connect-nunchuk.jpg} +\end{center} + +\section{Test the {\em nunchuk}} + +Reflash your system, both the {\em Device Tree}, Linux kernel image +and root filesystem, and boot it. + +In the kernel boot log, you should see a message like: + +\begin{verbatim} +input: Wiichuck expansion connector as /devices/platform/bus@f0000/20030000.i2c/i2c-3/3-0052/input/input0 +\end{verbatim} + +You can also explore {\em sysfs}, and see that your Nunchuk device is +handled by the system: + +\begin{bashinput} +cat /sys/bus/i2c/devices/3-0052/name +\end{bashinput} + +Now, to get the raw events coming from the Nunchuk, you can do: + +\begin{bashinput} +cat /dev/input/event0 +\end{bashinput} + +or, if you prefer to see hexadecimal values instead of raw binary: + +\begin{bashinput} +cat /dev/input/event0 | hexdump -C +\end{bashinput} + +You should see events when moving the Nunchuk (it has an +accelerometer), when moving the joystick and pushing the buttons. + +\section{Add and use {\em evtest}} + +Since the raw events from the Nunchuk are not very convenient to read, +let's install an application that will decode the raw input events +and display them in a more human readable format: \code{evtest}. + +Enable this package in Buildroot, restart the build, reflash the root +filesystem and reboot the system. Now you can use \code{evtest}: + +\begin{bashinput} +evtest /dev/input/event0 +\end{bashinput} + +\section{Generate a {\em defconfig}} + +Now that our system is already in a good shape, let's make sure its +configuration is properly saved and cannot be lost. Go in +\code{menuconfig}, and in the \code{Build options} menu. There is an +option called \code{Location to save buildroot config} which indicates +where Buildroot will save the {\em defconfig} file generated by +\code{make savedefconfig}. Adjust this value to +\code{$(TOPDIR)/configs/bootlin_defconfig}. + +Then, exit \code{menuconfig}, and run: + +\begin{bashinput} +make savedefconfig +\end{bashinput} + +Read the file \code{configs/bootlin_defconfig} generated in the +Buildroot sources. You will see the values for all the options for +which we selected a value different from the default. So it's a very +good summary of what our system is. + +Identify the options related to the following aspects of the system: + +\begin{itemize} +\item The architecture specification +\item The toolchain definition +\item The system configuration +\item The Linux kernel related configuration +\item The selection of packages +\item The U-Boot related configuration +\end{itemize} + +\section{Testing a full rebuild} + +To make sure that we are able to rebuild our system completely, we'll +start a build from scratch. And to learn something new, we'll use {\em + out of tree} build. + +To do so, create a build directory anywhere you want, and move inside +this directory: + +\begin{bashinput} +mkdir ~/bootlin/buildroot-build/ +cd ~/bootlin/buildroot-build/ +\end{bashinput} + +Now, we will load the \code{bootlin_defconfig}: + +\begin{bashinput} +make -C ~/bootlin/buildroot/ O=$(pwd) bootlin_defconfig +\end{bashinput} + +Let's explain a little bit what happens here. By using +\code{-C ~/bootlin/buildroot/}, we in fact tell \code{make} that the +\code{Makefile} to analyze is not in the current directory, but in the +directory passed as the \code{-C} argument. By passing \code{O=}, we +tell Buildroot where all the output should go: by default it goes in +\code{output/} inside the Buildroot sources, but here we override that +with the current directory (\code{$(pwd)}). + +This command will have two main effects: + +\begin{enumerate} + +\item It will load the \code{bootlin_defconfig} as the current + configuration. After running the command, read the file named + \code{.config}. It's much longer than the {\em defconfig}, because + it contains the values for all options. + +\item It will create a minimal \code{Makefile} in this output + directory, which will allow us to avoid doing the \code{make -C + ... O=...} dance each time. + +\end{enumerate} + +Now that this is done, start the build. You can again save the build +log: + +\begin{bashinput} +make 2>&1 | tee build.log +\end{bashinput} diff --git a/mk/buildroot.mk b/mk/buildroot.mk index 40381066c0..ced3516f42 100644 --- a/mk/buildroot.mk +++ b/mk/buildroot.mk @@ -3,6 +3,7 @@ BUILDROOT_SLIDES = \ about-us \ course-information-title \ shopping-list-beaglebone \ + shopping-list-beagleplay \ shopping-list-stm32mp157 \ course-information \ setup-lab \ @@ -42,3 +43,12 @@ BUILDROOT_STM32MP1_LABS = \ buildroot-advanced-packages \ buildroot-advanced \ buildroot-appdev + +BUILDROOT_BEAGLEPLAY_LABS = \ + setup \ + buildroot-basic-beagleplay \ + buildroot-rootfs-beagleplay \ + buildroot-new-packages-beagleplay \ + buildroot-advanced-packages-beagleplay \ + buildroot-advanced-beagleplay \ + buildroot-appdev-beagleplay diff --git a/slides/shopping-list-beagleplay/shopping-list-beagleplay.tex b/slides/shopping-list-beagleplay/shopping-list-beagleplay.tex index 905cb70b0f..b0c2811d46 100644 --- a/slides/shopping-list-beagleplay/shopping-list-beagleplay.tex +++ b/slides/shopping-list-beagleplay/shopping-list-beagleplay.tex @@ -3,6 +3,7 @@ \ifdefstring{\training}{yocto}{\settoggle{beagleplay-nunchuk}{true}}{} \ifdefstring{\training}{embedded-linux}{\settoggle{beagleplay-nunchuk}{true}}{} \ifdefstring{\training}{linux-kernel}{\settoggle{beagleplay-nunchuk}{true}}{} +\ifdefstring{\training}{buildroot}{\settoggle{beagleplay-nunchuk}{true}}{} \newtoggle{beagleplay-audio} \settoggle{beagleplay-audio}{false} diff --git a/slides/shopping-list-beagleplay/shopping-list-beagleplay.typ b/slides/shopping-list-beagleplay/shopping-list-beagleplay.typ index 52eae2ec7d..b5f72f3e00 100644 --- a/slides/shopping-list-beagleplay/shopping-list-beagleplay.typ +++ b/slides/shopping-list-beagleplay/shopping-list-beagleplay.typ @@ -1,7 +1,7 @@ #let training = sys.inputs.at("training", default: "") #let beagleplay-nunchuk = ( - training in ("yocto", "embedded-linux", "linux-kernel") + training in ("yocto", "embedded-linux", "linux-kernel", "buildroot") ) #let beagleplay-audio = (