bbcdirif.c
10.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
#include "bbclocal.h"
static void
__bbc_direct_close(void *f)
{
}
static int
__bbc_direct_setled(void *f, int ledmask)
{
bbc_hand *hp = f;
unsigned int hbuf[2];
int rv;
hbuf[0] = REQ_SET_LEDS;
hbuf[1] = ledmask;
if ((rv = __bbc_send_cmd(hp->bh_pofd, hbuf, 8)) < 0) return rv;
if ((rv = __bbc_read_rsp(hp->bh_pifd, hbuf, 8)) < 0) return rv;
if (hbuf[0] != 255-REQ_SET_LEDS) {
BBC_LOG(MSG_ERR, "__bbc_direct_setled: sync loss on REQ_SET_LEDS\n");
return BBC_SYNCLOST;
}
return BBC_OK;
}
#define BYTE(x) ((u8)((x) & 0xff))
static int
__bbc_direct_settime(void *f, time_t curtime)
{
bbc_hand *hp = f;
unsigned int hbuf[3];
struct tm *tm;
#ifndef WIN32
struct tm tms;
struct timespec ts;
#endif
int rv;
/*
* Convert to local time in China. Note that on the depot, the
* timezone is not set so we hardwire the local time conversion
* and use gmtime_r to crack it into components to be safe.
*/
curtime += 8*60*60; /* PRC time zone = UTC + 8 hours */
#ifndef WIN32
tm = gmtime_r(&curtime, &tms); /* gmtime_r doesn't change TZ */
#else
tm = gmtime(&curtime); /* gmtime doesn't change TZ */
#endif
if (tm == NULL) {
BBC_LOG(MSG_ERR, "__bbc_direct_settime: gmtime failed\n");
return BBC_ERROR;
}
tm->tm_mon += 1; /* tm_mon ranges from 0 to 11 */
if (tm->tm_year > 100) /* tm_year is years since 1900 */
tm->tm_year -= 100;
if (tm->tm_wday == 0)
tm->tm_wday = 7; /* RTC uses 1 = Mon, ... , 7 = Sun */
hbuf[0] = REQ_SET_TIME;
hbuf[1] = BYTE(tm->tm_year)<<24 | BYTE(tm->tm_mon)<<16
| BYTE(tm->tm_mday)<<8 | BYTE(tm->tm_wday);
hbuf[2] = BYTE(tm->tm_hour)<<16 | BYTE(tm->tm_min)<<8 | BYTE(tm->tm_sec);
if ((rv = __bbc_send_cmd(hp->bh_pofd, hbuf, 8)) < 0) return rv;
if ((rv = __bbc_read_rsp(hp->bh_pifd, hbuf, 8)) < 0) return rv;
if (hbuf[0] != 255-REQ_SET_TIME) {
BBC_LOG(MSG_ERR, "__bbc_direct_settime: sync loss on REQ_SET_TIME\n");
return BBC_SYNCLOST;
}
if ((rv = __bbc_send_cmd(hp->bh_pofd, &hbuf[2], 4)) < 0) return rv;
#ifndef WIN32
/*
* Setting the RTC on BB takes a few milliseconds. Make
* sure that the calling program stalls long enough for it
* to complete before proceeding. Needless to say, this is
* a kludge. The protocol should have been synchronous at
* this level, but changing it now is too messy.
*/
again:
ts.tv_sec = 1;
ts.tv_nsec = 0;
while (nanosleep(&ts, &ts) < 0) {
BBC_LOG(MSG_ERR, "nanosleep interrupted: sec %ld, nsec %ld\n", ts.tv_sec, ts.tv_nsec);
if (ts.tv_sec < 0 || ts.tv_sec > 1 || ts.tv_nsec < 0 || ts.tv_nsec > 999999999L)
goto again;
}
#endif
return BBC_OK;
}
static unsigned char junk[BB_FL_BLOCK_SIZE];
static int
__bbc_direct_read_blocks(void* f, u32 blk, int nblks, void* data, void* spare)
{
bbc_hand *hp = f;
unsigned int hbuf[2], r;
int rv;
BBC_LOG(MSG_DEBUG, "drb: READ_BLOCKS %d count %d (spare 0x%x)\n",
(int)blk, nblks, (int)spare);
/* Initialize spare area */
if (spare) {
memset(spare, 0xff, nblks * BB_FL_SPARE_SIZE);
}
while (nblks-- > 0) {
hbuf[0] = spare ? REQ_READ_BLOCK_SP : REQ_READ_BLOCK;
r = 255-hbuf[0];
hbuf[1] = blk;
if ((rv = __bbc_send_cmd(hp->bh_pofd, hbuf, 8)) < 0) {
BBC_LOG(MSG_ERR, "drd: read block %ld: send cmd %d failed with %d\n",
blk, hbuf[0], rv);
return rv;
}
if ((rv = __bbc_read_rsp(hp->bh_pifd, hbuf, 8)) < 0) {
BBC_LOG(MSG_ERR, "drd: read block %ld: read rsp failed with %d\n",
blk, rv);
return rv;
}
if (data == NULL) data = junk;
if ((rv = __bbc_read_data(hp->bh_pifd, data, BB_FL_BLOCK_SIZE)) < 0) {
BBC_LOG(MSG_ERR, "drd: read block %ld: read data (block) failed with %d\n",
blk, rv);
return rv;
}
if (spare)
if ((rv = __bbc_read_data(hp->bh_pifd, spare, BB_FL_SPARE_SIZE)) < 0) {
BBC_LOG(MSG_ERR, "drd: read block %ld: read data (spare) failed with %d\n",
blk, rv);
return rv;
}
if (hbuf[0] != r) {
BBC_LOG(MSG_ERR, "drd: read block %ld failed (sync loss)\n", blk);
return BBC_SYNCLOST;
}
else if (hbuf[1] != 0) {
BBC_LOG(MSG_ERR, "drd: read block %ld failed with error %d\n",
blk, hbuf[1]);
if ((hbuf[1] == BBCARD_ERR_FAIL) && (spare != NULL)) {
/*
* Check first to see if block marked bad by manufacturer.
* Note that ECC is not computed on the spare area, so single
* bit errors on the status byte will not be corrected. To
* match the way the ROM boot code handles the status byte,
* only consider the block marked bad if the status byte
* contains more than one zero bit.
*/
unsigned char *sparep = (unsigned char *)spare;
if (__bbc_zerobits(sparep[BB_FL_BLOCK_STATUS_OFF]) > 1) {
BBC_LOG(MSG_WARNING, "drd: Blk %ld marked bad by MFG (status %d)\n", blk,
sparep[BB_FL_BLOCK_STATUS_OFF]);
return BBC_BADBLK;
}
}
/* Try to map the error codes we got into libbbc error codes */
switch (hbuf[1]) {
case BBCARD_ERR_FAIL: return BBC_DATAERR;
case BBCARD_ERR_NO_CARD: return BBC_NOCARD;
case BBCARD_ERR_CHANGED: return BBC_CARDCHANGE;
case BBCARD_ERR_INVALID:
default: return BBC_ERROR; /* Some unknown error */
}
}
/*
* Need to check spare area here also, since a marked bad block
* may read without DBE (XXX check Samsung spec).
*/
if (spare) {
/* Check to see if block marked bad by manufacturer */
unsigned char *sparep = (unsigned char *)spare;
if (__bbc_zerobits(sparep[BB_FL_BLOCK_STATUS_OFF]) > 1) {
BBC_LOG(MSG_WARNING, "drd: Blk %ld marked bad by MFG (status %d)\n", blk,
sparep[BB_FL_BLOCK_STATUS_OFF]);
return BBC_BADBLK;
}
}
blk++;
if (data && data != junk)
(char *)data += BB_FL_BLOCK_SIZE;
if (spare)
(char *)spare += BB_FL_SPARE_SIZE;
}
return BBC_OK;
}
/*
* write blocks to the flash device. if spare is non-zero, use it
* as the spare area data for all pages in the block.
*/
static int
__bbc_direct_write_blocks(void* f, u32 blk, int nblks, const void* data, void* spare)
{
bbc_hand *hp = f;
unsigned int hbuf[2], r;
int rv;
BBC_LOG(MSG_DEBUG, "dwb: WRITE_BLOCK %d count %d spare 0x%x\n",
(int)blk, nblks, (int)spare);
while (nblks-- > 0) {
hbuf[0] = spare ? REQ_WRITE_BLOCK_SP : REQ_WRITE_BLOCK;
r = 255-hbuf[0];
hbuf[1] = blk;
if ((rv = __bbc_send_cmd(hp->bh_pofd, hbuf, 8)) < 0) {
BBC_LOG(MSG_ERR, "dwb: write block %ld: send cmd %d failed with %d\n",
blk, hbuf[0], rv);
return rv;
}
if ((rv = __bbc_send_data(hp->bh_pofd, data, BB_FL_BLOCK_SIZE)) < 0) {
BBC_LOG(MSG_ERR, "dwb: write block %ld: send data (block) failed with %d\n",
blk, rv);
return rv;
}
if (spare)
if ((rv = __bbc_send_data(hp->bh_pofd, spare, BB_FL_SPARE_SIZE)) < 0) {
BBC_LOG(MSG_ERR, "dwb: write block %ld: send data (spare) failed with %d\n",
blk, rv);
return rv;
}
if ((rv = __bbc_read_rsp(hp->bh_pifd, hbuf, 8)) < 0) {
BBC_LOG(MSG_ERR, "dwb: write block %ld: read rsp failed with %d\n",
blk, rv);
return rv;
}
if (hbuf[0] != r) {
BBC_LOG(MSG_ERR, "dwb: write block %ld failed (sync loss was 0x%x not 0x%x)\n", blk, hbuf[0], r);
return BBC_SYNCLOST;
}
else if (hbuf[1] != 0) {
BBC_LOG(MSG_ERR, "dwb: write block %ld failed with error %d\n",
blk, hbuf[1]);
/* Try to map the error codes we got into libbbc error codes */
switch (hbuf[1]) {
case BBCARD_ERR_FAIL: return BBC_DATAERR;
case BBCARD_ERR_NO_CARD: return BBC_NOCARD;
case BBCARD_ERR_CHANGED: return BBC_CARDCHANGE;
case BBCARD_ERR_INVALID:
default: return BBC_ERROR; /* Some unknown error */
}
}
blk++;
(char *)data += BB_FL_BLOCK_SIZE;
if (spare)
(char *)spare += BB_FL_SPARE_SIZE;
}
return BBC_OK;
}
int
#ifndef WIN32
__bbc_new_direct(bbc_hand *hp, const char *device)
#else
__bbc_new_direct(bbc_hand *hp)
#endif
{
flashif_t* f;
#ifndef WIN32
int fd;
#else
HANDLE hUsbWrite = INVALID_HANDLE_VALUE;
HANDLE hUsbRead = INVALID_HANDLE_VALUE;
char usbReadName[512], usbWriteName[512];
int bbrDevices=0, bbwDevices=0;
int rv;
#endif
#ifndef WIN32
BBC_LOG(MSG_DEBUG, "__bbc_new_direct %s\n", device);
if ((fd = open(device, O_RDWR)) < 0) {
perror(device);
return BBC_NODEV;
}
if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) {
BBC_LOG_SYSERROR("__bbc_new_direct: fcntl failed");
}
hp->bh_ufd = fd;
#else
hUsbWrite = open_usb_pipe(RDB_OUT_PIPE, usbWriteName, &bbrDevices);
if (hUsbWrite != INVALID_HANDLE_VALUE)
hUsbRead = open_usb_pipe(RDB_IN_PIPE, usbReadName, &bbwDevices);
if (bbrDevices>1 || bbwDevices>1) {
BBC_LOG(MSG_DEBUG, "Multiple BB USB device\n");
if (hUsbWrite != INVALID_HANDLE_VALUE)
CloseHandle(hUsbWrite);
if (hUsbRead != INVALID_HANDLE_VALUE)
CloseHandle(hUsbRead);
return BBC_MULTIPLE_BB;
} else if (hUsbWrite == INVALID_HANDLE_VALUE || hUsbRead == INVALID_HANDLE_VALUE) {
BBC_LOG(MSG_DEBUG, "Cannot open USB device\n");
if (GetLastError() == ERROR_SHARING_VIOLATION) {
rv = BBC_DEVBUSY;
}
else {
rv = BBC_NODEV;
}
if (hUsbWrite != INVALID_HANDLE_VALUE)
CloseHandle(hUsbWrite);
if (hUsbRead != INVALID_HANDLE_VALUE)
CloseHandle(hUsbRead);
return rv;
}
hp->bh_uwh = hUsbWrite;
hp->bh_urh = hUsbRead;
strncpy(hp->bh_readname, usbReadName, sizeof(hp->bh_readname));
strncpy(hp->bh_writename, usbWriteName, sizeof(hp->bh_writename));
#endif
if ((f = malloc(sizeof(flashif_t))) == NULL) {
BBC_LOG_SYSERROR("__bbc_new_direct: malloc failed");
#ifndef WIN32
close(fd);
#else
CloseHandle(hUsbWrite);
CloseHandle(hUsbRead);
#endif
return BBC_NOMEM;
}
hp->bh_flashif = f;
memset(f, 0, sizeof(flashif_t));
f->f = hp;
f->close = __bbc_direct_close;
f->setled = __bbc_direct_setled;
f->settime = __bbc_direct_settime;
f->read_blocks = __bbc_direct_read_blocks;
f->write_blocks = __bbc_direct_write_blocks;
return BBC_OK;
}