Linux学习笔记(21)——基于platform的杂项(MISC)设备驱动

  1. 修改设备树文件,增加beep节点
beep {
	#address-cells = <1>;
	#size-cells = <1>;
	compatible = "glen-beep";
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_beep>;			/* 属性设置蜂鸣器所使用的PIN对应的pinctrl节点 */
	beep-gpio = <&gpio5 1 GPIO_ACTIVE_LOW>;	/* 属性指定了蜂鸣器所使用的GPIO, 在这里就是GPIO5的IO01, 低电平有效 */
	status = "okay";
};
  1. 编写miscbeep设备程序
/* 
 * 文件名   : miscbeep.c
 * 作者     : glen  
 * 描述     : miscbeep驱动文件
 */
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define MISCBEEP_NAME   "miscbeep"  /* 名字 */
#define MISCBEEP_MINOR  200
#define BEEPOFF         "OFF"
#define BEEPON          "ON"

/* miscbeep设备结构体 */
struct miscbeep_dev {
    dev_t devid;                /* 设备号 */
    struct cdev cdev;           /* cdev */
    struct class *class;        /* 类 */
    struct device *device;      /* 设备 */
    struct device_node *nd;     /* 设备节点 */
    int beep_gpio;              /* beep所使用的GPIO编号 */
};

struct miscbeep_dev miscbeepdev;/* beep设备 */

/**
 * \brief   打开设备
 * \param   inode   传递给驱动的inode
 *          filp    设备文件
 * \retval   0 成功  其他 失败
 */
static int miscbeep_open(struct inode *inode, struct file *filp)
{
    filp->private_data = &miscbeepdev;
    return 0;
}

/**
 * \brief   向设备写数据
 * \param   filp    设备文件,表示打开的文件描述符
 *          buf     要写给设备写入的数据
 *          cnt     要写入的数据长度
 *          offt    相对于文件首地址的偏移
 * \retval  写入的字节数,如果为负值,表示写入失败
 */
static ssize_t miscbeep_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
    int ret;
    char data_buf[4];
    struct miscbeep_dev *dev = filp->private_data;

    ret = copy_from_user(data_buf, buf, (cnt < sizeof(data_buf) ? cnt : sizeof(data_buf)));
    if (ret < 0) {
        printk("kernel write failure!\r\n");
        return -EFAULT;
    }

    if (strcmp(data_buf, BEEPON) == 0) {
        gpio_set_value(dev->beep_gpio, 0);
    } else if (strcmp(data_buf, BEEPOFF) == 0) {
        gpio_set_value(dev->beep_gpio, 1);
    }

    return 0;
}

/*
 * 设备操作函数
 */
static struct file_operations miscbeep_fops = {
    .owner  = THIS_MODULE,
    .open   = miscbeep_open,
    .write  = miscbeep_write,
};

/*
 * MISC设备结构体
 */
static struct miscdevice beep_miscdev = {
    .minor  = MISCBEEP_MINOR,
    .name   = MISCBEEP_NAME,
    .fops   = &miscbeep_fops,
};

/**
 * \brief   platform驱动的probe函数,当驱动与设备匹配以后此函数执行
 * \param   dev platform设备
 * \retval  0 成功  其他负值 失败
 */
static int miscbeep_probe(struct platform_device *dev)
{
    int ret = 0;

    printk("beep driver and device was matched!\r\n");

    /* 设置BEEP所使用的GPIO */
    /* 获取设备节点 */
    miscbeepdev.nd = of_find_node_by_path("/beep");
    if (miscbeepdev.nd == NULL) {
        printk("beep node not found!\r\n");
        return -EINVAL;
    }

    /* 获取设备树中的gpio属性,得到BEEP所使用的BEEP编号 */
    miscbeepdev.beep_gpio = of_get_named_gpio(miscbeepdev.nd, "beep-gpio", 0);
    if (miscbeepdev.beep_gpio < 0) {
        printk("can't get beep-gpio!\r\n");
        return -EINVAL;
    }

    /* 设置GPIO5_IO01为输出, 并且输出高电平, 默认关闭BEEP */
    ret = gpio_direction_output(miscbeepdev.beep_gpio, 1);
    if (ret < 0) {
        printk("can't set gpio!\r\n");
    }

    /* 注册MISC设备 */
    ret = misc_register(&beep_miscdev);
    if (ret < 0) {
        printk("misc device register failed!\r\n");
        return -EFAULT;
    }

    return 0;
}

/**
 * \brief   remove函数,移除platform驱动的时候此函数会执行
 * \param   dev platform设备
 * \return  0 成功   其他值 失败
 */
static int miscbeep_remove(struct platform_device *dev)
{
    /* 销毁设备的时候关闭BEEP */
    gpio_set_value(miscbeepdev.beep_gpio, 1);

    /* 注销misc设备驱动 */
    misc_deregister(&beep_miscdev);
    return 0;
}

