/* Host Virtual Serial Interface (HVSI) is a protocol between the hosted OS * and the service processor on IBM pSeries servers. On these servers, there * are no serial ports under the OS's control, and sometimes there is no other * console available either. However, the service processor has two standard * serial ports, so this over-complicated protocol allows the OS to control * those ports by proxy. * * Besides data, the procotol supports the reading/writing of the serial * port's DTR line, and the reading of the CD line. This is to allow the OS to * control a modem attached to the service processor's serial port. Note that * the OS cannot change the speed of the port through this protocol.
*/
/* * we pass data via two 8-byte registers, so we would like our char arrays * properly aligned for those loads.
*/ #define __ALIGNED__ __attribute__((__aligned__(sizeof(long))))
struct hvsi_struct { struct tty_port port; struct delayed_work writer; struct work_struct handshaker;
wait_queue_head_t emptyq; /* woken when outbuf is emptied */
wait_queue_head_t stateq; /* woken when HVSI state changes */
spinlock_t lock; int index;
uint8_t throttle_buf[128];
uint8_t outbuf[N_OUTBUF]; /* to implement write_room and chars_in_buffer */ /* inbuf is for packet reassembly. leave a little room for leftovers. */
uint8_t inbuf[HVSI_MAX_PACKET + HVSI_MAX_READ];
uint8_t *inbuf_end; int n_throttle; int n_outbuf;
uint32_t vtermno;
uint32_t virq;
atomic_t seqno; /* HVSI packet sequence number */
uint16_t mctrl;
uint8_t state; /* HVSI protocol state */
uint8_t flags; #ifdef CONFIG_MAGIC_SYSRQ
uint8_t sysrq; #endif/* CONFIG_MAGIC_SYSRQ */
}; staticstruct hvsi_struct hvsi_ports[MAX_NR_HVSI_CONSOLES];
switch (be16_to_cpu(header->verb)) { case VSV_MODEM_CTL_UPDATE: if ((be32_to_cpu(header->word) & HVSI_TSCD) == 0) { /* CD went away; no more connection */
pr_debug("hvsi%i: CD dropped\n", hp->index);
hp->mctrl &= TIOCM_CD; if (tty && !C_CLOCAL(tty))
tty_hangup(tty);
} break; case VSV_CLOSE_PROTOCOL:
pr_debug("hvsi%i: service processor came back\n", hp->index); if (hp->state != HVSI_CLOSED) {
*to_handshake = hp;
} break; default:
printk(KERN_WARNING "hvsi%i: unknown HVSI control packet: ",
hp->index);
dump_packet(packet); break;
}
}
staticvoid hvsi_insert_chars(struct hvsi_struct *hp, constchar *buf, int len)
{ int i;
for (i=0; i < len; i++) { char c = buf[i]; #ifdef CONFIG_MAGIC_SYSRQ if (c == '\0') {
hp->sysrq = 1; continue;
} elseif (hp->sysrq) {
handle_sysrq(c);
hp->sysrq = 0; continue;
} #endif/* CONFIG_MAGIC_SYSRQ */
tty_insert_flip_char(&hp->port, c, 0);
}
}
/* * We could get 252 bytes of data at once here. But the tty layer only * throttles us at TTY_THRESHOLD_THROTTLE (128) bytes, so we could overflow * it. Accordingly we won't send more than 128 bytes at a time to the flip * buffer, which will give the tty buffer a chance to throttle us. Should the * value of TTY_THRESHOLD_THROTTLE change in n_tty.c, this code should be * revisited.
*/ #define TTY_THRESHOLD_THROTTLE 128 staticbool hvsi_recv_data(struct hvsi_struct *hp, const uint8_t *packet)
{ conststruct hvsi_header *header = (conststruct hvsi_header *)packet; const uint8_t *data = packet + sizeof(struct hvsi_header); int datalen = header->len - sizeof(struct hvsi_header); int overflow = datalen - TTY_THRESHOLD_THROTTLE;
if (overflow > 0) { /* * we still have more data to deliver, so we need to save off the * overflow and send it later
*/
pr_debug("%s: deferring overflow\n", __func__);
memcpy(hp->throttle_buf, data + TTY_THRESHOLD_THROTTLE, overflow);
hp->n_throttle = overflow;
}
returntrue;
}
/* * Returns true/false indicating data successfully read from hypervisor. * Used both to get packets for tty connections and to advance the state * machine during console handshaking (in which case tty = NULL and we ignore * incoming data).
*/ staticint hvsi_load_chunk(struct hvsi_struct *hp, struct tty_struct *tty, struct hvsi_struct **handshake)
{
uint8_t *packet = hp->inbuf; int chunklen; bool flip = false;
if (!is_header(packet)) {
printk(KERN_ERR "hvsi%i: got malformed packet\n", hp->index); /* skip bytes until we find a header or run out of data */ while ((packet < hp->inbuf_end) && (!is_header(packet)))
packet++; continue;
}
/* * must get all pending data because we only get an irq on empty->non-empty * transition
*/ static irqreturn_t hvsi_interrupt(int irq, void *arg)
{ struct hvsi_struct *hp = (struct hvsi_struct *)arg; struct hvsi_struct *handshake; struct tty_struct *tty; unsignedlong flags; int again = 1;
pr_debug("%s\n", __func__);
tty = tty_port_tty_get(&hp->port);
while (again) {
spin_lock_irqsave(&hp->lock, flags);
again = hvsi_load_chunk(hp, tty, &handshake);
spin_unlock_irqrestore(&hp->lock, flags);
if (handshake) {
pr_debug("hvsi%i: attempting re-handshake\n", handshake->index);
schedule_work(&handshake->handshaker);
}
}
spin_lock_irqsave(&hp->lock, flags); if (tty && hp->n_throttle && !tty_throttled(tty)) { /* we weren't hung up and we weren't throttled, so we can
* deliver the rest now */
hvsi_send_overflow(hp);
tty_flip_buffer_push(&hp->port);
}
spin_unlock_irqrestore(&hp->lock, flags);
tty_kref_put(tty);
return IRQ_HANDLED;
}
/* for boot console, before the irq handler is running */ staticint __init poll_for_state(struct hvsi_struct *hp, int state)
{ unsignedlong end_jiffies = jiffies + HVSI_TIMEOUT;
for (;;) {
hvsi_interrupt(hp->virq, (void *)hp); /* get pending data */
if (hp->state == state) return 0;
mdelay(5); if (time_after(jiffies, end_jiffies)) return -EIO;
}
}
/* wait for irq handler to change our state */ staticint wait_for_state(struct hvsi_struct *hp, int state)
{ int ret = 0;
if (!wait_event_timeout(hp->stateq, (hp->state == state), HVSI_TIMEOUT))
ret = -EIO;
/* note that we can only set DTR */ staticint hvsi_set_mctrl(struct hvsi_struct *hp, uint16_t mctrl)
{ struct hvsi_control packet __ALIGNED__; int wrote;
while (time_before(end_jiffies, jiffies)) if (0 == hvsi_read(hp, buf, HVSI_MAX_READ)) break;
}
staticint hvsi_handshake(struct hvsi_struct *hp)
{ int ret;
/* * We could have a CLOSE or other data waiting for us before we even try * to open; try to throw it all away so we don't get confused. (CLOSE * is the first message sent up the pipe when the FSP comes online. We * need to distinguish between "it came up a while ago and we're the first * user" and "it was just reset before it saw our handshake packet".)
*/
hvsi_drain_input(hp);
set_state(hp, HVSI_WAIT_FOR_VER_RESPONSE);
ret = hvsi_query(hp, VSV_SEND_VERSION_NUMBER); if (ret < 0) {
printk(KERN_ERR "hvsi%i: couldn't send version query\n", hp->index); return ret;
}
ret = hvsi_wait(hp, HVSI_OPEN); if (ret < 0) return ret;
printk(KERN_ERR "hvsi%i: re-handshaking failed\n", hp->index); if (is_console(hp)) { /* * ttys will re-attempt the handshake via hvsi_open, but * the console will not.
*/
printk(KERN_ERR "hvsi%i: lost console!\n", hp->index);
}
}
staticint hvsi_put_chars(struct hvsi_struct *hp, constchar *buf, int count)
{ struct hvsi_data packet __ALIGNED__; int ret;
ret = hvc_put_chars(hp->vtermno, (char *)&packet, packet.hdr.len); if (ret == packet.hdr.len) { /* return the number of chars written, not the packet length */ return count;
} return ret; /* return any errors */
}
/* 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);
}
} elseif (hp->port.count < 0)
printk(KERN_ERR "hvsi_close %lu: oops, count is %d\n",
hp - hvsi_ports, hp->port.count);
if (start_j == 0)
start_j = jiffies; #endif/* DEBUG */
spin_lock_irqsave(&hp->lock, flags);
pr_debug("%s: %i chars in buffer\n", __func__, 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;
}
pr_debug("%s: %i chars in buffer\n", __func__, 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", __func__); 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(tty) > 0)) {
size_t chunksize = min_t(size_t, count, hvsi_write_room(tty));
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 %zu, only wrote %zu\n", __func__,
origcount, total);
return total;
}
/* * I have never seen throttle or unthrottle called, so this little throttle * buffering scheme may or may not work.
*/ staticvoid hvsi_throttle(struct tty_struct *tty)
{ struct hvsi_struct *hp = tty->driver_data;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.