Idein / py-videocore

Python library for GPGPU on Raspberry Pi
MIT License
795 stars 89 forks source link

can't call get_max_temperature/get_temperature: struct.error: pack_into expected 7 items for packing (got 6) #48

Closed tedder closed 6 years ago

tedder commented 6 years ago

Hey, this is a little off topic, but .. not really. I'm trying to distill mailbox.py so I can use it in another open-source app without needing the heavy dependencies of py-videocore. I'm getting errors with the struct, and I don't think it has to do with what I've removed. Including the output first and then the code.

$ python3 vc.py
adding method: get_temperature
adding method: get_max_temperature
adding method: get_throttled
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__enter__', '__eq__', '__exit__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_add_simple_method', '_simple_call', 'close', 'fd', 'get_max_temperature', 'get_temperature', 'get_throttled']
calling: get_throttled
tag sz: 4
boom. returning.
 n1
0
calling: get_max_temperature
tag sz: 8
Traceback (most recent call last):
  File "vc.py", line 89, in <module>
    print(m.get_max_temperature())
  File "vc.py", line 62, in f
    r = self._simple_call(name, tag, req_fmt, res_fmt, list(args))[5:]
  File "vc.py", line 46, in _simple_call
    *([24 + tag_size, PROCESS_REQUEST, tag, tag_size, tag_size] + args + [0]))
struct.error: pack_into expected 7 items for packing (got 6)

Here's the "distilled" class.

$ cat vc.py

# the following is distilled (reduced) from `py-videocore`, which has a larger purpose but
# happens to include these methods. The following has a MIT license.
# source: https://github.com/nineties/py-videocore

import os
from array import array
from struct import calcsize, pack_into, unpack_from
from fcntl import ioctl

IOCTL_MAILBOX = 0xC0046400   # _IOWR(100, 0, char *)
IOCTL_BUFSIZE = 1024

PROCESS_REQUEST = 0x00000000
REQUEST_SUCCESS = 0x80000000
PARSE_ERROR     = 0x80000001

class MailBoxException(Exception):
  'mailbox exception'

class MailBox(object):
  def __init__(self):
    self.fd = os.open('/dev/vcio', os.O_RDONLY)

  def close(self):
    if self.fd:
      os.close(self.fd)
    self.fd = None

  def __enter__(self):
    return self

  def __exit__(self, exc_type, exc_value, traceback):
    self.close()
    return exc_value is None

  def _simple_call(self, name, tag, req_fmt, res_fmt, args):
    'Call a method which has constant length response.'

    print("calling: {}".format(name))
    # Since the mailbox property interface overwrites the request tag buffer for returning
    # values to the host, size of the buffer must have enough space for both request
    # arguments and returned values. It must also be 32-bit aligned.
    tag_size = (max(calcsize(req_fmt), calcsize(res_fmt)) + 3) // 4 * 4
    print("tag sz: {}".format(tag_size))

    buf = array('B', [0]*IOCTL_BUFSIZE)
    pack_into('=5L' + req_fmt + 'L', buf, 0,
            *([24 + tag_size, PROCESS_REQUEST, tag, tag_size, tag_size] + args + [0]))

    ioctl(self.fd, IOCTL_MAILBOX, buf, True)

    r = unpack_from('=5L' + res_fmt, buf, 0)
    if r[1] != REQUEST_SUCCESS:
      raise MailBoxException('Request failed', name, *args)

    assert(r[4] == 0x80000000 | calcsize(res_fmt))
    print("boom. returning.")
    return r

  @classmethod
  def _add_simple_method(cls, name, tag, req_fmt, res_fmt):
    print("adding method: {}".format(name))
    def f(self, *args):
      r = self._simple_call(name, tag, req_fmt, res_fmt, list(args))[5:]
      n = len(r)
      if n == 1:
        print(" n1")
        return r[0]
      elif n > 1:
        print(" n>1")
        return r
    setattr(cls, name, f)

MAILBOX_METHODS = [
    ('get_temperature',                  0x00030006,  'L',    'LL'),
    ('get_max_temperature',              0x0003000a,  'L',    'LL'),
    ('get_throttled',                    0x00030046,  '',     'L'),

]
for name, tag, req_fmt, res_fmt in MAILBOX_METHODS:
  MailBox._add_simple_method(name, tag, req_fmt, res_fmt)

m = MailBox()
#for name, tag, req_fmt, res_fmt in MAILBOX_METHODS:
#  m._add_simple_method(name, tag, req_fmt, res_fmt)
print(dir(m))
#print(dir(MailBox))
#print(MailBox.get_throttled())
print(m.get_throttled())
print(m.get_max_temperature())
tedder commented 6 years ago

oh! rubber duck debugging worked. The temperature methods require an argument be passed in, which is 0. Not sure what it represents, but calling m.get_max_temperature(0) works, m.get_max_temperature() fails.