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

linux字符驱动之初见

[复制链接]

该用户从未签到

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

EDA365欢迎您登录!

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

x
学习驱动也有长达一年多的时间了,受益最深的就是看韦东山老师的视频,如今已经几乎将二期三期的视频全部看完,甚至已经将二期视频看过好几遍,为了再次加深印象,我将韦老师的源码自己全部编写一遍。将所有遇到的问题,记录在此。觉得看了韦老师的视频,再看其他视频都是弱爆了。由于是文章记录,不可能写的非常详细,只摘录关键点,想具体详细的深入,还请去看韦老大的视频吧。
) A/ X* r  u& f. o5 i, m
7 a- W$ R9 Y3 Q* ?8 i* V这篇文章是主要是讲解字符驱动的框架,并没有涉及高级字符驱动。) U+ b0 m# W. P) O. _) c

0 W2 |+ u/ U, P) C5 N/ Q" j一、字符驱动框架
. A- Q4 z0 |4 W* t8 v2 y) Z8 j( Y; o" `0 Z
------------------------------------------------------------------------$ P/ a; H. w; I% L2 h- T: j* o' P

  L4 Q7 {2 F: m. jAPP:     open               read                   write
" s& R/ e, \1 O7 W) f: M* g+ @+ s
------------------------------------------------------------------------/ U! e& w( R" X! l5 [" J9 x2 G. D
) @0 P# C; g2 S1 q) @* p) T, I8 ~+ i
C 库
2 v4 I' P: a) n/ C% Y+ m7 @
) U$ {2 U1 n' T% B3 {------------------------------------------------------------------------
; q0 n% ?# ?0 h( o/ M. i- y# x" }4 H% T2 m" r
      system_open    system_read       system_write
. l* F# z( \" V# v  j8 q8 V
9 M$ n$ }; [/ ^% H
$ q8 E% {. a1 R& t/ B- Z* [# B( K! s9 P
------------------------------------------------------------------------
) N$ c$ ?) D1 W: j9 v: e& A7 Q. ~) H+ L% M
KERNEL:
8 q' O+ T+ U" K5 t7 {* R, c/ d1 P$ Q6 u) ]' S5 e2 l9 ?7 D
      led_open           led_read               led_wirte6 b9 F3 @. V( I8 y: Q
- Z5 T2 c, h9 n6 i2 Z- \9 W
------------------------------------------------------------------------
; A, I, t  z9 O( v2 V
% Z, ?5 u0 _1 B' @/ o3 p1 @! x5 f( Y) U# v- j1 T

8 W4 E9 T+ }0 N; i: c问:应用程序open如何找到驱动程序的open函数
0 E/ Y- c6 r* n( _2 H" B3 ~2 d2 j  \3 g' z0 d8 a8 B
答:应用程序的open通过C库的open函数,通过系统调用的system_open函数,进而通过swi val指令,进入内核,通过一定的办法来找到驱动程序的open函数。* g# v6 m7 O4 }! `, d: u2 \) K
, K8 ?/ v. m, j
问:通过什么样的方法来找到驱动程序的open函数$ m2 d  p6 z+ f- U, ]9 }, s# y
" S3 K/ u1 x- Z
答:通过一个注册函数+设备节点
$ E8 `5 L" A3 w/ s0 Q* E9 V
, Z# ]' ~4 P% o/ e* t& S! Y注册函数如下(旧的注册函数,新的以后再说):2 Z, O' n5 J  ^8 e& c0 ]  W! K

% z) a5 ~* c8 d  t. Q1 h5 S8 j' yregister_chrdev(unsigned int major, const char * name, const struct file_operations * fops)
$ S; x  t! C6 l! l) H' p1 ^+ H3 I3 ~+ z: `6 D$ L2 d; Y
参数1:主设备号(重要)) S; F1 z+ v) `0 e8 N0 g* m
  u0 S4 Z* ~5 Q0 c! K
参数2:名字(不重要)
7 q. L2 T6 G/ e( a( K
6 t0 c2 w# A+ k1 }$ o参数3:file_operations结构体(重要)
' }& {- }& M0 `3 h% J& O# Q0 T
设备节点:8 X4 n& d2 l5 }1 W7 K
, i* ^' u% H7 y" Q$ H% _
可以手工创建也可以自动创建,这里暂且只说手工创建7 }0 x9 v3 M" ~! \- w- m. D7 o

- N# S) v+ n, [; w$ kmknod  /dev/xxx  c  252  0  y) k6 O3 ^$ L% }/ Z- b5 K

