Commit | Line | Data |
---|---|---|
a35e3ab2 | 1 | /* hp.c 3.19 %G% */ |
04b9d53d | 2 | |
a5cc519e BJ |
3 | #include "../conf/hp.h" |
4 | #if NHP > 0 | |
04b9d53d | 5 | /* |
5c777efe | 6 | * RP06/RM03/RM05 disk driver |
04b9d53d BJ |
7 | */ |
8 | ||
9 | #include "../h/param.h" | |
10 | #include "../h/systm.h" | |
41888f16 | 11 | #include "../h/dk.h" |
04b9d53d BJ |
12 | #include "../h/buf.h" |
13 | #include "../h/conf.h" | |
14 | #include "../h/dir.h" | |
15 | #include "../h/user.h" | |
16 | #include "../h/map.h" | |
80e7c811 | 17 | #include "../h/pte.h" |
04b9d53d BJ |
18 | #include "../h/mba.h" |
19 | #include "../h/mtpr.h" | |
80e7c811 | 20 | #include "../h/vm.h" |
04b9d53d | 21 | |
04b9d53d BJ |
22 | struct device |
23 | { | |
24 | int hpcs1; /* control and Status register 1 */ | |
25 | int hpds; /* Drive Status */ | |
26 | int hper1; /* Error register 1 */ | |
27 | int hpmr; /* Maintenance */ | |
28 | int hpas; /* Attention Summary */ | |
29 | int hpda; /* Desired address register */ | |
30 | int hpdt; /* Drive type */ | |
31 | int hpla; /* Look ahead */ | |
32 | int hpsn; /* serial number */ | |
33 | int hpof; /* Offset register */ | |
34 | int hpdc; /* Desired Cylinder address register */ | |
35 | int hpcc; /* Current Cylinder */ | |
36 | int hper2; /* Error register 2 */ | |
37 | int hper3; /* Error register 3 */ | |
38 | int hpec1; /* Burst error bit position */ | |
39 | int hpec2; /* Burst error bit pattern */ | |
40 | }; | |
41 | ||
04b9d53d BJ |
42 | #define RP 022 |
43 | #define RM 024 | |
5c777efe | 44 | #define RM5 027 |
04b9d53d BJ |
45 | #define NSECT 22 |
46 | #define NTRAC 19 | |
47 | #define NRMSECT 32 | |
48 | #define NRMTRAC 5 | |
41888f16 | 49 | |
3cab1981 BJ |
50 | #define _hpSDIST 2 |
51 | #define _hpRDIST 3 | |
41888f16 BJ |
52 | |
53 | int hpSDIST = _hpSDIST; | |
54 | int hpRDIST = _hpRDIST; | |
55 | int hpseek; | |
04b9d53d BJ |
56 | |
57 | struct size | |
58 | { | |
59 | daddr_t nblocks; | |
60 | int cyloff; | |
61 | } hp_sizes[8] = | |
62 | { | |
5c777efe BJ |
63 | 15884, 0, /* A=cyl 0 thru 37 */ |
64 | 33440, 38, /* B=cyl 38 thru 117 */ | |
65 | 340670, 0, /* C=cyl 0 thru 814 */ | |
04b9d53d | 66 | 0, 0, |
04b9d53d BJ |
67 | 0, 0, |
68 | 0, 0, | |
5c777efe | 69 | 291346, 118, /* G=cyl 118 thru 814 */ |
04b9d53d | 70 | 0, 0, |
04b9d53d | 71 | }, rm_sizes[8] = { |
5c777efe BJ |
72 | 15884, 0, /* A=cyl 0 thru 99 */ |
73 | 33440, 100, /* B=cyl 100 thru 309 */ | |
74 | 131680, 0, /* C=cyl 0 thru 822 */ | |
04b9d53d BJ |
75 | 0, 0, |
76 | 0, 0, | |
77 | 0, 0, | |
5c777efe | 78 | 82080, 310, /* G=cyl 310 thru 822 */ |
04b9d53d | 79 | 0, 0, |
5c777efe BJ |
80 | }, rm5_sizes[8] = { |
81 | 15884, 0, /* A=cyl 0 thru 26 */ | |
82 | 33440, 27, /* B=cyl 27 thru 81 */ | |
83 | 500992, 0, /* C=cyl 0 thru 823 */ | |
84 | 15884, 562, /* D=cyl 562 thru 588 */ | |
85 | 55936, 589, /* E=cyl 589 thru 680 */ | |
86 | 86944, 681, /* F=cyl 681 thru 823 */ | |
87 | 159296, 562, /* G=cyl 562 thru 823 */ | |
88 | 291346, 82, /* H=cyl 82 thru 561 */ | |
04b9d53d BJ |
89 | }; |
90 | ||
91 | #define P400 020 | |
92 | #define M400 0220 | |
93 | #define P800 040 | |
94 | #define M800 0240 | |
95 | #define P1200 060 | |
96 | #define M1200 0260 | |
97 | int hp_offset[16] = | |
98 | { | |
99 | P400, M400, P400, M400, | |
100 | P800, M800, P800, M800, | |
101 | P1200, M1200, P1200, M1200, | |
102 | 0, 0, 0, 0, | |
103 | }; | |
104 | ||
105 | struct buf hptab; | |
106 | struct buf rhpbuf; | |
107 | struct buf hputab[NHP]; | |
108 | char hp_type[NHP]; /* drive type */ | |
109 | ||
110 | #define GO 01 | |
111 | #define PRESET 020 | |
112 | #define RTC 016 | |
113 | #define OFFSET 014 | |
41888f16 | 114 | #define SEEK 04 |
04b9d53d BJ |
115 | #define SEARCH 030 |
116 | #define RECAL 06 | |
117 | #define DCLR 010 | |
118 | #define WCOM 060 | |
119 | #define RCOM 070 | |
120 | ||
121 | #define IE 0100 | |
122 | #define PIP 020000 | |
123 | #define DRY 0200 | |
124 | #define ERR 040000 | |
125 | #define TRE 040000 | |
126 | #define DCK 0100000 | |
127 | #define WLE 04000 | |
128 | #define ECH 0100 | |
129 | #define VV 0100 | |
130 | #define DPR 0400 | |
131 | #define MOL 010000 | |
132 | #define FMT22 010000 | |
133 | ||
134 | #define b_cylin b_resid | |
135 | ||
136 | #ifdef INTRLVE | |
137 | daddr_t dkblock(); | |
138 | #endif | |
139 | ||
140 | hpstrategy(bp) | |
141 | register struct buf *bp; | |
142 | { | |
143 | register struct buf *dp; | |
144 | register unit, xunit, nspc; | |
145 | long sz, bn; | |
146 | struct size *sizes; | |
147 | ||
80e7c811 BJ |
148 | if ((mbaact&(1<<HPMBANUM)) == 0) |
149 | mbainit(HPMBANUM); | |
04b9d53d BJ |
150 | xunit = minor(bp->b_dev) & 077; |
151 | sz = bp->b_bcount; | |
152 | sz = (sz+511) >> 9; | |
153 | unit = dkunit(bp); | |
154 | if (hp_type[unit] == 0) { | |
155 | struct device *hpaddr; | |
f9b6e695 | 156 | double mspw; |
04b9d53d BJ |
157 | |
158 | /* determine device type */ | |
80e7c811 | 159 | hpaddr = mbadev(HPMBA, unit); |
f9b6e695 BJ |
160 | |
161 | /* record transfer rate (these are guesstimates secs/word) */ | |
162 | switch (hp_type[unit] = hpaddr->hpdt) { | |
163 | case RM: mspw = .0000019728; break; | |
164 | case RM5: mspw = .0000020345; break; | |
165 | case RP: mspw = .0000029592; break; | |
166 | } | |
167 | if (DK_N + unit <= DK_NMAX) | |
168 | dk_mspw[DK_N+unit] = mspw; | |
04b9d53d | 169 | } |
5c777efe BJ |
170 | switch (hp_type[unit]) { |
171 | ||
172 | case RM: | |
04b9d53d BJ |
173 | sizes = rm_sizes; |
174 | nspc = NRMSECT*NRMTRAC; | |
5c777efe BJ |
175 | break; |
176 | case RM5: | |
177 | sizes = rm5_sizes; | |
178 | nspc = NRMSECT*NTRAC; | |
179 | break; | |
180 | case RP: | |
04b9d53d BJ |
181 | sizes = hp_sizes; |
182 | nspc = NSECT*NTRAC; | |
5c777efe BJ |
183 | break; |
184 | default: | |
185 | printf("hp: unknown device type 0%o\n", hp_type[unit]); | |
186 | u.u_error = ENXIO; | |
187 | unit = NHP+1; /* force error */ | |
04b9d53d BJ |
188 | } |
189 | if (unit >= NHP || | |
190 | bp->b_blkno < 0 || | |
191 | (bn = dkblock(bp))+sz > sizes[xunit&07].nblocks) { | |
192 | bp->b_flags |= B_ERROR; | |
193 | iodone(bp); | |
194 | return; | |
195 | } | |
196 | bp->b_cylin = bn/nspc + sizes[xunit&07].cyloff; | |
197 | dp = &hputab[unit]; | |
81263dba | 198 | (void) spl5(); |
04b9d53d BJ |
199 | disksort(dp, bp); |
200 | if (dp->b_active == 0) { | |
201 | hpustart(unit); | |
202 | if(hptab.b_active == 0) | |
203 | hpstart(); | |
204 | } | |
81263dba | 205 | (void) spl0(); |
04b9d53d BJ |
206 | } |
207 | ||
208 | hpustart(unit) | |
209 | register unit; | |
210 | { | |
211 | register struct buf *bp, *dp; | |
212 | register struct device *hpaddr; | |
213 | daddr_t bn; | |
214 | int sn, cn, csn; | |
215 | ||
a35e3ab2 | 216 | ((struct mba_regs *)HPMBA)->mba_cr |= MBAIE; |
80e7c811 BJ |
217 | hpaddr = mbadev(HPMBA, 0); |
218 | hpaddr->hpas = 1<<unit; | |
04b9d53d BJ |
219 | |
220 | if(unit >= NHP) | |
221 | return; | |
99a08e2d BJ |
222 | if (unit+DK_N <= DK_NMAX) |
223 | dk_busy &= ~(1<<(unit+DK_N)); | |
04b9d53d BJ |
224 | dp = &hputab[unit]; |
225 | if((bp=dp->b_actf) == NULL) | |
226 | return; | |
80e7c811 | 227 | hpaddr = mbadev(HPMBA, unit); |
04b9d53d BJ |
228 | if((hpaddr->hpds & VV) == 0) { |
229 | hpaddr->hpcs1 = PRESET|GO; | |
230 | hpaddr->hpof = FMT22; | |
231 | } | |
232 | if(dp->b_active) | |
233 | goto done; | |
234 | dp->b_active++; | |
235 | if ((hpaddr->hpds & (DPR|MOL)) != (DPR|MOL)) | |
236 | goto done; | |
237 | ||
238 | bn = dkblock(bp); | |
239 | cn = bp->b_cylin; | |
5c777efe BJ |
240 | switch (hp_type[unit]) { |
241 | ||
242 | case RM: | |
04b9d53d | 243 | sn = bn%(NRMSECT*NRMTRAC); |
41888f16 | 244 | sn = (sn+NRMSECT-hpSDIST)%NRMSECT; |
5c777efe BJ |
245 | break; |
246 | case RM5: | |
247 | sn = bn%(NRMSECT*NTRAC); | |
248 | sn = (sn+NRMSECT-hpSDIST)%NRMSECT; | |
249 | break; | |
250 | case RP: | |
04b9d53d | 251 | sn = bn%(NSECT*NTRAC); |
41888f16 | 252 | sn = (sn+NSECT-hpSDIST)%NSECT; |
5c777efe BJ |
253 | break; |
254 | default: | |
255 | panic("hpustart"); | |
04b9d53d BJ |
256 | } |
257 | ||
258 | if(cn - (hpaddr->hpdc & 0xffff)) | |
259 | goto search; | |
41888f16 BJ |
260 | else if (hpseek) |
261 | goto done; | |
262 | csn = ((hpaddr->hpla & 0xffff)>>6) - sn + 1; | |
04b9d53d BJ |
263 | if(csn < 0) |
264 | csn += NSECT; | |
41888f16 | 265 | if(csn > NSECT-hpRDIST) |
04b9d53d BJ |
266 | goto done; |
267 | ||
268 | search: | |
269 | hpaddr->hpdc = cn; | |
41888f16 BJ |
270 | if (hpseek) |
271 | hpaddr->hpcs1 = SEEK|GO; | |
272 | else { | |
273 | hpaddr->hpda = sn; | |
274 | hpaddr->hpcs1 = SEARCH|GO; | |
275 | } | |
04b9d53d | 276 | unit += DK_N; |
c782a1b2 | 277 | if (unit <= DK_NMAX) { |
99a08e2d | 278 | dk_busy |= 1<<unit; |
f9b6e695 | 279 | dk_seek[unit]++; |
c782a1b2 | 280 | } |
04b9d53d BJ |
281 | return; |
282 | ||
283 | done: | |
284 | dp->b_forw = NULL; | |
285 | if(hptab.b_actf == NULL) | |
99a08e2d BJ |
286 | hptab.b_actf = dp; |
287 | else | |
04b9d53d BJ |
288 | hptab.b_actl->b_forw = dp; |
289 | hptab.b_actl = dp; | |
290 | } | |
291 | ||
292 | hpstart() | |
293 | { | |
294 | register struct buf *bp, *dp; | |
295 | register unit; | |
296 | register struct device *hpaddr; | |
297 | daddr_t bn; | |
298 | int dn, sn, tn, cn, nspc, ns; | |
299 | ||
300 | loop: | |
301 | if ((dp = hptab.b_actf) == NULL) | |
302 | return; | |
303 | if ((bp = dp->b_actf) == NULL) { | |
304 | hptab.b_actf = dp->b_forw; | |
305 | goto loop; | |
306 | } | |
307 | hptab.b_active++; | |
308 | unit = minor(bp->b_dev) & 077; | |
309 | dn = dkunit(bp); | |
310 | bn = dkblock(bp); | |
5c777efe BJ |
311 | switch (hp_type[dn]) { |
312 | case RM: | |
04b9d53d BJ |
313 | nspc = NRMSECT*NRMTRAC; |
314 | ns = NRMSECT; | |
315 | cn = rm_sizes[unit&07].cyloff; | |
5c777efe BJ |
316 | break; |
317 | case RM5: | |
318 | nspc = NRMSECT*NTRAC; | |
319 | ns = NRMSECT; | |
320 | cn = rm5_sizes[unit&07].cyloff; | |
321 | break; | |
322 | case RP: | |
04b9d53d BJ |
323 | nspc = NSECT*NTRAC; |
324 | ns = NSECT; | |
325 | cn = hp_sizes[unit&07].cyloff; | |
5c777efe BJ |
326 | break; |
327 | default: | |
328 | panic("hpstart"); | |
04b9d53d BJ |
329 | } |
330 | cn += bn/nspc; | |
331 | sn = bn%nspc; | |
332 | tn = sn/ns; | |
333 | sn = sn%ns; | |
334 | ||
80e7c811 | 335 | hpaddr = mbadev(HPMBA, dn); |
04b9d53d BJ |
336 | if ((hpaddr->hpds & (DPR|MOL)) != (DPR|MOL)) { |
337 | hptab.b_active = 0; | |
338 | hptab.b_errcnt = 0; | |
339 | dp->b_actf = bp->av_forw; | |
340 | bp->b_flags |= B_ERROR; | |
341 | iodone(bp); | |
342 | goto loop; | |
343 | } | |
15c4ad30 | 344 | if(hptab.b_errcnt >= 16 && (bp->b_flags&B_WRITE) == 0) { |
04b9d53d | 345 | hpaddr->hpof = hp_offset[hptab.b_errcnt & 017] | FMT22; |
80e7c811 | 346 | HPMBA->mba_cr &= ~MBAIE; |
04b9d53d BJ |
347 | hpaddr->hpcs1 = OFFSET|GO; |
348 | while(hpaddr->hpds & PIP) | |
349 | ; | |
80e7c811 | 350 | HPMBA->mba_cr |= MBAIE; |
04b9d53d BJ |
351 | } |
352 | hpaddr->hpdc = cn; | |
353 | hpaddr->hpda = (tn << 8) + sn; | |
354 | mbastart(bp, (int *)hpaddr); | |
355 | ||
99a08e2d | 356 | unit = dn+DK_N; |
99a08e2d BJ |
357 | if (unit <= DK_NMAX) { |
358 | dk_busy |= 1<<unit; | |
f9b6e695 | 359 | dk_xfer[unit]++; |
99a08e2d BJ |
360 | dk_wds[unit] += bp->b_bcount>>6; |
361 | } | |
04b9d53d BJ |
362 | } |
363 | ||
364 | hpintr(mbastat, as) | |
365 | { | |
366 | register struct buf *bp, *dp; | |
367 | register unit; | |
368 | register struct device *hpaddr; | |
369 | ||
370 | if(hptab.b_active) { | |
04b9d53d BJ |
371 | dp = hptab.b_actf; |
372 | bp = dp->b_actf; | |
373 | unit = dkunit(bp); | |
f9b6e695 | 374 | if (DK_N+unit <= DK_NMAX) |
99a08e2d | 375 | dk_busy &= ~(1<<(DK_N+unit)); |
80e7c811 BJ |
376 | hpaddr = mbadev(HPMBA, unit); |
377 | if (hpaddr->hpds & ERR || mbastat & MBAEBITS) { | |
04b9d53d BJ |
378 | while((hpaddr->hpds & DRY) == 0) |
379 | ; | |
380 | if(++hptab.b_errcnt > 28 || hpaddr->hper1&WLE) | |
80e7c811 BJ |
381 | bp->b_flags |= B_ERROR; |
382 | else | |
04b9d53d BJ |
383 | hptab.b_active = 0; |
384 | if(hptab.b_errcnt > 27) | |
385 | deverror(bp, mbastat, hpaddr->hper1); | |
386 | if ((hpaddr->hper1&0xffff) == DCK) { | |
387 | if (hpecc(hpaddr, bp)) | |
388 | return; | |
389 | } | |
390 | hpaddr->hpcs1 = DCLR|GO; | |
391 | if((hptab.b_errcnt&07) == 4) { | |
80e7c811 | 392 | HPMBA->mba_cr &= ~MBAIE; |
04b9d53d BJ |
393 | hpaddr->hpcs1 = RECAL|GO; |
394 | while(hpaddr->hpds & PIP) | |
395 | ; | |
80e7c811 | 396 | HPMBA->mba_cr |= MBAIE; |
04b9d53d BJ |
397 | } |
398 | } | |
399 | if(hptab.b_active) { | |
400 | if(hptab.b_errcnt) { | |
80e7c811 | 401 | HPMBA->mba_cr &= ~MBAIE; |
04b9d53d BJ |
402 | hpaddr->hpcs1 = RTC|GO; |
403 | while(hpaddr->hpds & PIP) | |
404 | ; | |
80e7c811 | 405 | HPMBA->mba_cr |= MBAIE; |
04b9d53d BJ |
406 | } |
407 | hptab.b_active = 0; | |
408 | hptab.b_errcnt = 0; | |
409 | hptab.b_actf = dp->b_forw; | |
410 | dp->b_active = 0; | |
411 | dp->b_errcnt = 0; | |
412 | dp->b_actf = bp->av_forw; | |
80e7c811 | 413 | bp->b_resid = -HPMBA->mba_bcr & 0xffff; |
04b9d53d BJ |
414 | iodone(bp); |
415 | if(dp->b_actf) | |
416 | hpustart(unit); | |
417 | } | |
418 | as &= ~(1<<unit); | |
419 | } else { | |
420 | if(as == 0) | |
80e7c811 | 421 | HPMBA->mba_cr |= MBAIE; |
04b9d53d BJ |
422 | } |
423 | for(unit=0; unit<NHP; unit++) | |
424 | if(as & (1<<unit)) | |
425 | hpustart(unit); | |
426 | hpstart(); | |
427 | } | |
428 | ||
429 | hpread(dev) | |
430 | { | |
431 | ||
432 | physio(hpstrategy, &rhpbuf, dev, B_READ, minphys); | |
433 | } | |
434 | ||
435 | hpwrite(dev) | |
436 | { | |
437 | ||
438 | physio(hpstrategy, &rhpbuf, dev, B_WRITE, minphys); | |
439 | } | |
440 | ||
441 | hpecc(rp, bp) | |
442 | register struct device *rp; | |
443 | register struct buf *bp; | |
444 | { | |
80e7c811 BJ |
445 | struct mba_regs *mbp = HPMBA; |
446 | register int i; | |
447 | caddr_t addr; | |
448 | int reg, bit, byte, npf, mask, o; | |
449 | int dn, bn, cn, tn, sn, ns, nt; | |
5f3edb0e | 450 | extern char buffers[NBUF][BSIZE]; |
80e7c811 | 451 | struct pte mpte; |
fa1d69d6 | 452 | int bcr; |
80e7c811 BJ |
453 | |
454 | /* | |
455 | * Npf is the number of sectors transferred before the sector | |
456 | * containing the ECC error, and reg is the MBA register | |
457 | * mapping (the first part of)the transfer. | |
458 | * O is offset within a memory page of the first byte transferred. | |
459 | */ | |
fa1d69d6 BJ |
460 | bcr = mbp->mba_bcr & 0xffff; |
461 | if (bcr) | |
462 | bcr |= 0xffff0000; /* sxt */ | |
acecdc5c | 463 | npf = btop(bcr + bp->b_bcount) - 1; |
f9b6e695 | 464 | reg = npf; |
80e7c811 BJ |
465 | o = (int)bp->b_un.b_addr & PGOFSET; |
466 | printf("%D ", bp->b_blkno + npf); | |
04b9d53d BJ |
467 | prdev("ECC", bp->b_dev); |
468 | mask = rp->hpec2&0xffff; | |
469 | if (mask == 0) { | |
470 | rp->hpof = FMT22; | |
80e7c811 | 471 | return (0); |
04b9d53d | 472 | } |
80e7c811 BJ |
473 | |
474 | /* | |
475 | * Compute the byte and bit position of the error. | |
476 | * The variable i is the byte offset in the transfer, | |
477 | * the variable byte is the offset from a page boundary | |
478 | * in main memory. | |
479 | */ | |
480 | i = (rp->hpec1&0xffff) - 1; /* -1 makes 0 origin */ | |
acecdc5c | 481 | bit = i&07; |
80e7c811 BJ |
482 | i = (i&~07)>>3; |
483 | byte = i + o; | |
484 | /* | |
485 | * Correct while possible bits remain of mask. Since mask | |
486 | * contains 11 bits, we continue while the bit offset is > -11. | |
487 | * Also watch out for end of this block and the end of the whole | |
488 | * transfer. | |
489 | */ | |
490 | while (i < 512 && (int)ptob(npf)+i < bp->b_bcount && bit > -11) { | |
491 | mpte = mbp->mba_map[reg+btop(byte)]; | |
492 | addr = ptob(mpte.pg_pfnum) + (byte & PGOFSET); | |
493 | putmemc(addr, getmemc(addr)^(mask<<bit)); | |
494 | byte++; | |
495 | i++; | |
496 | bit -= 8; | |
04b9d53d | 497 | } |
80e7c811 | 498 | hptab.b_active++; /* Either complete or continuing */ |
acecdc5c | 499 | if (bcr == 0) |
80e7c811 BJ |
500 | return (0); |
501 | /* | |
502 | * Have to continue the transfer... clear the drive, | |
503 | * and compute the position where the transfer is to continue. | |
504 | * We have completed npf+1 sectores of the transfer already; | |
505 | * restart at offset o of next sector (i.e. in MBA register reg+1). | |
506 | */ | |
507 | rp->hpcs1 = DCLR|GO; | |
508 | dn = dkunit(bp); | |
509 | bn = dkblock(bp); | |
5c777efe BJ |
510 | switch (hp_type[dn]) { |
511 | ||
512 | case RM: | |
513 | ns = NRMSECT; nt = NRMTRAC; break; | |
514 | case RM5: | |
515 | ns = NRMSECT; nt = NTRAC; break; | |
516 | case RP: | |
517 | ns = NSECT; nt = NTRAC; break; | |
518 | default: | |
519 | panic("hpecc"); | |
04b9d53d | 520 | } |
80e7c811 BJ |
521 | cn = bp->b_cylin; |
522 | sn = bn%(ns*nt) + npf + 1; | |
523 | tn = sn/ns; | |
524 | sn %= ns; | |
525 | cn += tn/nt; | |
526 | tn %= nt; | |
527 | rp->hpdc = cn; | |
528 | rp->hpda = (tn<<8) + sn; | |
529 | mbp->mba_sr = -1; | |
530 | mbp->mba_var = (int)ptob(reg+1) + o; | |
531 | rp->hpcs1 = RCOM|GO; | |
532 | return (1); | |
04b9d53d | 533 | } |
a5cc519e | 534 | #endif |