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

linux lcd设备驱动剖析三

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

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

    [LV.1]初来乍到

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

    EDA365欢迎您登录!

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

    x
    上一节文章中详细地剖析了probe函数,但是从始至终都没有看到打开读写文件接口的操作函数,只看到了下面这个操作结构体5 N. P: Y: m! I# D3 y
    ' e2 _3 a) F6 O: \/ Z
    # _' {& P. ^0 ]
    static struct fb_ops s3c2410fb_ops = {
    ( _' m/ y" R: a2 t. @% x/ c9 D        .owner                        = THIS_MODULE,
    1 c$ S+ x4 J! j; O6 G        .fb_check_var        = s3c2410fb_check_var,
    * _/ a6 T/ y4 f: `# D; Z& t        .fb_set_par                = s3c2410fb_set_par,
    . z: V  G- J- _' U2 }        .fb_blank                = s3c2410fb_blank,
    ! ^8 i& b0 a% ~$ J! g; x4 G        .fb_setcolreg        = s3c2410fb_setcolreg,; y8 B* a7 W3 {; u
            .fb_fillrect        = cfb_fillrect,1 i8 m1 z1 c. N! m5 [6 S3 C
            .fb_copyarea        = cfb_copyarea,& Y8 Q: r! G  }7 v
            .fb_imageblit        = cfb_imageblit,
    " N* x& f. o' s) x$ `};5 G3 |3 A" k  I
    这并不是我们想要的打开读写操作函数。上一节文章链接:http://blog.csdn.net/lwj103862095/article/details/18189765. X( h1 c, O* {+ |; l
    问:那到底帧缓冲设备的文件操作结构体在哪里呢?& \. f) ^5 N, U) g7 g# Y
    答:在drivers/vedio/fbmem.c文件里。
    4 K' P1 `2 X' ~5 _7 m( w5 {( t7 w# s0 S9 C! T
    从入口函数开始看:$ h8 g( ], B! u, V( d/ X( X4 B3 x

    ) |7 j1 n8 R9 s
    " F8 H* B; S+ r) g4 X: Ustatic int __init) [* i  p: h8 J+ V& v7 Y
    fbmem_init(void): f$ Z4 q0 c$ E4 @3 Q
    {; C. B9 P: x. c1 z' r) V
            proc_create("fb", 0, NULL, &fb_proc_fops);
    ) \9 F; D* r% V) |3 \+ r! P( q" ^+ t9 p9 [4 M
            if (register_chrdev(FB_MAJOR,"fb",&fb_fops)); _, y- u3 ]! T; m
                    printk("unable to get major %d for fb devs\n", FB_MAJOR);9 A8 N' _% w- E5 `

    % S( y) \' M/ i! q        fb_class = class_create(THIS_MODULE, "graphics");5 C$ ?: `7 y+ F4 N8 g- p7 S
            if (IS_ERR(fb_class)) {
    & \1 x; m; l2 {3 b: R                printk(KERN_WARNING "Unable to create fb class; errno = %ld\n", PTR_ERR(fb_class));" {- W  M( Q3 w! H' ~
                    fb_class = NULL;5 T+ s0 ^$ F  F. e
            }3 u! [3 h6 ^; B2 i+ a3 X# n: }
            return 0;
    7 ]+ t$ d  I$ |0 B}- R+ Q6 n+ L, N( j! Y% Y
    fbmem_init注册了一个主设备号为29的字符设备,并创了graphics类(图形类)。4 A: v2 O8 r8 c5 D7 y
    字符设备有一个关键的成员是文件操作结构体
    / K3 Q- ]% p* H2 v% _) [5 B3 R  t/ r2 p3 G. G! Y
    8 L) e; U. L) R6 d0 |
    static const struct file_operations fb_fops = {
    6 T+ P$ S+ h/ }, y2 D1 N5 J        .owner                         = THIS_MODULE,
    ; {4 m7 t$ Y7 E' n( J2 W% f* p        .read                         = fb_read,
    9 Q; g% b( D1 R: A        .write                         = fb_write,
    * {8 X; e/ @% K( y: [% U4 M+ H4 n' a        .unlocked_ioctl = fb_ioctl," ?% i2 p. L! p, R
            .mmap                         = fb_mmap,
    + {$ T0 V: q8 n. l* [9 S        .open                         = fb_open,
    & b8 m8 {( h' w        .release                 = fb_release,
    # y. L' ~% m- R: _$ _  [. }#ifdef HAVE_ARCH_FB_UNMAPPED_AREA8 P3 C6 Y/ T: t
            .get_unmapped_area = get_fb_unmapped_area,
      A- q5 \; G' h1 r0 \; ?#endif* x3 O6 T  m. t+ p4 \6 H
    };
    3 `+ P2 C, O+ F! W$ R4 Z3 O( y理所当然的,我们应当首先看的函数是open函数, z  {' ?+ h" _  V" g; ?

    4 \/ d1 @6 K- N/ X: z1 xstatic int: f: [; n( d" x2 c
    fb_open(struct inode *inode, struct file *file)
    8 C2 `( P0 F2 |5 w0 h- D% k* ^- ~1 H__acquires(&info->lock)3 S8 [. z& G4 o: Q
    __releases(&info->lock)) B/ y! U' l7 O( D2 V
    {
    ! S: C/ j2 }% T        int fbidx = iminor(inode);                /* 得到次设备号 */
    3 Q& i+ M$ Y  s$ Q$ g* `        struct fb_info *info;  _* S% L) E8 f/ y6 _8 ^
            int res = 0;
    3 H& d* o6 S& t" h) k" C" |% }! Q2 m( B* J' [
            if (fbidx >= FB_MAX)                        /* 次设备号有没有大于规定的最大值32 */
    4 b' T9 S1 f6 p) z                return -ENODEV;                                /* 没有这样的设备 */% e: M! e8 {4 i$ @; q# `3 Z
            info = registered_fb[fbidx];        /* 使用次设备号得到fb_info结构体 */8 W4 n( }/ Z: g& C4 I% `
            if (!info). M4 K# w0 X+ g" i  b: }* h
                    request_module("fb%d", fbidx);( \7 P3 ^0 F3 w- V

    7 r9 h  L. S: h4 U2 ?8 M5 }        /* 再次使用次设备号得到fb_info结构体 */- E! {( V* b! x1 v; b
            info = registered_fb[fbidx];
    5 j* R% }! r# X+ b        if (!info)
    $ ^* v' e4 T4 ~7 _2 K7 l+ p                return -ENODEV;
    : Q, Y" r: D$ c4 x       
    0 c+ u$ Q6 J+ V; G. W* f        mutex_lock(&info->lock);                /* 获取mutex */
    " o$ z% q; K: S3 d! S8 r) x6 @0 p$ [' Y# Z# Z
            /* 获取模块使用计数module,成功返回非NULL */* z& O5 A# g; y: Y5 w
            if (!try_module_get(info->fbops->owner)) {
    ! @8 a3 L4 U0 I6 \  F                res = -ENODEV;
    : c* \- e5 ^5 p! g3 B; B7 J( b                goto out;1 F* v+ |: B0 n: ^* S% w* G* U
            }
    7 ^9 N2 f$ w/ k1 d7 U
    5 o( O/ @. {! T; b1 q        /* 从registered_fb[]数组项里找到一个fb_info结构体保存到2 G: T# n$ ^$ _( a! e- x* r% t
             * struct file结构中的私有信息指针赋值给它呢是为了以后调用
    6 H2 ]8 V% I% S  J, H0 B& \; _         * read、write、ioctl等系统调用时找到这个struct fb_info结构# N% k) U* l$ z; [1 v
             */, f+ A5 l1 X/ K1 t+ L  {% k
            file->private_data = info;       
    - Z4 U% U# k  O( w3 A; G  s! w7 j3 |
    ( S6 E$ @5 |2 O+ p' j# v( X        /* registered_fb[]数组项里有没有默认的fb_open函数,如果有就使用它 */
    , ?" a* i4 `+ Y- u        if (info->fbops->fb_open) {
    " j7 G7 v, J# {$ W                res = info->fbops->fb_open(info,1);        3 _& Q* r, A! h% u, ]$ L
    + e# N$ d( y4 a: N. T' J7 ?
                    /* 有默认的fb_open并成功打开就删除模块计数 */
    , F3 [' l5 Z* c6 a- X                if (res)
    5 ?9 O4 L9 [: U- _# |1 B3 b                        module_put(info->fbops->owner);
    ' r* [; }( j7 ^! M4 O2 E        }  \, m+ v5 p: A  F4 R
    #ifdef CONFIG_FB_DEFERRED_IO                /* 这里没有定义,不用理会 */8 {  K! D2 I) j1 q4 K
            if (info->fbdefio)
    ; b% f! B& ^0 a1 _+ ^8 r8 Q                fb_deferred_io_open(info, inode, file);8 m6 g0 V1 I( n& b+ P
    #endif0 r# M4 q8 _) K, J5 Y% o
    out:
    1 K. Y! ^& t- @: E4 J        mutex_unlock(&info->lock);        /* 释放mutex */- \+ S9 ^6 h: ^9 N0 }' q
            return res;: z9 |9 M" @7 P, A
    }! }  e! U( G. Q5 ]
    发现fb_open函数是围绕fb_info来实现的,而fb_info设置为registered_fb[fbidx]
    0 D( l$ f1 h8 ~! p问:registered_fb[fbidx]结构体数组是在哪里被设置?- C$ a0 `9 Q$ p8 |3 d& r' h/ O

    2 O  ]' |0 ?; R- Y. b, X' ?1 b答:register_framebuffer函数里设置registered_fb
    . y# ]/ A( R& f5 l5 j0 V6 K) [' J- ?7 x# h* `3 H# N3 n( P
    / ]. N6 t4 G! `9 w
    /* register_framebuffer()函数的主要工作是设置fb_info结构体的一些成员 */( `6 S& L6 H2 e7 {! W% w5 H2 ~9 g: S
    int
    . x$ P  a2 @. [register_framebuffer(struct fb_info *fb_info)
    7 ?. t5 Z8 e+ n9 V2 e{( b5 o9 d4 _( s1 x0 V# P/ n0 M: x
            int i;
    , h' M- |  m& t1 m* ~( C! K. a        struct fb_event event;8 Q3 C% S2 Z4 K+ A1 f& e8 U
            struct fb_videomode mode;
    8 u: P3 ~4 |/ w+ v' H
    ; p$ {3 }: z. F2 p) T$ |' {4 {        /* num_registered_fb代表注册帧缓冲设备的个数 */: ]8 H1 u% P) t* C. J
            if (num_registered_fb == FB_MAX)3 u# D$ b; Z/ g# P3 U
                    return -ENXIO;
    7 ~3 {  a3 S" M; S7 n: H* c
    ; X; N4 a4 Q2 f" y1 s        if (fb_check_foreignness(fb_info))+ h8 T( W$ T; S4 P5 v# }
                    return -ENOSYS;& G1 t5 R) _' |$ h& W1 w/ V0 X4 J
    2 g/ g0 _1 a2 e+ ~) j$ O9 X: V
            num_registered_fb++;! z9 F/ d  E, V# d9 r+ m+ N

    . A6 F! t$ X7 P# R1 T9 L. S# {6 [8 L        /* 当registered_fb[]项都为NULL,就会break,找到一个空的次设备号 */
    - _. n( J) [/ J5 e5 h        for (i = 0 ; i < FB_MAX; i++)
    & T6 n: d6 x! Q- W, E                if (!registered_fb[i])        " Y' |( s) \: w5 t  O* y! Q: m1 q
                            break;: S% T2 F# d- E- Q/ l+ ?
            fb_info->node = i;
    $ R2 s& r: ]" t* j; A  L- p; p        mutex_init(&fb_info->lock);                /* 初始化mutex */
    , t6 S0 Y! f4 K- G
    " e* B# _3 k2 h/ c0 g; b& p        /* 因为在init加载函数里只创建了类,这里在类下面创建设备 */7 S" S7 i4 B* ?0 T) f
            fb_info->dev = device_create(fb_class, fb_info->device,0 U, W/ X- d+ N  z+ d
                                         MKDEV(FB_MAJOR, i), NULL, "fb%d", i);( H! `+ z- b+ ?& ?. [( Q; y
            if (IS_ERR(fb_info->dev)) {
    7 p8 w, a( Q. |3 N$ d3 w: A* k                /* Not fatal */( f. N. ^) t3 ~  C3 B& w6 m+ F# m
                    printk(KERN_WARNING "Unable to create device for framebuffer %d; errno = %ld\n", i, PTR_ERR(fb_info->dev));# k! ?9 d" c% t- b& x' X- ^( |- t  G' F
                    fb_info->dev = NULL;
    & w0 V& \7 c0 y6 x, Y* X  D1 v        } else
    3 F8 u6 i$ E$ O7 t! Z0 z                fb_init_device(fb_info);        /* 对struct fb_info做一些初始化 */
      r& U& {, G; ^( z/ _9 {2 P0 s* [2 R

    * |* @! x4 Z- F" e% r% r6 D        /* 初始化fb_info->pixmap结构体成员 */  x7 I. L) P* R$ }
            if (fb_info->pixmap.addr == NULL) {
    9 O& q$ n! }" z% s/ u5 O7 f: t# S                fb_info->pixmap.addr = kmalloc(FBPIXMAPSIZE, GFP_KERNEL); /* 8K大小 */* A2 ?5 B9 L/ \# q
                    if (fb_info->pixmap.addr) {
    & X5 l' J+ ]! G$ C& G                        fb_info->pixmap.size = FBPIXMAPSIZE;        /* 8K */% g' H, w# n& s
                            fb_info->pixmap.buf_align = 1;+ a8 b$ f% Y9 s8 G. s! n
                            fb_info->pixmap.scan_align = 1;! ^3 B, o6 k8 O8 X/ t$ B4 E
                            fb_info->pixmap.access_align = 32;
    # ]* R' Q, p, G0 I% H; ~/ `9 n& C' q1 O                        fb_info->pixmap.flags = FB_PIXMAP_DEFAULT;
      F0 W: E' Q) F# G" y3 j                }
    . ~+ o  n6 c( q* C/ o5 J        }        ( \/ u, Y* P' M
            fb_info->pixmap.offset = 0;& D& Y  {2 i* t- f/ O1 s  c& a

    2 ^+ q6 {0 t4 n3 m. i( u! y9 W        if (!fb_info->pixmap.blit_x)! l* Y9 @& a( o( a/ A9 ?  m
                    fb_info->pixmap.blit_x = ~(u32)0;9 H; c0 X3 o: B9 D# V) e8 {. P

    & [* \/ [# \% x6 i2 A        if (!fb_info->pixmap.blit_y)0 o  {# ?9 z8 C5 p. \, X
                    fb_info->pixmap.blit_y = ~(u32)0;( C! V! |; \6 j

    ; G+ [# J; G* x6 R, X: G        if (!fb_info->modelist.prev || !fb_info->modelist.next)
    0 {& s# b* U+ z* t                INIT_LIST_HEAD(&fb_info->modelist);        /* 初始化modelist链表 */! o+ ~9 }, q  j6 _
    ' A1 z9 Y1 G  D) p, _" R
            fb_var_to_videomode(&mode, &fb_info->var);9 e9 @2 Z- c  Y5 o+ b6 y
            fb_add_videomode(&mode, &fb_info->modelist);
    + k* q( F& l9 z, {0 M: k+ y2 ~7 o- p; H* Y9 i, l
            /* registered_fb[]数组项在这里被设置 */1 l6 V* u. ^- F- y/ l% i7 b9 Z; M
            registered_fb[i] = fb_info;3 q# k3 ]2 t: A" |8 ?& F- x
    ; S9 C/ K$ {; l+ m( u2 r
            event.info = fb_info;$ J& L5 l4 i8 _2 X

    6 C. F! B, L8 j6 w* G9 j) ?       
    6 X6 o, R3 W4 o  a9 C+ U* k        if (!lock_fb_info(fb_info)) /* 上锁 */- K* I' b* L, w
                    return -ENODEV;
    2 m& ]- }  g- R! y) B. {. Z        fb_notifier_call_chain(FB_EVENT_FB_REGISTERED, &event);
    ; C. f& c* u% n3 {        unlock_fb_info(fb_info);        /* 解锁 */
    ' Q8 ^: e9 X: [        return 0;1 @, m' Q1 k7 Y# B( f) t
    }
    ! ?5 D% W& Y0 ~5 N; i( u& b( u" ]" _fb_read函数源码分析/ f0 c' P+ M0 }9 \( ]2 G% v
      M  z% n1 l% X7 d# J( v
    static ssize_t
    0 _; G8 }9 R) s0 tfb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)& B: y7 V8 T5 O2 V
    {
    & V0 A& z- Z: ~* F; d5 |        unsigned long p = *ppos;
    $ d: {( N4 I6 y+ ^8 a5 S# q( C& B6 `        /* 通过file结构体的成员得到inode节点 */: h7 g( n( ]2 @: m
            struct inode *inode = file->f_path.dentry->d_inode;4 z8 T; B& T- ^& V

    3 `9 d& V) p2 E( M, e; \2 D% y        /* 获取次设备号 */, k; ~1 G& P6 C- b
            int fbidx = iminor(inode);- e2 q* ^' {+ S+ u3 ^+ ^% O
            /* 以次设备号为下标找到一项fb_info结构体 */# h3 _) `9 l3 t
            struct fb_info *info = registered_fb[fbidx];
    : @; |; s3 P1 x! k          y" J! n1 S0 x4 J5 j1 |3 ~
            u32 *buffer, *dst;
    2 w8 w% |4 |, w" P0 [7 O+ U6 T5 K        u32 __iomem *src;
    " M+ g/ S. f0 g7 o9 n" e        int c, i, cnt = 0, err = 0;
      z# {, ?+ n  M( u& I        unsigned long total_size;# z% ~7 U0 t: b( I. `4 x

    & Y* y/ K3 J: P/ H" y8 a        if (!info || ! info->screen_base) /* screen_base是虚拟(显存)基地址 */
    - U: e/ i# w7 k; \4 {- m                return -ENODEV;1 L! h9 ^3 H, [9 U  ~: P

    0 u5 u$ z# B# W* B* ?4 u6 Q        if (info->state != FBINFO_STATE_RUNNING)# W! J1 I0 D1 h/ Y
                    return -EPERM;                /* 禁止操作 */
    7 x5 \# u- U" |! X; h+ ]' D
    " _. i% P4 N9 H* _: x2 L1 v        /* 如果registered_fb[]项里面提供了fb_read()函数,就调用下面的函数 */( ?/ @% j4 k! s* ~
            if (info->fbops->fb_read)
    : B+ r" a/ s( K                return info->fbops->fb_read(info, buf, count, ppos);- [# f, k, P" x  Z2 f
    0 Q% X8 c5 a7 n' F# o9 q, `4 G; \% v
            /* 没有默认的读函数就从下面的screen_base里读数据 */0 n! }0 D' ~; g) O4 w' K  z# t
            total_size = info->screen_size;  /* x*y*4,x,y分别为屏幕分辨率 */6 B9 e2 d" O/ O# ^
    3 F$ z$ c! ]) p$ D' P
            if (total_size == 0)8 m: p% k3 q2 p8 }% `2 K2 D2 F
                    total_size = info->fix.smem_len;        /* fb缓冲区的长度 */4 u2 }. Q) f$ G6 w

    8 y2 h) C, B* @: @2 ~  \        if (p >= total_size)                        /* 调整读的偏移位置 */' V# X3 ^  U6 o; \- ^1 ?' O
                    return 0;4 H, {/ ^5 G1 Z) L/ y
    + E. Y. \; u9 I4 I; n
            if (count >= total_size)6 A3 O# o+ C" U, p6 X; O$ I
                    count = total_size;                  /* 一次性最多读多少个字节 */" `  h0 w( i7 S) m
    2 @" F1 x3 e# `
            if (count + p > total_size)* w* w6 v3 Y* `- q3 W* S
                    count = total_size - p;                /* 调整读的位置及能读多少字节 */
    ; O0 s. g/ B/ Z% a( S& Q1 S$ n9 U# O$ `. X" I# Q9 l! Z
            /* 分配内存,最大分配4K的大小 */
    1 q0 U) n' R6 l0 f        buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count,
    - f9 u3 u1 F- U/ K! J, J                         GFP_KERNEL);                ; M$ B$ ~, a8 j$ T+ y6 X
            if (!buffer): _" e( G- T' m) e& {0 K
                    return -ENOMEM;" g8 N  a' x$ E+ ~

    . Y$ _# ~* |, C  ^  [  J! B        src = (u32 __iomem *) (info->screen_base + p);  /* 源虚拟基地址 */
    $ L2 ^9 s, F* Q, [; ?
    $ `- E2 P, t8 b/ v  R        /* 如果registered_fb[]项里面提供了fb_sync()函数,就调用下面的函数 */1 U* c2 E7 n0 M
            if (info->fbops->fb_sync)               
    : U) S9 {2 t$ u1 y+ D6 p2 g* q$ y) P                info->fbops->fb_sync(info);
    3 K2 M9 V; h3 t+ R5 d5 d5 u# H$ g2 @) p
            while (count) {
    : n2 A- W. H' G# f& {" a+ |                /* 读多少计数变量,单位为byte */
    * T: i& E* u+ O( D, [" ?                c  = (count > PAGE_SIZE) ? PAGE_SIZE : count;
    ' l1 D- q# Y) R3 y8 Y7 d3 ~2 n                " ]8 {  C* h6 n
                    /* buffer是指向刚分配内存的首地址的指针 */2 ?/ u' b% A* k: T+ v$ G# s5 ]
                    dst = buffer;        /* dst指针指向buffer */
    5 J+ N3 I1 B; n. ?$ J# K) E% ]0 j! }" `& S! x* `. p* ^
                    /* 先除以4,因为每次读4个字节 */
    . x7 y. k1 h8 T! B% Q                for (i = c >> 2; i--; )                        , p) D9 C! F6 s' R- M
                            *dst++ = fb_readl(src++);        /* 拷贝源虚拟机基地址的数据到目标地址 */
    + E6 p" S; S5 M: \6 Q6 D, |: o& ]( T& A% J2 b% D
                    /* 判断是否以字节为单位来读取 */
    % c2 y6 o6 i- D8 u2 v3 k. o                if (c & 3) {                                        2 g% E% @2 Q* g7 t
                            u8 *dst8 = (u8 *) dst;
    ( x' t7 p* A' F9 m+ u                        u8 __iomem *src8 = (u8 __iomem *) src;
    ) t  q) j( V6 x9 [1 U, }
    ; X- R" L; p" L3 x! ?' [                        for (i = c & 3; i--;)
    ' A" H# l) x, J9 A+ F( R/ F                                *dst8++ = fb_readb(src8++);/* 拷贝源虚拟机基地址的数据到目标地址 */
      B! {( v8 u1 L  s
    . |+ ~( }8 R0 W& P3 z; D                        src = (u32 __iomem *) src8;* o. d8 Q- @+ n% P
                    }" ]4 E) m% h1 N( o# ~9 c9 I

    6 |1 M6 |* U  \7 W                /* 从内核刚申请内存的地址buffer拷贝c长度的数据到用户空间的buf里去 */. @9 C- {/ B  {: C
                    if (copy_to_user(buf, buffer, c)) {
    * Y# i3 \5 W2 r  D, G: v                        err = -EFAULT;        /* 成功拷贝,则err返回值为0 */& c& l- x/ t- Z# y# e, V- R( N
                            break;8 w- V. A% k3 r) g5 p& ]
                    }2 f3 n/ e8 I" @5 H5 X0 w, h
                    *ppos += c;                /* 调整偏移位置 */5 Z  r! j9 ?8 i" |  y
                    buf += c;                /* 调整用户的buf */
    0 p+ p0 {& O+ z. h7 s                cnt += c;9 x7 Q6 r: j6 W
                    /* count变量减去已经读取的c数量,用于判断while(count)是否为真*/( p+ m- o3 Q& k( D
                    count -= c;               
    . Z1 Z* `1 f! q5 _* P" v0 W        }2 [* L; p- j, A1 y& L

    7 ^; g* D$ y, [        kfree(buffer);                /* 释放内存 */
    6 n! H* M$ p; W: |. ]" d) k1 t2 p* a' o7 Q% o; ]
            return (err) ? err : cnt;        /* err = 0时,返回被拷贝成功的数量cnt *// a# f4 e2 j2 _/ v( N3 z6 _: [5 |
    }) b7 k* V; t( ^- N3 m
    fb_write函数源码分析( A( O% V( J  F6 f- S( l1 z

    8 }( g" k$ m% hstatic ssize_t
    ' K- X' v6 b% M0 rfb_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
      e6 n( K2 K: j- O& T3 h{
    " v+ O! _8 c$ E        unsigned long p = *ppos;
    % A$ u, ]4 [+ Q3 f        struct inode *inode = file->f_path.dentry->d_inode;' L/ t, f4 _$ O4 Y9 q, X3 c4 i
            int fbidx = iminor(inode);
    2 U  L1 L% K4 c3 u5 Y& p9 G        struct fb_info *info = registered_fb[fbidx];
    ! i& _, I" o& j2 _$ x: e$ i        u32 *buffer, *src;
    0 _2 a) N+ R$ I' ?+ h* @- p        u32 __iomem *dst;
    # \" h- S3 g( u: Z& }6 L        int c, i, cnt = 0, err = 0;) J/ _% E6 V" L+ G# P4 B
            unsigned long total_size;3 c2 |0 H! Y% w8 }
    ) ?" m6 M5 `  Y. O7 s8 i/ K8 n) G
            if (!info || !info->screen_base)
    * K' c* K- C( H# a6 G                return -ENODEV;0 t7 b8 D; W0 V5 d6 H; W, q

    , l7 R4 w* n, ]+ X: L1 e* C        if (info->state != FBINFO_STATE_RUNNING)
      V+ }) l1 L- G. r& S* x1 \                return -EPERM;/ }0 i" @2 v+ F5 A
    ) f- J* v) s: ~! w5 [$ m
            if (info->fbops->fb_write)1 q9 v- `- {* Z% o% F1 B5 G' d0 w
                    return info->fbops->fb_write(info, buf, count, ppos);- K# a# R. F5 _: G
           
    . W4 n; |1 c$ [" M5 t        total_size = info->screen_size;
    2 O  H% l4 A5 j. G3 w2 b! c# h" H7 `2 u$ K* z
            if (total_size == 0)
    4 r4 t4 ?) _% e: R                total_size = info->fix.smem_len;% {- H6 M: u  j$ G
    ! y3 y5 E- J5 v
            if (p > total_size)
    4 y) r$ H0 S+ W                return -EFBIG;' Y( ]: r* N6 h; T' h" @4 v# ^$ Q
    : O: F8 V6 W1 g. Y1 I4 q
            if (count > total_size) {. q) M8 B& K# P
                    err = -EFBIG;4 q+ p# U$ e8 j! I( x; @: o4 {
                    count = total_size;# A$ x2 g. ]( E1 P3 t2 c
            }, N5 n$ o8 Z3 v) I
    4 f. Z: c$ E# F8 G# n5 x( ?
            if (count + p > total_size) {
    - D- i, M' [! ~0 `+ b' v) ~                if (!err). R. x$ M$ y  {$ g# D$ H- J9 I
                            err = -ENOSPC;# O4 e% A7 C. z* ?
    ! R4 i  x) ^0 n5 m8 X2 h
                    count = total_size - p;/ q* ~" C8 `4 T0 i. V! I7 L$ Y
            }4 _- l, ~. p$ V# N# ^

    2 N# M) d" k3 B; b        buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count,0 [5 f/ G, p8 `5 d
                             GFP_KERNEL);. k6 J' W* B0 s7 r; k
            if (!buffer)3 i) {- K$ Z' e$ r
                    return -ENOMEM;
    ; P& h3 c; d5 E1 _, G$ u4 l5 P- G3 }- O$ K) v' f( T- q; ~) R
            dst = (u32 __iomem *) (info->screen_base + p);        /* 源虚拟基地址 */+ `& T8 Y; v  g9 W6 ^2 M

    ' q- K- X6 Y- H! t        if (info->fbops->fb_sync)
    , U2 s" h( Y  W  S& x                info->fbops->fb_sync(info);! V5 w) o6 D7 k" Z
    : P) s/ _& ?+ H& n2 c3 W
            while (count) {
    + M$ w" I! a$ O" u. T% G                c = (count > PAGE_SIZE) ? PAGE_SIZE : count;2 E& v( q1 u) K+ s6 o* _! U
                    src = buffer;        /* buffer为指向刚申请的内存的指针 */* |& b7 g+ @1 h; [$ r) S! S
    ( @! M7 u( i& S8 _) \) K* T
    " o& b. }: T2 ~  ~; ]
                    /* 从用户空间的buf地址里拷贝c长度的数据到src内存里,成功时返回0 */
    % v/ E' _% f% V- ?1 S                if (copy_from_user(src, buf, c)) {
    ! t, K# A5 O  t                        err = -EFAULT;
    % H. x. D3 |9 ]7 P# r6 Y4 p4 J                        break;
    , a' Q1 Y/ F& Z' S( J" f                }
    0 W' T8 ?2 d3 v( F- ^+ |  ^1 \$ E6 v. N4 V5 ]
                    for (i = c >> 2; i--; )                        /* 以4字节为单位拷贝数据 */
    & ^/ U5 _) [1 P9 N' q                        fb_writel(*src++, dst++);        /*     *dst++ = *src++     */
    + |8 k6 m4 I0 p, A" |
    * }. v% d7 c  u: h                if (c & 3) {                                        /* 以字节为单位拷贝数据 *// C3 L* i! i9 Y8 e. O$ D0 B7 {
                            u8 *src8 = (u8 *) src;
    - h$ K" y& c  P* e$ T. ?/ }/ \                        u8 __iomem *dst8 = (u8 __iomem *) dst;
    # F6 D' [7 w& C: x0 \; [6 R- m7 B0 s$ [, V% s6 [
                            for (i = c & 3; i--; )
    % S1 D0 s1 f7 P3 `6 A2 n                                fb_writeb(*src8++, dst8++);
    ( e: O) f9 M6 O/ n  x5 ?
    + O8 h0 u& B1 ~# Y5 \8 V' e2 E                        dst = (u32 __iomem *) dst8;
    % S* ?7 u0 `- K# }# h                }
    2 q$ ^6 [/ x9 D: a! E6 m& N
    / L+ l3 T. V: r9 w5 a! k4 q, I                *ppos += c;; y& P+ O' m! i) l
                    buf += c;
    1 x4 C6 |2 [. m6 h7 b# m. z                cnt += c;9 L9 _* X) p9 U- B$ U6 P" k  k
                    count -= c;$ T* R, B, ^  {1 y. P, ]
            }
    0 j( U, E. ?. @, H" u3 `3 i& j
    8 E9 L( F  w5 h4 z* K7 B$ ]: B        kfree(buffer);; W4 }: `% w/ F9 t

    4 z; ?4 T. y4 r6 Y0 `        return (cnt) ? cnt : err;9 S: ]/ O4 G* n+ y5 X( S
    }$ {% y4 x5 w& S; Z8 j0 j
    fb_ioctl函数源码分析) s" V& r0 F! h7 l1 W. P. ]
    1 O" p% k& h/ a# |& @1 _0 I
    static long fb_ioctl(struct file *file, unsigned int cmd, unsigned long arg): h! ~: _; Y2 C/ M
    {+ w3 u! h4 C2 p
            struct inode *inode = file->f_path.dentry->d_inode;
    + t9 c' D3 O- J+ m        int fbidx = iminor(inode);
    % d; h$ J9 W6 Q" E$ x        struct fb_info *info = registered_fb[fbidx];! R- u  x! Q  E0 M& L: I. V: v4 Y
    / D) U8 b3 p7 E8 L" M7 d* Q3 |) w2 M* o
            /* 这个才是真正的fb_ioctl驱动函数 */
    6 z7 Z. _4 {. \        return do_fb_ioctl(info, cmd, arg);        & [8 ^; {  @: j" f! O) Y
    }
    6 [8 Z( g* T+ ^: O+ ?: P# ?; C5 z$ Odo_fb_ioctl函数根据cmd来设置各种命令,这里仅举例说明:2 O% w" B: }( u5 q* x/ Q* Y6 X

    8 \0 Y5 A# |7 ?" c$ ]switch (cmd) {" i8 K' v2 L4 @& X
            case FBIOGET_VSCREENINFO:                /* 获得可变的屏幕参数 */
    / R! z6 w' y4 e                if (!lock_fb_info(info))        /* 如果info->fbops不为空,则上锁,成功返回1 */
    / H  c  k* C3 g$ ?2 [8 ?                        return -ENODEV;
    5 V5 r5 d4 e: B5 C                var = info->var;                        /* 可变参数变量的设置 */
    / E9 s, S" x$ U5 l% i# R" Q                unlock_fb_info(info);                /* 解锁 */
    7 R$ {% J3 L9 r  Q' `& H
    $ S$ @' b) D- r5 n' {4 d                /* 从内核空间的var地址拷贝var大小的数据到用户空间的argp地址里去 */3 f/ x/ d  `" K* D" \
                    ret = copy_to_user(argp, &var, sizeof(var)) ? -EFAULT : 0; /* 成功返回0 *// B! u; [3 f6 a  u) Q
                    break;5 F" D" _7 o1 ?5 d
    fb_mmap源码分析:
    * w" l% n( @. [# B4 [/ B, u# q; [9 h, i# H
    /* 这里分配的显存是在内核空间分配的,用户空间并不能直接访问,2 C: g' m# r8 E+ G+ r2 U2 d
    * 所以需要用到这里的mmap函数,直接将这段内存空间映射到
    - A5 Q& V; O+ L2 {$ e * 用户空间去,用户空间就能访问这段内存空间了。
    - [+ p* `$ [+ f( j */8 j* X8 A& `5 i, G
    static int; d$ F( W, w$ W2 c* G5 U
    fb_mmap(struct file *file, struct vm_area_struct * vma)  |: K4 n6 _: U: Q2 m6 ^, P9 a
    __acquires(&info->lock). v/ }7 @; k/ }9 j4 P. H$ Q0 s4 H1 X5 @
    __releases(&info->lock)
      O0 V8 Q7 ^1 N6 e{% u( W) i) j  T! K: P
            int fbidx = iminor(file->f_path.dentry->d_inode);
    1 N! ~# i1 l& q" O% }* ~& c2 |        struct fb_info *info = registered_fb[fbidx]; /* 通过次设备号找到fb_info结构体 */
    , E% }# G& {" \$ V1 X9 L        struct fb_ops *fb = info->fbops;1 R( k1 n- b# m3 H% }
            unsigned long off;8 O% R' n, g. J+ o
            unsigned long start;
    $ ~$ E% x  S3 M1 E" p9 }- b        u32 len;
    $ M; C" |, [; ]1 V' _
    5 V+ U- e$ _3 b1 B0 v% S9 j! ~        if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT))
    ) C# q9 E7 Y: J9 l+ V5 a( N0 q* F                return -EINVAL;) _' e  A3 k( b/ w3 p, q1 S+ g
            off = vma->vm_pgoff << PAGE_SHIFT;4 J8 d( c6 a: n  \# T+ r
            if (!fb)
    + p- k) }( m& {                return -ENODEV;
    : l! ?. w6 ^+ J; f, s2 y, l5 n% j* I8 D: Q7 M
            /* 如果registered_fb[]里有默认的fb_mmap就使用它 */
    8 o# b# _2 k! W3 k: n, u        if (fb->fb_mmap) {
    4 ^7 T0 A5 E* a                int res;
    . Y/ P$ W; a4 C( J, N1 a" x2 z                mutex_lock(&info->lock);' V  i4 l/ N9 p" @" K
                    res = fb->fb_mmap(info, vma);) j' W, U: V/ B9 X: v. `
                    mutex_unlock(&info->lock);, z# n: X$ N  K! w
                    return res;7 ~3 O! q0 O$ @5 \9 R- U6 Z
            }* f5 n. q& @' u  B# t7 M
    3 f1 s3 p0 q8 j
            mutex_lock(&info->lock);
    ' B4 X: n; p( V0 S2 I, N
    3 q6 P1 W5 W3 q3 b; j, g        /* frame buffer memory */
    - A3 ?& B; m6 J  [. {' W! ~3 R0 z9 a        start = info->fix.smem_start;        /* fb缓冲内存的开始位置(物理地址) */- y! y/ ^% u/ i' m+ b& E  w/ q
            len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.smem_len);
    : `( \3 O# K: h6 Z8 M  R        if (off >= len) {                                /* 偏移值大于len长度 */4 H/ m: V. M1 p: V1 {
                    /* memory mapped io */                /* 内存映射的IO *// A& N( E% r- \/ k6 ]0 _
                    off -= len;+ s9 z/ H( L$ x  }, e
                    if (info->var.accel_flags) {
    - s1 a! f) A: F* ?1 x$ h7 O2 k8 A. ]                        mutex_unlock(&info->lock);# @3 I5 [8 u5 w; i# x( ?  i+ _
                            return -EINVAL;
    ; K6 t5 G0 R- V( {3 ^% M/ W& D: y                }6 I# A! f4 h0 P& w5 U. v
                    start = info->fix.mmio_start;
    4 r$ Z; ?; I% m' }/ q* c                len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.mmio_len);1 A$ d! u7 b  L0 J2 ?) r! v# x
            }: _% e6 J: B/ U- y$ z) \
            mutex_unlock(&info->lock);
    6 Q- R0 ^9 O+ p, |5 I5 V* i# L0 |        start &= PAGE_MASK;* d' l0 h3 L- G. u
            if ((vma->vm_end - vma->vm_start + off) > len)8 M2 I7 ?: _- J0 b: J+ h& a
                    return -EINVAL;# n; [$ E& h% D
            off += start;+ m$ h9 [; A+ [" l/ ?4 k, h
            vma->vm_pgoff = off >> PAGE_SHIFT;; s% Y4 T3 `( o" \
            /* This is an IO map - tell maydump to skip this VMA */* i6 u7 Z5 i2 M' _
            vma->vm_flags |= VM_IO | VM_RESERVED;8 k( X3 E$ S/ ^
            fb_pgprotect(file, vma, off);
    ' `) j# `- }, _9 T" O2 Z# c1 G6 F
    1 a; R) C% `! j        /* io_remap_pfn_range正式映射物理内存到用户空间虚拟地址 */
    3 _8 l1 D) J, V1 e1 K: ?        if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT,
    . o6 ?8 h3 i, X                             vma->vm_end - vma->vm_start, vma->vm_page_prot))! f( e4 K/ }- S4 g) [
                    return -EAGAIN;- q/ O% J' Y* M5 P
            return 0;
    ' K1 X4 Z- S9 w& f& T  j. M% {, w% ~}* R* h' i2 Z0 e# H
    问:怎么写LCD驱动程序?9 a" d" d  \. U2 Z  F) U) Q2 |0 Z
    1. 分配一个fb_info结构体: framebuffer_alloc" z# i5 T* l  G  `7 y
    2. 设置( M% l# ^7 S$ U- L( b  p% v
    3. 注册: register_framebuffer
    & D) s1 ~, `: K3 i4 v& n4 t4. 硬件相关的操作
    ; z' S+ o  m. I0 [. Q
    ( }" f6 H( b/ N2 u$ C7 h
    2 i7 e/ m# g2 K! w3 _, W
    $ @* ^/ v1 ^4 |6 \  Q8 Y8 A' [, x" v
  • 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-7-2 20:15 , Processed in 0.078125 second(s), 23 queries , Gzip On.

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

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

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