嵌入式培训机构四个月实训课程笔记(完整版)-Linux ARM驱动编程第五天-ARM Linux编程之字符设备驱动(物联技术666)

本文主要是介绍嵌入式培训机构四个月实训课程笔记(完整版)-Linux ARM驱动编程第五天-ARM Linux编程之字符设备驱动(物联技术666),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

链接:https://pan.baidu.com/s/1V0E9IHSoLbpiWJsncmFgdA?pwd=1688
提取码:1688

教学内容:

1、内核模块的简单框架:

__init __exit执行完后就释放空间

简单框架:包含三个部分

1)模块初始化和模块退出函数

2)注册模块函数

3)模块许可

//***************************************************

#include <linux/module.h>    /*module_init()*/

#include <linux/kernel.h>        /* printk() */

#include <linux/init.h>            /* __init __exit */

static int __init myModule_init(void)   //红色名可自定义,使用static保证只能在本文件中调用

{

       /* Module init code */

       PRINTK("myModule_init\n");

       return 0;

}

static void __exit myModule_exit(void)          //红色名可自定义

{

       /* Module exit code */

       PRINTK("myModule_exit\n");

       return;

}

module_init(myModule_init);    //注册模块函数

module_exit(myModule_exit);   //注册

MODULE_AUTHOR("zhangda");       /*模块作者,可选*/

MODULE_LICENSE("GPL");                   /*模块许可证明,描述内核模块的许可权限,必须*/

MODULE_DESCRIPTION("A simple Hello World Module"); /*模块说明,可选*/

//******************************************

makefile的编写

驱动有两种方式,一为内核树之内,一为内核树以外,前者有点复杂,涉及到将驱动放到合适的内核树目录,修改相应的Makefile以及Kconfig文件,不过,天下无难易之事,为之,难亦不难了;后者所做的劳动就不用那么多了。这个Makfile只适合于后者。此外,内核的Makefile跟一般的应用程序的Makefile不太一样,就像驱动程序跟应用程序,内核头文件跟应用程序头文件等等,没必然关系,或者说是两码事,两者不能混为一谈。再有一点,驱动是跟内核打交道的,你的系统中必须有一个内核源代码。

//*******************************************

obj-m := module_test.o    //目标文件

ifneq ($(KERNELRELEASE),)

KERNELDIR = $(KERNELRELEASE)

else

KERNELDIR = /home/zhangda/linux_kernel/linux-2.6.34    //内核源代码地址

endif

$(PWD) := $(shell pwd)

default:

       $(MAKE) -C $(KERNELDIR) M=$(PWD) modules

       rm -rf *.o *.mod.c *.order *.symvers

clean:

       rm -rf *.ko

//***************************************************

2、模块间符号的相互引用

       在模块编程或内核编程中经常会遇到需要调用其它模块中符号的情况,在内核中专门有一张表来管理这些符号,可以通过 cat/proc/kallsyms查看该内核符号表我们也可以自己编写一个模块,并导出其符号供其它模块使用。

//****************************************

//****************************************

上图1,通过EXPORT_SYMBOL导出模块符号,方便图2使用符号函数。由于是给文件外函数使用,所以不能使用static修饰函数。

3linux字符设备驱动结构

描述字符设备的结构体(详细参考内核和宋宝华的设备驱动书)

//**************************

struct cdev {

       struct kobject kobj;     //内嵌kobject 对象

       struct module *owner;      //所属模块

       const struct file_operations *ops;    //文件操作结构

       struct list_head list;   

       dev_t dev;    //设备号

       unsigned int count;

};

//*****************************

设备号为32位,高12位为主设备号,低20位为次设备号;

此结构体描述了字符设备的信息,其中struct file_operations *ops结构体是向用户提高SPI接口函数的重要结构体。

file_operations :

//*******************************************

#include <linux/fs.h>

struct file_operations {

       struct module *owner;

// 指向拥有该结构的模块的指针,避免正在操作时被卸载,一般为初始化为THIS_MODULES

       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 *);//写接口

       ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);

       ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);

       int (*readdir) (struct file *, void *, filldir_t);

       unsigned int (*poll) (struct file *, struct poll_table_struct *);

       int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);//io控制接口

       long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);

       long (*compat_ioctl) (struct file *, unsigned int, unsigned long);

       int (*mmap) (struct file *, struct vm_area_struct *);

       int (*open) (struct inode *, struct file *);//打开接口

       int (*flush) (struct file *, fl_owner_t id);

       int (*release) (struct inode *, struct file *);//关闭接口

       int (*fsync) (struct file *, struct dentry *, int datasync);

       int (*aio_fsync) (struct kiocb *, int datasync);

       int (*fasync) (int, struct file *, int);

       int (*lock) (struct file *, int, struct file_lock *);

       ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);

       unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);

       int (*check_flags)(int);

       int (*flock) (struct file *, int, struct file_lock *);

       ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);

       ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);

       int (*setlease)(struct file *, long, struct file_lock **);

};

