你知道Linux内核字符设备驱动的写 *** 作?

你知道Linux内核字符设备驱动的写 *** 作?,第1张

应用程序write函数的使用:

char *p = “hello,world”; 
write(fd, p, 12); //将数据写入到设备

底层驱动write接口

struct file_operaTIons { ssize_t (*write) (struct file *file, const char __user *buf, size_t count, loff_t *ppos);};write接口作用:用于写设备,将数据写入到设备中与应用程序write的调用关系:应用程序调用write->...->调用驱动write接口参数:file:文件指针buf:保存用户缓冲区的首地址(p),在驱动程序中不能直接访问这个buf,如果驱动程序要向从用户空间将数据从buf拷贝到内核空间,必须利用内核提供的内存拷贝函数count:用户要写入的字节数,例如12字节 ppos:保存写的位置信息,例如 获取上一次的写位置: loff_t pos = *ppos; 假如这次成功写了12字节; 最后要更新写位置信息: *ppos = pos + 12;

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

切记:对于write接口的第二个参数buf,这个buf指针保存的是用户缓冲区的首地址,在内核空间不能直接访问 *** 作,需要利用内核的内存拷贝函数,将用户数据拷贝到内核空间,这个内存拷贝函数:

unsigned long copy_from_user(void *to, void __user *from, unsigned long n)作用:将用户缓冲区的数据拷贝到内核缓冲区中参数:to:目的地址,传递内核缓冲区的首地址from:源地址,传递用户缓冲区的首地址(buf)n:要拷贝的字节数将来只要看到__user修饰的指针,就不能在驱动中直接访问 *** 作,必须利用内存拷贝函数!

1

2

3

4

5

6

7

8

9

案例:编写字符设备驱动,提供write接口,将用户空间的数据写入到内核空间

int udata = 0x5555;  write(fd, &udata, sizeof(udata));

#include #include #include //struct file_operaTIons#include //struct cdev + 设备号#include #include #include //copy_to_user//声明描述LED硬件相关的数据结构struct led_resource { char *name; int gpio;};//定义初始化LED硬件信息static struct led_resource led_info[] = { [0] = { .name = "LED1", .gpio = S5PV210_GPC0(3) }, [1] = { .name = "LED2", .gpio = S5PV210_GPC0(4) }};//定义设备号static dev_t dev;//定义字符设备对象static struct cdev led_cdev;//调用关系:应用程序open->....->led_openstaTIc int led_open(struct inode *inode, struct file *file){ int i; for(i = 0; i < ARRAY_SIZE(led_info); i++) gpio_set_value(led_info[i].gpio, 1); printk("%s ", __func__); return 0; //执行成功返回0,执行失败返回负值 }//调用关系:应用程序close->...->led_closestaTIc int led_close(struct inode *inode, struct file *file){ int i; for(i = 0; i < ARRAY_SIZE(led_info); i++) gpio_set_value(led_info[i].gpio, 0); printk("%s ", __func__); return 0; //执行成功返回0,执行失败返回负值 }//调用关系:应用程序read->...->led_readstatic ssize_t led_read(struct file *file, char __user *buf, size_t count, loff_t *ppos){ //定义初始化内核缓冲区(存储空间再后1G虚拟内存中) int kdata = 0x5555; //将内核数据上报给用户 //切记:buf虽然保存的用户缓冲区的首地址,但不能直接访问 //*(int *)buf = kdata;错误 copy_to_user(buf, &kdata, sizeof(kdata)); printk("%s ", __func__); return sizeof(kdata); //失败返回负值,成功返回实际读取的字节数}//调用关系:应用程序write->...->最终调用led_writestatic ssize_t led_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos){ //定义内核缓冲区 int kdata; //拷贝用户数据到内核 copy_from_user(&kdata, buf, sizeof(kdata)); printk("%s:从用户写入的数据 kdata = %#x ", __func__, kdata); return count; //失败返回负值,成功返回写入的字节数}//定义初始化硬件 *** 作方法static struct file_operations led_fops = { .owner = THIS_MODULE, .open = led_open, //打开设备 .release = led_close, //关闭设备 .read = led_read, //读取设备 .write = led_write //写设备};static int led_init(void){ int i; //申请设备号 alloc_chrdev_region(&dev, 0, 1, "tarena"); //初始化字符设备对象 cdev_init(&led_cdev, &led_fops); //注册字符设备对象到内核 cdev_add(&led_cdev, dev, 1); //申请GPIO资源和配置GPIO为输出口,输出0(省电) for (i = 0; i < ARRAY_SIZE(led_info); i++) { gpio_request(led_info[i].gpio, led_info[i].name); gpio_direction_output(led_info[i].gpio, 0); } return 0;}static void led_exit(void){ int i; //输出0,释放GPIO资源 for (i = 0; i < ARRAY_SIZE(led_info); i++) { gpio_set_value(led_info[i].gpio, 0); gpio_free(led_info[i].gpio); } //卸载字符设备对象 cdev_del(&led_cdev); //释放设备号 unregister_chrdev_region(dev, 1);}module_init(led_init);module_exit(led_exit);MODULE_LICENSE("GPL");

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

