USB I/O

The intent of this forum is to discuss my DOS TSR programs (available at http://bretjohnson.us), how they work and don't work, new/missing features, status of updates, and anything else related to them that may need to be discussed.

Re: USB I/O

Postby Dinosaur » Sun Sep 13, 2009 9:39 am

Hi all

c.m you picked up on a spelling mistake of mine.
I create a Dos RM (Real Mode) memory block with Int 0x31 subfunction 0x100.
Then I copy my Struc from PM to RM and back to PM again after usbuhcil is finished.

Thanks for pointing out that there is another, never had the need to allocate memory
that way in PM.

Regards
Dinosaur
 
Posts: 70
Joined: Wed Jul 01, 2009 5:54 pm
Location: Salt Lake City USA

Re: USB I/O

Postby Bret » Sun Sep 13, 2009 12:44 pm

Dinosaur wrote:Where I am getting confused (it's easy) is the purpose of the linear address.
I misunderstood it to be a linear address of where the "device" is hiding.


Linear addresses are what DPMI and other protected-mode environments normally use. They are "better" than segment:offset addresses because they can theoretically let you address 4GB of memory, instead of being limited to 64kB at a time like segment:offset. They can also let you do things like access virtual memory (memory simulated on a hard drive) without you even needing to know it's not real memory, and offers "protection" by not letting programs access each others memory.

The problem is, linear addresses still require selectors (CS, DS, ES, FS, GS), which are called segments in Real/V86 mode. In protected mode, instead of the selector actually being a number that can be used to calculate something, it is simply an index number for a lookup table (LDT or GDT). Unfortunately, the lookup tables are protected, and you can't look at them unless you're in CPU ring 0 (the OS itself or a kernel/hardware-level driver). This is all part of the "protection" offered by environments like DPMI, since allowing access to the tables would allow programs to do things, either accidentally or on purpose, to another program's (or the OS's) memory. All part of the "virtual environment" that MS wants you to live in, which has both good points and bad points.

Dimosaur wrote:So, if usbuhcil translates the Seg:offs address that my app provides to issue a physical address to the host controller, (which is only a place to exchange commands & Data) then my only problem is to locate such a free address in Real Mode memory area.

This is where I thought that making a buffer for the Struct larger (1024 bytes) to allow room for this would solve that problem.
Equally, I can create another buffer specifically for this.
Am I still missing something ?


The buffer that contains the 64-byte structure does need to be segment:offset addressable, since USBUHCIL (a Real/V86 mode program) needs to be able to read it. However, the memory buffer used to transfer the data in and out of a USB device itself does not. USBUHCIL does not ever need to see the data -- it just cares about whether the data got transferred successfully or not, which the host controller knows. If you can provide a physical address directly (converting a linear address to a physical address somehow in DPMI), you can simply put the physical address into the 64-byte structure, set the "AddressIsPhysical" flag in the structure, and USBUHCIL will simply pass the address through unchanged to the host controller.

Christian's idea of using HDPMI and creating a DMA buffer seems like it could also work. The best solution, though, if it's possible, is to go directly from physical to linear, without even needing to mess with segment:offset in the middle.

****************************************

Dimosaur wrote:I was referring to this call, where bit 0 in BH was set.
But no error code in DX

Get Device Status Information
...


I can't duplicate this error. Are you sure you were looking at BH, and not BX or BL? Those will nearly always have bit 0 set, since the Configuration value (returned in BL) is nearly always 1.
Bret
 
Posts: 478
Joined: Fri Oct 10, 2008 3:43 am
Location: Rio Rancho, NM

Re: USB I/O

Postby Dinosaur » Sun Sep 13, 2009 4:07 pm

Hi all

Can I get a comment on this statement by HDPMI

) Conventional Memory

Conventional memory (address space 0-10FFFFh) is under control of DOS.
In XMS and raw mode HDPMI will initialize the page table for this region
so that physical memory addresses and linear memory addresses are identical.
For its API translation HDPMI will use a DOS memory block of 8 kB.
Additionally some host code has to be run in real/v86-mode, and a 2 kB
host stack also is located in conventional memory to make it accessible
for both modes. All in all HDPMI will use about 13 kB of DOS memory.


Does that mean that in Raw mode,if I create a block of memory in the first mByte
I can simply supply a Linear Address ?

And from djgpp
You have several alternatives to get the physical address of your buffer:

* Allocate the buffer in conventional memory, below the 1MB mark. This memory is mapped 1:1 by all DPMI servers, so the linear address is equal to the physical one. You can allocate a buffer in conventional memory using the library function __dpmi_allocate_dos_memory.
This means that the buffer I create is in fact both Linear & Physical.

Regards
Dinosaur
 
Posts: 70
Joined: Wed Jul 01, 2009 5:54 pm
Location: Salt Lake City USA

Re: USB I/O

Postby Bret » Sun Sep 13, 2009 11:43 pm

Dinosaur wrote:*Allocate the buffer in conventional memory, below the 1MB mark. This memory is mapped 1:1 by all DPMI servers, so the linear address is equal to the physical one. You can allocate a buffer in conventional memory using the library function __dpmi_allocate_dos_memory


If this is in fact true (if ALL DPMI servers map the memory 1:1), the problem appears to be solved. We can try testing it and see - maybe we've all spent a lot of effort worrying about something that's not even a problem.

I do know that under VCPI the statement is NOT true. Under VCPI, the memory between 0 and 640k is usually mapped 1:1, but the memory between 640k and 1MB is usually not.

