linux udev 自动创建node

36 篇文章 0 订阅
本文介绍了Linux中udev自动创建设备文件节点的原理和应用,讲解了udev取代devfs的原因,并提供了在嵌入式系统中配置和使用udev的详细步骤,包括下载、编译、配置udev以及启动udevd和udevstart。通过配置udev,可以简化设备管理,实现硬件平台无关的设备文件管理。同时,文章展示了如何在设备驱动中配合udev进行设备节点的动态创建和管理。
摘要由CSDN通过智能技术生成
 

我们知道,生成设备文件节点的方法有三个:

手动mknod

利用devfs

利用udev

Udev是今后发展的趋势

第一、什么是udev?

这篇文章UDEV Primer给我们娓娓道来,花点时间预习一下是值得的。当然,不知道udev是什么也没关系,
把它当个助记符好了,有了下面的上路指南,可以节省很多时间。我们只需要树立一个信念:udev很简单!
嵌入式的udev应用尤其简单。

第二、为什么udev要取代devfs?

这是生产关系适应生产力的需要,udev好,devfs坏,用好的不用坏的。

udev是硬件平台无关的,属于user space的进程,它脱离驱动层的关联而建立在操作系统之上,基于这种设计实现,我们可以随时修改及删除/dev下的设备文件名称和指向,随心所欲地按照我们的愿望安排和管理设备文件系统,而完成如此灵活的功能只需要简单地修改udev的配置文件即可,无需重新启动操作系统。udev已经使得我们对设备的管理如探囊取物般轻松自如。

第三、如何得到udev?

udev的主页在这里:http://www.kernel.org/pub/linux/utils/kernel/hotplug/udev.html
 
我们按照下面的步骤来生成udev的工具程序,以arm-linux为例:
1、下载udev-100.tar.bz2
2、tar xjf udev-100.tar.bz2
3、cd udev-100 编辑Makefile,查找CROSS_COMPILE,修改CROSS_COMPILE ?= arm-linux-
4、make
没有什么意外的话当前目录下生成udev,udevcontrol,udevd,udevinfo,udevmonitor,udevsettle,udevstart,
udevtest,udevtrigger九个工具程序,在嵌入式系统里,我们只需要udevd和udevstart就能使udev工作得很好,
其他工具则帮助我们完成udev的信息察看、事件捕捉或者更高级的操作。

第四、如何配置udev?

首先,udev需要内核sysfs和tmpfs的支持,sysfs为udev提供设备入口和uevent通道,tmpfs为udev设备文件提 供存放空间,也就是说,在上电之前系统上是没有足够的设备文件可用的,我们需要一些技巧让kernel先引导 起来。
首先使你的内核支持sysfs和tmpfs。如果采用2.6的2410默认配置,这步可以省略了。


其次,需要做的工作就是重新生成rootfs,把udevd和udevstart复制到/sbin目录。然后我们需要在/etc/下为udev
建立设备规则,这可以说是udev最为复杂的一步。这篇文章提供了最完整的指导:Writing udev rules文中描述的复杂规则我们可以暂时不用去理会,上路指南将带领我们轻松穿过这片迷雾。这里提供一个由简入繁的方法,对于嵌入式系统,这样做可以一劳永逸。

1、在前面用到的udev-100目录里,有一个etc目录,里面放着的udev目录包含了udev设备规则的详细样例文
本。为了简单而又简洁,我们只需要用到etc/udev/udev.conf这个文件,在我们的rootfs/etc下建立一个udev文件夹,把它复制过去,这个文件很简单,除了注释只有一行,是用来配置日志信息的,嵌入式系统也许用不上日志,但是udevd需要检查这个文件。

一下是文件内容,基本不用动

# udev.conf

# The initial syslog(3) priority: "err", "info", "debug" or its

# numerical equivalent. For runtime debugging, the daemons internal

# state can be changed with: "udevcontrol log_priority=<value>".

udev_log="err"

2、在rootfs/etc/udev下建立一个rules.d目录,生成一个空的配置文件

touch etc/udev/rules.d/udev.rules。
保存它,我们的设备文件系统基本上就可以了,udevd和udevstart会自动分析这个文件。

3、为了使udevd在kernel起来后能够自动运行,我们在rootfs/etc/init.d/rcS中增加以下几行:

##################################
echo "Starting udevd..."
/sbin/udevd --daemon
/sbin/udevstart
##################################
放到后面的位置
4、重新生成rootfs,烧写到flash指定的rootfs part中。

