📄 uvesafb.c
字号:
/* * A framebuffer driver for VBE 2.0+ compliant video cards * * (c) 2007 Michal Januszewski <spock@gentoo.org> * Loosely based upon the vesafb driver. * */#include <linux/init.h>#include <linux/module.h>#include <linux/moduleparam.h>#include <linux/skbuff.h>#include <linux/timer.h>#include <linux/completion.h>#include <linux/connector.h>#include <linux/random.h>#include <linux/platform_device.h>#include <linux/limits.h>#include <linux/fb.h>#include <linux/io.h>#include <linux/mutex.h>#include <video/edid.h>#include <video/uvesafb.h>#ifdef CONFIG_X86#include <video/vga.h>#endif#ifdef CONFIG_MTRR#include <asm/mtrr.h>#endif#include "edid.h"static struct cb_id uvesafb_cn_id = { .idx = CN_IDX_V86D, .val = CN_VAL_V86D_UVESAFB};static char v86d_path[PATH_MAX] = "/sbin/v86d";static char v86d_started; /* has v86d been started by uvesafb? */static struct fb_fix_screeninfo uvesafb_fix __devinitdata = { .id = "VESA VGA", .type = FB_TYPE_PACKED_PIXELS, .accel = FB_ACCEL_NONE, .visual = FB_VISUAL_TRUECOLOR,};static int mtrr __devinitdata = 3; /* enable mtrr by default */static int blank = 1; /* enable blanking by default */static int ypan = 1; /* 0: scroll, 1: ypan, 2: ywrap */static int pmi_setpal __devinitdata = 1; /* use PMI for palette changes */static int nocrtc __devinitdata; /* ignore CRTC settings */static int noedid __devinitdata; /* don't try DDC transfers */static int vram_remap __devinitdata; /* set amt. of memory to be used */static int vram_total __devinitdata; /* set total amount of memory */static u16 maxclk __devinitdata; /* maximum pixel clock */static u16 maxvf __devinitdata; /* maximum vertical frequency */static u16 maxhf __devinitdata; /* maximum horizontal frequency */static u16 vbemode __devinitdata; /* force use of a specific VBE mode */static char *mode_option __devinitdata;static struct uvesafb_ktask *uvfb_tasks[UVESAFB_TASKS_MAX];static DEFINE_MUTEX(uvfb_lock);/* * A handler for replies from userspace. * * Make sure each message passes consistency checks and if it does, * find the kernel part of the task struct, copy the registers and * the buffer contents and then complete the task. */static void uvesafb_cn_callback(void *data){ struct cn_msg *msg = data; struct uvesafb_task *utask; struct uvesafb_ktask *task; if (msg->seq >= UVESAFB_TASKS_MAX) return; mutex_lock(&uvfb_lock); task = uvfb_tasks[msg->seq]; if (!task || msg->ack != task->ack) { mutex_unlock(&uvfb_lock); return; } utask = (struct uvesafb_task *)msg->data; /* Sanity checks for the buffer length. */ if (task->t.buf_len < utask->buf_len || utask->buf_len > msg->len - sizeof(*utask)) { mutex_unlock(&uvfb_lock); return; } uvfb_tasks[msg->seq] = NULL; mutex_unlock(&uvfb_lock); memcpy(&task->t, utask, sizeof(*utask)); if (task->t.buf_len && task->buf) memcpy(task->buf, utask + 1, task->t.buf_len); complete(task->done); return;}static int uvesafb_helper_start(void){ char *envp[] = { "HOME=/", "PATH=/sbin:/bin", NULL, }; char *argv[] = { v86d_path, NULL, }; return call_usermodehelper(v86d_path, argv, envp, 1);}/* * Execute a uvesafb task. * * Returns 0 if the task is executed successfully. * * A message sent to the userspace consists of the uvesafb_task * struct and (optionally) a buffer. The uvesafb_task struct is * a simplified version of uvesafb_ktask (its kernel counterpart) * containing only the register values, flags and the length of * the buffer. * * Each message is assigned a sequence number (increased linearly) * and a random ack number. The sequence number is used as a key * for the uvfb_tasks array which holds pointers to uvesafb_ktask * structs for all requests. */static int uvesafb_exec(struct uvesafb_ktask *task){ static int seq; struct cn_msg *m; int err; int len = sizeof(task->t) + task->t.buf_len; /* * Check whether the message isn't longer than the maximum * allowed by connector. */ if (sizeof(*m) + len > CONNECTOR_MAX_MSG_SIZE) { printk(KERN_WARNING "uvesafb: message too long (%d), " "can't execute task\n", (int)(sizeof(*m) + len)); return -E2BIG; } m = kzalloc(sizeof(*m) + len, GFP_KERNEL); if (!m) return -ENOMEM; init_completion(task->done); memcpy(&m->id, &uvesafb_cn_id, sizeof(m->id)); m->seq = seq; m->len = len; m->ack = random32(); /* uvesafb_task structure */ memcpy(m + 1, &task->t, sizeof(task->t)); /* Buffer */ memcpy((u8 *)(m + 1) + sizeof(task->t), task->buf, task->t.buf_len); /* * Save the message ack number so that we can find the kernel * part of this task when a reply is received from userspace. */ task->ack = m->ack; mutex_lock(&uvfb_lock); /* If all slots are taken -- bail out. */ if (uvfb_tasks[seq]) { mutex_unlock(&uvfb_lock); err = -EBUSY; goto out; } /* Save a pointer to the kernel part of the task struct. */ uvfb_tasks[seq] = task; mutex_unlock(&uvfb_lock); err = cn_netlink_send(m, 0, gfp_any()); if (err == -ESRCH) { /* * Try to start the userspace helper if sending * the request failed the first time. */ err = uvesafb_helper_start(); if (err) { printk(KERN_ERR "uvesafb: failed to execute %s\n", v86d_path); printk(KERN_ERR "uvesafb: make sure that the v86d " "helper is installed and executable\n"); } else { v86d_started = 1; err = cn_netlink_send(m, 0, gfp_any()); } } if (!err && !(task->t.flags & TF_EXIT)) err = !wait_for_completion_timeout(task->done, msecs_to_jiffies(UVESAFB_TIMEOUT)); mutex_lock(&uvfb_lock); uvfb_tasks[seq] = NULL; mutex_unlock(&uvfb_lock); seq++; if (seq >= UVESAFB_TASKS_MAX) seq = 0;out: kfree(m); return err;}/* * Free a uvesafb_ktask struct. */static void uvesafb_free(struct uvesafb_ktask *task){ if (task) { if (task->done) kfree(task->done); kfree(task); }}/* * Prepare a uvesafb_ktask struct to be used again. */static void uvesafb_reset(struct uvesafb_ktask *task){ struct completion *cpl = task->done; memset(task, 0, sizeof(*task)); task->done = cpl;}/* * Allocate and prepare a uvesafb_ktask struct. */static struct uvesafb_ktask *uvesafb_prep(void){ struct uvesafb_ktask *task; task = kzalloc(sizeof(*task), GFP_KERNEL); if (task) { task->done = kzalloc(sizeof(*task->done), GFP_KERNEL); if (!task->done) { kfree(task); task = NULL; } } return task;}static void uvesafb_setup_var(struct fb_var_screeninfo *var, struct fb_info *info, struct vbe_mode_ib *mode){ struct uvesafb_par *par = info->par; var->vmode = FB_VMODE_NONINTERLACED; var->sync = FB_SYNC_VERT_HIGH_ACT; var->xres = mode->x_res; var->yres = mode->y_res; var->xres_virtual = mode->x_res; var->yres_virtual = (par->ypan) ? info->fix.smem_len / mode->bytes_per_scan_line : mode->y_res; var->xoffset = 0; var->yoffset = 0; var->bits_per_pixel = mode->bits_per_pixel; if (var->bits_per_pixel == 15) var->bits_per_pixel = 16; if (var->bits_per_pixel > 8) { var->red.offset = mode->red_off; var->red.length = mode->red_len; var->green.offset = mode->green_off; var->green.length = mode->green_len; var->blue.offset = mode->blue_off; var->blue.length = mode->blue_len; var->transp.offset = mode->rsvd_off; var->transp.length = mode->rsvd_len; } else { var->red.offset = 0; var->green.offset = 0; var->blue.offset = 0; var->transp.offset = 0; /* * We're assuming that we can switch the DAC to 8 bits. If * this proves to be incorrect, we'll update the fields * later in set_par(). */ if (par->vbe_ib.capabilities & VBE_CAP_CAN_SWITCH_DAC) { var->red.length = 8; var->green.length = 8; var->blue.length = 8; var->transp.length = 0; } else { var->red.length = 6; var->green.length = 6; var->blue.length = 6; var->transp.length = 0; } }}static int uvesafb_vbe_find_mode(struct uvesafb_par *par, int xres, int yres, int depth, unsigned char flags){ int i, match = -1, h = 0, d = 0x7fffffff; for (i = 0; i < par->vbe_modes_cnt; i++) { h = abs(par->vbe_modes[i].x_res - xres) + abs(par->vbe_modes[i].y_res - yres) + abs(depth - par->vbe_modes[i].depth); /* * We have an exact match in terms of resolution * and depth. */ if (h == 0) return i; if (h < d || (h == d && par->vbe_modes[i].depth > depth)) { d = h; match = i; } } i = 1; if (flags & UVESAFB_EXACT_DEPTH && par->vbe_modes[match].depth != depth) i = 0; if (flags & UVESAFB_EXACT_RES && d > 24) i = 0; if (i != 0) return match; else return -1;}static u8 *uvesafb_vbe_state_save(struct uvesafb_par *par){ struct uvesafb_ktask *task; u8 *state; int err; if (!par->vbe_state_size) return NULL; state = kmalloc(par->vbe_state_size, GFP_KERNEL); if (!state) return NULL; task = uvesafb_prep(); if (!task) { kfree(state); return NULL; } task->t.regs.eax = 0x4f04; task->t.regs.ecx = 0x000f; task->t.regs.edx = 0x0001; task->t.flags = TF_BUF_RET | TF_BUF_ESBX; task->t.buf_len = par->vbe_state_size; task->buf = state; err = uvesafb_exec(task); if (err || (task->t.regs.eax & 0xffff) != 0x004f) { printk(KERN_WARNING "uvesafb: VBE get state call " "failed (eax=0x%x, err=%d)\n", task->t.regs.eax, err); kfree(state); state = NULL; } uvesafb_free(task); return state;}static void uvesafb_vbe_state_restore(struct uvesafb_par *par, u8 *state_buf){ struct uvesafb_ktask *task; int err; if (!state_buf) return; task = uvesafb_prep(); if (!task) return; task->t.regs.eax = 0x4f04; task->t.regs.ecx = 0x000f; task->t.regs.edx = 0x0002; task->t.buf_len = par->vbe_state_size; task->t.flags = TF_BUF_ESBX; task->buf = state_buf; err = uvesafb_exec(task); if (err || (task->t.regs.eax & 0xffff) != 0x004f) printk(KERN_WARNING "uvesafb: VBE state restore call " "failed (eax=0x%x, err=%d)\n", task->t.regs.eax, err); uvesafb_free(task);}static int __devinit uvesafb_vbe_getinfo(struct uvesafb_ktask *task, struct uvesafb_par *par){ int err; task->t.regs.eax = 0x4f00; task->t.flags = TF_VBEIB; task->t.buf_len = sizeof(struct vbe_ib); task->buf = &par->vbe_ib; strncpy(par->vbe_ib.vbe_signature, "VBE2", 4); err = uvesafb_exec(task); if (err || (task->t.regs.eax & 0xffff) != 0x004f) { printk(KERN_ERR "uvesafb: Getting VBE info block failed " "(eax=0x%x, err=%d)\n", (u32)task->t.regs.eax, err); return -EINVAL; } if (par->vbe_ib.vbe_version < 0x0200) { printk(KERN_ERR "uvesafb: Sorry, pre-VBE 2.0 cards are " "not supported.\n"); return -EINVAL; } if (!par->vbe_ib.mode_list_ptr) { printk(KERN_ERR "uvesafb: Missing mode list!\n"); return -EINVAL; } printk(KERN_INFO "uvesafb: "); /* * Convert string pointers and the mode list pointer into * usable addresses. Print informational messages about the * video adapter and its vendor. */ if (par->vbe_ib.oem_vendor_name_ptr) printk("%s, ", ((char *)task->buf) + par->vbe_ib.oem_vendor_name_ptr); if (par->vbe_ib.oem_product_name_ptr) printk("%s, ", ((char *)task->buf) + par->vbe_ib.oem_product_name_ptr); if (par->vbe_ib.oem_product_rev_ptr) printk("%s, ", ((char *)task->buf) + par->vbe_ib.oem_product_rev_ptr); if (par->vbe_ib.oem_string_ptr) printk("OEM: %s, ", ((char *)task->buf) + par->vbe_ib.oem_string_ptr); printk("VBE v%d.%d\n", ((par->vbe_ib.vbe_version & 0xff00) >> 8), par->vbe_ib.vbe_version & 0xff); return 0;}static int __devinit uvesafb_vbe_getmodes(struct uvesafb_ktask *task, struct uvesafb_par *par){ int off = 0, err; u16 *mode; par->vbe_modes_cnt = 0; /* Count available modes. */ mode = (u16 *) (((u8 *)&par->vbe_ib) + par->vbe_ib.mode_list_ptr); while (*mode != 0xffff) { par->vbe_modes_cnt++; mode++; } par->vbe_modes = kzalloc(sizeof(struct vbe_mode_ib) * par->vbe_modes_cnt, GFP_KERNEL); if (!par->vbe_modes) return -ENOMEM; /* Get info about all available modes. */ mode = (u16 *) (((u8 *)&par->vbe_ib) + par->vbe_ib.mode_list_ptr); while (*mode != 0xffff) { struct vbe_mode_ib *mib; uvesafb_reset(task); task->t.regs.eax = 0x4f01; task->t.regs.ecx = (u32) *mode; task->t.flags = TF_BUF_RET | TF_BUF_ESDI; task->t.buf_len = sizeof(struct vbe_mode_ib); task->buf = par->vbe_modes + off; err = uvesafb_exec(task); if (err || (task->t.regs.eax & 0xffff) != 0x004f) { printk(KERN_WARNING "uvesafb: Getting mode info block "
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -