/** * Creates new threads * @param s */ void threads_create( struct state * s ) { int i; size_t sz; struct threads * t; pthread_attr_t attr; if ( !( t = s->thread_mngr ) ) return; t->running = 1; t->finished = 0; // Initialise the mutex which will sync the job queue if ( pthread_mutex_init( &t->queue_lock, NULL ) ) { state_error( s, "Cannot create queue mutex" ); } // Initialise the mutex which will be used with the signal if ( pthread_mutex_init( &t->exit_lock, NULL ) ) { state_error( s, "Cannot create exit mutex" ); } // Initialse the mutex which will guard the data_primes array if ( pthread_mutex_init( &t->save_lock, NULL ) ) { state_error( s, "Cannot create save mutex" ); } // Initialise the mutex which will guard the output array if ( pthread_rwlock_init( &t->write_lock, NULL ) ) { state_error( s, "Cannot create write mutex" ); } // Initialise the cond variable which will signal // the main thread when we're done if ( pthread_cond_init( &t->exit_cond, NULL) ) { state_error( s, "Cannot create exit signal" ); } // Allocate storage space for the thread handles sz = sizeof( pthread_t ) * s->thread_count; assert( t->threads = (pthread_t*)malloc( sz ) ); memset( t->threads, 0, sz ); // Create joinable threads with 2Mb stack pthread_attr_init( &attr ); pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE ); pthread_attr_setstacksize( &attr, 2 << 20 ); for ( i = 0; i < s->thread_count; ++i ) { if ( pthread_create( &t->threads[ i ], &attr, thread_func, s ) ) state_error( s, "Cannot create thread #%d", i ); } pthread_attr_destroy( &attr ); }
/** * Battery charging task */ void charger_task(void) { struct charge_state_context *ctx = &task_ctx; timestamp_t ts; int sleep_usec = CHARGE_POLL_PERIOD_SHORT, diff_usec, sleep_next; enum charge_state new_state; uint8_t batt_flags; while (1) { #ifdef CONFIG_SB_FIRMWARE_UPDATE if (sb_fw_update_in_progress()) { task_wait_event(CHARGE_MAX_SLEEP_USEC); continue; } #endif state_common(ctx); #ifdef CONFIG_CHARGER_TIMEOUT_HOURS if (ctx->curr.state == PWR_STATE_CHARGE && ctx->charge_state_updated_time.val + CONFIG_CHARGER_TIMEOUT_HOURS * HOUR < ctx->curr.ts.val) { CPRINTS("Charge timed out after %d hours", CONFIG_CHARGER_TIMEOUT_HOURS); charge_force_idle(1); } #endif /* CONFIG_CHARGER_TIMEOUT_HOURS */ switch (ctx->prev.state) { case PWR_STATE_INIT: case PWR_STATE_REINIT: new_state = state_init(ctx); break; case PWR_STATE_IDLE0: new_state = state_idle(ctx); /* If still idling, move from IDLE0 to IDLE */ if (new_state == PWR_STATE_UNCHANGE) new_state = PWR_STATE_IDLE; break; case PWR_STATE_IDLE: new_state = state_idle(ctx); break; case PWR_STATE_DISCHARGE: new_state = state_discharge(ctx); break; case PWR_STATE_CHARGE: new_state = state_charge(ctx); if (new_state == PWR_STATE_UNCHANGE && (ctx->curr.batt.state_of_charge >= BATTERY_LEVEL_NEAR_FULL)) { /* Almost done charging */ new_state = PWR_STATE_CHARGE_NEAR_FULL; } break; case PWR_STATE_CHARGE_NEAR_FULL: new_state = state_charge(ctx); if (new_state == PWR_STATE_UNCHANGE && (ctx->curr.batt.state_of_charge < BATTERY_LEVEL_NEAR_FULL)) { /* Battery below almost-full threshold. */ new_state = PWR_STATE_CHARGE; } break; case PWR_STATE_ERROR: new_state = state_error(ctx); break; default: CPRINTS("Charge state %d undefined", ctx->curr.state); ctx->curr.state = PWR_STATE_ERROR; new_state = PWR_STATE_ERROR; } if (state_machine_force_idle && ctx->prev.state != PWR_STATE_IDLE0 && ctx->prev.state != PWR_STATE_IDLE && ctx->prev.state != PWR_STATE_INIT && ctx->prev.state != PWR_STATE_REINIT) new_state = PWR_STATE_REINIT; if (new_state) { ctx->curr.state = new_state; CPRINTS("Charge state %s -> %s after %.6ld sec", state_name[ctx->prev.state], state_name[new_state], ctx->curr.ts.val - ctx->charge_state_updated_time.val); ctx->charge_state_updated_time = ctx->curr.ts; hook_notify(HOOK_CHARGE_STATE_CHANGE); } switch (new_state) { case PWR_STATE_IDLE0: /* * First time transitioning from init -> idle. Don't * set the flags or LED yet because we may transition * to charging on the next call and we don't want to * blink the LED green. */ sleep_usec = CHARGE_POLL_PERIOD_SHORT; break; case PWR_STATE_CHARGE_NEAR_FULL: /* * Battery is almost charged. The last few percent * take a loooong time, so fall through and look like * we're charged. This mirrors similar hacks at the * ACPI/kernel/UI level. */ case PWR_STATE_IDLE: batt_flags = *ctx->memmap_batt_flags; batt_flags &= ~EC_BATT_FLAG_CHARGING; batt_flags &= ~EC_BATT_FLAG_DISCHARGING; *ctx->memmap_batt_flags = batt_flags; /* Charge done */ sleep_usec = (new_state == PWR_STATE_IDLE ? CHARGE_POLL_PERIOD_LONG : CHARGE_POLL_PERIOD_CHARGE); break; case PWR_STATE_DISCHARGE: batt_flags = *ctx->memmap_batt_flags; batt_flags &= ~EC_BATT_FLAG_CHARGING; batt_flags |= EC_BATT_FLAG_DISCHARGING; *ctx->memmap_batt_flags = batt_flags; sleep_usec = CHARGE_POLL_PERIOD_LONG; break; case PWR_STATE_CHARGE: batt_flags = *ctx->memmap_batt_flags; batt_flags |= EC_BATT_FLAG_CHARGING; batt_flags &= ~EC_BATT_FLAG_DISCHARGING; *ctx->memmap_batt_flags = batt_flags; /* Charging */ sleep_usec = CHARGE_POLL_PERIOD_CHARGE; break; case PWR_STATE_ERROR: /* Error */ sleep_usec = CHARGE_POLL_PERIOD_CHARGE; break; case PWR_STATE_UNCHANGE: /* Don't change sleep duration */ break; default: /* Other state; poll quickly and hope it goes away */ sleep_usec = CHARGE_POLL_PERIOD_SHORT; } #ifdef CONFIG_EXTPOWER_FALCO watch_adapter_closely(ctx); sleep_usec = EXTPOWER_FALCO_POLL_PERIOD; #endif /* Show charging progress in console */ charging_progress(ctx); ts = get_time(); diff_usec = (int)(ts.val - ctx->curr.ts.val); sleep_next = sleep_usec - diff_usec; if (ctx->curr.state == PWR_STATE_DISCHARGE && chipset_in_state(CHIPSET_STATE_ANY_OFF | CHIPSET_STATE_SUSPEND)) { /* * Discharging and system is off or suspended, so no * need to poll frequently. charge_hook() will wake us * up if anything important changes. */ sleep_next = CHARGE_POLL_PERIOD_VERY_LONG - diff_usec; } else if (sleep_next < CHARGE_MIN_SLEEP_USEC) { sleep_next = CHARGE_MIN_SLEEP_USEC; } else if (sleep_next > CHARGE_MAX_SLEEP_USEC) { sleep_next = CHARGE_MAX_SLEEP_USEC; } task_wait_event(sleep_next); } }