5、如果需要动态改变设备规则,可以把etc/udev放到jffs或yaffs part,以备修改,根据需求而定,可以随时扩充udev.conf中的配置项。

上面叙述的是使我们的系统支持udev,相应的代码要做一些改变,在init和exit中添加,具体实例如下:

#include <linux/module.h>

#include <linux/types.h>

#include <linux/fs.h>

#include <linux/errno.h>

#include <linux/mm.h>

#include <linux/sched.h>

#include <linux/init.h>

#include <linux/cdev.h>

#include <asm/io.h>

#include <asm/system.h>

#include <asm/uaccess.h>

#include <linux/device.h>//for  udev

 

#include <asm-arm/arch-s3c2410/hardware.h>

 

#include <linux/ioport.h>

#include <asm/arch-s3c2410/regs-gpio.h>

 

#define GLOBALMEM_MAJOR 0    /*预设的ioport的主设备号*/

 

static unsigned int gpfcon_old = 0;

static unsigned int gpfdat_old = 0;

static unsigned int gpfup_old  = 0;

 

static int globalmem_major = GLOBALMEM_MAJOR;

struct resource *IO_port_resource;

 

 

/*globalmem设备结构体*/

struct globalmem_dev                                    

{                                                        

  struct cdev cdev; /*cdev结构体*/                            

};

 

struct globalmem_dev *globalmem_devp; /*设备结构体指针*/

/*文件打开函数*/

int globalmem_open(struct inode *inode, struct file *filp)

{

  /*将设备结构体指针赋值给文件私有数据指针*/

  filp->private_data = globalmem_devp;

      printk("In the open process! turn off the led!\n");

      outl(0x5500 | inl((unsigned long )S3C2410_GPFCON),(unsigned long )S3C2410_GPFCON);

      outl(0xF0 | inl((unsigned long )S3C2410_GPFUP),(unsigned long)S3C2410_GPFUP);

      outl(0xF0 | inl((unsigned long )S3C2410_GPFDAT),(unsigned long)S3C2410_GPFDAT);

 

  return 0;

}

/*文件释放函数*/

int globalmem_release(struct inode *inode, struct file *filp)

{

  return 0;

}

 

/*读函数*/

static ssize_t globalmem_read(struct file *filp, char __user *buf, size_t size,

  loff_t *ppos)

{

  int ret = 0;

  struct globalmem_dev *dev = filp->private_data; /*获得设备结构体指针*/

  return ret;

}

 

/*写函数*/

static ssize_t globalmem_write(struct file *filp, const char __user *buf,

  size_t size, loff_t *ppos)

{

  unsigned int count = size;

  int ret = 0;

  struct globalmem_dev *dev = filp->private_data; /*获得设备结构体指针*/

      unsigned char *userbuf;    

  /*用户空间->内核空间*/

//   get_user (status, userbuf); 

  if (copy_from_user(userbuf, (unsigned char *)buf, count))

    ret =  - EFAULT;

  else

  {

           outl ((*userbuf), (unsigned long)S3C2410_GPFDAT);

//         writel (*userbuf, (unsigned long)S3C2410_GPFDAT); 

    printk("write data from user to ioport!\n");

  }

  return ret;

}

/*文件操作结构体*/

static const struct file_operations globalmem_fops =

{

  .owner = THIS_MODULE,

  .read = globalmem_read,

  .write = globalmem_write,

  .open = globalmem_open,

  .release = globalmem_release,

};

struct class *my_class;

/*初始化并注册cdev*/

static void globalmem_setup_cdev(struct globalmem_dev *dev, int index)

{

  int err, devno = MKDEV(globalmem_major, index);

 

  cdev_init(&dev->cdev, &globalmem_fops);

  dev->cdev.owner = THIS_MODULE;

  dev->cdev.ops = &globalmem_fops;

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

  if (err)

    printk(KERN_ALERT "Error %d adding globalmem%d", err, index);

 

//下面是为udev准备的   

      my_class =class_create(THIS_MODULE, "ioport");

     if(IS_ERR(my_class)) {

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

         return ;

 }

 class_device_create(my_class,devno,NULL,"ioport",0);

//到此结束

 

}

 

/*设备驱动模块加载函数*/

int globalmem_init(void)

