freebsd-dev/sys/compat/ndis/subr_usbd.c

1494 lines
40 KiB
C
Raw Normal View History

- Correct one aspect of the driver_object/device_object/IRP framework: when we create a PDO, the driver_object associated with it is that of the parent driver, not the driver we're trying to attach. For example, if we attach a PCI device, the PDO we pass to the NdisAddDevice() function should contain a pointer to fake_pci_driver, not to the NDIS driver itself. For PCI or PCMCIA devices this doesn't matter because the child never needs to talk to the parent bus driver, but for USB, the child needs to be able to send IRPs to the parent USB bus driver, and for that to work the parent USB bus driver has to be hung off the PDO. This involves modifying windrv_lookup() so that we can search for bus drivers by name, if necessary. Our fake bus drivers attach themselves as "PCI Bus," "PCCARD Bus" and "USB Bus," so we can search for them using those names. The individual attachment stubs now create and attach PDOs to the parent bus drivers instead of hanging them off the NDIS driver's object, and in if_ndis.c, we now search for the correct driver object depending on the bus type, and use that to find the correct PDO. With this fix, I can get my sample USB ethernet driver to deliver an IRP to my fake parent USB bus driver's dispatch routines. - Add stub modules for USB support: subr_usbd.c, usbd_var.h and if_ndis_usb.c. The subr_usbd.c module is hooked up the build but currently doesn't do very much. It provides the stub USB parent driver object and a dispatch routine for IRM_MJ_INTERNAL_DEVICE_CONTROL. The only exported function at the moment is USBD_GetUSBDIVersion(). The if_ndis_usb.c stub compiles, but is not hooked up to the build yet. I'm putting these here so I can keep them under source code control as I flesh them out.
2005-02-24 21:49:14 +00:00
/*-
* Copyright (c) 2005
* Bill Paul <wpaul@windriver.com>. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Bill Paul.
* 4. Neither the name of the author nor the names of any co-contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/unistd.h>
#include <sys/types.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/module.h>
#include <sys/conf.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <machine/bus.h>
- Correct one aspect of the driver_object/device_object/IRP framework: when we create a PDO, the driver_object associated with it is that of the parent driver, not the driver we're trying to attach. For example, if we attach a PCI device, the PDO we pass to the NdisAddDevice() function should contain a pointer to fake_pci_driver, not to the NDIS driver itself. For PCI or PCMCIA devices this doesn't matter because the child never needs to talk to the parent bus driver, but for USB, the child needs to be able to send IRPs to the parent USB bus driver, and for that to work the parent USB bus driver has to be hung off the PDO. This involves modifying windrv_lookup() so that we can search for bus drivers by name, if necessary. Our fake bus drivers attach themselves as "PCI Bus," "PCCARD Bus" and "USB Bus," so we can search for them using those names. The individual attachment stubs now create and attach PDOs to the parent bus drivers instead of hanging them off the NDIS driver's object, and in if_ndis.c, we now search for the correct driver object depending on the bus type, and use that to find the correct PDO. With this fix, I can get my sample USB ethernet driver to deliver an IRP to my fake parent USB bus driver's dispatch routines. - Add stub modules for USB support: subr_usbd.c, usbd_var.h and if_ndis_usb.c. The subr_usbd.c module is hooked up the build but currently doesn't do very much. It provides the stub USB parent driver object and a dispatch routine for IRM_MJ_INTERNAL_DEVICE_CONTROL. The only exported function at the moment is USBD_GetUSBDIVersion(). The if_ndis_usb.c stub compiles, but is not hooked up to the build yet. I'm putting these here so I can keep them under source code control as I flesh them out.
2005-02-24 21:49:14 +00:00
#include <sys/bus.h>
#include <sys/queue.h>
#include <net/if.h>
#include <net/if_media.h>
#include <net80211/ieee80211_var.h>
#include <net80211/ieee80211_ioctl.h>
#include <dev/usb/usb.h>
#include <dev/usb/usb_core.h>
#include <dev/usb/usb_busdma.h>
#include <dev/usb/usb_defs.h>
#include <dev/usb/usb_process.h>
#include <dev/usb/usb_device.h>
#include <dev/usb/usb_error.h>
#include <dev/usb/usb_parse.h>
#include <dev/usb/usb_request.h>
- Correct one aspect of the driver_object/device_object/IRP framework: when we create a PDO, the driver_object associated with it is that of the parent driver, not the driver we're trying to attach. For example, if we attach a PCI device, the PDO we pass to the NdisAddDevice() function should contain a pointer to fake_pci_driver, not to the NDIS driver itself. For PCI or PCMCIA devices this doesn't matter because the child never needs to talk to the parent bus driver, but for USB, the child needs to be able to send IRPs to the parent USB bus driver, and for that to work the parent USB bus driver has to be hung off the PDO. This involves modifying windrv_lookup() so that we can search for bus drivers by name, if necessary. Our fake bus drivers attach themselves as "PCI Bus," "PCCARD Bus" and "USB Bus," so we can search for them using those names. The individual attachment stubs now create and attach PDOs to the parent bus drivers instead of hanging them off the NDIS driver's object, and in if_ndis.c, we now search for the correct driver object depending on the bus type, and use that to find the correct PDO. With this fix, I can get my sample USB ethernet driver to deliver an IRP to my fake parent USB bus driver's dispatch routines. - Add stub modules for USB support: subr_usbd.c, usbd_var.h and if_ndis_usb.c. The subr_usbd.c module is hooked up the build but currently doesn't do very much. It provides the stub USB parent driver object and a dispatch routine for IRM_MJ_INTERNAL_DEVICE_CONTROL. The only exported function at the moment is USBD_GetUSBDIVersion(). The if_ndis_usb.c stub compiles, but is not hooked up to the build yet. I'm putting these here so I can keep them under source code control as I flesh them out.
2005-02-24 21:49:14 +00:00
#include <compat/ndis/pe_var.h>
#include <compat/ndis/cfg_var.h>
#include <compat/ndis/resource_var.h>
#include <compat/ndis/ntoskrnl_var.h>
#include <compat/ndis/ndis_var.h>
#include <compat/ndis/hal_var.h>
#include <compat/ndis/usbd_var.h>
#include <dev/if_ndis/if_ndisvar.h>
- Correct one aspect of the driver_object/device_object/IRP framework: when we create a PDO, the driver_object associated with it is that of the parent driver, not the driver we're trying to attach. For example, if we attach a PCI device, the PDO we pass to the NdisAddDevice() function should contain a pointer to fake_pci_driver, not to the NDIS driver itself. For PCI or PCMCIA devices this doesn't matter because the child never needs to talk to the parent bus driver, but for USB, the child needs to be able to send IRPs to the parent USB bus driver, and for that to work the parent USB bus driver has to be hung off the PDO. This involves modifying windrv_lookup() so that we can search for bus drivers by name, if necessary. Our fake bus drivers attach themselves as "PCI Bus," "PCCARD Bus" and "USB Bus," so we can search for them using those names. The individual attachment stubs now create and attach PDOs to the parent bus drivers instead of hanging them off the NDIS driver's object, and in if_ndis.c, we now search for the correct driver object depending on the bus type, and use that to find the correct PDO. With this fix, I can get my sample USB ethernet driver to deliver an IRP to my fake parent USB bus driver's dispatch routines. - Add stub modules for USB support: subr_usbd.c, usbd_var.h and if_ndis_usb.c. The subr_usbd.c module is hooked up the build but currently doesn't do very much. It provides the stub USB parent driver object and a dispatch routine for IRM_MJ_INTERNAL_DEVICE_CONTROL. The only exported function at the moment is USBD_GetUSBDIVersion(). The if_ndis_usb.c stub compiles, but is not hooked up to the build yet. I'm putting these here so I can keep them under source code control as I flesh them out.
2005-02-24 21:49:14 +00:00
static driver_object usbd_driver;
static usb2_callback_t usbd_non_isoc_callback;
static usb2_callback_t usbd_ctrl_callback;
#define USBD_CTRL_READ_PIPE 0
#define USBD_CTRL_WRITE_PIPE 1
#define USBD_CTRL_MAX_PIPE 2
#define USBD_CTRL_READ_BUFFER_SP 256
#define USBD_CTRL_READ_BUFFER_SIZE \
(sizeof(struct usb2_device_request) + USBD_CTRL_READ_BUFFER_SP)
#define USBD_CTRL_WRITE_BUFFER_SIZE \
(sizeof(struct usb2_device_request))
static struct usb2_config usbd_default_epconfig[USBD_CTRL_MAX_PIPE] = {
[USBD_CTRL_READ_PIPE] = {
.type = UE_CONTROL,
.endpoint = 0x00, /* control pipe */
.direction = UE_DIR_ANY,
.if_index = 0,
.mh.bufsize = USBD_CTRL_READ_BUFFER_SIZE,
.mh.flags = { .short_xfer_ok = 1, },
.mh.callback = &usbd_ctrl_callback,
.mh.timeout = 5000, /* 5 seconds */
},
[USBD_CTRL_WRITE_PIPE] = {
.type = UE_CONTROL,
.endpoint = 0x00, /* control pipe */
.direction = UE_DIR_ANY,
.if_index = 0,
.mh.bufsize = USBD_CTRL_WRITE_BUFFER_SIZE,
.mh.flags = { .proxy_buffer = 1, },
.mh.callback = &usbd_ctrl_callback,
.mh.timeout = 5000, /* 5 seconds */
}
};
- Correct one aspect of the driver_object/device_object/IRP framework: when we create a PDO, the driver_object associated with it is that of the parent driver, not the driver we're trying to attach. For example, if we attach a PCI device, the PDO we pass to the NdisAddDevice() function should contain a pointer to fake_pci_driver, not to the NDIS driver itself. For PCI or PCMCIA devices this doesn't matter because the child never needs to talk to the parent bus driver, but for USB, the child needs to be able to send IRPs to the parent USB bus driver, and for that to work the parent USB bus driver has to be hung off the PDO. This involves modifying windrv_lookup() so that we can search for bus drivers by name, if necessary. Our fake bus drivers attach themselves as "PCI Bus," "PCCARD Bus" and "USB Bus," so we can search for them using those names. The individual attachment stubs now create and attach PDOs to the parent bus drivers instead of hanging them off the NDIS driver's object, and in if_ndis.c, we now search for the correct driver object depending on the bus type, and use that to find the correct PDO. With this fix, I can get my sample USB ethernet driver to deliver an IRP to my fake parent USB bus driver's dispatch routines. - Add stub modules for USB support: subr_usbd.c, usbd_var.h and if_ndis_usb.c. The subr_usbd.c module is hooked up the build but currently doesn't do very much. It provides the stub USB parent driver object and a dispatch routine for IRM_MJ_INTERNAL_DEVICE_CONTROL. The only exported function at the moment is USBD_GetUSBDIVersion(). The if_ndis_usb.c stub compiles, but is not hooked up to the build yet. I'm putting these here so I can keep them under source code control as I flesh them out.
2005-02-24 21:49:14 +00:00
static int32_t usbd_func_bulkintr(irp *);
static int32_t usbd_func_vendorclass(irp *);
static int32_t usbd_func_selconf(irp *);
static int32_t usbd_func_abort_pipe(irp *);
static usb2_error_t usbd_setup_endpoint(irp *, uint8_t,
struct usb2_endpoint_descriptor *);
static usb2_error_t usbd_setup_endpoint_default(irp *, uint8_t);
static usb2_error_t usbd_setup_endpoint_one(irp *, uint8_t,
struct ndisusb_ep *, struct usb2_config *);
static int32_t usbd_func_getdesc(irp *);
static union usbd_urb *usbd_geturb(irp *);
static struct ndisusb_ep*usbd_get_ndisep(irp *, usb_endpoint_descriptor_t *);
static int32_t usbd_iodispatch(device_object *, irp *);
static int32_t usbd_ioinvalid(device_object *, irp *);
static int32_t usbd_pnp(device_object *, irp *);
static int32_t usbd_power(device_object *, irp *);
static void usbd_irpcancel(device_object *, irp *);
static int32_t usbd_submit_urb(irp *);
static int32_t usbd_urb2nt(int32_t);
static void usbd_task(device_object *, void *);
static int32_t usbd_taskadd(irp *, unsigned);
static void usbd_xfertask(device_object *, void *);
static void dummy(void);
- Correct one aspect of the driver_object/device_object/IRP framework: when we create a PDO, the driver_object associated with it is that of the parent driver, not the driver we're trying to attach. For example, if we attach a PCI device, the PDO we pass to the NdisAddDevice() function should contain a pointer to fake_pci_driver, not to the NDIS driver itself. For PCI or PCMCIA devices this doesn't matter because the child never needs to talk to the parent bus driver, but for USB, the child needs to be able to send IRPs to the parent USB bus driver, and for that to work the parent USB bus driver has to be hung off the PDO. This involves modifying windrv_lookup() so that we can search for bus drivers by name, if necessary. Our fake bus drivers attach themselves as "PCI Bus," "PCCARD Bus" and "USB Bus," so we can search for them using those names. The individual attachment stubs now create and attach PDOs to the parent bus drivers instead of hanging them off the NDIS driver's object, and in if_ndis.c, we now search for the correct driver object depending on the bus type, and use that to find the correct PDO. With this fix, I can get my sample USB ethernet driver to deliver an IRP to my fake parent USB bus driver's dispatch routines. - Add stub modules for USB support: subr_usbd.c, usbd_var.h and if_ndis_usb.c. The subr_usbd.c module is hooked up the build but currently doesn't do very much. It provides the stub USB parent driver object and a dispatch routine for IRM_MJ_INTERNAL_DEVICE_CONTROL. The only exported function at the moment is USBD_GetUSBDIVersion(). The if_ndis_usb.c stub compiles, but is not hooked up to the build yet. I'm putting these here so I can keep them under source code control as I flesh them out.
2005-02-24 21:49:14 +00:00
static union usbd_urb *USBD_CreateConfigurationRequestEx(
usb_config_descriptor_t *,
struct usbd_interface_list_entry *);
static union usbd_urb *USBD_CreateConfigurationRequest(
usb_config_descriptor_t *,
uint16_t *);
static void USBD_GetUSBDIVersion(usbd_version_info *);
static usb_interface_descriptor_t *USBD_ParseConfigurationDescriptorEx(
usb_config_descriptor_t *, void *, int32_t, int32_t,
int32_t, int32_t, int32_t);
static usb_interface_descriptor_t *USBD_ParseConfigurationDescriptor(
usb_config_descriptor_t *, uint8_t, uint8_t);
/*
* We need to wrap these functions because these need `context switch' from
* Windows to UNIX before it's called.
*/
static funcptr usbd_iodispatch_wrap;
static funcptr usbd_ioinvalid_wrap;
static funcptr usbd_pnp_wrap;
static funcptr usbd_power_wrap;
static funcptr usbd_irpcancel_wrap;
static funcptr usbd_task_wrap;
static funcptr usbd_xfertask_wrap;
- Correct one aspect of the driver_object/device_object/IRP framework: when we create a PDO, the driver_object associated with it is that of the parent driver, not the driver we're trying to attach. For example, if we attach a PCI device, the PDO we pass to the NdisAddDevice() function should contain a pointer to fake_pci_driver, not to the NDIS driver itself. For PCI or PCMCIA devices this doesn't matter because the child never needs to talk to the parent bus driver, but for USB, the child needs to be able to send IRPs to the parent USB bus driver, and for that to work the parent USB bus driver has to be hung off the PDO. This involves modifying windrv_lookup() so that we can search for bus drivers by name, if necessary. Our fake bus drivers attach themselves as "PCI Bus," "PCCARD Bus" and "USB Bus," so we can search for them using those names. The individual attachment stubs now create and attach PDOs to the parent bus drivers instead of hanging them off the NDIS driver's object, and in if_ndis.c, we now search for the correct driver object depending on the bus type, and use that to find the correct PDO. With this fix, I can get my sample USB ethernet driver to deliver an IRP to my fake parent USB bus driver's dispatch routines. - Add stub modules for USB support: subr_usbd.c, usbd_var.h and if_ndis_usb.c. The subr_usbd.c module is hooked up the build but currently doesn't do very much. It provides the stub USB parent driver object and a dispatch routine for IRM_MJ_INTERNAL_DEVICE_CONTROL. The only exported function at the moment is USBD_GetUSBDIVersion(). The if_ndis_usb.c stub compiles, but is not hooked up to the build yet. I'm putting these here so I can keep them under source code control as I flesh them out.
2005-02-24 21:49:14 +00:00
int
usbd_libinit(void)
{
image_patch_table *patch;
int i;
- Correct one aspect of the driver_object/device_object/IRP framework: when we create a PDO, the driver_object associated with it is that of the parent driver, not the driver we're trying to attach. For example, if we attach a PCI device, the PDO we pass to the NdisAddDevice() function should contain a pointer to fake_pci_driver, not to the NDIS driver itself. For PCI or PCMCIA devices this doesn't matter because the child never needs to talk to the parent bus driver, but for USB, the child needs to be able to send IRPs to the parent USB bus driver, and for that to work the parent USB bus driver has to be hung off the PDO. This involves modifying windrv_lookup() so that we can search for bus drivers by name, if necessary. Our fake bus drivers attach themselves as "PCI Bus," "PCCARD Bus" and "USB Bus," so we can search for them using those names. The individual attachment stubs now create and attach PDOs to the parent bus drivers instead of hanging them off the NDIS driver's object, and in if_ndis.c, we now search for the correct driver object depending on the bus type, and use that to find the correct PDO. With this fix, I can get my sample USB ethernet driver to deliver an IRP to my fake parent USB bus driver's dispatch routines. - Add stub modules for USB support: subr_usbd.c, usbd_var.h and if_ndis_usb.c. The subr_usbd.c module is hooked up the build but currently doesn't do very much. It provides the stub USB parent driver object and a dispatch routine for IRM_MJ_INTERNAL_DEVICE_CONTROL. The only exported function at the moment is USBD_GetUSBDIVersion(). The if_ndis_usb.c stub compiles, but is not hooked up to the build yet. I'm putting these here so I can keep them under source code control as I flesh them out.
2005-02-24 21:49:14 +00:00
patch = usbd_functbl;
while (patch->ipt_func != NULL) {
windrv_wrap((funcptr)patch->ipt_func,
Create new i386 windows/bsd thunking layer, similar to the amd64 thunking layer, but with a twist. The twist has to do with the fact that Microsoft supports structured exception handling in kernel mode. On the i386 arch, exception handling is implemented by hanging an exception registration list off the Thread Environment Block (TEB), and the TEB is accessed via the %fs register. The problem is, we use %fs as a pointer to the pcpu stucture, which means any driver that tries to write through %fs:0 will overwrite the curthread pointer and make a serious mess of things. To get around this, Project Evil now creates a special entry in the GDT on each processor. When we call into Windows code, a context switch routine will fix up %fs so it points to our new descriptor, which in turn points to a fake TEB. When the Windows code returns, or calls out to an external routine, we swap %fs back again. Currently, Project Evil makes use of GDT slot 7, which is all 0s by default. I fully expect someone to jump up and say I can't do that, but I couldn't find any code that makes use of this entry anywhere. Sadly, this was the only method I could come up with that worked on both UP and SMP. (Modifying the LDT works on UP, but becomes incredibly complicated on SMP.) If necessary, the context switching stuff can be yanked out while preserving the convention calling wrappers. (Fortunately, it looks like Microsoft uses some special epilog/prolog code on amd64 to implement exception handling, so the same nastiness won't be necessary on that arch.) The advantages are: - Any driver that uses %fs as though it were a TEB pointer won't clobber pcpu. - All the __stdcall/__fastcall/__regparm stuff that's specific to gcc goes away. Also, while I'm here, switch NdisGetSystemUpTime() back to using nanouptime() again. It turns out nanouptime() is way more accurate than just using ticks(). On slower machines, the Atheros drivers I tested seem to take a long time to associate due to the loss in accuracy.
2005-04-11 02:02:35 +00:00
(funcptr *)&patch->ipt_wrap,
patch->ipt_argcnt, patch->ipt_ftype);
- Correct one aspect of the driver_object/device_object/IRP framework: when we create a PDO, the driver_object associated with it is that of the parent driver, not the driver we're trying to attach. For example, if we attach a PCI device, the PDO we pass to the NdisAddDevice() function should contain a pointer to fake_pci_driver, not to the NDIS driver itself. For PCI or PCMCIA devices this doesn't matter because the child never needs to talk to the parent bus driver, but for USB, the child needs to be able to send IRPs to the parent USB bus driver, and for that to work the parent USB bus driver has to be hung off the PDO. This involves modifying windrv_lookup() so that we can search for bus drivers by name, if necessary. Our fake bus drivers attach themselves as "PCI Bus," "PCCARD Bus" and "USB Bus," so we can search for them using those names. The individual attachment stubs now create and attach PDOs to the parent bus drivers instead of hanging them off the NDIS driver's object, and in if_ndis.c, we now search for the correct driver object depending on the bus type, and use that to find the correct PDO. With this fix, I can get my sample USB ethernet driver to deliver an IRP to my fake parent USB bus driver's dispatch routines. - Add stub modules for USB support: subr_usbd.c, usbd_var.h and if_ndis_usb.c. The subr_usbd.c module is hooked up the build but currently doesn't do very much. It provides the stub USB parent driver object and a dispatch routine for IRM_MJ_INTERNAL_DEVICE_CONTROL. The only exported function at the moment is USBD_GetUSBDIVersion(). The if_ndis_usb.c stub compiles, but is not hooked up to the build yet. I'm putting these here so I can keep them under source code control as I flesh them out.
2005-02-24 21:49:14 +00:00
patch++;
}
windrv_wrap((funcptr)usbd_ioinvalid,
(funcptr *)&usbd_ioinvalid_wrap, 2, WINDRV_WRAP_STDCALL);
windrv_wrap((funcptr)usbd_iodispatch,
(funcptr *)&usbd_iodispatch_wrap, 2, WINDRV_WRAP_STDCALL);
windrv_wrap((funcptr)usbd_pnp,
(funcptr *)&usbd_pnp_wrap, 2, WINDRV_WRAP_STDCALL);
windrv_wrap((funcptr)usbd_power,
(funcptr *)&usbd_power_wrap, 2, WINDRV_WRAP_STDCALL);
windrv_wrap((funcptr)usbd_irpcancel,
(funcptr *)&usbd_irpcancel_wrap, 2, WINDRV_WRAP_STDCALL);
windrv_wrap((funcptr)usbd_task,
(funcptr *)&usbd_task_wrap, 2, WINDRV_WRAP_STDCALL);
windrv_wrap((funcptr)usbd_xfertask,
(funcptr *)&usbd_xfertask_wrap, 2, WINDRV_WRAP_STDCALL);
- Correct one aspect of the driver_object/device_object/IRP framework: when we create a PDO, the driver_object associated with it is that of the parent driver, not the driver we're trying to attach. For example, if we attach a PCI device, the PDO we pass to the NdisAddDevice() function should contain a pointer to fake_pci_driver, not to the NDIS driver itself. For PCI or PCMCIA devices this doesn't matter because the child never needs to talk to the parent bus driver, but for USB, the child needs to be able to send IRPs to the parent USB bus driver, and for that to work the parent USB bus driver has to be hung off the PDO. This involves modifying windrv_lookup() so that we can search for bus drivers by name, if necessary. Our fake bus drivers attach themselves as "PCI Bus," "PCCARD Bus" and "USB Bus," so we can search for them using those names. The individual attachment stubs now create and attach PDOs to the parent bus drivers instead of hanging them off the NDIS driver's object, and in if_ndis.c, we now search for the correct driver object depending on the bus type, and use that to find the correct PDO. With this fix, I can get my sample USB ethernet driver to deliver an IRP to my fake parent USB bus driver's dispatch routines. - Add stub modules for USB support: subr_usbd.c, usbd_var.h and if_ndis_usb.c. The subr_usbd.c module is hooked up the build but currently doesn't do very much. It provides the stub USB parent driver object and a dispatch routine for IRM_MJ_INTERNAL_DEVICE_CONTROL. The only exported function at the moment is USBD_GetUSBDIVersion(). The if_ndis_usb.c stub compiles, but is not hooked up to the build yet. I'm putting these here so I can keep them under source code control as I flesh them out.
2005-02-24 21:49:14 +00:00
/* Create a fake USB driver instance. */
windrv_bus_attach(&usbd_driver, "USB Bus");
/* Set up our dipatch routine. */
for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++)
usbd_driver.dro_dispatch[i] =
(driver_dispatch)usbd_ioinvalid_wrap;
- Correct one aspect of the driver_object/device_object/IRP framework: when we create a PDO, the driver_object associated with it is that of the parent driver, not the driver we're trying to attach. For example, if we attach a PCI device, the PDO we pass to the NdisAddDevice() function should contain a pointer to fake_pci_driver, not to the NDIS driver itself. For PCI or PCMCIA devices this doesn't matter because the child never needs to talk to the parent bus driver, but for USB, the child needs to be able to send IRPs to the parent USB bus driver, and for that to work the parent USB bus driver has to be hung off the PDO. This involves modifying windrv_lookup() so that we can search for bus drivers by name, if necessary. Our fake bus drivers attach themselves as "PCI Bus," "PCCARD Bus" and "USB Bus," so we can search for them using those names. The individual attachment stubs now create and attach PDOs to the parent bus drivers instead of hanging them off the NDIS driver's object, and in if_ndis.c, we now search for the correct driver object depending on the bus type, and use that to find the correct PDO. With this fix, I can get my sample USB ethernet driver to deliver an IRP to my fake parent USB bus driver's dispatch routines. - Add stub modules for USB support: subr_usbd.c, usbd_var.h and if_ndis_usb.c. The subr_usbd.c module is hooked up the build but currently doesn't do very much. It provides the stub USB parent driver object and a dispatch routine for IRM_MJ_INTERNAL_DEVICE_CONTROL. The only exported function at the moment is USBD_GetUSBDIVersion(). The if_ndis_usb.c stub compiles, but is not hooked up to the build yet. I'm putting these here so I can keep them under source code control as I flesh them out.
2005-02-24 21:49:14 +00:00
usbd_driver.dro_dispatch[IRP_MJ_INTERNAL_DEVICE_CONTROL] =
(driver_dispatch)usbd_iodispatch_wrap;
usbd_driver.dro_dispatch[IRP_MJ_DEVICE_CONTROL] =
(driver_dispatch)usbd_iodispatch_wrap;
usbd_driver.dro_dispatch[IRP_MJ_POWER] =
(driver_dispatch)usbd_power_wrap;
usbd_driver.dro_dispatch[IRP_MJ_PNP] =
(driver_dispatch)usbd_pnp_wrap;
- Correct one aspect of the driver_object/device_object/IRP framework: when we create a PDO, the driver_object associated with it is that of the parent driver, not the driver we're trying to attach. For example, if we attach a PCI device, the PDO we pass to the NdisAddDevice() function should contain a pointer to fake_pci_driver, not to the NDIS driver itself. For PCI or PCMCIA devices this doesn't matter because the child never needs to talk to the parent bus driver, but for USB, the child needs to be able to send IRPs to the parent USB bus driver, and for that to work the parent USB bus driver has to be hung off the PDO. This involves modifying windrv_lookup() so that we can search for bus drivers by name, if necessary. Our fake bus drivers attach themselves as "PCI Bus," "PCCARD Bus" and "USB Bus," so we can search for them using those names. The individual attachment stubs now create and attach PDOs to the parent bus drivers instead of hanging them off the NDIS driver's object, and in if_ndis.c, we now search for the correct driver object depending on the bus type, and use that to find the correct PDO. With this fix, I can get my sample USB ethernet driver to deliver an IRP to my fake parent USB bus driver's dispatch routines. - Add stub modules for USB support: subr_usbd.c, usbd_var.h and if_ndis_usb.c. The subr_usbd.c module is hooked up the build but currently doesn't do very much. It provides the stub USB parent driver object and a dispatch routine for IRM_MJ_INTERNAL_DEVICE_CONTROL. The only exported function at the moment is USBD_GetUSBDIVersion(). The if_ndis_usb.c stub compiles, but is not hooked up to the build yet. I'm putting these here so I can keep them under source code control as I flesh them out.
2005-02-24 21:49:14 +00:00
return(0);
}
int
usbd_libfini(void)
{
image_patch_table *patch;
patch = usbd_functbl;
while (patch->ipt_func != NULL) {
windrv_unwrap(patch->ipt_wrap);
patch++;
}
windrv_unwrap(usbd_ioinvalid_wrap);
windrv_unwrap(usbd_iodispatch_wrap);
windrv_unwrap(usbd_pnp_wrap);
windrv_unwrap(usbd_power_wrap);
windrv_unwrap(usbd_irpcancel_wrap);
windrv_unwrap(usbd_task_wrap);
windrv_unwrap(usbd_xfertask_wrap);
- Correct one aspect of the driver_object/device_object/IRP framework: when we create a PDO, the driver_object associated with it is that of the parent driver, not the driver we're trying to attach. For example, if we attach a PCI device, the PDO we pass to the NdisAddDevice() function should contain a pointer to fake_pci_driver, not to the NDIS driver itself. For PCI or PCMCIA devices this doesn't matter because the child never needs to talk to the parent bus driver, but for USB, the child needs to be able to send IRPs to the parent USB bus driver, and for that to work the parent USB bus driver has to be hung off the PDO. This involves modifying windrv_lookup() so that we can search for bus drivers by name, if necessary. Our fake bus drivers attach themselves as "PCI Bus," "PCCARD Bus" and "USB Bus," so we can search for them using those names. The individual attachment stubs now create and attach PDOs to the parent bus drivers instead of hanging them off the NDIS driver's object, and in if_ndis.c, we now search for the correct driver object depending on the bus type, and use that to find the correct PDO. With this fix, I can get my sample USB ethernet driver to deliver an IRP to my fake parent USB bus driver's dispatch routines. - Add stub modules for USB support: subr_usbd.c, usbd_var.h and if_ndis_usb.c. The subr_usbd.c module is hooked up the build but currently doesn't do very much. It provides the stub USB parent driver object and a dispatch routine for IRM_MJ_INTERNAL_DEVICE_CONTROL. The only exported function at the moment is USBD_GetUSBDIVersion(). The if_ndis_usb.c stub compiles, but is not hooked up to the build yet. I'm putting these here so I can keep them under source code control as I flesh them out.
2005-02-24 21:49:14 +00:00
free(usbd_driver.dro_drivername.us_buf, M_DEVBUF);
return(0);
}
static int32_t
- Correct one aspect of the driver_object/device_object/IRP framework: when we create a PDO, the driver_object associated with it is that of the parent driver, not the driver we're trying to attach. For example, if we attach a PCI device, the PDO we pass to the NdisAddDevice() function should contain a pointer to fake_pci_driver, not to the NDIS driver itself. For PCI or PCMCIA devices this doesn't matter because the child never needs to talk to the parent bus driver, but for USB, the child needs to be able to send IRPs to the parent USB bus driver, and for that to work the parent USB bus driver has to be hung off the PDO. This involves modifying windrv_lookup() so that we can search for bus drivers by name, if necessary. Our fake bus drivers attach themselves as "PCI Bus," "PCCARD Bus" and "USB Bus," so we can search for them using those names. The individual attachment stubs now create and attach PDOs to the parent bus drivers instead of hanging them off the NDIS driver's object, and in if_ndis.c, we now search for the correct driver object depending on the bus type, and use that to find the correct PDO. With this fix, I can get my sample USB ethernet driver to deliver an IRP to my fake parent USB bus driver's dispatch routines. - Add stub modules for USB support: subr_usbd.c, usbd_var.h and if_ndis_usb.c. The subr_usbd.c module is hooked up the build but currently doesn't do very much. It provides the stub USB parent driver object and a dispatch routine for IRM_MJ_INTERNAL_DEVICE_CONTROL. The only exported function at the moment is USBD_GetUSBDIVersion(). The if_ndis_usb.c stub compiles, but is not hooked up to the build yet. I'm putting these here so I can keep them under source code control as I flesh them out.
2005-02-24 21:49:14 +00:00
usbd_iodispatch(dobj, ip)
device_object *dobj;
irp *ip;
{
device_t dev = dobj->do_devext;
int32_t status;
struct io_stack_location *irp_sl;
irp_sl = IoGetCurrentIrpStackLocation(ip);
switch (irp_sl->isl_parameters.isl_ioctl.isl_iocode) {
case IOCTL_INTERNAL_USB_SUBMIT_URB:
IRP_NDIS_DEV(ip) = dev;
status = usbd_submit_urb(ip);
break;
default:
device_printf(dev, "ioctl 0x%x isn't supported\n",
irp_sl->isl_parameters.isl_ioctl.isl_iocode);
status = USBD_STATUS_NOT_SUPPORTED;
break;
}
if (status == USBD_STATUS_PENDING)
return (STATUS_PENDING);
ip->irp_iostat.isb_status = usbd_urb2nt(status);
if (status != USBD_STATUS_SUCCESS)
ip->irp_iostat.isb_info = 0;
return (ip->irp_iostat.isb_status);
}
static int32_t
usbd_ioinvalid(dobj, ip)
device_object *dobj;
irp *ip;
{
device_t dev = dobj->do_devext;
struct io_stack_location *irp_sl;
irp_sl = IoGetCurrentIrpStackLocation(ip);
device_printf(dev, "invalid I/O dispatch %d:%d\n", irp_sl->isl_major,
irp_sl->isl_minor);
ip->irp_iostat.isb_status = STATUS_FAILURE;
ip->irp_iostat.isb_info = 0;
IoCompleteRequest(ip, IO_NO_INCREMENT);
return (STATUS_FAILURE);
}
static int32_t
usbd_pnp(dobj, ip)
device_object *dobj;
irp *ip;
{
device_t dev = dobj->do_devext;
struct io_stack_location *irp_sl;
irp_sl = IoGetCurrentIrpStackLocation(ip);
device_printf(dev, "%s: unsupported I/O dispatch %d:%d\n",
__func__, irp_sl->isl_major, irp_sl->isl_minor);
ip->irp_iostat.isb_status = STATUS_FAILURE;
ip->irp_iostat.isb_info = 0;
IoCompleteRequest(ip, IO_NO_INCREMENT);
return (STATUS_FAILURE);
}
static int32_t
usbd_power(dobj, ip)
device_object *dobj;
irp *ip;
{
device_t dev = dobj->do_devext;
struct io_stack_location *irp_sl;
irp_sl = IoGetCurrentIrpStackLocation(ip);
device_printf(dev, "%s: unsupported I/O dispatch %d:%d\n",
__func__, irp_sl->isl_major, irp_sl->isl_minor);
ip->irp_iostat.isb_status = STATUS_FAILURE;
ip->irp_iostat.isb_info = 0;
IoCompleteRequest(ip, IO_NO_INCREMENT);
return (STATUS_FAILURE);
}
/* Convert USBD_STATUS to NTSTATUS */
static int32_t
usbd_urb2nt(status)
int32_t status;
{
switch (status) {
case USBD_STATUS_SUCCESS:
return (STATUS_SUCCESS);
case USBD_STATUS_DEVICE_GONE:
return (STATUS_DEVICE_NOT_CONNECTED);
case USBD_STATUS_PENDING:
return (STATUS_PENDING);
case USBD_STATUS_NOT_SUPPORTED:
return (STATUS_NOT_IMPLEMENTED);
case USBD_STATUS_NO_MEMORY:
return (STATUS_NO_MEMORY);
case USBD_STATUS_REQUEST_FAILED:
return (STATUS_NOT_SUPPORTED);
case USBD_STATUS_CANCELED:
return (STATUS_CANCELLED);
default:
break;
}
return (STATUS_FAILURE);
}
/* Convert FreeBSD's usb2_error_t to USBD_STATUS */
static int32_t
usbd_usb2urb(int status)
{
switch (status) {
case USB_ERR_NORMAL_COMPLETION:
return (USBD_STATUS_SUCCESS);
case USB_ERR_PENDING_REQUESTS:
return (USBD_STATUS_PENDING);
case USB_ERR_TIMEOUT:
return (USBD_STATUS_TIMEOUT);
case USB_ERR_SHORT_XFER:
return (USBD_STATUS_ERROR_SHORT_TRANSFER);
case USB_ERR_IOERROR:
return (USBD_STATUS_XACT_ERROR);
case USB_ERR_NOMEM:
return (USBD_STATUS_NO_MEMORY);
case USB_ERR_INVAL:
return (USBD_STATUS_REQUEST_FAILED);
case USB_ERR_NOT_STARTED:
case USB_ERR_TOO_DEEP:
case USB_ERR_NO_POWER:
return (USBD_STATUS_DEVICE_GONE);
case USB_ERR_CANCELLED:
return (USBD_STATUS_CANCELED);
default:
break;
}
return (USBD_STATUS_NOT_SUPPORTED);
}
static union usbd_urb *
usbd_geturb(ip)
irp *ip;
{
struct io_stack_location *irp_sl;
irp_sl = IoGetCurrentIrpStackLocation(ip);
return (irp_sl->isl_parameters.isl_others.isl_arg1);
}
static int32_t
usbd_submit_urb(ip)
irp *ip;
{
device_t dev = IRP_NDIS_DEV(ip);
int32_t status;
union usbd_urb *urb;
urb = usbd_geturb(ip);
/*
* In a case of URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER,
* USBD_URB_STATUS(urb) would be set at callback functions like
* usbd_intr() or usbd_xfereof().
*/
switch (urb->uu_hdr.uuh_func) {
case URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER:
status = usbd_func_bulkintr(ip);
if (status != USBD_STATUS_SUCCESS &&
status != USBD_STATUS_PENDING)
USBD_URB_STATUS(urb) = status;
break;
case URB_FUNCTION_VENDOR_DEVICE:
case URB_FUNCTION_VENDOR_INTERFACE:
case URB_FUNCTION_VENDOR_ENDPOINT:
case URB_FUNCTION_VENDOR_OTHER:
case URB_FUNCTION_CLASS_DEVICE:
case URB_FUNCTION_CLASS_INTERFACE:
case URB_FUNCTION_CLASS_ENDPOINT:
case URB_FUNCTION_CLASS_OTHER:
status = usbd_func_vendorclass(ip);
USBD_URB_STATUS(urb) = status;
break;
case URB_FUNCTION_SELECT_CONFIGURATION:
status = usbd_func_selconf(ip);
USBD_URB_STATUS(urb) = status;
break;
case URB_FUNCTION_ABORT_PIPE:
status = usbd_func_abort_pipe(ip);
USBD_URB_STATUS(urb) = status;
break;
case URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE:
status = usbd_func_getdesc(ip);
USBD_URB_STATUS(urb) = status;
break;
default:
device_printf(dev, "func 0x%x isn't supported\n",
urb->uu_hdr.uuh_func);
USBD_URB_STATUS(urb) = status = USBD_STATUS_NOT_SUPPORTED;
break;
}
return (status);
}
static int32_t
usbd_func_getdesc(ip)
irp *ip;
{
#define NDISUSB_GETDESC_MAXRETRIES 3
device_t dev = IRP_NDIS_DEV(ip);
struct ndis_softc *sc = device_get_softc(dev);
struct usbd_urb_control_descriptor_request *ctldesc;
uint16_t actlen;
uint32_t len;
union usbd_urb *urb;
usb_config_descriptor_t *cdp;
usb2_error_t status;
urb = usbd_geturb(ip);
ctldesc = &urb->uu_ctldesc;
if (ctldesc->ucd_desctype == UDESC_CONFIG) {
/*
* The NDIS driver is not allowed to change the
* config! There is only one choice!
*/
cdp = usb2_get_config_descriptor(sc->ndisusb_dev);
if (cdp == NULL) {
status = USB_ERR_INVAL;
goto exit;
}
if (cdp->bDescriptorType != UDESC_CONFIG) {
device_printf(dev, "bad desc %d\n",
cdp->bDescriptorType);
status = USB_ERR_INVAL;
goto exit;
}
/* get minimum length */
len = MIN(UGETW(cdp->wTotalLength), ctldesc->ucd_trans_buflen);
/* copy out config descriptor */
memcpy(ctldesc->ucd_trans_buf, cdp, len);
/* set actual length */
actlen = len;
status = USB_ERR_NORMAL_COMPLETION;
} else {
NDISUSB_LOCK(sc);
status = usb2_req_get_desc(sc->ndisusb_dev, &sc->ndisusb_mtx,
&actlen, ctldesc->ucd_trans_buf, 2,
ctldesc->ucd_trans_buflen, ctldesc->ucd_langid,
ctldesc->ucd_desctype, ctldesc->ucd_idx,
NDISUSB_GETDESC_MAXRETRIES);
NDISUSB_UNLOCK(sc);
}
exit:
if (status != USB_ERR_NORMAL_COMPLETION) {
ctldesc->ucd_trans_buflen = 0;
return usbd_usb2urb(status);
}
ctldesc->ucd_trans_buflen = actlen;
ip->irp_iostat.isb_info = actlen;
return (USBD_STATUS_SUCCESS);
#undef NDISUSB_GETDESC_MAXRETRIES
}
static int32_t
usbd_func_selconf(ip)
irp *ip;
{
device_t dev = IRP_NDIS_DEV(ip);
int i, j;
struct ndis_softc *sc = device_get_softc(dev);
struct usb2_device *udev = sc->ndisusb_dev;
struct usb2_pipe *p = NULL;
struct usbd_interface_information *intf;
struct usbd_pipe_information *pipe;
struct usbd_urb_select_configuration *selconf;
union usbd_urb *urb;
usb_config_descriptor_t *conf;
usb_endpoint_descriptor_t *edesc;
usb2_error_t ret;
urb = usbd_geturb(ip);
selconf = &urb->uu_selconf;
conf = selconf->usc_conf;
if (conf == NULL) {
device_printf(dev, "select configuration is NULL\n");
return usbd_usb2urb(USB_ERR_NORMAL_COMPLETION);
}
intf = &selconf->usc_intf;
for (i = 0; i < conf->bNumInterface && intf->uii_len > 0; i++) {
ret = usb2_set_alt_interface_index(udev,
intf->uii_intfnum, intf->uii_altset);
if (ret != USB_ERR_NORMAL_COMPLETION && ret != USB_ERR_IN_USE) {
device_printf(dev,
"setting alternate interface failed: %s\n",
usb2_errstr(ret));
return usbd_usb2urb(ret);
}
for (j = 0; (p = usb2_pipe_foreach(udev, p)); j++) {
if (j >= intf->uii_numeps) {
device_printf(dev,
"endpoint %d and above are ignored",
intf->uii_numeps);
break;
}
edesc = p->edesc;
pipe = &intf->uii_pipes[j];
pipe->upi_handle = edesc;
pipe->upi_epaddr = edesc->bEndpointAddress;
pipe->upi_maxpktsize = UGETW(edesc->wMaxPacketSize);
pipe->upi_type = UE_GET_XFERTYPE(edesc->bmAttributes);
ret = usbd_setup_endpoint(ip, intf->uii_intfnum, edesc);
if (ret != USB_ERR_NORMAL_COMPLETION)
return usbd_usb2urb(ret);
if (pipe->upi_type != UE_INTERRUPT)
continue;
/* XXX we're following linux USB's interval policy. */
if (udev->speed == USB_SPEED_LOW)
pipe->upi_interval = edesc->bInterval + 5;
else if (udev->speed == USB_SPEED_FULL)
pipe->upi_interval = edesc->bInterval;
else {
int k0 = 0, k1 = 1;
do {
k1 = k1 * 2;
k0 = k0 + 1;
} while (k1 < edesc->bInterval);
pipe->upi_interval = k0;
}
}
intf = (struct usbd_interface_information *)(((char *)intf) +
intf->uii_len);
}
return USBD_STATUS_SUCCESS;
}
static usb2_error_t
usbd_setup_endpoint_one(ip, ifidx, ne, epconf)
irp *ip;
uint8_t ifidx;
struct ndisusb_ep *ne;
struct usb2_config *epconf;
{
device_t dev = IRP_NDIS_DEV(ip);
struct ndis_softc *sc = device_get_softc(dev);
struct usb2_xfer *xfer;
usb2_error_t status;
InitializeListHead(&ne->ne_active);
InitializeListHead(&ne->ne_pending);
KeInitializeSpinLock(&ne->ne_lock);
status = usb2_transfer_setup(sc->ndisusb_dev, &ifidx, ne->ne_xfer,
epconf, 1, sc, &sc->ndisusb_mtx);
if (status != USB_ERR_NORMAL_COMPLETION) {
device_printf(dev, "couldn't setup xfer: %s\n",
usb2_errstr(status));
return (status);
}
xfer = ne->ne_xfer[0];
xfer->priv_fifo = ne;
return (status);
}
static usb2_error_t
usbd_setup_endpoint_default(ip, ifidx)
irp *ip;
uint8_t ifidx;
{
device_t dev = IRP_NDIS_DEV(ip);
struct ndis_softc *sc = device_get_softc(dev);
usb2_error_t status;
if (ifidx > 0)
device_printf(dev, "warning: ifidx > 0 isn't supported.\n");
status = usbd_setup_endpoint_one(ip, ifidx, &sc->ndisusb_dread_ep,
&usbd_default_epconfig[USBD_CTRL_READ_PIPE]);
if (status != USB_ERR_NORMAL_COMPLETION)
return (status);
status = usbd_setup_endpoint_one(ip, ifidx, &sc->ndisusb_dwrite_ep,
&usbd_default_epconfig[USBD_CTRL_WRITE_PIPE]);
return (status);
}
static usb2_error_t
usbd_setup_endpoint(ip, ifidx, ep)
irp *ip;
uint8_t ifidx;
struct usb2_endpoint_descriptor *ep;
{
device_t dev = IRP_NDIS_DEV(ip);
struct ndis_softc *sc = device_get_softc(dev);
struct ndisusb_ep *ne;
struct usb2_config cfg;
struct usb2_xfer *xfer;
usb2_error_t status;
/* check for non-supported transfer types */
if (UE_GET_XFERTYPE(ep->bmAttributes) == UE_CONTROL ||
UE_GET_XFERTYPE(ep->bmAttributes) == UE_ISOCHRONOUS) {
device_printf(dev, "%s: unsuppotted transfer types %#x\n",
__func__, UE_GET_XFERTYPE(ep->bmAttributes));
return (USB_ERR_INVAL);
}
ne = &sc->ndisusb_ep[NDISUSB_GET_ENDPT(ep->bEndpointAddress)];
InitializeListHead(&ne->ne_active);
InitializeListHead(&ne->ne_pending);
KeInitializeSpinLock(&ne->ne_lock);
ne->ne_dirin = UE_GET_DIR(ep->bEndpointAddress) >> 7;
memset(&cfg, 0, sizeof(struct usb2_config));
cfg.type = UE_GET_XFERTYPE(ep->bmAttributes);
cfg.endpoint = UE_GET_ADDR(ep->bEndpointAddress);
cfg.direction = UE_GET_DIR(ep->bEndpointAddress);
cfg.mh.callback = &usbd_non_isoc_callback;
cfg.mh.bufsize = UGETW(ep->wMaxPacketSize);
cfg.mh.flags.proxy_buffer = 1;
if (UE_GET_DIR(ep->bEndpointAddress) == UE_DIR_IN)
cfg.mh.flags.short_xfer_ok = 1;
status = usb2_transfer_setup(sc->ndisusb_dev, &ifidx, ne->ne_xfer,
&cfg, 1, sc, &sc->ndisusb_mtx);
if (status != USB_ERR_NORMAL_COMPLETION) {
device_printf(dev, "couldn't setup xfer: %s\n",
usb2_errstr(status));
return (status);
}
xfer = ne->ne_xfer[0];
xfer->priv_fifo = ne;
if (UE_GET_DIR(ep->bEndpointAddress) == UE_DIR_IN)
xfer->timeout = NDISUSB_NO_TIMEOUT;
else {
if (UE_GET_XFERTYPE(ep->bmAttributes) == UE_BULK)
xfer->timeout = NDISUSB_TX_TIMEOUT;
else
xfer->timeout = NDISUSB_INTR_TIMEOUT;
}
return (status);
}
static int32_t
usbd_func_abort_pipe(ip)
irp *ip;
{
device_t dev = IRP_NDIS_DEV(ip);
struct ndis_softc *sc = device_get_softc(dev);
struct ndisusb_ep *ne;
union usbd_urb *urb;
urb = usbd_geturb(ip);
ne = usbd_get_ndisep(ip, urb->uu_pipe.upr_handle);
if (ne == NULL) {
device_printf(IRP_NDIS_DEV(ip), "get NULL endpoint info.\n");
return (USBD_STATUS_INVALID_PIPE_HANDLE);
}
NDISUSB_LOCK(sc);
usb2_transfer_stop(ne->ne_xfer[0]);
usb2_transfer_start(ne->ne_xfer[0]);
NDISUSB_UNLOCK(sc);
return (USBD_STATUS_SUCCESS);
}
static int32_t
usbd_func_vendorclass(ip)
irp *ip;
{
device_t dev = IRP_NDIS_DEV(ip);
int32_t error;
struct ndis_softc *sc = device_get_softc(dev);
struct ndisusb_ep *ne;
struct ndisusb_xfer *nx;
struct usbd_urb_vendor_or_class_request *vcreq;
union usbd_urb *urb;
if (!(sc->ndisusb_status & NDISUSB_STATUS_SETUP_EP)) {
/*
* XXX In some cases the interface number isn't 0. However
* some driver (eg. RTL8187L NDIS driver) calls this function
* before calling URB_FUNCTION_SELECT_CONFIGURATION.
*/
error = usbd_setup_endpoint_default(ip, 0);
if (error != USB_ERR_NORMAL_COMPLETION)
return usbd_usb2urb(error);
sc->ndisusb_status |= NDISUSB_STATUS_SETUP_EP;
}
urb = usbd_geturb(ip);
vcreq = &urb->uu_vcreq;
ne = (vcreq->uvc_trans_flags & USBD_TRANSFER_DIRECTION_IN) ?
&sc->ndisusb_dread_ep : &sc->ndisusb_dwrite_ep;
IRP_NDISUSB_EP(ip) = ne;
ip->irp_cancelfunc = (cancel_func)usbd_irpcancel_wrap;
nx = malloc(sizeof(struct ndisusb_xfer), M_USBDEV, M_NOWAIT | M_ZERO);
if (nx == NULL) {
device_printf(IRP_NDIS_DEV(ip), "out of memory\n");
return (USBD_STATUS_NO_MEMORY);
}
nx->nx_ep = ne;
nx->nx_priv = ip;
KeAcquireSpinLockAtDpcLevel(&ne->ne_lock);
InsertTailList((&ne->ne_pending), (&nx->nx_next));
KeReleaseSpinLockFromDpcLevel(&ne->ne_lock);
/* we've done to setup xfer. Let's transfer it. */
ip->irp_iostat.isb_status = STATUS_PENDING;
ip->irp_iostat.isb_info = 0;
USBD_URB_STATUS(urb) = USBD_STATUS_PENDING;
IoMarkIrpPending(ip);
error = usbd_taskadd(ip, NDISUSB_TASK_VENDOR);
if (error != USBD_STATUS_SUCCESS)
return (error);
return (USBD_STATUS_PENDING);
}
static void
usbd_irpcancel(dobj, ip)
device_object *dobj;
irp *ip;
{
device_t dev = IRP_NDIS_DEV(ip);
struct ndis_softc *sc = device_get_softc(dev);
struct ndisusb_ep *ne = IRP_NDISUSB_EP(ip);
if (ne == NULL) {
ip->irp_cancel = TRUE;
IoReleaseCancelSpinLock(ip->irp_cancelirql);
return;
}
/*
* Make sure that the current USB transfer proxy is
* cancelled and then restarted.
*/
NDISUSB_LOCK(sc);
usb2_transfer_stop(ne->ne_xfer[0]);
usb2_transfer_start(ne->ne_xfer[0]);
NDISUSB_UNLOCK(sc);
ip->irp_cancel = TRUE;
IoReleaseCancelSpinLock(ip->irp_cancelirql);
}
static void
usbd_xfer_complete(struct ndis_softc *sc, struct ndisusb_ep *ne,
struct ndisusb_xfer *nx, usb2_error_t status)
{
struct ndisusb_xferdone *nd;
uint8_t irql;
nd = malloc(sizeof(struct ndisusb_xferdone), M_USBDEV,
M_NOWAIT | M_ZERO);
if (nd == NULL) {
device_printf(sc->ndis_dev, "out of memory");
return;
}
nd->nd_xfer = nx;
nd->nd_status = status;
KeAcquireSpinLock(&sc->ndisusb_xferdonelock, &irql);
InsertTailList((&sc->ndisusb_xferdonelist), (&nd->nd_donelist));
KeReleaseSpinLock(&sc->ndisusb_xferdonelock, irql);
IoQueueWorkItem(sc->ndisusb_xferdoneitem,
(io_workitem_func)usbd_xfertask_wrap, WORKQUEUE_CRITICAL, sc);
}
static struct ndisusb_xfer *
usbd_aq_getfirst(struct ndis_softc *sc, struct ndisusb_ep *ne)
{
struct ndisusb_xfer *nx;
KeAcquireSpinLockAtDpcLevel(&ne->ne_lock);
if (IsListEmpty(&ne->ne_active)) {
device_printf(sc->ndis_dev,
"%s: the active queue can't be empty.\n", __func__);
KeReleaseSpinLockFromDpcLevel(&ne->ne_lock);
return (NULL);
}
nx = CONTAINING_RECORD(ne->ne_active.nle_flink, struct ndisusb_xfer,
nx_next);
RemoveEntryList(&nx->nx_next);
KeReleaseSpinLockFromDpcLevel(&ne->ne_lock);
return (nx);
}
static void
usbd_non_isoc_callback(struct usb2_xfer *xfer)
{
irp *ip;
struct ndis_softc *sc = xfer->priv_sc;
struct ndisusb_ep *ne = xfer->priv_fifo;
struct ndisusb_xfer *nx;
struct usbd_urb_bulk_or_intr_transfer *ubi;
uint8_t irql;
uint32_t len;
union usbd_urb *urb;
usb_endpoint_descriptor_t *ep;
switch (USB_GET_STATE(xfer)) {
case USB_ST_TRANSFERRED:
nx = usbd_aq_getfirst(sc, ne);
if (nx == NULL)
return;
/* copy in data with regard to the URB */
if (ne->ne_dirin != 0)
usb2_copy_out(xfer->frbuffers, 0, nx->nx_urbbuf,
xfer->frlengths[0]);
nx->nx_urbbuf += xfer->frlengths[0];
nx->nx_urbactlen += xfer->frlengths[0];
nx->nx_urblen -= xfer->frlengths[0];
/* check for short transfer */
if (xfer->actlen < xfer->sumlen)
nx->nx_urblen = 0;
else {
/* check remainder */
if (nx->nx_urblen > 0) {
KeAcquireSpinLock(&ne->ne_lock, &irql);
InsertHeadList((&ne->ne_active), (&nx->nx_next));
KeReleaseSpinLock(&ne->ne_lock, irql);
ip = nx->nx_priv;
urb = usbd_geturb(ip);
ubi = &urb->uu_bulkintr;
ep = ubi->ubi_epdesc;
goto extra;
}
}
usbd_xfer_complete(sc, ne, nx,
((xfer->actlen < xfer->sumlen) && (nx->nx_shortxfer == 0)) ?
USB_ERR_SHORT_XFER : USB_ERR_NORMAL_COMPLETION);
/* fall through */
case USB_ST_SETUP:
next:
/* get next transfer */
KeAcquireSpinLock(&ne->ne_lock, &irql);
if (IsListEmpty(&ne->ne_pending)) {
KeReleaseSpinLock(&ne->ne_lock, irql);
return;
}
nx = CONTAINING_RECORD(ne->ne_pending.nle_flink,
struct ndisusb_xfer, nx_next);
RemoveEntryList(&nx->nx_next);
/* add a entry to the active queue's tail. */
InsertTailList((&ne->ne_active), (&nx->nx_next));
KeReleaseSpinLock(&ne->ne_lock, irql);
ip = nx->nx_priv;
urb = usbd_geturb(ip);
ubi = &urb->uu_bulkintr;
ep = ubi->ubi_epdesc;
nx->nx_urbbuf = ubi->ubi_trans_buf;
nx->nx_urbactlen = 0;
nx->nx_urblen = ubi->ubi_trans_buflen;
nx->nx_shortxfer = (ubi->ubi_trans_flags &
USBD_SHORT_TRANSFER_OK) ? 1 : 0;
extra:
len = MIN(xfer->max_data_length, nx->nx_urblen);
if (UE_GET_DIR(ep->bEndpointAddress) == UE_DIR_OUT)
usb2_copy_in(xfer->frbuffers, 0, nx->nx_urbbuf, len);
xfer->frlengths[0] = len;
xfer->nframes = 1;
usb2_start_hardware(xfer);
break;
default:
nx = usbd_aq_getfirst(sc, ne);
if (nx == NULL)
return;
if (xfer->error != USB_ERR_CANCELLED) {
xfer->flags.stall_pipe = 1;
device_printf(sc->ndis_dev, "usb xfer warning (%s)\n",
usb2_errstr(xfer->error));
}
usbd_xfer_complete(sc, ne, nx, xfer->error);
if (xfer->error != USB_ERR_CANCELLED)
goto next;
break;
}
}
static void
usbd_ctrl_callback(struct usb2_xfer *xfer)
{
irp *ip;
struct ndis_softc *sc = xfer->priv_sc;
struct ndisusb_ep *ne = xfer->priv_fifo;
struct ndisusb_xfer *nx;
uint8_t irql;
union usbd_urb *urb;
struct usbd_urb_vendor_or_class_request *vcreq;
uint8_t type = 0;
struct usb2_device_request req;
switch (USB_GET_STATE(xfer)) {
case USB_ST_TRANSFERRED:
nx = usbd_aq_getfirst(sc, ne);
if (nx == NULL)
return;
ip = nx->nx_priv;
urb = usbd_geturb(ip);
vcreq = &urb->uu_vcreq;
if (vcreq->uvc_trans_flags & USBD_TRANSFER_DIRECTION_IN) {
usb2_copy_out(xfer->frbuffers + 1, 0,
vcreq->uvc_trans_buf, xfer->frlengths[1]);
nx->nx_urbactlen += xfer->frlengths[1];
}
usbd_xfer_complete(sc, ne, nx, USB_ERR_NORMAL_COMPLETION);
/* fall through */
case USB_ST_SETUP:
next:
/* get next transfer */
KeAcquireSpinLock(&ne->ne_lock, &irql);
if (IsListEmpty(&ne->ne_pending)) {
KeReleaseSpinLock(&ne->ne_lock, irql);
return;
}
nx = CONTAINING_RECORD(ne->ne_pending.nle_flink,
struct ndisusb_xfer, nx_next);
RemoveEntryList(&nx->nx_next);
/* add a entry to the active queue's tail. */
InsertTailList((&ne->ne_active), (&nx->nx_next));
KeReleaseSpinLock(&ne->ne_lock, irql);
ip = nx->nx_priv;
urb = usbd_geturb(ip);
vcreq = &urb->uu_vcreq;
switch (urb->uu_hdr.uuh_func) {
case URB_FUNCTION_CLASS_DEVICE:
type = UT_CLASS | UT_DEVICE;
break;
case URB_FUNCTION_CLASS_INTERFACE:
type = UT_CLASS | UT_INTERFACE;
break;
case URB_FUNCTION_CLASS_OTHER:
type = UT_CLASS | UT_OTHER;
break;
case URB_FUNCTION_CLASS_ENDPOINT:
type = UT_CLASS | UT_ENDPOINT;
break;
case URB_FUNCTION_VENDOR_DEVICE:
type = UT_VENDOR | UT_DEVICE;
break;
case URB_FUNCTION_VENDOR_INTERFACE:
type = UT_VENDOR | UT_INTERFACE;
break;
case URB_FUNCTION_VENDOR_OTHER:
type = UT_VENDOR | UT_OTHER;
break;
case URB_FUNCTION_VENDOR_ENDPOINT:
type = UT_VENDOR | UT_ENDPOINT;
break;
default:
/* never reached. */
break;
}
type |= (vcreq->uvc_trans_flags & USBD_TRANSFER_DIRECTION_IN) ?
UT_READ : UT_WRITE;
type |= vcreq->uvc_reserved1;
req.bmRequestType = type;
req.bRequest = vcreq->uvc_req;
USETW(req.wIndex, vcreq->uvc_idx);
USETW(req.wValue, vcreq->uvc_value);
USETW(req.wLength, vcreq->uvc_trans_buflen);
nx->nx_urbbuf = vcreq->uvc_trans_buf;
nx->nx_urblen = vcreq->uvc_trans_buflen;
nx->nx_urbactlen = 0;
usb2_copy_in(xfer->frbuffers, 0, &req, sizeof(req));
xfer->frlengths[0] = sizeof(req);
xfer->nframes = 1;
if (vcreq->uvc_trans_flags & USBD_TRANSFER_DIRECTION_IN) {
if (vcreq->uvc_trans_buflen >= USBD_CTRL_READ_BUFFER_SP)
device_printf(sc->ndis_dev,
"warning: not enough buffer space (%d).\n",
vcreq->uvc_trans_buflen);
xfer->frlengths[1] = MIN(xfer->max_data_length,
vcreq->uvc_trans_buflen);
xfer->nframes = 2;
} else {
if (nx->nx_urblen > 0)
device_printf(sc->ndis_dev,
"warning: not enough write buffer space"
" (%d).\n", nx->nx_urblen);
/*
* XXX with my local tests there was no cases to require
* a extra buffer until now but it'd need to update in
* the future if it needs to be.
*/
if (nx->nx_urblen > 0) {
usb2_copy_in(xfer->frbuffers + 1 , 0,
nx->nx_urbbuf, nx->nx_urblen);
xfer->frlengths[1] = nx->nx_urblen;
xfer->nframes = 2;
}
}
usb2_start_hardware(xfer);
break;
default:
nx = usbd_aq_getfirst(sc, ne);
if (nx == NULL)
return;
if (xfer->error != USB_ERR_CANCELLED) {
xfer->flags.stall_pipe = 1;
device_printf(sc->ndis_dev, "usb xfer warning (%s)\n",
usb2_errstr(xfer->error));
}
usbd_xfer_complete(sc, ne, nx, xfer->error);
if (xfer->error != USB_ERR_CANCELLED)
goto next;
break;
}
}
static struct ndisusb_ep *
usbd_get_ndisep(ip, ep)
irp *ip;
usb_endpoint_descriptor_t *ep;
{
device_t dev = IRP_NDIS_DEV(ip);
struct ndis_softc *sc = device_get_softc(dev);
struct ndisusb_ep *ne;
ne = &sc->ndisusb_ep[NDISUSB_GET_ENDPT(ep->bEndpointAddress)];
IRP_NDISUSB_EP(ip) = ne;
ip->irp_cancelfunc = (cancel_func)usbd_irpcancel_wrap;
return (ne);
}
static void
usbd_xfertask(dobj, arg)
device_object *dobj;
void *arg;
{
int error;
irp *ip;
device_t dev;
list_entry *l;
struct ndis_softc *sc = arg;
struct ndisusb_xferdone *nd;
struct ndisusb_xfer *nq;
struct usbd_urb_bulk_or_intr_transfer *ubi;
struct usbd_urb_vendor_or_class_request *vcreq;
union usbd_urb *urb;
usb2_error_t status;
void *priv;
dev = sc->ndis_dev;
if (IsListEmpty(&sc->ndisusb_xferdonelist))
return;
KeAcquireSpinLockAtDpcLevel(&sc->ndisusb_xferdonelock);
l = sc->ndisusb_xferdonelist.nle_flink;
while (l != &sc->ndisusb_xferdonelist) {
nd = CONTAINING_RECORD(l, struct ndisusb_xferdone, nd_donelist);
nq = nd->nd_xfer;
priv = nq->nx_priv;
status = nd->nd_status;
error = 0;
ip = priv;
urb = usbd_geturb(ip);
ip->irp_cancelfunc = NULL;
IRP_NDISUSB_EP(ip) = NULL;
switch (status) {
case USB_ERR_NORMAL_COMPLETION:
if (urb->uu_hdr.uuh_func ==
URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER) {
ubi = &urb->uu_bulkintr;
ubi->ubi_trans_buflen = nq->nx_urbactlen;
} else {
vcreq = &urb->uu_vcreq;
vcreq->uvc_trans_buflen = nq->nx_urbactlen;
}
ip->irp_iostat.isb_info = nq->nx_urbactlen;
ip->irp_iostat.isb_status = STATUS_SUCCESS;
USBD_URB_STATUS(urb) = USBD_STATUS_SUCCESS;
break;
case USB_ERR_CANCELLED:
ip->irp_iostat.isb_info = 0;
ip->irp_iostat.isb_status = STATUS_CANCELLED;
USBD_URB_STATUS(urb) = USBD_STATUS_CANCELED;
break;
default:
ip->irp_iostat.isb_info = 0;
USBD_URB_STATUS(urb) = usbd_usb2urb(status);
ip->irp_iostat.isb_status =
usbd_urb2nt(USBD_URB_STATUS(urb));
break;
}
l = l->nle_flink;
RemoveEntryList(&nd->nd_donelist);
free(nq, M_USBDEV);
free(nd, M_USBDEV);
if (error)
continue;
KeReleaseSpinLockFromDpcLevel(&sc->ndisusb_xferdonelock);
/* NB: call after cleaning */
IoCompleteRequest(ip, IO_NO_INCREMENT);
KeAcquireSpinLockAtDpcLevel(&sc->ndisusb_xferdonelock);
}
KeReleaseSpinLockFromDpcLevel(&sc->ndisusb_xferdonelock);
}
/*
* this function is for mainly deferring a task to the another thread because
* we don't want to be in the scope of HAL lock.
*/
static int32_t
usbd_taskadd(ip, type)
irp *ip;
unsigned type;
{
device_t dev = IRP_NDIS_DEV(ip);
struct ndis_softc *sc = device_get_softc(dev);
struct ndisusb_task *nt;
nt = malloc(sizeof(struct ndisusb_task), M_USBDEV, M_NOWAIT | M_ZERO);
if (nt == NULL)
return (USBD_STATUS_NO_MEMORY);
nt->nt_type = type;
nt->nt_ctx = ip;
KeAcquireSpinLockAtDpcLevel(&sc->ndisusb_tasklock);
InsertTailList((&sc->ndisusb_tasklist), (&nt->nt_tasklist));
KeReleaseSpinLockFromDpcLevel(&sc->ndisusb_tasklock);
IoQueueWorkItem(sc->ndisusb_taskitem,
(io_workitem_func)usbd_task_wrap, WORKQUEUE_CRITICAL, sc);
return (USBD_STATUS_SUCCESS);
}
static void
usbd_task(dobj, arg)
device_object *dobj;
void *arg;
{
irp *ip;
list_entry *l;
struct ndis_softc *sc = arg;
struct ndisusb_ep *ne;
struct ndisusb_task *nt;
union usbd_urb *urb;
if (IsListEmpty(&sc->ndisusb_tasklist))
return;
KeAcquireSpinLockAtDpcLevel(&sc->ndisusb_tasklock);
l = sc->ndisusb_tasklist.nle_flink;
while (l != &sc->ndisusb_tasklist) {
nt = CONTAINING_RECORD(l, struct ndisusb_task, nt_tasklist);
ip = nt->nt_ctx;
urb = usbd_geturb(ip);
KeReleaseSpinLockFromDpcLevel(&sc->ndisusb_tasklock);
NDISUSB_LOCK(sc);
switch (nt->nt_type) {
case NDISUSB_TASK_TSTART:
ne = usbd_get_ndisep(ip, urb->uu_bulkintr.ubi_epdesc);
if (ne == NULL)
goto exit;
usb2_transfer_start(ne->ne_xfer[0]);
break;
case NDISUSB_TASK_IRPCANCEL:
ne = usbd_get_ndisep(ip,
(nt->nt_type == NDISUSB_TASK_IRPCANCEL) ?
urb->uu_bulkintr.ubi_epdesc :
urb->uu_pipe.upr_handle);
if (ne == NULL)
goto exit;
usb2_transfer_stop(ne->ne_xfer[0]);
usb2_transfer_start(ne->ne_xfer[0]);
break;
case NDISUSB_TASK_VENDOR:
ne = (urb->uu_vcreq.uvc_trans_flags &
USBD_TRANSFER_DIRECTION_IN) ?
&sc->ndisusb_dread_ep : &sc->ndisusb_dwrite_ep;
usb2_transfer_start(ne->ne_xfer[0]);
break;
default:
break;
}
exit:
NDISUSB_UNLOCK(sc);
KeAcquireSpinLockAtDpcLevel(&sc->ndisusb_tasklock);
l = l->nle_flink;
RemoveEntryList(&nt->nt_tasklist);
free(nt, M_USBDEV);
}
KeReleaseSpinLockFromDpcLevel(&sc->ndisusb_tasklock);
}
static int32_t
usbd_func_bulkintr(ip)
irp *ip;
{
int32_t error;
struct ndisusb_ep *ne;
struct ndisusb_xfer *nx;
struct usbd_urb_bulk_or_intr_transfer *ubi;
union usbd_urb *urb;
usb_endpoint_descriptor_t *ep;
urb = usbd_geturb(ip);
ubi = &urb->uu_bulkintr;
ep = ubi->ubi_epdesc;
if (ep == NULL)
return (USBD_STATUS_INVALID_PIPE_HANDLE);
ne = usbd_get_ndisep(ip, ep);
if (ne == NULL) {
device_printf(IRP_NDIS_DEV(ip), "get NULL endpoint info.\n");
return (USBD_STATUS_INVALID_PIPE_HANDLE);
}
nx = malloc(sizeof(struct ndisusb_xfer), M_USBDEV, M_NOWAIT | M_ZERO);
if (nx == NULL) {
device_printf(IRP_NDIS_DEV(ip), "out of memory\n");
return (USBD_STATUS_NO_MEMORY);
}
nx->nx_ep = ne;
nx->nx_priv = ip;
KeAcquireSpinLockAtDpcLevel(&ne->ne_lock);
InsertTailList((&ne->ne_pending), (&nx->nx_next));
KeReleaseSpinLockFromDpcLevel(&ne->ne_lock);
/* we've done to setup xfer. Let's transfer it. */
ip->irp_iostat.isb_status = STATUS_PENDING;
ip->irp_iostat.isb_info = 0;
USBD_URB_STATUS(urb) = USBD_STATUS_PENDING;
IoMarkIrpPending(ip);
error = usbd_taskadd(ip, NDISUSB_TASK_TSTART);
if (error != USBD_STATUS_SUCCESS)
return (error);
return (USBD_STATUS_PENDING);
}
static union usbd_urb *
USBD_CreateConfigurationRequest(conf, len)
usb_config_descriptor_t *conf;
uint16_t *len;
{
struct usbd_interface_list_entry list[2];
union usbd_urb *urb;
bzero(list, sizeof(struct usbd_interface_list_entry) * 2);
list[0].uil_intfdesc = USBD_ParseConfigurationDescriptorEx(conf, conf,
-1, -1, -1, -1, -1);
urb = USBD_CreateConfigurationRequestEx(conf, list);
if (urb == NULL)
return NULL;
*len = urb->uu_selconf.usc_hdr.uuh_len;
return urb;
}
static union usbd_urb *
USBD_CreateConfigurationRequestEx(conf, list)
usb_config_descriptor_t *conf;
struct usbd_interface_list_entry *list;
{
int i, j, size;
struct usbd_interface_information *intf;
struct usbd_pipe_information *pipe;
struct usbd_urb_select_configuration *selconf;
usb_interface_descriptor_t *desc;
for (i = 0, size = 0; i < conf->bNumInterface; i++) {
j = list[i].uil_intfdesc->bNumEndpoints;
size = size + sizeof(struct usbd_interface_information) +
sizeof(struct usbd_pipe_information) * (j - 1);
}
size += sizeof(struct usbd_urb_select_configuration) -
sizeof(struct usbd_interface_information);
selconf = ExAllocatePoolWithTag(NonPagedPool, size, 0);
if (selconf == NULL)
return NULL;
selconf->usc_hdr.uuh_func = URB_FUNCTION_SELECT_CONFIGURATION;
selconf->usc_hdr.uuh_len = size;
selconf->usc_handle = conf;
selconf->usc_conf = conf;
intf = &selconf->usc_intf;
for (i = 0; i < conf->bNumInterface; i++) {
if (list[i].uil_intfdesc == NULL)
break;
list[i].uil_intf = intf;
desc = list[i].uil_intfdesc;
intf->uii_len = sizeof(struct usbd_interface_information) +
(desc->bNumEndpoints - 1) *
sizeof(struct usbd_pipe_information);
intf->uii_intfnum = desc->bInterfaceNumber;
intf->uii_altset = desc->bAlternateSetting;
intf->uii_intfclass = desc->bInterfaceClass;
intf->uii_intfsubclass = desc->bInterfaceSubClass;
intf->uii_intfproto = desc->bInterfaceProtocol;
intf->uii_handle = desc;
intf->uii_numeps = desc->bNumEndpoints;
pipe = &intf->uii_pipes[0];
for (j = 0; j < intf->uii_numeps; j++)
pipe[j].upi_maxtxsize =
USBD_DEFAULT_MAXIMUM_TRANSFER_SIZE;
intf = (struct usbd_interface_information *)((char *)intf +
intf->uii_len);
}
return ((union usbd_urb *)selconf);
- Correct one aspect of the driver_object/device_object/IRP framework: when we create a PDO, the driver_object associated with it is that of the parent driver, not the driver we're trying to attach. For example, if we attach a PCI device, the PDO we pass to the NdisAddDevice() function should contain a pointer to fake_pci_driver, not to the NDIS driver itself. For PCI or PCMCIA devices this doesn't matter because the child never needs to talk to the parent bus driver, but for USB, the child needs to be able to send IRPs to the parent USB bus driver, and for that to work the parent USB bus driver has to be hung off the PDO. This involves modifying windrv_lookup() so that we can search for bus drivers by name, if necessary. Our fake bus drivers attach themselves as "PCI Bus," "PCCARD Bus" and "USB Bus," so we can search for them using those names. The individual attachment stubs now create and attach PDOs to the parent bus drivers instead of hanging them off the NDIS driver's object, and in if_ndis.c, we now search for the correct driver object depending on the bus type, and use that to find the correct PDO. With this fix, I can get my sample USB ethernet driver to deliver an IRP to my fake parent USB bus driver's dispatch routines. - Add stub modules for USB support: subr_usbd.c, usbd_var.h and if_ndis_usb.c. The subr_usbd.c module is hooked up the build but currently doesn't do very much. It provides the stub USB parent driver object and a dispatch routine for IRM_MJ_INTERNAL_DEVICE_CONTROL. The only exported function at the moment is USBD_GetUSBDIVersion(). The if_ndis_usb.c stub compiles, but is not hooked up to the build yet. I'm putting these here so I can keep them under source code control as I flesh them out.
2005-02-24 21:49:14 +00:00
}
Create new i386 windows/bsd thunking layer, similar to the amd64 thunking layer, but with a twist. The twist has to do with the fact that Microsoft supports structured exception handling in kernel mode. On the i386 arch, exception handling is implemented by hanging an exception registration list off the Thread Environment Block (TEB), and the TEB is accessed via the %fs register. The problem is, we use %fs as a pointer to the pcpu stucture, which means any driver that tries to write through %fs:0 will overwrite the curthread pointer and make a serious mess of things. To get around this, Project Evil now creates a special entry in the GDT on each processor. When we call into Windows code, a context switch routine will fix up %fs so it points to our new descriptor, which in turn points to a fake TEB. When the Windows code returns, or calls out to an external routine, we swap %fs back again. Currently, Project Evil makes use of GDT slot 7, which is all 0s by default. I fully expect someone to jump up and say I can't do that, but I couldn't find any code that makes use of this entry anywhere. Sadly, this was the only method I could come up with that worked on both UP and SMP. (Modifying the LDT works on UP, but becomes incredibly complicated on SMP.) If necessary, the context switching stuff can be yanked out while preserving the convention calling wrappers. (Fortunately, it looks like Microsoft uses some special epilog/prolog code on amd64 to implement exception handling, so the same nastiness won't be necessary on that arch.) The advantages are: - Any driver that uses %fs as though it were a TEB pointer won't clobber pcpu. - All the __stdcall/__fastcall/__regparm stuff that's specific to gcc goes away. Also, while I'm here, switch NdisGetSystemUpTime() back to using nanouptime() again. It turns out nanouptime() is way more accurate than just using ticks(). On slower machines, the Atheros drivers I tested seem to take a long time to associate due to the loss in accuracy.
2005-04-11 02:02:35 +00:00
static void
- Correct one aspect of the driver_object/device_object/IRP framework: when we create a PDO, the driver_object associated with it is that of the parent driver, not the driver we're trying to attach. For example, if we attach a PCI device, the PDO we pass to the NdisAddDevice() function should contain a pointer to fake_pci_driver, not to the NDIS driver itself. For PCI or PCMCIA devices this doesn't matter because the child never needs to talk to the parent bus driver, but for USB, the child needs to be able to send IRPs to the parent USB bus driver, and for that to work the parent USB bus driver has to be hung off the PDO. This involves modifying windrv_lookup() so that we can search for bus drivers by name, if necessary. Our fake bus drivers attach themselves as "PCI Bus," "PCCARD Bus" and "USB Bus," so we can search for them using those names. The individual attachment stubs now create and attach PDOs to the parent bus drivers instead of hanging them off the NDIS driver's object, and in if_ndis.c, we now search for the correct driver object depending on the bus type, and use that to find the correct PDO. With this fix, I can get my sample USB ethernet driver to deliver an IRP to my fake parent USB bus driver's dispatch routines. - Add stub modules for USB support: subr_usbd.c, usbd_var.h and if_ndis_usb.c. The subr_usbd.c module is hooked up the build but currently doesn't do very much. It provides the stub USB parent driver object and a dispatch routine for IRM_MJ_INTERNAL_DEVICE_CONTROL. The only exported function at the moment is USBD_GetUSBDIVersion(). The if_ndis_usb.c stub compiles, but is not hooked up to the build yet. I'm putting these here so I can keep them under source code control as I flesh them out.
2005-02-24 21:49:14 +00:00
USBD_GetUSBDIVersion(ui)
usbd_version_info *ui;
{
- Correct one aspect of the driver_object/device_object/IRP framework: when we create a PDO, the driver_object associated with it is that of the parent driver, not the driver we're trying to attach. For example, if we attach a PCI device, the PDO we pass to the NdisAddDevice() function should contain a pointer to fake_pci_driver, not to the NDIS driver itself. For PCI or PCMCIA devices this doesn't matter because the child never needs to talk to the parent bus driver, but for USB, the child needs to be able to send IRPs to the parent USB bus driver, and for that to work the parent USB bus driver has to be hung off the PDO. This involves modifying windrv_lookup() so that we can search for bus drivers by name, if necessary. Our fake bus drivers attach themselves as "PCI Bus," "PCCARD Bus" and "USB Bus," so we can search for them using those names. The individual attachment stubs now create and attach PDOs to the parent bus drivers instead of hanging them off the NDIS driver's object, and in if_ndis.c, we now search for the correct driver object depending on the bus type, and use that to find the correct PDO. With this fix, I can get my sample USB ethernet driver to deliver an IRP to my fake parent USB bus driver's dispatch routines. - Add stub modules for USB support: subr_usbd.c, usbd_var.h and if_ndis_usb.c. The subr_usbd.c module is hooked up the build but currently doesn't do very much. It provides the stub USB parent driver object and a dispatch routine for IRM_MJ_INTERNAL_DEVICE_CONTROL. The only exported function at the moment is USBD_GetUSBDIVersion(). The if_ndis_usb.c stub compiles, but is not hooked up to the build yet. I'm putting these here so I can keep them under source code control as I flesh them out.
2005-02-24 21:49:14 +00:00
/* Pretend to be Windows XP. */
ui->uvi_usbdi_vers = USBDI_VERSION;
ui->uvi_supported_vers = USB_VER_2_0;
return;
}
static usb_interface_descriptor_t *
USBD_ParseConfigurationDescriptor(usb_config_descriptor_t *conf,
uint8_t intfnum, uint8_t altset)
{
return USBD_ParseConfigurationDescriptorEx(conf, conf, intfnum, altset,
-1, -1, -1);
}
static usb_interface_descriptor_t *
USBD_ParseConfigurationDescriptorEx(conf, start, intfnum,
altset, intfclass, intfsubclass, intfproto)
usb_config_descriptor_t *conf;
void *start;
int32_t intfnum;
int32_t altset;
int32_t intfclass;
int32_t intfsubclass;
int32_t intfproto;
{
struct usb2_descriptor *next = NULL;
usb_interface_descriptor_t *desc;
while ((next = usb2_desc_foreach(conf, next)) != NULL) {
desc = (usb_interface_descriptor_t *)next;
if (desc->bDescriptorType != UDESC_INTERFACE)
continue;
if (!(intfnum == -1 || desc->bInterfaceNumber == intfnum))
continue;
if (!(altset == -1 || desc->bAlternateSetting == altset))
continue;
if (!(intfclass == -1 || desc->bInterfaceClass == intfclass))
continue;
if (!(intfsubclass == -1 ||
desc->bInterfaceSubClass == intfsubclass))
continue;
if (!(intfproto == -1 || desc->bInterfaceProtocol == intfproto))
continue;
return (desc);
}
return (NULL);
}
Create new i386 windows/bsd thunking layer, similar to the amd64 thunking layer, but with a twist. The twist has to do with the fact that Microsoft supports structured exception handling in kernel mode. On the i386 arch, exception handling is implemented by hanging an exception registration list off the Thread Environment Block (TEB), and the TEB is accessed via the %fs register. The problem is, we use %fs as a pointer to the pcpu stucture, which means any driver that tries to write through %fs:0 will overwrite the curthread pointer and make a serious mess of things. To get around this, Project Evil now creates a special entry in the GDT on each processor. When we call into Windows code, a context switch routine will fix up %fs so it points to our new descriptor, which in turn points to a fake TEB. When the Windows code returns, or calls out to an external routine, we swap %fs back again. Currently, Project Evil makes use of GDT slot 7, which is all 0s by default. I fully expect someone to jump up and say I can't do that, but I couldn't find any code that makes use of this entry anywhere. Sadly, this was the only method I could come up with that worked on both UP and SMP. (Modifying the LDT works on UP, but becomes incredibly complicated on SMP.) If necessary, the context switching stuff can be yanked out while preserving the convention calling wrappers. (Fortunately, it looks like Microsoft uses some special epilog/prolog code on amd64 to implement exception handling, so the same nastiness won't be necessary on that arch.) The advantages are: - Any driver that uses %fs as though it were a TEB pointer won't clobber pcpu. - All the __stdcall/__fastcall/__regparm stuff that's specific to gcc goes away. Also, while I'm here, switch NdisGetSystemUpTime() back to using nanouptime() again. It turns out nanouptime() is way more accurate than just using ticks(). On slower machines, the Atheros drivers I tested seem to take a long time to associate due to the loss in accuracy.
2005-04-11 02:02:35 +00:00
static void
- Correct one aspect of the driver_object/device_object/IRP framework: when we create a PDO, the driver_object associated with it is that of the parent driver, not the driver we're trying to attach. For example, if we attach a PCI device, the PDO we pass to the NdisAddDevice() function should contain a pointer to fake_pci_driver, not to the NDIS driver itself. For PCI or PCMCIA devices this doesn't matter because the child never needs to talk to the parent bus driver, but for USB, the child needs to be able to send IRPs to the parent USB bus driver, and for that to work the parent USB bus driver has to be hung off the PDO. This involves modifying windrv_lookup() so that we can search for bus drivers by name, if necessary. Our fake bus drivers attach themselves as "PCI Bus," "PCCARD Bus" and "USB Bus," so we can search for them using those names. The individual attachment stubs now create and attach PDOs to the parent bus drivers instead of hanging them off the NDIS driver's object, and in if_ndis.c, we now search for the correct driver object depending on the bus type, and use that to find the correct PDO. With this fix, I can get my sample USB ethernet driver to deliver an IRP to my fake parent USB bus driver's dispatch routines. - Add stub modules for USB support: subr_usbd.c, usbd_var.h and if_ndis_usb.c. The subr_usbd.c module is hooked up the build but currently doesn't do very much. It provides the stub USB parent driver object and a dispatch routine for IRM_MJ_INTERNAL_DEVICE_CONTROL. The only exported function at the moment is USBD_GetUSBDIVersion(). The if_ndis_usb.c stub compiles, but is not hooked up to the build yet. I'm putting these here so I can keep them under source code control as I flesh them out.
2005-02-24 21:49:14 +00:00
dummy(void)
{
printf("USBD dummy called\n");
return;
}
image_patch_table usbd_functbl[] = {
IMPORT_SFUNC(USBD_CreateConfigurationRequest, 2),
IMPORT_SFUNC(USBD_CreateConfigurationRequestEx, 2),
IMPORT_SFUNC_MAP(_USBD_CreateConfigurationRequestEx@8,
USBD_CreateConfigurationRequestEx, 2),
IMPORT_SFUNC(USBD_GetUSBDIVersion, 1),
IMPORT_SFUNC(USBD_ParseConfigurationDescriptor, 3),
IMPORT_SFUNC(USBD_ParseConfigurationDescriptorEx, 7),
IMPORT_SFUNC_MAP(_USBD_ParseConfigurationDescriptorEx@28,
USBD_ParseConfigurationDescriptorEx, 7),
- Correct one aspect of the driver_object/device_object/IRP framework: when we create a PDO, the driver_object associated with it is that of the parent driver, not the driver we're trying to attach. For example, if we attach a PCI device, the PDO we pass to the NdisAddDevice() function should contain a pointer to fake_pci_driver, not to the NDIS driver itself. For PCI or PCMCIA devices this doesn't matter because the child never needs to talk to the parent bus driver, but for USB, the child needs to be able to send IRPs to the parent USB bus driver, and for that to work the parent USB bus driver has to be hung off the PDO. This involves modifying windrv_lookup() so that we can search for bus drivers by name, if necessary. Our fake bus drivers attach themselves as "PCI Bus," "PCCARD Bus" and "USB Bus," so we can search for them using those names. The individual attachment stubs now create and attach PDOs to the parent bus drivers instead of hanging them off the NDIS driver's object, and in if_ndis.c, we now search for the correct driver object depending on the bus type, and use that to find the correct PDO. With this fix, I can get my sample USB ethernet driver to deliver an IRP to my fake parent USB bus driver's dispatch routines. - Add stub modules for USB support: subr_usbd.c, usbd_var.h and if_ndis_usb.c. The subr_usbd.c module is hooked up the build but currently doesn't do very much. It provides the stub USB parent driver object and a dispatch routine for IRM_MJ_INTERNAL_DEVICE_CONTROL. The only exported function at the moment is USBD_GetUSBDIVersion(). The if_ndis_usb.c stub compiles, but is not hooked up to the build yet. I'm putting these here so I can keep them under source code control as I flesh them out.
2005-02-24 21:49:14 +00:00
/*
* This last entry is a catch-all for any function we haven't
* implemented yet. The PE import list patching routine will
* use it for any function that doesn't have an explicit match
* in this table.
*/
This commit makes a bunch of changes, some big, some not so big. - Remove the old task threads from kern_ndis.c and reimplement them in subr_ntoskrnl.c, in order to more properly emulate the Windows DPC API. Each CPU gets its own DPC queue/thread, and each queue can have low, medium and high importance DPCs. New APIs implemented: KeSetTargetProcessorDpc(), KeSetImportanceDpc() and KeFlushQueuedDpcs(). (This is the biggest change.) - Fix a bug in NdisMInitializeTimer(): the k_dpc pointer in the nmt_timer embedded in the ndis_miniport_timer struct must be set to point to the DPC, also embedded in the struct. Failing to do this breaks dequeueing of DPCs submitted via timers, and in turn breaks cancelling timers. - Fix a bug in KeCancelTimer(): if the timer is interted in the timer queue (i.e. the timeout callback is still pending), we have to both untimeout() the timer _and_ call KeRemoveQueueDpc() to nuke the DPC that might be pending. Failing to do this breaks cancellation of periodic timers, which always appear to be inserted in the timer queue. - Make use of the nmt_nexttimer field in ndis_miniport_timer: keep a queue of pending timers and cancel them all in ndis_halt_nic(), prior to calling MiniportHalt(). Also call KeFlushQueuedDpcs() to make sure any DPCs queued by the timers have expired. - Modify NdisMAllocateSharedMemory() and NdisMFreeSharedMemory() to keep track of both the virtual and physical addresses of the shared memory buffers that get handed out. The AirGo MIMO driver appears to have a bug in it: for one of the segments is allocates, it returns the wrong virtual address. This would confuse NdisMFreeSharedMemory() and cause a crash. Why it doesn't crash Windows too I have no idea (from reading the documentation for NdisMFreeSharedMemory(), it appears to be a violation of the API). - Implement strstr(), strchr() and MmIsAddressValid(). - Implement IoAllocateWorkItem(), IoFreeWorkItem(), IoQueueWorkItem() and ExQueueWorkItem(). (This is the second biggest change.) - Make NdisScheduleWorkItem() call ExQueueWorkItem(). (Note that the ExQueueWorkItem() API is deprecated by Microsoft, but NDIS still uses it, since NdisScheduleWorkItem() is incompatible with the IoXXXWorkItem() API.) - Change if_ndis.c to use the NdisScheduleWorkItem() interface for scheduling tasks. With all these changes and fixes, the AirGo MIMO driver for the Belkin F5D8010 Pre-N card now works. Special thanks to Paul Robinson (paul dawt robinson at pwermedia dawt net) for the loan of a card for testing.
2005-05-05 03:56:09 +00:00
{ NULL, (FUNC)dummy, NULL, 0, WINDRV_WRAP_STDCALL },
- Correct one aspect of the driver_object/device_object/IRP framework: when we create a PDO, the driver_object associated with it is that of the parent driver, not the driver we're trying to attach. For example, if we attach a PCI device, the PDO we pass to the NdisAddDevice() function should contain a pointer to fake_pci_driver, not to the NDIS driver itself. For PCI or PCMCIA devices this doesn't matter because the child never needs to talk to the parent bus driver, but for USB, the child needs to be able to send IRPs to the parent USB bus driver, and for that to work the parent USB bus driver has to be hung off the PDO. This involves modifying windrv_lookup() so that we can search for bus drivers by name, if necessary. Our fake bus drivers attach themselves as "PCI Bus," "PCCARD Bus" and "USB Bus," so we can search for them using those names. The individual attachment stubs now create and attach PDOs to the parent bus drivers instead of hanging them off the NDIS driver's object, and in if_ndis.c, we now search for the correct driver object depending on the bus type, and use that to find the correct PDO. With this fix, I can get my sample USB ethernet driver to deliver an IRP to my fake parent USB bus driver's dispatch routines. - Add stub modules for USB support: subr_usbd.c, usbd_var.h and if_ndis_usb.c. The subr_usbd.c module is hooked up the build but currently doesn't do very much. It provides the stub USB parent driver object and a dispatch routine for IRM_MJ_INTERNAL_DEVICE_CONTROL. The only exported function at the moment is USBD_GetUSBDIVersion(). The if_ndis_usb.c stub compiles, but is not hooked up to the build yet. I'm putting these here so I can keep them under source code control as I flesh them out.
2005-02-24 21:49:14 +00:00
/* End of list. */
{ NULL, NULL, NULL }
};
MODULE_DEPEND(ndis, usb, 1, 1, 1);