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

Linux字符设备驱动模版

[复制链接]

该用户从未签到

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

EDA365欢迎您登录!

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

x
本文将给出Linux字符设备驱动模版。大家可以根据实际需求修改本模版。
1 w: Q* N. Z+ r: Q9 |1 Q, l7 H* }" T4 z  d1 Z# d5 w% M
驱动名为hello,内部提供一个简单的buffer,用户可以调用write向设备写入数据,并调用read从设备读出数据。
1 R/ e- z8 Q! R2 q( s* c# l) g请注意:1. 若连续调用N次write,buffer只会保留最后一次write的数据。  B6 }4 g7 o! ]; F
             2. 每次read都将清空buffer。因此,必须先写write设备,再read设备。
3 h9 n2 X, [% X: c                 3. 目前驱动只能有一个进程访问,允许多个线程访问,并且多个线程共享一个buffer。
; ]0 Q: m" F0 t3 J0 J1 n, d1 L) s9 r8 g" p4 M) p8 \
下面的驱动代码适用于内核版本2.6.32,未在其他内核版本上经过测试。1 Q/ l' v$ X" r! |8 [. }
#include
# N) T2 g$ O6 l#include
. T3 k4 M* X& K#include
, O# t$ i$ {$ g7 L#include
( G8 o- W9 E" C) s#include ! o0 w( X: |% I: N! K
#define DEBUG
% V1 g$ a9 x) j/ _* V4 O#include + w/ l, A' k- {: F8 h
#include
3 w8 X/ {1 K9 O) j4 ?. e#include 0 i4 b" ]3 p; k: w9 N8 j9 {: ^
#include  
! z0 H# ]6 t$ G4 B% |#include
+ b# |; Y* R( K5 {0 g3 o; Y1 |0 E
#define DEVICE_NAME "hello"$ e' r8 _  G+ q. o* ~. S; U
! R; u& Y) I; k$ Z* q- o
static unsigned bufsize = 512;
4 _" F, e" D. q0 W4 Y" Wmodule_param(bufsize, uint, S_IRUGO);0 @; U: u+ ~9 ?  K4 J
MODULE_PARM_DESC(bufsize, "data bytes in biggest supported hello buffer");8 c% r) M, x$ l" @* y5 g

3 g* K- v$ d# ~6 e9 h$ ]struct hello_data{9 W* p. v1 j) e* r5 p5 U
        char *name;, R. V# n% K6 {; y4 }0 w0 @
        struct cdev chrdev;
7 `/ k1 i8 v0 t+ E' i$ a        struct class *cls;
) U/ G' Q" C0 u. O2 W- {        dev_t devt;
- T* ^& v* Z. U) M& P        struct device *dev;5 b: d9 l3 f- V1 C

3 n7 K) v" @1 A; H* A2 @        atomic_t available;9 b! v; p9 J$ j9 Z
        struct mutex mlock;  /* Protect @buf */
9 O9 h. k+ G  g6 S/ ~        char *buf;8 k' x+ B) a' i2 P! F6 T
        size_t bufsize;" @5 T# f: E/ N0 C4 Y6 G. d1 ~
        size_t actual_len;
9 @$ m; ~2 h7 j% {2 ?};
" E4 x" `# O, H, R$ u& J1 Z( b1 q% k
static struct hello_data * hello;
6 Z, L7 j9 M/ z* o/ O& O% y4 d6 ^5 K% ?
static int hello_open(struct inode *inode, struct file *file)
! W7 b  Q6 w0 e4 ~% G, [' T3 D{
( n- s0 q3 t: R- I& D        struct hello_data *hello = container_of(inode->i_cdev, struct hello_data, chrdev);
8 v# A. S* f. q$ {8 K
( Q9 E  \- w5 }9 @+ W# I1 g        /* Only ONE user can open the device. */
! J5 L* _: [6 E" T. }0 y        if(!atomic_dec_and_test(&hello->available)){5 j# V4 V5 j! q, x$ P. V8 v
                atomic_inc(&hello->available);
! r% I/ v1 f) W, b* L0 r) i) `                return -EBUSY;6 Z. X+ |( U0 n0 o. \1 L2 h
        }
3 L$ n; o0 e& ~% k: @. ~) ^$ M; J( F0 d% R2 a4 k7 m( U
        dev_dbg(hello->dev, "Hello open\n");# q. A/ A3 a; v9 e( `  q
