Я пишу драйвер ядра Linux для пользовательского USB-устройства, которое будет использовать массовые конечные точки, все, кажется, работает нормально, однако я получаю очень медленные скорости передачи данных.В частности, для записи и чтения данных объемом 10 МБ требуется ~ 25 секунд.Я попробовал это на встроенной системе и на виртуальной машине Linux, работающей на приемлемом ПК с похожими результатами.
Я использую комплект разработки EZ-USB FX2 от Cypress в качестве целевой платы.Он работает с встроенным программным обеспечением, которое устанавливает две входные и две конечные точки.Каждая конечная точка имеет двойную буферизацию и поддерживает 512-байтовые окна.Микропрограмма опрашивает конечные точки с помощью цикла while (1) в main (), без спящего режима, и копирует данные из оконечных точек в конечные точки, когда эти данные доступны с помощью автопойнтеров.Мне сказали, что это может справедливо перемещать данные в Windows с помощью их конкретного приложения, но у меня не было возможности проверить это.
Мой код (соответствующие части ниже) вызывает функцию под названием bulk_io в процедуре проверки устройства.Эта функция создает число (URB_SETS) выходных urb-адресов, которые пытаются записать 512 байт на устройство.Изменение этого числа от 1 до 32 не влияет на производительность.Все они копируют из одного и того же буфера.Обработчик обратного вызова для каждой операции записи в конечную точку out используется для создания urb для чтения в соответствующей конечной точке.Обратный вызов чтения создает другой urb-адрес записи, пока я не достигу общего числа запросов на запись / чтение, которые я хочу выполнить за один раз (20 000).Сейчас я работаю над тем, чтобы перенести большинство операций в функциях обратного вызова в нижние половины, если они блокируют другие прерывания.Я также думаю о переписывании встроенного программного обеспечения для Cypress FX2, чтобы использовать прерывания вместо опроса.Есть ли здесь что-нибудь необычное, чтобы сделать производительность настолько низкой?Заранее спасибо.Пожалуйста, дайте мне знать, если вы хотите увидеть больше кода, это всего лишь простой драйвер для тестирования ввода-вывода Cypress FX2.
Это функция обратного вызова записи конечной точки:
static void bulk_io_out_callback0(struct urb *t_urb) {
// will need to make this work with bottom half
struct usb_dev_stat *uds = t_urb->context;
struct urb *urb0 = usb_alloc_urb(0,GFP_KERNEL);
if (urb0 == NULL) {
printk("bulk_io_out_callback0: out of memory!");
}
usb_fill_bulk_urb(urb0, interface_to_usbdev(uds->intf), usb_rcvbulkpipe(uds->udev,uds->ep_in[0]), uds->buf_in, uds->max_packet, bulk_io_in_callback0, uds);
usb_submit_urb(urb0,GFP_KERNEL);
usb_free_urb(urb0);
}
Это функция обратного вызова чтения в конечной точке:
static void bulk_io_in_callback0(struct urb *t_urb) {
struct usb_dev_stat *uds = t_urb->context;
struct urb *urb0 = usb_alloc_urb(0,GFP_KERNEL);
if (urb0 == NULL) {
printk("bulk_io_out_callback0: out of memory!");
}
if (uds->seq--) {
usb_fill_bulk_urb(urb0, interface_to_usbdev(uds->intf), usb_sndbulkpipe(uds->udev,uds->ep_out[0]), uds->buf_out, uds->max_packet, bulk_io_out_callback0, uds);
usb_submit_urb(urb0,GFP_KERNEL);
}
else {
uds->t1 = get_seconds();
uds->buf_in[9] = 0; // to ensure we only print the first 8 chars below
printk("bulk_io_in_callback0: completed, time=%lds, bytes=%d, data=%s\n", (uds->t1-uds->t0), uds->max_packet*SEQ, uds->buf_in);
}
usb_free_urb(urb0);
}
Эта функция вызывается для установки начальных URL:
static int bulk_io (struct usb_interface *interface, struct usb_dev_stat *uds) {
struct urb *urb0;
int i;
uds->t0 = get_seconds();
memcpy(uds->buf_out,"abcd1234",8);
uds->seq = SEQ; // how many times we will run this
printk("bulk_io: starting up the stream, seq=%ld\n", uds->seq);
for (i = 0; i < URB_SETS; i++) {
urb0 = usb_alloc_urb(0,GFP_KERNEL);
if (urb0 == NULL) {
printk("bulk_io: out of memory!\n");
return(-1);
}
usb_fill_bulk_urb(urb0, interface_to_usbdev(uds->intf), usb_sndbulkpipe(uds->udev,uds->ep_out[0]), uds->buf_out, uds->max_packet, bulk_io_out_callback0, uds);
printk("bulk_io: submitted urb, status=%d\n", usb_submit_urb(urb0,GFP_KERNEL));
usb_free_urb(urb0); // we don't need this anymore
}
return(0);
}
Редактировать 1 Я проверил, что udev-> speed == 3, поэтому USB_SPEED_HIGH, то есть это не потому, что Linux думает, что это медленное устройство ....
Редактировать 2 Я переместил все вобратные вызовы, связанные с созданием urb (kmalloc, submit) и освобождением в нижние половины, та же производительность.