Beispiel #1
0
class DiskSensorPhysical(DiskSensor):
    """"
        This is an abstract class to help manage both physical and Xen
        implementations.  This will also allow us to expand to new 
        implementations very easily.
    """
    def __init__(self,
                 sensor_ip,
                 sensor_port=G.SENSOR_DISK.DEFAULT_PORT,
                 bind_ip="0.0.0.0",
                 bind_port=G.SENSOR_DISK.DEFAULT_PORT,
                 sector_size=G.SENSOR_DISK.DEFAULT_SECTOR_SIZE,
                 name=None,
                 use_threading=True):
        """ Initialize our sensor """

        # Set our variables
        self.sensor_ip = sensor_ip
        self.sensor_port = sensor_port

        self.bind_ip = bind_ip
        self.bind_port = bind_port

        self.sector_size = sector_size

        # Keep our connected state
        self.connected = False
        self.sata_enabled = False

        # Initialize a heap to re-order SATA frames that arrive out of order
        self.sata_heap = []
        self.last_lophi_seqn = None
        self.last_lophi_packet = None

        if name is not None:
            self.name = name

        # Are we reading a separate process?
        self.use_threading = use_threading
        self.read_queue = None
        self.packet_reader = None

        self.SOCK = None

        self.DROPPED_PACKETS = 0

        # Connect to the socket
        #         self._connect()

        DiskSensor.__init__(self)

        logger.debug("Initialized Disk Sensor. (%s)" % self.name)

    def __del__(self):
        logger.debug("Destroying Disk Sensor / Disconnecting. (%s)" %
                     self.name)
        try:
            self._disconnect()
        except:
            pass

    def _connect(self):
        """
            Connect to our sensor
            
            @param enable_sata: Will send the appropriate command to enable SATA
            extraction
        """

        if not self.connected:
            logger.debug("Connecting to disk sensor")

            try:
                # Open socket
                self.SOCK = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

                # Ensure that the socket is reusable
                self.SOCK.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

                # Bind to our port and listening for incoming packets
                self.SOCK.bind((self.bind_ip, self.bind_port))

                # Make our buffer much larger to help prevent packet loss
                self.SOCK.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF,
                                     G.UDP_RECV_BUFFER_SIZE)

                if self.use_threading:
                    logger.debug("Starting listener thread.")
                    # Start a process that will handle all of our reads
                    self.read_queue = multiprocessing.Queue()
                    self.packet_reader = PacketReaderUDP(
                        self.SOCK, self.read_queue)
                    self.packet_reader.start()

                self.connected = True

                return True

            except:
                logger.error("Could not connect to disk sensor")
                return False

        return True

#         # Set a timeout
#         self.SOCK.settimeout(READ_TIMEOUT)

