Linux字符设设备编程(四)之可爱的misc

字符设备编程(四)之可爱的 misc

在我们前面所学的字符设备编程中,当我们写好字符设备驱动并加载成功后;要使我们所写的驱动层程序和应用层程序之间搭建起通信桥梁,要通过设备节点。

很多时候,我们都是利用 mknod 命令手动创建设备节点;应用层程序通过设备节点 (open(“/dev/globalmem”,O_RDWR)) 和驱动层程序之间进行通话。

注:此处的 globalmem 是创建的一个设备节点名 

我们先来看看如何创建一个设备节点呢 ?

To user the driver,first create the proper device files .To do that,you need to know which major number the driver uses.(It is allocated dynamically in the initization routine when you install the driver,i.e. when running insmod)

(1)The easiest way to get that information is by reading /proc/devices using the following command:

cat /proc/devices |grep Tiger-global

With the major number returned by the above command.

think@think-laptop:~$ cat /proc/devices |grep Tiger-global

248 Tiger-global

(2)you can now create the devices files using mknod command:

sudo mknod /dev/globalmem c 248 0

(3)Finally,use chmod to set the read/write bits appropriately,for example:

sudo chmod 666 /dev/Tiger-global.

以上创建设备节点的命令虽然很简单;但都是我们手动进行创建的。 Tiger 是一个很懒的人;因此,我一直在想可不可以通过一个程序来自动完成上面的工作;这样,就可以不用每次写一个驱动程序,都来敲上面无聊的代码;而是由程序代替我们来完成。这个问题也纠结了我很长时间;最近,在 s3c6410上做项目时发现了三种方法来完成上面的自动添加设备。

首先,我想到的也是最简当的方法就是写一个很通用的脚本,来帮我们完成添加设备节点。

#Fliename :atuo_load

#Author :Tiger-john

#Time :2011-05-10

#Function :Automaticly, instally module,create the proper device # and set the read/write bits appropriately

#!/bin/sh

 

module =”globalmen ”

 

#Remove module from kernel (just in case it is still #running)

 

/sbin/rmmod $module

 

#install module

/sbin/insmod ./$module.ko

 

#find major numer used

major =$(cat /proc/devices | grep Tiger-global | awk '{printf $ 1}')

echo Using major number $major

 

# Remove old device files

rm -f /dev/${module}

 

 

#Create new device files

mknod /dev/${module} c $major 0

#change access mode (RW for everybody)

chmod 666 /dev/${module}

 

以上就是 auto_load 脚本,你只要修改两处地方它就是一个很通用的脚本:

(1)module =”globalmen ”

把红色部分改成你的驱动程序文件名

(2)major =$(cat /proc/devices | grep Tiger-global | awk '{printf $ 1}')

把红色部分改成你程序中的设备名就 OK 了 他就是一个通用的脚本了。

(3) 然后输入: sudo chmod 777 auto_load

执行脚本命令: sudo ./auto_load

执行脚本之后,就自动建立了设备节点

也有人就问了,如果驱动程序中支持多个设备,那么不是要建立不止一个设备节点文件么,那么上面的脚本还能用么?

Tiger 告诉你还是可以用的之用该一两处就好了

#Fliename :atuo_load

#Author :Tiger-john

#Time :2011-05-10

#Function :Automaticly, instally module,create the proper device # and set the read/write bits appropriately

#!/bin/sh

 

module =”globalmen ”

 

#Remove module from kernel (just in case it is still #running)

 

/sbin/rmmod $module

 

#install module

/sbin/insmod ./$module.ko

 

#find major numer used

major =$(cat /proc/devices | grep Tiger-global | awk '{printf $ 1}')

echo Using major number $major

 

# Remove old device files

rm -f /dev/${module}[0-1]

 

 

#Create new device files

mknod /dev/${module}0 c $major 0

mknod /dev/${module}1 c $major 0

#change access mode (RW for everybody)

chmod 666 /dev/${module}[0-1]

 

如果要创建 个设备节点只要把

rm -f /dev/${module}[0-1]

chmod 666 /dev/${module}[0-1]

中的 该成 就可以了

然后添加 

mknod /dev/${module}n c $major n

就 OK 了 .

因为本文章的重点是 misc 所以不对此脚本进行具体讲解,如果在使用的时候出现问题,可以给我留言。我会进行回复。

 

上面的方法虽然解决了自动添加设备节点的问题;但是,它本质上还是没有改变驱动程序,只是在应用层通过脚本完成了自动添加设备节点。

