patch-2.3.16 linux/drivers/macintosh/macserial.c

Next file: linux/drivers/macintosh/mediabay.c
Previous file: linux/drivers/macintosh/macio-adb.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.3.15/linux/drivers/macintosh/macserial.c linux/drivers/macintosh/macserial.c
@@ -9,6 +9,7 @@
 
 #include <linux/config.h>
 #include <linux/errno.h>
+#include <linux/module.h>
 #include <linux/signal.h>
 #include <linux/sched.h>
 #include <linux/timer.h>
@@ -26,6 +27,7 @@
 #include <linux/console.h>
 #endif
 
+#include <asm/init.h>
 #include <asm/io.h>
 #include <asm/pgtable.h>
 #include <asm/irq.h>
@@ -135,13 +137,13 @@
  * buffer across all the serial ports, since it significantly saves
  * memory if large numbers of serial ports are open.
  */
-static unsigned char tmp_buf[4096]; /* This is cheating */
-DECLARE_MUTEX(tmp_buf_sem);
+static unsigned char *tmp_buf;
+static DECLARE_MUTEX(tmp_buf_sem);
 
 
 static inline int __pmac
 serial_paranoia_check(struct mac_serial *info,
-					dev_t device, const char *routine)
+		      dev_t device, const char *routine)
 {
 #ifdef SERIAL_PARANOIA_CHECK
 	static const char *badmagic =
@@ -165,7 +167,7 @@
  * Reading and writing Z8530 registers.
  */
 static inline unsigned char __pmac read_zsreg(struct mac_zschannel *channel,
-				       unsigned char reg)
+					      unsigned char reg)
 {
 	unsigned char retval;
 	unsigned long flags;
@@ -185,7 +187,7 @@
 }
 
 static inline void __pmac write_zsreg(struct mac_zschannel *channel,
-			       unsigned char reg, unsigned char value)
+				      unsigned char reg, unsigned char value)
 {
 	unsigned long flags;
 
@@ -357,8 +359,12 @@
 
 static void transmit_chars(struct mac_serial *info)
 {
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
 	if ((read_zsreg(info->zs_channel, 0) & Tx_BUF_EMP) == 0)
-		return;
+		goto out;
 	info->tx_active = 0;
 
 	if (info->x_char) {
@@ -366,12 +372,12 @@
 		write_zsdata(info->zs_channel, info->x_char);
 		info->x_char = 0;
 		info->tx_active = 1;
-		return;
+		goto out;
 	}
 
 	if ((info->xmit_cnt <= 0) || info->tty->stopped || info->tx_stopped) {
 		write_zsreg(info->zs_channel, 0, RES_Tx_P);
-		return;
+		goto out;
 	}
 
 	/* Send char */
@@ -382,6 +388,9 @@
 
 	if (info->xmit_cnt < WAKEUP_CHARS)
 		rs_sched_event(info, RS_EVENT_WRITE_WAKEUP);
+
+ out:
+	restore_flags(flags);
 }
 
 static _INLINE_ void status_handle(struct mac_serial *info)
@@ -764,8 +773,10 @@
 			 */
 			mdelay(250);
 		}
+#ifdef CONFIG_PMAC_PBOOK
 		if (info->is_pwbk_ir)
 			pmu_enable_irled(1);
+#endif /* CONFIG_PMAC_PBOOK */
 	} else {
 #ifdef SERIAL_DEBUG_POWER
 		printk(KERN_INFO "ttyS%02d: shutting down hardware\n", info->line);
@@ -795,8 +806,10 @@
 			feature_clear(info->dev_node, FEATURE_Modem_Reset);
 			mdelay(25);
 		}
+#ifdef CONFIG_PMAC_PBOOK
 		if (info->is_pwbk_ir)
 			pmu_enable_irled(0);
