A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd
at master 330 lines 9.1 kB view raw
1/*************************************************************************** 2 * __________ __ ___. 3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___ 4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / 5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < 6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ 7 * \/ \/ \/ \/ \/ 8 * 9 * Copyright (C) 2024 Solomon Peachy 10 * 11 * This program is free software; you can redistribute it and/or 12 * modify it under the terms of the GNU General Public License 13 * as published by the Free Software Foundation; either version 2 14 * of the License, or (at your option) any later version. 15 * 16 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 17 * KIND, either express or implied. 18 * 19 ****************************************************************************/ 20 21/* This is intended to be #included into the ATA driver */ 22 23static sector_t total_sectors; 24static uint32_t log_sector_size; 25static uint16_t identify_info[ATA_IDENTIFY_WORDS] STORAGE_ALIGN_ATTR; 26#ifdef HAVE_LBA48 27static bool ata_lba48 = false; /* set for 48 bit addressing */ 28#endif 29static bool canflush = true; 30static int spinup_time = 0; 31static struct mutex ata_mutex SHAREDBSS_ATTR; 32 33#ifdef MAX_PHYS_SECTOR_SIZE 34static uint16_t phys_sector_mult = 1; 35#endif 36 37int ata_spinup_time(void) 38{ 39 return spinup_time; 40} 41 42#ifdef STORAGE_GET_INFO 43void ata_get_info(IF_MD(int drive,)struct storage_info *info) 44{ 45 unsigned short *src,*dest; 46 static char vendor[8]; 47 static char product[16]; 48 static char revision[4]; 49#ifdef HAVE_MULTIDRIVE 50 (void)drive; /* unused for now */ 51#endif 52 int i; 53 54 info->sector_size = log_sector_size; 55 info->num_sectors = total_sectors; 56#ifdef MAX_PHYS_SECTOR_SIZE 57 info->phys_sector_mult = phys_sector_mult; 58#endif 59 60 src = (unsigned short*)&identify_info[27]; 61 dest = (unsigned short*)vendor; 62 for (i=0;i<4;i++) 63 dest[i] = htobe16(src[i]); 64 info->vendor=vendor; 65 66 src = (unsigned short*)&identify_info[31]; 67 dest = (unsigned short*)product; 68 for (i=0;i<8;i++) 69 dest[i] = htobe16(src[i]); 70 info->product=product; 71 72 src = (unsigned short*)&identify_info[23]; 73 dest = (unsigned short*)revision; 74 for (i=0;i<2;i++) 75 dest[i] = htobe16(src[i]); 76 info->revision=revision; 77} 78#endif 79 80#ifdef MAX_PHYS_SECTOR_SIZE 81 82#ifdef MAX_VARIABLE_LOG_SECTOR 83#define __MAX_VARIABLE_LOG_SECTOR MAX_VARIABLE_LOG_SECTOR 84#else 85#define __MAX_VARIABLE_LOG_SECTOR SECTOR_SIZE 86#endif 87 88#ifndef STORAGE_ALIGN_ATTR 89#define STORAGE_ALIGN_ATTR __attribute__((aligned(sizeof(uint32_t)))) 90#endif 91 92struct sector_cache_entry { 93 unsigned char data[MAX_PHYS_SECTOR_SIZE]; 94 sector_t sectornum; /* logical sector */ 95 bool inuse; 96}; 97/* buffer for reading and writing large physical sectors */ 98static struct sector_cache_entry sector_cache STORAGE_ALIGN_ATTR; 99 100static int ata_transfer_sectors(uint64_t start, 101 int incount, 102 void* inbuf, 103 int write); 104 105static int cache_sector(sector_t sector) 106{ 107 int rc; 108 109 /* round down to physical sector boundary */ 110 sector &= ~(phys_sector_mult - 1); 111 112 /* check whether the sector is already cached */ 113 if (sector_cache.inuse && (sector_cache.sectornum == sector)) 114 return 0; 115 116 /* not found: read the sector */ 117 sector_cache.inuse = false; 118 rc = ata_transfer_sectors(sector, phys_sector_mult, sector_cache.data, false); 119 if (!rc) 120 { 121 sector_cache.sectornum = sector; 122 sector_cache.inuse = true; 123 } 124 return rc; 125} 126 127static inline int flush_current_sector(void) 128{ 129 return ata_transfer_sectors(sector_cache.sectornum, phys_sector_mult, 130 sector_cache.data, true); 131} 132 133int ata_read_sectors(IF_MD(int drive,) 134 sector_t start, 135 int incount, 136 void* inbuf) 137{ 138 int rc = 0; 139 int offset; 140 141#ifdef HAVE_MULTIDRIVE 142 (void)drive; /* unused for now */ 143#endif 144 mutex_lock(&ata_mutex); 145 146 offset = start & (phys_sector_mult - 1); 147 148 if (offset) /* first partial physical sector */ 149 { 150 int partcount = MIN(incount, phys_sector_mult - offset); 151 152 rc = cache_sector(start); 153 if (rc) 154 { 155 rc = rc * 10 - 1; 156 goto error; 157 } 158 memcpy(inbuf, sector_cache.data + offset * log_sector_size, 159 partcount * log_sector_size); 160 161 start += partcount; 162 inbuf += partcount * log_sector_size; 163 incount -= partcount; 164 } 165 offset = incount & (phys_sector_mult - 1); 166 incount -= offset; 167 168 if (incount) /* all complete physical sectors */ 169 { 170 rc = ata_transfer_sectors(start, incount, inbuf, false); 171 if (rc) 172 { 173 rc = rc * 10 - 2; 174 goto error; 175 } 176 start += incount; 177 inbuf += incount * log_sector_size; 178 } 179 180 if (offset) /* Trailing partial logical sector */ 181 { 182 rc = cache_sector(start); 183 if (rc) 184 { 185 rc = rc * 10 - 3; 186 goto error; 187 } 188 memcpy(inbuf, sector_cache.data, offset * log_sector_size); 189 } 190 191 error: 192 mutex_unlock(&ata_mutex); 193 194 return rc; 195} 196 197int ata_write_sectors(IF_MD(int drive,) 198 sector_t start, 199 int count, 200 const void* buf) 201{ 202 int rc = 0; 203 int offset; 204 205#ifdef HAVE_MULTIDRIVE 206 (void)drive; /* unused for now */ 207#endif 208 mutex_lock(&ata_mutex); 209 210 offset = start & (phys_sector_mult - 1); 211 212 if (offset) /* first partial physical sector */ 213 { 214 int partcount = MIN(count, phys_sector_mult - offset); 215 216 rc = cache_sector(start); 217 if (rc) 218 { 219 rc = rc * 10 - 1; 220 goto error; 221 } 222 memcpy(sector_cache.data + offset * log_sector_size, buf, 223 partcount * log_sector_size); 224 rc = flush_current_sector(); 225 if (rc) 226 { 227 rc = rc * 10 - 2; 228 goto error; 229 } 230 start += partcount; 231 buf += partcount * log_sector_size; 232 count -= partcount; 233 } 234 235 offset = count & (phys_sector_mult - 1); 236 count -= offset; 237 238 if (count) /* all complete physical sectors */ 239 { 240 rc = ata_transfer_sectors(start, count, (void*)buf, true); 241 if (rc) 242 { 243 rc = rc * 10 - 3; 244 goto error; 245 } 246 start += count; 247 buf += count * log_sector_size; 248 } 249 250 if (offset) /* Trailing partial logical sector */ 251 { 252 rc = cache_sector(start); 253 if (rc) 254 { 255 rc = rc * 10 - 4; 256 goto error; 257 } 258 memcpy(sector_cache.data, buf, offset * log_sector_size); 259 rc = flush_current_sector(); 260 if (rc) 261 { 262 rc = rc * 10 - 5; 263 goto error; 264 } 265 } 266 267 error: 268 mutex_unlock(&ata_mutex); 269 270 return rc; 271} 272 273static int ata_get_phys_sector_mult(void) 274{ 275 int rc = 0; 276 277 /* Find out the physical sector size */ 278 if((identify_info[106] & 0xe000) == 0x6000) /* B14, B13 */ 279 phys_sector_mult = BIT_N(identify_info[106] & 0x000f); 280 else 281 phys_sector_mult = 1; 282 283 DEBUGF("ata: %d logical sectors per phys sector", phys_sector_mult); 284 285 if((identify_info[209] & 0xc000) == 0x4000) { /* B14 */ 286 if (identify_info[209] & 0x3fff) { 287 panicf("ata: Unaligned logical/physical sector mapping"); 288 // XXX we can probably handle this by adding a fixed offset 289 // to all operations and subtracting this from the reported 290 // size. But we don't do tihs until we find a real-world need. 291 } 292 } 293 294 if (phys_sector_mult > 1) 295 { 296 /* Check if drive really needs emulation - if we can access 297 sector 1 then assume the drive supports "512e" and will handle 298 it better than us, so ignore the large physical sectors. 299 300 The exception here is if the device is partitioned to use 301 larger-than-logical "virtual" sectors; in that case we will 302 use whichever one (ie physical/"virtual") is larger. 303 */ 304 char throwaway[__MAX_VARIABLE_LOG_SECTOR]; 305 rc = ata_transfer_sectors(1, 1, &throwaway, false); 306 if (rc == 0) 307 phys_sector_mult = 1; 308 } 309 310 if (phys_sector_mult > (MAX_PHYS_SECTOR_SIZE/log_sector_size)) 311 panicf("Unsupported physical sector size: %ld", 312 phys_sector_mult * log_sector_size); 313 314 memset(&sector_cache, 0, sizeof(sector_cache)); 315 316 return 0; 317} 318 319void ata_set_phys_sector_mult(unsigned int mult) 320{ 321 unsigned int max = MAX_PHYS_SECTOR_SIZE/log_sector_size; 322 /* virtual sector could be larger than pyhsical sector */ 323 if (!mult || mult > max) 324 mult = max; 325 /* It needs to be at _least_ the size of the real multiplier */ 326 if (mult > phys_sector_mult) 327 phys_sector_mult = mult; 328} 329 330#endif /* MAX_PHYS_SECTOR_SIZE */