rcbench
Servo and ESC test bench: panel, IOmcu and shared C
Loading...
Searching...
No Matches
kst_session.c File Reference

KST session: the operations on one servo, run step by step. More...

#include "kst_session.h"
#include <string.h>

Macros

#define ALL_REGS   0xFFFFFFFFu
 Mask of all 32 registers, one bit each.

Enumerations

enum  {
  OP_NONE = 0 , OP_ENTER , OP_READ_ALL , OP_VERIFY ,
  OP_WRITE
}
 Operation the session runs.
enum  { T_IDLE = 0 , T_GAP , T_BUSY }
 Timer state of a transaction: idle, gap before the next frame, or busy.
enum  {
  E_WAITED = 0 , E_FIRST_READ , E_SYNC , E_LAST_READ ,
  W_PRE = 0 , W_WROTE , W_READ1 , W_READ2 ,
  W_CHECK , W_UNDO_WROTE , W_UNDO_READ1 , W_UNDO_READ2 ,
  W_UNDO_FINAL , W_FINAL , W_PLAN
}
 Steps of the enter sequence (E_*) and of a write (W_*).

Functions

static uint32_t now (const kst_session_t *s)
 Read the driver's microsecond clock.
static uint32_t diff_mask (const kst_image_t *a, const kst_image_t *b)
 Bit set of the registers where two images differ.
static void txn_begin (kst_session_t *s, kst_expect_t expect, uint8_t reg, uint8_t value)
 Set up the frame of one transaction: a read, a write or a sync.
static void wait_begin (kst_session_t *s, uint32_t us)
 Start a wait of the given time before the next frame.
static void note_reply (kst_session_t *s)
 Record the last transaction in the session statistics.
static bool txn_step (kst_session_t *s)
 True when the transaction is over and its reply is in last_reply.
static bool replied (const kst_session_t *s)
 Check whether the last transaction got a valid reply.
static void read_begin (kst_session_t *s)
 Start a read of all registers and reset its pass state.
static bool agreed (const kst_session_t *s, unsigned reg, uint8_t *value)
 The value of a register that KST_READ_EQUAL passes agree on.
static uint32_t tally (kst_session_t *s)
 Count the registers without the required equal reads, as a bit set.
static bool read_advance (kst_session_t *s)
 Takes the read that ended and starts the next.
static void forget_servo (kst_session_t *s)
 The servo was without power: the servo on the port may be another one.
static void finish (kst_session_t *s, kst_ses_t result)
 End the running operation with a result.
static void finish_in_doubt (kst_session_t *s, kst_ses_t result)
 The servo may hold part of a plan.
static void saw_servo (kst_session_t *s)
 The first full read after a write that ended with the servo not read: what differs from the image held is what the write may have changed.
static void took_image (kst_session_t *s)
 Take the read as the servo's image and check its fingerprint.
static void enter_advance (kst_session_t *s)
 Advance the programming-mode entry sequence.
static void read_all_advance (kst_session_t *s)
 Advance a read-all operation and take the image on success.
static void verify_advance (kst_session_t *s)
 Advance a verify operation by comparing the read with the expected image.
static void write_start (kst_session_t *s)
 Send the write frame of the current plan step.
static void write_done (kst_session_t *s)
 Count the current plan step as done.
static void undo_next (kst_session_t *s)
 Undo the next step in reverse order, or finish the undo.
static void undo_begin (kst_session_t *s, uint8_t held)
 The write of step_index failed for good; the register holds held.
static bool read_back_matches (const kst_session_t *s, uint8_t value)
 Check whether the last read-back gave the value written.
static void plan_advance (kst_session_t *s)
 One slice of the planning job.
static void write_advance (kst_session_t *s)
 Advance a plan write through its phases.
static bool advance (kst_session_t *s)
 False when the call did a slice of planning: kst_session_step() returns after it.
kst_ses_t kst_session_step (kst_session_t *s)
 Advance the running operation.
bool kst_session_init (kst_session_t *s, const kst_driver_t *driver)
 Prepare a session.
static kst_ses_t refuse (kst_session_t *s, kst_ses_t result)
 Record and return a refusal of an operation.
