我是靠谱客的博主 无限世界,最近开发中收集的这篇文章主要介绍Linux 设备驱动编写(misc)系列文章目录前言一、Helloworld二、杂项设备驱动(重头戏,有一定基础可以直接看这里)附录:杂项设备驱动(Ubuntu上运行的),觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

系列文章目录

第一章 Linux 中内核与驱动程序
第二章 Linux 设备驱动编写 (misc)
第三章 Linux 设备驱动编写及设备节点自动生成 (cdev)
第四章 Linux 平台总线platform与设备树
第五章 Linux 设备树中pinctrl与gpio(lichee nano pi)


文章目录

  • 系列文章目录
  • 前言
  • 一、Helloworld
    • 1.在Ubuntu上加载驱动
    • 2.在开发板上加载驱动
  • 二、杂项设备驱动(重头戏,有一定基础可以直接看这里)
    • 1.misc设备驱动编写思路
    • 2.用misc设备驱动点亮小灯
    • 3.测试程序编写
  • 附录:杂项设备驱动(Ubuntu上运行的)


前言

Linux设备驱动有很多种,整体分成三大类:字符设备设备驱动、块设备驱动和网络设备。其中前两者的区别是传输数据的单元大小,一个传输字符,一个传输块,网络设备是根据socket。除了上述三种之外还有一种叫做杂项设备驱动(B站迅为电子视频的叫法),所有的杂项设备(misc)的主设备号为10,而且它会自动生成设备节点,具体的过程,本文将详细展开。


一、Helloworld

路要一步步走,首先我们来看一下驱动程序中的hello world。

下面给出一个例子,有两个文件:hello.c和Makefile。这两个文件在一个目录下,通过make命令可以直接生成hello.ko命令,再使用insmod命令就可以安装驱动到内核。

hello.c

#include <linux/module.h>
#include <linux/init.h>
MODULE_LICENSE("Dual BSD/GPL");
static int hello_init(void)
{
 printk(KERN_ALERT "Hello word");
 return 0;
}
static void hello_exit(void)
{
 printk(KERN_ALERT "Goodbye,Hello word");
}
module_init( hello_init );
module_exit( hello_exit );

可以看出驱动程序的结构和我们一般编写的应用程序(比如第一个打印hello world的程序)不太一样。它里面没有main文件,那么他怎么运行呢?这也是驱动程序和应用程序的区别。在O’Reilly 的动物系列丛书中有本《Linux设备驱动程序》提到过这样一个概念(大概):驱动程序提供的是“机制”,应用程序提供的是“策略”。举个例子:机制就是游戏手柄上的上、下、左、右四个按钮对应人物的走动,策略就是你打游戏时什么自由的控制任务走动。

驱动程序有两个函数:module_init( )和module_exit( )。当在终端使用insmod加载模块时,会执行module_init( )内的程序去完成相应的注册;用rmmod命令卸载模块时,执行module_exit( )中的程序,将一些资源释放。加载好模块之后可以使用lsmod命令查看已加载模块,模块的名字就是你生成的ko文件的名字。

上述编写好了hello.c文件那么接下来需要编译成hello.ko文件,然后加载到内核当中去。

在此之前要先明确一个问题:Ubuntu上跑的程序是x86架构的,开发板上跑的是arm架构的。为此在这里分两个标题讲。注:在终端通过【file 文件名】的格式可以查看文件是哪一种。

1.在Ubuntu上加载驱动

编写了如下适配于Ubuntu的Makefile文件,讲它和hello.c文件放在一个目录下,在该目录打开终端make就可以,这里默认环境就是x86环境。

Makefile (Ubuntu版本-x86架构)

ifneq ($(KERNELRELEASE),)
        obj-m := hello.o // 要和.c文件同名字
else
        KDIR:=/lib/modules/$(shell uname -r)/build  //Ubuntu内核源码的路径
        PWD:=$(shell pwd)//当前路径
all:
        $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules //去内核源码中编译
endif
clean:
        rm -f *.o *.ko *.mod.c .hello*

make之后会生产hello.ko文件,然后依次运行以下命令

insmod hello.ko
ls hello

下面是我insmod hello.ko之后的,可以看到该模块并没有什么用处,毕竟是第一个hello world。而且由于是虚拟机,并不会打印出我们在注册时printk的消息,可以通过dmesg命令查看内核日志。

在这里插入图片描述

rmmod hello

2.在开发板上加载驱动

