Android Usb源碼分析
-
-
- android上usb裝置接入,拔出, 讀寫操作過程分析
-
-
- a. usb裝置插入,如何組成usbdevice. 存放在UsbManager.devicesList.
- b. usbManager open device 擷取usbConnect
- c. usbConnect如何claimInterface.
- d. bulk/control/interrupt收發資料.
-
- bulk傳輸:
- e. hal層代碼分析.
-
-
android上usb裝置接入,拔出, 讀寫操作過程分析
a. usb裝置插入,如何組成usbdevice. 存放在UsbManager.devicesList.
從兩個角度來看
1, 從裝置插入後, kernel端怎麼處理.
usb裝置插入後host端如何擷取該裝置資訊呢?
分析:
當usb裝置接入時, 會有硬體産生中斷. 收到中斷後host端會由總線驅動程式根據usb協定, 對端口0位址發送一個請求包給usb裝置.
2. 裝置收到請求後會将自己的裝置描述符資訊發給host端.
3. host端通過解析裝置描述符來擷取usb裝置資訊, 同時找到對應的裝置驅動, 調用其prob函數.
在linux 4.4.103上代碼跟蹤如下:
drivers/usb/core/hub.c
hub_irq(struct urb *urb)
|-kick_hub_wq(struct usb_hub *hub)
|-queue_work(hub_wq, &hub->events)
hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
|-INIT_WORK(&hub->events, hub_event);
|-hub_event(struct work_struct *work)
|–port_event(struct usb_hub *hub, int port1)
|—hub_port_connect_change(struct usb_hub *hub, int port1, u16 portstatus, u16 portchange)
|----hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus,u16 portchange)
|-----udev = usb_alloc_dev(hdev, hdev->bus, port1);
|-----hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,int retry_counter)
|------usb_control_msg()
|------hub_set_address(udev, devnum);
|------usb_get_device_descriptor(udev, 8);
…
|-----hcd->driver->update_device(hcd, udev);
未完待續…
2, 從調用角度來跟蹤.
擷取後又是如何将改裝置資訊組成usbdevice對象存放在deviceslist?
反過來跟蹤代碼, 從調用端看.
|-UsbManager#getDeviceList();
|–mService.getDeviceList(bundle);
|—UsbServer#getDeviceList(Bundle devices)
|----UsbHostManager#getDeviceList(Bundle devices)
從全局變量HashMap<String, UsbDevice> mDevices取出資料存入Bundle.
而更新mDevices資料由底層jni函數調用
現在我們需要跟蹤一下JNI層beginUsbDeviceAdded和adding configurations, interfaces and endpoints和endUsbDeviceAdded
分别是在什麼時候調用的
jni的調用來自于com_android_server_UsbHostManager.cpp base\services\core\jni 中
usb_device_added分别調用了
|–method_beginUsbDeviceAdded
|–method_addUsbConfiguration
|–method_addUsbInterface
|–method_addUsbEndpoint
|–method_endUsbDeviceAdded
而這個函數由java端調用.
static const JNINativeMethod method_table[] = {
{ "monitorUsbHostBus", "()V", (void*)android_server_UsbHostManager_monitorUsbHostBus },
{ "nativeOpenDevice", "(Ljava/lang/String;)Landroid/os/ParcelFileDescriptor;",
(void*)android_server_UsbHostManager_openDevice },
};
>UsbHostManager裡
public void systemReady() {
synchronized (mLock) {
// Create a thread to call into native code to wait for USB host events.
// This thread will call us back on usbDeviceAdded and usbDeviceRemoved.
Runnable runnable = this::monitorUsbHostBus;
new Thread(null, runnable, "UsbService host thread").start();
}
}
>systemReady調用來自于UsbService的Lifecycle/SystemService
> @Override
public void onBootPhase(int phase) {
if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
mUsbService.systemReady();
} else if (phase == SystemService.PHASE_BOOT_COMPLETED) {
mUsbService.bootCompleted();
}
}
是以操作又重新回到com_android_server_UsbHostManager.cpp中
static void android_server_UsbHostManager_monitorUsbHostBus(JNIEnv* /* env */, jobject thiz)
{
struct usb_host_context* context = usb_host_init();
if (!context) {
ALOGE("usb_host_init failed");
return;
}
// this will never return so it is safe to pass thiz directly
usb_host_run(context, usb_device_added, usb_device_removed, NULL, (void *)thiz);
}
跟蹤一下usb_host_run
>system/core/libusbhost/usbhost.c
void usb_host_run(struct usb_host_context *context,
usb_device_added_cb added_cb,
usb_device_removed_cb removed_cb,
usb_discovery_done_cb discovery_done_cb,
void *client_data)
{
int done;
done = usb_host_load(context, added_cb, removed_cb, discovery_done_cb, client_data);
while (!done) {
done = usb_host_read_event(context);
}
} /* usb_host_run() */
在循環中輪詢讀取事件
int usb_host_read_event(struct usb_host_context *context)
就是read在init中初始化的fd
struct usb_host_context *usb_host_init()
{
struct usb_host_context *context = calloc(1, sizeof(struct usb_host_context));
if (!context) {
fprintf(stderr, "out of memory in usb_host_context\n");
return NULL;
}
context->fd = inotify_init();
if (context->fd < 0) {
fprintf(stderr, "inotify_init failed\n");
free(context);
return NULL;
}
return context;
}
這裡就涉及到了linux的inotify.
可以看usb_host_load中添加了監聽目錄
/* watch the root for new subdirectories */
context->wdd = inotify_add_watch(context->fd, DEV_DIR, IN_CREATE | IN_DELETE);
也就是說當底層kernel收到usb裝置插入後在/dev目錄下建立了裝置節點或者usb裝置拔出後删除了裝置節點.
都會被監聽到.
進而通過jni層通知到java層.
這裡我們隻是擷取了通過inotify我們隻是擷取到了裝置的挂載的路徑. 而真正解析裝置資訊, 擷取相關裝置描述符則需要回到
com_android_server_UsbHostManager.cpp base\services\core\jni
usb_device_added(const char devname, void client_data)
struct usb_device device = usb_device_open(devname);
const usb_device_descriptor deviceDesc = usb_device_get_device_descriptor(device);
在usb_device_open裡, 直接open(fname)來open裝置節點.
open成功後通過usb_device_new(dev_name, fd);來擷取一個usb_device的結構體.
而描述符資訊是直接通過length = read(fd, device->desc, sizeof(device->desc));讀取4096個位元組來填充.
具體的描述符資訊是怎麼添加的 ,需要看kernel層的處理.
b. usbManager open device 擷取usbConnect
UsbManager#openDevice
/-ParcelFileDescriptor pfd = mService.openDevice(deviceName);
|-UsbService#openDevice
/-fd = mHostManager.openDevice(deviceName, getSettingsForUser(userIdInt));
|-UsbHostManager#openDevice
/-nativeOpenDevice(deviceName);
調用到JNI層com_android_server_UsbHostManager.cpp base\services\core\jni
static const JNINativeMethod method_table[] = {
{ “monitorUsbHostBus”, “()V”, (void*)android_server_UsbHostManager_monitorUsbHostBus },
{ “nativeOpenDevice”, “(Ljava/lang/String;)Landroid/os/ParcelFileDescriptor;”,
(void*)android_server_UsbHostManager_openDevice },
};
|-android_server_UsbHostManager_openDevice
/-usb_device_open
/–usb_device_new
同樣,通過open函數對指定檔案名(裝置挂載路徑)來打開裝置, 通過usb_device_new将裝置資訊放入usb_device結構體中傳回
open操作完後回到UsbManager中.
|-UsbDeviceConnection connection = new UsbDeviceConnection(device);
|-boolean result = connection.open(deviceName, pfd, mContext);
/-native_open
上一步open擷取的pfd用來給到connection的open, 調用到底層的native_open
找到對應的cpp檔案android_hardware_UsbDeviceConnection.cpp base\core\jni
映射數組如下:
static const JNINativeMethod method_table[] = {
{"native_open", "(Ljava/lang/String;Ljava/io/FileDescriptor;)Z",
(void *)android_hardware_UsbDeviceConnection_open},
{"native_close", "()V", (void *)android_hardware_UsbDeviceConnection_close},
{"native_get_fd", "()I", (void *)android_hardware_UsbDeviceConnection_get_fd},
{"native_get_desc", "()[B", (void *)android_hardware_UsbDeviceConnection_get_desc},
{"native_claim_interface", "(IZ)Z",(void *)android_hardware_UsbDeviceConnection_claim_interface},
{"native_release_interface","(I)Z", (void *)android_hardware_UsbDeviceConnection_release_interface},
{"native_set_interface","(II)Z", (void *)android_hardware_UsbDeviceConnection_set_interface},
{"native_set_configuration","(I)Z", (void *)android_hardware_UsbDeviceConnection_set_configuration},
{"native_control_request", "(IIII[BIII)I",
(void *)android_hardware_UsbDeviceConnection_control_request},
{"native_bulk_request", "(I[BIII)I",
(void *)android_hardware_UsbDeviceConnection_bulk_request},
{"native_request_wait", "(J)Landroid/hardware/usb/UsbRequest;",
(void *)android_hardware_UsbDeviceConnection_request_wait},
{ "native_get_serial", "()Ljava/lang/String;",
(void*)android_hardware_UsbDeviceConnection_get_serial },
{"native_reset_device","()Z", (void *)android_hardware_UsbDeviceConnection_reset_device},
};
來看對應的android_hardware_UsbDeviceConnection_open函數
最終也調用了UsbHost.c裡的usb_device_new函數, 好像和之前操作有點重複 , 不清楚為什麼
c. usbConnect如何claimInterface.
接着上面通過connection open操作之後 , 我們需要claimInterface 來確定有權限對指定interface進行操作.
那就調用到了jni的native_claim_interface, 也就是android_hardware_UsbDeviceConnection.cpp base\core\jni中
static const JNINativeMethod method_table[] = {
...
{"native_claim_interface", "(IZ)Z",(void *)android_hardware_UsbDeviceConnection_claim_interface},
...
};
調用int ret = usb_device_claim_interface(device, interfaceID);
int usb_device_claim_interface(struct usb_device *device, unsigned int interface)
{
return ioctl(device->fd, USBDEVFS_CLAIMINTERFACE, &interface);
}
最後調用到ioctl來和kernel通信.
進到/kernel/driver/ 執行grep -rn “USBDEVFS_CLAIMINTERFACE” 看下會調用到哪裡.
usb/core/devio.c:2306: case USBDEVFS_CLAIMINTERFACE:
可以發現跑到devio.c中進行處理.
看檔案開頭注釋來大概了解一下這個檔案的作用.
devio.c – User space communication with USB devices.
This file implements the usbfs/x/y files, where
x is the bus number and y the device number.
It allows user space programs/“drivers” to communicate directly
with USB devices without intervening kernel driver.
嗯 , 也就是免驅動直接和usb裝置進行互動
我們來看ioctl調用到的地方
/*
* NOTE: All requests here that have interface numbers as parameters
* are assuming that somehow the configuration has been prevented from
* changing. But there's no mechanism to ensure that...
*/
static long usbdev_do_ioctl(struct file *file, unsigned int cmd,void __user *p)
{
...
case USBDEVFS_CLAIMINTERFACE:
snoop(&dev->dev, "%s: CLAIMINTERFACE\n", __func__);
ret = proc_claiminterface(ps, p);
break;
...
}
static int proc_claiminterface(struct usb_dev_state *ps, void __user *arg)
{
unsigned int ifnum;
if (get_user(ifnum, (unsigned int __user *)arg))
return -EFAULT;
return claimintf(ps, ifnum);
}
static int claimintf(struct usb_dev_state *ps, unsigned int ifnum)
{
struct usb_device *dev = ps->dev;
struct usb_interface *intf;
int err;
if (ifnum >= 8*sizeof(ps->ifclaimed))
return -EINVAL;
/* already claimed */
if (test_bit(ifnum, &ps->ifclaimed))
return 0;
intf = usb_ifnum_to_if(dev, ifnum);
if (!intf)
err = -ENOENT;
else
err = usb_driver_claim_interface(&usbfs_driver, intf, ps);
if (err == 0)
set_bit(ifnum, &ps->ifclaimed);
return err;
}
後面就是
usb_driver_claim_interface - bind a driver to an interface
device_bind_driver - bind a driver to one device.
/-driver_sysfs_add
/-driver_bound
這裡的幾個函數就不是很了解了 , 後面再來跟進.
d. bulk/control/interrupt收發資料.
現在來看看資料讀寫都是怎麼操作的.
bulk傳輸:
也是從connection開始
|-UsbDeviceConnection#bulkTransfer
/-native_bulk_request
通過jni調用到android_hardware_UsbDeviceConnection.cpp base\core\jni
static const JNINativeMethod method_table[] = {
...
{"native_bulk_request", "(I[BIII)I",
(void *)android_hardware_UsbDeviceConnection_bulk_request},
...
};
需要注意的一點是bulk是支援雙向傳輸的 , 可以通過bulk接受資料,可以發送資料,但都通過同一個接口來操作.
想要區分是發送還是接受資料, 則有傳入端點類型參數來區分, 如果端點是out類型則表示是發送資料, 如果是in則是接受資料.
到了native層則是拿到端點的address來區分.
通過上面的調用, 則會調用到usbhost.c E:\storage\si\android_fw_8.1\system\core\libusbhost
int usb_device_bulk_transfer(struct usb_device *device,
int endpoint,
void* buffer,
int length,
unsigned int timeout)
{
struct usbdevfs_bulktransfer ctrl;
// need to limit request size to avoid EINVAL
if (length > MAX_USBFS_BUFFER_SIZE)
length = MAX_USBFS_BUFFER_SIZE;
memset(&ctrl, 0, sizeof(ctrl));
ctrl.ep = endpoint;
ctrl.len = length;
ctrl.data = buffer;
ctrl.timeout = timeout;
return ioctl(device->fd, USBDEVFS_BULK, &ctrl);
}
又通過ioctl調用到kernel層.
devio.c kernel/drivers/usb/core
static long usbdev_do_ioctl(struct file *file, unsigned int cmd,void __user *p)
{
...
case USBDEVFS_BULK:
snoop(&dev->dev, "%s: BULK\n", __func__);
ret = proc_bulk(ps, p);
if (ret >= 0)
inode->i_mtime = CURRENT_TIME;
break;
...
}
static int proc_bulk(struct usb_dev_state *ps, void __user *arg)
{
struct usb_device *dev = ps->dev;
struct usbdevfs_bulktransfer bulk;
unsigned int tmo, len1, pipe;
int len2;
unsigned char *tbuf;
int i, ret;
//将android層傳入的資料拷貝到kernel層
if (copy_from_user(&bulk, arg, sizeof(bulk)))
return -EFAULT;
ret = findintfep(ps->dev, bulk.ep);//看看目前endpoint在哪個interface
if (ret < 0)
return ret;
ret = checkintf(ps, ret);//檢查一下
if (ret)
return ret;
if (bulk.ep & USB_DIR_IN) //根據endpoint位址來建立輸入或者輸出管道
pipe = usb_rcvbulkpipe(dev, bulk.ep & 0x7f);
else
pipe = usb_sndbulkpipe(dev, bulk.ep & 0x7f);
if (!usb_maxpacket(dev, pipe, !(bulk.ep & USB_DIR_IN)))
return -EINVAL;
len1 = bulk.len;
if (len1 >= USBFS_XFER_MAX)
return -EINVAL;
ret = usbfs_increase_memory_usage(len1 + sizeof(struct urb));
if (ret)
return ret;
tbuf = kmalloc(len1, GFP_KERNEL);
if (!tbuf) {
ret = -ENOMEM;
goto done;
}
tmo = bulk.timeout;
if (bulk.ep & 0x80) {
if (len1 && !access_ok(VERIFY_WRITE, bulk.data, len1)) {
ret = -EINVAL;
goto done;
}
snoop_urb(dev, NULL, pipe, len1, tmo, SUBMIT, NULL, 0);
usb_unlock_device(dev);
i = usb_bulk_msg(dev, pipe, tbuf, len1, &len2, tmo);//bulk開始傳輸資料
usb_lock_device(dev);
snoop_urb(dev, NULL, pipe, len2, i, COMPLETE, tbuf, len2);
if (!i && len2) {
if (copy_to_user(bulk.data, tbuf, len2)) {
ret = -EFAULT;
goto done;
}
}
} else {
if (len1) {
if (copy_from_user(tbuf, bulk.data, len1)) {
ret = -EFAULT;
goto done;
}
}
snoop_urb(dev, NULL, pipe, len1, tmo, SUBMIT, tbuf, len1);
usb_unlock_device(dev);
i = usb_bulk_msg(dev, pipe, tbuf, len1, &len2, tmo);
usb_lock_device(dev);
snoop_urb(dev, NULL, pipe, len2, i, COMPLETE, NULL, 0);
}
ret = (i < 0 ? i : len2);
done:
kfree(tbuf);
usbfs_decrease_memory_usage(len1 + sizeof(struct urb));
return ret;
}
主要關注usb_bulk_msg
message.c \kernel\drivers\usb\core
先來看看函數的注釋
usb_bulk_msg - Builds a bulk urb, sends it off and waits for completion
@usb_dev: pointer to the usb device to send the message to
@pipe: endpoint “pipe” to send the message to
@data: pointer to the data to send
@len: length in bytes of the data to send
@actual_length: pointer to a location to put the actual length transferred
in bytes
@timeout: time in msecs to wait for the message to complete before
timing out (if 0 the wait is forever)
Context: !in_interrupt ()
This function sends a simple bulk message to a specified endpoint
and waits for the message to complete, or timeout.
Don’t use this function from within an interrupt context, like a bottom half
handler. If you need an asynchronous message, or need to send a message
from within interrupt context, use usb_submit_urb() If a thread in your
driver uses this call, make sure your disconnect() method can wait for it to
complete. Since you don’t have a handle on the URB used, you can’t cancel
the request.
Because there is no usb_interrupt_msg() and no USBDEVFS_INTERRUPT ioctl,
users are forced to abuse this routine by using it to submit URBs for
interrupt endpoints. We will take the liberty of creating an interrupt URB
(with the default interval) if the target is an interrupt endpoint.
Return:
If successful, 0. Otherwise a negative error number. The number of actual
bytes transferred will be stored in the @actual_length parameter.
函數作用就是: 建構一個urb(usb request block) , 發送出去, 然後等待它完成.
int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe,
void *data, int len, int *actual_length, int timeout)
{
struct urb *urb;
struct usb_host_endpoint *ep;
ep = usb_pipe_endpoint(usb_dev, pipe);
if (!ep || len < 0)
return -EINVAL;
urb = usb_alloc_urb(0, GFP_KERNEL);
if (!urb)
return -ENOMEM;
//填充urb
if ((ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
USB_ENDPOINT_XFER_INT) {
pipe = (pipe & ~(3 << 30)) | (PIPE_INTERRUPT << 30);
usb_fill_int_urb(urb, usb_dev, pipe, data, len,
usb_api_blocking_completion, NULL,
ep->desc.bInterval);
} else
usb_fill_bulk_urb(urb, usb_dev, pipe, data, len,
usb_api_blocking_completion, NULL);
return usb_start_wait_urb(urb, timeout, actual_length); //等待傳輸完成
}
|-usb_start_wait_urb
|-usb_submit_urb
\-usb_hcd_submit_urb
調用到hcd.c E:\bash\source\kernel\drivers\usb\core
usb_hcd_submit_urb
usbmon_urb_submit
可以看到應該調用到hcd部分了 , 應該是host controller driver.