linux红外对管(距离传感器)驱动 分析

来源:互联网 发布:ubuntu lts 安装 编辑:程序博客网 时间:2024/04/29 22:35

 

头文件:

/** filename: rohm_scm_proximity.h*/#ifndef __ROHM_SCM_PROXIMTIY__#define __ROHM_SCM_PROXIMTIY__#define ROHM_PROXIMITY "rohm_proximity"#define PROXIMITY_DEVICE "rohm_proximity"#include <linux/types.h>#include <linux/ioctl.h>#define ROHM_PROXIMITY_IOCTL_MAGIC 'r'#define ROHM_PROXIMITY_IOCTL_GET_ENABLED  _IOR(ROHM_PROXIMITY_IOCTL_MAGIC, 1, int *)#define ROHM_PROXIMITY_IOCTL_ENABLE   _IOW(ROHM_PROXIMITY_IOCTL_MAGIC, 2, int *)#endif


 

/*
 * File:         rohm_scm_proximity.c
 * Based on:
 * Author:       Yunlong Wang <Yunlong.Wang@spreadtrum.com>
 *
 * Created:   2011-04-08
 * Description:  Rohm Proximity Sensor Driver
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 */

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/gpio.h>
#include <linux/input.h>
#include <linux/platform_device.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <linux/wakelock.h>
#include <mach/mfp.h>
#include <mach/adc_drvapi.h>

#include "rohm_scm_proximity.h"

#define PS_DEBUG 1
#define RHOM_PROXIMITY_DATA_POLL_TIMER      100
#define RHOM_PROXIMITY_THRESHOLD            500
#define RHOM_PROXIMITY_ADC_CHANNEL          1

//static int debug_level=0;
static int debug_level=1;

