Linux 内核驱动自动创建设备节点并挂载设备
*注:本文来自http://blog.csdn.net/lwj103862095/article/details/17470573
一、首先需要在最开始定义两个数据结构:
static struct class *firstdrv_class;
static struct device *firstdrv_device;
二、在init函数里通过class_create和device_create函数创建相应的设备节点,示例代码如下:
static int first_drv_init(void)
{
/* 主设备号设置为0表示由系统自动分配主设备号 */
major = register_chrdev(, "first_drv", &first_drv_fops); /* 创建firstdrv类 */
firstdrv_class = class_create(THIS_MODULE, "firstdrv"); /* 在firstdrv类下创建xxx设备,供应用程序打开设备*/
firstdrv_device = device_create(firstdrv_class, NULL, MKDEV(major, ), NULL, "xxx");
return ;
}
三、在exit函数里通过device_destroy、class_unregister、class_destroy函数对设备进行卸载
static void first_drv_exit(void)
{
device_destroy(firstdrv_class,MKDEV(major,));
class_unregister(firstdrv_class);
class_destroy(firstdrv_class);
unregister_chrdev(major, "first_drv");
}
参考代码一、
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <linux/module.h>
#include <linux/device.h> //class_create static struct class *firstdrv_class;
static struct device *firstdrv_device; int major;
static int first_drv_open(struct inode * inode, struct file * filp)
{
printk("first_drv_open\n");
return ;
}
static int first_drv_write(struct file * file, const char __user * buffer, size_t count, loff_t * ppos)
{
printk("first_drv_write\n");
return ;
} /* File operations struct for character device */
static const struct file_operations first_drv_fops = {
.owner = THIS_MODULE,
.open = first_drv_open,
.write = first_drv_write,
}; /* 驱动入口函数 */
static int first_drv_init(void)
{
/* 主设备号设置为0表示由系统自动分配主设备号 */
major = register_chrdev(, "first_drv", &first_drv_fops); /* 创建firstdrv类 */
firstdrv_class = class_create(THIS_MODULE, "firstdrv"); /* 在firstdrv类下创建xxx设备,供应用程序打开设备*/
firstdrv_device = device_create(firstdrv_class, NULL, MKDEV(major, ), NULL, "xxx");
return ;
} /* 驱动出口函数 */
static void first_drv_exit(void)
{
unregister_chrdev(major, "first_drv");
device_unregister(firstdrv_device); //卸载类下的设备
class_destroy(firstdrv_class); //卸载类
} module_init(first_drv_init); //用于修饰入口函数
module_exit(first_drv_exit); //用于修饰出口函数 MODULE_AUTHOR("LWJ");
MODULE_DESCRIPTION("Just for Demon");
MODULE_LICENSE("GPL"); //遵循GPL协议
参考代码二
#include <linux/init.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <mach/regs-gpio.h>
#include <linux/device.h>
#include <mach/hardware.h>
#include <linux/cdev.h>
#include <asm/uaccess.h>
#include <linux/errno.h> #include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/gpio.h>
#include <linux/io.h> #include <mach/hardware.h>
#include <mach/map.h>
#include <mach/regs-gpio.h>
#include <mach/gpio-bank-e.h>
#include <mach/regs-clock.h>
#include <asm/irq.h> #include <plat/gpio-core.h>
#include <plat/gpio-cfg.h> //#define DEBUG
/* 相关引脚定义,方便以后移植 */
#define DEVICE_NAME "ds18b20"
#define DQ 2
#define CFG_IN 0
#define CFG_OUT 1 // ds18b20主次设备号(动态分配)
int ds18b20_major = ;
int ds18b20_minor = ;
int ds18b20_nr_devs = ; // 定义设备类型
static struct ds18b20_device {
struct cdev cdev;
};
struct ds18b20_device ds18b20_dev; static struct class *ds18b20_class; /* 函数声明 */
static int ds18b20_open(struct inode *inode, struct file *filp);
static int ds18b20_init(void);
static void write_byte(unsigned char data);
static unsigned char read_byte(void);
static ssize_t ds18b20_read(struct file *filp, char __user *buf,
size_t count, loff_t *f_pos);
void ds18b20_setup_cdev(struct ds18b20_device *dev, int index); void s3c6410_gpio_cfgpin(unsigned int pin, unsigned int function)
{
//s3c_gpio_cfgpin(pin,function);
unsigned int tmp;
tmp = readl(S3C64XX_GPECON);
tmp = (tmp & ~(0XF<<pin*))|(function<<pin*);
writel(tmp, S3C64XX_GPECON);
} void s3c6410_gpio_pullup(unsigned int pin, unsigned int to)
{
//s3c_gpio_setpull(pin,to);
unsigned int tmp;
tmp = readl(S3C64XX_GPEPUD);
tmp = (tmp & ~(<<pin*))|(to<<pin*);
writel(tmp, S3C64XX_GPEPUD);
} unsigned int s3c6410_gpio_getpin(unsigned int pin)
{
unsigned int tmp;
tmp = readl(S3C64XX_GPEDAT);
tmp = tmp & ( << (pin)); return tmp;
} void s3c6410_gpio_setpin(unsigned int pin, unsigned int dat)
{
unsigned int tmp;
tmp = readl(S3C64XX_GPEDAT);
tmp &= ~( << (pin));
tmp |= ( (dat) << (pin) );
writel(tmp, S3C64XX_GPEDAT); ;
} /******************************************************************************************************
** 函数名称: ds18b20_open()
** 函数功能: 打开设备,初始化ds18b20
** 入口参数: inode:设备文件信息; filp: 被打开的文件的信息
** 出口参数: 成功时返回0,失败返回-1
** 备 注:
******************************************************************************************************/
static int ds18b20_open(struct inode *inode, struct file *filp)
{
int flag = ;
/*struct ds18b20_device *dev;
dev = container_of(inode->i_cdev, struct ds18b20_device, cdev);
filp->private_data = dev;*/ flag = ds18b20_init();
if(flag & 0x01)
{
#ifdef DEBUG
printk(KERN_WARNING "open ds18b20 failed\n");
#endif
return -;
}
#ifdef DEBUG
printk(KERN_NOTICE "open ds18b20 successful\n");
#endif
return ;
} /******************************************************************************************************
** 函数名称: ds18b20_init()
** 函数功能: 复位ds18b20
** 入口参数: 无
** 出口参数: retval:成功返回0,失败返回1
** 备 注: 操作时序见ds18b20 datasheet
******************************************************************************************************/
static int ds18b20_init(void)
{
int retval = ; s3c6410_gpio_cfgpin(DQ, CFG_OUT);
s3c6410_gpio_pullup(DQ, ); s3c6410_gpio_setpin(DQ, );
udelay();
s3c6410_gpio_setpin(DQ, ); // 拉低ds18b20总线,复位ds18b20
udelay(); // 保持复位电平500us s3c6410_gpio_setpin(DQ, ); // 释放ds18b20总线
udelay(); // 若复位成功,ds18b20发出存在脉冲(低电平,持续60~240us)
s3c6410_gpio_cfgpin(DQ, CFG_IN);
retval = s3c6410_gpio_getpin(DQ); udelay();
s3c6410_gpio_cfgpin(DQ, CFG_OUT);
s3c6410_gpio_pullup(DQ, );
s3c6410_gpio_setpin(DQ, ); // 释放总线 return retval;
} /******************************************************************************************************
** 函数名称: write_byte()
** 函数功能: 向18b20写入一个字节数据
** 入口参数: data
** 出口参数: 无
** 备 注:
******************************************************************************************************/
static void write_byte(unsigned char data)
{
int i = ; s3c6410_gpio_cfgpin(DQ, CFG_OUT);
s3c6410_gpio_pullup(DQ, ); for (i = ; i < ; i ++)
{
// 总线从高拉至低电平时,就产生写时隙
s3c6410_gpio_setpin(DQ, );
udelay();
s3c6410_gpio_setpin(DQ, );
s3c6410_gpio_setpin(DQ, data & 0x01);
udelay();
data >>= ;
}
s3c6410_gpio_setpin(DQ, ); // 重新释放ds18b20总线
} /******************************************************************************************************
** 函数名称: read_byte()
** 函数功能: 从ds18b20读出一个字节数据
** 入口参数: 无
** 出口参数: 读出的数据
** 备 注:
******************************************************************************************************/
static unsigned char read_byte(void)
{
int i;
unsigned char data = ; for (i = ; i < ; i++)
{
// 总线从高拉至低,只需维持低电平17ts,再把总线拉高,就产生读时隙
s3c6410_gpio_cfgpin(DQ, CFG_OUT);
s3c6410_gpio_pullup(DQ, );
s3c6410_gpio_setpin(DQ, );
udelay();
s3c6410_gpio_setpin(DQ, );
udelay();
s3c6410_gpio_setpin(DQ, );
udelay();
data >>= ;
s3c6410_gpio_cfgpin(DQ, CFG_IN);
if (s3c6410_gpio_getpin(DQ))
data |= 0x80;
udelay();
}
s3c6410_gpio_cfgpin(DQ, CFG_OUT);
s3c6410_gpio_pullup(DQ, );
s3c6410_gpio_setpin(DQ, ); // 释放ds18b20总线
return data;
}
/******************************************************************************************************
** 函数名称: ds18b20_read()
** 函数功能: 读出18b20的温度
** 入口参数:
** 出口参数:
** 备 注:
******************************************************************************************************/
static ssize_t ds18b20_read(struct file *filp, char __user *buf,
size_t count, loff_t *f_pos)
{
int flag;
unsigned long err;
unsigned char result[] = {0x00, 0x00};
//struct ds18b20_device *dev = filp->private_data; flag = ds18b20_init();
if (flag)
{
#ifdef DEBUG
printk(KERN_WARNING "ds18b20 init failed\n");
#endif
return -;
} write_byte(0xcc);
write_byte(0x44); flag = ds18b20_init();
if (flag)
return -; write_byte(0xcc);
write_byte(0xbe); result[] = read_byte(); // 温度低八位
result[] = read_byte(); // 温度高八位 err = copy_to_user(buf, &result, sizeof(result));
return err ? -EFAULT : min(sizeof(result),count);
} /**************************************************************
* 字符驱动程序的核心,应用程序所调用的open,read等函数最终会
* 调用这个结构中的对应函数
*************************************************************/
static struct file_operations ds18b20_dev_fops = {
.owner = THIS_MODULE,
.open = ds18b20_open,
.read = ds18b20_read,
}; /******************************************************************************************************
** 函数名称: ds18b20_setup_cdev()
** 函数功能: 初始化cdev
** 入口参数: dev:设备结构体; index:
** 出口参数: 无
** 备 注:
******************************************************************************************************/
void ds18b20_setup_cdev(struct ds18b20_device *dev, int index)
{
int err, devno = MKDEV(ds18b20_major, ds18b20_minor + index); cdev_init(&dev->cdev, &ds18b20_dev_fops);
dev->cdev.owner = THIS_MODULE;
err = cdev_add(&(dev->cdev), devno, );
if (err)
{
#ifdef DEBUG
printk(KERN_NOTICE "ERROR %d add ds18b20\n", err);
#endif
}
} /******************************************************************************************************
** 函数名称: ds18b20_dev_init()
** 函数功能: 为温度传感器分配注册设备号,初始化cdev
** 入口参数: 无
** 出口参数: 若成功执行,返回0
** 备 注:
******************************************************************************************************/
static int __init ds18b20_dev_init(void)
{
ds18b20_major = register_chrdev(ds18b20_major, DEVICE_NAME, &ds18b20_dev_fops);
if (ds18b20_major<)
{
printk(DEVICE_NAME " Can't register major number!\n");
return -EIO;
} ds18b20_class = class_create(THIS_MODULE, DEVICE_NAME);
device_create(ds18b20_class, NULL, MKDEV(ds18b20_major, ds18b20_minor), NULL, DEVICE_NAME);
#ifdef DEBUG
printk(KERN_WARNING "register ds18b20 driver successful!\n");
#endif
return ;
} /******************************************************************************************************
** 函数名称: ds18b20_dev_exit()
** 函数功能: 注销设备
** 入口参数: 无
** 出口参数: 无
** 备 注:
******************************************************************************************************/
static void __exit ds18b20_dev_exit(void)
{
device_destroy(ds18b20_class, MKDEV(ds18b20_major,ds18b20_minor));
class_unregister(ds18b20_class);
class_destroy(ds18b20_class);
unregister_chrdev(ds18b20_major, DEVICE_NAME);
#ifdef DEBUG
printk(KERN_WARNING "Exit ds18b20 driver!\n");
#endif
} module_init(ds18b20_dev_init);
module_exit(ds18b20_dev_exit);
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("xinli_whut@163.com");
/******************************************************************************************************
** 文件到此结束
******************************************************************************************************/
Linux 内核驱动自动创建设备节点并挂载设备的更多相关文章
- Linux内核驱动学习(三)字符型设备驱动之初体验
Linux字符型设备驱动之初体验 文章目录 Linux字符型设备驱动之初体验 前言 框架 字符型设备 程序实现 cdev kobj owner file_operations dev_t 设备注册过程 ...
- linux内核驱动模型
linux内核驱动模型,以2.6.32内核为例.(一边写一边看的,有点乱.) 1.以内核对象为基础.用kobject表示,相当于其它对象的基类,是构建linux驱动模型的关键.具有相同类型的内核对象构 ...
- Linux内核驱动学习(八)GPIO驱动模拟输出PWM
文章目录 前言 原理图 IO模拟输出PWM 设备树 驱动端 调试信息 实验结果 附录 前言 上一篇的学习中介绍了如何在用户空间直接操作GPIO,并写了一个脚本可以产生PWM.本篇的学习会将写一个驱动操 ...
- 【引用】Linux 内核驱动--多点触摸接口
本文转载自James<Linux 内核驱动--多点触摸接口> 译自:linux-2.6.31.14\Documentation\input\multi-touch-protocol.t ...
- Linux内核驱动学习(六)GPIO之概览
文章目录 前言 功能 如何使用 设备树 API 总结 前言 GPIO(General Purpose Input/Output)通用输入/输出接口,是十分灵活软件可编程的接口,功能强大,十分常用,SO ...
- linux 内核驱动--Platform Device和Platform_driver注册过程
linux 内核驱动--Platform Device和Platform_driver注册过程 从 Linux 2.6 起引入了一套新的驱动管理和注册机制 :Platform_device 和 Pla ...
- Unix/Linux环境C编程新手教程(12) openSUSECCPP以及Linux内核驱动开发环境搭建
1. openSUSE是一款优秀的linux. watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvaXRjYXN0Y3Bw/font/5a6L5L2T/font ...
- Unix/Linux环境C编程入门教程(12) openSUSECCPP以及Linux内核驱动开发环境搭建
1. openSUSE是一款优秀的linux. 2.选择默认虚拟机 3.选择稍后安装操作系统 4.选择linux opensuse 5. 选择默认虚拟机名称 6.设置处理器为双核. 7.内存设置为2 ...
- Linux内核驱动开发之KGDB原理介绍及kgdboe方式配置
接博文<Linux内核驱动开发之KGDB单步调试内核(kgdboc方式)>.上篇博文中,仅简单介绍使用串口的Kgbd的流程(kgdboc方式),本文将重点介绍KGDB调试Linux内核的原 ...
随机推荐
- Javascript中的函数数学运算
1.Math函数与属性使用语法 Math.方法名(参数1,参数2,...); Math.属性; 说明 Math函数可以没有参数,比如Math.random()函数,或有多个参数,比如Math.max( ...
- Python3.x:如何识别图片上的文字
Python3.x:如何识别图片上的文字 安装pytesseract库,必须先安装其依赖的PIL及tesseract-ocr,其中PIL为图像处理库,而后面的tesseract-ocr则为google ...
- Activiti工作流引擎数据库表结构
Activiti工作流引擎数据库表结构 一.数据库表的命名 Acitiviti数据库中表的命名都是以ACT_开头的.第二部分是一个两个字符用例表的标识.此用例大体与服务API是匹配的. ACT_RE_ ...
- 从页面到服务器,node实现文件下载
起因: 新来了一个需求,让用户下载一个200m的zip文件,并且校验用户信息,难点:下载的文件是200M的. 现在维护的系统,以前的文件下载,走的是node的静态文件,用的express框架上自带的静 ...
- linux-android(任务处理)
1.开辟任务内存 2.设置每个任务优先级 ,,
- 起源游戏临时实体(Temp Entity)
如何查看实体 https://wiki.alliedmods.net/Temp_Entity_Lists_(Source) 这里是部分游戏的临时实体列表 # 还可以通过在游戏内输入来获取游戏的临时实体 ...
- kafka运行错误:提示找不到或者无法加载主类错误解决方法
kafaka版本:kafka_2.11-1.1.0原因有2个:1 目录不能有空格 D:\Soft\kafka_2.11-1.1.0 , 放在Program Files目录中一直有问题2 修改D ...
- 动态 K th
每一棵线段树是维护每一个序列前缀的值在任意区间的个数,如果还是按照静态的来做的话,那么每一次修改都要遍历O(n)棵树,时间就是O(2*M*nlogn)->TLE考虑到前缀和,我们通过树状数组来优 ...
- pycharm社区版创建django项目(Windows 8.1)
django是Python的一个开源web框架,在pycharm开发环境中,pycharm专业版在新建一个项目的时候有django选项,帮助创建一个django框架的项目.pycharm社区版需要自己 ...
- MySQL事务的隔离级别
为什么需要隔离 当多个线程都开启事务操作数据库中的数据时,数据库系统要能进行隔离操作,以保证各个线程获取数据的准确性,在介绍数据库提供的各种隔离级别之前,我们先看看如果不考虑事务的隔离性,会发生的几种 ...