It also doesn't seem likely, because of the multi-tasking nature of DPMI. Under DPMI (at least DOS-based Windows like 9x and Me), you can have several independent DOS boxes (command shells) at the same time. They all "share" the same DOS memory (including TSR's in Upper memory)) that was installed before Windows started, but the "extra" memory under 1MB that was not in use before Windows started is completely independent for each DOS box. This implies that the allocated memory is not mapped 1:1, at least under Windows DPMI.

c.m wrote:I think this depends on the CPU mode the DPMI host found when it installed. When no VCPI host/EMM was found and the CPU was in real mode, the DPMI host probably switches to real mode (instead of V86) to handle interrupts.


Based on this comment, it may actually depend on whether DPMI was loaded from protected mode (VCPI) or real mode, and whether the memory is allocated below or above 640k. I don't have any real experience actually programming under DPMI, so can't comment further.
Bret
 
Posts: 478
Joined: Fri Oct 10, 2008 3:43 am
Location: Rio Rancho, NM

Re: USB I/O

Postby Dinosaur » Mon Sep 14, 2009 9:23 am

Hi all

I think that the 1:1 statement may be true, si I will test it.
Have a look at this discussion on the subject.

http://www.freebasic.net/forum/viewtopic.php?t=14497

Regards
Dinosaur
 
Posts: 70
Joined: Wed Jul 01, 2009 5:54 pm
Location: Salt Lake City USA

Re: USB I/O

Postby c.m » Wed Sep 16, 2009 8:58 am

Bret wrote:Christian's idea of using HDPMI and creating a DMA buffer seems like it could also work. The best solution, though, if it's possible, is to go directly from physical to linear, without even needing to mess with segment:offset in the middle.

(linear to physical, but whatever)

Doesn't "Scatter/gather lock" allow this? The EDDS has a segment/selector field and a 32-bit offset field, which apparently allows usage of all of HDPMI's linear memory (since the call is handled by HDPMI itself). RBIL's documentation about this is a bit misleading; this is the EDDS passed to "Scatter/gather lock":

Code: Select all
Format of Extended DMA descriptor structure (EDDS):
Offset   Size   Description   (Table 03222)
00h   DWORD   region size
04h   DWORD   offset
08h   WORD   segment/selector
0Ah   WORD   reserved
0Ch   WORD   number available
0Eh   WORD   number used
10h   DWORD   region 0 physical address
14h   DWORD   region 0 size in bytes
18h   DWORD   region 1 physical address
1Ch   DWORD   region 1 size in bytes
   ...


As specified at the call's description, the fields "region size", "segment/selector", "offset", and the "number available" have to be set before the call. "Number available" apparantly specifies the number of region descriptor fields (8 byte each) available in the structure. On a successful return, "Number used" will contain the number of these fields required/used. Since the USB {controller/bus/device/interface/stuff} probably requires it's data in one chunk, you can simply set "number available" to 1 (and allocate 8 byte for one descriptor field). The call then doesn't succeed if HDPMI mapped the specified memory chunk to several physical chunks, since there aren't enough descriptor fields in your buffer. However I doubt HDPMI ever maps one of your memory blocks to several physical chunks anyway.

If you're going to use HDPMI for this, Int31.0401 returns the description string "HDPMI" as specified in the docs. This way you could detect usage with a wrong server.

Dinosaur wrote:*Allocate the buffer in conventional memory, below the 1MB mark. This memory is mapped 1:1 by all DPMI servers, so the linear address is equal to the physical one. You can allocate a buffer in conventional memory using the library function __dpmi_allocate_dos_memory


If this is in fact true (if ALL DPMI servers map the memory 1:1), the problem appears to be solved. We can try testing it and see - maybe we've all spent a lot of effort worrying about something that's not even a problem.

I do know that under VCPI the statement is NOT true. Under VCPI, the memory between 0 and 640k is usually mapped 1:1, but the memory between 640k and 1MB is usually not.


I think that's right, because VCPI is usually provided by EMM drivers, which map memory above 1 MiB to the UMA (640+ KiB) so DOS can utilize these blocks as UMBs. (I'm developing the new RxDOS kernel so I know a bit about things related to DOS's real-mode operation.) No DPMI server however disables the UMBs provided by underlying EMMs, which means the remapping is still in effect while the DPMI server runs. This limits the "no remapping" area up to 640 KiB only. You mustn't assume that DOS's LMA (low memory area) only uses this "safe" memory, however: Some EMMs extend the LMA into video memory at segment A000h. (This means setting DOS's way of memory allocation with Int21.58 to allocate LMA only might still theoretically allocate you a DOS memory block in remapped space.)

It also doesn't seem likely, because of the multi-tasking nature of DPMI. Under DPMI (at least DOS-based Windows like 9x and Me), you can have several independent DOS boxes (command shells) at the same time. They all "share" the same DOS memory (including TSR's in Upper memory)) that was installed before Windows started, but the "extra" memory under 1MB that was not in use before Windows started is completely independent for each DOS box. This implies that the allocated memory is not mapped 1:1, at least under Windows DPMI.


Some of the details differ, but for this problem it's the same result: Windows remaps the Real/V86 mode memory. Probably important though: Windows 3.1x in "386 Enhanced" mode also does that similar to Windows 4.x (9x/Me).
C. Masloch
c.m
 
Posts: 18
Joined: Sat Sep 12, 2009 5:49 am
Location: Germany

Re: USB I/O

Postby Dinosaur » Wed Sep 16, 2009 4:35 pm

Hi All

Christian, I have to admit to getting out of my depth here. I am quite proficient in asm for 16 bit environment
but when it come to protected mode, I am lost.

Currently my stumbling block is the CallBackAddr, and I took your advice and emailed japheth, and according to his response
the following code should work. However remember that this is in-line asm within FreeBasic.
Code: Select all
    mov       ax, 0303h
   push   ds
   mov       bx, cs
   mov       ds, bx
   mov       esi, OFFSET My_CallBack
   pop       es
   mov       edi, OFFSET PM_Struc
   int       0x31
This causes gpf's. However changing it to the following, doesnt.
Code: Select all
        mov     ax, 0x0303
        mov     esi, OFFSET My_CallBack
        push    ds
        pop     es
        mov     edi, OFFSET PM_Struc
        Int     0x31
But the Seg:Off created with this is wrong, as unplugging the usb lead causes
Exception 0D in ring 0
From that I have concluded that I have all the usb stuff right, because unplugging the lead
causes it to want to go to an address to notify me of the disconnect.
However by moving the in-line asm to a totally different area of my program, it still comes back
with the same Seg:off. (0FF5:0CD4)

As far as the DataAddress is concerned, I cant prove it until the CallBackAddr is correct, cause each time I try to do say
a "DoControl" , it executes, but there must be something wrong as it tries to goto the callback address.

I'm stalled

Regards
Dinosaur
 
Posts: 70
Joined: Wed Jul 01, 2009 5:54 pm
Location: Salt Lake City USA

Re: USB I/O

Postby Bret » Thu Sep 17, 2009 2:33 am

This is out of my league as well, but a couple of things I notice. With this Code:

Code: Select all
mov       ax, 0303h
   push   ds
   mov       bx, cs
   mov       ds, bx
   mov       esi, OFFSET My_CallBack
   pop       es
   mov       edi, OFFSET PM_Struc
   int       0x31


The DS selector is set to the same as CS during the DPMI call. Even if CS and DS physically point at the same memory, the CS selector is normally read-only and executable while DS is normally read/write and unexecutable. I think that may be what you're trying to work around with this code, but it may not be correct, or the values in your PM_STRUC (particularly the selectors) may not be correct. Copying CS to DS makes DS read-only.

I also noticed in RBIL that DPMI function 0303h refers to an IRET needed at the end of the PM call, which makes me wonder if it expects the real mode call to need an IRET (instead of a RETF) as well.

What exactly is "Exception 0D in ring 0" supposed to indicate? Why would a callback be operating in Ring 0 instead of Ring 3?

EDIT:

The following link appears to be a good example of how to use the real mode call-back in DPMI. Maybe you've already seen this, and maybe this is where everybody is getting their information from (Japheth's code looks like it was copied directly from here). In the example here, though, the call-back is for an IRQ processing routine, while the call-back provided by USBUHCIL is just a far call, so the stuff related to the flags must be removed.

http://www.delorie.com/djgpp/doc/dpmi/ch4.6.html
Bret
 
Posts: 478
Joined: Fri Oct 10, 2008 3:43 am
Location: Rio Rancho, NM

Re: USB I/O

Postby c.m » Thu Sep 17, 2009 5:57 am

Hi,

Dinosaur wrote:Currently my stumbling block is the CallBackAddr, and I took your advice and emailed japheth, and according to his response
the following code should work. However remember that this is in-line asm within FreeBasic.
Code: Select all
    mov       ax, 0303h
   push   ds
   mov       bx, cs
   mov       ds, bx
   mov       esi, OFFSET My_CallBack
   pop       es
   mov       edi, OFFSET PM_Struc
   int       0x31
This causes gpf's.


Can you distinguish whether this Int31 request causes the GPF, or the calling of your callback (from Real/V86 mode) later? Bret already mentioned the "strange" selector setup (which however is required for the call to Int31.0303), so you might try to use that code:

Code: Select all
        push ds                         ; save ds
        mov ax, 0303h
         push ds
         pop es                         ; (set es = ds)
        mov edi, OFFSET PM_Struc        ; set es:edi-> PM_Struc
         push cs
         pop ds                         ; (set ds = cs)
        mov esi, OFFSET My_CallBack     ; set ds:esi-> My_CallBack
        int 31h                         ; (call DPMI host)
        pop ds                          ; restore ds

(You may also want to save the selector in ES with another push/pop combination added.)

However changing it to the following, doesnt.
Code: Select all
        mov     ax, 0x0303
        mov     esi, OFFSET My_CallBack
        push    ds
        pop     es
        mov     edi, OFFSET PM_Struc
        Int     0x31
But the Seg:Off created with this is wrong, as unplugging the usb lead causes
Exception 0D in ring 0


This indicates your previous GPF was indeed caused by the selector mess. Try to write your code as shown above. RBIL mentions in its interrupt 0Dh (aka exception 0Dh) description:

Code: Select all
{General Protection Violation}
   called in protected mode on protection violations not covered by INT 06
     through INT 0C, including
       segment limit violations
       write to read-only segments
       accesses using null DS or ES selectors
       accesses to segments with privilege greater than CPL
       wrong descriptor type


You provided Int31.0303 in your code with a data selector in DS, but it needs a code selector. (That, or My_CallBack doesn't contain the necessary code.) If the code I provided still doesn't work, maybe FreeBasic somehow sets the selectors up in a way that we don't expect. Before asking the FreeBasic guys about it, however, make sure your code in My_CallBack contains something like this code: (it's copied from a demo program I wrote for NDebug, so the comments are a bit messy.)

