linux驅動開發:mma7660 sensor的配置

上一章節有介紹過這顆IC,是freescale的一個低精度的重力傳感器吧。我認爲,可以區分8個方向。x,y,z三軸。精度6bit,採樣值範圍[-32,+32].還有測量shake和tap等數據。
這邊的驅動沒有自己寫.只是有看懂它原先的驅動是如何實現和工作的.
它的驅動引入了一個 hwmon device的註冊。具體的作用是在sys/class/hwmon下生成了一個目錄hwmonx。
它可以添加你設備的一些屬性attr,以此來方便用戶去read那些屬性的狀態。網上也有說藉此來導出內核的信息非常方便,幾乎所有的sensor都喜歡這樣用。我們可以借用內核導出x,y,z三軸的值,來方便查看數據.介於此,我們可以添加相關的屬性,當訪問這些屬性文件時,實則是在調用對應的接口函數。比如讀相應的寄存器,再顯示給用戶界面。
另外一個:傳感器元件,都有一個特性。週期性的採樣數據並上報數據。
我們再實現觸摸屏驅動的時候,當你有手觸摸時,立即便會上報一次數據,否則則沒有.按鍵也是一樣。
像有的溫度傳感器,基本上是每次間隔一定的時間,才能採樣一次準確的數據。並不是立刻就能獲得數據的.需要一定的時間。
所以這邊引入了一個input子系統的擴展:input_polled_dev.

我們依舊把它作爲輸入子系統來實現。上報x,y,z三軸的絕對座標.

所以整個驅動的框架沒變。新多出來的功能是 屬性導出功能,hwmon設備的引入,和input_polled_dev的引入。

程序我都有註釋過,初始化完畢後,當有觸發過中斷時,程序便會更新一次TILT狀態寄存器。此時我們可以去讀我們需要的數據。根據讀到的數據做出相應的判斷.比如說是否旋轉屏幕。想一想手機的自動旋轉功能,當我們把屏幕橫着放時,畫面會對應做旋轉,這裏面也是通過sensor來識別當前的位置,根據位置做出畫面的相應調整。

常用的傳感器很多很多。溫度傳感器,壓力傳感器,靠近傳感器,聲音傳感器,加速度傳感器等等。
我們今天實現的只是很小的一類中的特定一個傳感器而已。

驅動實例:

/*
 * Copyright 2011 Freescale Semiconductor, Inc. All Rights Reserved.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * version 2 as published by the Free Software Foundation.
 *
 * 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.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA
 */

#ifndef __LINUX_MMA7660_H
#define __LINUX_MMA7660_H

#include <linux/types.h>

#ifdef __KERNEL__

/* MMA7760 Registers */
#define MMA7660_XOUT            0x00    // 6-bit output value X
#define MMA7660_YOUT            0x01    // 6-bit output value Y
#define MMA7660_ZOUT            0x02    // 6-bit output value Z
#define MMA7660_TILT            0x03    // Tilt status
#define MMA7660_SRST            0x04    // Sampling Rate Status
#define MMA7660_SPCNT           0x05    // Sleep Count
#define MMA7660_INTSU           0x06    // Interrupt Setup
#define MMA7660_MODE            0x07    // Mode
#define MMA7660_SR              0x08    // Auto-Wake/Sleep and Debounce Filter
#define MMA7660_PDET            0x09    // Tap Detection
#define MMA7660_PD              0x0a    // Tap Debounce Count

struct mma7660_platform_data {
    /* eint connected to chip */
    int irq;

    /* parameters for input device */
    int poll_interval;
    int input_fuzz;
    int input_flat;
};

#endif /* __KERNEL__ */

#endif /* __LINUX_MMA7660_H */

這邊因爲是掛在IIC總線上,同樣,IIC設備的實例化我們還是按照老方法來做:

static struct mma7660_platform_data mma7660_pdata = {
    .irq            = IRQ_EINT(11),
    .poll_interval  = 100,
    .input_fuzz     = 4,
    .input_flat     = 4,
};
static struct i2c_board_info smdkv210_i2c_devs0[] __initdata = {
    { I2C_BOARD_INFO("24c08", 0x50), },  
    {
        I2C_BOARD_INFO("mma7660", 0x4c),
        .platform_data = &mma7660_pdata,
    },

};

