blob: 17c6a15d6de4cd403ee80388634978750ff6a6b8 [file] [log] [blame]
# Copyright 2016 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Allows creation of gpio interface via stm32 usb."""
import array
import logging
import struct
import usb
import gpio_interface
import stm32usb
class SgpioError(Exception):
"""Class for exceptions of Sgpio."""
def __init__(self, msg, value=0):
"""SgpioError constructor.
msg: string, message describing error in detail
value: integer, value of error when non-zero status returned. Default=0
super(SgpioError, self).__init__(msg, value)
self.msg = msg
self.value = value
class Sgpio(gpio_interface.GpioInterface):
"""Provide interface to stm32 gpio USB endpoint.
Instance Variables:
_logger: Sgpio tagged log output
_susb: stm32 usb object
def __init__(self, vendor=0x18d1, product=0x500f, interface=1,
"""Sgpio constructor.
Loads libraries for libusb. Creates instance objects
and Gpio to iteract with the library and intializes them.
vendor : usb vendor id of stm32 device
product : usb product id of stm32 device
interface : interface number ( 1 - 4 ) of stm32 device to use
serialname: string of device serialname/number TODO: is this a thing?
SgpioError: An error accessing Sgpio object
self._logger = logging.getLogger('Sgpio')
self._susb = stm32usb.Susb(vendor=vendor, product=product,
interface=interface, serialname=serialname,
self._logger.debug('Set up stm32 gpio')
def __del__(self):
"""Sgpio destructor."""
def wr_rd(self, offset, width=1, dir_val=None, wr_val=None, chip=None,
"""Write and/or read GPIO bit.
offset : bit offset of the gpio to read or write
width : integer, number of contiguous bits in gpio to read or write
dir_val : Not used. defaulted to None.
wr_val : value to write to the GPIO. If unset, skips the write.
chip : Not used. defaulted to None.
muxfile : Not used. defaulted to None.
integer value from reading the gpio value ( masked & aligned )
'%s, width=%s, dir_val=%s, wr_val=%s)' %
(offset, width, dir_val, wr_val))
# Read preexisting values for debug output.
ret =, self._susb.TIMEOUT_MS)
read_mask = struct.unpack('<I', ret)[0]
self._logger.debug('Read mask: 0x%08x' % read_mask)
width_mask = (1 << width) - 1
set_mask = 0
clear_mask = 0
if wr_val != None:
set_mask = (wr_val & width_mask) << offset
clear_mask = (~wr_val & width_mask) << offset
byte_str = struct.pack('<II', set_mask, clear_mask)
ret = self._susb._write_ep.write(byte_str, self._susb.TIMEOUT_MS)
if (ret != len(byte_str)):
raise SgpioError('Wrote %d bytes, expected %d' % (ret, len(byte_str)))
# GPIO cached values update on read,
ret =, self._susb.TIMEOUT_MS)
ret =, self._susb.TIMEOUT_MS)
if len(ret) != 4:
raise SgpioError('Read error: expected 4 bytes, got %d [%s]' % (len(ret),
read_mask = struct.unpack('<I', ret)[0]
self._logger.debug('Read mask: 0x%08x' % read_mask)
readvalue = (read_mask >> offset) & width_mask
self._logger.debug('Read value: 0x%x' % readvalue)
return readvalue
def close(self):
"""Stm32gpio wind down logic.
Note: because we run this in a thread, an exception gets thrown at the very
end unless we explicitly predelete this instance.
del self._susb
def test():
"""Test code.
loglevel = logging.DEBUG
format='%(asctime)s - %(name)s - ' + '%(levelname)s - %(message)s')
sobj = Sgpio()
for i in range(1, 2):
rd_val = sobj.wr_rd(2, wr_val=0)
logging.debug('rd_val = %d after <2> -> 0', (rd_val))
if rd_val != 0:
logging.error('rd_val = %d != 0', (rd_val))
rd_val = sobj.wr_rd(2, wr_val=1)
logging.debug('rd_val = %d after <2> -> 1', (rd_val))
if rd_val != 1:
logging.error('rd_val = %d != 1', (rd_val))
rd_val = sobj.wr_rd(2, 1, wr_val=0)
logging.debug('rd_val = %d after <2> -> 0', (rd_val))
if rd_val != 0:
logging.error('rd_val = %d != 0', (rd_val))
# release as output
rd_val = sobj.wr_rd(2)
logging.debug('rd_val = %d after <2> dir released', (rd_val))'rd_val = %d should match pu/pd', (rd_val))
if __name__ == '__main__':