📄 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> SMBus 2.0 support by Mark Studebaker <mdsxyz123@yahoo.com> and Jean Delvare <khali@linux-fr.org> *//* $Id: i2c-core.c,v 1.115 2005/11/05 21:00:58 khali Exp $ */#include <linux/module.h>#include <linux/kernel.h>#include <linux/errno.h>#include <linux/slab.h>#include <linux/proc_fs.h>#include <linux/init.h>#include "i2c.h"#include <asm/uaccess.h>/* ----- global defines ---------------------------------------------------- */#if I2C_LINUX_2_4_BINARY_COMPATIBILITY#define I2C_LOCK_LIST(adap) down(&adap->bus)#define I2C_UNLOCK_LIST(adap) up(&adap->bus)#else#define I2C_LOCK_LIST(adap) down(&adap->list)#define I2C_UNLOCK_LIST(adap) up(&adap->list)#endif#define DEB(x) if (i2c_debug>=1) x;#define DEB2(x) if (i2c_debug>=2) x;/* ----- global variables -------------------------------------------------- */DECLARE_MUTEX(core_lists);static struct i2c_adapter *adapters[I2C_ADAP_MAX];static struct i2c_driver *drivers[I2C_DRIVER_MAX];/**** debug level */static int i2c_debug;/* --------------------------------------------------- * /proc entry declarations *---------------------------------------------------- */#ifdef CONFIG_PROC_FSstatic 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,};static int i2cproc_register(struct i2c_adapter *adap, int bus);static void i2cproc_remove(int bus);#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 = 0; down(&core_lists); 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; } #ifdef CONFIG_PROC_FS res = i2cproc_register(adap, i); if (res<0) goto ERROR0;#endif /* def CONFIG_PROC_FS */ adapters[i] = adap; /* init data types */ init_MUTEX(&adap->bus);#if !I2C_LINUX_2_4_BINARY_COMPATIBILITY init_MUTEX(&adap->list);#endif /* inform drivers of new adapters */ 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); DEB(printk(KERN_DEBUG "i2c-core.o: adapter %s registered as adapter %d.\n", adap->name,i));ERROR0: up(&core_lists); return res;}int i2c_del_adapter(struct i2c_adapter *adap){ int i,j,res = 0; down(&core_lists); for (i = 0; i < I2C_ADAP_MAX; i++) if (adap == adapters[i]) break; if (I2C_ADAP_MAX == i) { printk(KERN_WARNING "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... */ 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(KERN_WARNING "i2c-core.o: can't detach adapter %s " "while detaching driver %s: driver not " "detached!\n", adap->name, drivers[j]->name); goto ERROR0; } /* detach any active clients. This must be done first, because * it can fail; in which case we give up. */ 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(KERN_ERR "i2c-core.o: adapter %s not " "unregistered, because client at " "address %02x can't be detached\n", adap->name, client->addr); goto ERROR0; } }#ifdef CONFIG_PROC_FS i2cproc_remove(i);#endif /* def CONFIG_PROC_FS */ adapters[i] = NULL; DEB(printk(KERN_DEBUG "i2c-core.o: adapter unregistered: %s\n",adap->name));ERROR0: up(&core_lists); 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; down(&core_lists); 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); up(&core_lists); return -ENOMEM; } drivers[i] = driver; DEB(printk(KERN_DEBUG "i2c-core.o: driver %s registered.\n",driver->name)); /* 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]); } up(&core_lists); return 0;}int i2c_del_driver(struct i2c_driver *driver){ int i,j,k,res = 0; down(&core_lists); 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); up(&core_lists); 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(KERN_DEBUG "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. */ for (k=0;k<I2C_ADAP_MAX;k++) { struct i2c_adapter *adap = adapters[k]; if (adap == NULL) /* skip empty entries. */ continue; DEB2(printk(KERN_DEBUG "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(KERN_WARNING "i2c-core.o: while unregistering " "dummy driver %s, adapter %s could " "not be detached properly; driver " "not unloaded!\n", driver->name, adap->name); goto ERROR0; } } else { for (j=0;j<I2C_CLIENT_MAX;j++) { struct i2c_client *client = adap->clients[j]; if (client != NULL && client->driver == driver) { DEB2(printk(KERN_DEBUG "i2c-core.o: " "detaching client %s:\n", client->name)); if ((res = driver-> detach_client(client))) { printk(KERN_ERR "i2c-core.o: while " "unregistering driver " "`%s', the client at " "address %02x of " "adapter `%s' could not " "be detached; driver " "not unloaded!\n", driver->name, client->addr, adap->name); goto ERROR0; } } } } } drivers[i] = NULL; DEB(printk(KERN_DEBUG "i2c-core.o: driver unregistered: %s\n",driver->name));ERROR0: up(&core_lists); return res;}static 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_check_addr (struct i2c_adapter *adapter, int addr){ int rval; I2C_LOCK_LIST(adapter); rval = __i2c_check_addr(adapter, addr); I2C_UNLOCK_LIST(adapter); return rval;}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; I2C_LOCK_LIST(adapter); 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); I2C_UNLOCK_LIST(adapter); return -ENOMEM; } adapter->clients[i] = client; I2C_UNLOCK_LIST(adapter); if (adapter->client_register) if (adapter->client_register(client)) printk(KERN_DEBUG "i2c-core.o: warning: client_register seems " "to have failed for client %02x at adapter %s\n", client->addr,adapter->name); DEB(printk(KERN_DEBUG "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; if( (client->flags & I2C_CLIENT_ALLOW_USE) && (client->usage_count>0)) return -EBUSY; if (adapter->client_unregister != NULL) if ((res = adapter->client_unregister(client))) { printk(KERN_ERR "i2c-core.o: client_unregister [%s] failed, " "client not detached\n", client->name); return res; } I2C_LOCK_LIST(adapter); for (i = 0; i < I2C_CLIENT_MAX; i++) if (client == adapter->clients[i]) break; if (I2C_CLIENT_MAX == i) { printk(KERN_WARNING " i2c-core.o: unregister_client " "[%s] not found\n", client->name); I2C_UNLOCK_LIST(adapter); return -ENODEV; } adapter->clients[i] = NULL; I2C_UNLOCK_LIST(adapter); DEB(printk(KERN_DEBUG "i2c-core.o: client [%s] unregistered.\n",client->name)); return 0;}static void i2c_inc_use_client(struct i2c_client *client){ if (client->driver->inc_use != NULL) client->driver->inc_use(client); if (client->adapter->inc_use != NULL) client->adapter->inc_use(client->adapter);}static void i2c_dec_use_client(struct i2c_client *client){ if (client->driver->dec_use != NULL) client->driver->dec_use(client); if (client->adapter->dec_use != NULL) client->adapter->dec_use(client->adapter);}struct i2c_client *i2c_get_client(int driver_id, int adapter_id, struct i2c_client *prev){ int i,j; /* Will iterate through the list of clients in each adapter of adapters-list in search for a client that matches the search criteria. driver_id or adapter_id are ignored if set to 0. If both are ignored this returns first client found. */ i = j = 0; /* set starting point */ if(prev) { if(!(prev->adapter)) return (struct i2c_client *) -EINVAL; for(j=0; j < I2C_ADAP_MAX; j++) if(prev->adapter == adapters[j]) break; /* invalid starting point? */ if (I2C_ADAP_MAX == j) { printk(KERN_WARNING " i2c-core.o: get_client adapter for client:[%s] not found\n", prev->name); return (struct i2c_client *) -ENODEV; } for(i=0; i < I2C_CLIENT_MAX; i++) if(prev == adapters[j]->clients[i]) break; /* invalid starting point? */ if (I2C_CLIENT_MAX == i) { printk(KERN_WARNING " i2c-core.o: get_client client:[%s] not found\n", prev->name); return (struct i2c_client *) -ENODEV; } i++; /* start from one after prev */ } for(; j < I2C_ADAP_MAX; j++) { if(!adapters[j]) continue; if(adapter_id && (adapters[j]->id != adapter_id)) continue; for(; i < I2C_CLIENT_MAX; i++) { if(!adapters[j]->clients[i]) continue; if(driver_id && (adapters[j]->clients[i]->driver->id != driver_id)) continue; if(adapters[j]->clients[i]->flags & I2C_CLIENT_ALLOW_USE) return adapters[j]->clients[i]; } i = 0; } return 0;}int i2c_use_client(struct i2c_client *client)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -