123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467 |
- /*
- * driver: reading from and writing to system console on S/390 via SCLP
- *
- * Copyright IBM Corp. 1999, 2009
- *
- * Author(s): Martin Peschke <mpeschke@de.ibm.com>
- * Martin Schwidefsky <schwidefsky@de.ibm.com>
- */
- #include <linux/kmod.h>
- #include <linux/types.h>
- #include <linux/err.h>
- #include <linux/string.h>
- #include <linux/spinlock.h>
- #include <linux/ctype.h>
- #include <asm/uaccess.h>
- #include "sclp.h"
- #include "sclp_rw.h"
- /*
- * The room for the SCCB (only for writing) is not equal to a pages size
- * (as it is specified as the maximum size in the SCLP documentation)
- * because of the additional data structure described above.
- */
- #define MAX_SCCB_ROOM (PAGE_SIZE - sizeof(struct sclp_buffer))
- static void sclp_rw_pm_event(struct sclp_register *reg,
- enum sclp_pm_event sclp_pm_event)
- {
- sclp_console_pm_event(sclp_pm_event);
- }
- /* Event type structure for write message and write priority message */
- static struct sclp_register sclp_rw_event = {
- .send_mask = EVTYP_MSG_MASK,
- .pm_event_fn = sclp_rw_pm_event,
- };
- /*
- * Setup a sclp write buffer. Gets a page as input (4K) and returns
- * a pointer to a struct sclp_buffer structure that is located at the
- * end of the input page. This reduces the buffer space by a few
- * bytes but simplifies things.
- */
- struct sclp_buffer *
- sclp_make_buffer(void *page, unsigned short columns, unsigned short htab)
- {
- struct sclp_buffer *buffer;
- struct sccb_header *sccb;
- sccb = (struct sccb_header *) page;
- /*
- * We keep the struct sclp_buffer structure at the end
- * of the sccb page.
- */
- buffer = ((struct sclp_buffer *) ((addr_t) sccb + PAGE_SIZE)) - 1;
- buffer->sccb = sccb;
- buffer->retry_count = 0;
- buffer->messages = 0;
- buffer->char_sum = 0;
- buffer->current_line = NULL;
- buffer->current_length = 0;
- buffer->columns = columns;
- buffer->htab = htab;
- /* initialize sccb */
- memset(sccb, 0, sizeof(struct sccb_header));
- sccb->length = sizeof(struct sccb_header);
- return buffer;
- }
- /*
- * Return a pointer to the original page that has been used to create
- * the buffer.
- */
- void *
- sclp_unmake_buffer(struct sclp_buffer *buffer)
- {
- return buffer->sccb;
- }
- /*
- * Initialize a new message the end of the provided buffer with
- * enough room for max_len characters. Return 0 on success.
- */
- static int
- sclp_initialize_mto(struct sclp_buffer *buffer, int max_len)
- {
- struct sccb_header *sccb;
- struct msg_buf *msg;
- struct mdb *mdb;
- struct go *go;
- struct mto *mto;
- int msg_size;
- /* max size of new message including message text */
- msg_size = sizeof(struct msg_buf) + max_len;
- /* check if current buffer sccb can contain the mto */
- sccb = buffer->sccb;
- if ((MAX_SCCB_ROOM - sccb->length) < msg_size)
- return -ENOMEM;
- msg = (struct msg_buf *)((addr_t) sccb + sccb->length);
- memset(msg, 0, sizeof(struct msg_buf));
- msg->header.length = sizeof(struct msg_buf);
- msg->header.type = EVTYP_MSG;
- mdb = &msg->mdb;
- mdb->header.length = sizeof(struct mdb);
- mdb->header.type = 1;
- mdb->header.tag = 0xD4C4C240; /* ebcdic "MDB " */
- mdb->header.revision_code = 1;
- go = &mdb->go;
- go->length = sizeof(struct go);
- go->type = 1;
- mto = &mdb->mto;
- mto->length = sizeof(struct mto);
- mto->type = 4; /* message text object */
- mto->line_type_flags = LNTPFLGS_ENDTEXT; /* end text */
- /* set pointer to first byte after struct mto. */
- buffer->current_msg = msg;
- buffer->current_line = (char *) (mto + 1);
- buffer->current_length = 0;
- return 0;
- }
- /*
- * Finalize message initialized by sclp_initialize_mto(),
- * updating the sizes of MTO, enclosing MDB, event buffer and SCCB.
- */
- static void
- sclp_finalize_mto(struct sclp_buffer *buffer)
- {
- struct sccb_header *sccb;
- struct msg_buf *msg;
- /*
- * update values of sizes
- * (SCCB, Event(Message) Buffer, Message Data Block)
- */
- sccb = buffer->sccb;
- msg = buffer->current_msg;
- msg->header.length += buffer->current_length;
- msg->mdb.header.length += buffer->current_length;
- msg->mdb.mto.length += buffer->current_length;
- sccb->length += msg->header.length;
- /*
- * count number of buffered messages (= number of Message Text
- * Objects) and number of buffered characters
- * for the SCCB currently used for buffering and at all
- */
- buffer->messages++;
- buffer->char_sum += buffer->current_length;
- buffer->current_line = NULL;
- buffer->current_length = 0;
- buffer->current_msg = NULL;
- }
- /*
- * processing of a message including escape characters,
- * returns number of characters written to the output sccb
- * ("processed" means that is not guaranteed that the character have already
- * been sent to the SCLP but that it will be done at least next time the SCLP
- * is not busy)
- */
- int
- sclp_write(struct sclp_buffer *buffer, const unsigned char *msg, int count)
- {
- int spaces, i_msg;
- int rc;
- /*
- * parse msg for escape sequences (\t,\v ...) and put formated
- * msg into an mto (created by sclp_initialize_mto).
- *
- * We have to do this work ourselfs because there is no support for
- * these characters on the native machine and only partial support
- * under VM (Why does VM interpret \n but the native machine doesn't ?)
- *
- * Depending on i/o-control setting the message is always written
- * immediately or we wait for a final new line maybe coming with the
- * next message. Besides we avoid a buffer overrun by writing its
- * content.
- *
- * RESTRICTIONS:
- *
- * \r and \b work within one line because we are not able to modify
- * previous output that have already been accepted by the SCLP.
- *
- * \t combined with following \r is not correctly represented because
- * \t is expanded to some spaces but \r does not know about a
- * previous \t and decreases the current position by one column.
- * This is in order to a slim and quick implementation.
- */
- for (i_msg = 0; i_msg < count; i_msg++) {
- switch (msg[i_msg]) {
- case '\n': /* new line, line feed (ASCII) */
- /* check if new mto needs to be created */
- if (buffer->current_line == NULL) {
- rc = sclp_initialize_mto(buffer, 0);
- if (rc)
- return i_msg;
- }
- sclp_finalize_mto(buffer);
- break;
- case '\a': /* bell, one for several times */
- /* set SCLP sound alarm bit in General Object */
- if (buffer->current_line == NULL) {
- rc = sclp_initialize_mto(buffer,
- buffer->columns);
- if (rc)
- return i_msg;
- }
- buffer->current_msg->mdb.go.general_msg_flags |=
- GNRLMSGFLGS_SNDALRM;
- break;
- case '\t': /* horizontal tabulator */
- /* check if new mto needs to be created */
- if (buffer->current_line == NULL) {
- rc = sclp_initialize_mto(buffer,
- buffer->columns);
- if (rc)
- return i_msg;
- }
- /* "go to (next htab-boundary + 1, same line)" */
- do {
- if (buffer->current_length >= buffer->columns)
- break;
- /* ok, add a blank */
- *buffer->current_line++ = 0x40;
- buffer->current_length++;
- } while (buffer->current_length % buffer->htab);
- break;
- case '\f': /* form feed */
- case '\v': /* vertical tabulator */
- /* "go to (actual column, actual line + 1)" */
- /* = new line, leading spaces */
- if (buffer->current_line != NULL) {
- spaces = buffer->current_length;
- sclp_finalize_mto(buffer);
- rc = sclp_initialize_mto(buffer,
- buffer->columns);
- if (rc)
- return i_msg;
- memset(buffer->current_line, 0x40, spaces);
- buffer->current_line += spaces;
- buffer->current_length = spaces;
- } else {
- /* one an empty line this is the same as \n */
- rc = sclp_initialize_mto(buffer,
- buffer->columns);
- if (rc)
- return i_msg;
- sclp_finalize_mto(buffer);
- }
- break;
- case '\b': /* backspace */
- /* "go to (actual column - 1, actual line)" */
- /* decrement counter indicating position, */
- /* do not remove last character */
- if (buffer->current_line != NULL &&
- buffer->current_length > 0) {
- buffer->current_length--;
- buffer->current_line--;
- }
- break;
- case 0x00: /* end of string */
- /* transfer current line to SCCB */
- if (buffer->current_line != NULL)
- sclp_finalize_mto(buffer);
- /* skip the rest of the message including the 0 byte */
- i_msg = count - 1;
- break;
- default: /* no escape character */
- /* do not output unprintable characters */
- if (!isprint(msg[i_msg]))
- break;
- /* check if new mto needs to be created */
- if (buffer->current_line == NULL) {
- rc = sclp_initialize_mto(buffer,
- buffer->columns);
- if (rc)
- return i_msg;
- }
- *buffer->current_line++ = sclp_ascebc(msg[i_msg]);
- buffer->current_length++;
- break;
- }
- /* check if current mto is full */
- if (buffer->current_line != NULL &&
- buffer->current_length >= buffer->columns)
- sclp_finalize_mto(buffer);
- }
- /* return number of processed characters */
- return i_msg;
- }
- /*
- * Return the number of free bytes in the sccb
- */
- int
- sclp_buffer_space(struct sclp_buffer *buffer)
- {
- struct sccb_header *sccb;
- int count;
- sccb = buffer->sccb;
- count = MAX_SCCB_ROOM - sccb->length;
- if (buffer->current_line != NULL)
- count -= sizeof(struct msg_buf) + buffer->current_length;
- return count;
- }
- /*
- * Return number of characters in buffer
- */
- int
- sclp_chars_in_buffer(struct sclp_buffer *buffer)
- {
- int count;
- count = buffer->char_sum;
- if (buffer->current_line != NULL)
- count += buffer->current_length;
- return count;
- }
- /*
- * sets or provides some values that influence the drivers behaviour
- */
- void
- sclp_set_columns(struct sclp_buffer *buffer, unsigned short columns)
- {
- buffer->columns = columns;
- if (buffer->current_line != NULL &&
- buffer->current_length > buffer->columns)
- sclp_finalize_mto(buffer);
- }
- void
- sclp_set_htab(struct sclp_buffer *buffer, unsigned short htab)
- {
- buffer->htab = htab;
- }
- /*
- * called by sclp_console_init and/or sclp_tty_init
- */
- int
- sclp_rw_init(void)
- {
- static int init_done = 0;
- int rc;
- if (init_done)
- return 0;
- rc = sclp_register(&sclp_rw_event);
- if (rc == 0)
- init_done = 1;
- return rc;
- }
- #define SCLP_BUFFER_MAX_RETRY 1
- /*
- * second half of Write Event Data-function that has to be done after
- * interruption indicating completion of Service Call.
- */
- static void
- sclp_writedata_callback(struct sclp_req *request, void *data)
- {
- int rc;
- struct sclp_buffer *buffer;
- struct sccb_header *sccb;
- buffer = (struct sclp_buffer *) data;
- sccb = buffer->sccb;
- if (request->status == SCLP_REQ_FAILED) {
- if (buffer->callback != NULL)
- buffer->callback(buffer, -EIO);
- return;
- }
- /* check SCLP response code and choose suitable action */
- switch (sccb->response_code) {
- case 0x0020 :
- /* Normal completion, buffer processed, message(s) sent */
- rc = 0;
- break;
- case 0x0340: /* Contained SCLP equipment check */
- if (++buffer->retry_count > SCLP_BUFFER_MAX_RETRY) {
- rc = -EIO;
- break;
- }
- /* remove processed buffers and requeue rest */
- if (sclp_remove_processed((struct sccb_header *) sccb) > 0) {
- /* not all buffers were processed */
- sccb->response_code = 0x0000;
- buffer->request.status = SCLP_REQ_FILLED;
- rc = sclp_add_request(request);
- if (rc == 0)
- return;
- } else
- rc = 0;
- break;
- case 0x0040: /* SCLP equipment check */
- case 0x05f0: /* Target resource in improper state */
- if (++buffer->retry_count > SCLP_BUFFER_MAX_RETRY) {
- rc = -EIO;
- break;
- }
- /* retry request */
- sccb->response_code = 0x0000;
- buffer->request.status = SCLP_REQ_FILLED;
- rc = sclp_add_request(request);
- if (rc == 0)
- return;
- break;
- default:
- if (sccb->response_code == 0x71f0)
- rc = -ENOMEM;
- else
- rc = -EINVAL;
- break;
- }
- if (buffer->callback != NULL)
- buffer->callback(buffer, rc);
- }
- /*
- * Setup the request structure in the struct sclp_buffer to do SCLP Write
- * Event Data and pass the request to the core SCLP loop. Return zero on
- * success, non-zero otherwise.
- */
- int
- sclp_emit_buffer(struct sclp_buffer *buffer,
- void (*callback)(struct sclp_buffer *, int))
- {
- /* add current line if there is one */
- if (buffer->current_line != NULL)
- sclp_finalize_mto(buffer);
- /* Are there messages in the output buffer ? */
- if (buffer->messages == 0)
- return -EIO;
- buffer->request.command = SCLP_CMDW_WRITE_EVENT_DATA;
- buffer->request.status = SCLP_REQ_FILLED;
- buffer->request.callback = sclp_writedata_callback;
- buffer->request.callback_data = buffer;
- buffer->request.sccb = buffer->sccb;
- buffer->callback = callback;
- return sclp_add_request(&buffer->request);
- }
|