#include #include #include #include int main(void){ int fd; int udata = 0x5555; //定义用户缓冲区 //打开设备 //open->....->调用led_open fd = open("/dev/myled", O_RDWR); if (fd < 0) { printf("打开设备失败! "); return -1; } //write->...->调用led_write write(fd, &udata, sizeof(udata)); //关闭设备 //close->...->调用led_close close(fd); return 0;}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

案例:用户写1,开所有的灯;用户写0,关所有的灯;

#include #include #include //struct file_operations#include //struct cdev + 设备号#include #include #include //copy_to_user//声明描述LED硬件相关的数据结构struct led_resource { char *name; int gpio;};//定义初始化LED硬件信息static struct led_resource led_info[] = { [0] = { .name = "LED1", .gpio = S5PV210_GPC0(3) }, [1] = { .name = "LED2", .gpio = S5PV210_GPC0(4) }};//定义设备号static dev_t dev;//定义字符设备对象static struct cdev led_cdev;//调用关系:应用程序open->....->led_openstatic int led_open(struct inode *inode, struct file *file){ int i; for(i = 0; i < ARRAY_SIZE(led_info); i++) gpio_set_value(led_info[i].gpio, 1); printk("%s ", __func__); return 0; //执行成功返回0,执行失败返回负值 }//调用关系:应用程序close->...->led_closestatic int led_close(struct inode *inode, struct file *file){ int i; for(i = 0; i < ARRAY_SIZE(led_info); i++) gpio_set_value(led_info[i].gpio, 0); printk("%s ", __func__); return 0; //执行成功返回0,执行失败返回负值 }//调用关系:应用程序read->...->led_readstatic ssize_t led_read(struct file *file, char __user *buf, size_t count, loff_t *ppos){ //定义初始化内核缓冲区(存储空间再后1G虚拟内存中) int kdata = 0x5555; //将内核数据上报给用户 //切记:buf虽然保存的用户缓冲区的首地址,但不能直接访问 //*(int *)buf = kdata;错误 copy_to_user(buf, &kdata, sizeof(kdata)); printk("%s ", __func__); return sizeof(kdata); //失败返回负值,成功返回实际读取的字节数}//调用关系:应用程序write->...->最终调用led_writestatic ssize_t led_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos){ int i; //定义内核缓冲区 int kdata; //拷贝用户数据到内核 copy_from_user(&kdata, buf, sizeof(kdata)); //开或者关灯 for (i = 0; i < ARRAY_SIZE(led_info); i++) gpio_set_value(led_info[i].gpio, kdata); return count; //失败返回负值,成功返回写入的字节数}//定义初始化硬件操作方法static struct file_operations led_fops = { .owner = THIS_MODULE, .open = led_open, //打开设备 .release = led_close, //关闭设备 .read = led_read, //读取设备 .write = led_write //写设备};static int led_init(void){ int i; //申请设备号 alloc_chrdev_region(&dev, 0, 1, "tarena"); //初始化字符设备对象 cdev_init(&led_cdev, &led_fops); //注册字符设备对象到内核 cdev_add(&led_cdev, dev, 1); //申请GPIO资源和配置GPIO为输出口,输出0(省电) for (i = 0; i < ARRAY_SIZE(led_info); i++) { gpio_request(led_info[i].gpio, led_info[i].name); gpio_direction_output(led_info[i].gpio, 0); } return 0;}static void led_exit(void){ int i; //输出0,释放GPIO资源 for (i = 0; i < ARRAY_SIZE(led_info); i++) { gpio_set_value(led_info[i].gpio, 0); gpio_free(led_info[i].gpio); } //卸载字符设备对象 cdev_del(&led_cdev); //释放设备号 unregister_chrdev_region(dev, 1);}module_init(led_init);module_exit(led_exit);MODULE_LICENSE("GPL");

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

#include #include #include #include int main(void){ int fd; int udata; //定义用户缓冲区 //打开设备 //open->....->调用led_open fd = open("/dev/myled", O_RDWR); if (fd < 0) { printf("打开设备失败! "); return -1; } //write->...->调用led_write while (1) { udata = 1; //开 write(fd, &udata, sizeof(udata)); sleep(1); udata = 0; //关 write(fd, &udata, sizeof(udata)); sleep(1); } //关闭设备 //close->...->调用led_close close(fd); return 0;}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

