Fix bug in using nor flash storage without SD
[SCSI2SD.git] / software / SCSI2SD / src / cdrom.c
1 //      Copyright (C) 2014 Michael McMaster <michael@codesrc.com>
2 //
3 //      This file is part of SCSI2SD.
4 //
5 //      SCSI2SD is free software: you can redistribute it and/or modify
6 //      it under the terms of the GNU General Public License as published by
7 //      the Free Software Foundation, either version 3 of the License, or
8 //      (at your option) any later version.
9 //
10 //      SCSI2SD is distributed in the hope that it will be useful,
11 //      but WITHOUT ANY WARRANTY; without even the implied warranty of
12 //      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 //      GNU General Public License for more details.
14 //
15 //      You should have received a copy of the GNU General Public License
16 //      along with SCSI2SD.  If not, see <http://www.gnu.org/licenses/>.
17
18 #include "device.h"
19 #include "scsi.h"
20 #include "config.h"
21 #include "cdrom.h"
22
23 static const uint8_t SimpleTOC[] =
24 {
25         0x00, // toc length, MSB
26         0x12, // toc length, LSB
27         0x01, // First track number
28         0x01, // Last track number,
29         // TRACK 1 Descriptor
30         0x00, // reserved
31         0x14, // Q sub-channel encodes current position, Digital track
32         0x01, // Track 1,
33         0x00, // Reserved
34         0x00,0x00,0x00,0x00, // Track start sector (LBA)
35         0x00, // reserved
36         0x14, // Q sub-channel encodes current position, Digital track
37         0xAA, // Leadout Track
38         0x00, // Reserved
39         0x00,0x00,0x00,0x00, // Track start sector (LBA)
40 };
41
42 static const uint8_t SessionTOC[] =
43 {
44         0x00, // toc length, MSB
45         0x0A, // toc length, LSB
46         0x01, // First session number
47         0x01, // Last session number,
48         // TRACK 1 Descriptor
49         0x00, // reserved
50         0x14, // Q sub-channel encodes current position, Digital track
51         0x01, // First track number in last complete session
52         0x00, // Reserved
53         0x00,0x00,0x00,0x00 // LBA of first track in last session
54 };
55
56
57 static const uint8_t FullTOC[] =
58 {
59         0x00, // toc length, MSB
60         0x44, // toc length, LSB
61         0x01, // First session number
62         0x01, // Last session number,
63         // A0 Descriptor
64         0x01, // session number
65         0x14, // ADR/Control
66         0x00, // TNO
67         0xA0, // POINT
68         0x00, // Min
69         0x00, // Sec
70         0x00, // Frame
71         0x00, // Zero
72         0x01, // First Track number.
73         0x00, // Disc type 00 = Mode 1
74         0x00,  // PFRAME
75         // A1
76         0x01, // session number
77         0x14, // ADR/Control
78         0x00, // TNO
79         0xA1, // POINT
80         0x00, // Min
81         0x00, // Sec
82         0x00, // Frame
83         0x00, // Zero
84         0x01, // Last Track number
85         0x00, // PSEC
86         0x00,  // PFRAME
87         // A2
88         0x01, // session number
89         0x14, // ADR/Control
90         0x00, // TNO
91         0xA2, // POINT
92         0x00, // Min
93         0x00, // Sec
94         0x00, // Frame
95         0x00, // Zero
96         0x79, // LEADOUT position BCD
97         0x59, // leadout PSEC BCD
98         0x74, // leadout PFRAME BCD
99         // TRACK 1 Descriptor
100         0x01, // session number
101         0x14, // ADR/Control
102         0x00, // TNO
103         0x01, // Point
104         0x00, // Min
105         0x00, // Sec
106         0x00, // Frame
107         0x00, // Zero
108         0x00, // PMIN
109         0x00, // PSEC
110         0x00,  // PFRAME
111         // b0
112         0x01, // session number
113         0x54, // ADR/Control
114         0x00, // TNO
115         0xB1, // POINT
116         0x79, // Min BCD
117         0x59, // Sec BCD
118         0x74, // Frame BCD
119         0x00, // Zero
120         0x79, // PMIN BCD
121         0x59, // PSEC BCD
122         0x74,  // PFRAME BCD
123         // c0
124         0x01, // session number
125         0x54, // ADR/Control
126         0x00, // TNO
127         0xC0, // POINT
128         0x00, // Min
129         0x00, // Sec
130         0x00, // Frame
131         0x00, // Zero
132         0x00, // PMIN
133         0x00, // PSEC
134         0x00  // PFRAME
135 };
136
137 static void LBA2MSF(uint32_t LBA, uint8_t* MSF)
138 {
139         MSF[0] = 0; // reserved.
140         MSF[3] = LBA % 75; // M
141         uint32_t rem = LBA / 75;
142
143         MSF[2] = rem % 60; // S
144         MSF[1] = rem / 60;
145
146 }
147
148 static void doReadTOC(int MSF, uint8_t track, uint16_t allocationLength)
149 {
150         // We only support track 1.
151         // track 0 means "return all tracks"
152         if (track > 1)
153         {
154                 scsiDev.status = CHECK_CONDITION;
155                 scsiDev.target->state.sense.code = ILLEGAL_REQUEST;
156                 scsiDev.target->state.sense.asc = INVALID_FIELD_IN_CDB;
157                 scsiDev.phase = STATUS;
158         }
159         else
160         {
161                 uint32_t len = sizeof(SimpleTOC);
162                 memcpy(scsiDev.data, SimpleTOC, len);
163
164                 uint32_t capacity = getScsiCapacity(
165                         scsiDev.target->device,
166                         scsiDev.target->cfg->sdSectorStart,
167                         scsiDev.target->state.bytesPerSector,
168                         scsiDev.target->cfg->scsiSectors);
169
170                 // Replace start of leadout track
171                 if (MSF)
172                 {
173                         LBA2MSF(capacity, scsiDev.data + 0x10);
174                 }
175                 else
176                 {
177                         scsiDev.data[0x10] = capacity >> 24;
178                         scsiDev.data[0x11] = capacity >> 16;
179                         scsiDev.data[0x12] = capacity >> 8;
180                         scsiDev.data[0x13] = capacity;
181                 }
182
183                 if (len > allocationLength)
184                 {
185                         len = allocationLength;
186                 }
187                 scsiDev.dataLen = len;
188                 scsiDev.phase = DATA_IN;
189         }
190 }
191
192 static void doReadSessionInfo(uint8_t session, uint16_t allocationLength)
193 {
194         uint32_t len = sizeof(SessionTOC);
195         memcpy(scsiDev.data, SessionTOC, len);
196
197         if (len > allocationLength)
198         {
199                 len = allocationLength;
200         }
201         scsiDev.dataLen = len;
202         scsiDev.phase = DATA_IN;
203 }
204
205 static inline uint8_t
206 fromBCD(uint8_t val)
207 {
208         return ((val >> 4) * 10) + (val & 0xF);
209 }
210
211 static void doReadFullTOC(int convertBCD, uint8_t session, uint16_t allocationLength)
212 {
213         // We only support session 1.
214         if (session > 1)
215         {
216                 scsiDev.status = CHECK_CONDITION;
217                 scsiDev.target->state.sense.code = ILLEGAL_REQUEST;
218                 scsiDev.target->state.sense.asc = INVALID_FIELD_IN_CDB;
219                 scsiDev.phase = STATUS;
220         }
221         else
222         {
223                 uint32_t len = sizeof(FullTOC);
224                 memcpy(scsiDev.data, FullTOC, len);
225
226                 if (convertBCD)
227                 {
228                         int descriptor = 4;
229                         while (descriptor < len)
230                         {
231                                 int i;
232                                 for (i = 0; i < 7; ++i)
233                                 {
234                                         scsiDev.data[descriptor + i] =
235                                                 fromBCD(scsiDev.data[descriptor + 4 + i]);
236                                 }
237                                 descriptor += 11;
238                         }
239
240                 }
241
242                 if (len > allocationLength)
243                 {
244                         len = allocationLength;
245                 }
246                 scsiDev.dataLen = len;
247                 scsiDev.phase = DATA_IN;
248         }
249 }
250
251 static uint8_t SimpleHeader[] =
252 {
253         0x01, // 2048byte user data, L-EC in 288 byte aux field.
254         0x00, // reserved
255         0x00, // reserved
256         0x00, // reserved
257         0x00,0x00,0x00,0x00 // Track start sector (LBA or MSF)
258 };
259
260 void doReadHeader(int MSF, uint32_t lba, uint16_t allocationLength)
261 {
262         uint32_t len = sizeof(SimpleHeader);
263         memcpy(scsiDev.data, SimpleHeader, len);
264         if (len > allocationLength)
265         {
266                 len = allocationLength;
267         }
268         scsiDev.dataLen = len;
269         scsiDev.phase = DATA_IN;
270 }
271
272
273 // Handle direct-access scsi device commands
274 int scsiCDRomCommand()
275 {
276         int commandHandled = 1;
277
278         uint8 command = scsiDev.cdb[0];
279         if (command == 0x43)
280         {
281                 // CD-ROM Read TOC
282                 int MSF = scsiDev.cdb[1] & 0x02 ? 1 : 0;
283                 uint8_t track = scsiDev.cdb[6];
284                 uint16_t allocationLength =
285                         (((uint32_t) scsiDev.cdb[7]) << 8) +
286                         scsiDev.cdb[8];
287
288                 // Reject MMC commands for now, otherwise the TOC data format
289                 // won't be understood.
290                 // The "format" field is reserved for SCSI-2
291                 uint8_t format = scsiDev.cdb[2] & 0x0F;
292                 switch (format)
293                 {
294                         case 0: doReadTOC(MSF, track, allocationLength); break; // SCSI-2
295                         case 1: doReadSessionInfo(MSF, allocationLength); break; // MMC2
296                         case 2: doReadFullTOC(0, track, allocationLength); break; // MMC2
297                         case 3: doReadFullTOC(1, track, allocationLength); break; // MMC2
298                         default:
299                         {
300                                 scsiDev.status = CHECK_CONDITION;
301                                 scsiDev.target->state.sense.code = ILLEGAL_REQUEST;
302                                 scsiDev.target->state.sense.asc = INVALID_FIELD_IN_CDB;
303                                 scsiDev.phase = STATUS;
304                         }
305                 }
306         }
307         else if (command == 0x44)
308         {
309                 // CD-ROM Read Header
310                 int MSF = scsiDev.cdb[1] & 0x02 ? 1 : 0;
311                 uint32_t lba = 0; // IGNORED for now
312                 uint16_t allocationLength =
313                         (((uint32_t) scsiDev.cdb[7]) << 8) +
314                         scsiDev.cdb[8];
315                 doReadHeader(MSF, lba, allocationLength);
316         }
317         else
318         {
319                 commandHandled = 0;
320         }
321
322         return commandHandled;
323 }
324