A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita
audio
rust
zig
deno
mpris
rockbox
mpd
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(§or_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 */