Re: [PATCH 3/4] fbdev: uvesafb driver
From: Andrew Morton <akpm@linux-foundation.org>
Date: 2007-06-23 18:36:26
Also in:
lkml
On Sat, 23 Jun 2007 12:52:43 +0200 Michal Januszewski [off-list ref] wrote:
quoted hunk ↗ jump to hunk
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 <spock@gentoo.org> --- 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.
quoted hunk ↗ jump to hunk
...--- /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 <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 <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;
+}
+#endifYou might care to cc "H. Peter Anvin" [off-list ref] 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;
+};
+
+#endifA comment on the endif would be nice.
+#endif /* _UVESAFB_H */
------------------------------------------------------------------------- This SF.net email is sponsored by DB2 Express Download DB2 Express C - the FREE version of DB2 express and take control of your XML. No limits. Just data. Click to get it now. http://sourceforge.net/powerbar/db2/