安卓9.0馬達框架分析
關鍵詞:mt8167s;Android 9.0
前言
最近需要將之前的一些驅動接口轉爲安卓標準接口,方便上層應用或者第三方應用去適配。這篇文章先從簡單的馬達框架入手進行講解。
正文
整個馬達框架比較簡單,安卓官方已經幫我們實現了framework到HAL層,我們需要實現的就只有驅動層。這篇文章我們梳理一下從上層到底層怎麼流程。
1、APP層
import android.os.Vibrator;
import android.widget.ToggleButton;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
private Vibrator vibrator=null;
vibrator=(Vibrator)this.getSystemService(VIBRATOR_SERVICE);
toggleButton1=(ToggleButton)findViewById(R.id.toggleButton1);
/*短震動*/
toggleButton1.setOnCheckedChangeListener(new ToggleButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if(isChecked){
Log.i(TAG,"toggleButton1 enter vibrator.vibrate");
//設置震動週期,第二個參數爲 -1表示只震動一次
vibrator.vibrate(new long[]{1000, 10, 100, 1000},-1);
}else{
//取消震動
Log.i(TAG,"toggleButton1 enter vibrator.cancel()");
vibrator.cancel();
}
}
});
}
}
上面展示了一個最簡單的馬達震動應用代碼,獲得服務後即可調用接口進行驅動。
2、framework層
代碼路徑:frameworks\base\services\core\java\com\android\server\VibratorService.java
@Override // Binder call
public void vibrate(int uid, String opPkg, VibrationEffect effect, int usageHint,
IBinder token) {
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "vibrate");
try {
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.VIBRATE)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Requires VIBRATE permission");
}
if (token == null) {
Slog.e(TAG, "token must not be null");
return;
}
verifyIncomingUid(uid);
if (!verifyVibrationEffect(effect)) {
return;
}
// If our current vibration is longer than the new vibration and is the same amplitude,
// then just let the current one finish.
synchronized (mLock) {
if (effect instanceof VibrationEffect.OneShot
&& mCurrentVibration != null
&& mCurrentVibration.effect instanceof VibrationEffect.OneShot) {
VibrationEffect.OneShot newOneShot = (VibrationEffect.OneShot) effect;
VibrationEffect.OneShot currentOneShot =
(VibrationEffect.OneShot) mCurrentVibration.effect;
if (mCurrentVibration.hasTimeoutLongerThan(newOneShot.getDuration())
&& newOneShot.getAmplitude() == currentOneShot.getAmplitude()) {
if (DEBUG) {
Slog.d(TAG,
"Ignoring incoming vibration in favor of current vibration");
}
return;
}
}
// If the current vibration is repeating and the incoming one is non-repeating,
// then ignore the non-repeating vibration. This is so that we don't cancel
// vibrations that are meant to grab the attention of the user, like ringtones and
// alarms, in favor of one-shot vibrations that are likely quite short.
if (!isRepeatingVibration(effect)
&& mCurrentVibration != null
&& isRepeatingVibration(mCurrentVibration.effect)) {
if (DEBUG) {
Slog.d(TAG, "Ignoring incoming vibration in favor of alarm vibration");
}
return;
}
Vibration vib = new Vibration(token, effect, usageHint, uid, opPkg);
linkVibration(vib);
long ident = Binder.clearCallingIdentity();
try {
doCancelVibrateLocked();
startVibrationLocked(vib);
addToPreviousVibrationsLocked(vib);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
}
}
接口裏面會判斷一下權限,根據應用層傳遞的不同effect值,有不同的震動效果。然後就調用到JNI層,調用順序大概如下:
startVibrationLocked
startVibrationInnerLocked
doVibratorOn
vibratorOn
3、JNI層
代碼路徑:frameworks\base\services\core\jni\com_android_server_VibratorService.cpp
static void vibratorOn(JNIEnv* /* env */, jobject /* clazz */, jlong timeout_ms)
{
Status retStatus = halCall(&V1_0::IVibrator::on, timeout_ms).withDefault(Status::UNKNOWN_ERROR);
if (retStatus != Status::OK) {
ALOGE("vibratorOn command failed (%" PRIu32 ").", static_cast<uint32_t>(retStatus));
}
}
static void vibratorOff(JNIEnv* /* env */, jobject /* clazz */)
{
Status retStatus = halCall(&V1_0::IVibrator::off).withDefault(Status::UNKNOWN_ERROR);
if (retStatus != Status::OK) {
ALOGE("vibratorOff command failed (%" PRIu32 ").", static_cast<uint32_t>(retStatus));
}
}
static const JNINativeMethod method_table[] = {
{ "vibratorExists", "()Z", (void*)vibratorExists },
{ "vibratorInit", "()V", (void*)vibratorInit },
{ "vibratorOn", "(J)V", (void*)vibratorOn },
{ "vibratorOff", "()V", (void*)vibratorOff },
{ "vibratorSupportsAmplitudeControl", "()Z", (void*)vibratorSupportsAmplitudeControl},
{ "vibratorSetAmplitude", "(I)V", (void*)vibratorSetAmplitude},
{ "vibratorPerformEffect", "(JJ)J", (void*)vibratorPerformEffect}
};
int register_android_server_VibratorService(JNIEnv *env)
{
return jniRegisterNativeMethods(env, "com/android/server/VibratorService",
method_table, NELEM(method_table));
}
以馬達的On和off爲例,會調用到HAL層的on和off方法。
4、HIDL層
代碼路徑:hardware\interfaces\vibrator\1.0\default\Vibrator.cpp
Return<Status> Vibrator::on(uint32_t timeout_ms) {
int32_t ret = mDevice->vibrator_on(mDevice, timeout_ms);
if (ret != 0) {
ALOGE("on command failed : %s", strerror(-ret));
return Status::UNKNOWN_ERROR;
}
return Status::OK;
}
Return<Status> Vibrator::off() {
int32_t ret = mDevice->vibrator_off(mDevice);
if (ret != 0) {
ALOGE("off command failed : %s", strerror(-ret));
return Status::UNKNOWN_ERROR;
}
return Status::OK;
}
HIDL層是較新的安卓版本才引入的,是連接HAL層和JNI層的橋樑。
5、HAL層
代碼路徑:hardware\libhardware\modules\vibrator\vibrator.c
static const char THE_DEVICE[] = "/sys/class/timed_output/vibrator/enable";
static int sendit(unsigned int timeout_ms)
{
char value[TIMEOUT_STR_LEN]; /* large enough for millions of years */
snprintf(value, sizeof(value), "%u", timeout_ms);
return write_value(THE_DEVICE, value);
}
static int vibra_on(vibrator_device_t* vibradev __unused, unsigned int timeout_ms)
{
/* constant on, up to maximum allowed time */
return sendit(timeout_ms);
}
static int vibra_off(vibrator_device_t* vibradev __unused)
{
return sendit(0);
}
static int vibra_open(const hw_module_t* module, const char* id __unused,
hw_device_t** device __unused) {
bool use_led;
if (vibra_exists()) {
ALOGD("Vibrator using timed_output");
use_led = false;
} else if (vibra_led_exists()) {
ALOGD("Vibrator using LED trigger");
use_led = true;
} else {
ALOGE("Vibrator device does not exist. Cannot start vibrator");
return -ENODEV;
}
vibrator_device_t *vibradev = calloc(1, sizeof(vibrator_device_t));
if (!vibradev) {
ALOGE("Can not allocate memory for the vibrator device");
return -ENOMEM;
}
vibradev->common.tag = HARDWARE_DEVICE_TAG;
vibradev->common.module = (hw_module_t *) module;
vibradev->common.version = HARDWARE_DEVICE_API_VERSION(1,0);
vibradev->common.close = vibra_close;
if (use_led) {
vibradev->vibrator_on = vibra_led_on;
vibradev->vibrator_off = vibra_led_off;
} else {
vibradev->vibrator_on = vibra_on;
vibradev->vibrator_off = vibra_off;
}
*device = (hw_device_t *) vibradev;
return 0;
}
其實開啓和關閉馬達的工作很簡單,就是往節點"/sys/class/timed_output/vibrator/enable"寫入震動時間,所以可以想得到驅動層只需要提供一個節點供上層操作就好。
6、驅動層
馬達的驅動是基於kernel提供的timed_output框架完成的:
代碼路徑:kernel-4.4\drivers\staging\android\timed_output.c
代碼比較簡單,提供接口給驅動在"/sys/class/timed_output/"路徑下面建立自己的節點,並提供節點的device attribute的操作接口,當我們寫節點的時候就會調用到enable_store函數,並調用註冊驅動的enable函數。
static struct class *timed_output_class;
static ssize_t enable_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t size)
{
struct timed_output_dev *tdev = dev_get_drvdata(dev);
int value;
int rc;
rc = kstrtoint(buf, 0, &value);
if (rc != 0)
return -EINVAL;
tdev->enable(tdev, value);
return size;
}
static DEVICE_ATTR_RW(enable);
static struct attribute *timed_output_attrs[] = {
&dev_attr_enable.attr,
NULL,
};
ATTRIBUTE_GROUPS(timed_output);
static int create_timed_output_class(void)
{
if (!timed_output_class) {
timed_output_class = class_create(THIS_MODULE, "timed_output");
if (IS_ERR(timed_output_class))
return PTR_ERR(timed_output_class);
atomic_set(&device_count, 0);
timed_output_class->dev_groups = timed_output_groups;
}
return 0;
}
int timed_output_dev_register(struct timed_output_dev *tdev)
{
int ret;
if (!tdev || !tdev->name || !tdev->enable || !tdev->get_time)
return -EINVAL;
ret = create_timed_output_class();
if (ret < 0)
return ret;
tdev->index = atomic_inc_return(&device_count);
tdev->dev = device_create(timed_output_class, NULL,
MKDEV(0, tdev->index), NULL, "%s", tdev->name);
if (IS_ERR(tdev->dev))
return PTR_ERR(tdev->dev);
dev_set_drvdata(tdev->dev, tdev);
tdev->state = 0;
return 0;
}
現在我們看一下基於上面框架書寫的馬達驅動:
static void vibrator_off(void)
{
gpio_direction_output(gpio, !en_value);
wake_unlock(&vibdata.wklock); //震動關閉就可以釋放 wake_lock鎖
}
void motor_enable(struct timed_output_dev *sdev,int value)
{
mutex_lock(&vibdata.lock); //關鍵代碼段,同一時間只允許一個線程執行
/* cancelprevious timer and set GPIO according to value */
hrtimer_cancel(&vibdata.timer); //當先前定時器完成後 關閉這個定時器
cancel_work_sync(&vibdata.work); //當上次震動完成後 關閉這次動作
if(value)
{
wake_lock(&vibdata.wklock); //開始震動打開wake lock鎖不允許休眠
gpio_direction_output(gpio, en_value);
if(value > 0)
{
if(value > MAX_TIMEOUT)
value= MAX_TIMEOUT;
hrtimer_start(&vibdata.timer,
ktime_set(value / 1000, (value % 1000) * 1000000),
HRTIMER_MODE_REL);
}
}
else
vibrator_off();
mutex_unlock(&vibdata.lock);
}
struct timed_output_dev motot_driver = {
.name ="vibrator", //注意這個名字,由於HAL層裏面的設備爲//"/sys/class/timed_output/vibrator/enable"
//因此這個名字必須爲"vibrator"
.enable= motor_enable,
.get_time= get_time,
};
static enum hrtimer_restart vibrator_timer_func(struct hrtimer *timer) //定時器結束時候的回調函數
{
schedule_work(&vibdata.work); //定時器完成了 執行work隊列回調函數來關閉電機
return HRTIMER_NORESTART;
}
static void vibrator_work(struct work_struct *work)
{
vibrator_off();
}
static int motor_probe(struct platform_device *pdev)
{
struct device_node *node = pdev->dev.of_node;
enum of_gpio_flags flags;
int ret =0;
hrtimer_init(&vibdata.timer,CLOCK_MONOTONIC, HRTIMER_MODE_REL);
vibdata.timer.function= vibrator_timer_func;
INIT_WORK(&vibdata.work,vibrator_work);
...
ret=timed_output_dev_register(&motot_driver);
if (ret< 0)
goto err_to_dev_reg;
return 0;
err_to_dev_reg:
mutex_destroy(&vibdata.lock);
wake_lock_destroy(&vibdata.wklock);
printk("vibrator err!:%d\n",ret);
return ret;
}
1、
驅動接收上層傳遞過來的是震動時長,單位爲毫秒。在驅動裏註冊一個定時器,定時器倒計時到期後會喚醒註冊的工作隊列,最終會執行vibrator_work()函數去關閉馬達震動。
2、
調用timed_output框架提供的timed_output_dev_register()接口將我們的馬達驅動註冊進系統,這裏的關鍵就是我們需要自定義struct timed_output_dev結構體,填充enable和get_time函數。enable函數用來開啓馬達的震動:
void motor_enable(struct timed_output_dev *sdev,int value)
{
mutex_lock(&vibdata.lock); //關鍵代碼段,同一時間只允許一個線程執行
/* cancelprevious timer and set GPIO according to value */
hrtimer_cancel(&vibdata.timer); //當先前定時器完成後 關閉這個定時器
cancel_work_sync(&vibdata.work); //當上次震動完成後 關閉這次動作
if(value)
{
wake_lock(&vibdata.wklock); //開始震動打開wake lock鎖不允許休眠
gpio_direction_output(gpio, en_value);
if(value > 0)
{
if(value > MAX_TIMEOUT)
value= MAX_TIMEOUT;
hrtimer_start(&vibdata.timer,
ktime_set(value / 1000, (value % 1000) * 1000000),
HRTIMER_MODE_REL);
}
}
else
vibrator_off();
mutex_unlock(&vibdata.lock);
}
開啓震動的操作也很簡單,只是寫一下GPIO,然後重新開啓定時器,倒計時的時間就是寫入節點的值,到時間再把馬達關閉就好。
參考鏈接
https://blog.csdn.net/qq_34211365/article/details/105556842