当前位置:嗨网首页>书籍在线阅读

14-使用文件私有数据

  
选择背景色: 黄橙 洋红 淡粉 水蓝 草绿 白色 选择字体: 宋体 黑体 微软雅黑 楷体 选择字体大小: 恢复默认

6.3.6 使用文件私有数据

6.3.1~6.3.5节给出的代码完整地实现了预期的globalmem雏形,在其代码中,为globalmem设备结构体globalmem_dev定义了全局实例dev(见代码清单6.7第25行),而globalmem的驱动中read()、write()、ioctl()、llseek()函数都针对dev进行操作。

实际上,大多数Linux驱动工程师遵循一个“潜规则”,那就是将文件的私有数据private_data指向设备结构体,在read()、write()、ioctl()、llseek()等函数通过private_data访问设备结构体。

这个时候,我们要将各函数进行少量的修改,为了让读者朋友建立字符设备驱动的全貌视图,代码清单6.17列出了完整的使用文件私有数据的globalmem的设备驱动,本程序位于虚拟机/home/lihacker/develop/svn/ldd6410-read-only/training/kernel/drivers/globalmem/ch6目录。

代码清单6.17 使用文件私有数据的globalmem的设备驱动

1 #include <linux/module.h>

2 #include <linux/types.h>

3 #include <linux/fs.h>

4 #include <linux/errno.h>

5 #include <linux/mm.h>

6 #include <linux/sched.h>

7 #include <linux/init.h>

8 #include <linux/cdev.h>

9 #include <asm/io.h>

10 #include <asm/system.h>

11 #include <asm/uaccess.h>

12

13 #define GLOBALMEM_SIZE 0x1000 /全局内存最大4KB/

14 #define MEM_CLEAR 0x1 /清零全局内存/

15 #define GLOBALMEM_MAJOR 250 /预设的globalmem的主设备号/

16

17 static int globalmem_major = GLOBALMEM_MAJOR;

18 /globalmem设备结构体/

19 struct globalmem_dev {

20 struct cdev cdev; /cdev结构体/

21 unsigned char mem[GLOBALMEM_SIZE]; /全局内存/

22 };

23

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

25 /文件打开函数/

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

27 {

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

29 filp->private_data = globalmem_devp;

30 return 0;

31 }

32 /文件释放函数/

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

34 {

35 return 0;

36 }

37

38 / ioctl设备控制函数 /

39 static int globalmem_ioctl(struct inode inodep, struct file filp, unsigned

40 int cmd, unsigned long arg)

41 {

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

43

44 switch (cmd) {

45 case MEM_CLEAR:

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

47 printk(KERN_INFO "globalmem is set to zero\n");

48 break;

49

50 default:

51 return - EINVAL;

52 }

53

54 return 0;

55 }

56

57 /读函数/

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

59 loff_t *ppos)

60 {

61 unsigned long p = *ppos;

62 unsigned int count = size;

63 int ret = 0;

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

65

66 /分析和获取有效的写长度/

67 if (p >= GLOBALMEM_SIZE)

68 return 0;

69 if (count > GLOBALMEM_SIZE - p)

70 count = GLOBALMEM_SIZE - p;

71

72 /内核空间→用户空间/

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

74 ret = - EFAULT;

75 } else {

76 *ppos += count;

77 ret = count;

78

79 printk(KERN_INFO "read %u bytes(s) from %lu\n", count, p);

80 }

81

82 return ret;

83 }

84

85 /写函数/

86 static ssize_t globalmemwrite(struct file *filp, const char _user *buf,

87 size_t size, loff_t *ppos)

88 {

89 unsigned long p = *ppos;

90 unsigned int count = size;

91 int ret = 0;

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

93

94 /分析和获取有效的写长度/

95 if (p >= GLOBALMEM_SIZE)

96 return 0;

97 if (count > GLOBALMEM_SIZE - p)

98 count = GLOBALMEM_SIZE - p;

99

100 /用户空间→内核空间/

101 if (copy_from_user(dev->mem + p, buf, count))

102 ret = - EFAULT;

103 else {

104 *ppos += count;

105 ret = count;

106

107 printk(KERN_INFO "written %u bytes(s) from %lu\n", count, p);

108 }

109

110 return ret;

111 }

112

113 / seek文件定位函数 /

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

115 {

116 loff_t ret = 0;

117 switch (orig) {

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

119 if (offset < 0) {

120 ret = - EINVAL;

121 break;

122 }

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

124 ret = - EINVAL;

125 break;

126 }

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

128 ret = filp->f_pos;

129 break;

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

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

132 ret = - EINVAL;

133 break;

134 }

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

136 ret = - EINVAL;

137 break;

138 }

139 filp->f_pos += offset;

140 ret = filp->f_pos;

141 break;

142 default:

143 ret = - EINVAL;

144 break;

145 }

146 return ret;

147 }

148

149 /文件操作结构体/

150 static const struct file_operations globalmem_fops = {

151 .owner = THIS_MODULE,

152 .llseek = globalmem_llseek,

153 .read = globalmem_read,

154 .write = globalmem_write,

155 .ioctl = globalmem_ioctl,

156 .open = globalmem_open,

157 .release = globalmem_release,

158 };

159

160 /初始化并注册cdev/

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

162 {

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

164

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

166 dev->cdev.owner = THIS_MODULE;

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

168 if (err)

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

170 }

171

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

173 int globalmem_init(void)

174 {

175 int result;

176 dev_t devno = MKDEV(globalmem_major, 0);

177

178 / 申请设备号/

179 if (globalmem_major)

180 result = register_chrdev_region(devno, 1, "globalmem");

181 else { / 动态申请设备号 /

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

183 globalmem_major = MAJOR(devno);

184 }

185 if (result < 0)

186 return result;

187

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

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

190 if (!globalmem_devp) { /申请失败/

191 result = - ENOMEM;

192 goto fail_malloc;

193 }

194

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

196

197 globalmem_setup_cdev(globalmem_devp, 0);

198 return 0;

199

200 fail_malloc:

201 unregister_chrdev_region(devno, 1);

202 return result;

203 }

204

205 /模块卸载函数/

206 void globalmem_exit(void)

207 {

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

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

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

211 }

212

213 MODULE_AUTHOR("Barry Song [email protected]");

214 MODULE_LICENSE("Dual BSD/GPL");

215

216 module_param(globalmem_major, int, S_IRUGO);

217

218 module_init(globalmem_init);

219 module_exit(globalmem_exit);

除了在globalmem_open()函数中通过filp->private_data = globalmem_devp语句(见第29行)将设备结构体指针赋值给文件私有数据指针并在globalmem_read()、globalmem_write()、globalmem_llseek()和globalmem_ioctl()函数中通过struct globalmem_dev *dev = filp->private_data语句获得设备结构体指针并使用该指针操作设备结构体外,代码清单6.17与代码清单6.7~6.15的程序基本相同。

BZ___154_141_465_218_542.png 读者朋友们,这个时候,请您翻回到本书的第1章,再次阅读代码清单1.4,即Linux下LED的设备驱动,是否豁然开朗?

代码清单6.17仅仅作为使用private_data的范例,实际上,在这个程序中使用private_data没有任何意义,直接访问全局变量globalmem_devp会更加结构清晰。如果globalmem不只包括一个设备,而是同时包括两个或两个以上的设备,采用private_data的优势就会集中显现出来。

在不对代码清单6.17中的globalmem_read()、globalmem_write()、globalmem_ioctl()等重要函数及globalmem_fops结构体等数据结构进行任何修改的前提下,只是简单地修改globalmem_init()、globalmem_exit()和globalmem_open(),就可以轻松地让globalmem驱动中包含两个同样的设备(次设备号分别为0和1),如代码清单6.18所示。

代码清单6.18 支持2个globalmem设备的globalmem驱动

1 /文件打开函数/

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

3 {

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

5 struct globalmem_dev *dev;

6

7 dev = container_of(inode->i_cdev,struct globalmem_dev,cdev);

8 filp->private_data = dev;

9 return 0;

10 }

11

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

13 int globalmem_init(void)

14 {

15 int result;

16 dev_t devno = MKDEV(globalmem_major, 0);

17

18 / 申请设备号/

19 if (globalmem_major)

20 result = register_chrdev_region(devno, 2, "globalmem");

21 else { / 动态申请设备号 /

23 result = alloc_chrdev_region(&devno, 0, 2, "globalmem");

24 globalmem_major = MAJOR(devno);

25 }

26 if (result < 0)

27 return result;

28

29 / 动态申请两个设备结构体的内存/

30 globalmem_devp = kmalloc(2*sizeof(struct globalmem_dev), GFP_KERNEL);

31 if (!globalmem_devp) { /申请失败/

33 result = - ENOMEM;

34 goto fail_malloc;

35 }

36 memset(globalmem_devp, 0, 2*sizeof(struct globalmem_dev));

37

38 globalmem_setup_cdev(&globalmem_devp[0], 0);

39 globalmem_setup_cdev(&globalmem_devp[1], 1);

40 return 0;

41

42 fail_malloc: unregister_chrdev_region(devno, 1);

43 return result;

44 }

45

46 /模块卸载函数/

47 void globalmem_exit(void)

48 {

49 cdev_del(&(globalmem_devp[0].cdev));

50 cdev_del(&(globalmem_devp[1].cdev)); / 注销cdev /

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

52 unregister_chrdev_region(MKDEV(globalmem_major, 0), 2); /释放设备号/

53 }

/ 其他代码同清单6.16 /

代码清单6.18第7行调用的container_of()的作用是通过结构体成员的指针找到对应结构体的指针,这个技巧在Linux内核编程中十分常用。在container_of(inode->i_cdev,struct globalmem_dev, cdev)语句中,传给container_of()的第1个参数是结构体成员的指针,第2个参数为整个结构体的类型,第3个参数为传入的第1个参数即结构体成员的类型,container_of()返回值为整个结构体的指针。