| 463 | } |
| 464 | |
| 465 | WriteResult MultiLaneDevice::writeImpl(fl::span<const fl::span<const u8>> lane_data) { |
| 466 | if (!isReady()) { |
| 467 | FL_WARN("MultiLaneDevice: Not ready for write"); |
| 468 | return WriteResult("Device not ready"); |
| 469 | } |
| 470 | |
| 471 | if (lane_data.size() > pImpl->lanes.size()) { |
| 472 | FL_WARN("MultiLaneDevice: Too many lanes provided (" << lane_data.size() |
| 473 | << " > " << pImpl->lanes.size() << ")"); |
| 474 | return WriteResult("Too many lanes provided"); |
| 475 | } |
| 476 | |
| 477 | // Validate that all lanes have the same size (required for reliable transposition) |
| 478 | if (lane_data.size() > 1) { |
| 479 | size_t first_size = lane_data[0].size(); |
| 480 | for (size_t i = 1; i < lane_data.size(); i++) { |
| 481 | if (lane_data[i].size() != first_size) { |
| 482 | FL_WARN("MultiLaneDevice: Lane size mismatch - lane 0 has " << first_size |
| 483 | << " bytes, lane " << i << " has " << lane_data[i].size() << " bytes"); |
| 484 | return WriteResult("Lane size mismatch: all lanes must have identical sizes"); |
| 485 | } |
| 486 | } |
| 487 | } |
| 488 | |
| 489 | // Wait for previous transmission to complete |
| 490 | waitComplete(); |
| 491 | |
| 492 | // Write all lane data (can be optimized later with batch operations) |
| 493 | for (size_t i = 0; i < lane_data.size(); i++) { |
| 494 | pImpl->lanes[i].write(lane_data[i].data(), lane_data[i].size()); |
| 495 | } |
| 496 | |
| 497 | // Start hardware transmission (async) |
| 498 | auto flush_result = flush(); |
| 499 | if (!flush_result.ok()) { |
| 500 | FL_WARN("MultiLaneDevice: Flush failed"); |
| 501 | return WriteResult("Flush failed"); |
| 502 | } |
| 503 | |
| 504 | FL_DBG("MultiLaneDevice: Wrote " << lane_data.size() << " lanes atomically (async)"); |
| 505 | |
| 506 | // Return success - use device->wait() to block until complete |
| 507 | return WriteResult(); |
| 508 | } |
| 509 | |
| 510 | const MultiLaneDevice::Config& MultiLaneDevice::getConfig() const { |
| 511 | static const Config empty_config; |