Linux SPI 字符设备 驱动例子
其实,在Linux中,SPI和IIC的注册流程很相似。在这里我还是用iTOP4412做演示。从原理图可以得知我们要用到的引脚是这几个。用的是SPI_2。记住这个数字,下面设备注册要用到。首先我们要把iTOP4412中默认关于rfid的驱动代码注释了,这样才能加载我们的驱动代码打开平台文件默认是这样的将它注释了然后编译,烧进板子上。首先是注册设备的代码...
其实,在Linux中,SPI和IIC的注册流程很相似。在这里我还是用iTOP4412做演示。
从原理图可以得知我们要用到的引脚是这几个。用的是SPI_2。记住这个数字,下面设备注册要用到。
首先我们要把iTOP4412中默认关于rfid的驱动代码注释了,这样才能加载我们的驱动代码
打开平台文件
默认是这样的
将它注释了
然后编译,烧进板子上。
首先是注册设备的代码
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/spi/spi.h>
#include <linux/spi/spidev.h>
#include <linux/spi/spi_gpio.h>
#include <linux/gpio.h>
#include <plat/s3c64xx-spi.h>
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("HQU_Orange");
#define SPI_Device_Name "SPI_MDN"
#define SPI_Device_Bus_Num 2
static struct s3c64xx_spi_csinfo my_spi2_csi =
{
.line = EXYNOS4_GPC1(2),
.set_level = gpio_set_value,
.fb_delay = 0x2,
};
static struct spi_board_info SPI_Device_INFO =
{
.modalias = SPI_Device_Name, //初始化设备的名称
.platform_data = NULL, //平台文件信息
.max_speed_hz = 10*1000*1000, //初始化传输速率
.bus_num = SPI_Device_Bus_Num, //控制器编号
.chip_select = 0, //控制器片选的编号
.mode = SPI_MODE_0, //spi的模式
.controller_data = &my_spi2_csi,//片选IO的信息
};
static struct spi_device* MySPI_Device;
static int __init Mini_Linux_Driver_Init(void)
{
struct spi_master* MySPI_Mawster;
MySPI_Mawster=spi_busnum_to_master(SPI_Device_Bus_Num);
MySPI_Device=spi_new_device(MySPI_Mawster,&SPI_Device_INFO);
return 0;
}
static void __exit Mini_Linux_Driver_Exit(void)
{
spi_unregister_device(MySPI_Device);
}
module_init(Mini_Linux_Driver_Init);
module_exit(Mini_Linux_Driver_Exit);
注意这个片选引脚是上面我们注释了的片选引脚,这里要对应的上。
这里的SPI_Device_Bus_Num就是上面我们在板子上看到的2,表示这个设备搭载在SPI2上。
当然和IIC一样,也同样可以在仿写上面注释的部分注册SPI设备。
下面贴驱动代码
MySPI_driver.h
#ifndef _MYSPI_DRIVER_H_
#define _MYSPI_DRIVER_H_
#define SPI_Device_Name "SPI_MDN"
#define SPI_DeviceNode_Name "SPI_NodeName"
#define RC522_RESET_PIN EXYNOS4_GPK1(0)
#define DEVICE_NUM 1
#define DEV_MAJOR 0
#define DEV_MINOR 0
#define REGDEV_SIZE 3000
struct reg_dev
{
char *data;
unsigned long size;
struct cdev cdev;
};
#endif
MySPI_driver.c
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/spi/spi.h>
#include <linux/spi/spidev.h>
#include <linux/slab.h>
#include <linux/gpio.h>
#include <linux/fs.h>
#include <linux/moduleparam.h>
#include <linux/stat.h>
#include <linux/kdev_t.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <asm/uaccess.h>
#include <mach/gpio.h>
#include <plat/gpio-cfg.h>
#include <linux/compat.h>
#include "MySPI_driver.h"
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("HQU_Orange");
static struct spi_device* MySPI;
static int device_major_num;
static int device_minor_num;
static struct reg_dev *my_devices;
static struct class *myclass;
void rc522_reset(void);
int SPI_Driver_probe(struct spi_device *spi);
int SPI_Driver_remove(struct spi_device *spi);
int SPI_driver_fops_open(struct inode* p_inode,struct file* p_file);
int SPI_driver_fops_release(struct inode* p_inode,struct file* p_file);
ssize_t SPI_driver_fops_read(struct file *filp, char __user *buffer, size_t count, loff_t *ppos);
ssize_t SPI_driver_fops_write(struct file *filp,const char __user *buffer, size_t count, loff_t *ppos);
static struct spi_driver SPI_Driver =
{
.driver =
{
.name = SPI_Device_Name,
.owner = THIS_MODULE,
},
.probe = SPI_Driver_probe,
.remove = SPI_Driver_remove,
};
static struct file_operations SPI_driver_fops =
{
.owner = THIS_MODULE,
.open = SPI_driver_fops_open,
.release= SPI_driver_fops_release,
.write = SPI_driver_fops_write,
.read = SPI_driver_fops_read,
};
int SPI_Driver_probe(struct spi_device *spi)
{
int res;
int i;
dev_t device_ID;
MySPI = spi;
rc522_reset();
res=alloc_chrdev_region(&device_ID,device_minor_num,DEVICE_NUM,MySPI->modalias); //动态获得主设备号
if(res<0)
{
printk(KERN_EMERG "%s device major num get fail !\r\n",MySPI->modalias);
return -1;
}
device_major_num = MAJOR(device_ID);
printk("%s major num get from kernl is %d And minor num you input is %d \r\n",MySPI->modalias,device_major_num,device_minor_num);
my_devices = kmalloc(DEVICE_NUM * sizeof(struct reg_dev),GFP_KERNEL);
if(my_devices==NULL)
{
unregister_chrdev_region(device_ID,DEVICE_NUM);
printk(KERN_EMERG "kmalloc is fail!\n");
return -1;
}
memset(my_devices,0,DEVICE_NUM * sizeof(struct reg_dev));
myclass = class_create(THIS_MODULE,MySPI->modalias); //创建设备类
for(i=0;i<DEVICE_NUM;i++)
{
my_devices[i].data = kmalloc(REGDEV_SIZE,GFP_KERNEL);
memset(my_devices[i].data,0,REGDEV_SIZE);
cdev_init(&my_devices[i].cdev,&SPI_driver_fops); //初始化设备结构体与文件搭建联系
res = cdev_add(&my_devices[i].cdev,MKDEV(device_major_num,device_minor_num+i),1); //注册到系统
if(res<0) printk(KERN_EMERG "char device %s %d add fail \r\n ",MySPI->modalias,i);
else printk(KERN_EMERG "char device %s %d add succees \r\n ",MySPI->modalias,i);
device_create(myclass,NULL,MKDEV(device_major_num,device_minor_num+i),NULL,SPI_DeviceNode_Name"%d",i); //创建设备节点
}
return 0;
}
int SPI_Driver_remove(struct spi_device *spi)
{
printk(KERN_EMERG "char device %s remove \r\n ",spi->modalias);
return 0;
}
int SPI_driver_fops_open(struct inode* p_inode,struct file* p_file)
{
printk(KERN_EMERG "%s open \r\n ",p_file->f_path.dentry->d_iname);
return 0;
}
int SPI_driver_fops_release(struct inode* p_inode,struct file* p_filp)
{
printk(KERN_EMERG "%s close \r\n ",p_filp->f_path.dentry->d_iname);
return 0;
}
ssize_t SPI_driver_fops_read(struct file *filp, char __user *buffer, size_t count, loff_t *ppos)
{
char* value;
int status;
value=kmalloc(count*sizeof(char),GFP_KERNEL);
status=spi_read(MySPI,value,count);
status=copy_to_user(buffer,value,count);
kfree(value);
return status;
}
ssize_t SPI_driver_fops_write(struct file *filp, const char __user *buffer, size_t count, loff_t *ppos)
{
char* value;
int status;
value=kmalloc(count*sizeof(char),GFP_KERNEL);
status = copy_from_user(value,buffer,count);
status=spi_write(MySPI,value,count);
kfree(value);
return status;
}
void rc522_reset(void)
{
if(gpio_request_one(RC522_RESET_PIN, GPIOF_OUT_INIT_HIGH, "RC522_RESET"))
pr_err("failed to request GPK1_0 for RC522 reset control\n");
s3c_gpio_setpull(RC522_RESET_PIN, S3C_GPIO_PULL_UP);
gpio_set_value(RC522_RESET_PIN, 0);
mdelay(5);
gpio_set_value(RC522_RESET_PIN, 1);
gpio_free(RC522_RESET_PIN);
}
static int __init Mini_Linux_Driver_Init(void)
{
spi_register_driver(&SPI_Driver); //注册SPI驱动
return 0;
}
static void __exit Mini_Linux_Driver_Exit(void)
{
int i;
for(i=0;i<DEVICE_NUM;i++)
{
cdev_del(&(my_devices[i].cdev));
device_destroy(myclass,MKDEV(device_major_num,device_minor_num+i));
kfree(my_devices[i].data);
}
class_destroy(myclass);
kfree(my_devices);
unregister_chrdev_region(MKDEV(device_major_num,device_minor_num),DEVICE_NUM);
spi_unregister_driver(&SPI_Driver); //删除SPI驱动
}
module_param(device_minor_num,int,S_IRUSR); //输入次设备号
module_init(Mini_Linux_Driver_Init);
module_exit(Mini_Linux_Driver_Exit);
同样要注意在MySPI_driver.h中的SPI_Devcie_Name 要和前面注册的设备名字一致,这样才能匹配的上。
利用 spi_register_driver() 注册spi驱动,匹配成功后,执行prob
在probe中注册为具体设备
这是SPI的read和write的简易代码,请根据需要修改。
接下来进行实验
加载驱动模块
可以看到生成了在spi2.0这个主线的设备:SPI_MDN
加载驱动模块
成功生成了SPI_MDN驱动。
成功生成了SPI_NodeName0这个设备节点。
说明上面的设备和驱动都工作正常
成功卸载模块。
总结:
注册设备:
MySPI_Mawster=spi_busnum_to_master(SPI_Device_Bus_Num); //获得SPI控制器
MySPI_Device=spi_new_device(MySPI_Mawster,&SPI_Device_INFO);//将设备相关信息注册到控制器上
注册驱动:
spi_register_driver(&SPI_Driver); //注册SPI驱动
读写:
spi_write(MySPI,value,count); //写
spi_read(MySPI,value,count); //读
更多推荐
所有评论(0)