viocons.c
来自「linux 内核源代码」· C语言 代码 · 共 1,168 行 · 第 1/2 页
C
1,168 行
pi = (struct port_info *)tty->driver_data; if (!pi || viotty_paranoia_check(pi, tty->name, "viotty_close")) { spin_unlock_irqrestore(&consolelock, flags); return; } if (tty->count == 1) pi->tty = NULL; spin_unlock_irqrestore(&consolelock, flags);}/* * TTY Write method */static int viotty_write(struct tty_struct *tty, const unsigned char *buf, int count){ struct port_info *pi; pi = get_port_data(tty); if (pi == NULL) { hvlog("\n\rviotty_write: no port data."); return -ENODEV; } if (viochar_is_console(pi)) hvlogOutput(buf, count); /* * If the path to this LP is closed, don't bother doing anything more. * just dump the data on the floor and return count. For some reason * some user level programs will attempt to probe available tty's and * they'll attempt a viotty_write on an invalid port which maps to an * invalid target lp. If this is the case then ignore the * viotty_write call and, since the viopath isn't active to this * partition, return count. */ if (!viopath_isactive(pi->lp)) return count; return internal_write(pi, buf, count);}/* * TTY put_char method */static void viotty_put_char(struct tty_struct *tty, unsigned char ch){ struct port_info *pi; pi = get_port_data(tty); if (pi == NULL) return; /* This will append '\r' as well if the char is '\n' */ if (viochar_is_console(pi)) hvlogOutput(&ch, 1); if (viopath_isactive(pi->lp)) internal_write(pi, &ch, 1);}/* * TTY write_room method */static int viotty_write_room(struct tty_struct *tty){ int i; int room = 0; struct port_info *pi; unsigned long flags; spin_lock_irqsave(&consolelock, flags); pi = (struct port_info *)tty->driver_data; if (!pi || viotty_paranoia_check(pi, tty->name, "viotty_write_room")) { spin_unlock_irqrestore(&consolelock, flags); return 0; } /* If no buffers are used, return the max size. */ if (pi->used == 0) { spin_unlock_irqrestore(&consolelock, flags); return VIOCHAR_MAX_DATA * VIOCHAR_NUM_BUF; } /* * We retain the spinlock because we want to get an accurate * count and it can change on us between each operation if we * don't hold the spinlock. */ for (i = 0; ((i < VIOCHAR_NUM_BUF) && (room < VIOCHAR_MAX_DATA)); i++) room += (VIOCHAR_MAX_DATA - pi->bufferBytes[i]); spin_unlock_irqrestore(&consolelock, flags); if (room > VIOCHAR_MAX_DATA) room = VIOCHAR_MAX_DATA; return room;}/* * TTY chars_in_buffer method */static int viotty_chars_in_buffer(struct tty_struct *tty){ return 0;}static int viotty_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg){ switch (cmd) { /* * the ioctls below read/set the flags usually shown in the leds * don't use them - they will go away without warning */ case KDGETLED: case KDGKBLED: return put_user(0, (char *)arg); case KDSKBLED: return 0; } return n_tty_ioctl(tty, file, cmd, arg);}/* * Handle an open charLpEvent. Could be either interrupt or ack */static void vioHandleOpenEvent(struct HvLpEvent *event){ unsigned long flags; struct viocharlpevent *cevent = (struct viocharlpevent *)event; u8 port = cevent->virtual_device; struct port_info *pi; int reject = 0; if (hvlpevent_is_ack(event)) { if (port >= VTTY_PORTS) return; spin_lock_irqsave(&consolelock, flags); /* Got the lock, don't cause console output */ pi = &port_info[port]; if (event->xRc == HvLpEvent_Rc_Good) { pi->seq = pi->ack = 0; /* * This line allows connections from the primary * partition but once one is connected from the * primary partition nothing short of a reboot * of linux will allow access from the hosting * partition again without a required iSeries fix. */ pi->lp = event->xTargetLp; } spin_unlock_irqrestore(&consolelock, flags); if (event->xRc != HvLpEvent_Rc_Good) printk(VIOCONS_KERN_WARN "handle_open_event: event->xRc == (%d).\n", event->xRc); if (event->xCorrelationToken != 0) { atomic_t *aptr= (atomic_t *)event->xCorrelationToken; atomic_set(aptr, 1); } else printk(VIOCONS_KERN_WARN "weird...got open ack without atomic\n"); return; } /* This had better require an ack, otherwise complain */ if (!hvlpevent_need_ack(event)) { printk(VIOCONS_KERN_WARN "viocharopen without ack bit!\n"); return; } spin_lock_irqsave(&consolelock, flags); /* Got the lock, don't cause console output */ /* Make sure this is a good virtual tty */ if (port >= VTTY_PORTS) { event->xRc = HvLpEvent_Rc_SubtypeError; cevent->subtype_result_code = viorc_openRejected; /* * Flag state here since we can't printk while holding * a spinlock. */ reject = 1; } else { pi = &port_info[port]; if ((pi->lp != HvLpIndexInvalid) && (pi->lp != event->xSourceLp)) { /* * If this is tty is already connected to a different * partition, fail. */ event->xRc = HvLpEvent_Rc_SubtypeError; cevent->subtype_result_code = viorc_openRejected; reject = 2; } else { pi->lp = event->xSourceLp; event->xRc = HvLpEvent_Rc_Good; cevent->subtype_result_code = viorc_good; pi->seq = pi->ack = 0; reject = 0; } } spin_unlock_irqrestore(&consolelock, flags); if (reject == 1) printk(VIOCONS_KERN_WARN "open rejected: bad virtual tty.\n"); else if (reject == 2) printk(VIOCONS_KERN_WARN "open rejected: console in exclusive use by another partition.\n"); /* Return the acknowledgement */ HvCallEvent_ackLpEvent(event);}/* * Handle a close charLpEvent. This should ONLY be an Interrupt because the * virtual console should never actually issue a close event to the hypervisor * because the virtual console never goes away. A close event coming from the * hypervisor simply means that there are no client consoles connected to the * virtual console. * * Regardless of the number of connections masqueraded on the other side of * the hypervisor ONLY ONE close event should be called to accompany the ONE * open event that is called. The close event should ONLY be called when NO * MORE connections (masqueraded or not) exist on the other side of the * hypervisor. */static void vioHandleCloseEvent(struct HvLpEvent *event){ unsigned long flags; struct viocharlpevent *cevent = (struct viocharlpevent *)event; u8 port = cevent->virtual_device; if (hvlpevent_is_int(event)) { if (port >= VTTY_PORTS) { printk(VIOCONS_KERN_WARN "close message from invalid virtual device.\n"); return; } /* For closes, just mark the console partition invalid */ spin_lock_irqsave(&consolelock, flags); /* Got the lock, don't cause console output */ if (port_info[port].lp == event->xSourceLp) port_info[port].lp = HvLpIndexInvalid; spin_unlock_irqrestore(&consolelock, flags); printk(VIOCONS_KERN_INFO "close from %d\n", event->xSourceLp); } else printk(VIOCONS_KERN_WARN "got unexpected close acknowlegement\n");}/* * Handle a config charLpEvent. Could be either interrupt or ack */static void vioHandleConfig(struct HvLpEvent *event){ struct viocharlpevent *cevent = (struct viocharlpevent *)event; HvCall_writeLogBuffer(cevent->data, cevent->len); if (cevent->data[0] == 0x01) printk(VIOCONS_KERN_INFO "window resized to %d: %d: %d: %d\n", cevent->data[1], cevent->data[2], cevent->data[3], cevent->data[4]); else printk(VIOCONS_KERN_WARN "unknown config event\n");}/* * Handle a data charLpEvent. */static void vioHandleData(struct HvLpEvent *event){ struct tty_struct *tty; unsigned long flags; struct viocharlpevent *cevent = (struct viocharlpevent *)event; struct port_info *pi; int index; int num_pushed; u8 port = cevent->virtual_device; if (port >= VTTY_PORTS) { printk(VIOCONS_KERN_WARN "data on invalid virtual device %d\n", port); return; } /* * Hold the spinlock so that we don't take an interrupt that * changes tty between the time we fetch the port_info * pointer and the time we paranoia check. */ spin_lock_irqsave(&consolelock, flags); pi = &port_info[port]; /* * Change 05/01/2003 - Ryan Arnold: If a partition other than * the current exclusive partition tries to send us data * events then just drop them on the floor because we don't * want his stinking data. He isn't authorized to receive * data because he wasn't the first one to get the console, * therefore he shouldn't be allowed to send data either. * This will work without an iSeries fix. */ if (pi->lp != event->xSourceLp) { spin_unlock_irqrestore(&consolelock, flags); return; } tty = pi->tty; if (tty == NULL) { spin_unlock_irqrestore(&consolelock, flags); printk(VIOCONS_KERN_WARN "no tty for virtual device %d\n", port); return; } if (tty->magic != TTY_MAGIC) { spin_unlock_irqrestore(&consolelock, flags); printk(VIOCONS_KERN_WARN "tty bad magic\n"); return; } /* * Just to be paranoid, make sure the tty points back to this port */ pi = (struct port_info *)tty->driver_data; if (!pi || viotty_paranoia_check(pi, tty->name, "vioHandleData")) { spin_unlock_irqrestore(&consolelock, flags); return; } spin_unlock_irqrestore(&consolelock, flags); /* * Change 07/21/2003 - Ryan Arnold: functionality added to * support sysrq utilizing ^O as the sysrq key. The sysrq * functionality will only work if built into the kernel and * then only if sysrq is enabled through the proc filesystem. */ num_pushed = 0; for (index = 0; index < cevent->len; index++) { /* * Will be optimized away if !CONFIG_MAGIC_SYSRQ: */ if (sysrq_on()) { /* 0x0f is the ascii character for ^O */ if (cevent->data[index] == '\x0f') { vio_sysrq_pressed = 1; /* * continue because we don't want to add * the sysrq key into the data string. */ continue; } else if (vio_sysrq_pressed) { handle_sysrq(cevent->data[index], tty); vio_sysrq_pressed = 0; /* * continue because we don't want to add * the sysrq sequence into the data string. */ continue; } } /* * The sysrq sequence isn't included in this check if * sysrq is enabled and compiled into the kernel because * the sequence will never get inserted into the buffer. * Don't attempt to copy more data into the buffer than we * have room for because it would fail without indication. */ if(tty_insert_flip_char(tty, cevent->data[index], TTY_NORMAL) == 0) { printk(VIOCONS_KERN_WARN "input buffer overflow!\n"); break; } num_pushed++; } if (num_pushed) tty_flip_buffer_push(tty);}/* * Handle an ack charLpEvent. */static void vioHandleAck(struct HvLpEvent *event){ struct viocharlpevent *cevent = (struct viocharlpevent *)event; unsigned long flags; u8 port = cevent->virtual_device; if (port >= VTTY_PORTS) { printk(VIOCONS_KERN_WARN "data on invalid virtual device\n"); return; } spin_lock_irqsave(&consolelock, flags); port_info[port].ack = event->xCorrelationToken; spin_unlock_irqrestore(&consolelock, flags); if (port_info[port].used) send_buffers(&port_info[port]);}/* * Handle charLpEvents and route to the appropriate routine */static void vioHandleCharEvent(struct HvLpEvent *event){ int charminor; if (event == NULL) return; charminor = event->xSubtype & VIOMINOR_SUBTYPE_MASK; switch (charminor) { case viocharopen: vioHandleOpenEvent(event); break; case viocharclose: vioHandleCloseEvent(event); break; case viochardata: vioHandleData(event); break; case viocharack: vioHandleAck(event); break; case viocharconfig: vioHandleConfig(event); break; default: if (hvlpevent_is_int(event) && hvlpevent_need_ack(event)) { event->xRc = HvLpEvent_Rc_InvalidSubtype; HvCallEvent_ackLpEvent(event); } }}/* * Send an open event */static int send_open(HvLpIndex remoteLp, void *sem){ return HvCallEvent_signalLpEventFast(remoteLp, HvLpEvent_Type_VirtualIo, viomajorsubtype_chario | viocharopen, HvLpEvent_AckInd_DoAck, HvLpEvent_AckType_ImmediateAck, viopath_sourceinst(remoteLp), viopath_targetinst(remoteLp), (u64)(unsigned long)sem, VIOVERSION << 16, 0, 0, 0, 0);}static const struct tty_operations serial_ops = { .open = viotty_open, .close = viotty_close, .write = viotty_write, .put_char = viotty_put_char, .write_room = viotty_write_room, .chars_in_buffer = viotty_chars_in_buffer, .ioctl = viotty_ioctl,};static int __init viocons_init2(void){ atomic_t wait_flag; int rc; if (!firmware_has_feature(FW_FEATURE_ISERIES)) return -ENODEV; /* +2 for fudge */ rc = viopath_open(HvLpConfig_getPrimaryLpIndex(), viomajorsubtype_chario, VIOCHAR_WINDOW + 2); if (rc) printk(VIOCONS_KERN_WARN "error opening to primary %d\n", rc); if (viopath_hostLp == HvLpIndexInvalid) vio_set_hostlp(); /* * And if the primary is not the same as the hosting LP, open to the * hosting lp */ if ((viopath_hostLp != HvLpIndexInvalid) && (viopath_hostLp != HvLpConfig_getPrimaryLpIndex())) { printk(VIOCONS_KERN_INFO "open path to hosting (%d)\n", viopath_hostLp); rc = viopath_open(viopath_hostLp, viomajorsubtype_chario, VIOCHAR_WINDOW + 2); /* +2 for fudge */ if (rc) printk(VIOCONS_KERN_WARN "error opening to partition %d: %d\n", viopath_hostLp, rc); } if (vio_setHandler(viomajorsubtype_chario, vioHandleCharEvent) < 0) printk(VIOCONS_KERN_WARN "error seting handler for console events!\n"); /* * First, try to open the console to the hosting lp. * Wait on a semaphore for the response. */ atomic_set(&wait_flag, 0); if ((viopath_isactive(viopath_hostLp)) && (send_open(viopath_hostLp, (void *)&wait_flag) == 0)) { printk(VIOCONS_KERN_INFO "hosting partition %d\n", viopath_hostLp); while (atomic_read(&wait_flag) == 0) mb(); atomic_set(&wait_flag, 0); } /* * If we don't have an active console, try the primary */ if ((!viopath_isactive(port_info[0].lp)) && (viopath_isactive(HvLpConfig_getPrimaryLpIndex())) && (send_open(HvLpConfig_getPrimaryLpIndex(), (void *)&wait_flag) == 0)) { printk(VIOCONS_KERN_INFO "opening console to primary partition\n"); while (atomic_read(&wait_flag) == 0) mb(); } /* Initialize the tty_driver structure */ viotty_driver = alloc_tty_driver(VTTY_PORTS); viotty_driver->owner = THIS_MODULE; viotty_driver->driver_name = "vioconsole"; viotty_driver->name = "tty"; viotty_driver->name_base = 1; viotty_driver->major = TTY_MAJOR; viotty_driver->minor_start = 1; viotty_driver->type = TTY_DRIVER_TYPE_CONSOLE; viotty_driver->subtype = 1; viotty_driver->init_termios = tty_std_termios; viotty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_RESET_TERMIOS; tty_set_operations(viotty_driver, &serial_ops); if (tty_register_driver(viotty_driver)) { printk(VIOCONS_KERN_WARN "couldn't register console driver\n"); put_tty_driver(viotty_driver); viotty_driver = NULL; } unregister_console(&viocons_early); register_console(&viocons); return 0;}static int __init viocons_init(void){ int i; if (!firmware_has_feature(FW_FEATURE_ISERIES)) return -ENODEV; printk(VIOCONS_KERN_INFO "registering console\n"); for (i = 0; i < VTTY_PORTS; i++) { port_info[i].lp = HvLpIndexInvalid; port_info[i].magic = VIOTTY_MAGIC; } HvCall_setLogBufferFormatAndCodepage(HvCall_LogBuffer_ASCII, 437); add_preferred_console("viocons", 0, NULL); register_console(&viocons_early); return 0;}console_initcall(viocons_init);module_init(viocons_init2);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?