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 + -
显示快捷键?