ARM-Linux驅動–MTD驅動分析(二)

來源:互聯網
上載者:User

主機:Gentoo Linux 11.2 with linux kernel 3.0.6
硬體平台:FL2440(S3C2440)with linux kernel 2.6.35
原創作品,轉載請標明出處http://blog.csdn.net/yming0221/article/details/7205713


*接上文 ARM-Linux驅動--MTD驅動分析(一)

1、mtd_notifier結構體

//MTD裝置通知結構體struct mtd_notifier {void (*add)(struct mtd_info *mtd);//加入MTD原始/字元/塊裝置時執行void (*remove)(struct mtd_info *mtd);//移除MTD原始/字元/塊裝置時執行struct list_head list;//list是雙向鏈表,定義在include/linux/list.h};

而struct list_head定義在/include/linux/list.h中,核心中其宏定義和函數如下

INIT_LIST_HEAD(ptr) 初始化ptr節點為表頭,將前趨與後繼都指向自己。
LIST_HEAD(name) 聲明並初始化雙向迴圈鏈表name。

static inline void __list_add(struct list_head *new, struct list_head *prev, struct list_head *next)
向鏈表中在prev與next之間插入元素new
static inline void list_add(struct list_head *new, struct list_head *head)
在鏈表中前端節點後插入元素new,調用__list_add()實現。
static inline void list_add_tail(struct list_head *new, struct list_head *head)
在鏈表末尾插入元素new,調用__list_add()實現。

static inline void __list_del(struct list_head * prev, struct list_head * next)
刪除鏈表中prev與next之間的元素。
static inline void list_del(struct list_head *entry)
刪除鏈表中的元素entry。

static inline void list_del_init(struct list_head *entry)
從鏈表中刪除元素entry,並將其初始化為新的鏈表。
static inline void list_move(struct list_head *list, struct list_head *head)
從鏈表中刪除list元素,並將其加入head鏈表。
static inline void list_move_tail(struct list_head *list, struct list_head *head)
把list移動到鏈表末尾。

static inline int list_empty(const struct list_head *head)
測試鏈表是否為空白。

static inline void __list_splice(struct list_head *list, struct list_head *head)
將鏈表list與head合并。
static inline void list_splice(struct list_head *list, struct list_head *head)
在list不為空白的情況下,調用__list_splice()實現list與head的合并。
static inline void list_splice_init(struct list_head *list, struct list_head *head)
將兩鏈表合并,並將list初始化。

