Re: [PATCH 3/4] fbdev: uvesafb driver

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Sat, 23 Jun 2007 12:52:43 +0200 Michal Januszewski <[email protected]> wrote:

> Add the uvesafb driver, an enhanced version of vesafb that makes use of
> a userspace helper to run x86 Video BIOS code.
> 
> Signed-off-by: Michal Januszewski <[email protected]>
> ---
>  drivers/video/Kconfig   |   18 +
>  drivers/video/Makefile  |    1 +
>  drivers/video/uvesafb.c | 1902 +++++++++++++++++++++++++++++++++++++++++++++++
>  include/video/Kbuild    |    2 +-
>  include/video/uvesafb.h |  208 ++++++
>  5 files changed, 2130 insertions(+), 1 deletions(-)
> 
> diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
> index 403dac7..5cc03f9 100644
> --- a/drivers/video/Kconfig
> +++ b/drivers/video/Kconfig
> @@ -585,6 +585,24 @@ config FB_TGA
>  
>  	  Say Y if you have one of those.
>  
> +config FB_UVESA
> +	tristate "Userspace VESA VGA graphics support"
> +	depends on FB && CONNECTOR

These dependencies are insufficient.

> ...
>
> --- /dev/null
> +++ b/drivers/video/uvesafb.c
> @@ -0,0 +1,1902 @@
> +/*
> + * A framebuffer driver for VBE 2.0+ compliant video cards
> + *
> + * (c) 2007 Michal Januszewski <[email protected]>
> + *     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 <video/edid.h>
> +#include <video/vga.h>
> +#include <video/uvesafb.h>
> +#include <asm/io.h>
> +#include <asm/mtrr.h>

Only x86 has mtrr.h: you just broke allmodconfig on all other
architectures.

> +#include "edid.h"
> +
> +static struct cb_id uvesafb_cn_id = {
> +	.idx = CN_IDX_V86D,
> +	.val = CN_VAL_V86D_UVESAFB
> +};
> +static struct sock *nls;
> +static char v86d_path[PATH_MAX] = "/sbin/v86d";

Remove the PATH_MAX, save some memory.

Oh, it gets set via sysfs.  hrm.


> +static char v86d_started = 0;	/* has v86d been started by uvesafb? */

Unneeded initialisation-to-zero.

scripts/checkpatch.pl would have reported this.  It in fact generates 93
warnings against this patch and from a quick scan, they all look
legitimate.

> +static void uvesafb_cn_callback(void *data)
> +{
> +	struct cn_msg *msg = (struct cn_msg *)data;

unneeded cast of void*

> +	struct uvesafb_task *utask = (struct uvesafb_task *)msg->data;
> +	struct uvesafb_ktask *task;
> +
> +	if (msg->seq >= UVESAFB_TASKS_MAX)
> +		return;
> +
> +	task = uvfb_tasks[msg->seq];
> +
> +	if (!task || msg->ack != task->ack)
> +		return;
> +
> +	memcpy(&task->t, utask, sizeof(struct uvesafb_task));
> +
> +	if (task->t.buf_len && task->buf)
> +		memcpy(task->buf, ((u8*)utask) + sizeof(struct uvesafb_task),
> +						task->t.buf_len);

Isn't this

	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);
> +}

Now I'm wondering what this code actually does.  It would be nice to have
an overall design and implementation description in the changelog so we
don't have to reverse-engineer your thoughts from your implementation...

The comment over uvesafb_exec() helps.

> +static struct uvesafb_ktask *uvesafb_prep(void)
> +{
> +	struct uvesafb_ktask *task;
> +
> +	task = kzalloc(sizeof(struct uvesafb_ktask), GFP_ATOMIC);
> +	if (task) {
> +		task->done = kzalloc(sizeof(struct completion), GFP_ATOMIC);
> +		if (!task->done) {
> +			kfree(task);
> +			task = NULL;
> +		}
> +	}
> +	return task;
> +}

GFP_ATOMIC is unreliable and should be strenuously avoided.  I suspect all
callers can use GFP_KERNEL here.  If not, we should at least pass the gfp_t
into this function as an argument so that we can use GFP_KERNEL from as
many callsites as possible.

