Commit f5ec16b7 authored by Antonino Daplas's avatar Antonino Daplas Committed by Linus Torvalds

[PATCH] fbcon: take over console on driver registration

- This fixes another regression from 2.4.  If fbcon is compiled
  statically, and the framebuffer driver is compiled as a module, doing a
  'modprobe xxxfb' does nothing to the console.  This has generated
  numerous bug reports from users.

  With this patch, fbmem will notify fbcon upon driver registration
  allowing fbcon to take over the console.

- This also fixes con2fbmap not working if fbcon is compiled as a module
  using the same mechanism as described above.
Signed-off-by: default avatarAntonino Daplas <adaplas@pol.net>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 51fc00d1
......@@ -314,6 +314,28 @@ static int search_for_mapped_con(void)
return 0;
}
static int fbcon_takeover(void)
{
int err, i;
if (!num_registered_fb)
return -ENODEV;
for (i = first_fb_vc; i <= last_fb_vc; i++)
con2fb_map[i] = info_idx;
err = take_over_console(&fb_con, first_fb_vc, last_fb_vc,
fbcon_is_default);
if (err) {
for (i = first_fb_vc; i <= last_fb_vc; i++) {
con2fb_map[i] = -1;
}
info_idx = -1;
}
return err;
}
/**
* set_con2fb_map - map console to frame buffer device
* @unit: virtual console number to map
......@@ -322,7 +344,7 @@ static int search_for_mapped_con(void)
* Maps a virtual console @unit to a frame buffer device
* @newidx.
*/
int set_con2fb_map(int unit, int newidx)
static int set_con2fb_map(int unit, int newidx)
{
struct vc_data *vc = vc_cons[unit].d;
int oldidx = con2fb_map[unit];
......@@ -338,8 +360,7 @@ int set_con2fb_map(int unit, int newidx)
if (!search_for_mapped_con()) {
info_idx = newidx;
fb_console_init();
return 0;
return fbcon_takeover();
}
if (oldidx != -1)
......@@ -2730,12 +2751,25 @@ static int fbcon_mode_deleted(struct fb_info *info,
return found;
}
static int fbcon_fb_registered(int idx)
{
int ret = 0;
if (info_idx == -1) {
info_idx = idx;
ret = fbcon_takeover();
}
return ret;
}
static int fbcon_event_notify(struct notifier_block *self,
unsigned long action, void *data)
{
struct fb_event *event = (struct fb_event *) data;
struct fb_info *info = event->info;
struct fb_videomode *mode;
struct fb_con2fbmap *con2fb;
int ret = 0;
switch(action) {
......@@ -2752,6 +2786,17 @@ static int fbcon_event_notify(struct notifier_block *self,
mode = (struct fb_videomode *) event->data;
ret = fbcon_mode_deleted(info, mode);
break;
case FB_EVENT_FB_REGISTERED:
ret = fbcon_fb_registered(info->node);
break;
case FB_EVENT_SET_CONSOLE_MAP:
con2fb = (struct fb_con2fbmap *) event->data;
ret = set_con2fb_map(con2fb->console - 1, con2fb->framebuffer);
break;
case FB_EVENT_GET_CONSOLE_MAP:
con2fb = (struct fb_con2fbmap *) event->data;
con2fb->framebuffer = con2fb_map[con2fb->console - 1];
break;
}
return ret;
......@@ -2790,43 +2835,28 @@ const struct consw fb_con = {
static struct notifier_block fbcon_event_notifier = {
.notifier_call = fbcon_event_notify,
};
static int fbcon_event_notifier_registered;
/* can't be __init as it can be called by set_con2fb_map() later */
int fb_console_init(void)
int __init fb_console_init(void)
{
int err, i;
int i;
acquire_console_sem();
fb_register_client(&fbcon_event_notifier);
release_console_sem();
for (i = 0; i < MAX_NR_CONSOLES; i++)
con2fb_map[i] = -1;
if (!num_registered_fb)
return -ENODEV;
if (info_idx == -1) {
if (num_registered_fb) {
for (i = 0; i < FB_MAX; i++) {
if (registered_fb[i] != NULL) {
info_idx = i;
break;
}
}
fbcon_takeover();
}
for (i = first_fb_vc; i <= last_fb_vc; i++)
con2fb_map[i] = info_idx;
err = take_over_console(&fb_con, first_fb_vc, last_fb_vc,
fbcon_is_default);
if (err) {
for (i = first_fb_vc; i <= last_fb_vc; i++) {
con2fb_map[i] = -1;
}
return err;
}
acquire_console_sem();
if (!fbcon_event_notifier_registered) {
fb_register_client(&fbcon_event_notifier);
fbcon_event_notifier_registered = 1;
}
release_console_sem();
return 0;
}
......@@ -2835,10 +2865,7 @@ int fb_console_init(void)
void __exit fb_console_exit(void)
{
acquire_console_sem();
if (fbcon_event_notifier_registered) {
fb_unregister_client(&fbcon_event_notifier);
fbcon_event_notifier_registered = 0;
}
fb_unregister_client(&fbcon_event_notifier);
release_console_sem();
give_up_console(&fb_con);
}
......
......@@ -48,10 +48,6 @@ struct display {
struct fb_videomode *mode;
};
/* drivers/video/console/fbcon.c */
extern signed char con2fb_map[MAX_NR_CONSOLES];
extern int set_con2fb_map(int unit, int newidx);
/*
* Attribute Decoding
*/
......
......@@ -47,9 +47,6 @@
#include <linux/fb.h>
#ifdef CONFIG_FRAMEBUFFER_CONSOLE
#include "console/fbcon.h"
#endif
/*
* Frame buffer device initialization and setup routines
*/
......@@ -1236,10 +1233,9 @@ fb_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
struct fb_ops *fb = info->fbops;
struct fb_var_screeninfo var;
struct fb_fix_screeninfo fix;
#ifdef CONFIG_FRAMEBUFFER_CONSOLE
struct fb_con2fbmap con2fb;
#endif
struct fb_cmap_user cmap;
struct fb_event event;
void __user *argp = (void __user *)arg;
int i;
......@@ -1288,13 +1284,16 @@ fb_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
i = fb_cursor(info, argp);
release_console_sem();
return i;
#ifdef CONFIG_FRAMEBUFFER_CONSOLE
case FBIOGET_CON2FBMAP:
if (copy_from_user(&con2fb, argp, sizeof(con2fb)))
return -EFAULT;
if (con2fb.console < 1 || con2fb.console > MAX_NR_CONSOLES)
return -EINVAL;
con2fb.framebuffer = con2fb_map[con2fb.console-1];
con2fb.framebuffer = -1;
event.info = info;
event.data = &con2fb;
notifier_call_chain(&fb_notifier_list,
FB_EVENT_GET_CONSOLE_MAP, &event);
return copy_to_user(argp, &con2fb,
sizeof(con2fb)) ? -EFAULT : 0;
case FBIOPUT_CON2FBMAP:
......@@ -1310,11 +1309,14 @@ fb_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
#endif /* CONFIG_KMOD */
if (!registered_fb[con2fb.framebuffer])
return -EINVAL;
if (con2fb.console > 0 && con2fb.console < MAX_NR_CONSOLES)
return set_con2fb_map(con2fb.console-1,
con2fb.framebuffer);
if (con2fb.console > 0 && con2fb.console < MAX_NR_CONSOLES) {
event.info = info;
event.data = &con2fb;
return notifier_call_chain(&fb_notifier_list,
FB_EVENT_SET_CONSOLE_MAP,
&event);
}
return -EINVAL;
#endif /* CONFIG_FRAMEBUFFER_CONSOLE */
case FBIOBLANK:
acquire_console_sem();
i = fb_blank(info, arg);
......@@ -1493,6 +1495,7 @@ register_framebuffer(struct fb_info *fb_info)
{
int i;
struct class_device *c;
struct fb_event event;
if (num_registered_fb == FB_MAX)
return -ENXIO;
......@@ -1546,6 +1549,9 @@ register_framebuffer(struct fb_info *fb_info)
devfs_mk_cdev(MKDEV(FB_MAJOR, i),
S_IFCHR | S_IRUGO | S_IWUGO, "fb/%d", i);
event.info = fb_info;
notifier_call_chain(&fb_notifier_list,
FB_EVENT_FB_REGISTERED, &event);
return 0;
}
......
......@@ -452,6 +452,13 @@ struct fb_cursor_user {
#define FB_EVENT_RESUME 0x03
/* An entry from the modelist was removed */
#define FB_EVENT_MODE_DELETE 0x04
/* A driver registered itself */
#define FB_EVENT_FB_REGISTERED 0x05
/* get console to framebuffer mapping */
#define FB_EVENT_GET_CONSOLE_MAP 0x06
/* set console to framebuffer mapping */
#define FB_EVENT_SET_CONSOLE_MAP 0x07
struct fb_event {
struct fb_info *info;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment