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

Linux字符设备驱动模版

[复制链接]

该用户从未签到

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

EDA365欢迎您登录!

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

x
本文将给出Linux字符设备驱动模版。大家可以根据实际需求修改本模版。6 o) e# u7 J( c: d9 [: g  w
8 K8 g  w8 _# Q
驱动名为hello,内部提供一个简单的buffer,用户可以调用write向设备写入数据,并调用read从设备读出数据。6 y  P4 A- I( Z
请注意:1. 若连续调用N次write,buffer只会保留最后一次write的数据。, l: x3 q) q8 H: q6 q8 j) H" Y
             2. 每次read都将清空buffer。因此,必须先写write设备,再read设备。9 f' Z1 n5 F8 ^+ }1 T  g! {# h
                 3. 目前驱动只能有一个进程访问,允许多个线程访问,并且多个线程共享一个buffer。
3 @6 }% i- c& L* t! x' K9 r, N$ U& p+ r% G. U
下面的驱动代码适用于内核版本2.6.32,未在其他内核版本上经过测试。
3 S' V4 W, q- X/ B#include
5 n% U. {: Y1 R- W#include
4 t1 I& R$ |' P; Z#include
! Q7 L# u+ T% s#include
% r$ ]. W3 K# U% ~+ Q* Z: _8 e#include
5 E; s- M9 [, _5 E6 P#define DEBUG
% H6 ?9 Q* z) R8 f' m0 m' j#include
  e  r" Q# R8 t#include
( {& j  B- k2 b! w. H#include + Z& V7 Z' ^, C/ G# f
#include  
: E% d# Z$ H2 L* X  B#include 9 Y: W/ [, ?4 M+ E9 l, q( Z0 c
. W; @( i5 C1 v# v
#define DEVICE_NAME "hello"% Q& C* J2 r  H8 Y# N) ?8 O

9 w8 n" {: q0 Y% Ostatic unsigned bufsize = 512;- W5 b8 b: _( x3 v
module_param(bufsize, uint, S_IRUGO);" c# ?3 T8 F4 r( |
MODULE_PARM_DESC(bufsize, "data bytes in biggest supported hello buffer");
! h! @4 k- t6 t# ?7 d
% g2 C- h' Y2 w" S* mstruct hello_data{
1 X6 N/ s+ p: T9 T& C& s6 }' s        char *name;( @- k# [6 v( T  }
        struct cdev chrdev;; ]7 Q: G! t$ A7 I
        struct class *cls;
: _$ I$ i* G% W& K. d        dev_t devt;
' U$ N+ P" U' q# S% ~% F6 I. X" P+ r        struct device *dev;
2 g' g3 j2 P. ]! e+ r# x4 p/ P- G0 z5 S
        atomic_t available;
0 t% s4 F$ y2 ]- p" C  P3 \        struct mutex mlock;  /* Protect @buf */
1 F( y- ]$ P5 o1 i        char *buf;1 I" G) E2 m3 d  O2 ?: p
        size_t bufsize;
+ _4 o/ y6 h$ [0 N        size_t actual_len;1 W0 F" Y  y( v1 o4 X
};
$ f/ H/ L" B/ M3 _2 j$ n: w; s! r+ \
static struct hello_data * hello;2 N4 I% b' l/ n5 w& M5 N' K' _) N( R

