linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [ patch 4/7] drivers/serial/jsm: new serial device driver
@ 2005-02-27 23:39 Wen Xiong
  2005-02-28  3:21 ` Christoph Hellwig
  2005-02-28  6:39 ` Greg KH
  0 siblings, 2 replies; 34+ messages in thread
From: Wen Xiong @ 2005-02-27 23:39 UTC (permalink / raw)
  To: linux-kernel, linux-serial

[-- Attachment #1: Type: text/plain, Size: 146 bytes --]

This patch is for jsm_proc.c and includes the functions relating to 
/proc/jsm entry.

Signed-off-by: Wen Xiong <wendyx@us.ltcfwd.linux.ibm.com>


[-- Attachment #2: patch4.jasmine --]
[-- Type: text/plain, Size: 23737 bytes --]

diff -Nuar linux-2.6.9.orig/drivers/serial/jsm/jsm_proc.c linux-2.6.9.new/drivers/serial/jsm/jsm_proc.c
--- linux-2.6.9.orig/drivers/serial/jsm/jsm_proc.c	1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.9.new/drivers/serial/jsm/jsm_proc.c	2005-02-27 17:13:14.339983544 -0600
@@ -0,0 +1,951 @@
+/*
+ * Copyright 2003 Digi International (www.digi.com)
+ *	Scott H Kilau <Scott_Kilau at digi dot 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, or (at your option)
+ * any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; without even the 
+ * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ *	NOTE TO LINUX KERNEL HACKERS:  DO NOT REFORMAT THIS CODE!
+ *
+ *	This is shared code between Digi's CVS archive and the
+ *	Linux Kernel sources.
+ *	Changing the source just for reformatting needlessly breaks
+ *	our CVS diff history.
+ *
+ *	Send any bug fixes/changes to:  Eng.Linux at digi dot com.
+ *	Thank you.
+ *
+ *
+ * $Id: jsm_proc.c,v 1.38 2004/08/30 21:39:40 scottk Exp $
+ */
+#include <linux/kernel.h>
+#include <linux/version.h>
+#include <linux/sched.h>	/* For jiffies, task states */
+#include <linux/interrupt.h>	/* For tasklet and interrupt structs/defines */
+#include <linux/module.h>
+#include <linux/ctype.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/serial_reg.h>
+#include <linux/sched.h>		/* For in_egroup_p() */
+#include <linux/string.h>
+#include <asm/uaccess.h>		/* For copy_from_user/copy_to_user */
+
+#include "jsm_driver.h"
+#include "jsm_mgmt.h"
+
+/* The /proc/jsm directory */
+static struct proc_dir_entry *ProcJSM;
+
+static void *jsm_info_seq_start(struct seq_file *seq, loff_t *pos)
+{
+	if (*pos > 0)
+		return NULL;
+	else
+		return (void *)1;
+}
+
+static void *jsm_info_seq_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+	++*pos;
+	if (*pos > 0)
+		return NULL;
+	else
+		return (void *)1;
+}
+
+static void jsm_info_seq_stop(struct seq_file *seq, void *v)
+{
+
+}
+
+static int jsm_info_seq_show(struct seq_file *seq, void *v)
+{
+
+	seq_printf(seq, "Driver:\t\t%s\n", "jsm - 1.1-1-INKERNEL");
+	seq_printf(seq, "\n");
+	seq_printf(seq, "Debug:\t\t0x%x\n", jsm_debug);
+	seq_printf(seq, "Rawreadok:\t0x%x\n", jsm_rawreadok);
+	seq_printf(seq, "Max Boards:\t%d\n", MAXBOARDS);
+	seq_printf(seq, "Total Boards:\t%d\n", jsm_NumBoards);
+	seq_printf(seq, "Poll counter:\t%ld\n", jsm_poll_counter);
+	seq_printf(seq, "State:\t\t%s\n", jsm_driver_state_text[jsm_driver_state]);
+	return 0;
+
+}
+
+static struct seq_operations jsm_info_seq_ops = {
+	.start = jsm_info_seq_start,
+	.next  = jsm_info_seq_next,
+	.stop  = jsm_info_seq_stop,
+	.show  = jsm_info_seq_show,
+};
+
+static int jsm_info_open(struct inode *inode, struct file *file)
+{
+	struct seq_file *seq;
+	struct proc_dir_entry *proc;
+	int res;
+
+	res = seq_open(file, &jsm_info_seq_ops);
+	if (!res) {
+		/* recover the pointer buried in proc_dir_entry data */
+		seq = file->private_data;
+		proc = PDE(inode);
+		seq->private = proc->data;
+	}
+	return res;
+}
+
+
+static void *jsm_mknod_seq_start(struct seq_file *seq, loff_t *pos)
+{
+	if (*pos > 0)
+		return NULL;
+	else
+		return (void *)1;
+}
+
+static void *jsm_mknod_seq_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+	++*pos;
+	if (*pos > 0)
+		return NULL;
+	else
+		return (void *)1;
+}
+
+static void jsm_mknod_seq_stop(struct seq_file *seq, void *v)
+{
+
+}
+
+static int jsm_mknod_seq_show(struct seq_file *seq, void *v)
+{
+	int i;
+  	seq_printf(seq, "#\tCreate the management devices.\n");
+
+	for (i = 0; i < MAXMGMTDEVICES; i++) {
+		char tmp[100];
+		sprintf(tmp, "/dev/jsm/mgmt%d", i);
+		seq_printf(seq, "%s\t%d\t%d\t%d\n",
+			tmp, jsm_Major, i, 1);
+	}
+	return 0;
+}
+
+static struct seq_operations jsm_mknod_seq_ops = {
+	.start = jsm_mknod_seq_start,
+	.next  = jsm_mknod_seq_next,
+	.stop  = jsm_mknod_seq_stop,
+	.show  = jsm_mknod_seq_show,
+};
+
+static int jsm_mknod_open(struct inode *inode, struct file *file)
+{
+	struct seq_file *seq;
+	struct proc_dir_entry *proc;
+	int res;
+
+	res = seq_open(file, &jsm_mknod_seq_ops);
+	if (!res) {
+		/* recover the pointer buried in proc_dir_entry data */
+		seq = file->private_data;
+		proc = PDE(inode);
+		seq->private = proc->data;
+	}
+	return res;
+}
+
+static void *jsm_board_info_seq_start(struct seq_file *seq, loff_t *pos)
+{
+	if (*pos > 0)
+		return NULL;
+	else
+		return (void *)1;
+}
+
+static void *jsm_board_info_seq_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+	++*pos;
+	if (*pos > 0)
+		return NULL;
+	else
+		return (void *)1;
+}
+
+static void jsm_board_info_seq_stop(struct seq_file *seq, void *v)
+{
+
+}
+
+static int jsm_board_info_seq_show(struct seq_file *seq, void *v)
+{
+	struct board_t	*brd = (struct board_t *) seq->private;
+	char *name;
+
+	DPR_PROC(("jsm_proc_brd_info\n"));
+
+	if (!brd || (brd->magic != JSM_BOARD_MAGIC))
+		return 0;
+
+	name = brd->name;
+
+	seq_printf(seq, "Board Name = %s\n", name);
+	seq_printf(seq, "Board Type = %d\n", brd->type);
+	seq_printf(seq, "Number of Ports = %d\n", brd->nasync);
+
+	/*
+	 * report some things about the PCI bus that are important
+	 * to some applications
+	 */
+	seq_printf(seq, "Vendor ID = 0x%x\n", brd->vendor);
+	seq_printf(seq, "Device ID = 0x%x\n", brd->device);
+	seq_printf(seq, "Subvendor ID = 0x%x\n", brd->subvendor);
+	seq_printf(seq, "Subdevice ID = 0x%x\n", brd->subdevice);
+	seq_printf(seq, "Bus = %d\n", brd->pci_bus);
+	seq_printf(seq, "Slot = %d\n", brd->pci_slot);
+
+	/*
+	 * report the physical addresses assigned to us when we got
+	 * registered
+	 */	
+	seq_printf(seq, "Memory Base Address = 0x%lx\n", brd->membase);
+	seq_printf(seq, "Remapped Memory Base Address = 0x%p\n", brd->re_map_membase);
+
+	seq_printf(seq, "Current state of board = %s\n", jsm_state_text[brd->state]);
+	seq_printf(seq, "Interrupt #: %d. Times interrupted: %ld\n",
+		brd->irq, brd->intr_count);
+	seq_printf(seq, "Majors allocated to board = TTY: %d PR: %d\n",
+		brd->SerialDriver.major, brd->PrintDriver.major);
+
+	return 0;
+}
+
+static struct seq_operations jsm_board_info_seq_ops = {
+	.start = jsm_board_info_seq_start,
+	.next  = jsm_board_info_seq_next,
+	.stop  = jsm_board_info_seq_stop,
+	.show  = jsm_board_info_seq_show,
+};
+
+static int jsm_board_info_open(struct inode *inode, struct file *file)
+{
+	struct seq_file *seq;
+	struct proc_dir_entry *proc;
+	int res;
+
+	res = seq_open(file, &jsm_board_info_seq_ops);
+	if (!res) {
+		/* recover the pointer buried in proc_dir_entry data */
+		seq = file->private_data;
+		proc = PDE(inode);
+		seq->private = proc->data;
+	}
+	return res;
+}
+
+
+static void *jsm_board_stats_seq_start(struct seq_file *seq, loff_t *pos)
+{
+	if (*pos > 0)
+		return NULL;
+	else
+		return (void *)1;
+}
+
+static void *jsm_board_stats_seq_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+	++*pos;
+	if (*pos > 0)
+		return NULL;
+	else
+		return (void *)1;
+}
+
+static void jsm_board_stats_seq_stop(struct seq_file *seq, void *v)
+{
+
+}
+
+static int jsm_board_stats_seq_show(struct seq_file *seq, void *v)
+{
+	struct board_t	*brd = (struct board_t *) seq->private;
+	int i = 0;
+
+
+	if (!brd || (brd->magic != JSM_BOARD_MAGIC))
+		return 0;
+
+	/* Prepare the Header Labels */
+	seq_printf(seq, "%2s %10s %23s %10s %9s\n",
+		"Ch", "Chars Rx", "  Rx Par--Brk--Frm--Ovr",
+		"Chars Tx", "XON XOFF");
+
+	for (i = 0; i < brd->nasync; i++) {
+
+		struct channel_t *ch = brd->channels[i];
+
+		seq_printf(seq, "%2d ", i);
+		seq_printf(seq, "%10ld ", ch->ch_rxcount);
+		seq_printf(seq, "%4ld %4ld %4ld %4ld ", ch->ch_err_parity,
+			ch->ch_err_break, ch->ch_err_frame, ch->ch_err_overrun);
+		seq_printf(seq, "%10ld ", ch->ch_txcount);
+		seq_printf(seq, "%4ld %4ld ", ch->ch_xon_sends, ch->ch_xoff_sends);
+
+		seq_printf(seq, "\n");
+	}
+
+	return 0;
+}
+
+static struct seq_operations jsm_board_stats_seq_ops = {
+	.start = jsm_board_stats_seq_start,
+	.next  = jsm_board_stats_seq_next,
+	.stop  = jsm_board_stats_seq_stop,
+	.show  = jsm_board_stats_seq_show,
+};
+
+static int jsm_board_stats_open(struct inode *inode, struct file *file)
+{
+	struct seq_file *seq;
+	struct proc_dir_entry *proc;
+	int res;
+
+	res = seq_open(file, &jsm_board_stats_seq_ops);
+	if (!res) {
+		/* recover the pointer buried in proc_dir_entry data */
+		seq = file->private_data;
+		proc = PDE(inode);
+		seq->private = proc->data;
+		}
+
+	return res;
+}
+
+
+static void *jsm_board_flags_seq_start(struct seq_file *seq, loff_t *pos)
+{
+	if (*pos > 0)
+		return NULL;
+	else
+		return (void *)1;
+}
+
+static void *jsm_board_flags_seq_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+	++*pos;
+	if (*pos > 0)
+		return NULL;
+	else
+		return (void *)1;
+}
+
+static void jsm_board_flags_seq_stop(struct seq_file *seq, void *v)
+{
+
+}
+
+static int jsm_board_flags_seq_show(struct seq_file *seq, void *v)
+{
+	struct board_t	*brd = (struct board_t *) seq->private;
+	int i = 0;
+
+	DPR_PROC(("jsm_proc_brd_info\n"));
+
+	if (!brd || (brd->magic != JSM_BOARD_MAGIC))
+		return 0;
+
+	/* Prepare the Header Labels */
+	seq_printf(seq, "%2s %5s %5s %5s %5s %5s %10s  Line Status Flags\n",
+		"Ch", "CFlag", "IFlag", "OFlag", "LFlag", "DFlag", "Baud");
+
+	for (i = 0; i < brd->nasync; i++) {
+
+		struct channel_t *ch = brd->channels[i];
+
+		seq_printf(seq, "%2d ", i);
+		seq_printf(seq, "%5x ", ch->ch_c_cflag);
+		seq_printf(seq, "%5x ", ch->ch_c_iflag);
+		seq_printf(seq, "%5x ", ch->ch_c_oflag);
+		seq_printf(seq, "%5x ", ch->ch_c_lflag);
+		seq_printf(seq, "%10d ", ch->ch_old_baud);
+
+		if (!ch->ch_open_count)
+			seq_printf(seq, " -- -- -- -- -- -- --") ;
+		else {
+			seq_printf(seq, " op %s %s %s %s %s %s",
+				(ch->ch_mostat & UART_MCR_RTS) ? "rs" : "--",
+				(ch->ch_mistat & UART_MSR_CTS) ? "cs" : "--",
+				(ch->ch_mostat & UART_MCR_DTR) ? "tr" : "--",
+				(ch->ch_mistat & UART_MSR_DSR) ? "mr" : "--",
+				(ch->ch_mistat & UART_MSR_DCD) ? "cd" : "--",
+				(ch->ch_mistat & UART_MSR_RI)  ? "ri" : "--");
+		}
+
+		seq_printf(seq, "\n");
+	}
+
+	return 0;
+}
+
+static struct seq_operations jsm_board_flags_seq_ops = {
+	.start = jsm_board_flags_seq_start,
+	.next  = jsm_board_flags_seq_next,
+	.stop  = jsm_board_flags_seq_stop,
+	.show  = jsm_board_flags_seq_show,
+};
+
+static int jsm_board_flags_open(struct inode *inode, struct file *file)
+{
+	struct seq_file *seq;
+	struct proc_dir_entry *proc;
+	int res;
+
+	res = seq_open(file, &jsm_board_flags_seq_ops);
+	if (!res) {
+		/* recover the pointer buried in proc_dir_entry data */
+		seq = file->private_data;
+		proc = PDE(inode);
+		seq->private = proc->data;
+	}
+
+	return res;
+}
+
+
+
+static void *jsm_board_mknod_seq_start(struct seq_file *seq, loff_t *pos)
+{
+	if (*pos > 0)
+		return NULL;
+	else
+		return (void *)1;
+}
+
+static void *jsm_board_mknod_seq_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+	++*pos;
+	if (*pos > 0)
+		return NULL;
+	else
+		return (void *)1;
+}
+
+static void jsm_board_mknod_seq_stop(struct seq_file *seq, void *v)
+{
+
+}
+
+static int jsm_board_mknod_seq_show(struct seq_file *seq, void *v)
+{
+	struct board_t	*brd = (struct board_t *) seq->private;
+	char str[MAXTTYNAMELEN];
+
+	DPR_PROC(("jsm_proc_brd_info\n"));
+
+	if (!brd || (brd->magic != JSM_BOARD_MAGIC))
+		return 0;
+
+	/*
+	* For each board, output the device information in
+	* a handy table format...
+	*/
+	seq_printf(seq, "# Create the TTY and PR devices\n");
+
+	/* TTY devices */
+	sprintf(str, "ttyn%d%%p", brd->boardnum + 1);
+	seq_printf(seq, "%s\t\t\t%d\t%d\t%d\n", str,
+		brd->jsm_Serial_Major, 0, brd->maxports);
+
+	/* PR devices */
+	sprintf(str, "prn%d%%p", brd->boardnum + 1);
+	seq_printf(seq, "%s\t\t\t%d\t%d\t%d\n", str,
+		brd->jsm_TransparentPrint_Major, 128, brd->maxports);
+
+	return 0;
+}
+
+static struct seq_operations jsm_board_mknod_seq_ops = {
+	.start = jsm_board_mknod_seq_start,
+	.next  = jsm_board_mknod_seq_next,
+	.stop  = jsm_board_mknod_seq_stop,
+	.show  = jsm_board_mknod_seq_show,
+};
+
+static int jsm_board_mknod_open(struct inode *inode, struct file *file)
+{
+	struct seq_file *seq;
+	struct proc_dir_entry *proc;
+	int res;
+
+	res = seq_open(file, &jsm_board_mknod_seq_ops);
+	if (!res) {
+		/* recover the pointer buried in proc_dir_entry data */
+		seq = file->private_data;
+		proc = PDE(inode);
+		seq->private = proc->data;
+	}
+
+	return res;
+}
+
+
+static void *jsm_channel_info_seq_start(struct seq_file *seq, loff_t *pos)
+{
+	if (*pos > 0)
+		return NULL;
+	else
+		return (void *)1;
+}
+
+static void *jsm_channel_info_seq_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+	++*pos;
+	if (*pos > 0)
+		return NULL;
+	else
+		return (void *)1;
+}
+
+static void jsm_channel_info_seq_stop(struct seq_file *seq, void *v)
+{
+
+}
+
+static int jsm_channel_info_seq_show(struct seq_file *seq, void *v)
+{
+	struct channel_t	*ch = (struct channel_t *) seq->private;
+
+	DPR_PROC(("jsm_proc_info\n"));
+
+	if (!ch || (ch->magic != JSM_CHANNEL_MAGIC))
+		return 0;
+
+	seq_printf(seq, "Port number:\t\t%d\n", ch->ch_portnum);
+	seq_printf(seq, "\n");
+
+	/* Prepare the Header Labels */ 
+	seq_printf(seq, "%10s %23s %10s %9s\n",
+		"Chars Rx", "  Rx Par--Brk--Frm--Ovr",
+		"Chars Tx", "XON XOFF");
+	seq_printf(seq, "%10ld ", ch->ch_rxcount);
+	seq_printf(seq, "%4ld %4ld %4ld %4ld ", ch->ch_err_parity,
+		ch->ch_err_break, ch->ch_err_frame, ch->ch_err_overrun);
+	seq_printf(seq, "%10ld ", ch->ch_txcount);  
+	seq_printf(seq, "%4ld %4ld ", ch->ch_xon_sends, ch->ch_xoff_sends);
+	seq_printf(seq, "\n\n");
+
+	/* Prepare the Header Labels */
+	seq_printf(seq, "%5s %5s %5s %5s %5s %10s  Line Status Flags\n",
+		"CFlag", "IFlag", "OFlag", "LFlag", "DFlag", "Baud");
+
+	seq_printf(seq, "%5x ", ch->ch_c_cflag);
+	seq_printf(seq, "%5x ", ch->ch_c_iflag);
+	seq_printf(seq, "%5x ", ch->ch_c_oflag);
+	seq_printf(seq, "%5x ", ch->ch_c_lflag);
+	seq_printf(seq, "%10d ", ch->ch_old_baud);
+	if (!ch->ch_open_count)
+		seq_printf(seq, " -- -- -- -- -- -- --") ;
+	else {
+		seq_printf(seq, " op %s %s %s %s %s %s", 
+		(ch->ch_mostat & UART_MCR_RTS) ? "rs" : "--",
+		(ch->ch_mistat & UART_MSR_CTS) ? "cs" : "--",
+		(ch->ch_mostat & UART_MCR_DTR) ? "tr" : "--",
+		(ch->ch_mistat & UART_MSR_DSR) ? "mr" : "--",
+		(ch->ch_mistat & UART_MSR_DCD) ? "cd" : "--",
+		(ch->ch_mistat & UART_MSR_RI)  ? "ri" : "--");
+	} 
+	seq_printf(seq, "\n\n");
+
+	return 0;
+}
+
+static struct seq_operations jsm_channel_info_seq_ops = {
+	.start = jsm_channel_info_seq_start,
+	.next  = jsm_channel_info_seq_next,
+	.stop  = jsm_channel_info_seq_stop,
+	.show  = jsm_channel_info_seq_show,
+};
+
+static int jsm_channel_info_open(struct inode *inode, struct file *file)
+{
+	struct seq_file *seq;
+	struct proc_dir_entry *proc;
+	int res;
+
+	res = seq_open(file, &jsm_channel_info_seq_ops);
+	if (!res) {
+		/* recover the pointer buried in proc_dir_entry data */
+		seq = file->private_data;
+		proc = PDE(inode);
+		seq->private = proc->data;
+	}
+
+	return res;
+}
+
+
+static void *jsm_channel_sniff_seq_start(struct seq_file *seq, loff_t *pos)
+{
+	ulong  lock_flags;
+	struct channel_t	*ch = (struct channel_t *) seq->private;
+
+	if (!ch || (ch->magic != JSM_CHANNEL_MAGIC))
+		return NULL;
+
+	ch->ch_sniff_buf = kmalloc(SNIFF_MAX, GFP_KERNEL);
+	memset(ch->ch_sniff_buf, 0, SNIFF_MAX);
+
+	spin_lock_irqsave(&ch->ch_lock, lock_flags);
+	ch->ch_sniff_flags |= SNIFF_OPEN;
+	spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
+
+	return (void *)1;
+}
+
+static void *jsm_channel_sniff_seq_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+	++*pos;
+	if (*pos > 0)
+		return NULL;
+	else
+		return (void *)1;
+}
+
+static void jsm_channel_sniff_seq_stop(struct seq_file *seq, void *v)
+{
+ 	ulong  lock_flags;
+	struct channel_t	*ch = (struct channel_t *) seq->private;
+
+	spin_lock_irqsave(&ch->ch_lock, lock_flags);
+	ch->ch_sniff_flags &= ~(SNIFF_OPEN);
+	kfree(ch->ch_sniff_buf);
+	spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
+}
+
+
+static int jsm_channel_sniff_seq_show(struct seq_file *seq, void *v)
+{
+
+	struct channel_t	*ch = (struct channel_t *) seq->private;
+	int n;
+	int r;
+	int offset = 0;
+	int res = 0;
+	ssize_t rtn = 0;
+	ulong  lock_flags;
+	int count;
+	if (!ch || (ch->magic != JSM_CHANNEL_MAGIC)) {
+		rtn = -ENXIO;
+		goto done;
+	}
+
+	/*
+	 *  Wait for some data to appear in the buffer.
+	 */
+	spin_lock_irqsave(&ch->ch_lock, lock_flags);
+
+	for (;;) {
+		n = (ch->ch_sniff_in - ch->ch_sniff_out) & SNIFF_MASK;
+
+		if (n != 0)
+			break;
+
+		ch->ch_sniff_flags |= SNIFF_WAIT_DATA;
+
+		spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
+
+		/*
+		 * Go to sleep waiting until the condition becomes true.
+		 */
+		rtn = wait_event_interruptible(ch->ch_sniff_wait,
+			((ch->ch_sniff_flags & SNIFF_WAIT_DATA) == 0));
+
+		if (rtn)
+			goto done;
+
+		spin_lock_irqsave(&ch->ch_lock, lock_flags);
+	}
+
+	/*
+	 *  Read whatever is there.
+	 */
+
+	res = n;
+
+	r = SNIFF_MAX - ch->ch_sniff_out;
+
+	if (r <= n) {
+		spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
+		if (rtn) {
+			rtn = -EFAULT;
+			goto done;
+		}
+
+		spin_lock_irqsave(&ch->ch_lock, lock_flags);
+
+		ch->ch_sniff_out = 0;
+		n -= r;
+		offset = r;
+	}
+
+	spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
+	seq_printf(seq, "in =%d out=%d\n",ch->ch_sniff_in,ch->ch_sniff_out);
+	seq_printf(seq, "first=%x\n",ch->ch_sniff_buf[5]);
+	seq_printf(seq, "first=%x\n",ch->ch_sniff_buf[6]);
+	for (count = 0; count < n; count++) {
+		seq_printf(seq, "%x ",ch->ch_sniff_buf[ch->ch_sniff_out++]);
+	}
+	if (rtn) {
+		rtn = -EFAULT;
+		goto done;
+	}
+	spin_lock_irqsave(&ch->ch_lock, lock_flags);
+
+	seq_printf(seq,"\n");
+	seq_printf(seq, "in =%d out=%d\n",ch->ch_sniff_in,ch->ch_sniff_out);
+
+	/*
+	 *  Wakeup any thread waiting for buffer space.
+	 */
+
+	if (ch->ch_sniff_flags & SNIFF_WAIT_SPACE) {
+		ch->ch_sniff_flags &= ~SNIFF_WAIT_SPACE;
+		wake_up_interruptible(&ch->ch_sniff_wait);
+	}
+
+	spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
+
+done:
+	return rtn;
+}
+
+static struct seq_operations jsm_channel_sniff_seq_ops = {
+	.start = jsm_channel_sniff_seq_start,
+	.next  = jsm_channel_sniff_seq_next,
+	.stop  = jsm_channel_sniff_seq_stop,
+	.show  = jsm_channel_sniff_seq_show,
+};
+
+static int jsm_channel_sniff_open(struct inode *inode, struct file *file)
+{
+	struct seq_file *seq;
+	struct proc_dir_entry *proc;
+	int res;
+
+	res = seq_open(file, &jsm_channel_sniff_seq_ops);
+	if (!res) {
+		/* recover the pointer buried in proc_dir_entry data */
+		seq = file->private_data;
+		proc = PDE(inode);
+		seq->private = proc->data;
+	}
+
+	return res;
+}
+
+
+static struct file_operations jsm_info_operations = {
+	.open	= jsm_info_open,
+	.read	= seq_read,
+	.llseek  = seq_lseek,
+	.release = seq_release,
+};
+
+static struct file_operations jsm_mknod_operations = {
+	.open	= jsm_mknod_open,
+	.read	= seq_read,
+	.llseek  = seq_lseek,
+	.release = seq_release,
+};
+
+static struct file_operations jsm_board_info_operations = {
+	.open	= jsm_board_info_open,
+	.read	= seq_read,
+	.llseek  = seq_lseek,
+	.release = seq_release,
+};
+
+static struct file_operations jsm_board_stats_operations = {
+	.open	= jsm_board_stats_open,
+	.read	= seq_read,
+	.llseek  = seq_lseek,
+	.release = seq_release,
+};
+
+static struct file_operations jsm_board_flags_operations = {
+	.open	= jsm_board_flags_open,
+	.read	= seq_read,
+	.llseek  = seq_lseek,
+	.release = seq_release,
+};
+
+static struct file_operations jsm_board_mknod_operations = {
+	.open	= jsm_board_mknod_open,
+	.read	= seq_read,
+	.llseek  = seq_lseek,
+	.release = seq_release,
+};
+
+static struct file_operations jsm_channel_info_operations = 
+{
+	.open	= jsm_channel_info_open,
+	.read	= seq_read,
+	.llseek  = seq_lseek,
+	.release = seq_release,
+};
+
+static struct file_operations jsm_channel_sniff_operations = 
+{
+	.open	= jsm_channel_sniff_open,
+	.read	= seq_read,
+	.llseek  = seq_lseek,
+	.release = seq_release,
+};
+
+/*
+ * Register the basic /proc/jsm files that appear whenever
+ * the driver is loaded.
+ */
+void jsm_proc_register_basic_prescan(void)
+{
+	/*
+	 *Register /proc/jsm
+	 */
+
+	struct proc_dir_entry *entry;
+
+	ProcJSM = proc_mkdir("jsm",&proc_root);
+	if (ProcJSM)
+		ProcJSM->owner = THIS_MODULE;
+	else
+		printk(KERN_WARNING "cann't create /proc/net/jsm\n");
+
+	entry = create_proc_entry("info", S_IRUGO, ProcJSM);
+	if (entry)
+		entry->proc_fops = &jsm_info_operations;
+
+	entry = create_proc_entry("mknod", S_IRUGO, ProcJSM);
+	if (entry)
+		entry->proc_fops = &jsm_mknod_operations;
+}
+
+
+/*
+ * Register the basic /proc/jsm files that appear whenever
+ * the driver is loaded.
+ */
+int jsm_proc_register_basic_postscan(int board_num)
+{
+	int i;
+	char board[10];
+	struct proc_dir_entry *e, *board_e, *channel_e;
+	sprintf(board, "%d", board_num);
+
+	/* Set proc board entry pointer */
+	board_e = jsm_Board[board_num]->proc_entry_pointer = proc_mkdir(board, ProcJSM);
+
+	e = create_proc_entry("info", S_IRUGO, board_e);
+	if (!e)
+		return -ENOMEM;
+	e->proc_fops = &jsm_board_info_operations;
+	e->data = jsm_Board[board_num];
+
+
+	e = create_proc_entry("stats", S_IRUGO, board_e);
+	if (!e)
+		return -ENOMEM;
+	e->proc_fops = &jsm_board_stats_operations;
+	e->data = jsm_Board[board_num];
+
+	e = create_proc_entry("flags", S_IRUGO, board_e);
+	if (!e)
+		return -ENOMEM;
+	e->proc_fops = &jsm_board_flags_operations;
+	e->data = jsm_Board[board_num];
+
+	e = create_proc_entry("mknod", S_IRUGO, board_e);
+	if (!e)
+		return -ENOMEM;
+	e->proc_fops = &jsm_board_mknod_operations;
+	e->data = jsm_Board[board_num];
+
+	/*
+	 * Add new entries for each port.
+	 */
+	for (i = 0; i < jsm_Board[board_num]->nasync; i++) {
+
+		char channel[10];
+		sprintf(channel, "%d", i);
+
+		channel_e = jsm_Board[board_num]->channels[i]->proc_entry_pointer = proc_mkdir(channel,board_e);
+
+		e = create_proc_entry("info", S_IRUGO, channel_e);
+		if (!e)
+			return -ENOMEM;
+		e->proc_fops = &jsm_channel_info_operations;
+		e->data = jsm_Board[board_num]->channels[i];
+
+		e = create_proc_entry("sniff", S_IRUGO, channel_e);
+		if (!e)
+			return -ENOMEM;
+		e->proc_fops = &jsm_channel_sniff_operations;
+		e->data = jsm_Board[board_num]->channels[i];
+
+	}
+
+	return 0;
+}
+
+
+
+void jsm_proc_unregister_brd(int brd_number)
+{
+	int i = 0, j = 0;
+
+	i = brd_number;
+
+	char board_number[16];
+	sprintf(board_number,"%d",i);
+
+
+	for (j = 0; j < jsm_Board[i]->nasync; j++) {
+
+		char channel_number[16];
+		sprintf(channel_number,"%d",j);
+
+		remove_proc_entry("info",jsm_Board[i]->channels[j]->proc_entry_pointer);
+		remove_proc_entry("sniff",jsm_Board[i]->channels[j]->proc_entry_pointer);
+		remove_proc_entry(channel_number,jsm_Board[i]->proc_entry_pointer);
+
+	}
+
+	remove_proc_entry("info",jsm_Board[i]->proc_entry_pointer);
+	remove_proc_entry("mknod",jsm_Board[i]->proc_entry_pointer);
+	remove_proc_entry("flags",jsm_Board[i]->proc_entry_pointer);
+	remove_proc_entry("stats",jsm_Board[i]->proc_entry_pointer);
+	remove_proc_entry(board_number,ProcJSM);
+
+}
+
+void jsm_proc_unregister_all(void)
+{
+
+	/* Blow away the top proc entry */
+	remove_proc_entry("info",ProcJSM);
+	remove_proc_entry("mknod",ProcJSM);
+	remove_proc_entry("jsm", &proc_root);
+}

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [ patch 4/7] drivers/serial/jsm: new serial device driver
  2005-02-27 23:39 [ patch 4/7] drivers/serial/jsm: new serial device driver Wen Xiong
@ 2005-02-28  3:21 ` Christoph Hellwig
  2005-02-28  6:39 ` Greg KH
  1 sibling, 0 replies; 34+ messages in thread
From: Christoph Hellwig @ 2005-02-28  3:21 UTC (permalink / raw)
  To: Wen Xiong; +Cc: linux-kernel, linux-serial

On Sun, Feb 27, 2005 at 06:39:51PM -0500, Wen Xiong wrote:
> This patch is for jsm_proc.c and includes the functions relating to 
> /proc/jsm entry.

please don't put in more procfs driver interfaces.

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [ patch 4/7] drivers/serial/jsm: new serial device driver
  2005-02-27 23:39 [ patch 4/7] drivers/serial/jsm: new serial device driver Wen Xiong
  2005-02-28  3:21 ` Christoph Hellwig
@ 2005-02-28  6:39 ` Greg KH
  2005-03-04 21:08   ` Wen Xiong
  1 sibling, 1 reply; 34+ messages in thread
From: Greg KH @ 2005-02-28  6:39 UTC (permalink / raw)
  To: Wen Xiong; +Cc: linux-kernel, linux-serial

On Sun, Feb 27, 2005 at 06:39:51PM -0500, Wen Xiong wrote:
> This patch is for jsm_proc.c and includes the functions relating to 
> /proc/jsm entry.

No, don't add new /proc stuff.  Use sysfs, and if you want to spit out
more data, use debugfs.

What is the need for these files?

thanks,

greg k-h 

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [ patch 4/7] drivers/serial/jsm: new serial device driver
  2005-02-28  6:39 ` Greg KH
@ 2005-03-04 21:08   ` Wen Xiong
  2005-03-04 22:01     ` Greg KH
  0 siblings, 1 reply; 34+ messages in thread
From: Wen Xiong @ 2005-03-04 21:08 UTC (permalink / raw)
  To: Greg KH; +Cc: Wen Xiong, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 682 bytes --]

Greg KH wrote:

>On Sun, Feb 27, 2005 at 06:39:51PM -0500, Wen Xiong wrote:
>  
>
>>This patch is for jsm_proc.c and includes the functions relating to 
>>/proc/jsm entry.
>>    
>>
>
>No, don't add new /proc stuff.  Use sysfs, and if you want to spit out
>more data, use debugfs.
>
>What is the need for these files?
>
>thanks,
>
>greg k-h 
>-
>To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
>the body of a message to majordomo@vger.kernel.org
>More majordomo info at  http://vger.kernel.org/majordomo-info.html
>Please read the FAQ at  http://www.tux.org/lkml/
>
>  
>
Changed to use sysfs.

Signed-off-by: Wen Xiong <wendyx@us.ltcfwd.linux.ibm.com>



[-- Attachment #2: patch4.jasmine --]
[-- Type: text/plain, Size: 14983 bytes --]

diff -Nuar linux-2.6.11.org/drivers/serial/jsm/jsm_sysfs.c linux-2.6.11.new/drivers/serial/jsm/jsm_sysfs.c
--- linux-2.6.11.org/drivers/serial/jsm/jsm_sysfs.c	1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.11.new/drivers/serial/jsm/jsm_sysfs.c	2005-03-04 11:24:37.962944848 -0600
@@ -0,0 +1,463 @@
+/************************************************************************
+ * Copyright 2003 Digi International (www.digi.com)
+ *
+ * Copyright (C) 2004 IBM Corporation. All rights reserved.
+ *
+ * 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, or (at your option)
+ * any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; without even the 
+ * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License 
+ * along with this program; if not, write to the Free Software 
+ * Foundation, Inc., 59 * Temple Place - Suite 330, Boston,
+ * MA  02111-1307, USA.
+ *
+ * Contact Information:
+ * Scott H Kilau <Scott_Kilau@digi.com>
+ * Wendy Xiong   <wendyx@us.ltcfwd.linux.ibm.com>
+ *
+ ***********************************************************************/
+
+#include "jsm_driver.h"
+
+static struct class_simple *jsm_tty_class;
+
+int get_jsm_board_number(void)
+{
+        struct list_head *tmp;
+        struct jsm_board *cur_board_entry;
+        int adapter_count = 0;
+        u64 lock_flags;
+
+        spin_lock_irqsave(&jsm_board_head_lock, lock_flags);
+        list_for_each(tmp, &jsm_board_head) {
+        cur_board_entry =
+                list_entry(tmp, struct jsm_board,
+                        jsm_board_entry);
+                adapter_count++;
+        }
+        spin_unlock_irqrestore(&jsm_board_head_lock, lock_flags);
+
+        return adapter_count;
+}
+
+static ssize_t jsm_driver_version_show(struct device_driver *ddp, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "jsm_version: %s\n", "jsm: 1.1-1-INKERNEL");
+}
+static DRIVER_ATTR(version, S_IRUSR, jsm_driver_version_show, NULL);
+
+static ssize_t jsm_driver_boards_show(struct device_driver *ddp, char *buf)
+{
+	int adapter_count = 0;
+	adapter_count = get_jsm_board_number();
+	return snprintf(buf, PAGE_SIZE, "jsm_board_number: %d\n", adapter_count);
+}
+static DRIVER_ATTR(boards, S_IRUSR, jsm_driver_boards_show, NULL);
+
+static ssize_t jsm_driver_state_show(struct device_driver *ddp, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "jsm_state: %s\n", jsm_driver_state_text[jsm_driver_state]);
+}
+static DRIVER_ATTR(state, S_IRUSR, jsm_driver_state_show, NULL);
+
+static ssize_t jsm_driver_debug_show(struct device_driver *ddp, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "0x%x\n", debug);
+}
+
+static ssize_t jsm_driver_debug_store(struct device_driver *ddp, const char *buf, size_t count)
+{
+	sscanf(buf, "0x%x\n", &debug);
+	return count;
+}
+static DRIVER_ATTR(debug, (S_IRUSR | S_IWUSR), jsm_driver_debug_show, jsm_driver_debug_store);
+
+
+static ssize_t jsm_driver_rawreadok_show(struct device_driver *ddp, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "0x%x\n", rawreadok);
+}
+
+static ssize_t jsm_driver_rawreadok_store(struct device_driver *ddp, const char *buf, size_t count)
+{
+	sscanf(buf, "0x%x\n", &rawreadok);
+	return count;
+}
+static DRIVER_ATTR(rawreadok, (S_IRUSR | S_IWUSR), jsm_driver_rawreadok_show, jsm_driver_rawreadok_store);
+
+void jsm_create_driver_sysfiles(struct device_driver *driverfs)
+{
+	driver_create_file(driverfs, &driver_attr_version);
+	driver_create_file(driverfs, &driver_attr_boards);
+	driver_create_file(driverfs, &driver_attr_debug);
+	driver_create_file(driverfs, &driver_attr_rawreadok); 
+	driver_create_file(driverfs, &driver_attr_state);
+}
+
+void jsm_remove_driver_sysfiles(struct device_driver  *driverfs)
+{
+	driver_remove_file(driverfs, &driver_attr_version);
+	driver_remove_file(driverfs, &driver_attr_boards);
+	driver_remove_file(driverfs, &driver_attr_debug);
+	driver_remove_file(driverfs, &driver_attr_rawreadok);
+	driver_remove_file(driverfs, &driver_attr_state);
+}
+
+#define JSM_VERIFY_BOARD(p, bd)				\
+	if (!p)						\
+		return 0;				\
+	bd = (struct jsm_board *)dev_get_drvdata(p);	\
+	if (!bd)					\
+		return 0;				\
+	if (bd->state != BOARD_READY)			\
+		return 0;				\
+
+static ssize_t jsm_ports_state_show(struct device *p, char *buf)
+{
+	struct jsm_board *bd;
+	int count = 0;
+	int i = 0;
+
+	JSM_VERIFY_BOARD(p, bd);
+
+	for (i = 0; i < bd->nasync; i++) {
+		count += snprintf(buf + count, PAGE_SIZE - count,
+			"%d %s\n", bd->channels[i]->ch_portnum,
+			bd->channels[i]->ch_open_count ? "Open" : "Closed");
+	}
+	return count;
+}
+static DEVICE_ATTR(ports_state, S_IRUSR, jsm_ports_state_show, NULL);
+
+static ssize_t jsm_ports_baud_show(struct device *p, char *buf)
+{
+	struct jsm_board *bd;
+	int count = 0;
+	int i = 0;
+
+	JSM_VERIFY_BOARD(p, bd);
+
+	for (i = 0; i < bd->nasync; i++) {
+		count +=  snprintf(buf + count, PAGE_SIZE - count,
+			"%d %d\n", bd->channels[i]->ch_portnum, bd->channels[i]->ch_old_baud);
+	}
+	return count;
+}
+static DEVICE_ATTR(ports_baud, S_IRUSR, jsm_ports_baud_show, NULL);
+
+static ssize_t jsm_ports_msignals_show(struct device *p, char *buf)
+{
+	struct jsm_board *bd;
+	int count = 0;
+	int i = 0;
+
+	JSM_VERIFY_BOARD(p, bd);
+
+	for (i = 0; i < bd->nasync; i++) {
+		if (bd->channels[i]->ch_open_count) {
+			count += snprintf(buf + count, PAGE_SIZE - count,
+				"%d %s %s %s %s %s %s\n", bd->channels[i]->ch_portnum,
+				(bd->channels[i]->ch_mostat & UART_MCR_RTS) ? "RTS" : "",
+				(bd->channels[i]->ch_mistat & UART_MSR_CTS) ? "CTS" : "",
+				(bd->channels[i]->ch_mostat & UART_MCR_DTR) ? "DTR" : "",
+				(bd->channels[i]->ch_mistat & UART_MSR_DSR) ? "DSR" : "",
+				(bd->channels[i]->ch_mistat & UART_MSR_DCD) ? "DCD" : "",
+				(bd->channels[i]->ch_mistat & UART_MSR_RI)  ? "RI"  : "");
+		} else {
+			count += snprintf(buf + count, PAGE_SIZE - count,
+				"%d\n", bd->channels[i]->ch_portnum);
+		}
+	}
+	return count;
+}
+static DEVICE_ATTR(ports_msignals, S_IRUSR, jsm_ports_msignals_show, NULL);
+
+static ssize_t jsm_ports_iflag_show(struct device *p, char *buf)
+{
+	struct jsm_board *bd;
+	int count = 0;
+	int i = 0;
+
+	JSM_VERIFY_BOARD(p, bd);
+
+	for (i = 0; i < bd->nasync; i++) {
+		count += snprintf(buf + count, PAGE_SIZE - count, "%d %x\n",
+			bd->channels[i]->ch_portnum, bd->channels[i]->ch_c_iflag);
+	}
+	return count;
+}
+static DEVICE_ATTR(ports_iflag, S_IRUSR, jsm_ports_iflag_show, NULL);
+
+static ssize_t jsm_ports_cflag_show(struct device *p, char *buf)
+{
+	struct jsm_board *bd;
+	int count = 0;
+	int i = 0;
+
+	JSM_VERIFY_BOARD(p, bd);
+
+	for (i = 0; i < bd->nasync; i++) {
+		count += snprintf(buf + count, PAGE_SIZE - count, "%d %x\n",
+			bd->channels[i]->ch_portnum, bd->channels[i]->ch_c_cflag);
+	}
+	return count;
+}
+static DEVICE_ATTR(ports_cflag, S_IRUSR, jsm_ports_cflag_show, NULL);
+
+static ssize_t jsm_ports_oflag_show(struct device *p, char *buf)
+{
+	struct jsm_board *bd;
+	int count = 0;
+	int i = 0;
+
+	JSM_VERIFY_BOARD(p, bd);
+
+	for (i = 0; i < bd->nasync; i++) {
+		count += snprintf(buf + count, PAGE_SIZE - count, "%d %x\n",
+			bd->channels[i]->ch_portnum, bd->channels[i]->ch_c_oflag);
+	}
+	return count;
+}
+static DEVICE_ATTR(ports_oflag, S_IRUSR, jsm_ports_oflag_show, NULL);
+
+static ssize_t jsm_ports_lflag_show(struct device *p, char *buf)
+{
+	struct jsm_board *bd;
+	int count = 0;
+	int i = 0;
+
+	JSM_VERIFY_BOARD(p, bd);
+
+	for (i = 0; i < bd->nasync; i++) {
+		count += snprintf(buf + count, PAGE_SIZE - count, "%d %x\n",
+			bd->channels[i]->ch_portnum, bd->channels[i]->ch_c_lflag);
+	}
+	return count;
+}
+static DEVICE_ATTR(ports_lflag, S_IRUSR, jsm_ports_lflag_show, NULL);
+
+static ssize_t jsm_ports_rxcount_show(struct device *p, char *buf)
+{
+	struct jsm_board *bd;
+	int count = 0;
+	int i = 0;
+
+	JSM_VERIFY_BOARD(p, bd);
+
+	for (i = 0; i < bd->nasync; i++) {
+		count += snprintf(buf + count, PAGE_SIZE - count, "%d %ld\n",
+			bd->channels[i]->ch_portnum, bd->channels[i]->ch_rxcount);
+	}
+	return count;
+}
+static DEVICE_ATTR(ports_rxcount, S_IRUSR, jsm_ports_rxcount_show, NULL);
+
+static ssize_t jsm_ports_txcount_show(struct device *p, char *buf)
+{
+	struct jsm_board *bd;
+	int count = 0;
+	int i = 0;
+
+	JSM_VERIFY_BOARD(p, bd);
+
+	for (i = 0; i < bd->nasync; i++) {
+		count += snprintf(buf + count, PAGE_SIZE - count, "%d %ld\n",
+			bd->channels[i]->ch_portnum, bd->channels[i]->ch_txcount);
+	}
+	return count;
+}
+static DEVICE_ATTR(ports_txcount, S_IRUSR, jsm_ports_txcount_show, NULL);
+
+/* this function creates the sys files that will export each signal status
+ * to sysfs each value will be put in a separate filename
+ */
+void jsm_create_ports_sysfiles(struct jsm_board *bd, struct device *devicefs)
+{
+	dev_set_drvdata(devicefs, bd);
+	device_create_file(devicefs, &dev_attr_ports_state);
+	device_create_file(devicefs, &dev_attr_ports_baud);
+	device_create_file(devicefs, &dev_attr_ports_msignals);
+	device_create_file(devicefs, &dev_attr_ports_iflag);
+	device_create_file(devicefs, &dev_attr_ports_cflag);
+	device_create_file(devicefs, &dev_attr_ports_oflag);
+	device_create_file(devicefs, &dev_attr_ports_lflag);
+	device_create_file(devicefs, &dev_attr_ports_rxcount);
+	device_create_file(devicefs, &dev_attr_ports_txcount);
+}
+
+/* removes all the sys files created for that port */
+void jsm_remove_ports_sysfiles(struct jsm_board *bd, struct device *devicefs)
+{
+	device_remove_file(devicefs, &dev_attr_ports_state);
+	device_remove_file(devicefs, &dev_attr_ports_baud);
+	device_remove_file(devicefs, &dev_attr_ports_msignals);
+	device_remove_file(devicefs, &dev_attr_ports_iflag);
+	device_remove_file(devicefs, &dev_attr_ports_cflag);
+	device_remove_file(devicefs, &dev_attr_ports_oflag);
+	device_remove_file(devicefs, &dev_attr_ports_lflag);
+	device_remove_file(devicefs, &dev_attr_ports_rxcount);
+	device_remove_file(devicefs, &dev_attr_ports_txcount);
+}
+
+
+int jsm_tty_class_init(void)
+{
+	jsm_tty_class = class_simple_create(THIS_MODULE, "jsm_tty");
+	if (IS_ERR(jsm_tty_class))
+		return PTR_ERR(jsm_tty_class);
+
+	return 0;
+}
+
+int jsm_tty_class_destroy(void)
+{
+	if (IS_ERR(jsm_tty_class))
+		return PTR_ERR(jsm_tty_class);
+
+	class_simple_destroy(jsm_tty_class);
+	return 0;
+}
+
+#define JSM_VERIFY_CHANNEL(p, ch)			\
+	if (!p)						\
+		return 0;				\
+	ch = (struct jsm_channel *)class_get_devdata(p);\
+	if (!ch)					\
+		return 0;				\
+	if (ch->ch_bd->state != BOARD_READY)		\
+		return 0;				\
+
+static ssize_t jsm_tty_state_show(struct class_device *class_dev, char *buf)
+{
+	struct jsm_channel *ch;
+
+	JSM_VERIFY_CHANNEL(class_dev, ch);
+	return snprintf(buf, PAGE_SIZE, "%s\n", ch->ch_open_count ? "Open" : "Closed");
+}
+static CLASS_DEVICE_ATTR(state, S_IRUGO, jsm_tty_state_show, NULL);
+
+
+static ssize_t jsm_tty_baud_show(struct class_device *class_dev, char *buf)
+{
+	struct jsm_channel *ch;
+
+	JSM_VERIFY_CHANNEL(class_dev, ch);
+	return snprintf(buf, PAGE_SIZE, "%d\n", ch->ch_old_baud);
+}
+static CLASS_DEVICE_ATTR(baud, S_IRUGO, jsm_tty_baud_show, NULL);
+
+
+static ssize_t jsm_tty_msignals_show(struct class_device *class_dev, char *buf)
+{
+	struct jsm_channel *ch;
+
+	JSM_VERIFY_CHANNEL(class_dev, ch);
+	if (ch->ch_open_count) {
+		return snprintf(buf, PAGE_SIZE, "%s %s %s %s %s %s\n",
+			(ch->ch_mostat & UART_MCR_RTS) ? "RTS" : "",
+			(ch->ch_mistat & UART_MSR_CTS) ? "CTS" : "",
+			(ch->ch_mostat & UART_MCR_DTR) ? "DTR" : "",
+			(ch->ch_mistat & UART_MSR_DSR) ? "DSR" : "",
+			(ch->ch_mistat & UART_MSR_DCD) ? "DCD" : "",
+			(ch->ch_mistat & UART_MSR_RI)  ? "RI"  : "");
+	}
+	return 0;
+}
+static CLASS_DEVICE_ATTR(msignals, S_IRUGO, jsm_tty_msignals_show, NULL);
+
+static ssize_t jsm_tty_iflag_show(struct class_device *class_dev, char *buf)
+{
+	struct jsm_channel *ch;
+
+	JSM_VERIFY_CHANNEL(class_dev, ch);
+	return snprintf(buf, PAGE_SIZE, "%x\n", ch->ch_c_iflag);
+}
+static CLASS_DEVICE_ATTR(iflag, S_IRUGO, jsm_tty_iflag_show, NULL);
+
+
+static ssize_t jsm_tty_cflag_show(struct class_device *class_dev, char *buf)
+{
+	struct jsm_channel *ch;
+
+	JSM_VERIFY_CHANNEL(class_dev, ch);
+	return snprintf(buf, PAGE_SIZE, "%x\n", ch->ch_c_cflag);
+}
+static CLASS_DEVICE_ATTR(cflag, S_IRUGO, jsm_tty_cflag_show, NULL);
+
+static ssize_t jsm_tty_oflag_show(struct class_device *class_dev, char *buf)
+{
+	struct jsm_channel *ch;
+
+	JSM_VERIFY_CHANNEL(class_dev, ch);
+	return snprintf(buf, PAGE_SIZE, "%x\n", ch->ch_c_oflag);
+}
+static CLASS_DEVICE_ATTR(oflag, S_IRUGO, jsm_tty_oflag_show, NULL);
+
+static ssize_t jsm_tty_lflag_show(struct class_device *class_dev, char *buf)
+{
+	struct jsm_channel *ch;
+
+	JSM_VERIFY_CHANNEL(class_dev, ch);
+	return snprintf(buf, PAGE_SIZE, "%x\n", ch->ch_c_lflag);
+}
+static CLASS_DEVICE_ATTR(lflag, S_IRUGO, jsm_tty_lflag_show, NULL);
+
+static ssize_t jsm_tty_rxcount_show(struct class_device *class_dev, char *buf)
+{
+	struct jsm_channel *ch;
+
+	JSM_VERIFY_CHANNEL(class_dev, ch);
+	return snprintf(buf, PAGE_SIZE, "%ld\n", ch->ch_rxcount);
+}
+static CLASS_DEVICE_ATTR(rxcount, S_IRUGO, jsm_tty_rxcount_show, NULL);
+
+static ssize_t jsm_tty_txcount_show(struct class_device *class_dev, char *buf)
+{
+	struct jsm_channel *ch;
+
+	JSM_VERIFY_CHANNEL(class_dev, ch);
+	return snprintf(buf, PAGE_SIZE, "%ld\n", ch->ch_txcount);
+}
+static CLASS_DEVICE_ATTR(txcount, S_IRUGO, jsm_tty_txcount_show, NULL);
+
+void jsm_tty_register_device(struct jsm_channel *ch, struct device *device)
+{
+	struct class_device *jsm_class_member;
+	dev_t dev;
+
+	dev = MKDEV(ch->ch_bd->jsm_serial_major,
+			ch->ch_portnum + ch->ch_bd->boardnum * 2);
+
+	jsm_class_member = class_simple_device_add(jsm_tty_class, dev, device,
+			"ttyn%d", ch->ch_portnum + ch->ch_bd->boardnum*2);
+
+	if (IS_ERR(jsm_class_member))
+		return;
+
+	class_set_devdata(jsm_class_member, ch);
+	class_device_create_file(jsm_class_member, &class_device_attr_state);
+	class_device_create_file(jsm_class_member, &class_device_attr_baud);
+	class_device_create_file(jsm_class_member, &class_device_attr_msignals);
+	class_device_create_file(jsm_class_member, &class_device_attr_iflag);
+	class_device_create_file(jsm_class_member, &class_device_attr_cflag);
+	class_device_create_file(jsm_class_member, &class_device_attr_oflag);
+	class_device_create_file(jsm_class_member, &class_device_attr_lflag);
+	class_device_create_file(jsm_class_member, &class_device_attr_rxcount);
+	class_device_create_file(jsm_class_member, &class_device_attr_txcount);
+}
+
+void jsm_tty_unregister_device(struct jsm_channel *ch)
+{
+
+	class_simple_device_remove(MKDEV(ch->ch_bd->jsm_serial_major, 
+					ch->ch_portnum + ch->ch_bd->boardnum * 2));
+}

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [ patch 4/7] drivers/serial/jsm: new serial device driver
  2005-03-04 21:08   ` Wen Xiong
@ 2005-03-04 22:01     ` Greg KH
  2005-03-07 22:46       ` Wen Xiong
  0 siblings, 1 reply; 34+ messages in thread
From: Greg KH @ 2005-03-04 22:01 UTC (permalink / raw)
  To: Wen Xiong; +Cc: linux-kernel

On Fri, Mar 04, 2005 at 04:08:17PM -0500, Wen Xiong wrote:
> +int get_jsm_board_number(void)
> +{
> +        struct list_head *tmp;
> +        struct jsm_board *cur_board_entry;
> +        int adapter_count = 0;
> +        u64 lock_flags;
> +
> +        spin_lock_irqsave(&jsm_board_head_lock, lock_flags);
> +        list_for_each(tmp, &jsm_board_head) {
> +        cur_board_entry =
> +                list_entry(tmp, struct jsm_board,
> +                        jsm_board_entry);
> +                adapter_count++;
> +        }
> +        spin_unlock_irqrestore(&jsm_board_head_lock, lock_flags);
> +
> +        return adapter_count;
> +}

Should this be static?

And it's returning the number of boards, not the current board number,
right?

And you have a indenting error in the list_for_each() section...

> +static ssize_t jsm_driver_version_show(struct device_driver *ddp, char *buf)
> +{
> +	return snprintf(buf, PAGE_SIZE, "jsm_version: %s\n", "jsm: 1.1-1-INKERNEL");

Shouldn't that value also be in MODULE_VERSION()?  And if so, it should
be a #define somewhere.

Also, don't put "jsm:" in your sysfs files, the file name describes what
the value should be.  That goes for a lot of your sysfs files.

> +static ssize_t jsm_driver_debug_show(struct device_driver *ddp, char *buf)
> +{
> +	return snprintf(buf, PAGE_SIZE, "0x%x\n", debug);
> +}

"debug" is not a nice variable to have in the global namespace :(

Also, why not just make this a module paramater, that way it can be
modified through that interface, and you don't have to create your own?

> +#define JSM_VERIFY_BOARD(p, bd)				\
> +	if (!p)						\
> +		return 0;				\
> +	bd = (struct jsm_board *)dev_get_drvdata(p);	\

Cast is not needed.

> +	if (!bd)					\
> +		return 0;				\
> +	if (bd->state != BOARD_READY)			\
> +		return 0;				\

Don't break out of functions from within a macro.  Will cause headaches
for people reviewing your code in the future.

And shouldn't you be returning an error if one of these checks fail?

> +static ssize_t jsm_ports_state_show(struct device *p, char *buf)
> +{
> +	struct jsm_board *bd;
> +	int count = 0;
> +	int i = 0;
> +
> +	JSM_VERIFY_BOARD(p, bd);
> +
> +	for (i = 0; i < bd->nasync; i++) {
> +		count += snprintf(buf + count, PAGE_SIZE - count,
> +			"%d %s\n", bd->channels[i]->ch_portnum,
> +			bd->channels[i]->ch_open_count ? "Open" : "Closed");
> +	}
> +	return count;

No, make this a per-port value.  You are showing more than one value in
a single file.  You do this for a few other sysfs files :(

And who cares about this value?

> +static ssize_t jsm_ports_baud_show(struct device *p, char *buf)
> +{
> +	struct jsm_board *bd;
> +	int count = 0;
> +	int i = 0;
> +
> +	JSM_VERIFY_BOARD(p, bd);
> +
> +	for (i = 0; i < bd->nasync; i++) {
> +		count +=  snprintf(buf + count, PAGE_SIZE - count,
> +			"%d %d\n", bd->channels[i]->ch_portnum, bd->channels[i]->ch_old_baud);
> +	}
> +	return count;
> +}
> +static DEVICE_ATTR(ports_baud, S_IRUSR, jsm_ports_baud_show, NULL);

What's wrong with the standard tty ioctls that return this value, and
the other values you are exporting through sysfs?  What is all of this
data needed for?

> +#define JSM_VERIFY_CHANNEL(p, ch)			\
> +	if (!p)						\
> +		return 0;				\
> +	ch = (struct jsm_channel *)class_get_devdata(p);\
> +	if (!ch)					\
> +		return 0;				\
> +	if (ch->ch_bd->state != BOARD_READY)		\
> +		return 0;				\

Again, don't put return in a macro, and return an error if there really
is one.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [ patch 4/7] drivers/serial/jsm: new serial device driver
  2005-03-04 22:01     ` Greg KH
@ 2005-03-07 22:46       ` Wen Xiong
  2005-03-08  6:44         ` Greg KH
  0 siblings, 1 reply; 34+ messages in thread
From: Wen Xiong @ 2005-03-07 22:46 UTC (permalink / raw)
  To: Greg KH; +Cc: Wen Xiong, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 4076 bytes --]

Greg KH wrote:

>On Fri, Mar 04, 2005 at 04:08:17PM -0500, Wen Xiong wrote:
>  
>
>>+int get_jsm_board_number(void)
>>+{
>>+        struct list_head *tmp;
>>+        struct jsm_board *cur_board_entry;
>>+        int adapter_count = 0;
>>+        u64 lock_flags;
>>+
>>+        spin_lock_irqsave(&jsm_board_head_lock, lock_flags);
>>+        list_for_each(tmp, &jsm_board_head) {
>>+        cur_board_entry =
>>+                list_entry(tmp, struct jsm_board,
>>+                        jsm_board_entry);
>>+                adapter_count++;
>>+        }
>>+        spin_unlock_irqrestore(&jsm_board_head_lock, lock_flags);
>>+
>>+        return adapter_count;
>>+}
>>    
>>
>
>Should this be static?
>
>And it's returning the number of boards, not the current board number,
>right?
>
>And you have a indenting error in the list_for_each() section...
>
>  
>
>>+static ssize_t jsm_driver_version_show(struct device_driver *ddp, char *buf)
>>+{
>>+	return snprintf(buf, PAGE_SIZE, "jsm_version: %s\n", "jsm: 1.1-1-INKERNEL");
>>    
>>
>
>Shouldn't that value also be in MODULE_VERSION()?  And if so, it should
>be a #define somewhere.
>
>Also, don't put "jsm:" in your sysfs files, the file name describes what
>the value should be.  That goes for a lot of your sysfs files.
>
>  
>
>>+static ssize_t jsm_driver_debug_show(struct device_driver *ddp, char *buf)
>>+{
>>+	return snprintf(buf, PAGE_SIZE, "0x%x\n", debug);
>>+}
>>    
>>
>
>"debug" is not a nice variable to have in the global namespace :(
>
>Also, why not just make this a module paramater, that way it can be
>modified through that interface, and you don't have to create your own?
>
>  
>
>>+#define JSM_VERIFY_BOARD(p, bd)				\
>>+	if (!p)						\
>>+		return 0;				\
>>+	bd = (struct jsm_board *)dev_get_drvdata(p);	\
>>    
>>
>
>Cast is not needed.
>
>  
>
>>+	if (!bd)					\
>>+		return 0;				\
>>+	if (bd->state != BOARD_READY)			\
>>+		return 0;				\
>>    
>>
>
>Don't break out of functions from within a macro.  Will cause headaches
>for people reviewing your code in the future.
>
>And shouldn't you be returning an error if one of these checks fail?
>
>  
>
>>+static ssize_t jsm_ports_state_show(struct device *p, char *buf)
>>+{
>>+	struct jsm_board *bd;
>>+	int count = 0;
>>+	int i = 0;
>>+
>>+	JSM_VERIFY_BOARD(p, bd);
>>+
>>+	for (i = 0; i < bd->nasync; i++) {
>>+		count += snprintf(buf + count, PAGE_SIZE - count,
>>+			"%d %s\n", bd->channels[i]->ch_portnum,
>>+			bd->channels[i]->ch_open_count ? "Open" : "Closed");
>>+	}
>>+	return count;
>>    
>>
>
>No, make this a per-port value.  You are showing more than one value in
>a single file.  You do this for a few other sysfs files :(
>
>And who cares about this value?
>
>  
>
>>+static ssize_t jsm_ports_baud_show(struct device *p, char *buf)
>>+{
>>+	struct jsm_board *bd;
>>+	int count = 0;
>>+	int i = 0;
>>+
>>+	JSM_VERIFY_BOARD(p, bd);
>>+
>>+	for (i = 0; i < bd->nasync; i++) {
>>+		count +=  snprintf(buf + count, PAGE_SIZE - count,
>>+			"%d %d\n", bd->channels[i]->ch_portnum, bd->channels[i]->ch_old_baud);
>>+	}
>>+	return count;
>>+}
>>+static DEVICE_ATTR(ports_baud, S_IRUSR, jsm_ports_baud_show, NULL);
>>    
>>
>
>What's wrong with the standard tty ioctls that return this value, and
>the other values you are exporting through sysfs?  What is all of this
>data needed for?
>
>  
>
>>+#define JSM_VERIFY_CHANNEL(p, ch)			\
>>+	if (!p)						\
>>+		return 0;				\
>>+	ch = (struct jsm_channel *)class_get_devdata(p);\
>>+	if (!ch)					\
>>+		return 0;				\
>>+	if (ch->ch_bd->state != BOARD_READY)		\
>>+		return 0;				\
>>    
>>
>
>Again, don't put return in a macro, and return an error if there really
>is one.
>
>thanks,
>
>greg k-h
>-
>To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
>the body of a message to majordomo@vger.kernel.org
>More majordomo info at  http://vger.kernel.org/majordomo-info.html
>Please read the FAQ at  http://www.tux.org/lkml/
>
>  
>
Hi Greg,
Since some tools in Digi  need some new ioctls, so I still keep some new 
ioctls.

Thanks for your reviewing!
wendy

[-- Attachment #2: patch4.jasmine --]
[-- Type: text/plain, Size: 9252 bytes --]

diff -Nuar linux-2.6.11.org/drivers/serial/jsm/jsm_sysfs.c linux-2.6.11.new/drivers/serial/jsm/jsm_sysfs.c
--- linux-2.6.11.org/drivers/serial/jsm/jsm_sysfs.c	1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.11.new/drivers/serial/jsm/jsm_sysfs.c	2005-03-07 16:27:47.293998096 -0600
@@ -0,0 +1,289 @@
+/************************************************************************
+ * Copyright 2003 Digi International (www.digi.com)
+ *
+ * Copyright (C) 2004 IBM Corporation. All rights reserved.
+ *
+ * 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, or (at your option)
+ * any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; without even the 
+ * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License 
+ * along with this program; if not, write to the Free Software 
+ * Foundation, Inc., 59 * Temple Place - Suite 330, Boston,
+ * MA  02111-1307, USA.
+ *
+ * Contact Information:
+ * Scott H Kilau <Scott_Kilau@digi.com>
+ * Wendy Xiong   <wendyx@us.ltcfwd.linux.ibm.com>
+ *
+ ***********************************************************************/
+
+#include <linux/device.h>
+#include <linux/serial_reg.h>
+
+#include "jsm_driver.h"
+
+static struct class_simple *jsm_tty_class;
+
+int jsm_total_boardnum(void)
+{
+	struct list_head *tmp;
+	struct jsm_board *cur_board_entry;
+	int adapter_count = 0;
+	u64 lock_flags;
+
+	spin_lock_irqsave(&jsm_board_head_lock, lock_flags);
+	list_for_each(tmp, &jsm_board_head) {
+		cur_board_entry =
+			list_entry(tmp, struct jsm_board,
+			jsm_board_entry);
+		adapter_count++;
+	}
+	spin_unlock_irqrestore(&jsm_board_head_lock, lock_flags);
+
+	return adapter_count;
+}
+
+static ssize_t jsm_driver_version_show(struct device_driver *ddp, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "jsm_version: %s\n", JSM_VERSION);
+}
+static DRIVER_ATTR(version, S_IRUSR, jsm_driver_version_show, NULL);
+
+static ssize_t jsm_driver_boards_show(struct device_driver *ddp, char *buf)
+{
+	int adapter_count = 0;
+	adapter_count = jsm_total_boardnum();
+	return snprintf(buf, PAGE_SIZE, "jsm_board_number: %d\n", adapter_count);
+}
+static DRIVER_ATTR(boards, S_IRUSR, jsm_driver_boards_show, NULL);
+
+static ssize_t jsm_driver_state_show(struct device_driver *ddp, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "jsm_state: %s\n", jsm_driver_state_text[jsm_driver_state]);
+}
+static DRIVER_ATTR(state, S_IRUSR, jsm_driver_state_show, NULL);
+
+static ssize_t jsm_driver_debug_show(struct device_driver *ddp, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "0x%x\n", jsm_debug);
+}
+static DRIVER_ATTR(debug, S_IRUSR, jsm_driver_debug_show, NULL);
+
+
+static ssize_t jsm_driver_rawreadok_show(struct device_driver *ddp, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "0x%x\n", jsm_rawreadok);
+}
+static DRIVER_ATTR(rawreadok, S_IRUSR, jsm_driver_rawreadok_show, NULL);
+
+void jsm_create_driver_sysfiles(struct device_driver *driverfs)
+{
+	driver_create_file(driverfs, &driver_attr_version);
+	driver_create_file(driverfs, &driver_attr_boards);
+	driver_create_file(driverfs, &driver_attr_debug);
+	driver_create_file(driverfs, &driver_attr_rawreadok); 
+	driver_create_file(driverfs, &driver_attr_state);
+}
+
+void jsm_remove_driver_sysfiles(struct device_driver  *driverfs)
+{
+	driver_remove_file(driverfs, &driver_attr_version);
+	driver_remove_file(driverfs, &driver_attr_boards);
+	driver_remove_file(driverfs, &driver_attr_debug);
+	driver_remove_file(driverfs, &driver_attr_rawreadok);
+	driver_remove_file(driverfs, &driver_attr_state);
+}
+
+int jsm_tty_class_init(void)
+{
+	jsm_tty_class = class_simple_create(THIS_MODULE, "jsm_tty");
+	if (IS_ERR(jsm_tty_class))
+		return PTR_ERR(jsm_tty_class);
+
+	return 0;
+}
+
+int jsm_tty_class_destroy(void)
+{
+	if (IS_ERR(jsm_tty_class))
+		return PTR_ERR(jsm_tty_class);
+
+	class_simple_destroy(jsm_tty_class);
+	return 0;
+}
+
+static ssize_t jsm_tty_state_show(struct class_device *class_dev, char *buf)
+{
+	struct jsm_channel *ch;
+
+	if (class_dev) {
+		ch = class_get_devdata(class_dev);
+		if ( ch && (ch->ch_bd->state == BOARD_READY))
+			return snprintf(buf, PAGE_SIZE, "%s\n", ch->ch_open_count ? "Open" : "Closed");
+	}
+
+	return -EINVAL;
+}
+static CLASS_DEVICE_ATTR(state, S_IRUGO, jsm_tty_state_show, NULL);
+
+
+static ssize_t jsm_tty_baud_show(struct class_device *class_dev, char *buf)
+{
+	struct jsm_channel *ch;
+
+	if (class_dev) {
+		ch = class_get_devdata(class_dev);
+		if ( ch && (ch->ch_bd->state == BOARD_READY))
+		return snprintf(buf, PAGE_SIZE, "%d\n", ch->ch_old_baud);
+	}
+
+	return -EINVAL;
+}
+static CLASS_DEVICE_ATTR(baud, S_IRUGO, jsm_tty_baud_show, NULL);
+
+static ssize_t jsm_tty_msignals_show(struct class_device *class_dev, char *buf)
+{
+	struct jsm_channel *ch;
+
+	if (class_dev) {
+		ch = class_get_devdata(class_dev);
+		if ( ch && (ch->ch_bd->state == BOARD_READY))
+			if (ch->ch_open_count) {
+				return snprintf(buf, PAGE_SIZE, "%s %s %s %s %s %s\n",
+					(ch->ch_mostat & UART_MCR_RTS) ? "RTS" : "",
+					(ch->ch_mistat & UART_MSR_CTS) ? "CTS" : "",
+					(ch->ch_mostat & UART_MCR_DTR) ? "DTR" : "",
+					(ch->ch_mistat & UART_MSR_DSR) ? "DSR" : "",
+					(ch->ch_mistat & UART_MSR_DCD) ? "DCD" : "",
+					(ch->ch_mistat & UART_MSR_RI)  ? "RI"  : "");
+			}
+	}
+
+	return -EINVAL;
+}
+static CLASS_DEVICE_ATTR(msignals, S_IRUGO, jsm_tty_msignals_show, NULL);
+
+static ssize_t jsm_tty_iflag_show(struct class_device *class_dev, char *buf)
+{
+	struct jsm_channel *ch;
+
+	if (class_dev) {
+		ch = class_get_devdata(class_dev);
+		if ( ch && (ch->ch_bd->state == BOARD_READY))
+		return snprintf(buf, PAGE_SIZE, "%x\n", ch->ch_c_iflag);
+	}
+
+	return -EINVAL;
+}
+static CLASS_DEVICE_ATTR(iflag, S_IRUGO, jsm_tty_iflag_show, NULL);
+
+
+static ssize_t jsm_tty_cflag_show(struct class_device *class_dev, char *buf)
+{
+	struct jsm_channel *ch;
+
+	if (class_dev) {
+		ch = class_get_devdata(class_dev);
+		if ( ch && (ch->ch_bd->state == BOARD_READY))
+		return snprintf(buf, PAGE_SIZE, "%x\n", ch->ch_c_cflag);
+	}
+
+	return -EINVAL;
+}
+static CLASS_DEVICE_ATTR(cflag, S_IRUGO, jsm_tty_cflag_show, NULL);
+
+static ssize_t jsm_tty_oflag_show(struct class_device *class_dev, char *buf)
+{
+	struct jsm_channel *ch;
+
+	if (class_dev) {
+		ch = class_get_devdata(class_dev);
+		if ( ch && (ch->ch_bd->state == BOARD_READY))
+		return snprintf(buf, PAGE_SIZE, "%x\n", ch->ch_c_oflag);
+	}
+
+	return -EINVAL;
+}
+static CLASS_DEVICE_ATTR(oflag, S_IRUGO, jsm_tty_oflag_show, NULL);
+
+static ssize_t jsm_tty_lflag_show(struct class_device *class_dev, char *buf)
+{
+	struct jsm_channel *ch;
+
+	if (class_dev) {
+		ch = class_get_devdata(class_dev);
+		if ( ch && (ch->ch_bd->state == BOARD_READY))
+		return snprintf(buf, PAGE_SIZE, "%x\n", ch->ch_c_lflag);
+	}
+
+	return -EINVAL;
+}
+static CLASS_DEVICE_ATTR(lflag, S_IRUGO, jsm_tty_lflag_show, NULL);
+
+static ssize_t jsm_tty_rxcount_show(struct class_device *class_dev, char *buf)
+{
+	struct jsm_channel *ch;
+
+	if (class_dev) {
+		ch = class_get_devdata(class_dev);
+		if ( ch && (ch->ch_bd->state == BOARD_READY))
+		return snprintf(buf, PAGE_SIZE, "%ld\n", ch->ch_rxcount);
+	}
+
+	return -EINVAL;
+}
+static CLASS_DEVICE_ATTR(rxcount, S_IRUGO, jsm_tty_rxcount_show, NULL);
+
+static ssize_t jsm_tty_txcount_show(struct class_device *class_dev, char *buf)
+{
+	struct jsm_channel *ch;
+
+	if (class_dev) {
+		ch = class_get_devdata(class_dev);
+		if ( ch && (ch->ch_bd->state == BOARD_READY))
+		return snprintf(buf, PAGE_SIZE, "%ld\n", ch->ch_txcount);
+	}
+
+	return -EINVAL;
+}
+static CLASS_DEVICE_ATTR(txcount, S_IRUGO, jsm_tty_txcount_show, NULL);
+
+void jsm_tty_register_device(struct jsm_channel *ch, struct device *device)
+{
+	struct class_device *jsm_class_member;
+	dev_t dev;
+
+	dev = MKDEV(ch->ch_bd->jsm_serial_major,
+			ch->ch_portnum + ch->ch_bd->boardnum * 2);
+
+	jsm_class_member = class_simple_device_add(jsm_tty_class, dev, device,
+			"ttyn%d", ch->ch_portnum + ch->ch_bd->boardnum*2);
+
+	if (IS_ERR(jsm_class_member))
+		return;
+
+	class_set_devdata(jsm_class_member, ch);
+	class_device_create_file(jsm_class_member, &class_device_attr_state);
+	class_device_create_file(jsm_class_member, &class_device_attr_baud);
+	class_device_create_file(jsm_class_member, &class_device_attr_msignals);
+	class_device_create_file(jsm_class_member, &class_device_attr_iflag);
+	class_device_create_file(jsm_class_member, &class_device_attr_cflag);
+	class_device_create_file(jsm_class_member, &class_device_attr_oflag);
+	class_device_create_file(jsm_class_member, &class_device_attr_lflag);
+	class_device_create_file(jsm_class_member, &class_device_attr_rxcount);
+	class_device_create_file(jsm_class_member, &class_device_attr_txcount);
+}
+
+void jsm_tty_unregister_device(struct jsm_channel *ch)
+{
+
+	class_simple_device_remove(MKDEV(ch->ch_bd->jsm_serial_major, 
+					ch->ch_portnum + ch->ch_bd->boardnum * 2));
+}

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [ patch 4/7] drivers/serial/jsm: new serial device driver
  2005-03-07 22:46       ` Wen Xiong
@ 2005-03-08  6:44         ` Greg KH
  2005-03-08 18:55           ` Wen Xiong
  2005-03-09 16:11           ` [ patch 4/7] " Russell King
  0 siblings, 2 replies; 34+ messages in thread
From: Greg KH @ 2005-03-08  6:44 UTC (permalink / raw)
  To: Wen Xiong; +Cc: linux-kernel

On Mon, Mar 07, 2005 at 05:46:51PM -0500, Wen Xiong wrote:
> +static ssize_t jsm_driver_version_show(struct device_driver *ddp, char *buf)
> +{
> +	return snprintf(buf, PAGE_SIZE, "jsm_version: %s\n", JSM_VERSION);

Again, drop the "prefix:" from every sysfs file, it should not be there
(the data type is inferred by the name of the file, you do not have to
repeat it again.)

> +static ssize_t jsm_tty_state_show(struct class_device *class_dev, char *buf)
> +{
> +	struct jsm_channel *ch;
> +
> +	if (class_dev) {
> +		ch = class_get_devdata(class_dev);
> +		if ( ch && (ch->ch_bd->state == BOARD_READY))
> +			return snprintf(buf, PAGE_SIZE, "%s\n", ch->ch_open_count ? "Open" : "Closed");
> +	}
> +
> +	return -EINVAL;
> +}
> +static CLASS_DEVICE_ATTR(state, S_IRUGO, jsm_tty_state_show, NULL);

Who needs to know if a port is open or not?

> +static ssize_t jsm_tty_baud_show(struct class_device *class_dev, char *buf)
> +{
> +	struct jsm_channel *ch;
> +
> +	if (class_dev) {
> +		ch = class_get_devdata(class_dev);
> +		if ( ch && (ch->ch_bd->state == BOARD_READY))
> +		return snprintf(buf, PAGE_SIZE, "%d\n", ch->ch_old_baud);
> +	}
> +
> +	return -EINVAL;
> +}
> +static CLASS_DEVICE_ATTR(baud, S_IRUGO, jsm_tty_baud_show, NULL);

No, please delete these, and the other sysfs files that duplicate the
same info that you can get by using the standard Linux termios calls.
There is no need for them here.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [ patch 4/7] drivers/serial/jsm: new serial device driver
  2005-03-08  6:44         ` Greg KH
@ 2005-03-08 18:55           ` Wen Xiong
  2005-03-08 23:58             ` Greg KH
  2005-03-09 16:11           ` [ patch 4/7] " Russell King
  1 sibling, 1 reply; 34+ messages in thread
From: Wen Xiong @ 2005-03-08 18:55 UTC (permalink / raw)
  To: Greg KH; +Cc: Wen Xiong, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 1616 bytes --]

Greg KH wrote:

>On Mon, Mar 07, 2005 at 05:46:51PM -0500, Wen Xiong wrote:
>  
>
>>+static ssize_t jsm_driver_version_show(struct device_driver *ddp, char *buf)
>>+{
>>+	return snprintf(buf, PAGE_SIZE, "jsm_version: %s\n", JSM_VERSION);
>>    
>>
>
>Again, drop the "prefix:" from every sysfs file, it should not be there
>(the data type is inferred by the name of the file, you do not have to
>repeat it again.)
>
>  
>
>>+static ssize_t jsm_tty_state_show(struct class_device *class_dev, char *buf)
>>+{
>>+	struct jsm_channel *ch;
>>+
>>+	if (class_dev) {
>>+		ch = class_get_devdata(class_dev);
>>+		if ( ch && (ch->ch_bd->state == BOARD_READY))
>>+			return snprintf(buf, PAGE_SIZE, "%s\n", ch->ch_open_count ? "Open" : "Closed");
>>+	}
>>+
>>+	return -EINVAL;
>>+}
>>+static CLASS_DEVICE_ATTR(state, S_IRUGO, jsm_tty_state_show, NULL);
>>    
>>
>
>Who needs to know if a port is open or not?
>
>  
>
>>+static ssize_t jsm_tty_baud_show(struct class_device *class_dev, char *buf)
>>+{
>>+	struct jsm_channel *ch;
>>+
>>+	if (class_dev) {
>>+		ch = class_get_devdata(class_dev);
>>+		if ( ch && (ch->ch_bd->state == BOARD_READY))
>>+		return snprintf(buf, PAGE_SIZE, "%d\n", ch->ch_old_baud);
>>+	}
>>+
>>+	return -EINVAL;
>>+}
>>+static CLASS_DEVICE_ATTR(baud, S_IRUGO, jsm_tty_baud_show, NULL);
>>    
>>
>
>No, please delete these, and the other sysfs files that duplicate the
>same info that you can get by using the standard Linux termios calls.
>There is no need for them here.
>
>thanks,
>
>greg k-h
>
>  
>
Hi Greg,
        Removed some codes in jsm_sysfs.c.

Thanks for your reviewing the code.
wendy

[-- Attachment #2: patch4.jasmine --]
[-- Type: text/plain, Size: 3790 bytes --]

diff -Nuar linux-2.6.11.org/drivers/serial/jsm/jsm_sysfs.c linux-2.6.11.new/drivers/serial/jsm/jsm_sysfs.c
--- linux-2.6.11.org/drivers/serial/jsm/jsm_sysfs.c	1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.11.new/drivers/serial/jsm/jsm_sysfs.c	2005-03-08 12:19:57.498967368 -0600
@@ -0,0 +1,98 @@
+/************************************************************************
+ * Copyright 2003 Digi International (www.digi.com)
+ *
+ * Copyright (C) 2004 IBM Corporation. All rights reserved.
+ *
+ * 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, or (at your option)
+ * any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; without even the 
+ * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License 
+ * along with this program; if not, write to the Free Software 
+ * Foundation, Inc., 59 * Temple Place - Suite 330, Boston,
+ * MA  02111-1307, USA.
+ *
+ * Contact Information:
+ * Scott H Kilau <Scott_Kilau@digi.com>
+ * Wendy Xiong   <wendyx@us.ltcfwd.linux.ibm.com>
+ *
+ ***********************************************************************/
+#include <linux/device.h>
+#include <linux/serial_reg.h>
+
+#include "jsm_driver.h"
+
+int jsm_total_boardnum(void)
+{
+	struct list_head *tmp;
+	struct jsm_board *cur_board_entry;
+	int adapter_count = 0;
+	u64 lock_flags;
+
+	spin_lock_irqsave(&jsm_board_head_lock, lock_flags);
+	list_for_each(tmp, &jsm_board_head) {
+		cur_board_entry =
+			list_entry(tmp, struct jsm_board,
+			jsm_board_entry);
+		adapter_count++;
+	}
+	spin_unlock_irqrestore(&jsm_board_head_lock, lock_flags);
+
+	return adapter_count;
+}
+
+static ssize_t jsm_driver_version_show(struct device_driver *ddp, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%s\n", JSM_VERSION);
+}
+static DRIVER_ATTR(version, S_IRUSR, jsm_driver_version_show, NULL);
+
+static ssize_t jsm_driver_boards_show(struct device_driver *ddp, char *buf)
+{
+	int adapter_count = 0;
+	adapter_count = jsm_total_boardnum();
+	return snprintf(buf, PAGE_SIZE, "%d\n", adapter_count);
+}
+static DRIVER_ATTR(boards, S_IRUSR, jsm_driver_boards_show, NULL);
+
+static ssize_t jsm_driver_state_show(struct device_driver *ddp, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%s\n", jsm_driver_state_text[jsm_driver_state]);
+}
+static DRIVER_ATTR(state, S_IRUSR, jsm_driver_state_show, NULL);
+
+static ssize_t jsm_driver_debug_show(struct device_driver *ddp, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "0x%x\n", jsm_debug);
+}
+static DRIVER_ATTR(debug, S_IRUSR, jsm_driver_debug_show, NULL);
+
+static ssize_t jsm_driver_rawreadok_show(struct device_driver *ddp, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "0x%x\n", jsm_rawreadok);
+}
+static DRIVER_ATTR(rawreadok, S_IRUSR, jsm_driver_rawreadok_show, NULL);
+
+void jsm_create_driver_sysfiles(struct device_driver *driverfs)
+{
+	driver_create_file(driverfs, &driver_attr_version);
+	driver_create_file(driverfs, &driver_attr_boards);
+	driver_create_file(driverfs, &driver_attr_debug);
+	driver_create_file(driverfs, &driver_attr_rawreadok); 
+	driver_create_file(driverfs, &driver_attr_state);
+}
+
+void jsm_remove_driver_sysfiles(struct device_driver  *driverfs)
+{
+	driver_remove_file(driverfs, &driver_attr_version);
+	driver_remove_file(driverfs, &driver_attr_boards);
+	driver_remove_file(driverfs, &driver_attr_debug);
+	driver_remove_file(driverfs, &driver_attr_rawreadok);
+	driver_remove_file(driverfs, &driver_attr_state);
+}

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [ patch 4/7] drivers/serial/jsm: new serial device driver
  2005-03-08 18:55           ` Wen Xiong
@ 2005-03-08 23:58             ` Greg KH
  2005-03-09 15:47               ` Wen Xiong
  0 siblings, 1 reply; 34+ messages in thread
From: Greg KH @ 2005-03-08 23:58 UTC (permalink / raw)
  To: Wen Xiong; +Cc: linux-kernel

On Tue, Mar 08, 2005 at 01:55:33PM -0500, Wen Xiong wrote:
> +static ssize_t jsm_driver_boards_show(struct device_driver *ddp, char *buf)
> +{
> +	int adapter_count = 0;
> +	adapter_count = jsm_total_boardnum();
> +	return snprintf(buf, PAGE_SIZE, "%d\n", adapter_count);
> +}
> +static DRIVER_ATTR(boards, S_IRUSR, jsm_driver_boards_show, NULL);

Why is this file even needed?  You can just look at the number of sysfs
directories attached to this device, right?

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [ patch 4/7] drivers/serial/jsm: new serial device driver
  2005-03-08 23:58             ` Greg KH
@ 2005-03-09 15:47               ` Wen Xiong
  2005-03-09 16:35                 ` Greg KH
  0 siblings, 1 reply; 34+ messages in thread
From: Wen Xiong @ 2005-03-09 15:47 UTC (permalink / raw)
  To: Greg KH; +Cc: Wen Xiong, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 728 bytes --]

Greg KH wrote:

>On Tue, Mar 08, 2005 at 01:55:33PM -0500, Wen Xiong wrote:
>  
>
>>+static ssize_t jsm_driver_boards_show(struct device_driver *ddp, char *buf)
>>+{
>>+	int adapter_count = 0;
>>+	adapter_count = jsm_total_boardnum();
>>+	return snprintf(buf, PAGE_SIZE, "%d\n", adapter_count);
>>+}
>>+static DRIVER_ATTR(boards, S_IRUSR, jsm_driver_boards_show, NULL);
>>    
>>
>
>Why is this file even needed?  You can just look at the number of sysfs
>directories attached to this device, right?
>
>thanks,
>
>greg k-h
>
>  
>
We can find out the number of adapters in several ways in linux. But we 
are not sure the end customer really know how to find out.
Anyway, attatched the new patch4.jasmine for you.

Thanks,
wendy

[-- Attachment #2: patch4.jasmine --]
[-- Type: text/plain, Size: 2977 bytes --]

diff -Nuar linux-2.6.11.org/drivers/serial/jsm/jsm_sysfs.c linux-2.6.11.new/drivers/serial/jsm/jsm_sysfs.c
--- linux-2.6.11.org/drivers/serial/jsm/jsm_sysfs.c	1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.11.new/drivers/serial/jsm/jsm_sysfs.c	2005-03-09 09:37:00.520967448 -0600
@@ -0,0 +1,69 @@
+/************************************************************************
+ * Copyright 2003 Digi International (www.digi.com)
+ *
+ * Copyright (C) 2004 IBM Corporation. All rights reserved.
+ *
+ * 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, or (at your option)
+ * any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; without even the 
+ * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License 
+ * along with this program; if not, write to the Free Software 
+ * Foundation, Inc., 59 * Temple Place - Suite 330, Boston,
+ * MA  02111-1307, USA.
+ *
+ * Contact Information:
+ * Scott H Kilau <Scott_Kilau@digi.com>
+ * Wendy Xiong   <wendyx@us.ltcfwd.linux.ibm.com>
+ *
+ ***********************************************************************/
+#include <linux/device.h>
+#include <linux/serial_reg.h>
+
+#include "jsm_driver.h"
+
+static ssize_t jsm_driver_version_show(struct device_driver *ddp, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%s\n", JSM_VERSION);
+}
+static DRIVER_ATTR(version, S_IRUSR, jsm_driver_version_show, NULL);
+
+static ssize_t jsm_driver_state_show(struct device_driver *ddp, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%s\n", jsm_driver_state_text[jsm_driver_state]);
+}
+static DRIVER_ATTR(state, S_IRUSR, jsm_driver_state_show, NULL);
+
+static ssize_t jsm_driver_debug_show(struct device_driver *ddp, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "0x%x\n", jsm_debug);
+}
+static DRIVER_ATTR(debug, S_IRUSR, jsm_driver_debug_show, NULL);
+
+static ssize_t jsm_driver_rawreadok_show(struct device_driver *ddp, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "0x%x\n", jsm_rawreadok);
+}
+static DRIVER_ATTR(rawreadok, S_IRUSR, jsm_driver_rawreadok_show, NULL);
+
+void jsm_create_driver_sysfiles(struct device_driver *driverfs)
+{
+	driver_create_file(driverfs, &driver_attr_version);
+	driver_create_file(driverfs, &driver_attr_debug);
+	driver_create_file(driverfs, &driver_attr_rawreadok); 
+	driver_create_file(driverfs, &driver_attr_state);
+}
+
+void jsm_remove_driver_sysfiles(struct device_driver  *driverfs)
+{
+	driver_remove_file(driverfs, &driver_attr_version);
+	driver_remove_file(driverfs, &driver_attr_debug);
+	driver_remove_file(driverfs, &driver_attr_rawreadok);
+	driver_remove_file(driverfs, &driver_attr_state);
+}

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [ patch 4/7] drivers/serial/jsm: new serial device driver
  2005-03-08  6:44         ` Greg KH
  2005-03-08 18:55           ` Wen Xiong
@ 2005-03-09 16:11           ` Russell King
  1 sibling, 0 replies; 34+ messages in thread
From: Russell King @ 2005-03-09 16:11 UTC (permalink / raw)
  To: Greg KH; +Cc: Wen Xiong, linux-kernel

On Mon, Mar 07, 2005 at 10:44:25PM -0800, Greg KH wrote:
> On Mon, Mar 07, 2005 at 05:46:51PM -0500, Wen Xiong wrote:
> > +static ssize_t jsm_tty_baud_show(struct class_device *class_dev, char *buf)
> > +{
> > +	struct jsm_channel *ch;
> > +
> > +	if (class_dev) {
> > +		ch = class_get_devdata(class_dev);
> > +		if ( ch && (ch->ch_bd->state == BOARD_READY))
> > +		return snprintf(buf, PAGE_SIZE, "%d\n", ch->ch_old_baud);
> > +	}
> > +
> > +	return -EINVAL;
> > +}
> > +static CLASS_DEVICE_ATTR(baud, S_IRUGO, jsm_tty_baud_show, NULL);
> 
> No, please delete these, and the other sysfs files that duplicate the
> same info that you can get by using the standard Linux termios calls.
> There is no need for them here.

Greg, there's several other points about why the above is Bad(tm).

"class_dev" will always be non-null.

Note that this (and similar code) is racy.  Consider what happens when
the class device is removed (and the class devdata is NULL'd or kfree'd)
while another process on another CPU reads from one of these sysfs files.
*BANG*.

Also, if a class device attribute method is going to access data outside
the same allocation which the class device is a part of, you *absolutely*
*must* have some form of locking.

Also note that the formatting of these snippets of code is abismal.
There is a missing tab which makes it very non-readable.

With all of the above comments, it should be something like:

+static ssize_t jsm_tty_baud_show(struct class_device *class_dev, char *buf)
+{
+	struct jsm_channel *ch;
+	int ret = -EINVAL;
+
+	down(&some_lock);
+	ch = class_get_devdata(class_dev);
+	if (ch && (ch->ch_bd->state == BOARD_READY))
+		ret = snprintf(buf, PAGE_SIZE, "%d\n", ch->ch_old_baud);
+	up(&some_lock);
+
+	return ret;
+}
+static CLASS_DEVICE_ATTR(baud, S_IRUGO, jsm_tty_baud_show, NULL);

where "some_lock" is also taken in the device unregistration path, at
the point where the class devdata is NULL'd out.  (which the driver is
also missing.)

-- 
Russell King
 Linux kernel    2.6 ARM Linux   - http://www.arm.linux.org.uk/
 maintainer of:  2.6 Serial core

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [ patch 4/7] drivers/serial/jsm: new serial device driver
  2005-03-09 15:47               ` Wen Xiong
@ 2005-03-09 16:35                 ` Greg KH
  2005-03-09 17:18                   ` Wen Xiong
  0 siblings, 1 reply; 34+ messages in thread
From: Greg KH @ 2005-03-09 16:35 UTC (permalink / raw)
  To: Wen Xiong; +Cc: linux-kernel

On Wed, Mar 09, 2005 at 10:47:22AM -0500, Wen Xiong wrote:
> +static ssize_t jsm_driver_debug_show(struct device_driver *ddp, char *buf)
> +{
> +	return snprintf(buf, PAGE_SIZE, "0x%x\n", jsm_debug);
> +}
> +static DRIVER_ATTR(debug, S_IRUSR, jsm_driver_debug_show, NULL);

Should just be a module paramater, right?  So you can drop this too...

This file is getting quite small now :)

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [ patch 4/7] drivers/serial/jsm: new serial device driver
  2005-03-09 16:35                 ` Greg KH
@ 2005-03-09 17:18                   ` Wen Xiong
  2005-03-09 18:58                     ` Greg KH
  0 siblings, 1 reply; 34+ messages in thread
From: Wen Xiong @ 2005-03-09 17:18 UTC (permalink / raw)
  To: Greg KH; +Cc: Wen Xiong, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 568 bytes --]

Greg KH wrote:

>On Wed, Mar 09, 2005 at 10:47:22AM -0500, Wen Xiong wrote:
>  
>
>>+static ssize_t jsm_driver_debug_show(struct device_driver *ddp, char *buf)
>>+{
>>+	return snprintf(buf, PAGE_SIZE, "0x%x\n", jsm_debug);
>>+}
>>+static DRIVER_ATTR(debug, S_IRUSR, jsm_driver_debug_show, NULL);
>>    
>>
>
>Should just be a module paramater, right?  So you can drop this too...
>
>This file is getting quite small now :)
>
>thanks,
>
>greg k-h
>
>  
>
If I removed two module paramaters, only two files left: version and state.
 Removed all of them?

Thanks,
wendy


[-- Attachment #2: patch4.jasmine --]
[-- Type: text/plain, Size: 2332 bytes --]

diff -Nuar linux-2.6.11.org/drivers/serial/jsm/jsm_sysfs.c linux-2.6.11.new/drivers/serial/jsm/jsm_sysfs.c
--- linux-2.6.11.org/drivers/serial/jsm/jsm_sysfs.c	1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.11.new/drivers/serial/jsm/jsm_sysfs.c	2005-03-09 11:17:37.055947624 -0600
@@ -0,0 +1,53 @@
+/************************************************************************
+ * Copyright 2003 Digi International (www.digi.com)
+ *
+ * Copyright (C) 2004 IBM Corporation. All rights reserved.
+ *
+ * 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, or (at your option)
+ * any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; without even the 
+ * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License 
+ * along with this program; if not, write to the Free Software 
+ * Foundation, Inc., 59 * Temple Place - Suite 330, Boston,
+ * MA  02111-1307, USA.
+ *
+ * Contact Information:
+ * Scott H Kilau <Scott_Kilau@digi.com>
+ * Wendy Xiong   <wendyx@us.ltcfwd.linux.ibm.com>
+ *
+ ***********************************************************************/
+#include <linux/device.h>
+#include <linux/serial_reg.h>
+
+#include "jsm_driver.h"
+
+static ssize_t jsm_driver_version_show(struct device_driver *ddp, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%s\n", JSM_VERSION);
+}
+static DRIVER_ATTR(version, S_IRUSR, jsm_driver_version_show, NULL);
+
+static ssize_t jsm_driver_state_show(struct device_driver *ddp, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%s\n", jsm_driver_state_text[jsm_driver_state]);
+}
+static DRIVER_ATTR(state, S_IRUSR, jsm_driver_state_show, NULL);
+
+void jsm_create_driver_sysfiles(struct device_driver *driverfs)
+{
+	driver_create_file(driverfs, &driver_attr_version);
+	driver_create_file(driverfs, &driver_attr_state);
+}
+
+void jsm_remove_driver_sysfiles(struct device_driver  *driverfs)
+{
+	driver_remove_file(driverfs, &driver_attr_version);
+	driver_remove_file(driverfs, &driver_attr_state);
+}

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [ patch 4/7] drivers/serial/jsm: new serial device driver
  2005-03-09 17:18                   ` Wen Xiong
@ 2005-03-09 18:58                     ` Greg KH
  2005-03-11 15:29                       ` [ patch 1/5] " Wen Xiong
                                         ` (4 more replies)
  0 siblings, 5 replies; 34+ messages in thread
From: Greg KH @ 2005-03-09 18:58 UTC (permalink / raw)
  To: Wen Xiong; +Cc: linux-kernel

On Wed, Mar 09, 2005 at 12:18:21PM -0500, Wen Xiong wrote:
> Greg KH wrote:
> 
> >On Wed, Mar 09, 2005 at 10:47:22AM -0500, Wen Xiong wrote:
> > 
> >
> >>+static ssize_t jsm_driver_debug_show(struct device_driver *ddp, char 
> >>*buf)
> >>+{
> >>+	return snprintf(buf, PAGE_SIZE, "0x%x\n", jsm_debug);
> >>+}
> >>+static DRIVER_ATTR(debug, S_IRUSR, jsm_driver_debug_show, NULL);
> >>   
> >>
> >
> >Should just be a module paramater, right?  So you can drop this too...
> >
> >This file is getting quite small now :)
> >
> If I removed two module paramaters, only two files left: version and state.
> Removed all of them?

Move them to a different file?

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [ patch 1/5] drivers/serial/jsm: new serial device driver
  2005-03-09 18:58                     ` Greg KH
@ 2005-03-11 15:29                       ` Wen Xiong
  2005-03-12 13:06                         ` Domen Puncer
  2005-03-11 15:32                       ` [ patch 2/5] " Wen Xiong
                                         ` (3 subsequent siblings)
  4 siblings, 1 reply; 34+ messages in thread
From: Wen Xiong @ 2005-03-11 15:29 UTC (permalink / raw)
  To: Greg KH; +Cc: Wen Xiong, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 1000 bytes --]

Greg KH wrote:

>On Wed, Mar 09, 2005 at 12:18:21PM -0500, Wen Xiong wrote:
>  
>
>>Greg KH wrote:
>>
>>    
>>
>>>On Wed, Mar 09, 2005 at 10:47:22AM -0500, Wen Xiong wrote:
>>>
>>>
>>>      
>>>
>>>>+static ssize_t jsm_driver_debug_show(struct device_driver *ddp, char 
>>>>*buf)
>>>>+{
>>>>+	return snprintf(buf, PAGE_SIZE, "0x%x\n", jsm_debug);
>>>>+}
>>>>+static DRIVER_ATTR(debug, S_IRUSR, jsm_driver_debug_show, NULL);
>>>>  
>>>>
>>>>        
>>>>
>>>Should just be a module paramater, right?  So you can drop this too...
>>>
>>>This file is getting quite small now :)
>>>
>>>      
>>>
>>If I removed two module paramaters, only two files left: version and state.
>>Removed all of them?
>>    
>>
>
>Move them to a different file?
>  
>
Hi All,

Based on the second round comments and discussion, I cleared up the 
header files, merged /sysfs to another file and removed new ioctls.
I am going to send out whole patches again(totally 5 patches at the time).

Thanks for all your help!

>  
>

[-- Attachment #2: patch1.jasmine --]
[-- Type: text/plain, Size: 10686 bytes --]

diff -Nuar linux-2.6.11.org/drivers/serial/jsm/jsm_driver.c linux-2.6.11.new/drivers/serial/jsm/jsm_driver.c
--- linux-2.6.11.org/drivers/serial/jsm/jsm_driver.c	1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.11.new/drivers/serial/jsm/jsm_driver.c	2005-03-10 16:34:00.107901984 -0600
@@ -0,0 +1,406 @@
+/************************************************************************
+ * Copyright 2003 Digi International (www.digi.com)
+ *
+ * Copyright (C) 2004 IBM Corporation. All rights reserved.
+ *
+ * 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, or (at your option)
+ * any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; without even the 
+ * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License 
+ * along with this program; if not, write to the Free Software 
+ * Foundation, Inc., 59 * Temple Place - Suite 330, Boston,
+ * MA  02111-1307, USA.
+ *
+ * Contact Information:
+ * Scott H Kilau <Scott_Kilau@digi.com>
+ * Wendy Xiong   <wendyx@us.ltcfwd.linux.ibm.com>
+ *
+ ***********************************************************************/
+#include <linux/moduleparam.h>
+#include <linux/pci.h>
+
+#include "jsm.h"
+
+MODULE_AUTHOR("Digi International, http://www.digi.com");
+MODULE_DESCRIPTION("Driver for the Digi International Neo PCI based product line");
+MODULE_SUPPORTED_DEVICE("jsm");
+
+#define JSM_DRIVER_NAME "jsm"
+#define NR_PORTS	32 
+#define JSM_MINOR_START	0 
+
+struct uart_driver jsm_uart_driver = {
+	.owner		= THIS_MODULE,
+	.driver_name	= JSM_DRIVER_NAME,
+	.dev_name	= "ttyn", 
+	.major		= 253,
+	.minor		= JSM_MINOR_START, 
+	.nr		= NR_PORTS,
+	.cons		= NULL,
+};
+
+int jsm_debug;
+int jsm_rawreadok;
+module_param(jsm_debug, int, 0);
+module_param(jsm_rawreadok, int, 1);
+MODULE_PARM_DESC(jsm_debug, "Driver debugging level");
+MODULE_PARM_DESC(jsm_rawreadok, "Bypass flip buffers on input");
+
+/*
+ * Globals
+ */
+int		jsm_driver_state = DRIVER_INITIALIZED;
+spinlock_t	jsm_board_head_lock = SPIN_LOCK_UNLOCKED;
+LIST_HEAD(jsm_board_head);
+
+static struct pci_device_id jsm_pci_tbl[] = {
+	{ PCI_DEVICE (PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_NEO_2DB9),	0,	0,	0 },
+	{ PCI_DEVICE (PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_NEO_2DB9PRI),	0,	0,	1 },
+	{ PCI_DEVICE (PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_NEO_2RJ45),	0,	0,	2 },
+	{ PCI_DEVICE (PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_NEO_2RJ45PRI),	0,	0,	3 },
+	{ 0,}						/* 0 terminated list. */
+};
+MODULE_DEVICE_TABLE(pci, jsm_pci_tbl);
+
+static struct board_id jsm_Ids[] = {	
+	{ PCI_DEVICE_NEO_2DB9_PCI_NAME,		2 },
+	{ PCI_DEVICE_NEO_2DB9PRI_PCI_NAME,	2 },
+	{ PCI_DEVICE_NEO_2RJ45_PCI_NAME,	2 },
+	{ PCI_DEVICE_NEO_2RJ45PRI_PCI_NAME,	2 },
+	{ NULL,					0 }
+};
+
+char *jsm_driver_state_text[] = {
+	"Driver Initialized",
+	"Driver Ready."
+};
+
+static int jsm_finalize_board_init(struct jsm_board *brd) 
+{
+	int rc = 0;
+
+	jsm_printk(INIT, INFO, &brd->pci_dev, "start\n");
+
+	if (brd->irq) {
+		rc = request_irq(brd->irq, brd->bd_ops->intr, SA_INTERRUPT|SA_SHIRQ, "JSM", brd);
+
+		if (rc) {
+			printk(KERN_WARNING "Failed to hook IRQ %d\n",brd->irq);
+			brd->state = BOARD_FAILED;
+			brd->dpastatus = BD_NOFEP;
+			rc = -ENODEV;
+		} else
+			jsm_printk(INIT, INFO, &brd->pci_dev,
+				"Requested and received usage of IRQ %d\n", brd->irq);
+	}
+	return rc;
+}
+
+/*
+ * jsm_found_board()
+ *
+ * A board has been found, init it.
+ */
+static int jsm_found_board(struct pci_dev *pdev, int id)
+{
+	struct jsm_board *brd;
+	int i = 0;
+	int rc = 0;
+	struct list_head *tmp;
+	struct jsm_board *cur_board_entry;
+	unsigned long lock_flags;
+	int adapter_count = 0;
+
+	brd = (struct jsm_board *)kmalloc(sizeof(struct jsm_board), GFP_KERNEL);
+	if (!brd) {
+		dev_err(&pdev->dev, "memory allocation for board structure failed\n");
+		return -ENOMEM;
+	}
+	memset(brd, 0, sizeof(struct jsm_board));
+
+	spin_lock_irqsave(&jsm_board_head_lock, lock_flags);
+	list_for_each(tmp, &jsm_board_head) {
+		cur_board_entry = 
+			list_entry(tmp, struct jsm_board,
+				jsm_board_entry);
+		if (cur_board_entry->boardnum != adapter_count) {
+			break;
+		}
+		adapter_count++;
+	}
+
+	list_add_tail(&brd->jsm_board_entry, &jsm_board_head);
+	spin_unlock_irqrestore(&jsm_board_head_lock, lock_flags);
+
+	/* store the info for the board we've found */
+	brd->boardnum = adapter_count;
+	brd->pci_dev = pdev;
+	brd->name = jsm_Ids[id].name;
+	brd->maxports = jsm_Ids[id].maxports;
+	brd->dpastatus = BD_NOFEP;
+	init_waitqueue_head(&brd->state_wait);
+
+	spin_lock_init(&brd->bd_lock);
+	spin_lock_init(&brd->bd_intr_lock);
+
+	brd->state = BOARD_FOUND;
+
+	for (i = 0; i < brd->maxports; i++) 
+		brd->channels[i] = NULL;
+
+	/* store which revision we have */
+	pci_read_config_byte(pdev, PCI_REVISION_ID, &brd->rev);
+
+	brd->irq = pdev->irq;
+
+	switch(brd->pci_dev->device) {
+
+	case PCI_DEVICE_ID_NEO_2DB9:
+	case PCI_DEVICE_ID_NEO_2DB9PRI:
+	case PCI_DEVICE_ID_NEO_2RJ45:
+	case PCI_DEVICE_ID_NEO_2RJ45PRI:
+
+		/*
+		 * This chip is set up 100% when we get to it.
+		 * No need to enable global interrupts or anything. 
+		 */
+		brd->dpatype = T_NEO | T_PCIBUS;
+
+		jsm_printk(INIT, INFO, &brd->pci_dev,
+			"jsm_found_board - NEO adapter\n");
+
+		/* get the PCI Base Address Registers */
+		brd->membase	= pci_resource_start(pdev, 0);
+		brd->membase_end = pci_resource_end(pdev, 0);
+
+		if (brd->membase & 1)
+			brd->membase &= ~3;
+		else
+			brd->membase &= ~15;
+
+		/* Assign the board_ops struct */
+		brd->bd_ops = &jsm_neo_ops;
+
+		brd->bd_uart_offset = 0x200;
+		brd->bd_dividend = 921600;
+
+		brd->re_map_membase = ioremap(brd->membase, 0x1000);
+		jsm_printk(INIT, INFO, &brd->pci_dev,
+			"remapped mem: 0x%p\n", brd->re_map_membase);
+		if (!brd->re_map_membase) {
+			kfree(brd);
+			dev_err(&pdev->dev, "card has no PCI Memory resources, failing board.\n");
+			return -ENOMEM;
+		}
+		break;
+
+	default:
+		dev_err(&pdev->dev, "Did not find any compatible Neo or Classic PCI boards in system.\n");
+		kfree(brd);
+		return -ENXIO;
+	}
+
+	/*
+	 * Do tty device initialization.
+	 */
+	rc = jsm_finalize_board_init(brd);
+	if (rc < 0) {
+		dev_err(&pdev->dev, "Can't finalize board init (%d)\n", rc);
+		brd->state = BOARD_FAILED;
+		brd->dpastatus = BD_NOFEP;
+		goto failed;
+	}
+
+	rc = jsm_tty_init(brd);
+	if (rc < 0) {
+		dev_err(&pdev->dev, "Can't init tty devices (%d)\n", rc);
+		brd->state = BOARD_FAILED;
+		brd->dpastatus = BD_NOFEP;
+		free_irq(brd->irq, brd);
+		goto failed;
+	}
+
+	rc = jsm_uart_port_init(brd);
+	if (rc < 0) {
+		free_irq(brd->irq, brd);
+		goto failed;
+	}
+
+	brd->state = BOARD_READY;
+	brd->dpastatus = BD_RUNNING;
+
+	/* Log the information about the board */
+	dev_info(&pdev->dev, "board %d: %s (rev %d), irq %d\n",adapter_count, brd->name, brd->rev, brd->irq);
+
+	/*
+	 * allocate flip buffer for board.
+	 *
+	 * Okay to malloc with GFP_KERNEL, we are not at interrupt
+	 * context, and there are no locks held.
+	 */
+	brd->flipbuf = kmalloc(MYFLIPLEN, GFP_KERNEL);
+	if (!brd->flipbuf) {
+		dev_err(&pdev->dev, "memory allocation for flipbuf failed\n");
+		free_irq(brd->irq, brd);
+		kfree(brd);
+		iounmap((void *) brd->re_map_membase);
+		return -ENOMEM;
+	}
+	memset(brd->flipbuf, 0, MYFLIPLEN);
+
+	jsm_create_driver_sysfiles(pdev->dev.driver);
+
+	wake_up_interruptible(&brd->state_wait);
+	return 0;
+
+failed:
+	kfree(brd);
+	iounmap((void *) brd->re_map_membase);
+	return -ENXIO;
+}
+
+/* returns count (>= 0), or negative on error */
+static int jsm_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+	int rc;
+
+	rc = pci_enable_device(pdev);
+	if (rc) {
+		dev_err(&pdev->dev, "Device enable FAILED\n");
+		return rc;
+	} 
+
+	if ((rc = pci_request_regions(pdev, "jsm"))) {
+	dev_err(&pdev->dev, "pci_request_region FAILED\n");
+		pci_disable_device(pdev);
+		return rc;
+	}
+
+	if ((rc = jsm_found_board(pdev, ent->driver_data))) {
+		dev_err(&pdev->dev, "jsm_found_board FAILED\n");
+		pci_release_regions(pdev);
+		pci_disable_device(pdev);
+	 	return rc;
+	}
+	return rc;
+}
+
+
+/*
+ * jsm_cleanup_board()
+ *
+ * Free all the memory associated with a board
+ */
+static void jsm_cleanup_board(struct jsm_board *brd)
+{
+	int i = 0;
+
+	free_irq(brd->irq, brd);
+	iounmap(brd->re_map_membase);
+
+	/* Free all allocated channels structs */
+	for (i = 0; i < brd->maxports; i++) {
+		if (brd->channels[i]) {
+			if (brd->channels[i]->ch_rqueue)
+				kfree(brd->channels[i]->ch_rqueue);
+			if (brd->channels[i]->ch_equeue)
+				kfree(brd->channels[i]->ch_equeue);
+			if (brd->channels[i]->ch_wqueue)
+				kfree(brd->channels[i]->ch_wqueue);
+
+			kfree(brd->channels[i]);
+			brd->channels[i] = NULL;
+		}
+	}
+
+	pci_release_regions(brd->pci_dev);
+	pci_disable_device(brd->pci_dev);
+	kfree(brd->flipbuf);
+	kfree(brd);
+}
+
+static void jsm_remove_one(struct pci_dev *dev)
+{
+	unsigned long lock_flags;
+	struct list_head *tmp;
+	struct jsm_board *brd;
+
+	spin_lock_irqsave(&jsm_board_head_lock, lock_flags);
+	list_for_each(tmp, &jsm_board_head) {
+		brd = list_entry(tmp, struct jsm_board,
+					jsm_board_entry);
+		if ( brd != NULL && brd->pci_dev == dev) {
+			jsm_remove_uart_port(brd);
+			jsm_cleanup_board(brd);
+			list_del(&brd->jsm_board_entry);
+			break;
+		}
+	}
+	spin_unlock_irqrestore(&jsm_board_head_lock, lock_flags);
+	return;
+}
+
+struct pci_driver jsm_driver = {
+	.name		= "jsm",
+	.probe		= jsm_init_one,
+	.id_table	= jsm_pci_tbl,
+	.remove		= __devexit_p(jsm_remove_one),
+};
+
+/*
+ * jsm_init_module()
+ *
+ * Module load.  This is where it all starts.
+ */
+static int __init
+jsm_init_module(void)
+{
+	int rc = 0;
+
+	printk(KERN_INFO "%s, Digi International Part Number %s\n",
+			JSM_VERSION, JSM_VERSION);
+
+	/*
+	 * Initialize global stuff
+	 */
+
+	rc = uart_register_driver(&jsm_uart_driver);
+	if (rc < 0) {
+		return rc;
+	}
+
+	rc = pci_register_driver(&jsm_driver);
+	if (rc < 0) {
+		uart_unregister_driver(&jsm_uart_driver);
+		return rc;
+	}
+	jsm_driver_state = DRIVER_READY;
+
+	return rc;
+}
+
+module_init(jsm_init_module);
+
+/*
+ * jsm_exit_module()
+ *
+ * Module unload.  This is where it all ends.
+ */
+static void __exit
+jsm_exit_module(void)
+{
+	jsm_remove_driver_sysfiles(&(jsm_driver.driver));
+
+	pci_unregister_driver(&jsm_driver);
+
+	uart_unregister_driver(&jsm_uart_driver);
+}
+module_exit(jsm_exit_module);
+MODULE_LICENSE("GPL");

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [ patch 2/5] drivers/serial/jsm: new serial device driver
  2005-03-09 18:58                     ` Greg KH
  2005-03-11 15:29                       ` [ patch 1/5] " Wen Xiong
@ 2005-03-11 15:32                       ` Wen Xiong
  2005-03-30 14:55                         ` Russell King
  2005-03-11 15:38                       ` [ patch 3/5] " Wen Xiong
                                         ` (2 subsequent siblings)
  4 siblings, 1 reply; 34+ messages in thread
From: Wen Xiong @ 2005-03-11 15:32 UTC (permalink / raw)
  To: Greg KH; +Cc: Wen Xiong, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 110 bytes --]

The second patch for new jsm serial device driver.

Signed-off-by: Wen Xiong <wendyx@us.ltcfwd.linux.ibm.com>

[-- Attachment #2: patch2.jasmine --]
[-- Type: text/plain, Size: 27862 bytes --]

diff -Nuar linux-2.6.11.org/drivers/serial/jsm/jsm_tty.c linux-2.6.11.new/drivers/serial/jsm/jsm_tty.c
--- linux-2.6.11.org/drivers/serial/jsm/jsm_tty.c	1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.11.new/drivers/serial/jsm/jsm_tty.c	2005-03-10 16:34:37.342965976 -0600
@@ -0,0 +1,1043 @@
+/************************************************************************
+ * Copyright 2003 Digi International (www.digi.com)
+ *
+ * Copyright (C) 2004 IBM Corporation. All rights reserved.
+ *
+ * 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, or (at your option)
+ * any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; without even the 
+ * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License 
+ * along with this program; if not, write to the Free Software 
+ * Foundation, Inc., 59 * Temple Place - Suite 330, Boston,
+ * MA  02111-1307, USA.
+ *
+ * Contact Information:
+ * Scott H Kilau <Scott_Kilau@digi.com>
+ * Wendy Xiong   <wendyx@us.ltcfwd.linux.ibm.com>
+ *
+ ***********************************************************************/
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial_reg.h>
+#include <linux/delay.h>	/* For udelay */
+#include <linux/pci.h>	
+
+#include "jsm.h"
+
+static inline int jsm_get_mstat(struct jsm_channel *ch)
+{
+	unsigned char mstat;
+	unsigned char result;
+
+	jsm_printk(IOCTL, INFO, &ch->ch_bd->pci_dev, "start\n");
+
+	mstat = (ch->ch_mostat | ch->ch_mistat);
+
+	result = 0;
+
+	if (mstat & UART_MCR_DTR)
+		result |= TIOCM_DTR;
+	if (mstat & UART_MCR_RTS)
+		result |= TIOCM_RTS;
+	if (mstat & UART_MSR_CTS)
+		result |= TIOCM_CTS;
+	if (mstat & UART_MSR_DSR)
+		result |= TIOCM_DSR;
+	if (mstat & UART_MSR_RI)
+		result |= TIOCM_RI;
+	if (mstat & UART_MSR_DCD)
+		result |= TIOCM_CD;
+
+	jsm_printk(IOCTL, INFO, &ch->ch_bd->pci_dev, "finish\n");
+	return result;
+}
+
+static unsigned int jsm_tty_tx_empty(struct uart_port *port)
+{
+	return TIOCSER_TEMT;
+}
+
+/*
+ * Return modem signals to ld.
+ */
+static unsigned int jsm_tty_get_mctrl(struct uart_port *port)
+{
+	int result;
+	struct jsm_channel *channel = (struct jsm_channel *)port;
+
+	jsm_printk(IOCTL, INFO, &channel->ch_bd->pci_dev, "start\n");
+
+	result = jsm_get_mstat(channel);
+
+	if (result < 0)
+		return -ENXIO;
+
+	jsm_printk(IOCTL, INFO, &channel->ch_bd->pci_dev, "finish\n");
+
+	return result;
+}
+
+/*
+ * jsm_set_modem_info()
+ *
+ * Set modem signals, called by ld.
+ */
+static void jsm_tty_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+	struct jsm_channel *channel = (struct jsm_channel *)port;
+
+	jsm_printk(IOCTL, INFO, &channel->ch_bd->pci_dev, "start\n");
+
+	if (mctrl & TIOCM_RTS)
+		channel->ch_mostat |= UART_MCR_RTS;
+	else
+		channel->ch_mostat &= ~UART_MCR_RTS;
+
+	if (mctrl & TIOCM_DTR)
+		channel->ch_mostat |= UART_MCR_DTR;
+	else
+		channel->ch_mostat &= ~UART_MCR_DTR;
+
+	channel->ch_bd->bd_ops->assert_modem_signals(channel);
+
+	jsm_printk(IOCTL, INFO, &channel->ch_bd->pci_dev, "finish\n");
+	udelay(10);
+}
+
+static void jsm_tty_start_tx(struct uart_port *port, unsigned int tty_start)
+{
+	struct jsm_channel *channel = (struct jsm_channel *)port;
+
+	jsm_printk(IOCTL, INFO, &channel->ch_bd->pci_dev, "start\n");
+
+	channel->ch_flags &= ~(CH_STOP);
+	jsm_tty_write(port);
+
+	jsm_printk(IOCTL, INFO, &channel->ch_bd->pci_dev, "finish\n");
+}
+
+static void jsm_tty_stop_tx(struct uart_port *port, unsigned int tty_stop)
+{
+	struct jsm_channel *channel = (struct jsm_channel *)port;
+
+	jsm_printk(IOCTL, INFO, &channel->ch_bd->pci_dev, "start\n");
+
+	channel->ch_flags |= (CH_STOP);
+
+	jsm_printk(IOCTL, INFO, &channel->ch_bd->pci_dev, "finish\n");
+}
+
+static void jsm_tty_send_xchar(struct uart_port *port, char ch)
+{
+	unsigned long lock_flags;
+	struct jsm_channel *channel = (struct jsm_channel *)port;
+
+	spin_lock_irqsave(&port->lock, lock_flags);
+	if (ch == port->info->tty->termios->c_cc[VSTART])
+		channel->ch_bd->bd_ops->send_start_character(channel);
+
+	if (ch == port->info->tty->termios->c_cc[VSTOP])
+		channel->ch_bd->bd_ops->send_stop_character(channel);
+	spin_unlock_irqrestore(&port->lock, lock_flags);
+}
+
+static void jsm_tty_stop_rx(struct uart_port *port)
+{
+	struct jsm_channel *channel = (struct jsm_channel *)port;
+
+	channel->ch_bd->bd_ops->disable_receiver(channel);
+}
+
+static void jsm_tty_break(struct uart_port *port, int break_state)
+{
+	unsigned long lock_flags;
+	struct jsm_channel *channel = (struct jsm_channel *)port;
+
+	spin_lock_irqsave(&port->lock, lock_flags);
+	if (break_state == -1)
+		channel->ch_bd->bd_ops->send_break(channel);
+	else
+		channel->ch_bd->bd_ops->clear_break(channel, 0);
+
+	spin_unlock_irqrestore(&port->lock, lock_flags);
+}
+
+static int jsm_tty_open(struct uart_port *port)
+{
+	struct jsm_board *brd;
+	int rc = 0;
+	struct jsm_channel *channel = (struct jsm_channel *)port;
+
+	/* Get board pointer from our array of majors we have allocated */
+	brd = channel->ch_bd;
+
+	/*
+	 * Allocate channel buffers for read/write/error.
+	 * Set flag, so we don't get trounced on.
+	 */
+	channel->ch_flags |= (CH_OPENING);
+
+	/* Drop locks, as malloc with GFP_KERNEL can sleep */
+
+	if (!channel->ch_rqueue) {
+		channel->ch_rqueue = (u8 *) kmalloc(RQUEUESIZE, GFP_KERNEL);
+		if (!channel->ch_rqueue) {
+			jsm_printk(INIT, ERR, &channel->ch_bd->pci_dev,
+				"unable to allocate read queue buf");
+			return -ENOMEM;
+		}
+		memset(channel->ch_rqueue, 0, RQUEUESIZE);
+	}
+	if (!channel->ch_equeue) {
+		channel->ch_equeue = (u8 *) kmalloc(EQUEUESIZE, GFP_KERNEL);
+		if (!channel->ch_equeue) {
+			jsm_printk(INIT, ERR, &channel->ch_bd->pci_dev,
+				"unable to allocate error queue buf");
+			return -ENOMEM;
+		}
+		memset(channel->ch_equeue, 0, EQUEUESIZE);
+	}
+	if (!channel->ch_wqueue) {
+		channel->ch_wqueue = (u8 *) kmalloc(WQUEUESIZE, GFP_KERNEL);
+		if (!channel->ch_wqueue) {
+			jsm_printk(INIT, ERR, &channel->ch_bd->pci_dev,
+				"unable to allocate write queue buf");
+			return -ENOMEM;
+		}
+		memset(channel->ch_wqueue, 0, WQUEUESIZE);
+	}
+
+	channel->ch_flags &= ~(CH_OPENING);
+	/*
+	 * Initialize if neither terminal is open.
+	 */
+	jsm_printk(OPEN, INFO, &channel->ch_bd->pci_dev,
+		"jsm_open: initializing channel in open...\n");
+
+	/*
+	 * Flush input queues.
+	 */
+	channel->ch_r_head = channel->ch_r_tail = 0;
+	channel->ch_e_head = channel->ch_e_tail = 0;
+	channel->ch_w_head = channel->ch_w_tail = 0;
+
+	brd->bd_ops->flush_uart_write(channel);
+	brd->bd_ops->flush_uart_read(channel);
+
+	channel->ch_flags = 0;
+	channel->ch_cached_lsr = 0;
+	channel->ch_stops_sent = 0;
+
+	channel->ch_c_cflag	= port->info->tty->termios->c_cflag;
+	channel->ch_c_iflag	= port->info->tty->termios->c_iflag;
+	channel->ch_c_oflag	= port->info->tty->termios->c_oflag;
+	channel->ch_c_lflag	= port->info->tty->termios->c_lflag;
+	channel->ch_startc = port->info->tty->termios->c_cc[VSTART];
+	channel->ch_stopc = port->info->tty->termios->c_cc[VSTOP];
+
+	/* Tell UART to init itself */
+	brd->bd_ops->uart_init(channel);
+
+	/*
+	 * Run param in case we changed anything
+	 */
+	brd->bd_ops->param(channel);
+
+	jsm_carrier(channel);
+
+	channel->ch_open_count++;
+
+	jsm_printk(OPEN, INFO, &channel->ch_bd->pci_dev, "finish\n");
+	return rc;
+}
+
+static void jsm_tty_close(struct uart_port *port)
+{
+	struct jsm_board *bd;
+	struct termios *ts;
+	struct jsm_channel *channel = (struct jsm_channel *)port;
+
+	jsm_printk(CLOSE, INFO, &channel->ch_bd->pci_dev, "start\n");
+
+	bd = channel->ch_bd;
+	ts = channel->uart_port.info->tty->termios;
+
+	channel->ch_flags &= ~(CH_STOPI);
+
+	channel->ch_open_count--;
+
+	/*
+	 * If we have HUPCL set, lower DTR and RTS
+	 */
+	if (channel->ch_c_cflag & HUPCL) {
+		jsm_printk(CLOSE, INFO, &channel->ch_bd->pci_dev,
+			"Close. HUPCL set, dropping DTR/RTS\n");
+
+		/* Drop RTS/DTR */
+		channel->ch_mostat &= ~(UART_MCR_DTR | UART_MCR_RTS);
+		bd->bd_ops->assert_modem_signals(channel);
+	}
+
+	channel->ch_old_baud = 0;
+
+	/* Turn off UART interrupts for this port */
+	channel->ch_bd->bd_ops->uart_off(channel);
+
+	jsm_printk(CLOSE, INFO, &channel->ch_bd->pci_dev, "finish\n");
+}
+
+static void jsm_tty_set_termios(struct uart_port *port,
+				 struct termios *termios,
+				 struct termios *old_termios)
+{
+	unsigned long lock_flags;
+	struct jsm_channel *channel = (struct jsm_channel *)port;
+
+	spin_lock_irqsave(&port->lock, lock_flags);
+	channel->ch_c_cflag	= termios->c_cflag;
+	channel->ch_c_iflag	= termios->c_iflag;
+	channel->ch_c_oflag	= termios->c_oflag;
+	channel->ch_c_lflag	= termios->c_lflag;
+	channel->ch_startc	= termios->c_cc[VSTART];
+	channel->ch_stopc	= termios->c_cc[VSTOP];
+
+	channel->ch_bd->bd_ops->param(channel);
+	jsm_carrier(channel);
+	spin_unlock_irqrestore(&port->lock, lock_flags);
+}
+
+static const char *jsm_tty_type(struct uart_port *port)
+{
+	return "jsm";
+}
+
+static void jsm_tty_release_port(struct uart_port *port)
+{
+}
+
+static int jsm_tty_request_port(struct uart_port *port)
+{
+	return 0;
+}
+
+static void jsm_config_port(struct uart_port *port, int flags)
+{
+	port->type = PORT_JSM;
+}
+
+static struct uart_ops jsm_ops = {
+	.tx_empty	= jsm_tty_tx_empty,
+	.set_mctrl	= jsm_tty_set_mctrl,
+	.get_mctrl	= jsm_tty_get_mctrl,
+	.stop_tx	= jsm_tty_stop_tx,
+	.start_tx	= jsm_tty_start_tx,
+	.send_xchar	= jsm_tty_send_xchar,
+	.stop_rx	= jsm_tty_stop_rx,
+	.break_ctl	= jsm_tty_break,
+	.startup	= jsm_tty_open,
+	.shutdown	= jsm_tty_close,
+	.set_termios	= jsm_tty_set_termios,
+	.type		= jsm_tty_type,
+	.release_port	= jsm_tty_release_port,
+	.request_port	= jsm_tty_request_port,
+	.config_port	= jsm_config_port,
+};
+
+/*
+ * jsm_tty_init()
+ *
+ * Init the tty subsystem.  Called once per board after board has been
+ * downloaded and init'ed.
+ */
+int jsm_tty_init(struct jsm_board *brd)
+{
+	int i;
+	u8 *vaddr;
+	struct jsm_channel *ch;
+
+	if (!brd)
+		return -ENXIO;
+
+	jsm_printk(INIT, INFO, &brd->pci_dev, "start\n");
+
+	/*
+	 * Initialize board structure elements.
+	 */
+
+	vaddr = brd->re_map_membase;
+
+	brd->nasync = brd->maxports;
+
+	/*
+	 * Allocate channel memory that might not have been allocated
+	 * when the driver was first loaded.
+	 */
+	for (i = 0; i < brd->nasync; i++) {
+		if (!brd->channels[i]) {
+
+			/*
+			 * Okay to malloc with GFP_KERNEL, we are not at
+			 * interrupt context, and there are no locks held.
+			 */
+			brd->channels[i] = kmalloc(sizeof(struct jsm_channel), GFP_KERNEL);
+			if (!brd->channels[i]) {
+				jsm_printk(CORE, ERR, &brd->pci_dev,
+					"%s:%d Unable to allocate memory for channel struct\n",
+							 __FILE__, __LINE__);
+			}
+			memset(brd->channels[i], 0, sizeof(struct jsm_channel));
+		}
+	}
+
+	ch = brd->channels[0];
+	vaddr = brd->re_map_membase;
+
+	/* Set up channel variables */
+	for (i = 0; i < brd->nasync; i++, ch = brd->channels[i]) {
+
+		if (!brd->channels[i])
+			continue;
+
+		spin_lock_init(&ch->ch_lock);
+
+		if (brd->bd_uart_offset == 0x200)
+			ch->ch_neo_uart = (struct neo_uart_struct *) ((u64) vaddr 
+						+ (brd->bd_uart_offset * i));
+
+		ch->ch_bd = brd;
+		ch->ch_portnum = i;
+
+		/* .25 second delay */
+		ch->ch_close_delay = 250;
+
+		init_waitqueue_head(&ch->ch_flags_wait);
+	}
+
+	jsm_printk(INIT, INFO, &brd->pci_dev, "finish\n");
+	return 0;
+}
+
+int jsm_uart_port_init(struct jsm_board *brd)
+{
+	int i;
+	u8 *vaddr;
+	struct jsm_channel *ch;
+
+	if (!brd)
+		return -ENXIO;
+
+	jsm_printk(INIT, INFO, &brd->pci_dev, "start\n");
+
+	/*
+	 * Initialize board structure elements.
+	 */
+
+	vaddr = brd->re_map_membase;
+	brd->nasync = brd->maxports;
+
+	/* Set up channel variables */
+	for (i = 0; i < brd->nasync; i++, ch = brd->channels[i]) {
+
+		if (!brd->channels[i])
+			continue;
+
+		brd->channels[i]->uart_port.irq = brd->irq;
+		brd->channels[i]->uart_port.type = PORT_JSM;
+		brd->channels[i]->uart_port.iotype = UPIO_MEM;
+		brd->channels[i]->uart_port.membase = brd->re_map_membase;
+		brd->channels[i]->uart_port.fifosize = 16;
+		brd->channels[i]->uart_port.ops = &jsm_ops;
+		brd->channels[i]->uart_port.line = brd->channels[i]->ch_portnum + brd->boardnum * 2;
+		if (uart_add_one_port (&jsm_uart_driver, &brd->channels[i]->uart_port)) 
+			printk(KERN_INFO "Added device failed\n");
+		else
+			printk(KERN_INFO "Added device \n");
+	}
+
+	jsm_printk(INIT, INFO, &brd->pci_dev, "finish\n");
+	return 0;
+}
+
+int jsm_remove_uart_port(struct jsm_board *brd)
+{
+	int i;
+	struct jsm_channel *ch;
+
+	if (!brd)
+		return -ENXIO;
+
+	jsm_printk(INIT, INFO, &brd->pci_dev, "start\n");
+
+	/*
+	 * Initialize board structure elements.
+	 */
+
+	brd->nasync = brd->maxports;
+
+	/* Set up channel variables */
+	for (i = 0; i < brd->nasync; i++) {
+
+		if (!brd->channels[i])
+			continue;
+
+		ch = brd->channels[i];
+
+		uart_remove_one_port(&jsm_uart_driver, &brd->channels[i]->uart_port);
+	}
+
+	jsm_printk(INIT, INFO, &brd->pci_dev, "finish\n");
+	return 0;
+}
+
+void jsm_input(struct jsm_channel *ch)
+{
+	struct jsm_board *bd;
+	struct tty_struct *tp;
+	u32 rmask;
+	u16 head;
+	u16 tail;
+	int data_len;
+	u64 lock_flags;
+	int flip_len;
+	int len = 0;
+	int n = 0;
+	char *buf = NULL;
+	char *buf2 = NULL;
+	int s = 0;
+	int i = 0;
+
+	jsm_printk(READ, INFO, &ch->ch_bd->pci_dev, "start\n");
+
+	if (!ch)
+		return;
+
+	tp = ch->uart_port.info->tty;
+
+	bd = ch->ch_bd;
+	if(!bd)
+		return;
+
+	spin_lock_irqsave(&ch->ch_lock, lock_flags);
+
+	/* 
+	 *Figure the number of characters in the buffer.
+	 *Exit immediately if none.
+	 */
+
+	rmask = RQUEUEMASK;
+
+	head = ch->ch_r_head & rmask;
+	tail = ch->ch_r_tail & rmask;
+
+	data_len = (head - tail) & rmask;
+	if (data_len == 0) {
+		spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
+		return;
+	}
+
+	jsm_printk(READ, INFO, &ch->ch_bd->pci_dev, "start\n");
+
+	/*
+	 *If the device is not open, or CREAD is off, flush
+	 *input data and return immediately.
+	 */
+	if (!tp || 
+		!(tp->termios->c_cflag & CREAD) ) {
+
+		jsm_printk(READ, INFO, &ch->ch_bd->pci_dev,
+			"input. dropping %d bytes on port %d...\n", data_len, ch->ch_portnum);
+		ch->ch_r_head = tail;
+
+		/* Force queue flow control to be released, if needed */
+		jsm_check_queue_flow_control(ch);
+
+		spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
+		return;
+	}
+
+	/*
+	 * If we are throttled, simply don't read any data.
+	 */
+	if (ch->ch_flags & CH_STOPI) {
+		spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
+		jsm_printk(READ, INFO, &ch->ch_bd->pci_dev,
+			"Port %d throttled, not reading any data. head: %x tail: %x\n",
+			ch->ch_portnum, head, tail);
+		return;
+	}
+
+	jsm_printk(READ, INFO, &ch->ch_bd->pci_dev, "start 2\n");
+
+	/*
+	 * If the rxbuf is empty and we are not throttled, put as much
+	 * as we can directly into the linux TTY flip buffer.  
+	 * The jsm_rawreadok case takes advantage of carnal knowledge that
+	 * the char_buf and the flag_buf are next to each other and
+	 * are each of (2 * TTY_FLIPBUF_SIZE) size.
+	 *
+	 * NOTE: if(!tty->real_raw), the call to ldisc.receive_buf
+	 *actually still uses the flag buffer, so you can't
+	 *use it for input data
+	 */
+	if (jsm_rawreadok) {
+		if (tp->real_raw)
+			flip_len = MYFLIPLEN;
+		else
+			flip_len = 2 * TTY_FLIPBUF_SIZE;
+	} else
+		flip_len = TTY_FLIPBUF_SIZE - tp->flip.count;
+
+	len = min(data_len, flip_len);
+	len = min(len, (N_TTY_BUF_SIZE - 1) - tp->read_cnt);
+
+	if (len <= 0) {
+		spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
+		jsm_printk(READ, INFO, &ch->ch_bd->pci_dev, "jsm_input 1\n");
+		return;
+	}
+
+	/*
+	 * If we're bypassing flip buffers on rx, we can blast it
+	 * right into the beginning of the buffer.
+	 */ 
+	if (jsm_rawreadok) {
+		if (tp->real_raw) {
+			if (ch->ch_flags & CH_FLIPBUF_IN_USE) {
+				jsm_printk(READ, INFO, &ch->ch_bd->pci_dev,
+					"JSM - FLIPBUF in use. delaying input\n");
+				spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
+				return;
+			}
+			ch->ch_flags |= CH_FLIPBUF_IN_USE;
+			buf = ch->ch_bd->flipbuf;
+			buf2 = NULL;
+		} else {
+			buf = tp->flip.char_buf;
+			buf2 = tp->flip.flag_buf;
+		}
+	} else {
+		buf = tp->flip.char_buf_ptr;
+		buf2 = tp->flip.flag_buf_ptr;
+	}
+
+	n = len;
+
+	/*
+	 * n now contains the most amount of data we can copy,
+	 * bounded either by the flip buffer size or the amount
+	 * of data the card actually has pending...
+	 */
+	while (n) {
+		s = ((head >= tail) ? head : RQUEUESIZE) - tail;
+		s = min(s, n);
+
+		if (s <= 0)
+			break;
+
+		memcpy(buf, ch->ch_rqueue + tail, s);
+
+		/* buf2 is only set when port isn't raw */
+		if (buf2)
+			memcpy(buf2, ch->ch_equeue + tail, s);
+
+		tail += s;
+		buf += s;
+		if (buf2)
+			buf2 += s;
+		n -= s;
+		/* Flip queue if needed */
+		tail &= rmask;
+	}
+
+	/*  
+	 * In high performance mode, we don't have to update
+	 * flag_buf or any of the counts or pointers into flip buf.
+	 */
+	if (!jsm_rawreadok) {
+		if (I_PARMRK(tp) || I_BRKINT(tp) || I_INPCK(tp)) {
+			for (i = 0; i < len; i++) {
+				/*
+				 * Give the Linux ld the flags in the
+				 * format it likes.
+				 */
+				if (tp->flip.flag_buf_ptr[i] & UART_LSR_BI)
+					tp->flip.flag_buf_ptr[i] = TTY_BREAK;
+				else if (tp->flip.flag_buf_ptr[i] & UART_LSR_PE)
+					tp->flip.flag_buf_ptr[i] = TTY_PARITY;
+				else if (tp->flip.flag_buf_ptr[i] & UART_LSR_FE)
+					tp->flip.flag_buf_ptr[i] = TTY_FRAME;
+				else 
+					tp->flip.flag_buf_ptr[i] = TTY_NORMAL;
+			}
+		} else {
+			memset(tp->flip.flag_buf_ptr, 0, len);
+		}
+
+		tp->flip.char_buf_ptr += len;
+		tp->flip.flag_buf_ptr += len;
+		tp->flip.count += len;
+	}
+	else if (!tp->real_raw) {
+		if (I_PARMRK(tp) || I_BRKINT(tp) || I_INPCK(tp)) {
+			for (i = 0; i < len; i++) {
+				/*
+				 * Give the Linux ld the flags in the
+				 * format it likes.
+				 */
+				if (tp->flip.flag_buf_ptr[i] & UART_LSR_BI)
+					tp->flip.flag_buf_ptr[i] = TTY_BREAK;
+				else if (tp->flip.flag_buf_ptr[i] & UART_LSR_PE)
+					tp->flip.flag_buf_ptr[i] = TTY_PARITY;
+				else if (tp->flip.flag_buf_ptr[i] & UART_LSR_FE)
+					tp->flip.flag_buf_ptr[i] = TTY_FRAME;
+				else 
+					tp->flip.flag_buf_ptr[i] = TTY_NORMAL;
+			}
+		} else
+			memset(tp->flip.flag_buf, 0, len);
+	}
+
+	/*
+	 * If we're doing raw reads, jam it right into the
+	 * line disc bypassing the flip buffers.
+	 */
+	if (jsm_rawreadok) {
+		if (tp->real_raw) {
+			ch->ch_r_tail = tail & rmask;
+			ch->ch_e_tail = tail & rmask;
+
+			jsm_check_queue_flow_control(ch);
+
+			/* !!! WE *MUST* LET GO OF ALL LOCKS BEFORE CALLING RECEIVE BUF !!! */
+
+			spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
+
+			jsm_printk(READ, INFO, &ch->ch_bd->pci_dev,
+				"jsm_input. %d real_raw len:%d calling receive_buf for board %d\n",
+				__LINE__, len, ch->ch_bd->boardnum);
+			tp->ldisc.receive_buf(tp, ch->ch_bd->flipbuf, NULL, len);
+
+			/* Allow use of channel flip buffer again */
+			spin_lock_irqsave(&ch->ch_lock, lock_flags);
+			ch->ch_flags &= ~CH_FLIPBUF_IN_USE;
+			spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
+
+		} else {
+			ch->ch_r_tail = tail & rmask;
+			ch->ch_e_tail = tail & rmask;
+
+			jsm_check_queue_flow_control(ch);
+
+			/* !!! WE *MUST* LET GO OF ALL LOCKS BEFORE CALLING RECEIVE BUF !!! */
+			spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
+
+			jsm_printk(READ, INFO, &ch->ch_bd->pci_dev,
+				"jsm_input. %d not real_raw len:%d calling receive_buf for board %d\n", 
+				__LINE__, len, ch->ch_bd->boardnum);
+
+			tp->ldisc.receive_buf(tp, tp->flip.char_buf, tp->flip.flag_buf, len);
+		}
+	} else {
+		ch->ch_r_tail = tail & rmask;
+		ch->ch_e_tail = tail & rmask;
+
+		jsm_check_queue_flow_control(ch);
+
+		spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
+
+		jsm_printk(READ, INFO, &ch->ch_bd->pci_dev,
+			"jsm_input. %d not jsm_read raw okay scheduling flip\n", __LINE__); 
+		tty_schedule_flip(tp);
+	}
+
+	jsm_printk(IOCTL, INFO, &ch->ch_bd->pci_dev, "finish\n");
+}
+
+void jsm_carrier(struct jsm_channel *ch)
+{
+	struct jsm_board *bd;
+
+	int virt_carrier = 0;
+	int phys_carrier = 0;
+ 
+	jsm_printk(CARR, INFO, &ch->ch_bd->pci_dev, "start\n");
+	if (!ch)
+		return;
+
+	bd = ch->ch_bd;
+
+	if (!bd)
+		return;
+
+	if (ch->ch_mistat & UART_MSR_DCD) {
+		jsm_printk(CARR, INFO, &ch->ch_bd->pci_dev,
+			"mistat: %x D_CD: %x\n", ch->ch_mistat, ch->ch_mistat & UART_MSR_DCD);
+		phys_carrier = 1;
+	}
+
+	if (ch->ch_c_cflag & CLOCAL)
+		virt_carrier = 1;
+
+	jsm_printk(CARR, INFO, &ch->ch_bd->pci_dev,
+		"DCD: physical: %d virt: %d\n", phys_carrier, virt_carrier);
+
+	/*
+	 * Test for a VIRTUAL carrier transition to HIGH.
+	 */
+	if (((ch->ch_flags & CH_FCAR) == 0) && (virt_carrier == 1)) {
+
+		/*
+		 * When carrier rises, wake any threads waiting
+		 * for carrier in the open routine.
+		 */
+
+		jsm_printk(CARR, INFO, &ch->ch_bd->pci_dev,
+			"carrier: virt DCD rose\n");
+
+		if (waitqueue_active(&(ch->ch_flags_wait)))
+			wake_up_interruptible(&ch->ch_flags_wait);
+	}
+
+	/*
+	 * Test for a PHYSICAL carrier transition to HIGH.
+	 */
+	if (((ch->ch_flags & CH_CD) == 0) && (phys_carrier == 1)) {
+
+		/*
+		 * When carrier rises, wake any threads waiting
+		 * for carrier in the open routine.
+		 */
+
+		jsm_printk(CARR, INFO, &ch->ch_bd->pci_dev,
+			"carrier: physical DCD rose\n");
+
+		if (waitqueue_active(&(ch->ch_flags_wait)))
+			wake_up_interruptible(&ch->ch_flags_wait);
+	}
+
+	/*
+	 *  Test for a PHYSICAL transition to low, so long as we aren't
+	 *  currently ignoring physical transitions (which is what "virtual
+	 *  carrier" indicates).
+	 *
+	 *  The transition of the virtual carrier to low really doesn't
+	 *  matter... it really only means "ignore carrier state", not
+	 *  "make pretend that carrier is there".
+	 */
+	if ((virt_carrier == 0) && ((ch->ch_flags & CH_CD) != 0) 
+			&& (phys_carrier == 0)) {
+		/*
+		 *	When carrier drops:
+		 *
+		 *	Drop carrier on all open units.
+		 *
+		 *	Flush queues, waking up any task waiting in the
+		 *	line discipline.
+		 *
+		 *	Send a hangup to the control terminal.
+		 *
+		 *	Enable all select calls.
+		 */
+		if (waitqueue_active(&(ch->ch_flags_wait)))
+			wake_up_interruptible(&ch->ch_flags_wait);
+	}
+
+	/*
+	 *  Make sure that our cached values reflect the current reality.
+	 */
+	if (virt_carrier == 1)
+		ch->ch_flags |= CH_FCAR;
+	else
+		ch->ch_flags &= ~CH_FCAR;
+
+	if (phys_carrier == 1)
+		ch->ch_flags |= CH_CD;
+	else
+		ch->ch_flags &= ~CH_CD;
+}
+
+
+void jsm_check_queue_flow_control(struct jsm_channel *ch)
+{
+	int qleft = 0;
+
+	/* Store how much space we have left in the queue */
+	if ((qleft = ch->ch_r_tail - ch->ch_r_head - 1) < 0)
+		qleft += RQUEUEMASK + 1;
+
+	/*
+	 * Check to see if we should enforce flow control on our queue because
+	 * the ld (or user) isn't reading data out of our queue fast enuf.
+	 *
+	 * NOTE: This is done based on what the current flow control of the
+	 * port is set for.
+	 *
+	 * 1) HWFLOW (RTS) - Turn off the UART's Receive interrupt.
+	 *	This will cause the UART's FIFO to back up, and force
+	 *	the RTS signal to be dropped.
+	 * 2) SWFLOW (IXOFF) - Keep trying to send a stop character to
+	 *	the other side, in hopes it will stop sending data to us.
+	 * 3) NONE - Nothing we can do.  We will simply drop any extra data
+	 *	that gets sent into us when the queue fills up.
+	 */
+	if (qleft < 256) {
+		/* HWFLOW */
+		if (ch->ch_c_cflag & CRTSCTS) {
+			if(!(ch->ch_flags & CH_RECEIVER_OFF)) {
+				ch->ch_bd->bd_ops->disable_receiver(ch);
+				ch->ch_flags |= (CH_RECEIVER_OFF);
+				jsm_printk(READ, INFO, &ch->ch_bd->pci_dev,
+					"Internal queue hit hilevel mark (%d)! Turning off interrupts.\n",
+					qleft);
+			}
+		}
+		/* SWFLOW */
+		else if (ch->ch_c_iflag & IXOFF) {
+			if (ch->ch_stops_sent <= MAX_STOPS_SENT) {
+				ch->ch_bd->bd_ops->send_stop_character(ch);
+				ch->ch_stops_sent++;
+				jsm_printk(READ, INFO, &ch->ch_bd->pci_dev,
+					"Sending stop char! Times sent: %x\n", ch->ch_stops_sent);
+			}
+		}
+	}
+
+	/*
+	 * Check to see if we should unenforce flow control because
+	 * ld (or user) finally read enuf data out of our queue.
+	 *
+	 * NOTE: This is done based on what the current flow control of the
+	 * port is set for.
+	 *
+	 * 1) HWFLOW (RTS) - Turn back on the UART's Receive interrupt.
+	 *	This will cause the UART's FIFO to raise RTS back up,
+	 *	which will allow the other side to start sending data again.
+	 * 2) SWFLOW (IXOFF) - Send a start character to
+	 *	the other side, so it will start sending data to us again.
+	 * 3) NONE - Do nothing. Since we didn't do anything to turn off the
+	 *	other side, we don't need to do anything now.
+	 */
+	if (qleft > (RQUEUESIZE / 2)) {
+		/* HWFLOW */
+		if (ch->ch_c_cflag & CRTSCTS) {
+			if (ch->ch_flags & CH_RECEIVER_OFF) {
+				ch->ch_bd->bd_ops->enable_receiver(ch);
+				ch->ch_flags &= ~(CH_RECEIVER_OFF);
+				jsm_printk(READ, INFO, &ch->ch_bd->pci_dev,
+					"Internal queue hit lowlevel mark (%d)! Turning on interrupts.\n",
+					qleft);
+			}
+		}
+		/* SWFLOW */
+		else if (ch->ch_c_iflag & IXOFF && ch->ch_stops_sent) {
+			ch->ch_stops_sent = 0;
+			ch->ch_bd->bd_ops->send_start_character(ch);
+			jsm_printk(READ, INFO, &ch->ch_bd->pci_dev, "Sending start char!\n");
+		}
+	}
+}
+
+/*
+ * jsm_tty_write()
+ *
+ * Take data from the user or kernel and send it out to the FEP.
+ * In here exists all the Transparent Print magic as well.
+ */
+int jsm_tty_write(struct uart_port *port)
+{
+	int bufcount = 0, n = 0;
+	int data_count = 0,data_count1 =0;
+	u16 head;
+	u16 tail;
+	u16 tmask;
+	u32 remain;
+	int temp_tail = port->info->xmit.tail;
+	struct jsm_channel *channel = (struct jsm_channel *)port;
+
+	tmask = WQUEUEMASK;
+	head = (channel->ch_w_head) & tmask;
+	tail = (channel->ch_w_tail) & tmask;
+
+	if ((bufcount = tail - head - 1) < 0)
+		bufcount += WQUEUESIZE;
+
+	n = bufcount;
+
+	n = min(n, 56);
+	remain = WQUEUESIZE - head;
+
+	data_count = 0;
+	if (n >= remain) {
+		n -= remain;
+		while ((port->info->xmit.head != temp_tail) &&
+		(data_count < remain)) {
+			channel->ch_wqueue[head++] =
+			port->info->xmit.buf[temp_tail];
+
+			temp_tail++;
+			temp_tail &= (UART_XMIT_SIZE - 1);
+			data_count++;
+		}
+		if (data_count == remain) head = 0;
+	}
+
+	data_count1 = 0;
+	if (n > 0) {
+		remain = n;
+		while ((port->info->xmit.head != temp_tail) &&
+			(data_count1 < remain)) {
+			channel->ch_wqueue[head++] =
+				port->info->xmit.buf[temp_tail];
+
+			temp_tail++;
+			temp_tail &= (UART_XMIT_SIZE - 1);
+			data_count1++;
+
+		}
+	}
+
+	port->info->xmit.tail = temp_tail;
+
+	data_count += data_count1;
+	if (data_count) {
+		head &= tmask;
+		channel->ch_w_head = head;
+	}
+
+	if (data_count) {
+		channel->ch_bd->bd_ops->copy_data_from_queue_to_uart(channel);
+	}
+
+	return data_count;
+}
+
+static ssize_t jsm_driver_version_show(struct device_driver *ddp, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%s\n", JSM_VERSION);
+}
+static DRIVER_ATTR(version, S_IRUSR, jsm_driver_version_show, NULL);
+
+static ssize_t jsm_driver_state_show(struct device_driver *ddp, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%s\n", jsm_driver_state_text[jsm_driver_state]);
+}
+static DRIVER_ATTR(state, S_IRUSR, jsm_driver_state_show, NULL);
+
+void jsm_create_driver_sysfiles(struct device_driver *driverfs)
+{
+	driver_create_file(driverfs, &driver_attr_version);
+	driver_create_file(driverfs, &driver_attr_state);
+}
+
+void jsm_remove_driver_sysfiles(struct device_driver *driverfs)
+{
+	driver_remove_file(driverfs, &driver_attr_version);
+	driver_remove_file(driverfs, &driver_attr_state);
+}

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [ patch 3/5] drivers/serial/jsm: new serial device driver
  2005-03-09 18:58                     ` Greg KH
  2005-03-11 15:29                       ` [ patch 1/5] " Wen Xiong
  2005-03-11 15:32                       ` [ patch 2/5] " Wen Xiong
@ 2005-03-11 15:38                       ` Wen Xiong
  2005-03-11 15:53                         ` Arjan van de Ven
  2005-03-11 15:38                       ` [ patch 4/5] " Wen Xiong
  2005-03-11 15:38                       ` [ patch 5/5] " Wen Xiong
  4 siblings, 1 reply; 34+ messages in thread
From: Wen Xiong @ 2005-03-11 15:38 UTC (permalink / raw)
  To: Greg KH; +Cc: Wen Xiong, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 98 bytes --]

The third patch for jsm device driver.

Signed-off-by: Wen Xiong <wendyx@us.ltcfwd.linux.ibm.com>

[-- Attachment #2: patch3.jasmine --]
[-- Type: text/plain, Size: 38515 bytes --]

diff -Nuar linux-2.6.11.org/drivers/serial/jsm/jsm_neo.c linux-2.6.11.new/drivers/serial/jsm/jsm_neo.c
--- linux-2.6.11.org/drivers/serial/jsm/jsm_neo.c	1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.11.new/drivers/serial/jsm/jsm_neo.c	2005-03-11 09:07:53.902968424 -0600
@@ -0,0 +1,1402 @@
+/************************************************************************
+ * Copyright 2003 Digi International (www.digi.com)
+ *
+ * Copyright (C) 2004 IBM Corporation. All rights reserved.
+ *
+ * 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, or (at your option)
+ * any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; without even the 
+ * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License 
+ * along with this program; if not, write to the Free Software 
+ * Foundation, Inc., 59 * Temple Place - Suite 330, Boston,
+ * MA  02111-1307, USA.
+ *
+ * Contact Information:
+ * Scott H Kilau <Scott_Kilau@digi.com>
+ * Wendy Xiong   <wendyx@us.ltcfwd.linux.ibm.com>
+ *
+ ***********************************************************************/
+#include <linux/delay.h>	/* For udelay */
+#include <linux/serial_reg.h>	/* For the various UART offsets */
+#include <linux/tty.h>
+#include <linux/pci.h>
+#include <asm/io.h>
+
+#include "jsm.h"		/* Driver main header file */
+
+static u32 jsm_offset_table[8] = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 };
+
+static void neo_set_cts_flow_control(struct jsm_channel *ch)
+{
+	u8 ier = readb(&ch->ch_neo_uart->ier);
+	u8 efr = readb(&ch->ch_neo_uart->efr);
+
+	jsm_printk(PARAM, INFO, &ch->ch_bd->pci_dev, "Setting CTSFLOW\n");
+
+	/* Turn on auto CTS flow control */
+	ier |= (UART_17158_IER_CTSDSR);
+	efr |= (UART_17158_EFR_ECB | UART_17158_EFR_CTSDSR);
+
+	/* Turn off auto Xon flow control */
+	efr &= ~(UART_17158_EFR_IXON);
+
+	/* Why? Becuz Exar's spec says we have to zero it out before setting it */
+	writeb(0, &ch->ch_neo_uart->efr);
+
+	/* Turn on UART enhanced bits */
+	writeb(efr, &ch->ch_neo_uart->efr);
+
+	/* Turn on table D, with 8 char hi/low watermarks */
+	writeb((UART_17158_FCTR_TRGD | UART_17158_FCTR_RTS_4DELAY), &ch->ch_neo_uart->fctr);
+
+	/* Feed the UART our trigger levels */
+	writeb(8, &ch->ch_neo_uart->tfifo);
+	ch->ch_t_tlevel = 8;
+
+	writeb(ier, &ch->ch_neo_uart->ier);
+}
+
+static void neo_set_rts_flow_control(struct jsm_channel *ch)
+{
+	u8 ier = readb(&ch->ch_neo_uart->ier);
+	u8 efr = readb(&ch->ch_neo_uart->efr);
+
+	jsm_printk(PARAM, INFO, &ch->ch_bd->pci_dev, "Setting RTSFLOW\n");
+
+	/* Turn on auto RTS flow control */
+	ier |= (UART_17158_IER_RTSDTR);
+	efr |= (UART_17158_EFR_ECB | UART_17158_EFR_RTSDTR);
+
+	/* Turn off auto Xoff flow control */
+	ier &= ~(UART_17158_IER_XOFF);
+	efr &= ~(UART_17158_EFR_IXOFF);
+
+	/* Why? Becuz Exar's spec says we have to zero it out before setting it */
+	writeb(0, &ch->ch_neo_uart->efr);
+
+	/* Turn on UART enhanced bits */
+	writeb(efr, &ch->ch_neo_uart->efr);
+
+	writeb((UART_17158_FCTR_TRGD | UART_17158_FCTR_RTS_4DELAY), &ch->ch_neo_uart->fctr);
+	ch->ch_r_watermark = 4;
+
+	writeb(56, &ch->ch_neo_uart->rfifo);
+	ch->ch_r_tlevel = 56;
+
+	writeb(ier, &ch->ch_neo_uart->ier);
+
+	/*
+	 * From the Neo UART spec sheet:
+	 * The auto RTS/DTR function must be started by asserting
+	 * RTS/DTR# output pin (MCR bit-0 or 1 to logic 1 after
+	 * it is enabled.
+	 */
+	ch->ch_mostat |= (UART_MCR_RTS);
+}
+
+
+static void neo_set_ixon_flow_control(struct jsm_channel *ch)
+{
+	u8 ier = readb(&ch->ch_neo_uart->ier);
+	u8 efr = readb(&ch->ch_neo_uart->efr);
+
+	jsm_printk(PARAM, INFO, &ch->ch_bd->pci_dev, "Setting IXON FLOW\n");
+
+	/* Turn off auto CTS flow control */
+	ier &= ~(UART_17158_IER_CTSDSR);
+	efr &= ~(UART_17158_EFR_CTSDSR);
+
+	/* Turn on auto Xon flow control */
+	efr |= (UART_17158_EFR_ECB | UART_17158_EFR_IXON);
+
+	/* Why? Becuz Exar's spec says we have to zero it out before setting it */
+	writeb(0, &ch->ch_neo_uart->efr);
+
+	/* Turn on UART enhanced bits */
+	writeb(efr, &ch->ch_neo_uart->efr);
+
+	writeb((UART_17158_FCTR_TRGD | UART_17158_FCTR_RTS_8DELAY), &ch->ch_neo_uart->fctr);
+	ch->ch_r_watermark = 4;
+
+	writeb(32, &ch->ch_neo_uart->rfifo);
+	ch->ch_r_tlevel = 32;
+
+	/* Tell UART what start/stop chars it should be looking for */
+	writeb(ch->ch_startc, &ch->ch_neo_uart->xonchar1);
+	writeb(0, &ch->ch_neo_uart->xonchar2);
+
+	writeb(ch->ch_stopc, &ch->ch_neo_uart->xoffchar1);
+	writeb(0, &ch->ch_neo_uart->xoffchar2);
+
+	writeb(ier, &ch->ch_neo_uart->ier);
+}
+
+static void neo_set_ixoff_flow_control(struct jsm_channel *ch)
+{
+	u8 ier = readb(&ch->ch_neo_uart->ier);
+	u8 efr = readb(&ch->ch_neo_uart->efr);
+
+	jsm_printk(PARAM, INFO, &ch->ch_bd->pci_dev, "Setting IXOFF FLOW\n");
+
+	/* Turn off auto RTS flow control */
+	ier &= ~(UART_17158_IER_RTSDTR);
+	efr &= ~(UART_17158_EFR_RTSDTR);
+
+	/* Turn on auto Xoff flow control */
+	ier |= (UART_17158_IER_XOFF);
+	efr |= (UART_17158_EFR_ECB | UART_17158_EFR_IXOFF);
+
+	/* Why? Becuz Exar's spec says we have to zero it out before setting it */
+	writeb(0, &ch->ch_neo_uart->efr);
+
+	/* Turn on UART enhanced bits */
+	writeb(efr, &ch->ch_neo_uart->efr);
+
+	/* Turn on table D, with 8 char hi/low watermarks */
+	writeb((UART_17158_FCTR_TRGD | UART_17158_FCTR_RTS_8DELAY), &ch->ch_neo_uart->fctr);
+
+	writeb(8, &ch->ch_neo_uart->tfifo);
+	ch->ch_t_tlevel = 8;
+
+	/* Tell UART what start/stop chars it should be looking for */
+	writeb(ch->ch_startc, &ch->ch_neo_uart->xonchar1);
+	writeb(0, &ch->ch_neo_uart->xonchar2);
+
+	writeb(ch->ch_stopc, &ch->ch_neo_uart->xoffchar1);
+	writeb(0, &ch->ch_neo_uart->xoffchar2);
+
+	writeb(ier, &ch->ch_neo_uart->ier);
+}
+
+static void neo_set_no_input_flow_control(struct jsm_channel *ch)
+{
+	u8 ier = readb(&ch->ch_neo_uart->ier);
+	u8 efr = readb(&ch->ch_neo_uart->efr);
+
+	jsm_printk(PARAM, INFO, &ch->ch_bd->pci_dev, "Unsetting Input FLOW\n");
+
+	/* Turn off auto RTS flow control */
+	ier &= ~(UART_17158_IER_RTSDTR);
+	efr &= ~(UART_17158_EFR_RTSDTR);
+
+	/* Turn off auto Xoff flow control */
+	ier &= ~(UART_17158_IER_XOFF);
+	if (ch->ch_c_iflag & IXON)
+		efr &= ~(UART_17158_EFR_IXOFF);
+	else
+		efr &= ~(UART_17158_EFR_ECB | UART_17158_EFR_IXOFF);
+
+	/* Why? Becuz Exar's spec says we have to zero it out before setting it */
+	writeb(0, &ch->ch_neo_uart->efr);
+
+	/* Turn on UART enhanced bits */
+	writeb(efr, &ch->ch_neo_uart->efr);
+
+	/* Turn on table D, with 8 char hi/low watermarks */
+	writeb((UART_17158_FCTR_TRGD | UART_17158_FCTR_RTS_8DELAY), &ch->ch_neo_uart->fctr);
+
+	ch->ch_r_watermark = 0;
+
+	writeb(16, &ch->ch_neo_uart->tfifo);
+	ch->ch_t_tlevel = 16;
+
+	writeb(16, &ch->ch_neo_uart->rfifo);
+	ch->ch_r_tlevel = 16;
+
+	writeb(ier, &ch->ch_neo_uart->ier);
+}
+
+static void neo_set_no_output_flow_control(struct jsm_channel *ch)
+{
+	u8 ier = readb(&ch->ch_neo_uart->ier);
+	u8 efr = readb(&ch->ch_neo_uart->efr);
+
+	jsm_printk(PARAM, INFO, &ch->ch_bd->pci_dev, "Unsetting Output FLOW\n");
+
+	/* Turn off auto CTS flow control */
+	ier &= ~(UART_17158_IER_CTSDSR);
+	efr &= ~(UART_17158_EFR_CTSDSR);
+
+	/* Turn off auto Xon flow control */
+	if (ch->ch_c_iflag & IXOFF)
+		efr &= ~(UART_17158_EFR_IXON);
+	else
+		efr &= ~(UART_17158_EFR_ECB | UART_17158_EFR_IXON);
+
+	/* Why? Becuz Exar's spec says we have to zero it out before setting it */
+	writeb(0, &ch->ch_neo_uart->efr);
+
+	/* Turn on UART enhanced bits */
+	writeb(efr, &ch->ch_neo_uart->efr);
+
+	/* Turn on table D, with 8 char hi/low watermarks */
+	writeb((UART_17158_FCTR_TRGD | UART_17158_FCTR_RTS_8DELAY), &ch->ch_neo_uart->fctr);
+
+	ch->ch_r_watermark = 0;
+
+	writeb(16, &ch->ch_neo_uart->tfifo);
+	ch->ch_t_tlevel = 16;
+
+	writeb(16, &ch->ch_neo_uart->rfifo);
+	ch->ch_r_tlevel = 16;
+
+	writeb(ier, &ch->ch_neo_uart->ier);
+}
+
+static inline void neo_set_new_start_stop_chars(struct jsm_channel *ch)
+{
+
+	/* if hardware flow control is set, then skip this whole thing */
+	if (ch->ch_c_cflag & CRTSCTS)
+		return;
+
+	jsm_printk(PARAM, INFO, &ch->ch_bd->pci_dev, "start\n");
+
+	/* Tell UART what start/stop chars it should be looking for */
+	writeb(ch->ch_startc, &ch->ch_neo_uart->xonchar1);
+	writeb(0, &ch->ch_neo_uart->xonchar2);
+
+	writeb(ch->ch_stopc, &ch->ch_neo_uart->xoffchar1);
+	writeb(0, &ch->ch_neo_uart->xoffchar2);
+}
+
+static void neo_copy_data_from_uart_to_queue(struct jsm_channel *ch)
+{
+	int qleft = 0;
+	u8 linestatus = 0;
+	u8 error_mask = 0;
+	int n = 0;
+	int total = 0;
+	u16 head;
+	u16 tail;
+
+	if (!ch)
+		return;
+
+	/* cache head and tail of queue */
+	head = ch->ch_r_head & RQUEUEMASK;
+	tail = ch->ch_r_tail & RQUEUEMASK;
+
+	/* Get our cached LSR */
+	linestatus = ch->ch_cached_lsr;
+	ch->ch_cached_lsr = 0;
+
+	/* Store how much space we have left in the queue */
+	if ((qleft = tail - head - 1) < 0)
+		qleft += RQUEUEMASK + 1;
+
+	/*
+	 * If the UART is not in FIFO mode, force the FIFO copy to
+	 * NOT be run, by setting total to 0.
+	 *
+	 * On the other hand, if the UART IS in FIFO mode, then ask
+	 * the UART to give us an approximation of data it has RX'ed.
+	 */
+	if (!(ch->ch_flags & CH_FIFO_ENABLED))
+		total = 0;
+	else {
+		total = readb(&ch->ch_neo_uart->rfifo);
+
+		/*
+		 * EXAR chip bug - RX FIFO COUNT - Fudge factor.
+		 *
+		 * This resolves a problem/bug with the Exar chip that sometimes
+		 * returns a bogus value in the rfifo register.
+		 * The count can be any where from 0-3 bytes "off".
+		 * Bizarre, but true.
+		 */
+		total -= 3;
+	}
+
+	/*
+	 * Finally, bound the copy to make sure we don't overflow
+	 * our own queue...
+	 * The byte by byte copy loop below this loop this will
+	 * deal with the queue overflow possibility.
+	 */
+	total = min(total, qleft);
+
+	while (total > 0) { 
+		/*
+		 * Grab the linestatus register, we need to check
+		 * to see if there are any errors in the FIFO.
+		 */
+		linestatus = readb(&ch->ch_neo_uart->lsr);
+
+		/*
+		 * Break out if there is a FIFO error somewhere.
+		 * This will allow us to go byte by byte down below,
+		 * finding the exact location of the error.
+		 */
+		if (linestatus & UART_17158_RX_FIFO_DATA_ERROR)
+			break;
+
+		/* Make sure we don't go over the end of our queue */
+		n = min(((u32) total), (RQUEUESIZE - (u32) tail));
+
+		/*
+		 * Cut down n even further if needed, this is to fix
+		 * a problem with memcpy_fromio() with the Neo on the
+		 * IBM pSeries platform.
+		 * 15 bytes max appears to be the magic number.
+		 */
+		n = min((u32) n, (u32) 12);
+
+		/*
+		 * Since we are grabbing the linestatus register, which
+		 * will reset some bits after our read, we need to ensure
+		 * we don't miss our TX FIFO emptys.
+		 */
+		if (linestatus & (UART_LSR_THRE | UART_17158_TX_AND_FIFO_CLR))
+			ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM);
+
+		linestatus = 0;
+
+		/* Copy data from uart to the queue */
+		memcpy_fromio(ch->ch_rqueue + head, &ch->ch_neo_uart->txrxburst, n);
+		/*
+		 * Since RX_FIFO_DATA_ERROR was 0, we are guarenteed
+		 * that all the data currently in the FIFO is free of
+		 * breaks and parity/frame/orun errors.
+		 */
+		memset(ch->ch_equeue + head, 0, n);
+
+		/* Add to and flip head if needed */
+		head = (head + n) & RQUEUEMASK;
+		total -= n;
+		qleft -= n;
+		ch->ch_rxcount += n;
+	}
+
+	/*
+	 * Create a mask to determine whether we should
+	 * insert the character (if any) into our queue.
+	 */
+	if (ch->ch_c_iflag & IGNBRK)
+		error_mask |= UART_LSR_BI;
+
+	/*
+	 * Now cleanup any leftover bytes still in the UART.
+	 * Also deal with any possible queue overflow here as well.
+	 */
+	while (1) {
+
+		/*
+		 * Its possible we have a linestatus from the loop above
+		 * this, so we "OR" on any extra bits.
+		 */
+		linestatus |= readb(&ch->ch_neo_uart->lsr);
+
+		/*
+		 * If the chip tells us there is no more data pending to
+		 * be read, we can then leave.
+		 * But before we do, cache the linestatus, just in case.
+		 */
+		if (!(linestatus & UART_LSR_DR)) {
+			ch->ch_cached_lsr = linestatus;
+			break;
+		}
+
+		/* No need to store this bit */
+		linestatus &= ~UART_LSR_DR;
+
+		/*
+		 * Since we are grabbing the linestatus register, which
+		 * will reset some bits after our read, we need to ensure
+		 * we don't miss our TX FIFO emptys.
+		 */
+		if (linestatus & (UART_LSR_THRE | UART_17158_TX_AND_FIFO_CLR)) {
+			linestatus &= ~(UART_LSR_THRE | UART_17158_TX_AND_FIFO_CLR);
+			ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM);
+		}
+
+		/*
+		 * Discard character if we are ignoring the error mask.
+		 */
+		if (linestatus & error_mask) {
+			u8 discard;
+			linestatus = 0;
+			memcpy_fromio(&discard, &ch->ch_neo_uart->txrxburst, 1);
+			continue;
+		}
+
+		/*
+		 * If our queue is full, we have no choice but to drop some data.
+		 * The assumption is that HWFLOW or SWFLOW should have stopped
+		 * things way way before we got to this point.
+		 *
+		 * I decided that I wanted to ditch the oldest data first,
+		 * I hope thats okay with everyone? Yes? Good.
+		 */
+		while (qleft < 1) {
+			jsm_printk(READ, INFO, &ch->ch_bd->pci_dev, 
+				"Queue full, dropping DATA:%x LSR:%x\n",
+				ch->ch_rqueue[tail], ch->ch_equeue[tail]);
+
+			ch->ch_r_tail = tail = (tail + 1) & RQUEUEMASK;
+			ch->ch_err_overrun++;
+			qleft++;
+		}
+
+		memcpy_fromio(ch->ch_rqueue + head, &ch->ch_neo_uart->txrxburst, 1);
+		ch->ch_equeue[head] = (u8) linestatus;
+
+		jsm_printk(READ, INFO, &ch->ch_bd->pci_dev, 
+				"DATA/LSR pair: %x %x\n", ch->ch_rqueue[head], ch->ch_equeue[head]);
+
+		/* Ditch any remaining linestatus value. */
+		linestatus = 0;
+
+		/* Add to and flip head if needed */
+		head = (head + 1) & RQUEUEMASK;
+
+		qleft--;
+		ch->ch_rxcount++;
+	}
+
+	/*
+	 * Write new final heads to channel structure.
+	 */
+	ch->ch_r_head = head & RQUEUEMASK;
+	ch->ch_e_head = head & EQUEUEMASK;
+	jsm_input(ch);
+}
+
+static void neo_copy_data_from_queue_to_uart(struct jsm_channel *ch)
+{
+	u16 head;
+	u16 tail;
+	int n;
+	int s;
+	int qlen;
+	u32 len_written = 0;
+
+	if (!ch)
+		return;
+
+	/* No data to write to the UART */ 
+	if (ch->ch_w_tail == ch->ch_w_head)
+		return;
+
+	/* If port is "stopped", don't send any data to the UART */
+	if ((ch->ch_flags & CH_STOP) || (ch->ch_flags & CH_BREAK_SENDING))
+		return;
+	/*
+	 * If FIFOs are disabled. Send data directly to txrx register
+	 */
+	if (!(ch->ch_flags & CH_FIFO_ENABLED)) {
+		u8 lsrbits = readb(&ch->ch_neo_uart->lsr);
+
+		ch->ch_cached_lsr |= lsrbits;
+		if (ch->ch_cached_lsr & UART_LSR_THRE) {
+			ch->ch_cached_lsr &= ~(UART_LSR_THRE);
+
+			writeb(ch->ch_wqueue[ch->ch_w_tail], &ch->ch_neo_uart->txrx);
+			jsm_printk(WRITE, INFO, &ch->ch_bd->pci_dev, 
+					"Tx data: %x\n", ch->ch_wqueue[ch->ch_w_head]);
+			ch->ch_w_tail++;
+			ch->ch_w_tail &= WQUEUEMASK;
+			ch->ch_txcount++;
+		}
+		return;
+	}
+
+	/*
+	 * We have to do it this way, because of the EXAR TXFIFO count bug.
+	 */
+	if (!(ch->ch_flags & (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM)))
+		return; 
+
+	len_written = 0;
+	n = UART_17158_TX_FIFOSIZE - ch->ch_t_tlevel;
+
+	/* cache head and tail of queue */
+	head = ch->ch_w_head & WQUEUEMASK;
+	tail = ch->ch_w_tail & WQUEUEMASK;
+	qlen = (head - tail) & WQUEUEMASK;
+
+	/* Find minimum of the FIFO space, versus queue length */
+	n = min(n, qlen);
+
+	while (n > 0) {
+
+		s = ((head >= tail) ? head : WQUEUESIZE) - tail;
+		s = min(s, n);
+
+		if (s <= 0)
+			break;
+
+		memcpy_toio(&ch->ch_neo_uart->txrxburst, ch->ch_wqueue + tail, s);
+		/* Add and flip queue if needed */
+		tail = (tail + s) & WQUEUEMASK;
+		n -= s;
+		ch->ch_txcount += s;
+		len_written += s;
+	}
+
+	/* Update the final tail */
+	ch->ch_w_tail = tail & WQUEUEMASK;
+
+	if (len_written >= ch->ch_t_tlevel)
+		ch->ch_flags &= ~(CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM);
+
+	if (!jsm_tty_write(&ch->uart_port))
+		uart_write_wakeup(&ch->uart_port);
+}
+
+static void neo_parse_modem(struct jsm_channel *ch, u8 signals)
+{
+	u8 msignals = signals;
+
+	jsm_printk(MSIGS, INFO, &ch->ch_bd->pci_dev, 
+			"neo_parse_modem: port: %d msignals: %x\n", ch->ch_portnum, msignals);
+
+	if (!ch)
+		return;
+
+	/* Scrub off lower bits. They signify delta's, which I don't care about */
+	msignals &= 0xf0;
+
+	if (msignals & UART_MSR_DCD)
+		ch->ch_mistat |= UART_MSR_DCD;
+	else
+		ch->ch_mistat &= ~UART_MSR_DCD;
+
+	if (msignals & UART_MSR_DSR)
+		ch->ch_mistat |= UART_MSR_DSR;
+	else
+		ch->ch_mistat &= ~UART_MSR_DSR;
+
+	if (msignals & UART_MSR_RI)
+		ch->ch_mistat |= UART_MSR_RI;
+	else
+		ch->ch_mistat &= ~UART_MSR_RI;
+
+	if (msignals & UART_MSR_CTS)
+		ch->ch_mistat |= UART_MSR_CTS;
+	else
+		ch->ch_mistat &= ~UART_MSR_CTS;
+
+	jsm_printk(MSIGS, INFO, &ch->ch_bd->pci_dev, 
+			"Port: %d DTR: %d RTS: %d CTS: %d DSR: %d " "RI: %d CD: %d\n",
+		ch->ch_portnum,
+		!!((ch->ch_mistat | ch->ch_mostat) & UART_MCR_DTR),
+		!!((ch->ch_mistat | ch->ch_mostat) & UART_MCR_RTS),
+		!!((ch->ch_mistat | ch->ch_mostat) & UART_MSR_CTS), 
+		!!((ch->ch_mistat | ch->ch_mostat) & UART_MSR_DSR), 
+		!!((ch->ch_mistat | ch->ch_mostat) & UART_MSR_RI),
+		!!((ch->ch_mistat | ch->ch_mostat) & UART_MSR_DCD));
+}
+
+/* Make the UART raise any of the output signals we want up */
+static void neo_assert_modem_signals(struct jsm_channel *ch)
+{
+	u8 out;
+
+	if (!ch)
+		return;
+
+	out = ch->ch_mostat;
+
+	writeb(out, &ch->ch_neo_uart->mcr);
+
+	/* flush write operation */
+	readb(&ch->ch_neo_uart->mcr);
+}
+
+/*
+ * Flush the WRITE FIFO on the Neo.
+ *
+ * NOTE: Channel lock MUST be held before calling this function!
+ */
+static void neo_flush_uart_write(struct jsm_channel *ch)
+{
+	u8 tmp = 0;
+	int i = 0;
+
+	if (!ch)
+		return;
+
+	writeb((UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_XMIT), &ch->ch_neo_uart->isr_fcr);
+
+	for (i = 0; i < 10; i++) {
+
+		/* Check to see if the UART feels it completely flushed the FIFO. */
+		tmp = readb(&ch->ch_neo_uart->isr_fcr);
+		if (tmp & 4) {
+			jsm_printk(IOCTL, INFO, &ch->ch_bd->pci_dev, 
+					"Still flushing TX UART... i: %d\n", i);
+			udelay(10);
+		}
+		else
+			break;
+	}
+
+	ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM);
+}
+
+
+/*
+ * Flush the READ FIFO on the Neo.
+ *
+ * NOTE: Channel lock MUST be held before calling this function!
+ */
+static void neo_flush_uart_read(struct jsm_channel *ch)
+{
+	u8 tmp = 0;
+	int i = 0;
+
+	if (!ch)
+		return;
+
+	writeb((UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR), &ch->ch_neo_uart->isr_fcr);
+
+	for (i = 0; i < 10; i++) {
+
+		/* Check to see if the UART feels it completely flushed the FIFO. */
+		tmp = readb(&ch->ch_neo_uart->isr_fcr);
+		if (tmp & 2) {
+			jsm_printk(IOCTL, INFO, &ch->ch_bd->pci_dev, 
+					"Still flushing RX UART... i: %d\n", i);
+			udelay(10);
+		}
+		else
+			break;
+	}
+}
+
+/*
+ * No locks are assumed to be held when calling this function.
+ */
+void neo_clear_break(struct jsm_channel *ch, int force)
+{
+	u64 lock_flags;
+
+	spin_lock_irqsave(&ch->ch_lock, lock_flags);
+
+	/* Turn break off, and unset some variables */
+	if (ch->ch_flags & CH_BREAK_SENDING) {
+		u8 temp = readb(&ch->ch_neo_uart->lcr);
+		writeb((temp & ~UART_LCR_SBC), &ch->ch_neo_uart->lcr);
+
+		ch->ch_flags &= ~(CH_BREAK_SENDING);
+		jsm_printk(IOCTL, INFO, &ch->ch_bd->pci_dev, 
+				"clear break Finishing UART_LCR_SBC! finished: %lx\n", jiffies);
+	}
+	spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
+}
+
+/*
+ * Parse the ISR register.
+ */
+static inline void neo_parse_isr(struct jsm_board *brd, u32 port)
+{
+	struct jsm_channel *ch;
+	u8 isr;
+	u8 cause;
+	u64 lock_flags;
+
+	if (!brd)
+		return;
+
+	if (port > brd->maxports)
+		return;
+
+	ch = brd->channels[port];
+	if (!ch)
+		return;
+
+	/* Here we try to figure out what caused the interrupt to happen */
+	while (1) {
+
+		isr = readb(&ch->ch_neo_uart->isr_fcr);
+
+		/* Bail if no pending interrupt */
+		if (isr & UART_IIR_NO_INT)
+			break;
+
+		/*
+		 * Yank off the upper 2 bits, which just show that the FIFO's are enabled.
+		 */
+		isr &= ~(UART_17158_IIR_FIFO_ENABLED);
+
+		jsm_printk(INTR, INFO, &ch->ch_bd->pci_dev, 
+				"%s:%d isr: %x\n", __FILE__, __LINE__, isr);
+
+		if (isr & (UART_17158_IIR_RDI_TIMEOUT | UART_IIR_RDI)) {
+			/* Read data from uart -> queue */
+			neo_copy_data_from_uart_to_queue(ch);
+
+			/* Call our tty layer to enforce queue flow control if needed. */
+			spin_lock_irqsave(&ch->ch_lock, lock_flags);
+			jsm_check_queue_flow_control(ch);
+			spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
+		}
+
+		if (isr & UART_IIR_THRI) {
+			/* Transfer data (if any) from Write Queue -> UART. */
+			spin_lock_irqsave(&ch->ch_lock, lock_flags);
+			ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM);
+			spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
+			neo_copy_data_from_queue_to_uart(ch);
+		}
+
+		if (isr & UART_17158_IIR_XONXOFF) {
+			cause = readb(&ch->ch_neo_uart->xoffchar1);
+
+			jsm_printk(INTR, INFO, &ch->ch_bd->pci_dev, 
+					"Port %d. Got ISR_XONXOFF: cause:%x\n", port, cause);
+
+			/*
+			 * Since the UART detected either an XON or
+			 * XOFF match, we need to figure out which
+			 * one it was, so we can suspend or resume data flow.
+			 */
+			spin_lock_irqsave(&ch->ch_lock, lock_flags);
+			if (cause == UART_17158_XON_DETECT) {
+				/* Is output stopped right now, if so, resume it */
+				if (brd->channels[port]->ch_flags & CH_STOP) {
+					ch->ch_flags &= ~(CH_STOP);
+				}
+				jsm_printk(INTR, INFO, &ch->ch_bd->pci_dev, 
+						"Port %d. XON detected in incoming data\n", port);
+			} 
+			else if (cause == UART_17158_XOFF_DETECT) {
+				if (!(brd->channels[port]->ch_flags & CH_STOP)) {
+					ch->ch_flags |= CH_STOP;
+					jsm_printk(INTR, INFO, &ch->ch_bd->pci_dev, 
+							"Setting CH_STOP\n");
+				}
+				jsm_printk(INTR, INFO, &ch->ch_bd->pci_dev, 
+						"Port: %d. XOFF detected in incoming data\n", port);
+			}
+			spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
+		}
+
+		if (isr & UART_17158_IIR_HWFLOW_STATE_CHANGE) {
+			/*
+			 * If we get here, this means the hardware is doing auto flow control.
+			 * Check to see whether RTS/DTR or CTS/DSR caused this interrupt.
+			 */
+			cause = readb(&ch->ch_neo_uart->mcr);
+
+			/* Which pin is doing auto flow? RTS or DTR? */
+			spin_lock_irqsave(&ch->ch_lock, lock_flags);
+			if ((cause & 0x4) == 0) {
+				if (cause & UART_MCR_RTS)
+					ch->ch_mostat |= UART_MCR_RTS;
+				else
+					ch->ch_mostat &= ~(UART_MCR_RTS);
+			} else {
+				if (cause & UART_MCR_DTR)
+					ch->ch_mostat |= UART_MCR_DTR;
+				else
+					ch->ch_mostat &= ~(UART_MCR_DTR);
+			}
+			spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
+		}
+
+		/* Parse any modem signal changes */
+		jsm_printk(INTR, INFO, &ch->ch_bd->pci_dev, 
+				"MOD_STAT: sending to parse_modem_sigs\n");
+		neo_parse_modem(ch, readb(&ch->ch_neo_uart->msr));
+	}
+}
+
+static inline void neo_parse_lsr(struct jsm_board *brd, u32 port)
+{
+	struct jsm_channel *ch;
+	int linestatus;
+	u64 lock_flags;
+
+	if (!brd)
+		return;
+
+	if (port > brd->maxports)
+		return;
+
+	ch = brd->channels[port];
+	if (!ch)
+		return;
+
+	linestatus = readb(&ch->ch_neo_uart->lsr);
+
+	jsm_printk(INTR, INFO, &ch->ch_bd->pci_dev, 
+			"%s:%d port: %d linestatus: %x\n", __FILE__, __LINE__, port, linestatus);
+
+	ch->ch_cached_lsr |= linestatus;
+
+	if (ch->ch_cached_lsr & UART_LSR_DR) {
+		/* Read data from uart -> queue */
+		neo_copy_data_from_uart_to_queue(ch);
+		spin_lock_irqsave(&ch->ch_lock, lock_flags);
+		jsm_check_queue_flow_control(ch);
+		spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
+	}
+
+	/*
+	 * This is a special flag. It indicates that at least 1
+	 * RX error (parity, framing, or break) has happened.
+	 * Mark this in our struct, which will tell me that I have
+	 *to do the special RX+LSR read for this FIFO load.
+	 */
+	if (linestatus & UART_17158_RX_FIFO_DATA_ERROR)
+		jsm_printk(INTR, DEBUG, &ch->ch_bd->pci_dev, 
+			"%s:%d Port: %d Got an RX error, need to parse LSR\n",
+			__FILE__, __LINE__, port);
+
+	/*
+	 * The next 3 tests should *NOT* happen, as the above test
+	 * should encapsulate all 3... At least, thats what Exar says.
+	 */
+
+	if (linestatus & UART_LSR_PE) {
+		ch->ch_err_parity++;
+		jsm_printk(INTR, DEBUG, &ch->ch_bd->pci_dev, 
+			"%s:%d Port: %d. PAR ERR!\n", __FILE__, __LINE__, port);
+	}
+
+	if (linestatus & UART_LSR_FE) {
+		ch->ch_err_frame++;
+		jsm_printk(INTR, DEBUG, &ch->ch_bd->pci_dev, 
+			"%s:%d Port: %d. FRM ERR!\n", __FILE__, __LINE__, port);
+	}
+
+	if (linestatus & UART_LSR_BI) {
+		ch->ch_err_break++;
+		jsm_printk(INTR, DEBUG, &ch->ch_bd->pci_dev, 
+			"%s:%d Port: %d. BRK INTR!\n", __FILE__, __LINE__, port);
+	}
+
+	if (linestatus & UART_LSR_OE) {
+		/*
+		 * Rx Oruns. Exar says that an orun will NOT corrupt
+		 * the FIFO. It will just replace the holding register
+		 * with this new data byte. So basically just ignore this.
+		 * Probably we should eventually have an orun stat in our driver...
+		 */
+		ch->ch_err_overrun++;
+		jsm_printk(INTR, DEBUG, &ch->ch_bd->pci_dev, 
+			"%s:%d Port: %d. Rx Overrun!\n", __FILE__, __LINE__, port);
+	}
+
+	if (linestatus & UART_LSR_THRE) {
+		spin_lock_irqsave(&ch->ch_lock, lock_flags);
+		ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM);
+		spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
+
+		/* Transfer data (if any) from Write Queue -> UART. */
+		neo_copy_data_from_queue_to_uart(ch);
+	}
+	else if (linestatus & UART_17158_TX_AND_FIFO_CLR) {
+		spin_lock_irqsave(&ch->ch_lock, lock_flags);
+		ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM);
+		spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
+
+		/* Transfer data (if any) from Write Queue -> UART. */
+		neo_copy_data_from_queue_to_uart(ch);
+	}
+}
+
+/*
+ * neo_param()
+ * Send any/all changes to the line to the UART.
+ */
+static void neo_param(struct jsm_channel *ch)
+{
+	u8 lcr = 0;
+	u8 uart_lcr = 0;
+	u8 ier = 0;
+	u32 baud = 9600;
+	int quot = 0;
+	struct jsm_board *bd;
+
+	bd = ch->ch_bd;
+	if (!bd)
+		return;
+
+	/*
+	 * If baud rate is zero, flush queues, and set mval to drop DTR.
+	 */
+	if ((ch->ch_c_cflag & (CBAUD)) == 0) {
+		ch->ch_r_head = ch->ch_r_tail = 0;
+		ch->ch_e_head = ch->ch_e_tail = 0;
+		ch->ch_w_head = ch->ch_w_tail = 0;
+
+		neo_flush_uart_write(ch);
+		neo_flush_uart_read(ch);
+
+		ch->ch_flags |= (CH_BAUD0);
+		ch->ch_mostat &= ~(UART_MCR_RTS | UART_MCR_DTR);
+		neo_assert_modem_signals(ch);
+		ch->ch_old_baud = 0;
+		return;
+
+	} else if (ch->ch_custom_speed) {
+			baud = ch->ch_custom_speed;
+			if (ch->ch_flags & CH_BAUD0)
+				ch->ch_flags &= ~(CH_BAUD0);
+		} else {
+			int iindex = 0;
+			int jindex = 0;
+
+			const u64 bauds[4][16] = {
+				{ 
+					0,	50,	75,	110,
+					134,	150,	200,	300,
+					600,	1200,	1800,	2400,
+					4800,	9600,	19200,	38400 },
+				{ 
+					0,	57600,	115200, 230400,
+					460800, 150,	200,	921600,
+					600,	1200,	1800,	2400,
+					4800,	9600,	19200,	38400 },
+				{ 
+					0,	57600,	76800, 115200,
+					131657, 153600, 230400, 460800,
+					921600, 1200,	1800,	2400,
+					4800,	9600,	19200,	38400 },
+				{ 
+					0,	57600,	115200, 230400,
+					460800, 150,	200,	921600,
+					600,	1200,	1800,	2400,
+					4800,	9600,	19200,	38400 }
+			};
+
+			baud = C_BAUD(ch->uart_port.info->tty) & 0xff;
+
+			if (ch->ch_c_cflag & CBAUDEX)
+				iindex = 1;
+
+			jindex = baud;
+
+			if ((iindex >= 0) && (iindex < 4) && (jindex >= 0) && (jindex < 16))
+				baud = bauds[iindex][jindex];
+			else {
+				jsm_printk(IOCTL, DEBUG, &ch->ch_bd->pci_dev, 
+					"baud indices were out of range (%d)(%d)",
+				iindex, jindex);
+				baud = 0;
+			}
+
+			if (baud == 0)
+				baud = 9600;
+
+			if (ch->ch_flags & CH_BAUD0)
+				ch->ch_flags &= ~(CH_BAUD0);
+		}
+
+	if (ch->ch_c_cflag & PARENB)
+		lcr |= UART_LCR_PARITY;
+
+	if (!(ch->ch_c_cflag & PARODD))
+		lcr |= UART_LCR_EPAR;
+
+	/* 
+	 * Not all platforms support mark/space parity,
+	 * so this will hide behind an ifdef.
+	 */
+#ifdef CMSPAR
+	if (ch->ch_c_cflag & CMSPAR) 
+		lcr |= UART_LCR_SPAR;
+#endif
+
+	if (ch->ch_c_cflag & CSTOPB)
+		lcr |= UART_LCR_STOP;
+
+	switch (ch->ch_c_cflag & CSIZE) {
+		case CS5:
+			lcr |= UART_LCR_WLEN5;
+			break;
+		case CS6:
+			lcr |= UART_LCR_WLEN6;
+			break;
+		case CS7:
+			lcr |= UART_LCR_WLEN7;
+			break;
+		case CS8:
+		default:
+			lcr |= UART_LCR_WLEN8;
+		break;
+	}
+
+	ier = readb(&ch->ch_neo_uart->ier);
+	uart_lcr = readb(&ch->ch_neo_uart->lcr);
+
+	if (baud == 0)
+		baud = 9600;
+
+	quot = ch->ch_bd->bd_dividend / baud;
+
+	if (quot != 0) {
+		ch->ch_old_baud = baud;
+		writeb(UART_LCR_DLAB, &ch->ch_neo_uart->lcr);
+		writeb((quot & 0xff), &ch->ch_neo_uart->txrx);
+		writeb((quot >> 8), &ch->ch_neo_uart->ier);
+		writeb(lcr, &ch->ch_neo_uart->lcr);
+	}
+
+	if (uart_lcr != lcr)
+		writeb(lcr, &ch->ch_neo_uart->lcr);
+
+	if (ch->ch_c_cflag & CREAD)
+		ier |= (UART_IER_RDI | UART_IER_RLSI);
+
+	ier |= (UART_IER_THRI | UART_IER_MSI);
+
+	writeb(ier, &ch->ch_neo_uart->ier);
+
+	/* Set new start/stop chars */
+	neo_set_new_start_stop_chars(ch);
+
+	if (ch->ch_c_cflag & CRTSCTS)
+		neo_set_cts_flow_control(ch);
+	else if (ch->ch_c_iflag & IXON) {
+		/* If start/stop is set to disable, then we should disable flow control */
+		if ((ch->ch_startc == __DISABLED_CHAR) || (ch->ch_stopc == __DISABLED_CHAR))
+			neo_set_no_output_flow_control(ch);
+		else
+			neo_set_ixon_flow_control(ch);
+	}
+	else
+		neo_set_no_output_flow_control(ch);
+
+	if (ch->ch_c_cflag & CRTSCTS)
+		neo_set_rts_flow_control(ch);
+	else if (ch->ch_c_iflag & IXOFF) {
+		/* If start/stop is set to disable, then we should disable flow control */
+		if ((ch->ch_startc == __DISABLED_CHAR) || (ch->ch_stopc == __DISABLED_CHAR))
+			neo_set_no_input_flow_control(ch);
+		else
+			neo_set_ixoff_flow_control(ch);
+	} 
+	else
+		neo_set_no_input_flow_control(ch);
+	/*
+	 * Adjust the RX FIFO Trigger level if baud is less than 9600.
+	 * Not exactly elegant, but this is needed because of the Exar chip's
+	 * delay on firing off the RX FIFO interrupt on slower baud rates.
+	 */
+	if (baud < 9600) {
+		writeb(1, &ch->ch_neo_uart->rfifo);
+		ch->ch_r_tlevel = 1;
+	}
+
+	neo_assert_modem_signals(ch);
+
+	/* Get current status of the modem signals now */
+	neo_parse_modem(ch, readb(&ch->ch_neo_uart->msr));
+	return;
+}
+
+/*
+ * jsm_neo_intr()
+ *
+ * Neo specific interrupt handler.
+ */
+static irqreturn_t neo_intr(int irq, void *voidbrd, struct pt_regs *regs)
+{
+	struct jsm_board *brd = (struct jsm_board *) voidbrd;
+	struct jsm_channel *ch;
+	int port = 0;
+	int type = 0;
+	int current_port;
+	u32 tmp;
+	u32 uart_poll;
+	unsigned long lock_flags;
+	unsigned long lock_flags2;
+	int outofloop_count = 0;
+
+	brd->intr_count++;
+
+	/* Lock out the slow poller from running on this board. */
+	spin_lock_irqsave(&brd->bd_intr_lock, lock_flags);
+
+	/*
+	 * Read in "extended" IRQ information from the 32bit Neo register.
+	 * Bits 0-7: What port triggered the interrupt.
+	 * Bits 8-31: Each 3bits indicate what type of interrupt occurred.
+	 */
+	uart_poll = readl(brd->re_map_membase + UART_17158_POLL_ADDR_OFFSET);
+
+	jsm_printk(INTR, INFO, &brd->pci_dev, 
+		"%s:%d uart_poll: %x\n", __FILE__, __LINE__, uart_poll);
+
+	if (!uart_poll) {
+		jsm_printk(INTR, INFO, &brd->pci_dev, 
+			"Kernel interrupted to me, but no pending interrupts...\n");
+		spin_unlock_irqrestore(&brd->bd_intr_lock, lock_flags);
+		return IRQ_NONE;
+	}
+
+	/* At this point, we have at least SOMETHING to service, dig further... */
+
+	current_port = 0;
+
+	/* Loop on each port */
+	while (((uart_poll & 0xff) != 0) && (outofloop_count < 0xff)){
+
+		tmp = uart_poll;
+		outofloop_count++;
+
+		/* Check current port to see if it has interrupt pending */
+		if ((tmp & jsm_offset_table[current_port]) != 0) {
+			port = current_port;
+			type = tmp >> (8 + (port * 3));
+			type &= 0x7;
+		} else {
+			current_port++;
+			continue;
+		}
+
+		jsm_printk(INTR, INFO, &brd->pci_dev, 
+		"%s:%d port: %x type: %x\n", __FILE__, __LINE__, port, type);
+
+		/* Remove this port + type from uart_poll */
+		uart_poll &= ~(jsm_offset_table[port]);
+
+		if (!type) {
+			/* If no type, just ignore it, and move onto next port */
+			jsm_printk(INTR, ERR, &brd->pci_dev, 
+				"Interrupt with no type! port: %d\n", port);
+			continue;
+		}
+
+		/* Switch on type of interrupt we have */
+		switch (type) {
+
+		case UART_17158_RXRDY_TIMEOUT:
+			/*
+			 * RXRDY Time-out is cleared by reading data in the
+			* RX FIFO until it falls below the trigger level.
+			 */
+
+			/* Verify the port is in range. */
+			if (port > brd->nasync)
+				continue;
+
+			ch = brd->channels[port];
+			neo_copy_data_from_uart_to_queue(ch);
+
+			/* Call our tty layer to enforce queue flow control if needed. */
+			spin_lock_irqsave(&ch->ch_lock, lock_flags2);
+			jsm_check_queue_flow_control(ch);
+			spin_unlock_irqrestore(&ch->ch_lock, lock_flags2);
+
+			continue;
+
+		case UART_17158_RX_LINE_STATUS:
+			/*
+			 * RXRDY and RX LINE Status (logic OR of LSR[4:1])
+			 */
+			neo_parse_lsr(brd, port);
+			continue;
+
+		case UART_17158_TXRDY:
+			/*
+			 * TXRDY interrupt clears after reading ISR register for the UART channel.
+			 */
+
+			/*
+			 * Yes, this is odd...
+			 * Why would I check EVERY possibility of type of
+			 * interrupt, when we know its TXRDY???
+			 * Becuz for some reason, even tho we got triggered for TXRDY,
+			 * it seems to be occassionally wrong. Instead of TX, which
+			 * it should be, I was getting things like RXDY too. Weird.
+			 */
+			neo_parse_isr(brd, port);
+			continue;
+
+		case UART_17158_MSR:
+			/*
+			 * MSR or flow control was seen.
+			 */
+			neo_parse_isr(brd, port);
+			continue;
+
+		default:
+			/*
+			 * The UART triggered us with a bogus interrupt type.
+			 * It appears the Exar chip, when REALLY bogged down, will throw
+			 * these once and awhile.
+			 * Its harmless, just ignore it and move on.
+			 */
+			jsm_printk(INTR, ERR, &brd->pci_dev, 
+				"%s:%d Unknown Interrupt type: %x\n", __FILE__, __LINE__, type);
+			continue;
+		}
+	}
+
+	spin_unlock_irqrestore(&brd->bd_intr_lock, lock_flags);
+
+	jsm_printk(INTR, INFO, &brd->pci_dev, "finish.\n");
+	return IRQ_HANDLED;
+}
+
+/*
+ * Neo specific way of turning off the receiver.
+ * Used as a way to enforce queue flow control when in
+ * hardware flow control mode.
+ */
+static void neo_disable_receiver(struct jsm_channel *ch)
+{
+	u8 tmp = readb(&ch->ch_neo_uart->ier);
+	tmp &= ~(UART_IER_RDI);
+	writeb(tmp, &ch->ch_neo_uart->ier);
+}
+
+
+/*
+ * Neo specific way of turning on the receiver.
+ * Used as a way to un-enforce queue flow control when in
+ * hardware flow control mode.
+ */
+static void neo_enable_receiver(struct jsm_channel *ch)
+{
+	u8 tmp = readb(&ch->ch_neo_uart->ier);
+	tmp |= (UART_IER_RDI);
+	writeb(tmp, &ch->ch_neo_uart->ier);
+}
+
+static void neo_send_start_character(struct jsm_channel *ch)
+{
+	unsigned char xdata;
+	if (!ch)
+		return;
+
+	if (ch->ch_startc != __DISABLED_CHAR) {
+		ch->ch_xon_sends++;
+		writeb(ch->ch_startc, &ch->ch_neo_uart->txrx);
+
+		/* flush write operation */
+		xdata = readb(&ch->ch_neo_uart->txrx);
+	}
+}
+
+static void neo_send_stop_character(struct jsm_channel *ch)
+{
+	unsigned char xdata;
+
+	if (!ch)
+		return;
+
+	if (ch->ch_stopc != __DISABLED_CHAR) {
+		ch->ch_xoff_sends++;
+		writeb(ch->ch_stopc, &ch->ch_neo_uart->txrx);
+
+		/* flush write operation */
+		xdata = readb(&ch->ch_neo_uart->txrx);
+	}
+}
+
+/*
+ * neo_uart_init
+ */
+static void neo_uart_init(struct jsm_channel *ch)
+{
+	writeb(0, &ch->ch_neo_uart->ier);
+	writeb(0, &ch->ch_neo_uart->efr);
+	writeb(UART_EFR_ECB, &ch->ch_neo_uart->efr);
+
+	/* Clear out UART and FIFO */
+	readb(&ch->ch_neo_uart->txrx);
+	writeb((UART_FCR_ENABLE_FIFO|UART_FCR_CLEAR_RCVR|UART_FCR_CLEAR_XMIT), &ch->ch_neo_uart->isr_fcr);
+	readb(&ch->ch_neo_uart->lsr);
+	readb(&ch->ch_neo_uart->msr);
+
+	ch->ch_flags |= CH_FIFO_ENABLED;
+
+	/* Assert any signals we want up */
+	writeb(ch->ch_mostat, &ch->ch_neo_uart->mcr);
+}
+
+/*
+ * Make the UART completely turn off.
+ */
+static void neo_uart_off(struct jsm_channel *ch)
+{
+	/* Turn off UART enhanced bits */
+	writeb(0, &ch->ch_neo_uart->efr);
+
+	/* Stop all interrupts from occurring. */
+	writeb(0, &ch->ch_neo_uart->ier);
+}
+
+static u32 neo_get_uart_bytes_left(struct jsm_channel *ch)
+{
+	u8 left = 0;
+	u8 lsr = readb(&ch->ch_neo_uart->lsr);
+
+	/* We must cache the LSR as some of the bits get reset once read... */
+	ch->ch_cached_lsr |= lsr;
+ 
+	/* Determine whether the Transmitter is empty or not */
+	if (!(lsr & UART_LSR_TEMT))
+		left = 1;
+	else {
+		ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM);
+		left = 0;
+	}
+
+	return left;
+}
+
+/* Channel lock MUST be held by the calling function! */
+static void neo_send_break(struct jsm_channel *ch)
+{
+	/*
+	 * Set the time we should stop sending the break.
+	 * If we are already sending a break, toss away the existing
+	 * time to stop, and use this new value instead.
+	 */
+
+	/* Tell the UART to start sending the break */
+	if (!(ch->ch_flags & CH_BREAK_SENDING)) {
+		u8 temp = readb(&ch->ch_neo_uart->lcr);
+		writeb((temp | UART_LCR_SBC), &ch->ch_neo_uart->lcr);
+		ch->ch_flags |= (CH_BREAK_SENDING);
+	}
+}
+
+/*
+ * neo_send_immediate_char.
+ *
+ * Sends a specific character as soon as possible to the UART,
+ * jumping over any bytes that might be in the write queue.
+ *
+ * The channel lock MUST be held by the calling function.
+ */
+static void neo_send_immediate_char(struct jsm_channel *ch, unsigned char c)
+{
+	if (!ch)
+		return;
+
+	writeb(c, &ch->ch_neo_uart->txrx);
+}
+
+struct board_ops jsm_neo_ops = {
+	.intr				= neo_intr,
+	.uart_init			= neo_uart_init,
+	.uart_off			= neo_uart_off,
+	.param				= neo_param,
+	.assert_modem_signals		= neo_assert_modem_signals,
+	.flush_uart_write		= neo_flush_uart_write,
+	.flush_uart_read		= neo_flush_uart_read,
+	.disable_receiver		= neo_disable_receiver,
+	.enable_receiver		= neo_enable_receiver,
+	.send_break			= neo_send_break,
+	.clear_break			= neo_clear_break,
+	.send_start_character		= neo_send_start_character,
+	.send_stop_character		= neo_send_stop_character,
+	.copy_data_from_queue_to_uart	= neo_copy_data_from_queue_to_uart,
+	.get_uart_bytes_left		= neo_get_uart_bytes_left,
+	.send_immediate_char		= neo_send_immediate_char
+};

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [ patch 4/5] drivers/serial/jsm: new serial device driver
  2005-03-09 18:58                     ` Greg KH
                                         ` (2 preceding siblings ...)
  2005-03-11 15:38                       ` [ patch 3/5] " Wen Xiong
@ 2005-03-11 15:38                       ` Wen Xiong
  2005-03-11 15:38                       ` [ patch 5/5] " Wen Xiong
  4 siblings, 0 replies; 34+ messages in thread
From: Wen Xiong @ 2005-03-11 15:38 UTC (permalink / raw)
  To: Greg KH; +Cc: Wen Xiong, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 105 bytes --]

The fouth patch for jsm serial device driver.

Signed-off-by: Wen Xiong <wendyx@us.ltcfwd.linux.ibm.com>

[-- Attachment #2: patch4.jasmine --]
[-- Type: text/plain, Size: 15820 bytes --]

diff -Nuar linux-2.6.11.org/drivers/serial/jsm/jsm.h linux-2.6.11.new/drivers/serial/jsm/jsm.h
--- linux-2.6.11.org/drivers/serial/jsm/jsm.h	1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.11.new/drivers/serial/jsm/jsm.h	2005-03-10 16:35:31.942919160 -0600
@@ -0,0 +1,437 @@
+/************************************************************************
+ * Copyright 2003 Digi International (www.digi.com)
+ *
+ * Copyright (C) 2004 IBM Corporation. All rights reserved.
+ *
+ * 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, or (at your option)
+ * any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; without even the 
+ * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License 
+ * along with this program; if not, write to the Free Software 
+ * Foundation, Inc., 59 * Temple Place - Suite 330, Boston,
+ * MA  02111-1307, USA.
+ *
+ * Contact Information:
+ * Scott H Kilau <Scott_Kilau@digi.com>
+ * Wendy Xiong   <wendyx@us.ltcfwd.linux.ibm.com>
+ *
+ ***********************************************************************/
+
+#ifndef __JSM_DRIVER_H
+#define __JSM_DRIVER_H
+
+#include <linux/kernel.h>
+#include <linux/version.h>
+#include <linux/types.h>	/* To pick up the varions Linux types */
+#include <linux/tty.h>
+#include <linux/serial_core.h>
+#include <linux/device.h>
+
+/*
+ * Debugging levels can be set using debug insmod variable
+ * They can also be compiled out completely.
+ */
+enum {
+	DBG_INIT	= 0x01,
+	DBG_BASIC	= 0x02,
+	DBG_CORE	= 0x04,
+	DBG_OPEN	= 0x08,
+	DBG_CLOSE	= 0x10,
+	DBG_READ	= 0x20,
+	DBG_WRITE	= 0x40,
+	DBG_IOCTL	= 0x80,
+	DBG_PROC	= 0x100,
+	DBG_PARAM	= 0x200,
+	DBG_PSCAN	= 0x400,
+	DBG_EVENT	= 0x800,
+	DBG_DRAIN	= 0x1000,
+	DBG_MSIGS	= 0x2000,
+	DBG_MGMT	= 0x4000,
+	DBG_INTR	= 0x8000,
+	DBG_CARR	= 0x10000,
+};
+
+#define jsm_printk(nlevel, klevel, pdev, fmt, args...)	\
+	if ((DBG_##nlevel & jsm_debug))			\
+	dev_printk(KERN_##klevel, pdev->dev, fmt, ## args)
+
+#define MAXPORTS	8
+#define MAX_STOPS_SENT	5
+
+/* Board type definitions */
+ 
+#define T_NEO		0000
+#define T_CLASSIC	0001
+#define T_PCIBUS	0400 
+ 
+/* Board State Definitions */
+ 
+#define BD_RUNNING	0x0
+#define BD_REASON	0x7f 
+#define BD_NOTFOUND	0x1
+#define BD_NOIOPORT	0x2
+#define BD_NOMEM	0x3
+#define BD_NOBIOS	0x4 
+#define BD_NOFEP	0x5
+#define BD_FAILED	0x6
+#define BD_ALLOCATED	0x7
+#define BD_TRIBOOT	0x8
+#define BD_BADKME	0x80
+
+
+/* 4 extra for alignment play space */
+#define WRITEBUFLEN	((4096) + 4)
+#define MYFLIPLEN	N_TTY_BUF_SIZE
+
+#define JSM_VERSION	"jsm: 1.1-1-INKERNEL"
+#define JSM_PARTNUM	"40002438_A-INKERNEL"
+
+/*
+ * All the possible states the driver can be while being loaded.
+ */
+enum {
+	DRIVER_INITIALIZED = 0,
+	DRIVER_READY
+};
+
+/*
+ * All the possible states the board can be while booting up.
+ */
+enum {
+	BOARD_FAILED = 0,
+	BOARD_FOUND,
+	BOARD_READY
+};
+
+struct board_id {
+	u8 *name;
+	u32 maxports;
+};
+
+struct jsm_board;
+struct jsm_channel;
+
+/************************************************************************
+ * Per board operations structure					*
+ ************************************************************************/
+struct board_ops {
+	irqreturn_t (*intr) (int irq, void *voidbrd, struct pt_regs *regs);
+	void (*uart_init) (struct jsm_channel *ch);
+	void (*uart_off) (struct jsm_channel *ch);
+	void (*param) (struct jsm_channel *ch);
+	void (*assert_modem_signals) (struct jsm_channel *ch);
+	void (*flush_uart_write) (struct jsm_channel *ch);
+	void (*flush_uart_read) (struct jsm_channel *ch);
+	void (*disable_receiver) (struct jsm_channel *ch);
+	void (*enable_receiver) (struct jsm_channel *ch);
+	void (*send_break) (struct jsm_channel *ch);
+	void (*clear_break) (struct jsm_channel *ch, int);
+	void (*send_start_character) (struct jsm_channel *ch);
+	void (*send_stop_character) (struct jsm_channel *ch);
+	void (*copy_data_from_queue_to_uart) (struct jsm_channel *ch);
+	u32 (*get_uart_bytes_left) (struct jsm_channel *ch);
+	void (*send_immediate_char) (struct jsm_channel *ch, unsigned char);
+};
+
+
+/*
+ *	Per-board information
+ */
+struct jsm_board
+{
+	int		boardnum;	/* Board number: 0-32 */
+
+	int		type;		/* Type of board */
+	char		*name;		/* Product Name */
+	u8		rev;		/* PCI revision ID */
+	struct pci_dev	*pci_dev;
+	u32		maxports;	/* MAX ports this board can handle */
+
+	spinlock_t	bd_lock;	/* Used to protect board */
+
+	spinlock_t	bd_intr_lock;	/* Used to protect the poller tasklet and
+					 * the interrupt routine from each other.
+					 */
+
+	u32		state;		/* State of card. */
+	wait_queue_head_t state_wait;	/* Place to sleep on for state change */
+
+	u32		nasync;		/* Number of ports on card */
+
+	u32		irq;		/* Interrupt request number */
+	u64		intr_count;	/* Count of interrupts */
+
+	u64		membase;	/* Start of base memory of the card */
+	u64		membase_end;	/* End of base memory of the card */
+
+	u8		*re_map_membase;/* Remapped memory of the card */
+
+	u64		iobase;		/* Start of io base of the card */
+	u64		iobase_end;	/* End of io base of the card */
+
+	u32		bd_uart_offset;	/* Space between each UART */
+
+	struct jsm_channel *channels[MAXPORTS]; /* array of pointers to our channels. */
+	char		*flipbuf;	/* Our flip buffer, alloced if board is found */
+
+	u16		dpatype;	/* The board "type", as defined by DPA */
+	u16		dpastatus;	/* The board "status", as defined by DPA */
+
+	u32		bd_dividend;	/* Board/UARTs specific dividend */
+
+	struct board_ops *bd_ops;
+
+	struct list_head jsm_board_entry;
+};
+
+/************************************************************************ 
+ * Device flag definitions for ch_flags.
+ ************************************************************************/
+#define CH_PRON		0x0001		/* Printer on string		*/
+#define CH_STOP		0x0002		/* Output is stopped		*/
+#define CH_STOPI	0x0004		/* Input is stopped		*/
+#define CH_CD		0x0008		/* Carrier is present		*/
+#define CH_FCAR		0x0010		/* Carrier forced on		*/
+#define CH_HANGUP	0x0020		/* Hangup received		*/
+
+#define CH_RECEIVER_OFF	0x0040		/* Receiver is off		*/
+#define CH_OPENING	0x0080		/* Port in fragile open state	*/
+#define CH_CLOSING	0x0100		/* Port in fragile close state	*/
+#define CH_FIFO_ENABLED 0x0200		/* Port has FIFOs enabled	*/
+#define CH_TX_FIFO_EMPTY 0x0400		/* TX Fifo is completely empty	*/
+#define CH_TX_FIFO_LWM	0x0800		/* TX Fifo is below Low Water	*/
+#define CH_BREAK_SENDING 0x1000		/* Break is being sent		*/
+#define CH_LOOPBACK 0x2000		/* Channel is in lookback mode	*/
+#define CH_FLIPBUF_IN_USE 0x4000	/* Channel's flipbuf is in use	*/
+#define CH_BAUD0	0x08000		/* Used for checking B0 transitions */
+
+/* Our Read/Error/Write queue sizes */
+#define RQUEUEMASK	0x1FFF		/* 8 K - 1 */
+#define EQUEUEMASK	0x1FFF		/* 8 K - 1 */
+#define WQUEUEMASK	0x0FFF		/* 4 K - 1 */
+#define RQUEUESIZE	(RQUEUEMASK + 1)
+#define EQUEUESIZE	RQUEUESIZE
+#define WQUEUESIZE	(WQUEUEMASK + 1)
+
+
+/************************************************************************ 
+ * Channel information structure.
+ ************************************************************************/
+struct jsm_channel {
+	struct uart_port uart_port;
+	struct jsm_board	*ch_bd;		/* Board structure pointer	*/
+
+	spinlock_t	ch_lock;	/* provide for serialization */
+	wait_queue_head_t ch_flags_wait;
+
+	u32		ch_portnum;	/* Port number, 0 offset.	*/
+	u32		ch_open_count;	/* open count			*/
+	u32		ch_flags;	/* Channel flags		*/
+
+	u64		ch_close_delay;	/* How long we should drop RTS/DTR for */
+
+	u64		ch_cpstime;	/* Time for CPS calculations	*/
+
+	tcflag_t	ch_c_iflag;	/* channel iflags		*/
+	tcflag_t	ch_c_cflag;	/* channel cflags		*/
+	tcflag_t	ch_c_oflag;	/* channel oflags		*/
+	tcflag_t	ch_c_lflag;	/* channel lflags		*/
+	u8		ch_stopc;	/* Stop character		*/
+	u8		ch_startc;	/* Start character		*/
+
+	u32		ch_old_baud;	/* Cache of the current baud */
+	u32		ch_custom_speed;/* Custom baud, if set */
+
+	u32		ch_wopen;	/* Waiting for open process cnt */
+
+	u8		ch_mostat;	/* FEP output modem status	*/
+	u8		ch_mistat;	/* FEP input modem status	*/
+
+	struct neo_uart_struct *ch_neo_uart;	/* Pointer to the "mapped" UART struct */
+	u8		ch_cached_lsr;	/* Cached value of the LSR register */
+
+	u8		*ch_rqueue;	/* Our read queue buffer - malloc'ed */
+	u16		ch_r_head;	/* Head location of the read queue */
+	u16		ch_r_tail;	/* Tail location of the read queue */
+
+	u8		*ch_equeue;	/* Our error queue buffer - malloc'ed */
+	u16		ch_e_head;	/* Head location of the error queue */
+	u16		ch_e_tail;	/* Tail location of the error queue */
+
+	u8		*ch_wqueue;	/* Our write queue buffer - malloc'ed */
+	u16		ch_w_head;	/* Head location of the write queue */
+	u16		ch_w_tail;	/* Tail location of the write queue */
+
+	u64		ch_rxcount;	/* total of data received so far */
+	u64		ch_txcount;	/* total of data transmitted so far */
+
+	u8		ch_r_tlevel;	/* Receive Trigger level */
+	u8		ch_t_tlevel;	/* Transmit Trigger level */
+
+	u8		ch_r_watermark;	/* Receive Watermark */
+
+
+	u32		ch_stops_sent;	/* How many times I have sent a stop character
+					 * to try to stop the other guy sending.
+					 */
+	u64		ch_err_parity;	/* Count of parity errors on channel */
+	u64		ch_err_frame;	/* Count of framing errors on channel */
+	u64		ch_err_break;	/* Count of breaks on channel */
+	u64		ch_err_overrun; /* Count of overruns on channel */
+
+	u64		ch_xon_sends;	/* Count of xons transmitted */
+	u64		ch_xoff_sends;	/* Count of xoffs transmitted */
+};
+
+
+/************************************************************************ 
+ * Per channel/port NEO UART structure					*
+ ************************************************************************
+ *		Base Structure Entries Usage Meanings to Host		*
+ *									*
+ *	W = read write		R = read only				* 
+ *			U = Unused.					*
+ ************************************************************************/
+
+struct neo_uart_struct {
+	 u8 txrx;		/* WR	RHR/THR - Holding Reg */
+	 u8 ier;		/* WR	IER - Interrupt Enable Reg */
+	 u8 isr_fcr;		/* WR	ISR/FCR - Interrupt Status Reg/Fifo Control Reg */
+	 u8 lcr;		/* WR	LCR - Line Control Reg */
+	 u8 mcr;		/* WR	MCR - Modem Control Reg */
+	 u8 lsr;		/* WR	LSR - Line Status Reg */
+	 u8 msr;		/* WR	MSR - Modem Status Reg */
+	 u8 spr;		/* WR	SPR - Scratch Pad Reg */
+	 u8 fctr;		/* WR	FCTR - Feature Control Reg */
+	 u8 efr;		/* WR	EFR - Enhanced Function Reg */
+	 u8 tfifo;		/* WR	TXCNT/TXTRG - Transmit FIFO Reg */	
+	 u8 rfifo;		/* WR	RXCNT/RXTRG - Recieve FIFO Reg */
+	 u8 xoffchar1;	/* WR	XOFF 1 - XOff Character 1 Reg */
+	 u8 xoffchar2;	/* WR	XOFF 2 - XOff Character 2 Reg */
+	 u8 xonchar1;	/* WR	XON 1 - Xon Character 1 Reg */
+	 u8 xonchar2;	/* WR	XON 2 - XOn Character 2 Reg */
+
+	 u8 reserved1[0x2ff - 0x200]; /* U	Reserved by Exar */
+	 u8 txrxburst[64];	/* RW	64 bytes of RX/TX FIFO Data */
+	 u8 reserved2[0x37f - 0x340]; /* U	Reserved by Exar */
+	 u8 rxburst_with_errors[64];	/* R	64 bytes of RX FIFO Data + LSR */
+};
+
+/* Where to read the extended interrupt register (32bits instead of 8bits) */
+#define	UART_17158_POLL_ADDR_OFFSET	0x80
+
+/*
+ * These are the redefinitions for the FCTR on the XR17C158, since
+ * Exar made them different than their earlier design. (XR16C854)
+ */
+
+/* These are only applicable when table D is selected */
+#define UART_17158_FCTR_RTS_NODELAY	0x00
+#define UART_17158_FCTR_RTS_4DELAY	0x01
+#define UART_17158_FCTR_RTS_6DELAY	0x02
+#define UART_17158_FCTR_RTS_8DELAY	0x03
+#define UART_17158_FCTR_RTS_12DELAY	0x12
+#define UART_17158_FCTR_RTS_16DELAY	0x05
+#define UART_17158_FCTR_RTS_20DELAY	0x13
+#define UART_17158_FCTR_RTS_24DELAY	0x06
+#define UART_17158_FCTR_RTS_28DELAY	0x14
+#define UART_17158_FCTR_RTS_32DELAY	0x07
+#define UART_17158_FCTR_RTS_36DELAY	0x16
+#define UART_17158_FCTR_RTS_40DELAY	0x08
+#define UART_17158_FCTR_RTS_44DELAY	0x09
+#define UART_17158_FCTR_RTS_48DELAY	0x10
+#define UART_17158_FCTR_RTS_52DELAY	0x11
+
+#define UART_17158_FCTR_RTS_IRDA	0x10
+#define UART_17158_FCTR_RS485		0x20
+#define UART_17158_FCTR_TRGA		0x00
+#define UART_17158_FCTR_TRGB		0x40
+#define UART_17158_FCTR_TRGC		0x80
+#define UART_17158_FCTR_TRGD		0xC0
+
+/* 17158 trigger table selects.. */
+#define UART_17158_FCTR_BIT6		0x40
+#define UART_17158_FCTR_BIT7		0x80
+
+/* 17158 TX/RX memmapped buffer offsets */
+#define UART_17158_RX_FIFOSIZE		64
+#define UART_17158_TX_FIFOSIZE		64
+
+/* 17158 Extended IIR's */
+#define UART_17158_IIR_RDI_TIMEOUT	0x0C	/* Receiver data TIMEOUT */
+#define UART_17158_IIR_XONXOFF		0x10	/* Received an XON/XOFF char */
+#define UART_17158_IIR_HWFLOW_STATE_CHANGE 0x20	/* CTS/DSR or RTS/DTR state change */
+#define UART_17158_IIR_FIFO_ENABLED	0xC0	/* 16550 FIFOs are Enabled */
+
+/*
+ * These are the extended interrupts that get sent
+ * back to us from the UART's 32bit interrupt register
+ */
+#define UART_17158_RX_LINE_STATUS	0x1	/* RX Ready */
+#define UART_17158_RXRDY_TIMEOUT	0x2	/* RX Ready Timeout */
+#define UART_17158_TXRDY		0x3	/* TX Ready */
+#define UART_17158_MSR			0x4	/* Modem State Change */
+#define UART_17158_TX_AND_FIFO_CLR	0x40	/* Transmitter Holding Reg Empty */
+#define UART_17158_RX_FIFO_DATA_ERROR	0x80	/* UART detected an RX FIFO Data error */
+
+/*
+ * These are the EXTENDED definitions for the 17C158's Interrupt
+ * Enable Register.
+ */
+#define UART_17158_EFR_ECB	0x10	/* Enhanced control bit */
+#define UART_17158_EFR_IXON	0x2	/* Receiver compares Xon1/Xoff1 */
+#define UART_17158_EFR_IXOFF	0x8	/* Transmit Xon1/Xoff1 */
+#define UART_17158_EFR_RTSDTR	0x40	/* Auto RTS/DTR Flow Control Enable */
+#define UART_17158_EFR_CTSDSR	0x80	/* Auto CTS/DSR Flow COntrol Enable */
+
+#define UART_17158_XOFF_DETECT	0x1	/* Indicates whether chip saw an incoming XOFF char */
+#define UART_17158_XON_DETECT	0x2	/* Indicates whether chip saw an incoming XON char */
+
+#define UART_17158_IER_RSVD1	0x10	/* Reserved by Exar */
+#define UART_17158_IER_XOFF	0x20	/* Xoff Interrupt Enable */
+#define UART_17158_IER_RTSDTR	0x40	/* Output Interrupt Enable */
+#define UART_17158_IER_CTSDSR	0x80	/* Input Interrupt Enable */
+
+#define PCI_DEVICE_NEO_2DB9_PCI_NAME		"Neo 2 - DB9 Universal PCI"
+#define PCI_DEVICE_NEO_2DB9PRI_PCI_NAME		"Neo 2 - DB9 Universal PCI - Powered Ring Indicator"
+#define PCI_DEVICE_NEO_2RJ45_PCI_NAME		"Neo 2 - RJ45 Universal PCI"
+#define PCI_DEVICE_NEO_2RJ45PRI_PCI_NAME	"Neo 2 - RJ45 Universal PCI - Powered Ring Indicator"
+
+/*
+ * Our Global Variables.
+ */
+extern struct	uart_driver jsm_uart_driver;
+extern struct	board_ops jsm_neo_ops;
+extern int	jsm_debug;
+extern int	jsm_rawreadok;
+
+extern int	jsm_driver_state;	/* The state of the driver	*/
+extern char	*jsm_driver_state_text[];/* Array of driver state text */
+
+extern spinlock_t jsm_board_head_lock;
+extern struct list_head jsm_board_head;
+
+/*************************************************************************
+ *
+ * Prototypes for non-static functions used in more than one module
+ *
+ *************************************************************************/
+int jsm_tty_write(struct uart_port *port);
+int jsm_tty_init(struct jsm_board *);
+int jsm_uart_port_init(struct jsm_board *);
+int jsm_remove_uart_port(struct jsm_board *);
+void jsm_input(struct jsm_channel *ch);
+void jsm_carrier(struct jsm_channel *ch);
+void jsm_check_queue_flow_control(struct jsm_channel *ch);
+
+void jsm_create_driver_sysfiles(struct device_driver *);
+void jsm_remove_driver_sysfiles(struct device_driver *);
+
+#endif

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [ patch 5/5] drivers/serial/jsm: new serial device driver
  2005-03-09 18:58                     ` Greg KH
                                         ` (3 preceding siblings ...)
  2005-03-11 15:38                       ` [ patch 4/5] " Wen Xiong
@ 2005-03-11 15:38                       ` Wen Xiong
  4 siblings, 0 replies; 34+ messages in thread
From: Wen Xiong @ 2005-03-11 15:38 UTC (permalink / raw)
  To: Greg KH; +Cc: Wen Xiong, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 109 bytes --]

This is fifth patch for jsm serial device driver.

Signed-off-by: Wen Xiong <wendyx@us.ltcfwd.linux.ibm.com>

[-- Attachment #2: patch5.jasmine --]
[-- Type: text/plain, Size: 3002 bytes --]

diff -Nuar linux-2.6.11.org/drivers/serial/Kconfig linux-2.6.11.new/drivers/serial/Kconfig
--- linux-2.6.11.org/drivers/serial/Kconfig	2005-03-10 16:28:59.552930408 -0600
+++ linux-2.6.11.new/drivers/serial/Kconfig	2005-03-10 10:31:27.000000000 -0600
@@ -810,4 +810,19 @@
 	bool "TX39XX/49XX SIO act as standard serial"
 	depends on !SERIAL_8250 && SERIAL_TXX9
 
+config SERIAL_JSM
+        tristate "Digi International NEO PCI Support"
+        select SERIAL_CORE
+        help
+          This is a driver for Digi International's Neo series
+          of cards which provide multiple serial ports. You would need
+          something like this to connect more than two modems to your Linux
+          box, for instance in order to become a dial-in server. This driver
+          supports PCI boards only.
+          If you have a card like this, say Y here and read the file
+          <file:Documentation/jsm.txt>.
+
+          To compile this driver as a module, choose M here: the
+          module will be called jsm.
+
 endmenu
diff -Nuar linux-2.6.11.org/drivers/serial/Makefile linux-2.6.11.new/drivers/serial/Makefile
--- linux-2.6.11.org/drivers/serial/Makefile	2005-03-10 16:29:08.488901896 -0600
+++ linux-2.6.11.new/drivers/serial/Makefile	2005-03-10 10:31:39.000000000 -0600
@@ -49,3 +49,4 @@
 obj-$(CONFIG_SERIAL_MPSC) += mpsc.o
 obj-$(CONFIG_ETRAX_SERIAL) += crisv10.o
 obj-$(CONFIG_SERIAL_TXX9) += serial_txx9.o
+obj-$(CONFIG_SERIAL_JSM) += jsm/
diff -Nuar linux-2.6.11.org/drivers/serial/jsm/Makefile linux-2.6.11.new/drivers/serial/jsm/Makefile
--- linux-2.6.11.org/drivers/serial/jsm/Makefile	1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.11.new/drivers/serial/jsm/Makefile	2005-03-10 16:21:17.497884352 -0600
@@ -0,0 +1,8 @@
+#
+# Makefile for Jasmine adapter
+#
+
+obj-$(CONFIG_SERIAL_JSM) += jsm.o
+
+jsm-objs :=    jsm_driver.o jsm_neo.o jsm_tty.o
+
diff -Nuar linux-2.6.11.org/include/linux/pci_ids.h linux-2.6.11.new/include/linux/pci_ids.h
--- linux-2.6.11.org/include/linux/pci_ids.h	2005-03-10 16:31:13.891876144 -0600
+++ linux-2.6.11.new/include/linux/pci_ids.h	2005-03-10 16:24:43.854930712 -0600
@@ -1418,6 +1418,10 @@
 #define PCI_DEVICE_ID_DIGI_DF_M_E	0x0071
 #define PCI_DEVICE_ID_DIGI_DF_M_IOM2_A	0x0072
 #define PCI_DEVICE_ID_DIGI_DF_M_A	0x0073
+#define PCI_DEVICE_ID_NEO_2DB9          0x00C8
+#define PCI_DEVICE_ID_NEO_2DB9PRI       0x00C9
+#define PCI_DEVICE_ID_NEO_2RJ45         0x00CA
+#define PCI_DEVICE_ID_NEO_2RJ45PRI      0x00CB
 
 #define PCI_VENDOR_ID_MUTECH		0x1159
 #define PCI_DEVICE_ID_MUTECH_MV1000	0x0001
diff -Nuar linux-2.6.11.org/include/linux/serial_core.h linux-2.6.11.new/include/linux/serial_core.h
--- linux-2.6.11.org/include/linux/serial_core.h	2005-03-10 16:31:29.338924176 -0600
+++ linux-2.6.11.new/include/linux/serial_core.h	2005-03-10 16:23:42.031877360 -0600
@@ -106,6 +106,9 @@
 /* TXX9 type number */
 #define PORT_TXX9       64
 
+/*Digi jsm */
+#define PORT_JSM        65
+
 #ifdef __KERNEL__
 
 #include <linux/config.h>

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [ patch 3/5] drivers/serial/jsm: new serial device driver
  2005-03-11 15:38                       ` [ patch 3/5] " Wen Xiong
@ 2005-03-11 15:53                         ` Arjan van de Ven
  2005-03-11 16:39                           ` Wen Xiong
  0 siblings, 1 reply; 34+ messages in thread
From: Arjan van de Ven @ 2005-03-11 15:53 UTC (permalink / raw)
  To: Wen Xiong; +Cc: Greg KH, linux-kernel

On Fri, 2005-03-11 at 10:38 -0500, Wen Xiong wrote:

> +static void neo_set_cts_flow_control(struct jsm_channel *ch)
> +{
> +	u8 ier = readb(&ch->ch_neo_uart->ier);
> +	u8 efr = readb(&ch->ch_neo_uart->efr);
> +
...
> +
> +	writeb(ier, &ch->ch_neo_uart->ier);
> +}


Hi,

have you ever audited this driver for PCI posting errors? On very first
sight it looks like the driver doesn't do this correctly but it might
just be very subtle...


^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [ patch 3/5] drivers/serial/jsm: new serial device driver
  2005-03-11 15:53                         ` Arjan van de Ven
@ 2005-03-11 16:39                           ` Wen Xiong
  2005-03-11 16:46                             ` Arjan van de Ven
  0 siblings, 1 reply; 34+ messages in thread
From: Wen Xiong @ 2005-03-11 16:39 UTC (permalink / raw)
  To: Arjan van de Ven; +Cc: Wen Xiong, Greg KH, linux-kernel

Arjan van de Ven wrote:

>On Fri, 2005-03-11 at 10:38 -0500, Wen Xiong wrote:
>
>  
>
>>+static void neo_set_cts_flow_control(struct jsm_channel *ch)
>>+{
>>+	u8 ier = readb(&ch->ch_neo_uart->ier);
>>+	u8 efr = readb(&ch->ch_neo_uart->efr);
>>+
>>    
>>
>...
>  
>
>>+
>>+	writeb(ier, &ch->ch_neo_uart->ier);
>>+}
>>    
>>
>
>
>Hi,
>
>have you ever audited this driver for PCI posting errors? On very first
>sight it looks like the driver doesn't do this correctly but it might
>just be very subtle...
>
>
>  
>
Jeff pointed out several PCI posting errors last time.  Before we used 
udelay and now we changed to readb/readl instead of udelay this time.
But we only used PCI posting when we think maybe delay there.
So we have to do PCI posting on every writeb? Do you have some rules for 
doing PCI posting while writeb? depends on what kind of registers?

Thanks,
wendy


^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [ patch 3/5] drivers/serial/jsm: new serial device driver
  2005-03-11 16:39                           ` Wen Xiong
@ 2005-03-11 16:46                             ` Arjan van de Ven
  2005-03-11 22:34                               ` Wen Xiong
  0 siblings, 1 reply; 34+ messages in thread
From: Arjan van de Ven @ 2005-03-11 16:46 UTC (permalink / raw)
  To: Wen Xiong; +Cc: Greg KH, linux-kernel


> Jeff pointed out several PCI posting errors last time.  Before we used 
> udelay and now we changed to readb/readl instead of udelay this time.
> But we only used PCI posting when we think maybe delay there.
> So we have to do PCI posting on every writeb? 
not every

> Do you have some rules for 
> doing PCI posting while writeb? depends on what kind of registers?

basically you need to do posting flushes after every write for which you
assume later on the hardware received the write. If you do 5 writes in a
row you don't need 5 flushes. If you do a read later always anyway you
don't need any flushes.  
On the other side of the spectrum: if you do something to the hardware
and then wait for some event, you do need to flush that stuff.
So at minimum you want to make sure that at any point where you leave
the driver (to userspace or tty layer or ...) all writes have been
flushed. And at all points where you are going to wait for hardware
events.



^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [ patch 3/5] drivers/serial/jsm: new serial device driver
  2005-03-11 16:46                             ` Arjan van de Ven
@ 2005-03-11 22:34                               ` Wen Xiong
  2005-03-30 15:01                                 ` Russell King
  0 siblings, 1 reply; 34+ messages in thread
From: Wen Xiong @ 2005-03-11 22:34 UTC (permalink / raw)
  To: Arjan van de Ven; +Cc: Wen Xiong, Greg KH, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 1230 bytes --]

Arjan van de Ven wrote:

>>Jeff pointed out several PCI posting errors last time.  Before we used 
>>udelay and now we changed to readb/readl instead of udelay this time.
>>But we only used PCI posting when we think maybe delay there.
>>So we have to do PCI posting on every writeb? 
>>    
>>
>not every
>
>  
>
>>Do you have some rules for 
>>doing PCI posting while writeb? depends on what kind of registers?
>>    
>>
>
>basically you need to do posting flushes after every write for which you
>assume later on the hardware received the write. If you do 5 writes in a
>row you don't need 5 flushes. If you do a read later always anyway you
>don't need any flushes.  
>On the other side of the spectrum: if you do something to the hardware
>and then wait for some event, you do need to flush that stuff.
>So at minimum you want to make sure that at any point where you leave
>the driver (to userspace or tty layer or ...) all writes have been
>flushed. And at all points where you are going to wait for hardware
>events.
>
>
>
>  
>
Based on above all points, I audited the whole drvier for PCI posting 
issues.
Just added several PCI posting while the driver has interfaces with tty 
layer.

Thanks for your comments.
wendy



[-- Attachment #2: patch3.jasmine --]
[-- Type: text/plain, Size: 38515 bytes --]

diff -Nuar linux-2.6.11.org/drivers/serial/jsm/jsm_neo.c linux-2.6.11.new/drivers/serial/jsm/jsm_neo.c
--- linux-2.6.11.org/drivers/serial/jsm/jsm_neo.c	1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.11.new/drivers/serial/jsm/jsm_neo.c	2005-03-11 16:26:47.442988256 -0600
@@ -0,0 +1,1402 @@
+/************************************************************************
+ * Copyright 2003 Digi International (www.digi.com)
+ *
+ * Copyright (C) 2004 IBM Corporation. All rights reserved.
+ *
+ * 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, or (at your option)
+ * any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; without even the 
+ * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License 
+ * along with this program; if not, write to the Free Software 
+ * Foundation, Inc., 59 * Temple Place - Suite 330, Boston,
+ * MA  02111-1307, USA.
+ *
+ * Contact Information:
+ * Scott H Kilau <Scott_Kilau@digi.com>
+ * Wendy Xiong   <wendyx@us.ltcfwd.linux.ibm.com>
+ *
+ ***********************************************************************/
+#include <linux/delay.h>	/* For udelay */
+#include <linux/serial_reg.h>	/* For the various UART offsets */
+#include <linux/tty.h>
+#include <linux/pci.h>
+#include <asm/io.h>
+
+#include "jsm.h"		/* Driver main header file */
+
+static u32 jsm_offset_table[8] = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 };
+
+static void neo_set_cts_flow_control(struct jsm_channel *ch)
+{
+	u8 ier = readb(&ch->ch_neo_uart->ier);
+	u8 efr = readb(&ch->ch_neo_uart->efr);
+
+	jsm_printk(PARAM, INFO, &ch->ch_bd->pci_dev, "Setting CTSFLOW\n");
+
+	/* Turn on auto CTS flow control */
+	ier |= (UART_17158_IER_CTSDSR);
+	efr |= (UART_17158_EFR_ECB | UART_17158_EFR_CTSDSR);
+
+	/* Turn off auto Xon flow control */
+	efr &= ~(UART_17158_EFR_IXON);
+
+	/* Why? Becuz Exar's spec says we have to zero it out before setting it */
+	writeb(0, &ch->ch_neo_uart->efr);
+
+	/* Turn on UART enhanced bits */
+	writeb(efr, &ch->ch_neo_uart->efr);
+
+	/* Turn on table D, with 8 char hi/low watermarks */
+	writeb((UART_17158_FCTR_TRGD | UART_17158_FCTR_RTS_4DELAY), &ch->ch_neo_uart->fctr);
+
+	/* Feed the UART our trigger levels */
+	writeb(8, &ch->ch_neo_uart->tfifo);
+	ch->ch_t_tlevel = 8;
+
+	writeb(ier, &ch->ch_neo_uart->ier);
+}
+
+static void neo_set_rts_flow_control(struct jsm_channel *ch)
+{
+	u8 ier = readb(&ch->ch_neo_uart->ier);
+	u8 efr = readb(&ch->ch_neo_uart->efr);
+
+	jsm_printk(PARAM, INFO, &ch->ch_bd->pci_dev, "Setting RTSFLOW\n");
+
+	/* Turn on auto RTS flow control */
+	ier |= (UART_17158_IER_RTSDTR);
+	efr |= (UART_17158_EFR_ECB | UART_17158_EFR_RTSDTR);
+
+	/* Turn off auto Xoff flow control */
+	ier &= ~(UART_17158_IER_XOFF);
+	efr &= ~(UART_17158_EFR_IXOFF);
+
+	/* Why? Becuz Exar's spec says we have to zero it out before setting it */
+	writeb(0, &ch->ch_neo_uart->efr);
+
+	/* Turn on UART enhanced bits */
+	writeb(efr, &ch->ch_neo_uart->efr);
+
+	writeb((UART_17158_FCTR_TRGD | UART_17158_FCTR_RTS_4DELAY), &ch->ch_neo_uart->fctr);
+	ch->ch_r_watermark = 4;
+
+	writeb(56, &ch->ch_neo_uart->rfifo);
+	ch->ch_r_tlevel = 56;
+
+	writeb(ier, &ch->ch_neo_uart->ier);
+
+	/*
+	 * From the Neo UART spec sheet:
+	 * The auto RTS/DTR function must be started by asserting
+	 * RTS/DTR# output pin (MCR bit-0 or 1 to logic 1 after
+	 * it is enabled.
+	 */
+	ch->ch_mostat |= (UART_MCR_RTS);
+}
+
+
+static void neo_set_ixon_flow_control(struct jsm_channel *ch)
+{
+	u8 ier = readb(&ch->ch_neo_uart->ier);
+	u8 efr = readb(&ch->ch_neo_uart->efr);
+
+	jsm_printk(PARAM, INFO, &ch->ch_bd->pci_dev, "Setting IXON FLOW\n");
+
+	/* Turn off auto CTS flow control */
+	ier &= ~(UART_17158_IER_CTSDSR);
+	efr &= ~(UART_17158_EFR_CTSDSR);
+
+	/* Turn on auto Xon flow control */
+	efr |= (UART_17158_EFR_ECB | UART_17158_EFR_IXON);
+
+	/* Why? Becuz Exar's spec says we have to zero it out before setting it */
+	writeb(0, &ch->ch_neo_uart->efr);
+
+	/* Turn on UART enhanced bits */
+	writeb(efr, &ch->ch_neo_uart->efr);
+
+	writeb((UART_17158_FCTR_TRGD | UART_17158_FCTR_RTS_8DELAY), &ch->ch_neo_uart->fctr);
+	ch->ch_r_watermark = 4;
+
+	writeb(32, &ch->ch_neo_uart->rfifo);
+	ch->ch_r_tlevel = 32;
+
+	/* Tell UART what start/stop chars it should be looking for */
+	writeb(ch->ch_startc, &ch->ch_neo_uart->xonchar1);
+	writeb(0, &ch->ch_neo_uart->xonchar2);
+
+	writeb(ch->ch_stopc, &ch->ch_neo_uart->xoffchar1);
+	writeb(0, &ch->ch_neo_uart->xoffchar2);
+
+	writeb(ier, &ch->ch_neo_uart->ier);
+}
+
+static void neo_set_ixoff_flow_control(struct jsm_channel *ch)
+{
+	u8 ier = readb(&ch->ch_neo_uart->ier);
+	u8 efr = readb(&ch->ch_neo_uart->efr);
+
+	jsm_printk(PARAM, INFO, &ch->ch_bd->pci_dev, "Setting IXOFF FLOW\n");
+
+	/* Turn off auto RTS flow control */
+	ier &= ~(UART_17158_IER_RTSDTR);
+	efr &= ~(UART_17158_EFR_RTSDTR);
+
+	/* Turn on auto Xoff flow control */
+	ier |= (UART_17158_IER_XOFF);
+	efr |= (UART_17158_EFR_ECB | UART_17158_EFR_IXOFF);
+
+	/* Why? Becuz Exar's spec says we have to zero it out before setting it */
+	writeb(0, &ch->ch_neo_uart->efr);
+
+	/* Turn on UART enhanced bits */
+	writeb(efr, &ch->ch_neo_uart->efr);
+
+	/* Turn on table D, with 8 char hi/low watermarks */
+	writeb((UART_17158_FCTR_TRGD | UART_17158_FCTR_RTS_8DELAY), &ch->ch_neo_uart->fctr);
+
+	writeb(8, &ch->ch_neo_uart->tfifo);
+	ch->ch_t_tlevel = 8;
+
+	/* Tell UART what start/stop chars it should be looking for */
+	writeb(ch->ch_startc, &ch->ch_neo_uart->xonchar1);
+	writeb(0, &ch->ch_neo_uart->xonchar2);
+
+	writeb(ch->ch_stopc, &ch->ch_neo_uart->xoffchar1);
+	writeb(0, &ch->ch_neo_uart->xoffchar2);
+
+	writeb(ier, &ch->ch_neo_uart->ier);
+}
+
+static void neo_set_no_input_flow_control(struct jsm_channel *ch)
+{
+	u8 ier = readb(&ch->ch_neo_uart->ier);
+	u8 efr = readb(&ch->ch_neo_uart->efr);
+
+	jsm_printk(PARAM, INFO, &ch->ch_bd->pci_dev, "Unsetting Input FLOW\n");
+
+	/* Turn off auto RTS flow control */
+	ier &= ~(UART_17158_IER_RTSDTR);
+	efr &= ~(UART_17158_EFR_RTSDTR);
+
+	/* Turn off auto Xoff flow control */
+	ier &= ~(UART_17158_IER_XOFF);
+	if (ch->ch_c_iflag & IXON)
+		efr &= ~(UART_17158_EFR_IXOFF);
+	else
+		efr &= ~(UART_17158_EFR_ECB | UART_17158_EFR_IXOFF);
+
+	/* Why? Becuz Exar's spec says we have to zero it out before setting it */
+	writeb(0, &ch->ch_neo_uart->efr);
+
+	/* Turn on UART enhanced bits */
+	writeb(efr, &ch->ch_neo_uart->efr);
+
+	/* Turn on table D, with 8 char hi/low watermarks */
+	writeb((UART_17158_FCTR_TRGD | UART_17158_FCTR_RTS_8DELAY), &ch->ch_neo_uart->fctr);
+
+	ch->ch_r_watermark = 0;
+
+	writeb(16, &ch->ch_neo_uart->tfifo);
+	ch->ch_t_tlevel = 16;
+
+	writeb(16, &ch->ch_neo_uart->rfifo);
+	ch->ch_r_tlevel = 16;
+
+	writeb(ier, &ch->ch_neo_uart->ier);
+}
+
+static void neo_set_no_output_flow_control(struct jsm_channel *ch)
+{
+	u8 ier = readb(&ch->ch_neo_uart->ier);
+	u8 efr = readb(&ch->ch_neo_uart->efr);
+
+	jsm_printk(PARAM, INFO, &ch->ch_bd->pci_dev, "Unsetting Output FLOW\n");
+
+	/* Turn off auto CTS flow control */
+	ier &= ~(UART_17158_IER_CTSDSR);
+	efr &= ~(UART_17158_EFR_CTSDSR);
+
+	/* Turn off auto Xon flow control */
+	if (ch->ch_c_iflag & IXOFF)
+		efr &= ~(UART_17158_EFR_IXON);
+	else
+		efr &= ~(UART_17158_EFR_ECB | UART_17158_EFR_IXON);
+
+	/* Why? Becuz Exar's spec says we have to zero it out before setting it */
+	writeb(0, &ch->ch_neo_uart->efr);
+
+	/* Turn on UART enhanced bits */
+	writeb(efr, &ch->ch_neo_uart->efr);
+
+	/* Turn on table D, with 8 char hi/low watermarks */
+	writeb((UART_17158_FCTR_TRGD | UART_17158_FCTR_RTS_8DELAY), &ch->ch_neo_uart->fctr);
+
+	ch->ch_r_watermark = 0;
+
+	writeb(16, &ch->ch_neo_uart->tfifo);
+	ch->ch_t_tlevel = 16;
+
+	writeb(16, &ch->ch_neo_uart->rfifo);
+	ch->ch_r_tlevel = 16;
+
+	writeb(ier, &ch->ch_neo_uart->ier);
+}
+
+static inline void neo_set_new_start_stop_chars(struct jsm_channel *ch)
+{
+
+	/* if hardware flow control is set, then skip this whole thing */
+	if (ch->ch_c_cflag & CRTSCTS)
+		return;
+
+	jsm_printk(PARAM, INFO, &ch->ch_bd->pci_dev, "start\n");
+
+	/* Tell UART what start/stop chars it should be looking for */
+	writeb(ch->ch_startc, &ch->ch_neo_uart->xonchar1);
+	writeb(0, &ch->ch_neo_uart->xonchar2);
+
+	writeb(ch->ch_stopc, &ch->ch_neo_uart->xoffchar1);
+	writeb(0, &ch->ch_neo_uart->xoffchar2);
+}
+
+static void neo_copy_data_from_uart_to_queue(struct jsm_channel *ch)
+{
+	int qleft = 0;
+	u8 linestatus = 0;
+	u8 error_mask = 0;
+	int n = 0;
+	int total = 0;
+	u16 head;
+	u16 tail;
+
+	if (!ch)
+		return;
+
+	/* cache head and tail of queue */
+	head = ch->ch_r_head & RQUEUEMASK;
+	tail = ch->ch_r_tail & RQUEUEMASK;
+
+	/* Get our cached LSR */
+	linestatus = ch->ch_cached_lsr;
+	ch->ch_cached_lsr = 0;
+
+	/* Store how much space we have left in the queue */
+	if ((qleft = tail - head - 1) < 0)
+		qleft += RQUEUEMASK + 1;
+
+	/*
+	 * If the UART is not in FIFO mode, force the FIFO copy to
+	 * NOT be run, by setting total to 0.
+	 *
+	 * On the other hand, if the UART IS in FIFO mode, then ask
+	 * the UART to give us an approximation of data it has RX'ed.
+	 */
+	if (!(ch->ch_flags & CH_FIFO_ENABLED))
+		total = 0;
+	else {
+		total = readb(&ch->ch_neo_uart->rfifo);
+
+		/*
+		 * EXAR chip bug - RX FIFO COUNT - Fudge factor.
+		 *
+		 * This resolves a problem/bug with the Exar chip that sometimes
+		 * returns a bogus value in the rfifo register.
+		 * The count can be any where from 0-3 bytes "off".
+		 * Bizarre, but true.
+		 */
+		total -= 3;
+	}
+
+	/*
+	 * Finally, bound the copy to make sure we don't overflow
+	 * our own queue...
+	 * The byte by byte copy loop below this loop this will
+	 * deal with the queue overflow possibility.
+	 */
+	total = min(total, qleft);
+
+	while (total > 0) { 
+		/*
+		 * Grab the linestatus register, we need to check
+		 * to see if there are any errors in the FIFO.
+		 */
+		linestatus = readb(&ch->ch_neo_uart->lsr);
+
+		/*
+		 * Break out if there is a FIFO error somewhere.
+		 * This will allow us to go byte by byte down below,
+		 * finding the exact location of the error.
+		 */
+		if (linestatus & UART_17158_RX_FIFO_DATA_ERROR)
+			break;
+
+		/* Make sure we don't go over the end of our queue */
+		n = min(((u32) total), (RQUEUESIZE - (u32) tail));
+
+		/*
+		 * Cut down n even further if needed, this is to fix
+		 * a problem with memcpy_fromio() with the Neo on the
+		 * IBM pSeries platform.
+		 * 15 bytes max appears to be the magic number.
+		 */
+		n = min((u32) n, (u32) 12);
+
+		/*
+		 * Since we are grabbing the linestatus register, which
+		 * will reset some bits after our read, we need to ensure
+		 * we don't miss our TX FIFO emptys.
+		 */
+		if (linestatus & (UART_LSR_THRE | UART_17158_TX_AND_FIFO_CLR))
+			ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM);
+
+		linestatus = 0;
+
+		/* Copy data from uart to the queue */
+		memcpy_fromio(ch->ch_rqueue + head, &ch->ch_neo_uart->txrxburst, n);
+		/*
+		 * Since RX_FIFO_DATA_ERROR was 0, we are guarenteed
+		 * that all the data currently in the FIFO is free of
+		 * breaks and parity/frame/orun errors.
+		 */
+		memset(ch->ch_equeue + head, 0, n);
+
+		/* Add to and flip head if needed */
+		head = (head + n) & RQUEUEMASK;
+		total -= n;
+		qleft -= n;
+		ch->ch_rxcount += n;
+	}
+
+	/*
+	 * Create a mask to determine whether we should
+	 * insert the character (if any) into our queue.
+	 */
+	if (ch->ch_c_iflag & IGNBRK)
+		error_mask |= UART_LSR_BI;
+
+	/*
+	 * Now cleanup any leftover bytes still in the UART.
+	 * Also deal with any possible queue overflow here as well.
+	 */
+	while (1) {
+
+		/*
+		 * Its possible we have a linestatus from the loop above
+		 * this, so we "OR" on any extra bits.
+		 */
+		linestatus |= readb(&ch->ch_neo_uart->lsr);
+
+		/*
+		 * If the chip tells us there is no more data pending to
+		 * be read, we can then leave.
+		 * But before we do, cache the linestatus, just in case.
+		 */
+		if (!(linestatus & UART_LSR_DR)) {
+			ch->ch_cached_lsr = linestatus;
+			break;
+		}
+
+		/* No need to store this bit */
+		linestatus &= ~UART_LSR_DR;
+
+		/*
+		 * Since we are grabbing the linestatus register, which
+		 * will reset some bits after our read, we need to ensure
+		 * we don't miss our TX FIFO emptys.
+		 */
+		if (linestatus & (UART_LSR_THRE | UART_17158_TX_AND_FIFO_CLR)) {
+			linestatus &= ~(UART_LSR_THRE | UART_17158_TX_AND_FIFO_CLR);
+			ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM);
+		}
+
+		/*
+		 * Discard character if we are ignoring the error mask.
+		 */
+		if (linestatus & error_mask) {
+			u8 discard;
+			linestatus = 0;
+			memcpy_fromio(&discard, &ch->ch_neo_uart->txrxburst, 1);
+			continue;
+		}
+
+		/*
+		 * If our queue is full, we have no choice but to drop some data.
+		 * The assumption is that HWFLOW or SWFLOW should have stopped
+		 * things way way before we got to this point.
+		 *
+		 * I decided that I wanted to ditch the oldest data first,
+		 * I hope thats okay with everyone? Yes? Good.
+		 */
+		while (qleft < 1) {
+			jsm_printk(READ, INFO, &ch->ch_bd->pci_dev, 
+				"Queue full, dropping DATA:%x LSR:%x\n",
+				ch->ch_rqueue[tail], ch->ch_equeue[tail]);
+
+			ch->ch_r_tail = tail = (tail + 1) & RQUEUEMASK;
+			ch->ch_err_overrun++;
+			qleft++;
+		}
+
+		memcpy_fromio(ch->ch_rqueue + head, &ch->ch_neo_uart->txrxburst, 1);
+		ch->ch_equeue[head] = (u8) linestatus;
+
+		jsm_printk(READ, INFO, &ch->ch_bd->pci_dev, 
+				"DATA/LSR pair: %x %x\n", ch->ch_rqueue[head], ch->ch_equeue[head]);
+
+		/* Ditch any remaining linestatus value. */
+		linestatus = 0;
+
+		/* Add to and flip head if needed */
+		head = (head + 1) & RQUEUEMASK;
+
+		qleft--;
+		ch->ch_rxcount++;
+	}
+
+	/*
+	 * Write new final heads to channel structure.
+	 */
+	ch->ch_r_head = head & RQUEUEMASK;
+	ch->ch_e_head = head & EQUEUEMASK;
+	jsm_input(ch);
+}
+
+static void neo_copy_data_from_queue_to_uart(struct jsm_channel *ch)
+{
+	u16 head;
+	u16 tail;
+	int n;
+	int s;
+	int qlen;
+	u32 len_written = 0;
+
+	if (!ch)
+		return;
+
+	/* No data to write to the UART */ 
+	if (ch->ch_w_tail == ch->ch_w_head)
+		return;
+
+	/* If port is "stopped", don't send any data to the UART */
+	if ((ch->ch_flags & CH_STOP) || (ch->ch_flags & CH_BREAK_SENDING))
+		return;
+	/*
+	 * If FIFOs are disabled. Send data directly to txrx register
+	 */
+	if (!(ch->ch_flags & CH_FIFO_ENABLED)) {
+		u8 lsrbits = readb(&ch->ch_neo_uart->lsr);
+
+		ch->ch_cached_lsr |= lsrbits;
+		if (ch->ch_cached_lsr & UART_LSR_THRE) {
+			ch->ch_cached_lsr &= ~(UART_LSR_THRE);
+
+			writeb(ch->ch_wqueue[ch->ch_w_tail], &ch->ch_neo_uart->txrx);
+			jsm_printk(WRITE, INFO, &ch->ch_bd->pci_dev, 
+					"Tx data: %x\n", ch->ch_wqueue[ch->ch_w_head]);
+			ch->ch_w_tail++;
+			ch->ch_w_tail &= WQUEUEMASK;
+			ch->ch_txcount++;
+		}
+		return;
+	}
+
+	/*
+	 * We have to do it this way, because of the EXAR TXFIFO count bug.
+	 */
+	if (!(ch->ch_flags & (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM)))
+		return; 
+
+	len_written = 0;
+	n = UART_17158_TX_FIFOSIZE - ch->ch_t_tlevel;
+
+	/* cache head and tail of queue */
+	head = ch->ch_w_head & WQUEUEMASK;
+	tail = ch->ch_w_tail & WQUEUEMASK;
+	qlen = (head - tail) & WQUEUEMASK;
+
+	/* Find minimum of the FIFO space, versus queue length */
+	n = min(n, qlen);
+
+	while (n > 0) {
+
+		s = ((head >= tail) ? head : WQUEUESIZE) - tail;
+		s = min(s, n);
+
+		if (s <= 0)
+			break;
+
+		memcpy_toio(&ch->ch_neo_uart->txrxburst, ch->ch_wqueue + tail, s);
+		/* Add and flip queue if needed */
+		tail = (tail + s) & WQUEUEMASK;
+		n -= s;
+		ch->ch_txcount += s;
+		len_written += s;
+	}
+
+	/* Update the final tail */
+	ch->ch_w_tail = tail & WQUEUEMASK;
+
+	if (len_written >= ch->ch_t_tlevel)
+		ch->ch_flags &= ~(CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM);
+
+	if (!jsm_tty_write(&ch->uart_port))
+		uart_write_wakeup(&ch->uart_port);
+}
+
+static void neo_parse_modem(struct jsm_channel *ch, u8 signals)
+{
+	u8 msignals = signals;
+
+	jsm_printk(MSIGS, INFO, &ch->ch_bd->pci_dev, 
+			"neo_parse_modem: port: %d msignals: %x\n", ch->ch_portnum, msignals);
+
+	if (!ch)
+		return;
+
+	/* Scrub off lower bits. They signify delta's, which I don't care about */
+	msignals &= 0xf0;
+
+	if (msignals & UART_MSR_DCD)
+		ch->ch_mistat |= UART_MSR_DCD;
+	else
+		ch->ch_mistat &= ~UART_MSR_DCD;
+
+	if (msignals & UART_MSR_DSR)
+		ch->ch_mistat |= UART_MSR_DSR;
+	else
+		ch->ch_mistat &= ~UART_MSR_DSR;
+
+	if (msignals & UART_MSR_RI)
+		ch->ch_mistat |= UART_MSR_RI;
+	else
+		ch->ch_mistat &= ~UART_MSR_RI;
+
+	if (msignals & UART_MSR_CTS)
+		ch->ch_mistat |= UART_MSR_CTS;
+	else
+		ch->ch_mistat &= ~UART_MSR_CTS;
+
+	jsm_printk(MSIGS, INFO, &ch->ch_bd->pci_dev, 
+			"Port: %d DTR: %d RTS: %d CTS: %d DSR: %d " "RI: %d CD: %d\n",
+		ch->ch_portnum,
+		!!((ch->ch_mistat | ch->ch_mostat) & UART_MCR_DTR),
+		!!((ch->ch_mistat | ch->ch_mostat) & UART_MCR_RTS),
+		!!((ch->ch_mistat | ch->ch_mostat) & UART_MSR_CTS), 
+		!!((ch->ch_mistat | ch->ch_mostat) & UART_MSR_DSR), 
+		!!((ch->ch_mistat | ch->ch_mostat) & UART_MSR_RI),
+		!!((ch->ch_mistat | ch->ch_mostat) & UART_MSR_DCD));
+}
+
+/* Make the UART raise any of the output signals we want up */
+static void neo_assert_modem_signals(struct jsm_channel *ch)
+{
+	u8 out;
+
+	if (!ch)
+		return;
+
+	out = ch->ch_mostat;
+
+	writeb(out, &ch->ch_neo_uart->mcr);
+
+	/* flush write operation */
+	readb(&ch->ch_neo_uart->mcr);
+}
+
+/*
+ * Flush the WRITE FIFO on the Neo.
+ *
+ * NOTE: Channel lock MUST be held before calling this function!
+ */
+static void neo_flush_uart_write(struct jsm_channel *ch)
+{
+	u8 tmp = 0;
+	int i = 0;
+
+	if (!ch)
+		return;
+
+	writeb((UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_XMIT), &ch->ch_neo_uart->isr_fcr);
+
+	for (i = 0; i < 10; i++) {
+
+		/* Check to see if the UART feels it completely flushed the FIFO. */
+		tmp = readb(&ch->ch_neo_uart->isr_fcr);
+		if (tmp & 4) {
+			jsm_printk(IOCTL, INFO, &ch->ch_bd->pci_dev, 
+					"Still flushing TX UART... i: %d\n", i);
+			udelay(10);
+		}
+		else
+			break;
+	}
+
+	ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM);
+}
+
+
+/*
+ * Flush the READ FIFO on the Neo.
+ *
+ * NOTE: Channel lock MUST be held before calling this function!
+ */
+static void neo_flush_uart_read(struct jsm_channel *ch)
+{
+	u8 tmp = 0;
+	int i = 0;
+
+	if (!ch)
+		return;
+
+	writeb((UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR), &ch->ch_neo_uart->isr_fcr);
+
+	for (i = 0; i < 10; i++) {
+
+		/* Check to see if the UART feels it completely flushed the FIFO. */
+		tmp = readb(&ch->ch_neo_uart->isr_fcr);
+		if (tmp & 2) {
+			jsm_printk(IOCTL, INFO, &ch->ch_bd->pci_dev, 
+					"Still flushing RX UART... i: %d\n", i);
+			udelay(10);
+		}
+		else
+			break;
+	}
+}
+
+/*
+ * No locks are assumed to be held when calling this function.
+ */
+void neo_clear_break(struct jsm_channel *ch, int force)
+{
+	u64 lock_flags;
+
+	spin_lock_irqsave(&ch->ch_lock, lock_flags);
+
+	/* Turn break off, and unset some variables */
+	if (ch->ch_flags & CH_BREAK_SENDING) {
+		u8 temp = readb(&ch->ch_neo_uart->lcr);
+		writeb((temp & ~UART_LCR_SBC), &ch->ch_neo_uart->lcr);
+
+		ch->ch_flags &= ~(CH_BREAK_SENDING);
+		jsm_printk(IOCTL, INFO, &ch->ch_bd->pci_dev, 
+				"clear break Finishing UART_LCR_SBC! finished: %lx\n", jiffies);
+	}
+	spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
+}
+
+/*
+ * Parse the ISR register.
+ */
+static inline void neo_parse_isr(struct jsm_board *brd, u32 port)
+{
+	struct jsm_channel *ch;
+	u8 isr;
+	u8 cause;
+	u64 lock_flags;
+
+	if (!brd)
+		return;
+
+	if (port > brd->maxports)
+		return;
+
+	ch = brd->channels[port];
+	if (!ch)
+		return;
+
+	/* Here we try to figure out what caused the interrupt to happen */
+	while (1) {
+
+		isr = readb(&ch->ch_neo_uart->isr_fcr);
+
+		/* Bail if no pending interrupt */
+		if (isr & UART_IIR_NO_INT)
+			break;
+
+		/*
+		 * Yank off the upper 2 bits, which just show that the FIFO's are enabled.
+		 */
+		isr &= ~(UART_17158_IIR_FIFO_ENABLED);
+
+		jsm_printk(INTR, INFO, &ch->ch_bd->pci_dev, 
+				"%s:%d isr: %x\n", __FILE__, __LINE__, isr);
+
+		if (isr & (UART_17158_IIR_RDI_TIMEOUT | UART_IIR_RDI)) {
+			/* Read data from uart -> queue */
+			neo_copy_data_from_uart_to_queue(ch);
+
+			/* Call our tty layer to enforce queue flow control if needed. */
+			spin_lock_irqsave(&ch->ch_lock, lock_flags);
+			jsm_check_queue_flow_control(ch);
+			spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
+		}
+
+		if (isr & UART_IIR_THRI) {
+			/* Transfer data (if any) from Write Queue -> UART. */
+			spin_lock_irqsave(&ch->ch_lock, lock_flags);
+			ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM);
+			spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
+			neo_copy_data_from_queue_to_uart(ch);
+		}
+
+		if (isr & UART_17158_IIR_XONXOFF) {
+			cause = readb(&ch->ch_neo_uart->xoffchar1);
+
+			jsm_printk(INTR, INFO, &ch->ch_bd->pci_dev, 
+					"Port %d. Got ISR_XONXOFF: cause:%x\n", port, cause);
+
+			/*
+			 * Since the UART detected either an XON or
+			 * XOFF match, we need to figure out which
+			 * one it was, so we can suspend or resume data flow.
+			 */
+			spin_lock_irqsave(&ch->ch_lock, lock_flags);
+			if (cause == UART_17158_XON_DETECT) {
+				/* Is output stopped right now, if so, resume it */
+				if (brd->channels[port]->ch_flags & CH_STOP) {
+					ch->ch_flags &= ~(CH_STOP);
+				}
+				jsm_printk(INTR, INFO, &ch->ch_bd->pci_dev, 
+						"Port %d. XON detected in incoming data\n", port);
+			} 
+			else if (cause == UART_17158_XOFF_DETECT) {
+				if (!(brd->channels[port]->ch_flags & CH_STOP)) {
+					ch->ch_flags |= CH_STOP;
+					jsm_printk(INTR, INFO, &ch->ch_bd->pci_dev, 
+							"Setting CH_STOP\n");
+				}
+				jsm_printk(INTR, INFO, &ch->ch_bd->pci_dev, 
+						"Port: %d. XOFF detected in incoming data\n", port);
+			}
+			spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
+		}
+
+		if (isr & UART_17158_IIR_HWFLOW_STATE_CHANGE) {
+			/*
+			 * If we get here, this means the hardware is doing auto flow control.
+			 * Check to see whether RTS/DTR or CTS/DSR caused this interrupt.
+			 */
+			cause = readb(&ch->ch_neo_uart->mcr);
+
+			/* Which pin is doing auto flow? RTS or DTR? */
+			spin_lock_irqsave(&ch->ch_lock, lock_flags);
+			if ((cause & 0x4) == 0) {
+				if (cause & UART_MCR_RTS)
+					ch->ch_mostat |= UART_MCR_RTS;
+				else
+					ch->ch_mostat &= ~(UART_MCR_RTS);
+			} else {
+				if (cause & UART_MCR_DTR)
+					ch->ch_mostat |= UART_MCR_DTR;
+				else
+					ch->ch_mostat &= ~(UART_MCR_DTR);
+			}
+			spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
+		}
+
+		/* Parse any modem signal changes */
+		jsm_printk(INTR, INFO, &ch->ch_bd->pci_dev, 
+				"MOD_STAT: sending to parse_modem_sigs\n");
+		neo_parse_modem(ch, readb(&ch->ch_neo_uart->msr));
+	}
+}
+
+static inline void neo_parse_lsr(struct jsm_board *brd, u32 port)
+{
+	struct jsm_channel *ch;
+	int linestatus;
+	u64 lock_flags;
+
+	if (!brd)
+		return;
+
+	if (port > brd->maxports)
+		return;
+
+	ch = brd->channels[port];
+	if (!ch)
+		return;
+
+	linestatus = readb(&ch->ch_neo_uart->lsr);
+
+	jsm_printk(INTR, INFO, &ch->ch_bd->pci_dev, 
+			"%s:%d port: %d linestatus: %x\n", __FILE__, __LINE__, port, linestatus);
+
+	ch->ch_cached_lsr |= linestatus;
+
+	if (ch->ch_cached_lsr & UART_LSR_DR) {
+		/* Read data from uart -> queue */
+		neo_copy_data_from_uart_to_queue(ch);
+		spin_lock_irqsave(&ch->ch_lock, lock_flags);
+		jsm_check_queue_flow_control(ch);
+		spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
+	}
+
+	/*
+	 * This is a special flag. It indicates that at least 1
+	 * RX error (parity, framing, or break) has happened.
+	 * Mark this in our struct, which will tell me that I have
+	 *to do the special RX+LSR read for this FIFO load.
+	 */
+	if (linestatus & UART_17158_RX_FIFO_DATA_ERROR)
+		jsm_printk(INTR, DEBUG, &ch->ch_bd->pci_dev, 
+			"%s:%d Port: %d Got an RX error, need to parse LSR\n",
+			__FILE__, __LINE__, port);
+
+	/*
+	 * The next 3 tests should *NOT* happen, as the above test
+	 * should encapsulate all 3... At least, thats what Exar says.
+	 */
+
+	if (linestatus & UART_LSR_PE) {
+		ch->ch_err_parity++;
+		jsm_printk(INTR, DEBUG, &ch->ch_bd->pci_dev, 
+			"%s:%d Port: %d. PAR ERR!\n", __FILE__, __LINE__, port);
+	}
+
+	if (linestatus & UART_LSR_FE) {
+		ch->ch_err_frame++;
+		jsm_printk(INTR, DEBUG, &ch->ch_bd->pci_dev, 
+			"%s:%d Port: %d. FRM ERR!\n", __FILE__, __LINE__, port);
+	}
+
+	if (linestatus & UART_LSR_BI) {
+		ch->ch_err_break++;
+		jsm_printk(INTR, DEBUG, &ch->ch_bd->pci_dev, 
+			"%s:%d Port: %d. BRK INTR!\n", __FILE__, __LINE__, port);
+	}
+
+	if (linestatus & UART_LSR_OE) {
+		/*
+		 * Rx Oruns. Exar says that an orun will NOT corrupt
+		 * the FIFO. It will just replace the holding register
+		 * with this new data byte. So basically just ignore this.
+		 * Probably we should eventually have an orun stat in our driver...
+		 */
+		ch->ch_err_overrun++;
+		jsm_printk(INTR, DEBUG, &ch->ch_bd->pci_dev, 
+			"%s:%d Port: %d. Rx Overrun!\n", __FILE__, __LINE__, port);
+	}
+
+	if (linestatus & UART_LSR_THRE) {
+		spin_lock_irqsave(&ch->ch_lock, lock_flags);
+		ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM);
+		spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
+
+		/* Transfer data (if any) from Write Queue -> UART. */
+		neo_copy_data_from_queue_to_uart(ch);
+	}
+	else if (linestatus & UART_17158_TX_AND_FIFO_CLR) {
+		spin_lock_irqsave(&ch->ch_lock, lock_flags);
+		ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM);
+		spin_unlock_irqrestore(&ch->ch_lock, lock_flags);
+
+		/* Transfer data (if any) from Write Queue -> UART. */
+		neo_copy_data_from_queue_to_uart(ch);
+	}
+}
+
+/*
+ * neo_param()
+ * Send any/all changes to the line to the UART.
+ */
+static void neo_param(struct jsm_channel *ch)
+{
+	u8 lcr = 0;
+	u8 uart_lcr = 0;
+	u8 ier = 0;
+	u32 baud = 9600;
+	int quot = 0;
+	struct jsm_board *bd;
+
+	bd = ch->ch_bd;
+	if (!bd)
+		return;
+
+	/*
+	 * If baud rate is zero, flush queues, and set mval to drop DTR.
+	 */
+	if ((ch->ch_c_cflag & (CBAUD)) == 0) {
+		ch->ch_r_head = ch->ch_r_tail = 0;
+		ch->ch_e_head = ch->ch_e_tail = 0;
+		ch->ch_w_head = ch->ch_w_tail = 0;
+
+		neo_flush_uart_write(ch);
+		neo_flush_uart_read(ch);
+
+		ch->ch_flags |= (CH_BAUD0);
+		ch->ch_mostat &= ~(UART_MCR_RTS | UART_MCR_DTR);
+		neo_assert_modem_signals(ch);
+		ch->ch_old_baud = 0;
+		return;
+
+	} else if (ch->ch_custom_speed) {
+			baud = ch->ch_custom_speed;
+			if (ch->ch_flags & CH_BAUD0)
+				ch->ch_flags &= ~(CH_BAUD0);
+		} else {
+			int iindex = 0;
+			int jindex = 0;
+
+			const u64 bauds[4][16] = {
+				{ 
+					0,	50,	75,	110,
+					134,	150,	200,	300,
+					600,	1200,	1800,	2400,
+					4800,	9600,	19200,	38400 },
+				{ 
+					0,	57600,	115200, 230400,
+					460800, 150,	200,	921600,
+					600,	1200,	1800,	2400,
+					4800,	9600,	19200,	38400 },
+				{ 
+					0,	57600,	76800, 115200,
+					131657, 153600, 230400, 460800,
+					921600, 1200,	1800,	2400,
+					4800,	9600,	19200,	38400 },
+				{ 
+					0,	57600,	115200, 230400,
+					460800, 150,	200,	921600,
+					600,	1200,	1800,	2400,
+					4800,	9600,	19200,	38400 }
+			};
+
+			baud = C_BAUD(ch->uart_port.info->tty) & 0xff;
+
+			if (ch->ch_c_cflag & CBAUDEX)
+				iindex = 1;
+
+			jindex = baud;
+
+			if ((iindex >= 0) && (iindex < 4) && (jindex >= 0) && (jindex < 16))
+				baud = bauds[iindex][jindex];
+			else {
+				jsm_printk(IOCTL, DEBUG, &ch->ch_bd->pci_dev, 
+					"baud indices were out of range (%d)(%d)",
+				iindex, jindex);
+				baud = 0;
+			}
+
+			if (baud == 0)
+				baud = 9600;
+
+			if (ch->ch_flags & CH_BAUD0)
+				ch->ch_flags &= ~(CH_BAUD0);
+		}
+
+	if (ch->ch_c_cflag & PARENB)
+		lcr |= UART_LCR_PARITY;
+
+	if (!(ch->ch_c_cflag & PARODD))
+		lcr |= UART_LCR_EPAR;
+
+	/* 
+	 * Not all platforms support mark/space parity,
+	 * so this will hide behind an ifdef.
+	 */
+#ifdef CMSPAR
+	if (ch->ch_c_cflag & CMSPAR) 
+		lcr |= UART_LCR_SPAR;
+#endif
+
+	if (ch->ch_c_cflag & CSTOPB)
+		lcr |= UART_LCR_STOP;
+
+	switch (ch->ch_c_cflag & CSIZE) {
+		case CS5:
+			lcr |= UART_LCR_WLEN5;
+			break;
+		case CS6:
+			lcr |= UART_LCR_WLEN6;
+			break;
+		case CS7:
+			lcr |= UART_LCR_WLEN7;
+			break;
+		case CS8:
+		default:
+			lcr |= UART_LCR_WLEN8;
+		break;
+	}
+
+	ier = readb(&ch->ch_neo_uart->ier);
+	uart_lcr = readb(&ch->ch_neo_uart->lcr);
+
+	if (baud == 0)
+		baud = 9600;
+
+	quot = ch->ch_bd->bd_dividend / baud;
+
+	if (quot != 0) {
+		ch->ch_old_baud = baud;
+		writeb(UART_LCR_DLAB, &ch->ch_neo_uart->lcr);
+		writeb((quot & 0xff), &ch->ch_neo_uart->txrx);
+		writeb((quot >> 8), &ch->ch_neo_uart->ier);
+		writeb(lcr, &ch->ch_neo_uart->lcr);
+	}
+
+	if (uart_lcr != lcr)
+		writeb(lcr, &ch->ch_neo_uart->lcr);
+
+	if (ch->ch_c_cflag & CREAD)
+		ier |= (UART_IER_RDI | UART_IER_RLSI);
+
+	ier |= (UART_IER_THRI | UART_IER_MSI);
+
+	writeb(ier, &ch->ch_neo_uart->ier);
+
+	/* Set new start/stop chars */
+	neo_set_new_start_stop_chars(ch);
+
+	if (ch->ch_c_cflag & CRTSCTS)
+		neo_set_cts_flow_control(ch);
+	else if (ch->ch_c_iflag & IXON) {
+		/* If start/stop is set to disable, then we should disable flow control */
+		if ((ch->ch_startc == __DISABLED_CHAR) || (ch->ch_stopc == __DISABLED_CHAR))
+			neo_set_no_output_flow_control(ch);
+		else
+			neo_set_ixon_flow_control(ch);
+	}
+	else
+		neo_set_no_output_flow_control(ch);
+
+	if (ch->ch_c_cflag & CRTSCTS)
+		neo_set_rts_flow_control(ch);
+	else if (ch->ch_c_iflag & IXOFF) {
+		/* If start/stop is set to disable, then we should disable flow control */
+		if ((ch->ch_startc == __DISABLED_CHAR) || (ch->ch_stopc == __DISABLED_CHAR))
+			neo_set_no_input_flow_control(ch);
+		else
+			neo_set_ixoff_flow_control(ch);
+	} 
+	else
+		neo_set_no_input_flow_control(ch);
+	/*
+	 * Adjust the RX FIFO Trigger level if baud is less than 9600.
+	 * Not exactly elegant, but this is needed because of the Exar chip's
+	 * delay on firing off the RX FIFO interrupt on slower baud rates.
+	 */
+	if (baud < 9600) {
+		writeb(1, &ch->ch_neo_uart->rfifo);
+		ch->ch_r_tlevel = 1;
+	}
+
+	neo_assert_modem_signals(ch);
+
+	/* Get current status of the modem signals now */
+	neo_parse_modem(ch, readb(&ch->ch_neo_uart->msr));
+	return;
+}
+
+/*
+ * jsm_neo_intr()
+ *
+ * Neo specific interrupt handler.
+ */
+static irqreturn_t neo_intr(int irq, void *voidbrd, struct pt_regs *regs)
+{
+	struct jsm_board *brd = (struct jsm_board *) voidbrd;
+	struct jsm_channel *ch;
+	int port = 0;
+	int type = 0;
+	int current_port;
+	u32 tmp;
+	u32 uart_poll;
+	unsigned long lock_flags;
+	unsigned long lock_flags2;
+	int outofloop_count = 0;
+
+	brd->intr_count++;
+
+	/* Lock out the slow poller from running on this board. */
+	spin_lock_irqsave(&brd->bd_intr_lock, lock_flags);
+
+	/*
+	 * Read in "extended" IRQ information from the 32bit Neo register.
+	 * Bits 0-7: What port triggered the interrupt.
+	 * Bits 8-31: Each 3bits indicate what type of interrupt occurred.
+	 */
+	uart_poll = readl(brd->re_map_membase + UART_17158_POLL_ADDR_OFFSET);
+
+	jsm_printk(INTR, INFO, &brd->pci_dev, 
+		"%s:%d uart_poll: %x\n", __FILE__, __LINE__, uart_poll);
+
+	if (!uart_poll) {
+		jsm_printk(INTR, INFO, &brd->pci_dev, 
+			"Kernel interrupted to me, but no pending interrupts...\n");
+		spin_unlock_irqrestore(&brd->bd_intr_lock, lock_flags);
+		return IRQ_NONE;
+	}
+
+	/* At this point, we have at least SOMETHING to service, dig further... */
+
+	current_port = 0;
+
+	/* Loop on each port */
+	while (((uart_poll & 0xff) != 0) && (outofloop_count < 0xff)){
+
+		tmp = uart_poll;
+		outofloop_count++;
+
+		/* Check current port to see if it has interrupt pending */
+		if ((tmp & jsm_offset_table[current_port]) != 0) {
+			port = current_port;
+			type = tmp >> (8 + (port * 3));
+			type &= 0x7;
+		} else {
+			current_port++;
+			continue;
+		}
+
+		jsm_printk(INTR, INFO, &brd->pci_dev, 
+		"%s:%d port: %x type: %x\n", __FILE__, __LINE__, port, type);
+
+		/* Remove this port + type from uart_poll */
+		uart_poll &= ~(jsm_offset_table[port]);
+
+		if (!type) {
+			/* If no type, just ignore it, and move onto next port */
+			jsm_printk(INTR, ERR, &brd->pci_dev, 
+				"Interrupt with no type! port: %d\n", port);
+			continue;
+		}
+
+		/* Switch on type of interrupt we have */
+		switch (type) {
+
+		case UART_17158_RXRDY_TIMEOUT:
+			/*
+			 * RXRDY Time-out is cleared by reading data in the
+			* RX FIFO until it falls below the trigger level.
+			 */
+
+			/* Verify the port is in range. */
+			if (port > brd->nasync)
+				continue;
+
+			ch = brd->channels[port];
+			neo_copy_data_from_uart_to_queue(ch);
+
+			/* Call our tty layer to enforce queue flow control if needed. */
+			spin_lock_irqsave(&ch->ch_lock, lock_flags2);
+			jsm_check_queue_flow_control(ch);
+			spin_unlock_irqrestore(&ch->ch_lock, lock_flags2);
+
+			continue;
+
+		case UART_17158_RX_LINE_STATUS:
+			/*
+			 * RXRDY and RX LINE Status (logic OR of LSR[4:1])
+			 */
+			neo_parse_lsr(brd, port);
+			continue;
+
+		case UART_17158_TXRDY:
+			/*
+			 * TXRDY interrupt clears after reading ISR register for the UART channel.
+			 */
+
+			/*
+			 * Yes, this is odd...
+			 * Why would I check EVERY possibility of type of
+			 * interrupt, when we know its TXRDY???
+			 * Becuz for some reason, even tho we got triggered for TXRDY,
+			 * it seems to be occassionally wrong. Instead of TX, which
+			 * it should be, I was getting things like RXDY too. Weird.
+			 */
+			neo_parse_isr(brd, port);
+			continue;
+
+		case UART_17158_MSR:
+			/*
+			 * MSR or flow control was seen.
+			 */
+			neo_parse_isr(brd, port);
+			continue;
+
+		default:
+			/*
+			 * The UART triggered us with a bogus interrupt type.
+			 * It appears the Exar chip, when REALLY bogged down, will throw
+			 * these once and awhile.
+			 * Its harmless, just ignore it and move on.
+			 */
+			jsm_printk(INTR, ERR, &brd->pci_dev, 
+				"%s:%d Unknown Interrupt type: %x\n", __FILE__, __LINE__, type);
+			continue;
+		}
+	}
+
+	spin_unlock_irqrestore(&brd->bd_intr_lock, lock_flags);
+
+	jsm_printk(INTR, INFO, &brd->pci_dev, "finish.\n");
+	return IRQ_HANDLED;
+}
+
+/*
+ * Neo specific way of turning off the receiver.
+ * Used as a way to enforce queue flow control when in
+ * hardware flow control mode.
+ */
+static void neo_disable_receiver(struct jsm_channel *ch)
+{
+	u8 tmp = readb(&ch->ch_neo_uart->ier);
+	tmp &= ~(UART_IER_RDI);
+	writeb(tmp, &ch->ch_neo_uart->ier);
+}
+
+
+/*
+ * Neo specific way of turning on the receiver.
+ * Used as a way to un-enforce queue flow control when in
+ * hardware flow control mode.
+ */
+static void neo_enable_receiver(struct jsm_channel *ch)
+{
+	u8 tmp = readb(&ch->ch_neo_uart->ier);
+	tmp |= (UART_IER_RDI);
+	writeb(tmp, &ch->ch_neo_uart->ier);
+}
+
+static void neo_send_start_character(struct jsm_channel *ch)
+{
+	unsigned char xdata;
+	if (!ch)
+		return;
+
+	if (ch->ch_startc != __DISABLED_CHAR) {
+		ch->ch_xon_sends++;
+		writeb(ch->ch_startc, &ch->ch_neo_uart->txrx);
+
+		/* flush write operation */
+		xdata = readb(&ch->ch_neo_uart->txrx);
+	}
+}
+
+static void neo_send_stop_character(struct jsm_channel *ch)
+{
+	unsigned char xdata;
+
+	if (!ch)
+		return;
+
+	if (ch->ch_stopc != __DISABLED_CHAR) {
+		ch->ch_xoff_sends++;
+		writeb(ch->ch_stopc, &ch->ch_neo_uart->txrx);
+
+		/* flush write operation */
+		xdata = readb(&ch->ch_neo_uart->txrx);
+	}
+}
+
+/*
+ * neo_uart_init
+ */
+static void neo_uart_init(struct jsm_channel *ch)
+{
+	writeb(0, &ch->ch_neo_uart->ier);
+	writeb(0, &ch->ch_neo_uart->efr);
+	writeb(UART_EFR_ECB, &ch->ch_neo_uart->efr);
+
+	/* Clear out UART and FIFO */
+	readb(&ch->ch_neo_uart->txrx);
+	writeb((UART_FCR_ENABLE_FIFO|UART_FCR_CLEAR_RCVR|UART_FCR_CLEAR_XMIT), &ch->ch_neo_uart->isr_fcr);
+	readb(&ch->ch_neo_uart->lsr);
+	readb(&ch->ch_neo_uart->msr);
+
+	ch->ch_flags |= CH_FIFO_ENABLED;
+
+	/* Assert any signals we want up */
+	writeb(ch->ch_mostat, &ch->ch_neo_uart->mcr);
+}
+
+/*
+ * Make the UART completely turn off.
+ */
+static void neo_uart_off(struct jsm_channel *ch)
+{
+	/* Turn off UART enhanced bits */
+	writeb(0, &ch->ch_neo_uart->efr);
+
+	/* Stop all interrupts from occurring. */
+	writeb(0, &ch->ch_neo_uart->ier);
+}
+
+static u32 neo_get_uart_bytes_left(struct jsm_channel *ch)
+{
+	u8 left = 0;
+	u8 lsr = readb(&ch->ch_neo_uart->lsr);
+
+	/* We must cache the LSR as some of the bits get reset once read... */
+	ch->ch_cached_lsr |= lsr;
+ 
+	/* Determine whether the Transmitter is empty or not */
+	if (!(lsr & UART_LSR_TEMT))
+		left = 1;
+	else {
+		ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM);
+		left = 0;
+	}
+
+	return left;
+}
+
+/* Channel lock MUST be held by the calling function! */
+static void neo_send_break(struct jsm_channel *ch)
+{
+	/*
+	 * Set the time we should stop sending the break.
+	 * If we are already sending a break, toss away the existing
+	 * time to stop, and use this new value instead.
+	 */
+
+	/* Tell the UART to start sending the break */
+	if (!(ch->ch_flags & CH_BREAK_SENDING)) {
+		u8 temp = readb(&ch->ch_neo_uart->lcr);
+		writeb((temp | UART_LCR_SBC), &ch->ch_neo_uart->lcr);
+		ch->ch_flags |= (CH_BREAK_SENDING);
+	}
+}
+
+/*
+ * neo_send_immediate_char.
+ *
+ * Sends a specific character as soon as possible to the UART,
+ * jumping over any bytes that might be in the write queue.
+ *
+ * The channel lock MUST be held by the calling function.
+ */
+static void neo_send_immediate_char(struct jsm_channel *ch, unsigned char c)
+{
+	if (!ch)
+		return;
+
+	writeb(c, &ch->ch_neo_uart->txrx);
+}
+
+struct board_ops jsm_neo_ops = {
+	.intr				= neo_intr,
+	.uart_init			= neo_uart_init,
+	.uart_off			= neo_uart_off,
+	.param				= neo_param,
+	.assert_modem_signals		= neo_assert_modem_signals,
+	.flush_uart_write		= neo_flush_uart_write,
+	.flush_uart_read		= neo_flush_uart_read,
+	.disable_receiver		= neo_disable_receiver,
+	.enable_receiver		= neo_enable_receiver,
+	.send_break			= neo_send_break,
+	.clear_break			= neo_clear_break,
+	.send_start_character		= neo_send_start_character,
+	.send_stop_character		= neo_send_stop_character,
+	.copy_data_from_queue_to_uart	= neo_copy_data_from_queue_to_uart,
+	.get_uart_bytes_left		= neo_get_uart_bytes_left,
+	.send_immediate_char		= neo_send_immediate_char
+};

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [ patch 1/5] drivers/serial/jsm: new serial device driver
  2005-03-11 15:29                       ` [ patch 1/5] " Wen Xiong
@ 2005-03-12 13:06                         ` Domen Puncer
  2005-03-14 17:35                           ` Wen Xiong
  0 siblings, 1 reply; 34+ messages in thread
From: Domen Puncer @ 2005-03-12 13:06 UTC (permalink / raw)
  To: Wen Xiong; +Cc: Greg KH, linux-kernel

Just some nitpicking...

On 11/03/05 10:29 -0500, Wen Xiong wrote:
> + * Globals
> + */
> +int		jsm_driver_state = DRIVER_INITIALIZED;
> +spinlock_t	jsm_board_head_lock = SPIN_LOCK_UNLOCKED;

DEFINE_SPINLOCK()

> +LIST_HEAD(jsm_board_head);
> +
> +static struct pci_device_id jsm_pci_tbl[] = {
> +	{ PCI_DEVICE (PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_NEO_2DB9),	0,	0,	0 },
> +	{ PCI_DEVICE (PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_NEO_2DB9PRI),	0,	0,	1 },
> +	{ PCI_DEVICE (PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_NEO_2RJ45),	0,	0,	2 },
> +	{ PCI_DEVICE (PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_NEO_2RJ45PRI),	0,	0,	3 },
> +	{ 0,}						/* 0 terminated list. */
> +};
> +MODULE_DEVICE_TABLE(pci, jsm_pci_tbl);
> +
> +static struct board_id jsm_Ids[] = {	

Trailing whitespace.

> +	{ PCI_DEVICE_NEO_2DB9_PCI_NAME,		2 },
> +	{ PCI_DEVICE_NEO_2DB9PRI_PCI_NAME,	2 },
> +	{ PCI_DEVICE_NEO_2RJ45_PCI_NAME,	2 },
> +	{ PCI_DEVICE_NEO_2RJ45PRI_PCI_NAME,	2 },
> +	{ NULL,					0 }
> +};
> +
> +char *jsm_driver_state_text[] = {
> +	"Driver Initialized",
> +	"Driver Ready."
> +};
> +
> +static int jsm_finalize_board_init(struct jsm_board *brd) 

Trailing whitespace.

> +{
> +	int rc = 0;
> +
> +	jsm_printk(INIT, INFO, &brd->pci_dev, "start\n");
> +
> +	if (brd->irq) {
> +		rc = request_irq(brd->irq, brd->bd_ops->intr, SA_INTERRUPT|SA_SHIRQ, "JSM", brd);
> +
> +		if (rc) {
> +			printk(KERN_WARNING "Failed to hook IRQ %d\n",brd->irq);
> +			brd->state = BOARD_FAILED;
> +			brd->dpastatus = BD_NOFEP;
> +			rc = -ENODEV;
> +		} else
> +			jsm_printk(INIT, INFO, &brd->pci_dev,
> +				"Requested and received usage of IRQ %d\n", brd->irq);
> +	}
> +	return rc;
> +}
> +
> +/*
> + * jsm_found_board()
> + *
> + * A board has been found, init it.
> + */
> +static int jsm_found_board(struct pci_dev *pdev, int id)
> +{
> +	struct jsm_board *brd;
> +	int i = 0;
> +	int rc = 0;
> +	struct list_head *tmp;
> +	struct jsm_board *cur_board_entry;
> +	unsigned long lock_flags;
> +	int adapter_count = 0;
> +
> +	brd = (struct jsm_board *)kmalloc(sizeof(struct jsm_board), GFP_KERNEL);

Don't cast void pointers.

> +	if (!brd) {
> +		dev_err(&pdev->dev, "memory allocation for board structure failed\n");
> +		return -ENOMEM;
> +	}
> +	memset(brd, 0, sizeof(struct jsm_board));

sizeof(*brd)?

> +
> +	spin_lock_irqsave(&jsm_board_head_lock, lock_flags);
> +	list_for_each(tmp, &jsm_board_head) {
> +		cur_board_entry = 
> +			list_entry(tmp, struct jsm_board,
> +				jsm_board_entry);

list_for_each_entry would make it shorter.

> +		if (cur_board_entry->boardnum != adapter_count) {
> +			break;
> +		}
> +		adapter_count++;
> +	}
> +
> +	list_add_tail(&brd->jsm_board_entry, &jsm_board_head);
> +	spin_unlock_irqrestore(&jsm_board_head_lock, lock_flags);
> +
> +	/* store the info for the board we've found */
> +	brd->boardnum = adapter_count;
> +	brd->pci_dev = pdev;
> +	brd->name = jsm_Ids[id].name;
> +	brd->maxports = jsm_Ids[id].maxports;
> +	brd->dpastatus = BD_NOFEP;
> +	init_waitqueue_head(&brd->state_wait);
> +
> +	spin_lock_init(&brd->bd_lock);
> +	spin_lock_init(&brd->bd_intr_lock);
> +
> +	brd->state = BOARD_FOUND;
> +
> +	for (i = 0; i < brd->maxports; i++) 

Trailing whitespace.

> +		brd->channels[i] = NULL;
> +
> +	/* store which revision we have */
> +	pci_read_config_byte(pdev, PCI_REVISION_ID, &brd->rev);
> +
> +	brd->irq = pdev->irq;
> +
> +	switch(brd->pci_dev->device) {
> +
> +	case PCI_DEVICE_ID_NEO_2DB9:
> +	case PCI_DEVICE_ID_NEO_2DB9PRI:
> +	case PCI_DEVICE_ID_NEO_2RJ45:
> +	case PCI_DEVICE_ID_NEO_2RJ45PRI:
> +
> +		/*
> +		 * This chip is set up 100% when we get to it.
> +		 * No need to enable global interrupts or anything. 
> +		 */
> +		brd->dpatype = T_NEO | T_PCIBUS;
> +
> +		jsm_printk(INIT, INFO, &brd->pci_dev,
> +			"jsm_found_board - NEO adapter\n");
> +
> +		/* get the PCI Base Address Registers */
> +		brd->membase	= pci_resource_start(pdev, 0);
> +		brd->membase_end = pci_resource_end(pdev, 0);
> +
> +		if (brd->membase & 1)
> +			brd->membase &= ~3;
> +		else
> +			brd->membase &= ~15;
> +
> +		/* Assign the board_ops struct */
> +		brd->bd_ops = &jsm_neo_ops;
> +
> +		brd->bd_uart_offset = 0x200;
> +		brd->bd_dividend = 921600;
> +
> +		brd->re_map_membase = ioremap(brd->membase, 0x1000);
> +		jsm_printk(INIT, INFO, &brd->pci_dev,
> +			"remapped mem: 0x%p\n", brd->re_map_membase);
> +		if (!brd->re_map_membase) {
> +			kfree(brd);
> +			dev_err(&pdev->dev, "card has no PCI Memory resources, failing board.\n");
> +			return -ENOMEM;
> +		}
> +		break;
> +
> +	default:
> +		dev_err(&pdev->dev, "Did not find any compatible Neo or Classic PCI boards in system.\n");
> +		kfree(brd);
> +		return -ENXIO;
> +	}
> +
> +	/*
> +	 * Do tty device initialization.
> +	 */
> +	rc = jsm_finalize_board_init(brd);
> +	if (rc < 0) {
> +		dev_err(&pdev->dev, "Can't finalize board init (%d)\n", rc);
> +		brd->state = BOARD_FAILED;
> +		brd->dpastatus = BD_NOFEP;

I think it's already initialized to this.

> +		goto failed;
> +	}
> +
> +	rc = jsm_tty_init(brd);
> +	if (rc < 0) {
> +		dev_err(&pdev->dev, "Can't init tty devices (%d)\n", rc);
> +		brd->state = BOARD_FAILED;
> +		brd->dpastatus = BD_NOFEP;

Same here.

> +		free_irq(brd->irq, brd);
> +		goto failed;
> +	}
> +
> +	rc = jsm_uart_port_init(brd);
> +	if (rc < 0) {
> +		free_irq(brd->irq, brd);

Another label above "failed", that includes free_irq could be nicer.
Also... is brd->state == BOARD_FOUND intended here?

> +		goto failed;
> +	}
> +
> +	brd->state = BOARD_READY;
> +	brd->dpastatus = BD_RUNNING;
> +
> +	/* Log the information about the board */
> +	dev_info(&pdev->dev, "board %d: %s (rev %d), irq %d\n",adapter_count, brd->name, brd->rev, brd->irq);
> +
> +	/*
> +	 * allocate flip buffer for board.
> +	 *
> +	 * Okay to malloc with GFP_KERNEL, we are not at interrupt
> +	 * context, and there are no locks held.
> +	 */
> +	brd->flipbuf = kmalloc(MYFLIPLEN, GFP_KERNEL);
> +	if (!brd->flipbuf) {
> +		dev_err(&pdev->dev, "memory allocation for flipbuf failed\n");
> +		free_irq(brd->irq, brd);
> +		kfree(brd);
> +		iounmap((void *) brd->re_map_membase);
> +		return -ENOMEM;

Hmm... set retval and goto same?

> +	}
> +	memset(brd->flipbuf, 0, MYFLIPLEN);
> +
> +	jsm_create_driver_sysfiles(pdev->dev.driver);
> +
> +	wake_up_interruptible(&brd->state_wait);
> +	return 0;
> +
> +failed:
> +	kfree(brd);
> +	iounmap((void *) brd->re_map_membase);

Cast not needed, i believe.

> +	return -ENXIO;
> +}
> +
> +/* returns count (>= 0), or negative on error */
> +static int jsm_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
> +{
> +	int rc;
> +
> +	rc = pci_enable_device(pdev);
> +	if (rc) {
> +		dev_err(&pdev->dev, "Device enable FAILED\n");
> +		return rc;
> +	} 

Trailing whitespace.

> +
> +	if ((rc = pci_request_regions(pdev, "jsm"))) {

JSM_DRIVER_NAME?

> +	dev_err(&pdev->dev, "pci_request_region FAILED\n");
> +		pci_disable_device(pdev);
> +		return rc;
> +	}
> +
> +	if ((rc = jsm_found_board(pdev, ent->driver_data))) {
> +		dev_err(&pdev->dev, "jsm_found_board FAILED\n");
> +		pci_release_regions(pdev);
> +		pci_disable_device(pdev);
> +	 	return rc;
> +	}
> +	return rc;
> +}
> +
> +
> +/*
> + * jsm_cleanup_board()
> + *
> + * Free all the memory associated with a board
> + */
> +static void jsm_cleanup_board(struct jsm_board *brd)
> +{
> +	int i = 0;
> +
> +	free_irq(brd->irq, brd);
> +	iounmap(brd->re_map_membase);
> +
> +	/* Free all allocated channels structs */
> +	for (i = 0; i < brd->maxports; i++) {
> +		if (brd->channels[i]) {
> +			if (brd->channels[i]->ch_rqueue)
> +				kfree(brd->channels[i]->ch_rqueue);
> +			if (brd->channels[i]->ch_equeue)
> +				kfree(brd->channels[i]->ch_equeue);
> +			if (brd->channels[i]->ch_wqueue)
> +				kfree(brd->channels[i]->ch_wqueue);

kfree() handles NULL just fine.

> +
> +			kfree(brd->channels[i]);
> +			brd->channels[i] = NULL;
> +		}
> +	}
> +
> +	pci_release_regions(brd->pci_dev);
> +	pci_disable_device(brd->pci_dev);
> +	kfree(brd->flipbuf);
> +	kfree(brd);
> +}
> +
> +static void jsm_remove_one(struct pci_dev *dev)
> +{
> +	unsigned long lock_flags;
> +	struct list_head *tmp;
> +	struct jsm_board *brd;
> +
> +	spin_lock_irqsave(&jsm_board_head_lock, lock_flags);
> +	list_for_each(tmp, &jsm_board_head) {

list_for_each_entry_safe?

> +		brd = list_entry(tmp, struct jsm_board,
> +					jsm_board_entry);
> +		if ( brd != NULL && brd->pci_dev == dev) {
> +			jsm_remove_uart_port(brd);
> +			jsm_cleanup_board(brd);
> +			list_del(&brd->jsm_board_entry);
> +			break;
> +		}
> +	}
> +	spin_unlock_irqrestore(&jsm_board_head_lock, lock_flags);
> +	return;
> +}
> +
> +struct pci_driver jsm_driver = {
> +	.name		= "jsm",

JSM_DRIVER_NAME?

> +	.probe		= jsm_init_one,
> +	.id_table	= jsm_pci_tbl,
> +	.remove		= __devexit_p(jsm_remove_one),
> +};
> +
> +/*
> + * jsm_init_module()
> + *
> + * Module load.  This is where it all starts.
> + */
> +static int __init
> +jsm_init_module(void)

Only last two routines have return type in separate line.

> +{
> +	int rc = 0;
> +
> +	printk(KERN_INFO "%s, Digi International Part Number %s\n",
> +			JSM_VERSION, JSM_VERSION);

??

> +
> +	/*
> +	 * Initialize global stuff
> +	 */
> +
> +	rc = uart_register_driver(&jsm_uart_driver);
> +	if (rc < 0) {
> +		return rc;
> +	}
> +
> +	rc = pci_register_driver(&jsm_driver);
> +	if (rc < 0) {
> +		uart_unregister_driver(&jsm_uart_driver);
> +		return rc;
> +	}
> +	jsm_driver_state = DRIVER_READY;
> +
> +	return rc;
> +}
> +
> +module_init(jsm_init_module);
> +
> +/*
> + * jsm_exit_module()
> + *
> + * Module unload.  This is where it all ends.
> + */
> +static void __exit
> +jsm_exit_module(void)
> +{
> +	jsm_remove_driver_sysfiles(&(jsm_driver.driver));

No need for ().

> +
> +	pci_unregister_driver(&jsm_driver);
> +
> +	uart_unregister_driver(&jsm_uart_driver);
> +}
> +module_exit(jsm_exit_module);
> +MODULE_LICENSE("GPL");


^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [ patch 1/5] drivers/serial/jsm: new serial device driver
  2005-03-12 13:06                         ` Domen Puncer
@ 2005-03-14 17:35                           ` Wen Xiong
  2005-03-14 20:24                             ` Domen Puncer
  0 siblings, 1 reply; 34+ messages in thread
From: Wen Xiong @ 2005-03-14 17:35 UTC (permalink / raw)
  To: Domen Puncer; +Cc: Wen Xiong, Greg KH, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 175 bytes --]

Domen Puncer wrote:

>Just some nitpicking...
>
>  
>
Hi Domen, all,

Thanks for your comments. I  did some minor changes for patch1 based on 
Domen's comment.

Thanks,
wendy

[-- Attachment #2: patch1.jasmine --]
[-- Type: text/plain, Size: 10668 bytes --]

diff -Nuar linux-2.6.11.org/drivers/serial/jsm/jsm_driver.c linux-2.6.11.new/drivers/serial/jsm/jsm_driver.c
--- linux-2.6.11.org/drivers/serial/jsm/jsm_driver.c	1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.11.new/drivers/serial/jsm/jsm_driver.c	2005-03-14 11:13:08.143944608 -0600
@@ -0,0 +1,404 @@
+/************************************************************************
+ * Copyright 2003 Digi International (www.digi.com)
+ *
+ * Copyright (C) 2004 IBM Corporation. All rights reserved.
+ *
+ * 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, or (at your option)
+ * any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; without even the 
+ * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License 
+ * along with this program; if not, write to the Free Software 
+ * Foundation, Inc., 59 * Temple Place - Suite 330, Boston,
+ * MA  02111-1307, USA.
+ *
+ * Contact Information:
+ * Scott H Kilau <Scott_Kilau@digi.com>
+ * Wendy Xiong   <wendyx@us.ltcfwd.linux.ibm.com>
+ *
+ ***********************************************************************/
+#include <linux/moduleparam.h>
+#include <linux/pci.h>
+
+#include "jsm.h"
+
+MODULE_AUTHOR("Digi International, http://www.digi.com");
+MODULE_DESCRIPTION("Driver for the Digi International Neo PCI based product line");
+MODULE_SUPPORTED_DEVICE("jsm");
+
+#define JSM_DRIVER_NAME "jsm"
+#define NR_PORTS	32 
+#define JSM_MINOR_START	0 
+
+struct uart_driver jsm_uart_driver = {
+	.owner		= THIS_MODULE,
+	.driver_name	= JSM_DRIVER_NAME,
+	.dev_name	= "ttyn", 
+	.major		= 253,
+	.minor		= JSM_MINOR_START, 
+	.nr		= NR_PORTS,
+	.cons		= NULL,
+};
+
+int jsm_debug;
+int jsm_rawreadok;
+module_param(jsm_debug, int, 0);
+module_param(jsm_rawreadok, int, 1);
+MODULE_PARM_DESC(jsm_debug, "Driver debugging level");
+MODULE_PARM_DESC(jsm_rawreadok, "Bypass flip buffers on input");
+
+/*
+ * Globals
+ */
+int		jsm_driver_state = DRIVER_INITIALIZED;
+spinlock_t	jsm_board_head_lock = SPIN_LOCK_UNLOCKED;
+LIST_HEAD(jsm_board_head);
+
+static struct pci_device_id jsm_pci_tbl[] = {
+	{ PCI_DEVICE (PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_NEO_2DB9),	0,	0,	0 },
+	{ PCI_DEVICE (PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_NEO_2DB9PRI),	0,	0,	1 },
+	{ PCI_DEVICE (PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_NEO_2RJ45),	0,	0,	2 },
+	{ PCI_DEVICE (PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_NEO_2RJ45PRI),	0,	0,	3 },
+	{ 0,}						/* 0 terminated list. */
+};
+MODULE_DEVICE_TABLE(pci, jsm_pci_tbl);
+
+static struct board_id jsm_Ids[] = {
+	{ PCI_DEVICE_NEO_2DB9_PCI_NAME,		2 },
+	{ PCI_DEVICE_NEO_2DB9PRI_PCI_NAME,	2 },
+	{ PCI_DEVICE_NEO_2RJ45_PCI_NAME,	2 },
+	{ PCI_DEVICE_NEO_2RJ45PRI_PCI_NAME,	2 },
+	{ NULL,					0 }
+};
+
+char *jsm_driver_state_text[] = {
+	"Driver Initialized",
+	"Driver Ready."
+};
+
+static int jsm_finalize_board_init(struct jsm_board *brd)
+{
+	int rc = 0;
+
+	jsm_printk(INIT, INFO, &brd->pci_dev, "start\n");
+
+	if (brd->irq) {
+		rc = request_irq(brd->irq, brd->bd_ops->intr, SA_INTERRUPT|SA_SHIRQ, "JSM", brd);
+
+		if (rc) {
+			printk(KERN_WARNING "Failed to hook IRQ %d\n",brd->irq);
+			brd->state = BOARD_FAILED;
+			brd->dpastatus = BD_NOFEP;
+			rc = -ENODEV;
+		} else
+			jsm_printk(INIT, INFO, &brd->pci_dev,
+				"Requested and received usage of IRQ %d\n", brd->irq);
+	}
+	return rc;
+}
+
+/*
+ * jsm_found_board()
+ *
+ * A board has been found, init it.
+ */
+static int jsm_found_board(struct pci_dev *pdev, int id)
+{
+	struct jsm_board *brd;
+	int i = 0;
+	int rc = 0;
+	struct list_head *tmp;
+	struct jsm_board *cur_board_entry;
+	unsigned long lock_flags;
+	int adapter_count = 0;
+	int retval;
+
+	brd = kmalloc(sizeof(struct jsm_board), GFP_KERNEL);
+	if (!brd) {
+		dev_err(&pdev->dev, "memory allocation for board structure failed\n");
+		return -ENOMEM;
+	}
+	memset(brd, 0, sizeof(struct jsm_board));
+
+	spin_lock_irqsave(&jsm_board_head_lock, lock_flags);
+	list_for_each(tmp, &jsm_board_head) {
+		cur_board_entry = 
+			list_entry(tmp, struct jsm_board,
+				jsm_board_entry);
+		if (cur_board_entry->boardnum != adapter_count) {
+			break;
+		}
+		adapter_count++;
+	}
+
+	list_add_tail(&brd->jsm_board_entry, &jsm_board_head);
+	spin_unlock_irqrestore(&jsm_board_head_lock, lock_flags);
+
+	/* store the info for the board we've found */
+	brd->boardnum = adapter_count;
+	brd->pci_dev = pdev;
+	brd->name = jsm_Ids[id].name;
+	brd->maxports = jsm_Ids[id].maxports;
+	brd->dpastatus = BD_NOFEP;
+	init_waitqueue_head(&brd->state_wait);
+
+	spin_lock_init(&brd->bd_lock);
+	spin_lock_init(&brd->bd_intr_lock);
+
+	brd->state = BOARD_FOUND;
+
+	for (i = 0; i < brd->maxports; i++)
+		brd->channels[i] = NULL;
+
+	/* store which revision we have */
+	pci_read_config_byte(pdev, PCI_REVISION_ID, &brd->rev);
+
+	brd->irq = pdev->irq;
+
+	switch(brd->pci_dev->device) {
+
+	case PCI_DEVICE_ID_NEO_2DB9:
+	case PCI_DEVICE_ID_NEO_2DB9PRI:
+	case PCI_DEVICE_ID_NEO_2RJ45:
+	case PCI_DEVICE_ID_NEO_2RJ45PRI:
+
+		/*
+		 * This chip is set up 100% when we get to it.
+		 * No need to enable global interrupts or anything. 
+		 */
+		brd->dpatype = T_NEO | T_PCIBUS;
+
+		jsm_printk(INIT, INFO, &brd->pci_dev,
+			"jsm_found_board - NEO adapter\n");
+
+		/* get the PCI Base Address Registers */
+		brd->membase	= pci_resource_start(pdev, 0);
+		brd->membase_end = pci_resource_end(pdev, 0);
+
+		if (brd->membase & 1)
+			brd->membase &= ~3;
+		else
+			brd->membase &= ~15;
+
+		/* Assign the board_ops struct */
+		brd->bd_ops = &jsm_neo_ops;
+
+		brd->bd_uart_offset = 0x200;
+		brd->bd_dividend = 921600;
+
+		brd->re_map_membase = ioremap(brd->membase, 0x1000);
+		jsm_printk(INIT, INFO, &brd->pci_dev,
+			"remapped mem: 0x%p\n", brd->re_map_membase);
+		if (!brd->re_map_membase) {
+			kfree(brd);
+			dev_err(&pdev->dev, "card has no PCI Memory resources, failing board.\n");
+			return -ENOMEM;
+		}
+		break;
+
+	default:
+		dev_err(&pdev->dev, "Did not find any compatible Neo or Classic PCI boards in system.\n");
+		kfree(brd);
+		return -ENXIO;
+	}
+
+	/*
+	 * Do tty device initialization.
+	 */
+	rc = jsm_finalize_board_init(brd);
+	if (rc < 0) {
+		dev_err(&pdev->dev, "Can't finalize board init (%d)\n", rc);
+		brd->state = BOARD_FAILED;
+		retval = -ENXIO;
+		goto failed0;
+	}
+
+	rc = jsm_tty_init(brd);
+	if (rc < 0) {
+		dev_err(&pdev->dev, "Can't init tty devices (%d)\n", rc);
+		brd->state = BOARD_FAILED;
+		retval = -ENXIO;
+		goto failed1;
+	}
+
+	rc = jsm_uart_port_init(brd);
+	if (rc < 0) {
+		dev_err(&pdev->dev, "Can't init uart port (%d)\n", rc);
+		brd->state = BOARD_FAILED;
+		retval = -ENXIO;
+		goto failed1;
+	}
+
+	brd->state = BOARD_READY;
+	brd->dpastatus = BD_RUNNING;
+
+	/* Log the information about the board */
+	dev_info(&pdev->dev, "board %d: %s (rev %d), irq %d\n",adapter_count, brd->name, brd->rev, brd->irq);
+
+	/*
+	 * allocate flip buffer for board.
+	 *
+	 * Okay to malloc with GFP_KERNEL, we are not at interrupt
+	 * context, and there are no locks held.
+	 */
+	brd->flipbuf = kmalloc(MYFLIPLEN, GFP_KERNEL);
+	if (!brd->flipbuf) {
+		dev_err(&pdev->dev, "memory allocation for flipbuf failed\n");
+		brd->state = BOARD_FAILED;
+		retval = -ENOMEM;
+		goto failed1;
+	}
+	memset(brd->flipbuf, 0, MYFLIPLEN);
+
+	jsm_create_driver_sysfiles(pdev->dev.driver);
+
+	wake_up_interruptible(&brd->state_wait);
+	return 0;
+failed1:
+	free_irq(brd->irq, brd);
+failed0:
+	kfree(brd);
+	iounmap(brd->re_map_membase);
+	return retval;
+}
+
+/* returns count (>= 0), or negative on error */
+static int jsm_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+	int rc;
+
+	rc = pci_enable_device(pdev);
+	if (rc) {
+		dev_err(&pdev->dev, "Device enable FAILED\n");
+		return rc;
+	}
+
+	if ((rc = pci_request_regions(pdev, "jsm"))) {
+	dev_err(&pdev->dev, "pci_request_region FAILED\n");
+		pci_disable_device(pdev);
+		return rc;
+	}
+
+	if ((rc = jsm_found_board(pdev, ent->driver_data))) {
+		dev_err(&pdev->dev, "jsm_found_board FAILED\n");
+		pci_release_regions(pdev);
+		pci_disable_device(pdev);
+	 	return rc;
+	}
+	return rc;
+}
+
+
+/*
+ * jsm_cleanup_board()
+ *
+ * Free all the memory associated with a board
+ */
+static void jsm_cleanup_board(struct jsm_board *brd)
+{
+	int i = 0;
+
+	free_irq(brd->irq, brd);
+	iounmap(brd->re_map_membase);
+
+	/* Free all allocated channels structs */
+	for (i = 0; i < brd->maxports; i++) {
+		if (brd->channels[i]) {
+			if (brd->channels[i]->ch_rqueue)
+				kfree(brd->channels[i]->ch_rqueue);
+			if (brd->channels[i]->ch_equeue)
+				kfree(brd->channels[i]->ch_equeue);
+			if (brd->channels[i]->ch_wqueue)
+				kfree(brd->channels[i]->ch_wqueue);
+			kfree(brd->channels[i]);
+		}
+	}
+
+	pci_release_regions(brd->pci_dev);
+	pci_disable_device(brd->pci_dev);
+	kfree(brd->flipbuf);
+	kfree(brd);
+}
+
+static void jsm_remove_one(struct pci_dev *dev)
+{
+	unsigned long lock_flags;
+	struct list_head *tmp;
+	struct jsm_board *brd;
+
+	spin_lock_irqsave(&jsm_board_head_lock, lock_flags);
+	list_for_each(tmp, &jsm_board_head) {
+		brd = list_entry(tmp, struct jsm_board,
+					jsm_board_entry);
+		if ( brd != NULL && brd->pci_dev == dev) {
+			jsm_remove_uart_port(brd);
+			jsm_cleanup_board(brd);
+			list_del(&brd->jsm_board_entry);
+			break;
+		}
+	}
+	spin_unlock_irqrestore(&jsm_board_head_lock, lock_flags);
+	return;
+}
+
+struct pci_driver jsm_driver = {
+	.name		= "jsm",
+	.probe		= jsm_init_one,
+	.id_table	= jsm_pci_tbl,
+	.remove		= __devexit_p(jsm_remove_one),
+};
+
+/*
+ * jsm_init_module()
+ *
+ * Module load.  This is where it all starts.
+ */
+static int __init jsm_init_module(void)
+{
+	int rc = 0;
+
+	printk(KERN_INFO "%s, Digi International Part Number %s\n",
+			JSM_VERSION, JSM_VERSION);
+
+	/*
+	 * Initialize global stuff
+	 */
+
+	rc = uart_register_driver(&jsm_uart_driver);
+	if (rc < 0) {
+		return rc;
+	}
+
+	rc = pci_register_driver(&jsm_driver);
+	if (rc < 0) {
+		uart_unregister_driver(&jsm_uart_driver);
+		return rc;
+	}
+	jsm_driver_state = DRIVER_READY;
+
+	return rc;
+}
+
+module_init(jsm_init_module);
+
+/*
+ * jsm_exit_module()
+ *
+ * Module unload.  This is where it all ends.
+ */
+static void __exit jsm_exit_module(void)
+{
+	jsm_remove_driver_sysfiles(&jsm_driver.driver);
+
+	pci_unregister_driver(&jsm_driver);
+
+	uart_unregister_driver(&jsm_uart_driver);
+}
+module_exit(jsm_exit_module);
+MODULE_LICENSE("GPL");

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [ patch 1/5] drivers/serial/jsm: new serial device driver
  2005-03-14 17:35                           ` Wen Xiong
@ 2005-03-14 20:24                             ` Domen Puncer
  2005-03-14 21:24                               ` Wen Xiong
  0 siblings, 1 reply; 34+ messages in thread
From: Domen Puncer @ 2005-03-14 20:24 UTC (permalink / raw)
  To: Wen Xiong; +Cc: Greg KH, linux-kernel

On 14/03/05 12:35 -0500, Wen Xiong wrote:
> Domen Puncer wrote:
> 
> >Just some nitpicking...
> >
> > 
> >
> Hi Domen, all,
> 
> Thanks for your comments. I  did some minor changes for patch1 based on 
> Domen's comment.
> 

And i missed, what is probably a bug:


> +module_param(jsm_debug, int, 0);
> +module_param(jsm_rawreadok, int, 1);

Last parameter is sysfs file mode, or 0 if no file is to be created.


	Domen

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [ patch 1/5] drivers/serial/jsm: new serial device driver
  2005-03-14 20:24                             ` Domen Puncer
@ 2005-03-14 21:24                               ` Wen Xiong
  0 siblings, 0 replies; 34+ messages in thread
From: Wen Xiong @ 2005-03-14 21:24 UTC (permalink / raw)
  To: Domen Puncer; +Cc: Wen Xiong, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 557 bytes --]

Domen Puncer wrote:

>On 14/03/05 12:35 -0500, Wen Xiong wrote:
>  
>
>>Domen Puncer wrote:
>>
>>    
>>
>>>Just some nitpicking...
>>>
>>>
>>>
>>>      
>>>
>>Hi Domen, all,
>>
>>Thanks for your comments. I  did some minor changes for patch1 based on 
>>Domen's comment.
>>
>>    
>>
>
>And i missed, what is probably a bug:
>
>
>  
>
>>+module_param(jsm_debug, int, 0);
>>+module_param(jsm_rawreadok, int, 1);
>>    
>>
>
>Last parameter is sysfs file mode, or 0 if no file is to be created.
>
>
>	Domen
>
>  
>
I should set the mode to 0.

Thanks,
wendy

[-- Attachment #2: patch1.jasmine --]
[-- Type: text/plain, Size: 10668 bytes --]

diff -Nuar linux-2.6.11.org/drivers/serial/jsm/jsm_driver.c linux-2.6.11.new/drivers/serial/jsm/jsm_driver.c
--- linux-2.6.11.org/drivers/serial/jsm/jsm_driver.c	1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.11.new/drivers/serial/jsm/jsm_driver.c	2005-03-14 15:23:09.283905656 -0600
@@ -0,0 +1,404 @@
+/************************************************************************
+ * Copyright 2003 Digi International (www.digi.com)
+ *
+ * Copyright (C) 2004 IBM Corporation. All rights reserved.
+ *
+ * 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, or (at your option)
+ * any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED; without even the 
+ * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License 
+ * along with this program; if not, write to the Free Software 
+ * Foundation, Inc., 59 * Temple Place - Suite 330, Boston,
+ * MA  02111-1307, USA.
+ *
+ * Contact Information:
+ * Scott H Kilau <Scott_Kilau@digi.com>
+ * Wendy Xiong   <wendyx@us.ltcfwd.linux.ibm.com>
+ *
+ ***********************************************************************/
+#include <linux/moduleparam.h>
+#include <linux/pci.h>
+
+#include "jsm.h"
+
+MODULE_AUTHOR("Digi International, http://www.digi.com");
+MODULE_DESCRIPTION("Driver for the Digi International Neo PCI based product line");
+MODULE_SUPPORTED_DEVICE("jsm");
+
+#define JSM_DRIVER_NAME "jsm"
+#define NR_PORTS	32 
+#define JSM_MINOR_START	0 
+
+struct uart_driver jsm_uart_driver = {
+	.owner		= THIS_MODULE,
+	.driver_name	= JSM_DRIVER_NAME,
+	.dev_name	= "ttyn", 
+	.major		= 253,
+	.minor		= JSM_MINOR_START, 
+	.nr		= NR_PORTS,
+	.cons		= NULL,
+};
+
+int jsm_debug;
+int jsm_rawreadok;
+module_param(jsm_debug, int, 0);
+module_param(jsm_rawreadok, int, 0);
+MODULE_PARM_DESC(jsm_debug, "Driver debugging level");
+MODULE_PARM_DESC(jsm_rawreadok, "Bypass flip buffers on input");
+
+/*
+ * Globals
+ */
+int		jsm_driver_state = DRIVER_INITIALIZED;
+spinlock_t	jsm_board_head_lock = SPIN_LOCK_UNLOCKED;
+LIST_HEAD(jsm_board_head);
+
+static struct pci_device_id jsm_pci_tbl[] = {
+	{ PCI_DEVICE (PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_NEO_2DB9),	0,	0,	0 },
+	{ PCI_DEVICE (PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_NEO_2DB9PRI),	0,	0,	1 },
+	{ PCI_DEVICE (PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_NEO_2RJ45),	0,	0,	2 },
+	{ PCI_DEVICE (PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_NEO_2RJ45PRI),	0,	0,	3 },
+	{ 0,}						/* 0 terminated list. */
+};
+MODULE_DEVICE_TABLE(pci, jsm_pci_tbl);
+
+static struct board_id jsm_Ids[] = {
+	{ PCI_DEVICE_NEO_2DB9_PCI_NAME,		2 },
+	{ PCI_DEVICE_NEO_2DB9PRI_PCI_NAME,	2 },
+	{ PCI_DEVICE_NEO_2RJ45_PCI_NAME,	2 },
+	{ PCI_DEVICE_NEO_2RJ45PRI_PCI_NAME,	2 },
+	{ NULL,					0 }
+};
+
+char *jsm_driver_state_text[] = {
+	"Driver Initialized",
+	"Driver Ready."
+};
+
+static int jsm_finalize_board_init(struct jsm_board *brd)
+{
+	int rc = 0;
+
+	jsm_printk(INIT, INFO, &brd->pci_dev, "start\n");
+
+	if (brd->irq) {
+		rc = request_irq(brd->irq, brd->bd_ops->intr, SA_INTERRUPT|SA_SHIRQ, "JSM", brd);
+
+		if (rc) {
+			printk(KERN_WARNING "Failed to hook IRQ %d\n",brd->irq);
+			brd->state = BOARD_FAILED;
+			brd->dpastatus = BD_NOFEP;
+			rc = -ENODEV;
+		} else
+			jsm_printk(INIT, INFO, &brd->pci_dev,
+				"Requested and received usage of IRQ %d\n", brd->irq);
+	}
+	return rc;
+}
+
+/*
+ * jsm_found_board()
+ *
+ * A board has been found, init it.
+ */
+static int jsm_found_board(struct pci_dev *pdev, int id)
+{
+	struct jsm_board *brd;
+	int i = 0;
+	int rc = 0;
+	struct list_head *tmp;
+	struct jsm_board *cur_board_entry;
+	unsigned long lock_flags;
+	int adapter_count = 0;
+	int retval;
+
+	brd = kmalloc(sizeof(struct jsm_board), GFP_KERNEL);
+	if (!brd) {
+		dev_err(&pdev->dev, "memory allocation for board structure failed\n");
+		return -ENOMEM;
+	}
+	memset(brd, 0, sizeof(struct jsm_board));
+
+	spin_lock_irqsave(&jsm_board_head_lock, lock_flags);
+	list_for_each(tmp, &jsm_board_head) {
+		cur_board_entry = 
+			list_entry(tmp, struct jsm_board,
+				jsm_board_entry);
+		if (cur_board_entry->boardnum != adapter_count) {
+			break;
+		}
+		adapter_count++;
+	}
+
+	list_add_tail(&brd->jsm_board_entry, &jsm_board_head);
+	spin_unlock_irqrestore(&jsm_board_head_lock, lock_flags);
+
+	/* store the info for the board we've found */
+	brd->boardnum = adapter_count;
+	brd->pci_dev = pdev;
+	brd->name = jsm_Ids[id].name;
+	brd->maxports = jsm_Ids[id].maxports;
+	brd->dpastatus = BD_NOFEP;
+	init_waitqueue_head(&brd->state_wait);
+
+	spin_lock_init(&brd->bd_lock);
+	spin_lock_init(&brd->bd_intr_lock);
+
+	brd->state = BOARD_FOUND;
+
+	for (i = 0; i < brd->maxports; i++)
+		brd->channels[i] = NULL;
+
+	/* store which revision we have */
+	pci_read_config_byte(pdev, PCI_REVISION_ID, &brd->rev);
+
+	brd->irq = pdev->irq;
+
+	switch(brd->pci_dev->device) {
+
+	case PCI_DEVICE_ID_NEO_2DB9:
+	case PCI_DEVICE_ID_NEO_2DB9PRI:
+	case PCI_DEVICE_ID_NEO_2RJ45:
+	case PCI_DEVICE_ID_NEO_2RJ45PRI:
+
+		/*
+		 * This chip is set up 100% when we get to it.
+		 * No need to enable global interrupts or anything. 
+		 */
+		brd->dpatype = T_NEO | T_PCIBUS;
+
+		jsm_printk(INIT, INFO, &brd->pci_dev,
+			"jsm_found_board - NEO adapter\n");
+
+		/* get the PCI Base Address Registers */
+		brd->membase	= pci_resource_start(pdev, 0);
+		brd->membase_end = pci_resource_end(pdev, 0);
+
+		if (brd->membase & 1)
+			brd->membase &= ~3;
+		else
+			brd->membase &= ~15;
+
+		/* Assign the board_ops struct */
+		brd->bd_ops = &jsm_neo_ops;
+
+		brd->bd_uart_offset = 0x200;
+		brd->bd_dividend = 921600;
+
+		brd->re_map_membase = ioremap(brd->membase, 0x1000);
+		jsm_printk(INIT, INFO, &brd->pci_dev,
+			"remapped mem: 0x%p\n", brd->re_map_membase);
+		if (!brd->re_map_membase) {
+			kfree(brd);
+			dev_err(&pdev->dev, "card has no PCI Memory resources, failing board.\n");
+			return -ENOMEM;
+		}
+		break;
+
+	default:
+		dev_err(&pdev->dev, "Did not find any compatible Neo or Classic PCI boards in system.\n");
+		kfree(brd);
+		return -ENXIO;
+	}
+
+	/*
+	 * Do tty device initialization.
+	 */
+	rc = jsm_finalize_board_init(brd);
+	if (rc < 0) {
+		dev_err(&pdev->dev, "Can't finalize board init (%d)\n", rc);
+		brd->state = BOARD_FAILED;
+		retval = -ENXIO;
+		goto failed0;
+	}
+
+	rc = jsm_tty_init(brd);
+	if (rc < 0) {
+		dev_err(&pdev->dev, "Can't init tty devices (%d)\n", rc);
+		brd->state = BOARD_FAILED;
+		retval = -ENXIO;
+		goto failed1;
+	}
+
+	rc = jsm_uart_port_init(brd);
+	if (rc < 0) {
+		dev_err(&pdev->dev, "Can't init uart port (%d)\n", rc);
+		brd->state = BOARD_FAILED;
+		retval = -ENXIO;
+		goto failed1;
+	}
+
+	brd->state = BOARD_READY;
+	brd->dpastatus = BD_RUNNING;
+
+	/* Log the information about the board */
+	dev_info(&pdev->dev, "board %d: %s (rev %d), irq %d\n",adapter_count, brd->name, brd->rev, brd->irq);
+
+	/*
+	 * allocate flip buffer for board.
+	 *
+	 * Okay to malloc with GFP_KERNEL, we are not at interrupt
+	 * context, and there are no locks held.
+	 */
+	brd->flipbuf = kmalloc(MYFLIPLEN, GFP_KERNEL);
+	if (!brd->flipbuf) {
+		dev_err(&pdev->dev, "memory allocation for flipbuf failed\n");
+		brd->state = BOARD_FAILED;
+		retval = -ENOMEM;
+		goto failed1;
+	}
+	memset(brd->flipbuf, 0, MYFLIPLEN);
+
+	jsm_create_driver_sysfiles(pdev->dev.driver);
+
+	wake_up_interruptible(&brd->state_wait);
+	return 0;
+failed1:
+	free_irq(brd->irq, brd);
+failed0:
+	kfree(brd);
+	iounmap(brd->re_map_membase);
+	return retval;
+}
+
+/* returns count (>= 0), or negative on error */
+static int jsm_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+	int rc;
+
+	rc = pci_enable_device(pdev);
+	if (rc) {
+		dev_err(&pdev->dev, "Device enable FAILED\n");
+		return rc;
+	}
+
+	if ((rc = pci_request_regions(pdev, "jsm"))) {
+	dev_err(&pdev->dev, "pci_request_region FAILED\n");
+		pci_disable_device(pdev);
+		return rc;
+	}
+
+	if ((rc = jsm_found_board(pdev, ent->driver_data))) {
+		dev_err(&pdev->dev, "jsm_found_board FAILED\n");
+		pci_release_regions(pdev);
+		pci_disable_device(pdev);
+	 	return rc;
+	}
+	return rc;
+}
+
+
+/*
+ * jsm_cleanup_board()
+ *
+ * Free all the memory associated with a board
+ */
+static void jsm_cleanup_board(struct jsm_board *brd)
+{
+	int i = 0;
+
+	free_irq(brd->irq, brd);
+	iounmap(brd->re_map_membase);
+
+	/* Free all allocated channels structs */
+	for (i = 0; i < brd->maxports; i++) {
+		if (brd->channels[i]) {
+			if (brd->channels[i]->ch_rqueue)
+				kfree(brd->channels[i]->ch_rqueue);
+			if (brd->channels[i]->ch_equeue)
+				kfree(brd->channels[i]->ch_equeue);
+			if (brd->channels[i]->ch_wqueue)
+				kfree(brd->channels[i]->ch_wqueue);
+			kfree(brd->channels[i]);
+		}
+	}
+
+	pci_release_regions(brd->pci_dev);
+	pci_disable_device(brd->pci_dev);
+	kfree(brd->flipbuf);
+	kfree(brd);
+}
+
+static void jsm_remove_one(struct pci_dev *dev)
+{
+	unsigned long lock_flags;
+	struct list_head *tmp;
+	struct jsm_board *brd;
+
+	spin_lock_irqsave(&jsm_board_head_lock, lock_flags);
+	list_for_each(tmp, &jsm_board_head) {
+		brd = list_entry(tmp, struct jsm_board,
+					jsm_board_entry);
+		if ( brd != NULL && brd->pci_dev == dev) {
+			jsm_remove_uart_port(brd);
+			jsm_cleanup_board(brd);
+			list_del(&brd->jsm_board_entry);
+			break;
+		}
+	}
+	spin_unlock_irqrestore(&jsm_board_head_lock, lock_flags);
+	return;
+}
+
+struct pci_driver jsm_driver = {
+	.name		= "jsm",
+	.probe		= jsm_init_one,
+	.id_table	= jsm_pci_tbl,
+	.remove		= __devexit_p(jsm_remove_one),
+};
+
+/*
+ * jsm_init_module()
+ *
+ * Module load.  This is where it all starts.
+ */
+static int __init jsm_init_module(void)
+{
+	int rc = 0;
+
+	printk(KERN_INFO "%s, Digi International Part Number %s\n",
+			JSM_VERSION, JSM_VERSION);
+
+	/*
+	 * Initialize global stuff
+	 */
+
+	rc = uart_register_driver(&jsm_uart_driver);
+	if (rc < 0) {
+		return rc;
+	}
+
+	rc = pci_register_driver(&jsm_driver);
+	if (rc < 0) {
+		uart_unregister_driver(&jsm_uart_driver);
+		return rc;
+	}
+	jsm_driver_state = DRIVER_READY;
+
+	return rc;
+}
+
+module_init(jsm_init_module);
+
+/*
+ * jsm_exit_module()
+ *
+ * Module unload.  This is where it all ends.
+ */
+static void __exit jsm_exit_module(void)
+{
+	jsm_remove_driver_sysfiles(&jsm_driver.driver);
+
+	pci_unregister_driver(&jsm_driver);
+
+	uart_unregister_driver(&jsm_uart_driver);
+}
+module_exit(jsm_exit_module);
+MODULE_LICENSE("GPL");

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [ patch 2/5] drivers/serial/jsm: new serial device driver
  2005-03-11 15:32                       ` [ patch 2/5] " Wen Xiong
@ 2005-03-30 14:55                         ` Russell King
  0 siblings, 0 replies; 34+ messages in thread
From: Russell King @ 2005-03-30 14:55 UTC (permalink / raw)
  To: Wen Xiong; +Cc: Greg KH, linux-kernel

Here's some belated comments.  I won't even pretend to understand
any of your (imo overcomplex) driver - which is the reason I haven't
bothered commenting before now.

However, it seems that there may be some duplication between what
you're doing and what the rest of the kernel is doing for you.
This may not be desirable, and may actually cause subtle problems,
especially if you and the rest of the kernel decides to send, eg,
an XOFF character (so the remote end sees two XOFF characters).

On Fri, Mar 11, 2005 at 10:32:09AM -0500, Wen Xiong wrote:
> diff -Nuar linux-2.6.11.org/drivers/serial/jsm/jsm_tty.c linux-2.6.11.new/drivers/serial/jsm/jsm_tty.c
> --- linux-2.6.11.org/drivers/serial/jsm/jsm_tty.c	1969-12-31 18:00:00.000000000 -0600
> +++ linux-2.6.11.new/drivers/serial/jsm/jsm_tty.c	2005-03-10 16:34:37.342965976 -0600
> +static void jsm_tty_close(struct uart_port *port)
> +{
> +	struct jsm_board *bd;
> +	struct termios *ts;
> +	struct jsm_channel *channel = (struct jsm_channel *)port;
> +
> +	jsm_printk(CLOSE, INFO, &channel->ch_bd->pci_dev, "start\n");
> +
> +	bd = channel->ch_bd;
> +	ts = channel->uart_port.info->tty->termios;
> +
> +	channel->ch_flags &= ~(CH_STOPI);
> +
> +	channel->ch_open_count--;
> +
> +	/*
> +	 * If we have HUPCL set, lower DTR and RTS
> +	 */
> +	if (channel->ch_c_cflag & HUPCL) {
> +		jsm_printk(CLOSE, INFO, &channel->ch_bd->pci_dev,
> +			"Close. HUPCL set, dropping DTR/RTS\n");
> +
> +		/* Drop RTS/DTR */
> +		channel->ch_mostat &= ~(UART_MCR_DTR | UART_MCR_RTS);
> +		bd->bd_ops->assert_modem_signals(channel);
> +	}

This is not necessary.  Since you're using the serial core, if you
look at what it's already doing for you, you'll see that uart_shutdown()
already takes care of this.

> +void jsm_check_queue_flow_control(struct jsm_channel *ch)
> +{
> +	int qleft = 0;
> +
> +	/* Store how much space we have left in the queue */
> +	if ((qleft = ch->ch_r_tail - ch->ch_r_head - 1) < 0)
> +		qleft += RQUEUEMASK + 1;
> +
> +	/*
> +	 * Check to see if we should enforce flow control on our queue because
> +	 * the ld (or user) isn't reading data out of our queue fast enuf.
> +	 *
> +	 * NOTE: This is done based on what the current flow control of the
> +	 * port is set for.
> +	 *
> +	 * 1) HWFLOW (RTS) - Turn off the UART's Receive interrupt.
> +	 *	This will cause the UART's FIFO to back up, and force
> +	 *	the RTS signal to be dropped.
> +	 * 2) SWFLOW (IXOFF) - Keep trying to send a stop character to
> +	 *	the other side, in hopes it will stop sending data to us.
> +	 * 3) NONE - Nothing we can do.  We will simply drop any extra data
> +	 *	that gets sent into us when the queue fills up.
> +	 */
> +	if (qleft < 256) {
> +		/* HWFLOW */
> +		if (ch->ch_c_cflag & CRTSCTS) {
> +			if(!(ch->ch_flags & CH_RECEIVER_OFF)) {
> +				ch->ch_bd->bd_ops->disable_receiver(ch);
> +				ch->ch_flags |= (CH_RECEIVER_OFF);
> +				jsm_printk(READ, INFO, &ch->ch_bd->pci_dev,
> +					"Internal queue hit hilevel mark (%d)! Turning off interrupts.\n",
> +					qleft);
> +			}
> +		}
> +		/* SWFLOW */
> +		else if (ch->ch_c_iflag & IXOFF) {
> +			if (ch->ch_stops_sent <= MAX_STOPS_SENT) {
> +				ch->ch_bd->bd_ops->send_stop_character(ch);
> +				ch->ch_stops_sent++;
> +				jsm_printk(READ, INFO, &ch->ch_bd->pci_dev,
> +					"Sending stop char! Times sent: %x\n", ch->ch_stops_sent);
> +			}
> +		}
> +	}
> +
> +	/*
> +	 * Check to see if we should unenforce flow control because
> +	 * ld (or user) finally read enuf data out of our queue.
> +	 *
> +	 * NOTE: This is done based on what the current flow control of the
> +	 * port is set for.
> +	 *
> +	 * 1) HWFLOW (RTS) - Turn back on the UART's Receive interrupt.
> +	 *	This will cause the UART's FIFO to raise RTS back up,
> +	 *	which will allow the other side to start sending data again.
> +	 * 2) SWFLOW (IXOFF) - Send a start character to
> +	 *	the other side, so it will start sending data to us again.
> +	 * 3) NONE - Do nothing. Since we didn't do anything to turn off the
> +	 *	other side, we don't need to do anything now.
> +	 */
> +	if (qleft > (RQUEUESIZE / 2)) {
> +		/* HWFLOW */
> +		if (ch->ch_c_cflag & CRTSCTS) {
> +			if (ch->ch_flags & CH_RECEIVER_OFF) {
> +				ch->ch_bd->bd_ops->enable_receiver(ch);
> +				ch->ch_flags &= ~(CH_RECEIVER_OFF);
> +				jsm_printk(READ, INFO, &ch->ch_bd->pci_dev,
> +					"Internal queue hit lowlevel mark (%d)! Turning on interrupts.\n",
> +					qleft);
> +			}
> +		}
> +		/* SWFLOW */
> +		else if (ch->ch_c_iflag & IXOFF && ch->ch_stops_sent) {
> +			ch->ch_stops_sent = 0;
> +			ch->ch_bd->bd_ops->send_start_character(ch);
> +			jsm_printk(READ, INFO, &ch->ch_bd->pci_dev, "Sending start char!\n");
> +		}
> +	}
> +}

Wouldn't you think the kernel already takers are of flow control, given
that it already handles the sending of the X* characters?

-- 
Russell King
 Linux kernel    2.6 ARM Linux   - http://www.arm.linux.org.uk/
 maintainer of:  2.6 Serial core

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [ patch 3/5] drivers/serial/jsm: new serial device driver
  2005-03-11 22:34                               ` Wen Xiong
@ 2005-03-30 15:01                                 ` Russell King
  0 siblings, 0 replies; 34+ messages in thread
From: Russell King @ 2005-03-30 15:01 UTC (permalink / raw)
  To: Wen Xiong; +Cc: Arjan van de Ven, Greg KH, linux-kernel

On Fri, Mar 11, 2005 at 05:34:18PM -0500, Wen Xiong wrote:
> diff -Nuar linux-2.6.11.org/drivers/serial/jsm/jsm_neo.c linux-2.6.11.new/drivers/serial/jsm/jsm_neo.c
> --- linux-2.6.11.org/drivers/serial/jsm/jsm_neo.c	1969-12-31 18:00:00.000000000 -0600
> +++ linux-2.6.11.new/drivers/serial/jsm/jsm_neo.c	2005-03-11 16:26:47.442988256 -0600
> +/*
> + * neo_param()
> + * Send any/all changes to the line to the UART.
> + */
> +static void neo_param(struct jsm_channel *ch)
> +{
> +	u8 lcr = 0;
> +	u8 uart_lcr = 0;
> +	u8 ier = 0;
> +	u32 baud = 9600;
> +	int quot = 0;
> +	struct jsm_board *bd;
> +
> +	bd = ch->ch_bd;
> +	if (!bd)
> +		return;
> +
> +	/*
> +	 * If baud rate is zero, flush queues, and set mval to drop DTR.
> +	 */

The modem signal side of this is already handled for you.

> +			const u64 bauds[4][16] = {
> +				{ 
> +					0,	50,	75,	110,
> +					134,	150,	200,	300,
> +					600,	1200,	1800,	2400,
> +					4800,	9600,	19200,	38400 },
> +				{ 
> +					0,	57600,	115200, 230400,
> +					460800, 150,	200,	921600,
> +					600,	1200,	1800,	2400,
> +					4800,	9600,	19200,	38400 },
> +				{ 
> +					0,	57600,	76800, 115200,
> +					131657, 153600, 230400, 460800,
> +					921600, 1200,	1800,	2400,
> +					4800,	9600,	19200,	38400 },
> +				{ 
> +					0,	57600,	115200, 230400,
> +					460800, 150,	200,	921600,
> +					600,	1200,	1800,	2400,
> +					4800,	9600,	19200,	38400 }
> +			};
> +
> +			baud = C_BAUD(ch->uart_port.info->tty) & 0xff;
> +
> +			if (ch->ch_c_cflag & CBAUDEX)
> +				iindex = 1;

This is buggy.  You're making invalid assumptions about the
given baud rate flags in the termios.  Use the helper functions
provided please.

-- 
Russell King
 Linux kernel    2.6 ARM Linux   - http://www.arm.linux.org.uk/
 maintainer of:  2.6 Serial core

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [ patch 4/7] drivers/serial/jsm: new serial device driver
  2005-03-08 21:47 Kilau, Scott
  2005-03-09  0:02 ` Greg KH
@ 2005-03-09 16:16 ` Russell King
  1 sibling, 0 replies; 34+ messages in thread
From: Russell King @ 2005-03-09 16:16 UTC (permalink / raw)
  To: Kilau, Scott; +Cc: linux-kernel, greg, Wen Xiong

On Tue, Mar 08, 2005 at 03:47:45PM -0600, Kilau, Scott wrote:
> For example, lets say a customer has a modem connected to a serial port.
> 
> If you were to open up the port with an "stty -a" to get the current 
> settings and signals, you would unintentionally raise RTS and DTR.

That isn't special to this driver though.  Maybe it should be fixed for
all serial drivers, since the situation you mention above is not limited
to just this driver.

As you say, you may have a modem connected, which may have been configured
to automatically dial a predetermined number when DTR is raised.

Maybe we need a solution which applies to all drivers?

> This is why we export the various signals/stats/signals to sysfs (used
> to be proc), so our management tools can get the information about the
> serial port without being intrusive by opening up the port.

Note that exporting statistics can be a security bug, especially if that
includes the number of bytes sent/received.  For an explaination of this,
please lookup the reason why the /proc/tty/driver directory was made
unreadable to userspace.

-- 
Russell King
 Linux kernel    2.6 ARM Linux   - http://www.arm.linux.org.uk/
 maintainer of:  2.6 Serial core

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [ patch 4/7] drivers/serial/jsm: new serial device driver
  2005-03-09  2:36 Kilau, Scott
@ 2005-03-09  5:50 ` Greg KH
  0 siblings, 0 replies; 34+ messages in thread
From: Greg KH @ 2005-03-09  5:50 UTC (permalink / raw)
  To: Kilau, Scott; +Cc: linux-kernel, Wen Xiong

On Tue, Mar 08, 2005 at 08:36:57PM -0600, Kilau, Scott wrote:
> > > 
> > > If you were to open up the port with an "stty -a" to get the current
> 
> > > settings and signals, you would unintentionally raise RTS and DTR.
> >
> > Why not fix the driver to not change the current line settings if it
> is
> > not being opened for the first time?  That seems like a much simpler
> way
> > to solve this, and probably the saner way, as you don't want any user
> to
> > be able to mess up your modem...
> >
> 
> Oh, when the port is already open, the driver correctly would not muck
> with DTR/RTS.
> 
> I am talking about when the port is currently not open.
> 
> On first port open, DTR (and usually RTS) will always be raised.
> The serial device would see this DTR raise, and under some
> circumstances, react to it...

Ok, well, that sounds like something that all serial devices should
support, right?  And if so, why not add this to the serial core for
everyone to benifit?

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 34+ messages in thread

* RE: [ patch 4/7] drivers/serial/jsm: new serial device driver
@ 2005-03-09  2:36 Kilau, Scott
  2005-03-09  5:50 ` Greg KH
  0 siblings, 1 reply; 34+ messages in thread
From: Kilau, Scott @ 2005-03-09  2:36 UTC (permalink / raw)
  To: Greg KH; +Cc: linux-kernel, Wen Xiong

> > 
> > If you were to open up the port with an "stty -a" to get the current

> > settings and signals, you would unintentionally raise RTS and DTR.
>
> Why not fix the driver to not change the current line settings if it
is
> not being opened for the first time?  That seems like a much simpler
way
> to solve this, and probably the saner way, as you don't want any user
to
> be able to mess up your modem...
>
> thanks,
> 
> greg k-h

Oh, when the port is already open, the driver correctly would not muck
with DTR/RTS.

I am talking about when the port is currently not open.

On first port open, DTR (and usually RTS) will always be raised.
The serial device would see this DTR raise, and under some
circumstances,
react to it...

We have customers that use *really* *really* old serial devices on
our products, (we are talking 110 baud and even 50 baud (!!!)),
where an unintentional raise of DTR/RTS will freak the device out.

At any rate, that's the reason I exported the values to sysfs in the
original "dgnc" (outside-the-kernel-sources) driver.

Thanks,
Scott

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [ patch 4/7] drivers/serial/jsm: new serial device driver
  2005-03-08 21:47 Kilau, Scott
@ 2005-03-09  0:02 ` Greg KH
  2005-03-09 16:16 ` Russell King
  1 sibling, 0 replies; 34+ messages in thread
From: Greg KH @ 2005-03-09  0:02 UTC (permalink / raw)
  To: Kilau, Scott; +Cc: linux-kernel, Wen Xiong

On Tue, Mar 08, 2005 at 03:47:45PM -0600, Kilau, Scott wrote:
> > Who needs to know if a port is open or not?
> >
> <snipped some code> 
> >
> > +static ssize_t jsm_tty_baud_show(struct class_device *class_dev, char *buf)
> 
> > No, please delete these, and the other sysfs files that duplicate the
> > same info that you can get by using the standard Linux termios calls.
> > There is no need for them here.
> 
> Our serial port monitoring tools need to know these things, and to
> find them out *without* opening up the serial port to do an ioctl().
> 
> For example, lets say a customer has a modem connected to a serial port.
> 
> If you were to open up the port with an "stty -a" to get the current 
> settings and signals, you would unintentionally raise RTS and DTR.

Why not fix the driver to not change the current line settings if it is
not being opened for the first time?  That seems like a much simpler way
to solve this, and probably the saner way, as you don't want any user to
be able to mess up your modem...

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [ patch 4/7] drivers/serial/jsm: new serial device driver
@ 2005-03-08 21:47 Kilau, Scott
  2005-03-09  0:02 ` Greg KH
  2005-03-09 16:16 ` Russell King
  0 siblings, 2 replies; 34+ messages in thread
From: Kilau, Scott @ 2005-03-08 21:47 UTC (permalink / raw)
  To: linux-kernel; +Cc: greg, Wen Xiong

> Who needs to know if a port is open or not?
>
<snipped some code> 
>
> +static ssize_t jsm_tty_baud_show(struct class_device *class_dev, char
*buf)

> No, please delete these, and the other sysfs files that duplicate the
> same info that you can get by using the standard Linux termios calls.
> There is no need for them here.
> 
> thanks,
> greg k-h

Hi Greg, Wendy, all,

Our serial port monitoring tools need to know these things, and to
find them out *without* opening up the serial port to do an ioctl().

For example, lets say a customer has a modem connected to a serial port.

If you were to open up the port with an "stty -a" to get the current 
settings and signals, you would unintentionally raise RTS and DTR.

Now the modem sees DTR raised, and might react to it by mistake.

Usually raising and dropping RTS/DTR quickly on a modem won't hurt
anything,
but its not particularly a "good" when the modem is not expecting it.

This is why we export the various signals/stats/signals to sysfs (used
to be proc),
so our management tools can get the information about the serial port
without being
intrusive by opening up the port.

Scott

^ permalink raw reply	[flat|nested] 34+ messages in thread

end of thread, other threads:[~2005-03-30 15:02 UTC | newest]

Thread overview: 34+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2005-02-27 23:39 [ patch 4/7] drivers/serial/jsm: new serial device driver Wen Xiong
2005-02-28  3:21 ` Christoph Hellwig
2005-02-28  6:39 ` Greg KH
2005-03-04 21:08   ` Wen Xiong
2005-03-04 22:01     ` Greg KH
2005-03-07 22:46       ` Wen Xiong
2005-03-08  6:44         ` Greg KH
2005-03-08 18:55           ` Wen Xiong
2005-03-08 23:58             ` Greg KH
2005-03-09 15:47               ` Wen Xiong
2005-03-09 16:35                 ` Greg KH
2005-03-09 17:18                   ` Wen Xiong
2005-03-09 18:58                     ` Greg KH
2005-03-11 15:29                       ` [ patch 1/5] " Wen Xiong
2005-03-12 13:06                         ` Domen Puncer
2005-03-14 17:35                           ` Wen Xiong
2005-03-14 20:24                             ` Domen Puncer
2005-03-14 21:24                               ` Wen Xiong
2005-03-11 15:32                       ` [ patch 2/5] " Wen Xiong
2005-03-30 14:55                         ` Russell King
2005-03-11 15:38                       ` [ patch 3/5] " Wen Xiong
2005-03-11 15:53                         ` Arjan van de Ven
2005-03-11 16:39                           ` Wen Xiong
2005-03-11 16:46                             ` Arjan van de Ven
2005-03-11 22:34                               ` Wen Xiong
2005-03-30 15:01                                 ` Russell King
2005-03-11 15:38                       ` [ patch 4/5] " Wen Xiong
2005-03-11 15:38                       ` [ patch 5/5] " Wen Xiong
2005-03-09 16:11           ` [ patch 4/7] " Russell King
2005-03-08 21:47 Kilau, Scott
2005-03-09  0:02 ` Greg KH
2005-03-09 16:16 ` Russell King
2005-03-09  2:36 Kilau, Scott
2005-03-09  5:50 ` Greg KH

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).