Code: Select all
                ; This is a RM callback. Called from RM, executed in PM.
                ;
                ; INP:   es:edi-> RM call structure
                ;        ds:esi-> RM stack
                ;        ss:esp-> DPMI host internal stack
                ; CHG:   all (?) except es:edi
                ;        read/write RM call structure to communicate with RM code
callback:
        push dword [esi]
        pop dword [es:edi+2Ah]          ; set RM cs:ip
        add word [es:edi+2Eh], byte 4   ; pop 4 byte from the stack
        iret


Note how it modifies the Real/V86 mode stack and CS:IP register pair, since the DPMI host doesn't do that for you.

Dinosaur wrote:However by moving the in-line asm to a totally different area of my program, it still comes back
with the same Seg:off. (0FF5:0CD4)


This is intended. The returned address is a Real/V86 mode call back, which the DPMI host allocates for you. Jumping to this address in Real/V86 mode will pass control to the code specified as My_CallBack above. The DPMI host will save the address of My_CallBack internally and associate it somehow with the (dynamically allocated) call back address (f.e., 0FF5:0CD4).

Bret wrote:I also noticed in RBIL that DPMI function 0303h refers to an IRET needed at the end of the PM call, which makes me wonder if it expects the real mode call to need an IRET (instead of a RETF) as well.


No, it doesn't. As shown above, you've to define that yourself. The PM example code could retrieve the flags from the RM stack, set RM flags to that value and then adjust the stack by 6 byte instead if it were to be called as RM interrupt handler.

Maybe you've already seen this, and maybe this is where everybody is getting their information from


I wasn't aware of that code, and am using only RBIL too ;-)
C. Masloch
c.m
 
Posts: 18
Joined: Sat Sep 12, 2009 5:49 am
Location: Germany

Re: USB I/O

Postby Dinosaur » Thu Sep 17, 2009 9:52 am

Hi all

Christian, I really appreciate your efforts.
Reading through your response, it appears that in the callback the caller uses Real Mode registers ?
You cant use any PM instructions or registers or variables?

Although at one stage I simply put an Iret there, but still the same problem when I unplug the lead.
In all the reading and browsing I have done all of them do this at the start
Code: Select all
        push   ds
   mov   bx, cs
   mov   ds, bx
But that is what causes the gpf.This is the complete asm part of the routine
Code: Select all
    Asm
        push    ds
        push    es
        '---------
        mov     ax, 0x0303
        mov     dssave,ds             
        mov       esi, OFFSET My_CallBack
        mov     es,dssave
        mov       edi, OFFSET PM_Struc
        Int       0x31
        jc       1f

        mov     WORD PTR DataAddr[2],CX
        mov     WORD PTR DataAddr[0],DX
        mov     dword Ptr [Function], 0
        '---------
        pop     es
        pop     ds
        jmp     1f
    0:
        mov     dword Ptr [Function],0x14
        jmp     1f
My_CallBack:
       iret
    1:
    End Asm

I tried your suggestion in the callback, but some cause compiler problems. I dont think it even gets there, so it doesnt matter what is there.
Either the address is wrong or the way it is being called.
Bret the link you provide is where I AM getting my info from, can someone point me to another link ? I am amazed, that there is only one example
that I can find, and that is the one on your link.

I have asked questions on the FreeBasic site, and MichaelW has been very helpfull, but this issue is not exactly a general FB enquiry.
Will keep you guys informed as thing progress, I am not giving up on this.

Regards

Edit:What or Where is RBIL ?
or the values in your PM_STRUC (particularly the selectors) may not be correct.

32h or 50 bytes long
Code: Select all
Type PMdpmiStruc Field = 1
    edi     As UInteger
    esi     As UInteger
    ebp     As UInteger
    Res     As UInteger
    Ebx     As UInteger
    Edx     As UInteger
    Ecx     As UInteger
    Eax     As UInteger
    Flags   As UShort
    ES      As UShort
    DS      As UShort
    FS      As UShort
    GS      As UShort
    IP      As UShort
    CS      As UShort
    SP      As UShort
    SS      As UShort
End Type
Common Shared PM_Struc As PMdpmiStruc
Dinosaur
 
Posts: 70
Joined: Wed Jul 01, 2009 5:54 pm
Location: Salt Lake City USA

Re: USB I/O

Postby Bret » Thu Sep 17, 2009 11:03 am

I think at a minimum you need to at least have My_CallBack return the CS:IP and stack adjusted correctly. As you stated, it may not ever actually be getting called, but when it does, you don't want it to be the cause of any problems:

Code: Select all
My_CallBack:
  lodsd
  mov   es:[di+2Ah],eax        ;Copy return CS:IP
  add   WORD PTR es:[di+2Eh],4 ;Adjust Stack
  iret


RBIL is Ralf Brown's Interrupt List, the "bible" for pretty much anything related to interrupt calls, BIOS functions, and the like. It hasn't been updated in several years, yet there's never been anything even remotely close to replacing it as a general reference. It''s pretty common on programming forums to just call it RBIL and almost everybody knows what you're talking about. If you don't have a copy of it, you definitely need one.
Bret
 
Posts: 478
Joined: Fri Oct 10, 2008 3:43 am
Location: Rio Rancho, NM

Re: USB I/O

Postby Dinosaur » Thu Sep 17, 2009 11:40 am

Hi all

Thanks Bret, I am well aware of Ralp Browns list, just never come across it being called RBIL.
The code snippet compiled correctly, but didnt solve the problem, so as I said, it's not getting there.

Regards
Dinosaur
 
Posts: 70
Joined: Wed Jul 01, 2009 5:54 pm
Location: Salt Lake City USA

Re: USB I/O

Postby c.m » Thu Sep 17, 2009 5:28 pm

Dinosaur wrote:Reading through your response, it appears that in the callback the caller uses Real Mode registers ?
You cant use any PM instructions or registers or variables?


As stated by the example code's commentary, this *is* code that *executes* in PM. After all, you couldn't execute it with your linear code selector otherwise. It only *modifies* the state of RM by modifying the passed "PMdpmistruc" structure which is pointed to by es:edi during the callback. You don't have to worry about selector setup inside the call back's code: This is all handled by the DPMI host by passing you the values specified for the Int31.0303 call.

Code: Select all
        push   ds
   mov   bx, cs
   mov   ds, bx
But that is what causes the gpf.This is the complete asm part of the routine
Code: Select all
    Asm
        push    ds
        push    es
        '---------
        mov     ax, 0x0303
        mov     dssave,ds             
        mov       esi, OFFSET My_CallBack
        mov     es,dssave
        mov       edi, OFFSET PM_Struc
        Int       0x31
        jc       1f

        mov     WORD PTR DataAddr[2],CX
        mov     WORD PTR DataAddr[0],DX
        mov     dword Ptr [Function], 0
        '---------
        pop     es
        pop     ds
        jmp     1f
    0:
        mov     dword Ptr [Function],0x14
        jmp     1f
My_CallBack:
       iret
    1:
    End Asm


Now you're setting es to the value of ds (which is correct if PM_Struc can be accessed with the selector in ds), but you have to set ds to the value of cs before the Int31 too. Instead of dssave (which is a memory variable?) use (e)cx or (e)dx, because memory access might depend on the ds value (which you want to modify too).

I tried your suggestion in the callback, but some cause compiler problems. I dont think it even gets there, so it doesnt matter what is there.


Oops, my fault. The example was written for NASM. You would've had to adapt it to MASM or whatever assembler FreeBasic is using for in-line assembler. Try that:

Code: Select all
                ; This is a RM callback. Called from RM, executed in PM.
                ;
                ; INP:   es:edi-> RM call structure
                ;        ds:esi-> RM stack
                ;        ss:esp-> DPMI host internal stack
                ; CHG:   all (?) except es:edi
                ;        read/write RM call structure to communicate with RM code
callback:
        push dword PTR [esi]
        pop dword PTR es:[edi][2Ah]          ; set RM cs:ip
        add word PTR es:[edi][2Eh], byte 4   ; pop 4 byte from the stack
        iret


Either the address is wrong or the way it is being called.


In your above example, the code still didn't adjust the RM stack. Think about it this way: The RM code (of the USB drivers) "calls" the code at 0FF5:0CD4, which is allocated for your call back. Execution is then passed to protected mode. Here your code has the responsibility to adjust the CS:IP register pair (of the RM execution) before returning to the DPMI host. Just doing the "iret" doesn't carry this out, you'll simply leave 0FF5:0CD4 in the RM CS:IP registers.

Bret already provided another code snippet for a working call back, but I think it's important that you understand the difference and what these instructions are supposed to do, too.

Bret wrote:or the values in your PM_STRUC (particularly the selectors) may not be correct.

32h or 50 bytes long
Code: Select all
Type PMdpmiStruc Field = 1
    edi     As UInteger
    esi     As UInteger
    ebp     As UInteger
    Res     As UInteger
    Ebx     As UInteger
    Edx     As UInteger
    Ecx     As UInteger
    Eax     As UInteger
    Flags   As UShort
    ES      As UShort
    DS      As UShort
    FS      As UShort
    GS      As UShort
    IP      As UShort
    CS      As UShort
    SP      As UShort
    SS      As UShort
End Type
Common Shared PM_Struc As PMdpmiStruc


I think for the RM callback you don't need to initialize any of these, so you can't initialize them with wrong values. Either way, the structure never contains selectors: it's the structure of RM registers, so the segment registers are used for segments only.
C. Masloch
c.m
 
Posts: 18
Joined: Sat Sep 12, 2009 5:49 am
Location: Germany

Re: USB I/O

Postby Dinosaur » Thu Sep 17, 2009 6:36 pm

Hi all

Some progress now.
Japheth put me straight about the ds in the first section of code. Christian you basically said the same thing.
you copy CS to DS, that's good, but you don't save/restore the value of DS.
With a code selector in DS you cannot write to global variables anymore! This code looks better:

mov ax, 0x0303
push ds ;<-added
push ds
mov bx, cs
mov ds, bx
mov esi, OFFSET My_CallBack
pop es
mov edi, OFFSET _RM_STRUC
int 0x31
pop ds ;<-added
jc 1f
mov WORD PTR _DATAADDR[2],CX
Afther that, I didnt get any more gpf's. So the complete code.
Code: Select all
    Asm
        mov     ax, 0x0303
        push    ds     
        push    ds
        mov     bx, cs
        mov     ds, bx
        mov     esi, OFFSET My_CallBack
        pop     es
        mov     edi, OFFSET _RM_STRUC
        int     0x31
        pop     ds     
        jc      1f
   
        mov     WORD PTR DataAddr[2],CX
        mov     WORD PTR DataAddr[0],DX
        mov     dword Ptr [Function], 0
        '---------
        jmp     1f
    0:
        mov     dword Ptr [Function],0x14
        jmp     1f
