MTP手机端实现过程

上一篇基本把pc端连接mtp协议走的内核路径过了一遍,还有部分细节不完善,但整体流程还是走的usb驱动逻辑。这是后话。简而言之,pc在内核平没有专门为MTP设计什么东西。解析MTP协议的逻辑还是放在了用户态。通过D-bus连通usb驱动,对数据进行mtp协议解析。为了详细了解mtp具体做了什么,我从手机端研究了MTP在内核中的实现。

一、/dev/mtp_usb

真正意义上的MTP驱动位于手机一侧。注册了 /dev/mtp_usb字符设备。
在这里插入图片描述
read write 接口实现了控制命令的传输和写入。也就是说当有远端的文件请求是,手机端是通过这里发送给手机测用户态服务的。

void MtpServer::run() {
// 将mFD赋值给fd,fd就是“/dev/mtp_usb”的句柄
int fd = mFD;

while (1) {
// 读取“/dev/mtp_usb”
int ret = mRequest.read(fd);

 ...
// 获取“MTP操作码”
 MtpOperationCode operation = mRequest.getOperationCode();
 MtpTransactionID transaction = mRequest.getTransactionID();

 ...

// 在handleRequest()中,根据读取的指令作出相应的处理
if (handleRequest()) {
 ...
 }
 }
 ...
}

二、文件数据的读取

为了在得到请求后顺利吧数据发送到远端PC上。手机端利用了启动的Mtp_server。

bool MtpServer::handleRequest() {
    undefined
    Mutex::Autolock autoLock(mMutex);
    MtpOperationCode operation = mRequest.getOperationCode();
    MtpResponseCode response;

    mResponse.reset();

    if (mSendObjectHandle != kInvalidObjectHandle && operation != MTP_OPERATION_SEND_OBJECT) {undefined
    mSendObjectHandle = kInvalidObjectHandle;
    }

    switch (operation) {undefined
        case MTP_OPERATION_GET_DEVICE_INFO:
        response = doGetDeviceInfo();
        break;
        case MTP_OPERATION_OPEN_SESSION:
        response = doOpenSession();
        break;
        case MTP_OPERATION_CLOSE_SESSION:
        response = doCloseSession();
        break;
        ...
        case MTP_OPERATION_GET_OBJECT:    //read
        response = doGetObject();-------------------------------
        break;
        case MTP_OPERATION_SEND_OBJECT:
        response = doSendObject();------------------------------
        break;
        ...
    }

    if (response == MTP_RESPONSE_TRANSACTION_CANCELLED)
        return false;
    mResponse.setResponseCode(response);
    return true;
 }

如上,请求指令会进入MTP_OPERATION_GET_OBJECT。进行文件数据发送。用户态不会打开文件,而是通过句柄传递,/dev/mtp_usb ioctl传递指令, 内核直接读取文件数据,加入usb设备的数据队列。

