⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 pca9535kbd.c

📁 原创的使用PCA9535芯片作为键盘输入设备的Linux驱动程序
💻 C
字号:
/*    pca9535kb.c - 16-bit I/O port with interrupt and reset    Copyright (C) 2008 Watson Xu <xuhuashan@gmail.com>    This program is free software; you can redistribute it and/or modify    it under the terms of the GNU General Public License as published by    the Free Software Foundation; version 2 of the License.*/#include <linux/module.h>#include <linux/init.h>#include <linux/slab.h>#include <linux/i2c.h>#include <linux/interrupt.h>#include <linux/hwmon-sysfs.h>#include <linux/input.h>#include <asm/irq.h>#define PCA9535KBD_IRQ_VECTOR	IRQ_PJ7/* Addresses to scan *///static unsigned short normal_i2c[] = {0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, I2C_CLIENT_END};static unsigned short normal_i2c[] = {0x21, I2C_CLIENT_END};/* Insmod parameters */I2C_CLIENT_INSMOD_1(pca9535);enum pca9535kbd_cmd{	PCA9535_INPUT_0		= 0,	PCA9535_INPUT_1		= 1,	PCA9535_OUTPUT_0	= 2,	PCA9535_OUTPUT_1	= 3,	PCA9535_INVERT_0	= 4,	PCA9535_INVERT_1	= 5,	PCA9535_DIRECTION_0	= 6,	PCA9535_DIRECTION_1	= 7,};struct gpio_key_button {	u16			gpio_mask;	int			keycode;};struct gpio_key_button key_buttons[] = {	{ .gpio_mask = 0x0010, .keycode = KEY_F1 },	{ .gpio_mask = 0x0008, .keycode = KEY_F2 },	{ .gpio_mask = 0x0004, .keycode = KEY_F3 },	{ .gpio_mask = 0x0002, .keycode = KEY_F4 },	{ .gpio_mask = 0x0001, .keycode = KEY_F5 },	{ .gpio_mask = 0x1000, .keycode = KEY_F6 },	{ .gpio_mask = 0x0800, .keycode = KEY_F7 },	{ .gpio_mask = 0x0400, .keycode = KEY_F8 },	{ .gpio_mask = 0x0200, .keycode = KEY_F9 },	{ .gpio_mask = 0x0100, .keycode = KEY_F10 },};static irqreturn_t pca9535kbd_keys_isr(int irq, void *dev_id);static int pca9535kbd_attach_adapter(struct i2c_adapter *adapter);static int pca9535kbd_detect(struct i2c_adapter *adapter, int address, int kind);static int pca9535kbd_detach_client(struct i2c_client *client);/* This is the driver that will be inserted */static struct i2c_driver pca9535kbd_driver = {	.driver = {		.name	= "pca9535kbd",	},	.attach_adapter	= pca9535kbd_attach_adapter,	.detach_client	= pca9535kbd_detach_client,};struct pca9535kbd_data {	struct i2c_client	client;	struct input_dev	*input;	struct work_struct	pca9535kbd_work;	char			phys[32];	u16			port_state;};static void pca9535kbd_do_work(struct work_struct *work){	int i;	u16 new_state, pressed, released;	struct pca9535kbd_data *data = container_of(work,  struct pca9535kbd_data, pca9535kbd_work);	new_state = i2c_smbus_read_byte_data(&data->client, PCA9535_INPUT_0) 				| (i2c_smbus_read_byte_data(&data->client, PCA9535_INPUT_1) << 8);		if(new_state == data->port_state)		return;		/* detect buttons which be newly pressed */	released = data->port_state & ~new_state;	/* detect buttons which just has been released */	pressed = new_state & ~data->port_state;		for(i = 0; i < ARRAY_SIZE(key_buttons); i++) {		if(pressed & key_buttons[i].gpio_mask) {			input_event(data->input, EV_KEY, key_buttons[i].keycode, 1);			input_sync(data->input);		}		if(released & key_buttons[i].gpio_mask) {			input_event(data->input, EV_KEY, key_buttons[i].keycode, 0);			input_sync(data->input);		}	}		data->port_state = new_state;}static irqreturn_t pca9535kbd_keys_isr(int irq, void *dev_id){	struct pca9535kbd_data *data = (struct pca9535kbd_data *)dev_id;		schedule_work(&data->pca9535kbd_work);		return IRQ_HANDLED;}static int pca9535kbd_attach_adapter(struct i2c_adapter *adapter){	return i2c_probe(adapter, &addr_data, pca9535kbd_detect);}/* This function is called by i2c_probe */static int pca9535kbd_detect(struct i2c_adapter *adapter, int address, int kind){	struct i2c_client	*new_client;	struct input_dev	*input;	struct pca9535kbd_data *data;	int 				err = 0;	int					i;	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))		goto exit;	/* OK. For now, we presume we have a valid client. We now create the	   client structure, even though we cannot fill it completely yet. */	data = kzalloc(sizeof(struct pca9535kbd_data), GFP_KERNEL);	input = input_allocate_device();	if (!data || !input) {		err = -ENOMEM;		goto exit;	}		data->input = input;	new_client = &data->client;	i2c_set_clientdata(new_client, data);	new_client->addr = address;	new_client->adapter = adapter;	new_client->driver = &pca9535kbd_driver;	new_client->flags = 0;		new_client->irq = PCA9535KBD_IRQ_VECTOR;		INIT_WORK(&data->pca9535kbd_work, pca9535kbd_do_work);		err = request_irq(new_client->irq, pca9535kbd_keys_isr, 					  IRQF_TRIGGER_FALLING | IRQF_SHARED, "pca9535kbd", data);	if (err) {		printk(KERN_ERR "pca9535kbd: unable to claim irq %d; error %d\n",			new_client->irq, err);		goto exit_kfree;	}		if (kind < 0) {		/* Detection: the pca9535 only has 8 registers (0-7).		   A read of 7 should succeed, but a read of 8 should fail. */		if ((i2c_smbus_read_byte_data(new_client, 7) < 0) ||		    (i2c_smbus_read_byte_data(new_client, 8) >= 0))			goto exit_free_irq;	}	strlcpy(new_client->name, "pca9535kbd", I2C_NAME_SIZE);	/* Tell the I2C layer a new client has arrived */	if ((err = i2c_attach_client(new_client)))		goto exit_free_irq;		snprintf(data->phys, sizeof(data->phys), "%s/input0", new_client->dev.bus_id);		input->name = "PCA9535 Keyboard";	input->phys = data->phys;	input->dev.parent = &new_client->dev;		input->id.vendor = 0x0001;	input->id.product = 0x0001;	input->id.version = 0x0100;		for(i = 0; i < ARRAY_SIZE(key_buttons); i++) {		input_set_capability(input, EV_KEY, key_buttons[i].keycode);	}		err = input_register_device(input);	if (err)		goto exit_detach;		/* Initialize the PCA9535 to known state */	i2c_smbus_write_byte_data(new_client, PCA9535_DIRECTION_0, 0xFF);	i2c_smbus_write_byte_data(new_client, PCA9535_DIRECTION_1, 0xFF);	i2c_smbus_write_byte_data(new_client, PCA9535_INVERT_0, 0xFF);	i2c_smbus_write_byte_data(new_client, PCA9535_INVERT_1, 0xFF);		//data->port_state = i2c_smbus_read_byte_data(&data->client, PCA9535EIO_INPUT_0)	//		| (i2c_smbus_read_byte_data(&data->client, PCA9535EIO_INPUT_1) << 8);	return 0;exit_detach:	i2c_detach_client(new_client);exit_free_irq:	free_irq(new_client->irq, NULL);exit_kfree:	if(input)		input_free_device(input);	if(data)		kfree(data);exit:	return err;}static int pca9535kbd_detach_client(struct i2c_client *client){	int err;	struct pca9535kbd_data *data = container_of(client, struct pca9535kbd_data, client);	if ((err = i2c_detach_client(client)))		return err;		input_unregister_device(data->input);		free_irq(client->irq, data);	kfree(i2c_get_clientdata(client));	return 0;}static int __init pca9535kbd_init(void){	return i2c_add_driver(&pca9535kbd_driver);}static void __exit pca9535kbd_exit(void){	i2c_del_driver(&pca9535kbd_driver);}MODULE_AUTHOR("Watson Xu <xuhuashan@gmail.com>");MODULE_DESCRIPTION("PCA9535 driver");MODULE_LICENSE("GPL");module_init(pca9535kbd_init);module_exit(pca9535kbd_exit);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -