⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 uvesafb.c

📁 Linux环境下视频显示卡设备的驱动程序源代码
💻 C
📖 第 1 页 / 共 4 页
字号:
/* * 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 + -