Перейти к содержанию
    

COM-порт -общение компьютера с черным ящиком

Есть некое устройство и программа, которая общается с ним. Исходников и документации нет.

Общается по 3-м проводам (GND, RX, TX).

Хочется написать свою программку. Сниффинг обмена вроде бы прояснил ситуацию, протокол вроде бы понятен, но с общением есть проблемы :crying:

 

При работе через терминальные проги железяка или молчит в ответ на запросы, или отвечает один раз и больше не реагирует. :07:

 

Вот пример лога общения родной программы (использовал Eltima Serial Port Monitor 4, но пробовал и другие):

 

[12/02/2010 17:19:21] 9245  IRP_MJ_CREATE - Opens a COM port (COM1)
[12/02/2010 17:19:21] 9247  IOCTL_SERIAL_SET_QUEUE_SIZE - Request sets the size of the internal receive buffer
		InSize - 1024
		OutSize - 300
[12/02/2010 17:19:21] 9249  IOCTL_SERIAL_GET_BAUD_RATE
		BaudRate - 9600
[12/02/2010 17:19:21] 9251  IOCTL_SERIAL_GET_LINE_CONTROL
		StopBits - 0
		Parity - 0
		WordLength - 8
[12/02/2010 17:19:21] 9253  IOCTL_SERIAL_GET_CHARS
		EofChar - 0
		ErrorChar - 0
		BreakChar - 0
		EventChar - 0
		XonChar - 17
		XoffChar - 19
[12/02/2010 17:19:21] 9255  IOCTL_SERIAL_GET_HANDFLOW
		ControlHandShake - 0x1 (SERIAL_DTR_CONTROL)
		FlowReplace - 0x40 (SERIAL_RTS_CONTROL)
		XonLimit - 2048
		XoffLimit - 512

Зачем-то еще раз GET_BAUDRATE, GET_LINE_CONTROL, GET_CHARS, GET_HANDFLOW

[12/02/2010 17:19:21] 9265  IOCTL_SERIAL_SET_BAUD_RATE 
		BaudRate - 9600
[12/02/2010 17:19:21] 9267  IOCTL_SERIAL_SET_RTS
[12/02/2010 17:19:21] 9269  IOCTL_SERIAL_SET_DTR 
[12/02/2010 17:19:21] 9271  IOCTL_SERIAL_SET_LINE_CONTROL
		StopBits - 0
		Parity - 0
		WordLength - 8
[12/02/2010 17:19:21] 9273  IOCTL_SERIAL_SET_CHARS
		EofChar - 0
		ErrorChar - 0
		BreakChar - 0
		EventChar - 0
		XonChar - 17
		XoffChar - 19
[12/02/2010 17:19:21] 9275  IOCTL_SERIAL_SET_HANDFLOW - Request sets the configuration of handshake flow control
		ControlHandShake - 0x1 (SERIAL_DTR_CONTROL)
		FlowReplace - 0x40 (SERIAL_RTS_CONTROL)
		XonLimit - 2048
		XoffLimit - 512

Несколько десятков вот таких команд:

[12/02/2010 17:19:21] 9277  IRP_MJ_DEVICE_CONTROL - Request operates a serial port   STATUS_SUCCESS
	 IOCTL_SERIAL_GET_COMMSTATUS - Request returns information about the communication status of a COM port
		Errors - 0
		HoldReasons - 0
		AmountInInQueue - 0
		AmountInOutQueue - 0
		EofReceived - 0
		WaitForImmediate - 0

Запрос железке:

[12/02/2010 17:19:23] 9347  IRP_MJ_DEVICE_CONTROL - Request operates a serial port   STATUS_SUCCESS
	 IOCTL_SERIAL_SET_BREAK_ON - Sets the line control break signal active
9349  IRP_MJ_DEVICE_CONTROL - Request operates a serial port						STATUS_SUCCESS
	 IOCTL_SERIAL_SET_BREAK_OFF - Request sets the line control break signal inactive

