2011/02/21

Help for adb on windows

求救!!!求救!!!,不知道有沒有朋友可以教一下,怎樣在 windows 編譯 adb? 因為工作上的需求,需要在 windows 修改屬於自己的 adb.....

關於這個問題,我有找到How to build Android Windows SDK,這篇講的不錯,不過我找到的第一篇是構建Windows版的Android SDK,只是兩篇都沒有教人怎樣成功編譯 adb on windows.....嗚呼

2011/02/14

libusb 初探4-device IO

usb 作為一個通用 IO bus,當然就具備同步與非同步,當然非同步相對複雜,我們就看同步IO的就好了

同步 IO 有二種(其實還有第三種叫 stream, 似乎沒被實作):
libusb_bulk_transfer() : Perform a USB bulk transfer.
及 libusb_interrupt_transfer() : Perform a USB interrupt transfer.

而要跟 device 溝通,很重要的一個函數叫 libusb_control_transfer()
Function Documentation

int libusb_control_transfer ( libusb_device_handle *  dev_handle,
    uint8_t  bmRequestType,
    uint8_t  bRequest,
    uint16_t  wValue,
    uint16_t  wIndex,
    unsigned char *  data,
    uint16_t  wLength,
    unsigned int  timeout  
)   
Perform a USB control transfer.

The direction of the transfer is inferred from the bmRequestType field of the setup packet.

The wValue, wIndex and wLength fields values should be given in host-endian byte order.

Parameters:
    dev_handle  a handle for the device to communicate with
    bmRequestType  the request type field for the setup packet
    bRequest  the request field for the setup packet
    wValue  the value field for the setup packet
    wIndex  the index field for the setup packet
    data  a suitably-sized data buffer for either input or output (depending on direction bits within bmRequestType)
    wLength  the length field for the setup packet. The data buffer should be at least this size.
    timeout  timeout (in millseconds) that this function should wait before giving up due to no response being received. For an unlimited timeout, use value 0.

Returns:
  on success, the number of bytes actually transferred
  LIBUSB_ERROR_TIMEOUT if the transfer timed out
  LIBUSB_ERROR_PIPE if the control request was not supported by the device
  LIBUSB_ERROR_NO_DEVICE if the device has been disconnected
  another LIBUSB_ERROR code on other failures
上面有句話不知道有沒注意到: The wValue, wIndex and wLength fields values should be given in host-endian byte order

底下來寫一點心得:

.首先就是找到 usb device 有兩種方法,一種是透過 cnt = libusb_get_device_list(NULL, &devs) 這樣的呼叫,另一種則是透過 devh = libusb_open_device_with_vid_pid(NULL, 0x05ba, 0x000a);

.在 libusb_get_device_list() 的第一個參數 libusb_context,這是讓你可以同時控制兩個 libusb sessions, 一般情況不必管這個值的話就給 NULL 即可

.底下有一段利用 libusb_control_transfer() 來跟 device 溝通的範例:
#define CTRL_IN                 (LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_IN)
#define USB_RQ                  0x04

static int print_f0_data(void)
{
        unsigned char data[0x10];
        int r;
        unsigned int i;

        r = libusb_control_transfer(devh, CTRL_IN, USB_RQ, 0xf0, 0, data,
                sizeof(data), 0);
        if (r < 0) {
                fprintf(stderr, "F0 error %d\n", r);
                return r;
        }
        if ((unsigned int) r < sizeof(data)) {
                fprintf(stderr, "short read (%d)\n", r);
                return -1;
        }

        printf("F0 data:");
        for (i = 0; i < sizeof(data); i++)
                printf("%02x ", data[i]);
        printf("\n");
        return 0;
}
其中libusb_control_transfer() 裡的第二個參數決定 request 的型態,以上例是 LIBUSB_REQUEST_TYPE_VENDOR, 這個是非 STANDARD, 因此視 application(device) 而定。 .這邊再舉個例子:
int config;
    uint8_t tmp = 0;
    r = libusb_control_transfer(dev, LIBUSB_ENDPOINT_IN,
           LIBUSB_REQUEST_GET_CONFIGURATION, 0, 0, &tmp, 1, 1000);
    if (r == 1) {
          config = tmp;
    }
