root/usr/src/uts/common/io/fibre-channel/fca/emlxs/emlxs_msg.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at
 * http://www.opensource.org/licenses/cddl1.txt.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright (c) 2004-2012 Emulex. All rights reserved.
 * Use is subject to license terms.
 */

#define DEF_MSG_STRUCT  /* Needed for emlxs_messages.h in emlxs_msg.h */
#include <emlxs.h>

uint32_t emlxs_log_size         = 2048;
uint32_t emlxs_log_debugs       = 0x7FFFFFFF;
uint32_t emlxs_log_notices      = 0xFFFFFFFF;
uint32_t emlxs_log_warnings     = 0xFFFFFFFF;
uint32_t emlxs_log_errors       = 0xFFFFFFFF;

static uint32_t emlxs_msg_log_check(emlxs_port_t *port, emlxs_msg_t *msg);
static uint32_t emlxs_msg_print_check(emlxs_port_t *port, emlxs_msg_t *msg);
static void     emlxs_msg_sprintf(char *buffer, emlxs_msg_entry_t *entry);


uint32_t
emlxs_msg_log_create(emlxs_hba_t *hba)
{
        emlxs_msg_log_t *log = &LOG;
        uint32_t size = sizeof (emlxs_msg_entry_t) * emlxs_log_size;
        ddi_iblock_cookie_t iblock;

        /* Check if log is already created */
        if (log->entry) {
                cmn_err(CE_WARN, "?%s%d: message log already created. log=%p",
                    DRIVER_NAME, hba->ddiinst, (void *)log);
                return (0);
        }

        /* Clear the log */
        bzero(log, sizeof (emlxs_msg_log_t));

        /* Allocate the memory needed for the log file */
        log->entry = (emlxs_msg_entry_t *)kmem_zalloc(size, KM_SLEEP);

        /* Initialize */
        log->size = emlxs_log_size;
        log->instance = hba->ddiinst;
        log->start_time = emlxs_device.log_timestamp;

        if (!(hba->intr_flags & EMLXS_MSI_ENABLED)) {
                /* Get the current interrupt block cookie */
                (void) ddi_get_iblock_cookie(hba->dip, (uint_t)EMLXS_INUMBER,
                    &iblock);

                /* Create the log mutex lock */
                mutex_init(&log->lock, NULL, MUTEX_DRIVER, (void *)iblock);
        }
#ifdef  MSI_SUPPORT
        else {
                /* Create the temporary log mutex lock */
                mutex_init(&log->lock, NULL, MUTEX_DRIVER, NULL);
        }
#endif

        return (1);

} /* emlxs_msg_log_create() */


void
emlxs_msg_lock_reinit(emlxs_hba_t *hba)
{
        emlxs_msg_log_t *log = &LOG;

        /* Check if log is already destroyed */
        if (!log->entry) {
                cmn_err(CE_WARN,
                    "?%s%d: message log already destroyed. log=%p",
                    DRIVER_NAME, hba->ddiinst, (void *)log);

                return;
        }

        /* Destroy the temporary lock */
        mutex_destroy(&log->lock);

        /* Re-create the log mutex lock */
        mutex_init(&log->lock, NULL, MUTEX_DRIVER, DDI_INTR_PRI(hba->intr_arg));

        return;

} /* emlxs_msg_lock_reinit() */

void
emlxs_msg_log_destroy(emlxs_hba_t *hba)
{
        emlxs_msg_log_t *log = &LOG;
        uint32_t size;

        /* Check if log is already destroyed */
        if (!log->entry) {
                cmn_err(CE_WARN,
                    "?%s%d: message log already destroyed. log=%p",
                    DRIVER_NAME, hba->ddiinst, (void *)log);

                return;
        }

        /* Destroy the lock */
        mutex_destroy(&log->lock);

        /* Free the log buffer */
        size = sizeof (emlxs_msg_entry_t) * log->size;
        kmem_free(log->entry, size);

        /* Clear the log */
        bzero(log, sizeof (emlxs_msg_log_t));

        return;

} /* emlxs_msg_log_destroy() */


