diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /arch/ppc/platforms/residual.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'arch/ppc/platforms/residual.c')
-rw-r--r-- | arch/ppc/platforms/residual.c | 1034 |
1 files changed, 1034 insertions, 0 deletions
diff --git a/arch/ppc/platforms/residual.c b/arch/ppc/platforms/residual.c new file mode 100644 index 000000000000..0f84ca603612 --- /dev/null +++ b/arch/ppc/platforms/residual.c | |||
@@ -0,0 +1,1034 @@ | |||
1 | /* | ||
2 | * Code to deal with the PReP residual data. | ||
3 | * | ||
4 | * Written by: Cort Dougan (cort@cs.nmt.edu) | ||
5 | * Improved _greatly_ and rewritten by Gabriel Paubert (paubert@iram.es) | ||
6 | * | ||
7 | * This file is based on the following documentation: | ||
8 | * | ||
9 | * IBM Power Personal Systems Architecture | ||
10 | * Residual Data | ||
11 | * Document Number: PPS-AR-FW0001 | ||
12 | * | ||
13 | * This file is subject to the terms and conditions of the GNU General Public | ||
14 | * License. See the file COPYING in the main directory of this archive | ||
15 | * for more details. | ||
16 | * | ||
17 | */ | ||
18 | |||
19 | #include <linux/string.h> | ||
20 | #include <asm/residual.h> | ||
21 | #include <asm/pnp.h> | ||
22 | #include <asm/byteorder.h> | ||
23 | |||
24 | #include <linux/errno.h> | ||
25 | #include <linux/sched.h> | ||
26 | #include <linux/kernel.h> | ||
27 | #include <linux/mm.h> | ||
28 | #include <linux/stddef.h> | ||
29 | #include <linux/unistd.h> | ||
30 | #include <linux/ptrace.h> | ||
31 | #include <linux/slab.h> | ||
32 | #include <linux/user.h> | ||
33 | #include <linux/a.out.h> | ||
34 | #include <linux/tty.h> | ||
35 | #include <linux/major.h> | ||
36 | #include <linux/interrupt.h> | ||
37 | #include <linux/reboot.h> | ||
38 | #include <linux/init.h> | ||
39 | #include <linux/ioport.h> | ||
40 | #include <linux/pci.h> | ||
41 | #include <linux/ide.h> | ||
42 | |||
43 | #include <asm/sections.h> | ||
44 | #include <asm/mmu.h> | ||
45 | #include <asm/io.h> | ||
46 | #include <asm/pgtable.h> | ||
47 | #include <asm/ide.h> | ||
48 | |||
49 | |||
50 | unsigned char __res[sizeof(RESIDUAL)] __prepdata = {0,}; | ||
51 | RESIDUAL *res = (RESIDUAL *)&__res; | ||
52 | |||
53 | char * PnP_BASE_TYPES[] __initdata = { | ||
54 | "Reserved", | ||
55 | "MassStorageDevice", | ||
56 | "NetworkInterfaceController", | ||
57 | "DisplayController", | ||
58 | "MultimediaController", | ||
59 | "MemoryController", | ||
60 | "BridgeController", | ||
61 | "CommunicationsDevice", | ||
62 | "SystemPeripheral", | ||
63 | "InputDevice", | ||
64 | "ServiceProcessor" | ||
65 | }; | ||
66 | |||
67 | /* Device Sub Type Codes */ | ||
68 | |||
69 | unsigned char * PnP_SUB_TYPES[] __initdata = { | ||
70 | "\001\000SCSIController", | ||
71 | "\001\001IDEController", | ||
72 | "\001\002FloppyController", | ||
73 | "\001\003IPIController", | ||
74 | "\001\200OtherMassStorageController", | ||
75 | "\002\000EthernetController", | ||
76 | "\002\001TokenRingController", | ||
77 | "\002\002FDDIController", | ||
78 | "\002\0x80OtherNetworkController", | ||
79 | "\003\000VGAController", | ||
80 | "\003\001SVGAController", | ||
81 | "\003\002XGAController", | ||
82 | "\003\200OtherDisplayController", | ||
83 | "\004\000VideoController", | ||
84 | "\004\001AudioController", | ||
85 | "\004\200OtherMultimediaController", | ||
86 | "\005\000RAM", | ||
87 | "\005\001FLASH", | ||
88 | "\005\200OtherMemoryDevice", | ||
89 | "\006\000HostProcessorBridge", | ||
90 | "\006\001ISABridge", | ||
91 | "\006\002EISABridge", | ||
92 | "\006\003MicroChannelBridge", | ||
93 | "\006\004PCIBridge", | ||
94 | "\006\005PCMCIABridge", | ||
95 | "\006\006VMEBridge", | ||
96 | "\006\200OtherBridgeDevice", | ||
97 | "\007\000RS232Device", | ||
98 | "\007\001ATCompatibleParallelPort", | ||
99 | "\007\200OtherCommunicationsDevice", | ||
100 | "\010\000ProgrammableInterruptController", | ||
101 | "\010\001DMAController", | ||
102 | "\010\002SystemTimer", | ||
103 | "\010\003RealTimeClock", | ||
104 | "\010\004L2Cache", | ||
105 | "\010\005NVRAM", | ||
106 | "\010\006PowerManagement", | ||
107 | "\010\007CMOS", | ||
108 | "\010\010OperatorPanel", | ||
109 | "\010\011ServiceProcessorClass1", | ||
110 | "\010\012ServiceProcessorClass2", | ||
111 | "\010\013ServiceProcessorClass3", | ||
112 | "\010\014GraphicAssist", | ||
113 | "\010\017SystemPlanar", | ||
114 | "\010\200OtherSystemPeripheral", | ||
115 | "\011\000KeyboardController", | ||
116 | "\011\001Digitizer", | ||
117 | "\011\002MouseController", | ||
118 | "\011\003TabletController", | ||
119 | "\011\0x80OtherInputController", | ||
120 | "\012\000GeneralMemoryController", | ||
121 | NULL | ||
122 | }; | ||
123 | |||
124 | /* Device Interface Type Codes */ | ||
125 | |||
126 | unsigned char * PnP_INTERFACES[] __initdata = { | ||
127 | "\000\000\000General", | ||
128 | "\001\000\000GeneralSCSI", | ||
129 | "\001\001\000GeneralIDE", | ||
130 | "\001\001\001ATACompatible", | ||
131 | |||
132 | "\001\002\000GeneralFloppy", | ||
133 | "\001\002\001Compatible765", | ||
134 | "\001\002\002NS398_Floppy", /* NS Super I/O wired to use index | ||
135 | register at port 398 and data | ||
136 | register at port 399 */ | ||
137 | "\001\002\003NS26E_Floppy", /* Ports 26E and 26F */ | ||
138 | "\001\002\004NS15C_Floppy", /* Ports 15C and 15D */ | ||
139 | "\001\002\005NS2E_Floppy", /* Ports 2E and 2F */ | ||
140 | "\001\002\006CHRP_Floppy", /* CHRP Floppy in PR*P system */ | ||
141 | |||
142 | "\001\003\000GeneralIPI", | ||
143 | |||
144 | "\002\000\000GeneralEther", | ||
145 | "\002\001\000GeneralToken", | ||
146 | "\002\002\000GeneralFDDI", | ||
147 | |||
148 | "\003\000\000GeneralVGA", | ||
149 | "\003\001\000GeneralSVGA", | ||
150 | "\003\002\000GeneralXGA", | ||
151 | |||
152 | "\004\000\000GeneralVideo", | ||
153 | "\004\001\000GeneralAudio", | ||
154 | "\004\001\001CS4232Audio", /* CS 4232 Plug 'n Play Configured */ | ||
155 | |||
156 | "\005\000\000GeneralRAM", | ||
157 | /* This one is obviously wrong ! */ | ||
158 | "\005\000\000PCIMemoryController", /* PCI Config Method */ | ||
159 | "\005\000\001RS6KMemoryController", /* RS6K Config Method */ | ||
160 | "\005\001\000GeneralFLASH", | ||
161 | |||
162 | "\006\000\000GeneralHostBridge", | ||
163 | "\006\001\000GeneralISABridge", | ||
164 | "\006\002\000GeneralEISABridge", | ||
165 | "\006\003\000GeneralMCABridge", | ||
166 | /* GeneralPCIBridge = 0, */ | ||
167 | "\006\004\000PCIBridgeDirect", | ||
168 | "\006\004\001PCIBridgeIndirect", | ||
169 | "\006\004\002PCIBridgeRS6K", | ||
170 | "\006\005\000GeneralPCMCIABridge", | ||
171 | "\006\006\000GeneralVMEBridge", | ||
172 | |||
173 | "\007\000\000GeneralRS232", | ||
174 | "\007\000\001COMx", | ||
175 | "\007\000\002Compatible16450", | ||
176 | "\007\000\003Compatible16550", | ||
177 | "\007\000\004NS398SerPort", /* NS Super I/O wired to use index | ||
178 | register at port 398 and data | ||
179 | register at port 399 */ | ||
180 | "\007\000\005NS26ESerPort", /* Ports 26E and 26F */ | ||
181 | "\007\000\006NS15CSerPort", /* Ports 15C and 15D */ | ||
182 | "\007\000\007NS2ESerPort", /* Ports 2E and 2F */ | ||
183 | |||
184 | "\007\001\000GeneralParPort", | ||
185 | "\007\001\001LPTx", | ||
186 | "\007\001\002NS398ParPort", /* NS Super I/O wired to use index | ||
187 | register at port 398 and data | ||
188 | register at port 399 */ | ||
189 | "\007\001\003NS26EParPort", /* Ports 26E and 26F */ | ||
190 | "\007\001\004NS15CParPort", /* Ports 15C and 15D */ | ||
191 | "\007\001\005NS2EParPort", /* Ports 2E and 2F */ | ||
192 | |||
193 | "\010\000\000GeneralPIC", | ||
194 | "\010\000\001ISA_PIC", | ||
195 | "\010\000\002EISA_PIC", | ||
196 | "\010\000\003MPIC", | ||
197 | "\010\000\004RS6K_PIC", | ||
198 | |||
199 | "\010\001\000GeneralDMA", | ||
200 | "\010\001\001ISA_DMA", | ||
201 | "\010\001\002EISA_DMA", | ||
202 | |||
203 | "\010\002\000GeneralTimer", | ||
204 | "\010\002\001ISA_Timer", | ||
205 | "\010\002\002EISA_Timer", | ||
206 | "\010\003\000GeneralRTC", | ||
207 | "\010\003\001ISA_RTC", | ||
208 | |||
209 | "\010\004\001StoreThruOnly", | ||
210 | "\010\004\002StoreInEnabled", | ||
211 | "\010\004\003RS6KL2Cache", | ||
212 | |||
213 | "\010\005\000IndirectNVRAM", /* Indirectly addressed */ | ||
214 | "\010\005\001DirectNVRAM", /* Memory Mapped */ | ||
215 | "\010\005\002IndirectNVRAM24", /* Indirectly addressed - 24 bit */ | ||
216 | |||
217 | "\010\006\000GeneralPowerManagement", | ||
218 | "\010\006\001EPOWPowerManagement", | ||
219 | "\010\006\002PowerControl", // d1378 | ||
220 | |||
221 | "\010\007\000GeneralCMOS", | ||
222 | |||
223 | "\010\010\000GeneralOPPanel", | ||
224 | "\010\010\001HarddiskLight", | ||
225 | "\010\010\002CDROMLight", | ||
226 | "\010\010\003PowerLight", | ||
227 | "\010\010\004KeyLock", | ||
228 | "\010\010\005ANDisplay", /* AlphaNumeric Display */ | ||
229 | "\010\010\006SystemStatusLED", /* 3 digit 7 segment LED */ | ||
230 | "\010\010\007CHRP_SystemStatusLED", /* CHRP LEDs in PR*P system */ | ||
231 | |||
232 | "\010\011\000GeneralServiceProcessor", | ||
233 | "\010\012\000GeneralServiceProcessor", | ||
234 | "\010\013\000GeneralServiceProcessor", | ||
235 | |||
236 | "\010\014\001TransferData", | ||
237 | "\010\014\002IGMC32", | ||
238 | "\010\014\003IGMC64", | ||
239 | |||
240 | "\010\017\000GeneralSystemPlanar", /* 10/5/95 */ | ||
241 | NULL | ||
242 | }; | ||
243 | |||
244 | static const unsigned char __init *PnP_SUB_TYPE_STR(unsigned char BaseType, | ||
245 | unsigned char SubType) { | ||
246 | unsigned char ** s=PnP_SUB_TYPES; | ||
247 | while (*s && !((*s)[0]==BaseType | ||
248 | && (*s)[1]==SubType)) s++; | ||
249 | if (*s) return *s+2; | ||
250 | else return("Unknown !"); | ||
251 | }; | ||
252 | |||
253 | static const unsigned char __init *PnP_INTERFACE_STR(unsigned char BaseType, | ||
254 | unsigned char SubType, | ||
255 | unsigned char Interface) { | ||
256 | unsigned char ** s=PnP_INTERFACES; | ||
257 | while (*s && !((*s)[0]==BaseType | ||
258 | && (*s)[1]==SubType | ||
259 | && (*s)[2]==Interface)) s++; | ||
260 | if (*s) return *s+3; | ||
261 | else return NULL; | ||
262 | }; | ||
263 | |||
264 | static void __init printsmallvendor(PnP_TAG_PACKET *pkt, int size) { | ||
265 | int i, c; | ||
266 | char decomp[4]; | ||
267 | #define p pkt->S14_Pack.S14_Data.S14_PPCPack | ||
268 | switch(p.Type) { | ||
269 | case 1: | ||
270 | /* Decompress first 3 chars */ | ||
271 | c = *(unsigned short *)p.PPCData; | ||
272 | decomp[0]='A'-1+((c>>10)&0x1F); | ||
273 | decomp[1]='A'-1+((c>>5)&0x1F); | ||
274 | decomp[2]='A'-1+(c&0x1F); | ||
275 | decomp[3]=0; | ||
276 | printk(" Chip identification: %s%4.4X\n", | ||
277 | decomp, ld_le16((unsigned short *)(p.PPCData+2))); | ||
278 | break; | ||
279 | default: | ||
280 | printk(" Small vendor item type 0x%2.2x, data (hex): ", | ||
281 | p.Type); | ||
282 | for(i=0; i<size-2; i++) printk("%2.2x ", p.PPCData[i]); | ||
283 | printk("\n"); | ||
284 | break; | ||
285 | } | ||
286 | #undef p | ||
287 | } | ||
288 | |||
289 | static void __init printsmallpacket(PnP_TAG_PACKET * pkt, int size) { | ||
290 | static const unsigned char * intlevel[] = {"high", "low"}; | ||
291 | static const unsigned char * intsense[] = {"edge", "level"}; | ||
292 | |||
293 | switch (tag_small_item_name(pkt->S1_Pack.Tag)) { | ||
294 | case PnPVersion: | ||
295 | printk(" PnPversion 0x%x.%x\n", | ||
296 | pkt->S1_Pack.Version[0], /* How to interpret version ? */ | ||
297 | pkt->S1_Pack.Version[1]); | ||
298 | break; | ||
299 | // case Logicaldevice: | ||
300 | break; | ||
301 | // case CompatibleDevice: | ||
302 | break; | ||
303 | case IRQFormat: | ||
304 | #define p pkt->S4_Pack | ||
305 | printk(" IRQ Mask 0x%4.4x, %s %s sensitive\n", | ||
306 | ld_le16((unsigned short *)p.IRQMask), | ||
307 | intlevel[(size>3) ? !(p.IRQInfo&0x05) : 0], | ||
308 | intsense[(size>3) ? !(p.IRQInfo&0x03) : 0]); | ||
309 | #undef p | ||
310 | break; | ||
311 | case DMAFormat: | ||
312 | #define p pkt->S5_Pack | ||
313 | printk(" DMA channel mask 0x%2.2x, info 0x%2.2x\n", | ||
314 | p.DMAMask, p.DMAInfo); | ||
315 | #undef p | ||
316 | break; | ||
317 | case StartDepFunc: | ||
318 | printk("Start dependent function:\n"); | ||
319 | break; | ||
320 | case EndDepFunc: | ||
321 | printk("End dependent function\n"); | ||
322 | break; | ||
323 | case IOPort: | ||
324 | #define p pkt->S8_Pack | ||
325 | printk(" Variable (%d decoded bits) I/O port\n" | ||
326 | " from 0x%4.4x to 0x%4.4x, alignment %d, %d ports\n", | ||
327 | p.IOInfo&ISAAddr16bit?16:10, | ||
328 | ld_le16((unsigned short *)p.RangeMin), | ||
329 | ld_le16((unsigned short *)p.RangeMax), | ||
330 | p.IOAlign, p.IONum); | ||
331 | #undef p | ||
332 | break; | ||
333 | case FixedIOPort: | ||
334 | #define p pkt->S9_Pack | ||
335 | printk(" Fixed (10 decoded bits) I/O port from %3.3x to %3.3x\n", | ||
336 | (p.Range[1]<<8)|p.Range[0], | ||
337 | ((p.Range[1]<<8)|p.Range[0])+p.IONum-1); | ||
338 | #undef p | ||
339 | break; | ||
340 | case Res1: | ||
341 | case Res2: | ||
342 | case Res3: | ||
343 | printk(" Undefined packet type %d!\n", | ||
344 | tag_small_item_name(pkt->S1_Pack.Tag)); | ||
345 | break; | ||
346 | case SmallVendorItem: | ||
347 | printsmallvendor(pkt,size); | ||
348 | break; | ||
349 | default: | ||
350 | printk(" Type 0x2.2x%d, size=%d\n", | ||
351 | pkt->S1_Pack.Tag, size); | ||
352 | break; | ||
353 | } | ||
354 | } | ||
355 | |||
356 | static void __init printlargevendor(PnP_TAG_PACKET * pkt, int size) { | ||
357 | static const unsigned char * addrtype[] = {"I/O", "Memory", "System"}; | ||
358 | static const unsigned char * inttype[] = {"8259", "MPIC", "RS6k BUID %d"}; | ||
359 | static const unsigned char * convtype[] = {"Bus Memory", "Bus I/O", "DMA"}; | ||
360 | static const unsigned char * transtype[] = {"direct", "mapped", "direct-store segment"}; | ||
361 | static const unsigned char * L2type[] = {"WriteThru", "CopyBack"}; | ||
362 | static const unsigned char * L2assoc[] = {"DirectMapped", "2-way set"}; | ||
363 | |||
364 | int i; | ||
365 | char tmpstr[30], *t; | ||
366 | #define p pkt->L4_Pack.L4_Data.L4_PPCPack | ||
367 | switch(p.Type) { | ||
368 | case 2: | ||
369 | printk(" %d K %s %s L2 cache, %d/%d bytes line/sector size\n", | ||
370 | ld_le32((unsigned int *)p.PPCData), | ||
371 | L2type[p.PPCData[10]-1], | ||
372 | L2assoc[p.PPCData[4]-1], | ||
373 | ld_le16((unsigned short *)p.PPCData+3), | ||
374 | ld_le16((unsigned short *)p.PPCData+4)); | ||
375 | break; | ||
376 | case 3: | ||
377 | printk(" PCI Bridge parameters\n" | ||
378 | " ConfigBaseAddress %0x\n" | ||
379 | " ConfigBaseData %0x\n" | ||
380 | " Bus number %d\n", | ||
381 | ld_le32((unsigned int *)p.PPCData), | ||
382 | ld_le32((unsigned int *)(p.PPCData+8)), | ||
383 | p.PPCData[16]); | ||
384 | for(i=20; i<size-4; i+=12) { | ||
385 | int j, first; | ||
386 | if(p.PPCData[i]) printk(" PCI Slot %d", p.PPCData[i]); | ||
387 | else printk (" Integrated PCI device"); | ||
388 | for(j=0, first=1, t=tmpstr; j<4; j++) { | ||
389 | int line=ld_le16((unsigned short *)(p.PPCData+i+4)+j); | ||
390 | if(line!=0xffff){ | ||
391 | if(first) first=0; else *t++='/'; | ||
392 | *t++='A'+j; | ||
393 | } | ||
394 | } | ||
395 | *t='\0'; | ||
396 | printk(" DevFunc 0x%x interrupt line(s) %s routed to", | ||
397 | p.PPCData[i+1],tmpstr); | ||
398 | sprintf(tmpstr, | ||
399 | inttype[p.PPCData[i+2]-1], | ||
400 | p.PPCData[i+3]); | ||
401 | printk(" %s line(s) ", | ||
402 | tmpstr); | ||
403 | for(j=0, first=1, t=tmpstr; j<4; j++) { | ||
404 | int line=ld_le16((unsigned short *)(p.PPCData+i+4)+j); | ||
405 | if(line!=0xffff){ | ||
406 | if(first) first=0; else *t++='/'; | ||
407 | t+=sprintf(t,"%d(%c)", | ||
408 | line&0x7fff, | ||
409 | line&0x8000?'E':'L'); | ||
410 | } | ||
411 | } | ||
412 | printk("%s\n",tmpstr); | ||
413 | } | ||
414 | break; | ||
415 | case 5: | ||
416 | printk(" Bridge address translation, %s decoding:\n" | ||
417 | " Processor Bus Size Conversion Translation\n" | ||
418 | " 0x%8.8x 0x%8.8x 0x%8.8x %s %s\n", | ||
419 | p.PPCData[0]&1 ? "positive" : "subtractive", | ||
420 | ld_le32((unsigned int *)p.PPCData+1), | ||
421 | ld_le32((unsigned int *)p.PPCData+3), | ||
422 | ld_le32((unsigned int *)p.PPCData+5), | ||
423 | convtype[p.PPCData[2]-1], | ||
424 | transtype[p.PPCData[1]-1]); | ||
425 | break; | ||
426 | case 6: | ||
427 | printk(" Bus speed %d Hz, %d slot(s)\n", | ||
428 | ld_le32((unsigned int *)p.PPCData), | ||
429 | p.PPCData[4]); | ||
430 | break; | ||
431 | case 7: | ||
432 | printk(" SCSI buses: %d, id(s):", p.PPCData[0]); | ||
433 | for(i=1; i<=p.PPCData[0]; i++) | ||
434 | printk(" %d%c", p.PPCData[i], i==p.PPCData[0] ? '\n' : ','); | ||
435 | break; | ||
436 | case 9: | ||
437 | printk(" %s address (%d bits), at 0x%x size 0x%x bytes\n", | ||
438 | addrtype[p.PPCData[0]-1], | ||
439 | p.PPCData[1], | ||
440 | ld_le32((unsigned int *)(p.PPCData+4)), | ||
441 | ld_le32((unsigned int *)(p.PPCData+12))); | ||
442 | break; | ||
443 | case 10: | ||
444 | sprintf(tmpstr, | ||
445 | inttype[p.PPCData[0]-1], | ||
446 | p.PPCData[1]); | ||
447 | |||
448 | printk(" ISA interrupts routed to %s\n" | ||
449 | " lines", | ||
450 | tmpstr); | ||
451 | for(i=0; i<16; i++) { | ||
452 | int line=ld_le16((unsigned short *)p.PPCData+i+1); | ||
453 | if (line!=0xffff) printk(" %d(IRQ%d)", line, i); | ||
454 | } | ||
455 | printk("\n"); | ||
456 | break; | ||
457 | default: | ||
458 | printk(" Large vendor item type 0x%2.2x\n Data (hex):", | ||
459 | p.Type); | ||
460 | for(i=0; i<size-4; i++) printk(" %2.2x", p.PPCData[i]); | ||
461 | printk("\n"); | ||
462 | #undef p | ||
463 | } | ||
464 | } | ||
465 | |||
466 | static void __init printlargepacket(PnP_TAG_PACKET * pkt, int size) { | ||
467 | switch (tag_large_item_name(pkt->S1_Pack.Tag)) { | ||
468 | case LargeVendorItem: | ||
469 | printlargevendor(pkt, size); | ||
470 | break; | ||
471 | default: | ||
472 | printk(" Type 0x2.2x%d, size=%d\n", | ||
473 | pkt->S1_Pack.Tag, size); | ||
474 | break; | ||
475 | } | ||
476 | } | ||
477 | |||
478 | static void __init printpackets(PnP_TAG_PACKET * pkt, const char * cat) | ||
479 | { | ||
480 | if (pkt->S1_Pack.Tag== END_TAG) { | ||
481 | printk(" No packets describing %s resources.\n", cat); | ||
482 | return; | ||
483 | } | ||
484 | printk( " Packets describing %s resources:\n",cat); | ||
485 | do { | ||
486 | int size; | ||
487 | if (tag_type(pkt->S1_Pack.Tag)) { | ||
488 | size= 3 + | ||
489 | pkt->L1_Pack.Count0 + | ||
490 | pkt->L1_Pack.Count1*256; | ||
491 | printlargepacket(pkt, size); | ||
492 | } else { | ||
493 | size=tag_small_count(pkt->S1_Pack.Tag)+1; | ||
494 | printsmallpacket(pkt, size); | ||
495 | } | ||
496 | pkt = (PnP_TAG_PACKET *)((unsigned char *) pkt + size); | ||
497 | } while (pkt->S1_Pack.Tag != END_TAG); | ||
498 | } | ||
499 | |||
500 | void __init print_residual_device_info(void) | ||
501 | { | ||
502 | int i; | ||
503 | PPC_DEVICE *dev; | ||
504 | #define did dev->DeviceId | ||
505 | |||
506 | /* make sure we have residual data first */ | ||
507 | if (!have_residual_data) | ||
508 | return; | ||
509 | |||
510 | printk("Residual: %ld devices\n", res->ActualNumDevices); | ||
511 | for ( i = 0; | ||
512 | i < res->ActualNumDevices ; | ||
513 | i++) | ||
514 | { | ||
515 | char decomp[4], sn[20]; | ||
516 | const char * s; | ||
517 | dev = &res->Devices[i]; | ||
518 | s = PnP_INTERFACE_STR(did.BaseType, did.SubType, | ||
519 | did.Interface); | ||
520 | if(!s) { | ||
521 | sprintf(sn, "interface %d", did.Interface); | ||
522 | s=sn; | ||
523 | } | ||
524 | if ( did.BusId & PCIDEVICE ) | ||
525 | printk("PCI Device, Bus %d, DevFunc 0x%x:", | ||
526 | dev->BusAccess.PCIAccess.BusNumber, | ||
527 | dev->BusAccess.PCIAccess.DevFuncNumber); | ||
528 | if ( did.BusId & PNPISADEVICE ) printk("PNPISA Device:"); | ||
529 | if ( did.BusId & ISADEVICE ) | ||
530 | printk("ISA Device, Slot %d, LogicalDev %d:", | ||
531 | dev->BusAccess.ISAAccess.SlotNumber, | ||
532 | dev->BusAccess.ISAAccess.LogicalDevNumber); | ||
533 | if ( did.BusId & EISADEVICE ) printk("EISA Device:"); | ||
534 | if ( did.BusId & PROCESSORDEVICE ) | ||
535 | printk("ProcBus Device, Bus %d, BUID %d: ", | ||
536 | dev->BusAccess.ProcBusAccess.BusNumber, | ||
537 | dev->BusAccess.ProcBusAccess.BUID); | ||
538 | if ( did.BusId & PCMCIADEVICE ) printk("PCMCIA "); | ||
539 | if ( did.BusId & VMEDEVICE ) printk("VME "); | ||
540 | if ( did.BusId & MCADEVICE ) printk("MCA "); | ||
541 | if ( did.BusId & MXDEVICE ) printk("MX "); | ||
542 | /* Decompress first 3 chars */ | ||
543 | decomp[0]='A'-1+((did.DevId>>26)&0x1F); | ||
544 | decomp[1]='A'-1+((did.DevId>>21)&0x1F); | ||
545 | decomp[2]='A'-1+((did.DevId>>16)&0x1F); | ||
546 | decomp[3]=0; | ||
547 | printk(" %s%4.4lX, %s, %s, %s\n", | ||
548 | decomp, did.DevId&0xffff, | ||
549 | PnP_BASE_TYPES[did.BaseType], | ||
550 | PnP_SUB_TYPE_STR(did.BaseType,did.SubType), | ||
551 | s); | ||
552 | if ( dev->AllocatedOffset ) | ||
553 | printpackets( (union _PnP_TAG_PACKET *) | ||
554 | &res->DevicePnPHeap[dev->AllocatedOffset], | ||
555 | "allocated"); | ||
556 | if ( dev->PossibleOffset ) | ||
557 | printpackets( (union _PnP_TAG_PACKET *) | ||
558 | &res->DevicePnPHeap[dev->PossibleOffset], | ||
559 | "possible"); | ||
560 | if ( dev->CompatibleOffset ) | ||
561 | printpackets( (union _PnP_TAG_PACKET *) | ||
562 | &res->DevicePnPHeap[dev->CompatibleOffset], | ||
563 | "compatible"); | ||
564 | } | ||
565 | } | ||
566 | |||
567 | |||
568 | #if 0 | ||
569 | static void __init printVPD(void) { | ||
570 | #define vpd res->VitalProductData | ||
571 | int ps=vpd.PageSize, i, j; | ||
572 | static const char* Usage[]={ | ||
573 | "FirmwareStack", "FirmwareHeap", "FirmwareCode", "BootImage", | ||
574 | "Free", "Unpopulated", "ISAAddr", "PCIConfig", | ||
575 | "IOMemory", "SystemIO", "SystemRegs", "PCIAddr", | ||
576 | "UnPopSystemRom", "SystemROM", "ResumeBlock", "Other" | ||
577 | }; | ||
578 | static const unsigned char *FWMan[]={ | ||
579 | "IBM", "Motorola", "FirmWorks", "Bull" | ||
580 | }; | ||
581 | static const unsigned char *FWFlags[]={ | ||
582 | "Conventional", "OpenFirmware", "Diagnostics", "LowDebug", | ||
583 | "MultiBoot", "LowClient", "Hex41", "FAT", | ||
584 | "ISO9660", "SCSI_ID_Override", "Tape_Boot", "FW_Boot_Path" | ||
585 | }; | ||
586 | static const unsigned char *ESM[]={ | ||
587 | "Port92", "PCIConfigA8", "FF001030", "????????" | ||
588 | }; | ||
589 | static const unsigned char *SIOM[]={ | ||
590 | "Port850", "????????", "PCIConfigA8", "????????" | ||
591 | }; | ||
592 | |||
593 | printk("Model: %s\n",vpd.PrintableModel); | ||
594 | printk("Serial: %s\n", vpd.Serial); | ||
595 | printk("FirmwareSupplier: %s\n", FWMan[vpd.FirmwareSupplier]); | ||
596 | printk("FirmwareFlags:"); | ||
597 | for(j=0; j<12; j++) { | ||
598 | if (vpd.FirmwareSupports & (1<<j)) { | ||
599 | printk(" %s%c", FWFlags[j], | ||
600 | vpd.FirmwareSupports&(-2<<j) ? ',' : '\n'); | ||
601 | } | ||
602 | } | ||
603 | printk("NVRamSize: %ld\n", vpd.NvramSize); | ||
604 | printk("SIMMslots: %ld\n", vpd.NumSIMMSlots); | ||
605 | printk("EndianSwitchMethod: %s\n", | ||
606 | ESM[vpd.EndianSwitchMethod>2 ? 2 : vpd.EndianSwitchMethod]); | ||
607 | printk("SpreadIOMethod: %s\n", | ||
608 | SIOM[vpd.SpreadIOMethod>3 ? 3 : vpd.SpreadIOMethod]); | ||
609 | printk("Processor/Bus frequencies (Hz): %ld/%ld\n", | ||
610 | vpd.ProcessorHz, vpd.ProcessorBusHz); | ||
611 | printk("Time Base Divisor: %ld\n", vpd.TimeBaseDivisor); | ||
612 | printk("WordWidth, PageSize: %ld, %d\n", vpd.WordWidth, ps); | ||
613 | printk("Cache sector size, Lock granularity: %ld, %ld\n", | ||
614 | vpd.CoherenceBlockSize, vpd.GranuleSize); | ||
615 | for (i=0; i<res->ActualNumMemSegs; i++) { | ||
616 | int mask=res->Segs[i].Usage, first, j; | ||
617 | printk("%8.8lx-%8.8lx ", | ||
618 | res->Segs[i].BasePage*ps, | ||
619 | (res->Segs[i].PageCount+res->Segs[i].BasePage)*ps-1); | ||
620 | for(j=15, first=1; j>=0; j--) { | ||
621 | if (mask&(1<<j)) { | ||
622 | if (first) first=0; | ||
623 | else printk(", "); | ||
624 | printk("%s", Usage[j]); | ||
625 | } | ||
626 | } | ||
627 | printk("\n"); | ||
628 | } | ||
629 | } | ||
630 | |||
631 | /* | ||
632 | * Spit out some info about residual data | ||
633 | */ | ||
634 | void print_residual_device_info(void) | ||
635 | { | ||
636 | int i; | ||
637 | union _PnP_TAG_PACKET *pkt; | ||
638 | PPC_DEVICE *dev; | ||
639 | #define did dev->DeviceId | ||
640 | |||
641 | /* make sure we have residual data first */ | ||
642 | if (!have_residual_data) | ||
643 | return; | ||
644 | printk("Residual: %ld devices\n", res->ActualNumDevices); | ||
645 | for ( i = 0; | ||
646 | i < res->ActualNumDevices ; | ||
647 | i++) | ||
648 | { | ||
649 | dev = &res->Devices[i]; | ||
650 | /* | ||
651 | * pci devices | ||
652 | */ | ||
653 | if ( did.BusId & PCIDEVICE ) | ||
654 | { | ||
655 | printk("PCI Device:"); | ||
656 | /* unknown vendor */ | ||
657 | if ( !strncmp( "Unknown", pci_strvendor(did.DevId>>16), 7) ) | ||
658 | printk(" id %08lx types %d/%d", did.DevId, | ||
659 | did.BaseType, did.SubType); | ||
660 | /* known vendor */ | ||
661 | else | ||
662 | printk(" %s %s", | ||
663 | pci_strvendor(did.DevId>>16), | ||
664 | pci_strdev(did.DevId>>16, | ||
665 | did.DevId&0xffff) | ||
666 | ); | ||
667 | |||
668 | if ( did.BusId & PNPISADEVICE ) | ||
669 | { | ||
670 | printk(" pnp:"); | ||
671 | /* get pnp info on the device */ | ||
672 | pkt = (union _PnP_TAG_PACKET *) | ||
673 | &res->DevicePnPHeap[dev->AllocatedOffset]; | ||
674 | for (; pkt->S1_Pack.Tag != DF_END_TAG; | ||
675 | pkt++ ) | ||
676 | { | ||
677 | if ( (pkt->S1_Pack.Tag == S4_Packet) || | ||
678 | (pkt->S1_Pack.Tag == S4_Packet_flags) ) | ||
679 | printk(" irq %02x%02x", | ||
680 | pkt->S4_Pack.IRQMask[0], | ||
681 | pkt->S4_Pack.IRQMask[1]); | ||
682 | } | ||
683 | } | ||
684 | printk("\n"); | ||
685 | continue; | ||
686 | } | ||
687 | /* | ||
688 | * isa devices | ||
689 | */ | ||
690 | if ( did.BusId & ISADEVICE ) | ||
691 | { | ||
692 | printk("ISA Device: basetype: %d subtype: %d", | ||
693 | did.BaseType, did.SubType); | ||
694 | printk("\n"); | ||
695 | continue; | ||
696 | } | ||
697 | /* | ||
698 | * eisa devices | ||
699 | */ | ||
700 | if ( did.BusId & EISADEVICE ) | ||
701 | { | ||
702 | printk("EISA Device: basetype: %d subtype: %d", | ||
703 | did.BaseType, did.SubType); | ||
704 | printk("\n"); | ||
705 | continue; | ||
706 | } | ||
707 | /* | ||
708 | * proc bus devices | ||
709 | */ | ||
710 | if ( did.BusId & PROCESSORDEVICE ) | ||
711 | { | ||
712 | printk("ProcBus Device: basetype: %d subtype: %d", | ||
713 | did.BaseType, did.SubType); | ||
714 | printk("\n"); | ||
715 | continue; | ||
716 | } | ||
717 | /* | ||
718 | * pcmcia devices | ||
719 | */ | ||
720 | if ( did.BusId & PCMCIADEVICE ) | ||
721 | { | ||
722 | printk("PCMCIA Device: basetype: %d subtype: %d", | ||
723 | did.BaseType, did.SubType); | ||
724 | printk("\n"); | ||
725 | continue; | ||
726 | } | ||
727 | printk("Unknown bus access device: busid %lx\n", | ||
728 | did.BusId); | ||
729 | } | ||
730 | } | ||
731 | #endif | ||
732 | |||
733 | /* Returns the device index in the residual data, | ||
734 | any of the search items may be set as -1 for wildcard, | ||
735 | DevID number field (second halfword) is big endian ! | ||
736 | |||
737 | Examples: | ||
738 | - search for the Interrupt controller (8259 type), 2 methods: | ||
739 | 1) i8259 = residual_find_device(~0, | ||
740 | NULL, | ||
741 | SystemPeripheral, | ||
742 | ProgrammableInterruptController, | ||
743 | ISA_PIC, | ||
744 | 0); | ||
745 | 2) i8259 = residual_find_device(~0, "PNP0000", -1, -1, -1, 0) | ||
746 | |||
747 | - search for the first two serial devices, whatever their type) | ||
748 | iserial1 = residual_find_device(~0,NULL, | ||
749 | CommunicationsDevice, | ||
750 | RS232Device, | ||
751 | -1, 0) | ||
752 | iserial2 = residual_find_device(~0,NULL, | ||
753 | CommunicationsDevice, | ||
754 | RS232Device, | ||
755 | -1, 1) | ||
756 | - but search for typical COM1 and COM2 is not easy due to the | ||
757 | fact that the interface may be anything and the name "PNP0500" or | ||
758 | "PNP0501". Quite bad. | ||
759 | |||
760 | */ | ||
761 | |||
762 | /* devid are easier to uncompress than to compress, so to minimize bloat | ||
763 | in this rarely used area we unencode and compare */ | ||
764 | |||
765 | /* in residual data number is big endian in the device table and | ||
766 | little endian in the heap, so we use two parameters to avoid writing | ||
767 | two very similar functions */ | ||
768 | |||
769 | static int __init same_DevID(unsigned short vendor, | ||
770 | unsigned short Number, | ||
771 | char * str) | ||
772 | { | ||
773 | static unsigned const char hexdigit[]="0123456789ABCDEF"; | ||
774 | if (strlen(str)!=7) return 0; | ||
775 | if ( ( ((vendor>>10)&0x1f)+'A'-1 == str[0]) && | ||
776 | ( ((vendor>>5)&0x1f)+'A'-1 == str[1]) && | ||
777 | ( (vendor&0x1f)+'A'-1 == str[2]) && | ||
778 | (hexdigit[(Number>>12)&0x0f] == str[3]) && | ||
779 | (hexdigit[(Number>>8)&0x0f] == str[4]) && | ||
780 | (hexdigit[(Number>>4)&0x0f] == str[5]) && | ||
781 | (hexdigit[Number&0x0f] == str[6]) ) return 1; | ||
782 | return 0; | ||
783 | } | ||
784 | |||
785 | PPC_DEVICE __init *residual_find_device(unsigned long BusMask, | ||
786 | unsigned char * DevID, | ||
787 | int BaseType, | ||
788 | int SubType, | ||
789 | int Interface, | ||
790 | int n) | ||
791 | { | ||
792 | int i; | ||
793 | if (!have_residual_data) return NULL; | ||
794 | for (i=0; i<res->ActualNumDevices; i++) { | ||
795 | #define Dev res->Devices[i].DeviceId | ||
796 | if ( (Dev.BusId&BusMask) && | ||
797 | (BaseType==-1 || Dev.BaseType==BaseType) && | ||
798 | (SubType==-1 || Dev.SubType==SubType) && | ||
799 | (Interface==-1 || Dev.Interface==Interface) && | ||
800 | (DevID==NULL || same_DevID((Dev.DevId>>16)&0xffff, | ||
801 | Dev.DevId&0xffff, DevID)) && | ||
802 | !(n--) ) return res->Devices+i; | ||
803 | #undef Dev | ||
804 | } | ||
805 | return NULL; | ||
806 | } | ||
807 | |||
808 | PPC_DEVICE __init *residual_find_device_id(unsigned long BusMask, | ||
809 | unsigned short DevID, | ||
810 | int BaseType, | ||
811 | int SubType, | ||
812 | int Interface, | ||
813 | int n) | ||
814 | { | ||
815 | int i; | ||
816 | if (!have_residual_data) return NULL; | ||
817 | for (i=0; i<res->ActualNumDevices; i++) { | ||
818 | #define Dev res->Devices[i].DeviceId | ||
819 | if ( (Dev.BusId&BusMask) && | ||
820 | (BaseType==-1 || Dev.BaseType==BaseType) && | ||
821 | (SubType==-1 || Dev.SubType==SubType) && | ||
822 | (Interface==-1 || Dev.Interface==Interface) && | ||
823 | (DevID==0xffff || (Dev.DevId&0xffff) == DevID) && | ||
824 | !(n--) ) return res->Devices+i; | ||
825 | #undef Dev | ||
826 | } | ||
827 | return NULL; | ||
828 | } | ||
829 | |||
830 | static int __init | ||
831 | residual_scan_pcibridge(PnP_TAG_PACKET * pkt, struct pci_dev *dev) | ||
832 | { | ||
833 | int irq = -1; | ||
834 | |||
835 | #define data pkt->L4_Pack.L4_Data.L4_PPCPack.PPCData | ||
836 | if (dev->bus->number == data[16]) { | ||
837 | int i, size; | ||
838 | |||
839 | size = 3 + ld_le16((u_short *) (&pkt->L4_Pack.Count0)); | ||
840 | for (i = 20; i < size - 4; i += 12) { | ||
841 | unsigned char pin; | ||
842 | int line_irq; | ||
843 | |||
844 | if (dev->devfn != data[i + 1]) | ||
845 | continue; | ||
846 | |||
847 | pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin); | ||
848 | if (pin) { | ||
849 | line_irq = ld_le16((unsigned short *) | ||
850 | (&data[i + 4 + 2 * (pin - 1)])); | ||
851 | irq = (line_irq == 0xffff) ? 0 | ||
852 | : line_irq & 0x7fff; | ||
853 | } else | ||
854 | irq = 0; | ||
855 | |||
856 | break; | ||
857 | } | ||
858 | } | ||
859 | #undef data | ||
860 | |||
861 | return irq; | ||
862 | } | ||
863 | |||
864 | int __init | ||
865 | residual_pcidev_irq(struct pci_dev *dev) | ||
866 | { | ||
867 | int i = 0; | ||
868 | int irq = -1; | ||
869 | PPC_DEVICE *bridge; | ||
870 | |||
871 | while ((bridge = residual_find_device | ||
872 | (-1, NULL, BridgeController, PCIBridge, -1, i++))) { | ||
873 | |||
874 | PnP_TAG_PACKET *pkt; | ||
875 | if (bridge->AllocatedOffset) { | ||
876 | pkt = PnP_find_large_vendor_packet(res->DevicePnPHeap + | ||
877 | bridge->AllocatedOffset, 3, 0); | ||
878 | if (!pkt) | ||
879 | continue; | ||
880 | |||
881 | irq = residual_scan_pcibridge(pkt, dev); | ||
882 | if (irq != -1) | ||
883 | break; | ||
884 | } | ||
885 | } | ||
886 | |||
887 | return (irq < 0) ? 0 : irq; | ||
888 | } | ||
889 | |||
890 | void __init residual_irq_mask(char *irq_edge_mask_lo, char *irq_edge_mask_hi) | ||
891 | { | ||
892 | PPC_DEVICE *dev; | ||
893 | int i = 0; | ||
894 | unsigned short irq_mask = 0x000; /* default to edge */ | ||
895 | |||
896 | while ((dev = residual_find_device(-1, NULL, -1, -1, -1, i++))) { | ||
897 | PnP_TAG_PACKET *pkt; | ||
898 | unsigned short mask; | ||
899 | int size; | ||
900 | int offset = dev->AllocatedOffset; | ||
901 | |||
902 | if (!offset) | ||
903 | continue; | ||
904 | |||
905 | pkt = PnP_find_packet(res->DevicePnPHeap + offset, | ||
906 | IRQFormat, 0); | ||
907 | if (!pkt) | ||
908 | continue; | ||
909 | |||
910 | size = tag_small_count(pkt->S1_Pack.Tag) + 1; | ||
911 | mask = ld_le16((unsigned short *)pkt->S4_Pack.IRQMask); | ||
912 | if (size > 3 && (pkt->S4_Pack.IRQInfo & 0x0c)) | ||
913 | irq_mask |= mask; | ||
914 | } | ||
915 | |||
916 | *irq_edge_mask_lo = irq_mask & 0xff; | ||
917 | *irq_edge_mask_hi = irq_mask >> 8; | ||
918 | } | ||
919 | |||
920 | unsigned int __init residual_isapic_addr(void) | ||
921 | { | ||
922 | PPC_DEVICE *isapic; | ||
923 | PnP_TAG_PACKET *pkt; | ||
924 | unsigned int addr; | ||
925 | |||
926 | isapic = residual_find_device(~0, NULL, SystemPeripheral, | ||
927 | ProgrammableInterruptController, | ||
928 | ISA_PIC, 0); | ||
929 | if (!isapic) | ||
930 | goto unknown; | ||
931 | |||
932 | pkt = PnP_find_large_vendor_packet(res->DevicePnPHeap + | ||
933 | isapic->AllocatedOffset, 9, 0); | ||
934 | if (!pkt) | ||
935 | goto unknown; | ||
936 | |||
937 | #define p pkt->L4_Pack.L4_Data.L4_PPCPack | ||
938 | /* Must be 32-bit system address */ | ||
939 | if (!((p.PPCData[0] == 3) && (p.PPCData[1] == 32))) | ||
940 | goto unknown; | ||
941 | |||
942 | /* It doesn't seem to work where length != 1 (what can I say? :-/ ) */ | ||
943 | if (ld_le32((unsigned int *)(p.PPCData + 12)) != 1) | ||
944 | goto unknown; | ||
945 | |||
946 | addr = ld_le32((unsigned int *) (p.PPCData + 4)); | ||
947 | #undef p | ||
948 | return addr; | ||
949 | unknown: | ||
950 | return 0; | ||
951 | } | ||
952 | |||
953 | PnP_TAG_PACKET *PnP_find_packet(unsigned char *p, | ||
954 | unsigned packet_tag, | ||
955 | int n) | ||
956 | { | ||
957 | unsigned mask, masked_tag, size; | ||
958 | if(!p) return NULL; | ||
959 | if (tag_type(packet_tag)) mask=0xff; else mask=0xF8; | ||
960 | masked_tag = packet_tag&mask; | ||
961 | for(; *p != END_TAG; p+=size) { | ||
962 | if ((*p & mask) == masked_tag && !(n--)) | ||
963 | return (PnP_TAG_PACKET *) p; | ||
964 | if (tag_type(*p)) | ||
965 | size=ld_le16((unsigned short *)(p+1))+3; | ||
966 | else | ||
967 | size=tag_small_count(*p)+1; | ||
968 | } | ||
969 | return NULL; /* not found */ | ||
970 | } | ||
971 | |||
972 | PnP_TAG_PACKET __init *PnP_find_small_vendor_packet(unsigned char *p, | ||
973 | unsigned packet_type, | ||
974 | int n) | ||
975 | { | ||
976 | int next=0; | ||
977 | while (p) { | ||
978 | p = (unsigned char *) PnP_find_packet(p, 0x70, next); | ||
979 | if (p && p[1]==packet_type && !(n--)) | ||
980 | return (PnP_TAG_PACKET *) p; | ||
981 | next = 1; | ||
982 | }; | ||
983 | return NULL; /* not found */ | ||
984 | } | ||
985 | |||
986 | PnP_TAG_PACKET __init *PnP_find_large_vendor_packet(unsigned char *p, | ||
987 | unsigned packet_type, | ||
988 | int n) | ||
989 | { | ||
990 | int next=0; | ||
991 | while (p) { | ||
992 | p = (unsigned char *) PnP_find_packet(p, 0x84, next); | ||
993 | if (p && p[3]==packet_type && !(n--)) | ||
994 | return (PnP_TAG_PACKET *) p; | ||
995 | next = 1; | ||
996 | }; | ||
997 | return NULL; /* not found */ | ||
998 | } | ||
999 | |||
1000 | #ifdef CONFIG_PROC_PREPRESIDUAL | ||
1001 | static int proc_prep_residual_read(char * buf, char ** start, off_t off, | ||
1002 | int count, int *eof, void *data) | ||
1003 | { | ||
1004 | int n; | ||
1005 | |||
1006 | n = res->ResidualLength - off; | ||
1007 | if (n < 0) { | ||
1008 | *eof = 1; | ||
1009 | n = 0; | ||
1010 | } | ||
1011 | else { | ||
1012 | if (n > count) | ||
1013 | n = count; | ||
1014 | else | ||
1015 | *eof = 1; | ||
1016 | |||
1017 | memcpy(buf, (char *)res + off, n); | ||
1018 | *start = buf; | ||
1019 | } | ||
1020 | |||
1021 | return n; | ||
1022 | } | ||
1023 | |||
1024 | int __init | ||
1025 | proc_prep_residual_init(void) | ||
1026 | { | ||
1027 | if (have_residual_data) | ||
1028 | create_proc_read_entry("residual", S_IRUGO, NULL, | ||
1029 | proc_prep_residual_read, NULL); | ||
1030 | return 0; | ||
1031 | } | ||
1032 | |||
1033 | __initcall(proc_prep_residual_init); | ||
1034 | #endif | ||