/ src / core / spd.cc
spd.cc
  1  #include "version.h"
  2  #include "spd.h"
  3  #include "osutils.h"
  4  #include <sys/types.h>
  5  #include <sys/stat.h>
  6  #include <fcntl.h>
  7  #include <unistd.h>
  8  #include <string.h>
  9  #include <string>
 10  #include <dirent.h>
 11  #include <stdio.h>
 12  #include <cstring>
 13  
 14  __ID("@(#) $Id$");
 15  
 16  /* SPD is 2048-bit long */
 17  #define SPD_MAXSIZE (2048/8)
 18  #define SPD_BLKSIZE 0x10
 19  
 20  #define TYPE_EDO  0x02
 21  #define TYPE_SDRAM  0x04
 22  
 23  #define PROCSENSORS "/proc/sys/dev/sensors"
 24  #define EEPROMPREFIX "eeprom-"
 25  
 26  static unsigned char spd[SPD_MAXSIZE];
 27  static bool spd_page_loaded[SPD_MAXSIZE / SPD_BLKSIZE];
 28  static string current_eeprom = "";
 29  static unsigned int current_bank = 0;
 30  
 31  static unsigned char get_spd_byte(unsigned int offset)
 32  {
 33    if ((offset < 0) || (offset >= SPD_MAXSIZE))
 34      return 0;
 35  
 36    if (!spd_page_loaded[offset / SPD_BLKSIZE])
 37    {
 38      char chunkname[10];
 39      string name = "";
 40      FILE *in = NULL;
 41  
 42      snprintf(chunkname, sizeof(chunkname), "%02x",
 43        (offset / SPD_BLKSIZE) * SPD_BLKSIZE);
 44  
 45      name = current_eeprom + "/" + string(chunkname);
 46  
 47      in = fopen(name.c_str(), "r");
 48      if (in)
 49      {
 50        for (int i = 0; i < SPD_BLKSIZE; i++)
 51          if(fscanf(in, "%d",
 52            (int *) &spd[i + (offset / SPD_BLKSIZE) * SPD_BLKSIZE]) < 1)
 53              break;
 54        fclose(in);
 55        spd_page_loaded[offset / SPD_BLKSIZE] = true;
 56      }
 57      else
 58        spd_page_loaded[offset / SPD_BLKSIZE] = false;
 59    }
 60  
 61    return spd[offset];
 62  }
 63  
 64  
 65  static int selecteeprom(const struct dirent *d)
 66  {
 67    struct stat buf;
 68  
 69    if (d->d_name[0] == '.')
 70      return 0;
 71  
 72    if (lstat(d->d_name, &buf) != 0)
 73      return 0;
 74  
 75    if (!S_ISDIR(buf.st_mode))
 76      return 0;
 77  
 78    return (strncmp(d->d_name, EEPROMPREFIX, strlen(EEPROMPREFIX)) == 0);
 79  }
 80  
 81  
 82  static hwNode *get_current_bank(hwNode & memory)
 83  {
 84    char id[20];
 85    hwNode *result = NULL;
 86  
 87    if ((current_bank == 0) && (result = memory.getChild("bank")))
 88      return result;
 89  
 90    snprintf(id, sizeof(id), "bank:%d", current_bank);
 91    result = memory.getChild(id);
 92  
 93    if (!result)
 94      return memory.addChild(hwNode(id, hw::memory));
 95    else
 96      return result;
 97  }
 98  
 99  
100  static bool scan_eeprom(hwNode & memory,
101  string name)
102  {
103    int memory_type = -1;
104    char buff[20];
105    unsigned char checksum = 0;
106    unsigned char rows = 0;
107    unsigned char density = 0;
108    unsigned long long size = 0;
109  
110    current_eeprom = string(PROCSENSORS) + "/" + name;
111    memset(spd, 0, sizeof(spd));
112    memset(spd_page_loaded, 0, sizeof(spd_page_loaded));
113  
114    for (int i = 0; i < 63; i++)
115      checksum += get_spd_byte(i);
116  
117    if (checksum != get_spd_byte(63))
118      return false;
119  
120    memory_type = get_spd_byte(0x02);
121  
122    hwNode *bank = get_current_bank(memory);
123  
124    if (!bank)
125      return false;
126  
127    switch (memory_type)
128    {
129      case TYPE_SDRAM:
130        bank->setDescription("SDRAM");
131        break;
132      case TYPE_EDO:
133        bank->setDescription("EDO");
134        break;
135    }
136  
137    rows = get_spd_byte(5);
138    snprintf(buff, sizeof(buff), "%d", rows);
139    bank->setConfig("rows", buff);
140  
141    if (bank->getSize() == 0)
142    {
143      density = get_spd_byte(31);
144      for (int j = 0; (j < 8) && (rows > 0); j++)
145        if (density & (1 << j))
146      {
147        rows--;
148        size += (4 << j) * 1024 * 1024;             // MB
149        density ^= (1 << j);
150        if (density == 0)
151          size += rows * (4 << j) * 1024 * 1024;
152      }
153      bank->setSize(size);
154    }
155  
156    switch (get_spd_byte(11))                       // error detection and correction scheme
157    {
158      case 0x00:
159        bank->setConfig("errordetection", "none");
160        break;
161      case 0x01:
162        bank->addCapability("parity");
163        bank->setConfig("errordetection", "parity");
164        break;
165      case 0x02:
166        bank->addCapability("ecc");
167        bank->setConfig("errordetection", "ecc");
168        break;
169    }
170  
171    int version = get_spd_byte(62);
172  
173    snprintf(buff, sizeof(buff), "spd-%d.%d", (version & 0xF0) >> 4,
174      version & 0x0F);
175    bank->addCapability(buff);
176  
177    return true;
178  }
179  
180  
181  static bool scan_eeproms(hwNode & memory)
182  {
183    struct dirent **namelist;
184    int n;
185  
186    current_bank = 0;
187  
188    pushd(PROCSENSORS);
189    n = scandir(".", &namelist, selecteeprom, alphasort);
190    popd();
191  
192    if (n < 0)
193      return false;
194  
195    for (int i = 0; i < n; i++)
196    {
197      if (scan_eeprom(memory, namelist[i]->d_name))
198        current_bank++;
199      free(namelist[i]);
200    }
201    free(namelist);
202  
203    return true;
204  }
205  
206  
207  bool scan_spd(hwNode & n)
208  {
209    hwNode *memory = n.getChild("core/memory");
210  
211    current_bank = 0;
212  
213    if (!memory)
214    {
215      hwNode *core = n.getChild("core");
216  
217      if (!core)
218      {
219        n.addChild(hwNode("core", hw::bus));
220        core = n.getChild("core");
221      }
222  
223      if (core)
224      {
225        core->addChild(hwNode("memory", hw::memory));
226        memory = core->getChild("memory");
227      }
228    }
229  
230    if (memory)
231    {
232      memory->claim();
233  
234      return scan_eeproms(*memory);
235    }
236  
237    return false;
238  }