struct dht11_sensor_dev{ struct cdev cdev; unsigned long pin; unsigned char value[5]; unsigned char lock; unsigned int irq; struct timeval lasttv; int bitcount; int bytecount; int started; struct timer_list timer; void (*write_bit)(unsigned long pin, char bit); void (*delay)(short t); }; static struct dht11_sensor_dev *dht11_dev; struct class *dht11_class; static int major = DEVICE_MAJOR; static inline void usdelay(short t) { return ndelay(t * 1000); } static inline void gpio_write_bit(unsigned long pin, char bit) { gpio_set_value(pin, bit); } #if 0 static char gpio_read_bit(unsigned long pin) { return gpio_get_value(pin) ? 1 : 0; //return gpio_get_value(pin); } static void gpio_write_bit_dir(unsigned long pin, char bit) { if (bit) gpio_direction_input(pin); else gpio_direction_output(pin, 0); } static void gpio_write_bit_val(unsigned long pin, char bit) { gpio_set_value(pin, bit); } static int dht11_get_temperature(struct dht11_sensor_dev *dev) { return dev->value[0] * 1000 + dev->value[1]; } static int dht11_get_humidity(struct dht11_sensor_dev *dev) { return dev->value[2] * 1000 + dev->value[3]; } #endif static irqreturn_t dht11_interrupt(int irq, void *dev_id) { struct dht11_sensor_dev *dev = (struct dht11_sensor_dev *)dev_id; struct timeval tv; long deltv = 0; int signal = 0, data = 0; signal = gpio_get_value(dev->pin); do_gettimeofday(&tv); deltv = tv.tv_sec - dev->lasttv.tv_sec; data = (int)(deltv * 1000000 + (tv.tv_usec - dev->lasttv.tv_usec)); dev->lasttv = tv; if(dev->bytecount == 5) return IRQ_HANDLED; if((signal == 1) & (data > 40)){ dev->started = 1; return IRQ_HANDLED; } if((signal == 0) & (dev->started == 1)){ if(data > 80) return IRQ_HANDLED; if(data < 30){ dev->bitcount++; if(dev->bitcount == 8){ dev->bitcount = 0; dev->bytecount++; } return IRQ_HANDLED; } if (data > 60)//55 dev->value[dev->bytecount] = dev->value[dev->bytecount] | (0x80 >> dev->bitcount); dev->bitcount++; if(dev->bitcount == 8){ dev->bitcount = 0; dev->bytecount++; } } return IRQ_HANDLED; } static int setup_interrupts(void) { int result; dht11_dev->irq = gpio_to_irq(dht11_dev->pin); result = request_irq(dht11_dev->irq, dht11_interrupt, IRQ_TYPE_EDGE_BOTH, DEVICE_NAME, (void *)dht11_dev); switch (result) { case -EBUSY: printk(KERN_ERR "*%s(): IRQ %d is busy\n", __func__, dht11_dev->irq); return -EBUSY; case -EINVAL: printk(KERN_ERR "*%s(): Bad irq number or handler\n", __func__); return -EINVAL; default: //printk(KERN_INFO "*%s():Interrupt %04x obtained\n", __func__, dht11_dev->irq); break; } return 0; } static void clear_interrupts(void) { free_irq(dht11_dev->irq, (void *)dht11_dev); if(gpio_request(dht11_dev->pin, DEVICE_NAME)){ printk(KERN_INFO "[%s] gpio_request \n", __func__); return; } s3c_gpio_cfgpin(dht11_dev->pin, S3C_GPIO_OUTPUT); s3c_gpio_setpull(dht11_dev->pin, S3C_GPIO_PULL_NONE); //s5p_gpio_set_drvstr(dht11_dev->pin, S5P_GPIO_DRVSTR_LV2); gpio_set_value(dht11_dev->pin, 1); gpio_free(dht11_dev->pin); } static int dht11_reset(struct dht11_sensor_dev *dev) { if(gpio_request(dht11_dev->pin, DEVICE_NAME)){ printk(KERN_INFO "[%s] gpio_request \n", __func__); return -1; } //s3c_gpio_cfgpin(dht11_dev->pin, S3C_GPIO_OUTPUT); s3c_gpio_cfgpin(dht11_dev->pin, S3C_GPIO_OUTPUT); s3c_gpio_setpull(dht11_dev->pin, S3C_GPIO_PULL_NONE); //s5p_gpio_set_drvstr(dht11_dev->pin, S5P_GPIO_DRVSTR_LV2); gpio_set_value(dht11_dev->pin, 1); dev->write_bit(dev->pin, 0); msleep(18); dev->write_bit(dev->pin, 1); s3c_gpio_cfgpin(dev->pin, S3C_GPIO_INPUT); dev->delay(20); gpio_free(dht11_dev->pin); return 0; } static int dht11_checksum(struct dht11_sensor_dev *dev) { char tmp = 0; tmp = dev->value[0] + dev->value[1] + dev->value[2] + dev->value[3]; if(tmp != dev->value[4]){ //printk(KERN_INFO "[%s] %d %d\n", __func__, dev->value[4], tmp); return 0; } return 1; } static int dht11_sensor_open(struct inode *inode, struct file *filp) { if(dht11_dev->lock) return -EBUSY; try_module_get(THIS_MODULE); dht11_dev->lock = 1; return 0; } static int dht11_sensor_release(struct inode *inode,struct file *filp) { module_put(THIS_MODULE); dht11_dev->lock = 0; return 0; } static ssize_t dht11_sensor_read( struct file *filp,char __user *buf,size_t size,loff_t *f_pos) { unsigned long length =(size>48)? 48:size; int result = 0; char msg[48]; dht11_dev->started = 0; dht11_dev->bitcount = 0; dht11_dev->bytecount = 0; dht11_dev->value[0] = 0; dht11_dev->value[1] = 0; dht11_dev->value[2] = 0; dht11_dev->value[3] = 0; dht11_dev->value[4] = 0; if(!dht11_reset(dht11_dev)){ do_gettimeofday(&dht11_dev->lasttv); setup_interrupts(); } else{ dprint("*%s(): reset fail \n", __func__); return -1; } msleep(20); clear_interrupts(); //for(i = 0; i < 48; i++) dht11_dev->msg[i] = -1; sprintf(msg, "Humidity=%d.%d%%\nTemperature=%d.%dC\nResult=%d\n%c",\ dht11_dev->value[0], dht11_dev->value[1], \ dht11_dev->value[2], dht11_dev->value[3], \ dht11_checksum(dht11_dev), '\0'); length = strlen(msg); dprint(KERN_INFO "*%s(): length %lu\n", __func__, length); #if 0 while (length && *ptr) { put_user(*(ptr++), buf++); length--; result++; } put_user('\0', buf++); result++; *f_pos += result; return result; #else result=copy_to_user(buf, msg + *f_pos, length); //*f_pos += length; *f_pos = 0; //if(result) return length - result; //else result = length; return length; #endif } #if 0 static ssize_t dht11_sensor_write(struct file *filp,const char __user *buf,size_t size,loff_t *f_pos) { return 0; } #endif static struct file_operations dht11_sensor_fops={ .owner = THIS_MODULE, .read = dht11_sensor_read, .open = dht11_sensor_open, .release = dht11_sensor_release, }; static void dht11_sensor_setup_cdev( struct dht11_sensor_dev *dev,int minor,struct file_operations *fops) { int err, devno = MKDEV(major, minor); cdev_init(&(dev->cdev), fops); dev->cdev.owner = THIS_MODULE; dev->cdev.ops = fops; err=cdev_add(&(dev->cdev), devno, 1); if(err){ printk(KERN_NOTICE"erro %d adding %s %d\n",err,DEVICE_NAME,minor); } } int __init dht11_sensor_init(void){ int result; dev_t devno=MKDEV(major,0); if(major){ result = register_chrdev_region(devno, 1, DEVICE_NAME); } else{ result = alloc_chrdev_region(&devno, 0, 1, DEVICE_NAME); } if(result < 0){ printk(KERN_WARNING"%s: unable to get major %d\n",DEVICE_NAME,major); return result; } dht11_dev=kmalloc(sizeof(struct dht11_sensor_dev),GFP_KERNEL); if(!dht11_dev){ result=-ENOMEM; goto fail_malloc; } dht11_class = class_create(THIS_MODULE, DEVICE_NAME); device_create(dht11_class, NULL, MKDEV(DEVICE_MAJOR, 0), NULL, DEVICE_NAME); dht11_sensor_setup_cdev(dht11_dev, 0, &dht11_sensor_fops); dht11_dev->pin = GPIO_DOUT; dht11_dev->delay = usdelay; dht11_dev->write_bit = gpio_write_bit; result = gpio_request(dht11_dev->pin, DEVICE_NAME); if(result){ printk(KERN_INFO "[%s] gpio_request \n", __func__); goto fail_gpio; } //s3c_gpio_cfgpin(dht11_dev->pin, S3C_GPIO_OUTPUT); s3c_gpio_cfgpin(dht11_dev->pin, S3C_GPIO_OUTPUT); s3c_gpio_setpull(dht11_dev->pin, S3C_GPIO_PULL_NONE); //s5p_gpio_set_drvstr(dht11_dev->pin, S5P_GPIO_DRVSTR_LV2); gpio_set_value(dht11_dev->pin, 1); //gpio_direction_output(dht11_dev->pin, 1); gpio_free(dht11_dev->pin); return 0; fail_gpio: kfree(dht11_dev); fail_malloc: unregister_chrdev_region(MKDEV(major,0),1); return result; } void __exit dht11_sensor_exit(void) { cdev_del(&dht11_dev->cdev); device_destroy(dht11_class, MKDEV(major, 0)); class_destroy(dht11_class); //gpio_free(dht11_dev->pin); kfree(dht11_dev); unregister_chrdev_region(MKDEV(major, 0), 1); //printk("%s device uninstalled OK!\n", DEVICE_NAME); } module_init(dht11_sensor_init); module_exit(dht11_sensor_exit); MODULE_AUTHOR("jvaemape"); MODULE_DESCRIPTION("S3C GPIO MQ2 Drive"); MODULE_LICENSE("Dual BSD/GPL");