2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
7 * A full copy of the text of the CDDL should have accompanied this
8 * source. A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
13 * Copyright (c) 2018 Joyent, Inc.
17 #include <sys/types.h>
27 #include <libdevinfo.h>
29 #include <sys/sata/adapters/ahci/ahciem.h>
31 #define AHCIEM_IDENT "ident"
32 #define AHCIEM_FAULT "fault"
33 #define AHCIEM_NOACTIVITY "noactivity"
34 #define AHCIEM_DEFAULT "default"
35 #define AHCIEM_UNKNOWN "unknown"
39 static const char *ahciem_progname
;
43 ahci_em_led_state_t ahci_led
;
46 boolean_t
*ahci_found
;
51 ahciem_usage(const char *fmt
, ...)
61 (void) fprintf(stderr
, "Usage: %s [-s mode] [port]\n"
63 "\t-s mode\t\tset LED to mode\n",
68 ahciem_led_to_string(uint_t led
)
71 case AHCI_EM_LED_IDENT_ENABLE
:
72 return (AHCIEM_IDENT
);
73 case AHCI_EM_LED_FAULT_ENABLE
:
74 return (AHCIEM_FAULT
);
75 case AHCI_EM_LED_ACTIVITY_DISABLE
:
76 return (AHCIEM_NOACTIVITY
);
77 case (AHCI_EM_LED_IDENT_ENABLE
| AHCI_EM_LED_FAULT_ENABLE
):
78 return (AHCIEM_IDENT
"," AHCIEM_FAULT
);
79 case (AHCI_EM_LED_IDENT_ENABLE
| AHCI_EM_LED_ACTIVITY_DISABLE
):
80 return (AHCIEM_IDENT
"," AHCIEM_NOACTIVITY
);
81 case (AHCI_EM_LED_FAULT_ENABLE
| AHCI_EM_LED_ACTIVITY_DISABLE
):
82 return (AHCIEM_FAULT
"," AHCIEM_NOACTIVITY
);
84 case (AHCI_EM_LED_IDENT_ENABLE
| AHCI_EM_LED_FAULT_ENABLE
|
85 AHCI_EM_LED_ACTIVITY_DISABLE
):
86 return (AHCIEM_IDENT
"," AHCIEM_FAULT
"," AHCIEM_NOACTIVITY
);
89 return (AHCIEM_DEFAULT
);
91 return (AHCIEM_UNKNOWN
);
96 ahciem_match(ahciem_t
*ahci
, const char *port
)
100 if (ahci
->ahci_argc
== 0)
103 for (i
= 0; i
< ahci
->ahci_argc
; i
++) {
104 size_t len
= strlen(ahci
->ahci_argv
[i
]);
107 * Perform a partial match on the base name. This allows us to
108 * match all of a controller by using a string like "ahci0".
110 if (strncmp(ahci
->ahci_argv
[i
], port
, len
) == 0) {
111 ahci
->ahci_found
[i
] = B_TRUE
;
120 static ahci_em_led_state_t
121 ahciem_parse(const char *arg
)
123 if (strcmp(arg
, AHCIEM_IDENT
) == 0) {
124 return (AHCI_EM_LED_IDENT_ENABLE
);
125 } else if (strcmp(arg
, AHCIEM_FAULT
) == 0) {
126 return (AHCI_EM_LED_FAULT_ENABLE
);
127 } else if (strcmp(arg
, AHCIEM_NOACTIVITY
) == 0) {
128 return (AHCI_EM_LED_ACTIVITY_DISABLE
);
129 } else if (strcmp(arg
, AHCIEM_DEFAULT
) == 0) {
133 errx(EXIT_USAGE
, "invalid LED mode with -s: %s", arg
);
137 ahciem_set(ahciem_t
*ahci
, const char *portstr
, int fd
, int port
)
139 ahci_ioc_em_set_t set
;
141 bzero(&set
, sizeof (set
));
143 set
.aiems_port
= port
;
144 set
.aiems_op
= AHCI_EM_IOC_SET_OP_SET
;
145 set
.aiems_leds
= ahci
->ahci_led
;
147 if (ioctl(fd
, AHCI_EM_IOC_SET
, &set
) != 0) {
148 warn("failed to set LEDs on %s", portstr
);
154 ahciem_devinfo(di_node_t node
, void *arg
)
156 char *driver
, *mpath
, *fullpath
;
160 ahciem_t
*ahci
= arg
;
162 ahci_ioc_em_get_t get
;
164 if ((driver
= di_driver_name(node
)) == NULL
)
165 return (DI_WALK_CONTINUE
);
166 if (strcmp(driver
, "ahci") != 0)
167 return (DI_WALK_CONTINUE
);
168 inst
= di_instance(node
);
171 while ((m
= di_minor_next(node
, m
)) != DI_MINOR_NIL
) {
172 char *mname
= di_minor_name(m
);
174 if (mname
!= NULL
&& strcmp("devctl", mname
) == 0)
178 if (m
== DI_MINOR_NIL
) {
179 warnx("encountered ahci%d without devctl node", inst
);
180 return (DI_WALK_PRUNECHILD
);
183 if ((mpath
= di_devfs_minor_path(m
)) == NULL
) {
184 warnx("failed to get path for ahci%d devctl minor", inst
);
185 return (DI_WALK_PRUNECHILD
);
188 if (asprintf(&fullpath
, "/devices/%s", mpath
) == -1) {
189 warn("failed to construct /devices path from %s", mpath
);
190 return (DI_WALK_PRUNECHILD
);
193 if ((fd
= open(fullpath
, O_RDWR
)) < 0) {
194 warn("failed to open ahci%d devctl path %s", inst
, fullpath
);
198 bzero(&get
, sizeof (get
));
199 if (ioctl(fd
, AHCI_EM_IOC_GET
, &get
) != 0) {
200 warn("failed to get AHCI enclosure information for ahci%d",
206 if ((get
.aiemg_flags
& AHCI_EM_FLAG_CONTROL_ACTIVITY
) != 0) {
207 sup
= ahciem_led_to_string(AHCI_EM_LED_IDENT_ENABLE
|
208 AHCI_EM_LED_FAULT_ENABLE
| AHCI_EM_LED_ACTIVITY_DISABLE
);
210 sup
= ahciem_led_to_string(AHCI_EM_LED_IDENT_ENABLE
|
211 AHCI_EM_LED_FAULT_ENABLE
);
214 for (i
= 0; i
< AHCI_EM_IOC_MAX_PORTS
; i
++) {
218 if (((1 << i
) & get
.aiemg_nports
) == 0)
221 (void) snprintf(port
, sizeof (port
), "ahci%d/%u", inst
, i
);
222 if (!ahciem_match(ahci
, port
))
225 if (ahci
->ahci_set
) {
226 ahciem_set(ahci
, port
, fd
, i
);
230 state
= ahciem_led_to_string(get
.aiemg_status
[i
]);
231 (void) printf("%-20s %-12s %s,default\n", port
, state
, sup
);
236 return (DI_WALK_PRUNECHILD
);
240 main(int argc
, char *argv
[])
246 ahciem_progname
= basename(argv
[0]);
248 bzero(&ahci
, sizeof (ahciem_t
));
249 while ((c
= getopt(argc
, argv
, ":s:")) != -1) {
252 ahci
.ahci_set
= B_TRUE
;
253 ahci
.ahci_led
= ahciem_parse(optarg
);
256 ahciem_usage("option -%c requires an operand\n",
261 ahciem_usage("unknown option: -%c\n", optopt
);
268 ahci
.ahci_argc
= argc
;
269 ahci
.ahci_argv
= argv
;
271 ahci
.ahci_found
= calloc(argc
, sizeof (boolean_t
));
272 if (ahci
.ahci_found
== NULL
) {
273 err(EXIT_FAILURE
, "failed to alloc memory for %d "
278 if ((root
= di_init("/", DINFOCPYALL
)) == DI_NODE_NIL
) {
279 err(EXIT_FAILURE
, "failed to open devinfo tree");
282 if (!ahci
.ahci_set
) {
283 (void) printf("%-20s %-12s %s\n", "PORT", "ACTIVE",
287 if (di_walk_node(root
, DI_WALK_CLDFIRST
, &ahci
,
288 ahciem_devinfo
) != 0) {
289 err(EXIT_FAILURE
, "failed to walk devinfo tree");
293 for (i
= 0; i
< argc
; i
++) {
294 if (ahci
.ahci_found
[i
])
296 warnx("failed to find ahci enclosure port \"%s\"",