嵌入式培训机构四个月实训课程笔记(完整版)-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配置)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/471299.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

HCIA-HarmonyOS设备开发认证V2.0-轻量系统内核内存管理-静态内存

目录 一、内存管理二、静态内存2.1、静态内存运行机制2.2、静态内存开发流程2.3、静态内存接口2.4、实例2.5、代码分析&#xff08;待续...&#xff09;坚持就有收货 一、内存管理 内存管理模块管理系统的内存资源&#xff0c;它是操作系统的核心模块之一&#xff0c;主要包括…

【精选】Java面向对象进阶——接口

&#x1f36c; 博主介绍&#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是 hacker-routing &#xff0c;很高兴认识大家~ ✨主攻领域&#xff1a;【渗透领域】【应急响应】 【Java】 【VulnHub靶场复现】【面试分析】 &#x1f389;点赞➕评论➕收藏 …

TCP_IP(6)

网络层 在复杂的网络环境中确定一个合适的路径. IP协议 与TCP协议并列,都是网络体系中最核心的协议. 基本概念 主机:配有IP地址,但是不进行路由控制的设备; 路由器:即配有IP地址,又能进行路由控制; 节点:主机和路由器的统称; 协议头格式 4位版本号(version):指定IP协议的版…

hope实验室预备役第三次测试题解

目录 1.选数 2.奇怪的电梯 3.无线通讯网 4. Rotate Colored Subsequence 5.LOWER 6.Error Correction 1.选数 P1036 [NOIP2002 普及组] 选数 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 题目描述 已知 n 个整数 1,2,⋯ ,x1​,x2​,⋯,xn​&#xff0c;以及 1 个整…

C# VS2022+WinForm+Oracle19.3+Excel,根据数据库表定义书生成SQL

目标&#xff1a; 用Excel写数据库的表的定义书&#xff0c;用该工具生成SQL&#xff0c;在客户端执行&#xff0c;把表结构导入数据库&#xff0c;生成真正的表 Github代码下载 目录 0.完成下面开发环境的准备1 操作系统Win11 专业版 21H22 oracle 19.33 Visual Studio Commun…

2024年【安全员-B证】考试报名及安全员-B证复审模拟考试

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 安全员-B证考试报名考前必练&#xff01;安全生产模拟考试一点通每个月更新安全员-B证复审模拟考试题目及答案&#xff01;多做几遍&#xff0c;其实通过安全员-B证实操考试视频很简单。 1、【多选题】下列临边防护栏…

蓝桥杯:C++排序

排序 排序和排列是算法题目常见的基本算法。几乎每次蓝桥杯软件类大赛都有题目会用到排序或排列。常见的排序算法如下。 第(3)种排序算法不是基于比较的&#xff0c;而是对数值按位划分&#xff0c;按照以空间换取时间的思路来排序。看起来它们的复杂度更好&#xff0c;但实际…

linux的make和makefile

一.什么是make和makefile makefile作用&#xff1a; 一个工程中的源文件不计数&#xff0c;其按类型、功能、模块分别放在若干个目录中&#xff0c;makefile定义了一系列的规则来指定&#xff0c;哪些文件需要先编译&#xff0c;哪些文件需要后编译&#xff0c;哪些文件需要重…

【python之美】减少人工成本之批量修改TXT文件名称_2

批量修改文件名称 怎么批量创建txt文件&#xff0c;看上期特产 源文件如下&#xff1a; 批量修改代码 import ospath "C:\\Users\\Administrator\\Desktop\\text\\" file_names os.listdir(path) print(file_names)i 1 for file_name in file_names:name fil…

VueCLI核心知识4:动画效果、过渡效果

1 动画效果 【代码】 <template><div><button click"isShow !isShow">显示/隐藏</button><!-- <transition name"xxx" :appear"true"> 可以指定name属性&#xff0c;也可以不指定&#xff0c;name属性在有…

数据分析 | 手敲皮尔逊相关系数计算代码 | Python

import random random.seed(42) import numpy as np计算均值 def mean(x):return sum(x) / len(x)计算偏差 def derta(x):x_bar mean(x)result [x_i - x_bar for x_i in x]return result计算协方差 def covariance(a, b):dot_list [a_i * b_i for a_i, b_i in zip(derta(a),…

Codeforces Round 926 (Div. 2)(A~D)

文章目录 ABCD A 输出最大值减最小值&#xff0c;或者排序算一下答案 #include <bits/stdc.h> #define int long long #define rep(i,a,b) for(int i (a); i < (b); i) #define fep(i,a,b) for(int i (a); i > (b); --i) #define pii pair<int, int> #d…