2 * small utility to read from atapi and scsi devices
10 #include <sys/ioctl.h>
13 #include <linux/cdrom.h>
15 #define DISK "/dev/sda"
17 #define MAX_XFER (65535)
19 #define ALIGN(buf) (char *) (((unsigned long) (buf) + 4095) & ~(4095))
21 static char device[128];
22 static char output[128];
23 static char rate_output[128];
25 static int block_size;
26 static unsigned int blocks;
27 static unsigned long nr_blocks;
28 static int write_output;
29 static unsigned long start_lba;
30 static int force_unaligned;
31 static int write_rate;
33 int parse_options(int argc, char *argv[])
40 c = getopt(argc, argv, "d:n:o:s:p:ur:");
46 strcpy(device, optarg);
49 blocks = strtol(optarg, NULL, 10);
52 strcpy(output, optarg);
56 nr_blocks = strtol(optarg, NULL, 10);
59 start_lba = strtol(optarg, NULL, 10);
65 strcpy(rate_output, optarg);
69 printf("%s: -d device [-s # blocks] [-n # blocks per cmd] [-o output file] [-p start lba] [-u force unaligned buffer]\n", argv[0]);
77 int get_capacity(int fd, int *bs, unsigned long *lba)
79 struct sg_io_hdr *hdr = malloc(sizeof(*hdr));
80 unsigned char buf[8], cdb[16];
84 memset(hdr, 0, sizeof(*hdr));
85 memset(cdb, 0, sizeof(cdb));
86 memset(buf, 0, sizeof(buf));
88 hdr->interface_id = 'S';
89 hdr->cmd_len = sizeof(cdb);
90 hdr->dxfer_direction = SG_DXFER_FROM_DEV;
92 hdr->dxfer_len = sizeof(buf);
95 hdr->cmdp[0] = GPCMD_READ_CDVD_CAPACITY;
97 if (ioctl(fd, SG_IO, hdr) < 0) {
98 perror("read capacity\n");
99 printf("sense key: %x\n", hdr->status);
103 *lba = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
107 *bs = (buf[4] << 24) | (buf[5] << 16) | (buf[6] << 8) | buf[7];
111 unsigned long time_elapsed(struct timeval *start, struct timeval *end)
113 int seconds = end->tv_sec - start->tv_sec;
114 int useconds = end->tv_usec - start->tv_usec;
116 return (1000 * seconds) + (useconds / 1000);
119 void print_datarate(unsigned long sectors_since_last, int time_spent)
124 printf("disk %s: data rate %luKib/sec\r", device, 1000 * (sectors_since_last * block_size / 1024) / time_spent);
127 void write_datarate(int fd, unsigned long lba, unsigned long sectors, int time)
134 memset(buffer, 0, sizeof(buffer));
135 snprintf(buffer, sizeof(buffer), "%lu, %lu\n", lba, (sectors * block_size) / time);
136 write(fd, buffer, strlen(buffer));
139 int write_the_crap(int fd_out, char *buffer, int length)
143 ret = write(fd_out, buffer, length);
145 printf("\nwanted to write %u, wrote %d\n", length, ret);
150 int main(int argc, char *argv[])
152 struct sg_io_hdr *hdr;
153 unsigned long lba, bytes;
155 int fd, fd_out, fd_rate;
156 unsigned long time_spent;
157 unsigned long sectors_since_last, end_lba;
158 struct timeval start_time, end_time;
159 unsigned char cdb[16];
161 if (parse_options(argc, argv))
164 fd = open(device, O_RDONLY | O_NONBLOCK);
166 perror("open input");
172 fd_out = open(output, O_WRONLY | O_CREAT | O_TRUNC | O_LARGEFILE, 0644);
174 perror("open output");
181 fd_rate = open(rate_output, O_WRONLY | O_CREAT | O_TRUNC, 0644);
183 perror("open rate output");
188 if (get_capacity(fd, &block_size, &end_lba)) {
189 printf("%s: can't retrieve capacity\n", device);
194 blocks = MAX_XFER / block_size;
196 hdr = malloc(sizeof(*hdr));
197 memset(hdr, 0, sizeof(*hdr));
199 ptr = malloc(blocks * block_size + block_size - 1);
200 if (force_unaligned) {
202 if (!(((unsigned long) buffer) & (block_size - 1)))
208 memset(hdr->cmdp, 0, sizeof(cdb));
210 hdr->interface_id = 'S';
211 hdr->cmd_len = sizeof(cdb);
212 hdr->dxfer_direction = SG_DXFER_FROM_DEV;
213 hdr->dxferp = buffer;
214 hdr->cmdp[0] = GPCMD_READ_10;
217 nr_blocks = end_lba - lba + 1;
219 printf("disk %s: reading LBA %lu -> %lu", device, start_lba, end_lba);
220 printf(" in chunks of %u %ub blocks\n", blocks, block_size);
223 time_spent = sectors_since_last = 0;
224 gettimeofday(&start_time, NULL);
226 if (blocks > nr_blocks)
229 hdr->dxfer_len = blocks * block_size;
230 hdr->cmdp[2] = (lba >> 24) & 0xff;
231 hdr->cmdp[3] = (lba >> 16) & 0xff;
232 hdr->cmdp[4] = (lba >> 8) & 0xff;
233 hdr->cmdp[5] = lba & 0xff;
234 hdr->cmdp[7] = (blocks >> 8) & 0xff;
235 hdr->cmdp[8] = blocks & 0xff;
237 if (ioctl(fd, SG_IO, hdr) < 0) {
238 int good_blocks = (hdr->dxfer_len - hdr->resid) / block_size;
239 printf("sense key: %x\n", hdr->status);
241 printf("\n%s: IO error at lba %lu\n", device, lba + good_blocks);
242 blocks = good_blocks;
244 printf("\n%s: giving up at lba %lu\n", device, lba);
250 printf("%s: residual %u\n", device, hdr->resid);
252 gettimeofday(&end_time, NULL);
256 sectors_since_last += blocks;
257 time_spent = time_elapsed(&start_time, &end_time);
258 if (time_spent >= 500) {
259 print_datarate(sectors_since_last, time_spent);
262 write_datarate(fd_rate, lba, sectors_since_last, time_spent);
263 sectors_since_last = 0;
264 gettimeofday(&start_time, NULL);
268 bytes += blocks * block_size;
271 write_the_crap(fd_out, buffer, blocks * block_size);
273 } while (lba <= end_lba);
275 print_datarate(sectors_since_last, time_spent);