本文是对视频内容的个人学习记录。
具体请查看百问网资料,嵌入式linux应用开发手册v5.1,这里简单描述下
分层:在leddrv驱动程序中,把led操作部分抽象出来,设计成led_operations对象,所以我们就不用管硬件部分的工作,只知道这个对象,然后就可以进行引脚的初始化和操作。
struct led_operations{
void (*map)(void); //引脚映射
void (*unmap)(void); //引脚解映射
int (*init)(int which); //初始化
int (*ctl)(int which,char state); //操作引脚输出高低电平
};
分离:led是属于gpio的操作,假如有实现了一个板子的所有gpio相关配置chip_demo_gpio.c,我们只需要指定需要用到的那些引脚资源就行。
struct led_resource
{
int pin[LED_NUM];
};
资源指定实现
led_resource.h的内容,实现led_resource这个对象类型和 get_led_resource函数声明。
#ifndef MY_LED_RESOURCE
#define MY_LED_RESOURCE
#define GROUP(x) (x>>16)
#define PIN(x) ((x)&(0xffff))
#define GROUP_PIN(g,p) ((g<<16)|(p))
#define LED_NUM 2
struct led_resource
{
int pin[LED_NUM];
};
struct led_resource * get_led_resource(void);
#endif
board_A_led.c内容,具体指定资源和get_led_resource函数的实现,get_led_resource是为了给其他文件调用,以达到资源的指定。
#include "led_resource.h"
static struct led_resource board_A_led={
.pin[0]=GROUP_PIN(5,3),
.pin[1]=GROUP_PIN(3,3)
};
struct led_resource * get_led_resource(void){
return &board_A_led;
}
led_operations的实现
led_opr.h实现led_operations对象类型的定义和get_board_led_opr的声明,get_board_led_opr是给leddrv.c调用。
#ifndef MY_LED_OPR
#define MY_LED_OPR
struct led_operations{
void (*map)(void);
void (*unmap)(void);
int (*init)(int which);
int (*ctl)(int which,char state);
};
struct led_operations* get_board_led_opr(void);
#endif
chip_demo_gpio.c实现led_operations的具体操作和get_board_led_opr的实现。
#include "led_opr.h"
#include "led_resource.h"
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/uaccess.h>
static struct led_resource* led_rsc=NULL;
/* registers */
// IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 地址:0x02290000 + 0x14
static volatile unsigned int *IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3;
// GPIO5_GDIR 地址:0x020AC004
static volatile unsigned int *GPIO5_GDIR;
//GPIO5_DR 地址:0x020AC000
static volatile unsigned int *GPIO5_DR;
//which指定板子上的那个led灯
static int board_A_gpio_init(int which){
if(!led_rsc){
led_rsc=get_led_resource();
}
printk("init gpio: group %d, pin %d\n", GROUP(led_rsc->pin[which]), PIN(led_rsc->pin[which]));
switch(GROUP(led_rsc->pin[which])){
case 3:
{
printk("init pin of group 3 ...\n");
break;
}
case 5:
{
printk("init pin of group 5 ...\n");
*IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 &= ~0xf;
*IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 |= 0x5;
*GPIO5_GDIR |= (1<<3);
break;
}
}
return 0;
}
static int board_A_gpio_ctl(int which,char state){
printk("set led %d: group %d, pin %d\n", state ? 1 : 0, GROUP(led_rsc->pin[which]), PIN(led_rsc->pin[which]));
switch(GROUP(led_rsc->pin[which]))
{
case 3:
{
printk("set pin of group 3 ...\n");
break;
}
case 5:
{
printk("set pin of group 5 ...\n");
if (state)
{
*GPIO5_DR &= ~(1<<3);/* set gpio to let led on */
}
else
{
*GPIO5_DR |= (1<<3); /* set gpio to let led off */
}
break;
}
}
return 0;
}
void board_map(void){
/* ioremap */
// IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 地址:0x02290000 + 0x14
IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 = ioremap(0x02290000 + 0x14, 4);
// GPIO5_GDIR 地址:0x020AC004
GPIO5_GDIR = ioremap(0x020AC004, 4);
//GPIO5_DR 地址:0x020AC000
GPIO5_DR = ioremap(0x020AC000, 4);
}
void board_unmap(void){
iounmap(IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3);
iounmap(GPIO5_GDIR);
iounmap(GPIO5_DR);
}
static struct led_operations led_opr={
.map=board_map,
.unmap=board_unmap,
.init=board_A_gpio_init,
.ctl=board_A_gpio_ctl,
};
struct led_operations* get_board_led_opr(void){
return &led_opr;
}
leddrv驱动代码
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include <linux/kernel.h>
#include <asm/io.h>
#include "led_opr.h"
#include "led_resource.h"
#define BUF_SIZE 1024
#define MIN(x,y) ((x)<(y)?(x):(y))
extern struct led_operations* get_board_led_opr(void);
char ker_buf[BUF_SIZE];
static int major=0;
static struct class *led_class;
static struct led_operations *led_opr;
ssize_t led_write (struct file *file, const char __user *usr_buf, size_t size, loff_t *off_set){
struct inode *inode=file_inode(file);
int minor=iminor(inode);
int state;
printk("%s %s line %d\n",__FILE__,__FUNCTION__,__LINE__);
copy_from_user(&state,usr_buf,1);
led_opr->ctl(minor,state);
return 0;
}
int led_open (struct inode *inode, struct file *file){
int minor;
printk("%s %s line %d\n",__FILE__,__FUNCTION__,__LINE__);
minor=MINOR(inode->i_rdev);
led_opr->init(minor);
return 0;
}
int led_close(struct inode *inode, struct file *file){
printk("%s %s line %d\n",__FILE__,__FUNCTION__,__LINE__);
return 0;
}
static struct file_operations led_drv={
.owner=THIS_MODULE,
.open=led_open,
.release=led_close,
.write=led_write,
};
static int __init led_init(void){
int i=0;
printk("%s %s line %d\n",__FILE__,__FUNCTION__,__LINE__);
led_opr=get_board_led_opr();
led_opr->map();
major=register_chrdev(0,"myled",&led_drv);
led_class=class_create(THIS_MODULE,"myled_class");
for(i=0;i<LED_NUM;++i){
device_create(led_class,NULL,MKDEV(major,i),NULL,"myled%d",i);
}
return 0;
}
static void __exit led_exit(void){
int i;
printk("%s %s line %d\n",__FILE__,__FUNCTION__,__LINE__);
led_opr->unmap();
for(i=0;i<LED_NUM;++i){
device_destroy(led_class,MKDEV(major,i));
}
class_destroy(led_class);
unregister_chrdev(major,"led");
}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
makefile
# 1. 使用不同的开发板内核时, 一定要修改KERN_DIR
# 2. KERN_DIR中的内核要事先配置、编译, 为了能编译内核, 要先设置下列环境变量:
# 2.1 ARCH, 比如: export ARCH=arm64
# 2.2 CROSS_COMPILE, 比如: export CROSS_COMPILE=aarch64-linux-gnu-
# 2.3 PATH, 比如: export PATH=$PATH:/home/book/100ask_roc-rk3399-pc/ToolChain-6.3.1/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/bin
# 注意: 不同的开发板不同的编译器上述3个环境变量不一定相同,
# 请参考各开发板的高级用户使用手册
all:
make -C $(KERN_DIR) M=`pwd` modules
$(CROSS_COMPILE)gcc -o ledtest ledtest.c
clean:
make -C $(KERN_DIR) M=`pwd` modules clean
rm -rf modules.order
rm -f ledtest
100ask_led-y := leddrv.o chip_demo_gpio.o board_A_led_res.o
obj-m += 100ask_led.o
copy:
cp 100ask_led.ko ledtest /home/book/nfs
修改linux窗口打印级别
echo "7 4 1 7" > /proc/sys/kernel/printk