' r5 `8 Q6 |/ s, s
        if(!strcmp(hello->name, DEVICE_NAME))
8 o$ j, a8 ?7 S/ ?/ h2 {                dev_dbg(hello->dev, "This is hello\n");
9 @4 ]2 F* B' S3 @
* C7 A  c- Y' R& o; a3 ?$ l) Y2 F' a        file->private_data = hello;
- ^7 e# o% |! k+ B! C+ }( m( |! v% O
        return nonseekable_open(inode, file);        
& r+ L) X" k1 _5 I3 T+ Z}
3 t! R6 y6 x: K! Q  n, `& ~
4 ?+ ^4 l4 R; vstatic int hello_close(struct inode *inode, struct file *file)/ m' s$ `5 V% s
{6 {4 b' x1 R6 Q- G; W
        struct hello_data *hello = file->private_data;
6 E2 `, C4 J+ w9 x& K4 g  _! o+ s; G0 B  _2 R8 B
        dev_dbg(hello->dev, "Hello close\n");
- {) z( v" e2 k* f$ P: t- j       
7 u$ `; b; _  Z: @- z1 c' ~        file->private_data = NULL;' [6 m! m0 K! h( E
        atomic_inc(&hello->available);9 @! f- P: _; N+ G1 w7 j0 |
. |  k2 n8 M. t, I+ I. I8 B
        return 0;. \9 M& z7 K) A( e/ |: W( g$ k
}
9 R5 ]# z- y' v$ p* ]' @, d- L$ w0 Z1 u0 K4 v* ^6 ]8 m
static int  hello_read(struct file *file, char __user *buf, size_t count, loff_t *offp)5 P% `( J5 @6 v' S
{
2 I6 J& h3 T  {3 R: M1 ]7 z        struct hello_data *hello = file->private_data;
7 v0 J% V& O7 |6 Q9 r% ?* S7 f        unsigned long ret;0 V' E" h1 p; c; s
        u64 len;6 ?& A$ b% f- h+ I8 m9 f) K  P
/ s9 m% i. f8 Y( g
        if(hello->actual_len == 0 || count > hello->actual_len)" o- H1 `* s6 W  ?
                return -EINVAL;1 [/ O4 u1 t: g! k. S/ q  x4 M
, b1 v- x% C/ K3 l7 s& A
        len = min(count, hello->actual_len);
9 X+ {: a* l: J; Y3 u) ^; l        mutex_lock(&hello->mlock);! i- S7 u) N! r
        ret = copy_to_user(buf, (void *)hello->buf, len);
3 x* f9 i# m+ U& F, m1 K        if(ret < 0){
$ Z, n/ n8 o; A; \5 _                ret = -EFAULT;
: n3 Z# o& D3 _/ l                goto out;
2 }5 y+ Y$ o: C+ B$ s! R6 ^9 {        }
& ?: e( w3 Y0 Z* e5 c/ L  G! ~" i# L- `: S) H8 h* y- m
        hello->actual_len = 0;
% v, r. a+ |' j$ D9 X6 j. t+ [        ret = len;
2 e, E# ]; n4 J- D* B7 C' v- B) l# A' n- I% x; b, l
out:9 M' s8 J" I$ S( ^: Y8 i) u
        mutex_unlock(&hello->mlock);
2 x' c# i9 Q2 I% F! k        return ret;
2 u2 ^; O  a- \2 C2 J+ Y}
3 K3 p1 {2 ~6 ~" x$ S+ ?
$ G* p$ a9 h( @* d% J1 \static int  hello_write(struct file *file, const char __user *buf, size_t count, loff_t *offp)+ t) ?  X4 h( {! b) y! Z
{7 N. ?% g9 W: w) W
        struct hello_data *hello = file->private_data;) j1 Z5 u: F3 C, g0 s' n) g
        unsigned long ret;6 [: k6 X6 l. c' U) `+ V4 M
        u64 len;+ b  ^( ]3 u5 U- B2 u, E

) Q1 \* r+ P8 I; V# ?, n        if(count > hello->bufsize)8 i$ e, e" Y1 d' c1 w
                return -EINVAL;, r$ _  B- [6 ?. _0 s+ ]

* E$ S, j7 t5 P) Y0 H% h+ i% I( G" s        len = min(hello->bufsize, count);! L; v- i2 J$ p  q& u1 }. X* ?5 t
3 J& ]- u! G6 ~& W& W' F' G8 w2 M6 L$ e
        mutex_lock(&hello->mlock);7 ^1 M# J4 F9 x' {) _
        ret = copy_from_user(hello->buf, buf, len);0 B7 S8 ?( [9 q% F- n4 L
        if(ret < 0){" d. y$ D, l: u/ B
                ret = -EFAULT;
* K' d  |# P) n; b5 e# C/ S  l                goto out;
3 X5 @. ~; v1 m; g1 D        }
' q' U: ^$ I9 L9 Q! V; Y! T# `, x
        hello->actual_len = len;
9 Z! C3 m  {. ~* S1 i% V5 \        ret = len;
9 f2 o/ W. c6 W2 g1 ~. M" ?out:6 E+ h7 z  d) a) o1 t# l
        mutex_unlock(&hello->mlock);
! J$ }) k  B  S        return ret;
- S8 n! ^( j: ?% ^}
6 {" b  T1 c+ \5 L+ S$ Z7 D1 S( x5 @
* V( y; l9 @: u8 Astatic struct file_operations hello_fops =3 j! f1 e* h& D# x5 l- j
{( S& Z+ s' ^) B
        .owner        = THIS_MODULE,
. m0 f; O- R) L5 B6 y: p: a        .open   = hello_open,
" r" v& l! o/ F7 x1 T2 |6 K& e+ ]        .release= hello_close,
+ v2 N; ~; Y# J' f3 M7 z        .read         = hello_read,* X: p3 ?4 G, A. E6 w( ^( ^5 i
        .write         = hello_write,& j- s8 m3 M% I; l) _
        .llseek = no_llseek,
: p3 |/ c; U1 U* ~9 L};( B% g( s5 [! }1 ]* l9 q6 Z$ P
8 l2 }8 O+ |$ b# Y
static int __init hello_init(void)
  e1 h: B  y& D0 X, b( ~{
  G8 @) H! t" |5 T: t- d/ r, g+ W        int ret;
$ Y- l! X/ |! Y3 S( `+ |7 I/ j- H3 W7 [7 f, ~& j& W1 i8 g0 k( t
        hello = kmalloc(sizeof(struct hello_data), GFP_KERNEL);) R+ D! A1 V9 m; C2 C( a
        if(hello == NULL){! @1 p1 R6 }4 u4 V
                printk(KERN_ERR "Unable to malloc struct hello_data\n");% r9 y0 [+ m2 z
                return -ENOMEM;
* s9 h8 E. _6 l# R1 E. u        }. j9 [- D* M( \# t  y; Y+ ]
        " q, Y$ ^5 H- e* [
        hello->name = DEVICE_NAME;; T! ~! f) R: @1 a# A. f
        mutex_init(&hello->mlock);
9 E4 @) G1 K! W( c9 Y        atomic_set(&hello->available, 1);
$ O( B* P3 J; S9 _# Q, j2 d5 Y, h0 B( z. |) l
        hello->buf = kmalloc(bufsize, GFP_KERNEL);
9 a7 k9 n% i( {" o! q        if(hello->buf == NULL){1 d/ v* d/ i& d: e; X) R, G1 ~& g
                printk(KERN_ERR "Unable to malloc buf\n");. P% @, S" L2 n
                ret = -ENOMEM;# L9 e6 y8 Z: S6 J) A" {5 R2 n5 P
                goto err_no_mem;
5 M! Y9 k& _6 f# c1 l% }$ y        }2 O! ^# `8 d+ ?' i. @  J7 P$ T
        hello->bufsize = bufsize;
; A' P2 L$ ]1 l. l6 J- |8 z# T        printk(KERN_DEBUG "Buffer size: %d \n", hello->bufsize);- Q  M0 D8 b. y. ]/ h

- \* j9 p$ m/ Y) l+ X        /* Alloc the device number dynamically which is stored in the first parameter */
6 E: v3 S/ p6 H- m/ g4 N0 q            ret = alloc_chrdev_region(&hello->devt, 0, 1, DEVICE_NAME);% U7 m; C8 H9 A( h0 |0 n+ V
        if(ret < 0){
0 t+ n2 j4 L! s! i                printk("Alloc chrdev region error\n");
8 e& j& l; M8 h3 Q$ H. A% S5 n                goto error_alloc_region;
0 K; ]( j" `! W  y& I& [        }( l1 U" I" P9 s: A' f
        printk(KERN_DEBUG "Major = %d, minor = %d\n", MAJOR(hello->devt), MINOR(hello->devt));8 _) j& n  W+ `. q! j3 d& Y- H

- |6 T9 m  s+ f$ [$ a        cdev_init(&hello->chrdev, &hello_fops);
8 K9 O1 \% _$ S, e8 Z, w        hello->chrdev.owner = THIS_MODULE;
, ^# E9 L4 A( _7 c- Y' v; Q: B( G        hello->chrdev.ops = &hello_fops;) k6 E1 N" f3 H$ y. D2 `
* ]# M* p  J/ S/ ~7 i2 _2 }3 L
        ret = cdev_add(&hello->chrdev, hello->devt, 1);2 B9 K, N7 r: P$ H, b0 h
        if(ret < 0){- c8 }6 I" B; m
                printk(KERN_ERR "Cdev add error\n");; @4 n3 J1 |( R7 Q
                goto error_cdev_add;
# U& X) a6 U/ B+ O        }
+ V1 ]- ^% x1 |: m  ?0 B3 N1 D( M
0 R/ h9 z  ?' L) w        hello->cls = class_create(THIS_MODULE, DEVICE_NAME);( ^0 W, W* b6 r/ i. J; [, J
        if(IS_ERR(hello->cls)){. ]4 h( A: x; k6 `; a
                printk(KERN_ERR "Class create error\n");
: c2 t3 ~: b# e3 K0 k$ P1 D/ c                ret = -1;! {/ i1 Z# H5 L4 t0 u( M
                goto error_class_create;0 M( V0 Z% H# p1 j- B
        }' H; {* `8 t- \- L

, @% G& U- I' g        hello->dev = device_create(hello->cls, NULL, hello->devt, NULL, DEVICE_NAME);- O9 D) q7 b  H
        if(IS_ERR(hello->dev)){' u3 x4 b- w+ f
                printk(KERN_ERR "Create device error\n");( C; `1 D3 K5 u& J1 u4 O/ E
                ret = -1;+ l7 x, E: I1 s# u
                goto error_device_create;* J# w7 W$ I  J/ v
        }; ^. B1 l0 T8 W+ U% Y0 k4 b% {, i
5 ]5 G% k! }+ x$ }7 K
        return 0;. |8 A. |$ B: D6 @2 ^/ ^
) u7 C3 ~! a) W
error_device_create:# o7 Z1 Q4 ~5 Q4 Y  ^
error_class_create:
" J- X$ t9 D0 b) h        cdev_del(&hello->chrdev);
& {! v( d. y& J9 r5 I7 D! H* t/ U* ?$ s! X, E$ M% a% R- v& \
error_cdev_add:
$ F8 q3 ~" |$ K0 K8 e        unregister_chrdev_region(hello->devt, 1);
- f- o) S2 Z: G! L' g
% I: b; Q5 C# q/ c/ A" }error_alloc_region:- E; i8 X. s" ^2 d; u. U; J) C2 o
        kfree(hello->buf);1 Z% a, O0 \3 G& G5 P* A% e
) u- V! G) v* s: d3 e0 k
err_no_mem:* V* k& b/ ]+ x: W: y
        kfree(hello);9 q1 b, k  W0 F8 T

9 s- O1 k4 }5 D: u4 U' m8 l        return ret;
- ^/ H% Y5 |; x; p5 I1 i}! `! a0 b# M( c

. C5 m) d0 t6 {! q' |static void __exit hello_exit(void)
* n) S! ?2 y' j; h3 h{& V# M7 s( ~+ p6 t# Z  B( a
        dev_dbg(hello->dev, "Hello world exits\n");) V' d8 j- V9 ?& X  S+ ^

5 z9 o0 b3 k5 R3 u. r        cdev_del(&hello->chrdev);% I4 ], e  P' Z
        unregister_chrdev_region(hello->devt, 1);       
( F$ b; S/ m8 D, O5 l        device_destroy(hello->cls, hello->devt);
/ p( Q  ]/ D2 o        class_destroy(hello->cls);2 M4 h7 }' ?8 J* T) G6 E: @" w0 O
        kfree(hello->buf);# O- N! r& b% q! k7 k/ u& g
        kfree(hello);
2 p( }  M+ o) k( g  i1 a& A# X} ( X' P( g, C) k: D8 V8 [# V

1 K- g3 A$ \2 x% T+ i* `module_init(hello_init);
& r0 n: m% P& E# Vmodule_exit(hello_exit);/ H1 n1 t( v/ E9 O2 l  f

7 k5 k( q: q4 I0 Y1 ~( fMODULE_LICENSE("GPL");
. s* q7 |& u/ rMODULE_AUTHOR("yj4231@hotmail.com");1 B  a. c) W7 f; h3 S. G$ |
MODULE_DESCRIPTION("Hello template driver");
% B! s' Y' b/ Z) @. ~
7 U3 o+ _" |0 p7 m0 N" `' e( x, h* [: [0 w4 e5 K. H- `# x; b
对应的测试程序如下:2 h& ^1 d6 M3 r$ _5 R  p; R$ c
#include
2 \4 h5 W- \: V( @3 j9 u#include 6 J. X; ^' a( t
#include
4 E" O- ?6 c4 Q' W# j#include 8 J4 ]8 c0 f) W' A0 h  Y8 t
#include
% Z2 f* n7 @: V) V# G" x#include
' z! d. ^6 B% F( A/ |: `#include
7 S' L1 S( K5 _! C" S
4 p0 \3 Y3 k, }' W+ q' Qint main(int argc, char **argv)
0 b$ E" _7 U6 X8 b) D0 B{
* M& z6 A8 a3 W' u# n5 a        int fd, ret;
, p1 ^1 {# o( b" }" B! s        char data[512+1] = "Hello test";
+ P* P  j: T8 }+ p        char buf[512+1];
! `' w7 k. \2 g2 W9 y        int n;
: D2 D( x& y' \9 q" E2 b0 F) n1 M
        fd = open("/dev/hello", O_RDWR);6 ]* T: m+ l- h3 G3 b0 Q, M
        if(fd < 0){
) a- L: N$ x# j                perror("Open device fail");
  m, X6 O# V# x: h                return -1;
, ], w0 f2 \% T        }
, k' z" R0 L, R9 L) {7 S, \, t$ G9 ]& l8 q& r/ i; K' ]% M0 e0 D
        n = strlen(data);
3 x% |# q4 ]1 F( s, E4 e; q+ H; @        ret = write(fd, data, n);
! i+ Z& X+ b6 \: v# p: B        if(ret != n){
+ i/ H7 _, v9 z                printf("Write failed\n");" i; W$ W6 b* v* n
                exit(-1);
; h) @" ~) e% F0 J        }
* ]- x0 I2 W* z' W! b! i        printf("Write retval: %d\n", ret);8 Z$ z2 p: I, R/ C/ ~% J

+ @0 a( k' l3 M& `) w        ret = read(fd, buf, n);8 u+ O: [. R3 K
        if(ret != n){
6 O# }0 _" B2 Y+ I4 f, o6 G! @                printf("Read failed\n");- e! j* D- ~% n& _
                exit(-1);
3 o6 @$ ?- X5 \* G, S8 z8 G        }
. v0 b7 q, h1 X8 K% m* D        buf[n] = '\0';, d* E) T8 \' D1 d$ ?4 K9 w, @
) U% ^6 h+ I4 y+ l8 P( a. P
        printf("Read retval %d, %s\n", ret, buf);/ V. g, b) [0 L' ]
        sleep(1);
7 z7 Y) c, [8 G! d/ p- V        close(fd);
6 ]6 D6 d2 H% i4 k}$ V% x$ r/ H- j* X

% D. z* Q6 n- [3 z# h5 F2 _' H! E; N) z4 Z" T. t1 \
5 X* c# a, S+ ?0 e2 z
History:8 a7 @2 X$ z' M, Q: j
2014.02.15  单进程,多线程共享buffer版本(2.6.32)。
( \/ |3 ]3 t( X0 m
, P/ p  W7 R5 _1 i. L5 ]. Q3 U% o  ?1 a- J2 {
2 j0 w+ }; h( M$ v( F' C# Y. |4 y

该用户从未签到

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

本版积分规则

关闭

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

EDA365公众号

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

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

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

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

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