2 B- J4 Y% t9 h) L, O+ C * f' \: a0 w9 W# _4 x# d5 j) ?1 u核心层的代码以fbmem.c为主,核心层包括许多与具体硬件无关的代码,并且提供了API给用户空间。用户空间使用系统调用,系统调用会使用相应的API函数,最后会调用驱动层实现功能。对于不同的设备,驱动层的代码将有所不同。1 E/ F8 m9 q! q. V6 i- n
6 P& e. z; \: Z: n' _接下来的内容中,首先给出framerbuffer使用的数据结构;随后简单描述framerbuffer核心层;最后,针对S3C2440,对驱动代码进行分析。7 L$ J. g2 C8 y) r9 Y
3 l! Q+ F& b9 B% H, r d# E3 S2. 数据结构3 S2 J) |/ A$ k) s2 @
2.1 fb_info 结构' l W; U7 e- C9 A+ a( d- Q
该结构是内核用于描述一个特定framebuffer设备。其中包含了几个重要的结构,将在下面介绍。 $ y" T% ^; O. F* w5 V$ I6 u& K+ V ( G& o6 T" u$ n- c4 a/ p- a. p% G 下列代码位于include/linux/fb.h8 J W- r" P1 J; I
2 c0 d6 p1 n0 r. d" I$ @struct fb_info {4 g' d" N+ B( u! \5 Q6 a% W; E5 @
int node;2 _& }' v& H# p$ A3 ^% ^7 S
int flags;% H7 Z: D$ U, l: _$ D, ]
struct mutex lock; /* Lock for open/release/ioctl funcs */ ; l H6 c, [# W2 V struct fb_var_screeninfo var; /* Current var */ 6 ^' g0 @, ^! ] struct fb_fix_screeninfo fix; /* Current fix */! \) v2 Q* F5 G H; i5 Z
struct fb_monspecs monspecs; /* Current Monitor specs */ 7 P& z2 y. G4 `: @0 M8 }' s struct work_struct queue; /* Framebuffer event queue */! A7 s0 o G$ u" y& M- W" U
struct fb_pixmap pixmap; /* Image hardware mapper */ - F0 U; E) }" m* t: G struct fb_pixmap sprite; /* Cursor hardware mapper */ ' [5 D! Q7 @& v- j. H" ? struct fb_cmap cmap; /* Current cmap */ - A# C6 O$ c, Z% }* c7 x* n# h4 O struct list_head modelist; /* mode list */ $ W/ W% D! A# t" h' @' p+ t1 w, | struct fb_videomode *mode; /* current mode */) Q- Q( T' p6 w
4 y3 b' y. t% M0 x1 x1 O2 d
#ifdef CONFIG_FB_BACKLIGHT $ A# S# L' _$ j4 w. r. @& {6 o /* assigned backlight device */0 ^! I4 W+ E9 n) T9 P/ m+ G
/* set before framebuffer registration, : ^8 B; r* H0 J& X. I9 `! T/ { remove after unregister */ + c" `' ]" ^ C3 z0 _/ @ struct backlight_device *bl_dev;6 X; W# _# T; y6 X1 `
! N$ ~6 {+ l6 Y% t: g. I" q) o /* Backlight level curve */8 o) N4 v* z5 |9 G$ ~4 S( q
struct mutex bl_curve_mutex; ) N' l! }' ]6 A5 L. X" Y u8 bl_curve[FB_BACKLIGHT_LEVELS]; ; E$ D+ y* p/ a#endif ; m; N3 A2 B6 x#ifdef CONFIG_FB_DEFERRED_IO 3 F% `2 g7 U5 o struct delayed_work deferred_work;+ i; B- d" y' `4 x; m6 _) i
struct fb_deferred_io *fbdefio;: ^4 k0 c, ^# Z5 c
#endif 7 J% t z0 J n- g $ i. w7 a" f& T7 ` struct fb_ops *fbops; / Q1 g$ z- P8 j1 v struct device *device; /* This is the parent */ 0 Q* l5 t; b! |1 T0 t, F- K struct device *dev; /* This is this fb device */$ k+ W0 m8 {1 ^0 \
int class_flag; /* private sysfs flags */& b" \% U0 y) ^; k9 l" `
#ifdef CONFIG_FB_TILEBLITTING) ~4 L' u& P7 c2 d# _; b) o: m
struct fb_tile_ops *tileops; /* Tile Blitting */ 5 Q) a! n; {+ m2 ^% f#endif . r1 |2 F/ h6 a0 b char __iomem *screen_base; /* Virtual address */5 G# n6 r, \. P/ F" @
unsigned long screen_size; /* Amount of ioremapped VRAM or 0 */ - j# x: L3 g. |: a# p# @; ?, N void *pseudo_palette; /* Fake palette of 16 colors */ 2 J9 W; f# s3 c+ V+ ]( v
#define FBINFO_STATE_RUNNING 00 z+ }6 ^5 `5 z8 d6 G2 V
#define FBINFO_STATE_SUSPENDED 13 L% B$ O3 z8 V+ _0 B
u32 state; /* Hardware state i.e suspend */ * ~! Z5 H/ J" r) R5 Y j void *fbcon_par; /* fbcon use-only private area */ - S# w; L0 A3 b9 D/ R: y /* From here on everything is device dependent */# N# ^/ P7 I9 [8 x5 W5 z" T
void *par; 2 [" r+ H7 \5 d; l* Y4 ~% L6 S};- W" Q- t3 r* y F0 q1 E! p v$ W
2.2 fb_fix_screeninfo结构 * W; h8 _% N0 f# q* ` 该数据结构是不可改变的,也就是说用户空间不能改变该结构中的任何成员,在核心层我们将会看到这点。, C4 ^+ H5 o G9 V+ O+ r! _
7 ~; W5 m, n6 c; O
下列代码位于include/linux/fb.h : B8 \. k! W) y( o: J# I- O, m/ O5 p1 K( U3 }: c
struct fb_fix_screeninfo {/ a; k& g/ j$ `, q/ ]' r" U
char id[16]; /* identification string eg "TT Builtin" */( g0 U$ e0 U9 h7 e* `/ \. J
unsigned long smem_start; /* Start of frame buffer mem */8 D! ^0 x; k- J# s6 ?$ i8 f2 w4 |
/* (physical address) */ ; A+ T/ c: L* ?3 D! N7 v3 L& h5 C. h __u32 smem_len; /* Length of frame buffer mem */ # _) t! C5 x, _6 g, d __u32 type; /* see FB_TYPE_* */: g+ Y0 {% d8 r8 `; f
__u32 type_aux; /* Interleave for interleaved Planes */ 8 T, Q* p" f1 w p2 ~" t __u32 visual; /* see FB_VISUAL_* */ ' g. }! j* g- l2 T9 {, h* U9 b7 T
__u16 xpanstep; /* zero if no hardware panning */ d# A6 @+ s! G, P' V# O, l3 ]
__u16 ypanstep; /* zero if no hardware panning */ + `5 e( m( z" f( z* C% o7 z4 E7 r __u16 ywrapstep; /* zero if no hardware ywrap */1 m! @& n0 v$ \3 s9 J! J6 {
__u32 line_length; /* length of a line in bytes */9 p6 Y0 U5 d4 M$ h. m9 R. `' v
unsigned long mmio_start; /* Start of Memory Mapped I/O */4 `9 M) s: r3 g! B
/* (physical address) */ f7 j8 \! z* R: V' a3 { __u32 mmio_len; /* Length of Memory Mapped I/O */ ( J$ S" D+ l, o g5 p* ] __u32 accel; /* Indicate to driver which */ 6 @9 o% }% E3 A! K- U, { /* specific chip/card we have */. @9 ~ O+ d6 P: O7 w6 G6 L+ w+ c
__u16 reserved[3]; /* Reserved for future compatibility */- u$ \8 D/ A, F1 t
}; % w9 m2 z9 B2 q; N* ]( Z- A* F' w! i: W7 @
2.3 fb_var_screeninfo结构 Z9 o# m; y- [: K; g3 b 该数据结构是可以改变的,也就是说用户空间可以改变该结构中的成员。该数据结构中的很多成员就由板级信息复制而来,在驱动代码中我们将会看到这点。 $ N6 ~# Q$ D9 b8 B 1 {% G" {7 u4 C( y) b$ n4 {/ o 下列代码位于include/linux/fb.h1 x% c* V1 F% w# F! L. P
; Y$ Z) b6 `$ |7 q1 [/ x
struct fb_var_screeninfo {: Z5 l1 n% p/ w$ e: Q/ W6 E" `/ L
__u32 xres; /* visible resolution */ i. k: n/ [4 U' Y1 W6 K __u32 yres; 9 _, _+ q5 E2 H! _7 D7 g __u32 xres_virtual; /* virtual resolution */ & _- U$ X3 q# ^1 g; o __u32 yres_virtual; 7 m/ G1 V( f8 P, Z __u32 xoffset; /* offset from virtual to visible */ / C6 d7 H. _9 {- v R- ] __u32 yoffset; /* resolution */ 5 s) ?% T2 a3 p$ G2 D# o, d- @( y5 D" ]$ z0 z, |
__u32 bits_per_pixel; /* guess what */ : J( l4 G- v# T* L8 O" x __u32 grayscale; /* != 0 Graylevels instead of colors */% Q) ?4 X9 `! ~4 L, y" l
D" a g2 ^+ b) E! a% v
struct fb_bitfield red; /* bitfield in fb mem if true color, */ , h- Z4 @9 l: v+ o3 ? struct fb_bitfield green; /* else only length is significant */, S" \5 z! i% J. i, N0 ~
struct fb_bitfield blue; ( B3 \9 M9 k) ]2 Q* h- N struct fb_bitfield transp; /* transparency */ $ Q" Y1 n. V, e; X. N1 o# N$ o
& u2 }" n2 [* ?! {3 M8 m __u32 nonstd; /* != 0 Non standard pixel format */ ' o& J& {* i7 I7 b) z& H3 F1 n! I2 F# i2 l
__u32 activate; /* see FB_ACTIVATE_* */ + {. a |' Z! Y3 D8 j& y% I& @ ( J( g! N# O% R% ` __u32 height; /* height of picture in mm */ * x" Y# w& P ^8 a8 ^6 H __u32 width; /* width of picture in mm */ 3 `, {3 A- m% `! [2 p& G7 s- }7 @2 Z' w% S. I) K: j3 g
__u32 accel_flags; /* (OBSOLETE) see fb_info.flags */ 9 K' Z) z% a4 k9 l 6 x9 `- c. t& e /* Timing: All values in pixclocks, except pixclock (of course) */& b0 P8 M! R5 Q) e7 ^6 L' ^1 p0 I
__u32 pixclock; /* pixel clock in ps (pico seconds) */ ( @5 I) p! }5 h0 ^ __u32 left_margin; /* time from sync to picture */ ( Y6 h" w2 j% D0 Q9 B9 [ __u32 right_margin; /* time from picture to sync */ 7 u0 [5 n. Q) v! x __u32 upper_margin; /* time from sync to picture */ * K+ R3 [/ O. Q3 v$ i$ v2 ~0 W/ u \ __u32 lower_margin; . q ^; n, C/ Y* H% A1 G, P0 e __u32 hsync_len; /* length of horizontal sync */ 1 w5 A/ K7 U3 f5 _* e; t __u32 vsync_len; /* length of vertical sync */) w9 y( g) w; M/ |6 R; U
__u32 sync; /* see FB_SYNC_* */) w1 ~2 B; `6 E8 R4 w
__u32 vmode; /* see FB_VMODE_* */; C9 q' V' X5 U7 ~8 d
__u32 rotate; /* angle we rotate counter clockwise */ , Y7 u+ K: i: e# m" ^' j2 W8 Q% a/ v __u32 reserved[5]; /* Reserved for future compatibility */) T' h" Q9 v" A8 u$ x
};9 W. H5 R) p: a1 h! n& y4 m
# H. w0 j1 }8 e2.4 fb_ops结构: @- R0 N: ~! c
该结构描述了用于fb_info的方法,这些方法中有些是要驱动程序提供的,而有些可以使用内核提供的方法。1 U& S, ^/ W8 l# ?
k( n, D+ D3 R/ p: B+ i 下列代码位于include/linux/fb.h. Z' M2 L4 d1 J( r
- U M6 m5 i4 l/*$ O* I. D! S5 i
* Frame buffer operations" ]+ b9 Z' A+ _" ?4 \
*, j/ R2 A8 a$ q( l$ _ ^. @; Q) `
* LOCKING NOTE: those functions must _ALL_ be called with the console0 m- f& h" L% P/ G5 F: b4 K
* semaphore held, this is the only suitable locking mechanism we have 7 d+ ^ A0 N3 p0 M; c6 V' @+ ^ * in 2.6. Some may be called at interrupt time at this point though. 0 o1 V. g( f9 d) S */ 2 Q' A! q, P! ]0 r1 R ' l! x# H/ i: B: K; x; ^struct fb_ops { m0 R% t6 S2 d9 h$ K9 w2 S
/* open/release and usage marking */ ! Z) v! ]0 C* P, s$ k' S F struct module *owner;# W# {. h: m6 X" t2 L6 _9 s! X
int (*fb_open)(struct fb_info *info, int user);4 {' T1 Z& P& E! u; q$ f: ~/ Q
int (*fb_release)(struct fb_info *info, int user);9 H: q7 Z) W1 T& _6 S
; Q! f' I; K1 O' }% P
/* For framebuffers with strange non linear layouts or that do not . D4 O1 p3 e7 R * work with normal memory mapped access : v- h7 \" i0 m" E: T0 A+ p */: R7 Z u: a! O G# Z
ssize_t (*fb_read)(struct fb_info *info, char __user *buf,' {4 N5 N& M i% c1 y
size_t count, loff_t *ppos); 2 \, C9 S! `+ B1 n6 l9 e0 I" Z: q ssize_t (*fb_write)(struct fb_info *info, const char __user *buf, & ]* X6 `/ e) r& p5 ~ size_t count, loff_t *ppos);2 K! W& o; F) U+ w" c0 @! d
8 h2 w2 u2 S- h /* checks var and eventually tweaks it to something supported, % v9 W6 F( w6 _" a * DO NOT MODIFY PAR */$ U, f) N2 Y( V' N
int (*fb_check_var)(struct fb_var_screeninfo *var, struct fb_info *info); % a6 y! x/ ~: w" s/ K 8 e& r) I6 i; D0 Y /* set the video mode according to info->var */ * O8 D8 c8 y p2 i3 Z9 A+ }7 q int (*fb_set_par)(struct fb_info *info);: n5 W( Y0 V7 x& N) v8 c
6 |- o: v9 G# }5 L' l' C /* set color register */ + @7 t e9 @/ I; l R* K( @/ q int (*fb_setcolreg)(unsigned regno, unsigned red, unsigned green, ' |! A7 O' w. Y& p unsigned blue, unsigned transp, struct fb_info *info);, I1 ~- `6 c% [* Q+ Z* a% M6 Y
/ K4 Q% m" [9 O5 S
/* set color registers in batch */ 8 c! {: n9 j3 [1 F# a* @, L+ } int (*fb_setcmap)(struct fb_cmap *cmap, struct fb_info *info);- V- M8 i& W" w/ y+ M `5 n, L
( n7 [3 Z2 i$ ], M
/* blank display */ ! H9 l( Q E1 q, t, K5 j" q! X9 Y- a int (*fb_blank)(int blank, struct fb_info *info); E4 d2 l: t Z, x/ ?3 y3 H) `4 B5 h7 }: W9 v
/* pan display */' o4 P, N1 y ]' O9 w$ ~
int (*fb_pan_display)(struct fb_var_screeninfo *var, struct fb_info *info); 4 O: V9 Q8 g% n& m; b 0 S1 q* \- p+ z /* Draws a rectangle */ , y0 a* I% }$ w* G6 `4 B void (*fb_fillrect) (struct fb_info *info, const struct fb_fillrect *rect); e; y7 v) L/ B6 M
/* Copy data from area to another */' ?6 r- O ~/ N t6 y
void (*fb_copyarea) (struct fb_info *info, const struct fb_copyarea *region); 2 M. `. E+ p# ^ /* Draws a image to the display */) V" l" Z. o; Y; v4 b
void (*fb_imageblit) (struct fb_info *info, const struct fb_image *image); . P5 y! T1 x9 Z5 a ) I3 T7 u# H5 A& @ /* Draws cursor */ ( ~4 Q9 B8 R/ z1 z) E @# c2 z: ] int (*fb_cursor) (struct fb_info *info, struct fb_cursor *cursor); ; ^/ N+ [" N/ ` ! c/ f: ?% ]/ o# `/ l /* Rotates the display */# v% i3 @/ O+ h- T3 E+ f4 `5 I& Y
void (*fb_rotate)(struct fb_info *info, int angle); - f( Q1 l4 o8 K9 ], ~, M+ {% D1 v
/* wait for blit idle, optional */ ! s$ P5 C" @3 t* o# n* m int (*fb_sync)(struct fb_info *info); & s& Q# R" y. Y % I; K6 f0 j- _8 k2 M /* perform fb specific ioctl (optional) */8 A) r* [+ I& u; X
int (*fb_ioctl)(struct fb_info *info, unsigned int cmd,% x$ u& O1 h: t0 R; N6 E7 `
unsigned long arg);) Z% j7 n( x v& V' L0 Y
5 ^3 U. C1 I+ V# {" \, @ /* Handle 32bit compat ioctl (optional) */+ x+ t. U% Z( S2 R0 R0 u
int (*fb_compat_ioctl)(struct fb_info *info, unsigned cmd,4 ^; i0 x% d+ U& [) {
unsigned long arg); F* i* |3 n; \7 I c# C0 T, j1 X& i9 K; t
/* perform fb specific mmap */ $ h( E, s0 n! S$ y% G7 ] int (*fb_mmap)(struct fb_info *info, struct vm_area_struct *vma);; X, f0 t0 g+ z- \
9 W* @: m9 X+ z; B2 E /* save current hardware state */; Z4 r1 p, m. z( o+ z
void (*fb_save_state)(struct fb_info *info); + d3 W9 r7 L! F! {3 T6 p, O; o# P4 i% e5 e: ?. l" ]
/* restore saved state */ # g& ?( k4 d! C6 _" W% p' t void (*fb_restore_state)(struct fb_info *info); 9 i# V; A+ g) H* r W- l/ M: M6 u' y 9 a3 x3 v7 R% ^' O. D6 | /* get capability given var */2 c. r) p! b' y- a- z+ q
void (*fb_get_caps)(struct fb_info *info, struct fb_blit_caps *caps, 9 [& W0 Y/ n9 _. P, C6 t struct fb_var_screeninfo *var);/ D! V8 C% e) ]
}; ! j, q0 p8 ^+ R. Z: Q( O; x9 n% r/ B5 b4 y; e) _; @3 J. E
5 P1 _/ M7 X3 K7 y+ s7 x
3. frambuffer核心层; ?* Q2 h/ @% o4 f
首先来看下frmaebuffer子系统的初始化函数。 ' p3 X9 ?& ~5 a+ R6 P, G4 e$ F. ~
3.1 fbmem_init和fbmem_exit ' M2 q! \0 \# ?- S3 z下列代码位于drivers/video/fbmem.c " v4 ?6 {% J( w8 @0 P - V S0 }* G# ?" `* V1 {5 P7 q- \0 U/** 3 v6 q% G0 h2 r) `3 T, f * fbmem_init - init frame buffer subsystem ! e8 W/ u8 }1 l R * - j( C& d7 _* C" o* n, } * Initialize the frame buffer subsystem.+ T3 L+ f$ g# [0 H7 u" ^ z
*7 [4 d" Z/ X* v3 S; B
* NOTE: This function is _only_ to be called by drivers/char/mem.c. ' L3 b! ` t( f$ Q9 e * 8 h @, f; H( B6 | ?- v. i$ y */ ' B, y; I3 `8 Z/ q8 W2 Z# u0 q9 V( T& h* I# [* ]3 }
static int __init 3 i' P, r% O. sfbmem_init(void) ! O" s) }! E, @4 l6 ?* p# [{ % I: V$ Y* ?2 h; C9 h( ]2 m# D proc_create("fb", 0, NULL, &fb_proc_fops);! L% X0 T6 j' |7 e
! r+ V' l1 t5 U if (register_chrdev(FB_MAJOR,"fb",&fb_fops)) /*注册字符设备,major=29*/ , F' s+ @+ Y" s2 y( r6 B$ @ printk("unable to get major %d for fb devs\n", FB_MAJOR);: G: c; n: w6 o8 F* U
2 e Q* M/ m$ Q" ] fb_class = class_create(THIS_MODULE, "graphics"); /*创建类*/4 u# ~, m4 I. _0 p I; [
if (IS_ERR(fb_class)) {/ |* ?7 F+ p% E6 T+ w6 v
printk(KERN_WARNING "Unable to create fb class; errno = %ld\n", PTR_ERR(fb_class)); ' O/ |9 C% _3 ]/ s fb_class = NULL;( ?3 L0 b# s5 R+ U/ _) \/ E2 ?
}+ C) U+ W' g/ _, X% W% i
return 0;0 g- @: }% v) d9 r. N9 N1 U7 P2 M
} & Q- N F! z/ G8 k( g 5 S7 q! w, N9 ~#ifdef MODULE 5 l0 Q: d X& v( Tmodule_init(fbmem_init); 1 r% g6 r5 `5 ~+ Q* |2 sstatic void __exit ( x# |3 U) d/ h- Hfbmem_exit(void) B2 [- [2 N( H7 I{ 2 j9 C* ~7 ^+ T; k, n% G remove_proc_entry("fb", NULL); 7 k. e: ~( M% o+ g+ B4 d! { class_destroy(fb_class); 0 n( J& f1 h- b' \ unregister_chrdev(FB_MAJOR, "fb");( H1 e \* @& f* n
} ' z: B$ Y0 e, M! s& s) @6 `& W* y8 o9 s: i2 m+ z
module_exit(fbmem_exit);/ W. L) ?+ V" @1 ^% R% [9 N
MODULE_LICENSE("GPL");& ?, \$ Z3 v; K+ @- ^* \
MODULE_DESCRIPTION("Framebuffer base");% t( w. c& S4 w* V1 j( T
#else$ z8 i! @5 q$ l4 @% x8 t
subsys_initcall(fbmem_init);. W. N* ~/ Y& e3 P6 B, `
#endif 4 b: T. `! T9 K2 y _% n / P+ h$ G: v" U# P/ j# H l; B* Ustatic const struct file_operations fb_fops = {8 l3 g( J ~) X" q! d; H& B, M$ o
.owner = THIS_MODULE,8 ~, f+ }) q, J; H
.read = fb_read, 1 E: D( o3 [& r. }5 |# ]0 k% P .write = fb_write,4 A6 m H& m; ]( \3 T' J
.unlocked_ioctl = fb_ioctl,5 W8 c( T2 e; ]$ K+ Y/ M. O, d
#ifdef CONFIG_COMPAT% L6 `' k0 t9 N
.compat_ioctl = fb_compat_ioctl,- y+ _$ d9 D/ {& G$ j; F
#endif ; s+ J9 R* p2 ^6 w9 U .mmap = fb_mmap,9 h$ w! a1 {7 d
.open = fb_open,9 X( z' i! n/ n. h9 s$ ]( d$ q$ y6 B; f
.release = fb_release, 3 d2 _+ u+ n$ U3 n. K#ifdef HAVE_ARCH_FB_UNMAPPED_AREA6 Y" f2 \" L9 Y
.get_unmapped_area = get_fb_unmapped_area, H" Z% l( c8 ]! H( }
#endif ( b2 A, e, J0 z2 ?+ }& _- [#ifdef CONFIG_FB_DEFERRED_IO * F2 ?5 M$ `+ { H1 C5 W: j; g3 s" G .fsync = fb_deferred_io_fsync,* X* v7 T, Y, g i3 a
#endif 3 E9 I M, i$ g" w6 N6 a# g; H$ ~; _}; / F! b- a5 G! ^ * }# v+ A9 ]* m' n+ G2 f我们看到,如果不是作为模块,那么该初始化程序将在subsys_initcall阶段被调用。初始化时,仅仅注册了一个字符设备,并创建了一个类。通过字符设备,提供了API给用户空间,包open,release,write,read等。 0 c9 } n7 p7 X; n' o& W' K7 a随后我们看看如何分配一个fb_info结构。 1 N2 X% t; @9 D + K: s' s8 [( t7 _' i# G3.2 framebuffer_alloc 5 H! U" y# p" b9 j1 B下列代码位于drivers/video/fbmem.c2 k# n7 P: ~ S
4 O. e# f v* [4 w; E
/** / \5 ~0 Y- }5 Q" [ * framebuffer_alloc - creates a new frame buffer info structure9 N$ [) v8 g) Y5 T+ b
*: v4 v9 x2 X4 Y5 [- S
* @size: size of driver private data, can be zero 1 h; y% I$ o8 D7 w# X0 n; O * @dev: pointer to the device for this fb, this can be NULL. f5 Y: g* ~. L
*6 }: y' ?7 _4 L0 R* @
* Creates a new frame buffer info structure. Also reserves @size bytes % X1 v9 ^# d% Y) e w; | * for driver private data (info->par). info->par (if any) will be ; r% Q5 h5 k; L9 g- H8 l& s * aligned to sizeof(long). 3 g) O. p1 v2 {( ~& p *1 @5 f' \3 M, k$ }9 X. J
* Returns the new structure, or NULL if an error occured. 9 p5 ~$ |6 e- c5 v * - e; D9 X' ]6 A) I2 L *// U+ C! L# e; N) y7 S3 _
struct fb_info *framebuffer_alloc(size_t size, struct device *dev)0 l4 o j5 c2 q
{ ( m+ G" u, `. N# z- u- U/ \# [4 F. I# r#define BYTES_PER_LONG (BITS_PER_LONG/8): }; o- d0 h- c1 C1 q b
#define PADDING (BYTES_PER_LONG - (sizeof(struct fb_info) % BYTES_PER_LONG)) ; t# G8 @4 X1 C2 }3 a. r int fb_info_size = sizeof(struct fb_info);0 _& M0 G1 Z' J3 b3 `8 u) @7 j) B
struct fb_info *info; % Y2 n7 G% b9 Q2 Z4 [+ V! D char *p; + v. T1 I/ F5 n) d5 I $ a! ]3 w$ k; Z ~1 e if (size)5 x# D& K; n! U1 h6 c0 n
fb_info_size += PADDING; * Q$ k. `2 T+ k- K) `2 J2 S / j Z% F3 i B p = kzalloc(fb_info_size + size, GFP_KERNEL); , f2 ~3 ~* P3 M2 l/ Y5 t- ` 8 o- I. S4 n- I2 \! T: U if (!p) ; X5 B) V+ P1 a0 H return NULL;5 {& T2 B3 e' M
* p l+ ^+ z1 R5 K# X+ `4 x# u( a
info = (struct fb_info *) p; 2 [6 K1 g1 w, j) Q" Y + i! q+ N3 a+ W1 x: R if (size)" }" K0 X3 `5 `/ n) q7 }6 W: V1 r
info->par = p + fb_info_size;% q! U+ ~1 ~6 h3 c0 Z) H
0 d- k& j9 c* ?& E P5 O5 g
info->device = dev;! |/ Y/ G9 _/ _. ^! u# |
# M. s) B) r' m6 B
#ifdef CONFIG_FB_BACKLIGHT / M. W/ `4 d" F mutex_init(&info->bl_curve_mutex);( |2 y2 M' F4 {4 m% F
#endif+ r. j6 [* Z8 _ c2 D7 t2 r
* O' C. T0 {- J. S. c return info;1 b" j6 o' A# X: V. @
#undef PADDING( n- u7 {( Z6 ]; g: X; a' S
#undef BYTES_PER_LONG * x4 S& \" n& c5 S7 F6 Q% _* C}$ J8 V. z& e1 p- G
EXPORT_SYMBOL(framebuffer_alloc); & }3 O- w* \# \" R! f! h& V0 S在进行分配时,根据参数size的大小,分配了b_info_size + size的空间,然后让fb_info->par指向size的空间。因此par所指向的空间可视为设备特有的数据。 ~: e7 L) x1 X在分配了fb_info结构之后,需要将它注册到内核中。注册由register_framebuffer完成。我们来看下。2 `! P8 B1 V+ J. s
3 C* ~9 k: e' r( N3.3 register_framebuffer 2 s* N/ v" E ^: S0 p/ s% b下列代码位于drivers/video/fbmem.c : |/ L; o3 j' E5 g' L. X3 X + |" J+ w5 q: \- J. p( f3 [; n/**4 g/ A" W* u& a% }( C) j) b* x
* register_framebuffer - registers a frame buffer device: ?. i" f2 P9 b% y( v
* @fb_info: frame buffer info structure: g% U; x7 F1 v! q4 m9 `
*3 P, M# m6 i7 M7 ~# L
* Registers a frame buffer device @fb_info. ; _7 H/ l/ }6 Y' w5 Z0 P" l: P * ) m6 u! @3 o6 w3 J * Returns negative errno on error, or zero for success. & P6 k( B& D+ H: b8 h8 Y * / E* \8 D, U r* `- z' N */' H3 r0 Z2 B: T8 i% f9 Z) V: ]0 V
% l' p; `5 P+ u7 W3 R) X( ~int 5 o/ g9 v6 L& k2 `# wregister_framebuffer(struct fb_info *fb_info)* E) N) W( L q. \$ n
{ - E8 P' m8 `$ b, O4 C int i;8 k& N$ C3 R7 c9 Q+ L6 A
struct fb_event event;. v4 l. V# T9 J9 b4 p4 I3 t& C
struct fb_videomode mode; 3 Q2 y1 A5 w8 G1 ]6 C* `2 q, f4 U, s, I. k( S
if (num_registered_fb == FB_MAX) /*最多32个FB*/- Z( d; h. \5 X+ P
return -ENXIO; 7 d6 N/ A- b L+ u" f+ W$ N! B( O9 F) w! K X
if (fb_check_foreignness(fb_info))8 L4 ~) o( B% u( e. r: c* u
return -ENOSYS;- R8 u' z" D$ U
+ ]% X, P) ?, u$ R* [# L9 w$ L
num_registered_fb++; /*对注册的FB计数*/ N2 I, }: W' e) Q
/*寻找第一个空位*/1 |: Z9 H9 e+ Q- n
for (i = 0 ; i < FB_MAX; i++)/*FB_MAX=32,也就是最多32个framebuffer*/ 4 A2 U K! O; T& @ if (!registered_fb) /*struct fb_info *registered_fb[FB_MAX]*/1 A! o+ ]6 N% B2 S7 I7 O$ s5 `
break;0 H% O3 N8 U1 t, T) S0 V1 R
fb_info->node = i;9 q1 A Z1 l3 L7 C( X3 f
mutex_init(&fb_info->lock); /*初始化互斥体*/ ; s u& }% x- y- p x: a6 b6 i4 K 1 v+ [2 r/ T% e9 P0 p fb_info->dev = device_create(fb_class, fb_info->device,/*创建设备节点,节点名为fbx*/ - g/ W) p3 _( ?6 T+ p% n/ E, K- ` MKDEV(FB_MAJOR, i), NULL, "fb%d", i); . N; z! D F, Y0 Z* B if (IS_ERR(fb_info->dev)) {1 f5 B7 p) K/ n1 v6 g
/* Not fatal */7 ~- u+ P1 E5 p( a4 Q
printk(KERN_WARNING "Unable to create device for framebuffer %d; errno = %ld\n", i, PTR_ERR(fb_info->dev)); ( u. `1 X( J: i) n4 W0 R3 E fb_info->dev = NULL; + M( A; ]( l% O* m" Q" f } else6 @) x8 H4 V L+ Y! J
fb_init_device(fb_info); /*初始化,在class/graphics/fbx/下创建设备属性*/ + Y# K9 A9 z6 M% r5 p7 m' O N, s: }( Q- w5 ~. T0 \+ ]0 ^
if (fb_info->pixmap.addr == NULL) { ! z5 ~( ^ e# x+ f fb_info->pixmap.addr = kmalloc(FBPIXMAPSIZE, GFP_KERNEL);/*分配内存,1024 * 8字节*// ?7 X8 t% c: `5 v
if (fb_info->pixmap.addr) { 2 U5 J. p2 v0 I0 d0 F3 y* L" m/ C& C fb_info->pixmap.size = FBPIXMAPSIZE; 9 W& Y' f5 d4 z7 ]% L1 R fb_info->pixmap.buf_align = 1; 7 n6 o. s. ?# {, Y fb_info->pixmap.scan_align = 1; & A: J9 v6 |+ `7 `2 X6 G fb_info->pixmap.access_align = 32; # v; g! g- ^7 z2 ~8 S fb_info->pixmap.flags = FB_PIXMAP_DEFAULT; ; U2 R+ J% `' B3 [ } ) G L9 i4 o: j+ y } . U, T% `* n3 V fb_info->pixmap.offset = 0; - K1 \2 E3 K5 {' {2 J! @. p- A& i9 R m9 r9 U, U+ c
if (!fb_info->pixmap.blit_x)8 k! a) h6 X* V$ i1 N" O6 S
fb_info->pixmap.blit_x = ~(u32)0;* q. ~2 Y( l+ o b: x0 M' c% f
: C9 j8 R$ ?0 h5 e6 r& B3 n if (!fb_info->pixmap.blit_y)5 _& e( S$ J3 B3 D3 s) L0 O
fb_info->pixmap.blit_y = ~(u32)0; + | ^- c# n, G 2 c0 R8 F) R2 b9 N4 ]- R. |0 Z1 U if (!fb_info->modelist.prev || !fb_info->modelist.next) /*该链表没有指向其他节点*/4 o: r: J5 n+ }9 j2 y3 [
INIT_LIST_HEAD(&fb_info->modelist); /*初始化链表头*/ $ ]# j [4 g- \# l ( i4 V: W; W4 y" ^ fb_var_to_videomode(&mode, &fb_info->var);/*转换fb_var_screeninfo成fb_videomode*/( }8 w/ w" `- k3 K
fb_add_videomode(&mode, &fb_info->modelist);/*添加mode至链表中*/ 6 d: n7 Q; w6 l9 c registered_fb = fb_info; ( Z+ ^& U/ O6 [& b/ j( w0 D) o8 `7 c1 |' m- u
event.info = fb_info;% T5 |* P7 ~) ^4 T: h, `1 b
if (!lock_fb_info(fb_info)) & d" {% v# e( k9 I2 V return -ENODEV; 0 {3 z: o* L6 m) T- W fb_notifier_call_chain(FB_EVENT_FB_REGISTERED, &event);/*???*/8 I/ l$ l+ ?, V' ~5 M% _
unlock_fb_info(fb_info);- _+ B' [8 h4 o8 y
return 0;1 }+ d& y- [) |4 J% K
}1 |7 c) C0 r2 o
! I, m" i, i. h. c
从这个函数我们可以看出,framebuffer子系统只支持32个设备。在创建了设备节点以后,建立设备属性节点,随后将fb_var_screeninfo转换成fb_videomode,最后添加fb_videomode至链表中。 * P: \8 Z: X" U我们看下其中调用的函数,首先是fb_init_device。! D+ n3 L' h; L8 w, d
' K4 D- Q" P, k) @下列代码位于drivers/video/fbsysfs.c + t4 F( I% P' G6 f: H2 n; S& z - R6 J0 |0 I: s0 X. T$ D: A" }+ K0 ?int fb_init_device(struct fb_info *fb_info)9 Y' B2 a5 L7 x6 T( x2 W! ~ S
{ - ^4 l q" s8 [( c. r int i, error = 0;" B p- x% ^+ G
+ |2 n7 T( u9 e) }6 a' O8 F
dev_set_drvdata(fb_info->dev, fb_info);' ]2 l+ G" k, f" X$ |
# u2 w- i/ X. J2 T
fb_info->class_flag |= FB_SYSFS_FLAG_ATTR;! G! _4 G+ r9 _8 p D5 P Q1 a9 O
3 P+ B9 M/ W& K* \% a /*建立设备属性*/4 H, r% K7 o' @/ p) f8 |. i
for (i = 0; i < ARRAY_SIZE(device_attrs); i++) {( ~4 E5 g* L2 V1 U7 e
error = device_create_file(fb_info->dev, &device_attrs); ! p; d" e! u7 U h( q& T* U9 e' p& f: J% H* t
if (error)% S; M0 x/ t% I
break; G r) J6 w) ]) l# n
}0 E( r3 \& l+ K
- u) u$ F$ p1 I9 V0 _ q: Q if (error) { $ @- z1 w) D$ [* U while (--i >= 0)& G1 S) `; B; a
device_remove_file(fb_info->dev, &device_attrs); ( x8 T: u4 ~4 ~7 s. b( ~ fb_info->class_flag &= ~FB_SYSFS_FLAG_ATTR;5 h5 N/ W8 f; X i2 }) h
}% o* W4 E, {) |% z7 j/ `; Q
# Q- Y0 {- q% n% A; W return 0; v; d4 v1 ?1 u* q- N& [! w}6 M. n9 A6 N5 V$ l% g' {; h
* I$ s, t4 r, [: e/* When cmap is added back in it should be a binary attribute 7 L9 k3 E* U v& H( _ * not a text one. Consideration should also be given to converting 1 M S0 |4 l" J p& w# e * fbdev to use configfs instead of sysfs */: o$ @/ G0 F1 ^
static struct device_attribute device_attrs[] = { 2 J+ v8 a* a4 x __ATTR(bits_per_pixel, S_IRUGO|S_IWUSR, show_bpp, store_bpp), ( f; s1 S3 F- ~8 D# K s __ATTR(blank, S_IRUGO|S_IWUSR, show_blank, store_blank), $ W/ a) V. [: u2 _* X9 h __ATTR(console, S_IRUGO|S_IWUSR, show_console, store_console), 5 w- ~# d1 b1 O __ATTR(cursor, S_IRUGO|S_IWUSR, show_cursor, store_cursor), . m4 _ M- |/ [4 T- d __ATTR(mode, S_IRUGO|S_IWUSR, show_mode, store_mode), ' e7 f( s) i- P- {1 n! g __ATTR(modes, S_IRUGO|S_IWUSR, show_modes, store_modes), 2 k9 E- _! s( O$ ?! O __ATTR(pan, S_IRUGO|S_IWUSR, show_pan, store_pan),3 Z) @2 T0 V1 p3 P! c0 \8 y# V
__ATTR(virtual_size, S_IRUGO|S_IWUSR, show_virtual, store_virtual),& x( ^; k. H2 k+ w* p
__ATTR(name, S_IRUGO, show_name, NULL), % U# \' I0 f A+ a __ATTR(stride, S_IRUGO, show_stride, NULL),: H8 X8 _$ f7 ?# \! X
__ATTR(rotate, S_IRUGO|S_IWUSR, show_rotate, store_rotate), + R8 J8 [- i v# H$ T. L- Q% I! x __ATTR(state, S_IRUGO|S_IWUSR, show_fbstate, store_fbstate), 7 u1 e- }9 n) u2 _" T#ifdef CONFIG_FB_BACKLIGHT ; _2 [3 q' R9 n& H! \( N! T( @ __ATTR(bl_curve, S_IRUGO|S_IWUSR, show_bl_curve, store_bl_curve), 7 h7 W$ A! |; a0 W8 T9 U#endif0 g' I* n( e" p; t& p e. N
};; B$ S3 [% h. g; W8 K+ O8 u& p( V
# c: {6 s; E# r9 p1 A5 a
我们可以在/sys/class/graphics/fb0下发现这些属性文件。 ( Z5 G ]; O7 ~& Q[root@yj423 fb0]#pwd" K8 Y# C$ J3 @. L* Y7 `
/sys/class/graphics/fb0' u$ I- m$ V2 k! H1 l
[root@yj423 fb0]#ls % O1 R, D1 Y1 ]0 pbits_per_pixel cursor mode pan state uevent 6 s9 a8 K0 K7 O( fblank dev modes power stride virtual_size g( F- {/ ]) {3 o9 Qconsole device name rotate subsystem* W% m* f- e2 y, K h# I' _
( W, z9 p: Q8 W& D
; F' G8 {' C- r" v$ [
接着看下fb_var_to_videomode和fb_add_videomode函数。! z; Q s8 m6 p" ~
$ g9 T) \, Z" Q' p9 `7 X* y下列代码位于drivers/video/modedb.c和drivers/video/fb.h + Y5 F1 Q, W5 U3 B; V4 R 1 O* k) }' J6 r5 wstruct fb_videomode { 9 j9 B3 ^* G& ?1 R1 C1 L. R const char *name; /* optional */ 8 |: e6 d2 B, e+ ^3 C+ Q0 ] u32 refresh; /* optional *// k' [6 S: [0 B
u32 xres; . u) ~; X$ J( c' L. f. N4 ? u32 yres; 1 M6 @8 ~" \# H, m; _" [, I0 p u32 pixclock;. g9 D) m1 f4 g1 W& O I
u32 left_margin; ) W( ~/ Y$ c) V u32 right_margin; ) X3 R% G" z" r: F. c1 [ u32 upper_margin;3 l& C9 s' b" c* H/ {& ?9 [; W
u32 lower_margin;& T6 o9 k, q" p7 f0 `" b
u32 hsync_len; 5 j9 x! g2 j/ ^6 M u32 vsync_len; - p" L9 o5 s3 D2 D# M% \ u32 sync;. r4 q2 w& n) n& d9 J6 u- N
u32 vmode;' G9 C# k4 V3 q9 }/ {
u32 flag;+ B* H( U7 e$ W0 V8 z m: B9 s6 J
};: ]! n, r" F/ ^2 L
- W: x( R* D" z' Q: k8 ?/ M4 T
/** / H. g& M$ e; A, Z. ~2 x: f ^ * fb_var_to_videomode - convert fb_var_screeninfo to fb_videomode 1 c' Z" N5 b S# T4 W% g6 |6 o+ V * @mode: pointer to struct fb_videomode% g/ E) v) i) X- U& _2 I
* @var: pointer to struct fb_var_screeninfo ) c+ X' U$ s& i5 P2 i */8 ^" y; ^/ T3 s) z3 \2 V
void fb_var_to_videomode(struct fb_videomode *mode, / h6 D$ K) f6 l* l2 T. y# P- P const struct fb_var_screeninfo *var)( z# v$ I* D: b, N1 u' C; [
{& `. i( _+ [4 b$ h9 f
u32 pixclock, hfreq, htotal, vtotal; 7 q' L+ Y" p& p" x& F4 f8 ] , T/ v( ?, {7 `: P. m mode->name = NULL; 6 @ I% a1 Y/ N7 y mode->xres = var->xres;' p6 ]& {# Q# T& j, a
mode->yres = var->yres; , k; `, q' q, S; ] |6 T( N mode->pixclock = var->pixclock;4 L& _2 Y; m9 U. Z3 Q
mode->hsync_len = var->hsync_len;* F- O' z& Z- w C- R) K
mode->vsync_len = var->vsync_len; 8 Z- a5 n8 o! A4 U5 K mode->left_margin = var->left_margin;2 }3 ]* b% A X" s. c" ^1 K
mode->right_margin = var->right_margin; ; D0 D. O0 i6 Q+ w6 U% ] mode->upper_margin = var->upper_margin;- M# _9 \) z4 o/ H% w
mode->lower_margin = var->lower_margin; 1 i1 X, Z5 L9 \ c: p) O/ y mode->sync = var->sync;6 s, D6 B9 ^1 I% W
mode->vmode = var->vmode & FB_VMODE_MASK;2 A8 m: n* Y) Y- v( a6 ^* u
mode->flag = FB_MODE_IS_FROM_VAR;0 A: \# e; h% @( M! ?4 \1 {* v
mode->refresh = 0; + I8 A1 b1 E# Y9 ~! f" I5 o( y8 C 5 a% ^3 d# r. H# | if (!var->pixclock)4 M/ G* e9 e3 ]7 I b+ l
return;0 U& s* t! D% e& S# K
1 m. d2 G+ D6 X6 P: q. t$ h pixclock = PICOS2KHZ(var->pixclock) * 1000;$ n$ u; r* ?, Q* |* N' _ N
& _( i6 }+ s v7 p* L htotal = var->xres + var->right_margin + var->hsync_len + ( D( o0 k9 b8 `3 v! V% z var->left_margin;& p: \; b% E* q3 h# T% v% y. X
vtotal = var->yres + var->lower_margin + var->vsync_len + $ m, x3 O- v* i( b# @4 V- e var->upper_margin;+ x, z* }/ u* h2 q4 v5 P
4 H: |( M% L. V8 e# q
if (var->vmode & FB_VMODE_INTERLACED) : o7 J& P/ @7 N) m6 W" M vtotal /= 2; 3 V: b. g8 ^$ H: C, N if (var->vmode & FB_VMODE_DOUBLE)* D) N* s+ y+ `8 n/ l: B
vtotal *= 2; 9 w8 j) h M# J! K6 `6 G0 {, @6 x, e2 @
hfreq = pixclock/htotal; + I) Q: @. ?7 r C. s* ?% v mode->refresh = hfreq/vtotal; / n' `) r8 U2 i( B, P$ u; w+ W7 K} 2 |* [5 S2 y2 \# S1 n# T/ f5 r) j$ z& z- F! B3 M- H W/ q( G
/**. k' b2 S1 \9 {( H
* fb_add_videomode: adds videomode entry to modelist e4 d5 \# P# N/ r& L
* @mode: videomode to add 1 ~0 _9 _' h3 c% }& ^$ U- v * @head: struct list_head of modelist $ N* u8 m y8 i5 Z */ t, X# x9 F$ d+ h/ @' o8 N
* NOTES:) F [" _1 k" D: L2 B
* Will only add unmatched mode entries V+ K, T! @7 Q */ 9 e/ Y2 [( D! q+ `2 ^4 Kint fb_add_videomode(const struct fb_videomode *mode, struct list_head *head)/ |" i7 X0 e7 M! a
{ - ?0 _9 C& L! F struct list_head *pos;0 z0 f, r% c0 R, a* _( e
struct fb_modelist *modelist;d 6 Z* k5 c5 s* c3 O# y% L' r struct fb_videomode *m;- W+ ]( o: v: G0 ]
int found = 0; $ I0 z2 J5 S$ K; T& l: l. Z3 e /*遍历所有的fb_modelist,查找mode是否存在*/, g) Y% V; W& l* h3 ?4 P* W$ _6 a
list_for_each(pos, head) { / g! I; H6 {8 W modelist = list_entry(pos, struct fb_modelist, list);5 ]0 i' I) z# m9 `. t
m = &modelist->mode; 8 E! |! L2 p7 r& _ if (fb_mode_is_equal(m, mode)) { /*比较两个fb_videomode*/ 4 `" s5 Y, c- n* Y found = 1; /*该fb_videomode已存在*/' G7 \( C3 I: P
break; % E+ F+ ] ?) ]& s; ~* D } 4 ~$ R7 }, D$ R9 Z6 r } # V4 Q0 D! \5 w/ ?+ `/ H+ g if (!found) { /*不存在*/ ) v/ V7 @3 z! g8 J modelist = kmalloc(sizeof(struct fb_modelist), /*分配fb_modelist*/ 8 a: r3 s7 A6 P0 z, w: v# G GFP_KERNEL); , i; o; {. q+ l; f6 b/ E/ ^; o/ Y, W5 Y. j' e1 q: s
if (!modelist) 5 M j6 y9 i2 S* _ return -ENOMEM; ) j4 U0 N8 J4 \+ k" [0 n modelist->mode = *mode; /*保存mode*/ 5 k9 x; K+ r0 I4 e* `! W$ m( w' p list_add(&modelist->list, head);/*添加mode至链表中*/ * Z. B- U3 _5 i# i; H$ T* M }) O3 B( W9 @0 a8 Y9 K7 w. l
return 0;: M6 J1 o6 P- n; R9 X& W% G( w- X
} ( D: f9 L' ^ `$ I9 d+ E: F3 t- v
/** ! N. U7 S8 s) F6 q4 r4 v- S# a * fb_mode_is_equal - compare 2 videomodes2 i# j; i; B- a' ~
* @mode1: first videomode ' f; ]! Q9 V& p * @mode2: second videomode - `* R4 A( e6 j: _) ?3 X * % Y s3 S d; b4 g1 t3 T * RETURNS: + R2 [- g! K9 E * 1 if equal, 0 if not1 u# v6 ?: {; N9 X2 _
*/5 F% c) N6 e; J8 K' v% {- a1 y
int fb_mode_is_equal(const struct fb_videomode *mode1,- i" b0 V0 ^- ]: h% J+ m( T: |
const struct fb_videomode *mode2) 2 D% e/ N6 E9 T& z% {{9 v$ { \5 J2 }1 s8 p ~
return (mode1->xres == mode2->xres && * r$ |! s* X$ ^. W; A; J0 O6 S mode1->yres == mode2->yres &&% n% U5 m0 ` {2 N4 o
mode1->pixclock == mode2->pixclock &&2 V% _: F6 w( ^1 F* A( N
mode1->hsync_len == mode2->hsync_len && ! P; _* S# s* b' ]2 d6 U' B! M mode1->vsync_len == mode2->vsync_len &&. M7 H# h$ E2 q- C p: U$ X' s
mode1->left_margin == mode2->left_margin && & q4 s$ H: H; p, H6 x( T: R0 @7 N mode1->right_margin == mode2->right_margin &&' n" x. G. b0 I% b
mode1->upper_margin == mode2->upper_margin &&& U( ^1 M* ^6 v# O. R
mode1->lower_margin == mode2->lower_margin && : J( a* Q2 T) g# G$ g. {/ K7 N mode1->sync == mode2->sync && % n3 E( w) o1 _ mode1->vmode == mode2->vmode);! a; u7 Q0 `! C$ I4 H
} ' ]; V4 U/ I. _7 [( n" n* \, d2 ~, O* d* g2 i* f" o
! |. E5 m6 e( ]7 l8 Dfb_var_to_videomode函数只是将fb_var_screeninfo结构转换成fb_videomode结构。而fb_add_videomode函数查找是否该fb_videomode已经存在,如果不存在则添加到列表中。 " k: R. f' e- t0 t1 ?- e4 H3.4 字符设备方法 3 V: U J7 b/ {. p J9 d6 g 在看过framebuffer子系统建立和注册过程后,我们看下framebuffer留给用户空间的API是怎样实现的。" d6 v' H( f4 F: m4 I: P
/ R2 t2 M: D" D本小结只分析5个常用的方法,即open,release,read,write和ioctl。9 T+ @9 `! Q) _- |+ \6 h
$ X; e* |6 O1 U) c
因为所有的方法和struct fb_ops定义的方法有紧密的联系,而该结构的定义由驱动程序给出,在这里我们提前看下在驱动中是如何定义的。0 H6 |# {. B4 E
) I/ t8 |5 x, j% estatic int5 S9 W1 Y# |3 h/ D
fb_open(struct inode *inode, struct file *file)2 {2 [ e# R' I( ^
__acquires(&info->lock)# M3 S4 B+ H8 c. x+ X
__releases(&info->lock)3 Z/ a Q7 {+ v; z: w/ i3 u
{$ F, }, D; h1 @1 D2 P3 }' c
int fbidx = iminor(inode); 0 P# j3 Y c/ W1 S1 S struct fb_info *info;6 b: X% ?' v. z# A& J% R; k5 f
int res = 0; ) L1 y, m: i x, a7 c+ O3 t; f2 o0 P) t/ |% a7 K
if (fbidx >= FB_MAX) 3 N+ i& p( L# F return -ENODEV;# \# W! v/ f0 u2 r" I0 ^
info = registered_fb[fbidx]; /*在register_framebuffer函数中已经设置了元素*// O" q+ g. q# p0 z
if (!info) & r' w5 y t( r request_module("fb%d", fbidx); /*加载模块,这里不加载*/ # l4 ?' T/ s1 l+ h info = registered_fb[fbidx]; 0 y8 ~6 E! j( M) `; M& W if (!info) ! R- \- v! c; {2 _3 T& G return -ENODEV; % [& ~6 Z( x( a# F( q8 ?( X2 g/ g mutex_lock(&info->lock); /*加锁互斥体*/% K, k8 _; w0 X4 ]
if (!try_module_get(info->fbops->owner)) { /*增加模块引用计数*/# i; l& P3 Q& h, Y
res = -ENODEV;* X2 `3 q& z& X* i* H+ K% M' f
goto out; ) V# J4 \+ S5 E5 o } ; _0 s0 ^. e, F9 L file->private_data = info; /*保存info*/ / e( r5 ~' h" ]4 |) Q% N" A m if (info->fbops->fb_open) { /*这里fb_open方法为空*/ 8 G4 ]1 U: w. x res = info->fbops->fb_open(info,1); 8 q8 I9 g; v3 i& m- c# j if (res) & z; j! B" j. H* m! ~ module_put(info->fbops->owner);1 q1 V7 \2 ?& g) K- }9 |) e9 X
} 3 ~. L! g6 Q: ?8 I6 ~#ifdef CONFIG_FB_DEFERRED_IO 9 J2 y5 J2 Z& M% B. d5 U" @# H if (info->fbdefio) + t T8 B# G# ^6 y1 ~3 B fb_deferred_io_open(info, inode, file); ( J3 K( Z0 V) m/ X% e) p#endif ! X J: V1 g+ n5 {# M3 S/ E7 lout: * P3 |; |8 }" t0 K3 W) K5 C mutex_unlock(&info->lock); /*解锁互斥体*/ 1 {, M: }7 J" i return res;7 w+ |8 u! T A4 v
} 2 A8 \+ N* c% Q3 N8 y, R& P主要的一个工作就是增加模块引用计数。还有,程序会判断是否fb_open在驱动中给出,如果有则调用该方法。我们已经知道fb_open没有给出。+ s# b2 m2 ?% g, U/ A5 g# d7 F
3.4.2 release方法" \& p, n' f# m
下列代码位于drivers/video/fbmem.c8 u+ f0 l7 A9 E6 m# u
2 B1 ^9 }. w' k' p6 Rstatic int 8 z4 R0 o! V8 X: ?3 {fb_release(struct inode *inode, struct file *file) K( O: ?: K' F) b__acquires(&info->lock) . q! _7 u% ^1 `7 [) K& W! \. s__releases(&info->lock)' Y/ G9 M) ?% q, v
{ 0 t' _; x: u7 }0 B: z struct fb_info * const info = file->private_data;) W1 q r9 w, b3 D" }
5 h$ \' ~, r$ C; F4 l+ F3 m; G5 g
mutex_lock(&info->lock); 4 ?8 e/ x/ V$ b0 b) x if (info->fbops->fb_release) /*这里fb_release为空*/ + g" d. _9 Q$ D9 u info->fbops->fb_release(info,1); * i' q( m2 a% d7 f module_put(info->fbops->owner); /*减少模块引用计数*/ * t! V2 L4 J0 Q5 h+ |' m, X mutex_unlock(&info->lock); 7 e5 A" `$ J3 B! Q: ?5 | return 0;& m7 \: [1 G2 F% k; q
} 9 I6 N0 |+ A4 e, ^和open相反,减少模块引用计数。 ; z3 u, m9 m1 n0 o 6 a; n& @" N9 R* V" o+ F2 j3.4.3 write方法 ' B- \( |6 T2 M 通过调用该方法,LCD将显示画面。- U C1 [ n) d/ @1 y3 @
! W3 B% Y. n) { 下列代码位于drivers/video/fbmem.c 8 b! a0 o& X5 b8 C% i$ w; w " e+ A" o' W7 J$ i) y: ]7 wstatic ssize_t 3 f4 b% J, _( ^0 c# B5 @fb_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos): A" u& [( L0 K' B
{% A" E/ Q# W! C# ]1 w0 D
unsigned long p = *ppos;7 e4 R0 R! D% y0 o* E5 U
struct inode *inode = file->f_path.dentry->d_inode;: K Z3 q, Y* q9 @9 o
int fbidx = iminor(inode);$ \% M! w1 L* q I* W8 T$ B' S4 C
struct fb_info *info = registered_fb[fbidx];# U+ y1 S% D6 Q
u32 *buffer, *src;; v# r9 S' Z8 S7 ~8 g
u32 __iomem *dst;6 s- ]. e& h; @: e& c; |3 q) N
int c, i, cnt = 0, err = 0;* K/ z4 L) ]. l
unsigned long total_size; + `& f S9 w( K: O1 R4 ]5 G( Y# ~! y
if (!info || !info->screen_base) /*screen_base在驱动中给出*/( S) D9 z1 g' w* x x
return -ENODEV;: C% y7 h) L( K5 l; M7 Q
7 M& v" q o; R& \$ J; g: f% l if (info->state != FBINFO_STATE_RUNNING)4 h( C0 N+ ^% O1 b8 j; ]
return -EPERM; ! L( N, h2 [/ _1 h# s9 y . ]; l- y4 t6 q* _: {2 a( R if (info->fbops->fb_write) /*没有fb_write方法*/ + ]% d: j( ~2 B4 P return info->fbops->fb_write(info, buf, count, ppos);/ s4 j9 j" { S- n
8 @( L6 q7 U2 U$ A$ S! K/ F( C total_size = info->screen_size; /*screen_size没有给出*/ 3 J4 ^/ }2 C/ W; q! J/ i) r ; {' ^. A# z6 m1 L' B if (total_size == 0): b; R) Y( B# j" @
total_size = info->fix.smem_len;/*153600字节,驱动probe方法中计算*/, v9 y5 b+ [* e
, D( C9 W4 y: o4 H6 r
if (p > total_size) + ]9 u8 i" l4 E! R1 ?. G% u$ ~ return -EFBIG;; {& B. @9 Y) D5 n- U
0 h j& C& H& B* T+ q' }* b. A6 @ if (count > total_size) { /*要写入的字节数大于153600*/' v1 f! h; c# c8 v2 U& A
err = -EFBIG; /*file too big*/ + W* g. q- [8 \8 K' j: ` count = total_size; ) p6 D0 g4 V; {- A! W4 ^0 e: c }; W4 f K( H0 Q+ _# u" g
/ k' N4 _0 v. u( Q% N+ K3 M$ @; z% S9 Y if (count + p > total_size) {/*偏移量加上字节数超出了缓冲区*/8 W3 O! H+ E- s5 P. o
if (!err)* @- [% T9 {) ^/ U) f
err = -ENOSPC;* b: H! A6 J8 {
' u: b, p! E9 _ count = total_size - p; . \+ k5 G. y6 |& W `' M } " r, D# t! h! N: N" ?2 y. f5 t- h- K5 u6 F" O
/*分配buffer,GFP_KERNEL*/ 2 O4 }4 o* S" q0 [4 R& U buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count,1 ?2 D% Z- @ o: b" x
GFP_KERNEL); / 9 ]* ?3 Q. P! a/ M( G0 P- j" j& t if (!buffer) 5 e9 L+ k) @* `" c n return -ENOMEM; K+ y$ a0 O* A) N8 J: z2 A* u! \- H
! B. h2 {& g9 F) P dst = (u32 __iomem *) (info->screen_base + p);/*修改目的指针*/* M* |) ~1 R- _8 K
, `7 u% m2 Y- q% a if (info->fbops->fb_sync) /*没有定义fb_sync*/, c* S1 m9 R2 ~- H
info->fbops->fb_sync(info); & M% G( l; N! {0 x* [1 v ) f/ [5 C5 d) u+ O* M3 i9 ` while (count) { ( i5 e! E2 F9 k$ M8 W c = (count > PAGE_SIZE) ? PAGE_SIZE : count; , H' w: i; p3 T7 w* C src = buffer;8 S4 D3 d, z6 d5 Z: a
+ L$ F" Q9 Y3 T+ I7 ]( } /*从buf(用户空间)拷贝数据到src中,一开始为1页,最后为count字节*/- e" \0 j- s: ]0 `6 b) y
if (copy_from_user(src, buf, c)) { - b6 ~2 }$ T! E* d
err = -EFAULT; 4 I- ~' z' d; O* R6 Q* A0 i6 F5 v' t break; $ {, F& N2 i. _9 C } ( K- J# j. X) g! H u/ `" | /*一次for循环,写入4个字节数据到dst处*/& }2 L! N! L* R
for (i = c >> 2; i--; ) G6 b' j2 f6 E& g' V$ D fb_writel(*src++, dst++); 2 j9 S0 F7 G$ B5 ^; w0 P" a; g" f /*最后还有3个,2个或者1个字节*/, k5 x9 { F1 C1 K* O; b
if (c & 3) {. F, b* n. z: ^3 J1 X
u8 *src8 = (u8 *) src; # M1 o* D) c! @% g0 v u8 __iomem *dst8 = (u8 __iomem *) dst; $ a( n! q$ q$ ^ /*一次写入一个字节*/5 T4 H( @+ r7 W# r8 z4 _
for (i = c & 3; i--; ) E) `5 D ]+ B2 m. U: h/ X% C
fb_writeb(*src8++, dst8++); 7 V! q' M" |* `( i# `8 X. `( g/ k/ g
dst = (u32 __iomem *) dst8; 7 W" g! U8 }- F) [1 s- K } ( V% Z8 k R" Y# x* b) @9 G- a4 ]& K' v. M, n- B( B, Z; @
*ppos += c; /*用户空间偏移量增加*/& I( n4 ^2 M* W
buf += c; /*用户空间指针增加*/ 4 d0 ]" w5 S O9 } cnt += c; /*修改已发送字节数*/ : {. K, t4 l8 h) P: q% w count -= c; /*减去1页*/ 2 _) z9 q4 n! [2 { } . ^3 M ?* O7 @+ r, U6 Z5 B$ U- C8 |3 d9 b* g' Z4 {6 ]
kfree(buffer); /*释放buffer*/ 0 O) U2 q/ V) a4 K! o1 S4 z5 Z) Z* [& a8 N) O
return (cnt) ? cnt : err; 2 c+ E/ M4 c! P& o} # m H7 P. Q0 [ 5 }: [ d- g* W0 e! P这里,做了一系列的检查之后,开始拷贝数据。这里一个有三个buffer,一个是用户空间提供的buf,一个是在这里新开辟的buffer,还有就是驱动层提供的screen_base。 4 c: R8 W( Y; Z0 P G# I$ C数据流如下:4 m+ c, }- |9 t- f/ O
% [: [5 C4 k- d! S: _ . E5 n1 [5 U3 C$ u: M用户空间的数据首先被复制到buffer中,然后从buffer中复制到screen_base中,最后被映射到LCD上,LCD就显示响应的画面了。 % u( ]7 h" a8 f; Z8 r6 _6 [ b W8 |3 _ R/ s
3.4.4 read方法& F& P8 \8 k; _6 p+ O3 p- @
该方法用于读取屏幕画面的数据。 " M8 z0 y; _7 a1 t0 ~5 C & ~2 x, a% @7 oread和write类似,只是数据流是反响的,就不多做介绍了。 ) z# q9 O e, N8 T$ U F: `5 E & Z% p- L. \, n3 ?" [下列代码位于drivers/video/fbmem.c 2 [0 P+ l, v5 \* i3 V ; h' g/ x, j. N: Rstatic ssize_t 2 a# E3 K: \/ b X+ O& pfb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) & O2 P' j1 i! s+ @{, T" Y, S8 }: r4 M0 G
unsigned long p = *ppos;' B! C [+ t9 [. T5 w* R+ C$ k
struct inode *inode = file->f_path.dentry->d_inode; * h7 d# p* k! m. ?3 X int fbidx = iminor(inode);# G. `; u! Z: ^. B" E, K
struct fb_info *info = registered_fb[fbidx]; + I+ D) c* O w- `! ] u32 *buffer, *dst; & e* \) @% ]: k7 T Q u32 __iomem *src; 8 U0 t' M$ I% k$ u& `* u& ] A int c, i, cnt = 0, err = 0; ' T S" d7 s4 n' L" | unsigned long total_size;* H' b; r1 d$ {% n6 L3 K& w
# s6 U u9 L: J% y9 D if (!info || ! info->screen_base)! m0 G1 O/ r8 L u
return -ENODEV;& f( S4 z, } F% ~4 e8 L
- b3 B) k1 }8 l+ L5 f; u# L
if (info->state != FBINFO_STATE_RUNNING)# }: l6 M9 ~4 |8 W3 R6 h; c7 }
return -EPERM;/ G1 X$ n# ^' [, V, z: N1 m
4 i: F5 d% F3 F) ^! ?/ } if (info->fbops->fb_read) /*没有定义fb_read*/! J% v! w& A( l+ N+ |- _$ ^
return info->fbops->fb_read(info, buf, count, ppos);( H2 L0 a) [! a- i
5 K. C' n) H# E D4 m! R
total_size = info->screen_size; $ w$ x. R: q; O& N; x+ x( A) K5 Z0 g) z( i4 a: Y
if (total_size == 0) + N2 b9 _+ F/ Q9 C# q& _ total_size = info->fix.smem_len; ' p- l4 a5 F( T. o" n" Q, c! L3 I
if (p >= total_size) 5 @* t' `/ K/ G/ ~/ g8 h return 0; 7 _+ \6 ]7 o# b' _, j7 j% L/ S- X# A6 l' n6 f
if (count >= total_size) ( t L3 _& W9 `: v count = total_size; % f, n5 t" W/ S, S" @- d7 V' C; m0 h- X) K$ U& |
if (count + p > total_size) * E( D5 b( g9 j1 A1 \ count = total_size - p;3 Q! D! s9 O- W a- ^
' Y/ r, o; m/ g; }/ C, i) c
buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count,; i; a5 c; G% o o, f8 F9 ^
GFP_KERNEL);8 A4 `% {7 _# _% O. E* i- F; ]: f
if (!buffer)% m: C9 }# k9 D( \ H; ?
return -ENOMEM; 6 f. J% X) b5 K, U/ F9 q( X: _( J! L 7 p) K+ `+ ~% G/ h3 ]' M( r; N src = (u32 __iomem *) (info->screen_base + p); w, g0 x' x: j0 `* r7 p* v
4 d1 V) D- ?, [, M! Y0 {. r1 c' W if (info->fbops->fb_sync) " i* h& i1 a& Q( z info->fbops->fb_sync(info);/*没有定义fb_sync*/, W0 l% P" D' v% x1 K4 n
7 g9 Y& _+ ]$ ?. z w
while (count) { * x, G& h6 z% r6 n; ?6 F c = (count > PAGE_SIZE) ? PAGE_SIZE : count; 5 M" k0 u1 j0 q3 @' i dst = buffer;2 S0 l, w* B% O* d; n
for (i = c >> 2; i--; ) # {9 l$ B$ i$ d; [9 H *dst++ = fb_readl(src++); 9 ~; m8 H3 z$ e9 z if (c & 3) {4 X8 F6 F4 C' J
u8 *dst8 = (u8 *) dst;0 T/ ^, Q, j; c- y- _9 P) L
u8 __iomem *src8 = (u8 __iomem *) src; 7 i+ O5 U* d$ L* A: g) Y- R+ [ H8 C
for (i = c & 3; i--;)# n! w7 e3 G' @- H6 b
*dst8++ = fb_readb(src8++); . M R3 E8 m) L4 {: F7 `/ v O: m& @* o, M
src = (u32 __iomem *) src8; : M1 [9 O4 H- h. t& u( I( U7 ` }$ U& k+ D' z! f# o- [( H6 N
0 N4 [7 l, ]7 B3 C" t
if (copy_to_user(buf, buffer, c)) {3 W2 g5 M; w4 P# j6 r
err = -EFAULT;0 N# J6 J1 |% F- H2 `* ?
break; 3 Z$ Y1 G& o$ B' G } $ P0 k7 Y: l& _3 x& S$ N5 ?, w *ppos += c;+ x. e6 i1 p- u! f8 {
buf += c; 8 |) v+ }: j$ G cnt += c; 0 B2 N, |: L, {% V/ V w count -= c;- k" Z9 L! U# S! V
}! s2 K: z. f* Z z: {& t& U; }
5 J3 T V# P# [. W
kfree(buffer);* z/ [7 q5 k; @7 ^0 j$ @
, L, {, j+ W& a3 J
return (err) ? err : cnt; 3 j7 ?4 f' w% V' k3 X}9 q P' y# g& n3 |8 m# C6 Q
3.4.5 ioctl方法$ ?2 k, Z j3 W
这里只是简单的看下ioctl方法,这个函数调用很多其他的函数,详细的请自己看吧。 3 ~! _1 |& }6 r- m% Z , f' r5 }1 |7 R7 V# Y7 o% I下列代码位于drivers/video/fbmem.c! P" G0 T, G* O
" k3 U+ Y: I+ b+ s( P: astatic long fb_ioctl(struct file *file, unsigned int cmd, unsigned long arg)- B! a9 O3 H0 ~) j5 X
{ ( X, T9 G. I2 K8 @3 L# f4 ^ /*获取inode,再获取对应的fb_info*/& ^2 v8 F7 ~3 W
struct inode *inode = file->f_path.dentry->d_inode;1 @4 v }+ u* F( k" _$ N1 n8 p
int fbidx = iminor(inode); + o0 ]' N5 N. O7 _% E struct fb_info *info = registered_fb[fbidx]; & H$ s' z; i+ [: p5 Y C, c/ u $ P5 |4 A: Y- M7 Q, h) m return do_fb_ioctl(info, cmd, arg); ) c# j* |6 x" _+ ~}$ R/ u* g. n5 A7 e- T' Q
) n1 ^0 n) ~! ostatic long do_fb_ioctl(struct fb_info *info, unsigned int cmd,2 w- n+ n& A0 D6 n' b% S8 Y
unsigned long arg) & L3 l% F2 ^$ A+ {# \{# S1 H8 g1 W; r* Y+ Z
struct fb_ops *fb;2 N6 u8 c+ n! O9 L7 v3 i* E+ E U
struct fb_var_screeninfo var; 4 E4 ?0 J$ S `- C+ k struct fb_fix_screeninfo fix; 6 h) u! g0 D: v5 b# C: ]6 m& _ struct fb_con2fbmap con2fb; ! ^# g; c' k ]' B- G* M( V struct fb_cmap cmap_from; / o8 k& U* @7 k* ^0 k struct fb_cmap_user cmap; & s; G; a% T; d# M2 Y1 R$ j( m/ w' d struct fb_event event; / {5 ^$ j3 {% w8 X6 P' V void __user *argp = (void __user *)arg;. v+ O4 {* A# \9 }. ?2 u
long ret = 0; 8 T. N+ M! @# B, ?; ~/ Q& X% |8 |3 n" _8 L7 f
switch (cmd) {: t) }8 P6 ^/ c) y5 P) z
/*获取fb_var_screeninfo*/ 3 H C$ e: V) I0 L X4 r! A4 Q case FBIOGET_VSCREENINFO: & K8 F# I# _8 Y9 E
if (!lock_fb_info(info)) /*加锁互斥体info->lock*/+ q: y/ _# ^+ r1 N7 f
return -ENODEV;: W0 Y9 C% y3 u0 r
var = info->var; /*复制var*/ + |+ ^ d, L1 }* q7 r1 G8 l unlock_fb_info(info); : U; t; i8 R! b # _: b- E# _1 m% r# e ret = copy_to_user(argp, &var, sizeof(var)) ? -EFAULT : 0; /*复制var到用户空间*/8 B4 V* Q, n% R# d8 c! f' E
break; + h; _% [% F5 B* ?8 D( u' Q/ e2 B /*设置fb_var_screeninfo*/3 C2 v) g2 e; J/ B2 I, h
case FBIOPUT_VSCREENINFO:& r0 s* L( C6 u7 H% E
if (copy_from_user(&var, argp, sizeof(var))) /*从用户空间获取var*/ - T+ Q/ u& L7 O return -EFAULT;- z5 \$ | ~0 F) o
if (!lock_fb_info(info)), V) S6 Q! ?/ C& \' g
return -ENODEV;! u3 _" X$ ?, q! g: V
acquire_console_sem(); 1 `$ ^" }' Z- i# O* |: w info->flags |= FBINFO_MISC_USEREVENT;, {$ ^1 ~6 O6 E4 W0 X
ret = fb_set_var(info, &var); /*设置var*/ / ?! }8 i" n- H7 n% ?8 B$ o [# O info->flags &= ~FBINFO_MISC_USEREVENT; - D j. {& A1 _, B/ T/ y2 X release_console_sem();+ |% @$ _- o5 h3 ~& U9 t
unlock_fb_info(info);) M0 F: M" c [7 o- {
if (!ret && copy_to_user(argp, &var, sizeof(var))) : U% [7 h$ ^8 Z$ Z3 T& U$ x- f) K ret = -EFAULT; b( K2 G; b- f7 m$ e. k! l1 ]
break; J+ W2 L# z( _ /*获取fb_fix_screeninfo*/ /*fix为不可改变信息,只能获取,不能设置*/" O, m7 z+ t- I- P$ v
case FBIOGET_FSCREENINFO: * j8 y G0 O) U: B if (!lock_fb_info(info)) |: S; }- b2 G$ e% m
return -ENODEV;% F% x2 p I+ i' a$ {
fix = info->fix; ! }- S2 N! q C/ ^ unlock_fb_info(info);1 j" }" ^/ W8 j1 X! `
( a: o2 q2 M- P1 \1 O6 o" |( x" T
ret = copy_to_user(argp, &fix, sizeof(fix)) ? -EFAULT : 0;" ]4 O" |2 y U
break;5 N. _6 P8 f7 T' @# {: [% @
/*设置fb_cmap*/ 0 ~. d8 E9 n7 K4 T( N I. N case FBIOPUTCMAP: . ?- y3 K5 e" C+ q6 k4 E if (copy_from_user(&cmap, argp, sizeof(cmap))) 4 M( k w# x' E8 y& E return -EFAULT; 5 k" o4 V) U! ?, e/ z5 O ret = fb_set_user_cmap(&cmap, info); /*设置fb_cmap*/3 d2 m; d% P$ }- T% t, `* v& V
break;- s( t2 \( I5 i3 k* E/ j
/*获取fb_cmap*/ 7 [# x) v' p. o, f
case FBIOGETCMAP:& J9 N: I# V( h
if (copy_from_user(&cmap, argp, sizeof(cmap)))- z! _% j5 Z7 ]' f% s: L8 e7 C
return -EFAULT;; B/ c z0 G0 o, m- H
if (!lock_fb_info(info)) ( p/ N4 T* p6 \6 [ w8 z& P( T1 @4 [ return -ENODEV; 1 x' D; K5 U* T- L+ @! b cmap_from = info->cmap; 6 Q! A% y' ] P ?. B8 v; H unlock_fb_info(info); 7 c! M+ i9 M, S% q ret = fb_cmap_to_user(&cmap_from, &cmap);/*获取fb_cmp*/$ T' `" S' w; s ], N- z7 K; c H
break;5 D5 `. J, q+ I
case FBIOPAN_DISPLAY: : e. i# [5 e1 i, u3 N0 G) F if (copy_from_user(&var, argp, sizeof(var))) & h( ?- Z" k5 e% A: H& Q return -EFAULT; e3 z, D9 [7 f( v8 ` if (!lock_fb_info(info)) p: u- K% r. x' |! j( z4 h% r return -ENODEV; + n) o/ v" M7 G1 Z0 c# G" R acquire_console_sem();0 x+ c5 |6 s* \, e! w
ret = fb_pan_display(info, &var);+ x& b% B4 O6 j
release_console_sem(); ?7 b* G- n6 N
unlock_fb_info(info);6 `+ y# e* m4 f& e7 Y$ R, M
if (ret == 0 && copy_to_user(argp, &var, sizeof(var))) y8 @# Z4 J5 G* H% |- @1 p6 M( }
return -EFAULT; % E. `3 Y9 G& B5 o# a; E break;/ g, j$ x7 w& ]: J3 I1 l5 \" I. o/ z
case FBIO_CURSOR:' R7 G; z) i: n9 V+ J+ ]" b
ret = -EINVAL;! M5 R, |4 s( p# q' O4 E/ @
break;; @* B; w" k- M# ~ b( f
case FBIOGET_CON2FBMAP:( o% a- J7 p1 ?: D- F5 \% D: c
if (copy_from_user(&con2fb, argp, sizeof(con2fb)))% F. Z: I4 x$ Q0 o6 y
return -EFAULT;: _ {7 \# G H# c# `5 f2 {- d) Q/ j
if (con2fb.console < 1 || con2fb.console > MAX_NR_CONSOLES)+ f/ w1 j) O2 }% N6 y
return -EINVAL; 1 n5 F7 s; X8 I+ f f con2fb.framebuffer = -1; 8 M0 t; V" t8 Q, g | event.data = &con2fb; . z- @% ?1 w8 D8 r3 n' O if (!lock_fb_info(info))' ? N9 ~, r9 K( b' Q
return -ENODEV; " m- I1 j3 F* U. b8 s; H2 n event.info = info;7 W! }/ p u4 e5 S$ }) c
fb_notifier_call_chain(FB_EVENT_GET_CONSOLE_MAP, &event);* l$ q0 U6 `8 Q+ l# I* R( m) f6 @
unlock_fb_info(info);& L& v5 y' Q L4 O0 k" i! i. [! u
ret = copy_to_user(argp, &con2fb, sizeof(con2fb)) ? -EFAULT : 0; " _4 c8 }% W9 r) ~6 d4 I break; ; f" w7 Q* }6 |& _" u) [ case FBIOPUT_CON2FBMAP:; F/ F. Z/ H4 j/ u" S
if (copy_from_user(&con2fb, argp, sizeof(con2fb)))% t5 u" ] z% |, Q5 R3 i# I
return -EFAULT;" B1 e3 o( A: V" Z0 q7 K: ~
if (con2fb.console < 1 || con2fb.console > MAX_NR_CONSOLES) 7 f; {/ I$ }' ]' A: {! i return -EINVAL;5 B C, H2 m9 T% K, z, W+ G# D
if (con2fb.framebuffer < 0 || con2fb.framebuffer >= FB_MAX) 1 M) o6 N4 k& L0 v9 f7 O% R, K return -EINVAL;$ k8 ~, R& k3 m4 ~
if (!registered_fb[con2fb.framebuffer])1 }; [0 s5 p8 x+ U& W
request_module("fb%d", con2fb.framebuffer); . q& p/ h- ]7 U# U. c; k if (!registered_fb[con2fb.framebuffer]) { 0 c0 n- q2 m/ l+ m, }+ w3 a Y ret = -EINVAL;+ i& o% p6 b- L5 v
break; , b0 Y* a9 r. m: G% L- f }5 G& P% J1 R3 G h4 f6 f4 x
event.data = &con2fb;0 M! f/ b1 F' M q7 t/ T: Z) J
if (!lock_fb_info(info))4 c# K- ^0 O( J$ ]! s
return -ENODEV; 9 ]: _+ E, s. a7 q5 [' ? event.info = info;+ B8 @6 y, V8 [+ l4 C! k' V: t
ret = fb_notifier_call_chain(FB_EVENT_SET_CONSOLE_MAP, &event);, }! a9 a; k4 ~# }: X
unlock_fb_info(info);! R5 T) n) T1 @0 J
break; & z% X+ F; |. [; r; u/ M case FBIOBLANK: & y8 l: m* G0 T. B if (!lock_fb_info(info)) 8 y# y ]+ x! F6 ^ return -ENODEV; ! e' P* J' t0 i2 x# M; t acquire_console_sem();! F$ P }. K8 v1 \0 V
info->flags |= FBINFO_MISC_USEREVENT; 7 S- J5 t9 _: \ ret = fb_blank(info, arg); ?*最后调用驱动提供的s3c2410fb_blank*/1 B) @5 v. ` y* c7 I' U+ A2 {& s
info->flags &= ~FBINFO_MISC_USEREVENT; 9 Z, g8 F% O6 s- X- |( I: t% C release_console_sem(); . t, ]% h( h7 R) v0 D unlock_fb_info(info); 0 X' e; L4 m5 ?1 U) ` break; - t' _0 _9 I1 Y5 h default:( r( e% i8 |. ]3 R% M/ Q% I% M- ?
if (!lock_fb_info(info)) * b/ ]9 L1 {( ?4 _; p( r# I return -ENODEV; ! \) E2 a' F3 `% q fb = info->fbops;/ A& _0 H: }# k4 M- p; J- F
if (fb->fb_ioctl) /*fb_ioctl为空*/ ( ^/ _, W% p8 B$ b. @ ret = fb->fb_ioctl(info, cmd, arg);! h1 U" y% Q; L$ ^' p3 K- q+ ~
else4 u; o8 s% c: l! R
ret = -ENOTTY;/ N" c3 y: ]8 L+ @7 W, U
unlock_fb_info(info);9 x, S# N! e; a+ s* [
}8 G# ~. i& N* ]4 m3 H: z- ]
return ret;5 w9 {; G" f, s( E/ H9 R
} 0 A# p2 T( y: w( k! z4 @- j正如2.2小结所说的,fb_fix_screeninfo只能获取不能设置,因此,ioctl只提供了获取fb_fix_screeninfo的方法,而没有提供设置fb_fix_screeninfo的方法。7 a, G7 P, |) j0 ~
6 g: a; E' h4 I4 _# ], S+ u
3.5 小结 & N5 Z( ?* F; o2 S. b6 a9 { 本节对frambuffer的核心层进行了介绍。包括frambuffer子系统的创建,frambuffer的注册和提供给用户空间的5个API函数。下面开始介绍驱动层。: t* V: b5 W6 P4 ?
8 X$ @! L9 m0 X7 S4 q3 [! k
4. 驱动层 4 y# F) C/ y4 W$ f% \/ J本节将开始介绍S3C2440的frambuffer驱动,该驱动源码位于drivers/video/s3c2410fb.c9 Z9 J- e8 i: H I, Z, Z
! |! n# f# Q( n7 u- K, U+ X% r
首先来看下驱动模块的初始化和清除函数。 6 `, B0 c1 g' I1 ^7 o6 y) ?% F9 g) i3 Z( l/ [% @1 P* k
4.1 s3c2410fb_init和s3c2410fb_cleanup + }& E9 |# O: N9 kstatic struct platform_driver s3c2410fb_driver = { ! E$ z% }) P3 F$ a% D& [ .probe = s3c2410fb_probe, ( R( S9 a3 @4 x1 F1 u .remove = s3c2410fb_remove, & H4 j% Z% E" _7 G: G .suspend = s3c2410fb_suspend,9 q6 i5 K6 |4 X) P$ W) v) i
.resume = s3c2410fb_resume,( o! F/ p4 N* `: H
.driver = {! d) e3 B: ?# R3 N9 Y/ y
.name = "s3c2410-lcd",) P0 z. I; ^. v2 V1 F
.owner = THIS_MODULE, 3 ~6 G6 W1 x2 L7 q }, G, E: ~8 q: P# h4 H* c
};4 z( P. L& j5 x5 D2 I( H
$ { m- g* j0 ]0 g
int __init s3c2410fb_init(void) . t6 ^9 y4 U8 ]! P- R4 B{3 N( S9 d1 {& ]7 \% n
int ret = platform_driver_register(&s3c2410fb_driver);: }; d; ~1 u6 E4 x x
9 C) V$ y+ s; j* V3 P
if (ret == 0)8 V @: S5 @, t; G: _1 `6 h7 i
ret = platform_driver_register(&s3c2412fb_driver);; ; L: W/ s/ E& { a8 Z. `7 |2 e2 T( q# E* w1 T( N4 q# o9 a
return ret;0 ?& } p Z* P! U* A
} 0 u+ i3 _. U* W1 N- ?. k, H! F ! O3 N' z3 G. Z1 X+ _9 Estatic void __exit s3c2410fb_cleanup(void) $ U3 a; N# Y$ w( a# S{8 B$ P0 b2 a+ D$ i! |- X6 F
platform_driver_unregister(&s3c2410fb_driver); 0 u- `! {, @" G; L( f' } R# `# w4 L platform_driver_unregister(&s3c2412fb_driver); 5 p, x9 b! _7 o$ S. q) k, h! E}! T& p5 @, e7 M
+ j; B7 d8 [! |" Q" Rmodule_init(s3c2410fb_init); # S# l: c/ A! C$ ^0 n2 ]module_exit(s3c2410fb_cleanup);2 m# H3 K: E4 D7 ?, K
3 T# k5 {, {- c- M/ X. x" G
当platform_driver_register调用的最后会调用probe方法。接下来就来看看probe方法。 F5 x' z0 r& V5 L4 U' Y: H2 v+ G
4.2 probe方法 6 o8 j; c1 v w1 R4 ^+ X6 @struct s3c2410fb_info {9 i! _/ e4 |# Z3 {5 F8 j
struct device *dev; 6 p# [# [: E/ Z L4 I struct clk *clk; . @+ ^9 ^& h. i* T* b4 C1 z6 x( T8 p2 t E$ i% d
struct resource *mem; 5 I7 K; Y9 ?' U/ s) U void __iomem *io; /*虚拟地址*/ 0 V2 Y6 ~2 V& ~7 ~+ A8 b void __iomem *irq_base; ; H: D7 L) z+ d) M Z" d- g0 ]) z' o
enum s3c_drv_type drv_type; " J, E# x0 Z7 y- `! r struct s3c2410fb_hw regs; / ^% l7 o- R+ K5 S8 ^, U4 l/ B F! c, j
unsigned int palette_ready;3 v2 }# O/ W, o5 o2 l1 T/ e5 Y& e
# l) w/ E2 h% n
/* keep these registers in case we need to re-write palette */ " x) C" C; t* o/ l3 e+ A" I4 Y u32 palette_buffer[256]; / ~) \& b1 L1 d( ? u32 pseudo_pal[16];0 ]1 k- v1 ?1 Z' P) v; g
};1 B( U9 p. t7 n7 k3 D
# `9 Y8 u6 {! S0 ^struct s3c2410fb_mach_info {6 B- v! h* T' r% R& N K$ q: h
2 p( |5 w5 D3 }, p" R
struct s3c2410fb_display *displays; /* attached diplays info */$ h) I l9 [: W& R# g1 n, r
unsigned num_displays; /* number of defined displays */ 3 e5 K1 U6 Q# s" D4 B N unsigned default_display;9 O" Y, h% x6 U* e) U' z
& c" t+ Y! Q8 e q* ~& ~ U
/* GPIOs */; C/ M$ f/ O. P0 F- G, Q
" O6 o) O; g- b/ R4 E unsigned long gpcup;# w/ ]6 P- A$ Y, g
unsigned long gpcup_mask; p; ` H2 v" F$ p! Y
unsigned long gpccon;# U$ X H# Q% h, J
unsigned long gpccon_mask; 1 }4 Z! C* l8 k: S" v5 i% [ unsigned long gpdup; 5 l1 d4 u/ o/ i& O% e unsigned long gpdup_mask;2 }# J$ ^! U- D0 X
unsigned long gpdcon; 8 N7 m2 V* P3 ` unsigned long gpdcon_mask;- M, T8 b* \" z; ?7 R
f3 e: C5 ~+ k
/* lpc3600 control register */6 c( S1 Y6 A7 y6 `
unsigned long lpcsel;/ a/ x( y2 g6 C. h M: O1 N# D
};$ V/ u/ g9 F+ B: n$ G- |
% C7 |( j v& h, [
/* LCD description */: z# c8 w& t5 {0 J6 f( j, {; T
struct s3c2410fb_display {, ` V! g9 X7 Y
/* LCD type */ ) Z5 y5 M7 ]4 O# @2 C. e6 B unsigned type; & y9 @( ^4 R' K D3 G0 C$ k8 ^+ u' t% l( R
/* Screen size */6 {, f$ @/ O: K
unsigned short width; * M# [/ u! Q( I9 S7 g unsigned short height;' X& D: g }6 F% z* X
. x3 l( \9 M- `$ h$ s" j& }$ H: ? /* Screen info */ 8 u2 |2 O+ e) ]. @0 I2 q3 [ unsigned short xres; . g6 Y/ [# b- g) m3 R, N0 } unsigned short yres; ' s: e) W( D+ B6 k# e unsigned short bpp;$ j8 J' b+ K. o0 A( z
& a* M+ f5 s3 H. S" i! A! \+ Q unsigned pixclock; /* pixclock in picoseconds */- B" ]$ ^7 @0 Z c+ i
unsigned short left_margin; /* value in pixels (TFT) or HCLKs (STN) */2 T5 j. e; S+ m. e) ? }
unsigned short right_margin; /* value in pixels (TFT) or HCLKs (STN) */) B7 @" `! {" G$ q: c" g
unsigned short hsync_len; /* value in pixels (TFT) or HCLKs (STN) */ + k' i* ? G! k7 B" \, M# o, t unsigned short upper_margin; /* value in lines (TFT) or 0 (STN) */3 f+ t$ [, M3 d* \1 G5 L
unsigned short lower_margin; /* value in lines (TFT) or 0 (STN) */ 6 H6 U6 o" Q; ^0 i. {; Q* M1 { unsigned short vsync_len; /* value in lines (TFT) or 0 (STN) */ O9 N( A! b W ! E0 b" L. _( Q /* lcd configuration registers */7 Y2 c% f+ m# ~: E4 U U
unsigned long lcdcon5; ' U& `! B6 J- q};7 T- X: N% G; L7 ^
# ?& Q. u2 o. p4 I/ B0 v% T2 ?) Vstatic int __init s3c24xxfb_probe(struct platform_device *pdev,8 K0 E) }; i g* H
enum s3c_drv_type drv_type) / g S) b4 X+ K6 Z1 `$ g, E7 j# i+ R$ o{2 d7 ]3 U$ O* z4 E
struct s3c2410fb_info *info; . J/ M& w7 M5 w0 ~. b& u- o4 O( _ x struct s3c2410fb_display *display;2 u: O1 t0 p L& j0 \
struct fb_info *fbinfo; 4 ], V9 k" a* y; |7 _' a struct s3c2410fb_mach_info *mach_info; - S0 o( [. Y$ `1 r2 k7 l struct resource *res;" ~* T9 b4 r9 T; B- j7 }5 F
int ret;- \# d. r8 r6 W% [
int irq; ( \1 x2 A, m8 v3 K& O int i; 2 ~, M. n% d t. q( p! F" H int size; " C0 S: U; {$ Y( Q+ h u32 lcdcon1;) D ?9 }4 n7 y+ w) X
/*dev.platform_data由函数s3c24xx_fb_set_platdata(mach-smdk2410.c)设置,指向s3c2410fb_mach_info*/! K; z( b9 a# X% K2 T) x
mach_info = pdev->dev.platform_data; ! `" H, N# Y2 l% b) G
if (mach_info == NULL) { Z) Y0 b5 Y3 }5 }: H dev_err(&pdev->dev, 5 z7 E* b0 \5 [ "no platform data for lcd, cannot attach\n");5 M+ X' {% U- K- _
return -EINVAL; 0 l0 G( u) h/ l* `$ m2 |' D7 m/ W } , O! i( w- V2 u; p( U F /*在mach-smdk2440.c中,default_display=0, num_displays=1*/+ i7 q7 I/ C2 K$ y# |
if (mach_info->default_display >= mach_info->num_displays) { 1 M. c2 I5 l! ?$ p) r
dev_err(&pdev->dev, "default is %d but only %d displays\n", ! T. L+ [5 f& F' V y3 [* V7 | mach_info->default_display, mach_info->num_displays); # H* r+ f% l9 A1 J. O return -EINVAL;5 d5 P* a4 S q) s& l8 d/ H* b' c
} ! j! `1 a) `% F$ h 7 b4 m' t8 ^/ k: L. U* }4 h display = mach_info->displays + mach_info->default_display;2 n- j1 m# I2 g/ _
0 Z! W9 }8 X- ^* K8 q0 f7 D irq = platform_get_irq(pdev, 0); /*获取IRQ号,16号中断*/ " l; _+ E; P5 j3 L" B if (irq < 0) {3 e9 M5 q' a, \* B8 R
dev_err(&pdev->dev, "no irq for device\n");# J8 V% ^9 ]$ g% m
return -ENOENT;# p6 D4 I6 {" n' E2 H! z+ C7 _
}! j" P+ u1 P! s; }9 E
/*分配struct fb_info 其中包括sizeof字节的私有数据区*/ 0 q( C7 q" B+ R! j# h3 B% ` fbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info), &pdev->dev);* K3 s0 Z5 V# l; L, D* e( _
if (!fbinfo)( x6 E" F% c" G& ~) l2 m
return -ENOMEM; $ c) {: @2 @- T0 L' B' q$ J) f % v$ k; B6 q4 R1 w6 D platform_set_drvdata(pdev, fbinfo); /*让platform_device->dev.driver_data指向struct fb_info*/ / B: c) t, T8 x1 x , g; p6 f, z7 y8 G2 Z9 g" v* f info = fbinfo->par; /*par指向s3c2410fb_info*/ 9 O* P- a9 j* I. r' c9 C' E info->dev = &pdev->dev;/ y# O P3 ]! N- k
info->drv_type = drv_type;* J: c9 m) @% _! L( }/ n7 N
9 @4 f1 \. I- n) v2 H: k7 H res = platform_get_resource(pdev, IORESOURCE_MEM, 0);/*获取平台资源*/. B" ~% ]( B3 l5 c. q; q
if (res == NULL) { & s+ w1 W* y1 D( F dev_err(&pdev->dev, "failed to get memory registers\n"); 9 M& [% D& A# Y" ^ ret = -ENXIO;7 A0 F8 m. R/ l
goto dealloc_fb;9 \2 v" p5 E w' \2 I6 q1 n1 p- ?! @
}8 C( R8 r4 W( z" E b2 [4 x% A
* H/ ?0 k0 u/ y$ _/ ^$ h; o size = (res->end - res->start) + 1; /*IO内存申请*/ 0 z% h* V: L" _" g2 o7 ~" o info->mem = request_mem_region(res->start, size, pdev->name); # i6 ?6 T; G9 m6 G) F
if (info->mem == NULL) { 5 y; o4 g* F c# f% X9 L dev_err(&pdev->dev, "failed to get memory region\n"); ( k" L4 T2 M' b4 t3 a8 j" b9 f ret = -ENOENT;& h B7 X; C6 m! `( d0 S8 B
goto dealloc_fb;8 H( a4 \1 w4 W* j. J6 ?2 ~
} 7 N+ C5 J2 |+ r1 E: l + x, @" T; d. J7 E. G" q info->io = ioremap(res->start, size); /*IO内存映射,获取lcd第一个寄存器的映射地址*/ 3 E! Y4 d; ?) ~
if (info->io == NULL) {5 t" A z. _" T0 U7 X1 ]5 c
dev_err(&pdev->dev, "ioremap() of registers failed\n"); & w7 i6 G- Z8 B2 q/ I ret = -ENXIO; 1 ^: z% p+ H V3 E# X# ]/ V: q
goto release_mem;7 i0 a& x; k. R& r$ q% o
}! H5 `0 D7 ^% b& c' j% M8 f$ s
/*irq_base对应的物理地址是0X4D00 0054(寄存器LCDINTPND)*/; W* w8 @ z+ e+ G
info->irq_base = info->io + ((drv_type == DRV_S3C2412) ? S3C2412_LCDINTBASE : S3C2410_LCDINTBASE);; V. j' A6 J8 n7 F0 @- S
$ W6 @* D! Y9 f) Y+ o2 [
dprintk("devinit\n");9 l4 `8 b, D, V- y4 g m
{- e6 Q: w. e4 H% G" T strcpy(fbinfo->fix.id, driver_name); /*复制名字*/ , s4 J& Z6 k& N, m% k* Z% A& V/ h1 S4 H3 v# `; x& Q
/* Stop the video */3 w. B/ _' k6 M- s. V2 d* V8 a
lcdcon1 = readl(info->io + S3C2410_LCDCON1);& x/ ~6 @& ?# I
writel(lcdcon1 & ~S3C2410_LCDCON1_ENVID, info->io + S3C2410_LCDCON1); /*禁止LCD*/ ! X7 A5 j5 G `% s2 I; z, c- M, `1 Q2 r( F! k
fbinfo->fix.type = FB_TYPE_PACKED_PIXELS; 6 ?' A4 l1 T$ ?5 Y) L! V& a3 i# i fbinfo->fix.type_aux = 0; 5 M* e; I& d+ X! N9 O! u& Z fbinfo->fix.xpanstep = 0;. A/ F* ?1 n1 i W& p- R$ y
fbinfo->fix.ypanstep = 0;+ P" |( Y8 h* J/ h" ~: R8 f
fbinfo->fix.ywrapstep = 0; 2 P& Q) @% O5 v" z fbinfo->fix.accel = FB_ACCEL_NONE; /* no hardware accelerator */ & n% ~& O: I0 R" f# D3 c* P0 X. | % V$ k# Q# w5 _ fbinfo->var.nonstd = 0;7 }; ]7 J* }* `3 f- T8 h! u
fbinfo->var.activate = FB_ACTIVATE_NOW; 1 ^$ g5 i! V: D* @7 w p fbinfo->var.accel_flags = 0; `" E. Z3 O1 F4 w
fbinfo->var.vmode = FB_VMODE_NONINTERLACED;# ^& T5 L2 L0 s0 M) f/ S
' u, @0 a! F1 S* | fbinfo->fbops = &s3c2410fb_ops;0 W; L1 Z2 L7 ? o* o$ y' w
fbinfo->flags = FBINFO_FLAG_DEFAULT; ( }* ?' l6 T# R fbinfo->pseudo_palette = &info->pseudo_pal;- ~" a8 {: k5 A& c4 H
7 T' X- I7 |( b/ w( M, h. ? for (i = 0; i < 256; i++)$ C! ?, n' \3 K8 ]
info->palette_buffer = PALETTE_BUFF_CLEAR;* L. P8 W! \& A
9 u! V& Y+ c( d' C ret = request_irq(irq, s3c2410fb_irq, IRQF_DISABLED, pdev->name, info); /*申请IRQ,快速中断*/' W. b% q; C# J0 c% u
if (ret) {/ q$ ^$ @; t1 w y, N' U ]
dev_err(&pdev->dev, "cannot get irq %d - err %d\n", irq, ret); 3 j0 [. i' `5 v8 Z' i5 G; o ret = -EBUSY;. a* j6 @0 O) E- Q
goto release_regs;1 f9 ~1 h( T5 G2 e0 |/ g4 \: l
}+ s. A0 Q2 |3 g1 i/ n0 H2 M7 f- n' R
+ s" n) v; }8 o2 q7 a
info->clk = clk_get(NULL, "lcd"); /*获取时钟信息*/ # B' S4 g1 L+ Y0 {& q/ C6 A8 ~ if (!info->clk || IS_ERR(info->clk)) {7 O9 I/ ]! e9 w! h- n* Q: v' R
printk(KERN_ERR "failed to get lcd clock source\n");4 {5 B {/ ^7 y/ M1 R, m) C `
ret = -ENOENT;6 a& x& K- Y. @/ o- c, o
goto release_irq; 2 e* B% D- n" _9 S } H# g. J; ^ r" {4 P+ T+ k 6 w- x Q! H4 @. L' ^. {4 L' W clk_enable(info->clk); /*使能时钟*/7 J8 k8 P+ h6 D
dprintk("got and enabled clock\n");5 ]; X8 t( b* k
) C: U+ w* M0 e, N5 A* f msleep(1);7 _7 O7 J8 w3 _& N
; z2 r: O6 x9 ?& j. y) b1 @ /* find maximum required memory size for display */ 5 f* c0 l7 g) b7 _ /*在多个屏幕中,找出需要的最大memory*/0 p' a/ E7 H* j2 n# \( G
for (i = 0; i < mach_info->num_displays; i++) { 7 i, z t9 L% L9 D unsigned long smem_len = mach_info->displays.xres;- Y0 t3 N+ u7 `6 J' Y: ]+ D
/*所需的memory空间 = xres * yres * bpp / 8*/ 7 F3 h. R r7 l5 r$ U+ Q smem_len *= mach_info->displays.yres;7 v3 W! q5 T6 x. R! e7 @7 D8 v
smem_len *= mach_info->displays.bpp;. q- \: n3 [" H
smem_len >>= 3; $ C* A4 H0 N1 N( W if (fbinfo->fix.smem_len < smem_len)2 {! Q% M5 v( G+ a# J
fbinfo->fix.smem_len = smem_len;6 B) z. Y& N" w& \' | T. `4 e
} 6 d" t$ R& W$ ]+ Y7 ? 4 l F& p7 h ]0 @ /* Initialize video memory */ /*根据上面fix.smem_len的大小,获取DMA映射内存,一致性映射方式*/% f: ^3 u" T1 {( N% P, u
ret = s3c2410fb_map_video_memory(fbinfo);9 y+ |! K& z4 C
if (ret) {4 B, q* s- q q: u6 F
printk(KERN_ERR "Failed to allocate video RAM: %d\n", ret);- B' C6 C$ A2 Y# o
ret = -ENOMEM; ! h) i1 U5 W/ F; ^8 t goto release_clock;4 t! j) g; \% W& M6 i1 h/ R k
} 4 M: W% E* a9 \2 e$ x/ Y# G: S/ e; }6 \& o5 R. _8 Z; M7 ]; Y
dprintk("got video memory\n"); 1 t. z3 y+ s3 y- H/ L' c# P; F \9 z/ i- K4 a8 N# u
fbinfo->var.xres = display->xres; /*320*/ 6 w% r+ k* q w8 z7 m fbinfo->var.yres = display->yres; /*240*/ $ b8 }5 A( b4 R/ \! Q fbinfo->var.bits_per_pixel = display->bpp; /*16*/ 2 @" O. b( ~" I) c' r3 c) k, Y+ Y: ~( Y) W8 x% V$ B; z. ]
s3c2410fb_init_registers(fbinfo); /*LCD寄存器初始化*/ 5 I' A U& Z, b# i0 T. z! Q4 E0 z) t 3 e/ c0 `. ^) n; H: |4 S3 m s3c2410fb_check_var(&fbinfo->var, fbinfo);" k; D3 b: {) |( b1 p8 w
3 r7 v# l9 _5 d$ n' E8 X/ P4 A3 F ret = register_framebuffer(fbinfo); /*注册framebuffer*/ 2 z* t6 l2 Y( P% E9 O0 g1 b if (ret < 0) {% } Z2 y5 G0 L( ?; K' V& A
printk(KERN_ERR "Failed to register framebuffer device: %d\n",ret); - u% d7 `: x8 o# z goto free_video_memory;! n# B( J7 d4 P: E& y/ D
} / i* c- }0 w# j+ |! n6 M 4 O2 X7 O8 y' l! B, A /* create device files */2 d/ j' S# W6 k& A4 O, p9 H
ret = device_create_file(&pdev->dev, &dev_attr_debug); /*添加设备属性*/0 w; y; E7 K% u" I( @. d
if (ret) {) V$ u& p9 B* g8 x2 u
printk(KERN_ERR "failed to add debug attribute\n");' X, I! Q- P1 }2 E; }
}% M% H: J2 y. \0 Z# y# p& y
" o4 G4 K( v, ]0 y( L. d! M, S
printk(KERN_INFO "fb%d: %s frame buffer device\n", * u- [% U3 e# x4 W6 P$ L4 I fbinfo->node, fbinfo->fix.id); ! b0 o# d4 E! A. \* t7 g7 ^9 { ) Y2 _0 P% O9 {. \9 l return 0; ' @8 H C( ~( F/*一旦某个步骤发生错误,以注册的相反顺序开始注销*/ x" O7 o, \; W0 P% q# b9 T
free_video_memory: N: P C$ T6 p& I* g
s3c2410fb_unmap_video_memory(fbinfo);( g; p7 O& p" `5 M r. C
release_clock:) S; M, N& n! l, Q. l; X7 m
clk_disable(info->clk); 2 e& U- t* O7 u6 d clk_put(info->clk); ; D6 {$ e2 t! P, O, n$ ~$ Trelease_irq: 8 p0 @1 a& m4 x2 Z/ J- Y9 A! v4 N free_irq(irq, info);3 w4 I. W4 U' u1 _: {
release_regs: ( x$ G( U; a/ o J; {% U9 W iounmap(info->io);/ S* v3 v5 N4 q+ P( X
release_mem: 4 G8 H8 V9 s/ R5 b' ^1 \% A release_resource(info->mem); 1 z' I) J$ i' Q+ ^ kfree(info->mem);1 ]+ V+ v$ g9 l2 N" e4 A2 D$ |
dealloc_fb:+ e3 Q' O, {, N) ?; D9 U' ]+ z
platform_set_drvdata(pdev, NULL);/ F/ K0 M/ a( ~
framebuffer_release(fbinfo); ( P+ H: q0 B9 R/ { return ret;) l6 V* P4 P$ y Z
}2 x0 B" D4 t4 s7 `+ l' V
3 k0 {: X) `& a这里使用了三个新的数据结构。s3c2410fb_info是驱动程序使用的,里面将保存所有驱动程序所要使用的资源等。而s3c2410fb_display和s3c2410fb_mach_info,是由板级信息,通过platform总线添加到内核中。8 m3 N, j& F* ~% s6 V3 n O