Based on Linux 2.6.30 Kernel
Conforms to the Linux driver architecture model
The page read and write operations for 24c08 are optimized.
Fully simulates the file read/write mode and supports lseek operations.
This Code contains the device address. When i2c_add_driver is used, it will detect whether there is a device on the address.
However, during board-level development, the i2c_device is placed in the Board file,
I2c_device and i2c_driver match with each other based on the name field.
Reprinted please indicate the source
Original code
Note: In the updated Linux kernel, The i2c_driver struct has a few changes.
/** Eeprom-24c08.c-EEPROM driver for some mostly-compatible I2C chips. ** copyright (c) 2011 William Smith ** this program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */# include <Linux/module. h> # include <Linux/init. h> # include <Linux/slab. h> # include <Linux/I2C. h> // # includ E <Linux/BCD. h>/* New add */# include <Linux/cdev. h> # include <arm-ASM/uaccess. h> # include <Linux/Fs. h> # include <Linux/device. H & gt; # define eep_major 236/* Major number can also be dynamically allocated. */# define bnk_size 1024/* bank size */# define blk_size 256/* block size */# define pg_size 16/* page size */# define eeprom_slave_addr0 0x50 /* 7bit. binary: 1010000 */# define eeprom_slave_addr1 0x51/* 7bi T. binary: 1010001 */# define eeprom_slave_addr2 0x52/* 7bit. binary: 1010010 */# define eeprom_slave_addr3 0x53/* 7bit. binary: 1010011 */# define max_retrys 30/* For debug * // # define debug_eep # define history int max_retrys_record = 0; static struct eep_24c08 {struct i2c_client * client [4]; /* I2C client for this EEPROM */struct cdev eep_cdev;/* cdev */dev_t dev_num;/* Dev num */unsigned I NT cur_ptr;/* current file pointer */} * eep; static int eep_open (struct inode * inode, struct file * file) {# ifdef debug_eepprintk (kern_alert "EEPROM open \ n"); # endifmax_retrys_record = 0; Eep-> cur_ptr = 0; return 0;} static int eep_release (struct inode * inode, struct file * file) {# ifdef debug_eepprintk (kern_alert "EEPROM close \ n"); # endifreturn 0;} static loff_t eep_llseek (struct file * filp, loff_t off, I NT whence) {# ifdef debug_eepprintk (kern_alert "EEPROM lseek \ n"); # endifswitch (whence) {Case 0:/* seek_set */eep-> cur_ptr = off; break; case 1:/* seek_cur */eep-> cur_ptr + = off; break; Case 2:/* seek_end */eep-> cur_ptr = B _size + off; break; default: /* can't happen */Return-einval;} If (EEP-> cur_ptr <0) | (EEP-> cur_ptr> bnk_size )) {printk (kern_alert "EEPROM 'lseek () 'call, Param mistake \ n"); Eep-> C Ur_ptr = 0; Return-1;} return Eep-> cur_ptr;} static int _ rw_page (char * Buf, int length, int RW) {int ret, err = 0; int block; unsigned char word_addr; struct i2c_adapter * ADAP; struct i2c_msg [2]; int msg_num; unsigned char * buf_tmp = NULL; block = Eep-> cur_ptr/blk_size; ADAP = Eep-> client [block]-> adapter; word_addr = Eep-> cur_ptr % blk_size; # ifdef debug_eepprintk (kern_alert "_ rw_page (): % s: client [% d]-> Add R = 0x % 02x, cur_ptr = % d, block = % d, word_addr = % d \ n ", (RW? "Write": "read"), block, Eep-> client [block]-> ADDR, Eep-> cur_ptr, block, word_addr); # endifif (RW) {/* write data * // * alloc memory for buf_tmp */buf_tmp = kmalloc (Length + 1, gfp_kernel); If (! Buf_tmp) {printk (kern_alert "EEPROM kmalloc % d failed \ n", Length + 1); Return-enomem;} memcpy (buf_tmp + 1, Buf, length ); buf_tmp [0] = word_addr; MSG [0]. ADDR = Eep-> client [block]-> ADDR; MSG [0]. flags = 0; MSG [0]. len = Length + 1; MSG [0]. buf = buf_tmp;} else {/* read data */MSG [0]. ADDR = Eep-> client [block]-> ADDR; MSG [0]. flags = 0; MSG [0]. len = 1; MSG [0]. buf = & word_addr; MSG [1]. ADDR = Eep-> client [block]-> ADDR; MSG [1]. flags = I2 C_m_rd; MSG [1]. Len = length; MSG [1]. Buf = Buf;} msg_num = RW?; # Ifdef debug_eepprintk (kern_alert "msg_num = % d, MSG [0]: ADDR = 0x % 02x, flags = % d, Len = % d, Buf [0 ~ 3] = % 02x, % 02x, % 02x, % 02x \ n ", msg_num, MSG [0]. ADDR, MSG [0]. flags, MSG [0]. len, MSG [0]. buf [0], MSG [0]. buf [1], MSG [0]. buf [2], MSG [0]. buf [3]); # endifret = i2c_transfer (ADAP, MSG, msg_num); If (Ret <0) {# ifdef debug_eepprintk (kern_alert "i2c_transfer () err % d \ n ", RET); # endiferr = ret; goto exit;} else if (Ret <(RW? {Printk (kern_err "_ rw_page (): % s: error, actual write i2c_msg Number % d, desire i2c_msg Number % d \ n", (RW? "Write": "read"), RET, (RW? 2); err =-EIO; goto exit;} Exit: If (RW) kfree (buf_tmp); Return (ERR? Err: length);} static int rw_page (char * Buf, int length, int RW) {int I; int ret, err = 0; char * buf_tmp; /* alloc memory for buf_tmp */buf_tmp = kmalloc (length, gfp_kernel); If (! Buf_tmp) {printk (kern_alert "EEPROM kmalloc % d failed \ n", length); Return-enomem ;}for (I = 0; I <max_retrys; I ++) {ret = _ rw_page (BUF, length, RW); If (Ret <0) continue; ret = _ rw_page (buf_tmp, length, 0 ); if (Ret <0) continue; ret = memcmp (BUF, buf_tmp, length); If (ret = 0) break; else {# ifdef debug_eepprintk (kern_alert "rw_page (): % s: memcmp return % d \ n ", (RW? "Write": "read"), RET); printk (kern_alert "original Buf:"); for (I = 0; I <length; I ++) printk (kern_alert "% 02x", Buf [I]); printk (kern_alert "\ nrecheck Buf:"); for (I = 0; I <length; I ++) printk (kern_alert "% 02x", buf_tmp [I]); printk (kern_alert "\ n"); # endif }# ifdef debug_eep_rif (I) {printk (kern_alert "retry % d times. \ n ", I); if (I> max_retrys_record) max_retrys_record = I ;}# endifif (I = max_retrys) {printk (Kern _ Alert "reach max_retrys (% d times) limit. \ n", max_retrys); err =-eagain; goto exit;} Exit: kfree (buf_tmp); Return (ERR? Err: length);} static ssize_t eep_rw (char * Buf, size_t count, int RW) {int ret, err = 0; int length = count; int seg; char * buf_tmp = NULL, * buf_cur; # ifdef debug_eepprintk (kern_alert "##### eep_rw (), Buf = % P, Count = % d, RW = % d ###### \ n ", Buf, Count, RW); # endif/* Check Param */If (EEP-> cur_ptr + count> nkb_size) {printk (kern_alert "EEPROM try to % s over bnk_size: \ n" "\ tcur_ptr = % d, Count = % d, cur_ptr + Count = % D \ n ", (RW? "Write": "read"), Eep-> cur_ptr, Count, Eep-> cur_ptr + count); Return-einval;} If (RW! = 0 & RW! = 1) {printk (kern_alert "eep_rw () error, RW Param Value % d invalid. \ n ", RW); Return-einval;}/* alloc memory for buf_tmp */buf_tmp = kmalloc (count, gfp_kernel); If (! Buf_tmp) {printk (kern_alert "EEPROM kmalloc % d failed \ n", count); Return-enomem;} buf_cur = buf_tmp; If (RW = 1) copy_from_user (buf_tmp, (const char *) BUF, count); While (length> 0) {seg = length> pg_size? Pg_size: length; # ifdef debug_eepprintk (kern_alert "eep_rw (): % s: Count = % d, cur_ptr = % d, SEG = % d, length = % d \ n ", (RW? "Write": "read"), Count, Eep-> cur_ptr, seg, length); # endifret = rw_page (buf_cur, seg, RW); If (Ret <0) {printk (kern_alert "EEPROM % s error % d \ n", (RW? "Write": "read"), RET); err = ret; goto exit;} buf_cur + = seg; Length-= seg; Eep-> cur_ptr + = seg ;} if (RW = 0) copy_to_user (BUF, (const char *) buf_tmp, count); # ifdef debug_eep_rprintk (kern_alert "Max retrys record is % d \ n", max_retrys_record ); # endifexit: kfree (buf_tmp); Return (ERR? Err: Count);} static ssize_t eep_read (struct file * file, char _ User * Buf, size_t count, loff_t * PPOs) {return eep_rw (BUF, count, 0);} static ssize_t eep_write (struct file * file, const char _ User * Buf, size_t count, loff_t * PPOs) {return eep_rw (BUF, Count, 1 );} static struct file_operations eep_fops = {. owner = this_module ,. open = eep_open ,. release = eep_release ,. llseek = eep_llseek ,. read = eep_read ,. write = Eep_write,}; static const unsigned short normal [] = {eeprom_slave_addr0, delimiter, i2c_client_end}; static const unsigned short ignore [] = {i2c_client_end }; static const struct i2c_client_address_data eep_addr_data = {. normal_i2c = normal ,. forces = NULL ,. probe = ignore ,. ignore = ignore,};/* The higher version of Linux kernel is much more simple. *// * The device name this driver supported. */static const struct i2c_device_id eep_24c08_id [] = {"eeprom-blk0", eeprom_slave_addr0}, {"eeprom-blk1", threads}, {"eeprom-blk2", eeprom_slave_addr2}, {"eeprom-blk3 ", eeprom_slave_addr3 },{}};/* Well, there is some necessary to take a explaination. * Because of the device 24c08 has 8 K bits in it, it need 4 addresses. * In the I2C Protocal, The slave_addr data has only 8 bit, which cocould only address 256 bytes (2048 bits ). * So 24c08 has 4 different slave_addr addresses: 0x50, 0x51, 0x52, 0x53. * Each address is a part that has 2048 bits. totally, 4 parts have 8192 bits (8 K bits ). */module_device_table (I2C, eep_24c08_id);/* eep_probe-probe method for new-style driver model. * @ client: the i2c_client which already been register. * @ ID: The i2c_device_id which matchs. * When Dev and driver are both create and matched. this func will be called to init some specfic data. * usually, the device's initial step will be done here. * Note: when a driver has n devices, the probe will be called N times. * For different device, different i2c_device_id will be provided for use in the probe function. */static int _ devinit eep_probe (struct I2 C_client * client, const struct i2c_device_id * ID) {int I = 0; # ifdef debug_eepprintk (kern_alert "EEPROM probe \ n"); # endifwhile (eep_24c08_id [I]. driver_data) {If (client-> ADDR = (unsigned short) eep_24c08_id [I]. driver_data) {# ifdef debug_eepprintk (kern_alert "probe (): client-> ADDR = 0x % 02x, I = % d, eep_24c08_id [% d]. driver_data = 0x % 02x, eep_24c08_id [% d]. name = % s \ n ", client-> ADDR, I, I, (unsigned INT) eep _ 24c08_id [I]. driver_data, I, eep_24c08_id [I]. name); # endifeep-> client [I] = client;/* store the client into eep for further use. * // * client can also be stored in file-> P when open (). */} I ++;} return 0;} static int _ devexit eep_remove (struct i2c_client * client) {# ifdef debug_eepprintk (kern_alert "EEPROM remove \ n "); # endifreturn 0;}/* eep_detect-detect method is called when there is really some Devic E on '@ kind' address. @ i2c_client: The temp client device which is given to detect func's use. it is only temp which has not been register. @ kind: Indicate the number in i2c_client_address_data for each possible member. only for forces device. others condition is-1. @ i2c_board_info: The specific infomation's structure to store device's info. * This function was called before the real i2c_client I S create. * Some special device need a initializion before read/write it, do the init here. * init the client's infomation, And it need to fill at least the name * of i2c_board_info Param for registering which is also the name of client device. * notice: Its ADDR Member has already beed set. */INT eep_detect (struct i2c_client * client, int kind, struct i2c_board_info * bd_info) {int I = 0; # ifdef debug _ Eepprintk (kern_alert "EEPROM detect \ n"); # endif/* There is no need to take action of detect on I2C bus. */while (eep_24c08_id [I]. driver_data) {If (client-> ADDR = (unsigned short) eep_24c08_id [I]. driver_data) {strlcpy (bd_info-> type, eep_24c08_id [I]. name, i2c_name_size);/* In fact, giving the name is the only thing need to do. */bd_info-> flags = 0; # ifdef debug_eepprintk (kern_alert "detect (): I = % d, Client-> ADDR = 0x % 02x, bd_info-> type = % s \ n ", I, client-> ADDR, bd_info-> type ); # endif} I ++;}/* platform_data shoshould be study later */return 0;} static struct i2c_driver eep_driver = {. driver = {. name = "EEPROM-driver",/* name */. owner = this_module ,},. class = 1 ,. probe = eep_probe ,. remove = _ devexit_p (eep_remove ),. id_table = eep_24c08_id ,. detect = eep_detect ,. address_data = & eep_addr_data,}; static int _ init Eeprom_init (void) {int err = 0; eep = kmalloc (sizeof (struct eep_24c08), gfp_kernel); If (! Eep) {err =-enomem; goto exit;}/* Get and register the cdev for EEPROM */eep-> dev_num = mkdev (eep_major, 0 ); if (register_chrdev_region (EEP-> dev_num, 1, "EEPROM") <0) {printk (kern_alert "can't register device \ n"); goto exit_free ;} cdev_init (& Eep-> eep_cdev, & eep_fops); Eep-> queue = this_module; Eep-> queue = & eep_fops; If (cdev_add (& Eep-> eep_cdev, eep-> dev_num, 1) {printk (kern_alert "failed to add cdev of EEPROM \ n"); goto exit_unregister ;} /* Register i2c_driver to I2C core */err = i2c_add_driver (& eep_driver); If (ERR) {printk (kern_alert "registering I2C driver of EEPROM failed, errno is % d \ n ", err); goto exit_del_cdev;} # ifdef debug_eepprintk (kern_alert" EEPROM driver initialized. \ n "); # endifreturn 0; exit_del_cdev: cdev_del (& Eep-> eep_cdev); exit_unregister: Queue (EEP-> dev_num, 1); exit_free: kfree (EEP ); exit: Return err;} static void _ exit eeprom_exit (void) {i2c_del_driver (& eep_driver); cdev_del (& Eep-> eep_cdev); terminate (EEP-> dev_num, 1); kfree (EEP); # ifdef debug_eepprintk (kern_alert "EEPROM driver removed. \ n "); # endif} module_init (eeprom_init); module_exit (eeprom_exit); module_description (" EEPROM driver for 24c08 and similar chips "); module_license (" GPL "); reprinted please indicate the source