viocons.c
来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 1,334 行 · 第 1/3 页
C
1,334 行
/* -*- linux-c -*- * * drivers/char/viocons.c * * iSeries Virtual Terminal * * Authors: Dave Boutcher <boutcher@us.ibm.com> * Ryan Arnold <ryanarn@us.ibm.com> * Colin Devilbiss <devilbis@us.ibm.com> * Stephen Rothwell <sfr@au1.ibm.com> * * (C) Copyright 2000, 2001, 2002, 2003, 2004 IBM Corporation * * 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 of the * License, or (at your option) anyu later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; 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 */#include <linux/config.h>#include <linux/version.h>#include <linux/kernel.h>#include <linux/proc_fs.h>#include <linux/errno.h>#include <linux/vmalloc.h>#include <linux/mm.h>#include <linux/console.h>#include <linux/module.h>#include <asm/uaccess.h>#include <linux/init.h>#include <linux/wait.h>#include <linux/spinlock.h>#include <asm/ioctls.h>#include <linux/kd.h>#include <linux/tty.h>#include <linux/tty_flip.h>#include <linux/sysrq.h>#include <asm/iSeries/vio.h>#include <asm/iSeries/HvLpEvent.h>#include <asm/iSeries/HvCallEvent.h>#include <asm/iSeries/HvLpConfig.h>#include <asm/iSeries/HvCall.h>#ifdef CONFIG_VT#error You must turn off CONFIG_VT to use CONFIG_VIOCONS#endif#define VIOTTY_MAGIC (0x0DCB)#define VTTY_PORTS 10#define VIOCONS_KERN_WARN KERN_WARNING "viocons: "#define VIOCONS_KERN_INFO KERN_INFO "viocons: "static spinlock_t consolelock = SPIN_LOCK_UNLOCKED;static spinlock_t consoleloglock = SPIN_LOCK_UNLOCKED;#ifdef CONFIG_MAGIC_SYSRQstatic int vio_sysrq_pressed;extern int sysrq_enabled;#endif/* * The structure of the events that flow between us and OS/400. You can't * mess with this unless the OS/400 side changes too */struct viocharlpevent { struct HvLpEvent event; u32 reserved; u16 version; u16 subtype_result_code; u8 virtual_device; u8 len; u8 data[VIOCHAR_MAX_DATA];};/* * This is a place where we handle the distribution of memory * for copy_from_user() calls. The buffer_available array is to * help us determine which buffer to use. */#define VIOCHAR_NUM_CFU_BUFFERS 7static struct viocharlpevent viocons_cfu_buffer[VIOCHAR_NUM_CFU_BUFFERS];static atomic_t viocons_cfu_buffer_available[VIOCHAR_NUM_CFU_BUFFERS];#define VIOCHAR_WINDOW 10#define VIOCHAR_HIGHWATERMARK 3enum viocharsubtype { viocharopen = 0x0001, viocharclose = 0x0002, viochardata = 0x0003, viocharack = 0x0004, viocharconfig = 0x0005};enum viochar_rc { viochar_rc_ebusy = 1};#define VIOCHAR_NUM_BUF 16/* * Our port information. We store a pointer to one entry in the * tty_driver_data */static struct port_info { int magic; struct tty_struct *tty; HvLpIndex lp; u8 vcons; u64 seq; /* sequence number of last HV send */ u64 ack; /* last ack from HV *//* * When we get writes faster than we can send it to the partition, * buffer the data here. Note that used is a bit map of used buffers. * It had better have enough bits to hold VIOCHAR_NUM_BUF the bitops assume * it is a multiple of unsigned long */ unsigned long used; u8 *buffer[VIOCHAR_NUM_BUF]; int bufferBytes[VIOCHAR_NUM_BUF]; int curbuf; int bufferOverflow; int overflowMessage;} port_info[VTTY_PORTS];#define viochar_is_console(pi) ((pi) == &port_info[0])#define viochar_port(pi) ((pi) - &port_info[0])static void initDataEvent(struct viocharlpevent *viochar, HvLpIndex lp);static struct tty_driver *viotty_driver;void hvlog(char *fmt, ...){ int i; unsigned long flags; va_list args; static char buf[256]; spin_lock_irqsave(&consoleloglock, flags); va_start(args, fmt); i = vscnprintf(buf, sizeof(buf) - 1, fmt, args); va_end(args); buf[i++] = '\r'; HvCall_writeLogBuffer(buf, i); spin_unlock_irqrestore(&consoleloglock, flags);}void hvlogOutput(const char *buf, int count){ unsigned long flags; int begin; int index; static const char cr = '\r'; begin = 0; spin_lock_irqsave(&consoleloglock, flags); for (index = 0; index < count; index++) { if (buf[index] == '\n') { /* * Start right after the last '\n' or at the zeroth * array position and output the number of characters * including the newline. */ HvCall_writeLogBuffer(&buf[begin], index - begin + 1); begin = index + 1; HvCall_writeLogBuffer(&cr, 1); } } if ((index - begin) > 0) HvCall_writeLogBuffer(&buf[begin], index - begin); spin_unlock_irqrestore(&consoleloglock, flags);}/* * Make sure we're pointing to a valid port_info structure. Shamelessly * plagerized from serial.c */static inline int viotty_paranoia_check(struct port_info *pi, char *name, const char *routine){ static const char *bad_pi_addr = VIOCONS_KERN_WARN "warning: bad address for port_info struct (%s) in %s\n"; static const char *badmagic = VIOCONS_KERN_WARN "warning: bad magic number for port_info struct (%s) in %s\n"; if ((pi < &port_info[0]) || (viochar_port(pi) > VTTY_PORTS)) { printk(bad_pi_addr, name, routine); return 1; } if (pi->magic != VIOTTY_MAGIC) { printk(badmagic, name, routine); return 1; } return 0;}/* * This function should ONLY be called once from viocons_init2 */static void viocons_init_cfu_buffer(void){ int i; for (i = 1; i < VIOCHAR_NUM_CFU_BUFFERS; i++) atomic_set(&viocons_cfu_buffer_available[i], 1);}static struct viocharlpevent *viocons_get_cfu_buffer(void){ int i; /* * Grab the first available buffer. It doesn't matter if we * are interrupted during this array traversal as long as we * get an available space. */ for (i = 0; i < VIOCHAR_NUM_CFU_BUFFERS; i++) if (atomic_dec_if_positive(&viocons_cfu_buffer_available[i]) == 0 ) return &viocons_cfu_buffer[i]; hvlog("\n\rviocons: viocons_get_cfu_buffer : no free buffers found"); return NULL;}static void viocons_free_cfu_buffer(struct viocharlpevent *buffer){ int i; i = buffer - &viocons_cfu_buffer[0]; if (i >= (sizeof(viocons_cfu_buffer) / sizeof(viocons_cfu_buffer[0]))) { hvlog("\n\rviocons: viocons_free_cfu_buffer : buffer pointer not found in list."); return; } if (atomic_read(&viocons_cfu_buffer_available[i]) != 0) { hvlog("\n\rviocons: WARNING : returning unallocated cfu buffer."); return; } atomic_set(&viocons_cfu_buffer_available[i], 1);}/* * Add data to our pending-send buffers. * * NOTE: Don't use printk in here because it gets nastily recursive. * hvlog can be used to log to the hypervisor buffer */static int buffer_add(struct port_info *pi, const char *buf, size_t len){ size_t bleft; size_t curlen; const char *curbuf; int nextbuf; curbuf = buf; bleft = len; while (bleft > 0) { /* * If there is no space left in the current buffer, we have * filled everything up, so return. If we filled the previous * buffer we would already have moved to the next one. */ if (pi->bufferBytes[pi->curbuf] == VIOCHAR_MAX_DATA) { hvlog ("\n\rviocons: No overflow buffer available for memcpy().\n"); pi->bufferOverflow++; pi->overflowMessage = 1; break; } /* * Turn on the "used" bit for this buffer. If it's already on, * that's fine. */ set_bit(pi->curbuf, &pi->used); /* * See if this buffer has been allocated. If not, allocate it. */ if (pi->buffer[pi->curbuf] == NULL) { pi->buffer[pi->curbuf] = kmalloc(VIOCHAR_MAX_DATA, GFP_ATOMIC); if (pi->buffer[pi->curbuf] == NULL) { hvlog("\n\rviocons: kmalloc failed allocating spaces for buffer %d.", pi->curbuf); break; } } /* Figure out how much we can copy into this buffer. */ if (bleft < (VIOCHAR_MAX_DATA - pi->bufferBytes[pi->curbuf])) curlen = bleft; else curlen = VIOCHAR_MAX_DATA - pi->bufferBytes[pi->curbuf]; /* Copy the data into the buffer. */ memcpy(pi->buffer[pi->curbuf] + pi->bufferBytes[pi->curbuf], curbuf, curlen); pi->bufferBytes[pi->curbuf] += curlen; curbuf += curlen; bleft -= curlen; /* * Now see if we've filled this buffer. If not then * we'll try to use it again later. If we've filled it * up then we'll advance the curbuf to the next in the * circular queue. */ if (pi->bufferBytes[pi->curbuf] == VIOCHAR_MAX_DATA) { nextbuf = (pi->curbuf + 1) % VIOCHAR_NUM_BUF; /* * Move to the next buffer if it hasn't been used yet */ if (test_bit(nextbuf, &pi->used) == 0) pi->curbuf = nextbuf; } } return len - bleft;}/* * Send pending data * * NOTE: Don't use printk in here because it gets nastily recursive. * hvlog can be used to log to the hypervisor buffer */static void send_buffers(struct port_info *pi){ HvLpEvent_Rc hvrc; int nextbuf; struct viocharlpevent *viochar; unsigned long flags; spin_lock_irqsave(&consolelock, flags); viochar = (struct viocharlpevent *) vio_get_event_buffer(viomajorsubtype_chario); /* Make sure we got a buffer */ if (viochar == NULL) { hvlog("\n\rviocons: Can't get viochar buffer in sendBuffers()."); spin_unlock_irqrestore(&consolelock, flags); return; } if (pi->used == 0) { hvlog("\n\rviocons: in sendbuffers(), but no buffers used.\n"); vio_free_event_buffer(viomajorsubtype_chario, viochar); spin_unlock_irqrestore(&consolelock, flags); return; } /* * curbuf points to the buffer we're filling. We want to * start sending AFTER this one. */ nextbuf = (pi->curbuf + 1) % VIOCHAR_NUM_BUF; /* * Loop until we find a buffer with the used bit on */ while (test_bit(nextbuf, &pi->used) == 0) nextbuf = (nextbuf + 1) % VIOCHAR_NUM_BUF; initDataEvent(viochar, pi->lp); /* * While we have buffers with data, and our send window * is open, send them */ while ((test_bit(nextbuf, &pi->used)) && ((pi->seq - pi->ack) < VIOCHAR_WINDOW)) { viochar->len = pi->bufferBytes[nextbuf]; viochar->event.xCorrelationToken = pi->seq++; viochar->event.xSizeMinus1 = offsetof(struct viocharlpevent, data) + viochar->len; memcpy(viochar->data, pi->buffer[nextbuf], viochar->len); hvrc = HvCallEvent_signalLpEvent(&viochar->event); if (hvrc) { /* * MUST unlock the spinlock before doing a printk */ vio_free_event_buffer(viomajorsubtype_chario, viochar); spin_unlock_irqrestore(&consolelock, flags); printk(VIOCONS_KERN_WARN "error sending event! return code %d\n", (int)hvrc); return; } /* * clear the used bit, zero the number of bytes in * this buffer, and move to the next buffer */ clear_bit(nextbuf, &pi->used); pi->bufferBytes[nextbuf] = 0; nextbuf = (nextbuf + 1) % VIOCHAR_NUM_BUF; } /* * If we have emptied all the buffers, start at 0 again. * this will re-use any allocated buffers */ if (pi->used == 0) { pi->curbuf = 0; if (pi->overflowMessage) pi->overflowMessage = 0; if (pi->tty) { tty_wakeup(pi->tty); } } vio_free_event_buffer(viomajorsubtype_chario, viochar); spin_unlock_irqrestore(&consolelock, flags);}/* * Our internal writer. Gets called both from the console device and * the tty device. the tty pointer will be NULL if called from the console. * Return total number of bytes "written". * * NOTE: Don't use printk in here because it gets nastily recursive. hvlog * can be used to log to the hypervisor buffer */static int internal_write(struct port_info *pi, const char *buf, size_t len, struct viocharlpevent *viochar){ HvLpEvent_Rc hvrc; size_t bleft;
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?