1. 程式人生 > >Linux裝置模型(匯流排、裝置、驅動程式和類)之一:bus_type

Linux裝置模型(匯流排、裝置、驅動程式和類)之一:bus_type

      匯流排是處理器和一個或多個裝置之間的通道,在裝置模型中,所有的裝置都通過匯流排相連,甚至是內部的虛擬"platform"匯流排。可以通過ls -l /sys/bus看到系統載入的所有匯流排。
drwxr-xr-x root     root              1970-01-01 00:02 platform
drwxr-xr-x root     root              1970-01-01 00:02 spi
drwxr-xr-x root     root              1970-01-01 00:02 scsi
drwxr-xr-x root     root              1970-01-01 00:02 usb
drwxr-xr-x root     root              1970-01-01 00:02 serio
drwxr-xr-x root     root              1970-01-01 00:02 i2c
drwxr-xr-x root     root              1970-01-01 00:02 mmc
drwxr-xr-x root     root              1970-01-01 00:02 sdio
drwxr-xr-x root     root              1970-01-01 00:02 ac97
      匯流排可以相互插入。裝置模型展示了匯流排和它們所控制的裝置之間的實際連線。在Linux 裝置模型中,匯流排由bus_type 結構表示,定義在 <linux/device.h> :
struct bus_type {
    const char  *name;                            /*匯流排型別名稱*/
    struct bus_attribute *bus_attrs;       /*匯流排屬性*/
    struct device_attribute *dev_attrs;   /*裝置屬性,指為每個加入匯流排的裝置建立的預設屬性連結串列*/
    struct driver_attribute *drv_attrs;     /*驅動程式屬性*/

    int (*match)(struct device *dev, struct device_driver *drv);   /*匯流排操作函式*/
    int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
    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 (*suspend_late)(struct device *dev, pm_message_t state);
    int (*resume_early)(struct device *dev);
    int (*resume)(struct device *dev);
    struct pm_ext_ops *pm;
    struct bus_type_private *p;
};
1,匯流排的註冊和刪除,匯流排的主要註冊步驟:
(1)申明和初始化bus_type 結構體。只有很少的bus_type 成員需要初始化,大部分都由裝置模型核心控制。但必須為匯流排指定名字及一些必要的方法。例如:
struct bus_type ldd_bus_type = {
    .name = "ldd",
    .match = ldd_match,
    .uevent = ldd_uevent,
};
(2)呼叫bus_register函式註冊匯流排。int bus_register(struct bus_type *bus),該呼叫可能失敗,所以必須始終檢查返回值。
ret = bus_register(&ldd_bus_type);
if (ret)
   return ret;
若成功,新的匯流排子系統將被新增進系統,之後可以向匯流排新增裝置。當必須從系統中刪除一個匯流排時,呼叫:
void bus_unregister(struct bus_type *bus);


 2,匯流排方法
     在 bus_type 結構中定義了許多方法,它們允許匯流排核心作為裝置核心與單獨的驅動程式之間提供服務的中介,主要介紹以下兩個方法: int (*match)(struct device * dev, struct device_driver * drv);
/*當一個新裝置或者驅動被新增到這個匯流排時,這個方法會被呼叫一次或多次,若指定的驅動程式能夠處理指定的裝置,則返回非零值。*/
int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
/*在為使用者空間產生熱插拔事件之前,這個方法允許匯流排新增環境變數*/

      對裝置和驅動的迭代:若要編寫匯流排層程式碼,可能不得不對所有已經註冊到匯流排的裝置或驅動進行一些迭代操作,這可能需要仔細研究嵌入到 bus_type 結構中的其他資料結構,但最好使用核心提供的輔助函式:
int bus_for_each_dev(struct bus_type *bus, struct device *start, void *data, int (*fn)(struct device *, void *));
int bus_for_each_drv(struct bus_type *bus, struct device_driver *start, void *data, int (*fn)(struct device_driver *, void *));
/*這兩個函式迭代總線上的每個裝置或驅動程式(內部分別有next_device和next_driver),將關聯的device或device_driver傳遞給 fn,同時傳遞data 值。若start為NULL,則從第一個裝置開始;否則從start之後的第一個裝置開始。若fn返回非零值,迭代停止並且那個值從bus_for_each_dev 或bus_for_each_drv 返回。*/
 

3,匯流排屬性

     幾乎Linux 裝置模型中的每一層都提供新增屬性的函式,匯流排層也不例外。bus_attribute 型別定義在<linux/device.h> 如下:

struct bus_attribute {
    struct attribute    attr;
    ssize_t (*show)(struct bus_type *, char * buf);
    ssize_t (*store)(struct bus_type *, const char * buf, size_t count);
};
     核心提供了一個巨集在編譯時建立和初始化bus_attribute 結構:

BUS_ATTR(_name,_mode,_show,_store)/*這個巨集宣告一個結構,將bus_attr_作為給定_name 的字首來建立匯流排的真正名稱*/
/*匯流排的屬性必須顯式呼叫bus_create_file 來建立:*/
int bus_create_file(struct bus_type *bus, struct bus_attribute *attr);
/*刪除匯流排的屬性呼叫:*/
void bus_remove_file(struct bus_type *bus, struct bus_attribute *attr); 
      例如建立一個包含原始碼版本號簡單屬性方法如下:

static ssize_t show_bus_version(struct bus_type *bus, char *buf)
{
      return snprintf(buf, PAGE_SIZE, "%s/n", Version);
}

static BUS_ATTR(version, S_IRUGO, show_bus_version, NULL); //得到bus_attr_version

/*在模組載入時建立屬性檔案:*/
if (bus_create_file(&ldd_bus_type, &bus_attr_version))
      printk(KERN_NOTICE "Unable to create version attribute/n");

/*這個呼叫建立一個包含 lddbus 程式碼的版本號的屬性檔案(/sys/bus/ldd/version)*/