📄 pca9535kbd.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 + -