4 * Copyright (c) 2016 Samsung Electronics Co., Ltd.
6 * Licensed under the Apache License, Version 2.0 (the License);
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
22 #include <eventsystem.h>
23 #include <hal/device/hal-usb_gadget-interface.h>
26 #include "apps/apps.h"
27 #include "extcon/extcon.h"
30 #include "usb-debug.h"
32 static int noti_id = -1;
34 static unsigned int usb_current_mode = USB_FUNCTION_NONE;
35 static unsigned int usb_selected_mode = USB_FUNCTION_NONE;
37 static extcon_usb_state_e usb_connection = USB_DISCONNECTED;
41 * You must keep the order for the specific items.
43 * There may be several mode_v for a mode.
44 * In this case, the representative value should be placed on top.
46 * In case of SET_USB_SDB_DIAG and SET_USB_DIAG_SDB, the SET_USB_SDB_DIAG should be above.
47 * As another example, SET_USB_RNDIS_SDB should be at the top of them.
49 static const struct _usb_mode_mapping_table {
50 int mode_v; /* Integer defined by vconf */
51 unsigned int mode; /* Bitmap of usb function combination */
52 } usb_mode_mapping_table[] = {
53 /* Hack for performance. In most cases this is the value. */
54 {SET_USB_DEFAULT, USB_FUNCTION_MTP | USB_FUNCTION_ACM},
55 {SET_USB_SDB, USB_FUNCTION_MTP | USB_FUNCTION_ACM | USB_FUNCTION_SDB},
57 {SET_USB_NONE, USB_FUNCTION_NONE},
58 {SET_USB_RNDIS, USB_FUNCTION_RNDIS},
59 {SET_USB_RNDIS_DIAG, USB_FUNCTION_RNDIS | USB_FUNCTION_DIAG},
60 {SET_USB_DIAG_RMNET, USB_FUNCTION_DIAG | USB_FUNCTION_RMNET},
61 {SET_USB_ACM_SDB_DM, USB_FUNCTION_ACM | USB_FUNCTION_SDB | USB_FUNCTION_DM},
63 {SET_USB_SDB_DIAG, USB_FUNCTION_MTP | USB_FUNCTION_ACM | USB_FUNCTION_SDB | USB_FUNCTION_DIAG},
64 {SET_USB_DIAG_SDB, USB_FUNCTION_MTP | USB_FUNCTION_ACM | USB_FUNCTION_SDB | USB_FUNCTION_DIAG},
66 {SET_USB_RNDIS_SDB, USB_FUNCTION_ACM | USB_FUNCTION_SDB | USB_FUNCTION_RNDIS},
67 {SET_USB_RNDIS_TETHERING, USB_FUNCTION_ACM | USB_FUNCTION_SDB | USB_FUNCTION_RNDIS},
68 {SET_USB_RNDIS_SDB_ACM, USB_FUNCTION_ACM | USB_FUNCTION_SDB | USB_FUNCTION_RNDIS},
71 unsigned int get_mode_bitmap_from_vconf(int mode_v)
74 int array_len = sizeof(usb_mode_mapping_table)/sizeof(usb_mode_mapping_table[0]);
76 for (i = 0; i < array_len; i++) {
77 if (usb_mode_mapping_table[i].mode_v == mode_v)
78 return usb_mode_mapping_table[i].mode;
81 return USB_FUNCTION_INVALID;
84 static int get_mode_vconf_from_bitmap(unsigned int mode)
87 int array_len = sizeof(usb_mode_mapping_table)/sizeof(usb_mode_mapping_table[0]);
89 /* Caution: The order to search the usb_mode_mapping_table must start with a low index. */
90 for (i = 0; i < array_len; i++) {
91 if (usb_mode_mapping_table[i].mode == mode)
92 return usb_mode_mapping_table[i].mode_v;
95 return SET_USB_INVALID;
98 static void usb_state_send_system_event(int status)
104 case VCONFKEY_SYSMAN_USB_DISCONNECTED:
105 str = EVT_VAL_USB_DISCONNECTED;
108 case VCONFKEY_SYSMAN_USB_CONNECTED:
109 str = EVT_VAL_USB_CONNECTED;
112 case VCONFKEY_SYSMAN_USB_AVAILABLE:
113 str = EVT_VAL_USB_AVAILABLE;
120 _I("USB system_event (%s)", str);
123 bundle_add_str(b, EVT_KEY_USB_STATUS, str);
124 eventsystem_send_system_event(SYS_EVENT_USB_STATUS, b);
128 void usb_state_retrieve_selected_mode(void)
132 const unsigned int default_mode = USB_FUNCTION_MTP | USB_FUNCTION_ACM;
134 /* Never return here because deviced must never run in USB_FUNCTION_NONE mode */
135 if (vconf_get_int(VCONFKEY_USB_SEL_MODE, &mode_v) != VCONF_OK) {
136 mode_v = SET_USB_INVALID;
137 _E("Failed to get vconf value for USB sel mode: %d", vconf_get_ext_errno());
140 mode = get_mode_bitmap_from_vconf(mode_v);
143 * Deviced must never run in USB_FUNCTION_NONE mode.
144 * So if vconf value is invalid, deviced uses the default usb mode internally.
145 * Keep the problematic vconf values in order to define the problem correctly.
148 case USB_FUNCTION_INVALID: /* Invalid vconf */
149 usb_selected_mode = default_mode;
150 _E("Failed to convert vconf to USB mode. There is no mode matches up with vconf %d. Use default mode=%#x.", mode_v, default_mode);
153 case USB_FUNCTION_NONE: /* Invalid vconf */
154 usb_selected_mode = default_mode;
155 _E("There is USB_FUNCTION_NONE USB mode matches up with vconf %d. Use default mode=%#x.", mode_v, default_mode);
158 default: /* Success */
159 usb_selected_mode = mode;
160 _I("Succeeded to convert vconf to USB mode. vconf=%d, mode=%#x.", mode_v, mode);
165 * Tizen has no way of having different vconf value in engineer mode and user mode.
166 * So, alternatively, always enable SDB in engineer mode.
168 * Expected default sel_mode value in vconf in user mode: 1 (MTP + ACM)
169 * Expected default sel_mode value in vconf in engineer mode: 2 (MTP + ACM + SDB)
172 _I("Engineer mode. USB mode %#x, debug state %d", usb_selected_mode, get_usb_debug_state());
174 if (!(usb_selected_mode & USB_FUNCTION_SDB)) {
175 usb_selected_mode = USB_FUNCTION_MTP | USB_FUNCTION_ACM | USB_FUNCTION_SDB;
176 (void)usb_state_set_selected_mode(usb_selected_mode);
180 /* To sync with vconf for debug mode */
181 set_usb_debug_state((bool)(usb_selected_mode & USB_FUNCTION_SDB));
184 /* Cache of vconf_get_int(VCONFKEY_USB_SEL_MODE) */
185 unsigned int usb_state_get_selected_mode(void)
187 return usb_selected_mode;
190 /* Since it is changed externally by dbus and vconf, it should be processed even if it has the same value as before. */
191 int usb_state_set_selected_mode(unsigned int mode)
196 usb_selected_mode = mode;
198 mode_v = get_mode_vconf_from_bitmap(mode);
199 if (mode_v == SET_USB_INVALID) {
200 _E("Failed to convert USB selected_mode to vconf. There is no vconf matches up with USB mode %#x", mode);
204 ret = vconf_set_int(VCONFKEY_USB_SEL_MODE, mode_v);
206 _E("Failed to set vconf value for USB selected mode: %d", vconf_get_ext_errno());
211 /* Cache of vconf_get_int(VCONFKEY_USB_CUR_MODE) */
212 unsigned int usb_state_get_current_mode(void)
214 return usb_current_mode;
217 /* Because it is only changed by deviced, it is only processed when the vconf actually changes. */
218 int usb_state_set_current_mode(unsigned int mode)
222 static int old_mode_v = -1;
224 usb_current_mode = mode;
226 mode_v = get_mode_vconf_from_bitmap(mode);
227 if (mode_v == SET_USB_INVALID) {
228 _E("Failed to convert USB current_mode to vconf. There is no vconf matches up with mode %#x", mode);
232 if (old_mode_v != mode_v) {
234 ret = vconf_set_int(VCONFKEY_USB_CUR_MODE, mode_v);
236 _E("Failed to set vconf value for USB current mode: %d", vconf_get_ext_errno());
242 extcon_usb_state_e usb_state_get_connection(void)
244 return usb_connection;
247 void usb_state_set_connection(extcon_usb_state_e conn)
249 usb_connection = conn;
252 static void media_noti_cb(GVariant *var, void *user_data, GError *err)
258 _E("USB media notification error: %s", err->message);
262 if (!g_variant_get_safe(var, "(i)", &id)) {
263 _E("Failed to get variant(%s): no USB notification message", g_variant_get_type_string(var));
268 _D("USB media notification message(%d)", noti_id);
271 g_variant_unref(var);
274 static void add_notification_handler(void)
279 ret = add_async_notification("MediaDeviceNotiOn", media_noti_cb, NULL);
281 _E("Failed to add USB notification for usb connection: %d", ret);
285 static void remove_notification_handler(void)
290 ret = remove_notification("MediaDeviceNotiOff", noti_id);
292 _E("Failed to remove USB notification for usb connection: %d", ret);
298 void change_usb_state_notification_handler(unsigned int mode)
300 if (mode & USB_FUNCTION_MTP)
301 add_notification_handler();
302 else if (mode == USB_FUNCTION_NONE)
303 remove_notification_handler();
307 * USB_DISCONNECTED : VCONFKEY_SYSMAN_USB_DISCONNECTED
308 * USB_CONNECTED + USB_FUNCTION_NONE : VCONFKEY_SYSMAN_USB_CONNECTED
309 * USB_CONNECTED + USB_FUNCTION_OOOO : VCONFKEY_SYSMAN_USB_AVAILABLE
311 * When connecting the usb cable : "disconnected"(previous value) -> "connected" -> "available"
312 * When disconnecting the usb cable : "available"(previous value) -> "dosconnected"
313 * When changing usb mode : "available"(previous value) -> "connected" -> "available"
315 * When USB cable is connected but USB initialization fails : It stays "connected" without going to "available"
318 void send_usb_state_changed_event(int status)
320 static int old_status = -1;
322 if ((status != VCONFKEY_SYSMAN_USB_CONNECTED) &&
323 (status != VCONFKEY_SYSMAN_USB_AVAILABLE) &&
324 (status != VCONFKEY_SYSMAN_USB_DISCONNECTED)) {
325 _E("Invalid USB state changed event (%d)", status);
329 if (old_status == status)
334 usb_state_send_system_event(status);
336 if (vconf_set_int(VCONFKEY_SYSMAN_USB_STATUS, status) != VCONF_OK)
337 _E("Failed to set vconf value for USB status: %d", vconf_get_ext_errno());
339 /* Caution: calls after vconf_set_int(VCONFKEY_SYSMAN_USB_STATUS) */
340 broadcast_usb_state_changed();