int board_cut_off_battery(void) { int rv; /* Ship mode command must be sent twice to take effect */ rv = sb_write(SB_MANUFACTURER_ACCESS, SB_SHUTDOWN_DATA); if (rv != EC_SUCCESS) return EC_RES_ERROR; rv = sb_write(SB_MANUFACTURER_ACCESS, SB_SHUTDOWN_DATA); return rv ? EC_RES_ERROR : EC_RES_SUCCESS; }
static int cutoff(void) { int rv; /* Ship mode command must be sent twice to take effect */ rv = sb_write(SB_MANUFACTURER_ACCESS, SB_SHUTDOWN_DATA); if (rv != EC_SUCCESS) return rv; return sb_write(SB_MANUFACTURER_ACCESS, SB_SHUTDOWN_DATA); }
enum battery_disconnect_state battery_get_disconnect_state(void) { uint8_t data[6]; int rv; /* * Take note if we find that the battery isn't in disconnect state, * and always return NOT_DISCONNECTED without probing the battery. * This assumes the battery will not go to disconnect state during * runtime. */ static int not_disconnected; if (not_disconnected) return BATTERY_NOT_DISCONNECTED; if (extpower_is_present()) { /* Check if battery charging + discharging is disabled. */ rv = sb_write(SB_MANUFACTURER_ACCESS, PARAM_OPERATION_STATUS); if (rv) return BATTERY_DISCONNECT_ERROR; rv = sb_read_string(I2C_PORT_BATTERY, BATTERY_ADDR, SB_ALT_MANUFACTURER_ACCESS, data, 6); if (rv || (~data[3] & (BATTERY_DISCHARGING_DISABLED | BATTERY_CHARGING_DISABLED))) { not_disconnected = 1; return BATTERY_NOT_DISCONNECTED; } /* * Battery is neither charging nor discharging. Verify that * we didn't enter this state due to a safety fault. */ rv = sb_write(SB_MANUFACTURER_ACCESS, PARAM_SAFETY_STATUS); if (rv) return BATTERY_DISCONNECT_ERROR; rv = sb_read_string(I2C_PORT_BATTERY, BATTERY_ADDR, SB_ALT_MANUFACTURER_ACCESS, data, 6); if (rv || data[2] || data[3] || data[4] || data[5]) return BATTERY_DISCONNECT_ERROR; /* No safety fault, battery is disconnected */ return BATTERY_DISCONNECTED; } not_disconnected = 1; return BATTERY_NOT_DISCONNECTED; }
static void request_handler (aeEventLoop * el, int fd, void *data, int mask) { workerState *ws = (workerState *) data; simpleBuf *sb = &ws->out; int tw; if (ws->reconn_after > 0LL) { ws->reconn_after = 0LL; } tw = sb_write (sb, fd); if (tw < 0) { fprintf (stderr, "[smr-client] failed to sb_write:%d\n", tw); free_connection (ws, 1); return; } if (sb->ep - sb->cp == 0) { sb->cp = sb->buf; //reset sb aeDeleteFileEvent (el, fd, AE_WRITABLE); ws->wprepared = 0; } return; }
static int test_low_battery(void) { test_setup(1); ccprintf("[CHARGING TEST] Low battery with AC\n"); sb_write(SB_RELATIVE_STATE_OF_CHARGE, 2); wait_charging_state(); mock_chipset_state = CHIPSET_STATE_SOFT_OFF; hook_notify(HOOK_CHIPSET_SHUTDOWN); TEST_ASSERT(!is_hibernated); ccprintf("[CHARGING TEST] Low battery shutdown S0->S5\n"); mock_chipset_state = CHIPSET_STATE_ON; hook_notify(HOOK_CHIPSET_PRE_INIT); hook_notify(HOOK_CHIPSET_STARTUP); gpio_set_level(GPIO_AC_PRESENT, 0); is_hibernated = 0; sb_write(SB_CURRENT, -1000); sb_write(SB_RELATIVE_STATE_OF_CHARGE, 2); wait_charging_state(); mock_chipset_state = CHIPSET_STATE_SOFT_OFF; hook_notify(HOOK_CHIPSET_SHUTDOWN); wait_charging_state(); TEST_ASSERT(is_hibernated); ccprintf("[CHARGING TEST] Low battery shutdown S5\n"); is_hibernated = 0; sb_write(SB_RELATIVE_STATE_OF_CHARGE, 10); wait_charging_state(); sb_write(SB_RELATIVE_STATE_OF_CHARGE, 2); wait_charging_state(); TEST_ASSERT(is_hibernated); ccprintf("[CHARGING TEST] Low battery AP shutdown\n"); is_shutdown = 0; mock_chipset_state = CHIPSET_STATE_ON; sb_write(SB_RELATIVE_STATE_OF_CHARGE, 10); gpio_set_level(GPIO_AC_PRESENT, 1); sb_write(SB_CURRENT, 1000); wait_charging_state(); gpio_set_level(GPIO_AC_PRESENT, 0); sb_write(SB_CURRENT, -1000); sb_write(SB_RELATIVE_STATE_OF_CHARGE, 2); wait_charging_state(); usleep(32 * SECOND); wait_charging_state(); TEST_ASSERT(is_shutdown); return EC_SUCCESS; }
static int ctl_msg_recv(void) { int len; uint8_t buf[1024]; while( (len = recv(ctrl_fd, buf, sizeof(buf), 0)) > 0){ sb_write(sb_in, buf, len); } return 0; }
static int test_high_temp_battery(void) { test_setup(1); ccprintf("[CHARGING TEST] High battery temperature shutdown\n"); ev_clear(EC_HOST_EVENT_BATTERY_SHUTDOWN); sb_write(SB_TEMPERATURE, CELSIUS_TO_DECI_KELVIN(90)); wait_charging_state(); TEST_ASSERT(ev_is_set(EC_HOST_EVENT_BATTERY_SHUTDOWN)); TEST_ASSERT(!is_shutdown); sleep(CONFIG_BATTERY_CRITICAL_SHUTDOWN_TIMEOUT); TEST_ASSERT(is_shutdown); ccprintf("[CHARGING TEST] High battery temp S0->S5 hibernate\n"); mock_chipset_state = CHIPSET_STATE_SOFT_OFF; wait_charging_state(); TEST_ASSERT(is_hibernated); return EC_SUCCESS; }
static int test_low_battery_hostevents(void) { int state; test_setup(0); ccprintf("[CHARGING TEST] Low battery host events\n"); /* You know you make me wanna */ sb_write(SB_RELATIVE_STATE_OF_CHARGE, BATTERY_LEVEL_LOW + 1); ev_clear(EC_HOST_EVENT_BATTERY_LOW); ev_clear(EC_HOST_EVENT_BATTERY_CRITICAL); ev_clear(EC_HOST_EVENT_BATTERY_SHUTDOWN); state = wait_charging_state(); TEST_ASSERT(state == PWR_STATE_DISCHARGE); TEST_ASSERT(ev_is_clear(EC_HOST_EVENT_BATTERY_LOW)); TEST_ASSERT(ev_is_clear(EC_HOST_EVENT_BATTERY_CRITICAL)); TEST_ASSERT(ev_is_clear(EC_HOST_EVENT_BATTERY_SHUTDOWN)); /* (Shout) a little bit louder now */ sb_write(SB_RELATIVE_STATE_OF_CHARGE, BATTERY_LEVEL_LOW - 1); state = wait_charging_state(); TEST_ASSERT(state == PWR_STATE_DISCHARGE); TEST_ASSERT(ev_is_set(EC_HOST_EVENT_BATTERY_LOW)); TEST_ASSERT(ev_is_clear(EC_HOST_EVENT_BATTERY_CRITICAL)); TEST_ASSERT(ev_is_clear(EC_HOST_EVENT_BATTERY_SHUTDOWN)); TEST_ASSERT(!is_shutdown); /* (Shout) a little bit louder now */ sb_write(SB_RELATIVE_STATE_OF_CHARGE, BATTERY_LEVEL_CRITICAL + 1); state = wait_charging_state(); TEST_ASSERT(state == PWR_STATE_DISCHARGE); TEST_ASSERT(ev_is_set(EC_HOST_EVENT_BATTERY_LOW)); TEST_ASSERT(ev_is_clear(EC_HOST_EVENT_BATTERY_CRITICAL)); TEST_ASSERT(ev_is_clear(EC_HOST_EVENT_BATTERY_SHUTDOWN)); TEST_ASSERT(!is_shutdown); /* (Shout) a little bit louder now */ sb_write(SB_RELATIVE_STATE_OF_CHARGE, BATTERY_LEVEL_CRITICAL - 1); state = wait_charging_state(); TEST_ASSERT(state == PWR_STATE_DISCHARGE); TEST_ASSERT(ev_is_set(EC_HOST_EVENT_BATTERY_LOW)); TEST_ASSERT(ev_is_set(EC_HOST_EVENT_BATTERY_CRITICAL)); TEST_ASSERT(ev_is_clear(EC_HOST_EVENT_BATTERY_SHUTDOWN)); TEST_ASSERT(!is_shutdown); /* (Shout) a little bit louder now */ sb_write(SB_RELATIVE_STATE_OF_CHARGE, BATTERY_LEVEL_SHUTDOWN + 1); state = wait_charging_state(); TEST_ASSERT(state == PWR_STATE_DISCHARGE); TEST_ASSERT(ev_is_set(EC_HOST_EVENT_BATTERY_LOW)); TEST_ASSERT(ev_is_set(EC_HOST_EVENT_BATTERY_CRITICAL)); TEST_ASSERT(ev_is_clear(EC_HOST_EVENT_BATTERY_SHUTDOWN)); TEST_ASSERT(!is_shutdown); /* (Shout) a little bit louder now */ sb_write(SB_RELATIVE_STATE_OF_CHARGE, BATTERY_LEVEL_SHUTDOWN - 1); state = wait_charging_state(); TEST_ASSERT(state == PWR_STATE_DISCHARGE); TEST_ASSERT(ev_is_set(EC_HOST_EVENT_BATTERY_LOW)); TEST_ASSERT(ev_is_set(EC_HOST_EVENT_BATTERY_CRITICAL)); /* hey-hey-HEY-hey. Doesn't immediately shut down */ TEST_ASSERT(ev_is_set(EC_HOST_EVENT_BATTERY_SHUTDOWN)); TEST_ASSERT(!is_shutdown); /* after a while, the AP should shut down */ sleep(LOW_BATTERY_SHUTDOWN_TIMEOUT); TEST_ASSERT(is_shutdown); return EC_SUCCESS; }
static int test_charge_state(void) { enum charge_state state; uint32_t flags; /* On AC */ test_setup(1); ccprintf("[CHARGING TEST] AC on\n"); /* Detach battery, charging error */ ccprintf("[CHARGING TEST] Detach battery\n"); TEST_ASSERT(test_detach_i2c(I2C_PORT_BATTERY, BATTERY_ADDR) == EC_SUCCESS); msleep(BATTERY_DETACH_DELAY); state = wait_charging_state(); TEST_ASSERT(state == PWR_STATE_ERROR); /* Attach battery again, charging */ ccprintf("[CHARGING TEST] Attach battery\n"); test_attach_i2c(I2C_PORT_BATTERY, BATTERY_ADDR); /* And changing full capacity should trigger a host event */ ev_clear(EC_HOST_EVENT_BATTERY); sb_write(SB_FULL_CHARGE_CAPACITY, 0xeff0); state = wait_charging_state(); TEST_ASSERT(state == PWR_STATE_CHARGE); TEST_ASSERT(ev_is_set(EC_HOST_EVENT_BATTERY)); /* Unplug AC, discharging at 1000mAh */ ccprintf("[CHARGING TEST] AC off\n"); gpio_set_level(GPIO_AC_PRESENT, 0); sb_write(SB_CURRENT, -1000); state = wait_charging_state(); TEST_ASSERT(state == PWR_STATE_DISCHARGE); flags = charge_get_flags(); TEST_ASSERT(!(flags & CHARGE_FLAG_EXTERNAL_POWER)); TEST_ASSERT(!(flags & CHARGE_FLAG_FORCE_IDLE)); /* Discharging waaaay overtemp is ignored */ ccprintf("[CHARGING TEST] AC off, batt temp = 0xffff\n"); gpio_set_level(GPIO_AC_PRESENT, 0); sb_write(SB_CURRENT, -1000); state = wait_charging_state(); TEST_ASSERT(state == PWR_STATE_DISCHARGE); sb_write(SB_TEMPERATURE, 0xffff); state = wait_charging_state(); TEST_ASSERT(!is_shutdown); TEST_ASSERT(state == PWR_STATE_DISCHARGE); sb_write(SB_TEMPERATURE, CELSIUS_TO_DECI_KELVIN(40)); /* Discharging overtemp */ ccprintf("[CHARGING TEST] AC off, batt temp = 90 C\n"); gpio_set_level(GPIO_AC_PRESENT, 0); sb_write(SB_CURRENT, -1000); state = wait_charging_state(); TEST_ASSERT(state == PWR_STATE_DISCHARGE); sb_write(SB_TEMPERATURE, CELSIUS_TO_DECI_KELVIN(90)); state = wait_charging_state(); TEST_ASSERT(is_shutdown); TEST_ASSERT(state == PWR_STATE_DISCHARGE); sb_write(SB_TEMPERATURE, CELSIUS_TO_DECI_KELVIN(40)); /* Force idle */ ccprintf("[CHARGING TEST] AC on, force idle\n"); gpio_set_level(GPIO_AC_PRESENT, 1); sb_write(SB_CURRENT, 1000); state = wait_charging_state(); TEST_ASSERT(state == PWR_STATE_CHARGE); flags = charge_get_flags(); TEST_ASSERT(flags & CHARGE_FLAG_EXTERNAL_POWER); TEST_ASSERT(!(flags & CHARGE_FLAG_FORCE_IDLE)); charge_control(CHARGE_CONTROL_IDLE); state = wait_charging_state(); TEST_ASSERT(state == PWR_STATE_IDLE); flags = charge_get_flags(); TEST_ASSERT(flags & CHARGE_FLAG_EXTERNAL_POWER); TEST_ASSERT(flags & CHARGE_FLAG_FORCE_IDLE); charge_control(CHARGE_CONTROL_NORMAL); state = wait_charging_state(); TEST_ASSERT(state == PWR_STATE_CHARGE); /* Force discharge */ ccprintf("[CHARGING TEST] AC on, force discharge\n"); gpio_set_level(GPIO_AC_PRESENT, 1); sb_write(SB_CURRENT, 1000); charge_control(CHARGE_CONTROL_DISCHARGE); state = wait_charging_state(); TEST_ASSERT(state == PWR_STATE_IDLE); TEST_ASSERT(is_force_discharge); charge_control(CHARGE_CONTROL_NORMAL); state = wait_charging_state(); TEST_ASSERT(state == PWR_STATE_CHARGE); TEST_ASSERT(!is_force_discharge); return EC_SUCCESS; }
void sb_vfdprintf(int fd, const char *format, va_list args) { const char *fmt = format; const char *format_end = format + strlen(format); const char *conv = fmt; while (fmt < format_end) { conv = memchr(fmt, '%', format_end - fmt); if (conv != fmt) { size_t out_bytes = (conv ? conv : format_end) - fmt; sb_write(fd, fmt, out_bytes); if (!conv) return; } char buf[50]; size_t idx; unsigned int u, x; char hex_base; size_t padding = 0; unsigned int modifiers = 0; eat_more: switch (conv[1]) { default: sb_fdprintf(fd, "{invalid conversion specifier in string: %s}", format); break; case '%': if (modifiers) { inv_modifier: sb_fdprintf(fd, "{invalid modifier in string: %s}", format); } sb_write(fd, conv, 1); break; case 'z': ++conv; if (modifiers & MOD_SIZE_T) goto inv_modifier; modifiers |= MOD_SIZE_T; goto eat_more; case '*': ++conv; if (modifiers & MOD_STAR) goto inv_modifier; modifiers |= MOD_STAR; padding = va_arg(args, int); goto eat_more; case 'c': { char c = va_arg(args, int); sb_write(fd, &c, 1); break; } case 's': { char *s = va_arg(args, char *); if (!s) s = "(null)"; size_t len = strlen(s); while (len < padding--) sb_write(fd, " ", 1); sb_write(fd, s, len); break; } case 'd': case 'i': { long i; if (modifiers & MOD_SIZE_T) i = va_arg(args, ssize_t); else i = va_arg(args, int); u = i; if (i < 0) { sb_write(fd, "-", 1); u = i * -1; } out_uint: idx = 0; do { buf[idx++] = '0' + (u % 10); } while (u /= 10); while (idx--) sb_write(fd, buf+idx, 1); break; } case 'u': { u = va_arg(args, unsigned int); goto out_uint; break; } case 'X': { hex_base = 'A'; goto out_hex; } case 'x': { hex_base = 'a'; out_hex: if (modifiers & MOD_SIZE_T) x = va_arg(args, size_t); else x = va_arg(args, unsigned int); out_ptr: idx = 0; do { u = x % 16; buf[idx++] = (u < 10 ? '0' + u : hex_base + u - 10); } while (x /= 16); while (idx < padding) buf[idx++] = '0'; buf[idx++] = 'x'; buf[idx++] = '0'; while (idx--) sb_write(fd, buf+idx, 1); break; } case 'p': { void *p = va_arg(args, void *); hex_base = 'a'; padding = sizeof(p) * 2; x = (unsigned long)p; goto out_ptr; } } fmt = conv + 2; } }
static int test_low_battery(void) { test_setup(1); ccprintf("[CHARGING TEST] Low battery with AC and positive current\n"); sb_write(SB_RELATIVE_STATE_OF_CHARGE, 2); sb_write(SB_CURRENT, 1000); wait_charging_state(); mock_chipset_state = CHIPSET_STATE_SOFT_OFF; hook_notify(HOOK_CHIPSET_SHUTDOWN); TEST_ASSERT(!is_hibernated); ccprintf("[CHARGING TEST] Low battery with AC and negative current\n"); sb_write(SB_CURRENT, -1000); wait_charging_state(); sleep(CONFIG_BATTERY_CRITICAL_SHUTDOWN_TIMEOUT); TEST_ASSERT(is_hibernated); ccprintf("[CHARGING TEST] Low battery shutdown S0->S5\n"); mock_chipset_state = CHIPSET_STATE_ON; hook_notify(HOOK_CHIPSET_PRE_INIT); hook_notify(HOOK_CHIPSET_STARTUP); gpio_set_level(GPIO_AC_PRESENT, 0); is_hibernated = 0; sb_write(SB_CURRENT, -1000); sb_write(SB_RELATIVE_STATE_OF_CHARGE, 2); wait_charging_state(); mock_chipset_state = CHIPSET_STATE_SOFT_OFF; hook_notify(HOOK_CHIPSET_SHUTDOWN); wait_charging_state(); /* after a while, the EC should hibernate */ sleep(CONFIG_BATTERY_CRITICAL_SHUTDOWN_TIMEOUT); TEST_ASSERT(is_hibernated); ccprintf("[CHARGING TEST] Low battery shutdown S5\n"); is_hibernated = 0; sb_write(SB_RELATIVE_STATE_OF_CHARGE, 10); wait_charging_state(); sb_write(SB_RELATIVE_STATE_OF_CHARGE, 2); wait_charging_state(); /* after a while, the EC should hibernate */ sleep(CONFIG_BATTERY_CRITICAL_SHUTDOWN_TIMEOUT); TEST_ASSERT(is_hibernated); ccprintf("[CHARGING TEST] Low battery AP shutdown\n"); is_shutdown = 0; mock_chipset_state = CHIPSET_STATE_ON; sb_write(SB_RELATIVE_STATE_OF_CHARGE, 10); gpio_set_level(GPIO_AC_PRESENT, 1); sb_write(SB_CURRENT, 1000); wait_charging_state(); gpio_set_level(GPIO_AC_PRESENT, 0); sb_write(SB_CURRENT, -1000); sb_write(SB_RELATIVE_STATE_OF_CHARGE, 2); wait_charging_state(); usleep(32 * SECOND); wait_charging_state(); TEST_ASSERT(is_shutdown); return EC_SUCCESS; }
int board_cut_off_battery(void) { return sb_write(SB_SHIP_MODE_ADDR, SB_SHIP_MODE_DATA); }
/* Returns 0 on success, non-zero on failure */ static int save_table( lua_State * L, luaamf_SaveBuffer * sb, int index ) { luaamf_SaveBuffer numeric; luaamf_SaveBuffer associative; int result = LUAAMF_ESUCCESS; int numeric_index = 1; int key_value_pairs_number = 0; { void * alloc_ud = NULL; lua_Alloc alloc_fn = lua_getallocf(L, &alloc_ud); sb_init(&numeric, alloc_fn, alloc_ud); sb_init(&associative, alloc_fn, alloc_ud); } lua_pushnil(L); while (result == LUAAMF_ESUCCESS && lua_next(L, index) != 0) { int value_pos = lua_gettop(L); /* We need absolute values */ int key_pos = value_pos - 1; if(lua_type(L, key_pos) == LUA_TNUMBER && lua_tonumber(L, key_pos) == (float)numeric_index) { /* Save enumerated value. */ result = save_value(&numeric, L, value_pos, 1); numeric_index++; } else { /* Save associative key. */ result = save_value(&associative, L, key_pos, 0); /* Save associative value. */ if (result == LUAAMF_ESUCCESS) { result = save_value(&associative, L, value_pos, 1); key_value_pairs_number++; } } if (result == LUAAMF_ESUCCESS) { /* Remove value from stack, leave key for the next iteration. */ lua_pop(L, 1); } else { return result; } } /* write serilization here */ sb_writechar(sb, LUAAMF_ARRAY); encode_int(sb, 2 * key_value_pairs_number + 1); sb_write(sb, sb_buffer(&associative, &(associative.buf_size)), associative.buf_size); sb_writechar(sb, 0x001); sb_write(sb, sb_buffer(&numeric, &(numeric.buf_size)), numeric.buf_size); result = LUAAMF_ESUCCESS; sb_destroy(&numeric); sb_destroy(&associative); return result; }
static void cutoff(void) { /* Claim i2c and send cutoff command to battery. */ sb_write(SB_MANUFACTURER_ACCESS, PARAM_CUT_OFF); }
void sb_vfdprintf(int fd, const char *format, va_list args) { const char *fmt = format; const char *format_end = format + strlen(format); const char *conv = fmt; while (fmt < format_end) { conv = memchr(fmt, '%', format_end - fmt); if (conv != fmt) { size_t out_bytes = (conv ? conv : format_end) - fmt; sb_write(fd, fmt, out_bytes); if (!conv) return; } char buf[50]; size_t idx; unsigned long long int u, x; char hex_base; size_t padding = 0; unsigned int modifiers = 0; eat_more: switch (conv[1]) { default: sb_fdprintf(fd, "{invalid conversion specifier in string: %s}", format); break; case '%': if (modifiers) { inv_modifier: sb_fdprintf(fd, "{invalid modifier in string: %s}", format); } sb_write(fd, conv, 1); break; case 'l': ++conv; if (modifiers & MOD_LONG_T) { if (modifiers & MOD_LONG_LONG_T) goto inv_modifier; modifiers = (modifiers & ~MOD_LONG_T) | MOD_LONG_LONG_T; } else modifiers |= MOD_LONG_T; goto eat_more; case 'z': ++conv; if (modifiers & MOD_SIZE_T) goto inv_modifier; modifiers |= MOD_SIZE_T; goto eat_more; case '*': ++conv; if (modifiers & MOD_STAR) goto inv_modifier; modifiers |= MOD_STAR; padding = va_arg(args, int); goto eat_more; case '#': /* ignore */ ++conv; goto eat_more; case 'c': { char c = va_arg(args, int); sb_write(fd, &c, 1); break; } case 's': { char *s = va_arg(args, char *); if (!s) s = "(null)"; size_t len = strlen(s); while (len < padding--) sb_write(fd, " ", 1); sb_write(fd, s, len); break; } case 'd': case 'i': { long long i; if (modifiers & MOD_SIZE_T) i = va_arg(args, ssize_t); else if (modifiers & MOD_LONG_T) i = va_arg(args, long); else if (modifiers & MOD_LONG_LONG_T) i = va_arg(args, long long); else i = va_arg(args, int); u = i; if (i < 0) { sb_write(fd, "-", 1); u = i * -1; } out_uint: idx = 0; do { buf[idx++] = '0' + (u % 10); } while (u /= 10); while (idx--) sb_write(fd, buf+idx, 1); break; } case 'u': { if (modifiers & MOD_SIZE_T) u = va_arg(args, size_t); else if (modifiers & MOD_LONG_T) u = va_arg(args, unsigned long); else if (modifiers & MOD_LONG_LONG_T) u = va_arg(args, unsigned long long); else
static void set_battery_soc(int soc) { sb_write(SB_RELATIVE_STATE_OF_CHARGE, soc); sb_write(SB_ABSOLUTE_STATE_OF_CHARGE, soc); }