/***********************************************************************
 *
 * Copyrigth: Karl Hammar, Aspö Data, LGPL
 *
 */

#ifndef __MODBUS_H__
#define __MODBUS_H__

#include <aspoutil.h>

/*
 For proto TCP, (MBAP is in page 5) see:
http://www.modbus.org/docs/Modbus_Messaging_Implementation_Guide_V1_0b.pdf

 For the other two, see:
http://www.modbus.org/docs/PI_MBUS_300.pdf
 */

/* transport protocols used/supported */
extern int modbus_protocol;
#define MODBUS_PROTO_RTU	0
#define MODBUS_PROTO_ASCII	1
#define MODBUS_PROTO_TCP	2 /* also for UDP */

/* framing start or header bytes */
#define MODBUS_HDRSZ_RTU	1 /* (address & 0xff) */
#define MODBUS_HDRSZ_ASCII	3 /* uint8_t address = ??; printf(";%02hhx", address); */
#define MODBUS_HDRSZ_MBAP	7 /* also for UDP, MBAP hdr: uint8_t mbap[7] = { tid_hi, tid_lo, 0, 0, len_hi, len_lo, uid }; */

/* framing end or trailing bytes */
#define MODBUS_ENDSZ_RTU	2 /* 2bytes CRC */
#define MODBUS_ENDSZ_ASCII	2 /* 2bytes LRC */
#define MODBUS_ENDSZ_MBAP	0 /* no trailer */


/* http://www.modbus.org/docs/Modbus_Application_Protocol_V1_1b.pdf
   6 Function codes descriptions
   Annex A (Informative): MODBUS RESERVED FUNCTION CODES, SUBCODES AND MEI TYPES

   (Old http://www.modbus.org/docs/PI_MBUS_300.pdf Chapter 2 Data and Control Functions)

   a 8 bit value
*/
extern struct intstr modbus_function[];

/* http://www.modbus.org/docs/Modbus_Application_Protocol_V1_1b.pdf
   6.8.1 Sub-function codes supported by the serial line devices

   (Old http://www.modbus.org/docs/PI_MBUS_300.pdf Chapter 3 Diagnostic Subfunctions)

   a 16 bit value
*/
extern struct intstr modbus_Diagnostic_Subfunctions[];

/* http://www.modbus.org/docs/Modbus_Application_Protocol_V1_1b.pdf
   6.19 43 ( 0x2B) Encapsulated Interface Transport
   Annex A (Informative): MODBUS RESERVED FUNCTION CODES, SUBCODES AND MEI TYPES

   a 8 bit value
 */
extern struct intstr modbus_MEI_Subfunctions[];

/* http://www.modbus.org/docs/Modbus_Application_Protocol_V1_1b.pdf
   7 MODBUS Exception Responses

   (Old http://www.modbus.org/docs/PI_MBUS_300.pdf Appendix A)

   a 8 bit value
*/ 
extern struct intstr modbus_Exception_Codes[];

/* length of request package, for the MODBUS_PROTO_ASCII, double theese walues */
#define PDUSZ_Unknown				-1
#define PDUSZ_MAX				253

#define PDUSZ_01_Read_Coils			5
#define PDUSZ_02_Read_Discrete_Inputs		5
#define PDUSZ_03_Read_Holding_Registers		5
#define PDUSZ_04_Read_Input_Registers		5
#define PDUSZ_05_Write_Single_Coil		5
#define PDUSZ_06_Write_Single_Register		5
#define PDUSZ_07_Read_Exception_Status		1
#define PDUSZ_08_Diagnostics(n)			(3 + 2*(n)) /* see doc. */
#define PDUSZ_09_Program_484			PDUSZ_Unknown
#define PDUSZ_0A_Poll_484			PDUSZ_Unknown
#define PDUSZ_0B_Get_Comm_Event_Counter		1
#define PDUSZ_0C_Get_Comm_Event_Log		1
#define PDUSZ_0D_Program_Controller		PDUSZ_Unknown
#define PDUSZ_0E_Poll_Controller		PDUSZ_Unknown
#define PDUSZ_0F_Write_Multiple_Coils(n)	(6 + ((n)+7)/8) /* n = number of coils (bits) */
#define PDUSZ_10_Write_Multiple_registers(n)	(6 + 2*(n)) /* n = number of registers (16 bit values) */
#define PDUSZ_11_Report_Slave_ID		1
#define PDUSZ_12_Program_884_M84		PDUSZ_Unknown
#define PDUSZ_13_Reset_Comm_Link		PDUSZ_Unknown
#define PDUSZ_14_Read_File_Record(n)		(2 + 7*(n)) /* n = number of record groups */
#define PDUSZ_15_Write_File_Record(n,m)		(2 + 7*(n) + 2*(m)) /* n = number of record groups, m = number of registers */
#define PDUSZ_16_Mask_Write_Register		7
#define PDUSZ_17_Read_Write_Multiple_registers(n)	(10 + 2*(n)) /* n = number of regitsters */
#define PDUSZ_18_Read_FIFO_Queue		3
#define PDUSZ_2B_Encapsulated_Interface_Transport(n)	(2 + (n)) /* n = number of MEI type specific data bytes */