同樣,我們可以看到,在iic0上掛了兩個設備,一個是24c08,一個是mma7660,因爲slave addr的不同,我們可以訪問到不同的從設備。
驅動代碼:

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <asm/uaccess.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/input-polldev.h>
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/mma7660.h>
#include <mach/hardware.h>



#define MMA7660_NAME                "mma7660"   

#define POLL_INTERVAL               100
#define INPUT_FUZZ                  4
#define INPUT_FLAT                  4


#define __need_retry(__v)   (__v & (1 << 6))
#define __is_negative(__v)  (__v & (1 << 5))

static const char *mma7660_bafro[] = {
    "Unknown", "Front", "Back"
};
static const char *mma7660_pola[] = {
    "Unknown",
    "Left", "Right",
    "Rsvd", "Rsvd",
    "Down", "Up",
    "Rsvd",
};

static const struct i2c_device_id mma7660_id[] =
{
    {"mma7660",0},
    {}
};

struct mma7660_dev
{
    int poll_interval;
    int input_fuzz;
    int input_flat;
    struct device       *hwmon_dev;     //hwmon dev
    struct input_polled_dev *ip_dev;    //input poll dev
    struct i2c_client   * mma_iic_client; //iic client
    struct mma7660_platform_data *pdata;  // platform data
};

static int                  last_tilt = 0;
static int                  oper_mode;

static struct input_dev *i_dev = NULL;
static struct mma7660_dev *mma7660_struct = NULL;

static int mma7660_read_tilt(struct i2c_client *client, int *tilt);

static void mma7660_worker(struct work_struct *work)
{
#if 0
    int bafro, pola, shake, tap;
#endif
    int val = 0;

    mma7660_read_tilt(mma7660_struct->mma_iic_client, &val);

#if 0
    bafro = val & 0x03;
    if(bafro != (last_tilt & 0x03)) 
    {
        printk("%s\n", mma7660_bafro[bafro]);
    }

    pola = (val >> 2) & 0x07;
    if(pola != ((last_tilt >> 2) & 0x07))
    {
        printk("%s\n", mma7660_pola[pola]);
    }

    shake = (val >> 5) & 0x01;
    if(shake && shake != ((last_tilt >> 5) & 0x01))
    {
        printk("Shake\n");
    }

    tap = (val >> 7) & 0x01;
    if(tap && tap != ((last_tilt >> 7) & 0x01))
    {
        printk("Tap\n");
    }
#endif
    /* Save current status */
    last_tilt = val;
}

DECLARE_WORK(mma7660_work, mma7660_worker);

static irqreturn_t mma7660_interrupt(int irq, void *dev_id) 
{
    schedule_work(&mma7660_work);
    return IRQ_HANDLED;
}

/*read the data from  reg TILT */
static int mma7660_read_tilt(struct i2c_client *client, int *tilt)
{
    int val;

    do 
    {
        val = i2c_smbus_read_byte_data(client, MMA7660_TILT);
        if(val < 0)
        {
            dev_err(&client->dev, "Read register %02x failed, %d\n",MMA7660_TILT, val);
            return -EIO;
        }
    }while(__need_retry(val));//when bit6 is 1,can't read value,so do this to wait the TILT reg ready to read

    *tilt = (val & 0xff);

    return 0;
}
static int mma7660_read_xyz(struct i2c_client *client, int idx, int *xyz)
{
    int val;

    do 
    {
        val = i2c_smbus_read_byte_data(client, idx + MMA7660_XOUT);
        if(val < 0) 
        {
            dev_err(&client->dev, "Read register %02x failed, %d\n",idx + MMA7660_XOUT, val);
            return -EIO;
        }
    }while(__need_retry(val));

    *xyz = __is_negative(val) ? (val | ~0x3f) : (val & 0x3f);

    return 0;
}


