Android 4.4 ,Linux版本 3.10
增加lichee/linux-3.10/drivers/switch/switch_gpio_jack.c
,内容
/*
*
201804281007
采用了两个定时器,一个用于延时消抖,
另外一个用于输入接收上报数据。
使用两个键值进行按键事件上报,键值的取值
需要根据input.h中的定义进行取值。
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include //定义了GPIOx
#define DEVICE_NAME "switch_gpio_jack"
#define KEYBOARD_PIN_NUM (1)
#define IS_USE_DEVICE_TREE 1
#define IS_USE_KERNEL_OBJ 1
#define ZWQ_DEBUG 0 //调试信息
#define REPORT_TIME (1000) //输入结束时间1ms
#define DELAY_TIME (50)//消抖时间 Xms = HZ/DELAY_TIME = 1000/DELAY_TIME
#if (!IS_USE_DEVICE_TREE)
//lichee\linux-3.10\include\uapi\linux\input.h
#define CODE_JACK_OUT 202 //自定义旋转按键值 需要在特定范围的键值才能上报!!
#define CODE_JACK_INSERT 203 //自定义中间按键值
#define JACK_GPIO_PIN GPIOI(14)
#endif
static irqreturn_t irq_handler(int irq, void *p_data);//中断函数
typedef struct _desc{
//设备描述
unsigned int gpio;/*对应gpio口*/
unsigned int irq;/*对应中断号*/
int out_code;//键码
int insert_code;
int key_status;//按键状态
}keys_desc;
struct keys_device{
keys_desc keys_desc[KEYBOARD_PIN_NUM];
struct timer_list key_timer; //按键去抖定时器
struct timer_list report_timer; //按键上报定时器
int key_count; //按键状态
int key_code;
struct input_dev *input; //输入设备指针
int irq_occurred; //记录发生的中断号
};
static struct keys_device *p_keys_dev; //全局数据指针
#if IS_USE_KERNEL_OBJ
struct sw_jack_obj {
struct kobject kobj;
int state;
};
static struct kset *example_kset;
static struct sw_jack_obj *sw_jack_obj;
//=========================================
#define to_sw_jack_obj(x) container_of(x, struct sw_jack_obj, kobj)
/* a custom attribute that works just for a struct sw_jack_obj. */
struct sw_jack_attribute {
struct attribute attr;
ssize_t (*show)(struct sw_jack_obj *sw_jack, struct sw_jack_attribute *attr, char *buf);
ssize_t (*store)(struct sw_jack_obj *sw_jack, struct sw_jack_attribute *attr, const char *buf, size_t count);
};
#define to_sw_jack_attr(x) container_of(x, struct sw_jack_attribute, attr)
static ssize_t sw_jack_attr_show(struct kobject *kobj,
struct attribute *attr,
char *buf)
{
struct sw_jack_attribute *attribute;
struct sw_jack_obj *sw_jack;
attribute = to_sw_jack_attr(attr);
sw_jack = to_sw_jack_obj(kobj);
if (!attribute->show)
return -EIO;
return attribute->show(sw_jack, attribute, buf);
}
static ssize_t sw_jack_attr_store(struct kobject *kobj,
struct attribute *attr,
const char *buf, size_t len)
{
struct sw_jack_attribute *attribute;
struct sw_jack_obj *sw_jack;
attribute = to_sw_jack_attr(attr);
sw_jack = to_sw_jack_obj(kobj);
if (!attribute->store)
return -EIO;
return attribute->store(sw_jack, attribute, buf, len);
}
/* Our custom sysfs_ops that we will associate with our ktype later on */
static const struct sysfs_ops sw_jack_sysfs_ops = {
.show = sw_jack_attr_show,
.store = sw_jack_attr_store,
};
/*
* The release function for our object. This is REQUIRED by the kernel to
* have. We free the memory held in our object here.
*
* NEVER try to get away with just a "blank" release function to try to be
* smarter than the kernel. Turns out, no one ever is...
*/
static void sw_jack_release(struct kobject *kobj)
{
struct sw_jack_obj *sw_jack;
sw_jack = to_sw_jack_obj(kobj);
kfree(sw_jack);
}
/*
* The "sw_jack" file where the .sw_jack variable is read from and written to.
*/
static ssize_t state_show(struct sw_jack_obj *sw_jack_obj, struct sw_jack_attribute *attr,
char *buf)
{
int state = gpio_get_value(p_keys_dev->keys_desc[0].gpio);
return sprintf(buf, "%d\n", state);
}
static ssize_t state_store(struct sw_jack_obj *sw_jack_obj, struct sw_jack_attribute *attr,
const char *buf, size_t count)
{
int var;
sscanf(buf, "%du", &var);
sw_jack_obj->state = var;
return count;
}
// /sys/kernel/sw_jack/switch/state
static struct sw_jack_attribute state_attribute =
__ATTR(state, 0666, state_show, state_store);
//======================================================
static struct attribute *sw_jack_default_attrs[] = {
&state_attribute.attr,
NULL, /* need to NULL terminate the list of attributes */
};
static struct kobj_type sw_jack_ktype = {
.sysfs_ops = &sw_jack_sysfs_ops,
.release = sw_jack_release,
.default_attrs = sw_jack_default_attrs,
};
static struct sw_jack_obj *create_sw_jack_obj(const char *name)
{
struct sw_jack_obj *sw_jack;
int retval;
sw_jack = kzalloc(sizeof(*sw_jack), GFP_KERNEL);
if (!sw_jack)
return NULL;
sw_jack->kobj.kset = example_kset;
retval = kobject_init_and_add(&sw_jack->kobj, &sw_jack_ktype, NULL, "%s", name);
if (retval) {
kobject_put(&sw_jack->kobj);
return NULL;
}
kobject_uevent(&sw_jack->kobj, KOBJ_ADD);
return sw_jack;
}
static void destroy_sw_jack_obj(struct sw_jack_obj *sw_jack)
{
kobject_put(&sw_jack->kobj);
}
#endif
struct keys_device *get_dev(void)
{
return p_keys_dev;
}
void set_dev(struct keys_device *p_dev)
{
p_keys_dev = p_dev;
}
static void report_timer_function(unsigned long data)
{
int state = 0;
struct keys_device *p_dev = p_keys_dev;
state = gpio_get_value(p_dev->keys_desc[0].gpio);
if(state == 0)
{
input_report_key(p_keys_dev->input, p_dev->keys_desc[0].out_code,1 );
input_sync(p_dev->input);
input_report_key(p_keys_dev->input, p_dev->keys_desc[0].out_code,0 );
input_sync(p_dev->input);
}
else
{
input_report_key(p_keys_dev->input, p_dev->keys_desc[0].insert_code,1 );
input_sync(p_dev->input);
input_report_key(p_keys_dev->input, p_dev->keys_desc[0].insert_code,0 );
input_sync(p_dev->input);
}
}
static void timer_function(unsigned long data)
{
del_timer(&(p_keys_dev->report_timer));//删除定时器
add_timer(&(p_keys_dev->report_timer));
//开启输入结束定时
mod_timer(&(p_keys_dev->report_timer),jiffies+HZ/REPORT_TIME);
}
static irqreturn_t irq_handler(int irq, void *p_data)
{
p_keys_dev->irq_occurred = irq;//保存中断号
mod_timer(&(p_keys_dev->key_timer),jiffies+HZ/DELAY_TIME);
return IRQ_HANDLED;
}
#if IS_USE_DEVICE_TREE
static int parse_device_tree(struct device_node *node ,struct keys_device * keys_dev)
{
struct gpio_config config;
int ret = 0;
char pin_name[10];
if (!keys_dev)
return -ENOMEM;
if (!node)
return -ENOMEM;
keys_dev->keys_desc[0].gpio = of_get_named_gpio_flags(node, "switch-gpio", 0,(enum of_gpio_flags *)&config);
if (!gpio_is_valid(keys_dev->keys_desc[0].gpio)) {
printk("%s read %s fail",__func__,"switch-gpio");
return -EINVAL;
}
ret = of_property_read_u32(node, "insert-code", &keys_dev->keys_desc[0].insert_code);
if(ret){
printk("%s read %s fail",__func__,"insert-code");
return -EINVAL;
}
ret = of_property_read_u32(node, "out-code", &keys_dev->keys_desc[0].out_code);
if(ret){
printk("%s read %s fail",__func__,"out-code");
return -EINVAL;
}
printk("%s: gpio[%d],inser-code:%d,out-code:%d\n",__func__,
keys_dev->keys_desc[0].gpio,
keys_dev->keys_desc[0].insert_code,
keys_dev->keys_desc[0].out_code);
#if ZWQ_DEBUG
printk("%s get gpio is %d\n", __FUNCTION__,keys_dev->keys_desc[0].gpio);
sunxi_gpio_to_name(keys_dev->keys_desc[0].gpio, pin_name);
printk("gpio name = %s\n",pin_name);
#endif
return 0;
}
#endif
#if (!IS_USE_DEVICE_TREE)
//设置参数
static void keys_dev_init_data(struct keys_device *p_dev)
{
char pin_name[10];
//设置参数
p_dev->keys_desc[0].gpio = JACK_GPIO_PIN;
p_dev->keys_desc[0].out_code = CODE_JACK_OUT;
p_dev->keys_desc[0].insert_code = CODE_JACK_INSERT;
p_dev->keys_desc[0].key_status = 0;
#if ZWQ_DEBUG
printk("%s 获取gpio%d is %d\n", __FUNCTION__,0, p_dev->keys_desc[0].gpio);
sunxi_gpio_to_name(p_dev->keys_desc[0].gpio, pin_name);
printk("gpio%d name = %s\n",0,pin_name);
#endif
}
#endif
//申请中断号
static int keys_request_irq(struct keys_device *p_dev)
{
int i = 0;
int gpio = 0;
int ret = 0;
int irq_no = 0;
char pin_name[10];
long unsigned int config_set;
long unsigned int config_get;
int pull;
for(i = 0 ;i<sizeof(p_dev->keys_desc)/sizeof(p_dev->keys_desc[0]);i++)
{
//为每个按键都初始化gpio
gpio = p_dev->keys_desc[i].gpio;
gpio_free(gpio);
ret = gpio_request(gpio,"key_gpio");
if(ret)
{
printk("%s::request gpio %d failed\n",__FUNCTION__,gpio);
return -1;
}
//当gpio被设置为输入工作状态后,就可以检测中断信号
gpio_direction_input(gpio);
irq_no = gpio_to_irq(gpio);//申请中断号
if (irq_no < 0) {
printk("%s::Unable to get irq number for GPIO %d\n",__FUNCTION__,gpio);
return -1;
}
p_dev->keys_desc[i].irq = irq_no;
pull = 1;//1:high 0:low
sunxi_gpio_to_name(gpio, pin_name);
#if ZWQ_DEBUG
/*check if pin pull setting right */
pr_warn("step1: get [%s] pull value.\n", pin_name);
#endif
config_get = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_PUD, 0XFFFF);
pin_config_get(SUNXI_PINCTRL, pin_name, &config_get);
#if ZWQ_DEBUG
pr_warn(" [%s] pull value: %ld\n", pin_name, SUNXI_PINCFG_UNPACK_VALUE(config_get));
pr_warn("step2: set [%s] pull value to %d\n", pin_name, pull);
#endif
config_set = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_PUD, pull);
pin_config_set(SUNXI_PINCTRL, pin_name, config_set);
#if ZWQ_DEBUG
pr_warn("step3: get [%s] pull value.\n", pin_name);
#endif
config_get = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_PUD, 0XFFFF);
pin_config_get(SUNXI_PINCTRL, pin_name, &config_get);
#if ZWQ_DEBUG
pr_warn(" [%s] pull value: %ld\n", pin_name, SUNXI_PINCFG_UNPACK_VALUE(config_get));
#endif
}
return 0;
}
//释放中断号
static void dev_free_irqs(void)
{
int i;
struct keys_device *p_dev = get_dev();
for(i = 0; i < KEYBOARD_PIN_NUM; i++)
free_irq(p_dev->keys_desc[i].irq, p_dev);
}
//绑定中断函数
static int dev_bind_irqs(void)
{
int n_ret;
int i;
struct keys_device *p_dev = get_dev();
for(i = 0; i < KEYBOARD_PIN_NUM; i++)
{
n_ret = request_irq(p_dev->keys_desc[i].irq, irq_handler, IRQ_TYPE_EDGE_BOTH, DEVICE_NAME, p_dev);
if(n_ret)
{
printk("%s::gpio no[%d]. the %d: could not register interrupt\n",
__FUNCTION__, i,p_dev->keys_desc[i].irq);
}
}
if(n_ret)
{
printk("%s::some gpio could not register interrupt\n",__FUNCTION__);
goto fail;
}
return n_ret;
fail:
for(i--; i >= 0; i--)
{
disable_irq(p_dev->keys_desc[i].irq);
free_irq(p_dev->keys_desc[i].irq, p_dev);
}
return n_ret;
}
static int keys_probe(struct platform_device *pdev)
{
int err = -ENOMEM;
struct keys_device *p_keys_dev = NULL;
struct input_dev *input_dev = NULL;
struct device_node *node = pdev->dev.of_node;
printk("%s::enter!\n",__FUNCTION__);
p_keys_dev = kmalloc(sizeof(struct keys_device), GFP_KERNEL);//申请空间
if( !p_keys_dev )//失败
{
printk("%s kmalloc error!\n",__FUNCTION__);
return err;
}
#if (!IS_USE_DEVICE_TREE)
keys_dev_init_data(p_keys_dev);//设置gpio引脚
#else
err = parse_device_tree(node,p_keys_dev);
if(err)
goto fail;
#endif
if(keys_request_irq(p_keys_dev))//申请中断号
return err;
//1、为输入设备驱动对象申请内存空间
input_dev = input_allocate_device();
if (!input_dev)
{
printk("%s input_allocate_device error!\n",__FUNCTION__);
goto fail;
}
//保存输入设备到平台
p_keys_dev->input = input_dev;
platform_set_drvdata(pdev, p_keys_dev);
input_dev->name = DEVICE_NAME;//设备名称
input_dev->phys = DEVICE_NAME"/input8";//物理信息
input_dev->id.bustype = BUS_HOST;//总线类型
input_dev->dev.parent = &pdev->dev;//
input_dev->id.vendor = 0x0001;//
input_dev->id.product = 0x0001;//
input_dev->id.version = 0x0100;//版本号
//2.1注册事件
__set_bit(EV_KEY, input_dev->evbit);
__set_bit(EV_SYN, input_dev->evbit);
__set_bit(EV_REP, input_dev->evbit);
__set_bit(EV_REL, input_dev->evbit);
//2.2注册键值
__set_bit(p_keys_dev->keys_desc[0].insert_code, input_dev->keybit);
__set_bit(p_keys_dev->keys_desc[0].out_code, input_dev->keybit);
err = input_register_device(input_dev);
if( err )
{
printk("%s::input_register_device error!\n",__FUNCTION__);
goto fail_allocate;
}
set_dev(p_keys_dev);
//4
init_timer(&(p_keys_dev->key_timer));
p_keys_dev->key_timer.function = timer_function;
add_timer(&(p_keys_dev->key_timer));
init_timer(&(p_keys_dev->report_timer));
p_keys_dev->report_timer.function = report_timer_function;
add_timer(&(p_keys_dev->report_timer));
err = dev_bind_irqs();
if( err )
{
printk("%s dev_bind_irqs error!\n",__FUNCTION__);
goto fail_register;
}
example_kset = kset_create_and_add("sw_jack", NULL, kernel_kobj);
sw_jack_obj = create_sw_jack_obj("switch");
printk("%s::success!\n",__FUNCTION__);
return 0;
fail_register:
input_unregister_device(input_dev);
goto fail;
fail_allocate:
input_free_device(input_dev);
fail:
kfree(p_keys_dev);
return err;
}
static int keys_remove(struct platform_device *pdev)
{
struct keys_device *p_keys_dev = platform_get_drvdata(pdev);
dev_free_irqs();
del_timer(&(p_keys_dev->key_timer));
del_timer(&(p_keys_dev->report_timer));
input_unregister_device(p_keys_dev->input);
destroy_sw_jack_obj(sw_jack_obj);
kset_unregister(example_kset);
kfree(p_keys_dev);
return 0;
}
#if (!IS_USE_DEVICE_TREE)
static void keys_release(struct device *dev)
{
dev = dev;
}
#endif
static const struct of_device_id jack_gpio_switch_match[] = {
{
.compatible = "jack-switch-gpio", .data = NULL},
{
/* sentinel */ }
};
static struct platform_driver keys_device_driver = {
.probe = keys_probe,
.remove = keys_remove,
.driver = {
.name = DEVICE_NAME,
.owner = THIS_MODULE,
#if (IS_USE_DEVICE_TREE)
.of_match_table = of_match_ptr(jack_gpio_switch_match),
#endif
}
};
#if (!IS_USE_DEVICE_TREE)
static struct platform_device keys_platform_devicer = {
.name = DEVICE_NAME,
.id = -1,
.dev = {
.release = keys_release,
}
};
#endif
static int __init keys_init(void)
{
int n_ret;
printk("%s::enter\n",__FUNCTION__);
n_ret = platform_driver_register(&keys_device_driver);
if( n_ret )
return n_ret;
#if (!IS_USE_DEVICE_TREE)
n_ret = platform_device_register(&keys_platform_devicer);
if( n_ret )
goto fail;
#endif
return n_ret;
fail:
platform_driver_unregister(&keys_device_driver);
return n_ret;
}
static void __exit keys_exit(void)
{
printk("%s::exit\n",__FUNCTION__);
#if (!IS_USE_DEVICE_TREE)
platform_device_unregister(&keys_platform_devicer);
#endif
platform_driver_unregister(&keys_device_driver);
}
module_init(keys_init);
module_exit(keys_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("zwq, [email protected]");
MODULE_DESCRIPTION("zwq jack input events driver");
lichee/linux-3.10/drivers/switch/Makefile
末尾增加
obj-m += switch_gpio_jack.o
lichee/tools/pack/chips/sun8iw11p1/configs/magton-perf/sys_config.fex
末尾添加
[switch_gpio]
compatible = "jack-switch-gpio"
switch-name = "h2w"
switch-gpio = port:PI14<1><1>
out-code = 202
insert-code = 203
android/frameworks/base/core/res/res/values/attrs.xml
中
增加
<enum name="KEYCODE_M_JACK_OUT" value="10016" />
<enum name="KEYCODE_M_JACK_INSERT" value="10017" />
android/frameworks/native/include/input/KeycodeLabels.h
static const KeycodeLabel KEYCODES[]
末尾中增加
{
"KEYCODE_M_JACK_OUT", 10016},
{
"KEYCODE_M_JACK_INSERT", 10017},
android//frameworks/native/include/android/keycodes.h
中Key codes
枚举定义末尾增加
AKEYCODE_M_JACK_OUT = 10016,
AKEYCODE_M_JACK_INSERT = 10017,
android/frameworks/base/core/java/android/view/KeyEvent.java
中
public class KeyEvent extends InputEvent implements Parcelable
中增加
public static final int KEYCODE_M_JACK_OUT = 10016;
public static final int KEYCODE_M_JACK_INSERT = 10017;
private static void populateKeycodeSymbolicNames()
中增加
names.append(KEYCODE_M_JACK_OUT, "KEYCODE_M_JACK_OUT");
names.append(KEYCODE_M_JACK_INSERT, "KEYCODE_M_JACK_INSERT");
android/frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
private void sendJackState(int code){
//am broadcast -a android.intent.action.HEADSET_PLUG --ei "device" 4 --ei "state" 1 --es "name" "h2w" --ei "microphone" 0
Intent intent = new Intent();
int device = AudioSystem.DEVICE_OUT_WIRED_HEADSET;//4
int state = 0;
String name="h2w";
if(KeyEvent.KEYCODE_M_JACK_OUT == code)
state = 0;
else
state = 1;
intent.putExtra("device", device);
intent.putExtra("state", state);
intent.putExtra("name", name);
//intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
intent.setAction(Intent.ACTION_HEADSET_PLUG);
intent.putExtra("microphone", 1);
mContext.sendBroadcast(intent);
}
private long _interceptKeyBeforeDispatching(WindowState win, KeyEvent event, int policyFlags)
中增加
if((keyCode == KeyEvent.KEYCODE_M_JACK_OUT)||(keyCode == KeyEvent.KEYCODE_M_JACK_INSERT)){
sendJackState(keyCode);
return -1;
}
增加 android/device/softwinner/magton-perf/configs/switch_gpio_jack.kl
key 202 KEYCODE_M_JACK_INSERT
key 203 KEYCODE_M_JACK_OUT
加载驱动 system/vendor/modules/switch_gpio_jack.ko
后系统生成节点
/sys/kernel/sw_jack/switch/state