EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
文章目录( k4 @% B6 C2 @) h- G0 s
- 1 修改设备树文件
- 2 platform驱动程序
- 3 应用测试程序
0 `: h, H& o7 @" c
8 h Y- s9 C9 j: f5 ]1 修改设备树文件设备树文件可以直接使用第三十五章中添加的gpioled子节点即可,不用重复添加。 2 platform驱动程序本实验例程路径:i.MX6UL终结者光盘资料/06_Linux驱动例程/15_gpioled_dts- y6 \/ y: U: Q! C& }7 ?2 H
创建led_driver.c文件,具体内容如下: - 1 #include <linux/types.h>8 z. L1 P! m# p1 |# k8 q
- 2 #include <linux/kernel.h>% F8 Q% U% B( r+ W3 {7 ?9 E. T
- 3 #include <linux/delay.h>
2 r# L0 O# z* |- x- ~$ y% I0 ` - 4 #include <linux/ide.h>
9 r+ S" i5 i, W, w9 C0 G - 5 #include <linux/init.h>8 P4 m, n% f7 t& O! {7 c
- 6 #include <linux/module.h>
- N- ~( p) B9 [7 \ - 7 #include <linux/errno.h>
$ d1 v# y/ ?! v; U! L9 a7 P$ G - 8 #include <linux/gpio.h>) K* o' L' ~8 [! I4 ~$ O; O! Q
- 9 #include <linux/cdev.h>" O3 G+ ^8 @' b" m" a c( }0 h
- 10 #include <linux/device.h>
8 e/ ?. I5 O3 j - 11 #include <linux/of_gpio.h>+ d7 R2 a- \+ u: S
- 12 #include <linux/semaphore.h>
6 B7 R* w. H4 t/ d# _ - 13 #include <linux/timer.h>
( y# o' ?# W; Q1 u3 w - 14 #include <linux/irq.h>
+ c' v0 ]- p5 Z - 15 #include <linux/wait.h>
7 z$ U+ w7 M: H" P! @$ j7 d - 16 #include <linux/poll.h>
) Q; B: p' S p. f - 17 #include <linux/fs.h>
! B: a( @& S, @. i2 }- U - 18 #include <linux/fcntl.h>+ q3 H& x9 |, u/ X9 I
- 19 #include <linux/platform_device.h>
; |3 e' [" {4 k1 q' l# N+ a9 @ - 20 #include <asm/mach/map.h>3 u0 P0 o% D" H: Y1 y
- 21 #include <asm/uaccess.h>
L0 p! N5 a0 N4 h* U! M2 N+ T) j - 22 #include <asm/io.h>
$ P6 v" D0 E" R8 S- Z - 23 3 k+ Y J/ Y$ X0 t
- 24 #define LEDDEV_CNT 1 /* 设备号长度 */
' w. m; ^- w8 j - 25 #define LEDDEV_NAME "dtsplatled" /* 设备名字 */
: l: V) ^& S% X! k( ]( ^ - 26 #define LEDOFF 0
/ l0 O, y: Q/ I N9 C - 27 #define LEDON 1
: b8 S+ A, K/ A6 R0 P. v - 28
/ M# c5 i6 p% ?' h% i* e0 D - 29 /* leddev设备结构体 */
. i/ j& B* ^. \& p% C: z - 30 struct leddev_dev{
/ b; R( M2 I2 @0 w: Q& _ - 31 dev_t devid; /* 设备号 */( D/ D2 g8 `0 v
- 32 struct cdev cdev; /* cdev */
2 i* r0 N0 h. j; t6 Y1 i - 33 struct class *class; /* 类 */% C5 S$ I/ a/ g8 W! N# \6 Z
- 34 struct device *device; /* 设备 */
) {3 t% \# |) h' u" N* G - 35 int major; /* 主设备号 */
+ d$ ?. e7 M1 t - 36 struct device_node *node; /* LED设备节点 */* |8 s( {7 I2 O# h1 }0 J, t
- 37 int led0; /* LED灯GPIO标号 */
, {- [$ ~2 j c/ w f) v7 { - 38 };$ H8 U: s" P' n# H* c6 L3 g/ [
- 39 8 j- M* t" y, ^+ d4 s+ @ \
- 40 struct leddev_dev leddev; /* led设备 */; e; t' T; E+ p, k5 r
- 41
0 ~" ? Y" F4 i E3 k1 ]- r - 42 /*
7 X1 |! f' A( y, o" o1 { - 43 * @description : LED打开/关闭" r6 V9 Z# r7 }3 u. X
- 44 * @param - sta : LEDON(0) 打开LED,LEDOFF(1) 关闭LED
; s4 x. \" l3 e' f3 p; i6 ]0 i - 45 * @return : 无
) W- K* W! h4 Q& _4 ?( C9 y" d - 46 */$ Y0 o5 `, I( z& \
- 47 void led0_switch(u8 sta)' Y* B2 Y/ U: I, K3 W
- 48 {6 n% j: [& ~' x9 H& r1 ~
- 49 if (sta == LEDON )7 [$ G0 ` b4 B J7 s, q# z
- 50 gpio_set_value(leddev.led0, 0);
' ~$ w7 P4 c4 m" R5 d/ C9 H, n' c - 51 else if (sta == LEDOFF)
. n) n F7 ], a6 z5 R - 52 gpio_set_value(leddev.led0, 1);
* K1 M/ q1 @0 N5 A - 53 }
# T9 N0 |5 T! y3 L7 V3 s5 d - 54
% ]/ G( b, E9 @9 ^4 M9 \ - 55 /*
4 ?2 ^) K( ]5 F/ r& P - 56 * @description : 打开设备
& |/ e& ?6 Q" i - 57 * @param - inode : 传递给驱动的inode" L9 G- K* Y; J% d, Q% P4 k
- 58 * @param - filp : 设备文件,file结构体有个叫做private_data的成员变量- P8 {2 X6 J7 m% [4 W6 b
- 59 * 一般在open的时候将private_data指向设备结构体。9 [" V) t' F: u1 i' C
- 60 * @return : 0 成功;其他 失败9 L. n6 |+ u% E5 Z1 c
- 61 */
7 Q/ G3 y: ^) X2 f - 62 static int led_open(struct inode *inode, struct file *filp)( ~' ^ B/ ~4 ~- i* R; i3 y& g
- 63 {
% M5 C; ]4 D2 Y# B3 L - 64 filp->private_data = &leddev; /* 设置私有数据 */* P5 p ^. x* P, F
- 65 return 0;
$ @$ T! }0 E7 g( o - 66 }
. L! n& }3 h# W% j) M" ~% K* d - 67
# l/ e" V4 }4 A( [( l - 68 /*+ L: N& ^/ v9 m- G# |6 k% ~+ r
- 69 * @description : 向设备写数据 - }2 R9 |- s% K$ `1 c$ r2 ~4 j
- 70 * @param - filp : 设备文件,表示打开的文件描述符% p' S+ K$ p- Z6 v
- 71 * @param - buf : 要写给设备写入的数据! X1 x( N: z0 s. T2 |
- 72 * @param - cnt : 要写入的数据长度
1 m% k: B/ T4 Q - 73 * @param - offt : 相对于文件首地址的偏移1 M5 Z+ N% l+ P" U/ o; K3 D6 u/ w1 H. y
- 74 * @return : 写入的字节数,如果为负值,表示写入失败% I+ A9 s% I4 ]
- 75 */5 R. O9 N1 m2 p9 Q0 k/ s
- 76 static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)$ J; D" d2 D5 H" C
- 77 {
( o( o3 G8 g2 p/ ^- j% N - 78 int retvalue;
8 g% w. u! C7 ~5 i - 79 unsigned char databuf[2];
" x/ {$ j8 h! d+ L - 80 unsigned char ledstat;3 W( e- @ y7 H" \
- 81 $ T. d5 F4 E! T2 M
- 82 retvalue = copy_from_user(databuf, buf, cnt);: S$ A) m- H/ q$ T/ H( H5 c
- 83 if(retvalue < 0) {8 Y( O) o6 p% ~! s; {; D# m4 M
- 84 # V- Q* N. e2 G4 D1 W% r* A2 s8 V
- 85 printk("kernel write failed!\r\n");, N- K7 p z/ o' e: s. L4 }/ J
- 86 return -EFAULT;+ C9 d4 Q7 r0 U9 M5 ~$ Q. C5 I8 N
- 87 }! e- f/ |1 g/ U2 w5 P
- 88
: r7 D- Z4 V+ R - 89 ledstat = databuf[0];& @1 z! I% B z" ?% H% q5 R6 _0 W8 M
- 90 if (ledstat == LEDON) {7 d+ L/ o- B6 F- v( J% ^' C4 l
- 91 led0_switch(LEDON);
! I8 M! F! s% r& K/ p. N4 q - 92 } else if (ledstat == LEDOFF) {
/ o5 E% I5 w: u: a! E: Q' h - 93 led0_switch(LEDOFF);
- \) W$ H: u4 D - 94 }( t1 ?$ d2 \* V2 c& Q7 B% E6 X! Z2 U
- 95 return 0;
) c! F# L0 ^" T! i - 96 }
# @5 k9 F! F$ B( | - 97 1 _, V# [; ~) E/ u2 F
- 98 /* 设备操作函数 */
( m# V* A; v' t3 _ - 99 static struct file_operations led_fops = {* x1 W9 n) M2 J3 b! s3 U
- 100 .owner = THIS_MODULE,# I) Q2 G3 s; a) x) ^. B
- 101 .open = led_open,6 Z! D5 H1 a; @, E& L$ o
- 102 .write = led_write,6 y0 q- N& y4 V2 U$ ]3 ~" x
- 103 };. z6 x' u! Q# L9 Z
- 104
0 ^: j7 U3 D7 H, E7 d6 i - 105 /*
$ n/ G! q' {. |! n# r0 Z* Z - 106 * @description : flatform驱动的probe函数,当驱动与
+ @$ I8 i/ h' N0 z& t8 @0 |. l% I - 107 * 设备匹配以后此函数就会执行, H n0 l9 V9 Y& [5 |, e
- 108 * @param - dev : platform设备
2 p$ l" ?# }% L' i - 109 * @return : 0,成功;其他负值,失败2 L* S. M6 ?/ Z
- 110 */5 h) k: H* s8 j3 ^8 m3 U6 w' @! J
- 111 static int led_probe(struct platform_device *dev)' Z+ @5 u' Q. Q& C2 l( l
- 112 {
) b' s, I4 ^7 d h( [ - 113 printk("led driver and device was matched!\r\n");" a4 j6 @$ |, G) F( P2 u$ i( D
- 114 /* 1、设置设备号 */9 v: {! s* ~- l" W0 G. |
- 115 if (leddev.major) {/ O* p' }2 E1 [6 } z! _
- 116 leddev.devid = MKDEV(leddev.major, 0);
3 K1 Z# G7 @+ Z& H- c8 s - 117 register_chrdev_region(leddev.devid, LEDDEV_CNT, LEDDEV_NAME);
$ J, S1 ?" ~: V, V. Q - 118 } else {
* f! s# Q" s/ C% a - 119 alloc_chrdev_region(&leddev.devid, 0, LEDDEV_CNT, LEDDEV_NAME);# S) }+ y" ~2 S
- 120 leddev.major = MAJOR(leddev.devid);
2 D3 \; ]. ^% s5 P% {) D+ J0 w - 121 }. P* h7 N1 b6 z% h
- 122 , H+ F% }( w) y& J, \& ]/ K/ `: a
- 123 /* 2、注册设备 */
# m+ ~; N/ m8 b9 a/ v$ O- ?7 W, w- s - 124 cdev_init(&leddev.cdev, &led_fops);
- N% j% m3 v3 W0 \; a# } - 125 cdev_add(&leddev.cdev, leddev.devid, LEDDEV_CNT);5 i% l% J! S0 d3 u) u) [6 r0 k
- 126
: c# [4 }3 ?; o$ _: V8 ?6 F6 q - 127 /* 3、创建类 */
+ G$ w2 ]2 _" [) C% H- ]0 r. b - 128 leddev.class = class_create(THIS_MODULE, LEDDEV_NAME);/ }1 C) |9 Q1 L7 k L' y% x _# s# V9 `
- 129 if (IS_ERR(leddev.class)) {8 x: J& j' M Q7 [0 C' R
- 130 return PTR_ERR(leddev.class);9 R" S- b2 l/ h
- 131 }
4 o1 |! I5 ^: q - 132 / W" G! Q7 w7 M8 b, s9 s5 [ z
- 133 /* 4、创建设备 */' N S! f: l: `8 n5 F
- 134 leddev.device = device_create(leddev.class, NULL, leddev.devid,
& {; N* ^3 ]* V: D0 Y - NULL, LEDDEV_NAME);, ~6 D% J& c5 s; J; L" f* T
- 135 if (IS_ERR(leddev.device)) {+ d7 K8 R; Y. \, |
- 136 return PTR_ERR(leddev.device);
4 \0 a1 ^. U4 f - 137 }
, P+ L( K. D7 p9 ?* y# ? - 138
# g! ~3 D" i' |) M. _ - 139 /* 5、初始化IO */3 U/ b! P3 J2 s) h
- 140 leddev.node = of_find_node_by_path("/gpioled");
: ~% K9 m( l) ~3 v2 h) q - 141 if (leddev.node == NULL){
# A; u) ^( ^1 p% i K$ c - 142 printk("gpioled node nost find!\r\n");. e3 C7 k4 ]' F- Y. ]# d8 z) f+ V
- 143 return -EINVAL;9 j3 Q. {# r* k2 g7 R5 ], m3 F6 x
- 144 }
: R3 ]8 n! W. D, p9 d* @" Y$ R - 145
7 L. R* N# T* Q; m3 U( z& l, l$ s - 146 leddev.led0 = of_get_named_gpio(leddev.node, "led-gpio", 0);
5 C: \5 f2 L7 z. H - 147 if (leddev.led0 < 0) {
: t) F5 g# ?" d% Z - 148 printk("can't get led-gpio\r\n");1 G, ?. B! W. U
- 149 return -EINVAL;0 K. G* q& Y" f1 ^9 ?3 x
- 150 }7 X8 o( `! @9 \) X) O+ O
- 151
3 j0 T `# C/ I, ]. o! e9 r - 152 gpio_request(leddev.led0, "led0");
# L& P9 H, Q0 Q# p - 153 gpio_direction_output(leddev.led0, 1); /* led0 IO设置为输出,默认高电平 */. W; @- C2 U3 q
- 154 return 0;
+ h3 C# v, O7 O% T8 H) Y7 G3 b) z - 155 }
- `1 `( P) m8 g# e5 s1 P - 156 * |% k( Z% _4 h9 ~2 z8 n% ~6 x8 U
- 157 /*
, _+ F# H. T3 P* e% v; P/ h9 V - 158 * @description : platform驱动的remove函数,移除platform
! R" \9 f0 {+ y- S/ }/ U3 h, } - 驱动的时候此函数会执行; E* y6 O" }& T: i
- 159 * @param - dev : platform设备
' @4 V& k+ \' a. \ - 160 * @return : 0,成功;其他负值,失败' r( w1 Z( V3 F) L/ ^6 }& Z
- 161 */- h5 g. Y3 q5 }9 F% x" e
- 162 static int led_remove(struct platform_device *dev)
. q+ K7 t% U! H4 i - 163 {2 I R$ h1 g, l% t
- 164 gpio_set_value(leddev.led0, 1); /* 卸载驱动的时候关闭LED */
$ r5 |9 Y& B0 J - 165
' v9 B' R& @' ], m* y - 166 cdev_del(&leddev.cdev); /* 删除cdev */
2 o) [/ y9 _. F7 x - 167 unregister_chrdev_region(leddev.devid, LEDDEV_CNT); /* 注销设备号 */ Y5 L6 t& N* E: o
- 168 device_destroy(leddev.class, leddev.devid);
1 g) B1 A* @( S: z; H" r - 169 class_destroy(leddev.class);; I7 D# K, P ^- {
- 170 return 0;
' m0 R7 g" \: k. @ - 171 }
5 }5 D" g7 `( I - 172
/ z+ x, |# F3 z& { - 173 /* 匹配列表 */
% y; v4 U( J) T" y' c$ k, b - 174 static const struct of_device_id led_of_match[] = {
8 D7 f4 e/ x9 i6 n8 Z0 Z+ Y - 175 { .compatible = "gpioled" },2 L+ H) C8 h+ ^2 {$ L
- 176 { /* Sentinel */ }
6 B' {3 E# s7 M* V6 P: v - 177 };
$ {2 b& b* I# X5 e9 ? - 178 0 `7 I" g0 M* F4 a4 ~6 y
- 179 /* platform驱动结构体 */
, d5 G9 r3 {7 D2 K5 U5 w* n9 U* J - 180 static struct platform_driver led_driver = {
( z2 e q4 Q0 ^; g - 181 .driver = {( k6 N5 w: t8 X/ w
- 182 .name = "imx6ul-led", /* 驱动名字,用于和设备匹配 */* o# Y7 U; z+ H! Y' ?& b8 C- E
- 183 .of_match_table = led_of_match, /* 设备树匹配表 */5 i. C* K* J: v# _$ |8 X5 D6 x
- 184 },
; y, f$ U! V* n' D, {& ~+ n - 185 .probe = led_probe,
[ }5 Y% Z2 F: s - 186 .remove = led_remove,% P3 ?& b7 l3 `: c, e
- 187 };1 p1 y5 W2 o# P
- 188
8 f* C! l, j; L. ?4 M - 189 /*
; C+ n6 v' E7 l* w( s% S - 190 * @description : 驱动模块加载函数* O6 f' a8 \- ?* f3 e! w
- 191 * @param : 无. M1 f. T4 ~& o) R
- 192 * @return : 无
+ N% F/ e" o. X - 193 */
9 n0 N6 X4 W0 L' B" L1 T - 194 static int __init leddriver_init(void)( p, b* X+ n* I
- 195 {
0 l0 v# A/ s" v0 r2 S - 196 return platform_driver_register(&led_driver);
{5 ~/ ? v" d5 N0 r5 I - 197 }
0 m* i1 f2 f$ g6 |9 j - 198 ( n* Z# T( D7 E" c/ s
- 199 /*
. I2 j4 n. M6 w$ K - 200 * @description : 驱动模块卸载函数
) F2 v" Q( s% A) a4 K - 201 * @param : 无; F3 S9 v) ~ n6 `) B2 W$ ~
- 202 * @return : 无$ A) i$ `& a% ~* [
- 203 */2 d! j0 V. k/ S% b0 J
- 204 static void __exit leddriver_exit(void)
& R% F; }% j/ P - 205 {
1 a' @0 C- h z+ \0 B - 206 platform_driver_unregister(&led_driver);
9 s. g$ w1 {& n% ?9 P2 p1 |% r+ ^ - 207 }
0 x0 k( r4 q$ f$ j/ w - 208 8 A2 n, Z% f. f6 r7 g- x) s
- 209 module_init(leddriver_init);
2 u3 m! n( M) w) Z: s: T: q - 210 module_exit(leddriver_exit);
2 ~. C" L+ b9 W, }7 P2 p8 h. B7 y) D - 211 MODULE_LICENSE("GPL");8 O$ L. ]1 N3 w. G: ~& U$ C: m
- 212 MODULE_AUTHOR("topeet");
1 r; n' f! z: |, |8 i( }* X( b, \9 z8 ~
复制代码
) D8 \( H: J" Z' D8 e* y8 f第 174~177 行,匹配表,描述了此驱动都和什么样的设备匹配,第 175 行添加了一条值为"gpioled"的 compatible 属性值,当设备树中某个设备节点的 compatible 属性值也为 “gpioled”的时候就会与此驱动匹配。
: T5 ~/ t+ n1 V: t$ i第 180~187 行,platform_driver 驱动结构体,182 行设置这个 platform 驱动的名字为“imx6ul-led”,因此,当驱动加载成功以后就会在/sys/bus/platform/drivers/目录下存在一个名为“imx6u-led”的文件。 第 183 行设置 of_match_table 为上面的 led_of_match。 3 应用测试程序应用测试程序直接使用上一章44.3.2的led_test.c即可。 ![]() 4 h5 ?8 f1 V# l' V3 [
|