+#endif /* CONFIG_PMAC_PBOOK */
 			
 		if (info->zs_chan_a == info->zs_channel) {
 #ifdef SERIAL_DEBUG_POWER
@@ -987,7 +1000,6 @@
 static void rs_flush_chars(struct tty_struct *tty)
 {
 	struct mac_serial *info = (struct mac_serial *)tty->driver_data;
-	unsigned long flags;
 
 	if (serial_paranoia_check(info, tty->device, "rs_flush_chars"))
 		return;
@@ -997,53 +1009,76 @@
 		return;
 
 	/* Enable transmitter */
-	save_flags(flags); cli();
 	transmit_chars(info);
-	restore_flags(flags);
 }
 
 static int rs_write(struct tty_struct * tty, int from_user,
 		    const unsigned char *buf, int count)
 {
-	int	c, total = 0;
+	int	c, ret = 0;
 	struct mac_serial *info = (struct mac_serial *)tty->driver_data;
 	unsigned long flags;
 
 	if (serial_paranoia_check(info, tty->device, "rs_write"))
 		return 0;
 
-	if (!tty || !info->xmit_buf)
+	if (!tty || !info->xmit_buf || !tmp_buf)
 		return 0;
 
 	save_flags(flags);
-	while (1) {
-		cli();		
-		c = MIN(count, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
-				   SERIAL_XMIT_SIZE - info->xmit_head));
-		if (c <= 0)
-			break;
-
-		if (from_user) {
-			down(&tmp_buf_sem);
-			copy_from_user(tmp_buf, buf, c);
+	if (from_user) {
+		down(&tmp_buf_sem);
+		while (1) {
+			c = MIN(count,
+				MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
+				    SERIAL_XMIT_SIZE - info->xmit_head));
+			if (c <= 0)
+				break;
+
+			c -= copy_from_user(tmp_buf, buf, c);
+			if (!c) {
+				if (!ret)
+					ret = -EFAULT;
+				break;
+			}
+			cli();
 			c = MIN(c, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
 				       SERIAL_XMIT_SIZE - info->xmit_head));
 			memcpy(info->xmit_buf + info->xmit_head, tmp_buf, c);
-			up(&tmp_buf_sem);
-		} else
+			info->xmit_head = ((info->xmit_head + c) &
+					   (SERIAL_XMIT_SIZE-1));
+			info->xmit_cnt += c;
+			restore_flags(flags);
+			buf += c;
+			count -= c;
+			ret += c;
+		}
+		up(&tmp_buf_sem);
+	} else {
+		while (1) {
+			cli();		
+			c = MIN(count,
+				MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
+				    SERIAL_XMIT_SIZE - info->xmit_head));
+			if (c <= 0) {
+				restore_flags(flags);
+				break;
+			}
 			memcpy(info->xmit_buf + info->xmit_head, buf, c);
-		info->xmit_head = (info->xmit_head + c) & (SERIAL_XMIT_SIZE-1);
-		info->xmit_cnt += c;
-		restore_flags(flags);
-		buf += c;
-		count -= c;
-		total += c;
+			info->xmit_head = ((info->xmit_head + c) &
+					   (SERIAL_XMIT_SIZE-1));
+			info->xmit_cnt += c;
+			restore_flags(flags);
+			buf += c;
+			count -= c;
+			ret += c;
+		}
 	}
 	if (info->xmit_cnt && !tty->stopped && !info->tx_stopped
 	    && !info->tx_active)
 		transmit_chars(info);
 	restore_flags(flags);
-	return total;
+	return ret;
 }
 
 static int rs_write_room(struct tty_struct *tty)
@@ -1186,7 +1221,9 @@
 	tmp.close_delay = info->close_delay;
 	tmp.closing_wait = info->closing_wait;
 	tmp.custom_divisor = info->custom_divisor;
