设备一般分为:
1.字符设备(LCD,keyboard,IIC从设备)传输主要是字节流的形势
2.块设备:以块为单位 进行操作的设备 (磁盘 优盘 flash)
3.网络设备:以太网 wifi 等
设备的驱动要素:
1.设备号 用于对众多设备种类的区分
2.设备节点对应的某类设备文件
3.用户对驱动的操作实质就是文件IO的操作,应用空间的操作在驱动中必定有其对应的操作
编写步骤和规范
一:实现模块加载和驱动入口函数
module_init();
module_exit();
MODULE_LISENCE("GPL");
二:在模块中加载入口函数
a: 注册注册设备号 register_chrdev();
b: 创建设备节点 class_create() device_create()
c: 硬件初始化
地址映射:ioremap()
中断到申请
硬件寄存器初始化
d: 上层用户接口的对应操作:file_operations()
以下是设备驱动编写的常用函数
1.//设备号申请注册
int register_chrdev(unsigned int major, const char * name, const struct file_operations * fops)
参数1:主设备号(0~255) 参数为0时 系统自动为其分配 返回值为分配的主设备号
参数2:设备名 (随便取)
参数3:上层接口对应的操作函数
成功:0 失败:负数
2.//创建设备节点
struct class * class_create(owner, name)//先创建一个类
参数1:THIS_MODULE
参数2:类名
成功:返回一个class指针 失败:返回NULL
struct device *device_create(struct class * class, struct device * parent, dev_t devt, void * drvdata, const char * fmt, ...)
参数1:class指针
参数2:NULL
参数3:设备号(MKDEV(ma,mi) 12位的主设备号 + 20位的次设备号)
参数4:NULL
参数5:设备节点名(设备文件)
成功:返回一个device 指针 失败:返回NULL
3.//发送数据给用户 一般为 read操作
int copy_to_user(void * to, const void * from, unsigned long n)
参数1:目的的缓冲区(用户buf)
参数2:源缓冲区(内核buf)
参数3:要拷贝的大小
返回值:拷贝失败的个数
成功:为0 失败:>0
4.//从用户中获取数据 一般为write操作
int copy_from_user(void * to, const void * from, unsigned long n)
参数1:目的的缓冲区(内核buf)
参数2:源缓冲区(用户buf)
参数3:要拷贝的大小
返回值:拷贝失败的个数
成功:为0 失败:>0
5.//地址映射
void* ioremap(offset, size)//
参数1:物理地址
参数2:需要映射的大小
成功:虚拟地址地址(连续) 失败:返回NULL
6.//删除设备节点
device_destroy(struct class * class, dev_t devt)
7.//删除类
class_destroy(struct class * cls)
8.//注销设备
unregister_chrdev(unsigned int major, const char * name)
9.//解除映射
iounmap(const void __iomem * addr)
参数1:要解除映射的虚拟地址
简易字符驱动模型:
#include<linux/fs.h>
#include<linux/module.h>
#include<linux/init.h>
#include<linux/device.h>
#include<linux/uaccess.h>
#include<linux/types.h>
#include<asm/io.h>
//.ko的名字 就是 驱动的名字
//寄存器配置
volatile unsigned int *GPFSEL1 = NULL;
volatile unsigned int *GPSET0 = NULL;
volatile unsigned int *GPCLR0 = NULL;
//device part
static dev_t devo;
static int major = 112;
static int minor = 121;
static char *module_name = "pin17";
static struct class *pin17_class;
static struct device *pin17_class_dev;
static int pin17_write(struct file *file,const char __user *buf ,size_t count,loff_t *ppos )
{
int n_copy, userCmd1; //先定义变量 在打印 不然会出错
printk("pin17 write succes\n");
n_copy = copy_from_user((void *)&userCmd1,(void *)buf,count);
if(n_copy == 0)
{
printk("copy success \n");
printk("userCmd1 is %d\n",userCmd1);
}
if(userCmd1 == 1)
{
*GPSET0 = 0X01 << 17;
printk("set 1\n");
}
else if(userCmd1 == 0)
{
*GPCLR0 = 0X01 << 17;
printk("set 0");
}
else
printk("undo\n");
return 0;
}
static int pin17_open(struct inode *inode,struct file *file)
{
printk("pin17 open succes\n");
*GPFSEL1 &= ~(0x06 << 21);
*GPFSEL1 |= (0x01 << 21);
return 0;
}
static struct file_operations pin17_fops = {
.owner = THIS_MODULE,
.open = pin17_open,
.write = pin17_write,
};
int __init pin17_dri_init(void)
{
int ret ;
devo = MKDEV(major,minor);//设备号
ret = register_chrdev(major,module_name,&pin17_fops);//模块名 不关心
if(ret == 0)
printk("register_chrdev success\n");
pin17_class = class_create(THIS_MODULE,"pin17 of lsc"); //类名 sys/class
pin17_class_dev = device_create(pin17_class,NULL,devo,NULL,module_name);//这个 name 才是设备文件的名字
printk("the pin17 init success\n");
//寄存器初始化 将所需要的io口 的物理地址(寄存器地址 ) 转换为虚拟地址 提供到用户层 给用户使用
GPFSEL1 = (unsigned int*)ioremap(0x3f200004,4);
GPSET0 = (unsigned int*)ioremap(0x3f20001c,4);
GPCLR0 = (unsigned int*)ioremap(0x3f200028,4);
return 0;
}
void __exit pin17_dri_exit(void)
{
device_destroy(pin17_class,devo);
class_destroy(pin17_class);
unregister_chrdev(major,module_name);
iounmap(GPCLR0);//映射释放
iounmap(GPSET0);
iounmap(GPFSEL1);
printk("p17 had unstall success\n");
}
module_init(pin17_dri_init);
module_exit(pin17_dri_exit);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("lsc");
标准:
#include<linux/slab.h>
#include<linux/fs.h>
#include<linux/module.h>
#include<linux/init.h>
#include<linux/device.h>
#include<stdbool.h>
#include<linux/uaccess.h>
#include<linux/types.h>
#include<asm/io.h>
struct pin17_des{
unsigned int dev_major;//主设备号
struct class *cls;//设备类
struct device *dev;//设备节点
void *reg_map_GPFSEL1;//寄存器配置 存放其虚拟地址
void *reg_map_GPSET0;
void *reg_map_GPCLR0;
};
struct pin17_des *pin17_dev;//全局
static int pin17_dri_open(struct inode *filep, struct file *file)
{
writel(readl(pin17_dev->reg_map_GPFSEL1)&~(0x7<<21),pin17_dev->reg_map_GPFSEL1);
writel(readl(pin17_dev->reg_map_GPFSEL1)|(0x1<<21),pin17_dev->reg_map_GPFSEL1);
printk("pin17 open success\n");
return 0;
}
static ssize_t pin17_dri_write(struct file *file, const char __user *user_buf,size_t count, loff_t *ppos)
{
int a;int ret;
printk("write success\n");
ret = copy_from_user((void *)&a, (void *)user_buf, count);
if(ret)
{
printk("copy from user error\n");
}
if(a == 1)
{
writel(readl(pin17_dev->reg_map_GPSET0)|(0x1<<17),pin17_dev->reg_map_GPSET0);
}
else if(a == 0)
{
writel(readl(pin17_dev->reg_map_GPCLR0)|(0x1<<17),pin17_dev->reg_map_GPCLR0);
}
return 0;
}
static ssize_t pin17_dri_read(struct file *file, char __user *user_buf,size_t count, loff_t *ppos)
{
int a = 888;int ret;
printk("read success");
ret = copy_to_user((void*)user_buf, (void *)&a,4);
if(ret)
{
printk("copy to user error\n");
}
return 0;
}
static struct file_operations myfops = {
.owner = THIS_MODULE,
.open = pin17_dri_open,
.read = pin17_dri_read,
.write = pin17_dri_write,
};
static int __init pin17_dri_init(void)
{
int ret;
pin17_dev = kmalloc(sizeof(struct pin17_des),GFP_KERNEL);
if(IS_ERR(pin17_dev))
{
printk(KERN_ERR"malloc error\n");
return -ENOMEM;//内存出错宏
}
pin17_dev->dev_major = register_chrdev(0,"pin17",&myfops);//Ö÷É豸ºÅ ϵͳ·ÖÅä Çý¶¯Ãû:pin17
if(pin17_dev->dev_major < 0)
{
printk(KERN_ERR"register error\n");
ret = -EINVAL;
goto err_0;
}
pin17_dev->cls = class_create(THIS_MODULE, "pin17_class");//类的文件夹名 里面装的是设备文件
if(IS_ERR(pin17_dev->cls))
{
printk(KERN_ERR"class create error\n");
ret = PTR_ERR(pin17_dev->cls);
goto err_1;
}
pin17_dev->dev = device_create(pin17_dev->cls, NULL, MKDEV(pin17_dev->dev_major, 0), NULL,"pin17test");//pin17test 设备节点(文件)名
if(IS_ERR(pin17_dev->dev))
{
printk(KERN_ERR"device create error\n");
ret = PTR_ERR(pin17_dev->dev);
goto err_2;
}
pin17_dev->reg_map_GPFSEL1 = ioremap(0x3f200004,4);//映射
pin17_dev->reg_map_GPSET0 = ioremap(0x3f20001c,4);
pin17_dev->reg_map_GPCLR0 = ioremap(0x3f200028,4);
if(IS_ERR(pin17_dev->reg_map_GPFSEL1)||IS_ERR(pin17_dev->reg_map_GPSET0)||IS_ERR(pin17_dev->reg_map_GPCLR0))
{
printk(KERN_ERR"ioremap error\n");
ret = -ENOMEM;
goto err_3;
}
printk("pin17 dri insert success\n");
return 0;
err_3:
device_destroy(pin17_dev->cls, MKDEV(pin17_dev->dev_major,0));
err_2:
class_destroy(pin17_dev->cls);
err_1:
class_destroy(pin17_dev->cls);
err_0:
kfree(pin17_dev);
return ret;
}
static void __exit pin17_dri_exit(void)
{
device_destroy(pin17_dev->cls, MKDEV(pin17_dev->dev_major,0));
class_destroy(pin17_dev->cls);
unregister_chrdev(pin17_dev->dev_major,"pin17");
iounmap(pin17_dev->reg_map_GPCLR0);
iounmap(pin17_dev->reg_map_GPSET0);
iounmap(pin17_dev->reg_map_GPFSEL1);
kfree(pin17_dev);
printk("pin17 exit success\n");
}
module_init(pin17_dri_init);
module_exit(pin17_dri_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("pin17 control to pin17");
MODULE_AUTHOR("the monkey king");