static int mma7660_initial(struct i2c_client *client)
{
    int val;

    //1.set mode to stand by mode first by set bit0 (mode bit)to 0
    i2c_smbus_write_byte_data(client, MMA7660_MODE, 0x00);
    mdelay(10);
    // 2.set mode to test mode,it should  set in stand by mode by set the bit 2(ton bit) to 1 
    i2c_smbus_write_byte_data(client, MMA7660_MODE, 0x04);
    mdelay(10);
    //3.in test mode can set the reg MMA7660_XOUT,MMA7660_YOUT,MMA7660_ZOUT
    //3.1 clear the bit6(alert bit)
    i2c_smbus_write_byte_data(client, MMA7660_XOUT, 0x3f);
    //3.2 clear the bit6(alert bit)
    i2c_smbus_write_byte_data(client, MMA7660_YOUT, 0x3f);
    //3.3 clear the bit6(alert bit)
    i2c_smbus_write_byte_data(client, MMA7660_ZOUT, 0x3f);

    val = i2c_smbus_read_byte_data(client, MMA7660_ZOUT);

    if(val != 0x3f) 
    {
        printk("2.test write and read reg 0x02 but get wrong data: %d, no dev!!!\n",val);
        return -ENODEV;
    }

    //reset to stand by mode
    i2c_smbus_write_byte_data(client, MMA7660_MODE, 0x00);
    mdelay(10);


    //AMSR[2:0]bit0-bit2 :samples rate: 001----64 samples/sec  in Active and Auto-Sleep Mode
    //AWSR[1:0]:bit4-bit3: samples rate: 01-----16 Samples/Second  Auto-Wake Mode
    //bit7-bit5 :samples rate 001----- 3samples/sec TILT
    i2c_smbus_write_byte_data(client, MMA7660_SR, ((2<<5) | (1<<3) | 1));

    //sleep count:160,when reach to the timeout time,will go in auto wake mode,ps
    i2c_smbus_write_byte_data(client, MMA7660_SPCNT, 0xA0);

    //Tap detection threshold is ±4 counts
    //X-axis,Y-axis,Z-axis  is enabled for tap detection
    i2c_smbus_write_byte_data(client, MMA7660_PDET, 4);

    //Tap detection debounce filtering requires 16 adjacent tap detection tests to be the same to trigger a tap event and set the Tap
    //bit in the TILT (0x03) register, and optionally set an interrupt if PDINT is set in the INTSU (0x06) register. Tap detection
    //response time is nominally 1.04 ms.
    i2c_smbus_write_byte_data(client, MMA7660_PD, 15);

    /* Enable interrupt except exiting Auto-Sleep */
    //FBINT bit 0 ---1: Front/Back position change causes an interrupt
    //PLINT bit 1---1:Up/Down/Right/Left position change causes an interrupt
    //PDINT bit 2---1: Successful tap detection causes an interrupt
    //ASINT bit 3---0:  Exiting Auto-Sleep does not cause an interrupt
    //GINT bit4---0:There is not an automatic interrupt after everymeasurement
    //SHINTX bit 5---1:Shake detected on the X-axis causes an interrupt, and sets the Shake bit in the TILT register
    //SHINTY bit6---1: Shake detected on the Y-axis causes an interrupt, and sets the Shake bit in the TILT register
    //SHINTZ bit7---1: Shake detected on the Z-axis causes an interrupt, and sets the Shake bit in the TILT register.
    i2c_smbus_write_byte_data(client, MMA7660_INTSU, 0xe7);

    /* IPP, Auto-wake, auto-sleep and standby */
    //Active mode: bit0 set to 1 ,bit 2 set o 0
    //  AWE bit3----1:Auto-Wake is enabled
    //ASE bit4----1: Auto-Sleep is enabled
    //SCPS bit5---0: The prescaler is divide-by-1. The 8-bit internal Sleep Counter input clock is the samples per second set by
    //AMSR[2:0], so the clock range is 120 Hz to 1 Hz depending on AMSR[2:0] setting. Sleep Counter timeout range is
    //256 times the prescaled clock
    //IPP bit6---1: Interrupt output INT is push-pull
    //IAH bit7---0: Interrupt output INT is active low
    i2c_smbus_write_byte_data(client, MMA7660_MODE, 0x59);
    mdelay(10);

    /* Save current tilt status */
    mma7660_read_tilt(client, &last_tilt);

    return 0;
}


