一、綜述
BMA220重力感應器的驅動一共涉及三個檔案:bma220.c bma220.h bma220_driver.c。bma220.c是包含BMA220驅動涉及到的API,bma220.h包含BMA220寄存器地址等,bma220_driver.c是BMA220的I2C裝置驅動。我們這裡分析和修改,主要就是針對這個bma220_driver.c。
二、bma220_driver.c修改記錄
--- bma220_driver.c2012-06-14 19:56:50.000000000 +0800+++ bma220_driver_new.c2012-06-17 16:23:46.850824401 +0800@@ -92,7 +92,8 @@ #include <linux/jiffies.h> #include "bma220.h"-#include<linux/earlysuspend.h>+#include "bma220.c"//這個必須添加,否則編譯時間找不到使用到的bma220.c中的函數+//#include<linux/earlysuspend.h>//先是在linux核心下做移植,故先注釋掉earlysuspend相關代碼,下同。 #include <linux/mutex.h> #define BMA220_MAJOR101@@ -324,7 +325,7 @@ struct mutex mutex; struct workqueue_struct*queue_struct; struct work_structwork;-struct early_suspend early_suspend;+//struct early_suspend early_suspend; int enabled; #ifdef YAMAHA_SENSORS struct input_dev *input;@@ -542,11 +543,12 @@ { bma220acc_t acc; int ret;-if (bma220_client == NULL)+if (bma220_client == NULL) {//這個括弧必須加,否則在BMA220_DEBUG調試開關開啟時,read函數始終返回“-1”! #ifdef BMA220_DEBUG printk(KERN_INFO "I2C driver not install\n"); #endif return -1;+} bma220_read_accel_xyz(&acc); #ifdef BMA220_DEBUG@@ -1936,7 +1938,7 @@ .fops = &bma220_fops, }; -static int bma220_detect(struct i2c_client *client, int kind,+static int bma220_detect(struct i2c_client *client,//新舊核心的差異,較新核心的detect函數少了kind參數 struct i2c_board_info *info) { struct i2c_adapter *adapter = client->adapter;@@ -2057,10 +2059,10 @@ } INIT_WORK(&data->work, bma220_do_polling);-data->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN;-data->early_suspend.suspend = bma220_early_suspend;-data->early_suspend.resume = bma220_early_resume;-register_early_suspend(&data->early_suspend);+//data->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN;+//data->early_suspend.suspend = bma220_early_suspend;+//data->early_suspend.resume = bma220_early_resume;+//register_early_suspend(&data->early_suspend); i2c_set_clientdata(bma220_client, data); err = misc_register(&bma_device);@@ -2120,7 +2122,7 @@ return 0; } -static unsigned short normal_i2c[] = { I2C_CLIENT_END};+//static unsigned short normal_i2c[] = { I2C_CLIENT_END};//未用到,注釋掉 I2C_CLIENT_INSMOD_1(bma220); static const struct i2c_device_id bma220_id[] = {@@ -2137,7 +2139,7 @@ }, .class= I2C_CLASS_HWMON, .id_table= bma220_id,-.address_data= &addr_data,+//.address_data= &addr_data,//未找到定義處,注釋掉 .probe= bma220_probe, .remove= bma220_remove, .detect= bma220_detect,@@ -2157,3 +2159,6 @@ module_init(BMA220_init); module_exit(BMA220_exit);++MODULE_DESCRIPTION("I2C Acceleration Sensor Driver");//添加GPL協議,否則動態載入驅動模組時,報錯+MODULE_LICENSE("GPL");
三、BSP部分
下面摘錄BSP中涉及到的BMA220相關部分:
/***********************************************************//************************* BMA220 **************************//***********************************************************/#define BMA220_INT6static struct i2c_board_info __initdata mini_i2c2_boardinfo[] = {{I2C_BOARD_INFO("bma220", 0x0b),.irq = OMAP_GPIO_IRQ(BMA220_INT),},};static int __init omap3_mini_i2c_init(void){omap_register_i2c_bus(1, 2600, mini_i2c1_boardinfo,ARRAY_SIZE(mini_i2c1_boardinfo));omap_register_i2c_bus(2, 100, mini_i2c2_boardinfo,ARRAY_SIZE(mini_i2c2_boardinfo));/* Bus 3 is attached to the DVI port where devices like the pico DLP * projector don't work reliably with 400kHz */omap_register_i2c_bus(3, 100, mini_i2c3_boardinfo,ARRAY_SIZE(mini_i2c3_boardinfo));return 0;}static void __init omap3_mini_init(void){omap3_mux_init(board_mux, OMAP_PACKAGE_CUS);omap3_mini_i2c_init();platform_add_devices(omap3_mini_devices,ARRAY_SIZE(omap3_mini_devices));omap_serial_init();usb_musb_init();usb_ehci_init(&ehci_pdata);omap3mini_flash_init();/* Ensure SDRC pins are mux'd for self-refresh */omap_mux_init_signal("sdrc_cke0", OMAP_PIN_OUTPUT);omap_mux_init_signal("sdrc_cke1", OMAP_PIN_OUTPUT);mini_display_init();}
四. 添加驅動到核心
1. 先在driver/misc/目錄下建立sensors目錄
2. 在/driver/misc/Kconfig末尾添加一行:
source "drivers/misc/sensors/Kconfig"
3. 在/driver/misc/Makefile末尾添加一行:
obj-y+= sensors/
4. 在/driver/misc/sensors/目錄下添加5個檔案:
bma220.c bma220.h bma220_driver.c Kconfig Makefile
其中,bma220_driver.c為上面修改過的,bma220.c bma220.h為原始代碼。
/driver/misc/sensors/目錄下的Kconfig和Makefile的內容如下,其中Kconfig中第一項用於配置BMA220裝置驅動,第二項用於配置晶片1腳在產品正面的相對位置。
## Sensor strange devices#menuconfig SENSORS_DEVICESbool "Sensors devices"default y---help--- Say Y here to get to see options for device drivers from various different categories. This option alone does not add any kernel code. If you say N, all options in this submenu will be skipped and disabled.if SENSORS_DEVICESconfig INPUT_BMA220 tristate "Bosch Sensortec BMA220 G-Sensor" depends on I2Cconfig INPUT_BMA220_POSITION int "BMA220 Mounting Position on Board" depends on INPUT_BMA220 default "0" help Chip mounting position (pin 1). 0: top, upper-left 1: top, upper-right 2: top, lower-right 3: top, lower-left 4: bottom, upper-left 5: bottom, upper-right 6: bottom, lower-right 7: bottom, lower-leftendif # SENSOR_DEVICES
obj-$(CONFIG_INPUT_BMA220)+= bma220_driver.o
在bma220_driver.c用到了這個配置,如下:
/* * Transformation matrix for chip mounting position */static const int bma220_position_map[][3][3] = {{{0, -1, 0}, {1, 0, 0}, {0, 0, 1} }, /* top/upper-left */ /* 7627 */{{1, 0, 0}, {0, 1, 0}, {0, 0, 1} }, /* top/upper-right */ /* 7625 */{{0, 1, 0}, {-1, 0, 0}, {0, 0, 1} }, /* top/lower-right */{{-1, 0, 0}, {0, -1, 0}, {0, 0, 1} }, /* top/lower-left */{{0, 1, 0}, {1, 0, 0}, {0, 0, -1} }, /* bottom/upper-right */{{-1, 0, 0}, {0, 1, 0}, {0, 0, -1} }, /* bottom/upper-left */{{0, -1, 0}, {-1, 0, 0}, {0, 0, -1} }, /* bottom/lower-left */{{1, 0, 0} , {0, -1, 0} , {0, 0, -1} }, /* bottom/lower-right */};static int accel_xyz_transformation(int index, bma220acc_t acc){int data = 0;data += acc.x * bma220_position_map[CONFIG_INPUT_BMA220_POSITION][index][0];data += acc.y * bma220_position_map[CONFIG_INPUT_BMA220_POSITION][index][1];data += acc.z * bma220_position_map[CONFIG_INPUT_BMA220_POSITION][index][2];return data;}
配置介面如下:
最後,產生的.config裡包含的相關配置如下,其中第三項的值就被上述函數編譯時間使用。
CONFIG_SENSORS_DEVICES=yCONFIG_INPUT_BMA220=mCONFIG_INPUT_BMA220_POSITION=0
五、測試用應用程式
應用程式主要還是配置BMA220_SET_SUSPEND為1(預設為0,讀到的資料始終不變)。板子在機殼上是背面朝上,故測試結果是,板子正面朝上,Z軸值為達到負數最小值,背面朝上Z軸值為正數最大值。板子橫向立起來,X軸整數最大值。板子縱向立起來,Y軸整數最大值。測試結果表明,驅動正常。
#include <stdio.h>#include <unistd.h>#include <stdlib.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <sys/ioctl.h>#include "bma220_cmd.h"int main(int argc, char *argv[]){int fd;int ret;unsigned int cmd;unsigned long arg;signed char data[3];printf("--------------------------------------------------------------\n");printf("--- This is the begining of the test program of my driver! ---\n");printf("--------------------------------------------------------------\n");sleep(1);/* 1. open the device driver */printf("\n1. open device driver:\n");fd = open("/dev/bma220", O_RDWR);if (fd < 0) {printf("Device open error!\n");exit(1);}usleep(500000);/* 2. set event/calibration/mode/range */cmd = BMA220_SOFT_RESET; ret = ioctl(fd, cmd);if(ret < 0)printf("ERR: BMA220_SOFT_RESET, ret = %d\n", ret);usleep(100000);/*cmd = BMA220_IOCTL_EVENT_CTRL; arg = 4;ret = ioctl(fd, cmd, &arg);if(ret < 0)printf("ERR: BMA220_IOCTL_EVENT_CTRL, ret = %d\n", ret);usleep(100000);*//*cmd = BMA220_IOCTL_CALIBRATION; ret = ioctl(fd, cmd);if(ret < 0)printf("ERR: BMA220_IOCTL_CALIBRATION, ret = %d\n", ret);usleep(100000);*/cmd = BMA220_SET_MODE; arg = 0;//Set mode: normal moderet = ioctl(fd, cmd, &arg);if(ret < 0)printf("ERR: BMA220_SET_MODE, ret = %d\n", ret);usleep(100000);cmd = BMA220_GET_MODE; ret = ioctl(fd, cmd, &arg);if(ret < 0)printf("ERR: BMA220_GET_MODE, ret = %d\n", ret);printf("Mode: %ld\n", arg);usleep(100000);cmd = BMA220_SET_RANGE; arg = 0;//Set range: param range 0=2g, 1=4g, 2=8g, 3=16gret = ioctl(fd, cmd, &arg);if(ret < 0)printf("ERR: BMA220_SET_MODE, ret = %d\n", ret);usleep(100000);cmd = BMA220_GET_RANGE; ret = ioctl(fd, cmd, &arg);if(ret < 0)printf("ERR: BMA220_GET_MODE, ret = %d\n", ret);printf("Range: %ld\n", arg);usleep(100000);cmd = BMA220_SET_SUSPEND; arg = 1; //Must be 1, otherwise the third step will be to read the same dataret = ioctl(fd, cmd, &arg);if(ret < 0)printf("ERR: BMA220_SET_SUSPEND, ret = %d\n", ret);printf("Suspend: %ld\n", arg);usleep(100000);/* 3. read x, y, z by read function*/while (1) {ret = read(fd, data, 3);if(ret < 0)printf("ERR: read x/y/z, ret = %d\n", ret);printf("x = %d, y = %d, z = %d.\n", data[0], data[1], data[2]);usleep(500000);}/* 4. close device driver */close(fd);printf("---------------------------------------------------------\n");printf("--- This is the end of the test program of my driver! ---\n");printf("---------------------------------------------------------\n");return 0;}