1 /* $OpenBSD: mmc.c,v 1.26 2008/08/30 10:41:38 fgsch Exp $ */ 2 /* 3 * Copyright (c) 2006 Michael Coulter <mjc@openbsd.org> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include <sys/limits.h> 19 #include <sys/types.h> 20 #include <sys/scsiio.h> 21 #include <sys/param.h> 22 #include <scsi/cd.h> 23 #include <scsi/scsi_all.h> 24 #include <scsi/scsi_disk.h> 25 #include <err.h> 26 #include <errno.h> 27 #include <fcntl.h> 28 #include <stdio.h> 29 #include <string.h> 30 #include <unistd.h> 31 #include "extern.h" 32 33 extern int fd; 34 extern int mediacap[]; 35 extern char *cdname; 36 37 #define SCSI_GET_CONFIGURATION 0x46 38 39 #define MMC_FEATURE_HDR_LEN 8 40 41 int 42 get_media_type(void) 43 { 44 scsireq_t scr; 45 char buf[32]; 46 u_char disctype; 47 int rv, error; 48 49 rv = MEDIATYPE_UNKNOWN; 50 memset(buf, 0, sizeof(buf)); 51 memset(&scr, 0, sizeof(scr)); 52 53 scr.cmd[0] = READ_TOC; 54 scr.cmd[1] = 0x2; /* MSF */ 55 scr.cmd[2] = 0x4; /* ATIP */ 56 scr.cmd[8] = 0x20; 57 58 scr.flags = SCCMD_ESCAPE | SCCMD_READ; 59 scr.databuf = buf; 60 scr.datalen = sizeof(buf); 61 scr.cmdlen = 10; 62 scr.timeout = 120000; 63 scr.senselen = SENSEBUFLEN; 64 65 error = ioctl(fd, SCIOCCOMMAND, &scr); 66 if (error != -1 && scr.retsts == 0 && scr.datalen_used > 7) { 67 disctype = (buf[6] >> 6) & 0x1; 68 if (disctype == 0) 69 rv = MEDIATYPE_CDR; 70 else if (disctype == 1) 71 rv = MEDIATYPE_CDRW; 72 } 73 74 return (rv); 75 } 76 77 int 78 get_media_capabilities(int *cap, int rt) 79 { 80 scsireq_t scr; 81 u_char buf[4096]; 82 u_int32_t i, dlen; 83 u_int16_t feature; 84 u_int8_t feature_len; 85 int error; 86 87 memset(cap, 0, MMC_FEATURE_MAX / 8); 88 memset(buf, 0, sizeof(buf)); 89 memset(&scr, 0, sizeof(scr)); 90 91 scr.cmd[0] = SCSI_GET_CONFIGURATION; 92 scr.cmd[1] = rt; 93 *(u_int16_t *)(scr.cmd + 7) = htobe16(sizeof(buf)); 94 95 scr.flags = SCCMD_ESCAPE | SCCMD_READ; 96 scr.databuf = buf; 97 scr.datalen = sizeof(buf); 98 scr.cmdlen = 10; 99 scr.timeout = 120000; 100 scr.senselen = SENSEBUFLEN; 101 102 error = ioctl(fd, SCIOCCOMMAND, &scr); 103 if (error == -1 || scr.retsts != 0) 104 return (-1); 105 if (scr.datalen_used < MMC_FEATURE_HDR_LEN) 106 return (-1); /* Can't get the header. */ 107 108 /* Include the whole header in the length. */ 109 dlen = betoh32(*(u_int32_t *)buf) + 4; 110 if (dlen > scr.datalen_used) 111 dlen = scr.datalen_used; 112 113 for (i = MMC_FEATURE_HDR_LEN; i + 3 < dlen; i += feature_len) { 114 feature_len = buf[i + 3] + 4; 115 if (feature_len + i > dlen) 116 break; 117 118 feature = betoh16(*(u_int16_t *)(buf + i)); 119 if (feature >= MMC_FEATURE_MAX) 120 break; 121 122 setbit(cap, feature); 123 } 124 125 return (0); 126 } 127 128 int 129 set_speed(int wspeed) 130 { 131 scsireq_t scr; 132 int r; 133 134 memset(&scr, 0, sizeof(scr)); 135 scr.cmd[0] = SET_CD_SPEED; 136 scr.cmd[1] = (isset(mediacap, MMC_FEATURE_CDRW_CAV)) != 0; 137 *(u_int16_t *)(scr.cmd + 2) = htobe16(DRIVE_SPEED_OPTIMAL); 138 *(u_int16_t *)(scr.cmd + 4) = htobe16(wspeed); 139 140 scr.cmdlen = 12; 141 scr.datalen = 0; 142 scr.timeout = 120000; 143 scr.flags = SCCMD_ESCAPE; 144 scr.senselen = SENSEBUFLEN; 145 146 r = ioctl(fd, SCIOCCOMMAND, &scr); 147 return (r == 0 ? scr.retsts : -1); 148 } 149 150 int 151 blank(void) 152 { 153 struct scsi_blank *scb; 154 scsireq_t scr; 155 int r; 156 157 bzero(&scr, sizeof(scr)); 158 scb = (struct scsi_blank *)scr.cmd; 159 scb->opcode = BLANK; 160 scb->byte2 |= BLANK_MINIMAL; 161 scr.cmdlen = sizeof(*scb); 162 scr.datalen = 0; 163 scr.timeout = 120000; 164 scr.flags = SCCMD_ESCAPE; 165 scr.senselen = SENSEBUFLEN; 166 167 r = ioctl(fd, SCIOCCOMMAND, &scr); 168 return (r == 0 ? scr.retsts : -1); 169 } 170 171 int 172 unit_ready(void) 173 { 174 struct scsi_test_unit_ready *scb; 175 scsireq_t scr; 176 int r; 177 178 bzero(&scr, sizeof(scr)); 179 scb = (struct scsi_test_unit_ready *)scr.cmd; 180 scb->opcode = TEST_UNIT_READY; 181 scr.cmdlen = sizeof(*scb); 182 scr.datalen = 0; 183 scr.timeout = 120000; 184 scr.flags = SCCMD_ESCAPE; 185 scr.senselen = SENSEBUFLEN; 186 187 r = ioctl(fd, SCIOCCOMMAND, &scr); 188 return (r == 0 ? scr.retsts : -1); 189 } 190 191 int 192 synchronize_cache(void) 193 { 194 struct scsi_synchronize_cache *scb; 195 scsireq_t scr; 196 int r; 197 198 bzero(&scr, sizeof(scr)); 199 scb = (struct scsi_synchronize_cache *)scr.cmd; 200 scb->opcode = SYNCHRONIZE_CACHE; 201 scr.cmdlen = sizeof(*scb); 202 scr.datalen = 0; 203 scr.timeout = 120000; 204 scr.flags = SCCMD_ESCAPE; 205 scr.senselen = SENSEBUFLEN; 206 207 r = ioctl(fd, SCIOCCOMMAND, &scr); 208 return (r == 0 ? scr.retsts : -1); 209 } 210 211 int 212 close_session(void) 213 { 214 struct scsi_close_track *scb; 215 scsireq_t scr; 216 int r; 217 218 bzero(&scr, sizeof(scr)); 219 scb = (struct scsi_close_track *)scr.cmd; 220 scb->opcode = CLOSE_TRACK; 221 scb->closefunc = CT_CLOSE_SESS; 222 scr.cmdlen = sizeof(*scb); 223 scr.datalen = 0; 224 scr.timeout = 120000; 225 scr.flags = SCCMD_ESCAPE; 226 scr.senselen = SENSEBUFLEN; 227 228 r = ioctl(fd, SCIOCCOMMAND, &scr); 229 return (r == 0 ? scr.retsts : -1); 230 } 231 232 int 233 writetao(struct track_head *thp) 234 { 235 u_char modebuf[70], bdlen; 236 struct track_info *tr; 237 int r, track = 0; 238 239 if ((r = mode_sense_write(modebuf)) != SCCMD_OK) { 240 warnx("mode sense failed: %d", r); 241 return (r); 242 } 243 bdlen = modebuf[7]; 244 modebuf[2+8+bdlen] |= 0x40; /* Buffer Underrun Free Enable */ 245 modebuf[2+8+bdlen] |= 0x01; /* change write type to TAO */ 246 247 SLIST_FOREACH(tr, thp, track_list) { 248 track++; 249 switch (tr->type) { 250 case 'd': 251 modebuf[3+8+bdlen] = 0x04; /* track mode = data */ 252 modebuf[4+8+bdlen] = 0x08; /* 2048 block track mode */ 253 modebuf[8+8+bdlen] = 0x00; /* turn off XA */ 254 break; 255 case 'a': 256 modebuf[3+8+bdlen] = 0x00; /* track mode = audio */ 257 modebuf[4+8+bdlen] = 0x00; /* 2352 block track mode */ 258 modebuf[8+8+bdlen] = 0x00; /* turn off XA */ 259 break; 260 default: 261 warn("impossible tracktype detected"); 262 break; 263 } 264 while (unit_ready() != SCCMD_OK) 265 continue; 266 if ((r = mode_select_write(modebuf)) != SCCMD_OK) { 267 warnx("mode select failed: %d", r); 268 return (r); 269 } 270 271 set_speed(tr->speed); 272 writetrack(tr, track); 273 synchronize_cache(); 274 } 275 fprintf(stderr, "Closing session.\n"); 276 close_session(); 277 return (0); 278 } 279 280 int 281 writetrack(struct track_info *tr, int track) 282 { 283 struct timeval tv, otv, atv; 284 u_char databuf[65536], nblk; 285 u_int end_lba, lba, tmp; 286 scsireq_t scr; 287 int r; 288 289 nblk = 65535/tr->blklen; 290 bzero(&scr, sizeof(scr)); 291 scr.timeout = 300000; 292 scr.cmd[0] = WRITE_BIG; 293 scr.cmd[1] = 0x00; 294 scr.cmd[8] = nblk; /* Transfer length in blocks (LSB) */ 295 scr.cmdlen = 10; 296 scr.databuf = (caddr_t)databuf; 297 scr.datalen = nblk * tr->blklen; 298 scr.senselen = SENSEBUFLEN; 299 scr.flags = SCCMD_ESCAPE|SCCMD_WRITE; 300 301 timerclear(&otv); 302 atv.tv_sec = 1; 303 atv.tv_usec = 0; 304 305 if (get_nwa(&lba) != SCCMD_OK) { 306 warnx("cannot get next writable address"); 307 return (-1); 308 } 309 tmp = htobe32(lba); /* update lba in cdb */ 310 memcpy(&scr.cmd[2], &tmp, sizeof(tmp)); 311 312 if (tr->sz / tr->blklen + 1 > UINT_MAX || tr->sz < tr->blklen) { 313 warnx("file %s has invalid size", tr->file); 314 return (-1); 315 } 316 if (tr->sz % tr->blklen) { 317 warnx("file %s is not multiple of block length %d", 318 tr->file, tr->blklen); 319 end_lba = tr->sz / tr->blklen + lba + 1; 320 } else { 321 end_lba = tr->sz / tr->blklen + lba; 322 } 323 if (lseek(tr->fd, tr->off, SEEK_SET) == -1) 324 err(1, "seek failed for file %s", tr->file); 325 while (lba < end_lba && nblk != 0) { 326 while (lba + nblk <= end_lba) { 327 read(tr->fd, databuf, nblk * tr->blklen); 328 scr.cmd[8] = nblk; 329 scr.datalen = nblk * tr->blklen; 330 again: 331 r = ioctl(fd, SCIOCCOMMAND, &scr); 332 if (r != 0) { 333 printf("\r%60s", ""); 334 warn("ioctl failed while attempting to write"); 335 return (-1); 336 } 337 if (scr.retsts == SCCMD_SENSE && scr.sense[2] == 0x2) { 338 usleep(1000); 339 goto again; 340 } 341 if (scr.retsts != SCCMD_OK) { 342 printf("\r%60s", ""); 343 warnx("ioctl returned bad status while " 344 "attempting to write: %d", 345 scr.retsts); 346 return (r); 347 } 348 lba += nblk; 349 350 gettimeofday(&tv, NULL); 351 if (lba == end_lba || timercmp(&tv, &otv, >)) { 352 fprintf(stderr, 353 "\rtrack %02d '%c' %08u/%08u %3d%%", 354 track, tr->type, 355 lba, end_lba, 100 * lba / end_lba); 356 timeradd(&tv, &atv, &otv); 357 } 358 tmp = htobe32(lba); /* update lba in cdb */ 359 memcpy(&scr.cmd[2], &tmp, sizeof(tmp)); 360 } 361 nblk--; 362 } 363 printf("\n"); 364 close(tr->fd); 365 return (0); 366 } 367 368 int 369 mode_sense_write(unsigned char buf[]) 370 { 371 struct scsi_mode_sense_big *scb; 372 scsireq_t scr; 373 int r; 374 375 bzero(&scr, sizeof(scr)); 376 scb = (struct scsi_mode_sense_big *)scr.cmd; 377 scb->opcode = MODE_SENSE_BIG; 378 /* XXX: need to set disable block descriptors and check SCSI drive */ 379 scb->page = WRITE_PARAM_PAGE; 380 scb->length[1] = 0x46; /* 16 for the header + size from pg. 89 mmc-r10a.pdf */ 381 scr.cmdlen = sizeof(*scb); 382 scr.timeout = 4000; 383 scr.senselen = SENSEBUFLEN; 384 scr.datalen= 0x46; 385 scr.flags = SCCMD_ESCAPE|SCCMD_READ; 386 scr.databuf = (caddr_t)buf; 387 388 r = ioctl(fd, SCIOCCOMMAND, &scr); 389 return (r == 0 ? scr.retsts : -1); 390 } 391 392 int 393 mode_select_write(unsigned char buf[]) 394 { 395 struct scsi_mode_select_big *scb; 396 scsireq_t scr; 397 int r; 398 399 bzero(&scr, sizeof(scr)); 400 scb = (struct scsi_mode_select_big *)scr.cmd; 401 scb->opcode = MODE_SELECT_BIG; 402 403 /* 404 * INF-8020 says bit 4 in byte 2 is '1' 405 * INF-8090 refers to it as 'PF(1)' then doesn't 406 * describe it. 407 */ 408 scb->byte2 = 0x10; 409 scb->length[1] = 2 + buf[1] + 256 * buf[0]; 410 scr.timeout = 4000; 411 scr.senselen = SENSEBUFLEN; 412 scr.cmdlen = sizeof(*scb); 413 scr.datalen = 2 + buf[1] + 256 * buf[0]; 414 scr.flags = SCCMD_ESCAPE|SCCMD_WRITE; 415 scr.databuf = (caddr_t)buf; 416 417 r = ioctl(fd, SCIOCCOMMAND, &scr); 418 return (r == 0 ? scr.retsts : -1); 419 } 420 421 int 422 get_disc_size(off_t *availblk) 423 { 424 u_char databuf[28]; 425 struct scsi_read_track_info *scb; 426 scsireq_t scr; 427 int r, tmp; 428 429 bzero(&scr, sizeof(scr)); 430 scb = (struct scsi_read_track_info *)scr.cmd; 431 scr.timeout = 4000; 432 scr.senselen = SENSEBUFLEN; 433 scb->opcode = READ_TRACK_INFO; 434 scb->addrtype = RTI_TRACK; 435 scb->addr[3] = 1; 436 scb->data_len[1] = 0x1c; 437 scr.cmdlen = sizeof(*scb); 438 scr.datalen= 0x1c; 439 scr.flags = SCCMD_ESCAPE|SCCMD_READ; 440 scr.databuf = (caddr_t)databuf; 441 442 r = ioctl(fd, SCIOCCOMMAND, &scr); 443 memcpy(&tmp, &databuf[16], sizeof(tmp)); 444 *availblk = betoh32(tmp); 445 return (r == 0 ? scr.retsts : -1); 446 } 447 448 int 449 get_nwa(int *nwa) 450 { 451 u_char databuf[28]; 452 scsireq_t scr; 453 int r, tmp; 454 455 bzero(&scr, sizeof(scr)); 456 scr.timeout = 4000; 457 scr.senselen = SENSEBUFLEN; 458 scr.cmd[0] = READ_TRACK_INFO; 459 scr.cmd[1] = 0x01; 460 scr.cmd[5] = 0xff; /* Invisible Track */ 461 scr.cmd[7] = 0x00; 462 scr.cmd[8] = 0x1c; 463 scr.cmdlen = 10; 464 scr.datalen= 0x1c; 465 scr.flags = SCCMD_ESCAPE|SCCMD_READ; 466 scr.databuf = (caddr_t)databuf; 467 468 r = ioctl(fd, SCIOCCOMMAND, &scr); 469 memcpy(&tmp, &databuf[12], sizeof(tmp)); 470 *nwa = betoh32(tmp); 471 return (r == 0 ? scr.retsts : -1); 472 } 473