Jump to content

    

AT91RM9200 USB Host OHCI , HID Mouse.

Есть железо на базе AT91RM9200 в котором OHCI USB host.

На выходе UHP AT91RM9200 припаян Хаб TUSB2046B.

На данный момент реализованы управляющие (Control) запросы через OHCI, которыми читаю дескрипторы и управляю портами на выходе хаба.

 

На первом этапе стоит задача подключить мышь.

При подключении мыши определяем факт подключения, настраиваем поты хаба, делаем сброс порта ну и так далее…

Читаю дескрипторы мыши и ее отчетный дескриптор все получается.

После чего

1) Устанавливаю новый адрес мыши

2) Устанавливаю ее конфигурацию (Set_Configuration)

3) Для проверки читаю конфигурацию все ОК (Get_Configuration)

4) Устанавливаю отчетный протокол (пробовал и загрузочный) (Set_Protocol)

5) Тоже его читаю (Get_Protocol)

6) После чего пытаюсь прочитать отчет, через канал управления мыши и тут ничего не получается

 

Отчет читаю через специфический запрос HID Get_Report

 

Сам запрос

     bmRequestType      =  0xA1   
     bRequest               =  0x01    //GET_REPORT  
     wValue                =  0x0100 //Тип отчета Report ID  
     wIndex                =  0x0000 //интерфейс если установить в 1 то ошибка
     wLength               =  0x0005  // Length

[/code]

 

{0xA1,0x01,0x00,0x01,0x00,0x00,0x05,0x00}

 

В ответ приходит мой Setup пакет и никаких ошибок передачи.

0xA1,0x01,0x00,0x01,0x00 что послал то и вернулось.

Функции для чтения дескрипторов и для отчета одни и те же.

Дескрипторы читаются правильно проверял снифером на ПК, а вот с отчетами не понятно.

 

 

Тот порядок настроек, который под цифрами 1-6 это последняя вариация пробовал по разному, как и по разному формировал запрос HID Get_Report.

В результате либо ошибка, а если нет ошибки, то возвращается сам запрос.

Также игрался с настройками периода передачи Set_Idle, но ничего не помогает.

 

По стандарту данные c устройства HID нужно читать через канал Interrupt IN, но так же можно через канал Control ENDP0.

Подскажите, что я делаю не так?

Share this post


Link to post
Share on other sites

Не получается ни так ни этак.

Что-то совсем запутался, уже не знаю, что и делать.

 

Пытаюсь организовать канал прерывания для чтения репорта мыши.

Подскажите, как правильно сформировать список TD для запроса данных IN прерывания?

Один из моих вариантов.

 

           // |Формируем структуру дескриптора конечной точки ED
           AT91F_CreateEd(
        (unsigned int) &pCtrlED, // ED Address
        8,              // Max packet size
        0,             // TD format
        1,             // Skip
        1,             // Speed = "Low"  ("0" <=> "Full", "1" <=> "Low")
        0x0,         // Direction (брать из поля PID Td)
        0x1,         // Endpoint Address
        FuncAddress, // Func Address
        (unsigned int) &pCtrlTD[2],    // TDQTailPointer
        (unsigned int) &pCtrlTD[0],    // TDQHeadPointer
        0,          // ToggleCarry
        0x0);     // NextED

    //Data IN
    AT91F_CreateGenTd(
        (unsigned int) &pCtrlTD[0],    // TD Address
        3,          // Data Toggle (First data packet <=> всегда DATA1)
        0x00,    // DelayInterrupt
        0x2,      // Direction ("2" <=> In)
        1,          // Buffer Rounding
        (unsigned int) buffer,   // Current Buffer Pointer (сюда принимаем данные)
        (unsigned int) &pCtrlTD[1],    // Next TD
        5);        // Buffer Length

    //Status OUT
    AT91F_CreateGenTd(
        (unsigned int) &pCtrlTD[1],    // TD Address
        3,         // Data Toggle (Status packet <=> всегда DATA1)
        0x00,    // DelayInterrupt
        0x01,    // Direction  ("1" <=> Out)
        1,          // Buffer Rounding
        0x0,      // Current Buffer Pointer
        (unsigned int) &pCtrlTD[2],   // Next TD
        0x0);    // Buffer Length

        // Этот TD хостом никогда не обрабатывается
    AT91F_CreateGenTd(
        (unsigned int) &pCtrlTD[2],    // TD Address
        3,          // Data Toggle
        0x00,    // DelayInterrupt
        0x01,    // Direction ("1" <=> Out)
        1,         // Buffer Rounding
        0x0,     // Current Buffer Pointer
        (unsigned int) 0,   // Next TD
        0x0);   // Buffer Length

 

