Форум программистов, компьютерный форум, киберфорум
C/С++ под Linux
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.80/5: Рейтинг темы: голосов - 5, средняя оценка - 4.80
92 / 19 / 4
Регистрация: 11.04.2015
Сообщений: 1,019
Записей в блоге: 1

Почему не срабатывает драйвер?-2

13.08.2016, 18:31. Показов 1132. Ответов 6
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Здравствуйте!

Продолжаю писать модуль под мышь. Добился того, что драйвер встраивается в систему и отправляет данные в userspace. Мышь реагирует.
Но в буффер в userspace приходят нули, а функция usb_interrupt_msg() в драйвере возвращает отрицательное значение. Поэтому не могу реализовать функцию движения курсора.

Подскажите, пожалуйста, что не так в драйвере.

Вот код драйвера:

C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/usb.h>
 
#define MIN(a,b) (((a) <= (b)) ? (a) : (b))
#define INT_EP_IN 0x81 // my number for bulk_in getted in /proc/bus/usb/devices in section E
#define MAX_PKT_SIZE 4
 
#define USB_INFO KERN_INFO "MY:"
 
static struct usb_device *device;
static struct usb_class_driver class;
static unsigned char int_buf[MAX_PKT_SIZE];
 
static int mouse_open(struct inode *i, struct file *f)
{
    return 0;
}
static int mouse_close(struct inode *i, struct file *f)
{
    return 0;
}
static ssize_t mouse_read(struct file *f, char __user *buf, size_t cnt, loff_t *off)
{
    int retval;
    int read_cnt;
 
    /* Read the data from the bulk endpoint */
    retval = usb_interrupt_msg(device, usb_rcvintpipe(device, INT_EP_IN),
            int_buf, MAX_PKT_SIZE, &read_cnt, 10);
    if (retval)
    {
        printk(USB_INFO "Int message returned %d\n", retval);
        return retval;
    }
    if (copy_to_user(buf, int_buf, read_cnt))
    {
        return -EFAULT;
    }
 
    //return MIN(cnt, read_cnt);
    return read_cnt;
}
 
static struct file_operations fops =
{
    .open = mouse_open,
    .release = mouse_close,
    .read = mouse_read,
};
 
static int mouse_probe(struct usb_interface *interface, const struct usb_device_id *id)
{
    struct usb_host_interface *iface_desc;
    struct usb_endpoint_descriptor *endpoint;
    int i,retval;
 
    iface_desc = interface->cur_altsetting;
    printk(USB_INFO "Pen i/f %d now probed: (%04X:%04X)\n",
            iface_desc->desc.bInterfaceNumber,
            id->idVendor, id->idProduct);
    printk(USB_INFO "ID->bNumEndpoints: %02X\n",
            iface_desc->desc.bNumEndpoints);
    printk(USB_INFO "ID->bInterfaceClass: %02X\n",
            iface_desc->desc.bInterfaceClass);
 
    for (i = 0; i < iface_desc->desc.bNumEndpoints; i++)
    {
        endpoint = &iface_desc->endpoint[i].desc;
 
        printk(USB_INFO "ED[%d]->bEndpointAddress: 0x%02X\n",
                i, endpoint->bEndpointAddress);
        printk(USB_INFO "ED[%d]->bmAttributes: 0x%02X\n",
                i, endpoint->bmAttributes);
        printk(USB_INFO "ED[%d]->wMaxPacketSize: 0x%04X (%d)\n",
                i, endpoint->wMaxPacketSize,
                endpoint->wMaxPacketSize);
    }
 
    device = interface_to_usbdev(interface);
 
    class.name = "usb/mouse%d";
    class.fops = &fops;
    if ((retval = usb_register_dev(interface, &class)) < 0)
    {
        /* Something prevented us from registering this driver */
        printk(USB_INFO "Not able to get a minor for this device.");
    }
    else
    {
        printk(USB_INFO "Minor obtained: %d\n", interface->minor);
    }
 
    return retval;
}
 
static void mouse_disconnect(struct usb_interface *interface)
{
    printk(USB_INFO "Mouse i/f %d now disconnected\n",
            interface->cur_altsetting->desc.bInterfaceNumber);
 
    usb_deregister_dev(interface, &class);
}
 
/* Table of devices that work with this driver */
static struct usb_device_id mouse_table[] =
{
    { USB_DEVICE(0x046d, 0xc05a) }, // idVendor & idProduct of my mouse
    {} /* Terminating entry */
};
MODULE_DEVICE_TABLE (usb, mouse_table);
 