My_CallBack:
        mov     eax,&Hffff
        Mov     FdBack,eax
        push    dword PTR [esi]
        pop     dword PTR es:[edi][0x2A] 
        add     word Ptr es:[edi][0x2E], 4
        Sti
        iret       
    1:
    End Asm
After creating this CallBack, I claim ownership of an interface. Then I wait in a loop to see if
either Q is pressed or value of FdBack changes. By pulling the plug it should modify the variable if as you stated
Execution is then passed to protected mode.
But it doesnt, although there is no gpf when I pull the plug.So I suspect that I have set registers up to allow access to that variable.

Regards

Edit: Reading further, I can see that all the registers(on entry into callback) are set to RM parameters. However, the callback is notifying PM of a problem, so data has to be passed to PM, and for PM to make a decision with.
But the PM stack is locked. So how do we notify PM ??
Dinosaur
 
Posts: 70
Joined: Wed Jul 01, 2009 5:54 pm
Location: Salt Lake City USA

Re: USB I/O

Postby c.m » Fri Sep 18, 2009 1:19 am

Dinosaur wrote:Afther that, I didnt get any more gpf's. So the complete code.

Code: Select all
    Asm
        mov     ax, 0x0303
        push    ds     
        push    ds
        mov     bx, cs
        mov     ds, bx
        mov     esi, OFFSET My_CallBack
        pop     es
        mov     edi, OFFSET _RM_STRUC
        int     0x31
        pop     ds     
        jc      1f
   
        mov     WORD PTR DataAddr[2],CX
        mov     WORD PTR DataAddr[0],DX
        mov     dword Ptr [Function], 0
        '---------
        jmp     1f
    0:
        mov     dword Ptr [Function],0x14
        jmp     1f
My_CallBack:
        mov     eax,&Hffff
        Mov     FdBack,eax
        push    dword PTR [esi]
        pop     dword PTR es:[edi][0x2A] 
        add     word Ptr es:[edi][0x2E], 4
        Sti
        iret       
    1:
    End Asm


After creating this CallBack, I claim ownership of an interface. Then I wait in a loop to see if
either Q is pressed or value of FdBack changes. By pulling the plug it should modify the variable if as you stated
"Execution is then passed to protected mode." But it doesnt, although there is no gpf when I pull the plug. So I suspect that I have set registers up to allow access to that variable.


The problem is that inside the call-back, ds will be set up to another selector (as stated in the commentary and RBIL, ds:esi points to the RM stack) with which you can't access your code segment. You may work around this easily: Change the specification of your _RM_STRUC so that it contains another UShort field "FdBack" behind the ss field. Then you can access that field inside the callback as "word PTR es:[edi][0x32]". The line could look like this:

Code: Select all
        or word PTR es:[edi][0x32], -1

