Vendor Audio Requests implementation

Discussions about USB Audio on XMOS devices
MaximLiadov
XCore Addict
Posts: 130
Joined: Mon Apr 16, 2018 9:14 am

Post by MaximLiadov »

For the moment I made 100% working code to sent data from Host to Device (with TUSBAUDIO_ClassVendorRequestOut() on host):

Code: Select all

                case USB_BMREQ_H2D_VENDOR_DEV:
                    printf("sp.bRequest = %d; wIndex = %d; wValue = %d; direction = %d\n",
                            sp.bRequest, sp.wIndex, sp.wValue, sp.bmRequestType.Direction);
                    ...
                    result = XUD_RES_OKAY;
                    XUD_DoSetRequestStatus(ep0_in);
                    break;
So now I need in a template vise versa, Device to Host. I cannot understand how to get to Endpoint0 cycle from user's code. I do not need anything, but a small template or guidance. Please, help.


User avatar
fabriceo
XCore Addict
Posts: 164
Joined: Mon Jan 08, 2018 4:14 pm

Post by fabriceo »

Hi MaximLiadov

you are coming close to it.
intercepting the Vendor request inside the endpoint0.c is simple as putting these lignes inside the switch:

Code: Select all

#if defined( VENDOR_AUDIO_REQS )

                case USB_BMREQ_H2D_VENDOR_DEV:
                case USB_BMREQ_D2H_VENDOR_DEV:
                {
                    result = vendorAudioRequests(ep0_out, ep0_in, &sp, vendorIF );

                } break;
#endif
in my case I have added an interface parameter because I want to call routines running which will be on tile0 (for example read-write in flash).

now your vendorrequest.xc should be as simple as that:

Code: Select all

// executed from within endpoint0 upon usb commands arriving in ep0
int vendorAudioRequests(XUD_ep ep0_out, XUD_ep ep0_in,
                        USB_SetupPacket_t &sp , 
                        client VENDOR_IF(vIF)   // to access information available on tile 0
) {

    unsigned char buffer[512];
    XUD_Result_t result = XUD_RES_ERR;
    unsigned datalength;
    
    xprintf("VendorRequest 0x%X, wValue=0x%X, wIndex=0x%X, length=%d, dir=%d, type=%X\n",
             sp.bRequest,        sp.wValue,   sp.wIndex,   sp.wLength, sp.bmRequestType.Direction, sp.bmRequestType.Type);

    if(sp.bmRequestType.Direction == USB_BM_REQTYPE_DIRECTION_H2D) {
        // Host to device
        // get buffer associated with request
        if (sp.wLength)
            if (XUD_GetBuffer(ep0_out, buffer, datalength) != XUD_RES_OKAY) return XUD_RES_ERR;
    } else datalength = 0;

    // treat requests
    switch( sp.bRequest ) {

        case 0x48: {
            xprintf("bRequest = 0x48\n");

                xprintf("Host to Device with 4data\n");
                xprintf("buffer[0..3] = %X %X %X %X\n",buffer[0],buffer[1],buffer[2],buffer[3]);
                result = XUD_RES_OKAY;
        } break;

        case 0x49: {
            xprintf("bRequest = 0x49\n");
                xprintf("Device to Host with 2 data\n");
                datalength = 2;
                buffer[0] = 0x34; buffer[1] = 0x12;
                result = XUD_RES_OKAY;
        } break;


        default: break;
    } // switch brequest


    if (result == XUD_RES_OKAY){
        if (sp.bmRequestType.Direction == USB_BM_REQTYPE_DIRECTION_D2H && sp.wLength != 0) {
            // send back expected data to the host
            result = XUD_DoGetRequest(ep0_out, ep0_in, buffer, datalength, sp.wLength);
        } else {
            // acknoledge command treatment
            result = XUD_DoSetRequestStatus(ep0_in); }
    }
    return result;
}
in this example there are only 2 vendor requests. 0x48 is receiving information from host (H2D) and 0x49 is sending information from host upon request.


this works great for setting parameters or pulling information.
if you need to push infromation from device to host asynchronously then you will need to implement an 'interrupt' endpoint, like the feedback clock or the hid report but I have no example for this.

br/fabriceo
User avatar
Ross
XCore Expert
Posts: 962
Joined: Thu Dec 10, 2009 9:20 pm
Location: Bristol, UK

Post by Ross »

MaximLiadov wrote: Sun Apr 09, 2023 2:58 pm Thank you very much for your guidance!

Seems to be the old code and comment are obsolete:

Code: Select all

/* If result is ERR at this point, then request to audio interface not handled - handle vendor audio reqs */
                            if(result == XUD_RES_ERR)
It doesn't work for me for vendor audio reqs, as it is placed inside audio class reqs.

The new code that works for me is:

Code: Select all

                case USB_BMREQ_H2D_VENDOR_DEV:
                case USB_BMREQ_D2H_VENDOR_DEV:
Hope that could save someone's hours of useless trials to get there from host. :)

