?? i2c-core.c
字號:
/* i2c-core.c - a device driver for the iic-bus interface *//* ------------------------------------------------------------------------- *//* Copyright (C) 1995-99 Simon G. Vogl 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; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *//* ------------------------------------------------------------------------- *//* With some changes from Ky鰏ti M鋖kki <kmalkki@cc.hut.fi>. All SMBus-related things are written by Frodo Looijaard <frodol@dds.nl> *//* $Id: i2c-core.c,v 1.2 2004/02/06 13:19:45 laputa Exp $ */#include <linux/module.h>#include <linux/kernel.h>#include <linux/errno.h>#include <linux/slab.h>#include <linux/proc_fs.h>#include <linux/config.h>#include <linux/i2c.h>/* ----- compatibility stuff ----------------------------------------------- */#include <linux/version.h>#include <linux/init.h>#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,1)#define init_MUTEX(s) do { *(s) = MUTEX; } while(0)#endif#include <asm/uaccess.h>/* ----- global defines ---------------------------------------------------- *//* exclusive access to the bus */#define I2C_LOCK(adap) down(&adap->lock)#define I2C_UNLOCK(adap) up(&adap->lock) #define ADAP_LOCK() down(&adap_lock)#define ADAP_UNLOCK() up(&adap_lock)#define DRV_LOCK() down(&driver_lock)#define DRV_UNLOCK() up(&driver_lock)#define DEB(x) if (i2c_debug>=1) x;#define DEB2(x) if (i2c_debug>=2) x;/* ----- global variables -------------------------------------------------- *//**** lock for writing to global variables: the adapter & driver list */struct semaphore adap_lock;struct semaphore driver_lock;/**** adapter list */static struct i2c_adapter *adapters[I2C_ADAP_MAX];static int adap_count;/**** drivers list */static struct i2c_driver *drivers[I2C_DRIVER_MAX];static int driver_count;/**** debug level */static int i2c_debug = 0;/* --------------------------------------------------- * /proc entry declarations *---------------------------------------------------- */#ifdef CONFIG_PROC_FSstatic int i2cproc_init(void);static void i2cproc_cleanup(void);#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,3,27))static void monitor_bus_i2c(struct inode *inode, int fill);#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,58)) */static ssize_t i2cproc_bus_read(struct file * file, char * buf,size_t count, loff_t *ppos);static int read_bus_i2c(char *buf, char **start, off_t offset, int len, int *eof , void *private);/* To implement the dynamic /proc/bus/i2c-? files, we need our own implementation of the read hook */static struct file_operations i2cproc_operations = { read: i2cproc_bus_read,};#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,48))static struct inode_operations i2cproc_inode_operations = { &i2cproc_operations};#endifstatic int i2cproc_initialized = 0;#else /* undef CONFIG_PROC_FS */#define i2cproc_init() 0#define i2cproc_cleanup() 0#endif /* CONFIG_PROC_FS *//* --------------------------------------------------- * registering functions * --------------------------------------------------- *//* ----- * i2c_add_adapter is called from within the algorithm layer, * when a new hw adapter registers. A new device is register to be * available for clients. */int i2c_add_adapter(struct i2c_adapter *adap){ int i,j,res; ADAP_LOCK(); for (i = 0; i < I2C_ADAP_MAX; i++) if (NULL == adapters[i]) break; if (I2C_ADAP_MAX == i) { printk(KERN_WARNING " i2c-core.o: register_adapter(%s) - enlarge I2C_ADAP_MAX.\n", adap->name); res = -ENOMEM; goto ERROR0; } adapters[i] = adap; adap_count++; ADAP_UNLOCK(); /* init data types */ init_MUTEX(&adap->lock);#ifdef CONFIG_PROC_FS if (i2cproc_initialized) { char name[8]; struct proc_dir_entry *proc_entry; sprintf(name,"i2c-%d", i); proc_entry = create_proc_entry(name,0,proc_bus); if (! proc_entry) { printk("i2c-core.o: Could not create /proc/bus/%s\n", name); res = -ENOENT; goto ERROR1; }#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,48)) proc_entry->proc_fops = &i2cproc_operations;#else proc_entry->ops = &i2cproc_inode_operations;#endif#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,27)) proc_entry->owner = THIS_MODULE;#else proc_entry->fill_inode = &monitor_bus_i2c;#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,58)) */ adap->inode = proc_entry->low_ino; }#endif /* def CONFIG_PROC_FS */ /* inform drivers of new adapters */ DRV_LOCK(); for (j=0;j<I2C_DRIVER_MAX;j++) if (drivers[j]!=NULL && (drivers[j]->flags&(I2C_DF_NOTIFY|I2C_DF_DUMMY))) /* We ignore the return code; if it fails, too bad */ drivers[j]->attach_adapter(adap); DRV_UNLOCK(); DEB(printk("i2c-core.o: adapter %s registered as adapter %d.\n", adap->name,i)); return 0; ERROR1: ADAP_LOCK(); adapters[i] = NULL; adap_count--;ERROR0: ADAP_UNLOCK(); return res;}int i2c_del_adapter(struct i2c_adapter *adap){ int i,j,res; ADAP_LOCK(); for (i = 0; i < I2C_ADAP_MAX; i++) if (adap == adapters[i]) break; if (I2C_ADAP_MAX == i) { printk( "i2c-core.o: unregister_adapter adap [%s] not found.\n", adap->name); res = -ENODEV; goto ERROR0; } /* DUMMY drivers do not register their clients, so we have to * use a trick here: we call driver->attach_adapter to * *detach* it! Of course, each dummy driver should know about * this or hell will break loose... */ DRV_LOCK(); for (j = 0; j < I2C_DRIVER_MAX; j++) if (drivers[j] && (drivers[j]->flags & I2C_DF_DUMMY)) if ((res = drivers[j]->attach_adapter(adap))) { printk("i2c-core.o: can't detach adapter %s " "while detaching driver %s: driver not " "detached!",adap->name,drivers[j]->name); goto ERROR1; } DRV_UNLOCK(); /* detach any active clients. This must be done first, because * it can fail; in which case we give upp. */ for (j=0;j<I2C_CLIENT_MAX;j++) { struct i2c_client *client = adap->clients[j]; if (client!=NULL) /* detaching devices is unconditional of the set notify * flag, as _all_ clients that reside on the adapter * must be deleted, as this would cause invalid states. */ if ((res=client->driver->detach_client(client))) { printk("i2c-core.o: adapter %s not " "unregistered, because client at " "address %02x can't be detached. ", adap->name, client->addr); goto ERROR0; } }#ifdef CONFIG_PROC_FS if (i2cproc_initialized) { char name[8]; sprintf(name,"i2c-%d", i); remove_proc_entry(name,proc_bus); }#endif /* def CONFIG_PROC_FS */ adapters[i] = NULL; adap_count--; ADAP_UNLOCK(); DEB(printk("i2c-core.o: adapter unregistered: %s\n",adap->name)); return 0;ERROR0: ADAP_UNLOCK(); return res;ERROR1: DRV_UNLOCK(); return res;}/* ----- * What follows is the "upwards" interface: commands for talking to clients, * which implement the functions to access the physical information of the * chips. */int i2c_add_driver(struct i2c_driver *driver){ int i; DRV_LOCK(); for (i = 0; i < I2C_DRIVER_MAX; i++) if (NULL == drivers[i]) break; if (I2C_DRIVER_MAX == i) { printk(KERN_WARNING " i2c-core.o: register_driver(%s) " "- enlarge I2C_DRIVER_MAX.\n", driver->name); DRV_UNLOCK(); return -ENOMEM; } drivers[i] = driver; driver_count++; DRV_UNLOCK(); /* driver was successfully added */ DEB(printk("i2c-core.o: driver %s registered.\n",driver->name)); ADAP_LOCK(); /* now look for instances of driver on our adapters */ if (driver->flags& (I2C_DF_NOTIFY|I2C_DF_DUMMY)) { for (i=0;i<I2C_ADAP_MAX;i++) if (adapters[i]!=NULL) /* Ignore errors */ driver->attach_adapter(adapters[i]); } ADAP_UNLOCK(); return 0;}int i2c_del_driver(struct i2c_driver *driver){ int i,j,k,res; DRV_LOCK(); for (i = 0; i < I2C_DRIVER_MAX; i++) if (driver == drivers[i]) break; if (I2C_DRIVER_MAX == i) { printk(KERN_WARNING " i2c-core.o: unregister_driver: " "[%s] not found\n", driver->name); DRV_UNLOCK(); return -ENODEV; } /* Have a look at each adapter, if clients of this driver are still * attached. If so, detach them to be able to kill the driver * afterwards. */ DEB2(printk("i2c-core.o: unregister_driver - looking for clients.\n")); /* removing clients does not depend on the notify flag, else * invalid operation might (will!) result, when using stale client * pointers. */ ADAP_LOCK(); /* should be moved inside the if statement... */ for (k=0;k<I2C_ADAP_MAX;k++) { struct i2c_adapter *adap = adapters[k]; if (adap == NULL) /* skip empty entries. */ continue; DEB2(printk("i2c-core.o: examining adapter %s:\n", adap->name)); if (driver->flags & I2C_DF_DUMMY) { /* DUMMY drivers do not register their clients, so we have to * use a trick here: we call driver->attach_adapter to * *detach* it! Of course, each dummy driver should know about * this or hell will break loose... */ if ((res = driver->attach_adapter(adap))) { printk("i2c-core.o: while unregistering " "dummy driver %s, adapter %s could " "not be detached properly; driver " "not unloaded!",driver->name, adap->name); ADAP_UNLOCK(); return res; } } else { for (j=0;j<I2C_CLIENT_MAX;j++) { struct i2c_client *client = adap->clients[j]; if (client != NULL && client->driver == driver) { DEB2(printk("i2c-core.o: " "detaching client %s:\n", client->name)); if ((res = driver-> detach_client(client))) { printk("i2c-core.o: while " "unregistering driver " "`%s', the client at " "address %02x of adapter `%s' could not be detached; driver not unloaded!", driver->name, client->addr, adap->name); ADAP_UNLOCK(); return res; } } } } } ADAP_UNLOCK(); drivers[i] = NULL; driver_count--; DRV_UNLOCK(); DEB(printk("i2c-core.o: driver unregistered: %s\n",driver->name)); return 0;}int i2c_check_addr (struct i2c_adapter *adapter, int addr){ int i; for (i = 0; i < I2C_CLIENT_MAX ; i++) if (adapter->clients[i] && (adapter->clients[i]->addr == addr)) return -EBUSY; return 0;}int i2c_attach_client(struct i2c_client *client){ struct i2c_adapter *adapter = client->adapter; int i; if (i2c_check_addr(client->adapter,client->addr)) return -EBUSY; for (i = 0; i < I2C_CLIENT_MAX; i++) if (NULL == adapter->clients[i]) break; if (I2C_CLIENT_MAX == i) { printk(KERN_WARNING " i2c-core.o: attach_client(%s) - enlarge I2C_CLIENT_MAX.\n", client->name); return -ENOMEM; } adapter->clients[i] = client; adapter->client_count++; if (adapter->client_register) if (adapter->client_register(client)) printk("i2c-core.o: warning: client_register seems " "to have failed for client %02x at adapter %s\n", client->addr,adapter->name); DEB(printk("i2c-core.o: client [%s] registered to adapter [%s](pos. %d).\n", client->name, adapter->name,i)); if(client->flags & I2C_CLIENT_ALLOW_USE) client->usage_count = 0; return 0;}int i2c_detach_client(struct i2c_client *client){ struct i2c_adapter *adapter = client->adapter; int i,res; for (i = 0; i < I2C_CLIENT_MAX; i++) if (client == adapter->clients[i])
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -