109fe1b16Sdnielsen /*
209fe1b16Sdnielsen * CDDL HEADER START
309fe1b16Sdnielsen *
409fe1b16Sdnielsen * The contents of this file are subject to the terms of the
509fe1b16Sdnielsen * Common Development and Distribution License (the "License").
609fe1b16Sdnielsen * You may not use this file except in compliance with the License.
709fe1b16Sdnielsen *
809fe1b16Sdnielsen * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
909fe1b16Sdnielsen * or http://www.opensolaris.org/os/licensing.
1009fe1b16Sdnielsen * See the License for the specific language governing permissions
1109fe1b16Sdnielsen * and limitations under the License.
1209fe1b16Sdnielsen *
1309fe1b16Sdnielsen * When distributing Covered Code, include this CDDL HEADER in each
1409fe1b16Sdnielsen * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1509fe1b16Sdnielsen * If applicable, add the following below this CDDL HEADER, with the
1609fe1b16Sdnielsen * fields enclosed by brackets "[]" replaced with your own identifying
1709fe1b16Sdnielsen * information: Portions Copyright [yyyy] [name of copyright owner]
1809fe1b16Sdnielsen *
1909fe1b16Sdnielsen * CDDL HEADER END
2009fe1b16Sdnielsen */
2109fe1b16Sdnielsen
2209fe1b16Sdnielsen /*
2309fe1b16Sdnielsen * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
2409fe1b16Sdnielsen * Use is subject to license terms.
2509fe1b16Sdnielsen */
2609fe1b16Sdnielsen
2709fe1b16Sdnielsen #include <stdio.h>
2809fe1b16Sdnielsen #include <strings.h>
2909fe1b16Sdnielsen #include <libgen.h>
3009fe1b16Sdnielsen #include <cfga_scsi.h>
31*7b77b017Sjimand #include <sys/scfd/opcioif.h>
3209fe1b16Sdnielsen
3309fe1b16Sdnielsen
3409fe1b16Sdnielsen #define SCF_DRV "/devices/pseudo/scfd@200:rasctl"
3509fe1b16Sdnielsen #define SCFRETRY 3
3609fe1b16Sdnielsen #define SCFIOCWAIT 3
3709fe1b16Sdnielsen
3809fe1b16Sdnielsen
3909fe1b16Sdnielsen #define OPL_LOCATOR_OPT 0
4009fe1b16Sdnielsen #define OPL_LED_OPT 1
4109fe1b16Sdnielsen #define OPL_MODE_OPT 2
4209fe1b16Sdnielsen char *opl_opts[] = {
4309fe1b16Sdnielsen "locator",
4409fe1b16Sdnielsen "led",
4509fe1b16Sdnielsen "mode",
4609fe1b16Sdnielsen NULL
4709fe1b16Sdnielsen };
4809fe1b16Sdnielsen
4909fe1b16Sdnielsen
5009fe1b16Sdnielsen static scfga_ret_t
opl_get_scf_logical_disk(const apid_t * apidp,char ** errstring,scfiocgetdiskled_t * scf_disk)5109fe1b16Sdnielsen opl_get_scf_logical_disk(const apid_t *apidp, char **errstring,
5209fe1b16Sdnielsen scfiocgetdiskled_t *scf_disk)
5309fe1b16Sdnielsen {
5409fe1b16Sdnielsen int len;
5509fe1b16Sdnielsen char *phys_path;
5609fe1b16Sdnielsen char *strptr;
5709fe1b16Sdnielsen
5809fe1b16Sdnielsen phys_path = strdup(apidp->path);
5909fe1b16Sdnielsen if (phys_path == NULL) {
6009fe1b16Sdnielsen cfga_err(errstring, ENOMEM, ERR_OP_FAILED, 0);
6109fe1b16Sdnielsen return (SCFGA_ERR);
6209fe1b16Sdnielsen }
6309fe1b16Sdnielsen scf_disk->path[0] = '\0';
6409fe1b16Sdnielsen if ((strptr = strstr(phys_path, ":")) != NULL) {
6509fe1b16Sdnielsen strptr[0] = '\0';
6609fe1b16Sdnielsen len = snprintf((char *)scf_disk->path, sizeof (scf_disk->path),
6709fe1b16Sdnielsen "%s", (char *)(phys_path));
6809fe1b16Sdnielsen if (len >= sizeof (scf_disk->path)) {
6909fe1b16Sdnielsen free(phys_path);
7009fe1b16Sdnielsen cfga_err(errstring, 0, ERR_OP_FAILED, 0);
7109fe1b16Sdnielsen return (SCFGA_ERR);
7209fe1b16Sdnielsen }
7309fe1b16Sdnielsen } else {
7409fe1b16Sdnielsen free(phys_path);
7509fe1b16Sdnielsen cfga_err(errstring, 0, ERR_UNKNOWN, 0);
7609fe1b16Sdnielsen return (SCFGA_ERR);
7709fe1b16Sdnielsen }
7809fe1b16Sdnielsen free(phys_path);
7909fe1b16Sdnielsen
8009fe1b16Sdnielsen return (SCFGA_OK);
8109fe1b16Sdnielsen }
8209fe1b16Sdnielsen
8309fe1b16Sdnielsen
8409fe1b16Sdnielsen /*
8509fe1b16Sdnielsen * Open the SCF driver and use the ioctl interface to set or get the status.
8609fe1b16Sdnielsen *
8709fe1b16Sdnielsen * Returns 0 on success. Returns OP_FAILED on error.
8809fe1b16Sdnielsen */
8909fe1b16Sdnielsen static scfga_ret_t
opl_disk_led_control(apid_t * apidp,char ** errstring,struct cfga_msg * msgp,int request,scfiocgetdiskled_t * scf_disk)9009fe1b16Sdnielsen opl_disk_led_control(apid_t *apidp, char **errstring, struct cfga_msg *msgp,
9109fe1b16Sdnielsen int request, scfiocgetdiskled_t *scf_disk)
9209fe1b16Sdnielsen {
9309fe1b16Sdnielsen scfga_ret_t retval;
9409fe1b16Sdnielsen int scf_fd = -1;
9509fe1b16Sdnielsen int retry = 0;
9609fe1b16Sdnielsen
9709fe1b16Sdnielsen /* paranoid check */
9809fe1b16Sdnielsen if ((apidp == NULL) || (msgp == NULL) || (scf_disk == NULL)) {
9909fe1b16Sdnielsen cfga_err(errstring, 0, ERR_UNKNOWN, 0, 0);
10009fe1b16Sdnielsen return (SCFGA_ERR);
10109fe1b16Sdnielsen }
10209fe1b16Sdnielsen
10309fe1b16Sdnielsen retval = opl_get_scf_logical_disk((const apid_t *)apidp, errstring,
10409fe1b16Sdnielsen scf_disk);
10509fe1b16Sdnielsen if (retval != SCFGA_OK) {
10609fe1b16Sdnielsen /* errstring is set in opl_get_scf_logical_disk */
10709fe1b16Sdnielsen return (retval);
10809fe1b16Sdnielsen }
10909fe1b16Sdnielsen
11009fe1b16Sdnielsen /* Open a file descriptor for the scf driver. */
11109fe1b16Sdnielsen scf_fd = open(SCF_DRV, O_RDWR);
11209fe1b16Sdnielsen if (scf_fd < 0) {
11309fe1b16Sdnielsen cfga_err(errstring, errno, ERRARG_OPEN, SCF_DRV, 0);
11409fe1b16Sdnielsen return (SCFGA_LIB_ERR);
11509fe1b16Sdnielsen }
11609fe1b16Sdnielsen
11709fe1b16Sdnielsen /*
11809fe1b16Sdnielsen * Use the ioctl interface with the SCF driver to get/set the
11909fe1b16Sdnielsen * hdd locator indicator.
12009fe1b16Sdnielsen */
12109fe1b16Sdnielsen errno = 0;
12209fe1b16Sdnielsen while (ioctl(scf_fd, request, scf_disk) < 0) {
12309fe1b16Sdnielsen /* Check Retry Error Number */
12409fe1b16Sdnielsen if (errno != EBUSY && errno != EIO) {
12509fe1b16Sdnielsen break;
12609fe1b16Sdnielsen }
12709fe1b16Sdnielsen
12809fe1b16Sdnielsen /* Check Retry Times */
12909fe1b16Sdnielsen if (++retry > SCFRETRY) {
13009fe1b16Sdnielsen break;
13109fe1b16Sdnielsen }
13209fe1b16Sdnielsen errno = 0;
13309fe1b16Sdnielsen
13409fe1b16Sdnielsen (void) sleep(SCFIOCWAIT);
13509fe1b16Sdnielsen }
13609fe1b16Sdnielsen (void) close(scf_fd);
13709fe1b16Sdnielsen
13809fe1b16Sdnielsen if ((errno != 0) || (retry > SCFRETRY)) {
13909fe1b16Sdnielsen cfga_err(errstring, errno, ERR_OP_FAILED, 0, 0);
14009fe1b16Sdnielsen return (SCFGA_LIB_ERR);
14109fe1b16Sdnielsen }
14209fe1b16Sdnielsen return (SCFGA_OK);
14309fe1b16Sdnielsen }
14409fe1b16Sdnielsen
14509fe1b16Sdnielsen /*
14609fe1b16Sdnielsen * Print the value of the hard disk locator in a human friendly form.
14709fe1b16Sdnielsen */
14809fe1b16Sdnielsen static void
opl_print_locator(apid_t * apidp,struct cfga_msg * msgp,unsigned char led)14909fe1b16Sdnielsen opl_print_locator(apid_t *apidp, struct cfga_msg *msgp, unsigned char led)
15009fe1b16Sdnielsen {
15109fe1b16Sdnielsen led_modeid_t mode = LED_MODE_UNK;
15209fe1b16Sdnielsen
15309fe1b16Sdnielsen if ((msgp == NULL) || (msgp->message_routine == NULL)) {
15409fe1b16Sdnielsen return;
15509fe1b16Sdnielsen }
15609fe1b16Sdnielsen
15709fe1b16Sdnielsen cfga_msg(msgp, MSG_LED_HDR, 0);
15809fe1b16Sdnielsen switch ((int)led) {
15909fe1b16Sdnielsen case SCF_DISK_LED_ON:
16009fe1b16Sdnielsen mode = LED_MODE_FAULTED;
16109fe1b16Sdnielsen break;
16209fe1b16Sdnielsen
16309fe1b16Sdnielsen case SCF_DISK_LED_OFF:
16409fe1b16Sdnielsen mode = LED_MODE_OFF;
16509fe1b16Sdnielsen break;
16609fe1b16Sdnielsen
16709fe1b16Sdnielsen case SCF_DISK_LED_BLINK:
16809fe1b16Sdnielsen mode = LED_MODE_ON;
16909fe1b16Sdnielsen break;
17009fe1b16Sdnielsen
17109fe1b16Sdnielsen default:
17209fe1b16Sdnielsen mode = LED_MODE_UNK;
17309fe1b16Sdnielsen }
17409fe1b16Sdnielsen cfga_led_msg(msgp, apidp, LED_STR_LOCATOR, mode);
17509fe1b16Sdnielsen }
17609fe1b16Sdnielsen
17709fe1b16Sdnielsen /*
17809fe1b16Sdnielsen * Print the value of the hard disk fault LED in a human friendly form.
17909fe1b16Sdnielsen */
18009fe1b16Sdnielsen static void
opl_print_led(apid_t * apidp,struct cfga_msg * msgp,unsigned char led)18109fe1b16Sdnielsen opl_print_led(apid_t *apidp, struct cfga_msg *msgp, unsigned char led)
18209fe1b16Sdnielsen {
18309fe1b16Sdnielsen led_modeid_t mode = LED_MODE_UNK;
18409fe1b16Sdnielsen
18509fe1b16Sdnielsen if ((msgp == NULL) || (msgp->message_routine == NULL)) {
18609fe1b16Sdnielsen return;
18709fe1b16Sdnielsen }
18809fe1b16Sdnielsen
18909fe1b16Sdnielsen cfga_msg(msgp, MSG_LED_HDR, 0);
19009fe1b16Sdnielsen switch ((int)led) {
19109fe1b16Sdnielsen case SCF_DISK_LED_ON:
19209fe1b16Sdnielsen mode = LED_MODE_ON;
19309fe1b16Sdnielsen break;
19409fe1b16Sdnielsen
19509fe1b16Sdnielsen case SCF_DISK_LED_OFF:
19609fe1b16Sdnielsen mode = LED_MODE_OFF;
19709fe1b16Sdnielsen break;
19809fe1b16Sdnielsen
19909fe1b16Sdnielsen case SCF_DISK_LED_BLINK:
20009fe1b16Sdnielsen mode = LED_MODE_BLINK;
20109fe1b16Sdnielsen break;
20209fe1b16Sdnielsen
20309fe1b16Sdnielsen default:
20409fe1b16Sdnielsen mode = LED_MODE_UNK;
20509fe1b16Sdnielsen }
20609fe1b16Sdnielsen cfga_led_msg(msgp, apidp, LED_STR_FAULT, mode);
20709fe1b16Sdnielsen }
20809fe1b16Sdnielsen
20909fe1b16Sdnielsen static scfga_ret_t
opl_setlocator(const char * mode,apid_t * apidp,char ** errstring,struct cfga_msg * msgp)21009fe1b16Sdnielsen opl_setlocator(
21109fe1b16Sdnielsen const char *mode,
21209fe1b16Sdnielsen apid_t *apidp,
21309fe1b16Sdnielsen char **errstring,
21409fe1b16Sdnielsen struct cfga_msg *msgp)
21509fe1b16Sdnielsen {
21609fe1b16Sdnielsen scfga_ret_t retval;
21709fe1b16Sdnielsen scfiocgetdiskled_t scf_disk;
21809fe1b16Sdnielsen
21909fe1b16Sdnielsen if (strcmp(mode, "on") == 0) {
22009fe1b16Sdnielsen scf_disk.led = SCF_DISK_LED_BLINK;
22109fe1b16Sdnielsen } else if (strcmp(mode, "off") == 0) {
22209fe1b16Sdnielsen scf_disk.led = SCF_DISK_LED_OFF;
22309fe1b16Sdnielsen } else {
22409fe1b16Sdnielsen cfga_err(errstring, 0, ERRARG_OPT_INVAL, mode, 0);
22509fe1b16Sdnielsen return (SCFGA_ERR);
22609fe1b16Sdnielsen }
22709fe1b16Sdnielsen
22809fe1b16Sdnielsen retval = opl_disk_led_control(apidp, errstring, msgp,
22909fe1b16Sdnielsen SCFIOCSETDISKLED, &scf_disk);
23009fe1b16Sdnielsen
23109fe1b16Sdnielsen return (retval);
23209fe1b16Sdnielsen }
23309fe1b16Sdnielsen
23409fe1b16Sdnielsen
23509fe1b16Sdnielsen static scfga_ret_t
opl_getled(int print_switch,apid_t * apidp,char ** errstring,struct cfga_msg * msgp)23609fe1b16Sdnielsen opl_getled(
23709fe1b16Sdnielsen int print_switch,
23809fe1b16Sdnielsen apid_t *apidp,
23909fe1b16Sdnielsen char **errstring,
24009fe1b16Sdnielsen struct cfga_msg *msgp)
24109fe1b16Sdnielsen {
24209fe1b16Sdnielsen scfga_ret_t retval;
24309fe1b16Sdnielsen scfiocgetdiskled_t scf_disk;
24409fe1b16Sdnielsen
24509fe1b16Sdnielsen (void) memset((void *)&scf_disk, 0, sizeof (scf_disk));
24609fe1b16Sdnielsen
24709fe1b16Sdnielsen retval = opl_disk_led_control(apidp, errstring, msgp,
24809fe1b16Sdnielsen SCFIOCGETDISKLED, &scf_disk);
24909fe1b16Sdnielsen if (retval != SCFGA_OK) {
25009fe1b16Sdnielsen return (retval);
25109fe1b16Sdnielsen }
25209fe1b16Sdnielsen if (print_switch == OPL_LED_OPT) {
25309fe1b16Sdnielsen opl_print_led(apidp, msgp, scf_disk.led);
25409fe1b16Sdnielsen } else {
25509fe1b16Sdnielsen opl_print_locator(apidp, msgp, scf_disk.led);
25609fe1b16Sdnielsen }
25709fe1b16Sdnielsen
25809fe1b16Sdnielsen return (SCFGA_OK);
25909fe1b16Sdnielsen }
26009fe1b16Sdnielsen
26109fe1b16Sdnielsen
26209fe1b16Sdnielsen static scfga_ret_t
opl_setled(const char * mode,apid_t * apidp,char ** errstring,struct cfga_msg * msgp)26309fe1b16Sdnielsen opl_setled(
26409fe1b16Sdnielsen const char *mode,
26509fe1b16Sdnielsen apid_t *apidp,
26609fe1b16Sdnielsen char **errstring,
26709fe1b16Sdnielsen struct cfga_msg *msgp)
26809fe1b16Sdnielsen {
26909fe1b16Sdnielsen scfga_ret_t retval;
27009fe1b16Sdnielsen scfiocgetdiskled_t scf_disk;
27109fe1b16Sdnielsen
27209fe1b16Sdnielsen (void) memset((void *)&scf_disk, 0, sizeof (scf_disk));
27309fe1b16Sdnielsen
27409fe1b16Sdnielsen if (strcmp(mode, "on") == 0) {
27509fe1b16Sdnielsen scf_disk.led = SCF_DISK_LED_ON;
27609fe1b16Sdnielsen } else if (strcmp(mode, "off") == 0) {
27709fe1b16Sdnielsen scf_disk.led = SCF_DISK_LED_OFF;
27809fe1b16Sdnielsen } else if (strcmp(mode, "blink") == 0) {
27909fe1b16Sdnielsen scf_disk.led = SCF_DISK_LED_BLINK;
28009fe1b16Sdnielsen } else {
28109fe1b16Sdnielsen cfga_err(errstring, 0, ERRARG_OPT_INVAL, mode, 0);
28209fe1b16Sdnielsen return (SCFGA_ERR);
28309fe1b16Sdnielsen }
28409fe1b16Sdnielsen
28509fe1b16Sdnielsen retval = opl_disk_led_control(apidp, errstring, msgp,
28609fe1b16Sdnielsen SCFIOCSETDISKLED, &scf_disk);
28709fe1b16Sdnielsen return (retval);
28809fe1b16Sdnielsen }
28909fe1b16Sdnielsen
29009fe1b16Sdnielsen /*
29109fe1b16Sdnielsen * The func argument is a string in one of the two following forms:
29209fe1b16Sdnielsen * led=LED[,mode=MODE]
29309fe1b16Sdnielsen * locator[=on|off]
29409fe1b16Sdnielsen * which can generically be thought of as:
29509fe1b16Sdnielsen * name=value[,name=value]
29609fe1b16Sdnielsen * So first, split the function based on the comma into two name-value
29709fe1b16Sdnielsen * pairs.
29809fe1b16Sdnielsen */
29909fe1b16Sdnielsen /*ARGSUSED*/
30009fe1b16Sdnielsen 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)30109fe1b16Sdnielsen plat_dev_led(
30209fe1b16Sdnielsen const char *func,
30309fe1b16Sdnielsen scfga_cmd_t cmd,
30409fe1b16Sdnielsen apid_t *apidp,
30509fe1b16Sdnielsen prompt_t *argsp,
30609fe1b16Sdnielsen cfga_flags_t flags,
30709fe1b16Sdnielsen char **errstring)
30809fe1b16Sdnielsen {
30909fe1b16Sdnielsen scfga_ret_t retval = SCFGA_ERR;
31009fe1b16Sdnielsen char *optptr = (char *)func;
31109fe1b16Sdnielsen char *value = NULL;
31209fe1b16Sdnielsen
31309fe1b16Sdnielsen int opt_locator = 0;
31409fe1b16Sdnielsen int opt_led = 0;
31509fe1b16Sdnielsen int opt_mode = 0;
31609fe1b16Sdnielsen char *locator_value = NULL;
31709fe1b16Sdnielsen char *led_value = NULL;
31809fe1b16Sdnielsen char *mode_value = NULL;
31909fe1b16Sdnielsen
32009fe1b16Sdnielsen if (func == NULL) {
32109fe1b16Sdnielsen return (SCFGA_ERR);
32209fe1b16Sdnielsen }
32309fe1b16Sdnielsen
32409fe1b16Sdnielsen while (*optptr != '\0') {
32509fe1b16Sdnielsen switch (getsubopt(&optptr, opl_opts, &value)) {
32609fe1b16Sdnielsen case OPL_LOCATOR_OPT:
32709fe1b16Sdnielsen opt_locator++;
32809fe1b16Sdnielsen locator_value = value;
32909fe1b16Sdnielsen break;
33009fe1b16Sdnielsen case OPL_LED_OPT:
33109fe1b16Sdnielsen opt_led++;
33209fe1b16Sdnielsen led_value = value;
33309fe1b16Sdnielsen break;
33409fe1b16Sdnielsen case OPL_MODE_OPT:
33509fe1b16Sdnielsen opt_mode++;
33609fe1b16Sdnielsen mode_value = value;
33709fe1b16Sdnielsen break;
33809fe1b16Sdnielsen default:
33909fe1b16Sdnielsen cfga_err(errstring, 0, ERR_CMD_INVAL, 0);
34009fe1b16Sdnielsen return (SCFGA_OPNOTSUPP);
34109fe1b16Sdnielsen break;
34209fe1b16Sdnielsen }
34309fe1b16Sdnielsen }
34409fe1b16Sdnielsen
34509fe1b16Sdnielsen if (!opt_locator && !opt_led) {
34609fe1b16Sdnielsen cfga_err(errstring, 0, ERR_CMD_INVAL, 0);
34709fe1b16Sdnielsen return (SCFGA_ERR);
34809fe1b16Sdnielsen }
34909fe1b16Sdnielsen
35009fe1b16Sdnielsen if (opt_locator) {
35109fe1b16Sdnielsen if ((opt_locator > 1) || opt_led || opt_mode ||
35209fe1b16Sdnielsen (strncmp(func, "locator", strlen("locator")) != 0) ||
35309fe1b16Sdnielsen (locator_value &&
35409fe1b16Sdnielsen (strcmp(locator_value, "blink") == 0))) {
35509fe1b16Sdnielsen cfga_err(errstring, 0, ERR_CMD_INVAL, 0);
35609fe1b16Sdnielsen return (SCFGA_ERR);
35709fe1b16Sdnielsen }
35809fe1b16Sdnielsen
35909fe1b16Sdnielsen /* Options are sane so set or get the locator. */
36009fe1b16Sdnielsen if (locator_value) {
36109fe1b16Sdnielsen retval = opl_setlocator(locator_value, apidp,
36209fe1b16Sdnielsen errstring, argsp->msgp);
36309fe1b16Sdnielsen } else {
36409fe1b16Sdnielsen retval = opl_getled(OPL_LOCATOR_OPT, apidp, errstring,
36509fe1b16Sdnielsen argsp->msgp);
36609fe1b16Sdnielsen }
36709fe1b16Sdnielsen }
36809fe1b16Sdnielsen if (opt_led) {
36909fe1b16Sdnielsen if ((opt_led > 1) || (opt_mode > 1) || (opt_locator) ||
37009fe1b16Sdnielsen (strncmp(func, "led", strlen("led")) != 0) ||
37109fe1b16Sdnielsen (!led_value || strcmp(led_value, "fault")) ||
37209fe1b16Sdnielsen (opt_mode && !mode_value)) {
37309fe1b16Sdnielsen
37409fe1b16Sdnielsen cfga_err(errstring, 0, ERR_CMD_INVAL, 0);
37509fe1b16Sdnielsen return (SCFGA_ERR);
37609fe1b16Sdnielsen }
37709fe1b16Sdnielsen
37809fe1b16Sdnielsen /* options are sane so go ahead and set or get the led */
37909fe1b16Sdnielsen if (mode_value != NULL) {
38009fe1b16Sdnielsen retval = opl_setled(mode_value, apidp, errstring,
38109fe1b16Sdnielsen argsp->msgp);
38209fe1b16Sdnielsen } else {
38309fe1b16Sdnielsen retval = opl_getled(OPL_LED_OPT, apidp, errstring,
38409fe1b16Sdnielsen argsp->msgp);
38509fe1b16Sdnielsen }
38609fe1b16Sdnielsen }
38709fe1b16Sdnielsen return (retval);
38809fe1b16Sdnielsen
38909fe1b16Sdnielsen }
390