This commit was manufactured by cvs2svn to create tag
[linux-2.6.git] / drivers / char / esp.c
index 62f8e30..d67098c 100644 (file)
@@ -57,7 +57,6 @@
 #include <linux/ioport.h>
 #include <linux/mm.h>
 #include <linux/init.h>
-#include <linux/delay.h>
 
 #include <asm/system.h>
 #include <asm/io.h>
@@ -71,7 +70,6 @@
 
 #define NR_PORTS 64    /* maximum number of ports */
 #define NR_PRIMARY 8   /* maximum number of primary ports */
-#define REGION_SIZE 8   /* size of io region to request */
 
 /* The following variables can be set by giving module options */
 static int irq[NR_PRIMARY];    /* IRQ for each base port */
@@ -86,15 +84,15 @@ static unsigned int pio_threshold = ESP_PIO_THRESHOLD;
 
 MODULE_LICENSE("GPL");
 
-module_param_array(irq, int, NULL, 0);
-module_param_array(divisor, uint, NULL, 0);
-module_param(dma, uint, 0);
-module_param(rx_trigger, uint, 0);
-module_param(tx_trigger, uint, 0);
-module_param(flow_off, uint, 0);
-module_param(flow_on, uint, 0);
-module_param(rx_timeout, uint, 0);
-module_param(pio_threshold, uint, 0);
+MODULE_PARM(irq, "1-8i");
+MODULE_PARM(divisor, "1-8i");
+MODULE_PARM(dma, "i");
+MODULE_PARM(rx_trigger, "i");
+MODULE_PARM(tx_trigger, "i");
+MODULE_PARM(flow_off, "i");
+MODULE_PARM(flow_on, "i");
+MODULE_PARM(rx_timeout, "i");
+MODULE_PARM(pio_threshold, "i");
 
 /* END */
 
@@ -1265,7 +1263,7 @@ static void rs_flush_chars(struct tty_struct *tty)
        restore_flags(flags);
 }
 
