]>
Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* $Id: hysdn_boot.c,v 1.4.6.4 2001/09/23 22:24:54 kai Exp $ |
2 | * | |
3 | * Linux driver for HYSDN cards | |
4 | * specific routines for booting and pof handling | |
5 | * | |
6 | * Author Werner Cornelius (werner@titro.de) for Hypercope GmbH | |
7 | * Copyright 1999 by Werner Cornelius (werner@titro.de) | |
8 | * | |
9 | * This software may be used and distributed according to the terms | |
10 | * of the GNU General Public License, incorporated herein by reference. | |
11 | * | |
12 | */ | |
13 | ||
14 | #include <linux/vmalloc.h> | |
15 | #include <linux/slab.h> | |
7c0f6ba6 | 16 | #include <linux/uaccess.h> |
1da177e4 LT |
17 | |
18 | #include "hysdn_defs.h" | |
19 | #include "hysdn_pof.h" | |
20 | ||
21 | /********************************/ | |
22 | /* defines for pof read handler */ | |
23 | /********************************/ | |
24 | #define POF_READ_FILE_HEAD 0 | |
25 | #define POF_READ_TAG_HEAD 1 | |
26 | #define POF_READ_TAG_DATA 2 | |
27 | ||
28 | /************************************************************/ | |
29 | /* definition of boot specific data area. This data is only */ | |
30 | /* needed during boot and so allocated dynamically. */ | |
31 | /************************************************************/ | |
32 | struct boot_data { | |
c721bcce AM |
33 | unsigned short Cryptor; /* for use with Decrypt function */ |
34 | unsigned short Nrecs; /* records remaining in file */ | |
35 | unsigned char pof_state;/* actual state of read handler */ | |
36 | unsigned char is_crypted;/* card data is crypted */ | |
1da177e4 LT |
37 | int BufSize; /* actual number of bytes bufferd */ |
38 | int last_error; /* last occurred error */ | |
c721bcce AM |
39 | unsigned short pof_recid;/* actual pof recid */ |
40 | unsigned long pof_reclen;/* total length of pof record data */ | |
41 | unsigned long pof_recoffset;/* actual offset inside pof record */ | |
1da177e4 | 42 | union { |
c721bcce | 43 | unsigned char BootBuf[BOOT_BUF_SIZE];/* buffer as byte count */ |
1da177e4 LT |
44 | tPofRecHdr PofRecHdr; /* header for actual record/chunk */ |
45 | tPofFileHdr PofFileHdr; /* header from POF file */ | |
46 | tPofTimeStamp PofTime; /* time information */ | |
47 | } buf; | |
48 | }; | |
49 | ||
50 | /*****************************************************/ | |
51 | /* start decryption of successive POF file chuncks. */ | |
52 | /* */ | |
53 | /* to be called at start of POF file reading, */ | |
54 | /* before starting any decryption on any POF record. */ | |
55 | /*****************************************************/ | |
aade0e82 | 56 | static void |
1da177e4 LT |
57 | StartDecryption(struct boot_data *boot) |
58 | { | |
59 | boot->Cryptor = CRYPT_STARTTERM; | |
60 | } /* StartDecryption */ | |
61 | ||
62 | ||
63 | /***************************************************************/ | |
64 | /* decrypt complete BootBuf */ | |
65 | /* NOTE: decryption must be applied to all or none boot tags - */ | |
66 | /* to HI and LO boot loader and (all) seq tags, because */ | |
67 | /* global Cryptor is started for whole POF. */ | |
68 | /***************************************************************/ | |
aade0e82 | 69 | static void |
1da177e4 LT |
70 | DecryptBuf(struct boot_data *boot, int cnt) |
71 | { | |
c721bcce | 72 | unsigned char *bufp = boot->buf.BootBuf; |
1da177e4 LT |
73 | |
74 | while (cnt--) { | |
75 | boot->Cryptor = (boot->Cryptor >> 1) ^ ((boot->Cryptor & 1U) ? CRYPT_FEEDTERM : 0); | |
c721bcce | 76 | *bufp++ ^= (unsigned char)boot->Cryptor; |
1da177e4 LT |
77 | } |
78 | } /* DecryptBuf */ | |
79 | ||
80 | /********************************************************************************/ | |
81 | /* pof_handle_data executes the required actions dependent on the active record */ | |
82 | /* id. If successful 0 is returned, a negative value shows an error. */ | |
83 | /********************************************************************************/ | |
84 | static int | |
475be4d8 | 85 | pof_handle_data(hysdn_card *card, int datlen) |
1da177e4 LT |
86 | { |
87 | struct boot_data *boot = card->boot; /* pointer to boot specific data */ | |
88 | long l; | |
c721bcce | 89 | unsigned char *imgp; |
1da177e4 LT |
90 | int img_len; |
91 | ||
92 | /* handle the different record types */ | |
93 | switch (boot->pof_recid) { | |
94 | ||
475be4d8 JP |
95 | case TAG_TIMESTMP: |
96 | if (card->debug_flags & LOG_POF_RECORD) | |
97 | hysdn_addlog(card, "POF created %s", boot->buf.PofTime.DateTimeText); | |
98 | break; | |
99 | ||
100 | case TAG_CBOOTDTA: | |
101 | DecryptBuf(boot, datlen); /* we need to encrypt the buffer */ | |
102 | case TAG_BOOTDTA: | |
103 | if (card->debug_flags & LOG_POF_RECORD) | |
104 | hysdn_addlog(card, "POF got %s len=%d offs=0x%lx", | |
105 | (boot->pof_recid == TAG_CBOOTDTA) ? "CBOOTDATA" : "BOOTDTA", | |
106 | datlen, boot->pof_recoffset); | |
107 | ||
108 | if (boot->pof_reclen != POF_BOOT_LOADER_TOTAL_SIZE) { | |
109 | boot->last_error = EPOF_BAD_IMG_SIZE; /* invalid length */ | |
110 | return (boot->last_error); | |
111 | } | |
112 | imgp = boot->buf.BootBuf; /* start of buffer */ | |
113 | img_len = datlen; /* maximum length to transfer */ | |
114 | ||
115 | l = POF_BOOT_LOADER_OFF_IN_PAGE - | |
116 | (boot->pof_recoffset & (POF_BOOT_LOADER_PAGE_SIZE - 1)); | |
117 | if (l > 0) { | |
118 | /* buffer needs to be truncated */ | |
119 | imgp += l; /* advance pointer */ | |
120 | img_len -= l; /* adjust len */ | |
121 | } | |
122 | /* at this point no special handling for data wrapping over buffer */ | |
123 | /* is necessary, because the boot image always will be adjusted to */ | |
124 | /* match a page boundary inside the buffer. */ | |
125 | /* The buffer for the boot image on the card is filled in 2 cycles */ | |
126 | /* first the 1024 hi-words are put in the buffer, then the low 1024 */ | |
127 | /* word are handled in the same way with different offset. */ | |
128 | ||
129 | if (img_len > 0) { | |
130 | /* data available for copy */ | |
131 | if ((boot->last_error = | |
132 | card->writebootimg(card, imgp, | |
133 | (boot->pof_recoffset > POF_BOOT_LOADER_PAGE_SIZE) ? 2 : 0)) < 0) | |
134 | return (boot->last_error); | |
135 | } | |
136 | break; /* end of case boot image hi/lo */ | |
1da177e4 | 137 | |
475be4d8 JP |
138 | case TAG_CABSDATA: |
139 | DecryptBuf(boot, datlen); /* we need to encrypt the buffer */ | |
140 | case TAG_ABSDATA: | |
141 | if (card->debug_flags & LOG_POF_RECORD) | |
142 | hysdn_addlog(card, "POF got %s len=%d offs=0x%lx", | |
143 | (boot->pof_recid == TAG_CABSDATA) ? "CABSDATA" : "ABSDATA", | |
144 | datlen, boot->pof_recoffset); | |
1da177e4 | 145 | |
475be4d8 JP |
146 | if ((boot->last_error = card->writebootseq(card, boot->buf.BootBuf, datlen)) < 0) |
147 | return (boot->last_error); /* error writing data */ | |
1da177e4 | 148 | |
475be4d8 JP |
149 | if (boot->pof_recoffset + datlen >= boot->pof_reclen) |
150 | return (card->waitpofready(card)); /* data completely spooled, wait for ready */ | |
151 | ||
152 | break; /* end of case boot seq data */ | |
153 | ||
154 | default: | |
155 | if (card->debug_flags & LOG_POF_RECORD) | |
156 | hysdn_addlog(card, "POF got data(id=0x%lx) len=%d offs=0x%lx", boot->pof_recid, | |
157 | datlen, boot->pof_recoffset); | |
1da177e4 | 158 | |
475be4d8 | 159 | break; /* simply skip record */ |
1da177e4 LT |
160 | } /* switch boot->pof_recid */ |
161 | ||
162 | return (0); | |
163 | } /* pof_handle_data */ | |
164 | ||
165 | ||
166 | /******************************************************************************/ | |
167 | /* pof_write_buffer is called when the buffer has been filled with the needed */ | |
168 | /* number of data bytes. The number delivered is additionally supplied for */ | |
169 | /* verification. The functions handles the data and returns the needed number */ | |
170 | /* of bytes for the next action. If the returned value is 0 or less an error */ | |
171 | /* occurred and booting must be aborted. */ | |
172 | /******************************************************************************/ | |
173 | int | |
475be4d8 | 174 | pof_write_buffer(hysdn_card *card, int datlen) |
1da177e4 LT |
175 | { |
176 | struct boot_data *boot = card->boot; /* pointer to boot specific data */ | |
177 | ||
178 | if (!boot) | |
179 | return (-EFAULT); /* invalid call */ | |
180 | if (boot->last_error < 0) | |
181 | return (boot->last_error); /* repeated error */ | |
182 | ||
183 | if (card->debug_flags & LOG_POF_WRITE) | |
184 | hysdn_addlog(card, "POF write: got %d bytes ", datlen); | |
185 | ||
186 | switch (boot->pof_state) { | |
475be4d8 JP |
187 | case POF_READ_FILE_HEAD: |
188 | if (card->debug_flags & LOG_POF_WRITE) | |
189 | hysdn_addlog(card, "POF write: checking file header"); | |
190 | ||
191 | if (datlen != sizeof(tPofFileHdr)) { | |
192 | boot->last_error = -EPOF_INTERNAL; | |
193 | break; | |
194 | } | |
195 | if (boot->buf.PofFileHdr.Magic != TAGFILEMAGIC) { | |
196 | boot->last_error = -EPOF_BAD_MAGIC; | |
197 | break; | |
198 | } | |
199 | /* Setup the new state and vars */ | |
200 | boot->Nrecs = (unsigned short)(boot->buf.PofFileHdr.N_PofRecs); /* limited to 65535 */ | |
201 | boot->pof_state = POF_READ_TAG_HEAD; /* now start with single tags */ | |
202 | boot->last_error = sizeof(tPofRecHdr); /* new length */ | |
203 | break; | |
204 | ||
205 | case POF_READ_TAG_HEAD: | |
206 | if (card->debug_flags & LOG_POF_WRITE) | |
207 | hysdn_addlog(card, "POF write: checking tag header"); | |
208 | ||
209 | if (datlen != sizeof(tPofRecHdr)) { | |
210 | boot->last_error = -EPOF_INTERNAL; | |
1da177e4 | 211 | break; |
475be4d8 JP |
212 | } |
213 | boot->pof_recid = boot->buf.PofRecHdr.PofRecId; /* actual pof recid */ | |
214 | boot->pof_reclen = boot->buf.PofRecHdr.PofRecDataLen; /* total length */ | |
215 | boot->pof_recoffset = 0; /* no starting offset */ | |
1da177e4 | 216 | |
475be4d8 JP |
217 | if (card->debug_flags & LOG_POF_RECORD) |
218 | hysdn_addlog(card, "POF: got record id=0x%lx length=%ld ", | |
219 | boot->pof_recid, boot->pof_reclen); | |
1da177e4 | 220 | |
475be4d8 JP |
221 | boot->pof_state = POF_READ_TAG_DATA; /* now start with tag data */ |
222 | if (boot->pof_reclen < BOOT_BUF_SIZE) | |
223 | boot->last_error = boot->pof_reclen; /* limit size */ | |
224 | else | |
225 | boot->last_error = BOOT_BUF_SIZE; /* maximum */ | |
1da177e4 | 226 | |
475be4d8 JP |
227 | if (!boot->last_error) { /* no data inside record */ |
228 | boot->pof_state = POF_READ_TAG_HEAD; /* now start with single tags */ | |
229 | boot->last_error = sizeof(tPofRecHdr); /* new length */ | |
230 | } | |
231 | break; | |
1da177e4 | 232 | |
475be4d8 JP |
233 | case POF_READ_TAG_DATA: |
234 | if (card->debug_flags & LOG_POF_WRITE) | |
235 | hysdn_addlog(card, "POF write: getting tag data"); | |
236 | ||
237 | if (datlen != boot->last_error) { | |
238 | boot->last_error = -EPOF_INTERNAL; | |
1da177e4 | 239 | break; |
475be4d8 JP |
240 | } |
241 | if ((boot->last_error = pof_handle_data(card, datlen)) < 0) | |
242 | return (boot->last_error); /* an error occurred */ | |
243 | boot->pof_recoffset += datlen; | |
244 | if (boot->pof_recoffset >= boot->pof_reclen) { | |
245 | boot->pof_state = POF_READ_TAG_HEAD; /* now start with single tags */ | |
246 | boot->last_error = sizeof(tPofRecHdr); /* new length */ | |
247 | } else { | |
248 | if (boot->pof_reclen - boot->pof_recoffset < BOOT_BUF_SIZE) | |
249 | boot->last_error = boot->pof_reclen - boot->pof_recoffset; /* limit size */ | |
250 | else | |
251 | boot->last_error = BOOT_BUF_SIZE; /* maximum */ | |
252 | } | |
253 | break; | |
254 | ||
255 | default: | |
256 | boot->last_error = -EPOF_INTERNAL; /* unknown state */ | |
257 | break; | |
1da177e4 LT |
258 | } /* switch (boot->pof_state) */ |
259 | ||
260 | return (boot->last_error); | |
261 | } /* pof_write_buffer */ | |
262 | ||
263 | ||
264 | /*******************************************************************************/ | |
265 | /* pof_write_open is called when an open for boot on the cardlog device occurs. */ | |
266 | /* The function returns the needed number of bytes for the next operation. If */ | |
267 | /* the returned number is less or equal 0 an error specified by this code */ | |
268 | /* occurred. Additionally the pointer to the buffer data area is set on success */ | |
269 | /*******************************************************************************/ | |
270 | int | |
475be4d8 | 271 | pof_write_open(hysdn_card *card, unsigned char **bufp) |
1da177e4 LT |
272 | { |
273 | struct boot_data *boot; /* pointer to boot specific data */ | |
274 | ||
275 | if (card->boot) { | |
276 | if (card->debug_flags & LOG_POF_OPEN) | |
277 | hysdn_addlog(card, "POF open: already opened for boot"); | |
278 | return (-ERR_ALREADY_BOOT); /* boot already active */ | |
279 | } | |
280 | /* error no mem available */ | |
41f96935 | 281 | if (!(boot = kzalloc(sizeof(struct boot_data), GFP_KERNEL))) { |
1da177e4 LT |
282 | if (card->debug_flags & LOG_MEM_ERR) |
283 | hysdn_addlog(card, "POF open: unable to allocate mem"); | |
284 | return (-EFAULT); | |
285 | } | |
286 | card->boot = boot; | |
287 | card->state = CARD_STATE_BOOTING; | |
1da177e4 LT |
288 | |
289 | card->stopcard(card); /* first stop the card */ | |
290 | if (card->testram(card)) { | |
291 | if (card->debug_flags & LOG_POF_OPEN) | |
292 | hysdn_addlog(card, "POF open: DPRAM test failure"); | |
293 | boot->last_error = -ERR_BOARD_DPRAM; | |
294 | card->state = CARD_STATE_BOOTERR; /* show boot error */ | |
295 | return (boot->last_error); | |
296 | } | |
297 | boot->BufSize = 0; /* Buffer is empty */ | |
298 | boot->pof_state = POF_READ_FILE_HEAD; /* read file header */ | |
299 | StartDecryption(boot); /* if POF File should be encrypted */ | |
300 | ||
301 | if (card->debug_flags & LOG_POF_OPEN) | |
302 | hysdn_addlog(card, "POF open: success"); | |
303 | ||
304 | *bufp = boot->buf.BootBuf; /* point to buffer */ | |
305 | return (sizeof(tPofFileHdr)); | |
306 | } /* pof_write_open */ | |
307 | ||
308 | /********************************************************************************/ | |
309 | /* pof_write_close is called when an close of boot on the cardlog device occurs. */ | |
310 | /* The return value must be 0 if everything has happened as desired. */ | |
311 | /********************************************************************************/ | |
312 | int | |
475be4d8 | 313 | pof_write_close(hysdn_card *card) |
1da177e4 LT |
314 | { |
315 | struct boot_data *boot = card->boot; /* pointer to boot specific data */ | |
316 | ||
317 | if (!boot) | |
318 | return (-EFAULT); /* invalid call */ | |
319 | ||
320 | card->boot = NULL; /* no boot active */ | |
321 | kfree(boot); | |
322 | ||
323 | if (card->state == CARD_STATE_RUN) | |
324 | card->set_errlog_state(card, 1); /* activate error log */ | |
325 | ||
326 | if (card->debug_flags & LOG_POF_OPEN) | |
327 | hysdn_addlog(card, "POF close: success"); | |
328 | ||
329 | return (0); | |
330 | } /* pof_write_close */ | |
331 | ||
332 | /*********************************************************************************/ | |
333 | /* EvalSysrTokData checks additional records delivered with the Sysready Message */ | |
334 | /* when POF has been booted. A return value of 0 is used if no error occurred. */ | |
335 | /*********************************************************************************/ | |
336 | int | |
c721bcce | 337 | EvalSysrTokData(hysdn_card *card, unsigned char *cp, int len) |
1da177e4 LT |
338 | { |
339 | u_char *p; | |
340 | u_char crc; | |
341 | ||
342 | if (card->debug_flags & LOG_POF_RECORD) | |
343 | hysdn_addlog(card, "SysReady Token data length %d", len); | |
344 | ||
345 | if (len < 2) { | |
346 | hysdn_addlog(card, "SysReady Token Data to short"); | |
347 | return (1); | |
348 | } | |
349 | for (p = cp, crc = 0; p < (cp + len - 2); p++) | |
350 | if ((crc & 0x80)) | |
351 | crc = (((u_char) (crc << 1)) + 1) + *p; | |
352 | else | |
353 | crc = ((u_char) (crc << 1)) + *p; | |
354 | crc = ~crc; | |
355 | if (crc != *(cp + len - 1)) { | |
356 | hysdn_addlog(card, "SysReady Token Data invalid CRC"); | |
357 | return (1); | |
358 | } | |
359 | len--; /* don't check CRC byte */ | |
360 | while (len > 0) { | |
361 | ||
362 | if (*cp == SYSR_TOK_END) | |
363 | return (0); /* End of Token stream */ | |
364 | ||
365 | if (len < (*(cp + 1) + 2)) { | |
366 | hysdn_addlog(card, "token 0x%x invalid length %d", *cp, *(cp + 1)); | |
367 | return (1); | |
368 | } | |
369 | switch (*cp) { | |
475be4d8 JP |
370 | case SYSR_TOK_B_CHAN: /* 1 */ |
371 | if (*(cp + 1) != 1) | |
372 | return (1); /* length invalid */ | |
373 | card->bchans = *(cp + 2); | |
374 | break; | |
375 | ||
376 | case SYSR_TOK_FAX_CHAN: /* 2 */ | |
377 | if (*(cp + 1) != 1) | |
378 | return (1); /* length invalid */ | |
379 | card->faxchans = *(cp + 2); | |
380 | break; | |
381 | ||
382 | case SYSR_TOK_MAC_ADDR: /* 3 */ | |
383 | if (*(cp + 1) != 6) | |
384 | return (1); /* length invalid */ | |
385 | memcpy(card->mac_addr, cp + 2, 6); | |
386 | break; | |
387 | ||
388 | default: | |
389 | hysdn_addlog(card, "unknown token 0x%02x length %d", *cp, *(cp + 1)); | |
390 | break; | |
1da177e4 LT |
391 | } |
392 | len -= (*(cp + 1) + 2); /* adjust len */ | |
393 | cp += (*(cp + 1) + 2); /* and pointer */ | |
394 | } | |
395 | ||
396 | hysdn_addlog(card, "no end token found"); | |
397 | return (1); | |
398 | } /* EvalSysrTokData */ |