/* 匹配列表 */
static const struct of_device_id beep_of_match[] = {
    {.compatible = "glen-beep"},
    {}
};

/* platform驱动结构体 */
static struct platform_driver beep_driver = {
    .driver = {
        .name = "glen-beep",
        .of_match_table = beep_of_match,
    },
    .probe = miscbeep_probe,
    .remove = miscbeep_remove,
};

/**
 * \brief   驱动模块加载函数
 * \param   无
 * \retval  无
 */
static int __init miscbeep_init(void)
{
    return platform_driver_register(&beep_driver);
}

/**
 * \brief   驱动模块缷载函数
 * \param   无
 * \return  无
 */
static void __exit miscbeep_exit(void)
{
    platform_driver_unregister(&beep_driver);
}

/* 设备注册入口, 展开后
 * static initcall_t \
 *        __initcall_miscbeep_init6 \
 *        __used \
 *        __attribute__((__section__(".initcall6.init"))) \
 *        = miscbeep_init;
 */
module_init(miscbeep_init);

/* 设备注册出口, 展开后
 * static exitcall_t \
 *        __exitcall_miscbeep_exit \
 *        __exit_call \
 *        = miscbeep_exit;
 */
module_exit(miscbeep_exit);

/* 模块的许可证声明, 展开后
 * static const char __UNIQUE_ID_license__COUNTER__[] \
 *   __used __attribute__((section(".modinfo"), unused, aligned(1))) \
 *  = "license=GPL";
 */
MODULE_LICENSE("GPL");

/* 模块的作者声明, 展开后
 * static const char __UNIQUE_ID_author__COUNTER__[]					  \
 * __used __attribute__((section(".modinfo"), unused, aligned(1)))	  \
 * = "author=glen_cao"
 */
MODULE_AUTHOR("glen");
  1. 编写miscbeep测试程序
/*
 * 文件名   :  miscbeep_test.c
 * 作者     :  glen
 * 描述     :  miscbeep测试程序
 */

#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"
#include "poll.h"
#include "sys/select.h"
#include "sys/time.h"
#include "linux/ioctl.h"
#include "signal.h"


/**
 * @brief   : main函数
 * @par     : argc  argv数组元素的个数
 *            argv  参数数组
 * @retval  : 0 成功    其它 失败
 */
int main(int argc, char *argv[])
{
    static int fd = 0;  /* 文件描述符 */
    int ret = 0;
    char *filename;

    if (argc != 3) {
        printf("Error Usage!\r\n");
        return -1;
    }

    filename = argv[1];

    /* 打开驱动文件 */
    fd = open(filename, O_RDWR);       /* 阻塞访问 */
    if (fd < 0) {
        printf("Can't open file %s\r\n", filename);
        return -1;
    }

    ret = write(fd, argv[2], sizeof(argv[2]));
    if (ret < 0) {
        printf("BEEP control failure!\r\n");
        close(fd);
        return -1;
    }

    /* 关闭文件 */
    ret = close(fd);
    if (ret < 0) {
        printf("file %s close failed!\r\n", argv[1]);
        return -1;
    }
    return 0;
}

拷贝到NFS文件系统目录下

glen@ubuntu:~/linux/imx6ull/linux/driver/17_miscbeep$ sudo cp miscbeep.ko miscbeep_test ~/linux/nfs/rootfs/lib/modules/4.1.15
  1. 测试程序
    4.1 加载驱动模块
/lib/modules/4.1.15 # insmod miscbeep.ko
beep driver and device was matched!

4.2 查看杂项驱动目录下信息(miscbeep)

/lib/modules/4.1.15 # ls /sys/class/misc
autofs              loop-control        network_throughput
cpu_dma_latency     memory_bandwidth    rfkill
fuse                miscbeep            ubi_ctrl
hw_random           network_latency     watchdog

4.3 测试,在终输入下面信息,可以听到蜂鸣器开/关

/lib/modules/4.1.15 # ./miscbeep_test /dev/miscbeep ON
/lib/modules/4.1.15 # ./miscbeep_test /dev/miscbeep OFF
/lib/modules/4.1.15 # ./miscbeep_test /dev/miscbeep OFF
/lib/modules/4.1.15 # ./miscbeep_test /dev/miscbeep ON
/lib/modules/4.1.15 # ./miscbeep_test /dev/miscbeep OFF
/lib/modules/4.1.15 # ./miscbeep_test /dev/miscbeep ON
/lib/modules/4.1.15 # ./miscbeep_test /dev/miscbeep OFF
/lib/modules/4.1.15 # ./miscbeep_test /dev/miscbeep ON
/lib/modules/4.1.15 # rmmod miscbeep

你可能感兴趣的:(Linux)