Linux Device Driver I2C Architecture Analysis (4)

Source: Internet
Author: User
Seven: I2C Dev node operation now to analyze the above structural diagram of the i2c-dev.c in part. this section provides an adapter operation interface for the user space. this part of the code is actually a module. its initialization function is: module_init (i2c_dev_init); The i2c_dev_init () code is as follows: 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); If (RES) goto out; i2c_dev_class = class_create (this_module, "i2c-dev"); If (is_err (i2c_dev_class) goto out_unreg_chr Dev; Res = i2c_add_driver (& i2cdev_driver); If (RES) goto out_unreg_class; return 0; out_unreg_class: class_destroy (i2c_dev_class); outputs: outputs (outputs, "I2C"); Out: printk (kern_err "% s: Driver initialisation failed/N", _ file _); Return res;} First, a master device number is i2c_major (89 ), character device whose operation set is i2cdev_fops. then I registered a class named "i2c-dev. then I registered an I2C driver. as follows: res = i2c_add_driver (& i2cdev_driver); If (RES) goto out_unreg_class; i2cdev_driver is defined as follows: static struct i2c_driver i2cdev_driver = {. driver = {. name = "dev_driver ",},. id = i2c_driverid_i2cdev ,. attach_adapter = i2cdev_attach_adapter ,. detach_adapter = i2cdev_detach_adapter ,. detach_client = i2cdev_detach_client,}; that is, when it registers or has a new adapter, it returns its attach_adapter () function. the code of this function is as follows: static int i2cdev_attach_adapter (struct i2c_adapter * ADAP) {struc T 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 ), "I2C-% d", ADAP-> nr); If (is_err (i2c_dev-> Dev) {res = ptr_err (i2c_dev-> Dev); goto error ;} res = device_create_file (i2c_dev-> Dev, & dev_attr_name); If (Res) Goto error_destroy; pr_debug ("i2c-dev: adapter [% s] registered as minor % d/N", ADAP-> name, ADAP-> nr); Return 0; error_destroy: device_destroy (i2c_dev_class, mkdev (i2c_major, ADAP-> nr); error: return_i2c_dev (i2c_dev); Return res;} this function is also very simple. Call get_free_i2c_dev () A struct i2c_dev structure is allocated and initialized, so that i2c_dev-> ADAP points to the adapter of the operation. then, the i2c_dev will be linked to the i2c_dev_list linked list. then, a device is created based on the main device number i2c_major, ADAP-> Nr. if udev or If it is hotplug, it will automatically create related device nodes under/dev. as we said earlier, the operation function of all the device nodes with the master device number i2c_major is i2cdev_fops. its definition is as follows: static const struct file_operations i2cdev_fops = {. owner = this_module ,. llseek = no_llseek ,. read = i2cdev_read ,. write = i2cdev_write ,. IOCTL = i2cdev_ioctl ,. open = i2cdev_open ,. release = i2cdev_release,}; 7.1: i2cdev_open () Is the function corresponding to the open operation of I2C Dev (). the Code is as follows: 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; // obtain the i2c_dev i2c_dev = i2c_dev_get_by_minor (minor) from the i2c_dev_list linked list with the same device number; If (! I2c_dev) Return-enodev; // find adapter ADAP = i2c_get_adapter (i2c_dev-> ADAP-> nr) from i2c_adapter_idr with the bus number of apter; If (! ADAP) Return-enodev;/* 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. * // allocate and initialize an i2c_client structure client = kzarloc (sizeof (* client), gfp_kernel); If (! Client) {i2c_put_adapter (ADAP); Return-enomem;} snprintf (client-> name, i2c_name_size, "i2c-dev % d", ADAP-> nr ); client-> driver = & i2cdev_driver; // clinet-> adapter points to the adapter client of the operation-> adapter = ADAP; // associates with file-> private_data = client; return 0;} note that a struct i2c_client structure is allocated and initialized here. but this clinet is not registered. in addition, this function has a strange operation. didn't I point i2c_dev-> ADAP to the adapter to be operated before? Why do I need to find the adapter for this operation from i2c_adapter_idr using the adapter-> Nr keyword? Note that when calling i2c_get_adapter () to find the adapter operated on from the bus No. Nr, the reference count of the module will also be increased. this prevents the module from being accidentally released. some people may have such questions: i2c_dev-> ADAP-> Nr operation. If i2c_dev-> ADAP is released, will it cause system crash? Here, the i2cdev_attach_adapter () indirectly increases the reference count of an adapter. tatic int i2cdev_attach_adapter (struct i2c_adapter * ADAP ){...... i2c_dev-> Dev = device_create (i2c_dev_class, & ADAP-> Dev, mkdev (i2c_major, ADAP-> nr), "I2C-% d ", ADAP-> nr );......} as you can see, the device embedded in i2c_dev uses ADAP-> Dev as the parent node and adds a reference count of ADAP-> Dev in device_create. now, the open () operation is complete. 7.2: The operation function corresponding to the read operation for the read operation is as follows: static ssize_t i2cdev_read (struct file * file, char _ User * Buf, size_t count, loff_t * offset) {char * TMP; int ret; struct i2c_client * client = (struct i2c_client *) file-> private_data; if (count> 8192) Count = 8192; TMP = kmalloc (count, gfp_kernel); If (TMP = NULL) Return-enomem; pr_debug ("i2c-dev: i2C-% d reading % ZD bytes. /n ", iminor (file-> f_path.dentry-> d_inode), count); ret = i2c_master_recv (client, TMP, count); If (Ret> = 0) ret = copy_to_user (B UF, TMP, count )? -Efault: ret; kfree (TMP); return ret;} first obtains struct i2c_clinet from the file structure. then, in the cache area with the same length allocated in the kernel, i2c_master_recv () is called to read data from the device. then copy the read data to the user space. i2c_master_recv () code: int i2c_master_recv (struct i2c_client * client, char * Buf, int count) {struct i2c_adapter * ADAP = client-> adapter; struct i2c_msg MSG; int ret; MSG. ADDR = client-> ADDR; MSG. flags = client-> flags & i2c_m_ten; MSG. flags | = i2c_m_rd; MSG. len = cou NT; MSG. buf = Buf; ret = i2c_transfer (ADAP, & MSG, 1);/* if everything went OK (I. e. 1 MSG transmitted), return # bytes transmitted, else error code. */Return (ret = 1 )? Count: ret;} after reading the previous code, this function should be very simple, that is, initializing an i2c_msg for the read operation. then call i2c_tanster (). in the code, client-> flags & i2c_m_ten indicates whether the adapter adopts the 10-bit addressing mode. I will not analyze it in detail here. in addition, someone may see a problem. here, clinet-> where does ADDR come from? Yes, you should have another step to set the value of clinet-> ADDR before reading. this process is an IOCTL operation. IOCTL can be used to set the PEC flag, number of retries, time-out time, and send and receive data. Here we only look at the settings of clinet-> ADDR. the code snippet is as follows: static int i2cdev_ioctl (struct inode * inode, struct file * file, unsigned int cmd, unsigned long 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 cocould become busy later */client-> ADDR = ARG; return 0 ;............} it can be seen that the ioctl that calls i2c_slave or i2c_slave_force will set clinet-> ADDR. in addition, the annotations are also quite clear. for i2c_slave, i2cdev_check_addr () is also called (). check the address. If the adapter is already associated with the device, the check fails. 7.2: write operation: static ssize_t i2cdev_write (struct file * file, const char _ User * Buf, size_t count, loff_t * offset) {int ret; char * TMP; struct i2c_client * client = (struct i2c_client *) file-> private_data; If (count> 8192) Count = 8192; TMP = kmalloc (count, gfp_kernel ); if (TMP = NULL) Return-enomem; If (copy_from_user (TMP, Buf, count) {kfree (TMP); Return-efault;} pr_debug ("i2c-dev: i2C-% d writing % ZD bytes. /n ", iminor (file-> f_path.dentry-> d_inode), count); ret = i2c_master_send (client, TMP, count); kfree (TMP); ret ;} this operation is simple, that is, sending user space data to I2C devices.

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.