//****************************************

字符驱动设备的注册:

int register_chrdev(unsigned int major, const char *name,const struct file_operations *fops)

(内联函数)

第一个参数:主设备号,int型,0为内核自动分配

第二个参数:设备名

第三个参数:file_operations的结构体地址

返回分配的主设备号,大于0,错误返回负值。

void unregister_chrdev(MAJOR_NR, DRIVER_NAME);(内联函数)

第一个参数:分配的主设备号

第二个参数:设备名

//*********************

MAJOR_NR = register_chrdev(MAJOR_NR, DRIVER_NAME, &GPG_fops);

if(MAJOR_NR < 0)

{

       PRINTK("register char device fail!\n");

       return MAJOR_NR;

}

unregister_chrdev(MAJOR_NR, DRIVER_NAME);

//*****************************

自动创建节点:

#include<linux/device.h>

static struct class *my_class;

my_class= class_create(THIS_MODULE, "my_class");

device_create(my_class,NULL,dev_n,NULL,"hello");

注意:dev_n = MKDEV(MAJOR_NR, MINOR_NR);

设备卸载删除类和设备节点

device_destroy(my_class,dev_n);

class_destroy(my_class);

//******************************

my_class=class_create(THIS_MODULE,"udev_gpg");

device_create(my_class,NULL, MKDEV(MAJOR_NR, MINOR_NR), NULL,DRIVER_NAME);

device_destroy(my_class,MKDEV(MAJOR_NR, MINOR_NR));

class_destroy(my_class);

//******************************

用户态与内核态数据的交互:

用户应用程序与驱动程序分属于不同的进程空间,因此二者之间的数据应当采用以下函数进行交换

#include <asm/uaccess.h>

copy_to_user(user_buffer, kernel_buffer, n)

//从内核空间拷贝n字节数据到用户空间

copy_from_user(kernel_buffer, user_buffer, n)

//从用户空间拷贝n字节数据到内核空间

put_user(kernel_value, user_buffer)

//从内核空间拷贝一数据变量到用户空间

get_user(kernel_value, user_buffer)

//从用户空间拷贝一数据变量到内核空间

(内核空间数据可是任意类型)

字符设备驱动的流程:

1)、建立__init和__exit函数;并注册这2个函数module_init,module_exit,声明MODULE_LICENSE;

2)、创建file_operations结构体,并指明函数地址;

3)、字符设备驱动的注册,在__init函数里面注册,在__exit注销;

4)、在__init函数里面完成节点创建,在__exit注销节点;

5)、编写file_operations结构体中的函数,具体功能可以结合裸机驱动编写功能

例如:

//***********************************************

#include <linux/module.h>             /*module_init()*/

#include <linux/kernel.h> /* printk() */

#include <linux/init.h>      /* __init __exit */

#include <linux/fs.h>        /* file_operation */

#include <asm/uaccess.h> /* copy_to_user, copy_from_user */      

#include <linux/device.h>  /*class ,class_create ,device_create 等*/

#define GPGCON      (*(volatile unsigned long *)S3C2410_GPGCON) //虚拟地址

#define GPGDAT     (*(volatile unsigned long *)S3C2410_GPGDAT)

#define GPGUP      (*(volatile unsigned long *)S3C2410_GPGUP)

#include "my_ioctl.h"

#define MAJOR_NAME "my_ioctl_drv"

static int MAJOR_NR = 0;

static int MINOR_NR = 0;

struct class *myclass;

static int io_open(struct inode *inode, struct file *filp)

{     }

static int io_release(struct inode *inode, struct file *filp)

{     }

static ssize_t io_read(struct file *filp, char *buf,size_t count, loff_t *f_pos)

{     }

static ssize_t io_write(struct file *filp, const char *buf,size_t count, loff_t *f_pos)

{     }

static int io_ioctl(struct inode *inode, struct file *file,unsigned int cmd, unsigned long arg)

{     }

static struct file_operations ioctl_file_opt = {

       .owner = THIS_MODULE,

       .write = io_write,

       .read = io_read,

       .ioctl = io_ioctl,

       .open = io_open,

       .release = io_release,

};

static int __init my_inctl_init(void)

{

       PRINTK("come in init\n");

       MAJOR_NR = register_chrdev(MAJOR_NR, DRIVER_NAME, &ioctl_file_opt);

       if(MAJOR_NR < 0)

       {

             PRINTK("register char device fail!\n");

             return MAJOR_NR;

       }

       myclass=class_create(THIS_MODULE,"my_ioctl");

       device_create(myclass,NULL, MKDEV(MAJOR_NR, MINOR_NR), NULL,"my_ioctl");

       return 0;

}

static void __exit my_inctl_exit(void)