/* attr*/
static ssize_t mma7660_show_regs(struct device *dev,struct device_attribute *attr, char *buf)
{
    int reg, val;
    int i, len = 0;

    for(reg = 0; reg < 0x0b; reg++)
    {
        val = i2c_smbus_read_byte_data(mma7660_struct->mma_iic_client, reg);

        len += sprintf(buf + len, "REG: 0x%02x = 0x%02x ...... [ ", reg, val);
        for(i = 7; i >= 0; i--)
        {
            len += sprintf(buf + len, "%d", (val >> i) & 1);

            if((i % 4) == 0)
            {
                len += sprintf(buf + len, " ");
            }
        }
        len += sprintf(buf + len, "]\n");
    }

    return len;
}

static ssize_t mma7660_write_reg(struct device *dev,struct device_attribute *attr, const char *buf, size_t count)
{
    unsigned int reg, val;
    int ret;

    ret = sscanf(buf, "%x %x", &reg, &val);
    if(ret == 2)
    {
        if (reg >= 0 && reg <= 0x0a)
        {
            i2c_smbus_write_byte_data(mma7660_struct->mma_iic_client, reg, val);
            val = i2c_smbus_read_byte_data(mma7660_struct->mma_iic_client, reg);
            printk("REG: 0x%02x = 0x%02x\n", reg, val);
        }
    }

    return count;
}

static ssize_t mma7660_show_xyz_g(struct device *dev,struct device_attribute *attr, char *buf)
{
    int axis[3];
    int i;

    for (i = 0; i < 3; i++)
    {
        mma7660_read_xyz(mma7660_struct->mma_iic_client, i, &axis[i]);
    }

    return sprintf(buf, "%3d, %3d, %3d\n", axis[0], axis[1], axis[2]);
}

static ssize_t mma7660_show_axis_g(struct device *dev,struct device_attribute *attr, char *buf)
{
    int n = to_sensor_dev_attr(attr)->index;
    int val;

    mma7660_read_xyz(mma7660_struct->mma_iic_client, n, &val);

    return sprintf(buf, "%3d\n", val);
}

static ssize_t mma7660_show_tilt(struct device *dev,struct device_attribute *attr, char *buf)
{
    int val = 0, len = 0;

    mma7660_read_tilt(mma7660_struct->mma_iic_client, &val);

    len += sprintf(buf + len, "%s", mma7660_bafro[val & 0x03]);
    len += sprintf(buf + len, ", %s", mma7660_pola[(val >> 2) & 0x07]);

    if(val & (1 << 5))
    {
        len += sprintf(buf + len, ", Tap");
    }
    if(val & (1 << 7))
    {
        len += sprintf(buf + len, ", Shake");
    }
    len += sprintf(buf + len, "\n");

    return len;
}

static SENSOR_DEVICE_ATTR(registers, S_IRUGO | S_IWUGO,mma7660_show_regs, mma7660_write_reg, 0);
static SENSOR_DEVICE_ATTR(x_axis_g, S_IRUGO, mma7660_show_axis_g, NULL, 0);
static SENSOR_DEVICE_ATTR(y_axis_g, S_IRUGO, mma7660_show_axis_g, NULL, 1);
static SENSOR_DEVICE_ATTR(z_axis_g, S_IRUGO, mma7660_show_axis_g, NULL, 2);
static SENSOR_DEVICE_ATTR(all_axis_g, S_IRUGO, mma7660_show_xyz_g, NULL, 0);
static SENSOR_DEVICE_ATTR(tilt_status, S_IRUGO, mma7660_show_tilt, NULL, 0);

static struct attribute* mma7660_attrs[] = 
{
    &sensor_dev_attr_registers.dev_attr.attr,
    &sensor_dev_attr_x_axis_g.dev_attr.attr,
    &sensor_dev_attr_y_axis_g.dev_attr.attr,
    &sensor_dev_attr_z_axis_g.dev_attr.attr,
    &sensor_dev_attr_all_axis_g.dev_attr.attr,
    &sensor_dev_attr_tilt_status.dev_attr.attr,
    NULL
};

