Linux学习笔记(4)

本文介绍了如何在Linux中编写一个Beep设备驱动,包括设备树的配置、GPIO的使用、字符设备的注册与操作,以及与外部应用程序的交互过程。
摘要由CSDN通过智能技术生成

Beep 驱动

修改设备树:

重启开发板,查看设备节点:

编写  beep  设备结构体:

//    beep  设备结构体
struct beep_dev{

    struct device_node    *nd;        /* 设备节点 */
    int beep_gpio;                               /* beep所使用的GPIO编号        */

    dev_t devid;                                   /* 设备号      */
    int major;                                        /* 主设备号      */
    int minor;                                        /* 次设备号   */
   
    struct cdev cdev;                        /* cdev     cdev 结构体表示一个字符设备    */

    struct device *device;                /* 设备      */
    struct class *class;                     /* 类         */
    
};

struct  beep_dev  beep;    

编写定义Beep:

#define BEEP_CNT                1              /* 设备号个数 */
#define BEEP_NAME            "beep"    /* 名字 */

#define BEEP_OFF                 0              /* 关  beep */
#define BEEP_ON                  1               /* 开  beep*/

编写驱动入口函数:

//      驱动入口函数
static int __init beep_init(void)
{

    int ret = 0;
    const char *str;



/* 设置 Beep 所使用的GPIO */


    /* 1、获取设备节点:beep */
    beep.nd = of_find_node_by_path("/beep");
    if(beep.nd == NULL) {
        printk("beep node not find!\r\n");
        return -EINVAL;
    }


    /* 2.读取status属性 */
    ret = of_property_read_string(beep.nd, "status", &str);
    if(ret < 0)
        return -EINVAL;
    if (strcmp(str, "okay"))
        return -EINVAL;


   
    /* 3、获取compatible属性值并进行匹配 */
    ret = of_property_read_string(beep.nd, "compatible", &str);
    if(ret < 0) {
        printk("beep: Failed to get compatible property\n");
        return -EINVAL;
    }
    if (strcmp(str, "luckylp,beep")) {
        printk("beep: Compatible match failed\n");
        return -EINVAL;
    }
   

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


    /* 5.向gpio子系统申请使用GPIO */
    ret = gpio_request(beep.beep_gpio, "BEEP-GPIO");
    if (ret) {
        printk(KERN_ERR "beep: Failed to request beep-gpio\n");
        return ret;
    }


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





    /* 注册字符设备驱动 */

    /* 1、创建设备号 */

    if (beep.major) {                 /*  定义了设备号 */

        beep.devid = MKDEV(beep.major, 0);               /* 使用 MKDEV 来构建设备号,次设备号选择 0*/

        ret = register_chrdev_region(beep.devid, BEEP_CNT, BEEP_NAME);       //   注册设备号

        if(ret < 0) {
            pr_err("cannot register %s char driver [ret=%d]\n", BEEP_NAME, BEEP_CNT);
            goto free_gpio;
        }

    }
     else {                        /* 没有定义设备号 */
        ret = alloc_chrdev_region(&beep.devid, 0, BEEP_CNT, BEEP_NAME);          /* 申请设备号 */

        if(ret < 0) {
            pr_err("%s Couldn't alloc_chrdev_region, ret=%d\r\n", BEEP_NAME, ret);
            goto free_gpio;
        }

        beep.major = MAJOR(beep.devid);         /* 获取分配号的主设备号 */
        beep.minor = MINOR(beep.devid);            /* 获取分配号的次设备号 */
    }

    printk("beep major=%d,minor=%d\r\n",beep.major, beep.minor);


    /* 2、初始化cdev */
    beep.cdev.owner = THIS_MODULE;
    cdev_init(&beep.cdev, &beep_fops);          //  cdev 就是要初始化的 cdev 结构体变量,参数 fops 就是字符设备文件操作函数集合


    /* 3、添加一个cdev */
    cdev_add(&beep.cdev, beep.devid, BEEP_CNT);     //  向 Linux 系统添加字符设备(cdev 结构体变量)
    if(ret < 0)
        goto del_unregister;


    /* 4、创建类 */
    beep.class = class_create(THIS_MODULE, BEEP_NAME);
    if (IS_ERR(beep.class)) {
        goto del_cdev;
    }


    /* 5、创建设备 */
    beep.device = device_create(beep.class, NULL, beep.devid, NULL, BEEP_NAME);
    if (IS_ERR(beep.device)) {
        goto destroy_class;
    }
    return 0;




destroy_class:
            class_destroy(beep.class);
del_cdev:
            cdev_del(&beep.cdev);
del_unregister:
            unregister_chrdev_region(beep.devid, BEEP_CNT);
free_gpio:
            gpio_free(beep.beep_gpio);

    return -EIO;

}

