X-Git-Url: http://git.monstr.eu/?a=blobdiff_plain;f=drivers%2Ftty%2Fvt%2Fvc_screen.c;h=ad015cd4e82f0db8b2c8e238fb2e136104c4e45d;hb=5a52baaab029e38e919efff2abc0d4e89338d464;hp=778f83ea2249353398d9f788494e05db521ff29f;hpb=5e237e8c77279a0873a5e9806a5459ebc840c9ce;p=linux-2.6-microblaze.git diff --git a/drivers/tty/vt/vc_screen.c b/drivers/tty/vt/vc_screen.c index 778f83ea2249..ad015cd4e82f 100644 --- a/drivers/tty/vt/vc_screen.c +++ b/drivers/tty/vt/vc_screen.c @@ -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;