当前位置: 首页 > news >正文

驱动开发前传及led驱动(s5pv210)

涉及app、驱动两个的调用,包括读写操作、led驱动操作,

涉及动态映射、静态映射
makefile:

****************************
#KERN_VER = $(shell uname -r)
#KERN_DIR = /lib/modules/$(KERN_VER)/build

ubuntu的内核源码树
在ubuntu中编译安装模块就要索引到这个目录位置

root@ubuntu:/lib/modules# uname -r
4.15.0-142-generic

root@ubuntu:/lib/modules/4.15.0-142-generic/build# ls
arch    Documentation  include  Kconfig   mm              scripts   ubuntu
block   drivers        init     kernel    Module.symvers  security  usr
certs   firmware       ipc      lib       net             sound     virt
crypto  fs             Kbuild   Makefile  samples         tools

****************************************

KERN_DIR = /usr/local/drivers/kernel
ubuntu中要操作的内核源码目录位置


******************************
make -C $(KERN_DIR) M=`pwd` modules

这个就是索引到$(KERN_DIR)这个目录位置 然后进行modules ,将输出的文件放到
M=`pwd`,就是当前编译的位置

清除

.PHONY : clean
clean:
    make -C $(KERN_DIR) M=`pwd` modules clean 

****************************
在ubuntu中装载时的模块信息:

insmod :
装载模块

lsmod:
查看装载的模块信息

root@ubuntu:/mnt/hgfs/Embedded/drivers/2chardev/1moduletest# insmod module_test.ko
root@ubuntu:/mnt/hgfs/Embedded/drivers/2chardev/1moduletest# lsmod
Module                  Size  Used by
module_test            16384  0

rmmod:
卸载模块

root@ubuntu:/mnt/hgfs/Embedded/drivers/2chardev/1moduletest# rmmod module_test.ko
root@ubuntu:/mnt/hgfs/Embedded/drivers/2chardev/1moduletest# lsmod
Module                  Size  Used by
rfcomm                 77824  2


modinfo:
查看模块信息
文件名路径filename
作者author
许可证(有双许可)license
文件名称name
用于获取Linux内核模块源代码版本号srcversion
魔数vermagic(这里在ubuntu装载,就是ubuntu的版本号信息)


root@ubuntu:/mnt/hgfs/Embedded/drivers/2chardev/1moduletest# modinfo module_test.ko
filename:       /mnt/hgfs/Embedded/drivers/2chardev/1moduletest/module_test.ko
author:         sugar
license:        GPL
srcversion:     A85C0A45B7BF7A40A03F847
depends:        
retpoline:      Y
name:           module_test
vermagic:       4.15.0-142-generic SMP mod_unload 


*************************
在要操作的内核时的模块信息

root@ubuntu:/mnt/hgfs/Embedded/drivers/2chardev/1moduletest# modinfo module_test.ko
filename:       /mnt/hgfs/Embedded/drivers/2chardev/1moduletest/module_test.ko
author:         sugar
license:        GPL
depends:        
vermagic:       2.6.35.7 preempt mod_unload ARMv7 


******************************
在 开发板中操作:
lsmod insmod rmmod 都时一样的
只有 modinfo出错:

[@sugar modules]# modinfo module_test.ko 
modinfo: can't open '/lib/modules/2.6.35.7/modules.dep': No such file or directory
[@sugar /lib]# mkdir modules
[@sugar modules]# mkdir 2.6.35.7
[@sugar modules]# depmod
[@sugar modules]# modinfo module_test.ko 
filename:       module_test.ko

就是缺失了文件,补充上文件后,使用depmod指令就可以自动生成三个文件
[@sugar modules]# cd 2.6.35.7/
[@sugar 2.6.35.7]# ls
modules.alias    modules.dep      modules.symbols

这样之后就可以使用modinfo查看模块信息了

******************************************************
这里开始对文件填充,完成驱动的设计

********
首先明白我们要完成的是字符设备的驱动,那么就要完成对设备的注册,
不然内核无法识别我们完成驱动,那么就无法完成调用

*******
1、/linux/Fs.h
static inline int register_chrdev(unsigned int major, const char *name,
                  const struct file_operations *fops)
{
    return __register_chrdev(major, 0, 256, name, fops);
}

字符设备驱动注册的原函数
major:设备号     (先查看哪些设备号没有被使用,不能申请一个已经使用的设备号,要么就填0,让内核自动帮分配设备号,这样就可以万无一失)
name:名字
fops:绑定的文件操作,就是file_operation


这个函数应该和
static int __init chardev_init(void)
模块装载函数放在一起,就是初始化时就完成注册


注销设备驱动
static inline void unregister_chrdev(unsigned int major, const char *name)
{
    __unregister_chrdev(major, 0, 256, name);
}

只需要设备号major和名字name

注册和注销一定是要存在的,都是成对
就像module_init
    module_exit


2、/linux/Fs.h

struct file_operations{}
这个就是内核完成对相应函数操作的一个结构体
为什么需要这个,原因就是这个是驱动相关的,这个有相应的操作,
比如read、write等,但是,我们的操作在app应用层的read、write,
这很明显不是同一个,所以需要这一个结构体,完成应用层和驱动的一个联系
在app中调用,然后通过这个结构体,用函数指针绑定一个驱动中实例化的同样的
函数,完成操作

例子:
file_operations
/* File operations struct for character device */
static const struct file_operations twa_fops = {
    .owner        = THIS_MODULE,
    .unlocked_ioctl    = twa_chrdev_ioctl,
    .open        = twa_chrdev_open,
    .release    = NULL
};

文件名字改为自己要操作的函数名字
例如:
/* File operations struct for character device */
static const struct file_operations test_fops = {
    .owner        = THIS_MODULE,
    
    .open        = test_chrdev_open,
    .release    = test_chrdev_close
};

然后根据绑定函数完成相应函数功能

所以上面的字符设备注册函数:
ret = register_chrdev(MYMAJOR,MYNAME,&test_fops);
返回值,0是成功注册,负数时失败


**************************
开发板操作
0、如果用自定义设备号,查看开发板已有设备号

1、写好驱动函数和app函数
编译到开发板执行

2、insmod  .ko
完成注册

3、查看注册的设备号

4、用mknod 创建设备名字
mknod  /dev/name c 250 0

name是自己创建的名字
250是主设备号,0是次设备号

[root@sugar /root]# insmod module_test.ko 
[  775.991976] register_chrdev success...
[root@sugar /root]# cat /proc/devices 
[root@sugar /root]# mknod /dev/test c 250 0
[root@sugar /root]# ./app 
[  812.862579] test_chrdev_open success
[  812.864797] test_chrdev_close success

*************************
这个是完成一个读写操作,
首先是read、write函数
static ssize_t test_read(struct file *file, __user char *buffer, size_t count,
            loff_t *ppos)

static ssize_t test_write(struct file *file, const __user char *buffer,
             size_t count, loff_t *ppos)
然后是
关系到app 内核的两个内存的两个读写问题
联系到两个函数
copy_from_user
copy_to_user
函数原型:
static inline unsigned long __must_check copy_from_user(void *to, const void __user *from, unsigned long n)
{
    if (access_ok(VERIFY_READ, from, n))
        n = __copy_from_user(to, from, n);
    else /* security hole - plug it */
        memset(to, 0, n);
    return n;
}


static inline unsigned long __must_check copy_to_user(void __user *to, const void *from, unsigned long n)
{
    if (access_ok(VERIFY_WRITE, to, n))
        n = __copy_to_user(to, from, n);
    return n;
}

copy_from_user
注意函数内参数意义:
这个是从user中复制数据到内核中,
前一个参数to,为kbuf,为目标buf,后一个from就是源数据,从user中读取过来

copy_to_user
这个则对应read操作,从内核中读取到user

开发板中的操作:
[root@sugar /root]# insmod  module_test.ko 
[   42.113445] register_chrdev success...
[root@sugar /root]# cat /proc/devices 
250 sugar
[root@sugar /root]# mknod /dev/test c 250 0
[root@sugar /root]# ./app 
[   83.790004] test_chrdev_open success
[   83.792262] test_write ok
[   83.794697] copy_from_user success ...
[   83.798570] test_read ok
[   83.800958] copy_to_user success ... 
[   83.804642] test_chrdev_close success
open successthe read buf is hello_test


******************************************
上面就是一个驱动前的一个框架,未涉及到相关硬件驱动

*************************************
写相关硬件驱动前,首先明白一个静态映射
用到操作系统,里面的内存必定是用到虚拟地址

注意:
不同版本内核中的静态映射表表位置不同
不同soc的静态映射位置、文件名不同
所谓的映射表就是头文件中的宏定义

*******************************************

所以,了解静态映射表
1、虚拟地址基地址
arch/arm/plat-samsung/include/plat/map-base.h
#define S3C_ADDR_BASE    (0xFD000000)

Samsung移植时确定的静态映射表的基地址,
表中所有的基地址都是以这个地址+偏移量得到的

2、主映射表
arch/arm/plat-s5p/include/plat/map-s5p.h

#define S5P_VA_GPIO            S3C_ADDR(0x00500000)
#define S5P_VA_UART0        (S3C_VA_UART + 0x0)
#define S5P_VA_UART1        (S3C_VA_UART + 0x400)
.
.
.
cpu在安排寄存器地址时不是随意分配的,而是按模块区分的
例如gpio、uart....
每一个模块内的地址都是连续的
所以内核在定义寄存器地址时都是先找到基地址,
然后用基地址+偏移量来寻找具体的一个寄存器

map-s5p.h中定义的就是要用到的几个模块的寄存器基地址。
map-s5p.h中定义的是模块的寄存器基地址的虚拟地址。


