Linux Device Driver I2C Architecture Analysis (III)

Source: Internet
Author: User
6. After the registration of the adapter and I2C driver for other extensions, it seems that the entire architecture is similar. There are many other extensions. let's take an example of legacy, which is randomly searched in the kernel: In the linux-2.6.26.3/Drivers/hwmon/ad7418.c, the initialization function is: static int _ init ad7418_init (void) {return i2c_add_driver (& ad7418_driver);} The i2c_driver ad7418_driver structure is as follows: static struct i2c_driver ad7418_driver = {. driver = {. name = "ad7418 ",},. attach_adapter = ad7418_attach_adapter ,. detach_client = ad7418_detach_clie NT,}; there is no probe () function in this structure. It can be determined that it is a legacy driver. when these drivers are registered, the attach_adapter function of the driver is called. here is ad7418_attach_adapter. the code for this function is as follows: static int ad7418_attach_adapter (struct i2c_adapter * adapter) {If (! (Adapter-> Class & i2c_class_hwmon) return 0; return i2c_probe (adapter, & addr_data, ad7418_detect);} Here we met another function in the i2c-core, i2c_probe (). before analyzing this function, let's take a look at what addr_data is? # Define terminate (VAR, DESC)/static unsigned short var [i2c_client_max_opts] = i2c_client_defaults;/static unsigned int var ##_ num;/module_param_array (VAR, short, & var # _ num, 0);/module_parm_desc (VAR, DESC) # define i2c_client_module_parm_force (name)/i2c_client_module_parm (force _ # name,/"List of adapter, address pairs which are "/" unquestionably assumed to contain a' "/# Name "'Chip") # define i2c_client_insmod_common/i2c_client_module_parm (probe, "List of adapter, address pairs to scan"/"Additionally");/define (ignore, "List of adapter, address pairs not to "/" scan ");/static const struct i2c_client_address_data addr_data = {/. normal_i2c = normal_i2c ,/. probe = probe ,/. ignore = ignore ,/. forces = forces, //} # define i2c_client_force_text/"lis T of adapter, address pairs to boldly assume to be present "from this we can know that the three members in addr_data are module parameters. when loading a module, you can assign values to it using parameters. the parameters of the three modules are probe, ignore, and force. in addition, normal_i2c cannot be assigned a value as a module parameter. It can only be specified statically inside the driver. according to the module parameter model, probe refers to "List of adapter, address pairs to scan additionally" ignore refers to "List of adapter, address pairs not to scan "force refers to" List of adapters, address pairs to boldly assume to be present ". In fact, the data in these pairs appears in pairs. the previous part indicates In the bus number, any_i2c_bus represents any bus. the last part indicates the device address. now we can trace the i2c_probe () code. int i2c_probe (struct i2c_adapter * adapter, const struct i2c_client_address_data * address_data, INT (* found_proc) (struct i2c_adapter *, Int, INT) {int I, err; int adap_id = i2c_adapter_id (adapter);/* force entries are done first, and are not affected by ignore entries * // first scan the information in force, note that it is a second-level pointer. the information in ignore is invalid if (address_data-> Fo Rces) {const unsigned short * const * forces = address_data-> forces; int kind; For (kind = 0; forces [kind]; kind ++) {for (I = 0; forces [kind] [I]! = I2c_client_end; I + = 2) {If (Forces [kind] [I] = adap_id | forces [kind] [I] = any_i2c_bus) {dev_dbg (& adapter-> Dev, "found force" "parameter for adapter % d," "ADDR 0x % 02x, kind % d/N", adap_id, forces [kind] [I + 1], kind); err = i2c_probe_address (adapter, forces [kind] [I + 1], kind, found_proc); If (ERR) return err ;}}}/* Stop here if we can't use smbus_quick * // If the adapter does not support quick. unable to traverse the device above this adapter

