一、修改设备树文件
这里为了以后方便开发,我将荔枝派的设备树文件复制了一份,并且改成了我自己的名字。
然后在更改后的文件内添加自己需要的内容,比如我们这次使用的IO口信息。
和非设备树方式的IO口驱动一样,以PE2为例进行实验。
二、编写设备树版本的IO口驱动
#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 <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/cdev.h>
#include <linux/device.h>
#define DTSIO_CNT 1
#define DTSIO_NAME "DTS_IO"
#define HIGH 1
#define LOW 0
static void __iomem *PE_CFG0;
static void __iomem *PE_DATA;
struct dtsio_dev
{
/* data */
dev_t devid; //asd
struct cdev cdev;
struct class *class;
struct device *device;
int major;
int minor;
struct device_node *node;
};
struct dtsio_dev dtsio ;
void io_switch(u8 sta)
{
u32 val=0;
if(sta==HIGH)
{
val=readl(PE_DATA);
val|= (1 << 2);
writel(val, PE_DATA);
}
else if(sta==LOW)
{
val=readl(PE_DATA);
val &= ~(1 << 2);
writel(val, PE_DATA);
}
}
static int io_open(struct inode *inode,struct file *filp)
{
filp->private_data = &dtsio;
return 0;
}
static ssize_t io_read(struct file *filp,char __user *buf,size_t cnt,loff_t *offt)
{
return 0;
}
static ssize_t io_write(struct file *filp,const char __user *buf,size_t cnt, loff_t *offt)
{
int ret;
unsigned char databuf[1];
unsigned char io_sta;
ret = copy_from_user(databuf,buf,cnt);
if(ret<0)
{
printk("Kernel write failed!\r\n");
}
if(databuf[0]==HIGH)
{
io_switch(HIGH);
}
else if (databuf[0]==LOW)
{
io_switch(LOW);
}
return 0;
}
static int io_release(struct inode *inode,struct file *filp)
{
return 0;
}
/*
* 设备操作函数结构体
*/
static struct file_operations dtsio_fops = {
.owner = THIS_MODULE,
.open = io_open,
.read = io_read,
.write = io_write,
.release = io_release,
};
/*
* @description : 驱动入口函数
* @param : 无
* @return : 0 成功;其他 失败
*/
static int __init io_init(void)
{
int ret;
unsigned char i;
unsigned int val;
const char *str;
unsigned int regdata[10];
struct property *proper;
dtsio.node = of_find_node_by_path("/testIO");
if(dtsio.node == NULL)
{
printk("testIO node not found!\r\n");
return -EINVAL;
}
else
{
printk("testIO node has been found!\r\n");
}
proper = of_find_property(dtsio.node,"compatible",NULL);
if (proper == NULL)
{
printk("compatible property not found!\r\n");
}
else
{
printk("compatible = %s \r\n",(char *)proper->value);
}
ret = of_property_read_string(dtsio.node,"status",&str);
if(ret<0)
{
printk("test io status read failed!\r\n");
}
else
{
printk("status = %s \r\n",str);
}
ret = of_property_read_u32_array(dtsio.node,"reg",regdata,4);
if(ret<0)
{
printk("reg property read failed!\r\n");
}
else
{
printk("reg data : \r\n");
for(i=0;i<4;i++)
{
printk("%#x ",regdata[i]);
}
printk("\r\n");
}
PE_CFG0 = of_iomap(dtsio.node,0); // ioremap(PIO_CFG0(PE),1);
PE_DATA = of_iomap(dtsio.node,1); //ioremap(PIO_DATE(PE),1);
val = readl(PE_CFG0); //CFG
// printk(KERN_EMERG"PE_CFG0 = %x",val);
val &= ~(0x07<<8);
val |= 0x01<<8;
writel(val,PE_CFG0);
val = readl(PE_DATA); //PUL
val |= 0x00<<4; //00 disable 01 pull-up 10 pull-down 11 reserved
writel(val,PE_DATA);
if(dtsio.major)
{
dtsio.devid = MKDEV(dtsio.major,0);
register_chrdev_region(dtsio.devid,DTSIO_CNT,DTSIO_NAME);
}
else
{
alloc_chrdev_region(&dtsio.devid,0,DTSIO_CNT,DTSIO_NAME);
dtsio.major = MAJOR(dtsio.devid);
dtsio.minor = MINOR(dtsio.devid);
}
printk("dtsio major = %d ,minor = %d \r\n",dtsio.major,dtsio.minor);
dtsio.cdev.owner = THIS_MODULE;
cdev_init(&dtsio.cdev,&dtsio_fops);
cdev_add(&dtsio.cdev,dtsio.devid,DTSIO_CNT);
dtsio.class = class_create(THIS_MODULE,DTSIO_NAME);
if(IS_ERR(dtsio.class))
{
return PTR_ERR(dtsio.class);
}
dtsio.device = device_create(dtsio.class,NULL,dtsio.devid,NULL,DTSIO_NAME);
if (IS_ERR(dtsio.device))
{
return PTR_ERR(dtsio.device);
}
return 0;
}
static void __exit io_exit(void)
{
iounmap(PE_CFG0);
iounmap(PE_DATA);
cdev_del(&dtsio.cdev);
unregister_chrdev_region(dtsio.devid,DTSIO_CNT);
device_destroy(dtsio.class,dtsio.devid);
class_destroy(dtsio.class);
}
module_init(io_init);
module_exit(io_exit);
/*
* LICENSE 和作者信息
*/
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Distance");
代码就不过多介绍了,请参考正点原子嵌入式驱动开发教程
三、遇到的问题
其中有两个需要注意的地方,我在过程中遇到了相关问题
1、在write函数中使用了copy_from_user函数,传入的databuf参数必须是一个数组,否则驱动运行报错
static ssize_t io_write(struct file *filp,const char __user *buf,size_t cnt, loff_t *offt)
{
int ret;
unsigned char databuf[1];
unsigned char io_sta;
ret = copy_from_user(databuf,buf,cnt);
2、在init函数中,使用了property指针,printk打印property内信息时,需要进行强制类型转换
static int __init io_init(void)
{
int ret;
unsigned char i;
unsigned int val;
const char *str;
unsigned int regdata[10];
struct property *proper; /*注意这里*/
………………
proper = of_find_property(dtsio.node,"compatible",NULL);
if (proper == NULL)
{
printk("compatible property not found!\r\n");
}
else
{
printk("compatible = %s \r\n",(char *)proper->value); /*注意这里*/
}
………………
}
报错信息如下
四、运行测试
测试应用程序和非设备树的应用程序通用,可以直接拿来测试。
我们测试过程如下,仅供参考,需要根据自己的实际情况进行调整。