[12/02/2010 17:19:23] 9351  IRP_MJ_WRITE - Request transfers data from a client to a COM port	STATUS_SUCCESS
  00 ff ff 01 f0 a6 10 47						 	.??.?│.G

Вот тут непонятно, почему Errors = 1 - глюк программы-сниффера?

[12/02/2010 17:19:23] 9353  IRP_MJ_DEVICE_CONTROL - Request operates a serial port	STATUS_SUCCESS
	 IOCTL_SERIAL_GET_COMMSTATUS - Request returns information about the communication status of a COM port
		Errors - 1
		HoldReasons - 0
		AmountInInQueue - 0
		AmountInOutQueue - 0
		EofReceived - 0
		WaitForImmediate - 0
[12/02/2010 17:19:23] 9355  IRP_MJ_DEVICE_CONTROL - Request operates a serial port   STATUS_SUCCESS
	 IOCTL_SERIAL_GET_COMMSTATUS - Request returns information about the communication status of a COM port
		Errors - 0
		HoldReasons - 0
		AmountInInQueue - 25
		AmountInOutQueue - 0
		EofReceived - 0
		WaitForImmediate - 0

Ну и сам ответ железки:

[12/02/2010 17:19:23] 9357  IRP_MJ_READ - Transfers data from a COM port to a client   STATUS_SUCCESS
  00 00 00 00 01 f0 a6 10 29 99 04 50 00 00 00 00 	.....?│.)T.P....
  00 00 00 00 00 00 00 00 a3 99 04 50 00 00 00 00 	........?T.P....


Дальше пара IOCTL_SERIAL_GET_COMMSTATUS 
IOCTL_SERIAL_SET_BREAK_ON
IOCTL_SERIAL_SET_BREAK_OFF
IRP_MJ_WRITE	 00 ff ff 01 03 00 04 06	- запрос железке
IOCTL_SERIAL_GET_COMMSTATUS 
IRP_MJ_READ  - ответ железки

ну и т.д.

 

 

 

В winapi я не силен, попробовал накропать небольшую программенку, на основе каких-то примеров.

 

#include <windows.h>
#include <stdio.h>

HANDLE hCom;


char *pcCommPort = "COM1";


void mem_read (char *buffer, WORD addr, unsigned char count)
{
unsigned char command_mem_request [8] = {
	0, 0xFF, 0xFF, 1, 0xF0, 0xA6, 0x10, 0x47 };

unsigned char buffer2 [30];

// Зачем это - не знаю. 
// В логе фирменной программы была установка/снятие BREAK
SetCommBreak (hCom);
ClearCommBreak (hCom);	

// Запрос железке
WriteFile (hCom, @command_mem_request, 8, &dwBytes, NULL);	
if (dwBytes != 8) printf ("Error!!!\n");

// Хотим ответ
// По факту ничего не получаем
while (1)
{
	ReadFile (hCom, &buffer2, 0x1, &dwBytes, NULL);
	printf ("%u bytes read!\n", dwBytes);		
}
}


int main(int argc, char *argv[])
{
unsigned char buffer, buffer_prev = 0xF0;
COMMTIMEOUTS CommTimeouts;
DCB dcb;

hCom = CreateFile( pcCommPort,
	GENERIC_READ | GENERIC_WRITE,
	0, // must be opened with exclusive-access
	NULL, // no security attributes
	OPEN_EXISTING, // must use OPEN_EXISTING
	0, // not overlapped I/O
	NULL // hTemplate must be NULL for comm devices
	);

if (hCom == INVALID_HANDLE_VALUE)
{
	printf ("CreateFile failed with error %d.\n", GetLastError());
	return (1);
}


if (!SetupComm (hCom, 1024, 300); )
{
	printf ("SetupComm with error %d.\n", GetLastError());
	return (1);
}

if (!GetCommState (hCom, &dcb))
{
	printf ("GetCommState failed with error %d.\n", GetLastError());
	return (1);
}

dcb.BaudRate = CBR_9600;
dcb.ByteSize = 8; 
dcb.Parity = NOPARITY;
dcb.StopBits = ONESTOPBIT; 	

if (!SetCommState(hCom, &dcb))
{
	printf ("SetCommState failed with error %d.\n", GetLastError());
	return (1);
}

if (hCom != INVALID_HANDLE_VALUE && hCom != NULL)
	printf ("COM1 cofigured!\n");

mem_read (NULL, 0xA6F0, 0x10);

return (0);
}

 

 

