diff options
-rw-r--r-- | sys/dev/ips/ips.c | 66 | ||||
-rw-r--r-- | sys/dev/ips/ips.h | 59 | ||||
-rw-r--r-- | sys/dev/ips/ips_commands.c | 90 |
3 files changed, 211 insertions, 4 deletions
diff --git a/sys/dev/ips/ips.c b/sys/dev/ips/ips.c index 0cbcf6b..165ebb0 100644 --- a/sys/dev/ips/ips.c +++ b/sys/dev/ips/ips.c @@ -46,6 +46,25 @@ static struct cdevsw ips_cdevsw = { .d_maj = IPS_CDEV_MAJOR, }; +static const char* ips_adapter_name[] = { + "N/A", + "ServeRAID (copperhead)", + "ServeRAID II (copperhead refresh)", + "ServeRAID onboard (copperhead)", + "ServeRAID onboard (copperhead)", + "ServeRAID 3H (clarinet)", + "ServeRAID 3L (clarinet lite)", + "ServeRAID 4H (trombone)", + "ServeRAID 4M (morpheus)", + "ServeRAID 4L (morpheus lite)", + "ServeRAID 4Mx (neo)", + "ServeRAID 4Lx (neo lite)", + "ServeRAID 5i II (sarasota)", + "ServeRAID 5i (sarasota)", + "ServeRAID 6M (marco)", + "ServeRAID 6i (sebring)" +}; + static int ips_open(dev_t dev, int flags, int fmt, struct thread *td) { @@ -240,12 +259,44 @@ void ips_insert_free_cmd(ips_softc_t *sc, ips_command_t *command) if(!(sc->state & IPS_TIMEOUT)) ips_run_waiting_command(sc); } +static const char* ips_diskdev_statename(u_int8_t state) +{ + static char statebuf[20]; + switch(state){ + case IPS_LD_OFFLINE: + return("OFFLINE"); + break; + case IPS_LD_OKAY: + return("OK"); + break; + case IPS_LD_DEGRADED: + return("DEGRADED"); + break; + case IPS_LD_FREE: + return("FREE"); + break; + case IPS_LD_SYS: + return("SYS"); + break; + case IPS_LD_CRS: + return("CRS"); + break; + } + sprintf(statebuf,"UNKNOWN(0x%02x)", state); + return(statebuf); +} static int ips_diskdev_init(ips_softc_t *sc) { int i; for(i=0; i < IPS_MAX_NUM_DRIVES; i++){ - if(sc->drives[i].state & IPS_LD_OKAY){ + if(sc->drives[i].state == IPS_LD_FREE) continue; + device_printf(sc->dev, "Logical Drive %d: RAID%d sectors: %u, state %s\n", + i, sc->drives[i].raid_lvl, + sc->drives[i].sector_count, + ips_diskdev_statename(sc->drives[i].state)); + if(sc->drives[i].state == IPS_LD_OKAY || + sc->drives[i].state == IPS_LD_DEGRADED){ sc->diskdev[i] = device_add_child(sc->dev, NULL, -1); device_set_ivars(sc->diskdev[i],(void *)(uintptr_t) i); } @@ -369,15 +420,26 @@ int ips_adapter_init(ips_softc_t *sc) goto error; mtx_init(&sc->cmd_mtx, "ips command mutex", NULL, MTX_DEF); + + /* initialize ffdc values */ + microtime(&sc->ffdc_resettime); + sc->ffdc_resetcount = 1; + if ((i = ips_ffdc_reset(sc)) != 0) { + device_printf(sc->dev, "failed to send ffdc reset to device (%d)\n", i); + goto error; + } if ((i = ips_get_adapter_info(sc)) != 0) { device_printf(sc->dev, "failed to get adapter configuration data from device (%d)\n", i); goto error; } + ips_update_nvram(sc); /* no error check as failure doesn't matter */ + if(sc->adapter_type > 0 && sc->adapter_type <= IPS_ADAPTER_MAX_T){ + device_printf(sc->dev, "adapter type: %s\n", ips_adapter_name[sc->adapter_type]); + } if ((i = ips_get_drive_info(sc)) != 0) { device_printf(sc->dev, "failed to get drive configuration data from device (%d)\n", i); goto error; } - ips_update_nvram(sc); /* no error check as failure doesn't matter */ ips_cmdqueue_free(sc); if(sc->adapter_info.max_concurrent_cmds) diff --git a/sys/dev/ips/ips.h b/sys/dev/ips/ips.h index 3d0f19a..f644df6 100644 --- a/sys/dev/ips/ips.h +++ b/sys/dev/ips/ips.h @@ -39,6 +39,7 @@ #include <sys/bio.h> #include <sys/malloc.h> #include <sys/mutex.h> +#include <sys/time.h> #include <machine/bus_memio.h> #include <machine/bus.h> @@ -82,6 +83,7 @@ #define IPS_LD_OFFLINE 0x02 #define IPS_LD_OKAY 0x03 +#define IPS_LD_DEGRADED 0x04 #define IPS_LD_FREE 0x00 #define IPS_LD_SYS 0x06 #define IPS_LD_CRS 0x24 @@ -140,14 +142,47 @@ #define IPS_SG_READ_CMD 0x82 #define IPS_SG_WRITE_CMD 0x83 #define IPS_RW_NVRAM_CMD 0xBC +#define IPS_FFDC_CMD 0xD7 /* error information returned by the adapter */ #define IPS_MIN_ERROR 0x02 #define IPS_ERROR_STATUS 0x13000200 /* ahh, magic numbers */ -#define IPS_OS_FREEBSD 10 +#define IPS_OS_FREEBSD 8 #define IPS_VERSION_MAJOR "0.90" -#define IPS_VERSION_MINOR ".00" +#define IPS_VERSION_MINOR ".10" + +/* Adapter Types */ +#define IPS_ADAPTER_COPPERHEAD 0x01 +#define IPS_ADAPTER_COPPERHEAD2 0x02 +#define IPS_ADAPTER_COPPERHEADOB1 0x03 +#define IPS_ADAPTER_COPPERHEADOB2 0x04 +#define IPS_ADAPTER_CLARINET 0x05 +#define IPS_ADAPTER_CLARINETLITE 0x06 +#define IPS_ADAPTER_TROMBONE 0x07 +#define IPS_ADAPTER_MORPHEUS 0x08 +#define IPS_ADAPTER_MORPHEUSLITE 0x09 +#define IPS_ADAPTER_NEO 0x0A +#define IPS_ADAPTER_NEOLITE 0x0B +#define IPS_ADAPTER_SARASOTA2 0x0C +#define IPS_ADAPTER_SARASOTA1 0x0D +#define IPS_ADAPTER_MARCO 0x0E +#define IPS_ADAPTER_SEBRING 0x0F +#define IPS_ADAPTER_MAX_T IPS_ADAPTER_SEBRING + +/* values for ffdc_settime (from gmtime) */ +#define IPS_SECSPERMIN 60 +#define IPS_MINSPERHOUR 60 +#define IPS_HOURSPERDAY 24 +#define IPS_DAYSPERWEEK 7 +#define IPS_DAYSPERNYEAR 365 +#define IPS_DAYSPERLYEAR 366 +#define IPS_SECSPERHOUR (IPS_SECSPERMIN * IPS_MINSPERHOUR) +#define IPS_SECSPERDAY ((long) IPS_SECSPERHOUR * IPS_HOURSPERDAY) +#define IPS_MONSPERYEAR 12 +#define IPS_EPOCH_YEAR 1970 +#define IPS_LEAPS_THRU_END_OF(y) ((y) / 4 - (y) / 100 + (y) / 400) +#define ips_isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0)) /* * IPS MACROS @@ -232,6 +267,22 @@ typedef struct{ u_int32_t reserve3; } __attribute__((packed)) ips_adapter_info_cmd; +typedef struct{ + u_int8_t command; + u_int8_t id; + u_int8_t reset_count; + u_int8_t reset_type; + u_int8_t second; + u_int8_t minute; + u_int8_t hour; + u_int8_t day; + u_int8_t reserve1[4]; + u_int8_t month; + u_int8_t yearH; + u_int8_t yearL; + u_int8_t reserve2; +} __attribute__((packed)) ips_adapter_ffdc_cmd; + typedef union{ ips_generic_cmd generic_cmd; ips_drive_cmd drive_cmd; @@ -358,10 +409,13 @@ typedef struct ips_softc{ device_t dev; dev_t device_file; struct callout_handle timer; + u_int16_t adapter_type; ips_adapter_info_t adapter_info; device_t diskdev[IPS_MAX_NUM_DRIVES]; ips_drive_t drives[IPS_MAX_NUM_DRIVES]; u_int8_t drivecount; + u_int16_t ffdc_resetcount; + struct timeval ffdc_resettime; u_int8_t next_drive; u_int8_t max_cmds; volatile u_int8_t used_commands; @@ -387,6 +441,7 @@ extern int ips_flush_cache(ips_softc_t *sc); extern void ips_start_io_request(ips_softc_t *sc, struct bio *iobuf); extern int ips_get_drive_info(ips_softc_t *sc); extern int ips_get_adapter_info(ips_softc_t *sc); +extern int ips_ffdc_reset(ips_softc_t *sc); extern int ips_update_nvram(ips_softc_t *sc); extern int ips_clear_adapter(ips_softc_t *sc); diff --git a/sys/dev/ips/ips_commands.c b/sys/dev/ips/ips_commands.c index 67f1655..cb0dd76 100644 --- a/sys/dev/ips/ips_commands.c +++ b/sys/dev/ips/ips_commands.c @@ -434,6 +434,93 @@ int ips_flush_cache(ips_softc_t *sc) return 0; } +/* Simplified localtime to provide timevalues for ffdc. + * Taken from libc/stdtime/localtime.c + */ +void static ips_ffdc_settime(ips_adapter_ffdc_cmd *command, time_t sctime) +{ + long days, rem, y; + int yleap, *ip, month; + int year_lengths[2] = { IPS_DAYSPERNYEAR, IPS_DAYSPERLYEAR }; + int mon_lengths[2][IPS_MONSPERYEAR] = { + { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, + { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } + }; + + days = sctime / IPS_SECSPERDAY; + rem = sctime % IPS_SECSPERDAY; + + command->hour = rem / IPS_SECSPERHOUR; + rem = rem % IPS_SECSPERHOUR; + + command->minute = rem / IPS_SECSPERMIN; + command->second = rem % IPS_SECSPERMIN; + + y = IPS_EPOCH_YEAR; + while (days < 0 || days >= (long) year_lengths[yleap = ips_isleap(y)]) { + long newy; + + newy = y + days / IPS_DAYSPERNYEAR; + if (days < 0) + --newy; + days -= (newy - y) * IPS_DAYSPERNYEAR + + IPS_LEAPS_THRU_END_OF(newy - 1) - + IPS_LEAPS_THRU_END_OF(y - 1); + y = newy; + } + command->yearH = y / 100; + command->yearL = y % 100; + ip = mon_lengths[yleap]; + for(month = 0; days >= (long) ip[month]; ++month) + days = days - (long) ip[month]; + command->month = month + 1; + command->day = days + 1; +} + +static int ips_send_ffdc_reset_cmd(ips_command_t *command) +{ + ips_softc_t *sc = command->sc; + ips_cmd_status_t *status = command->arg; + ips_adapter_ffdc_cmd *command_struct; + + PRINTF(10,"ips test: got a command, building ffdc reset command\n"); + command->callback = ips_wakeup_callback; + command_struct = (ips_adapter_ffdc_cmd *)command->command_buffer; + command_struct->command = IPS_FFDC_CMD; + command_struct->id = command->id; + command_struct->reset_count = sc->ffdc_resetcount; + command_struct->reset_type = 0x80; + ips_ffdc_settime(command_struct, sc->ffdc_resettime.tv_sec); + + bus_dmamap_sync(sc->command_dmatag, command->command_dmamap, + BUS_DMASYNC_PREWRITE); + mtx_lock(&sc->cmd_mtx); + sc->ips_issue_cmd(command); + if (status->value != IPS_ERROR_STATUS) + msleep(status, &sc->cmd_mtx, 0, "ffdc", 0); + mtx_unlock(&sc->cmd_mtx); + ips_insert_free_cmd(sc, command); + return 0; +} + +int ips_ffdc_reset(ips_softc_t *sc) +{ + ips_cmd_status_t *status; + status = malloc(sizeof(ips_cmd_status_t), M_DEVBUF, M_NOWAIT|M_ZERO); + if(!status) + return ENOMEM; + if(ips_get_free_cmd(sc, ips_send_ffdc_reset_cmd, status, + IPS_NOWAIT_FLAG)){ + free(status, M_DEVBUF); + device_printf(sc->dev, "ERROR: unable to get a command! can't send ffdc reset!\n"); + } + if(COMMAND_ERROR(status)){ + device_printf(sc->dev, "ERROR: ffdc reset command failed!\n"); + } + free(status, M_DEVBUF); + return 0; +} + static void ips_write_nvram(ips_command_t *command){ ips_softc_t *sc = command->sc; ips_rw_nvram_cmd *command_struct; @@ -449,6 +536,9 @@ static void ips_write_nvram(ips_command_t *command){ bus_dmamap_sync(command->data_dmatag, command->data_dmamap, BUS_DMASYNC_POSTREAD); nvram = command->data_buffer; + /* retrieve adapter info and save in sc */ + sc->adapter_type = nvram->adapter_type; + strncpy(nvram->driver_high, IPS_VERSION_MAJOR, 4); strncpy(nvram->driver_low, IPS_VERSION_MINOR, 4); nvram->operating_system = IPS_OS_FREEBSD; |