旧接口注册LED字符驱动设备(静态映射)
#include <linux/init.h> // __init __exit
#include <linux/module.h> // module_init module_exit #include <linux/fs.h> //file_operations #include <asm/uaccess.h> //copy_from_user copy_to_user #include <mach/regs-gpio.h>
#include <mach/gpio-bank.h> #include <asm/string.h> #define rGPJ0CON *((volatile unsigned int *)S5PV210_GPJ0CON)
#define rGPJ0DAT *((volatile unsigned int *)S5PV210_GPJ0DAT) static int led_open(struct inode *inode, struct file *file);
ssize_t led_read(struct file *file, char __user *user, size_t count, loff_t *loff);
ssize_t led_write(struct file *file, const char __user *user, size_t count, loff_t *loff);
static int led_release(struct inode *inode, struct file *file); static int led_major = -;
static char kbuf[] = {};
static const struct file_operations led_fops = {
.owner = THIS_MODULE,
.open = led_open,
.read = led_read,
.write = led_write,
.release = led_release,
}; int led_open(struct inode *inode, struct file *file)
{
printk(KERN_INFO "led_open successful\n");
return ;
} ssize_t led_read(struct file *file, char __user *user, size_t ucount, loff_t *loff)
{
printk(KERN_INFO "led_read successful\n");
if (copy_to_user(user,kbuf , ucount))
{
printk(KERN_INFO "copy_to_user fail\n");
return -EINVAL;
}
printk(KERN_INFO "copy_to_user successful\n");
return strlen(kbuf);
} ssize_t led_write(struct file *file, const char __user *user, size_t ucount, loff_t *loff)
{
printk(KERN_INFO "led_write successful\n");
memset(kbuf,,sizeof(kbuf));
if (copy_from_user(kbuf, user, ucount))
{
printk(KERN_INFO "copy_from_user fail\n");
return -EINVAL;
} if(!strcmp(kbuf,"on"))
{
rGPJ0CON &=0xff000fff;
rGPJ0CON |=0x00111000;
rGPJ0DAT &=~((0x01<<)|(0x01<<)|(0x01<<));
}
else if(!strcmp(kbuf,"off"))
{
rGPJ0CON &=0xff000fff;
rGPJ0CON |=0x00111000;
rGPJ0DAT |=((0x01<<)|(0x01<<)|(0x01<<));
}
return ucount;
printk(KERN_INFO "copy_from_user successful\n");
} int led_release(struct inode *inode, struct file *file)
{
printk(KERN_INFO "led_release successful\n");
return ;
} // 模块安装函数
static int __init chrdev_init(void)
{
printk(KERN_INFO "chrdev_init successful\n"); if ((led_major = register_chrdev (, "led_dev", &led_fops)) < )
{
printk(KERN_WARNING "led_module.c: Failed to register character device.");
return -EINVAL;
}
return ;
} // 模块卸载函数
static void __exit chrdev_exit(void)
{ unregister_chrdev(led_major,"led_dev");
printk(KERN_INFO "chrdev_exit successful\n"); } module_init(chrdev_init);
module_exit(chrdev_exit); // MODULE_xxx这种宏作用是用来添加模块描述信息
MODULE_LICENSE("GPL"); // 描述模块的许可证
MODULE_AUTHOR("Musk <qq:739112417>"); // 描述模块的作者
MODULE_DESCRIPTION("led driver"); // 描述模块的介绍信息
MODULE_ALIAS("led test driver"); // 描述模块的别名信息
上述程序是led非常简陋,手动注册的字符驱动代码。
此部分我从小往上分析。
一. 模块部分
1.1. 模块相关分析参考上篇文章
二.驱动设备注册
2.1. 认识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 *);
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);
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 *, 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 **);
};
linux应用层使用file_operations结构访问驱动程序的函数,这个结构的每一个成员的名字都对应着一个调用。
2.2. 注册&卸载字符驱动函数
static inline int register_chrdev(unsigned int major, const char *name,
const struct file_operations *fops)
{
return __register_chrdev(major, , , name, fops);
} static inline void unregister_chrdev(unsigned int major, const char *name)
{
__unregister_chrdev(major, , , name);
}
2.2.1. 上述两个函数一般是成对出现的,使用注册了,在不使用时可以卸载。
2.2.2. 需要注意的,register_chrdev注册时,注册的设备子设备号为0,@major参数对应需要注册的设备的主设备号,当此参数为0时表示由内核分配主设备号。
2.2.3. 在内核管理设备时,使用数组管理所有设备(1~255),其中主设备号对应数组的下标。
2.2.4. 使用cat /proc/devices查看内核中已经注册过的字符设备驱动(和块设备驱动)
三. 内核空间与用户空间数据交互
3.1. 由于内核空间与用户空间的内存不能直接互访,因此借助函数copy_to_user()完成用户空间到内核空间的复制,函数copy_from_user()完成内核空间到用户空间的复制
3.2. 认识copy_to_user函数(函数在arch/arm/include/asm/uaccess.h)
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;
}
3.2.1. 此函数表示从内核空间拷贝n个字节到用户空间。
3.2.2. 先看函数的三个参数:*to是内核空间的指针,*from是用户空间指针,n表示从内核空间拷贝到用户空间数据的字节数。如果成功执行拷贝操作,则返回0,否则返回还没有完成拷贝的字节数
3.3. 认识copy_from_user函数(函数在arch/arm/include/asm/uaccess.h)
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, , n);
return n;
}
3.3.1. 此函数表示从用户空间拷贝n个字节到内核空间。
3.3.2. 先看函数的三个参数:*to是内核空间的指针,*from是用户空间指针,n表示从用户空间拷贝内核空间到数据的字节数。如果成功执行拷贝操作,则返回0,否则返回还没有完成拷贝的字节数
四. 设备节点(设备文件)的手动创建
4.1. 关于设备节点(设备文件)
4.1.1. 在Linux中,所有的设备访问都是通过文件的方式,一般的数据文件程序普通文件,设备节点称为设备文件
4.1.2. 对应字符类设备文件的关键信息是:设备号 = 主设备号 + 次设备号,使用ls -l去查看设备文件,就可以得到这个设备文件对应的主次设备号。
4.2. 创建设备节点
4.2.1. 使用mknod创建设备文件:mknod /dev/xxx c 主设备号 次设备号,其中xxx:表示设备节点名称(应用层可以使用open打开此文件)
五. 驱动中操作硬件
5.1. 虚拟地址
5.1.1. 由于内核层使用虚拟地址,无法字节使用物理地址操作寄存器。
5.1.2. 内核中物理地址映射分为:动态和静态
5.1.3. 静态映射方法的特点:
a. 在内核启动时建立静态映射表,在内核关机时销毁,中间一直有效,优点是执行效率高,缺点是始终占用虚拟地址空间,空间利用率低。
b. 不同版本内核静态映射表位置,文件名可能不同
c. 不同的SOC静态映射表位置,文件名可能不同
d. 所谓的映射表其实是头文件中的宏定义
5.1.4. 动态映射方法的特点:
a. 驱动程序根据需要随时动态建立使用和销毁映射,映射是短期临时的。类似c语言中的malloc分配内存。
b. 优点是按需使用地址空间,空间利用率高。缺点是每次使用前后都要去建立和销毁映射,操作繁琐
5.1.5. 虚拟地址到物理地址的映射可以多对1,但是不能1对多
5.2. 驱动中操作寄存器
5.2.1. 使用寄存器对应的虚拟地址直接读取。
5.2.2. 操作相关文件
5.2.2.1. 主映射表位于:arch/arm/plat-s5p/include/plat/map-s5p.h
a. CPU在安排寄存器地址时不是随意乱序分布的,而是按照模块去区分的。每一个模块内部的很多个寄存器的地址是连续的。所以内核在定义寄存器地址时都是先找到基地址,然后再用基地址+偏移量来寻找具体的一个寄存器。
b. map-s5p.h中定义的就是要用到的几个模块的寄存器基地址。
c. map-s5p.h中定义的是模块的寄存器基地址的虚拟地址。
5.2.2.2. 虚拟地址基地址定义在:arch/arm/plat-samsung/include/plat/map-base.h
a. #define S3C_ADDR_BASE(0xFD000000)// 三星移植时确定的静态映射表的基地址,表中的所有虚拟地址都是以这个地址+偏移量来指定的
5.2.2.3. GPIO相关的主映射表位于:arch/arm/mach-s5pv210/include/mach/regs-gpio.h
a. 表中是GPIO的各个端口的基地址的定义
5.2.2.4. GPIO的具体寄存器定义位于:arch/arm/mach-s5pv210/include/mach/gpio-bank.h
六. 应用层调用内核
6.1. 相关代码如下
#include <stdio.h> #include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h> #include <string.h> #define DEVFILE "/dev/led_dev" int main(void)
{
char buf[] = {};
int fd = -;
if((fd =open(DEVFILE, O_RDWR))<)
{
perror("open");
return -;
}
printf("open successful fd = %d\n",fd);
if(write(fd, "on", strlen("on"))<)
{
perror("write");
return -;
}
sleep();
memset(buf,,sizeof(buf));
if(read(fd, buf, )<)
{
perror("read");
return -;
}
printf("read data = %s\n",buf); if(write(fd, "off", strlen("off"))<)
{
perror("write");
return -;
}
sleep();
memset(buf,,sizeof(buf));
if(read(fd, buf, )<)
{
perror("read");
return -;
}
printf("read data = %s\n",buf); close(fd);
return ;
}
旧接口注册LED字符驱动设备(静态映射)的更多相关文章
- 旧接口注册LED字符驱动设备(动态映射)
#include <linux/init.h> // __init __exit #include <linux/module.h> // module_init module ...
- 新接口注册LED字符驱动设备
#include <linux/init.h> // __init __exit #include <linux/module.h> // module_init module ...
- 嵌入式Linux学习笔记(三) 字符型设备驱动--LED的驱动开发
在成功构建了一个能够运行在开发板平台的系统后,下一步就要正式开始应用的开发(这里前提是有一定的C语言基础,对ARM体系的软/硬件,这部分有疑问可能要参考其它教程),根据需求仔细分解任务,可以发现包含的 ...
- Linux内核驱动学习(三)字符型设备驱动之初体验
Linux字符型设备驱动之初体验 文章目录 Linux字符型设备驱动之初体验 前言 框架 字符型设备 程序实现 cdev kobj owner file_operations dev_t 设备注册过程 ...
- 嵌入式linux驱动开发之点亮led(驱动编程思想之初体验)
这节我们就开始开始进行实战啦!这里顺便说一下啊,出来做开发的基础很重要啊,基础不好,迟早是要恶补的.个人深刻觉得像这种嵌入式的开发对C语言和微机接口与原理是非常依赖的,必须要有深厚的基础才能hold的 ...
- 基于OMAPL138的Linux字符驱动_GPIO驱动AD9833(一)之miscdevice和ioctl
基于OMAPL138的Linux字符驱动_GPIO驱动AD9833(一)之miscdevice和ioctl 0. 导语 在嵌入式的道路上寻寻觅觅很久,进入嵌入式这个行业也有几年的时间了,从2011年后 ...
- fl2440 platform总线led字符设备驱动
首先需要知道的是,设备跟驱动是分开的.设备通过struct device来定义,也可以自己将结构体封装到自己定义的device结构体中: 例如:struct platform_device: 在inc ...
- linux设备驱动归纳总结(三):1.字符型设备之设备申请【转】
本文转载自:http://blog.chinaunix.net/uid-25014876-id-59416.html linux设备驱动归纳总结(三):1.字符型设备之设备申请 操作系统:Ubunru ...
- 【Linux开发】linux设备驱动归纳总结(三):1.字符型设备之设备申请
linux设备驱动归纳总结(三):1.字符型设备之设备申请 操作系统:Ubunru 10.04 实验平台:S3C2440 + linux2.6.29内核 注:在今后驱动程序的学习中经常需要查看内核源代 ...
随机推荐
- python 获取系统环境变量 os.environ and os.putenv
从一段code说起 “if "BATCH_CONFIG_INI" in os.environ:” 判断环境变量的值有没有定义 如果定义的话就去环境变量的值,否则就取当前目录下的co ...
- Django【第19篇】:Django之extra
extra过滤 extra extra(select=None, where=None, params=None, tables=None, order_by=None, select_params= ...
- 【bzoj2427】【软件安装】tarjan缩点+树形依赖背包
(上不了p站我要死了,侵权度娘背锅) Description 现在我们的手头有N个软件,对于一个软件i,它要占用Wi的磁盘空间,它的价值为Vi.我们希望从中选择一些软件安装到一台磁盘容量为M计算机上, ...
- webstorm主题更换和webstorm汉化
主题更换方式一 主题类型:*.jar 在webstorm程序中选择 : 菜单栏 File -> Setting ->Import Settings 选中下载的.jar文件 主题更换方式二 ...
- Laya 首日红点逻辑
Laya 首日红点逻辑 @author ixenos 2019-08-26 10:50:27 1.原理:显然,首日红点意味着包含进程销毁的情况,那么就要持久化存储信息,这里我们使用LocalStora ...
- R 画散点图
ggplot(data=df, aes(x=n, y=rt, group=kernel, shape=kernel, colour=kernel)) + geom_point(fill="w ...
- Python 元组遍历排序操作方法
在Python不可变数据类型中,有一个比较重要的角色那就是元组( tuple ).如果某个对像被定义为元组类型,那么就意味着它的值不能被修改,除非重新定义一个新的对像.元组和List列表常被放在一起进 ...
- How To Use the Widget Factory 使用widget factory创建插件
To start, we'll create a progress bar that just lets us set the progress once. 创建一个基于widget factory ...
- 高通平台msm8916修改开机logo【原创】
经过两天的奋战终于把开机logo给搞定了啊. 首先修改开机logo要从哪里入手呢?先分析一下源码看看. ---> void display_image_on_screen() { struct ...
- 高通Camera驱动分析【转】
本文转载自:http://blog.csdn.net/liwei16611/article/details/53955711 1.Sensor slave配置 结构体msm_camera_sensor ...