//
/*
mld.c: XidarML daemon.
Copyright (c) 1996 Eliot W. Dudley. All rights reserved.
edudley@servtech.com
XIDAR
3388 STATE RT 370
CATO NY 13033
You may distribute under the terms of either the GNU General Public
License or the Artistic License, as specified in the README file.
*/
/*
1-Wire, DS, DS1820, DS2405, DS2407, DS9097, and MicroLan
are trademarks and/or registered trademarks of
Dallas Semiconductor Coporation
*/
//=============================================================================
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "util.h"
#include "b.h"
#include "mlfnc.h"
#include "ml.h"
#include "mld.h"
#include "mlerr.h"
//=============================================================================
//=Prototype.==================================================================
char *MLGetpML(pML pml, char *cmd);
int TML(char *cmd, int pri_ret);
static int MLOpen(pML pml, char *dev);
static int MLWrite(pML pml, char *buf, int count, int flag, pMLNODE pmlnode);
static void MLCD(pML pml, pMLNODE pmlnode);
char *MLSearch(pML pml, char *cmd);
static char *MLSearch_(char *reply, pML pml);
static int MLSearchLoop(pML pml);
static int MLSearchNxt(pML pml, pMLNODE pmlnode, int search_cmd, int skip);
static pMLNODE MLNodeCreate(pML pml, pMLNODE pmlnode);
static int MLAddrCRC(pMLADDR pmladdr);
static int MLAddrCmp(const void *pmlnode0, const void *pmlnode1);
static int MLPathCmp(const void *pmlnode0, const void *pmlnode1);
static int MLNodeEq(pMLNODE pmlnode0, pMLNODE pmlnode1);
static pML MLPathReset(pML pml);
static int ML1820Read(pML, pMLNODE, BYTE *buf, int count, int offset);
static int ML2407Read(pML, pMLNODE, BYTE *buf, int count, int offset);
static int ML2405OnOff(pML, pMLNODE, int state);
static int ML2407OnOff(pML, pMLNODE, int state);
char *MLLoad(pML pml, char *cmd);
static char *MLLoad_(char *reply, pML pml, char *cmd, int *sort);
char *MLDump(pML pml, char *cmd);
static int MLNodeSpr(char *buf, pMLNODE pmlnode);
static int MLNodeFpr(FILE *FILE, pMLNODE pmlnode);
static BYTE *HexToI(BYTE *dst, BYTE *src, int cnt);
//=============================================================================
//=============================================================================
typedef struct serial_struct SERIAL_STRUCT;
typedef struct termios TERMIOS;
///
//@Memo: XidarML bus state information.
/**
All information for a 1-Wire bus. All nodes, their states, and their
topology. Also the serial port's device name, file descriptor,
termios state, and serial_struct state.
*/
struct MLt {
/// Serial port device name.
char *name;
/// File descriptor for the serial port device.
int fd;
/// For 115.2Kbps and custom baud rate control.
SERIAL_STRUCT serial_struct;
/// Normal termios line control.
TERMIOS termios;
/// Pointer list of all the nodes, sorted by name, address, and path.
PL mlnode;
/// The current working directory of the bus.
pMLNODE cwd;
/// Path to the cwd.
MLPATH mlpath;
/// Depth of the path.
char mldepth;
/// Buf for expanding i/o into one 115.2Kbps byte per bit of information.
BYTE buf[8 * ML_MAX_IO];
};
//=============================================================================
//=============================================================================
static MLDRIVER ds1820_driver = {
ML1820Read,
0,
};
static MLDRIVER ds2405_driver = {
0,
ML2405OnOff,
};
static MLDRIVER ds2407_driver = {
ML2407Read,
ML2407OnOff,
};
//=============================================================================
//=============================================================================
#define TML_SIZ 0x1000
#define ML_RESET_ADDR 1
#define ML_RESET_POWER 2
#define ML_TIMESTAMP 4
#define SEARCH_ROM 0xf0
#define SEARCH_ALARM 0xec
#define READ_STATUS 0xaa
#define DS1820_FAMILY 0x10
#define READ_POWER 0xb4
#define CONVERT 0x44
#define DS2407_FAMILY 0x12
#define CHANNEL_ACCESS 0xf5
#define DS2405_FAMILY 0x05
#define READ_SCRATCH 0xbe
#define WRITE_SCRATCH 0x4e
#define COPY_SCRATCH 0x48
#define RECALL 0xB8
#define MATCH_ROM 0x55
#define READ_ROM 0x33
#define SKIP_ROM 0xcc
#define DOUBLE_TEMP(ttt) (((double)ttt) / 65536.0)
#define ML_BYTE(destt, val) \
*(destt++) = val;
#define ML_CPY(destt, srcc, cntt) \
do{memcpy(destt, srcc, cntt); destt += cntt;} while(0);
#define ML_ADDR(destt, addrr) \
ML_CPY(destt, addrr, sizeof(MLADDR));
#define ML_MEMSET(destt, val, cntt) \
do{memset(destt, val, cntt); destt += cntt;} while(0);
#define ML_WRITE(pmll, buff, ptrr, flagg, pmlnodee) \
MLWrite( \
pmll, buff, 8 * ((ptrr) - (buff)), ML_RESET_ADDR | (flagg), pmlnodee)
//=============================================================================
//=Dallas eight-bit CRC table.=================================================
static const unsigned char
crc_table[] = {
0, 94,188,226, 97, 63,221,131,194,156,126, 32,163,253, 31, 65,
157,195, 33,127,252,162, 64, 30, 95, 1,227,189, 62, 96,130,220,
35,125,159,193, 66, 28,254,160,225,191, 93, 3,128,222, 60, 98,
190,224, 2, 92,223,129, 99, 61,124, 34,192,158, 29, 67,161,255,
70, 24,250,164, 39,121,155,197,132,218, 56,102,229,187, 89, 7,
219,133,103, 57,186,228, 6, 88, 25, 71,165,251,120, 38,196,154,
101, 59,217,135, 4, 90,184,230,167,249, 27, 69,198,152,122, 36,
248,166, 68, 26,153,199, 37,123, 58,100,134,216, 91, 5,231,185,
140,210, 48,110,237,179, 81, 15, 78, 16,242,172, 47,113,147,205,
17, 79,173,243,112, 46,204,146,211,141,111, 49,178,236, 14, 80,
175,241, 19, 77,206,144,114, 44,109, 51,209,143, 12, 82,176,238,
50,108,142,208, 83, 13,239,177,240,174, 76, 18,145,207, 45,115,
202,148,118, 40,171,245, 23, 73, 8, 86,180,234,105, 55,213,139,
87, 9,235,181, 54,104,138,212,149,203, 41,119,244,170, 72, 22,
233,183, 85, 11,136,214, 52,106, 43,117,151,201, 74, 20,246,168,
116, 42,200,150, 21, 75,169,247,182,232, 10, 84,215,137,107, 53,
};
//=============================================================================
//=============================================================================
//@Man: Exported functions.
//@{
//
/*/
Write count bits from buf to the bus object pml. Read the state of
the bus for each bit written and record in the same buf. According
to flag options, optionally reset the bus before the write, and/or
timestamp the pmlnode passed by reference. Returns error code less
than zero on error, else the number of bits written.
Source: \URL{../mld.c.html#MLWrite}
*/
int
MLWrite(
pML pml,
char *buf,
int count,
int flag,
pMLNODE pmlnode
) {
int ret;
int i;
BYTE w;
BYTE r;
struct timeval tv;
//==========================================================================
if ((ML_RESET_ADDR & flag) || (ML_RESET_POWER & flag)) {
//=Go to eight bit. 10.47KBaud for reset addr, 450 for reset power.======
tcflush(pml->fd, TCIOFLUSH);
pml->serial_struct.custom_divisor = (ML_RESET_ADDR & flag) ? 11 : 256;
ioctl(pml->fd, TIOCSSERIAL, &pml->serial_struct);
pml->termios.c_cflag = 0
// | CS6 // 0000020
// | CS7 // 0000040
| CS8 // 0000060
// | CSTOPB // 0000100
| CREAD // 0000200
// | PARENB // 0000400
// | PARODD // 0001000
| HUPCL // 0002000
| CLOCAL // 0004000
| B38400 // 0000017
;
tcsetattr(pml->fd, TCSANOW, &pml->termios);
//=======================================================================
//=Send the reset pulse.=================================================
// (115200Hz clock)/(Divisor = 11) --> 95.5uSec per bit.
// LSB first, start bit is low, stop bit is high.
// So five bits low, five bits high is 477uSec reset pulse.
// (115200Hz clock)/(Divisor = 256) --> 2222.2 uSec per bit.
// LSB first, start bit is low, stop bit is high.
// So nine bits low, one bit high is 20ms power reset pulse.
w = (ML_RESET_ADDR & flag) ? 0xf0 : 0x00;
if (0 > write(pml->fd, &w, 1)) {
Fatal(__LINE__, WRITE_FAILED, "%s %s.", pml->name, strerror(errno));
}
//=======================================================================
//=Convert bits to byte stream while waiting.============================
for (i = 0; i < count; ++i) {
pml->buf[i] = "\x00\xff"[test_bit(i, buf)];
}
//=======================================================================
//=Wait at least 2ms/30ms for response.==================================
if ((ret = ReadYield(pml->fd, (ML_RESET_ADDR & flag) ? 2000 : 30000))) {
if (0 > read(pml->fd, &r, 1)) {
Fatal(__LINE__, READ_FAILED, "%s %s.", pml->name, strerror(errno));
}
if (ML_RESET_ADDR & flag) {
//=No reset pulse detected, we're talking to the bus but no answer.
if (r == w) {
return(-NO_DEVICES_FOUND);
}
} else {
return(1);
}
} else {
//=We're not getting echo on the bus.=================================
return(-XIDARML_BUS_FAILURE);
}
//=======================================================================
// Go to 115.2KBaud, 6 bit.==============================================
pml->serial_struct.custom_divisor = 1;
ioctl(pml->fd, TIOCSSERIAL, &pml->serial_struct);
pml->termios.c_cflag = 0
| CS6 // 0000020
// | CS7 // 0000040
// | CS8 // 0000060
// | CSTOPB // 0000100
| CREAD // 0000200
// | PARENB // 0000400
// | PARODD // 0001000
| HUPCL // 0002000
| CLOCAL // 0004000
| B38400 // 0000017
;
tcsetattr(pml->fd, TCSANOW, &pml->termios);
//=======================================================================
//==========================================================================
} else {
//==========================================================================
//=Convert bits to byte stream for write.================================
for (i = 0; i < count; ++i) {
pml->buf[i] = "\x00\xff"[test_bit(i, buf)];
}
//=======================================================================
}
//==========================================================================
//==========================================================================
if (0 > write(pml->fd, pml->buf, count)) {
Fatal(__LINE__, WRITE_FAILED, "%s %s.", pml->name, strerror(errno));
}
//==========================================================================
//==========================================================================
if ((pmlnode) && (ML_TIMESTAMP & flag)) {
gettimeofday(&tv, (struct timezone *)0);
pmlnode->tv = tv;
}
//==========================================================================
//=Wait for response. Read them back.======================================
memset(buf, 0, (count + 7) / 8);
for (i = 0;;) {
if ((ret = ReadYield(pml->fd, 70 * (count - i)))) {
if (0 > (ret = read(pml->fd, pml->buf + i, count))) {
Fatal(__LINE__, READ_FAILED, "%s %s.", pml->name, strerror(errno));
}
if ((i += ret) < count) {
continue;
}
for (i = 0; i < count; ++i) {
if (b_00000001 & pml->buf[i]) {
set_bit(i, buf);
}
}
return(count);
}
break;
}
//==========================================================================
//=We're not getting echo on the bus.=======================================
return(-XIDARML_BUS_FAILURE);
//==========================================================================
}
//
/*/
If a device is not responding when it should be, call MLPathCheck()
to insure that the state of the bus is as advertised. This has been
found to be necessary when noise has caused a switching failure.
Source: \URL{../mld.c.html#MLPathCheck}
*/
void
MLPathCheck(
pML pml
) {
int i;
int ret;
MLNODE mlnode;
pMLNODE pmlnode;
int comfail;
int skip_it;
//=Find all in current path and mark as off, then on when found below.======
for (i = 0; pml->mlpath.p[i]; ++i) {
pml->mlpath.p[i]->state = 0;
}
//==========================================================================
//==========================================================================
memset(&mlnode, 0, sizeof(MLNODE));
mlnode.mladdr.rom.ds_family = DS2405_FAMILY;
mlnode.bit_highest = 7;
for (ret = 1, comfail = 0; ret != 0;) {
ret = MLSearchNxt(pml, &mlnode, SEARCH_ALARM, 7);
if (ret == -NO_ADDRESS_MATCH) {
break;
}
if (ret < 0) {
if (!comfail) {
VAPrintf(0, (VAPRINTF)vfprintf, pexec->LOG
,"MLSearchNxt failed: %s\n"
,ml_err[-ret]
);
fflush(pexec->LOG);
}
TaskDelaySec(1);
memset(&mlnode, 0, sizeof(MLNODE));
mlnode.bit_highest = 7;
comfail = 1;
continue;
}
comfail = 0;
skip_it = 0;
// If the device is in our path record the fact that it is on
// and leave it alone.
for (i = 0; pml->mlpath.p[i]; ++i) {
if (MLNodeEq(pml->mlpath.p[i], &mlnode)) {
pml->mlpath.p[i]->state = 1;
skip_it = 1;
break;
}
}
// If it's not in the path and it's not supposed to be on, turn it off.
if (
(! skip_it) &&
(
(!(pmlnode = GetMLNodeByAddr(pml, &mlnode))) ||
(!(pmlnode->state))
)
) {
switch (mlnode.mladdr.rom.ds_family) {
case DS2405_FAMILY: {
ML2405OnOff(pml, &mlnode, 0);
break;
}
case DS2407_FAMILY: {
ML2407OnOff(pml, &mlnode, 0);
break;
}
}
}
}
//==========================================================================
//=Find all in current path and turn on if not on already.==================
for (i = 0; pmlnode = pml->mlpath.p[i]; ++i) {
if (!(pmlnode->state)) {
(pmlnode->pmldriver->mlon_off)(pml, pmlnode, 1);
}
}
//==========================================================================
}
//
/*/
Lookup node in the node table according to its name.
Source: \URL{../mld.c.html#GetMLNodeByName}
*/
pMLNODE
GetMLNodeByName(
pML pml,
char *name
) {
pMLNODE *ppmlnode;
if ((ppmlnode = (pMLNODE *)PLByName(name, &pml->mlnode))) {
return(*ppmlnode);
}
return(0);
}
//
/*/
Lookup node in the node table according to its eight byte address.
Source: \URL{../mld.c.html#GetMLNodeByAddr}
*/
pMLNODE
GetMLNodeByAddr(
pML pml,
pMLNODE pmlnode
) {
pMLNODE *ppmlnode;
if ((ppmlnode = (pMLNODE *)PLSearch(
(pPLMBR)pmlnode, &pml->mlnode, ML_BY_ADDR, MLAddrCmp))) {
return(*ppmlnode);
}
return(0);
}
//@}
//End: Exported functions.
//=============================================================================
//=============================================================================
//@Man: XidarML daemon functions.
//@{
//
/*/
The XidarML daemon task object. Poll the high priority command
queue, and having drained it, walk the device list looking for low
priority requests.
Source: \URL{../mld.c.html#TML}
*/
/**
The daemon scans its high priority queue for messages. The first
token of any resulting command string is inspected. If the token
is "MLDoNode", a device name is read off the command string and the
deamon proceeds to satisfy all requests to talk to that device.
If the first token isn't "MLDoNode, then the token is taken to be a
function name and the function is called with this bus object as the
first argument and with a pointer to the rest of the command string
as the second argument.
When the high priority command queue is exhausted, the daemon resumes
working through the list of all nodes for this bus in "path" order.
Any task may request control of the bus by pending on the semaphore of
the node it is interested in. When the daemon visits the branch of
the net that contains a particular node, any task pending on that
node get control of the bus. This way a minimum amount of time is
spent activating and deactivating branching switches and maximum
throughput is achieved.
*/
int
TML(
char *cmd,
int pri_ret
) {
int i;
char *ptr;
char *reply;
char *fnc_name;
VAFNC fnc;
va_list va_args;
pEVENT pq = 0;
pML pml = 0;
pTCB ptcb;
pEVENT pfrom;
pMLNODE pmlnode;
if(0 == setjmp((ptcb = TaskCreate(&cmd, TML_SIZ))->jmp_buf)) {
CALLOC(pml, 1, ML);
MLOpen(pml, Shift(&cmd));
ptcb->pmsg = EventCreate(EVENT_NAME_DEFAULT, EVENT_MSG);
pq = QCreate(EVENT_NAME_DEFAULT, 64);
PLNew(&pml->mlnode, 256, 64, ML_BY_PATH + 1);
SpawnRet(pri_ret, ptcb->name);
}
StackClr(ptcb);
ENTER(TML);
MLPathReset(pml);
for (i = 0; ;) {
pmlnode = 0;
while ((va_args = (va_list)EventPend(pq, -1))) {
pfrom = va_arg(va_args, pEVENT);
ptr = cmd = va_arg(va_args, char *);
for (;;) {
if (!(fnc_name = Shift(&ptr))) {
reply = strcpy(BufNew(), "0 Function name is (nil).\n");
BufDelete(cmd);
EventSched(pfrom, reply);
break;
}
if (!(strcmp("MLDoNode", fnc_name))) {
pmlnode = GetMLNodeByName(pml, Shift(&ptr));
BufDelete(cmd);
EventSched(pfrom, strcpy(BufNew(), "1 MLDoNode"));
break;
}
if (!(fnc = GetFncByName(fnc_name))) {
reply = strcat(strcat(strcpy(BufNew(),
"0 Function "), fnc_name), " not found.");
BufDelete(cmd);
EventSched(pfrom, reply);
break;
}
reply = (fnc)(pml, strcpy(BufNew(), ptr ? ptr : ""));
BufDelete(cmd);
EventSched(pfrom, reply);
break;
}
if (pmlnode) {
break;
}
}
if (! pmlnode) {
if (i >= pml->mlnode.cnt) {
TaskDelayUSec(500000);
i = 0;
continue;
}
pmlnode = (pMLNODE)pml->mlnode.p[ML_BY_PATH][i++];
}
if (EventSemCount(pmlnode->psem) < 0) {
MLCD(pml, pmlnode);
while (EventSemCount(pmlnode->psem) < 0) {
EventSched(pmlnode->psem, 0);
EventPend(ptcb->pmsg, 0);
}
}
}
}
//
/*/
Open the serial device and store the resulting file descriptor. Some
of the usual ioctl/termios/tcsetattr stuff, some not-so-usual 16550A
custom speed stuff.
Source: \URL{../mld.c.html#MLOpen}
*/
static int
MLOpen(
pML pml,
char *dev
) {
STACK_CHECK();
ENTER(MLOpen);
if (!dev) {
Fatal(__LINE__, NO_SERIAL_DEVICE_SPECIFIED);
}
MALLOC(pml->name, strlen(dev) + 1, char);
if (0 > (pml->fd =
open(strcpy(pml->name, dev), O_RDWR))) {
Fatal(__LINE__, OPEN_FAILED, "%s %s.", pml->name, strerror(errno));
}
if (0 > ioctl(pml->fd, TIOCGSERIAL, &pml->serial_struct)) {
Fatal(__LINE__, IOCTL_FAILED, "%s %s.", pml->name, strerror(errno));
}
if (0 > tcgetattr(pml->fd, &pml->termios)) {
Fatal(__LINE__, TCGETATTR_FAILED, "%s %s.", pml->name, strerror(errno));
return(0);
}
tcflush(pml->fd, TCIOFLUSH);
pml->serial_struct.type = PORT_16550A;
pml->serial_struct.flags =
(pml->serial_struct.flags & ~ASYNC_SPD_MASK) | ASYNC_SPD_CUST;
pml->serial_struct.custom_divisor = 1;
ioctl(pml->fd, TIOCSSERIAL, &pml->serial_struct);
cfsetospeed(&pml->termios, B38400);
cfsetispeed(&pml->termios, B38400);
pml->termios.c_oflag = 0;
pml->termios.c_lflag = 0;
tcsetattr(pml->fd, TCSAFLUSH, &pml->termios);
RETURN(1);
}
//
/*/
Change the current working directory of the bus object to match that
of the device node indicated. Turn off switches going up the active
bus path until the destination path can be reached by turning on
switches on the way down.
Source: \URL{../mld.c.html#MLCD}
*/
void
MLCD(
pML pml,
pMLNODE pmlnode
) {
int i;
pMLNODE pmldir;
if (pmlnode->cwd != pml->cwd) {
for (; pml->mldepth >= 0; --pml->mldepth) {
if (
(pmldir = pml->mlpath.p[pml->mldepth]) &&
(pmldir != pmlnode->mlpath.p[pml->mldepth]) &&
(pmldir->state)
) {
(pmldir->pmldriver->mlon_off)(pml, pmldir, 0);
}
if (pmldir == pmlnode->mlpath.p[pml->mldepth]) {
break;
}
}
pml->cwd = pmlnode->cwd;
(pml->mlpath = pmlnode->mlpath).p[pml->mldepth = pmlnode->mldepth] = 0;
for (i = 0; i < pml->mldepth; ++i) {
if (!((pmldir = pml->mlpath.p[i])->state)) {
(pmldir->pmldriver->mlon_off)(pml, pmldir, 1);
}
}
}
}
//
/*/
Pass a pointer the bus object back to a client task.
\Ref{PLt} is an incomplete type outsid of mld.c.
Source: \URL{../mld.c.html#MLGetpML}
*/
char *
MLGetpML(
pML pml,
char *cmd
) {
char *reply;
BufDelete(cmd);
reply = BufNew();
*((pML *)reply) = pml;
return(reply);
}
//@}
//End: XidarML daemon functions.
//=============================================================================
//=============================================================================
//@Man: 1-Wire device driver functions.
//@{
#define CRC_NONE b_00000000
#define CRC_EACH_BYTE b_00000001
#define CRC_STATUS_PAGE b_00000010
#define CRC_DATA_PAGE b_00000011
#define CHANNEL_A b_00000100
#define CHANNEL_B b_00001000
#define CHANNEL_BOTH b_00001100
#define INTERLEAVE_ASYNC b_00000000
#define INTERLEAVE_SYNC b_00010000
#define R_W_TOGGLE b_00100000
#define INITIAL_MODE_R b_01000000
#define INITIAL_MODE_W b_00000000
#define ACTIVITY_LATCH_RESET b_10000000
//
/*/
Read the DS1820 scratchpad. Store the temperature as a fixed point
value, and record the high and low triggers. Timestamp the node.
Return greater than zero on success.
Source: \URL{../mld.c.html#ML1820Read}
*/
static int
ML1820Read(
pML pml,
pMLNODE pmlnode,
BYTE *buf,
int count,
int offset
) {
int i;
int ret;
BYTE cuf[32];
BYTE *ptr;
pDS1820_SCRATCH scratch;
DS1820 ds1820;
STACK_CHECK();
ptr = cuf;
ML_BYTE(ptr, MATCH_ROM);
ML_ADDR(ptr, &pmlnode->mladdr.rom);
ML_BYTE(ptr, CONVERT);
if (0 > (ret = ML_WRITE(pml, cuf, ptr, ML_TIMESTAMP, pmlnode))) {
return(ret);
}
TaskDelayUSec(600000);
ptr = cuf;
ML_BYTE(ptr, MATCH_ROM);
ML_ADDR(ptr, &pmlnode->mladdr.rom);
ML_BYTE(ptr, READ_SCRATCH);
scratch = (pDS1820_SCRATCH)ptr;
ML_MEMSET(ptr, 0xff, sizeof(DS1820_SCRATCH));
if (0 > (ret = ML_WRITE(pml, cuf, ptr, 0, pmlnode))) {
return(ret);
}
for (
ptr = (char *)scratch, i = 0;
(0xff == *ptr) && (i < sizeof(DS1820_SCRATCH));
i++, ptr++
) {
;
}
if ((i == sizeof(DS1820_SCRATCH)) || (!(scratch->cnt_per))) {
return(-XIDARML_BUS_FAILURE);
}
// According to the DS1820 data sheet:
// TEMPERATURE =
// TEMP_READ - 0.25 + ((COUNT_PER_C - COUNT_REMAIN) / COUNT_PER_C)
// Place decimal point between bits 15 and 16:
pmlnode->dev.ds1820.temp = (
(0xffff0000) &
(((scratch->t_msb << 24) | (scratch->t_lsb << 16)) >> 1)
) -
((1 << 16) / 4) +
(((scratch->cnt_per - scratch->cnt_remain) << 16) / (scratch->cnt_per));
pmlnode->dev.ds1820.t_h = scratch->t_h;
pmlnode->dev.ds1820.t_l = scratch->t_l;
if (buf) {
memcpy(buf, ((char *)&pmlnode->dev.ds1820) + offset, count);
}
return(count);
}
//
/*/
Turn a DS2405 on or off.
Source: \URL{../mld.c.html#ML2405OnOff}
*/
static int
ML2405OnOff(
pML pml,
pMLNODE pmlnode,
int state
) {
BYTE buf[32];
BYTE *ptr;
BYTE *status;
STACK_CHECK();
ptr = buf;
ML_BYTE(ptr, MATCH_ROM);
ML_ADDR(ptr, &pmlnode->mladdr.rom);
status = ptr;
ML_BYTE(ptr, 0xff);
ML_WRITE(pml, buf, ptr, ML_TIMESTAMP, pmlnode);
pmlnode->state = state;
return(1);
}
//
/*/
Turn a DS2407 on or off.
Source: \URL{../mld.c.html#ML2407OnOff}
*/
static int
ML2407OnOff(
pML pml,
pMLNODE pmlnode,
int state
) {
pmlnode->state = state;
}
//
/*/
Read the DS2407 state.
Source: \URL{../mld.c.html#ML2407Read}
*/
static int
ML2407Read(
pML pml,
pMLNODE pmlnode,
BYTE *buf,
int count,
int offset
) {
BYTE cuf[32];
BYTE *ptr;
BYTE *pchannel_info;
BYTE channel_info_0;
BYTE channel_info_1;
STACK_CHECK();
if (pmlnode->mladdr.rom.ds_family != DS2407_FAMILY) {
return(0);
}
ptr = cuf;
ML_BYTE(ptr, MATCH_ROM);
ML_ADDR(ptr, &pmlnode->mladdr.rom);
ML_BYTE(ptr, CHANNEL_ACCESS);
ML_BYTE(ptr, 0
| CRC_NONE //b_00000000
// | CRC_EACH_BYTE //b_00000001
// | CRC_STATUS_PAGE //b_00000010
// | CRC_DATA_PAGE //b_00000011
// | CHANNEL_A //b_00000100
// | CHANNEL_B //b_00001000
| CHANNEL_BOTH //b_00001100
// | INTERLEAVE_ASYNC //b_00000000
| INTERLEAVE_SYNC //b_00010000
// | R_W_TOGGLE //b_00100000
| INITIAL_MODE_R //b_01000000
// | INITIAL_MODE_W //b_00000000
// | ACTIVITY_LATCH_RESET //b_10000000
);
ML_BYTE(ptr, 0xff);
pchannel_info = ptr;
ML_BYTE(ptr, 0xff);
ML_WRITE(pml, cuf, ptr, ML_TIMESTAMP, pmlnode);
channel_info_0 = *pchannel_info;
ptr = cuf;
ML_BYTE(ptr, MATCH_ROM);
ML_ADDR(ptr, &pmlnode->mladdr.rom);
ML_BYTE(ptr, CHANNEL_ACCESS);
ML_BYTE(ptr, 0
| CRC_NONE //b_00000000
// | CRC_EACH_BYTE //b_00000001
// | CRC_STATUS_PAGE //b_00000010
// | CRC_DATA_PAGE //b_00000011
// | CHANNEL_A //b_00000100
// | CHANNEL_B //b_00001000
| CHANNEL_BOTH //b_00001100
// | INTERLEAVE_ASYNC //b_00000000
| INTERLEAVE_SYNC //b_00010000
// | R_W_TOGGLE //b_00100000
| INITIAL_MODE_R //b_01000000
// | INITIAL_MODE_W //b_00000000
| ACTIVITY_LATCH_RESET //b_10000000
);
ML_BYTE(ptr, 0xff);
pchannel_info = ptr;
ML_BYTE(ptr, 0xff);
ML_WRITE(pml, cuf, ptr, ML_TIMESTAMP, pmlnode);
channel_info_1 = *pchannel_info;
VAPrintf(0, (VAPRINTF)vfprintf, pexec->LOG
,"%02x%02x%02x%02x%02x%02x%02x%02x %02x %02x\n"
,pmlnode->mladdr.b[0]
,pmlnode->mladdr.b[1]
,pmlnode->mladdr.b[2]
,pmlnode->mladdr.b[3]
,pmlnode->mladdr.b[4]
,pmlnode->mladdr.b[5]
,pmlnode->mladdr.b[6]
,pmlnode->mladdr.b[7]
,channel_info_0
,channel_info_1
);
fflush(pexec->LOG);
return(1);
}
//@}
//End: 1-Wire device driver functions.
//=============================================================================
//=============================================================================
//@Man: Node list maintenance functions.
//@{
//=============================================================================
#if 0
main()
{
int i;
int j;
for (i = 0; i < 0x100; ++i) {
j = ((i << 4) & 0xf0) | ((i >> 4) & 0x0f);
j = ((j << 2) & 0xcc) | ((j >> 2) & 0x33);
j = ((j << 1) & 0xaa) | ((j >> 1) & 0x55);
printf("%s0x%02x"
,i % 8 ? ", " : ",\n "
,j
);
}
}
#endif
static const unsigned char
bit_rev_table[] = {
0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,
0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4,
0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec,
0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,
0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6,
0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1,
0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9,
0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed,
0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3,
0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7,
0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef,
0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff
};
//
/*/
Set up to load a configuration file. Re-sort the node list if
any node names changed.
Source: \URL{../mld.c.html#MLLoad}
*/
char *
MLLoad(
pML pml,
char *cmd
) {
int sort;
char *reply;
reply = strcpy(BufNew(), "1 MLLoad");
if (('0' != *(reply = MLLoad_(reply, pml, cmd, &sort))) && (sort)) {
qsort(&pml->mlnode.p[ML_BY_NAME][0],
pml->mlnode.cnt, sizeof(pPLMBR), NameCmp);
}
BufDelete(cmd);
return(reply);
}
//
/*/
Load the XidarML configuration file named int the command string.
The configuration file establishes the topology of the tree and
whatever names might have been assigned to the nodes. File is
same format as that resulting from an \Ref{MLDump}().
Source: \URL{../mld.c.html#MLLoad_}
*/
static char *
MLLoad_(
char *reply,
pML pml,
char *cmd,
int *sort
) {
char *ptr;
char *qtr;
char *file_name;
char *name;
char *buf;
char *cuf;
MLNODE mlnode;
pMLNODE pmlnode;
FILE *FILE;
STACK_CHECK();
ptr = cmd;
if ('0' == *FOpen(reply, &FILE, &file_name, "r", &ptr)) {
return(reply);
}
for (
buf = BufNew(),
cuf = BufNew(),
pmlnode = 0,
*sort = 0,
MLPathReset(pml);
fgets(ptr = buf, BUF_SIZ - 1, FILE);
) {
memset(&mlnode, 0, sizeof(MLNODE));
mlnode.mldepth = atoi(Shift(&ptr));
HexToI(mlnode.mladdr.b, Shift(&ptr), 8);
name = Shift(&ptr);
for (;;) {
if (mlnode.mldepth == pml->mldepth) {
break;
}
if (mlnode.mldepth > pml->mldepth) {
pml->mlpath.p[pml->mldepth++] = pml->cwd = pmlnode;
break;
}
while (mlnode.mldepth < pml->mldepth) {
pml->mlpath.p[--pml->mldepth] = 0;
}
break;
}
pml->cwd = pml->mldepth ? pml->mlpath.p[pml->mldepth - 1] : 0;
pmlnode = MLNodeCreate(pml, &mlnode);
if (strcmp(pmlnode->name, name)) {
*sort = 1;
if (strlen(pmlnode->name) >= strlen(name)) {
strcpy(pmlnode->name, name);
} else {
REALLOC(pmlnode->name, strlen(name) + 1, char);
strcpy(pmlnode->name, name);
}
}
}
BufDelete(cuf);
BufDelete(buf);
fclose(FILE);
MLPathReset(pml);
return(reply);
}
//
/*/
Compare two pointers to pointers to nodes according the addresses.
Source: \URL{../mld.c.html#MLAddrCmp}
*/
static int
MLAddrCmp(
const void *pmlnode0,
const void *pmlnode1
) {
int i;
int cmp;
BYTE *ptr;
BYTE *qtr;
STACK_CHECK();
if ((!*((pMLNODE *)pmlnode0)) && (!*((pMLNODE *)pmlnode1))) {return( 0);}
if (!*((pMLNODE *)pmlnode0)) {return( 1);}
if (!*((pMLNODE *)pmlnode1)) {return(-1);}
ptr = (BYTE *)(&(*((pMLNODE *)pmlnode0))->mladdr.rom);
qtr = (BYTE *)(&(*((pMLNODE *)pmlnode1))->mladdr.rom);
for (i = 0; i < 8; ++i) {
if ((cmp = bit_rev_table[*ptr++] - bit_rev_table[*qtr++])) {
return(cmp);
}
}
return(0);
}
//
/*/
Compare two pointers to pointers to nodes according the path order
of the two. Path order is by address within directories, where
any given address might also be a directory itself.
Source: \URL{../mld.c.html#MLPathCmp}
*/
static int
MLPathCmp(
const void *pmlnode0,
const void *pmlnode1
) {
int i;
pMLNODE path0;
pMLNODE path1;
int cmp;
pMLNODE p0;
pMLNODE p1;
STACK_CHECK();
if ((!*((pMLNODE *)pmlnode0)) && (!*((pMLNODE *)pmlnode1))) {return( 0);}
if (!*((pMLNODE *)pmlnode0)) {return( 1);}
if (!*((pMLNODE *)pmlnode1)) {return(-1);}
p0 = *(pMLNODE *)pmlnode0;
p1 = *(pMLNODE *)pmlnode1;
for (i = 0; i < CNTt(p0->mlpath.p); ++i) {
path0 = p0->mlpath.p[i];
path1 = p1->mlpath.p[i];
if ((!path0) && (!path1)) {return ( 0);}
if (!path0) {return (-1);}
if (!path1) {return ( 1);}
if ((cmp = MLAddrCmp(&path0, &path1))) {
return(cmp);
}
}
return(0);
}
//
/*/
Compare two nodes for address equality and hence identity.
Source: \URL{../mld.c.html#MLNodeEq}
*/
static int
MLNodeEq(
pMLNODE pmlnode0,
pMLNODE pmlnode1
) {
return(
(pmlnode0->mladdr.u[0] == pmlnode1->mladdr.u[0]) &&
(pmlnode0->mladdr.u[1] == pmlnode1->mladdr.u[1])
);
}
//
/*/
Return the node if it exists already. Else create the node, give
it a name and a driver table, and install it in the directory struture.
Source: \URL{../mld.c.html#MLNodeCreate}
*/
static pMLNODE
MLNodeCreate(
pML pml,
pMLNODE pmlnode
) {
pMLNODE *ppmlnode;
pMLNODE pmlnode_old;
STACK_CHECK();
if ((pmlnode_old = GetMLNodeByAddr(pml, pmlnode))) {
return(pmlnode_old);
}
ppmlnode = (pMLNODE *)PLGrow(&pml->mlnode);
CALLOC(*ppmlnode, 1, MLNODE);
**ppmlnode = *pmlnode;
if (!((*ppmlnode)->name)) {
MALLOC((*ppmlnode)->name, 17, char);
MLNodeSpr((*ppmlnode)->name, *ppmlnode);
}
(*ppmlnode)->psem = EventCreate("", EVENT_SEM);
(*ppmlnode)->cwd = pml->cwd;
(*ppmlnode)->mlpath = pml->mlpath;
(*ppmlnode)->mlpath.p[(*ppmlnode)->mldepth = pml->mldepth] = *ppmlnode;
switch ((*ppmlnode)->mladdr.rom.ds_family) {
case DS1820_FAMILY: {
(*ppmlnode)->pmldriver = &ds1820_driver;
break;
}
case DS2405_FAMILY: {
(*ppmlnode)->is_switch = 1;
(*ppmlnode)->pmldriver = &ds2405_driver;
break;
}
case DS2407_FAMILY: {
(*ppmlnode)->is_switch = 1;
(*ppmlnode)->pmldriver = &ds2407_driver;
break;
}
default: {
break;
}
};
PLInsert(*((pPLMBR *)ppmlnode), &pml->mlnode, ML_BY_NAME, NameCmp);
PLInsert(*((pPLMBR *)ppmlnode), &pml->mlnode, ML_BY_ADDR, MLAddrCmp);
PLInsert(*((pPLMBR *)ppmlnode), &pml->mlnode, ML_BY_PATH, MLPathCmp);
return(*ppmlnode);
}
//
/*/
Dump the bus tree out to the file named in the command string.
Source: \URL{../mld.c.html#MLDump}
*/
char *
MLDump(
pML pml,
char *cmd
) {
int i;
int len;
char *ptr;
char *buf;
char *cuf;
char *fmt;
char *reply;
char *file_name;
FILE *FILE;
pMLNODE pmlnode;
int max_name;
int max_mldepth;
STACK_CHECK();
reply = strcpy(BufNew(), "1 MLDump");
ptr = cmd;
if ('0' == *FOpen(reply, &FILE, &file_name, "w", &ptr)) {
BufDelete(cmd);
return(reply);
}
buf = BufNew();
cuf = BufNew();
fmt = BufNew();
for (max_mldepth = max_name = i = 0; i < pml->mlnode.cnt; ++i) {
pmlnode = (pMLNODE)pml->mlnode.p[ML_BY_PATH][i];
MAX(max_mldepth, pmlnode->mldepth);
len = strlen(pmlnode->name);
MAX(max_name, len);
}
VAPrintf(0, (VAPRINTF)vsprintf, fmt, "%%s%%s%%-%d.%ds\n",
max_name, max_name);
((char *)memset(cuf, ' ', 31))[31] = 0;
for (i = 0; i < pml->mlnode.cnt; ++i) {
pmlnode = (pMLNODE)pml->mlnode.p[ML_BY_PATH][i];
ptr = buf;
ptr += VAPrintf(0, (VAPRINTF)vsprintf, buf, "%d%s"
,pmlnode->mldepth
,cuf + 30 - (2 * pmlnode->mldepth)
);
ptr += MLNodeSpr(ptr, pmlnode->mlpath.p[pmlnode->mldepth]);
VAPrintf(0, (VAPRINTF)vfprintf, FILE, fmt
,buf
,cuf + 30 - (2 * (max_mldepth - pmlnode->mldepth))
,pmlnode->name ? pmlnode->name : ""
);
}
BufDelete(fmt);
BufDelete(cuf);
BufDelete(buf);
fclose(FILE);
BufDelete(cmd);
return(reply);
}
//
/*/
Convert hexadecimal string commencing at src into count bytes of
binary at dst.
Source: \URL{../mld.c.html#HexToI}
*/
BYTE *
HexToI(
BYTE *dst,
BYTE *src,
int count
) {
int i;
BYTE *ptr;
BYTE *map;
map = "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f";
for (ptr = src, i = 0; i < count; ++i, ++dst) {
*ptr = (BYTE)(isalpha(*ptr) ? (tolower(*ptr) + 10 - 'a') : (*ptr - '0'));
*dst = map[*ptr++] << 4;
*ptr = (BYTE)(isalpha(*ptr) ? (tolower(*ptr) + 10 - 'a') : (*ptr - '0'));
*dst |= map[*ptr++] << 0;
}
return(dst - count);
}
//@}
//End: Node list maintenance functions.
//=============================================================================
//=============================================================================
//@Man: Bus searching functions.
//@{
//
/*/
Set up for the recursive portion of the bus tree search by resetting
the bus and the daemon state.
Source: \URL{../mld.c.html#MLSearch}
*/
char *
MLSearch(
pML pml,
char *cmd
) {
char *reply;
BufDelete(cmd);
reply = strcpy(BufNew(), "1 MLSearch");
return(MLSearch_(reply, MLPathReset(pml)));
}
//
/*/
The recursive portion of the bus search. If any switch devices are
found in the current directory, activate them one by one and descend
down the tree from there.
Source: \URL{../mld.c.html#MLSearch_}
*/
static char *
MLSearch_(
char *reply,
pML pml
) {
int i;
pMLNODE pmlnode;
STACK_CHECK();
// Make sure only the active path is energized.
MLPathCheck(pml);
// Find any new nodes.
MLSearchLoop(pml);
//=Now run through pml->pmllist, find all nodes in this cwd, and descend.
for (i = 0; i < pml->mlnode.cnt; ++i) {
//=If this is a switch and it belongs to the current working directory,
// make it cwd and descend.
pmlnode = (pMLNODE)pml->mlnode.p[ML_BY_PATH][i];
if (
(pmlnode->cwd == pml->cwd) &&
(pmlnode->is_switch) &&
(pmlnode->pmldriver) &&
(pmlnode->pmldriver->mlon_off)
) {
pml->cwd = pmlnode;
// Turn it on.
(pmlnode->pmldriver->mlon_off)(pml, pmlnode, 1);
pml->mlpath.p[pml->mldepth++] = pmlnode;
MLSearch_(reply, pml);
pml->mlpath.p[--pml->mldepth] = 0;
// Turn it off.
(pmlnode->pmldriver->mlon_off)(pml, pmlnode, 0);
pml->cwd = pmlnode->cwd;
}
}
return(reply);
}
//
/*/
Repeat calls to \Ref{MLSearchNxt}() until all active devices have
been found.
Source: \URL{../mld.c.html#MLSearchLoop}
*/
static int
MLSearchLoop(
pML pml
) {
int ret;
int comfail;
MLNODE mlnode;
pMLNODE pthis;
memset(pthis = &mlnode, 0, sizeof(MLNODE));
pthis->bit_highest = -1;
for (ret = 1, comfail = 0; ret != 0;) {
ret = MLSearchNxt(pml, pthis, SEARCH_ROM, 0);
if (ret < 0) {
if (!comfail) {
VAPrintf(0, (VAPRINTF)vfprintf, pexec->LOG
,"MLSearchNxt failed: %s\n"
,ml_err[-ret]
);
fflush(pexec->LOG);
}
TaskDelaySec(1);
memset(pthis, 0, sizeof(MLNODE));
pthis->bit_highest = -1;
comfail = 1;
continue;
}
comfail = 0;
MLNodeCreate(pml, pthis);
pthis->name = 0;
}
return(1);
}
//=Using the bitops.h stuff that works on arrays of int.=======================
#define CHAR(x) (*((char *)&x))
// Have to release a semaphore before returning, goto works for me.
#define GOTO_RET(rett) do{ret=rett;goto MLSearchNxt_RETURN;}while(0);
//=============================================================================
//
/*/
Find the next node in the tree. pmlnode holds result unless negative
return value. Return of zero indicates this is the last node.
Source: \URL{../mld.c.html#MLSearchNxt}
*/
/**
The underlying problem with this whole mess is that each MLWrite()
implies one or more select() calls, and that takes until the
next 10ms tick (with the Linux I'm using as I write this). If we did
it one bit at a time it would take 10ms for the reset, 8 * 10ms for the
search command, and (64 * 3) * 10ms for the search, a little over
two seconds. Since exploring a complex tree means finding all the
switches and doing a complete rom search each time a new leaf is
turned on, I wanted to get this time down. With the methods I used
here I got it down to typically 300ms, which works great for what
I need. It would be easy enough to move a lot of this down into
the serial driver and get some dramatic gains through improved
sychronization, but I don't need to, and keeping it in user code has
at least a fighting chance of being ported over to other environments.
The main thing is to do as may bits as possible together in
each MLWrite().
The biggest win is the hail mary, which exploits the little endian,
sequentially assigned aspect of the DS device IDs.
Each device has an eight bit device code and forty eight bits of
address, except that I've never seen one with more than five nybles
of address, call it six, plus two nybles device code equals
thirty two bits, the rest should be zeros followed by the CRC. So at
the point where thirty two bits of deice ID have been resolved,
send the last (32 * 3) search slots as one big MLWrite() and
then analyze what was read back to see if the hail mary was
succussful (which it always is). If that don't work, search the
last twenty four bits of ID and then hail mary the CRC, which
should never have to be searched, hence two retries could be needed
at this level.
*/
static int
MLSearchNxt(
pML pml, // What ML.
pMLNODE pmlnode, // Starting node, resulting node.
int search_cmd, // SEARCH_ROM, SEARCH_ALARM, ...
int skip // Pre-select first so many bits.
) {
pMLADDR pmladdr;
int i;
int j;
int ret;
int c;
int retry;
BYTE buf[25];
BYTE cuf[25];
int bit_highest;
int bit_highest_save;
int bit_lowest;
int bit_lowest_save;
int bit_this;
int bit_highest_next;
int bit_skip;
int bit_hail_mary;
int start;
int finish;
struct timeval tv;
STACK_CHECK();
ENTER(MLSearchNxt);
//=Just to shorten up some of the expressions.==============================
pmladdr = &pmlnode->mladdr;
//==========================================================================
//=Save the initial state of the state variables so we can restart==========
// on a CRC error.
bit_highest_save = bit_highest = pmlnode->bit_highest;
bit_lowest_save = bit_lowest = skip;
//==========================================================================
gettimeofday(&tv, (struct timezone *)0);
start = (tv.tv_sec << 8) | (0xff & ((tv.tv_usec << 8) / 1000000));
for (bit_hail_mary = 32, retry = 0; retry < 2; ++retry) {
//=Send the search command byte. There are different types of searches.=
// Only send seven bits of the eight bit command. Each bit of the
// search is done with two search slots, and then a select slot. What
// to send in the select slot can't be known until after reading back
// the search slots. Earlier versions sent two search bits with a
// single MLWrite() and then decided what to do for the select slot.
// This was a big win, down to ~1200ms from ~1800ms. So then I
// adjusted the loop to send the select slot from the last time through
// with the search slots for this time around, bringing us down to
// ~600ms. To do this I needed one bit from the eight bit command
// byte to prime the pump for the first time through the loop.
CHAR(c) = search_cmd;
if (0 > (ret = MLWrite(
pml, (char *)&c, 7, ML_RESET_ADDR, 0))) GOTO_RET(ret);
//=Keep track of the search branch point for the next time.==============
bit_highest_next = -1;
//=We already know the select slots up to bit_lowest or bit_highest,=====
// whichever is higher.
// Bit_lowest is the lowest bit we need to search, as when searching
// only for devices with a particular device type, or doing what DS
// calls a "strong" access, that is to search with all sixty four bits
// stated in advance. The latter can be used to prove that the
// device is there before presuming to talk to it.
// Bit_highest is the highest unresolved bit for this time through,
// that is, where the search starts in.
bit_skip = bit_lowest > bit_highest ? bit_lowest : bit_highest;
//=Build up one big MLWrite() for all the known bits.===================
if (bit_skip > 0) {
memset(buf, 0xff, 25);
buf[0] = 0xfe | (search_cmd >> 7);
for (i = 0; i < bit_skip; ++i) {
if (!test_bit(i, pmladdr)) {
clear_bit(3 + (3 * i), buf);
}
}
//=Analyze the results.===============================================
if (0 > (ret = MLWrite(pml, buf, (bit_skip == 64) + i * 3, 0, 0))) {
GOTO_RET(ret);
}
for (i = 0; i < bit_skip; ++i) {
//=Results of the search slots for this bit.=======================
c =
((!test_bit(1 + (3 * i), buf)) << 1) |
((!test_bit(2 + (3 * i), buf)) << 0);
//=Both non-zero, there's branch point here, record the highest====
// for next time through.
if (c == 3) {
if (!test_bit(i, pmladdr)) {
pmlnode->bit_highest = bit_highest_next = i;
}
}
//=Neither non-zero, no device has either a one or a zero, so======
// no device has a matching address even up to this point.
if (c == 0) {
GOTO_RET(-NO_ADDRESS_MATCH);
}
}
//====================================================================
//=This was a successful strong access.===============================
if (bit_skip == 64) {
gettimeofday(&tv, (struct timezone *)0);
finish = (tv.tv_sec << 8) | (0xff & ((tv.tv_usec << 8) / 1000000));
VAPrintf(0, (VAPRINTF)vfprintf, pexec->LOG
," %3.0f %3d\n"
,((float)(finish - start)) / 0.256
,bit_skip
);
fflush(pexec->LOG);
GOTO_RET(1);
}
//====================================================================
//=Prime pump with leftover select slot.
CHAR(c) = test_bit(i - 1, pmladdr) ? b_00000111 : b_00000110;
} else {
//=Prime pump with leftover search command bit.
CHAR(c) = b_00000110 | (search_cmd >> 7);
}
//=======================================================================
//=OK. Any pre-selecting is done and (char *)&c holds the next==========
// select/search slots. From here to the hail mary point is hopefully
// the only unknown portion of the device address.
for (bit_this = bit_skip; bit_this < bit_hail_mary; ++bit_this) {
if (0 > (ret = MLWrite(pml, (char *)&c, 3, 0, 0))) GOTO_RET(ret);
//=We want bits one and two.==========================================
switch (b_00000011 & (CHAR(c) >> 1)) {
case 0: {
if (bit_this > bit_highest) {
bit_highest_next = bit_this;
clear_bit(bit_this, pmladdr);
goto SELECT_LO;
}
if (bit_this == bit_highest) {
set_bit(bit_this, pmladdr);
goto SELECT_HI;
}
if (test_bit(bit_this, pmladdr)) {
goto SELECT_HI;
}
bit_highest_next = bit_this;
goto SELECT_LO;
}
case 1: {
set_bit(bit_this, pmladdr);
goto SELECT_HI;
}
case 2: {
clear_bit(bit_this, pmladdr);
goto SELECT_LO;
}
case 3: {
GOTO_RET(-NO_ADDRESS_MATCH);
}
}
SELECT_HI: CHAR(c) = b_00000111; continue;
SELECT_LO: CHAR(c) = b_00000110; continue;
}
//=======================================================================
//=Clear out the address from bit_this up to the CRC. Calc the CRC.=====
if ((j = bit_this % 8)) {
for (i = bit_this; j < 8; ++j, ++i) {
clear_bit(i, pmladdr);
}
}
if (7 > (j = (bit_this + 7) / 8)) {
memset((char *)pmladdr + j, 0, 7 - j);
}
pmladdr->b[7] = MLAddrCRC(pmladdr);
//=======================================================================
//=Generate a search/select sequence from bit_this out to the end of the=
// address. buf holds the sequence, cuf holds a pre-calculated pattern
// of what we should expect to read back if the hail mary is successful.
memset(buf, 0xff, 25);
memset(cuf, 0xff, 25);
cuf[0] = buf[0] = 0xfe | (0x01 & c); // Left over select slot from loop.
for (j = 0, i = bit_this; i < 64; ++i, ++j) {
if (test_bit(i, pmladdr)) {
clear_bit(2 + (j * 3), cuf);
} else {
clear_bit(3 + (j * 3), buf);
clear_bit(1 + (j * 3), cuf);
clear_bit(3 + (j * 3), cuf);
}
}
if (0 > (ret = MLWrite(pml, buf, 1 + (j * 3), 0, 0))) GOTO_RET(ret);
//=======================================================================
//=Hail mary successful?=================================================
if (memcmp(buf, cuf, (j + 7) / 8)) {
//=Try again with no hail mary (except the CRC, which should always
// be knoweable in advance).
bit_highest = bit_highest_save;
if (bit_hail_mary < 56) {
bit_lowest = bit_this;
bit_hail_mary = 56;
} else {
bit_lowest = bit_lowest_save;
}
//=Go around once more.
continue;
}
//=======================================================================
//=If bit_highest_next never got bumped, that means no branching=========
// points were seen and the search is complete, which is signalled to
// the caller with a return of zero.
ret = (-1 != (bit_highest = bit_highest_next));
//=======================================================================
//=Each node carries around its own bit_highest so that MLSearchNxt()
// can be resumed with an arbitray node as the starting point.
pmlnode->bit_highest = bit_highest;
gettimeofday(&tv, (struct timezone *)0);
finish = (tv.tv_sec << 8) | (0xff & ((tv.tv_usec << 8) / 1000000));
MLNodeFpr(pexec->LOG, pmlnode);
VAPrintf(0, (VAPRINTF)vfprintf, pexec->LOG
," %3.0f %3d\n"
,((float)(finish - start)) / 0.256
,bit_skip
);
fflush(pexec->LOG);
GOTO_RET(ret);
}
ret = -CRC_ERROR;
MLSearchNxt_RETURN:;
RETURN(ret);
}
//
/*/
Return an eight bit DS CRC calculated against a 1-Wire address.
Source: \URL{../mld.c.html#MlAddrCRC}
*/
int
MLAddrCRC(
pMLADDR pmladdr
) {
int i;
int crc;
for (crc = 0, i = 0; i < 7; ++i) {
crc = crc_table[crc ^ pmladdr->b[i]];
}
return(crc);
}
//
/*/
Change directory to root and bounce the bus power.
Source: \URL{../mld.c.html#MLPathReset}
*/
pML
MLPathReset(
pML pml
) {
MLNODE mlnode;
memset(&mlnode, 0, sizeof(MLNODE));
MLCD(pml, &mlnode);
MLWrite(pml, (char *)0, 0, ML_RESET_POWER, 0);
return(pml);
}
//@}
//End: Bus searching functions.
//=============================================================================
static int
MLNodeSpr(
char *buf,
pMLNODE pmlnode
) {
return(VAPrintf(0 ,(VAPRINTF)vsprintf
,buf
,"%02x%02x%02x%02x%02x%02x%02x%02x"
,pmlnode->mladdr.b[0]
,pmlnode->mladdr.b[1]
,pmlnode->mladdr.b[2]
,pmlnode->mladdr.b[3]
,pmlnode->mladdr.b[4]
,pmlnode->mladdr.b[5]
,pmlnode->mladdr.b[6]
,pmlnode->mladdr.b[7]
));
}
static int
MLNodeFpr(
FILE *FILE,
pMLNODE pmlnode
) {
return(VAPrintf(0, (VAPRINTF)vfprintf
,FILE
,"%02x%02x%02x%02x%02x%02x%02x%02x"
,pmlnode->mladdr.b[0]
,pmlnode->mladdr.b[1]
,pmlnode->mladdr.b[2]
,pmlnode->mladdr.b[3]
,pmlnode->mladdr.b[4]
,pmlnode->mladdr.b[5]
,pmlnode->mladdr.b[6]
,pmlnode->mladdr.b[7]
));
}
//=============================================================================
//