00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
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
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
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
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
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