1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
//! `"Async Soft Channel"`'s `add_record` — the INP check C makes before an
//! asynchronous soft input record is ever processed.
//!
//! ```c
//! static long add_record(dbCommon *pcommon)
//! {
//! aiRecord *prec = (aiRecord *)pcommon;
//! DBLINK *plink = &prec->inp;
//! ...
//! if (dbLinkIsDefined(plink) && dbLinkIsConstant(plink))
//! return 0;
//!
//! if (plink->type != PV_LINK) {
//! long status = S_db_badField;
//!
//! recGblRecordError(status, prec,
//! "devAiSoftCallback (add_record) Illegal INP field");
//! return status;
//! }
//! ```
//! (`devAiSoftCallback.c:76-93`, and the same shape in the six input twins.)
//!
//! Two facts decide what this port owes.
//!
//! The first early return is DEAD at IOC init. `dbLinkIsDefined` is
//! `plink->lset != 0` (`dbLink.c:215-218`), and `iocInit.c::doResolveLinks`
//! calls `add_record` BEFORE `dbInitLink` for that same link
//! (`iocInit.c:546-559`), so `lset` is still NULL and the guard cannot fire.
//! It exists for the other caller, `dbPutFieldLink` (`dbAccess.c:1196`) — a
//! DTYP changed at runtime, on a link that is already initialised. So at
//! startup the ONLY test is `plink->type != PV_LINK`, which is why a
//! `field(INP, {const:9})` earns the error and a `field(INP, "someRecord")`
//! does not.
//!
//! Only the seven INPUT flavours have an `add_record` at all. `devAoSoft-
//! Callback.c` and the nine other output files declare a plain dset with no
//! `dsxt`, so an output `"Async Soft Channel"` record is never checked —
//! matching `softIoc` R7.0.10 on `asyncSoftTest.db`, which emits exactly seven
//! of these lines and nothing for `ao1`/`bo1`/`lso1`/`so1`.
use crate;
/// The `dev<T>SoftCallback` dset whose name goes in the message, for the seven
/// input record types C ships one for.
///
/// The name is not derivable from the record type — C abbreviates `int64in` to
/// `I64in`, `longin` to `Li` and `stringin` to `Si` — so it is a table, and the
/// table is also the gate: a record type absent from it has no `add_record` in
/// C and must not be checked.
/// C `plink->type == PV_LINK` for a link the `.db` parser has produced and
/// `dbInitLink` has not yet touched.
///
/// A PV link is the only thing `dbParseLink` turns into `PV_LINK`; this port
/// resolves the CA/CP/PP modifiers at parse time and so splits that one C type
/// across two variants, but both are the same `PV_LINK` here. Everything
/// else — a constant, an empty field, a JSON link (`{const:9}` included), a
/// `#C0 S0` hardware address — is one of C's other link types.
/// C `dev<T>SoftCallback::add_record`, for one record, at the point
/// `iocInit.c::doResolveLinks` calls it.
///
/// Reports and returns; `doResolveLinks` discards the status
/// (`iocInit.c:551-554`), so the record is built either way and the operator's
/// only warning that its INP can never be read is this line.
pub