# Send our "Wake Up" Packets
#         if self.sata_enable_mode:
#             self.sata_enable_all()

    def _disconnect(self):
        """ Close up shop """

        logger.debug("Disconnecting from disk sensor.")

        self.connected = False

        # Close our socket
        if self.SOCK is not None:
            logger.debug("Closing socket.")
            self.SOCK.close()
            self.SOCK = None

        # Are we using a threaded reader?
        if self.packet_reader is not None:
            logger.debug("Killing packet reader thread.")
            try:
                logger.debug("Trying to kill reader thread.")
                self.packet_reader.stop()
                self.packet_reader = None
                logger.debug("Reader thread killed.")
            except:
                G.print_traceback()
                pass

        # Close our threaded process for reading?
        if self.read_queue is not None:
            logger.debug("Closing queue.")
            self.read_queue.close()

    def _send_command(self, operation, memory_address, data=None):
        """
            This is used to interface with the card
        """

        if not self._connect():
            return False

        # Get our network packet
        packet = LOPHIPacket()

        # Set our agruments
        packet.MAGIC = G.SENSOR_DISK.MAGIC_LOPHI
        packet.op_code = operation
        packet.memory_addr = memory_address
        packet.memory_len = math.ceil(len(data) / 4)
        packet.frame_length = 3 + packet.memory_len  # 3 word header + data
        packet.data = data

        raw_data = repr(packet)

        logger.debug("Sending: %s" % repr(packet))
        # Send it across the wire
        self.SOCK.sendto(raw_data, (self.sensor_ip, self.sensor_port))

        # Get our response
        lophi_packet = self.get_lophi_packet()
        logger.debug("Response: %s" % str(lophi_packet))

        return lophi_packet

    def _read_raw_packet(self, size=G.MAX_PACKET_SIZE):
        """ 
            Read and return raw data from our socket
            
            @return: (data, address) 
        """

        if not self._connect():
            return False

        # Read UDP data from thread or off the wire
        if self.read_queue is not None:
            recv_data, recv_addr = self.read_queue.get()
        else:
            recv_data, recv_addr = self.SOCK.recvfrom(size)

        logger.debug("Read %d bytes." % len(recv_data))
        return recv_data, recv_addr

    def _get_sata_packet(self):
        """
            This segment of code makes sure that we read our SATA packets in 
            order in case they are re-ordered by the network.
            
            @return: LOPHI SATA packet guaranteed to be in the order sent by sensor.
            @todo: Handle packet loss
        """
        # Make sure SATA extraction is enabled
        if not self.sata_enabled:
            self.sata_enable_all()

        lophi_packet = None

        while 1:
            # Are we dealing with the heap?
            if len(self.sata_heap) > 0:
                logger.debug("pulling from heap")

                heap_next_seqn = self.sata_heap[0][0]
                if heap_next_seqn == (self.last_lophi_seqn + 1) % 65536 or len(
                        self.sata_heap) > MAX_HEAP_SIZE:

                    if len(self.sata_heap) > MAX_HEAP_SIZE:
                        self.DROPPED_PACKETS += 1
                        logger.warning(
                            "Sensor dropped packets. (Data may be unreliable)")
                    lophi_seqn, lophi_packet = heapq.heappop(self.sata_heap)
                    # hack to make sure we don't put it back on the heap
                    self.last_lophi_seqn = lophi_seqn
                    self.last_lophi_packet = lophi_packet

                    # Return our packet
                    return lophi_packet

                else:
                    lophi_packet = self.get_lophi_packet()
            else:
                # Get the next packet from the wire
                lophi_packet = self.get_lophi_packet()

            # Check the packet from the wire and see if it has the seqn we expected
            if lophi_packet.op_code == G.SENSOR_DISK.OP.SATA_FRAME:

                # Extract our SATA data
                sata_frame = SATAFrame(lophi_packet.data)

                # Get our sequence number
                lophi_seqn = sata_frame.seqn_num

                # Take care of our ordering
                if self.last_lophi_seqn is not None:
                    # If this is an out of order packet, push to heap
                    if lophi_seqn != (self.last_lophi_seqn + 1) % 65536:
                        heapq.heappush(self.sata_heap,
                                       (lophi_seqn, lophi_packet))
                        logger.debug(
                            "Got out of order packet. (Expected: %d, Got: %d)"
                            % ((self.last_lophi_seqn + 1) % 65536, lophi_seqn))
                        continue

                # Save our last seqn
                self.last_lophi_seqn = lophi_seqn
                self.last_lophi_packet = lophi_packet

                # Return our packet
                return sata_frame

            else:
                continue

    def is_up(self):
        """
            Check to see if the card is up
            
            @return: True/False
        """
        try:
            # Open socket
            sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

            # connect to our port+1 which is a UDP loopback
            addr = (self.sensor_ip, self.bind_port + 1)
            sock.connect(addr)

            # Set a timeout
            sock.settimeout(READ_TIMEOUT)

            # Try to send a packet to the UDP loopback
            sock.send("PING")

            # Ensure we got the same data back
            rtn = sock.recv(1024)
            if rtn == "PING":
                return True

            sock.close()
        except:
            pass

        # If anything goes wrong, the card isn't there.
        return False

    def get_disk_packet(self):
        """
            For now just return the SATA frame, we'll deal with this later.
        """
        logger.debug("Reading disk packet (SATA Frame in this case)...")
        return self._get_sata_packet()

    def get_lophi_packet(self):
        """
            Get the next packet header/data
            
            @return: LO-PHI packet as a dictionary 
                - lophi_header: LO-PHI defined header
                - lophi_data:                       
        """
        # Read data off the wire
        logger.debug("Reading LO-PHI packet...")
        recv_data, recv_addr = self._read_raw_packet()

        network_packet = LOPHIPacket(recv_data)

        #         logger.info("Received: %s"%network_packet)
        return network_packet

    """
        Physical Specific Functions
    """

    def get_version(self):
        """
            Returns the current version of the sensor
            
            @return: Version of sensor retrieved from card
        """
        self._send_command(G.SENSOR_DISK.OP.REG_READ,
                           G.SENSOR_DISK.ADDR.VERSION)
        packet = self.get_lophi_packet()
        version = packet.data
        return version

    def sata_enable_host(self):
        """
            Enable host only SATA extraction
        """
        logger.debug("Sending packet to enable sata extraction.")
        self._send_command(G.SENSOR_DISK.OP.REG_WRITE,
                           G.SENSOR_DISK.ADDR.SATA_CTRL, "\x00\x00\x00\x01")

    def sata_enable_device(self):
        """
            Enable device only SATA extraction
        """
        logger.debug("Sending packet to enable sata extraction.")
        self._send_command(G.SENSOR_DISK.OP.REG_WRITE,
                           G.SENSOR_DISK.ADDR.SATA_CTRL, "\x00\x00\x00\x02")

    def sata_enable_all(self):
        """
            Enable bi-directional SATA extraction
        """
        logger.debug("Sending packet to enable sata extraction.")
        self._send_command(G.SENSOR_DISK.OP.REG_WRITE,
                           G.SENSOR_DISK.ADDR.SATA_CTRL, "\x00\x00\x00\x03")
        self.sata_enabled = True

    def sata_disable(self):
        """
            Disable SATA extraction
        """
        logger.debug("Sending packet to disable sata extraction.")
        self._send_command(G.SENSOR_DISK.OP.REG_WRITE,
                           G.SENSOR_DISK.ADDR.SATA_CTRL, "\x00\x00\x00\x00")
        self.sata_enabled = False
        return True

    def print_all_registers(self):

        registers = self.get_all_registers()

        for (reg, addr, data) in registers:
            data_list = struct.unpack("%dB" % len(data), data)

            data_list2 = " ".join([hex(i) for i in data_list])

            print "%s (%s): %s / %s" % (reg, hex(addr), data_list2, data_list)

    def get_all_registers(self):
        """
            Returns all of the registers on the sensor
            
            @return: list of tuples (name, address, value)
        """
        logger.debug("Requesting all registers")

        registers = []
        for reg in G.SENSOR_DISK.ADDR.__dict__:  # @UndefinedVariable
            if reg.startswith("_"):
                continue

            addr = G.SENSOR_DISK.ADDR.__dict__[reg]  # @UndefinedVariable

            lophi_packet = self._send_command(G.SENSOR_DISK.OP.REG_READ, addr,
                                              "", 1)

            data = lophi_packet.data

            registers.append((reg, addr, data))

        return [x for x in registers if x is not None]

    def set_dest_ip(self, dest_ip):
        """
            Set the destination IP for our SATA frames
            
            @param dest_ip: Destination IP that SATA packets will be sent to 
        """

        logger.debug("Setting destination IP to %s." % dest_ip)

        ip = struct.pack('>I', NET.ip2int(dest_ip))

        lophi_packet = self._send_command(G.SENSOR_DISK.OP.REG_WRITE,
                                          G.SENSOR_DISK.ADDR.IP_DEST, ip)

        if lophi_packet.header[
                'op_code'] == G.SENSOR_DISK.OP.REG_WRITE and lophi_packet.header[
                    'memory_addr'] == G.SENSOR_DISK.ADDR.IP_DEST:
            return True
        else:
            return False

    def set_sensor_ip(self, ip):
        """
            Set the card IP address
            
            @param ip: IP address of this sensor 
        """

        logger.debug("Setting sensor IP to %s." % ip)

        ip = struct.pack('>I', NET.ip2int(ip))

        lophi_packet = self._send_command(G.SENSOR_DISK.OP.REG_WRITE,
                                          G.SENSOR_DISK.ADDR.IP_SENS, ip)

        if lophi_packet.header[
                'op_code'] == G.SENSOR_DISK.OP.REG_WRITE and lophi_packet.header[
                    'memory_addr'] == G.SENSOR_DISK.ADDR.IP_SENS:
            return True
        else:
            return False

    def set_udp_delay(self, delay):
        """
            Set the intrapacket delay for UDP packets
            
            @param delay: Delay in clock cycles (~10ns/cycle) between packets
        """

        logger.debug("Setting UDP intrapacket delay to: %s" % delay)

        delay = struct.pack('>I', delay)

        lophi_packet = self._send_command(G.SENSOR_DISK.OP.REG_WRITE,
                                          G.SENSOR_DISK.ADDR.UDP_DELAY, delay)

        if lophi_packet.header[
                'op_code'] == G.SENSOR_DISK.OP.REG_WRITE and lophi_packet.header[
                    'memory_addr'] == G.SENSOR_DISK.ADDR.UDP_DELAY:
            return True
        else:
            return False

    def set_mtu_size(self, mtu_size):
        """
            Set the MTU size for UDP packets
            
            @param mtu_size: Maximum transfer unit size for UDP packets in bytes
        """

        logger.debug("Setting MTU for packets to: %s" % mtu_size)

        mtu_size = struct.pack('>I', mtu_size)

        lophi_packet = self._send_command(G.SENSOR_DISK.OP.REG_WRITE,
                                          G.SENSOR_DISK.ADDR.MTU_SIZE,
                                          mtu_size)

        if lophi_packet.header[
                'op_code'] == G.SENSOR_DISK.OP.REG_WRITE and lophi_packet.header[
                    'memory_addr'] == G.SENSOR_DISK.ADDR.MTU_SIZE:
            return True
        else:
            return False
Beispiel #2
0
class MemorySensorPhysical(MemorySensor):
    """
        This is our interface to both our NetFPGA and ML507 boards using Josh's
        code.
    """
    def __init__(self,
                 sensor_ip=G.SENSOR_MEMORY.DEFAULT_IP,
                 sensor_port=G.SENSOR_MEMORY.DEFAULT_PORT,
                 cache_timeout=0,
                 name=None,
                 use_threading=False,
                 timeout=1,
                 retries=5):
        """
            Initialize our memory sensor.  Just saving values at this point.
    
            @param cache_timeout: How long to keep data in the cache (seconds)
            @param name: Human name of the sensor
            @param use_threading: This will spawn a new process to read replys 
            from the sensor.  Enables much faster reads, but will eventually 
            blow the UDP stack in the FPGA. 
        """
        # Sensor info
        self.sensor_ip = sensor_ip
        self.sensor_port = sensor_port

        # Socket variables
        self._sock = None
        self.TIMEOUT = timeout
        self.RETRIES = retries
        self.TIMED_OUT = False
        self.connect_count = 0

        # Are we reading a separate process?
        self.use_threading = use_threading
        self.read_queue = None
        self.packet_reader = None

        # Cache
        self.cache = {}
        self.cache_timeouts = {}
        self.CACHE_TIMEOUT = cache_timeout  # seconds

        # Keep track of our transaction
        self.transaction_no = 1

        if name is not None:
            self.name = name

        # Bad read regions (On XPSP3x86)
        # Ref: http://wiki.osdev.org/Memory_Map_%28x86%29
        self.BAD_MEM_REGIONS = [
            (0xA0000, 0x100000),  # VGA and PCI
            (0x7fe00000, 0x80000000),  # No Clue
            (0xdbf00000, 0x100000000),  # High Mem PCI devices
            (0x3ff00000, 0x40000000)  # No clue (End of memory?
        ]

        # Initialize our superclass as well
        MemorySensor.__init__(self)

    def __del__(self):
        """
            Clean up any connections when the object is destroyed
        """
        self._disconnect()

    def _connect(self):
        """
            Connect our socket. 
        """
        # Is the socket already open?
        if self._sock is None:
            # Open our socket
            try:
                logger.debug("Connecting to memory sensor. ")
                s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
                s.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 100000)
                s.connect((self.sensor_ip, self.sensor_port))

                #                 if self.timeout is not None:
                #                     s.settimeout(self.timeout)

                if self.use_threading:
                    logger.debug("Starting listener thread.")
                    # Start a process that will handle all of our reads
                    self.read_queue = multiprocessing.Queue()
                    self.packet_reader = PacketReaderUDP(s, self.read_queue)
                    self.packet_reader.start()
                elif self.TIMEOUT is not None and self.TIMEOUT > 0:
                    s.settimeout(self.TIMEOUT)

            except:
                logger.error("Could not connect to memory sensor. (%s,%d)" %
                             (self.sensor_ip, self.sensor_port))
                self.connect_count += 1

                if self.connect_count > self.RETRIES:
                    raise socket.error("Could not connect to memory sensor.")

                return False

            # Save our socket
            self._sock = s

        return True
        self.connect_count = 0

    def _disconnect(self):
        """
            Disconnect our socket.
        """
        if self._sock is not None:
            self._sock.close()
            self._sock = None

        if self.read_queue is not None:
            self.read_queue.close()
            self.read_queue = None

        if self.packet_reader is not None:
            self.packet_reader.terminate()
            self.packet_reader = None

    def _send_command(self, command, address, length, data=None):
        """
            Send a command to to the card
            
            @param command: LO-PHI command to send
            @param address: Address on the SUT (Not address on the FPGA)
            @param length: Length for the command (Not the length of the packet)
            @param data: Any data to append to the packet 
        """

        logger.debug("Sending command (0x%x, 0x%x, %d)" %
                     (command, address, length))

        # Build our payload
        packet = MemoryRapidPacket()

        # Constants
        packet.MAGIC_LOPHI = G.SENSOR_MEMORY.MAGIC_LOPHI
        packet.flags = G.SENSOR_MEMORY.DEFAULT_FLAGS

        # Command
        packet.operation = command

        # Split our address
        lowaddress = (address) & 0xFFFFFFFF
        highaddress = address >> 32
        packet.address_high = highaddress
        packet.address_low = lowaddress

        # Data
        packet.length = length
        packet.data = data

        # Transcation num
        packet.transaction_no = self.transaction_no

        # Increment our transcation number
        self.transaction_no = (self.transaction_no + 1) % 0x0000ffff

        # Keep trying to reconnect
        while not self._connect():
            time.sleep(1)

        # Send payload to our sensor
        sent = self._sock.send( ` packet `)
        if sent != len(packet):
            logger.error("Only sent {0} out of {1}".format(sent, len(packet)))
            dead = True

    def _read_raw_packet(self, size=G.MAX_PACKET_SIZE):
        """ 
            Read and return raw data from our socket
            
            @return: (data, address) 
        """

        # If we already timed out, assume the sensor is dead
        if self.TIMED_OUT:
            raise socket.timeout

        # Keep trying to reconnect
        while not self._connect():
            time.sleep(1)

        # Read UDP data off the wire
        if self.read_queue is not None:
            recv_data, recv_addr = self.read_queue.get()
        else:
            recv_data, recv_addr = self._sock.recvfrom(size)

        logger.debug("Read %d bytes." % len(recv_data))
        return recv_data, recv_addr

    def _get_read_response(self, length, read_multiple=False):
        """
        
        """
        data = ""
        if read_multiple:
            transaction_no = 0
        else:
            transaction_no = (self.transaction_no - 1) % 0x0000ffff

        while (len(data) < length):

            # Read a LO-PHI packet
            rapid_packet = self.get_rapid_packet()

            if rapid_packet.MAGIC_LOPHI != G.SENSOR_MEMORY.MAGIC_LOPHI:
                logger.error("Magic number mismatch. (%x)" %
                             (rapid_packet.MAGIC_LOPHI))
                return None

            # Same transaction?
            if (rapid_packet.transaction_no != transaction_no):
                logger.error("different transaction! %x instead of %x" %
                             (rapid_packet.transaction_no, transaction_no))
                return None

            # Is this the correct response?
            if (rapid_packet.operation !=
                    G.SENSOR_MEMORY.COMMAND.READ + 0x2):  # RAPID reply is 0x2
                logger.error("not a read?! {0}".format(rapid_packet.operation))
                logger.error(rapid_packet)
                continue

            if (rapid_packet.data is None
                    or len(rapid_packet.data) != rapid_packet.length):
                logger.error(
                    "DATA LENGTHS DON'T MATCH! (Expected: %d, Got: %d bytes)" %
                    (rapid_packet.length, len(rapid_packet.data)))

            # Append our data
            data += rapid_packet.data

            # look for next transaction
            transaction_no = (transaction_no + 1) % 0x0000ffff

        # Just in case we read more than we wanted, truncate off the end bytes
        return (data[:length])

        return None

    def _read_from_sensor(self, address, length):
        """
            This is the lowest level read command and the only read command that
             will acctually perform a memory read from the sensor.
            
            @param address: Physical memory address to read
            @param length: Length of memory to read starting at @address 
            
            @TODO: Remove our horrible hack once the hardware is up-to-date
        """
        with network_lock:
            # This is a HACK to work around a bug in the PCI sensor that has trouble
            # when not reading on word boundaries?
            adjust_addr = 0

            # Is our start address word aligned?
            if address % 4 != 0:
                adjust_addr = address % 4
                address -= adjust_addr
                length += adjust_addr

            # Only read in words.
            adjust_len = 0
            if length % 4 != 0:
                adjust_len = 4 - length % 4
                length += adjust_len

            self.transaction_no = 0
            rtn_data = ""

            remaining_length = length
            offset = 0

            if not self.use_threading:
                while remaining_length > 0:
                    # Calculate how much to read?
                    req_len = min(READ_CHUNK, remaining_length)
                    # Send our read command

                    # Try to read RETRIES times
                    attempt = 0
                    while attempt < self.RETRIES:
                        try:
                            # Send read command
                            self._send_command(G.SENSOR_MEMORY.COMMAND.READ,
                                               address + offset, req_len)
                            # get data off the wire
                            tmp = self._get_read_response(req_len)

                            # Something bad happen in the read, let's try to re-open the socket
                            if tmp is None:
                                logger.error(
                                    "Didn't get a response from sensor. Trying again."
                                )
                                self._disconnect()
                                self._connect()
                                continue

                            break

                        except socket.timeout:
                            logger.error("Memory sensor timeout (%d/%d)" %
                                         (attempt, self.RETRIES))
                            pass

                        attempt += 1

                    # if we hit our retries, the card has timed out.
                    if attempt == self.RETRIES:
                        logger.error("Memory sensor timed out! (0x%16X, %d)" %
                                     (address + offset, req_len))
                        raise socket.timeout

                    # If nothing came back, keep trying!
                    if tmp is None:
                        #                         continue
                        rtn_data = None
                        break

                    rtn_data += tmp

                    # Calculate how much more we have to read
                    remaining_length -= req_len
                    offset += req_len

            else:
                # Try to read RETRIES times
                attempt = 0
                while attempt < self.RETRIES:
                    try:

                        # Send all of our read commands
                        while remaining_length > 0:
                            # Calculate how much to read?
                            req_len = min(READ_CHUNK, remaining_length)
                            # Send our read command
                            self._send_command(G.SENSOR_MEMORY.COMMAND.READ,
                                               address + offset, req_len)

                            # Calculate how much more we have to read
                            remaining_length -= req_len
                            offset += req_len

                        # Read all of the data back at once.
                        rtn_data = self._get_read_response(length, True)

                        # Something bad happen in the read, let's try to re-open the socket
                        if rtn_data is None:
                            logger.error(
                                "Didn't get a response from sensor. (%d/%d)" %
                                (attempt, self.RETRIES))
                            time.sleep(1)
                            self._disconnect()
                            self._connect()
                            remaining_length = length
                        else:
                            break

                    except socket.timeout:
                        logger.error("Memory sensor timeout (%d/%d)" %
                                     (attempt, self.RETRIES))
                        pass

                    attempt += 1

                if attempt == self.RETRIES:
                    logger.error("Memory sensor timed out! (0x%16X, %d)" %
                                 (address, length))
                    raise socket.timeout

            # return the read data
            # HACK: start from our offset and truncate extra data appended to ensure
            # that we were word-aligned
            if rtn_data is None:
                return rtn_data
            else:
                return rtn_data[adjust_addr:length - adjust_len]

    def get_rapid_packet(self):
        """
            Read the next memory sensor packet from the wire.
            
            @return: LO-PHI packet as a dictionary 
                - lophi_header: LO-PHI defined header
                - lophi_data:  
        """
        # Read data off the wire
        logger.debug("Reading LO-PHI packet...")
        recv_data, recv_addr = self._read_raw_packet()

        network_packet = MemoryRapidPacket(recv_data)

        #         logger.info("Received: %s"%network_packet)
        return network_packet

    def write(self, address, data):
        """
            Write data to physical memory
        """
        from lophi.data import DataStruct

        class MemoryWritePacket(DataStruct):
            """
                This defines the header used by then RAPID protocol, which is abused
                by our memory sensors.
            """
            name = "RAPIDPacket"
            STRUCT = [('MAGIC_LOPHI', '!I'), ('transcation_no', '!H'),
                      ('operation', '!B'), ('address', '!7xQ'),
                      ('length', '!I')]

        write_packet = MemoryWritePacket()
        write_packet.address = address
        write_packet.data = data
        write_packet.length = len(data)
        write_packet.operation = G.SENSOR_MEMORY.COMMAND.WRITE
        write_packet.MAGIC_LOPHI = G.SENSOR_MEMORY.MAGIC_LOPHI

        # Keep trying to reconnect
        while not self._connect():
            time.sleep(1)

        # Send payload to our sensor
        sent = self._sock.send( ` write_packet `)
        if sent != len(write_packet):
            logger.error("Only sent {0} out of {1}".format(
                sent, len(write_packet)))
            dead = True