uint32_t
emlxs_msg_log(emlxs_port_t *port, const uint32_t fileno, const uint32_t line,
    emlxs_msg_t *msg, char *buffer)
{
        emlxs_hba_t *hba = HBA;
        emlxs_msg_entry_t *entry;
        emlxs_msg_entry_t *entry2;
        clock_t time;
        emlxs_msg_log_t *log;
        uint32_t last;
        emlxs_msg_t *msg2;

        /* Get the log file for this instance */
        log = &LOG;

        /* Check if log is initialized */
        if (log->entry == NULL) {
                return (0);
        }

        mutex_enter(&log->lock);

        /* Get the pointer to the last log entry */
        if (log->next == 0) {
                last = log->size - 1;
        } else {
                last = log->next - 1;
        }
        entry = &log->entry[last];

        /* Check if this matches the last message */
        if ((entry->instance == log->instance) &&
            (entry->vpi == port->vpi) &&
            (entry->fileno == fileno) &&
            (entry->line == line) &&
            (entry->msg == msg) &&
            (strcmp(entry->buffer, buffer) == 0)) {
                /* If the same message is being logged then increment */
                log->repeat++;

                mutex_exit(&log->lock);

                return (0);
        } else if (log->repeat) {
                /* Get the pointer to the next log entry */
                entry2 = &log->entry[log->next];

                /* Increment and check the next entry index */
                if (++(log->next) >= log->size) {
                        log->next = 0;
                }

                switch (entry->msg->level) {
                case EMLXS_NOTICE:
                        msg2 = &emlxs_notice_msg;
                        break;

                case EMLXS_WARNING:
                        msg2 = &emlxs_warning_msg;
                        break;

                case EMLXS_ERROR:
                        msg2 = &emlxs_error_msg;
                        break;

                case EMLXS_PANIC:
                        msg2 = &emlxs_panic_msg;
                        break;

                case EMLXS_DEBUG:
                default:
                        msg2 = &emlxs_debug_msg;
                        break;
                }

                /* Initialize */
                entry2->id = log->count++;
                entry2->fileno = entry->fileno;
                entry2->line = entry->line;
                entry2->msg = msg2;
                entry2->instance = log->instance;
                entry2->vpi = port->vpi;

                /* Save the additional info buffer */
                (void) snprintf(entry2->buffer, MAX_LOG_INFO_LENGTH,
                    "Last message repeated %d time(s).",
                    log->repeat);

                /* Set the entry time stamp */
                (void) drv_getparm(LBOLT, &time);
                entry2->time = time - log->start_time;

                gethrestime(&entry2->id_time);

                log->repeat = 0;
        }

        /* Get the pointer to the next log entry */
        entry = &log->entry[log->next];

        /* Increment and check the next entry index */
        if (++(log->next) >= log->size) {
                log->next = 0;
        }

        /* Initialize */
        entry->id = log->count++;
        entry->fileno = fileno;
        entry->line = line;
        entry->msg = msg;
        entry->instance = log->instance;
        entry->vpi = port->vpi;

        /* Save the additional info buffer */
        (void) strncpy(entry->buffer, buffer, (MAX_LOG_INFO_LENGTH - 1));
        entry->buffer[MAX_LOG_INFO_LENGTH - 1] = 0;

        /* Set the entry time stamp */
        (void) drv_getparm(LBOLT, &time);
        entry->time = time - log->start_time;

        gethrestime(&entry->id_time);

        mutex_exit(&log->lock);

        return (0);

} /* emlxs_msg_log() */


/*ARGSUSED*/
static uint32_t
emlxs_msg_log_check(emlxs_port_t *port, emlxs_msg_t *msg)
{

        switch (msg->level) {
        case EMLXS_DEBUG:
                if (msg->mask & emlxs_log_debugs) {
                        return (1);
                }
                break;

        case EMLXS_NOTICE:
                if (msg->mask & emlxs_log_notices) {
                        return (1);
                }
                break;

        case EMLXS_WARNING:
                if (msg->mask & emlxs_log_warnings) {
                        return (1);
                }
                break;

        case EMLXS_ERROR:
                if (msg->mask & emlxs_log_errors) {
                        return (1);
                }
                break;

        case EMLXS_PANIC:
                return (1);
        }

        return (0);

} /* emlxs_msg_log_check() */


static uint32_t
emlxs_msg_print_check(emlxs_port_t *port, emlxs_msg_t *msg)
{
        emlxs_hba_t *hba = HBA;
        emlxs_config_t *cfg;
        uint32_t rval = 0;

        cfg = &CFG;

        switch (msg->level) {
        case EMLXS_DEBUG:
                if (msg->mask & cfg[CFG_CONSOLE_DEBUGS].current) {
                        rval |= 2;
                }

                if (msg->mask & cfg[CFG_LOG_DEBUGS].current) {
                        rval |= 1;
                }

                break;

        case EMLXS_NOTICE:
                if (msg->mask & cfg[CFG_CONSOLE_NOTICES].current) {
                        rval |= 2;
                }

                if (msg->mask & cfg[CFG_LOG_NOTICES].current) {
                        rval |= 1;
                }

                break;

        case EMLXS_WARNING:
                if (msg->mask & cfg[CFG_CONSOLE_WARNINGS].current) {
                        rval |= 2;
                }

                if (msg->mask & cfg[CFG_LOG_WARNINGS].current) {
                        rval |= 1;
                }

                break;

        case EMLXS_ERROR:
                if (msg->mask & cfg[CFG_CONSOLE_ERRORS].current) {
                        rval |= 2;
                }

                if (msg->mask & cfg[CFG_LOG_ERRORS].current) {
                        rval |= 1;
                }
                break;

        case EMLXS_PANIC:
        default:
                rval |= 1;

        }

        return (rval);

} /* emlxs_msg_print_check() */


