cmvr_head/packages/erpc-1.14.0/erpc_python/erpc/transport.py
2025-12-30 15:44:41 +08:00

452 lines
15 KiB
Python

#!/usr/bin/env python
# Copyright (c) 2015-2016 Freescale Semiconductor, Inc.
# Copyright 2016-2023 NXP
# Copyright 2022 ACRIOS Systems s.r.o.
# All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
import struct
import socket
import threading
from socket import SHUT_RDWR
from .crc16 import Crc16
from .client import RequestError
import time
try:
import serial
SerialReady = True
except ImportError:
SerialReady = False
try:
from rpmsg.sysfs import RpmsgEndpoint
RpmsgEndpointReady = True
except ImportError:
RpmsgEndpointReady = False
try:
from libusbsio import *
LIBUSBSIOReady = True
except ImportError:
LIBUSBSIOReady = False
class Transport(object):
""" Base transport class.
"""
def __init__(self):
pass
def send(self, message):
raise NotImplementedError()
def receive(self):
raise NotImplementedError()
class FramedTransport(Transport):
HEADER_LEN = 6
def __init__(self):
super(FramedTransport, self).__init__()
self._sendLock = threading.Lock()
self._receiveLock = threading.Lock()
self._Crc16 = Crc16()
@property
def crc_16(self):
return self._Crc16
@crc_16.setter
def crc_16(self, crcStart):
if type(crcStart) is not int:
raise RequestError("invalid CRC, not a number")
self._Crc16 = Crc16(crcStart)
def send(self, message):
try:
self._sendLock.acquire()
crcBody = self._Crc16.computeCRC16(message)
messageLength = len(message)
crcHeader = self._Crc16.computeCRC16(bytearray(struct.pack('<H', messageLength))) + self._Crc16.computeCRC16(bytearray(struct.pack('<H', crcBody)))
crcHeader &= 0xFFFF # 2bytes
header = bytearray(struct.pack('<HHH', crcHeader, messageLength, crcBody))
assert len(header) == self.HEADER_LEN
self._base_send(header + message)
finally:
self._sendLock.release()
def receive(self):
try:
self._receiveLock.acquire()
# Read fixed size header containing the message length.
headerData = self._base_receive(self.HEADER_LEN)
crcHeader, messageLength, crcBody = struct.unpack('<HHH', headerData)
computedCrc = self._Crc16.computeCRC16(bytearray(struct.pack('<H', messageLength))) + self._Crc16.computeCRC16(bytearray(struct.pack('<H', crcBody)))
computedCrc &= 0xFFFF # 2bytes
if computedCrc != crcHeader:
raise RequestError("invalid header CRC")
# Now we know the length, read the rest of the message.
data = self._base_receive(messageLength)
computedCrc = self._Crc16.computeCRC16(data)
if computedCrc != crcBody:
raise RequestError("invalid message CRC")
return data
finally:
self._receiveLock.release()
def _base_send(self, data):
raise NotImplementedError()
def _base_receive(self):
raise NotImplementedError()
class SerialTransport(FramedTransport):
def __init__(self, url, baudrate, **kwargs):
super(SerialTransport, self).__init__()
if not SerialReady:
raise ImportError(
"Please, install pySerial module (sudo pip3 install pyserial).")
self._url = url
self._serial = serial.serial_for_url(
url, baudrate=baudrate, **kwargs) # 8N1 by default
def close(self):
self._serial.close()
def _base_send(self, data):
self._serial.write(data)
def _base_receive(self, count):
return self._serial.read(count)
class ConnectionClosed(Exception):
pass
class TCPTransport(FramedTransport):
def __init__(self, host, port, isServer):
super(TCPTransport, self).__init__()
self._host = host
self._port = port
self._isServer = isServer
self._sock = None
self._socket_lock = threading.Lock()
if self._isServer:
self._serverThread = threading.Thread(target=self._serve)
self._serverThread.daemon = True
self._serverThread.start()
self._serverSockEventStart = threading.Event()
else:
self._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self._sock.setsockopt(socket.SOL_TCP, socket.TCP_NODELAY, 1)
self._sock.connect((self._host, self._port))
def _serve(self):
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setblocking(1)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.setsockopt(socket.SOL_TCP, socket.TCP_NODELAY, 1)
s.bind((self._host, self._port))
s.listen(5)
while True:
self._sock, _ = s.accept()
self._serverSockEventStart.set()
def close(self):
if self._isServer:
self._serverSockEventStart.clear()
with self._socket_lock:
if self._sock is not None:
try:
self._sock.shutdown(SHUT_RDWR)
self._sock.close()
except OSError:
# May be raised by the OS if the socket was closed externally,
# thus invalidating the file descriptor.
pass
self._sock = None
def _base_send(self, message):
if self._isServer:
self._serverSockEventStart.wait()
if self._sock:
self._sock.sendall(message)
def _base_receive(self, count):
if self._isServer:
self._serverSockEventStart.wait()
if self._sock:
remaining = count
result = bytearray()
while remaining:
data = self._sock.recv(remaining)
if len(data) == 0:
self.close()
raise ConnectionClosed()
result += data
remaining -= len(data)
return result
class RpmsgTransport(Transport):
def __init__(self, ept_addr_local=None, ept_addr_remote=None, channel_name=None):
if not RpmsgEndpointReady:
raise ImportError(
"Please, install RPMsg from: https://github.com/EmbeddedRPC/erpc-imx-demos/tree/master/middleware/rpmsg-python")
if ept_addr_local is None:
ept_addr_local = RpmsgEndpoint.LOCAL_DEFAULT_ADDRESS
if ept_addr_remote is None:
ept_addr_remote = RpmsgEndpoint.REMOTE_DEFAULT_ADDRESS
if channel_name is None:
channel_name = RpmsgEndpoint.rpmsg_openamp_channel
self.ept_addr_remote = ept_addr_remote
self.ept = RpmsgEndpoint(
channel_name,
ept_addr_local,
RpmsgEndpoint.Types.DATAGRAM)
def send(self, message):
self.ept.send(message, self.ept_addr_remote)
def receive(self):
while True:
ret = self.ept.recv(-1)
if len(ret[1]) != 0:
return ret[1]
else:
time.sleep(0.001)
class LIBUSBSIOSPITransport(FramedTransport):
def __init__(self, baudrate=None, cs_gpio_port=None, cs_gpio_pin=None, devidx=None):
super(LIBUSBSIOSPITransport, self).__init__()
if not LIBUSBSIOReady:
raise ImportError("Please, install LIBUSBSIO module")
if baudrate is None:
baudrate = 500000
if devidx is None:
devidx = 0
self._baudrate = baudrate
self._cs_gpio_port = cs_gpio_port
self._cs_gpio_pin = cs_gpio_pin
self._devidx = devidx
self._gpioport = 0
self._gpiopin = 0
self._gpiomode = 0
# Load DLL from default directory without any debugging prints
self.sio = LIBUSBSIO()
# Get number of LIBUSBSIO devices
res = self.sio.GetNumPorts(vidpids=[LIBUSBSIO.VIDPID_LPCLINK2])
if res != 0:
self._gpioport = 1
self._gpiopin = 2
self._gpiomode = 1
else:
res = self.sio.GetNumPorts(vidpids=[LIBUSBSIO.VIDPID_MCULINK])
if res != 0:
self._gpioport = 0
self._gpiopin = 4
self._gpiomode = 0x100
else:
print('No LIBUSBSIO devices found \r\n')
return
print('Total LIBUSBSIO devices: %d \r\n' % res)
# Open device at given index
self._hSIOPort = self.sio.Open(int(self._devidx))
# Get the device version
s = self.sio.GetVersion()
str1 = ""
print('Device version: %s \r\n ' % str1.join(str(s)))
# Get number of available SPI ports
num_spi_ports = self.sio.GetNumSPIPorts()
print('Number of SPI ports available: %d \r\n' % num_spi_ports)
# Get max number of bytes supported for I2C/SPI transfers
max_num_bytes = self.sio.GetMaxDataSize()
print('Max number of bytes supported for I2C/SPI transfers: %d \r\n' %
max_num_bytes)
# Call SPI_Open and store the _hSPIPort handler
self._hSPIPort = self.sio.SPI_Open(
int(self._baudrate), portNum=0, dataSize=8, preDelay=0)
# Configure GPIO pin for SPI master-slave signalling
res = self.sio.GPIO_ConfigIOPin(
self._gpioport, self._gpiopin, self._gpiomode)
print('GPIO_ConfigIOPin res: %d \r\n' % res)
res = self.sio.GPIO_SetPortInDir(self._gpioport, self._gpiopin)
print('GPIO_SetPortInDir res: %d \r\n' % res)
res = self.sio.GPIO_GetPin(self._gpioport, self._gpiopin)
print('GPIO_GetPin res: %d \r\n' % res)
def close(self):
res = self._hSPIPort.Close()
self._hSPIPort = None
res = self.sio.Close()
self._hSIOPort = None
def _base_send(self, message):
# Wait for SPI master-slave signalling GPIO pin to be in low state
res = self.sio.GPIO_GetPin(self._gpioport, self._gpiopin)
while (1 == res):
res = self.sio.GPIO_GetPin(self._gpioport, self._gpiopin)
# Send the header first
data, rxbytesnumber = self._hSPIPort.Transfer(
0, 15, message[:self.HEADER_LEN], self.HEADER_LEN, 0)
if rxbytesnumber > 0:
#print('SPI received %d number of bytes' % rxbytesnumber)
# Send the payload/data
data, rxbytesnumber = self._hSPIPort.Transfer(
0, 15, bytes(message[self.HEADER_LEN:]), len(message) - self.HEADER_LEN, 0)
else:
print('SPI transfer error: %d' % rxbytesnumber)
def _base_receive(self, count):
# Wait for SPI master-slave signalling GPIO pin to be in low state
res = self.sio.GPIO_GetPin(self._gpioport, self._gpiopin)
while (1 == res):
res = self.sio.GPIO_GetPin(self._gpioport, self._gpiopin)
data, rxbytesnumber = self._hSPIPort.Transfer(
0, 15, range(count), count, 0)
if rxbytesnumber > 0:
#print('SPI received %d number of bytes' % rxbytesnumber)
return bytes(data[:count])
else:
print('SPI transfer error: %d' % rxbytesnumber)
res = self._hSPIPort.Reset()
return b"\00" * count
class LIBUSBSIOI2CTransport(FramedTransport):
def __init__(self, baudrate=None, devidx=None):
super(LIBUSBSIOI2CTransport, self).__init__()
if not LIBUSBSIOReady:
raise ImportError("Please, install LIBUSBSIO module")
if baudrate is None:
baudrate = 100000
if devidx is None:
devidx = 0
self._baudrate = baudrate
self._devidx = devidx
self._gpioport = 0
self._gpiopin = 0
self._gpiomode = 0
# Load DLL from default directory without any debugging prints
self.sio = LIBUSBSIO()
# Get number of LIBUSBSIO devices
res = self.sio.GetNumPorts(vidpids=[LIBUSBSIO.VIDPID_LPCLINK2])
if res != 0:
self._gpioport = 1
self._gpiopin = 2
self._gpiomode = 1
else:
res = self.sio.GetNumPorts(vidpids=[LIBUSBSIO.VIDPID_MCULINK])
if res != 0:
self._gpioport = 1
self._gpiopin = 3
self._gpiomode = 0x100
else:
print('No LIBUSBSIO devices found \r\n')
return
print('Total LIBUSBSIO devices: %d \r\n' % res)
# Open device at given index
self._hSIOPort = self.sio.Open(int(self._devidx))
# Get the device version
s = self.sio.GetVersion()
str1 = ""
print('Device version: %s \r\n ' % str1.join(str(s)))
# Get number of available I2C ports
num_spi_ports = self.sio.GetNumI2CPorts()
print('Number of I2C ports available: %d \r\n' % num_spi_ports)
# Get max number of bytes supported for SPI/I2C transfers
max_num_bytes = self.sio.GetMaxDataSize()
print('Max number of bytes supported for SPI/I2C transfers: %d \r\n' %
max_num_bytes)
# Call I2C_Open and store the _hI2CPort handler
self._hI2CPort = self.sio.I2C_Open(int(self._baudrate), 0, 0)
# Configure GPIO pin for I2C master-slave signalling
res = self.sio.GPIO_ConfigIOPin(
self._gpioport, self._gpiopin, self._gpiomode)
print('GPIO_ConfigIOPin res: %d \r\n' % res)
res = self.sio.GPIO_SetPortInDir(self._gpioport, self._gpiopin)
print('GPIO_SetPortInDir res: %d \r\n' % res)
res = self.sio.GPIO_GetPin(self._gpioport, self._gpiopin)
print('GPIO_GetPin res: %d \r\n' % res)
def close(self):
res = self._hI2CPort.Close()
self._hI2CPort = None
res = self.sio.Close()
self._hSIOPort = None
def _base_send(self, message):
# Wait for I2C master-slave signalling GPIO pin to be in low state
res = self.sio.GPIO_GetPin(self._gpioport, self._gpiopin)
while (1 == res):
res = self.sio.GPIO_GetPin(self._gpioport, self._gpiopin)
# Send the header first
data, rxbytesnumber = self._hI2CPort.FastXfer(
0x7E, message[:self.HEADER_LEN], self.HEADER_LEN, 0, False, True)
if rxbytesnumber > 0:
#print('I2C received %d number of bytes' % rxbytesnumber)
# Send the payload/data
data, rxbytesnumber = self._hI2CPort.FastXfer(
0x7E, bytes(message[self.HEADER_LEN:]), len(message) - self.HEADER_LEN, 0, False, True)
else:
print('I2C transfer error: %d' % rxbytesnumber)
def _base_receive(self, count):
# Wait for I2C master-slave signalling GPIO pin to be in low state
res = self.sio.GPIO_GetPin(self._gpioport, self._gpiopin)
while (1 == res):
res = self.sio.GPIO_GetPin(self._gpioport, self._gpiopin)
# Issue the I2C_Transfer API
data, rxbytesnumber = self._hI2CPort.FastXfer(0x7E, 0, 0, count, False, True)
if rxbytesnumber > 0:
#print('I2C received %d number of bytes' % rxbytesnumber)
return bytes(data[:count])
else:
#print('I2C transfer error: %d' % rxbytesnumber)
res = self._hI2CPort.Reset()
return b"\00" * count