hvsi.c
来自「linux 内核源代码」· C语言 代码 · 共 1,317 行 · 第 1/3 页
C
1,317 行
hp->tty = NULL; hp->inbuf_end = hp->inbuf; /* discard remaining partial packets */ /* only close down connection if it is not the console */ if (!is_console(hp)) { h_vio_signal(hp->vtermno, VIO_IRQ_DISABLE); /* no more irqs */ __set_state(hp, HVSI_CLOSED); /* * any data delivered to the tty layer after this will be * discarded (except for XON/XOFF) */ tty->closing = 1; spin_unlock_irqrestore(&hp->lock, flags); /* let any existing irq handlers finish. no more will start. */ synchronize_irq(hp->virq); /* hvsi_write_worker will re-schedule until outbuf is empty. */ hvsi_flush_output(hp); /* tell FSP to stop sending data */ hvsi_close_protocol(hp); /* * drain anything FSP is still in the middle of sending, and let * hvsi_handshake drain the rest on the next open. */ hvsi_drain_input(hp); spin_lock_irqsave(&hp->lock, flags); } } else if (hp->count < 0) printk(KERN_ERR "hvsi_close %lu: oops, count is %d\n", hp - hvsi_ports, hp->count); spin_unlock_irqrestore(&hp->lock, flags);}static void hvsi_hangup(struct tty_struct *tty){ struct hvsi_struct *hp = tty->driver_data; unsigned long flags; pr_debug("%s\n", __FUNCTION__); spin_lock_irqsave(&hp->lock, flags); hp->count = 0; hp->n_outbuf = 0; hp->tty = NULL; spin_unlock_irqrestore(&hp->lock, flags);}/* called with hp->lock held */static void hvsi_push(struct hvsi_struct *hp){ int n; if (hp->n_outbuf <= 0) return; n = hvsi_put_chars(hp, hp->outbuf, hp->n_outbuf); if (n > 0) { /* success */ pr_debug("%s: wrote %i chars\n", __FUNCTION__, n); hp->n_outbuf = 0; } else if (n == -EIO) { __set_state(hp, HVSI_FSP_DIED); printk(KERN_ERR "hvsi%i: service processor died\n", hp->index); }}/* hvsi_write_worker will keep rescheduling itself until outbuf is empty */static void hvsi_write_worker(struct work_struct *work){ struct hvsi_struct *hp = container_of(work, struct hvsi_struct, writer.work); unsigned long flags;#ifdef DEBUG static long start_j = 0; if (start_j == 0) start_j = jiffies;#endif /* DEBUG */ spin_lock_irqsave(&hp->lock, flags); pr_debug("%s: %i chars in buffer\n", __FUNCTION__, hp->n_outbuf); if (!is_open(hp)) { /* * We could have a non-open connection if the service processor died * while we were busily scheduling ourselves. In that case, it could * be minutes before the service processor comes back, so only try * again once a second. */ schedule_delayed_work(&hp->writer, HZ); goto out; } hvsi_push(hp); if (hp->n_outbuf > 0) schedule_delayed_work(&hp->writer, 10); else {#ifdef DEBUG pr_debug("%s: outbuf emptied after %li jiffies\n", __FUNCTION__, jiffies - start_j); start_j = 0;#endif /* DEBUG */ wake_up_all(&hp->emptyq); tty_wakeup(hp->tty); }out: spin_unlock_irqrestore(&hp->lock, flags);}static int hvsi_write_room(struct tty_struct *tty){ struct hvsi_struct *hp = (struct hvsi_struct *)tty->driver_data; return N_OUTBUF - hp->n_outbuf;}static int hvsi_chars_in_buffer(struct tty_struct *tty){ struct hvsi_struct *hp = (struct hvsi_struct *)tty->driver_data; return hp->n_outbuf;}static int hvsi_write(struct tty_struct *tty, const unsigned char *buf, int count){ struct hvsi_struct *hp = tty->driver_data; const char *source = buf; unsigned long flags; int total = 0; int origcount = count; spin_lock_irqsave(&hp->lock, flags); pr_debug("%s: %i chars in buffer\n", __FUNCTION__, hp->n_outbuf); if (!is_open(hp)) { /* we're either closing or not yet open; don't accept data */ pr_debug("%s: not open\n", __FUNCTION__); goto out; } /* * when the hypervisor buffer (16K) fills, data will stay in hp->outbuf * and hvsi_write_worker will be scheduled. subsequent hvsi_write() calls * will see there is no room in outbuf and return. */ while ((count > 0) && (hvsi_write_room(hp->tty) > 0)) { int chunksize = min(count, hvsi_write_room(hp->tty)); BUG_ON(hp->n_outbuf < 0); memcpy(hp->outbuf + hp->n_outbuf, source, chunksize); hp->n_outbuf += chunksize; total += chunksize; source += chunksize; count -= chunksize; hvsi_push(hp); } if (hp->n_outbuf > 0) { /* * we weren't able to write it all to the hypervisor. * schedule another push attempt. */ schedule_delayed_work(&hp->writer, 10); }out: spin_unlock_irqrestore(&hp->lock, flags); if (total != origcount) pr_debug("%s: wanted %i, only wrote %i\n", __FUNCTION__, origcount, total); return total;}/* * I have never seen throttle or unthrottle called, so this little throttle * buffering scheme may or may not work. */static void hvsi_throttle(struct tty_struct *tty){ struct hvsi_struct *hp = (struct hvsi_struct *)tty->driver_data; pr_debug("%s\n", __FUNCTION__); h_vio_signal(hp->vtermno, VIO_IRQ_DISABLE);}static void hvsi_unthrottle(struct tty_struct *tty){ struct hvsi_struct *hp = (struct hvsi_struct *)tty->driver_data; unsigned long flags; int shouldflip = 0; pr_debug("%s\n", __FUNCTION__); spin_lock_irqsave(&hp->lock, flags); if (hp->n_throttle) { hvsi_send_overflow(hp); shouldflip = 1; } spin_unlock_irqrestore(&hp->lock, flags); if (shouldflip) tty_flip_buffer_push(hp->tty); h_vio_signal(hp->vtermno, VIO_IRQ_ENABLE);}static int hvsi_tiocmget(struct tty_struct *tty, struct file *file){ struct hvsi_struct *hp = (struct hvsi_struct *)tty->driver_data; hvsi_get_mctrl(hp); return hp->mctrl;}static int hvsi_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear){ struct hvsi_struct *hp = (struct hvsi_struct *)tty->driver_data; unsigned long flags; uint16_t new_mctrl; /* we can only alter DTR */ clear &= TIOCM_DTR; set &= TIOCM_DTR; spin_lock_irqsave(&hp->lock, flags); new_mctrl = (hp->mctrl & ~clear) | set; if (hp->mctrl != new_mctrl) { hvsi_set_mctrl(hp, new_mctrl); hp->mctrl = new_mctrl; } spin_unlock_irqrestore(&hp->lock, flags); return 0;}static const struct tty_operations hvsi_ops = { .open = hvsi_open, .close = hvsi_close, .write = hvsi_write, .hangup = hvsi_hangup, .write_room = hvsi_write_room, .chars_in_buffer = hvsi_chars_in_buffer, .throttle = hvsi_throttle, .unthrottle = hvsi_unthrottle, .tiocmget = hvsi_tiocmget, .tiocmset = hvsi_tiocmset,};static int __init hvsi_init(void){ int i; hvsi_driver = alloc_tty_driver(hvsi_count); if (!hvsi_driver) return -ENOMEM; hvsi_driver->owner = THIS_MODULE; hvsi_driver->driver_name = "hvsi"; hvsi_driver->name = "hvsi"; hvsi_driver->major = HVSI_MAJOR; hvsi_driver->minor_start = HVSI_MINOR; hvsi_driver->type = TTY_DRIVER_TYPE_SYSTEM; hvsi_driver->init_termios = tty_std_termios; hvsi_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL; hvsi_driver->init_termios.c_ispeed = 9600; hvsi_driver->init_termios.c_ospeed = 9600; hvsi_driver->flags = TTY_DRIVER_REAL_RAW; tty_set_operations(hvsi_driver, &hvsi_ops); for (i=0; i < hvsi_count; i++) { struct hvsi_struct *hp = &hvsi_ports[i]; int ret = 1; ret = request_irq(hp->virq, hvsi_interrupt, IRQF_DISABLED, "hvsi", hp); if (ret) printk(KERN_ERR "HVSI: couldn't reserve irq 0x%x (error %i)\n", hp->virq, ret); } hvsi_wait = wait_for_state; /* irqs active now */ if (tty_register_driver(hvsi_driver)) panic("Couldn't register hvsi console driver\n"); printk(KERN_DEBUG "HVSI: registered %i devices\n", hvsi_count); return 0;}device_initcall(hvsi_init);/***** console (not tty) code: *****/static void hvsi_console_print(struct console *console, const char *buf, unsigned int count){ struct hvsi_struct *hp = &hvsi_ports[console->index]; char c[HVSI_MAX_OUTGOING_DATA] __ALIGNED__; unsigned int i = 0, n = 0; int ret, donecr = 0; mb(); if (!is_open(hp)) return; /* * ugh, we have to translate LF -> CRLF ourselves, in place. * copied from hvc_console.c: */ while (count > 0 || i > 0) { if (count > 0 && i < sizeof(c)) { if (buf[n] == '\n' && !donecr) { c[i++] = '\r'; donecr = 1; } else { c[i++] = buf[n++]; donecr = 0; --count; } } else { ret = hvsi_put_chars(hp, c, i); if (ret < 0) i = 0; i -= ret; } }}static struct tty_driver *hvsi_console_device(struct console *console, int *index){ *index = console->index; return hvsi_driver;}static int __init hvsi_console_setup(struct console *console, char *options){ struct hvsi_struct *hp = &hvsi_ports[console->index]; int ret; if (console->index < 0 || console->index >= hvsi_count) return -1; /* give the FSP a chance to change the baud rate when we re-open */ hvsi_close_protocol(hp); ret = hvsi_handshake(hp); if (ret < 0) return ret; ret = hvsi_get_mctrl(hp); if (ret < 0) return ret; ret = hvsi_set_mctrl(hp, hp->mctrl | TIOCM_DTR); if (ret < 0) return ret; hp->flags |= HVSI_CONSOLE; return 0;}static struct console hvsi_con_driver = { .name = "hvsi", .write = hvsi_console_print, .device = hvsi_console_device, .setup = hvsi_console_setup, .flags = CON_PRINTBUFFER, .index = -1,};static int __init hvsi_console_init(void){ struct device_node *vty; hvsi_wait = poll_for_state; /* no irqs yet; must poll */ /* search device tree for vty nodes */ for (vty = of_find_compatible_node(NULL, "serial", "hvterm-protocol"); vty != NULL; vty = of_find_compatible_node(vty, "serial", "hvterm-protocol")) { struct hvsi_struct *hp; const uint32_t *vtermno, *irq; vtermno = of_get_property(vty, "reg", NULL); irq = of_get_property(vty, "interrupts", NULL); if (!vtermno || !irq) continue; if (hvsi_count >= MAX_NR_HVSI_CONSOLES) { of_node_put(vty); break; } hp = &hvsi_ports[hvsi_count]; INIT_DELAYED_WORK(&hp->writer, hvsi_write_worker); INIT_WORK(&hp->handshaker, hvsi_handshaker); init_waitqueue_head(&hp->emptyq); init_waitqueue_head(&hp->stateq); spin_lock_init(&hp->lock); hp->index = hvsi_count; hp->inbuf_end = hp->inbuf; hp->state = HVSI_CLOSED; hp->vtermno = *vtermno; hp->virq = irq_create_mapping(NULL, irq[0]); if (hp->virq == NO_IRQ) { printk(KERN_ERR "%s: couldn't create irq mapping for 0x%x\n", __FUNCTION__, irq[0]); continue; } hvsi_count++; } if (hvsi_count) register_console(&hvsi_con_driver); return 0;}console_initcall(hvsi_console_init);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?