.最後再來看一個例子
void check_device(libusb_device *dev)
{
  ...
  int r = libusb_get_device_descriptor(dev, &desc);  // 先取得 device descriptor,因為內藏豐富資訊
  ...
  if (!is_adb_interface (desc.idVendor, desc.idProduct,
                         ADB_CLASS, ADB_SUBCLASS, ADB_PROTOCOL))
  { .... }

  /* 底下兩行再取得 device handler 的 bus, address 資訊, 以便給 libusb_open() 使用 */
  uh.dev_bus = libusb_get_bus_number(dev);
  uh.dev_addr = libusb_get_device_address(dev);

  /* 底下四行取得 device 的 active config descriptor,必須其 interface 有值才合法
     因為每個 usb device 是可以 config 成多個不同 device type,
     在此只需要取回現行的 config 即可(即 active config)
  */
  r = libusb_get_active_config_descriptor(dev, &config);
  if (config->interface != NULL) {
        found = check_usb_interfaces(config, &desc, &uh); // 檢查是否 support android adb
  }

  /* 底下開始要進一步與 device 溝通取回更多資訊 */

  r = libusb_open(dev, &uh.devh); // 類似 libusb_open_device_with_vid_pid()
  uh.dev = dev;

  if (found >= 0) {
        ....
        // 在對 endpoint 進行 I/O 之前,一定要 claim interface
        uh.interface = found;
        r = libusb_claim_interface(uh.devh, uh.interface);
        ....
        if (desc.iSerialNumber) {
            // reading serial
            uint16_t    buffer[128] = {0};
            uint16_t    languages[128] = {0};
            int languageCount = 0;

            memset(languages, 0, sizeof(languages));
            r = libusb_control_transfer(uh.devh,
                LIBUSB_ENDPOINT_IN |  LIBUSB_REQUEST_TYPE_STANDARD | LIBUSB_RECIPIENT_DEVICE,
                LIBUSB_REQUEST_GET_DESCRIPTOR, LIBUSB_DT_STRING << 8,
                0, (uint8_t *)languages, sizeof(languages), 0);
            ....
            languageCount = (r - 2) / 2;
            for (i = 1; i <= languageCount; ++i) {
                memset(buffer, 0, sizeof(buffer));

                /* 底下透過 request type 為 LIBUSB_REQUEST_TYPE_STANDARD 來指定
                   LIBUSB_REQUEST_GET_DESCRIPTOR 的方式取回 android device ID string
                   其傳回值型態由 LIBUSB_DT_STRING 指定,其值為 iSerialNumber
                */
                r = libusb_control_transfer(uh.devh,
                    LIBUSB_ENDPOINT_IN |  LIBUSB_REQUEST_TYPE_STANDARD | LIBUSB_RECIPIENT_DEVICE,
                    LIBUSB_REQUEST_GET_DESCRIPTOR, (LIBUSB_DT_STRING << 8) | desc.iSerialNumber,
                    languages[i], (uint8_t *)buffer, sizeof(buffer), 0);
                .... // 處理存在 buffer 中的 serial 資訊,也就是 android device ID
            }
            if (register_device(&uh, serial) == 0) {
                ..... // fail
            }
            else { // success
                libusb_ref_device(dev);
            }
        }
  }
  ......
}

libusb 初探3-usb device descriptor

完整說明請見 usb device descriptor,底下僅列出資料結構:

Data Fields

uint8_t bLength
Size of this descriptor (in bytes).
uint8_t bDescriptorType
Descriptor type.
uint16_t bcdUSB
USB specification release number in binary-coded decimal.
uint8_t bDeviceClass
USB-IF class code for the device.
uint8_t bDeviceSubClass
USB-IF subclass code for the device, qualified by the bDeviceClass value.
uint8_t bDeviceProtocol
USB-IF protocol code for the device, qualified by the bDeviceClass and bDeviceSubClass values.
uint8_t bMaxPacketSize0
Maximum packet size for endpoint 0.
uint16_t idVendor
USB-IF vendor ID.
uint16_t idProduct
USB-IF product ID.
uint16_t bcdDevice
Device release number in binary-coded decimal.
uint8_t iManufacturer
Index of string descriptor describing manufacturer.
uint8_t iProduct
Index of string descriptor describing product.
uint8_t iSerialNumber
Index of string descriptor containing device serial number.
uint8_t bNumConfigurations
Number of possible configurations.

libusb 初探2--libusb_get_device_list & libusb_get_device_descriptor

libusb_get_device_list() & libusb_get_device_descriptor() 這兩個函式,除了在 examples/lsusb.c 中出現範例之外,參考文件可以見libusb descriptor

攤開 libusb 的原始碼,裡面有一個 libusb_open_device_with_vid_pid(), 剛好就用了這兩個 api, 列之如下:


API_EXPORTED libusb_device_handle *libusb_open_device_with_vid_pid(
libusb_context *ctx, uint16_t vendor_id, uint16_t product_id)
{
struct libusb_device **devs;
struct libusb_device *found = NULL;
struct libusb_device *dev;
struct libusb_device_handle *handle = NULL;
size_t i = 0;
int r;

if (libusb_get_device_list(ctx, &devs) < 0) return NULL; while ((dev = devs[i++]) != NULL) { struct libusb_device_descriptor desc; r = libusb_get_device_descriptor(dev, &desc); if (r < 0) goto out; if (desc.idVendor == vendor_id && desc.idProduct == product_id) { found = dev; break; } } if (found) { r = libusb_open(found, &handle); if (r < 0) handle = NULL; } out: libusb_free_device_list(devs, 1); return handle; }

比較特別的就是多了 libusb_open()

透過 lsusb 研究 libusb

lsusb 是個很好用的工具,可以在 libusb取得,說明文件在這兒

打開 lsusb.c 可以看到程式架構很簡單除了初始化及後面的 print 及 free 外,比較重要的只有:
cnt = libusb_get_device_list(NULL, &devs)

原始碼如下:

int main(void)
{
libusb_device **devs;
int r;
ssize_t cnt;

r = libusb_init(NULL);
if (r < 0) return r; cnt = libusb_get_device_list(NULL, &devs); if (cnt < 0) return (int) cnt; print_devs(devs); libusb_free_device_list(devs, 1); libusb_exit(NULL); return 0; }


透過 libusb_get_device_list() 可以得到目前所有的 devices, 稍後再來看,先看看 print_devs()

static void print_devs(libusb_device **devs)
{
libusb_device *dev;
int i = 0;

while ((dev = devs[i++]) != NULL) {
struct libusb_device_descriptor desc;
int r = libusb_get_device_descriptor(dev, &desc);
if (r < 0) { fprintf(stderr, "failed to get device descriptor"); return; } printf("%04x:%04x (bus %d, device %d)\n", desc.idVendor, desc.idProduct, libusb_get_bus_number(dev), libusb_get_device_address(dev)); } }

對每個 usb device 做的動作就只有:
int r = libusb_get_device_descriptor(dev, &desc);
然後把 desc.idVendor, desc.idProduct, libusb_get_bus_number(dev), libusb_get_device_address(dev) 印出來

下一篇再來研究一下剩下的這兩個:
cnt = libusb_get_device_list(NULL, &devs)
int r = libusb_get_device_descriptor(dev, &desc);