OSDev

для всех
Текущее время: 29 мар 2024, 18:06

Часовой пояс: UTC + 3 часа




Начать новую тему Ответить на тему  [ Сообщений: 2 ] 
Автор Сообщение
 Заголовок сообщения: UHCI
СообщениеДобавлено: 03 ноя 2014, 03:22 

Зарегистрирован: 31 окт 2011, 18:20
Сообщения: 230
Привет всем.
Балуюсь тут с UHCI. Пока просто пытаюсь из тестовой библиотечки получать дескрипторы устройств в корневом хабе. На qemu после долгих тряхомудий все наконец завелось, а на VMWare - никак.
Контроллер конфигурируется как по маслу, никаких ошибок не замечено. Порты тоже благополучно ресетятся и включаются. К моменту начала передач у порта состояние 0x85, как и должно быть. Обращаюсь к портам по очереди.
При передаче пакета SetAddress (первый же пакет) VMWare успешно берет фрейм, проходит по очереди (в очереди пока 1 указатель на TD), находит первый TD и после этого ставит в нем биты CRC/Timeout error и STALL, зануляет длину пакета и все. Сам контроллер не падает и продолжает нормальную работу. Ему можно в очередь добавить еще новые TD и получить еще кучу STALL.
Раз контроллер регулярно успешно доходит до TD, значит сам контроллер работает и находит все структурки в памяти. Получается, ошибка уже непосредственно при передаче буфера устройству. Но при передаче токен сформирован нормально (т.к. работает на qemu, да и руками все биты проверял), статус тоже.
Писал по образу кода kolibri (не идеал конечно, но все же рабочий код). У меня сейчас почти один-в-один (за исключением того, что у меня пока тестовый код, вместо прерываний там циклы ожидания).
Формирование первого же падающего SETUP токена:
((8 - 1) << 21) | (0 << 19) | 0x2D = E0002D. Что на vmware, что на qemu. 8 - размер данных для SETUP передачи, 0 - fullSpeed (этот 0 читается из порта устройства), 2d - константа из док.
Формирование статуса:
(1 << 23) | (3 << 27) | (0 << 26) | 0x7FF = 188007FF. 1 - ACTIVE флаг, 3 - допустимое кол-во ошибок, 0 - DATA0, 7FF - нулевой текущий размер (по докам это поле только для результата контроллера, так что на него пофиг).
В буфере (8 байт) лежит константа
(USB_SET_ADDRESS << 8) | (curAddr << 16) = 0x10500, т.к. нулевой байт для битмапа типа данных и в данном случае должен быть 0.
Говорят (не в доках, а просто люди), буфер должен не пересекать границу в 4 кб. У меня буфер в стеке, адрес 0x3fff30 и размером 8 байт.
Все константы я вывел непосредственно из TD перед постановкой его в очередь.
В чем может быть проблема?

