Linux I2C设备驱动
1. 环境:
1.1 开发板:正点原子 I.MX6U ALPHA V2.2
1.2 开发PC:Ubuntu20.04
1.3 U-boot:uboot-imx-rel_imx_4.1.15_2.1.0_ga.tar.bz2
1.4 LInux内核:linux-imx-rel_imx_4.1.15_2.1.0_ga.tar.bz2
1.5 rootfs:busybox-1.29.0.tar.bz2制作
1.6 交叉编译工具链:gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf.tar.xz
2. 硬件说明
2.1 I2C器件:AP3216C(光强度、近距离、红外LED三合一传感器)
3. 设备树修改
3.1 在节点iomuxc/imx6ul-evk下新增pinctrl_i2c1节点,其实这个节点默认已经有了,且配置正确,如下
1 pinctrl_i2c1: i2c1grp { 2 fsl,pins = < 3 MX6UL_PAD_UART4_TX_DATA__I2C1_SCL 0x4001b8b0 4 MX6UL_PAD_UART4_RX_DATA__I2C1_SDA 0x4001b8b0 5 >; 6 };
3.2 在节点"&i2c1"中删除已经存在的节点,填入以下内容
1 &i2c1 { 2 clock-frequency = <100000>; 3 pinctrl-names = "default"; 4 pinctrl-0 = <&pinctrl_i2c1>; 5 status = "okay"; 6 7 ap3216c@1e { 8 compatible = "i2c-ap3216c"; 9 reg = <0x1e>; 10 }; 11 };
4. 驱动代码
1 #include <linux/types.h> 2 #include <linux/kernel.h> 3 #include <linux/delay.h> 4 #include <linux/ide.h> 5 #include <linux/init.h> 6 #include <linux/module.h> 7 #include <linux/errno.h> 8 #include <linux/gpio.h> 9 #include <linux/cdev.h> 10 #include <linux/device.h> 11 #include <linux/of_gpio.h> 12 #include <linux/semaphore.h> 13 #include <linux/timer.h> 14 #include <linux/i2c.h> 15 #include <asm/mach/map.h> 16 #include <asm/uaccess.h> 17 #include <asm/io.h> 18 19 #define AP3216C_ADDR 0X1E /* AP3216C器件地址 */ 20 21 /* AP3316C寄存器 */ 22 #define AP3216C_SYSTEMCONG 0x00 /* 配置寄存器 */ 23 #define AP3216C_INTSTATUS 0X01 /* 中断状态寄存器 */ 24 #define AP3216C_INTCLEAR 0X02 /* 中断清除寄存器 */ 25 #define AP3216C_IRDATALOW 0x0A /* IR数据低字节 */ 26 #define AP3216C_IRDATAHIGH 0x0B /* IR数据高字节 */ 27 #define AP3216C_ALSDATALOW 0x0C /* ALS数据低字节 */ 28 #define AP3216C_ALSDATAHIGH 0X0D /* ALS数据高字节 */ 29 #define AP3216C_PSDATALOW 0X0E /* PS数据低字节 */ 30 #define AP3216C_PSDATAHIGH 0X0F /* PS数据高字节 */ 31 32 struct ap3216c_device { 33 dev_t devno; 34 struct cdev ap3216c_cdev; 35 struct class *ap3216c_class; 36 struct device *ap3216c_dev; 37 struct device_node *node; 38 int major; 39 void *private_data; 40 unsigned short ir; 41 unsigned short als; 42 unsigned short ps; 43 }; 44 45 static struct ap3216c_device *ap3216c_dev; 46 47 48 //对寄存器多个写 49 static s32 ap3216c_write_regs(struct ap3216c_device *dev, u8 reg, u8 *data, u8 len) 50 { 51 u8 buf[256]; 52 struct i2c_msg msg; 53 54 struct i2c_client *client = (struct i2c_client *)dev->private_data; 55 buf[0] = reg; 56 memcpy(&buf[1], data, len); 57 msg.addr = client->addr; 58 msg.flags = 0; 59 msg.buf = buf; 60 msg.len = len + 1; 61 62 return i2c_transfer(client->adapter, &msg, 1); 63 64 } 65 66 //对寄存器单个写 67 static void ap3216c_write_reg(struct ap3216c_device *dev, u8 reg, u8 data) 68 { 69 u8 buf = 0; 70 buf = data; 71 ap3216c_write_regs(dev, reg, &buf, 1); 72 } 73 74 static int ap3216c_read_regs(struct ap3216c_device *dev, u8 reg, void *val, int len) 75 { 76 int ret; 77 struct i2c_msg msg[2]; 78 struct i2c_client *client = (struct i2c_client *)dev->private_data; 79 80 msg[0].addr = client->addr; 81 msg[0].flags = 0; 82 msg[0].buf = ® 83 msg[0].len = 1; 84 85 msg[1].addr = client->addr; 86 msg[1].flags = I2C_M_RD; 87 msg[1].buf = val; 88 msg[1].len = len; 89 90 ret = i2c_transfer(client->adapter, msg, 2); 91 if(2 == ret) 92 { 93 ret = 0; 94 } 95 else 96 { 97 printk("i2c read fail!\n"); 98 ret = -EREMOTEIO; 99 } 100 101 return ret; 102 } 103 104 static unsigned char ap3216c_read_reg(struct ap3216c_device *dev, u8 reg) 105 { 106 u8 data = 0; 107 ap3216c_read_regs(dev, reg, &data, 1); 108 return data; 109 } 110 111 112 //读取两个sensor及红外LED的六个寄存器值 113 static void ap3216c_readdata(struct ap3216c_device * dev) 114 { 115 unsigned char i = 0; 116 unsigned char buf[6]; 117 118 for(i = 0; i < 6; i++) 119 { 120 buf[i] = ap3216c_read_reg(dev, AP3216C_IRDATALOW + i); 121 } 122 123 if(buf[0] & 0x80) 124 dev->ir = 0; 125 else 126 dev->ir = ((unsigned short)buf[1] << 2) | (buf[0] & 0x03); 127 128 dev->als = ((unsigned short)buf[3] << 8) | buf[2]; 129 130 if(buf[4] & 0x40) 131 dev->ps = 0; 132 else 133 dev->ps = ((unsigned short)(buf[5] & 0x3f) << 4) | (buf[4] & 0x0f); 134 } 135 136 137 138 static int ap3216c_read(struct file *filp, char __user *buf, size_t size, loff_t *f_ops) 139 { 140 short data[3]; 141 long err = 0; 142 143 struct ap3216c_device *dev = (struct ap3216c_device *)filp->private_data; 144 ap3216c_readdata(dev); 145 146 data[0] = dev->ir; 147 data[1] = dev->als; 148 data[2] = dev->ps; 149 150 err = copy_to_user(buf, data, sizeof(data)); 151 152 return err; 153 } 154 155 156 static int ap3216c_open(struct inode *inode, struct file *filp) 157 { 158 //将ap3216c_dev赋值给文件指针私有数据,以便后续的读写操作 159 filp->private_data = ap3216c_dev; 160 161 //对ap3216c初始化 162 ap3216c_write_reg(ap3216c_dev, AP3216C_SYSTEMCONG, 0x04); //复位 163 mdelay(50); //复位至少10ms,参考ap3216c复位要求 164 ap3216c_write_reg(ap3216c_dev, AP3216C_SYSTEMCONG, 0x03); //开启ALS、PS、IR功能 165 return 0; 166 } 167 168 static int ap3216c_release(struct inode *inode, struct file *filp) 169 { 170 return 0; 171 } 172 173 174 static const struct file_operations ap3216c_fops = { 175 .owner = THIS_MODULE, 176 .open = ap3216c_open, 177 .release = ap3216c_release, 178 .read = ap3216c_read, 179 }; 180 181 182 static int ap3216c_probe(struct i2c_client *client, const struct i2c_device_id *id) 183 { 184 int ret = -1; 185 186 //为自定义机构提申请内核内存 187 ap3216c_dev = kmalloc(sizeof(struct ap3216c_device), GFP_KERNEL); 188 if(ap3216c_dev == NULL) 189 { 190 printk(KERN_ERR "kmalloc fail!\n"); 191 return -ENOMEM; 192 } 193 194 //动态申请设备号 195 ret = alloc_chrdev_region(&ap3216c_dev->devno, 0, 1, "ap3216c"); 196 if(ret < 0) 197 { 198 printk(KERN_ERR "register major failed!\n"); 199 ret = -EINVAL; 200 goto err1; 201 } 202 203 //获取主设备号 204 ap3216c_dev->major = MAJOR(ap3216c_dev->devno); 205 206 ///将设备与文件操作函数关联 207 cdev_init(&ap3216c_dev->ap3216c_cdev, &ap3216c_fops); 208 209 //注册设备 210 cdev_add(&ap3216c_dev->ap3216c_cdev, ap3216c_dev->devno, 1); 211 212 //创建设备类 213 ap3216c_dev->ap3216c_class = class_create(THIS_MODULE, "ap3216c"); 214 if(IS_ERR(ap3216c_dev->ap3216c_class)) 215 { 216 printk(KERN_ERR "failed to create class!\n"); 217 ret = PTR_ERR(ap3216c_dev->ap3216c_class); 218 goto err2; 219 } 220 221 //创建设备文件,此函数最后一个参数ap3216c,其实就是设备名,应用程序open打开的也是这个文件名 222 ap3216c_dev->ap3216c_dev = device_create(ap3216c_dev->ap3216c_class, NULL, ap3216c_dev->devno, NULL, "ap3216c"); 223 if(IS_ERR(ap3216c_dev->ap3216c_dev)) 224 { 225 printk(KERN_ERR "failed to create device!\n"); 226 ret = PTR_ERR(ap3216c_dev->ap3216c_dev); 227 goto err3; 228 } 229 230 //将i2c_client赋值给自定义结构体私有数据,以便后续的读写操作 231 ap3216c_dev->private_data = client; 232 233 printk("insmod ap3216 driver ok!\n"); 234 235 return 0; 236 237 err3: 238 class_destroy(ap3216c_dev->ap3216c_class); 239 240 err2: 241 unregister_chrdev(ap3216c_dev->major, "ap3216c"); 242 243 err1: 244 kfree(ap3216c_dev); 245 246 return ret; 247 } 248 249 static int ap3216c_remove(struct i2c_client *client) 250 { 251 cdev_del(&ap3216c_dev->ap3216c_cdev); 252 unregister_chrdev_region(ap3216c_dev->devno, 1); 253 device_destroy(ap3216c_dev->ap3216c_class, ap3216c_dev->devno); 254 class_destroy(ap3216c_dev->ap3216c_class); 255 256 return 0; 257 } 258 259 //传统驱动匹配方式 260 static const struct i2c_device_id ap3216c_id[] = {}; 261 262 //设备树驱动匹配方式 263 static const struct of_device_id ap3216c_of_match[] = { 264 {.compatible = "i2c-ap3216c"}, 265 }; 266 267 //填充I2C驱动结构其 268 static struct i2c_driver ap3216c_driver = { 269 .probe = ap3216c_probe, 270 .remove = ap3216c_remove, 271 .driver = { 272 .owner = THIS_MODULE, 273 .name = "ap3216c", 274 .of_match_table = ap3216c_of_match, 275 }, 276 .id_table = ap3216c_id, //即使不兼容传统的驱动匹配方式,也要此成员,否则设备树的方式也匹配不上!!!!!! 277 }; 278 279 280 281 static int __init i2c_ap3216c_init(void) 282 { 283 int ret = 0; 284 285 ret = i2c_add_driver(&ap3216c_driver); 286 return ret; 287 } 288 289 290 static void __exit i2c_ap3216c_exit(void) 291 { 292 i2c_del_driver(&ap3216c_driver); 293 } 294 295 296 297 module_init(i2c_ap3216c_init); 298 module_exit(i2c_ap3216c_exit); 299 MODULE_LICENSE("Dual BSD/GPL");
5. 测试代码
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <sys/types.h> 4 #include <sys/stat.h> 5 #include <fcntl.h> 6 7 int main(int argc, char **argv) 8 { 9 int fd, ret = -1; 10 unsigned short databuf[3]; 11 unsigned short ir, als, ps; 12 13 fd = open("/dev/ap3216c", O_RDWR); 14 15 printf("fd = %d\n", fd); 16 if(fd < 0) 17 { 18 perror("open fail!\n"); 19 exit(1); 20 } 21 22 while(1) 23 { 24 ret = read(fd, databuf, sizeof(databuf)); 25 if(0 == ret) 26 { 27 ir = databuf[0]; 28 als = databuf[1]; 29 ps = databuf[2]; 30 31 printf("ir = %d, als = %d, ps = %d\r\n", ir, als, ps); 32 } 33 usleep(200000); /*100ms */ 34 } 35 36 37 close(fd); 38 return 0; 39 }
总结:
1. I2C的驱动的probe函数,与平台总线字符设备驱动大致流程相似
2. I2C的读,需要先虚写,才能读,硬件协议如此
3. 结构体i2c_driver成员id_table,一定要填充,否则设备树驱动无法匹配上!