Commit | Line | Data |
---|---|---|
dc61eacc JA |
1 | /* |
2 | * small utility to read from atapi and scsi devices | |
3 | */ | |
dc61eacc JA |
4 | #include <stdio.h> |
5 | #include <fcntl.h> | |
6 | #include <stdlib.h> | |
7 | #include <string.h> | |
8 | #include <unistd.h> | |
9 | #include <getopt.h> | |
10 | #include <sys/ioctl.h> | |
11 | #include <sys/time.h> | |
12 | #include <scsi/sg.h> | |
13 | #include <linux/cdrom.h> | |
14 | ||
15 | #define DISK "/dev/sda" | |
16 | ||
17 | #define MAX_XFER (65535) | |
18 | ||
19 | #define ALIGN(buf) (char *) (((unsigned long) (buf) + 4095) & ~(4095)) | |
20 | ||
21 | static char device[128]; | |
22 | static char output[128]; | |
23 | static char rate_output[128]; | |
24 | ||
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; | |
32 | ||
33 | int parse_options(int argc, char *argv[]) | |
34 | { | |
35 | int c; | |
36 | ||
37 | strcpy(device, DISK); | |
38 | ||
39 | for (;;) { | |
40 | c = getopt(argc, argv, "d:n:o:s:p:ur:"); | |
41 | if (c == -1) | |
42 | break; | |
43 | ||
44 | switch (c) { | |
45 | case 'd': | |
46 | strcpy(device, optarg); | |
47 | break; | |
48 | case 'n': | |
49 | blocks = strtol(optarg, NULL, 10); | |
50 | break; | |
51 | case 'o': | |
52 | strcpy(output, optarg); | |
53 | write_output = 1; | |
54 | break; | |
55 | case 's': | |
56 | nr_blocks = strtol(optarg, NULL, 10); | |
57 | break; | |
58 | case 'p': | |
59 | start_lba = strtol(optarg, NULL, 10); | |
60 | break; | |
61 | case 'u': | |
62 | force_unaligned = 1; | |
63 | break; | |
64 | case 'r': | |
65 | strcpy(rate_output, optarg); | |
66 | write_rate = 1; | |
67 | break; | |
68 | default: | |
69 | printf("%s: -d device [-s # blocks] [-n # blocks per cmd] [-o output file] [-p start lba] [-u force unaligned buffer]\n", argv[0]); | |
70 | return -1; | |
71 | } | |
72 | } | |
73 | ||
74 | return 0; | |
75 | } | |
76 | ||
77 | int get_capacity(int fd, int *bs, unsigned long *lba) | |
78 | { | |
79 | struct sg_io_hdr *hdr = malloc(sizeof(*hdr)); | |
80 | unsigned char buf[8], cdb[16]; | |
81 | ||
82 | *bs = 0; | |
83 | ||
84 | memset(hdr, 0, sizeof(*hdr)); | |
85 | memset(cdb, 0, sizeof(cdb)); | |
86 | memset(buf, 0, sizeof(buf)); | |
87 | ||
88 | hdr->interface_id = 'S'; | |
89 | hdr->cmd_len = sizeof(cdb); | |
90 | hdr->dxfer_direction = SG_DXFER_FROM_DEV; | |
91 | hdr->dxferp = buf; | |
92 | hdr->dxfer_len = sizeof(buf); | |
93 | ||
94 | hdr->cmdp = cdb; | |
95 | hdr->cmdp[0] = GPCMD_READ_CDVD_CAPACITY; | |
96 | ||
97 | if (ioctl(fd, SG_IO, hdr) < 0) { | |
98 | perror("read capacity\n"); | |
99 | printf("sense key: %x\n", hdr->status); | |
100 | return -1; | |
101 | } | |
102 | ||
103 | *lba = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]; | |
104 | if (!*lba) | |
105 | *lba = ~0UL; | |
106 | ||
107 | *bs = (buf[4] << 24) | (buf[5] << 16) | (buf[6] << 8) | buf[7]; | |
108 | return 0; | |
109 | } | |
110 | ||
111 | unsigned long time_elapsed(struct timeval *start, struct timeval *end) | |
112 | { | |
113 | int seconds = end->tv_sec - start->tv_sec; | |
114 | int useconds = end->tv_usec - start->tv_usec; | |
115 | ||
116 | return (1000 * seconds) + (useconds / 1000); | |
117 | } | |
118 | ||
119 | void print_datarate(unsigned long sectors_since_last, int time_spent) | |
120 | { | |
121 | if (!time_spent) | |
122 | time_spent = 1; | |
123 | ||
124 | printf("disk %s: data rate %luKib/sec\r", device, 1000 * (sectors_since_last * block_size / 1024) / time_spent); | |
125 | } | |
126 | ||
127 | void write_datarate(int fd, unsigned long lba, unsigned long sectors, int time) | |
128 | { | |
129 | char buffer[32]; | |
130 | ||
131 | if (fd == -1) | |
132 | return; | |
133 | ||
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)); | |
137 | } | |
138 | ||
139 | int write_the_crap(int fd_out, char *buffer, int length) | |
140 | { | |
141 | int ret; | |
142 | ||
143 | ret = write(fd_out, buffer, length); | |
144 | if (ret != length) | |
145 | printf("\nwanted to write %u, wrote %d\n", length, ret); | |
146 | ||
147 | return ret; | |
148 | } | |
149 | ||
9b6d74e1 | 150 | void hdr_init(struct sg_io_hdr *hdr, char *p, unsigned char *cdb) |
4c9cc53d JA |
151 | { |
152 | memset(hdr, 0, sizeof(*hdr)); | |
153 | ||
154 | hdr->cmdp = cdb; | |
155 | memset(hdr->cmdp, 0, 10); | |
156 | ||
157 | hdr->interface_id = 'S'; | |
158 | hdr->cmd_len = 10; | |
159 | hdr->dxfer_direction = SG_DXFER_FROM_DEV; | |
160 | hdr->dxferp = p; | |
161 | hdr->cmdp[0] = GPCMD_READ_10; | |
162 | } | |
163 | ||
dc61eacc JA |
164 | int main(int argc, char *argv[]) |
165 | { | |
166 | struct sg_io_hdr *hdr; | |
167 | unsigned long lba, bytes; | |
168 | char *buffer, *ptr; | |
169 | int fd, fd_out, fd_rate; | |
170 | unsigned long time_spent; | |
171 | unsigned long sectors_since_last, end_lba; | |
172 | struct timeval start_time, end_time; | |
173 | unsigned char cdb[16]; | |
174 | ||
175 | if (parse_options(argc, argv)) | |
176 | return 1; | |
177 | ||
178 | fd = open(device, O_RDONLY | O_NONBLOCK); | |
179 | if (fd == -1) { | |
180 | perror("open input"); | |
181 | return 1; | |
182 | } | |
183 | ||
184 | fd_out = -1; | |
185 | if (write_output) { | |
186 | fd_out = open(output, O_WRONLY | O_CREAT | O_TRUNC | O_LARGEFILE, 0644); | |
187 | if (fd_out == -1) { | |
188 | perror("open output"); | |
189 | return 2; | |
190 | } | |
191 | } | |
192 | ||
193 | fd_rate = -1; | |
194 | if (write_rate) { | |
195 | fd_rate = open(rate_output, O_WRONLY | O_CREAT | O_TRUNC, 0644); | |
196 | if (fd_rate == -1) { | |
197 | perror("open rate output"); | |
198 | return 3; | |
199 | } | |
200 | } | |
201 | ||
202 | if (get_capacity(fd, &block_size, &end_lba)) { | |
203 | printf("%s: can't retrieve capacity\n", device); | |
204 | return 2; | |
205 | } | |
206 | ||
207 | if (!blocks) | |
208 | blocks = MAX_XFER / block_size; | |
209 | ||
210 | hdr = malloc(sizeof(*hdr)); | |
dc61eacc JA |
211 | |
212 | ptr = malloc(blocks * block_size + block_size - 1); | |
213 | if (force_unaligned) { | |
214 | buffer = ptr; | |
215 | if (!(((unsigned long) buffer) & (block_size - 1))) | |
216 | buffer++; | |
217 | } else | |
218 | buffer = ALIGN(ptr); | |
219 | ||
dc61eacc | 220 | lba = start_lba; |
fce10cc6 | 221 | if (!nr_blocks) |
dc61eacc JA |
222 | nr_blocks = end_lba - lba + 1; |
223 | ||
224 | printf("disk %s: reading LBA %lu -> %lu", device, start_lba, end_lba); | |
225 | printf(" in chunks of %u %ub blocks\n", blocks, block_size); | |
226 | ||
227 | bytes = 0; | |
228 | time_spent = sectors_since_last = 0; | |
229 | gettimeofday(&start_time, NULL); | |
230 | do { | |
231 | if (blocks > nr_blocks) | |
232 | blocks = nr_blocks; | |
233 | ||
4c9cc53d JA |
234 | hdr_init(hdr, buffer, cdb); |
235 | ||
dc61eacc JA |
236 | hdr->dxfer_len = blocks * block_size; |
237 | hdr->cmdp[2] = (lba >> 24) & 0xff; | |
238 | hdr->cmdp[3] = (lba >> 16) & 0xff; | |
239 | hdr->cmdp[4] = (lba >> 8) & 0xff; | |
240 | hdr->cmdp[5] = lba & 0xff; | |
241 | hdr->cmdp[7] = (blocks >> 8) & 0xff; | |
242 | hdr->cmdp[8] = blocks & 0xff; | |
243 | ||
244 | if (ioctl(fd, SG_IO, hdr) < 0) { | |
245 | int good_blocks = (hdr->dxfer_len - hdr->resid) / block_size; | |
246 | printf("sense key: %x\n", hdr->status); | |
247 | if (good_blocks) { | |
248 | printf("\n%s: IO error at lba %lu\n", device, lba + good_blocks); | |
249 | blocks = good_blocks; | |
250 | } else { | |
251 | printf("\n%s: giving up at lba %lu\n", device, lba); | |
252 | break; | |
253 | } | |
254 | } | |
255 | ||
256 | if (hdr->resid) | |
257 | printf("%s: residual %u\n", device, hdr->resid); | |
258 | ||
259 | gettimeofday(&end_time, NULL); | |
260 | ||
261 | nr_blocks -= blocks; | |
262 | ||
263 | sectors_since_last += blocks; | |
264 | time_spent = time_elapsed(&start_time, &end_time); | |
265 | if (time_spent >= 500) { | |
266 | print_datarate(sectors_since_last, time_spent); | |
267 | fflush(stdout); | |
268 | if (fd_rate != -1) | |
269 | write_datarate(fd_rate, lba, sectors_since_last, time_spent); | |
270 | sectors_since_last = 0; | |
271 | gettimeofday(&start_time, NULL); | |
272 | } | |
273 | ||
274 | lba += blocks; | |
275 | bytes += blocks * block_size; | |
276 | ||
277 | if (fd_out != -1) | |
278 | write_the_crap(fd_out, buffer, blocks * block_size); | |
279 | ||
fce10cc6 | 280 | } while (lba <= end_lba && nr_blocks); |
dc61eacc JA |
281 | |
282 | print_datarate(sectors_since_last, time_spent); | |
283 | printf("\n"); | |
284 | free(ptr); | |
285 | close(fd); | |
286 | return 0; | |
287 | } |