全志V40增加耳机输入检测(按键输入方式)

7 篇文章 0 订阅
5 篇文章 0 订阅

1.平台

Android 4.4 ,Linux版本 3.10

2.驱动

增加lichee/linux-3.10/drivers/switch/switch_gpio_jack.c,内容

/*
 * 	
201804281007
采用了两个定时器,一个用于延时消抖,
另外一个用于输入接收上报数据。

使用两个键值进行按键事件上报,键值的取值
需要根据input.h中的定义进行取值。

 */

#include <linux/types.h>  
#include <linux/delay.h>  
#include <linux/platform_device.h>  
#include <linux/init.h>  
#include <linux/input.h>  
#include <linux/irq.h>  
#include <linux/interrupt.h>  
#include <linux/jiffies.h>  
#include <linux/module.h>  
#include <linux/gpio.h>  
#include <linux/input/matrix_keypad.h>  
#include <linux/slab.h>  
#include <asm/io.h>  
#include <linux/miscdevice.h>  
#include <linux/printk.h>  
#include <linux/kernel.h>  
#include <linux/keyboard.h>
#include <linux/ioport.h>
#include <asm/irq.h>
#include <linux/timer.h>
#include <linux/clk.h>
#include <linux/of_platform.h>
#include <linux/of_irq.h>
#include <linux/of_address.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_gpio.h>
#include <linux/pinctrl/consumer.h>
#include <linux/pinctrl/pinconf-sunxi.h>
#include <linux/string.h>
#include <linux/ctype.h>
#include <linux/sys_config.h> //定义了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, 1548889230@qq.com");
MODULE_DESCRIPTION("zwq jack input events driver");

lichee/linux-3.10/drivers/switch/Makefile 末尾增加

obj-m += switch_gpio_jack.o

3.设备树

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><default><default><1>
out-code = 202
insert-code = 203

4. android frameworks

4.1. attrs.xml

android/frameworks/base/core/res/res/values/attrs.xml
<attr name="keycode">增加

	<enum name="KEYCODE_M_JACK_OUT" value="10016" />
    <enum name="KEYCODE_M_JACK_INSERT" value="10017" />

4.2. KeycodeLabels.h

android/frameworks/native/include/input/KeycodeLabels.h
static const KeycodeLabel KEYCODES[] 末尾中增加

   { "KEYCODE_M_JACK_OUT", 10016},
   { "KEYCODE_M_JACK_INSERT", 10017},

4.3. keycodes.h

android//frameworks/native/include/android/keycodes.h
Key codes枚举定义末尾增加

	AKEYCODE_M_JACK_OUT = 10016,
    AKEYCODE_M_JACK_INSERT = 10017,

4.4. KeyEvent.java

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");

4.5. PhoneWindowManager.java

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; 
}

4.6. switch_gpio_jack.kl

增加 android/device/softwinner/magton-perf/configs/switch_gpio_jack.kl

key  202  KEYCODE_M_JACK_INSERT
key  203  KEYCODE_M_JACK_OUT

5.现象

加载驱动 system/vendor/modules/switch_gpio_jack.ko后系统生成节点

/sys/kernel/sw_jack/switch/state
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值