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

linux lcd设备驱动剖析三

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

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

    [LV.1]初来乍到

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

    EDA365欢迎您登录!

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

    x
    上一节文章中详细地剖析了probe函数,但是从始至终都没有看到打开读写文件接口的操作函数,只看到了下面这个操作结构体
    & G' W6 ^( M7 h. G: u, c2 v- }! O2 j5 y

    1 t1 O: @, R% a' @+ ?& A+ ^static struct fb_ops s3c2410fb_ops = {& V7 s+ K9 C) h
            .owner                        = THIS_MODULE,+ ?9 C/ n3 d0 g
            .fb_check_var        = s3c2410fb_check_var,, O1 U& g% b2 Y
            .fb_set_par                = s3c2410fb_set_par,
      Z& s  M. r7 C& ?2 ?/ b( C        .fb_blank                = s3c2410fb_blank,
    $ ^2 [  l3 A& ~4 s: ?2 E. L  Z        .fb_setcolreg        = s3c2410fb_setcolreg,* {& v" l. V9 u* y
            .fb_fillrect        = cfb_fillrect,
    $ V9 H, \% \5 q& u4 N9 l, n        .fb_copyarea        = cfb_copyarea,) v7 R- D) j+ y6 [6 U
            .fb_imageblit        = cfb_imageblit,
    5 f& Z$ l7 P4 K3 v5 V, z4 D, i};
    & F% ], K% z0 k. l2 l这并不是我们想要的打开读写操作函数。上一节文章链接:http://blog.csdn.net/lwj103862095/article/details/18189765
    & k0 j/ w/ s6 o: @) g问:那到底帧缓冲设备的文件操作结构体在哪里呢?
    3 M5 b4 v  G+ c0 y答:在drivers/vedio/fbmem.c文件里。4 I& `5 C+ c. V2 \, }3 w
    ! l+ B5 K% W* O2 Q* E: g+ _; E
    从入口函数开始看:
    2 z1 K. \; m; c; d2 N* U* v5 ]4 ~2 |5 A0 h* P

    * T4 I; T9 k/ P8 l3 q( r. ]2 Cstatic int __init
    ' s: E* b( {  R1 E8 a/ y" q& gfbmem_init(void)/ h, B& B% ]4 {1 D$ |  Q! y
    {8 G% C% @4 A( D- _% `, F7 ^: M: `8 q8 B
            proc_create("fb", 0, NULL, &fb_proc_fops);
    + `; Z; j( A* ~$ l
    8 H. r" f% l! v# ?  h* d        if (register_chrdev(FB_MAJOR,"fb",&fb_fops))) Y, C6 s" H6 t
                    printk("unable to get major %d for fb devs\n", FB_MAJOR);
    ; o  Z4 c$ _- x/ d7 |4 q; c, r: G
    $ c4 O4 ~. ~# _9 S/ t+ S        fb_class = class_create(THIS_MODULE, "graphics");
    % D$ K" v& _9 p        if (IS_ERR(fb_class)) {8 o% P- Y; u8 Z5 r& j5 Q0 @
                    printk(KERN_WARNING "Unable to create fb class; errno = %ld\n", PTR_ERR(fb_class));
    9 X9 o) w0 C+ U                fb_class = NULL;
    / Q. a: S, T# ~" x: |1 g0 m        }
    ' ~  l; b3 X  ?        return 0;1 E) @0 ^5 L' L6 i7 T: w
    }
    * G: ^! ~  m+ K% C- r* dfbmem_init注册了一个主设备号为29的字符设备,并创了graphics类(图形类)。
    , Y$ m9 q5 [# R+ Y! u0 x4 J字符设备有一个关键的成员是文件操作结构体9 m$ h& ?  q7 K0 g$ w' d8 C4 C

    8 E: h2 N8 C8 F9 R
    , J" \8 H/ w3 e6 v/ t1 E+ R& E( Mstatic const struct file_operations fb_fops = {  T; L* Y6 N! s4 M- Q9 a6 D! g5 J
            .owner                         = THIS_MODULE,# {  g3 j1 Z$ E
            .read                         = fb_read,, J) K3 r+ J9 |, I* J6 P9 J
            .write                         = fb_write,
    ! }& {1 D' P% ?& A% M. b$ e4 j        .unlocked_ioctl = fb_ioctl,
    0 I! e0 u# D! I6 u; e0 [# K; K        .mmap                         = fb_mmap,2 O/ ]$ b* Y+ s" E4 J" [
            .open                         = fb_open,- I* I( ]& H* y
            .release                 = fb_release,: \! I3 W2 c! u3 a8 H3 K& p) ?
    #ifdef HAVE_ARCH_FB_UNMAPPED_AREA
    5 m0 w! ?1 F- l3 a2 Z% @        .get_unmapped_area = get_fb_unmapped_area,
      v3 H$ Z* p; h* k8 {% N#endif
    : j; O$ O6 r4 K- v};
    8 c+ i. r* G: A9 c理所当然的,我们应当首先看的函数是open函数
    ! J" Z" h4 Y) D6 k, |; X
    + h( W3 }# O0 x2 O' Pstatic int
    : i# I; @& H* I- B7 L: K9 mfb_open(struct inode *inode, struct file *file)0 b8 U' n9 ^6 O+ [$ N' h0 @
    __acquires(&info->lock)
    * q# T- A8 k/ R- n9 T3 @5 L__releases(&info->lock). f9 |1 l: V! x7 Y' c% k! \7 _' U' |
    {0 m. Y: J3 g: @; e! G% D2 H
            int fbidx = iminor(inode);                /* 得到次设备号 */6 `+ {+ B' Q5 W: v* b" d
            struct fb_info *info;
    - S; |' C) V6 {! j) M7 i' S; c0 i" z        int res = 0;6 _5 p* [4 z' P" `" ~" H
    ; u2 }3 I7 m1 V! c2 V- O  a
            if (fbidx >= FB_MAX)                        /* 次设备号有没有大于规定的最大值32 */6 Y9 `4 W) x8 m- c4 I( s$ |. [
                    return -ENODEV;                                /* 没有这样的设备 */
    5 R; h0 I( f0 k$ B, i. Y7 u        info = registered_fb[fbidx];        /* 使用次设备号得到fb_info结构体 */
    8 K8 `* Z( M. k% T        if (!info)
    # d1 d4 d# ?+ V  k6 l3 v; L4 z1 @* B                request_module("fb%d", fbidx);
    $ N* `, v. h+ m! ]! C! t2 ~. F- ~
    # f" M+ M! K4 [. U/ [. z# [; {        /* 再次使用次设备号得到fb_info结构体 */
    # a% K9 ]# ?8 b  M* X1 g+ Q        info = registered_fb[fbidx];3 m6 \+ a9 A4 E8 T& p* {$ E
            if (!info)
    ! N' s8 E5 N. |4 B                return -ENODEV;! ]8 p! L7 N" n! D) M( T
            , n7 @+ w% V6 c
            mutex_lock(&info->lock);                /* 获取mutex */8 n' |) q$ o: d4 q! O5 B1 P% A# h

    4 {' z( H' C/ }1 D- R. J        /* 获取模块使用计数module,成功返回非NULL */
    : Q  C  h5 M( @/ N. ?* `        if (!try_module_get(info->fbops->owner)) {
    8 g) k7 @& n+ E8 `; g                res = -ENODEV;+ w: C. c, j  h7 F, U, M" q
                    goto out;/ v0 f( c5 P' v7 J7 L6 U. Z
            }. R% w  J! \' R) j" N/ N6 |  J% E
    - V% s& \! x) `6 y6 ~7 t
            /* 从registered_fb[]数组项里找到一个fb_info结构体保存到: a  h2 l( s9 a
             * struct file结构中的私有信息指针赋值给它呢是为了以后调用& x3 f" R: O8 N" S! y( D
             * read、write、ioctl等系统调用时找到这个struct fb_info结构: h( \& E7 V( ]1 {2 @, \
             */+ d8 H& `, Z4 h& ~/ N. I
            file->private_data = info;       
    ) m+ @! K8 c0 P4 n) q. {" d/ A( m4 k9 `; I
            /* registered_fb[]数组项里有没有默认的fb_open函数,如果有就使用它 */- U" \( E! |$ t/ t& U7 r- t9 T6 [
            if (info->fbops->fb_open) {) T! m6 W2 ]0 t
                    res = info->fbops->fb_open(info,1);        " M4 N/ [. `4 Y- z+ Y2 ~3 n
    8 G7 w3 q6 N! A/ G$ Q! j' W
                    /* 有默认的fb_open并成功打开就删除模块计数 */. J) O" y+ p) ^) n1 D
                    if (res)( T# I6 i9 U: m8 j! d5 r
                            module_put(info->fbops->owner);2 b* ]2 g; F- [: U* l# q
            }
    ; O$ A. ~. ]8 B6 u. w! P( T#ifdef CONFIG_FB_DEFERRED_IO                /* 这里没有定义,不用理会 */! B3 G% I0 Z1 _/ V+ Q3 o0 b
            if (info->fbdefio)0 o* J: E5 ^; c! S: X
                    fb_deferred_io_open(info, inode, file);
    ( \- u) C. P* W$ n& o  M$ ~#endif
    " H5 g$ \' p' n3 c. e% rout:2 M& u) F0 S+ y9 f
            mutex_unlock(&info->lock);        /* 释放mutex */
    ' j, U& \$ G. q5 _* v        return res;
    # F( s* C5 y4 w1 y}
    " j& R! o6 q8 S" Z* f2 e发现fb_open函数是围绕fb_info来实现的,而fb_info设置为registered_fb[fbidx]
    ; \! [# l) O3 c0 w7 t0 k问:registered_fb[fbidx]结构体数组是在哪里被设置?; C6 e; ~% d5 D& ?5 o! W; d
    4 v8 Z  g& J* W( S0 \1 x3 l4 {2 ?; v
    答:register_framebuffer函数里设置registered_fb9 r( |; M. ]0 V

    * H7 k! d3 |* `4 b; T2 Z' ^" M9 }1 X5 v; Q
    /* register_framebuffer()函数的主要工作是设置fb_info结构体的一些成员 */# R! ~# |; n$ r# c
    int' Z5 d0 x' u9 q5 q/ Z
    register_framebuffer(struct fb_info *fb_info)
    ' k) A% J5 M+ d" U$ v{! b4 w; E# m* q) r! M
            int i;
    ; s% a) ]; s4 n- X* }& b        struct fb_event event;
    $ T' w+ |. S- q& W5 W4 Z+ s/ f        struct fb_videomode mode;. ?( p8 u) t: }9 O. c/ p" U& d! Y, s

    ) L: Q+ j$ D# r  b+ n6 m        /* num_registered_fb代表注册帧缓冲设备的个数 */& [) K3 e5 c  A. b+ U
            if (num_registered_fb == FB_MAX)
    0 D) m1 p8 C; Q5 p1 ^3 A                return -ENXIO;4 y5 i; V3 u6 y* M
    & x! T3 ^! T9 m5 o6 M
            if (fb_check_foreignness(fb_info))
    * \. Z- ?3 }- r& S  r                return -ENOSYS;
    * F& C' p# b' L
    1 M5 N3 M0 w- [, f; Z5 [4 k        num_registered_fb++;+ s0 ]4 u. _/ z+ r  @+ @' L6 p6 Q

    4 ~  f. A( N) z0 v; I, M' U1 K        /* 当registered_fb[]项都为NULL,就会break,找到一个空的次设备号 */
    8 x7 |, O! Y2 O; M# a        for (i = 0 ; i < FB_MAX; i++), k* k* H5 ]: l  y7 `, T& {
                    if (!registered_fb[i])        : ^3 S+ S$ K2 p
                            break;
    , T3 n& }% x! @5 C        fb_info->node = i;
    & \6 w3 D  l$ |4 E% N( a        mutex_init(&fb_info->lock);                /* 初始化mutex */
    5 W# Z3 t& _: M! b: k- A8 p5 J, v4 d: D6 g2 J- |+ @9 S
            /* 因为在init加载函数里只创建了类,这里在类下面创建设备 */4 y0 n$ q8 h" [4 y" \
            fb_info->dev = device_create(fb_class, fb_info->device,. n7 Y  ?8 z( A6 e7 r: M
                                         MKDEV(FB_MAJOR, i), NULL, "fb%d", i);
    4 J8 E0 o6 J$ N: t8 q/ x1 O3 _        if (IS_ERR(fb_info->dev)) {3 E- C+ ]0 e3 q; y5 t( n2 C* a& f
                    /* Not fatal */
    * V" \* B( [6 w5 |' X                printk(KERN_WARNING "Unable to create device for framebuffer %d; errno = %ld\n", i, PTR_ERR(fb_info->dev));
    # k+ t) t# V* m( T+ ]/ B                fb_info->dev = NULL;
    5 H. g% _% V' n8 {9 S        } else7 j' Q8 P; P, z! [: F% ^
                    fb_init_device(fb_info);        /* 对struct fb_info做一些初始化 */9 L8 j$ U+ A, f" m

    ' h: R3 y% T; c1 a. H5 }
    + X5 f: p6 D3 D" [; k6 ~, T        /* 初始化fb_info->pixmap结构体成员 *// L$ r; t/ ~3 Z+ Z! W
            if (fb_info->pixmap.addr == NULL) {
    + E0 K: Q) ?, `8 }6 y# r                fb_info->pixmap.addr = kmalloc(FBPIXMAPSIZE, GFP_KERNEL); /* 8K大小 */% E3 w! O8 T7 ~3 ~
                    if (fb_info->pixmap.addr) {
    " G6 _- D* B, |                        fb_info->pixmap.size = FBPIXMAPSIZE;        /* 8K */
    , |' i& T) K, J# S: E                        fb_info->pixmap.buf_align = 1;
    3 k" S0 c* o4 h8 I: v3 p/ j  ~                        fb_info->pixmap.scan_align = 1;
    9 b& K5 r! P( B                        fb_info->pixmap.access_align = 32;
    1 X3 a* z; e& ^. g' l                        fb_info->pixmap.flags = FB_PIXMAP_DEFAULT;
    & T% ?- S; ^( {6 e2 h% J; a                }
    9 Q# K0 @& n8 k7 F4 f0 O, Y9 i        }        . T2 J1 f+ \8 ]: z6 y, U
            fb_info->pixmap.offset = 0;
    % G( x0 [, m% K% S0 W6 n3 w# r7 u1 W, k# y- T' p
            if (!fb_info->pixmap.blit_x)
    1 @; B3 {% N; b! b( e                fb_info->pixmap.blit_x = ~(u32)0;0 N9 I% X8 {; A3 T5 H: Y

    ! F4 D' M+ c  a+ L        if (!fb_info->pixmap.blit_y): ]3 P! B" C0 V
                    fb_info->pixmap.blit_y = ~(u32)0;
    / y; S' e6 A; \. g* Z: q! y
    " N: I7 {' X8 R8 d9 V( k* f        if (!fb_info->modelist.prev || !fb_info->modelist.next)! u  R$ Y; e) @6 {/ A0 S# k% w! E/ e
                    INIT_LIST_HEAD(&fb_info->modelist);        /* 初始化modelist链表 */3 x$ h& t- {3 l3 ^: ~* _& ^

    ! n% b) M" e. Y5 g8 G/ K        fb_var_to_videomode(&mode, &fb_info->var);3 e7 z7 i: Y) S) q1 L/ x
            fb_add_videomode(&mode, &fb_info->modelist);
      v9 Z5 P: z6 ^8 r9 K1 y5 z6 @4 L" ?, n# m, U* x4 p0 z# I  ^
            /* registered_fb[]数组项在这里被设置 */
    : p, f1 c9 N1 e6 `. F- R        registered_fb[i] = fb_info;- i$ \* O. b. U$ K0 q4 D

    4 q; Z  f( z% z7 ?        event.info = fb_info;
    2 Q1 {% E9 ]( T$ U# R% c) t0 ~; J3 O6 b/ n7 E2 f7 w( l0 m2 E
            ) K( q- G" E0 {6 v( A$ \
            if (!lock_fb_info(fb_info)) /* 上锁 */
    ( R# q1 N, H# G; w2 b7 Z                return -ENODEV;
    $ T/ R, Y) f/ G; g: I4 `        fb_notifier_call_chain(FB_EVENT_FB_REGISTERED, &event);; j5 N( ?+ [2 W
            unlock_fb_info(fb_info);        /* 解锁 */
    " i2 Y/ t* J: D) W! R/ \        return 0;" h0 s0 w% W* X1 d0 y5 w
    }
    : }1 j+ l$ J0 i6 i& T+ Dfb_read函数源码分析7 I' h2 Z1 z7 s9 n9 q3 R
    % V1 ?8 I/ Z9 i6 z5 B5 E! }+ r: D
    static ssize_t  d8 B8 x& M2 e
    fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)# y; d1 {5 l3 w2 |8 z/ _9 G
    {
    - j+ x: u1 @- u! R: d        unsigned long p = *ppos;, s/ b6 V9 [/ Z. G) y) `
            /* 通过file结构体的成员得到inode节点 */
    $ y3 V6 X! g  I2 C! m  v        struct inode *inode = file->f_path.dentry->d_inode;% T! E: A* r3 S2 Z8 v+ @! i

    ! t) _& U/ [; D        /* 获取次设备号 */
    2 [' I$ v9 k/ u& X: ?3 \        int fbidx = iminor(inode);
    + ~" E! O) }. \        /* 以次设备号为下标找到一项fb_info结构体 */
    5 S8 U" j/ X  z( h2 z4 U9 S4 J1 ]        struct fb_info *info = registered_fb[fbidx];
      v) W7 a9 {/ z& x+ H        ; D& v% N2 C" J6 g+ k3 A
            u32 *buffer, *dst;; L3 m. m$ I+ _) i
            u32 __iomem *src;  o; }+ G7 \* ^# J
            int c, i, cnt = 0, err = 0;
    9 V: ]% |" d. I) H4 C9 j        unsigned long total_size;
      |- y' j0 b( I. v* }
    4 O8 f. |: u4 j# H6 s* K; ~2 r        if (!info || ! info->screen_base) /* screen_base是虚拟(显存)基地址 */
    : ~- |6 X- Z# ~- j. j: S) c                return -ENODEV;
    4 l) m8 s" t/ l
    6 p4 P; F! U) W8 ?4 E1 _        if (info->state != FBINFO_STATE_RUNNING)3 Z8 l# Z9 L; |8 }7 z, a! W
                    return -EPERM;                /* 禁止操作 */' A4 B1 D" Q3 A! w$ ^& J5 E& U2 H

    , C( s# {9 h- }% M$ e; ]        /* 如果registered_fb[]项里面提供了fb_read()函数,就调用下面的函数 */. g5 h1 C5 o4 y' e, T" A& O
            if (info->fbops->fb_read)
    6 ^2 ^4 g, Q  n* Z9 W4 w" E                return info->fbops->fb_read(info, buf, count, ppos);2 B4 [1 Y  i8 i. e8 W

    ; [1 |1 U) ?5 {( W        /* 没有默认的读函数就从下面的screen_base里读数据 */
    / h; B/ `5 V- |, v        total_size = info->screen_size;  /* x*y*4,x,y分别为屏幕分辨率 */
    + l6 m0 D1 m* P6 Q) {" O8 Q, j, P3 K6 m5 O" @: }
            if (total_size == 0)$ T+ X) P1 Y0 k
                    total_size = info->fix.smem_len;        /* fb缓冲区的长度 */0 ~$ z' h' R6 q- ~3 W

    ( I1 E1 G$ j; j/ H7 I        if (p >= total_size)                        /* 调整读的偏移位置 */# \; t* ~0 V3 @7 q
                    return 0;
    , J4 }% Y' e/ {* Z9 }7 C( M! G; R, F+ Z4 w
            if (count >= total_size)6 Q- {/ h( }3 f5 u
                    count = total_size;                  /* 一次性最多读多少个字节 */
    4 S2 r/ g0 a2 `! s/ j
    : z' ~2 r, V" @: ]) e        if (count + p > total_size)
    2 o4 r# Q- n+ n) z/ _                count = total_size - p;                /* 调整读的位置及能读多少字节 */
    8 f# V& R+ b% x& b" I
    - ^2 }% o( ?3 d* `1 Q8 x& |        /* 分配内存,最大分配4K的大小 */
    # F9 B9 T' w4 L0 i( I        buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count,
    + H( w, [5 x' N* f9 X                         GFP_KERNEL);                ! g# k' l% R7 P! g
            if (!buffer)! L% N( t; j+ P
                    return -ENOMEM;- V3 @% D6 |# h

    , H: r# |0 w  [9 q, I        src = (u32 __iomem *) (info->screen_base + p);  /* 源虚拟基地址 */3 q/ B& f" q$ J- ~; j( |1 \: ]
      e; S0 E6 X8 @
            /* 如果registered_fb[]项里面提供了fb_sync()函数,就调用下面的函数 */, S2 W# E! C4 C% _5 C
            if (info->fbops->fb_sync)                ! ?8 q" ?/ M1 R
                    info->fbops->fb_sync(info);
    ; y! v8 \7 v8 M8 }; d* U% F8 D' D& _; D/ Q
            while (count) {  M- q( c- Q0 F
                    /* 读多少计数变量,单位为byte */
    2 o# q9 v* ?3 `4 P1 ]4 x                c  = (count > PAGE_SIZE) ? PAGE_SIZE : count;
    ) w- T2 l! d* |3 y* L# |               
    2 f% t' l  v0 n" k3 W! v) T                /* buffer是指向刚分配内存的首地址的指针 */) h/ {& [+ l: E7 [& s; H  c4 f7 _
                    dst = buffer;        /* dst指针指向buffer */2 \( z2 {  {4 I" X) {

    ( M1 F, b# R+ R                /* 先除以4,因为每次读4个字节 */- h" d$ S9 ~' B& N: Z+ Q) V
                    for (i = c >> 2; i--; )                        # [' F0 c$ u/ Q  ?. D  Q) @- g
                            *dst++ = fb_readl(src++);        /* 拷贝源虚拟机基地址的数据到目标地址 */$ N7 k% {0 H/ `% A$ K' F8 Y. b0 ]
    * U! W+ a- m4 {2 J; j7 O, a
                    /* 判断是否以字节为单位来读取 */5 \- ^' ^4 E- }4 d6 X% e
                    if (c & 3) {                                       
    ( A7 I% C: S0 V- S                        u8 *dst8 = (u8 *) dst;/ \: {$ V% e* W  V* S: M/ Z- r
                            u8 __iomem *src8 = (u8 __iomem *) src;
    3 |/ \! \! k0 v7 E4 D
    & k5 u0 z; Y: ^- k0 h                        for (i = c & 3; i--;)
    % m6 h4 e  Z) j4 X; f8 Y( g                                *dst8++ = fb_readb(src8++);/* 拷贝源虚拟机基地址的数据到目标地址 */6 H" d( ~3 Q6 ]* F5 g- H

    - t+ Z/ d& g$ ~/ z: [& ^# u                        src = (u32 __iomem *) src8;
    7 X4 t: q2 Q1 K; s) v4 S9 ]$ F                }
    ; \' ~/ ^0 G  C& u$ _# \  K" j8 u% t' I! R
                    /* 从内核刚申请内存的地址buffer拷贝c长度的数据到用户空间的buf里去 */
      P: v% h4 B* P. `: Y1 x                if (copy_to_user(buf, buffer, c)) {. B8 v& f. V# M; X3 S# \
                            err = -EFAULT;        /* 成功拷贝,则err返回值为0 */& q5 Z0 }- W2 R/ z
                            break;
    " P3 e9 U& T( T! e' g8 p5 f                }3 D% c& E9 u: b1 @1 @% u
                    *ppos += c;                /* 调整偏移位置 */
    + i5 R! L- z+ b# q& l                buf += c;                /* 调整用户的buf */
    9 X; u1 T. w8 y) r                cnt += c;
    : T8 @  _8 c& C- H3 |: Q& O& a6 E4 i' u                /* count变量减去已经读取的c数量,用于判断while(count)是否为真*/
    5 F8 `. c% a2 t( n: V                count -= c;                ; A& Q. F* n2 e) W( C
            }+ R% D$ h( D0 n5 z

    * B4 H9 n9 q2 }* }7 s6 d; e) j! M$ y        kfree(buffer);                /* 释放内存 */  n; F+ U" `+ t) h5 O' q9 S
    ( Q; \1 B; f( Z7 e
            return (err) ? err : cnt;        /* err = 0时,返回被拷贝成功的数量cnt */" G6 z8 a# u- D. I  \7 u% l
    }; r* @% l5 d; W+ z8 ]
    fb_write函数源码分析
    7 P% Z8 d9 T1 `) Y' M( d: e7 b; R. |7 A
    static ssize_t5 M6 k$ Y' @2 J1 w. S
    fb_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)  B2 }' E4 t& ?: U
    {
    7 ~6 r  ]5 ]& J0 Z        unsigned long p = *ppos;
    7 ^5 o' ]2 k# L6 O! \6 @        struct inode *inode = file->f_path.dentry->d_inode;
    7 b# V3 @* U% @7 n1 G        int fbidx = iminor(inode);5 ^) o8 i) P7 Z" y- B( |$ a
            struct fb_info *info = registered_fb[fbidx];8 G2 L0 u2 z2 ^4 M+ y" F/ |
            u32 *buffer, *src;
    ) F3 U; i+ {/ s/ |        u32 __iomem *dst;
    - E( [0 I2 f4 z* T: n5 ?3 j; Y        int c, i, cnt = 0, err = 0;
    * l% F/ {; ?9 [        unsigned long total_size;4 T- x0 e( w# Q/ |3 k
    5 j1 M; T$ q3 c  P7 Z, J+ P7 ~
            if (!info || !info->screen_base)
    , O4 Q! l) Z7 X; x+ g                return -ENODEV;1 f0 I/ W2 G$ U. A8 ^

    ! u/ {7 U. U9 n7 s/ d) g        if (info->state != FBINFO_STATE_RUNNING)
    - t9 x; E5 {8 I$ J/ X) \' l                return -EPERM;
    ' o7 `5 X& s/ m0 ?
    / z, I& z: E+ _4 [6 Z        if (info->fbops->fb_write); R6 m" [3 ~0 g$ H
                    return info->fbops->fb_write(info, buf, count, ppos);& |2 E( j, b- |' t8 q6 D' q; Y! m
            6 w1 |5 C9 x3 l! k8 i8 d6 J
            total_size = info->screen_size;
    4 ], `" m4 D: v7 X+ {" O* E0 i& S0 k/ _5 A6 W) W$ m
            if (total_size == 0)2 r  S8 O1 ~7 q; ]$ }
                    total_size = info->fix.smem_len;
    - Z3 K7 B: K6 j2 x* o9 r$ X6 J9 m
    / X) B) @* S7 ^* W        if (p > total_size)$ v" z# q# O1 D8 n3 a
                    return -EFBIG;2 p9 h6 o  v# m& B
    ! G8 a4 z; n3 T1 O
            if (count > total_size) {
    2 H6 {+ ?/ T8 }" I                err = -EFBIG;/ n" ~$ h' k; i8 s
                    count = total_size;, |& s$ Y) P) ~4 k
            }
    7 {# d7 @7 x: }& F# r" `, C# S/ R
    4 |0 e, a3 e) h, F& X5 q, N( l/ z6 l1 g        if (count + p > total_size) {
    $ r  R8 }& J8 y4 B% V' _                if (!err)' [5 C+ |0 G3 W: l+ F( d1 F6 V
                            err = -ENOSPC;
    ! Z- s9 {3 f3 r$ m% \# S5 E- R( O( R4 M2 o! h
                    count = total_size - p;. P3 O5 p0 m- I
            }
    . y2 H8 Y; n* I1 w5 F, B# o$ d) E9 D7 k8 R% H* B- \( E
            buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count,5 L! s7 y2 \  G7 z0 h. R
                             GFP_KERNEL);" @1 a. B0 h: n% u0 {% M$ u. N  S
            if (!buffer)
    6 g6 N0 o, q+ W1 q" k: A  w" y                return -ENOMEM;; V2 ~) C  w8 A3 j% ~
    3 p% y+ j2 L% C
            dst = (u32 __iomem *) (info->screen_base + p);        /* 源虚拟基地址 */
    # z. W' s% n* u: w8 z7 k# p) F* x( X& V1 d" B8 }% O  k! O) E2 M
            if (info->fbops->fb_sync)8 A; P* U* ]% P) X9 ?* {" W  u
                    info->fbops->fb_sync(info);
    # G4 ^$ A5 @  b& B6 V9 N! ~
    ) o8 [' L2 E2 j0 X( Q2 l6 }2 i  p        while (count) {: L$ M4 n( b1 J
                    c = (count > PAGE_SIZE) ? PAGE_SIZE : count;
    . o5 z7 h8 ], |4 \8 s1 w2 I                src = buffer;        /* buffer为指向刚申请的内存的指针 */! C8 \* D5 v# U
    0 H  c( Y; _, X( {

    % W9 ]7 R( F6 a/ v: |* p                /* 从用户空间的buf地址里拷贝c长度的数据到src内存里,成功时返回0 */+ g$ ]. ^4 t2 T4 E
                    if (copy_from_user(src, buf, c)) {! H9 I9 [( ?* b- ~7 X7 y3 f1 Z
                            err = -EFAULT;
    - F$ R& P/ m( _7 R  r4 E* P- S                        break;
    2 a- S- j+ u1 X' B                }# _) A) V- _1 j

      l+ m3 ^' T) Y% }2 a                for (i = c >> 2; i--; )                        /* 以4字节为单位拷贝数据 */, D# U# N6 l" [7 Z# p/ B
                            fb_writel(*src++, dst++);        /*     *dst++ = *src++     */
    # p$ h+ Z# O8 v! d3 I
    + j8 I6 C7 K4 e4 c( s                if (c & 3) {                                        /* 以字节为单位拷贝数据 */: G- T/ _* i1 R$ R
                            u8 *src8 = (u8 *) src;
    1 p( G- U, G0 |1 x                        u8 __iomem *dst8 = (u8 __iomem *) dst;$ h' k# \, F/ c) N; I6 ~, N0 u

    ! ~( ^' |; w( \* v& W                        for (i = c & 3; i--; )
    " ^* a# X# ?" Y4 f: p                                fb_writeb(*src8++, dst8++);
    / v+ e- ?: |! A) J  G" M$ n5 W+ r+ L/ ^; u1 r
                            dst = (u32 __iomem *) dst8;$ {6 m# r. g! L( V7 [
                    }
    ) ?: m. T% u- v* l3 q3 z  R* x$ J* E4 c4 [, s$ w
                    *ppos += c;
    " A. r" x# \7 ?$ Z( x                buf += c;% U6 c: ]( \7 B+ z
                    cnt += c;" S  w8 M# m& {" Y
                    count -= c;  {3 f' @; J% m3 h
            }
    / E9 H5 f  F- `( t, I4 b! }1 i, T6 J9 U/ P) g& r  ?( r9 B2 r7 F
            kfree(buffer);
    , p; v8 J2 v7 G
    9 x, v: w" v* J) k2 `        return (cnt) ? cnt : err;
    $ L5 u$ [/ G$ [8 x+ [5 |3 e}
    . r0 V8 }0 y$ |; H) `+ wfb_ioctl函数源码分析4 U) Q" d: ]& O: Q5 p+ y, h
    / R  S" {' q; r: M
    static long fb_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
    # f& ~' _' u- P( f{
    ; Y% c: F0 ]% M; V2 T* ~        struct inode *inode = file->f_path.dentry->d_inode;* H. f- w' H1 Q- l  g0 G
            int fbidx = iminor(inode);$ g( R% F! P# |+ \1 A5 Y; F
            struct fb_info *info = registered_fb[fbidx];/ e! n& g  Y9 u/ B

      s# Q8 o- R0 L! K& r: u        /* 这个才是真正的fb_ioctl驱动函数 */
    6 Z! O5 F1 R4 a) U$ R        return do_fb_ioctl(info, cmd, arg);       
    9 h6 \! d' A/ G7 v# x% g}
    1 c% S+ h0 m9 Z# u/ w2 R. |do_fb_ioctl函数根据cmd来设置各种命令,这里仅举例说明:: S) E$ S+ a  ?  L9 ~* k$ v( P
    ( v# j( G" Z2 @. F8 p$ q7 {# ]
    switch (cmd) {+ v  B. i( M$ |" }; C0 }
            case FBIOGET_VSCREENINFO:                /* 获得可变的屏幕参数 */! A2 H# L0 U# a; Z
                    if (!lock_fb_info(info))        /* 如果info->fbops不为空,则上锁,成功返回1 */
    % {0 h% U9 ^: _9 F& s5 [                        return -ENODEV;
    # [4 f% P1 @. t9 ]- E3 n8 K- V                var = info->var;                        /* 可变参数变量的设置 */
      Y1 ?. e) D. u' E. v                unlock_fb_info(info);                /* 解锁 */2 X5 e; Y1 ~7 P! ]) b, v( h! z( y
    8 n9 V/ O; ^6 c7 Q, G% |
                    /* 从内核空间的var地址拷贝var大小的数据到用户空间的argp地址里去 */
    3 ?; Q" y/ y) ]9 O  l                ret = copy_to_user(argp, &var, sizeof(var)) ? -EFAULT : 0; /* 成功返回0 */2 m- `  v& K4 w& @; c
                    break;' p) K* t; @! ?5 ~
    fb_mmap源码分析:
    4 `. W% `7 n2 r9 Y5 G
    * e" b+ d2 g9 X: r6 X: @/ q; d9 v/* 这里分配的显存是在内核空间分配的,用户空间并不能直接访问,6 Y. ?/ ?# G8 z- K6 V
    * 所以需要用到这里的mmap函数,直接将这段内存空间映射到
    4 {) n# e7 Z! c( Y+ r2 C * 用户空间去,用户空间就能访问这段内存空间了。
    8 O. A. k1 n: A2 y5 Z */. V" S2 [" y$ Z3 r/ ]3 A
    static int3 G) G% G( q" B# s
    fb_mmap(struct file *file, struct vm_area_struct * vma)
    3 m! w# B' [/ R# N, X6 |  |__acquires(&info->lock)
    # ]2 c! m% {  G2 s__releases(&info->lock)
    ' J$ A) w0 _) a+ m4 i  {{" |( y3 V& [  M2 P$ d) ]
            int fbidx = iminor(file->f_path.dentry->d_inode);7 K+ B7 n) h* G4 w' v9 D' L
            struct fb_info *info = registered_fb[fbidx]; /* 通过次设备号找到fb_info结构体 */
    - X3 m1 e( ~& u" w        struct fb_ops *fb = info->fbops;7 J; p+ i* m# n( \3 s# R, G
            unsigned long off;% a$ P$ g% N8 G
            unsigned long start;
    , M: f4 {* t8 l        u32 len;
    7 ~8 I: f; G6 y" ^6 ?1 o5 z
    / {! ]1 E  q) Y, f) O! S        if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT))9 A) c8 j6 Y$ G: H5 Z
                    return -EINVAL;2 S% G$ u, f% z( O% S" Q
            off = vma->vm_pgoff << PAGE_SHIFT;2 n! o! U+ b% x8 G! b8 g
            if (!fb)
    4 |9 N; n. H4 Z( j- p                return -ENODEV;2 u' i" o1 y! \5 q6 s

    , h' {) t1 s0 T6 h) z        /* 如果registered_fb[]里有默认的fb_mmap就使用它 */
    - c9 H/ [$ p6 s5 C2 l! z        if (fb->fb_mmap) {0 ~" }8 s' c0 X# g9 e
                    int res;5 ^9 y& K9 W5 B+ F+ h
                    mutex_lock(&info->lock);7 E% [+ B4 U' a  j0 X( O
                    res = fb->fb_mmap(info, vma);( j! J( M7 ?; ?7 e, p7 e8 g
                    mutex_unlock(&info->lock);
    - Q& ]# g# ^# M0 c6 u9 z/ r                return res;& D, j) b* v, F% K( q9 \
            }
    * d8 p$ D! n: H# W( I: |! |. a: ~2 u7 B0 \) y6 D) A8 R
            mutex_lock(&info->lock);; z9 W# Q* U! V
    6 R. a0 m  q+ _' ~' {/ c9 e; p
            /* frame buffer memory */
    ; k3 H4 e* j: Y        start = info->fix.smem_start;        /* fb缓冲内存的开始位置(物理地址) */8 g3 d* ~) X8 O, d  z
            len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.smem_len);* w+ s# J& |7 v
            if (off >= len) {                                /* 偏移值大于len长度 */
    ( n$ g7 r  X. E+ h0 M$ X9 f                /* memory mapped io */                /* 内存映射的IO */) A) V% `' X& h/ d5 F* }
                    off -= len;# ^1 Q# U# y1 W: Q; I* ~
                    if (info->var.accel_flags) {9 d8 k# F  V; n! M- ?8 }- e
                            mutex_unlock(&info->lock);* N6 O1 X1 l  D" |3 v* ]
                            return -EINVAL;7 E; i7 @$ ~2 g' h# v( q0 L. e
                    }1 O# W& p) C% j4 x+ Z
                    start = info->fix.mmio_start;9 c& V# x& K; f; y, d
                    len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.mmio_len);
    " f; r! x. t6 R: a1 v        }% }+ z* S1 J4 N% q
            mutex_unlock(&info->lock);
    ! O9 _' [8 ]3 G2 ~( I; z; d0 B2 A& N: b        start &= PAGE_MASK;- u; {$ b& W" f# q
            if ((vma->vm_end - vma->vm_start + off) > len)( _1 J6 O. ], o* s( _
                    return -EINVAL;
    0 w7 v4 {- ^( ]8 A; d        off += start;: o$ ]- B3 n( S7 C
            vma->vm_pgoff = off >> PAGE_SHIFT;/ ~8 G( b+ y! C3 q
            /* This is an IO map - tell maydump to skip this VMA */, ?* U" Q% O* e& e* _
            vma->vm_flags |= VM_IO | VM_RESERVED;7 X+ g$ u5 B1 @7 G& e2 _
            fb_pgprotect(file, vma, off);
    7 K4 D: X: a8 C4 J9 C0 T1 R, k8 V6 Q6 Z0 p7 ^) _
            /* io_remap_pfn_range正式映射物理内存到用户空间虚拟地址 */
    3 n8 V& P' s, u. _' c/ p        if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT,
    ! w4 C- ?/ {( k4 N6 m                             vma->vm_end - vma->vm_start, vma->vm_page_prot))
    ' I  v  n8 t$ H+ K( m% V, {                return -EAGAIN;+ n/ p4 `5 V2 {: b2 p* G2 T
            return 0;
    ) R) X9 A! n; S+ F}* z' e) `* |# D8 A$ p
    问:怎么写LCD驱动程序?
    8 u; y5 ^% |% S/ D- k! m; N1 ?# \; W1. 分配一个fb_info结构体: framebuffer_alloc4 r& ^+ e1 y4 V) a3 @
    2. 设置
    3 \3 S7 f+ ?2 R7 n: N3. 注册: register_framebuffer
    8 ]! o) ?! E& w2 g4. 硬件相关的操作
    $ e) J1 i, C/ D/ K7 Y/ G5 T1 @7 ?3 C, [1 Z- }/ ]- J
    . r8 b1 n. R" k* V4 v
    , @. ~  U2 z. d8 N
  • 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-11-25 23:07 , Processed in 0.187500 second(s), 23 queries , Gzip On.

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

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

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