# s) `  s- v) F# W$ \( M具体什么含义,我就不多说了,看视频吧,很简单。! ~& X: Y4 w& l% x

$ Y: b. x. _: o9 z0 d7 X
. k' K/ d; ~5 s* {3 u- g7 @+ h/ g% s! x! U( z) a* O7 d
问:应用程序一般是由main函数开始执行,那么驱动程序一般是先执行什么?
- t. \5 j% Q; |5 z8 B1 i8 x- {! v$ G5 b! L5 L/ f" r; T
答:通过一个宏,指定驱动程序的入口函数,当装载驱动时就会执行入口函数。6 ~7 y, O3 u- ^0 e2 `

6 g7 E/ d& U, E/ O' o$ L1 w例如:module_init(first_drv_init);  //用于修饰入口函数0 d/ a2 g- T+ y2 o" ^* M" L
( R% ~. X; @3 k. \1 t
自然地,驱动程序的出口函数,则是在卸载驱动时就会执行出口函数。/ y( O+ A7 w- k
/ _& |* l% a; @/ l
例如:module_exit(first_drv_exit);  //用于修饰出口函数% N' Y1 _7 @, w" Q4 \$ j4 U# X8 t

5 V  e7 c9 q) h
+ V. Q$ A5 N, C1 ?& B* b0 Z
2 g# o# S4 _1 T4 m, F+ n3 S驱动源程序如下:
; h2 g7 p# s* h. v' z$ k* X
+ L, i& y' [% m# _+ {& c1 P1 N) v+ X. U9 C; X) `3 P% H
#include <linux/kernel.h>
" j) a6 |( s- H& a4 E' S8 X- T% q1 t#include <linux/fs.h>- z! T( k+ b4 R5 g* x
#include <linux/init.h># j" i7 r6 @! D9 y4 E/ d  ~
#include <linux/delay.h>
" D( ^4 L$ v4 L# B8 s#include <asm/uaccess.h># I2 J8 A7 ]: u
#include <asm/irq.h>2 E3 l' e: ^0 S6 ?) x4 [- n1 ^
#include <asm/io.h>
! E7 Q$ J% U' u& H#include <linux/module.h>
5 D0 k6 d% n) I% c. R
" P( Z* x- ?9 w" V. ^* H
4 }& p" @1 d2 h# |& Dint major;2 Z9 n& _5 s% P
static int first_drv_open(struct inode * inode, struct file * filp)3 \7 S) R; ^3 w" S" x
{
' Q8 X. L3 c: Y, D8 N        printk("first_drv_open\n");# e/ O+ u/ R6 x
        return 0;
- V& L' }) L! M9 ?# v! e$ s( x! v& q}
3 `8 J% k( _0 x$ B9 d  c9 nstatic int first_drv_write(struct file * file, const char __user * buffer, size_t count, loff_t * ppos)
4 ^, ~+ t7 i2 }" N# G- Z1 A5 X) v{% H6 Q! D& a9 X  |7 J2 x
        printk("first_drv_write\n");6 G# E& X# s" n' a6 \
        return 0;7 y* @: Y- ]" b: A# [
}  T+ N. V) O/ f9 i
; [( q/ S. t* s8 y. m) \* n
/* File operations struct for character device */
. B; M& W/ T3 N; J: H- ^static const struct file_operations first_drv_fops = {$ M* e3 H( }$ x  O4 j; l
        .owner                = THIS_MODULE,
$ U0 o% _4 I3 |% y' V5 f        .open                = first_drv_open,
3 n" a3 r: d8 f0 r1 b  @        .write      = first_drv_write,% I2 y) ?& q1 k  v7 E2 d7 G# t7 K4 ]4 I
};
/ W, A  h# [4 y* M8 l8 v9 u. L9 \  N& f! T8 o, n$ `
/* 驱动入口函数 */0 V( E8 G+ ~; g+ C) b
static int first_drv_init(void)/ ]" @  x! h1 F
{) h& a& t, Z- `
        /* 主设备号设置为0表示由系统自动分配主设备号 */  u) R( o4 {3 b# J! }( i- U" a( Y
        major = register_chrdev(0, "first_drv", &first_drv_fops);( k5 k7 `, |4 ^0 s- x9 L
        return 0;
