天天看點

linux分類驅動對字元裝置架構壓力的解除安裝

2.6核心引入了input字系統,usb子系統,misc子系統等一系列字元裝置子系統,在熟練掌握這些子系統之後,我們來看一下linux核心設計這麼些子系統的意義何在?可以連接配接的裝置越來越多,這些裝置的種類也越來越雜,傳統的字元裝置/塊裝置的分類已經不能滿足要求,以字元裝置為例,現在的linux字元裝置體系已經不能代表所有支援的字元裝置的最小交集,況且,在随着裝置的增多,越來越長的線性裝置連結清單給管理帶來了諸多不便,是以“需要增加一個層”來給字元裝置體系解除安裝一些壓力了,于是就将所有的“一類”裝置總結成“一個”裝置,然後将這一個裝置作為字元裝置加傳入連結表,而真正的字元裝置需要新設計的“層”來管理了,在重新設計之後,被總結出來的“那個”字元裝置已經不再是傳統的裝置了,而是“裝置類”了,同時,/dev目錄下也有了相應的“裝置類”,比如/dev/input,這個下面有一系列檔案,其中有些檔案就代表上面所說的“裝置類”,比如mice就是所有的滑鼠,而還有些檔案代表真實的裝置,比如mouse0,mouse1之類的。從應用的角度來看,input子系統确實解除安裝了字元裝置系統的壓力,隻因新增了一個層,至于這種層該如何實作,還是要看代碼:

static int __init input_init(void)

{

    int err;

    err = class_register(&input_class);

    err = input_proc_init();

    err = register_chrdev(INPUT_MAJOR, "input", &input_fops); //隻需要初始化一個字元裝置即可,主裝置号為0xd

    return 0;

}

static const struct file_operations input_fops = {

    .owner = THIS_MODULE,

    .open = input_open_file,

};

static int input_open_file(struct inode *inode, struct file *file)

    struct input_handler *handler;

    const struct file_operations *old_fops, *new_fops = NULL;

    handler = input_table[iminor(inode) >> 5]; //可看出,目前input子系統隻支援8類裝置

    new_fops = fops_get(handler->fops); //得到input子系統“層”中設定的真正的fops

    old_fops = file->f_op;

    file->f_op = new_fops;

    err = new_fops->open(inode, file);

    ...

由上述字元裝置體系的代碼可見,被解除安裝的真正的字元裝置需要input層來集中實作。可以想見,input子系統中一個裝置類就應該有一個handler,由裝置檔案inode得到的minor來索引,我們看一個例子:

# stat mice 

File: `mice'

Size: 0               Blocks: 0          IO Block: 4096   character special file

Device: 1607h/5639d     Inode: 23921566    Links: 1     Device type: d,3f  #主裝置号為0xd,次裝置号為0x3f

...

注意主次裝置号,input子系統将整個裝置号空間按照高3位分割成了8個子空間,每一個子空間代表input子系統中一類裝置的所有裝置。

static int __init mousedev_init(void)

    mousedev_mix = mousedev_create(NULL, &mousedev_handler, MOUSEDEV_MIX); //建立了/dev/input/mice檔案

    input_register_handler(&mousedev_handler);

mousedev_init隻是初始化了input子系統中滑鼠裝置的架構,然後通過mousedev_create建立了一個通用的滑鼠類,那麼真實的滑鼠類檔案何時建立呢?每一類input裝置都有一個input_handler,對于滑鼠就是mousedev_handler,它通過mousedev_init注冊進系統,所有的handler形成一個連結清單,同時系統中input子系統還擁有另一條連結清單,那就是input裝置,每當有新的裝置插入機器時,在更底層的裝置驅動知道這是一個input裝置時,就将之納入input子系統的管理,也就是初始化一個input_dev,然後将之連結進input_dev連結清單,随後周遊input_handler連結清單,在找到“可以處理該裝置”的input_handler之後調用其connect方法,對于滑鼠那就是mousedev_connect,在mousedev_connect中會初始化一個滑鼠裝置:mousedev_create:

static struct mousedev *mousedev_create(struct input_dev *dev, struct input_handler *handler, int minor)

    struct mousedev *mousedev;

    mousedev = kzalloc(sizeof(struct mousedev), GFP_KERNEL);

    if (minor == MOUSEDEV_MIX)

        strlcpy(mousedev->name, "mice", sizeof(mousedev->name));

    else

        snprintf(mousedev->name, sizeof(mousedev->name), "mouse%d", minor);

    mousedev->dev.devt = MKDEV(INPUT_MAJOR, MOUSEDEV_MINOR_BASE + minor);//用于建立/dev/input下面的檔案

input子系統中input_dev連結清單和input_handler連結清單相認的過程和底層bus中的的device連結清單和device_driver連結清單相認的過程幾乎是一樣的,運用了相同的思想。在device和device_driver的層次,頂多在pci的層次,由probe函數調用input子系統的初始化規程,比如配置設定input_dev,注冊中斷等等,注冊好input_dev之後,該裝置就被納入input子系統的管轄了,整個層次是device--pci...--input_dev--input_handler--input字元裝置--/dev/input/xxx,在中斷發生的時候,中斷處理程式會從參數中得到input_dev,然後就可以用input_report_XX來報告了,該報告可以順着剛剛說過的層次線索一直到達使用者空間需要該中斷結果的地方,比如移動了一下滑鼠的中斷會被Xwindow來使用。

     input子系統僅僅是一個解除安裝原始結構壓力的一個例子,諸如usb子系統,sound子系統以及misc子系統都是這樣實作的,給你的感覺并沒有什麼不一樣,如果所有的字元裝置統統直接在chrdev架構下實作,/dev下将被建立所有的字元裝置,如果很多字元被分類了,比如分到了input類中,或者分到了usb類中,那麼每一個裝置依然擁有一個/dev下的字元裝置檔案,隻是位置不同了,可能被放入了input,usb子目錄下,stat的結果仍然是character special file,沒有什麼不同,在使用sysfs/udevd的情況下,你甚至可以不建立input,usb等子目錄,依然将所有檔案建立在/dev目錄下也沒什麼不可。注意,input/usb/misc等子系統僅僅規整了核心裝置驅動的結構,這種規整對于使用者空間是透明的。我感覺,linux核心的這種規整最能展現計算機業的一句真理,那就是“加一個層次”,linux核心通過引入新的層次,在不改變原有字元驅動結構的情況下,成功将幾個新的分類驅動層次插入到了既有的架構,這同時也說明了linux核心中字元裝置架構原本的靈活性。

 本文轉自 dog250 51CTO部落格,原文連結:http://blog.51cto.com/dog250/1271824

繼續閱讀