struct device / device_driver 結構體
linux/include/linux/device.h
struct device
{
struct klist klist_children;
struct klist_node knode_parent; /* node in sibling list */
struct klist_node knode_driver;
struct klist_node knode_bus;
struct device *parent;
struct kobject kobj;
char bus_id[BUS_ID_SIZE]; /* position on parent bus */
struct device_type *type;
unsigned is_registered:1;
unsigned uevent_suppress:1;
struct semaphore sem; /* semaphore to synchronize calls to * its driver.*/
struct bus_type * bus; /* type of bus device is on */
struct device_driver *driver; /* which driver has allocated this device */
void *driver_data; /* data private to the driver */
void *platform_data; /* Platform specific data, device core doesn't touch it */
struct dev_pm_info power;
#ifdef CONFIG_NUMA
int numa_node; /* NUMA node this device is close to */
#endif
u64 *dma_mask; /* dma mask (if dma'able device) */
u64 coherent_dma_mask;/* Like dma_mask, but for alloc_coherent mappings as not all
hardware supports 64 bit addresses for consistent allocations such descriptors. */
struct list_head dma_pools; /* dma pools (if dma'ble) */
struct dma_coherent_mem *dma_mem; /* internal for coherent mem override */
/* arch specific additions */
struct dev_archdata archdata;
spinlock_t devres_lock;
struct list_head devres_head; /* class_device migration path */
struct list_head node;
struct class *class;
dev_t devt; /* dev_t, creates the sysfs "dev" */
struct attribute_group **groups; /* optional groups */
void (*release)(struct device * dev);
};
二、作用:
用於描述裝置相關的資訊裝置之間的層次關係,以及裝置與匯流排、驅動的關係。
三、詳解:
1、struct klist klist_children;
struct klist被定義在linux/include/linux/klist.h中,原型是:
struct klist {
spinlock_t k_lock;
struct list_head k_list;
void (*get)(struct klist_node *);
void (*put)(struct klist_node *);
};
可見它是對struct list_head的擴充套件,在此它的作用是連線裝置列表中的孩子列表。
2、struct klist_node knode_parent; /* node in sibling list */
struct klist_node被定義在linux/include/linux/klist.h,原型是:
struct klist_node {
struct klist * n_klist;
struct list_head n_node;
struct kref n_ref;
struct completion n_removed;
};
在此它的作用是表示它的兄弟節點。
3、struct klist_node knode_driver;
表示它的驅動節點。
4、struct klist_node knode_bus;
表示匯流排節點。
5、struct device *parent;
指向其父裝置。
6、struct kobject kobj;
這裡http://blog.chinaunix.net/u1/55599/showart.php?id=1086478有對kobject的解釋,此處
它是內嵌的一個kobject物件。
7、char bus_id[BUS_ID_SIZE];
bus_id表示其在父總線上的位置。BUS_ID_SIZE被定義為:
#define KOBJ_NAME_LEN 20 /*linux/include/linux/kobject.h*/
#define BUS_ID_SIZE KOBJ_NAME_LEN /*linux/include/linux/device.h*/
所以表示位置的字串長度不能超過20。
8、struct device_type *type;
被定義在/linux/include/linux/device.h中,原型是:
struct device_type {
const char *name;
struct attribute_group **groups;
int (*uevent)(struct device *dev, char **envp, int num_envp,char *buffer, int
buffer_size);
void (*release)(struct device *dev);
int (*suspend)(struct device * dev, pm_message_t state);
int (*resume)(struct device * dev);
};
device_type結構表示裝置的型別。一個裝置類或者匯流排可以包含不同型別的裝置,例如“分割槽
”和“磁碟” , “滑鼠”和“事件” 。device_type就可以標識某個裝置型別和該裝置的特有
資訊,它就等同於kobject結構中的kobj_type一樣。如果name資料成員被指定,那麼uevent成員
函式就會把它包含在DEVTYPE變數中。
9、unsigned is_registered:1;
標識該裝置是否已經被註冊過。is_registered:1這樣的形式表示is_registered這個變數只有一
位。在32位linux系統下,unsigned是4位元組32位,而經過is_registered:1這樣的限制後,變數
is_registered只有一位,其取值只能是1或者0,相當於聲明瞭一個boolean型別的變數。在此種
用法中,後面指定資料寬度的值只能大於0且小於本身的資料寬度。
10、struct bus_type * bus;
指向所連線匯流排的指標。
11、struct device_driver *driver;
指向被分配到該裝置的裝置驅動。
12、u64 *dma_mask; /*指向裝置DMA遮蔽字。*/
u64 coherent_dma_mask;/*裝置一致性DMA的遮蔽字。*/
struct list_head dma_pools; /*聚集的DMA緩衝池。*/
struct dma_coherent_mem *dma_mem; /*指向裝置所使用的一致性DMA儲存器描述符的指標*/
13、spinlock_t devres_lock;
定義一個裝置自旋鎖,用於互斥訪問裝置。關於自旋鎖的詳細講解參考:
http://www.deansys.com/doc/ldd3/ch05s05.html
14、void (*release)(struct device * dev);
釋放裝置描述符的回撥函式。
四、操作:
linux核心系統了一系列完整的對device操作的函式。
1、其中device_register()函式用來將一個新的device物件插入裝置模型。它在
linux/drivers/base/core.c中被實現:
int device_register(struct device *dev){ device_initialize(dev);
return device_add(dev);}
該函式首先是呼叫device_initialize()初始化device結構,具體是初始化嵌入的kobject結構
dev->kobj,初始化列表中的孩子列表kobj->klist_children,初始化DMA緩衝池dev->dma_pools,
初始化自旋鎖dev->devres_lock等。接下來device_add()函式才真正將該device物件dev插入設
備模型中。device_add()函式首先是通過kboject_add()函式將它新增到kobject層次,再把它添
加都全域性和兄弟連結串列中,最後新增到其他相關的子系統的驅動程式模型,完成device物件的註冊
。
2、device_unregister()完成相反的過程:/linux/drivers/base/core.c
void device_unregister(struct device * dev){
pr_debug("DEV: Unregistering device. ID = '%s'\n", dev->bus_id);
device_del(dev);
put_device(dev);
}
它會先以KERN_DEBUG級別列印登出裝置的資訊,然後才真正刪除裝置,減少裝置物件的引用計數
。
3、get_device()和put_device()分別是增加和減少裝置物件的引用計數。這兩個函式都定義在
:/linux/drivers/base/core.c中。具體是應用在註冊device物件時,device_add()函式會呼叫
get_device()增加對該device物件的引用計數。在登出裝置物件時,device_unregister()函式
直接呼叫put_device()函式減少對該device物件的引用計數。
device_driver
系統的每一個驅動程式都由struct device_driver,定義在/include/linux/device.h:
struct device_driver {
const char * name;
/*裝置驅動程式的名稱。在呼叫driver_register()往裝置驅動程式模型中插入一個新的device_driver物件時,driver_register()函式會呼叫bus_add_driver()函式,bus_add_driver()呼叫kobject_set_name()函式將name賦給drv>kobj.name或者drv->kobj.k_name。注:drv為要呼叫driver_register()註冊的device_driver型別的物件。*/
struct bus_type * bus; //指向匯流排描述符的指標,匯流排連線所支援的裝置
struct kobject kobj; //內嵌的kobject結構,主要用於計數
struct klist klist_devices;
struct klist_node knode_bus;
struct module * owner;
const char * mod_name;
struct module_kobject * mkobj;
int (*probe) (struct device * dev); //探測函式
int (*remove) (struct device * dev); //移除驅動
void (*shutdown) (struct device * dev); //關閉驅動
int (*suspend) (struct device * dev, pm_message_t state); //掛起驅動
int (*resume) (struct device * dev); //將掛起的驅動恢復執行
};
呼叫函式:
1、driver_register()函式:/linux/drivers/base/driver.c
int driver_register(struct device_driver * drv)
{if ((drv->bus->probe && drv->probe) ||(drv->bus->remove && drv->remove) ||(drv->bus->shutdown && drv->shutdown))
{printk(KERN_WARNING "Driver '%s' needs updating - please use bus_type methods/n", drv->name);}
klist_init(&drv->klist_devices, NULL, NULL);
return bus_add_driver(drv);}
函式首先是檢查device_driver型別的drv物件和其bus域的回撥函式是否已經被賦值,如果為空,則列印警告。核心的註冊是呼叫bus_add_driver()函式實現。在bus_add_driver()函式中,首先是檢查drv->bus,如果為空可立即返回EINVAL(無效的變數),註冊失敗,可見bus域必須提前初始化好才行。接下來是對kobj域進行初始化,檢查bus域等。最後呼叫add_bind_files()函式(實際上是由add_bind_files()函式呼叫driver_create_file()函式)在sys檔案系統下為其建立一個新的目錄。
2、driver_unregister()函式:/linux/drivers/base/driver.c/linux/drivers/base/driver.c
void driver_unregister(struct device_driver * drv)
{
bus_remove_driver(drv);
}
呼叫bus_remove_driver在/linux/drivers/base/bus.c:
void bus_remove_driver(struct device_driver * drv)
{
if (!drv->bus)
return;
remove_bind_files(drv);
driver_remove_attrs(drv->bus, drv);
klist_remove(&drv->knode_bus);
pr_debug("bus %s: remove driver %s/n", drv->bus->name, drv->name);
driver_detach(drv);
module_remove_driver(drv);
kobject_unregister(&drv->kobj);
put_bus(drv->bus);
}
在bus_remove_driver()函式中首先是檢查要解除安裝的device_driver型別的物件drv的bus域,如果為空則返回。此種情況會發生在呼叫driver_register()函式註冊drv時沒有檢查返回值,註冊失敗,但程式依然向下執行,到driver_unregister()時就會到688行處返回。接下來會刪除在呼叫driver_register()函式註冊時在sys檔案系統中建立的目錄,然後刪除drv物件的屬性,(屬性是記錄在檔案中的,刪除屬性其實是刪除記錄drv->bus屬性的檔案),刪除驅動模組,減少對drv->bus的引用計數等。
3、get_driver()和put_driver()函式:分別是增加和減少對該device_drvier型別的物件的引用計數。
4、int driver_create_file(struct device_driver * drv, struct driver_attribute * attr);
void driver_remove_file(struct device_driver * drv, struct driver_attribute * attr);
分別是在sys檔案系統中為device_driver物件建立和刪除目錄