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

linux lcd设备驱动剖析三

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

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

    [LV.1]初来乍到

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

    EDA365欢迎您登录!

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

    x
    上一节文章中详细地剖析了probe函数,但是从始至终都没有看到打开读写文件接口的操作函数,只看到了下面这个操作结构体
    - R- q: k1 B& R: v' y' }. E$ S8 a' U' J0 i2 E8 U; h6 E

    ) |" Y  t( U# \" u1 \# ^static struct fb_ops s3c2410fb_ops = {4 E! o+ n6 j& s4 }0 l5 S
            .owner                        = THIS_MODULE,( g# A2 r2 f  P
            .fb_check_var        = s3c2410fb_check_var,* `9 n5 T# l6 Q5 a" d+ W. c
            .fb_set_par                = s3c2410fb_set_par,
    * I7 [/ R  y' o6 u8 K        .fb_blank                = s3c2410fb_blank,9 k; d  ]& M( y1 s: M( h0 _3 m
            .fb_setcolreg        = s3c2410fb_setcolreg,
    : D& Z# W' Z$ }3 x* R( a        .fb_fillrect        = cfb_fillrect,
    # C. S5 h$ n2 q$ K( i        .fb_copyarea        = cfb_copyarea,
    + p$ s* E, ~2 @! Y( O        .fb_imageblit        = cfb_imageblit,  g( G5 j/ V' s1 s! x/ j- B
    };0 h2 q8 c& [4 `' n8 ]
    这并不是我们想要的打开读写操作函数。上一节文章链接:http://blog.csdn.net/lwj103862095/article/details/18189765
    3 Q; h8 ]( W3 L$ ^. Y问:那到底帧缓冲设备的文件操作结构体在哪里呢?" b) C7 K0 F4 z
    答:在drivers/vedio/fbmem.c文件里。
    - v% V. i  ^4 S7 E$ L7 I8 V
    % k4 X) _( t  V9 A5 }' F从入口函数开始看:
    , q! m( q$ u- X% l8 ~0 O# n
    % X2 E4 P* l8 _9 Z) q3 r' `
    6 T% c2 w% e' n) W' W0 a  G4 Mstatic int __init8 T5 m0 G/ E5 J
    fbmem_init(void)
    1 W7 `' r/ |$ ]2 U6 A{
    & \$ X  c/ B! `5 a1 k- Z/ i% H        proc_create("fb", 0, NULL, &fb_proc_fops); 4 p5 p- E* a4 t. @

    $ u+ V) E& c% h* w        if (register_chrdev(FB_MAJOR,"fb",&fb_fops))
    ' y% `8 e1 K: d. _! e' e                printk("unable to get major %d for fb devs\n", FB_MAJOR);
    ' Q/ m- T" ?! {$ ]. ^9 t' j8 _  b( N" T: F6 D! P6 i* Y
            fb_class = class_create(THIS_MODULE, "graphics");) f4 Z" `' w' ~
            if (IS_ERR(fb_class)) {
    8 P7 k8 Z) |1 F: z% j9 I0 ^$ q                printk(KERN_WARNING "Unable to create fb class; errno = %ld\n", PTR_ERR(fb_class));
    % \! O4 j5 U- y                fb_class = NULL;
    : V; }( a1 u. [9 G5 ]! C. k2 o        }$ K8 N2 w7 t! }
            return 0;/ p4 ^( k& Z4 k
    }
    0 a! s* c& I& v* Q4 y! i, Efbmem_init注册了一个主设备号为29的字符设备,并创了graphics类(图形类)。
    : M; b6 y9 W0 A1 ]1 B/ B* R字符设备有一个关键的成员是文件操作结构体2 T- w* D8 V- r# [' v
    , |/ F" t% T, @( c# `' z: I

    9 w# L) C- s- G$ q5 J4 Y5 sstatic const struct file_operations fb_fops = {5 }. Y/ V, A4 X: O" u, R* c' a
            .owner                         = THIS_MODULE,
    ' V' W# n) W1 P        .read                         = fb_read,
    # R" Y8 k: b+ |        .write                         = fb_write,
    8 ~, d$ P6 H8 A# U* S  i' u) O! l        .unlocked_ioctl = fb_ioctl,
    3 S' Z: p, ^. f  s$ X' `        .mmap                         = fb_mmap,
    & E: R1 _4 q; B, R2 L9 Y3 S        .open                         = fb_open,: v* U  t: ]8 v& ?/ e& o% W
            .release                 = fb_release,5 ?, {2 p6 c! H5 H) m+ ?% j
    #ifdef HAVE_ARCH_FB_UNMAPPED_AREA
    0 p# A: l9 W9 ~        .get_unmapped_area = get_fb_unmapped_area,0 ]0 G2 o1 e% A9 p7 ^/ Q0 @+ c  }
    #endif; q# ?% _# H  @/ V) a. c
    };( B# K, Y" ~9 b2 t  N  ^( P5 Y# ^4 f
    理所当然的,我们应当首先看的函数是open函数0 `. y# P( M& q1 d, b* b

    # {/ K- b# z/ s' \' Z/ Astatic int* f" G7 y1 I3 g9 \! S: x
    fb_open(struct inode *inode, struct file *file)
    9 A+ h- q/ \$ A__acquires(&info->lock)
    0 l2 Z" {) {6 ~5 Y$ |  o/ d__releases(&info->lock)0 p! j& ~& n  e2 b/ k, y3 y# ^4 [
    {
    ) H% N' W" u& \, R8 i        int fbidx = iminor(inode);                /* 得到次设备号 */
    9 k6 H" E7 z1 V# _        struct fb_info *info;
      w2 J. t2 m4 n        int res = 0;
    4 V3 \+ s8 N" E
    % a% P  r) G+ }2 q        if (fbidx >= FB_MAX)                        /* 次设备号有没有大于规定的最大值32 */
    7 p5 W7 P* y# t/ }                return -ENODEV;                                /* 没有这样的设备 */( H, u3 o" b0 R6 I0 C
            info = registered_fb[fbidx];        /* 使用次设备号得到fb_info结构体 */
    ( j, L1 i! a5 S        if (!info)
    " [- m: c9 A' }# I$ [                request_module("fb%d", fbidx);6 E+ {$ r& G$ |3 x2 R
    * Z/ l4 ^) u* a
            /* 再次使用次设备号得到fb_info结构体 */8 e# T5 o  i5 p+ O9 Y
            info = registered_fb[fbidx];! ]8 T/ M+ q8 M: z' ], F
            if (!info)* S' A; g% R, A/ r2 o
                    return -ENODEV;
    2 H0 p3 K' D- `" x  F       
    $ O2 c0 _! y" ~8 }& e        mutex_lock(&info->lock);                /* 获取mutex */
    ' X- B& i" h$ p4 A# Y
    6 F6 F' w; U/ V  x1 G7 W( v        /* 获取模块使用计数module,成功返回非NULL */9 u; D3 u0 N  G3 y6 s
            if (!try_module_get(info->fbops->owner)) {* ?' e, }- r# G0 Z; X
                    res = -ENODEV;) b) R2 K7 E' x+ |& W+ D& U8 \  ?
                    goto out;/ J% i0 Y3 s# k( U
            }
    * N$ `5 Z" R& a3 ^4 g  W* a# u% C- y( o( n, G- q3 P5 R2 w
            /* 从registered_fb[]数组项里找到一个fb_info结构体保存到
    2 m) m8 i$ s7 t) i' i         * struct file结构中的私有信息指针赋值给它呢是为了以后调用
    & d) N  T, j" B+ k) X( H5 r6 r: j         * read、write、ioctl等系统调用时找到这个struct fb_info结构) r6 I) o8 ?" [, c- T- I& i5 \
             */) Q/ a) u  _% \6 O
            file->private_data = info;       
    " ^0 [% M7 d2 {! U3 |8 W% S
    . K2 u0 _; K7 G) u- t; [        /* registered_fb[]数组项里有没有默认的fb_open函数,如果有就使用它 */4 u; O+ ?$ p9 O
            if (info->fbops->fb_open) {1 Z- `$ m3 R; l4 D
                    res = info->fbops->fb_open(info,1);       
    ' Z1 h% {, s* f6 J, z/ c5 Y9 f+ J  U0 w6 D4 U8 k1 \
                    /* 有默认的fb_open并成功打开就删除模块计数 */# w' f! G* [' S# y* e) z
                    if (res)
    ' _5 |9 F. _7 P& |  k2 y                        module_put(info->fbops->owner);
    6 J% _; G* c7 F0 Q: I  f9 e        }
    # \$ J4 l# m  y6 O#ifdef CONFIG_FB_DEFERRED_IO                /* 这里没有定义,不用理会 */
    5 a: j' C7 @  \. n! l2 Q        if (info->fbdefio)& ]& W: N. P6 `4 f
                    fb_deferred_io_open(info, inode, file);& {- M' F) ^) m8 F" z3 R. y
    #endif
    8 O: u* k3 I: L1 ?0 a$ T) Mout:# P, M- T+ c9 A: `, \. n) p
            mutex_unlock(&info->lock);        /* 释放mutex */
    . C# U- N9 \& h0 r# h  U        return res;4 q( e# G+ C3 s- q9 E+ j
    }
    8 D( J5 i* ?* n- H$ J; P8 J5 j发现fb_open函数是围绕fb_info来实现的,而fb_info设置为registered_fb[fbidx]9 y( G1 T! k4 E. {
    问:registered_fb[fbidx]结构体数组是在哪里被设置?. g. ~& S* J2 m$ Y' r
    8 r4 q- f, R* [/ [% p
    答:register_framebuffer函数里设置registered_fb
    ; S4 T4 k9 T5 V; s# D3 z7 y: S
    " y: y) h7 @6 p( g/ p8 N. T& A7 z/ V$ x
    /* register_framebuffer()函数的主要工作是设置fb_info结构体的一些成员 */
    8 `8 B( X) \1 v) Rint2 w0 n1 @& \/ Z3 i
    register_framebuffer(struct fb_info *fb_info)
    " f' _  S- S; ?& F( u# V{( _- t; G' e, _# E* g$ ]2 T
            int i;
    / _3 v# T3 M3 \$ u        struct fb_event event;9 g8 `5 z. w0 F9 H8 `; k: u
            struct fb_videomode mode;
    ; E- q/ j$ R- z, N9 |, a0 X- {! |, \! D
            /* num_registered_fb代表注册帧缓冲设备的个数 */& q) c+ G& v* v+ P4 z
            if (num_registered_fb == FB_MAX). [6 Y* U2 C" @: d
                    return -ENXIO;
    3 H/ X$ x6 J% E0 Z6 N. ^' ?* F9 {( z
    2 @* c, O9 ]5 I2 i        if (fb_check_foreignness(fb_info))" w7 c1 w/ Z* K
                    return -ENOSYS;
    6 P- s/ D2 M# t3 E3 K
    1 D9 s3 J  g1 O! s3 G: ?: ]* `: M. n        num_registered_fb++;
    ; m8 Q7 l# I! z$ M# W+ P: Y. s' C& w# I9 q% r& d+ C
            /* 当registered_fb[]项都为NULL,就会break,找到一个空的次设备号 */
    ! D& L* ?) k" u  D" e" ~        for (i = 0 ; i < FB_MAX; i++)
    " }& K7 ?+ d9 Z7 H* p; }6 g+ J                if (!registered_fb[i])        ; }" W4 X' a1 P8 [7 J
                            break;. }# B5 F& s, K4 P! B! I
            fb_info->node = i;8 d4 d4 Z7 V, ~6 v. ^+ {- g
            mutex_init(&fb_info->lock);                /* 初始化mutex */1 z1 e( i2 C5 Y/ A3 S7 w
    - U1 r, G* a1 h4 S- G' h+ T
            /* 因为在init加载函数里只创建了类,这里在类下面创建设备 */
    , j! h2 V$ ]: B# X- p        fb_info->dev = device_create(fb_class, fb_info->device,/ y0 a# D1 O: d, r0 d3 Q
                                         MKDEV(FB_MAJOR, i), NULL, "fb%d", i);
    , u* f, c" l( F$ `* g        if (IS_ERR(fb_info->dev)) {
    % p- _+ h/ a8 y/ o/ i2 [                /* Not fatal */4 V6 l; |' c) U( m/ P
                    printk(KERN_WARNING "Unable to create device for framebuffer %d; errno = %ld\n", i, PTR_ERR(fb_info->dev));
    9 q& v, L6 c. q; r                fb_info->dev = NULL;$ ~/ L" r$ }4 \' f
            } else& G0 P- W( g$ @6 t7 |
                    fb_init_device(fb_info);        /* 对struct fb_info做一些初始化 */
    8 d- f9 L8 y( l! @. ]
    # r: i, K- d! z; a1 _
      T  ?7 H2 t1 I* }1 ^        /* 初始化fb_info->pixmap结构体成员 */
    * T2 x; P# @. y" f  v5 ?        if (fb_info->pixmap.addr == NULL) {1 f. s3 Z' B+ Q
                    fb_info->pixmap.addr = kmalloc(FBPIXMAPSIZE, GFP_KERNEL); /* 8K大小 */5 W' ]' x' |( C) Y/ j# H
                    if (fb_info->pixmap.addr) {
    - Q8 ?0 \4 m  k. o: l. E- |+ }                        fb_info->pixmap.size = FBPIXMAPSIZE;        /* 8K */* f% r5 @0 F1 w9 W/ p
                            fb_info->pixmap.buf_align = 1;
    ; h+ X. H" t* x5 T+ y                        fb_info->pixmap.scan_align = 1;( B* r+ V0 r1 z+ j. C
                            fb_info->pixmap.access_align = 32;
    7 a4 |8 X9 o. ^$ m- C                        fb_info->pixmap.flags = FB_PIXMAP_DEFAULT;) O% M, }: D2 P" O
                    }& b: v, y/ b: u; N# `$ C
            }        / k6 |7 E3 q; Y" ?% M' U
            fb_info->pixmap.offset = 0;0 @$ v! P! ]; O
    & I4 v0 m: }' D
            if (!fb_info->pixmap.blit_x)
    4 e9 h! v  V2 m+ i) w. G: D                fb_info->pixmap.blit_x = ~(u32)0;
    1 D+ o, C# R) p  @* A3 l! a; J7 p) a6 K$ b; A/ _8 b& P. w6 G
            if (!fb_info->pixmap.blit_y)
    - D$ `5 a! O/ L8 v. g                fb_info->pixmap.blit_y = ~(u32)0;
    6 Q( X5 |: d" F) ]9 x5 r' P' A- `
            if (!fb_info->modelist.prev || !fb_info->modelist.next)( T) b% E! H: B/ a' t- j
                    INIT_LIST_HEAD(&fb_info->modelist);        /* 初始化modelist链表 */. I: U1 R7 C8 r& `0 Y1 r
    ' {7 Z: I. }# A" S* a
            fb_var_to_videomode(&mode, &fb_info->var);( O' {5 K' }' E. ?
            fb_add_videomode(&mode, &fb_info->modelist);: D" }0 B0 j/ N( W4 R

    5 w" A1 `" M. K$ `* F, {        /* registered_fb[]数组项在这里被设置 */
    0 d4 T8 j8 S# w2 x7 |        registered_fb[i] = fb_info;; T0 s- _' T/ @$ _" j

    * c" H9 \' R! x* b9 _# O        event.info = fb_info;
    7 Q( Q( o) I& ]! d' s
    $ }! Z. w$ R+ \4 F. [5 }* |       
    : B8 L$ z2 n, I) g* i        if (!lock_fb_info(fb_info)) /* 上锁 */
    : S3 J2 A6 L9 m) U! v8 K                return -ENODEV;$ O9 d, K5 s. R- R0 }- T3 H
            fb_notifier_call_chain(FB_EVENT_FB_REGISTERED, &event);
    + ]7 X3 Y6 v1 n, j* i. ]4 e" L        unlock_fb_info(fb_info);        /* 解锁 */
    1 F- o$ n9 q/ e: q        return 0;- V# O/ X$ P2 v7 \! I
    }5 Q; `) `9 L( S- D1 I
    fb_read函数源码分析  s, P9 y6 I3 g" m3 Y
    & D% z% `: x! S, i7 B; V2 m
    static ssize_t- A% n2 J! D7 ]
    fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos). m4 m. R  y# O+ o7 P8 n$ |" R
    {
    8 o) ~% Y5 j, O- I" f! T        unsigned long p = *ppos;4 l4 J' C- d$ U8 L6 m9 \2 @
            /* 通过file结构体的成员得到inode节点 */
    5 O# W" M4 P+ g/ ^8 j        struct inode *inode = file->f_path.dentry->d_inode;% g9 z7 B, G' }, L2 u% e# E9 E: y

      h4 \3 z9 I+ R/ d3 \6 v1 l/ p        /* 获取次设备号 */
    " s3 I5 A. {# e* v% p        int fbidx = iminor(inode);
    7 @! U) |! @) e8 [4 ?, [& o        /* 以次设备号为下标找到一项fb_info结构体 */$ {# Z9 w8 H: S7 c# p* B
            struct fb_info *info = registered_fb[fbidx];
    ) J5 W: Z+ K% ]; G# N- s       
    0 L* t. _! I. m1 N0 Q2 L) p7 D% P        u32 *buffer, *dst;
    ) r9 J7 A3 t/ Q; G4 h; n0 }        u32 __iomem *src;& u! h9 @# ?/ u" x1 B4 o' g6 H; U
            int c, i, cnt = 0, err = 0;( v7 ^* a! N, z0 I# E- `
            unsigned long total_size;/ q8 I$ O! K8 S9 q
    , R6 c3 b. A- n# }3 ^
            if (!info || ! info->screen_base) /* screen_base是虚拟(显存)基地址 */
    ) l4 H" T( N( `                return -ENODEV;" L% U+ Y5 g! E+ l, L
    4 Q! e9 q% ]' k4 l
            if (info->state != FBINFO_STATE_RUNNING)0 r- c0 I1 x. C9 u1 e
                    return -EPERM;                /* 禁止操作 */, }$ B- o( }9 }! m  Q, R* _8 ]4 y
    / [+ k3 n8 ]5 x, z. X. G& n4 _
            /* 如果registered_fb[]项里面提供了fb_read()函数,就调用下面的函数 */
    2 ?1 f1 F2 `  ]( ^2 J. j8 b7 `        if (info->fbops->fb_read)
    ) s3 H% \" ?2 {3 g( x% E                return info->fbops->fb_read(info, buf, count, ppos);: ^* n6 a/ I5 B  M: `8 Z* J0 @: g

    6 j$ H  X& E5 G5 \3 b+ M. u( y        /* 没有默认的读函数就从下面的screen_base里读数据 */" X- L) T. L( x- O2 x: u) r) _
            total_size = info->screen_size;  /* x*y*4,x,y分别为屏幕分辨率 */9 K" ?0 b2 ~5 o" x
      S3 N% z+ c: j5 D  [1 G
            if (total_size == 0)
    7 Y0 X4 h- D9 M+ Y# u1 T                total_size = info->fix.smem_len;        /* fb缓冲区的长度 */
    % ^4 }/ |& y) ]" B+ U
    " y8 R7 u( T0 d) s" q+ S+ W        if (p >= total_size)                        /* 调整读的偏移位置 */6 W: b: |8 _8 J( u/ U1 A3 i
                    return 0;8 E- S; ^" a" g: X0 G
    ) h! Y3 q' @4 q& x$ v# @, F
            if (count >= total_size)/ ~# D# |2 X5 b/ g
                    count = total_size;                  /* 一次性最多读多少个字节 */$ f% _+ H. g7 d2 H2 |" t$ P& H. Y
    ) u% Y2 q8 J  n7 p, [* h
            if (count + p > total_size)
    / K- f; q+ W6 Y                count = total_size - p;                /* 调整读的位置及能读多少字节 */( F& A8 Z5 I) g& S
    + Q7 s% I+ \8 ~2 @
            /* 分配内存,最大分配4K的大小 */- A9 K. I& B0 N1 v% y6 @
            buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count,
    1 g! \" o. `# ?+ m/ b                         GFP_KERNEL);               
    8 s/ [0 u+ V0 r  {" @! X        if (!buffer)
    , v! n6 c7 w# N' x                return -ENOMEM;( p" H" }- K6 U' t

    9 }' j" M6 v9 \1 `& I        src = (u32 __iomem *) (info->screen_base + p);  /* 源虚拟基地址 */
    ( n& r, ^9 b. I" @
    8 m5 W4 ]: V- _% c3 [7 U$ e7 K- y        /* 如果registered_fb[]项里面提供了fb_sync()函数,就调用下面的函数 */1 I; Q% k. U5 r6 h7 k# @! L! U
            if (info->fbops->fb_sync)               
    ; n, Y6 x4 l4 w8 N  k3 z7 v- S9 u                info->fbops->fb_sync(info);
    : w% E: X* |# t+ e$ M2 p2 ~4 ?5 F6 v: n7 j5 }' e3 A, Q7 w! q
            while (count) {
    % _" c! k' y9 i8 R$ O+ H                /* 读多少计数变量,单位为byte */0 ^. Z, U. r* K2 L7 @/ n. Y& M; ~
                    c  = (count > PAGE_SIZE) ? PAGE_SIZE : count;
    8 L: e% P& L! Y! L                4 K2 ]" f3 R$ u" q0 K. ?" R. n
                    /* buffer是指向刚分配内存的首地址的指针 */9 p6 b, U9 O& X1 R) E
                    dst = buffer;        /* dst指针指向buffer */
    : [8 w# M: a/ R$ g4 g1 p' V
    6 i: ^9 p5 U1 a8 T7 O                /* 先除以4,因为每次读4个字节 */
    1 J; }+ n2 x3 `4 ~+ h) z                for (i = c >> 2; i--; )                        ; x5 D3 G9 J; b9 b( E, ]2 T
                            *dst++ = fb_readl(src++);        /* 拷贝源虚拟机基地址的数据到目标地址 */$ O5 l+ _! j5 y

    ! d) S0 Z; M+ _! ~                /* 判断是否以字节为单位来读取 */
    4 v# G9 i6 A% Q6 N6 @2 }/ H! ?) X. y                if (c & 3) {                                        * P% y+ T" a; l: K! ?8 a0 x3 R
                            u8 *dst8 = (u8 *) dst;5 |; G' a) L  D2 a
                            u8 __iomem *src8 = (u8 __iomem *) src;3 k3 y3 C; J+ N
    8 r  w2 x; Q. `6 T, O
                            for (i = c & 3; i--;)
    - @* H; a9 r0 D4 o4 Q                                *dst8++ = fb_readb(src8++);/* 拷贝源虚拟机基地址的数据到目标地址 */
    7 i$ {) ]1 j$ \4 q4 j8 x5 _: q7 W+ `$ i' ?1 ]+ y$ h$ ]/ _: i, L8 ?  F
                            src = (u32 __iomem *) src8;2 F; i' k9 t/ z1 p
                    }9 ~- ~8 V5 o% x

    $ j% z  p9 T7 `, ^' G5 K( N                /* 从内核刚申请内存的地址buffer拷贝c长度的数据到用户空间的buf里去 */
    / n) T: y& @; n7 ?                if (copy_to_user(buf, buffer, c)) {, I% l' `1 T2 D- u8 K8 V
                            err = -EFAULT;        /* 成功拷贝,则err返回值为0 */$ |/ w9 J( L2 x/ W1 R2 T6 y
                            break;
    # u0 [4 c7 j1 m2 n/ X7 p7 m                }) a- q6 z/ t; D# f+ r! `9 V
                    *ppos += c;                /* 调整偏移位置 */% B0 j% F/ k' p/ O( I/ ^
                    buf += c;                /* 调整用户的buf */9 J% H, Z6 ~$ j) D! L1 f
                    cnt += c;
    9 s1 w4 i* n5 I9 Y4 H# G8 d                /* count变量减去已经读取的c数量,用于判断while(count)是否为真*/- ~3 T9 e: C% t1 l8 l; \
                    count -= c;               
    ' F; F7 k$ e: D! L6 x# l2 y        }
    ) |7 N- E$ @7 Y* v; J. Y+ Z" @6 ^9 [; n
            kfree(buffer);                /* 释放内存 */
      X- H" z7 K" `5 C: }6 I% p6 d. Z- L- f& |$ o8 W8 C
            return (err) ? err : cnt;        /* err = 0时,返回被拷贝成功的数量cnt */
    - F8 i1 P% l) w}
    5 b3 n- R1 |) |" k: o! |  `5 H7 O4 bfb_write函数源码分析* `( ]% R  Z1 l4 B  u
    ! U* u7 S, p4 I# a) X: L
    static ssize_t
    2 \0 \( R1 a: T' Nfb_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)# u# ?7 D6 T7 o! ^
    {* N3 Z# X5 c" z% ?
            unsigned long p = *ppos;
    ) x& v* w" v" f! K9 c        struct inode *inode = file->f_path.dentry->d_inode;7 U( Q% F9 D$ \4 o$ ?6 j3 K
            int fbidx = iminor(inode);
    * @" A( K, g% `# k' R        struct fb_info *info = registered_fb[fbidx];( M9 y# o# R8 R% C
            u32 *buffer, *src;
    - E: \) n) V9 O        u32 __iomem *dst;
    ) v- |1 e* Q5 N3 q* ?        int c, i, cnt = 0, err = 0;
    - e# u& n! V+ m0 M! J% Z        unsigned long total_size;
    7 \. ~# B( g9 }8 p
    ! V/ D6 s  Q4 _+ c( Q" c3 f        if (!info || !info->screen_base)/ e1 P9 p0 y9 @% z/ K5 [" _+ Y7 U: L
                    return -ENODEV;; _7 v# \4 `6 V9 N
      A* d; C+ a0 U. i
            if (info->state != FBINFO_STATE_RUNNING)
    5 \5 N" S+ j% t7 D6 _                return -EPERM;4 _- t7 {! M8 d2 `. o

    / _* ?* V( |0 X. v0 N0 _7 u- m" _, f        if (info->fbops->fb_write): j* F; G. V" u- L! X7 ]% }
                    return info->fbops->fb_write(info, buf, count, ppos);
    3 B- K$ d9 W) L! @% V! c       
    9 p% a0 E0 g! r$ P8 q9 f        total_size = info->screen_size;( U0 @4 h1 r" d6 Y/ p* k

    0 ^! w- }4 P# @$ ~2 I  X        if (total_size == 0)# e) b& O3 N% T- K
                    total_size = info->fix.smem_len;* a9 S2 Y/ _6 R! Z

    8 y- L5 y" E! ^6 c: n        if (p > total_size); T4 v9 F- g5 O8 m6 f
                    return -EFBIG;& g# [" x1 D  g

    ' K9 U5 ?- n9 ?( m/ w        if (count > total_size) {
    # v8 l! m* Y) S# G; v                err = -EFBIG;/ W3 ?% V: S- P- s, R. X0 v
                    count = total_size;
    5 b' L  b# `. r        }
    5 n' H" t  U8 e* b9 y
    1 j( O: P7 i( Q" E        if (count + p > total_size) {
    ' l$ T# [5 r5 L$ x7 G                if (!err)% {6 f8 ]* S: r( ~( v6 m" ~
                            err = -ENOSPC;
    , `; h  [' a$ w0 Y; {0 j9 d1 t9 R: |7 V5 p+ S$ C. m
                    count = total_size - p;# u3 `% b9 u+ R7 N5 c. G  I
            }) l% b( s. p" O

    3 ^9 y* Q9 n( X* K; V' x        buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count,0 M# d9 E. ~. J/ Y
                             GFP_KERNEL);
    : Y, l& q5 o7 Z: Z4 W        if (!buffer)4 Z* B7 _0 l1 b# |
                    return -ENOMEM;/ q) q* q" a; z. U9 O
    & r3 S7 b" W& J
            dst = (u32 __iomem *) (info->screen_base + p);        /* 源虚拟基地址 */
    5 M3 p1 K- a# A6 R- R/ R& Q1 `
    7 S- {  U, D1 n+ S3 \7 _# k        if (info->fbops->fb_sync)
    ! x4 I, V" O! [& i& u  H                info->fbops->fb_sync(info);
    * A# M3 z: ]! c( U, K7 o* A
    $ k, n$ ~) g. v, B        while (count) {
    % C) L1 h3 G( @' w* B/ ?                c = (count > PAGE_SIZE) ? PAGE_SIZE : count;
    7 F0 A, l: n6 t7 ^                src = buffer;        /* buffer为指向刚申请的内存的指针 */
    ; o& O9 |* |6 g2 D$ \' [( ]) _9 R# k0 Z0 N2 }/ F" S- @( D

    8 M! j+ f# ~) X1 E                /* 从用户空间的buf地址里拷贝c长度的数据到src内存里,成功时返回0 */1 W3 Y+ I! C3 w  _% z
                    if (copy_from_user(src, buf, c)) {+ Z! E% U; c# R- ?
                            err = -EFAULT;
    ( G# u4 `1 H6 B' C  Q( a8 }( e5 @8 l                        break;/ C3 x" R# }$ b9 {' E
                    }
    * ~1 p( w; T9 f# `$ C- f3 H1 B, z0 z! @  g, F$ A( z3 v6 v
                    for (i = c >> 2; i--; )                        /* 以4字节为单位拷贝数据 */$ V$ A1 R) H* p4 O
                            fb_writel(*src++, dst++);        /*     *dst++ = *src++     */3 K  J3 ^6 W2 t/ ]- o" s) E0 V' N% t

    / n# K5 s) \2 a# M6 E/ l' r3 o                if (c & 3) {                                        /* 以字节为单位拷贝数据 */+ q$ C# v( ?, x8 d. d7 _' F
                            u8 *src8 = (u8 *) src;3 P) u6 l3 b$ v& Y3 y
                            u8 __iomem *dst8 = (u8 __iomem *) dst;
    ) W7 F6 g) w9 {4 U: r9 {% K8 |: m/ X3 f- w% Y3 ?* r
                            for (i = c & 3; i--; )
    ! Y/ @1 K' \: G7 w$ W                                fb_writeb(*src8++, dst8++);
    0 C7 ?, r& c# S6 r$ c4 D4 H
    3 e: U2 J  I$ G                        dst = (u32 __iomem *) dst8;
    5 }8 E4 b% W1 c# @7 A8 g9 J2 G  a+ O                }6 }/ B$ Z$ b; s. v/ e
    6 j0 r( |) ]) K, S
                    *ppos += c;# m7 H: C; }( G  I+ T, P/ p
                    buf += c;
    , H. L% T) ^1 r                cnt += c;/ ?* a& h4 S$ M* e2 f& h& j6 F
                    count -= c;+ E  H( l. F: A) c: f
            }8 ^" e! b. w: T# C& G5 c, w( ?

    6 d0 _3 X3 l( j  G        kfree(buffer);
    / b+ O8 F& J" m! `1 n5 D8 Q3 F2 H7 O; ^+ g2 p9 `: \
            return (cnt) ? cnt : err;
    1 k; f2 O/ S. H- w: z6 M7 U}
    7 {2 ^. _5 t8 O$ {7 Sfb_ioctl函数源码分析  N+ R/ J& I8 \

    , g8 Y1 p& ]. \: W0 O% T7 tstatic long fb_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
    $ `$ J* f  y, ]  ~{
    ! B. c; ?$ `% {        struct inode *inode = file->f_path.dentry->d_inode;
    ) l; P5 ~# |# E. h4 K* C        int fbidx = iminor(inode);
    + b" W8 Q% i1 E7 _- Y/ M        struct fb_info *info = registered_fb[fbidx];/ h! ?+ ~: s% g  {

    9 t( \% l3 L" k        /* 这个才是真正的fb_ioctl驱动函数 */3 E5 g5 w) n0 Z9 `7 p
            return do_fb_ioctl(info, cmd, arg);        : p) X) b9 }4 B' ^4 |$ B
    }
    & ~& f! t5 K9 Q+ C& ]% ado_fb_ioctl函数根据cmd来设置各种命令,这里仅举例说明:
    + ~" S/ S8 q3 m& u: j2 Q. T" H; M
    ' M  a7 j( {' \- iswitch (cmd) {3 i- H. s% @1 p* x' Q
            case FBIOGET_VSCREENINFO:                /* 获得可变的屏幕参数 */& L) v. G% Q$ c5 m7 W& ^" {' e
                    if (!lock_fb_info(info))        /* 如果info->fbops不为空,则上锁,成功返回1 */
    / v" `9 Z- ~; ~9 f                        return -ENODEV;
    & q0 x$ h$ H- a9 @* |4 f* t                var = info->var;                        /* 可变参数变量的设置 */4 U6 ~6 {, ]( |( J4 C1 f
                    unlock_fb_info(info);                /* 解锁 */
    8 Y1 c8 }+ ^* U5 y& C- y4 q& C7 r# I1 `" g- k
                    /* 从内核空间的var地址拷贝var大小的数据到用户空间的argp地址里去 */
    9 p* B5 z1 V4 x! F                ret = copy_to_user(argp, &var, sizeof(var)) ? -EFAULT : 0; /* 成功返回0 */
    3 L& I0 G5 E0 L                break;
    / j: N. ?# |/ k' f1 n- g6 Sfb_mmap源码分析:
    * r' P2 ^8 L# ]' t2 U' D- o
    ( `* G# D/ N4 ]/ z" u/* 这里分配的显存是在内核空间分配的,用户空间并不能直接访问,
    + M2 P4 G2 S9 U2 l/ D% a; { * 所以需要用到这里的mmap函数,直接将这段内存空间映射到
    ' s, a6 Q) y7 M5 c4 T * 用户空间去,用户空间就能访问这段内存空间了。
    9 M: C! Z. f! ]4 P8 \ */
    ( C# R3 @( S! g6 Cstatic int; t+ R& \+ I( r/ O- E$ j/ Y' f# i7 Y
    fb_mmap(struct file *file, struct vm_area_struct * vma)$ W2 Z* S) x  v$ z- L8 e
    __acquires(&info->lock)
    ' H) v, L- u( h  r( _+ L4 C__releases(&info->lock)7 o2 W6 w( x! t7 G: z* F- I
    {
    & r! d* o4 S3 ~        int fbidx = iminor(file->f_path.dentry->d_inode);
    3 d1 z( M$ G& o4 c        struct fb_info *info = registered_fb[fbidx]; /* 通过次设备号找到fb_info结构体 */
    9 U4 J8 N! N4 ~' u2 q        struct fb_ops *fb = info->fbops;- [: |! m1 Y- o
            unsigned long off;
    $ W3 y! P/ P$ i8 Y8 C8 T        unsigned long start;
    , c& s/ s9 x0 b! m. A        u32 len;
    $ R8 ?& U) t# D. n
    6 l! }: @7 i3 \        if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT))
    # v" w8 G6 p4 t( B; a% O                return -EINVAL;% P/ E$ _! W6 v. N+ |1 v7 D# Z
            off = vma->vm_pgoff << PAGE_SHIFT;
    4 \7 l! Y$ {( _% x+ D, Q9 {        if (!fb)" H; J: Z! A3 f& f4 V, `6 I
                    return -ENODEV;
    3 N3 {+ F; h6 D; ]$ i$ K' g
    : F- l5 y/ X7 g3 N' P2 l, R  N* g        /* 如果registered_fb[]里有默认的fb_mmap就使用它 */# [( ?$ S5 g3 I! Z: f
            if (fb->fb_mmap) {
    , K" N( T2 q  x2 t9 J6 r7 Z! O                int res;1 T9 ~5 @8 S5 t3 T$ U, o
                    mutex_lock(&info->lock);
    3 x  E4 ~! K! b) j; S8 n: U7 M0 P                res = fb->fb_mmap(info, vma);
    . ^. L% r3 g: O$ D, r                mutex_unlock(&info->lock);
    # f; D8 [5 g0 J* Z# J- \/ h                return res;$ w5 H# {  O4 o! z+ l! ]
            }/ A+ N! F4 c% m

    % l8 r8 a- l; |4 d; D$ @        mutex_lock(&info->lock);
    % [9 a. ]" }$ \" e8 r6 v
    0 P# P9 ]3 R  h4 ]5 F2 h        /* frame buffer memory */
    & F, s8 F0 c0 o        start = info->fix.smem_start;        /* fb缓冲内存的开始位置(物理地址) */7 X0 G" y5 Y8 x4 Z, S9 Y
            len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.smem_len);
    7 ^+ {% o) m5 Y+ a4 X        if (off >= len) {                                /* 偏移值大于len长度 */4 L' e% ]1 ?& b% s8 i% s
                    /* memory mapped io */                /* 内存映射的IO */
    . q% g( O8 ]5 E+ q; [5 t                off -= len;
    6 Z" ~- }7 B7 h& E3 x: d5 h( H                if (info->var.accel_flags) {
      b4 p& m5 {+ b! k0 D4 E                        mutex_unlock(&info->lock);
    8 a6 v5 G0 g0 c8 h5 Z                        return -EINVAL;
    ) ?  n6 ^! D: L9 l5 Y5 s: F                }
    ( B9 C9 E( n1 k8 y1 e                start = info->fix.mmio_start;
    ! t" u" p+ \0 k                len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.mmio_len);
    8 H3 B4 w" \0 d. j) j. I% d        }
    ' F( p  a  o1 V& C5 B6 z        mutex_unlock(&info->lock);+ I( ^& B9 y2 J0 {; y: F6 ^) ^
            start &= PAGE_MASK;
    ! K1 X! b/ q, I% L  ^' y. f' O  x; t        if ((vma->vm_end - vma->vm_start + off) > len), P3 U5 W' ]" X* f& A/ D/ [
                    return -EINVAL;
    $ P- w$ ^7 D: p1 U4 o/ Y( ]1 ]        off += start;+ [, I4 b( Y. z/ I1 h
            vma->vm_pgoff = off >> PAGE_SHIFT;2 Q4 F! S" T! x  n  w7 y
            /* This is an IO map - tell maydump to skip this VMA */" }8 P0 v/ C# L: E) E
            vma->vm_flags |= VM_IO | VM_RESERVED;  d- n/ w# n: E: ]
            fb_pgprotect(file, vma, off);
    ! ^% W4 F# d0 a/ ~) l" y. l" U% _
    & J; [' M0 O* v! h1 k$ @8 m        /* io_remap_pfn_range正式映射物理内存到用户空间虚拟地址 */
    8 V5 ]5 j1 M: O# r7 s7 T8 s4 {4 }        if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT,7 W: p8 y' {: V% J6 q
                                 vma->vm_end - vma->vm_start, vma->vm_page_prot))9 O8 W+ L  x7 N8 w6 z( J& j
                    return -EAGAIN;
    / Q1 r4 O# U' \( L  L+ P        return 0;
    , A9 P( N. S2 d6 b}
    / ~: ?3 n; T' s3 Q, h' h4 ?' c问:怎么写LCD驱动程序?: y- J* n# A3 R" {. p6 G: s# E5 I% m1 U
    1. 分配一个fb_info结构体: framebuffer_alloc
    % L4 c# M; c8 ~; b/ W2. 设置
    . K* n1 O/ l4 i- m) a3. 注册: register_framebuffer
    3 M7 x" f; j) N! ]4 P4. 硬件相关的操作
    7 f7 k4 V* I8 V, f; G
    ! Z8 I8 J# S9 T! h. |! w
    ! z7 n4 v  h& p7 M8 f& U8 e8 T8 A2 r: Z3 N0 ]3 ^) F$ 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-11-26 01:59 , Processed in 0.156250 second(s), 23 queries , Gzip On.

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

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

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