Rev 475 |
Rev 477 |
Go to most recent revision |
Blame |
Compare with Previous |
Last modification |
View Log
| RSS feed
/*
* linux/drivers/video/fbmem.c
*
* Copyright (C) 1994 Martin Schaller
*
* 2001 - Documented with DocBook
* - Brad Douglas <brad@neruo.com>
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file COPYING in the main directory of this archive
* for more details.
*/
#include <linuxcomp.h>
#include <linux/config.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/smp_lock.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/mman.h>
#include <linux/tty.h>
#include <linux/init.h>
#include <linux/linux_logo.h>
#include <linux/proc_fs.h>
#ifdef CONFIG_KMOD
#include <linux/kmod.h>
#endif
#include <linux/devfs_fs_kernel.h>
#if defined(__mc68000__) || defined(CONFIG_APUS)
#include <asm/setup.h>
#endif
#include <asm/io.h>
#include <asm/uaccess.h>
#include <asm/page.h>
#include <asm/pgtable.h>
#include <linux/fb.h>
#ifdef CONFIG_FRAMEBUFFER_CONSOLE
#include "console/fbcon.h"
#endif
/*
* Frame buffer device initialization and setup routines
*/
extern int acornfb_init
(void);
extern int acornfb_setup
(char*);
extern int amifb_init
(void);
extern int amifb_setup
(char*);
extern int anakinfb_init
(void);
extern int atafb_init
(void);
extern int atafb_setup
(char*);
extern int macfb_init
(void);
extern int macfb_setup
(char*);
extern int cyberfb_init
(void);
extern int cyberfb_setup
(char*);
extern int pm2fb_init
(void);
extern int pm2fb_setup
(char*);
extern int pm3fb_init
(void);
extern int pm3fb_setup
(char*);
extern int clps711xfb_init
(void);
extern int cyber2000fb_init
(void);
extern int cyber2000fb_setup
(char*);
extern int retz3fb_init
(void);
extern int retz3fb_setup
(char*);
extern int clgenfb_init
(void);
extern int clgenfb_setup
(char*);
extern int hitfb_init
(void);
extern int vfb_init
(void);
extern int vfb_setup
(char*);
extern int offb_init
(void);
extern int atyfb_init
(void);
extern int atyfb_setup
(char*);
extern int aty128fb_init
(void);
extern int aty128fb_setup
(char*);
extern int neofb_init
(void);
extern int neofb_setup
(char*);
extern int igafb_init
(void);
extern int igafb_setup
(char*);
extern int imsttfb_init
(void);
extern int imsttfb_setup
(char*);
extern int dnfb_init
(void);
extern int tgafb_init
(void);
extern int tgafb_setup
(char*);
extern int virgefb_init
(void);
extern int virgefb_setup
(char*);
extern int resolver_video_setup
(char*);
extern int s3triofb_init
(void);
extern int vesafb_init
(void);
extern int vesafb_setup
(char*);
extern int vga16fb_init
(void);
extern int vga16fb_setup
(char*);
extern int hgafb_init
(void);
extern int hgafb_setup
(char*);
extern int matroxfb_init
(void);
extern int matroxfb_setup
(char*);
extern int hpfb_init
(void);
extern int control_init
(void);
extern int control_setup
(char*);
extern int platinum_init
(void);
extern int platinum_setup
(char*);
extern int valkyriefb_init
(void);
extern int valkyriefb_setup
(char*);
extern int chips_init
(void);
extern int g364fb_init
(void);
extern int sa1100fb_init
(void);
extern int fm2fb_init
(void);
extern int fm2fb_setup
(char*);
extern int q40fb_init
(void);
extern int sun3fb_init
(void);
extern int sun3fb_setup
(char *);
extern int sgivwfb_init
(void);
extern int sgivwfb_setup
(char*);
extern int rivafb_init
(void);
extern int rivafb_setup
(char*);
extern int tdfxfb_init
(void);
extern int tdfxfb_setup
(char*);
extern int tridentfb_init
(void);
extern int tridentfb_setup
(char*);
extern int sisfb_init
(void);
extern int sisfb_setup
(char*);
extern int stifb_init
(void);
extern int stifb_setup
(char*);
extern int pmagbafb_init
(void);
extern int pmagbbfb_init
(void);
extern int maxinefb_init
(void);
extern int tx3912fb_init
(void);
extern int tx3912fb_setup
(char*);
extern int radeonfb_init
(void);
extern int radeonfb_setup
(char*);
extern int e1355fb_init
(void);
extern int e1355fb_setup
(char*);
extern int pvr2fb_init
(void);
extern int pvr2fb_setup
(char*);
extern int sstfb_init
(void);
extern int sstfb_setup
(char*);
extern int i810fb_init
(void);
extern int i810fb_setup
(char*);
extern int ffb_init
(void);
extern int ffb_setup
(char*);
extern int cg6_init
(void);
extern int cg6_setup
(char*);
extern int cg3_init
(void);
extern int cg3_setup
(char*);
extern int bw2_init
(void);
extern int bw2_setup
(char*);
extern int cg14_init
(void);
extern int cg14_setup
(char*);
extern int p9100_init
(void);
extern int p9100_setup
(char*);
extern int tcx_init
(void);
extern int tcx_setup
(char*);
extern int leo_init
(void);
extern int leo_setup
(char*);
static struct {
const char *name
;
int (*init
)(void);
int (*setup
)(char*);
} fb_drivers
[] __initdata
= {
/*
* Chipset specific drivers that use resource management
*/
#ifdef CONFIG_FB_RETINAZ3
{ "retz3fb", retz3fb_init
, retz3fb_setup
},
#endif
#ifdef CONFIG_FB_AMIGA
{ "amifb", amifb_init
, amifb_setup
},
#endif
#ifdef CONFIG_FB_ANAKIN
{ "anakinfb", anakinfb_init
, NULL
},
#endif
#ifdef CONFIG_FB_CLPS711X
{ "clps711xfb", clps711xfb_init
, NULL
},
#endif
#ifdef CONFIG_FB_CYBER
{ "cyberfb", cyberfb_init
, cyberfb_setup
},
#endif
#ifdef CONFIG_FB_CYBER2000
{ "cyber2000fb", cyber2000fb_init
, cyber2000fb_setup
},
#endif
#ifdef CONFIG_FB_PM2
{ "pm2fb", pm2fb_init
, pm2fb_setup
},
#endif
#ifdef CONFIG_FB_PM3
{ "pm3fb", pm3fb_init
, pm3fb_setup
},
#endif
#ifdef CONFIG_FB_CLGEN
{ "clgenfb", clgenfb_init
, clgenfb_setup
},
#endif
#ifdef CONFIG_FB_ATY
{ "atyfb", atyfb_init
, atyfb_setup
},
#endif
#ifdef CONFIG_FB_MATROX
{ "matroxfb", matroxfb_init
, matroxfb_setup
},
#endif
#ifdef CONFIG_FB_ATY128
{ "aty128fb", aty128fb_init
, aty128fb_setup
},
#endif
#ifdef CONFIG_FB_NEOMAGIC
{ "neofb", neofb_init
, neofb_setup
},
#endif
#ifdef CONFIG_FB_VIRGE
{ "virgefb", virgefb_init
, virgefb_setup
},
#endif
#ifdef CONFIG_FB_RIVA
{ "rivafb", rivafb_init
, rivafb_setup
},
#endif
#ifdef CONFIG_FB_3DFX
{ "tdfxfb", tdfxfb_init
, tdfxfb_setup
},
#endif
#ifdef CONFIG_FB_RADEON
{ "radeonfb", radeonfb_init
, radeonfb_setup
},
#endif
#ifdef CONFIG_FB_CONTROL
{ "controlfb", control_init
, control_setup
},
#endif
#ifdef CONFIG_FB_PLATINUM
{ "platinumfb", platinum_init
, platinum_setup
},
#endif
#ifdef CONFIG_FB_VALKYRIE
{ "valkyriefb", valkyriefb_init
, valkyriefb_setup
},
#endif
#ifdef CONFIG_FB_CT65550
{ "chipsfb", chips_init
, NULL
},
#endif
#ifdef CONFIG_FB_IMSTT
{ "imsttfb", imsttfb_init
, imsttfb_setup
},
#endif
#ifdef CONFIG_FB_S3TRIO
{ "s3triofb", s3triofb_init
, NULL
},
#endif
#ifdef CONFIG_FB_FM2
{ "fm2fb", fm2fb_init
, fm2fb_setup
},
#endif
#ifdef CONFIG_FB_SIS
{ "sisfb", sisfb_init
, sisfb_setup
},
#endif
#ifdef CONFIG_FB_TRIDENT
{ "tridentfb", tridentfb_init
, tridentfb_setup
},
#endif
#ifdef CONFIG_FB_I810
{ "i810fb", i810fb_init
, i810fb_setup
},
#endif
#ifdef CONFIG_FB_STI
{ "stifb", stifb_init
, stifb_setup
},
#endif
#ifdef CONFIG_FB_FFB
{ "ffb", ffb_init
, ffb_setup
},
#endif
#ifdef CONFIG_FB_CG6
{ "cg6fb", cg6_init
, cg6_setup
},
#endif
#ifdef CONFIG_FB_CG3
{ "cg3fb", cg3_init
, cg3_setup
},
#endif
#ifdef CONFIG_FB_BW2
{ "bw2fb", bw2_init
, bw2_setup
},
#endif
#ifdef CONFIG_FB_CG14
{ "cg14fb", cg14_init
, cg14_setup
},
#endif
#ifdef CONFIG_FB_P9100
{ "p9100fb", p9100_init
, p9100_setup
},
#endif
#ifdef CONFIG_FB_TCX
{ "tcxfb", tcx_init
, tcx_setup
},
#endif
#ifdef CONFIG_FB_LEO
{ "leofb", leo_init
, leo_setup
},
#endif
/*
* Generic drivers that are used as fallbacks
*
* These depend on resource management and must be initialized
* _after_ all other frame buffer devices that use resource
* management!
*/
#ifdef CONFIG_FB_OF
{ "offb", offb_init
, NULL
},
#endif
#ifdef CONFIG_FB_VESA
{ "vesafb", vesafb_init
, vesafb_setup
},
#endif
/*
* Chipset specific drivers that don't use resource management (yet)
*/
#ifdef CONFIG_FB_SGIVW
{ "sgivwfb", sgivwfb_init
, sgivwfb_setup
},
#endif
#ifdef CONFIG_FB_ACORN
{ "acornfb", acornfb_init
, acornfb_setup
},
#endif
#ifdef CONFIG_FB_ATARI
{ "atafb", atafb_init
, atafb_setup
},
#endif
#ifdef CONFIG_FB_MAC
{ "macfb", macfb_init
, macfb_setup
},
#endif
#ifdef CONFIG_FB_HGA
{ "hgafb", hgafb_init
, hgafb_setup
},
#endif
#ifdef CONFIG_FB_IGA
{ "igafb", igafb_init
, igafb_setup
},
#endif
#ifdef CONFIG_APOLLO
{ "apollofb", dnfb_init
, NULL
},
#endif
#ifdef CONFIG_FB_Q40
{ "q40fb", q40fb_init
, NULL
},
#endif
#ifdef CONFIG_FB_TGA
{ "tgafb", tgafb_init
, tgafb_setup
},
#endif
#ifdef CONFIG_FB_HP300
{ "hpfb", hpfb_init
, NULL
},
#endif
#ifdef CONFIG_FB_G364
{ "g364fb", g364fb_init
, NULL
},
#endif
#ifdef CONFIG_FB_SA1100
{ "sa1100fb", sa1100fb_init
, NULL
},
#endif
#ifdef CONFIG_FB_SUN3
{ "sun3fb", sun3fb_init
, sun3fb_setup
},
#endif
#ifdef CONFIG_FB_HIT
{ "hitfb", hitfb_init
, NULL
},
#endif
#ifdef CONFIG_FB_TX3912
{ "tx3912fb", tx3912fb_init
, tx3912fb_setup
},
#endif
#ifdef CONFIG_FB_E1355
{ "e1355fb", e1355fb_init
, e1355fb_setup
},
#endif
#ifdef CONFIG_FB_PVR2
{ "pvr2fb", pvr2fb_init
, pvr2fb_setup
},
#endif
#ifdef CONFIG_FB_PMAG_BA
{ "pmagbafb", pmagbafb_init
, NULL
},
#endif
#ifdef CONFIG_FB_PMAGB_B
{ "pmagbbfb", pmagbbfb_init
, NULL
},
#endif
#ifdef CONFIG_FB_MAXINE
{ "maxinefb", maxinefb_init
, NULL
},
#endif
#ifdef CONFIG_FB_VOODOO1
{ "sstfb", sstfb_init
, sstfb_setup
},
#endif
/*
* Generic drivers that don't use resource management (yet)
*/
#ifdef CONFIG_FB_VGA16
{ "vga16fb", vga16fb_init
, vga16fb_setup
},
#endif
#ifdef CONFIG_GSP_RESOLVER
/* Not a real frame buffer device... */
{ "resolverfb", NULL
, resolver_video_setup
},
#endif
#ifdef CONFIG_FB_VIRTUAL
/*
* Vfb must be last to avoid that it becomes your primary display if
* other display devices are present
*/
{ "vfb", vfb_init
, vfb_setup
},
#endif
};
#define NUM_FB_DRIVERS (sizeof(fb_drivers)/sizeof(*fb_drivers))
#define FBPIXMAPSIZE 8192
//extern const char *global_mode_option;
static initcall_t pref_init_funcs
[FB_MAX
];
static int num_pref_init_funcs __initdata
= 0;
struct fb_info
*registered_fb
[FB_MAX
];
int num_registered_fb
;
#ifdef CONFIG_FB_OF
static int ofonly __initdata
= 0;
#endif
/*
* Drawing helpers.
*/
u8 sys_inbuf
(u8
*src
)
{
return *src
;
}
void sys_outbuf
(u8
*src
, u8
*dst
, unsigned int size
)
{
memcpy(dst
, src
, size
);
}
void move_buf_aligned
(struct fb_info
*info
, u8
*dst
, u8
*src
, u32 d_pitch
,
u32 s_pitch
, u32 height
)
{
int i
;
for (i
= height
; i
--; ) {
info
->pixmap.
outbuf(src
, dst
, s_pitch
);
src
+= s_pitch
;
dst
+= d_pitch
;
}
}
void move_buf_unaligned
(struct fb_info
*info
, u8
*dst
, u8
*src
, u32 d_pitch
,
u32 height
, u32 mask
, u32 shift_high
, u32 shift_low
,
u32 mod
, u32 idx
)
{
int i
, j
;
u8 tmp
;
for (i
= height
; i
--; ) {
for (j
= 0; j
< idx
; j
++) {
tmp
= info
->pixmap.
inbuf(dst
+j
);
tmp
&= mask
;
tmp
|= *src
>> shift_low
;
info
->pixmap.
outbuf(&tmp
, dst
+j
, 1);
tmp
= *src
<< shift_high
;
info
->pixmap.
outbuf(&tmp
, dst
+j
+1, 1);
src
++;
}
tmp
= info
->pixmap.
inbuf(dst
+idx
);
tmp
&= mask
;
tmp
|= *src
>> shift_low
;
info
->pixmap.
outbuf(&tmp
, dst
+idx
, 1);
if (shift_high
< mod
) {
tmp
= *src
<< shift_high
;
info
->pixmap.
outbuf(&tmp
, dst
+idx
+1, 1);
}
src
++;
dst
+= d_pitch
;
}
}
/*
* we need to lock this section since fb_cursor
* may use fb_imageblit()
*/
u32 fb_get_buffer_offset
(struct fb_info
*info
, u32 size
)
{
u32 align
= info
->pixmap.
buf_align - 1;
u32 offset
, count
= 1000;
spin_lock
(&info
->pixmap.
lock);
offset
= info
->pixmap.
offset + align
;
offset
&= ~align
;
if (offset
+ size
> info
->pixmap.
size) {
while (atomic_read
(&info
->pixmap.
count) && count
--);
if (info
->fbops
->fb_sync
&&
info
->pixmap.
flags & FB_PIXMAP_SYNC
)
info
->fbops
->fb_sync
(info
);
offset
= 0;
}
info
->pixmap.
offset = offset
+ size
;
atomic_inc
(&info
->pixmap.
count);
smp_mb__after_atomic_inc
();
spin_unlock
(&info
->pixmap.
lock);
return offset
;
}
#ifdef CONFIG_LOGO
#include <linux/linux_logo.h>
static inline unsigned safe_shift
(unsigned d
, int n
)
{
return n
< 0 ? d
>> -n
: d
<< n
;
}
static void __init fb_set_logocmap
(struct fb_info
*info
,
const struct linux_logo
*logo
)
{
struct fb_cmap palette_cmap
;
u16 palette_green
[16];
u16 palette_blue
[16];
u16 palette_red
[16];
int i
, j
, n
;
const unsigned char *clut
= logo
->clut
;
palette_cmap.
start = 0;
palette_cmap.
len = 16;
palette_cmap.
red = palette_red
;
palette_cmap.
green = palette_green
;
palette_cmap.
blue = palette_blue
;
palette_cmap.
transp = NULL
;
for (i
= 0; i
< logo
->clutsize
; i
+= n
) {
n
= logo
->clutsize
- i
;
/* palette_cmap provides space for only 16 colors at once */
if (n
> 16)
n
= 16;
palette_cmap.
start = 32 + i
;
palette_cmap.
len = n
;
for (j
= 0; j
< n
; ++j
) {
palette_cmap.
red[j
] = clut
[0] << 8 | clut
[0];
palette_cmap.
green[j
] = clut
[1] << 8 | clut
[1];
palette_cmap.
blue[j
] = clut
[2] << 8 | clut
[2];
clut
+= 3;
}
fb_set_cmap
(&palette_cmap
, 1, info
);
}
}
static void __init fb_set_logo_truepalette
(struct fb_info
*info
,
const struct linux_logo
*logo
,
u32
*palette
)
{
unsigned char mask
[9] = { 0,0x80,0xc0,0xe0,0xf0,0xf8,0xfc,0xfe,0xff };
unsigned char redmask
, greenmask
, bluemask
;
int redshift
, greenshift
, blueshift
;
int i
;
const unsigned char *clut
= logo
->clut
;
/*
* We have to create a temporary palette since console palette is only
* 16 colors long.
*/
/* Bug: Doesn't obey msb_right ... (who needs that?) */
redmask
= mask
[info
->var.
red.
length < 8 ? info
->var.
red.
length : 8];
greenmask
= mask
[info
->var.
green.
length < 8 ? info
->var.
green.
length : 8];
bluemask
= mask
[info
->var.
blue.
length < 8 ? info
->var.
blue.
length : 8];
redshift
= info
->var.
red.
offset - (8 - info
->var.
red.
length);
greenshift
= info
->var.
green.
offset - (8 - info
->var.
green.
length);
blueshift
= info
->var.
blue.
offset - (8 - info
->var.
blue.
length);
for ( i
= 0; i
< logo
->clutsize
; i
++) {
palette
[i
+32] = (safe_shift
((clut
[0] & redmask
), redshift
) |
safe_shift
((clut
[1] & greenmask
), greenshift
) |
safe_shift
((clut
[2] & bluemask
), blueshift
));
clut
+= 3;
}
}
static void __init fb_set_logo_directpalette
(struct fb_info
*info
,
const struct linux_logo
*logo
,
u32
*palette
)
{
int redshift
, greenshift
, blueshift
;
int i
;
redshift
= info
->var.
red.
offset;
greenshift
= info
->var.
green.
offset;
blueshift
= info
->var.
blue.
offset;
for (i
= 32; i
< logo
->clutsize
; i
++)
palette
[i
] = i
<< redshift
| i
<< greenshift
| i
<< blueshift
;
}
static void __init fb_set_logo
(struct fb_info
*info
,
const struct linux_logo
*logo
, u8
*dst
,
int depth
)
{
int i
, j
, shift
;
const u8
*src
= logo
->data
;
u8 d
, xor
= 0;
switch (depth
) {
case 4:
for (i
= 0; i
< logo
->height
; i
++)
for (j
= 0; j
< logo
->width
; src
++) {
*dst
++ = *src
>> 4;
j
++;
if (j
< logo
->width
) {
*dst
++ = *src
& 0x0f;
j
++;
}
}
break;
case ~
1:
xor
= 0xff;
case 1:
for (i
= 0; i
< logo
->height
; i
++) {
shift
= 7;
d
= *src
++ ^ xor
;
for (j
= 0; j
< logo
->width
; j
++) {
*dst
++ = (d
>> shift
) & 1;
shift
= (shift
-1) & 7;
if (shift
== 7)
d
= *src
++ ^ xor
;
}
}
break;
}
}
/*
* Three (3) kinds of logo maps exist. linux_logo_clut224 (>16 colors),
* linux_logo_vga16 (16 colors) and linux_logo_mono (2 colors). Depending on
* the visual format and color depth of the framebuffer, the DAC, the
* pseudo_palette, and the logo data will be adjusted accordingly.
*
* Case 1 - linux_logo_clut224:
* Color exceeds the number of console colors (16), thus we set the hardware DAC
* using fb_set_cmap() appropriately. The "needs_cmapreset" flag will be set.
*
* For visuals that require color info from the pseudo_palette, we also construct
* one for temporary use. The "needs_directpalette" or "needs_truepalette" flags
* will be set.
*
* Case 2 - linux_logo_vga16:
* The number of colors just matches the console colors, thus there is no need
* to set the DAC or the pseudo_palette. However, the bitmap is packed, ie,
* each byte contains color information for two pixels (upper and lower nibble).
* To be consistent with fb_imageblit() usage, we therefore separate the two
* nibbles into separate bytes. The "depth" flag will be set to 4.
*
* Case 3 - linux_logo_mono:
* This is similar with Case 2. Each byte contains information for 8 pixels.
* We isolate each bit and expand each into a byte. The "depth" flag will
* be set to 1.
*/
static struct logo_data
{
int depth
;
int needs_directpalette
;
int needs_truepalette
;
int needs_cmapreset
;
const struct linux_logo
*logo
;
} fb_logo
;
int fb_prepare_logo
(struct fb_info
*info
)
{
memset(&fb_logo
, 0, sizeof(struct logo_data
));
switch (info
->fix.
visual) {
case FB_VISUAL_TRUECOLOR
:
if (info
->var.
bits_per_pixel >= 8)
fb_logo.
needs_truepalette = 1;
break;
case FB_VISUAL_DIRECTCOLOR
:
if (info
->var.
bits_per_pixel >= 24) {
fb_logo.
needs_directpalette = 1;
fb_logo.
needs_cmapreset = 1;
}
break;
case FB_VISUAL_PSEUDOCOLOR
:
fb_logo.
needs_cmapreset = 1;
break;
}
/* Return if no suitable logo was found */
fb_logo.
logo = fb_find_logo
(info
->var.
bits_per_pixel);
if (!fb_logo.
logo || fb_logo.
logo->height
> info
->var.
yres) {
fb_logo.
logo = NULL
;
return 0;
}
/* What depth we asked for might be different from what we get */
if (fb_logo.
logo->type
== LINUX_LOGO_CLUT224
)
fb_logo.
depth = 8;
else if (fb_logo.
logo->type
== LINUX_LOGO_VGA16
)
fb_logo.
depth = 4;
else
fb_logo.
depth = 1;
return fb_logo.
logo->height
;
}
int fb_show_logo
(struct fb_info
*info
)
{
u32
*palette
= NULL
, *saved_pseudo_palette
= NULL
;
unsigned char *logo_new
= NULL
;
struct fb_image image
;
int x
;
/* Return if the frame buffer is not mapped */
if (fb_logo.
logo == NULL
)
return 0;
image.
depth = fb_logo.
depth;
image.
data = fb_logo.
logo->data
;
if (fb_logo.
needs_cmapreset)
fb_set_logocmap
(info
, fb_logo.
logo);
if (fb_logo.
needs_truepalette ||
fb_logo.
needs_directpalette) {
palette
= kmalloc
(256 * 4, GFP_KERNEL
);
if (palette
== NULL
)
return 0;
if (fb_logo.
needs_truepalette)
fb_set_logo_truepalette
(info
, fb_logo.
logo, palette
);
else
fb_set_logo_directpalette
(info
, fb_logo.
logo, palette
);
saved_pseudo_palette
= info
->pseudo_palette
;
info
->pseudo_palette
= palette
;
}
if (fb_logo.
depth == 4) {
logo_new
= kmalloc
(fb_logo.
logo->width
* fb_logo.
logo->height
,
GFP_KERNEL
);
if (logo_new
== NULL
) {
if (palette
)
kfree
(palette
);
if (saved_pseudo_palette
)
info
->pseudo_palette
= saved_pseudo_palette
;
return 0;
}
image.
data = logo_new
;
fb_set_logo
(info
, fb_logo.
logo, logo_new
, fb_logo.
depth);
}
image.
width = fb_logo.
logo->width
;
image.
height = fb_logo.
logo->height
;
image.
dy = 0;
for (x
= 0; x
< num_online_cpus
() * (fb_logo.
logo->width
+ 8) &&
x
<= info
->var.
xres-fb_logo.
logo->width
; x
+= (fb_logo.
logo->width
+ 8)) {
image.
dx = x
;
info
->fbops
->fb_imageblit
(info
, &image
);
//atomic_dec(&info->pixmap.count);
//smp_mb__after_atomic_dec();
}
if (palette
!= NULL
)
kfree
(palette
);
if (saved_pseudo_palette
!= NULL
)
info
->pseudo_palette
= saved_pseudo_palette
;
if (logo_new
!= NULL
)
kfree
(logo_new
);
return fb_logo.
logo->height
;
}
#else
int fb_prepare_logo
(struct fb_info
*info
) { return 0; }
int fb_show_logo
(struct fb_info
*info
) { return 0; }
#endif /* CONFIG_LOGO */
static int fbmem_read_proc
(char *buf
, char **start
, off_t offset
,
int len
, int *eof
, void *private
)
{
struct fb_info
**fi
;
int clen
;
clen
= 0;
for (fi
= registered_fb
; fi
< ®istered_fb
[FB_MAX
] && len
< 4000; fi
++)
if (*fi
)
clen
+= sprintf26
(buf
+ clen
, "%d %s\n",
(*fi
)->node
,
(*fi
)->fix.
id);
*start
= buf
+ offset
;
if (clen
> offset
)
clen
-= offset
;
else
clen
= 0;
return clen
< len
? clen
: len
;
}
static ssize_t
fb_read
(struct file
*file
, char *buf
, size_t count
, loff_t
*ppos
)
{
unsigned long p
= *ppos
;
struct inode
*inode
= file
->f_dentry
->d_inode
;
int fbidx
= iminor
(inode
);
struct fb_info
*info
= registered_fb
[fbidx
];
if (!info
|| ! info
->screen_base
)
return -ENODEV
;
if (info
->fbops
->fb_read
)
return info
->fbops
->fb_read
(file
, buf
, count
, ppos
);
if (p
>= info
->fix.
smem_len)
return 0;
if (count
>= info
->fix.
smem_len)
count
= info
->fix.
smem_len;
if (count
+ p
> info
->fix.
smem_len)
count
= info
->fix.
smem_len - p
;
if (info
->fbops
->fb_sync
)
info
->fbops
->fb_sync
(info
);
if (count
) {
char *base_addr
;
base_addr
= info
->screen_base
;
count
-= copy_to_user
(buf
, base_addr
+p
, count
);
if (!count
)
return -EFAULT
;
*ppos
+= count
;
}
return count
;
}
static ssize_t
fb_write
(struct file
*file
, const char *buf
, size_t count
, loff_t
*ppos
)
{
unsigned long p
= *ppos
;
struct inode
*inode
= file
->f_dentry
->d_inode
;
int fbidx
= iminor
(inode
);
struct fb_info
*info
= registered_fb
[fbidx
];
int err
;
if (!info
|| !info
->screen_base
)
return -ENODEV
;
if (info
->fbops
->fb_write
)
return info
->fbops
->fb_write
(file
, buf
, count
, ppos
);
if (p
> info
->fix.
smem_len)
return -ENOSPC
;
if (count
>= info
->fix.
smem_len)
count
= info
->fix.
smem_len;
err
= 0;
if (count
+ p
> info
->fix.
smem_len) {
count
= info
->fix.
smem_len - p
;
err
= -ENOSPC
;
}
if (info
->fbops
->fb_sync
)
info
->fbops
->fb_sync
(info
);
if (count
) {
char *base_addr
;
base_addr
= info
->screen_base
;
count
-= copy_from_user
(base_addr
+p
, buf
, count
);
*ppos
+= count
;
err
= -EFAULT
;
}
if (count
)
return count
;
return err
;
}
#ifdef CONFIG_KMOD
static void try_to_load
(int fb
)
{
request_module
("fb%d", fb
);
}
#endif /* CONFIG_KMOD */
int
fb_cursor
(struct fb_info
*info
, struct fb_cursor
*sprite
)
{
struct fb_cursor cursor
;
int err
;
if (copy_from_user
(&cursor
, sprite
, sizeof(struct fb_cursor
)))
return -EFAULT
;
if (cursor.
set & FB_CUR_SETCUR
)
info
->cursor.
enable = 1;
if (cursor.
set & FB_CUR_SETCMAP
) {
err
= fb_copy_cmap
(&cursor.
image.
cmap, &sprite
->image.
cmap, 1);
if (err
)
return err
;
}
if (cursor.
set & FB_CUR_SETSHAPE
) {
int size
= ((cursor.
image.
width + 7) >> 3) * cursor.
image.
height;
if ((cursor.
image.
height != info
->cursor.
image.
height) ||
(cursor.
image.
width != info
->cursor.
image.
width))
cursor.
set |= FB_CUR_SETSIZE
;
cursor.
image.
data = kmalloc
(size
, GFP_KERNEL
);
if (!cursor.
image.
data)
return -ENOMEM
;
cursor.
mask = kmalloc
(size
, GFP_KERNEL
);
if (!cursor.
mask) {
kfree
(cursor.
image.
data);
return -ENOMEM
;
}
if (copy_from_user
(&cursor.
image.
data, sprite
->image.
data, size
) ||
copy_from_user
(cursor.
mask, sprite
->mask
, size
)) {
kfree
(cursor.
image.
data);
kfree
(cursor.
mask);
return -EFAULT
;
}
}
info
->cursor.
set = cursor.
set;
info
->cursor.
rop = cursor.
rop;
err
= info
->fbops
->fb_cursor
(info
, &cursor
);
return err
;
}
int
fb_pan_display
(struct fb_info
*info
, struct fb_var_screeninfo
*var
)
{
int xoffset
= var
->xoffset
;
int yoffset
= var
->yoffset
;
int err
;
if (xoffset
< 0 || yoffset
< 0 || !info
->fbops
->fb_pan_display
||
xoffset
+ info
->var.
xres > info
->var.
xres_virtual ||
yoffset
+ info
->var.
yres > info
->var.
yres_virtual)
return -EINVAL
;
if ((err
= info
->fbops
->fb_pan_display
(var
, info
)))
return err
;
info
->var.
xoffset = var
->xoffset
;
info
->var.
yoffset = var
->yoffset
;
if (var
->vmode
& FB_VMODE_YWRAP
)
info
->var.
vmode |= FB_VMODE_YWRAP
;
else
info
->var.
vmode &= ~FB_VMODE_YWRAP
;
return 0;
}
int
fb_set_var
(struct fb_info
*info
, struct fb_var_screeninfo
*var
)
{
int err
;
if (memcmp(&info
->var
, var
, sizeof(struct fb_var_screeninfo
))) {
if (!info
->fbops
->fb_check_var
) {
*var
= info
->var
;
return 0;
}
if ((err
= info
->fbops
->fb_check_var
(var
, info
)))
return err
;
if ((var
->activate
& FB_ACTIVATE_MASK
) == FB_ACTIVATE_NOW
) {
info
->var
= *var
;
if (info
->fbops
->fb_set_par
)
info
->fbops
->fb_set_par
(info
);
fb_pan_display
(info
, &info
->var
);
fb_set_cmap
(&info
->cmap
, 1, info
);
}
}
return 0;
}
int
fb_blank
(struct fb_info
*info
, int blank
)
{
/* ??? Variable sized stack allocation. */
u16 black
[info
->cmap.
len];
struct fb_cmap cmap
;
if (info
->fbops
->fb_blank
&& !info
->fbops
->fb_blank
(blank
, info
))
return 0;
if (blank
) {
memset(black
, 0, info
->cmap.
len * sizeof(u16
));
cmap.
red = cmap.
green = cmap.
blue = black
;
cmap.
transp = info
->cmap.
transp ? black
: NULL
;
cmap.
start = info
->cmap.
start;
cmap.
len = info
->cmap.
len;
} else
cmap
= info
->cmap
;
return fb_set_cmap
(&cmap
, 1, info
);
}
int
fb_ioctl
(struct inode
*inode
, struct file
*file
, unsigned int cmd
,
unsigned long arg
)
{
int fbidx
= iminor
(inode
);
struct fb_info
*info
= registered_fb
[fbidx
];
struct fb_ops
*fb
= info
->fbops
;
struct fb_var_screeninfo var
;
struct fb_fix_screeninfo fix
;
#ifdef CONFIG_FRAMEBUFFER_CONSOLE
struct fb_con2fbmap con2fb
;
#endif
struct fb_cmap cmap
;
int i
;
if (!fb
)
return -ENODEV
;
switch (cmd
) {
case FBIOGET_VSCREENINFO
:
return copy_to_user
((void *) arg
, &info
->var
,
sizeof(var
)) ? -EFAULT
: 0;
case FBIOPUT_VSCREENINFO
:
if (copy_from_user
(&var
, (void *) arg
, sizeof(var
)))
return -EFAULT
;
i
= fb_set_var
(info
, &var
);
if (i
) return i
;
if (copy_to_user
((void *) arg
, &var
, sizeof(var
)))
return -EFAULT
;
return 0;
case FBIOGET_FSCREENINFO
:
return copy_to_user
((void *) arg
, &info
->fix
,
sizeof(fix
)) ? -EFAULT
: 0;
case FBIOPUTCMAP
:
if (copy_from_user
(&cmap
, (void *) arg
, sizeof(cmap
)))
return -EFAULT
;
return (fb_set_cmap
(&cmap
, 0, info
));
case FBIOGETCMAP
:
if (copy_from_user
(&cmap
, (void *) arg
, sizeof(cmap
)))
return -EFAULT
;
return (fb_copy_cmap
(&info
->cmap
, &cmap
, 0));
case FBIOPAN_DISPLAY
:
if (copy_from_user
(&var
, (void *) arg
, sizeof(var
)))
return -EFAULT
;
if ((i
= fb_pan_display
(info
, &var
)))
return i
;
if (copy_to_user
((void *) arg
, &var
, sizeof(var
)))
return -EFAULT
;
return 0;
case FBIO_CURSOR
:
return (fb_cursor
(info
, (struct fb_cursor
*) arg
));
#ifdef CONFIG_FRAMEBUFFER_CONSOLE
case FBIOGET_CON2FBMAP
:
if (copy_from_user
(&con2fb
, (void *)arg
, sizeof(con2fb
)))
return -EFAULT
;
if (con2fb.
console < 1 || con2fb.
console > MAX_NR_CONSOLES
)
return -EINVAL
;
con2fb.
framebuffer = con2fb_map
[con2fb.
console-1];
return copy_to_user
((void *)arg
, &con2fb
,
sizeof(con2fb
)) ? -EFAULT
: 0;
case FBIOPUT_CON2FBMAP
:
if (copy_from_user
(&con2fb
, (void *)arg
, sizeof(con2fb
)))
return - EFAULT
;
if (con2fb.
console < 0 || con2fb.
console > MAX_NR_CONSOLES
)
return -EINVAL
;
if (con2fb.
framebuffer < 0 || con2fb.
framebuffer >= FB_MAX
)
return -EINVAL
;
#ifdef CONFIG_KMOD
if (!registered_fb
[con2fb.
framebuffer])
try_to_load
(con2fb.
framebuffer);
#endif /* CONFIG_KMOD */
if (!registered_fb
[con2fb.
framebuffer])
return -EINVAL
;
if (con2fb.
console != 0)
set_con2fb_map
(con2fb.
console-1, con2fb.
framebuffer);
else
fb_console_init
();
return 0;
#endif /* CONFIG_FRAMEBUFFER_CONSOLE */
case FBIOBLANK
:
return fb_blank
(info
, arg
);
default:
if (fb
->fb_ioctl
== NULL
)
return -EINVAL
;
return fb
->fb_ioctl
(inode
, file
, cmd
, arg
, info
);
}
}
static int
fb_mmap
(struct file
*file
, struct vm_area_struct
* vma
)
{
int fbidx
= iminor
(file
->f_dentry
->d_inode
);
struct fb_info
*info
= registered_fb
[fbidx
];
struct fb_ops
*fb
= info
->fbops
;
unsigned long off
;
#if !defined(__sparc__) || defined(__sparc_v9__)
unsigned long start
;
u32 len
;
#endif
if (vma
->vm_pgoff
> (~0UL
>> PAGE_SHIFT
))
return -EINVAL
;
off
= vma
->vm_pgoff
<< PAGE_SHIFT
;
if (!fb
)
return -ENODEV
;
if (fb
->fb_mmap
) {
int res
;
lock_kernel
();
res
= fb
->fb_mmap
(info
, file
, vma
);
unlock_kernel
();
return res
;
}
#if defined(__sparc__) && !defined(__sparc_v9__)
/* Should never get here, all fb drivers should have their own
mmap routines */
return -EINVAL
;
#else
/* !sparc32... */
lock_kernel
();
/* frame buffer memory */
start
= info
->fix.
smem_start;
len
= PAGE_ALIGN
((start
& ~PAGE_MASK
) + info
->fix.
smem_len);
if (off
>= len
) {
/* memory mapped io */
off
-= len
;
if (info
->var.
accel_flags) {
unlock_kernel
();
return -EINVAL
;
}
start
= info
->fix.
mmio_start;
len
= PAGE_ALIGN
((start
& ~PAGE_MASK
) + info
->fix.
mmio_len);
}
unlock_kernel
();
start
&= PAGE_MASK
;
if ((vma
->vm_end
- vma
->vm_start
+ off
) > len
)
return -EINVAL
;
off
+= start
;
vma
->vm_pgoff
= off
>> PAGE_SHIFT
;
/* This is an IO map - tell maydump to skip this VMA */
vma
->vm_flags
|= VM_IO
;
#if defined(__sparc_v9__)
vma
->vm_flags
|= (VM_SHM
| VM_LOCKED
);
if (io_remap_page_range
(vma
, vma
->vm_start
, off
,
vma
->vm_end
- vma
->vm_start
, vma
->vm_page_prot
, 0))
return -EAGAIN
;
#else
#if defined(__mc68000__)
#if defined(CONFIG_SUN3)
pgprot_val
(vma
->vm_page_prot
) |= SUN3_PAGE_NOCACHE
;
#elif defined(CONFIG_MMU)
if (CPU_IS_020_OR_030
)
pgprot_val
(vma
->vm_page_prot
) |= _PAGE_NOCACHE030
;
if (CPU_IS_040_OR_060
) {
pgprot_val
(vma
->vm_page_prot
) &= _CACHEMASK040
;
/* Use no-cache mode, serialized */
pgprot_val
(vma
->vm_page_prot
) |= _PAGE_NOCACHE_S
;
}
#endif
#elif defined(__powerpc__)
pgprot_val
(vma
->vm_page_prot
) |= _PAGE_NO_CACHE
|_PAGE_GUARDED
;
#elif defined(__alpha__)
/* Caching is off in the I/O space quadrant by design. */
#elif defined(__i386__) || defined(__x86_64__)
//if (boot_cpu_data.x86 > 3)
// pgprot_val(vma->vm_page_prot) |= _PAGE_PCD;
#elif defined(__mips__)
pgprot_val
(vma
->vm_page_prot
) &= ~_CACHE_MASK
;
pgprot_val
(vma
->vm_page_prot
) |= _CACHE_UNCACHED
;
#elif defined(__sh__)
pgprot_val
(vma
->vm_page_prot
) &= ~_PAGE_CACHABLE
;
#elif defined(__hppa__)
pgprot_val
(vma
->vm_page_prot
) |= _PAGE_NO_CACHE
;
#elif defined(__ia64__) || defined(__arm__)
vma
->vm_page_prot
= pgprot_writecombine
(vma
->vm_page_prot
);
#else
#warning What do we have to do here??
#endif
if (io_remap_page_range
(vma
, vma
->vm_start
, off
,
vma
->vm_end
- vma
->vm_start
, vma
->vm_page_prot
))
return -EAGAIN
;
#endif /* !__sparc_v9__ */
return 0;
#endif /* !sparc32 */
}
static int
fb_open
(struct inode
*inode
, struct file
*file
)
{
int fbidx
= iminor
(inode
);
struct fb_info
*info
;
int res
= 0;
if (fbidx
>= FB_MAX
)
return -ENODEV
;
#ifdef CONFIG_KMOD
if (!(info
= registered_fb
[fbidx
]))
try_to_load
(fbidx
);
#endif /* CONFIG_KMOD */
if (!(info
= registered_fb
[fbidx
]))
return -ENODEV
;
if (!try_module_get
(info
->fbops
->owner
))
return -ENODEV
;
if (info
->fbops
->fb_open
) {
res
= info
->fbops
->fb_open
(info
,1);
if (res
)
module_put
(info
->fbops
->owner
);
}
info
->fbops
->fb_set_par
(info
);
fb_pan_display
(info
, &info
->var
);
if (info
->var.
bits_per_pixel >= 8) {
fb_prepare_logo
(info
);
fb_show_logo
(info
);
}
return res
;
}
static int
fb_release
(struct inode
*inode
, struct file
*file
)
{
int fbidx
= iminor
(inode
);
struct fb_info
*info
;
lock_kernel
();
info
= registered_fb
[fbidx
];
if (info
->fbops
->fb_release
)
info
->fbops
->fb_release
(info
,1);
module_put
(info
->fbops
->owner
);
unlock_kernel
();
return 0;
}
static struct file_operations fb_fops
= {
.
owner = THIS_MODULE
,
.
read = fb_read
,
.
write = fb_write
,
.
ioctl = fb_ioctl
,
.
mmap = fb_mmap
,
.
open = fb_open
,
.
release = fb_release
,
#ifdef HAVE_ARCH_FB_UNMAPPED_AREA
.
get_unmapped_area = get_fb_unmapped_area
,
#endif
};
/**
* register_framebuffer - registers a frame buffer device
* @fb_info: frame buffer info structure
*
* Registers a frame buffer device @fb_info.
*
* Returns negative errno on error, or zero for success.
*
*/
int
register_framebuffer
(struct fb_info
*fb_info
)
{
int i
;
if (num_registered_fb
== FB_MAX
)
return -ENXIO
;
num_registered_fb
++;
for (i
= 0 ; i
< FB_MAX
; i
++)
if (!registered_fb
[i
])
break;
fb_info
->node
= i
;
if (fb_info
->pixmap.
addr == NULL
) {
fb_info
->pixmap.
addr = kmalloc
(FBPIXMAPSIZE
, GFP_KERNEL
);
if (fb_info
->pixmap.
addr) {
fb_info
->pixmap.
size = FBPIXMAPSIZE
;
fb_info
->pixmap.
buf_align = 1;
fb_info
->pixmap.
scan_align = 1;
fb_info
->pixmap.
flags = FB_PIXMAP_DEFAULT
;
}
}
fb_info
->pixmap.
offset = 0;
if (fb_info
->pixmap.
outbuf == NULL
)
fb_info
->pixmap.
outbuf = sys_outbuf
;
if (fb_info
->pixmap.
inbuf == NULL
)
fb_info
->pixmap.
inbuf = sys_inbuf
;
spin_lock_init
(&fb_info
->pixmap.
lock);
registered_fb
[i
] = fb_info
;
devfs_mk_cdev
(MKDEV
(FB_MAJOR
, i
),
S_IFCHR
| S_IRUGO
| S_IWUGO
, "fb/%d", i
);
return 0;
}
/**
* unregister_framebuffer - releases a frame buffer device
* @fb_info: frame buffer info structure
*
* Unregisters a frame buffer device @fb_info.
*
* Returns negative errno on error, or zero for success.
*
*/
int
unregister_framebuffer
(struct fb_info
*fb_info
)
{
int i
;
i
= fb_info
->node
;
if (!registered_fb
[i
])
return -EINVAL
;
devfs_remove
("fb/%d", i
);
if (fb_info
->pixmap.
addr)
kfree
(fb_info
->pixmap.
addr);
registered_fb
[i
]=NULL
;
num_registered_fb
--;
return 0;
}
/**
* fbmem_init - init frame buffer subsystem
*
* Initialize the frame buffer subsystem.
*
* NOTE: This function is _only_ to be called by drivers/char/mem.c.
*
*/
void __init
fbmem_init
(void)
{
int i
;
create_proc_read_entry
("fb", 0, 0, fbmem_read_proc
, NULL
);
devfs_mk_dir
("fb");
if (register_chrdev
(FB_MAJOR
,"fb",&fb_fops
))
printk
("unable to get major %d for fb devs\n", FB_MAJOR
);
#ifdef CONFIG_FB_OF
if (ofonly
) {
offb_init
();
return;
}
#endif
/*
* Probe for all builtin frame buffer devices
*/
for (i
= 0; i
< num_pref_init_funcs
; i
++)
pref_init_funcs
[i
]();
for (i
= 0; i
< NUM_FB_DRIVERS
; i
++)
if (fb_drivers
[i
].
init)
fb_drivers
[i
].
init();
}
extern int linuxcomp_setfd
(struct inode
*i
, int i_rdev
);
/* Shark Inode emulation */
int fb_open_inode
(int num
) {
struct inode
*i
;
i
= (struct inode
*)kmalloc
(sizeof(struct inode
),GFP_KERNEL
);
linuxcomp_setfd
(i
,num
);
if (fb_open
(i
,NULL
)) {
kfree
(i
);
return -1;
}
kfree
(i
);
return 0;
}
/* Shark Inode emulation */
int fb_close_inode
(int num
) {
struct inode
*i
;
i
= (struct inode
*)kmalloc
(sizeof(struct inode
),GFP_KERNEL
);
linuxcomp_setfd
(i
,num
);
if(fb_release
(i
,NULL
)) {
kfree
(i
);
return -1;
}
kfree
(i
);
return 0;
}
int fb_set_mode_inode
(int num
, int wx
, int wy
, int bpp
) {
struct inode
*i
;
struct fb_var_screeninfo var
;
i
= (struct inode
*)kmalloc
(sizeof(struct inode
),GFP_KERNEL
);
linuxcomp_setfd
(i
,num
);
if (fb_ioctl
(i
, NULL
, FBIOGET_VSCREENINFO
, (unsigned long)&var
)) {
kfree
(i
);
return -1;
}
var.
xres = wx
;
var.
yres = wy
;
var.
xres_virtual = wx
;
var.
yres_virtual = wy
;
var.
xoffset = 0;
var.
yoffset = 0;
var.
bits_per_pixel = bpp
;
if (fb_ioctl
(i
, NULL
, FBIOPUT_VSCREENINFO
, (unsigned long)&var
)) {
kfree
(i
);
return -1;
}
kfree
(i
);
return 0;
}
/**
* video_setup - process command line options
* @options: string of options
*
* Process command line options for frame buffer subsystem.
*
* NOTE: This function is a __setup and __init function.
*
* Returns zero.
*
*/
int __init video_setup
(char *options
)
{
int i
, j
;
if (!options
|| !*options
)
return 0;
#ifdef CONFIG_FB_OF
if (!strcmp(options
, "ofonly")) {
ofonly
= 1;
return 0;
}
#endif
if (num_pref_init_funcs
== FB_MAX
)
return 0;
for (i
= 0; i
< NUM_FB_DRIVERS
; i
++) {
j
= strlen(fb_drivers
[i
].
name);
if (!strncmp(options
, fb_drivers
[i
].
name, j
) &&
options
[j
] == ':') {
if (!strcmp(options
+j
+1, "off"))
fb_drivers
[i
].
init = NULL
;
else {
if (fb_drivers
[i
].
init) {
pref_init_funcs
[num_pref_init_funcs
++] =
fb_drivers
[i
].
init;
fb_drivers
[i
].
init = NULL
;
}
if (fb_drivers
[i
].
setup)
fb_drivers
[i
].
setup(options
+j
+1);
}
return 0;
}
}
/*
* If we get here no fb was specified.
* We consider the argument to be a global video mode option.
*/
//global_mode_option = options;
return 0;
}
__setup
("video=", video_setup
);
/*
* Visible symbols for modules
*/
EXPORT_SYMBOL
(register_framebuffer
);
EXPORT_SYMBOL
(unregister_framebuffer
);
EXPORT_SYMBOL
(num_registered_fb
);
EXPORT_SYMBOL
(registered_fb
);
EXPORT_SYMBOL
(fb_prepare_logo
);
EXPORT_SYMBOL
(fb_show_logo
);
EXPORT_SYMBOL
(fb_set_var
);
EXPORT_SYMBOL
(fb_blank
);
EXPORT_SYMBOL
(fb_pan_display
);
EXPORT_SYMBOL
(fb_get_buffer_offset
);
EXPORT_SYMBOL
(move_buf_unaligned
);
EXPORT_SYMBOL
(move_buf_aligned
);
MODULE_LICENSE
("GPL");