hvsi.c
来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 1,320 行 · 第 1/3 页
C
1,320 行
if (tty_hung_up_p(filp)) return; spin_lock_irqsave(&hp->lock, flags); if (--hp->count == 0) { hp->tty = NULL; hp->inbuf_end = hp->inbuf; /* discard remaining partial packets */ /* only close down connection if it is not the console */ if (!(hp->flags & HVSI_CONSOLE)) { 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; pr_debug("%s\n", __FUNCTION__); hp->count = 0; hp->tty = NULL;}/* 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) { /* * either all data was sent or there was an error, and we throw away * data on error. */ hp->n_outbuf = 0; }}/* hvsi_write_worker will keep rescheduling itself until outbuf is empty */static void hvsi_write_worker(void *arg){ struct hvsi_struct *hp = (struct hvsi_struct *)arg; 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); 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); if (test_bit(TTY_DO_WRITE_WAKEUP, &hp->tty->flags) && hp->tty->ldisc.write_wakeup) hp->tty->ldisc.write_wakeup(hp->tty); wake_up_interruptible(&hp->tty->write_wait); } 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, int from_user, const unsigned char *buf, int count){ struct hvsi_struct *hp = tty->driver_data; const char *source = buf; char *kbuf = NULL; unsigned long flags; int total = 0; int origcount = count; if (from_user) { kbuf = kmalloc(count, GFP_KERNEL); if (kbuf == NULL) return -ENOMEM; if (copy_from_user(kbuf, buf, count)) { kfree(kbuf); return -EFAULT; } source = kbuf; } spin_lock_irqsave(&hp->lock, flags); 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 (from_user) kfree(kbuf); 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 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->devfs_name = "hvsi/"; 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->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, SA_INTERRUPT, "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_INFO "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; uint32_t *vtermno; uint32_t *irq; vtermno = (uint32_t *)get_property(vty, "reg", NULL); irq = (uint32_t *)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_WORK(&hp->writer, hvsi_write_worker, hp); init_waitqueue_head(&hp->emptyq); init_waitqueue_head(&hp->stateq); hp->lock = SPIN_LOCK_UNLOCKED; hp->index = hvsi_count; hp->inbuf_end = hp->inbuf; hp->state = HVSI_CLOSED; hp->vtermno = *vtermno; hp->virq = virt_irq_create_mapping(irq[0]); if (hp->virq == NO_IRQ) { printk(KERN_ERR "%s: couldn't create irq mapping for 0x%x\n", __FUNCTION__, hp->virq); continue; } else hp->virq = irq_offset_up(hp->virq); hvsi_count++; } if (hvsi_count) register_console(&hvsi_con_driver); return 0;}console_initcall(hvsi_console_init);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?