diff --git a/man/man3/hm2_pktuart_read.3hm2 b/man/man3/hm2_pktuart_read.3hm2 new file mode 100755 index 00000000000..b9fa037c677 --- /dev/null +++ b/man/man3/hm2_pktuart_read.3hm2 @@ -0,0 +1,69 @@ +\# Author Boris Skegin +\# Issued under the terms of the GPL v2 License or any later version +.TH hm2_pktuart_read "3hm2" "2016-02-26" "LinuxCNC Documentation" "Hostmot2" +.SH NAME + +hm2_pktuart_read \- read data from a Hostmot2 UART buffer + +.SH SYNTAX +.HP +int hm2_pktuart_read(char *name, unsigned char data[], u8 *num_frames, u16 *max_frame_length, u16 frame_sizes[]) + +.SH DESCRIPTION +\fBhm2_pktuart_read\fR reads data from the PktUART "name". +"name" is a unique string given to each PktUART during hostmot2 setup. The names of +the available channels are printed to standard output during the driver loading +process and take the form: +hm2_..pktuart. For example hm2_5i25.0.pktuart.0 + +This function reads a variable number of PktUART packets from the the specified +channel. It should be used inside a realtime HAL component registered with the +main hostmot2 driver using the function hm2_pktuart_setup in the setup +code. + +"(*num_frames)*(*max_frame_length)" should be <= sizeof "data", which one tries +to estimate or guess before calling the function. +If there are more bytes in the buffer than the size of data array is, then +RxArraySizeError is returned. + +Note that the PktUART MaxFrameSize is 1024 bytes as hard-coded in hostmot2.vhd . + + +.SH RETURN VALUE +Returns the number of bytes read on success and negative error codes on failure. + +"num_frames" which pointer is passed by value is set to the number of successfully +datagrams read. + +Negative error codes are: +.TP +.B -1 - low level read/write error +.TP +.B -EINVAL - any PktUART configuration error per instance +.TP +.B +.TP +.B -110 - RxStartbitError, Rx mode register error +.TP +.B -111 - RxOverrunError, Rx mode register error +.TP +.B -114 - RxRCFIFOError, Rx mode register error +.TP +.B +.TP +.B -1115 - RxPacketOverrrunError, Rx count register error +.TP +.B -1114 - RxPacketStartbitError, Rx count register error +.TP +.B +.TP +.B -1120 - RxPacketSizeZero, the size of the received packet is zero +.TP +.B -1140 - RxArraySizeError, data array is too small for all the data in the buffer + + +.SH SEE ALSO +.TP +.B man hm2_pktuart_setup, man hm2_pktuart_send +.TP +See src/hal/components/mesa_pktgyro_test.comp for an example usage. diff --git a/man/man3/hm2_pktuart_send.3hm2 b/man/man3/hm2_pktuart_send.3hm2 new file mode 100755 index 00000000000..492b16c44ec --- /dev/null +++ b/man/man3/hm2_pktuart_send.3hm2 @@ -0,0 +1,49 @@ +\# Author Boris Skegin +\# Issued under the terms of the GPL v2 License or any later version +.TH hm2_pktuart_send "3hm2" "2016-02-29" "LinuxCNC Documentation" "Hostmot2" +.SH NAME + +hm2_pktuart_send \- write data to a Hostmot2 PktUART + +.SH SYNTAX +.HP +int hm2_uart_send(char *name, unsigned char data[], u8 *num_frames, u16 frame_sizes[]) + +.SH DESCRIPTION +\fBhm2_pktuart_send\fR writes "num_frames" of data to the PktUART "name" from the +buffer "data" with frame sizes preset in "frame_sizes[]" array. +"frame_sizes[]" array should not have more than 16 elements as this is the highest +number of frames that can be sent out in the so called "burst mode". + +Note that the PktUART MaxFrameSize is 1024 bytes as hard-coded in hostmot2.vhd . + +"name" is a unique string given to each PktUART during hostmot2 setup. The names of +the available channels are printed to standard output during the driver loading +process and take the form: +hm2_..pktuart. For example hm2_5i25.0.pktuart.0 . + +This function sends a variable number of PktUART packets (less or equal 16) from +the the specified channel. It should be used inside a realtime HAL component +registered with the main hostmot2 driver using the function hm2_pktuart_setup +in the setup code. + +.SH RETURN VALUE +Returns the number of bytes sent on success and negative error codes on failure. + +"num_frames" which pointer is passed by value is set to the number of successfully +datagrams sent out. + +Negative error codes are: +.TP +.B -1 - low level read/write error +.TP +.B -EINVAL - any PktUART configuration error per instance +.TP +.B -214 - TxSCFIFOError, Tx Send Count FIFO Error + + + +.SH SEE ALSO +.B man hm2_pktuart_setup, man hm2_pktuart_read +.TP +See src/hal/components/mesa_pktgyro_test.comp for an example usage. diff --git a/man/man3/hm2_pktuart_setup.3hm2 b/man/man3/hm2_pktuart_setup.3hm2 new file mode 100755 index 00000000000..0282a6b5d97 --- /dev/null +++ b/man/man3/hm2_pktuart_setup.3hm2 @@ -0,0 +1,102 @@ +\# Author Boris Skegin +\# Issued under the terms of the GPL v2 License or any later version +.TH hm2_pktuart_setup "3hm2" "2016-02-29" "LinuxCNC Documentation" "Hostmot2" +.SH NAME + +hm2_pktuart_setup \- setup a Hostmot2 PktUART instance +.SH SYNTAX +.HP +int hm2_pktuart_setup(char *name, int bitrate, s32 tx_mode, s32 rx_mode, int txclear, int rxclear) + +.SH DESCRIPTION +\fBhm2_pktuart_setup\fR Setup the bitrate for the PktUART named "name". +"name" is a unique string given to each PktUART during hostmot2 +setup. The names of the available UARTs are printed to standard output during +the driver loading process and take the form: +hm2_..pktuart. For example hm2_5i25.0.pktuart.0 . + +Hostmot2 UARTs are good to about 10 Mb/sec, but higher data rates (with any UART) +trade speed for susceptibility to impulse noise. + + +The PktUART function allows different RX and TX bitrates, but that is not currently +supported by this driver. + + +tx_mode is bit mask defined in the Hostmot2 regmap: +.TP +.B Bit 21 FrameBuffer Has Data +.TP +.B Bits 20..16 Frames to send +.TP +.B Bits 15..8 InterFrame delay in bit times +.TP +.B Bit 7 Send busy, Transmit Logic active +.TP +.B Bit 6 Drive Enable bit (enables external RS-422/485 Driver when set) +.TP +.B Bit 5 Drive enable Auto (Automatic external drive enable) +.TP +.B Bit 4 SCFIFO Error +.TP +.B Bits 3..0 Drive enable delay (delay from asserting drive enable + to start of data transmit. In CLock Low periods + + + + +.TP +rx_mode is bit mask defined in the Hostmot2 regmap: +.TP +.B Bit 21 FrameBuffer has data +.TP +.B Bits 20..16 Frames received +.TP +.B Bits 15..8 InterFrame delay in bit times +.TP +.B Bit 7 Rx Logic active +.TP +.B Bit 6 RXMask +.TP +.B Bit 5 Unused +.TP +.B Bit 4 RCFIFO Error +.TP +.B Bit 3 RXEnable (must be set to receive packets) +.TP +.B Bit 2 RXMask Enable (enables input data masking when transmitting) +.TP +.B Bit 1 Overrun error (no stop bit when expected) (sticky) +.TP +.B Bit 0 False Start bit error (sticky) + +.PP +rx_mode and tx_mode registers are currently write-only. +One can get the instance number of a PktUART instance +with the help of hm2_get_pktuart function in order +to read and write to Rx and Tx registers. + +.PP +To write only to the tx_mode DriveEnable bit call this function with the bitrate +unchanged and -1 as the rx_mode +To change bitrate without altering mode settings send -1 to both modes. +.PP +txclear==1 aborts any sends in process, clears the data FIFO and +clears the send count FIFO. +.PP +rxclear==1 aborts any receives in process, clears the data FIFO and +clears the receive count FIFO. +.PP +txclear!=1 or rxclear!=1 lets the corresponding registers unchanged. + +.SH RETURN VALUE +Returns 0 on success and -1 or -EINVAL on failure. + +.SH SEE ALSO +.B man hm2_pktuart_send, man hm2_pktuart_read +.TP +See src/hal/components/mesa_pktgyro_test.comp for an example usage. + +.SH FUTURE DEVELOPMENT +This function is subject to change as digital filters will be added on the Rx UART, +and bit rate register will gain a 12 bit field for the input filter constant. diff --git a/src/Makefile b/src/Makefile index 15ce58a7408..2e0101c1127 100755 --- a/src/Makefile +++ b/src/Makefile @@ -1345,8 +1345,9 @@ hostmot2-objs += \ hal/drivers/mesa-hostmot2/tp_pwmgen.o \ hal/drivers/mesa-hostmot2/sserial.o \ hal/drivers/mesa-hostmot2/stepgen.o \ - hal/drivers/mesa-hostmot2/bspi.o \ - hal/drivers/mesa-hostmot2/uart.o \ + hal/drivers/mesa-hostmot2/bspi.o \ + hal/drivers/mesa-hostmot2/uart.o \ + hal/drivers/mesa-hostmot2/pktuart.o \ hal/drivers/mesa-hostmot2/watchdog.o \ hal/drivers/mesa-hostmot2/pins.o \ hal/drivers/mesa-hostmot2/dpll.o \ diff --git a/src/hal/components/mesa_pktgyro_test.comp b/src/hal/components/mesa_pktgyro_test.comp new file mode 100755 index 00000000000..34495ef7d50 --- /dev/null +++ b/src/hal/components/mesa_pktgyro_test.comp @@ -0,0 +1,295 @@ +component mesa_pktgyro_test "PktUART simple test with Microstrain 3DM-GX3-15 gyro"; + +description """This component is written in order to test +the PktUART driver for Mesa. It resembles partly Andy Pugh's mesa_uart.comp . + +This module uses the names= mode of loadrt declaration to specifiy which PktUART +instances to enable. A check is included to ensure that the count= option is +not used instead. +For simplicity we test only one PktUART instance, therefore load the component +like this: + +\\fB loadrt mesa_uart names=hm2_5i25.0.pktuart.0\\fR + +The PktUART instance names are printed to the dmesg buffer during the Hostmot2 +setup sequence, one for each PktUART instance included in the bitfile loaded to +each installed card during the Hostmot2 setup sequence. Type "dmesg" at the +terminal prompt to view the output. +If you want to work with more than one PktUART instance, consult Andy Pugh's +mesa_uart.comp + +The component exports only one function, namely receive, which needs to be added +to a realtime thread. +To test this component set DEBUG=5 before and execute this HAL script: +\\fB loadrt hostmot2\\fR +\\fB loadrt hm2_pci\\fR +\\fB loadrt mesa_pktgyro_test names=hm2_5i25.0.pktuart.0\\fR +\\fB loadrt threads name1=test1 period1=10000000\\fR +\\fB addf hm2_5i25.0.pktuart.0.receive test1\\fR +\\fB start\\fR + +Check linuxcnc.log for debug output. + +"""; + + + +/* +******************************************* +In order to compile: +Copy to machinekit/src/hal/components +../../../bin/comp --install mesa_pktgyro_test.comp +******************************************* +*/ + + +author "Boris Skegin"; +license "GPL"; + +include ; +include "../drivers/mesa-hostmot2/hostmot2.h"; + + +pin out s32 rxbytes "Number of Bytes received or negative Error code"; + +variable char *name; // PktUART name + +option extra_setup yes; + +function receive; + +;; + + + +#define BAUDRATE (115200) + + + +/* This uses the RTAPI_MP_ARRAY_STRING macro to load the list of PktUART channels +into an array. This is copied into the *name string of each */ + +char *pktuart_chans[4] = {0,}; +RTAPI_MP_ARRAY_STRING(pktuart_chans, 2, "PktUART Channel names"); + +static hostmot2_t* hm2=NULL; + + + +FUNCTION(receive){ + u16 max_frame_length = 20; + u8 num_frames = 20; + unsigned char Replyd3[num_frames*max_frame_length]; + u16 frame_sizes3[20]; + rxbytes=hm2_pktuart_read(name, Replyd3, &num_frames, &max_frame_length, frame_sizes3); + rtapi_print_msg(RTAPI_MSG_INFO, "PktUART receive: got %d bytes, %d frames\n", rxbytes, num_frames); + + //Print out the actual frame sizes + int i; + for(i=0;iclock_freq); + Bitrate is (RXBitrate_Register_Value/1048576)*ClockLow */ + /* http://freeby.mesanet.com/regmap + The PktUARTxMode register is used for setting and checking the + PktUARTx's operation mode, timing and status: + Bit 21 FrameBuffer Has Data + Bits 20..16 Frames to send + Bits 15..8 InterFrame delay in bit times + Bit 7 Tr Logic active (not an error) + Bit 6 Drive Enable bit (enables external RS-422/485 Driver when set) + Bit 5 Drive enable Auto (Automatic external drive enable) + Bit 4 unused + Bits 3..0 Drive enable delay (delay from asserting drive enable + to start of data transmit. In CLock Low periods + */ + + /* http://freeby.mesanet.com/regmap + The PktUARTrMode register is used for setting and checking the PktUARTr's + operation mode, timing, and status + Bit 21 FrameBuffer has data + Bits 20..16 Frames received + Bits 15..8 InterFrame delay in bit times + Bit 7 Rx Logic active ( not an error) + Bit 6 RXMask + Bit 5 Unused + Bit 4 RCFIFO Error + Bit 3 RXEnable (must be set to receive packets) + Bit 2 RXMask Enable (enables input data masking when transmitting) + Bit 1 Overrun error (no stop bit when expected) (sticky) + Bit 0 False Start bit error (sticky) + */ + + + /* + In case our device is streaming data from the very beginning, + at first we do not set RXEnable but clear Rx and Tx registers. + Then we read out whatever is in the buffer, send the DISABLE STREAM + datagram and only then set RXEnable bit. + */ + int retval=hm2_pktuart_setup(name, BAUDRATE , 0x0ff20, 0x007f00,1,1); + if (retval<0) + { + rtapi_print_msg(1, "PktUART for gyro setup problem: %d\n", retval); + return -1; + } + + /* + We expect the max frame length to be 58 byte if the gyro is + streaming data from the beggining, + but in case the InterFrame delay is not appropriate, + the frame size can be longer. + Anyway as long as the array size we pass to hm2_pktuart_read + is big enough, we can read everything which is in the Rx buffer. + */ + u16 max_frame_length = 58*2; + u8 num_frames = 20; + + // If Rx buffer <= 1024 bytes, than 2*58*20 bytes of read1 array are enough + unsigned char read1[num_frames*max_frame_length]; + u16 read1_sizes[num_frames]; + + // read out as many frames as possible + retval=hm2_pktuart_read(name, read1, &num_frames, &max_frame_length, read1_sizes); + + rtapi_print_msg(RTAPI_MSG_INFO, "PktUART after first read: got %d bytes\n", retval); + + + + //Print out the actual frame sizes + u16 i; + for(i=0;ipktuart.num_instances > 0) { + for (i = 0; i < (*hm2)->pktuart.num_instances ; i++) { + if (!strcmp((*hm2)->pktuart.instance[i].name, name)) {return i;} + } + } + } + return -1; +} + + + + + EXPORT_SYMBOL_GPL(hm2_get_sserial); // returns a pointer to a remote struct hm2_sserial_remote_t *hm2_get_sserial(hostmot2_t** hm2, char *name){ @@ -255,6 +274,8 @@ const char *hm2_get_general_function_name(int gtag) { case HM2_GTAG_BSPI: return "Buffered SPI Interface"; case HM2_GTAG_UART_RX: return "UART Receive Channel"; case HM2_GTAG_UART_TX: return "UART Transmit Channel"; + case HM2_GTAG_PKTUART_RX: return "PktUART Receive Channel"; + case HM2_GTAG_PKTUART_TX: return "PktUART Transmit Channel"; case HM2_GTAG_HM2DPLL: return "Hostmot2 DPLL"; default: { static char unknown[100]; @@ -324,6 +345,7 @@ static int hm2_parse_config_string(hostmot2_t *hm2, char *config_string) { hm2->config.stepgen_width = 2; // To avoid nasty surprises with table mode hm2->config.num_bspis = -1; hm2->config.num_uarts = -1; + hm2->config.num_pktuarts = -1; hm2->config.num_dplls = -1; hm2->config.num_leds = -1; hm2->config.enable_raw = 0; @@ -418,6 +440,10 @@ static int hm2_parse_config_string(hostmot2_t *hm2, char *config_string) { token += 10; hm2->config.num_uarts = simple_strtol(token, NULL, 0); + } else if (strncmp(token, "num_pktuarts=", 13) == 0) { + token += 13; + hm2->config.num_pktuarts = simple_strtol(token, NULL, 0); + } else if (strncmp(token, "num_leds=", 9) == 0) { token += 9; hm2->config.num_leds = simple_strtol(token, NULL, 0); @@ -459,6 +485,7 @@ static int hm2_parse_config_string(hostmot2_t *hm2, char *config_string) { HM2_DBG(" num_stepgens=%d\n", hm2->config.num_stepgens); HM2_DBG(" num_bspis=%d\n", hm2->config.num_bspis); HM2_DBG(" num_uarts=%d\n", hm2->config.num_uarts); + HM2_DBG(" num_pktuarts=%d\n", hm2->config.num_pktuarts); HM2_DBG(" enable_raw=%d\n", hm2->config.enable_raw); HM2_DBG(" firmware=%s\n", hm2->config.firmware ? hm2->config.firmware : "(NULL)"); @@ -905,6 +932,11 @@ static int hm2_parse_module_descriptors(hostmot2_t *hm2) { md_accepted = hm2_uart_parse_md(hm2, md_index); break; + case HM2_GTAG_PKTUART_RX: + case HM2_GTAG_PKTUART_TX: + md_accepted = hm2_pktuart_parse_md(hm2, md_index); + break; + case HM2_GTAG_HM2DPLL: md_accepted = hm2_dpll_parse_md(hm2, md_index); break; diff --git a/src/hal/drivers/mesa-hostmot2/hostmot2.h b/src/hal/drivers/mesa-hostmot2/hostmot2.h index 546e404fc0c..d536bc70551 100755 --- a/src/hal/drivers/mesa-hostmot2/hostmot2.h +++ b/src/hal/drivers/mesa-hostmot2/hostmot2.h @@ -100,7 +100,7 @@ char **argv_split(gfp_t gfp, const char *str, int *argcp); // -// Module Descriptor constants +// Module Descriptor constants from IDROMConst.vhd // #define HM2_GTAG_WATCHDOG (2) @@ -112,6 +112,8 @@ char **argv_split(gfp_t gfp, const char *str, int *argcp); #define HM2_GTAG_SSI (8) #define HM2_GTAG_UART_TX (9) #define HM2_GTAG_UART_RX (10) +#define HM2_GTAG_PKTUART_TX (27) // PktUART uses same addresses as normal UART with +#define HM2_GTAG_PKTUART_RX (28) // the assumption you would not use both in one config #define HM2_GTAG_TRANSLATIONRAM (11) #define HM2_GTAG_MUXED_ENCODER (12) #define HM2_GTAG_MUXED_ENCODER_SEL (13) @@ -834,6 +836,33 @@ typedef struct { u8 num_registers; } hm2_uart_t; +// +// PktUART +// + +typedef struct { + u32 clock_freq; + u32 bitrate; + u32 tx_fifo_count_addr; + u32 tx_bitrate_addr; + u32 tx_addr; + u32 tx_mode_addr; + u32 rx_fifo_count_addr; + u32 rx_bitrate_addr; + u32 rx_addr; + u32 rx_mode_addr; + char name[HAL_NAME_LEN+1]; +} hm2_pktuart_instance_t; + +typedef struct { + int version; + int num_instances; + hm2_pktuart_instance_t *instance; + u8 instances; + u8 num_registers; + struct rtapi_heap *heap; +} hm2_pktuart_t; + // // HM2DPLL // @@ -1002,6 +1031,7 @@ typedef struct { int num_sserials; int num_bspis; int num_uarts; + int num_pktuarts; int num_dplls; char sserial_modes[4][8]; int enable_raw; @@ -1041,6 +1071,7 @@ typedef struct { hm2_sserial_t sserial; hm2_bspi_t bspi; hm2_uart_t uart; + hm2_pktuart_t pktuart; hm2_ioport_t ioport; hm2_watchdog_t watchdog; hm2_dpll_t dpll; @@ -1087,6 +1118,7 @@ void hm2_print_modules(hostmot2_t *hm2); hm2_sserial_remote_t *hm2_get_sserial(hostmot2_t **hm2, char *name); int hm2_get_bspi(hostmot2_t **hm2, char *name); int hm2_get_uart(hostmot2_t **hm2, char *name); +int hm2_get_pktuart(hostmot2_t **hm2, char *name); // @@ -1261,13 +1293,28 @@ int hm2_uart_parse_md(hostmot2_t *hm2, int md_index); void hm2_uart_print_module(hostmot2_t *hm2); void hm2_uart_cleanup(hostmot2_t *hm2); void hm2_uart_write(hostmot2_t *hm2); -void hm2_uart_force_write(hostmot2_t *hm2); +void hm2_uart_force_write(hostmot2_t *hm2); void hm2_uart_prepare_tram_write(hostmot2_t *hm2, long period); void hm2_uart_process_tram_read(hostmot2_t *hm2, long period); int hm2_uart_setup(char *name, int bitrate, s32 tx_mode, s32 rx_mode); int hm2_uart_send(char *name, unsigned char data[], int count); int hm2_uart_read(char *name, unsigned char data[]); +// +// PktUART functions +// + +int hm2_pktuart_parse_md(hostmot2_t *hm2, int md_index); +void hm2_pktuart_print_module(hostmot2_t *hm2); +void hm2_pktuart_cleanup(hostmot2_t *hm2); +void hm2_pktuart_write(hostmot2_t *hm2); +void hm2_pktuart_force_write(hostmot2_t *hm2); // ?? +void hm2_pktuart_prepare_tram_write(hostmot2_t *hm2, long period); //?? +void hm2_pktuart_process_tram_read(hostmot2_t *hm2, long period); // ?? +int hm2_pktuart_setup(char *name, int bitrate, s32 tx_mode, s32 rx_mode, int txclear, int rxclear); +int hm2_pktuart_send(char *name, unsigned char data[], u8 *num_frames, u16 frame_sizes[]); +int hm2_pktuart_read(char *name, unsigned char data[], u8 *num_frames, u16 *max_frame_length, u16 frame_sizes[]); + // // hm2dpll functions // diff --git a/src/hal/drivers/mesa-hostmot2/pins.c b/src/hal/drivers/mesa-hostmot2/pins.c index 7027563ab21..5b478e30fc3 100755 --- a/src/hal/drivers/mesa-hostmot2/pins.c +++ b/src/hal/drivers/mesa-hostmot2/pins.c @@ -217,6 +217,18 @@ static const char* hm2_get_pin_secondary_name(hm2_pin_t *pin) { } break; + case HM2_GTAG_PKTUART_RX: + switch (sec_pin) { + case 0x1: return "RX Data"; + } + break; + case HM2_GTAG_PKTUART_TX: + switch (sec_pin) { + case 0x1: return "TX Data"; + case 0x2: return "Drv Enable"; + } + break; + case HM2_GTAG_DPLL: // Not Supported Currently switch (sec_pin) { case 0x1: return "SynchIn"; @@ -586,6 +598,8 @@ void hm2_configure_pins(hostmot2_t *hm2) { hm2_pins_allocate_all(hm2, HM2_GTAG_BSPI, hm2->bspi.num_instances); hm2_pins_allocate_all(hm2, HM2_GTAG_UART_RX, hm2->uart.num_instances); hm2_pins_allocate_all(hm2, HM2_GTAG_UART_TX , hm2->uart.num_instances); + hm2_pins_allocate_all(hm2, HM2_GTAG_PKTUART_RX, hm2->pktuart.num_instances); + hm2_pins_allocate_all(hm2, HM2_GTAG_PKTUART_TX , hm2->pktuart.num_instances); hm2_pins_allocate_all(hm2, HM2_GTAG_SMARTSERIAL, hm2->sserial.num_instances); // muxed encoder gets the sel pins hm2_pins_allocate_all(hm2, HM2_GTAG_MUXED_ENCODER_SEL, hm2->encoder.num_instances); diff --git a/src/hal/drivers/mesa-hostmot2/pktuart.c b/src/hal/drivers/mesa-hostmot2/pktuart.c new file mode 100755 index 00000000000..c21956fcaf4 --- /dev/null +++ b/src/hal/drivers/mesa-hostmot2/pktuart.c @@ -0,0 +1,548 @@ +// +// Copyright (C) 2011 Andy Pugh, 2016 Boris Skegin +// +// 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 +// MERinstTABILITY 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +// + +#include "config_module.h" +#include RTAPI_INC_SLAB_H +#include "rtapi.h" +#include "rtapi_app.h" +#include "rtapi_string.h" +#include "rtapi_math.h" +#include "hal.h" +#include "hostmot2.h" + + +#define MaxTrFrames (16) // Send counts are written to 16 deep FIFO, burst mode + +// Tx mode register errors +#define TxSCFIFOError (214) // Tx Send Count FIFO Error + + +// Rx mode register errors +#define RxRCFIFOError (114) // RCFIFO Error, Bit 4 +#define RxOverrunError (111) // Overrun error (no stop bit when expected) (sticky), Bit 1 +#define RxStartbitError (110) // False Start bit error (sticky), Bit 0 + +// Rx count register errors +#define RxPacketOverrrunError (1115) // Bit 15 Overrun error in this packet +#define RxPacketStartbitError (1114) // Bit 14 False Start bit error in this packet + +// the next two error conditions +// are very unprobable, but we consider them nevertheless +#define RxPacketSizeZero (1120) // the length of the received packet is 0 +#define RxArraySizeError (1140) // sizeof(data array)= num_frames*max_frame_length is too small for all the data in the buffer + +int hm2_pktuart_parse_md(hostmot2_t *hm2, int md_index) +{ + // All this function actually does is allocate memory + // and give the uart modules names. + + + // + // some standard sanity checks + // + + int i, r = -EINVAL; + hm2_module_descriptor_t *md = &hm2->md[md_index]; + static int last_gtag = -1; + + //The PktUART declares a TX and RX module separately + + if (!hm2_md_is_consistent_or_complain(hm2, md_index, 0, 4, 4, 0x000F)) { + HM2_ERR("inconsistent Module Descriptor!\n"); + return -EINVAL; + } + + if (hm2->pktuart.num_instances > 1 && last_gtag == md->gtag) { + HM2_ERR( + "found duplicate Module Descriptor for %s (inconsistent " + "firmware), not loading driver %i %i\n", + hm2_get_general_function_name(md->gtag), md->gtag, last_gtag + ); + return -EINVAL; + } + last_gtag = md->gtag; + + if (hm2->config.num_pktuarts > md->instances) { + HM2_ERR( + "config defines %d pktuarts, but only %d are available, " + "not loading driver\n", + hm2->config.num_pktuarts, + md->instances + ); + return -EINVAL; + } + + if (hm2->config.num_pktuarts == 0) { + return 0; + } + + // + // looks good, start, or continue, initializing + // + + if (hm2->pktuart.num_instances == 0){ + if (hm2->config.num_pktuarts == -1) { + hm2->pktuart.num_instances = md->instances; + } else { + hm2->pktuart.num_instances = hm2->config.num_pktuarts; + } + + hm2->pktuart.instance = (hm2_pktuart_instance_t *)hal_malloc(hm2->pktuart.num_instances + * sizeof(hm2_pktuart_instance_t)); + if (hm2->pktuart.instance == NULL) { + HM2_ERR("out of memory!\n"); + r = -ENOMEM; + goto fail0; + } + } + + for (i = 0 ; i < hm2->pktuart.num_instances ; i++){ + hm2_pktuart_instance_t *inst = &hm2->pktuart.instance[i]; + // For the time being we assume that all PktUARTS come on pairs + if (inst->clock_freq == 0){ + inst->clock_freq = md->clock_freq; + r = rtapi_snprintf(inst->name, sizeof(inst->name), "%s.pktuart.%01d", hm2->llio->name, i); + HM2_PRINT("created PktUART Interface function %s.\n", inst->name); + } + if (md->gtag == HM2_GTAG_PKTUART_TX){ + inst->tx_addr = md->base_address + i * md->instance_stride; + inst->tx_fifo_count_addr = (md->base_address + + md->register_stride + + i * md->instance_stride); + inst->tx_bitrate_addr = (md->base_address + + 2 * md->register_stride + + i * md->instance_stride); + inst->tx_mode_addr = (md->base_address + + 3 * md->register_stride + +i * md->instance_stride); + } + else if (md->gtag == HM2_GTAG_PKTUART_RX){ + inst->rx_addr = md->base_address + i * md->instance_stride; + inst->rx_fifo_count_addr = (md->base_address + + md->register_stride + + i * md->instance_stride); + inst->rx_bitrate_addr = (md->base_address + + 2 * md->register_stride + + i * md->instance_stride); + inst->rx_mode_addr = (md->base_address + + 3 * md->register_stride + +i * md->instance_stride); + } + else{ + HM2_ERR("Something very wierd happened"); + goto fail0; + } + + } + + return hm2->pktuart.num_instances; +fail0: + return r; +} + +EXPORT_SYMBOL_GPL(hm2_pktuart_setup); +// use -1 for tx_mode and rx_mode to leave the mode unchanged +// use 1 for txclear or rxclear to issue a clear command for Tx or Rx registers +int hm2_pktuart_setup(char *name, int bitrate, s32 tx_mode, s32 rx_mode, int txclear, int rxclear){ + hostmot2_t *hm2; + hm2_pktuart_instance_t *inst = 0; + u32 buff; + int i,r; + + i = hm2_get_pktuart(&hm2, name); + if (i < 0){ + HM2_ERR_NO_LL("Can not find PktUART instance %s.\n", name); + return -EINVAL; + } + inst = &hm2->pktuart.instance[i]; + + buff = (u32)((bitrate * 1048576.0)/inst->clock_freq); //20 bits in this version + r = 0; + if (buff != inst->bitrate){ + inst->bitrate = buff; + r += hm2->llio->write(hm2->llio, inst->rx_bitrate_addr, &buff, sizeof(u32)); + r += hm2->llio->write(hm2->llio, inst->tx_bitrate_addr, &buff, sizeof(u32)); + + /* http://freeby.mesanet.com/regmap + The PktUARTx/PktUARTr mode register has a special data command that clears the PktUARTx/PktUARTr + Clearing aborts any sends/receives in process, clears the data FIFO and + clears the send count FIFO. To issue a clear command, you write 0x80010000 + to the PktUARTx/PktUARTr mode register. + */ + buff = 0x80010000; + if (txclear==1) + r += hm2->llio->write(hm2->llio, inst->tx_mode_addr, &buff, sizeof(u32)); // clear sends, data FIFO and count register + if (rxclear==1) + r += hm2->llio->write(hm2->llio, inst->rx_mode_addr, &buff, sizeof(u32)); // clear receives, data FIFO and count register + } + + /* http://freeby.mesanet.com/regmap + The PktUARTxMode register is used for setting and checking the + PktUARTx's operation mode, timing and status: + Bit 21 FrameBuffer Has Data + Bits 20..16 Frames to send + Bits 15..8 InterFrame delay in bit times + Bit 7 Transmit Logic active, not an error + Bit 6 Drive Enable bit (enables external RS-422/485 Driver when set) + Bit 5 Drive enable Auto (Automatic external drive enable) + Bit 4 unused + Bits 3..0 Drive enable delay (delay from asserting drive enable + to start of data transmit). In CLock Low periods + */ + if (tx_mode >= 0) { + buff = ((u32)tx_mode) & 0xffff; + r += hm2->llio->write(hm2->llio, inst->tx_mode_addr, &buff, sizeof(u32)); + } + + /* http://freeby.mesanet.com/regmap + The PktUARTrMode register is used for setting and checking the PktUARTr's + operation mode, timing, and status + Bit 21 FrameBuffer has data + Bits 20..16 Frames received + Bits 15..8 InterFrame delay in bit times + Bit 7 Receive Logic active, not an error + Bit 6 RXMask + Bit 5 Unused + Bit 4 RCFIFO Error + Bit 3 RXEnable (must be set to receive packets) + Bit 2 RXMask Enable (enables input data masking when transmitting) + Bit 1 Overrun error (no stop bit when expected) (sticky) + Bit 0 False Start bit error (sticky) + */ + if (rx_mode >= 0) { + buff = ((u32)rx_mode) & 0xffff; + r += hm2->llio->write(hm2->llio, inst->rx_mode_addr, &buff, sizeof(u32)); + } + + if (r < 0) { + HM2_ERR("PktUART: hm2->llio->write failure %s\n", name); + return -1; + } + + return 0; +} + + +EXPORT_SYMBOL_GPL(hm2_pktuart_send); +int hm2_pktuart_send(char *name, unsigned char data[], u8 *num_frames, u16 frame_sizes[]) +{ + hostmot2_t *hm2; + u32 buff; + int r, c; + int inst; + + inst = hm2_get_pktuart(&hm2, name); + if (inst < 0){ + HM2_ERR_NO_LL("Can not find PktUART instance %s.\n", name); + return -EINVAL; + } + if (hm2->pktuart.instance[inst].bitrate == 0){ + HM2_ERR("%s has not been configured.\n", name); + return -EINVAL; + } + + c = 0; + u16 count = 0; + /* + we work with nframes as a local copy of num_frames, + so that we can return the num_frames sent out + in case of SCFIFO error. + */ + u8 nframes = *num_frames; + + /* http://freeby.mesanet.com/regmap + Send counts are written to 16 deep FIFO allowing up to 16 packets to be + sent in a burst (subject to data FIFO depth limits). + */ + // Test if num_frames <= MaxTrFrames + if ((*num_frames) > MaxTrFrames){ + nframes = MaxTrFrames; + } else{ + nframes = *num_frames; + } + + *num_frames = 0; + + u8 i; + for (i = 0; i < nframes; i++){ + count = count + frame_sizes[i]; + while (c < count - 3){ + buff = (data[c] + + (data[c+1] << 8) + + (data[c+2] << 16) + + (data[c+3] << 24)); + r = hm2->llio->write(hm2->llio, hm2->pktuart.instance[inst].tx_addr, + &buff, sizeof(u32)); + if (r < 0) { + HM2_ERR("%s send: hm2->llio->write failure\n", name); + return -1; + } + c = c + 4; + } + + + // Now write the last bytes with bytes number < 4 + switch(count - c){ + case 0: + break; + case 1: + buff = data[c]; + r = hm2->llio->write(hm2->llio, hm2->pktuart.instance[inst].tx_addr, + &buff, sizeof(u32)); + if (r < 0){ + HM2_ERR("%s send: hm2->llio->write failure\n", name); + return -1; + } + break; + case 2: + buff = (data[c] + + (data[c+1] << 8)); + r = hm2->llio->write(hm2->llio, hm2->pktuart.instance[inst].tx_addr, + &buff, sizeof(u32)); + if (r < 0){ + HM2_ERR("%s send: hm2->llio->write failure\n", name); + return -1; + } + break; + case 3: + buff = (data[c] + + (data[c+1] << 8) + + (data[c+2] << 16)); + r = hm2->llio->write(hm2->llio, hm2->pktuart.instance[inst].tx_addr, + &buff, sizeof(u32)); + if (r < 0){ + HM2_ERR("%s send: hm2->llio->write failure\n", name); + return -1; + } + break; + default: + HM2_ERR("%s send error in buffer parsing: count = %i, i = %i\n", name, count, c); + return -1; + } // end switch + + // Write the number of bytes to be sent to PktUARTx sendcount register + buff = (u32) frame_sizes[i]; + r = hm2->llio->write(hm2->llio, hm2->pktuart.instance[inst].tx_fifo_count_addr, + &buff, sizeof(u32)); + // Check for Send Count FIFO error + r = hm2->llio->read(hm2->llio, hm2->pktuart.instance[inst].tx_mode_addr, + &buff, sizeof(u32)); + if ((buff >> 4) & 0x01) { + HM2_ERR_NO_LL("%s: SCFFIFO error\n", name); + return -TxSCFIFOError; + } + + if (r < 0){ + HM2_ERR("%s send: hm2->llio->write failure\n", name); + return -1; + } + + (*num_frames)++; + c = count; + } // for loop + + return count; +} + +EXPORT_SYMBOL_GPL(hm2_pktuart_read); +int hm2_pktuart_read(char *name, unsigned char data[], u8 *num_frames, u16 *max_frame_length, u16 frame_sizes[]) +{ + hostmot2_t *hm2; + int r, c; + int bytes_total = 0; // total amount of bytes read + u16 countp; // packets count + u16 countb; // bytes count for the oldest packet received + int inst; + u32 buff; + u16 data_size=(*num_frames)*(*max_frame_length); + + inst = hm2_get_pktuart(&hm2, name); + + if (inst < 0){ + HM2_ERR_NO_LL("Can not find PktUART instance %s.\n", name); + *num_frames=0; + return -EINVAL; + } + if (hm2->pktuart.instance[inst].bitrate == 0 ) { + HM2_ERR("%s has not been configured.\n", name); + *num_frames=0; + return -EINVAL; + } + + + // First poll the mode register for a non zero frames recieved count + // (mode register bits 20..16) + r = hm2->llio->read(hm2->llio, hm2->pktuart.instance[inst].rx_mode_addr, + &buff, sizeof(u32)); + if (r < 0) { + HM2_ERR("%s read: hm2->llio->write failure %s\n", name); + return -1; // make the error message more detailed + } + countp = (buff >> 16) & 0x1f; + // We expect to read at least 1 frame. + // If there is no complete frame yet in the buffer, + // we'll deal with this by checking error bits. + *num_frames = 0; + + // Bit 7 set does not really indicate any error condition, + // but very probably means that the cycle time of the thread, + // which you attach this function to, is not appropriate. + if ((buff >> 7) & 0x1){ + HM2_INFO("%s: Rx Logic active\n", name); + } + + // Now check the error bits + if ((buff >> 1) & 0x1){ + HM2_ERR_NO_LL("%s: Overrun error, no stop bit\n", name); + return -RxOverrunError; + } + if (buff & 0x1){ + HM2_ERR_NO_LL("%s: False Start bit error\n", name); + return -RxStartbitError; + } + + // RCFIFO Error will get sticky if it is a consequence of either Overrun or False Start bit error? + if ((buff >> 4) & 0x1){ + HM2_ERR_NO_LL("%s: RCFIFO Error\n", name); + return -RxRCFIFOError; + } + + if (countp==0){ + HM2_ERR_NO_LL("%s: no new frames \n", name); + return 0; // return zero bytes + } + + + int i=0; + while ( i < countp ) { + buff=0; + /* The receive count register is a FIFO that contains the byte counts + of recieved packets. Since it is a FIFO it must only be read once after it + has be determined that there are packets available to read. */ + r = hm2->llio->read(hm2->llio, hm2->pktuart.instance[inst].rx_fifo_count_addr, + &buff, sizeof(u32)); + + countb = buff & 0x3ff; // PktUARTr receive count register Bits 9..0 : bytes in receive packet + + if ((buff >> 14) & 0x1) { + HM2_ERR_NO_LL("%s has False Start bit error in this packet.\n", name); + return -RxPacketStartbitError; + } + + if ((buff >> 15) & 0x1) { + HM2_ERR_NO_LL("%s has Overrun error in this packet\n", name); + return -RxPacketOverrrunError; + } + + // a packet is completely received, but its byte count is zero + // is very unprobable, however we intercept this error too + if (countb==0) { + HM2_ERR_NO_LL("%s: packet %d has %d bytes.\n", name, countp+1, countb); + return -RxPacketSizeZero; + } + + if (( bytes_total+countb)> data_size) { + HM2_ERR_NO_LL("%s: bytes avalaible %d are more than data array size %d\n", name, bytes_total+countb, data_size); + return -RxArraySizeError ; + } + + (*num_frames)++; // increment num_frames to be returned at the end + c = 0; + buff = 0; + frame_sizes[i]=countb; + + while (c < countb - 3){ + r = hm2->llio->read(hm2->llio, hm2->pktuart.instance[inst].rx_addr, + &buff, sizeof(u32)); + + if (r < 0) { + HM2_ERR("%s read: hm2->llio->read failure\n", name); + return r; + } + + data[bytes_total+c] = (buff & 0x000000FF); // i*frame_sizes[i] + data[bytes_total+c+1] = (buff & 0x0000FF00) >> 8; + data[bytes_total+c+2] = (buff & 0x00FF0000) >> 16; + data[bytes_total+c+3] = (buff & 0xFF000000) >> 24; + c = c + 4; + + } + + switch(countb - c){ + case 0: + break; + case 1: + r = hm2->llio->read(hm2->llio, hm2->pktuart.instance[inst].rx_addr, + &buff, sizeof(u32)); + data[bytes_total+c] = (buff & 0x000000FF); + break; + case 2: + r = hm2->llio->read(hm2->llio, hm2->pktuart.instance[inst].rx_addr, + &buff, sizeof(u32)); + data[bytes_total+c] = (buff & 0x000000FF); + data[bytes_total+c+1] = (buff & 0x0000FF00) >> 8; + break; + case 3: + r = hm2->llio->read(hm2->llio, hm2->pktuart.instance[inst].rx_addr, + &buff, sizeof(u32)); + data[bytes_total+c] = (buff & 0x000000FF); + data[bytes_total+c+1] = (buff & 0x0000FF00) >> 8; + data[bytes_total+c+2] = (buff & 0x00FF0000) >> 16; + break; + default: + HM2_ERR_NO_LL("PktUART READ: Error in buffer parsing.\n"); + return -EINVAL; + } + if (r < 0) { + HM2_ERR("%s read: hm2->llio->write failure\n", name); + return -1; + } + + bytes_total = bytes_total + countb; + i++; // one frame/datagram read + }// frame loop + + + return bytes_total; +} + +void hm2_pktuart_print_module(hostmot2_t *hm2){ + int i; + HM2_PRINT("PktUART: %d\n", hm2->pktuart.num_instances); + if (hm2->pktuart.num_instances <= 0) return; + HM2_PRINT(" version: %d\n", hm2->pktuart.version); + HM2_PRINT(" channel configurations\n"); + for (i = 0; i < hm2->pktuart.num_instances; i ++) { + HM2_PRINT(" clock_frequency: %d Hz (%s MHz)\n", + hm2->pktuart.instance[i].clock_freq, + hm2_hz_to_mhz(hm2->pktuart.instance[i].clock_freq)); + HM2_PRINT(" instance %d:\n", i); + HM2_PRINT(" HAL name = %s\n", hm2->pktuart.instance[i].name); + } +} + +// The following standard Hostmot2 functions are not currently used by pktuart. + +void hm2_pktuart_cleanup(hostmot2_t *hm2) +{ +} + +void hm2_pktuart_write(hostmot2_t *hm2) +{ +} +