Beispiel #3
0
class DiskSensorPhysical(DiskSensor):
    """"
        This is an abstract class to help manage both physical and Xen
        implementations.  This will also allow us to expand to new 
        implementations very easily.
    """

    def __init__(self, sensor_ip, 
                 sensor_port=G.SENSOR_DISK.DEFAULT_PORT,
                 bind_ip="0.0.0.0",
                 bind_port = G.SENSOR_DISK.DEFAULT_PORT,
                 sector_size=G.SENSOR_DISK.DEFAULT_SECTOR_SIZE,
                 name=None,
                 use_threading=True):
        """ Initialize our sensor """

        # Set our variables
        self.sensor_ip = sensor_ip
        self.sensor_port = sensor_port
       
        self.bind_ip = bind_ip
        self.bind_port = bind_port
        
        self.sector_size = sector_size
        
        # Keep our connected state
        self.connected = False
        self.sata_enabled = False
        
        # Initialize a heap to re-order SATA frames that arrive out of order
        self.sata_heap = []
        self.last_lophi_seqn = None
        self.last_lophi_packet = None
                
        if name is not None:
            self.name = name
        
        # Are we reading a separate process?
        self.use_threading = use_threading
        self.read_queue = None
        self.packet_reader = None
        
        self.SOCK = None
        
        self.DROPPED_PACKETS = 0
        
        # Connect to the socket
