Linux基于框架编写底层驱动代码(基础篇)

本文详细介绍了Linux字符设备驱动的编写过程,包括底层驱动和上层验证。在Ubuntu环境下,讲解了如何编译及在树莓派上部署和测试驱动。此外,还展示了如何进行IO口操作,通过ioremap实现IO寄存器的访问。最后,提供了上层应用的代码,用于控制GPIO口的高低电平状态。

一、驱动框架

1.底层驱动

#include <linux/fs.h>            //file_operations声明
#include <linux/module.h>    //module_init  module_exit声明
#include <linux/init.h>      //__init  __exit 宏定义声明
#include <linux/device.h>        //class  devise声明
#include <linux/uaccess.h>   //copy_from_user 的头文件
#include <linux/types.h>     //设备号  dev_t 类型声明
#include <asm/io.h>          //ioremap iounmap的头文件


static struct class *pin4_class;
static struct device *pin4_class_dev;

static dev_t devno;                //设备号
static int major =231;                     //主设备号
static int minor =0;                       //次设备号
static char *module_name="pin4";   //模块名


//led_read函数
static int pin4_read(struct file *file1,char __user *buf,size_t count, loff_t *ppos)
{
    printk("pin4_read\n");  //内核的打印函数和printf类似

    return 0;
}


//led_open函数
static int pin4_open(struct inode *inode,struct file *file)
{
    printk("pin4_open\n");  //内核的打印函数和printf类似



    return 0;
}

//led_write函数
static ssize_t pin4_write(struct file *file,const char __user *buf,size_t count, loff_t *ppos)
{

printk("pin4_write\n"); 
    return 0;
}

static struct file_operations pin4_fops = {

    .owner = THIS_MODULE,
    .open  = pin4_open,
    .write = pin4_write,
    .read  = pin4_read,
};

int __init pin4_drv_init(void)  //真实入口 
{

    int ret;
    devno = MKDEV(major,minor);  //创建设备号
    ret   = register_chrdev(major, module_name,&pin4_fops);  //注册驱动  告诉内核,把这个驱动加入到内核驱动的链表中

    pin4_class=class_create(THIS_MODULE,"myfirstdemo");//由代码在Dev下自动生成设备
    pin4_class_dev =device_create(pin4_class,NULL,devno,NULL,module_name);  //创建设备文件


    return 0;
}

void __exit pin4_drv_exit(void)
{

    device_destroy(pin4_class,devno);
    class_destroy(pin4_class);
    unregister_chrdev(major, module_name);  //卸载驱动

}

module_init(pin4_drv_init);  //入口 内核加载该驱动的时候,这个宏会被调用
module_exit(pin4_drv_exit);
MODULE_LICENSE("GPL v2");

2.上层验证

#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<stdio.h>
int main()
{
        int fd;
        fd = open("/dev/pin4",O_RDWR);
        if(fd<0){
                printf("open failed!\n");
                perror("res");
        }else{

                printf("open success!\n");
        }
        fd = write(fd,'1',1);


}

二、在ubantu下编译

1.将做好的代码文件移动到/system/linux-rpi-4.14.y/drivers/char下面
2.打开vi Makefile在obj-y += mem.o random.o下
添加obj-m += 1.o(文件名)
3.添加完成后回到/system/linux-rpi-4.14.y下执行命令ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- KERNEL=kernel7 make modules进行编译
编译成功生成drivers/char/1.ko

  CHK     include/config/kernel.release
  CHK     include/generated/uapi/linux/version.h
  CHK     include/generated/utsrelease.h
  CHK     include/generated/bounds.h
  CHK     include/generated/timeconst.h
  CHK     include/generated/asm-offsets.h
  CALL    scripts/checksyscalls.sh
  CHK     scripts/mod/devicetable-offsets.h
  CHK     kernel/config_data.h
  CC [M]  drivers/char/1.o
  Building modules, stage 2.
  MODPOST 1523 modules
  CC      drivers/char/1.mod.o
  LD [M]  drivers/char/1.ko//表示编译成功

三、驱动代码编译和测试

1.将底层驱动文件拷贝到树莓派上
scp drivers/char/1.ko pi@192.168.43.88:/home/pi
2.将上层文件拷贝到树莓派上
首先回到上层文件所在的目录执行arm-linux-gnueabihf-gcc 2.c -o test命令进行编译上层文件,编译通过后执行scp test pi@192.168.43.88:/home/pi拷贝到树莓派上
3.在树莓派上执行命令sudo insmod 1.ko将内核加载进去
然后执行命令ls /dev/pin4或者lsmod验证是否加载内核成功,加载成功后给执行命令sudo chmod 666 /dev/pin4给其运行权限,然后运行上层文件,可以用命令dmesg |grep pin4查看是否运行成功

四、IO口操作代码编程

1.代码
底层代码

#include <linux/fs.h>            //file_operations声明
#include <linux/module.h>    //module_init  module_exit声明
#include <linux/init.h>      //__init  __exit 宏定义声明
#include <linux/device.h>        //class  devise声明
#include <linux/uaccess.h>   //copy_from_user 的头文件
#include <linux/types.h>     //设备号  dev_t 类型声明
#include <asm/io.h>          //ioremap iounmap的头文件


static struct class *pin4_class;
static struct device *pin4_class_dev;