> +/*
> + * 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 = 0;
> +	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 exec task\n", (int)(sizeof(*m) + len));
> +		return -E2BIG;
> +	}
> +
> +	m = kmalloc(sizeof(*m) + len, GFP_ATOMIC);

More GFP_ATOMIC badness

> +	if (!m)
> +		return -ENOMEM;
> +
> +	init_completion(task->done);
> +
> +	memset(m, 0, sizeof(*m) + len);

kzalloc()

> +	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 + sizeof(task->t) + sizeof(*m),
> +		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;
> +
> +	/* If all slots are taken -- bail out. */
> +	if (uvfb_tasks[seq])
> +		return -EBUSY;
> +
> +	/* Save a pointer to the kernel part of the task struct. */
> +	uvfb_tasks[seq] = task;
> +
> +	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());
> +		}
> +	}
> +	kfree(m);
> +
> +	if (!err && !(task->t.flags & TF_EXIT))
> +		err = !wait_for_completion_timeout(task->done,
> +				msecs_to_jiffies(UVESAFB_TIMEOUT));

If you can run wait_for_completion() here then you can use GFP_KERNEL up
there.

> +	uvfb_tasks[seq] = NULL;
> +
> +	seq++;
> +	if (seq >= UVESAFB_TASKS_MAX)
> +		seq = 0;
> +
> +	return err;
> +}
>
> ...
>
> +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 = (u8*) &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=%x)\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;

whitespace went wonky here.

> +	}
> +
> +	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. */

Commenting style is

	/* Convert string pointers and the mode list pointer into
	 * usable addresses. Print informational messages about the
	 * video adapter and its vendor.
	 */

or

	/*
	 * 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 = (u8*) (par->vbe_modes + off);
> +
> +		err = uvesafb_exec(task);
> +		if (err || (task->t.regs.eax & 0xffff) != 0x004f) {
> +			printk(KERN_ERR "uvesafb: Getting mode info block "
> +				"for mode 0x%x failed (eax=0x%x, err=%x)\n",
> +				*mode, (u32)task->t.regs.eax, err);
> +			return -EINVAL;
> +		}
> +
> +		mib = (struct vbe_mode_ib*)task->buf;

Perhaps uvesafb_ktask.buf should be void* so we can lose some typecasts.

> +		mib->mode_id = *mode;
> +
> +		/* We only want modes that are supported with the current
> +		 * hardware configuration, color, graphics and that have
> +		 * support for the LFB. */
> +		if ((mib->mode_attr & VBE_MODE_MASK) == VBE_MODE_MASK &&
> +		     mib->bits_per_pixel >= 8) {
> +			off++;
> +		} else {
> +			par->vbe_modes_cnt--;
> +		}
> +		mode++;
> +		mib->depth = mib->red_len + mib->green_len + mib->blue_len;
> +
> +		/* Handle 8bpp modes and modes with broken color component
> +		 * lengths. */
> +		if (mib->depth == 0 || (mib->depth == 24 &&
> +					mib->bits_per_pixel == 32))
> +			mib->depth = mib->bits_per_pixel;
> +	}
> +
> +	return 0;
> +}
> +
> +#ifdef __i386__

CONFIG_X86 would be more typical.

Be aware that CONFIG_X86 is true for both i386 and x86_64.  You don't state
whether this code works on x86_64.  If it can, it should.

> +static int __devinit uvesafb_vbe_getpmi(struct uvesafb_ktask *task,
> +		struct uvesafb_par *par)
> +{
> +	int i, err;
> +
> +	uvesafb_reset(task);
> +	task->t.regs.eax = 0x4f0a;
> +	task->t.regs.ebx = 0x0;
> +	err = uvesafb_exec(task);
> +
> +	if ((task->t.regs.eax & 0xffff) != 0x4f || task->t.regs.es < 0xc000) {
> +		par->pmi_setpal = par->ypan = 0;
> +	} else {
> +		par->pmi_base = (u16*)phys_to_virt(((u32)task->t.regs.es << 4)
> +						+ task->t.regs.edi);
> +		par->pmi_start = (void*)((char*)par->pmi_base +
> +						par->pmi_base[1]);
> +		par->pmi_pal = (void*)((char*)par->pmi_base +
> +						par->pmi_base[2]);
> +		printk(KERN_INFO "uvesafb: protected mode interface info at "
> +				 "%04x:%04x\n",
> +				 (u16)task->t.regs.es, (u16)task->t.regs.edi);
> +		printk(KERN_INFO "uvesafb: pmi: set display start = %p, "
> +				 "set palette = %p\n", par->pmi_start,
> +				 par->pmi_pal);
> +
> +		if (par->pmi_base[3]) {
> +			printk(KERN_INFO "uvesafb: pmi: ports = ");
> +			for (i = par->pmi_base[3]/2;
> +					par->pmi_base[i] != 0xffff; i++)
> +				printk("%x ", par->pmi_base[i]);
> +			printk("\n");
> +
> +			if (par->pmi_base[i] != 0xffff) {
> +				printk(KERN_INFO "uvesafb: can't handle memory"
> +						 " requests, pmi disabled\n");
> +				par->ypan = par->pmi_setpal = 0;
> +			}
> +		}
> +	}
> +	return 0;
> +}
> +#endif

