android Sensor 驱动编写--opt3001光感驱动为例
分析Android sensor
Android sensor Framework 層以及APP如何讀取sensor 數(shù)據(jù)。網(wǎng)上有很多文章不再累述。
由于我使用的是Android 5.1(kernel 3.10) ,不自帶 opt3001的驅(qū)動(dòng),我發(fā)現(xiàn)kernel 4.10 已經(jīng)自帶opt3001的驅(qū)動(dòng)。我不會(huì)為了一個(gè)驅(qū)動(dòng)而換掉 kernel。而且 kernel 4.10 opt3001的驅(qū)動(dòng)高度依賴與iio。兩個(gè)kernel 版本的iio修改較大。移植也不是簡(jiǎn)單的復(fù)制。索性不看 kernel 4.10 上的代碼。我們分析 Android 5.1(kernel 3.10)的代碼。
其實(shí)寫一個(gè)驅(qū)動(dòng)讀取 opt3001 中的數(shù)據(jù)并不難。看一看opt3001 的數(shù)據(jù)手冊(cè),就幾個(gè)寄存器。配置也很簡(jiǎn)單。
然后我們看一下Android 是如何從kernel 中讀取 光感數(shù)據(jù)的。自然是去看 HAL層的代碼。我可以在hardware目錄下找到LightSensor.cpp 。一般我們拿到的源碼 HAL層到Framework,數(shù)據(jù)通路都是好的。不需要修改。
我們分析一下 LightSensor.cpp 我們可以看到class LightSensor 繼承自 SamsungSensorBase 。SamsungSensorBase 又繼承自 SensorBase。分析這幾個(gè)類。并沒有什么難的。
我們先看 SensorBase。
SensorBase::SensorBase(const char* dev_name,const char* data_name): dev_name(dev_name), data_name(data_name),dev_fd(-1), data_fd(-1) {if (data_name) {data_fd = openInput(data_name);} } int SensorBase::open_device() {if (dev_fd<0 && dev_name) {dev_fd = open(dev_name, O_RDONLY);LOGE_IF(dev_fd<0, "Couldn't open %s (%s)", dev_name, strerror(errno));}return 0; } static int getInput(const char *inputName) {int fd = -1;unsigned i; static bool first = true;static struct input_dev dev[255];if (first) { int fd = -1;const char *dirname = "/dev/input";char devname[PATH_MAX];char *filename;DIR *dir;struct dirent *de;first = false;for (i = 0; i < sizeof(dev)/sizeof(dev[0]); i++) {dev[i].fd = -1;dev[i].name[0] = '\0';} i = 0;dir = opendir(dirname);if (dir == NULL)return -1;strcpy(devname, dirname);filename = devname + strlen(devname);*filename++ = '/';while ((de = readdir(dir))) {if (de->d_name[0] == '.' &&(de->d_name[1] == '\0' || (de->d_name[1] == '.' && de->d_name[2] == '\0')))continue;strcpy(filename, de->d_name);fd = open(devname, O_RDONLY);if (fd >= 0) {char name[80];if (ioctl(fd, EVIOCGNAME(sizeof(name) - 1), &name) >= 1) {dev[i].fd = fd;strncpy(dev[i].name, name, sizeof(dev[i].name));}}i++;}closedir(dir);}for (i = 0; i < sizeof(dev)/sizeof(dev[0]); i++) {if (!strncmp(inputName, dev[i].name, sizeof(dev[i].name))) {fd = dev[i].fd;break;}}LOGE_IF(fd < 0, "couldn't find '%s' input device", inputName);return fd; } int SensorBase::openInput(const char* inputName) {int fd = -1;const char *dirname = "/dev/input";char devname[PATH_MAX];char *filename;DIR *dir;struct dirent *de;return getInput(inputName);dir = opendir(dirname);if(dir == NULL)return -1;strcpy(devname, dirname);filename = devname + strlen(devname);*filename++ = '/';while((de = readdir(dir))) {if(de->d_name[0] == '.' &&(de->d_name[1] == '\0' ||(de->d_name[1] == '.' && de->d_name[2] == '\0')))continue;strcpy(filename, de->d_name);fd = open(devname, O_RDONLY);if (fd>=0) {char name[80];if (ioctl(fd, EVIOCGNAME(sizeof(name) - 1), &name) < 1) {name[0] = '\0';}if (!strcmp(name, inputName)) {strcpy(input_name, filename);break;} else {close(fd);fd = -1;}}}closedir(dir);LOGE_IF(fd<0, "couldn't find '%s' input device", inputName);return fd; }這段代碼很簡(jiǎn)單了。可以看出:
這里要用到 dev_name 和 data_name。
dev_name 用open()函數(shù)打開,這個(gè)最熟悉不過了。
data_name 從代碼來看 是一個(gè)input 設(shè)備,從 getInput() 方法中很清楚的看到,程序要在 /dev/input目錄下打開所有input 設(shè)備, 查找一個(gè)name 匹配 data_name的設(shè)備。
我們看一下 SamsungSensorBase
SamsungSensorBase::SamsungSensorBase(const char *dev_name,const char *data_name,int sensor_code): SensorBase(dev_name, data_name),mEnabled(false), //cvt_zxl modifymHasPendingEvent(false),mInputReader(4),mSensorCode(sensor_code),mLock(PTHREAD_MUTEX_INITIALIZER) {mPendingEvent.version = sizeof(sensors_event_t);memset(mPendingEvent.data, 0, sizeof(mPendingEvent.data));if (!data_fd)return;mInputSysfsEnable = makeSysfsName(input_name, "enable");if (!mInputSysfsEnable) {ALOGE("%s: unable to allocate mem for %s:enable", __func__,data_name);return;}mInputSysfsPollDelay = makeSysfsName(input_name, "poll_delay");if (!mInputSysfsPollDelay) {ALOGE("%s: unable to allocate mem for %s:poll_delay", __func__,data_name);return;}int flags = fcntl(data_fd, F_GETFL, 0);fcntl(data_fd, F_SETFL, flags | O_NONBLOCK); }int SamsungSensorBase::readEvents(sensors_event_t* data, int count) {if (count < 1)return -EINVAL;pthread_mutex_lock(&mLock);int numEventReceived = 0;if (mHasPendingEvent) {mHasPendingEvent = false;if (mEnabled) {mPendingEvent.timestamp = getTimestamp();*data = mPendingEvent;numEventReceived++;}goto done;}input_event const* event;while (count && mInputReader.readEvent(data_fd, &event)) {if (event->type == EV_ABS) {if (event->code == mSensorCode) {if (mEnabled && handleEvent(event)) {mPendingEvent.timestamp = timevalToNano(event->time);*data++ = mPendingEvent;count--;numEventReceived++;}}}mInputReader.next();}done:pthread_mutex_unlock(&mLock);return numEventReceived;}readEvents()這個(gè)方法 顯然是在讀input 設(shè)備上報(bào)的數(shù)據(jù)。充這個(gè)方法我們可以看到 input event 的 type 必須是 EV_ABS ,還有 code 必須是 mSensorCode ,mSensorCode 這個(gè)參數(shù)很顯然 需要具體的實(shí)現(xiàn)類。
在 SamsungSensorBase 的構(gòu)造函數(shù)中,可以看到 mSensorCode。
讀取到 event 之后,把event 交給 handleEvent。handleEvent(),也由具體實(shí)現(xiàn)類來處理。
是時(shí)候去看一下 LightSensor 了
從這里我們就看到了:
dev_name 為 /dev/lightsensor
data_name 為 lightsensor-level
mSensorCode 為 ABS_MISC
看 handleEvent(); 就知道,最終數(shù)據(jù)被填充到了 mPendingEvent.light。
整個(gè)過程就非常清晰了。
另外看一眼 handleEnable 就知道了 dev_name 用 ioctl(dev_fd, LIGHTSENSOR_IOCTL_ENABLE, &flags); 來控制使能。
驅(qū)動(dòng)要求
知道這些,寫驅(qū)動(dòng) 就非常簡(jiǎn)單了。
我們需要寫一個(gè) miscdevice :
- name 為 lightsensor
- 支持ioctl ,LIGHTSENSOR_IOCTL_ENABLE 控制使能
我們要寫一個(gè)input 設(shè)備:
- name 為 lightsensor-level
- 事件類型 EV_ABS
- code 為 ABS_MISC
驅(qū)動(dòng)編寫
分析到這里,寫驅(qū)動(dòng) 就太簡(jiǎn)單了。列出幾個(gè)代碼片段
static struct miscdevice opt3001_lsensor_misc = {.minor = MISC_DYNAMIC_MINOR,.name = "lightsensor",.fops = &opt3001_lsensor_fops };//讀芯片id static int opt3001_read_id(struct i2c_client *client) {char manufacturer[2];u16 device_id;int ret;ret = i2c_smbus_read_word_swapped(client, OPT3001_MANUFACTURER_ID);if (ret < 0) {printk("failed to read register %02x\n",OPT3001_MANUFACTURER_ID);return ret;}manufacturer[0] = ret >> 8;manufacturer[1] = ret & 0xff;ret = i2c_smbus_read_word_swapped(client, OPT3001_DEVICE_ID);if (ret < 0) {printk("failed to read register %02x\n",OPT3001_DEVICE_ID);return ret;}device_id = ret;printk("Found %c%c OPT%04x\n", manufacturer[0],manufacturer[1], device_id);return 0;} //配置 opt3001 static int opt3001_configure(struct i2c_client *client) {int ret;ret = i2c_smbus_write_word_swapped(client, OPT3001_CONFIGURATION,OPT3001_CONFIGURATION_VAL);if (ret < 0) {printk("failed to write register %02x\n",OPT3001_CONFIGURATION);return ret;}return 0; } //注冊(cè) input設(shè)備 static int init_input_dev(void) {int ret;input_dev = input_allocate_device();if(!input_dev){printk(KERN_ERR"%s: could not allocate input device for lsensor\n", __FUNCTION__);return -ENOMEM;}input_dev->name = "lightsensor-level";set_bit(EV_ABS, input_dev->evbit);input_set_abs_params(input_dev, ABS_MISC, 0, 83865600, 0, 0);ret = input_register_device(input_dev);if(ret < 0) {printk(KERN_ERR"%s: could not register input device for lsensor\n", __FUNCTION__);return ret;}return 0;} //讀取 opt3001 光感強(qiáng)度 static int opt3001_get_lux(struct i2c_client *client) {int ret;u16 mantissa;u8 exponent;u32 value;int lux;ret = i2c_smbus_read_word_swapped(client, OPT3001_RESULT);if (ret < 0) {printk("failed to read register %02x\n",OPT3001_RESULT);return -1;}value = ret;exponent = OPT3001_REG_EXPONENT(value);mantissa = OPT3001_REG_MANTISSA(value);lux = 10 * (mantissa << exponent);return lux;} //上傳光強(qiáng)度 void opt3001_handler(struct work_struct *work) {struct i2c_client * client = g_client;int val;if(g_enable) {val = opt3001_get_lux(client);input_report_abs(input_dev,ABS_MISC,val);input_sync(input_dev);}schedule_delayed_work(&getwork,100); }總結(jié)
以上是生活随笔為你收集整理的android Sensor 驱动编写--opt3001光感驱动为例的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Python?Python!(pytho
- 下一篇: Android 酷我音乐API抓取