. j$ T- ^7 `) L: ]; q}
4 v- s( H- k) u% H! |( Q& n. K2 b2 J7 L& ?# w* s5 J0 U
/* 驱动出口函数 */1 ^! l5 \) T' H3 T5 o) y
static void first_drv_exit(void)2 J  L$ j- N& t* h9 D2 f
{' |9 R% Y( P* f5 x& g
        unregister_chrdev(major, "first_drv");
- c- |3 a9 z8 F0 r% ?0 w1 u+ z}
2 o' e0 {  f) f, f) q: e* d8 c
* N6 q: S. B# a- g5 qmodule_init(first_drv_init);  //用于修饰入口函数
7 ~9 A/ v/ G- O; ~' r; @! ^6 kmodule_exit(first_drv_exit);  //用于修饰出口函数        ) \" M7 G) U4 @( p# ]9 `0 v

+ D& O; V/ b2 H: h. M3 xMODULE_AUTHOR("LWJ");
6 ?7 `3 j7 F2 EMODULE_DESCRIPTION("Just for Demon");, F9 i: q# C! Z" C
MODULE_LICENSE("GPL");  //遵循GPL协议
1 K) F4 G# Y2 P  G/ P4 t6 @( p2 n5 W/ @9 O
Makefile源码如下:
: p3 V- m; U7 g' Y' q# W8 C; ?  W8 x1 v$ \9 J) M, e  m6 s! P: Q* v2 U
ifneq ($(KERNELRELEASE),)
) J1 S- \4 |; {
# Q% I7 F+ ^. E% ]: v, pobj-m := first_drv.o, K  q* f/ n" ?; M7 y

" {3 B$ b& F/ Y" M6 ~+ M: Kelse
- Q. t6 {, |2 P6 _- B5 V0 D        " O% R# [- W* l# O$ J0 k% P
KDIR := /home/opt/EmbedSky/linux-2.6.30.4
6 r7 j1 h" T4 M5 Q0 ]. C
+ H, Q( M$ D3 v$ o; call:
4 n* {7 K9 D* H+ ^9 v        make -C $(KDIR) M=$(PWD) modules ARCH=ARM CROSS_COMPILE=arm-linux-& W9 @1 t/ `7 C% t0 C" }
clean:" m6 {/ G, M+ i) `) `
        rm -f *.ko *.o *.mod.o *.mod.c *.symvers
: r# Y7 Z! r9 c2 K; T
' {" T* i7 ]* ], Z  C4 N. c& Iendif
! z4 H. D) M+ \8 }3 A8 Y! P2 Z! o# f- b/ q; B
测试程序如下:4 k/ T  b$ _$ A2 ^  l9 |( B0 M2 p' @4 t
- W: t- Y  k2 K+ B+ ^
#include <stdio.h>
6 {4 m4 l' ~1 m8 G5 X! q#include <sys/types.h>
' ~9 }, ^/ v$ J+ g, a3 ~) @#include <sys/stat.h>% I2 b* F0 K# W2 E0 D
#include <fcntl.h>
2 ]' x' Q, o6 ~" f( N#include <unistd.h>9 {4 d$ f! f: G

! I4 r. z1 ]0 s. `int main(void)8 b6 a7 a8 R- R& z# g
0 U6 G  Z1 [9 v: t& F
{" t8 _3 ?8 d! w9 g: i
        int fd;& f7 k; f6 O# c. J
        int val = 1;
$ u* ~) j  b  F, z3 D  t        fd = open("/dev/xxx",O_RDWR);
. `3 s, T5 `4 e; N8 |  M( ]        if(fd < 0)+ h" V9 O* J7 ?; t9 ]7 x) C
        {
/ L. p; C& ]0 X& ?2 c2 y( V- e5 f( C                printf("open error\n");
4 q, O* h: i0 K8 k2 y  F( J        }
5 K, _# ?4 U5 W$ x  P6 i+ [        1 w+ y* s# C7 r5 G0 |4 v; W) B
        write(fd,&val,4);
7 p+ I0 T8 W- T% W0 k9 H       
0 P& J! |3 V. A% A2 e        return 0;
4 U% f  u6 r* s) x7 @; E+ r9 }  @}0 i6 v$ N$ \7 B1 [# @
- X- y# \& G0 N+ n
开发板上的测试步骤如下:
4 v5 b/ T: `# x5 ~# B% E. u! H/ p% P
+ \. p0 K8 X# s* u[WJ2440]# insmod first_drv.ko ; d1 {! v+ Y( t8 I) Z% p# ^
[WJ2440]# ./first_test
" u9 f9 y' D) r7 _; S. Kopen error* n& N0 U7 B' j3 R$ U
[WJ2440]# cat proc/devices
+ _1 W# d4 z9 }) W' X& [Character devices:
, T9 u3 M2 `& o* y; m2 h  1 mem/ B% T3 D& |; y2 @
  4 /dev/vc/03 k8 i: {- O& P, n
  4 tty
5 C! s. M. s' n- \3 p  5 /dev/tty
* H" u0 B+ _7 S  5 /dev/console! y+ o5 h; |" D$ e; f; Q9 j; r+ o
  5 /dev/ptmx
8 d) n1 {, F( J. N  7 vcs
: \. Z2 W/ y' e( |/ Z" i 10 misc# y: m* D* f$ W  {' R) Z
13 input
: T* Z% I5 X7 F- m6 P% P, ~ 14 sound
5 m, g7 x( p( C6 s4 R 29 fb
. D; ^$ \0 }  s- ]1 ^7 R 81 video4linux
3 U6 B1 {! q: y" m& t 89 i2c
5 @' y$ `+ ], _( q& d. r 90 mtd. `% V1 _* ~0 G: X9 W$ X
116 alsa
0 h9 m  v0 r4 _7 @# D( f' B128 ptm: ~1 h4 }; G/ |
136 pts! D+ y- n) q6 F: b, W! `  j. G' U, \
180 usb0 ^6 c1 C6 {0 H1 Z( E
188 ttyUSB
% V/ V# U0 v1 P; u189 usb_device
! x1 i$ Q) o# `* S4 F204 tq2440_serial
# _1 U. o; @6 x; t; I: s252 first_drv
& l4 v0 l) f- _3 W: z253 usb_endpoint& ?; j, j3 i# u" `/ Y* G( x
254 rtc6 r3 ~/ w' {; f( B$ R
6 y) {- T5 ^& T  q- s
Block devices:
* V& ^( x: j0 q8 M4 `! z259 blkext* H( ]& M6 ?" Z$ i, N
  7 loop0 }% n. N$ l- f: R" e1 l
  8 sd
* j1 P7 U* f% v" l" ~) J 31 mtdblock. i0 m7 Q/ b6 K+ l+ ?, B
65 sd
0 o9 k/ D* B8 {7 Q" o3 B9 q' U: d7 Y 66 sd% E# b( s" b9 J* o
67 sd
! V8 A2 _; `! h, [9 n 68 sd$ d& w- Y7 k) t& y1 l
69 sd
# F$ R5 `" w4 J& ^9 l 70 sd5 f2 h2 t; s/ E* |0 e: K  k0 _/ ~+ ]
71 sd: \$ {* ?& b; `  z: C/ l1 A( u
128 sd
' n- a5 R2 F& ~8 C129 sd
/ M: ~0 u( d" o8 Z130 sd
6 o5 T8 H3 Y0 c# P$ l( q131 sd
& U+ y; t2 z, v6 M; Q132 sd# A/ E) q1 n$ A- R6 z3 [
133 sd
9 `5 ^3 s9 _, s134 sd
8 _, e$ _7 {" Q# ~! X! b135 sd
; C- x" U" R8 Z4 ]9 \$ o179 mmc. P2 ?: {1 s' C' q4 C& z6 }
[WJ2440]# mknod /dev/xxx c 252 0
+ E7 |' k& N, `' f3 ~[WJ2440]# ls -l /dev/xxx
' C' x: U/ K6 j: b5 b3 _1 y0 e: Fcrw-r--r--    1 root     root      252,   0 Jan  1 20:49 /dev/xxx0 g% \6 ~3 T) }% t. U7 f
[WJ2440]# ./first_test
, o# {7 f# v( Yfirst_drv_open/ E# i. k0 b2 R# X4 j
first_drv_write8 f* ~! |5 X/ y2 Y# w0 O* D, y
[WJ2440]# % z9 O2 g( x% W, S: H5 Q
, o" |# |( m% T5 u2 Y3 U0 B1 \3 h

+ y0 t# P9 U  E9 K
) c: ?% k1 _- v& _/ W0 f

该用户从未签到

2#
发表于 2020-4-27 13:22 | 只看该作者
linux字符驱动之初见
  • TA的每日心情

    2019-11-29 15:37
  • 签到天数: 1 天

    [LV.1]初来乍到

    3#
    发表于 2020-4-28 13:56 | 只看该作者
    linux字符驱动之初见
    您需要登录后才可以回帖 登录 | 注册

    本版积分规则

    关闭

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

    EDA365公众号

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

    GMT+8, 2025-10-26 12:43 , Processed in 0.125000 second(s), 23 queries , Gzip On.

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

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

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