You might care to cc "H.  Peter Anvin" <[email protected]> on the next version
of this patch - he's a whizz at this sort of low-level x86 bios
communication stuff.

> +/* Check whether a video mode is supported by the Video BIOS and is
> + * compatible with the monitor limits. */
> +static int __devinit uvesafb_is_valid_mode(struct fb_videomode *mode,
> +		struct fb_info *info)
> +{
> +	if (info->monspecs.gtf) {
> +		fb_videomode_to_var(&info->var, mode);
> +		if (fb_validate_mode(&info->var, info))
> +			return -1;
> +	}
> +
> +	if (uvesafb_vbe_find_mode(info->par, mode->xres, mode->yres, 8,
> +				UVESAFB_NEED_EXACT_RES) == -1)
> +		return -1;
> +
> +	return 0;
> +}
> +
> +static int __devinit uvesafb_vbe_getedid(struct uvesafb_ktask *task,
> +		struct fb_info *info)
> +{
> +	struct uvesafb_par *par = (struct uvesafb_par *)info->par;

fb_info.par has type void* hence all casts such as the above can and should
be removed.

> +	int err = 0;
> +
> +	if (noedid || par->vbe_ib.vbe_version < 0x0300)
> +		return -EINVAL;
> +
> +	task->t.regs.eax = 0x4f15;
> +	task->t.regs.ebx = 0;
> +	task->t.regs.ecx = 0;
> +	task->t.buf_len = 0;
> +	task->t.flags = 0;
> +
> +	err = uvesafb_exec(task);
> +
> +	if ((task->t.regs.eax & 0xffff) != 0x004f || err)
> +		return -EINVAL;
> +
> +	if ((task->t.regs.ebx & 0x3) == 3) {
> +		printk(KERN_INFO "uvesafb: VBIOS/hardware supports both "
> +				 "DDC1 and DDC2 transfers\n");
> +	} else if ((task->t.regs.ebx & 0x3) == 2) {
> +		printk(KERN_INFO "uvesafb: VBIOS/hardware supports DDC2 "
> +				 "transfers\n");
> +	} else if ((task->t.regs.ebx & 0x3) == 1) {
> +		printk(KERN_INFO "uvesafb: VBIOS/hardware supports DDC1 "
> +				 "transfers\n");
> +	} else {
> +		printk(KERN_INFO "uvesafb: VBIOS/hardware doesn't support "
> +				 "DDC transfers\n");
> +		return -EINVAL;
> +	}
> +
> +	task->t.regs.eax = 0x4f15;
> +	task->t.regs.ebx = 1;
> +	task->t.regs.ecx = task->t.regs.edx = 0;
> +	task->t.flags = TF_BUF_RET | TF_BUF_ESDI;
> +	task->t.buf_len = EDID_LENGTH;
> +	task->buf = kzalloc(EDID_LENGTH, GFP_KERNEL);
> +
> +	err = uvesafb_exec(task);
> +
> +	if ((task->t.regs.eax & 0xffff) == 0x004f && !err) {
> +		fb_edid_to_monspecs(task->buf, &info->monspecs);
> +
> +		if (info->monspecs.vfmax && info->monspecs.hfmax) {
> +			/* If the maximum pixel clock wasn't specified in
> +			 * the EDID block, set it to 300 MHz. */
> +			if (info->monspecs.dclkmax == 0)
> +				info->monspecs.dclkmax = 300 * 1000000;
> +			info->monspecs.gtf = 1;
> +		}
> +	} else {
> +		err = -EINVAL;
> +	}
> +
> +	kfree(task->buf);
> +	return err;
> +}
> +
> +static void __devinit uvesafb_vbe_getmonspecs(struct uvesafb_ktask *task,
> +		struct fb_info *info)
> +{
> +	struct uvesafb_par *par = info->par;
> +	int i;
> +	memset(&info->monspecs, 0, sizeof(struct fb_monspecs));

Missing a newline above.

> +	/* If we don't get all necessary data from the EDID block,
> +	 * mark it as incompatible with the GTF. */
> +	if (uvesafb_vbe_getedid(task, info))
> +		info->monspecs.gtf = 0;
> +
> +	/* Kernel command line overrides. */
> +	if (maxclk)
> +		info->monspecs.dclkmax = maxclk * 1000000;
> +	if (maxvf)
> +		info->monspecs.vfmax = maxvf;
> +	if (maxhf)
> +		info->monspecs.hfmax = maxhf * 1000;
> +
> +	/* In case DDC transfers are not supported the user can provide
> +	 * monitor limits manually. Lower limits are set to "safe" values. */
> +	if (info->monspecs.gtf == 0 && maxclk && maxvf && maxhf) {
> +		info->monspecs.dclkmin = 0;
> +		info->monspecs.vfmin = 60;
> +		info->monspecs.hfmin = 29000;
> +		info->monspecs.gtf = 1;
> +	}
> +
> +	if (info->monspecs.gtf)
> +		printk(KERN_INFO
> +			"uvesafb: monitor limits: vf = %d Hz, hf = %d kHz, "
> +			"clk = %d MHz\n", info->monspecs.vfmax,
> +			(int)(info->monspecs.hfmax / 1000),
> +			(int)(info->monspecs.dclkmax / 1000000));
> +	else
> +		printk(KERN_INFO "uvesafb: no monitor limits have been set\n");
> +
> +	/* Add VBE modes to the modelist. */
> +	for (i = 0; i < par->vbe_modes_cnt; i++) {
> +		struct fb_var_screeninfo var;
> +		struct vbe_mode_ib *mode;
> +		struct fb_videomode vmode;
> +
> +		mode = &par->vbe_modes[i];
> +		memset(&var, 0, sizeof(var));
> +
> +		var.xres = mode->x_res;
> +		var.yres = mode->y_res;
> +
> +		fb_get_mode(FB_VSYNCTIMINGS | FB_IGNOREMON, 60, &var, info);
> +		fb_var_to_videomode(&vmode, &var);
> +		fb_add_videomode(&vmode, &info->modelist);
> +	}
> +
> +	/* Add valid VESA modes to our modelist. */
> +	for (i = 0; i < VESA_MODEDB_SIZE; i++) {
> +		if (!uvesafb_is_valid_mode((struct fb_videomode*)
> +						&vesa_modes[i], info))
> +			fb_add_videomode(&vesa_modes[i], &info->modelist);
> +	}
> +
> +	for (i = 0; i < info->monspecs.modedb_len; i++) {
> +		if (!uvesafb_is_valid_mode(&info->monspecs.modedb[i], info))
> +			fb_add_videomode(&info->monspecs.modedb[i],
> +					&info->modelist);
> +	}
> +
> +	return;
> +}
> +
> +static void __devinit uvesafb_vbe_getstatesize(struct uvesafb_ktask *task,
> +		struct uvesafb_par *par)
> +{
> +	int err;
> +	uvesafb_reset(task);

Here also.

> +	/* Get the VBE state buffer size. We want all available
> +	 * hardware state data (CL = 0x0f). */
> +	task->t.regs.eax = 0x4f04;
> +	task->t.regs.ecx = 0x000f;
> +	task->t.regs.edx = 0x0000;
> +	task->t.flags = 0;
> +
> +	err = uvesafb_exec(task);
> +
> +	if (err || (task->t.regs.eax & 0xffff) != 0x004f) {
> +		printk(KERN_WARNING "uvesafb: VBE state buffer size "
> +			"cannot be determined (eax=0x%x, err=%d)\n",
> +			task->t.regs.eax, err);
> +		par->vbe_state_size = 0;
> +		return;
> +	}
> +
> +	par->vbe_state_size = 64 * (task->t.regs.ebx & 0xffff);
> +}
> +
> +static int __devinit uvesafb_vbe_init(struct fb_info *info)
> +{
> +	struct uvesafb_ktask *task = NULL;
> +	struct uvesafb_par *par = info->par;
> +	int err;
> +
> +	task = uvesafb_prep();
> +	if (!task)
> +		return -ENOMEM;
> +
> +	if ((err = uvesafb_vbe_getinfo(task, par)))

checkpatch.pl will do my work here..

> +		goto out;
> +	if ((err = uvesafb_vbe_getmodes(task, par)))
> +		goto out;
> +	par->nocrtc = nocrtc;
> +#ifdef __i386__
> +	par->pmi_setpal = pmi_setpal;
> +	par->ypan = ypan;
> +
> +	if (par->pmi_setpal || par->ypan)
> +		uvesafb_vbe_getpmi(task, par);
> +#else
> +	/* The protected mode interface is not available on non-x86. */
> +	par->pmi_setpal = par->ypan = 0;
> +#endif
> +
> +	INIT_LIST_HEAD(&info->modelist);
> +	uvesafb_vbe_getmonspecs(task, info);
> +	uvesafb_vbe_getstatesize(task, par);
> +
> +out:	uvesafb_free(task);
> +	return err;
> +}
> +
> +static int __devinit uvesafb_vbe_init_mode(struct fb_info *info)
> +{
> +	struct list_head *pos;
> +	struct fb_modelist *modelist;
> +	struct fb_videomode *mode;
> +	struct uvesafb_par *par = info->par;
> +	int i, modeid;
> +
> +	/* Has the user requested a specific VESA mode? */
> +	if (vbemode) {
> +		for (i = 0; i < par->vbe_modes_cnt; i++) {
> +			if (par->vbe_modes[i].mode_id == vbemode) {
> +				fb_get_mode(FB_VSYNCTIMINGS | FB_IGNOREMON, 60,
> +							&info->var, info);
> +				/* With pixclock set to 0, the default BIOS
> +				 * timings will be used in set_par(). */
> +				info->var.pixclock = 0;
> +				modeid = i;
> +				goto gotmode;
> +			}
> +		}
> +		printk(KERN_INFO "uvesafb: requested VBE mode 0x%x is "
> +				 "unavailable\n", vbemode);
> +		vbemode = 0;
> +	}
> +
> +	i = 0;
> +	list_for_each(pos, &info->modelist) {
> +		i++;
> +	}

Unneeded braces

> +	mode = kzalloc(i * sizeof(*mode), GFP_KERNEL);

Check for and handle the allocation failure, please.

> +	i = 0;
> +	list_for_each(pos, &info->modelist) {
> +		modelist = list_entry(pos, struct fb_modelist, list);
> +		mode[i] = modelist->mode;
> +		i++;
> +	}
> +
> +	if (!mode_option)
> +		mode_option = UVESAFB_DEFAULT_MODE;
> +
> +	i = fb_find_mode(&info->var, info, mode_option, mode, i,
> +		NULL, 8);
> +
> +	kfree(mode);
> +
> +	/* fb_find_mode() failed */
> +	if (i == 0 || i >= 3) {
> +		info->var.xres = 640;
> +		info->var.yres = 480;
> +		mode = (struct fb_videomode*)
> +				fb_find_best_mode(&info->var, &info->modelist);
> +
> +		if (mode) {
> +			fb_videomode_to_var(&info->var, mode);
> +		} else {
> +			modeid = par->vbe_modes[0].mode_id;
> +			fb_get_mode(FB_VSYNCTIMINGS | FB_IGNOREMON, 60,
> +				    &info->var, info);
> +			goto gotmode;
> +		}
> +	}
> +
> +	/* Look for a matching VBE mode. */
> +	modeid = uvesafb_vbe_find_mode(par, info->var.xres, info->var.yres,
> +			info->var.bits_per_pixel, UVESAFB_NEED_EXACT_RES);
> +
> +	if (modeid == -1)
> +		return -EINVAL;
> +
> +gotmode:
> +	uvesafb_setup_var(&info->var, info, &par->vbe_modes[modeid]);
> +
> +	/* If we are not VBE3.0+ compliant, we're done -- the BIOS will
> +	 * ignore our mode timings anyway. */
> +	if (par->vbe_ib.vbe_version < 0x0300)
> +		fb_get_mode(FB_VSYNCTIMINGS | FB_IGNOREMON, 60,
> +					&info->var, info);
> +
> +	return modeid;
> +}
> +
> +static int uvesafb_setpalette(struct uvesafb_pal_entry *entries, int count,
> +			     int start, struct fb_info *info)
> +{
> +	struct uvesafb_ktask *task;
> +	struct uvesafb_par *par = info->par;
> +	int i = par->mode_idx;
> +	int err = 0;
> +
> +	/* We support palette modifications for 8 bpp modes only, so
> +	 * there can never be more than 256 entries. */
> +	if (start + count > 256)
> +		return -EINVAL;
> +
> +	/* Use VGA registers if mode is VGA-compatible. */
> +	if (i >= 0 && i < par->vbe_modes_cnt &&
> +	    par->vbe_modes[i].mode_attr & VBE_MODE_VGACOMPAT) {
> +		for (i = 0; i < count; i++) {
> +			outb_p(start + i,        dac_reg);
> +			outb_p(entries[i].red,   dac_val);
> +			outb_p(entries[i].green, dac_val);
> +			outb_p(entries[i].blue,  dac_val);
> +		}
> +	} else if (par->pmi_setpal) {
> +		__asm__ __volatile__(
> +		"call *(%%esi)"
> +		: /* no return value */
> +		: "a" (0x4f09),         /* EAX */
> +		  "b" (0),              /* EBX */
> +		  "c" (count),          /* ECX */
> +		  "d" (start),          /* EDX */
> +		  "D" (entries),        /* EDI */
> +		  "S" (&par->pmi_pal)); /* ESI */

uh, so we're CONFIG_X86_32-only, yes?

> +	} else {
> +		task = uvesafb_prep();
> +		if (!task)
> +			return -ENOMEM;
> +
> +		task->t.regs.eax = 0x4f09;
> +		task->t.regs.ebx = 0x0;
> +		task->t.regs.ecx = count;
> +		task->t.regs.edx = start;
> +		task->t.flags = TF_BUF_ESDI;
> +		task->t.buf_len = sizeof(struct uvesafb_pal_entry) * count;
> +		task->buf = (u8*) entries;
> +
> +		err = uvesafb_exec(task);
> +		if ((task->t.regs.eax & 0xffff) != 0x004f)
> +			err = 1;
> +
> +		uvesafb_free(task);
> +	}
> +	return err;
> +}
> +
>
> ...
>
> +
> +static struct device_attribute device_attrs[] = {
> +	__ATTR(vbe_version, S_IRUGO, uvesafb_show_vbe_ver, NULL),
> +	__ATTR(vbe_modes, S_IRUGO, uvesafb_show_vbe_modes, NULL),
> +	__ATTR(oem_vendor, S_IRUGO, uvesafb_show_vendor, NULL),
> +	__ATTR(oem_product_name, S_IRUGO, uvesafb_show_product_name, NULL),
> +	__ATTR(oem_product_rev, S_IRUGO, uvesafb_show_product_rev, NULL),
> +	__ATTR(oem_string, S_IRUGO, uvesafb_show_oem_string, NULL),
> +	__ATTR(nocrtc, S_IRUGO | S_IWUSR, uvesafb_show_nocrtc,
> +		uvesafb_store_nocrtc),
> +};

