Subversion Repositories freemyipod

Rev

Rev 975 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
975 user890104 1
//
2
//
3
//    Copyright 2011 TheSeven
4
//    Copyright 2012 njsg
5
//
6
//
7
//    This file is part of emCORE.
8
//
9
//    emCORE is free software: you can redistribute it and/or
10
//    modify it under the terms of the GNU General Public License as
11
//    published by the Free Software Foundation, either version 2 of the
12
//    License, or (at your option) any later version.
13
//
14
//    emCORE is distributed in the hope that it will be useful,
15
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
16
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17
//    See the GNU General Public License for more details.
18
//
19
//    You should have received a copy of the GNU General Public License along
20
//    with emCORE.  If not, see <http://www.gnu.org/licenses/>.
21
//
22
//
23
 
976 user890104 24
// This is a linux-based implementation of ipodscsi, originally
25
// implemented by TheSeven for Microsoft Windows.
26
//
27
// Currently, this only works for iPod 6g/Classic, but it should also
28
// work with minor modifications for, e.g., Nano 4g, which uses a
29
// similar firmware update mechanism.
30
 
975 user890104 31
#include <string.h>
32
#include <errno.h>
33
#include <unistd.h>
34
#include <fcntl.h>
35
#include <stdio.h>
36
#include <sys/ioctl.h>
37
#include <scsi/sg.h>
38
#include <stdlib.h>
39
 
976 user890104 40
#include "build/version.h"
975 user890104 41
 
42
#define APPLE_PREFIX 0xc6
43
#define CMD_INIT_FIRMWARE 0x90
44
#define CMD_SEND_FIRMWARE 0x91
976 user890104 45
#define CMD_PARTITION 0x94
975 user890104 46
 
976 user890104 47
// Return a file descriptor for the SCSI device, after checking if it
48
// is really a SCSI-generic device.
975 user890104 49
int open_scsi(const char * scsi_device) {
50
  int version;
51
  int sg_fd;
52
 
976 user890104 53
  // Open SCSI device 
54
  if ((sg_fd = open(scsi_device, O_RDWR)) < 0) 
55
  {
975 user890104 56
    perror("error opening given file name");
57
    exit(EXIT_FAILURE);
58
  }
59
 
976 user890104 60
  // Request a very simple scsi-generic ioctl(). If this fails, this
61
  // is not a scsi-generic device.
62
  if (ioctl(sg_fd, SG_GET_VERSION_NUM, &version) < 0) 
63
  {
975 user890104 64
    printf("%s is not a SCSI-generic device.\n", scsi_device);
65
    exit(EXIT_FAILURE);
66
  }
67
 
68
  return sg_fd;
69
}
70
 
71
int usage(char const* msg, char const* msgarg, int argc, char const* const* argv)
72
{
73
    printf(msg, msgarg);
74
    printf("\n"
75
           "\n"
76
           "Usage: %s <scsi_device> <command> [options...]\n"
77
           "\n"
78
	   "  scsi_device is the path to a device managed by the\n"
79
	   "  'scsi-generic' driver. Those are usually /dev/sgN,\n"
80
	   "  where N is the device number.\n"
81
           "\n"
82
           "Commands:\n"
976 user890104 83
           "  writefirmware [-p] [-r] <firmware.mse>\n"
975 user890104 84
           "    -r: Reboot device\n"
85
           "    -p: Repartition device\n", argv[0]);
86
    exit(EXIT_SUCCESS);
87
}
88
 
