TA的每日心情 | 怒 2019-11-20 15:22 |
|---|
签到天数: 2 天 [LV.1]初来乍到
|
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
|
|