工作生活

嵌入式Linux开发 2 | 编写基于虚拟硬件的驱动程序

2019-07-01  本文已影响0人  Ricsy


编写

mkdir driver && cd driver

1. 简单驱动程序

说明:

  • 只有加载和卸载功能
#include<linux/module.h>

//驱动程序初始化函数
 static int __init hello_init(void)
 {
     printk(KERN_INFO"welcome to Hello character driver!\n");
     return 0;
 }

//驱动程序退出函数
 void __exit hello_exit(void)
 {
     printk("Goodbye!Character driver is so easy!\n");
 }

//向系统登记函数
 module_init(hello_init);
 module_exit(hello_exit);

//遵守GPL开源协议
 MODULE_LICENSE("Dual BSD/GPL");
obj-m:=hello_driver.o

KDIR:=/lib/modules/$(shell uname -r)/build
SRCPWD:=$(shell pwd)

all:
     make -C $(KDIR) M=$(SRCPWD) modules
clean:
     rm -f *.o *.mod.o *.mod.c *.symvers Mo* mo*
     echo
     ls -lh
cleanall:
     make clean
     rm -f *.ko
     echo
     ls -lh
insmod:
     insmod hello_driver.ko
     lsmod | more
rmmod:
     rmmod hello_driver

提示:

  • 注意Makefile格式中的Tab位置
  • 示例文件的云盘链接mwcj

2. 驱动程序进阶1

说明:

  • 增加修改设备属性功能
#include<linux/module.h>
#include<linux/fs.h>

#define DEVICE_NAME "hello"

static int demoMajor=0;

static long  hello_ioctl(struct file *filp,unsigned int cmd,unsigned long arg)
{
    switch(cmd)
    {
        case 0:
            printk("command 0 is run!\n");
            break;
        case 1:
            printk("command 1 is run!\n");
            break;
        default:
            printk("not known command!\n");
            break;
    }
    return 0;
}

//向系统登记hello_ioctl函数
static struct file_operations hello_fops=
{
    owner:THIS_MODULE,
    unlocked_ioctl:hello_ioctl,
};

//驱动程序初始化函数
static int __init hello_init(void)
{
    demoMajor=register_chrdev(0,DEVICE_NAME,&hello_fops);
    if(demoMajor<0)
    {
        printk(KERN_NOTICE DEVICE_NAME"register failure\n");
        return demoMajor;
    }
    return 0;
}
//驱动程序退出函数
void __exit hello_exit(void)
{   
    if(demoMajor>0)
        unregister_chrdev(demoMajor,DEVICE_NAME);
}

//向系统登记函数
module_init(hello_init);
module_exit(hello_exit);

//遵守GPL开源协议
MODULE_LICENSE("Dual BSD/GPL");

可以看到有hello,成功!!!

#include<stdio.h>
#include<stdlib.h>
#include<fcntl.h>

#define DEVICE_FILE "/dev/hello"

int main()
{
    int fd,cmd;

    //打开设备文件
    fd=open("/dev/hello",O_RDWR);
    if(fd<=0)
    {
        perror(DEVICE_FILE);
        exit(1);
    }

    //使用ioctl修改硬件属性
    for(cmd=0;cmd<=4;cmd++)
    {
        sleep(1);
        ioctl(fd,cmd,NULL);
    }

    //关闭设备文件
    close(fd);
    return 0;
}

gcc -o test testHelloDevice.c
./test
dmesg | tail

提示:

  • mknod命令格式:
    mkmod <设备文件名> <设备类型> <主设备号> <次设备号>
  • 为了方便管理,设备文件名与设备名相同
  • 设备类型为c(字符设备)或b(块设备)

参阅:


3. 驱动程序进阶2

说明:

  • 增加读写接口
#include<linux/module.h>
#include<linux/fs.h>
#include<linux/poll.h>
#include<linux/string.h>

#define DEVICE_NAME "hello"

//用于保存虚拟硬件返回给应用程序的字符串
static char drv_buff[6]="abcde";
//用于接收应用程序发送给虚拟硬件的字符串数据
static char data_from_user[1024];
//虚拟硬件返回给应用程序的字符串的格式标志
static unsigned char data_format=0;

static int demoMajor=0;

