Android Sensor詳解(4)driver的攻防戰

kernel drvier架構

下面我將以psensor,light sensor爲例具體說明整個sensor的driver是如何啓動的

註冊驅動

很多人在看driver的時候首先會去注意probe函數,僅知道這個是入口函數,其他的不在管了。

當然在我開發過程中也確實如此,因爲整個框架都是固定好的,一般是不會改變的。

但身爲一個rd就該知道整體的架構是什麼,不能讓人給問倒了。於是可以看下面的code

static const struct i2c_device_id cm36xxx_i2c_id[] = {
	{CM36xxx_I2C_NAME, 0},
	{}
};
#ifdef CONFIG_OF
static struct of_device_id cm36xxx_match_table[] = {
	{ .compatible = "capella,cm36xxx",},
	{ },
};
#else
#define cm36xxx_match_table NULL
#endif

static struct i2c_driver cm36xxx_driver = {
	.id_table = cm36xxx_i2c_id,
	.probe = cm36xxx_probe,
	.driver = {
		.name = CM36xxx_I2C_NAME,
		.owner = THIS_MODULE,
		.pm = &cm36xxx_pm,    
    .of_match_table = of_match_ptr(cm36656_match_table),     
	},
};

static int __init cm36xxxinit(void)
{
	return i2c_add_driver(&cm36xxx_driver);
}

static void __exit cm36xxx_exit(void)
{
	i2c_del_driver(&cm36xxx_driver);
}

看到整個函數包含了**__init__exit**函數,其中就是添加i2c的driver與delete driver。

那麼整個cm36xxx_driver又是如何構造的呢?

它包含了id_table,probe,driver,of_match_table。其中值得你關心的是of_match_table,cm36xxx_match_table這張table直接定義了系統map時的信息,因而在Android Sensor詳解(3)porting drvier
講到compatible要與kernel driver中的cm36xxx_match_table中的compatible一致就是源於此處。

probe函數的家常

衆所周知probr函數是入口函數,那麼它主要的功能簡單,就是初始化整個driver。當然在這裏僅僅介紹driver的一些基本功能,具體不會進行細化。

  • malloc驅動結構題,並獲取i2c的client與中斷。
struct cm36xxx_info *lpi;
lpi = kzalloc(sizeof(struct cm36xxx_info), GFP_KERNEL);
	if (!lpi)
		return -ENOMEM;
	lpi->i2c_client = client;
	lpi->irq = client->irq;
	i2c_set_clientdata(client, lpi);
  • 讀取dtsi進行相關初始化
static int cm36xxx_parse_dt(struct device *dev,
				struct cm36xxx_info *lpi)
{
	struct device_node *np = dev->of_node;
	u32 temp_val;
	int rc;
	rc = of_get_named_gpio_flags(np, "capella,intrpin-gpios",
			0, NULL);//設置int的gpio
	if (rc < 0) 
	{
		dev_err(dev, "Unable to read interrupt pin number\n");
		return rc;
	} 
	else
	{
		lpi->intr_pin = rc;
 	  D("[LS][CM36656]%s GET INTR PIN \n", __func__);   
	}

	rc = of_property_read_u32(np, "capella,slave_address", &temp_val);//設置driver的i2c地址
	if (rc)
	{
		dev_err(dev, "Unable to read slave_address\n");
		return rc;
	} 
	else
	{
		lpi->slave_addr = (uint8_t)temp_val;
	}
  
	D("[PS][CM36xxx]%s PARSE OK \n", __func__);

	return 0;
}
if( cm36xxx_parse_dt(&client->dev, lpi) < 0 )
	{
		ret = -EBUSY;
		goto err_platform_data_null;  
	}
  • 初始化電
    這個就要自己寫regulator了,當然在這裏不介紹了,後續會對regulator做一些解釋(屆時會在這裏做一個鏈接)

  • 上各種鎖
    要知道你在操作底層的driver時,特別ic可能就需要時序,突然有人打斷,那麼整個driver就是一場災難性的bug

  • 各種初始化操作
    這部分請自行參照ic的spec進行動作,特別注意一些setup函數的先後順序

  • 創建input樹與misc樹

