m4api.c
Go to the documentation of this file.
00001 /*
00002  * m4api, a tool for M4-ATX DC-DC power supplies
00003  * (c) 2009-2010 Ken Tossell <ktossell@umd.edu>
00004  *
00005  * This is free software; you can redistribute it and/or modify it
00006  * under the terms of the GNU Lesser General Public License, version 2.1,
00007  * as published by the Free Software Foundation. 
00008  *
00009  * This software is distributed in the hope that it will be useful, but WITHOUT
00010  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
00011  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
00012  * for more details.
00013  *
00014  * You should have received a copy of the GNU Lesser General Public License
00015  * along with this software; if not, write to the Free Software Foundation,
00016  * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
00017  */
00018 
00019 #include <stdio.h>
00020 #include <usb.h>
00021 
00022 #include "m4atx_battery_monitor/m4api.h"
00023 
00024 static size_t m4TypeLengths[13] = {
00025  1, 1, 1, 1, 1, 1, 1, 2,
00026  1, 2, 2, 1, 1
00027 };
00028 
00029 static float m4TypeConversionsV1[13] = {
00030  0.1123, 0.076625, 0.0375, 0.0188136,
00031  1.0, 1.0, 1.0, 1.0,
00032  10.0, 10.0, 1.0, 1.0,
00033  1.0
00034 };
00035 
00036 
00037 static float m4TypeConversionsV2[13] = {
00038  0.1572, 0.1165, 0.0389, 0.0195,
00039  1.0, 1.0, 1.0, 1.0,
00040  10.0, 10.0, 1.0, 1.0,
00041  1.0
00042 };
00043 
00044 static float *m4TypeConversions = NULL;
00045 
00046 struct m4Version m4CurrentVersion = {
00047     .major = 0,
00048     .minor = 0,
00049 };
00050 
00051 static int m4TypeForms[13] = {
00052  M4_FLOAT, M4_FLOAT, M4_FLOAT, M4_FLOAT,
00053  M4_INTEG, M4_INTEG, M4_INTEG, M4_INTEG,
00054  M4_INTEG, M4_INTEG, M4_TIMER, M4_INTEG,
00055  M4_INTEG
00056 };
00057 
00058 char* m4TypeDescs[14] = {
00059   "V", "V", "V", "V",
00060   "deg. C", "sec", "ms", "ms",
00061   "ms", "ms", "", "tries",
00062   ""
00063 };
00064 
00065 struct m4DiagField m4DiagFields[6] = {
00066   {M4_VLT_12_11, 2, "VIN", "Input voltage"},
00067   {M4_VLT_12_11, 3, "IGN", "Ignition voltage"},
00068   {M4_VLT_33_01, 4, "33V", "Voltage on 3.3V rail"},
00069   {M4_VLT_5_03, 5, "5V", "Voltage on 5V rail"},
00070   {M4_VLT_12_07, 6, "12V", "Voltage on 12V rail"},
00071   {M4_DEG, 12, "TEMP", "Temperature"},
00072 };
00073 
00074 size_t m4NumDiagFields = sizeof(m4DiagFields) / sizeof(m4DiagFields[0]);
00075 
00076 struct m4ConfigField m4ConfigFields[47] = {
00077   {M4_VLT_12_11, 0x00, "IGN_HIGH", "Ignition HIGH limit"},
00078   {M4_VLT_12_11, 0x01, "IGN_LOW", "Ignition LOW limit"},
00079   {M4_MSC_10_1, 0x02, "IGN_DBC", "Ignition debounce"},
00080   {M4_SEC, 0x03, "PSU_DELAY", "Delay before PSU startup"},
00081   {M4_VLT_12_11, 0x04, "VIN_MIN_START", "Min. VIN voltage at PSU startup"},
00082   {M4_VLT_12_11, 0x05, "VIN_MIN_ON", "Min. VIN voltage while all rails are on"},
00083   {M4_VLT_12_11, 0x06, "VIN_MIN_5V", "Min. VIN voltage while 5VSB is on"},
00084   {M4_VLT_12_11, 0x07, "VIN_MAX", "Max. allowed VIN voltage"},
00085   {M4_VLT_12_07, 0x08, "12V_MAX", "Max. limit for 12V"},
00086   {M4_VLT_12_07, 0x09, "12V_MIN", "Min. limit for 12V"},
00087   {M4_VLT_5_03, 0x0a, "5V_MAX", "Max. limit for 5V"},
00088   {M4_VLT_5_03, 0x0b, "5V_MIN", "Min. limit for 5V"},
00089   {M4_VLT_33_01, 0x0c, "33V_MAX", "Max. limit for 3.3V"},
00090   {M4_VLT_33_01, 0x0d, "33V_MIN", "Min. limit for 3.3V"},
00091   {M4_MSC_1_1, 0x0e, "12V_TIME", "12V rail startup time"},
00092   {M4_MSC_1_1, 0x0f, "33V_TIME", "3.3V rail startup time"},
00093   {M4_MSC_10_2, 0x10, "PWRSW", "PWRSW 'push-down' time"},
00094   {M4_TRY, 0x12, "PSU_ON_TIME", "Try to turn ON the PSU for N time"},
00095   {M4_SEC, 0x13, "ON_DELAY", "Delay between ON tries"},
00096   {M4_TRY, 0x14, "PSU_OFF_TIME", "Try to turn OFF the PSU for N time"},
00097   {M4_SEC, 0x15, "OFF_DELAY", "Delay between OFF tries"},
00098   {M4_SEC, 0x16, "EMG_TIME_5VSB", "Emergency shutdown timer for 5VSB"},
00099   {M4_SEC, 0x17, "EMG_TIMER", "Emergency shutdown timer"},
00100   {M4_MSC_10_1, 0x18, "PS_ON_0", "PS ON 0 threshold"},
00101   {M4_MSC_10_1, 0x19, "PS_ON_1", "PS ON 1 threshold"},
00102   {M4_MSC_10_2, 0x1a, "THUMP", "Thump timeout"},
00103   {M4_DEG, 0x1c, "TEMP_MAX", "Max. temperature"},
00104   {M4_DEG, 0x1d, "TEMP_MIN", "Min. temperature"},
00105   {M4_BYT, 0x1e, "EMG_OFF_MODE", "Emergency OFF mode selector"},
00106   {M4_MSC_10_1, 0x1f, "5V_SBY_DLY", "5V standby PSW-on delay"},
00107   {M4_TIM, 0x20, "OFF_DELAY_0", "Off-delay MODE 0"},
00108   {M4_TIM, 0x22, "OFF_HARD_0", "Hard-off MODE 0"},
00109   {M4_TIM, 0x24, "OFF_DELAY_1", "Off-delay MODE 1"},
00110   {M4_TIM, 0x26, "OFF_HARD_1", "Hard-off MODE 1"},
00111   {M4_TIM, 0x28, "OFF_DELAY_2", "Off-delay MODE 2"},
00112   {M4_TIM, 0x2a, "OFF_HARD_2", "Hard-off MODE 2"},
00113   {M4_TIM, 0x2c, "OFF_DELAY_3", "Off-delay MODE 3"},
00114   {M4_TIM, 0x2e, "OFF_HARD_3", "Hard-off MODE 3"},
00115   {M4_TIM, 0x30, "OFF_DELAY_4", "Off-delay MODE 4"},
00116   {M4_TIM, 0x32, "OFF_HARD_4", "Hard-off MODE 4"},
00117   {M4_TIM, 0x34, "OFF_DELAY_5", "Off-delay MODE 5"},
00118   {M4_TIM, 0x36, "OFF_HARD_5", "Hard-off MODE 5"},
00119   {M4_TIM, 0x38, "OFF_DELAY_6", "Off-delay MODE 6"},
00120   {M4_TIM, 0x3a, "OFF_HARD_6", "Hard-off MODE 6"},
00121   {M4_TIM, 0x3c, "OFF_DELAY_7", "Off-delay MODE 7"},
00122   {M4_TIM, 0x3e, "OFF_HARD_7", "Hard-off MODE 7"},
00123   /* big gap with no (known) fields */
00124   {M4_BYT, 0xff, "RESET", "Reset to factory defaults"},
00125 };
00126 
00127 #define VENDOR 0x04d8
00128 #define PRODUCT 0xd001
00129 #define READ_ENDPOINT 0x81
00130 #define WRITE_ENDPOINT 0x01
00131 #define TIMEOUT 3000
00132 
00133 
00134 static usb_dev_handle *m4InitUSB() {
00135   struct usb_bus *bus;
00136   struct usb_device *dev;
00137 
00138   usb_init();
00139 
00140   if (usb_find_busses() < 0) {
00141     return NULL;
00142   }
00143 
00144   if (usb_find_devices() < 0) {
00145     return NULL;
00146   }
00147 
00148   bus = usb_get_busses();
00149 
00150   while (bus) {
00151     dev = bus->devices;
00152 
00153     while (dev) {
00154       if (dev->descriptor.idVendor == VENDOR &&
00155           dev->descriptor.idProduct == PRODUCT) {
00156         usb_dev_handle *handle = usb_open(dev);
00157 
00158         if (handle) {
00159 #ifdef LIBUSB_HAS_DETACH_KERNEL_DRIVER_NP
00160           /* Linux usually claims HID devices for its usbhid driver. */
00161           usb_detach_kernel_driver_np(handle, 0);
00162 #endif
00163           if (usb_set_configuration(handle, 1) >= 0) {
00164             if (usb_claim_interface(handle, 0) >= 0) {
00165               if (usb_set_altinterface(handle, 0) < 0) {
00166                 usb_close(handle);
00167                 return NULL;
00168               }
00169             } else {
00170               usb_close(handle);
00171               return NULL;
00172             }
00173           } else {
00174             usb_close(handle);
00175             return NULL;
00176           }
00177 
00178           return handle;
00179         }
00180       }
00181 
00182       dev = dev->next;
00183     }
00184 
00185     bus = bus->next;
00186   }
00187 
00188   return NULL;
00189 }
00190 
00191 int m4Read(usb_dev_handle *dev, unsigned char *buf, unsigned int len, int timeout) {
00192   return usb_interrupt_read(dev, READ_ENDPOINT, (char*) buf, len, timeout);
00193 }
00194 
00195 int m4Write(usb_dev_handle *dev, unsigned char *buf, unsigned int len, int timeout) {
00196   return usb_interrupt_write(dev, WRITE_ENDPOINT, (char*) buf, len, timeout);
00197 }
00198 
00199 /* Gets the handle to the usb device and sets up version and conversions parameters */
00200 usb_dev_handle *m4Init() {
00201 
00202   usb_dev_handle *dev = m4InitUSB();
00203   char buf[24];
00204 
00205   if (dev == NULL)
00206     return NULL;
00207 
00208   m4FetchDiag(dev, buf);
00209 
00210   if (m4CheckVersion(buf) < 0)
00211     return NULL;
00212 
00213   printf("Firmware version %d.%d\n", m4CurrentVersion.major, m4CurrentVersion.minor);
00214 
00215   if (m4CurrentVersion.major < 2)
00216     m4TypeConversions = m4TypeConversionsV1;
00217   else
00218     m4TypeConversions = m4TypeConversionsV2;
00219 
00220   return dev;
00221 }
00222 
00223 
00224 int m4FetchDiag (usb_dev_handle *dev, char *buf) {
00225   unsigned char pollCmd[] = {0x81, 0x00};
00226   
00227   if (m4Write(dev, pollCmd, 2, TIMEOUT) != 2)
00228     return -1;
00229 
00230   if (m4Read(dev, buf, 24, TIMEOUT) != 24)
00231     return -1;
00232 
00233   if (buf[0] != 0x21)
00234     return -1;
00235  
00236   return 0;
00237 }
00238 
00239 int m4CheckVersion(char *buf)
00240 {
00241   m4CurrentVersion.major = ((buf[23]) >> 4) & 0x0f;
00242   m4CurrentVersion.minor = (buf[23]) & 0x0f;
00243 
00244   if (m4CurrentVersion.major == 0)
00245     return -1;
00246 
00247   return 0;
00248 }
00249 
00250 int m4GetDiag (usb_dev_handle *dev, struct m4Diagnostics *diag) {
00251   char buf[24];
00252   int field_id;
00253 
00254   if (m4FetchDiag(dev, buf) < 0)
00255     return -1;
00256 
00257   diag->vin = m4GetVal(M4_VLT_12_11, buf + 2);
00258   diag->vign = m4GetVal(M4_VLT_12_11, buf + 3);
00259   diag->v33 = m4GetVal(M4_VLT_33_01, buf + 4);
00260   diag->v5 = m4GetVal(M4_VLT_5_03, buf + 5);
00261   diag->v12 = m4GetVal(M4_VLT_12_07, buf + 6);
00262   diag->temp = m4GetVal(M4_DEG, buf + 12);
00263 
00264   return 0;
00265 }
00266 
00267 float m4GetVal(enum m4Type type, char *posn) {
00268   float val;
00269   short tmp_sh;
00270   int tmp_i;
00271 
00272   if (m4TypeConversions == NULL)
00273   {
00274     printf("ERROR: No type conversion values\n");
00275     exit(-1);
00276   }
00277 
00278   switch (m4TypeLengths[type]) {
00279     case 1:
00280       tmp_i = posn[0];
00281       if (type != M4_DEG) tmp_i &= 0xff;
00282       break;
00283     case 2:
00284       if (type == M4_DEG) {
00285         tmp_sh = (posn[0] << 8) | (posn[1]);
00286         tmp_i = (float) tmp_sh;
00287       } else {
00288         tmp_i = ((unsigned char) posn[0] << 8) | ((unsigned char) posn[1]);
00289       }
00290       break;
00291     default:
00292       printf("ERROR: typeLen(%d) == %lu!\n", type, m4TypeLengths[type]);
00293       exit(-1);
00294   }
00295 
00296   // printf("%x ", tmp_i);
00297 
00298   val = tmp_i * m4TypeConversions[type];
00299 
00300   return val;
00301 }
00302 
00303 void m4PrintVal(enum m4Type type, float val) {
00304   int tmp_i, sec, min, hr;
00305 
00306   switch (m4TypeForms[type]) {
00307     case M4_INTEG:
00308       if (type == M4_DEG)
00309         printf("%+d", (int) val);
00310       else
00311         printf("%d", (int) val);
00312       break;
00313     case M4_FLOAT:
00314       printf("%0.2f", val);
00315       break;
00316     case M4_TIMER:
00317       tmp_i = (int) val;
00318 
00319       if (tmp_i == 0xffff) {
00320         printf("never");
00321       } else {
00322         sec = tmp_i % 60;
00323         min = (tmp_i % 3600) / 60;
00324         hr = tmp_i / 3600;
00325         
00326         printf("%02d:%02d:%02d", hr, min, sec);
00327       }
00328       break;
00329     default:
00330       printf("ERROR: typeForm(%d) == %d!\n", type, m4TypeForms[type]);
00331       break;
00332   }
00333 }
00334 
00335 int m4GetConfig(usb_dev_handle *dev, struct m4ConfigField *field, char *buf) {
00336   unsigned char cmd[24] = {0xa4, 0xa1};
00337 
00338   cmd[2] = field->index;
00339   cmd[3] = m4TypeLengths[field->type];
00340 
00341   if (m4Write(dev, cmd, 24, TIMEOUT) != 24)
00342     return -1;
00343 
00344   if (m4Read(dev, buf, 24, TIMEOUT) != 24)
00345     return -1;
00346 
00347   if (buf[0] != 0x31)
00348     return -1;
00349 
00350   return 0;
00351 }
00352 
00353 int m4ParseValue(enum m4Type type, char const *strval, char *buf) {
00354   int intval;
00355   float fval;
00356 
00357   int hr, min, sec;
00358 
00359   switch(m4TypeForms[type]) {
00360   case M4_INTEG:
00361     intval = atoi(strval) / (int) m4TypeConversions[type];
00362     break;
00363   case M4_FLOAT:
00364     fval = atof(strval);
00365     intval = fval / m4TypeConversions[type];
00366     break;
00367   case M4_TIMER:
00368     if (!strcasecmp("never", strval))
00369       intval = 0xffff;
00370     else {
00371       if (sscanf(strval, "%d:%d:%d", &hr, &min, &sec) != 3)
00372         return -1;
00373       else
00374         intval = 3600 * hr + 60 * min + sec;
00375     }
00376   }
00377 
00378   if (m4TypeLengths[type] == 2) {
00379     buf[0] = intval >> 8;
00380     buf[1] = intval;
00381   } else {
00382     buf[0] = intval;
00383     buf[1] = 0;
00384   }
00385 
00386   return 0;
00387 }
00388 
00389 int m4GetFloat(usb_dev_handle *dev, enum m4FieldID fid, float *out) {
00390   char buf[24];
00391   struct m4ConfigField *field;
00392   
00393   field = &m4ConfigFields[fid];
00394 
00395   if (m4GetConfig(dev, field, buf))
00396     return -1;
00397 
00398   *out = m4GetVal(field->type, &buf[4]);
00399 
00400   return 0;
00401 }
00402 
00403 int m4SetFloat(usb_dev_handle *dev, enum m4FieldID fid, float val) {
00404   char binary[2];
00405   int ival;
00406   struct m4ConfigField *field;
00407   
00408   field = &m4ConfigFields[fid];
00409 
00410   ival = val / m4TypeConversions[field->type];
00411 
00412   if (m4TypeLengths[field->type] == 2) {
00413     binary[0] = ival >> 8;
00414     binary[1] = ival;
00415   } else {
00416     binary[0] = ival;
00417     binary[1] = 0;
00418   }
00419 
00420   return m4SetBinary(dev, field, binary);
00421 }
00422 
00423 int m4GetInteger(usb_dev_handle *dev, enum m4FieldID fid, int *out) {
00424   char buf[24];
00425   struct m4ConfigField *field;
00426     
00427   field = &m4ConfigFields[fid];
00428 
00429   if (m4GetConfig(dev, field, buf))
00430     return -1;
00431 
00432   *out = (int) m4GetVal(field->type, &buf[4]);
00433 
00434   return 0;
00435 }
00436 
00437 int m4SetInteger(usb_dev_handle *dev, enum m4FieldID fid, int val) {
00438   char binary[2];
00439   struct m4ConfigField *field;
00440 
00441   field = &m4ConfigFields[fid];
00442 
00443   val /= m4TypeConversions[field->type];
00444 
00445   if (m4TypeLengths[field->type] == 2) {
00446     binary[0] = val >> 8;
00447     binary[1] = val;
00448   } else {
00449     binary[0] = val;
00450     binary[1] = 0;
00451   }
00452 
00453   return m4SetBinary(dev, field, binary);
00454 }
00455 
00456 int m4SetConfig(usb_dev_handle *dev, struct m4ConfigField *field, char const *strval) {
00457   char binary[] = {0, 0};
00458 
00459   if (m4ParseValue(field->type, strval, binary) < 0)
00460     return -1;
00461 
00462   return m4SetBinary(dev, field, binary);
00463 }
00464 
00465 int m4SetBinary(usb_dev_handle *dev, struct m4ConfigField *field, char const *val) {
00466   char buf[24];
00467   unsigned char cmd[24] = {0xa4, 0xa0};
00468 
00469   cmd[2] = field->index;
00470   cmd[3] = m4TypeLengths[field->type];
00471   cmd[4] = val[0];
00472   cmd[5] = val[1];
00473 
00474   if (m4Write(dev, cmd, 24, TIMEOUT) != 24)
00475     return -1;
00476 
00477   if (m4Read(dev, buf, 24, TIMEOUT) != 24)
00478     return -1;
00479 
00480   if (buf[0] != 0x31 || buf[2] != cmd[2]
00481       || buf[4] != cmd[4] || buf[5] != cmd[5])
00482     return -1;
00483 
00484   return 0;
00485 }
00486 
00487 void m4PrintDiag(char *buf) {
00488   int field_id;
00489   size_t config_offset = 0;
00490   float value;
00491 
00492   for (field_id = 0; field_id < m4NumDiagFields; ++field_id) {
00493     printf("%s:\t", m4DiagFields[field_id].name);
00494     value = m4GetVal(m4DiagFields[field_id].type, buf + m4DiagFields[field_id].index);
00495     m4PrintVal(m4DiagFields[field_id].type, value);
00496     puts("");
00497   }
00498 }
00499 
00500 int m4ConfigField(char const *name) {
00501   int field_id;
00502 
00503   for (field_id = 0; field_id < M4_NUM_CONFIG_FIELDS; ++field_id) {
00504     if (!strcasecmp(m4ConfigFields[field_id].name, name))
00505       return field_id;
00506   }
00507 
00508   return -1;
00509 }
00510 


m4atx_battery_monitor
Author(s): Ken Tossell , Chris Dunkers , Russell Toris
autogenerated on Thu Jun 6 2019 19:29:30