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

📄 pca9535dio.c

📁 使用PCA9535芯片作数字输入/输出端口的驱动程序
💻 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 + -