• 正文
    • mydevice.c源码
    • 完整mydevice.c示例源码
    • 编译
    • 编写测试应用源码device_app.c
    • 测试
  • 相关推荐
申请入驻 产业图谱

飞凌嵌入式ElfBoard ELF 1板卡-字符驱动测试示例

03/17 13:40
209
加入交流群
扫码加入
获取工程师必备礼包
参与热点资讯讨论

例程代码路径:ELF 1开发板资料包3-例程源码3-2 驱动例程源码2_字符驱动mydevice

mydevice.c源码

(一)首先包含头文件

#include <linux/module.h>       // 包含模块相关函数的头文件

#include <linux/fs.h>           // 包含文件系统相关函数的头文件

#include <linux/uaccess.h>      // 包含用户空间数据访问函数的头文件

#include <linux/cdev.h>         //包含字符设备头文件

(二)声明变量

#define DEVICE_NAME "mydevice"  // 设备名称

static dev_t dev_num;            //分配的设备号

struct  cdev my_cdev;          //字符设备指针

int major;  //主设备号

int minor;  //次设备号

(三)驱动模块入口和出口函数

module_init(mydevice_init); // 指定驱动程序的初始化函数

module_exit(mydevice_exit); // 指定驱动程序的清理函数

(四)入口函数和出口函数实现

static int __init mydevice_init(void)

{

int ret;

// 在这里执行驱动程序的初始化操作

// 注册字符设备驱动程序

ret = alloc_chrdev_region(&dev_num,0,1,DEVICE_NAME);

if (ret < 0) {

printk(KERN_ALERT "Failed to allocate device number: %dn", ret);

return ret;

}

major=MAJOR(dev_num);       //获取主设备号

minor=MINOR(dev_num);       //获取次设备号

printk(KERN_INFO "major number: %dn",major);

printk(KERN_INFO "minor number: %dn",minor);

my_cdev.owner = THIS_MODULE;

cdev_init(&my_cdev,&fops);    //初始化字符设备结构体

cdev_add(&my_cdev,dev_num,1);  //将字符设备添加到内核中

printk(KERN_INFO "Device registered successfully.n");

return 0;

}

static void __exit mydevice_exit(void)

{

// 在这里执行驱动程序的清理操作

// 删除字符设备

cdev_del(&my_cdev);

// 注销字符设备驱动程序

unregister_chrdev(0, DEVICE_NAME);

printk(KERN_INFO "Device unregistered.n");

}

入口函数mydevice_init(void)中主要有以下几个函数的调用:

(1)调用int alloc_chrdev_region(dev_t *dev, unsigned int firstminor, unsigned int count, const char *name);函数进行设备号分配。

dev:指向dev_t类型的指针,用于接收分配的设备号范围的起始设备号;

firstminor:指定次设备号的起始值。次设备号用于区分同一主设备号下的不同设备实例;

count:指定要分配的设备号数量;

name:指定字符设备的名称,这是一个用于标识设备的字符串,可以在/proc/devices中找到;

(2)调用MAJOR(dev)获取主设备号:

MAJOR(dev)宏接受一个dev_t类型的设备号作为参数,并返回该设备号的主设备号。

(3)调用MINOR(dev)获取次设备号:

MINOR(dev)宏接受一个dev_t类型的设备号作为参数,并返回该设备号的次设备号。

(4)调用void cdev_init(struct cdev *cdev, const struct file_operations *fops);函数初始化字符设备结构体。

cdev:指向要初始化的struct cdev结构体的指针;

fops:指向包含设备操作函数的struct file_operations结构体的指针;

(5)调用int cdev_add(struct cdev *p, dev_t dev, unsigned int count);函数将字符设备添加到内核中。

p:指向要添加的struct cdev结构体的指针;

dev:指定要添加的设备号。通常是由alloc_chrdev_region或register_chrdev_region函数分配得到的设备号;

count:指定要添加的设备号数量;

cdev_add函数将字符设备添加到内核中,并与指定的设备号相关联。当成功添加字符设备后,它将被分配一个主设备号和次设备号,并成为系统中的一个可用设备;

注意:在调用cdev_add之前,应先使用cdev_init函数对struct cdev进行初始化,并设置正确的文件操作函数。

出口函数mydevice_exit(void)中主要有以下两个函数的调用:

(1)调用void unregister_chrdev(unsigned int major, const char *name);函数注销已注册的字符设备。

major:要注销的字符设备的主设备号;

name:字符设备的名称;

unregister_chrdev函数将指定的字符设备从内核中注销,并释放相关的资源。注销后,该字符设备将不再可用;

(2)调用void cdev_del(struct cdev *p);函数删除字符设备。

p:指向要删除的struct cdev结构体的指针;

cdev_del函数从内核中删除指定的字符设备,并释放相关的资源。删除后,该字符设备将不再可用;

(五)定义fops结构体

static struct file_operations fops = {

.owner = THIS_MODULE,

.open = device_open,

.release = device_release,

.read = device_read,

.write = device_write,

};

file_operations结构体定义如下:

struct file_operations {

struct module *owner;

loff_t (*llseek) (struct file *, loff_t, int);

ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);

ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);

int (*open) (struct inode *, struct file *);

int (*release) (struct inode *, struct file *);

// 其他成员函数...

};

file_oprations结构中定义了非常多的成员,但是对于大多数字符驱动而言,只需要实现,其中很少的几个方法即可。下面将忽略不常用的成员,对常用成员进行介绍:

(1)owner:指向持有该结构体的模块的指针;

(2)llseek:用于实现文件的定位(seek)操作;

(3)read:用于实现从文件读取数据的操作;

(4)write:用于实现向文件写入数据的操作;

(5)open:用于打开文件时的处理操作;

(6)release:用于关闭文件时的处理操作;

(六)设备操作函数的实现

static int device_open(struct inode *inode, struct file *file)

{

// 在这里处理设备打开的操作

printk(KERN_INFO "This is device_open.n");

return 0;

}

static int device_release(struct inode *inode, struct file *file)

{

// 在这里处理设备关闭的操作

printk(KERN_INFO "This is device_release.n");

return 0;

}

static ssize_t device_read(struct file *file, char __user *buffer, size_t length, loff_t *offset)

{

// 在这里处理设备读取的操作

printk(KERN_INFO "This is device_read.n");

return 0;

}

static ssize_t device_write(struct file *file, const char __user *buffer, size_t length, loff_t *offset)

{

// 在这里处理设备写入的操作

printk(KERN_INFO "This is device_write.n");

return 0;

}

(七)声明模块信息

MODULE_LICENSE("GPL"); // 指定模块的许可证信息

MODULE_AUTHOR("Your Name"); // 指定模块的作者信息

MODULE_DESCRIPTION("A simple Hello World driver"); // 指定模块的描述信息

完整mydevice.c示例源码

#include <linux/module.h>       // 包含模块相关函数的头文件

#include <linux/fs.h>           // 包含文件系统相关函数的头文件

#include <linux/uaccess.h>      // 包含用户空间数据访问函数的头文件

#include <linux/cdev.h>         //包含字符设备头文件

#define DEVICE_NAME "mydevice"  // 设备名称

static dev_t dev_num;   //分配的设备号

struct  cdev my_cdev;          //字符设备指针

int major;  //主设备号

int minor;  //次设备号

static int device_open(struct inode *inode, struct file *file)

{

// 在这里处理设备打开的操作

printk(KERN_INFO "This is device_open.n");

return 0;

}

static int device_release(struct inode *inode, struct file *file)

{

// 在这里处理设备关闭的操作

printk(KERN_INFO "This is device_release.n");

return 0;

}

static ssize_t device_read(struct file *file, char __user *buffer, size_t length, loff_t *offset)

{

// 在这里处理设备读取的操作

printk(KERN_INFO "This is device_read.n");

return 0;

}

static ssize_t device_write(struct file *file, const char __user *buffer, size_t length, loff_t *offset)

{

// 在这里处理设备写入的操作

printk(KERN_INFO "This is device_write.n");

return 0;

}

static struct file_operations fops = {

.owner = THIS_MODULE,

.open = device_open,

.release = device_release,

.read = device_read,

.write = device_write,

};

static int __init mydevice_init(void)

{

int ret;

// 在这里执行驱动程序的初始化操作

// 注册字符设备驱动程序

ret = alloc_chrdev_region(&dev_num,0,1,DEVICE_NAME);

if (ret < 0) {

printk(KERN_ALERT "Failed to allocate device number: %dn", ret);

return ret;

}

major=MAJOR(dev_num);   //获取主设备号

minor=MINOR(dev_num);   //获取次设备号

printk(KERN_INFO "major number: %dn",major);

printk(KERN_INFO "minor number: %dn",minor);

my_cdev.owner = THIS_MODULE;

cdev_init(&my_cdev,&fops);   //初始化字符设备结构体

cdev_add(&my_cdev,dev_num,1);   //将字符设备添加到内核中

printk(KERN_INFO "Device registered successfully.n");

return 0;

}

static void __exit mydevice_exit(void)

{

// 在这里执行驱动程序的清理操作

// 删除字符设备

cdev_del(&my_cdev);

// 注销字符设备驱动程序

unregister_chrdev(0, DEVICE_NAME);

printk(KERN_INFO "Device unregistered.n");

}

module_init(mydevice_init);

module_exit(mydevice_exit);

MODULE_LICENSE("GPL");      // 指定模块的许可证信息

MODULE_AUTHOR("Your Name"); // 指定模块的作者信息

MODULE_DESCRIPTION("A simple character device driver"); // 指定模块的描述信息

编译

复制helloworld驱动中的Makefile文件,将其中的hello.o修改为mydevice.o,效果如下:

设置环境变量,编译:

 . /opt/fsl-imx-x11/4.1.15-2.0.0/environment-setup-cortexa7hf-neon-poky-linux-gnueabi

elf@ubuntu:~/work/test/02_字符驱动/mydevice$ make

将生成的mydevice.ko文件拷贝到开发板中。

编写测试应用源码device_app.c

驱动中已经给应用层提供了open、read、write的接口,接下来应用层就可以进行相应的系统调用。

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

#include <sys/ioctl.h>

#include <errno.h>

#include <fcntl.h>

#define DEV_NAME "/dev/mydevice"

int main(int argc, char *argv[])

{

int reg;

int fd = 0;

int dat = 0;

fd = open (DEV_NAME, O_RDWR);

if (fd < 0) {

perror("Open "DEV_NAME" Failed!n");

exit(1);

}

reg = read(fd, &dat, 1);

if (reg<0) {

perror("read "DEV_NAME" Failed!n");

exit(1);

}

dat = 0;

reg = write(fd, &dat, 1);

if (reg<0) {

perror("write "DEV_NAME" Failed!n");

exit(1);

}

close(fd);

return 0;

}

设置环境变量:

 . /opt/fsl-imx-x11/4.1.15-2.0.0/environment-setup-cortexa7hf-neon-poky-linux-gnueabi

编译,将生成的device_app文件拷贝到开发板中:

elf@ubuntu:~/work/test/02_字符驱动/device_app$ $CC device_app.c -o device_app

测试

(一)加载驱动

root@ELF1:~# insmod mydevice.ko

major number: 246

minor number: 0

Device registered successfully.

(二)可以看到生成的主设备号为246,次设备号为0。我们需要使用主设备号和次设备号手动创建/dev/下的设备节点。

root@ELF1:~# mknod /dev/mydevice c 246 0

mknod的基本语法如下:

mknod <device_file> <file_type> <major> <minor>

<device_file>:指定要创建的设备文件节点的路径和名称;

<file_type>:指定设备文件的类型,可以是b(块设备)或c(字符设备);

<major>:指定设备文件的主设备号;

<minor>:指定设备文件的次设备号;

(三)运行测试应用程序

root@ELF1:~# ./device_app

This is device_open.

This is device_read.

This is device_write.

This is device_release.

(四)卸载驱动

root@ELF1:~# rmmod mydevice.ko

Device unregistered.

点赞
收藏
评论
分享
加入交流群
举报

相关推荐

登录即可解锁
  • 海量技术文章
  • 设计资源下载
  • 产业链客户资源
  • 写文章/发需求
立即登录