From f0d334292dce3efa9be5f750d94f4c7953d972c8 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Thu, 7 Nov 2002 15:24:20 +0000 Subject: [PATCH] Upgrade dmidecode to version 1.8 git-svn-id: http://lm-sensors.org/svn/lm-sensors/trunk@1583 7894878c-1315-0410-8ee3-d5d059ff63e0 --- CHANGES | 2 +- prog/detect/dmidecode.c | 635 +++++++++++++++++++++++++++++++++++----- 2 files changed, 565 insertions(+), 72 deletions(-) diff --git a/CHANGES b/CHANGES index 365e8ca4..55ceb992 100644 --- a/CHANGES +++ b/CHANGES @@ -40,7 +40,7 @@ ask CVS about it: Module lm87: Add multiple VRM version support Module w83781d: Fix in0/in1 initialization Module smartbatt: New - Program dmidecode: Fix read bug + Program dmidecode: Fix read bug; upgrade to version 1.8 Program doc-insmod: Complete rewrite; fix author output Program doc-features: Complete rewrite; handle missing sysctl entries; handle unknown prefix with suggestion diff --git a/prog/detect/dmidecode.c b/prog/detect/dmidecode.c index e372e14c..ff4440dc 100644 --- a/prog/detect/dmidecode.c +++ b/prog/detect/dmidecode.c @@ -1,7 +1,7 @@ /* - * DMI decode rev 1.4 + * DMI decode rev 1.8 * - * (C) 2000,2001 Alan Cox + * (C) 2000-2002 Alan Cox * * 2-July-2001 Matt Domsch * Additional structures displayed per SMBIOS 2.3.1 spec @@ -33,10 +33,89 @@ * C++ style comments removed * Commented out code removed * DMI 0.0 case handled - * Fix return value in dmi_port_type and dmi_port_connector_type + * Fix return value of dmi_port_type and dmi_port_connector_type * - * Licensed under the GNU Public license. If you want to use it in with - * another license just ask. + * 23-August-2002 Alan Cox + * Make the code pass -Wall -pedantic by fixing a few harmless sign of + * pointer mismatches + * Correct main prototype + * Check for compilers with wrong type sizes + * + * 17-Sep-2002 Larry Lile + * Type 16 & 17 structures displayed per SMBIOS 2.3.1 spec + * + * 20-September-2002 Dave Johnson + * Fix comparisons in dmi_bus_name + * Fix comparison in dmi_processor_type + * Fix bitmasking in dmi_onboard_type + * Fix return value of dmi_temp_loc + * + * 28-September-2002 Jean Delvare + * Fix missing coma in dmi_bus_name + * Remove unwanted bitmaskings in dmi_mgmt_dev_type, dmi_mgmt_addr_type, + * dmi_fan_type, dmi_volt_loc, dmi_temp_loc and dmi_status + * Fix DMI table read bug ("dmi: read: Success") + * Make the code pass -W again + * Fix return value of dmi_card_size + * + * 05-October-2002 Jean Delvare + * More ACPI decoded + * More PNP decoded + * More SYSID decoded + * PCI Interrupt Routing decoded + * BIOS32 Service Directory decoded + * Sony system detection (unconfirmed) + * Checksums verified whenever possible + * Better checks on file read and close + * Define VERSION and display version at beginning + * More secure decoding (won't run off the table in any case) + * Do not try to decode more structures than announced + * Fix an off-by-one error that caused the last address being + * scanned to be 0x100000, not 0xFFFF0 as it should + * + * 10-October-2002 Jean Delvare + * Remove extra semicolon at the end of dmi_memory_array_use + * Fix compilation warnings + * Add missing backslash in DMI case 37 + * Fix BIOS ROM size (DMI case 0) + * + * 12-October-2002 Jean Delvare + * Fix maximum cache size and installed size being inverted + * Fix typos in port types + * + * 14-October-2002 Jean Delvare + * Fix typo in dmi_memory_array_location + * Replace Kbyte by kB in DMI case 16 + * Add DDR entry in dmi_memory_device_type + * Fix extra s in SYSIS + * + * 15-October-2002 Jean Delvare + * Fix bad index in DMI case 27 (cooling device) + * + * Licensed under the GNU Public license. If you want to use it in with + * another license just ask. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * For the avoidance of doubt the "preferred form" of this code is one which + * is in an open unpatent encumbered format. Where cryptographic key signing + * forms part of the process of creating an executable the information + * including keys needed to generate an equivalently functional executable + * are deemed to be part of the source code. + * [In the light of TCPA and Palladium the author urges + * other open source authors to add such a clarification] */ #include @@ -45,6 +124,8 @@ #include #include +#define VERSION "1.8" + typedef unsigned char u8; typedef unsigned short u16; typedef unsigned int u32; @@ -52,7 +133,7 @@ typedef unsigned int u32; static void dump_raw_data(void *data, unsigned int length) { - unsigned char buffer1[80], buffer2[80], *b1, *b2, c; + char buffer1[80], buffer2[80], *b1, *b2, c; unsigned char *p = data; unsigned long column=0; unsigned int length_printed = 0; @@ -91,18 +172,21 @@ struct dmi_header u16 handle; }; -static char *dmi_string(struct dmi_header *dm, u8 s) +static const char *dmi_string(struct dmi_header *dm, u8 s) { - u8 *bp=(u8 *)dm; - if (!s) return NULL; + char *bp=(char *)dm; + if(s==0) + return ""; bp+=dm->length; - while(s>1) + while(s>1 && *bp) { bp+=strlen(bp); bp++; s--; } + if(!*bp) + return ""; return bp; } @@ -168,7 +252,7 @@ const char *dmi_bus_name(u8 num) "MCA ", "EISA ", "PCI ", - "PCMCIA " + "PCMCIA ", "VLB ", "Proprietary ", "CPU Slot ", @@ -188,9 +272,9 @@ const char *dmi_bus_name(u8 num) "PC98/Card" }; - if(num<=0x12) + if(num<=0x11) return bus[num]; - if(num>=0xA0 && num<0xA5) + if(num>=0xA0 && num<=0xA4) return jpbus[num - 0xA0]; return ""; } @@ -214,9 +298,9 @@ const char *dmi_bus_width(u8 code) const char *dmi_card_size(u8 v) { - if(v==2) - return("Short "); if(v==3) + return("Short "); + if(v==4) return("Long "); return ""; } @@ -334,6 +418,149 @@ const char *dmi_port_connector_type(u8 code) return ""; } +static const char *dmi_memory_array_location(u8 num) +{ + static const char *memory_array_location[]={ + "", + "Other", + "Unknown", + "System board or motherboard", + "ISA add-on card", + "EISA add-on card", + "PCI add-on card", + "MCA add-on card", + "PCMCIA add-on card", + "Proprietary add-on card", + "NuBus", + }; + static const char *jp_memory_array_location[]={ + "PC-98/C20 add-on card", + "PC-98/C24 add-on card", + "PC-98/E add-on card", + "PC-98/Local bus add-on card", + }; + if(num<=0x0A) + return memory_array_location[num]; + if(num>=0xA0 && num<0xA3) + return jp_memory_array_location[num]; + return ""; +} + +static const char *dmi_memory_array_use(u8 num) +{ + static const char *memory_array_use[]={ + "", + "Other", + "Unknown", + "System memory", + "Video memory", + "Flash memory", + "Non-volatile RAM", + "Cache memory", + }; + if (num > 0x07) + return ""; + return memory_array_use[num]; +} + +static const char *dmi_memory_array_error_correction_type(u8 num) +{ + static const char *memory_array_error_correction_type[]={ + "", + "Other", + "Unknown", + "None", + "Parity", + "Single-bit ECC", + "Multi-bit ECC", + "CRC", + }; + if (num > 0x07) + return ""; + return memory_array_error_correction_type[num]; +} + +static const char *dmi_memory_device_form_factor(u8 num) +{ + static const char *memory_device_form_factor[]={ + "", + "Other", + "Unknown", + "SIMM", + "SIP", + "Chip", + "DIP", + "ZIP", + "Proprietary Card", + "DIMM", + "TSOP", + "Row of chips", + "RIMM", + "SODIMM", + "SRIMM", + }; + if (num > 0x0E) + return ""; + return memory_device_form_factor[num]; +} + +static const char *dmi_memory_device_type(u8 num) +{ + static const char *memory_device_type[]={ + "", + "Other", + "Unknown", + "DRAM", + "EDRAM", + "VRAM", + "SRAM", + "RAM", + "ROM", + "FLASH", + "EEPROM", + "FEPROM", + "EPROM", + "CDRAM", + "3DRAM", + "SDRAM", + "SGRAM", + "RDRAM", + "DDR" + }; + if (num > 0x12) + return ""; + return memory_device_type[num]; +} + +static void dmi_memory_device_detail(u8 v) +{ + printf("\t\tType Detail: "); + if (v&(1<<1)) + printf("Other "); + if (v&(1<<2)) + printf("Unknown "); + if (v&(1<<3)) + printf("Fast-paged "); + if (v&(1<<4)) + printf("Static column "); + if (v&(1<<5)) + printf("Pseudo-static "); + if (v&(1<<6)) + printf("RAMBUS "); + if (v&(1<<7)) + printf("Synchronous "); + if (v&(1<<8)) + printf("CMOS "); + if (v&(1<<9)) + printf("EDO "); + if (v&(1<<10)) + printf("Window DRAM "); + if (v&(1<<11)) + printf("Cache DRAM "); + if (v&(1<<12)) + printf("Non-volatile "); + printf("\n"); +} const char *dmi_port_type(u8 code) { @@ -346,8 +573,8 @@ const char *dmi_port_type(u8 code) "Parallel Port ECP/EPP", "Serial Port XT/AT Compatible", "Serial Port 16450 Compatible", - "Serial Port 16650 Compatible", - "Serial Port 16650A Compatible", + "Serial Port 16550 Compatible", + "Serial Port 16550A Compatible", "SCSI Port", "MIDI Port", "Joy Stick Port", @@ -401,7 +628,7 @@ const char *dmi_processor_type(u8 code) if(code == 0xFF) return "Other"; - if (code > 0xA1) + if (code > 6) return ""; return processor_type[code]; } @@ -458,7 +685,7 @@ const char *dmi_onboard_type(u8 code) "Token Ring", "Sound", }; - code &= 0x80; + code &= ~0x80; if (code > 7) return ""; return onboard_type[code]; @@ -482,7 +709,7 @@ const char *dmi_mgmt_dev_type(u8 code) "W83781D", "HT82H791", }; - code &= 0x80; + if (code > 0x0d) return ""; return type[code]; @@ -498,7 +725,7 @@ const char *dmi_mgmt_addr_type(u8 code) "Memory", "SMBus", }; - code &= 0x80; + if (code > 5) return ""; return type[code]; @@ -526,7 +753,7 @@ const char *dmi_fan_type(u8 code) "Active Cooling", "Passive Cooling", }; - code &= 0x80; + if (code > 0x11) return ""; return type[code]; @@ -548,7 +775,7 @@ const char *dmi_volt_loc(u8 code) "Power Unit", "Add-in Card", }; - code &= 0x80; + if (code > 0x0b) return ""; return type[code]; @@ -562,10 +789,12 @@ const char *dmi_temp_loc(u8 code) "Power System Board", "Drive Back Plane", }; - code &= 0x80; + if (code <= 0x0b) return dmi_volt_loc(code); - return type[code - 0x0c]; + if (code <= 0x0f) + return type[code - 0x0c]; + return ""; } const char *dmi_status(u8 code) @@ -579,7 +808,7 @@ const char *dmi_status(u8 code) "Critical", "Non-recoverable", }; - code &= 0x80; + if (code > 6) return ""; return type[code]; @@ -640,9 +869,9 @@ const char *dmi_speed(u8 *data, int indx) static void dmi_table(int fd, u32 base, int len, int num) { - char *buf=malloc(len); + unsigned char *buf=malloc(len); struct dmi_header *dm; - u8 *data; + u8 *data, *next; int i=0; int r=0, r2=0; @@ -662,15 +891,20 @@ static void dmi_table(int fd, u32 base, int len, int num) perror("dmi: lseek"); return; } - while(r2!=len && (r=read(fd, buf+r2, len-r2))!=0) + while(r2!=len && (r=read(fd, buf+r2, len-r2))!=0 && r!=-1) r2+=r; - if(r==0) + if(r==-1) { perror("dmi: read"); return; } + if(r==0) + { + fputs("dmi: read: Unexpected end of file\n", stderr); + return; + } data = buf; - while(data+sizeof(struct dmi_header)<=(u8*)buf+len) + while(itype, dm->length); /* we won't read beyond allocated memory */ - if(data+dm->length>(u8*)buf+len) + next=data+dm->length; + while(next-buf+1len) { printf("\tIncomplete structure, abort decoding.\n"); break; @@ -698,7 +936,7 @@ static void dmi_table(int fd, u32 base, int len, int num) printf("\t\tBIOS base: 0x%04X0\n", data[7]<<8|data[6]); printf("\t\tROM size: %dK\n", - 64*data[9]); + 64*(data[9]+1)); printf("\t\tCapabilities:\n"); u=data[13]<<24|data[12]<<16|data[11]<<8|data[10]; u2=data[17]<<24|data[16]<<16|data[15]<<8|data[14]; @@ -853,9 +1091,9 @@ static void dmi_table(int fd, u32 base, int len, int num) else printf("disabled\n"); printf("\t\tL%d Cache Size: ", 1+(u&7)); - dmi_cache_size(data[7]|data[8]<<8); - printf("\t\tL%d Cache Maximum: ", 1+(u&7)); dmi_cache_size(data[9]|data[10]<<8); + printf("\t\tL%d Cache Maximum: ", 1+(u&7)); + dmi_cache_size(data[7]|data[8]<<8); printf("\t\tL%d Cache Type: ", 1+(u&7)); dmi_decode_cache(data[13]); printf("\n"); @@ -959,9 +1197,97 @@ static void dmi_table(int fd, u32 base, int len, int num) case 16: printf("\tPhysical Memory Array\n"); + printf("\t\tLocation: %s\n", + dmi_memory_array_location(data[4])); + printf("\t\tUse: %s\n", + dmi_memory_array_use(data[5])); + printf("\t\tError Correction Type: %s\n", + dmi_memory_array_error_correction_type(data[6])); + u2 = data[10]<<24|data[9]<<16|data[8]<<8|data[7]; + printf("\t\tMaximum Capacity: "); + if (u2 == 0x80000000) + printf("Unknown\n"); + else + printf("%u kB\n", u2); + printf("\t\tError Information Handle: "); + u = data[12]<<8|data[11]; + if (u == 0xffff) { + printf("None\n"); + } else if (u == 0xfffe) { + printf("Not Provided\n"); + } else { + printf("0x%04X\n", u); + } + printf("\t\tNumber of Devices: %u\n", data[14]<<8|data[13]); break; case 17: printf("\tMemory Device\n"); + printf("\t\tArray Handle: 0x%04X\n", data[5]<<8|data[4]); + printf("\t\tError Information Handle: "); + u = data[7]<<8|data[6]; + if (u == 0xffff) { + printf("None\n"); + } else if (u == 0xfffe) { + printf("Not Provided\n"); + } else { + printf("0x%04X\n", u); + } + u = data[9]<<8|data[8]; + printf("\t\tTotal Width: "); + if (u == 0xffff) + printf("Unknown\n"); + else + printf("%u bits\n", u); + u = data[11]<<8|data[10]; + printf("\t\tData Width: "); + if (u == 0xffff) + printf("Unknown\n"); + else + printf("%u bits\n", u); + u = data[13]<<8|data[12]; + printf("\t\tSize: "); + if (u == 0xffff) + printf("Unknown\n"); + else + printf("%u %sbyte\n", (u&0x7fff), (u&0x8000) ? "K" : "M"); + printf("\t\tForm Factor: %s\n", + dmi_memory_device_form_factor(data[14])); + if (data[15] != 0) { + printf("\t\tSet: "); + if (data[15] == 0xff) + printf("Unknown\n"); + else + printf("0x%02X\n", data[15]); + } + printf("\t\tLocator: %s\n", + dmi_string(dm, data[16])); + printf("\t\tBank Locator: %s\n", + dmi_string(dm, data[17])); + printf("\t\tType: %s\n", + dmi_memory_device_type(data[18])); + u = data[20]<<8|data[19]; + if (u&0x1ffe) + dmi_memory_device_detail(u); + if (dm->length > 21) { + u = data[22]<<8|data[21]; + printf("\t\tSpeed: "); + if (u == 0) + printf("Unknown\n"); + else + printf("%u MHz (%.1f ns)\n", u, (1000.0/u)); + } + if (dm->length > 23) + printf("\t\tManufacturer: %s\n", + dmi_string(dm, data[23])); + if (dm->length > 24) + printf("\t\tSerial Number: %s\n", + dmi_string(dm, data[24])); + if (dm->length > 25) + printf("\t\tAsset Tag: %s\n", + dmi_string(dm, data[25])); + if (dm->length > 26) + printf("\t\tPart Number: %s\n", + dmi_string(dm, data[26])); break; case 18: printf("\t32-bit Memory Error Information\n"); @@ -1023,9 +1349,9 @@ static void dmi_table(int fd, u32 base, int len, int num) case 27: printf("\tCooling Device\n"); printf("\t\tDevice Type: %s\n", - dmi_fan_type(data[5] & 0x1f)); + dmi_fan_type(data[6] & 0x1f)); printf("\t\tDevice Status: %s\n", - dmi_status(data[5] >> 5)); + dmi_status(data[6] >> 5)); if(dm->length > 0x0c) printf("\t\tNominal Speed: %s\n", dmi_speed(data, 0x0c)); @@ -1112,7 +1438,7 @@ static void dmi_table(int fd, u32 base, int len, int num) dump_raw_data(data+4, dm->length-4); break; case 37: - printf("\tMemory Channeln"); + printf("\tMemory Channel\n"); break; case 38: printf("\tIPMI Device\n"); @@ -1140,30 +1466,88 @@ static void dmi_table(int fd, u32 base, int len, int num) } - data+=dm->length; - while(*data || data[1]) - data++; - data+=2; + data=next; i++; } if(i!=num) { - printf("Wrong DMI structures count: %d announced, %d decoded.\n", num, i); + printf("Wrong DMI structures count: %d announced, only %d decoded.\n", + num, i); } if(data-(u8*)buf!=len) { - printf("Wrong DMI structures length: %d bytes announced, %d bytes decoded.\n", len, data-(u8*)buf); + printf("Wrong DMI structures length: %d bytes announced, %d bytes decoded.\n", + len, data-(u8*)buf); } free(buf); } -int main(void) +static const char *acpi_version(u8 code) { - unsigned char buf[20]; + static const char *version[]={ + " 1.0", + "", + " 2.0" + }; + + if(code<=2) + return version[code]; + return ""; +} + + +static const char *pnp_event_notification(u8 code) +{ + static const char *notification[]={ + "Not Supported", + "Polling", + "Asynchronous" + }; + + if(code<=2) + return notification[code]; + return ""; +} + + +static void pir_exclusive_irqs(u16 code) +{ + if(code==0) + printf(" None"); + else + { + u8 a; + for(a=0; a<16; a++) + if(code&(1<1?"s":""); + printf("SYSID table at 0x%08X.\n", + *(u32 *)(buf+10)); + } + else + lseek(fd, -16, SEEK_CUR); + } + else if(memcmp(buf, "_DMI_", 5)==0 && checksum(buf, 15)) { u16 num=buf[13]<<8|buf[12]; u16 len=buf[7]<<8|buf[6]; u32 base=buf[11]<<24|buf[10]<<16|buf[9]<<8|buf[8]; + /* if version is 0.0, the real version is taken from _SM_ above */ printf("DMI %d.%d present.\n", buf[14]?buf[14]>>4:smmajver, buf[14]?buf[14]&0x0F:smminver); printf("%d structures occupying %d bytes.\n", @@ -1205,28 +1615,111 @@ int main(void) lseek(fd, fp+16, SEEK_SET); } else if(memcmp(buf, "$PnP", 4)==0) - printf("PNP BIOS present.\n"); + { + /* complete buffer, compute checksum */ + read(fd, buf+16, 32); /* arbitrary, at least 1 */ + if(buf[5]<=48 && checksum(buf, buf[5])) + { + fp+=32; + printf("PNP %u.%u present.\n", + buf[4]>>4, buf[4]&0x0F); + printf("\tEvent Notification: %s\n", + pnp_event_notification((*(u16 *)(buf+6))&0x03)); + if(((*(u16 *)(buf+6))&0x03)==1) + printf("\tEvent Notification Flag Address: 0x%08X\n", + *(u32 *)(buf+9)); + printf("\tReal Mode Code Address: %04X:%04X\n", + *(u16 *)(buf+0x0F), *(u16 *)(buf+0x0D)); + printf("\tReal Mode Data Address: %04X:0000\n", + *(u16 *)(buf+0x1B)); + printf("\tProtected Mode Code Address: 0x%08X\n", + *(u32 *)(buf+0x13)+*(u16 *)(buf+0x11)); + printf("\tProtected Mode Data Address: 0x%08X\n", + *(u32 *)(buf+0x1D)); + if(*(u32 *)(buf+0x17)!=0) + printf("\tOEM Device Identifier: 0x%08X\n", + *(u32 *)(buf+0x17)); + } + else + lseek(fd, -32, SEEK_CUR); + } else if(memcmp(buf, "RSD PTR ", 8)==0) { - int a; - unsigned char sum=0; - printf("RSD PTR found at 0x%lX.\n", fp); - - if(buf[15]!=0) + /* complete buffer, compute checksum */ + read(fd, buf+16, 4); + if(checksum(buf, 20)) { - printf("Reserved check failed.\n"); + lseek(fd, 12, SEEK_CUR); + fp+=16; + printf("ACPI%s present.\n", + acpi_version(buf[15])); + printf("\tOEM ID: "); + fwrite(buf+9, 6, 1, stdout); + printf("\n"); + printf("RSD table at 0x%08X.\n", + *(u32 *)(buf+16)); } - printf("OEM "); - fwrite(buf+9, 6, 1, stdout); - printf("\n"); - read(fd,buf+16,4); - lseek(fd, -4, SEEK_CUR); - for(a=0;a<20;a++) - sum+=buf[a]; - if(sum!=0) - printf("Bad checksum.\n"); + else + lseek(fd, -4, SEEK_CUR); } + else if(memcmp(buf, "$SNY", 4)==0) + { + /* complete buffer, compute checksum */ + read(fd, buf+16, 16); + if(buf[5]<=32 && checksum(buf, buf[5])) + { + fp+=16; + printf("Sony system detected (?).\n"); + } + else + lseek(fd, -16, SEEK_CUR); + } + else if(memcmp(buf, "_32_", 4)==0) + { + /* complete buffer, reposition file, compute checksum */ + read(fd, buf+16, 32); + lseek(fd, -32, SEEK_CUR); + if(buf[9]>=1 && buf[9]<=3 && checksum(buf, buf[9]<<4)) + { + printf("BIOS32 Service Directory present.\n"); + printf("\tCalling Interface Address: 0x%08X\n", + *(u32 *)(buf+4)); + lseek(fd, (buf[9]-1)<<4, SEEK_CUR); + fp+=(buf[9]-1)<<4; + } + } + else if(memcmp(buf, "$PIR", 4)==0) + { + /* complete buffer, reposition file */ + read(fd, buf+16, 16); + lseek(fd, -16, SEEK_CUR); + if(buf[5]!=0 && buf[30]==0) + { + printf("PCI Interrupt Routing %u.%u present.\n", + buf[5], buf[4]); + printf("\tTable Size: %u bytes\n", + *(u16 *)(buf+6)); + printf("\tRouter ID: %02x:%02x.%1x\n", + buf[8], buf[9]>>3, buf[9]&0x07); + printf("\tExclusive IRQs:"); + pir_exclusive_irqs(*(u16 *)(buf+10)); + printf("\n"); + if(*(u32 *)(buf+12)!=0) + printf("\tCompatible Router: %04x:%04x\n", + *(u16 *)(buf+12), *(u16 *)(buf+14)); + if(*(u32 *)(buf+16)!=0) + printf("\tMiniport Data: 0x%08X\n", + *(u32 *)(buf+16)); + } + } + fp+=16; } - close(fd); + + if(close(fd)==-1) + { + perror("/dev/mem"); + exit(1); + } + return 0; }