vcam
經過我繳交 pull request 後,老師這樣改名及 desroyed vcamfb 並不好,改名可能會導致追蹤的錯誤,
給我一點建議 要我看 Linux Virtual Framebuffer Device Driver 及 fbset 要我先關注其中記憶體配置的部分
在 virtual_fb.c
中 vfb_init
和 vfb_setup
為 __init
備標註為初始化,
vfb_init
static int __init vfb_init(void)
{
int ret = 0;
#ifndef MODULE
char *option = NULL;
if (fb_get_options("vfb", &option))
return -ENODEV;
vfb_setup(option);
#endif
if (!vfb_enable)
return -ENXIO;
ret = platform_driver_register(&vfb_driver);
if (!ret) {
vfb_device = platform_device_alloc("vfb", 0);
if (vfb_device)
ret = platform_device_add(vfb_device);
else
ret = -ENOMEM;
if (ret) {
platform_device_put(vfb_device);
platform_driver_unregister(&vfb_driver);
}
}
return ret;
}
在 vfb_setup
會設 vfb_enable = 1
platform_driver_register
對 vfb_driver
註冊
platforn_device 其中 probe 的意思是
static struct platform_driver vfb_driver = {
.probe = vfb_probe,
.remove = vfb_remove,
.driver = {
.name = "vfb",
},
};
struct platform_driver
the probe routine is to detect devices residing on the bus and to create device nodes corresponding to these devices.
檢查 devices 並創立 device
vfb_probe
static int vfb_probe(struct platform_device *dev)
{
struct fb_info *info;
unsigned int size = PAGE_ALIGN(videomemorysize);
int retval = -ENOMEM;
/*
* For real video cards we use ioremap.
*/
if (!(videomemory = vmalloc_32_user(size)))
return retval;
info = framebuffer_alloc(sizeof(u32) * 256, &dev->dev);
if (!info)
goto err;
info->screen_base = (char __iomem *)videomemory;
info->fbops = &vfb_ops;
if (!fb_find_mode(&info->var, info, mode_option,
NULL, 0, &vfb_default, 8)){
fb_err(info, "Unable to find usable video mode.\n");
retval = -EINVAL;
goto err1;
}
vfb_fix.smem_start = (unsigned long) videomemory;
vfb_fix.smem_len = videomemorysize;
info->fix = vfb_fix;
info->pseudo_palette = info->par;
info->par = NULL;
info->flags = FBINFO_FLAG_DEFAULT;
retval = fb_alloc_cmap(&info->cmap, 256, 0);
if (retval < 0)
goto err1;
retval = register_framebuffer(info);
if (retval < 0)
goto err2;
platform_set_drvdata(dev, info);
vfb_set_par(info);
fb_info(info, "Virtual frame buffer device, using %ldK of video memory\n",
videomemorysize >> 10);
return 0;
err2:
fb_dealloc_cmap(&info->cmap);
err1:
framebuffer_release(info);
err:
vfree(videomemory);
return retval;
}
vfb_probe
使用 size = 1024 * 1024 * 1 (1MB)
static void *videomemory = vmalloc_32_user(size)
For real video cards we use ioremap. ioremap is map bus memory into CPU space.
void * vmalloc_32_user(unsigned long size)
allocate zeroed virtually contiguous 32bit memory
unsigned long size
allocation size
struct fb_info *info = framebuffer_alloc(sizeof(u32) * 256, &dev->dev)
但不是很清楚為什麼是 1024 bytes
struct platform_device {
const char *name;
int id;
bool id_auto;
struct device dev;
u64 platform_dma_mask;
struct device_dma_parameters dma_parms;
u32 num_resources;
struct resource *resource;
const struct platform_device_id *id_entry;
char *driver_override; /* Driver name to force a match */
/* MFD cell pointer */
struct mfd_cell *mfd_cell;
/* arch specific additions */
struct pdev_archdata archdata;
};
unsigned int size = PAGE_ALIGN(videomemorysize);
其中 unsigned long videomemorysize
fb_alloc_cmap(&info->cmap, 256, 0);
cmap is not used
register_framebuffer(info);
註冊一個 framebuffer
platform_set_drvdata(dev, info);
將 info 寫入 dev 中。
#define PAGE_ALIGN(addr) ALIGN(addr, PAGE_SIZE)
to align the pointer to the (next) page boundary
struct fb_info *framebuffer_alloc(size_t size, struct device *dev)
creates a new frame buffer info structure
struct fb_ops
Frame buffer operations
fb_alloc_cmap(struct fb_cmap *cmap, int len, int transp)
- fb_alloc_cmap - allocate a colormap
- @cmap: frame buffer colormap structure
- @len: length of @cmap
- @transp: boolean, 1 if there is transparency, 0 otherwise
- @flags: flags for kmalloc memory allocation
register_framebuffer(struct fb_info *fb_info)
registers a frame buffer device
platform_set_drvdata(dev, info);
Driver data, set and get with dev_set_drvdata/dev_get_drvdata
vfb_fix.smem_start = (unsigned long) videomemory;
vfb_fix.smem_len = videomemorysize;
info->fix = vfb_fix;
fb_fix_screeninfo
struct fb_fix_screeninfo {
char id[16]; /* identification string eg "TT Builtin" */
unsigned long smem_start; /* Start of frame buffer mem */
/* (physical address) */
__u32 smem_len; /* Length of frame buffer mem */
__u32 type; /* see FB_TYPE_* */
__u32 type_aux; /* Interleave for interleaved Planes */
__u32 visual; /* see FB_VISUAL_* */
__u16 xpanstep; /* zero if no hardware panning */
__u16 ypanstep; /* zero if no hardware panning */
__u16 ywrapstep; /* zero if no hardware ywrap */
__u32 line_length; /* length of a line in bytes */
unsigned long mmio_start; /* Start of Memory Mapped I/O */
/* (physical address) */
__u32 mmio_len; /* Length of Memory Mapped I/O */
__u32 accel; /* Indicate to driver which */
/* specific chip/card we have */
__u16 capabilities; /* see FB_CAP_* */
__u16 reserved[2]; /* Reserved for future compatibility */
};
對應到 他所設的 fb_fix_screeninfo
vfb_fix
static struct fb_fix_screeninfo vfb_fix = {
.id = "Virtual FB",
.type = FB_TYPE_PACKED_PIXELS,
.visual = FB_VISUAL_PSEUDOCOLOR,
.xpanstep = 1,
.ypanstep = 1,
.ywrapstep = 1,
.accel = FB_ACCEL_NONE,
};
vfb_setup
static int __init vfb_setup(char *options)
{
char *this_opt;
vfb_enable = 0;
if (!options)
return 1;
vfb_enable = 1;
if (!*options)
return 1;
while ((this_opt = strsep(&options, ",")) != NULL) {
if (!*this_opt)
continue;
/* Test disable for backwards compatibility */
if (!strcmp(this_opt, "disable"))
vfb_enable = 0;
else
mode_option = this_opt;
}
return 1;
}
#endif /* MODULE */
fb_ops
static struct platform_driver vfb_driver = {
.probe = vfb_probe,
.remove = vfb_remove,
.driver = {
.name = "vfb",
},
};
其中 framebuffer 操作 fb_ops
static const struct fb_ops vfb_ops = {
/* For framebuffers with strange non linear layouts or that do not
* work with normal memory mapped access
*/
.fb_read = fb_sys_read,
.fb_write = fb_sys_write,
.fb_check_var = vfb_check_var, /* checks var and eventually tweaks it to something supported,
* DO NOT MODIFY PAR */
.fb_set_par = vfb_set_par, /* set the video mode according to info->var */
.fb_setcolreg = vfb_setcolreg, /* set color register */
.fb_pan_display = vfb_pan_display, /* pan display */
.fb_fillrect = sys_fillrect, /* Draws a rectangle */
.fb_copyarea = sys_copyarea, /* Copy data from area to another */
.fb_imageblit = sys_imageblit, /* Draws a image to the display */
.fb_mmap = vfb_mmap, /* perform fb specific mmap */
};
set the video mode
static int vfb_set_par(struct fb_info *info)
{
switch (info->var.bits_per_pixel) {
case 1:
info->fix.visual = FB_VISUAL_MONO01;
break;
case 8:
info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
break;
case 16:
case 24:
case 32:
info->fix.visual = FB_VISUAL_TRUECOLOR;
break;
}
info->fix.line_length = get_line_length(info->var.xres_virtual,
info->var.bits_per_pixel);
return 0;
}
#define FB_VISUAL_MONO01 0 /* Monochr. 1=Black 0=White */
#define FB_VISUAL_PSEUDOCOLOR 3 /* Pseudo color (like atari) */
#define FB_VISUAL_TRUECOLOR 2 /* True color */
注意到 case 24 為 rgb 888 沒對其中 設 flag
set the color
static int vfb_check_var(struct fb_var_screeninfo *var,
struct fb_info *info)
{
u_long line_length;
/*
* FB_VMODE_CONUPDATE and FB_VMODE_SMOOTH_XPAN are equal!
* as FB_VMODE_SMOOTH_XPAN is only used internally
*/
if (var->vmode & FB_VMODE_CONUPDATE) {
var->vmode |= FB_VMODE_YWRAP;
var->xoffset = info->var.xoffset;
var->yoffset = info->var.yoffset;
}
/*
* Some very basic checks
*/
if (!var->xres)
var->xres = 1;
if (!var->yres)
var->yres = 1;
if (var->xres > var->xres_virtual)
var->xres_virtual = var->xres;
if (var->yres > var->yres_virtual)
var->yres_virtual = var->yres;
if (var->bits_per_pixel <= 1)
var->bits_per_pixel = 1;
else if (var->bits_per_pixel <= 8)
var->bits_per_pixel = 8;
else if (var->bits_per_pixel <= 16)
var->bits_per_pixel = 16;
else if (var->bits_per_pixel <= 24)
var->bits_per_pixel = 24;
else if (var->bits_per_pixel <= 32)
var->bits_per_pixel = 32;
else
return -EINVAL;
if (var->xres_virtual < var->xoffset + var->xres)
var->xres_virtual = var->xoffset + var->xres;
if (var->yres_virtual < var->yoffset + var->yres)
var->yres_virtual = var->yoffset + var->yres;
/*
* Memory limit
*/
line_length =
get_line_length(var->xres_virtual, var->bits_per_pixel);
if (line_length * var->yres_virtual > videomemorysize)
return -ENOMEM;
/*
* Now that we checked it we alter var. The reason being is that the video
* mode passed in might not work but slight changes to it might make it
* work. This way we let the user know what is acceptable.
*/
switch (var->bits_per_pixel) {
case 24: /* RGB 888 */
var->red.offset = 0;
var->red.length = 8;
var->green.offset = 8;
var->green.length = 8;
var->blue.offset = 16;
var->blue.length = 8;
var->transp.offset = 0;
var->transp.length = 0;
break;
case 32: /* RGBA 8888 */
var->red.offset = 0;
var->red.length = 8;
var->green.offset = 8;
var->green.length = 8;
var->blue.offset = 16;
var->blue.length = 8;
var->transp.offset = 24;
var->transp.length = 8;
break;
}
var->red.msb_right = 0;
var->green.msb_right = 0;
var->blue.msb_right = 0;
var->transp.msb_right = 0;
return 0;
}
get_line_length
為 bytpesperline 的意思 weight * 3 for rgb888
vfb_mmap
static int vfb_mmap(struct fb_info *info,
struct vm_area_struct *vma)
{
return remap_vmalloc_range(vma, (void *)info->fix.smem_start, vma->vm_pgoff);
}
int remap_vmalloc_range(struct vm_area_struct *vma, void *addr, unsigned long pgoff)
map vmalloc pages to userspace
This function checks that addr is a valid vmalloc’ed area, and that it is big enough to cover the vma. Will return failure if that criteria isn’t met.
struct vm_area_struct *vma
vma to cover (map full range of vma)
void *addr
vmalloc memory
unsigned long pgoff
number of pages into addr before first page to map
檢查 mmap 的大小。 以大小府和 640 * 480
參考資料:
remap_vmalloc_range
vfb_setcolreg
static int vfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
u_int transp, struct fb_info *info)
{
if (regno >= 256) /* no. of hw registers */
return 1;
/*
* Program hardware... do anything you want with transp
*/
/* grayscale works only partially under directcolor */
if (info->var.grayscale) {
/* grayscale = 0.30*R + 0.59*G + 0.11*B */
red = green = blue =
(red * 77 + green * 151 + blue * 28) >> 8;
}
/* Directcolor:
* var->{color}.offset contains start of bitfield
* var->{color}.length contains length of bitfield
* {hardwarespecific} contains width of RAMDAC
* cmap[X] is programmed to (X << red.offset) | (X << green.offset) | (X << blue.offset)
* RAMDAC[X] is programmed to (red, green, blue)
*
* Pseudocolor:
* var->{color}.offset is 0 unless the palette index takes less than
* bits_per_pixel bits and is stored in the upper
* bits of the pixel value
* var->{color}.length is set so that 1 << length is the number of available
* palette entries
* cmap is not used
* RAMDAC[X] is programmed to (red, green, blue)
*
* Truecolor:
* does not use DAC. Usually 3 are present.
* var->{color}.offset contains start of bitfield
* var->{color}.length contains length of bitfield
* cmap is programmed to (red << red.offset) | (green << green.offset) |
* (blue << blue.offset) | (transp << transp.offset)
* RAMDAC does not exist
*/
#define CNVT_TOHW(val,width) ((((val)<<(width))+0x7FFF-(val))>>16)
switch (info->fix.visual) {
case FB_VISUAL_TRUECOLOR:
case FB_VISUAL_PSEUDOCOLOR:
red = CNVT_TOHW(red, info->var.red.length);
green = CNVT_TOHW(green, info->var.green.length);
blue = CNVT_TOHW(blue, info->var.blue.length);
transp = CNVT_TOHW(transp, info->var.transp.length);
break;
case FB_VISUAL_DIRECTCOLOR:
red = CNVT_TOHW(red, 8); /* expect 8 bit DAC */
green = CNVT_TOHW(green, 8);
blue = CNVT_TOHW(blue, 8);
/* hey, there is bug in transp handling... */
transp = CNVT_TOHW(transp, 8);
break;
}
#undef CNVT_TOHW
/* Truecolor has hardware independent palette */
if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
u32 v;
if (regno >= 16)
return 1;
v = (red << info->var.red.offset) |
(green << info->var.green.offset) |
(blue << info->var.blue.offset) |
(transp << info->var.transp.offset);
switch (info->var.bits_per_pixel) {
case 8:
break;
case 16:
((u32 *) (info->pseudo_palette))[regno] = v;
break;
case 24:
case 32:
((u32 *) (info->pseudo_palette))[regno] = v;
break;
}
return 0;
}
return 0;
}
其中 在 CNVT_TOHW 標示出 rgb bit 的位置,但是 rgb 24 可以就不需要,因為 case 24 本來就沒設立 flag
vfb_pan_display
/*
* Pan or Wrap the Display
*
* This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag
*/
static int vfb_pan_display(struct fb_var_screeninfo *var,
struct fb_info *info)
{
if (var->vmode & FB_VMODE_YWRAP) {
if (var->yoffset >= info->var.yres_virtual ||
var->xoffset)
return -EINVAL;
} else {
if (var->xoffset + info->var.xres > info->var.xres_virtual ||
var->yoffset + info->var.yres > info->var.yres_virtual)
return -EINVAL;
}
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;
}
會設定 xoffset
yoffset
也會修改成 vmode == FB_VMODE_YWRAP
sysfillrect.c 為 ops sys_fillrect
syscopyarea.c 為 ops sys_copyarea
sysimgblt.c 為 ops sys_imgbit
其中為使用在各 .c 檔的 function 。