TA的每日心情 | 怒 2019-11-20 15:22 |
|---|
签到天数: 2 天 [LV.1]初来乍到
|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
上一节文章中详细地剖析了probe函数,但是从始至终都没有看到打开读写文件接口的操作函数,只看到了下面这个操作结构体
" O- V: n2 P+ V1 z. L
: U* d+ y4 p2 r& Q9 D& K+ C7 Q) j; J' O
; ^7 p+ f; z# R. U% t/ Istatic struct fb_ops s3c2410fb_ops = {
% ~7 U+ y7 X6 e .owner = THIS_MODULE,
$ t' x9 x) P L0 } .fb_check_var = s3c2410fb_check_var,
7 T3 r# z1 W$ S: G9 l1 O+ x .fb_set_par = s3c2410fb_set_par,
- h s& F$ ^3 p+ T6 D7 I .fb_blank = s3c2410fb_blank,, t1 r- u/ v. s, z8 q% V
.fb_setcolreg = s3c2410fb_setcolreg,
6 \9 V5 K. i M9 |5 Q% Y$ l .fb_fillrect = cfb_fillrect,
9 b5 }, X) B9 z/ `- E* G .fb_copyarea = cfb_copyarea,0 N D, Z% v- V* x. v1 Z
.fb_imageblit = cfb_imageblit,$ [+ C- [; y& {8 p
};/ Y" d1 U0 \# c6 f, S
这并不是我们想要的打开读写操作函数。上一节文章链接:http://blog.csdn.net/lwj103862095/article/details/18189765
; z4 J3 U) Z9 i, g, z问:那到底帧缓冲设备的文件操作结构体在哪里呢?
" k; j* q9 Z5 k- E答:在drivers/vedio/fbmem.c文件里。7 J5 r3 B. D1 v: V& ?
- K- ]- W! n9 Z7 O0 }从入口函数开始看:' Z+ |$ W& \& F: E0 w- G. D5 }
& _. z4 ?. g, O, J, v( e3 g6 m* T0 M
@# s2 X, X, r M; V+ Y" _static int __init
' e- P) ]) q# ~* J# Tfbmem_init(void)9 V2 Z8 T" b% y! e% \+ m
{9 b3 M# E6 q& ]+ ^
proc_create("fb", 0, NULL, &fb_proc_fops);
6 }8 y r" L( f u( y+ R$ S+ K* ]- v+ _& W/ A; f
if (register_chrdev(FB_MAJOR,"fb",&fb_fops))
* r |# N7 I( w# }+ K printk("unable to get major %d for fb devs\n", FB_MAJOR);3 I' ?1 h6 } f4 O' z* w
- q$ g3 o6 }" f
fb_class = class_create(THIS_MODULE, "graphics");
/ A5 A4 H) K7 [& e% q! m if (IS_ERR(fb_class)) {) z* d$ N. y1 u& Q6 h
printk(KERN_WARNING "Unable to create fb class; errno = %ld\n", PTR_ERR(fb_class));
7 k& I# @4 M3 p' ?+ j7 ]# a. l fb_class = NULL;+ [/ k5 y1 T1 u- i N- a. V$ s
}
8 @1 M8 I, }6 h+ _ return 0;
- [$ }7 _8 a; K}
9 M5 P2 l, ?% Q: T! F2 vfbmem_init注册了一个主设备号为29的字符设备,并创了graphics类(图形类)。7 N. w: j' y) Z- i- W
字符设备有一个关键的成员是文件操作结构体
9 P5 G7 [8 |% P3 F) `6 t* p6 l5 M# _- s; w4 P, h
* d% L% r. X& K( D7 W
static const struct file_operations fb_fops = {4 z$ E4 D8 n' H$ m( P3 q/ j
.owner = THIS_MODULE,
2 A* K+ [: V( B0 S' C8 U .read = fb_read,* \; Q9 v1 G1 X2 b& v: z' p
.write = fb_write,
% W9 f" ]7 z6 z% } .unlocked_ioctl = fb_ioctl,% l, t% [ i! B0 K. f0 t
.mmap = fb_mmap,( }9 o3 Q2 R! w9 K
.open = fb_open,
1 I: a( c7 a6 B& q/ x( y .release = fb_release,
% z0 Y8 Z }" ?' {! x#ifdef HAVE_ARCH_FB_UNMAPPED_AREA _& \2 ^* Y- z# [
.get_unmapped_area = get_fb_unmapped_area,) a# P" N! r/ ]* n) t' J
#endif4 o! M$ {; I( z0 }3 a
};/ O& |4 P) L8 D0 z7 \8 y
理所当然的,我们应当首先看的函数是open函数
" c) I" Z: d8 f! }* R
5 q" T" x H& w; `& ^6 h& r2 mstatic int' q% i. [0 C" g# d0 N
fb_open(struct inode *inode, struct file *file)! o& b; K! Z# _; ]4 n( V* j8 N
__acquires(&info->lock)
* @8 G" T! a7 m. N2 T__releases(&info->lock)
6 \. x+ A G$ L+ |{
* L5 G1 I5 l6 O7 q% o int fbidx = iminor(inode); /* 得到次设备号 */4 u+ ?& G C5 Z9 k7 C- j- {$ E
struct fb_info *info;5 G h$ K' w% b* x" U# H
int res = 0;
4 u$ J ?. y6 c6 _8 y: ?+ W" q" J* W1 B* }( a% l d1 a
if (fbidx >= FB_MAX) /* 次设备号有没有大于规定的最大值32 */
# p- Y2 q8 B$ f9 d: u) U% h; J$ M) s8 B return -ENODEV; /* 没有这样的设备 */
5 h( G2 M; n5 b. v info = registered_fb[fbidx]; /* 使用次设备号得到fb_info结构体 */ ?0 T: [; C7 v+ S- k4 ^& U- \2 N
if (!info)5 @9 O2 ^% R: ~/ s5 g# f" M" d
request_module("fb%d", fbidx);
) h2 }$ H9 T& n- F1 _6 a
% ]# @3 ^- q$ u4 ^ /* 再次使用次设备号得到fb_info结构体 */ I- Q% A; z, e6 i/ j; t& t
info = registered_fb[fbidx];
! l" k& G7 z/ V3 F/ A) A if (!info)
; V! f, }1 N6 u, f8 } T I3 w return -ENODEV;, s) j# j# T! y: Q) q' n# ^) }
8 h% ?6 c$ _# Y0 I0 n' e3 r
mutex_lock(&info->lock); /* 获取mutex */5 b6 I/ A4 b7 w6 @, n2 L- H# c
1 Y7 g" D9 n+ v6 L7 X /* 获取模块使用计数module,成功返回非NULL */
2 m3 m" Z1 Z8 ~% ? if (!try_module_get(info->fbops->owner)) {' @0 g, K0 C3 C& {- n
res = -ENODEV;! K1 |3 ?) L! E9 F" T# H; n
goto out;! U, L5 S% u7 g* `5 v" u7 y
}/ M _! j. N7 }% O5 k
( |& U" n: S: C. ~) Q1 x8 g" O
/* 从registered_fb[]数组项里找到一个fb_info结构体保存到$ T0 D$ N- d v' Y
* struct file结构中的私有信息指针赋值给它呢是为了以后调用) r& t& f0 W7 H4 O/ @
* read、write、ioctl等系统调用时找到这个struct fb_info结构( K! c$ ~ i9 p7 E9 K
*/
, Q$ ]6 v" p D2 m" w. N file->private_data = info; * H2 E4 d" M4 h& a! q& o
0 q5 R% l9 w# T6 l: J
/* registered_fb[]数组项里有没有默认的fb_open函数,如果有就使用它 */" N6 @# C5 K& Q, Z6 T. H
if (info->fbops->fb_open) {
$ I# T" a' o/ X0 Z' L0 [1 S res = info->fbops->fb_open(info,1); 6 a1 ]8 H( G$ b2 U
' P. t' J( J0 K+ ~3 s5 E /* 有默认的fb_open并成功打开就删除模块计数 */
+ J' A3 \# h4 ?$ z0 t* _" J if (res); {, ^* z3 }3 |# ~
module_put(info->fbops->owner);- m) G7 Y, z! r; @+ s) ?( r, p5 ^6 f
}6 k, i6 n( ]9 X4 F" Z
#ifdef CONFIG_FB_DEFERRED_IO /* 这里没有定义,不用理会 */
! c- H9 i5 ~* d! H! C if (info->fbdefio)$ \4 b0 F! f& V+ W( G: R. w
fb_deferred_io_open(info, inode, file);1 p# \& Q0 _; y! `( K+ \0 A
#endif7 U# N3 U0 n' x7 I4 H$ |5 y
out:
# h, B" a% _7 c# D% J9 ^2 i mutex_unlock(&info->lock); /* 释放mutex */: e* C, ?5 y! R5 }0 M
return res;
4 q' G! q9 B6 F }2 s( {}# m' B9 ?$ I3 ]4 x2 S
发现fb_open函数是围绕fb_info来实现的,而fb_info设置为registered_fb[fbidx]
3 h4 n% i Y% b& s" U9 Q7 e" G6 c问:registered_fb[fbidx]结构体数组是在哪里被设置?
2 m o$ O* ]% w% R! v9 u4 F! m4 X) ~
5 g+ b; c( Q! m1 x+ H答:register_framebuffer函数里设置registered_fb4 ^) q0 m4 d0 g7 d8 V! S
/ {3 U4 L" h" H" g. m
3 z) x7 z+ V. g+ h/* register_framebuffer()函数的主要工作是设置fb_info结构体的一些成员 */
% k* [( {5 M+ ~7 Q2 a( [, |int: `+ Q/ m" Z s, C
register_framebuffer(struct fb_info *fb_info)8 C! s! s. a0 [+ x
{
2 P4 E9 e Y! d4 C" k5 | int i;
0 f5 x/ o* W$ m0 S' X struct fb_event event;
+ V7 i. h8 G6 D% q: c/ o) B struct fb_videomode mode;
! P0 V x: R5 ?' L! @$ K9 u; z( s Q( n6 v+ U: W3 b
/* num_registered_fb代表注册帧缓冲设备的个数 */! H3 x9 g8 ]+ L6 m$ n. `
if (num_registered_fb == FB_MAX)8 w! {. }5 w! e# C# h% H
return -ENXIO;* c4 T7 K' }9 s3 P* v8 [
0 \7 J i- N/ R, y+ ^ if (fb_check_foreignness(fb_info)). }' d& Q$ q$ a0 S) _+ r
return -ENOSYS;
2 S7 E0 t3 O& j8 C: X Y! J# H# b8 f/ A! A( a) v
num_registered_fb++;+ y% h, a; s) G# B) }
& v/ t6 G% O0 f
/* 当registered_fb[]项都为NULL,就会break,找到一个空的次设备号 */
1 T( ~ v* e2 t% Q/ u8 }' m for (i = 0 ; i < FB_MAX; i++) A7 G& ~5 o0 W
if (!registered_fb[i]) " t) E9 }; H ?3 Z8 [/ X% G
break;7 Q0 H, Y7 h4 V+ R
fb_info->node = i; x9 i( n" W' P3 R; m+ b
mutex_init(&fb_info->lock); /* 初始化mutex */
( w! \( `; L( l9 c9 f; R i1 W* Q# \& g; e7 v$ m3 O
/* 因为在init加载函数里只创建了类,这里在类下面创建设备 */
/ |1 o" ~( W1 @- @3 x0 F- g) u fb_info->dev = device_create(fb_class, fb_info->device,, `- [0 X( p! `( w8 N; [5 U
MKDEV(FB_MAJOR, i), NULL, "fb%d", i);
6 u9 n9 v4 D- L) d" S; s if (IS_ERR(fb_info->dev)) {6 p9 n7 y' g. z% K, j
/* Not fatal */+ }: a% N! f# ]
printk(KERN_WARNING "Unable to create device for framebuffer %d; errno = %ld\n", i, PTR_ERR(fb_info->dev));7 P2 g" B. ]5 o$ _7 ^8 g+ Q1 Q+ k" d
fb_info->dev = NULL;7 W8 p# ]" c R# y4 g/ I; o3 M( F
} else
) u+ N n+ P8 E$ { fb_init_device(fb_info); /* 对struct fb_info做一些初始化 */
2 j2 t8 I! t$ h. q. t* D( T! h3 ~; S9 N4 z. w# f
$ N4 ?: U+ E; z4 H9 K+ h /* 初始化fb_info->pixmap结构体成员 */
0 v. P3 h+ r9 r7 `5 }- W4 ` if (fb_info->pixmap.addr == NULL) {- E0 p/ M, N8 K; s- _6 L
fb_info->pixmap.addr = kmalloc(FBPIXMAPSIZE, GFP_KERNEL); /* 8K大小 */ n" d% { P5 }" p
if (fb_info->pixmap.addr) {8 Q8 U3 G( j( ^; q- y
fb_info->pixmap.size = FBPIXMAPSIZE; /* 8K */
/ { l. e$ r1 t7 L fb_info->pixmap.buf_align = 1;
4 Y6 q2 z. Q2 Z4 l% D, e fb_info->pixmap.scan_align = 1;
8 s6 X. C' _. R( q3 V4 S9 T2 m fb_info->pixmap.access_align = 32;
* S6 |3 T& S, T* H. n fb_info->pixmap.flags = FB_PIXMAP_DEFAULT;
5 g3 A5 I* e8 P: \. x }
. i6 ]9 `4 {* K( k4 v5 u } * l/ V9 b( |# `% d5 D4 O7 X( \" a
fb_info->pixmap.offset = 0;9 l9 B: }, E! [; U
1 E& Q8 ?% e3 s8 f if (!fb_info->pixmap.blit_x), \( l: w+ g# G8 R
fb_info->pixmap.blit_x = ~(u32)0;
! c& |5 R- C' T! c
3 A. y& q% u( L! {8 z1 ^ if (!fb_info->pixmap.blit_y) U$ L% U9 l F4 g( P
fb_info->pixmap.blit_y = ~(u32)0;: B' u4 v1 w7 d" J3 ]
+ Z5 P" F! Z5 x+ z6 X6 q
if (!fb_info->modelist.prev || !fb_info->modelist.next)& y9 t* C) C* D- B( f# k$ N* B
INIT_LIST_HEAD(&fb_info->modelist); /* 初始化modelist链表 */* H" g5 t: }; ~" N% F/ c
! R# q! A1 u- P: ]
fb_var_to_videomode(&mode, &fb_info->var);
; Z$ E5 v# s) N+ G5 O fb_add_videomode(&mode, &fb_info->modelist);/ a( B' a% B1 Z2 }. y* l+ i0 O
: w- L6 Y# S* s1 L$ |
/* registered_fb[]数组项在这里被设置 */
: t+ s+ ^& z2 ?7 ?. ^# k6 A8 ] registered_fb[i] = fb_info;
' W( h& P0 V) h# V8 l1 F
. d9 [3 ]# c7 [: p" ~9 | event.info = fb_info;
3 Y1 W& F6 S( k, @1 a1 d/ w4 e3 e# I9 `& J
# Q& U# X" _* i/ D
if (!lock_fb_info(fb_info)) /* 上锁 */, g0 s9 p8 |) W9 p$ J8 v& d/ v
return -ENODEV;
0 Z: [7 M, X- `4 N2 F fb_notifier_call_chain(FB_EVENT_FB_REGISTERED, &event);+ G0 T" i( Y; e' h [
unlock_fb_info(fb_info); /* 解锁 */
3 u* i- s% b4 P% @ X4 W return 0;/ l+ C9 ^9 C* y8 P9 P* R& {* ^
}5 ?/ J b& A( R( b
fb_read函数源码分析2 W" O5 B* I& { k# S* n
7 V! w6 `7 j3 Q+ A2 c
static ssize_t
B- M- x! ]7 j0 N1 k0 h0 ?fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)' {4 ^( ^/ Z" i" z. m( t
{
! P5 Q7 Z& ^+ u) ]+ K+ j unsigned long p = *ppos;
0 {, l- C! L& F5 C& m /* 通过file结构体的成员得到inode节点 */
# c2 b+ ?0 n0 @5 E struct inode *inode = file->f_path.dentry->d_inode;
7 L; \; Z6 a$ ]+ A0 g1 o9 i$ ~2 E& z/ z/ h+ x. s
/* 获取次设备号 */$ T, i( i, y& u9 o
int fbidx = iminor(inode);: o+ R/ q* u+ j
/* 以次设备号为下标找到一项fb_info结构体 */
. i3 O/ n# \7 e6 s( e struct fb_info *info = registered_fb[fbidx];# X3 O( \7 w! c. r" s1 Q
* g% {$ j- z7 f5 Q: b L u32 *buffer, *dst;+ f- U) L- i. l ^" G u
u32 __iomem *src;
0 D+ Y1 ] U( b- i int c, i, cnt = 0, err = 0;
4 k4 e1 O. I5 U. z$ O5 K! ^ unsigned long total_size;
- R7 O) N; E% p# U5 K+ C+ S. @4 q2 w; X P
if (!info || ! info->screen_base) /* screen_base是虚拟(显存)基地址 */
) y0 G; K) F" g return -ENODEV;
, y+ f$ S7 r+ l2 \3 b. j% u3 P7 |. X" ^+ ?, H9 q
if (info->state != FBINFO_STATE_RUNNING)6 `" B9 R( c; Y* F5 t
return -EPERM; /* 禁止操作 */
! r8 X3 k, `# |$ x6 S# [ v0 S( Y, K: u. @) V: w' l9 h( q- B2 o
/* 如果registered_fb[]项里面提供了fb_read()函数,就调用下面的函数 */0 \9 O+ U" d. y. @- D* U; ?
if (info->fbops->fb_read)
7 I1 k2 c' R& J' Y7 B8 `1 S# I1 \ return info->fbops->fb_read(info, buf, count, ppos);0 u* h% q% L, B
/ u% C) ?) k0 n! s
/* 没有默认的读函数就从下面的screen_base里读数据 */: X' i" s# C( ]5 E$ L" y. r3 w
total_size = info->screen_size; /* x*y*4,x,y分别为屏幕分辨率 */9 M4 B0 n) v" ~( v7 I5 r
( g1 a2 f& N% [( M i
if (total_size == 0)
9 T* L% e+ ~* h0 _ total_size = info->fix.smem_len; /* fb缓冲区的长度 */
) t3 p9 Z" C# V$ s Q8 f: ?1 Z! S. ^
if (p >= total_size) /* 调整读的偏移位置 *// m5 A; m# T' f h6 d" e. ?
return 0;2 I+ f! K- W- g
& w9 |: H5 q% K2 Q1 F: c6 f if (count >= total_size)
6 ^ m) f5 O) s, s; \% K count = total_size; /* 一次性最多读多少个字节 */1 D. j% T5 _5 {% d' Z
' T6 {* S" y9 ?
if (count + p > total_size)5 g' Z6 N1 f, J
count = total_size - p; /* 调整读的位置及能读多少字节 */
' B) s. T% ]5 Z# z
3 ^# Z# B; W; R. K7 Y /* 分配内存,最大分配4K的大小 */9 u. H: w; G* [/ {/ f
buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count,
3 V$ P/ M2 w& o0 e. L$ R5 W GFP_KERNEL);
' x/ F# {2 f# w( [% B/ w" \8 ^ if (!buffer)- T, L2 A: r* v
return -ENOMEM;
* r2 ^+ Y3 j" H* E% e0 `
R+ D- K% E( H; O4 F5 l* J) w src = (u32 __iomem *) (info->screen_base + p); /* 源虚拟基地址 */
# A4 [# W4 N' u/ B; g. E e! U: c$ t# u9 P) a; `/ q
/* 如果registered_fb[]项里面提供了fb_sync()函数,就调用下面的函数 */5 t$ A5 {3 a# E, e% a$ m- h" y6 f5 v
if (info->fbops->fb_sync)
' @5 c! P+ F5 P$ h6 x' Y, p info->fbops->fb_sync(info);
# L9 J j1 \8 h, S: U# ?6 E* a$ m3 A' R! R
while (count) {
! {* H: X! [+ |9 C" s7 l /* 读多少计数变量,单位为byte */
4 B) L: Q. C# y) G, L: h c = (count > PAGE_SIZE) ? PAGE_SIZE : count;
5 y9 X& Q) ^) X% y0 s$ a
& ]: r- h0 [# e4 r7 C /* buffer是指向刚分配内存的首地址的指针 */
" q- O/ D3 s% [) e dst = buffer; /* dst指针指向buffer */
3 F0 D1 G4 B+ [0 c2 S9 k9 ?3 R" y$ b
/* 先除以4,因为每次读4个字节 */; U4 c& ~8 F3 g1 P8 T% @
for (i = c >> 2; i--; ) ( b/ z+ V0 {& H
*dst++ = fb_readl(src++); /* 拷贝源虚拟机基地址的数据到目标地址 */
* i! W& R' w2 `4 J7 p* M! \$ s$ Q* S6 o" Y( J/ l8 Z
/* 判断是否以字节为单位来读取 */
|, h, A/ _+ K1 i! `- c if (c & 3) { 5 a3 K2 m, a4 Y8 @% `
u8 *dst8 = (u8 *) dst;
+ B- M5 U# ^# F9 v7 D: p% S- S4 D u8 __iomem *src8 = (u8 __iomem *) src;5 X4 \ r. z: R( L0 G
) h3 |8 X7 ?1 q; B for (i = c & 3; i--;)
4 M+ G9 A! ^' E" b *dst8++ = fb_readb(src8++);/* 拷贝源虚拟机基地址的数据到目标地址 */
! `: q1 v% `" j0 v n
( x% K" i5 E, @( ^7 r src = (u32 __iomem *) src8;) k( O( `4 N) O L, S, v |
}
( ~7 A# |3 m1 d$ o& d
: B# f) l' o; S, K: `) A+ I* g /* 从内核刚申请内存的地址buffer拷贝c长度的数据到用户空间的buf里去 */
9 Z) E- ?& n: z8 T$ r if (copy_to_user(buf, buffer, c)) {
. J; I) o: g2 y' \5 y err = -EFAULT; /* 成功拷贝,则err返回值为0 */
$ V9 {# m% l+ K3 J break;
* a3 Z6 K! C( j: A }" [+ ~0 p3 e1 W- [) S+ A% z c
*ppos += c; /* 调整偏移位置 */
* k A* D2 `8 L; d" u& Z buf += c; /* 调整用户的buf */4 K; C" m( N3 k" |
cnt += c;/ Z( Q, f8 B n1 S5 f
/* count变量减去已经读取的c数量,用于判断while(count)是否为真*/- F$ g, N0 P. z* f" n
count -= c; - R+ Y+ U% n- z2 ~* h
}( u$ Q4 w0 L5 {0 B9 Z# E N9 K0 U+ d
. H! w: q1 |& a8 q% ~: U
kfree(buffer); /* 释放内存 */
; e) g1 @- Z3 I+ C5 U g
- f. R2 Y0 D& F- Z. ^* e0 u0 V return (err) ? err : cnt; /* err = 0时,返回被拷贝成功的数量cnt *// O! o( K, k: {; _3 g
}
0 [! u2 m6 c2 @$ I6 j* Afb_write函数源码分析1 `* F2 e/ K' A! r1 u' c" r
2 c. A: w) t( \* o" k* h9 @static ssize_t; b9 r4 o* e7 V8 ]
fb_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
: i' m) x( j0 D0 ~& |{
2 s( l5 B- S: ^4 J6 s( q! T unsigned long p = *ppos;
0 L' t& ^* L# Q- r& N struct inode *inode = file->f_path.dentry->d_inode;
% n6 g- i$ X1 z+ g' t1 N: w& h int fbidx = iminor(inode);+ b" _1 W8 E7 Y" @; c0 ~
struct fb_info *info = registered_fb[fbidx];4 Y P: e, \& d+ Q0 c# u' V5 _
u32 *buffer, *src;. h( q& T" N9 h
u32 __iomem *dst; X* A; R$ B. I# P x% R. c* T
int c, i, cnt = 0, err = 0;+ W9 D6 |6 j9 E' B
unsigned long total_size;
; x& `$ v0 C! A& K" n: P; c6 n) u: ?* k" ~
if (!info || !info->screen_base)
- e% z1 ^* U- U# {; p# E return -ENODEV;- |8 W% a5 X* G) V* K+ L) o3 i7 z
' f1 n$ \; d, M( [4 f9 E
if (info->state != FBINFO_STATE_RUNNING)
n+ m3 u0 G. Q8 D' { return -EPERM;
$ x0 Q- `/ Z; \" j @; Y" A' j" i6 o
if (info->fbops->fb_write)
$ B, d, i/ z: L9 w2 O7 [0 ]+ O return info->fbops->fb_write(info, buf, count, ppos);
6 k$ O) R/ N1 c7 W, Z$ X
1 e8 I& [* h% p j. j1 J' o) f total_size = info->screen_size;
0 U. Y7 c, ^( M2 s% P }* f; z5 k* D0 `7 d4 `5 T& W) Z; x
if (total_size == 0)
f! b4 n7 a8 M) ]: E8 n total_size = info->fix.smem_len;
. M) E. v; }* @, b; i1 {. [5 [+ S7 D6 B2 D
if (p > total_size)
2 s! X: \. p0 j; W return -EFBIG;. A y1 d" D6 o! ]
! C9 u5 V1 \- Q+ k( Y- M/ l7 `# |
if (count > total_size) {4 N4 ?! M; g6 [
err = -EFBIG;
# S* V* r8 v& _8 B& n count = total_size;
( Y/ ?# V- c% Z% W2 `+ P0 l }+ N/ E, J7 E& @, X: K* b2 o4 y
! H1 G# V' q( ^% ?& ?- Z% q+ ? y if (count + p > total_size) {$ I$ G) S ], c: ^
if (!err)
& E' {& h; X5 C" ^( z+ T( h err = -ENOSPC;
: X4 a( [3 M( U/ m$ |" R- v: _' }# D U# @
count = total_size - p;* w$ z* }) ?, s; s7 S5 ?9 Y& I* E
}) i; M5 E! Y3 F; Z# v) G
0 c" }( |; I. C: Y
buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count,
" n$ R- G/ c9 M9 E( @# l# q5 e GFP_KERNEL);
& D& z% s6 r& D# M5 c/ J2 ~ if (!buffer)8 z2 u0 P7 m% |
return -ENOMEM;
4 j9 u: K9 c6 u4 v6 w" r' }% P2 u8 t a) f1 g4 L& b0 R7 L- h# }
dst = (u32 __iomem *) (info->screen_base + p); /* 源虚拟基地址 */
8 D$ o0 t. {# F5 }( ^
- v1 o4 P6 Y# I" q" D& ^1 a if (info->fbops->fb_sync)" b( Y* ]2 i/ ~! I" y8 T9 e0 ~
info->fbops->fb_sync(info);
. c$ t) b) y, _7 G7 w4 n0 M+ n$ c. g& y n8 [; I o) t
while (count) { c% E/ P0 T* R$ u3 h+ F6 E
c = (count > PAGE_SIZE) ? PAGE_SIZE : count;4 X. D, Z% a, i) u
src = buffer; /* buffer为指向刚申请的内存的指针 */
1 _7 P8 B, I1 W- |# N( e
/ f( o+ q: w& \* K+ Z% I1 k7 `9 ~& |$ {
/* 从用户空间的buf地址里拷贝c长度的数据到src内存里,成功时返回0 */" F( |& n2 r v' T
if (copy_from_user(src, buf, c)) {
- i" Q/ i9 }+ d3 I8 T; D% K9 V! F$ E err = -EFAULT;1 C" z" C' ~7 W6 r# h
break;6 S/ C$ w) C4 D) _
}% r' @2 f9 `: {, f/ ^/ g3 O
' p' F7 i& X9 K/ p- z. n
for (i = c >> 2; i--; ) /* 以4字节为单位拷贝数据 */
6 [2 t3 i2 R* B+ A5 J, { G fb_writel(*src++, dst++); /* *dst++ = *src++ */" @, `* N# r- ]6 C6 M/ y& Y
0 U1 L) d& N* v
if (c & 3) { /* 以字节为单位拷贝数据 */
4 f" E; [! y6 W5 `% Z0 A n u8 *src8 = (u8 *) src;
( h+ ~5 B6 p. _' { u8 __iomem *dst8 = (u8 __iomem *) dst;6 V1 ]! n+ M; c7 l* r( g
6 }- c' v/ @) A& s- s: q2 [$ s0 f" ` for (i = c & 3; i--; )
q: \) h- h& K$ b9 [ T( ? fb_writeb(*src8++, dst8++);
$ R: a: J/ S# W$ j' D9 ^
. e! m. H1 y) C: Q3 h& f5 Z' g dst = (u32 __iomem *) dst8;( U% v, y" ?" v% V. V
}
6 ~ D$ `5 X7 A7 ?
- r' |) y/ G% J7 N) ?" r1 v( `- W \ *ppos += c;
: U3 U1 m! ?) y% V$ U, T, { buf += c;
. c: W' \& {1 r$ `4 A4 V: f( H7 |5 G0 p cnt += c;. p- E9 t. A3 K
count -= c;
% e% p7 M, Z; Z5 q }
6 d ~3 ~5 S& r) Q$ C& S: v0 G, ?- y" Y) m) d2 Z3 g, |
kfree(buffer);5 ]% w' `/ x- l( G
+ Z s+ r( B8 u- d- g5 n( A! E return (cnt) ? cnt : err;
/ J8 Q3 d" u5 r! O' D2 b$ B4 I}& n: L) z J! E; g3 e. h& P% D
fb_ioctl函数源码分析. ~5 |/ p- p" @9 j: L6 H: A: f5 g
! O* @! E5 y$ ^static long fb_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
% j, r4 c, i# z) c' [{% x4 \+ b g3 o% q9 R" J
struct inode *inode = file->f_path.dentry->d_inode;: a7 m" \6 H7 f# b
int fbidx = iminor(inode);
: l6 t$ ~1 a. I4 t) W2 \ j+ u$ z struct fb_info *info = registered_fb[fbidx];
7 j7 D) ^& ~+ \( `$ L1 i
. M9 e% \4 o% o3 P /* 这个才是真正的fb_ioctl驱动函数 */7 ^) o( D- L9 p% O; G- q
return do_fb_ioctl(info, cmd, arg);
8 [. b1 ^8 G" D: t}$ y; r+ t+ A) m, \2 D
do_fb_ioctl函数根据cmd来设置各种命令,这里仅举例说明:; H- s5 E6 N2 d% F+ ^
$ x+ C9 x: C% t; f7 w
switch (cmd) {9 J+ Q6 M& }) h4 Z& i
case FBIOGET_VSCREENINFO: /* 获得可变的屏幕参数 */) v) @0 `4 g' Z8 k7 _+ p5 z
if (!lock_fb_info(info)) /* 如果info->fbops不为空,则上锁,成功返回1 */
5 K$ |' T( {0 Z/ d5 o# p return -ENODEV;$ I @6 @5 Z6 o- Q4 G" X
var = info->var; /* 可变参数变量的设置 */
+ ^( `0 H) s0 P& a6 W2 H unlock_fb_info(info); /* 解锁 */+ C ]- S0 z H V; ?; `2 k3 k. f0 u
% c* [& n( z1 J; J( A, m3 p! g /* 从内核空间的var地址拷贝var大小的数据到用户空间的argp地址里去 */
0 N( H1 |3 g' c ret = copy_to_user(argp, &var, sizeof(var)) ? -EFAULT : 0; /* 成功返回0 */
6 |+ z A/ I# c break;! @0 F. X8 g% l
fb_mmap源码分析:
C3 L& N5 O5 |1 _) {! {$ q) D) O- x h, F
/* 这里分配的显存是在内核空间分配的,用户空间并不能直接访问,% z9 J$ V! w2 L- _
* 所以需要用到这里的mmap函数,直接将这段内存空间映射到) y( p# s8 s2 M
* 用户空间去,用户空间就能访问这段内存空间了。
( M2 |5 [% V' k- m+ ?0 Y/ T */
* p5 M3 A% B9 h; Z4 N ?static int
! W3 E; `% w7 a/ Efb_mmap(struct file *file, struct vm_area_struct * vma)
Y7 t$ c: `* R3 O( D6 ~__acquires(&info->lock), Q; u& a! R4 b8 b
__releases(&info->lock)
8 m5 T2 n& [- E. ^6 @5 G8 G{9 p9 B8 I0 t2 S& S& F
int fbidx = iminor(file->f_path.dentry->d_inode);, X: o* ]% S* k! ?9 b' A' ]3 B
struct fb_info *info = registered_fb[fbidx]; /* 通过次设备号找到fb_info结构体 */
& a* ~& I$ G7 h9 r( e4 d1 b. b2 [% Q struct fb_ops *fb = info->fbops;
* ~, d4 P3 k' O- N7 w0 t7 c unsigned long off;
/ m* A. s+ M5 y' | unsigned long start;, P! X! }, _; v- Z9 l
u32 len;
u8 J; v$ O% b3 k" @
( N( |3 f. q! e5 X: G e$ X if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT))
) {5 P9 I7 P2 [- }& q/ n' { return -EINVAL;& H- C% }7 {) T; d% h# L6 w" ?
off = vma->vm_pgoff << PAGE_SHIFT;6 X8 s3 K$ r, }$ ?* x3 \ d
if (!fb)
$ l! M7 B6 A# r2 h return -ENODEV;6 F( t/ {( m6 }" I- O
; M B! ^, a) O: c' v
/* 如果registered_fb[]里有默认的fb_mmap就使用它 */
) b7 `9 M' `/ }2 Z ~ if (fb->fb_mmap) {
, n$ H8 ?" A- u F7 o5 x int res;
/ G+ T: q$ Z/ O2 T F9 [ mutex_lock(&info->lock);5 M, g, o# N7 X0 J/ R- j
res = fb->fb_mmap(info, vma);
) }0 e; n A7 _ mutex_unlock(&info->lock);
8 u* F* |) R" j& z6 ~2 N3 r }* f+ u return res;( V! @5 n5 ~/ F0 b
}
2 d0 T' P2 |" r& i2 _* N' K/ ]& d
mutex_lock(&info->lock);
) s4 F8 g2 \! o( U% N$ u6 t8 X/ Y& A1 t; N" _/ m0 V4 k- B8 ?- w
/* frame buffer memory */" p9 Y% l( ?2 `
start = info->fix.smem_start; /* fb缓冲内存的开始位置(物理地址) */
" }$ J6 r$ U& S! \ len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.smem_len);
0 v& c& _2 I4 I+ M/ c6 ] if (off >= len) { /* 偏移值大于len长度 */
) ]2 O9 w7 ^+ e4 a# A# s: `! k /* memory mapped io */ /* 内存映射的IO */
1 T! s0 F( C, C9 M, T$ o$ _7 D off -= len;% i* M" p% w, |2 F' C4 e5 ^
if (info->var.accel_flags) {* f3 J* \0 _. U4 |
mutex_unlock(&info->lock);8 K4 B: A" e" U2 B
return -EINVAL;9 s7 u6 e( k* t0 z1 f) n
}
# j' v3 b! F" [4 s6 O* T, b start = info->fix.mmio_start;
3 h% u3 ~! U: N! N P" g- f8 T len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.mmio_len);) y7 l4 U2 Y1 h( H; Q
}
5 V5 v/ z" w/ P9 ]! G mutex_unlock(&info->lock);+ p' C. S2 G6 f |1 I P2 j
start &= PAGE_MASK;
& h( m- e" {3 F& J" h8 z ~ if ((vma->vm_end - vma->vm_start + off) > len)/ D3 w% X F/ P3 c
return -EINVAL;$ _( e) A+ ? `! m: e3 Z7 {
off += start;2 T9 @8 q) x6 \$ O! M& i+ A1 E$ e
vma->vm_pgoff = off >> PAGE_SHIFT;/ ~& O( U% N3 Z# e8 q) q: W" B
/* This is an IO map - tell maydump to skip this VMA */% W& B% C* s, Q7 ]
vma->vm_flags |= VM_IO | VM_RESERVED;
, ]' s) S9 r" p% k6 Q* _) F8 n) _# H4 U fb_pgprotect(file, vma, off);
7 Z9 V' Z' L- u" \- M; [
& e: P* S& |/ z6 p /* io_remap_pfn_range正式映射物理内存到用户空间虚拟地址 */7 O4 g1 V8 j( A) E T) ]
if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT,
( }2 x/ P: Y4 I% }1 G vma->vm_end - vma->vm_start, vma->vm_page_prot)), X* v& H% D7 \5 m- Z
return -EAGAIN;4 {% `, E# W! {+ k9 R6 }
return 0;
. M/ V) D) ~& G/ }+ e- [1 Q}
2 q' g. f; U7 E' e4 {问:怎么写LCD驱动程序?* R3 o! Z- A2 g7 x( w4 \
1. 分配一个fb_info结构体: framebuffer_alloc( R' V: D* ?4 c/ {7 m& k
2. 设置
. U2 A0 @. Q$ G" P3. 注册: register_framebuffer0 w( g; a. n9 a7 ~4 R5 q' }
4. 硬件相关的操作9 o5 O: U6 e3 V, Y4 h
# z# Y* I& \9 h M
- s+ J' H4 a$ ?7 f: J
5 q" g: R$ q7 L1 A |
|