Лог сниффера:

 

IRP_MJ_CREATE
IOCTL_SERIAL_SET_QUEUE_SIZE
		InSize - 1024
		OutSize - 300
IOCTL_SERIAL_GET_BAUD_RATE
		BaudRate - 9600
IOCTL_SERIAL_GET_LINE_CONTROL
		StopBits - 0
		Parity - 0
		WordLength - 8
IOCTL_SERIAL_GET_CHARS
		EofChar - 0
		ErrorChar - 0
		BreakChar - 0
		EventChar - 0
		XonChar - 17
		XoffChar - 19
IOCTL_SERIAL_GET_HANDFLOW
		ControlHandShake - 0x1 (SERIAL_DTR_CONTROL)
		FlowReplace - 0x40 (SERIAL_RTS_CONTROL)
		XonLimit - 2048
		XoffLimit - 512

Зачем-то еще раз GET_BAUDRATE, GET_LINE_CONTROL, GET_CHARS, GET_HANDFLOW

IOCTL_SERIAL_SET_BAUD_RATE
		BaudRate - 9600
IOCTL_SERIAL_SET_RTS
IOCTL_SERIAL_SET_DTR
IOCTL_SERIAL_SET_LINE_CONTROL
		StopBits - 0
		Parity - 0
		WordLength - 8
IOCTL_SERIAL_SET_CHARS
		EofChar - 0
		ErrorChar - 0
		BreakChar - 0
		EventChar - 0
		XonChar - 17
		XoffChar - 19
IOCTL_SERIAL_SET_HANDFLOW
		ControlHandShake - 0x1 (SERIAL_DTR_CONTROL)
		FlowReplace - 0x40 (SERIAL_RTS_CONTROL)
		XonLimit - 2048
		XoffLimit - 512

Даем запрос устройству.

IOCTL_SERIAL_SET_BREAK_ON
IOCTL_SERIAL_SET_BREAK_OFF
IRP_MJ_WRITE 00 ff ff 01 f0 a6 10 47

И вместо ответа вот такая ерунда в цикле:

14101  IRP_MJ_READ - STATUS_TIMEOUT

 

 

Есть у кого-нибудь мысли, что я делаю не так?

 

Какая функция winapi вызывает IOCTL_SERIAL_GET_COMMSTATUS?

 

P.S. Прошу прощения за большой объем сообщения :laughing:

Изменено пользователем rezident
Уменьшение видимого размера цитаты исходника.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Какая функция winapi вызывает IOCTL_SERIAL_GET_COMMSTATUS?

