| #include <linux/completion.h> |
| |
| //#define DISABLE_HOST_OS_DETECTION 1 |
| //#define DISABLE_NCM_ECM_DETECTION 1 |
| #define HAS_USB_CDC_NCM (0x1 << 0) |
| #define HAS_USB_CDC_ECM (0x1 << 1) |
| #define USB_FUNCTION_NAME_LEN (256) |
| #define NCM_GUARD_TIMEOUT_JIFFIES (5 * HZ) |
| //#define DUMP_OS_DETECT_STRUCT 1 |
| |
| struct os_detect { |
| /* reconfigure work after detecting the os */ |
| struct work_struct reconfigure_work; |
| /* apple second stage work */ |
| struct work_struct ncm_ecm_detection_work; |
| struct work_struct restart_work; |
| struct delayed_work ncm_guard_work; |
| int default_os; |
| int bankup_default_os; |
| int cur_os; |
| /* the index of the USB_DT_STRING */ |
| int cmd_idx; |
| char win7_bankup[USB_FUNCTION_NAME_LEN]; |
| char win8_bankup[USB_FUNCTION_NAME_LEN]; |
| char apple_bankup[USB_FUNCTION_NAME_LEN]; |
| char os_linux_bankup[USB_FUNCTION_NAME_LEN]; |
| char win7[USB_FUNCTION_NAME_LEN]; |
| char win8[USB_FUNCTION_NAME_LEN]; |
| char apple[USB_FUNCTION_NAME_LEN]; |
| char os_linux[USB_FUNCTION_NAME_LEN]; |
| char os[USB_FUNCTION_NAME_LEN]; |
| /* for the second stage functions */ |
| char win7_stage2[USB_FUNCTION_NAME_LEN]; |
| char win8_stage2[USB_FUNCTION_NAME_LEN]; |
| char apple_stage2[USB_FUNCTION_NAME_LEN]; |
| char linux_stage2[USB_FUNCTION_NAME_LEN]; |
| char has_get_bos; |
| u32 bos_len; |
| char nr_bos; |
| char has_set_intf; |
| char apple_has_ncm_ecm; |
| char linux_has_ncm_ecm; |
| char nr_str_before_setcfg; |
| char nr_str; |
| u32 str_dt_bitmap; |
| u32 cfg_dt_bitmap; |
| u32 has_setconfig; |
| u32 nr_usb_req; |
| struct timer_list linux_timer; |
| struct timer_list apple_timer; |
| #define NR_RCD_REQLEN (6) |
| /* the length first 6 requset */ |
| u16 reqlen[NR_RCD_REQLEN]; |
| |
| #define NR_DELAY_REQ_BEFORE_RECONFIG (3) |
| u16 nr_dreq_before_recfg; |
| u32 nr_ncm_setdatainf; |
| u32 nr_ncm_setctrlinf; |
| |
| #define KTIME_MS_BETWEEN_RSTINF_AND_SUSPEND (10 * 1000) |
| s64 reset_intf_time_ms; |
| |
| bool string_0_err; |
| bool string_n0_err; |
| }; |
| |
| static struct os_detect os_detect; |
| static int old_max_speed; |
| static int os_detect_done; |
| |
| static char *os_type_name(int os_type) |
| { |
| switch (os_type) { |
| case HOST_OS_TYPE_WIN7_WINXP: |
| return "WIN7/WINXP"; |
| case HOST_OS_TYPE_APPLE_STAGE1: |
| return "APPLEs1"; |
| case HOST_OS_TYPE_APPLE: |
| return "APPLE"; |
| case HOST_OS_TYPE_WIN8: |
| return "WIN8"; |
| case HOST_OS_TYPE_WIN7_OR_WIN8: |
| return "WIN7_OR_WIN8"; |
| case HOST_OS_TYPE_LINUX_STAGE1: |
| return "LINUXs1"; |
| case HOST_OS_TYPE_LINUX: |
| return "LINUX"; |
| default: |
| return "UNKNOWN"; |
| } |
| } |
| |
| static ssize_t __attr_show(struct device *pdev, char *src, char *dst) |
| { |
| struct android_dev *dev = dev_get_drvdata(pdev); |
| ssize_t size; |
| mutex_lock(&dev->mutex); |
| size = sprintf(dst, "%s", src); |
| mutex_unlock(&dev->mutex); |
| return size; |
| } |
| |
| static ssize_t __attr_store(struct device *pdev, const char *src, |
| char *dst, int len, size_t size) |
| { |
| struct android_dev *dev = dev_get_drvdata(pdev); |
| mutex_lock(&dev->mutex); |
| |
| if (dev->enabled) { |
| mutex_unlock(&dev->mutex); |
| return -EBUSY; |
| } |
| |
| memset(dst, 0, len); |
| strlcpy(dst, src, len); |
| mutex_unlock(&dev->mutex); |
| |
| return size; |
| } |
| |
| static void set_ncm_ecm_flag(void) |
| { |
| if (strnstr(os_detect.apple, "ncm", USB_FUNCTION_NAME_LEN)) |
| os_detect.apple_has_ncm_ecm |= HAS_USB_CDC_NCM; |
| else |
| os_detect.apple_has_ncm_ecm &= ~HAS_USB_CDC_NCM; |
| |
| if (strnstr(os_detect.apple, "ecm", USB_FUNCTION_NAME_LEN)) |
| os_detect.apple_has_ncm_ecm |= HAS_USB_CDC_ECM; |
| else |
| os_detect.apple_has_ncm_ecm &= ~HAS_USB_CDC_ECM; |
| |
| if (strnstr(os_detect.os_linux, "ncm", USB_FUNCTION_NAME_LEN)) |
| os_detect.linux_has_ncm_ecm |= HAS_USB_CDC_NCM; |
| else |
| os_detect.linux_has_ncm_ecm &= ~HAS_USB_CDC_NCM; |
| |
| if (strnstr(os_detect.os_linux, "ecm", USB_FUNCTION_NAME_LEN)) |
| os_detect.linux_has_ncm_ecm |= HAS_USB_CDC_ECM; |
| else |
| os_detect.linux_has_ncm_ecm &= ~HAS_USB_CDC_ECM; |
| |
| #ifdef DISABLE_NCM_ECM_DETECTION |
| os_detect.linux_has_ncm_ecm = os_detect.apple_has_ncm_ecm = 0; |
| #endif |
| } |
| |
| static ssize_t |
| win7_show(struct device *pdev, struct device_attribute *attr, char *buf) |
| { |
| return __attr_show(pdev, os_detect.win7, buf); |
| } |
| |
| static ssize_t |
| win7_store(struct device *pdev, struct device_attribute *attr, |
| const char *buff, size_t size) |
| { |
| return __attr_store(pdev, buff, os_detect.win7, |
| sizeof(os_detect.win7), size); |
| } |
| |
| static ssize_t |
| win8_show(struct device *pdev, struct device_attribute *attr, char *buf) |
| { |
| return __attr_show(pdev, os_detect.win8, buf); |
| } |
| |
| static ssize_t |
| win8_store(struct device *pdev, struct device_attribute *attr, |
| const char *buff, size_t size) |
| { |
| return __attr_store(pdev, buff, os_detect.win8, |
| sizeof(os_detect.win8), size); |
| } |
| |
| static ssize_t |
| apple_show(struct device *pdev, struct device_attribute *attr, char *buf) |
| { |
| return __attr_show(pdev, os_detect.apple, buf); |
| } |
| |
| static ssize_t |
| apple_store(struct device *pdev, struct device_attribute *attr, |
| const char *buff, size_t size) |
| { |
| return __attr_store(pdev, buff, os_detect.apple, |
| sizeof(os_detect.apple), size); |
| } |
| |
| static ssize_t |
| linux_show(struct device *pdev, struct device_attribute *attr, char *buf) |
| { |
| return __attr_show(pdev, os_detect.os_linux, buf); |
| } |
| |
| static ssize_t |
| linux_store(struct device *pdev, struct device_attribute *attr, |
| const char *buff, size_t size) |
| { |
| return __attr_store(pdev, buff, os_detect.os_linux, |
| sizeof(os_detect.os_linux), size); |
| } |
| |
| static ssize_t |
| os_show(struct device *pdev, |
| struct device_attribute *attr, char *buf) |
| { |
| return __attr_show(pdev, os_detect.os, buf); |
| } |
| |
| static ssize_t |
| win7_stage2_show(struct device *pdev, |
| struct device_attribute *attr, char *buf) |
| { |
| return __attr_show(pdev, os_detect.win7_stage2, buf); |
| } |
| |
| static ssize_t |
| win7_stage2_store(struct device *pdev, |
| struct device_attribute *attr, |
| const char *buff, size_t size) |
| { |
| return __attr_store(pdev, buff, os_detect.win7_stage2, |
| sizeof(os_detect.win7_stage2), size); |
| } |
| |
| static ssize_t |
| win8_stage2_show(struct device *pdev, |
| struct device_attribute *attr, char *buf) |
| { |
| return __attr_show(pdev, os_detect.win8_stage2, buf); |
| } |
| |
| static ssize_t |
| win8_stage2_store(struct device *pdev, |
| struct device_attribute *attr, |
| const char *buff, size_t size) |
| { |
| return __attr_store(pdev, buff, os_detect.win8_stage2, |
| sizeof(os_detect.win8_stage2), size); |
| } |
| |
| static ssize_t |
| apple_stage2_show(struct device *pdev, |
| struct device_attribute *attr, char *buf) |
| { |
| return __attr_show(pdev, os_detect.apple_stage2, buf); |
| } |
| |
| static ssize_t |
| apple_stage2_store(struct device *pdev, |
| struct device_attribute *attr, |
| const char *buff, size_t size) |
| { |
| return __attr_store(pdev, buff, os_detect.apple_stage2, |
| sizeof(os_detect.apple_stage2), size); |
| } |
| |
| static ssize_t |
| linux_stage2_show(struct device *pdev, |
| struct device_attribute *attr, char *buf) |
| { |
| return __attr_show(pdev, os_detect.linux_stage2, buf); |
| } |
| |
| static ssize_t |
| linux_stage2_store(struct device *pdev, |
| struct device_attribute *attr, |
| const char *buff, size_t size) |
| { |
| return __attr_store(pdev, buff, os_detect.linux_stage2, |
| sizeof(os_detect.linux_stage2), size); |
| } |
| |
| static ssize_t __functions_store(struct android_dev *dev, |
| char *buff, size_t size) |
| { |
| char *name; |
| char buf[USB_FUNCTION_NAME_LEN], *b; |
| char aliases[USB_FUNCTION_NAME_LEN], *a; |
| int err; |
| int is_ffs; |
| int ffs_enabled = 0; |
| |
| mutex_lock(&dev->mutex); |
| |
| if (dev->enabled) { |
| mutex_unlock(&dev->mutex); |
| return -EBUSY; |
| } |
| |
| INIT_LIST_HEAD(&dev->enabled_functions); |
| |
| strlcpy(buf, buff, sizeof(buf)); |
| b = strim(buf); |
| #if defined (CONFIG_USB_G_MBIM) |
| mbim_enabled = 0; |
| #endif |
| while (b) { |
| name = strsep(&b, ","); |
| if (!name) |
| continue; |
| |
| is_ffs = 0; |
| strlcpy(aliases, dev->ffs_aliases, sizeof(aliases)); |
| a = aliases; |
| |
| while (a) { |
| char *alias = strsep(&a, ","); |
| if (alias && !strcmp(name, alias)) { |
| is_ffs = 1; |
| break; |
| } |
| } |
| |
| if (is_ffs) { |
| if (ffs_enabled) |
| continue; |
| err = android_enable_function(dev, "ffs"); |
| if (err) |
| pr_err("android_usb: Cannot enable ffs (%d)", |
| err); |
| else |
| ffs_enabled = 1; |
| continue; |
| } |
| |
| err = android_enable_function(dev, name); |
| if (err) |
| pr_err("android_usb: Cannot enable '%s' (%d)", |
| name, err); |
| } |
| |
| mutex_unlock(&dev->mutex); |
| |
| return size; |
| } |
| |
| static ssize_t __os_store(struct android_dev *dev, |
| const char *buf, size_t size) |
| { |
| char *b; |
| mutex_lock(&dev->mutex); |
| memset(os_detect.os, 0, sizeof(os_detect.os)); |
| strlcpy(os_detect.os, buf, sizeof(os_detect.os)); |
| if (strncmp(os_detect.os, "win7", 4) == 0) |
| b = os_detect.win7; |
| else if (strncmp(os_detect.os, "win8", 4) == 0) |
| b = os_detect.win8; |
| else if (strncmp(os_detect.os, "apple", 5) == 0) |
| b = os_detect.apple; |
| else if (strncmp(os_detect.os, "linux", 5) == 0) |
| b = os_detect.os_linux; |
| else |
| b = os_detect.win8; |
| mutex_unlock(&dev->mutex); |
| return __functions_store(dev, b, size); |
| } |
| |
| static ssize_t |
| os_store(struct device *pdev, struct device_attribute *attr, |
| const char *buff, size_t size) |
| { |
| struct android_dev *dev = dev_get_drvdata(pdev); |
| if (strncmp(buff, "win7", 4) == 0) |
| os_detect.default_os = HOST_OS_TYPE_WIN7_WINXP; |
| else if (strncmp(buff, "win8", 4) == 0) |
| os_detect.default_os = HOST_OS_TYPE_WIN8; |
| else if (strncmp(buff, "apple", 5) == 0) |
| os_detect.default_os = HOST_OS_TYPE_APPLE; |
| else if (strncmp(buff, "linux", 5) == 0) |
| os_detect.default_os = HOST_OS_TYPE_LINUX; |
| else |
| os_detect.default_os = HOST_OS_TYPE_WIN7_WINXP; |
| os_detect.bankup_default_os = os_detect.default_os; |
| |
| pr_info("### Default OS is %s, %s\n", |
| os_type_name(os_detect.default_os), buff); |
| |
| strcpy(os_detect.win7_bankup, os_detect.win7); |
| strcpy(os_detect.win8_bankup, os_detect.win8); |
| strcpy(os_detect.os_linux_bankup, os_detect.os_linux); |
| strcpy(os_detect.apple_bankup, os_detect.apple); |
| pr_info("win7: %s %s\n", os_detect.win7_bankup, os_detect.win7); |
| set_ncm_ecm_flag(); |
| |
| return __os_store(dev, buff, size); |
| } |
| |
| static DEVICE_ATTR(win7, S_IRUGO | S_IWUSR, win7_show, win7_store); |
| static DEVICE_ATTR(win8, S_IRUGO | S_IWUSR, win8_show, win8_store); |
| static DEVICE_ATTR(apple, S_IRUGO | S_IWUSR, apple_show, apple_store); |
| static DEVICE_ATTR(olinux, S_IRUGO | S_IWUSR, linux_show, linux_store); |
| static DEVICE_ATTR(os, S_IRUGO | S_IWUSR, os_show, os_store); |
| static DEVICE_ATTR(win7_s2, S_IRUGO | S_IWUSR, |
| win7_stage2_show, win7_stage2_store); |
| static DEVICE_ATTR(win8_s2, S_IRUGO | S_IWUSR, |
| win8_stage2_show, win8_stage2_store); |
| static DEVICE_ATTR(apple_s2, S_IRUGO | S_IWUSR, |
| apple_stage2_show, apple_stage2_store); |
| static DEVICE_ATTR(olinux_s2, S_IRUGO | S_IWUSR, |
| linux_stage2_show, linux_stage2_store); |
| |
| #ifdef DUMP_OS_DETECT_STRUCT |
| static void dump_os_detect_struct(void) |
| { |
| pr_err("os_detect.default_os: %d\n", os_detect.default_os); |
| pr_err("os_detect.bankup_default_os: %d\n", os_detect.bankup_default_os); |
| pr_err("os_detect.cur_os: %d\n", os_detect.cur_os); |
| pr_err("os_detect.cmd_idx: %d\n", os_detect.cmd_idx); |
| pr_err("os_detect.apple: %s\n", os_detect.apple); |
| pr_err("os_detect.os_linux: %s\n", os_detect.os_linux); |
| pr_err("os_detect.has_get_bos: %d\n", os_detect.has_get_bos); |
| pr_err("os_detect.bos_len: %d\n", os_detect.bos_len); |
| pr_err("os_detect.nr_bos: %d\n", os_detect.nr_bos); |
| pr_err("os_detect.has_set_intf: %d\n", os_detect.has_set_intf); |
| pr_err("os_detect.apple_has_ncm_ecm: 0x%x\n", os_detect.apple_has_ncm_ecm); |
| pr_err("os_detect.linux_has_ncm_ecm: 0x%x\n", os_detect.linux_has_ncm_ecm); |
| pr_err("os_detect.nr_str_before_setcfg: %d\n", os_detect.nr_str_before_setcfg); |
| pr_err("os_detect.nr_str: %d\n", os_detect.nr_str; |
| pr_err("os_detect.str_dt_bitmap: 0x%x\n", os_detect.str_dt_bitmap); |
| pr_err("os_detect.cfg_dt_bitmap: 0x%x\n", os_detect.cfg_dt_bitmap); |
| pr_err("os_detect.has_setconfig: %d\n", os_detect.has_setconfig); |
| pr_err("os_detect.nr_usb_req: %d\n", os_detect.nr_usb_req); |
| pr_err("os_detect.nr_dreq_before_recfg: %d\n", os_detect.nr_dreq_before_recfg); |
| pr_err("os_detect.reset_intf_time_ms: 0x%llx\n", os_detect.reset_intf_time_ms); |
| pr_err("os_detect.nr_ncm_setdatainf: %d\n", os_detect.nr_ncm_setdatainf); |
| pr_err("os_detect.nr_ncm_setctrlinf: %d\n", os_detect.nr_ncm_setctrlinf); |
| pr_err("os_detect.old_max_speed: %d\n", old_max_speed); |
| pr_err("os_detect.os_detect_done: %d\n", os_detect_done); |
| } |
| #else |
| static void dump_os_detect_struct(void) {} |
| #endif |
| |
| static void set_cdc_ncm(char *buf) |
| { |
| pr_info("set ncm on %s\n", buf); |
| strncpy(buf, "ncm", 3); |
| } |
| |
| static void set_cdc_ecm(char *buf) |
| { |
| pr_info("set ecm on %s\n", buf); |
| strncpy(buf, "ecm", 3); |
| } |
| |
| int os_detect_is_done(void) |
| { |
| return !!os_detect_done; |
| } |
| |
| static void os_detect_set_done(void) |
| { |
| pr_info("%s\n", __func__); |
| os_detect_done = 1; |
| } |
| |
| void os_detect_clear_done(void) |
| { |
| pr_info("%s\n", __func__); |
| os_detect_done = 0; |
| } |
| |
| static void os_detect_struct_clear(void) |
| { |
| int i; |
| |
| os_detect.cur_os = HOST_OS_TYPE_UNKNOWN; |
| os_detect.cmd_idx = 0; |
| os_detect.has_get_bos = 0; |
| os_detect.bos_len = 0; |
| os_detect.nr_bos = 0; |
| os_detect.has_set_intf = 0; |
| os_detect.str_dt_bitmap = 0; |
| os_detect.cfg_dt_bitmap = 0; |
| os_detect.has_setconfig = 0; |
| os_detect.nr_usb_req = 0; |
| os_detect.nr_str_before_setcfg = 0; |
| os_detect.nr_str = 0; |
| for (i = 0; i < NR_RCD_REQLEN; i++) |
| os_detect.reqlen[i] = 0; |
| os_detect.nr_dreq_before_recfg = 0; |
| os_detect.nr_ncm_setdatainf = 0; |
| os_detect.nr_ncm_setctrlinf = 0; |
| os_detect.reset_intf_time_ms = 0; |
| os_detect.string_0_err = false; |
| os_detect.string_n0_err = false; |
| } |
| static void android_dev_reconfigure(int os_type) |
| { |
| os_detect.cur_os = os_type; |
| pr_info("Default OS: %s, Current OS: %s\n", |
| os_type_name(os_detect.default_os), |
| os_type_name(os_detect.cur_os)); |
| |
| os_detect.has_setconfig = 0; |
| BUG_ON(os_type == HOST_OS_TYPE_UNKNOWN); |
| /* We need to give the default os in the script like below: |
| ** echo win8 > /sys/class/android_usb/android0/os |
| */ |
| BUG_ON(os_detect.default_os == HOST_OS_TYPE_UNKNOWN); |
| if (os_type == HOST_OS_TYPE_WIN7_OR_WIN8) |
| return; |
| |
| os_detect_set_done(); |
| schedule_work(&os_detect.reconfigure_work); |
| } |
| |
| static void android_reconfigure_work(struct work_struct *data) |
| { |
| struct android_dev *dev = _android_dev; |
| char *os; |
| char *apple = "apple"; |
| char *win7 = "win7"; |
| char *win8 = "win8"; |
| char *os_linux = "linux"; |
| |
| pr_debug("Andoid device reconfigure...\n"); |
| |
| if (old_max_speed != 0) { |
| dev->cdev->gadget->max_speed = old_max_speed; |
| /* restore bcdUSB default value to 0x0200 */ |
| dev->cdev->desc.bcdUSB = cpu_to_le16(0x0200); |
| old_max_speed = 0; |
| } |
| os_detect.default_os = os_detect.bankup_default_os; |
| android_dev_enable(0); |
| |
| /* give some time to diag app and netifd to finish */ |
| msleep(800); |
| switch (os_detect.cur_os) { |
| case HOST_OS_TYPE_LINUX: |
| os = os_linux; |
| break; |
| case HOST_OS_TYPE_APPLE: |
| os = apple; |
| break; |
| case HOST_OS_TYPE_WIN7_WINXP: |
| os = win7; |
| break; |
| case HOST_OS_TYPE_WIN8: |
| os = win8; |
| break; |
| default: |
| pr_info("Unknown OS type...\n"); |
| if (os_detect.cmd_idx) |
| WARN_ON(1); |
| /* set it as the default os type apple */ |
| os = apple; |
| } |
| __os_store(dev, os, strlen(os)); |
| dump_os_detect_struct(); |
| android_dev_enable(1); |
| } |
| |
| static void android_restart_work(struct work_struct *data) |
| { |
| pr_info("%s\n", __func__); |
| android_dev_enable(0); |
| /* |
| * do not turn on usb very early as there need some time for |
| * host pc to boot up |
| */ |
| msleep(5000); |
| /* usb_os_restore(); */ |
| dump_os_detect_struct(); |
| android_dev_enable(1); |
| } |
| |
| |
| static void usb_os_restore_apple(void) |
| { |
| struct android_dev *dev = _android_dev; |
| char *os = "apple"; |
| |
| pr_info("%s apple: %s, apple_bank: %s\n", |
| __func__, os_detect.apple, os_detect.apple_bankup); |
| strcpy(os_detect.apple, os_detect.apple_bankup); |
| strcpy(os_detect.os_linux, os_detect.os_linux_bankup); |
| /* make sure it's ncm here, otherwise will not got to here */ |
| BUG_ON(!(os_detect.apple_has_ncm_ecm & HAS_USB_CDC_NCM)); |
| BUG_ON(!(os_detect.linux_has_ncm_ecm & HAS_USB_CDC_NCM)); |
| set_cdc_ncm(os_detect.apple); |
| __os_store(dev, os, strlen(os)); |
| os_detect_struct_clear(); |
| os_detect.default_os = HOST_OS_TYPE_APPLE; |
| } |
| |
| static void ncm_ecm_detection_work(struct work_struct *data) |
| { |
| pr_info("##enter %s\n", __func__); |
| del_timer(&os_detect.apple_timer); |
| del_timer(&os_detect.linux_timer); |
| pr_info("%s: del_timer done\n", __func__); |
| android_dev_enable(0); |
| /* give some time to diag app and netifd to finish */ |
| msleep(800); |
| /*set the ncm on apple and clear os detection struct */ |
| usb_os_restore_apple(); |
| dump_os_detect_struct(); |
| android_dev_enable(1); |
| pr_info("##exit %s\n", __func__); |
| } |
| |
| static void ncm_guard_work(struct work_struct *data) |
| { |
| pr_info("##########call %s\n", __func__); |
| del_timer(&os_detect.apple_timer); |
| pr_info("%s: del_timer done\n", __func__); |
| android_dev_enable(0); |
| /* give some time to diag app and netifd to finish */ |
| msleep(800); |
| usb_os_restore(); |
| dump_os_detect_struct(); |
| android_dev_enable(1); |
| } |
| |
| static void usb_force_reenumerate(void) |
| { |
| struct android_dev *dev = _android_dev; |
| char *os; |
| |
| pr_info("Clear os_detect to re-enumerate\n"); |
| os_detect.default_os = os_detect.bankup_default_os; |
| os_detect_struct_clear(); |
| os_detect_set_done(); |
| strcpy(os_detect.win7, os_detect.win7_bankup); |
| strcpy(os_detect.win8, os_detect.win8_bankup); |
| strcpy(os_detect.os_linux, os_detect.os_linux_bankup); |
| strcpy(os_detect.apple, os_detect.apple_bankup); |
| set_ncm_ecm_flag(); |
| old_max_speed = 0; |
| if (os_detect.default_os == HOST_OS_TYPE_WIN7_WINXP) |
| os = "win7"; |
| else if (os_detect.default_os == HOST_OS_TYPE_WIN8) |
| os = "win8"; |
| else if (os_detect.default_os == HOST_OS_TYPE_APPLE) |
| os = "apple"; |
| else if (os_detect.default_os == HOST_OS_TYPE_LINUX) |
| os = "linux"; |
| else |
| BUG(); |
| __os_store(dev, os, strlen(os)); |
| schedule_work(&os_detect.reconfigure_work); |
| } |
| |
| static void linux_reconfig_timer(struct timer_list *timer) |
| { |
| int os_type = HOST_OS_TYPE_LINUX; |
| |
| pr_info("\n$$$$$$$$linux re-config timer:%d\n", |
| os_detect.has_set_intf); |
| |
| if (unlikely(((os_detect.nr_str_before_setcfg == 0) && |
| os_detect.has_setconfig)) |
| || os_detect.string_0_err |
| || os_detect.string_n0_err) { |
| pr_err("nr_str_before_setcfg %d, string_0_err: %d %d", |
| os_detect.nr_str_before_setcfg, |
| os_detect.string_0_err, |
| os_detect.string_n0_err); |
| WARN(1, "meet BIOS in linux timer"); |
| os_detect_struct_clear(); |
| os_detect.cur_os = HOST_OS_TYPE_UNKNOWN; |
| return; |
| } |
| |
| os_detect.has_setconfig = 0; |
| if (((os_detect.default_os == HOST_OS_TYPE_LINUX) && |
| (os_detect.linux_has_ncm_ecm & HAS_USB_CDC_NCM) && |
| (!os_detect.has_set_intf)) || |
| ((os_detect.default_os == HOST_OS_TYPE_APPLE) && |
| (os_detect.apple_has_ncm_ecm & HAS_USB_CDC_NCM) && |
| (!os_detect.has_set_intf) && |
| (os_detect.linux_has_ncm_ecm & HAS_USB_CDC_NCM))) |
| set_cdc_ecm(os_detect.os_linux); |
| |
| android_dev_reconfigure(os_type); |
| os_detect_clear_done(); |
| } |
| |
| static void apple_reconfig_timer(struct timer_list *timer) |
| { |
| int os_type = HOST_OS_TYPE_APPLE; |
| |
| pr_info("\n$$$$$$$$apple re-config timer:%d\n", |
| os_detect.has_set_intf); |
| |
| os_detect.has_setconfig = 0; |
| if (((os_detect.default_os == HOST_OS_TYPE_LINUX) && |
| (os_detect.linux_has_ncm_ecm & HAS_USB_CDC_NCM) && |
| (os_detect.apple_has_ncm_ecm & HAS_USB_CDC_NCM)) || |
| ((os_detect.default_os == HOST_OS_TYPE_APPLE) && |
| (os_detect.apple_has_ncm_ecm & HAS_USB_CDC_NCM))) |
| set_cdc_ecm(os_detect.apple); |
| |
| cancel_delayed_work(&os_detect.ncm_guard_work); |
| android_dev_reconfigure(os_type); |
| os_detect_clear_done(); |
| } |
| |
| void usb_os_detect(struct usb_composite_dev *cdev, |
| const struct usb_ctrlrequest *ctrl) |
| { |
| int os_type = HOST_OS_TYPE_UNKNOWN; |
| u16 w_value; |
| |
| if (ctrl->bRequestType != 0x21 && ctrl->bRequestType != 0xa1) { |
| pr_info("%2d: 0x%x. 0x%x. 0x%x. 0x%x. 0x%x\n", |
| os_detect.nr_usb_req, |
| ctrl->bRequestType, ctrl->bRequest, |
| ctrl->wValue, ctrl->wIndex, ctrl->wLength); |
| dump_os_detect_struct(); |
| } |
| |
| #ifdef DISABLE_HOST_OS_DETECTION |
| return; |
| #endif |
| |
| /* skip os detection for production mode */ |
| if (system_is_prod_mode()) |
| return; |
| |
| /* record the reset interface time */ |
| if (unlikely((HOST_OS_TYPE_APPLE == os_detect.cur_os) && |
| (0x01 == ctrl->bRequestType) && |
| (0x0b == ctrl->bRequest) && |
| (0x00 == ctrl->wValue) && |
| (0x01 == ctrl->wIndex) && |
| (0x00 == ctrl->wLength))) { |
| os_detect.reset_intf_time_ms = ktime_to_ms(ktime_get()); |
| } |
| |
| /* NCM: USB_CDC_GET_NTB_PARAMETERS */ |
| if (unlikely((HOST_OS_TYPE_APPLE_STAGE1 == os_detect.cur_os) && |
| (0xA1 == ctrl->bRequestType) && |
| (0x80 == ctrl->bRequest) && |
| (0x00 != ctrl->wLength) && |
| (os_detect.apple_has_ncm_ecm & HAS_USB_CDC_NCM))) { |
| pr_info("NCM: del apple_timer\n"); |
| del_timer(&os_detect.apple_timer); |
| cancel_delayed_work(&os_detect.ncm_guard_work); |
| os_type = HOST_OS_TYPE_APPLE; |
| os_detect.has_setconfig = 0; |
| android_dev_reconfigure(os_type); |
| return; |
| } |
| |
| /* APPLE+NCM */ |
| if (unlikely((HOST_OS_TYPE_APPLE == os_detect.cur_os) && |
| (0xA1 == ctrl->bRequestType) && |
| (0x80 == ctrl->bRequest) && |
| (0x00 != ctrl->wLength) && |
| (os_detect.apple_has_ncm_ecm & HAS_USB_CDC_NCM))) { |
| pr_info("NCM: trig guard_timer\n"); |
| schedule_delayed_work(&os_detect.ncm_guard_work, |
| NCM_GUARD_TIMEOUT_JIFFIES); |
| } |
| |
| /* APPLE+ECM */ |
| if (unlikely((HOST_OS_TYPE_APPLE == os_detect.cur_os) && |
| (0x80 == ctrl->bRequestType) && |
| (0x08 == ctrl->bRequest) && |
| (0x00 == ctrl->wValue) && |
| (0x00 == ctrl->wIndex) && |
| (0x01 == ctrl->wLength) && |
| (0x0 == strncmp(os_detect.apple, "ecm", 3)))) { |
| pr_info("ECM: trig guard_timer\n"); |
| schedule_delayed_work(&os_detect.ncm_guard_work, |
| NCM_GUARD_TIMEOUT_JIFFIES); |
| } |
| |
| /* APPLE+NCM: cancel guard timer on set_intf */ |
| if (unlikely((HOST_OS_TYPE_APPLE == os_detect.cur_os) && |
| (0x01 == ctrl->bRequestType) && |
| (0x0b == ctrl->bRequest) && |
| (0x01 == ctrl->wValue) && |
| (0x01 == ctrl->wIndex) && |
| (0x00 == ctrl->wLength))) { |
| pr_info("APPLE: cancel guard_timer\n"); |
| cancel_delayed_work(&os_detect.ncm_guard_work); |
| } |
| |
| /* APPLE restart case */ |
| if (unlikely((HOST_OS_TYPE_APPLE == os_detect.cur_os) && |
| (0x80 == ctrl->bRequestType) && |
| (0x06 == ctrl->bRequest) && |
| (0x0100 == ctrl->wValue) && |
| (0x40 != ctrl->wLength) && |
| (0x12 != ctrl->wLength))) { |
| pr_info("APPLE: restart\n"); |
| invoke_os_detect_restore(); |
| } |
| |
| /* add some more delay for winxp to avoid unkown device */ |
| if ((HOST_OS_TYPE_UNKNOWN != os_detect.cur_os) && |
| (HOST_OS_TYPE_WIN7_OR_WIN8 != os_detect.cur_os) && |
| (HOST_OS_TYPE_APPLE_STAGE1 != os_detect.cur_os) && |
| (HOST_OS_TYPE_LINUX_STAGE1 != os_detect.cur_os)) { |
| if (0 == os_detect.nr_dreq_before_recfg) |
| return; |
| else { |
| /* delay N request before re-cfg */ |
| pr_info("nr_dreq_before_recfg: %d\n", |
| os_detect.nr_dreq_before_recfg); |
| if (--os_detect.nr_dreq_before_recfg) |
| return; |
| else { |
| android_dev_reconfigure(os_detect.cur_os); |
| return; |
| } |
| } |
| } |
| |
| /* reset the start point */ |
| if (HOST_OS_TYPE_UNKNOWN == os_detect.cur_os) { |
| if (ctrl->bRequestType == 0x80 && ctrl->bRequest == 0x06 && |
| ctrl->wValue == 0x0100 && ctrl->wLength == 0x40) { |
| pr_info("###clear os detection structure\n"); |
| os_detect_struct_clear(); |
| } |
| } |
| |
| if (os_detect.nr_usb_req >= 0 && |
| os_detect.nr_usb_req < NR_RCD_REQLEN) |
| os_detect.reqlen[os_detect.nr_usb_req] = ctrl->wLength; |
| |
| os_detect.nr_usb_req++; |
| if (old_max_speed == 0) { |
| old_max_speed = cdev->gadget->max_speed; |
| cdev->gadget->max_speed = USB_SPEED_SUPER; |
| } |
| |
| w_value = le16_to_cpu(ctrl->wValue); |
| |
| switch (ctrl->bRequest) { |
| /* we handle all standard USB descriptors */ |
| case USB_REQ_GET_DESCRIPTOR: |
| if (ctrl->bRequestType != USB_DIR_IN) |
| return; |
| |
| /*skip if apple/linux is detected except bos_dt */ |
| if ((HOST_OS_TYPE_LINUX_STAGE1 == os_detect.cur_os) || |
| (HOST_OS_TYPE_APPLE_STAGE1 == os_detect.cur_os && |
| (USB_DT_BOS != (w_value >> 8)))) { |
| os_detect.nr_str++; |
| pr_info("os_detect.nr_str: %d\n", os_detect.nr_str); |
| if ((w_value >> 8) == USB_DT_STRING) { |
| if ((os_detect.string_0_err == false) && (os_detect.nr_str <= 4)) { |
| if(((w_value & 0xff) == 0) && |
| (ctrl->wLength != 0xff && ctrl->wLength != 0x2)) |
| os_detect.string_0_err = true; |
| } |
| if (os_detect.string_n0_err == false) { |
| if(((w_value & 0xff) != 0) && |
| (ctrl->wIndex == 0)) |
| os_detect.string_n0_err = true; |
| } |
| } |
| return; |
| } |
| if (HOST_OS_TYPE_UNKNOWN == os_detect.cur_os) |
| os_detect.cmd_idx++; |
| |
| switch (w_value >> 8) { |
| case USB_DT_DEVICE: |
| /* windows XP does a second get dt_device to confirm |
| * whether the function is really changed? |
| */ |
| if ((5 == os_detect.nr_usb_req) && (0x12 == ctrl->wLength) |
| && ((os_detect.cfg_dt_bitmap & 0x1F) == 0x18) |
| && (HOST_OS_TYPE_UNKNOWN == os_detect.cur_os)) { |
| pr_info("WINXP is double checking device\n"); |
| os_type = HOST_OS_TYPE_WIN7_WINXP; |
| os_detect.has_setconfig = 0; |
| android_dev_reconfigure(os_type); |
| return; |
| } |
| break; |
| |
| case USB_DT_STRING: |
| /* inc nr_str_before_setcfg before scfg is set */ |
| if (!os_detect.has_setconfig) |
| os_detect.nr_str_before_setcfg++; |
| else if (os_detect.nr_str_before_setcfg == 0) { |
| pr_info("return has_scfg =1 & nr_str_before_setcfg == 0\n"); |
| return; |
| } |
| |
| if (HOST_OS_TYPE_UNKNOWN == os_detect.cur_os || |
| HOST_OS_TYPE_WIN7_OR_WIN8 == os_detect.cur_os) { |
| /* do nothing if the 2rd dt is string dt */ |
| if (os_detect.cmd_idx == 2) { |
| return; |
| /* return if the 3rd dt is string dt */ |
| } else if (os_detect.cmd_idx == 3) { |
| os_detect.str_dt_bitmap |= (0x1 << 3); |
| return; |
| } else if ((os_detect.cmd_idx == 4) && |
| ((os_detect.str_dt_bitmap & 0x8)== 0x8) && |
| (os_detect.nr_usb_req < 8)) { |
| if ((os_detect.apple_has_ncm_ecm & (HAS_USB_CDC_NCM)) |
| && (HOST_OS_TYPE_APPLE == os_detect.default_os)) { |
| pr_info("apple stage1 with NCM, TRIG timer\n"); |
| os_type = HOST_OS_TYPE_APPLE_STAGE1; |
| os_detect.cur_os = HOST_OS_TYPE_APPLE_STAGE1; |
| timer_setup(&os_detect.apple_timer, apple_reconfig_timer, 0); |
| os_detect.apple_timer.expires = jiffies + 15 * HZ; |
| add_timer(&os_detect.apple_timer); |
| } else { |
| pr_info("detected APPLE w/o ncm\n"); |
| os_type = HOST_OS_TYPE_APPLE; |
| /* default set as none-APPLE and need ncm |
| * then we do a second stage usb os detection |
| */ |
| if (os_detect.apple_has_ncm_ecm & HAS_USB_CDC_NCM) { |
| pr_info("Trig NCM/ECM detection on APPLE\n"); |
| os_detect.cur_os = HOST_OS_TYPE_APPLE; |
| schedule_work(&os_detect.ncm_ecm_detection_work); |
| os_detect_set_done(); |
| return; |
| } |
| } |
| } else { |
| if (w_value & 0xFF) { |
| os_type = HOST_OS_TYPE_WIN7_OR_WIN8; |
| pr_info("### Host is Win7 Or Win8(w_value = 0x%x)\n", w_value); |
| /* When connect to a new Win7 or Win8, |
| ** the w_value of the first get string |
| ** will equal to 0xEE. |
| ** We do nothing when w_value = 0xEE(android_dev_reconfigure |
| ** will ignore HOST_OS_TYPE_WIN7_OR_WIN8), |
| ** and wait for the next get string command. |
| */ |
| if ((w_value & 0xFF) != 0xEE) { |
| if (os_detect.has_get_bos && os_detect.nr_bos == 1) |
| os_type = HOST_OS_TYPE_WIN8; |
| else |
| os_type = HOST_OS_TYPE_WIN7_WINXP; |
| } |
| } else if ((HOST_OS_TYPE_UNKNOWN == os_detect.cur_os) && |
| (((os_detect.cfg_dt_bitmap & 0x1F) == 0x18) || |
| (((os_detect.cfg_dt_bitmap & 0x6F) == 0x60) && |
| os_detect.has_get_bos))) { |
| pr_info("##config_map: 0x%x\n", os_detect.cfg_dt_bitmap); |
| if (((os_detect.reqlen[0] == 0x40 || os_detect.reqlen[0] == 0x08) && |
| (os_detect.reqlen[1] == 0x12)) && |
| ((((os_detect.cfg_dt_bitmap & 0x1F) == 0x18) && |
| (os_detect.reqlen[2] == 0x9 || os_detect.reqlen[2] == 0x20) && |
| (os_detect.reqlen[3] != 0xff)) || |
| (((os_detect.cfg_dt_bitmap & 0x6F) == 0x60) && |
| (os_detect.reqlen[4] == 0x9 || os_detect.reqlen[4] == 0x20) && |
| (os_detect.reqlen[5] != 0xff)))) { |
| pr_info("linux stage1\n"); |
| os_type = HOST_OS_TYPE_LINUX_STAGE1; |
| os_detect.cur_os = HOST_OS_TYPE_LINUX_STAGE1; |
| timer_setup(&os_detect.linux_timer, linux_reconfig_timer, 0); |
| os_detect.linux_timer.expires = jiffies + 2 * HZ; |
| add_timer(&os_detect.linux_timer); |
| } else { |
| pr_info("reglen[0...5] = [%2x %2x %2x %2x %2x %2x]\n", |
| os_detect.reqlen[0], |
| os_detect.reqlen[1], |
| os_detect.reqlen[2], |
| os_detect.reqlen[3], |
| os_detect.reqlen[4], |
| os_detect.reqlen[5]); |
| } |
| } else { |
| if (os_detect.reqlen[2] == 0x9) |
| os_detect.nr_dreq_before_recfg = |
| NR_DELAY_REQ_BEFORE_RECONFIG; |
| else |
| os_detect.nr_dreq_before_recfg = 0; |
| pr_info("win7/xp: delay %d req\n", |
| os_detect.nr_dreq_before_recfg); |
| if (os_detect.has_get_bos && os_detect.nr_bos == 1 |
| && os_detect.bos_len == 0xff) { |
| pr_info("WIN8-V2\n"); |
| os_detect.cur_os = HOST_OS_TYPE_WIN8; |
| os_type = HOST_OS_TYPE_WIN8; |
| } else { |
| os_detect.cur_os = HOST_OS_TYPE_WIN7_WINXP; |
| os_type = HOST_OS_TYPE_WIN7_WINXP; |
| } |
| /* do re-config if no extra delay needed */ |
| if (0 == os_detect.nr_dreq_before_recfg) |
| android_dev_reconfigure(os_detect.cur_os); |
| return; |
| } |
| } |
| |
| if (os_detect.cur_os != HOST_OS_TYPE_APPLE_STAGE1 && |
| os_detect.cur_os != HOST_OS_TYPE_LINUX_STAGE1 && |
| os_type != HOST_OS_TYPE_UNKNOWN) { |
| android_dev_reconfigure(os_type); |
| return; |
| } |
| } |
| break; |
| case USB_DT_BOS: |
| os_detect.has_get_bos = 1; |
| os_detect.bos_len = ctrl->wLength; /* last one is ok */ |
| os_detect.nr_bos++; |
| break; |
| case USB_DT_CONFIG: |
| if (os_detect.cmd_idx < BITS_PER_LONG) { |
| os_detect.cfg_dt_bitmap |= |
| (0x1 << os_detect.cmd_idx); |
| } |
| break; |
| default: |
| break; |
| } |
| break; |
| |
| case USB_REQ_GET_CONFIGURATION: |
| pr_info("gcfg:cur_os: %d\n", os_detect.cur_os); |
| if ((os_detect.cur_os == HOST_OS_TYPE_APPLE_STAGE1) && |
| (os_detect.apple_has_ncm_ecm & |
| (HAS_USB_CDC_NCM | HAS_USB_CDC_ECM)) && |
| (!os_detect.has_get_bos)) { |
| pr_info("mod timer\n"); |
| mod_timer(&os_detect.apple_timer, (jiffies + 3 * HZ)); |
| return; |
| } |
| break; |
| case USB_REQ_SET_INTERFACE: |
| pr_info("cur_os: %d\n", os_detect.cur_os); |
| if ((os_detect.cur_os == HOST_OS_TYPE_APPLE_STAGE1) && |
| (os_detect.apple_has_ncm_ecm & |
| (HAS_USB_CDC_NCM | HAS_USB_CDC_ECM))) { |
| pr_info("%s:del apple_timer\n", __func__); |
| del_timer(&os_detect.apple_timer); |
| cancel_delayed_work(&os_detect.ncm_guard_work); |
| os_type = HOST_OS_TYPE_APPLE; |
| os_detect.has_setconfig = 0; |
| android_dev_reconfigure(os_type); |
| return; |
| } |
| if (os_detect.cur_os == HOST_OS_TYPE_LINUX_STAGE1) { |
| os_detect.has_set_intf = 1; |
| } |
| break; |
| default: |
| break; |
| } |
| |
| if (USB_DIR_OUT == ctrl->bRequestType |
| && USB_REQ_SET_CONFIGURATION == ctrl->bRequest) { |
| pr_info("meet usb set_config in os-detect\n"); |
| os_detect.has_setconfig = 1; |
| /* trig NCM/ECM detection for linux*/ |
| if ((os_detect.cur_os == HOST_OS_TYPE_LINUX_STAGE1) |
| && (0 != os_detect.nr_str_before_setcfg) |
| && (HOST_OS_TYPE_APPLE != os_detect.default_os) |
| && (os_detect.linux_has_ncm_ecm & HAS_USB_CDC_NCM)) { |
| pr_info("Trig NCM/ECM detection on LINUX\n"); |
| del_timer(&os_detect.linux_timer); |
| os_detect.cur_os = HOST_OS_TYPE_LINUX; |
| schedule_work(&os_detect.ncm_ecm_detection_work); |
| os_detect_set_done(); |
| return; |
| } |
| /* force a enumeration on windows resume from hibernation */ |
| if ((os_detect.nr_usb_req == 3 || os_detect.nr_usb_req == 4) && |
| os_detect.reqlen[0] == 0x40 && |
| os_detect.reqlen[1] == 0x12) { |
| pr_info("####resume from hibernation and re-enumerate\n"); |
| usb_force_reenumerate(); |
| return; |
| } |
| } |
| } |
| |
| void usb_os_restore(void) |
| { |
| struct android_dev *dev = _android_dev; |
| char *os; |
| |
| pr_info("%s\n", __func__); |
| os_detect.default_os = os_detect.bankup_default_os; |
| strcpy(os_detect.win7, os_detect.win7_bankup); |
| strcpy(os_detect.win8, os_detect.win8_bankup); |
| strcpy(os_detect.os_linux, os_detect.os_linux_bankup); |
| strcpy(os_detect.apple, os_detect.apple_bankup); |
| set_ncm_ecm_flag(); |
| |
| if (os_detect.default_os == HOST_OS_TYPE_WIN7_WINXP) |
| os = "win7"; |
| else if (os_detect.default_os == HOST_OS_TYPE_WIN8) |
| os = "win8"; |
| else if (os_detect.default_os == HOST_OS_TYPE_APPLE) |
| os = "apple"; |
| else if (os_detect.default_os == HOST_OS_TYPE_LINUX) |
| os = "linux"; |
| else |
| BUG(); |
| |
| if (os_detect.apple_has_ncm_ecm & HAS_USB_CDC_NCM) |
| set_cdc_ncm(os_detect.apple); |
| if (os_detect.linux_has_ncm_ecm & HAS_USB_CDC_NCM) |
| set_cdc_ncm(os_detect.os_linux); |
| |
| __os_store(dev, os, strlen(os)); |
| os_detect_struct_clear(); |
| } |
| |
| /* this should be canceled before the close the usb function */ |
| void cancel_reconfigure_work(void) |
| { |
| pr_info("%s:del timer\n", __func__); |
| del_timer(&os_detect.linux_timer); |
| del_timer(&os_detect.apple_timer); |
| pr_info("done\n"); |
| cancel_work_sync(&os_detect.restart_work); |
| cancel_delayed_work(&os_detect.ncm_guard_work); |
| cancel_work_sync(&os_detect.reconfigure_work); |
| cancel_work_sync(&os_detect.ncm_ecm_detection_work); |
| |
| if (old_max_speed != 0) { |
| _android_dev->cdev->gadget->max_speed = old_max_speed; |
| /* restore bcdUSB default value to 0x0200 */ |
| _android_dev->cdev->desc.bcdUSB = cpu_to_le16(0x0200); |
| old_max_speed = 0; |
| } |
| } |
| |
| void usb_os_detect_init(void) |
| { |
| INIT_WORK(&os_detect.reconfigure_work, |
| android_reconfigure_work); |
| INIT_WORK(&os_detect.restart_work, |
| android_restart_work); |
| INIT_DELAYED_WORK(&os_detect.ncm_guard_work, |
| ncm_guard_work); |
| INIT_WORK(&os_detect.ncm_ecm_detection_work, |
| ncm_ecm_detection_work); |
| |
| os_detect.default_os = HOST_OS_TYPE_UNKNOWN; |
| os_detect.bankup_default_os = HOST_OS_TYPE_UNKNOWN; |
| timer_setup(&os_detect.linux_timer, linux_reconfig_timer, 0); |
| timer_setup(&os_detect.apple_timer, apple_reconfig_timer, 0); |
| os_detect_struct_clear(); |
| } |
| |
| bool host_os_is_win8(void) |
| { |
| return (os_detect.cur_os == HOST_OS_TYPE_WIN8); |
| } |
| |
| bool host_os_is_win7(void) |
| { |
| return (os_detect.cur_os == |
| HOST_OS_TYPE_WIN7_WINXP); |
| } |
| |
| bool host_os_is_linux(void) |
| { |
| return (os_detect.cur_os == HOST_OS_TYPE_LINUX); |
| } |
| |
| bool host_os_is_apple(void) |
| { |
| return (os_detect.cur_os == HOST_OS_TYPE_APPLE); |
| } |
| |
| bool host_os_is_unknown(void) |
| { |
| return (os_detect.cur_os == HOST_OS_TYPE_UNKNOWN); |
| } |
| |
| void usb_os_detect_reset_state(void) |
| { |
| /* |
| * os detection is not done and at least recvd more than 2 |
| * requests,reset other states receive more than 2 requests |
| */ |
| pr_info("RState-OS: %d%s,cmdidx: %d,NrStrNosConfig: %d," |
| "has_setconfig %d, reqlen[0]: %d\n", |
| os_detect.cur_os, |
| os_type_name(os_detect.cur_os), |
| os_detect.cmd_idx, |
| os_detect.nr_str_before_setcfg, |
| os_detect.has_setconfig, |
| os_detect.reqlen[0]); |
| |
| /* bios case */ |
| if ((os_detect.cur_os == HOST_OS_TYPE_UNKNOWN && os_detect.cmd_idx > 2) || |
| (os_detect.cur_os != HOST_OS_TYPE_UNKNOWN && |
| ((os_detect.has_setconfig && os_detect.nr_str_before_setcfg == 0) || |
| (os_detect.reqlen[0] != 0x40 && os_detect.reqlen[0] != 0x12)))) { |
| /* special case for some linux u3 host */ |
| if ((os_detect.cur_os == HOST_OS_TYPE_LINUX) && |
| (os_detect.reqlen[0] == 0x8) && |
| (os_detect.nr_str_before_setcfg > 0)) { |
| pr_info("linux usb3 host, don't reset state\n"); |
| return; |
| } |
| |
| pr_info("##Clear os_detect\n"); |
| os_detect.default_os = os_detect.bankup_default_os; |
| os_detect_struct_clear(); |
| } |
| } |
| |
| void usb_function_set_stage2(void) |
| { |
| struct android_dev *dev = _android_dev; |
| char *os; |
| |
| pr_info("Default OS: %s, Current OS: %s\n", |
| os_type_name(os_detect.default_os), |
| os_type_name(os_detect.cur_os)); |
| if (in_irq()) |
| os_detect_set_done(); |
| |
| strcpy(os_detect.win7, os_detect.win7_stage2); |
| strcpy(os_detect.win8, os_detect.win8_stage2); |
| strcpy(os_detect.apple, os_detect.apple_stage2); |
| strcpy(os_detect.os_linux, os_detect.linux_stage2); |
| |
| set_ncm_ecm_flag(); |
| |
| if (((HOST_OS_TYPE_APPLE == os_detect.cur_os) && |
| (os_detect.apple_has_ncm_ecm & HAS_USB_CDC_NCM)) || |
| ((HOST_OS_TYPE_LINUX == os_detect.cur_os) && |
| (os_detect.linux_has_ncm_ecm & HAS_USB_CDC_NCM))) { |
| pr_info("restore os detect\n"); |
| old_max_speed = 0; |
| if (os_detect.default_os == HOST_OS_TYPE_WIN7_WINXP) |
| os = "win7"; |
| else if (os_detect.default_os == HOST_OS_TYPE_WIN8) |
| os = "win8"; |
| else if (os_detect.default_os == HOST_OS_TYPE_APPLE) |
| os = "apple"; |
| else if (os_detect.default_os == HOST_OS_TYPE_LINUX) |
| os = "linux"; |
| else |
| BUG(); |
| __os_store(dev, os, strlen(os)); |
| os_detect.default_os = os_detect.bankup_default_os; |
| os_detect_struct_clear(); |
| } |
| |
| schedule_work(&os_detect.reconfigure_work); |
| } |
| |
| #if defined (CONFIG_USB_G_MBIM) |
| bool is_mbim_enabled(void) |
| { |
| return mbim_enabled; |
| } |
| #endif |
| |
| static bool should_reset_os_detect(void) |
| { |
| s64 ktime_now; |
| |
| ktime_now = ktime_to_ms(ktime_get()); |
| |
| if (os_detect.reset_intf_time_ms != 0) { |
| if ((ktime_now - os_detect.reset_intf_time_ms) < |
| KTIME_MS_BETWEEN_RSTINF_AND_SUSPEND) { |
| os_detect.reset_intf_time_ms = 0; |
| return true; |
| } else { |
| os_detect.reset_intf_time_ms = 0; |
| return false; |
| } |
| } |
| |
| return false; |
| } |
| |
| /* |
| * called from interrupt context |
| */ |
| void invoke_os_detect_restore(void) |
| { |
| if (!should_reset_os_detect()) { |
| pr_info("no recent reset intf detected\n"); |
| } |
| |
| if (HOST_OS_TYPE_APPLE == os_detect.cur_os |
| || HOST_OS_TYPE_APPLE_STAGE1== os_detect.cur_os) { |
| pr_info("trigger os detect restore\n"); |
| cancel_delayed_work(&os_detect.ncm_guard_work); |
| cancel_work_sync(&os_detect.ncm_ecm_detection_work); |
| del_timer(&os_detect.apple_timer); |
| pr_info("%s: del timer done\n", __func__); |
| schedule_work(&os_detect.restart_work); |
| } |
| } |
| |