Linux下SPI设备驱动实验:向SPI驱动框架中加入字符设备驱动框架代码

一.  简介

前一篇文章编写了SPI设备驱动框架代码,文章如下:

Linux下SPI设备驱动实验:SPI设备驱动框架编写-CSDN博客

本文继续SPI驱动代码的编写。向SPI驱动框架中加入字符设备驱动框架代码。

二.  向SPI驱动框架中加入字符设备驱动框架代码

1.  添加字符设备驱动框架的代码

打开 ubuntu系统,通过 vscode 打开18_spi 工程。向SPI设备驱动框架中添加字符设备驱动框架的代码。添加如下:

(1)  字符设备注册的一套流程,放在 spi_driver的 probe函数中。

(2)  字符设备注销的一套流程,放在 spi_driver的 remove函数中。

添加字符设备驱动框架的代码后,spi_icm20608.c文件中代码如下:

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/of.h>
#include <linux/spi/spi.h>
#include <linux/delay.h>

#define  ICM20608_NAME   "icm20608"
#define  ICM20608_CNT    1

//设备结构体
struct icm20608_Dev{
    dev_t devid;  //设备号
    int major;     //主设备号
    int minor;     //次设备号 
    struct cdev led_cdev; 
    struct class * class;   //类(自动创建设备节点用)
    struct device * device; //设备(自动创建设备节点用) 
};

struct icm20608_Dev icm20608_dev;

/*打开设字符备函数 */
static int icm20608_open(struct inode *inode, struct file *filp)
{
    printk("icm20608_open\n");
    return 0;
}

/*读字符设备中数据的函数*/
ssize_t icm20608_read(struct file * filp, char __user * buf, size_t count, loff_t * ppos)
{
    int ret = 0;
    printk("icm20608_read!\n");
    return ret;
}

/*关闭设备函数*/
int icm20608_release(struct inode * inode, struct file * filp)
{
    printk("led_release\n");
    return 0;
}

//字符设备的函数集
const struct file_operations fops = {  
    .owner = THIS_MODULE,
    .open = icm20608_open,
    .read = icm20608_read,
    .release = icm20608_release,
};

static int icm20608_probe(struct spi_device* spi_dev)
{
    int ret = 0;
    printk("icm20608_probe!\n");
        //设备号的分配
    icm20608_dev.major = 0;
    if(icm20608_dev.major) //给定主设备号
    {
        icm20608_dev.devid = MKDEV(icm20608_dev.major, 0);
        ret = register_chrdev_region(icm20608_dev.devid, ICM20608_CNT, ICM20608_NAME);
    }
    else{ //没有给定主设备号
        ret = alloc_chrdev_region(&(icm20608_dev.devid), 0, ICM20608_CNT, ICM20608_NAME);	
        icm20608_dev.major = MAJOR(icm20608_dev.devid);
        icm20608_dev.minor = MINOR(icm20608_dev.devid);
    }
    printk("dev.major: %d\n", icm20608_dev.major);
    printk("dev.minor: %d\n", icm20608_dev.minor);
	if (ret < 0) {
		printk("register-chrdev failed!\n");
		goto fail_devid;
	}
    //初始化设备
    icm20608_dev.led_cdev.owner = THIS_MODULE;
    cdev_init(&icm20608_dev.led_cdev, &fops);
    
    //注册设备
    ret = cdev_add(&icm20608_dev.led_cdev, icm20608_dev.devid, ICM20608_CNT);
    if(ret < 0)
    {
        printk("cdev_add failed!\r\n");
        goto fail_adddev;
    }

/* 3.自动创建设备节点 */
    //创建类
    icm20608_dev.class = class_create(THIS_MODULE, ICM20608_NAME); 
    if (IS_ERR(icm20608_dev.class)) {
		ret = PTR_ERR(icm20608_dev.class);
		goto fail_class;
	}   
    //创建设备
    icm20608_dev.device = device_create(icm20608_dev.class, NULL, icm20608_dev.devid, NULL, ICM20608_NAME);
    if (IS_ERR(icm20608_dev.device)) {
		ret = PTR_ERR(icm20608_dev.device);
		goto fail_dev_create;
	}  

    return 0;

fail_dev_create:
    class_destroy(icm20608_dev.class);
fail_class:
    cdev_del(&icm20608_dev.led_cdev);
fail_adddev:
    unregister_chrdev_region(icm20608_dev.devid, ICM20608_CNT);
fail_devid:
    return ret;
}

