| 1 | #include <stdio.h> |
| 2 | #include <unistd.h> |
| 3 | #include <stdlib.h> |
| 4 | #include <stdint.h> |
| 5 | #include <fcntl.h> |
| 6 | #include <string.h> |
| 7 | #include <errno.h> |
| 8 | #include <getopt.h> |
| 9 | |
| 10 | #include "speed8.h" |
| 11 | #include "link.h" |
| 12 | #include "rk05.h" |
| 13 | #include "memory.h" |
| 14 | #include "log.h" |
| 15 | |
| 16 | #define PORT "/dev/ttyUSB0" |
| 17 | |
| 18 | int fd; |
| 19 | |
| 20 | static const struct option longopts[]={ |
| 21 | {"help",0,NULL,'h'}, |
| 22 | {"read",0,NULL,'r'}, |
| 23 | {"write",0,NULL,'w'}, |
| 24 | {"verbose",0,NULL,'v'}, |
| 25 | {"port",1,NULL,'p'}, |
| 26 | {"drive",1,NULL,'d'}, |
| 27 | {"quiet",0,NULL,'q'}, |
| 28 | {"loglevel",1,NULL,'l'}, |
| 29 | {"verify",0,NULL,'V'}, |
| 30 | {NULL} |
| 31 | }; |
| 32 | |
| 33 | static const char optstring[]="hrwvp:d:l:qV"; |
| 34 | |
| 35 | static void help(void){ |
| 36 | fprintf(stderr, "\ |
| 37 | \n\ |
| 38 | Usage: rktool {-r|-w} [-v] [-V] [-h] [-d<unit_number>] [-l<loglevel>] <imagefile>\n\ |
| 39 | \n\ |
| 40 | This tool will help you to backup restore RK05 media on a PDP8 running speed8.\n\ |
| 41 | \n\ |
| 42 | Options:\n\ |
| 43 | \n\ |
| 44 | -r, --read Read disk (default if nothing specified)\n\ |
| 45 | -w, --write Write disk\n\ |
| 46 | -d, --drive= Select drive number 0-3 (defaults to 0)\n\ |
| 47 | -p, --port= Serial port to use (defaults to /dev/ttyUSB0)\n\ |
| 48 | -V, --verify Verify after read or write\n\ |
| 49 | -v, --verbose Be verbose\n\ |
| 50 | -q, --quiet Be quiet, opposite of -v\n\ |
| 51 | -l, --loglevel= Set loglevel for debugging purposes\n\ |
| 52 | -h, --help Display this help and exit\n\ |
| 53 | \n\ |
| 54 | "); |
| 55 | exit(100); |
| 56 | } |
| 57 | |
| 58 | static char defaultport[]=PORT; |
| 59 | |
| 60 | int main(int argc, char ** argv){ |
| 61 | int res; |
| 62 | int e; |
| 63 | |
| 64 | int drive=0; |
| 65 | #define READ 0 |
| 66 | #define WRITE 1 |
| 67 | int action=READ; |
| 68 | char * port=defaultport; |
| 69 | char * file=NULL; |
| 70 | int verify=0; |
| 71 | char opt; |
| 72 | |
| 73 | opterr=1; |
| 74 | opt=getopt_long(argc,argv,optstring,longopts,NULL); |
| 75 | |
| 76 | while(opt>0){ |
| 77 | switch(opt){ |
| 78 | case 'v': |
| 79 | loglevel++; |
| 80 | break; |
| 81 | case 'h': |
| 82 | help(); |
| 83 | break; |
| 84 | case 'r': |
| 85 | action=READ; |
| 86 | break; |
| 87 | case 'w': |
| 88 | action=WRITE; |
| 89 | break; |
| 90 | case 'p': |
| 91 | if (!optarg) { |
| 92 | error("-p needs an argument!\n"); |
| 93 | exit(1); |
| 94 | } |
| 95 | port=optarg; |
| 96 | break; |
| 97 | case 'd': |
| 98 | if (!optarg) { |
| 99 | error("-d needs an argument!\n"); |
| 100 | exit(1); |
| 101 | } |
| 102 | drive=strtol(optarg, NULL, 0); |
| 103 | break; |
| 104 | case 'q': |
| 105 | loglevel=LL_QUIET; |
| 106 | break; |
| 107 | case 'l': |
| 108 | if (!optarg) { |
| 109 | error("-l needs an argument!\n"); |
| 110 | exit(1); |
| 111 | } |
| 112 | loglevel=strtol(optarg,NULL,0); |
| 113 | break; |
| 114 | case 'V': |
| 115 | verify=1; |
| 116 | break; |
| 117 | case '?': |
| 118 | help(); |
| 119 | break; |
| 120 | } |
| 121 | opt=getopt_long(argc,argv,optstring,longopts,NULL); |
| 122 | } |
| 123 | |
| 124 | file=argv[optind]; |
| 125 | |
| 126 | if (!file) { |
| 127 | fprintf(stderr,"Error: No file specified!\n"); |
| 128 | help(); |
| 129 | } |
| 130 | |
| 131 | debug("Opening port \"%s\".\n",port); |
| 132 | debug("Image file: %s\n", file); |
| 133 | debug("Flushing first\n"); |
| 134 | |
| 135 | res=link_flush(port); |
| 136 | |
| 137 | if (res <0){ |
| 138 | ERROR perror("While initializing link"); |
| 139 | return -1; |
| 140 | } |
| 141 | debug("Opening for real now\n"); |
| 142 | res=link_open(port); |
| 143 | if (res <0){ |
| 144 | ERROR perror("While initializing link"); |
| 145 | return -1; |
| 146 | } |
| 147 | |
| 148 | if (action==WRITE){ |
| 149 | uint16_t origin[RK05_DISKSIZE]; |
| 150 | uint16_t readback[RK05_DISKSIZE]; |
| 151 | |
| 152 | int fd=open(file,O_RDONLY); |
| 153 | if (fd<0){ |
| 154 | ERROR perror("While opening file"); |
| 155 | exit(2); |
| 156 | } |
| 157 | res=read(fd,(char*)origin,sizeof(origin)); |
| 158 | WARN if (res<RK05_DISKSIZE*2) |
| 159 | warn("Read only %i bytes! Hope, that's ok.\n",res); |
| 160 | close(fd); |
| 161 | |
| 162 | res=rk05_write_disk(drive,origin); |
| 163 | if(res<0){ |
| 164 | ERROR perror("While writing disk image"); |
| 165 | exit(3); |
| 166 | } |
| 167 | |
| 168 | if (verify){ |
| 169 | info("Reading back image.\n"); |
| 170 | res=rk05_read_disk(drive,readback); |
| 171 | if(res<0){ |
| 172 | ERROR perror("While reading disk image"); |
| 173 | exit(3); |
| 174 | } |
| 175 | int i; |
| 176 | e=0; |
| 177 | int cylinder,side,sector,word; |
| 178 | for (cylinder=0; cylinder<RK05_CYLINDERS;cylinder++) |
| 179 | for (side=0; side<1; side++) |
| 180 | for (sector=0; sector<16; sector++) |
| 181 | for (word=0; word<256; word++){ |
| 182 | i=cylinder*2*16*256+side*16*256+sector*256+word; |
| 183 | if (readback[i]!=origin[i]){ |
| 184 | error("Verify mismatch! Cylinder %i, Side %i, Sector %i, Word %i\n", |
| 185 | cylinder,side,sector,word); |
| 186 | e++; |
| 187 | } |
| 188 | } |
| 189 | if (e) { |
| 190 | error("Found %i verify errors!\n",e); |
| 191 | exit(3); |
| 192 | } |
| 193 | } // verify |
| 194 | } // WRITE |
| 195 | |
| 196 | if (action==READ){ |
| 197 | |
| 198 | uint16_t target[RK05_DISKSIZE]; |
| 199 | uint16_t target2[RK05_DISKSIZE]; |
| 200 | |
| 201 | info("Reading drive %i\n",drive); |
| 202 | res=0; |
| 203 | res=rk05_read_disk(drive,target); |
| 204 | if(res<0){ |
| 205 | ERROR perror("While reading disk image"); |
| 206 | exit(3); |
| 207 | } |
| 208 | if (verify){ |
| 209 | e=0; |
| 210 | info("Reading again\n"); |
| 211 | res=rk05_read_disk(drive,target2); |
| 212 | if(res<0){ |
| 213 | ERROR perror("While reading disk image"); |
| 214 | exit(3); |
| 215 | } |
| 216 | int i; |
| 217 | for (i=0; i<RK05_DISKSIZE ;i++){ |
| 218 | debug("verify: %i\n",i); |
| 219 | if (target[i]!=target2[i]){ |
| 220 | error("Verify mismatch! Cylinder %i, Side %i, Sector %i, Word %i\n", |
| 221 | i>>13,(i>>12)&1,(i>>8)&15,i&256); |
| 222 | e++; |
| 223 | } |
| 224 | } |
| 225 | } |
| 226 | int fd=open(file,O_WRONLY|O_CREAT|O_TRUNC,0640); |
| 227 | if (fd<0){ |
| 228 | ERROR perror("While opening file"); |
| 229 | exit(2); |
| 230 | } |
| 231 | debug("File \"%s\" opened.\n",file); |
| 232 | res=write(fd,target,sizeof(target)); |
| 233 | if (res!=sizeof(target)){ |
| 234 | ERROR perror ("While writing file to disk"); |
| 235 | exit(1); |
| 236 | } |
| 237 | |
| 238 | } |
| 239 | |
| 240 | link_close(); |
| 241 | link_flush(file); |
| 242 | if (e) { |
| 243 | error("Found %i verify errors!\n",e); |
| 244 | exit(5); |
| 245 | } |
| 246 | return 0; |
| 247 | } |