If (! I2c_check_functionality (adapter, identifier) {If (address_data-> probe [0] = i2c_client_end & address_data-> normal_i2c [0] = i2c_client_end) return 0; dev_warn (& adapter-> Dev, "SMBus Quick Command not supported," "Can't probe for chips/N"); Return-1 ;} /* probe entries are done second, and are not affected by ignore entries either * // traverse the information above probe. the information on ignore has no impact on it. Ss_data-> probe [I]! = I2c_client_end; I + = 2) {If (address_data-> probe [I] = adap_id | address_data-> probe [I] = any_i2c_bus) {dev_dbg (& adapter-> Dev, "found probe parameter for" "adapter % d, ADDR 0x % 02x/N", adap_id, address_data-> probe [I + 1]); err = i2c_probe_address (adapter, address_data-> probe [I + 1],-1, found_proc); If (ERR) return err ;}}/* normal entries are done last, unless shadowed by an ignore entry * /// Finally traverse the information above normal_i2c. The information above cannot be in ignore. For (I = 0; address_data-> normal_i2c [I]! = I2c_client_end; I + = 1) {Int J, ignore; ignore = 0; For (j = 0; address_data-> ignore [J]! = I2c_client_end; j + = 2) {If (address_data-> ignore [J] = adap_id | address_data-> ignore [J] = any_i2c_bus) & address_data-> ignore [J + 1] = address_data-> normal_i2c [I]) {dev_dbg (& adapter-> Dev, "Found ignore" "parameter for adapter % d," "ADDR 0x % 02x/N", adap_id, address_data-> ignore [J + 1]); ignore = 1; break ;}} if (ignore) continue; dev_dbg (& adapter-> Dev, "found normal entry for adapter % D, "" ADDR 0x % 02x/N ", adap_id, address_data-> normal_i2c [I]); err = i2c_probe_address (adapter, address_data-> normal_i2c [I],-1, found_proc); If (ERR) return err;} return 0;} This code is very simple. It should be well understood in combination with the comments added above the code. if the match is successful, i2c_probe_address () is called (). the code for this function is as follows: static int i2c_probe_address (struct i2c_adapter * adapter, int ADDR, int kind, INT (* found_proc) (struct i2c_adapter *, Int, INT) {int err; /* Make sure the ad Dress is valid * // if the address is less than 0x03 or greater than 0x77 is invalid (ADDR <0x03 | ADDR> 0x77) {dev_warn (& adapter-> Dev, "invalid probe address 0x % 02x/N", ADDR); Return-einval ;} /* Skip if already in use * // The adapter already has this device. If (i2c_check_addr (adapter, ADDR) return 0; /* Make sure there is something at this address, unless forced * // If kind is less than 0. check whether the device if (kind <0) {If (i2c_smbus_xfer (adapter, ADDR, 0, 0, 0, i2c_smbus_quick, null) <0) return 0;/* prevent 24rf08 partial uption */If (ADDR &~ 0x0f) = 0x50) i2c_smbus_xfer (adapter, ADDR, 0, 0, 0, i2c_smbus_quick, null );} /* finally call the custom detection function * // call the callback function err = found_proc (adapter, ADDR, kind ); /*-enodev can be returned if there is a chip at the given address but it isn' t supported by this chip driver. we catch it here as this isn' t an error. */If (ERR =-enodev) Err = 0; If (ERR) dev_warn (& adapter-> Dev, "client Creation failed at 0x % x (% d)/n ", ADDR, err); Return err;} First, perform a series of validity checks on the passed parameters. in addition, if the adapter already has a device with this address. failure will also be returned. all devices under the adapter use adapter-> Dev as the parent node. therefore, you only need to traverse the sub-device under adapter-> Dev to see if the current address is occupied. if kind is <0. you also need the adapter to check whether the bus has a device with this address. the method is to send a read quick request to this address. if the address has a response, it indicates that the address has this device. another case is the 24rf08 device. if this device exists on the adapter, the callback function for the driver call will be called. if you have any questions about the IIC transmission method, refer to the SMBus section in the Intel ich5 manual. tracking i2c_smbus_xfer (). code such Below: s32 i2c_smbus_xfer (struct i2c_adapter * adapter, 2010addr, unsigned short flags, char read_write, u8 command, int size, Union i2c_smbus_data * Data) {s32 res; flags & = i2c_m_ten | i2c_client_pec; If (adapter-> algo-> smbus_xfer) {mutex_lock (& adapter-> bus_lock); Res = adapter-> algo-> smbus_xfer (adapter, Adapter, ADDR, flags, read_write, command, size, data); mutex_unlock (& adapter-> bus_lock);} else res = i2c_smb Us_xfer_emulated (adapter, ADDR, flags, read_write, command, size, data); Return res;} if the adapter has the smbus_xfer () function, it is directly called for sending. Otherwise, that is, if the adapter does not support the SMBus protocol, call i2c_smbus_xfer_emulated () to continue processing. follow up i2c_smbus_xfer_emulated (). code: static s32 i2c_smbus_xfer_emulated (struct i2c_adapter * adapter, 2010addr, unsigned short flags, char read_write, u8 command, int size, Union i2c_smbus_data * Data) {/* so we need to generate A series of msgs. in the case of writing, we need to use only one message; when reading, we need two. we initialize most things with sane defaults, to keep the code below somewhat simpler. * /// the write operation only performs one interaction, while the read operation sometimes performs two operations. // sometimes the read operation must first write the command and then read the data from the bus. // here, the code is concise. two cache zones are used to unify the two conditions. unsigned char msgbuf0 [i2c_smbus_block_max + 3]; unsigned char msgbuf1 [i2c_smbus_block_max + 2]; // generally, read operations need to interact twice. for exceptions Next we will analyze int num = read_write = i2c_smbus_read?; // The data that interacts with the device. Generally, the MSG [0] stores the information written to the device and the received information in MSB [1. except for initialization of // MSG [2], the sending cache occupies one byte by default, and no receiving cache struct i2c_msg [2] ={{ ADDR, flags, 1, msgbuf0 },{ ADDR, flags | i2c_m_rd, 0, msgbuf1 }}; int I; u8 partial_pec = 0; // copy the information to be sent to the first byte msgbuf0 [0] = command; Switch (size) {// quick type. Other valid data is not transmitted, write the address to the bus and wait for a response. // set the sending cache length to 0. adjust the flag bit of MSG [0] According to the read/write operation. // This type of transmission only requires one bus interaction. Case i2c_smbus_quick: msg [0]. Len = 0;/* Special Case: The read/write field is used as data */MSG [0]. Flags = flags | (read_write = i2c_smbus_read )? I2c_m_rd: 0; num = 1; break; Case i2c_smbus_byte: // The byte type indicates that only one byte is written and read at a time. in this case, both read and write will only interact once. // This type of read has an exception. The data it reads is not stored in MSG [1, instead, it is stored in MSG [0] If (read_write = i2c_smbus_read) {/* Special Case: only a read! */MSG [0]. flags = i2c_m_rd | flags; num = 1;} break; Case i2c_smbus_byte_data: // byte_data refers to the command + data transmission form. in this case, the write operation only requires one interaction, but the read operation takes two times. // The command is written to the bus for the first time, and the second secondary conversion direction. write the device address and read flag to the bus. // read operations should be performed later. // write operations take two bytes, namely command + data. the valid data of the read operation is only one byte // The Interaction count can be initialized with the value if (read_write = i2c_smbus_read) MSG [1]. len = 1; else {MSG [0]. len = 2; msgbuf0 [1] = data-> byte;} break; Case i2c_smbus_word_data: // word_data refers to the command + dubyte format. this is similar to the case of byte_data. // The data size of the interaction is different than that of the two. If (read_write = i2c_smbus_read) MSG [1]. len = 2; else {MSG [0]. len = 3; msgbuf0 [1] = data-> word & 0xff; msgbuf0 [2] = data-> word> 8;} break; Case i2c_smbus_proc_call: // The proc_call method is similar to the write word_data method, except that after the word_data is written, it needs to wait for its response. // it needs to interact twice, and write read num = 2 at a time; /* Special Case */read_write = i2c_smbus_read; MSG [0]. len = 3; MSG [1]. len = 2; msgbuf0 [1] = data-> word & 0xff; msgbuf0 [2] = data-> word> 8; break; Case i2c_smbus_block_data: // block_data: refers to the case of command + n data segments. // For a read operation, it must first write the command to the bus and then read n data segments. the command to be written has been // placed in MSG [0. now you only need to set the flag of MSG [1] to the i2c_m_recv_len bit, and the valid length of MSG [1] is 1 byte. this is because the // Adapter driver will handle the issue. currently, we do not know how many data segments to upload. // for writing: MSG [1] is not required as usual. all data to be written is stored in MSB [0. the cache length in // MSG [0] should also be updated if (read_write = i2c_smbus_read) {MSG [1]. flags | = i2c_m_recv_len; MSG [1]. len = 1;/* block length will be added by the underlying bus driver */} else {/data-> block [0] indicates the number of segments of data in the end. the total length must be 2 because command + Count + n data segments MSG [0]. len = data-> block [0] + 2; If (MSG [0]. len> i2c_smbus_block_max + 2) {dev_err (& adapter-> Dev, "smbus_access called with" "invalid block write size (% d)/n ", data-> block [0]); Return-1;} for (I = 1; I <MSG [0]. len; I ++) msgbuf0 [I] = data-> block [I-1];} break; Case i2c_smbus_block_proc_call: // proc_call: After writing block_data, wait until its response message is compared with block_data, but there is only one more response. num = 2;/* another special case */read_write = i2c_smbus_read; if (data-> block [0]> i2c_smbus_block_max) {dev_err (& adapter-> Dev, "% s called with invalid" "Block proc call size (% d) /n ", _ FUNC __, data-> block [0]); Return-1;} MSG [0]. len = data-> block [0] + 2; for (I = 1; I <MSG [0]. len; I ++) msgbuf0 [I] = data-> block [I-1]; MSG [1]. flags | = i2c_m_recv_len; MSG [1]. len = 1;/* block length will be added by the underlying bus driver */break; Case i2c_smbus_i2c_block_data: // I2C block_data is similar to block_data, but when reading, the data length is pre-defined. in addition, // The Count field does not need to be transmitted in the middle compared with block_data. (count indicates the number of data segments) if (read_write = i2c_smbus_read) {MSG [1]. len = data-> block [0];} else {MSG [0]. len = data-> block [0] + 1; if (MSG [0]. len> i2c_smbus_block_max + 1) {dev_err (& adapter-> Dev, "i2c_smbus_xfer_emulated called with" "invalid block write size (% d)/n ", data-> block [0]); Return-1 ;}for (I = 1; I <= data-> block [0]; I ++) msgbuf0 [I] = data-> block [I];} break; default: dev_err (& adapter-> Dev, "smbus_access called with invalid size (% d)/n ", size); Return-1;} // If Pec. quick and I2C block_data do not support PEC

I = (flags & i2c_client_pec) & size! = I2c_smbus_quick & size! = I2c_smbus_i2c_block_data); if (I) {/* compute pec if first message is a write * // if the first operation is a write operation if (! (MSG [0]. flags & i2c_m_rd) {// if only the write operation is if (num = 1)/* write only * // if only the write operation is required, the write cache must be expanded to one byte, it is used to store the calculated pec i2c_smbus_add_pec (& MSG [0]); else/* write followed by read * // if there are still read operations, calculate the previously written PEC (note that in this case, you do not need to // expand the write cache, because you do not need to send Pec. only Pec) partial_pec = i2c_smbus_msg_pec (0, & MSG [0]);} /* Ask for PEC if last message is a read * // if the last message is read. also receive pec from slave. therefore, the receiving cache needs to expand a byte if (MSG [num-1]. flags & i2c_m_rd) MSG [num-1]. len ++;} If (i2c_transfer (adapter, MSG, num) <0) Return-1;/* Check pec if last message is a read * // after the operation is complete, if the last operation is a PEC read operation. check whether PEC is correct. If (I & (MSG [num-1]. flags & i2c_m_rd) {If (i2c_smbus_check_pec (partial_pec, & MSG [num-1]) <0) Return-1 ;}// the operation is complete, now we can put the data in the data section and return it. if (read_write = i2c_smbus_read) Switch (size) {Case i2c_smbus_byte: Data-> byte = msgbuf0 [0]; break; Case I2 C_smbus_byte_data: Data-> byte = msgbuf1 [0]; break; Case i2c_smbus_word_data: case when: Data-> word = msgbuf1 [0] | (msgbuf1 [1] <8 ); break; Case i2c_smbus_i2c_block_data: for (I = 0; I <data-> block [0]; I ++) Data-> block [I + 1] = msgbuf1 [I]; break; Case i2c_smbus_block_data: Case i2c_smbus_block_proc_call: for (I = 0; I <msgbuf1 [0] + 1; I ++) Data-> block [I] = msgbuf1 [I]; break;} retur N 0;} added detailed comments on this function. The configuration and Intel datasheet should be easy to understand. during the preceding interaction, the subfunction i2c_transfer () is called (). its code is as follows: int i2c_transfer (struct i2c_adapter * ADAP, struct i2c_msg * msgs, int num) {int ret; If (ADAP-> algo-> master_xfer) {# ifdef debug for (ret = 0; RET <num; RET ++) {dev_dbg (& ADAP-> Dev, "master_xfer [% d] % C, ADDR = 0x % 02x, "" Len = % d % s/n ", RET, (MSGs [RET]. flags & i2c_m_rd )? 'R': 'w', msgs [RET]. ADDR, msgs [RET]. Len, (MSGs [RET]. Flags & i2c_m_recv_len )? "+": "") ;}# Endif if (in_atomic () | irqs_disabled () {ret = mutex_trylock (& ADAP-> bus_lock); If (! RET)/* I2C activity is ongoing. */Return-eagain;} else {mutex_lock_nested (& ADAP-> bus_lock, ADAP-> level);} ret = ADAP-> algo-> master_xfer (ADAP, msgs, num); mutex_unlock (& ADAP-> bus_lock); return ret;} else {dev_dbg (& ADAP-> Dev, "I2C level transfers not supported/N "); return-enosys ;}} because the synchronization here uses mutex. first, judge whether sleep is sufficient. If not, try to get the lock. if the lock fails, it is returned. This operation is to avoid sleep. We can also see that the actual transfer is completed by ADAP-> algo-> master_xfer ().. Here, we finally analyzed the execution of i2c_probe_address (). After this analysis, we also know how the data is transmitted. let's take a look at i2c_probe. if i2c_probe_address () is successful. it indicates that such a device exists on the bus. the callback function in the driver will be called. in the driver of ad7148, return i2c_probe (adapter, & addr_data, ad7418_detect); that is, the callback function to be called is ad7418_detect (). in this function, we only analyze the components related to the I2C framework. the code snippet is as follows: static int ad7418_detect (struct i2c_adapter * adapter, int address, int kind) {struct i2c_client * client ;...... ...... Client-> ADDR = address; client-> adapter = adapter; client-> driver = & ad7418_driver; i2c_set_clientdata (client, data );...... ...... If (ERR = i2c_attach_client (client) goto exit_free ;...... ......} Combined with the above driver analysis in the new-style form. it is found that the same routine is used, that is, the client is initialized. then call i2c_attach_client (). the subsequent procedures are the same as those analyzed above. however, the difference is that the driver named ad7418_driver has been specified by clinet. after you register clinet-> Dev, you will not go through the bus-> match and bus-> probe processes.

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.