void
emlxs_msg_printf(emlxs_port_t *port, const uint32_t fileno,
    const uint32_t line, emlxs_msg_t *msg,
    const char *fmt, ...)
{
        emlxs_hba_t *hba = HBA;
        va_list valist;
        char va_str[256];
        char msg_str[512];
        char *level;
        int32_t cmn_level;
        uint32_t rval;
        char driver[32];

        va_str[0] = 0;

        if (fmt) {
                va_start(valist, fmt);
                (void) vsnprintf(va_str, sizeof (va_str), fmt, valist);
                va_end(valist);
        }

#ifdef FMA_SUPPORT
        if (msg->fm_ereport_code) {
                emlxs_fm_ereport(hba, msg->fm_ereport_code);
        }

        if (msg->fm_impact_code) {
                emlxs_fm_service_impact(hba, msg->fm_impact_code);
        }
#endif  /* FMA_SUPPORT */

        /* Check if msg should be logged */
        if (emlxs_msg_log_check(port, msg)) {
                /* Log the message */
                if (emlxs_msg_log(port, fileno, line, msg, va_str)) {
                        return;
                }
        }

        /* Check if msg should be printed */
        if (rval = emlxs_msg_print_check(port, msg)) {
                cmn_level = CE_CONT;

                switch (msg->level) {
                case EMLXS_DEBUG:
                        level = "  DEBUG";
                        break;

                case EMLXS_NOTICE:
                        level = " NOTICE";
                        break;

                case EMLXS_WARNING:
                        level = "WARNING";
                        break;

                case EMLXS_ERROR:
                        level = "  ERROR";
                        break;

                case EMLXS_PANIC:
                        cmn_level = CE_PANIC;
                        level = "  PANIC";
                        break;

                default:
                        level = "UNKNOWN";
                        break;
                }

                if (port->vpi == 0) {
                        (void) snprintf(driver, sizeof (driver), "%s%d",
                            DRIVER_NAME, hba->ddiinst);
                } else {
                        (void) snprintf(driver, sizeof (driver), "%s%d.%d",
                            DRIVER_NAME, hba->ddiinst, port->vpi);
                }

                /* Generate the message string */
                if (msg->buffer[0] != 0) {
                        if (va_str[0] != 0) {
                                (void) snprintf(msg_str, sizeof (msg_str),
                                    "[%2X.%04X]%s:%7s:%4d: %s (%s)\n", fileno,
                                    line, driver, level, msg->id, msg->buffer,
                                    va_str);
                        } else {
                                (void) snprintf(msg_str, sizeof (msg_str),
                                    "[%2X.%04X]%s:%7s:%4d: %s\n",
                                    fileno, line, driver, level, msg->id,
                                    msg->buffer);
                        }
                } else {
                        if (va_str[0] != 0) {
                                (void) snprintf(msg_str, sizeof (msg_str),
                                    "[%2X.%04X]%s:%7s:%4d: (%s)\n", fileno,
                                    line, driver, level, msg->id, va_str);
                        } else {
                                (void) snprintf(msg_str, sizeof (msg_str),
                                    "[%2X.%04X]%s:%7s:%4d\n",
                                    fileno, line, driver, level, msg->id);
                        }
                }

                switch (rval) {
                case 1: /* MESSAGE LOG ONLY */
                        /* Message log & console, if system booted in */
                        /* verbose mode (CE_CONT only) */
                        cmn_err(cmn_level, "?%s", msg_str);
                        break;

                case 2: /* CONSOLE ONLY */
                        cmn_err(cmn_level, "^%s", msg_str);
                        break;

                case 3: /* CONSOLE AND MESSAGE LOG */
                        cmn_err(cmn_level, "%s", msg_str);
                        break;

                }

        }

        return;

} /* emlxs_msg_printf() */