{

       if(MAJOR_NR > 0)

       {

             unregister_chrdev(MAJOR_NR, DRIVER_NAME);

             device_destroy(myclass,MKDEV(MAJOR_NR, MINOR_NR));

             class_destroy(myclass);

       }

       PRINTK("exit ioctl\n");

       return;

}

module_init(my_inctl_init);

module_exit(my_inctl_exit);

MODULE_LICENSE("GPL");

MODULE_AUTHOR("HB");

//***************************************************

配置s3c2410的文件

cp arch/arm/configs/s3c2410_defconfig .config

make menuconfig(执行s3c2410配置)

这篇关于嵌入式培训机构四个月实训课程笔记(完整版)-Linux ARM驱动编程第五天-ARM Linux编程之字符设备驱动(物联技术666)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/718420

相关文章

SpringBoot3实现Gzip压缩优化的技术指南

《SpringBoot3实现Gzip压缩优化的技术指南》随着Web应用的用户量和数据量增加,网络带宽和页面加载速度逐渐成为瓶颈,为了减少数据传输量,提高用户体验,我们可以使用Gzip压缩HTTP响应,... 目录1、简述2、配置2.1 添加依赖2.2 配置 Gzip 压缩3、服务端应用4、前端应用4.1 N

Linux换行符的使用方法详解

《Linux换行符的使用方法详解》本文介绍了Linux中常用的换行符LF及其在文件中的表示,展示了如何使用sed命令替换换行符,并列举了与换行符处理相关的Linux命令,通过代码讲解的非常详细,需要的... 目录简介检测文件中的换行符使用 cat -A 查看换行符使用 od -c 检查字符换行符格式转换将

Linux系统配置NAT网络模式的详细步骤(附图文)

《Linux系统配置NAT网络模式的详细步骤(附图文)》本文详细指导如何在VMware环境下配置NAT网络模式,包括设置主机和虚拟机的IP地址、网关,以及针对Linux和Windows系统的具体步骤,... 目录一、配置NAT网络模式二、设置虚拟机交换机网关2.1 打开虚拟机2.2 管理员授权2.3 设置子

揭秘Python Socket网络编程的7种硬核用法

《揭秘PythonSocket网络编程的7种硬核用法》Socket不仅能做聊天室,还能干一大堆硬核操作,这篇文章就带大家看看Python网络编程的7种超实用玩法,感兴趣的小伙伴可以跟随小编一起... 目录1.端口扫描器:探测开放端口2.简易 HTTP 服务器:10 秒搭个网页3.局域网游戏:多人联机对战4.

Linux系统中卸载与安装JDK的详细教程

《Linux系统中卸载与安装JDK的详细教程》本文详细介绍了如何在Linux系统中通过Xshell和Xftp工具连接与传输文件,然后进行JDK的安装与卸载,安装步骤包括连接Linux、传输JDK安装包... 目录1、卸载1.1 linux删除自带的JDK1.2 Linux上卸载自己安装的JDK2、安装2.1

Java并发编程必备之Synchronized关键字深入解析

《Java并发编程必备之Synchronized关键字深入解析》本文我们深入探索了Java中的Synchronized关键字,包括其互斥性和可重入性的特性,文章详细介绍了Synchronized的三种... 目录一、前言二、Synchronized关键字2.1 Synchronized的特性1. 互斥2.

Linux卸载自带jdk并安装新jdk版本的图文教程

《Linux卸载自带jdk并安装新jdk版本的图文教程》在Linux系统中,有时需要卸载预装的OpenJDK并安装特定版本的JDK,例如JDK1.8,所以本文给大家详细介绍了Linux卸载自带jdk并... 目录Ⅰ、卸载自带jdkⅡ、安装新版jdkⅠ、卸载自带jdk1、输入命令查看旧jdkrpm -qa

Linux samba共享慢的原因及解决方案

《Linuxsamba共享慢的原因及解决方案》:本文主要介绍Linuxsamba共享慢的原因及解决方案,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录linux samba共享慢原因及解决问题表现原因解决办法总结Linandroidux samba共享慢原因及解决

Java利用JSONPath操作JSON数据的技术指南

《Java利用JSONPath操作JSON数据的技术指南》JSONPath是一种强大的工具,用于查询和操作JSON数据,类似于SQL的语法,它为处理复杂的JSON数据结构提供了简单且高效... 目录1、简述2、什么是 jsONPath?3、Java 示例3.1 基本查询3.2 过滤查询3.3 递归搜索3.4

Python中随机休眠技术原理与应用详解

《Python中随机休眠技术原理与应用详解》在编程中,让程序暂停执行特定时间是常见需求,当需要引入不确定性时,随机休眠就成为关键技巧,下面我们就来看看Python中随机休眠技术的具体实现与应用吧... 目录引言一、实现原理与基础方法1.1 核心函数解析1.2 基础实现模板1.3 整数版实现二、典型应用场景2