📄 diag_l2.c
字号:
/*
* freediag - Vehicle Diagnostic Utility
*
*
* Copyright (C) 2001 Richard Almeida & Ibex Ltd (rpa@ibex.co.uk)
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*************************************************************************
*
* L2 diagnostic interface, generic routines
*
* This sits "under" the L2 per-protocol (such as ISO 14230, SAE J1979)
* - understands the protocol format,
* - removes the "half duplex" echos from half duplex interfaces
* - pads messages as needed,
* - and sends "tester present" messages at the correct intervals to keep
* the link to an ECU alive
*
*/
#include "diag_os.h" /* operating specific includes */
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include "diag.h"
#include "diag_l1.h"
#include "diag_l2.h"
#include "diag_err.h"
#include "diag_general.h"
/* includes for the different level 2 layers */
/* these are referenced in the diag_proto structure */
#include "diag_l2_raw.h"
#include "diag_l2_iso9141.h"
#include "diag_l2_iso14230.h"
#include "diag_l2_saej1850.h"
#include "diag_l2_vag.h"
#include "diag_l2_mb1.h"
/* */
static char *cvsid = "$Id: diag_l2.c,v 1.9 2002/06/16 22:26:44 rpalmeida Exp $";
int diag_l2_debug = 0;
#define DIAG_MODULE "diag_l2"
/*
* Interface to individual protocols
*/
typedef struct diag_proto
{
int diag_id;
int diag_l2_flags;
/* Individual L2 routines, see description of interface in diag_l2.h */
int (*diag_l2_proto_startcomms)(diag_l2_conn_t*, u_int16_t, int, u_int8_t, u_int8_t);
int (*diag_l2_proto_stopcomms)(diag_l2_conn_t*);
int (*diag_l2_proto_send)(diag_l2_conn_t*, diag_msg_t*);
int (*diag_l2_proto_recv)(diag_l2_conn_t *d_l2_conn, int timeout, void (*callback)(void *handle, diag_msg_t *msg), void *handle);
diag_msg_t * (*diag_l2_proto_request)(diag_l2_conn_t*, diag_msg_t*, int*);
int (*diag_l2_proto_atp)(diag_l2_conn_t* d_l2_conn, int p2min, int p2max, int p3min, int p3max, int p4min);
void (*diag_l2_proto_timer)(int);
void (*diag_l2_proto_timeout)(diag_l2_conn_t*);
} diag_proto_t;
/*
* These are the L2 protocols
*
* This table must be in numeric order of the DIAG_L2_PROT_XXX as defined
* in diag_l2.h
* the init routine will check this and exit if its not.
*/
diag_proto_t diag_protocols[] =
{
{ DIAG_L2_PROT_RAW, 0,
diag_l2_proto_raw_startcomms,
diag_l2_proto_raw_stopcomms,
diag_l2_proto_raw_send,
diag_l2_proto_raw_recv,
diag_l2_proto_raw_request,
NULL,
diag_l2_proto_raw_timer,
diag_l2_proto_raw_timeout
},
{ DIAG_L2_PROT_ISO9141, 0,
diag_l2_proto_9141_startcomms,
diag_l2_proto_raw_stopcomms,
diag_l2_proto_9141_send,
diag_l2_proto_9141_recv,
diag_l2_proto_9141_request,
NULL,
diag_l2_proto_raw_timer,
diag_l2_proto_raw_timeout
},
{ DIAG_L2_PROT_ISO9141_2, DIAG_L2_FLAG_FRAMED,
diag_l2_proto_9141_2_startcomms,
diag_l2_proto_raw_stopcomms,
diag_l2_proto_9141_send,
diag_l2_proto_9141_recv,
diag_l2_proto_9141_request,
NULL,
diag_l2_proto_raw_timer,
diag_l2_proto_raw_timeout,
},
{ DIAG_L2_PROT_ISO14230, DIAG_L2_FLAG_FRAMED | DIAG_L2_FLAG_DATA_ONLY
| DIAG_L2_FLAG_KEEPALIVE | DIAG_L2_FLAG_DOESCKSUM,
diag_l2_proto_14230_startcomms,
diag_l2_proto_14230_stopcomms,
diag_l2_proto_14230_send,
diag_l2_proto_14230_recv,
diag_l2_proto_14230_request,
diag_l2_proto_14230_atp,
diag_l2_proto_14230_timer,
diag_l2_proto_14230_timeout,
},
{ DIAG_L2_PROT_SAEJ1850, DIAG_L2_FLAG_FRAMED | DIAG_L2_FLAG_DATA_ONLY
| DIAG_L2_FLAG_DOESCKSUM | DIAG_L2_FLAG_CONNECTS_ALWAYS,
diag_l2_proto_j1850_startcomms,
diag_l2_proto_j1850_stopcomms,
diag_l2_proto_j1850_send,
diag_l2_proto_j1850_recv,
diag_l2_proto_j1850_request,
NULL, NULL, NULL
},
{ DIAG_L2_PROT_CAN, 0,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL },
{ DIAG_L2_PROT_VAG, DIAG_L2_FLAG_FRAMED | DIAG_L2_FLAG_DOESCKSUM,
diag_l2_proto_vag_startcomms,
diag_l2_proto_raw_stopcomms,
diag_l2_proto_vag_send,
diag_l2_proto_vag_recv,
diag_l2_proto_vag_request,
NULL,
diag_l2_proto_raw_timer,
diag_l2_proto_raw_timeout,
},
{ DIAG_L2_PROT_MB1, DIAG_L2_FLAG_FRAMED | DIAG_L2_FLAG_DATA_ONLY
| DIAG_L2_FLAG_KEEPALIVE | DIAG_L2_FLAG_DOESCKSUM,
diag_l2_proto_mb1_startcomms,
diag_l2_proto_raw_stopcomms,
diag_l2_proto_mb1_send,
diag_l2_proto_mb1_recv,
diag_l2_proto_mb1_request,
NULL,
diag_l2_proto_raw_timer,
diag_l2_proto_mb1_timeout,
},
};
/*
* Values for diag_state
*
* The state values are mainly used by the timer code to determine if
* keepalive timers are needed.
*/
#define DIAG_L2_STATE_CLOSED 0 /* Not in use (but not free for anyones use !!) */
#define DIAG_L2_STATE_SENTCONREQ 1 /* Sent connection request (waiting for response/reject) */
#define DIAG_L2_STATE_OPEN 2 /* Up and running */
#define DIAG_L2_STATE_CLOSING 3 /* Sent close request (possibly), waiting for response/timeout */
/*
* The list of connections, not searched often so no need to hash
* (maybe when we actually finish supporting talking to more than
* one ECU on one interface at once)
*/
diag_l2_conn_t *diag_l2_connections = NULL;
diag_l2_conn_t *diag_l2_conbyid[256]; /* Look up by ECU address */
int diag_l2_init_done = 0; /* Init done */
/*
* The list of L1 devices we have open, not used often so no need to hash
*/
diag_l2_link_t *diag_l2_links = NULL;
/*
* Find our link to the L1 device, by name
*/
diag_l2_link_t *
diag_l2_findlink(char *dev_name)
{
diag_l2_link_t *d_l2_link = diag_l2_links;
while (d_l2_link)
{
if ( strcmp(d_l2_link -> diag_l2_name , dev_name) == 0)
return(d_l2_link);
d_l2_link = d_l2_link -> next;
}
return (NULL);
}
/*
* Find our link to the L1 device, by Fd
*/
diag_l2_link_t *
diag_l2_findlink_byfd(int fd)
{
diag_l2_link_t *d_l2_link = diag_l2_links;
while (d_l2_link)
{
if (d_l2_link->diag_l2_fd == fd)
return(d_l2_link);
d_l2_link = d_l2_link -> next;
}
return (NULL);
}
/*
* Remove a link, caller should free it
*/
int
diag_l2_rmlink(diag_l2_link_t *d)
{
diag_l2_link_t *d_l2_link, *d_l2_last;
d_l2_link = diag_l2_links;
d_l2_last = NULL;
while (d_l2_link)
{
if (d_l2_link == d)
{
if (d_l2_last)
d_l2_last = d->next;
else
diag_l2_links = d->next;
break;
}
d_l2_last = d_l2_link;
d_l2_link = d_l2_link -> next;
}
return (0);
}
/*
*
* remove a L2 connection from our list
* - up to the caller to have shut it down properly first
*/
int
diag_l2_rmconn(diag_l2_conn_t *d)
{
diag_l2_conn_t *d_l2_conn = diag_l2_connections;
diag_l2_conn_t *d_l2_last_conn = NULL;
while (d_l2_conn)
{
if (d_l2_conn == d)
{
/* Remove it from list */
if (d_l2_last_conn)
d_l2_last_conn->next = d->next ;
else
diag_l2_connections = d->next;
break;
}
d_l2_last_conn = d_l2_conn;
d_l2_conn = d_l2_conn->next;
}
return (0);
}
/*
* Called regularly to check timeouts etc (call at least once per
* second)
*/
void
diag_l2_timer()
{
diag_l2_conn_t *d_l2_conn;
diag_proto_t *dp;
struct timeval now;
int i;
/*
* Go thru L2 protocols and call their timer()
* routines
*/
for (i=0, dp = diag_protocols ; i<ARRAY_SIZE(diag_protocols); i++, dp++)
{
if (dp->diag_l2_proto_timer)
dp->diag_l2_proto_timer(0);
}
/*
* Calculate the expiration time, we use 2/3 of P3max
* to calculate when to call the L2 protocol timeout() routine
*/
(void)gettimeofday(&now, NULL);
for (d_l2_conn = diag_l2_connections;
d_l2_conn; d_l2_conn = d_l2_conn -> next)
{
struct timeval expiry;
int expired = 0;
/*
* If in monitor mode, we don't do anything as were
* just listening
*/
if ((d_l2_conn->diag_l2_type & DIAG_L2_TYPE_INITMASK)
== DIAG_L2_TYPE_MONINIT)
{
continue;
}
/* Check the send timers vs the p3max timer */
memcpy(&expiry, &d_l2_conn->diag_l2_lastsend, sizeof(now));
expiry.tv_sec += (d_l2_conn->diag_l2_p3max*2/3) / 1000;
expiry.tv_usec += ((d_l2_conn->diag_l2_p3max*2/3) % 1000) * 1000;
if (expiry.tv_usec > 1000000)
{
expiry.tv_sec ++;
expiry.tv_usec -= 1000000;
}
if (now.tv_sec > expiry.tv_sec)
expired = 1;
if (now.tv_sec == expiry.tv_sec)
{
if (now.tv_usec > expiry.tv_usec)
expired = 1;
}
/* And if expired, call the timeout routine */
if (expired && diag_protocols[d_l2_conn->diag_l2_protocol].diag_l2_proto_timeout)
diag_protocols[d_l2_conn->diag_l2_protocol].diag_l2_proto_timeout(d_l2_conn);
}
}
/*
* Add a message to the message list on the L2 connection
*/
void
diag_l2_addmsg(diag_l2_conn_t *d_l2_conn, diag_msg_t *msg)
{
diag_msg_t *tmsg = d_l2_conn->diag_msg;
if (d_l2_conn->diag_msg == NULL)
{
d_l2_conn->diag_msg = msg;
d_l2_conn->diag_msg->mcnt = 1;
return;
}
/* Add to end of list */
while (tmsg)
{
if (tmsg->next == NULL)
{
msg->next = NULL;
tmsg->next = msg;
d_l2_conn->diag_msg->mcnt ++;
break;
}
tmsg = tmsg->next;
}
}
/************************************************************************/
/* PUBLIC Interface starts here */
/************************************************************************/
/*
* Init called to initialise local structures, and same for layers below
*/
int diag_l2_init()
{
diag_proto_t *dp;
int i;
if (diag_l2_debug & DIAG_DEBUG_INIT)
fprintf(stderr,"%s: diag_l2_init called\n", DIAG_MODULE);
if ( diag_l2_init_done )
return(0);
diag_l2_init_done = 1;
memset(diag_l2_conbyid, 0, sizeof(diag_l2_conbyid));
/*
* Sanity check, check that the protocol table is in numeric order
*/
for (i=0, dp = diag_protocols; i<ARRAY_SIZE(diag_protocols); i++, dp++)
{
if (dp->diag_id != i)
{
fprintf(stderr,"ERROR: Diag protocols defined wrongly\n");
fprintf(stderr,"ERROR: element %d value %d\n",
i, dp->diag_id);
}
}
/*
* And go do the layer 1 init
*/
return(diag_l1_init());
}
/*
* Open a link to a Layer 1 device, returns fd, if device is already
* open, close and then re-open it (as we need to pass it a new "protocol"
* field if the l1 protocol is different
*
* The ID is the interface number, for instance COM0 may be ID 0, COM1 = ID 1
* etc
*
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -