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

linux lcd设备驱动剖析三

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

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

    [LV.1]初来乍到

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

    EDA365欢迎您登录!

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

    x
    上一节文章中详细地剖析了probe函数,但是从始至终都没有看到打开读写文件接口的操作函数,只看到了下面这个操作结构体: W4 M1 K$ Z4 X9 i
    0 v# h  z$ U; L$ K' m% r) t) @7 J1 v6 ^
    $ b6 ?0 T- M8 \$ T6 d
    static struct fb_ops s3c2410fb_ops = {
    # F  `7 E6 @  p$ D4 p) Z5 t        .owner                        = THIS_MODULE,
    , m/ b0 ?$ a% b# N5 I$ V        .fb_check_var        = s3c2410fb_check_var,
    5 k  e- A0 K2 A( X        .fb_set_par                = s3c2410fb_set_par,
    : F7 g8 l& e' O2 F. }/ @7 ^+ b) C        .fb_blank                = s3c2410fb_blank,6 D: B- [" [1 B+ o  _! ]& b
            .fb_setcolreg        = s3c2410fb_setcolreg,2 p% ~+ {8 Q2 k* f
            .fb_fillrect        = cfb_fillrect,- S. d9 `0 U2 c/ F" Q; f6 e* W& B! t
            .fb_copyarea        = cfb_copyarea,. N* u) t$ P" \7 ?$ i# f
            .fb_imageblit        = cfb_imageblit,
    " \$ @; P' r5 w3 o3 u$ s  i};
    ( B% D) K0 j( S' U$ X这并不是我们想要的打开读写操作函数。上一节文章链接:http://blog.csdn.net/lwj103862095/article/details/18189765( Z% j! r$ ]: o! _# H0 c
    问:那到底帧缓冲设备的文件操作结构体在哪里呢?. G; @. ~0 z- ]$ W7 z& A  G) a
    答:在drivers/vedio/fbmem.c文件里。: O0 n9 |0 }$ Y) l4 t

    0 L3 _) V# l: t从入口函数开始看:" p  s: `$ L$ |1 H  c* Y

    ( ]6 c" Z8 o5 U  g6 f
    6 U0 f/ @! A  ~& _: r. y3 Hstatic int __init* ?5 ^  t* V" m8 h
    fbmem_init(void)
    3 @! [5 ^- Z1 J$ M  d( E. a{
    & l" n' I5 P- v7 T1 l3 n" }  _        proc_create("fb", 0, NULL, &fb_proc_fops);
    5 f+ |0 J% A8 C$ [
    / {4 @& }# ~6 o$ F% H, g* Q        if (register_chrdev(FB_MAJOR,"fb",&fb_fops))$ L1 D3 M/ @: T# v2 I3 M5 R0 f2 a0 b
                    printk("unable to get major %d for fb devs\n", FB_MAJOR);/ b5 f9 w0 N& c/ ^" X, I$ A3 K0 n
    0 G# p9 F7 R* \; _1 t; h6 |
            fb_class = class_create(THIS_MODULE, "graphics");
    ; }. Q% J3 o: Q$ t# |        if (IS_ERR(fb_class)) {
    $ {" D# U8 }- K% J# O  l  e                printk(KERN_WARNING "Unable to create fb class; errno = %ld\n", PTR_ERR(fb_class));3 ^- E! p( e  W. B' H9 b
                    fb_class = NULL;
    $ q2 `1 ?: S/ U7 b, B+ a        }
    % \# E  K- V% A% h5 B, r        return 0;$ o+ h2 S. d; f4 P3 S  {0 U, w
    }& c8 _2 ~9 E5 J) E
    fbmem_init注册了一个主设备号为29的字符设备,并创了graphics类(图形类)。
    3 |8 }8 ]" w% }# W. N字符设备有一个关键的成员是文件操作结构体7 |9 X: q& M5 ]& u0 g1 @# C

    6 I" D, {+ Z( i" F0 L. Y+ V$ O$ ^% o+ ^" @
    static const struct file_operations fb_fops = {2 L) ^. H. ?- r
            .owner                         = THIS_MODULE,. D5 p$ j9 \9 E6 Y9 u. m  @$ H+ u
            .read                         = fb_read,
    ; X: h6 B  j; ?8 O5 }7 m5 R8 b        .write                         = fb_write,. K, t: q% s; @  e- V) u3 R) `3 [
            .unlocked_ioctl = fb_ioctl,* g/ M! L5 |1 \! Q, y
            .mmap                         = fb_mmap,
    & Z% Z0 n% p+ w2 }7 X        .open                         = fb_open,
    & o& C% @1 t3 c* K: D6 {7 `8 @        .release                 = fb_release,
    , t) P2 ?0 D5 c; n#ifdef HAVE_ARCH_FB_UNMAPPED_AREA5 O! S& o) R+ Y9 g4 O4 N
            .get_unmapped_area = get_fb_unmapped_area,( `9 d, ~, s" P& _
    #endif( o& W/ a. H: B* H  T! Y
    };  T% m+ {& _/ Q; B$ W7 w6 `# Z
    理所当然的,我们应当首先看的函数是open函数
    # E& l7 Q0 E+ |6 s: Q: K; e! E. Y3 k7 R4 u; C
    static int- I0 x+ s, a& U6 {
    fb_open(struct inode *inode, struct file *file)3 g4 ~4 B+ e5 b: `* u. o
    __acquires(&info->lock)
    # B( \9 o4 O1 R+ X__releases(&info->lock)
    1 f: C/ n% R: u' \{5 j/ H% J4 C; \" @7 r$ {0 d! c8 u& }
            int fbidx = iminor(inode);                /* 得到次设备号 */* b7 ?+ S' K2 j" f. A3 x" n
            struct fb_info *info;
    : O! D; ]" Y& p/ B% M1 u        int res = 0;
    ( W* M2 e7 I  e% W9 Z. z1 Q7 T# c; N. G) D  L5 O/ R! m5 x0 j
            if (fbidx >= FB_MAX)                        /* 次设备号有没有大于规定的最大值32 */$ z, z& [5 J4 t0 g
                    return -ENODEV;                                /* 没有这样的设备 */
    9 a! d# a( r9 J7 Z; Y6 Q- i0 ]6 r        info = registered_fb[fbidx];        /* 使用次设备号得到fb_info结构体 */- v- r! u( w' z2 {3 K
            if (!info)$ x4 p2 e! X0 r
                    request_module("fb%d", fbidx);$ Z7 I- Y! T. R, v, @

    ) u" f. F' U* j- Y3 d- z        /* 再次使用次设备号得到fb_info结构体 */
    1 g8 c* K2 r& [4 }6 `$ R3 W1 T        info = registered_fb[fbidx];
    # d% J& ?- R$ i% s! T" T/ O        if (!info)
    2 d: d% I9 e" }, x+ N                return -ENODEV;
    & B1 t: H& ^& B) e' \7 J6 C6 y        5 o  T# g& w6 \( I0 J
            mutex_lock(&info->lock);                /* 获取mutex */
    8 P/ }$ y3 p0 I3 H+ o5 o. v8 C# J5 p6 C" @
            /* 获取模块使用计数module,成功返回非NULL */
    6 B1 Z5 Z4 W2 @% |4 a0 ^        if (!try_module_get(info->fbops->owner)) {
    % o0 U) _' M5 P# F8 o3 k( E                res = -ENODEV;
    & [+ c3 j9 w# j2 r" Q7 a6 @                goto out;, [0 _4 z& U/ M: p
            }. }8 x4 {# K+ ]' U! k* ?9 R) Q
    7 q9 _! [3 \# g1 |( h" o
            /* 从registered_fb[]数组项里找到一个fb_info结构体保存到5 a$ U# g3 g8 l! v
             * struct file结构中的私有信息指针赋值给它呢是为了以后调用* f+ H$ G8 F$ d& c$ p, g
             * read、write、ioctl等系统调用时找到这个struct fb_info结构
    # b- i& t8 o3 g) _( V         */# R4 V( a' M9 ], L5 a
            file->private_data = info;       
    ! X6 Y( r" w" e- w  j
    ( ~& `6 l) b5 g0 B        /* registered_fb[]数组项里有没有默认的fb_open函数,如果有就使用它 */
    6 y7 ^9 g' Y. d/ ~2 s, v        if (info->fbops->fb_open) {, e& K3 Q; j7 ~. c
                    res = info->fbops->fb_open(info,1);        . [! t+ Z. a, R4 y! j

      r( R; I% b+ H3 N. P* h* z+ a                /* 有默认的fb_open并成功打开就删除模块计数 */' g7 A; P+ b8 u% |
                    if (res)- b# B1 N/ k4 Y: f& f! t5 n" t
                            module_put(info->fbops->owner);
    7 U5 b  x/ H- Q& D; M, a        }) H+ {9 m& v, v
    #ifdef CONFIG_FB_DEFERRED_IO                /* 这里没有定义,不用理会 */
    % y3 o1 E0 m0 b% w9 j* K, M        if (info->fbdefio)  s* z/ C" O  `# }( L
                    fb_deferred_io_open(info, inode, file);
    2 S9 I2 a5 t$ N+ t9 a3 `" I! \#endif
    - [* a. C% U$ P* mout:( ], B6 s* B( U* Y6 k
            mutex_unlock(&info->lock);        /* 释放mutex */
    # w: Z# b% g' b        return res;
    7 M; R( Q; s" ?+ v: G0 e}
    # M& B; Q) @2 R% {发现fb_open函数是围绕fb_info来实现的,而fb_info设置为registered_fb[fbidx]
    $ S3 n* E! C5 _4 [; m" }) g问:registered_fb[fbidx]结构体数组是在哪里被设置?# [* m  ?; C7 Q9 q

    : u3 t% U' }, K/ Q/ A7 [答:register_framebuffer函数里设置registered_fb; e9 h( X: @! t5 l5 h# ^
    , N6 Q( g& W7 o! Z3 P

      m9 G' H. W; n/* register_framebuffer()函数的主要工作是设置fb_info结构体的一些成员 */
    ( l' e4 Y( d" }5 Q. Dint
    0 B7 e% t1 h& _4 J, h' d$ Aregister_framebuffer(struct fb_info *fb_info)2 k. V0 }/ L9 i! u
    {
    , T) e; X1 t% u) c5 b        int i;
    ' w3 T+ z+ Y- D# l* k# g0 O        struct fb_event event;) a' _) n$ j1 F% ?% ]) N* A
            struct fb_videomode mode;% b  p# E& a- C  ?9 n

    * H( J4 l3 H  J! U+ ]' F( o        /* num_registered_fb代表注册帧缓冲设备的个数 */% ?* O: w4 j1 n
            if (num_registered_fb == FB_MAX)( R+ B0 a" h; A/ B, e
                    return -ENXIO;2 ]2 d2 S* C; i6 U( w; Y- S
    1 P/ x0 Y) c  X3 X
            if (fb_check_foreignness(fb_info))$ F+ v6 h6 v# |. A9 |5 q
                    return -ENOSYS;0 h, w. w% p2 }. W9 u

    ; |7 e! o( }6 @4 p* \# {        num_registered_fb++;! t" i4 h, U7 Q# P# O2 G2 l$ A7 N% h- G

    % B: H6 B0 e/ l( z  a5 j        /* 当registered_fb[]项都为NULL,就会break,找到一个空的次设备号 */
    , c* ]& U  i; Q2 U& v        for (i = 0 ; i < FB_MAX; i++)4 D# r% m* z, D, U2 x, V2 Q& h2 T
                    if (!registered_fb[i])       
    6 c) }* I2 c1 q! a; g                        break;
    & x& o2 j" P* _& Y8 C$ o8 I( r        fb_info->node = i;
    8 _" y) Z& k/ A5 r1 w        mutex_init(&fb_info->lock);                /* 初始化mutex */
    : U# d) D2 y% I  i
    * n& I! g& E: ~& S! O1 K        /* 因为在init加载函数里只创建了类,这里在类下面创建设备 */
    - n; g( K/ R/ M+ q8 m        fb_info->dev = device_create(fb_class, fb_info->device,/ Q1 @2 ^, E3 i  i/ d, W  G
                                         MKDEV(FB_MAJOR, i), NULL, "fb%d", i);7 \- G% |, V# r7 n! v
            if (IS_ERR(fb_info->dev)) {8 Q) u! K% e8 a
                    /* Not fatal */
    % M7 W1 B6 r7 a# s                printk(KERN_WARNING "Unable to create device for framebuffer %d; errno = %ld\n", i, PTR_ERR(fb_info->dev));! k' o% s  v4 R( R. m
                    fb_info->dev = NULL;
    / R" g6 B- [1 r# I        } else
    ! P& W& }7 i: _                fb_init_device(fb_info);        /* 对struct fb_info做一些初始化 */
    ; j! @) e0 M* B# F9 ~1 i6 q. Z( ?# T) C; O" K- x% v

    4 L& A* @2 e. {6 n9 r" b        /* 初始化fb_info->pixmap结构体成员 */
    6 z* M/ ^  s: e( |) y9 i- {        if (fb_info->pixmap.addr == NULL) {- N# `9 x' m! N8 X8 n
                    fb_info->pixmap.addr = kmalloc(FBPIXMAPSIZE, GFP_KERNEL); /* 8K大小 */
    3 L1 A; S6 H3 y! d                if (fb_info->pixmap.addr) {% T- M  s7 x0 }3 h
                            fb_info->pixmap.size = FBPIXMAPSIZE;        /* 8K *// ]# A' x3 F$ V) _8 C8 O
                            fb_info->pixmap.buf_align = 1;
    : T2 {2 o0 a' l3 K* F# m8 k: S                        fb_info->pixmap.scan_align = 1;
    & J. G3 r  N! S                        fb_info->pixmap.access_align = 32;
    0 F" H% A9 Z9 }# d                        fb_info->pixmap.flags = FB_PIXMAP_DEFAULT;
    - o4 g- {0 a; l4 w/ I1 e" ?6 X& k                }
    9 N- B5 u9 O* B8 X4 u3 }0 I        }        5 k8 }9 p4 h* `/ |3 p7 K4 e/ j
            fb_info->pixmap.offset = 0;. ^. U. X3 ?" Z1 e7 d

    4 z' g0 n+ z% }        if (!fb_info->pixmap.blit_x)
    , |& z+ u3 F# m, {$ P                fb_info->pixmap.blit_x = ~(u32)0;1 H+ M( a  J5 }
    * i, H7 ?, q1 n8 u; }1 s' k) h
            if (!fb_info->pixmap.blit_y)
    : K# C1 G9 O; ]" R  |( t                fb_info->pixmap.blit_y = ~(u32)0;. x& u. {' A0 R2 a# R, e) y% @

    % s9 D+ N& @1 x7 V        if (!fb_info->modelist.prev || !fb_info->modelist.next)
    0 @/ ]7 b  V: }+ m% A                INIT_LIST_HEAD(&fb_info->modelist);        /* 初始化modelist链表 */; k  `" a% o8 E! q% Y7 I1 M

    0 N$ m, I" m' d        fb_var_to_videomode(&mode, &fb_info->var);, W( y/ C3 z  K1 I
            fb_add_videomode(&mode, &fb_info->modelist);
    / K8 I9 _& Z; i$ w
      G0 [& `) n; s' t& z! b        /* registered_fb[]数组项在这里被设置 */: M  B# s' }) I! w1 g3 H
            registered_fb[i] = fb_info;
    2 r; A3 r% M! ^; D; i$ N/ u& Z# C& V% d2 e& a! l# a
            event.info = fb_info;
    % b6 k7 D/ f, A/ v" Z( y
      |1 Z* G! R& e: }        % o2 m& t; z5 Z" @
            if (!lock_fb_info(fb_info)) /* 上锁 */
    ; M% c% s0 }, R# d                return -ENODEV;, O( e7 _3 M- s$ r' b
            fb_notifier_call_chain(FB_EVENT_FB_REGISTERED, &event);
    ( u1 o8 }# R+ l  p. @- Y' H0 E        unlock_fb_info(fb_info);        /* 解锁 */
    / V$ b1 V- j, O) m        return 0;4 b  y: M4 K/ O- a, U+ m
    }$ w; ?: p1 Q  h
    fb_read函数源码分析- L7 }. _' G& E* ?9 ], o4 p

    ' W+ B2 r- w: V& h. nstatic ssize_t: A  y1 l1 |# Q6 B4 n
    fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
    9 S3 [9 V  `$ B3 {$ t1 m{* _' H; D! W8 F$ Q7 X* y  g( [
            unsigned long p = *ppos;
    3 F5 y4 J: g% p: L* o3 h        /* 通过file结构体的成员得到inode节点 */" A9 T5 m1 n# e5 y" n9 j
            struct inode *inode = file->f_path.dentry->d_inode;
    9 @0 y. B7 `3 ~$ o) N
      C' k! n1 P' o  h0 @2 g- X        /* 获取次设备号 */
    / u' N+ D2 _! K( e) I; q, A9 G+ I        int fbidx = iminor(inode);. s6 v' d) ?% ], m. O' y
            /* 以次设备号为下标找到一项fb_info结构体 */8 z: p( \9 w3 I0 J
            struct fb_info *info = registered_fb[fbidx];
    9 F0 j; w4 P5 q* ^        / Y" x, @% k4 h  L! Q
            u32 *buffer, *dst;1 e9 c6 W' N! n! F% b* K
            u32 __iomem *src;
    * m0 r/ I- u* ~7 K" k: y, M        int c, i, cnt = 0, err = 0;
    5 c2 ^% C& b% u$ o# J4 ?1 e        unsigned long total_size;
    " ^: q% G5 y! V7 @) E  n2 x; c$ \9 _! K* s  o6 ]# S5 U) }7 K
            if (!info || ! info->screen_base) /* screen_base是虚拟(显存)基地址 */
    0 k5 V9 {8 h% a2 @% r                return -ENODEV;
    8 l# X9 I0 d$ A* C- A# L% S2 {- R% f4 w7 l: H5 R
            if (info->state != FBINFO_STATE_RUNNING)
    6 \9 Q  q' n" C- g* k                return -EPERM;                /* 禁止操作 */
    ! `! x" Q3 I. w8 C% V4 W  t- T2 N
    . o, f* g+ u6 t: L& m2 |7 G6 {7 e        /* 如果registered_fb[]项里面提供了fb_read()函数,就调用下面的函数 */
    2 v, w9 O$ Z+ t6 _4 I( e) m# J        if (info->fbops->fb_read)2 X7 ?. s5 ], z
                    return info->fbops->fb_read(info, buf, count, ppos);
    0 z  U; x- [) z
    $ z4 a% B; [1 U; P, {        /* 没有默认的读函数就从下面的screen_base里读数据 */* @0 c( J* s8 N
            total_size = info->screen_size;  /* x*y*4,x,y分别为屏幕分辨率 */+ f- O7 l( r; x7 b8 p
    # D2 ?' V) V+ \! O$ I
            if (total_size == 0)6 c  A" w9 B7 e3 ^
                    total_size = info->fix.smem_len;        /* fb缓冲区的长度 */
    ! L0 f( P0 K, O% \5 x# P( _: `: Y; d4 B" a! M
            if (p >= total_size)                        /* 调整读的偏移位置 */5 i' b( s2 Q6 N5 g
                    return 0;
    9 g. C/ A5 f! b) X( b0 g
    & o$ y3 Z, l* }2 ?) O1 w3 \0 S. h        if (count >= total_size)
    & M0 u- B9 ?$ T                count = total_size;                  /* 一次性最多读多少个字节 */
    ) B* ]& z: z! i4 R- D
    ! T$ Y* s1 j5 h( ~! w        if (count + p > total_size)
    * D+ j3 M6 W7 p  U" q$ s* f/ P                count = total_size - p;                /* 调整读的位置及能读多少字节 */
    % C' E: s( r+ x  f& \
    - t/ }" r/ ?8 S# V+ U  @        /* 分配内存,最大分配4K的大小 */
    5 i+ ~; B7 J  L# b+ N( q        buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count,
    9 T# ~% B3 b/ Q( N3 c                         GFP_KERNEL);               
    " f, M* l9 c! e: T  W# V! T        if (!buffer)
    8 s! R/ c5 T/ p6 Q% a: C2 M0 d0 U                return -ENOMEM;
    1 c/ m( Q5 B. _/ F: C' Y" G  U( m- e$ X8 h1 ?0 D) Y2 P
            src = (u32 __iomem *) (info->screen_base + p);  /* 源虚拟基地址 */* }: ~# g/ U$ L8 w6 H7 g& B

    ' w7 `* g# _3 t# u9 ~4 S+ n        /* 如果registered_fb[]项里面提供了fb_sync()函数,就调用下面的函数 */
    4 o! w* W3 a% x) z        if (info->fbops->fb_sync)               
    ! k  w3 |7 Z. |: A8 O. [                info->fbops->fb_sync(info);1 i6 I2 P7 c+ A- u& X7 y3 y) F7 P3 |  Y
    9 P& r# S; o: `& X% W" b' u+ s
            while (count) {
    2 w* _5 |4 h3 j5 U7 }9 M                /* 读多少计数变量,单位为byte */
    2 ^! y# i1 v, L& V, B) I                c  = (count > PAGE_SIZE) ? PAGE_SIZE : count; 6 ~- w; _" x1 o( g( j0 I
                   
    . P& d3 H8 E' d; Z( @6 I                /* buffer是指向刚分配内存的首地址的指针 */
    , s2 K' s3 B' h* E0 L' h9 ?" y                dst = buffer;        /* dst指针指向buffer */
    / V1 ^8 h' A2 K$ j# r& f2 R+ O: A
                    /* 先除以4,因为每次读4个字节 */  I/ m% _1 ~1 h, j4 i" F
                    for (i = c >> 2; i--; )                        . W) |/ `; a1 y7 G2 g
                            *dst++ = fb_readl(src++);        /* 拷贝源虚拟机基地址的数据到目标地址 */% O2 c$ p5 f/ H, a$ a' p3 c

    . M4 X. t3 G" L- |& N0 g; A) ?& Y                /* 判断是否以字节为单位来读取 */; i+ d( n& Q  S! S: v& q) V+ |. a7 l
                    if (c & 3) {                                        ( k, \, D' e$ M6 c
                            u8 *dst8 = (u8 *) dst;9 b' t- Y" D0 [+ P7 _. P
                            u8 __iomem *src8 = (u8 __iomem *) src;' ^7 {+ G/ ~0 s
    ' d: E- \) n8 {6 u: \  }
                            for (i = c & 3; i--;)* l1 h( Z, ^% }  g+ s1 h
                                    *dst8++ = fb_readb(src8++);/* 拷贝源虚拟机基地址的数据到目标地址 */
    3 Q& W; m# s8 c: U- I6 z7 p9 H. [$ u2 x" c+ U* t" K
                            src = (u32 __iomem *) src8;
    * T0 o& h( ~  \* i' J8 N                }: f6 C3 b/ [) @! n, t* O
    2 v, U% \* y4 h& _. i, K
                    /* 从内核刚申请内存的地址buffer拷贝c长度的数据到用户空间的buf里去 */4 T6 f$ h9 T; O8 l6 m
                    if (copy_to_user(buf, buffer, c)) {. Z. f- |3 f, D7 K! H' n
                            err = -EFAULT;        /* 成功拷贝,则err返回值为0 */
    , j* k+ ~! Z( g/ Q/ J$ O                        break;
    / g$ i, A# G( |5 p0 |                }* i5 l9 g4 C5 W3 T; B  _! p# M6 J
                    *ppos += c;                /* 调整偏移位置 */" l) l1 H. j) y! R! q3 N9 v3 F
                    buf += c;                /* 调整用户的buf */
    ! o' z6 I0 O1 Z) r8 l                cnt += c;
    0 ~- }( E) m+ A                /* count变量减去已经读取的c数量,用于判断while(count)是否为真*/+ z# E% W" K* }4 v
                    count -= c;               
    5 T' F  ?7 M$ T5 z        }  [" k( D0 [! X) T( O$ O& j& m# r
    - ^( M3 G/ D" m6 S6 J
            kfree(buffer);                /* 释放内存 */7 [' z1 |" s4 u  E+ N5 D# X

    7 O# |" v; S2 W! S        return (err) ? err : cnt;        /* err = 0时,返回被拷贝成功的数量cnt */
    , x9 |! ^# Y0 q! E' A% [. s  s. Y* s3 V}1 ~& ^  O  f6 C/ E' S6 @; p
    fb_write函数源码分析+ x6 Q$ C* p; h, ~; q+ z

      P% H) E2 j% N& a8 n3 Xstatic ssize_t
      C3 N6 @6 |; Y) r, xfb_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
    8 e& @$ j4 t  E- f1 D# Z{  a8 [, B" {: ~9 {
            unsigned long p = *ppos;
    ! Z! W$ K0 P2 @' S% b1 z$ v        struct inode *inode = file->f_path.dentry->d_inode;
    ; M2 Y6 }0 ~1 b; x( X        int fbidx = iminor(inode);
    . f& T8 x0 _8 K+ h6 A        struct fb_info *info = registered_fb[fbidx];
    5 e& n: X( t$ \! u: v        u32 *buffer, *src;0 U9 N) v- H1 F' t* K  q$ _
            u32 __iomem *dst;
    & D4 M7 m" k1 @1 n5 w' g; r        int c, i, cnt = 0, err = 0;
    6 s9 O2 g) @% y* y1 W- Z3 z% J        unsigned long total_size;  w; w% ?; C1 \
    9 x; |* N4 |0 I6 u# c6 G
            if (!info || !info->screen_base): u2 B# H3 m# x
                    return -ENODEV;  L( o2 H+ ]2 [- F: w2 g

    3 R8 m! y9 F: i: I. @        if (info->state != FBINFO_STATE_RUNNING)
    % K0 d2 m1 _( c( |7 A' @* h4 P                return -EPERM;" U- O1 p/ _' V  L8 k6 b; z
    0 B2 K( `: S9 y9 d3 q3 `  T
            if (info->fbops->fb_write)1 l; l9 W7 i) U" o& b9 p
                    return info->fbops->fb_write(info, buf, count, ppos);3 `7 s+ y( v/ V* _0 A6 }
           
    ! B" D: l7 r- w        total_size = info->screen_size;8 B! m0 R( x4 v7 s" D
    / u3 o! `" `/ y3 D( h
            if (total_size == 0)" H- Q, m; C+ I. o, Q1 d1 s
                    total_size = info->fix.smem_len;/ J; a- S8 q7 m' D* z8 R

    * B0 o  \6 w" X* L1 X1 N        if (p > total_size)
    . P$ a) x; k$ h4 u1 C, y                return -EFBIG;
    , s. c. `+ v/ p+ s) e) j
    3 X7 R2 u% i3 x7 `        if (count > total_size) {: I: c2 Q1 h5 D7 t2 i1 ^" G# c1 ?5 }
                    err = -EFBIG;
    $ p- A' `- W8 {8 Y                count = total_size;
    ( g) d$ x: E. S3 o        }; k& b3 M+ v$ {* h
    ) a! ]; w1 G$ [
            if (count + p > total_size) {
    # i" |9 f! O( _: }0 B3 o                if (!err)
    % c* v$ J: f& m7 ^$ }4 Q                        err = -ENOSPC;
    8 i/ L; [! R3 r9 K$ }' f3 i# |' c9 o) I
                    count = total_size - p;
    9 ]# C$ }. m: H% Z+ u! S( z( m7 ~        }
    " |2 E9 ?4 h: _. \& g9 g$ C7 P% m: f+ Q% `8 [# U! ]
            buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count,
    ' ^3 j( Q9 |; w$ \* i( h* L                         GFP_KERNEL);
    ( A( U' `) C! Z+ d* L, ]3 x. w        if (!buffer)
    0 M: ?1 P; K; J1 W! C                return -ENOMEM;5 w1 ~8 i+ W- A4 k* q' ?

    3 X+ r- w- O, c        dst = (u32 __iomem *) (info->screen_base + p);        /* 源虚拟基地址 */
    9 H% S  [8 p/ [( f
    0 @% S1 n8 ~0 Y. X! D3 G7 `! k        if (info->fbops->fb_sync)
    - V, t  d; h7 e                info->fbops->fb_sync(info);
    + @5 [- S* A* B
    & {* |3 H, k0 c+ r        while (count) {
    / |5 ~* |$ T) G* r                c = (count > PAGE_SIZE) ? PAGE_SIZE : count;
    ) ?6 t1 B7 Y" \9 T                src = buffer;        /* buffer为指向刚申请的内存的指针 */
    ) I7 B6 ]- M$ }& ^: q% q# e- b+ U6 t9 ?! s* t1 `5 Z* _. t# I* z
    ) T( l: J9 K# P
                    /* 从用户空间的buf地址里拷贝c长度的数据到src内存里,成功时返回0 */
    2 }- P. I; q. i. D0 {. c                if (copy_from_user(src, buf, c)) {$ e3 @6 a% r( ?6 }1 Y1 P+ ?
                            err = -EFAULT;' E/ R( S6 Q: H! c" h' H
                            break;% p) Z; W! L% F, P% i
                    }+ a9 e' h$ A! x& {9 U

    9 G6 T" b+ o$ L. S5 t, V" n. x5 w                for (i = c >> 2; i--; )                        /* 以4字节为单位拷贝数据 */; S  q0 H: E# C, j
                            fb_writel(*src++, dst++);        /*     *dst++ = *src++     */3 V. Y  x8 D0 _$ V

    ' u* f$ x' l2 ^( I3 m8 i                if (c & 3) {                                        /* 以字节为单位拷贝数据 */- i7 A( r; g6 q- |9 U; Z
                            u8 *src8 = (u8 *) src;
    ! V- I6 L2 W- _& A; o- q8 v. ]                        u8 __iomem *dst8 = (u8 __iomem *) dst;
    + f+ o& S% O2 f8 \: ^6 v  ?, F8 s
    / ?; `! e3 B2 l- n- s5 X' c8 `                        for (i = c & 3; i--; )6 _) J# u3 `% U' ?9 v) B
                                    fb_writeb(*src8++, dst8++);0 G: ~2 i3 G* r8 C" n% Y! p

    1 v; z2 M8 ^. r$ ~' ~; J: z9 p" d                        dst = (u32 __iomem *) dst8;' K7 ~; a2 m: y$ D
                    }1 y3 J; D# H/ G$ H9 i' a. B
    8 H& r1 v, j1 |  r
                    *ppos += c;( C" y5 Z' w1 R  ^9 l6 O
                    buf += c;3 v& |5 {* X2 s  v
                    cnt += c;  `. i5 y% N6 {# B
                    count -= c;' a0 A' z, }& y- m7 {$ C
            }" y% Q' a# g0 L' [: a0 @5 l: L' V' R

    ; [. O& I% b0 C( m: G- z        kfree(buffer);& }# j6 m& U: [$ D# r

    8 B3 n& R4 E! H+ u* U        return (cnt) ? cnt : err;
    ' ^; o  M8 q3 A}3 y5 R, _0 f0 r
    fb_ioctl函数源码分析* n+ [1 O! r! W$ f" Q+ N) U& w
      k! w/ B* v3 Q0 Y7 F. K
    static long fb_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
    ! Z0 t9 a# v' h$ W1 l& u{5 W8 n- X1 H/ S; S
            struct inode *inode = file->f_path.dentry->d_inode;
    + J) R* M. L- g$ v. q1 t        int fbidx = iminor(inode);
    , [/ R% L. f1 w" r0 w& \        struct fb_info *info = registered_fb[fbidx];2 C! Q# z8 M, X9 f3 q

      P$ ^2 H) z  k5 O! P& ]/ f5 E        /* 这个才是真正的fb_ioctl驱动函数 *// i5 w" ?2 g: E* _. z" j
            return do_fb_ioctl(info, cmd, arg);        ) r+ C+ k& w9 ~# O3 F/ Y
    }) y, Y$ m  k4 U9 V+ L* n
    do_fb_ioctl函数根据cmd来设置各种命令,这里仅举例说明:
    & O" |0 Q3 v8 X/ J7 ]6 i  P# p0 J# f% p7 @5 u! n6 P( O& a
    switch (cmd) {' Z2 r+ H3 Q# W! h& Y2 z* V
            case FBIOGET_VSCREENINFO:                /* 获得可变的屏幕参数 */
    " w8 ^/ L: j* E                if (!lock_fb_info(info))        /* 如果info->fbops不为空,则上锁,成功返回1 */; r, R! |2 B$ |
                            return -ENODEV;0 j% a8 E) u6 A: `( K, l; j
                    var = info->var;                        /* 可变参数变量的设置 */2 T9 M" N9 m; P" k0 h
                    unlock_fb_info(info);                /* 解锁 */1 {+ k2 T& i* E( P* z
      M) Z3 q1 A" \- ~
                    /* 从内核空间的var地址拷贝var大小的数据到用户空间的argp地址里去 */0 O( Y$ Y6 R+ F4 R; z5 E$ g
                    ret = copy_to_user(argp, &var, sizeof(var)) ? -EFAULT : 0; /* 成功返回0 */. {* H; H4 j' d) Q! u# n) R
                    break;! R; N7 w/ h" t" w
    fb_mmap源码分析:
      P( @. I0 G% g: j9 ]5 G& }( W
    7 D3 S; l  y1 Q. w- N3 v: h- m, k/* 这里分配的显存是在内核空间分配的,用户空间并不能直接访问,
    ' j5 y" l  |/ r% l  U! ^/ E9 ^ * 所以需要用到这里的mmap函数,直接将这段内存空间映射到% s, r7 D4 c+ P1 g  I3 Q
    * 用户空间去,用户空间就能访问这段内存空间了。
    ) j" t' f% }1 Z9 Z" S- {+ l* e */7 v7 B3 o. _% f& R
    static int8 s. X* e% Q$ V! I' [2 b8 n! O6 R; B
    fb_mmap(struct file *file, struct vm_area_struct * vma)( D8 _: t! v( F5 X
    __acquires(&info->lock)7 D  P; H$ ~9 I1 f
    __releases(&info->lock); e7 M% ~+ H6 k2 j% t8 v0 _) k9 J! Y$ z
    {
    6 V- @- p* a7 z- x( a& `        int fbidx = iminor(file->f_path.dentry->d_inode);
    % v) t+ C  Q" i! H7 B3 O& C        struct fb_info *info = registered_fb[fbidx]; /* 通过次设备号找到fb_info结构体 */
    * J" K3 L* |) Q) E( z6 e4 c        struct fb_ops *fb = info->fbops;
    + S( R" S2 {4 U8 @! G- k        unsigned long off;
    ( t1 [0 {1 q: G  L2 r  h# Y+ W        unsigned long start;
    ) \" V1 y( j6 T8 m, b        u32 len;
    9 D% o/ k( d  n5 \: D% R9 M% o  F" `0 N3 T7 b+ b
            if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT))! J- G3 k5 }0 C; o3 X7 u) _
                    return -EINVAL;8 K0 b2 _2 E1 L& V" \3 u
            off = vma->vm_pgoff << PAGE_SHIFT;+ q. y0 Q# v( E
            if (!fb)
    & h) z$ q8 `/ N% v                return -ENODEV;* k8 L) p! T$ V( v, V, O

    6 k" X. m1 z# W4 `        /* 如果registered_fb[]里有默认的fb_mmap就使用它 */
    3 @* V# G! w1 K/ E, H2 R        if (fb->fb_mmap) {' _$ z/ C, j  {$ M% Z/ M! s) q' [
                    int res;
    # Z/ G% {) u* D& ~2 `                mutex_lock(&info->lock);
    - L9 |( E. ^6 y0 {                res = fb->fb_mmap(info, vma);# A$ o, y- m4 _7 O, g8 Z4 V7 h
                    mutex_unlock(&info->lock);  P) p9 h' b" E
                    return res;7 b, \1 F) z5 y  t
            }
    5 f' s1 E+ d- y( a5 E$ d0 D7 j
    ( t) v/ r0 R7 g9 A8 f* o  @* z        mutex_lock(&info->lock);
      ^' A7 e+ `5 _2 g& N9 ~, S. J
    2 }: G, A5 Z, K/ @        /* frame buffer memory */
    8 I# G, h) v" j. ]        start = info->fix.smem_start;        /* fb缓冲内存的开始位置(物理地址) */
    ) S( n5 P2 ?! M+ H        len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.smem_len);
    ) K' K* r, q2 i# ^5 E+ S        if (off >= len) {                                /* 偏移值大于len长度 */3 m" X* e/ M* v) x2 y6 k( b& r
                    /* memory mapped io */                /* 内存映射的IO */1 F/ x2 A; C; j0 I$ s" g6 N4 `
                    off -= len;% F. j1 P9 a! M' R3 M
                    if (info->var.accel_flags) {4 l8 b6 P' V8 u$ Y+ n+ B- J
                            mutex_unlock(&info->lock);
    , s0 b4 G; C7 P! X                        return -EINVAL;9 W$ ]. J$ ~) K6 n
                    }
    / O1 O$ R0 K- E7 ?7 a( O- O                start = info->fix.mmio_start;9 Q) G* {. Y9 X$ ~( l
                    len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.mmio_len);8 V  ?) b& s% u  w, b" n& x
            }; M2 F0 q5 }; c! l8 g$ F, f
            mutex_unlock(&info->lock);7 r* N6 D5 I: ]" O+ \" R( X
            start &= PAGE_MASK;
      P2 A& W. r+ Z  p- \$ Y        if ((vma->vm_end - vma->vm_start + off) > len)
    7 v% }3 a' t# x% \                return -EINVAL;
    ; z% j% V8 C" h4 w6 n        off += start;+ u. z9 s+ ~. C) X8 q( q
            vma->vm_pgoff = off >> PAGE_SHIFT;9 W/ f& J, n  I  o& }2 H
            /* This is an IO map - tell maydump to skip this VMA */
    / E" u2 x8 X2 X# T2 i7 U        vma->vm_flags |= VM_IO | VM_RESERVED;
    : x. o! D! P1 t9 b7 _7 N/ q! E        fb_pgprotect(file, vma, off);
    " R$ G& e7 ^. E+ h2 T% b& ?& s% b, k1 i# ?: T* I. n
            /* io_remap_pfn_range正式映射物理内存到用户空间虚拟地址 */
    6 |* t4 V: u" ^        if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT,5 t9 C1 c' E/ {. T0 V% b, w
                                 vma->vm_end - vma->vm_start, vma->vm_page_prot))% e9 d- n. W6 t
                    return -EAGAIN;( Q+ V- ~' s1 [1 u& F% i. I
            return 0;! D  k, F7 f0 o9 F1 }9 I/ R: Q
    }3 G- P+ J) b0 k$ F! M
    问:怎么写LCD驱动程序?+ H- t! l+ K  B( u
    1. 分配一个fb_info结构体: framebuffer_alloc" q' Z" t( f7 W( W
    2. 设置
    ' v  F  Y/ u+ g  @9 }6 D# B3. 注册: register_framebuffer
    0 `) O$ e8 @( T+ X4. 硬件相关的操作
    + ?# O4 _* j+ L6 j: _, `! [2 z( i: N- U  n
    3 G0 Q3 v+ |  O5 z& o. O" h, a+ N
    5 `+ h9 t) m, c! h) G
  • 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 21:36 , Processed in 0.156250 second(s), 23 queries , Gzip On.

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

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

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