哪个网站做照片书最好,seo投放是什么意思,织梦网站建设实训总结,常州建设网站公司网站测试几天发现一个bug#xff0c;就是无法一次读取32个字节的数据#xff0c;1-31,33,128,512都试过了#xff0c;唯独无法读取32个字节#xff0c;驱动未报错#xff0c;但是读取的都是0#xff0c;找不到原因#xff0c;估计应该是全志iic驱动的问题#xff0c;暂时没…测试几天发现一个bug就是无法一次读取32个字节的数据1-31,33,128,512都试过了唯独无法读取32个字节驱动未报错但是读取的都是0找不到原因估计应该是全志iic驱动的问题暂时没有折腾尽量避开32字节读取吧32字节写入是没问题的。
使用的字符驱动可以读写任意字节32字节读取除外可以使用lseek设置读写地址首先设置内核设备数在对应的iic节点下添加fram支持。
fram: fram50 {compatible general,iic_fram;reg 0x50;};
驱动代码如下
// SPDX-License-Identifier: GPL-2.0
#include linux/kernel.h
#include linux/slab.h
#include linux/module.h
#include linux/delay.h
#include linux/i2c.h
#include linux/init.h
#include linux/leds.h
#include linux/mutex.h
#include linux/gpio/consumer.h
#include linux/of_gpio.h
#include linux/gpio.h
#include linux/regulator/consumer.h
#include linux/uaccess.h
#include linux/device.h //自动创建/dev设备节点需要
#include linux/kdev_t.h //设备号用到的头文件和宏函数
#include linux/fs.h
#include linux/cdev.h
#include linux/uaccess.htypedef unsigned int u32;
typedef unsigned short u16;
typedef unsigned char u8;typedef volatile unsigned int vu32;
typedef volatile unsigned short vu16;
typedef volatile unsigned char vu8;typedef unsigned int const uc32; /* Read Only */
typedef unsigned short const uc16; /* Read Only */
typedef unsigned char const uc8; /* Read Only */#define DEVICE_NAME iic_fram // 设备名字
#define DTS_COMPATIBLE general,iic_fram //设备树中对应的COMPATIBLE信息名称//注意fram使用的是 MB85RC16PNF-G-JNERE1 进行测试2KB容量FRAM的通讯有点不一样芯片地址为4bit然后3bit寄存器高地址1bit读写标识最终依旧使用的是8bit寄存器模式但是地址范围0-7FF//设备驱动私有结构体数据定义
struct fram_type {int fram_init_finished;struct i2c_client* fram_client;struct mutex mutex_lock;//定义互斥锁struct class* device_class; //注册后的设备节点classstruct device* device; //注册的设备dev_t devno; //设备号struct cdev cd;int size; //容量信息
};
static struct fram_type* sg_fram NULL; //再iic设备注册后进行初始化非NULL意味着硬件初始化正常在probe中进行初始化//注册驱动时传入参数参数为farm容量信息默认为2KB单位字节
static int SIZE -1; //通过注册驱动的时候传入参数如 insmode iic_fram SIZE4096 实现容量设置
module_param(SIZE, int, S_IRUSR); //S_IRUSR在include/linux/stat.h/*************************************************************************************************************************
*函数 : int fram_read_data(struct i2c_client* client, u16 addr, u16 ByteCount, u8* pData, bool isKernel)
*功能 : iic驱动读取寄存器数据
*参数 : client:句柄addr寄存器地址ByteCount要读取的数据数量pData:数据缓冲区;isKernel:是否是内核读取如果是内核读取将不需要进行内核数据与用户空间数据转换
*返回 : 0 错误其它读取的字节数
*依赖 : 底层宏定义
*作者 : cp1300139.com
*时间 : 2024-03-04
*最后修改时间 : 2024-03-04
*说明 :
*************************************************************************************************************************/
int fram_read_data(struct i2c_client* client, u16 addr, u16 ByteCount, u8 __user* pData, bool isKernel)
{int ret;u8 addr_buff[1];u8* pkbuff;struct i2c_msg msgs[] {//写命令{.addr client-addr | ((addr 8) 0x07),.flags 0,.len 1, //数据长度为1字节的地址.buf addr_buff, //寄存器地址},//读取数据{.addr client-addr | ((addr 8) 0x07),.flags I2C_M_RD,.len ByteCount, //数据长度为n字节的数据.buf NULL, //buf-等会设置}};//dev_info(client-dev, i2c_smbus_read_16bit_i2c_block_data addr%d ByteCount%d client-addr%d\r\n, addr, ByteCount, client-addr);//申请内核内存准备读取数据pkbuff (u8*)kmalloc(ByteCount, GFP_KERNEL | GFP_DMA | __GFP_ZERO); //申请内存-iic会用到DMA内存要连续if (pkbuff NULL){printk(fram read out of memory! kmalloc(%dB)\r\n, ByteCount);return -1;}msgs[1].buf pkbuff; //读取的数据存放到缓冲区//准备寄存器地址数据addr_buff[0] addr 0xFF; //地址低位ret i2c_transfer(client-adapter, msgs, ARRAY_SIZE(msgs)); //写入命令读取数据if (ret 0){dev_err(client-dev, i2c_transfer read addr%d ByteCount%d failed!\r\n, addr, ByteCount);ret -1;}else{if (isKernel true) //当前处于内核空间{memcpy(pData, pkbuff, ByteCount);ret ByteCount; //返回数据长度}else{ret copy_to_user(pData, pkbuff, ByteCount); //读取成功将数据拷贝到用户空间if (ret ! 0){printk(fram copy_to_user error(%dB)\r\n, ret);ret -1;}else{ret ByteCount; //返回数据长度}}}kfree(pkbuff); //释放申请的内存return ret;
}/*************************************************************************************************************************
*函数 : int fram_write_data(struct i2c_client* client, u16 addr, u16 ByteCount, u8* pData)
*功能 : iic驱动写入寄存器数据
*参数 : client:句柄addr寄存器地址ByteCount要读取的数据数量pData:数据缓冲区
*返回 : 0 错误其它读取的字节数
*依赖 : 底层宏定义
*作者 : cp1300139.com
*时间 : 2024-03-04
*最后修改时间 : 2024-03-04
*说明 :
*************************************************************************************************************************/
int fram_write_data(struct i2c_client* client, u16 addr, u16 ByteCount, const u8 __user* pData)
{int ret;u8* buf;struct i2c_msg msgs[] {//写数据命令{.addr client-addr | ((addr 8) 0x07),.flags 0,.len ByteCount 1, //数据长度为2字节的地址n字节的数据//.buf reg, //buf等会赋值需要申请内存}};//dev_info(client-dev, i2c_smbus_write_16bit_i2c_block_data write addr%d ByteCount%d\r\n, addr, ByteCount);buf (u8*)kmalloc(ByteCount 1, GFP_KERNEL | GFP_DMA | __GFP_ZERO); //申请内存-iic会用到DMA内存要连续if (buf NULL){dev_err(client-dev, i2c_smbus_write_16bit_i2c_block_data out of memory! kmalloc(%dB)\r\n, ByteCount 1);return -1;}msgs[0].buf buf; //记录申请的内存//准备数据buf[0] addr 0xFF; //地址低位ret copy_from_user(buf[1], pData, ByteCount); //用户空间数据拷贝到内核空间if (ret ! 0) {printk(fram copy_from_user error(%dB)\r\n, ret);ret -EFAULT;}else{ret i2c_transfer(client-adapter, msgs, ARRAY_SIZE(msgs)); //写入数据if (ret ARRAY_SIZE(msgs)){ret ByteCount; //返回写入的数据长度}else{dev_err(client-dev, i2c_transfer write addr%d ByteCount%d failed!\r\n, addr, ByteCount);ret -1;}}kfree(buf); //释放申请的内存return ret;
}/*************************************************************************************************************************
*函数 : int fram_Init(struct i2c_client* client)
*功能 : fram初始化
*参数 : client:iic句柄
*返回 : 0:初始化成功其它:初始化失败
*依赖 : 底层宏定义
*作者 : cp1300139.com
*时间 : 2024-03-03
*最后修改时间 : 2024-03-03
*说明 :
*************************************************************************************************************************/
int fram_Init(struct i2c_client* client)
{u8 i;struct fram_type* fram i2c_get_clientdata(client); //获取私有数据int ret;u8 temp;if (fram NULL){dev_err(client-dev, i2c_get_clientdata(client) null\r\n);return -1;}//读取地址0只要能读取到就认为初始化成功for (i 0; i 3; i){ret fram_read_data(client, 0, 1, temp, true);if (ret 0){dev_err(client-dev, Failed to read 0x00\r\n);msleep(5);}else{dev_info(client-dev, addr 0x00:0x%X\r\n, temp);break;}}if (ret 0) return -1;return 0;
}//
//标准文件接口相关
/** description : 打开设备* param - inode : 传递给驱动的inode* param - filp : 设备文件file结构体有个叫做private_data的成员变量* 一般在open的时候将private_data指向设备结构体。* return : 0 成功;其他 失败*/
static int device_open(struct inode* inode, struct file* filp)
{if (sg_fram NULL){printk(fram not initialized\r\n);return -1;}//dev_info(sg_fram-fram_client-dev, device_open\n);return 0;
}/** description : 从设备读取数据* param - filp : 要打开的设备文件(文件描述符)* param - buf : 返回给用户空间的数据缓冲区* param - cnt : 要读取的数据长度* param - offt : 相对于文件首地址的偏移* return : 读取的字节数如果为负值表示读取失败*/
static ssize_t device_read(struct file* filp, char __user* buf, size_t cnt, loff_t* offt)
{u32 offset (u32)*offt;int ret;if (sg_fram NULL){printk(fram not initialized\r\n);return -1;}if (buf NULL){printk(buf is NULL!\r\n);return -EFAULT;}if (cnt 0 || cnt sg_fram-size || (cnt offset) sg_fram-size){printk(fram Read out of range cnt%d offt%d(max size:%dB)\r\n, cnt, offset, sg_fram-size);return 0;}//读取数据mutex_lock(sg_fram-mutex_lock); //阻塞式上互斥锁抢不到就一直阻塞ret fram_read_data(sg_fram-fram_client, (u16)offset, (u16)cnt, buf, false);mutex_unlock(sg_fram-mutex_lock); //解锁return ret;
}/** description : 向设备写数据* param - filp : 设备文件表示打开的文件描述符* param - buf : 要写给设备写入的数据* param - cnt : 要写入的数据长度* param - offt : 相对于文件首地址的偏移* return : 写入的字节数如果为负值表示写入失败*/
static ssize_t device_write(struct file* filp, const char __user* buf, size_t cnt, loff_t* offt)
{u32 offset (u32)*offt;int ret;if (sg_fram NULL){printk(fram not initialized\r\n);return -1;}if (buf NULL){printk(buf is NULL!\r\n);return -EFAULT;}if (cnt 0 || cnt sg_fram-size || (cnt offset) sg_fram-size){printk(fram write out of range cnt%d offt%d(max size:%dB)\r\n, cnt, offset, sg_fram-size);return 0;}mutex_lock(sg_fram-mutex_lock); //阻塞式上互斥锁抢不到就一直阻塞ret fram_write_data(sg_fram-fram_client, (u16)offset, (u16)cnt, buf);mutex_unlock(sg_fram-mutex_lock); //解锁return ret;
}/** description : 设置文件读写偏移* param - filp : 设备文件表示打开的文件描述符* param - off : 读写偏移* param - whence : 光标参考位置* return : 当前文件的纸质位置如果为负值表示写入失败*/
static loff_t device_llseek(struct file* filp, loff_t off, int whence)
{//struct scull_dev* dev filp-private_data;loff_t newpos;//printk(device_llseek off%u whence%d\r\n, (u32)off, whence);switch (whence){case 0: //SEEK_SET 从开始的偏移{newpos off;if (newpos sg_fram-size) newpos sg_fram-size - 1; //防止超出范围}break;case 1: //SEEK_CUR 在当前位置加上偏移{newpos filp-f_pos off;if (newpos sg_fram-size) newpos sg_fram-size - 1; //防止超出范围}break;case 2: //SEEK_END 偏移位置文件结尾 之外{newpos sg_fram-size - 1; //不允许超出文件}break;default: return -EINVAL;}if (newpos 0) return -EINVAL;filp-f_pos newpos;return newpos;
}/** description : 关闭/释放设备* param - filp : 要关闭的设备文件(文件描述符)* return : 0 成功;其他 失败*/
static int device_release(struct inode* inode, struct file* filp)
{//printk(fram_release\r\n);return 0;
}//申请i2c资源顺便进行初始化
static int device_probe(struct i2c_client *client,const struct i2c_device_id *id)
{int ret;struct fram_type* fram;dev_info(client-dev, fram_probe\n);//判断iic适配器是否正常if (!i2c_check_functionality(client-adapter, I2C_FUNC_I2C)){return -ENODEV;}//申请内核内存资源fram devm_kzalloc(client-dev, sizeof(struct fram_type), GFP_KERNEL);if (!fram){return -ENOMEM;}fram-fram_init_finished 0; //初始化未完成fram-fram_client client; //记录i2c接口指针fram-device_class NULL; //设备节点无效fram-device NULL; //设备节点无效//需要提前设置否则在 fram_Init 中需要调用 i2c_get_clientdata () 将返回空i2c_set_clientdata(client, fram); // 将fram作为i2c次设备的私有数据区中的设备驱动私有数据if (SIZE 0){dev_info(client-dev, SIZE%dB\n, SIZE 0xFFFF);}else{SIZE 2048; //默认为2KB}if (SIZE 128) SIZE 128;if (SIZE 0x7FF) SIZE 0x7FF; //最大大小限制为0x7FFfram-size SIZE; //初始化FRAM大小dev_info(client-dev, FRAM SIZE:%dB\n, fram-size);//开始芯片硬件探测与初始化ret fram_Init(client); //初始化并设置初值也可以通过注册驱动的时候传入参数实现初始化if (ret 0){dev_err(client-dev, invalid init fram\n);return -1;}mutex_init(fram-mutex_lock);//初始化互斥锁sg_fram fram; //记录全局设备数据设备硬件初始化完成了fram-fram_init_finished 1; //初始化完成dev_info(client-dev, fram probe succeeded\n);return 0;
}//移除iic驱动
static int device_remove(struct i2c_client* client)
{struct fram_type* fram i2c_get_clientdata(client); //获取私有数据dev_info(client-dev, fram_remove\n);//释放互斥锁信号if (fram ! NULL fram-fram_init_finished){mutex_destroy(fram-mutex_lock);}return 0;
}//
//iic接口相关
static const struct of_device_id fram_match_table[] {{.compatible DTS_COMPATIBLE,},{},
};
MODULE_DEVICE_TABLE(of, fram_match_table);static const struct i2c_device_id fram_id[] {{ DTS_COMPATIBLE, 0 },{},
};
MODULE_DEVICE_TABLE(i2c, fram_id);static struct i2c_driver fram_driver {.driver {.name DEVICE_NAME,.owner THIS_MODULE,.of_match_table fram_match_table,},.probe device_probe, //注册IIC.remove device_remove,.id_table fram_id,};//module_i2c_driver(fram_driver); //需要使用 i2c_add_driver 在 init中进行注册iic适配器不能直接使用宏//设备驱动操作相关接口结构体
static struct file_operations sg_device_opera_fops {.owner THIS_MODULE,.open device_open, //打开驱动文件接口.read device_read, //读取接口.write device_write, //写文件接口.release device_release, //释放文件接口.llseek device_llseek, //设置文件偏移//.ioctl device_ioctl, //参数设置接口
};//驱动接口-驱动入口函数-初始化与申请资源
static int __init device_init(void)
{int retvalue;//printk(device_module_init\n);retvalue i2c_add_driver(fram_driver); //添加iic驱动if (retvalue) {printk(%s i2c_add_driver failed! %d\n, __func__, retvalue);return -ENODEV;}if (sg_fram NULL) //设备硬件初始化失败{printk(initialization failed!\r\n);return -EIO;}//注册字符设备驱动retvalue alloc_chrdev_region(sg_fram-devno, 0, 1, DEVICE_NAME); //自动申请设备号从0开始申请1个if (retvalue 0) {pr_err(alloc_chrdev_region failed!(%d), retvalue);i2c_del_driver(fram_driver); //移除iic设备return retvalue;}printk(MAJOR is %d\n, MAJOR(sg_fram-devno));printk(MINOR is %d\n, MINOR(sg_fram-devno));cdev_init(sg_fram-cd, sg_device_opera_fops); //字符驱动结构体初始化retvalue cdev_add(sg_fram-cd, sg_fram-devno, 1); //注册字符设备驱动数量1if (retvalue 0) {pr_err(cdev_add failed!(%d), retvalue);i2c_del_driver(fram_driver); //移除iic设备unregister_chrdev_region(sg_fram-devno, 1); //注销一个范围的设备号return retvalue;}//自动在/dev目录下创建设备节点sg_fram-device_class class_create(THIS_MODULE, DEVICE_NAME); 创建类if (NULL sg_fram-device_class){printk(KERN_INFO create calss failed\n);cdev_del(sg_fram-cd);unregister_chrdev_region(sg_fram-devno, 1); //注销一个范围的设备号i2c_del_driver(fram_driver); //移除iic设备return -1;}else{sg_fram-device device_create(sg_fram-device_class, NULL, sg_fram-devno, NULL, DEVICE_NAME); //创建设备if (NULL sg_fram-device){printk(KERN_INFO create device failed\n);cdev_del(sg_fram-cd);unregister_chrdev_region(sg_fram-devno, 1);class_destroy(sg_fram-device_class);i2c_del_driver(fram_driver); //移除iic设备return -1;}}printk(%s succeeded\n, DEVICE_NAME);return 0;
}//驱动接口-驱动出口函数-注销资源
static void __exit device_exit(void)
{printk(device_module_exit\n);i2c_del_driver(fram_driver); //移除iic设备cdev_del(sg_fram-cd); //注销字符设备驱动unregister_chrdev_region(sg_fram-devno, 1); //注销设备号device_del(sg_fram-device);printk(KERN_INFO delete device /dev/my_char_dev \n);class_destroy(sg_fram-device_class);printk(KERN_INFO delete device /sys/class/my_char_dev \n);}module_init(device_init);
module_exit(device_exit);MODULE_LICENSE(GPL);
MODULE_AUTHOR(cp1300139.com);测试代码 void fram_test(void)
{int fd;u8 buff[512];int len;int i;fd open(/dev/iic_fram, O_RDWR);if(fd 0){printf(open iic_frame error:%d\r\n, fd);return ;}lseek(fd, 0, SEEK_SET);len read(fd, buff, 32);if(len 0){printf(read iic_frame error:%d\r\n, fd);return ;}for(i 0;i len;i ){printf(0x%02X \t, buff[i]);}printf(\r\n);//lseek(fd, 0, SEEK_SET);/*for(i 0;i len;i ){buff[i] i0xF0;}len write(fd, buff, len);if(len 0){printf(write iic_frame error:%d\r\n, fd);return ;}*/close(fd);}
读取32字节全部是0 试试33字节就正常了 仔细看底层驱动的打印信息 sunxi-i2c sunxi-i2c2: drv-mode: dma read data end
读取32字节的时候这个打印都结束了才提示DMA读取完成很有可能就是因为数据都没读取完成但是底层已经返回了原因未知珍爱生命远离linux驱动凑合着用吧(⊙o⊙)