个人笔记
首先,我们要在设备树上添加设备信息。先在设备树 iomuxc 节点上添加pinctrl信息
pinctrl_beep_1: beep-1{
fsl,pins = <
MX6ULL_PAD_SNVS_TAMPER1__GPIO5_IO01 0x10b0
>;
};
MX6UL 是imx6ul-pinfunc.h头文件中定义的。
#define MX6ULL_PAD_SNVS_TAMPER1__GPIO5_IO01 0x000C 0x0050 0x0000 0x5 0x0
第一个 0x000C 代表mux_reg寄存器的偏移地址
第二个 0x0050 是conf_reg寄存器偏移地址。
第三个 0x0000 是input_reg寄存器的偏移地址
第四个 0x5 是 mux_reg 寄存器的值,也就是设置在 mux_reg 寄存器里面的值为 0x05 查数据手册能知道这是复用该端口作为 gpio 口。
第五个 是 input_reg 的值 这里设为 0 没有用到。
跟在 MX6ULL_PAD_SNVS_TAMPER1__GPIO5_IO01 后面的 0x10b0 就是电器属性的值 就是 conf_reg 的值。
注意 定义完着个 io 后你要看别的 io 有没有复用这个口,如果有那是用时就会失败,因为端口被别的设备复用了,我们写的这个就会使用不成功。所以要去把其他 MX6ULL_PAD_SNVS_TAMPER1__GPIO5_IO01 给屏蔽掉。
然后添加设备节点信息:
beep{
compatible = "lakzhu,beep";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_beep_1>;
beep-gpios = <&gpio5 1 GPIO_ACTIVE_HIGH>;
status = "ok";
};
pinctrl-0 链接着上面的 io 信息 led-gpios 表示 是 gpio5 的 1 号口, 高电平有效。设备信息添加完成。
添加完成后 编译设备树写入开发板。
然后就是编写设备了。
首先写注册函数注册与卸载:
static int __init beep_init(void)
{
int ret = 0;
return 0;
}
static void __exit beep_exit(void)
{
}
//设备注册与卸载
module_init(beep_init);
module_exit(beep_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("lakzhu");
构建设备结构体
struct beep_dev{
dev_t devid;
struct cdev cdev;
int major;
int minor;
struct class *class;
struct device *device;
struct device_node *nd;
int beep_gpio;
};
struct beep_dev beep;
//操作函数
static const struct file_operations filp = {
.owner = THIS_MODULE,
.open = beep_open,
.release = beep_release,
.write = beep_write,
};
注册字符设备:
static int __init beep_init(void)
{
int ret = 0;
//注册字符设备
beep.major = 0;
if(beep.major){
beep.devid = MKDEV(beep.major, 0);
register_chrdev_region(beep.devid, BEEP_CNT, BEEP_NAME);
}else{
ret = alloc_chrdev_region(&beep.devid, 0, BEEP_CNT, BEEP_NAME);
beep.major = MAJOR(beep.devid);
beep.minor = MINOR(beep.devid);
}
printk("maj:%d min:%d\r\n", beep.major, beep.minor);
if(ret < 0){
goto fail_devid;
}
}
fail_devid:
return ret;
初始化字符设备
//初始化 dev
beep.cdev.owner = THIS_MODULE;
cdev_init(&beep.cdev, &filp);
//cdev 添加到设备
ret = cdev_add(&beep.cdev, beep.devid, BEEP_CNT);
if(ret < 0){
goto fail_cdev;
}
fail_cdev:
unregister_chrdev_region(beep.devid, BEEP_CNT);//失败了要把之前注册的设备号删除
创建类
beep.class = class_create(THIS_MODULE, BEEP_NAME);
if(IS_ERR(beep.class)){
ret = PTR_ERR(beep.class);
goto fail_class;
}
fail_class:
cdev_del(&beep.cdev);
创建设备
//创建设备
beep.device = device_create(beep.class, NULL, beep.devid, NULL, BEEP_NAME);
if(IS_ERR(beep.device)){
ret = PTR_ERR(beep.device);
goto fail_device;
}
fail_device:
class_destroy(beep.class);
通过 of 函数获取设备节点信息
beep.nd = NULL;
beep.nd = of_find_node_by_path("/beep");
if(beep.nd == NULL){
ret = -ENAVAIL;
goto fail_nd;
}
fail_node:
device_destroy(beep.class, beep.devid);
获取对应的 led 信息:
beep.beep_gpio = of_get_named_gpio(beep.nd, "beep-gpios", 0);
if(beep.beep_gpio < 0){
ret = -ENAVAIL;
goto fail_GetGpio;
}
fail_GetGpio:
申请io 申请了才能使用
ret = gpio_request(beep.beep_gpio, "beep-gpios");
if(ret){
printk("can't request beep gpio \r\n");
goto fail_request;
}
fail_request:
使用io
ret = gpio_direction_output(beep.beep_gpio, 0);
if(ret < 0){
goto fail_output;
}
gpio_set_value(beep.beep_gpio, 0);
return ret;
fail_output:
gpio_free(beep.beep_gpio);
函数退出,要把注册过的都注销掉
static void __exit beep_exit(void)
{
gpio_set_value(beep.beep_gpio, 1);
//注销字符驱动
cdev_del(&beep.cdev);
//删除设备号
unregister_chrdev_region(beep.devid, BEEP_CNT);
//摧毁驱动
device_destroy(beep.class, beep.devid);
//摧毁类
class_destroy(beep.class);
//销毁io
gpio_free(beep.beep_gpio);
}
文件操作函数
static int beep_open(struct inode *inode, struct file *filp)
{
filp->private_data = &beep;
return 0;
}
static int beep_release(struct inode *inode, struct file *filp)
{
return 0;
}
static ssize_t beep_write(struct file *filp, const char __user *buf, size_t count, loff_t *ppos)
{
int ret;
unsigned char data[1];
struct beep_dev *dev = filp->private_data;
ret = copy_from_user(data, buf, count);
if(ret < 0)
return -ENAVAIL;
if(data[0] == DEEPON){
gpio_set_value(dev->beep_gpio, DEEPON);
}else if(data[0] == DEEPOFF){
gpio_set_value(dev->beep_gpio, DEEPOFF);
}
return 0;
}
完整 的文件
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#define BEEP_CNT 1
#define BEEP_NAME "beep"
#define DEEPOFF 1
#define DEEPON 0
//gpio 设备结构体
struct beep_dev{
dev_t devid;
struct cdev cdev;
int major;
int minor;
struct class *class;
struct device *device;
struct device_node *nd;
int beep_gpio;
};
struct beep_dev beep;
static int beep_open(struct inode *inode, struct file *filp)
{
filp->private_data = &beep;
return 0;
}
static int beep_release(struct inode *inode, struct file *filp)
{
return 0;
}
static ssize_t beep_write(struct file *filp, const char __user *buf, size_t count, loff_t *ppos)
{
int ret;
unsigned char data[1];
struct beep_dev *dev = filp->private_data;
ret = copy_from_user(data, buf, count);
if(ret < 0)
return -ENAVAIL;
if(data[0] == DEEPON){
gpio_set_value(dev->beep_gpio, DEEPON);
}else if(data[0] == DEEPOFF){
gpio_set_value(dev->beep_gpio, DEEPOFF);
}
return 0;
}
static const struct file_operations filp = {
.owner = THIS_MODULE,
.open = beep_open,
.release = beep_release,
.write = beep_write,
};
//入口出口
static int __init beep_init(void)
{
int ret = 0;
//注册字符设备
beep.major = 0;
if(beep.major){
beep.devid = MKDEV(beep.major, 0);
register_chrdev_region(beep.devid, BEEP_CNT, BEEP_NAME);
}else{
ret = alloc_chrdev_region(&beep.devid, 0, BEEP_CNT, BEEP_NAME);
beep.major = MAJOR(beep.devid);
beep.minor = MINOR(beep.devid);
}
printk("maj:%d min:%d\r\n", beep.major, beep.minor);
if(ret < 0){
goto fail_devid;
}
//初始化 dev
beep.cdev.owner = THIS_MODULE;
cdev_init(&beep.cdev, &filp);
//cdev 添加到设备
ret = cdev_add(&beep.cdev, beep.devid, BEEP_CNT);
if(ret < 0){
goto fail_cdev;
}
//创建设备节点
beep.class = class_create(THIS_MODULE, BEEP_NAME);
if(IS_ERR(beep.class)){
ret = PTR_ERR(beep.class);
goto fail_class;
}
//创建设备
beep.device = device_create(beep.class, NULL, beep.devid, NULL, BEEP_NAME);
if(IS_ERR(beep.device)){
ret = PTR_ERR(beep.device);
goto fail_device;
}
printk("1");
//获取设备节点
beep.nd = NULL;
beep.nd = of_find_node_by_path("/beep");
if(beep.nd == NULL){
ret = -ENAVAIL;
goto fail_nd;
}
printk("2");
//获取led的对应的gpio
beep.beep_gpio = of_get_named_gpio(beep.nd, "beep-gpios", 0);
if(beep.beep_gpio < 0){
ret = -ENAVAIL;
goto fail_GetGpio;
}
printk("3");
//申请io 申请了才能使用
ret = gpio_request(beep.beep_gpio, "beep-gpios");
if(ret){
printk("can't request beep gpio \r\n");
goto fail_request;
}
printk("4");
//使用io
ret = gpio_direction_output(beep.beep_gpio, 0);
if(ret < 0){
goto fail_output;
}
gpio_set_value(beep.beep_gpio, 0);
return ret;
fail_output:
gpio_free(beep.beep_gpio);
fail_request:
fail_GetGpio:
fail_nd:
device_destroy(beep.class, beep.devid);
fail_device:
class_destroy(beep.class);
fail_class:
cdev_del(&beep.cdev);
fail_cdev:
unregister_chrdev_region(beep.devid, BEEP_CNT);
fail_devid:
return ret;
}
static void __exit beep_exit(void)
{
gpio_set_value(beep.beep_gpio, 1);
//注销字符驱动
cdev_del(&beep.cdev);
//删除设备号
unregister_chrdev_region(beep.devid, BEEP_CNT);
//摧毁驱动
device_destroy(beep.class, beep.devid);
//摧毁类
class_destroy(beep.class);
//销毁io
gpio_free(beep.beep_gpio);
}
//设备注册与卸载
module_init(beep_init);
module_exit(beep_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("lakzhu");
编译下载到板子上就能使用了。
有新设备 要是用 先 depmod 然后才能modpure 函数