HOST: Gentoo Linux 11.2 with Linux kernel 3.0.6
Hardware Platform: fl2440 (S3C2440) with Linux kernel 2.6.35
Original works, reprinted please indicate the source http://blog.csdn.net/yming0221/article/details/7205713
* Link to the arm-Linux driver-MTD driver analysis (1)
1. mtd_notifier struct
// MTD device notification struct mtd_notifier {void (* Add) (struct mtd_info * MTD); // execute void (* remove) when adding the original MTD/character/block Device) (struct mtd_info * MTD); // execute struct list_head list when removing the original/character/block device; // list is a two-way linked list, defined in include/Linux/list. h };
Struct list_head is defined in/include/Linux/list. h. The macro definition and function in the kernel are as follows:
Init_list_head (PTR) initializes the PTR node as the header, and points both the forward and subsequent directions to itself.
List_head (name) declares and initializes the name of a two-way circular linked list.
Static inline void _ list_add (struct list_head * New, struct list_head * Prev, struct list_head * Next)
Insert element new between Prev and next in the linked list
Static inline void list_add (struct list_head * New, struct list_head * head)
Insert the new element after the first node in the linked list and call _ list_add.
Static inline void list_add_tail (struct list_head * New, struct list_head * head)
Insert element new at the end of the linked list and call _ list_add.
Static inline void _ list_del (struct list_head * Prev, struct list_head * Next)
Delete the elements between Prev and next in the linked list.
Static inline void list_del (struct list_head * entry)
Delete the element entry in the linked list.
Static inline void list_del_init (struct list_head * entry)
Delete the element entry from the linked list and initialize it as a new linked list.
Static inline void list_move (struct list_head * List, struct list_head * head)
Delete the list element from the linked list and add it to the head linked list.
Static inline void list_move_tail (struct list_head * List, struct list_head * head)
Move the list to the end of the linked list.
Static inline int list_empty (const struct list_head * head)
Test whether the linked list is empty.
Static inline void _ list_splice (struct list_head * List, struct list_head * head)
Merge the list and head of the linked list.
Static inline void list_splice (struct list_head * List, struct list_head * head)
When list is not empty, call _ list_splice () to merge list and head.
Static inline void list_splice_init (struct list_head * List, struct list_head * head)
Merge two linked lists and initialize the list.
List_entry (PTR, type, member)
What is the definition of list_entry?
A. list_entry is defined in the kernel source File Include/Linux/list. h:
# Define list_entry (PTR, type, member)
(Type *) (char *) (PTR)-(unsigned long) (& (type *) 0)-> member )))
B. Its function is to convert the pointer PTR of list_head to the starting address of its host structure. The host structure is type, and PTR is defined as a Member member in its host structure.
2. add_mtd_device Function
/*** Add_mtd_device-register an MTD device * @ MTD: pointer to new MTD device INFO structure *** Add a device to the list of MTD devices present in the system, and * policy each currently active MTD 'user' of its arrival. returns * zero on success or 1 on failure, which currently will only happen * if there is insufficient memory or a sysfs error. * /// Add the MTD device function, add the MTD device to the MTD device linked list, and notify all MTD users of the MTD device. 0 indicates success, and 1 indicates an error (insufficient memory or file system error) int add_mtd_device (struct mtd_info * MTD) {struct mtd_notifier * not; // define an MTD device notifier int I, error; // The following is the mtd_info struct information if (! MTD-> backing_dev_info) {Switch (MTD-> type) {Case mtd_ram: // mtd_ram is defined in include/MTD/mtd-abi.hmtd-> backing_dev_info = & mtd_bdi_rw_mappable; break; Case mtd_rom: MTD-> backing_dev_info = & mtd_bdi_ro_mappable; break; default: MTD-> backing_dev_info = & mtd_bdi_unmappable; break ;}} bug_on (MTD-> writesize = 0 ); mutex_lock (& mtd_table_mutex); // lock the operation mtd_table do {If (! Idr_pre_get (& mtd_idr, gfp_kernel) // allocate memory goto fail_locked for mtd_idr; error = idr_get_new (& mtd_idr, MTD, & I ); // associate the ID with mtd_idr} while (error =-eagain); If (error) goto fail_locked; MTD-> Index = I; MTD-> usecount = 0; if (is_power_of_2 (MTD-> erasesize) MTD-> erasesize_shift = FFs (MTD-> erasesize)-1; elsemtd-> erasesize_shift = 0; if (is_power_of_2 (MTD-> writesize) MTD-> writesize_shift = FFs (MTD-> writesize)-1; els Emtd-> writesize_shift = 0; MTD-> erasesize_mask = (1 <MTD-> erasesize_shift)-1; MTD-> writesize_mask = (1 <MTD-> writesize_shift) -1;/* Some chips always power up locked. unlock them now */If (MTD-> flags & mtd_writeable) & (MTD-> flags & mtd_powerup_lock) & MTD-> unlock) {If (MTD-> unlock (MTD, 0, MTD-> size) printk (kern_warning "% s: Unlock failed, writes may not work \ n ", MTD-> name);}/* caller Shou Ld have set Dev. parent to match the * physical device. */MTD-> Dev. type = & mtd_devtype; MTD-> Dev. class = & mtd_class; MTD-> Dev. devt = mtd_devt (I); // set the MTD device name dev_set_name (& MTD-> Dev, "MTD % d", I ); // set MTD device information mtd_infodev_set_drvdata (& MTD-> Dev, MTD); // register the device if (device_register (& MTD-> Dev )! = 0) goto fail_added; // create a device if (mtd_devt (I) device_create (& mtd_class, MTD-> Dev. parent, mtd_devt (I) + 1, null, "MTD % dro", I); debug (0, "MTD: giving out device % d to % s \ n ", i, MTD-> name);/* No Need To Get A refcount on the module containing the notifier, since we hold the mtd_table_mutex * // traverse the list linked list and execute the add () function for each mtd_notifier to operate the newly added MTD device, notify all MTD users of the arrival of new MTD devices list_for_each_entry (not, & mtd_notifiers, list) Not-> Add (MTD); // unlock the semaphore mutex_unlock (& mtd_table_mutex ); /* We _ know _ we aren't being removed, because our caller is still holding us here. so none of this try _ nonsense, and no bitching about it either. */_ module_get (this_module); Return 0; fail_added: idr_remove (& mtd_idr, I); fail_locked: mutex_unlock (& mtd_table_mutex); return 1 ;}
The IDR mechanism used is as follows:
(1) Obtain IDR
To use IDR in code, you must first include <Linux/IDR. h>. Next, we need to allocate the IDR struct in the Code and initialize it:
Void idr_init (struct IDR * IDP );
IDR is defined as follows:
Struct IDR {
Struct idr_layer * top;
Struct idr_layer * id_free;
Int layers;
Int id_free_cnt;
Spinlock_t lock;
};
/* IDR is the core structure of the IDR Mechanism */
(2) allocate memory for IDR
Int idr_pre_get (struct IDR * IDP, unsigned int gfp_mask );
Before obtaining an idnumber through IDR, You need to allocate memory.
0 indicates an error, and a non-zero value indicates normal.
(3) Allocate an idnumber and associate the ID number with the pointer.
Int idr_get_new (struct IDR * IDP, void * PTR, int * ID );
Int idr_get_new_abve (struct IDR * IDP, void * PTR, int start_id, int * ID );
IDP: the IDR pointer previously initialized through idr_init
ID: Id automatically assigned by the kernel
PTR: pointer associated with ID
Start_id: Start ID. When the kernel allocates an idnumber, it starts from start_id. If you assign an ID to an I2C node, you can use the device address as start_id.
If the function call is normal, 0 is returned. If no ID is available,-enospc is returned.
In practice, the above functions are often used as follows:
Again:
If (idr_pre_get (& my_idr, gfp_kernel) = 0 ){
/* No memory, give up entirely */
}
Spin_lock (& my_lock );
Result = idr_get_new (& my_idr, & target, & ID );
If (result =-eagain ){
Sigh ();
Spin_unlock (& my_lock );
Goto again;
}
(4) Search for the corresponding pointer using the ID number
Void * idr_find (struct IDR * IDP, int ID );
The returned value is a pointer associated with the given ID. If no pointer exists, null is returned.
(5) delete ID
To delete an ID, use:
Void idr_remove (struct IDR * IDP, int ID );
Using the above methods, the kernel code can generate the corresponding ID number for the sub-device and inode. These functions are defined in lib/IDR. C.
3. del_mtd_device Function
/*** Del_mtd_device-unregister an MTD device * @ MTD: pointer to MTD device INFO structure *** remove a device from the list of MTD devices present in the system, * And between Y each currently active MTD 'user' of its departure. * returns zero on success or 1 on failure, which currently will happen * If the requested device does not appear to be present in the list. * /// Delete the MTD device function. // Remove the MTD device information from the MTD device linked list and notify all MTD users to remove the MTD device. // If 0 is returned, success is returned. If 1 is returned, an error occurs (the device information does not exist in the device linked list). Int del_mtd_device (struct mtd_info * MTD) {int ret; struct mtd_notifier * not; // define a mtd_notifier pointer mutex_lock (& mtd_table_mutex); If (idr_find (& mtd_idr, MTD-> index )! = MTD) {ret =-enodev; goto out_error;}/* No Need To Get A refcount on the module containingthe notifier, since we hold the mtd_table_mutex * // traverses the list linked list, then, each mtd_notifier executes the remove function to notify each MTD user of the device's removal list_for_each_entry (not, & mtd_notifiers, list) Not-> remove (MTD); If (MTD-> usecount) {printk (kern_notice "Removing MTD device # % d (% s) with use count % d \ n", MTD-> index, MTD-> name, MTD-> usecount ); ret =-ebusy;} else {device_unregister (& MTD-> Dev); // remove the MTD device idr_remove (& mtd_idr, MTD-> index ); // remove the mtd id and release the allocated memory module_put (this_module); ret = 0;} out_error: mutex_unlock (& mtd_table_mutex); return ret ;}
4. register_mtd_user Function
/*** Register_mtd_user-register a 'user' of MTD devices. * @ New: pointer to notifier INFO structure ** registers a pair of callbacks function to be called upon addition * or removal of MTD devices. causes the 'add' callback to be immediately * invoked for each MTD device currently present in the system. * /// the user of the original MTD device registers the MTD device (specific character device or block device) // The parameter is the new MTD notification device and adds it to the mtd_notifiers queue, then void Merge (struct mtd_notifier * New) {struct mtd_info * MTD; mutex_lock (& mtd_table_mutex); // Insert the new-> List header into the list_add (& New-> list, & mtd_notifiers); _ module_get (this_module); // execute the Add function mtd_for_each_device (MTD) New-> Add (MTD); mutex_unlock (& mtd_table_mutex) on the original MTD );}
5. unregister_mtd_user Function
/*** Unregister_mtd_user-unregister A 'user' of MTD devices. * @ old: pointer to notifier INFO structure ** removes a callback function pair from the list of 'users' to be * notified upon addition or removal of MTD devices. causes the * 'delete' callback to be immediately invoked for each MTD device * currently present in the system. * /// Delete the MTD device. // Notify all MTD devices of the original MTD to execute the remove () function, and delete the int unregister_mtd_user (struct mtd_notifier * old) of the deleted MTD device from the mtd_notifier queue) {struct mtd_info * MTD; mutex_lock (& mtd_table_mutex); module_put (this_module); // notify all MTD devices of the original MTD to execute the remove () function mtd_for_each_device (MTD) old-> remove (MTD); // Delete the list_del (& old-> List) from the mtd_notifier queue of the deleted MTD device; mutex_unlock (& mtd_table_mutex ); return 0 ;}
6. Get the operation pointer of The MTD device, but the parameters are different. One is to get the operation address of the MTD device according to the device address and the other is the name of the installation device.
Struct mtd_info * get_mtd_device (struct mtd_info * MTD, int num)
Struct mtd_info * get_mtd_device_nm (const char * name)
The first function is analyzed below.
/*** Get_mtd_device-obtain a validated handle for an MTD device * @ MTD: last known address of the required MTD device * @ num: internal device Number of the required MTD device ** given a number and null address, return the num 'th entry in the device * table, if any. given an address and num =-1, search the device table * for a device with that address and return if it's still present. given * both, R Eturn the num 'th driver only if its address matches. return * error code if not. * // obtain the operation address of the MTD device based on the device address: struct mtd_info * get_mtd_device (struct mtd_info * MTD, int num) {struct mtd_info * ret = NULL, * Other; int err =-enodev; // lock mtd_table for mutex_lock (& mtd_table_mutex); If (num =-1) {// num =-1 & if the linked list is not empty, the MTD address mtd_for_each_device (other) {If (Other = MTD) {ret = MTD; break ;}} is returned ;}}} else if (Num> = 0) {// n Um> = 0. Find the num device. If it is not empty, return the address. If it is empty, return nullret = idr_find (& mtd_idr, num); If (MTD & MTD! = RET) ret = NULL;} If (! RET) {ret = err_ptr (ERR); goto out;} err = _ get_mtd_device (RET); // If (ERR) ret = err_ptr (ERR); out: mutex_unlock (& mtd_table_mutex); // unlock mutex semaphore return ret;} int _ get_mtd_device (struct mtd_info * MTD) {int err; If (! Try_module_get (MTD-> owner) Return-enodev; If (MTD-> get_device) {err = MTD-> get_device (MTD); If (ERR) {module_put (MTD-> owner); Return err ;}} MTD-> usecount ++; // Add the user counter of the original MTD to return 0 ;}
Second Function
/*** Get_mtd_device_nm-obtain a validated handle for an MTD device by * device name * @ Name: MTD device name to open ** this function returns MTD device description structure in case of * success and an error code in case of failure. * /// obtain the operation address of the original MTD device through the device name. // This function is similar to the above function, however, the struct mtd_info * get_mtd_device_nm (const char * Name) {int err =-enodev; struct mtd_info * MTD = NULL is returned by comparing the name field of the MTD device cyclically, * Other; mutex_lock (& mtd_table_mutex); mtd_for_each_device (other) {If (! Strcmp (name, other-> name) {MTD = Other; break;} If (! MTD) goto out_unlock; If (! Try_module_get (MTD-> owner) goto out_unlock; If (MTD-> get_device) {err = MTD-> get_device (MTD); If (ERR) goto out_put ;} MTD-> usecount ++; mutex_unlock (& mtd_table_mutex); Return MTD; out_put: module_put (MTD-> owner); out_unlock: mutex_unlock (& mtd_table_mutex ); return err_ptr (ERR );}
Next, we will analyze how MTD raw device partition is implemented: Arm-Linux driver-MTD driver analysis (III)