android Sensor 驱动编写--opt3001光感驱动为例
分析Android sensor
Android sensor Framework 層以及APP如何讀取sensor 數據。網上有很多文章不再累述。
由于我使用的是Android 5.1(kernel 3.10) ,不自帶 opt3001的驅動,我發現kernel 4.10 已經自帶opt3001的驅動。我不會為了一個驅動而換掉 kernel。而且 kernel 4.10 opt3001的驅動高度依賴與iio。兩個kernel 版本的iio修改較大。移植也不是簡單的復制。索性不看 kernel 4.10 上的代碼。我們分析 Android 5.1(kernel 3.10)的代碼。
其實寫一個驅動讀取 opt3001 中的數據并不難。看一看opt3001 的數據手冊,就幾個寄存器。配置也很簡單。
然后我們看一下Android 是如何從kernel 中讀取 光感數據的。自然是去看 HAL層的代碼。我可以在hardware目錄下找到LightSensor.cpp 。一般我們拿到的源碼 HAL層到Framework,數據通路都是好的。不需要修改。
我們分析一下 LightSensor.cpp 我們可以看到class LightSensor 繼承自 SamsungSensorBase 。SamsungSensorBase 又繼承自 SensorBase。分析這幾個類。并沒有什么難的。
我們先看 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; }這段代碼很簡單了。可以看出:
這里要用到 dev_name 和 data_name。
dev_name 用open()函數打開,這個最熟悉不過了。
data_name 從代碼來看 是一個input 設備,從 getInput() 方法中很清楚的看到,程序要在 /dev/input目錄下打開所有input 設備, 查找一個name 匹配 data_name的設備。
我們看一下 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()這個方法 顯然是在讀input 設備上報的數據。充這個方法我們可以看到 input event 的 type 必須是 EV_ABS ,還有 code 必須是 mSensorCode ,mSensorCode 這個參數很顯然 需要具體的實現類。
在 SamsungSensorBase 的構造函數中,可以看到 mSensorCode。
讀取到 event 之后,把event 交給 handleEvent。handleEvent(),也由具體實現類來處理。
是時候去看一下 LightSensor 了
從這里我們就看到了:
dev_name 為 /dev/lightsensor
data_name 為 lightsensor-level
mSensorCode 為 ABS_MISC
看 handleEvent(); 就知道,最終數據被填充到了 mPendingEvent.light。
整個過程就非常清晰了。
另外看一眼 handleEnable 就知道了 dev_name 用 ioctl(dev_fd, LIGHTSENSOR_IOCTL_ENABLE, &flags); 來控制使能。
驅動要求
知道這些,寫驅動 就非常簡單了。
我們需要寫一個 miscdevice :
- name 為 lightsensor
- 支持ioctl ,LIGHTSENSOR_IOCTL_ENABLE 控制使能
我們要寫一個input 設備:
- name 為 lightsensor-level
- 事件類型 EV_ABS
- code 為 ABS_MISC
驅動編寫
分析到這里,寫驅動 就太簡單了。列出幾個代碼片段
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; } //注冊 input設備 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 光感強度 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;} //上傳光強度 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); }總結
以上是生活随笔為你收集整理的android Sensor 驱动编写--opt3001光感驱动为例的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Python?Python!(pytho
- 下一篇: 华为P20 Pro拆机图和BOM(主要核