#include <stdio.h>
#include <strings.h>
#include <libgen.h>
#include <cfga_scsi.h>
#include <sys/scfd/opcioif.h>
#define SCF_DRV "/devices/pseudo/scfd@200:rasctl"
#define SCFRETRY 3
#define SCFIOCWAIT 3
#define OPL_LOCATOR_OPT 0
#define OPL_LED_OPT 1
#define OPL_MODE_OPT 2
char *opl_opts[] = {
"locator",
"led",
"mode",
NULL
};
static scfga_ret_t
opl_get_scf_logical_disk(const apid_t *apidp, char **errstring,
scfiocgetdiskled_t *scf_disk)
{
int len;
char *phys_path;
char *strptr;
phys_path = strdup(apidp->path);
if (phys_path == NULL) {
cfga_err(errstring, ENOMEM, ERR_OP_FAILED, 0);
return (SCFGA_ERR);
}
scf_disk->path[0] = '\0';
if ((strptr = strstr(phys_path, ":")) != NULL) {
strptr[0] = '\0';
len = snprintf((char *)scf_disk->path, sizeof (scf_disk->path),
"%s", (char *)(phys_path));
if (len >= sizeof (scf_disk->path)) {
free(phys_path);
cfga_err(errstring, 0, ERR_OP_FAILED, 0);
return (SCFGA_ERR);
}
} else {
free(phys_path);
cfga_err(errstring, 0, ERR_UNKNOWN, 0);
return (SCFGA_ERR);
}
free(phys_path);
return (SCFGA_OK);
}
static scfga_ret_t
opl_disk_led_control(apid_t *apidp, char **errstring, struct cfga_msg *msgp,
int request, scfiocgetdiskled_t *scf_disk)
{
scfga_ret_t retval;
int scf_fd = -1;
int retry = 0;
if ((apidp == NULL) || (msgp == NULL) || (scf_disk == NULL)) {
cfga_err(errstring, 0, ERR_UNKNOWN, 0, 0);
return (SCFGA_ERR);
}
retval = opl_get_scf_logical_disk((const apid_t *)apidp, errstring,
scf_disk);
if (retval != SCFGA_OK) {
return (retval);
}
scf_fd = open(SCF_DRV, O_RDWR);
if (scf_fd < 0) {
cfga_err(errstring, errno, ERRARG_OPEN, SCF_DRV, 0);
return (SCFGA_LIB_ERR);
}
errno = 0;
while (ioctl(scf_fd, request, scf_disk) < 0) {
if (errno != EBUSY && errno != EIO) {
break;
}
if (++retry > SCFRETRY) {
break;
}
errno = 0;
(void) sleep(SCFIOCWAIT);
}
(void) close(scf_fd);
if ((errno != 0) || (retry > SCFRETRY)) {
cfga_err(errstring, errno, ERR_OP_FAILED, 0, 0);
return (SCFGA_LIB_ERR);
}
return (SCFGA_OK);
}
static void
opl_print_locator(apid_t *apidp, struct cfga_msg *msgp, unsigned char led)
{
led_modeid_t mode = LED_MODE_UNK;
if ((msgp == NULL) || (msgp->message_routine == NULL)) {
return;
}
cfga_msg(msgp, MSG_LED_HDR, 0);
switch ((int)led) {
case SCF_DISK_LED_ON:
mode = LED_MODE_FAULTED;
break;
case SCF_DISK_LED_OFF:
mode = LED_MODE_OFF;
break;
case SCF_DISK_LED_BLINK:
mode = LED_MODE_ON;
break;
default:
mode = LED_MODE_UNK;
}
cfga_led_msg(msgp, apidp, LED_STR_LOCATOR, mode);
}
static void
opl_print_led(apid_t *apidp, struct cfga_msg *msgp, unsigned char led)
{
led_modeid_t mode = LED_MODE_UNK;
if ((msgp == NULL) || (msgp->message_routine == NULL)) {
return;
}
cfga_msg(msgp, MSG_LED_HDR, 0);
switch ((int)led) {
case SCF_DISK_LED_ON:
mode = LED_MODE_ON;
break;
case SCF_DISK_LED_OFF:
mode = LED_MODE_OFF;
break;
case SCF_DISK_LED_BLINK:
mode = LED_MODE_BLINK;
break;
default:
mode = LED_MODE_UNK;
}
cfga_led_msg(msgp, apidp, LED_STR_FAULT, mode);
}
static scfga_ret_t
opl_setlocator(
const char *mode,
apid_t *apidp,
char **errstring,
struct cfga_msg *msgp)
{
scfga_ret_t retval;
scfiocgetdiskled_t scf_disk;
if (strcmp(mode, "on") == 0) {
scf_disk.led = SCF_DISK_LED_BLINK;
} else if (strcmp(mode, "off") == 0) {
scf_disk.led = SCF_DISK_LED_OFF;
} else {
cfga_err(errstring, 0, ERRARG_OPT_INVAL, mode, 0);
return (SCFGA_ERR);
}
retval = opl_disk_led_control(apidp, errstring, msgp,
SCFIOCSETDISKLED, &scf_disk);
return (retval);
}
static scfga_ret_t
opl_getled(
int print_switch,
apid_t *apidp,
char **errstring,
struct cfga_msg *msgp)
{
scfga_ret_t retval;
scfiocgetdiskled_t scf_disk;
(void) memset((void *)&scf_disk, 0, sizeof (scf_disk));
retval = opl_disk_led_control(apidp, errstring, msgp,
SCFIOCGETDISKLED, &scf_disk);
if (retval != SCFGA_OK) {
return (retval);
}
if (print_switch == OPL_LED_OPT) {
opl_print_led(apidp, msgp, scf_disk.led);
} else {
opl_print_locator(apidp, msgp, scf_disk.led);
}
return (SCFGA_OK);
}
static scfga_ret_t
opl_setled(
const char *mode,
apid_t *apidp,
char **errstring,
struct cfga_msg *msgp)
{
scfga_ret_t retval;
scfiocgetdiskled_t scf_disk;
(void) memset((void *)&scf_disk, 0, sizeof (scf_disk));
if (strcmp(mode, "on") == 0) {
scf_disk.led = SCF_DISK_LED_ON;
} else if (strcmp(mode, "off") == 0) {
scf_disk.led = SCF_DISK_LED_OFF;
} else if (strcmp(mode, "blink") == 0) {
scf_disk.led = SCF_DISK_LED_BLINK;
} else {
cfga_err(errstring, 0, ERRARG_OPT_INVAL, mode, 0);
return (SCFGA_ERR);
}
retval = opl_disk_led_control(apidp, errstring, msgp,
SCFIOCSETDISKLED, &scf_disk);
return (retval);
}
scfga_ret_t
plat_dev_led(
const char *func,
scfga_cmd_t cmd,
apid_t *apidp,
prompt_t *argsp,
cfga_flags_t flags,
char **errstring)
{
scfga_ret_t retval = SCFGA_ERR;
char *optptr = (char *)func;
char *value = NULL;
int opt_locator = 0;
int opt_led = 0;
int opt_mode = 0;
char *locator_value = NULL;
char *led_value = NULL;
char *mode_value = NULL;
if (func == NULL) {
return (SCFGA_ERR);
}
while (*optptr != '\0') {
switch (getsubopt(&optptr, opl_opts, &value)) {
case OPL_LOCATOR_OPT:
opt_locator++;
locator_value = value;
break;
case OPL_LED_OPT:
opt_led++;
led_value = value;
break;
case OPL_MODE_OPT:
opt_mode++;
mode_value = value;
break;
default:
cfga_err(errstring, 0, ERR_CMD_INVAL, 0);
return (SCFGA_OPNOTSUPP);
break;
}
}
if (!opt_locator && !opt_led) {
cfga_err(errstring, 0, ERR_CMD_INVAL, 0);
return (SCFGA_ERR);
}
if (opt_locator) {
if ((opt_locator > 1) || opt_led || opt_mode ||
(strncmp(func, "locator", strlen("locator")) != 0) ||
(locator_value &&
(strcmp(locator_value, "blink") == 0))) {
cfga_err(errstring, 0, ERR_CMD_INVAL, 0);
return (SCFGA_ERR);
}
if (locator_value) {
retval = opl_setlocator(locator_value, apidp,
errstring, argsp->msgp);
} else {
retval = opl_getled(OPL_LOCATOR_OPT, apidp, errstring,
argsp->msgp);
}
}
if (opt_led) {
if ((opt_led > 1) || (opt_mode > 1) || (opt_locator) ||
(strncmp(func, "led", strlen("led")) != 0) ||
(!led_value || strcmp(led_value, "fault")) ||
(opt_mode && !mode_value)) {
cfga_err(errstring, 0, ERR_CMD_INVAL, 0);
return (SCFGA_ERR);
}
if (mode_value != NULL) {
retval = opl_setled(mode_value, apidp, errstring,
argsp->msgp);
} else {
retval = opl_getled(OPL_LED_OPT, apidp, errstring,
argsp->msgp);
}
}
return (retval);
}