mirror of
https://github.com/openmv/openmv.git
synced 2025-11-04 14:49:50 +08:00
WINC: Add support for AP mode.
This commit is contained in:
parent
32dded64fd
commit
a4561c7efa
@ -15,12 +15,14 @@
|
||||
#include "py/objlist.h"
|
||||
#include "py/stream.h"
|
||||
#include "py/runtime.h"
|
||||
#include "lib/netutils/netutils.h"
|
||||
#include "modnetwork.h"
|
||||
#include "pin.h"
|
||||
#include "genhdr/pins.h"
|
||||
#include "spi.h"
|
||||
#include "pybioctl.h"
|
||||
#include "common.h"
|
||||
#include "py_helper.h"
|
||||
|
||||
// WINC's includes
|
||||
#include "driver/include/nmasic.h"
|
||||
@ -47,6 +49,7 @@
|
||||
|
||||
static volatile bool ip_obtained = false;
|
||||
static volatile bool wlan_connected = false;
|
||||
static volatile uint32_t connected_sta_ip = false;
|
||||
|
||||
static void *async_request_data;
|
||||
static uint8_t async_request_type=0;
|
||||
@ -62,6 +65,13 @@ typedef struct {
|
||||
struct sockaddr_in addr;
|
||||
} accept_t;
|
||||
|
||||
typedef enum {
|
||||
WINC_MODE_STA,
|
||||
WINC_MODE_AP,
|
||||
WINC_MODE_P2P,
|
||||
WINC_MODE_FIRMWARE,
|
||||
}winc_mode_t;
|
||||
|
||||
/**
|
||||
* DNS Callback.
|
||||
*
|
||||
@ -212,7 +222,63 @@ static void socket_callback(SOCKET sock, uint8_t msg_type, void *msg)
|
||||
}
|
||||
|
||||
/**
|
||||
* WiFi Callback.
|
||||
* WiFi Callback in AP mode.
|
||||
*
|
||||
* msg_type: type of Wi-Fi notification. Possible types are:
|
||||
* M2M_WIFI_RESP_CON_STATE_CHANGED
|
||||
* M2M_WIFI_RESP_CONN_INFO
|
||||
* M2M_WIFI_REQ_DHCP_CONF
|
||||
* M2M_WIFI_REQ_WPS
|
||||
* M2M_WIFI_RESP_IP_CONFLICT
|
||||
* M2M_WIFI_RESP_SCAN_DONE
|
||||
* M2M_WIFI_RESP_SCAN_RESULT
|
||||
* M2M_WIFI_RESP_CURRENT_RSSI
|
||||
* M2M_WIFI_RESP_CLIENT_INFO
|
||||
* M2M_WIFI_RESP_PROVISION_INFO
|
||||
* M2M_WIFI_RESP_DEFAULT_CONNECT
|
||||
*
|
||||
* In case Bypass mode is defined :
|
||||
* M2M_WIFI_RESP_ETHERNET_RX_PACKET
|
||||
*
|
||||
* In case Monitoring mode is used:
|
||||
* M2M_WIFI_RESP_WIFI_RX_PACKET
|
||||
*
|
||||
* msg: A pointer to a buffer containing the notification parameters (if any).
|
||||
* It should be casted to the correct data type corresponding to the notification type.
|
||||
*/
|
||||
|
||||
static void wifi_callback_ap(uint8_t msg_type, void *msg)
|
||||
{
|
||||
switch (msg_type) {
|
||||
case M2M_WIFI_RESP_CON_STATE_CHANGED: {
|
||||
tstrM2mWifiStateChanged *wifi_state = (tstrM2mWifiStateChanged *)msg;
|
||||
if (wifi_state->u8CurrState == M2M_WIFI_CONNECTED) {
|
||||
debug_printf("Station connected\r\n");
|
||||
} else if (wifi_state->u8CurrState == M2M_WIFI_DISCONNECTED) {
|
||||
connected_sta_ip = 0;
|
||||
debug_printf("Station disconnected\r\n");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case M2M_WIFI_REQ_DHCP_CONF: {
|
||||
uint8_t *ip = (uint8_t *)msg;
|
||||
debug_printf("Station connected. IP is %u.%u.%u.%u\r\n", ip[0], ip[1], ip[2], ip[3]);
|
||||
((uint8_t*) &connected_sta_ip)[0] = ip[0];
|
||||
((uint8_t*) &connected_sta_ip)[1] = ip[1];
|
||||
((uint8_t*) &connected_sta_ip)[2] = ip[2];
|
||||
((uint8_t*) &connected_sta_ip)[3] = ip[3];
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
debug_printf("Unknown message type: %d\n", msg_type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* WiFi Callback in STA mode.
|
||||
*
|
||||
* msg_type: type of Wi-Fi notification. Possible types are:
|
||||
* M2M_WIFI_RESP_CON_STATE_CHANGED
|
||||
@ -236,7 +302,7 @@ static void socket_callback(SOCKET sock, uint8_t msg_type, void *msg)
|
||||
* msg: A pointer to a buffer containing the notification parameters (if any).
|
||||
* It should be casted to the correct data type corresponding to the notification type.
|
||||
*/
|
||||
static void wifi_callback(uint8_t msg_type, void *msg)
|
||||
static void wifi_callback_sta(uint8_t msg_type, void *msg)
|
||||
{
|
||||
// Index of scan list to request scan result.
|
||||
static uint8_t scan_request_index = 0;
|
||||
@ -251,11 +317,11 @@ static void wifi_callback(uint8_t msg_type, void *msg)
|
||||
}
|
||||
|
||||
case M2M_WIFI_RESP_CON_STATE_CHANGED: {
|
||||
tstrM2mWifiStateChanged *pstrWifiState = (tstrM2mWifiStateChanged *)msg;
|
||||
if (pstrWifiState->u8CurrState == M2M_WIFI_CONNECTED) {
|
||||
tstrM2mWifiStateChanged *wifi_state = (tstrM2mWifiStateChanged *)msg;
|
||||
if (wifi_state->u8CurrState == M2M_WIFI_CONNECTED) {
|
||||
wlan_connected = true;
|
||||
m2m_wifi_request_dhcp_client();
|
||||
} else if (pstrWifiState->u8CurrState == M2M_WIFI_DISCONNECTED) {
|
||||
} else if (wifi_state->u8CurrState == M2M_WIFI_DISCONNECTED) {
|
||||
ip_obtained = false;
|
||||
wlan_connected = false;
|
||||
async_request_done = true;
|
||||
@ -630,54 +696,86 @@ typedef struct _winc_obj_t {
|
||||
static const winc_obj_t winc_obj = {{(mp_obj_type_t*)&mod_network_nic_type_winc}};
|
||||
|
||||
// Initialise the module using the given SPI bus and pins and return a winc object.
|
||||
static mp_obj_t winc_make_new(const mp_obj_type_t *type, mp_uint_t n_args, mp_uint_t n_kw, const mp_obj_t *args)
|
||||
static mp_obj_t winc_make_new(const mp_obj_type_t *type, mp_uint_t n_args, mp_uint_t n_kw, const mp_obj_t *all_args)
|
||||
{
|
||||
// check arguments
|
||||
mp_arg_check_num(n_args, n_kw, 0, 1, false);
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_mode, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = WINC_MODE_STA } },
|
||||
};
|
||||
|
||||
// parse args
|
||||
mp_map_t kw_args;
|
||||
mp_map_init_fixed_table(&kw_args, n_kw, all_args + n_args);
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all(n_args, all_args, &kw_args, MP_ARRAY_SIZE(args), allowed_args, args);
|
||||
|
||||
// get mode
|
||||
winc_mode_t winc_mode = args[0].u_int;
|
||||
|
||||
// Initialize the BSP.
|
||||
nm_bsp_init();
|
||||
|
||||
// Firmware update enabled
|
||||
if (n_args && mp_obj_get_int(args[0]) == true) {
|
||||
// Enter download mode.
|
||||
printf("Enabling download mode...\n");
|
||||
if (m2m_wifi_download_mode() != M2M_SUCCESS) {
|
||||
nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "Failed to enter download mode!"));
|
||||
switch (winc_mode) {
|
||||
case WINC_MODE_FIRMWARE: {
|
||||
// Enter download mode.
|
||||
printf("Running in Firmware Upgrade mode...\n");
|
||||
if (m2m_wifi_download_mode() != M2M_SUCCESS) {
|
||||
nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "Failed to enter download mode!"));
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// Initialize Wi-Fi parameters structure.
|
||||
tstrWifiInitParam param;
|
||||
memset((uint8_t *)¶m, 0, sizeof(tstrWifiInitParam));
|
||||
param.pfAppWifiCb = wifi_callback;
|
||||
|
||||
// Initialize Wi-Fi driver with data and status callbacks.
|
||||
int ret = m2m_wifi_init(¶m);
|
||||
if (M2M_SUCCESS != ret) {
|
||||
nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "failed to init WINC1500 module"));
|
||||
}
|
||||
|
||||
uint8_t mac_addr_valid;
|
||||
uint8_t mac_addr[M2M_MAC_ADDRES_LEN];
|
||||
// Get MAC Address from OTP.
|
||||
m2m_wifi_get_otp_mac_address(mac_addr, &mac_addr_valid);
|
||||
case WINC_MODE_AP:
|
||||
case WINC_MODE_STA: {
|
||||
tstrWifiInitParam param;
|
||||
// Initialize Wi-Fi parameters structure.
|
||||
memset((uint8_t *)¶m, 0, sizeof(tstrWifiInitParam));
|
||||
if (winc_mode == WINC_MODE_AP) {
|
||||
param.pfAppWifiCb = wifi_callback_ap;
|
||||
} else {
|
||||
param.pfAppWifiCb = wifi_callback_sta;
|
||||
}
|
||||
|
||||
if (!mac_addr_valid) {
|
||||
// User define MAC Address.
|
||||
const char main_user_define_mac_address[] = {0xf8, 0xf0, 0x05, 0x20, 0x0b, 0x09};
|
||||
// Cannot found MAC Address from OTP. Set user define MAC address.
|
||||
m2m_wifi_set_mac_address((uint8_t *) main_user_define_mac_address);
|
||||
}
|
||||
// Initialize Wi-Fi driver with data and status callbacks.
|
||||
if (m2m_wifi_init(¶m) != M2M_SUCCESS) {
|
||||
nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "failed to init WINC1500 module"));
|
||||
}
|
||||
|
||||
// Initialize socket layer.
|
||||
socketDeinit();
|
||||
socketInit();
|
||||
uint8_t mac_addr_valid;
|
||||
uint8_t mac_addr[M2M_MAC_ADDRES_LEN];
|
||||
// Get MAC Address from OTP.
|
||||
m2m_wifi_get_otp_mac_address(mac_addr, &mac_addr_valid);
|
||||
|
||||
// Register sockets callback functions
|
||||
registerSocketCallback(socket_callback, resolve_callback);
|
||||
if (!mac_addr_valid) {
|
||||
// User define MAC Address.
|
||||
const char main_user_define_mac_address[] = {0xf8, 0xf0, 0x05, 0x20, 0x0b, 0x09};
|
||||
// Cannot found MAC Address from OTP. Set user define MAC address.
|
||||
m2m_wifi_set_mac_address((uint8_t *) main_user_define_mac_address);
|
||||
}
|
||||
|
||||
// Register with network module
|
||||
mod_network_register_nic((mp_obj_t)&winc_obj);
|
||||
// Initialize socket layer.
|
||||
socketDeinit();
|
||||
socketInit();
|
||||
|
||||
// Register sockets callback functions
|
||||
registerSocketCallback(socket_callback, resolve_callback);
|
||||
|
||||
// Register with network module
|
||||
mod_network_register_nic((mp_obj_t)&winc_obj);
|
||||
|
||||
if (winc_mode == WINC_MODE_AP) {
|
||||
printf("Running in Access Point mode...\n");
|
||||
} else {
|
||||
printf("Running in Station mode...\n");
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case WINC_MODE_P2P:
|
||||
nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "P2P mode is not supported yet!"));
|
||||
break;
|
||||
default:
|
||||
nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "WiFi mode is not supported!"));
|
||||
}
|
||||
|
||||
return (mp_obj_t)&winc_obj;
|
||||
@ -722,6 +820,67 @@ static mp_obj_t winc_connect(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
// method start_ap(ssid, key=None, security=OPEN)
|
||||
static mp_obj_t winc_start_ap(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args)
|
||||
{
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_ssid, MP_ARG_REQUIRED| MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
|
||||
{ MP_QSTR_key, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
|
||||
{ MP_QSTR_security, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = M2M_WIFI_SEC_OPEN } }, //M2M_WIFI_SEC_WPA_PSK
|
||||
{ MP_QSTR_channel, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 1} },
|
||||
};
|
||||
|
||||
// parse args
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
|
||||
// get ssid
|
||||
mp_uint_t ssid_len;
|
||||
const char *ssid = mp_obj_str_get_data(args[0].u_obj, &ssid_len);
|
||||
|
||||
// get key and security
|
||||
mp_uint_t key_len = 0;
|
||||
const char *key = NULL;
|
||||
mp_uint_t security = args[2].u_int;
|
||||
|
||||
if (security != M2M_WIFI_SEC_OPEN && security != M2M_WIFI_SEC_WEP) {
|
||||
nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "AP mode supports WEP security only."));
|
||||
}
|
||||
|
||||
if (security == M2M_WIFI_SEC_WEP && args[1].u_obj == MP_OBJ_NULL) {
|
||||
nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "Missing WEP key!"));
|
||||
}
|
||||
|
||||
key = mp_obj_str_get_data(args[1].u_obj, &key_len);
|
||||
|
||||
// get channel
|
||||
mp_uint_t channel = args[3].u_int;
|
||||
|
||||
tstrM2MAPConfig apconfig;
|
||||
memset(&apconfig, 0, sizeof(tstrM2MAPConfig));
|
||||
strcpy((char *)&apconfig.au8SSID, ssid);
|
||||
apconfig.u8ListenChannel = channel;
|
||||
apconfig.u8SecType = security;
|
||||
apconfig.au8DHCPServerIP[0] = 192;
|
||||
apconfig.au8DHCPServerIP[1] = 168;
|
||||
apconfig.au8DHCPServerIP[2] = 1;
|
||||
apconfig.au8DHCPServerIP[3] = 1;
|
||||
|
||||
if (security != M2M_WIFI_SEC_OPEN) {
|
||||
strcpy((char *)&apconfig.au8WepKey, key);
|
||||
apconfig.u8KeySz = WEP_40_KEY_STRING_SIZE;
|
||||
apconfig.u8KeyIndx = 1;
|
||||
}
|
||||
|
||||
// Initialize WiFi in AP mode.
|
||||
if (m2m_wifi_enable_ap(&apconfig) != M2M_SUCCESS) {
|
||||
nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "failed to start in AP mode"));
|
||||
}
|
||||
|
||||
printf("AP mode started. You can connect to %s.\r\n", ssid);
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
static mp_obj_t winc_disconnect(mp_obj_t self_in)
|
||||
{
|
||||
m2m_wifi_disconnect();
|
||||
@ -733,6 +892,45 @@ static mp_obj_t winc_isconnected(mp_obj_t self_in)
|
||||
return mp_obj_new_bool(wlan_connected && ip_obtained);
|
||||
}
|
||||
|
||||
static mp_obj_t winc_connected_sta(mp_obj_t self_in)
|
||||
{
|
||||
mp_obj_t sta_list = mp_obj_new_list(0, NULL);
|
||||
|
||||
if (connected_sta_ip == 0) {
|
||||
m2m_wifi_handle_events(NULL);
|
||||
}
|
||||
|
||||
if (connected_sta_ip != 0) {
|
||||
mp_obj_list_append(sta_list, netutils_format_inet_addr(
|
||||
(uint8_t*)&connected_sta_ip, 0, NETUTILS_BIG));
|
||||
}
|
||||
|
||||
return sta_list;
|
||||
}
|
||||
|
||||
static mp_obj_t winc_wait_for_sta(mp_obj_t self_in, mp_obj_t timeout_in)
|
||||
{
|
||||
// get timeout
|
||||
mp_obj_t sta_list = mp_obj_new_list(0, NULL);
|
||||
mp_uint_t timeout = mp_obj_get_int(timeout_in);
|
||||
|
||||
uint32_t tick_start = HAL_GetTick();
|
||||
while (connected_sta_ip == 0) {
|
||||
// Handle pending events from network controller.
|
||||
m2m_wifi_handle_events(NULL);
|
||||
if (timeout && ((HAL_GetTick() - tick_start) >= timeout)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (connected_sta_ip != 0) {
|
||||
mp_obj_list_append(sta_list, netutils_format_inet_addr(
|
||||
(uint8_t*)&connected_sta_ip, 0, NETUTILS_BIG));
|
||||
}
|
||||
|
||||
return sta_list;
|
||||
|
||||
}
|
||||
static mp_obj_t winc_ifconfig(mp_obj_t self_in)
|
||||
{
|
||||
mp_obj_t info_list;
|
||||
@ -843,8 +1041,11 @@ static mp_obj_t winc_fw_update(mp_obj_t self_in)
|
||||
}
|
||||
|
||||
static MP_DEFINE_CONST_FUN_OBJ_KW(winc_connect_obj, 1, winc_connect);
|
||||
static MP_DEFINE_CONST_FUN_OBJ_KW(winc_start_ap_obj,1, winc_start_ap);
|
||||
static MP_DEFINE_CONST_FUN_OBJ_1(winc_disconnect_obj, winc_disconnect);
|
||||
static MP_DEFINE_CONST_FUN_OBJ_1(winc_isconnected_obj, winc_isconnected);
|
||||
static MP_DEFINE_CONST_FUN_OBJ_1(winc_connected_sta_obj,winc_connected_sta);
|
||||
static MP_DEFINE_CONST_FUN_OBJ_2(winc_wait_for_sta_obj, winc_wait_for_sta);
|
||||
static MP_DEFINE_CONST_FUN_OBJ_1(winc_ifconfig_obj, winc_ifconfig);
|
||||
static MP_DEFINE_CONST_FUN_OBJ_1(winc_scan_obj, winc_scan);
|
||||
static MP_DEFINE_CONST_FUN_OBJ_1(winc_rssi_obj, winc_rssi);
|
||||
@ -854,8 +1055,11 @@ static MP_DEFINE_CONST_FUN_OBJ_1(winc_fw_update_obj, winc_fw_update);
|
||||
|
||||
static const mp_map_elem_t winc_locals_dict_table[] = {
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_connect), (mp_obj_t)&winc_connect_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_start_ap), (mp_obj_t)&winc_start_ap_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_disconnect), (mp_obj_t)&winc_disconnect_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_isconnected), (mp_obj_t)&winc_isconnected_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_connected_sta), (mp_obj_t)&winc_connected_sta_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_wait_for_sta), (mp_obj_t)&winc_wait_for_sta_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_ifconfig), (mp_obj_t)&winc_ifconfig_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_scan), (mp_obj_t)&winc_scan_obj },
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_rssi), (mp_obj_t)&winc_rssi_obj },
|
||||
@ -867,6 +1071,11 @@ static const mp_map_elem_t winc_locals_dict_table[] = {
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_WEP), MP_OBJ_NEW_SMALL_INT(M2M_WIFI_SEC_WEP) }, // Security type WEP (40 or 104) OPEN OR SHARED.
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_WPA_PSK), MP_OBJ_NEW_SMALL_INT(M2M_WIFI_SEC_WPA_PSK) }, // Network is secured with WPA/WPA2 personal(PSK).
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_802_1X), MP_OBJ_NEW_SMALL_INT(M2M_WIFI_SEC_802_1X) }, // Network is secured with WPA/WPA2 Enterprise.
|
||||
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_MODE_STA), MP_OBJ_NEW_SMALL_INT(WINC_MODE_STA) }, // Start in Staion mode.
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_MODE_AP), MP_OBJ_NEW_SMALL_INT(WINC_MODE_AP) }, // Start in Access Point mode.
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_MODE_P2P), MP_OBJ_NEW_SMALL_INT(WINC_MODE_P2P) }, // Start in Peer-to-Peer (WiFi Direct) mode.
|
||||
{ MP_OBJ_NEW_QSTR(MP_QSTR_MODE_FIRMWARE), MP_OBJ_NEW_SMALL_INT(WINC_MODE_FIRMWARE) }, // Start in Firmware Upgrade mode.
|
||||
};
|
||||
|
||||
static MP_DEFINE_CONST_DICT(winc_locals_dict, winc_locals_dict_table);
|
||||
|
||||
@ -281,8 +281,11 @@ Q(IPPROTO_RAW)
|
||||
// for WINC1500 module
|
||||
Q(WINC)
|
||||
Q(connect)
|
||||
Q(start_ap)
|
||||
Q(disconnect)
|
||||
Q(isconnected)
|
||||
Q(connected_sta)
|
||||
Q(wait_for_sta)
|
||||
Q(ifconfig)
|
||||
Q(fw_version)
|
||||
Q(fw_dump)
|
||||
@ -293,6 +296,10 @@ Q(OPEN)
|
||||
Q(WEP)
|
||||
Q(WPA_PSK)
|
||||
Q(802_1X)
|
||||
Q(MODE_STA)
|
||||
Q(MODE_AP)
|
||||
Q(MODE_P2P)
|
||||
Q(MODE_FIRMWARE)
|
||||
Q(ssid)
|
||||
Q(key)
|
||||
Q(security)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user