: [7 _  a  }! m3 F' ]# C* Y( _static int hello_open(struct inode *inode, struct file *file)7 Z5 x3 v2 U; A
{% o+ z. U; N& D5 {, u! m
        struct hello_data *hello = container_of(inode->i_cdev, struct hello_data, chrdev);
: g9 o. W5 p' x
+ W0 i+ L  f" Y8 D+ e( Z        /* Only ONE user can open the device. */3 N7 l; P) M5 R
        if(!atomic_dec_and_test(&hello->available)){
& U- H; v- q& l                atomic_inc(&hello->available);+ u4 A; q4 a  E) v! E
                return -EBUSY;) S+ ?6 {( D$ }+ J6 w8 N/ _
        }& n7 {& _/ Y: S- q3 Y: H$ `

8 u/ ]% k* c/ v- m& j, f  m        dev_dbg(hello->dev, "Hello open\n");8 K1 t3 x4 g& z# P/ ^& M0 P
1 n; R' ^* S3 h# @
        if(!strcmp(hello->name, DEVICE_NAME))
0 @. O' `/ f$ [. M$ V5 Z                dev_dbg(hello->dev, "This is hello\n");
( m- r1 I# Y  k7 |! ^4 Q
) l! \& A  O1 U) K2 u( u        file->private_data = hello;% @$ k( M1 x/ `6 Z8 Y

9 b! i: p- O- d! [        return nonseekable_open(inode, file);        
' H1 L; N8 H; ~: [1 K9 g! F" D}  E1 o' L0 w- \1 V1 K" D+ F/ K
& O5 a8 z7 i  K: z8 v& h
static int hello_close(struct inode *inode, struct file *file)- t6 L- `) C- p) W% z$ ~/ y6 y
{
$ r; j. s4 j5 {/ H8 e6 o        struct hello_data *hello = file->private_data;% d: }/ @2 r) E/ F& d) c
: |/ O6 ]' \* z+ C) R
        dev_dbg(hello->dev, "Hello close\n");
  z7 S# ~+ M5 L0 C8 m# K4 L  t        $ [# L% K  g% E* W; @# o  ^; [
        file->private_data = NULL;  l1 {1 a$ G7 A
        atomic_inc(&hello->available);
( V, o) U# T5 M  l% ^' O: E  R
: J/ `2 t* E- f+ w. x" C3 a        return 0;
3 G- q/ y) e6 q1 G# O9 B+ w( T}
+ t: c* y: E/ S- p! U( R
3 ^: x% J, f5 k, v; L  C1 xstatic int  hello_read(struct file *file, char __user *buf, size_t count, loff_t *offp)  o# Y" P( F' ]# W
{
' ^. }4 r3 p* F% Q. ]        struct hello_data *hello = file->private_data;
( t* E" [9 D( A% C        unsigned long ret;0 d$ H& `* n) y( ]
        u64 len;' L9 ]" l3 Q  G8 G" m