-	return copy_to_user(retinfo,&tmp,sizeof(*retinfo));
+	if (copy_to_user(retinfo,&tmp,sizeof(*retinfo)))
+		return -EFAULT;
+	return 0;
 }
 
 static int set_serial_info(struct mac_serial * info,
@@ -1196,9 +1233,8 @@
 	struct mac_serial old_info;
 	int 			retval = 0;
 
-	if (!new_info)
+	if (copy_from_user(&new_serial,new_info,sizeof(new_serial)))
 		return -EFAULT;
-	copy_from_user(&new_serial,new_info,sizeof(new_serial));
 	old_info = *info;
 
 	if (!capable(CAP_SYS_ADMIN)) {
@@ -1248,29 +1284,30 @@
 static int get_lsr_info(struct mac_serial * info, unsigned int *value)
 {
 	unsigned char status;
+	unsigned long flags;
 
-	cli();
+	save_flags(flags); cli();
 	status = read_zsreg(info->zs_channel, 0);
-	sti();
-	put_user(status,value);
-	return 0;
+	restore_flags(flags);
+	status = (status & Tx_BUF_EMP)? TIOCSER_TEMT: 0;
+	return put_user(status,value);
 }
 
 static int get_modem_info(struct mac_serial *info, unsigned int *value)
 {
 	unsigned char control, status;
 	unsigned int result;
+	unsigned long flags;
 
-	cli();
+	save_flags(flags); cli();
 	control = info->curregs[5];
 	status = read_zsreg(info->zs_channel, 0);
-	sti();
+	restore_flags(flags);
 	result =  ((control & RTS) ? TIOCM_RTS: 0)
 		| ((control & DTR) ? TIOCM_DTR: 0)
 		| ((status  & DCD) ? TIOCM_CAR: 0)
 		| ((status  & CTS) ? 0: TIOCM_CTS);
-	put_user(result,value);
-	return 0;
+	return put_user(result,value);
 }
 
 static int set_modem_info(struct mac_serial *info, unsigned int cmd,
@@ -1278,13 +1315,13 @@
 {
 	int error;
 	unsigned int arg, bits;
+	unsigned long flags;
 
-	error = verify_area(VERIFY_READ, value, sizeof(int));
+	error = get_user(arg, value);
 	if (error)
 		return error;
-	get_user(arg, value);
 	bits = (arg & TIOCM_RTS? RTS: 0) + (arg & TIOCM_DTR? DTR: 0);
-	cli();
+	save_flags(flags); cli();
 	switch (cmd) {
 	case TIOCMBIS:
 		info->curregs[5] |= bits;
@@ -1296,12 +1333,12 @@
 		info->curregs[5] = (info->curregs[5] & ~(DTR | RTS)) | bits;
 		break;
 	default:
-		sti();
+		restore_flags(flags);
 		return -EINVAL;
 	}
 	info->pendregs[5] = info->curregs[5];
 	write_zsreg(info->zs_channel, 5, info->curregs[5]);
-	sti();
+	restore_flags(flags);
 	return 0;
 }
 
@@ -1330,7 +1367,6 @@
 static int rs_ioctl(struct tty_struct *tty, struct file * file,
 		    unsigned int cmd, unsigned long arg)
 {
-	int error;
 	struct mac_serial * info = (struct mac_serial *)tty->driver_data;
 
 #ifdef CONFIG_KGDB
@@ -1341,48 +1377,31 @@
 		return -ENODEV;
 
 	if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
-	    (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGWILD)  &&
-	    (cmd != TIOCSERSWILD) && (cmd != TIOCSERGSTRUCT)) {
+	    (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGSTRUCT)) {
 		if (tty->flags & (1 << TTY_IO_ERROR))
 		    return -EIO;
 	}
 	
 	switch (cmd) {
 		case TIOCMGET:
-			error = verify_area(VERIFY_WRITE, (void *) arg,
-				sizeof(unsigned int));
-			if (error)
-				return error;
 			return get_modem_info(info, (unsigned int *) arg);
 		case TIOCMBIS:
 		case TIOCMBIC:
 		case TIOCMSET:
 			return set_modem_info(info, cmd, (unsigned int *) arg);
 		case TIOCGSERIAL:
-			error = verify_area(VERIFY_WRITE, (void *) arg,
-						sizeof(struct serial_struct));
-			if (error)
-				return error;
 			return get_serial_info(info,
 					       (struct serial_struct *) arg);
 		case TIOCSSERIAL:
 			return set_serial_info(info,
 					       (struct serial_struct *) arg);
 		case TIOCSERGETLSR: /* Get line status register */
-			error = verify_area(VERIFY_WRITE, (void *) arg,
-				sizeof(unsigned int));
-			if (error)
-				return error;
-			else
-			    return get_lsr_info(info, (unsigned int *) arg);
+			return get_lsr_info(info, (unsigned int *) arg);
 
 		case TIOCSERGSTRUCT:
-			error = verify_area(VERIFY_WRITE, (void *) arg,
-						sizeof(struct mac_serial));
-			if (error)
-				return error;
-			copy_from_user((struct mac_serial *) arg,
-				       info, sizeof(struct mac_serial));
+			if (copy_to_user((struct mac_serial *) arg,
+					 info, sizeof(struct mac_serial)))
+				return -EFAULT;
 			return 0;
 			
 		default:
@@ -1674,7 +1693,7 @@
 		    (tty->termios->c_cflag & CBAUD))
 			zs_rtsdtr(info, 1);
 		sti();
-		current->state = TASK_INTERRUPTIBLE;
+		set_current_state(TASK_INTERRUPTIBLE);
 		if (tty_hung_up_p(filp) ||
 		    !(info->flags & ZILOG_INITIALIZED)) {
 #ifdef SERIAL_DO_RESTART
@@ -1726,6 +1745,7 @@
 {
 	struct mac_serial	*info;
 	int 			retval, line;
+	unsigned long		page;
 
 	line = MINOR(tty->device) - tty->driver.minor_start;
 	if ((line < 0) || (line >= zs_channels_found))
@@ -1747,6 +1767,16 @@
 	tty->driver_data = info;
 	info->tty = tty;
 
+	if (!tmp_buf) {
+		page = get_free_page(GFP_KERNEL);
+		if (!page)
+			return -ENOMEM;
+		if (tmp_buf)
+			free_page(page);
+		else
+			tmp_buf = (unsigned char *) page;
+	}
+
 	/*
 	 * If the port is the middle of closing, bail out now
 	 */
@@ -1817,6 +1847,7 @@
 	struct device_node *dev, *ch;
 	struct mac_serial **pp;
 	int n, lenp;
+	char *conn;
 
 	n = 0;
 	pp = &zs_chain;
@@ -1842,9 +1873,11 @@
 			zs_soft[n].zs_channel->parent = &zs_soft[n];
 			zs_soft[n].is_cobalt_modem = device_is_compatible(ch, "cobalt");
 
-			/* XXX tested only with wallstreet PowerBook, should do no harm anyway */	
-			zs_soft[n].is_pwbk_ir = (strcmp(get_property(ch, "AAPL,connector",
-				&lenp), "infrared") == 0);
+			/* XXX tested only with wallstreet PowerBook,
+			   should do no harm anyway */
+			conn = get_property(ch, "AAPL,connector", &lenp);
+			zs_soft[n].is_pwbk_ir =
+				conn && (strcmp(conn, "infrared") == 0);
 
 			/* XXX this assumes the prom puts chan A before B */
 			if (n & 1)
@@ -2024,6 +2057,30 @@
 
 	return 0;
 }
+
+#ifdef MODULE
+int init_module(void)
+{
+	macserial_init();
+	return 0;
+}
+
+void cleanup_module(void)
+{
+	int i;
+	unsigned long flags;
+	struct mac_serial *info;
+
+	for (info = zs_chain, i = 0; info; info = info->zs_next, i++)
+		set_scc_power(info, 0);
+	save_flags(flags); cli();
+	for (i = 0; i < zs_channels_found; ++i)
+		free_irq(zs_soft[i].irq, &zs_soft[i]);
+	restore_flags(flags);
+	tty_unregister_driver(&callout_driver);
+	tty_unregister_driver(&serial_driver);
+}
+#endif /* MODULE */
 
 #if 0
 /*

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)