1 /* 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 5 * 1.0 of the CDDL. 6 * 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. 10 */ 11 12 /* 13 * Copyright (c) 2018, Joyent, Inc. 14 */ 15 16 #include <unistd.h> 17 #include <stropts.h> 18 #include <fcntl.h> 19 #include <libnvpair.h> 20 #include <string.h> 21 #include <strings.h> 22 #include <sys/fm/protocol.h> 23 #include <sys/stat.h> 24 #include <sys/types.h> 25 #include <fm/libtopo.h> 26 #include <fm/topo_mod.h> 27 #include <libgen.h> 28 29 #include "sys/sata/adapters/ahci/ahciem.h" 30 31 #define TOPO_METH_AHCI_LED_MODE_VERSION 0 32 33 /* 34 * This enum is used to more clearly demonstrate the mapping between libtopo's 35 * concept of LED types and the types represented in AHCI. 36 */ 37 typedef enum { 38 AHCI_FAC_IDENT, 39 AHCI_FAC_FAULT 40 } ahci_fac_led_t; 41 42 static int fac_prov_ahci_enum(topo_mod_t *, tnode_t *, const char *, 43 topo_instance_t, topo_instance_t, void *, void *); 44 45 /* 46 * ahci facility provider methods 47 */ 48 static int ahci_led_mode(topo_mod_t *, tnode_t *, topo_version_t, 49 nvlist_t *, nvlist_t **); 50 51 static const topo_modops_t ahci_ops = { fac_prov_ahci_enum, NULL }; 52 53 static const topo_modinfo_t ahci_info = 54 { "ahci facility provider", FM_FMRI_SCHEME_HC, TOPO_VERSION, 55 &ahci_ops }; 56 57 static const topo_method_t ahci_fac_methods[] = { 58 { "ahci_led_mode", TOPO_PROP_METH_DESC, 59 TOPO_METH_AHCI_LED_MODE_VERSION, 60 TOPO_STABILITY_INTERNAL, ahci_led_mode }, 61 { NULL } 62 }; 63 64 int 65 _topo_init(topo_mod_t *mod, topo_version_t version) 66 { 67 if (getenv("TOPOFACAHCIDEBUG") != NULL) 68 topo_mod_setdebug(mod); 69 70 return (topo_mod_register(mod, &ahci_info, TOPO_VERSION)); 71 } 72 73 void 74 _topo_fini(topo_mod_t *mod) 75 { 76 topo_mod_unregister(mod); 77 } 78 79 /* 80 * Get or set the LED state for a given LED of type (facility node type, port). 81 * This function returns -1 on error. 82 */ 83 static int 84 do_led_control(topo_mod_t *mod, const char *devctl, uint32_t port, 85 ahci_fac_led_t fac_type, uint32_t *ledmode, boolean_t set) 86 { 87 int fd = -1, ret = -1; 88 89 if ((fd = open(devctl, (set ? O_RDWR : O_RDONLY))) == -1) { 90 topo_mod_dprintf(mod, "devctl open failed: %s", 91 strerror(errno)); 92 goto out; 93 } 94 95 if (set) { 96 ahci_ioc_em_set_t ahci_set; 97 uint32_t led_status = *ledmode; 98 uint_t op, leds; 99 100 switch (fac_type) { 101 case AHCI_FAC_IDENT: 102 leds = AHCI_EM_LED_IDENT_ENABLE; 103 break; 104 case AHCI_FAC_FAULT: 105 leds = AHCI_EM_LED_FAULT_ENABLE; 106 break; 107 default: 108 topo_mod_dprintf(mod, "invalid facility node type: %d", 109 fac_type); 110 goto out; 111 } 112 113 if (led_status) { 114 op = AHCI_EM_IOC_SET_OP_ADD; 115 } else { 116 op = AHCI_EM_IOC_SET_OP_REM; 117 } 118 119 bzero(&ahci_set, sizeof (ahci_set)); 120 ahci_set.aiems_port = port; 121 ahci_set.aiems_op = op; 122 ahci_set.aiems_leds = leds; 123 124 if (ioctl(fd, AHCI_EM_IOC_SET, &ahci_set) == -1) { 125 topo_mod_dprintf(mod, "ioctl failed: %s", 126 strerror(errno)); 127 goto out; 128 } 129 } else { 130 uint_t led_set = 0; 131 ahci_ioc_em_get_t ahci_get; 132 133 bzero(&ahci_get, sizeof (ahci_get)); 134 if (ioctl(fd, AHCI_EM_IOC_GET, &ahci_get) == -1) { 135 topo_mod_dprintf(mod, "led control ioctl failed: %s", 136 strerror(errno)); 137 goto out; 138 } 139 140 switch (fac_type) { 141 case AHCI_FAC_IDENT: 142 led_set = ahci_get.aiemg_status[port] & 143 AHCI_EM_LED_IDENT_ENABLE; 144 break; 145 case AHCI_FAC_FAULT: 146 led_set = ahci_get.aiemg_status[port] & 147 AHCI_EM_LED_FAULT_ENABLE; 148 break; 149 default: 150 topo_mod_dprintf(mod, "invalid facility node type: %d", 151 fac_type); 152 goto out; 153 } 154 155 *ledmode = (led_set != 0) ? 156 TOPO_LED_STATE_ON : TOPO_LED_STATE_OFF; 157 } 158 ret = 0; 159 160 out: 161 if (fd >= 0) { 162 (void) close(fd); 163 } 164 return (ret); 165 } 166 167 static int 168 ahci_led_mode(topo_mod_t *mod, tnode_t *node, topo_version_t vers, 169 nvlist_t *in, nvlist_t **nvout) 170 { 171 int err, ret = 0; 172 tnode_t *pnode = topo_node_parent(node); 173 uint32_t type, ledmode = 0, ahci_port; 174 nvlist_t *pargs, *nvl; 175 char *devctl = NULL; 176 boolean_t set; 177 ahci_fac_led_t fac_type; 178 179 if (vers > TOPO_METH_AHCI_LED_MODE_VERSION) { 180 return (topo_mod_seterrno(mod, ETOPO_METHOD_VERNEW)); 181 } 182 183 if (topo_prop_get_uint32(node, TOPO_PGROUP_FACILITY, TOPO_FACILITY_TYPE, 184 &type, &err) != 0) { 185 topo_mod_dprintf(mod, "%s: Failed to lookup %s property " 186 "(%s)", __func__, TOPO_FACILITY_TYPE, topo_strerror(err)); 187 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 188 } 189 190 /* 191 * While the AHCI specification includes bits for locate, fault, and 192 * activity LEDs, we generally only need to account for locate and fault 193 * LEDs, as activity LEDs are typically disabled in hardware. 194 */ 195 switch (type) { 196 case (TOPO_LED_TYPE_SERVICE): 197 fac_type = AHCI_FAC_FAULT; 198 break; 199 case (TOPO_LED_TYPE_LOCATE): 200 fac_type = AHCI_FAC_IDENT; 201 break; 202 default: 203 topo_mod_dprintf(mod, "%s: Invalid LED type: 0x%x\n", __func__, 204 type); 205 ret = topo_mod_seterrno(mod, EMOD_NVL_INVAL); 206 return (ret); 207 } 208 209 if (topo_prop_get_string(pnode, TOPO_PGROUP_BINDING, 210 TOPO_BINDING_DEVCTL, &devctl, &err) != 0 || 211 topo_prop_get_uint32(pnode, TOPO_PGROUP_BINDING, 212 TOPO_BINDING_PORT, &ahci_port, &err) != 0) { 213 topo_mod_dprintf(mod, "%s: Facility was missing ahci binding " 214 "properties\n", __func__); 215 ret = topo_mod_seterrno(mod, EMOD_NVL_INVAL); 216 goto out; 217 } 218 219 if ((nvlist_lookup_nvlist(in, TOPO_PROP_PARGS, &pargs) == 0) && 220 nvlist_exists(pargs, TOPO_PROP_VAL_VAL)) { 221 /* 222 * Set the LED mode. 223 */ 224 set = B_TRUE; 225 226 if ((ret = nvlist_lookup_uint32(pargs, TOPO_PROP_VAL_VAL, 227 &ledmode)) != 0) { 228 topo_mod_dprintf(mod, "%s: Failed to lookup %s nvpair " 229 "(%s)\n", __func__, TOPO_PROP_VAL_VAL, 230 strerror(ret)); 231 ret = topo_mod_seterrno(mod, EMOD_NVL_INVAL); 232 goto out; 233 234 } 235 topo_mod_dprintf(mod, "%s: Setting LED mode to %s\n", __func__, 236 ledmode ? "ON" : "OFF"); 237 } else { 238 /* 239 * Get the LED mode 240 */ 241 set = B_FALSE; 242 topo_mod_dprintf(mod, "%s: Getting LED mode\n", __func__); 243 } 244 ret = do_led_control(mod, devctl, ahci_port, fac_type, &ledmode, set); 245 246 if (ret == -1) { 247 topo_mod_dprintf(mod, "%s: do_led_control failed", __func__); 248 ret = topo_mod_seterrno(mod, EMOD_UNKNOWN); 249 goto out; 250 } 251 252 if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0 || 253 nvlist_add_string(nvl, TOPO_PROP_VAL_NAME, TOPO_LED_MODE) != 0 || 254 nvlist_add_uint32(nvl, TOPO_PROP_VAL_TYPE, TOPO_TYPE_UINT32) != 0 || 255 nvlist_add_uint32(nvl, TOPO_PROP_VAL_VAL, ledmode) != 0) { 256 topo_mod_dprintf(mod, "%s: Failed to allocate 'out' nvlist\n", 257 __func__); 258 nvlist_free(nvl); 259 ret = topo_mod_seterrno(mod, EMOD_NOMEM); 260 goto out; 261 } 262 *nvout = nvl; 263 264 out: 265 topo_mod_strfree(mod, devctl); 266 return (ret); 267 } 268 269 static int 270 fac_prov_ahci_enum(topo_mod_t *mod, tnode_t *rnode, const char *name, 271 topo_instance_t min, topo_instance_t max, void *arg, void *unused) 272 { 273 274 if (topo_node_flags(rnode) == TOPO_NODE_FACILITY) { 275 if (topo_method_register(mod, rnode, ahci_fac_methods) != 0) { 276 topo_mod_dprintf(mod, "%s: topo_method_register() " 277 "failed: %s", __func__, topo_mod_errmsg(mod)); 278 return (-1); 279 } 280 return (0); 281 } 282 283 topo_mod_dprintf(mod, "%s: unexpected node flags 0x%x", __func__, 284 topo_node_flags(rnode)); 285 return (-1); 286 } 287