static kst_ses_t may_start (const kst_session_t *s, bool needs_mode)
 The checks every operation starts with; KST_SES_OK lets it start.
static kst_ses_t started (kst_session_t *s, uint8_t op)
 Start an operation: reset its state and record its code.
kst_ses_t kst_session_enter (kst_session_t *s)
 Enter programming mode.
kst_ses_t kst_session_read_all (kst_session_t *s)
 Read all 32 registers: 3 passes, all 3 equal for every register.
kst_ses_t kst_session_verify (kst_session_t *s, const kst_image_t *expected)
 Read all and compare with expected, which is copied. diff_regs names the registers that differ.
static bool plan_is_sound (const kst_plan_t *plan, kst_plan_t *built)
 A plan as kst_plan.h builds one.
static bool plan_is_own (const kst_session_t *s, const kst_plan_t *plan)
 True when plan shares a byte with the session's own plan.
static kst_ses_t write_gate (kst_session_t *s, const kst_plan_t *plan, bool confirm_unchecked)
 Check the preconditions of a plan write before it starts.
kst_ses_t kst_session_write (kst_session_t *s, const kst_plan_t *plan, bool confirm_unchecked)
 Run a plan the caller built.
static kst_ses_t plan_gate (kst_session_t *s, bool restore, bool no_target)
 The checks of an operation that plans its own writes; KST_SES_OK lets it plan.
static kst_ses_t plan_started (kst_session_t *s, kst_plan_result_t r, bool confirm_unchecked)
 The job is begun with r: the planning starts, or the request is refused with the planner's reason.
kst_ses_t kst_session_edit (kst_session_t *s, const kst_image_t *target, kst_field_set_t unlocked)
 Change the servo from image to target, which is copied before the session's plan is cleared: it may be the target of kst_session_plan().
kst_ses_t kst_session_restore (kst_session_t *s, bool confirm_unchecked)
 Take the servo from image back to the backup, planned as kst_session_edit() plans.
kst_ses_t kst_session_release_pairing (kst_session_t *s)
 Write 0x00 to register 0x1D, as kst_plan_release_pairing() plans it.
const kst_plan_t * kst_session_plan (const kst_session_t *s)
 The plan of the last kst_session_edit(), kst_session_restore() or kst_session_release_pairing(), for a journal or a display.
void kst_session_abort (kst_session_t *s)
 End the running operation with KST_SES_ERR_ABORTED.
void kst_session_power_cycled (kst_session_t *s)
 The servo's supply was off: it has left programming mode, and the servo on the line may be another one.

Detailed Description

KST session: the operations on one servo, run step by step.

SPDX-License-Identifier: MIT

Function Documentation

◆ forget_servo()

void forget_servo ( kst_session_t * s)
static

The servo was without power: the servo on the port may be another one.

The backup and the record of the writes go; the next full read is the new backup.

◆ kst_session_abort()

void kst_session_abort ( kst_session_t * s)

End the running operation with KST_SES_ERR_ABORTED.

A transaction on the wire is finished first: a frame is never cut. An operation that plans ends at the next kst_session_step() call.

Parameters
sSession.

◆ kst_session_edit()

kst_ses_t kst_session_edit ( kst_session_t * s,
const kst_image_t * target,
kst_field_set_t unlocked )

Change the servo from image to target, which is copied before the session's plan is cleared: it may be the target of kst_session_plan().

unlocked is the unlock set of kst_plan_edit().

Refused at once, before any planning and in this order: a running operation (KST_SES_ERR_BUSY), a NULL target (KST_SES_ERR_ARG, whatever the state of the session), no programming mode, no backup (KST_SES_ERR_NO_BACKUP), a locked session (KST_SES_ERR_LOCKED), an image that fails the fingerprint (KST_SES_ERR_FINGERPRINT), and a request kst_plan_edit_begin() refuses: KST_SES_ERR_NO_PLAN with plan_result KST_PLAN_ERR_R00, KST_PLAN_ERR_PAIRING, or KST_PLAN_ERR_RULES with plan_rules.