3 b. D# m' W$ }9 g; B3 n        if(hello->actual_len == 0 || count > hello->actual_len)
1 G( {  i3 b$ g& e2 u+ \$ R9 _                return -EINVAL;
! E/ z/ K4 X2 {- X  g0 {% a. X( a4 C. O/ X$ ^. m- [/ g3 J9 S' @
        len = min(count, hello->actual_len);
/ a1 t) W) E4 n* G+ R2 K+ s        mutex_lock(&hello->mlock);$ G. I) K  x# ~. ^" Y, i, |, f
        ret = copy_to_user(buf, (void *)hello->buf, len);
7 k; [9 K: T: `6 `4 W2 R        if(ret < 0){  t6 U. [% z5 G3 G* L
                ret = -EFAULT;" ?* m' P! m' P/ p8 p/ ^! b; K
                goto out;
' l/ \+ d$ O2 M        }) u( z0 `8 f; w! _6 k8 ?' H8 u
+ G+ J! G# ^7 u6 f6 o1 s
        hello->actual_len = 0;3 @! M  z" G/ N7 E( ?
        ret = len;# [4 l" b' A4 U+ `4 j, w7 H

  A( c+ w6 M4 F0 p# R* Fout:) D% ]: N9 O' S% Y
        mutex_unlock(&hello->mlock);
1 Y2 K0 S# ]6 I& L        return ret;
1 Y2 d0 i; e0 Y" Z; Q/ h}+ a$ h' [0 t0 e3 K1 q* M

1 j# @6 v+ r$ Q: ?) l9 a$ G3 ystatic int  hello_write(struct file *file, const char __user *buf, size_t count, loff_t *offp)
* N. ?8 h) @$ p4 n: D: e{  a& p( e1 s+ }8 Y
        struct hello_data *hello = file->private_data;9 L$ a0 x" v( g1 p
        unsigned long ret;$ B) b) O( u: t+ J) K# b, W
        u64 len;
+ D* i/ y( [' w( s& v& [/ H5 \  `2 X/ ]7 z" I$ W
        if(count > hello->bufsize)6 J6 J5 Y0 C. K+ d" ~8 l
                return -EINVAL;
; k1 J- e3 M. b: u" K) b7 |! I# Q4 B/ ~( C0 D4 u% U: u
        len = min(hello->bufsize, count);
. y* u( p8 i: I8 @3 F# P
. H1 [* C! D! f" p5 K        mutex_lock(&hello->mlock);: X5 o! R- Y5 a2 x  K
        ret = copy_from_user(hello->buf, buf, len);
+ k( a' Y6 d+ _        if(ret < 0){
# r6 c( i# {4 r                ret = -EFAULT;
; O& m& c6 Q  G% G, X! i                goto out;9 Q6 _/ q: ]8 @" @
        }& |+ @% N( s0 @2 Z# P. ~

3 d" Z, w5 N. z) V        hello->actual_len = len;
) o* A# B2 g/ y+ }8 q! d        ret = len;
, g* x0 y8 S* Iout:5 h4 x* D$ e0 H5 Y3 r
        mutex_unlock(&hello->mlock);
5 v0 e; F: f/ B7 l6 p0 V& X        return ret;
4 {" v  B  F6 z, R* q5 y}# C; L! R; e; u5 v. f; k
2 c& V& c+ l. N4 o
static struct file_operations hello_fops =/ n6 u! n" c. h0 o8 u9 v' z3 c
{
" g; N+ V, Z; f3 m3 a        .owner        = THIS_MODULE,
, P* g/ ^. M0 U4 P3 g5 A        .open   = hello_open,/ b4 D/ w  ^4 l& x1 \2 _" G% ^
        .release= hello_close,
1 I) k3 b4 @# T& i1 W" C9 C        .read         = hello_read,
, \8 [6 K& t  A9 i, N        .write         = hello_write,
1 w' @# O# A  W! u. G* a8 \        .llseek = no_llseek,: {7 C! s: d- s# }3 s
};' [5 i# @8 _; `2 S; `3 x2 v  P. x

; J' N/ {' l: O7 k4 wstatic int __init hello_init(void)! T4 b2 h+ N& }) y* d5 x1 f0 c
{
7 p0 ^7 I, ]" L+ |2 f! A        int ret;
( ^- }: h. E: Q5 i/ F  T  x
1 ^- y: U* P. b8 ~# c* _        hello = kmalloc(sizeof(struct hello_data), GFP_KERNEL);, r( i% }* N9 N# X! M
        if(hello == NULL){- |7 S# i2 M, x* [5 e/ S
                printk(KERN_ERR "Unable to malloc struct hello_data\n");1 y: f7 ?( |/ Q7 R
                return -ENOMEM;
+ P) P2 j+ Z& J- V9 g7 H7 I# f        }
7 g$ ~( h0 O. B& G# V       
3 \! T% n( y, P$ H        hello->name = DEVICE_NAME;
! ^5 D& }" }, `" ?! O: b4 ^/ z        mutex_init(&hello->mlock);
. Y  {4 p# \, L+ ?0 s/ w$ k7 I        atomic_set(&hello->available, 1);* M# `5 q5 a$ i  ~
3 b+ m4 ]: B% x' @
        hello->buf = kmalloc(bufsize, GFP_KERNEL);
; f1 L! W& h& l        if(hello->buf == NULL){- ~: O/ t! J/ I( O  N! j
                printk(KERN_ERR "Unable to malloc buf\n");7 k; }- U1 t5 C8 \
                ret = -ENOMEM;
% W4 c1 X7 f, T) m" F- P" Z, E6 l                goto err_no_mem;0 g" |. g7 s& j" h2 {7 @
        }
0 l. _7 ?5 p& H0 Y0 Z        hello->bufsize = bufsize;
9 [! g" o$ C% U        printk(KERN_DEBUG "Buffer size: %d \n", hello->bufsize);& ]! K  F! i# u5 E9 M) X4 t
+ \# r! ^7 s$ U# X2 r0 m; i  |
        /* Alloc the device number dynamically which is stored in the first parameter */6 N& v9 `1 W0 R9 t
            ret = alloc_chrdev_region(&hello->devt, 0, 1, DEVICE_NAME);9 Q. A. J: B' x. K7 L
        if(ret < 0){
. }: i5 p2 Z" z4 Z                printk("Alloc chrdev region error\n");. T$ V. a& p2 r! x
                goto error_alloc_region;7 d2 O; V! K/ y$ |4 M. P
        }) V* h: }/ Q" {; w' o* N  S( L& b
        printk(KERN_DEBUG "Major = %d, minor = %d\n", MAJOR(hello->devt), MINOR(hello->devt));
) T: J/ K" y1 {& L& K! _8 P- _) i2 T8 K' d
        cdev_init(&hello->chrdev, &hello_fops);$ T9 t! t' ?( P9 h8 O; R. r- F+ y$ ?
        hello->chrdev.owner = THIS_MODULE;
" T' I' K& k0 }" [0 i5 ~% ^, k# W        hello->chrdev.ops = &hello_fops;9 I' l# Q2 m5 t. n3 S1 A; T
: L; u% L6 q: b; a
        ret = cdev_add(&hello->chrdev, hello->devt, 1);
, t8 X' k1 c! X: m) n  g        if(ret < 0){" V9 I) M- Z4 S7 w
                printk(KERN_ERR "Cdev add error\n");! ~; Z5 h  a: @& X9 i: |/ {# d
                goto error_cdev_add;
7 S# m( k0 n9 a' v0 Q  h  u        }7 U$ C$ ^- B: I, |8 B  \: L/ J) y

. g! b* E% r5 Z! s1 p; P0 p        hello->cls = class_create(THIS_MODULE, DEVICE_NAME);
3 O1 q  p# I% K5 Q# V' S( a. t        if(IS_ERR(hello->cls)){
$ q2 Z5 \. r7 N% C                printk(KERN_ERR "Class create error\n");9 j9 S5 u3 I9 _; |4 }( J. L, B9 |
                ret = -1;" t4 X4 o: w& p# e6 J
                goto error_class_create;( p5 Q6 K1 F/ g& L( h
        }2 v1 P/ t' [! U" g2 H7 C4 p0 x6 H
6 U$ T* ]! A7 Z; k( z
        hello->dev = device_create(hello->cls, NULL, hello->devt, NULL, DEVICE_NAME);: K* B2 _& h2 ?
        if(IS_ERR(hello->dev)){
; i2 Q% \& n+ r) y/ X" U- n                printk(KERN_ERR "Create device error\n");7 K1 K, n" W0 o" }' h8 O! ~" n# z: b
                ret = -1;
  V% }! k  m) @6 x  i                goto error_device_create;0 i) _/ Z7 T' ]: M. ]
        }
% e7 l& F$ K0 V' \" N- e' e. H, |
  t0 N/ A( D) `9 Y% z2 V        return 0;
% e  Y5 {. d3 p5 a2 v* @  H7 N0 X, j' [+ M9 p  B/ F
error_device_create:7 Q: p* v4 @: X
error_class_create:
1 s6 E- ]5 N$ e! N! G- ]        cdev_del(&hello->chrdev);% `  L; o; t. k# |/ x
' I5 b/ D0 X; ~8 q& t
error_cdev_add:& g  J' G! [" U, b& |. R" m$ e
        unregister_chrdev_region(hello->devt, 1);
+ D0 a3 v1 I0 F; Z) _
8 A. Y2 T6 |2 e- Xerror_alloc_region:1 v; l3 h* K6 f/ w
        kfree(hello->buf);
/ B! Y. Z: R% S( f/ v: g/ L7 S% t) o  F- E9 N1 q: {$ E1 v* J0 q( t& K
err_no_mem:
: H* p' m5 l" l0 G& |2 O        kfree(hello);
- k! T* @1 [' @8 ]
7 s/ M# W9 q  G+ t3 U( Z        return ret;
0 i& V' P. b2 O9 x; U0 s}
! n' T" W. T. H
1 b4 q% N3 k& \- _9 Q- Hstatic void __exit hello_exit(void)$ `8 C  B; Z& _- r; |# U
{
& E# f6 j3 d8 h7 K        dev_dbg(hello->dev, "Hello world exits\n");0 `! }" y3 s8 V* ?( [! B& p

5 u6 M0 A5 e5 F- V2 o1 s        cdev_del(&hello->chrdev);
3 p6 n) T% K; K% ]( e* G( D, c        unregister_chrdev_region(hello->devt, 1);       
8 \) c) y8 ]9 d0 u( e        device_destroy(hello->cls, hello->devt);0 W! k! \) s+ K* w
        class_destroy(hello->cls);
# X7 X, e5 C* ^; e% z0 d        kfree(hello->buf);
% U! `, o" B! q% e: u" @        kfree(hello);8 H# v; F' b. \7 m+ v
}
8 ?  Y2 r2 F: R; U4 k
. P  T! b( b+ [9 K) Zmodule_init(hello_init);
+ x) [) `+ ?/ u) n! X( Y8 M  Gmodule_exit(hello_exit);
2 ^" b! l4 r8 R5 F: ]; a1 c% ^! O7 B% z
MODULE_LICENSE("GPL");6 D7 H# Z: [5 q1 o6 C
MODULE_AUTHOR("yj4231@hotmail.com");7 L; w+ N2 S( f2 |
MODULE_DESCRIPTION("Hello template driver");$ H3 w) s4 U0 p2 I# q, H3 n

( ]: p& d/ g* Y: \" ^/ w/ U! ^2 s- w4 t) ^! j3 X% b
对应的测试程序如下:
  L3 L* d  \& Y. b  Z#include
6 D  N" B, ~" i6 ?/ w! I#include * x* H( z- h2 E; o8 B/ h% ^1 u( @& T
#include 9 L+ U# G' l: E! c+ x2 {  O4 O$ i, W
#include
$ W5 z+ y8 C6 d. L8 ]#include
' Y3 T2 ?! q4 M! a# m/ ~8 O#include
% j5 K; r6 ^$ L  }$ b. d6 T4 R#include
  v) f/ E( x$ ^, M" v  s
2 T* D1 L- U1 @, Sint main(int argc, char **argv)% v4 x' \5 q- I; y
{
% T6 f2 H7 s+ R1 t4 \/ L( g5 S0 s        int fd, ret;7 m7 {( u) w  e
        char data[512+1] = "Hello test";; x- }; f- R, N$ C& l4 l
        char buf[512+1];7 a% G. u, h$ n5 x' w5 a1 R
        int n;
& H/ r- f# R/ c9 {/ e3 b) x* E8 l4 t0 _& _/ m
        fd = open("/dev/hello", O_RDWR);+ O7 K+ w% D. R; P3 f  B  P; l/ I
        if(fd < 0){
  G8 ^3 A+ F4 l& b1 B                perror("Open device fail");
4 T+ ?, ]6 g% _9 v9 f2 i                return -1;
4 |& E, h' @. G5 i$ M' V6 \+ k        }' Z$ C# R7 ^& j3 `, Q! \+ l

: S, y( U) @3 d* n        n = strlen(data);( x' ~& e+ j8 B8 F& |1 E( z' J2 r
        ret = write(fd, data, n);
+ w2 _8 p$ h* F0 N0 {        if(ret != n){) T6 V# J# X+ t* p/ h, K$ K3 x( l
                printf("Write failed\n");3 ?& {/ C, W/ U+ Y; W3 n
                exit(-1);% O9 c8 E8 e% m, O
        }
- X! _9 E0 u& u        printf("Write retval: %d\n", ret);  J* E% W; x+ Y
1 O# @7 w7 g' I  ~
        ret = read(fd, buf, n);( d- ]! U: d" K+ o
        if(ret != n){& @# W( L7 R: e' L& A3 H  }
                printf("Read failed\n");
; e& _8 n' }+ m8 N                exit(-1);  j1 @# A. ^. X$ M! I& [9 @; z
        }
/ ^2 C! u# S5 `4 x4 e; u# i        buf[n] = '\0';, w6 Z+ R" e" ~) u4 ?0 H, i
% ^+ B: h1 A! A  D, j
        printf("Read retval %d, %s\n", ret, buf);, V+ y  j5 U- h7 j- D. d8 `3 }
        sleep(1);0 _1 F: B# Y+ |8 X% x/ W$ w. W- y
        close(fd);$ ?$ Y4 k/ n3 f* t
}  T: f2 c; n' C: v. C. |

. P. ^4 V8 I4 r, ^5 d0 \; O/ H4 M% N" v+ I7 G( F
! I3 f4 r4 f) C0 Y
History:2 R/ j6 t( Z8 m: X. z
2014.02.15  单进程,多线程共享buffer版本(2.6.32)。
& e0 r# r& Q8 a- `( c- a9 F+ b$ b% X4 l# p% P( `
! V3 J, ?' @! B5 @

3 s* t) U% q/ I

该用户从未签到

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

本版积分规则

关闭

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

EDA365公众号

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

GMT+8, 2025-9-23 02:58 , Processed in 0.109375 second(s), 24 queries , Gzip On.

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

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

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