/* * AMD SGPIO LED control * Copyright (C) 2019, Advanced Micro Devices, Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if _HAVE_DMALLOC_H #include #endif #include "config.h" #include "ibpi.h" #include "list.h" #include "utils.h" #include "amd.h" #include "amd_sgpio.h" #define HOST_CAP_EMS (1 << 6) #define DECLARE_SGPIO(type, name, shift, mask) \ uint32_t _##type##_##name##_shift = shift; \ uint64_t _##type##_##name##_mask = mask << shift; \ \ static void set_sgpio_##type##_##name(sgpio_##type##_t * hdr, \ sgpio_##type##_t val) \ { \ *hdr |= (val << _##type##_##name##_shift); \ } \ \ static uint32_t get_sgpio_##type##_##name(sgpio_##type##_t * hdr)\ { \ return (*hdr & _##type##_##name##_mask) \ >> _##type##_##name##_shift; \ } #define DECLARE_SGPIO_RO(type, name, shift, mask) \ uint32_t _##type##_##name##_shift = shift; \ uint64_t _##type##_##name##_mask = mask; \ \ static uint32_t get_sgpio_##type##_##name(sgpio_##type##_t * hdr)\ { \ return (*hdr & _##type##_##name##_mask) \ >> _##type##_##name##_shift; \ } typedef uint32_t sgpio_hdr_t; #define SGPIO_HDR_MSG_TYPE_SGPIO 0x03 DECLARE_SGPIO(hdr, msg_type, 4, 0xF) DECLARE_SGPIO(hdr, data_size, 8, 0xFF) DECLARE_SGPIO(hdr, msg_size, 16, 0xFF) typedef uint64_t sgpio_req_t; #define SGPIO_REQ_REG_TYPE_CFG 0x00 #define SGPIO_REQ_REG_TYPE_TX 0x03 #define SGPIO_REQ_REG_TYPE_AMD 0xC0 DECLARE_SGPIO(req, frame_type, 0, 0xFFL) DECLARE_SGPIO(req, function, 8, 0xFFL) DECLARE_SGPIO(req, reg_type, 16, 0xFFL) DECLARE_SGPIO(req, reg_index, 24, 0xFFL) DECLARE_SGPIO(req, reg_count, 32, 0xFFL) typedef uint32_t sgpio_amd_t; DECLARE_SGPIO(amd, initiator, 0, 0x1) DECLARE_SGPIO(amd, polarity_flip, 4, 0x1) DECLARE_SGPIO(amd, return_to_normal, 5, 0x1) DECLARE_SGPIO(amd, bypass_enable, 6, 0x1) typedef uint64_t sgpio_cfg_t; DECLARE_SGPIO_RO(cfg, version, 8, 0xF); DECLARE_SGPIO_RO(cfg, gp_reg_count, 16, 0xF); DECLARE_SGPIO_RO(cfg, cfg_reg_count, 20, 0x7); DECLARE_SGPIO(cfg, gpio_enable, 23, 0x1); DECLARE_SGPIO_RO(cfg, drive_count, 24, 0xFF); DECLARE_SGPIO(cfg, blink_gen_a, 40, 0xFL); DECLARE_SGPIO(cfg, blink_gen_b, 44, 0xFL); DECLARE_SGPIO(cfg, max_on, 48, 0xFL); DECLARE_SGPIO(cfg, force_off, 52, 0xFL); DECLARE_SGPIO(cfg, stretch_on, 56, 0xFL); DECLARE_SGPIO(cfg, stretch_off, 60, 0xFL); #define DECLARE_LED(name, shift, mask) \ uint32_t _led_##name##_shift = shift; \ uint64_t _led_##name##_mask = mask; \ \ static void set_##name##_led(drive_led_t *hdr, uint32_t val) \ { \ *hdr |= (val << _led_##name##_shift); \ } \ \ static uint32_t get_##name##_led(drive_led_t *hdr) \ { \ return (*hdr & _led_##name##_mask) >> _led_##name##_shift;\ } typedef uint8_t drive_led_t; DECLARE_LED(error, 0, 0x07); DECLARE_LED(locate, 3, 0x18); DECLARE_LED(activity, 5, 0xE0); typedef struct sgpio_transmit_register { drive_led_t drive[4]; } __attribute__ ((__packed__)) sgpio_tx_t; struct amd_register { sgpio_hdr_t hdr; sgpio_req_t req; sgpio_amd_t amd; } __attribute__ ((__packed__)); struct config_register { sgpio_hdr_t hdr; sgpio_req_t req; sgpio_cfg_t cfg; } __attribute__ ((__packed__)); struct transmit_register { sgpio_hdr_t hdr; sgpio_req_t req; sgpio_tx_t tx; } __attribute__ ((__packed__)); static uint8_t ibpi_pattern[] = { [IBPI_PATTERN_NONE] = 0x00, [IBPI_PATTERN_REBUILD] = 0x07, [IBPI_PATTERN_HOTSPARE] = 0x02, [IBPI_PATTERN_PFA] = 0x03, [IBPI_PATTERN_FAILED_DRIVE] = 0x00, [IBPI_PATTERN_LOCATE] = 0x07, [IBPI_PATTERN_LOCATE_OFF] = 0x00 }; struct drive_leds { drive_led_t error; drive_led_t locate; drive_led_t activity; } __attribute__ ((__packed__)); #define INIT_LED(e, l, a) {.error = e, .locate = l, .activity = a} static struct drive_leds tx_leds_blink_gen_a[] = { [IBPI_PATTERN_NORMAL] = INIT_LED(0, 0, 0b101), [IBPI_PATTERN_ONESHOT_NORMAL] = INIT_LED(0, 0, 0b101), [IBPI_PATTERN_REBUILD] = INIT_LED(0b010, 0, 0), [IBPI_PATTERN_HOTSPARE] = INIT_LED(0b010, 0, 0), [IBPI_PATTERN_PFA] = INIT_LED(0b010, 0, 0), [IBPI_PATTERN_FAILED_DRIVE] = INIT_LED(0b001, 0, 0), [IBPI_PATTERN_LOCATE] = INIT_LED(0b010, 0, 0b010), [IBPI_PATTERN_LOCATE_OFF] = INIT_LED(0, 0, 0b101), [99] = INIT_LED(0, 0, 0b101), }; static struct drive_leds tx_leds_blink_gen_b[] = { [IBPI_PATTERN_NORMAL] = INIT_LED(0, 0, 0b101), [IBPI_PATTERN_ONESHOT_NORMAL] = INIT_LED(0, 0, 0b101), [IBPI_PATTERN_REBUILD] = INIT_LED(0b110, 0, 0), [IBPI_PATTERN_HOTSPARE] = INIT_LED(0b110, 0, 0), [IBPI_PATTERN_PFA] = INIT_LED(0b110, 0, 0), [IBPI_PATTERN_FAILED_DRIVE] = INIT_LED(0b001, 0, 0), [IBPI_PATTERN_LOCATE] = INIT_LED(0b110, 0, 0b110), [IBPI_PATTERN_LOCATE_OFF] = INIT_LED(0, 0, 0b101), [99] = INIT_LED(0, 0, 0b101), }; #define CACHE_SZ 1024 int cache_fd = 0; struct cache_entry { struct drive_leds leds[4]; uint8_t blink_gen_a; uint8_t blink_gen_b; uint16_t reserved; } __attribute__ ((__packed__)); static struct cache_entry *sgpio_cache; static void _put_cache(void) { munmap(sgpio_cache, CACHE_SZ); if (cache_fd) { flock(cache_fd, LOCK_UN); fsync(cache_fd); close(cache_fd); cache_fd = 0; } } static int _open_and_map_cache(void) { struct stat sbuf; if (cache_fd) return 0; cache_fd = shm_open("/ledmon_amd_sgpio_cache", O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); if (cache_fd < 1) { log_error("Couldn't open SGPIO cache: %s", strerror(errno)); return -1; } flock(cache_fd, LOCK_EX); fstat(cache_fd, &sbuf); if (sbuf.st_size == 0) { if (ftruncate(cache_fd, CACHE_SZ) != 0) { log_error("Couldn't truncate SGPIO cache: %s", strerror(errno)); return -1; } } sgpio_cache = mmap(NULL, CACHE_SZ, PROT_READ | PROT_WRITE, MAP_SHARED, cache_fd, 0); if (sgpio_cache == MAP_FAILED) { log_error("Couldn't map SGPIO cache: %s", strerror(errno)); _put_cache(); return -1; } return 0; } static struct cache_entry *_get_cache(struct amd_drive *drive) { int rc, index; rc = _open_and_map_cache(); if (rc) return NULL; /* The sgpio_cache is just an array of cache_entry structs, and * each cache_entry describes the drive LED settings for 4 drives. * To find the corresponding cache entry for an ata port we need * to first round down to the nearest multiple of 4, then divide * by four. This gives us the following mapping; * cache_entry[0] => drive 0 to drive 3 * cache_entry[1] => drive 4 to drive 7 * cache_entry[n] => drive (4*n) to drive (4*n + 3) */ index = (drive->ata_port / 4); return &sgpio_cache[index]; } static int _send_sgpio_register(const char *em_buffer_path, void *reg, int reg_len) { int count; int saved_errno; int retries = 3; do { int fd = open(em_buffer_path, O_WRONLY); if (fd < 0) { log_error("Couldn't open EM buffer %s: %s", em_buffer_path, strerror(errno)); return -1; } count = write(fd, reg, reg_len); saved_errno = errno; close(fd); /* Insert small sleep to ensure hardware has enough time to * see the register change and read it. Without the sleep * multiple writes can result in an EBUSY return because * hardware has not cleared the EM_CTL_TM (Transmit Message) * bit. */ usleep(1000); if (count == reg_len || saved_errno != EBUSY) break; } while (--retries != 0); if (count != reg_len) { log_error("Couldn't write SGPIO register: %s", strerror(saved_errno)); return -1; } return 0; } static sgpio_hdr_t _init_sgpio_hdr(int data_size, int msg_size) { sgpio_hdr_t hdr = 0; set_sgpio_hdr_msg_type(&hdr, SGPIO_HDR_MSG_TYPE_SGPIO); set_sgpio_hdr_data_size(&hdr, data_size); set_sgpio_hdr_msg_size(&hdr, msg_size); return hdr; } static void _dump_sgpio_hdr(const char *type, sgpio_hdr_t hdr) { log_debug("%s SGPIO Header: %08x\n", type, hdr); log_debug(REG_FMT_2, "message type", get_sgpio_hdr_msg_type(&hdr), "data size", get_sgpio_hdr_data_size(&hdr)); log_debug(REG_FMT_1, "message size", get_sgpio_hdr_msg_size(&hdr)); } static sgpio_req_t _init_sgpio_req(int frame_type, int function, int reg_type, int reg_index, int reg_count) { sgpio_req_t req = 0; set_sgpio_req_frame_type(&req, frame_type); set_sgpio_req_function(&req, function); set_sgpio_req_reg_type(&req, reg_type); set_sgpio_req_reg_index(&req, reg_index); set_sgpio_req_reg_count(&req, reg_count); return req; } static void _dump_sgpio_req(const char *type, sgpio_req_t req) { uint32_t *r = (uint32_t *)&req; log_debug("%s SGPIO Request Register: %08x %08x\n", type, r[0], r[1]); log_debug(REG_FMT_2, "frame type", get_sgpio_req_frame_type(&req), "function", get_sgpio_req_function(&req)); log_debug(REG_FMT_2, "register type", get_sgpio_req_reg_type(&req), "register index", get_sgpio_req_reg_index(&req)); log_debug(REG_FMT_1, "register count", get_sgpio_req_reg_count(&req)); } static sgpio_cfg_t _init_sgpio_cfg(int gpio_enable, int blink_a, int blink_b, int force_off, int max_on, int stretch_off, int stretch_on) { sgpio_cfg_t cfg = 0; if (gpio_enable) set_sgpio_cfg_gpio_enable(&cfg, 1); set_sgpio_cfg_blink_gen_a(&cfg, blink_a); set_sgpio_cfg_blink_gen_b(&cfg, blink_b); set_sgpio_cfg_max_on(&cfg, max_on); set_sgpio_cfg_force_off(&cfg, force_off); set_sgpio_cfg_stretch_on(&cfg, stretch_on); set_sgpio_cfg_stretch_off(&cfg, stretch_off); return cfg; } static void _dump_sgpio_cfg(const char *type, sgpio_cfg_t cfg) { uint32_t *r = (uint32_t *)&cfg; log_debug("%s SGPIO Configuration Register: %08x %08x\n", type, r[0], r[1]); log_debug(REG_FMT_2, "version", get_sgpio_cfg_version(&cfg), "gp register count", get_sgpio_cfg_gp_reg_count(&cfg)); log_debug(REG_FMT_2, "cfg register count", get_sgpio_cfg_cfg_reg_count(&cfg), "gpio enabled", get_sgpio_cfg_gpio_enable(&cfg)); log_debug(REG_FMT_2, "drive count", get_sgpio_cfg_drive_count(&cfg), "blink gen rate A", get_sgpio_cfg_blink_gen_a(&cfg)); log_debug(REG_FMT_2, "blink gen rate B", get_sgpio_cfg_blink_gen_b(&cfg), "force activity off", get_sgpio_cfg_force_off(&cfg)); log_debug(REG_FMT_2, "max activity on", get_sgpio_cfg_max_on(&cfg), "stretch activity off", get_sgpio_cfg_stretch_off(&cfg)); log_debug(REG_FMT_1, "stretch activity on", get_sgpio_cfg_stretch_on(&cfg)); } static sgpio_amd_t _init_sgpio_amd(int initiator, int polarity, int bypass, int normal) { sgpio_amd_t amd = 0; set_sgpio_amd_initiator(&amd, initiator); set_sgpio_amd_polarity_flip(&amd, polarity); set_sgpio_amd_bypass_enable(&amd, bypass); set_sgpio_amd_return_to_normal(&amd, normal); return amd; } static void _dump_sgpio_amd(const char *type, sgpio_amd_t amd) { log_debug("%s SGPIO AMD Register: %08x\n", type, amd); log_debug(REG_FMT_2, "initiator", get_sgpio_amd_initiator(&amd), "polarity", get_sgpio_amd_polarity_flip(&amd)); log_debug(REG_FMT_2, "bypass enable", get_sgpio_amd_bypass_enable(&amd), "return to normal", get_sgpio_amd_return_to_normal(&amd)); } static int _write_cfg_register(const char *em_buffer_path, struct cache_entry *cache, int ibpi) { struct config_register cfg_reg; cfg_reg.hdr = _init_sgpio_hdr(0, sizeof(cfg_reg)); cfg_reg.req = _init_sgpio_req(0x40, 0x82, SGPIO_REQ_REG_TYPE_CFG, 0, 2); if (cache->blink_gen_a) cache->blink_gen_b = ibpi_pattern[ibpi]; else cache->blink_gen_a = ibpi_pattern[ibpi]; cfg_reg.cfg = _init_sgpio_cfg(1, cache->blink_gen_a, cache->blink_gen_b, 2, 1, 0, 0); _dump_sgpio_hdr("CFG", cfg_reg.hdr); _dump_sgpio_req("CFG", cfg_reg.req); _dump_sgpio_cfg("CFG", cfg_reg.cfg); return _send_sgpio_register(em_buffer_path, &cfg_reg, sizeof(cfg_reg)); } static void _dump_sgpio_tx(const char *type, sgpio_tx_t tx) { int i; log_debug("%s SGPIO TX Register: %08x\n", type, tx); for (i = 0; i < 4; i++) { log_debug("\tdrive %d: error %x, locate %x, activity %x\n", i, get_error_led(&tx.drive[i]), get_locate_led(&tx.drive[i]), get_activity_led(&tx.drive[i])); } } static int _write_tx_register(const char *em_buffer_path, struct transmit_register *tx_reg) { tx_reg->hdr = _init_sgpio_hdr(0, sizeof(*tx_reg)); tx_reg->req = _init_sgpio_req(0x40, 0x82, SGPIO_REQ_REG_TYPE_TX, 0, 1); _dump_sgpio_hdr("TX", tx_reg->hdr); _dump_sgpio_req("TX", tx_reg->req); _dump_sgpio_tx("TX", tx_reg->tx); return _send_sgpio_register(em_buffer_path, tx_reg, sizeof(*tx_reg)); } static void _set_tx_drive_leds(struct transmit_register *tx_reg, struct cache_entry *cache, int drive_bay, int ibpi) { int i; struct drive_leds *leds; memset(&tx_reg->tx, 0, sizeof(tx_reg->tx)); if (cache->blink_gen_a) leds = &tx_leds_blink_gen_b[ibpi]; else leds = &tx_leds_blink_gen_a[ibpi]; cache->leds[drive_bay].error = leds->error; cache->leds[drive_bay].locate = leds->locate; cache->leds[drive_bay].activity = leds->activity; for (i = 0; i < 4; i++) { set_error_led(&tx_reg->tx.drive[i], cache->leds[i].error); set_locate_led(&tx_reg->tx.drive[i], cache->leds[i].locate); set_activity_led(&tx_reg->tx.drive[i], cache->leds[i].activity); } } static int _init_tx_drive_leds(struct transmit_register *tx_reg, struct cache_entry *cache) { int i; int init_done = 0; memset(tx_reg, 0, sizeof(*tx_reg)); for (i = 0; i < 4; i++) { if (cache->leds[i].error || cache->leds[i].locate || cache->leds[i].activity) continue; _set_tx_drive_leds(tx_reg, cache, i, 99); init_done = 1; } return init_done; } static int _write_amd_register(const char *em_buffer_path, struct amd_drive *drive) { struct amd_register amd_reg; amd_reg.hdr = _init_sgpio_hdr(0, sizeof(amd_reg)); amd_reg.req = _init_sgpio_req(0x40, 0x82, SGPIO_REQ_REG_TYPE_AMD, 0, 1); amd_reg.amd = _init_sgpio_amd(drive->initiator, 0, 1, 1); _dump_sgpio_hdr("AMD", amd_reg.hdr); _dump_sgpio_req("AMD", amd_reg.req); _dump_sgpio_amd("AMD", amd_reg.amd); return _send_sgpio_register(em_buffer_path, &amd_reg, sizeof(amd_reg)); } static int _get_amd_sgpio_drive(const char *start_path, struct amd_drive *drive) { char *a, *p; int found; char path[PATH_MAX]; char ata_dir[PATH_MAX]; /* Start the search at the ataXX directory */ strncpy(ata_dir, start_path, PATH_MAX); ata_dir[PATH_MAX - 1] = 0; a = p = strstr(ata_dir, "ata"); if (!p) { log_info("Couldn't find ata path for %s", start_path); return -1; } /* terminate the path after the ataXX/ part */ p = strchr(p, '/'); if (!p) return 1; *p = '\0'; /* skip past 'ata' to get the ata port number */ a += 3; drive->ata_port = strtoul(a, NULL, 10); found = _find_file_path(ata_dir, "port_no", path, PATH_MAX); if (!found) { log_info("Couldn't find 'port_no' for %s\n", ata_dir); return -1; } drive->port = get_int(path, -1, "port_no"); if (drive->port == -1) return -1; drive->drive_bay = 8 - drive->port; if (drive->drive_bay < 4) { drive->initiator = 1; } else { drive->drive_bay -= 4; drive->initiator = 0; } log_debug("AMD Drive: port %d, ata port %d, drive bay %d, initiator %d", drive->port, drive->ata_port, drive->drive_bay, drive->initiator); return 0; } static int _set_ibpi(struct block_device *device, enum ibpi_pattern ibpi) { int rc; struct amd_drive drive; struct transmit_register tx_reg; struct cache_entry *cache; struct cache_entry cache_dup; log_info("\n"); log_info("Setting %s...", ibpi2str(ibpi)); log_debug("\tdevice: ...%s", strstr(device->sysfs_path, "/ata")); log_debug("\tbuffer: ...%s", strstr(device->cntrl_path, "/ata")); /* Retrieve the port number and correlate that to the drive slot. * Port numbers 8..1 correspond to slot numbers 0..7. This is * further broken down (since we can only control 4 drive slots) * such that initiator = 1 for slots 0..3 and initiator = 0 for * slots 4..7, the slot value is reduced by 4 for slots 4..7 so * we can calculate the correct bits to set in the register for * that drive. */ rc = _get_amd_sgpio_drive(device->sysfs_path, &drive); if (rc) return rc; cache = _get_cache(&drive); if (!cache) return -EINVAL; /* Save copy of cache entry */ memcpy(&cache_dup, cache, sizeof(cache_dup)); rc = _write_amd_register(device->cntrl_path, &drive); if (rc) goto _set_ibpi_error; rc = _write_cfg_register(device->cntrl_path, cache, ibpi); if (rc) goto _set_ibpi_error; memset(&tx_reg, 0, sizeof(tx_reg)); _set_tx_drive_leds(&tx_reg, cache, drive.drive_bay, ibpi); rc = _write_tx_register(device->cntrl_path, &tx_reg); _set_ibpi_error: if (rc) { /* Restore saved cache entry */ memcpy(cache, &cache_dup, sizeof(*cache)); } _put_cache(); return rc; } static int _amd_sgpio_init_one(const char *path, struct amd_drive *drive, struct cache_entry *cache) { int rc, do_init; struct transmit_register tx_reg; do_init = _init_tx_drive_leds(&tx_reg, cache); if (!do_init) return 0; log_debug("Initializing host %d..%d:", drive->ata_port, drive->ata_port + 3); log_debug("\tbuffer: %s", strstr(path, "/ata")); rc = _write_amd_register(path, drive); if (rc) return rc; rc = _write_cfg_register(path, cache, IBPI_PATTERN_NONE); if (rc) return rc; return _write_tx_register(path, &tx_reg); } static int _amd_sgpio_init(const char *path) { int rc; char em_path[PATH_MAX+10]; /* 10 == strlen("/em_buffer") */ struct amd_drive drive; struct cache_entry *cache; struct cache_entry cache_dup; snprintf(em_path, PATH_MAX+10, "%s/em_buffer", path); rc = _get_amd_sgpio_drive(em_path, &drive); if (rc) { log_error("Couldn't find drive info for %s\n", em_path); return rc; } cache = _get_cache(&drive); if (!cache) { log_error("Couldn't retrieve cache"); return -1; } /* Save copy of cache entry */ memcpy(&cache_dup, cache, sizeof(cache_dup)); rc = _amd_sgpio_init_one(em_path, &drive, cache); if (rc) { log_error("SGPIO register init failed for bank %d, %s", drive.initiator, em_path); /* Restore saved cache entry */ memcpy(cache, &cache_dup, sizeof(*cache)); goto _init_amd_sgpio_err; } _put_cache(); /* AMD uses SGPIO registers to control drive LEDs in sets of 8 * drives. The initiator bit in the amd register controls which * set of four drives (0-3 or 4-7) the transmit register is * updating. * * When initializing the registers we want to do all 8 drives * so we need to reset the drive ata_port and initiator values. */ if (drive.initiator) { drive.ata_port -= 4; drive.initiator = 0; } else { drive.ata_port += 4; drive.initiator = 1; } cache = _get_cache(&drive); if (!cache) { log_error("Couldn't retrieve cache"); return -1; } /* Save copy of cache entry */ memcpy(&cache_dup, cache, sizeof(cache_dup)); rc = _amd_sgpio_init_one(em_path, &drive, cache); if (rc) { log_error("SGPIO register init failed for bank %d, %s", drive.initiator, em_path); /* Restore saved cache entry */ memcpy(cache, &cache_dup, sizeof(*cache)); } _init_amd_sgpio_err: _put_cache(); return rc; } int _amd_sgpio_em_enabled(const char *path) { char *p; int rc, found; uint32_t caps; char em_path[PATH_MAX]; /* Check that libahci module was loaded with ahci_em_messages=1 */ p = get_text("/sys/module/libahci/parameters", "ahci_em_messages"); if (!p || (p && *p == 'N')) { log_info("Kernel libahci module enclosure management messaging not enabled.\n"); if (p) free(p); return 0; } free(p); /* Find base path for enclosure management */ found = _find_file_path(path, "em_buffer", em_path, PATH_MAX); if (!found) { log_info("Couldn't find base EM path for %s\n", path); return 0; } /* Validate that enclosure management is supported */ p = get_text(em_path, "em_message_supported"); if (!p) { log_info("Couldn't get 'em_messages_supported' for %s", path); return 0; } if (strstr(p, "sgpio") == NULL) { log_info("SGPIO EM not supported for %s\n", path); free(p); return 0; } free(p); /* Verify host enclosure management capabilities */ p = get_text(em_path, "ahci_host_caps"); if (!p) { log_info("Couldn't read host capabilities for %s\n", path); return 0; } rc = sscanf(p, "%" SCNx32, &caps); free(p); if (rc <= 0) { log_info("Couldn't parse host capabilities for %s", path); return 0; } if (!(caps & HOST_CAP_EMS)) { log_info("EM not supported for %s", path); return 0; } rc = _amd_sgpio_init(em_path); return rc ? 0 : 1; } int _amd_sgpio_write(struct block_device *device, enum ibpi_pattern ibpi) { /* write only if state has changed */ if (ibpi == device->ibpi_prev) return 1; if ((ibpi < IBPI_PATTERN_NORMAL) || (ibpi > IBPI_PATTERN_LOCATE_OFF)) __set_errno_and_return(ERANGE); if ((ibpi == IBPI_PATTERN_DEGRADED) || (ibpi == IBPI_PATTERN_FAILED_ARRAY)) __set_errno_and_return(ENOTSUP); return _set_ibpi(device, ibpi); } char *_amd_sgpio_get_path(const char *cntrl_path) { int len, found; char *em_buffer_path; char tmp[PATH_MAX]; em_buffer_path = malloc(PATH_MAX); if (!em_buffer_path) { log_error("Couldn't allocate memory to get path for %s\n%s", cntrl_path, strerror(errno)); return NULL; } found = _find_file_path(cntrl_path, "em_buffer", tmp, PATH_MAX); if (!found) { log_error("Couldn't find EM buffer for %s\n", cntrl_path); free(em_buffer_path); return NULL; } len = snprintf(em_buffer_path, PATH_MAX, "%s/em_buffer", tmp); if (len < 0 || len >= PATH_MAX) { free(em_buffer_path); return NULL; } return em_buffer_path; }