1. 程式人生 > >udev動態申請裝置號

udev動態申請裝置號

轉載自:http://blog.csdn.net/yongan1006/article/details/6632480#comments

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

#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));

下面介紹下函式class_creat和class_device_creat的原型:

class_create()

-------------------------------------------------
linux-2.6.22/include/linux/device.h
struct class *class_create(struct module *owner, const char *name)
    class_create - create a struct class structure
    @owner: pointer to the module that is to "own" this struct class
    @name: pointer to a string for the name of this class.
在/sys/class/下建立類目錄


class_device_create()

-------------------------------------------------
linux-2.6.22/include/linux/device.h
struct class_device *class_device_creat(struct class        *cls,
                                         struct class_device *parent,
                                         dev_t               devt,
                                         struct device       *device,
                                         const char          *fmt, ...)

    class_device_create - creates a class device and registers it with sysfs
    @cls: pointer to the struct class that this device should be registered to.
    @parent: pointer to the parent struct class_device of this new device, if any.
    @devt: the dev_t for the char device to be added.
    @device: a pointer to a struct device that is assiociated with this class device.
    @fmt: string for the class device's name

void class_destroy(struct class *cls);/*銷燬/sys/class下的類   */

void class_device_destroy(struct class *cls, dev_t devt);   /*銷燬一個類裝置*/

引數含義同上

補充:

在Linux2.6中,針對上面的這個問題不同的版本有些修改,使用前要先檢視下/.../include/linux/device.h裡的函式宣告,如我用的是Linux2.6.29,裡面就沒有class_device_create函式,而直接使用device_create就可以了,而在之前的版本如Linux2.6.15,裡面就要用class_device_create函式 。

上面的內容說的很有用。

此前,我按照《linux裝置驅動開發詳解》的例程學習,發現驅動並不能正常工作。鄙人實在是太菜了,甚至沒想過用手工去建立裝置節點,所以常常為裝置節點的事苦惱。

下面,我結合自己的實踐,分享下我的實驗成果:

驅動原始碼:

#include <linux/init.h>

#include <linux/module.h>

#include <linux/types.h>

#include <linux/fs.h>

#include <linux/cdev.h>

#include <linux/sched.h>

#include <linux/mm.h>

#include <linux/device.h>

#include <asm/io.h>

#include <asm/system.h>

#include <asm/uaccess.h>

#define MAJOR_NUM 251

/*此處要根據自己的/proc/device/目錄下的情況,選一下不重複的主裝置號就好*/

#define DEVSIZE 0X1000

MODULE_LICENSE("GPL");

/*定義裝置結構體,其中包括分配cdev*/

struct globalvar_dev{

       struct cdev cdev;

       unsigned char mem[DEVSIZE];

};

struct globalvar_dev dev;     

struct class *myclass;

static ssize_t globalvar_read(struct file *filp,char*buf,size_t count,loff_t *ppos)

{

       unsigned long p=*ppos;

       int ret=0;

/*分析和獲取有效的讀長度*/

       if(p>=DEVSIZE)//要讀的位元組數太大

              return 0;

       if(count>DEVSIZE-p)//要求的位元組數太大

              count=DEVSIZE-p;

       if(copy_to_user(buf,(void *)(dev.mem+p),count)){

              ret=-EFAULT;

              }

       else{

              *ppos +=count;

              ret = count;

              printk(KERN_INFO "read %d bytes from %ld\n",count,p);

              }

       return ret;

}

static ssize_t globalvar_write(struct file *filp,const char*buf,size_t count,loff_t *ppos)

{

       unsigned long p = *ppos;

       int ret = 0;

       /*分析和獲取有效的讀長度*/

       if(p>=DEVSIZE)//要讀的位元組數太大

              return 0;

       if(count>DEVSIZE-p)//要求的位元組數太大

              count=DEVSIZE-p;

       if(copy_from_user(dev.mem+p,buf,count))

              ret = -EFAULT;

       else{

              *ppos += count;

              ret = count;

              printk(KERN_INFO "written %d byte from %ld\n",count,p);

              }

       return ret;      

}

static loff_t globalvar_llseek(struct file *filp,loff_t offset,int orig)