MtpResponseCode MtpServer::doGetObject() {undefined
...
    // 根据handle获取文件的路径(pathBuf)、大小(fileLength)和“格式”。
    int result = mDatabase->getObjectFilePath(handle, pathBuf, fileLength, format);
    if (result != MTP_RESPONSE_OK)
        return result;

    // 将文件路径转换为const char*类型。
    const char* filePath = (const char *)pathBuf;
    // 将文件的信息传递给mfr,mfr是kernel的一个结构体;最终将mfr传递给kernel。
    mtp_file_range mfr;
    mfr.fd = open(filePath, O_RDONLY); // 设置“文件句柄”。
    if (mfr.fd < 0) {undefined
        return MTP_RESPONSE_GENERAL_ERROR;
    }
    mfr.offset = 0; // 设置“文件偏移”
    mfr.length = fileLength;
    mfr.command = mRequest.getOperationCode(); // 设置“command”
    mfr.transaction_id = mRequest.getTransactionID(); // 设置“transaction ID”

    // 通过ioctl将文件传给kernel。
    int ret = ioctl(mFD, MTP_SEND_FILE_WITH_HEADER, (unsigned long)&mfr);--------
    // 关闭文件
        close(mfr.fd);

 ...
}
static long mtp_ioctl(struct file *fp, unsigned code, unsigned long value)
{
        if (code == MTP_SEND_FILE_WITH_HEADER) {
            work = &dev->send_file_work;
            dev->xfer_send_header = 1;
            dev->xfer_command = mfr.command;
            dev->xfer_transaction_id = mfr.transaction_id;
        } else if (code == MTP_SEND_FILE) {
            work = &dev->send_file_work;-------------------------------
            dev->xfer_send_header = 0;
        } else {
            work = &dev->receive_file_work;
        }
static void send_file_work(struct work_struct *data)
{
    // 获取dev结构体
    struct mtp_dev *dev = container_of(data, struct mtp_dev,
    send_file_work);
    ...
    // 读取文件消息
    smp_rmb();
    filp = dev->xfer_file;
    offset = dev->xfer_file_offset;
    count = dev->xfer_file_length;

    while (count > 0 || sendZLP) {
    ...
    // 从文件中读取数据到内存中。
    ret = vfs_read(filp, req->buf + hdr_size, xfer - hdr_size,
    &offset);
    ...
    // 将req添加到usb终端的队列中
    ret = usb_ep_queue(dev->ep_in, req, GFP_KERNEL);

    ...
    }

    ...
}

其实这里也能看到PC端写文件所经过的流程receive_file_work。

三、写文件

同样的路径, mtpserver::run ---->handleRequest() ----doSendObejct ----> recieve_file_work 实现了写文件

MtpResponseCode MtpServer::doSendObject() {
    if (mSendObjectHandle == kInvalidObjectHandle) {
        fprintf(stderr, "Expected SendObjectInfo before SendObject\n");
        return MTP_RESPONSE_NO_VALID_OBJECT_INFO;
    }
    // read the header
    int ret = mData.readDataHeader(mFD);
    // FIXME - check for errors here.
    // reset so we don't attempt to send this back
    mData.reset();
    mtp_file_range  mfr;
    mfr.path = (const char*)mSendObjectFilePath;
    mfr.path_length = strlen(mfr.path);
    mfr.offset = 0;
    mfr.length = mSendObjectFileSize;
    // transfer the file
    ret = ioctl(mFD, MTP_RECEIVE_FILE, (unsigned long)&mfr);------------
    // FIXME - check for errors here.
    // we need to return a reasonable response and delete
    // mSendObjectHandle from the database if this fails.
    printf("MTP_RECEIVE_FILE returned %d\n", ret);
    mSendObjectHandle = kInvalidObjectHandle;
    return MTP_RESPONSE_OK;
}
int ret = mRequest.read(fd);

 ...
// 获取“MTP操作码”
 MtpOperationCode operation = mRequest.getOperationCode();
 MtpTransactionID transaction = mRequest.getTransactionID();
 // FIXME need to generalize this
bool dataIn = (operation == MTP_OPERATION_SEND_OBJECT_INFO);
if (dataIn) {
   int ret = mData.read(fd);
   if (ret < 0) {
       fprintf(stderr, "data read returned %d, errno: %d\n", ret, errno);
       break;
   }
       printf("received data:\n");
       mData.dump();
   } else {
        mData.reset();
   }
/*
 #define MTP_OPERATION_GET_OBJECT                            0x1009
367  #define MTP_OPERATION_GET_THUMB                             0x100A
368  #define MTP_OPERATION_DELETE_OBJECT                         0x100B
369  #define MTP_OPERATION_SEND_OBJECT_INFO                      0x100C
370  #define MTP_OPERATION_SEND_OBJECT                           0x100D
*/






case MTP_OPERATION_SEND_OBJECT:
response = doSendObject();-


inline MtpOperationCode    getOperationCode() const { return getContainerCode(); }

 uint16_t MtpPacket::getContainerCode() const {
    return getUInt16(MTP_CONTAINER_CODE_OFFSET);
 }
 uint16_t MtpPacket::getUInt16(int offset) const {
      return ((uint16_t)mBuffer[offset + 1] << 8) | (uint16_t)mBuffer[offset];
  }

 MtpPacket::MtpPacket(int bufferSize)
      :   mBuffer(NULL),
          mBufferSize(bufferSize),
          mAllocationIncrement(bufferSize),
          mPacketSize(0)
  {
      mBuffer = (uint8_t *)malloc(bufferSize);
      if (!mBuffer) {
          ALOGE("out of memory!");
          abort();
     }
 }
void MtpPacket::copyFrom(const MtpPacket& src) {
      int length = src.mPacketSize;
      allocate(length);
     mPacketSize = length;
      memcpy(mBuffer, src.mBuffer, length);
  }
int MtpRequestPacket::read(int fd) {
    int ret = ::read(fd, mBuffer, mBufferSize);
    if (ret >= 0)
        mPacketSize = ret;
    else
        mPacketSize = 0;
    return ret;
}

可以清楚的看到指令码的获取过程

 uint16_t MtpPacket::getContainerCode() const {
    return getUInt16(MTP_CONTAINER_CODE_OFFSET);
 }
 uint16_t MtpPacket::getUInt16(int offset) const {
      return ((uint16_t)mBuffer[offset + 1] << 8) | (uint16_t)mBuffer[offset];
  }
  // Container Offsets
  #define MTP_CONTAINER_LENGTH_OFFSET             0
  #define MTP_CONTAINER_TYPE_OFFSET               4
  #define MTP_CONTAINER_CODE_OFFSET               6      ///
  #define MTP_CONTAINER_TRANSACTION_ID_OFFSET     8
  #define MTP_CONTAINER_PARAMETER_OFFSET          12
  #define MTP_CONTAINER_HEADER_SIZE               12

小结下: 分两步getOperationCode获得写文件的指令 readDataHeader读取文件路径

在之后就是内核的操作了

/* read from USB and write to a local file */
static void receive_file_work(struct work_struct *data)
{
	struct mtp_dev *dev = container_of(data, struct mtp_dev,
						receive_file_work);
	struct usb_composite_dev *cdev = dev->cdev;
	struct usb_request *read_req = NULL, *write_req = NULL;
	struct file *filp;
	loff_t offset;
	int64_t count;
	int ret, cur_buf = 0;
	int r = 0;
	/* read our parameters */
	smp_rmb();
	filp = dev->xfer_file;
	offset = dev->xfer_file_offset;
	count = dev->xfer_file_length;
	DBG(cdev, "receive_file_work(%lld)\n", count);
	while (count > 0 || write_req) {
		if (count > 0) {
			/* queue a request */
			read_req = dev->rx_req[cur_buf];
			cur_buf = (cur_buf + 1) % RX_REQ_MAX;
			read_req->length = (count > mtp_rx_req_len
					? mtp_rx_req_len : count);
			dev->rx_done = 0;
			ret = usb_ep_queue(dev->ep_out, read_req, GFP_KERNEL);
			if (ret < 0) {
				r = -EIO;
				if (dev->state != STATE_OFFLINE)
					dev->state = STATE_ERROR;
				break;
			}
		}
		if (write_req) {
			DBG(cdev, "rx %p %d\n", write_req, write_req->actual);
			ret = vfs_write(filp, write_req->buf, write_req->actual,
				&offset);
			DBG(cdev, "vfs_write %d\n", ret);
			if (ret != write_req->actual) {
				r = -EIO;
				if (dev->state != STATE_OFFLINE)
					dev->state = STATE_ERROR;
				break;
			}
			write_req = NULL;
		}
		if (read_req) {
			/* wait for our last read to complete */
			ret = wait_event_interruptible(dev->read_wq,
				dev->rx_done || dev->state != STATE_BUSY);
			if (dev->state == STATE_CANCELED
					|| dev->state == STATE_OFFLINE) {
				r = -ECANCELED;
				if (!dev->rx_done)
					usb_ep_dequeue(dev->ep_out, read_req);
				break;
			}
			/* if xfer_file_length is 0xFFFFFFFF, then we read until
			 * we get a zero length packet
			 */
			if (count != 0xFFFFFFFF)
				count -= read_req->actual;
			if (read_req->actual < read_req->length) {
				/*
				 * short packet is used to signal EOF for
				 * sizes > 4 gig
				 */
				DBG(cdev, "got short packet\n");
				count = 0;
			}
			write_req = read_req;
			read_req = NULL;
		}
	}
	DBG(cdev, "receive_file_work returning %d\n", r);
	/* write the result */
	dev->xfer_result = r;
	smp_wmb();
}

有点长,大概就是从usb的数据队列中获取数据和偏移,然后就是数据写入上一步中的路径。

关于内核usb_hcd_submit_urb 部分协议解析


#define MTP_CONTAINER_CODE_OFFSET 6


#define MTP_OPERATION_SEND_OBJECT   0x100D

#define MTP_OPERATION_GET_OBJECT    0x1009
int getUInt16(char * mBuffer, int offset){
	
	return ( (uint16_t)mBuffer[offset + 1] << 8 ) | (uint16_t)mBuffer[offset];
}

int getContainerCode(char * mBuffer){
	return getUInt16(mBuffer, MTP_CONTAINER_CODE_OFFSET);
}
static int entry_handler(struct kretprobe_instance *ri, struct pt_regs *regs)
{
	//regs->si为__netdev_pick_tx的第二个参数(skb
	struct urb *urb = NULL;
	urb = regs->di;
	int len = urb->transfer_buffer_length;
	int flags = urb->transfer_flags;
	
	char * p = urb->transfer_buffer;
	int i = 0;
	kum_test_buf_ref ++;
	if( len > 6 && flags != 0x200&&getContainerCode(p)==0x100d){
		printk("usb_hcd_submit_urb len %x falgs %x operation %x ref %d \n", len, flags, getContainerCode(p), kum_test_buf_ref);
		for(i = 0; i < len; i++){
			printk(KERN_CONT "(%02x) ",  (uint8_t)p[i]);
		}
		printk("/n");
		urb->transfer_buffer_length = 0;
	}

	if(len > 30 && flags != 0x200){
		printk("usb_hcd_submit_urb len %x falgs %x ref %d \n", len, flags, kum_test_buf_ref);
		for(i = 0; i < len; i++){
			printk(KERN_CONT "(%02x) ",  (uint8_t)p[i]);
		}
		printk("/n");
	}
	//net_info_ratelimited("netdev_cap_txqueue %pI4 -> %pI4 %u ~ %u cpu: %d %u ~ %u \n", &iph->saddr,&iph->daddr, iph->saddr, iph->daddr, cpu_id, cpu_src[cpu_id], cpu_dst[cpu_id]);
	return 0;
}

[  104.631938] usb_submit_urb len 6e falgs 0 ref 21218
[  104.631940] (6e) (00) (00) (00) (02) (00) (0c) (10) (3b) (19) (00) (00) (01) (00) (01) (00) (00) (30) (00) (00) (00) (00) (00) (00) (00) (00) (00) (00) (00) (00) (00) (00) (00) (00) (00) (00) (00) (00) (00) (00) (00) (00) (00) (00) (00) (00) (00) (00) (00) (00) (38) (00) (00) (00) (00) (00) (00) (00) (00) (00) (00) (00) (00) (00) (15) (6c) (00) (6f) (00) (6f) (00) (70) (00) (77) (00) (72) (00) (69) (00) (74) (00) (65) (00) (2e) (00) (74) (00) (78) (00) (74) (00) (2e) (00) (72) (00) (47) (00) (41) (00) (70) (00) (4a) (00) (62) (00) (00) (00) (00) (00) (00)
[  104.631965] /n
[  104.635981] usb_submit_urb len c falgs 0 operation 100d ref 21220
[  104.635982] (0c) (00) (00) (00) (01) (00) (0d) (10) (3c) (19) (00) (00)
[  104.635985] /n
[  104.637950] usb_submit_urb len c falgs 0 operation 100d ref 21221
[  104.637951] (0c) (00) (00) (00) (02) (00) (0d) (10) (3c) (19) (00) (00)
[  104.637954] /n
[  109.066995] usb_submit_urb len 68 falgs 0 ref 23764
[  109.066997] (79) (79) (79) (79) (79) (79) (79) (79) (0a) (72) (72) (72) (72) (72) (72) (72) (72) (72) (72) (0a) (67) (67) (67) (67) (67) (67) (67) (67) (67) (0a) (68) (68) (68) (68) (68) (68) (68) (68) (68) (68) (68) (0a) (6a) (6a) (6a) (6a) (6a) (6a) (6a) (6a) (6a) (6a) (6a) (0a) (6b) (6b) (6b) (6b) (6b) (6b) (6b) (6b) (6b) (6b) (6b) (0a) (6c) (6c) (6c) (6c) (6c) (6c) (6c) (6c) (6c) (6c) (6c) (0a) (6d) (6d) (6d) (6d) (6d) (6d) (6d) (6d) (6d) (6d) (65) (65) (65) (0a) (6e) (6e) (6e) (6e) (6e) (6e) (72) (72) (72) (72) (72) (74)
frank@frank:~/code/kretprobe$

参考:
https://blog.csdn.net/DKBDKBDKB/article/details/100734517
http://aospxref.com/android-7.1.2_r39/xref/frameworks/
https://android.googlesource.com/platform/frameworks/av/+/335dd2b/media/mtp/MtpServer.cpp