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

linux lcd设备驱动剖析三

[复制链接]
  • TA的每日心情

    2019-11-20 15:22
  • 签到天数: 2 天

    [LV.1]初来乍到

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

    EDA365欢迎您登录!

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

    x
    上一节文章中详细地剖析了probe函数,但是从始至终都没有看到打开读写文件接口的操作函数,只看到了下面这个操作结构体
    " O- V: n2 P+ V1 z. L
    : U* d+ y4 p2 r& Q9 D& K+ C7 Q) j; J' O
    ; ^7 p+ f; z# R. U% t/ Istatic struct fb_ops s3c2410fb_ops = {
    % ~7 U+ y7 X6 e        .owner                        = THIS_MODULE,
    $ t' x9 x) P  L0 }        .fb_check_var        = s3c2410fb_check_var,
    7 T3 r# z1 W$ S: G9 l1 O+ x        .fb_set_par                = s3c2410fb_set_par,
    - h  s& F$ ^3 p+ T6 D7 I        .fb_blank                = s3c2410fb_blank,, t1 r- u/ v. s, z8 q% V
            .fb_setcolreg        = s3c2410fb_setcolreg,
    6 \9 V5 K. i  M9 |5 Q% Y$ l        .fb_fillrect        = cfb_fillrect,
    9 b5 }, X) B9 z/ `- E* G        .fb_copyarea        = cfb_copyarea,0 N  D, Z% v- V* x. v1 Z
            .fb_imageblit        = cfb_imageblit,$ [+ C- [; y& {8 p
    };/ Y" d1 U0 \# c6 f, S
    这并不是我们想要的打开读写操作函数。上一节文章链接:http://blog.csdn.net/lwj103862095/article/details/18189765
    ; z4 J3 U) Z9 i, g, z问:那到底帧缓冲设备的文件操作结构体在哪里呢?
    " k; j* q9 Z5 k- E答:在drivers/vedio/fbmem.c文件里。7 J5 r3 B. D1 v: V& ?

    - K- ]- W! n9 Z7 O0 }从入口函数开始看:' Z+ |$ W& \& F: E0 w- G. D5 }
    & _. z4 ?. g, O, J, v( e3 g6 m* T0 M

      @# s2 X, X, r  M; V+ Y" _static int __init
    ' e- P) ]) q# ~* J# Tfbmem_init(void)9 V2 Z8 T" b% y! e% \+ m
    {9 b3 M# E6 q& ]+ ^
            proc_create("fb", 0, NULL, &fb_proc_fops);
    6 }8 y  r" L( f  u( y+ R$ S+ K* ]- v+ _& W/ A; f
            if (register_chrdev(FB_MAJOR,"fb",&fb_fops))
    * r  |# N7 I( w# }+ K                printk("unable to get major %d for fb devs\n", FB_MAJOR);3 I' ?1 h6 }  f4 O' z* w
    - q$ g3 o6 }" f
            fb_class = class_create(THIS_MODULE, "graphics");
    / A5 A4 H) K7 [& e% q! m        if (IS_ERR(fb_class)) {) z* d$ N. y1 u& Q6 h
                    printk(KERN_WARNING "Unable to create fb class; errno = %ld\n", PTR_ERR(fb_class));
    7 k& I# @4 M3 p' ?+ j7 ]# a. l                fb_class = NULL;+ [/ k5 y1 T1 u- i  N- a. V$ s
            }
    8 @1 M8 I, }6 h+ _        return 0;
    - [$ }7 _8 a; K}
    9 M5 P2 l, ?% Q: T! F2 vfbmem_init注册了一个主设备号为29的字符设备,并创了graphics类(图形类)。7 N. w: j' y) Z- i- W
    字符设备有一个关键的成员是文件操作结构体
    9 P5 G7 [8 |% P3 F) `6 t* p6 l5 M# _- s; w4 P, h
    * d% L% r. X& K( D7 W
    static const struct file_operations fb_fops = {4 z$ E4 D8 n' H$ m( P3 q/ j
            .owner                         = THIS_MODULE,
    2 A* K+ [: V( B0 S' C8 U        .read                         = fb_read,* \; Q9 v1 G1 X2 b& v: z' p
            .write                         = fb_write,
    % W9 f" ]7 z6 z% }        .unlocked_ioctl = fb_ioctl,% l, t% [  i! B0 K. f0 t
            .mmap                         = fb_mmap,( }9 o3 Q2 R! w9 K
            .open                         = fb_open,
    1 I: a( c7 a6 B& q/ x( y        .release                 = fb_release,
    % z0 Y8 Z  }" ?' {! x#ifdef HAVE_ARCH_FB_UNMAPPED_AREA  _& \2 ^* Y- z# [
            .get_unmapped_area = get_fb_unmapped_area,) a# P" N! r/ ]* n) t' J
    #endif4 o! M$ {; I( z0 }3 a
    };/ O& |4 P) L8 D0 z7 \8 y
    理所当然的,我们应当首先看的函数是open函数
    " c) I" Z: d8 f! }* R
    5 q" T" x  H& w; `& ^6 h& r2 mstatic int' q% i. [0 C" g# d0 N
    fb_open(struct inode *inode, struct file *file)! o& b; K! Z# _; ]4 n( V* j8 N
    __acquires(&info->lock)
    * @8 G" T! a7 m. N2 T__releases(&info->lock)
    6 \. x+ A  G$ L+ |{
    * L5 G1 I5 l6 O7 q% o        int fbidx = iminor(inode);                /* 得到次设备号 */4 u+ ?& G  C5 Z9 k7 C- j- {$ E
            struct fb_info *info;5 G  h$ K' w% b* x" U# H
            int res = 0;
    4 u$ J  ?. y6 c6 _8 y: ?+ W" q" J* W1 B* }( a% l  d1 a
            if (fbidx >= FB_MAX)                        /* 次设备号有没有大于规定的最大值32 */
    # p- Y2 q8 B$ f9 d: u) U% h; J$ M) s8 B                return -ENODEV;                                /* 没有这样的设备 */
    5 h( G2 M; n5 b. v        info = registered_fb[fbidx];        /* 使用次设备号得到fb_info结构体 */  ?0 T: [; C7 v+ S- k4 ^& U- \2 N
            if (!info)5 @9 O2 ^% R: ~/ s5 g# f" M" d
                    request_module("fb%d", fbidx);
    ) h2 }$ H9 T& n- F1 _6 a
    % ]# @3 ^- q$ u4 ^        /* 再次使用次设备号得到fb_info结构体 */  I- Q% A; z, e6 i/ j; t& t
            info = registered_fb[fbidx];
    ! l" k& G7 z/ V3 F/ A) A        if (!info)
    ; V! f, }1 N6 u, f8 }  T  I3 w                return -ENODEV;, s) j# j# T! y: Q) q' n# ^) }
            8 h% ?6 c$ _# Y0 I0 n' e3 r
            mutex_lock(&info->lock);                /* 获取mutex */5 b6 I/ A4 b7 w6 @, n2 L- H# c

    1 Y7 g" D9 n+ v6 L7 X        /* 获取模块使用计数module,成功返回非NULL */
    2 m3 m" Z1 Z8 ~% ?        if (!try_module_get(info->fbops->owner)) {' @0 g, K0 C3 C& {- n
                    res = -ENODEV;! K1 |3 ?) L! E9 F" T# H; n
                    goto out;! U, L5 S% u7 g* `5 v" u7 y
            }/ M  _! j. N7 }% O5 k
    ( |& U" n: S: C. ~) Q1 x8 g" O
            /* 从registered_fb[]数组项里找到一个fb_info结构体保存到$ T0 D$ N- d  v' Y
             * struct file结构中的私有信息指针赋值给它呢是为了以后调用) r& t& f0 W7 H4 O/ @
             * read、write、ioctl等系统调用时找到这个struct fb_info结构( K! c$ ~  i9 p7 E9 K
             */
    , Q$ ]6 v" p  D2 m" w. N        file->private_data = info;        * H2 E4 d" M4 h& a! q& o
    0 q5 R% l9 w# T6 l: J
            /* registered_fb[]数组项里有没有默认的fb_open函数,如果有就使用它 */" N6 @# C5 K& Q, Z6 T. H
            if (info->fbops->fb_open) {
    $ I# T" a' o/ X0 Z' L0 [1 S                res = info->fbops->fb_open(info,1);        6 a1 ]8 H( G$ b2 U

    ' P. t' J( J0 K+ ~3 s5 E                /* 有默认的fb_open并成功打开就删除模块计数 */
    + J' A3 \# h4 ?$ z0 t* _" J                if (res); {, ^* z3 }3 |# ~
                            module_put(info->fbops->owner);- m) G7 Y, z! r; @+ s) ?( r, p5 ^6 f
            }6 k, i6 n( ]9 X4 F" Z
    #ifdef CONFIG_FB_DEFERRED_IO                /* 这里没有定义,不用理会 */
    ! c- H9 i5 ~* d! H! C        if (info->fbdefio)$ \4 b0 F! f& V+ W( G: R. w
                    fb_deferred_io_open(info, inode, file);1 p# \& Q0 _; y! `( K+ \0 A
    #endif7 U# N3 U0 n' x7 I4 H$ |5 y
    out:
    # h, B" a% _7 c# D% J9 ^2 i        mutex_unlock(&info->lock);        /* 释放mutex */: e* C, ?5 y! R5 }0 M
            return res;
    4 q' G! q9 B6 F  }2 s( {}# m' B9 ?$ I3 ]4 x2 S
    发现fb_open函数是围绕fb_info来实现的,而fb_info设置为registered_fb[fbidx]
    3 h4 n% i  Y% b& s" U9 Q7 e" G6 c问:registered_fb[fbidx]结构体数组是在哪里被设置?
    2 m  o$ O* ]% w% R! v9 u4 F! m4 X) ~
    5 g+ b; c( Q! m1 x+ H答:register_framebuffer函数里设置registered_fb4 ^) q0 m4 d0 g7 d8 V! S

    / {3 U4 L" h" H" g. m
    3 z) x7 z+ V. g+ h/* register_framebuffer()函数的主要工作是设置fb_info结构体的一些成员 */
    % k* [( {5 M+ ~7 Q2 a( [, |int: `+ Q/ m" Z  s, C
    register_framebuffer(struct fb_info *fb_info)8 C! s! s. a0 [+ x
    {
    2 P4 E9 e  Y! d4 C" k5 |        int i;
    0 f5 x/ o* W$ m0 S' X        struct fb_event event;
    + V7 i. h8 G6 D% q: c/ o) B        struct fb_videomode mode;
    ! P0 V  x: R5 ?' L! @$ K9 u; z( s  Q( n6 v+ U: W3 b
            /* num_registered_fb代表注册帧缓冲设备的个数 */! H3 x9 g8 ]+ L6 m$ n. `
            if (num_registered_fb == FB_MAX)8 w! {. }5 w! e# C# h% H
                    return -ENXIO;* c4 T7 K' }9 s3 P* v8 [

    0 \7 J  i- N/ R, y+ ^        if (fb_check_foreignness(fb_info)). }' d& Q$ q$ a0 S) _+ r
                    return -ENOSYS;
    2 S7 E0 t3 O& j8 C: X  Y! J# H# b8 f/ A! A( a) v
            num_registered_fb++;+ y% h, a; s) G# B) }
    & v/ t6 G% O0 f
            /* 当registered_fb[]项都为NULL,就会break,找到一个空的次设备号 */
    1 T( ~  v* e2 t% Q/ u8 }' m        for (i = 0 ; i < FB_MAX; i++)  A7 G& ~5 o0 W
                    if (!registered_fb[i])        " t) E9 }; H  ?3 Z8 [/ X% G
                            break;7 Q0 H, Y7 h4 V+ R
            fb_info->node = i;  x9 i( n" W' P3 R; m+ b
            mutex_init(&fb_info->lock);                /* 初始化mutex */
    ( w! \( `; L( l9 c9 f; R  i1 W* Q# \& g; e7 v$ m3 O
            /* 因为在init加载函数里只创建了类,这里在类下面创建设备 */
    / |1 o" ~( W1 @- @3 x0 F- g) u        fb_info->dev = device_create(fb_class, fb_info->device,, `- [0 X( p! `( w8 N; [5 U
                                         MKDEV(FB_MAJOR, i), NULL, "fb%d", i);
    6 u9 n9 v4 D- L) d" S; s        if (IS_ERR(fb_info->dev)) {6 p9 n7 y' g. z% K, j
                    /* Not fatal */+ }: a% N! f# ]
                    printk(KERN_WARNING "Unable to create device for framebuffer %d; errno = %ld\n", i, PTR_ERR(fb_info->dev));7 P2 g" B. ]5 o$ _7 ^8 g+ Q1 Q+ k" d
                    fb_info->dev = NULL;7 W8 p# ]" c  R# y4 g/ I; o3 M( F
            } else
    ) u+ N  n+ P8 E$ {                fb_init_device(fb_info);        /* 对struct fb_info做一些初始化 */
    2 j2 t8 I! t$ h. q. t* D( T! h3 ~; S9 N4 z. w# f

    $ N4 ?: U+ E; z4 H9 K+ h        /* 初始化fb_info->pixmap结构体成员 */
    0 v. P3 h+ r9 r7 `5 }- W4 `        if (fb_info->pixmap.addr == NULL) {- E0 p/ M, N8 K; s- _6 L
                    fb_info->pixmap.addr = kmalloc(FBPIXMAPSIZE, GFP_KERNEL); /* 8K大小 */  n" d% {  P5 }" p
                    if (fb_info->pixmap.addr) {8 Q8 U3 G( j( ^; q- y
                            fb_info->pixmap.size = FBPIXMAPSIZE;        /* 8K */
    / {  l. e$ r1 t7 L                        fb_info->pixmap.buf_align = 1;
    4 Y6 q2 z. Q2 Z4 l% D, e                        fb_info->pixmap.scan_align = 1;
    8 s6 X. C' _. R( q3 V4 S9 T2 m                        fb_info->pixmap.access_align = 32;
    * S6 |3 T& S, T* H. n                        fb_info->pixmap.flags = FB_PIXMAP_DEFAULT;
    5 g3 A5 I* e8 P: \. x                }
    . i6 ]9 `4 {* K( k4 v5 u        }        * l/ V9 b( |# `% d5 D4 O7 X( \" a
            fb_info->pixmap.offset = 0;9 l9 B: }, E! [; U

    1 E& Q8 ?% e3 s8 f        if (!fb_info->pixmap.blit_x), \( l: w+ g# G8 R
                    fb_info->pixmap.blit_x = ~(u32)0;
    ! c& |5 R- C' T! c
    3 A. y& q% u( L! {8 z1 ^        if (!fb_info->pixmap.blit_y)  U$ L% U9 l  F4 g( P
                    fb_info->pixmap.blit_y = ~(u32)0;: B' u4 v1 w7 d" J3 ]
    + Z5 P" F! Z5 x+ z6 X6 q
            if (!fb_info->modelist.prev || !fb_info->modelist.next)& y9 t* C) C* D- B( f# k$ N* B
                    INIT_LIST_HEAD(&fb_info->modelist);        /* 初始化modelist链表 */* H" g5 t: }; ~" N% F/ c
    ! R# q! A1 u- P: ]
            fb_var_to_videomode(&mode, &fb_info->var);
    ; Z$ E5 v# s) N+ G5 O        fb_add_videomode(&mode, &fb_info->modelist);/ a( B' a% B1 Z2 }. y* l+ i0 O
    : w- L6 Y# S* s1 L$ |
            /* registered_fb[]数组项在这里被设置 */
    : t+ s+ ^& z2 ?7 ?. ^# k6 A8 ]        registered_fb[i] = fb_info;
    ' W( h& P0 V) h# V8 l1 F
    . d9 [3 ]# c7 [: p" ~9 |        event.info = fb_info;
    3 Y1 W& F6 S( k, @1 a1 d/ w4 e3 e# I9 `& J
            # Q& U# X" _* i/ D
            if (!lock_fb_info(fb_info)) /* 上锁 */, g0 s9 p8 |) W9 p$ J8 v& d/ v
                    return -ENODEV;
    0 Z: [7 M, X- `4 N2 F        fb_notifier_call_chain(FB_EVENT_FB_REGISTERED, &event);+ G0 T" i( Y; e' h  [
            unlock_fb_info(fb_info);        /* 解锁 */
    3 u* i- s% b4 P% @  X4 W        return 0;/ l+ C9 ^9 C* y8 P9 P* R& {* ^
    }5 ?/ J  b& A( R( b
    fb_read函数源码分析2 W" O5 B* I& {  k# S* n
    7 V! w6 `7 j3 Q+ A2 c
    static ssize_t
      B- M- x! ]7 j0 N1 k0 h0 ?fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)' {4 ^( ^/ Z" i" z. m( t
    {
    ! P5 Q7 Z& ^+ u) ]+ K+ j        unsigned long p = *ppos;
    0 {, l- C! L& F5 C& m        /* 通过file结构体的成员得到inode节点 */
    # c2 b+ ?0 n0 @5 E        struct inode *inode = file->f_path.dentry->d_inode;
    7 L; \; Z6 a$ ]+ A0 g1 o9 i$ ~2 E& z/ z/ h+ x. s
            /* 获取次设备号 */$ T, i( i, y& u9 o
            int fbidx = iminor(inode);: o+ R/ q* u+ j
            /* 以次设备号为下标找到一项fb_info结构体 */
    . i3 O/ n# \7 e6 s( e        struct fb_info *info = registered_fb[fbidx];# X3 O( \7 w! c. r" s1 Q
           
    * g% {$ j- z7 f5 Q: b  L        u32 *buffer, *dst;+ f- U) L- i. l  ^" G  u
            u32 __iomem *src;
    0 D+ Y1 ]  U( b- i        int c, i, cnt = 0, err = 0;
    4 k4 e1 O. I5 U. z$ O5 K! ^        unsigned long total_size;
    - R7 O) N; E% p# U5 K+ C+ S. @4 q2 w; X  P
            if (!info || ! info->screen_base) /* screen_base是虚拟(显存)基地址 */
    ) y0 G; K) F" g                return -ENODEV;
    , y+ f$ S7 r+ l2 \3 b. j% u3 P7 |. X" ^+ ?, H9 q
            if (info->state != FBINFO_STATE_RUNNING)6 `" B9 R( c; Y* F5 t
                    return -EPERM;                /* 禁止操作 */
    ! r8 X3 k, `# |$ x6 S# [  v0 S( Y, K: u. @) V: w' l9 h( q- B2 o
            /* 如果registered_fb[]项里面提供了fb_read()函数,就调用下面的函数 */0 \9 O+ U" d. y. @- D* U; ?
            if (info->fbops->fb_read)
    7 I1 k2 c' R& J' Y7 B8 `1 S# I1 \                return info->fbops->fb_read(info, buf, count, ppos);0 u* h% q% L, B
    / u% C) ?) k0 n! s
            /* 没有默认的读函数就从下面的screen_base里读数据 */: X' i" s# C( ]5 E$ L" y. r3 w
            total_size = info->screen_size;  /* x*y*4,x,y分别为屏幕分辨率 */9 M4 B0 n) v" ~( v7 I5 r
    ( g1 a2 f& N% [( M  i
            if (total_size == 0)
    9 T* L% e+ ~* h0 _                total_size = info->fix.smem_len;        /* fb缓冲区的长度 */
    ) t3 p9 Z" C# V$ s  Q8 f: ?1 Z! S. ^
            if (p >= total_size)                        /* 调整读的偏移位置 *// m5 A; m# T' f  h6 d" e. ?
                    return 0;2 I+ f! K- W- g

    & w9 |: H5 q% K2 Q1 F: c6 f        if (count >= total_size)
    6 ^  m) f5 O) s, s; \% K                count = total_size;                  /* 一次性最多读多少个字节 */1 D. j% T5 _5 {% d' Z
    ' T6 {* S" y9 ?
            if (count + p > total_size)5 g' Z6 N1 f, J
                    count = total_size - p;                /* 调整读的位置及能读多少字节 */
    ' B) s. T% ]5 Z# z
    3 ^# Z# B; W; R. K7 Y        /* 分配内存,最大分配4K的大小 */9 u. H: w; G* [/ {/ f
            buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count,
    3 V$ P/ M2 w& o0 e. L$ R5 W                         GFP_KERNEL);               
    ' x/ F# {2 f# w( [% B/ w" \8 ^        if (!buffer)- T, L2 A: r* v
                    return -ENOMEM;
    * r2 ^+ Y3 j" H* E% e0 `
      R+ D- K% E( H; O4 F5 l* J) w        src = (u32 __iomem *) (info->screen_base + p);  /* 源虚拟基地址 */
    # A4 [# W4 N' u/ B; g. E  e! U: c$ t# u9 P) a; `/ q
            /* 如果registered_fb[]项里面提供了fb_sync()函数,就调用下面的函数 */5 t$ A5 {3 a# E, e% a$ m- h" y6 f5 v
            if (info->fbops->fb_sync)               
    ' @5 c! P+ F5 P$ h6 x' Y, p                info->fbops->fb_sync(info);
    # L9 J  j1 \8 h, S: U# ?6 E* a$ m3 A' R! R
            while (count) {
    ! {* H: X! [+ |9 C" s7 l                /* 读多少计数变量,单位为byte */
    4 B) L: Q. C# y) G, L: h                c  = (count > PAGE_SIZE) ? PAGE_SIZE : count;
    5 y9 X& Q) ^) X% y0 s$ a               
    & ]: r- h0 [# e4 r7 C                /* buffer是指向刚分配内存的首地址的指针 */
    " q- O/ D3 s% [) e                dst = buffer;        /* dst指针指向buffer */
    3 F0 D1 G4 B+ [0 c2 S9 k9 ?3 R" y$ b
                    /* 先除以4,因为每次读4个字节 */; U4 c& ~8 F3 g1 P8 T% @
                    for (i = c >> 2; i--; )                        ( b/ z+ V0 {& H
                            *dst++ = fb_readl(src++);        /* 拷贝源虚拟机基地址的数据到目标地址 */
    * i! W& R' w2 `4 J7 p* M! \$ s$ Q* S6 o" Y( J/ l8 Z
                    /* 判断是否以字节为单位来读取 */
      |, h, A/ _+ K1 i! `- c                if (c & 3) {                                        5 a3 K2 m, a4 Y8 @% `
                            u8 *dst8 = (u8 *) dst;
    + B- M5 U# ^# F9 v7 D: p% S- S4 D                        u8 __iomem *src8 = (u8 __iomem *) src;5 X4 \  r. z: R( L0 G

    ) h3 |8 X7 ?1 q; B                        for (i = c & 3; i--;)
    4 M+ G9 A! ^' E" b                                *dst8++ = fb_readb(src8++);/* 拷贝源虚拟机基地址的数据到目标地址 */
    ! `: q1 v% `" j0 v  n
    ( x% K" i5 E, @( ^7 r                        src = (u32 __iomem *) src8;) k( O( `4 N) O  L, S, v  |
                    }
    ( ~7 A# |3 m1 d$ o& d
    : B# f) l' o; S, K: `) A+ I* g                /* 从内核刚申请内存的地址buffer拷贝c长度的数据到用户空间的buf里去 */
    9 Z) E- ?& n: z8 T$ r                if (copy_to_user(buf, buffer, c)) {
    . J; I) o: g2 y' \5 y                        err = -EFAULT;        /* 成功拷贝,则err返回值为0 */
    $ V9 {# m% l+ K3 J                        break;
    * a3 Z6 K! C( j: A                }" [+ ~0 p3 e1 W- [) S+ A% z  c
                    *ppos += c;                /* 调整偏移位置 */
    * k  A* D2 `8 L; d" u& Z                buf += c;                /* 调整用户的buf */4 K; C" m( N3 k" |
                    cnt += c;/ Z( Q, f8 B  n1 S5 f
                    /* count变量减去已经读取的c数量,用于判断while(count)是否为真*/- F$ g, N0 P. z* f" n
                    count -= c;                - R+ Y+ U% n- z2 ~* h
            }( u$ Q4 w0 L5 {0 B9 Z# E  N9 K0 U+ d
    . H! w: q1 |& a8 q% ~: U
            kfree(buffer);                /* 释放内存 */
    ; e) g1 @- Z3 I+ C5 U  g
    - f. R2 Y0 D& F- Z. ^* e0 u0 V        return (err) ? err : cnt;        /* err = 0时,返回被拷贝成功的数量cnt *// O! o( K, k: {; _3 g
    }
    0 [! u2 m6 c2 @$ I6 j* Afb_write函数源码分析1 `* F2 e/ K' A! r1 u' c" r

    2 c. A: w) t( \* o" k* h9 @static ssize_t; b9 r4 o* e7 V8 ]
    fb_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
    : i' m) x( j0 D0 ~& |{
    2 s( l5 B- S: ^4 J6 s( q! T        unsigned long p = *ppos;
    0 L' t& ^* L# Q- r& N        struct inode *inode = file->f_path.dentry->d_inode;
    % n6 g- i$ X1 z+ g' t1 N: w& h        int fbidx = iminor(inode);+ b" _1 W8 E7 Y" @; c0 ~
            struct fb_info *info = registered_fb[fbidx];4 Y  P: e, \& d+ Q0 c# u' V5 _
            u32 *buffer, *src;. h( q& T" N9 h
            u32 __iomem *dst;  X* A; R$ B. I# P  x% R. c* T
            int c, i, cnt = 0, err = 0;+ W9 D6 |6 j9 E' B
            unsigned long total_size;
    ; x& `$ v0 C! A& K" n: P; c6 n) u: ?* k" ~
            if (!info || !info->screen_base)
    - e% z1 ^* U- U# {; p# E                return -ENODEV;- |8 W% a5 X* G) V* K+ L) o3 i7 z
    ' f1 n$ \; d, M( [4 f9 E
            if (info->state != FBINFO_STATE_RUNNING)
      n+ m3 u0 G. Q8 D' {                return -EPERM;
    $ x0 Q- `/ Z; \" j  @; Y" A' j" i6 o
            if (info->fbops->fb_write)
    $ B, d, i/ z: L9 w2 O7 [0 ]+ O                return info->fbops->fb_write(info, buf, count, ppos);
    6 k$ O) R/ N1 c7 W, Z$ X       
    1 e8 I& [* h% p  j. j1 J' o) f        total_size = info->screen_size;
    0 U. Y7 c, ^( M2 s% P  }* f; z5 k* D0 `7 d4 `5 T& W) Z; x
            if (total_size == 0)
      f! b4 n7 a8 M) ]: E8 n                total_size = info->fix.smem_len;
    . M) E. v; }* @, b; i1 {. [5 [+ S7 D6 B2 D
            if (p > total_size)
    2 s! X: \. p0 j; W                return -EFBIG;. A  y1 d" D6 o! ]
    ! C9 u5 V1 \- Q+ k( Y- M/ l7 `# |
            if (count > total_size) {4 N4 ?! M; g6 [
                    err = -EFBIG;
    # S* V* r8 v& _8 B& n                count = total_size;
    ( Y/ ?# V- c% Z% W2 `+ P0 l        }+ N/ E, J7 E& @, X: K* b2 o4 y

    ! H1 G# V' q( ^% ?& ?- Z% q+ ?  y        if (count + p > total_size) {$ I$ G) S  ], c: ^
                    if (!err)
    & E' {& h; X5 C" ^( z+ T( h                        err = -ENOSPC;
    : X4 a( [3 M( U/ m$ |" R- v: _' }# D  U# @
                    count = total_size - p;* w$ z* }) ?, s; s7 S5 ?9 Y& I* E
            }) i; M5 E! Y3 F; Z# v) G
    0 c" }( |; I. C: Y
            buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count,
    " n$ R- G/ c9 M9 E( @# l# q5 e                         GFP_KERNEL);
    & D& z% s6 r& D# M5 c/ J2 ~        if (!buffer)8 z2 u0 P7 m% |
                    return -ENOMEM;
    4 j9 u: K9 c6 u4 v6 w" r' }% P2 u8 t  a) f1 g4 L& b0 R7 L- h# }
            dst = (u32 __iomem *) (info->screen_base + p);        /* 源虚拟基地址 */
    8 D$ o0 t. {# F5 }( ^
    - v1 o4 P6 Y# I" q" D& ^1 a        if (info->fbops->fb_sync)" b( Y* ]2 i/ ~! I" y8 T9 e0 ~
                    info->fbops->fb_sync(info);
    . c$ t) b) y, _7 G7 w4 n0 M+ n$ c. g& y  n8 [; I  o) t
            while (count) {  c% E/ P0 T* R$ u3 h+ F6 E
                    c = (count > PAGE_SIZE) ? PAGE_SIZE : count;4 X. D, Z% a, i) u
                    src = buffer;        /* buffer为指向刚申请的内存的指针 */
    1 _7 P8 B, I1 W- |# N( e
    / f( o+ q: w& \* K+ Z% I1 k7 `9 ~& |$ {
                    /* 从用户空间的buf地址里拷贝c长度的数据到src内存里,成功时返回0 */" F( |& n2 r  v' T
                    if (copy_from_user(src, buf, c)) {
    - i" Q/ i9 }+ d3 I8 T; D% K9 V! F$ E                        err = -EFAULT;1 C" z" C' ~7 W6 r# h
                            break;6 S/ C$ w) C4 D) _
                    }% r' @2 f9 `: {, f/ ^/ g3 O
    ' p' F7 i& X9 K/ p- z. n
                    for (i = c >> 2; i--; )                        /* 以4字节为单位拷贝数据 */
    6 [2 t3 i2 R* B+ A5 J, {  G                        fb_writel(*src++, dst++);        /*     *dst++ = *src++     */" @, `* N# r- ]6 C6 M/ y& Y
    0 U1 L) d& N* v
                    if (c & 3) {                                        /* 以字节为单位拷贝数据 */
    4 f" E; [! y6 W5 `% Z0 A  n                        u8 *src8 = (u8 *) src;
    ( h+ ~5 B6 p. _' {                        u8 __iomem *dst8 = (u8 __iomem *) dst;6 V1 ]! n+ M; c7 l* r( g

    6 }- c' v/ @) A& s- s: q2 [$ s0 f" `                        for (i = c & 3; i--; )
      q: \) h- h& K$ b9 [  T( ?                                fb_writeb(*src8++, dst8++);
    $ R: a: J/ S# W$ j' D9 ^
    . e! m. H1 y) C: Q3 h& f5 Z' g                        dst = (u32 __iomem *) dst8;( U% v, y" ?" v% V. V
                    }
    6 ~  D$ `5 X7 A7 ?
    - r' |) y/ G% J7 N) ?" r1 v( `- W  \                *ppos += c;
    : U3 U1 m! ?) y% V$ U, T, {                buf += c;
    . c: W' \& {1 r$ `4 A4 V: f( H7 |5 G0 p                cnt += c;. p- E9 t. A3 K
                    count -= c;
    % e% p7 M, Z; Z5 q        }
    6 d  ~3 ~5 S& r) Q$ C& S: v0 G, ?- y" Y) m) d2 Z3 g, |
            kfree(buffer);5 ]% w' `/ x- l( G

    + Z  s+ r( B8 u- d- g5 n( A! E        return (cnt) ? cnt : err;
    / J8 Q3 d" u5 r! O' D2 b$ B4 I}& n: L) z  J! E; g3 e. h& P% D
    fb_ioctl函数源码分析. ~5 |/ p- p" @9 j: L6 H: A: f5 g

    ! O* @! E5 y$ ^static long fb_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
    % j, r4 c, i# z) c' [{% x4 \+ b  g3 o% q9 R" J
            struct inode *inode = file->f_path.dentry->d_inode;: a7 m" \6 H7 f# b
            int fbidx = iminor(inode);
    : l6 t$ ~1 a. I4 t) W2 \  j+ u$ z        struct fb_info *info = registered_fb[fbidx];
    7 j7 D) ^& ~+ \( `$ L1 i
    . M9 e% \4 o% o3 P        /* 这个才是真正的fb_ioctl驱动函数 */7 ^) o( D- L9 p% O; G- q
            return do_fb_ioctl(info, cmd, arg);       
    8 [. b1 ^8 G" D: t}$ y; r+ t+ A) m, \2 D
    do_fb_ioctl函数根据cmd来设置各种命令,这里仅举例说明:; H- s5 E6 N2 d% F+ ^
    $ x+ C9 x: C% t; f7 w
    switch (cmd) {9 J+ Q6 M& }) h4 Z& i
            case FBIOGET_VSCREENINFO:                /* 获得可变的屏幕参数 */) v) @0 `4 g' Z8 k7 _+ p5 z
                    if (!lock_fb_info(info))        /* 如果info->fbops不为空,则上锁,成功返回1 */
    5 K$ |' T( {0 Z/ d5 o# p                        return -ENODEV;$ I  @6 @5 Z6 o- Q4 G" X
                    var = info->var;                        /* 可变参数变量的设置 */
    + ^( `0 H) s0 P& a6 W2 H                unlock_fb_info(info);                /* 解锁 */+ C  ]- S0 z  H  V; ?; `2 k3 k. f0 u

    % c* [& n( z1 J; J( A, m3 p! g                /* 从内核空间的var地址拷贝var大小的数据到用户空间的argp地址里去 */
    0 N( H1 |3 g' c                ret = copy_to_user(argp, &var, sizeof(var)) ? -EFAULT : 0; /* 成功返回0 */
    6 |+ z  A/ I# c                break;! @0 F. X8 g% l
    fb_mmap源码分析:
      C3 L& N5 O5 |1 _) {! {$ q) D) O- x  h, F
    /* 这里分配的显存是在内核空间分配的,用户空间并不能直接访问,% z9 J$ V! w2 L- _
    * 所以需要用到这里的mmap函数,直接将这段内存空间映射到) y( p# s8 s2 M
    * 用户空间去,用户空间就能访问这段内存空间了。
    ( M2 |5 [% V' k- m+ ?0 Y/ T */
    * p5 M3 A% B9 h; Z4 N  ?static int
    ! W3 E; `% w7 a/ Efb_mmap(struct file *file, struct vm_area_struct * vma)
      Y7 t$ c: `* R3 O( D6 ~__acquires(&info->lock), Q; u& a! R4 b8 b
    __releases(&info->lock)
    8 m5 T2 n& [- E. ^6 @5 G8 G{9 p9 B8 I0 t2 S& S& F
            int fbidx = iminor(file->f_path.dentry->d_inode);, X: o* ]% S* k! ?9 b' A' ]3 B
            struct fb_info *info = registered_fb[fbidx]; /* 通过次设备号找到fb_info结构体 */
    & a* ~& I$ G7 h9 r( e4 d1 b. b2 [% Q        struct fb_ops *fb = info->fbops;
    * ~, d4 P3 k' O- N7 w0 t7 c        unsigned long off;
    / m* A. s+ M5 y' |        unsigned long start;, P! X! }, _; v- Z9 l
            u32 len;
      u8 J; v$ O% b3 k" @
    ( N( |3 f. q! e5 X: G  e$ X        if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT))
    ) {5 P9 I7 P2 [- }& q/ n' {                return -EINVAL;& H- C% }7 {) T; d% h# L6 w" ?
            off = vma->vm_pgoff << PAGE_SHIFT;6 X8 s3 K$ r, }$ ?* x3 \  d
            if (!fb)
    $ l! M7 B6 A# r2 h                return -ENODEV;6 F( t/ {( m6 }" I- O
    ; M  B! ^, a) O: c' v
            /* 如果registered_fb[]里有默认的fb_mmap就使用它 */
    ) b7 `9 M' `/ }2 Z  ~        if (fb->fb_mmap) {
    , n$ H8 ?" A- u  F7 o5 x                int res;
    / G+ T: q$ Z/ O2 T  F9 [                mutex_lock(&info->lock);5 M, g, o# N7 X0 J/ R- j
                    res = fb->fb_mmap(info, vma);
    ) }0 e; n  A7 _                mutex_unlock(&info->lock);
    8 u* F* |) R" j& z6 ~2 N3 r  }* f+ u                return res;( V! @5 n5 ~/ F0 b
            }
    2 d0 T' P2 |" r& i2 _* N' K/ ]& d
            mutex_lock(&info->lock);
    ) s4 F8 g2 \! o( U% N$ u6 t8 X/ Y& A1 t; N" _/ m0 V4 k- B8 ?- w
            /* frame buffer memory */" p9 Y% l( ?2 `
            start = info->fix.smem_start;        /* fb缓冲内存的开始位置(物理地址) */
    " }$ J6 r$ U& S! \        len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.smem_len);
    0 v& c& _2 I4 I+ M/ c6 ]        if (off >= len) {                                /* 偏移值大于len长度 */
    ) ]2 O9 w7 ^+ e4 a# A# s: `! k                /* memory mapped io */                /* 内存映射的IO */
    1 T! s0 F( C, C9 M, T$ o$ _7 D                off -= len;% i* M" p% w, |2 F' C4 e5 ^
                    if (info->var.accel_flags) {* f3 J* \0 _. U4 |
                            mutex_unlock(&info->lock);8 K4 B: A" e" U2 B
                            return -EINVAL;9 s7 u6 e( k* t0 z1 f) n
                    }
    # j' v3 b! F" [4 s6 O* T, b                start = info->fix.mmio_start;
    3 h% u3 ~! U: N! N  P" g- f8 T                len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.mmio_len);) y7 l4 U2 Y1 h( H; Q
            }
    5 V5 v/ z" w/ P9 ]! G        mutex_unlock(&info->lock);+ p' C. S2 G6 f  |1 I  P2 j
            start &= PAGE_MASK;
    & h( m- e" {3 F& J" h8 z  ~        if ((vma->vm_end - vma->vm_start + off) > len)/ D3 w% X  F/ P3 c
                    return -EINVAL;$ _( e) A+ ?  `! m: e3 Z7 {
            off += start;2 T9 @8 q) x6 \$ O! M& i+ A1 E$ e
            vma->vm_pgoff = off >> PAGE_SHIFT;/ ~& O( U% N3 Z# e8 q) q: W" B
            /* This is an IO map - tell maydump to skip this VMA */% W& B% C* s, Q7 ]
            vma->vm_flags |= VM_IO | VM_RESERVED;
    , ]' s) S9 r" p% k6 Q* _) F8 n) _# H4 U        fb_pgprotect(file, vma, off);
    7 Z9 V' Z' L- u" \- M; [
    & e: P* S& |/ z6 p        /* io_remap_pfn_range正式映射物理内存到用户空间虚拟地址 */7 O4 g1 V8 j( A) E  T) ]
            if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT,
    ( }2 x/ P: Y4 I% }1 G                             vma->vm_end - vma->vm_start, vma->vm_page_prot)), X* v& H% D7 \5 m- Z
                    return -EAGAIN;4 {% `, E# W! {+ k9 R6 }
            return 0;
    . M/ V) D) ~& G/ }+ e- [1 Q}
    2 q' g. f; U7 E' e4 {问:怎么写LCD驱动程序?* R3 o! Z- A2 g7 x( w4 \
    1. 分配一个fb_info结构体: framebuffer_alloc( R' V: D* ?4 c/ {7 m& k
    2. 设置
    . U2 A0 @. Q$ G" P3. 注册: register_framebuffer0 w( g; a. n9 a7 ~4 R5 q' }
    4. 硬件相关的操作9 o5 O: U6 e3 V, Y4 h

    # z# Y* I& \9 h  M
    - s+ J' H4 a$ ?7 f: J
    5 q" g: R$ q7 L1 A
  • TA的每日心情

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

    [LV.1]初来乍到

    2#
    发表于 2020-4-26 14:08 | 只看该作者
    linux lcd设备驱动剖析三
    您需要登录后才可以回帖 登录 | 注册

    本版积分规则

    关闭

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

    EDA365公众号

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

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

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

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

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