static struct usb_driver mouse_driver =
{
    .name = "mouse_driver",
    .probe = mouse_probe,
    .disconnect = mouse_disconnect,
    .id_table = mouse_table,
};
 
static int __init mouse_init(void)
{
    int result;
 
    /* Register this driver with the USB subsystem */
    if ((result = usb_register(&mouse_driver)))
    {
        printk(USB_INFO "usb_register failed. Error number %d", result);
    }
    return result;
}
 
static void __exit mouse_exit(void)
{
    /* Deregister this driver with the USB subsystem */
    usb_deregister(&mouse_driver);
}
 
module_init(mouse_init);
module_exit(mouse_exit);
 
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Maksim Skopin");
MODULE_DESCRIPTION("USB Mouse Device Driver");
Вот вывод в kernelspace:

Bash
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
MY:Int message returned -110
MY:Int message returned -110
MY:Int message returned -110
MY:Int message returned -110
MY:Int message returned -110
MY:Int message returned -110
MY:Int message returned -110
MY:Int message returned -110
MY:Int message returned -110
MY:Int message returned -110
MY:Int message returned -110
MY:Int message returned -110
MY:Int message returned -110
MY:Int message returned -110
MY:Int message returned -110
MY:Int message returned -110
MY:Int message returned -110
MY:Int message returned -110
MY:Int message returned -110
MY:Int message returned -110
[root@mskopin user_mode]#
Вот код userspace:

C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
//#include <assert.h>
 
#define COUNT 4
 
int main()
{
    int fd;
    static unsigned char read_buffer[COUNT];
    int count,x=0,y=0;
    
    fd=open("/dev/mouse0", O_RDWR);
    if(fd<0){
        printf("Open error occured!\n");
    }
 
    while(1){
        memset (read_buffer,0,COUNT);
        
        count = read(fd,read_buffer,COUNT);
        if(count<0){
            //printf("ERROR of read! Readed %d\n",count);
        }else{
            printf("Read is OK! Readed read_bufer[0]=%d,read_buffer[1]=%d,read_buffer[2]=%d\n",read_buffer[0],read_buffer[1],read_buffer[2]);
        }
 
        if(count>0){
            y+=read_buffer[2];
            x+=read_buffer[1];
            printf("x=%d,y=%d\n",x,y);
        }
        //printf("%c[%d;%df",0x1B,y,x); // gotoxy implementation
        //printf("%c[%d;%d;%dm %s ",'\e',0,97,40,"+"); // cursor
    }
    close(fd);
 
    return 0;
 
}
Вот его вывод:

Bash
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Read is OK! Readed read_bufer[0]=0,read_buffer[1]=0,read_buffer[2]=0
x=0,y=0
Read is OK! Readed read_bufer[0]=0,read_buffer[1]=0,read_buffer[2]=0
x=0,y=0
Read is OK! Readed read_bufer[0]=0,read_buffer[1]=0,read_buffer[2]=0
x=0,y=0
Read is OK! Readed read_bufer[0]=0,read_buffer[1]=0,read_buffer[2]=0
x=0,y=0
Read is OK! Readed read_bufer[0]=0,read_buffer[1]=0,read_buffer[2]=0
x=0,y=0
Read is OK! Readed read_bufer[0]=0,read_buffer[1]=0,read_buffer[2]=0
x=0,y=0
Read is OK! Readed read_bufer[0]=0,read_buffer[1]=0,read_buffer[2]=0
x=0,y=0
Read is OK! Readed read_bufer[0]=0,read_buffer[1]=0,read_buffer[2]=0
x=0,y=0
Read is OK! Readed read_bufer[0]=0,read_buffer[1]=0,read_buffer[2]=0
x=0,y=0
^Z
[5]+  Stopped                 ./mymouse3_user
[root@mskopin user_mode]#
0
IT_Exp
Эксперт
34794 / 4073 / 2104
Регистрация: 17.06.2006
Сообщений: 32,602
Блог
13.08.2016, 18:31
Ответы с готовыми решениями:

Почему срабатывает for?
#include &lt;stdio.h&gt; #include &lt;conio.h&gt; long int fact ( int N ) { int f, i; for ( i=1, f=1 ; i &lt;= N ; i++ ) f*=i; return...

Почему не срабатывает?
Почему следующий код работает: &lt;?php if (($_SERVER !== '/') || ($_SERVER !== '/index.php')): ?&gt; &lt;style type=&quot;text/css&quot;&gt; ...