Then the session plans, one slice per kst_session_step() call: at most KST_PLAN_WORK_CAP units, 2,161 calls. The planning ends the operation with KST_SES_ERR_NO_PLAN, before any frame, when plan_result is KST_PLAN_ERR_NO_PATH (no order of the writes with valid images in between) or KST_PLAN_ERR_TOO_COMPLEX (the cap is spent without a verdict; a change of fewer fields at a time takes fewer units). An abort while planning ends it with KST_SES_ERR_ABORTED; nothing was sent and the session is not locked.

With plan_result KST_PLAN_OK the plan runs as kst_session_write() describes from step 1. kst_session_plan() gives the plan.

Parameters
sSession.
targetImage to reach.
unlockedFields whose KST_ACCESS_LOCKED is lifted.
Returns
KST_SES_BUSY while planning, then the result.

◆ kst_session_enter()

kst_ses_t kst_session_enter ( kst_session_t * s)

Enter programming mode.

The caller has stopped PWM on the pin.

Sequence: line low for KST_ENTRY_LOW_US; read 0x01; on no reply the sync burst; on no acknowledge one more read of 0x01. A servo already in the mode answers the first read, or, when that read is lost, the last one: it does not acknowledge a second sync. No other bit rate is tried.

The mode lasts until the servo's supply is removed.

Parameters
sSession.
Returns
KST_SES_BUSY while the sequence runs, then the result.

◆ kst_session_init()

bool kst_session_init ( kst_session_t * s,
const kst_driver_t * driver )

Prepare a session.

False when the driver lacks a function.

Parameters
sSession to prepare.
driverDriver to use.
Returns
false when the driver lacks a function.

◆ kst_session_plan()

const kst_plan_t * kst_session_plan ( const kst_session_t * s)

The plan of the last kst_session_edit(), kst_session_restore() or kst_session_release_pairing(), for a journal or a display.

It is the planner's plan while planning is 0 and plan_result is KST_PLAN_OK, until the next operation of these 3 or kst_session_write() starts. NULL for a NULL session.

Parameters
sSession.
Returns
The plan; NULL for a NULL session.

◆ kst_session_power_cycled()

void kst_session_power_cycled ( kst_session_t * s)

The servo's supply was off: it has left programming mode, and the servo on the line may be another one.

A running operation is aborted. The backup, wrote_regs and the lock are dropped, after the end of the running operation where one runs: an edit, a write and a restore are refused with KST_SES_ERR_NO_BACKUP until a kst_session_read_all() takes the servo as it then is for the new backup.

Parameters
sSession.

◆ kst_session_read_all()

kst_ses_t kst_session_read_all ( kst_session_t * s)

Read all 32 registers: 3 passes, all 3 equal for every register.

On any disagreement 2 more passes; a register without 3 equal reads in 5 fails the read. A pass without a single reply ends the read with KST_SES_ERR_NO_SERVO.

The first image read in a session becomes the backup. The result is KST_SES_OK for a servo that fails the fingerprint too; fingerprint_ok and fingerprint tell.

Parameters
sSession.
Returns
KST_SES_BUSY while the read runs, then the result.

◆ kst_session_release_pairing()

kst_ses_t kst_session_release_pairing ( kst_session_t * s)

Write 0x00 to register 0x1D, as kst_plan_release_pairing() plans it.

Refused like an edit. The plan is 1 write or none and is built in the call: planning stays 0.

Parameters
sSession.
Returns
The result.

◆ kst_session_restore()

kst_ses_t kst_session_restore ( kst_session_t * s,
bool confirm_unchecked )

Take the servo from image back to the backup, planned as kst_session_edit() plans.

A locked session and an image that fails the fingerprint do not refuse it. KST_SES_ERR_NO_PLAN at once with plan_result KST_PLAN_ERR_R00.

The backup goes to the servo it was read from only. The read before the first write ends the restore with KST_SES_ERR_CHANGED, nothing written, when a register outside wrote_regs differs from the backup; diff_regs names those registers. After a write that ended without a full read of the servo (a failed read, an abort, KST_SES_ERR_TORN) the registers that differ at the next full read count as changed by that write.

A plan the planner marks unchecked runs with confirm_unchecked only. Without it the operation ends with KST_SES_ERR_UNCONFIRMED when the planning ends, before any frame; plan_result is KST_PLAN_OK and kst_session_plan() gives the plan that waits for the confirmation. The second call plans again.

