实验目的
通过对 LED 驱动的移植,明白如何把驱动程序直接编译到内核中
实验环境准备
实验材料:GEC6818实验箱、电脑、OTG线
实验环境:VMware下Linux系统,windows系统、secureCRT平台
第一步:编写led驱动程序led.c及kconfig文件、config文件
- 找好实验箱的内核源码,并将其放到虚拟机中,并进入虚拟机下该内核源码目录,我这里是:/home/Hello/demo/6818GEC/kernel
cd /home/Hello/demo/6818GEC/kernel
- 接着进入drivers/char
Hello@University:~/demo/6818GEC/kernel$ cd drivers/char
- 在当前目录下新建led目录,并进入
mkdir led
cd led
- 编写Kconfig文件
config GEC6818_LED_DRIVER
bool "GEC6818 led driver"
default n
help
compile for leddriver,y for kernel,m for module.
- 编写Makefile文件
obj-$(CONFIG_GEC6818_LED_DRIVER) += led.o
- 编写led驱动程序led.c
/*---------------------------------------
*功能描述: 实现LED的驱动
*创建者: kaoyangou
*创建时间: 2021,05,03
---------------------------------------
*修改日志:
*修改内容:
*修改人:
*修改时间:
----------------------------------------*/
/*************************************************
*头文件
*************************************************/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/ioctl.h>
#include <linux/cdev.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#include <cfg_type.h>
#define DEVICE_NAME "Led" //定义设备名字
//定义Led的结构体,包括管脚和名字
struct led {
int gpio;
char *name;
};
//定义Led的管脚和名字
static struct led led_gpios[] = {
{PAD_GPIO_B+26,"led1"},
{PAD_GPIO_C+11,"led2"},
{PAD_GPIO_C+7,"led3"},
{PAD_GPIO_C+12,"led4"},
};
#define LED_NUM 4 //ARRAY_SIZE(led_gpios)
#define TEST_MAX_NR 4 //定义命令的最大序数
#define TEST_MAGIC 'x' //定义幻数
/*************************************************
*led_open函数
*************************************************/
static int led_open(struct inode *inode, struct file *filp)
{
printk(DEVICE_NAME ":open\n");
return 0;
}
/*************************************************
*LED控制函数
*************************************************/
static long gec5260_leds_ioctl(struct file *filp, unsigned int cmd,
unsigned long arg)
{
#if 1
printk("led_num = %d \n", LED_NUM);
if(_IOC_TYPE(cmd) != TEST_MAGIC) return - EINVAL;
if(_IOC_NR(cmd) > TEST_MAX_NR) return - EINVAL;
gpio_set_value(led_gpios[_IOC_NR(cmd)].gpio, arg);
printk(DEVICE_NAME": %d %lu\n", _IOC_NR(cmd), arg);
#endif
//printk("xxxx %lu, %d xxxxx \n", cmd, arg);
return 0;
}
/*************************************************
*文件操作集
*************************************************/
static struct file_operations gec5260_led_dev_fops = {
.owner = THIS_MODULE,
.open = led_open,
.unlocked_ioctl = gec5260_leds_ioctl,
};
/*************************************************
*杂项设备
*************************************************/
static struct miscdevice gec5260_led_dev = {
.minor = MISC_DYNAMIC_MINOR,
.name = DEVICE_NAME,
.fops = &gec5260_led_dev_fops,
};
/********************************************************************
*驱动的初始化函数--->从内核中申请资源(内核、中断、设备号、锁....)
********************************************************************/
static int __init gec5260_led_dev_init(void)
{
int ret, i;
gpio_free(PAD_GPIO_B+26);
gpio_free(PAD_GPIO_C+11);
gpio_free(PAD_GPIO_C+7);
gpio_free(PAD_GPIO_C+12);
for (i = 0; i < LED_NUM; i++)
{
ret = gpio_request(led_gpios[i].gpio, led_gpios[i].name); //io申请
if (ret) //失败则打印出那个管教申请失败
{
printk("%s: request GPIO %d for LED failed, ret = %d\n", led_gpios[i].name, led_gpios[i].gpio, ret);
return ret;
}
gpio_direction_output(led_gpios[i].gpio,0); //设置io为输出管脚
}
ret = misc_register(&gec5260_led_dev); //杂项设备申请
printk(DEVICE_NAME"\tinitialized\n"); //显示申请成功
return ret;
}
/*****************************************************************
*驱动退出函数 --->将申请的资源还给内核
*****************************************************************/
static void __exit gec5260_led_dev_exit(void)
{
int i;
//释放管教
for (i = 0; i < LED_NUM; i++)
{
gpio_free(led_gpios[i].gpio);
}
//移除杂项设备
misc_deregister(&gec5260_led_dev);
}
module_init(gec5260_led_dev_init); //驱动的入口函数会调用一个用户的初始化函数
module_exit(gec5260_led_dev_exit); //驱动的出口函数会调用一个用户的退出函数
/***************************************************************
*驱动的描述信息: #modinfo *.ko , 驱动的描述信息并不是必需的。
***************************************************************/
MODULE_AUTHOR("kaoyangou"); //驱动的作者
MODULE_DESCRIPTION("the LED of driver"); //驱动的描述
MODULE_LICENSE("GPL"); //遵循的协议
第二步:将led驱动程序编译进内核中
- 进到kernel顶层目录使用make menuconfig,弹出图形界面,进行配置
Hello@University:~/demo/6818GEC/kernel$ make menuconfig
- 选择Device Drivers,然后选择select:
- 选择Character devices,然后选择select:
- 选择GEC6818 led driver,键盘按y进行选择,星号*表示选中,然后exit:
- 然后一直exit,直到弹出是否确认保存配置,选择yes:
- 最后拷贝配置文件.config 到当前目录的arch/arm/configs/GEC6818_defconfig目录下
Hello@University:~/demo/6818GEC/kernel$ cp .config arch/arm/configs/GEC6818_defconfig
第三步:将驱动编译进内核
注意:将驱动编译进内核,即:先将驱动程序放入内核源码中,然后写好相关配置,然后把内核源码编译一下
- 进到kernel目录的上一级目录:
- 然后输入以下命令进行内核编译,生成boot.img文件:
Hello@University:~/demo/6818GEC$ mk -k
- 在当前目录~/demo/6818GEC/out/target/product/GEC6818下找到boot.img文件,将其拖拽到pc机(Windows系统下)
第四步:将内核移植到实验箱当中
- 首先,使用串口线将电脑与实验箱相连,注意要安装好串口线的驱动,启动实验箱,然后在securityCRT上按空格进入uboot模式(自行配置和securityCRT与实验箱相连)
- uboot模式下,在securityCRT命令行输入fastboot进入实验箱的刷机模式:
fastboot
- 接着在电脑的设备管理器上看是否能找到实验箱设备,
注意:更新下该设备的驱动,选择adb驱动(注意事先已装好adb工具的驱动,adb驱动是配合fastboot使用的),使得能正常刷机 - 使用fastboot工具进行刷机,将GECuboot.bin、rootfs-6818-restore.ext4以及刚编译好的boot.img文件放在fastboot工具的目录下,在cmd命令行下进入该目录,目录结构如下:
- 在cmd命令行下输入一下命令进行操作:
fastboot flash boot boot.img
- 等待cmd命令行上显示刷入成功,然后重启实验箱,等待其正常启动,然后在securityCRT上键入CTRL+C进入实验箱的系统,输入以下命令,出现预想结果,则成功:
ls /dev/Led -l
结果如下: