Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

702 lines
17 KiB

4 years ago
  1. /*++
  2. Copyright (c) 1995 Microsoft Corporation
  3. Module Name:
  4. cdp.c
  5. Abstract:
  6. A user mode app that allows simple commands to be sent to a
  7. selected scsi device.
  8. Environment:
  9. User mode only
  10. Revision History:
  11. 03-26-96 : Created
  12. --*/
  13. #include <string.h>
  14. #include <stdio.h>
  15. #include <stdlib.h>
  16. #include <assert.h>
  17. #include <windows.h>
  18. #include <devioctl.h>
  19. #include <ntddscsi.h>
  20. #define _NTSRB_ // to keep srb.h from being included
  21. #include <scsi.h>
  22. #ifdef DBG
  23. #define dbg(x) x
  24. #else
  25. #define dbg(x) /* x */
  26. #endif
  27. #define ARGUMENT_USED(x) (x == NULL)
  28. typedef struct {
  29. char *Name;
  30. char *Description;
  31. DWORD (*Function)(HANDLE device, int argc, char *argv[]);
  32. } COMMAND;
  33. typedef struct {
  34. SCSI_PASS_THROUGH Spt;
  35. char SenseInfoBuffer[18];
  36. char DataBuffer[0]; // Allocate buffer space
  37. // after this
  38. } SPT_WITH_BUFFERS, *PSPT_WITH_BUFFERS;
  39. typedef struct {
  40. UCHAR Reserved1;
  41. UCHAR ADR : 4;
  42. UCHAR Control : 4;
  43. UCHAR TrackNumber;
  44. UCHAR Reserved2;
  45. union {
  46. UCHAR Swap[4];
  47. ULONG Block;
  48. struct {
  49. UCHAR Reserved;
  50. UCHAR M;
  51. UCHAR S;
  52. UCHAR F;
  53. } MSF;
  54. } Address;
  55. } TRACK, *PTRACK;
  56. #define MAKE_SPT(p, size, cdbsize, datain, datasize) \
  57. size = sizeof(SPT_WITH_BUFFERS) + datasize; \
  58. p = (PSPT_WITH_BUFFERS) malloc(size); \
  59. memset(p, 0, size); \
  60. p->Spt.Length = sizeof(p->Spt); \
  61. p->Spt.CdbLength = cdbsize; \
  62. p->Spt.SenseInfoLength = 12; \
  63. p->Spt.DataIn = datain; \
  64. p->Spt.DataTransferLength = datasize; \
  65. p->Spt.TimeOutValue = 20; \
  66. p->Spt.SenseInfoOffset = \
  67. ((DWORD) &(p->SenseInfoBuffer[0]) - (DWORD) (p)); \
  68. p->Spt.DataBufferOffset = \
  69. ((DWORD) &(p->DataBuffer[0]) - (DWORD) (p))
  70. DWORD StartStopCommand(HANDLE device, int argc, char *argv[]);
  71. DWORD TestCommand(HANDLE device, int argc, char *argv[]);
  72. DWORD ReadTOCCommand(HANDLE device, int argc, char *argv[]);
  73. DWORD PlayCommand(HANDLE device, int argc, char *argv[]);
  74. DWORD PauseResumeCommand(HANDLE device, int argc, char *argv[]);
  75. DWORD SendCommand(HANDLE device, int argc, char *argv[]);
  76. DWORD ListCommand(HANDLE device, int argc, char *argv[]);
  77. //
  78. // List of commands
  79. // all command names are case sensitive
  80. // arguments are passed into command routines
  81. // list must be terminated with NULL command
  82. // command will not be listed in help if description == NULL
  83. //
  84. COMMAND CommandArray[] = {
  85. {"eject", "spins down and ejects the specified drive", StartStopCommand},
  86. {"help", "help for all commands", ListCommand},
  87. {"load", "loads the specified drive", StartStopCommand},
  88. {"pause", "pauses audio playback", PauseResumeCommand},
  89. {"play", "[start track [end track]] plays audio tracks [", PlayCommand},
  90. {"resume", "resumes paused audio playback", PauseResumeCommand},
  91. {"send", NULL, SendCommand},
  92. {"start", "spins up the drive", StartStopCommand},
  93. {"stop", "spinds down the drive", StartStopCommand},
  94. {"test", NULL, TestCommand},
  95. {"toc", "prints the table of contents", ReadTOCCommand},
  96. {NULL, NULL, NULL}
  97. };
  98. #define STATUS_SUCCESS 0
  99. int __cdecl main(int argc, char *argv[])
  100. {
  101. int i = 0;
  102. HANDLE h;
  103. char buffer[32];
  104. if(argc < 3) {
  105. printf("Usage: cdp <drive> <command> [parameters]\n");
  106. printf("possible commands: \n");
  107. ListCommand(NULL, argc, argv);
  108. printf("\n");
  109. return -1;
  110. }
  111. sprintf(buffer, "\\\\.\\%s", argv[1]);
  112. dbg(printf("Sending command %s to drive %s\n", argv[2], buffer));
  113. h = CreateFile(buffer,
  114. GENERIC_READ,
  115. FILE_SHARE_READ,
  116. NULL,
  117. OPEN_EXISTING,
  118. FILE_ATTRIBUTE_NORMAL,
  119. NULL);
  120. if(h == INVALID_HANDLE_VALUE) {
  121. printf("Error %d opening device %s\n", GetLastError(), buffer);
  122. return -2;
  123. }
  124. //
  125. // Iterate through the command array and find the correct function to
  126. // call.
  127. //
  128. while(CommandArray[i].Name != NULL) {
  129. if(strcmp(argv[2], CommandArray[i].Name) == 0) {
  130. (CommandArray[i].Function)(h, (argc - 2), &(argv[2]));
  131. break;
  132. }
  133. i++;
  134. }
  135. if(CommandArray[i].Name == NULL) {
  136. printf("Unknown command %s\n", argv[2]);
  137. }
  138. CloseHandle(h);
  139. return 0;
  140. }
  141. DWORD StartStopCommand(HANDLE device, int argc, char *argv[])
  142. /*++
  143. Routine Description:
  144. Sends down a startstop command.
  145. Arguments:
  146. device - a file handle to send the ioctl to
  147. argc - the number of additional arguments. should be zero
  148. argv[0] - "eject", "load", "start" or "stop"
  149. Return Value:
  150. STATUS_SUCCESS if successful
  151. The value of GetLastError() from the point of failure
  152. --*/
  153. {
  154. PSPT_WITH_BUFFERS spt;
  155. PCDB cdb;
  156. DWORD returned;
  157. DWORD errorValue = STATUS_SUCCESS;
  158. DWORD packetSize;
  159. UCHAR loadEject = 0;
  160. UCHAR start = 0;
  161. if(strcmp("eject", argv[0]) == 0) {
  162. loadEject = 1;
  163. start = 0;
  164. } else if(strcmp("load", argv[0]) == 0) {
  165. loadEject = 1;
  166. start = 1;
  167. } else if(strcmp("start", argv[0]) == 0) {
  168. loadEject = 0;
  169. start = 1;
  170. } else if(strcmp("stop", argv[0]) == 0) {
  171. loadEject = 0;
  172. start = 0;
  173. } else {
  174. assert(0);
  175. }
  176. MAKE_SPT(spt, packetSize, 6, 0, 0);
  177. cdb = (PCDB) &(spt->Spt.Cdb[0]);
  178. cdb->START_STOP.OperationCode = SCSIOP_START_STOP_UNIT;
  179. cdb->START_STOP.Immediate = 0;
  180. cdb->START_STOP.Start = start;
  181. cdb->START_STOP.LoadEject = loadEject;
  182. if(!DeviceIoControl(device,
  183. IOCTL_SCSI_PASS_THROUGH,
  184. spt,
  185. sizeof(SPT_WITH_BUFFERS),
  186. spt,
  187. sizeof(SPT_WITH_BUFFERS),
  188. &returned,
  189. FALSE)) {
  190. errorValue = GetLastError();
  191. printf("Eject - error sending IOCTL (%d)\n", errorValue);
  192. }
  193. free(spt);
  194. return errorValue;
  195. }
  196. DWORD LoadCommand(HANDLE device, int argc, char *argv[])
  197. /*++
  198. Routine Description:
  199. Creates an START_STOP pass through ioctl and sends it to the passed in
  200. file handle
  201. Arguments:
  202. device - a file handle to send the ioctl to
  203. argc - the number of additional arguments. should be zero
  204. argv - the additional arguments
  205. Return Value:
  206. STATUS_SUCCESS if successful
  207. The value of GetLastError() from the point of failure
  208. --*/
  209. {
  210. PSPT_WITH_BUFFERS spt;
  211. PCDB cdb;
  212. DWORD returned;
  213. DWORD errorValue = STATUS_SUCCESS;
  214. DWORD packetSize;
  215. printf("Loading cd\n");
  216. MAKE_SPT(spt, packetSize, 6, 0, 0);
  217. cdb = (PCDB) &(spt->Spt.Cdb[0]);
  218. cdb->START_STOP.OperationCode = SCSIOP_START_STOP_UNIT;
  219. cdb->START_STOP.Immediate = 0;
  220. cdb->START_STOP.Start = 1;
  221. cdb->START_STOP.LoadEject = 1;
  222. if(!DeviceIoControl(device,
  223. IOCTL_SCSI_PASS_THROUGH,
  224. spt,
  225. packetSize,
  226. spt,
  227. packetSize,
  228. &returned,
  229. FALSE)) {
  230. errorValue = GetLastError();
  231. printf("Load - error sending IOCTL (%d)\n", errorValue);
  232. }
  233. free(spt);
  234. return STATUS_SUCCESS;
  235. }
  236. DWORD ReadTOCCommand(HANDLE device, int argc, char *argv[])
  237. /*++
  238. Routine Description:
  239. Reads and prints out the cdrom's table of contents
  240. Arguments:
  241. device - a file handle to send the ioctl to
  242. argc - the number of additional arguments. should be zero
  243. argv - the additional arguments
  244. Return Value:
  245. STATUS_SUCCESS if successful
  246. The value of GetLastError() from the point of failure
  247. --*/
  248. {
  249. PSPT_WITH_BUFFERS spt;
  250. DWORD errorValue = STATUS_SUCCESS;
  251. DWORD packetSize, returned = 0;
  252. PCDB cdb;
  253. int numTracks, i;
  254. PTRACK track;
  255. // DWORD addr;
  256. printf("Reading Table of Contents\n");
  257. //
  258. // Allocate an SPT packet big enough to hold the 4 byte TOC header
  259. //
  260. MAKE_SPT(spt, packetSize, 10, 1, 0x04);
  261. cdb = (PCDB) &(spt->Spt.Cdb[0]);
  262. cdb->READ_TOC.OperationCode = SCSIOP_READ_TOC;
  263. cdb->READ_TOC.Msf = 0;
  264. cdb->READ_TOC.StartingTrack = 0;
  265. cdb->READ_TOC.AllocationLength[1] = 0x04;
  266. if(!DeviceIoControl(device,
  267. IOCTL_SCSI_PASS_THROUGH,
  268. spt,
  269. packetSize,
  270. spt,
  271. packetSize,
  272. &returned,
  273. FALSE)) {
  274. errorValue = GetLastError();
  275. printf("Error %d sending READ_TOC pass through\n", errorValue);
  276. goto EndReadTOC;
  277. } else {
  278. dbg(printf("READ_TOC pass through returned %d bytes\n", returned));
  279. }
  280. printf("TOC Data Length: %d\n", (WORD) *(spt->DataBuffer));
  281. printf("First Track Number: %d\n", spt->DataBuffer[2]);
  282. printf("Last Track Number: %d\n", spt->DataBuffer[3]);
  283. numTracks = spt->DataBuffer[3] - spt->DataBuffer[2] + 1;
  284. free(spt);
  285. MAKE_SPT(spt, packetSize, 10, 1, (4 + (numTracks * 8)));
  286. cdb = (PCDB) &(spt->Spt.Cdb[0]);
  287. cdb->READ_TOC.OperationCode = SCSIOP_READ_TOC;
  288. cdb->READ_TOC.Msf = 0;
  289. cdb->READ_TOC.StartingTrack = 1;
  290. cdb->READ_TOC.AllocationLength[1] = 0x04 + (numTracks * 8);
  291. if(!DeviceIoControl(device,
  292. IOCTL_SCSI_PASS_THROUGH,
  293. spt,
  294. packetSize,
  295. spt,
  296. packetSize,
  297. &returned,
  298. FALSE)) {
  299. errorValue = GetLastError();
  300. printf("Error %d sending READ_TOC pass through\n", errorValue);
  301. goto EndReadTOC;
  302. } else {
  303. dbg(printf("READ_TOC pass through returned %d bytes\n", returned));
  304. }
  305. track = (PTRACK) &(spt->DataBuffer[4]);
  306. printf("Number ADR Control Address (MSF)\n");
  307. printf("------ --- ------- -------------\n");
  308. for(i = 0; i < numTracks; i++) {
  309. printf("%6d %3d %7d %3d:%3d:%3d\n", track->TrackNumber,
  310. track->ADR,
  311. track->Control,
  312. track->Address.MSF.M,
  313. track->Address.MSF.S,
  314. track->Address.MSF.F);
  315. track++;
  316. }
  317. EndReadTOC:
  318. free(spt);
  319. return errorValue;
  320. }
  321. DWORD PlayCommand(HANDLE device, int argc, char *argv[])
  322. /*++
  323. Routine Description:
  324. Plays an audio track
  325. Arguments:
  326. device - a file handle to send the ioctl to
  327. argc - the number of additional arguments.
  328. argv[1] - the starting track. Starts at zero if this is not here
  329. argv[2] - the ending track. Let track if not specified
  330. Return Value:
  331. STATUS_SUCCESS if successful
  332. The value of GetLastError() from the point of failure
  333. --*/
  334. {
  335. PSPT_WITH_BUFFERS spt;
  336. DWORD errorValue = STATUS_SUCCESS;
  337. DWORD packetSize, returned = 0;
  338. PCDB cdb;
  339. char startingTrack = 1, endingTrack = -1;
  340. if(argc > 2) endingTrack = atoi(argv[2]);
  341. if(argc > 1) startingTrack = atoi(argv[1]);
  342. printf("Playing from track %d to ", startingTrack);
  343. if(endingTrack == -1) {
  344. printf("end of disk\n");
  345. } else {
  346. printf("track %d\n", endingTrack);
  347. }
  348. //
  349. // Allocate an SPT packet big enough to hold the 4 byte TOC header
  350. //
  351. MAKE_SPT(spt, packetSize, 10, 1, 0);
  352. //
  353. // Unfortunately no one defined the PLAY_INDEX command for us so
  354. // cheat and use MSF
  355. //
  356. cdb = (PCDB) &(spt->Spt.Cdb[0]);
  357. cdb->PLAY_AUDIO_MSF.OperationCode = SCSIOP_PLAY_TRACK_INDEX;
  358. cdb->PLAY_AUDIO_MSF.StartingS = startingTrack;
  359. cdb->PLAY_AUDIO_MSF.StartingF = 0;
  360. cdb->PLAY_AUDIO_MSF.EndingS = endingTrack;
  361. cdb->PLAY_AUDIO_MSF.EndingF = 0;
  362. if(!DeviceIoControl(device,
  363. IOCTL_SCSI_PASS_THROUGH,
  364. spt,
  365. packetSize,
  366. spt,
  367. packetSize,
  368. &returned,
  369. FALSE)) {
  370. errorValue = GetLastError();
  371. printf("Error %d sending PLAY_AUDIO_INDEX pass through\n", errorValue);
  372. } else {
  373. dbg(printf("PLAY_AUDIO_INDEX pass through returned %d bytes\n", returned));
  374. }
  375. free(spt);
  376. return errorValue;
  377. }
  378. DWORD PauseResumeCommand(HANDLE device, int argc, char *argv[])
  379. /*++
  380. Routine Description:
  381. pauses or resumes audio playback
  382. Arguments:
  383. device - a file handle to send the ioctl to
  384. argc - the number of additional arguments.
  385. argv[0] - "pause" or "resume"
  386. Return Value:
  387. STATUS_SUCCESS if successful
  388. The value of GetLastError() from the point of failure
  389. --*/
  390. {
  391. PSPT_WITH_BUFFERS spt;
  392. DWORD errorValue = STATUS_SUCCESS;
  393. DWORD packetSize, returned = 0;
  394. PCDB cdb;
  395. char pauseResume;
  396. if(strcmp("pause", argv[0]) == 0) pauseResume = 0;
  397. else pauseResume = 1;
  398. printf("%s cdrom playback\n", (pauseResume ? "Pausing" : "Resuming"));
  399. //
  400. // Allocate an SPT packet big enough to hold the 4 byte TOC header
  401. //
  402. MAKE_SPT(spt, packetSize, 10, 1, 0);
  403. //
  404. // Unfortunately no one defined the PLAY_INDEX command for us so
  405. // cheat and use MSF
  406. //
  407. cdb = (PCDB) &(spt->Spt.Cdb[0]);
  408. cdb->PAUSE_RESUME.OperationCode = SCSIOP_PAUSE_RESUME;
  409. cdb->PAUSE_RESUME.Action = pauseResume;
  410. if(!DeviceIoControl(device,
  411. IOCTL_SCSI_PASS_THROUGH,
  412. spt,
  413. packetSize,
  414. spt,
  415. packetSize,
  416. &returned,
  417. FALSE)) {
  418. errorValue = GetLastError();
  419. printf("Error %d sending PAUSE_RESUME pass through\n", errorValue);
  420. } else {
  421. dbg(printf("PAUSE_RESUME pass through returned %d bytes\n", returned));
  422. }
  423. free(spt);
  424. return errorValue;
  425. }
  426. DWORD SendCommand(HANDLE device, int argc, char *argv[])
  427. /*++
  428. Routine Description:
  429. Parses a hex byte string and creates a cdb to send down.
  430. Arguments:
  431. device - a file handle to send the ioctl to
  432. argc - the number of additional arguments. should be zero
  433. argv[1] - The CDB to send in a quoted hex byte string
  434. "47 00 00 00 01 00 00 ff 00 00"
  435. argv[2] - for data in commands: the number of bytes (decimal) to
  436. expect from the target
  437. for data out commands: a quoted byte string containing the
  438. data to be sent
  439. Return Value:
  440. STATUS_SUCCESS if successful
  441. The value of GetLastError() from the point of failure
  442. --*/
  443. {
  444. PSPT_WITH_BUFFERS spt;
  445. PCDB cdb;
  446. DWORD returned;
  447. DWORD errorValue = STATUS_SUCCESS;
  448. DWORD packetSize;
  449. int i;
  450. UCHAR cdbLength = 0;
  451. DWORD dataLength = 0;
  452. MAKE_SPT(spt, packetSize, 6, 0, 0);
  453. cdb = (PCDB) &(spt->Spt.Cdb[0]);
  454. //
  455. // Determine the length of the CDB first
  456. //
  457. for(i = 0; i < 12; i++) {
  458. spt->Spt.Cdb[i];
  459. }
  460. if(!DeviceIoControl(device,
  461. IOCTL_SCSI_PASS_THROUGH,
  462. spt,
  463. sizeof(SPT_WITH_BUFFERS),
  464. spt,
  465. sizeof(SPT_WITH_BUFFERS),
  466. &returned,
  467. FALSE)) {
  468. errorValue = GetLastError();
  469. printf("Eject - error sending IOCTL (%d)\n", errorValue);
  470. }
  471. free(spt);
  472. return errorValue;
  473. }
  474. DWORD TestCommand(HANDLE device, int argc, char *argv[])
  475. /*++
  476. Routine Description:
  477. Tests the command "parsing"
  478. Arguments:
  479. device - a file handle to send the ioctl to
  480. argc - the number of additional arguments. should be zero
  481. argv - the additional arguments
  482. Return Value:
  483. STATUS_SUCCESS if successful
  484. The value of GetLastError() from the point of failure
  485. --*/
  486. {
  487. int i;
  488. printf("Test - %d additional arguments\n", argc);
  489. for(i = 0; i < argc; i++) {
  490. printf("arg %d: %s\n", i, argv[i]);
  491. }
  492. return STATUS_SUCCESS;
  493. }
  494. DWORD ListCommand(HANDLE device, int argc, char *argv[])
  495. /*++
  496. Routine Description:
  497. Prints out the command list
  498. Arguments:
  499. device - unused
  500. argc - unused
  501. argv - unused
  502. Return Value:
  503. STATUS_SUCCESS
  504. --*/
  505. {
  506. int i = 0;
  507. while(CommandArray[i].Name != NULL) {
  508. if(CommandArray[i].Description != NULL) {
  509. printf("\t%s - %s\n",
  510. CommandArray[i].Name,
  511. CommandArray[i].Description);
  512. }
  513. i++;
  514. }
  515. return STATUS_SUCCESS;
  516. }