#if PS_DEBUG
#define PS_DBG(format, ...) \
    if(debug_level == 1) \
        printk(KERN_INFO "PROXIMITY: " format , ## __VA_ARGS__)
#else
#define PS_DBG(format, ...)
#endif

extern int sprd_3rdparty_gpio_proximity_trans;
extern int sprd_3rdparty_gpio_proximity_recv;


struct rohm_proximity_platform_data {
        int gpio_trans; /*control transmit light*/
        int gpio_recv;  /*control recevie light*/
};

static struct rohm_proximity_platform_data proximity_platform_data;
static struct wake_lock prox_delayed_work_wake_lock;


static struct rohm_proximity_data {
    struct input_dev *input_dev;
    struct rohm_proximity_platform_data *pdata;
    struct delayed_work work;
    struct workqueue_struct *work_queue;
    int enabled;
} proximity_data;

static int misc_opened;
static int proximity_start=0;


static struct platform_device rohm_proximity_device = {
        .name           = ROHM_PROXIMITY,
        .id             = -1,
        .dev    = {
        .platform_data  = &proximity_platform_data,
    },
};

 

static int rohm_proximity_enable(struct rohm_proximity_data *data);
static int rohm_proximity_disable(struct rohm_proximity_data *data);

static ssize_t rohm_proximity_show_control(struct device* cd, struct device_attribute *attr, char* buf)
{
    ssize_t ret = 0;

    if(proximity_start==1)
        sprintf(buf, "Proximity Start\n");
    else
        sprintf(buf, "Proximity Stop\n");
   
    ret = strlen(buf) + 1;
    return ret;
}

static ssize_t rohm_proximity_store_control(struct device* cd, struct device_attribute *attr, const char* buf, size_t len)
{
    unsigned long on_off = simple_strtoul(buf, NULL, 10);
    proximity_start = on_off;
   
    if(on_off==1)
    {
        printk("Proximity Start\n");
        rohm_proximity_enable(&proximity_data);
    }
    else
    {
        printk("Proximity Stop\n");
        rohm_proximity_disable(&proximity_data);
    }
   
    return len;
}

static ssize_t rohm_proximity_show_debug(struct device* cd,struct device_attribute *attr, char* buf)
{
    ssize_t ret = 0;
   
    sprintf(buf, "ROHM Debug %d\n",debug_level);
   
    ret = strlen(buf) + 1;

    return ret;
}

static ssize_t rohm_proximity_store_debug(struct device* cd, struct device_attribute *attr,
               const char* buf, size_t len)
{
    unsigned long on_off = simple_strtoul(buf, NULL, 10);
    debug_level = on_off;

    printk("%s: debug_level=%d\n",__func__, debug_level);
   
    return len;
}


static DEVICE_ATTR(control, S_IRUGO | S_IWUSR, rohm_proximity_show_control, rohm_proximity_store_control);
static DEVICE_ATTR(debug, S_IRUGO | S_IWUSR, rohm_proximity_show_debug, rohm_proximity_store_debug);


static int rohm_proximity_create_sysfs(struct platform_device *client)
{
    struct device *dev = &(client->dev);
    int err = 0;

    PS_DBG("%s\n", __func__);
   
    if ((err = device_create_file(dev, &dev_attr_control)))
        goto err_out;

    if ((err = device_create_file(dev, &dev_attr_debug)))
        goto err_out;

    return 0;
   
err_out:
    return err;
}

 

static int rohm_proximity_get_adc(void)
{
    int val;
   
    val = ADC_GetValue(RHOM_PROXIMITY_ADC_CHANNEL, ADC_SCALE_3V);  
    return val;
}

static int rohm_proximity_report(struct rohm_proximity_data *data)
{
    int data1, data2;
    int val;
   
    //get environment adc
    gpio_set_value(data->pdata->gpio_recv, 1);
    msleep(30); //for stable
    data1 = rohm_proximity_get_adc();

    //get infrared adc
    gpio_set_value(data->pdata->gpio_trans, 1);
    msleep(30);
    data2 = rohm_proximity_get_adc();

    val = (data2-data1)>RHOM_PROXIMITY_THRESHOLD? 0:1;

    PS_DBG("data1=%d; data2=%d; proximity=%d\n", data1, data2, val);
 
    /* 0 is close, 1 is far */
    input_report_abs(data->input_dev, ABS_DISTANCE, val);
    input_sync(data->input_dev);

    gpio_set_value(data->pdata->gpio_recv, 0);
    gpio_set_value(data->pdata->gpio_trans, 0);
   
    return val;
}

static void rohm_proximity_start_work(int ms, struct rohm_proximity_data *data)
{
    queue_delayed_work(data->work_queue, &data->work,msecs_to_jiffies(ms));
}

static void rohm_proximity_stop_work(struct rohm_proximity_data *data)
{
    PS_DBG("%s\n", __func__);
    cancel_rearming_delayed_workqueue(data->work_queue,&data->work);
    flush_workqueue(data->work_queue);
}

static void rohm_proximity_work(struct work_struct *work)
{
    struct rohm_proximity_data *data = container_of(work, struct rohm_proximity_data, work.work);
    rohm_proximity_report(data);
    rohm_proximity_start_work(RHOM_PROXIMITY_DATA_POLL_TIMER, data);
}

static int rohm_proximity_enable(struct rohm_proximity_data *data)
{
    PS_DBG("%s\n", __func__);
    data->enabled = 1;
    wake_lock(&prox_delayed_work_wake_lock);
    rohm_proximity_start_work(RHOM_PROXIMITY_DATA_POLL_TIMER, data);
    return 0;
}

static int rohm_proximity_disable(struct rohm_proximity_data *data)
{
    PS_DBG("%s\n", __func__);
    data->enabled = 0;
    wake_unlock(&prox_delayed_work_wake_lock);
    rohm_proximity_stop_work(data);
    return 0;
}

static int rohm_proximity_setup(struct rohm_proximity_data *ip)
{
    struct rohm_proximity_platform_data *pdata = ip->pdata;

    //config gpio
    pdata->gpio_trans = sprd_3rdparty_gpio_proximity_trans;
    pdata->gpio_recv = sprd_3rdparty_gpio_proximity_recv;

    PS_DBG("%s, gpio_recv=%d; gpio_trans=%d\n", __func__, pdata->gpio_recv, pdata->gpio_trans);

    gpio_direction_output(pdata->gpio_trans, 0);
    gpio_direction_output(pdata->gpio_recv, 0);

    //set control gpio low
    gpio_set_value(pdata->gpio_trans, 0);
    gpio_set_value(pdata->gpio_recv, 0);

    //init adc
    ADC_Init();

    return 0;
}

static int rohm_proximity_open(struct inode *inode, struct file *file)
{
    PS_DBG("%s\n", __func__);
    if (misc_opened)
        return -EBUSY;
    misc_opened = 1;
    return 0;
}

static int rohm_proximity_release(struct inode *inode, struct file *file)
{
    PS_DBG("%s\n", __func__);
    misc_opened = 0;
    return rohm_proximity_disable(&proximity_data);
}

static long rohm_proximity_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
    int val;
    PS_DBG("%s cmd %d\n", __func__, _IOC_NR(cmd));
    switch (cmd) {
    case ROHM_PROXIMITY_IOCTL_ENABLE:
        if (get_user(val, (unsigned long __user *)arg))
            return -EFAULT;
        if (val)
            return rohm_proximity_enable(&proximity_data);
        else
            return rohm_proximity_disable(&proximity_data);
        break;
    case ROHM_PROXIMITY_IOCTL_GET_ENABLED:
        return put_user(proximity_data.enabled, (unsigned long __user *)arg);
        break;
    default:
        pr_err("%s: invalid cmd %d\n", __func__, _IOC_NR(cmd));
        return -EINVAL;
    }
}

