G-sensor驅動分析

重力傳感器代碼分析node

 

重力傳感器驅動的功能,主要是向HAL層提供IOCTRL接口,並經過input設備上報數據。芯片實際數據的讀取是採用i2c協議讀取原始數據,而且做爲i2c設備掛載在系統上工做的。linux

1、調用關係算法

    採用模塊化的編程方式,一下介紹函數的調用關係。編程

module_init(aac_MMAxxxxx_init);數組

module_exit(aac_MMAxxxxx_exit);安全

    模塊中定義了驅動初始化和退出函數,具體實現以下模塊化

static int __init aac_MMAxxxxxFC_init(void)函數

{spa

    int ret;線程

    if ((ret = i2c_add_driver(&aac_MMAxxxxxFC_i2c_driver))) {

        printk(KERN_WARNING "aac_MMAxxxxxFC_init failed. /n");

        return ret;

    }

    return ret;   

}

static void  __exit aac_MMAxxxxxFC_exit(void)

{

    i2c_del_driver(&aac_MMAxxxxxFC_i2c_driver);

}

    調用i2c_add_driver函數將aac_MMAxxxxxFC_i2c_driver驅動添加實現了初始化函數,exit函數則調用i2c_del_driver函數將aac_MMAxxxxxFC_i2c_driver驅動刪除。

    對於aac_MMAxxxxxFC_i2c_driver驅動結構體,由6個參數實現該結構體。Driver用模塊名來填充,probe、remove、suspend、resume分別用相對應的函數來填充。Id_table用aac_ MMAxxxxxFC_id來填充索引表。實現代碼以下:

static struct i2c_driver aac_MMAxxxxxFC_i2c_driver = {

    .driver = {

        .name = MMAxxxxx_MODULE_NAME,

    },

    .probe = aac_MMAxxxxxFC_probe,

    .remove = aac_MMAxxxxxFC_remove,

    .id_table   = aac_MMAxxxxxFC_id,

#ifndef CONFIG_HAS_EARLYSUSPEND

    .suspend  = mmaxxxxx_suspend,

    .resume   = mmaxxxxx_resume,

#endif

};

1.一、   對於結構體中索引表aac_MMAxxxxxFC_id數組,具體實現以下:

static const struct i2c_device_id aac_MMAxxxxxFC_id[] = {

    { MMAxxxxx_MODULE_NAME, 0 },

    { }

};

1.二、   MMAxxxxx_MODULE_NAME表示驅動名。在頭文件中定義了具體名字"mmaxxxxx"

1.三、   aac_MMAxxxxxFC_probe函數是i2c驅動尋找設備的經典實現,這裏將具體分析下實現過程。實現思路是首先註冊i2c功能函數類型,而後分配misc設備空間並註冊,接下來分配輸入設備空間並註冊,注意將misc設備獲取數據傳給input設備數據中。最後建立工做隊列,實現位置信息數據處理。

具體代碼以下:

1.3.1調用i2c_check_functionality,函數返回咱們須要類型的i2c適配器

    if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {

        ret = -ENODEV;

        goto exit0;

    }

而且將入口函數參數client賦值給靜態全局變量g_client

    g_client = client;

1.3.2調用kzalloc函數給空結構體賦值,空結構體的意義在於尋址。調用sysfs_create_group函數將設備體建立到mmaxxxxx_attr_group組,主要爲調試使用,關於具體調試文件系統將在文章後面章節介紹。調用misc_register函數註冊misc設備mmaxxxxx_misc_device。

mmaxxxxx_misc_data = kzalloc(sizeof(struct mmaxxxxx_data), GFP_KERNEL);

    if (!mmaxxxxx_misc_data) {

        ret = -ENOMEM;

        goto exit1;

    }

    //init sysfs entry

    ret = sysfs_create_group(&client->dev.kobj, &mmaxxxxx_attr_group);

    if (ret)

        goto exit2;

    //misc_register

     ret = misc_register(&mmaxxxxx_misc_device);

    if (ret < 0) {

         dev_err(&client->dev, "mmaxxxxx_device register failed/n");

         goto exit3;

        }

1.3.3調用input_allocate_device函數給input設備分配空間

        //input_allocate

        input = input_allocate_device();

    if (!input) {

            ret = -ENOMEM;

            printk("input device allocate failed/n");

            goto exit4;

    }

對input各個屬性項目填充,name、phys表示映射的物理端口、id.bustype、id.vendor、id.product、

    input->name = "mmaxxxxx";

    input->phys = "mmaxxxxx/input0";

    input->id.bustype = BUS_HOST;

    input->id.vendor = 0x0001;

    input->id.product = 0x0001;

    input->id.version = 0x0100;

    //evbit選擇了事件類型,absbit表示了絕對值的數據

    input->evbit[0] = BIT(EV_ABS);

    input->absbit[0] = BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_Z);

    //將input賦值給全局變量g_input_dev設備。

    g_input_dev = input;

    ret = input_register_device(g_input_dev);

    if (ret) {

            printk("unable to register input polled device /n");

            goto exit5;

    }

    //把misc設備數據賦值給輸入設備系統數據

    input_set_drvdata(g_input_dev, mmaxxxxx_misc_data);

 

1.3.4調用init_MUTEX函數將信號量申請爲互斥體信號,在分別在mmaxxxxx_misc_ioctl、asensor_thread、mmaxxxxx_resume函數中調用,確保時間可以安全賦值。

而後建立工做隊列asensor_wq,調用INIT_DELAYED_WORK函數asensor_thread數據處理函數添加到工做任務asensor_delayed_work當中。注意,此時工做隊列並無開始工做,須要mmaxxxxx_misc_ioctl接到HAL層的控制信息時纔打開端口,進行工做。

    init_MUTEX(&sem_thread);

    asensor_wq = create_workqueue("MMAxxxxxFC_workqueue");

    if (!asensor_wq) {

        printk("can't create a workqueue/n");

        ret = -1;

        goto exit6;

    }

    INIT_DELAYED_WORK(&asensor_delayed_work, asensor_thread);

1.四、   aac_MMAxxxxxFC_remove函數是做爲probe函數的反過程實現的,主要是取消工做隊列和釋放相關資源。

具體代碼實現以下

    ret = cancel_delayed_work(&asensor_delayed_work);

    if(ret == 0){

        flush_workqueue(asensor_wq);

    }

    destroy_workqueue(asensor_wq);

    asensor_wq = NULL;

#ifdef CONFIG_HAS_EARLYSUSPEND

    unregister_early_suspend(&early_suspend);

#endif

    //clean input

    mmaxxxxx_input_cleanup(); 

    //sysfs

    sysfs_remove_group(&client->dev.kobj, &mmaxxxxx_attr_group);

    //misc

    misc_deregister(&mmaxxxxx_misc_device);

 

1.五、   mmaxxxxx_suspend和mmaxxxxx_resume函數主要是睡眠和喚醒時做用,關掉外設電源並釋放佔用的相關資源。喚醒時實現反過程。

static int mmaxxxxx_suspend(struct i2c_client *ic, pm_message_t mesg)

{

    printk("########mmaxxxxx_suspend");

    cancel_delayed_work_sync(&asensor_delayed_work);

    aac_MMAxxxxxFC_close_client();

    return 0;

}

static int mmaxxxxx_resume(struct i2c_client *ic)

{

    printk("########mmaxxxxx_resume");

    aac_MMAxxxxxFC_init_client();

   

    down_interruptible(&sem_thread);

    time_delay.tv_sec = 0;

    time_delay.tv_usec = read_interval;

    up(&sem_thread);

    if(1 == thread_flag)

    {     

        printk("########resume!/n");

            queue_delayed_work(asensor_wq, &asensor_delayed_work, timeval_to_jiffies(&time_delay));  

    }

    return 0;

}

 

2.     特殊處理函數

2.1 misc控制函數

主要做用是處理HAL層的IOCTL命令,起到打開、關閉的任務。

首先定義了混雜設備結構體mmaxxxxx_misc_device,該結構體體由3個field組成,第一個表示misc設備的此設備號,第二個爲misc設備的名字,第三個爲misc操做結構體。操做結構體由咱們自行定義。

static struct miscdevice mmaxxxxx_misc_device = {

    .minor = MISC_DYNAMIC_MINOR,

    .name = "mmaxxxxx",

    .fops = &mmaxxxxx_misc_fops,

};

    而後定義Misc操做結構體,該結構體由3個field組成,第一個表示全部者,屬性固定爲本模塊,即THIS_MODULE。第二個表示打開函數,處理數據信息,第三個表示控制函數,處理misc設備的相關控制命令。

    static const struct file_operations mmaxxxxx_misc_fops = {

    .owner = THIS_MODULE,

    .open =  mmaxxxxx_misc_open,

    .ioctl = mmaxxxxx_misc_ioctl,

};

    做爲傳感器輸入設備,打開函數使用的也是數據流,因此定位數據沒有意義。這種狀況下,不能簡單不聲明lseek操做,由於默認方法是容許定位的。默認定位的方法是調用lseek函數在數據區往上或往下定位數據。在open方法中調用nonseekable_open()時,它會通知內核設備不支持lseek。

    函數實現以下:

    static int mmaxxxxx_misc_open(struct inode *inode, struct file *file)

{

    int err;

    err = nonseekable_open(inode, file);

    if (err < 0)

        return err;

    file->private_data = mmaxxxxx_misc_data;

    return 0;

}

這裏注意的是文件的私有數據賦值對象爲mmaxxxxx_misc_data,是一個空結構體變量。難道也僅僅是爲了尋址麼?

    Ioctl函數做爲misc設備核心的操做函數,主要做用是經過HAL層中相關command字的控制,給應用層提供了控制方法,最終實現設備體的狀態獲取,延時,激活,關閉,如匹配字不符合,則控制參數有誤退出。

   Ioctl函數中主要包括的控制命令爲MMAxxxxx_IOCTL_GET_STATE、MMAxxxxx_IOCTL_SET_DELAY、MMAxxxxx_IOCTL_ENABLE、MMAxxxxx_IOCTL_DISABLE幾個命令。

