Use the EVENTHANDLER system to hook into the usb device configuration and

perform a function such as ejecting a 3G autoinstaller disk. The eventhandler
system properly tracks threads and is safe to unload, remove the
setting/clearing of a function pointer in the kernel by u3g(4) which included a
tsleep for safety.
This commit is contained in:
Andrew Thompson 2009-12-17 21:42:10 +00:00
parent 2b54315009
commit 2a4c6157ca
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=200653
5 changed files with 112 additions and 132 deletions

View File

@ -122,8 +122,13 @@ static void u3g_stop_read(struct ucom_softc *ucom);
static void u3g_start_write(struct ucom_softc *ucom);
static void u3g_stop_write(struct ucom_softc *ucom);
static void u3g_test_autoinst(void *, struct usb_device *,
struct usb_attach_arg *);
static int u3g_driver_loaded(struct module *mod, int what, void *arg);
static eventhandler_tag u3g_etag;
static const struct usb_config u3g_config[U3G_N_TRANSFER] = {
[U3G_BULK_WR] = {
@ -360,58 +365,48 @@ u3g_sael_m460_init(struct usb_device *udev)
}
}
static int
u3g_lookup_huawei(struct usb_attach_arg *uaa)
{
/* Calling the lookup function will also set the driver info! */
return (usbd_lookup_id_by_uaa(u3g_devs, sizeof(u3g_devs), uaa));
}
/*
* The following function handles 3G modem devices (E220, Mobile,
* etc.) with auto-install flash disks for Windows/MacOSX on the first
* interface. After some command or some delay they change appearance
* to a modem.
*/
static usb_error_t
u3g_test_huawei_autoinst(struct usb_device *udev,
static void
u3g_test_autoinst(void *arg, struct usb_device *udev,
struct usb_attach_arg *uaa)
{
struct usb_interface *iface;
struct usb_interface_descriptor *id;
uint32_t flags;
if (udev == NULL) {
return (USB_ERR_INVAL);
}
if (uaa->dev_state != UAA_DEV_READY)
return;
iface = usbd_get_iface(udev, 0);
if (iface == NULL) {
return (USB_ERR_INVAL);
}
if (iface == NULL)
return;
id = iface->idesc;
if (id == NULL) {
return (USB_ERR_INVAL);
}
if (id->bInterfaceClass != UICLASS_MASS) {
return (USB_ERR_INVAL);
}
if (u3g_lookup_huawei(uaa)) {
if (id == NULL || id->bInterfaceClass != UICLASS_MASS)
return;
if (usbd_lookup_id_by_uaa(u3g_devs, sizeof(u3g_devs), uaa)) {
/* no device match */
return (USB_ERR_INVAL);
return;
}
flags = USB_GET_DRIVER_INFO(uaa);
if (flags & U3GFL_HUAWEI_INIT) {
u3g_huawei_init(udev);
} else if (flags & U3GFL_SCSI_EJECT) {
return (usb_test_autoinstall(udev, 0, 1));
if (usb_test_autoinstall(udev, 0, 1) != 0)
return;
} else if (flags & U3GFL_SIERRA_INIT) {
u3g_sierra_init(udev);
} else {
/* no quirks */
return (USB_ERR_INVAL);
return;
}
return (0); /* success */
uaa->dev_state = UAA_DEV_EJECTING;
return; /* success */
}
static int
@ -420,10 +415,11 @@ u3g_driver_loaded(struct module *mod, int what, void *arg)
switch (what) {
case MOD_LOAD:
/* register our autoinstall handler */
usb_test_huawei_autoinst_p = &u3g_test_huawei_autoinst;
u3g_etag = EVENTHANDLER_REGISTER(usb_dev_configured,
u3g_test_autoinst, NULL, EVENTHANDLER_PRI_ANY);
break;
case MOD_UNLOAD:
usb_test_huawei_unload(NULL);
EVENTHANDLER_DEREGISTER(usb_dev_configured, u3g_etag);
break;
default:
return (EOPNOTSUPP);
@ -445,7 +441,7 @@ u3g_probe(device_t self)
if (uaa->info.bInterfaceClass != UICLASS_VENDOR) {
return (ENXIO);
}
return (u3g_lookup_huawei(uaa));
return (usbd_lookup_id_by_uaa(u3g_devs, sizeof(u3g_devs), uaa));
}
static int

View File

@ -1204,6 +1204,7 @@ usb_init_attach_arg(struct usb_device *udev,
uaa->device = udev;
uaa->usb_mode = udev->flags.usb_mode;
uaa->port = udev->port_no;
uaa->dev_state = UAA_DEV_READY;
uaa->info.idVendor = UGETW(udev->ddesc.idVendor);
uaa->info.idProduct = UGETW(udev->ddesc.idProduct);
@ -1453,6 +1454,9 @@ usb_alloc_device(device_t parent_dev, struct usb_bus *bus,
size_t scratch_size;
usb_error_t err;
uint8_t device_index;
uint8_t config_index;
uint8_t config_quirk;
uint8_t set_config_failed;
DPRINTF("parent_dev=%p, bus=%p, parent_hub=%p, depth=%u, "
"port_index=%u, port_no=%u, speed=%u, usb_mode=%u\n",
@ -1732,96 +1736,91 @@ usb_alloc_device(device_t parent_dev, struct usb_bus *bus,
/* fetch the vendor and product strings from the device */
usbd_set_device_strings(udev);
if (udev->flags.usb_mode == USB_MODE_HOST) {
uint8_t config_index;
uint8_t config_quirk;
uint8_t set_config_failed = 0;
if (udev->flags.usb_mode == USB_MODE_DEVICE) {
/* USB device mode setup is complete */
err = 0;
goto config_done;
}
/*
* Most USB devices should attach to config index 0 by
* default
*/
if (usb_test_quirk(&uaa, UQ_CFG_INDEX_0)) {
config_index = 0;
config_quirk = 1;
} else if (usb_test_quirk(&uaa, UQ_CFG_INDEX_1)) {
config_index = 1;
config_quirk = 1;
} else if (usb_test_quirk(&uaa, UQ_CFG_INDEX_2)) {
config_index = 2;
config_quirk = 1;
} else if (usb_test_quirk(&uaa, UQ_CFG_INDEX_3)) {
config_index = 3;
config_quirk = 1;
} else if (usb_test_quirk(&uaa, UQ_CFG_INDEX_4)) {
config_index = 4;
config_quirk = 1;
} else {
config_index = 0;
config_quirk = 0;
}
/*
* Most USB devices should attach to config index 0 by
* default
*/
if (usb_test_quirk(&uaa, UQ_CFG_INDEX_0)) {
config_index = 0;
config_quirk = 1;
} else if (usb_test_quirk(&uaa, UQ_CFG_INDEX_1)) {
config_index = 1;
config_quirk = 1;
} else if (usb_test_quirk(&uaa, UQ_CFG_INDEX_2)) {
config_index = 2;
config_quirk = 1;
} else if (usb_test_quirk(&uaa, UQ_CFG_INDEX_3)) {
config_index = 3;
config_quirk = 1;
} else if (usb_test_quirk(&uaa, UQ_CFG_INDEX_4)) {
config_index = 4;
config_quirk = 1;
} else {
config_index = 0;
config_quirk = 0;
}
set_config_failed = 0;
repeat_set_config:
DPRINTF("setting config %u\n", config_index);
DPRINTF("setting config %u\n", config_index);
/* get the USB device configured */
err = usbd_set_config_index(udev, config_index);
if (err) {
if (udev->ddesc.bNumConfigurations != 0) {
if (!set_config_failed) {
set_config_failed = 1;
/* XXX try to re-enumerate the device */
err = usbd_req_re_enumerate(
udev, NULL);
if (err == 0)
goto repeat_set_config;
}
DPRINTFN(0, "Failure selecting "
"configuration index %u: %s, port %u, "
"addr %u (ignored)\n",
config_index, usbd_errstr(err), udev->port_no,
udev->address);
/* get the USB device configured */
err = usbd_set_config_index(udev, config_index);
if (err) {
if (udev->ddesc.bNumConfigurations != 0) {
if (!set_config_failed) {
set_config_failed = 1;
/* XXX try to re-enumerate the device */
err = usbd_req_re_enumerate(udev, NULL);
if (err == 0)
goto repeat_set_config;
}
DPRINTFN(0, "Failure selecting configuration index %u:"
"%s, port %u, addr %u (ignored)\n",
config_index, usbd_errstr(err), udev->port_no,
udev->address);
}
/*
* Some USB devices do not have any configurations. Ignore any
* set config failures!
*/
err = 0;
goto config_done;
}
if (!config_quirk && config_index + 1 < udev->ddesc.bNumConfigurations) {
if ((udev->cdesc->bNumInterface < 2) &&
usbd_get_no_descriptors(udev->cdesc, UDESC_ENDPOINT) == 0) {
DPRINTFN(0, "Found no endpoints, trying next config\n");
config_index++;
goto repeat_set_config;
}
if (config_index == 0) {
/*
* Some USB devices do not have any
* configurations. Ignore any set config
* failures!
* Try to figure out if we have an
* auto-install disk there:
*/
err = 0;
} else if (config_quirk) {
/* user quirk selects configuration index */
} else if ((config_index + 1) < udev->ddesc.bNumConfigurations) {
if ((udev->cdesc->bNumInterface < 2) &&
(usbd_get_no_descriptors(udev->cdesc,
UDESC_ENDPOINT) == 0)) {
DPRINTFN(0, "Found no endpoints "
"(trying next config)\n");
if (usb_test_autoinstall(udev, 0, 0) == 0) {
DPRINTFN(0, "Found possible auto-install "
"disk (trying next config)\n");
config_index++;
goto repeat_set_config;
}
if (config_index == 0) {
/*
* Try to figure out if we have an
* auto-install disk there:
*/
if (usb_test_autoinstall(udev, 0, 0) == 0) {
DPRINTFN(0, "Found possible auto-install "
"disk (trying next config)\n");
config_index++;
goto repeat_set_config;
}
}
} else if (usb_test_huawei_autoinst_p(udev, &uaa) == 0) {
DPRINTFN(0, "Found Huawei auto-install disk\n");
/* leave device unconfigured */
usb_unconfigure(udev, 0);
}
} else {
err = 0; /* set success */
}
EVENTHANDLER_INVOKE(usb_dev_configured, udev, &uaa);
if (uaa.dev_state != UAA_DEV_READY) {
/* leave device unconfigured */
usb_unconfigure(udev, 0);
}
config_done:
DPRINTF("new dev (addr %d), udev=%p, parent_hub=%p\n",
udev->address, udev, udev->parent_hub);

View File

@ -57,7 +57,6 @@ static usb_handle_req_t usb_temp_get_desc_w;
static usb_temp_setup_by_index_t usb_temp_setup_by_index_w;
static usb_temp_unsetup_t usb_temp_unsetup_w;
static usb_test_quirk_t usb_test_quirk_w;
static usb_test_huawei_autoinst_t usb_test_huawei_autoinst_w;
static usb_quirk_ioctl_t usb_quirk_ioctl_w;
/* global variables */
@ -65,7 +64,6 @@ usb_handle_req_t *usb_temp_get_desc_p = &usb_temp_get_desc_w;
usb_temp_setup_by_index_t *usb_temp_setup_by_index_p = &usb_temp_setup_by_index_w;
usb_temp_unsetup_t *usb_temp_unsetup_p = &usb_temp_unsetup_w;
usb_test_quirk_t *usb_test_quirk_p = &usb_test_quirk_w;
usb_test_huawei_autoinst_t *usb_test_huawei_autoinst_p = &usb_test_huawei_autoinst_w;
usb_quirk_ioctl_t *usb_quirk_ioctl_p = &usb_quirk_ioctl_w;
devclass_t usb_devclass_ptr = NULL;
@ -105,13 +103,6 @@ usb_temp_unsetup_w(struct usb_device *udev)
}
}
static usb_error_t
usb_test_huawei_autoinst_w(struct usb_device *udev,
struct usb_attach_arg *uaa)
{
return (USB_ERR_INVAL);
}
void
usb_quirk_unload(void *arg)
{
@ -156,17 +147,3 @@ usb_bus_unload(void *arg)
pause("WAIT", hz);
}
void
usb_test_huawei_unload(void *arg)
{
/* reset function pointers */
usb_test_huawei_autoinst_p = &usb_test_huawei_autoinst_w;
/* wait for CPU to exit the loaded functions, if any */
/* XXX this is a tradeoff */
pause("WAIT", 16*hz);
}

View File

@ -37,8 +37,6 @@ struct usb_device_request;
typedef usb_error_t (usb_temp_setup_by_index_t)(struct usb_device *udev,
uint16_t index);
typedef usb_error_t (usb_test_huawei_autoinst_t)(struct usb_device *udev,
struct usb_attach_arg *uaa);
typedef uint8_t (usb_test_quirk_t)(const struct usbd_lookup_info *info,
uint16_t quirk);
typedef int (usb_quirk_ioctl_t)(unsigned long cmd, caddr_t data,
@ -51,13 +49,11 @@ extern usb_handle_req_t *usb_temp_get_desc_p;
extern usb_temp_setup_by_index_t *usb_temp_setup_by_index_p;
extern usb_temp_unsetup_t *usb_temp_unsetup_p;
extern usb_test_quirk_t *usb_test_quirk_p;
extern usb_test_huawei_autoinst_t *usb_test_huawei_autoinst_p;
extern usb_quirk_ioctl_t *usb_quirk_ioctl_p;
extern devclass_t usb_devclass_ptr;
/* function prototypes */
void usb_test_huawei_unload(void *);
void usb_temp_unload(void *);
void usb_quirk_unload(void *);
void usb_bus_unload(void *);

View File

@ -29,6 +29,7 @@
struct usb_fifo;
struct usb_xfer;
struct usb_device;
struct usb_attach_arg;
struct usb_interface;
struct usb_endpoint;
struct usb_page_cache;
@ -98,6 +99,13 @@ typedef int (usb_fifo_ioctl_t)(struct usb_fifo *fifo, u_long cmd, void *addr, in
typedef void (usb_fifo_cmd_t)(struct usb_fifo *fifo);
typedef void (usb_fifo_filter_t)(struct usb_fifo *fifo, struct usb_mbuf *m);
/* USB events */
#include <sys/eventhandler.h>
typedef void (*usb_dev_configured_t)(void *, struct usb_device *,
struct usb_attach_arg *);
EVENTHANDLER_DECLARE(usb_dev_configured, usb_dev_configured_t);
/*
* The following macros are used used to convert milliseconds into
* HZ. We use 1024 instead of 1000 milliseconds per second to save a
@ -338,6 +346,10 @@ struct usb_attach_arg {
enum usb_hc_mode usb_mode; /* host or device mode */
uint8_t port;
uint8_t use_generic; /* hint for generic drivers */
uint8_t dev_state;
#define UAA_DEV_READY 0
#define UAA_DEV_DISABLED 1
#define UAA_DEV_EJECTING 2
};
/*