После я подсоединяю ED к HCCA. HccaInterrruptTable[0] и установив бит PLE в HcControl.

Ждем события мыши, так как предварительно было установлено Idle = 0.

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

Но в pCtrlTD[1].control в поле CC = 5 ошибка

DEVICENOTRESPONDING Device did not respond to token (IN) or did not provide a

handshake (OUT)

 

И все от мыши больше ничего принять нельзя.

 

Перечитал кучу литературы с нулевым результатов.

 

Передача прерывания состоит из трех пакетов

Token Packet

Data Packet

H/S Packet

 

pCtrlTD[0] – это Data Packet

pCtrlTD[1] – должен быть H/S Packet (но он передается с ошибкой)

 

Кто формирует Token Packet.

Думаю, что его формирует OHCI из ED, но может его нужно указывать явно через дополнительный TD (так тоже пробовал, но ничего не получилось).

 

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

 

Share this post


Link to post
Share on other sites

Нашел два файла, где есть пример чтения репортов мыши через канал прерывания.

Правда у меня вызывает сомнение реализация функции заполнения дескриптора конечной точки.

 

void OHCI_Create_ed (tED **ed, tTD **td, u32 FA, u32 EN, u32 D, u32 S, u32 K, u32 F, u32 MPS)
{
  if (((*ed) != NULL) || ((*td) != NULL))
    rExit("OHCI_Create_ed failed.");
  (*ed) = rmalloc16B ("HC_ENDPOINT_DESCRIPTOR");
  (*ed)->Control  = 0;
  (*ed)->Control |= (FA << 0);  //7-bit
  (*ed)->Control |= (EN << 7);  //2-bit
  (*ed)->Control |= (D  << 9);  //2-bit
  (*ed)->Control |= (S  << 11); //1-bit
  (*ed)->Control |= (K  << 12); //1-bit
  (*ed)->Control |= (F  << 13); //1-bit
  (*ed)->Control |= (MPS<< 14);
  (*td) = rmalloc16B ("HC_TRANSFER_DESCRIPTOR");
  (*td)->Control  = 0;
  (*td)->CBP    = NULL;
  (*td)->NextTD = NULL;
  (*td)->BE     = NULL;
  (*ed)->TailP  = (*td);
  (*ed)->HeadP  = (*td);
  (*ed)->NextED = NULL;
}

 

Не сходятся со стандартом биты которые устанавливаются в Control

(D  << 9),(S  << 11),(K  << 12),(F  << 13),(MPS<< 14);

Должно быть так

(D  << 11),(S  << 13),(K  << 14),(F  << 15),(MPS<< 16);

 

В исходниках по тексту видно, что при, заполнении ED программист подбирал значения, но не везде.

Поэтому не понятно работоспособен ли данный пример.

 