My current question is: how to send message from device to host, outside of main XUD cycle? I.e. how to interrupt XUD from user's part of code to get into "case USB_BMREQ_D2H_VENDOR_DEV"? Any documentation link or guidance or code template would be very appriciated.
I think you misunderstand the comment/code. It would get called if you used TUSBAUDIO_AudioControlRequestSet/TUSBAUDIO_AudioControlRequestGet to a unit the code is not already aware of.
User avatar
Ross
XCore Expert
Posts: 962
Joined: Thu Dec 10, 2009 9:20 pm
Location: Bristol, UK

Post by Ross »

MaximLiadov wrote: Mon Apr 10, 2023 10:49 pm For the moment I made 100% working code to sent data from Host to Device (with TUSBAUDIO_ClassVendorRequestOut() on host):

Code: Select all

                case USB_BMREQ_H2D_VENDOR_DEV:
                    printf("sp.bRequest = %d; wIndex = %d; wValue = %d; direction = %d\n",
                            sp.bRequest, sp.wIndex, sp.wValue, sp.bmRequestType.Direction);
                    ...
                    result = XUD_RES_OKAY;
                    XUD_DoSetRequestStatus(ep0_in);
                    break;
So now I need in a template vise versa, Device to Host. I cannot understand how to get to Endpoint0 cycle from user's code. I do not need anything, but a small template or guidance. Please, help.
Fabricio is correct, you cannot instigate a EP0 request from the device. You would need some sort of interrupt EP, which would be quite a custom implementation, both in the driver and the device.. You could use add a unit properly in the Audio Class descriptors and use the existing interrupt functionality. Alternatively you could poll the device (not really in the spirit of the specs)
MaximLiadov
XCore Addict
Posts: 130
Joined: Mon Apr 16, 2018 9:14 am

Post by MaximLiadov »

Dear Fabrice, your code and explanation is just fantastic! Exactly what I am looking for. Thanks a lot! You made my day. Hope someday I could be up to your level. I will try my best. I already implemented interface inside endpoint0 like you said, to send data from tile1 to tile0. So all is not so bad. :)
Last edited by MaximLiadov on Tue Apr 11, 2023 1:36 pm, edited 1 time in total.
MaximLiadov
XCore Addict
Posts: 130
Joined: Mon Apr 16, 2018 9:14 am

Post by MaximLiadov »

Ross wrote: Tue Apr 11, 2023 12:06 pm Fabricio is correct, you cannot instigate a EP0 request from the device. You would need some sort of interrupt EP, which would be quite a custom implementation, both in the driver and the device.. You could use add a unit properly in the Audio Class descriptors and use the existing interrupt functionality. Alternatively you could poll the device (not really in the spirit of the specs)
Dear Ross, you are absolutely right! I understood it wrong, like I could interrupt EP0 with some magic way. My mistake. EP processing cycle is only for Get/Set from the host. If user have pressed some hw button or turned knob on the device, then I need to rise a flag "something happened, please read me" to the host first. Ok, got it!
MaximLiadov
XCore Addict
Posts: 130
Joined: Mon Apr 16, 2018 9:14 am

Post by MaximLiadov »

Guys, one more question if I may ask. I see FU_VOLUME_CONTROL /* Direction: Device-to-host */ in sources. I want to use it for common good. It transfers volume when the host calls a standard Audio Class request GetVolume. Right?

I do not understand, what is it for in particular? What is a typical scenario? If a device has a hardware volume knob, how could the host know that the volume was changed on the device and the host needs to update this information in the OS by calling GetVolume? When I trace the Thesycon control panel, it calls GetVolume and reads the actual volume from the device when the control panel opens the VolumeControl window. Even more strange thing: every time we change volume in Thesycon control panel or in OS, it calls a pair of functions SetVolume and right away GetVolume. Why does it read back the same volume that was sent? Is there any standard way to inform the host to call GetVolume at any moment? Can I use any standard way to control volume on the device and inform the host from the device (i.e. USB audio class, so it could work in any OS without a panel).

Strange thing is. No problem to control device volume from host, it's completely suited UAC stardard. How to do this from device to host? I cannot find any public information. I know about HID volume multimedia keybord buttons emulation from host, but I don't like this way. I want to to send to OS exact volume position, stored in the device's memory, right away. Is it possible at all? Did I miss something?
Last edited by MaximLiadov on Wed Apr 12, 2023 9:46 pm, edited 1 time in total.
User avatar
fabriceo
XCore Addict
Posts: 164
Joined: Mon Jan 08, 2018 4:14 pm

Post by fabriceo »

Hi quick reply , you have to enable HID interface and simulate a change like pushing volume + or volume - buttons and then this change will be sent via the hid report and interrupt automatically , which will trigger some getvolume request from the host. Once you have the data alligned then you release these virtual buttons. A bit tricky to implement, but thats a trick I found on some forum, to get roon certification for example :)
Anyone having solved that ?
MaximLiadov
XCore Addict
Posts: 130
Joined: Mon Apr 16, 2018 9:14 am

Post by MaximLiadov »

I also asked this question Thesycon guys. Might be they know something.
MaximLiadov
XCore Addict
Posts: 130
Joined: Mon Apr 16, 2018 9:14 am

Post by MaximLiadov »

Here is my Vendor Request implementation. Now the drivers panel is much more informative. The idea behind that, all these options (more than 30) could be changed by user from the device and from the host.

Image
You do not have the required permissions to view the files attached to this post.