/* function bytes as defines */
#define MODBUS_fRead_Coils		0x01
#define MODBUS_fRead_Discrete_Inputs	0x02
#define MODBUS_fRead_Holding_Registers	0x03
#define MODBUS_fRead_Input_Registers	0x04
#define MODBUS_fWrite_Single_Coil	0x05
#define MODBUS_fWrite_Single_Register	0x06
#define MODBUS_fRead_Exception_Status	0x07
#define MODBUS_fDiagnostics		0x08
#define MODBUS_fProgram_484		0x09
#define MODBUS_fPoll_484		0x0A
#define MODBUS_fGet_Comm_Event_Counter	0x0B
#define MODBUS_fGet_Comm_Event_Log	0x0C
#define MODBUS_fProgram_Controller	0x0D
#define MODBUS_fPoll_Controller		0x0E
#define MODBUS_fWrite_Multiple_Coils	0x0F
#define MODBUS_fWrite_Multiple_registers 0x10
#define MODBUS_fReport_Slave_ID		0x11
#define MODBUS_fProgram_884_M84		0x12
#define MODBUS_fReset_Comm_Link		0x13
#define MODBUS_fRead_File_Record	0x14
#define MODBUS_fWrite_File_Record	0x15
#define MODBUS_fMask_Write_Register	0x16
#define MODBUS_fRead_Write_Multiple_registers 0x17
#define MODBUS_fRead_FIFO_Queue		0x18
#define MODBUS_fEncapsulated_Interface_Transport 0x2B


int modbus_mbapcheck(int sz, uint8_t *buf, uint8_t uid);
int modbus_mbap_mkhdr( size_t buflen, uint8_t *buf, uint16_t tid, uint16_t len, uint8_t uid);
int modbus_req_read( size_t buflen, uint8_t *buf, uint8_t func, uint16_t starting_address, uint16_t count);

#define modbus_req_Read_Coils(            len, buf, sta, cnt) modbus_req_read(len, buf, 0x01, sta, cnt)
#define modbus_req_Read_Discrete_Inputs(  len, buf, sta, cnt) modbus_req_read(len, buf, 0x02, sta, cnt)
#define modbus_req_Read_Holding_Registers(len, buf, sta, cnt) modbus_req_read(len, buf, 0x03, sta, cnt)
#define modbus_req_Read_Input_Registers(  len, buf, sta, cnt) modbus_req_read(len, buf, 0x04, sta, cnt)

#define MODBUS_WRREG(buf, val) ((buf)[0] = ((val)>>8) & 0xff, (buf)[1] = (val) * 0xff)

/* theese are suitable to put values into initializers */
#define MODBUS_INIT16(val) (((val)>>8) & 0xff), ((val) & 0xff)  /* e.g. uint8_t buf[2] = { MODBUS_REGINIT( val ) }; */
#define MODBUS_INITMBAP( tid, len, uid ) MODBUS_INIT16( tid ), 0x00, 0x00, MODBUS_INIT16( len ), uid
#define MODBUS_INITREQREAD( func, sta, cnt ) ((func) & 0xff), MODBUS_INIT16(sta), MODBUS_INIT16(cnt)
#define MODBUS_INITREQWRITE( func, adr, val ) MODBUS_INITREQREAD( func, adr, val )

#endif
