Tangaoo an embedded software engineer

树莓派驱动编程

2021-08-05
tangoo

在树莓派平台进行 Linux kernel 驱动编程指引。

1. 前置环境

1.1. 开发环境

  • raspberry pi 4
  • kernel version: 4.19.71-rt24-v7l+
  • wireless connect: ssh pi@192.168.31.25

1.2. 安装编译依赖

编译驱动需要依赖内核树,有两种方法:

  • 安装内核头文件,编译时引用该头文件目录
sudo apt-get install raspberrypi-kernel-headers
  • 下载对应内核源码,编译时引用该内核文件目录。(下载内核源码版本一定要与目标平台一致)

1.3. 编译方式

均在 raspberry pi 本地编译,为使用交叉编译。如要交叉编译开发可在 raspberry 处下载交叉编译工具。但是不支持 MAC。

2. Hello World

直接贴代码,

  • hello.c
#include <linux/init.h>
#include <linux/module.h>

MODULE_LICENSE("Dual BSD/GPL");

static int hello_init(void)
{
	printk(KERN_ALERT"Hello World enter\n");
	return 0;
}
static void hello_exit(void)
{
	printk(KERN_ALERT"Hello World exit\n");
}

module_init(hello_init);
module_exit(hello_exit);
MODULE_AUTHOR("Song YanNa");
MODULE_DESCRIPTION("A Sample Hello World Module");
MODULE_ALIAS("A Sample module");

注意 kernel 里面不支持 libc 库,那么意味着在用户空间里的 libc 的头文件,接口调用都无效。

  • Makefile
obj-m := hello.o

CURRENT_PATH := $(shell pwd)
# LINUX_KERNEL := $(shell uname -r)
# LINUX_KERNEL_PATH := /usr/src/linux-headers-$(LINUX_KERNEL)
LINUX_KERNEL_PATH := /home/pi/linux-rpi-4.19.y-rt

all:
	$(MAKE) -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules
clean:
	$(MAKE) -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) clean

注意 编码 module 引用的内核树依赖必须与运行环境中一致。

3. 应用层与驱动层如何关联

  • 驱动代码 dev_nr.c
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>

MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("tango");
MODULE_ALIAS("A Sample module");
MODULE_DESCRIPTION("Register a device nr, and implement some callback function");

static int dev_nr_open(struct inode* device_file, struct file* inst)
{
	printk("dev_nr, open was callback \n");
	return 0;
}

static int dev_nr_close(struct inode* device_file, struct file* inst)
{
	printk("dev_nr, close was callback \n");
	return 0;
}

static struct file_operations fops = {
	.owner		= THIS_MODULE,
	.open		= dev_nr_open,
	.release    = dev_nr_close,
};

#define MYMAJOR 90

static int __init dev_nr_init(void)
{
	int ret;
	printk("kernel enter\n");

	// register device nr
	ret = register_chrdev(MYMAJOR, "tango_nr", &fops);
	if (ret == 0) printk("dev_nr, register major: %d, minor: %d \n", MYMAJOR, 0);
	else if (ret > 0) printk("dev_nr, register major: %d, minor: %d \n", ret>>20, ret&0xFFFFF);
	else
	{
		printk("register failed \n");
		return -1;
	}

	return 0;
}
static void __exit dev_nr_exit(void)
{
	unregister_chrdev(MYMAJOR, "tango_nr");
	printk("exit kernel\n");
}

module_init(dev_nr_init);
module_exit(dev_nr_exit);

驱动程序会注册有唯一的 Major-Minor 号(示例程序是 90 0,且要保证该号没有被系统中其他驱动占用,cat /proc/devices 可以查询)。然后再创建节点并关联此 Major-Minor 号。sudo mknod /dev/tango_nr c 90 0。最后应用程序打开该设备节点就可以关联上驱动了。这是手动模式,当然也可以在驱动初始化时自动分配 Major-Minor 号和创建设备节点。

应用程序代码 app_nr.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>

int main (void)
{
	int dev = open("/dev/tango_nr", O_RDONLY);
	if (dev == -1)
	{
		printf("open failed \n");
		return -1;
	}

	printf("open success\n");

	close(dev);
}

上一篇 开始用 Emacs

下一篇 LDD3 读书笔记

Comments

Content