(In FreeBasic, you've to access FdBack as member of the structure then, too.)

BTW, you've a "sti" instruction up there in your code. I would remove it, is there a reason you added it?

Another thing I noticed (and can't answer myself, since I didn't read much of Bret's documentation yet): Is the call back used only to notify you when the interface disconnected? Because, if it would be used for other things too, you probably want to add a conditional to My_CallBack so to set FdBack only if the interface really got disconnected.
C. Masloch
c.m
 
Posts: 18
Joined: Sat Sep 12, 2009 5:49 am
Location: Germany

Re: USB I/O

Postby Bret » Fri Sep 18, 2009 3:30 am

c.m wrote:BTW, you've a "sti" instruction up there in your code. I would remove it, is there a reason you added it?


The description in the link I had up above says that if the far call can potentially be multitasked, such as if it is part of an IRQ (which the USB calls are), then either interrupts should remain disabled (CLI, not STI) or you should temporarily move things to another section of memory. USBUHCIL shouldn't ever "multitask" from within the same IRQ, but if you ever end up needing to control more than one USB interface at the same time, and the interfaces can be on different host controllers with different IRQ's, there could be some conflicts. Another way around it is to always set up different call-back routines for each interface and each type of call back (ownership notifications vs packet notifications), but DPMI only needs to provide a total of 16 call-back routines, so the resources are limited and can run out very quickly. You can also use a single call-back routine for everything, and distinguish between them somehow with the User Packet ID (see below).

c.m wrote:Another thing I noticed (and can't answer myself, since I didn't read much of Bret's documentation yet): Is the call back used only to notify you when the interface disconnected? Because, if it would be used for other things too, you probably want to add a conditional to My_CallBack so to set FdBack only if the interface really got disconnected.


There are numerous reasons that a callback can occur besides disconnects, and there can even be multiple notifications for the same "event". For instance, if a device is connected to an upstream hub, and the hub is disconnected, the owner is notified that the upstream hub was disconnected, and is also notified that the device has been disconnected. The exact reason for the notification is in the AX register for the call, BX contains the User Packet ID that was provided during the registration process, and details such as the Host Index & Device Address are in the CX and DX registers.

EDIT:

c.m wrote:The problem is that inside the call-back, ds will be set up to another selector (as stated in the commentary and RBIL, ds:esi points to the RM stack) with which you can't access your code segment. You may work around this easily: Change the specification of your _RM_STRUC so that it contains another UShort field "FdBack" behind the ss field. Then you can access that field inside the callback as "word PTR es:[edi][0x32]".


I think the problem is slightly larger than that. Dinosaur needs to be able to access the same PM memory from BOTH the call back code and the FreeBASIC code. At least for now (during simple testing), he just wants to set a semaphore flag from the call-back code to indicate something has happened, and then immediately return to USBUHCIL (remember, this is all happening inside an IRQ, so the call-back code can't waste a lot of time). He then monitors and processes the flag from FreeBASIC running as a foreground process, outside the critical timing constraints of the IRQ.

Rather than adding a UShort field for FdBack to the _RM_STRUC, could he add a UInteger field and store his "real" DS there so he can use it in the call-back code? I assume that DPMI will only change the first 50 bytes of the structure, and leave the rest of it completely alone at all times?
Bret
 
Posts: 478
Joined: Fri Oct 10, 2008 3:43 am
Location: Rio Rancho, NM

Re: USB I/O

Postby c.m » Fri Sep 18, 2009 9:20 am

Bret wrote:You can also use a single call-back routine for everything, and distinguish between them somehow with the User Packet ID (see below).


Yes, I think that's the best way to do it. I've read some of the descriptions in USBAPI.DOC now.

Rather than adding a UShort field for FdBack to the _RM_STRUC, could he add a UInteger field and store his "real" DS there so he can use it in the call-back code? I assume that DPMI will only change the first 50 bytes of the structure, and leave the rest of it completely alone at all times?


Yes, for the DPMI host, the structure has only 50 bytes. And you've extended my hack in a clever way, although it could be done easier: The whole _RM_STRUC is addressed using the data selector, so just set DS inside the callback to the value of ES, or address all data with ES. (However this isn't as general a solution as storing the actual selector behind the structure, because that would also work if _RM_STRUC wasn't with the normal data. BTW, we don't need a UInteger field for selectors: They're still only 16-bit values even in 32-bit PM.)
C. Masloch
c.m
 
Posts: 18
Joined: Sat Sep 12, 2009 5:49 am
Location: Germany

Re: USB I/O

Postby Bret » Fri Sep 18, 2009 9:53 am

I thought of another possibility as well, if extending the _RM_STRUC doesn't work for some weird reason. I was trying to "save" the User Packet ID exclusively for differentiation of the different call-back interfaces and address, but I'm not sure it's necessary.

Correct me if I'm wrong, but a selector doesn't really need 16 bits. It only needs 13 bits since there can only be 8192 selector entries in a GDT or LDT (and a real xDT would probably never actually use more than a few hundred, even under a task-switching OS like Windows). The upper three bits should never be set in a selector. So, you could use the lower 13 bits of the User Packet ID to store DS, and the upper three bits (8 possible values) to differentiate between interfaces and/or call-back types.

FWIW, in my real-mode TSR drivers, I usually have separate routines for the different call-back types, and use the User Packet ID to select the interface.
Bret
 
Posts: 478
Joined: Fri Oct 10, 2008 3:43 am
Location: Rio Rancho, NM

Re: USB I/O

Postby Dinosaur » Fri Sep 18, 2009 10:08 am

Hi all

BTW, you've a "sti" instruction up there in your code. I would remove it, is there a reason you added it?

then either interrupts should remain disabled (CLI, not STI) or you should temporarily move things

My reading of the dpmi spec show that the caller enters the CallBack with Interupts disabled. It is the client's (dpmi)
responsibility to enable them when done.That is why I have STI. I dont recall if iret does this as well.

I think the Struc addition is a good solution, will work on that and see if I can knock this over.
The caller has definitely come to the right address after the "unplug" , as when I changed my code to
mov [FdBack],ax 'write to location pointed to by FdBack
there was a gpf. Was the wrong thing to do anyway, but it proved it.

Bret, with your familiarity of usb devices, is it normal to set led's using a control instruction.?
If I purchase another brand board is it likely that this changes.?

Regards
And thanks guy's
Dinosaur
 
Posts: 70
Joined: Wed Jul 01, 2009 5:54 pm
Location: Salt Lake City USA

Re: USB I/O

Postby Dinosaur » Fri Sep 18, 2009 10:29 am

Hi all

I think the cleanest way to do this is to save the registers that hold the callback info
into a PM location (Struc mod or other).

Also, I would use different callbacks for different usb devices, as the function assigned to them
would need to be identified at the time of callback. It also leaves the way open for other processes
to "own" a device.

REgards
Dinosaur
 
Posts: 70
Joined: Wed Jul 01, 2009 5:54 pm
Location: Salt Lake City USA

Re: USB I/O

Postby Bret » Fri Sep 18, 2009 11:08 am

Dinosaur wrote:My reading of the dpmi spec show that the caller enters the CallBack with Interupts disabled. It is the client's (dpmi) responsibility to enable them when done.That is why I have STI. I dont recall if iret does this as well.


CLI disables interrupts, STI enables them. Maybe backwards from what seems logical, but the way it is nonetheless. FWIW, USBUHCIL issues an STI and a CLD immediately before issuing the callback, so interrupts are enabled and string functions move forward. DPMI could change one or both of those before it gets to your code, though.

IRET simply restores whatever flags are saved on the stack, so it could either enable or disable the interrupts.

Dinosaur wrote:I think the Struc addition is a good solution, will work on that and see if I can knock this over. The caller has definitely come to the right address after the "unplug" , as when I changed my code to mov [FdBack],ax 'write to location pointed to by FdBack there was a gpf. Was the wrong thing to do anyway, but it proved it.


It's exciting to be this close! I know you still have quite a bit of work before everything is done, but it looks like you're getting past the major stumbling blocks.

Dinosaur wrote:Bret, with your familiarity of usb devices, is it normal to set led's using a control instruction.? If I purchase another brand board is it likely that this changes.?


No, it isn't normal, though I don't have a lot of experience with such things. My exposure so far is limited to the LED's on USB keyboards, and they are set by sending a bulk (or periodic interrupt) transaction rather than a control transaction. I would have expected the U-HID to do the same thing, since it is classified as a Human Interface Device just like a keyboard is, and LED control is clearly defined in the HID specs. As a friend of mine often says, "The U in USB is far from Universal."
Bret
 
Posts: 478
Joined: Fri Oct 10, 2008 3:43 am
Location: Rio Rancho, NM

Re: USB I/O

Postby Dinosaur » Fri Sep 18, 2009 11:26 am

Hi all

STI enables them.
That is why, when I was finished with the Callback I issued a STI

I decided that the using a seperate global variable as a flag may be the best way.
In other words, extend the RM_Struc and put the PM ds in it.
Then in the callback, restore the PM ds, and set the Flag.
Restore the RM ds, and quit.

The RM_Struc is supposed to have all the registers from the callback.
Using my
CopyToPM( sel2, 0, @pm_Struc, 52 ) '' Sel, Src, Dest, Bytes
I then have the access to all the registers set by the caller.
That's the theory anyway.Doing it is another thing.

Regards
Dinosaur
 
Posts: 70
Joined: Wed Jul 01, 2009 5:54 pm
Location: Salt Lake City USA

Re: USB I/O

Postby Dinosaur » Fri Sep 18, 2009 11:57 am

Hi all

Ok, getting the full filled in Struc.
Bret, what is in AX if I pull the plug ?
I am getting:
Ax =2 Unknown Hardware Problem with Host

Regards

Edit:
It appears that I dont need the Global Flag to find out if the Caller has been there.
Simply monitoring the PM_Struc.ebx for my UserPktId is enough.
By clearing the Struc after creating the CallBackAddr, the Struc remains clear until a callback happens.
Then all the detail in the Struc is left filled in, by dpmi.
Monitoring the ebx register in the struc, is an indication that a callback has happened.

What is annoying, is that I have read and re-read the desription on int31.0303 dozens of times,
and I couldnt extract the fact that the Struc is left intact for PM to use.
Dinosaur
 
Posts: 70
Joined: Wed Jul 01, 2009 5:54 pm
Location: Salt Lake City USA

Re: USB I/O

Postby Bret » Fri Sep 18, 2009 12:57 pm

Dinosaur wrote:Bret, what is in AX if I pull the plug ?
I am getting:
Ax =2 Unknown Hardware Problem with Host


The notification codes sent to interface owners are listed in the table on page 10 of USBAPI.DOC. AX = 2 is OwnerCallDvcDisc, "Registered Device Disconnected" -- that's exactly what you should get!

AX = 2 "Unknown Hardware Problem with Host" is the immediate response returned by the Int 14h Request (the table on page 35 of USBAPI.DOC).

FYI, the notification codes for packet transactions are bit-mapped, and described in the table on page 14, and different from either of those listed above. It's probably going to be confusing for awhile to keep all of the different codes straight in your head.

Dinosaur wrote:What is annoying, is that I have read and re-read the desription on int31.0303 dozens of times, and I couldnt extract the fact that the Struc is left intact for PM to use.


I haven't read it in detail or multiple times like you have, but I think I remember it saying you couldn't depend on it being in any particular state except during the call-back process itself. It makes sense that DPMI wouldn't mess with it between calls, but it may depend on the specific DPMI implementation.
Bret
 
Posts: 478
Joined: Fri Oct 10, 2008 3:43 am
Location: Rio Rancho, NM

Re: USB I/O

Postby c.m » Fri Sep 18, 2009 6:20 pm

Dinosaur wrote:Also, I would use different callbacks for different usb devices, as the function assigned to them
would need to be identified at the time of callback.


You could use an array of structures where you save the UserPktId along with the other information you need for that device in each structure. As Bret mentioned, there might be a limited number of RM callbacks that you can allocate from the DPMI host.

Bret wrote:CLI disables interrupts, STI enables them. Maybe backwards from what seems logical, but the way it is nonetheless.


The thought never occured to me. These instructions are just like CLC (Clear Carry Flag) and STC (Set Carry Flag): "Clear Interrupt Flag" and "Set Interrupt Flag". Interrupts are allowed when the Interrupt Flag is set. Easy. (I see however it might be confusing when you read it as "Clear Interrupts" and "Set Interrupts".)

Dinosaur wrote:
Bret wrote:STI enables them.


That is why, when I was finished with the Callback I issued a STI


If the DPMI host wanted you to run with enabled interrupts I'd better enter your callback in that state. Enabling interrupts just in front of the iret will service pending interrupts, and then immediately disable interrupts again (because I'm sure the DPMI host internally disables interrupts, and the iret probably doesn't return directly to Real or even V86 mode). I don't know if that's a good thing to do or not.

I decided that the using a seperate global variable as a flag may be the best way.
In other words, extend the RM_Struc and put the PM ds in it.
Then in the callback, restore the PM ds, and set the Flag.
Restore the RM ds, and quit.


You don't need to restore the DS passed to you: It isn't the "RM DS" (which would indicate a segment, or the RM DS's segment value converted to a selector) but instead points along with esi to the RM stack. Int31.0303 documentation states that you don't need to preserve that pointer, however.
C. Masloch
c.m
 
Posts: 18
Joined: Sat Sep 12, 2009 5:49 am
Location: Germany

PreviousNext

Return to Programs

Who is online

Users browsing this forum: No registered users and 2 guests

cron