301 {
304 "Device not initialized");
305 }
306
307
308 size_t expected_size = 0;
309 bool found_first = false;
310
311 for (
size_t i = 0; i <
pImpl->lanes.size(); i++) {
312 size_t lane_size =
pImpl->lanes[i].bufferSize();
313
314 if (lane_size > 0) {
315 if (!found_first) {
316
317 expected_size = lane_size;
318 found_first = true;
319 } else if (lane_size != expected_size) {
320
321 FL_WARN(
"MultiLaneDevice: Lane size mismatch - expected " << expected_size
322 << " bytes (lane 0), but lane " << i << " has " << lane_size << " bytes");
324 "Lane size mismatch: all lanes must have identical sizes");
325 }
326 }
327 }
328
329 if (expected_size == 0) {
330 FL_WARN(
"MultiLaneDevice: No data to flush (all lanes empty)");
332 "No data to transmit");
333 }
334
335
336 size_t max_size = expected_size;
337
338
339 DMABuffer dma_buffer =
pImpl->backend->acquireDMABuffer(max_size);
340
341 if (!dma_buffer.ok()) {
342 FL_WARN(
"MultiLaneDevice: Failed to acquire DMA buffer");
344 "Failed to acquire DMA buffer");
345 }
346
347
348 const char* error = nullptr;
349 bool transpose_ok = false;
350
351 if (
pImpl->backend_type == 1) {
352
353 if (
pImpl->lanes.size() > 0) {
354 fl::span<const u8> lane_data =
pImpl->lanes[0].data();
355 fl::span<u8> dma_data = dma_buffer.data();
356
357
358 if (lane_data.
size() != dma_data.
size()) {
359 FL_WARN(
"MultiLaneDevice: DMA buffer size mismatch - expected " << lane_data.
size()
360 <<
" bytes, got " << dma_data.
size() <<
" bytes");
361 error = "DMA buffer size mismatch";
362 transpose_ok = false;
363 } else {
364
365 for (
size_t i = 0; i < lane_data.
size(); i++) {
366 dma_data[i] = lane_data[i];
367 }
368 transpose_ok = true;
369 }
370 } else {
371 error = "No lanes configured";
372 transpose_ok = false;
373 }
374
375 }
else if (
pImpl->backend_type == 2) {
376
378 if (
pImpl->lanes.size() > 0) {
379 lane0 = SPITransposer::LaneData{
380 pImpl->lanes[0].data(),
381 fl::span<const u8>()
382 };
383 }
384 if (
pImpl->lanes.size() > 1) {
385 lane1 = SPITransposer::LaneData{
386 pImpl->lanes[1].data(),
387 fl::span<const u8>()
388 };
389 }
390
392
393 }
else if (
pImpl->backend_type == 4) {
394
396 for (
size_t i = 0; i <
pImpl->lanes.size() && i < 4; i++) {
397 lanes[i] = SPITransposer::LaneData{
398 pImpl->lanes[i].data(),
399 fl::span<const u8>()
400 };
401 }
402
404 dma_buffer.data(), &error);
405
406 }
else if (
pImpl->backend_type == 8) {
407
409 for (
size_t i = 0; i <
pImpl->lanes.size() && i < 8; i++) {
410 lanes[i] = SPITransposer::LaneData{
411 pImpl->lanes[i].data(),
412 fl::span<const u8>()
413 };
414 }
415
417 }
418
419 if (!transpose_ok) {
420 FL_WARN(
"MultiLaneDevice: Transposition failed - " << (error ? error :
"unknown error"));
422 error ? error : "Transposition failed");
423 }
424
425
426 bool transmit_ok =
pImpl->backend->transmit(TransmitMode::ASYNC);
427
428 if (!transmit_ok) {
429 FL_WARN(
"MultiLaneDevice: Hardware transmit failed");
431 "Hardware transmit failed");
432 }
433
434
437 }
438
439 FL_DBG(
"MultiLaneDevice: Flushed " <<
pImpl->lanes.size() <<
" lanes ("
440 << max_size << " bytes per lane)");
441
442
443
445}
static bool transpose8(const fl::optional< LaneData > lanes[8], fl::span< u8 > output, const char **error=nullptr) FL_NOEXCEPT
Transpose 8 lanes of data into interleaved octal-SPI format.
static bool transpose4(const fl::optional< LaneData > &lane0, const fl::optional< LaneData > &lane1, const fl::optional< LaneData > &lane2, const fl::optional< LaneData > &lane3, fl::span< u8 > output, const char **error=nullptr) FL_NOEXCEPT
Transpose 4 lanes of data into interleaved quad-SPI format.
static bool transpose2(const fl::optional< LaneData > &lane0, const fl::optional< LaneData > &lane1, fl::span< u8 > output, const char **error=nullptr) FL_NOEXCEPT
Transpose 2 lanes of data into interleaved dual-SPI format.
static expected failure(E err, const char *msg=nullptr) FL_NOEXCEPT
Create error result.
static expected success(T value) FL_NOEXCEPT
Create successful result.
constexpr fl::size size() const FL_NOEXCEPT
bool isReady() const
Check if device is initialized.
Lane & lane(size_t lane_id)
Get access to a specific lane.
fl::unique_ptr< Impl > pImpl