linux下编写一个简单的ko

编译内核文件.ko

1、操作系统:linux

2、编译使用的文件

# ls
hello.c  helloworld.h  Makefile  world.c

最简单的编译ko只需要一个.c文件和一个Makefile即可

3、文件详情

首先看Makefile

obj-m :=start.o
start-objs := hello.o world.o
LINUX_KERNEL := $(shell uname -r)
LINUX_KERNEL_PATH :=/usr/src/linux-headers-${LINUX_KERNEL}/
PWD :=$(shell pwd)  
modules :  
        $(MAKE) -C $(LINUX_KERNEL_PATH) M=$(PWD) modules  
.PHONEY:clean  
clean :  
        rm -f *.o *.ko *.mod.c 

分析

obj-m := ///这个后面即是最后生成的**.ko,此处为start.ko。可以并列多个。
**-objs := ///**和你上面的ko名字对应,后面的为生成这个ko所需的.o文件(由同名的.c文件编译)
LINUX_KERNEL := $(shell uname -r) ///uname -r 查看内核版本 
LINUX_KERNEL_PATH :=/usr/src/linux-headers-${LINUX_KERNEL}/ ///这个即为内核版本文件夹路径
PWD :=$(shell pwd)  ///pwd 当前目录
modules :  
        $(MAKE) -C $(LINUX_KERNEL_PATH) M=$(PWD) modules  
.PHONEY:clean  
clean :   /// make clean 时删除 .o .ko .mod.c
        rm -f *.o *.ko *.mod.c 

最重要的.c文件
hello.c

#include <linux/init.h>  
#include <linux/module.h>  
#include "helloworld.h"
 
MODULE_LICENSE("GPL");  
static int hello_init(void)  
{  
  printk("999 Hello, world\n");  
  test_world();
  return 0;  
}  
static void hello_exit(void)  
{  
  printk("Goodbye, cruel world\n");  
}  
  
module_init(hello_init);  
module_exit(hello_exit);

分析

#include <linux/init.h>  ///这两个为内核编译必备头文件
#include <linux/module.h>  
#include "helloworld.h"
 
MODULE_LICENSE("GPL");  ///module证书?
static int hello_init(void)  ///加载ko,insmod
{  
  printk("999 Hello, world\n");  
  test_world();
  return 0;  
}  
static void hello_exit(void)  ///卸载ko,rmmod
{  
  printk("Goodbye, cruel world\n");  
}  
  
module_init(hello_init); ///每个ko文件的最后都必须有module_init和module_exit 
module_exit(hello_exit);

配角文件
world.c

#include <linux/kernel.h>
void test_world(void){
printk("world---\n");
} 

helloworld.h

void test_world(void);

4、编译方法

输入make即可
正确编译的话应该会有以下文件,包含最终ko

# ls
hello.c  hello.o  helloworld.h  Makefile  modules.order  Module.symvers  start.ko  start.mod.c  start.mod.o  start.o  world.c  world.o

5、加载卸载ko

加载:insmod start.ko
卸载:rmmod start.ko
查看内核消息:dmesg

### 开发基础的 I2C 设备驱动程序 在 Linux 系统中开发一个基础的 I2C 设备驱动程序,主要涉及定义 `i2c_driver` 结构体、注册和注销驱动程序以及与设备进行通信。以下是详细的实现步骤。 --- #### 定义 `i2c_driver` 结构体 Linux 内核中的 I2C 子系统通过 `struct i2c_driver` 来定义设备驱动的核心操作函数,包括探测 (`probe`) 和移除 (`remove`) 回调函数: ```c struct i2c_driver { int (*probe)(struct i2c_client *client, const struct i2c_device_id *id); int (*remove)(struct i2c_client *client); struct device_driver driver; }; ``` 其中,`probe` 函数会在内核检测到匹配的设备时被调用,用于初始化设备;`remove` 函数则用于清理资源[^2]。 --- #### 注册 I2C 驱动程序 使用 `i2c_add_driver` 宏来注册 I2C 驱动程序: ```c #define i2c_add_driver(driver) i2c_register_driver(THIS_MODULE, driver) ``` 该宏内部调用了 `i2c_register_driver` 函数,并将模块信息传递给它。注册后,I2C 核心会尝试将驱动程序与总线上已有的设备进行匹配[^2]。 --- #### 注销 I2C 驱动程序 当需要卸载驱动程序时,使用 `i2c_del_driver` 函数进行注销: ```c void i2c_del_driver(struct i2c_driver *driver); ``` 该函数会从 I2C 总线中移除驱动程序,并释放相关资源。 --- #### 使用 `module_i2c_driver` 一键注册 为了简化注册和注销流程,可以使用 `module_i2c_driver` 宏,它会自动处理模块加载和卸载时的注册/注销操作: ```c module_i2c_driver(my_i2c_driver); ``` 其中 `my_i2c_driver` 是预先定义好的 `i2c_driver` 实例。 --- #### 示例代码:简单的 I2C 驱动程序 以下是一个完整的示例代码,展示如何编写一个基本的 I2C 设备驱动程序: ```c #include <linux/module.h> #include <linux/i2c.h> #include <linux/kernel.h> /* 设备ID表 */ static const struct i2c_device_id my_i2c_id[] = { { "my_i2c_device", 0 }, {} }; MODULE_DEVICE_TABLE(i2c, my_i2c_id); /* 探测函数 */ static int my_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { pr_info("I2C device probed\n"); return 0; } /* 移除函数 */ static int my_i2c_remove(struct i2c_client *client) { pr_info("I2C device removed\n"); return 0; } /* I2C驱动结构体 */ static struct i2c_driver my_i2c_driver = { .driver = { .name = "my_i2c_device", .owner = THIS_MODULE, }, .probe = my_i2c_probe, .remove = my_i2c_remove, .id_table = my_i2c_id, }; /* 一键注册驱动 */ module_i2c_driver(my_i2c_driver); MODULE_AUTHOR("Your Name"); MODULE_DESCRIPTION("Simple I2C Driver Example"); MODULE_LICENSE("GPL"); ``` --- #### 设备树配置(可选) 如果目标平台使用设备树 (Device Tree),还需要在 `.dts` 文件中添加相应的节点,以确保驱动程序能够正确匹配设备: ```dts i2c@1a4 { compatible = "my_i2c_device"; reg = <0x48>; }; ``` 其中 `compatible` 字段应与驱动程序中定义的 `name` 相匹配,而 `reg` 指定了设备的 I2C 地址。 --- #### 编译和加载驱动 编写完驱动程序后,可以通过如下方式编译并加载: 1. 创建 `Makefile`: ```makefile obj-m += my_i2c_driver.o all: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules clean: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean ``` 2. 编译模块: ```bash make ``` 3. 加载模块: ```bash insmod my_i2c_driver.ko ``` 4. 查看日志确认驱动是否成功加载: ```bash dmesg | grep "I2C" ``` ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值