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