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