#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if IS_ENABLED(CONFIG_USE_S2M_PDIC) #include #endif #if IS_ENABLED(CONFIG_CCIC_NOTIFIER) #include #endif struct ifconn_manager_data *ifconn_manage; int ifconn_manager_get_muic_cable_type(void) { return ifconn_manage->muic_cable_type; } int ifconn_manager_get_pd_con_state(void) { return ifconn_manage->pd_con_state; } static int _ifconn_manager_template_notify(struct ifconn_notifier_template *template) { int ret; if (template == NULL) { pr_info("%s: err, template is NULL\n", __func__); return -1; } ret = ifconn_notifier_notify(template->src, template->dest, template->id, template->event, IFCONN_NOTIFY_PARAM_TEMPLATE, template); if (ret < 0) pr_err("%s: Fail to send noti\n", __func__); return ret; } static void ifconn_manager_noti_work(struct ifconn_manager_data *ifconn_manager, struct ifconn_notifier_template *template) { pr_info("%s: enter, template : %p\n", __func__, template); mutex_lock(&ifconn_manager->noti_mutex); template->up_src = template->src; template->src = IFCONN_NOTIFY_MANAGER; switch (template->up_src) { case IFCONN_NOTIFY_MUIC: if (ifconn_manager->ccic_drp_state == IFCONN_MANAGER_USB_STATUS_DETACH) { if (template->attach && ifconn_manager->vbus_state == IFCONN_MANAGER_VBUS_STATUS_HIGH) { msleep(2000); template->drp = IFCONN_MANAGER_USB_STATUS_ATTACH_UFP; ifconn_manager->ccic_drp_state = IFCONN_MANAGER_USB_STATUS_ATTACH_UFP; } else { template->drp = IFCONN_MANAGER_USB_STATUS_DETACH; ifconn_manager->ccic_drp_state = IFCONN_MANAGER_USB_STATUS_DETACH; } template->sub3 = 0; template->data = NULL; } template->src = IFCONN_NOTIFY_MANAGER_MUIC; _ifconn_manager_template_notify(template); break; case IFCONN_NOTIFY_CCIC: pr_info("%s: dbg, line : %d\n", __func__, __LINE__); if ((ifconn_manager->ccic_drp_state != IFCONN_MANAGER_USB_STATUS_ATTACH_UFP) || ifconn_manager->is_UFPS) { pr_info("usb: [M] %s: skip case\n", __func__); goto EXIT_NOTI_WORK; } //template->src = IFCONN_NOTIFY_MANAGER; template->src = IFCONN_NOTIFY_MANAGER_PDIC; template->data = NULL; template->id = IFCONN_NOTIFY_ID_USB; pr_info("usb: [M] %s: usb=%d, pd=%d\n", __func__, ifconn_manager->usb_enum_state, ifconn_manager->pd_con_state); if (!ifconn_manager->usb_enum_state || (ifconn_manager->muic_data_refresh && ifconn_manager->cable_type == IFCONN_CABLE_TYPE_MUIC_CHARGER)) { /* TA cable Type */ template->dest = IFCONN_NOTIFY_USB; template->attach = IFCONN_NOTIFY_DETACH; template->drp = IFCONN_MANAGER_USB_STATUS_DETACH; template->sub3 = 0; } else { /* USB cable Type */ template->dest = IFCONN_NOTIFY_BATTERY; template->attach = 0; template->rprd = 0; template->cable_type = IFCONN_PD_USB_TYPE; } _ifconn_manager_template_notify(template); break; case IFCONN_NOTIFY_PDIC: pr_info("%s: pdic case, up_src : %d\n", __func__, template->up_src); //template->src = IFCONN_NOTIFY_MANAGER; template->src = IFCONN_NOTIFY_MANAGER_PDIC; template->dest = IFCONN_NOTIFY_BATTERY; template->id = IFCONN_NOTIFY_ID_POWER_STATUS; template->event = IFCONN_NOTIFY_EVENT_ATTACH; _ifconn_manager_template_notify(template); break; default: break; } EXIT_NOTI_WORK: mutex_unlock(&ifconn_manager->noti_mutex); } static int ifconn_manager_manage_muic(struct ifconn_manager_data *ifconn_manager, struct ifconn_notifier_template *template) { if (ifconn_manager == NULL || template == NULL) return -1; ifconn_manager->muic_action = template->attach; ifconn_manager->muic_cable_type = template->cable_type; ifconn_manager->muic_data_refresh = 1; pr_info("%s, usb: [M] %s: cable type : (%d), id : %d\n", __func__, __func__, template->cable_type, template->id); if (template->id == IFCONN_NOTIFY_ID_ATTACH) { switch (template->cable_type) { case ATTACHED_DEV_USB_MUIC: case ATTACHED_DEV_CDP_MUIC: case ATTACHED_DEV_UNOFFICIAL_ID_USB_MUIC: case ATTACHED_DEV_UNOFFICIAL_ID_CDP_MUIC: case ATTACHED_DEV_JIG_USB_OFF_MUIC: case ATTACHED_DEV_JIG_USB_ON_MUIC: if (template->attach) ifconn_manager->cable_type = IFCONN_CABLE_TYPE_MUIC_USB; template->dest = IFCONN_NOTIFY_USB; template->id = IFCONN_NOTIFY_ID_USB; ifconn_manager_noti_work(ifconn_manager, template); break; case ATTACHED_DEV_TA_MUIC: pr_info("usb: [M] %s: TA(%d) %s\n", __func__, template->cable_type, template->attach ? "Attached" : "Detached"); if (ifconn_manager->muic_action) ifconn_manager->cable_type = IFCONN_CABLE_TYPE_MUIC_CHARGER; if (template->attach && ifconn_manager->ccic_drp_state == IFCONN_MANAGER_USB_STATUS_ATTACH_UFP) { /* Turn off the USB Phy when connected to the charger */ template->src = IFCONN_NOTIFY_MUIC; template->dest = IFCONN_NOTIFY_USB; template->id = IFCONN_NOTIFY_ID_USB; template->attach = IFCONN_NOTIFY_DETACH; template->drp = IFCONN_NOTIFY_EVENT_DETACH; template->sub3 = 0; template->data = NULL; ifconn_manager_noti_work(ifconn_manager, template); } break; case ATTACHED_DEV_AFC_CHARGER_PREPARE_MUIC: case ATTACHED_DEV_QC_CHARGER_PREPARE_MUIC: pr_info("usb: [M] %s: AFC or QC Prepare(%d) %s\n", __func__, template->cable_type, template->attach ? "Attached" : "Detached"); break; case ATTACHED_DEV_TIMEOUT_OPEN_MUIC: pr_info ("usb: [M] %s: DCD Timeout device is detected(%d) %s\n", __func__, template->cable_type, template->attach ? "Attached" : "Detached"); if (ifconn_manager->muic_action) { ifconn_manager->cable_type = IFCONN_CABLE_TYPE_MUIC_TIMEOUT_OPEN_DEVICE; } break; default: break; } template->id = IFCONN_NOTIFY_ID_ATTACH; template->src = IFCONN_NOTIFY_MUIC; template->dest = IFCONN_NOTIFY_BATTERY; ifconn_manager_noti_work(ifconn_manager, template); } else if (template->id == IFCONN_NOTIFY_ID_DETACH) { template->src = IFCONN_NOTIFY_MUIC; template->dest = IFCONN_NOTIFY_BATTERY; ifconn_manager_noti_work(ifconn_manager, template); } return 0; } static int ifconn_manager_manage_battery(struct ifconn_manager_data *ifconn_manager, struct ifconn_notifier_template *template) { int ret; if (ifconn_manager == NULL || template == NULL) return -1; switch (template->id) { case IFCONN_NOTIFY_ID_SELECT_PDO: ret = ifconn_notifier_notify(IFCONN_NOTIFY_MANAGER, IFCONN_NOTIFY_CCIC, template->id, template->event, IFCONN_NOTIFY_PARAM_TEMPLATE, template); break; default: break; } return 0; } static int ifconn_manager_manage_pdic(struct ifconn_manager_data *ifconn_manager, struct ifconn_notifier_template *template) { if (ifconn_manager == NULL || template == NULL) return -1; pr_info("%s, usb: [M] %s: cable type : (%d), id : %d\n", __func__, __func__, template->cable_type, template->id); switch (template->id) { case IFCONN_NOTIFY_ID_POWER_STATUS: if (template->attach || template->event == IFCONN_NOTIFY_EVENT_ATTACH) { ifconn_manager->pd_con_state = 1; if ((ifconn_manager->ccic_drp_state == IFCONN_MANAGER_USB_STATUS_ATTACH_UFP) && !ifconn_manager->is_UFPS) { pr_info("usb: [M] %s: PD charger + UFP\n", __func__); } } template->dest = IFCONN_NOTIFY_BATTERY; if (ifconn_manager->pd == NULL) ifconn_manager->pd = template->data; ifconn_manager_noti_work(ifconn_manager, template); break; case IFCONN_NOTIFY_ID_DP_CONNECT: template->sub1 = template->event; template->src = IFCONN_NOTIFY_MANAGER; template->dest = IFCONN_NOTIFY_DP; _ifconn_manager_template_notify(template); break; case IFCONN_NOTIFY_ID_DP_HPD: #if IS_ENABLED(CONFIG_USE_S2M_PDIC) template->sub1 = ((struct usbpd_manager_data *)template->data)->hpd; template->sub2 = ((struct usbpd_manager_data *)template->data)->hpdirq; #endif template->src = IFCONN_NOTIFY_MANAGER; template->dest = IFCONN_NOTIFY_DP; _ifconn_manager_template_notify(template); break; case IFCONN_NOTIFY_ID_DP_LINK_CONF: #if IS_ENABLED(CONFIG_USE_S2M_PDIC) template->sub1 = ((struct usbpd_manager_data *)template->data)->dp_selected_pin; #endif template->src = IFCONN_NOTIFY_MANAGER; template->dest = IFCONN_NOTIFY_DP; _ifconn_manager_template_notify(template); break; default: break; } return 0; } static int ifconn_manager_manage_ccic(struct ifconn_manager_data *ifconn_manager, struct ifconn_notifier_template *template) { if (ifconn_manager == NULL || template == NULL) return -1; switch (template->id) { case IFCONN_NOTIFY_ID_ATTACH: if (ifconn_manager->ccic_attach_state != template->sub1) { /*attach */ ifconn_manager->ccic_attach_state = template->sub1; ifconn_manager->muic_data_refresh = 0; ifconn_manager->is_UFPS = 0; if (ifconn_manager->ccic_attach_state == IFCONN_NOTIFY_ATTACH) { pr_info("usb: [M] %s: IFCONN_NOTIFY_ATTACH\n", __func__); ifconn_manager->water_det = 0; ifconn_manager->pd_con_state = 0; } else { /* IFCONN_NOTIFY_DETACH */ pr_info ("usb: [M] %s: IFCONN_NOTIFY_DETACH (pd=%d, cable_type=%d)\n", __func__, ifconn_manager->pd_con_state, ifconn_manager->cable_type); if (ifconn_manager->pd_con_state) { ifconn_manager->pd_con_state = 0; template->src = IFCONN_NOTIFY_CCIC; template->dest = IFCONN_NOTIFY_BATTERY; template->id = IFCONN_NOTIFY_ID_ATTACH; template->attach = IFCONN_NOTIFY_DETACH; template->rprd = 0; template->cable_type = ATTACHED_DEV_UNOFFICIAL_ID_ANY_MUIC; template->data = NULL; } } } break; case IFCONN_NOTIFY_ID_DETACH: template->src = IFCONN_NOTIFY_MANAGER; template->dest = IFCONN_NOTIFY_BATTERY; _ifconn_manager_template_notify(template); return 0; case IFCONN_NOTIFY_ID_RID: ifconn_manager->ccic_rid_state = template->sub1; break; case IFCONN_NOTIFY_ID_USB: if ((ifconn_manager->cable_type == IFCONN_CABLE_TYPE_MUIC_CHARGER) || (template->sub2 != IFCONN_MANAGER_USB_STATUS_DETACH /*drp */ && (ifconn_manager->ccic_rid_state == IFCONN_MANAGER_RID_523K || ifconn_manager->ccic_rid_state == IFCONN_MANAGER_RID_619K))) { return 0; } break; case IFCONN_NOTIFY_ID_WATER: if (template->sub1) { /* attach */ #if IS_ENABLED(CONFIG_IFCONN_WATER_EVENT_ENABLE) if (!ifconn_manager->water_det) { ifconn_manager->water_det = 1; ifconn_manager->water_count++; complete(&ccic_attach_done); template->src = IFCONN_NOTIFY_CCIC; template->dest = IFCONN_NOTIFY_MUIC; template->id = IFCONN_NOTIFY_ID_WATER; template->attach = IFCONN_NOTIFY_ATTACH; template->rprd = 0; template->cable_type = 0; template->pd = NULL; ifconn_manager_noti_work(ifconn_manager, template); /*update water time */ water_dry_time_update((int)template->sub1); if (ifconn_manager->muic_action == IFCONN_NOTIFY_ATTACH) { template->sub3 = ATTACHED_DEV_UNDEFINED_RANGE_MUIC; /* cable_type */ } else { /* If the cable is not connected, skip the battery event. */ return 0; } } else { /* Ignore duplicate events */ return 0; } #else if (!ifconn_manager->water_det) { ifconn_manager->water_det = 1; if (ifconn_manager->muic_action == IFCONN_NOTIFY_ATTACH) { template->src = IFCONN_NOTIFY_CCIC; template->dest = IFCONN_NOTIFY_BATTERY; template->id = IFCONN_NOTIFY_ID_ATTACH; template->attach = IFCONN_NOTIFY_DETACH; template->rprd = 0; template->cable_type = ifconn_manager->muic_cable_type; template->data = NULL; ifconn_manager_noti_work(ifconn_manager, template); } } return 0; #endif } else { ifconn_manager->water_det = 0; ifconn_manager->dry_count++; return 0; } break; default: break; } ifconn_manager_noti_work(ifconn_manager, template); return 0; } static int ifconn_manager_manage_vbus(struct ifconn_manager_data *ifconn_manager, struct ifconn_notifier_template *template) { ifconn_notifier_vbus_t vbus_type = template->vbus_type; pr_info("usb: [M] %s: vbus_type=%s, WATER DET=%d ATTACH=%s (%d)\n", __func__, vbus_type == IFCONN_NOTIFY_VBUS_HIGH ? "HIGH" : "LOW", ifconn_manager->water_det, ifconn_manager->ccic_attach_state == IFCONN_NOTIFY_ATTACH ? "ATTACH" : "DETATCH", ifconn_manager->muic_attach_state_without_ccic); ifconn_manager->vbus_state = vbus_type; #if IS_ENABLED(CONFIG_IFCONN_WATER_EVENT_ENABLE) init_completion(&ccic_attach_done); if ((ifconn_manager->water_det == 1) && (template->vbus_type == IFCONN_NOTIFY_VBUS_HIGH)) wait_for_completion_timeout(&ccic_attach_done, msecs_to_jiffies(2000)); switch (vbus_type) { case IFCONN_NOTIFY_VBUS_HIGH: if (ifconn_manager->water_det) { ifconn_manager->waterChg_count++; template->src = IFCONN_NOTIFY_CCIC; template->dest = IFCONN_NOTIFY_BATTERY; template->id = IFCONN_NOTIFY_ID_WATER; template->attach = IFCONN_NOTIFY_ATTACH; template->rprd = 0; template->cable_type = ATTACHED_DEV_UNDEFINED_RANGE_MUIC; template->data = NULL; _ifconn_manager_template_notify(template); } break; case IFCONN_NOTIFY_VBUS_LOW: if (ifconn_manager->water_det) { template->src = IFCONN_NOTIFY_CCIC; template->dest = IFCONN_NOTIFY_BATTERY; template->id = IFCONN_NOTIFY_ID_ATTACH; template->attach = IFCONN_NOTIFY_DETACH; template->rprd = 0; template->cable_type = ATTACHED_DEV_UNDEFINED_RANGE_MUIC; template->data = NULL; _ifconn_manager_template_notify(template); } break; default: break; } #endif return 0; } static int ifconn_manager_parse_dt(struct ifconn_manager_data *ifconn_manager) { struct device_node *np = ifconn_manager->dev->of_node; struct ifconn_manager_platform_data *pdata = ifconn_manager->pdata; int ret = 0; if (!np) return -1; ret = of_property_read_string(np, "ifconn,usbpd", (char const **)&pdata->usbpd_name); if (ret < 0) goto err; ret = of_property_read_string(np, "ifconn,muic", (char const **)&pdata->muic_name); err: return ret; } #if IS_ENABLED(CONFIG_IFCONN_SUPPORT_THREAD) static struct ifconn_notifier_template *_ifconn_manager_dequeue_template (struct ifconn_manager_data *ifconn_manager) { struct ifconn_notifier_template *target = NULL; struct ifconn_manager_template *step = NULL; if (ifconn_manager->hp) { pr_info("%s: ifconn_manager->hp : %p!!!!!\n", __func__, ifconn_manager->hp); pr_info("%s: ifconn_manager->hp->node : %p!!!!!\n", __func__, &ifconn_manager->hp->node); _ifconn_show_attr(&ifconn_manager->hp->node); if (ifconn_manager->hp == ifconn_manager->tp) step = NULL; else step = (struct ifconn_manager_template *)ifconn_manager->hp->np; target = &ifconn_manager->hp->node; //kfree(ifconn_manager->hp); ifconn_manager->hp = step; ifconn_manager->template_cnt--; } pr_info("%s: target : %p!!!!!\n", __func__, target); _ifconn_show_attr(target); return target; } static void _ifconn_manager_enqueue_template (struct ifconn_manager_data *ifconn_manager, struct ifconn_notifier_template *template) { struct ifconn_manager_template *template_ifc = NULL; template_ifc = kzalloc(sizeof(struct ifconn_manager_template), GFP_KERNEL); pr_info("%s: enter --- line : %d!!!!!\n", __func__, __LINE__); memcpy(&template_ifc->node, template, sizeof(struct ifconn_notifier_template)); if (template->data != NULL) { switch (template->id) { case IFCONN_NOTIFY_ID_POWER_STATUS: memcpy(template_ifc->node.data, template->data, sizeof(usbpd_pdic_sink_state_t)); break; case IFCONN_NOTIFY_ID_DP_CONNECT: case IFCONN_NOTIFY_ID_DP_HPD: case IFCONN_NOTIFY_ID_DP_LINK_CONF: memcpy(template_ifc->node.data, template->data, sizeof(struct usbpd_info)); break; default: break; } } if (ifconn_manager->hp == NULL) { ifconn_manager->tp = ifconn_manager->hp = template_ifc; ifconn_manager->template_cnt++; } else { if (ifconn_manager->template_cnt < TEMPLATE_MAX) ifconn_manager->template_cnt++; else _ifconn_manager_dequeue_template(ifconn_manager); template_ifc->rp = (void *)ifconn_manager->tp; ifconn_manager->tp->np = (void *)template_ifc; ifconn_manager->tp = template_ifc; } pr_info("%s: hp : %p!!!!!\n", __func__, ifconn_manager->hp); pr_info("%s: tp : %p!!!!!\n", __func__, ifconn_manager->tp); pr_info("%s: ifconn_manager->hp->node : %p!!!!!\n", __func__, &ifconn_manager->hp->node); _ifconn_show_attr(&ifconn_manager->hp->node); _ifconn_show_attr(&ifconn_manager->tp->node); } #endif static void ifconn_manager_work(struct work_struct *work) { struct ifconn_manager_data *ifconn_manager = container_of(work, struct ifconn_manager_data, noti_work); struct ifconn_notifier_template *template = NULL; pr_info("%s: enter --- line : %d!!!!!\n", __func__, __LINE__); template = ifconn_manager->template; #if IS_ENABLED(CONFIG_IFCONN_SUPPORT_THREAD) if (ifconn_manager->template != NULL) template = ifconn_manager->template; else template = _ifconn_manager_dequeue_template(ifconn_manager); pr_info("%s: enter line : %d!!!!!\n", __func__, __LINE__); #endif switch (template->src) { case IFCONN_NOTIFY_PDIC: ifconn_manager_manage_pdic(ifconn_manager, template); break; case IFCONN_NOTIFY_MUIC: ifconn_manager_manage_muic(ifconn_manager, template); break; case IFCONN_NOTIFY_CCIC: ifconn_manager_manage_ccic(ifconn_manager, template); break; case IFCONN_NOTIFY_VBUS: ifconn_manager_manage_vbus(ifconn_manager, template); break; case IFCONN_NOTIFY_BATTERY: ifconn_manager_manage_battery(ifconn_manager, template); break; case IFCONN_NOTIFY_DP: break; case IFCONN_NOTIFY_USB_DP: break; default: break; } } static int ifconn_manager_isr(struct notifier_block *nb, unsigned long action, void *data) { struct ifconn_manager_data *ifconn_manager = container_of(nb, struct ifconn_manager_data, nb); struct ifconn_notifier_template *template = (struct ifconn_notifier_template *)data; if (data == NULL) { pr_info("%s: data NULL\n", __func__); return -1; } pr_info("%s: enter src : %d\n", __func__, template->src); #if IS_ENABLED(CONFIG_IFCONN_SUPPORT_THREAD) mutex_lock(&ifconn_manager->enqueue_mutex); ifconn_manager->template = NULL; if (template->event == IFCONN_NOTIFY_EVENT_IN_HURRY) { #endif ifconn_manager->template = template; ifconn_manager_work(&ifconn_manager->noti_work); #if IS_ENABLED(CONFIG_IFCONN_SUPPORT_THREAD) } else { pr_info("%s: enter src : %d, line : %d!!!!!\n", __func__, template->src, __LINE__); _ifconn_manager_enqueue_template(ifconn_manager, template); schedule_work(&ifconn_manager->noti_work); } mutex_unlock(&ifconn_manager->enqueue_mutex); #endif return 0; } static int ifconn_manager_probe(struct platform_device *pdev) { struct ifconn_manager_data *ifconn_manager; struct ifconn_manager_platform_data *pdata = NULL; int ret = 0, i = 0; pr_info("%s: enter\n", __func__); ifconn_manager = kzalloc(sizeof(*ifconn_manager), GFP_KERNEL); if (!ifconn_manager) return -ENOMEM; ifconn_manager->dev = &pdev->dev; dev_set_drvdata(ifconn_manager->dev, ifconn_manager); if (pdev->dev.of_node) { pdata = devm_kzalloc(&pdev->dev, sizeof(struct ifconn_manager_platform_data), GFP_KERNEL); if (!pdata) { dev_err(&pdev->dev, "Failed to allocate memory\n"); ret = -ENOMEM; goto ERROR_PDATA_FREE; } ifconn_manager->pdata = pdata; if (ifconn_manager_parse_dt(ifconn_manager)) { dev_err(&pdev->dev, "%s: Failed to get ifconn manager dt\n", __func__); ret = -EINVAL; goto ERROR_IFCONN_FREE; } } else { pdata = dev_get_platdata(&pdev->dev); ifconn_manager->pdata = pdata; } if (ret < 0) goto ERROR_PDATA_FREE; mutex_init(&ifconn_manager->noti_mutex); mutex_init(&ifconn_manager->workqueue_mutex); #if IS_ENABLED(CONFIG_IFCONN_SUPPORT_THREAD) mutex_init(&ifconn_manager->enqueue_mutex); INIT_WORK(&ifconn_manager->noti_work, ifconn_manager_work); ifconn_manager->hp = NULL; ifconn_manager->tp = NULL; ifconn_manager->template = NULL; ifconn_manager->template_cnt = 0; #endif for (i = IFCONN_NOTIFY_USB; i <= IFCONN_NOTIFY_USB_DP; i++) { ret = ifconn_notifier_register(&ifconn_manager->nb, ifconn_manager_isr, IFCONN_NOTIFY_MANAGER, i); } ifconn_manage = ifconn_manager; return 0; ERROR_PDATA_FREE: kfree(pdata); ERROR_IFCONN_FREE: kfree(ifconn_manager); return ret; } static int ifconn_manager_remove(struct platform_device *pdev) { struct ifconn_manager_data *ifconn_manager = platform_get_drvdata(pdev); mutex_destroy(&ifconn_manager->noti_mutex); return 0; } static void ifconn_manager_shutdown(struct device *dev) { } #if IS_ENABLED(CONFIG_OF) static struct of_device_id ifconn_manager_dt_ids[] = { {.compatible = "samsung,ifconn"}, {} }; MODULE_DEVICE_TABLE(of, ifconn_manager_dt_ids); #endif /* CONFIG_OF */ static struct platform_driver ifconn_manager_driver = { .driver = { .name = "ifconn", .owner = THIS_MODULE, .shutdown = ifconn_manager_shutdown, #if IS_ENABLED(CONFIG_OF) .of_match_table = ifconn_manager_dt_ids, #endif }, .probe = ifconn_manager_probe, .remove = ifconn_manager_remove, }; static int __init ifconn_manager_init(void) { pr_info("%s, ++\n", __func__); return platform_driver_register(&ifconn_manager_driver); } static void __exit ifconn_manager_exit(void) { platform_driver_unregister(&ifconn_manager_driver); } late_initcall(ifconn_manager_init); module_exit(ifconn_manager_exit); MODULE_DESCRIPTION("ifconn manager"); MODULE_AUTHOR("Samsung Electronics"); MODULE_LICENSE("GPL");