- 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>
|
2009-06-23 02:19:59 +00:00
|
|
|
#include <sys/sx.h>
|
|
|
|
#include <sys/condvar.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/module.h>
|
|
|
|
#include <sys/conf.h>
|
|
|
|
#include <sys/mbuf.h>
|
2008-12-27 08:03:32 +00:00
|
|
|
#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>
|
|
|
|
|
2008-12-27 08:03:32 +00:00
|
|
|
#include <net/if.h>
|
|
|
|
#include <net/if_media.h>
|
|
|
|
#include <net80211/ieee80211_var.h>
|
|
|
|
#include <net80211/ieee80211_ioctl.h>
|
|
|
|
|
2009-03-07 07:26:22 +00:00
|
|
|
#include <dev/usb/usb.h>
|
2009-06-23 02:19:59 +00:00
|
|
|
#include <dev/usb/usbdi.h>
|
|
|
|
#include <dev/usb/usbdi_util.h>
|
2009-03-07 07:26:22 +00:00
|
|
|
#include <dev/usb/usb_busdma.h>
|
|
|
|
#include <dev/usb/usb_device.h>
|
|
|
|
#include <dev/usb/usb_request.h>
|
2008-12-27 08:03:32 +00:00
|
|
|
|
- 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>
|
2008-12-27 08:03:32 +00:00
|
|
|
#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;
|
2009-05-29 18:46:57 +00:00
|
|
|
static usb_callback_t usbd_non_isoc_callback;
|
|
|
|
static usb_callback_t usbd_ctrl_callback;
|
2009-03-18 02:38:35 +00:00
|
|
|
|
|
|
|
#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
|
2009-06-26 01:42:41 +00:00
|
|
|
#define USBD_CTRL_WRITE_BUFFER_SP 256
|
2009-03-18 02:38:35 +00:00
|
|
|
#define USBD_CTRL_READ_BUFFER_SIZE \
|
2009-05-28 17:36:36 +00:00
|
|
|
(sizeof(struct usb_device_request) + USBD_CTRL_READ_BUFFER_SP)
|
2009-03-18 02:38:35 +00:00
|
|
|
#define USBD_CTRL_WRITE_BUFFER_SIZE \
|
2009-06-26 01:42:41 +00:00
|
|
|
(sizeof(struct usb_device_request) + USBD_CTRL_WRITE_BUFFER_SP)
|
2009-05-28 17:36:36 +00:00
|
|
|
static struct usb_config usbd_default_epconfig[USBD_CTRL_MAX_PIPE] = {
|
2009-03-18 02:38:35 +00:00
|
|
|
[USBD_CTRL_READ_PIPE] = {
|
|
|
|
.type = UE_CONTROL,
|
|
|
|
.endpoint = 0x00, /* control pipe */
|
|
|
|
.direction = UE_DIR_ANY,
|
|
|
|
.if_index = 0,
|
2009-04-05 18:20:38 +00:00
|
|
|
.bufsize = USBD_CTRL_READ_BUFFER_SIZE,
|
|
|
|
.flags = { .short_xfer_ok = 1, },
|
|
|
|
.callback = &usbd_ctrl_callback,
|
|
|
|
.timeout = 5000, /* 5 seconds */
|
2009-03-18 02:38:35 +00:00
|
|
|
},
|
|
|
|
[USBD_CTRL_WRITE_PIPE] = {
|
|
|
|
.type = UE_CONTROL,
|
|
|
|
.endpoint = 0x00, /* control pipe */
|
|
|
|
.direction = UE_DIR_ANY,
|
|
|
|
.if_index = 0,
|
2009-04-05 18:20:38 +00:00
|
|
|
.bufsize = USBD_CTRL_WRITE_BUFFER_SIZE,
|
|
|
|
.flags = { .proxy_buffer = 1, },
|
|
|
|
.callback = &usbd_ctrl_callback,
|
|
|
|
.timeout = 5000, /* 5 seconds */
|
2009-03-18 02:38:35 +00:00
|
|
|
}
|
|
|
|
};
|
- 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
|
|
|
|
2008-12-27 08:03:32 +00:00
|
|
|
static int32_t usbd_func_bulkintr(irp *);
|
|
|
|
static int32_t usbd_func_vendorclass(irp *);
|
|
|
|
static int32_t usbd_func_selconf(irp *);
|
2009-03-07 07:26:22 +00:00
|
|
|
static int32_t usbd_func_abort_pipe(irp *);
|
2009-05-29 18:46:57 +00:00
|
|
|
static usb_error_t usbd_setup_endpoint(irp *, uint8_t,
|
2009-05-28 17:36:36 +00:00
|
|
|
struct usb_endpoint_descriptor *);
|
2009-05-29 18:46:57 +00:00
|
|
|
static usb_error_t usbd_setup_endpoint_default(irp *, uint8_t);
|
|
|
|
static usb_error_t usbd_setup_endpoint_one(irp *, uint8_t,
|
2009-05-28 17:36:36 +00:00
|
|
|
struct ndisusb_ep *, struct usb_config *);
|
2008-12-27 08:03:32 +00:00
|
|
|
static int32_t usbd_func_getdesc(irp *);
|
|
|
|
static union usbd_urb *usbd_geturb(irp *);
|
2009-03-07 07:26:22 +00:00
|
|
|
static struct ndisusb_ep*usbd_get_ndisep(irp *, usb_endpoint_descriptor_t *);
|
2008-12-27 08:03:32 +00:00
|
|
|
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);
|
2009-03-12 02:51:55 +00:00
|
|
|
static void usbd_task(device_object *, void *);
|
|
|
|
static int32_t usbd_taskadd(irp *, unsigned);
|
2008-12-27 08:03:32 +00:00
|
|
|
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
|
|
|
|
2008-12-27 08:03:32 +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;
|
2009-03-12 02:51:55 +00:00
|
|
|
static funcptr usbd_task_wrap;
|
2008-12-27 08:03:32 +00:00
|
|
|
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;
|
2008-12-27 08:03:32 +00:00
|
|
|
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++;
|
|
|
|
}
|
|
|
|
|
2008-12-27 08:03:32 +00:00
|
|
|
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);
|
2009-03-12 02:51:55 +00:00
|
|
|
windrv_wrap((funcptr)usbd_task,
|
|
|
|
(funcptr *)&usbd_task_wrap, 2, WINDRV_WRAP_STDCALL);
|
2008-12-27 08:03:32 +00:00
|
|
|
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. */
|
2008-12-27 08:03:32 +00:00
|
|
|
for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++)
|
2009-03-07 07:26:22 +00:00
|
|
|
usbd_driver.dro_dispatch[i] =
|
2008-12-27 08:03:32 +00:00
|
|
|
(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] =
|
2008-12-27 08:03:32 +00:00
|
|
|
(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
|
|
|
|
2009-11-02 11:07:42 +00:00
|
|
|
return (0);
|
- 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_libfini(void)
|
|
|
|
{
|
|
|
|
image_patch_table *patch;
|
|
|
|
|
|
|
|
patch = usbd_functbl;
|
|
|
|
while (patch->ipt_func != NULL) {
|
|
|
|
windrv_unwrap(patch->ipt_wrap);
|
|
|
|
patch++;
|
|
|
|
}
|
|
|
|
|
2008-12-27 08:03:32 +00:00
|
|
|
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);
|
2009-03-12 02:51:55 +00:00
|
|
|
windrv_unwrap(usbd_task_wrap);
|
2008-12-27 08:03:32 +00:00
|
|
|
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);
|
|
|
|
|
2009-11-02 11:07:42 +00:00
|
|
|
return (0);
|
- 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
|
|
|
}
|
|
|
|
|
2008-12-27 08:03:32 +00:00
|
|
|
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;
|
|
|
|
{
|
2008-12-27 08:03:32 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2009-05-29 18:46:57 +00:00
|
|
|
/* Convert FreeBSD's usb_error_t to USBD_STATUS */
|
2008-12-27 08:03:32 +00:00
|
|
|
static int32_t
|
|
|
|
usbd_usb2urb(int status)
|
|
|
|
{
|
|
|
|
|
|
|
|
switch (status) {
|
2009-03-07 07:26:22 +00:00
|
|
|
case USB_ERR_NORMAL_COMPLETION:
|
2008-12-27 08:03:32 +00:00
|
|
|
return (USBD_STATUS_SUCCESS);
|
2009-03-07 07:26:22 +00:00
|
|
|
case USB_ERR_PENDING_REQUESTS:
|
2008-12-27 08:03:32 +00:00
|
|
|
return (USBD_STATUS_PENDING);
|
2009-03-07 07:26:22 +00:00
|
|
|
case USB_ERR_TIMEOUT:
|
2008-12-27 08:03:32 +00:00
|
|
|
return (USBD_STATUS_TIMEOUT);
|
2009-03-07 07:26:22 +00:00
|
|
|
case USB_ERR_SHORT_XFER:
|
2008-12-27 08:03:32 +00:00
|
|
|
return (USBD_STATUS_ERROR_SHORT_TRANSFER);
|
2009-03-07 07:26:22 +00:00
|
|
|
case USB_ERR_IOERROR:
|
2008-12-27 08:03:32 +00:00
|
|
|
return (USBD_STATUS_XACT_ERROR);
|
2009-03-07 07:26:22 +00:00
|
|
|
case USB_ERR_NOMEM:
|
2008-12-27 08:03:32 +00:00
|
|
|
return (USBD_STATUS_NO_MEMORY);
|
2009-03-07 07:26:22 +00:00
|
|
|
case USB_ERR_INVAL:
|
2008-12-27 08:03:32 +00:00
|
|
|
return (USBD_STATUS_REQUEST_FAILED);
|
2009-03-07 07:26:22 +00:00
|
|
|
case USB_ERR_NOT_STARTED:
|
|
|
|
case USB_ERR_TOO_DEEP:
|
|
|
|
case USB_ERR_NO_POWER:
|
2008-12-27 08:03:32 +00:00
|
|
|
return (USBD_STATUS_DEVICE_GONE);
|
2009-03-07 07:26:22 +00:00
|
|
|
case USB_ERR_CANCELLED:
|
2008-12-27 08:03:32 +00:00
|
|
|
return (USBD_STATUS_CANCELED);
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2009-03-07 07:26:22 +00:00
|
|
|
|
2008-12-27 08:03:32 +00:00
|
|
|
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;
|
2009-03-07 07:26:22 +00:00
|
|
|
case URB_FUNCTION_ABORT_PIPE:
|
|
|
|
status = usbd_func_abort_pipe(ip);
|
|
|
|
USBD_URB_STATUS(urb) = status;
|
|
|
|
break;
|
2008-12-27 08:03:32 +00:00
|
|
|
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;
|
|
|
|
{
|
2009-03-07 07:26:22 +00:00
|
|
|
#define NDISUSB_GETDESC_MAXRETRIES 3
|
2008-12-27 08:03:32 +00:00
|
|
|
device_t dev = IRP_NDIS_DEV(ip);
|
2009-03-07 07:26:22 +00:00
|
|
|
struct ndis_softc *sc = device_get_softc(dev);
|
2008-12-27 08:03:32 +00:00
|
|
|
struct usbd_urb_control_descriptor_request *ctldesc;
|
2009-03-07 07:26:22 +00:00
|
|
|
uint16_t actlen;
|
2008-12-27 08:03:32 +00:00
|
|
|
uint32_t len;
|
|
|
|
union usbd_urb *urb;
|
2009-03-07 07:26:22 +00:00
|
|
|
usb_config_descriptor_t *cdp;
|
2009-05-29 18:46:57 +00:00
|
|
|
usb_error_t status;
|
2008-12-27 08:03:32 +00:00
|
|
|
|
|
|
|
urb = usbd_geturb(ip);
|
|
|
|
ctldesc = &urb->uu_ctldesc;
|
|
|
|
if (ctldesc->ucd_desctype == UDESC_CONFIG) {
|
2009-03-07 07:26:22 +00:00
|
|
|
/*
|
|
|
|
* The NDIS driver is not allowed to change the
|
|
|
|
* config! There is only one choice!
|
|
|
|
*/
|
2009-06-15 01:02:43 +00:00
|
|
|
cdp = usbd_get_config_descriptor(sc->ndisusb_dev);
|
2009-03-07 07:26:22 +00:00
|
|
|
if (cdp == NULL) {
|
|
|
|
status = USB_ERR_INVAL;
|
|
|
|
goto exit;
|
2008-12-27 08:03:32 +00:00
|
|
|
}
|
|
|
|
if (cdp->bDescriptorType != UDESC_CONFIG) {
|
|
|
|
device_printf(dev, "bad desc %d\n",
|
|
|
|
cdp->bDescriptorType);
|
2009-03-07 07:26:22 +00:00
|
|
|
status = USB_ERR_INVAL;
|
|
|
|
goto exit;
|
2008-12-27 08:03:32 +00:00
|
|
|
}
|
2009-03-07 07:26:22 +00:00
|
|
|
/* 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 {
|
2009-03-12 02:51:55 +00:00
|
|
|
NDISUSB_LOCK(sc);
|
2009-06-15 01:02:43 +00:00
|
|
|
status = usbd_req_get_desc(sc->ndisusb_dev, &sc->ndisusb_mtx,
|
2009-03-07 07:26:22 +00:00
|
|
|
&actlen, ctldesc->ucd_trans_buf, 2,
|
|
|
|
ctldesc->ucd_trans_buflen, ctldesc->ucd_langid,
|
|
|
|
ctldesc->ucd_desctype, ctldesc->ucd_idx,
|
|
|
|
NDISUSB_GETDESC_MAXRETRIES);
|
2009-03-12 02:51:55 +00:00
|
|
|
NDISUSB_UNLOCK(sc);
|
2009-03-07 07:26:22 +00:00
|
|
|
}
|
|
|
|
exit:
|
|
|
|
if (status != USB_ERR_NORMAL_COMPLETION) {
|
2008-12-27 08:03:32 +00:00
|
|
|
ctldesc->ucd_trans_buflen = 0;
|
|
|
|
return usbd_usb2urb(status);
|
|
|
|
}
|
|
|
|
|
|
|
|
ctldesc->ucd_trans_buflen = actlen;
|
|
|
|
ip->irp_iostat.isb_info = actlen;
|
|
|
|
|
|
|
|
return (USBD_STATUS_SUCCESS);
|
2009-03-07 07:26:22 +00:00
|
|
|
#undef NDISUSB_GETDESC_MAXRETRIES
|
2008-12-27 08:03:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int32_t
|
|
|
|
usbd_func_selconf(ip)
|
|
|
|
irp *ip;
|
|
|
|
{
|
|
|
|
device_t dev = IRP_NDIS_DEV(ip);
|
|
|
|
int i, j;
|
2009-03-07 07:26:22 +00:00
|
|
|
struct ndis_softc *sc = device_get_softc(dev);
|
2009-05-28 17:36:36 +00:00
|
|
|
struct usb_device *udev = sc->ndisusb_dev;
|
2009-06-07 19:41:11 +00:00
|
|
|
struct usb_endpoint *ep = NULL;
|
2008-12-27 08:03:32 +00:00
|
|
|
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;
|
2009-05-29 18:46:57 +00:00
|
|
|
usb_error_t ret;
|
2008-12-27 08:03:32 +00:00
|
|
|
|
|
|
|
urb = usbd_geturb(ip);
|
|
|
|
|
|
|
|
selconf = &urb->uu_selconf;
|
|
|
|
conf = selconf->usc_conf;
|
|
|
|
if (conf == NULL) {
|
|
|
|
device_printf(dev, "select configuration is NULL\n");
|
2009-03-07 07:26:22 +00:00
|
|
|
return usbd_usb2urb(USB_ERR_NORMAL_COMPLETION);
|
2008-12-27 08:03:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
intf = &selconf->usc_intf;
|
|
|
|
for (i = 0; i < conf->bNumInterface && intf->uii_len > 0; i++) {
|
2009-06-15 01:02:43 +00:00
|
|
|
ret = usbd_set_alt_interface_index(udev,
|
2009-03-07 07:26:22 +00:00
|
|
|
intf->uii_intfnum, intf->uii_altset);
|
|
|
|
if (ret != USB_ERR_NORMAL_COMPLETION && ret != USB_ERR_IN_USE) {
|
2008-12-27 08:03:32 +00:00
|
|
|
device_printf(dev,
|
|
|
|
"setting alternate interface failed: %s\n",
|
2009-06-15 01:02:43 +00:00
|
|
|
usbd_errstr(ret));
|
2008-12-27 08:03:32 +00:00
|
|
|
return usbd_usb2urb(ret);
|
|
|
|
}
|
2009-03-07 07:26:22 +00:00
|
|
|
|
2009-06-15 01:02:43 +00:00
|
|
|
for (j = 0; (ep = usb_endpoint_foreach(udev, ep)); j++) {
|
2008-12-27 08:03:32 +00:00
|
|
|
if (j >= intf->uii_numeps) {
|
|
|
|
device_printf(dev,
|
|
|
|
"endpoint %d and above are ignored",
|
|
|
|
intf->uii_numeps);
|
|
|
|
break;
|
|
|
|
}
|
2009-06-07 19:41:11 +00:00
|
|
|
edesc = ep->edesc;
|
2008-12-27 08:03:32 +00:00
|
|
|
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);
|
2009-03-07 07:26:22 +00:00
|
|
|
|
|
|
|
ret = usbd_setup_endpoint(ip, intf->uii_intfnum, edesc);
|
|
|
|
if (ret != USB_ERR_NORMAL_COMPLETION)
|
|
|
|
return usbd_usb2urb(ret);
|
|
|
|
|
2008-12-27 08:03:32 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2009-11-02 11:07:42 +00:00
|
|
|
return (USBD_STATUS_SUCCESS);
|
2008-12-27 08:03:32 +00:00
|
|
|
}
|
|
|
|
|
2009-05-29 18:46:57 +00:00
|
|
|
static usb_error_t
|
2009-03-18 02:38:35 +00:00
|
|
|
usbd_setup_endpoint_one(ip, ifidx, ne, epconf)
|
|
|
|
irp *ip;
|
|
|
|
uint8_t ifidx;
|
|
|
|
struct ndisusb_ep *ne;
|
2009-05-28 17:36:36 +00:00
|
|
|
struct usb_config *epconf;
|
2009-03-18 02:38:35 +00:00
|
|
|
{
|
|
|
|
device_t dev = IRP_NDIS_DEV(ip);
|
|
|
|
struct ndis_softc *sc = device_get_softc(dev);
|
2009-05-28 17:36:36 +00:00
|
|
|
struct usb_xfer *xfer;
|
2009-05-29 18:46:57 +00:00
|
|
|
usb_error_t status;
|
2009-03-18 02:38:35 +00:00
|
|
|
|
|
|
|
InitializeListHead(&ne->ne_active);
|
|
|
|
InitializeListHead(&ne->ne_pending);
|
|
|
|
KeInitializeSpinLock(&ne->ne_lock);
|
|
|
|
|
2009-06-15 01:02:43 +00:00
|
|
|
status = usbd_transfer_setup(sc->ndisusb_dev, &ifidx, ne->ne_xfer,
|
2009-03-18 02:38:35 +00:00
|
|
|
epconf, 1, sc, &sc->ndisusb_mtx);
|
|
|
|
if (status != USB_ERR_NORMAL_COMPLETION) {
|
|
|
|
device_printf(dev, "couldn't setup xfer: %s\n",
|
2009-06-15 01:02:43 +00:00
|
|
|
usbd_errstr(status));
|
2009-03-18 02:38:35 +00:00
|
|
|
return (status);
|
|
|
|
}
|
|
|
|
xfer = ne->ne_xfer[0];
|
2009-06-23 02:19:59 +00:00
|
|
|
usbd_xfer_set_priv(xfer, ne);
|
2009-03-18 02:38:35 +00:00
|
|
|
|
|
|
|
return (status);
|
|
|
|
}
|
|
|
|
|
2009-05-29 18:46:57 +00:00
|
|
|
static usb_error_t
|
2009-03-18 02:38:35 +00:00
|
|
|
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);
|
2009-05-29 18:46:57 +00:00
|
|
|
usb_error_t status;
|
2009-03-18 02:38:35 +00:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2009-05-29 18:46:57 +00:00
|
|
|
static usb_error_t
|
2009-03-07 07:26:22 +00:00
|
|
|
usbd_setup_endpoint(ip, ifidx, ep)
|
|
|
|
irp *ip;
|
|
|
|
uint8_t ifidx;
|
2009-05-28 17:36:36 +00:00
|
|
|
struct usb_endpoint_descriptor *ep;
|
2009-03-07 07:26:22 +00:00
|
|
|
{
|
|
|
|
device_t dev = IRP_NDIS_DEV(ip);
|
|
|
|
struct ndis_softc *sc = device_get_softc(dev);
|
|
|
|
struct ndisusb_ep *ne;
|
2009-05-28 17:36:36 +00:00
|
|
|
struct usb_config cfg;
|
|
|
|
struct usb_xfer *xfer;
|
2009-05-29 18:46:57 +00:00
|
|
|
usb_error_t status;
|
2009-03-07 07:26:22 +00:00
|
|
|
|
|
|
|
/* 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;
|
|
|
|
|
2009-05-28 17:36:36 +00:00
|
|
|
memset(&cfg, 0, sizeof(struct usb_config));
|
2009-03-07 07:26:22 +00:00
|
|
|
cfg.type = UE_GET_XFERTYPE(ep->bmAttributes);
|
|
|
|
cfg.endpoint = UE_GET_ADDR(ep->bEndpointAddress);
|
|
|
|
cfg.direction = UE_GET_DIR(ep->bEndpointAddress);
|
2009-04-05 18:20:38 +00:00
|
|
|
cfg.callback = &usbd_non_isoc_callback;
|
|
|
|
cfg.bufsize = UGETW(ep->wMaxPacketSize);
|
|
|
|
cfg.flags.proxy_buffer = 1;
|
2009-03-07 07:26:22 +00:00
|
|
|
if (UE_GET_DIR(ep->bEndpointAddress) == UE_DIR_IN)
|
2009-04-05 18:20:38 +00:00
|
|
|
cfg.flags.short_xfer_ok = 1;
|
2009-03-07 07:26:22 +00:00
|
|
|
|
2009-06-15 01:02:43 +00:00
|
|
|
status = usbd_transfer_setup(sc->ndisusb_dev, &ifidx, ne->ne_xfer,
|
2009-03-12 02:51:55 +00:00
|
|
|
&cfg, 1, sc, &sc->ndisusb_mtx);
|
2009-03-07 07:26:22 +00:00
|
|
|
if (status != USB_ERR_NORMAL_COMPLETION) {
|
|
|
|
device_printf(dev, "couldn't setup xfer: %s\n",
|
2009-06-15 01:02:43 +00:00
|
|
|
usbd_errstr(status));
|
2009-03-07 07:26:22 +00:00
|
|
|
return (status);
|
|
|
|
}
|
|
|
|
xfer = ne->ne_xfer[0];
|
2009-06-23 02:19:59 +00:00
|
|
|
usbd_xfer_set_priv(xfer, ne);
|
2009-03-07 07:26:22 +00:00
|
|
|
if (UE_GET_DIR(ep->bEndpointAddress) == UE_DIR_IN)
|
2009-06-23 02:19:59 +00:00
|
|
|
usbd_xfer_set_timeout(xfer, NDISUSB_NO_TIMEOUT);
|
2009-03-07 07:26:22 +00:00
|
|
|
else {
|
|
|
|
if (UE_GET_XFERTYPE(ep->bmAttributes) == UE_BULK)
|
2009-06-23 02:19:59 +00:00
|
|
|
usbd_xfer_set_timeout(xfer, NDISUSB_TX_TIMEOUT);
|
2009-03-07 07:26:22 +00:00
|
|
|
else
|
2009-06-23 02:19:59 +00:00
|
|
|
usbd_xfer_set_timeout(xfer, NDISUSB_INTR_TIMEOUT);
|
2009-03-07 07:26:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return (status);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int32_t
|
|
|
|
usbd_func_abort_pipe(ip)
|
|
|
|
irp *ip;
|
|
|
|
{
|
2009-03-12 02:51:55 +00:00
|
|
|
device_t dev = IRP_NDIS_DEV(ip);
|
|
|
|
struct ndis_softc *sc = device_get_softc(dev);
|
2009-03-07 07:26:22 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2009-03-12 02:51:55 +00:00
|
|
|
NDISUSB_LOCK(sc);
|
2009-06-15 01:02:43 +00:00
|
|
|
usbd_transfer_stop(ne->ne_xfer[0]);
|
|
|
|
usbd_transfer_start(ne->ne_xfer[0]);
|
2009-03-12 02:51:55 +00:00
|
|
|
NDISUSB_UNLOCK(sc);
|
2009-03-07 07:26:22 +00:00
|
|
|
|
|
|
|
return (USBD_STATUS_SUCCESS);
|
|
|
|
}
|
|
|
|
|
2008-12-27 08:03:32 +00:00
|
|
|
static int32_t
|
|
|
|
usbd_func_vendorclass(ip)
|
|
|
|
irp *ip;
|
|
|
|
{
|
|
|
|
device_t dev = IRP_NDIS_DEV(ip);
|
2009-03-18 02:38:35 +00:00
|
|
|
int32_t error;
|
2009-03-07 07:26:22 +00:00
|
|
|
struct ndis_softc *sc = device_get_softc(dev);
|
2009-03-18 02:38:35 +00:00
|
|
|
struct ndisusb_ep *ne;
|
|
|
|
struct ndisusb_xfer *nx;
|
2008-12-27 08:03:32 +00:00
|
|
|
struct usbd_urb_vendor_or_class_request *vcreq;
|
|
|
|
union usbd_urb *urb;
|
2009-03-18 02:38:35 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
2008-12-27 08:03:32 +00:00
|
|
|
|
|
|
|
urb = usbd_geturb(ip);
|
|
|
|
vcreq = &urb->uu_vcreq;
|
2009-03-18 02:38:35 +00:00
|
|
|
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;
|
2008-12-27 08:03:32 +00:00
|
|
|
|
2009-03-18 02:38:35 +00:00
|
|
|
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);
|
2008-12-27 08:03:32 +00:00
|
|
|
}
|
2009-03-18 02:38:35 +00:00
|
|
|
nx->nx_ep = ne;
|
|
|
|
nx->nx_priv = ip;
|
|
|
|
KeAcquireSpinLockAtDpcLevel(&ne->ne_lock);
|
|
|
|
InsertTailList((&ne->ne_pending), (&nx->nx_next));
|
|
|
|
KeReleaseSpinLockFromDpcLevel(&ne->ne_lock);
|
2008-12-27 08:03:32 +00:00
|
|
|
|
2009-03-18 02:38:35 +00:00
|
|
|
/* 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);
|
2008-12-27 08:03:32 +00:00
|
|
|
|
2009-03-18 02:38:35 +00:00
|
|
|
error = usbd_taskadd(ip, NDISUSB_TASK_VENDOR);
|
|
|
|
if (error != USBD_STATUS_SUCCESS)
|
|
|
|
return (error);
|
2008-12-27 08:03:32 +00:00
|
|
|
|
2009-03-18 02:38:35 +00:00
|
|
|
return (USBD_STATUS_PENDING);
|
2008-12-27 08:03:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
usbd_irpcancel(dobj, ip)
|
|
|
|
device_object *dobj;
|
|
|
|
irp *ip;
|
|
|
|
{
|
2009-03-17 05:57:43 +00:00
|
|
|
device_t dev = IRP_NDIS_DEV(ip);
|
|
|
|
struct ndis_softc *sc = device_get_softc(dev);
|
2009-03-07 07:26:22 +00:00
|
|
|
struct ndisusb_ep *ne = IRP_NDISUSB_EP(ip);
|
2008-12-27 08:03:32 +00:00
|
|
|
|
2009-03-07 07:26:22 +00:00
|
|
|
if (ne == NULL) {
|
2008-12-27 08:03:32 +00:00
|
|
|
ip->irp_cancel = TRUE;
|
|
|
|
IoReleaseCancelSpinLock(ip->irp_cancelirql);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2009-03-07 07:26:22 +00:00
|
|
|
* Make sure that the current USB transfer proxy is
|
|
|
|
* cancelled and then restarted.
|
2008-12-27 08:03:32 +00:00
|
|
|
*/
|
2009-03-17 05:57:43 +00:00
|
|
|
NDISUSB_LOCK(sc);
|
2009-06-15 01:02:43 +00:00
|
|
|
usbd_transfer_stop(ne->ne_xfer[0]);
|
|
|
|
usbd_transfer_start(ne->ne_xfer[0]);
|
2009-03-17 05:57:43 +00:00
|
|
|
NDISUSB_UNLOCK(sc);
|
2008-12-27 08:03:32 +00:00
|
|
|
|
|
|
|
ip->irp_cancel = TRUE;
|
|
|
|
IoReleaseCancelSpinLock(ip->irp_cancelirql);
|
|
|
|
}
|
|
|
|
|
2009-03-07 07:26:22 +00:00
|
|
|
static void
|
|
|
|
usbd_xfer_complete(struct ndis_softc *sc, struct ndisusb_ep *ne,
|
2009-05-29 18:46:57 +00:00
|
|
|
struct ndisusb_xfer *nx, usb_error_t status)
|
2008-12-27 08:03:32 +00:00
|
|
|
{
|
2009-03-07 07:26:22 +00:00
|
|
|
struct ndisusb_xferdone *nd;
|
|
|
|
uint8_t irql;
|
2008-12-27 08:03:32 +00:00
|
|
|
|
2009-03-07 07:26:22 +00:00
|
|
|
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;
|
2008-12-27 08:03:32 +00:00
|
|
|
|
2009-03-07 07:26:22 +00:00
|
|
|
KeAcquireSpinLock(&sc->ndisusb_xferdonelock, &irql);
|
|
|
|
InsertTailList((&sc->ndisusb_xferdonelist), (&nd->nd_donelist));
|
|
|
|
KeReleaseSpinLock(&sc->ndisusb_xferdonelock, irql);
|
2008-12-27 08:03:32 +00:00
|
|
|
|
2009-03-07 07:26:22 +00:00
|
|
|
IoQueueWorkItem(sc->ndisusb_xferdoneitem,
|
|
|
|
(io_workitem_func)usbd_xfertask_wrap, WORKQUEUE_CRITICAL, sc);
|
|
|
|
}
|
2008-12-27 08:03:32 +00:00
|
|
|
|
2009-03-07 07:26:22 +00:00
|
|
|
static struct ndisusb_xfer *
|
|
|
|
usbd_aq_getfirst(struct ndis_softc *sc, struct ndisusb_ep *ne)
|
|
|
|
{
|
|
|
|
struct ndisusb_xfer *nx;
|
|
|
|
|
2009-03-12 02:51:55 +00:00
|
|
|
KeAcquireSpinLockAtDpcLevel(&ne->ne_lock);
|
2009-03-07 07:26:22 +00:00
|
|
|
if (IsListEmpty(&ne->ne_active)) {
|
|
|
|
device_printf(sc->ndis_dev,
|
|
|
|
"%s: the active queue can't be empty.\n", __func__);
|
2009-03-12 02:51:55 +00:00
|
|
|
KeReleaseSpinLockFromDpcLevel(&ne->ne_lock);
|
2009-03-07 07:26:22 +00:00
|
|
|
return (NULL);
|
|
|
|
}
|
|
|
|
nx = CONTAINING_RECORD(ne->ne_active.nle_flink, struct ndisusb_xfer,
|
|
|
|
nx_next);
|
|
|
|
RemoveEntryList(&nx->nx_next);
|
2009-03-12 02:51:55 +00:00
|
|
|
KeReleaseSpinLockFromDpcLevel(&ne->ne_lock);
|
2008-12-27 08:03:32 +00:00
|
|
|
|
2009-03-07 07:26:22 +00:00
|
|
|
return (nx);
|
2008-12-27 08:03:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2009-06-23 02:19:59 +00:00
|
|
|
usbd_non_isoc_callback(struct usb_xfer *xfer, usb_error_t error)
|
2008-12-27 08:03:32 +00:00
|
|
|
{
|
2009-03-07 07:26:22 +00:00
|
|
|
irp *ip;
|
2009-06-23 02:19:59 +00:00
|
|
|
struct ndis_softc *sc = usbd_xfer_softc(xfer);
|
|
|
|
struct ndisusb_ep *ne = usbd_xfer_get_priv(xfer);
|
2008-12-27 08:03:32 +00:00
|
|
|
struct ndisusb_xfer *nx;
|
2009-03-07 07:26:22 +00:00
|
|
|
struct usbd_urb_bulk_or_intr_transfer *ubi;
|
2009-06-23 02:19:59 +00:00
|
|
|
struct usb_page_cache *pc;
|
2008-12-27 08:03:32 +00:00
|
|
|
uint8_t irql;
|
2009-03-07 07:26:22 +00:00
|
|
|
uint32_t len;
|
|
|
|
union usbd_urb *urb;
|
|
|
|
usb_endpoint_descriptor_t *ep;
|
2009-06-23 02:19:59 +00:00
|
|
|
int actlen, sumlen;
|
|
|
|
|
|
|
|
usbd_xfer_status(xfer, &actlen, &sumlen, NULL, NULL);
|
2008-12-27 08:03:32 +00:00
|
|
|
|
2009-03-07 07:26:22 +00:00
|
|
|
switch (USB_GET_STATE(xfer)) {
|
|
|
|
case USB_ST_TRANSFERRED:
|
|
|
|
nx = usbd_aq_getfirst(sc, ne);
|
2009-06-23 02:19:59 +00:00
|
|
|
pc = usbd_xfer_get_frame(xfer, 0);
|
2009-03-07 07:26:22 +00:00
|
|
|
if (nx == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* copy in data with regard to the URB */
|
|
|
|
if (ne->ne_dirin != 0)
|
2009-06-23 02:19:59 +00:00
|
|
|
usbd_copy_out(pc, 0, nx->nx_urbbuf, actlen);
|
|
|
|
nx->nx_urbbuf += actlen;
|
|
|
|
nx->nx_urbactlen += actlen;
|
|
|
|
nx->nx_urblen -= actlen;
|
2009-03-07 07:26:22 +00:00
|
|
|
|
|
|
|
/* check for short transfer */
|
2009-06-23 02:19:59 +00:00
|
|
|
if (actlen < sumlen)
|
2009-03-07 07:26:22 +00:00
|
|
|
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,
|
2009-06-23 02:19:59 +00:00
|
|
|
((actlen < sumlen) && (nx->nx_shortxfer == 0)) ?
|
2009-03-07 07:26:22 +00:00
|
|
|
USB_ERR_SHORT_XFER : USB_ERR_NORMAL_COMPLETION);
|
2008-12-27 08:03:32 +00:00
|
|
|
|
2009-03-07 07:26:22 +00:00
|
|
|
/* 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:
|
2009-06-23 02:19:59 +00:00
|
|
|
len = MIN(usbd_xfer_max_len(xfer), nx->nx_urblen);
|
|
|
|
pc = usbd_xfer_get_frame(xfer, 0);
|
2009-03-07 07:26:22 +00:00
|
|
|
if (UE_GET_DIR(ep->bEndpointAddress) == UE_DIR_OUT)
|
2009-06-23 02:19:59 +00:00
|
|
|
usbd_copy_in(pc, 0, nx->nx_urbbuf, len);
|
|
|
|
usbd_xfer_set_frame_len(xfer, 0, len);
|
|
|
|
usbd_xfer_set_frames(xfer, 1);
|
2009-06-15 01:02:43 +00:00
|
|
|
usbd_transfer_submit(xfer);
|
2009-03-07 07:26:22 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
nx = usbd_aq_getfirst(sc, ne);
|
|
|
|
if (nx == NULL)
|
|
|
|
return;
|
2009-06-23 02:19:59 +00:00
|
|
|
if (error != USB_ERR_CANCELLED) {
|
|
|
|
usbd_xfer_set_stall(xfer);
|
2009-03-07 07:26:22 +00:00
|
|
|
device_printf(sc->ndis_dev, "usb xfer warning (%s)\n",
|
2009-06-23 02:19:59 +00:00
|
|
|
usbd_errstr(error));
|
2009-03-07 07:26:22 +00:00
|
|
|
}
|
2009-06-23 02:19:59 +00:00
|
|
|
usbd_xfer_complete(sc, ne, nx, error);
|
|
|
|
if (error != USB_ERR_CANCELLED)
|
2009-03-07 07:26:22 +00:00
|
|
|
goto next;
|
|
|
|
break;
|
|
|
|
}
|
2008-12-27 08:03:32 +00:00
|
|
|
}
|
|
|
|
|
2009-03-18 02:38:35 +00:00
|
|
|
static void
|
2009-06-23 02:19:59 +00:00
|
|
|
usbd_ctrl_callback(struct usb_xfer *xfer, usb_error_t error)
|
2009-03-18 02:38:35 +00:00
|
|
|
{
|
|
|
|
irp *ip;
|
2009-06-23 02:19:59 +00:00
|
|
|
struct ndis_softc *sc = usbd_xfer_softc(xfer);
|
|
|
|
struct ndisusb_ep *ne = usbd_xfer_get_priv(xfer);
|
2009-03-18 02:38:35 +00:00
|
|
|
struct ndisusb_xfer *nx;
|
|
|
|
uint8_t irql;
|
|
|
|
union usbd_urb *urb;
|
|
|
|
struct usbd_urb_vendor_or_class_request *vcreq;
|
2009-06-23 02:19:59 +00:00
|
|
|
struct usb_page_cache *pc;
|
2009-03-18 02:38:35 +00:00
|
|
|
uint8_t type = 0;
|
2009-05-28 17:36:36 +00:00
|
|
|
struct usb_device_request req;
|
2009-06-23 02:19:59 +00:00
|
|
|
int len;
|
2009-03-18 02:38:35 +00:00
|
|
|
|
|
|
|
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) {
|
2009-06-23 02:19:59 +00:00
|
|
|
pc = usbd_xfer_get_frame(xfer, 1);
|
2009-06-23 06:00:31 +00:00
|
|
|
len = usbd_xfer_frame_len(xfer, 1);
|
2009-06-23 02:19:59 +00:00
|
|
|
usbd_copy_out(pc, 0, vcreq->uvc_trans_buf, len);
|
|
|
|
nx->nx_urbactlen += len;
|
2009-03-18 02:38:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
2009-06-23 02:19:59 +00:00
|
|
|
pc = usbd_xfer_get_frame(xfer, 0);
|
|
|
|
usbd_copy_in(pc, 0, &req, sizeof(req));
|
|
|
|
usbd_xfer_set_frame_len(xfer, 0, sizeof(req));
|
|
|
|
usbd_xfer_set_frames(xfer, 1);
|
2009-03-18 02:38:35 +00:00
|
|
|
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);
|
2009-06-23 02:19:59 +00:00
|
|
|
usbd_xfer_set_frame_len(xfer, 1,
|
|
|
|
MIN(usbd_xfer_max_len(xfer),
|
|
|
|
vcreq->uvc_trans_buflen));
|
|
|
|
usbd_xfer_set_frames(xfer, 2);
|
2009-03-18 02:38:35 +00:00
|
|
|
} else {
|
2009-06-26 01:42:41 +00:00
|
|
|
if (nx->nx_urblen > USBD_CTRL_WRITE_BUFFER_SP)
|
2009-03-18 02:38:35 +00:00
|
|
|
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) {
|
2009-06-23 02:19:59 +00:00
|
|
|
pc = usbd_xfer_get_frame(xfer, 1);
|
|
|
|
usbd_copy_in(pc, 0, nx->nx_urbbuf,
|
|
|
|
nx->nx_urblen);
|
|
|
|
usbd_xfer_set_frame_len(xfer, 1, nx->nx_urblen);
|
|
|
|
usbd_xfer_set_frames(xfer, 2);
|
2009-03-18 02:38:35 +00:00
|
|
|
}
|
|
|
|
}
|
2009-06-15 01:02:43 +00:00
|
|
|
usbd_transfer_submit(xfer);
|
2009-03-18 02:38:35 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
nx = usbd_aq_getfirst(sc, ne);
|
|
|
|
if (nx == NULL)
|
|
|
|
return;
|
2009-06-23 02:19:59 +00:00
|
|
|
if (error != USB_ERR_CANCELLED) {
|
|
|
|
usbd_xfer_set_stall(xfer);
|
2009-03-18 02:38:35 +00:00
|
|
|
device_printf(sc->ndis_dev, "usb xfer warning (%s)\n",
|
2009-06-23 02:19:59 +00:00
|
|
|
usbd_errstr(error));
|
2009-03-18 02:38:35 +00:00
|
|
|
}
|
2009-06-23 02:19:59 +00:00
|
|
|
usbd_xfer_complete(sc, ne, nx, error);
|
|
|
|
if (error != USB_ERR_CANCELLED)
|
2009-03-18 02:38:35 +00:00
|
|
|
goto next;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-03-07 07:26:22 +00:00
|
|
|
static struct ndisusb_ep *
|
|
|
|
usbd_get_ndisep(ip, ep)
|
|
|
|
irp *ip;
|
|
|
|
usb_endpoint_descriptor_t *ep;
|
2008-12-27 08:03:32 +00:00
|
|
|
{
|
2009-03-07 07:26:22 +00:00
|
|
|
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;
|
2008-12-27 08:03:32 +00:00
|
|
|
|
2009-03-07 07:26:22 +00:00
|
|
|
return (ne);
|
2008-12-27 08:03:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
2009-03-07 07:26:22 +00:00
|
|
|
struct ndisusb_xferdone *nd;
|
|
|
|
struct ndisusb_xfer *nq;
|
2008-12-27 08:03:32 +00:00
|
|
|
struct usbd_urb_bulk_or_intr_transfer *ubi;
|
2009-03-18 02:38:35 +00:00
|
|
|
struct usbd_urb_vendor_or_class_request *vcreq;
|
2008-12-27 08:03:32 +00:00
|
|
|
union usbd_urb *urb;
|
2009-05-29 18:46:57 +00:00
|
|
|
usb_error_t status;
|
2009-03-07 07:26:22 +00:00
|
|
|
void *priv;
|
2008-12-27 08:03:32 +00:00
|
|
|
|
|
|
|
dev = sc->ndis_dev;
|
|
|
|
|
2009-03-07 07:26:22 +00:00
|
|
|
if (IsListEmpty(&sc->ndisusb_xferdonelist))
|
2008-12-27 08:03:32 +00:00
|
|
|
return;
|
|
|
|
|
2009-03-12 02:51:55 +00:00
|
|
|
KeAcquireSpinLockAtDpcLevel(&sc->ndisusb_xferdonelock);
|
2009-03-07 07:26:22 +00:00
|
|
|
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;
|
2008-12-27 08:03:32 +00:00
|
|
|
error = 0;
|
|
|
|
ip = priv;
|
|
|
|
urb = usbd_geturb(ip);
|
2009-03-07 07:26:22 +00:00
|
|
|
|
2008-12-27 08:03:32 +00:00
|
|
|
ip->irp_cancelfunc = NULL;
|
2009-03-07 07:26:22 +00:00
|
|
|
IRP_NDISUSB_EP(ip) = NULL;
|
|
|
|
|
2008-12-27 08:03:32 +00:00
|
|
|
switch (status) {
|
2009-03-07 07:26:22 +00:00
|
|
|
case USB_ERR_NORMAL_COMPLETION:
|
2009-03-18 02:38:35 +00:00
|
|
|
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;
|
|
|
|
}
|
2009-03-07 07:26:22 +00:00
|
|
|
ip->irp_iostat.isb_info = nq->nx_urbactlen;
|
2008-12-27 08:03:32 +00:00
|
|
|
ip->irp_iostat.isb_status = STATUS_SUCCESS;
|
|
|
|
USBD_URB_STATUS(urb) = USBD_STATUS_SUCCESS;
|
|
|
|
break;
|
2009-03-07 07:26:22 +00:00
|
|
|
case USB_ERR_CANCELLED:
|
2008-12-27 08:03:32 +00:00
|
|
|
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;
|
|
|
|
}
|
2009-03-07 07:26:22 +00:00
|
|
|
|
2008-12-27 08:03:32 +00:00
|
|
|
l = l->nle_flink;
|
2009-03-07 07:26:22 +00:00
|
|
|
RemoveEntryList(&nd->nd_donelist);
|
|
|
|
free(nq, M_USBDEV);
|
|
|
|
free(nd, M_USBDEV);
|
2008-12-27 08:03:32 +00:00
|
|
|
if (error)
|
|
|
|
continue;
|
2009-03-12 02:51:55 +00:00
|
|
|
KeReleaseSpinLockFromDpcLevel(&sc->ndisusb_xferdonelock);
|
2008-12-27 08:03:32 +00:00
|
|
|
/* NB: call after cleaning */
|
|
|
|
IoCompleteRequest(ip, IO_NO_INCREMENT);
|
2009-03-12 02:51:55 +00:00
|
|
|
KeAcquireSpinLockAtDpcLevel(&sc->ndisusb_xferdonelock);
|
2008-12-27 08:03:32 +00:00
|
|
|
}
|
2009-03-12 02:51:55 +00:00
|
|
|
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;
|
2009-06-15 01:02:43 +00:00
|
|
|
usbd_transfer_start(ne->ne_xfer[0]);
|
2009-03-12 02:51:55 +00:00
|
|
|
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;
|
|
|
|
|
2009-06-15 01:02:43 +00:00
|
|
|
usbd_transfer_stop(ne->ne_xfer[0]);
|
|
|
|
usbd_transfer_start(ne->ne_xfer[0]);
|
2009-03-12 02:51:55 +00:00
|
|
|
break;
|
2009-03-18 02:38:35 +00:00
|
|
|
case NDISUSB_TASK_VENDOR:
|
|
|
|
ne = (urb->uu_vcreq.uvc_trans_flags &
|
|
|
|
USBD_TRANSFER_DIRECTION_IN) ?
|
|
|
|
&sc->ndisusb_dread_ep : &sc->ndisusb_dwrite_ep;
|
2009-06-15 01:02:43 +00:00
|
|
|
usbd_transfer_start(ne->ne_xfer[0]);
|
2009-03-18 02:38:35 +00:00
|
|
|
break;
|
2009-03-12 02:51:55 +00:00
|
|
|
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);
|
2008-12-27 08:03:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int32_t
|
|
|
|
usbd_func_bulkintr(ip)
|
|
|
|
irp *ip;
|
|
|
|
{
|
2009-03-12 02:51:55 +00:00
|
|
|
int32_t error;
|
2009-03-07 07:26:22 +00:00
|
|
|
struct ndisusb_ep *ne;
|
|
|
|
struct ndisusb_xfer *nx;
|
2008-12-27 08:03:32 +00:00
|
|
|
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);
|
|
|
|
|
2009-03-07 07:26:22 +00:00
|
|
|
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);
|
2008-12-27 08:03:32 +00:00
|
|
|
}
|
|
|
|
|
2009-03-07 07:26:22 +00:00
|
|
|
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);
|
2008-12-27 08:03:32 +00:00
|
|
|
}
|
2009-03-07 07:26:22 +00:00
|
|
|
nx->nx_ep = ne;
|
|
|
|
nx->nx_priv = ip;
|
2009-03-12 02:51:55 +00:00
|
|
|
KeAcquireSpinLockAtDpcLevel(&ne->ne_lock);
|
2009-03-07 07:26:22 +00:00
|
|
|
InsertTailList((&ne->ne_pending), (&nx->nx_next));
|
2009-03-12 02:51:55 +00:00
|
|
|
KeReleaseSpinLockFromDpcLevel(&ne->ne_lock);
|
2008-12-27 08:03:32 +00:00
|
|
|
|
|
|
|
/* 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);
|
|
|
|
|
2009-03-12 02:51:55 +00:00
|
|
|
error = usbd_taskadd(ip, NDISUSB_TASK_TSTART);
|
|
|
|
if (error != USBD_STATUS_SUCCESS)
|
|
|
|
return (error);
|
2008-12-27 08:03:32 +00:00
|
|
|
|
2009-03-07 07:26:22 +00:00
|
|
|
return (USBD_STATUS_PENDING);
|
2008-12-27 08:03:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static union usbd_urb *
|
|
|
|
USBD_CreateConfigurationRequest(conf, len)
|
2009-03-07 07:26:22 +00:00
|
|
|
usb_config_descriptor_t *conf;
|
2008-12-27 08:03:32 +00:00
|
|
|
uint16_t *len;
|
|
|
|
{
|
2009-03-07 07:26:22 +00:00
|
|
|
struct usbd_interface_list_entry list[2];
|
|
|
|
union usbd_urb *urb;
|
2008-12-27 08:03:32 +00:00
|
|
|
|
|
|
|
bzero(list, sizeof(struct usbd_interface_list_entry) * 2);
|
2009-03-07 07:26:22 +00:00
|
|
|
list[0].uil_intfdesc = USBD_ParseConfigurationDescriptorEx(conf, conf,
|
2008-12-27 08:03:32 +00:00
|
|
|
-1, -1, -1, -1, -1);
|
2009-03-07 07:26:22 +00:00
|
|
|
urb = USBD_CreateConfigurationRequestEx(conf, list);
|
|
|
|
if (urb == NULL)
|
2009-11-02 11:07:42 +00:00
|
|
|
return (NULL);
|
2008-12-27 08:03:32 +00:00
|
|
|
|
2009-03-07 07:26:22 +00:00
|
|
|
*len = urb->uu_selconf.usc_hdr.uuh_len;
|
2009-11-02 11:07:42 +00:00
|
|
|
return (urb);
|
2008-12-27 08:03:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static union usbd_urb *
|
|
|
|
USBD_CreateConfigurationRequestEx(conf, list)
|
|
|
|
usb_config_descriptor_t *conf;
|
|
|
|
struct usbd_interface_list_entry *list;
|
|
|
|
{
|
2009-03-07 07:26:22 +00:00
|
|
|
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;
|
2008-12-27 08:03:32 +00:00
|
|
|
|
|
|
|
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);
|
|
|
|
|
2009-03-07 07:26:22 +00:00
|
|
|
selconf = ExAllocatePoolWithTag(NonPagedPool, size, 0);
|
|
|
|
if (selconf == NULL)
|
2009-11-02 11:07:42 +00:00
|
|
|
return (NULL);
|
2009-03-07 07:26:22 +00:00
|
|
|
selconf->usc_hdr.uuh_func = URB_FUNCTION_SELECT_CONFIGURATION;
|
|
|
|
selconf->usc_hdr.uuh_len = size;
|
|
|
|
selconf->usc_handle = conf;
|
|
|
|
selconf->usc_conf = conf;
|
2008-12-27 08:03:32 +00:00
|
|
|
|
2009-03-07 07:26:22 +00:00
|
|
|
intf = &selconf->usc_intf;
|
|
|
|
for (i = 0; i < conf->bNumInterface; i++) {
|
2008-12-27 08:03:32 +00:00
|
|
|
if (list[i].uil_intfdesc == NULL)
|
|
|
|
break;
|
|
|
|
|
2009-03-07 07:26:22 +00:00
|
|
|
list[i].uil_intf = intf;
|
|
|
|
desc = list[i].uil_intfdesc;
|
2008-12-27 08:03:32 +00:00
|
|
|
|
2009-03-07 07:26:22 +00:00
|
|
|
intf->uii_len = sizeof(struct usbd_interface_information) +
|
2008-12-27 08:03:32 +00:00
|
|
|
(desc->bNumEndpoints - 1) *
|
|
|
|
sizeof(struct usbd_pipe_information);
|
2009-03-07 07:26:22 +00:00
|
|
|
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 =
|
2008-12-27 08:03:32 +00:00
|
|
|
USBD_DEFAULT_MAXIMUM_TRANSFER_SIZE;
|
|
|
|
|
2009-03-07 07:26:22 +00:00
|
|
|
intf = (struct usbd_interface_information *)((char *)intf +
|
2008-12-27 08:03:32 +00:00
|
|
|
intf->uii_len);
|
2009-03-07 07:26:22 +00:00
|
|
|
}
|
2008-12-27 08:03:32 +00:00
|
|
|
|
2009-03-07 07:26:22 +00:00
|
|
|
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;
|
|
|
|
{
|
2009-03-07 07:26:22 +00:00
|
|
|
|
- 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;
|
|
|
|
}
|
|
|
|
|
2008-12-27 08:03:32 +00:00
|
|
|
static usb_interface_descriptor_t *
|
2009-02-24 18:09:31 +00:00
|
|
|
USBD_ParseConfigurationDescriptor(usb_config_descriptor_t *conf,
|
|
|
|
uint8_t intfnum, uint8_t altset)
|
2008-12-27 08:03:32 +00:00
|
|
|
{
|
2009-03-07 07:26:22 +00:00
|
|
|
|
|
|
|
return USBD_ParseConfigurationDescriptorEx(conf, conf, intfnum, altset,
|
2008-12-27 08:03:32 +00:00
|
|
|
-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;
|
|
|
|
{
|
2009-05-28 17:36:36 +00:00
|
|
|
struct usb_descriptor *next = NULL;
|
2008-12-27 08:03:32 +00:00
|
|
|
usb_interface_descriptor_t *desc;
|
|
|
|
|
2009-06-15 01:02:43 +00:00
|
|
|
while ((next = usb_desc_foreach(conf, next)) != NULL) {
|
2009-03-16 11:19:07 +00:00
|
|
|
desc = (usb_interface_descriptor_t *)next;
|
2008-12-27 08:03:32 +00:00
|
|
|
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");
|
|
|
|
}
|
|
|
|
|
|
|
|
image_patch_table usbd_functbl[] = {
|
2008-12-27 08:03:32 +00:00
|
|
|
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 }
|
|
|
|
};
|
|
|
|
|
2008-12-27 08:03:32 +00:00
|
|
|
MODULE_DEPEND(ndis, usb, 1, 1, 1);
|