Linux Serial subsystem development
 help / color / mirror / Atom feed
* ST16C654 stoppes transmitting after a while
From: Tobias Arp @ 2012-07-17  7:43 UTC (permalink / raw)
  To: linux-serial

Hi,

we use on our system (ARM board Processor EP9315) a quad uart (ST16C654) with kernel 3.2 (the quad uart is connected only to one irq, so the four interfaces of the quad uart have to share this irq). Multiple simultanous  connections are working fine for some hours (3 Modem connections and one null modem connection / transferring data in both directions with hardware handshake (CRTSCTS) enabled). Then one connection can't send data to the remote host anymore (often the null modem connection). The driver is the 8250 serial driver.

I found out that the hw_stopped flag is set to 1 but not set to 0 again although CTS is set (measuring with the oscilloscope and reading out the modem status register are confirming this / i modified the serial8250_get_mctrl function to print out these values).

It seems to me that an CTS change (UART_MSR_DCTS) is lost.
Could this cause this behaviour?

Any suggestions for a workaround are appreciated.

Thanks in  advance

Tobias





^ permalink raw reply

* Re: [PATCH 06/12] 8250: use the 8250 register interface not the legacy one
From: Greg KH @ 2012-07-16 21:29 UTC (permalink / raw)
  To: Alan Cox; +Cc: linux-serial
In-Reply-To: <20120716212911.77c996ad@pyramind.ukuu.org.uk>

On Mon, Jul 16, 2012 at 09:29:11PM +0100, Alan Cox wrote:
> On Mon, 16 Jul 2012 13:09:50 -0700
> Greg KH <greg@kroah.com> wrote:
> 
> > On Sat, Jul 14, 2012 at 03:33:13PM +0100, Alan Cox wrote:
> > > From: Alan Cox <alan@linux.intel.com>
> > > 
> > > The old interface just copies bits over and calls the newer one.
> > > In addition we can now pass more information.
> > > 
> > > Updated to fix the cases reported by Stephen Rothwell and those found
> > > by Fegguang's nifty new 0 day build tester. In particular it now builds
> > > correctly for openfirmware and for OLPC.
> > > 
> > > Signed-off-by: Alan Cox <alan@linux.intel.com>
> > 
> > I already applied an older version of this, so it doesn't apply
> > properly.
> > 
> > can you redo this patch series based on my tty-next branch now, and
> > resend these last 6 patches, if they are still needed?
> 
> I can do but that will simply be a series of reverts of the old stuff
> followed by the patches I sent... we need to rewind to before the lock
> change patch or it all ends up impossible to bisect.

Ok, that's fine with me.

greg k-h

^ permalink raw reply

* Re: [PATCH 06/12] 8250: use the 8250 register interface not the legacy one
From: Alan Cox @ 2012-07-16 20:29 UTC (permalink / raw)
  To: Greg KH; +Cc: linux-serial
In-Reply-To: <20120716200950.GB15662@kroah.com>

On Mon, 16 Jul 2012 13:09:50 -0700
Greg KH <greg@kroah.com> wrote:

> On Sat, Jul 14, 2012 at 03:33:13PM +0100, Alan Cox wrote:
> > From: Alan Cox <alan@linux.intel.com>
> > 
> > The old interface just copies bits over and calls the newer one.
> > In addition we can now pass more information.
> > 
> > Updated to fix the cases reported by Stephen Rothwell and those found
> > by Fegguang's nifty new 0 day build tester. In particular it now builds
> > correctly for openfirmware and for OLPC.
> > 
> > Signed-off-by: Alan Cox <alan@linux.intel.com>
> 
> I already applied an older version of this, so it doesn't apply
> properly.
> 
> can you redo this patch series based on my tty-next branch now, and
> resend these last 6 patches, if they are still needed?

I can do but that will simply be a series of reverts of the old stuff
followed by the patches I sent... we need to rewind to before the lock
change patch or it all ends up impossible to bisect.

Alan

^ permalink raw reply

* Re: [PATCH 06/12] 8250: use the 8250 register interface not the legacy one
From: Greg KH @ 2012-07-16 20:09 UTC (permalink / raw)
  To: Alan Cox; +Cc: linux-serial
In-Reply-To: <20120714143300.26945.64185.stgit@localhost.localdomain>

On Sat, Jul 14, 2012 at 03:33:13PM +0100, Alan Cox wrote:
> From: Alan Cox <alan@linux.intel.com>
> 
> The old interface just copies bits over and calls the newer one.
> In addition we can now pass more information.
> 
> Updated to fix the cases reported by Stephen Rothwell and those found
> by Fegguang's nifty new 0 day build tester. In particular it now builds
> correctly for openfirmware and for OLPC.
> 
> Signed-off-by: Alan Cox <alan@linux.intel.com>

I already applied an older version of this, so it doesn't apply
properly.

can you redo this patch series based on my tty-next branch now, and
resend these last 6 patches, if they are still needed?

thanks,

greg k-h

^ permalink raw reply

* Re: [PATCH 04/12] usb: fix sillies in the metro USB driver
From: Greg KH @ 2012-07-16 20:07 UTC (permalink / raw)
  To: Alan Cox; +Cc: linux-serial
In-Reply-To: <20120714143221.26945.1549.stgit@localhost.localdomain>

On Sat, Jul 14, 2012 at 03:32:30PM +0100, Alan Cox wrote:
> From: Alan Cox <alan@linux.intel.com>
> 
> Bits noticed doing the termios conversion
> 
> Signed-off-by: Alan Cox <alan@linux.intel.com>
> ---

Already applied previously.

greg k-h

^ permalink raw reply

* [PATCH v3 11/11] MAINTAINERS: add fblog entry
From: David Herrmann @ 2012-07-15 19:04 UTC (permalink / raw)
  To: linux-kernel
  Cc: Florian Tobias Schandinat, Andrew Morton, Greg Kroah-Hartman,
	linux-fbdev, linux-serial, Alan Cox, David Herrmann
In-Reply-To: <1342379086-7583-1-git-send-email-dh.herrmann@googlemail.com>

Add myself as maintainer for the fblog driver to the MAINTAINERS file.

Signed-off-by: David Herrmann <dh.herrmann@googlemail.com>
---
 MAINTAINERS | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 719f57f..227d5ca 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2854,6 +2854,12 @@ F:	drivers/video/
 F:	include/video/
 F:	include/linux/fb.h
 
+FRAMEBUFFER LOG DRIVER
+M:	David Herrmann <dh.herrmann@googlemail.com>
+L:	linux-serial@vger.kernel.org
+S:	Maintained
+F:	drivers/video/console/fblog.c
+
 FREESCALE DMA DRIVER
 M:	Li Yang <leoli@freescale.com>
 M:	Zhang Wei <zw@zh-kernel.org>
-- 
1.7.11.2


^ permalink raw reply related

* [PATCH v3 07/11] fblog: allow selecting fbs via sysfs
From: David Herrmann @ 2012-07-15 19:04 UTC (permalink / raw)
  To: linux-kernel
  Cc: Florian Tobias Schandinat, Andrew Morton, Greg Kroah-Hartman,
	linux-fbdev, linux-serial, Alan Cox, David Herrmann
In-Reply-To: <1342379086-7583-1-git-send-email-dh.herrmann@googlemail.com>

fblog is mainly useful during boot, reboot, panics and maintenance. In all
cases you often want to control which monitors are used for console
output. Moreover, in multi-seat environments it is desireable to reduce
system-overhead by not drawing the console to all framebuffers. Two
mechanisms to select framebuffers for fblog are added:

1) "active" module parameter: This parameter selects whether new
framebuffers are opened automatically. By default this is on, that is, all
framebuffers are automatically used by fblog during boot. By passing
fblog.active=0 you can deactivate this.
The init process can set this to 0 via
/sys/modules/fblog/parameters/active, too. However, this does not affect
already available and used framebuffers in any way.

2) "active" sysfs attribute for each fblog object. Reading this value
returns whether a framebuffer is currently active. Writing it opens/closes
the framebuffer. This allows runtime control which fbs are used. For
instance, init can set these to 0 after bootup.

Signed-off-by: David Herrmann <dh.herrmann@googlemail.com>
---
 drivers/video/console/fblog.c | 53 ++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 52 insertions(+), 1 deletion(-)

diff --git a/drivers/video/console/fblog.c b/drivers/video/console/fblog.c
index 1c526c5..5519f91 100644
--- a/drivers/video/console/fblog.c
+++ b/drivers/video/console/fblog.c
@@ -44,6 +44,7 @@ struct fblog_fb {
 
 static DEFINE_MUTEX(fblog_registration_lock);
 static struct fblog_fb *fblog_fbs[FB_MAX];
+static bool active = 1;
 
 #define to_fblog_dev(_d) container_of(_d, struct fblog_fb, dev)
 
@@ -115,6 +116,40 @@ static void fblog_close(struct fblog_fb *fb, bool kill_dev)
 	mutex_unlock(&fb->lock);
 }
 
+static ssize_t fblog_dev_active_show(struct device *dev,
+				     struct device_attribute *attr,
+				     char *buf)
+{
+	struct fblog_fb *fb = to_fblog_dev(dev);
+
+	return snprintf(buf, PAGE_SIZE, "%d\n",
+			!!test_bit(FBLOG_OPEN, &fb->flags));
+}
+
+static ssize_t fblog_dev_active_store(struct device *dev,
+				      struct device_attribute *attr,
+				      const char *buf,
+				      size_t count)
+{
+	struct fblog_fb *fb = to_fblog_dev(dev);
+	unsigned long num;
+	int ret = 0;
+
+	num = simple_strtoul(buf, NULL, 10);
+
+	mutex_lock(&fb->info->lock);
+	if (num)
+		ret = fblog_open(fb);
+	else
+		fblog_close(fb, false);
+	mutex_unlock(&fb->info->lock);
+
+	return ret ? ret : count;
+}
+
+static DEVICE_ATTR(active, S_IRUGO | S_IWUSR | S_IWGRP, fblog_dev_active_show,
+		   fblog_dev_active_store);
+
 /*
  * fblog framebuffer list
  * The fblog_fbs[] array contains all currently registered framebuffers. If a
@@ -148,6 +183,7 @@ static void fblog_do_unregister(struct fb_info *info)
 	fblog_fbs[info->node] = NULL;
 
 	fblog_close(fb, true);
+	device_remove_file(&fb->dev, &dev_attr_active);
 	device_del(&fb->dev);
 	put_device(&fb->dev);
 }
@@ -156,6 +192,7 @@ static void fblog_do_register(struct fb_info *info, bool force)
 {
 	struct fblog_fb *fb;
 	int ret;
+	bool do_open = true;
 
 	fb = fblog_fbs[info->node];
 	if (fb && fb->info != info) {
@@ -186,7 +223,18 @@ static void fblog_do_register(struct fb_info *info, bool force)
 		return;
 	}
 
-	fblog_open(fb);
+	ret = device_create_file(&fb->dev, &dev_attr_active);
+	if (ret) {
+		pr_err("fblog: cannot create sysfs entry");
+		/* do not open fb if we cannot create control file */
+		do_open = false;
+	}
+
+	if (!active)
+		do_open = false;
+
+	if (do_open)
+		fblog_open(fb);
 }
 
 static void fblog_register(struct fb_info *info, bool force)
@@ -321,6 +369,9 @@ static void __exit fblog_exit(void)
 	}
 }
 
+module_param(active, bool, S_IRUGO | S_IWUSR | S_IWGRP);
+MODULE_PARM_DESC(active, "Activate fblog by default");
+
 module_init(fblog_init);
 module_exit(fblog_exit);
 MODULE_LICENSE("GPL");
-- 
1.7.11.2


^ permalink raw reply related

* [PATCH v3 06/11] fblog: open fb on registration
From: David Herrmann @ 2012-07-15 19:04 UTC (permalink / raw)
  To: linux-kernel
  Cc: Florian Tobias Schandinat, Andrew Morton, Greg Kroah-Hartman,
	linux-fbdev, linux-serial, Alan Cox, David Herrmann
In-Reply-To: <1342379086-7583-1-git-send-email-dh.herrmann@googlemail.com>

This opens the framebuffer upon registration so we can use it for
drawing-operations. On unregistration we close it again.

While opening/closing or accessing the fb in any other way, we must hold
the fb-mutex. However, since the notifiers are often called with the mutex
already held, we cannot lock it _after_ taking the
fblog_registration_lock. Therefore, we require the caller to make sure the
fb-mutex is held.

Signed-off-by: David Herrmann <dh.herrmann@googlemail.com>
---
 drivers/video/console/fblog.c | 94 ++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 93 insertions(+), 1 deletion(-)

diff --git a/drivers/video/console/fblog.c b/drivers/video/console/fblog.c
index 279f4d8..1c526c5 100644
--- a/drivers/video/console/fblog.c
+++ b/drivers/video/console/fblog.c
@@ -32,12 +32,14 @@
 
 enum fblog_flags {
 	FBLOG_KILLED,
+	FBLOG_OPEN,
 };
 
 struct fblog_fb {
 	unsigned long flags;
 	struct fb_info *info;
 	struct device dev;
+	struct mutex lock;
 };
 
 static DEFINE_MUTEX(fblog_registration_lock);
@@ -46,6 +48,74 @@ static struct fblog_fb *fblog_fbs[FB_MAX];
 #define to_fblog_dev(_d) container_of(_d, struct fblog_fb, dev)
 
 /*
+ * fblog_open/close()
+ * These functions manage access to the underlying framebuffer. While opened, we
+ * have a valid reference to the fb and can use it for drawing operations. When
+ * the fb is unregistered, we drop our reference and close the fb so it can get
+ * deleted properly. We also mark it as dead so no further fblog_open() call
+ * will succeed.
+ * Both functions must be called with the fb->info->lock mutex held! But make
+ * sure to lock it _before_ locking the fblog-registration-lock. Otherwise, we
+ * will dead-lock with fb-registration.
+ */
+
+static int fblog_open(struct fblog_fb *fb)
+{
+	int ret;
+
+	mutex_lock(&fb->lock);
+
+	if (test_bit(FBLOG_KILLED, &fb->flags)) {
+		ret = -ENODEV;
+		goto unlock;
+	}
+
+	if (test_bit(FBLOG_OPEN, &fb->flags)) {
+		ret = 0;
+		goto unlock;
+	}
+
+	if (!try_module_get(fb->info->fbops->owner)) {
+		ret = -ENODEV;
+		goto out_killed;
+	}
+
+	if (fb->info->fbops->fb_open && fb->info->fbops->fb_open(fb->info, 0)) {
+		ret = -EIO;
+		goto out_unref;
+	}
+
+	set_bit(FBLOG_OPEN, &fb->flags);
+	mutex_unlock(&fb->lock);
+	return 0;
+
+out_unref:
+	module_put(fb->info->fbops->owner);
+out_killed:
+	set_bit(FBLOG_KILLED, &fb->flags);
+unlock:
+	mutex_unlock(&fb->lock);
+	return ret;
+}
+
+static void fblog_close(struct fblog_fb *fb, bool kill_dev)
+{
+	mutex_lock(&fb->lock);
+
+	if (test_bit(FBLOG_OPEN, &fb->flags)) {
+		if (fb->info->fbops->fb_release)
+			fb->info->fbops->fb_release(fb->info, 0);
+		module_put(fb->info->fbops->owner);
+		clear_bit(FBLOG_OPEN, &fb->flags);
+	}
+
+	if (kill_dev)
+		set_bit(FBLOG_KILLED, &fb->flags);
+
+	mutex_unlock(&fb->lock);
+}
+
+/*
  * fblog framebuffer list
  * The fblog_fbs[] array contains all currently registered framebuffers. If a
  * framebuffer is in that list, we always must make sure that we own a reference
@@ -77,6 +147,7 @@ static void fblog_do_unregister(struct fb_info *info)
 
 	fblog_fbs[info->node] = NULL;
 
+	fblog_close(fb, true);
 	device_del(&fb->dev);
 	put_device(&fb->dev);
 }
@@ -99,6 +170,7 @@ static void fblog_do_register(struct fb_info *info, bool force)
 		return;
 
 	fb->info = info;
+	mutex_init(&fb->lock);
 	__module_get(THIS_MODULE);
 	device_initialize(&fb->dev);
 	fb->dev.class = fb_class;
@@ -113,6 +185,8 @@ static void fblog_do_register(struct fb_info *info, bool force)
 		put_device(&fb->dev);
 		return;
 	}
+
+	fblog_open(fb);
 }
 
 static void fblog_register(struct fb_info *info, bool force)
@@ -134,6 +208,7 @@ static int fblog_event(struct notifier_block *self, unsigned long action,
 {
 	struct fb_event *event = data;
 	struct fb_info *info = event->info;
+	struct fblog_fb *fb;
 
 	switch(action) {
 	case FB_EVENT_FB_REGISTERED:
@@ -145,8 +220,21 @@ static int fblog_event(struct notifier_block *self, unsigned long action,
 	case FB_EVENT_FB_UNREGISTERED:
 		/* This is called when a low-level system driver unregisters a
 		 * framebuffer. The registration lock is held but the console
-		 * lock might not be held. */
+		 * lock might not be held. The fb-lock is not held, either! */
+		mutex_lock(&info->lock);
 		fblog_unregister(info);
+		mutex_unlock(&info->lock);
+		break;
+	case FB_EVENT_FB_UNBIND:
+		/* Called directly before unregistering an FB. The FB is still
+		 * valid here and the registration lock is held but the console
+		 * lock might not be held (really?). */
+		mutex_lock(&fblog_registration_lock);
+		fb = fblog_fbs[info->node];
+		mutex_unlock(&fblog_registration_lock);
+
+		if (fb)
+			fblog_close(fb, true);
 		break;
 	}
 
@@ -163,7 +251,9 @@ static void fblog_scan(void)
 		if (!info || IS_ERR(info))
 			continue;
 
+		mutex_lock(&info->lock);
 		fblog_register(info, false);
