找回密码
 注册
关于网站域名变更的通知
查看: 226|回复: 1
打印 上一主题 下一主题

Linux字符设备驱动模版

[复制链接]

该用户从未签到

跳转到指定楼层
1#
发表于 2020-5-29 09:31 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

EDA365欢迎您登录!

您需要 登录 才可以下载或查看,没有帐号?注册

x
本文将给出Linux字符设备驱动模版。大家可以根据实际需求修改本模版。
  _3 J: @1 P3 U* e% Q- N: K
! k( ]( ?+ W" n- n  Z- g1 O5 C7 s驱动名为hello,内部提供一个简单的buffer,用户可以调用write向设备写入数据,并调用read从设备读出数据。
8 z2 w& ?/ b# a" ?% B& c请注意:1. 若连续调用N次write,buffer只会保留最后一次write的数据。
- Q  `% P2 g: @& O; G% `             2. 每次read都将清空buffer。因此,必须先写write设备,再read设备。
- s) T! }& V3 b+ D4 s/ G4 ^                 3. 目前驱动只能有一个进程访问,允许多个线程访问,并且多个线程共享一个buffer。1 \2 B( d( b" G) D9 f
4 D- ?: s# R( H& k4 P8 s5 d$ u
下面的驱动代码适用于内核版本2.6.32,未在其他内核版本上经过测试。
4 I$ k% U4 v* b  n- \2 K#include   T1 G9 q: @8 b/ v( ?
#include ; c# z2 f& o  ^3 C& B: V
#include
" l4 X  D$ [' v2 v7 K+ m#include
7 ]" s1 N8 _* a9 K8 K# ?; X; b# M  t#include   r# c& M/ O8 C
#define DEBUG
. c- `8 ^9 y4 y8 x9 m#include
: g2 V* V! r+ T& m$ Y# k- l#include 3 _4 \; L6 P8 z& Y
#include 1 k5 M. K/ W( k8 B
#include  : O" V" {8 m0 l3 A
#include
2 g- d% V: z2 R) L8 R# x3 V
* b6 K2 f! x" N! |7 G# w4 Q#define DEVICE_NAME "hello"
6 r6 x3 ?% I, m0 ]  r, U: v  R( [: ?( `" H+ c
static unsigned bufsize = 512;5 F. P9 {7 q' _% |# f; P
module_param(bufsize, uint, S_IRUGO);, u5 [8 |. z. F
MODULE_PARM_DESC(bufsize, "data bytes in biggest supported hello buffer");% q. K0 Z- U' [
( L0 J6 D6 m7 c% \$ K  u" o
struct hello_data{. @# ^1 Z7 q% q; ^
        char *name;
3 ^, L* e* t7 r# e9 c6 z* D        struct cdev chrdev;* U+ S! n: L, J9 ~; v& g+ g- d1 u
        struct class *cls;
4 \: {0 s9 O0 H$ N+ d8 a4 k        dev_t devt;
. h7 s/ t' R0 j9 }        struct device *dev;3 A& I; O2 j: E) s2 v

& P4 y3 O& R6 \* w  Q8 U7 h        atomic_t available;( x8 _: H7 C. J. k, N
        struct mutex mlock;  /* Protect @buf */. y' G" p8 M1 r, j
        char *buf;/ Q+ g8 R1 s; ^: d7 u8 B% n4 z
        size_t bufsize;
" }7 X, N1 o% h* h4 f' u        size_t actual_len;2 V! Q0 Y9 m1 G, a6 m& G
};  s, d- X* F* x; ?/ h; {

6 H1 C+ c- h# g! Estatic struct hello_data * hello;
4 B) z$ f( B3 @4 S
: |0 K: w1 `7 @( L" M1 N) D# Rstatic int hello_open(struct inode *inode, struct file *file)
- o/ p. \% V3 K{3 l; Z" X' Q, E+ \
        struct hello_data *hello = container_of(inode->i_cdev, struct hello_data, chrdev);
, X( q6 [9 G; V3 @* {) a* g5 @9 X8 B" ^! O
        /* Only ONE user can open the device. */
2 Z! g) G. e( b0 c" Y: J        if(!atomic_dec_and_test(&hello->available)){
% Y8 P. `* J  @/ ]                atomic_inc(&hello->available);3 U& s( Z7 p  O0 ^1 S/ Y; R
                return -EBUSY;
. m2 p$ F9 s9 J        }: m! X0 `& v2 M6 U/ V
/ [( H: Z% S, r! w5 x# n; }
        dev_dbg(hello->dev, "Hello open\n");8 X; P* n* Y2 M8 w4 ?! k& p1 f
+ M4 n( l/ L- r& M
        if(!strcmp(hello->name, DEVICE_NAME))
# v' \2 @; L8 `" ]/ W! Q+ o                dev_dbg(hello->dev, "This is hello\n");
9 ?6 a6 b- A1 V$ V/ O/ K+ L
; l1 ]: J+ e- Q5 c1 {+ `, w% N        file->private_data = hello;! R, I. n5 j) B3 F5 F5 y# w* Q

7 b' M& @9 e" [0 P. |* F4 Z        return nonseekable_open(inode, file);         ) `; Q; m# g4 a) J/ o
}
3 F, g% z4 ~: j1 M, ]
4 I, U$ N% l- _: \" nstatic int hello_close(struct inode *inode, struct file *file)& x* n4 d- t: z7 A4 ^( _* @0 C
{# h+ A# Y7 o' y+ i- @( z! d3 q
        struct hello_data *hello = file->private_data;
/ H0 U  B. M9 X, Z  Y- R) d( w. }9 t
        dev_dbg(hello->dev, "Hello close\n");% t" y5 L) l; F$ R3 c; x
        + K& E& `/ r) i+ r
        file->private_data = NULL;" K" f$ C5 P3 g
        atomic_inc(&hello->available);
/ A3 Q' Z4 e! l9 D6 O7 b( D) r- ?  ~/ O/ |
        return 0;
4 }0 h" Y) q, S: O7 r$ j}9 K! X6 C, [& K7 {) C) S
& h* t# L  c3 L3 z9 `( h# x
static int  hello_read(struct file *file, char __user *buf, size_t count, loff_t *offp)
# T) O1 e8 h  q$ S1 f{
  N  @, M- o' u( K8 C$ D        struct hello_data *hello = file->private_data;
( K9 y6 p5 `, z. |        unsigned long ret;* c! {2 H$ }0 @2 @* X$ J0 i
        u64 len;' H$ @* n1 u5 \7 v0 h. M1 a; v

6 W+ V$ D) Z; |        if(hello->actual_len == 0 || count > hello->actual_len)
  y4 |6 O9 U; o' K                return -EINVAL;
2 ]# r! Z; ]. s# J2 m! S) l; Q$ h: O! f
3 ]7 r2 q; m: n  ~        len = min(count, hello->actual_len);
; s% S" V4 R# A" j5 k+ D, Q2 F0 h        mutex_lock(&hello->mlock);, ?- p! Z' [* d' S6 o+ [* ~2 _
        ret = copy_to_user(buf, (void *)hello->buf, len);) a+ T$ R& ]# ?' O9 v, O
        if(ret < 0){/ t" R9 Q3 j6 E5 v( \
                ret = -EFAULT;1 F2 N' K( u' ~4 ~
                goto out;% a3 d9 T' ]+ H; L5 f/ Z: A7 o/ w' _
        }; \9 t( _/ @  |# Z( |7 w, k4 g

* u0 ?# V7 S9 o- K  r# h        hello->actual_len = 0;
. \+ k! ]4 v* c  C  @- s        ret = len;0 q4 b7 e( L, u9 r; ^+ F

" M2 l9 Z4 O! ~6 iout:
+ ^# p0 S: F. f5 {9 ~        mutex_unlock(&hello->mlock);
( A5 F5 X2 x, ^; `        return ret;# c) ^: H- y9 P' G; _
}# N$ f; E9 @8 d( J
# h  h' Y. Y% O- d7 L
static int  hello_write(struct file *file, const char __user *buf, size_t count, loff_t *offp)
+ f' `! z3 v  h, A& ]$ k{! X' w+ M& Y4 _7 V' d( a9 P/ d
        struct hello_data *hello = file->private_data;
9 C) o) p0 s6 m        unsigned long ret;+ x+ ^; |( R  ]1 O3 L/ U# w8 _, [
        u64 len;% _( u- H7 y2 x) Z( v$ X

' e. v6 r% Z$ w( B" U        if(count > hello->bufsize); f4 y- p9 ^1 i# o4 O- l& j$ `% h
                return -EINVAL;1 [. |+ {  N) t) Q

: N, B2 `: a, o5 P0 N! z        len = min(hello->bufsize, count);7 n( j' `5 M2 f% n8 \- _; E8 ?
8 h, J4 Y' D) X! ^" \
        mutex_lock(&hello->mlock);- n  {7 l$ f$ H/ Q- I
        ret = copy_from_user(hello->buf, buf, len);
9 ^5 x* y$ y/ E& q! `        if(ret < 0){
% |4 x9 P# s) j0 G) M7 N                ret = -EFAULT;
. H) F/ M; |) n+ [3 ]                goto out;
3 }/ O( M4 r8 o+ a        }* E, z' j4 G/ _7 s

( r% o2 Y% M; L        hello->actual_len = len;8 V7 ^6 x4 R& {7 J
        ret = len;
% G+ ~5 p0 k8 e4 g. A' Dout:6 F4 B4 b! q8 A( z( e/ C0 j
        mutex_unlock(&hello->mlock);. k2 l/ Z6 u! C8 ]; q
        return ret;; S) b% [  o5 \
}! E8 V2 `8 {! x/ Z0 s- G) h
: [; K, X; f8 Y2 ^# d& T
static struct file_operations hello_fops =
: k; ^# h( b2 ]{
' j( V% R. k! ]+ F" V3 ]        .owner        = THIS_MODULE,
6 `9 x3 h6 c9 _        .open   = hello_open,
: ]& k1 g1 {. _# a        .release= hello_close,3 E9 M2 a: ?+ A' d
        .read         = hello_read,
; j; E2 d  q+ `. i0 [        .write         = hello_write,/ Z( m0 i7 L* G2 ~" k8 m
        .llseek = no_llseek,: h+ T: Y+ w* q6 c: N
};
6 U, w; O$ C7 L$ j3 B4 N% B& a: U: u( Q1 n' N' f  O3 c
static int __init hello_init(void)
3 }  O# o& Y- Z5 R+ n) p/ U{  N9 r6 J; w, x1 H- O
        int ret;
6 s4 o/ Z* ~1 E% ^8 ?. B  y5 _/ a- H0 m
        hello = kmalloc(sizeof(struct hello_data), GFP_KERNEL);
9 n7 x8 X, ~0 E. [        if(hello == NULL){
/ P$ ?8 e  F! j- }+ r3 [6 C                printk(KERN_ERR "Unable to malloc struct hello_data\n");
6 k: \4 s. b! a# H: @                return -ENOMEM;
9 m  O7 c  G/ Z: F0 x& `8 y, G4 K1 e        }6 Q9 q8 U: B7 S5 V
        ( N' p  d+ Q0 ]0 e+ R4 L
        hello->name = DEVICE_NAME;9 `% D1 L# X( C, R1 {% A, l+ [2 M
        mutex_init(&hello->mlock);
, B0 w) H  Y9 |+ p" P+ [2 W        atomic_set(&hello->available, 1);
& c" X; Z+ \( F& y% x8 G* v
$ g& K+ r/ x- w1 Q9 R' ^: U3 N; E        hello->buf = kmalloc(bufsize, GFP_KERNEL);- z: s7 N5 L$ G6 t2 {: H$ z
        if(hello->buf == NULL){) C. z+ J) p% h2 [) {& U, C' f6 V
                printk(KERN_ERR "Unable to malloc buf\n");
3 U" L! p: O3 X* q, \                ret = -ENOMEM;
  K  U5 M6 l3 @- V                goto err_no_mem;. s! }5 B# j* B. ~/ L
        }; i+ j9 h. w6 j6 _) C7 ^- E
        hello->bufsize = bufsize;
/ L5 V, D1 T4 z; i5 Z        printk(KERN_DEBUG "Buffer size: %d \n", hello->bufsize);% L( b6 y6 n' {3 z9 m

0 w1 F  r. I! z( T9 H( q) p2 \1 G        /* Alloc the device number dynamically which is stored in the first parameter */
1 Q  J+ V9 y9 }5 b3 e' K            ret = alloc_chrdev_region(&hello->devt, 0, 1, DEVICE_NAME);
7 K: |3 y, f6 |        if(ret < 0){" |; {% `! N# C9 _) N+ a6 D8 f8 p
                printk("Alloc chrdev region error\n");; z7 x/ F" i6 h8 s$ _
                goto error_alloc_region;" K3 C% j9 a8 R, T# z' ^& L
        }
" M( f9 Y; V) Z3 B& C3 b5 @+ [        printk(KERN_DEBUG "Major = %d, minor = %d\n", MAJOR(hello->devt), MINOR(hello->devt));2 N0 p. X; g, i4 ]

( Z& @# J! y: x# `        cdev_init(&hello->chrdev, &hello_fops);/ J6 S' d% l4 ?0 N; C1 O0 ]
        hello->chrdev.owner = THIS_MODULE;
/ r9 D+ b* a- Y) T) d! Q        hello->chrdev.ops = &hello_fops;
) {( u% y0 ]$ R, k7 z- H
3 z: {2 F# {* {8 L        ret = cdev_add(&hello->chrdev, hello->devt, 1);
/ o7 L- X5 O8 T: S$ j! w$ t. y        if(ret < 0){! h. d+ \3 d2 E) z6 K( K
                printk(KERN_ERR "Cdev add error\n");  U0 P, x* v& p+ K/ q# }* W5 g; Z
                goto error_cdev_add;
9 {) {8 o( `8 O' N2 y        }
/ j, \  z- ]8 E" P( G4 ~9 [* I' F! X. a8 s2 I1 ]
        hello->cls = class_create(THIS_MODULE, DEVICE_NAME);' u/ ], O! n8 k2 @! c/ o
        if(IS_ERR(hello->cls)){/ e" K/ m/ P: L3 u3 N
                printk(KERN_ERR "Class create error\n");- }+ C5 F0 K2 i
                ret = -1;& N& M0 s& O, ~; T6 I$ O& N6 J$ l
                goto error_class_create;- h: {: k4 y- C, d# |
        }
/ n/ n8 \0 {6 c4 ^/ ?5 k1 R3 S' D  ~4 z
        hello->dev = device_create(hello->cls, NULL, hello->devt, NULL, DEVICE_NAME);3 B) {* \; f! ]& B
        if(IS_ERR(hello->dev)){9 A/ u+ D0 _. S- p/ m  l3 y
                printk(KERN_ERR "Create device error\n");
* R; v1 r: X6 l                ret = -1;
# b6 b* L7 h% p; T, v                goto error_device_create;
4 l  V3 h! o( L        }3 l2 {4 Y' q; u4 H
6 @" ?4 k3 p: {) ~% U  a+ U7 Z( |
        return 0;3 U- U# v7 g- N# d7 ?) R: e7 U' W

, t% y' R/ m& _+ |error_device_create:% V5 `8 A3 _* L* t6 {  |. v* T
error_class_create:! }# W' l, m4 X8 z" ]
        cdev_del(&hello->chrdev);
3 A- p9 P! A% A1 T4 z5 S& I. D1 k( j8 n
error_cdev_add:. N- P; n. f8 u) U$ m
        unregister_chrdev_region(hello->devt, 1);. ^$ x( h% d4 d
" n% ^6 d' g* V% }0 a) m4 F
error_alloc_region:/ K6 L" h: W. M: Y2 V# \
        kfree(hello->buf);0 R3 T9 U! F  z) a5 d3 |: N( P% W
% X! x2 T" h# C
err_no_mem:4 w& S) x) r' U1 h3 w
        kfree(hello);
- L: |+ z4 A7 Q5 Z& k6 A' G4 t; }$ R4 N. F
        return ret;
) n4 V; P* [4 }& s: C" w8 l/ G}
$ _. ]! u" Y0 ?9 B- I' F2 t9 `, P5 v: [- x8 z% \. `) Y
static void __exit hello_exit(void)
+ `1 Y4 G' p/ u+ y( P{( }9 i; H# h! S( [% V) @' y
        dev_dbg(hello->dev, "Hello world exits\n");4 }8 Z. C5 O; D9 e$ B  J; O! u6 K

% U9 {1 @* H0 S! D7 C7 L        cdev_del(&hello->chrdev);) w+ k) z2 x( T- A) J% z6 z
        unregister_chrdev_region(hello->devt, 1);        * C. Y+ V% w$ \) ^
        device_destroy(hello->cls, hello->devt);" R& |( X$ j2 S
        class_destroy(hello->cls);
- ~4 E8 p$ u  M+ Q2 U        kfree(hello->buf);
- U/ G! c+ D; j9 x' J        kfree(hello);- N$ ~; N' h, C" q+ R2 [
} 0 x1 q% H/ J* c: b, h+ x
: @) H- ^3 f* f3 @
module_init(hello_init);
* [/ @5 E/ P  D% o9 zmodule_exit(hello_exit);
% ?1 W. Q7 F2 [; x8 S5 ?# |  U. t1 E1 V
MODULE_LICENSE("GPL");; W  W1 S  D( r6 z& @$ i7 a
MODULE_AUTHOR("yj4231@hotmail.com");/ ?, G* p) v9 e$ f; Z
MODULE_DESCRIPTION("Hello template driver");
  T6 I% e4 X: X
% H+ M" G' T; u5 V. _& Z+ Z+ @! U) h" \6 C, e, O* y
对应的测试程序如下:
$ ~" V+ @) ~7 P+ Y6 M/ d/ L2 k#include
9 R' M/ t+ c9 g  i#include
% W9 z! L3 T5 t' x#include ) q7 T# B; x3 W: \& R# |0 K
#include
9 H4 n+ _4 ]8 T0 O#include   h. H( T1 ^* D1 G( o0 s* x6 O0 ^$ y
#include # t# S. V& B8 E1 x- J& q
#include
8 X( J4 C7 G( A$ p$ T! r$ A6 q
8 T+ |! Q6 U; R/ Y# J: dint main(int argc, char **argv): N! X, _% e) _/ C8 N
{9 J% i$ D  L/ Z2 o
        int fd, ret;5 }6 V3 T" X$ ?6 ?4 y+ F
        char data[512+1] = "Hello test";3 ~1 Y' G8 P7 n1 O3 X: Q
        char buf[512+1];) `, I2 H+ A% }
        int n;
. l5 ~7 A8 I. O7 n  C: {% O6 ^' N2 f% l$ o% l4 C. T
        fd = open("/dev/hello", O_RDWR);
" B2 G9 L! d2 `  J& j0 j; E$ v- P* H        if(fd < 0){
3 a% }5 c. ~3 j% _9 R5 P+ |8 p1 h                perror("Open device fail");
- [; _( Y- c5 ?8 K- L4 A                return -1;2 U& p# o& k/ o3 G
        }& y4 H  [* k  [  ^& U' s6 U6 l4 n
0 Z5 `; ]% _% w9 t
        n = strlen(data);
+ P7 f* G* k, P$ O3 w9 l        ret = write(fd, data, n);1 k0 q* v$ [: s' ^' I
        if(ret != n){$ ~2 ^3 q$ }/ P' M
                printf("Write failed\n");) }( R2 ?, m, F3 K9 Y: c7 l$ [
                exit(-1);
) D$ C5 C# K& h) M* v* G; r) u        }1 S9 D( s. f: P% W, H. d- n' a
        printf("Write retval: %d\n", ret);+ A; C' g9 t: a3 p; v- \4 |& b# E
7 d7 Y& E2 A9 O9 X
        ret = read(fd, buf, n);( o& ]7 u$ P% x2 S/ w
        if(ret != n){: r, [- M8 y2 H3 y
                printf("Read failed\n");- B: c. f! e. V/ K8 S! |  L2 W" G
                exit(-1);
" [0 h! r  t* L+ |2 D4 K2 @2 N        }- {7 Q* [2 R+ Y4 x; d
        buf[n] = '\0';
+ H4 b8 f3 Q  B) J4 f' d0 `3 Q7 P" J8 O$ b% H! A
        printf("Read retval %d, %s\n", ret, buf);/ j# G- K. [7 X- V2 v/ m8 o
        sleep(1);" a8 a3 {1 M& W4 H
        close(fd);
% |# s* ^4 |: O9 r}0 ^" \- Z. p. s+ j, V
" l" \  B0 H7 I% t

4 ~- Y' Y3 d, G5 {8 r7 g3 @. u
2 f- z" X. y4 Z4 U' r( {" E: N1 rHistory:
% u, A+ z( X8 i2014.02.15  单进程,多线程共享buffer版本(2.6.32)。0 C- V3 p; B3 g' x# V

) b' L7 C% k8 P; R0 W8 D6 O6 G2 o" L' z" r

5 G1 _" u( x, M8 C

该用户从未签到

2#
发表于 2020-5-29 11:02 | 只看该作者
Linux字符设备驱动模版
您需要登录后才可以回帖 登录 | 注册

本版积分规则

关闭

推荐内容上一条 /1 下一条

EDA365公众号

关于我们|手机版|EDA365电子论坛网 ( 粤ICP备18020198号-1 )

GMT+8, 2025-11-26 04:37 , Processed in 0.171875 second(s), 23 queries , Gzip On.

深圳市墨知创新科技有限公司

地址:深圳市南山区科技生态园2栋A座805 电话:19926409050

快速回复 返回顶部 返回列表