static int psensor_setup(struct cm36xxx_info *lpi)
{
	int ret;

	lpi->ps_input_dev = input_allocate_device();
	if (!lpi->ps_input_dev) {
		pr_err(
			"[PS][CM36xxx error]%s: could not allocate ps input device\n",
			__func__);
		return -ENOMEM;
	}
	lpi->ps_input_dev->name = "cm36xxx-ps";
	set_bit(EV_ABS, lpi->ps_input_dev->evbit);
	input_set_abs_params(lpi->ps_input_dev, ABS_DISTANCE, 0, 1, 0, 0);

	ret = input_register_device(lpi->ps_input_dev);
	if (ret < 0) {
		pr_err(
			"[PS][CM36xxx error]%s: could not register ps input device\n",
			__func__);
		goto err_free_ps_input_device;
	}

	ret = misc_register(&psensor_misc);
	if (ret < 0) {
		pr_err(
			"[PS][CM36xxx error]%s: could not register ps misc device\n",
			__func__);
		goto err_unregister_ps_input_device;
	}

	return ret;

err_unregister_ps_input_device:
	input_unregister_device(lpi->ps_input_dev);
	return ret;
err_free_ps_input_device:
	input_free_device(lpi->ps_input_dev);
	return ret;
}

整個系統是如何獲取sensor的數據的?明確的說其實在我們的眼中有2種模式,一個是通過input子系統,向上層發送event,另外一種是上層向driver寫io,driver再將data回覆。
input_register_device函數將幫助driver註冊倒input子系統中去。此時你可以到input相關節點去找到節點了。最爲重要的是,你真的建好了這個節點嗎?

adb shell getevent

直接就能知道你的driver註冊上沒,當底層adc值發生變化時,是否有數值在event上。

misc_register函數將幫忙driver建立起一整套misc文件樹,只要包含open enable disable relase ioctol,當然這個會在後續的博客中說明。

static const struct file_operations psensor_fops = {
	.owner = THIS_MODULE,
	.open = psensor_open,
	.release = psensor_release,
	.unlocked_ioctl = psensor_ioctl
};
  • 創建class文件樹
ret = cm36xxx_setup(lpi);
	if (ret < 0) {
		pr_err("[PS_ERR][CM36xxx error]%s: cm36xxx_setup error!\n", __func__);
		goto err_cm36xxx_setup;
	}
	lpi->cm36xxx_class = class_create(THIS_MODULE, "capella_sensors");
	if (IS_ERR(lpi->cm36xxx_class)) {
		ret = PTR_ERR(lpi->cm36xxx_class);
		lpi->cm36xxx_class = NULL;
		goto err_create_class;
	}

	lpi->ls_dev = device_create(lpi->cm36xxx_class,
				NULL, 0, "%s", "lightsensor");
	if (unlikely(IS_ERR(lpi->ls_dev))) {
		ret = PTR_ERR(lpi->ls_dev);
		lpi->ls_dev = NULL;
		goto err_create_ls_device;
	}
	lpi->ps_dev = device_create(lpi->cm36xxx_class,
				NULL, 0, "%s", "proximity");
	if (unlikely(IS_ERR(lpi->ps_dev))) {
		ret = PTR_ERR(lpi->ps_dev);
		lpi->ps_dev = NULL;
		goto err_create_ps_device;
	}

當然,在完成了setup之後,需要在建立sysclass樹,這將方便我們進行debug。
class_create(THIS_MODULE, “capella_sensors”)函數將建立在sys/class/capella_sensor
而device_create
將在上述目錄下分別建立起自己的節點。這些函數的用法可以在百度上都找的到

  • 啓動workqueue處理事件
lpi->lp_wq = create_singlethread_workqueue("cm36656_wq");
	if (!lpi->lp_wq) {
		pr_err("[PS][CM36656 error]%s: can't create workqueue\n", __func__);
		ret = -ENOMEM;
		goto err_create_singlethread_workqueue;
	}
  • 強制出錯處理

這個是你在寫driver最需要關注的一點。當你寫driver的時候一定要確保哪一步出了錯,在出錯處理時絕對不會影響到系統。不會使系統hang up。

驅動完善

此時你該做的是將所有的需求進行整理,然後進行各式各樣的修改,當請注意,sensor上報數據只有16個data。

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