1use crate::object_dict::{
5 find_object_entry, ConstField, ODEntry, ObjectAccess, ProvidesSubObjects, SubObjectAccess,
6};
7use zencan_common::{
8 objects::{AccessType, DataType, ObjectCode, PdoMapping, SubInfo},
9 sdo::AbortCode,
10 AtomicCell, CanId,
11};
12
13const N_MAPPING_PARAMS: usize = 8;
18
19#[derive(Clone, Copy)]
20struct MappingEntry {
21 object: &'static ODEntry<'static>,
22 sub: u8,
23 length: u8,
24}
25
26#[allow(missing_debug_implementations)]
28pub struct Pdo {
29 cob_id: AtomicCell<CanId>,
31 valid: AtomicCell<bool>,
33 rtr_disabled: AtomicCell<bool>,
35 transmission_type: AtomicCell<u8>,
42 sync_counter: AtomicCell<u8>,
44 pub buffered_value: AtomicCell<Option<[u8; 8]>>,
46 valid_maps: AtomicCell<u8>,
50 mapping_params: [AtomicCell<Option<MappingEntry>>; N_MAPPING_PARAMS],
54}
55
56impl Default for Pdo {
57 fn default() -> Self {
58 Self::new()
59 }
60}
61
62impl Pdo {
63 pub const fn new() -> Self {
65 let cob_id = AtomicCell::new(CanId::Std(0));
66 let valid = AtomicCell::new(false);
67 let rtr_disabled = AtomicCell::new(false);
68 let transmission_type = AtomicCell::new(0);
69 let sync_counter = AtomicCell::new(0);
70 let buffered_value = AtomicCell::new(None);
71 let valid_maps = AtomicCell::new(0);
72 let mapping_params = [const { AtomicCell::new(None) }; N_MAPPING_PARAMS];
73 Self {
74 cob_id,
75 valid,
76 rtr_disabled,
77 transmission_type,
78 sync_counter,
79 buffered_value,
80 valid_maps,
81 mapping_params,
82 }
83 }
84
85 pub fn set_valid(&self, value: bool) {
87 self.valid.store(value);
88 }
89
90 pub fn valid(&self) -> bool {
92 self.valid.load()
93 }
94
95 pub fn set_transmission_type(&self, value: u8) {
97 self.transmission_type.store(value);
98 }
99
100 pub fn transmission_type(&self) -> u8 {
102 self.transmission_type.load()
103 }
104
105 pub fn set_cob_id(&self, value: CanId) {
107 self.cob_id.store(value)
108 }
109
110 pub fn cob_id(&self) -> CanId {
112 self.cob_id.load()
113 }
114
115 pub fn sync_update(&self) -> bool {
119 if !self.valid.load() {
120 return false;
121 }
122
123 let transmission_type = self.transmission_type.load();
124 if transmission_type == 0 {
125 true
128 } else if transmission_type <= 240 {
129 let cnt = self.sync_counter.fetch_add(1) + 1;
130 cnt == transmission_type
131 } else {
132 false
133 }
134 }
135
136 pub fn read_events(&self) -> bool {
138 if !self.valid.load() {
139 return false;
140 }
141
142 for i in 0..self.mapping_params.len() {
143 let param = self.mapping_params[i].load();
144 if param.is_none() {
145 break;
146 }
147 let param = param.unwrap();
148 if param.object.data.read_event_flag(param.sub) {
149 return true;
150 }
151 }
152 false
153 }
154
155 pub(crate) fn clear_events(&self) {
156 for i in 0..self.mapping_params.len() {
157 let param = self.mapping_params[i].load();
158 if param.is_none() {
159 break;
160 }
161 let param = param.unwrap();
162 param.object.data.clear_events();
163 }
164 }
165
166 pub(crate) fn store_pdo_data(&self, data: &[u8]) {
167 let mut offset = 0;
168 let valid_maps = self.valid_maps.load() as usize;
169 for (i, param) in self.mapping_params.iter().enumerate() {
170 if i >= valid_maps {
171 break;
172 }
173 let param = param.load();
174 if param.is_none() {
175 break;
176 }
177 let param = param.unwrap();
178 let length = param.length as usize;
179 if offset + length > data.len() {
180 break;
181 }
182 let data_to_write = &data[offset..offset + length];
183 param.object.data.write(param.sub, data_to_write).ok();
186 offset += length;
187 }
188 }
189
190 pub(crate) fn read_pdo_data(&self, data: &mut [u8]) {
191 let mut offset = 0;
192 let valid_maps = self.valid_maps.load() as usize;
193 for (i, param) in self.mapping_params.iter().enumerate() {
194 if i >= valid_maps {
195 break;
196 }
197 let param = param.load();
198 if param.is_none() {
201 break;
202 }
203 let param = param.unwrap();
204 let length = param.length as usize;
205 if offset + length > data.len() {
206 break;
207 }
208 param
211 .object
212 .data
213 .read(param.sub, 0, &mut data[offset..offset + length])
214 .ok();
215 offset += length;
216 }
217 }
218}
219
220struct PdoCobSubObject {
221 pdo: &'static Pdo,
222}
223
224impl PdoCobSubObject {
225 pub const fn new(pdo: &'static Pdo) -> Self {
226 Self { pdo }
227 }
228}
229
230impl SubObjectAccess for PdoCobSubObject {
231 fn read(&self, offset: usize, buf: &mut [u8]) -> Result<usize, AbortCode> {
232 let cob_id = self.pdo.cob_id.load();
233 let mut value = cob_id.raw();
234 if cob_id.is_extended() {
235 value |= 1 << 29;
236 }
237 if self.pdo.rtr_disabled.load() {
238 value |= 1 << 30;
239 }
240 if !self.pdo.valid.load() {
241 value |= 1 << 31;
242 }
243
244 let bytes = value.to_le_bytes();
245 if offset < bytes.len() {
246 let read_len = buf.len().min(bytes.len() - offset);
247 buf[0..read_len].copy_from_slice(&bytes[offset..offset + read_len]);
248 Ok(read_len)
249 } else {
250 Ok(0)
251 }
252 }
253
254 fn read_size(&self) -> usize {
255 4
256 }
257
258 fn write(&self, data: &[u8]) -> Result<(), AbortCode> {
259 if data.len() < 4 {
260 Err(AbortCode::DataTypeMismatchLengthLow)
261 } else if data.len() > 4 {
262 Err(AbortCode::DataTypeMismatchLengthHigh)
263 } else {
264 let value = u32::from_le_bytes(data.try_into().unwrap());
265 let not_valid = (value & (1 << 31)) != 0;
266 let no_rtr = (value & (1 << 30)) != 0;
267 let extended_id = (value & (1 << 29)) != 0;
268
269 let can_id = if extended_id {
270 CanId::Extended(value & 0x1FFFFFFF)
271 } else {
272 CanId::Std((value & 0x7FF) as u16)
273 };
274 self.pdo.cob_id.store(can_id);
275 self.pdo.valid.store(!not_valid);
276 self.pdo.rtr_disabled.store(no_rtr);
277 Ok(())
278 }
279 }
280}
281
282struct PdoTransmissionTypeSubObject {
283 pdo: &'static Pdo,
284}
285
286impl PdoTransmissionTypeSubObject {
287 pub const fn new(pdo: &'static Pdo) -> Self {
288 Self { pdo }
289 }
290}
291
292impl SubObjectAccess for PdoTransmissionTypeSubObject {
293 fn read(&self, offset: usize, buf: &mut [u8]) -> Result<usize, AbortCode> {
294 if offset > 1 {
295 return Ok(0);
296 }
297 buf[0] = self.pdo.transmission_type();
298 Ok(1)
299 }
300
301 fn read_size(&self) -> usize {
302 1
303 }
304
305 fn write(&self, data: &[u8]) -> Result<(), AbortCode> {
306 if data.is_empty() {
307 Err(AbortCode::DataTypeMismatchLengthLow)
308 } else {
309 self.pdo.set_transmission_type(data[0]);
310 Ok(())
311 }
312 }
313}
314
315#[allow(missing_debug_implementations)]
317pub struct PdoCommObject {
318 cob: PdoCobSubObject,
319 transmission_type: PdoTransmissionTypeSubObject,
320}
321
322impl PdoCommObject {
323 pub const fn new(pdo: &'static Pdo) -> Self {
325 let cob = PdoCobSubObject::new(pdo);
326 let transmission_type = PdoTransmissionTypeSubObject::new(pdo);
327 Self {
328 cob,
329 transmission_type,
330 }
331 }
332}
333
334impl ProvidesSubObjects for PdoCommObject {
335 fn get_sub_object(&self, sub: u8) -> Option<(SubInfo, &dyn SubObjectAccess)> {
336 match sub {
337 0 => Some((
338 SubInfo::MAX_SUB_NUMBER,
339 const { &ConstField::new(2u8.to_le_bytes()) },
340 )),
341 1 => Some((SubInfo::new_u32().rw_access().persist(true), &self.cob)),
342 2 => Some((
343 SubInfo::new_u8().rw_access().persist(true),
344 &self.transmission_type,
345 )),
346 _ => None,
347 }
348 }
349
350 fn object_code(&self) -> ObjectCode {
351 ObjectCode::Record
352 }
353}
354
355#[allow(missing_debug_implementations)]
357pub struct PdoMappingObject {
358 od: &'static [ODEntry<'static>],
359 pdo: &'static Pdo,
360}
361
362impl PdoMappingObject {
363 pub const fn new(od: &'static [ODEntry<'static>], pdo: &'static Pdo) -> Self {
365 Self { od, pdo }
366 }
367}
368
369impl ObjectAccess for PdoMappingObject {
370 fn read(&self, sub: u8, offset: usize, buf: &mut [u8]) -> Result<usize, AbortCode> {
371 if sub == 0 {
372 if offset < 1 && !buf.is_empty() {
373 buf[0] = self.pdo.valid_maps.load();
374 Ok(1)
375 } else {
376 Ok(0)
377 }
378 } else if sub <= self.pdo.mapping_params.len() as u8 {
379 let value = if let Some(param) = self.pdo.mapping_params[(sub - 1) as usize].load() {
380 ((param.object.index as u32) << 16)
381 + ((param.sub as u32) << 8)
382 + param.length as u32 * 8
383 } else {
384 0u32
385 };
386 let bytes = value.to_le_bytes();
387 let read_len = buf.len().min(bytes.len() - offset);
388 buf[..read_len].copy_from_slice(&bytes[offset..offset + read_len]);
389 Ok(read_len)
390 } else {
391 Err(AbortCode::NoSuchSubIndex)
392 }
393 }
394
395 fn read_size(&self, sub: u8) -> Result<usize, AbortCode> {
396 if sub == 0 {
397 Ok(1)
398 } else if sub <= N_MAPPING_PARAMS as u8 {
399 Ok(4)
400 } else {
401 Err(AbortCode::NoSuchSubIndex)
402 }
403 }
404
405 fn write(&self, sub: u8, data: &[u8]) -> Result<(), AbortCode> {
406 if sub == 0 {
407 self.pdo.valid_maps.store(data[0]);
408 Ok(())
409 } else if sub <= self.pdo.mapping_params.len() as u8 {
410 if data.len() != 4 {
411 return Err(AbortCode::DataTypeMismatch);
412 }
413 let value = u32::from_le_bytes(data.try_into().unwrap());
414
415 let object_id = (value >> 16) as u16;
416 let mapping_sub = ((value & 0xFF00) >> 8) as u8;
417 let length = (value & 0xFF) as usize;
419 if (length % 8) != 0 {
420 return Err(AbortCode::IncompatibleParameter);
422 }
423 let length = length / 8;
424 let entry = find_object_entry(self.od, object_id).ok_or(AbortCode::NoSuchObject)?;
425 let sub_info = entry.data.sub_info(mapping_sub)?;
426 if sub_info.size < length {
427 return Err(AbortCode::IncompatibleParameter);
428 }
429 self.pdo.mapping_params[(sub - 1) as usize].store(Some(MappingEntry {
430 object: entry,
431 sub: mapping_sub,
432 length: length as u8,
433 }));
434 Ok(())
435 } else {
436 Err(AbortCode::NoSuchSubIndex)
437 }
438 }
439
440 fn object_code(&self) -> ObjectCode {
441 ObjectCode::Record
442 }
443
444 fn sub_info(&self, sub: u8) -> Result<SubInfo, AbortCode> {
445 if sub == 0 {
446 Ok(SubInfo {
447 size: 1,
448 data_type: DataType::UInt8,
449 access_type: AccessType::Rw,
450 pdo_mapping: PdoMapping::None,
451 persist: true,
452 })
453 } else if sub <= self.pdo.mapping_params.len() as u8 {
454 Ok(SubInfo {
455 size: 4,
456 data_type: DataType::UInt32,
457 access_type: AccessType::Rw,
458 pdo_mapping: PdoMapping::None,
459 persist: true,
460 })
461 } else {
462 Err(AbortCode::NoSuchSubIndex)
463 }
464 }
465}