i2c--ioctl--主机控制器驱动(i2c_adapter)--外设驱动(i2c_driver)
生活随笔
收集整理的這篇文章主要介紹了
i2c--ioctl--主机控制器驱动(i2c_adapter)--外设驱动(i2c_driver)
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
updating...
i2c驅動目錄
[root@localhost i2c]# pwd /opt/FriendlyArm/mini2440/linux-2.6.32.2/drivers/i2c [root@localhost i2c]# tree //.有省略 |-- Kconfig |-- Makefile |-- algos | |-- Kconfig | |-- Makefile | |-- built-in.o | |-- i2c-algo-bit.c | |-- i2c-algo-pca.c | |-- i2c-algo-pcf.c | |-- i2c-algo-pcf.h | `-- modules.order |-- built-in.o |-- busses | |-- Kconfig | |-- Makefile | |-- ... | -- i2c-s3c2410.c |-- chips | |-- Kconfig | |-- Makefile | |-- ds1682.c | `-- tsl2550.c |-- i2c-boardinfo.c |-- i2c-core.c |-- i2c-core.h |-- i2c-core.o |-- i2c-dev.c
i2c-s3c2410.c里面有一個重要的全局結構體,
struct s3c24xx_i2c {spinlock_t lock;wait_queue_head_t wait;unsigned int suspended:1;struct i2c_msg *msg;unsigned int msg_num;unsigned int msg_idx;unsigned int msg_ptr;unsigned int tx_setup;unsigned int irq;enum s3c24xx_i2c_state state;unsigned long clkrate;void __iomem *regs;struct clk *clk;struct device *dev;struct resource *ioarea;struct i2c_adapter adap;#ifdef CONFIG_CPU_FREQstruct notifier_block freq_transition; #endif };
①i2c-s3c2410.c實現i2c總線驅動(即cpu的i2c主控制器和外接設備的i2c接口的通信),
1.需要填充i2c_adapter結構體。i2c_adapter結構體即i2c適配器即i2c主控制器,對于不同的硬件需要有不同的配置。然后向通過i2c核心添加這個適配器。
??? strlcpy(i2c->adap.name, "s3c2410-i2c", sizeof(i2c->adap.name));i2c->adap.owner?? = THIS_MODULE;i2c->adap.algo??? = &s3c24xx_i2c_algorithm;//指定adapter的通信算法結構體i2c->adap.retries = 2;i2c->adap.class?? = I2C_CLASS_HWMON | I2C_CLASS_SPD;i2c->tx_setup???? = 50;...ret = i2c_add_numbered_adapter(&i2c->adap);
2.需要填充i2c-algorithm結構體。i2c-algorithm結構體即指定i2c通信算法,
static const struct i2c_algorithm s3c24xx_i2c_algorithm = {.master_xfer = s3c24xx_i2c_xfer,.functionality = s3c24xx_i2c_func, }; 在s3c24xx_i2c_xfer里(其調用到的函數里)用到i2c_msg結構體訪問i2c.
②i2c-dev.c 實現i2c設備驅動。即實現i2c_driver結構體并向i2c核心注冊,還要實現設備自身的驅動如普通字符設備(或許也可以用misc設備)的驅動,以便與用戶空間交互。
如下
static int __init i2c_dev_init(void) {int res;printk(KERN_INFO "i2c /dev entries driver\n");res = register_chrdev(I2C_MAJOR, "i2c", &i2cdev_fops);// .........return res; } 在i2cdev_attach_adapter里面向sysfs文件系統寫入信息。以便i2c_adapter”熱插拔“的時候動態創建節點.
static int i2cdev_attach_adapter(struct i2c_adapter *adap) {struct i2c_dev *i2c_dev;int res;i2c_dev = get_free_i2c_dev(adap);if (IS_ERR(i2c_dev))return PTR_ERR(i2c_dev);/* register this i2c device with the driver core */i2c_dev->dev = device_create(i2c_dev_class, &adap->dev,MKDEV(I2C_MAJOR, adap->nr), NULL,"i2c-%d", adap->nr); ......... }
i2c總線驅動(對應i2c_adapter)和i2c設備驅動(對應i2c_driver)是通過i2c核心關聯起來的。當有一個i2c_adapter通過i2c核心添加,i2c核心會自動匹配對應的i2c_driver,調用i2c_driver的probe函數....不過在i2c-dev.c 沒有實現這個函數指針,而是實現了attach_adapter函數指針。
****************************************************************************************************************************************
在i2c-dev.h中定義了如下ioctl命令:
/* /dev/i2c-X ioctl commands. The ioctl's parameter is always an* unsigned long, except for:* - I2C_FUNCS, takes pointer to an unsigned long* - I2C_RDWR, takes pointer to struct i2c_rdwr_ioctl_data* - I2C_SMBUS, takes pointer to struct i2c_smbus_ioctl_data*/ #define I2C_RETRIES 0x0701 /* number of times a device address shouldbe polled when not acknowledging */ #define I2C_TIMEOUT 0x0702 /* set timeout in units of 10 ms *//* NOTE: Slave address is 7 or 10 bits, but 10-bit addresses* are NOT supported! (due to code brokenness)*/ #define I2C_SLAVE 0x0703 /* Use this slave address */ #define I2C_SLAVE_FORCE 0x0706 /* Use this slave address, even if itis already in use by a driver! */ #define I2C_TENBIT 0x0704 /* 0 for 7 bit addrs, != 0 for 10 bit */#define I2C_FUNCS 0x0705 /* Get the adapter functionality mask */#define I2C_RDWR 0x0707 /* Combined R/W transfer (one STOP only) */#define I2C_PEC 0x0708 /* != 0 to use PEC with SMBus */ #define I2C_SMBUS 0x0720 /* SMBus transfer */
當在用戶側使用ioctl(fd,?I2C_RDWR,?(unsigned?long)&e2prom_data);讀取數據時,會調用驅動的ioctl設備方法
i2c-dev.c
static long i2cdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) {struct i2c_client *client = (struct i2c_client *)file->private_data;unsigned long funcs;dev_dbg(&client->adapter->dev, "ioctl, cmd=0x%02x, arg=0x%02lx\n",cmd, arg);switch ( cmd ) {case I2C_SLAVE:case I2C_SLAVE_FORCE:/* NOTE: devices set up to work with "new style" drivers* can't use I2C_SLAVE, even when the device node is not* bound to a driver. Only I2C_SLAVE_FORCE will work.** Setting the PEC flag here won't affect kernel drivers,* which will be using the i2c_client node registered with* the driver model core. Likewise, when that client has* the PEC flag already set, the i2c-dev driver won't see* (or use) this setting.*/if ((arg > 0x3ff) ||(((client->flags & I2C_M_TEN) == 0) && arg > 0x7f))return -EINVAL;if (cmd == I2C_SLAVE && i2cdev_check_addr(client->adapter, arg))return -EBUSY;/* REVISIT: address could become busy later */client->addr = arg;return 0;case I2C_TENBIT:if (arg)client->flags |= I2C_M_TEN;elseclient->flags &= ~I2C_M_TEN;return 0;case I2C_PEC:if (arg)client->flags |= I2C_CLIENT_PEC;elseclient->flags &= ~I2C_CLIENT_PEC;return 0;case I2C_FUNCS:funcs = i2c_get_functionality(client->adapter);return put_user(funcs, (unsigned long __user *)arg);case I2C_RDWR:return i2cdev_ioctl_rdrw(client, arg);case I2C_SMBUS:return i2cdev_ioctl_smbus(client, arg);case I2C_RETRIES:client->adapter->retries = arg;break;case I2C_TIMEOUT:/* For historical reasons, user-space sets the timeout* value in units of 10 ms.*/client->adapter->timeout = msecs_to_jiffies(arg * 10);break;default:/* NOTE: returning a fault code here could cause trouble* in buggy userspace code. Some old kernel bugs returned* zero in this case, and userspace code might accidentally* have depended on that bug.*/return -ENOTTY;}return 0; }而?
?case I2C_RDWR:
?return i2cdev_ioctl_rdrw(client, arg);
先看client是什么東東,line 3有
struct i2c_client *client = (struct i2c_client *)file->private_data;
接著看一下打開設備時的動作,如下
static int i2cdev_open(struct inode *inode, struct file *file) {unsigned int minor = iminor(inode);struct i2c_client *client;struct i2c_adapter *adap;struct i2c_dev *i2c_dev;int ret = 0;lock_kernel();i2c_dev = i2c_dev_get_by_minor(minor);if (!i2c_dev) {ret = -ENODEV;goto out;}adap = i2c_get_adapter(i2c_dev->adap->nr);if (!adap) {ret = -ENODEV;goto out;}/* This creates an anonymous i2c_client, which may later be* pointed to some address using I2C_SLAVE or I2C_SLAVE_FORCE.** This client is ** NEVER REGISTERED ** with the driver model* or I2C core code!! It just holds private copies of addressing* information and maybe a PEC flag.*/client = kzalloc(sizeof(*client), GFP_KERNEL);if (!client) {i2c_put_adapter(adap);ret = -ENOMEM;goto out;}snprintf(client->name, I2C_NAME_SIZE, "i2c-dev %d", adap->nr);client->driver = &i2cdev_driver;//client綁定i2c_driverclient->adapter = adap;//client綁定i2c_adapterfile->private_data = client;//一般都是這樣搞的,文件私有數據指向clientout:unlock_kernel();return ret; }其中這2行
??? client->driver = &i2cdev_driver;//client綁定i2c_driver
?? ?client->adapter = adap;//client綁定i2c_adapter
很明確的說明了client就是把driver(i2c-dev.c)和adapter(i2c-s3c2410.c)關聯
回到i2cdev_ioctl_rdrw(client, arg);源碼是
static noinline int i2cdev_ioctl_rdrw(struct i2c_client *client,unsigned long arg) {struct i2c_rdwr_ioctl_data rdwr_arg;struct i2c_msg *rdwr_pa;u8 __user **data_ptrs;int i, res;if (copy_from_user(&rdwr_arg,(struct i2c_rdwr_ioctl_data __user *)arg,sizeof(rdwr_arg)))return -EFAULT;/* Put an arbitrary limit on the number of messages that can* be sent at once */if (rdwr_arg.nmsgs > I2C_RDRW_IOCTL_MAX_MSGS)return -EINVAL;rdwr_pa = (struct i2c_msg *)kmalloc(rdwr_arg.nmsgs * sizeof(struct i2c_msg),GFP_KERNEL);if (!rdwr_pa)return -ENOMEM;if (copy_from_user(rdwr_pa, rdwr_arg.msgs,rdwr_arg.nmsgs * sizeof(struct i2c_msg))) {kfree(rdwr_pa);return -EFAULT;}data_ptrs = kmalloc(rdwr_arg.nmsgs * sizeof(u8 __user *), GFP_KERNEL);if (data_ptrs == NULL) {kfree(rdwr_pa);return -ENOMEM;}res = 0;for (i = 0; i < rdwr_arg.nmsgs; i++) {/* Limit the size of the message to a sane amount;* and don't let length change either. */if ((rdwr_pa[i].len > 8192) ||(rdwr_pa[i].flags & I2C_M_RECV_LEN)) {res = -EINVAL;break;}data_ptrs[i] = (u8 __user *)rdwr_pa[i].buf;rdwr_pa[i].buf = kmalloc(rdwr_pa[i].len, GFP_KERNEL);if (rdwr_pa[i].buf == NULL) {res = -ENOMEM;break;}if (copy_from_user(rdwr_pa[i].buf, data_ptrs[i],rdwr_pa[i].len)) {++i; /* Needs to be kfreed too */res = -EFAULT;break;}}if (res < 0) {int j;for (j = 0; j < i; ++j)kfree(rdwr_pa[j].buf);kfree(data_ptrs);kfree(rdwr_pa);return res;}res = i2c_transfer(client->adapter, rdwr_pa, rdwr_arg.nmsgs);//while (i-- > 0) {if (res >= 0 && (rdwr_pa[i].flags & I2C_M_RD)) {if (copy_to_user(data_ptrs[i], rdwr_pa[i].buf,rdwr_pa[i].len))res = -EFAULT;}kfree(rdwr_pa[i].buf);}kfree(data_ptrs);kfree(rdwr_pa);return res; } line 68調用了i2c? core提供的一個函數i2c_transfer(client->adapter, rdwr_pa, rdwr_arg.nmsgs);看下這個函數的注釋
是一個要在設備驅動(i2c-dev.c)里面調用以便訪問總線驅動(i2c-s3c2410.c)的接口函數。
/* ----------------------------------------------------* the functional interface to the i2c busses.* ----------------------------------------------------*//*** i2c_transfer - execute a single or combined I2C message* @adap: Handle to I2C bus* @msgs: One or more messages to execute before STOP is issued to* terminate the operation; each message begins with a START.* @num: Number of messages to be executed.** Returns negative errno, else the number of messages executed.** Note that there is no requirement that each message be sent to* the same slave address, although that is the most common model.*/ int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)調用此函數時,會將設備驅動的i2c_msg傳輸給總線驅動,i2c核心自動調用總線驅動里的master_xfer函數指針指向的實際執行數據傳輸的函數將i2c_msg在i2c主控制器與i2c外設之間傳輸。
static const struct i2c_algorithm s3c24xx_i2c_algorithm = {.master_xfer = s3c24xx_i2c_xfer,.functionality = s3c24xx_i2c_func, };
使用i2c subsystem的這種分離設計是為了cpu平臺更換時可以保持與用戶的接口不變:
比如有1個i2c_driver(設備驅動,處理用戶接口)和5個i2c_adapter(代表5個cpu平臺的i2c主控制器驅動,處理與特定設備的寄存器配置和信息傳輸的具體實現)。在換平臺的時候,只需要實現i2c_adapter部分,而i2c_driver不必改變,因為它最終調用i2c核心的i2c_transfer()與i2c_adapter交互。
而如果不分離。即將用戶接口的實現(比如字符設備)和i2c數據傳輸的實現夠放在一個文件里面------傳統的做法,則公司在換cpu平臺的時候,就需要修改整個驅動文件。
這種分離設計也可以實現cpu平臺不變外設更換時,可以保持同一套用戶接口。此時仍然是只需要修改i2c_adapter或者如果外設特性能完全相同則不必修改。
但對于專注于一個cpu一個外設的廠家而言這種分離設計就有點浪費了,,,
并且這種設計也遵守內核實現機制(套路)的思想。比如i2c子系統為已經定義了結構體i2c_msg,在用戶空間只需填充i2c_msg然后發送。
綜上可知:
即內核實現怎么發,用戶決定發什么。而內核實現怎么發的同時,還考慮到用戶的換平臺的情況,所以采用分離設計將用戶接口與具體的怎么發分離。
雖然i2c子系統是按照這個思想設計的,但和同樣是這個設計思想的spi子系統對比,卻沒有spi的明朗。
--2011年12月4日
幾個重要的結構體
i2c.h
/*** struct i2c_driver - represent an I2C device driver* @class: What kind of i2c device we instantiate (for detect)* @attach_adapter: Callback for bus addition (for legacy drivers)* @detach_adapter: Callback for bus removal (for legacy drivers)* @probe: Callback for device binding* @remove: Callback for device unbinding* @shutdown: Callback for device shutdown* @suspend: Callback for device suspend* @resume: Callback for device resume* @command: Callback for bus-wide signaling (optional)* @driver: Device driver model driver* @id_table: List of I2C devices supported by this driver* @detect: Callback for device detection* @address_data: The I2C addresses to probe, ignore or force (for detect)* @clients: List of detected clients we created (for i2c-core use only)** The driver.owner field should be set to the module owner of this driver.* The driver.name field should be set to the name of this driver.** For automatic device detection, both @detect and @address_data must* be defined. @class should also be set, otherwise only devices forced* with module parameters will be created. The detect function must* fill at least the name field of the i2c_board_info structure it is* handed upon successful detection, and possibly also the flags field.** If @detect is missing, the driver will still work fine for enumerated* devices. Detected devices simply won't be supported. This is expected* for the many I2C/SMBus devices which can't be detected reliably, and* the ones which can always be enumerated in practice.** The i2c_client structure which is handed to the @detect callback is* not a real i2c_client. It is initialized just enough so that you can* call i2c_smbus_read_byte_data and friends on it. Don't do anything* else with it. In particular, calling dev_dbg and friends on it is* not allowed.*/ struct i2c_driver {unsigned int class;/* Notifies the driver that a new bus has appeared or is about to be* removed. You should avoid using this if you can, it will probably* be removed in a near future.*/int (*attach_adapter)(struct i2c_adapter *);int (*detach_adapter)(struct i2c_adapter *);/* Standard driver model interfaces */int (*probe)(struct i2c_client *, const struct i2c_device_id *);int (*remove)(struct i2c_client *);/* driver model interfaces that don't relate to enumeration */void (*shutdown)(struct i2c_client *);int (*suspend)(struct i2c_client *, pm_message_t mesg);int (*resume)(struct i2c_client *);/* a ioctl like command that can be used to perform specific functions* with the device.*/int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);struct device_driver driver;const struct i2c_device_id *id_table;/* Device detection callback for automatic device creation */int (*detect)(struct i2c_client *, int kind, struct i2c_board_info *);const struct i2c_client_address_data *address_data;struct list_head clients; }; #define to_i2c_driver(d) container_of(d, struct i2c_driver, driver)
/*** struct i2c_client - represent an I2C slave device* @flags: I2C_CLIENT_TEN indicates the device uses a ten bit chip address;* I2C_CLIENT_PEC indicates it uses SMBus Packet Error Checking* @addr: Address used on the I2C bus connected to the parent adapter.* @name: Indicates the type of the device, usually a chip name that's* generic enough to hide second-sourcing and compatible revisions.* @adapter: manages the bus segment hosting this I2C device* @driver: device's driver, hence pointer to access routines* @dev: Driver model device node for the slave.* @irq: indicates the IRQ generated by this device (if any)* @detected: member of an i2c_driver.clients list or i2c-core's* userspace_devices list** An i2c_client identifies a single device (i.e. chip) connected to an* i2c bus. The behaviour exposed to Linux is defined by the driver* managing the device.*/ struct i2c_client {unsigned short flags; /* div., see below */unsigned short addr; /* chip address - NOTE: 7bit *//* addresses are stored in the *//* _LOWER_ 7 bits */char name[I2C_NAME_SIZE];struct i2c_adapter *adapter; /* the adapter we sit on */struct i2c_driver *driver; /* and our access routines */struct device dev; /* the device structure */int irq; /* irq issued by device */struct list_head detected; }; #define to_i2c_client(d) container_of(d, struct i2c_client, dev)
/*** struct i2c_msg - an I2C transaction segment beginning with START* @addr: Slave address, either seven or ten bits. When this is a ten* bit address, I2C_M_TEN must be set in @flags and the adapter* must support I2C_FUNC_10BIT_ADDR.* @flags: I2C_M_RD is handled by all adapters. No other flags may be* provided unless the adapter exported the relevant I2C_FUNC_** flags through i2c_check_functionality().* @len: Number of data bytes in @buf being read from or written to the* I2C slave address. For read transactions where I2C_M_RECV_LEN* is set, the caller guarantees that this buffer can hold up to* 32 bytes in addition to the initial length byte sent by the* slave (plus, if used, the SMBus PEC); and this value will be* incremented by the number of block data bytes received.* @buf: The buffer into which data is read, or from which it's written.** An i2c_msg is the low level representation of one segment of an I2C* transaction. It is visible to drivers in the @i2c_transfer() procedure,* to userspace from i2c-dev, and to I2C adapter drivers through the* @i2c_adapter.@master_xfer() method.** Except when I2C "protocol mangling" is used, all I2C adapters implement* the standard rules for I2C transactions. Each transaction begins with a* START. That is followed by the slave address, and a bit encoding read* versus write. Then follow all the data bytes, possibly including a byte* with SMBus PEC. The transfer terminates with a NAK, or when all those* bytes have been transferred and ACKed. If this is the last message in a* group, it is followed by a STOP. Otherwise it is followed by the next* @i2c_msg transaction segment, beginning with a (repeated) START.** Alternatively, when the adapter supports I2C_FUNC_PROTOCOL_MANGLING then* passing certain @flags may have changed those standard protocol behaviors.* Those flags are only for use with broken/nonconforming slaves, and with* adapters which are known to support the specific mangling options they* need (one or more of IGNORE_NAK, NO_RD_ACK, NOSTART, and REV_DIR_ADDR).*/ struct i2c_msg {__u16 addr; /* slave address */__u16 flags; #define I2C_M_TEN 0x0010 /* this is a ten bit chip address */ #define I2C_M_RD 0x0001 /* read data, from slave to master */ #define I2C_M_NOSTART 0x4000 /* if I2C_FUNC_PROTOCOL_MANGLING */ #define I2C_M_REV_DIR_ADDR 0x2000 /* if I2C_FUNC_PROTOCOL_MANGLING */ #define I2C_M_IGNORE_NAK 0x1000 /* if I2C_FUNC_PROTOCOL_MANGLING */ #define I2C_M_NO_RD_ACK 0x0800 /* if I2C_FUNC_PROTOCOL_MANGLING */ #define I2C_M_RECV_LEN 0x0400 /* length will be first received byte */__u16 len; /* msg length */__u8 *buf; /* pointer to msg data */ };
/** i2c_adapter is the structure used to identify a physical i2c bus along* with the access algorithms necessary to access it.*/ struct i2c_adapter {struct module *owner;unsigned int id;unsigned int class; /* classes to allow probing for */const struct i2c_algorithm *algo; /* the algorithm to access the bus */void *algo_data;/* data fields that are valid for all devices */u8 level; /* nesting level for lockdep */struct mutex bus_lock;int timeout; /* in jiffies */int retries;struct device dev; /* the adapter device */int nr;char name[48];struct completion dev_released; }; #define to_i2c_adapter(d) container_of(d, struct i2c_adapter, dev)
/** The following structs are for those who like to implement new bus drivers:* i2c_algorithm is the interface to a class of hardware solutions which can* be addressed using the same bus algorithms - i.e. bit-banging or the PCF8584* to name two of the most common.*/ struct i2c_algorithm {/* If an adapter algorithm can't do I2C-level access, set master_xferto NULL. If an adapter algorithm can do SMBus access, setsmbus_xfer. If set to NULL, the SMBus protocol is simulatedusing common I2C messages *//* master_xfer should return the number of messages successfullyprocessed, or a negative value on error */int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,int num);int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,unsigned short flags, char read_write,u8 command, int size, union i2c_smbus_data *data);/* To determine what the adapter supports */u32 (*functionality) (struct i2c_adapter *); };
/*** struct i2c_board_info - template for device creation* @type: chip type, to initialize i2c_client.name* @flags: to initialize i2c_client.flags* @addr: stored in i2c_client.addr* @platform_data: stored in i2c_client.dev.platform_data* @archdata: copied into i2c_client.dev.archdata* @irq: stored in i2c_client.irq** I2C doesn't actually support hardware probing, although controllers and* devices may be able to use I2C_SMBUS_QUICK to tell whether or not there's* a device at a given address. Drivers commonly need more information than* that, such as chip type, configuration, associated IRQ, and so on.** i2c_board_info is used to build tables of information listing I2C devices* that are present. This information is used to grow the driver model tree.* For mainboards this is done statically using i2c_register_board_info();* bus numbers identify adapters that aren't yet available. For add-on boards,* i2c_new_device() does this dynamically with the adapter already known.*/ struct i2c_board_info {char type[I2C_NAME_SIZE];unsigned short flags;unsigned short addr;void *platform_data;struct dev_archdata *archdata;int irq; };
s3c2440的i2c主控制器僅5個寄存器,在linux內核中卻搞得這么繁雜,,,,,
IICCON 0x54000000 R/W IIC-Bus control register
IICSTAT 0x54000004 R/W IIC-Bus control/status register
IICADD 0x54000008 R/W IIC-Bus address register
IICDS 0x5400000C R/W IIC-Bus transmit/receive data shift register
IICLC 0x54000010 R/W IIC-Bus multi-master line control register
并且micro2440的eeprom的i2c驅動最外層是platform總線然后是i2c總線然后才是面向用戶訪問的字符設備。。。?
目前的一點個人理解,有待更新。。。
存在的問題,在insmod時i2c核心具體怎么匹配外設驅動(設備)和總線(控制器驅動),在ioctl時i2c核心具體怎么處理(匹配)外設驅動和總線,i2c具體數據的傳輸。有時間再去細讀。
對總線設備驅動模型認識不清,對sysfs認識不清,對mdev如何利用sysfs創建設備節點的過程不清。
refer to
http://blog.csdn.net/cjok376240497/article/details/6982883
http://blog.csdn.net/hongtao_liu/article/details/4964244
http://blog.csdn.net/hongtao_liu/article/details/5260739
i2c驅動目錄
[root@localhost i2c]# pwd /opt/FriendlyArm/mini2440/linux-2.6.32.2/drivers/i2c [root@localhost i2c]# tree //.有省略 |-- Kconfig |-- Makefile |-- algos | |-- Kconfig | |-- Makefile | |-- built-in.o | |-- i2c-algo-bit.c | |-- i2c-algo-pca.c | |-- i2c-algo-pcf.c | |-- i2c-algo-pcf.h | `-- modules.order |-- built-in.o |-- busses | |-- Kconfig | |-- Makefile | |-- ... | -- i2c-s3c2410.c |-- chips | |-- Kconfig | |-- Makefile | |-- ds1682.c | `-- tsl2550.c |-- i2c-boardinfo.c |-- i2c-core.c |-- i2c-core.h |-- i2c-core.o |-- i2c-dev.c
i2c-s3c2410.c里面有一個重要的全局結構體,
struct s3c24xx_i2c {spinlock_t lock;wait_queue_head_t wait;unsigned int suspended:1;struct i2c_msg *msg;unsigned int msg_num;unsigned int msg_idx;unsigned int msg_ptr;unsigned int tx_setup;unsigned int irq;enum s3c24xx_i2c_state state;unsigned long clkrate;void __iomem *regs;struct clk *clk;struct device *dev;struct resource *ioarea;struct i2c_adapter adap;#ifdef CONFIG_CPU_FREQstruct notifier_block freq_transition; #endif };
①i2c-s3c2410.c實現i2c總線驅動(即cpu的i2c主控制器和外接設備的i2c接口的通信),
1.需要填充i2c_adapter結構體。i2c_adapter結構體即i2c適配器即i2c主控制器,對于不同的硬件需要有不同的配置。然后向通過i2c核心添加這個適配器。
??? strlcpy(i2c->adap.name, "s3c2410-i2c", sizeof(i2c->adap.name));i2c->adap.owner?? = THIS_MODULE;i2c->adap.algo??? = &s3c24xx_i2c_algorithm;//指定adapter的通信算法結構體i2c->adap.retries = 2;i2c->adap.class?? = I2C_CLASS_HWMON | I2C_CLASS_SPD;i2c->tx_setup???? = 50;...ret = i2c_add_numbered_adapter(&i2c->adap);
2.需要填充i2c-algorithm結構體。i2c-algorithm結構體即指定i2c通信算法,
static const struct i2c_algorithm s3c24xx_i2c_algorithm = {.master_xfer = s3c24xx_i2c_xfer,.functionality = s3c24xx_i2c_func, }; 在s3c24xx_i2c_xfer里(其調用到的函數里)用到i2c_msg結構體訪問i2c.
②i2c-dev.c 實現i2c設備驅動。即實現i2c_driver結構體并向i2c核心注冊,還要實現設備自身的驅動如普通字符設備(或許也可以用misc設備)的驅動,以便與用戶空間交互。
如下
static int __init i2c_dev_init(void) {int res;printk(KERN_INFO "i2c /dev entries driver\n");res = register_chrdev(I2C_MAJOR, "i2c", &i2cdev_fops);// .........return res; } 在i2cdev_attach_adapter里面向sysfs文件系統寫入信息。以便i2c_adapter”熱插拔“的時候動態創建節點.
static int i2cdev_attach_adapter(struct i2c_adapter *adap) {struct i2c_dev *i2c_dev;int res;i2c_dev = get_free_i2c_dev(adap);if (IS_ERR(i2c_dev))return PTR_ERR(i2c_dev);/* register this i2c device with the driver core */i2c_dev->dev = device_create(i2c_dev_class, &adap->dev,MKDEV(I2C_MAJOR, adap->nr), NULL,"i2c-%d", adap->nr); ......... }
i2c總線驅動(對應i2c_adapter)和i2c設備驅動(對應i2c_driver)是通過i2c核心關聯起來的。當有一個i2c_adapter通過i2c核心添加,i2c核心會自動匹配對應的i2c_driver,調用i2c_driver的probe函數....不過在i2c-dev.c 沒有實現這個函數指針,而是實現了attach_adapter函數指針。
****************************************************************************************************************************************
在i2c-dev.h中定義了如下ioctl命令:
/* /dev/i2c-X ioctl commands. The ioctl's parameter is always an* unsigned long, except for:* - I2C_FUNCS, takes pointer to an unsigned long* - I2C_RDWR, takes pointer to struct i2c_rdwr_ioctl_data* - I2C_SMBUS, takes pointer to struct i2c_smbus_ioctl_data*/ #define I2C_RETRIES 0x0701 /* number of times a device address shouldbe polled when not acknowledging */ #define I2C_TIMEOUT 0x0702 /* set timeout in units of 10 ms *//* NOTE: Slave address is 7 or 10 bits, but 10-bit addresses* are NOT supported! (due to code brokenness)*/ #define I2C_SLAVE 0x0703 /* Use this slave address */ #define I2C_SLAVE_FORCE 0x0706 /* Use this slave address, even if itis already in use by a driver! */ #define I2C_TENBIT 0x0704 /* 0 for 7 bit addrs, != 0 for 10 bit */#define I2C_FUNCS 0x0705 /* Get the adapter functionality mask */#define I2C_RDWR 0x0707 /* Combined R/W transfer (one STOP only) */#define I2C_PEC 0x0708 /* != 0 to use PEC with SMBus */ #define I2C_SMBUS 0x0720 /* SMBus transfer */
當在用戶側使用ioctl(fd,?I2C_RDWR,?(unsigned?long)&e2prom_data);讀取數據時,會調用驅動的ioctl設備方法
i2c-dev.c
static long i2cdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) {struct i2c_client *client = (struct i2c_client *)file->private_data;unsigned long funcs;dev_dbg(&client->adapter->dev, "ioctl, cmd=0x%02x, arg=0x%02lx\n",cmd, arg);switch ( cmd ) {case I2C_SLAVE:case I2C_SLAVE_FORCE:/* NOTE: devices set up to work with "new style" drivers* can't use I2C_SLAVE, even when the device node is not* bound to a driver. Only I2C_SLAVE_FORCE will work.** Setting the PEC flag here won't affect kernel drivers,* which will be using the i2c_client node registered with* the driver model core. Likewise, when that client has* the PEC flag already set, the i2c-dev driver won't see* (or use) this setting.*/if ((arg > 0x3ff) ||(((client->flags & I2C_M_TEN) == 0) && arg > 0x7f))return -EINVAL;if (cmd == I2C_SLAVE && i2cdev_check_addr(client->adapter, arg))return -EBUSY;/* REVISIT: address could become busy later */client->addr = arg;return 0;case I2C_TENBIT:if (arg)client->flags |= I2C_M_TEN;elseclient->flags &= ~I2C_M_TEN;return 0;case I2C_PEC:if (arg)client->flags |= I2C_CLIENT_PEC;elseclient->flags &= ~I2C_CLIENT_PEC;return 0;case I2C_FUNCS:funcs = i2c_get_functionality(client->adapter);return put_user(funcs, (unsigned long __user *)arg);case I2C_RDWR:return i2cdev_ioctl_rdrw(client, arg);case I2C_SMBUS:return i2cdev_ioctl_smbus(client, arg);case I2C_RETRIES:client->adapter->retries = arg;break;case I2C_TIMEOUT:/* For historical reasons, user-space sets the timeout* value in units of 10 ms.*/client->adapter->timeout = msecs_to_jiffies(arg * 10);break;default:/* NOTE: returning a fault code here could cause trouble* in buggy userspace code. Some old kernel bugs returned* zero in this case, and userspace code might accidentally* have depended on that bug.*/return -ENOTTY;}return 0; }而?
?case I2C_RDWR:
?return i2cdev_ioctl_rdrw(client, arg);
先看client是什么東東,line 3有
struct i2c_client *client = (struct i2c_client *)file->private_data;
接著看一下打開設備時的動作,如下
static int i2cdev_open(struct inode *inode, struct file *file) {unsigned int minor = iminor(inode);struct i2c_client *client;struct i2c_adapter *adap;struct i2c_dev *i2c_dev;int ret = 0;lock_kernel();i2c_dev = i2c_dev_get_by_minor(minor);if (!i2c_dev) {ret = -ENODEV;goto out;}adap = i2c_get_adapter(i2c_dev->adap->nr);if (!adap) {ret = -ENODEV;goto out;}/* This creates an anonymous i2c_client, which may later be* pointed to some address using I2C_SLAVE or I2C_SLAVE_FORCE.** This client is ** NEVER REGISTERED ** with the driver model* or I2C core code!! It just holds private copies of addressing* information and maybe a PEC flag.*/client = kzalloc(sizeof(*client), GFP_KERNEL);if (!client) {i2c_put_adapter(adap);ret = -ENOMEM;goto out;}snprintf(client->name, I2C_NAME_SIZE, "i2c-dev %d", adap->nr);client->driver = &i2cdev_driver;//client綁定i2c_driverclient->adapter = adap;//client綁定i2c_adapterfile->private_data = client;//一般都是這樣搞的,文件私有數據指向clientout:unlock_kernel();return ret; }其中這2行
??? client->driver = &i2cdev_driver;//client綁定i2c_driver
?? ?client->adapter = adap;//client綁定i2c_adapter
很明確的說明了client就是把driver(i2c-dev.c)和adapter(i2c-s3c2410.c)關聯
回到i2cdev_ioctl_rdrw(client, arg);源碼是
static noinline int i2cdev_ioctl_rdrw(struct i2c_client *client,unsigned long arg) {struct i2c_rdwr_ioctl_data rdwr_arg;struct i2c_msg *rdwr_pa;u8 __user **data_ptrs;int i, res;if (copy_from_user(&rdwr_arg,(struct i2c_rdwr_ioctl_data __user *)arg,sizeof(rdwr_arg)))return -EFAULT;/* Put an arbitrary limit on the number of messages that can* be sent at once */if (rdwr_arg.nmsgs > I2C_RDRW_IOCTL_MAX_MSGS)return -EINVAL;rdwr_pa = (struct i2c_msg *)kmalloc(rdwr_arg.nmsgs * sizeof(struct i2c_msg),GFP_KERNEL);if (!rdwr_pa)return -ENOMEM;if (copy_from_user(rdwr_pa, rdwr_arg.msgs,rdwr_arg.nmsgs * sizeof(struct i2c_msg))) {kfree(rdwr_pa);return -EFAULT;}data_ptrs = kmalloc(rdwr_arg.nmsgs * sizeof(u8 __user *), GFP_KERNEL);if (data_ptrs == NULL) {kfree(rdwr_pa);return -ENOMEM;}res = 0;for (i = 0; i < rdwr_arg.nmsgs; i++) {/* Limit the size of the message to a sane amount;* and don't let length change either. */if ((rdwr_pa[i].len > 8192) ||(rdwr_pa[i].flags & I2C_M_RECV_LEN)) {res = -EINVAL;break;}data_ptrs[i] = (u8 __user *)rdwr_pa[i].buf;rdwr_pa[i].buf = kmalloc(rdwr_pa[i].len, GFP_KERNEL);if (rdwr_pa[i].buf == NULL) {res = -ENOMEM;break;}if (copy_from_user(rdwr_pa[i].buf, data_ptrs[i],rdwr_pa[i].len)) {++i; /* Needs to be kfreed too */res = -EFAULT;break;}}if (res < 0) {int j;for (j = 0; j < i; ++j)kfree(rdwr_pa[j].buf);kfree(data_ptrs);kfree(rdwr_pa);return res;}res = i2c_transfer(client->adapter, rdwr_pa, rdwr_arg.nmsgs);//while (i-- > 0) {if (res >= 0 && (rdwr_pa[i].flags & I2C_M_RD)) {if (copy_to_user(data_ptrs[i], rdwr_pa[i].buf,rdwr_pa[i].len))res = -EFAULT;}kfree(rdwr_pa[i].buf);}kfree(data_ptrs);kfree(rdwr_pa);return res; } line 68調用了i2c? core提供的一個函數i2c_transfer(client->adapter, rdwr_pa, rdwr_arg.nmsgs);看下這個函數的注釋
是一個要在設備驅動(i2c-dev.c)里面調用以便訪問總線驅動(i2c-s3c2410.c)的接口函數。
/* ----------------------------------------------------* the functional interface to the i2c busses.* ----------------------------------------------------*//*** i2c_transfer - execute a single or combined I2C message* @adap: Handle to I2C bus* @msgs: One or more messages to execute before STOP is issued to* terminate the operation; each message begins with a START.* @num: Number of messages to be executed.** Returns negative errno, else the number of messages executed.** Note that there is no requirement that each message be sent to* the same slave address, although that is the most common model.*/ int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)調用此函數時,會將設備驅動的i2c_msg傳輸給總線驅動,i2c核心自動調用總線驅動里的master_xfer函數指針指向的實際執行數據傳輸的函數將i2c_msg在i2c主控制器與i2c外設之間傳輸。
static const struct i2c_algorithm s3c24xx_i2c_algorithm = {.master_xfer = s3c24xx_i2c_xfer,.functionality = s3c24xx_i2c_func, };
使用i2c subsystem的這種分離設計是為了cpu平臺更換時可以保持與用戶的接口不變:
比如有1個i2c_driver(設備驅動,處理用戶接口)和5個i2c_adapter(代表5個cpu平臺的i2c主控制器驅動,處理與特定設備的寄存器配置和信息傳輸的具體實現)。在換平臺的時候,只需要實現i2c_adapter部分,而i2c_driver不必改變,因為它最終調用i2c核心的i2c_transfer()與i2c_adapter交互。
而如果不分離。即將用戶接口的實現(比如字符設備)和i2c數據傳輸的實現夠放在一個文件里面------傳統的做法,則公司在換cpu平臺的時候,就需要修改整個驅動文件。
這種分離設計也可以實現cpu平臺不變外設更換時,可以保持同一套用戶接口。此時仍然是只需要修改i2c_adapter或者如果外設特性能完全相同則不必修改。
但對于專注于一個cpu一個外設的廠家而言這種分離設計就有點浪費了,,,
并且這種設計也遵守內核實現機制(套路)的思想。比如i2c子系統為已經定義了結構體i2c_msg,在用戶空間只需填充i2c_msg然后發送。
綜上可知:
即內核實現怎么發,用戶決定發什么。而內核實現怎么發的同時,還考慮到用戶的換平臺的情況,所以采用分離設計將用戶接口與具體的怎么發分離。
雖然i2c子系統是按照這個思想設計的,但和同樣是這個設計思想的spi子系統對比,卻沒有spi的明朗。
--2011年12月4日
幾個重要的結構體
i2c.h
/*** struct i2c_driver - represent an I2C device driver* @class: What kind of i2c device we instantiate (for detect)* @attach_adapter: Callback for bus addition (for legacy drivers)* @detach_adapter: Callback for bus removal (for legacy drivers)* @probe: Callback for device binding* @remove: Callback for device unbinding* @shutdown: Callback for device shutdown* @suspend: Callback for device suspend* @resume: Callback for device resume* @command: Callback for bus-wide signaling (optional)* @driver: Device driver model driver* @id_table: List of I2C devices supported by this driver* @detect: Callback for device detection* @address_data: The I2C addresses to probe, ignore or force (for detect)* @clients: List of detected clients we created (for i2c-core use only)** The driver.owner field should be set to the module owner of this driver.* The driver.name field should be set to the name of this driver.** For automatic device detection, both @detect and @address_data must* be defined. @class should also be set, otherwise only devices forced* with module parameters will be created. The detect function must* fill at least the name field of the i2c_board_info structure it is* handed upon successful detection, and possibly also the flags field.** If @detect is missing, the driver will still work fine for enumerated* devices. Detected devices simply won't be supported. This is expected* for the many I2C/SMBus devices which can't be detected reliably, and* the ones which can always be enumerated in practice.** The i2c_client structure which is handed to the @detect callback is* not a real i2c_client. It is initialized just enough so that you can* call i2c_smbus_read_byte_data and friends on it. Don't do anything* else with it. In particular, calling dev_dbg and friends on it is* not allowed.*/ struct i2c_driver {unsigned int class;/* Notifies the driver that a new bus has appeared or is about to be* removed. You should avoid using this if you can, it will probably* be removed in a near future.*/int (*attach_adapter)(struct i2c_adapter *);int (*detach_adapter)(struct i2c_adapter *);/* Standard driver model interfaces */int (*probe)(struct i2c_client *, const struct i2c_device_id *);int (*remove)(struct i2c_client *);/* driver model interfaces that don't relate to enumeration */void (*shutdown)(struct i2c_client *);int (*suspend)(struct i2c_client *, pm_message_t mesg);int (*resume)(struct i2c_client *);/* a ioctl like command that can be used to perform specific functions* with the device.*/int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);struct device_driver driver;const struct i2c_device_id *id_table;/* Device detection callback for automatic device creation */int (*detect)(struct i2c_client *, int kind, struct i2c_board_info *);const struct i2c_client_address_data *address_data;struct list_head clients; }; #define to_i2c_driver(d) container_of(d, struct i2c_driver, driver)
/*** struct i2c_client - represent an I2C slave device* @flags: I2C_CLIENT_TEN indicates the device uses a ten bit chip address;* I2C_CLIENT_PEC indicates it uses SMBus Packet Error Checking* @addr: Address used on the I2C bus connected to the parent adapter.* @name: Indicates the type of the device, usually a chip name that's* generic enough to hide second-sourcing and compatible revisions.* @adapter: manages the bus segment hosting this I2C device* @driver: device's driver, hence pointer to access routines* @dev: Driver model device node for the slave.* @irq: indicates the IRQ generated by this device (if any)* @detected: member of an i2c_driver.clients list or i2c-core's* userspace_devices list** An i2c_client identifies a single device (i.e. chip) connected to an* i2c bus. The behaviour exposed to Linux is defined by the driver* managing the device.*/ struct i2c_client {unsigned short flags; /* div., see below */unsigned short addr; /* chip address - NOTE: 7bit *//* addresses are stored in the *//* _LOWER_ 7 bits */char name[I2C_NAME_SIZE];struct i2c_adapter *adapter; /* the adapter we sit on */struct i2c_driver *driver; /* and our access routines */struct device dev; /* the device structure */int irq; /* irq issued by device */struct list_head detected; }; #define to_i2c_client(d) container_of(d, struct i2c_client, dev)
/*** struct i2c_msg - an I2C transaction segment beginning with START* @addr: Slave address, either seven or ten bits. When this is a ten* bit address, I2C_M_TEN must be set in @flags and the adapter* must support I2C_FUNC_10BIT_ADDR.* @flags: I2C_M_RD is handled by all adapters. No other flags may be* provided unless the adapter exported the relevant I2C_FUNC_** flags through i2c_check_functionality().* @len: Number of data bytes in @buf being read from or written to the* I2C slave address. For read transactions where I2C_M_RECV_LEN* is set, the caller guarantees that this buffer can hold up to* 32 bytes in addition to the initial length byte sent by the* slave (plus, if used, the SMBus PEC); and this value will be* incremented by the number of block data bytes received.* @buf: The buffer into which data is read, or from which it's written.** An i2c_msg is the low level representation of one segment of an I2C* transaction. It is visible to drivers in the @i2c_transfer() procedure,* to userspace from i2c-dev, and to I2C adapter drivers through the* @i2c_adapter.@master_xfer() method.** Except when I2C "protocol mangling" is used, all I2C adapters implement* the standard rules for I2C transactions. Each transaction begins with a* START. That is followed by the slave address, and a bit encoding read* versus write. Then follow all the data bytes, possibly including a byte* with SMBus PEC. The transfer terminates with a NAK, or when all those* bytes have been transferred and ACKed. If this is the last message in a* group, it is followed by a STOP. Otherwise it is followed by the next* @i2c_msg transaction segment, beginning with a (repeated) START.** Alternatively, when the adapter supports I2C_FUNC_PROTOCOL_MANGLING then* passing certain @flags may have changed those standard protocol behaviors.* Those flags are only for use with broken/nonconforming slaves, and with* adapters which are known to support the specific mangling options they* need (one or more of IGNORE_NAK, NO_RD_ACK, NOSTART, and REV_DIR_ADDR).*/ struct i2c_msg {__u16 addr; /* slave address */__u16 flags; #define I2C_M_TEN 0x0010 /* this is a ten bit chip address */ #define I2C_M_RD 0x0001 /* read data, from slave to master */ #define I2C_M_NOSTART 0x4000 /* if I2C_FUNC_PROTOCOL_MANGLING */ #define I2C_M_REV_DIR_ADDR 0x2000 /* if I2C_FUNC_PROTOCOL_MANGLING */ #define I2C_M_IGNORE_NAK 0x1000 /* if I2C_FUNC_PROTOCOL_MANGLING */ #define I2C_M_NO_RD_ACK 0x0800 /* if I2C_FUNC_PROTOCOL_MANGLING */ #define I2C_M_RECV_LEN 0x0400 /* length will be first received byte */__u16 len; /* msg length */__u8 *buf; /* pointer to msg data */ };
/** i2c_adapter is the structure used to identify a physical i2c bus along* with the access algorithms necessary to access it.*/ struct i2c_adapter {struct module *owner;unsigned int id;unsigned int class; /* classes to allow probing for */const struct i2c_algorithm *algo; /* the algorithm to access the bus */void *algo_data;/* data fields that are valid for all devices */u8 level; /* nesting level for lockdep */struct mutex bus_lock;int timeout; /* in jiffies */int retries;struct device dev; /* the adapter device */int nr;char name[48];struct completion dev_released; }; #define to_i2c_adapter(d) container_of(d, struct i2c_adapter, dev)
/** The following structs are for those who like to implement new bus drivers:* i2c_algorithm is the interface to a class of hardware solutions which can* be addressed using the same bus algorithms - i.e. bit-banging or the PCF8584* to name two of the most common.*/ struct i2c_algorithm {/* If an adapter algorithm can't do I2C-level access, set master_xferto NULL. If an adapter algorithm can do SMBus access, setsmbus_xfer. If set to NULL, the SMBus protocol is simulatedusing common I2C messages *//* master_xfer should return the number of messages successfullyprocessed, or a negative value on error */int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,int num);int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,unsigned short flags, char read_write,u8 command, int size, union i2c_smbus_data *data);/* To determine what the adapter supports */u32 (*functionality) (struct i2c_adapter *); };
/*** struct i2c_board_info - template for device creation* @type: chip type, to initialize i2c_client.name* @flags: to initialize i2c_client.flags* @addr: stored in i2c_client.addr* @platform_data: stored in i2c_client.dev.platform_data* @archdata: copied into i2c_client.dev.archdata* @irq: stored in i2c_client.irq** I2C doesn't actually support hardware probing, although controllers and* devices may be able to use I2C_SMBUS_QUICK to tell whether or not there's* a device at a given address. Drivers commonly need more information than* that, such as chip type, configuration, associated IRQ, and so on.** i2c_board_info is used to build tables of information listing I2C devices* that are present. This information is used to grow the driver model tree.* For mainboards this is done statically using i2c_register_board_info();* bus numbers identify adapters that aren't yet available. For add-on boards,* i2c_new_device() does this dynamically with the adapter already known.*/ struct i2c_board_info {char type[I2C_NAME_SIZE];unsigned short flags;unsigned short addr;void *platform_data;struct dev_archdata *archdata;int irq; };
s3c2440的i2c主控制器僅5個寄存器,在linux內核中卻搞得這么繁雜,,,,,
IICCON 0x54000000 R/W IIC-Bus control register
IICSTAT 0x54000004 R/W IIC-Bus control/status register
IICADD 0x54000008 R/W IIC-Bus address register
IICDS 0x5400000C R/W IIC-Bus transmit/receive data shift register
IICLC 0x54000010 R/W IIC-Bus multi-master line control register
并且micro2440的eeprom的i2c驅動最外層是platform總線然后是i2c總線然后才是面向用戶訪問的字符設備。。。?
目前的一點個人理解,有待更新。。。
存在的問題,在insmod時i2c核心具體怎么匹配外設驅動(設備)和總線(控制器驅動),在ioctl時i2c核心具體怎么處理(匹配)外設驅動和總線,i2c具體數據的傳輸。有時間再去細讀。
對總線設備驅動模型認識不清,對sysfs認識不清,對mdev如何利用sysfs創建設備節點的過程不清。
refer to
http://blog.csdn.net/cjok376240497/article/details/6982883
http://blog.csdn.net/hongtao_liu/article/details/4964244
http://blog.csdn.net/hongtao_liu/article/details/5260739
轉載于:https://www.cnblogs.com/-song/archive/2011/12/02/3331911.html
總結
以上是生活随笔為你收集整理的i2c--ioctl--主机控制器驱动(i2c_adapter)--外设驱动(i2c_driver)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: i2c--insmod
- 下一篇: 扩展adc 1