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