1> 设备树信息
/home/rd047/amlogic_code_one/1000_311D2_driverinterface/release/common/ebsw_skg/ebsw_skg/dts/t7_a311d2_an400.dts
adc_keypad {
compatible = "amlogic, adc_keypad";
status = "okay";
key_name = "update", "vol-", "vol+", "enter";
key_num = <4>;
io-channels = <&saradc 2>;
io-channel-names = "key-chan-2";
key_chan = <2 2 2 2>;
key_code = <141 114 115 28>;
key_val = <0 143 266 389>; //val=voltage/1800mV*1023
key_tolerance = <40 40 40 40>;
};
该节点是 \节点下的子节点
2> 驱动代码
通过 compatible = “amlogic, adc_keypad”; 寻找相应驱动文件
rd047@syrjb:~/amlogic_code_one/1000_311D2_driverinterface/release/common/drivers$ grep -rn “amlogic, adc_keypad” ./
./amlogic/input/keyboard/adc_keypad.c:607: {.compatible = “amlogic, adc_keypad”,},
驱动文件:
/home/rd047/amlogic_code_one/1000_311D2_driverinterface/release/common/drivers/amlogic/input/keyboard/adc_keypad.c
2.1) 加载/卸载函数
late_initcall(meson_adc_kp_init); //wjc_note 驱动模块加载函数
module_exit(meson_adc_kp_exit); //wjc_note 驱动模块卸载函数
MODULE_AUTHOR("Amlogic");
MODULE_DESCRIPTION("ADC Keypad Driver");
MODULE_LICENSE("GPL");
late_initcall 和 module_init 函数的区别,这里有个知识点,就是驱动模块的初始化加载函数是有区别的,不同宏对应的加载顺序不同。
#define module_init(x) __initcall(x);
#define __initcall(fn) device_initcall(fn)
#define pure_initcall(fn) __define_initcall("0",fn,1)
#define core_initcall(fn) __define_initcall("1",fn,1)
#define core_initcall_sync(fn) __define_initcall("1s",fn,1s)
#define postcore_initcall(fn) __define_initcall("2",fn,2)
#define postcore_initcall_sync(fn) __define_initcall("2s",fn,2s)
#define arch_initcall(fn) __define_initcall("3",fn,3)
#define arch_initcall_sync(fn) __define_initcall("3s",fn,3s)
#define subsys_initcall(fn) __define_initcall("4",fn,4)
#define subsys_initcall_sync(fn) __define_initcall("4s",fn,4s)
#define fs_initcall(fn) __define_initcall("5",fn,5)
#define fs_initcall_sync(fn) __define_initcall("5s",fn,5s)
#define rootfs_initcall(fn) __define_initcall("rootfs",fn,rootfs)
#define device_initcall(fn) __define_initcall("6",fn,6)
#define device_initcall_sync(fn) __define_initcall("6s",fn,6s)
#define late_initcall(fn) __define_initcall("7",fn,7)
#define late_initcall_sync(fn) __define_initcall("7s",fn,7s)
所以module_init定义的驱动初始化会比late_initcall定义的驱动初始化先被执行,怎么做的好处是,有些驱动的加载是有顺序要求的,有依赖关系的。 像该按键板驱动就依赖input子系统和adc采集驱动
2.2) 具体的加载函数 meson_adc_kp_init
static int __init meson_adc_kp_init(void)
{
return platform_driver_register(&kp_driver); //wjc_note 竟然是平台总线的做法
}
2.3) adc_keypad驱动对象的定义 kp_driver
static struct platform_driver kp_driver = {
.probe = meson_adc_kp_probe,
.remove = meson_adc_kp_remove,
.suspend = meson_adc_kp_suspend,
.resume = meson_adc_kp_resume,
.shutdown = meson_adc_kp_shutdown,
.driver = {
.name = DRIVE_NAME,
.of_match_table = key_dt_match,
},
};
2.3.1) 设备树匹配表
static const struct of_device_id key_dt_match[] = {
{.compatible = "amlogic, adc_keypad",},
{},
};
==>所以platform_ddeice和platform_driver会匹配成功去调用 probe函数
2.3.2) probe函数
static int meson_adc_kp_probe(struct platform_device *pdev)
{
struct meson_adc_kp *kp; //wjc_note struct meson_adc_kp 应该是该驱动对应的按键板信息的对象
struct input_dev *input; //wjc_note 猜测当知道哪个按键被按下的时候,会通过input子系统去上报该按键对应的键值
int ret = 0;
send_data_to_bl301(); //wjc_note 什么也不做
kernel_keypad_enable_mode_enable();
kp = kzalloc(sizeof(*kp), GFP_KERNEL); //wjc_note 给adc_keypad驱动申请一个全局信息结构体,用于保存该驱动对象中锁使用的所有信息
if (!kp)
return -ENOMEM;
platform_set_drvdata(pdev, kp); //wjc_note 将按键板驱动信息对象保存在pdev->dev->p->driver_data 后面会用 platform_get_drvdata 将该数据取出来
mutex_init(&kp->kp_lock); //wjc_note 互斥锁
INIT_LIST_HEAD(&kp->adckey_head); //wjc_note 初始化一个链表,后面这个链表保存了该按键板上所以按键的信息,该链表的结点就是一个按键信息对象
kp->report_code = 0; //wjc_note 保存记录当前这次按键按下的键值
kp->prev_code = 0; //wjc_note 上一个按下按键的键值
kp->count = 0; //wjc_note 用于记录当前按键的按键次数,只有连续两次轮询的都是该按键按下才会上报该按键对应的键值
ret = meson_adc_kp_get_devtree_pdata(pdev, kp); //wjc_note 这里是获取设备树中定义的所有信息,并将信息保存在 kp中
if (ret)
goto err;
/*alloc input poll device*/
kp->poll_dev = devm_input_allocate_polled_device(&pdev->dev); //wjc 创建一个轮询检测按键板上按键的按并上报键值的对象,并给它申请内存
if (!kp->poll_dev) {
dev_err(&pdev->dev, "alloc input poll device failed!\n");
ret = -ENOMEM;
goto err;
}
kp->poll_dev->poll = meson_adc_kp_poll; //wjc_note 具体轮询按键板上按键和上报按下按键对应键值的操作函数,最后会分析它是该轮询检测按键版按键任务对应的任务操作函数 非常重要
kp->poll_dev->poll_interval = kp->poll_period; //wjc_note 初始化该轮询检测上报对象的成员
kp->poll_dev->private = kp; //wjc_note 轮询检测上报对象的private成员保存驱动中全局变量信息,相当于继承了kp
input = kp->poll_dev->input; //wjc_note 这不就是input子系统中的input对象吗? 所以验证了,该轮询对象最终会通过input流程上报按键按下对于的键值
set_bit(EV_KEY, input->evbit); //wjc_note input流程,设置按键对象事件和重复按键事件 即设置上报的事件类型
set_bit(EV_REP, input->evbit); //wjc_note 这里是设置上报的是哪些键值
meson_adc_kp_init_keybit(kp);
input->name = "adc_keypad"; //wjc_note 初始化inputdev
input->phys = "adc_keypad/input0";
input->dev.parent = &pdev->dev;
input->id.bustype = BUS_ISA;
input->id.vendor = 0x0001;
input->id.product = 0x0001;
input->id.version = 0x0100;
input->rep[REP_DELAY] = 0xffffffff;
input->rep[REP_PERIOD] = 0xffffffff;
input->keycodesize = sizeof(unsigned short);
input->keycodemax = 0x1ff;
/*init class*/
kp->kp_class.name = DRIVE_NAME;
kp->kp_class.owner = THIS_MODULE;
kp->kp_class.class_groups = meson_adckey_groups;
ret = class_register(&kp->kp_class); //wjc_note 驱动流程中的注册设备类 可通过cat /sys/class/ input文件下面查看相应的class信息
if (ret) {
dev_err(&pdev->dev, "fail to create adc keypad class.\n");
goto err;
}
/*register input poll device*/
ret = input_register_polled_device(kp->poll_dev); //wjc_note 猜测一下这里就是注册轮询检测上报对象驱动,肯定会包含input设备的注册
if (ret) {
dev_err(&pdev->dev,
"unable to register keypad input poll device.\n");
goto err1;
}
return ret;
err1:
class_unregister(&kp->kp_class);
err:
meson_adc_kp_list_free(kp);
kfree(kp);
return ret;
}
2.3.2.1] meson_adc_kp 变量类型
/home/rd047/amlogic_code_one/1000_311D2_driverinterface/release/common/drivers/amlogic/input/keyboard/adc_keypad.h
struct meson_adc_kp {
unsigned char chan[8];
unsigned char chan_num; /*number of channel exclude duplicate*/
unsigned char count;
unsigned int report_code;
unsigned int prev_code;
unsigned int poll_period; /*key scan period*/
struct mutex kp_lock;
struct class kp_class;
struct list_head adckey_head;
struct input_polled_dev *poll_dev;
struct iio_channel *pchan[8];
#ifdef CONFIG_AMLOGIC_LEGACY_EARLY_SUSPEND
struct early_suspend early_suspend;
#endif
};
2.3.2.2] send_data_to_bl301 函数分析
static char adc_key_mode_name[MAX_NAME_LEN] = "abcdef"; //wjc_note 初始化的时候
static void send_data_to_bl301(void)
{
u32 val;
if (!strcmp(adc_key_mode_name, "POWER_WAKEUP_POWER")) {
val = 0; /*only power key resume*/
scpi_send_usr_data(SCPI_CL_POWER, &val, sizeof(val));
} else if (!strcmp(adc_key_mode_name, "POWER_WAKEUP_ANY")) {
val = 1; /*any key resume*/
scpi_send_usr_data(SCPI_CL_POWER, &val, sizeof(val));
} else if (!strcmp(adc_key_mode_name, "POWER_WAKEUP_NONE")) {
val = 2; /*no key can resume*/
scpi_send_usr_data(SCPI_CL_POWER, &val, sizeof(val));
}
}
<综上所述> 如果没去设置adc_key_mode_name的话, send_data_to_bl301就相当于空函数
2.3.2.3] kernel_keypad_enable_mode_enable 函数分析
static char kernelkey_en_name[MAX_NAME_LEN] = "abcdef";
static void kernel_keypad_enable_mode_enable(void)
{
if (!strcmp(kernelkey_en_name, "KEYPAD_UNLOCK"))
keypad_enable_flag = 1; /*unlock, normal mode*/
else if (!strcmp(kernelkey_en_name, "KEYPAD_LOCK"))
keypad_enable_flag = 0; /*lock, press key will be not useful*/
else
keypad_enable_flag = 1;
}
<综上所述> 如果没去设置kernelkey_en_name的话, kernel_keypad_enable_mode_enable就相当于空函数
2.3.2.4] meson_adc_kp_get_devtree_pdata 函数分析
static int meson_adc_kp_get_devtree_pdata(struct platform_device *pdev,
struct meson_adc_kp *kp)
{
int ret;
int count;
int value;
int state = 0;
unsigned char cnt;
const char *uname;
unsigned int key_num;
struct adc_key *key;
struct of_phandle_args chanspec;
if (!pdev->dev.of_node) {
dev_err(&pdev->dev, "failed to get device node\n");
return -EINVAL;
}
count = of_property_count_strings(pdev->dev.of_node,
"io-channel-names"); //wjc_note 获取adc_keypad节点下,io-channel-names 属性的值 io-channel-names = "key-chan-2"; count = 1
if (count < 0) {
dev_err(&pdev->dev, "failed to get io-channel-names");
return -ENODATA;
}
ret = of_property_read_u32(pdev->dev.of_node, "poll-interval", &value); //wjc_note 没有这个属性,of_property_read_u32 成功返回0,失败返回负数
if (ret)
kp->poll_period = POLL_INTERVAL_DEFAULT; //wjc_note 代码应该走这里
else
kp->poll_period = value;
for (cnt = 0; cnt < count; cnt++) {
ret = of_parse_phandle_with_args(pdev->dev.of_node,
"io-channels",
"#io-channel-cells",
cnt, &chanspec);
if (ret)
return ret;
if (!chanspec.args_count) //wjc_note 判断有没有从属性列表 "io-channels"中获取到数据
return -EINVAL;
if (chanspec.args[0] >= 8) { //wjc_note 获取到的数据是io-channels = <&saradc 2> ,这里猜测chanspec.args[0] =2 ,即adc采样的通道号合法性判断。
dev_err(&pdev->dev, "invalid channel index[%u]\n",
chanspec.args[0]);
return -EINVAL;
}
ret = of_property_read_string_index(pdev->dev.of_node, //wjc_note 获取属性io-channel-names的值 io-channel-names = "key-chan-2"; 所以, uname = "key-chan-2"
"io-channel-names",
cnt, &uname);
if (ret < 0) {
dev_err(&pdev->dev, "invalid channel name index[%d]\n",
cnt);
return -EINVAL;
}
kp->pchan[chanspec.args[0]] = devm_iio_channel_get(&pdev->dev,
uname);
if (IS_ERR(kp->pchan[chanspec.args[0]]))
return PTR_ERR(kp->pchan[chanspec.args[0]]);
}
ret = of_property_read_u32(pdev->dev.of_node, "key_num", &key_num); //wjc_note 获取实现"key_num"的值 key_num = <4>;
if (ret) {
dev_err(&pdev->dev, "failed to get key_num!\n");
return -EINVAL;
}
for (cnt = 0; cnt < key_num; cnt++) { //wjc_note 循环处理获取按键板上的按键信息
key = kzalloc(sizeof(*key), GFP_KERNEL); //wjc_note 申请一个key对象内存用于保存该按键板上的每个按键的信息,最后将每个按键的信息添加到链表中
if (!key)
return -ENOMEM;
ret = of_property_read_string_index(pdev->dev.of_node, //wjc_note 依次获取按键对应的按键名称 key_name = "update", "vol-", "vol+", "enter";
"key_name", cnt, &uname);
if (ret < 0) {
dev_err(&pdev->dev, "invalid key name index[%d]\n",
cnt);
state = -EINVAL;
goto err;
}
snprintf(key->name, MAX_NAME_LEN, "%s", uname); //wjc_note 获取到的按键的名称,保存在key对象中
ret = of_property_read_u32_index(pdev->dev.of_node, //wjc_note 依次获取按键对应的键值 key_code = <141 114 115 28>;并将结果保存在key对象中 可以对象就是前面初始化的链表的结点类型
"key_code", cnt, &key->code);
if (ret < 0) {
dev_err(&pdev->dev, "invalid key code index[%d]\n",
cnt);
state = -EINVAL;
goto err;
}
ret = of_property_read_u32_index(pdev->dev.of_node, //wjc_note 依次获取按键对应的adc采样通道 key_chan = <2 2 2 2>;并将结果保存在key对象在
"key_chan", cnt, &key->chan);
if (ret < 0) {
dev_err(&pdev->dev, "invalid key chan index[%d]\n",
cnt);
state = -EINVAL;
goto err;
}
if (!kp->pchan[key->chan]) { //wjc_note 判断该按键的adc采样通道是否存在,即在设备树中有没有定义该按键需要的采样通道,和前面获取采样通道属性呼应了,
//如果该按键对应的采样通道不存在那这个按键设备树定义是有问题的,就直接goto到err中
dev_err(&pdev->dev, "invalid channel[%u], please enable it first by DTS\n",
key->chan);
state = -EINVAL;
goto err;
}
ret = of_property_read_u32_index(pdev->dev.of_node,
"key_val", cnt, &key->value); //wjc_note 依次获取按键对应的采样值 key_val = <0 143 266 389>; //val=voltage/1800mV*1023
if (ret < 0) {
dev_err(&pdev->dev, "invalid key value index[%d]\n",
cnt);
state = -EINVAL;
goto err;
}
ret = of_property_read_u32_index(pdev->dev.of_node, //wjc_note 依次获取按键对应的误差范围 key_tolerance = <40 40 40 40>;
"key_tolerance",
cnt, &key->tolerance);
if (ret < 0) {
dev_err(&pdev->dev, "invalid key tolerance index[%d]\n",
cnt);
state = -EINVAL;
goto err;
}
list_add_tail(&key->list, &kp->adckey_head); //wjc_note ******* 非常重要 把从设备树中获取到的按键板上的每个按键的对象(包含了该按键的所有信息)都添加到链表adckey_head中 设计完美
// 最终驱动全局信息对象 kp中包含所有一切的设备树信息
}
meson_adc_kp_get_valid_chan(kp);
return 0;
err:
kfree(key);
return state;
}
a> of_property_count_strings 函数分析
int of_property_count_strings(struct device_node *np, const char *propname)
从设备结点np中读取属性名为propname的字符串型属性值的个数
b> of_parse_phandle_with_args 函数分析
int of_parse_phandle_with_args(const struct device_node *np, const char *list_name,const char *cells_name, int index,struct of_phandle_args *out_args)
{
if (index < 0)
return -EINVAL;
return __of_parse_phandle_with_args(np, list_name, cells_name, index, out_args);
}
EXPORT_SYMBOL(of_parse_phandle_with_args);
函数作用:获得节点 phandle 列表中的某个节点
参数 np 指向当前节点;list_name 指向节点中 phandle 列表的属性名;
cells_name 参数指明 phandle 指向的节点所含的 cells 个数;
index 表示 phandle 列 表的索引,0 代表第一个 phandle,1 代表第二个 phandle;
out_args 参数用于存储 phandle 中的参数
故下列代码的含义为
ret = of_parse_phandle_with_args(pdev->dev.of_node,
"io-channels",
"#io-channel-cells",
cnt, &chanspec); //wjc_note cnt = 0
获取节点"adc_keypad"中属性名称为"io-channels",一组属性值大小为"#io-channel-cells",中下标为0的属性值,并将结果保存在chanspec中
即chanspec中保存的内容为 &saradc 2
c. kp->pchan[chanspec.args[0]] = devm_iio_channel_get(&pdev->dev,uname); 代码分析
kp->pchan的成员类型 ==> struct iio_channel *pchan[8];
/home/rd047/amlogic_code_one/1000_311D2_driverinterface/release/common/include/linux/iio/consumer.h
struct iio_channel {
struct iio_dev *indio_dev;
const struct iio_chan_spec *channel;
void *data;
};
/home/rd047/amlogic_code_one/1000_311D2_driverinterface/release/common/drivers/iio/inkern.c
struct iio_channel *devm_iio_channel_get(struct device *dev,
const char *channel_name)
{
struct iio_channel **ptr, *channel;
ptr = devres_alloc(devm_iio_channel_free, sizeof(*ptr), GFP_KERNEL);
if (!ptr)
return ERR_PTR(-ENOMEM);
channel = iio_channel_get(dev, channel_name);
if (IS_ERR(channel)) {
devres_free(ptr);
return channel;
}
*ptr = channel;
devres_add(dev, ptr);
return channel;
}
<综上所述> 看不懂这些玩意, 大致猜测是将 kp->pchan[2] 中的信息完善好并申请好内存 ********** 看不懂
d. 按键板上按键的信息保存对象,也是链表 kp->adckey_head 的节点类型
struct adc_key {
char name[MAX_NAME_LEN];
unsigned int chan;
unsigned int code; /* input key code */
int value; /* voltage/3.3v * 1023 */
int tolerance;
struct list_head list;
};
<综上所述> 猜测最终链表adckey_head中会保存按键板上所有按键的信息 这个设计就非常的nice 值得学习
e. meson_adc_kp_get_valid_chan 函数分析
static void meson_adc_kp_get_valid_chan(struct meson_adc_kp *kp)
{
unsigned char incr;
struct adc_key *key;
mutex_lock(&kp->kp_lock);
kp->chan_num = 0; /*recalculate*/
list_for_each_entry(key, &kp->adckey_head, list) { //wjc_note 遍历链表
if (kp->chan_num == 0) { //wjc_note 第一个结点走这里
kp->chan[kp->chan_num++] = key->chan; //wjc_note key->chan代表每一个按键对应的adc成员通道,所以这里应该是将每个按键对应的采样通道号依次保存在kp的的chan成员中
} else {
for (incr = 0; incr < kp->chan_num; incr++) {
if (key->chan == kp->chan[incr])
break;
if (incr == (kp->chan_num - 1))
kp->chan[kp->chan_num++] = key->chan;
}
}
}
mutex_unlock(&kp->kp_lock);
}
<综上所述> 该函数的作用就是把每个key对应的通道信息依次保存在kp的chan成员中即 kp->chan[0],代表第一个按键对应的采样通道,kp->chan[0],代表第二个按键对应的采样通道... ...
2.3.2.5] 实现轮询获取按键板上按键的状态
代码分析:
/*alloc input poll device*/
kp->poll_dev = devm_input_allocate_polled_device(&pdev->dev); //wjc_note 申请一个 轮询设备对象
if (!kp->poll_dev) {
dev_err(&pdev->dev, "alloc input poll device failed!\n");
ret = -ENOMEM;
goto err;
}
//wjc_note 初始化这个轮询设备
kp->poll_dev->poll = meson_adc_kp_poll;
kp->poll_dev->poll_interval = kp->poll_period;
kp->poll_dev->private = kp;
I> struct input_polled_dev 类型的定义
struct input_polled_dev *poll_dev;
/home/rd047/amlogic_code_one/1000_311D2_driverinterface/release/common/include/linux/input-polldev.h
struct input_polled_dev {
void *private;
void (*open)(struct input_polled_dev *dev);
void (*close)(struct input_polled_dev *dev);
void (*poll)(struct input_polled_dev *dev);
unsigned int poll_interval; /* msec */
unsigned int poll_interval_max; /* msec */
unsigned int poll_interval_min; /* msec */
struct input_dev *input; //wjc_note 这不就是input子系统中的input设备吗? 说明系统中会把按键板中的按键作为一个输入设备,走input_event那个流程
/* private: */
struct delayed_work work;
bool devres_managed;
};
II> poll成员是什么作用, meson_adc_kp_poll 函数是什么功能?
poll成员对应的函数就是对按键板上的按键进行轮询检测,如果有按键被按下就通过input子系统中的上报流程将该按键对应的键值上报
至于 meson_adc_kp_poll函数的具体分析最终在看 (肯定包含检测那个按键按下的过程,和上报键值的过程)
III> meson_adc_kp_init_keybit 函数分析
static void meson_adc_kp_init_keybit(struct meson_adc_kp *kp)
{
struct adc_key *key;
list_for_each_entry(key, &kp->adckey_head, list)
set_bit(key->code,
kp->poll_dev->input->keybit); /*set event code*/
}
<综上所述> meson_adc_kp_init_keybit 函数就是吧按键板上所有按键对应的键值都注册,等等上报的键值就是这些中的某个
IV> input_register_polled_device 函数分析
分析它最终注册了一个什么样的驱动
/home/rd047/amlogic_code_one/1000_311D2_driverinterface/release/common/drivers/input/input-polldev.c
int input_register_polled_device(struct input_polled_dev *dev)
{
struct input_polled_devres *devres = NULL;
struct input_dev *input = dev->input; //wjc_note input设备
int error;
if (dev->devres_managed) { //wjc_note 刚才好像没有设置 input_polled_dev的devres_managed成员所以应该是NULL的,代码不走这里
devres = devres_alloc(devm_input_polldev_unregister,
sizeof(*devres), GFP_KERNEL);
if (!devres)
return -ENOMEM;
devres->polldev = dev;
}
input_set_drvdata(input, dev); //wjc_note 将input数据保存到dev中的private成员中
INIT_DELAYED_WORK(&dev->work, input_polled_device_work); //wjc_note 初始化一个工作项,大胆猜测,这就是轮询的实现方法,工作队列中的工作项 *******
if (!dev->poll_interval)
dev->poll_interval = 500;
if (!dev->poll_interval_max)
dev->poll_interval_max = dev->poll_interval;
input->open = input_open_polled_device;
input->close = input_close_polled_device;
input->dev.groups = input_polldev_attribute_groups;
error = input_register_device(input); //wjc_note 不出所料,最终就是想input子系统中注册了该input对象,即将按键板的所有按键作为了一个input对象整体
if (error) {
devres_free(devres);
return error;
}
/*
* Take extra reference to the underlying input device so
* that it survives call to input_unregister_polled_device()
* and is deleted only after input_free_polled_device()
* has been invoked. This is needed to ease task of freeing
* sparse keymaps.
*/
input_get_device(input);
if (dev->devres_managed) {
dev_dbg(input->dev.parent, "%s: registering %s with devres.\n",
__func__, dev_name(&input->dev));
devres_add(input->dev.parent, devres);
}
return 0;
}
EXPORT_SYMBOL(input_register_polled_device);
V> 万事俱备了,现在可以来分析一下,按键是如何被检测的,键值是如何上报的,检测是如何轮询的
meson_adc_kp_poll 函数分析
static void meson_adc_kp_poll(struct input_polled_dev *dev)
{
struct meson_adc_kp *kp = dev->private; //wjc_note 获取驱动中的全局信息对象 kp,kp中保存了按键板中的信息和所有按键的信息 即按键板整个设备的所有信息
int code = meson_adc_kp_search_key(kp); //wjc_note 检测是否有按键按下,如果有按键按下则code即为该按键的键值,如果没有按键按下code=0
if (kp->report_code && kp->report_code != code) {
dev_info(&kp->poll_dev->input->dev,
"key %d up\n", kp->report_code);
input_report_key(kp->poll_dev->input, kp->report_code, 0);
input_sync(kp->poll_dev->input);
kp->report_code = 0;
}
if (code) {
if (kp->count > 0 && code != kp->prev_code)
kp->count = 0;
if (kp->count < KEY_JITTER_COUNT) {
kp->count++;
} else {
if (keypad_enable_flag && kp->report_code != code) {
dev_info(&kp->poll_dev->input->dev,
"key %d down\n", code);
input_report_key(kp->poll_dev->input, code, 1);
input_sync(kp->poll_dev->input);
kp->report_code = code;
}
kp->count = 0;
}
kp->prev_code = code;
}
}
1> int code = meson_adc_kp_search_key(kp); 代码分析
static int meson_adc_kp_search_key(struct meson_adc_kp *kp)
{
struct adc_key *key;
int value, i;
mutex_lock(&kp->kp_lock);
for (i = 0; i < kp->chan_num; i++) { //wjc_note 这里kp->chan_num = 4,因为在函数 meson_adc_kp_get_valid_chan中最终将 kp->chan_num的值修改为4 ,按键板上只有4个按键所以最多只需要获取4个通道的值
if (iio_read_channel_processed(kp->pchan[kp->chan[i]],
&value) >= 0) {
if (value < 0)
continue;
list_for_each_entry(key, &kp->adckey_head, list) {
if (key->chan == kp->chan[i] &&
(value >= key->value - key->tolerance) &&
(value <= key->value + key->tolerance)) {
mutex_unlock(&kp->kp_lock);
return key->code;
}
}
}
}
mutex_unlock(&kp->kp_lock);
return KEY_RESERVED;
}
2> iio_read_channel_processed 函数分析
猜测是获取指定adc通道下的采样值,采样值保存在第二个参数中。
iio_read_channel_processed(kp->pchan[kp->chan[i]],&value)
/home/rd047/amlogic_code_one/1000_311D2_driverinterface/release/common/drivers/iio/inkern.c
int iio_read_channel_processed(struct iio_channel *chan, int *val)
{
int ret;
mutex_lock(&chan->indio_dev->info_exist_lock);
if (chan->indio_dev->info == NULL) {
ret = -ENODEV;
goto err_unlock;
}
if (iio_channel_has_info(chan->channel, IIO_CHAN_INFO_PROCESSED)) {
ret = iio_channel_read(chan, val, NULL,
IIO_CHAN_INFO_PROCESSED);
} else {
ret = iio_channel_read(chan, val, NULL, IIO_CHAN_INFO_RAW);
if (ret < 0)
goto err_unlock;
ret = iio_convert_raw_to_processed_unlocked(chan, *val, val, 1);
}
err_unlock:
mutex_unlock(&chan->indio_dev->info_exist_lock);
return ret;
}
EXPORT_SYMBOL_GPL(iio_read_channel_processed);
慢慢来
(1) kp->chan[i]的值:
meson_adc_kp_get_valid_chan 函数中记录了kp->chan[i]值的来源
kp->chan[kp->chan_num++] = key->chan;
(2) key->chan 值的来源
该值是在函数 meson_adc_kp_get_devtree_pdata 中设置的
ret = of_property_read_u32_index(pdev->dev.of_node,
"key_chan", cnt, &key->chan);
<综上所述> kp->chan[i] 就是设备树中key_chan = <2 2 2 2>; 的值
所以代码中 kp->chan[i] == 2
(3) kp->pchan[2]的值的来源
在函数 meson_adc_kp_get_devtree_pdata 中有如下代码
kp->pchan[chanspec.args[0]] = devm_iio_channel_get(&pdev->dev,uname); 这里chanspec.args[0]就是adc采样通道号,根据设备知等于 2
即 kp->pchan[2]
<综上所述> kp->pchan[2] = devm_iio_channel_get(&pdev->dev,uname); 而这里的uname急速通道的名称 io-channel-names = "key-chan-2";
devm_iio_channel_get 函数分析
/home/rd047/amlogic_code_one/1000_311D2_driverinterface/release/common/drivers/iio/inkern.c
struct iio_channel *devm_iio_channel_get(struct device *dev,const char *channel_name)
{
struct iio_channel **ptr, *channel;
ptr = devres_alloc(devm_iio_channel_free, sizeof(*ptr), GFP_KERNEL);
if (!ptr)
return ERR_PTR(-ENOMEM);
channel = iio_channel_get(dev, channel_name);
if (IS_ERR(channel)) {
devres_free(ptr);
return channel;
}
*ptr = channel;
devres_add(dev, ptr);
return channel;
}
iio_channel_get 函数分析
struct iio_channel *iio_channel_get(struct device *dev,const char *channel_name)
{
const char *name = dev ? dev_name(dev) : NULL;
struct iio_channel *channel;
if (dev) {
channel = of_iio_channel_get_by_name(dev->of_node,
channel_name);
if (channel != NULL)
return channel;
}
return iio_channel_get_sys(name, channel_name);
}
。。。。 。。。。。。。。。。。。。 看不懂,这个adc采样驱动看不懂,下次有空在分析吧 重新回到 meson_adc_kp_search_key 函数中来
*************** ************** **********************
static int meson_adc_kp_search_key(struct meson_adc_kp *kp)
{
struct adc_key *key;
int value, i;
mutex_lock(&kp->kp_lock);
for (i = 0; i < kp->chan_num; i++) {
if (iio_read_channel_processed(kp->pchan[kp->chan[i]],
&value) >= 0) { //wjc_note 猜测 函数 iio_read_channel_processed 是去获取adc采样通道2中的采样值,并将采样结果保存在 value中。 for(4),就是将按键板中每个按键定义
//的采样值去和当前实际获取到的采样值进行比较。
if (value < 0)
continue;
list_for_each_entry(key, &kp->adckey_head, list) {
if (key->chan == kp->chan[i] &&
(value >= key->value - key->tolerance) &&
(value <= key->value + key->tolerance)) { //wjc_note 这里就是判断获取到的按键采样值是否在该按键的范围内,如果是的话返回该按键对应的键值(即最终input上报的键值)
mutex_unlock(&kp->kp_lock);
return key->code;
}
}
}
}
mutex_unlock(&kp->kp_lock);
return KEY_RESERVED; //wjc_note #define KEY_RESERVED 0 如果没有按键按下就返回0
}
=================================
重新回到 meson_adc_kp_poll 的函数分析
static void meson_adc_kp_poll(struct input_polled_dev *dev)
{
struct meson_adc_kp *kp = dev->private; //wjc_note 获取驱动中的全局信息对象 kp,kp中保存了按键板中的信息和所有按键的信息 即按键板整个设备的所有信息
int code = meson_adc_kp_search_key(kp); //wjc_note 检测是否有按键按下,如果有按键按下则code即为该按键的键值,如果没有按键按下code=0
if (kp->report_code && kp->report_code != code) { //wjc_note 在 函数 meson_adc_kp_probe 中,将kp->report_code = 0; 故该代码不执行
dev_info(&kp->poll_dev->input->dev,
"key %d up\n", kp->report_code);
input_report_key(kp->poll_dev->input, kp->report_code, 0);
input_sync(kp->poll_dev->input);
kp->report_code = 0;
}
if (code) { //wjc_note 判断是否有按键按下,进入即代表code!=0 ,则说明按键板上的按键有被按下
if (kp->count > 0 && code != kp->prev_code) //wjc_note 判断此时按下的按键释放和上一次按下的按键是否为同一个按键
kp->count = 0;
if (kp->count < KEY_JITTER_COUNT) { //wjc_note #define KEY_JITTER_COUNT 1 这里的设计好像是要连续按下两次同一个键才会去上报该键值
kp->count++;
} else {
if (keypad_enable_flag && kp->report_code != code) {
dev_info(&kp->poll_dev->input->dev,
"key %d down\n", code);
input_report_key(kp->poll_dev->input, code, 1); //wjc_note input子系统上报按键按下对于的键值,并且value =1
input_sync(kp->poll_dev->input); //wjc_note 上报同步事件
kp->report_code = code;
}
kp->count = 0;
}
kp->prev_code = code;
}
}
为什么猜测说有连续按下两次同一个键才会上报?????
分析如下:
系统起来后,kp->count = 0,kp->prev_code,那么你这时候按下一个按键a,那么因为kp->count = 0所以只是执行kp->count++,和kp->prev_code = 按键a对应的键值,
如果你再次按下按键a,那么此时 kp->count =1 && code == kp->prev_code,那么此时就执行了上报代码
猜测这样设计的好处是为了避免连续上报 (当你按下按键的持续时间只需要超过两次轮询的时间那么判定还是有效的)
=========================
现在按键怎么检测和上报都学习了,可以看一下是怎么轮询的
看代码 input_register_polled_device
这里会创建一个工作队列项,那么轮询肯定和这个工作队列项有关系
a> 工作队列项
/home/rd047/amlogic_code_one/1000_311D2_driverinterface/release/common/drivers/input/input-polldev.c
int input_register_polled_device(struct input_polled_dev *dev)
{
INIT_DELAYED_WORK(&dev->work, input_polled_device_work);
}
b> 该工作队列项对应的函数 input_polled_device_work
static void input_polled_device_work(struct work_struct *work)
{
struct input_polled_dev *dev =
container_of(work, struct input_polled_dev, work.work);
dev->poll(dev); //wjc_note 这个不就是去调用前面分析的 meson_adc_kp_poll 函数吗?????? kp->poll_dev->poll = meson_adc_kp_poll;
input_polldev_queue_work(dev); //wjc_note 轮询结束重新放回等待队列中,等待下次的工作
}