具體可參考代碼。如MMAxxxxx_IOCTL_GET_STATE中主要經過copy_to_user將線程標示位賦值給參數argp,從而獲取狀態。其餘幾個具體參考代碼。

    static int mmaxxxxx_misc_ioctl(struct inode *inode, struct file *file,

                  unsigned int cmd, unsigned long arg)

{

    void __user *argp = (void __user *)arg;

    int32_t interval;

   

    switch( cmd )

    {

        case MMAxxxxx_IOCTL_GET_STATE:

        {

            if(copy_to_user(argp, &thread_flag, sizeof(thread_flag)))

                return -EFAULT;

            //printk("MMAxxxxx_IOCTL_GET_STAT/n");

            break;

        }

        case MMAxxxxx_IOCTL_ENABLE:

        {

            aac_MMAxxxxxFC_init_client();

            thread_flag = 1;

               

                down_interruptible(&sem_thread);

                time_delay.tv_sec = 0;

                time_delay.tv_usec = read_interval;

                up(&sem_thread);           

                queue_delayed_work(asensor_wq, &asensor_delayed_work, timeval_to_jiffies(&time_delay));

               

                //printk("MMAxxxxx_IOCTL_ENABLE/n");

            break;

        }

    }

}

    在MMAxxxxx_IOCTL_ENABLE控制命令下,經過原子操做定義了延時的時間,將工做任務asensor_delayed_work添加到工做隊列asensor_wq中,這樣就循環開始了該工做。

    2.2 工做函數asensor_thread

    工做函數中主要是經過i2c線讀取相關的輸出數據。I2c讀取的方式這裏再也不詳述,這裏主要經過調用i2c_smbus_read_i2c_block_data函數,讀取連續三個地址的數值,經過數據處理,根據硬件相關的貼片方式,輸出正確的xyz結果。

    處理過的結果用自定義的結構體保存。

struct _mmaxxxxx_data{

    int  x_data;

    int  y_data;

    int  z_data;

};

    這裏有個須要處理的地方就是有些芯片靈敏度太高,能夠經過濾波算法進行相關的去抖動處理。具體參考後續文章。

3   調試信息控制文件接口

    這裏經過static int g_print = 0來實現是否輸出打印信息,介紹相關知識以前,須要先了解linux內核中sys文件系統的介紹。sysfs 屬性的功能只能靠閱讀源代碼來理解。在內核中, sysfs 屬性通常是由 __ATTR 系列的宏來聲明的,如對設備的使用 DEVICE_ATTR ,對總線使用 BUS_ATTR ,對驅動使用 DRIVER_ATTR ,對類別(class)使用  CLASS_ATTR, 這四個高級的宏來自於 <include/linux/device.h>, 都是以更低層的來自 <include/linux/sysfs.h> 中的 __ATTR/__ATRR_RO 宏實現;所以咱們在內核源碼樹中相應位置 drivers/scsi/ 找到這幾個宏的使用狀況,能夠獲得在 drivers/scsi/scsi_sysfs.c 中。

      下面經過代碼介紹DEVICE_ATTR的添加過程。

      3.1定義控制變量

static int g_print = 0;

    3.2定義mmaxxxxx_show_print函數

功能主要是將g_print打印到內存當中。

static ssize_t mmaxxxxx_show_print(struct device *dev,

            struct device_attribute *attr, char *buf)

{

 

    return sprintf(buf, "%d/n", g_print);

}

    3.3定義mmaxxxxx_store_print函數

    功能主要是獲取buf中存在的控制值。

static ssize_t mmaxxxxx_store_print(struct device *dev,

            struct device_attribute *attr, char *buf,size_t count)

{

    unsigned long val = simple_strtoul(buf, NULL, 10);

    //adjust_light(val);

        //ggg=val;

    g_print = val;

    return count;

}

    3.4填充設備屬性DEVICE_ATTR

static DEVICE_ATTR(print, S_IWUSR | S_IRUGO,mmaxxxxx_show_print, mmaxxxxx_store_print);

其中有四個參數,分別表示是稱、權限位、讀函數、寫函數。

      3.5屬性數組mmaxxxxx_attributes

主要是填充設備屬性位置。

      static struct attribute *mmaxxxxx_attributes[] = {

      &dev_attr_print.attr,

      NULL

};

      3.6將屬性數組加到屬性組(group)裏。

static const struct attribute_group mmaxxxxx_attr_group = {

      .attrs = mmaxxxxx_attributes,

};

至此完成了屬性組的添加工做,經過adb鏈接去硬件系統中對應的文件爲sys/devices/i2c-0/x-xxxx/printx-xxxx對應的是芯片的地址線。

4    總結

文章中採用標準模塊化得方法,調用內核函數,將i2c模塊掛載到內核系統當中,並經過misc設備留接口給上層提供調用。在模塊工做過程當中,經過i2c讀函數獲取了實時的位置信息,並經過input設備將數據上報給用戶層。

相關文章
相關標籤/搜索