#         self._connect()
        
        DiskSensor.__init__(self)
        
        logger.debug("Initialized Disk Sensor. (%s)"%self.name)
        
        
    def __del__(self):
        logger.debug("Destroying Disk Sensor / Disconnecting. (%s)"%self.name)
        try:
            self._disconnect()
        except:
            pass


    def _connect(self):
        """
            Connect to our sensor
            
            @param enable_sata: Will send the appropriate command to enable SATA
            extraction
        """

        

        if not self.connected:
            logger.debug("Connecting to disk sensor")
            
            try:
                # Open socket
                self.SOCK = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        
                # Ensure that the socket is reusable
                self.SOCK.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        
                # Bind to our port and listening for incoming packets
                self.SOCK.bind((self.bind_ip, self.bind_port))
        
                # Make our buffer much larger to help prevent packet loss
                self.SOCK.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, G.UDP_RECV_BUFFER_SIZE)
                
                
                if self.use_threading:
                    logger.debug("Starting listener thread.")
                    # Start a process that will handle all of our reads
                    self.read_queue = multiprocessing.Queue()
                    self.packet_reader = PacketReaderUDP(self.SOCK,self.read_queue)
                    self.packet_reader.start()
            
                self.connected = True
                
                return True
                
            except:
                logger.error("Could not connect to disk sensor")
                return False
        
        return True

#         # Set a timeout
#         self.SOCK.settimeout(READ_TIMEOUT)
        
        # Send our "Wake Up" Packets
#         if self.sata_enable_mode:
#             self.sata_enable_all()
            
        


    def _disconnect(self):
        """ Close up shop """
        
        logger.debug("Disconnecting from disk sensor.")
        
        self.connected = False
        
        # Close our socket
        if self.SOCK is not None:
            logger.debug("Closing socket.")
            self.SOCK.close()
            self.SOCK = None
            
 
        
        # Are we using a threaded reader?         
        if self.packet_reader is not None:
            logger.debug("Killing packet reader thread.")
            try:
                logger.debug("Trying to kill reader thread.")
                self.packet_reader.stop()
                self.packet_reader = None
                logger.debug("Reader thread killed.")
            except:
                G.print_traceback()
                pass
            
        # Close our threaded process for reading?
        if self.read_queue is not None:
            logger.debug("Closing queue.")
            self.read_queue.close()  

        
        
        
    def _send_command(self, operation, memory_address, data=None):
        """
            This is used to interface with the card
        """
        
        if not self._connect():
            return False
        
        # Get our network packet
        packet = LOPHIPacket()

        # Set our agruments
        packet.MAGIC = G.SENSOR_DISK.MAGIC_LOPHI
        packet.op_code = operation
        packet.memory_addr = memory_address
        packet.memory_len = math.ceil(len(data) / 4)
        packet.frame_length = 3 + packet.memory_len # 3 word header + data
        packet.data = data

        raw_data = repr(packet)

        logger.debug("Sending: %s" % repr(packet))
        # Send it across the wire
        self.SOCK.sendto(raw_data, (self.sensor_ip, self.sensor_port))
        
        # Get our response
        lophi_packet = self.get_lophi_packet()
        logger.debug("Response: %s"%str(lophi_packet))
        
        return lophi_packet


    def _read_raw_packet(self,size=G.MAX_PACKET_SIZE):
        """ 
            Read and return raw data from our socket
            
            @return: (data, address) 
        """
        
        if not self._connect():
            return False
            
        # Read UDP data from thread or off the wire
        if self.read_queue is not None:
            recv_data, recv_addr = self.read_queue.get()
        else:
            recv_data, recv_addr = self.SOCK.recvfrom(size)

        logger.debug("Read %d bytes."%len(recv_data))
        return recv_data, recv_addr


    def _get_sata_packet(self):
        """
            This segment of code makes sure that we read our SATA packets in 
            order in case they are re-ordered by the network.
            
            @return: LOPHI SATA packet guaranteed to be in the order sent by sensor.
            @todo: Handle packet loss
        """
        # Make sure SATA extraction is enabled
        if not self.sata_enabled:
            self.sata_enable_all()
        
        lophi_packet = None
        
        while 1:
            # Are we dealing with the heap?
            if len(self.sata_heap) > 0:
                logger.debug("pulling from heap")
                
                heap_next_seqn = self.sata_heap[0][0]
                if heap_next_seqn == (self.last_lophi_seqn+1)%65536 or len(self.sata_heap) > MAX_HEAP_SIZE:
                    
                    if len(self.sata_heap) > MAX_HEAP_SIZE:
                        self.DROPPED_PACKETS += 1
                        logger.warning("Sensor dropped packets. (Data may be unreliable)")
                    lophi_seqn, lophi_packet = heapq.heappop(self.sata_heap)
                    # hack to make sure we don't put it back on the heap
                    self.last_lophi_seqn = lophi_seqn
                    self.last_lophi_packet = lophi_packet
                    
                    # Return our packet
                    return lophi_packet
                
                else:
                    lophi_packet = self.get_lophi_packet()
            else:
                # Get the next packet from the wire
                lophi_packet = self.get_lophi_packet()
            
            # Check the packet from the wire and see if it has the seqn we expected
            if lophi_packet.op_code == G.SENSOR_DISK.OP.SATA_FRAME:
                
                # Extract our SATA data
                sata_frame = SATAFrame(lophi_packet.data)
                
                # Get our sequence number
                lophi_seqn = sata_frame.seqn_num
                
                # Take care of our ordering
                if self.last_lophi_seqn is not None:
                    # If this is an out of order packet, push to heap
                    if lophi_seqn != (self.last_lophi_seqn+1)%65536:
                        heapq.heappush(self.sata_heap,(lophi_seqn,lophi_packet))
                        logger.debug("Got out of order packet. (Expected: %d, Got: %d)"%(
                                                (self.last_lophi_seqn+1)%65536,
                                                lophi_seqn))
                        continue
                
                # Save our last seqn
                self.last_lophi_seqn = lophi_seqn
                self.last_lophi_packet = lophi_packet
                
                # Return our packet
                return sata_frame
                
            else:
                continue
    
    def is_up(self):
        """
            Check to see if the card is up
            
            @return: True/False
        """
        try:
            # Open socket
            sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    
            # connect to our port+1 which is a UDP loopback
            addr = (self.sensor_ip, self.bind_port+1)
            sock.connect(addr)
            
            # Set a timeout
            sock.settimeout(READ_TIMEOUT)
        
            # Try to send a packet to the UDP loopback
            sock.send("PING")
            
            # Ensure we got the same data back
            rtn = sock.recv(1024)
            if rtn == "PING":
                return True
            
            sock.close()
        except:
            pass
        
        # If anything goes wrong, the card isn't there.
        return False
    
    
    def get_disk_packet(self):
        """
            For now just return the SATA frame, we'll deal with this later.
        """
        logger.debug("Reading disk packet (SATA Frame in this case)...")
        return self._get_sata_packet()

    
    def get_lophi_packet(self):
        """
            Get the next packet header/data
            
            @return: LO-PHI packet as a dictionary 
                - lophi_header: LO-PHI defined header
                - lophi_data:                       
        """
        # Read data off the wire
        logger.debug("Reading LO-PHI packet...")
        recv_data, recv_addr = self._read_raw_packet()

        network_packet = LOPHIPacket(recv_data)

