上一篇基本把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$