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:
parent
2b54315009
commit
2a4c6157ca
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=200653
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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 *);
|
||||
|
@ -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
|
||||
};
|
||||
|
||||
/*
|
||||
|
Loading…
Reference in New Issue
Block a user