#         logger.info("Received: %s"%network_packet)        
        return network_packet

    
    """
        Physical Specific Functions
    """


    def get_version(self):
        """
            Returns the current version of the sensor
            
            @return: Version of sensor retrieved from card
        """
        self._send_command(G.SENSOR_DISK.OP.REG_READ, G.SENSOR_DISK.ADDR.VERSION)
        packet = self.get_lophi_packet()
        version = packet.data
        return version

    def sata_enable_host(self):
        """
            Enable host only SATA extraction
        """
        logger.debug("Sending packet to enable sata extraction.")
        self._send_command(G.SENSOR_DISK.OP.REG_WRITE, 
                           G.SENSOR_DISK.ADDR.SATA_CTRL,
                            "\x00\x00\x00\x01")

    def sata_enable_device(self):
        """
            Enable device only SATA extraction
        """
        logger.debug("Sending packet to enable sata extraction.")
        self._send_command(G.SENSOR_DISK.OP.REG_WRITE, 
                           G.SENSOR_DISK.ADDR.SATA_CTRL,
                            "\x00\x00\x00\x02")

    def sata_enable_all(self):
        """
            Enable bi-directional SATA extraction
        """
        logger.debug("Sending packet to enable sata extraction.")
        self._send_command(G.SENSOR_DISK.OP.REG_WRITE,
                           G.SENSOR_DISK.ADDR.SATA_CTRL,
                           "\x00\x00\x00\x03")
        self.sata_enabled = True

    def sata_disable(self):
        """
            Disable SATA extraction
        """
        logger.debug("Sending packet to disable sata extraction.")
        self._send_command(G.SENSOR_DISK.OP.REG_WRITE,
                           G.SENSOR_DISK.ADDR.SATA_CTRL,
                           "\x00\x00\x00\x00")
        self.sata_enabled = False
        return True

    def print_all_registers(self):
        
        
        registers = self.get_all_registers()

        for (reg, addr, data) in registers:
            data_list = struct.unpack("%dB" % len(data), data)
                
            data_list2 = " ".join([hex(i) for i in data_list])
            
            print "%s (%s): %s / %s"%(reg,hex(addr),data_list2,data_list)
            

    def get_all_registers(self):
        """
            Returns all of the registers on the sensor
            
            @return: list of tuples (name, address, value)
        """
        logger.debug("Requesting all registers")
        
        
        
        registers = []
        for reg in G.SENSOR_DISK.ADDR.__dict__: # @UndefinedVariable
            if reg.startswith("_"):
                continue
            
            addr = G.SENSOR_DISK.ADDR.__dict__[reg] # @UndefinedVariable
            
            lophi_packet = self._send_command(G.SENSOR_DISK.OP.REG_READ, addr, "",1)
            
            data = lophi_packet.data
            
            registers.append((reg,addr,data))
            
        return [x for x in registers if x is not None] 
        
        

    def set_dest_ip(self,dest_ip):
        """
            Set the destination IP for our SATA frames
            
            @param dest_ip: Destination IP that SATA packets will be sent to 
        """
        
        logger.debug("Setting destination IP to %s."%dest_ip)
        
        ip = struct.pack('>I',NET.ip2int(dest_ip))
        
        lophi_packet = self._send_command(G.SENSOR_DISK.OP.REG_WRITE, 
                                          G.SENSOR_DISK.ADDR.IP_DEST, 
                                          ip)
        
        if lophi_packet.header['op_code'] == G.SENSOR_DISK.OP.REG_WRITE and lophi_packet.header['memory_addr'] ==  G.SENSOR_DISK.ADDR.IP_DEST:
            return True
        else:
            return False
        
        
    def set_sensor_ip(self,ip):
        """
            Set the card IP address
            
            @param ip: IP address of this sensor 
        """
        
        logger.debug("Setting sensor IP to %s."%ip)
        
        ip = struct.pack('>I',NET.ip2int(ip))
        
        lophi_packet = self._send_command(G.SENSOR_DISK.OP.REG_WRITE, 
                                          G.SENSOR_DISK.ADDR.IP_SENS, 
                                          ip)
        
        if lophi_packet.header['op_code'] == G.SENSOR_DISK.OP.REG_WRITE and lophi_packet.header['memory_addr'] ==  G.SENSOR_DISK.ADDR.IP_SENS:
            return True
        else:
            return False

        
    def set_udp_delay(self,delay):
        """
            Set the intrapacket delay for UDP packets
            
            @param delay: Delay in clock cycles (~10ns/cycle) between packets
        """
        
        logger.debug("Setting UDP intrapacket delay to: %s"%delay)
        
        delay = struct.pack('>I',delay)
        
        lophi_packet = self._send_command(G.SENSOR_DISK.OP.REG_WRITE, 
                                          G.SENSOR_DISK.ADDR.UDP_DELAY,
                                          delay)
        
        if lophi_packet.header['op_code'] == G.SENSOR_DISK.OP.REG_WRITE and lophi_packet.header['memory_addr'] ==  G.SENSOR_DISK.ADDR.UDP_DELAY:
            return True
        else:
            return False
        
        
    def set_mtu_size(self,mtu_size):
        """
            Set the MTU size for UDP packets
            
            @param mtu_size: Maximum transfer unit size for UDP packets in bytes
        """
        
        logger.debug("Setting MTU for packets to: %s"%mtu_size)
        
        mtu_size = struct.pack('>I',mtu_size)
        
        lophi_packet = self._send_command(G.SENSOR_DISK.OP.REG_WRITE, 
                                          G.SENSOR_DISK.ADDR.MTU_SIZE,
                                          mtu_size)
        
        if lophi_packet.header['op_code'] == G.SENSOR_DISK.OP.REG_WRITE and lophi_packet.header['memory_addr'] ==  G.SENSOR_DISK.ADDR.MTU_SIZE:
            return True
        else:
            return False