现在,我们开始第二中方法也是本文的重点 通过修改驱动程序来让系统自动创建设备节点。

一 可爱的 misc 的身世

1> 输入命令 cat /dev/proc

Character devices:

1 mem

4 /dev/vc/0

4 tty

4 ttyS

5 /dev/tty

5 /dev/console

5 /dev/ptmx

6 lp

7 vcs

10 misc

我们可以看到 misc 是一个主设备号为 10 的字符设备。

2> 那么 Linux 为什么要定义一个 misc 呢?

misc 驱动是一些拥有着共同特性的简单字符设备驱动。内核抽象出这些特性而形成一些 API (在文件 drivers/char/misc.c 中实现),以简化这些设备驱动程序的初始化。所有的 misc 设备被分配给同一个主设备号 MISC_MAJOR(10), 但是每一个驱动设备可以选择一个单独的次设备号。

如果一个字符设备驱动要驱动多个设备,那么它就不应该用 misc 设备来实现

这是 misc 唯一的一个局限指出;因此也导致我最后想出了第三中方法,只是后话,在后面的文章中我会给大家介绍第三中方法 )

3>misc 的可爱之处:

如果大家熟悉字符设备驱动编程的过程的话都知道一个字符设备在初始化过程中要完成下面的步骤:

(1) 通过 alloc_chrdev_region() 分配设备号

(2) 使用 cdev_init() 和 cdev_add() 来注册设备

而一个 misc 驱动,则可以之用一个 misc_register() 来完成这所用的步骤。

并且它还有一个最开爱之处,就是他会自动创建设备节点。

二 .misc 的具体实现

1. 在 Linux 内核中,使用 struct miscdevice 来表示 miscdevice.

struct miscdevice {

int minor;

const char *name;

const struct file_operations *fops;

struct list_head list;

struct device *parent;

struct device *this_device;

};

1>minor 是这个混杂设备的次设备号,若有系统自动配置,则可以设置为 MISC_DYNANIC_MINOR

2>name: 是设备名

3>*fops 是 misc 设备文件操作结构体。

其他三个参数很少使用。

2.misc 设备注册函数

extern int misc_register(struct miscdevic *misc);

3.misc 设备的注销:

extern int misc_deregister(struct miscdevice *misc);

4. 使用 misc 驱动要包含头文件:

#include<linux/miscdevice.h>

三程序

我们还是以 globamen 驱动为例,用 misc 的特点来编写字符设备驱动。

1 /** Filename :globalmen.c

2 ** Author :Tiger-john

3 ** Time :2011-05-24

4 */

5 #include<linux/kernel.h>

6 #include<linux/init.h>

7 #include<linux/module.h>

8 #include<linux/cdev.h>

9 #include<linux/types.h>

10 #include<linux/fs.h>

11 #include<asm/uaccess.h>

12 #include<linux/errno.h>

13 #include<linux/miscdevice.h>

14

15 #define DEVICE_NAME "Tiger-global"

16 #define GLOBALMEM_SIZE 0x1000// 全局内存大小

17 #define MEM_CLEAR 0x1// 清空全局内存

18

19 #define GLOBAL_BUG

20

21 struct globalmem_dev

22 {

23 struct cdev cdev;//cdev 结构体

24 unsigned char mem[GLOBALMEM_SIZE];

25 };

26 struct globalmem_dev *global_dev;

27 // 文件打开函数

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

29 {

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

31 filp->private_data = global_dev;

32 return 0;

33 }

34 // 文件读函数

35 static ssize_t globalmem_read(struct file *filp,char __user *buf,size_t size,loff_t *ppos)

36 {

37 unsigned long p = *ppos;

38 unsigned int count = size;

39 int retval = 0;

39 int retval = 0;

40 struct globalmem_dev *dev = filp->private_data;

41 // 判断

42 if(p >= GLOBALMEM_SIZE)

43 return count ? -ENXIO : 0;

44 if(count > GLOBALMEM_SIZE -p)

45 count = GLOBALMEM_SIZE -p;

46 // 从内核空间向用户空间写数据

47 if(copy_to_user(buf,(void *)(dev->mem +p),count)){

48 retval = -EFAULT;

49 } else {

50 *ppos +=count;

51 retval =count;

52 #ifdef GLOBAL_BUG

53 printk(KERN_INFO "read %ld bytes from %ld/n",count,p);

54 #endif

55 }

56 return retval;

57

58 }