{

  int result;

  dev_t devno = MKDEV(globalmem_major, 0);

 

  /* 申请设备号*/

  if (globalmem_major)

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

  else  /* 动态申请设备号 */

  {

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

    globalmem_major = MAJOR(devno);

  } 

  if (result < 0)

    return result;

   

  /* 动态申请设备结构体的内存*/

  globalmem_devp = kmalloc(sizeof(struct globalmem_dev), GFP_KERNEL);

  if (!globalmem_devp)    /*申请失败*/

  {

    result =  - ENOMEM;

    goto fail;

  }

  memset(globalmem_devp, 0, sizeof(struct globalmem_dev));

 

  globalmem_setup_cdev(globalmem_devp, 0);

      if ((IO_port_resource=request_region((unsigned long)S3C2410_GPFCON, 0x0c,"ioport"))==NULL)

           goto fail;

      else{ 

           printk("In the init process! \n");

           gpfcon_old = (unsigned int) inl((unsigned long)S3C2410_GPFCON);

           gpfdat_old = (unsigned int) inl((unsigned long)S3C2410_GPFDAT);

           gpfup_old = (unsigned int) inl((unsigned long)S3C2410_GPFUP);

           printk("S3C2410_GPFCON is %8X\n",gpfcon_old);

           printk("S3C2410_GPFUP  is %8X\n",gpfup_old);

           printk("S3C2410_GPFDAT is %8X\n",gpfdat_old);

//测试hardware.h的读写函数          

           s3c2410_gpio_setpin(S3C2410_GPF4, 1);

           s3c2410_gpio_setpin(S3C2410_GPF5, 1); 

           s3c2410_gpio_setpin(S3C2410_GPF6, 1); 

           s3c2410_gpio_setpin(S3C2410_GPF7, 1); 

       

           return 0;

}

  fail: unregister_chrdev_region(devno, 1);

  return result;

}

 

/*模块卸载函数*/

void globalmem_exit(void)

{

      if (IO_port_resource!=NULL) release_region((unsigned long)S3C2410_GPFCON, 0x0c);

  cdev_del(&globalmem_devp->cdev);   /*注销cdev*/

  kfree(globalmem_devp);     /*释放设备结构体内存*/

 

//测试hardware.h的读写函数 

      s3c2410_gpio_setpin(S3C2410_GPF4, 0);

      s3c2410_gpio_setpin(S3C2410_GPF5, 0); 

      s3c2410_gpio_setpin(S3C2410_GPF6, 0); 

      s3c2410_gpio_setpin(S3C2410_GPF7, 0);       

                

//下面的是为udev准备的     

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

  class_destroy(my_class); 

//结束

 

  unregister_chrdev_region(MKDEV(globalmem_major, 0), 1); /*释放设备号*/

}

 

MODULE_AUTHOR("AK-47");

MODULE_LICENSE("Dual BSD/GPL");

 

module_param(globalmem_major, int, S_IRUGO);

 

module_init(globalmem_init);

module_exit(globalmem_exit);

 

通过上述代码增加后,当我们insmod我们生成的.ko文件后,在/dev下面就可以生成相应的设备文件节点了。省去了mknod去创建文件节点的麻烦。而且由于udevd做成了守护进程,每当加载新的驱动程序的时候会自动的创建设备文件节点。

当然前提不要忘记了:udevd、udevstart已经运行。

 

附录:

再次改动后的rcs文件

#! /bin/sh

echo "Processing etc/init.d/rcS"

#hostname ${HOSTNAME}

hostname AK-47

echo " Mount all"

/bin/mount -a

echo " Start mdev...."

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

#mdev -s

echo "****************************************************"

echo " rootfs by NFS, s3c2410"

echo " Created by Mr.Liu @ 2008.11.28"

echo " Good Luck"

echo " www.neusoft.edu.cn"

echo "****************************************************"

echo

export QTDIR=/usr/

export LD_LIBRARY_PATH=$QTDIR/lib:$LD_LIBRARY_PATH

export PATH=$QTDIR/qt_bin:$QTDIR/bin:$PATH

ln -sf /dev/ts0 /dev/h3600_tsraw

ln -sf /dev/ts0 /dev/h3600_ts

ln -s /dev/scsi/host0/bus0/target0/lun0/part1 /dev/sda1

echo "ifconfig eth0 192.168.0.1"

/sbin/ifconfig eth0 192.168.0.1

echo "start udev!"

/sbin/udevd --daemon

/sbin/udevstart

echo "finished run udev!"

#/bin/menu -qws &

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值