hvc_console.c

来自「linux 内核源代码」· C语言 代码 · 共 908 行 · 第 1/2 页

C
908
字号
	spin_unlock_irqrestore(&hp->lock, flags);	if (irq)		free_irq(irq, hp);	while(temp_open_count) {		--temp_open_count;		kobject_put(kobjp);	}}/* * Push buffered characters whether they were just recently buffered or waiting * on a blocked hypervisor.  Call this function with hp->lock held. */static void hvc_push(struct hvc_struct *hp){	int n;	n = hp->ops->put_chars(hp->vtermno, hp->outbuf, hp->n_outbuf);	if (n <= 0) {		if (n == 0) {			hp->do_wakeup = 1;			return;		}		/* throw away output on error; this happens when		   there is no session connected to the vterm. */		hp->n_outbuf = 0;	} else		hp->n_outbuf -= n;	if (hp->n_outbuf > 0)		memmove(hp->outbuf, hp->outbuf + n, hp->n_outbuf);	else		hp->do_wakeup = 1;}static int hvc_write(struct tty_struct *tty, const unsigned char *buf, int count){	struct hvc_struct *hp = tty->driver_data;	unsigned long flags;	int rsize, written = 0;	/* This write was probably executed during a tty close. */	if (!hp)		return -EPIPE;	if (hp->count <= 0)		return -EIO;	spin_lock_irqsave(&hp->lock, flags);	/* Push pending writes */	if (hp->n_outbuf > 0)		hvc_push(hp);	while (count > 0 && (rsize = hp->outbuf_size - hp->n_outbuf) > 0) {		if (rsize > count)			rsize = count;		memcpy(hp->outbuf + hp->n_outbuf, buf, rsize);		count -= rsize;		buf += rsize;		hp->n_outbuf += rsize;		written += rsize;		hvc_push(hp);	}	spin_unlock_irqrestore(&hp->lock, flags);	/*	 * Racy, but harmless, kick thread if there is still pending data.	 */	if (hp->n_outbuf)		hvc_kick();	return written;}/* * This is actually a contract between the driver and the tty layer outlining * how much write room the driver can guarantee will be sent OR BUFFERED.  This * driver MUST honor the return value. */static int hvc_write_room(struct tty_struct *tty){	struct hvc_struct *hp = tty->driver_data;	if (!hp)		return -1;	return hp->outbuf_size - hp->n_outbuf;}static int hvc_chars_in_buffer(struct tty_struct *tty){	struct hvc_struct *hp = tty->driver_data;	if (!hp)		return -1;	return hp->n_outbuf;}/* * timeout will vary between the MIN and MAX values defined here.  By default * and during console activity we will use a default MIN_TIMEOUT of 10.  When * the console is idle, we increase the timeout value on each pass through * msleep until we reach the max.  This may be noticeable as a brief (average * one second) delay on the console before the console responds to input when * there has been no input for some time. */#define MIN_TIMEOUT		(10)#define MAX_TIMEOUT		(2000)static u32 timeout = MIN_TIMEOUT;#define HVC_POLL_READ	0x00000001#define HVC_POLL_WRITE	0x00000002static int hvc_poll(struct hvc_struct *hp){	struct tty_struct *tty;	int i, n, poll_mask = 0;	char buf[N_INBUF] __ALIGNED__;	unsigned long flags;	int read_total = 0;	spin_lock_irqsave(&hp->lock, flags);	/* Push pending writes */	if (hp->n_outbuf > 0)		hvc_push(hp);	/* Reschedule us if still some write pending */	if (hp->n_outbuf > 0)		poll_mask |= HVC_POLL_WRITE;	/* No tty attached, just skip */	tty = hp->tty;	if (tty == NULL)		goto bail;	/* Now check if we can get data (are we throttled ?) */	if (test_bit(TTY_THROTTLED, &tty->flags))		goto throttled;	/* If we aren't interrupt driven and aren't throttled, we always	 * request a reschedule	 */	if (hp->irq == 0)		poll_mask |= HVC_POLL_READ;	/* Read data if any */	for (;;) {		int count = tty_buffer_request_room(tty, N_INBUF);		/* If flip is full, just reschedule a later read */		if (count == 0) {			poll_mask |= HVC_POLL_READ;			break;		}		n = hp->ops->get_chars(hp->vtermno, buf, count);		if (n <= 0) {			/* Hangup the tty when disconnected from host */			if (n == -EPIPE) {				spin_unlock_irqrestore(&hp->lock, flags);				tty_hangup(tty);				spin_lock_irqsave(&hp->lock, flags);			} else if ( n == -EAGAIN ) {				/*				 * Some back-ends can only ensure a certain min				 * num of bytes read, which may be > 'count'.				 * Let the tty clear the flip buff to make room.				 */				poll_mask |= HVC_POLL_READ;			}			break;		}		for (i = 0; i < n; ++i) {#ifdef CONFIG_MAGIC_SYSRQ			if (hp->index == hvc_con_driver.index) {				/* Handle the SysRq Hack */				/* XXX should support a sequence */				if (buf[i] == '\x0f') {	/* ^O */					sysrq_pressed = 1;					continue;				} else if (sysrq_pressed) {					handle_sysrq(buf[i], tty);					sysrq_pressed = 0;					continue;				}			}#endif /* CONFIG_MAGIC_SYSRQ */			tty_insert_flip_char(tty, buf[i], 0);		}		read_total += n;	} throttled:	/* Wakeup write queue if necessary */	if (hp->do_wakeup) {		hp->do_wakeup = 0;		tty_wakeup(tty);	} bail:	spin_unlock_irqrestore(&hp->lock, flags);	if (read_total) {		/* Activity is occurring, so reset the polling backoff value to		   a minimum for performance. */		timeout = MIN_TIMEOUT;		tty_flip_buffer_push(tty);	}	return poll_mask;}#if defined(CONFIG_XMON) && defined(CONFIG_SMP)extern cpumask_t cpus_in_xmon;#elsestatic const cpumask_t cpus_in_xmon = CPU_MASK_NONE;#endif/* * This kthread is either polling or interrupt driven.  This is determined by * calling hvc_poll() who determines whether a console adapter support * interrupts. */static int khvcd(void *unused){	int poll_mask;	struct hvc_struct *hp;	set_freezable();	__set_current_state(TASK_RUNNING);	do {		poll_mask = 0;		hvc_kicked = 0;		try_to_freeze();		wmb();		if (cpus_empty(cpus_in_xmon)) {			spin_lock(&hvc_structs_lock);			list_for_each_entry(hp, &hvc_structs, next) {				poll_mask |= hvc_poll(hp);			}			spin_unlock(&hvc_structs_lock);		} else			poll_mask |= HVC_POLL_READ;		if (hvc_kicked)			continue;		if (poll_mask & HVC_POLL_WRITE) {			yield();			continue;		}		set_current_state(TASK_INTERRUPTIBLE);		if (!hvc_kicked) {			if (poll_mask == 0)				schedule();			else {				if (timeout < MAX_TIMEOUT)					timeout += (timeout >> 6) + 1;				msleep_interruptible(timeout);			}		}		__set_current_state(TASK_RUNNING);	} while (!kthread_should_stop());	return 0;}static const struct tty_operations hvc_ops = {	.open = hvc_open,	.close = hvc_close,	.write = hvc_write,	.hangup = hvc_hangup,	.unthrottle = hvc_unthrottle,	.write_room = hvc_write_room,	.chars_in_buffer = hvc_chars_in_buffer,};/* callback when the kboject ref count reaches zero. */static void destroy_hvc_struct(struct kobject *kobj){	struct hvc_struct *hp = container_of(kobj, struct hvc_struct, kobj);	unsigned long flags;	spin_lock(&hvc_structs_lock);	spin_lock_irqsave(&hp->lock, flags);	list_del(&(hp->next));	spin_unlock_irqrestore(&hp->lock, flags);	spin_unlock(&hvc_structs_lock);	kfree(hp);}static struct kobj_type hvc_kobj_type = {	.release = destroy_hvc_struct,};struct hvc_struct __devinit *hvc_alloc(uint32_t vtermno, int irq,					struct hv_ops *ops, int outbuf_size){	struct hvc_struct *hp;	int i;	/* We wait until a driver actually comes along */	if (!hvc_driver) {		int err = hvc_init();		if (err)			return ERR_PTR(err);	}	hp = kmalloc(ALIGN(sizeof(*hp), sizeof(long)) + outbuf_size,			GFP_KERNEL);	if (!hp)		return ERR_PTR(-ENOMEM);	memset(hp, 0x00, sizeof(*hp));	hp->vtermno = vtermno;	hp->irq = irq;	hp->ops = ops;	hp->outbuf_size = outbuf_size;	hp->outbuf = &((char *)hp)[ALIGN(sizeof(*hp), sizeof(long))];	kobject_init(&hp->kobj);	hp->kobj.ktype = &hvc_kobj_type;	spin_lock_init(&hp->lock);	spin_lock(&hvc_structs_lock);	/*	 * find index to use:	 * see if this vterm id matches one registered for console.	 */	for (i=0; i < MAX_NR_HVC_CONSOLES; i++)		if (vtermnos[i] == hp->vtermno &&		    cons_ops[i] == hp->ops)			break;	/* no matching slot, just use a counter */	if (i >= MAX_NR_HVC_CONSOLES)		i = ++last_hvc;	hp->index = i;	list_add_tail(&(hp->next), &hvc_structs);	spin_unlock(&hvc_structs_lock);	return hp;}int __devexit hvc_remove(struct hvc_struct *hp){	unsigned long flags;	struct kobject *kobjp;	struct tty_struct *tty;	spin_lock_irqsave(&hp->lock, flags);	tty = hp->tty;	kobjp = &hp->kobj;	if (hp->index < MAX_NR_HVC_CONSOLES)		vtermnos[hp->index] = -1;	/* Don't whack hp->irq because tty_hangup() will need to free the irq. */	spin_unlock_irqrestore(&hp->lock, flags);	/*	 * We 'put' the instance that was grabbed when the kobject instance	 * was initialized using kobject_init().  Let the last holder of this	 * kobject cause it to be removed, which will probably be the tty_hangup	 * below.	 */	kobject_put(kobjp);	/*	 * This function call will auto chain call hvc_hangup.  The tty should	 * always be valid at this time unless a simultaneous tty close already	 * cleaned up the hvc_struct.	 */	if (tty)		tty_hangup(tty);	return 0;}/* Driver initialization: called as soon as someone uses hvc_alloc(). */static int hvc_init(void){	struct tty_driver *drv;	int err;	/* We need more than hvc_count adapters due to hotplug additions. */	drv = alloc_tty_driver(HVC_ALLOC_TTY_ADAPTERS);	if (!drv) {		err = -ENOMEM;		goto out;	}	drv->owner = THIS_MODULE;	drv->driver_name = "hvc";	drv->name = "hvc";	drv->major = HVC_MAJOR;	drv->minor_start = HVC_MINOR;	drv->type = TTY_DRIVER_TYPE_SYSTEM;	drv->init_termios = tty_std_termios;	drv->flags = TTY_DRIVER_REAL_RAW;	tty_set_operations(drv, &hvc_ops);	/* Always start the kthread because there can be hotplug vty adapters	 * added later. */	hvc_task = kthread_run(khvcd, NULL, "khvcd");	if (IS_ERR(hvc_task)) {		printk(KERN_ERR "Couldn't create kthread for console.\n");		err = PTR_ERR(hvc_task);		goto put_tty;	}	err = tty_register_driver(drv);	if (err) {		printk(KERN_ERR "Couldn't register hvc console driver\n");		goto stop_thread;	}	/* FIXME: This mb() seems completely random.  Remove it. */	mb();	hvc_driver = drv;	return 0;put_tty:	put_tty_driver(hvc_driver);stop_thread:	kthread_stop(hvc_task);	hvc_task = NULL;out:	return err;}/* This isn't particularly necessary due to this being a console driver * but it is nice to be thorough. */static void __exit hvc_exit(void){	if (hvc_driver) {		kthread_stop(hvc_task);		tty_unregister_driver(hvc_driver);		/* return tty_struct instances allocated in hvc_init(). */		put_tty_driver(hvc_driver);		unregister_console(&hvc_con_driver);	}}module_exit(hvc_exit);

⌨️ 快捷键说明

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