uint32_t
emlxs_msg_log_get(emlxs_hba_t *hba, emlxs_log_req_t *req,
    emlxs_log_resp_t *resp)
{
        emlxs_msg_log_t *log;
        uint32_t first;
        uint32_t last;
        uint32_t count;
        uint32_t index;
        uint32_t i;
        char *resp_buf;

        log = &LOG;

        mutex_enter(&log->lock);

        /* Check if buffer is empty */
        if (log->count == 0) {
                /* If so, exit now */
                resp->first = 0;
                resp->last = 0;
                resp->count = 0;
                mutex_exit(&log->lock);

                return (1);
        }

        /* Get current log entry ranges */

        /* Get last entry id saved */
        last = log->count - 1;

        /* Check if request is out of current range */
        if (req->first > last) {
                /* if so, exit now */
                resp->first = last;
                resp->last = last;
                resp->count = 0;
                mutex_exit(&log->lock);

                return (0);
        }

        /* Get oldest entry id and its index */

        /* Check if buffer has already been filled once */
        if (log->count >= log->size) {
                first = log->count - log->size;
                index = log->next;
        } else {        /* Buffer not yet filled */

                first = 0;
                index = 0;
        }

        /* Check if requested first message is greater than actual. */
        /* If so, adjust for it.  */
        if (req->first > first) {
                /* Adjust entry index to first requested message */
                index += (req->first - first);
                if (index >= log->size) {
                        index -= log->size;
                }

                first = req->first;
        }

        /* Get the total number of messages available for return */
        count = last - first + 1;

        /* Check if requested count is less than actual.  If so, adjust it. */
        if (req->count < count) {
                count = req->count;
        }

        /* Fill in the response header */
        resp->count = count;
        resp->first = first;
        resp->last = last;

        /* Fill the response buffer */
        resp_buf = (char *)resp + sizeof (emlxs_log_resp_t);
        for (i = 0; i < count; i++) {
                emlxs_msg_sprintf(resp_buf, &log->entry[index]);

                /* Increment the response buffer */
                resp_buf += MAX_LOG_MSG_LENGTH;

                /* Increment index */
                if (++index >= log->size) {
                        index = 0;
                }
        }

        mutex_exit(&log->lock);

        return (1);

} /* emlxs_msg_log_get() */



static void
emlxs_msg_sprintf(char *buffer, emlxs_msg_entry_t *entry)
{
        char *level;
        emlxs_msg_t *msg;
        uint32_t secs;
        uint32_t hsecs;
        char buf[256];
        uint32_t buflen;
        char driver[32];

        msg = entry->msg;

        hsecs = (entry->time % 100);
        secs = entry->time / 100;

        switch (msg->level) {
        case EMLXS_DEBUG:
                level = "  DEBUG";
                break;

        case EMLXS_NOTICE:
                level = " NOTICE";
                break;

        case EMLXS_WARNING:
                level = "WARNING";
                break;

        case EMLXS_ERROR:
                level = "  ERROR";
                break;

        case EMLXS_PANIC:
                level = "  PANIC";
                break;

        default:
                level = "UNKNOWN";
                break;
        }

        if (entry->vpi == 0) {
                (void) snprintf(driver, sizeof (driver), "%s%d", DRIVER_NAME,
                    entry->instance);
        } else {
                (void) snprintf(driver, sizeof (driver), "%s%d.%d", DRIVER_NAME,
                    entry->instance, entry->vpi);
        }

        /* Generate the message string */
        if (msg->buffer[0] != 0) {
                if (entry->buffer[0] != 0) {
                        (void) snprintf(buf, sizeof (buf),
                            "%8d.%02d: %6d:[%2X.%04X]%s:%7s:%4d: %s (%s)\n",
                            secs, hsecs, entry->id, entry->fileno,
                            entry->line, driver, level, msg->id, msg->buffer,
                            entry->buffer);

                } else {
                        (void) snprintf(buf, sizeof (buf),
                            "%8d.%02d: %6d:[%2X.%04X]%s:%7s:%4d: %s\n", secs,
                            hsecs, entry->id, entry->fileno, entry->line,
                            driver, level, msg->id, msg->buffer);
                }
        } else {
                if (entry->buffer[0] != 0) {
                        (void) snprintf(buf, sizeof (buf),
                            "%8d.%02d: %6d:[%2X.%04X]%s:%7s:%4d: (%s)\n",
                            secs, hsecs, entry->id, entry->fileno,
                            entry->line, driver, level, msg->id,
                            entry->buffer);
                } else {
                        (void) snprintf(buf, sizeof (buf),
                            "%8d.%02d: %6d:[%2X.%04X]%s:%7s:%4d\n",
                            secs, hsecs, entry->id, entry->fileno,
                            entry->line, driver, level, msg->id);
                }
        }

        bzero(buffer, MAX_LOG_MSG_LENGTH);
        buflen = strlen(buf);

        if (buflen > (MAX_LOG_MSG_LENGTH - 1)) {
                (void) strncpy(buffer, buf, (MAX_LOG_MSG_LENGTH - 2));
                buffer[MAX_LOG_MSG_LENGTH - 2] = '\n';
        } else {
                (void) strncpy(buffer, buf, buflen);
        }

        return;

} /* emlxs_msg_sprintf() */