I2C bus EEPROM (24C08) Linux Driver (original)

Source: Internet
Author: User

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

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.