-static int rs_write(struct tty_struct * tty,
+static int rs_write(struct tty_struct * tty, int from_user,
                    const unsigned char *buf, int count)
 {
        int     c, t, ret = 0;
@@ -1278,6 +1276,9 @@ static int rs_write(struct tty_struct * tty,
        if (!tty || !info->xmit_buf || !tmp_buf)
                return 0;
            
+       if (from_user)
+               down(&tmp_buf_sem);
+
        while (1) {
                /* Thanks to R. Wolff for suggesting how to do this with */
                /* interrupts enabled */
@@ -1296,7 +1297,18 @@ static int rs_write(struct tty_struct * tty,
                if (c <= 0)
                        break;
 
-               memcpy(info->xmit_buf + info->xmit_head, buf, c);
+               if (from_user) {
+                       c -= copy_from_user(tmp_buf, buf, c);
+
+                       if (!c) {
+                               if (!ret)
+                                       ret = -EFAULT;
+                               break;
+                       }
+
+                       memcpy(info->xmit_buf + info->xmit_head, tmp_buf, c);
+               } else
+                       memcpy(info->xmit_buf + info->xmit_head, buf, c);
 
                info->xmit_head = (info->xmit_head + c) & (ESP_XMIT_SIZE-1);
                info->xmit_cnt += c;
@@ -1305,6 +1317,9 @@ static int rs_write(struct tty_struct * tty,
                ret += c;
        }
 
+       if (from_user)
+               up(&tmp_buf_sem);
+       
        save_flags(flags); cli();
 
        if (info->xmit_cnt && !tty->stopped && !(info->IER & UART_IER_THRI)) {
@@ -2051,7 +2066,8 @@ static void rs_close(struct tty_struct *tty, struct file * filp)
 
        if (info->blocked_open) {
                if (info->close_delay) {
-                       msleep_interruptible(jiffies_to_msecs(info->close_delay));
+                       set_current_state(TASK_INTERRUPTIBLE);
+                       schedule_timeout(info->close_delay);
                }
                wake_up_interruptible(&info->open_wait);
        }
@@ -2082,7 +2098,8 @@ static void rs_wait_until_sent(struct tty_struct *tty, int timeout)
 
        while ((serial_in(info, UART_ESI_STAT1) != 0x03) ||
                (serial_in(info, UART_ESI_STAT2) != 0xff)) {
-               msleep_interruptible(jiffies_to_msecs(char_time));
+               set_current_state(TASK_INTERRUPTIBLE);
+               schedule_timeout(char_time);
 
                if (signal_pending(current))
                        break;
@@ -2327,21 +2344,19 @@ static _INLINE_ void show_serial_version(void)
  * This routine is called by espserial_init() to initialize a specific serial
  * port.
  */
-static _INLINE_ int autoconfig(struct esp_struct * info)
+static _INLINE_ int autoconfig(struct esp_struct * info, int *region_start)
 {
        int port_detected = 0;
        unsigned long flags;
 
-       if (!request_region(info->port, REGION_SIZE, "esp serial"))
-               return -EIO;
-
        save_flags(flags); cli();
        
        /*
         * Check for ESP card
         */
 
-       if (serial_in(info, UART_ESI_BASE) == 0xf3) {
+       if (!check_region(info->port, 8) && 
+           serial_in(info, UART_ESI_BASE) == 0xf3) {
                serial_out(info, UART_ESI_CMD1, 0x00);
                serial_out(info, UART_ESI_CMD1, 0x01);
 
@@ -2357,6 +2372,19 @@ static _INLINE_ int autoconfig(struct esp_struct * info)
                                        info->irq = 4;
                        }
 
+                       if (ports && (ports->port == (info->port - 8))) {
+                               release_region(*region_start,
+                                              info->port - *region_start);
+                       } else
+                               *region_start = info->port;
+
+                       if (!request_region(*region_start,
+                                      info->port - *region_start + 8,
+                                      "esp serial"))
+                       {
+                               restore_flags(flags);
+                               return -EIO;
+                       }
 
                        /* put card in enhanced mode */
                        /* this prevents access through */
@@ -2369,8 +2397,6 @@ static _INLINE_ int autoconfig(struct esp_struct * info)
                        serial_out(info, UART_ESI_CMD2, 0x00);
                }
        }
-       if (!port_detected)
-               release_region(info->port, REGION_SIZE);
 
        restore_flags(flags);
        return (port_detected);
@@ -2404,6 +2430,7 @@ static struct tty_operations esp_ops = {
 int __init espserial_init(void)
 {
        int i, offset;
+       int region_start;
        struct esp_struct * info;
        struct esp_struct *last_primary = NULL;
        int esp[] = {0x100,0x140,0x180,0x200,0x240,0x280,0x300,0x380};
@@ -2489,7 +2516,7 @@ int __init espserial_init(void)
                info->irq = irq[i];
                info->line = (i * 8) + (offset / 8);
 
-               if (!autoconfig(info)) {
+               if (!autoconfig(info, &region_start)) {
                        i++;
                        offset = 0;
                        continue;
@@ -2565,6 +2592,7 @@ static void __exit espserial_exit(void)
 {
        unsigned long flags;
        int e1;
+       unsigned int region_start, region_end;
        struct esp_struct *temp_async;
        struct esp_pio_buffer *pio_buf;
 
@@ -2579,8 +2607,27 @@ static void __exit espserial_exit(void)
 
        while (ports) {
                if (ports->port) {
-                       release_region(ports->port, REGION_SIZE);
+                       region_start = region_end = ports->port;
+                       temp_async = ports;
+
+                       while (temp_async) {
+                               if ((region_start - temp_async->port) == 8) {
+                                       region_start = temp_async->port;
+                                       temp_async->port = 0;
+                                       temp_async = ports;
+                               } else if ((temp_async->port - region_end)
+                                          == 8) {
+                                       region_end = temp_async->port;
+                                       temp_async->port = 0;
+                                       temp_async = ports;
+                               } else
+                                       temp_async = temp_async->next_port;
+                       }
+                       
+                       release_region(region_start,
+                                      region_end - region_start + 8);
                }
+
                temp_async = ports->next_port;
                kfree(ports);
                ports = temp_async;