viocons.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 1,334 行 · 第 1/3 页

C
1,334
字号
	size_t curlen;	const char *curbuf;	unsigned long flags;	int copy_needed = (viochar == NULL);	/*	 * Write to the hvlog of inbound data are now done prior to	 * calling internal_write() since internal_write() is only called in	 * the event that an lp event path is active, which isn't the case for	 * logging attempts prior to console initialization.	 *	 * If there is already data queued for this port, send it prior to	 * attempting to send any new data.	 */	if (pi->used)		send_buffers(pi);	spin_lock_irqsave(&consolelock, flags);	/*	 * If the internal_write() was passed a pointer to a	 * viocharlpevent then we don't need to allocate a new one	 * (this is the case where we are internal_writing user space	 * data).  If we aren't writing user space data then we need	 * to get an event from viopath.	 */	if (copy_needed) {		/* This one is fetched from the viopath data structure */		viochar = (struct viocharlpevent *)			vio_get_event_buffer(viomajorsubtype_chario);		/* Make sure we got a buffer */		if (viochar == NULL) {			spin_unlock_irqrestore(&consolelock, flags);			hvlog("\n\rviocons: Can't get viochar buffer in internal_write().");			return -EAGAIN;		}		initDataEvent(viochar, pi->lp);	}	curbuf = buf;	bleft = len;	while ((bleft > 0) && (pi->used == 0) &&	       ((pi->seq - pi->ack) < VIOCHAR_WINDOW)) {		if (bleft > VIOCHAR_MAX_DATA)			curlen = VIOCHAR_MAX_DATA;		else			curlen = bleft;		viochar->event.xCorrelationToken = pi->seq++;		if (copy_needed) {			memcpy(viochar->data, curbuf, curlen);			viochar->len = curlen;		}		viochar->event.xSizeMinus1 =		    offsetof(struct viocharlpevent, data) + curlen;		hvrc = HvCallEvent_signalLpEvent(&viochar->event);		if (hvrc) {			spin_unlock_irqrestore(&consolelock, flags);			if (copy_needed)				vio_free_event_buffer(viomajorsubtype_chario, viochar);			hvlog("viocons: error sending event! %d\n", (int)hvrc);			return len - bleft;		}		curbuf += curlen;		bleft -= curlen;	}	/* If we didn't send it all, buffer as much of it as we can. */	if (bleft > 0)		bleft -= buffer_add(pi, curbuf, bleft);	/*	 * Since we grabbed it from the viopath data structure, return	 * it to the data structure.	 */	if (copy_needed)		vio_free_event_buffer(viomajorsubtype_chario, viochar);	spin_unlock_irqrestore(&consolelock, flags);	return len - bleft;}static struct port_info *get_port_data(struct tty_struct *tty){	unsigned long flags;	struct port_info *pi;	spin_lock_irqsave(&consolelock, flags);	if (tty) {		pi = (struct port_info *)tty->driver_data;		if (!pi || viotty_paranoia_check(pi, tty->name,					     "get_port_data")) {			pi = NULL;		}	} else		/*		 * If this is the console device, use the lp from		 * the first port entry		 */		pi = &port_info[0];	spin_unlock_irqrestore(&consolelock, flags);	return pi;}/* * Initialize the common fields in a charLpEvent */static void initDataEvent(struct viocharlpevent *viochar, HvLpIndex lp){	memset(viochar, 0, sizeof(struct viocharlpevent));	viochar->event.xFlags.xValid = 1;	viochar->event.xFlags.xFunction = HvLpEvent_Function_Int;	viochar->event.xFlags.xAckInd = HvLpEvent_AckInd_NoAck;	viochar->event.xFlags.xAckType = HvLpEvent_AckType_DeferredAck;	viochar->event.xType = HvLpEvent_Type_VirtualIo;	viochar->event.xSubtype = viomajorsubtype_chario | viochardata;	viochar->event.xSourceLp = HvLpConfig_getLpIndex();	viochar->event.xTargetLp = lp;	viochar->event.xSizeMinus1 = sizeof(struct viocharlpevent);	viochar->event.xSourceInstanceId = viopath_sourceinst(lp);	viochar->event.xTargetInstanceId = viopath_targetinst(lp);}/* * early console device write */static void viocons_write_early(struct console *co, const char *s, unsigned count){	hvlogOutput(s, count);}/* * console device write */static void viocons_write(struct console *co, const char *s, unsigned count){	int index;	int begin;	struct port_info *pi;	static const char cr = '\r';	/*	 * Check port data first because the target LP might be valid but	 * simply not active, in which case we want to hvlog the output.	 */	pi = get_port_data(NULL);	if (pi == NULL) {		hvlog("\n\rviocons_write: unable to get port data.");		return;	}	hvlogOutput(s, count);	if (!viopath_isactive(pi->lp)) {		/*		 * This is a VERY noisy trace message in the case where the		 * path manager is not active or in the case where this		 * function is called prior to viocons initialization.  It is		 * being commented out for the sake of a clear trace buffer.		 */#if 0		 hvlog("\n\rviocons_write: path not active to lp %d", pi->lp);#endif		return;	}	/* 	 * Any newline character found will cause a	 * carriage return character to be emitted as well. 	 */	begin = 0;	for (index = 0; index < count; index++) {		if (s[index] == '\n') {			/* 			 * Newline found. Print everything up to and 			 * including the newline			 */			internal_write(pi, &s[begin], index - begin + 1,					NULL);			begin = index + 1;			/* Emit a carriage return as well */			internal_write(pi, &cr, 1, NULL);		}	}	/* If any characters left to write, write them now */	if ((index - begin) > 0)		internal_write(pi, &s[begin], index - begin, NULL);}/* * Work out the device associate with this console */static struct tty_driver *viocons_device(struct console *c, int *index){	*index = c->index;	return viotty_driver;}/* * console device I/O methods */static struct console viocons_early = {	.name = "viocons",	.write = viocons_write_early,	.flags = CON_PRINTBUFFER,	.index = -1,};static struct console viocons = {	.name = "viocons",	.write = viocons_write,	.device = viocons_device,	.flags = CON_PRINTBUFFER,	.index = -1,};/* * TTY Open method */static int viotty_open(struct tty_struct *tty, struct file *filp){	int port;	unsigned long flags;	struct port_info *pi;	port = tty->index;	if ((port < 0) || (port >= VTTY_PORTS))		return -ENODEV;	spin_lock_irqsave(&consolelock, flags);	pi = &port_info[port];	/* If some other TTY is already connected here, reject the open */	if ((pi->tty) && (pi->tty != tty)) {		spin_unlock_irqrestore(&consolelock, flags);		printk(VIOCONS_KERN_WARN		       "attempt to open device twice from different ttys\n");		return -EBUSY;	}	tty->driver_data = pi;	pi->tty = tty;	spin_unlock_irqrestore(&consolelock, flags);	return 0;}/* * TTY Close method */static void viotty_close(struct tty_struct *tty, struct file *filp){	unsigned long flags;	struct port_info *pi;	spin_lock_irqsave(&consolelock, flags);	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, int from_user,			const unsigned char *buf, int count){	int ret;	int total = 0;	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)) {		/* Noisy trace.  Commented unless needed. */#if 0		 hvlog("\n\rviotty_write: viopath NOT active for lp %d.",pi->lp);#endif		return count;	}	/*	 * If the viotty_write is invoked from user space we want to do the	 * copy_from_user() into an event buffer from the cfu buffer before	 * internal_write() is called because internal_write may need to buffer	 * data which will need to grab a spin_lock and we shouldn't	 * copy_from_user() while holding a spin_lock.  Should internal_write()	 * not need to buffer data then it'll just use the event we created here	 * rather than checking one out from vio_get_event_buffer().	 */	if (from_user) {		struct viocharlpevent *viochar;		int curlen;		const char *curbuf = buf;		viochar = viocons_get_cfu_buffer();		if (viochar == NULL)			return -EAGAIN;		initDataEvent(viochar, pi->lp);		while (count > 0) {			if (count > VIOCHAR_MAX_DATA)				curlen = VIOCHAR_MAX_DATA;			else				curlen = count;			viochar->len = curlen;			ret = copy_from_user(viochar->data, curbuf, curlen);			if (ret)				break;			ret = internal_write(pi, viochar->data,					viochar->len, viochar);			total += ret;			if (ret != curlen)				break;			count -= curlen;			curbuf += curlen;		}		viocons_free_cfu_buffer(viochar);	} else		total = internal_write(pi, buf, count, NULL);	return total;}/* * 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, NULL);}/* * 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;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?