На всякий случай, инициализация контроллера и перебор портов (почти совпадает с кодом kolibri).
Код:
   // UHCI Init
   uint16_t cfg;
   uint16_t ioBase, ioSize;
   SUsbUHCIContext *uhci;
   ioBase = ReadPCIReg(pci, 0x20, PCI_SIZE_WORD) & (~3);    // get IO base
   uint16_t cmd = _inpw(ioBase + UHCI_USBCMD) & (~1);
   _outpw(ioBase + UHCI_USBCMD, cmd);  // stop controller
   _outpw(ioBase + UHCI_USBINTR, 0);      // disable interrupts
   WritePCIReg(pci, UHCI_USBLEGSUP, PCI_SIZE_WORD, UHCI_USBLEGSUP_RWC); // disable bios handling
   cfg = ReadPCIReg(pci, 4, PCI_SIZE_WORD);
   WritePCIReg(pci, 4, PCI_SIZE_WORD, cfg & (~1));    // disable IO access
   WritePCIReg(pci, 0x20, PCI_SIZE_WORD, -1);
   ioSize = ~(ReadPCIReg(pci, 0x20, PCI_SIZE_WORD) & (~3)) + 1;  // get IO size
   WritePCIReg(pci, 0x20, PCI_SIZE_WORD, ioBase);    // restore state
   WritePCIReg(pci, 4, PCI_SIZE_WORD, cfg | 5);      // enable IO and bus mastering
   _outpw(ioBase + UHCI_USBCMD, UHCI_USBCMD_HCRESET); // reset device
   dummySleep(10);
   // reset all registers
   uint32_t ioEnd = ioBase + ioSize;
   uint32_t tbase = ioBase + UHCI_PORT1STATUS;
   while (tbase < ioEnd)
   {
      int16_t val = _inpw(tbase);
      if (val == 0xFFFF || !(val & 0x80))
         break;
      _outpw(tbase, 0);
      tbase += 2;
   }
   uint16_t portCnt = (tbase - (ioBase + UHCI_PORT1STATUS)) >> 1;
   if (!portCnt)
   {
      DSPrint("No ports on UHCI controller");
      return 0;
   }
   _outpw(ioBase + UHCI_STATUSREG, 0x3f); // clear status
   _outpw(ioBase + UHCI_FRAMENUM, 0);      // clear frame number

   uhci = createUHCI(ioBase, portCnt);
   uint32_t uhciBase = GetPhysAddr(uhci);
   _outpd(ioBase + UHCI_BASEADDRES, uhciBase);  // set base address
   _outpw(ioBase + UHCI_SOFMODITFY, 64);
   _outpw(ioBase + UHCI_USBCMD, 0xC1);     // start: out Run + Configured + MaxPacket is 64 bytes
   dummySleep(10);
   // loop through ports
   for (uint64_t i = 0; i < uhci->portCnt; ++i)
   {
      uint16_t curPort = uhci->ioBase + UHCI_PORT1STATUS + i * 2;
      uint16_t pstat = _inpw(curPort);
      if ((pstat & 0x0A) == 0)
         continue;
      // clear status and read again
      _outpw(curPort, pstat);
      uint16_t stat = _inpw(curPort);
      if ((pstat & 2) != 0)
      {
         // state changed
         if (stat & 1)
         {
            //DSPrint("Device connected");
            uhci->portStats |= (1 << i);
            _outpw(curPort, stat | 2);      // reset port
            dummySleep(10);
            uint16_t resSt = _inpw(curPort);
            _outpw(curPort, resSt & (~(2 << 8))); // clear reset
            dummySleep(1);
            resSt = _inpw(curPort); // enable port, clear status change
            _outpw(curPort, resSt | 6);
            dummySleep(1);
            resSt = _inpw(curPort);
            if ((resSt & 1) == 0)
               DSPrint("Port disabled after reset");
            bool isLowspeed = (resSt >> 8) & 1;
            uhciNewDevice(uhci, isLowspeed);
         }
         else
         {
            DSPrint("Device disconnected");
            uhci->portStats &= ~(1 << i);
         }
      }
      else
      {
         // state not changed
         if ((pstat & 8) && !(stat & 4))
            DSPrint("usb port disabled");
      }
   }
...
void uhciNewDevice(SUsbUHCIContext *uhci, bool isLowspeed)
{
   uint64_t curAddr = usbAddr++;
   SChannel *chan = createChannel(uhci, 0, 0, 64, USB_PIPE_CONTROL, isLowspeed);
   insertChannel(chan);
   char config[8];
   *((uint64_t *)config) = (USB_SET_ADDRESS << 8) | (curAddr << 16);
   usbControlTransfer(chan, (uint64_t *)config, 0, 0);
   while (chan->lastTD->prev)
   {
      completeTransfers(chan);
      dummySleep(10);
   }
}
...
void usbControlTransfer(SChannel *chan, uint64_t *config, void *buf, uint16_t size)
{
   addTransfer(chan, config, 8, 0, 0);
   addTransfer(chan, 0, 0, 1, 1);
}

На реальном железе пока нет возможности проверить.
Нашел на английском форуме осдев частично похожую проблему, но там никаких ответов нет уже 4 года.
Кстати, тег [SPOILER] возможно не помешал бы на форуме.


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: UHCI
СообщениеДобавлено: 03 ноя 2014, 17:20 

Зарегистрирован: 31 окт 2011, 18:20
Сообщения: 230
Ошибка нашлась.
Код:
_outpw(curPort, stat | 2);      // reset port

Забыл сдвинуть эту двойку на 8, и вместо RESET'а писал в Connect status changed. Поменял на
Код:
_outpw(curPort, stat | (2 << 8));      // reset port

И вроде заработало.
Что интересно, в спеках USB по подключению устройства написано так:
Цитата:
1. The hub to which the device is now attached informs the host of the event via a reply on its
status change pipe (refer to Section 10.13.1). At this point, the device has been reset, is in the
Default state and the port to which it is attached is enabled and ready to respond to control
transfer requests on the default control pipe.
2. The host determines the exact nature of the change by querying the hub.
3. Now that the host knows the port to which the new device has been attached, the host then may
reset the device again if it wishes, but it is not required to do so.


а в спеках UHCI написано, что цель ресета порта такая:
Цитата:
When in the Reset State, the port is disabled and sends the USB Reset signaling.

Но как обычно, всем пофиг на документацию.


Вернуться к началу
 Профиль  
 
Показать сообщения за:  Поле сортировки  
Начать новую тему Ответить на тему  [ Сообщений: 2 ] 

Часовой пояс: UTC + 3 часа


Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 4


Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете добавлять вложения

Найти:
Перейти:  
cron
Создано на основе phpBB® Forum Software © phpBB Group
Русская поддержка phpBB