static const struct attribute_group mma7660_group = {
    .attrs      = mma7660_attrs,
};
//Input interfaces
static void mma7660_report_abs(void)
{
    int axis[3];
    int i;

    for(i = 0; i < 3; i++)
    {
        mma7660_read_xyz(mma7660_struct->mma_iic_client, i, &axis[i]);
    }

    input_report_abs(mma7660_struct->ip_dev->input, ABS_X, axis[0]);
    input_report_abs(mma7660_struct->ip_dev->input, ABS_Y, axis[1]);
    input_report_abs(mma7660_struct->ip_dev->input, ABS_Z, axis[2]);
    input_sync(mma7660_struct->ip_dev->input);

    //printk("3-Axis ... %3d, %3d, %3d\n", axis[0], axis[1], axis[2]);
}

static void mma7660_dev_poll(struct input_polled_dev *dev)
{
    mma7660_report_abs();
}

/*****int (*probe)(struct i2c_client *, const struct i2c_device_id *)*****/

static int mma7660_probe(struct i2c_client *uc_i2c_client, const struct i2c_device_id * uc_i2c_id_table)
{
    int err=0;
    //check iic
    if(!i2c_check_functionality(uc_i2c_client->adapter,I2C_FUNC_I2C)) 
    {
        err = -ENODEV;
        goto FAIL_CHECK_FUNC;
    }

    //check platdata
    if(!uc_i2c_client->dev.platform_data)
    {   
        err = -ENODATA;
        goto FAIL_NO_PLATFORM_DATA;
    }

    //allooc buf  
    mma7660_struct = kzalloc(sizeof(struct mma7660_dev),GFP_KERNEL);
    if(!mma7660_struct)
    {
        err=-ENOMEM;
        goto FAIL_KZALLOC;
    }
    //initial start
    mma7660_struct->mma_iic_client = uc_i2c_client;
    mma7660_struct->pdata = uc_i2c_client->dev.platform_data;

    mma7660_struct->poll_interval =((mma7660_struct->pdata->poll_interval >0) ? mma7660_struct->pdata->poll_interval : POLL_INTERVAL);
    mma7660_struct->input_flat =((mma7660_struct->pdata->input_flat >0) ? mma7660_struct->pdata->input_flat : INPUT_FLAT);
    mma7660_struct->input_fuzz =((mma7660_struct->pdata->input_fuzz >0) ? mma7660_struct->pdata->input_fuzz : INPUT_FUZZ);

    if(mma7660_struct->pdata->irq)
    {
        mma7660_struct->mma_iic_client->irq = mma7660_struct->pdata->irq;
    }
    else
    {
        err = -ENODATA;
        printk("3.the platformdata no irq data\n");
        goto FAIL_NO_IRQ_DATA;
    }
    //initial the dev
    err = mma7660_initial(mma7660_struct->mma_iic_client);
    if(err < 0)
    {
        goto FAIL_MMA7660_INIT;
    }

    err = sysfs_create_group(&mma7660_struct->mma_iic_client->dev.kobj, &mma7660_group);
    if(err)
    {
        printk("4.create sysfs group failed!\n");
        goto FAIL_CREATE_GROUP;
    }

    // register to hwmon device 
    mma7660_struct->hwmon_dev = hwmon_device_register(&mma7660_struct->mma_iic_client->dev);
    if(IS_ERR(mma7660_struct->hwmon_dev))
    {
        err = PTR_ERR(mma7660_struct->hwmon_dev);
        printk("5.hwmon register failed!\n");
        goto FAIL_HWMON_REGISTER;
    }

    //alloc the input poll device  
    mma7660_struct->ip_dev = input_allocate_polled_device();
    if(!mma7660_struct->ip_dev)
    {
        err = -ENOMEM;
        printk("6.alloc poll device failed!\n");
        goto FAIL_ALLLOC_INPUT_PDEV;
    }

    mma7660_struct->ip_dev->poll = mma7660_dev_poll;
    mma7660_struct->ip_dev->poll_interval =mma7660_struct->pdata->poll_interval;

    i_dev = mma7660_struct->ip_dev->input;

    i_dev->name = MMA7660_NAME;
    i_dev->id.bustype = BUS_I2C;
    i_dev->id.vendor = 0x12FA;
    i_dev->id.product = 0x7660;
    i_dev->id.version = 0x0100;
    i_dev->dev.parent = &mma7660_struct->mma_iic_client->dev; 

    set_bit(EV_ABS, i_dev->evbit);

    set_bit(ABS_X, i_dev->absbit);
    set_bit(ABS_Y, i_dev->absbit);
    set_bit(ABS_Z, i_dev->absbit);

    input_set_abs_params(i_dev, ABS_X, -512, 512, mma7660_struct->input_fuzz, mma7660_struct->input_flat);
    input_set_abs_params(i_dev, ABS_Y, -512, 512, mma7660_struct->input_fuzz, mma7660_struct->input_flat);
    input_set_abs_params(i_dev, ABS_Z, -512, 512, mma7660_struct->input_fuzz, mma7660_struct->input_flat);

    err = input_register_polled_device(mma7660_struct->ip_dev);
    if(err)
    {       
        printk("7.register poll device failed!");
        goto FAIL_INPUT_REGISTER_PDEV;
    }

    //register interrupt handle 
    err = request_irq(mma7660_struct->pdata->irq, mma7660_interrupt,IRQF_TRIGGER_FALLING, MMA7660_NAME, NULL);
    if(err)
    {
        printk("8.request irq failed!\n");
        goto FAIL_REQUEST_IRQ;
    }

    printk("mma7660 driver probe success!\n");

    return 0;

FAIL_REQUEST_IRQ:
    input_unregister_polled_device(mma7660_struct->ip_dev);
FAIL_INPUT_REGISTER_PDEV:
    input_free_polled_device(mma7660_struct->ip_dev);
FAIL_ALLLOC_INPUT_PDEV:
    hwmon_device_unregister(mma7660_struct->hwmon_dev);
FAIL_HWMON_REGISTER:
    sysfs_remove_group(&mma7660_struct->mma_iic_client->dev.kobj,&mma7660_group);
FAIL_CREATE_GROUP:
FAIL_MMA7660_INIT:
FAIL_NO_IRQ_DATA:
    kfree(mma7660_struct);
FAIL_KZALLOC:
FAIL_NO_PLATFORM_DATA:
FAIL_CHECK_FUNC:

    return err;

}
/*****int (*remove)(struct i2c_client *)*****/
static int mma7660_remove(struct i2c_client *uc_i2c_client)
{
    disable_irq(mma7660_struct->pdata->irq);

    free_irq(mma7660_struct->pdata->irq,NULL);

    input_unregister_polled_device(mma7660_struct->ip_dev);
    input_free_polled_device(mma7660_struct->ip_dev);
    hwmon_device_unregister(mma7660_struct->hwmon_dev);
    sysfs_remove_group(&mma7660_struct->mma_iic_client->dev.kobj,&mma7660_group);

    return 0;
}
static int mma7660_suspend(struct i2c_client *client, pm_message_t state)
{
    int ret;

    oper_mode = i2c_smbus_read_byte_data(client, MMA7660_MODE);

    ret = i2c_smbus_write_byte_data(client, MMA7660_MODE, 0);
    if(ret)
    {
        printk("%s: set mode (0) for suspend failed, ret = %d\n",MMA7660_NAME, ret);
    }

    return 0;
}

