MMC Card Driver Analysis

Source: Internet
Author: User
Tags c queue
1. Basic Knowledge: 1. Basic Structure of the Linux Device Driver. 2. the basic architecture of block Device Drivers (I believe that sbull people who have studied ldd3 should not be able to solve the problem. If it is just a bit of a horse, you have to make up for it.) 3. linux Device Driver Model. 2. Driver Analysis first, to clarify the files we need to analyze. The following files are from the linux-2.6.24 source code, we focus on the analysis of the basic architecture of the driver, so the differences between different kernel versions is not very large. The MMC/SD card driver is located in the drivers/MMC directory. We only list the files involved in the analysis process: card/block. c queue. c/queue. hcore/bus. c/bus. h core. c/core. h host. c/host. h MMC. c mmc_ops.c/mmc_ops.h analyzes the MMC card. The SD card driver process is similar. For example, the Controller of the MMC/SD card of s3c24xx is used as the host/SCC. c/SCC. h. The controllers of other types are similar. In Linux, directory division is very exquisite. These files are distributed in three directories, it exactly corresponds to the three layers of the MMC/SD Driver (here we will look at the division of layers. Here we have a concept. When we finish the analysis, we will look back at it, and you will find it very vivid) (1) The block layer implements a card-based block Device Driver Based on the framework of the Linux block device driver. in C, we can see the definition of the block_device_operations struct variable required for writing a block device driver program, including the implementation of the open/release/Request function, while the queue. c is the encapsulation of the Request queue provided by the kernel. We do not need to understand it in depth for the time being. We only need to know that a block device needs a request queue. (2) The core layer encapsulates the command of the MMC/SD card, such as the recognition, setting, and reading/writing of the memory card. For example, no matter what the card should have some identification, setting, and read/write commands, these processes are all necessary, but specific operations will be unique for different cards. Core. the C file is created by SD. c. MMC. c files support the core. c abstracts the commonalities of MMC cards and SD cards, and their differences are defined by SD. c, sd_ops.c, MMC. C and mmc_ops.c. (3) The host controller at the host controller layer depends on different platforms. For example, the host controller at the S3C2410 card must be different from that at the Atmel card. Therefore, it must be implemented for different controllers. For example, if you want to use cloudmonitor to register a function, you must first set the function, such as interrupt function registration and all-powerful controller. Then it registers a host to the core layer and describes it with the mmc_host_ops structure. Then the core layer can operate the s3c24xx card controller with this host, the core layer does not need to be known, for example, the s3c24xx card controller or the Atmel card controller. The driver Hierarchy Diagram is good. After a rough understanding of these directories, let's look at several important data structures: struct mmc_host is used to describe the card controller struct mmc_card to describe the card struct mmc_driver to describe the MMC card driver struct mmc_host_ops to describe the card controller operation set, used to register operation functions from the host controller Layer, this isolates the core layer from the specific host controller. That is to say, to operate the host controller, the core uses the function pointer given in this Ops to operate, and cannot directly call the function of a specific master controller. Phase 1: Start from the beginning of the courseware mci_init and look at the static int _ init courseware mci_init (void) {platform_driver_register (& courseware mci_driver_2410);} The platform_driver_register function is available, we know that there will be a corresponding platform_device_register function, but where is it? I didn't see it. Is it because the probe function given in the "initiai_driver_2410" won't be executed ??? Of course not. The MCI interface is generally prepared by hardware (I think this is the case). Therefore, when the system starts, platform_device_register will be called to register the resources on the board. If this hardware resource is not available, then we will not use this driver. Okay, we assume there is
You can run the probe function on your own if you have registered hardware resources corresponding to cloudmci_driver_2410. Let's take a look at the "Maid: static struct platform_driver {. driver. name = "s3c2410-sdi ",. probe = initiai_probe_2410 ,. remove = initiai_remove ,. suspend = maid ,. resume = s3mci_resume,}; let's look at the callback function. Let's just look at the summary function. Forget it: static int s3mci_probe (struct platform_device * pdev, int is2440) // from/host/cloudmonitor. c {struct mmc_host * MMC; struct s3mci_host * Host; Int ret ;...... MMC = mmc_alloc_host (sizeof (struct initiai_host), & pdev-> Dev); If (! MMC) {ret =-enomem; goto probe_out ;}...... MMC-> Ops = & initiai_ops ;...... Ret = mmc_add_host (MMC); If (RET) {dev_err (& pdev-> Dev, "failed to add MMC host. \ n"); goto free_dmabuf ;}...... Platform_set_drvdata (pdev, MMC); Return 0 ;......} This function is very long and has many events, but we are concerned about the entire driver architecture/process, so we can filter out some details and only look at the two most important functions: mmc_alloc_host and mmc_add_host. The name of the function is very good. The former is to apply for a mmc_host, and the latter is to add a mmc_host. There is another operation in the middle, that is, the ops Member of the MMC is assigned with the value of "initiai_ops. To apply for mmc_host is of course very simple, that is, to apply for a struct (which we will see later because of other things in it). Where can we add the struct? View
Mmc_add_host function: int mmc_add_host (struct mmc_host * Host) // from core/host. c {int err ;...... Err = device_add (& host-> class_dev); If (ERR) return err; mmc_start_host (host); Return 0 ;}it's easy to add a device, then call mmc_start_host. Skip the device_add action first. Let's see mmc_start_host: void mmc_start_host (struct mmc_host * Host) // from/host/core. c {mmc_power_off (host); // power down mmc_detect_change (host, 0 );//???} It seems that there are only two lines of code, but concentration is the essence. mmc_power_off (host) only knows what mmc_detect_change is doing. skip this step first to see what mmc_detect_change is doing. What exactly does it do? Let's take a look. Void mmc_detect_change (struct mmc_host * Host, unsigned long delay) // Core/core. c {detail (& host-> detect, delay);} static int mmc_schedule_delayed_work (struct delayed_work * Work, unsigned long delay) {return queue_delayed_work (workqueue, work, delay );} mmc_detect_change jumps again, and finally calls queue_delayed_work. If you do not know the function, check <ldd3> and <understand the Linux kernel in depth> 〉〉, the Code tells us to add a delayed job in the workqueue queue, which is described by host-> detect, after the subsequent delay jiffies, a function recorded in host-> detect will be executed. So here, the function "initiai_probe" is over, but the process is not over yet. workqueue
This work queue is still busy. In a short time, it will call the function in host-> detect. Which function is this function and what is it used? It seems that detect is included in the host. It is estimated that it is the function set in the application just now. Let's look back at mmc_alloc_host: struct mmc_host * mmc_alloc_host (INT extra, struct device * Dev) // from core/host. c {struct mmc_host * Host; host = kzarloc (sizeof (struct mmc_host) + extra, gfp_kernel); If (! Host) return NULL; init_delayed_work (& host-> detect, mmc_rescan); Return host;} If you read the introduction of the queue_delayed_work function, I believe it will not be unfamiliar with init_delayed_work. No nonsense. Let's see mmc_rescan: // from core/host. cvoid mmc_rescan (struct work_struct * Work) /// from core/host. c {struct mmc_host * Host = container_of (work, struct mmc_host, detect. work); u32 OCR; int err ;...... /* Detect a newly inserted card */...... /** First we search for sdio... */err = mmc_send_io_op_cond (host, 0, & OCR); If (! Err) {If (mmc_attach_sdio (host, OCR) mmc_power_off (host); goto out ;}/**... then normal SD... */err = mmc_send_app_op_cond (host, 0, & OCR); If (! Err) {If (mmc_attach_sd (host, OCR) mmc_power_off (host); goto out ;}/**... and finally MMC. */err = mmc_send_op_cond (host, 0, & OCR); If (! Err) {If (mmc_attach_mmc (host, OCR) mmc_power_off (host); goto out;} mmc_release_host (host); mmc_power_off (host); out: if (host-> caps & mmc_cap_needs_poll) mmc_schedule_delayed_work (& host-> detect, Hz);} browse a function, look at the function name, and look at the comment. Do you know anything? It is detecting whether a card is inserted with a card controller. If a card is inserted, the corresponding action is taken. Here we need to understand that the SD/MMC card we usually use is a card. If we want to operate it, we need to use the SD/MMC card controller, so we can see that there is a struct mmc_card, struct mmc_host. Here, let's take a look at the operations that the function does: Prepare a mmc_host structure, and then add a master controller device to the kernel, finally, mmc_rescan is called to check whether a card is inserted. If a card is inserted, you can operate the card. What if no card is inserted? Didn't mmc_rescan be called once in white? Yes, it is indeed a white call. But why can the PC still detect the card insertion? It seems that card detection is not only performed in the last step of probe, but also in other places. Card inserts are usually inserted artificially at any time. In this case, the interruption mechanism is usually used to provide external intrusion to the system and then take action. The SD/MMC card did the same. I found it and found that an interrupt function, called the initiai_irq_cd, was registered in the initiai_probe.
IRQ card detect), this is the case. Let's take a look at this function first: static irqreturn_t 89cmci_irq_cd (int irq, void * dev_id) // host/cloudmonitor. c {struct initiai_host * Host = (struct initiai_host *) dev_id; mmc_detect_change (host-> MMC, msecs_to_jiffies (500); Return irq_handled;} You don't need to think about this function, just jump to mmc_rescan to see it. I already know whether the mmc_rescan is inserted into the detection card. Since the card can be inserted at any time, let's see what actions have been done after the card is inserted. Stage 2: In mmc_rescan, both the SD card and the MMC card must be detected. Let's proceed with the next step. Assume that someone has inserted the MMC card, the following lines should be taken: Err = mmc_send_op_cond (host, 0, & OCR); If (! Err) {If (mmc_attach_mmc (host, OCR) mmc_power_off (host); goto out;} mmc_send_op_cond this function is said to have read the card value, I am not clear about the meaning of this value. It is like reading the flash ID when detecting flash. The NIC is also like this. You don't need to care about the value, you only need to know that it can identify an MMC card to be inserted. If no error is returned for this value, mmc_attach_mmc is required:/** starting point for MMC card init. */INT mmc_attach_mmc (struct mmc_host * Host, u32 OCR) // Core/MMC. c {int err ;...... Mmc_attach_bus_ops (host); // This is related to the power management of the bus. Skip ...... /** Detect and init the card. */err = mmc_init_card (host, host-> OCR, null); If (ERR) goto err ;...... Mmc_release_host (host); err = mmc_add_card (host-> card); If (ERR) goto remove_card; return 0; remove_card :...... Err :...... Return err;} Let's look at several key functions. mmc_init_card indicates that a card is initialized from the function name. This card is described using the struct mmc_card structure, then call mmc_add_card to add the card device to the kernel. Let's see what mmc_init_card has done: static int mmc_init_card (struct mmc_host * Host, u32 OCR, struct mmc_card * oldcard) {struct mmc_card * card; int err; u32 CID [4]; unsigned int max_dtr ;...... /** Allocate card structure. */card = mmc_alloc_card (host, & mmc_type); If (is_err (card) {err = ptr_err (card); goto err;} card-> type = mmc_type_mmc; card-> RCA = 1; memcpy (card-> raw_cid, CID, sizeof (card-> raw_cid ));...... Host-> card = card; return 0; free_card :...... Err :...... Return err;} deletes all hardware operations, and finally applies for a struct mmc_card structure for mmc_alloc_card, then assign mmc_type_mmc to the card-> type, and finally assign the card to the host-> card, which is consistent with the specific hardware, because a master controller usually inserts a card, if there is a card, host-> card has a value. If there is no card, host-> card itself is null. Get into mmc_alloc_card to see:/** allocate and initialise a new MMC card structure. */struct mmc_card * struct (struct mmc_host * Host, struct device_type * type) {struct mmc_card * card; card = kzarloc (sizeof (struct mmc_card), gfp_kernel); If (! Card) return err_ptr (-enomem); card-> host = host; device_initialize (& Card-> Dev); card-> Dev. Parent = mmc_classdev (host ); Card-> Dev. Bus = & mmc_bus_type; Card-> Dev. release = mmc_release_card; card-> Dev. type = type; return card;} the struct mmc_card structure contains a struct device structure. The mmc_alloc_card not only applies for memory, but also fills several members in the struct device, especially card-> Dev. bus = & mmc_bus_type; this sentence should be focused on. After applying for a mmc_card structure and simple initialization, the mission of mmc_init_card is complete, and then call mmc_add_card to add the card device to the kernel. Mmc_add_card is actually very simple. It is to call device_add to add card-> Dev to the kernel. Anyone who knows the bus model knows that the bus in device_add should act. Which bus is the specific one? It depends on which bus is specified in the dev you sent when calling device_add. The card we sent> Dev. What is the specific point of card> Dev. Bus? The mmc_bus_type: static struct bus_type mmc_bus_type = {. name = "MMC ",. dev_attrs = mmc_dev_attrs ,. match = mmc_bus_match ,. uevent = mmc_bus_uevent ,. probe = mmc_bus_probe ,. remove = mmc_bus_remove ,. suspend = mmc_bus_suspend ,. resume = mmc_bus_resume,}; In device_add, the bus corresponding to the device will match your device and all drivers mounted on the bus. Then, the match function is called, if yes, the probe function of the bus or the probe function of the driver will be called. Let's take a look at how mmc_bus_match matches: Static int mmc_bus_match (struct device * Dev, struct device_driver * DRV) {return 1;} it seems that match will always succeed, so run Probe: static int mmc_bus_probe (struct device * Dev) {struct mmc_driver * DRV = to_mmc_driver (Dev-> driver); struct mmc_card * card = dev_to_mmc_card (Dev ); return DRV-> probe (card);} This is a bit of trouble. In this function, we call DRV-> probe (). What is this DRV? Above: struct mmc_driver * DRV = to_mmc_driver (Dev-> driver); the match function always returns 1. It seems that any driver hanging on this bus may come here, the same is true, but fortunately there is only one driver hanging on this bus. It is defined as static struct mmc_driver = {. DRV = {. name = "mmcblk ",},. probe = mmc_blk_probe ,. remove = mmc_blk_remove ,. suspend = mmc_blk_suspend ,. resume = mmc_blk_resume,}; when you see this, card/CORE/host has all been pulled in. When you look at several functions in mmc_driver, you will understand how they can be linked. Let's continue. Stage 3: we have seen that DRV-> probe is called in bus probe, and this function corresponds to mmc_blk_probe. How does mmc_driver mount to mmc_bus, let's look at mmc_blk_init (), just a few lines of code, it should be difficult. Static int mmc_blk_probe (struct mmc_card * card) // from card/block. c {struct mmc_blk_data * md; int err ;...... MD = mmc_blk_alloc (card); If (is_err (MD) return ptr_err (MD );...... Add_disk (MD-> disk); Return 0; Out: mmc_blk_put (MD); Return err;} Let's look at the important functions. When we see this function finally called add_disk, what do you think? If you don't know what I'm talking about, I guess you haven't seen ldd3, or you can just watch it out. Let me tell you: If you see add_disk, It means there will be alloc_disk and the initialization queue action in front, which is not shown in mmc_blk_probe, then let's look at the line of mmc_blk_alloc (card: static struct mmc_blk_data * partition (struct mmc_card * card) {struct mmc_blk_data * md; int devidx, RET; devidx = random (dev_use, mmc_num_minors); If (devidx> = random) return err_ptr (-enospc); _ set_bit (devidx, dev_use); Md = kzarloc (sizeof (struct mmc_blk_data), gfp_kern El); If (! MD) {ret =-enomem; goto out;}/** set the read-only status based on the supported commands * and the write protect switch. */MD-> read_only = mmc_blk_readonly (card); MD-> disk = alloc_disk (1 <mmc_shift); If (MD-> disk = NULL) {ret =-enomem; goto err_kfree;} spin_lock_init (& MD-> lock); MD-> usage = 1; ret = mmc_init_queue (& MD-> queue, card, & MD-> lock); If (RET) goto err_putdisk; MD-> queue. issue _ Fn = mmc_blk_issue_rq; MD-> queue. data = md; MD-> disk-> major = mmc_block_major; MD-> disk-> first_minor = devidx <mmc_shift; MD-> disk-> fops = & mmc_bdops; MD-> disk-> private_data = md; MD-> disk-> queue = MD-> queue. queue; MD-> disk-> driverfs_dev = & Card-> dev;/** as discussed on lkml, genhd_fl_removable shocould: **-be set for removable media with permanent Block devices *-be unset for removable BL Ock devices with permanent media ** since MMC Block devices clearly fall under the second * case, we do not set genhd_fl_removable. userspace * shocould use the block device creation/destruction hotplug * messages to tell when the card is present. */sprintf (MD-> disk-> disk_name, "mmcblk % d", devidx); blk_queue_logical_block_size (MD-> queue. queue, 512); If (! Mmc_card_sd (card) & mmc_card_blockaddr (card) {/** the ext_csd sector count is in number or 512 byte * sectors. */set_capacity (MD-> disk, card-> ext_csd.sectors);} else {/** the CSD Capacity field is in units of read_blkbits. * set_capacity takes units of 512 bytes. */set_capacity (MD-> disk, card-> CSD. capacity <(card-> CSD. read_blkbits-9);} return md; err_putdisk: put_disk (MD-> disk); err _ Kfree: kfree (MD); Out: Return err_ptr (RET);} when we see the code of this function, we naturally recall the entire block device-driven routine: 1. allocate and initialize request queues, And Bind Request queues and request functions. 2. Allocate, initialize gendisk, assign values to major, fops, queue, and other members of gendisk, and add gendisk. 3. register the block device driver. Let's see if the MMC driver follows this rule. 1. mmc_init_queue initially queues and binds mmc_blk_issue_rq; function to the Request function; 2. alloc_disk is allocated with the gendisk structure, major, fops, and queue are initialized;

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.