BOOL GetCommState(
    HANDLE hFile,    // handle of communications device
    LPDCB lpDCB    // address of device-control block structure
);

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Нет =(

на GetCommState сниффер выдает

IOCTL_SERIAL_GET_BAUD_RATE

IOCTL_SERIAL_GET_LINE_

IOCTL_SERIAL_GET_CHARS

IOCTL_SERIAL_GET_HANDFLOW

 

GetCommModemStatus тоже не подходит =(

Изменено пользователем MEFF

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Тогда не знаю...

Но если вы так быстро это проверяете, то проверьте для меня вот это:

BOOL TransmitCommChar(
    HANDLE hFile,    // handle of communications device
    char cChar     // character to transmit
);

Дает ли она какой-нибудь IOCTL, а если да, то какой?

Это самая загадочная команда из всех, т.к. про нее прочти ничего не написано. А очень хотелось бы знать, в каком виде она поступает на USB-устройство.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Какая функция winapi вызывает IOCTL_SERIAL_GET_COMMSTATUS?

Скорее всего эта

 

BOOL ClearCommError(

    HANDLE hFile,    // handle to communications device
    LPDWORD lpErrors,    // pointer to variable to receive error codes
    LPCOMSTAT lpStat    // pointer to buffer for communications status  
   );

 

The ClearCommError function retrieves information about a communications error and reports the current status of a communications device. The function is called when a communications error occurs, and it clears the device's error flag to enable additional input and output (I/O) operations.

 

Эта функция заполняет структуру COMSTAT.

И часто используется для того, чтобы определить количество данных в буфере приемника

Изменено пользователем UniSoft

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Тогда не знаю...

Но если вы так быстро это проверяете, то проверьте для меня вот это:

BOOL TransmitCommChar(
    HANDLE hFile,    // handle of communications device
    char cChar     // character to transmit
);

Дает ли она какой-нибудь IOCTL, а если да, то какой?

Это самая загадочная команда из всех, т.к. про нее прочти ничего не написано. А очень хотелось бы знать, в каком виде она поступает на USB-устройство.

 

[12/02/2010 22:23:32] 41636 IRP_MJ_DEVICE_CONTROL - Request operates a serial port

STATUS_SUCCESS

IOCTL_SERIAL_IMMEDIATE_CHAR - Request causes a specified character to be transmitted as soon as possible

 

Попробовал с помощью нее побайтно запрос отправить - все равно устройство не отвечает.

Serial Port Monitor ее как-то странно обрабатывает. Что команда есть - рисует, а в dump view (дамп принимаемых/отсылаемых байт) ничего нет. Наверное не поддерживает ее полностью =)

 

Скорее всего эта

 

BOOL ClearCommError(

 

Спасибо! Это действительно она.

Правда, после посылки запроса и ClearCommError, железка не отвечает AmountInInQueue = 0 =(

 

         IOCTL_SERIAL_GET_COMMSTATUS - Request returns information about the communication status of a COM port
            Errors - 0
            HoldReasons - 0
            AmountInInQueue - 0
            AmountInOutQueue - 0
            EofReceived - 0
            WaitForImmediate - 0

 

То ли порт как-то хитро инициализируется, то ли что.... =(

Изменено пользователем MEFF

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

[12/02/2010 22:23:32]

То ли порт как-то хитро инициализируется, то ли что.... =(

А что за прога если не секрет?

Если не большая, и написана не на каком-нибудь визуал басике или жаве, то я могу глянуть что там происходит.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

А что за прога если не секрет?

Если не большая, и написана не на каком-нибудь визуал басике или жаве, то я могу глянуть что там происходит.

 

Прога - специфическая, для мониторинга оборудования.

Она защищена HardLock донглом. Написана ногами, и наверняка на васике =)

 

Как распаковать exe-шник от донгловского упаковщика не знаю =(

Изменено пользователем MEFF

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

вот так должно работать

 

#include <windows.h>
#include <stdio.h>

HANDLE hCom;


char *pcCommPort = "\\\\.\\COM1";

int comport_read(HANDLE hPort,char *buffer,int cnt,int timeout)
{
DWORD dwErr;
OVERLAPPED o;
COMSTAT cstat;
int BytesReaded = 0;
int tmo = GetTickCount();

while ( (GetTickCount() - tmo) < timeout )
{
	ClearCommError(hPort, &dwErr, &cstat);
	if (cstat.cbInQue >= cnt) break;
	Sleep(1);   // это нужно чтобы дать поработать другим процессам системы,
			// а не занимать весь временной слот ожидая данные и тем самым не грузить систему
}

BytesReaded = cstat.cbInQue;
if (BytesReaded > 0)
{
	if (BytesReaded > cnt) 
		BytesReaded = cnt;

	o.Internal		= 0;
	o.InternalHigh	= 0;
	o.loffset		= 0;
	o.OffsetHigh	= 0;
	o.hEvent		= 0;

	// можно и так
	// memclr(&o,0,sizeof(OVERLAPPED));

	if (!ReadFile(hPort,buffer,BytesReaded,&BytesReaded,&o))
	{
		BytesReaded = 0;
	}
}
return (BytesReaded);
}


void mem_read (char *buffer, WORD addr, unsigned char count)
{
unsigned char command_mem_request [8] = {
	0, 0xFF, 0xFF, 1, 0xF0, 0xA6, 0x10, 0x47 };

unsigned char buffer2 [300];
OVERLAPPED o;

PurgeComm(hCom, PURGE_TXCLEAR | PURGE_RXCLEAR);

// Зачем это - не знаю. 
// В логе фирменной программы была установка/снятие BREAK
//	SetCommBreak (hCom);
//	ClearCommBreak (hCom);	

// Запрос железке
// обнулим структуру OVERLAPPED
o.Internal		= 0;
o.InternalHigh	= 0;
o.loffset		= 0;
o.OffsetHigh	= 0;
o.hEvent		= 0;
// можно и так
// memclr(&o,0,sizeof(OVERLAPPED));
WriteFile (hCom, @command_mem_request, 8, &dwBytes, &o);	
if (dwBytes != 8) printf ("Error!!!\n");

// Хотим ответ
// По факту возможно получаем

dwBytes = comport_read(hCom,&buffer2,32,5000); // << ждем ответ 5 секунд
printf ("%u bytes read!\n", dwBytes);	  
}


int main(int argc, char *argv[])
{
unsigned char buffer, buffer_prev = 0xF0;
COMMTIMEOUTS CommTimeouts;
DCB dcb;

hCom = CreateFile( pcCommPort,
	GENERIC_READ | GENERIC_WRITE,
	NULL, // must be opened with exclusive-access
	NULL, // no security attributes
	OPEN_EXISTING, // must use OPEN_EXISTING
	0, // not overlapped I/O
	FILE_FLAG_OVERLAPPED // hTemplate must be NULL for comm devices
	);

if (hCom == INVALID_HANDLE_VALUE)
{
	printf ("CreateFile failed with error %d.\n", GetLastError());
	return (1);
}

if (!SetupComm (hCom, 1024, 300); )
{
	printf ("SetupComm with error %d.\n", GetLastError());
	return (1);
}

if (!GetCommState (hCom, &dcb))
{
	printf ("GetCommState failed with error %d.\n", GetLastError());
	return (1);
}

dcb.BaudRate = CBR_9600;
dcb.ByteSize = 8; 
dcb.Parity = NOPARITY;
dcb.StopBits = ONESTOPBIT;	 

if (!SetCommState(hCom, &dcb))
{
	printf ("SetCommState failed with error %d.\n", GetLastError());
	return (1);
}

PurgeComm(hCom,PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR);

if (hCom != INVALID_HANDLE_VALUE && hCom != NULL)
	printf ("COM1 cofigured!\n");

mem_read (NULL, 0xA6F0, 0x10);

return (0);
}

Изменено пользователем rezident
Уменьшение видимого размера цитаты исходника.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

вот так должно работать

 

#include <windows.h>
#include <stdio.h>

 

 

    if (!WriteFile (hCom, &command_mem_request, 8, &dwBytes, &o))
    {
        printf ("WriteFile failed with error %d.\n", GetLastError());
        return;    
    }

 

WriteFile failed with error 997.

 

Сейчас попробую сам поразбираться...

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

    if (!WriteFile (hCom, &command_mem_request, 8, &dwBytes, &o))
    {
        printf ("WriteFile failed with error %d.\n", GetLastError());
        return;    
    }

 

WriteFile failed with error 997.

 

Сейчас попробую сам поразбираться...

упс, конечно-же, я не туда прописал константу

 

    hCom = CreateFile( pcCommPort,
        GENERIC_READ | GENERIC_WRITE,
        NULL, // must be opened with exclusive-access
        NULL, // no security attributes
        OPEN_EXISTING, // must use OPEN_EXISTING
        0, // not overlapped I/O
        FILE_FLAG_OVERLAPPED //  [color="#FF0000"][b]<<<< Вот тут ошибся[/b][/color]
        );

 

надо так

 

    hCom = CreateFile( pcCommPort,
        GENERIC_READ | GENERIC_WRITE,
        NULL, // must be opened with exclusive-access
        NULL, // no security attributes
        OPEN_EXISTING, // must use OPEN_EXISTING
        FILE_FLAG_OVERLAPPED , // not overlapped I/O
        NULL //  
        );

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

упс, конечно-же, я не туда прописал константу

 

надо так

 

    hCom = CreateFile( pcCommPort,
        GENERIC_READ | GENERIC_WRITE,
        NULL, // must be opened with exclusive-access
        NULL, // no security attributes
        OPEN_EXISTING, // must use OPEN_EXISTING
        FILE_FLAG_OVERLAPPED , // not overlapped I/O
        NULL //  
        );

 

Почти =)

 

    hCom = CreateFile( pcCommPort,
                GENERIC_READ | GENERIC_WRITE,
                0,     // must be opened with exclusive-access
                NULL,    // no security attributes
                OPEN_EXISTING, // must use OPEN_EXISTING
                FILE_FLAG_OVERLAPPED, // hTemplate must be NULL for comm devices
            NULL
        );

 

Это все я понял и так...

 

Тут выяснился вопрос. Если устройство выключить/включить, то работает и ваш вариант, и мой.

Но только на один раз.

Например, нужно послать команды (и получить ответ после каждой)

00 FF FF 01 F0 A6 10 47

00 FF FF 01 03 00 04 06

00 FF FF 01 F0 F0 0A 0B

 

На первую команду устройство отвечает, а на остальные - тишина.

Устройство больше не реагирует на команды от нашей проги. Нужно его выключить/включить - и оно опять работает на одну команду.

 

 

Самое интересное, что фирменная прога отлично пашет даже когда железка якобы зависла (не отвечала на самописную программку).

 

Т.е. как по мне, дело не в железке...

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Тут выяснился вопрос. Если устройство выключить/включить, то работает и ваш вариант, и мой.

Но только на один раз.

Например, нужно послать команды (и получить ответ после каждой)

00 FF FF 01 F0 A6 10 47

00 FF FF 01 03 00 04 06

00 FF FF 01 F0 F0 0A 0B

 

На первую команду устройство отвечает, а на остальные - тишина.

Устройство больше не реагирует на команды от нашей проги. Нужно его выключить/включить - и оно опять работает на одну команду.

 

 

Самое интересное, что фирменная прога отлично пашет даже когда железка якобы зависла (не отвечала на самописную программку).

 

Т.е. как по мне, дело не в железке...

Ну тут уже нужно более детально анализировать протокол обмена...

А там точно не используются дополнительные сигналы порта, как DTR и/или RTS???

либо программный контроль через BREAK??? не зря они там есть...

А не отвечает, значит чего-то ждет, может нужно дрыгнуть какой ногой порта, либо может то самое дрыгание BREAK'ом...

 

Самое интересное, что фирменная прога отлично пашет даже когда железка якобы зависла

Это еще раз подтверждает, что устройство как-то перезапускается... и скорее всего это один из сигналов...

 

00 FF FF 01 F0 A6 10 47

а последний байт в командах это CRC (XOR всех байт) ;)

 

Для более подробной информации нужно глубже копать само устройство

Изменено пользователем UniSoft

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Ну тут уже нужно более детально анализировать протокол обмена...

А там точно не используются дополнительные сигналы порта, как DTR и/или RTS???

 

DTR и RTS не используются. Я отпаял в кабеле все провода кроме TX, RX и GND =)

 

либо программный контроль через BREAK??? не зря они там есть...

А не отвечает, значит чего-то ждет, может нужно дрыгнуть какой ногой порта, либо может то самое дрыгание BREAK'ом...

 

SetCommBreak (hCom);

ClearCommBreak (hCom);

Это не дрыганье? Или нужно что-то сложнее? Завтра буду искать, чтобы почитать на эту тему.

 

Это еще раз подтверждает, что устройство как-то перезапускается... и скорее всего это один из сигналов...

 

а последний байт в командах это CRC (XOR всех байт) ;)

Ага =)

 

Для более подробной информации нужно глубже копать само устройство

Не знаю уже куда копать, кроме логов сниффера =( А там вроде ничего такого. Только BREAK ON-OFF перед WRITE

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...