static struct file_operations rohm_proximity_fops = {
    .owner = THIS_MODULE,
    .open = rohm_proximity_open,
    .release = rohm_proximity_release,
    .unlocked_ioctl = rohm_proximity_ioctl
};

struct miscdevice rohm_proximity_misc = {
    .minor = MISC_DYNAMIC_MINOR,
    .name = PROXIMITY_DEVICE,
    .fops = &rohm_proximity_fops
};

 

static int rohm_proximity_probe(struct platform_device *pdev)
{
    int rc = -EIO;
    struct input_dev *input_dev;
    struct rohm_proximity_data *ip;
    struct rohm_proximity_platform_data *pdata;

    PS_DBG("%s\n", __func__);

    wake_lock_init(&prox_delayed_work_wake_lock, WAKE_LOCK_SUSPEND, "prox_delayed_work");

    pdata = pdev->dev.platform_data;
   
    if (!pdata) {
        pr_err("%s: missing pdata!\n", __func__);
        goto done;
    }


    ip = &proximity_data;
    platform_set_drvdata(pdev, ip);

    PS_DBG("%s: allocating input device\n", __func__);
    input_dev = input_allocate_device();
    if (!input_dev) {
        pr_err("%s: could not allocate input device\n", __func__);
        rc = -ENOMEM;
        goto done;
    }
    ip->input_dev = input_dev;
    ip->pdata = pdata;
    input_set_drvdata(input_dev, ip);

    input_dev->name = "proximity";
    input_dev->phys = "proximity";
    input_dev->id.bustype = BUS_HOST;
    input_dev->id.vendor = 0x0001;
    input_dev->id.product = 0x0001;
    input_dev->id.version = 0x0100;

    set_bit(EV_ABS, input_dev->evbit);
    input_set_abs_params(input_dev, ABS_DISTANCE, 0, 1, 0, 0);

    PS_DBG("%s: registering input device\n", __func__);
    rc = input_register_device(input_dev);
    if (rc < 0) {
        pr_err("%s: could not register input device\n", __func__);
        goto err_free_input_device;
    }

    PS_DBG("%s: registering misc device\n", __func__);
    rc = misc_register(&rohm_proximity_misc);
    if (rc < 0) {
        pr_err("%s: could not register misc device\n", __func__);
        goto err_unregister_input_device;
    }

    //create work queue
    INIT_DELAYED_WORK(&ip->work, rohm_proximity_work);
    ip->work_queue = create_singlethread_workqueue("proximity");       

    rohm_proximity_create_sysfs(pdev);
    //rohm_proximity_enable(&proximity_data);//add by DO驱动自动启动,不用上层调用
    rc = rohm_proximity_setup(ip);
    if (!rc)
        goto done;

    misc_deregister(&rohm_proximity_misc);

err_unregister_input_device:
    input_unregister_device(input_dev);
err_free_input_device:
    input_free_device(input_dev);
    wake_lock_destroy(&prox_delayed_work_wake_lock);
done:
    PS_DBG("%s, Done\n", __func__);
    return rc;
}

static int rohm_proximity_remove(struct platform_device *pdev)
{
    struct rohm_proximity_platform_data *pdata=pdev->dev.platform_data;

    cancel_rearming_delayed_workqueue(proximity_data.work_queue, &proximity_data.work); 
    flush_workqueue(proximity_data.work_queue);
    destroy_workqueue(proximity_data.work_queue);

    misc_deregister(&rohm_proximity_misc);
    input_unregister_device(proximity_data.input_dev);
    input_free_device(proximity_data.input_dev);

    //unregister gpio
    gpio_free(pdata->gpio_trans);
    gpio_free(pdata->gpio_recv);

    wake_lock_destroy(&prox_delayed_work_wake_lock);

    return 0;
}


static struct platform_driver rohm_proximity_driver = {
    .probe = rohm_proximity_probe,
    .remove = rohm_proximity_remove,
    .driver = {
        .name = ROHM_PROXIMITY,
        .owner = THIS_MODULE
    },
};


static int __devinit rohm_proximity_init(void)
{
    PS_DBG("%s\n",__func__);
    platform_device_register(&rohm_proximity_device);
    return platform_driver_register(&rohm_proximity_driver);
}

static void rohm_proximity_exit(void)
{
    PS_DBG("%s\n",__func__);
   
    platform_device_unregister(&rohm_proximity_device);
    platform_driver_unregister(&rohm_proximity_driver);
}

module_init(rohm_proximity_init);
module_exit(rohm_proximity_exit);

MODULE_DESCRIPTION("Rohm Proximity driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:proximity sensor");