Сделал ED, TD как в примере.

 

           // Формируем структуру дескриптора конечной точки ED
           AT91F_CreateEd(
        (unsigned int) &pED, // ED Address
        8,              // Max packet size
        0,             // TD format
        1,             // Skip
        1,             // Speed = "Low"  ("0" <=> "Full", "1" <=> "Low")
        0x0,         // Direction (брать из поля PID Td)
        0x1,         // Endpoint Address
        FuncAddress, // Func Address
        (unsigned int) &pTD[1],    // TDQTailPointer
        (unsigned int) &pTD[0],    // TDQHeadPointer
        0,            // ToggleCarry
        0x0);       // NextED

           // Формируем структуры дескрипторов передачи ТD
    //Data IN
    AT91F_CreateGenTd(
        (unsigned int) &pTD[0],   // TD Address
        3,                                     // Data Toggle (First data packet <=> всегда DATA1)
        0x00,                               // DelayInterrupt
        0x2,                                 // Direction ("2" <=> In)
        1,                                     // Buffer Rounding
        (unsigned int) buffer,       // Current Buffer Pointer (сюда принимаем данные)
        (unsigned int) &pTD[1],    // Next TD
        4);                                    // Buffer Length


           // Этот TD хостом не должен обрабатывается
    AT91F_CreateGenTd(
        (unsigned int) &pTD[1],    // TD Address
        0,                                      // Data Toggle
        0,                                      // DelayInterrupt
        0,                                      // Direction ("1" <=> Out)
        0,                                      // Buffer Rounding
        0,                                      // Current Buffer Pointer
        (unsigned int) 0,                // Next TD
        0);                                     // Buffer Length

 

Пропали ошибки.

Стал отлавливать события от мыши.

Вот только в приемном буфере одни нули.

Ладно будем разбираться дальше.

 

Файлы примера прикладываю.

ohci_example.rar

Share this post


Link to post
Share on other sites

Наметился прогресс.

Только странно это как то.

 

В спецификации сказано что ED и TD должны быть выровнены по границе 16 бит, что и делаю.

Должен ли быть выровнен приемный буфер (который указываем в регистре CBP дескриптора передачи TD) ничего не сказано (или не нашел где указано).

Выровнял приемный буфер по границе диапазона и начал принимать данные, но через раз.

 

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

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

Думаю, что в этом заключается проблема потери данных или…. (см. ниже)

 

Не понятно, что делать с DATA TOGLE (DT).

1 Переключение (DT) происходит в процессе передачи пакетов данных, которые больше чем максимальный пакет, разрешенный для устройства? (В пределах одной транзакции)

2 Данные должны переключаются каждый раз при приеме очередного пакета? (В каждой отдельной транзакции).

 

Сейчас, я читаю данные каждый раз с DT=1 предполагая, что процесс проходит по типу вопроса 1.

 

Вечером буду проверять.

 

Share this post


Link to post
Share on other sites

Решил проблему.

 

Во время передачи данных должно выполнятся переключение триггера данных DATA TOGLE.

В принципе этим занимается сам хост контроллер OHCI от нас (Драйвера Хост Контроллера) требуется только правильно начать процесс передачи данных, и дальше не мешать.

Необходимо сохранять бит C (toggleCarry) поля HeadP дескриптора конечной точки ED при начале следующей транзакции.

 

Алгоритм работы Хост Контроллера примерно такой (очень примерно).

После выполнения транзакции (передача/прием) хост контроллер сохраняет значение последнего переключения данных в бите “C” поля HeadP дескриптора конечной точки.

При этом в старшие биты ED->HeadP записывается адрес следующего дескриптора передачи (TD) и если в дескрипторе конечной точки (ED) значения старших бит поля HeadP равно TailP, то этот ED больше не будет обрабатываться хост контроллером. Можно считать что транзакция окончена.

Так же обработанный дескриптор передачи (который до этого содержался в поле HeadP дескриптора конечной точки (ED) ) будет изъят из очереди (из этой ED) и помещен в HcDoneHead.

 

Если нет ошибок, которые можно подсмотреть в битах CC поля DWord0 дескриптора передачи (TD).

Об ошибке также говорит установленный бит H (Halted) в поле HeadP дескриптора конечной точки (ED).

Другими словами если СС==0 и H == 0 можно считать, что транзакция закончена успешно.

 

Хост контроллер больше не будет обрабатывать эту связку ED TD.

Поэтому нужно сформировать новую транзакцию, для чего необходимо обновить поля дескрипторов передачи и конечной точки.

 

Нужно восстановить значения в полях

1) дескриптора передачи (TD):

CBP – здесь находится адрес приемного буфера

BE – это адрес но конец буфера (обновляем если в CPB каждый раз записываем новое значение, например организовано FIFO)

NextTD – здесь указатель на следующий дескриптор передач (в нашем случае это пустой TD)

2) дескриптора конечной точки (ED):

В HeadP - необходимо прописать указатель на TD, в котором содержится адрес приемного буфера, причем необходимо сохранить бит Toggle Carry.

 

Примерный алгоритм показан ниже.

Обновить дескрипторы необходимо после срабатывания прерывания по изъятию TD из списка ED, с перемещением его в очередь HcDoneHead.

 

AT91S_UHP_ED *pInIntED;

AT91S_UHP_TD *pInIntTD[2];

 

           
// Равенство HeadP и TailP означает, что pInIntED обработан хост контроллером
if((pInIntED->HeadP & 0xFFFFFFF0) == pInIntED->TailP)
{
    // Запрещаем обрабатывать pInIntED
    pInIntED->Control |= (1 << 14);

    // Сюда помещаем начал адреса приемного буфера
    pInIntTD[0]->CBP    = (unsigned int)fifo.get_next_inc();
    // и адрес его окончания
    pInIntTD[0]->BE     = pInIntTD[0]->CBP + (BuffLen) ? CurBufPtr + BuffLen - 1 : CurBufPtr;
    // Адрес нулевого TD
    pInIntTD[0]->NextTD = (unsigned int)pInIntTD[1];
    
    // Присваиваем указатель на TD и сохраняем значение бита “C”
    pInIntED->HeadP     = (unsigned int)pInIntTD[0] | (0x0000000E & pInIntED->HeadP);

  // Разрешаем обрабатывать pInIntED
  pInIntED->Control &= ~(1 << 14);
            flag = MOUSE_EVENT;
        
}

 

Предполагаю вопрос из первого поста должен решится правильным переключением DATA TOGLE. (Пока нет времени это проверить).

 

Ниже привожу, как заполнить дескрипторы конечной точки и передачи для транзакций IN.

 

           
  //Create a default endpoint descriptor
    AT91F_CreateEd(
        (unsigned int) pInIntED,    // ED Address
        m_nMaxPacketSize,                // Max packet size
        0,                               // TD format 0 - generic 1 - Isochronous
        1,                               // Skip Запрешаем в будующем обрабатывать хост контроллеру этот дескриптор конечной точки
        speed,                           // Speed = ("0" <=> "Full", "1" <=> "Low")
        0x02,                            // Direction IN
        enpN,                            // Номер Endpoint 
        m_wDeviceAddress,                // Адрес устройства Func Address
        (unsigned int) pInIntTD[1], // TDQTailPointer
        (unsigned int) pInIntTD[0], // TDQHeadPointer
        0,                               // ToggleCarry
        0);                              // NextED 
  //Create transfer descriptor
    //Data IN
    AT91F_CreateGenTd(
        (unsigned int) pInIntTD[0], // TD Address
        0,                               // Data Toggle (First data packet  <=> "0")
        0,                               // DelayInterrupt
        0x2,                             // Direction ("2" <=> IN)
        1,                               // Buffer Rounding
        (unsigned int) buffer,           // Current Buffer Pointer
        (unsigned int) pInIntTD[1], // Next TD
        length);                         // Buffer Length
  // Пустой дескриптор передачи
    AT91F_CreateGenTd(
        (unsigned int) pInIntTD[1], // TD Address
        0,                               // Data Toggle 
        0,                               // DelayInterrupt
        0,                               // Direction  
        0,                               // Buffer Rounding
        0,                               // Current Buffer Pointer
                0,                               // Next TD
        0);                              // Buffer Length
  // Размещаем ED в таблице прерываний 
  // При таком размещении транзакции будут генерироваться с 
  // периодом 8 mS
  HCCA.HccaInterruptTable[4]  = (unsigned int)pInIntED;
  HCCA.HccaInterruptTable[12] = (unsigned int)pInIntED;
  HCCA.HccaInterruptTable[20] = (unsigned int)pInIntED;
  HCCA.HccaInterruptTable[28] = (unsigned int)pInIntED;

    ///Устанавливаем бит "PeriodicListEnable". Остальные биты не трогаем
    AT91C_BASE_UHP->UHP_HcControl |= 0x04;
    /* ************************************************ */
    /* Generate traffic between UHP and UDP             */
    /* ************************************************ */
  //Skip == 0, т.е. "запускаем" список TD в работу
  pInIntED[idx]->Control &= ~(1 << 14);

 

