#include #include #include #include #include #include #include #include "crc32.h" #include "sd-spi.h" #define SOCKET_PATH "/tmp/uraid" #define MAX_CLIENTS 10 #define NUM_MIRROR_SETS 2 #define NUM_DISKS_PER_SET 2 #define MAX_SECTORS 512 static int DEVICE_ID_SETS[NUM_MIRROR_SETS][NUM_DISKS_PER_SET] = {{0,1},{2,3}}; // Run the full QSPI/SD initialization sequence volatile static uint32_t* spi0_regs; volatile static uint32_t* spi1_regs; typedef struct RAID_Frame { int cmd; char filename[32]; size_t buffer_size; char buffer[2048]; } RAID_Frame; enum URAID_command{ URAID_CMD_IDLE, URAID_CMD_WRITE, URAID_CMD_READ, }; void write_to_device(RAID_Frame *rf){ // Internal representation of the data that must be flushed to disk /* Normally when you have storage devices, the filesystem layer does all the heavy lifting of finding out sector/block(s) that a file may be written to. If we had a block device, we could just mount a filesystem to it and have the FS give us a sector number to write to. However because of our troubles with block device we don't have this liberty. Writing a "filesystem" within our daemon to give us block number would take us too long and is byeond scope of this project. Instead because we dont have the filesystem layer and doing a half-assed emulation of it. If we were to pretend we have a single storage device. Our file would be layed out sequentially in terms of sectors, these are the "superblocks" that the file spans. however because we are doing RAID and our single storage device is actually multiple disks. The super block gets converted to a block that gets written across all the mirror sets. */ uint32_t label_hash = crc32(rf->filename,strlen(rf->filename)); uint32_t start_super_block = label_hash % MAX_SECTORS; size_t block_spanned = (rf->buffer_size / 512) + 1; for(size_t b = 0; b buffer, 0); } } } void *handle_client(void *arg) { int client_socket = *((int *)arg); ssize_t bytes_read; printf("[uraid] client connected, socket: %d\n",client_socket); while(1){ // Reads the command sent by the client RAID_Frame rf; while( bytes_read = read(client_socket, &rf, sizeof(RAID_Frame))){ switch (rf.cmd) { case URAID_CMD_IDLE: break; case URAID_CMD_WRITE: write_to_device(&rf); default: break; } } } // Close the client socket close(client_socket); pthread_exit(NULL); } int main() { uint8_t init = SDSPIFullInitialization(&spi0_regs, &spi1_regs); if (init) { printf("ERROR: Failed to initialize\n"); return 1; } printf("INFO: Resetting QSPI Peripheral\n"); ResetQSPIPeripheral(spi0_regs); printf("INFO: Initializing QSPI Peripheral\n"); InitQSPIPeripheral(spi0_regs); // initialize in disabled state; uint8_t status = SDSPIInit(spi0_regs, 0); if (status) { printf("ERROR: Failed to initialize\n"); return 1; } else { printf("INFO: Successfully initialized SD Card\n"); } int server_socket, client_socket[MAX_CLIENTS]; struct sockaddr_un server_addr, client_addr; socklen_t client_addr_len; pthread_t thread_id[MAX_CLIENTS]; int client_count = 0; printf("[uraid] URAID\n"); // Creates a UNIX socket, user applications will use liburaid to communicate with the daemon server_socket = socket(AF_UNIX, SOCK_STREAM, 0); if (server_socket == -1) { perror("[uraid] Error creating socket\n"); exit(EXIT_FAILURE); } // Binds the socket to a path memset(&server_addr, 0, sizeof(struct sockaddr_un)); server_addr.sun_family = AF_UNIX; strncpy(server_addr.sun_path, SOCKET_PATH, sizeof(server_addr.sun_path) - 1); if (bind(server_socket, (struct sockaddr *)&server_addr, sizeof(struct sockaddr_un)) == -1) { perror("[uraid] Error binding socket\n"); exit(EXIT_FAILURE); } // Listen for incoming connections if (listen(server_socket, 5) == -1) { perror("Error listening on socket\n"); exit(EXIT_FAILURE); } printf("[uraid] Daemon listening on socket %s\n", SOCKET_PATH); // Accept and handle incoming connections while (1) { client_addr_len = sizeof(struct sockaddr_un); client_socket[client_count] = accept(server_socket, (struct sockaddr *)&client_addr, &client_addr_len); if (client_socket[client_count] == -1) { perror("Error accepting connection\n"); continue; } // Create a thread to handle client communication if (pthread_create(&thread_id[client_count], NULL, handle_client, &client_socket[client_count]) != 0) { perror("Error creating thread\n"); close(client_socket[client_count]); continue; } // Increment the client count client_count++; // If maximum clients reached, break out of loop if (client_count >= MAX_CLIENTS) { printf("Maximum number of clients reached. No longer accepting connections.\n\n"); break; } } // Join all threads for (int i = 0; i < client_count; i++) { pthread_join(thread_id[i], NULL); } // Close server socket close(server_socket); return 0; }