{

       loff_t ret;

       switch(orig)

       {

              case 0:

                     if (offset<0){

                            ret=-EINVAL;

                            break;

                            }

                     if((unsigned int)offset >DEVSIZE){

                            ret=-EINVAL;

                            break;

                            }

                     filp->f_pos=(unsigned int)offset;

                     ret=filp->f_pos;

                     break;

              case 1:

                     if (filp->f_pos+offset<0){

                            ret=-EINVAL;

                            break;

                            }

                     if(filp->f_pos+(unsigned int)offset >DEVSIZE){

                            ret=-EINVAL;

                            break;

                            }

                     filp->f_pos+=(unsigned int)offset;

                     ret=filp->f_pos;

                     break;    

              case 2:

                     if (DEVSIZE-1+offset<0){

                            ret=-EINVAL;

                            break;

                            }

                     if(DEVSIZE-1+(unsigned int)offset >DEVSIZE){

                            ret=-EINVAL;

                            break;

                            }

                     filp->f_pos=DEVSIZE-1+(unsigned int)offset;

                     ret=filp->f_pos;

                     break;    

              default:

                     ret = -EINVAL;

              }

       return ret;

}

struct file_operations globalvar_fops=

{

       .owner=THIS_MODULE,

       .write=globalvar_write,

       .read=globalvar_read,

       .llseek=globalvar_llseek,

       };

static int  __init  globalvar_init(void)

{

int err;

int result;

//第一步:取得裝置號

dev_t devno = MKDEV(MAJOR_NUM,0);/*主,次裝置號*/

if(MAJOR_NUM)

/*register_chrdev_region(裝置號指標,裝置號數目,裝置名)*/

       result=register_chrdev_region(devno,1,"globalvar");

else

{

/*alloc_chrdev_region(分配到的裝置號指標,起始次裝置號,

需要分配的裝置號數目,裝置名稱)*/

       result = alloc_chrdev_region(&devno,0,1,"globalvar");

       //MAJOR_NUM=MAJOR(devno);

}

/*第二步:初始化cdev*/

cdev_init(&dev.cdev,&globalvar_fops);/*建立cdev和file_operations之間的連線*/

dev.cdev.owner=THIS_MODULE;

dev.cdev.ops=&globalvar_fops;

/*第三步:註冊字元裝置*/

/*cdev_add(cdev結構指標,裝置號,裝置數目)*/

err=cdev_add(&dev.cdev,devno,1);

if(err)

       printk(KERN_NOTICE "Error %d adding globalvar",err);

/*增加對udev的支援*/

myclass = class_create(THIS_MODULE, "globalvar");

device_create(myclass, NULL, MKDEV(MAJOR_NUM, 0), NULL, "globalvar");

printk("globalvar installs success");

return 0;

}

static void __exit globalvar_exit(void)

       class_destroy(myclass);

       device_destroy(myclass,MKDEV(MAJOR_NUM,0));

      cdev_del(&dev.cdev);/*登出cdev結構*/

       unregister_chrdev_region(MKDEV(MAJOR_NUM,0),1);/*登出裝置號*/

       printk("globalvar exits success");

}

module_init(globalvar_init);

module_exit(globalvar_exit);

上面引用的文章已經說的很清楚,如果你手工建立節點,就不需要紅色字型的內容。加上紅色字型內容後,如果你的平臺已經有了udev ,那麼它就會自動建立裝置節點。

測試原始碼:

#include<sys/types.h>

#include<sys/stat.h>

#include<stdio.h>

#include<fcntl.h>

void main()

{

       FILE *fp0 =NULL;

       char Buf[4096];

       strcpy(Buf,"globalvar is char dev!");

       printf("BUF:%s\n",Buf);

       fp0=fopen("/dev/globalvar","r+");

       if(fp0==NULL){

              printf("Open globalvar Error!\n");

              return -1;

              }

       fwrite(Buf,sizeof(Buf),1,fp0);

       fseek(fp0,0,SEEK_SET);

       strcpy(Buf,"Buf is NULL!");

       printf("BUF:%s\n",Buf);

       fread(Buf,sizeof(Buf),1,fp0);

       printf("BUF: %s\n",Buf);

       return 0;

}

以上原始碼,我已測試通過。只是還有一點不毛病:

看到了嗎?本來就是

BUF:glogalvar is char dev!

Read 4096 bytes from

……

我不知道怎麼搞的,它們混到一塊兒了,不過,這個問題應該和驅動沒有關係。如果哪們仁兄知道怎麼回事,還請告訴我一下。好了,基本上就這樣了。