static int mma7660_resume(struct i2c_client *client)
{
    int ret;

    ret = i2c_smbus_write_byte_data(client, MMA7660_MODE, oper_mode);
    if (ret) 
    {
        printk("%s: set mode (%d) for resume failed, ret = %d\n",MMA7660_NAME, oper_mode, ret);
    }
    return 0;
}

static struct i2c_driver mma7660_drv =
{
    .driver =
    {
        .name = MMA7660_NAME,   
        .owner= THIS_MODULE,
    },
    .probe   = mma7660_probe,
    .remove  = mma7660_remove,
    .suspend = mma7660_suspend,
    .resume  = mma7660_resume,
    //match use
    .id_table = mma7660_id,
};

int __init  mma7660_init(void)
{
    i2c_add_driver(&mma7660_drv);   
    return 0;
}

void __exit mma7660_exit(void)
{
    i2c_del_driver(&mma7660_drv);
}






module_init(mma7660_init);
module_exit(mma7660_exit);
MODULE_LICENSE("GPL");

整個驅動最難理解的地方應該還是在probe函數裏面。當初始化完成以後,大部分的函數都是在做讀操作而已。我們可以根據芯片手冊來了解它的配置。同時比較難理解的是它本次採用的驅動框架。其他沒什麼難的,和之氣的輸入子系統都是一樣的。

實驗現象:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章