Ну и до кучи

 

           
typedef struct _AT91S_UHP_ED {
    volatile unsigned int Control;
    volatile unsigned int TailP;
    volatile unsigned int HeadP;
    volatile unsigned int NextEd;
} AT91S_UHP_ED, *AT91PS_UHP_ED;
typedef struct _AT91S_UHP_TD {
    volatile unsigned int Control;
    volatile unsigned int CBP;
    volatile unsigned int NextTD;
    volatile unsigned int BE;
} AT91S_UHP_TD, *AT91PS_UHP_TD;
typedef struct _AT91S_UHP_HCCA {
    volatile unsigned int    HccaInterruptTable[32];
    volatile unsigned short  HccaFrameNumber;
    volatile unsigned short  HccaPad1;
    volatile unsigned int    HccaDoneHead;
    volatile unsigned char   reserved[116];
} AT91S_UHP_HCCA, *AT91PS_UHP_HCCA;
//*----------------------------------------------------------------------------
//* Init a pre-allocated endpoint descriptor. !!!
//* ED must be aligned on a 16 bytes boundary
//*----------------------------------------------------------------------------
__inline void AT91F_CreateEd(
    unsigned int EDAddr,
    unsigned int MaxPacket,
    unsigned int TDFormat,
    unsigned int Skip,
    unsigned int Speed,
    unsigned int Direction,
    unsigned int EndPt,
    unsigned int FuncAddress,
    unsigned int TDQTailPntr,
    unsigned int TDQHeadPntr,
    unsigned int ToggleCarry,
    unsigned int NextED)
{
    AT91PS_UHP_ED pED = (AT91PS_UHP_ED) EDAddr;
    pED->Control = (MaxPacket << 16) | (TDFormat << 15) |
                   (Skip << 14) | (Speed << 13) | (Direction << 11) |
                   (EndPt << 7) | FuncAddress;
    pED->TailP   = (TDQTailPntr & 0xFFFFFFF0);
    pED->HeadP   = (TDQHeadPntr & 0xFFFFFFF0) | (ToggleCarry << 1);
    pED->NextEd  = (NextED & 0xFFFFFFF0);
}
//*----------------------------------------------------------------------------
//* Init a pre-allocated transfer descriptor. !!!
//* TD must be aligned on a 16 bytes boundary
//*----------------------------------------------------------------------------
__inline void AT91F_CreateGenTd(
    unsigned int GenTdAddr,
    unsigned int DataToggle,
    unsigned int DelayInterrupt,
    unsigned int Direction,
    unsigned int BufRnding,
    unsigned int CurBufPtr,
    unsigned int NextTD,
    unsigned int BuffLen)
{
    AT91PS_UHP_TD pTD = (AT91PS_UHP_TD) GenTdAddr;
    pTD->Control = (DataToggle << 24) | (DelayInterrupt << 21) | (Direction << 19) | (BufRnding << 18) | 0xF0000000;
    pTD->CBP     = CurBufPtr;
    pTD->NextTD  = (NextTD & 0xFFFFFFF0);
    pTD->BE      = (BuffLen) ? CurBufPtr + BuffLen - 1 : CurBufPtr;
}

 

 

Перечитал написанное, одни ED и TD, нужно их в голове упорядочить, на что у меня ушло не мало времени.

Из информации одни стандарты и примеры типа U_Boot где голову сломать можно.

 

Отдельное спасибо автору сайта, где есть перевод стандарта правда не полный

http://microcontrollerov.net/books/OpenHCI

 

Ну и спасибо всем откликнувшимся ;)

 

 

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this