Parameters
sSession.
confirm_uncheckedtrue to run a plan the planner marks unchecked.
Returns
KST_SES_BUSY while planning, then the result.

◆ kst_session_step()

kst_ses_t kst_session_step ( kst_session_t * s)

Advance the running operation.

KST_SES_BUSY while it runs, then its result; the result again on every later call. A call that plans does one slice of KST_PLAN_SLICE_UNITS units and returns.

Parameters
sSession.
Returns
KST_SES_BUSY, or the result of the operation.

◆ kst_session_verify()

kst_ses_t kst_session_verify ( kst_session_t * s,
const kst_image_t * expected )

Read all and compare with expected, which is copied. diff_regs names the registers that differ.

Parameters
sSession.
expectedImage to compare with; copied.
Returns
KST_SES_BUSY while the read runs, then the result.

◆ kst_session_write()

kst_ses_t kst_session_write ( kst_session_t * s,
const kst_plan_t * plan,
bool confirm_unchecked )

Run a plan the caller built.

The plan is copied. plan must not be the session's own copy or overlap it: that is answered with KST_SES_ERR_ARG.

This call runs the planner a second time and to its end inside the call, without a cap: its duration grows with the plan. A caller that must not block uses kst_session_edit(), kst_session_restore() and kst_session_release_pairing().

Refused before any frame: no programming mode, no backup, a plan that is not the one kst_plan.h builds for its kind, start, target and unlock set (the planner is run again, into the session's own copy, and its writes, their order, the settled marks and the unchecked mark are compared), an edit or release plan that starts from an image that fails the fingerprint, a locked session, a restore whose target is not the backup, an unchecked restore without confirm_unchecked.

Then:

  1. Read all. The servo must equal the plan's start image; for a restore see kst_session_restore(). KST_SES_ERR_CHANGED otherwise: image stays the one held before, and kst_session_read_all() comes before the next plan.
  2. Per write: the write frame, then 2 reads of that register. Both equal to the value: next write. Otherwise read all; a difference in any other register ends the plan with KST_SES_ERR_UNINTENDED; a register that does hold the value counts as written; else the write is repeated, KST_WRITE_ATTEMPTS in total.
  3. A write that fails for good: the writes back to the last settled step are undone in reverse order, each with 2 reads, and the failed register is written back when it holds neither value. Read all confirms. Result KST_SES_ERR_ROLLED_BACK, or KST_SES_ERR_WRITE_FAILED when nothing had to be undone. The servo holds a valid image; image tells which. An undo that fails 3 times: KST_SES_ERR_TORN.
  4. After the last write: read all against the plan's target. All 32 registers are compared, because a bit error in a frame's address field writes another register.

A plan that ends with KST_SES_ERR_TORN, KST_SES_ERR_UNINTENDED, KST_SES_ERR_VERIFY, or with a read, driver or abort result after its first write frame, locks the session. A restore plan that succeeds, or a verify against the backup that succeeds, unlocks it.

Parameters
sSession.
planPlan to run; copied.
confirm_uncheckedtrue to run an unchecked restore.
Returns
The result of the run.

◆ plan_advance()

void plan_advance ( kst_session_t * s)
static

One slice of the planning job.

The plan it ends with is the one the session runs: nothing outside the planner writes s->plan in between.

◆ plan_gate()

kst_ses_t plan_gate ( kst_session_t * s,
bool restore,
bool no_target )
static

The checks of an operation that plans its own writes; KST_SES_OK lets it plan.

restore: the way back, which a lock and a failed fingerprint do not refuse.

◆ plan_is_sound()

bool plan_is_sound ( const kst_plan_t * plan,
kst_plan_t * built )
static

A plan as kst_plan.h builds one.

The planner is run again on the plan's start, target and unlock set, and the plan has to be the one it gives: the same writes in the same order, the same settled marks and the same unchecked mark. A plan changed after it was built, in its order or in the mark that asks for a confirmation, is not that plan.

The planner builds into built, which is the session's own plan: no operation runs, so nothing reads it, and a second plan of 208 bytes is not on the stack. plan does not overlap it.

◆ read_advance()

bool read_advance ( kst_session_t * s)
static

Takes the read that ended and starts the next.

True when the passes are over; read_result and read hold the outcome.