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