案例:用户能够指定其中某个灯的开关状态;

提示:

用户不仅仅要告诉灯的开关状态,还要告诉驱动用户现在要想 *** 作哪个灯;

#include #include #include //struct file_operations#include //struct cdev + 设备号#include #include #include //copy_to_user//声明LED *** 作的数据结构struct led_cmd { int index; int cmd;};//声明描述LED硬件相关的数据结构struct led_resource { char *name; int gpio;};//定义初始化LED硬件信息static struct led_resource led_info[] = { [0] = { .name = "LED1", .gpio = S5PV210_GPC0(3) }, [1] = { .name = "LED2", .gpio = S5PV210_GPC0(4) }};//定义设备号static dev_t dev;//定义字符设备对象static struct cdev led_cdev;//调用关系:应用程序open->....->led_openstatic int led_open(struct inode *inode, struct file *file){ int i; for(i = 0; i < ARRAY_SIZE(led_info); i++) gpio_set_value(led_info[i].gpio, 1); printk("%s ", __func__); return 0; //执行成功返回0,执行失败返回负值 }//调用关系:应用程序close->...->led_closestatic int led_close(struct inode *inode, struct file *file){ int i; for(i = 0; i < ARRAY_SIZE(led_info); i++) gpio_set_value(led_info[i].gpio, 0); printk("%s ", __func__); return 0; //执行成功返回0,执行失败返回负值 }//调用关系:应用程序read->...->led_readstatic ssize_t led_read(struct file *file, char __user *buf, size_t count, loff_t *ppos){ //定义初始化内核缓冲区(存储空间再后1G虚拟内存中) int kdata = 0x5555; //将内核数据上报给用户 //切记:buf虽然保存的用户缓冲区的首地址,但不能直接访问 //*(int *)buf = kdata;错误 copy_to_user(buf, &kdata, sizeof(kdata)); printk("%s ", __func__); return sizeof(kdata); //失败返回负值,成功返回实际读取的字节数}//调用关系:应用程序write->...->最终调用led_writestatic ssize_t led_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos){ //定义内核缓冲区 struct led_cmd kdata; //拷贝用户数据到内核 copy_from_user(&kdata, buf, sizeof(kdata)); //开或者关灯 gpio_set_value(led_info[kdata.index - 1].gpio, kdata.cmd); printk("%s:第%d灯被%s ", __func__, kdata.index, kdata.cmd?"打开":"关闭"); return count; //失败返回负值,成功返回写入的字节数}//定义初始化硬件 *** 作方法static struct file_operations led_fops = { .owner = THIS_MODULE, .open = led_open, //打开设备 .release = led_close, //关闭设备 .read = led_read, //读取设备 .write = led_write //写设备};static int led_init(void){ int i; //申请设备号 alloc_chrdev_region(&dev, 0, 1, "tarena"); //初始化字符设备对象 cdev_init(&led_cdev, &led_fops); //注册字符设备对象到内核 cdev_add(&led_cdev, dev, 1); //申请GPIO资源和配置GPIO为输出口,输出0(省电) for (i = 0; i < ARRAY_SIZE(led_info); i++) { gpio_request(led_info[i].gpio, led_info[i].name); gpio_direction_output(led_info[i].gpio, 0); } return 0;}static void led_exit(void){ int i; //输出0,释放GPIO资源 for (i = 0; i < ARRAY_SIZE(led_info); i++) { gpio_set_value(led_info[i].gpio, 0); gpio_free(led_info[i].gpio); } //卸载字符设备对象 cdev_del(&led_cdev); //释放设备号 unregister_chrdev_region(dev, 1);}module_init(led_init);module_exit(led_exit);MODULE_LICENSE("GPL");

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

#include #include #include #include //声明LED *** 作的数据结构struct led_cmd { int index; //指定灯的编号 int cmd; //开关命令};int main(void){ int fd; struct led_cmd udata; //定义用户缓冲区 //打开设备 //open->....->调用led_open fd = open("/dev/myled", O_RDWR); if (fd < 0) { printf("打开设备失败! "); return -1; } //write->...->调用led_write while (1) { udata.index = 1; //第一个灯 udata.cmd = 1; //开 write(fd, &udata, sizeof(udata)); sleep(1); udata.index = 2; //第二个灯 write(fd, &udata, sizeof(udata)); sleep(1); } //关闭设备 //close->...->调用led_close close(fd); return 0;}
 

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/dianzi/2714693.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-08-17
下一篇 2022-08-17

发表评论

登录后才能评论

评论列表(0条)

保存