+		mutex_unlock(&info->lock);
 
 		/* There is a very subtle race-condition. Even though we might
 		 * own a reference to the fb, it may still get unregistered
@@ -224,7 +314,9 @@ static void __exit fblog_exit(void)
 		if (!info || IS_ERR(info))
 			continue;
 
+		mutex_lock(&info->lock);
 		fblog_unregister(info);
+		mutex_unlock(&info->lock);
 		put_fb_info(info);
 	}
 }
-- 
1.7.11.2


^ permalink raw reply related

* [PATCH v3 04/11] fbdev: export get_fb_info()/put_fb_info()
From: David Herrmann @ 2012-07-15 19:04 UTC (permalink / raw)
  To: linux-kernel
  Cc: Florian Tobias Schandinat, Andrew Morton, Greg Kroah-Hartman,
	linux-fbdev, linux-serial, Alan Cox, David Herrmann
In-Reply-To: <1342379086-7583-1-git-send-email-dh.herrmann@googlemail.com>

When adding other internal users of the framebuffer subsystem, we need a
way to get references to framebuffers. These two functions already exist
so export them.

Signed-off-by: David Herrmann <dh.herrmann@googlemail.com>
---
 drivers/video/fbmem.c | 6 ++++--
 include/linux/fb.h    | 3 +++
 2 files changed, 7 insertions(+), 2 deletions(-)

diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c
index 0dff12a..1312ba2 100644
--- a/drivers/video/fbmem.c
+++ b/drivers/video/fbmem.c
@@ -46,7 +46,7 @@ static DEFINE_MUTEX(registration_lock);
 struct fb_info *registered_fb[FB_MAX] __read_mostly;
 int num_registered_fb __read_mostly;
 
-static struct fb_info *get_fb_info(unsigned int idx)
+struct fb_info *get_fb_info(unsigned int idx)
 {
 	struct fb_info *fb_info;
 
@@ -61,14 +61,16 @@ static struct fb_info *get_fb_info(unsigned int idx)
 
 	return fb_info;
 }
+EXPORT_SYMBOL_GPL(get_fb_info);
 
-static void put_fb_info(struct fb_info *fb_info)
+void put_fb_info(struct fb_info *fb_info)
 {
 	if (!atomic_dec_and_test(&fb_info->count))
 		return;
 	if (fb_info->fbops->fb_destroy)
 		fb_info->fbops->fb_destroy(fb_info);
 }
+EXPORT_SYMBOL_GPL(put_fb_info);
 
 int lock_fb_info(struct fb_info *info)
 {
diff --git a/include/linux/fb.h b/include/linux/fb.h
index ac3f1c6..2d51c0e 100644
--- a/include/linux/fb.h
+++ b/include/linux/fb.h
@@ -1033,6 +1033,9 @@ static inline void unlock_fb_info(struct fb_info *info)
 	mutex_unlock(&info->lock);
 }
 
+extern struct fb_info *get_fb_info(unsigned int idx);
+extern void put_fb_info(struct fb_info *fb_info);
+
 static inline void __fb_pad_aligned_buffer(u8 *dst, u32 d_pitch,
 					   u8 *src, u32 s_pitch, u32 height)
 {
-- 
1.7.11.2


^ permalink raw reply related

* [PATCH v3 03/11] fblog: new framebuffer kernel log dummy driver
From: David Herrmann @ 2012-07-15 19:04 UTC (permalink / raw)
  To: linux-kernel
  Cc: Florian Tobias Schandinat, Andrew Morton, Greg Kroah-Hartman,
	linux-fbdev, linux-serial, Alan Cox, David Herrmann
In-Reply-To: <1342379086-7583-1-git-send-email-dh.herrmann@googlemail.com>

Fblog displays all kernel log messages on all connected framebuffers. It
replaces fbcon when CONFIG_VT=n is selected. Its main purpose is to debug
boot problems by displaying the whole boot log on the screen. This patch
provides the first dummy module-init/deinit functions.

As it uses all the font and fb functions I placed it in
drivers/video/console. However, this means that we need to move the check
for CONFIG_VT in Makefile/Kconfig from drivers/video into
drivers/video/console as fblog does not depend on CONFIG_VT.

Signed-off-by: David Herrmann <dh.herrmann@googlemail.com>
---
 drivers/video/Kconfig          |  5 +----
 drivers/video/Makefile         |  2 +-
 drivers/video/console/Kconfig  | 37 +++++++++++++++++++++++++++++--------
 drivers/video/console/Makefile |  1 +
 drivers/video/console/fblog.c  | 41 +++++++++++++++++++++++++++++++++++++++++
 5 files changed, 73 insertions(+), 13 deletions(-)
 create mode 100644 drivers/video/console/fblog.c

diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 0217f74..e8fd53d 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -2448,10 +2448,7 @@ source "drivers/video/omap/Kconfig"
 source "drivers/video/omap2/Kconfig"
 source "drivers/video/exynos/Kconfig"
 source "drivers/video/backlight/Kconfig"
-
-if VT
-	source "drivers/video/console/Kconfig"
-endif
+source "drivers/video/console/Kconfig"
 
 if FB || SGI_NEWPORT_CONSOLE
 	source "drivers/video/logo/Kconfig"
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index ee8dafb..9f8a7f0 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -11,7 +11,7 @@ fb-y                              := fbmem.o fbmon.o fbcmap.o fbsysfs.o \
                                      modedb.o fbcvt.o
 fb-objs                           := $(fb-y)
 
-obj-$(CONFIG_VT)		  += console/
+obj-y				  += console/
 obj-$(CONFIG_LOGO)		  += logo/
 obj-y				  += backlight/
 
diff --git a/drivers/video/console/Kconfig b/drivers/video/console/Kconfig
index e2c96d0..7374362 100644
--- a/drivers/video/console/Kconfig
+++ b/drivers/video/console/Kconfig
@@ -6,7 +6,7 @@ menu "Console display driver support"
 
 config VGA_CONSOLE
 	bool "VGA text console" if EXPERT || !X86
-	depends on !4xx && !8xx && !SPARC && !M68K && !PARISC && !FRV && !SUPERH && !BLACKFIN && !AVR32 && !MN10300 && (!ARM || ARCH_FOOTBRIDGE || ARCH_INTEGRATOR || ARCH_NETWINDER)
+	depends on VT && !4xx && !8xx && !SPARC && !M68K && !PARISC && !FRV && !SUPERH && !BLACKFIN && !AVR32 && !MN10300 && (!ARM || ARCH_FOOTBRIDGE || ARCH_INTEGRATOR || ARCH_NETWINDER)
 	default y
 	help
 	  Saying Y here will allow you to use Linux in text mode through a
@@ -45,7 +45,7 @@ config VGACON_SOFT_SCROLLBACK_SIZE
 	 screenfuls of scrollback buffer
 
 config MDA_CONSOLE
-	depends on !M68K && !PARISC && ISA
+	depends on VT && !M68K && !PARISC && ISA
 	tristate "MDA text console (dual-headed) (EXPERIMENTAL)"
 	---help---
 	  Say Y here if you have an old MDA or monochrome Hercules graphics
@@ -61,14 +61,14 @@ config MDA_CONSOLE
 
 config SGI_NEWPORT_CONSOLE
         tristate "SGI Newport Console support"
-        depends on SGI_IP22 
+        depends on VT && SGI_IP22
         help
           Say Y here if you want the console on the Newport aka XL graphics
           card of your Indy.  Most people say Y here.
 
 config DUMMY_CONSOLE
 	bool
-	depends on VGA_CONSOLE!=y || SGI_NEWPORT_CONSOLE!=y 
+	depends on VT && (VGA_CONSOLE!=y || SGI_NEWPORT_CONSOLE!=y)
 	default y
 
 config DUMMY_CONSOLE_COLUMNS
@@ -89,7 +89,7 @@ config DUMMY_CONSOLE_ROWS
 
 config FRAMEBUFFER_CONSOLE
 	tristate "Framebuffer Console support"
-	depends on FB
+	depends on VT && FB
 	select CRC32
 	help
 	  Low-level framebuffer-based console driver.
@@ -122,16 +122,37 @@ config FRAMEBUFFER_CONSOLE_ROTATION
 
 config STI_CONSOLE
         bool "STI text console"
-        depends on PARISC
+        depends on VT && PARISC
         default y
         help
           The STI console is the builtin display/keyboard on HP-PARISC
           machines.  Say Y here to build support for it into your kernel.
           The alternative is to use your primary serial port as a console.
 
+config FBLOG
+	tristate "Framebuffer Kernel Log Driver"
+	depends on !VT && FB
+	default n
+	help
+	  This driver displays all kernel log messages on all connected
+	  framebuffers. It is mutually exclusive with CONFIG_FRAMEBUFFER_CONSOLE
+	  and CONFIG_VT. It was mainly created for debugging purposes when
+	  CONFIG_VT is not selected but you still want kernel boot messages on
+	  the screen.
+
+	  This driver overwrites all other graphics output on the framebuffer as
+	  long as it is active so the kernel log will always be visible. You
+	  need to disable this driver via sysfs to be able to start another
+	  graphics application.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the module will
+	  be called fblog.
+
 config FONTS
 	bool "Select compiled-in fonts"
-	depends on FRAMEBUFFER_CONSOLE || STI_CONSOLE
+	depends on FRAMEBUFFER_CONSOLE || STI_CONSOLE || FBLOG
 	help
 	  Say Y here if you would like to use fonts other than the default
 	  your frame buffer console usually use.
@@ -158,7 +179,7 @@ config FONT_8x8
 
 config FONT_8x16
 	bool "VGA 8x16 font" if FONTS
-	depends on FRAMEBUFFER_CONSOLE || SGI_NEWPORT_CONSOLE || STI_CONSOLE || USB_SISUSBVGA_CON
+	depends on FRAMEBUFFER_CONSOLE || SGI_NEWPORT_CONSOLE || STI_CONSOLE || USB_SISUSBVGA_CON || FBLOG
 	default y if !SPARC && !FONTS
 	help
 	  This is the "high resolution" font for the VGA frame buffer (the one
diff --git a/drivers/video/console/Makefile b/drivers/video/console/Makefile
index 9a52226..ec0e155 100644
--- a/drivers/video/console/Makefile
+++ b/drivers/video/console/Makefile
@@ -20,6 +20,7 @@ font-objs += $(font-objs-y)
 
 # Each configuration option enables a list of files.
 
+obj-$(CONFIG_FBLOG)               += fblog.o font.o
 obj-$(CONFIG_DUMMY_CONSOLE)       += dummycon.o
 obj-$(CONFIG_SGI_NEWPORT_CONSOLE) += newport_con.o font.o
 obj-$(CONFIG_STI_CONSOLE)         += sticon.o sticore.o font.o
diff --git a/drivers/video/console/fblog.c b/drivers/video/console/fblog.c
new file mode 100644
index 0000000..fb39737
--- /dev/null
+++ b/drivers/video/console/fblog.c
@@ -0,0 +1,41 @@
+/*
+ * Framebuffer Kernel Log Driver
+ * Copyright (c) 2012 David Herrmann <dh.herrmann@googlemail.com>
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+/*
+ * Framebuffer Kernel Log
+ * This driver prints the kernel log to all connected display devices. It
+ * replaces CONFIG_VT and cannot run simultaneously with it. It does not provide
+ * any virtual-terminal, though. It should only be used to get kernel boot
+ * messages to debug kernel errors.
+ * Hence, this driver is neither optimized for speed, nor does it provide any
+ * fancy features like colored text output.
+ * This driver forcibly writes to the framebuffer while active, therefore, you
+ * cannot run other graphics applications simultaneously. You need to disable
+ * all fblog instances before running other graphics applications.
+ */
+
+#include <linux/module.h>
+
+static int __init fblog_init(void)
+{
+	return 0;
+}
+
+static void __exit fblog_exit(void)
+{
+}
+
+module_init(fblog_init);
+module_exit(fblog_exit);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("David Herrmann <dh.herrmann@googlemail.com>");
+MODULE_DESCRIPTION("Framebuffer Kernel Log Driver");
-- 
1.7.11.2


^ permalink raw reply related

* [PATCH v3 01/11] fbcon: move update_attr() into separate source file
From: David Herrmann @ 2012-07-15 19:04 UTC (permalink / raw)
  To: linux-kernel
  Cc: Florian Tobias Schandinat, Andrew Morton, Greg Kroah-Hartman,
	linux-fbdev, linux-serial, Alan Cox, David Herrmann
In-Reply-To: <1342379086-7583-1-git-send-email-dh.herrmann@googlemail.com>

If we want to use update_attr() independently from fbcon, we need to split
it off from bitblit.c and fbcon.h. Therefore, introduce a new header and
source file (fbdraw.[ch]) which does not depende on vc_* and fbcon_*
structures in any way.

This does not introduce any new code nor does it make the paths deeper,
it simply splits the function off.

The other update_attr() functions (inside the rotation sources) seem
similar but are significantly different and I haven't found a way to merge
them efficiently (which is probably the reason why they are split off
now). Furthermore, I do not intend to use them in the coming code so there
is no need to split them off.

Signed-off-by: David Herrmann <dh.herrmann@googlemail.com>
---
 drivers/video/console/Makefile  |  3 ++-
 drivers/video/console/bitblit.c | 26 +++--------------------
 drivers/video/console/fbcon.h   |  5 +----
 drivers/video/console/fbdraw.c  | 46 +++++++++++++++++++++++++++++++++++++++++
 drivers/video/console/fbdraw.h  | 26 +++++++++++++++++++++++
 5 files changed, 78 insertions(+), 28 deletions(-)
 create mode 100644 drivers/video/console/fbdraw.c
 create mode 100644 drivers/video/console/fbdraw.h

diff --git a/drivers/video/console/Makefile b/drivers/video/console/Makefile
index a862e91..9a52226 100644
--- a/drivers/video/console/Makefile
+++ b/drivers/video/console/Makefile
@@ -25,7 +25,8 @@ obj-$(CONFIG_SGI_NEWPORT_CONSOLE) += newport_con.o font.o
 obj-$(CONFIG_STI_CONSOLE)         += sticon.o sticore.o font.o
 obj-$(CONFIG_VGA_CONSOLE)         += vgacon.o
 obj-$(CONFIG_MDA_CONSOLE)         += mdacon.o
-obj-$(CONFIG_FRAMEBUFFER_CONSOLE) += fbcon.o bitblit.o font.o softcursor.o
+obj-$(CONFIG_FRAMEBUFFER_CONSOLE) += fbcon.o bitblit.o font.o softcursor.o \
+                                     fbdraw.o
 ifeq ($(CONFIG_FB_TILEBLITTING),y)
 obj-$(CONFIG_FRAMEBUFFER_CONSOLE)     += tileblit.o
 endif
diff --git a/drivers/video/console/bitblit.c b/drivers/video/console/bitblit.c
index 28b1a83..6ec2905 100644
--- a/drivers/video/console/bitblit.c
+++ b/drivers/video/console/bitblit.c
@@ -22,26 +22,6 @@
 /*
  * Accelerated handlers.
  */
-static void update_attr(u8 *dst, u8 *src, int attribute,
-			       struct vc_data *vc)
-{
-	int i, offset = (vc->vc_font.height < 10) ? 1 : 2;
-	int width = DIV_ROUND_UP(vc->vc_font.width, 8);
-	unsigned int cellsize = vc->vc_font.height * width;
-	u8 c;
-
-	offset = cellsize - (offset * width);
-	for (i = 0; i < cellsize; i++) {
-		c = src[i];
-		if (attribute & FBCON_ATTRIBUTE_UNDERLINE && i >= offset)
-			c = 0xff;
-		if (attribute & FBCON_ATTRIBUTE_BOLD)
-			c |= c >> 1;
-		if (attribute & FBCON_ATTRIBUTE_REVERSE)
-			c = ~c;
-		dst[i] = c;
-	}
-}
 
 static void bit_bmove(struct vc_data *vc, struct fb_info *info, int sy,
 		      int sx, int dy, int dx, int height, int width)
@@ -88,7 +68,7 @@ static inline void bit_putcs_aligned(struct vc_data *vc, struct fb_info *info,
 					  charmask)*cellsize;
 
 		if (attr) {
-			update_attr(buf, src, attr, vc);
+			fbdraw_update_attr(buf, src, attr, &vc->vc_font);
 			src = buf;
 		}
 
@@ -123,7 +103,7 @@ static inline void bit_putcs_unaligned(struct vc_data *vc,
 					  charmask)*cellsize;
 
 		if (attr) {
-			update_attr(buf, src, attr, vc);
+			fbdraw_update_attr(buf, src, attr, &vc->vc_font);
 			src = buf;
 		}
 
@@ -275,7 +255,7 @@ static void bit_cursor(struct vc_data *vc, struct fb_info *info, int mode,
 			return;
 		kfree(ops->cursor_data);
 		ops->cursor_data = dst;
-		update_attr(dst, src, attribute, vc);
+		fbdraw_update_attr(dst, src, attribute, &vc->vc_font);
 		src = dst;
 	}
 
diff --git a/drivers/video/console/fbcon.h b/drivers/video/console/fbcon.h
index 6bd2e0c..8623bac 100644
--- a/drivers/video/console/fbcon.h
+++ b/drivers/video/console/fbcon.h
@@ -16,6 +16,7 @@
 #include <linux/vt_kern.h>
 
 #include <asm/io.h>
+#include "fbdraw.h"
 
 #define FBCON_FLAGS_INIT         1
 #define FBCON_FLAGS_CURSOR_TIMER 2
@@ -219,10 +220,6 @@ extern void fbcon_set_tileops(struct vc_data *vc, struct fb_info *info);
 extern void fbcon_set_bitops(struct fbcon_ops *ops);
 extern int  soft_cursor(struct fb_info *info, struct fb_cursor *cursor);
 
-#define FBCON_ATTRIBUTE_UNDERLINE 1
-#define FBCON_ATTRIBUTE_REVERSE   2
-#define FBCON_ATTRIBUTE_BOLD      4
-
 static inline int real_y(struct display *p, int ypos)
 {
 	int rows = p->vrows;
diff --git a/drivers/video/console/fbdraw.c b/drivers/video/console/fbdraw.c
new file mode 100644
index 0000000..aa18f7e
--- /dev/null
+++ b/drivers/video/console/fbdraw.c
@@ -0,0 +1,46 @@
+/*
+ * Framebuffer helpers for image draw-operations
+ *
+ * Copyright (c) 2004 Antonino Daplas <adaplas @pol.net>
+ * Copyright (c) 2012 David Herrmann <dh.herrmann@googlemail.com>
+ *
+ * Originally from drivers/video/console/bitblit.c which itself originally is
+ * from the 'accel_*' routines in drivers/video/console/fbcon.c.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file COPYING in the main directory of this archive for
+ * more details.
+ */
+
+#include <linux/console.h>
+#include <linux/fb.h>
+#include <linux/kd.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include "fbdraw.h"
+
+void fbdraw_update_attr(u8 *dst, const u8 *src, int attribute,
+			struct console_font *font)
+{
+	int i, offset = (font->height < 10) ? 1 : 2;
+	int width = DIV_ROUND_UP(font->width, 8);
+	unsigned int cellsize = font->height * width;
+	u8 c;
+
+	offset = cellsize - (offset * width);
+	for (i = 0; i < cellsize; i++) {
+		c = src[i];
+		if (attribute & FBCON_ATTRIBUTE_UNDERLINE && i >= offset)
+			c = 0xff;
+		if (attribute & FBCON_ATTRIBUTE_BOLD)
+			c |= c >> 1;
+		if (attribute & FBCON_ATTRIBUTE_REVERSE)
+			c = ~c;
+		dst[i] = c;
+	}
+}
+EXPORT_SYMBOL_GPL(fbdraw_update_attr);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("David Herrmann <dh.herrmann@googlemail.com>");
+MODULE_DESCRIPTION("Framebuffer helpers for image draw-operations");
diff --git a/drivers/video/console/fbdraw.h b/drivers/video/console/fbdraw.h
new file mode 100644
index 0000000..77edd7f
--- /dev/null
+++ b/drivers/video/console/fbdraw.h
@@ -0,0 +1,26 @@
+/*
+ * Framebuffer helpers for image draw-operations
+ *
+ * Copyright (c) 2012 David Herrmann <dh.herrmann@googlemail.com>
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file COPYING in the main directory of this archive for
+ * more details.
+ */
+
+#ifndef _VIDEO_FBDRAW_H
+#define _VIDEO_FBDRAW_H
+
+#include <linux/console.h>
+#include <linux/fb.h>
+#include <linux/kd.h>
+
+/* fbcon character attributes */
+#define FBCON_ATTRIBUTE_UNDERLINE 1
+#define FBCON_ATTRIBUTE_REVERSE   2
+#define FBCON_ATTRIBUTE_BOLD      4
+
+void fbdraw_update_attr(u8 *dst, const u8 *src, int attribute,
+			struct console_font *font);
+
+#endif /* _VIDEO_FBDRAW_H */
-- 
1.7.11.2


^ permalink raw reply related

* [PATCH v3 10/11] fblog: draw console to framebuffers
From: David Herrmann @ 2012-07-15 19:04 UTC (permalink / raw)
  To: linux-kernel
  Cc: Florian Tobias Schandinat, Andrew Morton, Greg Kroah-Hartman,
	linux-fbdev, linux-serial, Alan Cox, David Herrmann
In-Reply-To: <1342379086-7583-1-git-send-email-dh.herrmann@googlemail.com>

If not disabled or suspended, we now blit the console data to each
framebuffer. We only redraw on changes to avoid consuming too much CPU
power.

This isn't optimized for speed, currently. However, fblog is mainly used
for debugging purposes so this can be optimized later.

Signed-off-by: David Herrmann <dh.herrmann@googlemail.com>
---
 drivers/video/console/fblog.c | 107 +++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 106 insertions(+), 1 deletion(-)

diff --git a/drivers/video/console/fblog.c b/drivers/video/console/fblog.c
index ae01742..81b0b42 100644
--- a/drivers/video/console/fblog.c
+++ b/drivers/video/console/fblog.c
@@ -28,8 +28,11 @@
 #include <linux/console.h>
 #include <linux/device.h>
 #include <linux/fb.h>
+#include <linux/font.h>
+#include <linux/kd.h>
 #include <linux/module.h>
 #include <linux/mutex.h>
+#include "fbdraw.h"
 
 /**
  * struct fblog_buf: Console text buffer
@@ -66,6 +69,7 @@ struct fblog_fb {
 	struct device dev;
 	struct mutex lock;
 	struct fblog_buf buf;
+	struct console_font font;
 };
 
 static DEFINE_MUTEX(fblog_registration_lock);
@@ -174,6 +178,58 @@ static void fblog_buf_write(struct fblog_buf *buf, const char *str, size_t len)
 	}
 }
 
+static void fblog_redraw_clear(struct fblog_fb *fb)
+{
+	struct fb_fillrect region;
+	struct fb_info *info = fb->info;
+
+	region.color = 0;
+	region.dx = 0;
+	region.dy = 0;
+	region.width = info->var.xres;
+	region.height = info->var.yres;
+	region.rop = ROP_COPY;
+
+	info->fbops->fb_fillrect(info, &region);
+}
+
+static void fblog_redraw(struct fblog_fb *fb)
+{
+	size_t i;
+
+	mutex_lock(&fb->lock);
+	if (!test_bit(FBLOG_OPEN, &fb->flags) ||
+	    test_bit(FBLOG_SUSPENDED, &fb->flags) ||
+	    test_bit(FBLOG_BLANKED, &fb->flags)) {
+		mutex_unlock(&fb->lock);
+		return;
+	}
+
+	fblog_redraw_clear(fb);
+
+	for (i = 0; i < fb->buf.height; ++i) {
+		fbdraw_font(fb->info, &fb->font, false, 0, i, 7, 0, 0,
+			    fb->buf.lines[i], fb->buf.width);
+	}
+
+	mutex_unlock(&fb->lock);
+}
+
+static void fblog_refresh(struct fblog_fb *fb)
+{
+	unsigned int width, height;
+
+	mutex_lock(&fb->lock);
+	if (test_bit(FBLOG_OPEN, &fb->flags)) {
+		width = fb->info->var.xres / fb->font.width;
+		height = fb->info->var.yres / fb->font.height;
+		fblog_buf_resize(&fb->buf, width, height);
+	}
+	mutex_unlock(&fb->lock);
+
+	fblog_redraw(fb);
+}
+
 /*
  * fblog_open/close()
  * These functions manage access to the underlying framebuffer. While opened, we
@@ -190,6 +246,10 @@ static int fblog_open(struct fblog_fb *fb)
 {
 	static const char init_str[] = "Framebuffer log initialized\n";
 	int ret;
+	struct fb_var_screeninfo var;
+	const struct fb_videomode *mode;
+	unsigned int width, height;
+	const struct font_desc *font;
 
 	mutex_lock(&fb->lock);
 
@@ -203,6 +263,13 @@ static int fblog_open(struct fblog_fb *fb)
 		goto unlock;
 	}
 
+	font = get_default_font(var.xres, var.yres, fb->info->pixmap.blit_x,
+				fb->info->pixmap.blit_y);
+	if (!font) {
+		ret = -ENODEV;
+		goto unlock;
+	}
+
 	if (!try_module_get(fb->info->fbops->owner)) {
 		ret = -ENODEV;
 		goto out_killed;
@@ -213,10 +280,22 @@ static int fblog_open(struct fblog_fb *fb)
 		goto out_unref;
 	}
 
-	fblog_buf_resize(&fb->buf, 80, 24);
+	var = fb->info->var;
+	mode = fb_find_best_mode(&var, &fb->info->modelist);
+	var.activate = FB_ACTIVATE_NOW | FB_ACTIVATE_FORCE;
+	fb_set_var(fb->info, &var);
+
+	fb->font.width = font->width;
+	fb->font.height = font->height;
+	fb->font.data = (void*)font->data;
+
+	width = var.xres / fb->font.width;
+	height = var.yres / fb->font.height;
+	fblog_buf_resize(&fb->buf, width, height);
 	fblog_buf_write(&fb->buf, init_str, sizeof(init_str) - 1);
 	set_bit(FBLOG_OPEN, &fb->flags);
 	mutex_unlock(&fb->lock);
+	fblog_redraw(fb);
 	return 0;
 
 out_unref:
@@ -453,6 +532,31 @@ static int fblog_event(struct notifier_block *self, unsigned long action,
 		else
 			set_bit(FBLOG_BLANKED, &fb->flags);
 		break;
+	case FB_EVENT_MODE_DELETE:
+		/* This is sent when a video mode is removed. The current video
+		 * mode is never removed! The console lock is held while this is
+		 * called. */
+		/* fallthrough */
+	case FB_EVENT_NEW_MODELIST:
+		/* This is sent when the modelist got changed. The console-lock
+		 * is held and we should reset the mode. */
+		/* fallthrough */
+	case FB_EVENT_MODE_CHANGE_ALL:
+		/* This is the same as below but notifies us that the user used
+		 * the FB_ACTIVATE_ALL flag when setting the video mode. */
+		/* fallthrough */
+	case FB_EVENT_MODE_CHANGE:
+		/* This is called when the _user_ changes the video mode via
+		 * ioctls. It is not sent, when the kernel changes the mode
+		 * internally. This callback is called inside fb_set_var() so
+		 * the console lock is held. */
+		mutex_lock(&fblog_registration_lock);
+		fb = fblog_fbs[info->node];
+		mutex_unlock(&fblog_registration_lock);
+
+		if (fb)
+			fblog_refresh(fb);
+		break;
 	}
 
 	return 0;
@@ -509,6 +613,7 @@ static void fblog_con_write(struct console *con, const char *buf,
 	for (i = 0; i < FB_MAX; ++i) {
 		if (fblog_fbs[i]) {
 			fblog_buf_write(&fblog_fbs[i]->buf, buf, len);
+			fblog_redraw(fblog_fbs[i]);
 		}
 	}
 	mutex_unlock(&fblog_registration_lock);
-- 
1.7.11.2

^ permalink raw reply related

* [PATCH v3 09/11] fblog: register console driver
From: David Herrmann @ 2012-07-15 19:04 UTC (permalink / raw)
  To: linux-kernel
  Cc: Florian Tobias Schandinat, Andrew Morton, Greg Kroah-Hartman,
	linux-fbdev, linux-serial, Alan Cox, David Herrmann
In-Reply-To: <1342379086-7583-1-git-send-email-dh.herrmann@googlemail.com>

We want to print the kernel log to all FBs so we need a console driver.
This registers the driver on startup and writes all messages to all
registered fblog instances.

We cannot share a console buffer between FBs because they might have
different resolutions. Therefore, we create one buffer per object. We
destroy the buffer during close() so we do not waste memory if it is not
used.

Signed-off-by: David Herrmann <dh.herrmann@googlemail.com>
---
 drivers/video/console/fblog.c | 150 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 150 insertions(+)

diff --git a/drivers/video/console/fblog.c b/drivers/video/console/fblog.c
index 9b975bc..ae01742 100644
--- a/drivers/video/console/fblog.c
+++ b/drivers/video/console/fblog.c
@@ -25,11 +25,34 @@
 
 #define pr_fmt(_fmt) KBUILD_MODNAME ": " _fmt
 
+#include <linux/console.h>
 #include <linux/device.h>
 #include <linux/fb.h>
 #include <linux/module.h>
 #include <linux/mutex.h>
 
+/**
+ * struct fblog_buf: Console text buffer
+ *
+ * Each framebuffer has its own text buffer which contains all characters that
+ * are currently printed on screen. The buffers might have different sizes and
+ * can be resized during runtime. When the buffer content changes, we redraw the
+ * screen.
+ *
+ * width: Width of buffer in characters
+ * height: Height of buffer in characters
+ * lines: Array of lines
+ * pos_x: Cursor x-position
+ * pos_y: Cursor y-position
+ */
+struct fblog_buf {
+	size_t width;
+	size_t height;
+	u16 **lines;
+	size_t pos_x;
+	size_t pos_y;
+};
+
 enum fblog_flags {
 	FBLOG_KILLED,
 	FBLOG_OPEN,
@@ -42,6 +65,7 @@ struct fblog_fb {
 	struct fb_info *info;
 	struct device dev;
 	struct mutex lock;
+	struct fblog_buf buf;
 };
 
 static DEFINE_MUTEX(fblog_registration_lock);
@@ -50,6 +74,106 @@ static bool active = 1;
 
 #define to_fblog_dev(_d) container_of(_d, struct fblog_fb, dev)
 
+static void fblog_buf_resize(struct fblog_buf *buf, size_t width,
+			     size_t height)
+{
+	u16 **lines = NULL;
+	size_t i, j, minw, minh;
+
+	if (buf->height == height && buf->width == width)
+		return;
+
+	if (width && height) {
+		lines = kzalloc(height * sizeof(char *), GFP_KERNEL);
+		if (!lines)
+			return;
+
+		for (i = 0; i < height; ++i) {
+			lines[i] = kzalloc(width * sizeof(u16), GFP_KERNEL);
+			if (!lines[i]) {
+				while (i--)
+					kfree(lines[i]);
+				return;
+			}
+		}
+
+		/* copy old lines */
+		minw = min(width, buf->width);
+		minh = min(height, buf->height);
+		if (height >= buf->height)
+			i = 0;
+		else
+			i = buf->height - height;
+
+		for (j = 0; j < minh; ++i, ++j)
+			memcpy(lines[j], buf->lines[i], minw * sizeof(u16));
+	} else {
+		width = 0;
+		height = 0;
+	}
+
+	for (i = 0; i < buf->height; ++i)
+		kfree(buf->lines[i]);
+	kfree(buf->lines);
+
+	buf->lines = lines;
+	buf->width = width;
+	buf->height = height;
+}
+
+static void fblog_buf_deinit(struct fblog_buf *buf)
+{
+	fblog_buf_resize(buf, 0, 0);
+}
+
+static void fblog_buf_rotate(struct fblog_buf *buf)
+{
+	u16 *line;
+
+	if (!buf->height)
+		return;
+
+	line = buf->lines[0];
+	memset(line, 0, sizeof(u16) * buf->width);
+
+	memmove(buf->lines, &buf->lines[1], sizeof(char*) * (buf->height - 1));
+	buf->lines[buf->height - 1] = line;
+}
+
+static void fblog_buf_write(struct fblog_buf *buf, const char *str, size_t len)
+{
+	char c;
+
+	if (!buf->height)
+		return;
+
+	while (len--) {
+		c = *str++;
+
+		if (c == 0)
+			c = '?';
+
+		if (c == '\n') {
+			buf->pos_x = 0;
+			if (++buf->pos_y >= buf->height) {
+				buf->pos_y = buf->height - 1;
+				fblog_buf_rotate(buf);
+			}
+		} else {
+			if (buf->pos_x >= buf->width) {
+				buf->pos_x = 0;
+				++buf->pos_y;
+			}
+			if (buf->pos_y >= buf->height) {
+				buf->pos_y = buf->height - 1;
+				fblog_buf_rotate(buf);
+			}
+
+			buf->lines[buf->pos_y][buf->pos_x++] = c;
+		}
+	}
+}
+
 /*
  * fblog_open/close()
  * These functions manage access to the underlying framebuffer. While opened, we
@@ -64,6 +188,7 @@ static bool active = 1;
 
 static int fblog_open(struct fblog_fb *fb)
 {
+	static const char init_str[] = "Framebuffer log initialized\n";
 	int ret;
 
 	mutex_lock(&fb->lock);
@@ -88,6 +213,8 @@ static int fblog_open(struct fblog_fb *fb)
 		goto out_unref;
 	}
 
+	fblog_buf_resize(&fb->buf, 80, 24);
+	fblog_buf_write(&fb->buf, init_str, sizeof(init_str) - 1);
 	set_bit(FBLOG_OPEN, &fb->flags);
 	mutex_unlock(&fb->lock);
 	return 0;
@@ -110,6 +237,7 @@ static void fblog_close(struct fblog_fb *fb, bool kill_dev)
 			fb->info->fbops->fb_release(fb->info, 0);
 		module_put(fb->info->fbops->owner);
 		clear_bit(FBLOG_OPEN, &fb->flags);
+		fblog_buf_deinit(&fb->buf);
 	}
 
 	if (kill_dev)
@@ -372,6 +500,26 @@ static struct notifier_block fblog_notifier = {
 	.notifier_call = fblog_event,
 };
 
+static void fblog_con_write(struct console *con, const char *buf,
+			    unsigned int len)
+{
+	int i;
+
+	mutex_lock(&fblog_registration_lock);
+	for (i = 0; i < FB_MAX; ++i) {
+		if (fblog_fbs[i]) {
+			fblog_buf_write(&fblog_fbs[i]->buf, buf, len);
+		}
+	}
+	mutex_unlock(&fblog_registration_lock);
+}
+
+static struct console fblog_con_driver = {
+	.name = "fblog",
+	.write = fblog_con_write,
+	.flags = CON_PRINTBUFFER | CON_ENABLED,
+};
+
 static int __init fblog_init(void)
 {
 	int ret;
@@ -383,6 +531,7 @@ static int __init fblog_init(void)
 	}
 
 	fblog_scan();
+	register_console(&fblog_con_driver);
 
 	return 0;
 }
@@ -392,6 +541,7 @@ static void __exit fblog_exit(void)
 	unsigned int i;
 	struct fb_info *info;
 
+	unregister_console(&fblog_con_driver);
 	fb_unregister_client(&fblog_notifier);
 
 	/* We scan through the whole registered_fb array here instead of
-- 
1.7.11.2

^ permalink raw reply related

* [PATCH v3 08/11] fblog: cache framebuffer BLANK and SUSPEND states
From: David Herrmann @ 2012-07-15 19:04 UTC (permalink / raw)
  To: linux-kernel
  Cc: Florian Tobias Schandinat, Andrew Morton, Greg Kroah-Hartman,
	linux-fbdev, linux-serial, Alan Cox, David Herrmann
In-Reply-To: <1342379086-7583-1-git-send-email-dh.herrmann@googlemail.com>

We must cache these states so we will never draw to the framebuffer while
it is suspended or blanked.

Signed-off-by: David Herrmann <dh.herrmann@googlemail.com>
---
 drivers/video/console/fblog.c | 41 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 41 insertions(+)

diff --git a/drivers/video/console/fblog.c b/drivers/video/console/fblog.c
index 5519f91..9b975bc 100644
--- a/drivers/video/console/fblog.c
+++ b/drivers/video/console/fblog.c
@@ -33,6 +33,8 @@
 enum fblog_flags {
 	FBLOG_KILLED,
 	FBLOG_OPEN,
+	FBLOG_SUSPENDED,
+	FBLOG_BLANKED,
 };
 
 struct fblog_fb {
@@ -257,6 +259,7 @@ static int fblog_event(struct notifier_block *self, unsigned long action,
 	struct fb_event *event = data;
 	struct fb_info *info = event->info;
 	struct fblog_fb *fb;
+	int *blank;
 
 	switch(action) {
 	case FB_EVENT_FB_REGISTERED:
@@ -284,6 +287,44 @@ static int fblog_event(struct notifier_block *self, unsigned long action,
 		if (fb)
 			fblog_close(fb, true);
 		break;
+	case FB_EVENT_SUSPEND:
+		/* This is called when the low-level display driver suspends the
+		 * video system. We should not access the video system while it
+		 * is suspended. This is called with the console lock held. */
+		mutex_lock(&fblog_registration_lock);
+		fb = fblog_fbs[info->node];
+		mutex_unlock(&fblog_registration_lock);
+
+		if (fb)
+			set_bit(FBLOG_SUSPENDED, &fb->flags);
+		break;
+	case FB_EVENT_RESUME:
+		/* This is called when the low-level display driver resumes
+		 * operating. It is called with the console lock held. */
+		mutex_lock(&fblog_registration_lock);
+		fb = fblog_fbs[info->node];
+		mutex_unlock(&fblog_registration_lock);
+
+		if (fb)
+			clear_bit(FBLOG_SUSPENDED, &fb->flags);
+		break;
+	case FB_EVENT_BLANK:
+		/* This gets called _after_ the framebuffer was successfully
+		 * blanked. The console-lock is always held while fb_blank is
+		 * called and during this callback. */
+		mutex_lock(&fblog_registration_lock);
+		fb = fblog_fbs[info->node];
+		mutex_unlock(&fblog_registration_lock);
+
+		if (!fb)
+			break;
+
+		blank = (int*)event->data;
+		if (*blank == FB_BLANK_UNBLANK)
+			clear_bit(FBLOG_BLANKED, &fb->flags);
+		else
+			set_bit(FBLOG_BLANKED, &fb->flags);
+		break;
 	}
 
 	return 0;
-- 
1.7.11.2

^ permalink raw reply related

* [PATCH v3 05/11] fblog: register one fblog object per framebuffer
From: David Herrmann @ 2012-07-15 19:04 UTC (permalink / raw)
  To: linux-kernel
  Cc: Florian Tobias Schandinat, Andrew Morton, Greg Kroah-Hartman,
	linux-fbdev, linux-serial, Alan Cox, David Herrmann
In-Reply-To: <1342379086-7583-1-git-send-email-dh.herrmann@googlemail.com>

One fblog object is associated to each registered framebuffer. This way,
we can draw the console to each framebuffer. When a framebuffer driver
unregisters a framebuffer, we also unregister our fblog object. That is,
our lifetime is coupled to the lifetime of the framebuffer. However, this
does not mean that we are always active. On the contrary, we do not even
own a reference to the framebuffer. We don't need it as we are notified
_before_ the last reference is dropped.

However, if other users have a reference to our object, we simply mark it
as dead when the associated framebuffer dies and leave it alone. When the
last reference is dropped, it will be automatically freed.

Signed-off-by: David Herrmann <dh.herrmann@googlemail.com>
---
 drivers/video/console/fblog.c | 195 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 195 insertions(+)

diff --git a/drivers/video/console/fblog.c b/drivers/video/console/fblog.c
index fb39737..279f4d8 100644
--- a/drivers/video/console/fblog.c
+++ b/drivers/video/console/fblog.c
@@ -23,15 +23,210 @@
  * all fblog instances before running other graphics applications.
  */
 
+#define pr_fmt(_fmt) KBUILD_MODNAME ": " _fmt
+
+#include <linux/device.h>
+#include <linux/fb.h>
 #include <linux/module.h>
+#include <linux/mutex.h>
+
+enum fblog_flags {
+	FBLOG_KILLED,
+};
+
+struct fblog_fb {
+	unsigned long flags;
+	struct fb_info *info;
+	struct device dev;
+};
+
+static DEFINE_MUTEX(fblog_registration_lock);
+static struct fblog_fb *fblog_fbs[FB_MAX];
+
+#define to_fblog_dev(_d) container_of(_d, struct fblog_fb, dev)
+
+/*
+ * fblog framebuffer list
+ * The fblog_fbs[] array contains all currently registered framebuffers. If a
+ * framebuffer is in that list, we always must make sure that we own a reference
+ * to it. If it is added through the notifier callbacks, then this is always
+ * guaranteed.
+ * We are only interested in registered framebuffers. That is, if a driver calls
+ * unregister_framebuffer() we directly unlink it from our list. This guarantees
+ * that the associated fb_info is always valid. However, we might still have
+ * pending users so we mark it as dead so no further framebuffer actions are
+ * done. If the last user then drops a reference, the memory gets freed
+ * automatically.
+ */
+
+static void fblog_release(struct device *dev)
+{
+	struct fblog_fb *fb = to_fblog_dev(dev);
+
+	kfree(fb);
+	module_put(THIS_MODULE);
+}
+
+static void fblog_do_unregister(struct fb_info *info)
+{
+	struct fblog_fb *fb;
+
+	fb = fblog_fbs[info->node];
+	if (!fb || fb->info != info)
+		return;
+
+	fblog_fbs[info->node] = NULL;
+
+	device_del(&fb->dev);
+	put_device(&fb->dev);
+}
+
+static void fblog_do_register(struct fb_info *info, bool force)
+{
+	struct fblog_fb *fb;
+	int ret;
+
+	fb = fblog_fbs[info->node];
+	if (fb && fb->info != info) {
+		if (!force)
+			return;
+
+		fblog_do_unregister(fb->info);
+	}
+
+	fb = kzalloc(sizeof(*fb), GFP_KERNEL);
+	if (!fb)
+		return;
+
+	fb->info = info;
+	__module_get(THIS_MODULE);
+	device_initialize(&fb->dev);
+	fb->dev.class = fb_class;
+	fb->dev.release = fblog_release;
+	dev_set_name(&fb->dev, "fblog%d", info->node);
+	fblog_fbs[info->node] = fb;
+
+	ret = device_add(&fb->dev);
+	if (ret) {
+		fblog_fbs[info->node] = NULL;
+		set_bit(FBLOG_KILLED, &fb->flags);
+		put_device(&fb->dev);
+		return;
+	}
+}
+
+static void fblog_register(struct fb_info *info, bool force)
+{
+	mutex_lock(&fblog_registration_lock);
+	fblog_do_register(info, force);
+	mutex_unlock(&fblog_registration_lock);
+}
+
+static void fblog_unregister(struct fb_info *info)
+{
+	mutex_lock(&fblog_registration_lock);
+	fblog_do_unregister(info);
+	mutex_unlock(&fblog_registration_lock);
+}
+
+static int fblog_event(struct notifier_block *self, unsigned long action,
+		       void *data)
+{
+	struct fb_event *event = data;
+	struct fb_info *info = event->info;
+
+	switch(action) {
+	case FB_EVENT_FB_REGISTERED:
+		/* This is called when a low-level system driver registers a new
+		 * framebuffer. The registration lock is held but the console
+		 * lock might not be held when this is called. */
+		fblog_register(info, true);
+		break;
+	case FB_EVENT_FB_UNREGISTERED:
+		/* This is called when a low-level system driver unregisters a
+		 * framebuffer. The registration lock is held but the console
+		 * lock might not be held. */
+		fblog_unregister(info);
+		break;
+	}
+
+	return 0;
+}
+
+static void fblog_scan(void)
+{
+	unsigned int i;
+	struct fb_info *info, *tmp;
+
+	for (i = 0; i < FB_MAX; ++i) {
+		info = get_fb_info(i);
+		if (!info || IS_ERR(info))
+			continue;
+
+		fblog_register(info, false);
+
+		/* There is a very subtle race-condition. Even though we might
+		 * own a reference to the fb, it may still get unregistered
+		 * between our call from get_fb_info() and fblog_register().
+		 * Therefore, we simply check whether the same fb still is
+		 * registered by calling get_fb_info() again. Only if they
+		 * differ we know that it got unregistered, therefore, we
+		 * call fblog_unregister() with the old pointer. */
+
+		tmp = get_fb_info(i);
+		if (tmp && !IS_ERR(tmp))
+			put_fb_info(tmp);
+		if (tmp != info)
+			fblog_unregister(info);
+
+		/* Here we either called fblog_unregister() and therefore do not
+		 * need any reference to the fb, or we can be sure that the FB
+		 * is registered and FB_EVENT_FB_UNREGISTERED will be called
+		 * before the last reference is dropped. Hence, we can drop our
+		 * reference here. */
+
+		put_fb_info(info);
+	}
+}
+
+static struct notifier_block fblog_notifier = {
+	.notifier_call = fblog_event,
+};
 
 static int __init fblog_init(void)
 {
+	int ret;
+
+	ret = fb_register_client(&fblog_notifier);
+	if (ret) {
+		pr_err("cannot register framebuffer notifier\n");
+		return ret;
+	}
+
+	fblog_scan();
+
 	return 0;
 }
 
 static void __exit fblog_exit(void)
 {
+	unsigned int i;
+	struct fb_info *info;
+
+	fb_unregister_client(&fblog_notifier);
+
+	/* We scan through the whole registered_fb array here instead of
+	 * fblog_fbs because we need to get the device lock _before_ the
+	 * fblog-registration-lock. */
+
+	for (i = 0; i < FB_MAX; ++i) {
+		info = get_fb_info(i);
+		if (!info || IS_ERR(info))
+			continue;
+
+		fblog_unregister(info);
+		put_fb_info(info);
+	}
 }
 
 module_init(fblog_init);
-- 
1.7.11.2

^ permalink raw reply related

* [PATCH v3 02/11] fbcon: move bit_putcs() into separate source file
From: David Herrmann @ 2012-07-15 19:04 UTC (permalink / raw)
  To: linux-kernel
  Cc: Florian Tobias Schandinat, Andrew Morton, Greg Kroah-Hartman,
	linux-fbdev, linux-serial, Alan Cox, David Herrmann
In-Reply-To: <1342379086-7583-1-git-send-email-dh.herrmann@googlemail.com>

If we want to use font-draw-operations in other modules than fbcon, we
need to split this function off of fbcon headers and sources. This also
makes bit_putcs() totally independent of vc_* and fbcon_* structures.

As scr_read() cannot be called inside of non-fbcon/vt functions, we need
to assemble the buffer before passing it to fbdraw_font(). This slows down
this operations a little bit but my rough benchmark showed that it didn't
really matter. Anyway, if it does, we can still put it into VT_BUF_HAVE_RW
conditions so platforms that don't use it won't be affected. And for other
platforms we can add the buffer to the vc-struct so we can reuse it.

Signed-off-by: David Herrmann <dh.herrmann@googlemail.com>
---
 drivers/video/console/bitblit.c | 127 ++++------------------------------------
 drivers/video/console/fbdraw.c  | 125 +++++++++++++++++++++++++++++++++++++++
 drivers/video/console/fbdraw.h  |   4 ++
 3 files changed, 139 insertions(+), 117 deletions(-)

diff --git a/drivers/video/console/bitblit.c b/drivers/video/console/bitblit.c
index 6ec2905..c5d897b 100644
--- a/drivers/video/console/bitblit.c
+++ b/drivers/video/console/bitblit.c
@@ -54,132 +54,25 @@ static void bit_clear(struct vc_data *vc, struct fb_info *info, int sy,
 	info->fbops->fb_fillrect(info, &region);
 }
 
-static inline void bit_putcs_aligned(struct vc_data *vc, struct fb_info *info,
-				     const u16 *s, u32 attr, u32 cnt,
-				     u32 d_pitch, u32 s_pitch, u32 cellsize,
-				     struct fb_image *image, u8 *buf, u8 *dst)
-{
-	u16 charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff;
-	u32 idx = vc->vc_font.width >> 3;
-	u8 *src;
-
-	while (cnt--) {
-		src = vc->vc_font.data + (scr_readw(s++)&
-					  charmask)*cellsize;
-
-		if (attr) {
-			fbdraw_update_attr(buf, src, attr, &vc->vc_font);
-			src = buf;
-		}
-
-		if (likely(idx == 1))
-			__fb_pad_aligned_buffer(dst, d_pitch, src, idx,
-						image->height);
-		else
-			fb_pad_aligned_buffer(dst, d_pitch, src, idx,
-					      image->height);
-
-		dst += s_pitch;
-	}
-
-	info->fbops->fb_imageblit(info, image);
-}
-
-static inline void bit_putcs_unaligned(struct vc_data *vc,
-				       struct fb_info *info, const u16 *s,
-				       u32 attr, u32 cnt, u32 d_pitch,
-				       u32 s_pitch, u32 cellsize,
-				       struct fb_image *image, u8 *buf,
-				       u8 *dst)
-{
-	u16 charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff;
-	u32 shift_low = 0, mod = vc->vc_font.width % 8;
-	u32 shift_high = 8;
-	u32 idx = vc->vc_font.width >> 3;
-	u8 *src;
-
-	while (cnt--) {
-		src = vc->vc_font.data + (scr_readw(s++)&
-					  charmask)*cellsize;
-
-		if (attr) {
-			fbdraw_update_attr(buf, src, attr, &vc->vc_font);
-			src = buf;
-		}
-
-		fb_pad_unaligned_buffer(dst, d_pitch, src, idx,
-					image->height, shift_high,
-					shift_low, mod);
-		shift_low += mod;
-		dst += (shift_low >= 8) ? s_pitch : s_pitch - 1;
-		shift_low &= 7;
-		shift_high = 8 - shift_low;
-	}
-
-	info->fbops->fb_imageblit(info, image);
-
-}
-
 static void bit_putcs(struct vc_data *vc, struct fb_info *info,
 		      const unsigned short *s, int count, int yy, int xx,
 		      int fg, int bg)
 {
-	struct fb_image image;
-	u32 width = DIV_ROUND_UP(vc->vc_font.width, 8);
-	u32 cellsize = width * vc->vc_font.height;
-	u32 maxcnt = info->pixmap.size/cellsize;
-	u32 scan_align = info->pixmap.scan_align - 1;
-	u32 buf_align = info->pixmap.buf_align - 1;
-	u32 mod = vc->vc_font.width % 8, cnt, pitch, size;
+	u16 *buf;
+	int i;
 	u32 attribute = get_attribute(info, scr_readw(s));
-	u8 *dst, *buf = NULL;
 
-	image.fg_color = fg;
-	image.bg_color = bg;
-	image.dx = xx * vc->vc_font.width;
-	image.dy = yy * vc->vc_font.height;
-	image.height = vc->vc_font.height;
-	image.depth = 1;
+	buf = kmalloc(sizeof(*buf) * count, GFP_KERNEL);
+	if (!buf)
+		return;
 
-	if (attribute) {
-		buf = kmalloc(cellsize, GFP_KERNEL);
-		if (!buf)
-			return;
-	}
-
-	while (count) {
-		if (count > maxcnt)
-			cnt = maxcnt;
-		else
-			cnt = count;
-
-		image.width = vc->vc_font.width * cnt;
-		pitch = DIV_ROUND_UP(image.width, 8) + scan_align;
-		pitch &= ~scan_align;
-		size = pitch * image.height + buf_align;
-		size &= ~buf_align;
-		dst = fb_get_buffer_offset(info, &info->pixmap, size);
-		image.data = dst;
-
-		if (!mod)
-			bit_putcs_aligned(vc, info, s, attribute, cnt, pitch,
-					  width, cellsize, &image, buf, dst);
-		else
-			bit_putcs_unaligned(vc, info, s, attribute, cnt,
-					    pitch, width, cellsize, &image,
-					    buf, dst);
-
-		image.dx += cnt * vc->vc_font.width;
-		count -= cnt;
-		s += cnt;
-	}
+	for (i = 0; i < count; ++i)
+		buf[i] = scr_readw(s++);
 
-	/* buf is always NULL except when in monochrome mode, so in this case
-	   it's a gain to check buf against NULL even though kfree() handles
-	   NULL pointers just fine */
-	if (unlikely(buf))
-		kfree(buf);
+	fbdraw_font(info, &vc->vc_font, vc->vc_hi_font_mask, xx, yy, fg, bg,
+		    attribute, buf, count);
 
+	kfree(buf);
 }
 
 static void bit_clear_margins(struct vc_data *vc, struct fb_info *info,
diff --git a/drivers/video/console/fbdraw.c b/drivers/video/console/fbdraw.c
index aa18f7e..f7c3da7 100644
--- a/drivers/video/console/fbdraw.c
+++ b/drivers/video/console/fbdraw.c
@@ -41,6 +41,131 @@ void fbdraw_update_attr(u8 *dst, const u8 *src, int attribute,
 }
 EXPORT_SYMBOL_GPL(fbdraw_update_attr);
 
+static inline void bit_putcs_aligned(struct fb_info *info, bool hi_font,
+				     struct console_font *font, u32 attribute,
+				     u32 d_pitch, u32 s_pitch, u32 cellsize,
+				     struct fb_image *image, u8 *buf, u8 *dst,
+				     const u16 *chars, size_t cnt)
+{
+	u16 charmask = hi_font ? 0x1ff : 0xff;
+	u32 idx = font->width >> 3;
+	u8 *src;
+
+	while (cnt--) {
+		src = font->data + ((*chars++) & charmask) * cellsize;
+
+		if (attribute) {
+			fbdraw_update_attr(buf, src, attribute, font);
+			src = buf;
+		}
+
+		if (likely(idx == 1))
+			__fb_pad_aligned_buffer(dst, d_pitch, src, idx,
+						image->height);
+		else
+			fb_pad_aligned_buffer(dst, d_pitch, src, idx,
+					      image->height);
+
+		dst += s_pitch;
+	}
+
+	info->fbops->fb_imageblit(info, image);
+}
+
+static inline void bit_putcs_unaligned(struct fb_info *info, bool hi_font,
+				       struct console_font *font, u32 attribute,
+				       u32 d_pitch, u32 s_pitch, u32 cellsize,
+				       struct fb_image *image, u8 *buf, u8 *dst,
+				       const u16 *chars, size_t cnt)
+{
+	u16 charmask = hi_font ? 0x1ff : 0xff;
+	u32 shift_low = 0, mod = font->width % 8;
+	u32 shift_high = 8;
+	u32 idx = font->width >> 3;
+	u8 *src;
+
+	while (cnt--) {
+		src = font->data + ((*chars++) & charmask) * cellsize;
+
+		if (attribute) {
+			fbdraw_update_attr(buf, src, attribute, font);
+			src = buf;
+		}
+
+		fb_pad_unaligned_buffer(dst, d_pitch, src, idx,
+					image->height, shift_high,
+					shift_low, mod);
+		shift_low += mod;
+		dst += (shift_low >= 8) ? s_pitch : s_pitch - 1;
+		shift_low &= 7;
+		shift_high = 8 - shift_low;
+	}
+
+	info->fbops->fb_imageblit(info, image);
+}
+
+void fbdraw_font(struct fb_info *info, struct console_font *font, bool hi_font,
+		 unsigned int xpos, unsigned int ypos, int fg, int bg,
+		 u32 attribute, const u16 *chars, size_t count)
+{
+	struct fb_image image;
+	u32 width = DIV_ROUND_UP(font->width, 8);
+	u32 cellsize = width * font->height;
+	u32 maxcnt = info->pixmap.size / cellsize;
+	u32 scan_align = info->pixmap.scan_align - 1;
+	u32 buf_align = info->pixmap.buf_align - 1;
+	u32 mod = font->width % 8, cnt, pitch, size;
+	u8 *dst, *buf = NULL;
+
+	image.fg_color = fg;
+	image.bg_color = bg;
+	image.dx = xpos * font->width;
+	image.dy = ypos * font->height;
+	image.height = font->height;
+	image.depth = 1;
+
+	if (attribute) {
+		buf = kmalloc(cellsize, GFP_KERNEL);
+		if (!buf)
+			return;
+	}
+
+	while (count) {
+		if (count > maxcnt)
+			cnt = maxcnt;
+		else
+			cnt = count;
+
+		image.width = font->width * cnt;
+		pitch = DIV_ROUND_UP(image.width, 8) + scan_align;
+		pitch &= ~scan_align;
+		size = pitch * image.height + buf_align;
+		size &= ~buf_align;
+		dst = fb_get_buffer_offset(info, &info->pixmap, size);
+		image.data = dst;
+
+		if (!mod)
+			bit_putcs_aligned(info, hi_font, font, attribute,
+					  pitch, width, cellsize, &image, buf,
+					  dst, chars, cnt);
+		else
+			bit_putcs_unaligned(info, hi_font, font, attribute,
+					    pitch, width, cellsize, &image, buf,
+					    dst, chars, cnt);
+
+		image.dx += cnt * font->width;
+		count -= cnt;
+		chars += cnt;
+	}
+
+	/* buf is always NULL except when in monochrome mode, so in this case
+	   it's a gain to check buf against NULL even though kfree() handles
+	   NULL pointers just fine */
+	if (unlikely(buf))
+		kfree(buf);
+}
+EXPORT_SYMBOL_GPL(fbdraw_font);
+
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("David Herrmann <dh.herrmann@googlemail.com>");
 MODULE_DESCRIPTION("Framebuffer helpers for image draw-operations");
diff --git a/drivers/video/console/fbdraw.h b/drivers/video/console/fbdraw.h
index 77edd7f..b9f1ffa 100644
--- a/drivers/video/console/fbdraw.h
+++ b/drivers/video/console/fbdraw.h
@@ -23,4 +23,8 @@
 void fbdraw_update_attr(u8 *dst, const u8 *src, int attribute,
 			struct console_font *font);
 
+void fbdraw_font(struct fb_info *info, struct console_font *font, bool hi_font,
+		 unsigned int xpos, unsigned int ypos, int fg, int bg,
+		 u32 attribute, const u16 *chars, size_t count);
+
 #endif /* _VIDEO_FBDRAW_H */
-- 
1.7.11.2

^ permalink raw reply related

* [PATCH v3 00/11] fblog: Framebuffer kernel log driver v3
From: David Herrmann @ 2012-07-15 19:04 UTC (permalink / raw)
  To: linux-kernel
  Cc: Florian Tobias Schandinat, Andrew Morton, Greg Kroah-Hartman,
	linux-fbdev, linux-serial, Alan Cox, David Herrmann

Hi

This is revision 3 of the fblog driver. This driver is a replacement for fbcon
for systems that do not want/need CONFIG_VT. It simply prints the kernel log to
all connected framebuffers. Previous versions are available here:
  v2: http://thread.gmane.org/gmane.linux.serial/8133
  v1: http://marc.info/?l=linux-kernel&m=133988465602225&w=2

This patchset is against linux-next from last week (Wednesday or Thursday).

I've fixed all issues that were reported last week. They were all minor
nitpicks:
  - Changed EXPORT_SYMBOL to EXPORT_SYMBOL_GPL for exported fbdev functions
  - Removed FBLOG_STR
  - Added pr_fmt()
  - Fixed two minor coding-style issues
  - Fixed one possible subtle deadlock in device registration (switched locking
    order)

If you want to test it but don't want to disable CONFIG_VT, simply remove the
dependency to !VT of fblog. This will cause both fblog and fbcon to draw to the
framebuffers but both draw only on changes so it will still be possible to debug
and test fblog.

It would be nice if someone could tell me what tree this will go through or how
I can get it into linux-next as I have no idea who is maintaining fbcon. If
there are other major or minor issues, please tell me so I can work on them.

I am also working on improving the render-path by using dma-buf and partial
redraws like fbcon. Furthermore, I will add a panic-handler so fblog draws
oopses/panics to the framebuffers even though it is disabled. However, I want to
get this basic driver in before making it more complex and making review too
hard.

Thanks! Regards
David

David Herrmann (11):
  fbcon: move update_attr() into separate source file
  fbcon: move bit_putcs() into separate source file
  fblog: new framebuffer kernel log dummy driver
  fbdev: export get_fb_info()/put_fb_info()
  fblog: register one fblog object per framebuffer
  fblog: open fb on registration
  fblog: allow selecting fbs via sysfs
  fblog: cache framebuffer BLANK and SUSPEND states
  fblog: register console driver
  fblog: draw console to framebuffers
  MAINTAINERS: add fblog entry

 MAINTAINERS                     |   6 +
 drivers/video/Kconfig           |   5 +-
 drivers/video/Makefile          |   2 +-
 drivers/video/console/Kconfig   |  37 ++-
 drivers/video/console/Makefile  |   4 +-
 drivers/video/console/bitblit.c | 149 +--------
 drivers/video/console/fbcon.h   |   5 +-
 drivers/video/console/fbdraw.c  | 171 ++++++++++
 drivers/video/console/fbdraw.h  |  30 ++
 drivers/video/console/fblog.c   | 675 ++++++++++++++++++++++++++++++++++++++++
 drivers/video/fbmem.c           |   6 +-
 include/linux/fb.h              |   3 +
 12 files changed, 935 insertions(+), 158 deletions(-)
 create mode 100644 drivers/video/console/fbdraw.c
 create mode 100644 drivers/video/console/fbdraw.h
 create mode 100644 drivers/video/console/fblog.c

-- 
1.7.11.2

^ permalink raw reply

* Re: [PATCH] tty: of_serial: add no-loopback-test property
From: Gabor Juhos @ 2012-07-14 15:41 UTC (permalink / raw)
  To: Alan Cox
  Cc: Grant Likely, Alan Cox, Greg Kroah-Hartman, Rob Landley,
	devicetree-discuss, linux-serial
In-Reply-To: <20120714150101.1810c829@pyramind.ukuu.org.uk>

2012.07.14. 16:01 keltezéssel, Alan Cox írta:
> On Sat, 14 Jul 2012 14:06:20 +0200
> Gabor Juhos <juhosg@openwrt.org> wrote:
> 
>> The loopback test mode is not implemented in all
>> NS16550 compatible UARTs. The 8250 driver uses the
>> UPF_SKIP_TEST flag to indicate this, however it is
>> not possible to set this flag via device-tree.
>>
>> Add a new 'no-loopback-test' property, and modify
>> the of_serial driver to set the UPF_SKIP_TEST flag
>> if the property is present.
>>
>> Signed-off-by: Gabor Juhos <juhosg@openwrt.org>
> 
> Looks good to me but clashes with the sync of the termios changes, which
> we urgently need to get in first.
> 
> Greg - I'll add this to my patch stack with the relevant changes so that
> it doesn't get messed up.

Thank you!

-Gabor
--
To unsubscribe from this list: send the line "unsubscribe linux-serial" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* [PATCH 12/12] tty: localise the lock
From: Alan Cox @ 2012-07-14 14:34 UTC (permalink / raw)
  To: greg, linux-serial
In-Reply-To: <20120714142740.26945.98609.stgit@localhost.localdomain>

From: Alan Cox <alan@linux.intel.com>

The termios and other changes mean the other protections needed on the driver
tty arrays should be adequate. Turn it all back on.

This contains pieces folded in from the fixes made to the original patches

| From: Geert Uytterhoeven <geert@linux-m68k.org>	(fix m68k)
| From: Paul Gortmaker <paul.gortmaker@windriver.com>	(fix cris)
| From: Jiri Kosina <jkosina@suze.cz>			(lockdep)
| From: Eric Dumazet <eric.dumazet@gmail.com>		(lockdep)

Signed-off-by: Alan Cox <alan@linux.intel.com>
---

 drivers/tty/amiserial.c      |   14 ++++----
 drivers/tty/cyclades.c       |    2 +
 drivers/tty/n_r3964.c        |   10 +++---
 drivers/tty/pty.c            |   25 ++++++++-------
 drivers/tty/serial/crisv10.c |    8 ++---
 drivers/tty/synclink.c       |    4 +-
 drivers/tty/synclink_gt.c    |    4 +-
 drivers/tty/synclinkmp.c     |    4 +-
 drivers/tty/tty_io.c         |   67 ++++++++++++++++++++++++----------------
 drivers/tty/tty_ldisc.c      |   67 ++++++++++++++++++++++------------------
 drivers/tty/tty_mutex.c      |   71 +++++++++++++++++++++++++++++++++---------
 drivers/tty/tty_port.c       |    6 ++--
 include/linux/tty.h          |   23 ++++++++------
 net/bluetooth/rfcomm/tty.c   |    4 +-
 14 files changed, 190 insertions(+), 119 deletions(-)

diff --git a/drivers/tty/amiserial.c b/drivers/tty/amiserial.c
index 0e8441e..998731f 100644
--- a/drivers/tty/amiserial.c
+++ b/drivers/tty/amiserial.c
@@ -1033,7 +1033,7 @@ static int get_serial_info(struct tty_struct *tty, struct serial_state *state,
 	if (!retinfo)
 		return -EFAULT;
 	memset(&tmp, 0, sizeof(tmp));
-	tty_lock();
+	tty_lock(tty);
 	tmp.line = tty->index;
 	tmp.port = state->port;
 	tmp.flags = state->tport.flags;
@@ -1042,7 +1042,7 @@ static int get_serial_info(struct tty_struct *tty, struct serial_state *state,
 	tmp.close_delay = state->tport.close_delay;
 	tmp.closing_wait = state->tport.closing_wait;
 	tmp.custom_divisor = state->custom_divisor;
-	tty_unlock();
+	tty_unlock(tty);
 	if (copy_to_user(retinfo,&tmp,sizeof(*retinfo)))
 		return -EFAULT;
 	return 0;
@@ -1059,12 +1059,12 @@ static int set_serial_info(struct tty_struct *tty, struct serial_state *state,
 	if (copy_from_user(&new_serial,new_info,sizeof(new_serial)))
 		return -EFAULT;
 
-	tty_lock();
+	tty_lock(tty);
 	change_spd = ((new_serial.flags ^ port->flags) & ASYNC_SPD_MASK) ||
 		new_serial.custom_divisor != state->custom_divisor;
 	if (new_serial.irq || new_serial.port != state->port ||
 			new_serial.xmit_fifo_size != state->xmit_fifo_size) {
-		tty_unlock();
+		tty_unlock(tty);
 		return -EINVAL;
 	}
   
@@ -1074,7 +1074,7 @@ static int set_serial_info(struct tty_struct *tty, struct serial_state *state,
 		    (new_serial.xmit_fifo_size != state->xmit_fifo_size) ||
 		    ((new_serial.flags & ~ASYNC_USR_MASK) !=
 		     (port->flags & ~ASYNC_USR_MASK))) {
-			tty_unlock();
+			tty_unlock(tty);
 			return -EPERM;
 		}
 		port->flags = ((port->flags & ~ASYNC_USR_MASK) |
@@ -1084,7 +1084,7 @@ static int set_serial_info(struct tty_struct *tty, struct serial_state *state,
 	}
 
 	if (new_serial.baud_base < 9600) {
-		tty_unlock();
+		tty_unlock(tty);
 		return -EINVAL;
 	}
 
@@ -1116,7 +1116,7 @@ check_and_exit:
 		}
 	} else
 		retval = startup(tty, state);
-	tty_unlock();
+	tty_unlock(tty);
 	return retval;
 }
 
diff --git a/drivers/tty/cyclades.c b/drivers/tty/cyclades.c
index e77db71..c8850ea 100644
--- a/drivers/tty/cyclades.c
+++ b/drivers/tty/cyclades.c
@@ -1599,7 +1599,7 @@ static int cy_open(struct tty_struct *tty, struct file *filp)
 	 * If the port is the middle of closing, bail out now
 	 */
 	if (tty_hung_up_p(filp) || (info->port.flags & ASYNC_CLOSING)) {
-		wait_event_interruptible_tty(info->port.close_wait,
+		wait_event_interruptible_tty(tty, info->port.close_wait,
 				!(info->port.flags & ASYNC_CLOSING));
 		return (info->port.flags & ASYNC_HUP_NOTIFY) ? -EAGAIN: -ERESTARTSYS;
 	}
diff --git a/drivers/tty/n_r3964.c b/drivers/tty/n_r3964.c
index 5c6c314..1e64050 100644
--- a/drivers/tty/n_r3964.c
+++ b/drivers/tty/n_r3964.c
@@ -1065,7 +1065,7 @@ static ssize_t r3964_read(struct tty_struct *tty, struct file *file,
 
 	TRACE_L("read()");
 
-	tty_lock();
+	tty_lock(tty);
 
 	pClient = findClient(pInfo, task_pid(current));
 	if (pClient) {
@@ -1077,7 +1077,7 @@ static ssize_t r3964_read(struct tty_struct *tty, struct file *file,
 				goto unlock;
 			}
 			/* block until there is a message: */
-			wait_event_interruptible_tty(pInfo->read_wait,
+			wait_event_interruptible_tty(tty, pInfo->read_wait,
 					(pMsg = remove_msg(pInfo, pClient)));
 		}
 
@@ -1107,7 +1107,7 @@ static ssize_t r3964_read(struct tty_struct *tty, struct file *file,
 	}
 	ret = -EPERM;
 unlock:
-	tty_unlock();
+	tty_unlock(tty);
 	return ret;
 }
 
@@ -1156,7 +1156,7 @@ static ssize_t r3964_write(struct tty_struct *tty, struct file *file,
 	pHeader->locks = 0;
 	pHeader->owner = NULL;
 
-	tty_lock();
+	tty_lock(tty);
 
 	pClient = findClient(pInfo, task_pid(current));
 	if (pClient) {
@@ -1175,7 +1175,7 @@ static ssize_t r3964_write(struct tty_struct *tty, struct file *file,
 	add_tx_queue(pInfo, pHeader);
 	trigger_transmit(pInfo);
 
-	tty_unlock();
+	tty_unlock(tty);
 
 	return 0;
 }
diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c
index 60c08ce..cc8ebf2 100644
--- a/drivers/tty/pty.c
+++ b/drivers/tty/pty.c
@@ -47,6 +47,7 @@ static void pty_close(struct tty_struct *tty, struct file *filp)
 	wake_up_interruptible(&tty->read_wait);
 	wake_up_interruptible(&tty->write_wait);
 	tty->packet = 0;
+	/* Review - krefs on tty_link ?? */
 	if (!tty->link)
 		return;
 	tty->link->packet = 0;
@@ -62,9 +63,9 @@ static void pty_close(struct tty_struct *tty, struct file *filp)
 		        mutex_unlock(&devpts_mutex);
 		}
 #endif
-		tty_unlock();
+		tty_unlock(tty);
 		tty_vhangup(tty->link);
-		tty_lock();
+		tty_lock(tty);
 	}
 }
 
@@ -596,26 +597,27 @@ static int ptmx_open(struct inode *inode, struct file *filp)
 		return retval;
 
 	/* find a device that is not in use. */
-	tty_lock();
+	mutex_lock(&devpts_mutex);
 	index = devpts_new_index(inode);
-	tty_unlock();
 	if (index < 0) {
 		retval = index;
 		goto err_file;
 	}
 
+	mutex_unlock(&devpts_mutex);
+
 	mutex_lock(&tty_mutex);
-	mutex_lock(&devpts_mutex);
 	tty = tty_init_dev(ptm_driver, index);
-	mutex_unlock(&devpts_mutex);
-	tty_lock();
-	mutex_unlock(&tty_mutex);
 
 	if (IS_ERR(tty)) {
 		retval = PTR_ERR(tty);
 		goto out;
 	}
 
+	/* The tty returned here is locked so we can safely
+	   drop the mutex */
+	mutex_unlock(&tty_mutex);
+
 	set_bit(TTY_PTY_LOCK, &tty->flags); /* LOCK THE SLAVE */
 
 	tty_add_file(tty, filp);
@@ -628,16 +630,17 @@ static int ptmx_open(struct inode *inode, struct file *filp)
 	if (retval)
 		goto err_release;
 
-	tty_unlock();
+	tty_unlock(tty);
 	return 0;
 err_release:
-	tty_unlock();
+	tty_unlock(tty);
 	tty_release(inode, filp);
 	return retval;
 out:
+	mutex_unlock(&tty_mutex);
 	devpts_kill_index(inode, index);
-	tty_unlock();
 err_file:
+        mutex_unlock(&devpts_mutex);
 	tty_free_file(filp);
 	return retval;
 }
diff --git a/drivers/tty/serial/crisv10.c b/drivers/tty/serial/crisv10.c
index 6b705b2..a770b10 100644
--- a/drivers/tty/serial/crisv10.c
+++ b/drivers/tty/serial/crisv10.c
@@ -3976,7 +3976,7 @@ block_til_ready(struct tty_struct *tty, struct file * filp,
 	 */
 	if (tty_hung_up_p(filp) ||
 	    (info->flags & ASYNC_CLOSING)) {
-		wait_event_interruptible_tty(info->close_wait,
+		wait_event_interruptible_tty(tty, info->close_wait,
 			!(info->flags & ASYNC_CLOSING));
 #ifdef SERIAL_DO_RESTART
 		if (info->flags & ASYNC_HUP_NOTIFY)
@@ -4052,9 +4052,9 @@ block_til_ready(struct tty_struct *tty, struct file * filp,
 		printk("block_til_ready blocking: ttyS%d, count = %d\n",
 		       info->line, info->count);
 #endif
-		tty_unlock();
+		tty_unlock(tty);
 		schedule();
-		tty_lock();
+		tty_lock(tty);
 	}
 	set_current_state(TASK_RUNNING);
 	remove_wait_queue(&info->open_wait, &wait);
@@ -4115,7 +4115,7 @@ rs_open(struct tty_struct *tty, struct file * filp)
 	 */
 	if (tty_hung_up_p(filp) ||
 	    (info->flags & ASYNC_CLOSING)) {
-		wait_event_interruptible_tty(info->close_wait,
+		wait_event_interruptible_tty(tty, info->close_wait,
 			!(info->flags & ASYNC_CLOSING));
 #ifdef SERIAL_DO_RESTART
 		return ((info->flags & ASYNC_HUP_NOTIFY) ?
diff --git a/drivers/tty/synclink.c b/drivers/tty/synclink.c
index bdeeb31..991bae8 100644
--- a/drivers/tty/synclink.c
+++ b/drivers/tty/synclink.c
@@ -3338,9 +3338,9 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp,
 			printk("%s(%d):block_til_ready blocking on %s count=%d\n",
 				 __FILE__,__LINE__, tty->driver->name, port->count );
 				 
-		tty_unlock();
+		tty_unlock(tty);
 		schedule();
-		tty_lock();
+		tty_lock(tty);
 	}
 	
 	set_current_state(TASK_RUNNING);
diff --git a/drivers/tty/synclink_gt.c b/drivers/tty/synclink_gt.c
index f02d18a..9130253 100644
--- a/drivers/tty/synclink_gt.c
+++ b/drivers/tty/synclink_gt.c
@@ -3336,9 +3336,9 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp,
 		}
 
 		DBGINFO(("%s block_til_ready wait\n", tty->driver->name));
-		tty_unlock();
+		tty_unlock(tty);
 		schedule();
-		tty_lock();
+		tty_lock(tty);
 	}
 
 	set_current_state(TASK_RUNNING);
diff --git a/drivers/tty/synclinkmp.c b/drivers/tty/synclinkmp.c
index ae75a3c..95fd4e2 100644
--- a/drivers/tty/synclinkmp.c
+++ b/drivers/tty/synclinkmp.c
@@ -3357,9 +3357,9 @@ static int block_til_ready(struct tty_struct *tty, struct file *filp,
 			printk("%s(%d):%s block_til_ready() count=%d\n",
 				 __FILE__,__LINE__, tty->driver->name, port->count );
 
-		tty_unlock();
+		tty_unlock(tty);
 		schedule();
-		tty_lock();
+		tty_lock(tty);
 	}
 
 	set_current_state(TASK_RUNNING);
diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c
index e991a4b..78d7e7a 100644
--- a/drivers/tty/tty_io.c
+++ b/drivers/tty/tty_io.c
@@ -185,6 +185,7 @@ void free_tty_struct(struct tty_struct *tty)
 		put_device(tty->dev);
 	kfree(tty->write_buf);
 	tty_buffer_free_all(tty);
+	tty->magic = 0xDEADDEAD;
 	kfree(tty);
 }
 
@@ -573,7 +574,7 @@ void __tty_hangup(struct tty_struct *tty)
 	}
 	spin_unlock(&redirect_lock);
 
-	tty_lock();
+	tty_lock(tty);
 
 	/* some functions below drop BTM, so we need this bit */
 	set_bit(TTY_HUPPING, &tty->flags);
@@ -666,7 +667,7 @@ void __tty_hangup(struct tty_struct *tty)
 	clear_bit(TTY_HUPPING, &tty->flags);
 	tty_ldisc_enable(tty);
 
-	tty_unlock();
+	tty_unlock(tty);
 
 	if (f)
 		fput(f);
@@ -1103,12 +1104,12 @@ void tty_write_message(struct tty_struct *tty, char *msg)
 {
 	if (tty) {
 		mutex_lock(&tty->atomic_write_lock);
-		tty_lock();
+		tty_lock(tty);
 		if (tty->ops->write && !test_bit(TTY_CLOSING, &tty->flags)) {
-			tty_unlock();
+			tty_unlock(tty);
 			tty->ops->write(tty, msg, strlen(msg));
 		} else
-			tty_unlock();
+			tty_unlock(tty);
 		tty_write_unlock(tty);
 	}
 	return;
@@ -1405,6 +1406,7 @@ struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx)
 	}
 	initialize_tty_struct(tty, driver, idx);
 
+	tty_lock(tty);
 	retval = tty_driver_install_tty(driver, tty);
 	if (retval < 0)
 		goto err_deinit_tty;
@@ -1420,9 +1422,11 @@ struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx)
 	retval = tty_ldisc_setup(tty, tty->link);
 	if (retval)
 		goto err_release_tty;
+	/* Return the tty locked so that it cannot vanish under the caller */
 	return tty;
 
 err_deinit_tty:
+	tty_unlock(tty);
 	deinitialize_tty_struct(tty);
 	free_tty_struct(tty);
 err_module_put:
@@ -1431,6 +1435,7 @@ err_module_put:
 
 	/* call the tty release_tty routine to clean out this slot */
 err_release_tty:
+	tty_unlock(tty);
 	printk_ratelimited(KERN_INFO "tty_init_dev: ldisc open failed, "
 				 "clearing slot %d\n", idx);
 	release_tty(tty, idx);
@@ -1612,7 +1617,7 @@ int tty_release(struct inode *inode, struct file *filp)
 	if (tty_paranoia_check(tty, inode, __func__))
 		return 0;
 
-	tty_lock();
+	tty_lock(tty);
 	check_tty_count(tty, __func__);
 
 	__tty_fasync(-1, filp, 0);
@@ -1621,10 +1626,11 @@ int tty_release(struct inode *inode, struct file *filp)
 	pty_master = (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
 		      tty->driver->subtype == PTY_TYPE_MASTER);
 	devpts = (tty->driver->flags & TTY_DRIVER_DEVPTS_MEM) != 0;
+	/* Review: parallel close */
 	o_tty = tty->link;
 
 	if (tty_release_checks(tty, o_tty, idx)) {
-		tty_unlock();
+		tty_unlock(tty);
 		return 0;
 	}
 
@@ -1636,7 +1642,7 @@ int tty_release(struct inode *inode, struct file *filp)
 	if (tty->ops->close)
 		tty->ops->close(tty, filp);
 
-	tty_unlock();
+	tty_unlock(tty);
 	/*
 	 * Sanity check: if tty->count is going to zero, there shouldn't be
 	 * any waiters on tty->read_wait or tty->write_wait.  We test the
@@ -1659,7 +1665,7 @@ int tty_release(struct inode *inode, struct file *filp)
 		   opens on /dev/tty */
 
 		mutex_lock(&tty_mutex);
-		tty_lock();
+		tty_lock_pair(tty, o_tty);
 		tty_closing = tty->count <= 1;
 		o_tty_closing = o_tty &&
 			(o_tty->count <= (pty_master ? 1 : 0));
@@ -1690,7 +1696,7 @@ int tty_release(struct inode *inode, struct file *filp)
 
 		printk(KERN_WARNING "%s: %s: read/write wait queue active!\n",
 				__func__, tty_name(tty, buf));
-		tty_unlock();
+		tty_unlock_pair(tty, o_tty);
 		mutex_unlock(&tty_mutex);
 		schedule();
 	}
@@ -1753,7 +1759,7 @@ int tty_release(struct inode *inode, struct file *filp)
 
 	/* check whether both sides are closing ... */
 	if (!tty_closing || (o_tty && !o_tty_closing)) {
-		tty_unlock();
+		tty_unlock_pair(tty, o_tty);
 		return 0;
 	}
 
@@ -1766,14 +1772,16 @@ int tty_release(struct inode *inode, struct file *filp)
 	tty_ldisc_release(tty, o_tty);
 	/*
 	 * The release_tty function takes care of the details of clearing
-	 * the slots and preserving the termios structure.
+	 * the slots and preserving the termios structure. The tty_unlock_pair
+	 * should be safe as we keep a kref while the tty is locked (so the
+	 * unlock never unlocks a freed tty).
 	 */
 	release_tty(tty, idx);
+	tty_unlock_pair(tty, o_tty);
 
 	/* Make this pty number available for reallocation */
 	if (devpts)
 		devpts_kill_index(inode, idx);
-	tty_unlock();
 	return 0;
 }
 
@@ -1877,6 +1885,9 @@ static struct tty_driver *tty_lookup_driver(dev_t device, struct file *filp,
  *	Locking: tty_mutex protects tty, tty_lookup_driver and tty_init_dev.
  *		 tty->count should protect the rest.
  *		 ->siglock protects ->signal/->sighand
+ *
+ *	Note: the tty_unlock/lock cases without a ref are only safe due to
+ *	tty_mutex
  */
 
 static int tty_open(struct inode *inode, struct file *filp)
@@ -1900,8 +1911,7 @@ retry_open:
 	retval = 0;
 
 	mutex_lock(&tty_mutex);
-	tty_lock();
-
+	/* This is protected by the tty_mutex */
 	tty = tty_open_current_tty(device, filp);
 	if (IS_ERR(tty)) {
 		retval = PTR_ERR(tty);
@@ -1922,17 +1932,19 @@ retry_open:
 	}
 
 	if (tty) {
+		tty_lock(tty);
 		retval = tty_reopen(tty);
-		if (retval)
+		if (retval < 0) {
+			tty_unlock(tty);
 			tty = ERR_PTR(retval);
-	} else
+		}
+	} else	/* Returns with the tty_lock held for now */
 		tty = tty_init_dev(driver, index);
 
 	mutex_unlock(&tty_mutex);
 	if (driver)
 		tty_driver_kref_put(driver);
 	if (IS_ERR(tty)) {
-		tty_unlock();
 		retval = PTR_ERR(tty);
 		goto err_file;
 	}
@@ -1961,7 +1973,7 @@ retry_open:
 		printk(KERN_DEBUG "%s: error %d in opening %s...\n", __func__,
 				retval, tty->name);
 #endif
-		tty_unlock(); /* need to call tty_release without BTM */
+		tty_unlock(tty); /* need to call tty_release without BTM */
 		tty_release(inode, filp);
 		if (retval != -ERESTARTSYS)
 			return retval;
@@ -1973,17 +1985,15 @@ retry_open:
 		/*
 		 * Need to reset f_op in case a hangup happened.
 		 */
-		tty_lock();
 		if (filp->f_op == &hung_up_tty_fops)
 			filp->f_op = &tty_fops;
-		tty_unlock();
 		goto retry_open;
 	}
-	tty_unlock();
+	tty_unlock(tty);
 
 
 	mutex_lock(&tty_mutex);
-	tty_lock();
+	tty_lock(tty);
 	spin_lock_irq(&current->sighand->siglock);
 	if (!noctty &&
 	    current->signal->leader &&
@@ -1991,11 +2001,10 @@ retry_open:
 	    tty->session == NULL)
 		__proc_set_tty(current, tty);
 	spin_unlock_irq(&current->sighand->siglock);
-	tty_unlock();
+	tty_unlock(tty);
 	mutex_unlock(&tty_mutex);
 	return 0;
 err_unlock:
-	tty_unlock();
 	mutex_unlock(&tty_mutex);
 	/* after locks to avoid deadlock */
 	if (!IS_ERR_OR_NULL(driver))
@@ -2078,10 +2087,13 @@ out:
 
 static int tty_fasync(int fd, struct file *filp, int on)
 {
+	struct tty_struct *tty = file_tty(filp);
 	int retval;
-	tty_lock();
+
+	tty_lock(tty);
 	retval = __tty_fasync(fd, filp, on);
-	tty_unlock();
+	tty_unlock(tty);
+
 	return retval;
 }
 
@@ -2918,6 +2930,7 @@ void initialize_tty_struct(struct tty_struct *tty,
 	tty->pgrp = NULL;
 	tty->overrun_time = jiffies;
 	tty_buffer_init(tty);
+	mutex_init(&tty->legacy_mutex);
 	mutex_init(&tty->termios_mutex);
 	mutex_init(&tty->ldisc_mutex);
 	init_waitqueue_head(&tty->write_wait);
diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c
index 073e533..9260c08 100644
--- a/drivers/tty/tty_ldisc.c
+++ b/drivers/tty/tty_ldisc.c
@@ -568,7 +568,7 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc)
 	if (IS_ERR(new_ldisc))
 		return PTR_ERR(new_ldisc);
 
-	tty_lock();
+	tty_lock(tty);
 	/*
 	 *	We need to look at the tty locking here for pty/tty pairs
 	 *	when both sides try to change in parallel.
@@ -582,12 +582,12 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc)
 	 */
 
 	if (tty->ldisc->ops->num == ldisc) {
-		tty_unlock();
+		tty_unlock(tty);
 		tty_ldisc_put(new_ldisc);
 		return 0;
 	}
 
-	tty_unlock();
+	tty_unlock(tty);
 	/*
 	 *	Problem: What do we do if this blocks ?
 	 *	We could deadlock here
@@ -595,7 +595,7 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc)
 
 	tty_wait_until_sent(tty, 0);
 
-	tty_lock();
+	tty_lock(tty);
 	mutex_lock(&tty->ldisc_mutex);
 
 	/*
@@ -605,10 +605,10 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc)
 
 	while (test_bit(TTY_LDISC_CHANGING, &tty->flags)) {
 		mutex_unlock(&tty->ldisc_mutex);
-		tty_unlock();
+		tty_unlock(tty);
 		wait_event(tty_ldisc_wait,
 			test_bit(TTY_LDISC_CHANGING, &tty->flags) == 0);
-		tty_lock();
+		tty_lock(tty);
 		mutex_lock(&tty->ldisc_mutex);
 	}
 
@@ -623,7 +623,7 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc)
 
 	o_ldisc = tty->ldisc;
 
-	tty_unlock();
+	tty_unlock(tty);
 	/*
 	 *	Make sure we don't change while someone holds a
 	 *	reference to the line discipline. The TTY_LDISC bit
@@ -650,7 +650,7 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc)
 
 	retval = tty_ldisc_wait_idle(tty, 5 * HZ);
 
-	tty_lock();
+	tty_lock(tty);
 	mutex_lock(&tty->ldisc_mutex);
 
 	/* handle wait idle failure locked */
@@ -665,7 +665,7 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc)
 		clear_bit(TTY_LDISC_CHANGING, &tty->flags);
 		mutex_unlock(&tty->ldisc_mutex);
 		tty_ldisc_put(new_ldisc);
-		tty_unlock();
+		tty_unlock(tty);
 		return -EIO;
 	}
 
@@ -708,7 +708,7 @@ enable:
 	if (o_work)
 		schedule_work(&o_tty->buf.work);
 	mutex_unlock(&tty->ldisc_mutex);
-	tty_unlock();
+	tty_unlock(tty);
 	return retval;
 }
 
@@ -816,11 +816,11 @@ void tty_ldisc_hangup(struct tty_struct *tty)
 	 * need to wait for another function taking the BTM
 	 */
 	clear_bit(TTY_LDISC, &tty->flags);
-	tty_unlock();
+	tty_unlock(tty);
 	cancel_work_sync(&tty->buf.work);
 	mutex_unlock(&tty->ldisc_mutex);
 retry:
-	tty_lock();
+	tty_lock(tty);
 	mutex_lock(&tty->ldisc_mutex);
 
 	/* At this point we have a closed ldisc and we want to
@@ -831,7 +831,7 @@ retry:
 		if (atomic_read(&tty->ldisc->users) != 1) {
 			char cur_n[TASK_COMM_LEN], tty_n[64];
 			long timeout = 3 * HZ;
-			tty_unlock();
+			tty_unlock(tty);
 
 			while (tty_ldisc_wait_idle(tty, timeout) == -EBUSY) {
 				timeout = MAX_SCHEDULE_TIMEOUT;
@@ -894,6 +894,23 @@ int tty_ldisc_setup(struct tty_struct *tty, struct tty_struct *o_tty)
 	tty_ldisc_enable(tty);
 	return 0;
 }
+
+static void tty_ldisc_kill(struct tty_struct *tty)
+{
+	mutex_lock(&tty->ldisc_mutex);
+	/*
+	 * Now kill off the ldisc
+	 */
+	tty_ldisc_close(tty, tty->ldisc);
+	tty_ldisc_put(tty->ldisc);
+	/* Force an oops if we mess this up */
+	tty->ldisc = NULL;
+
+	/* Ensure the next open requests the N_TTY ldisc */
+	tty_set_termios_ldisc(tty, N_TTY);
+	mutex_unlock(&tty->ldisc_mutex);
+}
+
 /**
  *	tty_ldisc_release		-	release line discipline
  *	@tty: tty being shut down
@@ -912,27 +929,19 @@ void tty_ldisc_release(struct tty_struct *tty, struct tty_struct *o_tty)
 	 * race with the set_ldisc code path.
 	 */
 
-	tty_unlock();
+	tty_unlock_pair(tty, o_tty);
 	tty_ldisc_halt(tty);
 	tty_ldisc_flush_works(tty);
-	tty_lock();
-
-	mutex_lock(&tty->ldisc_mutex);
-	/*
-	 * Now kill off the ldisc
-	 */
-	tty_ldisc_close(tty, tty->ldisc);
-	tty_ldisc_put(tty->ldisc);
-	/* Force an oops if we mess this up */
-	tty->ldisc = NULL;
+	if (o_tty) {
+		tty_ldisc_halt(o_tty);
+		tty_ldisc_flush_works(o_tty);
+	}
+	tty_lock_pair(tty, o_tty);
 
-	/* Ensure the next open requests the N_TTY ldisc */
-	tty_set_termios_ldisc(tty, N_TTY);
-	mutex_unlock(&tty->ldisc_mutex);
 
-	/* This will need doing differently if we need to lock */
+	tty_ldisc_kill(tty);
 	if (o_tty)
-		tty_ldisc_release(o_tty, NULL);
+		tty_ldisc_kill(o_tty);
 
 	/* And the memory resources remaining (buffers, termios) will be
 	   disposed of when the kref hits zero */
diff --git a/drivers/tty/tty_mutex.c b/drivers/tty/tty_mutex.c
index 9ff986c..67feac9 100644
--- a/drivers/tty/tty_mutex.c
+++ b/drivers/tty/tty_mutex.c
@@ -4,29 +4,70 @@
 #include <linux/semaphore.h>
 #include <linux/sched.h>
 
-/*
- * The 'big tty mutex'
- *
- * This mutex is taken and released by tty_lock() and tty_unlock(),
- * replacing the older big kernel lock.
- * It can no longer be taken recursively, and does not get
- * released implicitly while sleeping.
- *
- * Don't use in new code.
- */
-static DEFINE_MUTEX(big_tty_mutex);
+/* Legacy tty mutex glue */
+
+enum {
+	TTY_MUTEX_NORMAL,
+	TTY_MUTEX_NESTED,
+};
 
 /*
  * Getting the big tty mutex.
  */
-void __lockfunc tty_lock(void)
+
+static void __lockfunc tty_lock_nested(struct tty_struct *tty,
+				       unsigned int subclass)
 {
-	mutex_lock(&big_tty_mutex);
+	if (tty->magic != TTY_MAGIC) {
+		printk(KERN_ERR "L Bad %p\n", tty);
+		WARN_ON(1);
+		return;
+	}
+	tty_kref_get(tty);
+	mutex_lock_nested(&tty->legacy_mutex, subclass);
+}
+
+void __lockfunc tty_lock(struct tty_struct *tty)
+{
+	return tty_lock_nested(tty, TTY_MUTEX_NORMAL);
 }
 EXPORT_SYMBOL(tty_lock);
 
-void __lockfunc tty_unlock(void)
+void __lockfunc tty_unlock(struct tty_struct *tty)
 {
-	mutex_unlock(&big_tty_mutex);
+	if (tty->magic != TTY_MAGIC) {
+		printk(KERN_ERR "U Bad %p\n", tty);
+		WARN_ON(1);
+		return;
+	}
+	mutex_unlock(&tty->legacy_mutex);
+	tty_kref_put(tty);
 }
 EXPORT_SYMBOL(tty_unlock);
+
+/*
+ * Getting the big tty mutex for a pair of ttys with lock ordering
+ * On a non pty/tty pair tty2 can be NULL which is just fine.
+ */
+void __lockfunc tty_lock_pair(struct tty_struct *tty,
+					struct tty_struct *tty2)
+{
+	if (tty < tty2) {
+		tty_lock(tty);
+		tty_lock_nested(tty2, TTY_MUTEX_NESTED);
+	} else {
+		if (tty2 && tty2 != tty)
+			tty_lock(tty2);
+		tty_lock_nested(tty, TTY_MUTEX_NESTED);
+	}
+}
+EXPORT_SYMBOL(tty_lock_pair);
+
+void __lockfunc tty_unlock_pair(struct tty_struct *tty,
+						struct tty_struct *tty2)
+{
+	tty_unlock(tty);
+	if (tty2 && tty2 != tty)
+		tty_unlock(tty2);
+}
+EXPORT_SYMBOL(tty_unlock_pair);
diff --git a/drivers/tty/tty_port.c b/drivers/tty/tty_port.c
index edcb827..5246763 100644
--- a/drivers/tty/tty_port.c
+++ b/drivers/tty/tty_port.c
@@ -239,7 +239,7 @@ int tty_port_block_til_ready(struct tty_port *port,
 
 	/* block if port is in the process of being closed */
 	if (tty_hung_up_p(filp) || port->flags & ASYNC_CLOSING) {
-		wait_event_interruptible_tty(port->close_wait,
+		wait_event_interruptible_tty(tty, port->close_wait,
 				!(port->flags & ASYNC_CLOSING));
 		if (port->flags & ASYNC_HUP_NOTIFY)
 			return -EAGAIN;
@@ -305,9 +305,9 @@ int tty_port_block_til_ready(struct tty_port *port,
 			retval = -ERESTARTSYS;
 			break;
 		}
-		tty_unlock();
+		tty_unlock(tty);
 		schedule();
-		tty_lock();
+		tty_lock(tty);
 	}
 	finish_wait(&port->open_wait, &wait);
 
diff --git a/include/linux/tty.h b/include/linux/tty.h
index a39e723..acca24b 100644
--- a/include/linux/tty.h
+++ b/include/linux/tty.h
@@ -268,6 +268,7 @@ struct tty_struct {
 	struct mutex ldisc_mutex;
 	struct tty_ldisc *ldisc;
 
+	struct mutex legacy_mutex;
 	struct mutex termios_mutex;
 	spinlock_t ctrl_lock;
 	/* Termios values are protected by the termios mutex */
@@ -609,8 +610,12 @@ extern long vt_compat_ioctl(struct tty_struct *tty,
 
 /* tty_mutex.c */
 /* functions for preparation of BKL removal */
-extern void __lockfunc tty_lock(void) __acquires(tty_lock);
-extern void __lockfunc tty_unlock(void) __releases(tty_lock);
+extern void __lockfunc tty_lock(struct tty_struct *tty);
+extern void __lockfunc tty_unlock(struct tty_struct *tty);
+extern void __lockfunc tty_lock_pair(struct tty_struct *tty,
+				struct tty_struct *tty2);
+extern void __lockfunc tty_unlock_pair(struct tty_struct *tty,
+				struct tty_struct *tty2);
 
 /*
  * this shall be called only from where BTM is held (like close)
@@ -625,9 +630,9 @@ extern void __lockfunc tty_unlock(void) __releases(tty_lock);
 static inline void tty_wait_until_sent_from_close(struct tty_struct *tty,
 		long timeout)
 {
-	tty_unlock(); /* tty->ops->close holds the BTM, drop it while waiting */
+	tty_unlock(tty); /* tty->ops->close holds the BTM, drop it while waiting */
 	tty_wait_until_sent(tty, timeout);
-	tty_lock();
+	tty_lock(tty);
 }
 
 /*
@@ -642,16 +647,16 @@ static inline void tty_wait_until_sent_from_close(struct tty_struct *tty,
  *
  * Do not use in new code.
  */
-#define wait_event_interruptible_tty(wq, condition)			\
+#define wait_event_interruptible_tty(tty, wq, condition)		\
 ({									\
 	int __ret = 0;							\
 	if (!(condition)) {						\
-		__wait_event_interruptible_tty(wq, condition, __ret);	\
+		__wait_event_interruptible_tty(tty, wq, condition, __ret);	\
 	}								\
 	__ret;								\
 })
 
-#define __wait_event_interruptible_tty(wq, condition, ret)		\
+#define __wait_event_interruptible_tty(tty, wq, condition, ret)		\
 do {									\
 	DEFINE_WAIT(__wait);						\
 									\
@@ -660,9 +665,9 @@ do {									\
 		if (condition)						\
 			break;						\
 		if (!signal_pending(current)) {				\
-			tty_unlock();					\
+			tty_unlock(tty);					\
 			schedule();					\
-			tty_lock();					\
+			tty_lock(tty);					\
 			continue;					\
 		}							\
 		ret = -ERESTARTSYS;					\
diff --git a/net/bluetooth/rfcomm/tty.c b/net/bluetooth/rfcomm/tty.c
index 87ddd05..b54c86a 100644
--- a/net/bluetooth/rfcomm/tty.c
+++ b/net/bluetooth/rfcomm/tty.c
@@ -705,9 +705,9 @@ static int rfcomm_tty_open(struct tty_struct *tty, struct file *filp)
 			break;
 		}
 
-		tty_unlock();
+		tty_unlock(tty);
 		schedule();
-		tty_lock();
+		tty_lock(tty);
 	}
 	set_current_state(TASK_RUNNING);
 	remove_wait_queue(&dev->wait, &wait);


^ permalink raw reply related

* [PATCH 11/12] tty: Move the handling of the tty release logic
From: Alan Cox @ 2012-07-14 14:34 UTC (permalink / raw)
  To: greg, linux-serial
In-Reply-To: <20120714142740.26945.98609.stgit@localhost.localdomain>

From: Alan Cox <alan@linux.intel.com>

Now that we don't have tty->termios tied to drivers->tty we can untangle
the logic here. In addition we can push the removal logic out of the
destructor path.

At that point we can think about sorting out tty_port and console and all
the other ugly hangovers.

Signed-off-by: Alan Cox <alan@linux.intel.com>
---

 drivers/tty/pty.c               |   12 ++----------
 drivers/tty/tty_io.c            |   16 +++++-----------
 drivers/tty/vt/vt.c             |    1 -
 drivers/usb/serial/usb-serial.c |    3 +--
 include/linux/tty.h             |    1 -
 include/linux/tty_driver.h      |   11 +++--------
 6 files changed, 11 insertions(+), 33 deletions(-)

diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c
index 5ad7ccc..60c08ce 100644
--- a/drivers/tty/pty.c
+++ b/drivers/tty/pty.c
@@ -527,12 +527,6 @@ static struct tty_struct *pts_unix98_lookup(struct tty_driver *driver,
 	return tty;
 }
 
-static void pty_unix98_shutdown(struct tty_struct *tty)
-{
-	tty_driver_remove_tty(tty->driver, tty);
-	/* We have our own method as we don't use the tty index */
-}
-
 /* We have no need to install and remove our tty objects as devpts does all
    the work for us */
 
@@ -558,9 +552,8 @@ static const struct tty_operations ptm_unix98_ops = {
 	.unthrottle = pty_unthrottle,
 	.set_termios = pty_set_termios,
 	.ioctl = pty_unix98_ioctl,
-	.shutdown = pty_unix98_shutdown,
-	.cleanup = pty_cleanup,
-	.resize = pty_resize
+	.resize = pty_resize,
+	.cleanup = pty_cleanup
 };
 
 static const struct tty_operations pty_unix98_ops = {
@@ -575,7 +568,6 @@ static const struct tty_operations pty_unix98_ops = {
 	.chars_in_buffer = pty_chars_in_buffer,
 	.unthrottle = pty_unthrottle,
 	.set_termios = pty_set_termios,
-	.shutdown = pty_unix98_shutdown,
 	.cleanup = pty_cleanup,
 };
 
diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c
index d6feca1..e991a4b 100644
--- a/drivers/tty/tty_io.c
+++ b/drivers/tty/tty_io.c
@@ -1446,12 +1446,6 @@ void tty_free_termios(struct tty_struct *tty)
 }
 EXPORT_SYMBOL(tty_free_termios);
 
-void tty_shutdown(struct tty_struct *tty)
-{
-	tty_driver_remove_tty(tty->driver, tty);
-	tty_free_termios(tty);
-}
-EXPORT_SYMBOL(tty_shutdown);
 
 /**
  *	release_one_tty		-	release tty structure memory
@@ -1495,11 +1489,6 @@ static void queue_release_one_tty(struct kref *kref)
 {
 	struct tty_struct *tty = container_of(kref, struct tty_struct, kref);
 
-	if (tty->ops->shutdown)
-		tty->ops->shutdown(tty);
-	else
-		tty_shutdown(tty);
-
 	/* The hangup queue is now free so we can reuse it rather than
 	   waste a chunk of memory for each port */
 	INIT_WORK(&tty->hangup_work, release_one_tty);
@@ -1539,6 +1528,11 @@ static void release_tty(struct tty_struct *tty, int idx)
 	/* This should always be true but check for the moment */
 	WARN_ON(tty->index != idx);
 
+	if (tty->ops->shutdown)
+		tty->ops->shutdown(tty);
+	tty_free_termios(tty);
+	tty_driver_remove_tty(tty->driver, tty);
+
 	if (tty->link)
 		tty_kref_put(tty->link);
 	tty_kref_put(tty);
diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c
index dbceaeb..e07ded3 100644
--- a/drivers/tty/vt/vt.c
+++ b/drivers/tty/vt/vt.c
@@ -2850,7 +2850,6 @@ static void con_shutdown(struct tty_struct *tty)
 	console_lock();
 	vc->port.tty = NULL;
 	console_unlock();
-	tty_shutdown(tty);
 }
 
 static int default_italic_color    = 2; // green (ASCII)
diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c
index 5fe2135..aa4b0d7 100644
--- a/drivers/usb/serial/usb-serial.c
+++ b/drivers/usb/serial/usb-serial.c
@@ -305,8 +305,7 @@ static void serial_close(struct tty_struct *tty, struct file *filp)
  * Do the resource freeing and refcount dropping for the port.
  * Avoid freeing the console.
  *
- * Called asynchronously after the last tty kref is dropped,
- * and the tty layer has already done the tty_shutdown(tty);
+ * Called asynchronously after the last tty kref is dropped.
  */
 static void serial_cleanup(struct tty_struct *tty)
 {
diff --git a/include/linux/tty.h b/include/linux/tty.h
index d5e75ee..a39e723 100644
--- a/include/linux/tty.h
+++ b/include/linux/tty.h
@@ -423,7 +423,6 @@ extern void tty_unthrottle(struct tty_struct *tty);
 extern int tty_do_resize(struct tty_struct *tty, struct winsize *ws);
 extern void tty_driver_remove_tty(struct tty_driver *driver,
 				  struct tty_struct *tty);
-extern void tty_shutdown(struct tty_struct *tty);
 extern void tty_free_termios(struct tty_struct *tty);
 extern int is_current_pgrp_orphaned(void);
 extern struct pid *tty_get_pgrp(struct tty_struct *tty);
diff --git a/include/linux/tty_driver.h b/include/linux/tty_driver.h
index 04419c1..80e72dc 100644
--- a/include/linux/tty_driver.h
+++ b/include/linux/tty_driver.h
@@ -45,14 +45,9 @@
  *
  * void (*shutdown)(struct tty_struct * tty);
  *
- * 	This routine is called synchronously when a particular tty device
- *	is closed for the last time freeing up the resources.
- *	Note that tty_shutdown() is not called if ops->shutdown is defined.
- *	This means one is responsible to take care of calling ops->remove (e.g.
- *	via tty_driver_remove_tty) and releasing tty->termios.
- *	Note that this hook may be called from *all* the contexts where one
- *	uses tty refcounting (e.g. tty_port_tty_get).
- *
+ * 	This routine is called under the tty lock when a particular tty device
+ *	is closed for the last time. It executes before the tty resources
+ *	are freed so may execute while another function holds a tty kref.
  *
  * void (*cleanup)(struct tty_struct * tty);
  *


^ permalink raw reply related

* [PATCH 10/12] vt: fix the keyboard/led locking
From: Alan Cox @ 2012-07-14 14:34 UTC (permalink / raw)
  To: greg, linux-serial
In-Reply-To: <20120714142740.26945.98609.stgit@localhost.localdomain>

From: Alan Cox <alan@linux.intel.com>

We touch the LED from both keyboard callback and direct paths. In
one case we've got the lock held way up the call chain and in the
other we haven't. This leads to complete insanity so fix it by giving
the LED bits their own lock.

Signed-off-by: Alan Cox <alan@linux.intel.com>
---

 drivers/tty/vt/keyboard.c |   41 +++++++++++++++++++++++------------------
 include/linux/kbd_kern.h  |    1 -
 2 files changed, 23 insertions(+), 19 deletions(-)

diff --git a/drivers/tty/vt/keyboard.c b/drivers/tty/vt/keyboard.c
index 9b4f60a..681765b 100644
--- a/drivers/tty/vt/keyboard.c
+++ b/drivers/tty/vt/keyboard.c
@@ -119,6 +119,7 @@ static const int NR_TYPES = ARRAY_SIZE(max_vals);
 
 static struct input_handler kbd_handler;
 static DEFINE_SPINLOCK(kbd_event_lock);
+static DEFINE_SPINLOCK(led_lock);
 static unsigned long key_down[BITS_TO_LONGS(KEY_CNT)];	/* keyboard key bitmap */
 static unsigned char shift_down[NR_SHIFT];		/* shift state counters.. */
 static bool dead_key_next;
@@ -984,7 +985,7 @@ static void k_brl(struct vc_data *vc, unsigned char value, char up_flag)
  * or (ii) whatever pattern of lights people want to show using KDSETLED,
  * or (iii) specified bits of specified words in kernel memory.
  */
-unsigned char getledstate(void)
+static unsigned char getledstate(void)
 {
 	return ledstate;
 }
@@ -992,7 +993,7 @@ unsigned char getledstate(void)
 void setledstate(struct kbd_struct *kbd, unsigned int led)
 {
         unsigned long flags;
-        spin_lock_irqsave(&kbd_event_lock, flags);
+        spin_lock_irqsave(&led_lock, flags);
 	if (!(led & ~7)) {
 		ledioctl = led;
 		kbd->ledmode = LED_SHOW_IOCTL;
@@ -1000,7 +1001,7 @@ void setledstate(struct kbd_struct *kbd, unsigned int led)
 		kbd->ledmode = LED_SHOW_FLAGS;
 
 	set_leds();
-	spin_unlock_irqrestore(&kbd_event_lock, flags);
+	spin_unlock_irqrestore(&led_lock, flags);
 }
 
 static inline unsigned char getleds(void)
@@ -1051,8 +1052,11 @@ int vt_get_leds(int console, int flag)
 {
 	struct kbd_struct * kbd = kbd_table + console;
 	int ret;
+	unsigned long flags;
 
+	spin_lock_irqsave(&led_lock, flags);
 	ret = vc_kbd_led(kbd, flag);
+	spin_unlock_irqrestore(&led_lock, flags);
 
 	return ret;
 }
@@ -1088,11 +1092,11 @@ void vt_set_led_state(int console, int leds)
 void vt_kbd_con_start(int console)
 {
 	struct kbd_struct * kbd = kbd_table + console;
-/*	unsigned long flags; */
-/*	spin_lock_irqsave(&kbd_event_lock, flags); */
+	unsigned long flags;
+	spin_lock_irqsave(&led_lock, flags);
 	clr_vc_kbd_led(kbd, VC_SCROLLOCK);
 	set_leds();
-/*	spin_unlock_irqrestore(&kbd_event_lock, flags); */
+	spin_unlock_irqrestore(&led_lock, flags);
 }
 
 /**
@@ -1101,21 +1105,15 @@ void vt_kbd_con_start(int console)
  *
  *	Handle console stop. This is a wrapper for the VT layer
  *	so that we can keep kbd knowledge internal
- *
- *	FIXME: We eventually need to hold the kbd lock here to protect
- *	the LED updating. We can't do it yet because fn_hold calls stop_tty
- *	and start_tty under the kbd_event_lock, while normal tty paths
- *	don't hold the lock. We probably need to split out an LED lock
- *	but not during an -rc release!
  */
 void vt_kbd_con_stop(int console)
 {
 	struct kbd_struct * kbd = kbd_table + console;
-/*	unsigned long flags; */
-/*	spin_lock_irqsave(&kbd_event_lock, flags); */
+	unsigned long flags;
+	spin_lock_irqsave(&led_lock, flags);
 	set_vc_kbd_led(kbd, VC_SCROLLOCK);
 	set_leds();
-/*	spin_unlock_irqrestore(&kbd_event_lock, flags); */
+	spin_unlock_irqrestore(&led_lock, flags);
 }
 
 /*
@@ -1127,7 +1125,12 @@ void vt_kbd_con_stop(int console)
  */
 static void kbd_bh(unsigned long dummy)
 {
-	unsigned char leds = getleds();
+	unsigned char leds;
+	unsigned long flags;
+	
+	spin_lock_irqsave(&led_lock, flags);
+	leds = getleds();
+	spin_unlock_irqrestore(&led_lock, flags);
 
 	if (leds != ledstate) {
 		input_handler_for_each_handle(&kbd_handler, &leds,
@@ -2032,11 +2035,11 @@ int vt_do_kdskled(int console, int cmd, unsigned long arg, int perm)
 			return -EPERM;
 		if (arg & ~0x77)
 			return -EINVAL;
-                spin_lock_irqsave(&kbd_event_lock, flags);
+                spin_lock_irqsave(&led_lock, flags);
 		kbd->ledflagstate = (arg & 7);
 		kbd->default_ledflagstate = ((arg >> 4) & 7);
 		set_leds();
-                spin_unlock_irqrestore(&kbd_event_lock, flags);
+                spin_unlock_irqrestore(&led_lock, flags);
 		return 0;
 
 	/* the ioctls below only set the lights, not the functions */
@@ -2131,8 +2134,10 @@ void vt_reset_keyboard(int console)
 	clr_vc_kbd_mode(kbd, VC_CRLF);
 	kbd->lockstate = 0;
 	kbd->slockstate = 0;
+	spin_lock(&led_lock);
 	kbd->ledmode = LED_SHOW_FLAGS;
 	kbd->ledflagstate = kbd->default_ledflagstate;
+	spin_unlock(&led_lock);
 	/* do not do set_leds here because this causes an endless tasklet loop
 	   when the keyboard hasn't been initialized yet */
 	spin_unlock_irqrestore(&kbd_event_lock, flags);
diff --git a/include/linux/kbd_kern.h b/include/linux/kbd_kern.h
index af9137d..b7c8cdc 100644
--- a/include/linux/kbd_kern.h
+++ b/include/linux/kbd_kern.h
@@ -65,7 +65,6 @@ struct kbd_struct {
 
 extern int kbd_init(void);
 
-extern unsigned char getledstate(void);
 extern void setledstate(struct kbd_struct *kbd, unsigned int led);
 
 extern int do_poke_blanked_console;


^ permalink raw reply related

* [PATCH 09/12] tty: tidy up the RESET_TERMIOS case
From: Alan Cox @ 2012-07-14 14:34 UTC (permalink / raw)
  To: greg, linux-serial
In-Reply-To: <20120714142740.26945.98609.stgit@localhost.localdomain>

From: Alan Cox <alan@linux.intel.com>

If we are going to reset the termios then we don't need the driver side
buffers at all as we now have the tty allocation.

Signed-off-by: Alan Cox <alan@linux.intel.com>
---

 drivers/tty/tty_io.c |   45 +++++++++++++++++++++++----------------------
 1 file changed, 23 insertions(+), 22 deletions(-)

diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c
index cfd12da..d6feca1 100644
--- a/drivers/tty/tty_io.c
+++ b/drivers/tty/tty_io.c
@@ -1249,16 +1249,20 @@ int tty_init_termios(struct tty_struct *tty)
 	struct ktermios *tp;
 	int idx = tty->index;
 
-	tp = tty->driver->termios[idx];
-	if (tp == NULL) {
-		tp = kmalloc(sizeof(struct ktermios), GFP_KERNEL);
-		if (tp == NULL)
-			return -ENOMEM;
-		*tp = tty->driver->init_termios;
-		tty->driver->termios[idx] = tp;
-	}
-	tty->termios = *tp;
-
+	if (tty->driver->flags & TTY_DRIVER_RESET_TERMIOS)
+		tty->termios = tty->driver->init_termios;
+	else {
+		tp = tty->driver->termios[idx];
+		if (tp == NULL) {
+			tp = kzalloc(sizeof(struct ktermios[2]), GFP_KERNEL);
+			if (tp == NULL)
+				return -ENOMEM;
+			tp[0] = tty->driver->init_termios;
+			tty->driver->termios[idx] = tp;
+		}
+		tty->termios = tp[0];
+		tty->termios_locked = tp[1];
+        }
 	/* Compatibility until drivers always set this */
 	tty->termios.c_ispeed = tty_termios_input_baud_rate(&tty->termios);
 	tty->termios.c_ospeed = tty_termios_baud_rate(&tty->termios);
@@ -1435,16 +1439,9 @@ err_release_tty:
 
 void tty_free_termios(struct tty_struct *tty)
 {
-	struct ktermios *tp;
 	int idx = tty->index;
 	/* Kill this flag and push into drivers for locking etc */
-	if (tty->driver->flags & TTY_DRIVER_RESET_TERMIOS) {
-		/* FIXME: Locking on ->termios array */
-		tp = tty->driver->termios[idx];
-		tty->driver->termios[idx] = NULL;
-		kfree(tp);
-	}
-	else
+	if (!(tty->driver->flags & TTY_DRIVER_RESET_TERMIOS))
 		*tty->driver->termios[idx] = tty->termios;
 }
 EXPORT_SYMBOL(tty_free_termios);
@@ -3072,16 +3069,19 @@ static void destruct_tty_driver(struct kref *kref)
 		 * drivers are removed from the kernel.
 		 */
 		for (i = 0; i < driver->num; i++) {
-			tp = driver->termios[i];
-			if (tp) {
-				driver->termios[i] = NULL;
-				kfree(tp);
+			if (!(driver->flags & TTY_DRIVER_RESET_TERMIOS)) {
+				tp = driver->termios[i];
+				if (tp) {
+					driver->termios[i] = NULL;
+					kfree(tp);
+				}
 			}
 			if (!(driver->flags & TTY_DRIVER_DYNAMIC_DEV))
 				tty_unregister_device(driver, i);
 		}
 		p = driver->ttys;
 		proc_tty_unregister_driver(driver);
+		/* FIXME: who frees driver->termios itself */
 		driver->ttys = NULL;
 		driver->termios = NULL;
 		kfree(p);
@@ -3121,6 +3121,7 @@ int tty_register_driver(struct tty_driver *driver)
 	void **p = NULL;
 	struct device *d;
 
+	/* FIXME: at this point we are overallocating for the RESET_TERMIOS case */
 	if (!(driver->flags & TTY_DRIVER_DEVPTS_MEM) && driver->num) {
 		p = kzalloc(driver->num * 2 * sizeof(void *), GFP_KERNEL);
 		if (!p)


^ permalink raw reply related

* [PATCH 08/12] 8250: add support for ASIX devices with a FIFO bug
From: Alan Cox @ 2012-07-14 14:33 UTC (permalink / raw)
  To: greg, linux-serial
In-Reply-To: <20120714142740.26945.98609.stgit@localhost.localdomain>

From: Alan Cox <alan@linux.intel.com>

Information and a different patch provided by <donald@asix.com.tw>. We do
it a little differently to keep the modularity and to avoid playing with
RLSI.

We add a new uart bug for the parity flaw and set it in the pci matches.
If parity check is enabled then we drop the FIFO trigger to 1 as per the
Asix reference code.

Signed-off-by: Alan Cox <alan@linux.intel.com>
---

 drivers/tty/serial/8250/8250.c     |    8 ++++++--
 drivers/tty/serial/8250/8250.h     |    1 +
 drivers/tty/serial/8250/8250_pci.c |   24 +++++++++++++++++++++---
 3 files changed, 28 insertions(+), 5 deletions(-)

diff --git a/drivers/tty/serial/8250/8250.c b/drivers/tty/serial/8250/8250.c
index ca42d16..44f52c6 100644
--- a/drivers/tty/serial/8250/8250.c
+++ b/drivers/tty/serial/8250/8250.c
@@ -2202,6 +2202,7 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios,
 	unsigned char cval, fcr = 0;
 	unsigned long flags;
 	unsigned int baud, quot;
+	int fifo_bug = 0;
 
 	switch (termios->c_cflag & CSIZE) {
 	case CS5:
@@ -2221,8 +2222,11 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios,
 
 	if (termios->c_cflag & CSTOPB)
 		cval |= UART_LCR_STOP;
-	if (termios->c_cflag & PARENB)
+	if (termios->c_cflag & PARENB) {
 		cval |= UART_LCR_PARITY;
+		if (up->bugs & UART_BUG_PARITY)
+			fifo_bug = 1;
+	}
 	if (!(termios->c_cflag & PARODD))
 		cval |= UART_LCR_EPAR;
 #ifdef CMSPAR
@@ -2246,7 +2250,7 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios,
 
 	if (up->capabilities & UART_CAP_FIFO && port->fifosize > 1) {
 		fcr = uart_config[port->type].fcr;
-		if (baud < 2400) {
+		if (baud < 2400 || fifo_bug) {
 			fcr &= ~UART_FCR_TRIGGER_MASK;
 			fcr |= UART_FCR_TRIGGER_1;
 		}
diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h
index 4c87b83..972b521 100644
--- a/drivers/tty/serial/8250/8250.h
+++ b/drivers/tty/serial/8250/8250.h
@@ -48,6 +48,7 @@ struct serial8250_config {
 #define UART_BUG_TXEN	(1 << 1)	/* UART has buggy TX IIR status */
 #define UART_BUG_NOMSR	(1 << 2)	/* UART has buggy MSR status bits (Au1x00) */
 #define UART_BUG_THRE	(1 << 3)	/* UART has buggy THRE reassertion */
+#define UART_BUG_PARITY	(1 << 4)	/* UART mishandles parity if FIFO enabled */
 
 #define PROBE_RSA	(1 << 0)
 #define PROBE_ANY	(~0)
diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c
index 2ef9a07..62e10fe 100644
--- a/drivers/tty/serial/8250/8250_pci.c
+++ b/drivers/tty/serial/8250/8250_pci.c
@@ -1032,8 +1032,15 @@ static int pci_oxsemi_tornado_init(struct pci_dev *dev)
 	return number_uarts;
 }
 
-static int
-pci_default_setup(struct serial_private *priv,
+static int pci_asix_setup(struct serial_private *priv,
+		  const struct pciserial_board *board,
+		  struct uart_8250_port *port, int idx)
+{
+	port->bugs |= UART_BUG_PARITY;
+	return pci_default_setup(priv, board, port, idx);
+}
+
+static int pci_default_setup(struct serial_private *priv,
 		  const struct pciserial_board *board,
 		  struct uart_8250_port *port, int idx)
 {
@@ -1187,6 +1194,7 @@ pci_xr17c154_setup(struct serial_private *priv,
 #define PCIE_DEVICE_ID_NEO_2_OX_IBM	0x00F6
 #define PCI_DEVICE_ID_PLX_CRONYX_OMEGA	0xc001
 #define PCI_DEVICE_ID_INTEL_PATSBURG_KT 0x1d3d
+#define PCI_VENDOR_ID_ASIX		0x9710
 
 /* Unknown vendors/cards - this should not be in linux/pci_ids.h */
 #define PCI_SUBDEVICE_ID_UNKNOWN_0x1584	0x1584
@@ -1726,7 +1734,17 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = {
 		.subvendor	= PCI_ANY_ID,
 		.subdevice	= PCI_ANY_ID,
 		.setup		= pci_omegapci_setup,
-	 },
+	},
+	/*
+	 * ASIX devices with FIFO bug
+	 */
+	{
+		.vendor		= PCI_VENDOR_ID_ASIX,
+		.device		= PCI_ANY_ID,
+		.subvendor	= PCI_ANY_ID,
+		.subdevice	= PCI_ANY_ID,
+		.setup		= pci_asix_setup,
+	},
 	/*
 	 * Default "match everything" terminator entry
 	 */


^ permalink raw reply related

* [PATCH 07/12] 8250: propogate the bugs field
From: Alan Cox @ 2012-07-14 14:33 UTC (permalink / raw)
  To: greg, linux-serial
In-Reply-To: <20120714142740.26945.98609.stgit@localhost.localdomain>

From: Alan Cox <alan@linux.intel.com>

Now we are using the uart_8250_port this is trivial

Signed-off-by: Alan Cox <alan@linux.intel.com>
---

 drivers/tty/serial/8250/8250.c |    1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/tty/serial/8250/8250.c b/drivers/tty/serial/8250/8250.c
index 2b24685..ca42d16 100644
--- a/drivers/tty/serial/8250/8250.c
+++ b/drivers/tty/serial/8250/8250.c
@@ -3155,6 +3155,7 @@ int serial8250_register_8250_port(struct uart_8250_port *up)
 		uart->port.regshift     = up->port.regshift;
 		uart->port.iotype       = up->port.iotype;
 		uart->port.flags        = up->port.flags | UPF_BOOT_AUTOCONF;
+		uart->bugs		= up->bugs;
 		uart->port.mapbase      = up->port.mapbase;
 		uart->port.private_data = up->port.private_data;
 		if (up->port.dev)


^ permalink raw reply related

* [PATCH 06/12] 8250: use the 8250 register interface not the legacy one
From: Alan Cox @ 2012-07-14 14:33 UTC (permalink / raw)
  To: greg, linux-serial
In-Reply-To: <20120714142740.26945.98609.stgit@localhost.localdomain>

From: Alan Cox <alan@linux.intel.com>

The old interface just copies bits over and calls the newer one.
In addition we can now pass more information.

Updated to fix the cases reported by Stephen Rothwell and those found
by Fegguang's nifty new 0 day build tester. In particular it now builds
correctly for openfirmware and for OLPC.

Signed-off-by: Alan Cox <alan@linux.intel.com>
---

 arch/mips/cavium-octeon/serial.c     |   30 ++++++-----
 drivers/char/mwave/mwavedd.c         |   16 +++---
 drivers/misc/ibmasm/uart.c           |   16 +++---
 drivers/net/ethernet/sgi/ioc3-eth.c  |   22 ++++----
 drivers/tty/serial/8250/8250.c       |   71 +++++++++-----------------
 drivers/tty/serial/8250/8250.h       |   30 -----------
 drivers/tty/serial/8250/8250_acorn.c |   22 ++++----
 drivers/tty/serial/8250/8250_dw.c    |   38 +++++++-------
 drivers/tty/serial/8250/8250_gsc.c   |   26 +++++-----
 drivers/tty/serial/8250/8250_hp300.c |   26 +++++-----
 drivers/tty/serial/8250/8250_pci.c   |   92 +++++++++++++++++-----------------
 drivers/tty/serial/8250/8250_pnp.c   |   28 +++++-----
 drivers/tty/serial/8250/serial_cs.c  |   30 ++++++-----
 drivers/tty/serial/of_serial.c       |    9 +++
 include/linux/serial_8250.h          |   33 +++++++++++-
 15 files changed, 236 insertions(+), 253 deletions(-)

diff --git a/arch/mips/cavium-octeon/serial.c b/arch/mips/cavium-octeon/serial.c
index 138b221..6527864 100644
--- a/arch/mips/cavium-octeon/serial.c
+++ b/arch/mips/cavium-octeon/serial.c
@@ -47,40 +47,40 @@ static int __devinit octeon_serial_probe(struct platform_device *pdev)
 {
 	int irq, res;
 	struct resource *res_mem;
-	struct uart_port port;
+	struct uart_8250_port uart;
 
 	/* All adaptors have an irq.  */
 	irq = platform_get_irq(pdev, 0);
 	if (irq < 0)
 		return irq;
 
-	memset(&port, 0, sizeof(port));
+	memset(&uart, 0, sizeof(uart));
 
-	port.flags = ASYNC_SKIP_TEST | UPF_SHARE_IRQ | UPF_FIXED_TYPE;
-	port.type = PORT_OCTEON;
-	port.iotype = UPIO_MEM;
-	port.regshift = 3;
-	port.dev = &pdev->dev;
+	uart.port.flags = ASYNC_SKIP_TEST | UPF_SHARE_IRQ | UPF_FIXED_TYPE;
+	uart.port.type = PORT_OCTEON;
+	uart.port.iotype = UPIO_MEM;
+	uart.port.regshift = 3;
+	uart.port.dev = &pdev->dev;
 
 	if (octeon_is_simulation())
 		/* Make simulator output fast*/
-		port.uartclk = 115200 * 16;
+		uart.port.uartclk = 115200 * 16;
 	else
-		port.uartclk = octeon_get_io_clock_rate();
+		uart.port.uartclk = octeon_get_io_clock_rate();
 
-	port.serial_in = octeon_serial_in;
-	port.serial_out = octeon_serial_out;
-	port.irq = irq;
+	uart.port.serial_in = octeon_serial_in;
+	uart.port.serial_out = octeon_serial_out;
+	uart.port.irq = irq;
 
 	res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	if (res_mem == NULL) {
 		dev_err(&pdev->dev, "found no memory resource\n");
 		return -ENXIO;
 	}
-	port.mapbase = res_mem->start;
-	port.membase = ioremap(res_mem->start, resource_size(res_mem));
+	uart.port.mapbase = res_mem->start;
+	uart.port.membase = ioremap(res_mem->start, resource_size(res_mem));
 
-	res = serial8250_register_port(&port);
+	res = serial8250_register_8250_port(&uart);
 
 	return res >= 0 ? 0 : res;
 }
diff --git a/drivers/char/mwave/mwavedd.c b/drivers/char/mwave/mwavedd.c
index 1d82d58..164544a 100644
--- a/drivers/char/mwave/mwavedd.c
+++ b/drivers/char/mwave/mwavedd.c
@@ -430,7 +430,7 @@ static ssize_t mwave_write(struct file *file, const char __user *buf,
 
 static int register_serial_portandirq(unsigned int port, int irq)
 {
-	struct uart_port uart;
+	struct uart_8250_port uart;
 	
 	switch ( port ) {
 		case 0x3f8:
@@ -462,14 +462,14 @@ static int register_serial_portandirq(unsigned int port, int irq)
 	} /* switch */
 	/* irq is okay */
 
-	memset(&uart, 0, sizeof(struct uart_port));
+	memset(&uart, 0, sizeof(uart));
 	
-	uart.uartclk =  1843200;
-	uart.iobase = port;
-	uart.irq = irq;
-	uart.iotype = UPIO_PORT;
-	uart.flags =  UPF_SHARE_IRQ;
-	return serial8250_register_port(&uart);
+	uart.port.uartclk =  1843200;
+	uart.port.iobase = port;
+	uart.port.irq = irq;
+	uart.port.iotype = UPIO_PORT;
+	uart.port.flags =  UPF_SHARE_IRQ;
+	return serial8250_register_8250_port(&uart);
 }
 
 
diff --git a/drivers/misc/ibmasm/uart.c b/drivers/misc/ibmasm/uart.c
index 1dcb9ae..01e2b0d 100644
--- a/drivers/misc/ibmasm/uart.c
+++ b/drivers/misc/ibmasm/uart.c
@@ -33,7 +33,7 @@
 
 void ibmasm_register_uart(struct service_processor *sp)
 {
-	struct uart_port uport;
+	struct uart_8250_port uart;
 	void __iomem *iomem_base;
 
 	iomem_base = sp->base_address + SCOUT_COM_B_BASE;
@@ -47,14 +47,14 @@ void ibmasm_register_uart(struct service_processor *sp)
 		return;
 	}
 
-	memset(&uport, 0, sizeof(struct uart_port));
-	uport.irq	= sp->irq;
-	uport.uartclk	= 3686400;
-	uport.flags	= UPF_SHARE_IRQ;
-	uport.iotype	= UPIO_MEM;
-	uport.membase	= iomem_base;
+	memset(&uart, 0, sizeof(uart));
+	uart.port.irq		= sp->irq;
+	uart.port.uartclk	= 3686400;
+	uart.port.flags		= UPF_SHARE_IRQ;
+	uart.port.iotype	= UPIO_MEM;
+	uart.port.membase	= iomem_base;
 
-	sp->serial_line = serial8250_register_port(&uport);
+	sp->serial_line = serial8250_register_8250_port(&uart);
 	if (sp->serial_line < 0) {
 		dev_err(sp->dev, "Failed to register serial port\n");
 		return;
diff --git a/drivers/net/ethernet/sgi/ioc3-eth.c b/drivers/net/ethernet/sgi/ioc3-eth.c
index b5ba308..3e5519a 100644
--- a/drivers/net/ethernet/sgi/ioc3-eth.c
+++ b/drivers/net/ethernet/sgi/ioc3-eth.c
@@ -1147,15 +1147,17 @@ static void __devinit ioc3_8250_register(struct ioc3_uartregs __iomem *uart)
 {
 #define COSMISC_CONSTANT 6
 
-	struct uart_port port = {
-		.irq		= 0,
-		.flags		= UPF_SKIP_TEST | UPF_BOOT_AUTOCONF,
-		.iotype		= UPIO_MEM,
-		.regshift	= 0,
-		.uartclk	= (22000000 << 1) / COSMISC_CONSTANT,
-
-		.membase	= (unsigned char __iomem *) uart,
-		.mapbase	= (unsigned long) uart,
+	struct uart_8250_port port = {
+	        .port = {
+			.irq		= 0,
+			.flags		= UPF_SKIP_TEST | UPF_BOOT_AUTOCONF,
+			.iotype		= UPIO_MEM,
+			.regshift	= 0,
+			.uartclk	= (22000000 << 1) / COSMISC_CONSTANT,
+
+			.membase	= (unsigned char __iomem *) uart,
+			.mapbase	= (unsigned long) uart,
+                }
 	};
 	unsigned char lcr;
 
@@ -1164,7 +1166,7 @@ static void __devinit ioc3_8250_register(struct ioc3_uartregs __iomem *uart)
 	uart->iu_scr = COSMISC_CONSTANT,
 	uart->iu_lcr = lcr;
 	uart->iu_lcr;
-	serial8250_register_port(&port);
+	serial8250_register_8250_port(&port);
 }
 
 static void __devinit ioc3_serial_probe(struct pci_dev *pdev, struct ioc3 *ioc3)
diff --git a/drivers/tty/serial/8250/8250.c b/drivers/tty/serial/8250/8250.c
index 8123f78..2b24685 100644
--- a/drivers/tty/serial/8250/8250.c
+++ b/drivers/tty/serial/8250/8250.c
@@ -2979,36 +2979,36 @@ void serial8250_resume_port(int line)
 static int __devinit serial8250_probe(struct platform_device *dev)
 {
 	struct plat_serial8250_port *p = dev->dev.platform_data;
-	struct uart_port port;
+	struct uart_8250_port uart;
 	int ret, i, irqflag = 0;
 
-	memset(&port, 0, sizeof(struct uart_port));
+	memset(&uart, 0, sizeof(uart));
 
 	if (share_irqs)
 		irqflag = IRQF_SHARED;
 
 	for (i = 0; p && p->flags != 0; p++, i++) {
-		port.iobase		= p->iobase;
-		port.membase		= p->membase;
-		port.irq		= p->irq;
-		port.irqflags		= p->irqflags;
-		port.uartclk		= p->uartclk;
-		port.regshift		= p->regshift;
-		port.iotype		= p->iotype;
-		port.flags		= p->flags;
-		port.mapbase		= p->mapbase;
-		port.hub6		= p->hub6;
-		port.private_data	= p->private_data;
-		port.type		= p->type;
-		port.serial_in		= p->serial_in;
-		port.serial_out		= p->serial_out;
-		port.handle_irq		= p->handle_irq;
-		port.handle_break	= p->handle_break;
-		port.set_termios	= p->set_termios;
-		port.pm			= p->pm;
-		port.dev		= &dev->dev;
-		port.irqflags		|= irqflag;
-		ret = serial8250_register_port(&port);
+		uart.port.iobase	= p->iobase;
+		uart.port.membase	= p->membase;
+		uart.port.irq		= p->irq;
+		uart.port.irqflags	= p->irqflags;
+		uart.port.uartclk	= p->uartclk;
+		uart.port.regshift	= p->regshift;
+		uart.port.iotype	= p->iotype;
+		uart.port.flags		= p->flags;
+		uart.port.mapbase	= p->mapbase;
+		uart.port.hub6		= p->hub6;
+		uart.port.private_data	= p->private_data;
+		uart.port.type		= p->type;
+		uart.port.serial_in	= p->serial_in;
+		uart.port.serial_out	= p->serial_out;
+		uart.port.handle_irq	= p->handle_irq;
+		uart.port.handle_break	= p->handle_break;
+		uart.port.set_termios	= p->set_termios;
+		uart.port.pm		= p->pm;
+		uart.port.dev		= &dev->dev;
+		uart.port.irqflags	|= irqflag;
+		ret = serial8250_register_8250_port(&uart);
 		if (ret < 0) {
 			dev_err(&dev->dev, "unable to register port at index %d "
 				"(IO%lx MEM%llx IRQ%d): %d\n", i,
@@ -3081,7 +3081,7 @@ static struct platform_driver serial8250_isa_driver = {
 static struct platform_device *serial8250_isa_devs;
 
 /*
- * serial8250_register_port and serial8250_unregister_port allows for
+ * serial8250_register_8250_port and serial8250_unregister_port allows for
  * 16x50 serial ports to be configured at run-time, to support PCMCIA
  * modems and PCI multiport cards.
  */
@@ -3198,29 +3198,6 @@ int serial8250_register_8250_port(struct uart_8250_port *up)
 EXPORT_SYMBOL(serial8250_register_8250_port);
 
 /**
- *	serial8250_register_port - register a serial port
- *	@port: serial port template
- *
- *	Configure the serial port specified by the request. If the
- *	port exists and is in use, it is hung up and unregistered
- *	first.
- *
- *	The port is then probed and if necessary the IRQ is autodetected
- *	If this fails an error is returned.
- *
- *	On success the port is ready to use and the line number is returned.
- */
-int serial8250_register_port(struct uart_port *port)
-{
-	struct uart_8250_port up;
-
-	memset(&up, 0, sizeof(up));
-	memcpy(&up.port, port, sizeof(*port));
-	return serial8250_register_8250_port(&up);
-}
-EXPORT_SYMBOL(serial8250_register_port);
-
-/**
  *	serial8250_unregister_port - remove a 16x50 serial port at runtime
  *	@line: serial line number
  *
diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h
index f9719d1..4c87b83 100644
--- a/drivers/tty/serial/8250/8250.h
+++ b/drivers/tty/serial/8250/8250.h
@@ -13,36 +13,6 @@
 
 #include <linux/serial_8250.h>
 
-struct uart_8250_port {
-	struct uart_port	port;
-	struct timer_list	timer;		/* "no irq" timer */
-	struct list_head	list;		/* ports on this IRQ */
-	unsigned short		capabilities;	/* port capabilities */
-	unsigned short		bugs;		/* port bugs */
-	unsigned int		tx_loadsz;	/* transmit fifo load size */
-	unsigned char		acr;
-	unsigned char		ier;
-	unsigned char		lcr;
-	unsigned char		mcr;
-	unsigned char		mcr_mask;	/* mask of user bits */
-	unsigned char		mcr_force;	/* mask of forced bits */
-	unsigned char		cur_iotype;	/* Running I/O type */
-
-	/*
-	 * Some bits in registers are cleared on a read, so they must
-	 * be saved whenever the register is read but the bits will not
-	 * be immediately processed.
-	 */
-#define LSR_SAVE_FLAGS UART_LSR_BRK_ERROR_BITS
-	unsigned char		lsr_saved_flags;
-#define MSR_SAVE_FLAGS UART_MSR_ANY_DELTA
-	unsigned char		msr_saved_flags;
-
-	/* 8250 specific callbacks */
-	int			(*dl_read)(struct uart_8250_port *);
-	void			(*dl_write)(struct uart_8250_port *, int);
-};
-
 struct old_serial_port {
 	unsigned int uart;
 	unsigned int baud_base;
diff --git a/drivers/tty/serial/8250/8250_acorn.c b/drivers/tty/serial/8250/8250_acorn.c
index b0ce8c5..8574983 100644
--- a/drivers/tty/serial/8250/8250_acorn.c
+++ b/drivers/tty/serial/8250/8250_acorn.c
@@ -43,7 +43,7 @@ serial_card_probe(struct expansion_card *ec, const struct ecard_id *id)
 {
 	struct serial_card_info *info;
 	struct serial_card_type *type = id->data;
-	struct uart_port port;
+	struct uart_8250_port uart;
 	unsigned long bus_addr;
 	unsigned int i;
 
@@ -62,19 +62,19 @@ serial_card_probe(struct expansion_card *ec, const struct ecard_id *id)
 
 	ecard_set_drvdata(ec, info);
 
-	memset(&port, 0, sizeof(struct uart_port));
-	port.irq	= ec->irq;
-	port.flags	= UPF_BOOT_AUTOCONF | UPF_SHARE_IRQ;
-	port.uartclk	= type->uartclk;
-	port.iotype	= UPIO_MEM;
-	port.regshift	= 2;
-	port.dev	= &ec->dev;
+	memset(&uart, 0, sizeof(struct uart_8250_port));
+	uart.port.irq	= ec->irq;
+	uart.port.flags	= UPF_BOOT_AUTOCONF | UPF_SHARE_IRQ;
+	uart.port.uartclk	= type->uartclk;
+	uart.port.iotype	= UPIO_MEM;
+	uart.port.regshift	= 2;
+	uart.port.dev	= &ec->dev;
 
 	for (i = 0; i < info->num_ports; i ++) {
-		port.membase = info->vaddr + type->offset[i];
-		port.mapbase = bus_addr + type->offset[i];
+		uart.port.membase = info->vaddr + type->offset[i];
+		uart.port.mapbase = bus_addr + type->offset[i];
 
-		info->ports[i] = serial8250_register_port(&port);
+		info->ports[i] = serial8250_register_8250_port(&uart);
 	}
 
 	return 0;
diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c
index f574eef..c3b2ec0 100644
--- a/drivers/tty/serial/8250/8250_dw.c
+++ b/drivers/tty/serial/8250/8250_dw.c
@@ -89,7 +89,7 @@ static int dw8250_handle_irq(struct uart_port *p)
 
 static int __devinit dw8250_probe(struct platform_device *pdev)
 {
-	struct uart_port port = {};
+	struct uart_8250_port uart = {};
 	struct resource *regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	struct resource *irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
 	struct device_node *np = pdev->dev.of_node;
@@ -104,28 +104,28 @@ static int __devinit dw8250_probe(struct platform_device *pdev)
 	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
 	if (!data)
 		return -ENOMEM;
-	port.private_data = data;
-
-	spin_lock_init(&port.lock);
-	port.mapbase = regs->start;
-	port.irq = irq->start;
-	port.handle_irq = dw8250_handle_irq;
-	port.type = PORT_8250;
-	port.flags = UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF | UPF_IOREMAP |
+	uart.port.private_data = data;
+
+	spin_lock_init(&uart.port.lock);
+	uart.port.mapbase = regs->start;
+	uart.port.irq = irq->start;
+	uart.port.handle_irq = dw8250_handle_irq;
+	uart.port.type = PORT_8250;
+	uart.port.flags = UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF | UPF_IOREMAP |
 		UPF_FIXED_PORT | UPF_FIXED_TYPE;
-	port.dev = &pdev->dev;
+	uart.port.dev = &pdev->dev;
 
-	port.iotype = UPIO_MEM;
-	port.serial_in = dw8250_serial_in;
-	port.serial_out = dw8250_serial_out;
+	uart.port.iotype = UPIO_MEM;
+	uart.port.serial_in = dw8250_serial_in;
+	uart.port.serial_out = dw8250_serial_out;
 	if (!of_property_read_u32(np, "reg-io-width", &val)) {
 		switch (val) {
 		case 1:
 			break;
 		case 4:
-			port.iotype = UPIO_MEM32;
-			port.serial_in = dw8250_serial_in32;
-			port.serial_out = dw8250_serial_out32;
+			uart.port.iotype = UPIO_MEM32;
+			uart.port.serial_in = dw8250_serial_in32;
+			uart.port.serial_out = dw8250_serial_out32;
 			break;
 		default:
 			dev_err(&pdev->dev, "unsupported reg-io-width (%u)\n",
@@ -135,15 +135,15 @@ static int __devinit dw8250_probe(struct platform_device *pdev)
 	}
 
 	if (!of_property_read_u32(np, "reg-shift", &val))
-		port.regshift = val;
+		uart.port.regshift = val;
 
 	if (of_property_read_u32(np, "clock-frequency", &val)) {
 		dev_err(&pdev->dev, "no clock-frequency property set\n");
 		return -EINVAL;
 	}
-	port.uartclk = val;
+	uart.port.uartclk = val;
 
-	data->line = serial8250_register_port(&port);
+	data->line = serial8250_register_8250_port(&uart);
 	if (data->line < 0)
 		return data->line;
 
diff --git a/drivers/tty/serial/8250/8250_gsc.c b/drivers/tty/serial/8250/8250_gsc.c
index d8c0ffb..097dff9 100644
--- a/drivers/tty/serial/8250/8250_gsc.c
+++ b/drivers/tty/serial/8250/8250_gsc.c
@@ -26,7 +26,7 @@
 
 static int __init serial_init_chip(struct parisc_device *dev)
 {
-	struct uart_port port;
+	struct uart_8250_port uart;
 	unsigned long address;
 	int err;
 
@@ -48,21 +48,21 @@ static int __init serial_init_chip(struct parisc_device *dev)
 	if (dev->id.sversion != 0x8d)
 		address += 0x800;
 
-	memset(&port, 0, sizeof(port));
-	port.iotype	= UPIO_MEM;
+	memset(&uart, 0, sizeof(uart));
+	uart.port.iotype	= UPIO_MEM;
 	/* 7.272727MHz on Lasi.  Assumed the same for Dino, Wax and Timi. */
-	port.uartclk	= 7272727;
-	port.mapbase	= address;
-	port.membase	= ioremap_nocache(address, 16);
-	port.irq	= dev->irq;
-	port.flags	= UPF_BOOT_AUTOCONF;
-	port.dev	= &dev->dev;
-
-	err = serial8250_register_port(&port);
+	uart.port.uartclk	= 7272727;
+	uart.port.mapbase	= address;
+	uart.port.membase	= ioremap_nocache(address, 16);
+	uart.port.irq	= dev->irq;
+	uart.port.flags	= UPF_BOOT_AUTOCONF;
+	uart.port.dev	= &dev->dev;
+
+	err = serial8250_register_8250_port(&uart);
 	if (err < 0) {
 		printk(KERN_WARNING
-			"serial8250_register_port returned error %d\n", err);
-		iounmap(port.membase);
+			"serial8250_register_8250_port returned error %d\n", err);
+		iounmap(uart.port.membase);
 		return err;
 	}
 
diff --git a/drivers/tty/serial/8250/8250_hp300.c b/drivers/tty/serial/8250/8250_hp300.c
index c13438c..8f1dd2c 100644
--- a/drivers/tty/serial/8250/8250_hp300.c
+++ b/drivers/tty/serial/8250/8250_hp300.c
@@ -171,7 +171,7 @@ static int __devinit hpdca_init_one(struct dio_dev *d,
 		return 0;
 	}
 #endif
-	memset(&port, 0, sizeof(struct uart_port));
+	memset(&uart, 0, sizeof(uart));
 
 	/* Memory mapped I/O */
 	port.iotype = UPIO_MEM;
@@ -182,7 +182,7 @@ static int __devinit hpdca_init_one(struct dio_dev *d,
 	port.membase = (char *)(port.mapbase + DIO_VIRADDRBASE);
 	port.regshift = 1;
 	port.dev = &d->dev;
-	line = serial8250_register_port(&port);
+	line = serial8250_register_8250_port(&uart);
 
 	if (line < 0) {
 		printk(KERN_NOTICE "8250_hp300: register_serial() DCA scode %d"
@@ -210,7 +210,7 @@ static int __init hp300_8250_init(void)
 #ifdef CONFIG_HPAPCI
 	int line;
 	unsigned long base;
-	struct uart_port uport;
+	struct uart_8250_port uart;
 	struct hp300_port *port;
 	int i;
 #endif
@@ -248,26 +248,26 @@ static int __init hp300_8250_init(void)
 		if (!port)
 			return -ENOMEM;
 
-		memset(&uport, 0, sizeof(struct uart_port));
+		memset(&uart, 0, sizeof(uart));
 
 		base = (FRODO_BASE + FRODO_APCI_OFFSET(i));
 
 		/* Memory mapped I/O */
-		uport.iotype = UPIO_MEM;
-		uport.flags = UPF_SKIP_TEST | UPF_SHARE_IRQ \
+		uart.port.iotype = UPIO_MEM;
+		uart.port.flags = UPF_SKIP_TEST | UPF_SHARE_IRQ \
 			      | UPF_BOOT_AUTOCONF;
 		/* XXX - no interrupt support yet */
-		uport.irq = 0;
-		uport.uartclk = HPAPCI_BAUD_BASE * 16;
-		uport.mapbase = base;
-		uport.membase = (char *)(base + DIO_VIRADDRBASE);
-		uport.regshift = 2;
+		uart.port.irq = 0;
+		uart.port.uartclk = HPAPCI_BAUD_BASE * 16;
+		uart.port.mapbase = base;
+		uart.port.membase = (char *)(base + DIO_VIRADDRBASE);
+		uart.port.regshift = 2;
 
-		line = serial8250_register_port(&uport);
+		line = serial8250_register_8250_port(&uart);
 
 		if (line < 0) {
 			printk(KERN_NOTICE "8250_hp300: register_serial() APCI"
-			       " %d irq %d failed\n", i, uport.irq);
+			       " %d irq %d failed\n", i, uart.port.irq);
 			kfree(port);
 			continue;
 		}
diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c
index 66e5909..2ef9a07 100644
--- a/drivers/tty/serial/8250/8250_pci.c
+++ b/drivers/tty/serial/8250/8250_pci.c
@@ -44,7 +44,7 @@ struct pci_serial_quirk {
 	int	(*init)(struct pci_dev *dev);
 	int	(*setup)(struct serial_private *,
 			 const struct pciserial_board *,
-			 struct uart_port *, int);
+			 struct uart_8250_port *, int);
 	void	(*exit)(struct pci_dev *dev);
 };
 
@@ -59,7 +59,7 @@ struct serial_private {
 };
 
 static int pci_default_setup(struct serial_private*,
-	  const struct pciserial_board*, struct uart_port*, int);
+	  const struct pciserial_board*, struct uart_8250_port *, int);
 
 static void moan_device(const char *str, struct pci_dev *dev)
 {
@@ -74,7 +74,7 @@ static void moan_device(const char *str, struct pci_dev *dev)
 }
 
 static int
-setup_port(struct serial_private *priv, struct uart_port *port,
+setup_port(struct serial_private *priv, struct uart_8250_port *port,
 	   int bar, int offset, int regshift)
 {
 	struct pci_dev *dev = priv->dev;
@@ -93,17 +93,17 @@ setup_port(struct serial_private *priv, struct uart_port *port,
 		if (!priv->remapped_bar[bar])
 			return -ENOMEM;
 
-		port->iotype = UPIO_MEM;
-		port->iobase = 0;
-		port->mapbase = base + offset;
-		port->membase = priv->remapped_bar[bar] + offset;
-		port->regshift = regshift;
+		port->port.iotype = UPIO_MEM;
+		port->port.iobase = 0;
+		port->port.mapbase = base + offset;
+		port->port.membase = priv->remapped_bar[bar] + offset;
+		port->port.regshift = regshift;
 	} else {
-		port->iotype = UPIO_PORT;
-		port->iobase = base + offset;
-		port->mapbase = 0;
-		port->membase = NULL;
-		port->regshift = 0;
+		port->port.iotype = UPIO_PORT;
+		port->port.iobase = base + offset;
+		port->port.mapbase = 0;
+		port->port.membase = NULL;
+		port->port.regshift = 0;
 	}
 	return 0;
 }
@@ -113,7 +113,7 @@ setup_port(struct serial_private *priv, struct uart_port *port,
  */
 static int addidata_apci7800_setup(struct serial_private *priv,
 				const struct pciserial_board *board,
-				struct uart_port *port, int idx)
+				struct uart_8250_port *port, int idx)
 {
 	unsigned int bar = 0, offset = board->first_offset;
 	bar = FL_GET_BASE(board->flags);
@@ -140,7 +140,7 @@ static int addidata_apci7800_setup(struct serial_private *priv,
  */
 static int
 afavlab_setup(struct serial_private *priv, const struct pciserial_board *board,
-	      struct uart_port *port, int idx)
+	      struct uart_8250_port *port, int idx)
 {
 	unsigned int bar, offset = board->first_offset;
 
@@ -195,7 +195,7 @@ static int pci_hp_diva_init(struct pci_dev *dev)
 static int
 pci_hp_diva_setup(struct serial_private *priv,
 		const struct pciserial_board *board,
-		struct uart_port *port, int idx)
+		struct uart_8250_port *port, int idx)
 {
 	unsigned int offset = board->first_offset;
 	unsigned int bar = FL_GET_BASE(board->flags);
@@ -370,7 +370,7 @@ static void __devexit pci_ni8430_exit(struct pci_dev *dev)
 /* SBS Technologies Inc. PMC-OCTPRO and P-OCTAL cards */
 static int
 sbs_setup(struct serial_private *priv, const struct pciserial_board *board,
-		struct uart_port *port, int idx)
+		struct uart_8250_port *port, int idx)
 {
 	unsigned int bar, offset = board->first_offset;
 
@@ -525,7 +525,7 @@ static int pci_siig_init(struct pci_dev *dev)
 
 static int pci_siig_setup(struct serial_private *priv,
 			  const struct pciserial_board *board,
-			  struct uart_port *port, int idx)
+			  struct uart_8250_port *port, int idx)
 {
 	unsigned int bar = FL_GET_BASE(board->flags) + idx, offset = 0;
 
@@ -619,7 +619,7 @@ static int pci_timedia_init(struct pci_dev *dev)
 static int
 pci_timedia_setup(struct serial_private *priv,
 		  const struct pciserial_board *board,
-		  struct uart_port *port, int idx)
+		  struct uart_8250_port *port, int idx)
 {
 	unsigned int bar = 0, offset = board->first_offset;
 
@@ -653,7 +653,7 @@ pci_timedia_setup(struct serial_private *priv,
 static int
 titan_400l_800l_setup(struct serial_private *priv,
 		      const struct pciserial_board *board,
-		      struct uart_port *port, int idx)
+		      struct uart_8250_port *port, int idx)
 {
 	unsigned int bar, offset = board->first_offset;
 
@@ -754,7 +754,7 @@ static int pci_ni8430_init(struct pci_dev *dev)
 static int
 pci_ni8430_setup(struct serial_private *priv,
 		 const struct pciserial_board *board,
-		 struct uart_port *port, int idx)
+		 struct uart_8250_port *port, int idx)
 {
 	void __iomem *p;
 	unsigned long base, len;
@@ -781,7 +781,7 @@ pci_ni8430_setup(struct serial_private *priv,
 
 static int pci_netmos_9900_setup(struct serial_private *priv,
 				const struct pciserial_board *board,
-				struct uart_port *port, int idx)
+				struct uart_8250_port *port, int idx)
 {
 	unsigned int bar;
 
@@ -1035,7 +1035,7 @@ static int pci_oxsemi_tornado_init(struct pci_dev *dev)
 static int
 pci_default_setup(struct serial_private *priv,
 		  const struct pciserial_board *board,
-		  struct uart_port *port, int idx)
+		  struct uart_8250_port *port, int idx)
 {
 	unsigned int bar, offset = board->first_offset, maxnr;
 
@@ -1057,15 +1057,15 @@ pci_default_setup(struct serial_private *priv,
 static int
 ce4100_serial_setup(struct serial_private *priv,
 		  const struct pciserial_board *board,
-		  struct uart_port *port, int idx)
+		  struct uart_8250_port *port, int idx)
 {
 	int ret;
 
 	ret = setup_port(priv, port, 0, 0, board->reg_shift);
-	port->iotype = UPIO_MEM32;
-	port->type = PORT_XSCALE;
-	port->flags = (port->flags | UPF_FIXED_PORT | UPF_FIXED_TYPE);
-	port->regshift = 2;
+	port->port.iotype = UPIO_MEM32;
+	port->port.type = PORT_XSCALE;
+	port->port.flags = (port->port.flags | UPF_FIXED_PORT | UPF_FIXED_TYPE);
+	port->port.regshift = 2;
 
 	return ret;
 }
@@ -1073,16 +1073,16 @@ ce4100_serial_setup(struct serial_private *priv,
 static int
 pci_omegapci_setup(struct serial_private *priv,
 		      const struct pciserial_board *board,
-		      struct uart_port *port, int idx)
+		      struct uart_8250_port *port, int idx)
 {
 	return setup_port(priv, port, 2, idx * 8, 0);
 }
 
 static int skip_tx_en_setup(struct serial_private *priv,
 			const struct pciserial_board *board,
-			struct uart_port *port, int idx)
+			struct uart_8250_port *port, int idx)
 {
-	port->flags |= UPF_NO_TXEN_TEST;
+	port->port.flags |= UPF_NO_TXEN_TEST;
 	printk(KERN_DEBUG "serial8250: skipping TxEn test for device "
 			  "[%04x:%04x] subsystem [%04x:%04x]\n",
 			  priv->dev->vendor,
@@ -1131,11 +1131,11 @@ static unsigned int kt_serial_in(struct uart_port *p, int offset)
 
 static int kt_serial_setup(struct serial_private *priv,
 			   const struct pciserial_board *board,
-			   struct uart_port *port, int idx)
+			   struct uart_8250_port *port, int idx)
 {
-	port->flags |= UPF_BUG_THRE;
-	port->serial_in = kt_serial_in;
-	port->handle_break = kt_handle_break;
+	port->port.flags |= UPF_BUG_THRE;
+	port->port.serial_in = kt_serial_in;
+	port->port.handle_break = kt_handle_break;
 	return skip_tx_en_setup(priv, board, port, idx);
 }
 
@@ -1151,9 +1151,9 @@ static int pci_eg20t_init(struct pci_dev *dev)
 static int
 pci_xr17c154_setup(struct serial_private *priv,
 		  const struct pciserial_board *board,
-		  struct uart_port *port, int idx)
+		  struct uart_8250_port *port, int idx)
 {
-	port->flags |= UPF_EXAR_EFR;
+	port->port.flags |= UPF_EXAR_EFR;
 	return pci_default_setup(priv, board, port, idx);
 }
 
@@ -2720,7 +2720,7 @@ serial_pci_matches(const struct pciserial_board *board,
 struct serial_private *
 pciserial_init_ports(struct pci_dev *dev, const struct pciserial_board *board)
 {
-	struct uart_port serial_port;
+	struct uart_8250_port uart;
 	struct serial_private *priv;
 	struct pci_serial_quirk *quirk;
 	int rc, nr_ports, i;
@@ -2760,22 +2760,22 @@ pciserial_init_ports(struct pci_dev *dev, const struct pciserial_board *board)
 	priv->dev = dev;
 	priv->quirk = quirk;
 
-	memset(&serial_port, 0, sizeof(struct uart_port));
-	serial_port.flags = UPF_SKIP_TEST | UPF_BOOT_AUTOCONF | UPF_SHARE_IRQ;
-	serial_port.uartclk = board->base_baud * 16;
-	serial_port.irq = get_pci_irq(dev, board);
-	serial_port.dev = &dev->dev;
+	memset(&uart, 0, sizeof(uart));
+	uart.port.flags = UPF_SKIP_TEST | UPF_BOOT_AUTOCONF | UPF_SHARE_IRQ;
+	uart.port.uartclk = board->base_baud * 16;
+	uart.port.irq = get_pci_irq(dev, board);
+	uart.port.dev = &dev->dev;
 
 	for (i = 0; i < nr_ports; i++) {
-		if (quirk->setup(priv, board, &serial_port, i))
+		if (quirk->setup(priv, board, &uart, i))
 			break;
 
 #ifdef SERIAL_DEBUG_PCI
 		printk(KERN_DEBUG "Setup PCI port: port %lx, irq %d, type %d\n",
-		       serial_port.iobase, serial_port.irq, serial_port.iotype);
+		       uart.port.iobase, uart.port.irq, uart.port.iotype);
 #endif
 
-		priv->line[i] = serial8250_register_port(&serial_port);
+		priv->line[i] = serial8250_register_8250_port(&uart);
 		if (priv->line[i] < 0) {
 			printk(KERN_WARNING "Couldn't register serial port %s: %d\n", pci_name(dev), priv->line[i]);
 			break;
diff --git a/drivers/tty/serial/8250/8250_pnp.c b/drivers/tty/serial/8250/8250_pnp.c
index a2f2365..fde5aa6 100644
--- a/drivers/tty/serial/8250/8250_pnp.c
+++ b/drivers/tty/serial/8250/8250_pnp.c
@@ -424,7 +424,7 @@ static int __devinit serial_pnp_guess_board(struct pnp_dev *dev, int *flags)
 static int __devinit
 serial_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id)
 {
-	struct uart_port port;
+	struct uart_8250_port uart;
 	int ret, line, flags = dev_id->driver_data;
 
 	if (flags & UNKNOWN_DEV) {
@@ -433,32 +433,32 @@ serial_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id)
 			return ret;
 	}
 
-	memset(&port, 0, sizeof(struct uart_port));
+	memset(&uart, 0, sizeof(uart));
 	if (pnp_irq_valid(dev, 0))
-		port.irq = pnp_irq(dev, 0);
+		uart.port.irq = pnp_irq(dev, 0);
 	if (pnp_port_valid(dev, 0)) {
-		port.iobase = pnp_port_start(dev, 0);
-		port.iotype = UPIO_PORT;
+		uart.port.iobase = pnp_port_start(dev, 0);
+		uart.port.iotype = UPIO_PORT;
 	} else if (pnp_mem_valid(dev, 0)) {
-		port.mapbase = pnp_mem_start(dev, 0);
-		port.iotype = UPIO_MEM;
-		port.flags = UPF_IOREMAP;
+		uart.port.mapbase = pnp_mem_start(dev, 0);
+		uart.port.iotype = UPIO_MEM;
+		uart.port.flags = UPF_IOREMAP;
 	} else
 		return -ENODEV;
 
 #ifdef SERIAL_DEBUG_PNP
 	printk(KERN_DEBUG
 		"Setup PNP port: port %x, mem 0x%lx, irq %d, type %d\n",
-		       port.iobase, port.mapbase, port.irq, port.iotype);
+		       uart.port.iobase, uart.port.mapbase, uart.port.irq, uart.port.iotype);
 #endif
 
-	port.flags |= UPF_SKIP_TEST | UPF_BOOT_AUTOCONF;
+	uart.port.flags |= UPF_SKIP_TEST | UPF_BOOT_AUTOCONF;
 	if (pnp_irq_flags(dev, 0) & IORESOURCE_IRQ_SHAREABLE)
-		port.flags |= UPF_SHARE_IRQ;
-	port.uartclk = 1843200;
-	port.dev = &dev->dev;
+		uart.port.flags |= UPF_SHARE_IRQ;
+	uart.port.uartclk = 1843200;
+	uart.port.dev = &dev->dev;
 
-	line = serial8250_register_port(&port);
+	line = serial8250_register_8250_port(&uart);
 	if (line < 0)
 		return -ENODEV;
 
diff --git a/drivers/tty/serial/8250/serial_cs.c b/drivers/tty/serial/8250/serial_cs.c
index 29b695d..b7d48b3 100644
--- a/drivers/tty/serial/8250/serial_cs.c
+++ b/drivers/tty/serial/8250/serial_cs.c
@@ -73,7 +73,7 @@ struct serial_quirk {
 	unsigned int prodid;
 	int multi;		/* 1 = multifunction, > 1 = # ports */
 	void (*config)(struct pcmcia_device *);
-	void (*setup)(struct pcmcia_device *, struct uart_port *);
+	void (*setup)(struct pcmcia_device *, struct uart_8250_port *);
 	void (*wakeup)(struct pcmcia_device *);
 	int (*post)(struct pcmcia_device *);
 };
@@ -105,9 +105,9 @@ struct serial_cfg_mem {
  * Elan VPU16551 UART with 14.7456MHz oscillator
  * manfid 0x015D, 0x4C45
  */
-static void quirk_setup_brainboxes_0104(struct pcmcia_device *link, struct uart_port *port)
+static void quirk_setup_brainboxes_0104(struct pcmcia_device *link, struct uart_8250_port *uart)
 {
-	port->uartclk = 14745600;
+	uart->port.uartclk = 14745600;
 }
 
 static int quirk_post_ibm(struct pcmcia_device *link)
@@ -343,25 +343,25 @@ static void serial_detach(struct pcmcia_device *link)
 static int setup_serial(struct pcmcia_device *handle, struct serial_info * info,
 			unsigned int iobase, int irq)
 {
-	struct uart_port port;
+	struct uart_8250_port uart;
 	int line;
 
-	memset(&port, 0, sizeof (struct uart_port));
-	port.iobase = iobase;
-	port.irq = irq;
-	port.flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_SHARE_IRQ;
-	port.uartclk = 1843200;
-	port.dev = &handle->dev;
+	memset(&uart, 0, sizeof(uart));
+	uart.port.iobase = iobase;
+	uart.port.irq = irq;
+	uart.port.flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_SHARE_IRQ;
+	uart.port.uartclk = 1843200;
+	uart.port.dev = &handle->dev;
 	if (buggy_uart)
-		port.flags |= UPF_BUGGY_UART;
+		uart.port.flags |= UPF_BUGGY_UART;
 
 	if (info->quirk && info->quirk->setup)
-		info->quirk->setup(handle, &port);
+		info->quirk->setup(handle, &uart);
 
-	line = serial8250_register_port(&port);
+	line = serial8250_register_8250_port(&uart);
 	if (line < 0) {
-		printk(KERN_NOTICE "serial_cs: serial8250_register_port() at "
-		       "0x%04lx, irq %d failed\n", (u_long)iobase, irq);
+		pr_err("serial_cs: serial8250_register_8250_port() at 0x%04lx, irq %d failed\n",
+							(unsigned long)iobase, irq);
 		return -EINVAL;
 	}
 
diff --git a/drivers/tty/serial/of_serial.c b/drivers/tty/serial/of_serial.c
index 34e7187..ffc7879 100644
--- a/drivers/tty/serial/of_serial.c
+++ b/drivers/tty/serial/of_serial.c
@@ -144,8 +144,15 @@ static int __devinit of_platform_serial_probe(struct platform_device *ofdev)
 	switch (port_type) {
 #ifdef CONFIG_SERIAL_8250
 	case PORT_8250 ... PORT_MAX_8250:
-		ret = serial8250_register_port(&port);
+	{
+		/* For now the of bindings don't support the extra
+		   8250 specific bits */
+		struct uart_8250_port port8250;
+		memset(&port8250, 0, sizeof(port8250));
+		port8250.port = port;
+		ret = serial8250_register_8250_port(&port8250);
 		break;
+	}
 #endif
 #ifdef CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL
 	case PORT_NWPSERIAL:
diff --git a/include/linux/serial_8250.h b/include/linux/serial_8250.h
index a416e92..c174c90 100644
--- a/include/linux/serial_8250.h
+++ b/include/linux/serial_8250.h
@@ -65,11 +65,38 @@ enum {
  * platform device.  Using these will make your driver
  * dependent on the 8250 driver.
  */
-struct uart_port;
-struct uart_8250_port;
+
+struct uart_8250_port {
+	struct uart_port	port;
+	struct timer_list	timer;		/* "no irq" timer */
+	struct list_head	list;		/* ports on this IRQ */
+	unsigned short		capabilities;	/* port capabilities */
+	unsigned short		bugs;		/* port bugs */
+	unsigned int		tx_loadsz;	/* transmit fifo load size */
+	unsigned char		acr;
+	unsigned char		ier;
+	unsigned char		lcr;
+	unsigned char		mcr;
+	unsigned char		mcr_mask;	/* mask of user bits */
+	unsigned char		mcr_force;	/* mask of forced bits */
+	unsigned char		cur_iotype;	/* Running I/O type */
+
+	/*
+	 * Some bits in registers are cleared on a read, so they must
+	 * be saved whenever the register is read but the bits will not
+	 * be immediately processed.
+	 */
+#define LSR_SAVE_FLAGS UART_LSR_BRK_ERROR_BITS
+	unsigned char		lsr_saved_flags;
+#define MSR_SAVE_FLAGS UART_MSR_ANY_DELTA
+	unsigned char		msr_saved_flags;
+
+	/* 8250 specific callbacks */
+	int			(*dl_read)(struct uart_8250_port *);
+	void			(*dl_write)(struct uart_8250_port *, int);
+};
 
 int serial8250_register_8250_port(struct uart_8250_port *);
-int serial8250_register_port(struct uart_port *);
 void serial8250_unregister_port(int line);
 void serial8250_suspend_port(int line);
 void serial8250_resume_port(int line);


^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox