新接口注册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> #include <linux/ioport.h> //request_mem_region
#include <asm/io.h> //ioremap #include <linux/cdev.h> #define MYNAME "led_dev"
#define DEVNUM 1 #define S5PV210_PA_GPIOJ0CON 0xe0200240 volatile unsigned int *rGPJ0CON = NULL;
volatile unsigned int *rGPJ0DAT = NULL; static dev_t led_dev_no = ;
/************ 静态使用cdev**********/
//static struct cdev led_cdev;
/************ 动态使用cdev**********/
static struct cdev *pled_cdev = NULL; 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 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)
{
int ret = -;
int retval = -;
printk(KERN_INFO "chrdev_init successful\n");
/******指定主设备号注册*****/
//led_dev_no = MKDEV(250,0);
//retval = register_chrdev_region(led_dev_no, DEVNUM,MYNAME);
/******内核自动分配主设备号注册*****/
retval = alloc_chrdev_region(&led_dev_no,,DEVNUM,MYNAME);
if (retval)
{
printk(KERN_WARNING "alloc_chrdev_region fail\n.");
ret = -EINVAL;
goto err_reg;
}
printk(KERN_INFO "alloc_chrdev_region successful major = %d,minor = %d \n",MAJOR(led_dev_no),MINOR(led_dev_no));
/****动态申请cdev实体******/
pled_cdev = cdev_alloc();
printk(KERN_INFO "cdev_alloc successful\n");
cdev_init(pled_cdev, &led_fops);
retval = cdev_add(pled_cdev, led_dev_no, DEVNUM);
if (retval)
{
printk(KERN_WARNING "cdev_add fail\n.");
ret = -EINVAL;
goto err_add;
}
printk(KERN_INFO "cdev_add successful\n"); if (request_mem_region(S5PV210_PA_GPIOJ0CON, , "GPIOJ0CON") == NULL)
{
printk(KERN_WARNING "failed to get memory region\n");
ret = -ENOENT;
goto err_req;
}
rGPJ0CON = ioremap(S5PV210_PA_GPIOJ0CON,);
if (rGPJ0CON == NULL)
{
printk(KERN_WARNING "fail to ioremap() region\n");
ret = -ENOENT;
goto err_map;
}
rGPJ0DAT = rGPJ0CON+;
return ; err_map:
printk(KERN_INFO "err_map err\n");
release_mem_region(S5PV210_PA_GPIOJ0CON,); err_req:
printk(KERN_INFO "err_req err\n");
/***for static cdev******/
//cdev_del(&led_cdev);
/***for dynamic cdev******/
cdev_del(pled_cdev); err_add:
printk(KERN_INFO "err_add err\n");
unregister_chrdev_region(led_dev_no, DEVNUM); err_reg:
return ret; } // 模块卸载函数
static void __exit chrdev_exit(void)
{
iounmap(rGPJ0CON);
release_mem_region(S5PV210_PA_GPIOJ0CON,);
/***for static cdev******/
//cdev_del(&led_cdev);
/***for dynamic cdev******/
cdev_del(pled_cdev); unregister_chrdev_region(led_dev_no, DEVNUM);
printk(KERN_INFO "chrdev_exit successful\n"); } module_init(chrdev_init);
module_exit(chrdev_exit); // MODULE_xxx这种宏作用是用来添加模块描述信息
MODULE_LICENSE("GPL"); // 描述模块的许可证
MODULE_AUTHOR("musk"); // 描述模块的作者
MODULE_DESCRIPTION("module test"); // 描述模块的介绍信息
MODULE_ALIAS("alias xxx"); // 描述模块的别名信息
一. 关于设备注册新老接口
1.1. 老接口:register_chrdev
1.2. 新接口:register_chrdev_region/alloc_chrdev_region + cdev
1.3. 新老接口不同点
a. 老接口不能注册子设备号
b. 新接口可以注册子设备号,并且可以一次注册多个子设备号。
c. register_chrdev = alloc_chrdev_region + cdev_init + cdev_add,所有新接口步骤更多
二. dev_t 成员
2.1. dev_t定义:
typedef u_long dev_t;
2.2. dev;设备号,31位,高12位是主设备号,低20位是次设备号。以下函数可以操作设备号:
#define MINORBITS 20
#define MINORMASK ((1U << MINORBITS) - 1) #define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS)) //获得主设备号
#define MINOR(dev) ((unsigned int) ((dev) & MINORMASK)) //获得此设备号
#define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi)) //由主次设备号得到设备号
三. 注册/主线设备号
3.1. 静态注册函数register_chrdev_region:
/**
* register_chrdev_region() - register a range of device numbers
* @from: the first in the desired range of device numbers; must include
* the major number.
* @count: the number of consecutive device numbers required
* @name: the name of the device or driver.
*
* Return value is zero on success, a negative error code on failure.
*/
int register_chrdev_region(dev_t from, unsigned count, const char *name)
{
struct char_device_struct *cd;
dev_t to = from + count;
dev_t n, next; for (n = from; n < to; n = next) {
next = MKDEV(MAJOR(n)+, );
if (next > to)
next = to;
cd = __register_chrdev_region(MAJOR(n), MINOR(n),
next - n, name);
if (IS_ERR(cd))
goto fail;
}
return ;
fail:
to = n;
for (n = from; n < to; n = next) {
next = MKDEV(MAJOR(n)+, );
kfree(__unregister_chrdev_region(MAJOR(n), MINOR(n), next - n));
}
return PTR_ERR(cd);
}
a. 此函数需要静态指定设备号。
3.2. 动态申请注册函数alloc_chrdev_region
/**
* alloc_chrdev_region() - register a range of char device numbers
* @dev: output parameter for first assigned number
* @baseminor: first of the requested range of minor numbers
* @count: the number of minor numbers required
* @name: the name of the associated device or driver
*
* Allocates a range of char device numbers. The major number will be
* chosen dynamically, and returned (along with the first minor number)
* in @dev. Returns zero or a negative error code.
*/
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,
const char *name)
{
struct char_device_struct *cd;
cd = __register_chrdev_region(, baseminor, count, name);
if (IS_ERR(cd))
return PTR_ERR(cd);
*dev = MKDEV(cd->major, cd->baseminor);
return ;
}
3.2.1. 参数dev是输出型参数,也就是自动分配的设备号
3.3. 注销设备号unregister_chrdev_region
/**
* unregister_chrdev_region() - return a range of device numbers
* @from: the first in the range of numbers to unregister
* @count: the number of device numbers to unregister
*
* This function will unregister a range of @count device numbers,
* starting with @from. The caller should normally be the one who
* allocated those numbers in the first place...
*/
void unregister_chrdev_region(dev_t from, unsigned count)
{
dev_t to = from + count;
dev_t n, next; for (n = from; n < to; n = next) {
next = MKDEV(MAJOR(n)+, );
if (next > to)
next = to;
kfree(__unregister_chrdev_region(MAJOR(n), MINOR(n), next - n));
}
}
4. cdev结构体
4.1. cdev结构体定义
struct cdev {
struct kobject kobj; //内嵌的kobject对象
struct module *owner; //使属模块
const struct file_operations *ops; //文件操作结构体
struct list_head list; //linux 内核所维护的链表指针
dev_t dev; //设备号
unsigned int count; //设备数目
};
4.2. cdev定义有两种方式,struct cdev cdev;另外一种struct cdev *cdev; cdev=cdev_alloc();一种静态声明定义,另一种动态分配。
4.3. cdev相关操作函数
4.3.1. cdev通过函数cdev_init()初始化,主要工作就是将file_operations和cdev关联起来。file_operations是字符驱动需要实现的主要内容。
4.3.2. cdev通过cdev_add()实现cdev的注册,所谓注册就是将cdev根据设备号(dev_t)添加到cdev数组(cdev_map)中供系统管理。
4.3.3. cdev通过cdev_del()将cdev从cdev_map中移除。
4.4. cdev初始化
4.4.1. 静态初始化
/**
* cdev_init() - initialize a cdev structure
* @cdev: the structure to initialize
* @fops: the file_operations for this device
*
* Initializes @cdev, remembering @fops, making it ready to add to the
* system with cdev_add().
*/
void cdev_init(struct cdev *cdev, const struct file_operations *fops)
{
memset(cdev, , sizeof *cdev);
INIT_LIST_HEAD(&cdev->list);
kobject_init(&cdev->kobj, &ktype_cdev_default);
cdev->ops = fops;
}
4.4.2. 动态初始化
/**
* cdev_alloc() - allocate a cdev structure
*
* Allocates and returns a cdev structure, or NULL on failure.
*/
struct cdev *cdev_alloc(void)
{
struct cdev *p = kzalloc(sizeof(struct cdev), GFP_KERNEL);
if (p) {
INIT_LIST_HEAD(&p->list);
kobject_init(&p->kobj, &ktype_cdev_dynamic);
}
return p;
}
4.5. cdev_add添加字符设备
/**
* cdev_add() - add a char device to the system
* @p: the cdev structure for the device
* @dev: the first device number for which this device is responsible
* @count: the number of consecutive minor numbers corresponding to this
* device
*
* cdev_add() adds the device represented by @p to the system, making it
* live immediately. A negative error code is returned on failure.
*/
int cdev_add(struct cdev *p, dev_t dev, unsigned count)
{
p->dev = dev;
p->count = count;
return kobj_map(cdev_map, dev, count, NULL, exact_match, exact_lock, p);
}
4.6. cdev_del()删除字符设备
/**
* cdev_del() - remove a cdev from the system
* @p: the cdev structure to be removed
*
* cdev_del() removes @p from the system, possibly freeing the structure
* itself.
*/
void cdev_del(struct cdev *p)
{
cdev_unmap(p->dev, p->count);
kobject_put(&p->kobj);
}
5.应用层调用内核
5.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年后 ...
- 驱动程序分层分离概念_总线驱动设备模型_P
分层概念: 驱动程序向上注册的原理: 比如:输入子程序一个input.c作为一层,下层为Dev.c和Dir.c,分别编写Dev.c和Dir.c向上Input.c注册:如图所示 分离概念: 分离概念主要 ...
- fl2440 platform总线led字符设备驱动
首先需要知道的是,设备跟驱动是分开的.设备通过struct device来定义,也可以自己将结构体封装到自己定义的device结构体中: 例如:struct platform_device: 在inc ...
- linux设备驱动归纳总结(三):1.字符型设备之设备申请【转】
本文转载自:http://blog.chinaunix.net/uid-25014876-id-59416.html linux设备驱动归纳总结(三):1.字符型设备之设备申请 操作系统:Ubunru ...
随机推荐
- NOIP2019(CSP2019) 游记
NOIP2019(CSP2019) 游记 近一年的似乎也就是感觉比别的学校的同学水平低的不止一点,到现在也没有搞清楚大概应该怎么科学有效的练习,并不会思考"为什么想不到"和&quo ...
- 深入理解MySql事务
事务是MySQL等关系型数据库区别于NoSQL的重要方面,是保证数据一致性的重要手段.本文将首先介绍MySQL事务相关的基础概念,然后介绍事务的ACID特性,并分析其实现原理. MySQL博大精深,文 ...
- LeetCode--152--乘积最大子序列(python)
给定一个整数数组 nums ,找出一个序列中乘积最大的连续子序列(该序列至少包含一个数). 示例 1: 输入: [2,3,-2,4]输出: 6解释: 子数组 [2,3] 有最大乘积 6.示例 2: 输 ...
- React Native 中不用手点击,代码实现切换底部tabBar
参考:https://www.jianshu.com/p/02c630ed7725 tabBar1页面有按钮,点击切换到tabBar2 直接用this.props.navigation.navigat ...
- 对SQL 优化,提升性能!
对SQL 进行优化能够有效提高SQL 语句的执行效率,降低系统资源开销,是开发者提高后端系统处理能力的首选方案. 新产品上线后,随着运营推广活动的开始,业务进入快速增长期,数据库作为后端系统唯一或者主 ...
- 批量下载文件php
做了个照片墙,要提供批量下载照片的功能,如果你会文件下载,那批量也是小菜一碟,就是把文件打包压缩为 zip 文件再下载,而php的内置类ZipArchive()让你很容易实现. 首先,配置php.i ...
- CF786B Legacy 线段树优化建图 + spfa
CodeForces 786B Rick和他的同事们做出了一种新的带放射性的婴儿食品(???根据图片和原文的确如此...),与此同时很多坏人正追赶着他们.因此Rick想在坏人们捉到他之前把他的遗产留给 ...
- [luogu]P2680 运输计划[二分答案][树上差分]
[luogu]P2680 [NOIP2015]运输计划 题目背景 公元 2044 年,人类进入了宇宙纪元. 题目描述 L 国有 n 个星球,还有 n-1 条双向航道,每条航道建立在两个星球之间,这 n ...
- Pandas中的qcut和cut
qcut与cut的主要区别: qcut:传入参数,要将数据分成多少组,即组的个数,具体的组距是由代码计算 cut:传入参数,是分组依据.具体见示例 1.qcut方法,参考链接:http://panda ...
- es的索引库模板
在实际的生产中,如果要插入大批量数据的时候需要使用多个索引库,如果我们还是手工指定每个索引的配置信息settings和mappings,是非常耗时的: 针对这种情况,es有index template ...