list_entry(ptr, type, member)
list_entry的定義是怎麼回事?
a. list_entry的定義在核心源檔案include/linux/list.h中:
#define list_entry(ptr, type, member)
((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
b. 其功能是根據list_head型指標ptr換算成其宿主結構的起始地址,該宿主結構是type型的,而ptr在其宿主結構中定義為member成員。 

2、add_mtd_device函數

/** *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 *notify 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. *///添加MTD裝置函數,將MTD裝置加入MTD裝置鏈表,並通知所有的MTD user該MTD裝置。返回0表示成功,返回1表示出錯(記憶體不足或檔案系統錯誤)int add_mtd_device(struct mtd_info *mtd){struct mtd_notifier *not;//定義一個MTD裝置通知器int i, error;//下面是設定mtd_info結構體資訊if (!mtd->backing_dev_info) {switch (mtd->type) {case MTD_RAM://MTD_RAM定義在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);//給操作mtd_table加鎖do {if (!idr_pre_get(&mtd_idr, GFP_KERNEL))//為mtd_idr分配記憶體goto fail_locked;error = idr_get_new(&mtd_idr, mtd, &i);//將id號和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;elsemtd->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 should 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);//設定mtd裝置名稱dev_set_name(&mtd->dev, "mtd%d", i);//設定mtd裝置資訊mtd_infodev_set_drvdata(&mtd->dev, mtd);//註冊裝置if (device_register(&mtd->dev) != 0)goto fail_added;//建立裝置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 *///遍曆list鏈表將每個mtd_notifier執行add()函數,對新加入的mtd裝置操作,通知所有的MTD user新的MTD裝置的到來list_for_each_entry(not, &mtd_notifiers, list)not->add(mtd);//解鎖訊號量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;}

其中用到的IDR機制如下:

(1)獲得idr
要在代碼中使用idr,首先要包括<linux/idr.h>。接下來,我們要在代碼中分配idr結構體,並初始化:
void idr_init(struct idr *idp);
其中idr定義如下:
struct idr {
struct idr_layer *top;
struct idr_layer *id_free;
int layers;
int id_free_cnt;
spinlock_t lock;
};
/* idr是idr機制的核心結構體 */
(2)為idr分配記憶體
int idr_pre_get(struct idr *idp, unsigned int gfp_mask);
每次通過idr獲得ID號之前,需要先分配記憶體。
返回0表示錯誤,非零值代表正常
(3)分配ID號並將ID號和指標關聯
int idr_get_new(struct idr *idp, void *ptr, int *id);
int idr_get_new_above(struct idr *idp, void *ptr, int start_id, int *id);
idp: 之前通過idr_init初始化的idr指標
id: 由核心自動分配的ID號
ptr: 和ID號相關聯的指標
start_id: 起始ID號。核心在分配ID號時,會從start_id開始。如果為I2C節點分配ID號,可以將裝置地址作為start_id
函數調用正常返回0,如果沒有ID可以分配,則返回-ENOSPC
在實際中,上述函數常常採用如下方式使用:
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)通過ID號搜尋對應的指標
void *idr_find(struct idr *idp, int id);
傳回值是和給定id相關聯的指標,如果沒有,則返回NULL
(5)刪除ID
要刪除一個ID,使用:
void idr_remove(struct idr *idp, int id);
通過上面這些方法,核心代碼可以為子裝置,inode產生對應的ID號。這些函數都定義在lib/idr.c中

3、del_mtd_device函數

/** *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 notify 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. *///刪除mtd裝置函數。//從MTD裝置的鏈表中移除該MTD裝置資訊,並通知系統中所有的MTD user該MTD裝置的移除。//返回0表示成功,返回1表示出錯(該裝置資訊不存在裝置鏈表中)int del_mtd_device (struct mtd_info *mtd){int ret;struct mtd_notifier *not;//定義一個mtd_notifier指標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 *///遍曆list鏈表,並使每個mtd_notifier執行remove函數,通知每個MTD user該裝置的移除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);//移除MTD裝置idr_remove(&mtd_idr, mtd->index);//移除mtd的id號並釋放已指派的記憶體module_put(THIS_MODULE);ret = 0;}out_error:mutex_unlock(&mtd_table_mutex);return ret;}

4、register_mtd_user函數

/** *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. *///MTD原始裝置使用者註冊MTD裝置(具體的字元裝置或塊裝置)//參數是新的mtd通知器,將其加入mtd_notifiers隊列,然後void register_mtd_user (struct mtd_notifier *new){struct mtd_info *mtd;mutex_lock(&mtd_table_mutex);//將new->list頭插mtd_notifiers入鏈表list_add(&new->list, &mtd_notifiers); __module_get(THIS_MODULE);//對每個MTD原始裝置執行add函數mtd_for_each_device(mtd)new->add(mtd);mutex_unlock(&mtd_table_mutex);}

5、unregister_mtd_user函數

/** *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 *'remove' callback to be immediately invoked for each MTD device *currently present in the system. *///刪除MTD裝置。//通知所有該MTD原始裝置的MTD裝置執行remove()函數,將被刪除的MTD裝置的通知器從mtd_notifier隊列中刪除int unregister_mtd_user (struct mtd_notifier *old){struct mtd_info *mtd;mutex_lock(&mtd_table_mutex);module_put(THIS_MODULE);//通知所有該MTD原始裝置的MTD裝置執行remove()函數mtd_for_each_device(mtd)old->remove(mtd);//將被刪除的MTD裝置的通知器從mtd_notifier隊列中刪除list_del(&old->list);mutex_unlock(&mtd_table_mutex);return 0;}

6、擷取MTD裝置的操作指標,只是參數不同,一個是按照裝置地址,另一個是安裝裝置的名稱來擷取MTD裝置的操作地址

struct mtd_info *get_mtd_device(struct mtd_info *mtd, int num)

struct mtd_info *get_mtd_device_nm(const char *name)

下面現分析第一個函數

/** *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, return the num'th driver only if its address matches. Return *error code if not. *///根據裝置地址來擷取MTD裝置的操作地址struct mtd_info *get_mtd_device(struct mtd_info *mtd, int num){struct mtd_info *ret = NULL, *other;int err = -ENODEV;//給mtd_table加鎖,以便互斥訪問mutex_lock(&mtd_table_mutex);if (num == -1) {//num=-1&&鏈表不空,則返回mtd的地址mtd_for_each_device(other) {if (other == mtd) {ret = mtd;break;}}} else if (num >= 0) {//num>=0,尋找第num個裝置,若不空,返回地址,若為空白,返回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);//解鎖互斥訊號量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++;//增加該MTD原始裝置的使用者計數器return 0;}

第二個函數

/** *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. *///通過裝置名稱來獲得相應的MTD原始裝置的操作地址//該函數和上面的函數類似,不過就是通過迴圈比較MTD裝置的name欄位來返回struct mtd_info *get_mtd_device_nm(const char *name){int err = -ENODEV;struct mtd_info *mtd = NULL, *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);}

下篇分析MTD原始裝置的分區實現方法ARM-Linux驅動--MTD驅動分析(三)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

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.