//硬件设备属性修改
static long  hello_ioctl(struct file *filp,unsigned int cmd,unsigned long arg)
{
    switch(cmd)
    {
        case 0:
            if(data_format!=0)
            {
                data_format=0;
                strcpy(drv_buff,"abcde");
            }
            break;
        case 1:
            if(data_format!=1)
            {
                data_format=1;
                strcpy(drv_buff,"ABCDE");
            }
            break;
        default:
            break;
    }
    return 0;
}

//读接口函数
static ssize_t hello_read(struct file *filp,char *buffer,size_t count,loff_t *ppos)
{
    int real_count,i;
    real_count=sizeof(drv_buff);
    i=copy_to_user(buffer,drv_buff,real_count);
    if(i<0)
        printk("copy_to_user() failure!\n");
    return(ssize_t)real_count;
}

//写接口函数
static ssize_t hello_write(struct file *filp,const char *buffer,size_t count,loff_t *ppos)
{
    int i;
    memset(data_from_user,'\0',sizeof(data_from_user));
    i=copy_from_user(data_from_user,buffer,count);
    if(i<0)
        printk("copy_from_user()failure!\n");
    printk(data_from_user);
    return(ssize_t)count;
}


//向系统注册ioctl、read、write函数
static struct file_operations hello_fops=
{
    owner:THIS_MODULE,
    unlocked_ioctl:hello_ioctl,
    read:hello_read,
    write:hello_write,
};

static int __init hello_init(void)
{
    demoMajor=register_chrdev(0,DEVICE_NAME,&hello_fops);
    if(demoMajor<0)
    {
        printk(KERN_NOTICE DEVICE_NAME"register failure\n");
        return demoMajor;
    }
    return 0;
}

void __exit hello_exit(void)
{   
    if(demoMajor>0)
        unregister_chrdev(demoMajor,DEVICE_NAME);
}

module_init(hello_init);
module_exit(hello_exit);

MODULE_LICENSE("Dual BSD/GPL");

提醒:

  • 加载新驱动程序前记得卸载旧版本
  • 在系统硬件没有太大的改变的情况下,每次加载驱动程序动态获得的主设备号一般不会改变,即对应的设备文件仍可用,若主设备号改变,则需删除旧版本的设备文件,以新主设备号重建设备文件.
#include<stdio.h>
#include<stdlib.h>
#include<fcntl.h>
#include<string.h>

#define DEVICE_FILE "/dev/hello"
#define BUFFER_SIZE 1024

int main()
{
    int fd;
    char buff[BUFFER_SIZE];

    //打开设备文件
    fd=open("/dev/hello",O_RDWR);
    if(fd<=0)
    {
        perror(DEVICE_FILE);
        exit(1);
    }

    //读硬件设备默认状态下的数据
    sleep(1);
    memset(buff,'\0',BUFFER_SIZE);
    read(fd,buff,BUFFER_SIZE);
    printf("read a default string:%s\n",buff);

    //发送修改硬件属性的命令1
    sleep(1);
    ioctl(fd,1,NULL);
    
    //读命令1后硬件设备的数据
    memset(buff,'\0',BUFFER_SIZE);
    read(fd,buff,BUFFER_SIZE);
    printf("read a string after cmd 1:%s\n",buff);
    
    //发送修改硬件属性的命令0
    sleep(1);
    ioctl(fd,0,NULL); 

    //读命令0后硬件设备的数据
    memset(buff,'\0',BUFFER_SIZE);
    read(fd,buff,BUFFER_SIZE);
    printf("read a string after cmd 0:%s\n",buff);

    //往硬件设备写入字符串
    memset(buff,'\0',BUFFER_SIZE);
    strcpy(buff,"I love Linux!");
    printf("write string \"%s\" to the device\n",buff);
    write(fd,buff,strlen(buff));

    //关闭设备文件
    close(fd);
    return 0;
}

调试


Q&A


1. *** /lib/modules/3.10.0-957.21.3.el7.x86_64/build: 没有那个文件或目录。

rm build
ln -s /usr/src/kernels/3.10.0-957.21.3.el7.x86_64 /lib/modules/3.10.0-957.21.3.el7.x86_64/build

成功!!!

参阅:


2. disagrees about version of symbol modulelay

发现不存在内核开发包
yum install kernel-devel -y
再次查看结果

没有报错,且系统内核已加载的模块中有hello_driver,说明成功!!!

参阅:

3. 错误:初始值设定项里有未知的字段‘ioctl’

可以看出在当前系统内核中fs.h中不存在
int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);

提示:


更新中......


上一篇 下一篇

猜你喜欢

热点阅读