59 static ssize_t globalmem_write(struct file *filp,const char __user *buf,size_t size,loff_t *ppos)

60 {

61 unsigned long p = *ppos;

62 unsigned int count = size;

63 int retval;

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

65 // 判断

66 if ( p >= GLOBALMEM_SIZE)

67 return count ? -ENXIO :0;

68 if (count > GLOBALMEM_SIZE -p)

69 count = GLOBALMEM_SIZE-p;

70 // 从用户空间想内核空间写数据

71 if(copy_from_user((void*)dev->mem+p,buf,count))

72 retval = -EFAULT;

73 else {

74 *ppos += count;

75 retval = count;

76 #ifdef GLOBAL_BUG

77 printk(KERN_INFO "writen %d bytes from %d/n",count,p);

78 #endif

79 }

80 return retval;

81 }

82 // 文件定位函数

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

84 {

85 loff_t retval = 0;

86 switch (orig)

87 {

88 case 0 :// 相对文件开始位置偏移

89 if (offset < 0){

90 retval = - EINVAL;

91 break;

92 }

93 if ((unsigned int)offset > GLOBALMEM_SIZE){

94

95 retval = -EINVAL;

96 break;

97 }

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

99 retval = filp->f_pos;

100 case 1: // 相对文件当前位置偏移

101 if ((filp->f_pos + offset) > GLOBALMEM_SIZE){

102

103 retval = -EINVAL;

104 break;

105 }

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

107

108 retval = -EINVAL;

109 break;

110 }

111 //seek_cur

112 filp->f_pos += offset;

113 retval = filp->f_pos;

114 break;

115 default :

116 retval = -EINVAL;

117 break;

118 }

119 return retval;

120 }

121 static int globalmem_ioctl(struct inode *inode,struct file *filp,unsigned int cmd,unsigned long arg)

122 {

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

124 switch (cmd)

125 {

126 case MEM_CLEAR :

127 memset(dev->mem,0,GLOBALMEM_SIZE);

128 printk(KERN_INFO "globalmem is set to zero/n");

129 break;

130 default :

131 return -EINVAL;

132 }

133 return 0;

134 }

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

136 {

137 return 0;

138 }

139 static const struct file_operations globalmem_fops =

140 {

141 .owner = THIS_MODULE,

142 .open = globalmem_open,

143 .llseek = globalmem_llseek,

144 .read = globalmem_read,

145 .write = globalmem_write,

146 .ioctl = globalmem_ioctl,

147 .release = globalmem_release,

148 };

149

150 static struct miscdevice misc={

151 .minor = MISC_DYNAMIC_MINOR,

152 .name = DEVICE_NAME,

153 .fops = &globalmem_fops,

154 };

155

156 static int __init global_init(void)

157 {

158 int result;

159 global_dev = kmalloc(sizeof(struct globalmem_dev),GFP_KERNEL);

160 if(!global_dev){

161 result = -ENOMEM;

162

163 }

164 memset(global_dev,0,sizeof(struct globalmem_dev));

165

166 result = misc_register(&misc);

167 printk(DEVICE_NAME"initialized/n");

168 return result;

169 }

170 static void __exit global_exit(void)

171 {

172

173 kfree(global_dev);

174 misc_deregister(&misc);

175 printk("Goodbye global module!/n");

176 }

177 MODULE_LICENSE("GPL");

178 module_init(global_init);

179 module_exit(global_exit);

180 MODULE_AUTHOR("Tiger John");

181 MODULE_DESCRIPTION("a globalmem module");

我们发现用 misc 编写驱动程序只是

a.static int __init global_init(void) () 函数,

b.static void __exit global_exit(void) 函数不同,

c. 多了一个

static struct miscdevice misc={

151 .minor = MISC_DYNAMIC_MINOR,

152 .name = DEVICE_NAME,

153 .fops = &globalmem_fops,

154 };

其他的都和我们以前编写的 globalmen 驱动程序一模一样,而且用 misc 方法比以前的方法在初始化过程中变得简洁,并且系统会自动创建设备节点。我们要做的只是

(1)make

(2)sudo insmod globalmen.ko 加载模块

(3)sudo chmod 666 /dev/Tiger-globalmen 修改设备节点权限。后面的的应用层程序编写都和前面的方法相同,如果你不明白的话可以参看《 Linux 字符设备驱动 三 

但是, misc 方法只使用于每次驱动一个设备文件,如果要驱动多个设备文件就不能用此方法了,而要用我们后面讲的第三种方法 .

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值