字符设备驱动流程
一.修改设备树文件
1.添加pinctrl节点
1.找到相关板卡的dts文件;如打开imx6ull-alientek-emmc.dts
其中肯定有iomux节点(input/ouput Multiplexer),在此节点下添加pinctrl节点;命名格式为:
pinctrl_xxx:name{
fsl,pins = <
复用功能配置 电气属性配置
>;
}
//例子
princtrl_beep: beepgrp {
fsl,pins = <
MX6ULL_PAD_SNVS_TAMPER1__GPIO5_IO01
0X10B0
>
}
其中MX6ULL_PAD_SNVS_TAMPER1__GPIO5_IO01 实际是一个宏定义,抛开外壳,本质是配置IOMUXC的复用功能,以及记录起配置电气属性的寄存器等,0x10b0即为配置电气属性。
2.添加设备节点
在根节点创建设备节点,格式如下;
name {
#address-cells = <num>;
#size-cells = <num>;
compatible = "string";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_xxx>;
xxx-gpio = <&gpiox num polarity>;
status = "okay";
};
//例子
beep{
#address-cells = <1>;
#size-cells = <1>;
compatible = "atkalpha-beep";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_beep>;
beep-gpio = <&gpio5 1 GPIO_ACTIVE_HIGH>;
status = "okay";
};
3.检查Pin是否被其他外设使用
检查有要复用的PIN被使用的话,那就要屏蔽相应的PIN
4.编译设备树
make dtbs执行这行命令,然后使用新编译的.dtb文件启动linux,进入/proc/device-tree目录查看设备节点是否存在
二.编写字符设备驱动文件
第一步:加载和卸载函数,定义相关宏定义
#define XXX_CNT NUM /*设备号个数*/
#define xxx_NAME "STRING" /*名字*/
static int __init xxx_init(){}
static void __exit xxx_exit(){}
module_init()
module_exit()
第二步:编写设备结构体以及文件结构体
struct xxx_dev{
dev_t devid;
struct cdev cdev;
struct class *class;
struct device *device;
int major;
int minor;
struct device_node *nd;
int xxx_gpio;
}
struct xxx_dev xxx;
static struct file_operation xxx_fops ={
.owner = THIS_MODULE,
.open = xxx_open,
.write = xxx_write,
.release = xxx_release,
.read = xxx_read,
};
第三步获取相关设备节点
//获取设备节点
xxx.nd = of_find_node_by_path("path");
//例子:beep.nd = of_find_node_by_path("/beep");
//获取设备树的GPIO属性,得到设备使用的GPIO编码
xxx.xxx_gpio =
of_get_named_gpio(xxx.nd,"propname",0);
/*
例子:beep.beep_gpio =
of_get_named_gpio(beep.nd,"beep-gpio",0)
*/
//"beep-gpio"是指BEEP设备节点中beep-gpio = <&gpio5 1 GPIO_ACTIVE_HIGH>;
ret =gpio_direction_output(xxx.xxx_gpio,1)
第四步:注册字符设备驱动
if(xxx.major){
xxx.devid = MKDEV(xxx.major,0)
register_chrdev_region
(xxx.devid,xxx_CNT,XXX_NAME);
}else{
alloc_chrdev_region (&xxx.devid,0,xxx_CNT,xxx_NAME);
xxx.major = MAJOR(xxx.devid);
xxx.minor = MINOR(xxx.devid);
}
第五步:初始化cdev,添加cdev,class,设备
cdev_init(&xxx.cdev,&xxx_fops);
cdev_add(&xxx.cdev,xxx_devid,xxx_CNT);
xxx.class = class_create(THIS_MODULE,xxx_NAME);
XXX.device = device_create
(xxx.class,NULL,XXX.devid,NULL,XXX_NAME);
第六步.编写文件操作函数
static int xxx_open(struct inode *inode,struct file *filp)
static ssize_t xxx_write(struct file *filp,const char __user *buf,size_t cnt,loff_t *offt)
static int xxx_release(struct inode *inode ,struct file *filp)
第七步.编写驱动出口函数
cdev_del(&xxx.cdev);
unregister_chrdev_region(xxx.devid,xxx_CNT);
device_destroy(xxx.class,xxx.devid);
class_destroy(xxx.class)
第八步 开源
MODULE_LICENSE(“GPL”)