CUSE IOCTL 回复包含指向字符数组的结构体

CUSE IOCTL Reply with Struct Containing a Pointer to a Character Array

ctypesFUSECUSE 部分的帮助下,我正在 python 中的用户 space 中实现设备驱动程序。这不是我的第一个 CUSE 驱动程序,过去我已经能够对 return 结构执行以下操作。

class tvStruct(Structure):
    _fields_ = [("tv_sec",c_long),
                ("tv_usec",c_long)]

可以将 timeval 结构传递回使用具有适当编号的 IOCTL 调用的应用程序。以下是CUSE驱动的IOCTL文件操作。

def ioctl(self, req, cmd, arg_p, file_info, uflags, in_buff_p, in_bufsz, out_bufsz):
    ioctl = DECODE_IOC(cmd)
    if ioctl == IOC_READ_TIMEVAL:
        if not in_buff_p:
            PyCuse.fuse_reply_ioctl_retry(req,
                                          pointer(PyCuse.iovec(cast(arg_p,c_void_p),
                                          sizeof(tvStruct))),1,
                                          pointer(PyCuse.iovec(cast(arg_p,c_void_p),
                                          sizeof(tvStruct))),1)
        else:
            tvPtr = cast(in_buff_p,POINTER(tvStruct))
            PyCuse.fuse_reply_ioctl(req, 0, tvPtr, sizeof(tvStruct))
    else:
        print("%s Unrecognized IOCTL #...\n",self.devname)
        PyCuse.fuse_reply_err(req,1)

我正在开发的当前驱动程序需要return一个包含指向字符数组的指针的结构。

# C++ Struct
typedef struct {
    __u16 addr;
    __u16 length;
    __u8* pBuf;
} EEPromData;

# Python Class
class EEPromDataStruct(Structure):
    _fields_ = [("addr",c_ushort),
                ("length",c_ushort),
                ("pBuf",POINTER(c_char))] # I have tried several other ctypes types 
                                          # for `pBuf`, but to no avail yet

我知道我无法将指针从 Python 分配给 pBuf,因为它们不是同一个内存上下文。我还了解到,使用 copy_to_user 从内核 space 中这有点容易。我受要求是 python 应用程序的约束,因为它附加到 python GUI。我感谢所有帮助。

更新 1

我知道这可以通过fuse_reply_ioctl_retry方法解决。此方法允许我从应用程序复制数据或向应用程序复制数据。现在的问题是,在第二次调用 fuse_reply_ioctl_retry 时,FUSE return 向应用程序发送错误代码。我将继续尝试解决此问题,但在此期间如果有任何帮助,我将不胜感激。谢谢。

我想通了。诀窍是为要访问其内容的结构中的每个指针调用 fuse_reply_ioctl_retry,无论是修改它们还是只是从中读取。事实证明,使用 pythonctypes 以及 FUSE/CUSE 非常麻烦,如果其他人正在这样做,我强烈建议为此应用程序切换到直接 C,因为 ctypes 可能已经够棘手了,但在 CUSE/FUSE 系统之上,它非常可怕。我会的,但我必须 python 因为这是另一个应用程序的附加组件。小吐槽结束。

这是解决方案的精简版。

# ctypes Structure for driver
class EEPromDataStruct(Structure):
    _fields_ = [("addr",c_ushort),
                ("length",c_ushort),
                ("pBuf",c_ulong)] #u8*
                # Note that this is a long instead of some pointer.
                # This is so I can easily know the application address
                # the pointer holds.
...

# The IOCTL file operation for this driver
def ioctl(self, req, cmd, arg_p, file_info, uflags, in_buff_p,
                            in_bufsz, out_bufsz):
    ioctl = DECODE_IOC(cmd)
    if ioctl == IOC_READ_EEPROM:
        if not in_buff_p:
            PyCuse.fuse_reply_ioctl_retry(req,pointer(PyCuse.iovec(
                                          cast(arg_p,c_void_p),sizeof(
                                          EEPromDataStruct))),1,None,0)
        else:
            if out_bufsz == 0:
                eepromDataPtr = cast(in_buff_p,POINTER(EEPromDataStruct))
                addr = eepromDataPtr.contents.addr
                length = eepromDataPtr.contents.length
                pBuf = eepromDataPtr.contents.pBuf
                # Load from pBuf
                out_iovecs = pointer(PyCuse.iovec(cast(pBuf,c_void_p),length))
                in_iovecs = pointer(PyCuse.iovec(cast(pBuf,c_void_p),length))
                PyCuse.fuse_reply_ioctl_retry(req,in_iovecs,1,out_iovecs,1)
            else:
                eepromBuff.value = "Some String Here"
                PyCuse.fuse_reply_ioctl(req, 0, eepromBuff, out_bufsz)
        else:
            print("%s Unrecognized IOCTL #...\n",self.devname)
            PyCuse.fuse_reply_err(req,1)

if not in_buff_p 将检查应用程序内存是否已加载到驱动程序中,如果没有,它将调用 fuse_reply_ioctl_retry 进行加载。请注意,现在的一个主要区别是为这次重试指定的输出数据向量是 None。这很重要,因为我随后会检查输出大小。如果它是 0,那么我知道它是我的主要 EEPromDataStruct 并且我继续这条路。我准备了一些数据向量并重试。最后,我获取指针并为其内容分配一些字符串。

这是一个漫长的过程。但它有效。希望对有需要的人有所帮助。