Почему не срабатывает SetTimeout?
Здравствуйте, почему функция срабатывает сразу после клика, а не через 3 секунды после? setTimeout($(function(){ ...

6
92 / 19 / 4
Регистрация: 11.04.2015
Сообщений: 1,019
Записей в блоге: 1
14.08.2016, 20:26  [ТС]
Как оказалось, ошибка -110 ( ETIMEDOUT) в выводе драйвера несущественна. Она просто означает, что мышка не двигалась. Когда мышка движется, функция usb_interrupt_msg() возвращает 0 и данные пересылаются.

Но пересылаются в буффер только нули. Может кто подсказать почему? Не могу понять...

Вот новый код драйвера:

C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/usb.h>
//#include <linux/input.h>
 
#define MIN(a,b) (((a) <= (b)) ? (a) : (b))
#define INT_EP_IN 0x81 // my number for bulk_in getted in /proc/bus/usb/devices in section E
#define MAX_PKT_SIZE 4
 
#define USB_INFO KERN_INFO "MY:"
 
static struct usb_device *device;
static struct usb_class_driver class;
static unsigned char int_buf[MAX_PKT_SIZE];
 
static int mouse_open(struct inode *i, struct file *f)
{
    return 0;
}
static int mouse_close(struct inode *i, struct file *f)
{
    return 0;
}
static ssize_t mouse_read(struct file *f, char __user *buf, size_t cnt, loff_t *off)
{
    int retval;
    int read_cnt;
 
    /* Read the data from the bulk endpoint */
    retval = usb_interrupt_msg(device, usb_rcvintpipe(device, INT_EP_IN),
            int_buf, MAX_PKT_SIZE, &read_cnt, 10);
    if (!retval){
        printk(USB_INFO "Int message returned retval=%d,int_buf[0]=%d,int_buf[1]=%d,int_buf[2]=%d\n", 
                retval,int_buf[0],int_buf[1],int_buf[2]);
        if (copy_to_user(buf, int_buf, read_cnt)){
            return -EFAULT;
        }
        return retval;
    }
    
 
    //return MIN(cnt, read_cnt);
    return -1;
}
 
static struct file_operations fops =
{
    .open = mouse_open,
    .release = mouse_close,
    .read = mouse_read,
};
 
static int mouse_probe(struct usb_interface *interface, const struct usb_device_id *id)
{
    struct usb_host_interface *iface_desc;
    struct usb_endpoint_descriptor *endpoint;
    int i,retval;
 
    iface_desc = interface->cur_altsetting;
    printk(USB_INFO "Pen i/f %d now probed: (%04X:%04X)\n",
            iface_desc->desc.bInterfaceNumber,
            id->idVendor, id->idProduct);
    printk(USB_INFO "ID->bNumEndpoints: %02X\n",
            iface_desc->desc.bNumEndpoints);
    printk(USB_INFO "ID->bInterfaceClass: %02X\n",
            iface_desc->desc.bInterfaceClass);
 
    for (i = 0; i < iface_desc->desc.bNumEndpoints; i++)
    {
        endpoint = &iface_desc->endpoint[i].desc;
 
        printk(USB_INFO "ED[%d]->bEndpointAddress: 0x%02X\n",
                i, endpoint->bEndpointAddress);
        printk(USB_INFO "ED[%d]->bmAttributes: 0x%02X\n",
                i, endpoint->bmAttributes);
        printk(USB_INFO "ED[%d]->wMaxPacketSize: 0x%04X (%d)\n",
                i, endpoint->wMaxPacketSize,
                endpoint->wMaxPacketSize);
    }
 
    device = interface_to_usbdev(interface);
 
    class.name = "usb/mouse%d";
    class.fops = &fops;
    if ((retval = usb_register_dev(interface, &class)) < 0)
    {
        /* Something prevented us from registering this driver */
        printk(USB_INFO "Not able to get a minor for this device.");
    }
    else
    {
        printk(USB_INFO "Minor obtained: %d\n", interface->minor);
    }
 
    return retval;
}
 
static void mouse_disconnect(struct usb_interface *interface)
{
    printk(USB_INFO "Mouse i/f %d now disconnected\n",
            interface->cur_altsetting->desc.bInterfaceNumber);
 
    usb_deregister_dev(interface, &class);
}
 
/* Table of devices that work with this driver */
static struct usb_device_id mouse_table[] =
{
    { USB_DEVICE(0x046d, 0xc05a) }, // idVendor & idProduct of my mouse
    {} /* Terminating entry */
};
MODULE_DEVICE_TABLE (usb, mouse_table);
 
static struct usb_driver mouse_driver =
{
    .name = "mouse_driver",
    .probe = mouse_probe,
    .disconnect = mouse_disconnect,
    .id_table = mouse_table,
};
 
static int __init mouse_init(void)
{
    int result;
 
    /* Register this driver with the USB subsystem */
    if ((result = usb_register(&mouse_driver)))
    {
        printk(USB_INFO "usb_register failed. Error number %d", result);
    }
    return result;
}
 
static void __exit mouse_exit(void)
{
    /* Deregister this driver with the USB subsystem */
    usb_deregister(&mouse_driver);
}
 
module_init(mouse_init);
module_exit(mouse_exit);
 
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Maksim Skopin");
MODULE_DESCRIPTION("USB Mouse Device Driver");
Вот вывод в dmesg:

Bash
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
MY:Int message returned retval=0,int_buf[0]=0,int_buf[1]=0,int_buf[2]=0
MY:Int message returned retval=0,int_buf[0]=0,int_buf[1]=0,int_buf[2]=0
MY:Int message returned retval=0,int_buf[0]=0,int_buf[1]=0,int_buf[2]=0
MY:Int message returned retval=0,int_buf[0]=0,int_buf[1]=0,int_buf[2]=0
MY:Int message returned retval=0,int_buf[0]=0,int_buf[1]=0,int_buf[2]=0
MY:Int message returned retval=0,int_buf[0]=0,int_buf[1]=0,int_buf[2]=0
MY:Int message returned retval=0,int_buf[0]=0,int_buf[1]=0,int_buf[2]=0
MY:Int message returned retval=0,int_buf[0]=0,int_buf[1]=0,int_buf[2]=0
MY:Int message returned retval=0,int_buf[0]=0,int_buf[1]=0,int_buf[2]=0
MY:Int message returned retval=0,int_buf[0]=0,int_buf[1]=0,int_buf[2]=0
MY:Int message returned retval=0,int_buf[0]=0,int_buf[1]=0,int_buf[2]=0
MY:Int message returned retval=0,int_buf[0]=0,int_buf[1]=0,int_buf[2]=0
MY:Int message returned retval=0,int_buf[0]=0,int_buf[1]=0,int_buf[2]=0
MY:Int message returned retval=0,int_buf[0]=0,int_buf[1]=0,int_buf[2]=0
MY:Int message returned retval=0,int_buf[0]=0,int_buf[1]=0,int_buf[2]=0
MY:Int message returned retval=0,int_buf[0]=0,int_buf[1]=0,int_buf[2]=0
MY:Int message returned retval=0,int_buf[0]=0,int_buf[1]=0,int_buf[2]=0
MY:Int message returned retval=0,int_buf[0]=0,int_buf[1]=0,int_buf[2]=0
MY:Int message returned retval=0,int_buf[0]=0,int_buf[1]=0,int_buf[2]=0
MY:Int message returned retval=0,int_buf[0]=0,int_buf[1]=0,int_buf[2]=0
[root@mskopin user_mode]# echo "1-1.3:1.0" > /sys/bus/usb/drivers/mouse_driver/unbind
[root@mskopin user_mode]# echo "1-1.3:1.0" > /sys/bus/usb/drivers/usbhid/bind
[root@mskopin user_mode]#
0
Почетный модератор
 Аватар для Humanoid
11559 / 4353 / 453
Регистрация: 12.06.2008
Сообщений: 12,455
14.08.2016, 22:16
Цитата Сообщение от max_sk Посмотреть сообщение
usb_interrupt_msg
А оно вообще умеет на приём работать? В доке сказано:
This function sends a simple interrupt message to a specified endpoint and waits for the message to complete, or timeout.
...поэтому есть некоторые сомнения. Меня почему-то тянет посмотреть в сторону usb_submit_urb(), но с уверенностью сказать не могу.
1
92 / 19 / 4
Регистрация: 11.04.2015
Сообщений: 1,019
Записей в блоге: 1
15.08.2016, 12:50  [ТС]
Ладно, подумаю над этим...

Добавлено через 14 часов 24 минуты
Нет, функцию вроде менять не надо, судя по этой статье:
http://dmilvdv.narod.ru/Transl... _urbs.html
Просто я заменил usb_bulk_msg на usb_interrupt_msg. Эти функции индентичны но предназначены для получения и отправки данных от устройств с разными скоростями - usb_bulk_msg для флешек и других mass storage, а usb_interrupt_msg для более медленных мыши и клавиатуры.

Как оказалось, массив int_buf нужно было зарезервировать в памяти через kmalloc (). И тогда данные стали приходить. Но опять же, эти данные не меняются и вообще, непонятно, что это за данные. Я думал это координаты курсора... :-(
0
Почетный модератор
 Аватар для Humanoid
11559 / 4353 / 453
Регистрация: 12.06.2008
Сообщений: 12,455
16.08.2016, 00:33
Цитата Сообщение от max_sk Посмотреть сообщение
Я думал это координаты курсора
Там смещение курсора относительно предыдущего состояния по каждой из осей + состояние кнопок. Например, мышку сдвинули и она передаёт на компьютер что-то вроде: x -5, y +8, кнопки отпущены. А ОС уже сдвигает курсор на 5 пикселей влево и на 8 вверх.
1
92 / 19 / 4
Регистрация: 11.04.2015
Сообщений: 1,019
Записей в блоге: 1
16.08.2016, 13:20  [ТС]
Спасибо, Humanoid, мне сейчас попалась книга про драйвера для Линукса Джонатана Корбета и двух соавторов, третье издание, на русском языке. Там досконально описано, как написать драйвер для USB-устройства. Буду вечером переделывать и смотреть, что получится...
0
92 / 19 / 4
Регистрация: 11.04.2015
Сообщений: 1,019
Записей в блоге: 1
17.08.2016, 21:46  [ТС]
УРА!!! ПОЛУЧИЛОСЬ!!!
Оказывается, что int_buf нужно было задавать через поинтер, и соответственно kmalloc тоже делать через поинтер

Данные успешно считываются и меняются, осталось только курсор присобачить в userspace.
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
BasicMan
Эксперт
29316 / 5623 / 2384
Регистрация: 17.02.2009
Сообщений: 30,364
Блог
17.08.2016, 21:46
Помогаю со студенческими работами здесь

Почему не срабатывает импортёр?
Дошли руки (нашлась задача) до DXL. Экспортирую док-т в поток, парсю его SAXParser-ом, пытаюсь импортировать обратно. Импортёр говорит...

Почему не срабатывает ScrollViewer
Доброго времени суток. не пойму почему не срабатывает ScrollViewer всё перепробовал. Заранее спасибо! &lt;Grid...

Почему не срабатывает return?
всем привет. подскажите почему метод не завершается по return? public void depthFirstSearch(int rootVertex) { ...

Почему не срабатывает инкремент?
В комментарии показал вывод программы. Все переменные почему равны 2 Получается инкремент не оказывает никакого влияния? int a=0,...

Почему не срабатывает z-index?
Верхний серый блок перекрылся белым средним, а нижний серый нет. Кто подскажет? Сайт http://ifb79318.bget.ru/ Спасибо.


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
7
Ответ Создать тему
Новые блоги и статьи
Отправка уведомления на почту при изменении наименования справочника
Maks 24.03.2026
Программная отправка письма электронной почты на примере изменения наименования типового справочника "Склады" в конфигурации БП3. Перед реализацией необходимо выполнить настройку системной учетной. . .
модель ЗдравоСохранения 5. Меньше увольнений- больше дохода!
anaschu 24.03.2026
Теперь система здравосохранения уменьшает количество увольнений. 9TO2GP2bpX4 a42b81fb172ffc12ca589c7898261ccb/ https:/ / rutube. ru/ video/ a42b81fb172ffc12ca589c7898261ccb/ Слева синяя линия -. . .
Midnight Chicago Blues
kumehtar 24.03.2026
Такой Midnight Chicago Blues, знаешь?. . Когда вечерние улицы становятся ночными, а ты не можешь уснуть. Ты идёшь в любимый старый бар, и бармен наливает тебе виски. Ты смотришь на пролетающие. . .
SDL3 для Desktop (MinGW): Вывод текста со шрифтом TTF с помощью библиотеки SDL3_ttf на Си и C++
8Observer8 24.03.2026
Содержание блога Финальные проекты на Си и на C++: finish-text-sdl3-c. zip finish-text-sdl3-cpp. zip
Жизнь в неопределённости
kumehtar 23.03.2026
Жизнь — это постоянное существование в неопределённости. Например, даже если у тебя есть список дел, невозможно дойти до точки, где всё окончательно завершено и больше ничего не осталось. В принципе,. . .
Модель здравоСохранения: работники работают быстрее после её введения.
anaschu 23.03.2026
geJalZw1fLo Корпорация до введения программа здравоохранения имела много невыполненных работниками заданий, после введения программы количество заданий выросло. Но на выплатах по больничным это. . .
Контроль уникальности заводского номера
Maks 23.03.2026
Алгоритм контроля уникальности заводского (или серийного) номера на примере нетипового документа выдачи шин для спецтехники с табличной частью, разработанного в конфигурации КА2. Номеклатура. . .
Хочу заставить корпорации вкладываться в здоровье сотрудников: делаю мат модель здравосохранения
anaschu 22.03.2026
e7EYtONaj8Y Z4Tv2zpXVVo https:/ / github. com/ shumilovas/ med2. git
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru