This chapter shows how to write a "hello world" driver on the Pico-G1 board, expose it as a user-space interface, and call it from an application — the complete path from writing the driver to building and calling it. It builds on Deploying Your First Application; read that chapter first.
Driver execution flow:
hellodrv_initruns when the module is loadedmisc_register()registers the device- The kernel creates
/dev/hellodrvautomatically- A user-space program opens
/dev/hellodrv- User space sends commands to the driver with
ioctl- The driver returns data to user space with
copy_to_user()hellodrv_exitruns when the module is unloadedmisc_deregister()unregisters the device
Following Deploying Your First Application, change helloworld.c to the version below, then build and flash as described there.
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#define HELLODRV_MAGIC 'H'
#define HELLODRV_GET_MSG _IOR(HELLODRV_MAGIC, 0x01, char *)
int main(void)
{
int fd;
char buf[64] = {0};
printf("hello world from firmware!\n");
fd = open("/dev/hellodrv", O_RDWR);
if (fd < 0) {
perror("open /dev/hellodrv failed");
return -1;
}
if (ioctl(fd, HELLODRV_GET_MSG, buf) < 0) {
perror("ioctl HELLODRV_GET_MSG failed");
close(fd);
return -1;
}
printf("message from driver: %s\n", buf);
close(fd);
return 0;
}
# Run from the SDK root:
cd source/kernel/linux-5.10.y/drivers/misc
mkdir hellodrv
cd hellodrv
touch hellodrv.c
touch Makefile
touch Kconfig
Note
linux-5.10.ycomes fromLinux System --> Kernel --> Kernel Versionin menuconfig. If 4.9 is configured, uselinux-4.9.yinstead.- The misc driver type is the Linux "miscellaneous device driver", intended for small drivers that do not belong to a specific subsystem but still need to expose a
/devinterface. It is simpler than a full character device driver and is well suited to learning and prototyping.
- Register a device in the kernel
- Create a device node on the board, such as
/dev/hellodrv- Let the user-space program
helloworldtalk to the driver through that node- Implement a minimal feature first: read a fixed string from the driver with ioctl
ioctl
ioctl stands for I/O control — an input/output control interface. It is one of the ways a user-space program communicates with a driver in Linux.
#include <linux/module.h>
#include <linux/init.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/ioctl.h>
#include <linux/device.h>
/*
* Driver name.
* The device node created under /dev will be /dev/hellodrv
*/
#define HELLODRV_NAME "hellodrv"
/*
* ioctl magic number.
* Distinguishes ioctl commands of different drivers to avoid conflicts.
*/
#define HELLODRV_MAGIC 'H'
/*
* ioctl command: fetch a string from the driver.
* _IOR means user space reads data from the kernel.
* The third argument is the data type, here char *.
*/
#define HELLODRV_GET_MSG _IOR(HELLODRV_MAGIC, 0x01, char *)
/*
* The string returned to user space.
* A fixed message is enough to verify that user space and kernel space communicate.
*/
static const char *g_msg = "hello from driver";
/*
* Device open callback.
* Entered when user space calls open("/dev/hellodrv", ...).
*/
static int hellodrv_open(struct inode *inode, struct file *filp)
{
pr_info("hellodrv: device opened\n");
return 0;
}
/*
* Device release callback.
* Entered when user space calls close(fd).
*/
static int hellodrv_release(struct inode *inode, struct file *filp)
{
pr_info("hellodrv: device closed\n");
return 0;
}
/*
* ioctl callback.
* A user-space program interacts with the driver through ioctl(fd, cmd, arg).
*
* cmd: command passed from user space
* arg: argument passed from user space, usually a user-space address
*/
static long hellodrv_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
char msg[64];
switch (cmd) {
case HELLODRV_GET_MSG:
/*
* Clear the temporary buffer first.
*/
memset(msg, 0, sizeof(msg));
/*
* Copy the fixed string into the local buffer.
*/
snprintf(msg, sizeof(msg), "%s", g_msg);
/*
* Copy the data from kernel space to user space.
* Never pass a kernel address directly to user space; use copy_to_user.
*/
if (copy_to_user((void __user *)arg, msg, strlen(msg) + 1))
return -EFAULT;
pr_info("hellodrv: ioctl GET_MSG\n");
return 0;
default:
/*
* Unknown command: return an invalid-argument error.
*/
return -EINVAL;
}
}
/*
* File operations table.
* The kernel uses it to know which operations this device supports.
*/
static const struct file_operations hellodrv_fops = {
.owner = THIS_MODULE,
.open = hellodrv_open,
.release = hellodrv_release,
.unlocked_ioctl = hellodrv_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = hellodrv_ioctl,
#endif
};
/*
* misc device structure.
* After misc_register, /dev/hellodrv is created automatically.
*/
static struct miscdevice hellodrv_miscdev = {
.minor = MISC_DYNAMIC_MINOR, /* allocate the minor number dynamically */
.name = HELLODRV_NAME, /* device node name */
.fops = &hellodrv_fops, /* bind the file operations */
.mode = 0666, /* device node permissions: read and write */
};
/*
* Module init function.
* Runs when the driver is loaded.
*/
static int __init hellodrv_init(void)
{
int ret;
ret = misc_register(&hellodrv_miscdev);
if (ret) {
pr_err("hellodrv: misc_register failed, ret=%d\n", ret);
return ret;
}
pr_info("hellodrv: module loaded, /dev/%s created\n", HELLODRV_NAME);
return 0;
}
/*
* Module exit function.
* Runs when the driver is unloaded.
*/
static void __exit hellodrv_exit(void)
{
misc_deregister(&hellodrv_miscdev);
pr_info("hellodrv: module unloaded\n");
}
/*
* Declare the module entry and exit points.
*/
module_init(hellodrv_init);
module_exit(hellodrv_exit);
/*
* Module information.
*/
MODULE_LICENSE("GPL");
MODULE_AUTHOR("ljh");
MODULE_DESCRIPTION("Hello driver for helloworld app");
obj-$(CONFIG_HELLODRV) += hellodrv.o
config HELLODRV
tristate "Hello driver"
help
A simple misc driver for user-space test.
CONFIG_HELLODRV is enabled, descend into hellodrv/ and build it.obj-$(CONFIG_HELLODRV) += hellodrv/
drivers/misc/hellodrv/Kconfig. source "drivers/misc/hellodrv/Kconfig"
source/kernel/linux-5.10.y/arch/arm/configs/xmorca_defconfig and append CONFIG_HELLODRV=m at the end. Verify it was applied:grep CONFIG_HELLODRV source/kernel/linux-5.10.y/arch/arm/configs/xmorca_defconfig
make linux
grep CONFIG_HELLODRV out/xm7206v12a/linux-5.10.y/.config
find out/xm7206v12a/linux-5.10.y -name "hellodrv.ko"
cd out/xm7206v12a/rootfs/lib/
mkdir -p modules/5.10.0/extr
# back in the SDK root:
cp -f out/xm7206v12a/linux-5.10.y/drivers/misc/hellodrv/hellodrv.ko out/xm7206v12a/rootfs/lib/modules/5.10.0/extr/
# confirm the copy
ls -l out/xm7206v12a/rootfs/lib/modules/5.10.0/extr/hellodrv.ko
# repackage the image
make fs_image
insmod lib/modules/5.10.0/extr/hellodrv.ko
/usr/bin/helloworld
/ # insmod lib/modules/5.10.0/extr/hellodrv.ko
hellodrv: module loaded, /dev/hellodrv created
/ # /usr/bin/helloworld
hello world from firmware!
hellodrv: device opened
hellodrv: ioctl GET_MSG
message from driver: hello from driver
hellodrv: device closed
/ #