ios实现解析外设fat32文件系统(1)-基础工作

来源:互联网 发布:word2016破解知乎 编辑:程序博客网 时间:2024/05/18 02:22


       首先我们需要明确一个概念就是:这种USB外设的归类都是mass-storage,也就是海量存储设备,FAT32系统秉承一个理念就是:一切都是文件,根目录是文件,MBR是文件,硬盘读取的单位是扇区,而数据存储是以簇为单位,所以你读写设备都是要以扇区为单位的,而你读取扇区就相当于读取那个扇区地址存储的文件的数据。另外一个需要明确的是,我们这里读取使用的是scsi的指令,可以参考wiki上的关于scsi指令的介绍:SCSI Command  由于我们只进行读取操作,所以我们只会用到scsi的read(10) 0x28.

首先有关EA读写的部分可以参考官方的EADemo,我这里只讲下怎么处理读取逻辑以及读取后数据怎么处理。

首先我们看看fat32的数据结构:


       我们可以知道根目录并不是第一个扇区,一般情况下我们需要解析完前面2个扇区之后才能找到根目录的起始扇区进而读取根目录

       之前说过我们是用SCSI指令读取扇区的,而scsi指令是字节流,指令路径是CBW->Bluk-in-Bluk-out-CSW,我们首先需要一个CBW的结构体

      

struct CBW{        uint32_t  dCBWSignature;    uint32_t  dCBWTag;    uint32_t  dCBWDataTransferLength;    uint8_t   bmCBWFlags;    uint8_t   bCBWLUN;    uint8_t   bCBWCBLength;    uint8_t   u8OPCode;    uint8_t   u8LUN;    uint8_t   au8Data[14];    };
     接着我们创建一个结构体对象,这个结构体对象后面也会用到

      struct CBW cbwCmd;

      然后我们根据格式封装一个read10函数

void read_10(uint32_t logicalBlockAddress, uint32_t blockNum){    uint32_t tmp;    uint8_t *p = cbwCmd.au8Data;    cbwCmd.dCBWSignature = 0x43425355;//    cbwCmd.dCBWTag = arc4random();//dCBWTag    cbwCmd.dCBWDataTransferLength = blockNum * 512;//    cbwCmd.bmCBWFlags =  0x80;//device to host    cbwCmd.bCBWLUN = 0x00;    cbwCmd.bCBWCBLength = 0x0A;//read10 command 10 bytes    cbwCmd.u8OPCode = 0x28;//opcode    cbwCmd.u8LUN = 0x00;//para1    memset(p, 0, 14);    tmp = logicalBlockAddress;    *((uint8_t *)(p+0)) = *((uint8_t *)&tmp+3);    *((uint8_t *)(p+1)) = *((uint8_t *)&tmp+2);    *((uint8_t *)(p+2)) = *((uint8_t *)&tmp+1);    *((uint8_t *)(p+3)) = *((uint8_t *)&tmp+0);    tmp = blockNum;    *((uint8_t *)(p+5)) = *((uint8_t *)&tmp+1);    *((uint8_t *)(p+6)) = *((uint8_t *)&tmp+0);    }

     这样这个指令我们只需要传入扇区数和读取的扇区的数量就可以读取到扇区数据了。

    我们回到我们的控制器,

我们可以直接将EA框架中的EADSessionController这个类的文件移植到你的工程里,里面对数据收发已经做了逻辑,我们可以只做收发工作。 先创建一个EADSessionController类的对象,我们所有的接口都会调用到他。然后我们还需要一个NSData对象,来接受每一次传输回来的data。

@property(nonatomic,strong) EADSessionController *_eaSessionController;


    接下来来到EA的第一个代理方法

   

- (void)_accessoryDidConnect:(NSNotification *)notification{        EAAccessory *connectedAccessory = [[notification userInfo] objectForKey:EAAccessoryKey];        [self._eaSessionController setupControllerForAccessory:connectedAccessory withProtocolString:[connectedAccessory.protocolStrings objectAtIndex:0]];    [self._eaSessionController openSession] ;    if (self._eaSessionController._session!=nil) {        readcount++;        read_10(0, 1);        [self._eaSessionController writeData:[NSData dataWithBytes:&cbwCmd length:32]];    }    }

     我们打开session之后先读取0扇区数据,读取之后设备会返回0扇区的数据到之前的data对象里,具体代码在EA接收数据的方法

  

   

- (void)_sessionDataReceived:(NSNotification *)notification{    EADSessionController *sessionController = (EADSessionController *)[notification object];    NSInteger bytesAvailable = 0;    while ((bytesAvailable = [sessionController readBytesAvailable]) > 0) {        data = [sessionController readData:bytesAvailable];        if (data) {            _totalBytesRead += bytesAvailable;                    }              }

          由于我们需要插上外设之后显示在tableview上,我们需要一个循环读取,我们接受到0扇区数据处理了之后接着读取MBR,然后根目录,以此类推,我们可以定义一个全局的变量readcount,

         然后我们的代码就变成了如此

       

- (void)_sessionDataReceived:(NSNotification *)notification{    EADSessionController *sessionController = (EADSessionController *)[notification object];    NSInteger bytesAvailable = 0;    while ((bytesAvailable = [sessionController readBytesAvailable]) > 0) {        data = [sessionController readData:bytesAvailable];        if (data) {            _totalBytesRead += bytesAvailable;                    }            }    switch (readcount) {        case 1:{           <p class="p1"><span class="s1">            </span><span class="s2">readcount</span><span class="s1">++;</span></p>            break;        }                    case 2:{          <p class="p1"><span class="s1">            </span><span class="s2">readcount</span><span class="s1">++;</span></p>            break;        }        case 3:{  <p class="p1"><span class="s1">            </span><span class="s2">readcount</span><span class="s1">++;</span></p>            break;        }                          default:            break;       }}


  后面的处理工作都在这里面进行

   


0 0
原创粉丝点击