static int icm20608_remove(struct spi_device* spi_dev)
{
    printk("icm20608_remove!\n");
    /*注销字符设备*/
    //1. 删除设备
    cdev_del(&icm20608_dev.led_cdev);
    //2. 注销设备号
    unregister_chrdev_region(icm20608_dev.devid, ICM20608_CNT);

    /*摧毁类与设备(自动创建设备节点时用) */
    //3. 摧毁设备
    device_destroy(icm20608_dev.class, icm20608_dev.devid);
    //4. 摧毁类
    class_destroy(icm20608_dev.class);
    return 0;
}

//传统驱动与设备匹配方法
static struct spi_device_id spi_device_id_table[] = {
    { "icm20608", 0},
    { }
};

//设备树匹配方法
static struct of_device_id of_device_table[] = {
    { .compatible = "alientek,icm20608" },  //必须与设备树中设备节点compatible值一致
    { }
};

/*SPI驱动结构体*/
struct spi_driver icm20608_driver = {
    .driver = {
        .name = "icm20608",
        .owner = THIS_MODULE,
        .of_match_table = of_match_ptr(of_device_table),
    },
    .id_table = spi_device_id_table,
    .probe = icm20608_probe,
    .remove = icm20608_remove,
};

/*模块加载 */
static int __init icm20608_init(void)
{
    return spi_register_driver(&icm20608_driver);
}

/*模块卸载 */
static void __exit icm20608_exit(void)
{
    spi_unregister_driver(&icm20608_driver);
}

/*驱动加载与卸载 */
module_init(icm20608_init);
module_exit(icm20608_exit);
MODULE_LICENSE("GPL"); //模块 Licence
MODULE_AUTHOR("WeiWuXian"); //作者

2.  编译驱动模块

对以上的驱动代码进行模块化编译:

wangtian@wangtian-virtual-machine:~/zhengdian_Linux/Linux_Drivers/18_spi$ make
make -C /home/wangtian/zhengdian_Linux/linux/kernel/linux-imx-rel_imx_4.1.15_2.1.0_ga M=/home/wangtian/zhengdian_Linux/Linux_Drivers/18_spi modules
make[1]: 进入目录“/home/wangtian/zhengdian_Linux/linux/kernel/linux-imx-rel_imx_4.1.15_2.1.0_ga”
  CC [M]  /home/wangtian/zhengdian_Linux/Linux_Drivers/18_spi/spi_icm20608.o
  Building modules, stage 2.
  MODPOST 1 modules
  CC      /home/wangtian/zhengdian_Linux/Linux_Drivers/18_spi/spi_icm20608.mod.o
  LD [M]  /home/wangtian/zhengdian_Linux/Linux_Drivers/18_spi/spi_icm20608.ko
make[1]: 离开目录“/home/wangtian/zhengdian_Linux/linux/kernel/linux-imx-rel_imx_4.1.15_2.1.0_ga”

可以看出,驱动模块正常编译通过。

三. 编写应用程序(测试程序) 

在 18_spi工程目录下,创建应用程序 icm20608_app.c文件。icm20608_app.c文件代码实现如下:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>

/*
* 打开/关闭 Led灯
* 参数:
* ./icm20608_app /dev/icm20608 
*/
int main(int argc, char* argv[])
{
    int fd = 0,ret = 0;
    char * device_name = NULL;
    unsigned short data[3] = {0};

    if(argc != 2)
    {
        printf("main's param number error!\n");
        return -1;
    }

    device_name = argv[1];
    fd = open(device_name, O_RDWR);
    if(fd < 0)
    {
        printf("open led device failed!\n");
        return -1;
    }
    
    ret = read(fd, data, sizeof(data));

    close(fd);
    return 0;
}

终端进入 18_spi工程的根目录下,输入如下命令编译应用程序:

arm-linux-gnueabihf-gcc icm20608_app.c -o icm20608_app

这里可以编译通过。生成 icm20608_app。

接下来通过运行应用程序,对驱动模块进行测试,确定是否会正常运行到 open函数,read函数,release函数。

测试方法以及确认SPI驱动是否运行正常,可以参考如下文章:

I2C驱动实验:测试I2C驱动框架代码-CSDN博客

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值