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