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 #include"ap3216creg.h" 19 20 /* 这个驱动对应只有一个同类设备的问题,如果有多个同类型的传感器怎么处理,这就需要再学习了 */ 21 /* 一、配置设备相关的信息 */ 22 23 #define AP3216C_CNT 1 /* 定义设备数量 */ 24 #define AP3216C_NAME "ap3216c" /* 设备名称 */ 25 26 struct ap3216c_dev { 27 dev_t devid; /* 设备号,dev_t就是个普通的整数,是主设备号和次设备号计算出来的 */ 28 struct cdev cdev; /* cdev, 表示一个字符设备,需要向系统注册 */ 29 struct class *class; /* 类,表示一类设备,在/sys/class下,还理解的不深刻*/ 30 struct device *device; /* 创建的设备,在/dev/下显示的设备,通过device_create创建 */ 31 struct device_node *nd; /* 设备节点,存储设备树相关的信息,接收申请的返回值?本实验没有用到*/ 32 int major; /* 主设备号*/ 33 void *private_data; /* 私有数据,这个应该主要是自己用的,这里存储的是i2c_client,和filp->private_data要区分开 */ 34 unsigned short ir, als, ps; /* 传感器数据 */ 35 }; 36 37 static struct ap3216c_dev ap3216cdev; /* 全局变量,用于整体设备的控制 */ 38 /** 39 * @自我理解:封装读取寄存器这个函数,用于被后面调用,这里可以读取以reg为首地址的连续的多个值,跟 40 * 后面的ap3216c_read_reg读取单个寄存器的要区分开,这个ap3216没有用到(可能也不支持)读取连续地址对应的数值,这 41 * 个函数可能是为了编码规范而编写的,是一些通用的编程规范。 42 * @param - dev : ap2316c设备 43 * 44 */ 45 46 /* 二、一些自定义的读取写函数,这里的都不确定,因为基本都是自己定义的 */ 47 48 static int ap3216c_read_regs(struct ap3216c_dev *dev, u8 reg, void *val, int len) 49 { 50 int ret; /* 保存返回值 */ 51 struct i2c_msg msg[2]; /* i2c发送的数据,这里需要写入,然后读取,所以需要两个就行了。msg可以理解为数据包 */ 52 struct i2c_client *client = (struct i2c_client*)dev->private_data; /* 从自定义的设备结构体中取出i2c_client这个重要的操作对象 */ 53 /**也就是需要先向芯片发送写命令,然后发送要读取的寄存器的地址(相当于把寄存器地址写入芯片), 54 * 然后再发送读命令,这样芯片返回的数据就是刚才写入的这个寄存器地址的对应的值了? 55 * 这里的好几个读和写的含义要区分开,一个是对于i2c来说的,一个是对于寄存器来说。 56 * */ 57 /* msg[0]为一个写命令数据包:为发送要读取的寄存器的首地址 */ 58 msg[0].addr = client->addr; /* ap3216c地址 */ 59 msg[0].flags = 0; /* 标记为发送数据,就是i2c协议中的那个读写标志位 */ 60 msg[0].buf = ® /* 向芯片发送(写入)要读取的寄存器的地址 */ 61 msg[0].len = 1; /* 要发送的数据长度,也就是寄存器地址长度,以字节为单位 */ 62 63 /* msg[1]为一个读命令数据包:读取msg[0]中发送的寄存器地址的值 */ 64 msg[1].addr = client->addr; /* ap3216c地址 */ 65 msg[1].flags = I2C_M_RD; /* 标记为发送数据,就是i2c协议中的那个读写标志位 */ 66 msg[1].buf = ® /* 向芯片发送(写入)要读取的寄存器的地址 */ 67 msg[1].len = 1; /* 要发送的数据长度,也就是寄存器地址长度,以字节为单位 */ 68 69 ret = i2c_transfer(client->adapter, msg, 2); /* 发送数据,这里client->adapter参数不能省略,因为一个i2c口对应一个适配器,也就是说一个设备如果有多个i2c口,就会有多个适配器,所以需要制指定从那个i2c口发,这里msg是一个数组,所以是一个地址(严格意义上不算地址)*/ 70 if(ret == 2) { 71 ret = 0; // 发送成功 72 } else { 73 printk("i2c rd failed=%d reg=%06x len=%d\n",ret, reg, len); 74 ret = -EREMOTEIO; /* EREMOTEIO = 121 */ 75 } 76 return ret; 77 } 78 79 /** 80 * 向寄存器写入数据,这里也是可以写入连续的数据,跟ap3216c_write_reg区分开,解释见上 81 * 面的ap3216c_read_regs注释 82 */ 83 static s32 ap3216c_write_regs(struct ap3216c_dev *dev, u8 reg, u8 *buf, u8 len) 84 { 85 u8 b[256]; /* 申请256字节栈空间 */ 86 struct i2c_msg msg; /* 要写入数据的数据包 */ 87 struct i2c_client *client = (struct i2c_client *)dev->private_data; 88 89 90 /* 要发送的数据b,在主机发送写命令后,ap3216c要求先发送要写入的寄存器地址,然后再发送要写入的数据 */ 91 b[0] = reg; /* 要写入数据的寄存器首地址 */ 92 memcpy(&b[1], buf, len); /* b[1]为目标,buf为源,len长度,b[0]已经存放了寄存器地址,从b[1]开始存储要写入的数据 */ 93 94 msg.addr = client->addr; /* 地址 */ 95 msg.flags = 0; /* 写数据 */ 96 97 msg.buf = b; /* 要写入的数据缓冲区 */ 98 msg.len = len + 1; /* 要写入的数据的长度,还有个寄存器地址 */ 99 100 return i2c_transfer(client->adapter, &msg, 1); /* 这里msg是一个实体,所以需要取地址 */ 101 } 102 103 /** 104 * 只读取一个字节,所以用了unsigned char 105 */ 106 static unsigned char ap3216c_read_reg(struct ap3216c_dev *dev, u8 reg) 107 { 108 u8 data = 0; 109 ap3216c_read_regs(dev, reg, &data, 1); /* 长度为1,所以只读取一个寄存器 */ 110 return data; 111 112 #if 0 /* 这里也是一个知识点,i2c_smbus_read_byte_data函数是对i2c_transfer跟高级别的封装,可能需要开启某些选项才行 */ 113 struct i2c_client *client = (struct i2c_client *)dev->private_data; 114 return i2c_smbus_read_byte_data(client, reg); 115 #endif 116 } 117 118 /** 119 * 具体关系和解释同上面的读函数 120 */ 121 static void ap3216c_write_reg(struct ap3216c_dev *dev, u8 reg, u8 data) 122 { 123 u8 buf = 0; 124 buf = data; 125 ap3216c_write_regs(dev, reg, &buf, 1); 126 } 127 128 /** 129 * 利用上面的函数,对上面函数进行封装,读取原始数据,如果同时打开ALS,IR+PS的话两次数据读取的时间间隔要大于112.5ms 130 * 发现这个ap3216c_dev结构体贯穿时钟啊,看来很重要呢 131 */ 132 void ap3216c_readdata(struct ap3216c_dev *dev) 133 { 134 unsigned char i =0; /* 可能尽量节省空间吧 */ 135 unsigned char buf [6]; /* 节省空间 */ 136 137 for(i = 0; i < 6; i++){ 138 buf[i] = ap3216c_read_reg(dev, AP3216C_IRDATALOW + 1); 139 } 140 141 if(buf[0] & 0X80){ /* 看数据手册可知其第八位为数据是否有效标志位 */ 142 dev->ir = 0; /* 无效的话,设置为0 */ 143 } else { 144 /* buf[0]低两位 + buf[1] */ 145 dev->ir = ((unsigned short)buf[1] << 2) | (buf[0] & 0X03); /* unsigned short 等价于 unsigned short int 占用2字节,是int的一半?也是为了节省空间*/ 146 } 147 148 dev->als = ((unsigned short)buf[3] << 8) | buf[2]; /* 数据计算同理 */ 149 if(buf[4] & 0x40){ /* 标志位,为1则数据无效 */ 150 dev->ps = 0; 151 } else { 152 dev->ps = ((unsigned short)(buf[5] & 0x3F) << 4) | (buf[4] & 0x0F); 153 } 154 } 155 156 157 /* 三、用户层面文件操作相关的接口*/ 158 159 /** 160 * 被用户打开时调用的函数,跟prob函数分开,那个是与设备树匹配成功后执行的函数 161 * @param-inode: 内核用来保存文件元信息,比如文件类型、权限、大小、时间戳、设备号等,以供驱动可以判断一些东西 162 * 只有打开的时候会传递inode,然后内核会自动将file结构体中的f_inode赋值为inode。 163 */ 164 static int ap3616_open(struct inode *inode, struct file *filp) 165 { 166 filp->private_data = &ap3216cdev; /* 注意这里的private_data跟自定义的private_data不一样,这个是linux编程指定的,不是自定义 */ 167 /* 初始化ap3216c, 正常来说这些应该都要加判断是否成功的吧 */ 168 ap3216c_write_reg(&ap3216cdev, AP3216C_SYSTEMCONG, 0x04); /* 0x40是软复位 */ 169 mdelay(50); /* 延迟50ms,因为复位需要实践 */ 170 ap3216c_write_reg(&ap3216cdev, AP3216C_SYSTEMCONG, 0x03); /* 使能ALS+PS+IR */ 171 return 0; 172 } 173 /* 174 * @description : 从设备读取数据 175 * 注意:这里不需要传递inode了,因为在filp里面已经有了 176 * @param - filp : 要打开的设备文件(文件描述符) 177 * @param - buf : 返回给用户空间的数据缓冲区 178 * @param - cnt : 要读取的数据长度 179 * @param - offt : 相对于文件首地址的偏移 180 * @return : 读取的字节数,如果为负值,表示读取失败 181 */ 182 static ssize_t ap3216c_read(struct file *filp, char __user *buf, size_t cnt, loff_t *off) 183 { 184 short data[3]; /* 保存数据 */ 185 long err = 0; /* 返回值,可以用ret吧? */ 186 187 struct ap3216c_dev *dev = (struct ap3216c_dev*)filp->private_data; 188 189 ap3216c_readdata(dev); 190 191 data[0] = dev->ir; 192 data[1] = dev->als; 193 data[2] = dev->ps; 194 err = copy_to_user(buf, data, sizeof(data)); 195 return 0; 196 } 197 /** 198 * 用不到write,但是加上吧,了解下函数结构 199 * @param – filp : 设备文件,表示打开的文件描述符 200 * 注意:这里不需要传递inode了,因为在filp里面已经有了 201 * @param - buf : 要写给设备写入的数据 202 * @param - cnt : 要写入的数据长度 203 * @param – offt : 相对于文件首地址的偏移 204 */ 205 static ssize_t ap3216c_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt) 206 { 207 return 0; 208 } 209 /** 210 * 用户层面文件关闭/释放设备时候调用的函数,跟后面驱动相关的exit/remove函数区分开 211 */ 212 static int ap3216_release(struct inode *inode, struct file *filp) 213 { 214 return 0; 215 } 216 217 /** 218 * 构建AP3216用户文件操作层面的结构体函数,注意,这里是文件操作 219 */ 220 static const struct file_operations ap3216c_ops = { 221 .owner = THIS_MODULE, 222 .open = ap3616_open, 223 .read = ap3216c_read, 224 .write = ap3216c_write, 225 .release = ap3216_release, 226 }; 227 228 /* 到此未知,有关用户层面对文件的打开关闭操作已经有了 */ 229 230 /* 四、下面是驱动层面干的事情了,就是对设备相关的操作了,更加底层 */ 231 232 /** 233 * prob函数,最重要的函数,用于设配匹配成功后执行 234 * 要记住不同总线的prob函数应该是不一样的 235 * 这里的这两个参数应该是从设备树或者传统匹配方式得到的数据,设备匹配成功后会把这两个参数传递进来 236 */ 237 static int ap3216c_probe(struct i2c_client *client, const struct i2c_device_id *id) 238 { 239 /* 1. 构建设备号,AP3216C_CNT>1的情况我还没懂,但大体意思就是一个驱动管理多个同类型的传感器,可以公用一套驱动代码,等待研究*/ 240 if(ap3216cdev.major){ /* 存在设备号,也就是有自己想设置的设号 */ 241 ap3216cdev.devid = MKDEV(ap3216cdev.major, 0); /* 设置主设别号和次设备号,次设备号一般设置为0 */ 242 register_chrdev_region(ap3216cdev.devid, AP3216C_CNT, AP3216C_NAME); 243 } else { 244 alloc_chrdev_region(&ap3216cdev.devid, 0, AP3216C_CNT, AP3216C_NAME); /* 得到设备号,可以解析出主设备号和次设备号,参数0代表的是启示开始分配的设备号,一般从0开始,也就是说如果从0开始,就是从0开始一个个查找,查找到可用的就用上,也对应上一个驱动适配多个相同设备了 */ 245 ap3216cdev.major = MAJOR(ap3216cdev.devid); /* 解析出主设备号 */ 246 } 247 248 /* 2. 注册字符设备,向内核中注册一个字符设备,但是用户层面还是看不到相应的设备 */ 249 cdev_init(&ap3216cdev.cdev, &ap3216c_ops); /* 第一个参数是要初始化的设别结构体,第二个是文件操作函数 */ 250 cdev_add(&ap3216cdev.cdev, ap3216cdev.devid, AP3216C_CNT); /* 第三个参数是要创建的设备数量 */ 251 252 /* 3. 创建类,就是在/sys/class/创建同一类设备的文件夹 */ 253 ap3216cdev.class = class_create(THIS_MODULE, AP3216C_NAME); 254 if( IS_ERR(ap3216cdev.class)){ /* 如果创建类是黑白失败 */ 255 return PTR_ERR(ap3216cdev.class); 256 } 257 258 /* 4. 创建设备,使其暴露在/dev/下面,让用户使用 */ 259 /** 260 * device_create是个可变参数函数,参数 class就是设备要创建哪个类下面;参数 parent是父 261 * 设备,一般为 NULL,也就是没有父设备;参数 devt是设备号;参数 drvdata是设备可能会使用 262 * 的一些数据,一般为 NULL;参数 fmt是设备名字,如果设置 fmt=xxx的话,就会生成 /dev/xxx这个设备文件。返回值就是创建好的设备。 263 */ 264 ap3216cdev.device = device_create(ap3216cdev.class, NULL, ap3216cdev.devid, NULL, AP3216C_NAME); 265 if(IS_ERR(ap3216cdev.device)){ 266 return PTR_ERR(ap3216cdev.device); 267 } 268 ap3216cdev.private_data = client; 269 270 return 0; 271 } 272 /** 273 * 设备移除时对设备进行的操作,可能类似设备拔出之后的操作 274 */ 275 static int ap3216c_remove(struct i2c_client *client) 276 { 277 /*1. 删除字符设备相关 */ 278 cdev_del(&ap3216cdev.cdev); 279 /* 这个注销函数,应该是从devid开始的设备号,忘后AP3216C_CNT注销AP3216C_CNT个设备,因为前面注册的时候,是对应AP3216C_CNT个设备的 */ 280 /* 这些函数的详细用法还需要单独学习,可以看官方手册? */ 281 unregister_chrdev_region(ap3216cdev.devid, AP3216C_CNT); /* 这里AP3216C_CNT是1,也就是说值对应一个设备的情况,多个设备的情况还需要再学习 */ 282 283 /* 2. 注销掉类和设备 */ 284 device_destroy(ap3216cdev.class, ap3216cdev.devid); 285 class_destroy(ap3216cdev.class); 286 return 0; 287 } 288 289 /* 除了设备树之外的匹配方式都叫传统方式 */ 290 /* 传统匹配方式 */ 291 static const struct i2c_device_id ap3216c_id[] = { 292 {"alientek,ap3216c", 0}, /* 注意这里的0不是什么ID,而是一个私有数据,可能是用于区分不同的设备? */ 293 {} 294 }; 295 296 /* 设备树匹配方式 */ 297 static const struct of_device_id ap3216c_of_match[] = { 298 { .compatible = "alientek,ap3216c"}, 299 { } 300 }; 301 302 /* 写完相关函数之后,下面配置驱动相关的操作的结构体 */ 303 /** 304 * 感觉这个才是真正封装好的驱动,其中有驱动的各种操作,所以init中是将这个add进去的 305 */ 306 static struct i2c_driver ap3216c_driver = { 307 .probe = ap3216c_probe, 308 .remove = ap3216c_remove, 309 .driver = { 310 .owner = THIS_MODULE, 311 .name = "ap3216c", 312 .of_match_table = ap3216c_of_match, 313 }, 314 .id_table = ap3216c_id, 315 }; 316 317 /* 驱动入口函数,这个是最先执行的函数 */ 318 319 static int __init ap3216c_init(void) 320 { 321 int ret = 0; 322 ret = i2c_add_driver(&ap3216c_driver); 323 return ret; 324 } 325 /** 326 * 驱动出口函数,没什么好说的,卸载模块的时候用 327 */ 328 static void __exit ap3216c_exit(void) 329 { 330 i2c_del_driver(&ap3216c_driver); 331 } 332 333 /* module_i2c_driver(ap3216c_driver) */ /* 这个是直接把module_init和module_exit简化成要给函数了 */ 334 module_init(ap3216c_init); 335 module_exit(ap3216c_exit); 336 MODULE_LICENSE("GPL"); 337 MODULE_AUTHOR("ydteng");