89
int main(int argc, char const* const* argv)
90
{
976 user890104 91
    int arg = 3;
975 user890104 92
    char const* mse_filename = NULL;
93
    char const* scsi_device = NULL;
94
    int repartition = 0;
95
    int reboot = 0;
96
 
976 user890104 97
    int f; // Firmware file descriptor
975 user890104 98
    unsigned long bytes;
99
 
976 user890104 100
    printf("iPodSCSI for linux v. " VERSION " r" VERSION_SVN " - Copyright 2012 by Nuno J. Silva (njsg)\n"
101
	   "Based on the original iPodSCSI Windows code by Michael Sparmann (TheSeven)\n"
975 user890104 102
           "This is free software; see the source for copying conditions.  There is NO\n"
103
           "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
104
           "\n");
105
 
976 user890104 106
    if (argc < 3) return usage("Not enough arguments specified", NULL, argc, argv);
975 user890104 107
 
976 user890104 108
    if (strcmp(argv[2], "writefirmware") != 0)
109
    {
110
      usage("Unknown command: %s", argv[2], argc, argv);
111
    }
112
 
113
    scsi_device = argv[1];
114
 
115
 
975 user890104 116
    while (arg < argc)
117
    {
118
        if (argv[arg][0] == '-')
119
        {
120
            if (!strcmp(argv[arg], "-p")) repartition = 1;
121
            else if (!strcmp(argv[arg], "-r")) reboot = 1;
122
            else return usage("Unknown option: %s", argv[arg], argc, argv);
123
        }
976 user890104 124
        else if (mse_filename) return usage("Excessive argument: %s", argv[arg], argc, argv);
125
        else mse_filename = argv[arg];
975 user890104 126
        arg++;
127
    }
128
 
129
    int sg_fd = open_scsi (scsi_device);
130
 
131
    if (!mse_filename) return usage("No MSE file name specified", NULL, argc, argv);
132
 
133
 
134
    f = open(mse_filename, O_RDONLY);
135
 
136
    if (f == -1)
137
    {
138
      perror("Error while opening MSE file");
139
      exit(EXIT_FAILURE);
140
    }
141
 
142
 
143
    {
976 user890104 144
      // Get MSE file size 
975 user890104 145
      struct stat *f_stat = malloc (sizeof (struct stat));
146
      unsigned long size;
147
 
976 user890104 148
      if (fstat (f, f_stat) == -1) 
149
      {
975 user890104 150
	perror("Error while getting MSE file size");
151
	exit(EXIT_FAILURE);
152
      }
153
 
154
      bytes = f_stat->st_size;
155
 
976 user890104 156
      if (bytes & 0xfff) 
157
      {
975 user890104 158
	fprintf(stderr, "MSE file size must be a multiple of 4096\n");
159
	exit (EXIT_FAILURE);
160
      }
161
    }
162
 
163
    int sectors = bytes >> 12;
164
 
976 user890104 165
    // Most commands will have the same prefix.
975 user890104 166
    unsigned char cmdBlk[] = {APPLE_PREFIX, 0, 0, 0, 0, 0};
167
 
976 user890104 168
 
169
    // Prepare a SCSI command structure, which will be changed as
170
    // needed later.
975 user890104 171
    sg_io_hdr_t io_hdr;
172
    memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
173
    io_hdr.interface_id = 'S';
174
    io_hdr.dxfer_direction = SG_DXFER_NONE;
175
    io_hdr.dxfer_len = 0;
176
    io_hdr.dxferp = NULL;
177
    io_hdr.cmdp = cmdBlk;
178
    io_hdr.cmd_len = sizeof(cmdBlk);
179
    io_hdr.mx_sb_len = 0;
180
    io_hdr.sbp = NULL;
181
 
182
    if (repartition)
976 user890104 183
    {
184
      printf("Repartitioning...");
185
      int partsize = sectors << 2;
186
      cmdBlk[1] = CMD_PARTITION;
187
      cmdBlk[2] = (partsize >> 24) & 0xff;
188
      cmdBlk[3] = (partsize >> 16) & 0xff;
189
      cmdBlk[4] = (partsize >> 8) & 0xff;
190
      cmdBlk[5] = (partsize) & 0xff;
191
 
192
      io_hdr.timeout = 60000;     
193
 
194
      if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) 
975 user890104 195
      {
976 user890104 196
	perror("iPod repartitioning SG_IO ioctl error");
197
	exit(EXIT_FAILURE);
198
      }
199
      printf(" done\n");
200
    }
201
 
975 user890104 202
 
976 user890104 203
    printf("Initiating firmware transfer...");
975 user890104 204
 
205
    cmdBlk[1] = CMD_INIT_FIRMWARE;
206
    cmdBlk[2] = 0;
207
    cmdBlk[3] = 0;
208
    cmdBlk[4] = 0;
209
    cmdBlk[5] = 0;
210
 
211
    io_hdr.timeout = 1000;     
212
 
213
 
976 user890104 214
 
215
    if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) 
216
    {
975 user890104 217
      perror("iPod firmware transfer init SG_IO ioctl error");
218
      exit(EXIT_FAILURE);
219
    }
220
    printf(" done\n");
221
 
976 user890104 222
 
223
 
975 user890104 224
    printf("Writing firmware...");
225
 
226
    cmdBlk[1] = CMD_SEND_FIRMWARE;
227
 
228
    while (sectors)
976 user890104 229
    {
230
      int tsize = sectors > 0x10 ? 0x10 : sectors;
231
      int got = 0;
975 user890104 232
 
976 user890104 233
      char * buf = malloc((tsize << 12) * sizeof(char));
975 user890104 234
 
976 user890104 235
      while (got < (tsize << 12))
236
      {
237
	int b = read(f, buf + got, (tsize << 12) - got);
238
	if (b == -1) 
239
	{
240
	  perror("Error reading from MSE file");
241
	  exit(EXIT_FAILURE);
242
	}
243
	got += b;
244
      }
975 user890104 245
 
976 user890104 246
      cmdBlk[3] = tsize;
975 user890104 247
 
976 user890104 248
      io_hdr.dxfer_direction = SG_DXFER_TO_DEV;
249
      io_hdr.dxfer_len = tsize << 12;
250
      io_hdr.dxferp = buf;
251
      io_hdr.timeout = 5000; 
975 user890104 252
 
976 user890104 253
      if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) 
254
      {
255
	perror("iPod firmware transfer SG_IO ioctl error");
256
	exit(EXIT_FAILURE);
257
      }
975 user890104 258
 
976 user890104 259
      sectors -= tsize;
260
      printf(".");
261
      fflush(stdout);
975 user890104 262
    }
263
    printf(" done\n");
264
 
265
 
266
    if (reboot)
267
    {
268
        printf("Rebooting device...");
269
 
270
	cmdBlk[0] = 0x1b;
271
	cmdBlk[1] = 0;
272
	cmdBlk[2] = 0;
273
	cmdBlk[3] = 0;
274
	cmdBlk[4] = 0x02;
275
	cmdBlk[5] = 0;
276
 
277
	io_hdr.dxfer_direction = SG_DXFER_NONE;
278
	io_hdr.dxfer_len = 0;
279
	io_hdr.dxferp = NULL;
280
	io_hdr.timeout = 10000;     
281
 
976 user890104 282
	if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) 
283
	{
975 user890104 284
	  perror("iPod reboot SG_IO ioctl error");
285
	  exit(EXIT_FAILURE);
286
	}
287
 
288
        printf(" done\n");
289
    }
290
 
291
    close(sg_fd);
292
    exit(EXIT_SUCCESS);
293
}