static dev_t devno;                //设备号
static int major =231;                     //主设备号
static int minor =0;                       //次设备号
static char *module_name="pin4";   //模块名

//volatile 指令不会因为编译器的优化而省略,且每次直接读值
volatile unsigned int* GPFSEL0 = NULL;
volatile unsigned int* GPSET0  = NULL;
volatile unsigned int* GPCLR0  = NULL;

//led_read函数
static int pin4_read(struct file *file1,char __user *buf,size_t count, loff_t *ppos)
{
    printk("pin4_read\n");  //内核的打印函数和printf类似

    return 0;
}


//led_open函数
static int pin4_open(struct inode *inode,struct file *file)
{
    printk("pin4_open\n");  //内核的打印函数和printf类似

    //配置pin4引脚为输出引脚 bit 12-14配置成001 
    *GPFSEL0 &= ~(0x6 << 12); //把bit 14  bit13 配置成0
    *GPFSEL0 |= (0x1 << 12);

    return 0;
}
//led_write函数
static ssize_t pin4_write(struct file *file,const char __user *buf,size_t count, loff_t *ppos)
{
    //获取上层write函数的值
    //根据值来操作io口,高电平或者低电平
    int userCmd;

    printk("pin4_write\n");

    copy_from_user(&userCmd,buf,count);

    //printk("get value\n");

    if(userCmd == 1){

            *GPSET0 |= 0x1 << 4;
            printk("get 1\n");

    }else if(userCmd == 0){

            *GPCLR0 |= 0x1 << 4;
            printk("get 0\n");

    }else{
            printk("undo");
    }
        return 0;
}

static struct file_operations pin4_fops = {

    .owner = THIS_MODULE,
    .open  = pin4_open,
    .write = pin4_write,
    .read  = pin4_read,
};

int __init pin4_drv_init(void)  //真实入口 
{

    int ret;
    printk("insmod driver pin4 success\n");
    devno = MKDEV(major,minor);  //创建设备号
 ret   = register_chrdev(major, module_name,&pin4_fops);  //注册驱动  告诉内核,把这个驱动加入到内核驱动的链表中

    pin4_class=class_create(THIS_MODULE,"myfirstdemo");//由代码在Dev下自动生成设备
    pin4_class_dev =device_create(pin4_class,NULL,devno,NULL,module_name);  //创建设备文件

    GPFSEL0 = (volatile unsigned int *)ioremap(0x3f200000,4);//将物理地址转换成虚拟地址用函数ioremap,io寄存器映射成普
通内存单元进行访问
    GPSET0 = (volatile unsigned int *)ioremap(0x3f20001C,4);
    GPCLR0 = (volatile unsigned int *)ioremap(0x3f200028,4);

    return 0;
}

void __exit pin4_drv_exit(void)
{
    iounmap(GPFSEL0);
    iounmap(GPSET0);
    iounmap(GPCLR0);


    device_destroy(pin4_class,devno);
    class_destroy(pin4_class);
    unregister_chrdev(major, module_name);  //卸载驱动

}

module_init(pin4_drv_init);  //入口 内核加载该驱动的时候,这个宏会被调用
module_exit(pin4_drv_exit);
MODULE_LICENSE("GPL v2");

上层代码

#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<stdio.h>
int main()
{
        int fd;
        int cmd;
        int data;
        fd = open("/dev/pin4",O_RDWR);
        if(fd<0){
                printf("open failed!\n");
                perror("res");
        }else{

                printf("open success!\n");
        }
        printf("input commd :1/0 \n 1:set pin4 high \n 0:set pin4 low\n");

        scanf("%d",&cmd);

        if(cmd == 1){
                data = 1;
        }
        if(cmd == 0){
                data = 0;

        }
        printf("data = %d\n",data);
        fd = write(fd,&data,1);


}

2.代码编译
1.在目录/system/linux-rpi-4.14.y下使用指令ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- KERNEL=kernel7 make modules进行底层代码的编译
2.在上层代码所在目录使用指令arm-linux-gnueabihf-gcc 2.c -o test进行编译
3.在树莓派上交叉编译
1.在树莓派上执行指令:
sudo rmmod 1.ko(文件名)
rm 1.ko(文件名)
rm test(文件名)
删除树莓派上之前加载的内核
使用指令lsmod或ls /dev/pin4查看是否删除成功
2.在ubantu上使用指令scp test pi@192.168.43.88:/home/pi将上层代码拷贝到树莓派上
使用指令scp drivers/char/1.ko pi@192.168.43.88:/home/pi将底层代码拷贝到树莓派上
可在ubantu上使用指令md5sum drivers/char/1.ko(文件目录+文件名),在树莓派上使用指令md5sum 1.ko(文件名)看是否成功拷贝(成功拷贝后返回的数值一致)
3.在树莓派上执行命令sudo insmod 1.ko将内核加载进去然后执行命令ls /dev/pin4或者lsmod验证是否加载内核成功,加载成功后给执行命令sudo chmod 666 /dev/pin4给其运行权限
4.使用./test(文件名)运行程序,可以用命令dmesg |grep pin4查看是否运行成功,命令dmsg可以查看底层代码运行状态,成功运行之后可以使用命令gpio readall查看指定IO口是否有响应。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值