/* * Copyright 2008-2012, Haiku Inc. All rights reserved. * Distributed under the terms of the MIT License. * * Authors: * Michael Lotz */ #include "usb_disk.h" #include #include #include #include #include #include #include "scsi_sense.h" #include "usb_disk_scsi.h" #define DRIVER_NAME "usb_disk" #define DEVICE_NAME_BASE "disk/usb/" #define DEVICE_NAME DEVICE_NAME_BASE"%" B_PRIu32 "/%d/raw" //#define TRACE_USB_DISK #ifdef TRACE_USB_DISK #define TRACE(x...) dprintf(DRIVER_NAME ": " x) #define TRACE_ALWAYS(x...) dprintf(DRIVER_NAME ": " x) #else #define TRACE(x...) /* nothing */ #define TRACE_ALWAYS(x...) dprintf(DRIVER_NAME ": " x) #endif int32 api_version = B_CUR_DRIVER_API_VERSION; static usb_module_info *gUSBModule = NULL; static disk_device *gDeviceList = NULL; static uint32 gDeviceCount = 0; static uint32 gLunCount = 0; static mutex gDeviceListLock; static char **gDeviceNames = NULL; static const uint8 kDeviceIcon[] = { 0x6e, 0x63, 0x69, 0x66, 0x0a, 0x04, 0x01, 0x73, 0x05, 0x01, 0x02, 0x01, 0x06, 0x02, 0xb1, 0xf8, 0x5d, 0x3a, 0x2f, 0xbf, 0xbe, 0xdb, 0x67, 0xb6, 0x98, 0x06, 0x4b, 0x22, 0x15, 0x47, 0x13, 0x02, 0x00, 0xed, 0xed, 0xed, 0xff, 0xab, 0xbc, 0xc6, 0x02, 0x01, 0x06, 0x02, 0xb9, 0x82, 0x56, 0x32, 0x7d, 0xfb, 0xb8, 0x06, 0x39, 0xbe, 0xd9, 0xb5, 0x4b, 0x7d, 0x31, 0x4a, 0xa4, 0xe7, 0x00, 0xd1, 0xde, 0xe4, 0xff, 0x7a, 0x9c, 0xae, 0x02, 0x00, 0x16, 0x02, 0x38, 0xe9, 0xaa, 0x3b, 0x7b, 0x1d, 0xbf, 0xb0, 0xa6, 0x3d, 0x16, 0x76, 0x4b, 0x84, 0x81, 0x48, 0x37, 0x36, 0x00, 0x99, 0xff, 0x53, 0x02, 0x00, 0x16, 0x02, 0xba, 0x38, 0x9a, 0xb8, 0xef, 0x79, 0x3e, 0x34, 0x8b, 0xbf, 0x56, 0x52, 0x48, 0x2c, 0x61, 0x4c, 0x4e, 0xec, 0x00, 0x40, 0xff, 0x01, 0x05, 0xff, 0x05, 0x46, 0x02, 0x01, 0x16, 0x02, 0x35, 0xc2, 0x71, 0x3a, 0xf6, 0x84, 0xb9, 0xf3, 0x5b, 0x34, 0x81, 0xa0, 0x49, 0xc0, 0x57, 0x49, 0x6e, 0x51, 0xff, 0xf3, 0x00, 0x52, 0x02, 0x01, 0x06, 0x02, 0x38, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x80, 0x00, 0x49, 0xa0, 0x00, 0x49, 0x80, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x06, 0xe3, 0x06, 0x0c, 0x06, 0x09, 0xab, 0xaa, 0x03, 0x3e, 0x5e, 0x3c, 0x5f, 0x3e, 0x5e, 0x60, 0x4d, 0x5a, 0x4a, 0x60, 0x47, 0x56, 0x42, 0x50, 0x45, 0x4c, 0x43, 0x2a, 0x54, 0x36, 0x5d, 0x36, 0x5d, 0x3a, 0x60, 0x06, 0x08, 0xfb, 0xea, 0x27, 0x49, 0x26, 0x48, 0x27, 0x49, 0x32, 0x56, 0x37, 0x59, 0x37, 0x59, 0x38, 0x5a, 0x3b, 0x59, 0x3a, 0x5a, 0x3b, 0x59, 0x58, 0x3c, 0x52, 0x36, 0x43, 0x29, 0x27, 0x45, 0x27, 0x45, 0x26, 0x46, 0x06, 0x05, 0xab, 0x03, 0x27, 0x49, 0x26, 0x48, 0x27, 0x49, 0x32, 0x56, 0x52, 0x36, 0x43, 0x29, 0x27, 0x45, 0x27, 0x45, 0x26, 0x46, 0x0a, 0x05, 0xc2, 0x1c, 0xb8, 0xf9, 0x4f, 0x25, 0xc9, 0x4c, 0xb7, 0xc4, 0x5a, 0x30, 0x51, 0x39, 0x0a, 0x04, 0xc5, 0x50, 0xbb, 0xc0, 0xc2, 0x1c, 0xb8, 0xf9, 0x4f, 0x25, 0xc9, 0x4c, 0xb7, 0xc4, 0x0a, 0x04, 0x51, 0x39, 0xc5, 0x50, 0xbb, 0xc0, 0xc9, 0x4c, 0xb7, 0xc4, 0x5a, 0x30, 0x0a, 0x04, 0x4f, 0x2f, 0x51, 0x31, 0x53, 0x2f, 0x51, 0x2d, 0x06, 0x04, 0xee, 0x4f, 0x35, 0x53, 0x30, 0x51, 0x32, 0x55, 0x2e, 0x58, 0x2c, 0x54, 0x31, 0x56, 0x2f, 0x52, 0x33, 0x06, 0x04, 0xee, 0x31, 0x58, 0x40, 0x47, 0x39, 0x4e, 0x47, 0x40, 0x50, 0x38, 0x41, 0x48, 0x48, 0x41, 0x3a, 0x4f, 0x08, 0x02, 0x3a, 0x40, 0x3e, 0x3c, 0x02, 0x04, 0x3e, 0x3a, 0xbe, 0x48, 0x3a, 0xbf, 0x9f, 0x3a, 0x41, 0x3d, 0x41, 0xbd, 0xe2, 0x41, 0xbf, 0x39, 0x3e, 0x40, 0xbf, 0x9f, 0x40, 0xbe, 0x48, 0x40, 0x3b, 0x3d, 0x3b, 0xbf, 0x39, 0x3b, 0xbd, 0xe2, 0x06, 0x05, 0xbe, 0x02, 0x32, 0x56, 0x36, 0x5a, 0x36, 0x5a, 0x37, 0x5b, 0x3a, 0x5a, 0x39, 0x5b, 0x3a, 0x5a, 0x58, 0x3c, 0x52, 0x36, 0x11, 0x0a, 0x00, 0x01, 0x00, 0x00, 0x0a, 0x01, 0x01, 0x03, 0x12, 0x3f, 0xe9, 0x8e, 0xbb, 0x54, 0xe2, 0x3b, 0x54, 0xe2, 0x3f, 0xe9, 0x8e, 0xc6, 0x4d, 0xd9, 0x45, 0xc0, 0xc5, 0x01, 0x17, 0x84, 0x00, 0x04, 0x0a, 0x02, 0x01, 0x04, 0x02, 0x3f, 0xe9, 0x8e, 0xbb, 0x54, 0xe2, 0x3b, 0x54, 0xe2, 0x3f, 0xe9, 0x8e, 0xc6, 0x4d, 0xd9, 0x45, 0xc0, 0xc5, 0x0a, 0x03, 0x01, 0x05, 0x02, 0x3f, 0xe9, 0x8e, 0xbb, 0x54, 0xe2, 0x3b, 0x54, 0xe2, 0x3f, 0xe9, 0x8e, 0xc6, 0x4d, 0xd9, 0x45, 0xc0, 0xc5, 0x0a, 0x01, 0x01, 0x01, 0x12, 0x3f, 0xe9, 0x8e, 0xbb, 0x54, 0xe2, 0x3b, 0x54, 0xe2, 0x3f, 0xe9, 0x8e, 0xc6, 0xb0, 0x64, 0x46, 0x78, 0x3b, 0x01, 0x17, 0x84, 0x00, 0x04, 0x0a, 0x04, 0x01, 0x02, 0x02, 0x3f, 0xe9, 0x8e, 0xbb, 0x54, 0xe2, 0x3b, 0x54, 0xe2, 0x3f, 0xe9, 0x8e, 0xc6, 0xb0, 0x64, 0x46, 0x78, 0x3b, 0x0a, 0x05, 0x01, 0x0b, 0x02, 0x3f, 0xe9, 0x8e, 0xbb, 0x54, 0xe2, 0x3b, 0x54, 0xe2, 0x3f, 0xe9, 0x8e, 0xc6, 0xb0, 0x64, 0x46, 0x78, 0x3b, 0x0a, 0x01, 0x01, 0x06, 0x02, 0x3f, 0xe9, 0x8e, 0xbb, 0x54, 0xe2, 0x3b, 0x54, 0xe2, 0x3f, 0xe9, 0x8e, 0xc6, 0x5b, 0x2d, 0x45, 0x43, 0x93, 0x0a, 0x01, 0x01, 0x06, 0x02, 0x3f, 0xe9, 0x8e, 0xbb, 0x54, 0xe2, 0x3b, 0x54, 0xe2, 0x3f, 0xe9, 0x8e, 0xc7, 0x7d, 0x8b, 0x44, 0x36, 0x9a, 0x0a, 0x06, 0x02, 0x07, 0x08, 0x02, 0x3f, 0xe9, 0x8e, 0xbb, 0x54, 0xe2, 0x3b, 0x54, 0xe2, 0x3f, 0xe9, 0x8e, 0xc6, 0x4d, 0xd9, 0x45, 0xc0, 0xc5, 0x0a, 0x01, 0x01, 0x09, 0x12, 0x3f, 0x6c, 0x5c, 0xba, 0xea, 0x46, 0x3a, 0xea, 0x46, 0x3f, 0x6c, 0x5c, 0xc5, 0x19, 0x6c, 0x46, 0x6b, 0x36, 0x01, 0x17, 0x8c, 0x22, 0x04, 0x0a, 0x07, 0x01, 0x09, 0x12, 0x3f, 0xe9, 0x8e, 0xbb, 0x54, 0xe2, 0x3b, 0x54, 0xe2, 0x3f, 0xe9, 0x8e, 0xc6, 0x4d, 0xd9, 0x45, 0xc0, 0xc5, 0x01, 0x17, 0x88, 0x22, 0x04, 0x0a, 0x08, 0x01, 0x09, 0x12, 0x3f, 0xe9, 0x8e, 0xbb, 0x54, 0xe2, 0x3b, 0x54, 0xe2, 0x3f, 0xe9, 0x8e, 0xc6, 0x01, 0xed, 0x46, 0x11, 0xa8, 0x01, 0x17, 0x85, 0x22, 0x04, 0x0a, 0x01, 0x01, 0x0a, 0x12, 0x3f, 0xe9, 0x8e, 0xbb, 0x54, 0xe2, 0x3a, 0xaa, 0x52, 0x3f, 0x21, 0x43, 0xc6, 0x59, 0xd0, 0x46, 0xdb, 0x8c, 0x01, 0x17, 0x84, 0x00, 0x04, 0x0a, 0x06, 0x01, 0x0a, 0x02, 0x3f, 0xe9, 0x8e, 0xbb, 0x54, 0xe2, 0x3b, 0x54, 0xe2, 0x3f, 0xe9, 0x8e, 0xc6, 0x99, 0xc6, 0x45, 0x5e, 0x3a, 0x0a, 0x01, 0x01, 0x0a, 0x12, 0x3f, 0x21, 0x43, 0xba, 0xaa, 0x52, 0x3a, 0xaa, 0x52, 0x3f, 0x21, 0x43, 0xc7, 0xd2, 0xa7, 0x49, 0x5f, 0xed, 0x01, 0x17, 0x84, 0x00, 0x04, 0x0a, 0x09, 0x01, 0x0a, 0x02, 0x3f, 0xe9, 0x8e, 0xbb, 0x54, 0xe2, 0x3b, 0x54, 0xe2, 0x3f, 0xe9, 0x8e, 0xc8, 0xa5, 0xc8, 0x48, 0xeb, 0x05 }; const unsigned char kCDIcon[] = { 0x6e, 0x63, 0x69, 0x66, 0x05, 0x05, 0x00, 0x02, 0x03, 0x06, 0x05, 0xb8, 0x12, 0xa5, 0xbe, 0x03, 0xe1, 0x3d, 0xe7, 0x84, 0xb8, 0x02, 0x10, 0x49, 0xf7, 0x9f, 0x49, 0xed, 0xd8, 0x00, 0xf1, 0xf1, 0xf1, 0x36, 0xd9, 0xdd, 0xf4, 0x8a, 0x99, 0x96, 0xb9, 0xb4, 0xb8, 0xbe, 0xdb, 0xff, 0xf4, 0xf4, 0xf4, 0x04, 0xeb, 0xd0, 0x02, 0x00, 0x06, 0x02, 0x3c, 0x92, 0xc0, 0x38, 0x8f, 0x5f, 0xb8, 0x54, 0x50, 0x3c, 0x57, 0x63, 0x48, 0xd8, 0xdf, 0x48, 0x89, 0x5b, 0x00, 0x41, 0x37, 0xa9, 0xff, 0xb9, 0xb9, 0xb9, 0x04, 0x01, 0x7e, 0x04, 0x02, 0x04, 0x3f, 0x2c, 0x4e, 0x2c, 0x30, 0x2c, 0x22, 0x40, 0x22, 0x34, 0x22, 0x4c, 0x3f, 0x54, 0x30, 0x54, 0x4e, 0x54, 0x5c, 0x40, 0x5c, 0x4c, 0x5c, 0x34, 0x02, 0x04, 0x3f, 0x3a, 0x43, 0x3a, 0x3b, 0x3a, 0x39, 0x3e, 0x39, 0x3c, 0x39, 0x40, 0x3f, 0x42, 0x3b, 0x42, 0x43, 0x42, 0x45, 0x3e, 0x45, 0x40, 0x45, 0x3c, 0x02, 0x04, 0x4b, 0x3e, 0x4b, 0x3a, 0x4b, 0x42, 0x3f, 0x46, 0x47, 0x46, 0x37, 0x46, 0x33, 0x3e, 0x33, 0x42, 0x33, 0x3a, 0x3f, 0xbb, 0xf7, 0x37, 0xbb, 0xf7, 0x47, 0xbb, 0xf7, 0x02, 0x04, 0x40, 0x2a, 0x54, 0x2a, 0x50, 0x2c, 0x5c, 0x40, 0x5c, 0x34, 0x5c, 0x4c, 0x40, 0x56, 0x50, 0x54, 0x54, 0x56, 0x60, 0x40, 0x60, 0x4c, 0x60, 0x34, 0x06, 0x0a, 0x04, 0x01, 0x03, 0x00, 0x0a, 0x00, 0x02, 0x00, 0x01, 0x18, 0x15, 0xff, 0x01, 0x17, 0x84, 0x00, 0x04, 0x0a, 0x00, 0x02, 0x00, 0x01, 0x18, 0x00, 0x15, 0x01, 0x17, 0x86, 0x00, 0x04, 0x0a, 0x01, 0x02, 0x00, 0x02, 0x00, 0x0a, 0x02, 0x02, 0x02, 0x01, 0x00, 0x0a, 0x03, 0x01, 0x02, 0x10, 0x01, 0x17, 0x82, 0x00, 0x04 }; const unsigned char kMemoryStickIcon[] = { 0x6e, 0x63, 0x69, 0x66, 0x09, 0x05, 0x00, 0x04, 0x00, 0x63, 0x02, 0x00, 0x06, 0x02, 0x3a, 0x9c, 0xfb, 0x3b, 0x07, 0x44, 0xbd, 0x68, 0x2d, 0x3c, 0xf0, 0x9a, 0x49, 0x8c, 0xd7, 0x4a, 0x02, 0xf0, 0x00, 0x67, 0x72, 0xc0, 0xff, 0x5e, 0x68, 0xae, 0x02, 0x00, 0x06, 0x02, 0x39, 0xd8, 0xc8, 0x38, 0x57, 0x6f, 0xbb, 0x1e, 0xa3, 0x3c, 0x90, 0x06, 0x4b, 0x3b, 0x28, 0x49, 0x91, 0x9a, 0x00, 0x55, 0x55, 0x84, 0xff, 0x73, 0x79, 0xaa, 0x02, 0x00, 0x06, 0x02, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xba, 0xbf, 0xed, 0xff, 0x81, 0x8b, 0xd9, 0x02, 0x00, 0x06, 0x02, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x4a, 0x00, 0x00, 0xa1, 0x24, 0x8d, 0x00, 0xbd, 0xc1, 0xe9, 0xfe, 0x83, 0x8d, 0xdc, 0x03, 0xc3, 0xca, 0xff, 0x03, 0x08, 0x13, 0x47, 0x03, 0x08, 0x13, 0x47, 0x0d, 0x0a, 0x04, 0x5e, 0xbc, 0x52, 0x3c, 0x5a, 0x42, 0x5a, 0x62, 0xbd, 0x1e, 0x0a, 0x06, 0x22, 0x46, 0x22, 0x4b, 0x3c, 0x58, 0x5e, 0x36, 0x5e, 0x32, 0x46, 0x2a, 0x06, 0x08, 0xfe, 0x9b, 0x3c, 0x52, 0xbc, 0xae, 0xc6, 0x3d, 0xbd, 0x92, 0xc6, 0xa6, 0xbc, 0x79, 0xc6, 0x25, 0x37, 0x52, 0xbc, 0x66, 0xc7, 0x08, 0xba, 0x74, 0xc6, 0x22, 0x26, 0x4a, 0xb7, 0x26, 0xc4, 0x8c, 0xb5, 0x34, 0xc3, 0xa6, 0xb5, 0x9e, 0xc2, 0xfb, 0xb5, 0xc9, 0xc3, 0x0f, 0xb4, 0x89, 0xc2, 0x7b, 0x22, 0x46, 0x4b, 0x3c, 0x58, 0x0a, 0x04, 0x3c, 0x52, 0x5e, 0x32, 0x5e, 0x36, 0x3c, 0x58, 0x0a, 0x04, 0x3c, 0x52, 0x5e, 0x32, 0x46, 0x2a, 0x22, 0x47, 0x06, 0x07, 0xaa, 0x39, 0xc4, 0x10, 0xbf, 0x1a, 0xbc, 0xac, 0xc5, 0xbc, 0x37, 0x50, 0x38, 0x51, 0x50, 0xc4, 0xbc, 0xbe, 0xcb, 0x40, 0x38, 0xbf, 0xe4, 0xbc, 0x7f, 0xbf, 0x8f, 0xbc, 0xa6, 0x06, 0x08, 0xaa, 0xab, 0xb5, 0xeb, 0xc3, 0x48, 0xbe, 0x7b, 0xbc, 0x6a, 0xbe, 0x67, 0xbb, 0xbb, 0xb5, 0x3f, 0xc3, 0x31, 0x26, 0x4a, 0xb5, 0x2e, 0xc3, 0xa5, 0xb6, 0x74, 0xc4, 0x2f, 0x37, 0x52, 0x38, 0x51, 0x30, 0x4d, 0x0a, 0x04, 0x38, 0x50, 0x4a, 0x3e, 0x3c, 0x38, 0xb5, 0xeb, 0xc3, 0x48, 0x06, 0x0d, 0xf6, 0xff, 0xff, 0x03, 0xcc, 0x52, 0xbd, 0x0d, 0xbd, 0x16, 0xca, 0xee, 0xbd, 0x16, 0xca, 0xee, 0xbd, 0x16, 0xca, 0x64, 0xbd, 0x01, 0xc9, 0xf8, 0xbc, 0x36, 0xca, 0x10, 0xbc, 0xb6, 0xc9, 0xbc, 0xbb, 0xb5, 0xc8, 0xf4, 0xbc, 0x36, 0xc8, 0xf4, 0xbb, 0xd9, 0xc9, 0x21, 0xbc, 0xb6, 0xc8, 0x70, 0xbd, 0x16, 0xc8, 0xf7, 0xbd, 0x01, 0xc7, 0xff, 0xbd, 0x13, 0xc7, 0xea, 0xbc, 0x36, 0xc7, 0xbd, 0xbc, 0xda, 0xc7, 0xea, 0xbb, 0xd9, 0xc6, 0xd3, 0xbc, 0x36, 0xc7, 0x24, 0xbb, 0xbb, 0xc6, 0xa6, 0xbc, 0xb0, 0xc5, 0xdb, 0xbd, 0x16, 0xc6, 0x56, 0xbc, 0xfb, 0xc5, 0xdb, 0xbd, 0x16, 0xc4, 0x5f, 0xbd, 0x16, 0xc4, 0x5f, 0xbd, 0x16, 0xc5, 0x24, 0xbc, 0xcb, 0xc6, 0x2f, 0xbb, 0xd9, 0xc5, 0xcf, 0xbc, 0x6c, 0xc7, 0xa8, 0xbb, 0x82, 0xca, 0x94, 0xbb, 0xd9, 0xc9, 0x1e, 0xbb, 0x7c, 0xca, 0xfa, 0xbc, 0x69, 0xcc, 0x52, 0xbd, 0x0d, 0xcb, 0x92, 0xbc, 0xcb, 0xcc, 0x52, 0xbd, 0x0d, 0x06, 0x04, 0xfe, 0xcc, 0x52, 0xbd, 0x91, 0xcc, 0x52, 0xbd, 0x94, 0xcc, 0x52, 0xbd, 0x94, 0xc9, 0xa7, 0xbd, 0xee, 0xc4, 0x5f, 0xbd, 0x91, 0xc7, 0x00, 0xbd, 0xee, 0xc7, 0x12, 0xbd, 0x55, 0xcc, 0x52, 0xbd, 0x91, 0xc9, 0xb3, 0xbd, 0x52, 0xcc, 0x52, 0xbd, 0x91, 0x02, 0x03, 0xc5, 0x75, 0xbe, 0x50, 0xc5, 0x75, 0xbe, 0x50, 0xc7, 0x75, 0xbe, 0x24, 0xcb, 0x50, 0xbe, 0x50, 0xc9, 0x63, 0xbe, 0x24, 0xc9, 0x5a, 0xbe, 0x92, 0xc5, 0x75, 0xbe, 0x50, 0xc7, 0x66, 0xbe, 0x92, 0xc5, 0x75, 0xbe, 0x50, 0x02, 0x02, 0xc6, 0x2f, 0xbe, 0xdd, 0xc7, 0xa5, 0xbf, 0x22, 0xc7, 0xae, 0xbe, 0xb3, 0xca, 0x97, 0xbe, 0xdd, 0xc9, 0x24, 0xbe, 0xb0, 0xc9, 0x1e, 0xbf, 0x22, 0x0a, 0x03, 0x45, 0x2d, 0x4a, 0x2f, 0x4a, 0x2c, 0x0a, 0x0a, 0x01, 0x01, 0x00, 0x00, 0x0a, 0x00, 0x01, 0x01, 0x10, 0x01, 0x17, 0x84, 0x00, 0x04, 0x0a, 0x02, 0x01, 0x02, 0x00, 0x0a, 0x03, 0x01, 0x03, 0x00, 0x0a, 0x04, 0x01, 0x04, 0x00, 0x0a, 0x03, 0x01, 0x06, 0x00, 0x0a, 0x06, 0x01, 0x05, 0x00, 0x0a, 0x05, 0x01, 0x07, 0x00, 0x0a, 0x07, 0x04, 0x09, 0x0a, 0x0b, 0x08, 0x02, 0xbe, 0x56, 0x60, 0x3e, 0x5e, 0xcf, 0xbe, 0x5e, 0xcf, 0xbe, 0x56, 0x60, 0x4c, 0xe9, 0x91, 0x41, 0xdd, 0x95, 0x0a, 0x08, 0x01, 0x0c, 0x00 }; const unsigned char kSDIcon[] = { 0x6e, 0x63, 0x69, 0x66, 0x09, 0x05, 0x00, 0x04, 0x00, 0x63, 0x02, 0x00, 0x06, 0x02, 0x3a, 0x9c, 0xfb, 0x3b, 0x07, 0x44, 0xbd, 0x68, 0x2d, 0x3c, 0xf0, 0x9a, 0x49, 0x8c, 0xd7, 0x4a, 0x02, 0xf0, 0x00, 0xa4, 0xa4, 0xa4, 0xff, 0x5a, 0x5b, 0x65, 0x02, 0x00, 0x16, 0x02, 0x39, 0xd8, 0xc8, 0x38, 0x57, 0x6f, 0xbb, 0x1e, 0xa3, 0x3c, 0x90, 0x06, 0x4b, 0x3b, 0x28, 0x49, 0x91, 0x9a, 0x00, 0x55, 0xff, 0x39, 0x02, 0x00, 0x16, 0x02, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0xff, 0x5b, 0x02, 0x00, 0x06, 0x02, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x4a, 0x00, 0x00, 0xa1, 0x24, 0x8d, 0x00, 0xe5, 0xe7, 0xf9, 0xfe, 0xb3, 0xb5, 0xc3, 0x05, 0xc6, 0x03, 0x08, 0x13, 0x47, 0x05, 0xff, 0x0e, 0x0a, 0x04, 0x60, 0xc0, 0x4e, 0x44, 0x5e, 0x4a, 0x5e, 0x60, 0x47, 0x0a, 0x0c, 0x22, 0x46, 0x22, 0x48, 0x22, 0x4b, 0x44, 0x5c, 0xca, 0x15, 0xc2, 0x2a, 0x59, 0x45, 0x5b, 0x43, 0x5c, 0x44, 0x5e, 0x42, 0x5e, 0x3e, 0xcb, 0x07, 0x33, 0x46, 0x2a, 0x06, 0x08, 0xfe, 0x9b, 0x44, 0x56, 0xbf, 0xde, 0xc7, 0xd5, 0xc0, 0xc2, 0xc8, 0x3e, 0xbf, 0xa9, 0xc7, 0xbd, 0x3f, 0x56, 0xbf, 0x96, 0xc8, 0xa0, 0xbd, 0xa4, 0xc7, 0xba, 0x26, 0x4a, 0xb7, 0x26, 0xc4, 0x8c, 0xb5, 0x34, 0xc3, 0xa6, 0xb5, 0x9e, 0xc2, 0xfb, 0xb5, 0xc9, 0xc3, 0x0f, 0xb4, 0x89, 0xc2, 0x7b, 0x22, 0x46, 0x4b, 0x44, 0x5c, 0x0a, 0x0c, 0x44, 0x56, 0xca, 0x87, 0xbf, 0xe4, 0x5a, 0xbf, 0x83, 0x5b, 0xbf, 0x1d, 0xca, 0xe7, 0xbf, 0x92, 0x5e, 0x3e, 0x5e, 0x42, 0xca, 0xeb, 0xc1, 0x54, 0xca, 0x85, 0xc0, 0xf5, 0x5a, 0x44, 0xca, 0x86, 0xc1, 0xb9, 0x44, 0x5c, 0x0a, 0x0d, 0x44, 0x56, 0xca, 0x8f, 0xbf, 0xf3, 0x5a, 0xbf, 0x93, 0xca, 0x90, 0xbf, 0x22, 0xca, 0xf3, 0xbf, 0x9e, 0x5e, 0x3e, 0xcb, 0x09, 0xba, 0x91, 0x46, 0x2a, 0xc0, 0x1e, 0xb8, 0x9c, 0x41, 0x2f, 0x3b, 0x34, 0xbd, 0x91, 0xba, 0xab, 0x22, 0x47, 0x06, 0x07, 0xaa, 0x39, 0xc9, 0xa4, 0xbf, 0x80, 0xc0, 0xa8, 0xc7, 0xba, 0x41, 0x55, 0x42, 0x56, 0x55, 0xca, 0x50, 0xbf, 0x31, 0x44, 0x34, 0xc1, 0x7c, 0xba, 0xe7, 0xc1, 0x27, 0xbb, 0x0e, 0x06, 0x08, 0xaa, 0xab, 0xb5, 0x1f, 0xc2, 0xe2, 0xbf, 0xad, 0xba, 0xd2, 0xbf, 0x99, 0xba, 0x23, 0xb4, 0x73, 0xc2, 0xcb, 0x24, 0x49, 0xb4, 0x62, 0xc3, 0x3f, 0xb5, 0xa8, 0xc3, 0xc9, 0x40, 0x57, 0x41, 0x56, 0x32, 0x4e, 0x0a, 0x04, 0x40, 0x55, 0x58, 0x3f, 0x40, 0x33, 0xb5, 0x85, 0xc2, 0xe2, 0x0a, 0x03, 0x44, 0x2d, 0x46, 0x2f, 0x42, 0x31, 0x0a, 0x08, 0x47, 0x30, 0x47, 0x31, 0x49, 0x32, 0x4a, 0x32, 0x4a, 0x34, 0x49, 0x34, 0x47, 0x33, 0x45, 0x32, 0x04, 0x0f, 0xaf, 0xff, 0xeb, 0x3f, 0xc9, 0x8a, 0xc3, 0xb2, 0xc9, 0x8a, 0xc3, 0xb2, 0xc9, 0x8a, 0xc4, 0xc4, 0xc3, 0x50, 0xc4, 0xc4, 0xc7, 0xed, 0xc4, 0xda, 0xbc, 0x23, 0xc4, 0xa6, 0xb6, 0x1e, 0xc3, 0xee, 0xb3, 0x4d, 0xcb, 0xb1, 0xc1, 0x6c, 0xcc, 0x29, 0xba, 0x22, 0xcc, 0x29, 0xcb, 0xd2, 0xcc, 0x29, 0xd5, 0xf5, 0xc5, 0xb4, 0xd4, 0x64, 0xcc, 0x92, 0xd7, 0x12, 0xc0, 0xdb, 0xcd, 0xcd, 0xbd, 0x13, 0xd2, 0xd7, 0xbe, 0x92, 0xca, 0x67, 0xbc, 0x11, 0xc4, 0x35, 0xb9, 0x75, 0xc4, 0x35, 0xba, 0xac, 0xc4, 0x35, 0xb7, 0xe8, 0xcb, 0x40, 0xb7, 0xdc, 0xc8, 0x5e, 0xb7, 0xf3, 0xce, 0x66, 0xb7, 0xc6, 0xda, 0x9a, 0xb7, 0xc2, 0xdc, 0x27, 0xb3, 0x5d, 0xca, 0xaa, 0xb3, 0x8e, 0xd3, 0x61, 0xb3, 0x21, 0xbe, 0x1d, 0xb4, 0x2f, 0xbb, 0x7a, 0xb9, 0xa2, 0xbb, 0xc5, 0xb6, 0xf8, 0xbb, 0x18, 0xbd, 0x17, 0xc2, 0x95, 0xc0, 0x93, 0xbf, 0xc1, 0xbf, 0x7e, 0xc5, 0x46, 0xc1, 0x9a, 0xc9, 0x8a, 0xc3, 0xb2, 0xc9, 0x8a, 0xc2, 0x5d, 0xc9, 0x8a, 0xc3, 0xb2, 0x00, 0x0c, 0xeb, 0x0d, 0xbb, 0x77, 0xeb, 0x0d, 0xbb, 0x77, 0xea, 0xfe, 0xbb, 0x59, 0xea, 0xd8, 0xbb, 0x1d, 0xea, 0xeb, 0xbb, 0x3b, 0xea, 0xd8, 0xbb, 0x1d, 0xf9, 0xf9, 0xb7, 0x4e, 0xf9, 0xf9, 0xb7, 0x4e, 0xfc, 0x60, 0xb8, 0x7a, 0xff, 0x6c, 0xbb, 0xeb, 0xfe, 0x62, 0xb9, 0xfc, 0xff, 0x80, 0xbd, 0x9e, 0xff, 0x16, 0xc1, 0x17, 0xff, 0x80, 0xbf, 0x60, 0xf6, 0x6e, 0xcb, 0xd7, 0xd7, 0xca, 0xcc, 0x22, 0xdf, 0xeb, 0xcb, 0xc4, 0xd7, 0xca, 0xcc, 0x22, 0xdb, 0x52, 0xc2, 0x30, 0xdb, 0x52, 0xc2, 0x30, 0xdd, 0x79, 0xc2, 0x1d, 0xe2, 0x70, 0xc1, 0x71, 0xe0, 0x94, 0xc1, 0xce, 0xe4, 0x48, 0xc1, 0x13, 0xe7, 0x49, 0xc0, 0x05, 0xe5, 0xf0, 0xc0, 0x97, 0xe8, 0xa2, 0xbf, 0x73, 0xea, 0x5c, 0xbe, 0x1d, 0xe9, 0xb0, 0xbe, 0xce, 0xeb, 0x05, 0xbd, 0x6d, 0xeb, 0x36, 0xbb, 0xfe, 0xeb, 0x58, 0xbc, 0xb9, 0xeb, 0x2e, 0xbb, 0xd1, 0xeb, 0x0d, 0xbb, 0x77, 0xeb, 0x1f, 0xbb, 0xa4, 0xeb, 0x0d, 0xbb, 0x77, 0x04, 0x06, 0xff, 0x0b, 0xf6, 0xc4, 0xb5, 0xfc, 0xf6, 0xc4, 0xb5, 0xfc, 0xf5, 0xe3, 0xb5, 0x9f, 0xf3, 0xc7, 0xb4, 0xee, 0xf4, 0xac, 0xb5, 0x2a, 0xf3, 0xc7, 0xb4, 0xee, 0xe8, 0xb5, 0xb9, 0x66, 0xe8, 0xb5, 0xb9, 0x66, 0xe9, 0x07, 0xb9, 0x8f, 0xe9, 0xc3, 0xba, 0x16, 0xe9, 0x83, 0xb9, 0xed, 0xe9, 0xd5, 0xba, 0x25, 0xe9, 0xf7, 0xba, 0x3c, 0xe9, 0xe4, 0xba, 0x31, 0xe9, 0xf7, 0xba, 0x3c, 0xf6, 0xc4, 0xb5, 0xfc, 0x04, 0x07, 0xff, 0x2f, 0xed, 0xb0, 0xb3, 0xe8, 0xed, 0xb0, 0xb3, 0xe8, 0xeb, 0xdb, 0xb3, 0xa5, 0xe9, 0x70, 0xb3, 0x7f, 0xea, 0x55, 0xb3, 0x87, 0xe5, 0xe8, 0xb3, 0x69, 0xe0, 0x97, 0xb3, 0x5a, 0xe4, 0x84, 0xb3, 0x56, 0xe0, 0x97, 0xb3, 0x5a, 0xdf, 0x06, 0xb7, 0xc6, 0xdf, 0x06, 0xb7, 0xc6, 0xdf, 0xfe, 0xb7, 0xcd, 0xe2, 0x0e, 0xb7, 0xeb, 0xe0, 0x6e, 0xb7, 0xc2, 0xe3, 0x8d, 0xb8, 0x11, 0xe6, 0x24, 0xb8, 0x9c, 0xe4, 0xed, 0xb8, 0x49, 0xe6, 0x24, 0xb8, 0x9c, 0xed, 0xb0, 0xb3, 0xe8, 0x0d, 0x0a, 0x01, 0x01, 0x00, 0x00, 0x0a, 0x00, 0x01, 0x01, 0x10, 0x01, 0x17, 0x84, 0x00, 0x04, 0x0a, 0x02, 0x01, 0x02, 0x00, 0x0a, 0x03, 0x01, 0x03, 0x00, 0x0a, 0x04, 0x01, 0x04, 0x00, 0x0a, 0x03, 0x01, 0x06, 0x00, 0x0a, 0x06, 0x01, 0x05, 0x00, 0x0a, 0x05, 0x01, 0x07, 0x00, 0x0a, 0x00, 0x01, 0x0a, 0x02, 0x3a, 0x19, 0xca, 0x38, 0x20, 0xc9, 0xb8, 0x20, 0xc9, 0x3a, 0x19, 0xca, 0x49, 0x04, 0x3f, 0x48, 0xbc, 0x82, 0x0a, 0x00, 0x01, 0x0b, 0x02, 0x3a, 0x19, 0xca, 0x38, 0x20, 0xc9, 0xb8, 0x20, 0xc9, 0x3a, 0x19, 0xca, 0x49, 0x04, 0x3f, 0x48, 0xbc, 0x82, 0x0a, 0x00, 0x01, 0x0c, 0x02, 0x3a, 0x19, 0xca, 0x38, 0x20, 0xc9, 0xb8, 0x20, 0xc9, 0x3a, 0x19, 0xca, 0x49, 0x04, 0x3f, 0x48, 0xbc, 0x82, 0x0a, 0x00, 0x01, 0x0d, 0x02, 0x3a, 0x19, 0xca, 0x38, 0x20, 0xc9, 0xb8, 0x20, 0xc9, 0x3a, 0x19, 0xca, 0x49, 0x04, 0x3f, 0x48, 0xbc, 0x82, 0x0a, 0x08, 0x02, 0x08, 0x09, 0x00 }; const unsigned char kMobileIcon[] = { 0x6e, 0x63, 0x69, 0x66, 0x09, 0x05, 0x00, 0x04, 0x00, 0x66, 0x02, 0x00, 0x06, 0x02, 0x38, 0xf1, 0x86, 0x38, 0x62, 0x09, 0xba, 0xd4, 0x58, 0x3b, 0x7e, 0xb7, 0x4a, 0x28, 0x8f, 0x47, 0xaf, 0x7c, 0x00, 0xee, 0xef, 0xf3, 0xff, 0xce, 0xd2, 0xd9, 0x02, 0x00, 0x06, 0x03, 0x39, 0x3a, 0x47, 0xba, 0x81, 0x7b, 0x3f, 0x5c, 0xe7, 0x3e, 0x2a, 0x74, 0xc5, 0xab, 0xe0, 0x49, 0x3b, 0x67, 0x00, 0xff, 0xff, 0xff, 0x42, 0xd9, 0xde, 0xe5, 0xff, 0xb5, 0xb8, 0xbf, 0x02, 0x00, 0x06, 0x02, 0x38, 0xdc, 0xc0, 0x32, 0xb6, 0x96, 0xb9, 0xc3, 0xec, 0x3f, 0xf8, 0xe2, 0x4a, 0x00, 0x51, 0xb9, 0x4b, 0xdc, 0x00, 0xce, 0xd2, 0xd9, 0xff, 0x95, 0x9a, 0xa6, 0x02, 0x03, 0x06, 0x04, 0xba, 0x6c, 0x40, 0xb8, 0x55, 0xde, 0xbb, 0x7a, 0x07, 0x3d, 0x9b, 0x59, 0x49, 0xea, 0xb7, 0x49, 0x0b, 0x3e, 0x20, 0xda, 0xde, 0xe5, 0x52, 0xff, 0xff, 0xff, 0xa4, 0xff, 0xff, 0xff, 0xff, 0x95, 0x9a, 0xa6, 0x05, 0xff, 0x02, 0x00, 0x06, 0x03, 0x38, 0x2d, 0xf0, 0x3a, 0x3d, 0xb3, 0xbf, 0x98, 0xe5, 0x3d, 0x7f, 0x98, 0x4b, 0xc7, 0x2b, 0x44, 0x03, 0xbe, 0x00, 0x9e, 0xc9, 0xff, 0xb2, 0x60, 0x99, 0xde, 0xff, 0x7f, 0xb0, 0xef, 0x03, 0x73, 0x79, 0x80, 0x15, 0x0a, 0x0a, 0x29, 0x54, 0x39, 0x5e, 0x3c, 0x5c, 0x3d, 0x5a, 0x46, 0x53, 0x47, 0x53, 0x59, 0x44, 0x59, 0x43, 0x4a, 0x3c, 0x29, 0x52, 0x0a, 0x08, 0x4a, 0x39, 0x4b, 0x3a, 0x3a, 0x5c, 0x38, 0x5c, 0x27, 0x52, 0x29, 0x4e, 0x2a, 0x4d, 0x38, 0x31, 0x0a, 0x0a, 0x4a, 0x39, 0x3b, 0x57, 0x39, 0x59, 0x37, 0x59, 0x36, 0x5a, 0x29, 0x52, 0x27, 0x52, 0x29, 0x4e, 0x2a, 0x4d, 0x38, 0x31, 0x0a, 0x0a, 0x27, 0x52, 0x29, 0x52, 0x36, 0x5a, 0x37, 0x59, 0x39, 0x59, 0x3b, 0x57, 0x4a, 0x39, 0x4b, 0x3a, 0x3a, 0x5c, 0x38, 0x5c, 0x08, 0x02, 0x35, 0x45, 0x2e, 0x51, 0x08, 0x02, 0x3b, 0x48, 0x34, 0x54, 0x08, 0x02, 0x3b, 0x53, 0x2d, 0x4b, 0x08, 0x02, 0x2f, 0x47, 0x3d, 0x4e, 0x0a, 0x08, 0x31, 0x42, 0x30, 0x40, 0x3f, 0x22, 0x52, 0x29, 0x52, 0x2a, 0x43, 0x48, 0x42, 0x49, 0x3f, 0x49, 0x0a, 0x0a, 0x31, 0x42, 0x30, 0x40, 0x3f, 0x22, 0x52, 0x29, 0x52, 0x2a, 0x43, 0x48, 0x42, 0x49, 0x3f, 0x49, 0x50, 0x29, 0x3f, 0x23, 0x0a, 0x04, 0x31, 0x42, 0x3f, 0x23, 0x50, 0x29, 0x3f, 0x49, 0x0a, 0x04, 0x48, 0x28, 0x47, 0x29, 0x44, 0x28, 0x45, 0x27, 0x0a, 0x04, 0x43, 0x26, 0x44, 0x25, 0x4b, 0x28, 0x4a, 0x29, 0x0a, 0x04, 0x4c, 0x2c, 0x44, 0x3c, 0x38, 0x36, 0x3f, 0x27, 0x0a, 0x04, 0x39, 0x3e, 0x3c, 0x40, 0x3d, 0x3e, 0xbd, 0x62, 0xbe, 0x44, 0x08, 0x02, 0x37, 0x40, 0x3c, 0x43, 0x08, 0x02, 0x36, 0x42, 0x3a, 0x39, 0x08, 0x02, 0x35, 0x3d, 0x37, 0x3e, 0x08, 0x02, 0x3b, 0x45, 0x40, 0x3c, 0x08, 0x02, 0x40, 0x43, 0x3d, 0x41, 0x08, 0x03, 0x38, 0x36, 0x3f, 0x27, 0x4c, 0x2c, 0x0f, 0x0a, 0x01, 0x01, 0x00, 0x10, 0x01, 0x15, 0x84, 0x00, 0x04, 0x0a, 0x00, 0x01, 0x01, 0x18, 0x00, 0x15, 0x01, 0x17, 0x86, 0x02, 0x04, 0x0a, 0x00, 0x01, 0x01, 0x18, 0x15, 0xff, 0x01, 0x17, 0x84, 0x02, 0x04, 0x0a, 0x03, 0x01, 0x02, 0x00, 0x0a, 0x04, 0x01, 0x03, 0x00, 0x0a, 0x08, 0x04, 0x04, 0x05, 0x06, 0x07, 0x18, 0x15, 0xff, 0x01, 0x17, 0x81, 0x00, 0x04, 0x0a, 0x00, 0x01, 0x08, 0x18, 0x00, 0x15, 0x01, 0x17, 0x86, 0x02, 0x04, 0x0a, 0x00, 0x01, 0x08, 0x18, 0x15, 0xff, 0x01, 0x17, 0x84, 0x02, 0x04, 0x0a, 0x05, 0x01, 0x09, 0x00, 0x0a, 0x02, 0x01, 0x0a, 0x00, 0x0a, 0x00, 0x01, 0x0b, 0x08, 0x15, 0xff, 0x0a, 0x06, 0x01, 0x0c, 0x08, 0x15, 0xff, 0x0a, 0x07, 0x01, 0x0d, 0x00, 0x0a, 0x06, 0x01, 0x0e, 0x08, 0x15, 0xff, 0x0a, 0x08, 0x06, 0x13, 0x12, 0x11, 0x10, 0x0f, 0x14, 0x18, 0x15, 0xff, 0x01, 0x17, 0x81, 0x00, 0x04 }; const unsigned char kZipIcon[] = { 0x6e, 0x63, 0x69, 0x66, 0x0d, 0x03, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x6a, 0x02, 0x00, 0x06, 0x02, 0x38, 0x6a, 0xad, 0x38, 0xeb, 0x7a, 0xbb, 0x77, 0x73, 0x3a, 0xde, 0x88, 0x48, 0xce, 0xdc, 0x4a, 0x75, 0xed, 0x00, 0x1e, 0x31, 0x8d, 0xff, 0x27, 0x29, 0x91, 0x02, 0x00, 0x06, 0x02, 0x38, 0x6a, 0xad, 0x38, 0xeb, 0x7a, 0xbb, 0x77, 0x73, 0x3a, 0xde, 0x88, 0x48, 0xce, 0xdc, 0x4a, 0x75, 0xed, 0x00, 0x8a, 0x8a, 0x8a, 0xff, 0x0f, 0x0d, 0x7a, 0x05, 0x7d, 0x02, 0x00, 0x06, 0x02, 0x39, 0xc6, 0x30, 0x36, 0xa8, 0x1b, 0xb9, 0x51, 0xe6, 0x3c, 0x5b, 0xb3, 0x4b, 0x4d, 0xa8, 0x4a, 0x1a, 0xa1, 0x00, 0x16, 0x34, 0x77, 0xff, 0x23, 0x23, 0x84, 0x02, 0x00, 0x06, 0x02, 0xb7, 0x16, 0xa4, 0xba, 0x87, 0xfe, 0x3c, 0xb2, 0x07, 0xb9, 0x49, 0xed, 0x48, 0x87, 0xd8, 0x4a, 0x1e, 0x62, 0x01, 0x15, 0x37, 0xca, 0xfe, 0x5e, 0x5e, 0xf8, 0x02, 0x00, 0x06, 0x02, 0xb7, 0x16, 0xa4, 0xba, 0x87, 0xfe, 0x3c, 0xb2, 0x07, 0xb9, 0x49, 0xed, 0x48, 0x87, 0xd8, 0x4a, 0x1e, 0x62, 0x00, 0x24, 0x36, 0x95, 0xfe, 0x17, 0x14, 0x80, 0x02, 0x00, 0x16, 0x05, 0x36, 0xef, 0x60, 0x38, 0xe2, 0xe5, 0xbd, 0x22, 0x41, 0x3b, 0x2f, 0xce, 0x4a, 0x0e, 0x78, 0x4a, 0x5a, 0x6c, 0x00, 0xb8, 0x38, 0xe6, 0x77, 0xb8, 0xbd, 0xd2, 0xff, 0x93, 0x02, 0x00, 0x16, 0x05, 0x34, 0x0a, 0x8f, 0x38, 0xd2, 0xa2, 0xba, 0xb4, 0xc5, 0x35, 0xe9, 0xec, 0x49, 0xfd, 0x24, 0x4a, 0x64, 0x62, 0x00, 0xe1, 0x38, 0xff, 0x75, 0xd7, 0xba, 0xf3, 0xfd, 0xd0, 0x02, 0x00, 0x16, 0x02, 0x36, 0x67, 0xbe, 0x39, 0xdd, 0xbc, 0xbe, 0x50, 0x04, 0x3a, 0xe0, 0x9f, 0x4b, 0x85, 0x01, 0x49, 0x2a, 0x3f, 0x00, 0xff, 0xff, 0xd8, 0x03, 0x38, 0x6d, 0xbe, 0x02, 0x00, 0x16, 0x02, 0x3c, 0x40, 0xef, 0x3b, 0x82, 0xd1, 0xba, 0xeb, 0x42, 0x3b, 0xbf, 0x4d, 0x4a, 0xa7, 0xce, 0x49, 0x5d, 0xc1, 0xff, 0x01, 0x00, 0x4a, 0x12, 0x0a, 0x05, 0x44, 0x5a, 0x44, 0x40, 0x5e, 0x40, 0x5f, 0x45, 0x49, 0x5a, 0x0a, 0x06, 0x45, 0x58, 0x5c, 0x42, 0x5c, 0x40, 0x3d, 0x34, 0xb5, 0x6b, 0xc2, 0x2e, 0xb5, 0x63, 0xc3, 0xbb, 0x0a, 0x04, 0x44, 0x58, 0x26, 0x4a, 0x26, 0x47, 0x44, 0x54, 0x0a, 0x04, 0x44, 0x59, 0x5c, 0x42, 0x5c, 0x3e, 0x44, 0x54, 0x0a, 0x05, 0x44, 0x56, 0x5c, 0x40, 0x3d, 0x34, 0xb5, 0x6b, 0xc2, 0x2e, 0xb5, 0x43, 0xc3, 0x3b, 0x0a, 0x04, 0x2a, 0x4c, 0x3f, 0x56, 0x3f, 0x53, 0x2a, 0x4a, 0x0a, 0x04, 0x2a, 0x4b, 0x39, 0x52, 0x39, 0x50, 0x2a, 0x4a, 0x0a, 0x04, 0x31, 0x42, 0x45, 0x4c, 0x3f, 0x53, 0x2a, 0x4a, 0x0a, 0x04, 0x3f, 0x49, 0x38, 0x50, 0x2a, 0x4a, 0x31, 0x43, 0x0a, 0x04, 0x3f, 0x36, 0x57, 0x3e, 0x49, 0x4b, 0x32, 0x40, 0x08, 0x02, 0x3c, 0x3b, 0x4d, 0x43, 0x00, 0x02, 0x3b, 0x3b, 0x31, 0x36, 0x43, 0x3f, 0x48, 0x3d, 0x3f, 0x36, 0xc4, 0x82, 0xbf, 0xc7, 0x00, 0x02, 0x3e, 0x3c, 0xbc, 0x58, 0xbd, 0x93, 0x47, 0x3e, 0x45, 0x44, 0x3d, 0x43, 0x4d, 0x45, 0x02, 0x04, 0x39, 0x3b, 0x39, 0x3d, 0x39, 0x39, 0x3c, 0x38, 0x3b, 0x38, 0x3e, 0x38, 0x3e, 0x3a, 0x3f, 0x3a, 0x41, 0x37, 0x3c, 0x3d, 0x3c, 0x42, 0x3c, 0x40, 0x02, 0x04, 0x46, 0x3c, 0x46, 0x3e, 0x46, 0x3a, 0x48, 0x3a, 0x46, 0x3a, 0x4a, 0x3a, 0x4a, 0x3c, 0x4a, 0x3a, 0x4a, 0x3e, 0x48, 0x3e, 0x4a, 0x3e, 0x46, 0x3e, 0x0a, 0x04, 0x45, 0x42, 0x42, 0x45, 0x45, 0x47, 0x48, 0x44, 0x0a, 0x03, 0x4e, 0x43, 0x4d, 0x3f, 0x48, 0x44, 0x0a, 0x04, 0x32, 0x4b, 0x36, 0x48, 0x32, 0x46, 0x2e, 0x4a, 0x0d, 0x0a, 0x01, 0x01, 0x00, 0x20, 0x1e, 0x20, 0x0a, 0x00, 0x01, 0x01, 0x30, 0x1e, 0x20, 0x01, 0x17, 0x84, 0x00, 0x04, 0x0a, 0x02, 0x01, 0x02, 0x20, 0x1e, 0x20, 0x0a, 0x05, 0x01, 0x03, 0x20, 0x1e, 0x20, 0x0a, 0x06, 0x01, 0x04, 0x20, 0x1e, 0x20, 0x0a, 0x03, 0x01, 0x05, 0x20, 0x1e, 0x20, 0x0a, 0x07, 0x01, 0x07, 0x20, 0x1e, 0x20, 0x0a, 0x08, 0x01, 0x06, 0x20, 0x1e, 0x20, 0x0a, 0x09, 0x01, 0x08, 0x20, 0x1e, 0x20, 0x0a, 0x0a, 0x01, 0x09, 0x20, 0x1e, 0x20, 0x0a, 0x0c, 0x03, 0x0a, 0x0b, 0x0c, 0x1a, 0x40, 0x1d, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x9d, 0xc9, 0xc2, 0x91, 0x98, 0x43, 0x6d, 0xc2, 0x20, 0xff, 0x01, 0x17, 0x81, 0x00, 0x04, 0x0a, 0x0c, 0x04, 0x0d, 0x0e, 0x0f, 0x10, 0x0a, 0x3f, 0xff, 0xbd, 0x34, 0xaf, 0xbc, 0xb4, 0xe0, 0x2c, 0x3f, 0xbc, 0x62, 0x3e, 0x74, 0x62, 0x41, 0xfe, 0xe7, 0x20, 0xff, 0x0a, 0x02, 0x01, 0x11, 0x20, 0x1e, 0x20 }; static const uint8 kFloppyIcon[] = { 0x6e, 0x63, 0x69, 0x66, 0x0d, 0x03, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x6a, 0x02, 0x00, 0x16, 0x02, 0x38, 0x6a, 0xad, 0x38, 0xeb, 0x7a, 0xbb, 0x77, 0x73, 0x3a, 0xde, 0x88, 0x48, 0xce, 0xdc, 0x4a, 0x75, 0xed, 0x00, 0x7a, 0xff, 0x4b, 0x02, 0x00, 0x16, 0x02, 0x38, 0x6a, 0xad, 0x38, 0xeb, 0x7a, 0xbb, 0x77, 0x73, 0x3a, 0xde, 0x88, 0x48, 0xce, 0xdc, 0x4a, 0x75, 0xed, 0x00, 0x8a, 0xff, 0x2c, 0x05, 0x7d, 0x02, 0x00, 0x16, 0x02, 0x39, 0xc6, 0x30, 0x36, 0xa8, 0x1b, 0xb9, 0x51, 0xe6, 0x3c, 0x5b, 0xb3, 0x4b, 0x4d, 0xa8, 0x4a, 0x1a, 0xa1, 0x00, 0x3a, 0xff, 0x5d, 0x02, 0x00, 0x16, 0x02, 0xb7, 0x16, 0xa4, 0xba, 0x87, 0xfe, 0x3c, 0xb2, 0x07, 0xb9, 0x49, 0xed, 0x48, 0x87, 0xd8, 0x4a, 0x1e, 0x62, 0x01, 0x75, 0xfe, 0xc4, 0x02, 0x00, 0x16, 0x02, 0xb7, 0x16, 0xa4, 0xba, 0x87, 0xfe, 0x3c, 0xb2, 0x07, 0xb9, 0x49, 0xed, 0x48, 0x87, 0xd8, 0x4a, 0x1e, 0x62, 0x00, 0x5c, 0xfe, 0x9b, 0x02, 0x00, 0x16, 0x05, 0x36, 0xef, 0x60, 0x38, 0xe2, 0xe5, 0xbd, 0x22, 0x41, 0x3b, 0x2f, 0xce, 0x4a, 0x0e, 0x78, 0x4a, 0x5a, 0x6c, 0x00, 0xb8, 0x38, 0xe6, 0x77, 0xb8, 0xbd, 0xd2, 0xff, 0x93, 0x02, 0x00, 0x16, 0x05, 0x34, 0x0a, 0x8f, 0x38, 0xd2, 0xa2, 0xba, 0xb4, 0xc5, 0x35, 0xe9, 0xec, 0x49, 0xfd, 0x24, 0x4a, 0x64, 0x62, 0x00, 0xe1, 0x38, 0xff, 0x75, 0xd7, 0xba, 0xf3, 0xfd, 0xd0, 0x02, 0x00, 0x16, 0x02, 0x36, 0x67, 0xbe, 0x39, 0xdd, 0xbc, 0xbe, 0x50, 0x04, 0x3a, 0xe0, 0x9f, 0x4b, 0x85, 0x01, 0x49, 0x2a, 0x3f, 0x00, 0xff, 0xff, 0xd8, 0x03, 0x38, 0x6d, 0xbe, 0x02, 0x00, 0x16, 0x02, 0x3c, 0x40, 0xef, 0x3b, 0x82, 0xd1, 0xba, 0xeb, 0x42, 0x3b, 0xbf, 0x4d, 0x4a, 0xa7, 0xce, 0x49, 0x5d, 0xc1, 0xff, 0x01, 0x00, 0x4a, 0x12, 0x0a, 0x05, 0x44, 0x5a, 0x44, 0x40, 0x5e, 0x40, 0x5f, 0x45, 0x49, 0x5a, 0x0a, 0x06, 0x45, 0x58, 0x5c, 0x42, 0x5c, 0x40, 0x3d, 0x34, 0xb5, 0x6b, 0xc2, 0x2e, 0xb5, 0x63, 0xc3, 0xbb, 0x0a, 0x04, 0x44, 0x58, 0x26, 0x4a, 0x26, 0x47, 0x44, 0x54, 0x0a, 0x04, 0x44, 0x59, 0x5c, 0x42, 0x5c, 0x3e, 0x44, 0x54, 0x0a, 0x05, 0x44, 0x56, 0x5c, 0x40, 0x3d, 0x34, 0xb5, 0x6b, 0xc2, 0x2e, 0xb5, 0x43, 0xc3, 0x3b, 0x0a, 0x04, 0x2a, 0x4c, 0x3f, 0x56, 0x3f, 0x53, 0x2a, 0x4a, 0x0a, 0x04, 0x2a, 0x4b, 0x39, 0x52, 0x39, 0x50, 0x2a, 0x4a, 0x0a, 0x04, 0x31, 0x42, 0x45, 0x4c, 0x3f, 0x53, 0x2a, 0x4a, 0x0a, 0x04, 0x3f, 0x49, 0x38, 0x50, 0x2a, 0x4a, 0x31, 0x43, 0x0a, 0x04, 0x3f, 0x36, 0x57, 0x3e, 0x49, 0x4b, 0x32, 0x40, 0x08, 0x02, 0x3c, 0x3b, 0x4d, 0x43, 0x00, 0x02, 0x3b, 0x3b, 0x31, 0x36, 0x43, 0x3f, 0x48, 0x3d, 0x3f, 0x36, 0xc4, 0x82, 0xbf, 0xc7, 0x00, 0x02, 0x3e, 0x3c, 0xbc, 0x58, 0xbd, 0x93, 0x47, 0x3e, 0x45, 0x44, 0x3d, 0x43, 0x4d, 0x45, 0x02, 0x04, 0x39, 0x3b, 0x39, 0x3d, 0x39, 0x39, 0x3c, 0x38, 0x3b, 0x38, 0x3e, 0x38, 0x3e, 0x3a, 0x3f, 0x3a, 0x41, 0x37, 0x3c, 0x3d, 0x3c, 0x42, 0x3c, 0x40, 0x02, 0x04, 0x46, 0x3c, 0x46, 0x3e, 0x46, 0x3a, 0x48, 0x3a, 0x46, 0x3a, 0x4a, 0x3a, 0x4a, 0x3c, 0x4a, 0x3a, 0x4a, 0x3e, 0x48, 0x3e, 0x4a, 0x3e, 0x46, 0x3e, 0x0a, 0x04, 0x45, 0x42, 0x42, 0x45, 0x45, 0x47, 0x48, 0x44, 0x0a, 0x03, 0x4e, 0x43, 0x4d, 0x3f, 0x48, 0x44, 0x0a, 0x04, 0x32, 0x4b, 0x36, 0x48, 0x32, 0x46, 0x2e, 0x4a, 0x0d, 0x0a, 0x01, 0x01, 0x00, 0x20, 0x1e, 0x20, 0x0a, 0x00, 0x01, 0x01, 0x30, 0x1e, 0x20, 0x01, 0x17, 0x84, 0x00, 0x04, 0x0a, 0x02, 0x01, 0x02, 0x20, 0x1e, 0x20, 0x0a, 0x05, 0x01, 0x03, 0x20, 0x1e, 0x20, 0x0a, 0x06, 0x01, 0x04, 0x20, 0x1e, 0x20, 0x0a, 0x03, 0x01, 0x05, 0x20, 0x1e, 0x20, 0x0a, 0x07, 0x01, 0x07, 0x20, 0x1e, 0x20, 0x0a, 0x08, 0x01, 0x06, 0x20, 0x1e, 0x20, 0x0a, 0x09, 0x01, 0x08, 0x20, 0x1e, 0x20, 0x0a, 0x0a, 0x01, 0x09, 0x20, 0x1e, 0x20, 0x0a, 0x0c, 0x03, 0x0a, 0x0b, 0x0c, 0x1a, 0x40, 0x1d, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x9d, 0xc9, 0xc2, 0x91, 0x98, 0x43, 0x6d, 0xc2, 0x20, 0xff, 0x01, 0x17, 0x81, 0x00, 0x04, 0x0a, 0x0c, 0x04, 0x0d, 0x0e, 0x0f, 0x10, 0x0a, 0x3f, 0xff, 0xbd, 0x34, 0xaf, 0xbc, 0xb4, 0xe0, 0x2c, 0x3f, 0xbc, 0x62, 0x3e, 0x74, 0x62, 0x41, 0xfe, 0xe7, 0x20, 0xff, 0x0a, 0x02, 0x01, 0x11, 0x20, 0x1e, 0x20 }; device_icon kKeyIconData = { (int32)sizeof(kDeviceIcon), (void *)kDeviceIcon }; device_icon kCDIconData = { (int32)sizeof(kCDIcon), (void *)kCDIcon }; device_icon kMSIconData = { (int32)sizeof(kMemoryStickIcon), (void *)kMemoryStickIcon }; device_icon kSDIconData = { (int32)sizeof(kSDIcon), (void *)kSDIcon }; device_icon kMobileIconData = { (int32)sizeof(kMobileIcon), (void *)kMobileIcon }; device_icon kFloppyIconData = { (int32)sizeof(kFloppyIcon), (void *)kFloppyIcon }; struct { const char *vendor; const char *product; device_icon *icon; const char *name; } kIconMatches[] = { // matches for Hama USB 2.0 Card Reader 35 in 1 // vendor: "Transcend Information, Inc." // product: "63-in-1 Multi-Card Reader/Writer" ver. 0100 // which report things like "Generic " "USB CF Reader " // { NULL, " CF Reader", &kCFIconData, "devices/drive-removable-media-flash" }, { NULL, " SD Reader", &kSDIconData, "devices/drive-removable-media-flash" }, { NULL, " MS Reader", &kMSIconData, "devices/drive-removable-media-flash" }, // { NULL, " SM Reader", &kSMIconData, "devices/drive-removable-media-flash" }, // match for my Kazam mobile phone // stupid thing says "MEDIATEK" " FLASH DISK " even for internal memory { "MEDIATEK", NULL, &kMobileIconData, "devices/drive-removable-media-flash" }, { NULL, NULL, NULL, NULL } }; // //#pragma mark - Forward Declarations // static void usb_disk_callback(void *cookie, status_t status, void *data, size_t actualLength); status_t usb_disk_mass_storage_reset(disk_device *device); uint8 usb_disk_get_max_lun(disk_device *device); void usb_disk_reset_recovery(disk_device *device); status_t usb_disk_transfer_data(disk_device *device, bool directionIn, void *data, size_t dataLength); status_t usb_disk_receive_csw(disk_device *device, usb_massbulk_command_status_wrapper *status); status_t usb_disk_operation(device_lun *lun, uint8* operation, size_t opLength, void *data, size_t *dataLength, bool directionIn, err_act *action = NULL); status_t usb_disk_send_diagnostic(device_lun *lun); status_t usb_disk_request_sense(device_lun *lun, err_act *action); status_t usb_disk_mode_sense(device_lun *lun); status_t usb_disk_test_unit_ready(device_lun *lun, err_act *action = NULL); status_t usb_disk_inquiry(device_lun *lun); status_t usb_disk_reset_capacity(device_lun *lun); status_t usb_disk_update_capacity(device_lun *lun); status_t usb_disk_synchronize(device_lun *lun, bool force); // //#pragma mark - Device Allocation Helper Functions // void usb_disk_free_device_and_luns(disk_device *device) { mutex_lock(&device->lock); mutex_destroy(&device->lock); delete_sem(device->notify); delete_sem(device->interruptLock); for (uint8 i = 0; i < device->lun_count; i++) free(device->luns[i]); free(device->luns); free(device); } // //#pragma mark - Bulk-only Mass Storage Functions // status_t usb_disk_mass_storage_reset(disk_device *device) { return gUSBModule->send_request(device->device, USB_REQTYPE_INTERFACE_OUT | USB_REQTYPE_CLASS, USB_MASSBULK_REQUEST_MASS_STORAGE_RESET, 0x0000, device->interface, 0, NULL, NULL); } uint8 usb_disk_get_max_lun(disk_device *device) { uint8 result = 0; size_t actualLength = 0; // devices that do not support multiple LUNs may stall this request if (gUSBModule->send_request(device->device, USB_REQTYPE_INTERFACE_IN | USB_REQTYPE_CLASS, USB_MASSBULK_REQUEST_GET_MAX_LUN, 0x0000, device->interface, 1, &result, &actualLength) != B_OK || actualLength != 1) { return 0; } if (result > MAX_LOGICAL_UNIT_NUMBER) { // invalid max lun return 0; } return result; } void usb_disk_reset_recovery(disk_device *device) { usb_disk_mass_storage_reset(device); gUSBModule->clear_feature(device->bulk_in, USB_FEATURE_ENDPOINT_HALT); gUSBModule->clear_feature(device->bulk_out, USB_FEATURE_ENDPOINT_HALT); if (device->is_ufi) gUSBModule->clear_feature(device->interrupt, USB_FEATURE_ENDPOINT_HALT); } status_t usb_disk_transfer_data(disk_device *device, bool directionIn, void *data, size_t dataLength) { status_t result = gUSBModule->queue_bulk(directionIn ? device->bulk_in : device->bulk_out, data, dataLength, usb_disk_callback, device); if (result != B_OK) { TRACE_ALWAYS("failed to queue data transfer: %s\n", strerror(result)); return result; } do { result = acquire_sem_etc(device->notify, 1, B_RELATIVE_TIMEOUT, 10 * 1000 * 1000); if (result == B_TIMED_OUT) { // Cancel the transfer and collect the sem that should now be // released through the callback on cancel. Handling of device // reset is done in usb_disk_operation() when it detects that // the transfer failed. gUSBModule->cancel_queued_transfers(directionIn ? device->bulk_in : device->bulk_out); acquire_sem_etc(device->notify, 1, B_RELATIVE_TIMEOUT, 0); } } while (result == B_INTERRUPTED); if (result != B_OK) { TRACE_ALWAYS("acquire_sem failed while waiting for data transfer: %s\n", strerror(result)); return result; } return B_OK; } void usb_disk_interrupt(void* cookie, int32 status, void* data, size_t length) { disk_device* device = (disk_device*)cookie; // We release the lock even if the interrupt is invalid. This way there // is at least a chance for the driver to terminate properly. release_sem(device->interruptLock); if (length != 2) { TRACE_ALWAYS("interrupt of length %" B_PRIuSIZE "! (expected 2)\n", length); // In this case we do not reschedule the interrupt. This means the // driver will be locked. The interrupt should perhaps be scheduled // when starting a transfer instead. But getting there means something // is really broken, so... return; } // Reschedule the interrupt for next time gUSBModule->queue_interrupt(device->interrupt, device->interruptBuffer, 2, usb_disk_interrupt, cookie); } status_t usb_disk_receive_csw_interrupt(disk_device *device, interrupt_status_wrapper *status) { TRACE("Waiting for result...\n"); gUSBModule->queue_interrupt(device->interrupt, device->interruptBuffer, 2, usb_disk_interrupt, device); acquire_sem(device->interruptLock); status->status = device->interruptBuffer[0]; status->misc = device->interruptBuffer[1]; return B_OK; } status_t usb_disk_receive_csw_bulk(disk_device *device, usb_massbulk_command_status_wrapper *status) { status_t result = usb_disk_transfer_data(device, true, status, sizeof(usb_massbulk_command_status_wrapper)); if (result != B_OK) return result; if (device->status != B_OK || device->actual_length != sizeof(usb_massbulk_command_status_wrapper)) { // receiving the command status wrapper failed return B_ERROR; } return B_OK; } status_t usb_disk_operation_interrupt(device_lun *lun, uint8* operation, void *data, size_t *dataLength, bool directionIn, err_act *_action) { TRACE("operation: lun: %u; op: 0x%x; data: %p; dlen: %p (%lu); in: %c\n", lun->logical_unit_number, operation[0], data, dataLength, dataLength ? *dataLength : 0, directionIn ? 'y' : 'n'); disk_device* device = lun->device; // Step 1 : send the SCSI operation as a class specific request size_t actualLength = 12; status_t result = gUSBModule->send_request(device->device, USB_REQTYPE_CLASS | USB_REQTYPE_INTERFACE_OUT, 0 /*request*/, 0/*value*/, device->interface/*index*/, 12, operation, &actualLength); if (result != B_OK || actualLength != 12) { TRACE("Command stage: wrote %ld bytes (error: %s)\n", actualLength, strerror(result)); // There was an error, we have to do a request sense to reset the device if (operation[0] != SCSI_REQUEST_SENSE_6) { usb_disk_request_sense(lun, _action); } return result; } // Step 2 : data phase : send or receive data size_t transferedData = 0; if (data != NULL && dataLength != NULL && *dataLength > 0) { // we have data to transfer in a data stage result = usb_disk_transfer_data(device, directionIn, data, *dataLength); if (result != B_OK) { TRACE("Error %s in data phase\n", strerror(result)); return result; } transferedData = device->actual_length; if (device->status != B_OK || transferedData != *dataLength) { // sending or receiving of the data failed if (device->status == B_DEV_STALLED) { TRACE("stall while transfering data\n"); gUSBModule->clear_feature(directionIn ? device->bulk_in : device->bulk_out, USB_FEATURE_ENDPOINT_HALT); } else { TRACE_ALWAYS("sending or receiving of the data failed\n"); usb_disk_reset_recovery(device); return B_ERROR; } } } // step 3 : wait for the device to send the interrupt ACK if (operation[0] != SCSI_REQUEST_SENSE_6) { interrupt_status_wrapper status; result = usb_disk_receive_csw_interrupt(device, &status); if (result != B_OK) { // in case of a stall or error clear the stall and try again TRACE("Error receiving interrupt: %s. Retrying...\n", strerror(result)); gUSBModule->clear_feature(device->bulk_in, USB_FEATURE_ENDPOINT_HALT); result = usb_disk_receive_csw_interrupt(device, &status); } if (result != B_OK) { TRACE_ALWAYS("receiving the command status interrupt failed\n"); usb_disk_reset_recovery(device); return result; } // wait for the device to finish the operation. result = usb_disk_request_sense(lun, _action); } return result; } status_t usb_disk_operation_bulk(device_lun *lun, uint8* operation, size_t operationLength, void *data, size_t *dataLength, bool directionIn, err_act *_action) { TRACE("operation: lun: %u; op: %u; data: %p; dlen: %p (%lu); in: %c\n", lun->logical_unit_number, operation[0], data, dataLength, dataLength ? *dataLength : 0, directionIn ? 'y' : 'n'); disk_device *device = lun->device; usb_massbulk_command_block_wrapper command; command.signature = USB_MASSBULK_CBW_SIGNATURE; command.tag = device->current_tag++; command.data_transfer_length = (dataLength != NULL ? *dataLength : 0); command.flags = (directionIn ? USB_MASSBULK_CBW_DATA_INPUT : USB_MASSBULK_CBW_DATA_OUTPUT); command.lun = lun->logical_unit_number; command.command_block_length = device->is_atapi ? ATAPI_COMMAND_LENGTH : operationLength; memset(command.command_block, 0, sizeof(command.command_block)); memcpy(command.command_block, operation, operationLength); status_t result = usb_disk_transfer_data(device, false, &command, sizeof(usb_massbulk_command_block_wrapper)); if (result != B_OK) return result; if (device->status != B_OK || device->actual_length != sizeof(usb_massbulk_command_block_wrapper)) { // sending the command block wrapper failed TRACE_ALWAYS("sending the command block wrapper failed: %s\n", strerror(device->status)); usb_disk_reset_recovery(device); return B_ERROR; } size_t transferedData = 0; if (data != NULL && dataLength != NULL && *dataLength > 0) { // we have data to transfer in a data stage result = usb_disk_transfer_data(device, directionIn, data, *dataLength); if (result != B_OK) return result; transferedData = device->actual_length; if (device->status != B_OK || transferedData != *dataLength) { // sending or receiving of the data failed if (device->status == B_DEV_STALLED) { TRACE("stall while transfering data\n"); gUSBModule->clear_feature(directionIn ? device->bulk_in : device->bulk_out, USB_FEATURE_ENDPOINT_HALT); } else { TRACE_ALWAYS("sending or receiving of the data failed: %s\n", strerror(device->status)); usb_disk_reset_recovery(device); return B_ERROR; } } } usb_massbulk_command_status_wrapper status; result = usb_disk_receive_csw_bulk(device, &status); if (result != B_OK) { // in case of a stall or error clear the stall and try again gUSBModule->clear_feature(device->bulk_in, USB_FEATURE_ENDPOINT_HALT); result = usb_disk_receive_csw_bulk(device, &status); } if (result != B_OK) { TRACE_ALWAYS("receiving the command status wrapper failed: %s\n", strerror(result)); usb_disk_reset_recovery(device); return result; } if (status.signature != USB_MASSBULK_CSW_SIGNATURE || status.tag != command.tag) { // the command status wrapper is not valid TRACE_ALWAYS("command status wrapper is not valid: %#" B_PRIx32 "\n", status.signature); usb_disk_reset_recovery(device); return B_ERROR; } switch (status.status) { case USB_MASSBULK_CSW_STATUS_COMMAND_PASSED: case USB_MASSBULK_CSW_STATUS_COMMAND_FAILED: { // The residue from "status.data_residue" is not maintained // correctly by some devices, so calculate it instead. uint32 residue = command.data_transfer_length - transferedData; if (dataLength != NULL) { *dataLength -= residue; if (transferedData < *dataLength) { TRACE_ALWAYS("less data transfered than indicated: %" B_PRIuSIZE " vs. %" B_PRIuSIZE "\n", transferedData, *dataLength); *dataLength = transferedData; } } if (status.status == USB_MASSBULK_CSW_STATUS_COMMAND_PASSED) { // the operation is complete and has succeeded return B_OK; } else { if (operation[0] == SCSI_REQUEST_SENSE_6) return B_ERROR; // the operation is complete but has failed at the SCSI level if (operation[0] != SCSI_TEST_UNIT_READY_6) { TRACE_ALWAYS("operation %#" B_PRIx8 " failed at the SCSI level\n", operation[0]); } result = usb_disk_request_sense(lun, _action); return result == B_OK ? B_ERROR : result; } } case USB_MASSBULK_CSW_STATUS_PHASE_ERROR: { // a protocol or device error occured TRACE_ALWAYS("phase error in operation %#" B_PRIx8 "\n", operation[0]); usb_disk_reset_recovery(device); return B_ERROR; } default: { // command status wrapper is not meaningful TRACE_ALWAYS("command status wrapper has invalid status\n"); usb_disk_reset_recovery(device); return B_ERROR; } } } status_t usb_disk_operation(device_lun *lun, uint8* operation, size_t opLength, void *data, size_t *dataLength, bool directionIn, err_act *_action) { if (lun->device->is_ufi) { return usb_disk_operation_interrupt(lun, operation, data, dataLength, directionIn, _action); } else { return usb_disk_operation_bulk(lun, operation, opLength, data, dataLength, directionIn, _action); } } // //#pragma mark - Helper/Convenience Functions // status_t usb_disk_send_diagnostic(device_lun *lun) { uint8 commandBlock[12]; memset(commandBlock, 0, sizeof(commandBlock)); commandBlock[0] = SCSI_SEND_DIAGNOSTIC; commandBlock[1] = (lun->logical_unit_number << 5) | 4; status_t result = usb_disk_operation(lun, commandBlock, 6, NULL, NULL, false); int retry = 100; err_act action = err_act_ok; while(result == B_DEV_NO_MEDIA && retry > 0) { snooze(10000); result = usb_disk_request_sense(lun, &action); retry--; } if (result != B_OK) TRACE("Send Diagnostic failed: %s\n", strerror(result)); return result; } status_t usb_disk_request_sense(device_lun *lun, err_act *_action) { size_t dataLength = sizeof(scsi_request_sense_6_parameter); uint8 commandBlock[12]; memset(commandBlock, 0, sizeof(commandBlock)); commandBlock[0] = SCSI_REQUEST_SENSE_6; commandBlock[1] = lun->logical_unit_number << 5; commandBlock[2] = 0; // page code commandBlock[4] = dataLength; scsi_request_sense_6_parameter parameter; status_t result = B_ERROR; for (uint32 tries = 0; tries < 3; tries++) { result = usb_disk_operation(lun, commandBlock, 6, ¶meter, &dataLength, true); if (result != B_TIMED_OUT) break; snooze(100000); } if (result != B_OK) { TRACE_ALWAYS("getting request sense data failed: %s\n", strerror(result)); return result; } const char *label = NULL; err_act action = err_act_fail; status_t status = B_ERROR; scsi_get_sense_asc_info((parameter.additional_sense_code << 8) | parameter.additional_sense_code_qualifier, &label, &action, &status); if (parameter.sense_key > SCSI_SENSE_KEY_NOT_READY && parameter.sense_key != SCSI_SENSE_KEY_UNIT_ATTENTION) { TRACE_ALWAYS("request_sense: key: 0x%02x; asc: 0x%02x; ascq: " "0x%02x; %s\n", parameter.sense_key, parameter.additional_sense_code, parameter.additional_sense_code_qualifier, label ? label : "(unknown)"); } if ((parameter.additional_sense_code == 0 && parameter.additional_sense_code_qualifier == 0) || label == NULL) { scsi_get_sense_key_info(parameter.sense_key, &label, &action, &status); } if (status == B_DEV_MEDIA_CHANGED) { lun->media_changed = true; lun->media_present = true; } else if (parameter.sense_key == SCSI_SENSE_KEY_UNIT_ATTENTION && status != B_DEV_NO_MEDIA) { lun->media_present = true; } else if (status == B_DEV_NOT_READY) { lun->media_present = false; usb_disk_reset_capacity(lun); } if (_action != NULL) *_action = action; return status; } status_t usb_disk_mode_sense(device_lun *lun) { size_t dataLength = sizeof(scsi_mode_sense_6_parameter); uint8 commandBlock[12]; memset(commandBlock, 0, sizeof(commandBlock)); commandBlock[0] = SCSI_MODE_SENSE_6; commandBlock[1] = SCSI_MODE_PAGE_DEVICE_CONFIGURATION; commandBlock[2] = 0; // Current values commandBlock[3] = dataLength >> 8; commandBlock[4] = dataLength; scsi_mode_sense_6_parameter parameter; status_t result = usb_disk_operation(lun, commandBlock, 6, ¶meter, &dataLength, true); if (result != B_OK) { TRACE_ALWAYS("getting mode sense data failed: %s\n", strerror(result)); return result; } lun->write_protected = (parameter.device_specific & SCSI_DEVICE_SPECIFIC_WRITE_PROTECT) != 0; TRACE_ALWAYS("write protected: %s\n", lun->write_protected ? "yes" : "no"); return B_OK; } status_t usb_disk_test_unit_ready(device_lun *lun, err_act *_action) { // if unsupported we assume the unit is fixed and therefore always ok if (lun->device->is_ufi || !lun->device->tur_supported) return B_OK; status_t result = B_OK; uint8 commandBlock[12]; memset(commandBlock, 0, sizeof(commandBlock)); if (lun->device->is_atapi) { commandBlock[0] = SCSI_START_STOP_UNIT_6; commandBlock[1] = lun->logical_unit_number << 5; commandBlock[2] = 0; commandBlock[3] = 0; commandBlock[4] = 1; result = usb_disk_operation(lun, commandBlock, 6, NULL, NULL, false, _action); } else { commandBlock[0] = SCSI_TEST_UNIT_READY_6; commandBlock[1] = lun->logical_unit_number << 5; commandBlock[2] = 0; commandBlock[3] = 0; commandBlock[4] = 0; result = usb_disk_operation(lun, commandBlock, 6, NULL, NULL, true, _action); } if (result == B_DEV_INVALID_IOCTL) { lun->device->tur_supported = false; return B_OK; } return result; } status_t usb_disk_inquiry(device_lun *lun) { size_t dataLength = sizeof(scsi_inquiry_6_parameter); uint8 commandBlock[12]; memset(commandBlock, 0, sizeof(commandBlock)); commandBlock[0] = SCSI_INQUIRY_6; commandBlock[1] = lun->logical_unit_number << 5; commandBlock[2] = 0; // page code commandBlock[4] = dataLength; scsi_inquiry_6_parameter parameter; status_t result = B_ERROR; err_act action = err_act_ok; for (uint32 tries = 0; tries < 3; tries++) { result = usb_disk_operation(lun, commandBlock, 6, ¶meter, &dataLength, true, &action); if (result == B_OK || (action != err_act_retry && action != err_act_many_retries)) { break; } } if (result != B_OK) { TRACE_ALWAYS("getting inquiry data failed: %s\n", strerror(result)); lun->device_type = B_DISK; lun->removable = true; return result; } TRACE("peripherial_device_type 0x%02x\n", parameter.peripherial_device_type); TRACE("peripherial_qualifier 0x%02x\n", parameter.peripherial_qualifier); TRACE("removable_medium %s\n", parameter.removable_medium ? "yes" : "no"); TRACE("version 0x%02x\n", parameter.version); TRACE("response_data_format 0x%02x\n", parameter.response_data_format); TRACE_ALWAYS("vendor_identification \"%.8s\"\n", parameter.vendor_identification); TRACE_ALWAYS("product_identification \"%.16s\"\n", parameter.product_identification); TRACE_ALWAYS("product_revision_level \"%.4s\"\n", parameter.product_revision_level); memcpy(lun->vendor_name, parameter.vendor_identification, MIN(sizeof(lun->vendor_name), sizeof(parameter.vendor_identification))); memcpy(lun->product_name, parameter.product_identification, MIN(sizeof(lun->product_name), sizeof(parameter.product_identification))); memcpy(lun->product_revision, parameter.product_revision_level, MIN(sizeof(lun->product_revision), sizeof(parameter.product_revision_level))); lun->device_type = parameter.peripherial_device_type; /* 1:1 mapping */ lun->removable = (parameter.removable_medium == 1); return B_OK; } status_t usb_disk_reset_capacity(device_lun *lun) { lun->block_size = 512; lun->block_count = 0; return B_OK; } status_t usb_disk_update_capacity(device_lun *lun) { size_t dataLength = sizeof(scsi_read_capacity_10_parameter); scsi_read_capacity_10_parameter parameter; status_t result = B_ERROR; err_act action = err_act_ok; uint8 commandBlock[12]; memset(commandBlock, 0, sizeof(commandBlock)); commandBlock[0] = SCSI_READ_CAPACITY_10; commandBlock[1] = lun->logical_unit_number << 5; // Retry reading the capacity up to three times. The first try might only // yield a unit attention telling us that the device or media status // changed, which is more or less expected if it is the first operation // on the device or the device only clears the unit atention for capacity // reads. for (int32 i = 0; i < 5; i++) { result = usb_disk_operation(lun, commandBlock, 10, ¶meter, &dataLength, true, &action); if (result == B_OK || (action != err_act_retry && action != err_act_many_retries)) { break; } // In some cases, it's best to wait a little for the device to settle // before retrying. if (lun->device->is_ufi && (result == B_DEV_NO_MEDIA || result == B_TIMED_OUT || result == B_DEV_STALLED)) snooze(10000); } if (result != B_OK) { TRACE_ALWAYS("failed to update capacity: %s\n", strerror(result)); lun->media_present = false; lun->media_changed = false; usb_disk_reset_capacity(lun); return result; } lun->media_present = true; lun->media_changed = false; lun->block_size = ntohl(parameter.logical_block_length); lun->block_count = ntohl(parameter.last_logical_block_address) + 1; return B_OK; } status_t usb_disk_synchronize(device_lun *lun, bool force) { if (lun->device->is_ufi) { // UFI use interrupt because it runs all commands immediately, and // tells us when its done. There is no cache involved in that case, // so nothing to synchronize. return B_UNSUPPORTED; } if (lun->device->sync_support == 0) { // this device reported an illegal request when syncing or repeatedly // returned an other error, it apparently does not support syncing... return B_UNSUPPORTED; } if (!lun->should_sync && !force) return B_OK; uint8 commandBlock[12]; memset(commandBlock, 0, sizeof(commandBlock)); commandBlock[0] = SCSI_SYNCHRONIZE_CACHE_10; commandBlock[1] = lun->logical_unit_number << 5; status_t result = usb_disk_operation(lun, commandBlock, 10, NULL, NULL, false); if (result == B_OK) { lun->device->sync_support = SYNC_SUPPORT_RELOAD; lun->should_sync = false; return B_OK; } if (result == B_DEV_INVALID_IOCTL) lun->device->sync_support = 0; else lun->device->sync_support--; return result; } // //#pragma mark - Device Attach/Detach Notifications and Callback // static void usb_disk_callback(void *cookie, status_t status, void *data, size_t actualLength) { //TRACE("callback()\n"); disk_device *device = (disk_device *)cookie; device->status = status; device->actual_length = actualLength; release_sem(device->notify); } static status_t usb_disk_device_added(usb_device newDevice, void **cookie) { TRACE("device_added(0x%08" B_PRIx32 ")\n", newDevice); disk_device *device = (disk_device *)malloc(sizeof(disk_device)); device->device = newDevice; device->removed = false; device->open_count = 0; device->interface = 0xff; device->current_tag = 0; device->sync_support = SYNC_SUPPORT_RELOAD; device->tur_supported = true; device->is_atapi = false; device->is_ufi = false; device->luns = NULL; // scan through the interfaces to find our bulk-only data interface const usb_configuration_info *configuration = gUSBModule->get_configuration(newDevice); if (configuration == NULL) { free(device); return B_ERROR; } for (size_t i = 0; i < configuration->interface_count; i++) { usb_interface_info *interface = configuration->interface[i].active; if (interface == NULL) continue; if (interface->descr->interface_class == USB_MASS_STORAGE_DEVICE_CLASS && (((interface->descr->interface_subclass == 0x06 /* SCSI */ || interface->descr->interface_subclass == 0x02 /* ATAPI */ || interface->descr->interface_subclass == 0x05 /* ATAPI */) && interface->descr->interface_protocol == 0x50 /* bulk-only */) || (interface->descr->interface_subclass == 0x04 /* UFI */ && interface->descr->interface_protocol == 0x00))) { bool hasIn = false; bool hasOut = false; bool hasInt = false; for (size_t j = 0; j < interface->endpoint_count; j++) { usb_endpoint_info *endpoint = &interface->endpoint[j]; if (endpoint == NULL) continue; if (!hasIn && (endpoint->descr->endpoint_address & USB_ENDPOINT_ADDR_DIR_IN) != 0 && endpoint->descr->attributes == USB_ENDPOINT_ATTR_BULK) { device->bulk_in = endpoint->handle; hasIn = true; } else if (!hasOut && (endpoint->descr->endpoint_address & USB_ENDPOINT_ADDR_DIR_IN) == 0 && endpoint->descr->attributes == USB_ENDPOINT_ATTR_BULK) { device->bulk_out = endpoint->handle; hasOut = true; } else if (!hasInt && (endpoint->descr->endpoint_address & USB_ENDPOINT_ADDR_DIR_IN) && endpoint->descr->attributes == USB_ENDPOINT_ATTR_INTERRUPT) { device->interrupt = endpoint->handle; hasInt = true; } if (hasIn && hasOut && hasInt) break; } if (!(hasIn && hasOut)) { // Missing one of the required endpoints, try next interface continue; } device->interface = interface->descr->interface_number; device->is_atapi = interface->descr->interface_subclass != 0x06 && interface->descr->interface_subclass != 0x04; device->is_ufi = interface->descr->interface_subclass == 0x04; if (device->is_ufi && !hasInt) { // UFI without interrupt endpoint is not possible. continue; } break; } } if (device->interface == 0xff) { TRACE_ALWAYS("no valid bulk-only or CBI interface found\n"); free(device); return B_ERROR; } mutex_init(&device->lock, "usb_disk device lock"); device->notify = create_sem(0, "usb_disk callback notify"); if (device->notify < B_OK) { mutex_destroy(&device->lock); status_t result = device->notify; free(device); return result; } if (device->is_ufi) { device->interruptLock = create_sem(0, "usb_disk interrupt lock"); if (device->interruptLock < B_OK) { mutex_destroy(&device->lock); delete_sem(device->notify); status_t result = device->interruptLock; free(device); return result; } } device->lun_count = usb_disk_get_max_lun(device) + 1; device->luns = (device_lun **)malloc(device->lun_count * sizeof(device_lun *)); for (uint8 i = 0; i < device->lun_count; i++) device->luns[i] = NULL; status_t result = B_OK; TRACE_ALWAYS("device reports a lun count of %d\n", device->lun_count); for (uint8 i = 0; i < device->lun_count; i++) { // create the individual luns present on this device device_lun *lun = (device_lun *)malloc(sizeof(device_lun)); if (lun == NULL) { result = B_NO_MEMORY; break; } device->luns[i] = lun; lun->device = device; lun->logical_unit_number = i; lun->should_sync = false; lun->media_present = true; lun->media_changed = true; memset(lun->vendor_name, 0, sizeof(lun->vendor_name)); memset(lun->product_name, 0, sizeof(lun->product_name)); memset(lun->product_revision, 0, sizeof(lun->product_revision)); usb_disk_reset_capacity(lun); // initialize this lun result = usb_disk_inquiry(lun); if (device->is_ufi) { // Reset the device // If we don't do it all the other commands except inquiry and send // diagnostics will be stalled. result = usb_disk_send_diagnostic(lun); } err_act action = err_act_ok; for (uint32 tries = 0; tries < 8; tries++) { TRACE("usb lun %" B_PRIu8 " inquiry attempt %" B_PRIu32 " begin\n", i, tries); status_t ready = usb_disk_test_unit_ready(lun, &action); if (ready == B_OK || ready == B_DEV_NO_MEDIA || ready == B_DEV_MEDIA_CHANGED) { if (lun->device_type == B_CD) lun->write_protected = true; // TODO: check for write protection; disabled since some // devices lock up when getting the mode sense else if (/*usb_disk_mode_sense(lun) != B_OK*/true) lun->write_protected = false; TRACE("usb lun %" B_PRIu8 " ready. write protected = %c%s\n", i, lun->write_protected ? 'y' : 'n', ready == B_DEV_NO_MEDIA ? " (no media inserted)" : ""); break; } TRACE("usb lun %" B_PRIu8 " inquiry attempt %" B_PRIu32 " failed\n", i, tries); if (action != err_act_retry && action != err_act_many_retries) break; bigtime_t snoozeTime = 1000000 * tries; TRACE("snoozing %" B_PRIu64 " microseconds for usb lun\n", snoozeTime); snooze(snoozeTime); } if (result != B_OK) break; } if (result != B_OK) { TRACE_ALWAYS("failed to initialize logical units: %s\n", strerror(result)); usb_disk_free_device_and_luns(device); return result; } mutex_lock(&gDeviceListLock); device->device_number = 0; disk_device *other = gDeviceList; while (other != NULL) { if (other->device_number >= device->device_number) device->device_number = other->device_number + 1; other = (disk_device *)other->link; } device->link = (void *)gDeviceList; gDeviceList = device; gLunCount += device->lun_count; for (uint8 i = 0; i < device->lun_count; i++) sprintf(device->luns[i]->name, DEVICE_NAME, device->device_number, i); mutex_unlock(&gDeviceListLock); TRACE("new device: 0x%p\n", device); *cookie = (void *)device; return B_OK; } static status_t usb_disk_device_removed(void *cookie) { TRACE("device_removed(0x%p)\n", cookie); disk_device *device = (disk_device *)cookie; mutex_lock(&gDeviceListLock); if (gDeviceList == device) { gDeviceList = (disk_device *)device->link; } else { disk_device *element = gDeviceList; while (element) { if (element->link == device) { element->link = device->link; break; } element = (disk_device *)element->link; } } gLunCount -= device->lun_count; gDeviceCount--; device->removed = true; gUSBModule->cancel_queued_transfers(device->bulk_in); gUSBModule->cancel_queued_transfers(device->bulk_out); if (device->open_count == 0) usb_disk_free_device_and_luns(device); mutex_unlock(&gDeviceListLock); return B_OK; } // //#pragma mark - Partial Buffer Functions // static bool usb_disk_needs_partial_buffer(device_lun *lun, off_t position, size_t length, uint32 &blockPosition, uint16 &blockCount) { blockPosition = (uint32)(position / lun->block_size); if ((off_t)blockPosition * lun->block_size != position) return true; blockCount = (uint16)(length / lun->block_size); if ((size_t)blockCount * lun->block_size != length) return true; return false; } static status_t usb_disk_block_read(device_lun *lun, uint32 blockPosition, uint16 blockCount, void *buffer, size_t *length) { uint8 commandBlock[12]; memset(commandBlock, 0, sizeof(commandBlock)); if (lun->device->is_ufi) { commandBlock[0] = SCSI_READ_12; commandBlock[1] = lun->logical_unit_number << 5; commandBlock[2] = blockPosition >> 24; commandBlock[3] = blockPosition >> 16; commandBlock[4] = blockPosition >> 8; commandBlock[5] = blockPosition; commandBlock[6] = 0; // blockCount >> 24; commandBlock[7] = 0; // blockCount >> 16; commandBlock[8] = blockCount >> 8; commandBlock[9] = blockCount; status_t result = B_OK; for (int tries = 0; tries < 5; tries++) { result = usb_disk_operation(lun, commandBlock, 12, buffer, length, true); if (result == B_OK) break; else snooze(10000); } return result; } else { commandBlock[0] = SCSI_READ_10; commandBlock[1] = 0; commandBlock[2] = blockPosition >> 24; commandBlock[3] = blockPosition >> 16; commandBlock[4] = blockPosition >> 8; commandBlock[5] = blockPosition; commandBlock[7] = blockCount >> 8; commandBlock[8] = blockCount; status_t result = usb_disk_operation(lun, commandBlock, 10, buffer, length, true); return result; } } static status_t usb_disk_block_write(device_lun *lun, uint32 blockPosition, uint16 blockCount, void *buffer, size_t *length) { uint8 commandBlock[12]; memset(commandBlock, 0, sizeof(commandBlock)); if (lun->device->is_ufi) { commandBlock[0] = SCSI_WRITE_12; commandBlock[1] = lun->logical_unit_number << 5; commandBlock[2] = blockPosition >> 24; commandBlock[3] = blockPosition >> 16; commandBlock[4] = blockPosition >> 8; commandBlock[5] = blockPosition; commandBlock[6] = blockCount >> 24; commandBlock[7] = blockCount >> 16; commandBlock[8] = blockCount >> 8; commandBlock[9] = blockCount; status_t result; result = usb_disk_operation(lun, commandBlock, 12, buffer, length, false); int retry = 10; err_act action = err_act_ok; while (result == B_DEV_NO_MEDIA && retry > 0) { snooze(10000); result = usb_disk_request_sense(lun, &action); retry--; } if (result == B_OK) lun->should_sync = true; return result; } else { commandBlock[0] = SCSI_WRITE_10; commandBlock[2] = blockPosition >> 24; commandBlock[3] = blockPosition >> 16; commandBlock[4] = blockPosition >> 8; commandBlock[5] = blockPosition; commandBlock[7] = blockCount >> 8; commandBlock[8] = blockCount; status_t result = usb_disk_operation(lun, commandBlock, 10, buffer, length, false); if (result == B_OK) lun->should_sync = true; return result; } } static status_t usb_disk_prepare_partial_buffer(device_lun *lun, off_t position, size_t length, void *&partialBuffer, void *&blockBuffer, uint32 &blockPosition, uint16 &blockCount) { blockPosition = (uint32)(position / lun->block_size); blockCount = (uint16)((uint32)((position + length + lun->block_size - 1) / lun->block_size) - blockPosition); size_t blockLength = blockCount * lun->block_size; blockBuffer = malloc(blockLength); if (blockBuffer == NULL) { TRACE_ALWAYS("no memory to allocate partial buffer\n"); return B_NO_MEMORY; } status_t result = usb_disk_block_read(lun, blockPosition, blockCount, blockBuffer, &blockLength); if (result != B_OK) { TRACE_ALWAYS("block read failed when filling partial buffer: %s\n", strerror(result)); free(blockBuffer); return result; } off_t offset = position - (off_t)blockPosition * lun->block_size; partialBuffer = (uint8 *)blockBuffer + offset; return B_OK; } // //#pragma mark - Driver Hooks // static status_t usb_disk_open(const char *name, uint32 flags, void **cookie) { TRACE("open(%s)\n", name); if (strncmp(name, DEVICE_NAME_BASE, strlen(DEVICE_NAME_BASE)) != 0) return B_NAME_NOT_FOUND; int32 lastPart = 0; size_t nameLength = strlen(name); for (int32 i = nameLength - 1; i >= 0; i--) { if (name[i] == '/') { lastPart = i; break; } } char rawName[nameLength + 4]; strncpy(rawName, name, lastPart + 1); rawName[lastPart + 1] = 0; strcat(rawName, "raw"); TRACE("opening raw device %s for %s\n", rawName, name); mutex_lock(&gDeviceListLock); disk_device *device = gDeviceList; while (device) { for (uint8 i = 0; i < device->lun_count; i++) { device_lun *lun = device->luns[i]; if (strncmp(rawName, lun->name, 32) == 0) { // found the matching device/lun if (device->removed) { mutex_unlock(&gDeviceListLock); return B_ERROR; } device->open_count++; *cookie = lun; mutex_unlock(&gDeviceListLock); return B_OK; } } device = (disk_device *)device->link; } mutex_unlock(&gDeviceListLock); return B_NAME_NOT_FOUND; } static status_t usb_disk_close(void *cookie) { TRACE("close()\n"); device_lun *lun = (device_lun *)cookie; disk_device *device = lun->device; mutex_lock(&device->lock); if (!device->removed) usb_disk_synchronize(lun, false); mutex_unlock(&device->lock); return B_OK; } static status_t usb_disk_free(void *cookie) { TRACE("free()\n"); mutex_lock(&gDeviceListLock); device_lun *lun = (device_lun *)cookie; disk_device *device = lun->device; device->open_count--; if (device->open_count == 0 && device->removed) { // we can simply free the device here as it has been removed from // the device list in the device removed notification hook usb_disk_free_device_and_luns(device); } mutex_unlock(&gDeviceListLock); return B_OK; } static inline void normalize_name(char *name, size_t nameLength) { bool wasSpace = false; size_t insertIndex = 0; for (size_t i = 0; i < nameLength; i++) { bool isSpace = name[i] == ' '; if (isSpace && wasSpace) continue; name[insertIndex++] = name[i]; wasSpace = isSpace; } if (insertIndex > 0 && name[insertIndex - 1] == ' ') insertIndex--; name[insertIndex] = 0; } static status_t usb_disk_ioctl(void *cookie, uint32 op, void *buffer, size_t length) { device_lun *lun = (device_lun *)cookie; disk_device *device = lun->device; mutex_lock(&device->lock); if (device->removed) { mutex_unlock(&device->lock); return B_DEV_NOT_READY; } status_t result = B_DEV_INVALID_IOCTL; switch (op) { case B_GET_DEVICE_SIZE: { if (lun->media_changed) { result = usb_disk_update_capacity(lun); if (result != B_OK) break; } size_t size = lun->block_size * lun->block_count; result = user_memcpy(buffer, &size, sizeof(size)); break; } case B_GET_MEDIA_STATUS: { err_act action = err_act_ok; for (uint32 tries = 0; tries < 3; tries++) { status_t ready = usb_disk_test_unit_ready(lun, &action); if (ready == B_OK || ready == B_DEV_NO_MEDIA || (action != err_act_retry && action != err_act_many_retries)) { *(status_t *)buffer = ready; break; } snooze(500000); } TRACE("B_GET_MEDIA_STATUS: 0x%08" B_PRIx32 "\n", *(status_t *)buffer); result = B_OK; break; } case B_GET_GEOMETRY: { if (lun->media_changed) { result = usb_disk_update_capacity(lun); if (result != B_OK) break; } device_geometry geometry; devfs_compute_geometry_size(&geometry, lun->block_count, lun->block_size); geometry.device_type = lun->device_type; geometry.removable = lun->removable; geometry.read_only = lun->write_protected; geometry.write_once = lun->device_type == B_WORM; TRACE("B_GET_GEOMETRY: %" B_PRId32 " sectors at %" B_PRId32 " bytes per sector\n", geometry.cylinder_count, geometry.bytes_per_sector); result = user_memcpy(buffer, &geometry, sizeof(device_geometry)); break; } case B_FLUSH_DRIVE_CACHE: TRACE("B_FLUSH_DRIVE_CACHE\n"); result = usb_disk_synchronize(lun, true); break; case B_EJECT_DEVICE: { uint8 commandBlock[12]; memset(commandBlock, 0, sizeof(commandBlock)); commandBlock[0] = SCSI_START_STOP_UNIT_6; commandBlock[1] = lun->logical_unit_number << 5; commandBlock[4] = 2; result = usb_disk_operation(lun, commandBlock, 6, NULL, NULL, false); break; } case B_LOAD_MEDIA: { uint8 commandBlock[12]; memset(commandBlock, 0, sizeof(commandBlock)); commandBlock[0] = SCSI_START_STOP_UNIT_6; commandBlock[1] = lun->logical_unit_number << 5; commandBlock[4] = 3; result = usb_disk_operation(lun, commandBlock, 6, NULL, NULL, false); break; } case B_GET_ICON: // We don't support this legacy ioctl anymore, but the two other // icon ioctls below instead. break; case B_GET_ICON_NAME: { const char *iconName = "devices/drive-removable-media-usb"; char vendor[sizeof(lun->vendor_name)+1]; char product[sizeof(lun->product_name)+1]; if (device->is_ufi) { iconName = "devices/drive-floppy-usb"; } switch (lun->device_type) { case B_CD: case B_OPTICAL: iconName = "devices/drive-optical"; break; case B_TAPE: // TODO default: snprintf(vendor, sizeof(vendor), "%.8s", lun->vendor_name); snprintf(product, sizeof(product), "%.16s", lun->product_name); for (int i = 0; kIconMatches[i].icon; i++) { if (kIconMatches[i].vendor != NULL && strstr(vendor, kIconMatches[i].vendor) == NULL) continue; if (kIconMatches[i].product != NULL && strstr(product, kIconMatches[i].product) == NULL) continue; iconName = kIconMatches[i].name; } break; } result = user_strlcpy((char *)buffer, iconName, B_FILE_NAME_LENGTH); break; } case B_GET_VECTOR_ICON: { device_icon *icon = &kKeyIconData; char vendor[sizeof(lun->vendor_name)+1]; char product[sizeof(lun->product_name)+1]; if (length != sizeof(device_icon)) { result = B_BAD_VALUE; break; } if (device->is_ufi) { // UFI is specific for floppy drives icon = &kFloppyIconData; } else { switch (lun->device_type) { case B_CD: case B_OPTICAL: icon = &kCDIconData; break; case B_TAPE: // TODO default: snprintf(vendor, sizeof(vendor), "%.8s", lun->vendor_name); snprintf(product, sizeof(product), "%.16s", lun->product_name); for (int i = 0; kIconMatches[i].icon; i++) { if (kIconMatches[i].vendor != NULL && strstr(vendor, kIconMatches[i].vendor) == NULL) continue; if (kIconMatches[i].product != NULL && strstr(product, kIconMatches[i].product) == NULL) continue; icon = kIconMatches[i].icon; } break; } } device_icon iconData; if (user_memcpy(&iconData, buffer, sizeof(device_icon)) != B_OK) { result = B_BAD_ADDRESS; break; } if (iconData.icon_size >= icon->icon_size) { if (user_memcpy(iconData.icon_data, icon->icon_data, (size_t)icon->icon_size) != B_OK) { result = B_BAD_ADDRESS; break; } } iconData.icon_size = icon->icon_size; result = user_memcpy(buffer, &iconData, sizeof(device_icon)); break; } case B_GET_DEVICE_NAME: { size_t nameLength = sizeof(lun->vendor_name) + sizeof(lun->product_name) + sizeof(lun->product_revision) + 3; char name[nameLength]; snprintf(name, nameLength, "%.8s %.16s %.4s", lun->vendor_name, lun->product_name, lun->product_revision); normalize_name(name, nameLength); result = user_strlcpy((char *)buffer, name, length); if (result > 0) result = B_OK; TRACE_ALWAYS("got device name \"%s\": %s\n", name, strerror(result)); break; } default: TRACE_ALWAYS("unhandled ioctl %" B_PRId32 "\n", op); break; } mutex_unlock(&device->lock); return result; } static status_t usb_disk_read(void *cookie, off_t position, void *buffer, size_t *length) { if (buffer == NULL || length == NULL) return B_BAD_VALUE; TRACE("read(%" B_PRIdOFF ", %ld)\n", position, *length); device_lun *lun = (device_lun *)cookie; disk_device *device = lun->device; mutex_lock(&device->lock); if (device->removed) { *length = 0; mutex_unlock(&device->lock); return B_DEV_NOT_READY; } status_t result = B_ERROR; uint32 blockPosition = 0; uint16 blockCount = 0; bool needsPartial = usb_disk_needs_partial_buffer(lun, position, *length, blockPosition, blockCount); if (needsPartial) { void *partialBuffer = NULL; void *blockBuffer = NULL; result = usb_disk_prepare_partial_buffer(lun, position, *length, partialBuffer, blockBuffer, blockPosition, blockCount); if (result == B_OK) { memcpy(buffer, partialBuffer, *length); free(blockBuffer); } } else { result = usb_disk_block_read(lun, blockPosition, blockCount, buffer, length); } mutex_unlock(&device->lock); if (result == B_OK) { TRACE("read successful with %ld bytes\n", *length); return B_OK; } *length = 0; TRACE_ALWAYS("read failed: %s\n", strerror(result)); return result; } static status_t usb_disk_write(void *cookie, off_t position, const void *buffer, size_t *length) { if (buffer == NULL || length == NULL) return B_BAD_VALUE; TRACE("write(%" B_PRIdOFF", %ld)\n", position, *length); device_lun *lun = (device_lun *)cookie; disk_device *device = lun->device; mutex_lock(&device->lock); if (device->removed) { *length = 0; mutex_unlock(&device->lock); return B_DEV_NOT_READY; } status_t result = B_ERROR; uint32 blockPosition = 0; uint16 blockCount = 0; bool needsPartial = usb_disk_needs_partial_buffer(lun, position, *length, blockPosition, blockCount); if (needsPartial) { void *partialBuffer = NULL; void *blockBuffer = NULL; result = usb_disk_prepare_partial_buffer(lun, position, *length, partialBuffer, blockBuffer, blockPosition, blockCount); if (result == B_OK) { memcpy(partialBuffer, buffer, *length); size_t blockLength = blockCount * lun->block_size; result = usb_disk_block_write(lun, blockPosition, blockCount, blockBuffer, &blockLength); free(blockBuffer); } } else { result = usb_disk_block_write(lun, blockPosition, blockCount, (void *)buffer, length); } mutex_unlock(&device->lock); if (result == B_OK) { TRACE("write successful with %ld bytes\n", *length); return B_OK; } *length = 0; TRACE_ALWAYS("write failed: %s\n", strerror(result)); return result; } // //#pragma mark - Driver Entry Points // status_t init_hardware() { TRACE("init_hardware()\n"); return B_OK; } status_t init_driver() { TRACE("init_driver()\n"); static usb_notify_hooks notifyHooks = { &usb_disk_device_added, &usb_disk_device_removed }; static usb_support_descriptor supportedDevices[] = { { 0x08 /* mass storage */, 0x06 /* SCSI */, 0x50 /* bulk */, 0, 0 }, { 0x08 /* mass storage */, 0x02 /* ATAPI */, 0x50 /* bulk */, 0, 0 }, { 0x08 /* mass storage */, 0x05 /* ATAPI */, 0x50 /* bulk */, 0, 0 }, { 0x08 /* mass storage */, 0x04 /* UFI */, 0x00, 0, 0 } }; gDeviceList = NULL; gDeviceCount = 0; gLunCount = 0; mutex_init(&gDeviceListLock, "usb_disk device list lock"); TRACE("trying module %s\n", B_USB_MODULE_NAME); status_t result = get_module(B_USB_MODULE_NAME, (module_info **)&gUSBModule); if (result < B_OK) { TRACE_ALWAYS("getting module failed: %s\n", strerror(result)); mutex_destroy(&gDeviceListLock); return result; } gUSBModule->register_driver(DRIVER_NAME, supportedDevices, 4, NULL); gUSBModule->install_notify(DRIVER_NAME, ¬ifyHooks); return B_OK; } void uninit_driver() { TRACE("uninit_driver()\n"); gUSBModule->uninstall_notify(DRIVER_NAME); mutex_lock(&gDeviceListLock); if (gDeviceNames) { for (int32 i = 0; gDeviceNames[i]; i++) free(gDeviceNames[i]); free(gDeviceNames); gDeviceNames = NULL; } mutex_destroy(&gDeviceListLock); put_module(B_USB_MODULE_NAME); } const char ** publish_devices() { TRACE("publish_devices()\n"); if (gDeviceNames) { for (int32 i = 0; gDeviceNames[i]; i++) free(gDeviceNames[i]); free(gDeviceNames); gDeviceNames = NULL; } gDeviceNames = (char **)malloc(sizeof(char *) * (gLunCount + 1)); if (gDeviceNames == NULL) return NULL; int32 index = 0; mutex_lock(&gDeviceListLock); disk_device *device = gDeviceList; while (device) { for (uint8 i = 0; i < device->lun_count; i++) gDeviceNames[index++] = strdup(device->luns[i]->name); device = (disk_device *)device->link; } gDeviceNames[index++] = NULL; mutex_unlock(&gDeviceListLock); return (const char **)gDeviceNames; } device_hooks * find_device(const char *name) { TRACE("find_device()\n"); static device_hooks hooks = { &usb_disk_open, &usb_disk_close, &usb_disk_free, &usb_disk_ioctl, &usb_disk_read, &usb_disk_write, NULL, NULL, NULL, NULL }; return &hooks; }