Beispiel #4
0
class MemorySensorPhysical(MemorySensor):
    """
        This is our interface to both our NetFPGA and ML507 boards using Josh's
        code.
    """
    
    
    def __init__(self, sensor_ip=G.SENSOR_MEMORY.DEFAULT_IP, 
                sensor_port=G.SENSOR_MEMORY.DEFAULT_PORT,  
                 cache_timeout=0, 
                 name=None,
                 use_threading=False,
                 timeout=1,
                 retries=5):
        """
            Initialize our memory sensor.  Just saving values at this point.
    
            @param cache_timeout: How long to keep data in the cache (seconds)
            @param name: Human name of the sensor
            @param use_threading: This will spawn a new process to read replys 
            from the sensor.  Enables much faster reads, but will eventually 
            blow the UDP stack in the FPGA. 
        """
        # Sensor info
        self.sensor_ip = sensor_ip
        self.sensor_port = sensor_port
        
        # Socket variables
        self._sock = None
        self.TIMEOUT = timeout
        self.RETRIES = retries
        self.TIMED_OUT = False
        self.connect_count = 0
        
        # Are we reading a separate process?
        self.use_threading = use_threading
        self.read_queue = None
        self.packet_reader = None
        
        # Cache
        self.cache = {}
        self.cache_timeouts = {}
        self.CACHE_TIMEOUT = cache_timeout # seconds
        
        # Keep track of our transaction
        self.transaction_no = 1
        
        if name is not None:
            self.name = name
            
        # Bad read regions (On XPSP3x86)
        # Ref: http://wiki.osdev.org/Memory_Map_%28x86%29
        self.BAD_MEM_REGIONS = [(0xA0000, 0x100000), # VGA and PCI
                                (0x7fe00000,0x80000000), # No Clue
                                (0xdbf00000,0x100000000), # High Mem PCI devices
                                (0x3ff00000,0x40000000) # No clue (End of memory?
                                ]
        
        # Initialize our superclass as well
        MemorySensor.__init__(self)
        
        
    def __del__(self):
        """
            Clean up any connections when the object is destroyed
        """
        self._disconnect()
        
        
    def _connect(self):
        """
            Connect our socket. 
        """
        # Is the socket already open?
        if self._sock is None:
            # Open our socket
            try:
                logger.debug("Connecting to memory sensor. ")
                s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
                s.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 100000)
                s.connect((self.sensor_ip, self.sensor_port))
                
#                 if self.timeout is not None:
#                     s.settimeout(self.timeout)
    
                if self.use_threading:
                    logger.debug("Starting listener thread.")
                    # Start a process that will handle all of our reads
                    self.read_queue = multiprocessing.Queue()
                    self.packet_reader = PacketReaderUDP(s,self.read_queue)
                    self.packet_reader.start()
                elif self.TIMEOUT is not None and self.TIMEOUT > 0 :
                    s.settimeout(self.TIMEOUT)
                    
                
            except:
                logger.error("Could not connect to memory sensor. (%s,%d)"%(self.sensor_ip,self.sensor_port))
                self.connect_count += 1
                
                if self.connect_count > self.RETRIES:
                    raise socket.error("Could not connect to memory sensor.")
                
                return False
            
            
            # Save our socket
            self._sock = s

        return True
        self.connect_count = 0
    
    
    def _disconnect(self):
        """
            Disconnect our socket.
        """
        if self._sock is not None:
            self._sock.close()
            self._sock = None
            
        if self.read_queue is not None:
            self.read_queue.close()
            self.read_queue = None
            
        if self.packet_reader is not None:
            self.packet_reader.terminate()
            self.packet_reader = None
    
    
    def _send_command(self,command,address,length, data=None):
        """
            Send a command to to the card
            
            @param command: LO-PHI command to send
            @param address: Address on the SUT (Not address on the FPGA)
            @param length: Length for the command (Not the length of the packet)
            @param data: Any data to append to the packet 
        """
        
        logger.debug("Sending command (0x%x, 0x%x, %d)"%(command,address,length))
   
        # Build our payload
        packet = MemoryRapidPacket()
        
        # Constants
        packet.MAGIC_LOPHI = G.SENSOR_MEMORY.MAGIC_LOPHI
        packet.flags = G.SENSOR_MEMORY.DEFAULT_FLAGS
        
        # Command
        packet.operation = command
        
        # Split our address
        lowaddress = (address) & 0xFFFFFFFF
        highaddress = address >> 32
        packet.address_high = highaddress
        packet.address_low = lowaddress
        
        # Data
        packet.length = length
        packet.data = data
        
        # Transcation num
        packet.transaction_no = self.transaction_no
        
        # Increment our transcation number
        self.transaction_no = (self.transaction_no+1)%0x0000ffff
                
        # Keep trying to reconnect
        while not self._connect():
            time.sleep(1)
        
        # Send payload to our sensor
        sent = self._sock.send(`packet`)
        if sent != len(packet):
            logger.error("Only sent {0} out of {1}".format(sent, len(packet)))
            dead = True
            
    
    
    def _read_raw_packet(self,size=G.MAX_PACKET_SIZE):
        """ 
            Read and return raw data from our socket
            
            @return: (data, address) 
        """
        
        # If we already timed out, assume the sensor is dead
        if self.TIMED_OUT:
            raise socket.timeout
        
        # Keep trying to reconnect
        while not self._connect():
            time.sleep(1)
            
        # Read UDP data off the wire
        if self.read_queue is not None:
            recv_data, recv_addr = self.read_queue.get()
        else:
            recv_data, recv_addr = self._sock.recvfrom(size)
            

        logger.debug("Read %d bytes."%len(recv_data))
        return recv_data, recv_addr
    
        
    def _get_read_response(self,length,read_multiple=False):
        """
        
        """
        data = ""
        if read_multiple:
            transaction_no = 0
        else:
            transaction_no = (self.transaction_no -1)%0x0000ffff
            
        
        while(len(data) < length):
            
            # Read a LO-PHI packet
            rapid_packet = self.get_rapid_packet()
            
            if rapid_packet.MAGIC_LOPHI != G.SENSOR_MEMORY.MAGIC_LOPHI:
                logger.error("Magic number mismatch. (%x)"%(rapid_packet.MAGIC_LOPHI))
                return None
            
            # Same transaction?
            if(rapid_packet.transaction_no != transaction_no):
                logger.error("different transaction! %x instead of %x"%(rapid_packet.transaction_no, 
                                                                             transaction_no))
                return None
            
            # Is this the correct response?
            if(rapid_packet.operation != G.SENSOR_MEMORY.COMMAND.READ + 0x2): # RAPID reply is 0x2
                logger.error("not a read?! {0}".format(rapid_packet.operation))
                logger.error(rapid_packet)
                continue
            
            if(rapid_packet.data is None or len(rapid_packet.data) != rapid_packet.length):
                logger.error("DATA LENGTHS DON'T MATCH! (Expected: %d, Got: %d bytes)"%(rapid_packet.length,
                                                                                        len(rapid_packet.data)))
            
            # Append our data
            data += rapid_packet.data
            
            # look for next transaction
            transaction_no = (transaction_no+1)%0x0000ffff

        # Just in case we read more than we wanted, truncate off the end bytes
        return (data[:length])
        
        return None
        
        
    def _read_from_sensor(self,address,length):
        """
            This is the lowest level read command and the only read command that
             will acctually perform a memory read from the sensor.
            
            @param address: Physical memory address to read
            @param length: Length of memory to read starting at @address 
            
            @TODO: Remove our horrible hack once the hardware is up-to-date
        """
        with network_lock:
            # This is a HACK to work around a bug in the PCI sensor that has trouble
            # when not reading on word boundaries?
            adjust_addr = 0
            
            # Is our start address word aligned?
            if address%4 != 0:
                adjust_addr = address%4
                address -= adjust_addr
                length += adjust_addr
                
            # Only read in words.
            adjust_len = 0
            if length%4 != 0:
                adjust_len = 4-length%4
                length += adjust_len
            
            self.transaction_no = 0
            rtn_data = ""
            
            remaining_length = length
            offset = 0
            
            if not self.use_threading:
                while remaining_length > 0:
                    # Calculate how much to read?
                    req_len = min(READ_CHUNK,remaining_length)
                    # Send our read command
                    
                    # Try to read RETRIES times
                    attempt = 0
                    while attempt < self.RETRIES:
                        try:
                            # Send read command
                            self._send_command(G.SENSOR_MEMORY.COMMAND.READ, 
                                               address+offset, 
                                               req_len)
                            # get data off the wire
                            tmp = self._get_read_response(req_len)
                            
                            # Something bad happen in the read, let's try to re-open the socket
                            if tmp is None:
                                logger.error("Didn't get a response from sensor. Trying again.")
                                self._disconnect()
                                self._connect()
                                continue
                            
                            break
                        
                        except socket.timeout:
                            logger.error("Memory sensor timeout (%d/%d)"%(attempt,
                                         self.RETRIES))
                            pass
                        
                        attempt += 1
                        
                    # if we hit our retries, the card has timed out.
                    if attempt == self.RETRIES:
                        logger.error("Memory sensor timed out! (0x%16X, %d)"%
                                     (address+offset,
                                     req_len))
                        raise socket.timeout

                    # If nothing came back, keep trying!
                    if tmp is None:
#                         continue
                        rtn_data = None
                        break
                    
                    rtn_data += tmp 
                    
                    # Calculate how much more we have to read
                    remaining_length -= req_len
                    offset += req_len
            
            else:
                # Try to read RETRIES times
                attempt = 0
                while attempt < self.RETRIES:
                    try:
                        
                        # Send all of our read commands
                        while remaining_length > 0:
                            # Calculate how much to read?
                            req_len = min(READ_CHUNK,remaining_length)
                            # Send our read command
                            self._send_command(G.SENSOR_MEMORY.COMMAND.READ, address+offset, req_len)
                        
                            # Calculate how much more we have to read
                            remaining_length -= req_len
                            offset += req_len
                            
                        # Read all of the data back at once.
                        rtn_data = self._get_read_response(length, True)
                        
                        # Something bad happen in the read, let's try to re-open the socket
                        if rtn_data is None:
                            logger.error("Didn't get a response from sensor. (%d/%d)"%(attempt,
                                     self.RETRIES))
                            time.sleep(1)
                            self._disconnect()
                            self._connect()
                            remaining_length = length
                        else:
                            break
                        
                    except socket.timeout:
                        logger.error("Memory sensor timeout (%d/%d)"%(attempt,
                                     self.RETRIES))
                        pass
                    
                    attempt += 1
                        
                if attempt == self.RETRIES:
                    logger.error("Memory sensor timed out! (0x%16X, %d)"%
                                 (address,
                                 length))
                    raise socket.timeout
                    
                
        
    
            # return the read data
            # HACK: start from our offset and truncate extra data appended to ensure
            # that we were word-aligned
            if rtn_data is None:
                return rtn_data
            else:
                return rtn_data[adjust_addr:length-adjust_len]
        
    
    
    
        
    def get_rapid_packet(self):
        """
            Read the next memory sensor packet from the wire.
            
            @return: LO-PHI packet as a dictionary 
                - lophi_header: LO-PHI defined header
                - lophi_data:  
        """
        # Read data off the wire
        logger.debug("Reading LO-PHI packet...")
        recv_data, recv_addr = self._read_raw_packet()

        network_packet = MemoryRapidPacket(recv_data)

#         logger.info("Received: %s"%network_packet)        
        return network_packet
    
    
    def write(self,address,data):
        """
            Write data to physical memory
        """
        from lophi.data import DataStruct
        class MemoryWritePacket(DataStruct):
            """
                This defines the header used by then RAPID protocol, which is abused
                by our memory sensors.
            """
            name = "RAPIDPacket"
            STRUCT = [('MAGIC_LOPHI','!I'),
                      ('transcation_no','!H'), 
                      ('operation','!B'), 
                      ('address','!7xQ'), 
                      ('length','!I')
                      ]
        
        write_packet = MemoryWritePacket()
        write_packet.address = address
        write_packet.data = data
        write_packet.length = len(data)
        write_packet.operation = G.SENSOR_MEMORY.COMMAND.WRITE
        write_packet.MAGIC_LOPHI = G.SENSOR_MEMORY.MAGIC_LOPHI
        
        # Keep trying to reconnect
        while not self._connect():
            time.sleep(1)
        
        # Send payload to our sensor
        sent = self._sock.send(`write_packet`)
        if sent != len(write_packet):
            logger.error("Only sent {0} out of {1}".format(sent, len(write_packet)))
            dead = True