本人的项目是一个用oled来显示的android项目;需要在系统睡眠关闭主屏幕的时候,一起把oled屏幕关掉。但这是并没有进入系统睡眠。因此不能使用kernel里的suspend 与resume机制来控制oled的显示和背光。 可以使用framebuffer的功能来从用户态到kernel态来传递命令。
在用户层,可以使用消息的机制,来获得用户关屏的动作,无论是短按电源键还是时间到关屏,都会有一个消息发布;这是上层hardware从可以获得这个消息,然后通过framebuffer来告知kernel层;本文主要说明这个用户层和kernel之间的通信机制。
一. kernel对framebuffer的注册和使用
1. probe函数里的变量定义:
struct fb_info *info;
u32 vmem_size;
struct ssd1307fb_par *par;
u8 *vmem;
2. 主要变量的初始化:(只有部分)
info = framebuffer_alloc(sizeof(struct ssd1307fb_par), &client->dev);
if (!info) {
dev_err(&client->dev, "Couldn't allocate framebuffer.\n");
return -ENOMEM;
}
vmem_size = par->width * par->height / 8;
vmem = vmalloc(vmem_size);
if (!vmem) {
dev_err(&client->dev, "Couldn't allocate graphical memory.\n");
ret = -ENOMEM;
goto fb_alloc_error;
}
info->fbops = &ssd1307fb_ops;
info->fix = ssd1307fb_fix;
info->fix.line_length = par->width / 8;
info->fbdefio = &ssd1307fb_defio;
dev_info(&client->dev, "%s info->fix.line_length=%d\n",
__func__, info->fix.line_length);
info->var = ssd1307fb_var;
info->var.xres = par->width;
info->var.xres_virtual = par->width;
info->var.yres = par->height;
info->var.yres_virtual = par->height;
info->var.red.length = 1;
info->var.red.offset = 0;
info->var.green.length = 1;
info->var.green.offset = 0;
info->var.blue.length = 1;
info->var.blue.offset = 0;
info->screen_base = (char *)vmem;
info->fix.smem_start = (unsigned long)vmem;
info->fix.smem_len = vmem_size;
fb_deferred_io_init(info);
3. 注册
ret = register_framebuffer(info);
if (ret) {
dev_err(&client->dev, "Couldn't register the framebuffer\n");
goto panel_init_error;
}
4. 在上面的注册中,还有一些文件节点需要定义:
static struct fb_fix_screeninfo ssd1307fb_fix = {
.id = "Solomon SSD1307",
.type = FB_TYPE_PACKED_PIXELS,
.visual = FB_VISUAL_MONO10,
.xpanstep = 0,
.ypanstep = 0,
.ywrapstep = 0,
.accel = FB_ACCEL_NONE,
};
static struct fb_var_screeninfo ssd1307fb_var = {
.bits_per_pixel = 1,
};
static struct fb_ops ssd1307fb_ops = {
.owner = THIS_MODULE,
.fb_read = fb_sys_read,
.fb_write = ssd1307fb_write,
.fb_fillrect = ssd1307fb_fillrect,
.fb_copyarea = ssd1307fb_copyarea,
.fb_imageblit = ssd1307fb_imageblit,
.fb_blank = ssd1305fb_blank,
};
static void ssd1307fb_deferred_io(struct fb_info *info,
struct list_head *pagelist)
{
ssd1305fb_update_display(info->par);
}
static struct fb_deferred_io ssd1307fb_defio = {
.delay = HZ,
.deferred_io = ssd1307fb_deferred_io,
};
在上面的注册中, .fb_blank = ssd1305fb_blank, 就是对关屏动作的回调函数的调用。
二、 上层对framebuffer的调用
int fbfd = 0;
struct fb_var_screeninfo vinfo;
struct fb_fix_screeninfo finfo;
long int screensize = 0;
char *fbp = 0;
// Open the file for reading and writing
fbfd = open("/dev/graphics/fb3", O_RDWR);
if (fbfd < 0) {
printf("Error: cannot open framebuffer device.\n");
exit(1);
}
printf("The framebuffer device was opened successfully. fbfd=%x\n", fbfd);
screensize = 128 * 8;
printf("the screensize is %d\n", screensize );
// Map the device to memory
fbp = (char *)mmap(0, screensize, PROT_READ | PROT_WRITE, MAP_SHARED,
fbfd, 0);
if ((int)fbp == -1) {
printf("Error: failed to map framebuffer device to memory.\n");
exit(4);
}
printf("The framebuffer device was mapped to memory successfully.\n");
memcpy(fbp, picture5, screensize);
munmap(fbp, screensize);
sleep(10);
printf("start blank OLED\n");
ioctl(fbfd, FBIOBLANK, 0); // enable bklight
ioctl(fbfd, FBIOBLANK, 1); // disable bkilght
close(fbfd);
在上面中,ioctl(fbfd, FBIOBLANK, 0) ; 函数就是对底层函数ssd1305fb_blank()的调用。