Can we use an attribute group here?

> +static void __devinit uvesafb_create_sysfs(struct device *dev,
> +		struct fb_info *info)
> +{
> +	int i, err = 0;
> +
> +	for (i = 0; i < ARRAY_SIZE(device_attrs); i++) {
> +		err |= device_create_file(dev, &device_attrs[i]);
> +	}

Unneeded braces

> +	if (err != 0)
> +		printk(KERN_WARNING "fb%d: failed to register attributes\n",
> +			info->node);
> +}
> +
> +static void __devinit uvesafb_remove_sysfs(struct device *dev)
> +{
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(device_attrs); i++) {
> +		device_remove_file(dev, &device_attrs[i]);
> +	}

ditto

> +}
> +
> +static int __devinit uvesafb_probe(struct platform_device *dev)
> +{
> +	struct fb_info *info;
> +	struct vbe_mode_ib *mode = NULL;
> +	struct uvesafb_par *par;
> +	int err = 0, i;
> +
> +	info = framebuffer_alloc(sizeof(*par) +	sizeof(u32) * 256, &dev->dev);
> +	if (!info)
> +		return -ENOMEM;
> +
> +	par = info->par;
> +
> +	if ((err = uvesafb_vbe_init(info))) {
> +		printk(KERN_ERR "uvesafb: vbe_init() failed with %d\n", err);
> +		goto out;
> +	}
> +
> +	info->fbops = &uvesafb_ops;
> +
> +	i = uvesafb_vbe_init_mode(info);
> +	if (i < 0) {
> +		err = -EINVAL;
> +		goto out;
> +	} else {
> +		mode = &par->vbe_modes[i];
> +	}
> +
> +	if (fb_alloc_cmap(&info->cmap, 256, 0) < 0) {
> +		err = -ENXIO;
> +		goto out;
> +	}
> +
> +	uvesafb_init_info(info, mode);
> +
> +	if (!request_mem_region(info->fix.smem_start, info->fix.smem_len,
> +	    "uvesafb")) {
> +		printk(KERN_WARNING "uvesafb: cannot reserve video memory at "
> +		       "0x%lx\n", info->fix.smem_start);
> +		/* We cannot make this fatal. Sometimes this comes from magic
> +		   spaces our resource handlers simply don't know about. */

so...  what happens?  The driver starts altering mrmory regions which it
doesn't own?

> +	}
> +
> +	info->screen_base = ioremap(info->fix.smem_start, info->fix.smem_len);
> +
> +	if (!info->screen_base) {
> +		printk(KERN_ERR
> +		       "uvesafb: abort, cannot ioremap 0x%x bytes of video "
> +		       "memory at 0x%lx\n",
> +		       info->fix.smem_len, info->fix.smem_start);
> +		err = -EIO;
> +		goto out_mem;
> +	}
> +
> +	if (!request_region(0x3c0, 32, "uvesafb")) {
> +		printk(KERN_ERR "uvesafb: request region 0x3c0-0x3e0 failed\n");
> +		err = -EIO;
> +		goto out_unmap;
> +	}
> +
> +	uvesafb_init_mtrr(info);
> +	platform_set_drvdata(dev, info);
> +
> +	if (register_framebuffer(info) < 0) {
> +		printk(KERN_ERR
> +		       "uvesafb: failed to register framebuffer device\n");
> +		err = -EINVAL;
> +		goto out_reg;
> +	}
> +
> +	printk(KERN_INFO "uvesafb: framebuffer at 0x%lx, mapped to 0x%p, "
> +		       "using %dk, total %dk\n", info->fix.smem_start,
> +		       info->screen_base, info->fix.smem_len/1024,
> +		       par->vbe_ib.total_memory * 64);
> +	printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node,
> +		       info->fix.id);
> +
> +	uvesafb_create_sysfs(&dev->dev, info);
> +	return 0;
> +
> +out_reg:
> +	release_region(0x3c0, 32);
> +out_unmap:
> +	iounmap(info->screen_base);
> +out_mem:
> +	release_mem_region(info->fix.smem_start, info->fix.smem_len);
> +	if (!list_empty(&info->modelist))
> +		fb_destroy_modelist(&info->modelist);
> +	fb_destroy_modedb(info->monspecs.modedb);
> +	fb_dealloc_cmap(&info->cmap);
> +out:
> +	if (par->vbe_modes)
> +		kfree(par->vbe_modes);
> +
> +	framebuffer_release(info);
> +	return err;
> +}
>
> ...
>
> +#ifndef MODULE
> +static int __devinit uvesafb_setup(char *options)
> +{
> +	char *this_opt;
> +
> +	if (!options || !*options)
> +		return 0;
> +
> +	while ((this_opt = strsep(&options, ",")) != NULL) {
> +		if (!*this_opt) continue;
> +
> +		if (!strcmp(this_opt, "redraw"))
> +			ypan = 0;
> +		else if (!strcmp(this_opt, "ypan"))
> +			ypan = 1;
> +		else if (!strcmp(this_opt, "ywrap"))
> +			ypan = 2;
> +		else if (!strcmp(this_opt, "vgapal"))
> +			pmi_setpal = 0;
> +		else if (!strcmp(this_opt, "pmipal"))
> +			pmi_setpal = 1;
> +		else if (!strncmp(this_opt, "mtrr:", 5))
> +			mtrr = simple_strtoul(this_opt+5, NULL, 0);
> +		else if (!strcmp(this_opt, "nomtrr"))
> +			mtrr = 0;
> +		else if (!strcmp(this_opt, "nocrtc"))
> +			nocrtc = 1;
> +		else if (!strcmp(this_opt, "noedid"))
> +			noedid = 1;
> +		else if (!strcmp(this_opt, "noblank"))
> +			blank = 0;
> +		else if (!strncmp(this_opt, "vtotal:", 7))
> +			vram_total = simple_strtoul(this_opt + 7, NULL, 0);
> +		else if (!strncmp(this_opt, "vremap:", 7))
> +			vram_remap = simple_strtoul(this_opt + 7, NULL, 0);
> +		else if (!strncmp(this_opt, "maxhf:", 6))
> +			maxhf = simple_strtoul(this_opt + 6, NULL, 0);
> +		else if (!strncmp(this_opt, "maxvf:", 6))
> +			maxvf = simple_strtoul(this_opt + 6, NULL, 0);
> +		else if (!strncmp(this_opt, "maxclk:", 7))
> +			maxclk = simple_strtoul(this_opt + 7, NULL, 0);
> +		else if (!strncmp(this_opt, "vbemode:", 8))
> +			vbemode = simple_strtoul(this_opt + 8, NULL,0);
> +		else if (this_opt[0] >= '0' && this_opt[0] <= '9') {
> +			mode_option = this_opt;
> +		} else {
> +			printk(KERN_WARNING
> +			       "uvesafb: unrecognized option %s\n", this_opt);
> +		}
> +	}
> +
> +	return 0;
> +}
> +#endif /* !MODULE */

