1. 程式人生 > >自動建立裝置節點 ,手動建立…

自動建立裝置節點 ,手動建立…

error: implicit declaration of  ‘class_device_create’

這個程式碼在Kernel 2.6.18下面編譯的時候沒有任何問題。為什麼在2.6.34下面會出現這個錯誤呢?難道class_device_create這個kernel API已經從新版kernel裡面移除了?

google了一下,發現確實是已經被移除了,在新版的kernel裡面,可以使用device_create來代替,引數完全一致。

在LXR(http://lxr.linux.no)網站上面查找了一番,發現class_device_create在2.6.25裡面還有,從2.6.26起就被移除了。

Linux Kernel的Kernel API是經常會變化的。這給需要支援多個版本的Driver帶來了不小的麻煩。有沒有什麼地方可以很方便的知 道 Linux Kernel各個版本之間Kernel API的變化?暫時沒有找到。一個可行的方法是,遇到問題之後,到LXR裡面如搜尋一下,LXR 可以搜尋特定的kernel版本。

之前寫的字元類裝置驅動,沒有自動建立裝置節點,因為只使用了register_chrdev()函式,只是註冊了這個裝置。然後在系統啟動後,就要自己建立裝置節點mknod,這樣雖然是可行的,但是比較麻煩。於是想在__init函式裡面,自動建立裝置節點。

    經過查閱資料,發現建立裝置節點使用了兩個函式  class_create()和class_device_create(),當然在 __exit()函式裡,要使用class_destory()和class_device_desotry()登出建立的裝置節點!

       問題來了,編譯了之後,發現報錯 error: implicit declaration of  'class_device_create'等幾個錯誤。經過分析,應該是Linux 核心版本不同的原因!早期的版本,使用的是上面說的兩個函式,但是在2.6.29以後(我用的是2.6.32的),使用的函式則變成 了 class_create()和device_create(),並且要在宣告中加入#i nclude  ,因為定義這些函式是在Linux2.6.32/include/linux /device.h裡面!

      經過這些修改後,驅動編譯成功,就能夠自動建立裝置節點了!

自學驅動以來,一直都是在載入模組後採用手動建立節點,雖然這個過程比較簡單,畢竟還是有點麻煩,尤其是在除錯模組的時候。 #insmod module_name.ko #mknod /dev/module_name c  MAJOR  MINOR #

在2.4裡裝置檔案採用的是devfs,在2.6裡已經用udev取代devfs,為解決上面那樣手動建立節點的麻煩,我們可以在程式里加上建立節點這項,如下: 以字元裝置char_dev為例,在驅動初始化的程式碼裡呼叫class_create為該裝置建立一個class,再為每個裝置呼叫 class_device_create建立對應的裝置,這樣的module被載入時,undev daemon就會自動在/dev下建立char_dev裝置檔案。大概方法如下:

struct class *myclass = class_create(THIS_MODULE, “char_dev”);
class_device_create(myclass, NULL, MKDEV(major_num, 0), NULL, “char_dev”);

當然,在exit函式中要把建立的class移除:
class_destory(&xxx_dev->cdev);
class_device_desotry(my_class,MKDEV(major_num,0));

下面以一個簡單字元裝置驅動來展示如何使用這幾個函式

  1. #include 
  2. #include 
  3. #include 
  4. #include 
  5. #include 
  6. #include 
  7. MODULE_LICENSE ("GPL");  
  8. int hello_major = 555;  
  9. int hello_minor = 0;  
  10. int number_of_devices = 1;  
  11. struct cdev cdev;  
  12.     dev_t dev = 0;  
  13. struct file_operations hello_fops = {  
  14.       .owner = THIS_MODULE  
  15.     };  
  16. staticvoid char_reg_setup_cdev (void)  
  17.     {  
  18.        int error, devno = MKDEV (hello_major, hello_minor);  
  19.        cdev_init (&cdev, &hello_fops);  
  20.        cdev.owner = THIS_MODULE;  
  21.        cdev.ops = &hello_fops;  
  22.        error = cdev_add (&cdev, devno , 1);  
  23.        if (error)  
  24.            printk (KERN_NOTICE "Error %d adding char_reg_setup_cdev", error);  
  25. }  
  26. structclass *my_class;  
  27. staticint __init hello_2_init (void)  
  28.     {  
  29.        int result;  
  30.        dev = MKDEV (hello_major, hello_minor);  
  31.        result = register_chrdev_region (dev, number_of_devices, "hello");  
  32.        if (result<0) {  
  33.            printk (KERN_WARNING "hello: can't get major number %d/n", hello_major);  
  34.            return result;  
  35.      }  
  36.  char_reg_setup_cdev ();  
  37.      my_class = class_create(THIS_MODULE, "my_class");  
  38.      if(IS_ERR(my_class))  
  39.      {  
  40.           printk("Err: failed in creating class./n");  
  41.           return -1;  
  42.       }  
  43.       device_create( my_class, NULL, MKDEV(hello_major, 0), "hello""%d", 0 );  
  44.   printk (KERN_INFO "Registered character driver/n");  
  45.       return 0;  
  46.     }  
  47. staticvoid __exit hello_2_exit (void)  
  48.     {  
  49.        dev_t devno = MKDEV (hello_major, hello_minor);  
  50.        cdev_del (&cdev);  
  51.    device_destroy(my_class, MKDEV(adc_major, 0));         //delete device node under /dev
  52.        class_destroy(my_class);                               //delete class created by us
  53.    unregister_chrdev_region (devno, number_of_devices);  
  54.    printk (KERN_INFO "char driver cleaned up/n");  
  55.     }  
  56. module_init (hello_2_init);  
  57. module_exit (hello_2_exit);  

這樣,模組載入後,就能在/dev目錄下找到hello0這個裝置節點了。

mdev是busybox自帶的一個簡化版的udev,適合於嵌入式的應用埸合。其具有使用簡單的特點。它的作用,就是在系統啟動和熱插拔 或動態載入驅動程式時,自動產生驅動程式所需的節點檔案。在以busybox為基礎構建嵌入式linux的根檔案系統時,使用它是最優

的選擇。

mdev使用

mdev的使用在busybox中的mdev.txt文件已經將得很詳細了。但作為例子,我簡單講講我的使用過程:

(1)在編譯時加上對mdev的支援(我是使用的是busybox1.10.1):

Linux System Utilities --->



mdev



Support /etc/mdev.conf



Support command execution at device addition/removal

(2)在啟動時加上使用mdev的命令:

我在自己建立的根檔案系統(nfs)中的/linuxrc檔案中添加了如下指令:

#掛載/sys為sysfs檔案系統

echo "----------mount /sys as sysfs"

/bin/mount -t tmpfs mdev /dev

/bin/mount -t sysfs sysfs /sys

echo "----------Starting mdev......"

/bin/echo /sbin/mdev > /proc/sys/kernel/hotplug

mdev -s

注意:是/bin/echo /sbin/mdev > /proc/sys/kernel/hotplug,並非/bin/echo /bin/mdev > /proc/sys/kernel/hotplug。

busybox的文件有錯!!



(3)在你的驅動中加上對類裝置介面的支援。

在驅動程式的初始化函式中,使用下述的類似語句,就能在類裝置目錄下新增包含裝置號的名為“dev”的屬性檔案。並通過mdev

在/dev目錄下產生gpio_dev0的裝置節點檔案。

my_class = class_create(THIS_MODULE, "gpio_class");

if(IS_ERR(my_class)) {

printk("Err: failed in creating class.\n");

return -1;

}



class_device_create(my_class, NULL, MKDEV(gpio_major_number, 0), NULL, "gpio_dev%d" ,0);

在驅動程式的清除程式段,加入以下語句,以完成清除工作。

class_device_destroy(my_class, MKDEV(gpio_major_number, 0));

class_destroy(my_class);

需要的標頭檔案是linux/device.h,因此程式的開始應加入下句

#include

另外,my_class是class型別的結構體指標,要在程式開始時宣告成全域性變數。

struct class *my_class;

上述程式中的gpio_major_number是裝置的主節點號。可以換成需要的節點號。gpio_dev是最終生成的裝置節點檔案的名子。%d是

用於以相同裝置自動編號的。gpio_class是建立的class的名稱,當驅動程式載入後,可以在/sys/class的目錄下看到它。

上述語句也不一定要在初始化和清除階段使用,可以根據需要在其它地方使用。

(4)至於/etc/mdev.conf檔案,可有可無,不影響使用,只是添加了些功能。

關於mdev的使用方法,我在網上找到一篇中文版的。大家可以到我上傳的資源中下載。

要想真正用好mdev,適當知道一下原理是必不可少的。現在簡單介紹一下mdev的原理:

執行mdev -s

:以‘-s’為引數呼叫位於

/sbin目錄寫的mdev(其實是個連結,作用是傳遞引數給/bin目錄下的busybox程式並呼叫它),mdev掃描 /sys/class 和

/sys/block

中所有的類裝置目錄,如果在目錄中含有名為“dev”的檔案,且檔案中包含的是裝置號,則mdev就利用這些資訊為這個裝置在/dev

下建立裝置節點檔案。一般只在啟動時才執行一次 “mdev -s”。

熱插拔事件:由於啟動時運行了命

令:echo /sbin/mdev > /proc/sys/kernel/hotplug ,那麼當有熱插拔事件產生時,核心就會呼叫位於

/sbin目錄的mdev。這時mdev通過環境變數中的 ACTION 和

DEVPATH,(這兩個變數是系統自帶的)來確定此次熱插拔事件的動作以及影響了/sys中的那個目錄。接著會看看這個目錄中是否有

“dev”的屬性檔案,如果有就利用這些資訊為

這個裝置在/dev 下建立裝置節點檔案。

最後,附上我在工作中編寫的一段簡單的gpio控制驅動程式。此程式沒有什麼功能,主要是做一些測試用的。有興趣的朋友可以用

它測試一下上述的mdev的使用方法。我用的是友善公司的mini2440開發板。



補充:1

[b]為mdev的執行準備環境

mdev需要改寫/dev和/sys兩個目錄。所以必須保證這兩個目錄是可寫的(一般會用到sysfs,tmpfs。所以要重新編譯核心)。

然後在你的啟動指令碼檔案中加入

/bin/mdev -s[/b]

[b]補充2:[/b]

[b] ·/etc/fstab[/b]

[b]這是mount -a要讀取的文字。根據需要編寫。[/b]