vc_screen: extract vcs_read_buf_noattr
[linux-2.6-microblaze.git] / drivers / tty / vt / vc_screen.c
index 778f83e..ad015cd 100644 (file)
@@ -53,7 +53,7 @@
 #undef attr
 #undef org
 #undef addr
-#define HEADER_SIZE    4
+#define HEADER_SIZE    4u
 
 #define CON_BUF_SIZE (CONFIG_BASE_SMALL ? 256 : PAGE_SIZE)
 
@@ -177,12 +177,14 @@ vcs_poll_data_get(struct file *file)
        return poll;
 }
 
-/*
- * Returns VC for inode.
+/**
+ * vcs_vc -- return VC for @inode
+ * @inode: inode for which to return a VC
+ * @viewed: returns whether this console is currently foreground (viewed)
+ *
  * Must be called with console_lock.
  */
-static struct vc_data*
-vcs_vc(struct inode *inode, int *viewed)
+static struct vc_data *vcs_vc(struct inode *inode, bool *viewed)
 {
        unsigned int currcons = console(inode);
 
@@ -191,54 +193,109 @@ vcs_vc(struct inode *inode, int *viewed)
        if (currcons == 0) {
                currcons = fg_console;
                if (viewed)
-                       *viewed = 1;
+                       *viewed = true;
        } else {
                currcons--;
                if (viewed)
-                       *viewed = 0;
+                       *viewed = false;
        }
        return vc_cons[currcons].d;
 }
 
-/*
- * Returns size for VC carried by inode.
+/**
+ * vcs_size -- return size for a VC in @vc
+ * @vc: which VC
+ * @attr: does it use attributes?
+ * @unicode: is it unicode?
+ *
  * Must be called with console_lock.
  */
-static int
-vcs_size(struct inode *inode)
+static int vcs_size(const struct vc_data *vc, bool attr, bool unicode)
 {
        int size;
-       struct vc_data *vc;
 
        WARN_CONSOLE_UNLOCKED();
 
-       vc = vcs_vc(inode, NULL);
-       if (!vc)
-               return -ENXIO;
-
        size = vc->vc_rows * vc->vc_cols;
 
-       if (use_attributes(inode)) {
-               if (use_unicode(inode))
+       if (attr) {
+               if (unicode)
                        return -EOPNOTSUPP;
-               size = 2*size + HEADER_SIZE;
-       } else if (use_unicode(inode))
+
+               size = 2 * size + HEADER_SIZE;
+       } else if (unicode)
                size *= 4;
+
        return size;
 }
 
 static loff_t vcs_lseek(struct file *file, loff_t offset, int orig)
 {
+       struct inode *inode = file_inode(file);
+       struct vc_data *vc;
        int size;
 
        console_lock();
-       size = vcs_size(file_inode(file));
+       vc = vcs_vc(inode, NULL);
+       if (!vc) {
+               console_unlock();
+               return -ENXIO;
+       }
+
+       size = vcs_size(vc, use_attributes(inode), use_unicode(inode));
        console_unlock();
        if (size < 0)
                return size;
        return fixed_size_llseek(file, offset, orig, size);
 }
 
+static int vcs_read_buf_uni(struct vc_data *vc, char *con_buf,
+               unsigned int pos, unsigned int count, bool viewed)
+{
+       unsigned int nr, row, col, maxcol = vc->vc_cols;
+       int ret;
+
+       ret = vc_uniscr_check(vc);
+       if (ret)
+               return ret;
+
+       pos /= 4;
+       row = pos / maxcol;
+       col = pos % maxcol;
+       nr = maxcol - col;
+       do {
+               if (nr > count / 4)
+                       nr = count / 4;
+               vc_uniscr_copy_line(vc, con_buf, viewed, row, col, nr);
+               con_buf += nr * 4;
+               count -= nr * 4;
+               row++;
+               col = 0;
+               nr = maxcol;
+       } while (count);
+
+       return 0;
+}
+
+static void vcs_read_buf_noattr(const struct vc_data *vc, char *con_buf,
+               unsigned int pos, unsigned int count, bool viewed)
+{
+       u16 *org;
+       unsigned int col, maxcol = vc->vc_cols;
+
+       org = screen_pos(vc, pos, viewed);
+       col = pos % maxcol;
+       pos += maxcol - col;
+
+       while (count-- > 0) {
+               *con_buf++ = (vcs_scr_readw(vc, org++) & 0xff);
+               if (++col == maxcol) {
+                       org = screen_pos(vc, pos, viewed);
+                       col = 0;
+                       pos += maxcol;
+               }
+       }
+}
 
 static ssize_t
 vcs_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
@@ -246,11 +303,12 @@ vcs_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
        struct inode *inode = file_inode(file);
        struct vc_data *vc;
        struct vcs_poll_data *poll;
-       long pos, read;
-       int attr, uni_mode, row, col, maxcol, viewed;
-       unsigned short *org = NULL;
+       u16 *org;
+       unsigned int read, col, maxcol;
        ssize_t ret;
        char *con_buf;
+       loff_t pos;
+       bool viewed, attr, uni_mode;
 
        con_buf = (char *) __get_free_page(GFP_KERNEL);
        if (!con_buf)
@@ -284,15 +342,14 @@ vcs_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
        ret = 0;
        while (count) {
                char *con_buf0, *con_buf_start;
-               long this_round, size;
-               ssize_t orig_count;
-               long p = pos;
+               unsigned int this_round, orig_count, p = pos;
+               int size;
 
                /* Check whether we are above size each round,
                 * as copy_to_user at the end of this loop
                 * could sleep.
                 */
-               size = vcs_size(inode);
+               size = vcs_size(vc, attr, uni_mode);
                if (size < 0) {
                        if (read)
                                break;
@@ -317,42 +374,15 @@ vcs_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
                orig_count = this_round;
                maxcol = vc->vc_cols;
                if (uni_mode) {
-                       unsigned int nr;
-
-                       ret = vc_uniscr_check(vc);
+                       ret = vcs_read_buf_uni(vc, con_buf, pos, this_round,
+                                       viewed);
                        if (ret)
                                break;
-                       p /= 4;
-                       row = p / vc->vc_cols;
-                       col = p % maxcol;
-                       nr = maxcol - col;
-                       do {
-                               if (nr > this_round/4)
-                                       nr = this_round/4;
-                               vc_uniscr_copy_line(vc, con_buf0, viewed,
-                                                   row, col, nr);
-                               con_buf0 += nr * 4;
-                               this_round -= nr * 4;
-                               row++;
-                               col = 0;
-                               nr = maxcol;
-                       } while (this_round);
                } else if (!attr) {
-                       org = screen_pos(vc, p, viewed);
-                       col = p % maxcol;
-                       p += maxcol - col;
-                       while (this_round-- > 0) {
-                               *con_buf0++ = (vcs_scr_readw(vc, org++) & 0xff);
-                               if (++col == maxcol) {
-                                       org = screen_pos(vc, p, viewed);
-                                       col = 0;
-                                       p += maxcol;
-                               }
-                       }
+                       vcs_read_buf_noattr(vc, con_buf, pos, this_round,
+                                       viewed);
                } else {
                        if (p < HEADER_SIZE) {
-                               size_t tmp_count;
-
                                /* clamp header values if they don't fit */
                                con_buf0[0] = min(vc->vc_rows, 0xFFu);
                                con_buf0[1] = min(vc->vc_cols, 0xFFu);
@@ -365,12 +395,8 @@ vcs_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
                                        orig_count = this_round - p;
                                }
 
-                               tmp_count = HEADER_SIZE;
-                               if (tmp_count > this_round)
-                                       tmp_count = this_round;
-
                                /* Advance state pointers and move on. */
-                               this_round -= tmp_count;
+                               this_round -= min(HEADER_SIZE, this_round);
                                p = HEADER_SIZE;
                                con_buf0 = con_buf + HEADER_SIZE;
                                /* If this_round >= 0, then p is even... */
@@ -443,18 +469,129 @@ unlock_out:
        return ret;
 }
 
+static u16 *vcs_write_buf_noattr(struct vc_data *vc, const char *con_buf,
+               unsigned int pos, unsigned int count, bool viewed, u16 **org0)
+{
+       u16 *org;
+       unsigned int col, maxcol = vc->vc_cols;
+
+       *org0 = org = screen_pos(vc, pos, viewed);
+       col = pos % maxcol;
+       pos += maxcol - col;
+
+       while (count > 0) {
+               unsigned char c = *con_buf++;
+
+               count--;
+               vcs_scr_writew(vc,
+                              (vcs_scr_readw(vc, org) & 0xff00) | c, org);
+               org++;
+               if (++col == maxcol) {
+                       org = screen_pos(vc, pos, viewed);
+                       col = 0;
+                       pos += maxcol;
+               }
+       }
+
+       return org;
+}
+
+/*
+ * Compilers (gcc 10) are unable to optimize the swap in cpu_to_le16. So do it
+ * the poor man way.
+ */
+static inline u16 vc_compile_le16(u8 hi, u8 lo)
+{
+#ifdef __BIG_ENDIAN
+       return (lo << 8u) | hi;
+#else
+       return (hi << 8u) | lo;
+#endif
+}
+
+static u16 *vcs_write_buf(struct vc_data *vc, const char *con_buf,
+               unsigned int pos, unsigned int count, bool viewed, u16 **org0)
+{
+       u16 *org;
+       unsigned int col, maxcol = vc->vc_cols;
+       unsigned char c;
+
+       /* header */
+       if (pos < HEADER_SIZE) {
+               char header[HEADER_SIZE];
+
+               getconsxy(vc, header + 2);
+               while (pos < HEADER_SIZE && count > 0) {
+                       count--;
+                       header[pos++] = *con_buf++;
+               }
+               if (!viewed)
+                       putconsxy(vc, header + 2);
+       }
+
+       if (!count)
+               return NULL;
+
+       pos -= HEADER_SIZE;
+       col = (pos/2) % maxcol;
+
+       *org0 = org = screen_pos(vc, pos/2, viewed);
+
+       /* odd pos -- the first single character */
+       if (pos & 1) {
+               count--;
+               c = *con_buf++;
+               vcs_scr_writew(vc, vc_compile_le16(c, vcs_scr_readw(vc, org)),
+                               org);
+               org++;
+               pos++;
+               if (++col == maxcol) {
+                       org = screen_pos(vc, pos/2, viewed);
+                       col = 0;
+               }
+       }
+
+       pos /= 2;
+       pos += maxcol - col;
+
+       /* even pos -- handle attr+character pairs */
+       while (count > 1) {
+               unsigned short w;
+
+               w = get_unaligned(((unsigned short *)con_buf));
+               vcs_scr_writew(vc, w, org++);
+               con_buf += 2;
+               count -= 2;
+               if (++col == maxcol) {
+                       org = screen_pos(vc, pos, viewed);
+                       col = 0;
+                       pos += maxcol;
+               }
+       }
+
+       if (!count)
+               return org;
+
+       /* odd pos -- the remaining character */
+       c = *con_buf++;
+       vcs_scr_writew(vc, vc_compile_le16(vcs_scr_readw(vc, org) >> 8, c),
+                               org);
+
+       return org;
+}
+
 static ssize_t
 vcs_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
 {
        struct inode *inode = file_inode(file);
        struct vc_data *vc;
-       long pos;
-       long attr, size, written;
-       char *con_buf0;
-       int col, maxcol, viewed;
-       u16 *org0 = NULL, *org = NULL;
-       size_t ret;
        char *con_buf;
+       u16 *org0, *org;
+       unsigned int written;
+       int size;
+       ssize_t ret;
+       loff_t pos;
+       bool viewed, attr;
 
        if (use_unicode(inode))
                return -EOPNOTSUPP;
@@ -476,7 +613,11 @@ vcs_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
        if (!vc)
                goto unlock_out;
 
-       size = vcs_size(inode);
+       size = vcs_size(vc, attr, false);
+       if (size < 0) {
+               ret = size;
+               goto unlock_out;
+       }
        ret = -EINVAL;
        if (pos < 0 || pos > size)
                goto unlock_out;
@@ -484,9 +625,7 @@ vcs_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
                count = size - pos;
        written = 0;
        while (count) {
-               long this_round = count;
-               size_t orig_count;
-               long p;
+               unsigned int this_round = count;
 
                if (this_round > CON_BUF_SIZE)
                        this_round = CON_BUF_SIZE;
@@ -515,7 +654,7 @@ vcs_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
                 * the user buffer, so recheck.
                 * Return data written up to now on failure.
                 */
-               size = vcs_size(inode);
+               size = vcs_size(vc, attr, false);
                if (size < 0) {
                        if (written)
                                break;
@@ -531,95 +670,18 @@ vcs_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
                 * under the lock using the local kernel buffer.
                 */
 
-               con_buf0 = con_buf;
-               orig_count = this_round;
-               maxcol = vc->vc_cols;
-               p = pos;
-               if (!attr) {
-                       org0 = org = screen_pos(vc, p, viewed);
-                       col = p % maxcol;
-                       p += maxcol - col;
-
-                       while (this_round > 0) {
-                               unsigned char c = *con_buf0++;
-
-                               this_round--;
-                               vcs_scr_writew(vc,
-                                              (vcs_scr_readw(vc, org) & 0xff00) | c, org);
-                               org++;
-                               if (++col == maxcol) {
-                                       org = screen_pos(vc, p, viewed);
-                                       col = 0;
-                                       p += maxcol;
-                               }
-                       }
-               } else {
-                       if (p < HEADER_SIZE) {
-                               char header[HEADER_SIZE];
-
-                               getconsxy(vc, header + 2);
-                               while (p < HEADER_SIZE && this_round > 0) {
-                                       this_round--;
-                                       header[p++] = *con_buf0++;
-                               }
-                               if (!viewed)
-                                       putconsxy(vc, header + 2);
-                       }
-                       p -= HEADER_SIZE;
-                       col = (p/2) % maxcol;
-                       if (this_round > 0) {
-                               org0 = org = screen_pos(vc, p/2, viewed);
-                               if ((p & 1) && this_round > 0) {
-                                       char c;
-
-                                       this_round--;
-                                       c = *con_buf0++;
-#ifdef __BIG_ENDIAN
-                                       vcs_scr_writew(vc, c |
-                                            (vcs_scr_readw(vc, org) & 0xff00), org);
-#else
-                                       vcs_scr_writew(vc, (c << 8) |
-                                            (vcs_scr_readw(vc, org) & 0xff), org);
-#endif
-                                       org++;
-                                       p++;
-                                       if (++col == maxcol) {
-                                               org = screen_pos(vc, p/2, viewed);
-                                               col = 0;
-                                       }
-                               }
-                               p /= 2;
-                               p += maxcol - col;
-                       }
-                       while (this_round > 1) {
-                               unsigned short w;
-
-                               w = get_unaligned(((unsigned short *)con_buf0));
-                               vcs_scr_writew(vc, w, org++);
-                               con_buf0 += 2;
-                               this_round -= 2;
-                               if (++col == maxcol) {
-                                       org = screen_pos(vc, p, viewed);
-                                       col = 0;
-                                       p += maxcol;
-                               }
-                       }
-                       if (this_round > 0) {
-                               unsigned char c;
-
-                               c = *con_buf0++;
-#ifdef __BIG_ENDIAN
-                               vcs_scr_writew(vc, (vcs_scr_readw(vc, org) & 0xff) | (c << 8), org);
-#else
-                               vcs_scr_writew(vc, (vcs_scr_readw(vc, org) & 0xff00) | c, org);
-#endif
-                       }
-               }
-               count -= orig_count;
-               written += orig_count;
-               buf += orig_count;
-               pos += orig_count;
-               if (org0)
+               if (attr)
+                       org = vcs_write_buf(vc, con_buf, pos, this_round,
+                                       viewed, &org0);
+               else
+                       org = vcs_write_buf_noattr(vc, con_buf, pos, this_round,
+                                       viewed, &org0);
+
+               count -= this_round;
+               written += this_round;
+               buf += this_round;
+               pos += this_round;
+               if (org)
                        update_region(vc, (unsigned long)(org0), org - org0);
        }
        *ppos += written;