IIC设备驱动,控制si7006测量温湿度
驱动文件:
#include <linux/init.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/gpio.h>
#include <linux/of_irq.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/i2c.h>
#include <linux/module.h>
#define GET_HUM _IOR('s',0,int)
#define GET_TEMP _IOR('s',1,int)
int si7006_open(struct inode *, struct file *);
long si7006_ioctl(struct file *, unsigned int, unsigned long);
int si7006_close(struct inode *, struct file *);
int si7006_up(void);
void si7006_del(void);
struct dev_struct {
int major;
int minor;
int count;
dev_t devno;
char *name;
struct class *cls;
struct device *dev;
struct cdev *cdev;
struct file_operations fops;
};
struct dev_struct si7006 = {
.major=0,
.minor=0,
.count=1,
.name="si7006",
.fops={
.open=si7006_open,
.unlocked_ioctl=si7006_ioctl,
.release=si7006_close,
},
};
struct i2c_client *cli;
unsigned int i2c_read_hum(unsigned char reg)
{
int ret;
unsigned short hum;
struct i2c_msg r_msg[] = {
[0] = {
.addr = cli->addr,
.flags = 0,
.len = 1,
.buf=(char *)®,
},
[1]={
.addr=cli->addr,
.flags=1,
.len=2,
.buf=(char *)&hum,
},
};
ret = i2c_transfer(cli->adapter, r_msg, ARRAY_SIZE(r_msg));
if(ret!=ARRAY_SIZE(r_msg)){
printk("消息发送失败\n");
return -EINVAL;
}
hum = hum >> 8 | hum << 8;
return hum;
}
int i2c_read_temp(unsigned char reg)
{
int ret;
short temp;
struct i2c_msg r_msg[] = {
[0] = {
.addr = cli->addr,
.flags = 0,
.len = 1,
.buf=(char *)®,
},
[1]={
.addr=cli->addr,
.flags=1,
.len=2,
.buf=(char *)&temp,
},
};
ret = i2c_transfer(cli->adapter, r_msg, ARRAY_SIZE(r_msg));
if(ret!=ARRAY_SIZE(r_msg)){
printk("消息发送失败\n");
return -EINVAL;
}
temp = temp >> 8 | temp << 8;
return temp;
}
int si7006_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
int ret;
cli = client;
ret = si7006_up(); // 创建字符设备驱动
if(ret<0){
goto out1;
}
return 0;
out1:
return ret;
}
int si7006_remove(struct i2c_client *client)
{
si7006_del();
return 0;
}
struct of_device_id of_match[] = {
{.compatible="hqyj,si7006"},
{}
};
struct i2c_driver i2c_si7006 = {
.probe=si7006_probe,
.remove=si7006_remove,
.driver={
.name="si7006",
.of_match_table=of_match,
},
};
module_i2c_driver(i2c_si7006); //一键注册宏
MODULE_DEVICE_TABLE(of,of_match); //热插拔
MODULE_LICENSE("GPL");
/**********************************************************************/
int si7006_up(void)
{
int ret;
int i;
// 申请字符设备
si7006.cdev=cdev_alloc();
if(si7006.cdev==NULL){
printk("字符设备申请失败\n");
ret = -ENOMEM;
goto ERR1;
}
//初始化字符设备
cdev_init(si7006.cdev,&si7006.fops);
// 申请设备号
if(si7006.major==0){
ret = alloc_chrdev_region(&si7006.devno,si7006.minor,si7006.count,si7006.name);
if(ret<0){
printk("申请设备号失败\n");
goto ERR2;
}
si7006.major = MAJOR(si7006.devno);
}else if(si7006.major>0){
ret = register_chrdev_region(MKDEV(si7006.major,si7006.minor),si7006.count,si7006.name);
if(ret<0){
printk("申请设备号失败\n");
goto ERR2;
}
si7006.devno = MKDEV(si7006.major, si7006.minor);
}
//向内核注册字符设备
ret=cdev_add(si7006.cdev,si7006.devno,si7006.count);
if(ret<0){
printk("注册字符设备失败\n");
goto ERR3;
}
//自动创建设备节点
//向上层提交目录
si7006.cls=class_create(THIS_MODULE,si7006.name);
if(IS_ERR(si7006.cls)){
printk("向上层提交目录失败\n");
ret = PTR_ERR(si7006.cls);
goto ERR4;
}
//向上层提交节点信息
if(si7006.count>1){
for (i = si7006.minor; i < si7006.minor+si7006.count;i++){
si7006.dev = device_create(si7006.cls, NULL, MKDEV(si7006.major,i), NULL,\
"%s%d", si7006.name, i);
if(IS_ERR(si7006.dev)){
printk("向上层提交节点信息失败\n");
ret = PTR_ERR(si7006.dev);
goto ERR6;
}
}
}else{
si7006.dev = device_create(si7006.cls, NULL, si7006.devno, NULL,"%s", si7006.name);
if(IS_ERR(si7006.dev)){
printk("向上层提交节点信息失败\n");
ret = PTR_ERR(si7006.dev);
goto ERR5;
}
}
return 0;
ERR6:
for (i--; i >= si7006.major;i--){
device_destroy(si7006.cls,MKDEV(si7006.major,i));
}
ERR5:
class_destroy(si7006.cls);
ERR4:
cdev_del(si7006.cdev);
ERR3:
unregister_chrdev_region(si7006.devno,si7006.count);
ERR2:
kfree(si7006.cdev);
ERR1:
return ret;
}
void si7006_del(void)
{
int i;
if(si7006.count>1){
for (i = si7006.minor; i < si7006.minor+si7006.count;i++){
device_destroy(si7006.cls,MKDEV(si7006.major,i));
}
}else{
device_destroy(si7006.cls,si7006.devno);
}
class_destroy(si7006.cls);
cdev_del(si7006.cdev);
unregister_chrdev_region(si7006.devno,si7006.count);
kfree(si7006.cdev);
}
int si7006_open(struct inode *inode, struct file *file)
{
return 0;
}
long si7006_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
int ret;
unsigned int hum;
int temp;
switch (cmd){
case GET_HUM:
hum = i2c_read_hum(0xE5);
ret = copy_to_user((void *)arg,&hum,_IOC_SIZE(cmd));
if(ret){
printk("数据拷贝失败\n");
return -EINVAL;
}
break;
case GET_TEMP:
temp = i2c_read_temp(0xE3);
ret = copy_to_user((void *)arg,&temp,_IOC_SIZE(cmd));
if(ret){
printk("数据拷贝失败\n");
return -EINVAL;
}
break;
}
return 0;
}
int si7006_close(struct inode *inode, struct file *file)
{
return 0;
}
测试文件:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/epoll.h>
#include <fcntl.h>
#include <signal.h>
#include <string.h>
#define GET_HUM _IOR('s',0,int)
#define GET_TEMP _IOR('s',1,int)
#define PRINT_ERR(msg) \
do \
{ \
perror(msg); \
return -1; \
} while (0)
int main(int argc,const char * argv[])
{
int temp_data;
unsigned int hum_data;
float temp, hum;
int fd = open("/dev/si7006", O_RDWR);
if(fd<0){
PRINT_ERR("open error");
exit(-1);
}
while(1){
ioctl(fd,GET_HUM,&hum_data);
ioctl(fd, GET_TEMP, &temp_data);
hum = 125.0 * hum_data / 65536 - 6;
temp = 175.72 * temp_data / 65536 - 46.85;
printf("hum=%.2f,temp=%.2f\n", hum, temp);
sleep(1);
}
return 0;
}