上述用的是Ubuntu的内核去编译的模块,所以能在Ubuntu上加载驱动。所以要想在开发板上加载驱动,需要开发板的内核。这里就要求你在开发板上烧写的内核,你的Ubuntu上也有有一套源码。
以我的开发板为例,我用的是荔枝nano开发板。用的内核是4.14.0-licheepi-nano,是一个主线Linux内核。

这里的环境不再是默认的x86架构,需要更改成适合arm架构的环境。

//更改arm环境
export ARCH=arm
export CROSS_COMPILE=arm-linux-gnueabihf- //这里具体看你哪里用的是什么交叉编译器

更环境变量之后,还需要将内核编译一下。注:这是在内核文件里make,也是需要arm环境的。这里如果不编译内核时会报错的,我这里报错:*** 没有规则可制作目标“modules”

make -j12 //开12线程,会快一些

上述内核改好之后在你写驱动的文件夹里,也设置成arm环境,利用下面的Makefile去make。

Makefile (开发板版本-arm架构)适配于我的内核的Makefile文件

obj-m := hello.o
KDIR:=/home/ice/workspace/linuxtf //linuxtf是我的开发板的内核源码
PWD:=$(shell pwd)
all:
        make -C $(KDIR) M=$(PWD) modules
clean:
        rm -f *.o *.ko *.mod.* Module.* modules.*

用file命令查看该文件的类型,是ARM的。
在这里插入图片描述

完事之后会生成hello.ko文件,你可以通过tftp或者nfs等方式将该文件传输到开发板。
用这些命令:insmod 、rmmod、lsmod去操作。


二、杂项设备驱动(重头戏,有一定基础可以直接看这里)

路要一步步走。上述说了Ubuntu上跑的程序是x86架构的,开发板上跑的是arm架构的,并且讲了怎么去设置两种环境。下面来看杂项设备驱动(misc_device)

杂项设备驱动是一种比字符设备要简单的驱动类型,一些简单的点灯程序也是完全够用。通过cat /proc/misc可以看到内核中的杂项设备号。注意这里的设备号和名字并不是设备文件(节点),设备节点在 /dev目录下。

1.misc设备驱动编写思路

杂项设备的编写不再是Helloworld那样简单了只能注册、注销。现在的目标是点灯。注:具体的一些基本概念请看上一节的内容,在此不作过多的赘述。

注册思路:
1注册杂项设备<=2构建杂项设备结构体
3构架file_operations结构体
4卸载杂项设备

这么说可能不太还理解,我来解释一下:我们要写一个驱动,当然要注册了,但是要注册什么呢?这就需要杂项设备结构体,注册了之后还要有一步就是通过file_operations结构体链接应用层和驱动层。最后是卸载驱动。整体思路就是这样。
1 、extern int misc_register(struct miscdevice *misc);注册杂项设备
extern int misc_deregister(struct miscdevice *misc);注销杂项设备
2 、杂项设备结构体如下图所示:
在这里插入图片描述
minor 代表次设备号;
name 代表设备节点的名字;
&fops 代表与之相关的file_operations结构体。只写这三个就够了。

3 、file_operations结构体的定义
在这里插入图片描述
小结所以实际完成驱动的编写的步骤是(1)填充miscdevice这个结构体(2)填充file_operations这个结构体(3)注册杂项设备并生生成设备节点。

2.用misc设备驱动点亮小灯

现在来给它加上点灯的功能,这里就提一句:由于Linux内核中不允许直接访问实际物理地址,需要在内核中用ioremap函数将实际地址转换成虚拟地址。具体你要点亮哪一个引脚上的LED看自己开发板的引脚的地址。

misc_led.c //将open、release、write、read补充上就行。

#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>

#include <linux/kernel.h>   /* printk() */
#include <linux/miscdevice.h> /* misc... */
#include <linux/fs.h>       /* everything... */
#include <linux/types.h>    /* size_t */

#include <linux/uaccess.h>

#include <linux/io.h>

#define GPIO 0x01c20800 //找你自己的
unsigned int *vir_gpio;


int misc_open(struct inode *inode,struct file *file)
{
    printk( "open my_misc_devn");
    return 0;
}

int misc_release(struct inode *inode,struct file *file)
{
    printk( "release my_misc_devn");
    return 0;
}

ssize_t misc_read(struct file *file,char __user *ubuf,size_t size,loff_t *loff_t)
{
    char kbuf[64] = "xieshangle";

    if(copy_to_user(ubuf,kbuf,strlen(kbuf))!= 0)
    {
        printk( "copy_to_user errorn");
        return -1;
    }
    printk( "misc_readn");
    return 0;
}

