diff options
Diffstat (limited to 'drivers/isdn/hysdn/hysdn_boot.c')
-rw-r--r-- | drivers/isdn/hysdn/hysdn_boot.c | 399 |
1 files changed, 399 insertions, 0 deletions
diff --git a/drivers/isdn/hysdn/hysdn_boot.c b/drivers/isdn/hysdn/hysdn_boot.c new file mode 100644 index 000000000000..6c04281e57b8 --- /dev/null +++ b/drivers/isdn/hysdn/hysdn_boot.c | |||
@@ -0,0 +1,399 @@ | |||
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> | ||
16 | #include <asm/uaccess.h> | ||
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 { | ||
33 | word Cryptor; /* for use with Decrypt function */ | ||
34 | word Nrecs; /* records remaining in file */ | ||
35 | uchar pof_state; /* actual state of read handler */ | ||
36 | uchar is_crypted; /* card data is crypted */ | ||
37 | int BufSize; /* actual number of bytes bufferd */ | ||
38 | int last_error; /* last occurred error */ | ||
39 | word pof_recid; /* actual pof recid */ | ||
40 | ulong pof_reclen; /* total length of pof record data */ | ||
41 | ulong pof_recoffset; /* actual offset inside pof record */ | ||
42 | union { | ||
43 | uchar BootBuf[BOOT_BUF_SIZE]; /* buffer as byte count */ | ||
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 | /*****************************************************/ | ||
56 | void | ||
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 | /***************************************************************/ | ||
69 | void | ||
70 | DecryptBuf(struct boot_data *boot, int cnt) | ||
71 | { | ||
72 | uchar *bufp = boot->buf.BootBuf; | ||
73 | |||
74 | while (cnt--) { | ||
75 | boot->Cryptor = (boot->Cryptor >> 1) ^ ((boot->Cryptor & 1U) ? CRYPT_FEEDTERM : 0); | ||
76 | *bufp++ ^= (uchar) boot->Cryptor; | ||
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 | ||
85 | pof_handle_data(hysdn_card * card, int datlen) | ||
86 | { | ||
87 | struct boot_data *boot = card->boot; /* pointer to boot specific data */ | ||
88 | long l; | ||
89 | uchar *imgp; | ||
90 | int img_len; | ||
91 | |||
92 | /* handle the different record types */ | ||
93 | switch (boot->pof_recid) { | ||
94 | |||
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 */ | ||
137 | |||
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); | ||
145 | |||
146 | if ((boot->last_error = card->writebootseq(card, boot->buf.BootBuf, datlen) < 0)) | ||
147 | return (boot->last_error); /* error writing data */ | ||
148 | |||
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); | ||
158 | |||
159 | break; /* simply skip record */ | ||
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 | ||
174 | pof_write_buffer(hysdn_card * card, int datlen) | ||
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) { | ||
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 = (word) (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; | ||
211 | break; | ||
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 */ | ||
216 | |||
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); | ||
220 | |||
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 */ | ||
226 | |||
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; | ||
232 | |||
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; | ||
239 | break; | ||
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; | ||
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 | ||
271 | pof_write_open(hysdn_card * card, uchar ** bufp) | ||
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 */ | ||
281 | if (!(boot = kmalloc(sizeof(struct boot_data), GFP_KERNEL))) { | ||
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; | ||
288 | memset(boot, 0, sizeof(struct boot_data)); | ||
289 | |||
290 | card->stopcard(card); /* first stop the card */ | ||
291 | if (card->testram(card)) { | ||
292 | if (card->debug_flags & LOG_POF_OPEN) | ||
293 | hysdn_addlog(card, "POF open: DPRAM test failure"); | ||
294 | boot->last_error = -ERR_BOARD_DPRAM; | ||
295 | card->state = CARD_STATE_BOOTERR; /* show boot error */ | ||
296 | return (boot->last_error); | ||
297 | } | ||
298 | boot->BufSize = 0; /* Buffer is empty */ | ||
299 | boot->pof_state = POF_READ_FILE_HEAD; /* read file header */ | ||
300 | StartDecryption(boot); /* if POF File should be encrypted */ | ||
301 | |||
302 | if (card->debug_flags & LOG_POF_OPEN) | ||
303 | hysdn_addlog(card, "POF open: success"); | ||
304 | |||
305 | *bufp = boot->buf.BootBuf; /* point to buffer */ | ||
306 | return (sizeof(tPofFileHdr)); | ||
307 | } /* pof_write_open */ | ||
308 | |||
309 | /********************************************************************************/ | ||
310 | /* pof_write_close is called when an close of boot on the cardlog device occurs. */ | ||
311 | /* The return value must be 0 if everything has happened as desired. */ | ||
312 | /********************************************************************************/ | ||
313 | int | ||
314 | pof_write_close(hysdn_card * card) | ||
315 | { | ||
316 | struct boot_data *boot = card->boot; /* pointer to boot specific data */ | ||
317 | |||
318 | if (!boot) | ||
319 | return (-EFAULT); /* invalid call */ | ||
320 | |||
321 | card->boot = NULL; /* no boot active */ | ||
322 | kfree(boot); | ||
323 | |||
324 | if (card->state == CARD_STATE_RUN) | ||
325 | card->set_errlog_state(card, 1); /* activate error log */ | ||
326 | |||
327 | if (card->debug_flags & LOG_POF_OPEN) | ||
328 | hysdn_addlog(card, "POF close: success"); | ||
329 | |||
330 | return (0); | ||
331 | } /* pof_write_close */ | ||
332 | |||
333 | /*********************************************************************************/ | ||
334 | /* EvalSysrTokData checks additional records delivered with the Sysready Message */ | ||
335 | /* when POF has been booted. A return value of 0 is used if no error occurred. */ | ||
336 | /*********************************************************************************/ | ||
337 | int | ||
338 | EvalSysrTokData(hysdn_card * card, uchar * cp, int len) | ||
339 | { | ||
340 | u_char *p; | ||
341 | u_char crc; | ||
342 | |||
343 | if (card->debug_flags & LOG_POF_RECORD) | ||
344 | hysdn_addlog(card, "SysReady Token data length %d", len); | ||
345 | |||
346 | if (len < 2) { | ||
347 | hysdn_addlog(card, "SysReady Token Data to short"); | ||
348 | return (1); | ||
349 | } | ||
350 | for (p = cp, crc = 0; p < (cp + len - 2); p++) | ||
351 | if ((crc & 0x80)) | ||
352 | crc = (((u_char) (crc << 1)) + 1) + *p; | ||
353 | else | ||
354 | crc = ((u_char) (crc << 1)) + *p; | ||
355 | crc = ~crc; | ||
356 | if (crc != *(cp + len - 1)) { | ||
357 | hysdn_addlog(card, "SysReady Token Data invalid CRC"); | ||
358 | return (1); | ||
359 | } | ||
360 | len--; /* don't check CRC byte */ | ||
361 | while (len > 0) { | ||
362 | |||
363 | if (*cp == SYSR_TOK_END) | ||
364 | return (0); /* End of Token stream */ | ||
365 | |||
366 | if (len < (*(cp + 1) + 2)) { | ||
367 | hysdn_addlog(card, "token 0x%x invalid length %d", *cp, *(cp + 1)); | ||
368 | return (1); | ||
369 | } | ||
370 | switch (*cp) { | ||
371 | case SYSR_TOK_B_CHAN: /* 1 */ | ||
372 | if (*(cp + 1) != 1) | ||
373 | return (1); /* length invalid */ | ||
374 | card->bchans = *(cp + 2); | ||
375 | break; | ||
376 | |||
377 | case SYSR_TOK_FAX_CHAN: /* 2 */ | ||
378 | if (*(cp + 1) != 1) | ||
379 | return (1); /* length invalid */ | ||
380 | card->faxchans = *(cp + 2); | ||
381 | break; | ||
382 | |||
383 | case SYSR_TOK_MAC_ADDR: /* 3 */ | ||
384 | if (*(cp + 1) != 6) | ||
385 | return (1); /* length invalid */ | ||
386 | memcpy(card->mac_addr, cp + 2, 6); | ||
387 | break; | ||
388 | |||
389 | default: | ||
390 | hysdn_addlog(card, "unknown token 0x%02x length %d", *cp, *(cp + 1)); | ||
391 | break; | ||
392 | } | ||
393 | len -= (*(cp + 1) + 2); /* adjust len */ | ||
394 | cp += (*(cp + 1) + 2); /* and pointer */ | ||
395 | } | ||
396 | |||
397 | hysdn_addlog(card, "no end token found"); | ||
398 | return (1); | ||
399 | } /* EvalSysrTokData */ | ||