编写设备驱动出口函数:

//      驱动出口函数
static void __exit beep_exit(void)
{
        /* 注销字符设备驱动 */
    cdev_del(&beep.cdev);                                                                /*  删除cdev */
    unregister_chrdev_region(beep.devid, BEEP_CNT);     /* 注销设备号 */
    device_destroy(beep.class, beep.devid);                                /* 注销设备 */
    class_destroy(beep.class);                                                            /* 注销类 */
    gpio_free(beep.beep_gpio);                                                         /* 释放GPIO */

}



module_init(beep_init);                   //  注册模块加载函数
module_exit(beep_exit);                 //  注册模块卸载函数
MODULE_LICENSE("GPL");            //  LICENSE 采用 GPL 协议

编写设备操作函数:

//            打开设备
static int beep_open(struct inode *inode, struct file *filp)
{
        filp->private_data = &beep;             /* 设置私有数据 */
        return 0;
}


//          从设备读取数据
static ssize_t beep_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
        return 0;
}

//        向设备写数据
static ssize_t beep_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{

    int retvalue;
    unsigned char databuf[1];
    unsigned char beepstat;

    struct beep_dev *dev = filp->private_data;

    retvalue = copy_from_user(databuf, buf, cnt);             /* 接收APP发送过来的数据 */
    if(retvalue < 0) {

        printk("kernel write failed!\r\n");
        return -EFAULT;

    }

    beepstat = databuf[0];                /* 获取状态值 */

    if(beepstat == BEEP_ON) {    
        gpio_set_value(dev->beep_gpio, 0);            /* 打开beep */

    } else if(beepstat == BEEP_OFF) {

        gpio_set_value(dev->beep_gpio, 1);            /* 关闭beep */
    }

    return 0;

}

//            关闭/释放设备
static int beep_release(struct inode *inode, struct file *filp)
{
        return 0;
}




/* 设备操作函数 */
static struct file_operations beep_fops = {
    .owner = THIS_MODULE,
    .open =beep_open,
    .read = beep_read,
    .write = beep_write,
    .release = beep_release,
};

编写BeepAPP:


int main(int argc, char *argv[])
{

    char *filename;
    int fd, retvalue;
    unsigned char databuf[1];


    /* 1:   判断是否有3个参数     */
    if(argc != 3){

        printf("Error Usage!\r\n");
        return -1;
    }


     /*   2:    argv[1]保存着设备名字 ,   打开beep驱动 */
    filename = argv[1];
    fd = open(filename, O_RDWR);
    if(fd < 0){
        printf("file %s open failed!\r\n", argv[1]);
        return -1;
    }


      /* 3:     要执行的操作:打开或关闭    */
    databuf[0] = atoi(argv[2]);                  
    retvalue = write(fd, databuf, sizeof(databuf));         /*      向/dev/beep文件写入数据     */
    if(retvalue < 0){
        printf("BEEP Control Failed!\r\n");
        close(fd);
        return -1;
    }


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


     /* 5:   */
    return 0;


}
 

编译完成后:

depmod

modprobe beep

./beepAPP /dev/beep 1

同理,LED也一样。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

luckylp178

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值