1 #include "acd.h" 2 3 int 4 msfconv(Fmt *fp) 5 { 6 Msf m; 7 8 m = va_arg(fp->args, Msf); 9 fmtprint(fp, "%d.%d.%d", m.m, m.s, m.f); 10 return 0; 11 } 12 13 static int 14 status(Drive *d) 15 { 16 uchar cmd[12]; 17 18 memset(cmd, 0, sizeof cmd); 19 cmd[0] = 0xBD; 20 return scsi(d->scsi, cmd, sizeof cmd, nil, 0, Snone); 21 } 22 23 static int 24 playmsf(Drive *d, Msf start, Msf end) 25 { 26 uchar cmd[12]; 27 28 memset(cmd, 0, sizeof cmd); 29 cmd[0] = 0x47; 30 cmd[3] = start.m; 31 cmd[4] = start.s; 32 cmd[5] = start.f; 33 cmd[6] = end.m; 34 cmd[7] = end.s; 35 cmd[8] = end.f; 36 37 return scsi(d->scsi, cmd, sizeof cmd, nil, 0, Snone); 38 } 39 40 int 41 playtrack(Drive *d, int start, int end) 42 { 43 Toc *t; 44 45 t = &d->toc; 46 47 if(t->ntrack == 0) 48 return -1; 49 50 if(start < 0) 51 start = 0; 52 if(end >= t->ntrack) 53 end = t->ntrack-1; 54 if(end < start) 55 end = start; 56 57 return playmsf(d, t->track[start].start, t->track[end].end); 58 } 59 60 int 61 resume(Drive *d) 62 { 63 uchar cmd[12]; 64 65 memset(cmd, 0, sizeof cmd); 66 cmd[0] = 0x4B; 67 cmd[8] = 0x01; 68 return scsi(d->scsi, cmd, sizeof cmd, nil, 0, Snone); 69 } 70 71 int 72 pause(Drive *d) 73 { 74 uchar cmd[12]; 75 76 memset(cmd, 0, sizeof cmd); 77 cmd[0] = 0x4B; 78 return scsi(d->scsi, cmd, sizeof cmd, nil, 0, Snone); 79 } 80 81 int 82 stop(Drive *d) 83 { 84 uchar cmd[12]; 85 86 memset(cmd, 0, sizeof cmd); 87 cmd[0] = 0x4E; 88 return scsi(d->scsi, cmd, sizeof cmd, nil, 0, Snone); 89 } 90 91 int 92 eject(Drive *d) 93 { 94 uchar cmd[12]; 95 96 memset(cmd, 0, sizeof cmd); 97 cmd[0] = 0x1B; 98 cmd[1] = 1; 99 cmd[4] = 2; 100 return scsi(d->scsi, cmd, sizeof cmd, nil, 0, Snone); 101 } 102 103 int 104 ingest(Drive *d) 105 { 106 uchar cmd[12]; 107 108 memset(cmd, 0, sizeof cmd); 109 cmd[0] = 0x1B; 110 cmd[1] = 1; 111 cmd[4] = 3; 112 return scsi(d->scsi, cmd, sizeof cmd, nil, 0, Snone); 113 } 114 115 static Msf 116 rdmsf(uchar *p) 117 { 118 Msf msf; 119 120 msf.m = p[0]; 121 msf.s = p[1]; 122 msf.f = p[2]; 123 return msf; 124 } 125 126 static ulong 127 rdlba(uchar *p) 128 { 129 return (p[0]<<16) | (p[1]<<8) | p[2]; 130 } 131 132 /* not a Drive, so that we don't accidentally touch Drive.toc */ 133 int 134 gettoc(Scsi *s, Toc *t) 135 { 136 int i, n; 137 uchar cmd[12]; 138 uchar resp[1024]; 139 140 Again: 141 memset(t, 0, sizeof(*t)); 142 memset(cmd, 0, sizeof cmd); 143 cmd[0] = 0x43; 144 cmd[1] = 0x02; 145 cmd[7] = sizeof(resp)>>8; 146 cmd[8] = sizeof(resp); 147 148 s->changetime = 1; 149 /* scsi sets nchange, changetime */ 150 if(scsi(s, cmd, sizeof cmd, resp, sizeof(resp), Sread) < 4) 151 return -1; 152 153 if(s->changetime == 0) { 154 t->ntrack = 0; 155 werrstr("no media"); 156 return -1; 157 } 158 159 if(t->nchange == s->nchange && t->changetime != 0) 160 return 0; 161 162 t->nchange = s->nchange; 163 t->changetime = s->changetime; 164 165 if(t->ntrack > MTRACK) 166 t->ntrack = MTRACK; 167 168 DPRINT(2, "%d %d\n", resp[3], resp[2]); 169 t->ntrack = resp[3]-resp[2]+1; 170 t->track0 = resp[2]; 171 172 n = ((resp[0]<<8) | resp[1])+2; 173 if(n < 4+8*(t->ntrack+1)) { 174 werrstr("bad read0 %d %d", n, 4+8*(t->ntrack+1)); 175 return -1; 176 } 177 178 for(i=0; i<=t->ntrack; i++) /* <=: track[ntrack] = end */ 179 t->track[i].start = rdmsf(resp+4+i*8+5); 180 181 for(i=0; i<t->ntrack; i++) 182 t->track[i].end = t->track[i+1].start; 183 184 memset(cmd, 0, sizeof cmd); 185 cmd[0] = 0x43; 186 cmd[7] = sizeof(resp)>>8; 187 cmd[8] = sizeof(resp); 188 if(scsi(s, cmd, sizeof cmd, resp, sizeof(resp), Sread) < 4) 189 return -1; 190 191 if(s->changetime != t->changetime || s->nchange != t->nchange) { 192 fprint(2, "disk changed underfoot; repeating\n"); 193 goto Again; 194 } 195 196 n = ((resp[0]<<8) | resp[1])+2; 197 if(n < 4+8*(t->ntrack+1)) { 198 werrstr("bad read"); 199 return -1; 200 } 201 202 for(i=0; i<=t->ntrack; i++) 203 t->track[i].bstart = rdlba(resp+4+i*8+5); 204 205 for(i=0; i<t->ntrack; i++) 206 t->track[i].bend = t->track[i+1].bstart; 207 208 return 0; 209 } 210 211 static void 212 dumptoc(Toc *t) 213 { 214 int i; 215 216 fprint(1, "%d tracks\n", t->ntrack); 217 for(i=0; i<t->ntrack; i++) 218 print("%d. %M-%M (%lud-%lud)\n", i+1, 219 t->track[i].start, t->track[i].end, 220 t->track[i].bstart, t->track[i].bend); 221 } 222 223 static void 224 ping(Drive *d) 225 { 226 uchar cmd[12]; 227 228 memset(cmd, 0, sizeof cmd); 229 cmd[0] = 0x43; 230 scsi(d->scsi, cmd, sizeof(cmd), nil, 0, Snone); 231 } 232 233 static int 234 playstatus(Drive *d, Cdstatus *stat) 235 { 236 uchar cmd[12], resp[16]; 237 238 memset(cmd, 0, sizeof cmd); 239 cmd[0] = 0x42; 240 cmd[1] = 0x02; 241 cmd[2] = 0x40; 242 cmd[3] = 0x01; 243 cmd[7] = sizeof(resp)>>8; 244 cmd[8] = sizeof(resp); 245 if(scsi(d->scsi, cmd, sizeof(cmd), resp, sizeof(resp), Sread) < 0) 246 return -1; 247 248 switch(resp[1]){ 249 case 0x11: 250 stat->state = Splaying; 251 break; 252 case 0x12: 253 stat->state = Spaused; 254 break; 255 case 0x13: 256 stat->state = Scompleted; 257 break; 258 case 0x14: 259 stat->state = Serror; 260 break; 261 case 0x00: /* not supported */ 262 case 0x15: /* no current status to return */ 263 default: 264 stat->state = Sunknown; 265 break; 266 } 267 268 stat->track = resp[6]; 269 stat->index = resp[7]; 270 stat->abs = rdmsf(resp+9); 271 stat->rel = rdmsf(resp+13); 272 return 0; 273 } 274 275 void 276 cdstatusproc(void *v) 277 { 278 Drive *d; 279 Toc t; 280 Cdstatus s; 281 282 t.changetime = ~0; 283 t.nchange = ~0; 284 285 threadsetname("cdstatusproc"); 286 d = v; 287 DPRINT(2, "cdstatus %d\n", getpid()); 288 for(;;) { 289 ping(d); 290 //DPRINT(2, "d %d %d t %d %d\n", d->scsi->changetime, d->scsi->nchange, t.changetime, t.nchange); 291 if(playstatus(d, &s) == 0) 292 send(d->cstatus, &s); 293 if(d->scsi->changetime != t.changetime || d->scsi->nchange != t.nchange) { 294 if(gettoc(d->scsi, &t) == 0) { 295 DPRINT(2, "sendtoc...\n"); 296 if(debug) dumptoc(&t); 297 send(d->ctocdisp, &t); 298 } else 299 DPRINT(2, "error: %r\n"); 300 } 301 sleep(1000); 302 } 303 } 304