3、现在要操作的是led,用到gpio
gpio相关的主映射表位于
arch/arm/mach-s5pv210/include/mach/regs-gpio.h
用到GPJ0
表中是GPIO的各个端口的基地址的定义
#define S5PV210_GPJ0_BASE        (S5P_VA_GPIO + 0x240)

4、GPJ0的具体寄存器定义:
arch/arm/mach-s5pv210/include/mach/gpio-bank.h

#define S5PV210_GPJ0CON            (S5PV210_GPJ0_BASE + 0x00)
#define S5PV210_GPJ0DAT            (S5PV210_GPJ0_BASE + 0x04)
#define S5PV210_GPJ0PUD            (S5PV210_GPJ0_BASE + 0x08)
#define S5PV210_GPJ0DRV            (S5PV210_GPJ0_BASE + 0x0c)
#define S5PV210_GPJ0CONPDN        (S5PV210_GPJ0_BASE + 0x10)
#define S5PV210_GPJ0PUDPDN        (S5PV210_GPJ0_BASE + 0x14)

总结:
实质就是一个虚拟地址总基地址+各个模块的基地址+模块内端口的基地址+模块内端口的具体寄存器地址

S5PV210_GPJ0CON     = S3C_ADDR_BASE+0x00500000+ 0x240 + 0x00
                =    0xFD000000    + 0x00500000    + 0x240 + 0x00
                0xFD500240


**************************
动态:

建立动态映射(静态动态两个映射可以共存的)
建立的过程
首先是申请需要映射的内存资源
然后才是到 真正用来映射的物理地址到虚拟地址的转换

(这里因为要操作led,用到gpj0)
#define GPJ0CON_PA    0xe0200240
#define GPJ0DAT_PA     0xe0200244

unsigned int *pGPJ0CON;
unsigned int *pGPJ0DAT;


1、request_mem_region
向内核申请需要映射的资源
if (!request_mem_region(GPJ0CON_PA, 4, "GPJ0CON"))
        return -EBUSY;
    if (!request_mem_region(GPJ0DAT_PA, 4, "GPJ0DAT"))
        return -EBUSY;

2、ioremap
真正用来实现映射,传进去物理地址,映射返回一个虚拟地址
//传入物理地址转化为虚拟地址
    pGPJ0CON = ioremap(GPJ0CON_PA, 4);
    pGPJ0DAT = ioremap(GPJ0DAT_PA, 4);

ioremap 的返回值是一个指针(unsigned int *)

************
用上面这两个就可以完成一个物理地址到虚拟地址的一个申请并映射过程
直接对两个虚拟地址进行操作即可
*pGPJ0CON = 0x11111111;
*pGPJ0DAT = ((0<<3) | (0<<4) | (0<<5));
*pGPJ0DAT = ((1<<3) | (1<<4) | (1<<5));


******************************
申请、映射
结束的时候也一定要进行
解除映射、释放申请
iounmap
iounmap(pGPJ0CON);
iounmap(pGPJ0DAT);
传进去一个映射返回的虚拟地址

release_mem_region
release_mem_region(GPJ0CON_PA, 4);
release_mem_region(GPJ0DAT_PA, 4);
传进去一个物理地址 和大小

相关文章:

  • 使用OceanBase的Oblogminer进行日志挖掘的实践
  • NLP进化史:从规则模板到思维链推理,七次范式革命全解析
  • Vue3 + Element Plus 获取表格列信息
  • Jupyter notebook中的感叹号!魔法命令介绍
  • 爱普生RX8111CE实时时钟模块在汽车防盗系统中的应用
  • 亚远景-如何高效实施ASPICE认证标准:汽车软件企业的实践指南
  • TIA Portal V20HMI仿真时数值无法写入虚拟plc解决教程
  • HOT 100 | 73.矩阵置零、54.螺旋矩阵、48.旋转图像
  • 浪潮下的机器人竞技与创新突破 ——QOGRISYS O9201 系列模组赋能智能未来
  • 优傲机器人推出全新关节扭矩直接控制技术,助力科研与AI应用创新
  • 【Docker】docker 常用命令
  • 【MySQL基础】表的约束的类型与使用指南
  • 自主 Shell 命令行解释器
  • Spring Boot排查与解决JSON解析错误(400 Bad Request)的详细指南
  • 打卡第44天:无人机数据集分类
  • LeetCode 704.二分查找
  • 【Qt】信号与槽
  • 深度解析Linux用户生态:账户架构设计与系统运维实战技巧》
  • 轻量级密码算法PRESENT的C语言实现(无第三方库)
  • Vue3 + TypeScript 操作第三方库(Element Plus 的 ElTable)的内部属性
  • wordpress obj cache/吉林seo推广
  • 北京高端网站建设公司浩森宇特/百度怎样免费发布信息
  • 网站企业建设方案/seo编辑是干什么的
  • 武汉云时代网站建设公司怎么样/重庆seo网络优化咨询热线
  • 怎么做网站封面上的图/长春网站优化页面
  • 发布asp.net网站到虚拟主机/长春网站优化方案