The #ifdef MODULE is unpleasing.  Can we use module_param() here?  That'll
work for both modular and non-modular case.

> +#define uvesafb_free(task)					\
> +do {								\
> +	if (task) {						\
> +		if (task->done)					\
> +			kfree(task->done);			\
> +		kfree(task);					\
> +		task = NULL;					\
> +	}							\
> +} while(0)

I don't think this need to be a macro?  Code should be written in C unless
there is some reason why it _must_ be a macro.

Oh, it sneakily zeroes a variable within the caller's context.  Ow.  Can
we not do that please?  Someone who is reading this code will see a call to
a "function" called uvesafb_free() and the last thing they will expect is
that "function" magically zeroed out a local variable.

> +#define uvesafb_reset(task)					\
> +do {								\
> +	struct completion *cpl = task->done;			\
> +	memset(task, 0, sizeof(struct uvesafb_ktask));		\
> +	task->done = cpl;					\
> +} while(0)							\

This can be a regular C function.

> +static int uvesafb_exec(struct uvesafb_ktask *tsk);
> +
> +#define UVESAFB_NEED_EXACT_RES		1
> +#define UVESAFB_NEED_EXACT_DEPTH	2
> +
> +struct uvesafb_par {
> +	struct vbe_ib vbe_ib;		/* VBE Info Block */
> +	struct vbe_mode_ib *vbe_modes;	/* list of supported VBE modes */
> +	int vbe_modes_cnt;
> +
> +	u8 nocrtc;
> +	u8 ypan;			/* 0 - nothing, 1 - ypan, 2 - ywrap */
> +	u8 pmi_setpal;			/* pmi for palette changes */
> +	u16  *pmi_base;			/* protected mode interface location */
> +	void (*pmi_start)(void);
> +	void (*pmi_pal)(void);
> +
> +	u8 *vbe_state_orig;		/* original hardware state, before the driver was loaded */
> +	u8 *vbe_state_saved;		/* state saved by fb_save_state */
> +	int vbe_state_size;
> +	atomic_t ref_count;
> +
> +	int mode_idx;
> +	struct vbe_crtc_ib crtc;
> +};
> +
> +#endif

A comment on the endif would be nice.

> +#endif /* _UVESAFB_H */

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[Index of Archives]     [Kernel Newbies]     [Netfilter]     [Bugtraq]     [Photo]     [Stuff]     [Gimp]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Video 4 Linux]     [Linux for the blind]     [Linux Resources]
  Powered by Linux