📄 pca9535dio.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/fs.h>#include <linux/cdev.h>#include <asm/irq.h>#include <asm/uaccess.h>#define DIGITIO_MAJOR 253#define PCA9535DIO_IRQ_VECTOR IRQ_PJ7static int digitio_major = DIGITIO_MAJOR;/* Addresses to scan *///static unsigned short normal_i2c[] = {0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, I2C_CLIENT_END};static unsigned short normal_i2c[] = {0x20, I2C_CLIENT_END};/* Insmod parameters */I2C_CLIENT_INSMOD_1(pca9535dio);enum pca9535dio_cmd{ PCA9535DIO_INPUT_0 = 0, PCA9535DIO_INPUT_1 = 1, PCA9535DIO_OUTPUT_0 = 2, PCA9535DIO_OUTPUT_1 = 3, PCA9535DIO_INVERT_0 = 4, PCA9535DIO_INVERT_1 = 5, PCA9535DIO_DIRECTION_0 = 6, PCA9535DIO_DIRECTION_1 = 7,};static irqreturn_t pca9535dio_keys_isr(int irq, void *dev_id);static int pca9535dio_attach_adapter(struct i2c_adapter *adapter);static int pca9535dio_detect(struct i2c_adapter *adapter, int address, int kind);static int pca9535dio_detach_client(struct i2c_client *client);static int pca9535dio_open(struct inode *inode, struct file *filp); static int pca9535dio_release(struct inode *inode, struct file *filp);static ssize_t pca9535dio_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos);static ssize_t pca9535dio_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos);static int pca9535dio_fasync(int fd, struct file *filp, int mode);/* This is the driver that will be inserted */static struct i2c_driver pca9535dio_driver = { .driver = { .name = "pca9535dio", }, .attach_adapter = pca9535dio_attach_adapter, .detach_client = pca9535dio_detach_client,};struct pca9535dio_data { struct i2c_client client; struct cdev cdev; u16 port_state; u16 direction; struct work_struct pca9535dio_work; struct fasync_struct *fasync;};static void pca9535dio_do_work(struct work_struct * work){ u16 new_state, pressed, released; struct pca9535dio_data *data = container_of(work, struct pca9535dio_data, pca9535dio_work); new_state = i2c_smbus_read_byte_data(&data->client, PCA9535DIO_INPUT_0) | (i2c_smbus_read_byte_data(&data->client, PCA9535DIO_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; if(pressed || released) { if(data->fasync) kill_fasync(&data->fasync, SIGIO, POLL_IN); } data->port_state = new_state;}static irqreturn_t pca9535dio_keys_isr(int irq, void *dev_id){ struct pca9535dio_data *data = (struct pca9535dio_data *)dev_id; schedule_work(&data->pca9535dio_work); return IRQ_HANDLED;}static int pca9535dio_open(struct inode *inode, struct file *filp){ struct pca9535dio_data *data; data = container_of(inode->i_cdev, struct pca9535dio_data, cdev); filp->private_data = (void*)data; return 0;}static int pca9535dio_release(struct inode *inode, struct file *filp){ pca9535dio_fasync(-1, filp, 0); return 0;}static ssize_t pca9535dio_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos){ u16 state; struct pca9535dio_data *data = filp->private_data; if(count > 2) count = 2; state = i2c_smbus_read_byte_data(&data->client, PCA9535DIO_INPUT_0) | (i2c_smbus_read_byte_data(&data->client, PCA9535DIO_INPUT_1) << 8); if(copy_to_user(buf, (char*)&state, count)) return -EFAULT; return count;}static ssize_tpca9535dio_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos){ struct pca9535dio_data *data = filp->private_data; u16 state = 0; if(count > 2) count = 2; if(copy_from_user((char *)&state, buf, count)) return -EFAULT; i2c_smbus_write_byte_data(&data->client, PCA9535DIO_OUTPUT_0, state); return count;}static int pca9535dio_fasync(int fd, struct file *filp, int mode){ struct pca9535dio_data *data = filp->private_data; int retval; retval = fasync_helper(fd, filp, mode, &data->fasync); return retval < 0 ? retval : 0;}static const struct file_operations pca9535dio_fops = { .owner = THIS_MODULE, .open = pca9535dio_open, .release = pca9535dio_release, .read = pca9535dio_read, .write = pca9535dio_write, //.ioctl = pca9535dio_ioctl, .fasync = pca9535dio_fasync,};static int pca9535dio_attach_adapter(struct i2c_adapter *adapter){ return i2c_probe(adapter, &addr_data, pca9535dio_detect);}/* This function is called by i2c_probe */static int pca9535dio_detect(struct i2c_adapter *adapter, int address, int kind){ struct i2c_client *new_client; struct cdev *cdev; struct pca9535dio_data *data; int err = 0; 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 pca9535dio_data), GFP_KERNEL); new_client = &data->client; i2c_set_clientdata(new_client, data); new_client->addr = address; new_client->adapter = adapter; new_client->driver = &pca9535dio_driver; new_client->flags = 0; cdev = &data->cdev; cdev_init(cdev, &pca9535dio_fops); cdev->owner = THIS_MODULE; cdev->ops = &pca9535dio_fops; err = cdev_add(cdev, MKDEV(digitio_major, 0), 1); if(err) goto exit_kfree; new_client->irq = PCA9535DIO_IRQ_VECTOR; INIT_WORK(&data->pca9535dio_work, pca9535dio_do_work); err = request_irq(new_client->irq, pca9535dio_keys_isr, IRQF_TRIGGER_FALLING | IRQF_SHARED, "pca9535dio", data); if (err) { printk(KERN_ERR "pca9535dio: unable to claim irq %d; error %d\n", new_client->irq, err); goto exit_cdev_del; } 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 0 if ((i2c_smbus_read_byte_data(new_client, 7) < 0) || (i2c_smbus_read_byte_data(new_client, 8) >= 0)) goto exit_free_irq;#endif } strlcpy(new_client->name, "pca9535dio", I2C_NAME_SIZE); /* Tell the I2C layer a new client has arrived */ if ((err = i2c_attach_client(new_client))) goto exit_free_irq; /* Initialize the PCA9535 to known state */ i2c_smbus_write_byte_data(new_client, PCA9535DIO_OUTPUT_0, 0x00); i2c_smbus_write_byte_data(new_client, PCA9535DIO_OUTPUT_1, 0xFF); i2c_smbus_write_byte_data(new_client, PCA9535DIO_DIRECTION_0, 0xF0); i2c_smbus_write_byte_data(new_client, PCA9535DIO_DIRECTION_1, 0xFF); i2c_smbus_write_byte_data(new_client, PCA9535DIO_INVERT_0, 0x00); i2c_smbus_write_byte_data(new_client, PCA9535DIO_INVERT_1, 0x00); data->port_state = i2c_smbus_read_byte_data(&data->client, PCA9535DIO_INPUT_0) | (i2c_smbus_read_byte_data(&data->client, PCA9535DIO_INPUT_1) << 8); return 0;exit_free_irq: free_irq(new_client->irq, data);exit_cdev_del: cdev_del(cdev);exit_kfree: kfree(data);exit: return err;}static int pca9535dio_detach_client(struct i2c_client *client){ int err; struct pca9535dio_data *data = container_of(client, struct pca9535dio_data, client); if ((err = i2c_detach_client(client))) return err; free_irq(client->irq, data); cdev_del(&data->cdev); kfree(i2c_get_clientdata(client)); return 0;}static int __init pca9535dio_init(void){ int result; dev_t devno = MKDEV(digitio_major, 0); if(DIGITIO_MAJOR) result = register_chrdev_region(devno, 1, "PCA9535DIO"); else { result = alloc_chrdev_region(&devno, 0, 1, "PCA9535DIO"); digitio_major = MAJOR(devno); } if(result < 0) return result; return i2c_add_driver(&pca9535dio_driver);}static void __exit pca9535dio_exit(void){ i2c_del_driver(&pca9535dio_driver); unregister_chrdev_region(MKDEV(digitio_major, 0), 1);}MODULE_AUTHOR("Watson Xu <xuhuashan@gmail.com>");MODULE_DESCRIPTION("PCA9535 driver");MODULE_LICENSE("GPL");module_init(pca9535dio_init);module_exit(pca9535dio_exit);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -