USB/IP:iso_packet_descriptor的大小及Haskell虚拟USB设备实现疑问
USB/IP中iso_packet_descriptor的大小与实现方案
描述符固定大小
iso_packet_descriptor的标准固定大小为16字节,这个定义来自USB内核核心层的struct usb_iso_packet_descriptor结构体,USB/IP协议直接复用了该结构体,因此规范文档中未单独明确。
实现方案
等时包的处理逻辑和批量/中断包不同,并非直接使用剩余空间作为描述符,而是遵循以下步骤:
- 确定描述符数量:从USB请求的
num_packets字段获取需要生成的iso_packet_descriptor实例数量 - 计算总描述符长度:总长度为
num_packets * 16字节,这部分是固定开销 - 分配数据空间:包的剩余空间用于存储等时帧的实际数据,每个描述符的
length字段对应单帧数据长度,所有帧长度之和需等于剩余空间大小 - 序列化/反序列化处理:
- 编码时,先按固定16字节每个的规则序列化所有描述符,再拼接后续的等时数据
- 解码时,先读取
num_packets * 16字节解析为描述符数组,再根据每个描述符的offset和length从剩余字节中提取对应帧的数据
Haskell代码示例
import Data.ByteString (ByteString) import qualified Data.ByteString as BS import Data.Word (Word32) import Data.Binary.Put (runPut, putWord32le) import Data.Binary.Get (runGet, getWord32le) import Data.List (unfoldr) -- 定义等时包描述符数据类型 data IsoPacketDescriptor = IsoPacketDescriptor { ipdLength :: Word32 -- 单帧数据长度 , ipdStatus :: Word32 -- 帧传输状态 , ipdOffset :: Word32 -- 帧数据在包中的偏移 , ipdReserved :: Word32 -- 保留字段,设为0即可 } deriving (Show, Eq) -- 序列化单个描述符(固定16字节) serializeIsoPd :: IsoPacketDescriptor -> ByteString serializeIsoPd pd = runPut $ do putWord32le $ ipdLength pd putWord32le $ ipdStatus pd putWord32le $ ipdOffset pd putWord32le $ ipdReserved pd -- 序列化描述符数组 serializeIsoPdList :: [IsoPacketDescriptor] -> ByteString serializeIsoPdList = BS.concat . map serializeIsoPd -- 反序列化单个描述符 deserializeIsoPd :: ByteString -> IsoPacketDescriptor deserializeIsoPd bs = runGet $ do len <- getWord32le stat <- getWord32le off <- getWord32le res <- getWord32le return $ IsoPacketDescriptor len stat off res -- 反序列化描述符数组(需传入数量) deserializeIsoPdList :: Int -> ByteString -> [IsoPacketDescriptor] deserializeIsoPdList num bs = map deserializeIsoPd $ splitEvery 16 bs where splitEvery n = takeWhile (not . BS.null) . unfoldr (Just . BS.splitAt n)
内容的提问来源于stack exchange,提问作者Erdel von Mises
相关产品推荐
相关产品推荐