ssize_t misc_write(struct file *file,const char __user *ubuf,size_t size,loff_t *loff_t)
{
    char kbuf[64] = {0};

    if(copy_from_user(kbuf,ubuf,size)!= 0)
    {
        printk( "copy_from_user errorn");
        return -1;
    }
    if(kbuf[0] == 1) 		 //当用户对设备进行写操作时,将寄存器地址置位,
        *vir_gpio |=(1 << 1);//具体高位亮还是地位亮看自己的开发板电路
    else if(kbuf[0] == 0)
        *vir_gpio &=~(1 << 1);


    return 0;
}


struct file_operations misc_fops={
  .owner   =THIS_MODULE,
  .open    =misc_open,
  .release =misc_release,
  .read    =misc_read,
  .write   = misc_write,
};

struct miscdevice misc_dev ={
	.minor =MISC_DYNAMIC_MINOR,
	.name  ="my_misc",
	.fops  =&misc_fops,

};

static int misc_init(void)
{
    int ret;
    ret =misc_register(&misc_dev);
    if(ret < 0){
        printk( "misc register is failedn");
        return -1;
    }
    printk( "misc register is succeedn");

    vir_gpio = ioremap(GPIO,4);
    if(vir_gpio == NULL)
    {
        printk( "ioremap is failedn");
        return -EBUSY;
    }
    printk( "GPIO is ioremap to vir_gpion");

    return 0;

}
static void misc_exit(void)
{
    misc_deregister(&misc_dev);
    iounmap(vir_gpio);
    printk(KERN_ALERT "Goodbye,miscn");
}

/* register the init and exit routine of the module */
module_init( misc_init );
module_exit( misc_exit );

MODULE_LICENSE("GPL");

make之后加载到开发板,insmod之后再来编写一个测试程序。

3.测试程序编写

app.c //
./app 1
./app 0
送入什么将对应引脚置为对应电平

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int main(int argc,char *argv[])
{
    int fd;
    char buf[64] = {0};

    fd = open("/dev/my_misc",O_RDWR);

    if(fd < 0)
    {
        perror("open errorn");
        return fd;
    }

    //read(fd,buf,sizeof(buf));//
    //printf("buf is %sn",buf);//

    buf[0] = atoi(argv[1]);
    write(fd,buf,sizeof(buf));

    if(buf[0] == 1)
        printf("gao dian ping");
    else if(buf[0] == 0)
        printf("di dian ping");

    close(fd);
    return 0;
}

这里注意对应用程序进行编译用的·和环境的交叉编译器有一点不一样。

arm-linux-gnueabi-gcc app.c -o app -static

会生成arm架构的app可执行文件。
在这里插入图片描述
app编译好之后也传输到开发板:
在这里插入图片描述

由于荔枝nano单板没有led灯,我在此将信息打印出来了。

附录:杂项设备驱动(Ubuntu上运行的)

#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>

#include <linux/kernel.h>   /* printk() */
#include <linux/miscdevice.h> /* misc... */
#include <linux/fs.h>       /* everything... */

struct file_operations misc_fops={
  .owner=THIS_MODULE
};

struct miscdevice misc_dev ={
	.minor=MISC_DYNAMIC_MINOR,
	.name="my_misc",
	.fops=&misc_fops,

};

static int misc_init(void)
{
    int ret;
    ret =misc_register(&misc_dev);
    if(ret < 0){
        printk( "misc register is failedn");//注册失败打印失败
        return -1;
    }
    printk( "misc register is succeedn");//注册成功打印成功
    return 0;

}
static void misc_exit(void)
{
    misc_deregister(&misc_dev);
    printk(KERN_ALERT "Goodbye,miscn");
}

/* register the init and exit routine of the module */
module_init( misc_init );
module_exit( misc_exit );

MODULE_LICENSE("GPL");

现在Ubuntu上加载一下模块,insmod再rmmod之后,由于是虚拟机不能直接显示printk的信息,利用dmesg可以查看内核日志。
在这里插入图片描述

最后

以上就是无限世界为你收集整理的Linux 设备驱动编写(misc)系列文章目录前言一、Helloworld二、杂项设备驱动(重头戏,有一定基础可以直接看这里)附录:杂项设备驱动(Ubuntu上运行的)的全部内容,希望文章能够帮你解决Linux 设备驱动编写(misc)系列文章目录前言一、Helloworld二、杂项设备驱动(重头戏,有一定基础可以直接看这里)附录:杂项设备驱动(Ubuntu上运行的)所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(47)

评论列表共有 0 条评论

立即
投稿
返回
顶部