diff --git a/sys/dev/usb/serial/u3g.c b/sys/dev/usb/serial/u3g.c index 22e4e8f6b2bb..d7d8fd80b42a 100644 --- a/sys/dev/usb/serial/u3g.c +++ b/sys/dev/usb/serial/u3g.c @@ -1,1287 +1,1289 @@ /* * Copyright (c) 2008 AnyWi Technologies * Author: Andrea Guzzo * * based on uark.c 1.1 2006/08/14 08:30:22 jsg * * * parts from ubsa.c 183348 2008-09-25 12:00:56Z phk * * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $FreeBSD$ */ /* * NOTE: * * - The detour through the tty layer is ridiculously expensive wrt * buffering due to the high speeds. * * We should consider adding a simple r/w device which allows * attaching of PPP in a more efficient way. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "usbdevs.h" #define USB_DEBUG_VAR u3g_debug #include #include #include #include #include #ifdef USB_DEBUG static int u3g_debug = 0; static SYSCTL_NODE(_hw_usb, OID_AUTO, u3g, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, "USB 3g"); SYSCTL_INT(_hw_usb_u3g, OID_AUTO, debug, CTLFLAG_RWTUN, &u3g_debug, 0, "Debug level"); #endif #define U3G_MAXPORTS 12 #define U3G_CONFIG_INDEX 0 #define U3G_BSIZE 2048 #define U3G_TXSIZE (U3G_BSIZE / U3G_TXFRAMES) #define U3G_TXFRAMES 4 /* Eject methods; See also usb_quirks.h:UQ_MSC_EJECT_* */ #define U3GINIT_HUAWEI 1 /* Requires Huawei init command */ #define U3GINIT_SIERRA 2 /* Requires Sierra init command */ #define U3GINIT_SCSIEJECT 3 /* Requires SCSI eject command */ #define U3GINIT_REZERO 4 /* Requires SCSI rezero command */ #define U3GINIT_ZTESTOR 5 /* Requires ZTE SCSI command */ #define U3GINIT_CMOTECH 6 /* Requires CMOTECH SCSI command */ #define U3GINIT_WAIT 7 /* Device reappears after a delay */ #define U3GINIT_SAEL_M460 8 /* Requires vendor init */ #define U3GINIT_HUAWEISCSI 9 /* Requires Huawei SCSI init command */ #define U3GINIT_HUAWEISCSI2 10 /* Requires Huawei SCSI init command (2) */ #define U3GINIT_TCT 11 /* Requires TCT Mobile init command */ enum { U3G_BULK_WR, U3G_BULK_RD, U3G_INTR, U3G_N_TRANSFER, }; struct u3g_softc { struct ucom_super_softc sc_super_ucom; struct ucom_softc sc_ucom[U3G_MAXPORTS]; struct usb_xfer *sc_xfer[U3G_MAXPORTS][U3G_N_TRANSFER]; uint8_t sc_iface[U3G_MAXPORTS]; /* local status register */ uint8_t sc_lsr[U3G_MAXPORTS]; /* local status register */ uint8_t sc_msr[U3G_MAXPORTS]; /* u3g status register */ uint16_t sc_line[U3G_MAXPORTS]; /* line status */ struct usb_device *sc_udev; struct mtx sc_mtx; uint8_t sc_numports; }; static device_probe_t u3g_probe; static device_attach_t u3g_attach; static device_detach_t u3g_detach; static void u3g_free_softc(struct u3g_softc *); static usb_callback_t u3g_write_callback; static usb_callback_t u3g_read_callback; static usb_callback_t u3g_intr_callback; static void u3g_cfg_get_status(struct ucom_softc *, uint8_t *, uint8_t *); static void u3g_cfg_set_dtr(struct ucom_softc *, uint8_t); static void u3g_cfg_set_rts(struct ucom_softc *, uint8_t); static void u3g_start_read(struct ucom_softc *ucom); 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_poll(struct ucom_softc *ucom); static void u3g_free(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] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, .bufsize = U3G_BSIZE,/* bytes */ .frames = U3G_TXFRAMES, .flags = {.pipe_bof = 1,.force_short_xfer = 1,}, .callback = &u3g_write_callback, }, [U3G_BULK_RD] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, .bufsize = U3G_BSIZE,/* bytes */ .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, .callback = &u3g_read_callback, }, [U3G_INTR] = { .type = UE_INTERRUPT, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, .flags = {.pipe_bof = 1,.short_xfer_ok = 1,.no_pipe_ok = 1,}, .bufsize = 0, /* use wMaxPacketSize */ .callback = &u3g_intr_callback, }, }; static const struct ucom_callback u3g_callback = { .ucom_cfg_get_status = &u3g_cfg_get_status, .ucom_cfg_set_dtr = &u3g_cfg_set_dtr, .ucom_cfg_set_rts = &u3g_cfg_set_rts, .ucom_start_read = &u3g_start_read, .ucom_stop_read = &u3g_stop_read, .ucom_start_write = &u3g_start_write, .ucom_stop_write = &u3g_stop_write, .ucom_poll = &u3g_poll, .ucom_free = &u3g_free, }; static device_method_t u3g_methods[] = { DEVMETHOD(device_probe, u3g_probe), DEVMETHOD(device_attach, u3g_attach), DEVMETHOD(device_detach, u3g_detach), DEVMETHOD_END }; static devclass_t u3g_devclass; static driver_t u3g_driver = { .name = "u3g", .methods = u3g_methods, .size = sizeof(struct u3g_softc), }; static const STRUCT_USB_HOST_ID u3g_devs[] = { #define U3G_DEV(v,p,i) { USB_VPI(USB_VENDOR_##v, USB_PRODUCT_##v##_##p, i) } U3G_DEV(ABIT, AK_020, 0), U3G_DEV(ACERP, H10, 0), U3G_DEV(AIRPLUS, MCD650, 0), U3G_DEV(AIRPRIME, PC5220, 0), U3G_DEV(AIRPRIME, AC313U, 0), U3G_DEV(ALINK, 3G, 0), U3G_DEV(ALINK, 3GU, 0), U3G_DEV(ALINK, DWM652U5, 0), U3G_DEV(ALINK, SIM7600E, 0), U3G_DEV(AMOI, H01, 0), U3G_DEV(AMOI, H01A, 0), U3G_DEV(AMOI, H02, 0), U3G_DEV(ANYDATA, ADU_500A, 0), U3G_DEV(ANYDATA, ADU_620UW, 0), U3G_DEV(ANYDATA, ADU_E100X, 0), U3G_DEV(AXESSTEL, DATAMODEM, 0), U3G_DEV(CMOTECH, CDMA_MODEM1, 0), U3G_DEV(CMOTECH, CGU628, U3GINIT_CMOTECH), U3G_DEV(DELL, U5500, 0), U3G_DEV(DELL, U5505, 0), U3G_DEV(DELL, U5510, 0), U3G_DEV(DELL, U5520, 0), U3G_DEV(DELL, U5520_2, 0), U3G_DEV(DELL, U5520_3, 0), U3G_DEV(DELL, U5700, 0), U3G_DEV(DELL, U5700_2, 0), U3G_DEV(DELL, U5700_3, 0), U3G_DEV(DELL, U5700_4, 0), U3G_DEV(DELL, U5720, 0), U3G_DEV(DELL, U5720_2, 0), U3G_DEV(DELL, U5730, 0), U3G_DEV(DELL, U5730_2, 0), U3G_DEV(DELL, U5730_3, 0), U3G_DEV(DELL, U740, 0), U3G_DEV(DLINK, DWR510_CD, U3GINIT_SCSIEJECT), U3G_DEV(DLINK, DWR510, 0), U3G_DEV(DLINK, DWM157_CD, U3GINIT_SCSIEJECT), U3G_DEV(DLINK, DWM157, 0), U3G_DEV(DLINK, DWM157_CD_2, U3GINIT_SCSIEJECT), U3G_DEV(DLINK, DWM157_2, 0), U3G_DEV(DLINK, DWM222_CD, U3GINIT_SCSIEJECT), U3G_DEV(DLINK, DWM222, 0), U3G_DEV(DLINK3, DWM652, 0), U3G_DEV(HP, EV2200, 0), U3G_DEV(HP, HS2300, 0), U3G_DEV(HP, UN2420_QDL, 0), U3G_DEV(HP, UN2420, 0), U3G_DEV(HP, LT4132, U3GINIT_HUAWEISCSI2), U3G_DEV(HUAWEI, E1401, U3GINIT_HUAWEI), U3G_DEV(HUAWEI, E1402, U3GINIT_HUAWEI), U3G_DEV(HUAWEI, E1403, U3GINIT_HUAWEI), U3G_DEV(HUAWEI, E1404, U3GINIT_HUAWEI), U3G_DEV(HUAWEI, E1405, U3GINIT_HUAWEI), U3G_DEV(HUAWEI, E1406, U3GINIT_HUAWEI), U3G_DEV(HUAWEI, E1407, U3GINIT_HUAWEI), U3G_DEV(HUAWEI, E1408, U3GINIT_HUAWEI), U3G_DEV(HUAWEI, E1409, U3GINIT_HUAWEI), U3G_DEV(HUAWEI, E140A, U3GINIT_HUAWEI), U3G_DEV(HUAWEI, E140B, U3GINIT_HUAWEI), U3G_DEV(HUAWEI, E140D, U3GINIT_HUAWEI), U3G_DEV(HUAWEI, E140E, U3GINIT_HUAWEI), U3G_DEV(HUAWEI, E140F, U3GINIT_HUAWEI), U3G_DEV(HUAWEI, E1410, U3GINIT_HUAWEI), U3G_DEV(HUAWEI, E1411, U3GINIT_HUAWEI), U3G_DEV(HUAWEI, E1412, U3GINIT_HUAWEI), U3G_DEV(HUAWEI, E1413, U3GINIT_HUAWEI), U3G_DEV(HUAWEI, E1414, U3GINIT_HUAWEI), U3G_DEV(HUAWEI, E1415, U3GINIT_HUAWEI), U3G_DEV(HUAWEI, E1416, U3GINIT_HUAWEI), U3G_DEV(HUAWEI, E1417, U3GINIT_HUAWEI), U3G_DEV(HUAWEI, E1418, U3GINIT_HUAWEI), U3G_DEV(HUAWEI, E1419, U3GINIT_HUAWEI), U3G_DEV(HUAWEI, E141A, U3GINIT_HUAWEI), U3G_DEV(HUAWEI, E141B, U3GINIT_HUAWEI), U3G_DEV(HUAWEI, E141C, U3GINIT_HUAWEI), U3G_DEV(HUAWEI, E141D, U3GINIT_HUAWEI), U3G_DEV(HUAWEI, E141E, U3GINIT_HUAWEI), U3G_DEV(HUAWEI, E141F, U3GINIT_HUAWEI), U3G_DEV(HUAWEI, E1420, U3GINIT_HUAWEI), U3G_DEV(HUAWEI, E1421, U3GINIT_HUAWEI), U3G_DEV(HUAWEI, E1422, U3GINIT_HUAWEI), U3G_DEV(HUAWEI, E1423, U3GINIT_HUAWEI), U3G_DEV(HUAWEI, E1424, U3GINIT_HUAWEI), U3G_DEV(HUAWEI, E1425, U3GINIT_HUAWEI), U3G_DEV(HUAWEI, E1426, U3GINIT_HUAWEI), U3G_DEV(HUAWEI, E1427, U3GINIT_HUAWEI), U3G_DEV(HUAWEI, E1428, U3GINIT_HUAWEI), U3G_DEV(HUAWEI, E1429, U3GINIT_HUAWEI), U3G_DEV(HUAWEI, E142A, U3GINIT_HUAWEI), U3G_DEV(HUAWEI, E142B, U3GINIT_HUAWEI), U3G_DEV(HUAWEI, E142C, U3GINIT_HUAWEI), U3G_DEV(HUAWEI, E142D, U3GINIT_HUAWEI), U3G_DEV(HUAWEI, E142E, U3GINIT_HUAWEI), U3G_DEV(HUAWEI, E142F, U3GINIT_HUAWEI), U3G_DEV(HUAWEI, E1430, U3GINIT_HUAWEI), U3G_DEV(HUAWEI, E1431, U3GINIT_HUAWEI), U3G_DEV(HUAWEI, E1432, U3GINIT_HUAWEI), U3G_DEV(HUAWEI, E1433, U3GINIT_HUAWEI), U3G_DEV(HUAWEI, E1434, U3GINIT_HUAWEI), U3G_DEV(HUAWEI, E1435, U3GINIT_HUAWEI), U3G_DEV(HUAWEI, E1436, U3GINIT_HUAWEI), U3G_DEV(HUAWEI, E1437, U3GINIT_HUAWEI), U3G_DEV(HUAWEI, E1438, U3GINIT_HUAWEI), U3G_DEV(HUAWEI, E1439, U3GINIT_HUAWEI), U3G_DEV(HUAWEI, E143A, U3GINIT_HUAWEI), U3G_DEV(HUAWEI, E143B, U3GINIT_HUAWEI), U3G_DEV(HUAWEI, E143C, U3GINIT_HUAWEI), U3G_DEV(HUAWEI, E143D, U3GINIT_HUAWEI), U3G_DEV(HUAWEI, E143E, U3GINIT_HUAWEI), U3G_DEV(HUAWEI, E143F, U3GINIT_HUAWEI), U3G_DEV(HUAWEI, E173, 0), U3G_DEV(HUAWEI, E173_INIT, U3GINIT_HUAWEISCSI), U3G_DEV(HUAWEI, E3131, 0), U3G_DEV(HUAWEI, E3131_INIT, U3GINIT_HUAWEISCSI2), U3G_DEV(HUAWEI, E180V, U3GINIT_HUAWEI), U3G_DEV(HUAWEI, E220, U3GINIT_HUAWEI), U3G_DEV(HUAWEI, E220BIS, U3GINIT_HUAWEI), U3G_DEV(HUAWEI, E392, U3GINIT_HUAWEISCSI), U3G_DEV(HUAWEI, ME909U, U3GINIT_HUAWEISCSI2), U3G_DEV(HUAWEI, ME909S, U3GINIT_HUAWEISCSI2), U3G_DEV(HUAWEI, MOBILE, U3GINIT_HUAWEI), U3G_DEV(HUAWEI, E1752, U3GINIT_HUAWEISCSI), U3G_DEV(HUAWEI, E1820, U3GINIT_HUAWEISCSI), U3G_DEV(HUAWEI, K3771, U3GINIT_HUAWEI), U3G_DEV(HUAWEI, K3771_INIT, U3GINIT_HUAWEISCSI2), U3G_DEV(HUAWEI, K3772, U3GINIT_HUAWEI), U3G_DEV(HUAWEI, K3772_INIT, U3GINIT_HUAWEISCSI2), U3G_DEV(HUAWEI, K3765, U3GINIT_HUAWEI), U3G_DEV(HUAWEI, K3765_INIT, U3GINIT_HUAWEISCSI), U3G_DEV(HUAWEI, K3770, U3GINIT_HUAWEI), U3G_DEV(HUAWEI, K3770_INIT, U3GINIT_HUAWEISCSI), U3G_DEV(HUAWEI, K4505, U3GINIT_HUAWEI), U3G_DEV(HUAWEI, K4505_INIT, U3GINIT_HUAWEISCSI), U3G_DEV(HUAWEI, ETS2055, U3GINIT_HUAWEI), U3G_DEV(HUAWEI, E3272_INIT, U3GINIT_HUAWEISCSI2), U3G_DEV(HUAWEI, E3272, 0), U3G_DEV(KYOCERA2, CDMA_MSM_K, 0), U3G_DEV(KYOCERA2, KPC680, 0), U3G_DEV(LONGCHEER, WM66, U3GINIT_HUAWEI), U3G_DEV(LONGCHEER, DISK, U3GINIT_TCT), U3G_DEV(LONGCHEER, W14, 0), U3G_DEV(LONGCHEER, XSSTICK, 0), U3G_DEV(MERLIN, V620, 0), U3G_DEV(NEOTEL, PRIME, 0), U3G_DEV(NOVATEL, E725, 0), U3G_DEV(NOVATEL, ES620, 0), U3G_DEV(NOVATEL, ES620_2, 0), U3G_DEV(NOVATEL, EU730, 0), U3G_DEV(NOVATEL, EU740, 0), U3G_DEV(NOVATEL, EU870D, 0), U3G_DEV(NOVATEL, MC760, 0), U3G_DEV(NOVATEL, MC547, 0), U3G_DEV(NOVATEL, MC679, 0), U3G_DEV(NOVATEL, MC950D, 0), U3G_DEV(NOVATEL, MC990D, 0), U3G_DEV(NOVATEL, MIFI2200, U3GINIT_SCSIEJECT), U3G_DEV(NOVATEL, MIFI2200V, U3GINIT_SCSIEJECT), U3G_DEV(NOVATEL, U720, 0), U3G_DEV(NOVATEL, U727, 0), U3G_DEV(NOVATEL, U727_2, 0), U3G_DEV(NOVATEL, U740, 0), U3G_DEV(NOVATEL, U740_2, 0), U3G_DEV(NOVATEL, U760, U3GINIT_SCSIEJECT), U3G_DEV(NOVATEL, U870, 0), U3G_DEV(NOVATEL, V620, 0), U3G_DEV(NOVATEL, V640, 0), U3G_DEV(NOVATEL, V720, 0), U3G_DEV(NOVATEL, V740, 0), U3G_DEV(NOVATEL, X950D, 0), U3G_DEV(NOVATEL, XU870, 0), U3G_DEV(MOTOROLA2, MB886, U3GINIT_SCSIEJECT), U3G_DEV(OPTION, E6500, 0), U3G_DEV(OPTION, E6501, 0), U3G_DEV(OPTION, E6601, 0), U3G_DEV(OPTION, E6721, 0), U3G_DEV(OPTION, E6741, 0), U3G_DEV(OPTION, E6761, 0), U3G_DEV(OPTION, E6800, 0), U3G_DEV(OPTION, E7021, 0), U3G_DEV(OPTION, E7041, 0), U3G_DEV(OPTION, E7061, 0), U3G_DEV(OPTION, E7100, 0), U3G_DEV(OPTION, GE40X, 0), U3G_DEV(OPTION, GT3G, 0), U3G_DEV(OPTION, GT3GPLUS, 0), U3G_DEV(OPTION, GT3GQUAD, 0), U3G_DEV(OPTION, GT3G_1, 0), U3G_DEV(OPTION, GT3G_2, 0), U3G_DEV(OPTION, GT3G_3, 0), U3G_DEV(OPTION, GT3G_4, 0), U3G_DEV(OPTION, GT3G_5, 0), U3G_DEV(OPTION, GT3G_6, 0), U3G_DEV(OPTION, GTHSDPA, 0), U3G_DEV(OPTION, GTM380, 0), U3G_DEV(OPTION, GTMAX36, 0), U3G_DEV(OPTION, GTMAX380HSUPAE, 0), U3G_DEV(OPTION, GTMAXHSUPA, 0), U3G_DEV(OPTION, GTMAXHSUPAE, 0), U3G_DEV(OPTION, VODAFONEMC3G, 0), U3G_DEV(PANASONIC, CFF9_3G_QDL, 0), U3G_DEV(PANASONIC, CFF9_3G, 0), U3G_DEV(QISDA, H20_1, 0), U3G_DEV(QISDA, H20_2, 0), U3G_DEV(QISDA, H21_1, 0), U3G_DEV(QISDA, H21_2, 0), U3G_DEV(QUALCOMM, NTT_L02C_MODEM, U3GINIT_SCSIEJECT), U3G_DEV(QUALCOMM2, AC8700, 0), U3G_DEV(QUALCOMM2, MF330, 0), U3G_DEV(QUALCOMM2, SIM5218, 0), U3G_DEV(QUALCOMM2, WM620, 0), U3G_DEV(QUALCOMM2, VW110L, U3GINIT_SCSIEJECT), U3G_DEV(QUALCOMM2, GOBI2000_QDL, 0), U3G_DEV(QUALCOMM2, GOBI2000, 0), U3G_DEV(QUALCOMM2, VT80N, 0), U3G_DEV(QUALCOMM3, VFAST2, 0), U3G_DEV(QUALCOMMINC, AC2726, 0), U3G_DEV(QUALCOMMINC, AC682_INIT, U3GINIT_SCSIEJECT), U3G_DEV(QUALCOMMINC, AC682, 0), U3G_DEV(QUALCOMMINC, AC8700, 0), U3G_DEV(QUALCOMMINC, AC8710, 0), U3G_DEV(QUALCOMMINC, CDMA_MSM, U3GINIT_SCSIEJECT), U3G_DEV(QUALCOMMINC, E0002, 0), U3G_DEV(QUALCOMMINC, E0003, 0), U3G_DEV(QUALCOMMINC, E0004, 0), U3G_DEV(QUALCOMMINC, E0005, 0), U3G_DEV(QUALCOMMINC, E0006, 0), U3G_DEV(QUALCOMMINC, E0007, 0), U3G_DEV(QUALCOMMINC, E0008, 0), U3G_DEV(QUALCOMMINC, E0009, 0), U3G_DEV(QUALCOMMINC, E000A, 0), U3G_DEV(QUALCOMMINC, E000B, 0), U3G_DEV(QUALCOMMINC, E000C, 0), U3G_DEV(QUALCOMMINC, E000D, 0), U3G_DEV(QUALCOMMINC, E000E, 0), U3G_DEV(QUALCOMMINC, E000F, 0), U3G_DEV(QUALCOMMINC, E0010, 0), U3G_DEV(QUALCOMMINC, E0011, 0), U3G_DEV(QUALCOMMINC, E0012, 0), U3G_DEV(QUALCOMMINC, E0013, 0), U3G_DEV(QUALCOMMINC, E0014, 0), U3G_DEV(QUALCOMMINC, E0017, 0), U3G_DEV(QUALCOMMINC, E0018, 0), U3G_DEV(QUALCOMMINC, E0019, 0), U3G_DEV(QUALCOMMINC, E0020, 0), U3G_DEV(QUALCOMMINC, E0021, 0), U3G_DEV(QUALCOMMINC, E0022, 0), U3G_DEV(QUALCOMMINC, E0023, 0), U3G_DEV(QUALCOMMINC, E0024, 0), U3G_DEV(QUALCOMMINC, E0025, 0), U3G_DEV(QUALCOMMINC, E0026, 0), U3G_DEV(QUALCOMMINC, E0027, 0), U3G_DEV(QUALCOMMINC, E0028, 0), U3G_DEV(QUALCOMMINC, E0029, 0), U3G_DEV(QUALCOMMINC, E0030, 0), U3G_DEV(QUALCOMMINC, E0032, 0), U3G_DEV(QUALCOMMINC, E0033, 0), U3G_DEV(QUALCOMMINC, E0037, 0), U3G_DEV(QUALCOMMINC, E0039, 0), U3G_DEV(QUALCOMMINC, E0042, 0), U3G_DEV(QUALCOMMINC, E0043, 0), U3G_DEV(QUALCOMMINC, E0048, 0), U3G_DEV(QUALCOMMINC, E0049, 0), U3G_DEV(QUALCOMMINC, E0051, 0), U3G_DEV(QUALCOMMINC, E0052, 0), U3G_DEV(QUALCOMMINC, E0054, 0), U3G_DEV(QUALCOMMINC, E0055, 0), U3G_DEV(QUALCOMMINC, E0057, 0), U3G_DEV(QUALCOMMINC, E0058, 0), U3G_DEV(QUALCOMMINC, E0059, 0), U3G_DEV(QUALCOMMINC, E0060, 0), U3G_DEV(QUALCOMMINC, E0061, 0), U3G_DEV(QUALCOMMINC, E0062, 0), U3G_DEV(QUALCOMMINC, E0063, 0), U3G_DEV(QUALCOMMINC, E0064, 0), U3G_DEV(QUALCOMMINC, E0066, 0), U3G_DEV(QUALCOMMINC, E0069, 0), U3G_DEV(QUALCOMMINC, E0070, 0), U3G_DEV(QUALCOMMINC, E0073, 0), U3G_DEV(QUALCOMMINC, E0076, 0), U3G_DEV(QUALCOMMINC, E0078, 0), U3G_DEV(QUALCOMMINC, E0082, 0), U3G_DEV(QUALCOMMINC, E0086, 0), U3G_DEV(QUALCOMMINC, SURFSTICK, 0), U3G_DEV(QUALCOMMINC, E2002, 0), U3G_DEV(QUALCOMMINC, E2003, 0), U3G_DEV(QUALCOMMINC, K3772_Z, 0), U3G_DEV(QUALCOMMINC, K3772_Z_INIT, U3GINIT_SCSIEJECT), U3G_DEV(QUALCOMMINC, MF112, U3GINIT_ZTESTOR), U3G_DEV(QUALCOMMINC, MF195E, 0), U3G_DEV(QUALCOMMINC, MF195E_INIT, U3GINIT_SCSIEJECT), U3G_DEV(QUALCOMMINC, MF626, 0), U3G_DEV(QUALCOMMINC, MF628, 0), U3G_DEV(QUALCOMMINC, MF633R, 0), /* the following is a RNDIS device, no modem features */ U3G_DEV(QUALCOMMINC, ZTE_MF730M, U3GINIT_SCSIEJECT), U3G_DEV(QUANTA, GKE, 0), U3G_DEV(QUANTA, GLE, 0), U3G_DEV(QUANTA, GLX, 0), U3G_DEV(QUANTA, Q101, 0), U3G_DEV(QUANTA, Q111, 0), U3G_DEV(QUECTEL, EC25, 0), U3G_DEV(SIERRA, AC402, 0), U3G_DEV(SIERRA, AC595U, 0), U3G_DEV(SIERRA, AC313U, 0), U3G_DEV(SIERRA, AC597E, 0), U3G_DEV(SIERRA, AC875, 0), U3G_DEV(SIERRA, AC875E, 0), U3G_DEV(SIERRA, AC875U, 0), U3G_DEV(SIERRA, AC875U_2, 0), U3G_DEV(SIERRA, AC880, 0), U3G_DEV(SIERRA, AC880E, 0), U3G_DEV(SIERRA, AC880U, 0), U3G_DEV(SIERRA, AC881, 0), U3G_DEV(SIERRA, AC881E, 0), U3G_DEV(SIERRA, AC881U, 0), U3G_DEV(SIERRA, AC885E, 0), U3G_DEV(SIERRA, AC885E_2, 0), U3G_DEV(SIERRA, AC885U, 0), U3G_DEV(SIERRA, AIRCARD580, 0), U3G_DEV(SIERRA, AIRCARD595, 0), U3G_DEV(SIERRA, C22, 0), U3G_DEV(SIERRA, C597, 0), U3G_DEV(SIERRA, C888, 0), U3G_DEV(SIERRA, E0029, 0), U3G_DEV(SIERRA, E6892, 0), U3G_DEV(SIERRA, E6893, 0), U3G_DEV(SIERRA, EM5625, 0), U3G_DEV(SIERRA, EM5725, 0), U3G_DEV(SIERRA, MC5720, 0), U3G_DEV(SIERRA, MC5720_2, 0), U3G_DEV(SIERRA, MC5725, 0), U3G_DEV(SIERRA, MC5727, 0), U3G_DEV(SIERRA, MC5727_2, 0), U3G_DEV(SIERRA, MC5728, 0), U3G_DEV(SIERRA, MC7354, 0), U3G_DEV(SIERRA, MC7355, 0), U3G_DEV(SIERRA, MC7430, 0), U3G_DEV(SIERRA, MC8700, 0), U3G_DEV(SIERRA, MC8755, 0), U3G_DEV(SIERRA, MC8755_2, 0), U3G_DEV(SIERRA, MC8755_3, 0), U3G_DEV(SIERRA, MC8755_4, 0), U3G_DEV(SIERRA, MC8765, 0), U3G_DEV(SIERRA, MC8765_2, 0), U3G_DEV(SIERRA, MC8765_3, 0), U3G_DEV(SIERRA, MC8775, 0), U3G_DEV(SIERRA, MC8775_2, 0), U3G_DEV(SIERRA, MC8780, 0), U3G_DEV(SIERRA, MC8780_2, 0), U3G_DEV(SIERRA, MC8780_3, 0), U3G_DEV(SIERRA, MC8781, 0), U3G_DEV(SIERRA, MC8781_2, 0), U3G_DEV(SIERRA, MC8781_3, 0), U3G_DEV(SIERRA, MC8785, 0), U3G_DEV(SIERRA, MC8785_2, 0), U3G_DEV(SIERRA, MC8790, 0), U3G_DEV(SIERRA, MC8791, 0), U3G_DEV(SIERRA, MC8792, 0), U3G_DEV(SIERRA, MINI5725, 0), U3G_DEV(SIERRA, T11, 0), U3G_DEV(SIERRA, T598, 0), U3G_DEV(SILABS, SAEL, U3GINIT_SAEL_M460), U3G_DEV(STELERA, C105, 0), U3G_DEV(STELERA, E1003, 0), U3G_DEV(STELERA, E1004, 0), U3G_DEV(STELERA, E1005, 0), U3G_DEV(STELERA, E1006, 0), U3G_DEV(STELERA, E1007, 0), U3G_DEV(STELERA, E1008, 0), U3G_DEV(STELERA, E1009, 0), U3G_DEV(STELERA, E100A, 0), U3G_DEV(STELERA, E100B, 0), U3G_DEV(STELERA, E100C, 0), U3G_DEV(STELERA, E100D, 0), U3G_DEV(STELERA, E100E, 0), U3G_DEV(STELERA, E100F, 0), U3G_DEV(STELERA, E1010, 0), U3G_DEV(STELERA, E1011, 0), U3G_DEV(STELERA, E1012, 0), U3G_DEV(TCTMOBILE, X060S, 0), U3G_DEV(TCTMOBILE, X080S, U3GINIT_TCT), U3G_DEV(TELIT, UC864E, 0), U3G_DEV(TELIT, UC864G, 0), U3G_DEV(TLAYTECH, TEU800, 0), U3G_DEV(TOSHIBA, G450, 0), U3G_DEV(TOSHIBA, HSDPA, 0), U3G_DEV(YISO, C893, 0), U3G_DEV(WETELECOM, WM_D200, 0), /* Autoinstallers */ U3G_DEV(NOVATEL, ZEROCD, U3GINIT_SCSIEJECT), U3G_DEV(OPTION, GTICON322, U3GINIT_REZERO), U3G_DEV(QUALCOMMINC, ZTE_STOR, U3GINIT_ZTESTOR), U3G_DEV(QUALCOMMINC, ZTE_STOR2, U3GINIT_SCSIEJECT), U3G_DEV(QUANTA, Q101_STOR, U3GINIT_SCSIEJECT), U3G_DEV(SIERRA, TRUINSTALL, U3GINIT_SIERRA), #undef U3G_DEV }; DRIVER_MODULE(u3g, uhub, u3g_driver, u3g_devclass, u3g_driver_loaded, 0); MODULE_DEPEND(u3g, ucom, 1, 1, 1); MODULE_DEPEND(u3g, usb, 1, 1, 1); MODULE_VERSION(u3g, 1); USB_PNP_HOST_INFO(u3g_devs); static int u3g_sierra_init(struct usb_device *udev) { struct usb_device_request req; req.bmRequestType = UT_VENDOR; req.bRequest = UR_SET_INTERFACE; USETW(req.wValue, UF_DEVICE_REMOTE_WAKEUP); USETW(req.wIndex, UHF_PORT_CONNECTION); USETW(req.wLength, 0); if (usbd_do_request_flags(udev, NULL, &req, NULL, 0, NULL, USB_MS_HZ)) { /* ignore any errors */ } return (0); } static int u3g_huawei_init(struct usb_device *udev) { struct usb_device_request req; req.bmRequestType = UT_WRITE_DEVICE; req.bRequest = UR_SET_FEATURE; USETW(req.wValue, UF_DEVICE_REMOTE_WAKEUP); USETW(req.wIndex, UHF_PORT_SUSPEND); USETW(req.wLength, 0); if (usbd_do_request_flags(udev, NULL, &req, NULL, 0, NULL, USB_MS_HZ)) { /* ignore any errors */ } return (0); } static int u3g_huawei_is_cdce(uint16_t idVendor, uint8_t bInterfaceSubClass, uint8_t bInterfaceProtocol) { /* * This function returns non-zero if the interface being * probed is of type CDC ethernet, which the U3G driver should * not attach to. See sys/dev/usb/net/if_cdce.c for matching * entries. */ if (idVendor != USB_VENDOR_HUAWEI) goto done; switch (bInterfaceSubClass) { case 0x02: switch (bInterfaceProtocol) { case 0x16: case 0x46: case 0x76: return (1); default: break; } break; case 0x03: switch (bInterfaceProtocol) { case 0x16: return (1); default: break; } break; default: break; } done: return (0); } static void u3g_sael_m460_init(struct usb_device *udev) { static const uint8_t setup[][24] = { { 0x41, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, { 0x41, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00 }, { 0x41, 0x13, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, { 0xc1, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x40, 0x02 }, { 0xc1, 0x08, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00 }, { 0x41, 0x07, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00 }, { 0xc1, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 }, { 0x41, 0x01, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00 }, { 0x41, 0x07, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00 }, { 0x41, 0x03, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00 }, { 0x41, 0x19, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x13 }, { 0x41, 0x13, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x09, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00 }, { 0x41, 0x12, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00 }, { 0x41, 0x01, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00 }, { 0x41, 0x07, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00 }, { 0x41, 0x03, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00 }, { 0x41, 0x19, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x13 }, { 0x41, 0x13, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x09, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00 }, { 0x41, 0x07, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00 }, }; struct usb_device_request req; usb_error_t err; uint16_t len; uint8_t buf[0x300]; uint8_t n; DPRINTFN(1, "\n"); if (usbd_req_set_alt_interface_no(udev, NULL, 0, 0)) { DPRINTFN(0, "Alt setting 0 failed\n"); return; } for (n = 0; n != nitems(setup); n++) { memcpy(&req, setup[n], sizeof(req)); len = UGETW(req.wLength); if (req.bmRequestType & UE_DIR_IN) { if (len > sizeof(buf)) { DPRINTFN(0, "too small buffer\n"); continue; } err = usbd_do_request(udev, NULL, &req, buf); } else { if (len > (sizeof(setup[0]) - 8)) { DPRINTFN(0, "too small buffer\n"); continue; } err = usbd_do_request(udev, NULL, &req, __DECONST(uint8_t *, &setup[n][8])); } if (err) { DPRINTFN(1, "request %u failed\n", (unsigned int)n); /* * Some of the requests will fail. Stop doing * requests when we are getting timeouts so * that we don't block the explore/attach * thread forever. */ if (err == USB_ERR_TIMEOUT) break; } } } /* * 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 void u3g_test_autoinst(void *arg, struct usb_device *udev, struct usb_attach_arg *uaa) { struct usb_interface *iface; struct usb_interface_descriptor *id; int error; unsigned long method; if (uaa->dev_state != UAA_DEV_READY) return; iface = usbd_get_iface(udev, 0); if (iface == NULL) return; id = iface->idesc; if (id == NULL || id->bInterfaceClass != UICLASS_MASS) return; if (usb_test_quirk(uaa, UQ_MSC_EJECT_HUAWEI)) method = U3GINIT_HUAWEI; else if (usb_test_quirk(uaa, UQ_MSC_EJECT_SIERRA)) method = U3GINIT_SIERRA; else if (usb_test_quirk(uaa, UQ_MSC_EJECT_SCSIEJECT)) method = U3GINIT_SCSIEJECT; else if (usb_test_quirk(uaa, UQ_MSC_EJECT_REZERO)) method = U3GINIT_REZERO; else if (usb_test_quirk(uaa, UQ_MSC_EJECT_ZTESTOR)) method = U3GINIT_ZTESTOR; else if (usb_test_quirk(uaa, UQ_MSC_EJECT_CMOTECH)) method = U3GINIT_CMOTECH; else if (usb_test_quirk(uaa, UQ_MSC_EJECT_WAIT)) method = U3GINIT_WAIT; else if (usb_test_quirk(uaa, UQ_MSC_EJECT_HUAWEISCSI)) method = U3GINIT_HUAWEISCSI; else if (usb_test_quirk(uaa, UQ_MSC_EJECT_HUAWEISCSI2)) method = U3GINIT_HUAWEISCSI2; else if (usb_test_quirk(uaa, UQ_MSC_EJECT_TCT)) method = U3GINIT_TCT; else if (usbd_lookup_id_by_uaa(u3g_devs, sizeof(u3g_devs), uaa) == 0) method = USB_GET_DRIVER_INFO(uaa); else return; /* no device match */ if (bootverbose) { printf("Ejecting %s %s using method %ld\n", usb_get_manufacturer(udev), usb_get_product(udev), method); } switch (method) { case U3GINIT_HUAWEI: error = u3g_huawei_init(udev); break; case U3GINIT_HUAWEISCSI: error = usb_msc_eject(udev, 0, MSC_EJECT_HUAWEI); break; case U3GINIT_HUAWEISCSI2: error = usb_msc_eject(udev, 0, MSC_EJECT_HUAWEI2); break; case U3GINIT_SCSIEJECT: error = usb_msc_eject(udev, 0, MSC_EJECT_STOPUNIT); break; case U3GINIT_REZERO: error = usb_msc_eject(udev, 0, MSC_EJECT_REZERO); break; case U3GINIT_ZTESTOR: error = usb_msc_eject(udev, 0, MSC_EJECT_STOPUNIT); if (error == 0) error = usb_msc_eject(udev, 0, MSC_EJECT_ZTESTOR); break; case U3GINIT_CMOTECH: error = usb_msc_eject(udev, 0, MSC_EJECT_CMOTECH); break; case U3GINIT_TCT: error = usb_msc_eject(udev, 0, MSC_EJECT_TCT); break; case U3GINIT_SIERRA: error = u3g_sierra_init(udev); break; case U3GINIT_WAIT: /* Just pretend we ejected, the card will timeout */ error = 0; break; default: /* no 3G eject quirks */ error = EOPNOTSUPP; break; } if (error == 0) { /* success, mark the udev as disappearing */ uaa->dev_state = UAA_DEV_EJECTING; } } static int u3g_driver_loaded(struct module *mod, int what, void *arg) { switch (what) { case MOD_LOAD: /* register our autoinstall handler */ u3g_etag = EVENTHANDLER_REGISTER(usb_dev_configured, u3g_test_autoinst, NULL, EVENTHANDLER_PRI_ANY); break; case MOD_UNLOAD: EVENTHANDLER_DEREGISTER(usb_dev_configured, u3g_etag); break; default: return (EOPNOTSUPP); } return (0); } static int u3g_probe(device_t self) { struct usb_attach_arg *uaa = device_get_ivars(self); if (uaa->usb_mode != USB_MODE_HOST) { return (ENXIO); } if (uaa->info.bConfigIndex != U3G_CONFIG_INDEX) { return (ENXIO); } if (uaa->info.bInterfaceClass != UICLASS_VENDOR) { return (ENXIO); } if (u3g_huawei_is_cdce(uaa->info.idVendor, uaa->info.bInterfaceSubClass, uaa->info.bInterfaceProtocol)) { return (ENXIO); } return (usbd_lookup_id_by_uaa(u3g_devs, sizeof(u3g_devs), uaa)); } static int u3g_attach(device_t dev) { struct usb_config u3g_config_tmp[U3G_N_TRANSFER]; struct usb_attach_arg *uaa = device_get_ivars(dev); struct u3g_softc *sc = device_get_softc(dev); struct usb_interface *iface; struct usb_interface_descriptor *id; uint32_t iface_valid; int error, type, nports; int ep, n; uint8_t i; DPRINTF("sc=%p\n", sc); type = USB_GET_DRIVER_INFO(uaa); if (type == U3GINIT_SAEL_M460 || usb_test_quirk(uaa, UQ_MSC_EJECT_SAEL_M460)) { u3g_sael_m460_init(uaa->device); } /* copy in USB config */ for (n = 0; n != U3G_N_TRANSFER; n++) u3g_config_tmp[n] = u3g_config[n]; device_set_usb_desc(dev); mtx_init(&sc->sc_mtx, "u3g", NULL, MTX_DEF); ucom_ref(&sc->sc_super_ucom); sc->sc_udev = uaa->device; /* Claim all interfaces on the device */ iface_valid = 0; for (i = uaa->info.bIfaceIndex; i < USB_IFACE_MAX; i++) { iface = usbd_get_iface(uaa->device, i); if (iface == NULL) break; id = usbd_get_interface_descriptor(iface); if (id == NULL || id->bInterfaceClass != UICLASS_VENDOR) continue; if (u3g_huawei_is_cdce(uaa->info.idVendor, id->bInterfaceSubClass, id->bInterfaceProtocol)) continue; usbd_set_parent_iface(uaa->device, i, uaa->info.bIfaceIndex); iface_valid |= (1<device, &i, sc->sc_xfer[nports], u3g_config_tmp, U3G_N_TRANSFER, &sc->sc_ucom[nports], &sc->sc_mtx); if (error) { /* next interface */ i++; ep = 0; continue; } iface = usbd_get_iface(uaa->device, i); id = usbd_get_interface_descriptor(iface); sc->sc_iface[nports] = id->bInterfaceNumber; if (bootverbose && sc->sc_xfer[nports][U3G_INTR]) { device_printf(dev, "port %d supports modem control\n", nports); } /* set stall by default */ mtx_lock(&sc->sc_mtx); - usbd_xfer_set_stall(sc->sc_xfer[nports][U3G_BULK_WR]); - usbd_xfer_set_stall(sc->sc_xfer[nports][U3G_BULK_RD]); + usbd_xfer_set_zlp(sc->sc_xfer[nports][U3G_BULK_WR]); mtx_unlock(&sc->sc_mtx); nports++; /* found one port */ ep++; if (nports == U3G_MAXPORTS) break; } if (nports == 0) { device_printf(dev, "no ports found\n"); goto detach; } sc->sc_numports = nports; error = ucom_attach(&sc->sc_super_ucom, sc->sc_ucom, sc->sc_numports, sc, &u3g_callback, &sc->sc_mtx); if (error) { DPRINTF("ucom_attach failed\n"); goto detach; } ucom_set_pnpinfo_usb(&sc->sc_super_ucom, dev); device_printf(dev, "Found %u port%s.\n", sc->sc_numports, sc->sc_numports > 1 ? "s":""); return (0); detach: u3g_detach(dev); return (ENXIO); } static int u3g_detach(device_t dev) { struct u3g_softc *sc = device_get_softc(dev); uint8_t subunit; DPRINTF("sc=%p\n", sc); /* NOTE: It is not dangerous to detach more ports than attached! */ ucom_detach(&sc->sc_super_ucom, sc->sc_ucom); for (subunit = 0; subunit != U3G_MAXPORTS; subunit++) usbd_transfer_unsetup(sc->sc_xfer[subunit], U3G_N_TRANSFER); device_claim_softc(dev); u3g_free_softc(sc); return (0); } UCOM_UNLOAD_DRAIN(u3g); static void u3g_free_softc(struct u3g_softc *sc) { if (ucom_unref(&sc->sc_super_ucom)) { mtx_destroy(&sc->sc_mtx); device_free_softc(sc); } } static void u3g_free(struct ucom_softc *ucom) { u3g_free_softc(ucom->sc_parent); } static void u3g_start_read(struct ucom_softc *ucom) { struct u3g_softc *sc = ucom->sc_parent; /* start interrupt endpoint (if configured) */ usbd_transfer_start(sc->sc_xfer[ucom->sc_subunit][U3G_INTR]); /* start read endpoint */ usbd_transfer_start(sc->sc_xfer[ucom->sc_subunit][U3G_BULK_RD]); } static void u3g_stop_read(struct ucom_softc *ucom) { struct u3g_softc *sc = ucom->sc_parent; /* stop interrupt endpoint (if configured) */ usbd_transfer_stop(sc->sc_xfer[ucom->sc_subunit][U3G_INTR]); /* stop read endpoint */ usbd_transfer_stop(sc->sc_xfer[ucom->sc_subunit][U3G_BULK_RD]); } static void u3g_start_write(struct ucom_softc *ucom) { struct u3g_softc *sc = ucom->sc_parent; usbd_transfer_start(sc->sc_xfer[ucom->sc_subunit][U3G_BULK_WR]); } static void u3g_stop_write(struct ucom_softc *ucom) { struct u3g_softc *sc = ucom->sc_parent; usbd_transfer_stop(sc->sc_xfer[ucom->sc_subunit][U3G_BULK_WR]); } static void u3g_write_callback(struct usb_xfer *xfer, usb_error_t error) { struct ucom_softc *ucom = usbd_xfer_softc(xfer); struct usb_page_cache *pc; uint32_t actlen; uint32_t frame; switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: case USB_ST_SETUP: tr_setup: + if (usbd_xfer_get_and_clr_zlp(xfer)) + break; + for (frame = 0; frame != U3G_TXFRAMES; frame++) { usbd_xfer_set_frame_offset(xfer, frame * U3G_TXSIZE, frame); pc = usbd_xfer_get_frame(xfer, frame); if (ucom_get_data(ucom, pc, 0, U3G_TXSIZE, &actlen) == 0) break; usbd_xfer_set_frame_len(xfer, frame, actlen); } if (frame != 0) { usbd_xfer_set_frames(xfer, frame); usbd_transfer_submit(xfer); } break; default: /* Error */ if (error != USB_ERR_CANCELLED) { /* do a builtin clear-stall */ usbd_xfer_set_stall(xfer); goto tr_setup; } break; } } static void u3g_read_callback(struct usb_xfer *xfer, usb_error_t error) { struct ucom_softc *ucom = usbd_xfer_softc(xfer); struct usb_page_cache *pc; int actlen; usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: pc = usbd_xfer_get_frame(xfer, 0); ucom_put_data(ucom, pc, 0, actlen); case USB_ST_SETUP: tr_setup: usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); usbd_transfer_submit(xfer); break; default: /* Error */ if (error != USB_ERR_CANCELLED) { /* do a builtin clear-stall */ usbd_xfer_set_stall(xfer); goto tr_setup; } break; } } static void u3g_cfg_get_status(struct ucom_softc *ucom, uint8_t *lsr, uint8_t *msr) { struct u3g_softc *sc = ucom->sc_parent; /* XXX Note: sc_lsr is always zero */ *lsr = sc->sc_lsr[ucom->sc_subunit]; *msr = sc->sc_msr[ucom->sc_subunit]; } static void u3g_cfg_set_line(struct ucom_softc *ucom) { struct u3g_softc *sc = ucom->sc_parent; struct usb_device_request req; req.bmRequestType = UT_WRITE_CLASS_INTERFACE; req.bRequest = UCDC_SET_CONTROL_LINE_STATE; USETW(req.wValue, sc->sc_line[ucom->sc_subunit]); req.wIndex[0] = sc->sc_iface[ucom->sc_subunit]; req.wIndex[1] = 0; USETW(req.wLength, 0); ucom_cfg_do_request(sc->sc_udev, ucom, &req, NULL, 0, 1000); } static void u3g_cfg_set_dtr(struct ucom_softc *ucom, uint8_t onoff) { struct u3g_softc *sc = ucom->sc_parent; DPRINTF("onoff = %d\n", onoff); if (onoff) sc->sc_line[ucom->sc_subunit] |= UCDC_LINE_DTR; else sc->sc_line[ucom->sc_subunit] &= ~UCDC_LINE_DTR; u3g_cfg_set_line(ucom); } static void u3g_cfg_set_rts(struct ucom_softc *ucom, uint8_t onoff) { struct u3g_softc *sc = ucom->sc_parent; DPRINTF("onoff = %d\n", onoff); if (onoff) sc->sc_line[ucom->sc_subunit] |= UCDC_LINE_RTS; else sc->sc_line[ucom->sc_subunit] &= ~UCDC_LINE_RTS; u3g_cfg_set_line(ucom); } static void u3g_intr_callback(struct usb_xfer *xfer, usb_error_t error) { struct ucom_softc *ucom = usbd_xfer_softc(xfer); struct u3g_softc *sc = ucom->sc_parent; struct usb_page_cache *pc; struct usb_cdc_notification pkt; int actlen; uint16_t wLen; uint8_t mstatus; usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: if (actlen < 8) { /* usb_cdc_notification with 2 data bytes */ DPRINTF("message too short (expected 8, received %d)\n", actlen); goto tr_setup; } pc = usbd_xfer_get_frame(xfer, 0); usbd_copy_out(pc, 0, &pkt, actlen); wLen = UGETW(pkt.wLength); if (wLen < 2) { DPRINTF("message too short (expected 2 data bytes, received %d)\n", wLen); goto tr_setup; } if (pkt.bmRequestType == UCDC_NOTIFICATION && pkt.bNotification == UCDC_N_SERIAL_STATE) { /* * Set the serial state in ucom driver based on * the bits from the notify message */ DPRINTF("notify bytes = 0x%02x, 0x%02x\n", pkt.data[0], pkt.data[1]); /* currently, lsr is always zero. */ sc->sc_lsr[ucom->sc_subunit] = 0; sc->sc_msr[ucom->sc_subunit] = 0; mstatus = pkt.data[0]; if (mstatus & UCDC_N_SERIAL_RI) sc->sc_msr[ucom->sc_subunit] |= SER_RI; if (mstatus & UCDC_N_SERIAL_DSR) sc->sc_msr[ucom->sc_subunit] |= SER_DSR; if (mstatus & UCDC_N_SERIAL_DCD) sc->sc_msr[ucom->sc_subunit] |= SER_DCD; ucom_status_change(ucom); } case USB_ST_SETUP: tr_setup: usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); usbd_transfer_submit(xfer); return; default: /* Error */ if (error != USB_ERR_CANCELLED) { /* try to clear stall first */ usbd_xfer_set_stall(xfer); goto tr_setup; } return; } } static void u3g_poll(struct ucom_softc *ucom) { struct u3g_softc *sc = ucom->sc_parent; usbd_transfer_poll(sc->sc_xfer[ucom->sc_subunit], U3G_N_TRANSFER); } diff --git a/sys/dev/usb/serial/uark.c b/sys/dev/usb/serial/uark.c index 904977e1ec14..072edf6389cd 100644 --- a/sys/dev/usb/serial/uark.c +++ b/sys/dev/usb/serial/uark.c @@ -1,467 +1,469 @@ /* $OpenBSD: uark.c,v 1.1 2006/08/14 08:30:22 jsg Exp $ */ /* * Copyright (c) 2006 Jonathan Gray * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $FreeBSD$ */ /* * NOTE: all function names beginning like "uark_cfg_" can only * be called from within the config thread function ! */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "usbdevs.h" #define USB_DEBUG_VAR usb_debug #include #include #include #define UARK_BUF_SIZE 1024 /* bytes */ #define UARK_SET_DATA_BITS(x) ((x) - 5) #define UARK_PARITY_NONE 0x00 #define UARK_PARITY_ODD 0x08 #define UARK_PARITY_EVEN 0x18 #define UARK_STOP_BITS_1 0x00 #define UARK_STOP_BITS_2 0x04 #define UARK_BAUD_REF 3000000 #define UARK_WRITE 0x40 #define UARK_READ 0xc0 #define UARK_REQUEST 0xfe #define UARK_CONFIG_INDEX 0 #define UARK_IFACE_INDEX 0 enum { UARK_BULK_DT_WR, UARK_BULK_DT_RD, UARK_N_TRANSFER, }; struct uark_softc { struct ucom_super_softc sc_super_ucom; struct ucom_softc sc_ucom; struct usb_xfer *sc_xfer[UARK_N_TRANSFER]; struct usb_device *sc_udev; struct mtx sc_mtx; uint8_t sc_msr; uint8_t sc_lsr; }; /* prototypes */ static device_probe_t uark_probe; static device_attach_t uark_attach; static device_detach_t uark_detach; static void uark_free_softc(struct uark_softc *); static usb_callback_t uark_bulk_write_callback; static usb_callback_t uark_bulk_read_callback; static void uark_free(struct ucom_softc *); static void uark_start_read(struct ucom_softc *); static void uark_stop_read(struct ucom_softc *); static void uark_start_write(struct ucom_softc *); static void uark_stop_write(struct ucom_softc *); static int uark_pre_param(struct ucom_softc *, struct termios *); static void uark_cfg_param(struct ucom_softc *, struct termios *); static void uark_cfg_get_status(struct ucom_softc *, uint8_t *, uint8_t *); static void uark_cfg_set_break(struct ucom_softc *, uint8_t); static void uark_cfg_write(struct uark_softc *, uint16_t, uint16_t); static void uark_poll(struct ucom_softc *ucom); static const struct usb_config uark_xfer_config[UARK_N_TRANSFER] = { [UARK_BULK_DT_WR] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, .bufsize = UARK_BUF_SIZE, .flags = {.pipe_bof = 1,.force_short_xfer = 1,}, .callback = &uark_bulk_write_callback, }, [UARK_BULK_DT_RD] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, .bufsize = UARK_BUF_SIZE, .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, .callback = &uark_bulk_read_callback, }, }; static const struct ucom_callback uark_callback = { .ucom_cfg_get_status = &uark_cfg_get_status, .ucom_cfg_set_break = &uark_cfg_set_break, .ucom_cfg_param = &uark_cfg_param, .ucom_pre_param = &uark_pre_param, .ucom_start_read = &uark_start_read, .ucom_stop_read = &uark_stop_read, .ucom_start_write = &uark_start_write, .ucom_stop_write = &uark_stop_write, .ucom_poll = &uark_poll, .ucom_free = &uark_free, }; static device_method_t uark_methods[] = { /* Device methods */ DEVMETHOD(device_probe, uark_probe), DEVMETHOD(device_attach, uark_attach), DEVMETHOD(device_detach, uark_detach), DEVMETHOD_END }; static devclass_t uark_devclass; static driver_t uark_driver = { .name = "uark", .methods = uark_methods, .size = sizeof(struct uark_softc), }; static const STRUCT_USB_HOST_ID uark_devs[] = { {USB_VPI(USB_VENDOR_ARKMICRO, USB_PRODUCT_ARKMICRO_ARK3116, 0)}, }; DRIVER_MODULE(uark, uhub, uark_driver, uark_devclass, NULL, 0); MODULE_DEPEND(uark, ucom, 1, 1, 1); MODULE_DEPEND(uark, usb, 1, 1, 1); MODULE_VERSION(uark, 1); USB_PNP_HOST_INFO(uark_devs); static int uark_probe(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); if (uaa->usb_mode != USB_MODE_HOST) { return (ENXIO); } if (uaa->info.bConfigIndex != 0) { return (ENXIO); } if (uaa->info.bIfaceIndex != UARK_IFACE_INDEX) { return (ENXIO); } return (usbd_lookup_id_by_uaa(uark_devs, sizeof(uark_devs), uaa)); } static int uark_attach(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); struct uark_softc *sc = device_get_softc(dev); int32_t error; uint8_t iface_index; device_set_usb_desc(dev); mtx_init(&sc->sc_mtx, "uark", NULL, MTX_DEF); ucom_ref(&sc->sc_super_ucom); sc->sc_udev = uaa->device; iface_index = UARK_IFACE_INDEX; error = usbd_transfer_setup (uaa->device, &iface_index, sc->sc_xfer, uark_xfer_config, UARK_N_TRANSFER, sc, &sc->sc_mtx); if (error) { device_printf(dev, "allocating control USB " "transfers failed\n"); goto detach; } /* clear stall at first run */ mtx_lock(&sc->sc_mtx); - usbd_xfer_set_stall(sc->sc_xfer[UARK_BULK_DT_WR]); - usbd_xfer_set_stall(sc->sc_xfer[UARK_BULK_DT_RD]); + usbd_xfer_set_zlp(sc->sc_xfer[UARK_BULK_DT_WR]); mtx_unlock(&sc->sc_mtx); error = ucom_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc, &uark_callback, &sc->sc_mtx); if (error) { DPRINTF("ucom_attach failed\n"); goto detach; } ucom_set_pnpinfo_usb(&sc->sc_super_ucom, dev); return (0); /* success */ detach: uark_detach(dev); return (ENXIO); /* failure */ } static int uark_detach(device_t dev) { struct uark_softc *sc = device_get_softc(dev); ucom_detach(&sc->sc_super_ucom, &sc->sc_ucom); usbd_transfer_unsetup(sc->sc_xfer, UARK_N_TRANSFER); device_claim_softc(dev); uark_free_softc(sc); return (0); } UCOM_UNLOAD_DRAIN(uark); static void uark_free_softc(struct uark_softc *sc) { if (ucom_unref(&sc->sc_super_ucom)) { mtx_destroy(&sc->sc_mtx); device_free_softc(sc); } } static void uark_free(struct ucom_softc *ucom) { uark_free_softc(ucom->sc_parent); } static void uark_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error) { struct uark_softc *sc = usbd_xfer_softc(xfer); struct usb_page_cache *pc; uint32_t actlen; switch (USB_GET_STATE(xfer)) { case USB_ST_SETUP: case USB_ST_TRANSFERRED: tr_setup: + if (usbd_xfer_get_and_clr_zlp(xfer)) + break; + pc = usbd_xfer_get_frame(xfer, 0); if (ucom_get_data(&sc->sc_ucom, pc, 0, UARK_BUF_SIZE, &actlen)) { usbd_xfer_set_frame_len(xfer, 0, actlen); usbd_transfer_submit(xfer); } - return; + break; default: /* Error */ if (error != USB_ERR_CANCELLED) { /* try to clear stall first */ usbd_xfer_set_stall(xfer); goto tr_setup; } - return; + break; } } static void uark_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error) { struct uark_softc *sc = usbd_xfer_softc(xfer); struct usb_page_cache *pc; int actlen; usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: pc = usbd_xfer_get_frame(xfer, 0); ucom_put_data(&sc->sc_ucom, pc, 0, actlen); case USB_ST_SETUP: tr_setup: usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); usbd_transfer_submit(xfer); return; default: /* Error */ if (error != USB_ERR_CANCELLED) { /* try to clear stall first */ usbd_xfer_set_stall(xfer); goto tr_setup; } return; } } static void uark_start_read(struct ucom_softc *ucom) { struct uark_softc *sc = ucom->sc_parent; usbd_transfer_start(sc->sc_xfer[UARK_BULK_DT_RD]); } static void uark_stop_read(struct ucom_softc *ucom) { struct uark_softc *sc = ucom->sc_parent; usbd_transfer_stop(sc->sc_xfer[UARK_BULK_DT_RD]); } static void uark_start_write(struct ucom_softc *ucom) { struct uark_softc *sc = ucom->sc_parent; usbd_transfer_start(sc->sc_xfer[UARK_BULK_DT_WR]); } static void uark_stop_write(struct ucom_softc *ucom) { struct uark_softc *sc = ucom->sc_parent; usbd_transfer_stop(sc->sc_xfer[UARK_BULK_DT_WR]); } static int uark_pre_param(struct ucom_softc *ucom, struct termios *t) { if ((t->c_ospeed < 300) || (t->c_ospeed > 115200)) return (EINVAL); return (0); } static void uark_cfg_param(struct ucom_softc *ucom, struct termios *t) { struct uark_softc *sc = ucom->sc_parent; uint32_t speed = t->c_ospeed; uint16_t data; /* * NOTE: When reverse computing the baud rate from the "data" all * allowed baud rates are within 3% of the initial baud rate. */ data = (UARK_BAUD_REF + (speed / 2)) / speed; uark_cfg_write(sc, 3, 0x83); uark_cfg_write(sc, 0, data & 0xFF); uark_cfg_write(sc, 1, data >> 8); uark_cfg_write(sc, 3, 0x03); if (t->c_cflag & CSTOPB) data = UARK_STOP_BITS_2; else data = UARK_STOP_BITS_1; if (t->c_cflag & PARENB) { if (t->c_cflag & PARODD) data |= UARK_PARITY_ODD; else data |= UARK_PARITY_EVEN; } else data |= UARK_PARITY_NONE; switch (t->c_cflag & CSIZE) { case CS5: data |= UARK_SET_DATA_BITS(5); break; case CS6: data |= UARK_SET_DATA_BITS(6); break; case CS7: data |= UARK_SET_DATA_BITS(7); break; default: case CS8: data |= UARK_SET_DATA_BITS(8); break; } uark_cfg_write(sc, 3, 0x00); uark_cfg_write(sc, 3, data); } static void uark_cfg_get_status(struct ucom_softc *ucom, uint8_t *lsr, uint8_t *msr) { struct uark_softc *sc = ucom->sc_parent; /* XXX Note: sc_lsr is always zero */ *lsr = sc->sc_lsr; *msr = sc->sc_msr; } static void uark_cfg_set_break(struct ucom_softc *ucom, uint8_t onoff) { struct uark_softc *sc = ucom->sc_parent; DPRINTF("onoff=%d\n", onoff); uark_cfg_write(sc, 4, onoff ? 0x01 : 0x00); } static void uark_cfg_write(struct uark_softc *sc, uint16_t index, uint16_t value) { struct usb_device_request req; usb_error_t err; req.bmRequestType = UARK_WRITE; req.bRequest = UARK_REQUEST; USETW(req.wValue, value); USETW(req.wIndex, index); USETW(req.wLength, 0); err = ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, &req, NULL, 0, 1000); if (err) { DPRINTFN(0, "device request failed, err=%s " "(ignored)\n", usbd_errstr(err)); } } static void uark_poll(struct ucom_softc *ucom) { struct uark_softc *sc = ucom->sc_parent; usbd_transfer_poll(sc->sc_xfer, UARK_N_TRANSFER); } diff --git a/sys/dev/usb/serial/ubsa.c b/sys/dev/usb/serial/ubsa.c index 10b37984db2f..3286df7331b4 100644 --- a/sys/dev/usb/serial/ubsa.c +++ b/sys/dev/usb/serial/ubsa.c @@ -1,696 +1,698 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD AND BSD-2-Clause-NetBSD * * Copyright (c) 2002, Alexander Kabaev . * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 THE AUTHOR OR CONTRIBUTORS 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 __FBSDID("$FreeBSD$"); /*- * Copyright (c) 2001 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Ichiro FUKUHARA (ichiro@ichiro.org). * * 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. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 THE FOUNDATION OR CONTRIBUTORS * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "usbdevs.h" #define USB_DEBUG_VAR ubsa_debug #include #include #include #ifdef USB_DEBUG static int ubsa_debug = 0; static SYSCTL_NODE(_hw_usb, OID_AUTO, ubsa, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, "USB ubsa"); SYSCTL_INT(_hw_usb_ubsa, OID_AUTO, debug, CTLFLAG_RWTUN, &ubsa_debug, 0, "ubsa debug level"); #endif #define UBSA_BSIZE 1024 /* bytes */ #define UBSA_CONFIG_INDEX 0 #define UBSA_IFACE_INDEX 0 #define UBSA_REG_BAUDRATE 0x00 #define UBSA_REG_STOP_BITS 0x01 #define UBSA_REG_DATA_BITS 0x02 #define UBSA_REG_PARITY 0x03 #define UBSA_REG_DTR 0x0A #define UBSA_REG_RTS 0x0B #define UBSA_REG_BREAK 0x0C #define UBSA_REG_FLOW_CTRL 0x10 #define UBSA_PARITY_NONE 0x00 #define UBSA_PARITY_EVEN 0x01 #define UBSA_PARITY_ODD 0x02 #define UBSA_PARITY_MARK 0x03 #define UBSA_PARITY_SPACE 0x04 #define UBSA_FLOW_NONE 0x0000 #define UBSA_FLOW_OCTS 0x0001 #define UBSA_FLOW_ODSR 0x0002 #define UBSA_FLOW_IDSR 0x0004 #define UBSA_FLOW_IDTR 0x0008 #define UBSA_FLOW_IRTS 0x0010 #define UBSA_FLOW_ORTS 0x0020 #define UBSA_FLOW_UNKNOWN 0x0040 #define UBSA_FLOW_OXON 0x0080 #define UBSA_FLOW_IXON 0x0100 /* line status register */ #define UBSA_LSR_TSRE 0x40 /* Transmitter empty: byte sent */ #define UBSA_LSR_TXRDY 0x20 /* Transmitter buffer empty */ #define UBSA_LSR_BI 0x10 /* Break detected */ #define UBSA_LSR_FE 0x08 /* Framing error: bad stop bit */ #define UBSA_LSR_PE 0x04 /* Parity error */ #define UBSA_LSR_OE 0x02 /* Overrun, lost incoming byte */ #define UBSA_LSR_RXRDY 0x01 /* Byte ready in Receive Buffer */ #define UBSA_LSR_RCV_MASK 0x1f /* Mask for incoming data or error */ /* modem status register */ /* All deltas are from the last read of the MSR. */ #define UBSA_MSR_DCD 0x80 /* Current Data Carrier Detect */ #define UBSA_MSR_RI 0x40 /* Current Ring Indicator */ #define UBSA_MSR_DSR 0x20 /* Current Data Set Ready */ #define UBSA_MSR_CTS 0x10 /* Current Clear to Send */ #define UBSA_MSR_DDCD 0x08 /* DCD has changed state */ #define UBSA_MSR_TERI 0x04 /* RI has toggled low to high */ #define UBSA_MSR_DDSR 0x02 /* DSR has changed state */ #define UBSA_MSR_DCTS 0x01 /* CTS has changed state */ enum { UBSA_BULK_DT_WR, UBSA_BULK_DT_RD, UBSA_INTR_DT_RD, UBSA_N_TRANSFER, }; struct ubsa_softc { struct ucom_super_softc sc_super_ucom; struct ucom_softc sc_ucom; struct usb_xfer *sc_xfer[UBSA_N_TRANSFER]; struct usb_device *sc_udev; struct mtx sc_mtx; uint8_t sc_iface_no; /* interface number */ uint8_t sc_iface_index; /* interface index */ uint8_t sc_lsr; /* local status register */ uint8_t sc_msr; /* UBSA status register */ }; static device_probe_t ubsa_probe; static device_attach_t ubsa_attach; static device_detach_t ubsa_detach; static void ubsa_free_softc(struct ubsa_softc *); static usb_callback_t ubsa_write_callback; static usb_callback_t ubsa_read_callback; static usb_callback_t ubsa_intr_callback; static void ubsa_cfg_request(struct ubsa_softc *, uint8_t, uint16_t); static void ubsa_free(struct ucom_softc *); static void ubsa_cfg_set_dtr(struct ucom_softc *, uint8_t); static void ubsa_cfg_set_rts(struct ucom_softc *, uint8_t); static void ubsa_cfg_set_break(struct ucom_softc *, uint8_t); static int ubsa_pre_param(struct ucom_softc *, struct termios *); static void ubsa_cfg_param(struct ucom_softc *, struct termios *); static void ubsa_start_read(struct ucom_softc *); static void ubsa_stop_read(struct ucom_softc *); static void ubsa_start_write(struct ucom_softc *); static void ubsa_stop_write(struct ucom_softc *); static void ubsa_cfg_get_status(struct ucom_softc *, uint8_t *, uint8_t *); static void ubsa_poll(struct ucom_softc *ucom); static const struct usb_config ubsa_config[UBSA_N_TRANSFER] = { [UBSA_BULK_DT_WR] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, .bufsize = UBSA_BSIZE, /* bytes */ .flags = {.pipe_bof = 1,.force_short_xfer = 1,}, .callback = &ubsa_write_callback, }, [UBSA_BULK_DT_RD] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, .bufsize = UBSA_BSIZE, /* bytes */ .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, .callback = &ubsa_read_callback, }, [UBSA_INTR_DT_RD] = { .type = UE_INTERRUPT, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, .bufsize = 0, /* use wMaxPacketSize */ .callback = &ubsa_intr_callback, }, }; static const struct ucom_callback ubsa_callback = { .ucom_cfg_get_status = &ubsa_cfg_get_status, .ucom_cfg_set_dtr = &ubsa_cfg_set_dtr, .ucom_cfg_set_rts = &ubsa_cfg_set_rts, .ucom_cfg_set_break = &ubsa_cfg_set_break, .ucom_cfg_param = &ubsa_cfg_param, .ucom_pre_param = &ubsa_pre_param, .ucom_start_read = &ubsa_start_read, .ucom_stop_read = &ubsa_stop_read, .ucom_start_write = &ubsa_start_write, .ucom_stop_write = &ubsa_stop_write, .ucom_poll = &ubsa_poll, .ucom_free = &ubsa_free, }; static const STRUCT_USB_HOST_ID ubsa_devs[] = { /* AnyData ADU-500A */ {USB_VPI(USB_VENDOR_ANYDATA, USB_PRODUCT_ANYDATA_ADU_500A, 0)}, /* AnyData ADU-E100A/H */ {USB_VPI(USB_VENDOR_ANYDATA, USB_PRODUCT_ANYDATA_ADU_E100X, 0)}, /* Axesstel MV100H */ {USB_VPI(USB_VENDOR_AXESSTEL, USB_PRODUCT_AXESSTEL_DATAMODEM, 0)}, /* BELKIN F5U103 */ {USB_VPI(USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5U103, 0)}, /* BELKIN F5U120 */ {USB_VPI(USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5U120, 0)}, /* GoHubs GO-COM232 */ {USB_VPI(USB_VENDOR_ETEK, USB_PRODUCT_ETEK_1COM, 0)}, /* GoHubs GO-COM232 */ {USB_VPI(USB_VENDOR_GOHUBS, USB_PRODUCT_GOHUBS_GOCOM232, 0)}, /* Peracom */ {USB_VPI(USB_VENDOR_PERACOM, USB_PRODUCT_PERACOM_SERIAL1, 0)}, }; static device_method_t ubsa_methods[] = { DEVMETHOD(device_probe, ubsa_probe), DEVMETHOD(device_attach, ubsa_attach), DEVMETHOD(device_detach, ubsa_detach), DEVMETHOD_END }; static devclass_t ubsa_devclass; static driver_t ubsa_driver = { .name = "ubsa", .methods = ubsa_methods, .size = sizeof(struct ubsa_softc), }; DRIVER_MODULE(ubsa, uhub, ubsa_driver, ubsa_devclass, NULL, 0); MODULE_DEPEND(ubsa, ucom, 1, 1, 1); MODULE_DEPEND(ubsa, usb, 1, 1, 1); MODULE_VERSION(ubsa, 1); USB_PNP_HOST_INFO(ubsa_devs); static int ubsa_probe(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); if (uaa->usb_mode != USB_MODE_HOST) { return (ENXIO); } if (uaa->info.bConfigIndex != UBSA_CONFIG_INDEX) { return (ENXIO); } if (uaa->info.bIfaceIndex != UBSA_IFACE_INDEX) { return (ENXIO); } return (usbd_lookup_id_by_uaa(ubsa_devs, sizeof(ubsa_devs), uaa)); } static int ubsa_attach(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); struct ubsa_softc *sc = device_get_softc(dev); int error; DPRINTF("sc=%p\n", sc); device_set_usb_desc(dev); mtx_init(&sc->sc_mtx, "ubsa", NULL, MTX_DEF); ucom_ref(&sc->sc_super_ucom); sc->sc_udev = uaa->device; sc->sc_iface_no = uaa->info.bIfaceNum; sc->sc_iface_index = UBSA_IFACE_INDEX; error = usbd_transfer_setup(uaa->device, &sc->sc_iface_index, sc->sc_xfer, ubsa_config, UBSA_N_TRANSFER, sc, &sc->sc_mtx); if (error) { DPRINTF("could not allocate all pipes\n"); goto detach; } /* clear stall at first run */ mtx_lock(&sc->sc_mtx); - usbd_xfer_set_stall(sc->sc_xfer[UBSA_BULK_DT_WR]); - usbd_xfer_set_stall(sc->sc_xfer[UBSA_BULK_DT_RD]); + usbd_xfer_set_zlp(sc->sc_xfer[UBSA_BULK_DT_WR]); mtx_unlock(&sc->sc_mtx); error = ucom_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc, &ubsa_callback, &sc->sc_mtx); if (error) { DPRINTF("ucom_attach failed\n"); goto detach; } ucom_set_pnpinfo_usb(&sc->sc_super_ucom, dev); return (0); detach: ubsa_detach(dev); return (ENXIO); } static int ubsa_detach(device_t dev) { struct ubsa_softc *sc = device_get_softc(dev); DPRINTF("sc=%p\n", sc); ucom_detach(&sc->sc_super_ucom, &sc->sc_ucom); usbd_transfer_unsetup(sc->sc_xfer, UBSA_N_TRANSFER); device_claim_softc(dev); ubsa_free_softc(sc); return (0); } UCOM_UNLOAD_DRAIN(ubsa); static void ubsa_free_softc(struct ubsa_softc *sc) { if (ucom_unref(&sc->sc_super_ucom)) { mtx_destroy(&sc->sc_mtx); device_free_softc(sc); } } static void ubsa_free(struct ucom_softc *ucom) { ubsa_free_softc(ucom->sc_parent); } static void ubsa_cfg_request(struct ubsa_softc *sc, uint8_t index, uint16_t value) { struct usb_device_request req; usb_error_t err; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = index; USETW(req.wValue, value); req.wIndex[0] = sc->sc_iface_no; req.wIndex[1] = 0; USETW(req.wLength, 0); err = ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, &req, NULL, 0, 1000); if (err) { DPRINTFN(0, "device request failed, err=%s " "(ignored)\n", usbd_errstr(err)); } } static void ubsa_cfg_set_dtr(struct ucom_softc *ucom, uint8_t onoff) { struct ubsa_softc *sc = ucom->sc_parent; DPRINTF("onoff = %d\n", onoff); ubsa_cfg_request(sc, UBSA_REG_DTR, onoff ? 1 : 0); } static void ubsa_cfg_set_rts(struct ucom_softc *ucom, uint8_t onoff) { struct ubsa_softc *sc = ucom->sc_parent; DPRINTF("onoff = %d\n", onoff); ubsa_cfg_request(sc, UBSA_REG_RTS, onoff ? 1 : 0); } static void ubsa_cfg_set_break(struct ucom_softc *ucom, uint8_t onoff) { struct ubsa_softc *sc = ucom->sc_parent; DPRINTF("onoff = %d\n", onoff); ubsa_cfg_request(sc, UBSA_REG_BREAK, onoff ? 1 : 0); } static int ubsa_pre_param(struct ucom_softc *ucom, struct termios *t) { DPRINTF("sc = %p\n", ucom->sc_parent); switch (t->c_ospeed) { case B0: case B300: case B600: case B1200: case B2400: case B4800: case B9600: case B19200: case B38400: case B57600: case B115200: case B230400: break; default: return (EINVAL); } return (0); } static void ubsa_cfg_param(struct ucom_softc *ucom, struct termios *t) { struct ubsa_softc *sc = ucom->sc_parent; uint16_t value = 0; DPRINTF("sc = %p\n", sc); switch (t->c_ospeed) { case B0: ubsa_cfg_request(sc, UBSA_REG_FLOW_CTRL, 0); ubsa_cfg_set_dtr(&sc->sc_ucom, 0); ubsa_cfg_set_rts(&sc->sc_ucom, 0); break; case B300: case B600: case B1200: case B2400: case B4800: case B9600: case B19200: case B38400: case B57600: case B115200: case B230400: value = B230400 / t->c_ospeed; ubsa_cfg_request(sc, UBSA_REG_BAUDRATE, value); break; default: return; } if (t->c_cflag & PARENB) value = (t->c_cflag & PARODD) ? UBSA_PARITY_ODD : UBSA_PARITY_EVEN; else value = UBSA_PARITY_NONE; ubsa_cfg_request(sc, UBSA_REG_PARITY, value); switch (t->c_cflag & CSIZE) { case CS5: value = 0; break; case CS6: value = 1; break; case CS7: value = 2; break; default: case CS8: value = 3; break; } ubsa_cfg_request(sc, UBSA_REG_DATA_BITS, value); value = (t->c_cflag & CSTOPB) ? 1 : 0; ubsa_cfg_request(sc, UBSA_REG_STOP_BITS, value); value = 0; if (t->c_cflag & CRTSCTS) value |= UBSA_FLOW_OCTS | UBSA_FLOW_IRTS; if (t->c_iflag & (IXON | IXOFF)) value |= UBSA_FLOW_OXON | UBSA_FLOW_IXON; ubsa_cfg_request(sc, UBSA_REG_FLOW_CTRL, value); } static void ubsa_start_read(struct ucom_softc *ucom) { struct ubsa_softc *sc = ucom->sc_parent; /* start interrupt endpoint */ usbd_transfer_start(sc->sc_xfer[UBSA_INTR_DT_RD]); /* start read endpoint */ usbd_transfer_start(sc->sc_xfer[UBSA_BULK_DT_RD]); } static void ubsa_stop_read(struct ucom_softc *ucom) { struct ubsa_softc *sc = ucom->sc_parent; /* stop interrupt endpoint */ usbd_transfer_stop(sc->sc_xfer[UBSA_INTR_DT_RD]); /* stop read endpoint */ usbd_transfer_stop(sc->sc_xfer[UBSA_BULK_DT_RD]); } static void ubsa_start_write(struct ucom_softc *ucom) { struct ubsa_softc *sc = ucom->sc_parent; usbd_transfer_start(sc->sc_xfer[UBSA_BULK_DT_WR]); } static void ubsa_stop_write(struct ucom_softc *ucom) { struct ubsa_softc *sc = ucom->sc_parent; usbd_transfer_stop(sc->sc_xfer[UBSA_BULK_DT_WR]); } static void ubsa_cfg_get_status(struct ucom_softc *ucom, uint8_t *lsr, uint8_t *msr) { struct ubsa_softc *sc = ucom->sc_parent; DPRINTF("\n"); *lsr = sc->sc_lsr; *msr = sc->sc_msr; } static void ubsa_write_callback(struct usb_xfer *xfer, usb_error_t error) { struct ubsa_softc *sc = usbd_xfer_softc(xfer); struct usb_page_cache *pc; uint32_t actlen; switch (USB_GET_STATE(xfer)) { case USB_ST_SETUP: case USB_ST_TRANSFERRED: tr_setup: + if (usbd_xfer_get_and_clr_zlp(xfer)) + break; + pc = usbd_xfer_get_frame(xfer, 0); if (ucom_get_data(&sc->sc_ucom, pc, 0, UBSA_BSIZE, &actlen)) { usbd_xfer_set_frame_len(xfer, 0, actlen); usbd_transfer_submit(xfer); } - return; + break; default: /* Error */ if (error != USB_ERR_CANCELLED) { /* try to clear stall first */ usbd_xfer_set_stall(xfer); goto tr_setup; } - return; + break; } } static void ubsa_read_callback(struct usb_xfer *xfer, usb_error_t error) { struct ubsa_softc *sc = usbd_xfer_softc(xfer); struct usb_page_cache *pc; int actlen; usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: pc = usbd_xfer_get_frame(xfer, 0); ucom_put_data(&sc->sc_ucom, pc, 0, actlen); case USB_ST_SETUP: tr_setup: usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); usbd_transfer_submit(xfer); return; default: /* Error */ if (error != USB_ERR_CANCELLED) { /* try to clear stall first */ usbd_xfer_set_stall(xfer); goto tr_setup; } return; } } static void ubsa_intr_callback(struct usb_xfer *xfer, usb_error_t error) { struct ubsa_softc *sc = usbd_xfer_softc(xfer); struct usb_page_cache *pc; uint8_t buf[4]; int actlen; usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: if (actlen >= (int)sizeof(buf)) { pc = usbd_xfer_get_frame(xfer, 0); usbd_copy_out(pc, 0, buf, sizeof(buf)); /* * MSR bits need translation from ns16550 to SER_* values. * LSR bits are ns16550 in hardware and ucom. */ sc->sc_msr = 0; if (buf[3] & UBSA_MSR_CTS) sc->sc_msr |= SER_CTS; if (buf[3] & UBSA_MSR_DCD) sc->sc_msr |= SER_DCD; if (buf[3] & UBSA_MSR_RI) sc->sc_msr |= SER_RI; if (buf[3] & UBSA_MSR_DSR) sc->sc_msr |= SER_DSR; sc->sc_lsr = buf[2]; DPRINTF("lsr = 0x%02x, msr = 0x%02x\n", sc->sc_lsr, sc->sc_msr); ucom_status_change(&sc->sc_ucom); } else { DPRINTF("ignoring short packet, %d bytes\n", actlen); } /* FALLTHROUGH */ case USB_ST_SETUP: tr_setup: usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); usbd_transfer_submit(xfer); return; default: /* Error */ if (error != USB_ERR_CANCELLED) { /* try to clear stall first */ usbd_xfer_set_stall(xfer); goto tr_setup; } return; } } static void ubsa_poll(struct ucom_softc *ucom) { struct ubsa_softc *sc = ucom->sc_parent; usbd_transfer_poll(sc->sc_xfer, UBSA_N_TRANSFER); } diff --git a/sys/dev/usb/serial/ubser.c b/sys/dev/usb/serial/ubser.c index 2a35756b0a7e..61b9c6c433fb 100644 --- a/sys/dev/usb/serial/ubser.c +++ b/sys/dev/usb/serial/ubser.c @@ -1,557 +1,559 @@ /*- * Copyright (c) 2004 Bernd Walter * * $URL: https://devel.bwct.de/svn/projects/ubser/ubser.c $ * $Date: 2004-02-29 01:53:10 +0100 (Sun, 29 Feb 2004) $ * $Author: ticso $ * $Rev: 1127 $ */ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD AND BSD-2-Clause-NetBSD * * Copyright (c) 2001-2002, Shunsuke Akiyama . * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 THE AUTHOR OR CONTRIBUTORS 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. */ /*- * Copyright (c) 2000 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (lennart@augustsson.net). * * 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. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 THE FOUNDATION OR CONTRIBUTORS * 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 __FBSDID("$FreeBSD$"); /* * BWCT serial adapter driver */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "usbdevs.h" #define USB_DEBUG_VAR ubser_debug #include #include #include #define UBSER_UNIT_MAX 32 /* Vendor Interface Requests */ #define VENDOR_GET_NUMSER 0x01 #define VENDOR_SET_BREAK 0x02 #define VENDOR_CLEAR_BREAK 0x03 #ifdef USB_DEBUG static int ubser_debug = 0; static SYSCTL_NODE(_hw_usb, OID_AUTO, ubser, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, "USB ubser"); SYSCTL_INT(_hw_usb_ubser, OID_AUTO, debug, CTLFLAG_RWTUN, &ubser_debug, 0, "ubser debug level"); #endif enum { UBSER_BULK_DT_WR, UBSER_BULK_DT_RD, UBSER_N_TRANSFER, }; struct ubser_softc { struct ucom_super_softc sc_super_ucom; struct ucom_softc sc_ucom[UBSER_UNIT_MAX]; struct usb_xfer *sc_xfer[UBSER_N_TRANSFER]; struct usb_device *sc_udev; struct mtx sc_mtx; uint16_t sc_tx_size; uint8_t sc_numser; uint8_t sc_iface_no; uint8_t sc_iface_index; uint8_t sc_curr_tx_unit; }; /* prototypes */ static device_probe_t ubser_probe; static device_attach_t ubser_attach; static device_detach_t ubser_detach; static void ubser_free_softc(struct ubser_softc *); static usb_callback_t ubser_write_callback; static usb_callback_t ubser_read_callback; static void ubser_free(struct ucom_softc *); static int ubser_pre_param(struct ucom_softc *, struct termios *); static void ubser_cfg_set_break(struct ucom_softc *, uint8_t); static void ubser_cfg_get_status(struct ucom_softc *, uint8_t *, uint8_t *); static void ubser_start_read(struct ucom_softc *); static void ubser_stop_read(struct ucom_softc *); static void ubser_start_write(struct ucom_softc *); static void ubser_stop_write(struct ucom_softc *); static void ubser_poll(struct ucom_softc *ucom); static const struct usb_config ubser_config[UBSER_N_TRANSFER] = { [UBSER_BULK_DT_WR] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, .bufsize = 0, /* use wMaxPacketSize */ .flags = {.pipe_bof = 1,.force_short_xfer = 1,}, .callback = &ubser_write_callback, }, [UBSER_BULK_DT_RD] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, .bufsize = 0, /* use wMaxPacketSize */ .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, .callback = &ubser_read_callback, }, }; static const struct ucom_callback ubser_callback = { .ucom_cfg_set_break = &ubser_cfg_set_break, .ucom_cfg_get_status = &ubser_cfg_get_status, .ucom_pre_param = &ubser_pre_param, .ucom_start_read = &ubser_start_read, .ucom_stop_read = &ubser_stop_read, .ucom_start_write = &ubser_start_write, .ucom_stop_write = &ubser_stop_write, .ucom_poll = &ubser_poll, .ucom_free = &ubser_free, }; static device_method_t ubser_methods[] = { DEVMETHOD(device_probe, ubser_probe), DEVMETHOD(device_attach, ubser_attach), DEVMETHOD(device_detach, ubser_detach), DEVMETHOD_END }; static devclass_t ubser_devclass; static driver_t ubser_driver = { .name = "ubser", .methods = ubser_methods, .size = sizeof(struct ubser_softc), }; DRIVER_MODULE(ubser, uhub, ubser_driver, ubser_devclass, NULL, 0); MODULE_DEPEND(ubser, ucom, 1, 1, 1); MODULE_DEPEND(ubser, usb, 1, 1, 1); MODULE_VERSION(ubser, 1); static int ubser_probe(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); if (uaa->usb_mode != USB_MODE_HOST) { return (ENXIO); } /* check if this is a BWCT vendor specific ubser interface */ if ((strcmp(usb_get_manufacturer(uaa->device), "BWCT") == 0) && (uaa->info.bInterfaceClass == 0xff) && (uaa->info.bInterfaceSubClass == 0x00)) return (0); return (ENXIO); } static int ubser_attach(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); struct ubser_softc *sc = device_get_softc(dev); struct usb_device_request req; uint8_t n; int error; device_set_usb_desc(dev); mtx_init(&sc->sc_mtx, "ubser", NULL, MTX_DEF); ucom_ref(&sc->sc_super_ucom); sc->sc_iface_no = uaa->info.bIfaceNum; sc->sc_iface_index = uaa->info.bIfaceIndex; sc->sc_udev = uaa->device; /* get number of serials */ req.bmRequestType = UT_READ_VENDOR_INTERFACE; req.bRequest = VENDOR_GET_NUMSER; USETW(req.wValue, 0); req.wIndex[0] = sc->sc_iface_no; req.wIndex[1] = 0; USETW(req.wLength, 1); error = usbd_do_request_flags(uaa->device, NULL, &req, &sc->sc_numser, 0, NULL, USB_DEFAULT_TIMEOUT); if (error || (sc->sc_numser == 0)) { device_printf(dev, "failed to get number " "of serial ports: %s\n", usbd_errstr(error)); goto detach; } if (sc->sc_numser > UBSER_UNIT_MAX) sc->sc_numser = UBSER_UNIT_MAX; device_printf(dev, "found %i serials\n", sc->sc_numser); error = usbd_transfer_setup(uaa->device, &sc->sc_iface_index, sc->sc_xfer, ubser_config, UBSER_N_TRANSFER, sc, &sc->sc_mtx); if (error) { goto detach; } sc->sc_tx_size = usbd_xfer_max_len(sc->sc_xfer[UBSER_BULK_DT_WR]); if (sc->sc_tx_size == 0) { DPRINTFN(0, "invalid tx_size\n"); goto detach; } /* initialize port numbers */ for (n = 0; n < sc->sc_numser; n++) { sc->sc_ucom[n].sc_portno = n; } error = ucom_attach(&sc->sc_super_ucom, sc->sc_ucom, sc->sc_numser, sc, &ubser_callback, &sc->sc_mtx); if (error) { goto detach; } ucom_set_pnpinfo_usb(&sc->sc_super_ucom, dev); mtx_lock(&sc->sc_mtx); - usbd_xfer_set_stall(sc->sc_xfer[UBSER_BULK_DT_WR]); - usbd_xfer_set_stall(sc->sc_xfer[UBSER_BULK_DT_RD]); + usbd_xfer_set_zlp(sc->sc_xfer[UBSER_BULK_DT_WR]); usbd_transfer_start(sc->sc_xfer[UBSER_BULK_DT_RD]); mtx_unlock(&sc->sc_mtx); return (0); /* success */ detach: ubser_detach(dev); return (ENXIO); /* failure */ } static int ubser_detach(device_t dev) { struct ubser_softc *sc = device_get_softc(dev); DPRINTF("\n"); ucom_detach(&sc->sc_super_ucom, sc->sc_ucom); usbd_transfer_unsetup(sc->sc_xfer, UBSER_N_TRANSFER); device_claim_softc(dev); ubser_free_softc(sc); return (0); } UCOM_UNLOAD_DRAIN(ubser); static void ubser_free_softc(struct ubser_softc *sc) { if (ucom_unref(&sc->sc_super_ucom)) { mtx_destroy(&sc->sc_mtx); device_free_softc(sc); } } static void ubser_free(struct ucom_softc *ucom) { ubser_free_softc(ucom->sc_parent); } static int ubser_pre_param(struct ucom_softc *ucom, struct termios *t) { DPRINTF("\n"); /* * The firmware on our devices can only do 8n1@9600bps * without handshake. * We refuse to accept other configurations. */ /* ensure 9600bps */ switch (t->c_ospeed) { case 9600: break; default: return (EINVAL); } /* 2 stop bits not possible */ if (t->c_cflag & CSTOPB) return (EINVAL); /* XXX parity handling not possible with current firmware */ if (t->c_cflag & PARENB) return (EINVAL); /* we can only do 8 data bits */ switch (t->c_cflag & CSIZE) { case CS8: break; default: return (EINVAL); } /* we can't do any kind of hardware handshaking */ if ((t->c_cflag & (CRTS_IFLOW | CDTR_IFLOW | CDSR_OFLOW | CCAR_OFLOW)) != 0) return (EINVAL); /* * XXX xon/xoff not supported by the firmware! * This is handled within FreeBSD only and may overflow buffers * because of delayed reaction due to device buffering. */ return (0); } static __inline void ubser_inc_tx_unit(struct ubser_softc *sc) { sc->sc_curr_tx_unit++; if (sc->sc_curr_tx_unit >= sc->sc_numser) { sc->sc_curr_tx_unit = 0; } } static void ubser_write_callback(struct usb_xfer *xfer, usb_error_t error) { struct ubser_softc *sc = usbd_xfer_softc(xfer); struct usb_page_cache *pc; uint8_t buf[1]; uint8_t first_unit = sc->sc_curr_tx_unit; uint32_t actlen; switch (USB_GET_STATE(xfer)) { case USB_ST_SETUP: case USB_ST_TRANSFERRED: tr_setup: + if (usbd_xfer_get_and_clr_zlp(xfer)) + break; + pc = usbd_xfer_get_frame(xfer, 0); do { if (ucom_get_data(sc->sc_ucom + sc->sc_curr_tx_unit, pc, 1, sc->sc_tx_size - 1, &actlen)) { buf[0] = sc->sc_curr_tx_unit; usbd_copy_in(pc, 0, buf, 1); usbd_xfer_set_frame_len(xfer, 0, actlen + 1); usbd_transfer_submit(xfer); ubser_inc_tx_unit(sc); /* round robin */ break; } ubser_inc_tx_unit(sc); } while (sc->sc_curr_tx_unit != first_unit); - return; + break; default: /* Error */ if (error != USB_ERR_CANCELLED) { /* try to clear stall first */ usbd_xfer_set_stall(xfer); goto tr_setup; } - return; + break; } } static void ubser_read_callback(struct usb_xfer *xfer, usb_error_t error) { struct ubser_softc *sc = usbd_xfer_softc(xfer); struct usb_page_cache *pc; uint8_t buf[1]; int actlen; usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: if (actlen < 1) { DPRINTF("invalid actlen=0!\n"); goto tr_setup; } pc = usbd_xfer_get_frame(xfer, 0); usbd_copy_out(pc, 0, buf, 1); if (buf[0] >= sc->sc_numser) { DPRINTF("invalid serial number!\n"); goto tr_setup; } ucom_put_data(sc->sc_ucom + buf[0], pc, 1, actlen - 1); case USB_ST_SETUP: tr_setup: usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); usbd_transfer_submit(xfer); return; default: /* Error */ if (error != USB_ERR_CANCELLED) { /* try to clear stall first */ usbd_xfer_set_stall(xfer); goto tr_setup; } return; } } static void ubser_cfg_set_break(struct ucom_softc *ucom, uint8_t onoff) { struct ubser_softc *sc = ucom->sc_parent; uint8_t x = ucom->sc_portno; struct usb_device_request req; usb_error_t err; if (onoff) { req.bmRequestType = UT_READ_VENDOR_INTERFACE; req.bRequest = VENDOR_SET_BREAK; req.wValue[0] = x; req.wValue[1] = 0; req.wIndex[0] = sc->sc_iface_no; req.wIndex[1] = 0; USETW(req.wLength, 0); err = ucom_cfg_do_request(sc->sc_udev, ucom, &req, NULL, 0, 1000); if (err) { DPRINTFN(0, "send break failed, error=%s\n", usbd_errstr(err)); } } } static void ubser_cfg_get_status(struct ucom_softc *ucom, uint8_t *lsr, uint8_t *msr) { /* fake status bits */ *lsr = 0; *msr = SER_DCD; } static void ubser_start_read(struct ucom_softc *ucom) { struct ubser_softc *sc = ucom->sc_parent; usbd_transfer_start(sc->sc_xfer[UBSER_BULK_DT_RD]); } static void ubser_stop_read(struct ucom_softc *ucom) { struct ubser_softc *sc = ucom->sc_parent; usbd_transfer_stop(sc->sc_xfer[UBSER_BULK_DT_RD]); } static void ubser_start_write(struct ucom_softc *ucom) { struct ubser_softc *sc = ucom->sc_parent; usbd_transfer_start(sc->sc_xfer[UBSER_BULK_DT_WR]); } static void ubser_stop_write(struct ucom_softc *ucom) { struct ubser_softc *sc = ucom->sc_parent; usbd_transfer_stop(sc->sc_xfer[UBSER_BULK_DT_WR]); } static void ubser_poll(struct ucom_softc *ucom) { struct ubser_softc *sc = ucom->sc_parent; usbd_transfer_poll(sc->sc_xfer, UBSER_N_TRANSFER); } diff --git a/sys/dev/usb/serial/uchcom.c b/sys/dev/usb/serial/uchcom.c index 6b17141c6ece..aef1515d3586 100644 --- a/sys/dev/usb/serial/uchcom.c +++ b/sys/dev/usb/serial/uchcom.c @@ -1,911 +1,913 @@ /* $NetBSD: uchcom.c,v 1.1 2007/09/03 17:57:37 tshiozak Exp $ */ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD AND BSD-2-Clause-NetBSD * * Copyright (c) 2007, Takanori Watanabe * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 THE AUTHOR OR CONTRIBUTORS 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. */ /* * Copyright (c) 2007 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Takuya SHIOZAKI (tshiozak@netbsd.org). * * 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. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 THE FOUNDATION OR CONTRIBUTORS * 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 __FBSDID("$FreeBSD$"); /* * Driver for WinChipHead CH341/340, the worst USB-serial chip in the * world. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "usbdevs.h" #define USB_DEBUG_VAR uchcom_debug #include #include #include #ifdef USB_DEBUG static int uchcom_debug = 0; static SYSCTL_NODE(_hw_usb, OID_AUTO, uchcom, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, "USB uchcom"); SYSCTL_INT(_hw_usb_uchcom, OID_AUTO, debug, CTLFLAG_RWTUN, &uchcom_debug, 0, "uchcom debug level"); #endif #define UCHCOM_IFACE_INDEX 0 #define UCHCOM_CONFIG_INDEX 0 #define UCHCOM_REV_CH340 0x0250 #define UCHCOM_INPUT_BUF_SIZE 8 #define UCHCOM_REQ_GET_VERSION 0x5F #define UCHCOM_REQ_READ_REG 0x95 #define UCHCOM_REQ_WRITE_REG 0x9A #define UCHCOM_REQ_RESET 0xA1 #define UCHCOM_REQ_SET_DTRRTS 0xA4 #define UCHCOM_REG_STAT1 0x06 #define UCHCOM_REG_STAT2 0x07 #define UCHCOM_REG_BPS_PRE 0x12 #define UCHCOM_REG_BPS_DIV 0x13 #define UCHCOM_REG_BPS_MOD 0x14 #define UCHCOM_REG_BPS_PAD 0x0F #define UCHCOM_REG_BREAK1 0x05 #define UCHCOM_REG_LCR1 0x18 #define UCHCOM_REG_LCR2 0x25 #define UCHCOM_VER_20 0x20 #define UCHCOM_VER_30 0x30 #define UCHCOM_BASE_UNKNOWN 0 #define UCHCOM_BPS_MOD_BASE 20000000 #define UCHCOM_BPS_MOD_BASE_OFS 1100 #define UCHCOM_DTR_MASK 0x20 #define UCHCOM_RTS_MASK 0x40 #define UCHCOM_BRK_MASK 0x01 #define UCHCOM_LCR1_MASK 0xAF #define UCHCOM_LCR2_MASK 0x07 #define UCHCOM_LCR1_RX 0x80 #define UCHCOM_LCR1_TX 0x40 #define UCHCOM_LCR1_PARENB 0x08 #define UCHCOM_LCR1_CS8 0x03 #define UCHCOM_LCR2_PAREVEN 0x07 #define UCHCOM_LCR2_PARODD 0x06 #define UCHCOM_LCR2_PARMARK 0x05 #define UCHCOM_LCR2_PARSPACE 0x04 #define UCHCOM_INTR_STAT1 0x02 #define UCHCOM_INTR_STAT2 0x03 #define UCHCOM_INTR_LEAST 4 #define UCHCOM_BULK_BUF_SIZE 1024 /* bytes */ enum { UCHCOM_BULK_DT_WR, UCHCOM_BULK_DT_RD, UCHCOM_INTR_DT_RD, UCHCOM_N_TRANSFER, }; struct uchcom_softc { struct ucom_super_softc sc_super_ucom; struct ucom_softc sc_ucom; struct usb_xfer *sc_xfer[UCHCOM_N_TRANSFER]; struct usb_device *sc_udev; struct mtx sc_mtx; uint8_t sc_dtr; /* local copy */ uint8_t sc_rts; /* local copy */ uint8_t sc_version; uint8_t sc_msr; uint8_t sc_lsr; /* local status register */ }; struct uchcom_divider { uint8_t dv_prescaler; uint8_t dv_div; uint8_t dv_mod; }; struct uchcom_divider_record { uint32_t dvr_high; uint32_t dvr_low; uint32_t dvr_base_clock; struct uchcom_divider dvr_divider; }; static const struct uchcom_divider_record dividers[] = { {307200, 307200, UCHCOM_BASE_UNKNOWN, {7, 0xD9, 0}}, {921600, 921600, UCHCOM_BASE_UNKNOWN, {7, 0xF3, 0}}, {2999999, 23530, 6000000, {3, 0, 0}}, {23529, 2942, 750000, {2, 0, 0}}, {2941, 368, 93750, {1, 0, 0}}, {367, 1, 11719, {0, 0, 0}}, }; #define NUM_DIVIDERS nitems(dividers) static const STRUCT_USB_HOST_ID uchcom_devs[] = { {USB_VPI(USB_VENDOR_WCH, USB_PRODUCT_WCH_CH341SER, 0)}, {USB_VPI(USB_VENDOR_WCH2, USB_PRODUCT_WCH2_CH341SER, 0)}, {USB_VPI(USB_VENDOR_WCH2, USB_PRODUCT_WCH2_CH341SER_2, 0)}, }; /* protypes */ static void uchcom_free(struct ucom_softc *); static int uchcom_pre_param(struct ucom_softc *, struct termios *); static void uchcom_cfg_get_status(struct ucom_softc *, uint8_t *, uint8_t *); static void uchcom_cfg_open(struct ucom_softc *ucom); static void uchcom_cfg_param(struct ucom_softc *, struct termios *); static void uchcom_cfg_set_break(struct ucom_softc *, uint8_t); static void uchcom_cfg_set_dtr(struct ucom_softc *, uint8_t); static void uchcom_cfg_set_rts(struct ucom_softc *, uint8_t); static void uchcom_start_read(struct ucom_softc *); static void uchcom_start_write(struct ucom_softc *); static void uchcom_stop_read(struct ucom_softc *); static void uchcom_stop_write(struct ucom_softc *); static void uchcom_update_version(struct uchcom_softc *); static void uchcom_convert_status(struct uchcom_softc *, uint8_t); static void uchcom_update_status(struct uchcom_softc *); static void uchcom_set_dtr_rts(struct uchcom_softc *); static int uchcom_calc_divider_settings(struct uchcom_divider *, uint32_t); static void uchcom_set_baudrate(struct uchcom_softc *, uint32_t); static void uchcom_poll(struct ucom_softc *ucom); static device_probe_t uchcom_probe; static device_attach_t uchcom_attach; static device_detach_t uchcom_detach; static void uchcom_free_softc(struct uchcom_softc *); static usb_callback_t uchcom_intr_callback; static usb_callback_t uchcom_write_callback; static usb_callback_t uchcom_read_callback; static const struct usb_config uchcom_config_data[UCHCOM_N_TRANSFER] = { [UCHCOM_BULK_DT_WR] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, .bufsize = UCHCOM_BULK_BUF_SIZE, .flags = {.pipe_bof = 1,.force_short_xfer = 1,}, .callback = &uchcom_write_callback, }, [UCHCOM_BULK_DT_RD] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, .bufsize = UCHCOM_BULK_BUF_SIZE, .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, .callback = &uchcom_read_callback, }, [UCHCOM_INTR_DT_RD] = { .type = UE_INTERRUPT, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, .bufsize = 0, /* use wMaxPacketSize */ .callback = &uchcom_intr_callback, }, }; static struct ucom_callback uchcom_callback = { .ucom_cfg_get_status = &uchcom_cfg_get_status, .ucom_cfg_set_dtr = &uchcom_cfg_set_dtr, .ucom_cfg_set_rts = &uchcom_cfg_set_rts, .ucom_cfg_set_break = &uchcom_cfg_set_break, .ucom_cfg_open = &uchcom_cfg_open, .ucom_cfg_param = &uchcom_cfg_param, .ucom_pre_param = &uchcom_pre_param, .ucom_start_read = &uchcom_start_read, .ucom_stop_read = &uchcom_stop_read, .ucom_start_write = &uchcom_start_write, .ucom_stop_write = &uchcom_stop_write, .ucom_poll = &uchcom_poll, .ucom_free = &uchcom_free, }; /* ---------------------------------------------------------------------- * driver entry points */ static int uchcom_probe(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); DPRINTFN(11, "\n"); if (uaa->usb_mode != USB_MODE_HOST) { return (ENXIO); } if (uaa->info.bConfigIndex != UCHCOM_CONFIG_INDEX) { return (ENXIO); } if (uaa->info.bIfaceIndex != UCHCOM_IFACE_INDEX) { return (ENXIO); } return (usbd_lookup_id_by_uaa(uchcom_devs, sizeof(uchcom_devs), uaa)); } static int uchcom_attach(device_t dev) { struct uchcom_softc *sc = device_get_softc(dev); struct usb_attach_arg *uaa = device_get_ivars(dev); int error; uint8_t iface_index; DPRINTFN(11, "\n"); device_set_usb_desc(dev); mtx_init(&sc->sc_mtx, "uchcom", NULL, MTX_DEF); ucom_ref(&sc->sc_super_ucom); sc->sc_udev = uaa->device; switch (uaa->info.idProduct) { case USB_PRODUCT_WCH2_CH341SER: device_printf(dev, "CH340 detected\n"); break; case USB_PRODUCT_WCH2_CH341SER_2: device_printf(dev, "CH341 detected\n"); break; default: device_printf(dev, "New CH340/CH341 product 0x%04x detected\n", uaa->info.idProduct); break; } iface_index = UCHCOM_IFACE_INDEX; error = usbd_transfer_setup(uaa->device, &iface_index, sc->sc_xfer, uchcom_config_data, UCHCOM_N_TRANSFER, sc, &sc->sc_mtx); if (error) { DPRINTF("one or more missing USB endpoints, " "error=%s\n", usbd_errstr(error)); goto detach; } /* clear stall at first run */ mtx_lock(&sc->sc_mtx); - usbd_xfer_set_stall(sc->sc_xfer[UCHCOM_BULK_DT_WR]); - usbd_xfer_set_stall(sc->sc_xfer[UCHCOM_BULK_DT_RD]); + usbd_xfer_set_zlp(sc->sc_xfer[UCHCOM_BULK_DT_WR]); mtx_unlock(&sc->sc_mtx); error = ucom_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc, &uchcom_callback, &sc->sc_mtx); if (error) { goto detach; } ucom_set_pnpinfo_usb(&sc->sc_super_ucom, dev); return (0); detach: uchcom_detach(dev); return (ENXIO); } static int uchcom_detach(device_t dev) { struct uchcom_softc *sc = device_get_softc(dev); DPRINTFN(11, "\n"); ucom_detach(&sc->sc_super_ucom, &sc->sc_ucom); usbd_transfer_unsetup(sc->sc_xfer, UCHCOM_N_TRANSFER); device_claim_softc(dev); uchcom_free_softc(sc); return (0); } UCOM_UNLOAD_DRAIN(uchcom); static void uchcom_free_softc(struct uchcom_softc *sc) { if (ucom_unref(&sc->sc_super_ucom)) { mtx_destroy(&sc->sc_mtx); device_free_softc(sc); } } static void uchcom_free(struct ucom_softc *ucom) { uchcom_free_softc(ucom->sc_parent); } /* ---------------------------------------------------------------------- * low level i/o */ static void uchcom_ctrl_write(struct uchcom_softc *sc, uint8_t reqno, uint16_t value, uint16_t index) { struct usb_device_request req; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = reqno; USETW(req.wValue, value); USETW(req.wIndex, index); USETW(req.wLength, 0); DPRINTF("WR REQ 0x%02X VAL 0x%04X IDX 0x%04X\n", reqno, value, index); ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, &req, NULL, 0, 1000); } static void uchcom_ctrl_read(struct uchcom_softc *sc, uint8_t reqno, uint16_t value, uint16_t index, void *buf, uint16_t buflen) { struct usb_device_request req; req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = reqno; USETW(req.wValue, value); USETW(req.wIndex, index); USETW(req.wLength, buflen); DPRINTF("RD REQ 0x%02X VAL 0x%04X IDX 0x%04X LEN %d\n", reqno, value, index, buflen); ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, &req, buf, USB_SHORT_XFER_OK, 1000); } static void uchcom_write_reg(struct uchcom_softc *sc, uint8_t reg1, uint8_t val1, uint8_t reg2, uint8_t val2) { DPRINTF("0x%02X<-0x%02X, 0x%02X<-0x%02X\n", (unsigned)reg1, (unsigned)val1, (unsigned)reg2, (unsigned)val2); uchcom_ctrl_write( sc, UCHCOM_REQ_WRITE_REG, reg1 | ((uint16_t)reg2 << 8), val1 | ((uint16_t)val2 << 8)); } static void uchcom_read_reg(struct uchcom_softc *sc, uint8_t reg1, uint8_t *rval1, uint8_t reg2, uint8_t *rval2) { uint8_t buf[UCHCOM_INPUT_BUF_SIZE]; uchcom_ctrl_read( sc, UCHCOM_REQ_READ_REG, reg1 | ((uint16_t)reg2 << 8), 0, buf, sizeof(buf)); DPRINTF("0x%02X->0x%02X, 0x%02X->0x%02X\n", (unsigned)reg1, (unsigned)buf[0], (unsigned)reg2, (unsigned)buf[1]); if (rval1) *rval1 = buf[0]; if (rval2) *rval2 = buf[1]; } static void uchcom_get_version(struct uchcom_softc *sc, uint8_t *rver) { uint8_t buf[UCHCOM_INPUT_BUF_SIZE]; uchcom_ctrl_read(sc, UCHCOM_REQ_GET_VERSION, 0, 0, buf, sizeof(buf)); if (rver) *rver = buf[0]; } static void uchcom_get_status(struct uchcom_softc *sc, uint8_t *rval) { uchcom_read_reg(sc, UCHCOM_REG_STAT1, rval, UCHCOM_REG_STAT2, NULL); } static void uchcom_set_dtr_rts_10(struct uchcom_softc *sc, uint8_t val) { uchcom_write_reg(sc, UCHCOM_REG_STAT1, val, UCHCOM_REG_STAT1, val); } static void uchcom_set_dtr_rts_20(struct uchcom_softc *sc, uint8_t val) { uchcom_ctrl_write(sc, UCHCOM_REQ_SET_DTRRTS, val, 0); } /* ---------------------------------------------------------------------- * middle layer */ static void uchcom_update_version(struct uchcom_softc *sc) { uchcom_get_version(sc, &sc->sc_version); DPRINTF("Chip version: 0x%02x\n", sc->sc_version); } static void uchcom_convert_status(struct uchcom_softc *sc, uint8_t cur) { sc->sc_dtr = !(cur & UCHCOM_DTR_MASK); sc->sc_rts = !(cur & UCHCOM_RTS_MASK); cur = ~cur & 0x0F; sc->sc_msr = (cur << 4) | ((sc->sc_msr >> 4) ^ cur); } static void uchcom_update_status(struct uchcom_softc *sc) { uint8_t cur; uchcom_get_status(sc, &cur); uchcom_convert_status(sc, cur); } static void uchcom_set_dtr_rts(struct uchcom_softc *sc) { uint8_t val = 0; if (sc->sc_dtr) val |= UCHCOM_DTR_MASK; if (sc->sc_rts) val |= UCHCOM_RTS_MASK; if (sc->sc_version < UCHCOM_VER_20) uchcom_set_dtr_rts_10(sc, ~val); else uchcom_set_dtr_rts_20(sc, ~val); } static void uchcom_cfg_set_break(struct ucom_softc *ucom, uint8_t onoff) { struct uchcom_softc *sc = ucom->sc_parent; uint8_t brk1; uint8_t brk2; uchcom_read_reg(sc, UCHCOM_REG_BREAK1, &brk1, UCHCOM_REG_LCR1, &brk2); if (onoff) { /* on - clear bits */ brk1 &= ~UCHCOM_BRK_MASK; brk2 &= ~UCHCOM_LCR1_TX; } else { /* off - set bits */ brk1 |= UCHCOM_BRK_MASK; brk2 |= UCHCOM_LCR1_TX; } uchcom_write_reg(sc, UCHCOM_REG_BREAK1, brk1, UCHCOM_REG_LCR1, brk2); } static int uchcom_calc_divider_settings(struct uchcom_divider *dp, uint32_t rate) { const struct uchcom_divider_record *rp; uint32_t div; uint32_t rem; uint32_t mod; uint8_t i; /* find record */ for (i = 0; i != NUM_DIVIDERS; i++) { if (dividers[i].dvr_high >= rate && dividers[i].dvr_low <= rate) { rp = ÷rs[i]; goto found; } } return (-1); found: dp->dv_prescaler = rp->dvr_divider.dv_prescaler; if (rp->dvr_base_clock == UCHCOM_BASE_UNKNOWN) dp->dv_div = rp->dvr_divider.dv_div; else { div = rp->dvr_base_clock / rate; rem = rp->dvr_base_clock % rate; if (div == 0 || div >= 0xFF) return (-1); if ((rem << 1) >= rate) div += 1; dp->dv_div = (uint8_t)-div; } mod = (UCHCOM_BPS_MOD_BASE / rate) + UCHCOM_BPS_MOD_BASE_OFS; mod = mod + (mod / 2); dp->dv_mod = (mod + 0xFF) / 0x100; return (0); } static void uchcom_set_baudrate(struct uchcom_softc *sc, uint32_t rate) { struct uchcom_divider dv; if (uchcom_calc_divider_settings(&dv, rate)) return; /* * According to linux code we need to set bit 7 of UCHCOM_REG_BPS_PRE, * otherwise the chip will buffer data. */ uchcom_write_reg(sc, UCHCOM_REG_BPS_PRE, dv.dv_prescaler | 0x80, UCHCOM_REG_BPS_DIV, dv.dv_div); uchcom_write_reg(sc, UCHCOM_REG_BPS_MOD, dv.dv_mod, UCHCOM_REG_BPS_PAD, 0); } /* ---------------------------------------------------------------------- * methods for ucom */ static void uchcom_cfg_get_status(struct ucom_softc *ucom, uint8_t *lsr, uint8_t *msr) { struct uchcom_softc *sc = ucom->sc_parent; DPRINTF("\n"); /* XXX Note: sc_lsr is always zero */ *lsr = sc->sc_lsr; *msr = sc->sc_msr; } static void uchcom_cfg_set_dtr(struct ucom_softc *ucom, uint8_t onoff) { struct uchcom_softc *sc = ucom->sc_parent; DPRINTF("onoff = %d\n", onoff); sc->sc_dtr = onoff; uchcom_set_dtr_rts(sc); } static void uchcom_cfg_set_rts(struct ucom_softc *ucom, uint8_t onoff) { struct uchcom_softc *sc = ucom->sc_parent; DPRINTF("onoff = %d\n", onoff); sc->sc_rts = onoff; uchcom_set_dtr_rts(sc); } static void uchcom_cfg_open(struct ucom_softc *ucom) { struct uchcom_softc *sc = ucom->sc_parent; DPRINTF("\n"); uchcom_update_version(sc); uchcom_update_status(sc); } static int uchcom_pre_param(struct ucom_softc *ucom, struct termios *t) { struct uchcom_divider dv; switch (t->c_cflag & CSIZE) { case CS8: break; default: return (EIO); } if ((t->c_cflag & CSTOPB) != 0) return (EIO); if ((t->c_cflag & PARENB) != 0) return (EIO); if (uchcom_calc_divider_settings(&dv, t->c_ospeed)) { return (EIO); } return (0); /* success */ } static void uchcom_cfg_param(struct ucom_softc *ucom, struct termios *t) { struct uchcom_softc *sc = ucom->sc_parent; uchcom_get_version(sc, NULL); uchcom_ctrl_write(sc, UCHCOM_REQ_RESET, 0, 0); uchcom_set_baudrate(sc, t->c_ospeed); if (sc->sc_version < UCHCOM_VER_30) { uchcom_read_reg(sc, UCHCOM_REG_LCR1, NULL, UCHCOM_REG_LCR2, NULL); uchcom_write_reg(sc, UCHCOM_REG_LCR1, 0x50, UCHCOM_REG_LCR2, 0x00); } else { /* * Set up line control: * - enable transmit and receive * - set 8n1 mode * To do: support other sizes, parity, stop bits. */ uchcom_write_reg(sc, UCHCOM_REG_LCR1, UCHCOM_LCR1_RX | UCHCOM_LCR1_TX | UCHCOM_LCR1_CS8, UCHCOM_REG_LCR2, 0x00); } uchcom_update_status(sc); uchcom_ctrl_write(sc, UCHCOM_REQ_RESET, 0x501f, 0xd90a); uchcom_set_baudrate(sc, t->c_ospeed); uchcom_set_dtr_rts(sc); uchcom_update_status(sc); } static void uchcom_start_read(struct ucom_softc *ucom) { struct uchcom_softc *sc = ucom->sc_parent; /* start interrupt endpoint */ usbd_transfer_start(sc->sc_xfer[UCHCOM_INTR_DT_RD]); /* start read endpoint */ usbd_transfer_start(sc->sc_xfer[UCHCOM_BULK_DT_RD]); } static void uchcom_stop_read(struct ucom_softc *ucom) { struct uchcom_softc *sc = ucom->sc_parent; /* stop interrupt endpoint */ usbd_transfer_stop(sc->sc_xfer[UCHCOM_INTR_DT_RD]); /* stop read endpoint */ usbd_transfer_stop(sc->sc_xfer[UCHCOM_BULK_DT_RD]); } static void uchcom_start_write(struct ucom_softc *ucom) { struct uchcom_softc *sc = ucom->sc_parent; usbd_transfer_start(sc->sc_xfer[UCHCOM_BULK_DT_WR]); } static void uchcom_stop_write(struct ucom_softc *ucom) { struct uchcom_softc *sc = ucom->sc_parent; usbd_transfer_stop(sc->sc_xfer[UCHCOM_BULK_DT_WR]); } /* ---------------------------------------------------------------------- * callback when the modem status is changed. */ static void uchcom_intr_callback(struct usb_xfer *xfer, usb_error_t error) { struct uchcom_softc *sc = usbd_xfer_softc(xfer); struct usb_page_cache *pc; uint8_t buf[UCHCOM_INTR_LEAST]; int actlen; usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: DPRINTF("actlen = %u\n", actlen); if (actlen >= UCHCOM_INTR_LEAST) { pc = usbd_xfer_get_frame(xfer, 0); usbd_copy_out(pc, 0, buf, UCHCOM_INTR_LEAST); DPRINTF("data = 0x%02X 0x%02X 0x%02X 0x%02X\n", (unsigned)buf[0], (unsigned)buf[1], (unsigned)buf[2], (unsigned)buf[3]); uchcom_convert_status(sc, buf[UCHCOM_INTR_STAT1]); ucom_status_change(&sc->sc_ucom); } case USB_ST_SETUP: tr_setup: usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); usbd_transfer_submit(xfer); break; default: /* Error */ if (error != USB_ERR_CANCELLED) { /* try to clear stall first */ usbd_xfer_set_stall(xfer); goto tr_setup; } break; } } static void uchcom_write_callback(struct usb_xfer *xfer, usb_error_t error) { struct uchcom_softc *sc = usbd_xfer_softc(xfer); struct usb_page_cache *pc; uint32_t actlen; switch (USB_GET_STATE(xfer)) { case USB_ST_SETUP: case USB_ST_TRANSFERRED: tr_setup: + if (usbd_xfer_get_and_clr_zlp(xfer)) + break; + pc = usbd_xfer_get_frame(xfer, 0); if (ucom_get_data(&sc->sc_ucom, pc, 0, usbd_xfer_max_len(xfer), &actlen)) { DPRINTF("actlen = %d\n", actlen); usbd_xfer_set_frame_len(xfer, 0, actlen); usbd_transfer_submit(xfer); } break; default: /* Error */ if (error != USB_ERR_CANCELLED) { /* try to clear stall first */ usbd_xfer_set_stall(xfer); goto tr_setup; } break; } } static void uchcom_read_callback(struct usb_xfer *xfer, usb_error_t error) { struct uchcom_softc *sc = usbd_xfer_softc(xfer); struct usb_page_cache *pc; int actlen; usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: if (actlen > 0) { pc = usbd_xfer_get_frame(xfer, 0); ucom_put_data(&sc->sc_ucom, pc, 0, actlen); } case USB_ST_SETUP: tr_setup: usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); usbd_transfer_submit(xfer); break; default: /* Error */ if (error != USB_ERR_CANCELLED) { /* try to clear stall first */ usbd_xfer_set_stall(xfer); goto tr_setup; } break; } } static void uchcom_poll(struct ucom_softc *ucom) { struct uchcom_softc *sc = ucom->sc_parent; usbd_transfer_poll(sc->sc_xfer, UCHCOM_N_TRANSFER); } static device_method_t uchcom_methods[] = { /* Device interface */ DEVMETHOD(device_probe, uchcom_probe), DEVMETHOD(device_attach, uchcom_attach), DEVMETHOD(device_detach, uchcom_detach), DEVMETHOD_END }; static driver_t uchcom_driver = { .name = "uchcom", .methods = uchcom_methods, .size = sizeof(struct uchcom_softc) }; static devclass_t uchcom_devclass; DRIVER_MODULE(uchcom, uhub, uchcom_driver, uchcom_devclass, NULL, 0); MODULE_DEPEND(uchcom, ucom, 1, 1, 1); MODULE_DEPEND(uchcom, usb, 1, 1, 1); MODULE_VERSION(uchcom, 1); USB_PNP_HOST_INFO(uchcom_devs); diff --git a/sys/dev/usb/serial/ufoma.c b/sys/dev/usb/serial/ufoma.c index bc3f00a7b473..ca52752accad 100644 --- a/sys/dev/usb/serial/ufoma.c +++ b/sys/dev/usb/serial/ufoma.c @@ -1,1263 +1,1265 @@ /* $NetBSD: umodem.c,v 1.45 2002/09/23 05:51:23 simonb Exp $ */ #include __FBSDID("$FreeBSD$"); #define UFOMA_HANDSFREE /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD AND BSD-2-Clause-NetBSD * * Copyright (c) 2005, Takanori Watanabe All rights reserved. * Copyright (c) 2003 M. Warner Losh * * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 THE AUTHOR OR CONTRIBUTORS 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. */ /*- * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (lennart@augustsson.net) at * Carlstedt Research & Technology. * * 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. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 THE FOUNDATION OR CONTRIBUTORS * 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. */ /* * Comm Class spec: http://www.usb.org/developers/devclass_docs/usbccs10.pdf * http://www.usb.org/developers/devclass_docs/usbcdc11.pdf */ /* * TODO: * - Implement a Call Device for modems without multiplexed commands. */ /* * NOTE: all function names beginning like "ufoma_cfg_" can only * be called from within the config thread function ! */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "usbdevs.h" #define USB_DEBUG_VAR usb_debug #include #include #include typedef struct ufoma_mobile_acm_descriptor { uint8_t bFunctionLength; uint8_t bDescriptorType; uint8_t bDescriptorSubtype; uint8_t bType; uint8_t bMode[1]; } __packed usb_mcpc_acm_descriptor; #define UISUBCLASS_MCPC 0x88 #define UDESC_VS_INTERFACE 0x44 #define UDESCSUB_MCPC_ACM 0x11 #define UMCPC_ACM_TYPE_AB1 0x1 #define UMCPC_ACM_TYPE_AB2 0x2 #define UMCPC_ACM_TYPE_AB5 0x5 #define UMCPC_ACM_TYPE_AB6 0x6 #define UMCPC_ACM_MODE_DEACTIVATED 0x0 #define UMCPC_ACM_MODE_MODEM 0x1 #define UMCPC_ACM_MODE_ATCOMMAND 0x2 #define UMCPC_ACM_MODE_OBEX 0x60 #define UMCPC_ACM_MODE_VENDOR1 0xc0 #define UMCPC_ACM_MODE_VENDOR2 0xfe #define UMCPC_ACM_MODE_UNLINKED 0xff #define UMCPC_CM_MOBILE_ACM 0x0 #define UMCPC_ACTIVATE_MODE 0x60 #define UMCPC_GET_MODETABLE 0x61 #define UMCPC_SET_LINK 0x62 #define UMCPC_CLEAR_LINK 0x63 #define UMCPC_REQUEST_ACKNOWLEDGE 0x31 #define UFOMA_MAX_TIMEOUT 15 /* standard says 10 seconds */ #define UFOMA_CMD_BUF_SIZE 64 /* bytes */ #define UFOMA_BULK_BUF_SIZE 1024 /* bytes */ enum { UFOMA_CTRL_ENDPT_INTR, UFOMA_CTRL_ENDPT_READ, UFOMA_CTRL_ENDPT_WRITE, UFOMA_CTRL_ENDPT_MAX, }; enum { UFOMA_BULK_ENDPT_WRITE, UFOMA_BULK_ENDPT_READ, UFOMA_BULK_ENDPT_MAX, }; struct ufoma_softc { struct ucom_super_softc sc_super_ucom; struct ucom_softc sc_ucom; struct cv sc_cv; struct mtx sc_mtx; struct usb_xfer *sc_ctrl_xfer[UFOMA_CTRL_ENDPT_MAX]; struct usb_xfer *sc_bulk_xfer[UFOMA_BULK_ENDPT_MAX]; uint8_t *sc_modetable; device_t sc_dev; struct usb_device *sc_udev; uint32_t sc_unit; uint16_t sc_line; uint8_t sc_num_msg; uint8_t sc_nobulk; uint8_t sc_ctrl_iface_no; uint8_t sc_ctrl_iface_index; uint8_t sc_data_iface_no; uint8_t sc_data_iface_index; uint8_t sc_cm_cap; uint8_t sc_acm_cap; uint8_t sc_lsr; uint8_t sc_msr; uint8_t sc_modetoactivate; uint8_t sc_currentmode; }; /* prototypes */ static device_probe_t ufoma_probe; static device_attach_t ufoma_attach; static device_detach_t ufoma_detach; static void ufoma_free_softc(struct ufoma_softc *); static usb_callback_t ufoma_ctrl_read_callback; static usb_callback_t ufoma_ctrl_write_callback; static usb_callback_t ufoma_intr_callback; static usb_callback_t ufoma_bulk_write_callback; static usb_callback_t ufoma_bulk_read_callback; static void *ufoma_get_intconf(struct usb_config_descriptor *, struct usb_interface_descriptor *, uint8_t, uint8_t); static void ufoma_cfg_link_state(struct ufoma_softc *); static void ufoma_cfg_activate_state(struct ufoma_softc *, uint16_t); static void ufoma_free(struct ucom_softc *); static void ufoma_cfg_open(struct ucom_softc *); static void ufoma_cfg_close(struct ucom_softc *); static void ufoma_cfg_set_break(struct ucom_softc *, uint8_t); static void ufoma_cfg_get_status(struct ucom_softc *, uint8_t *, uint8_t *); static void ufoma_cfg_set_dtr(struct ucom_softc *, uint8_t); static void ufoma_cfg_set_rts(struct ucom_softc *, uint8_t); static int ufoma_pre_param(struct ucom_softc *, struct termios *); static void ufoma_cfg_param(struct ucom_softc *, struct termios *); static int ufoma_modem_setup(device_t, struct ufoma_softc *, struct usb_attach_arg *); static void ufoma_start_read(struct ucom_softc *); static void ufoma_stop_read(struct ucom_softc *); static void ufoma_start_write(struct ucom_softc *); static void ufoma_stop_write(struct ucom_softc *); static void ufoma_poll(struct ucom_softc *ucom); /*sysctl stuff*/ static int ufoma_sysctl_support(SYSCTL_HANDLER_ARGS); static int ufoma_sysctl_current(SYSCTL_HANDLER_ARGS); static int ufoma_sysctl_open(SYSCTL_HANDLER_ARGS); static const struct usb_config ufoma_ctrl_config[UFOMA_CTRL_ENDPT_MAX] = { [UFOMA_CTRL_ENDPT_INTR] = { .type = UE_INTERRUPT, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, .bufsize = sizeof(struct usb_cdc_notification), .callback = &ufoma_intr_callback, }, [UFOMA_CTRL_ENDPT_READ] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = UE_DIR_ANY, .bufsize = (sizeof(struct usb_device_request) + UFOMA_CMD_BUF_SIZE), .flags = {.short_xfer_ok = 1,}, .callback = &ufoma_ctrl_read_callback, .timeout = 1000, /* 1 second */ }, [UFOMA_CTRL_ENDPT_WRITE] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = UE_DIR_ANY, .bufsize = (sizeof(struct usb_device_request) + 1), .callback = &ufoma_ctrl_write_callback, .timeout = 1000, /* 1 second */ }, }; static const struct usb_config ufoma_bulk_config[UFOMA_BULK_ENDPT_MAX] = { [UFOMA_BULK_ENDPT_WRITE] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, .bufsize = UFOMA_BULK_BUF_SIZE, .flags = {.pipe_bof = 1,.force_short_xfer = 1,}, .callback = &ufoma_bulk_write_callback, }, [UFOMA_BULK_ENDPT_READ] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, .bufsize = UFOMA_BULK_BUF_SIZE, .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, .callback = &ufoma_bulk_read_callback, }, }; static const struct ucom_callback ufoma_callback = { .ucom_cfg_get_status = &ufoma_cfg_get_status, .ucom_cfg_set_dtr = &ufoma_cfg_set_dtr, .ucom_cfg_set_rts = &ufoma_cfg_set_rts, .ucom_cfg_set_break = &ufoma_cfg_set_break, .ucom_cfg_param = &ufoma_cfg_param, .ucom_cfg_open = &ufoma_cfg_open, .ucom_cfg_close = &ufoma_cfg_close, .ucom_pre_param = &ufoma_pre_param, .ucom_start_read = &ufoma_start_read, .ucom_stop_read = &ufoma_stop_read, .ucom_start_write = &ufoma_start_write, .ucom_stop_write = &ufoma_stop_write, .ucom_poll = &ufoma_poll, .ucom_free = &ufoma_free, }; static device_method_t ufoma_methods[] = { /* Device methods */ DEVMETHOD(device_probe, ufoma_probe), DEVMETHOD(device_attach, ufoma_attach), DEVMETHOD(device_detach, ufoma_detach), DEVMETHOD_END }; static devclass_t ufoma_devclass; static driver_t ufoma_driver = { .name = "ufoma", .methods = ufoma_methods, .size = sizeof(struct ufoma_softc), }; static const STRUCT_USB_HOST_ID ufoma_devs[] = { {USB_IFACE_CLASS(UICLASS_CDC), USB_IFACE_SUBCLASS(UISUBCLASS_MCPC),}, }; DRIVER_MODULE(ufoma, uhub, ufoma_driver, ufoma_devclass, NULL, 0); MODULE_DEPEND(ufoma, ucom, 1, 1, 1); MODULE_DEPEND(ufoma, usb, 1, 1, 1); MODULE_VERSION(ufoma, 1); USB_PNP_HOST_INFO(ufoma_devs); static int ufoma_probe(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); struct usb_interface_descriptor *id; struct usb_config_descriptor *cd; usb_mcpc_acm_descriptor *mad; int error; if (uaa->usb_mode != USB_MODE_HOST) return (ENXIO); error = usbd_lookup_id_by_uaa(ufoma_devs, sizeof(ufoma_devs), uaa); if (error) return (error); id = usbd_get_interface_descriptor(uaa->iface); cd = usbd_get_config_descriptor(uaa->device); if (id == NULL || cd == NULL) return (ENXIO); mad = ufoma_get_intconf(cd, id, UDESC_VS_INTERFACE, UDESCSUB_MCPC_ACM); if (mad == NULL) return (ENXIO); #ifndef UFOMA_HANDSFREE if ((mad->bType == UMCPC_ACM_TYPE_AB5) || (mad->bType == UMCPC_ACM_TYPE_AB6)) return (ENXIO); #endif return (BUS_PROBE_GENERIC); } static int ufoma_attach(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); struct ufoma_softc *sc = device_get_softc(dev); struct usb_config_descriptor *cd; struct usb_interface_descriptor *id; struct sysctl_ctx_list *sctx; struct sysctl_oid *soid; usb_mcpc_acm_descriptor *mad; uint8_t elements; int32_t error; sc->sc_udev = uaa->device; sc->sc_dev = dev; sc->sc_unit = device_get_unit(dev); mtx_init(&sc->sc_mtx, "ufoma", NULL, MTX_DEF); ucom_ref(&sc->sc_super_ucom); cv_init(&sc->sc_cv, "CWAIT"); device_set_usb_desc(dev); DPRINTF("\n"); /* setup control transfers */ cd = usbd_get_config_descriptor(uaa->device); id = usbd_get_interface_descriptor(uaa->iface); sc->sc_ctrl_iface_no = id->bInterfaceNumber; sc->sc_ctrl_iface_index = uaa->info.bIfaceIndex; error = usbd_transfer_setup(uaa->device, &sc->sc_ctrl_iface_index, sc->sc_ctrl_xfer, ufoma_ctrl_config, UFOMA_CTRL_ENDPT_MAX, sc, &sc->sc_mtx); if (error) { device_printf(dev, "allocating control USB " "transfers failed\n"); goto detach; } mad = ufoma_get_intconf(cd, id, UDESC_VS_INTERFACE, UDESCSUB_MCPC_ACM); if (mad == NULL) { goto detach; } if (mad->bFunctionLength < sizeof(*mad)) { device_printf(dev, "invalid MAD descriptor\n"); goto detach; } if ((mad->bType == UMCPC_ACM_TYPE_AB5) || (mad->bType == UMCPC_ACM_TYPE_AB6)) { sc->sc_nobulk = 1; } else { sc->sc_nobulk = 0; if (ufoma_modem_setup(dev, sc, uaa)) { goto detach; } } elements = (mad->bFunctionLength - sizeof(*mad) + 1); /* initialize mode variables */ sc->sc_modetable = malloc(elements + 1, M_USBDEV, M_WAITOK); if (sc->sc_modetable == NULL) { goto detach; } sc->sc_modetable[0] = (elements + 1); memcpy(&sc->sc_modetable[1], mad->bMode, elements); sc->sc_currentmode = UMCPC_ACM_MODE_UNLINKED; sc->sc_modetoactivate = mad->bMode[0]; /* clear stall at first run, if any */ mtx_lock(&sc->sc_mtx); - usbd_xfer_set_stall(sc->sc_bulk_xfer[UFOMA_BULK_ENDPT_WRITE]); - usbd_xfer_set_stall(sc->sc_bulk_xfer[UFOMA_BULK_ENDPT_READ]); + usbd_xfer_set_zlp(sc->sc_bulk_xfer[UFOMA_BULK_ENDPT_WRITE]); mtx_unlock(&sc->sc_mtx); error = ucom_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc, &ufoma_callback, &sc->sc_mtx); if (error) { DPRINTF("ucom_attach failed\n"); goto detach; } ucom_set_pnpinfo_usb(&sc->sc_super_ucom, dev); /*Sysctls*/ sctx = device_get_sysctl_ctx(dev); soid = device_get_sysctl_tree(dev); SYSCTL_ADD_PROC(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "supportmode", CTLFLAG_RD | CTLTYPE_STRING | CTLFLAG_MPSAFE, sc, 0, ufoma_sysctl_support, "A", "Supporting port role"); SYSCTL_ADD_PROC(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "currentmode", CTLFLAG_RD | CTLTYPE_STRING | CTLFLAG_MPSAFE, sc, 0, ufoma_sysctl_current, "A", "Current port role"); SYSCTL_ADD_PROC(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "openmode", CTLFLAG_RW | CTLTYPE_STRING | CTLFLAG_MPSAFE, sc, 0, ufoma_sysctl_open, "A", "Mode to transit when port is opened"); SYSCTL_ADD_UINT(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "comunit", CTLFLAG_RD, &(sc->sc_super_ucom.sc_unit), 0, "Unit number as USB serial"); return (0); /* success */ detach: ufoma_detach(dev); return (ENXIO); /* failure */ } static int ufoma_detach(device_t dev) { struct ufoma_softc *sc = device_get_softc(dev); ucom_detach(&sc->sc_super_ucom, &sc->sc_ucom); usbd_transfer_unsetup(sc->sc_ctrl_xfer, UFOMA_CTRL_ENDPT_MAX); usbd_transfer_unsetup(sc->sc_bulk_xfer, UFOMA_BULK_ENDPT_MAX); if (sc->sc_modetable) { free(sc->sc_modetable, M_USBDEV); } cv_destroy(&sc->sc_cv); device_claim_softc(dev); ufoma_free_softc(sc); return (0); } UCOM_UNLOAD_DRAIN(ufoma); static void ufoma_free_softc(struct ufoma_softc *sc) { if (ucom_unref(&sc->sc_super_ucom)) { mtx_destroy(&sc->sc_mtx); device_free_softc(sc); } } static void ufoma_free(struct ucom_softc *ucom) { ufoma_free_softc(ucom->sc_parent); } static void * ufoma_get_intconf(struct usb_config_descriptor *cd, struct usb_interface_descriptor *id, uint8_t type, uint8_t subtype) { struct usb_descriptor *desc = (void *)id; while ((desc = usb_desc_foreach(cd, desc))) { if (desc->bDescriptorType == UDESC_INTERFACE) { return (NULL); } if ((desc->bDescriptorType == type) && (desc->bDescriptorSubtype == subtype)) { break; } } return (desc); } static void ufoma_cfg_link_state(struct ufoma_softc *sc) { struct usb_device_request req; int32_t error; req.bmRequestType = UT_WRITE_VENDOR_INTERFACE; req.bRequest = UMCPC_SET_LINK; USETW(req.wValue, UMCPC_CM_MOBILE_ACM); USETW(req.wIndex, sc->sc_ctrl_iface_no); USETW(req.wLength, sc->sc_modetable[0]); ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, &req, sc->sc_modetable, 0, 1000); error = cv_timedwait(&sc->sc_cv, &sc->sc_mtx, hz); if (error) { DPRINTF("NO response\n"); } } static void ufoma_cfg_activate_state(struct ufoma_softc *sc, uint16_t state) { struct usb_device_request req; int32_t error; req.bmRequestType = UT_WRITE_VENDOR_INTERFACE; req.bRequest = UMCPC_ACTIVATE_MODE; USETW(req.wValue, state); USETW(req.wIndex, sc->sc_ctrl_iface_no); USETW(req.wLength, 0); ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, &req, NULL, 0, 1000); error = cv_timedwait(&sc->sc_cv, &sc->sc_mtx, (UFOMA_MAX_TIMEOUT * hz)); if (error) { DPRINTF("No response\n"); } } static void ufoma_ctrl_read_callback(struct usb_xfer *xfer, usb_error_t error) { struct ufoma_softc *sc = usbd_xfer_softc(xfer); struct usb_device_request req; struct usb_page_cache *pc0, *pc1; int len, aframes, nframes; usbd_xfer_status(xfer, NULL, NULL, &aframes, &nframes); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: tr_transferred: if (aframes != nframes) goto tr_setup; pc1 = usbd_xfer_get_frame(xfer, 1); len = usbd_xfer_frame_len(xfer, 1); if (len > 0) ucom_put_data(&sc->sc_ucom, pc1, 0, len); /* FALLTHROUGH */ case USB_ST_SETUP: tr_setup: if (sc->sc_num_msg) { sc->sc_num_msg--; req.bmRequestType = UT_READ_CLASS_INTERFACE; req.bRequest = UCDC_GET_ENCAPSULATED_RESPONSE; USETW(req.wIndex, sc->sc_ctrl_iface_no); USETW(req.wValue, 0); USETW(req.wLength, UFOMA_CMD_BUF_SIZE); pc0 = usbd_xfer_get_frame(xfer, 0); usbd_copy_in(pc0, 0, &req, sizeof(req)); usbd_xfer_set_frame_len(xfer, 0, sizeof(req)); usbd_xfer_set_frame_len(xfer, 1, UFOMA_CMD_BUF_SIZE); usbd_xfer_set_frames(xfer, 2); usbd_transfer_submit(xfer); } return; default: /* Error */ DPRINTF("error = %s\n", usbd_errstr(error)); if (error == USB_ERR_CANCELLED) { return; } goto tr_transferred; } } static void ufoma_ctrl_write_callback(struct usb_xfer *xfer, usb_error_t error) { struct ufoma_softc *sc = usbd_xfer_softc(xfer); struct usb_device_request req; struct usb_page_cache *pc; uint32_t actlen; switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: tr_transferred: case USB_ST_SETUP: pc = usbd_xfer_get_frame(xfer, 1); if (ucom_get_data(&sc->sc_ucom, pc, 0, 1, &actlen)) { req.bmRequestType = UT_WRITE_CLASS_INTERFACE; req.bRequest = UCDC_SEND_ENCAPSULATED_COMMAND; USETW(req.wIndex, sc->sc_ctrl_iface_no); USETW(req.wValue, 0); USETW(req.wLength, 1); 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_frame_len(xfer, 1, 1); usbd_xfer_set_frames(xfer, 2); usbd_transfer_submit(xfer); } return; default: /* Error */ DPRINTF("error = %s\n", usbd_errstr(error)); if (error == USB_ERR_CANCELLED) { return; } goto tr_transferred; } } static void ufoma_intr_callback(struct usb_xfer *xfer, usb_error_t error) { struct ufoma_softc *sc = usbd_xfer_softc(xfer); struct usb_cdc_notification pkt; struct usb_page_cache *pc; uint16_t wLen; uint16_t temp; uint8_t mstatus; int actlen; usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: if (actlen < 8) { DPRINTF("too short message\n"); goto tr_setup; } if (actlen > (int)sizeof(pkt)) { DPRINTF("truncating message\n"); actlen = sizeof(pkt); } pc = usbd_xfer_get_frame(xfer, 0); usbd_copy_out(pc, 0, &pkt, actlen); actlen -= 8; wLen = UGETW(pkt.wLength); if (actlen > wLen) { actlen = wLen; } if ((pkt.bmRequestType == UT_READ_VENDOR_INTERFACE) && (pkt.bNotification == UMCPC_REQUEST_ACKNOWLEDGE)) { temp = UGETW(pkt.wValue); sc->sc_currentmode = (temp >> 8); if (!(temp & 0xff)) { DPRINTF("Mode change failed!\n"); } cv_signal(&sc->sc_cv); } if (pkt.bmRequestType != UCDC_NOTIFICATION) { goto tr_setup; } switch (pkt.bNotification) { case UCDC_N_RESPONSE_AVAILABLE: if (!(sc->sc_nobulk)) { DPRINTF("Wrong serial state!\n"); break; } if (sc->sc_num_msg != 0xFF) { sc->sc_num_msg++; } usbd_transfer_start(sc->sc_ctrl_xfer[UFOMA_CTRL_ENDPT_READ]); break; case UCDC_N_SERIAL_STATE: if (sc->sc_nobulk) { DPRINTF("Wrong serial state!\n"); break; } /* * Set the serial state in ucom driver based on * the bits from the notify message */ if (actlen < 2) { DPRINTF("invalid notification " "length, %d bytes!\n", actlen); break; } DPRINTF("notify bytes = 0x%02x, 0x%02x\n", pkt.data[0], pkt.data[1]); /* currently, lsr is always zero. */ sc->sc_lsr = 0; sc->sc_msr = 0; mstatus = pkt.data[0]; if (mstatus & UCDC_N_SERIAL_RI) { sc->sc_msr |= SER_RI; } if (mstatus & UCDC_N_SERIAL_DSR) { sc->sc_msr |= SER_DSR; } if (mstatus & UCDC_N_SERIAL_DCD) { sc->sc_msr |= SER_DCD; } ucom_status_change(&sc->sc_ucom); break; default: break; } case USB_ST_SETUP: tr_setup: usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); usbd_transfer_submit(xfer); return; default: /* Error */ if (error != USB_ERR_CANCELLED) { /* try to clear stall first */ usbd_xfer_set_stall(xfer); goto tr_setup; } return; } } static void ufoma_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error) { struct ufoma_softc *sc = usbd_xfer_softc(xfer); struct usb_page_cache *pc; uint32_t actlen; switch (USB_GET_STATE(xfer)) { case USB_ST_SETUP: case USB_ST_TRANSFERRED: tr_setup: + if (usbd_xfer_get_and_clr_zlp(xfer)) + break; + pc = usbd_xfer_get_frame(xfer, 0); if (ucom_get_data(&sc->sc_ucom, pc, 0, UFOMA_BULK_BUF_SIZE, &actlen)) { usbd_xfer_set_frame_len(xfer, 0, actlen); usbd_transfer_submit(xfer); } - return; + break; default: /* Error */ if (error != USB_ERR_CANCELLED) { /* try to clear stall first */ usbd_xfer_set_stall(xfer); goto tr_setup; } - return; + break; } } static void ufoma_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error) { struct ufoma_softc *sc = usbd_xfer_softc(xfer); struct usb_page_cache *pc; int actlen; usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: pc = usbd_xfer_get_frame(xfer, 0); ucom_put_data(&sc->sc_ucom, pc, 0, actlen); case USB_ST_SETUP: tr_setup: usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); usbd_transfer_submit(xfer); return; default: /* Error */ if (error != USB_ERR_CANCELLED) { /* try to clear stall first */ usbd_xfer_set_stall(xfer); goto tr_setup; } return; } } static void ufoma_cfg_open(struct ucom_softc *ucom) { struct ufoma_softc *sc = ucom->sc_parent; /* empty input queue */ if (sc->sc_num_msg != 0xFF) { sc->sc_num_msg++; } if (sc->sc_currentmode == UMCPC_ACM_MODE_UNLINKED) { ufoma_cfg_link_state(sc); } if (sc->sc_currentmode == UMCPC_ACM_MODE_DEACTIVATED) { ufoma_cfg_activate_state(sc, sc->sc_modetoactivate); } } static void ufoma_cfg_close(struct ucom_softc *ucom) { struct ufoma_softc *sc = ucom->sc_parent; ufoma_cfg_activate_state(sc, UMCPC_ACM_MODE_DEACTIVATED); } static void ufoma_cfg_set_break(struct ucom_softc *ucom, uint8_t onoff) { struct ufoma_softc *sc = ucom->sc_parent; struct usb_device_request req; uint16_t wValue; if (sc->sc_nobulk || (sc->sc_currentmode == UMCPC_ACM_MODE_OBEX)) { return; } if (!(sc->sc_acm_cap & USB_CDC_ACM_HAS_BREAK)) { return; } wValue = onoff ? UCDC_BREAK_ON : UCDC_BREAK_OFF; req.bmRequestType = UT_WRITE_CLASS_INTERFACE; req.bRequest = UCDC_SEND_BREAK; USETW(req.wValue, wValue); req.wIndex[0] = sc->sc_ctrl_iface_no; req.wIndex[1] = 0; USETW(req.wLength, 0); ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, &req, NULL, 0, 1000); } static void ufoma_cfg_get_status(struct ucom_softc *ucom, uint8_t *lsr, uint8_t *msr) { struct ufoma_softc *sc = ucom->sc_parent; /* XXX Note: sc_lsr is always zero */ *lsr = sc->sc_lsr; *msr = sc->sc_msr; } static void ufoma_cfg_set_line_state(struct ufoma_softc *sc) { struct usb_device_request req; /* Don't send line state emulation request for OBEX port */ if (sc->sc_currentmode == UMCPC_ACM_MODE_OBEX) { return; } req.bmRequestType = UT_WRITE_CLASS_INTERFACE; req.bRequest = UCDC_SET_CONTROL_LINE_STATE; USETW(req.wValue, sc->sc_line); req.wIndex[0] = sc->sc_ctrl_iface_no; req.wIndex[1] = 0; USETW(req.wLength, 0); ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, &req, NULL, 0, 1000); } static void ufoma_cfg_set_dtr(struct ucom_softc *ucom, uint8_t onoff) { struct ufoma_softc *sc = ucom->sc_parent; if (sc->sc_nobulk) { return; } if (onoff) sc->sc_line |= UCDC_LINE_DTR; else sc->sc_line &= ~UCDC_LINE_DTR; ufoma_cfg_set_line_state(sc); } static void ufoma_cfg_set_rts(struct ucom_softc *ucom, uint8_t onoff) { struct ufoma_softc *sc = ucom->sc_parent; if (sc->sc_nobulk) { return; } if (onoff) sc->sc_line |= UCDC_LINE_RTS; else sc->sc_line &= ~UCDC_LINE_RTS; ufoma_cfg_set_line_state(sc); } static int ufoma_pre_param(struct ucom_softc *ucom, struct termios *t) { return (0); /* we accept anything */ } static void ufoma_cfg_param(struct ucom_softc *ucom, struct termios *t) { struct ufoma_softc *sc = ucom->sc_parent; struct usb_device_request req; struct usb_cdc_line_state ls; if (sc->sc_nobulk || (sc->sc_currentmode == UMCPC_ACM_MODE_OBEX)) { return; } DPRINTF("\n"); memset(&ls, 0, sizeof(ls)); USETDW(ls.dwDTERate, t->c_ospeed); if (t->c_cflag & CSTOPB) { ls.bCharFormat = UCDC_STOP_BIT_2; } else { ls.bCharFormat = UCDC_STOP_BIT_1; } if (t->c_cflag & PARENB) { if (t->c_cflag & PARODD) { ls.bParityType = UCDC_PARITY_ODD; } else { ls.bParityType = UCDC_PARITY_EVEN; } } else { ls.bParityType = UCDC_PARITY_NONE; } switch (t->c_cflag & CSIZE) { case CS5: ls.bDataBits = 5; break; case CS6: ls.bDataBits = 6; break; case CS7: ls.bDataBits = 7; break; case CS8: ls.bDataBits = 8; break; } req.bmRequestType = UT_WRITE_CLASS_INTERFACE; req.bRequest = UCDC_SET_LINE_CODING; USETW(req.wValue, 0); req.wIndex[0] = sc->sc_ctrl_iface_no; req.wIndex[1] = 0; USETW(req.wLength, UCDC_LINE_STATE_LENGTH); ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, &req, &ls, 0, 1000); } static int ufoma_modem_setup(device_t dev, struct ufoma_softc *sc, struct usb_attach_arg *uaa) { struct usb_config_descriptor *cd; struct usb_cdc_acm_descriptor *acm; struct usb_cdc_cm_descriptor *cmd; struct usb_interface_descriptor *id; struct usb_interface *iface; uint8_t i; int32_t error; cd = usbd_get_config_descriptor(uaa->device); id = usbd_get_interface_descriptor(uaa->iface); cmd = ufoma_get_intconf(cd, id, UDESC_CS_INTERFACE, UDESCSUB_CDC_CM); if ((cmd == NULL) || (cmd->bLength < sizeof(*cmd))) { return (EINVAL); } sc->sc_cm_cap = cmd->bmCapabilities; sc->sc_data_iface_no = cmd->bDataInterface; acm = ufoma_get_intconf(cd, id, UDESC_CS_INTERFACE, UDESCSUB_CDC_ACM); if ((acm == NULL) || (acm->bLength < sizeof(*acm))) { return (EINVAL); } sc->sc_acm_cap = acm->bmCapabilities; device_printf(dev, "data interface %d, has %sCM over data, " "has %sbreak\n", sc->sc_data_iface_no, sc->sc_cm_cap & USB_CDC_CM_OVER_DATA ? "" : "no ", sc->sc_acm_cap & USB_CDC_ACM_HAS_BREAK ? "" : "no "); /* get the data interface too */ for (i = 0;; i++) { iface = usbd_get_iface(uaa->device, i); if (iface) { id = usbd_get_interface_descriptor(iface); if (id && (id->bInterfaceNumber == sc->sc_data_iface_no)) { sc->sc_data_iface_index = i; usbd_set_parent_iface(uaa->device, i, uaa->info.bIfaceIndex); break; } } else { device_printf(dev, "no data interface\n"); return (EINVAL); } } error = usbd_transfer_setup(uaa->device, &sc->sc_data_iface_index, sc->sc_bulk_xfer, ufoma_bulk_config, UFOMA_BULK_ENDPT_MAX, sc, &sc->sc_mtx); if (error) { device_printf(dev, "allocating BULK USB " "transfers failed\n"); return (EINVAL); } return (0); } static void ufoma_start_read(struct ucom_softc *ucom) { struct ufoma_softc *sc = ucom->sc_parent; /* start interrupt transfer */ usbd_transfer_start(sc->sc_ctrl_xfer[UFOMA_CTRL_ENDPT_INTR]); /* start data transfer */ if (sc->sc_nobulk) { usbd_transfer_start(sc->sc_ctrl_xfer[UFOMA_CTRL_ENDPT_READ]); } else { usbd_transfer_start(sc->sc_bulk_xfer[UFOMA_BULK_ENDPT_READ]); } } static void ufoma_stop_read(struct ucom_softc *ucom) { struct ufoma_softc *sc = ucom->sc_parent; /* stop interrupt transfer */ usbd_transfer_stop(sc->sc_ctrl_xfer[UFOMA_CTRL_ENDPT_INTR]); /* stop data transfer */ if (sc->sc_nobulk) { usbd_transfer_stop(sc->sc_ctrl_xfer[UFOMA_CTRL_ENDPT_READ]); } else { usbd_transfer_stop(sc->sc_bulk_xfer[UFOMA_BULK_ENDPT_READ]); } } static void ufoma_start_write(struct ucom_softc *ucom) { struct ufoma_softc *sc = ucom->sc_parent; if (sc->sc_nobulk) { usbd_transfer_start(sc->sc_ctrl_xfer[UFOMA_CTRL_ENDPT_WRITE]); } else { usbd_transfer_start(sc->sc_bulk_xfer[UFOMA_BULK_ENDPT_WRITE]); } } static void ufoma_stop_write(struct ucom_softc *ucom) { struct ufoma_softc *sc = ucom->sc_parent; if (sc->sc_nobulk) { usbd_transfer_stop(sc->sc_ctrl_xfer[UFOMA_CTRL_ENDPT_WRITE]); } else { usbd_transfer_stop(sc->sc_bulk_xfer[UFOMA_BULK_ENDPT_WRITE]); } } static struct umcpc_modetostr_tab{ int mode; char *str; }umcpc_modetostr_tab[]={ {UMCPC_ACM_MODE_DEACTIVATED, "deactivated"}, {UMCPC_ACM_MODE_MODEM, "modem"}, {UMCPC_ACM_MODE_ATCOMMAND, "handsfree"}, {UMCPC_ACM_MODE_OBEX, "obex"}, {UMCPC_ACM_MODE_VENDOR1, "vendor1"}, {UMCPC_ACM_MODE_VENDOR2, "vendor2"}, {UMCPC_ACM_MODE_UNLINKED, "unlinked"}, {0, NULL} }; static char *ufoma_mode_to_str(int mode) { int i; for(i = 0 ;umcpc_modetostr_tab[i].str != NULL; i++){ if(umcpc_modetostr_tab[i].mode == mode){ return umcpc_modetostr_tab[i].str; } } return NULL; } static int ufoma_str_to_mode(char *str) { int i; for(i = 0 ;umcpc_modetostr_tab[i].str != NULL; i++){ if(strcmp(str, umcpc_modetostr_tab[i].str)==0){ return umcpc_modetostr_tab[i].mode; } } return -1; } static int ufoma_sysctl_support(SYSCTL_HANDLER_ARGS) { struct ufoma_softc *sc = (struct ufoma_softc *)oidp->oid_arg1; struct sbuf sb; int i; char *mode; sbuf_new(&sb, NULL, 1, SBUF_AUTOEXTEND); for(i = 1; i < sc->sc_modetable[0]; i++){ mode = ufoma_mode_to_str(sc->sc_modetable[i]); if(mode !=NULL){ sbuf_cat(&sb, mode); }else{ sbuf_printf(&sb, "(%02x)", sc->sc_modetable[i]); } if(i < (sc->sc_modetable[0]-1)) sbuf_cat(&sb, ","); } sbuf_trim(&sb); sbuf_finish(&sb); sysctl_handle_string(oidp, sbuf_data(&sb), sbuf_len(&sb), req); sbuf_delete(&sb); return 0; } static int ufoma_sysctl_current(SYSCTL_HANDLER_ARGS) { struct ufoma_softc *sc = (struct ufoma_softc *)oidp->oid_arg1; char *mode; char subbuf[]="(XXX)"; mode = ufoma_mode_to_str(sc->sc_currentmode); if(!mode){ mode = subbuf; snprintf(subbuf, sizeof(subbuf), "(%02x)", sc->sc_currentmode); } sysctl_handle_string(oidp, mode, strlen(mode), req); return 0; } static int ufoma_sysctl_open(SYSCTL_HANDLER_ARGS) { struct ufoma_softc *sc = (struct ufoma_softc *)oidp->oid_arg1; char *mode; char subbuf[40]; int newmode; int error; int i; mode = ufoma_mode_to_str(sc->sc_modetoactivate); if(mode){ strncpy(subbuf, mode, sizeof(subbuf)); }else{ snprintf(subbuf, sizeof(subbuf), "(%02x)", sc->sc_modetoactivate); } error = sysctl_handle_string(oidp, subbuf, sizeof(subbuf), req); if(error != 0 || req->newptr == NULL){ return error; } if((newmode = ufoma_str_to_mode(subbuf)) == -1){ return EINVAL; } for(i = 1 ; i < sc->sc_modetable[0] ; i++){ if(sc->sc_modetable[i] == newmode){ sc->sc_modetoactivate = newmode; return 0; } } return EINVAL; } static void ufoma_poll(struct ucom_softc *ucom) { struct ufoma_softc *sc = ucom->sc_parent; usbd_transfer_poll(sc->sc_ctrl_xfer, UFOMA_CTRL_ENDPT_MAX); usbd_transfer_poll(sc->sc_bulk_xfer, UFOMA_BULK_ENDPT_MAX); } diff --git a/sys/dev/usb/serial/uftdi.c b/sys/dev/usb/serial/uftdi.c index 52977352fb94..9dc00e82253f 100644 --- a/sys/dev/usb/serial/uftdi.c +++ b/sys/dev/usb/serial/uftdi.c @@ -1,2014 +1,2016 @@ /* $NetBSD: uftdi.c,v 1.13 2002/09/23 05:51:23 simonb Exp $ */ /*- * SPDX-License-Identifier: BSD-2-Clause-NetBSD * * Copyright (c) 2000 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (lennart@augustsson.net). * * 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. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 THE FOUNDATION OR CONTRIBUTORS * 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 __FBSDID("$FreeBSD$"); /* * NOTE: all function names beginning like "uftdi_cfg_" can only * be called from within the config thread function ! */ /* * FTDI FT232x, FT2232x, FT4232x, FT8U100AX and FT8U232xM serial adapters. * * Note that we specifically do not do a reset or otherwise alter the state of * the chip during attach, detach, open, and close, because it could be * pre-initialized (via an attached serial eeprom) to power-on into a mode such * as bitbang in which the pins are being driven to a specific state which we * must not perturb. The device gets reset at power-on, and doesn't need to be * reset again after that to function, except as directed by ioctl() calls. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "usbdevs.h" #define USB_DEBUG_VAR uftdi_debug #include #include #include #include #include static SYSCTL_NODE(_hw_usb, OID_AUTO, uftdi, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, "USB uftdi"); #ifdef USB_DEBUG static int uftdi_debug = 0; SYSCTL_INT(_hw_usb_uftdi, OID_AUTO, debug, CTLFLAG_RWTUN, &uftdi_debug, 0, "Debug level"); #endif #define UFTDI_CONFIG_INDEX 0 /* * IO buffer sizes and FTDI device procotol sizes. * * Note that the output packet size in the following defines is not the usb * protocol packet size based on bus speed, it is the size dictated by the FTDI * device itself, and is used only on older chips. * * We allocate buffers bigger than the hardware's packet size, and process * multiple packets within each buffer. This allows the controller to make * optimal use of the usb bus by conducting multiple transfers with the device * during a single bus timeslice to fill or drain the chip's fifos. * * The output data on newer chips has no packet header, and we are able to pack * any number of output bytes into a buffer. On some older chips, each output * packet contains a 1-byte header and up to 63 bytes of payload. The size is * encoded in 6 bits of the header, hence the 64-byte limit on packet size. We * loop to fill the buffer with many of these header+payload packets. * * The input data on all chips consists of packets which contain a 2-byte header * followed by data payload. The total size of the packet is wMaxPacketSize * which can change based on the bus speed (e.g., 64 for full speed, 512 for * high speed). We loop to extract the headers and payloads from the packets * packed into an input buffer. */ #define UFTDI_IBUFSIZE 2048 #define UFTDI_IHDRSIZE 2 #define UFTDI_OBUFSIZE 2048 #define UFTDI_OPKTSIZE 64 enum { UFTDI_BULK_DT_WR, UFTDI_BULK_DT_RD, UFTDI_N_TRANSFER, }; enum { DEVT_SIO, DEVT_232A, DEVT_232B, DEVT_2232D, /* Includes 2232C */ DEVT_232R, DEVT_2232H, DEVT_4232H, DEVT_232H, DEVT_230X, }; #define DEVF_BAUDBITS_HINDEX 0x01 /* Baud bits in high byte of index. */ #define DEVF_BAUDCLK_12M 0X02 /* Base baud clock is 12MHz. */ struct uftdi_softc { struct ucom_super_softc sc_super_ucom; struct ucom_softc sc_ucom; struct usb_device *sc_udev; struct usb_xfer *sc_xfer[UFTDI_N_TRANSFER]; device_t sc_dev; struct mtx sc_mtx; uint32_t sc_unit; uint16_t sc_last_lcr; uint16_t sc_bcdDevice; uint8_t sc_devtype; uint8_t sc_devflags; uint8_t sc_hdrlen; uint8_t sc_msr; uint8_t sc_lsr; uint8_t sc_bitmode; }; struct uftdi_param_config { uint16_t baud_lobits; uint16_t baud_hibits; uint16_t lcr; uint8_t v_start; uint8_t v_stop; uint8_t v_flow; }; /* prototypes */ static device_probe_t uftdi_probe; static device_attach_t uftdi_attach; static device_detach_t uftdi_detach; static void uftdi_free_softc(struct uftdi_softc *); static usb_callback_t uftdi_write_callback; static usb_callback_t uftdi_read_callback; static void uftdi_free(struct ucom_softc *); static void uftdi_cfg_open(struct ucom_softc *); static void uftdi_cfg_close(struct ucom_softc *); static void uftdi_cfg_set_dtr(struct ucom_softc *, uint8_t); static void uftdi_cfg_set_rts(struct ucom_softc *, uint8_t); static void uftdi_cfg_set_break(struct ucom_softc *, uint8_t); static int uftdi_set_parm_soft(struct ucom_softc *, struct termios *, struct uftdi_param_config *); static int uftdi_pre_param(struct ucom_softc *, struct termios *); static void uftdi_cfg_param(struct ucom_softc *, struct termios *); static void uftdi_cfg_get_status(struct ucom_softc *, uint8_t *, uint8_t *); static int uftdi_reset(struct ucom_softc *, int); static int uftdi_set_bitmode(struct ucom_softc *, uint8_t, uint8_t); static int uftdi_get_bitmode(struct ucom_softc *, uint8_t *, uint8_t *); static int uftdi_set_latency(struct ucom_softc *, int); static int uftdi_get_latency(struct ucom_softc *, int *); static int uftdi_set_event_char(struct ucom_softc *, int); static int uftdi_set_error_char(struct ucom_softc *, int); static int uftdi_ioctl(struct ucom_softc *, uint32_t, caddr_t, int, struct thread *); static void uftdi_start_read(struct ucom_softc *); static void uftdi_stop_read(struct ucom_softc *); static void uftdi_start_write(struct ucom_softc *); static void uftdi_stop_write(struct ucom_softc *); static void uftdi_poll(struct ucom_softc *ucom); static const struct usb_config uftdi_config[UFTDI_N_TRANSFER] = { [UFTDI_BULK_DT_WR] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, .bufsize = UFTDI_OBUFSIZE, .flags = {.pipe_bof = 1,}, .callback = &uftdi_write_callback, }, [UFTDI_BULK_DT_RD] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, .bufsize = UFTDI_IBUFSIZE, .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, .callback = &uftdi_read_callback, }, }; static const struct ucom_callback uftdi_callback = { .ucom_cfg_get_status = &uftdi_cfg_get_status, .ucom_cfg_set_dtr = &uftdi_cfg_set_dtr, .ucom_cfg_set_rts = &uftdi_cfg_set_rts, .ucom_cfg_set_break = &uftdi_cfg_set_break, .ucom_cfg_param = &uftdi_cfg_param, .ucom_cfg_open = &uftdi_cfg_open, .ucom_cfg_close = &uftdi_cfg_close, .ucom_pre_param = &uftdi_pre_param, .ucom_ioctl = &uftdi_ioctl, .ucom_start_read = &uftdi_start_read, .ucom_stop_read = &uftdi_stop_read, .ucom_start_write = &uftdi_start_write, .ucom_stop_write = &uftdi_stop_write, .ucom_poll = &uftdi_poll, .ucom_free = &uftdi_free, }; static device_method_t uftdi_methods[] = { /* Device interface */ DEVMETHOD(device_probe, uftdi_probe), DEVMETHOD(device_attach, uftdi_attach), DEVMETHOD(device_detach, uftdi_detach), DEVMETHOD_END }; static devclass_t uftdi_devclass; static driver_t uftdi_driver = { .name = "uftdi", .methods = uftdi_methods, .size = sizeof(struct uftdi_softc), }; static const STRUCT_USB_HOST_ID uftdi_devs[] = { #define UFTDI_DEV(v, p, i) \ { USB_VPI(USB_VENDOR_##v, USB_PRODUCT_##v##_##p, i) } UFTDI_DEV(ACTON, SPECTRAPRO, 0), UFTDI_DEV(ALTI2, N3, 0), UFTDI_DEV(ANALOGDEVICES, GNICE, UFTDI_JTAG_IFACE(0)), UFTDI_DEV(ANALOGDEVICES, GNICEPLUS, UFTDI_JTAG_IFACE(0)), UFTDI_DEV(ATMEL, STK541, 0), UFTDI_DEV(BAYER, CONTOUR_CABLE, 0), UFTDI_DEV(BBELECTRONICS, 232USB9M, 0), UFTDI_DEV(BBELECTRONICS, 485USB9F_2W, 0), UFTDI_DEV(BBELECTRONICS, 485USB9F_4W, 0), UFTDI_DEV(BBELECTRONICS, 485USBTB_2W, 0), UFTDI_DEV(BBELECTRONICS, 485USBTB_4W, 0), UFTDI_DEV(BBELECTRONICS, TTL3USB9M, 0), UFTDI_DEV(BBELECTRONICS, TTL5USB9M, 0), UFTDI_DEV(BBELECTRONICS, USO9ML2, 0), UFTDI_DEV(BBELECTRONICS, USO9ML2DR, 0), UFTDI_DEV(BBELECTRONICS, USO9ML2DR_2, 0), UFTDI_DEV(BBELECTRONICS, USOPTL4, 0), UFTDI_DEV(BBELECTRONICS, USOPTL4DR, 0), UFTDI_DEV(BBELECTRONICS, USOPTL4DR2, 0), UFTDI_DEV(BBELECTRONICS, USOTL4, 0), UFTDI_DEV(BBELECTRONICS, USPTL4, 0), UFTDI_DEV(BBELECTRONICS, USTL4, 0), UFTDI_DEV(BBELECTRONICS, ZZ_PROG1_USB, 0), UFTDI_DEV(BRAINBOXES, US257, 0), UFTDI_DEV(BRAINBOXES, US25701, 0), UFTDI_DEV(CONTEC, COM1USBH, 0), UFTDI_DEV(DRESDENELEKTRONIK, SENSORTERMINALBOARD, 0), UFTDI_DEV(DRESDENELEKTRONIK, WIRELESSHANDHELDTERMINAL, 0), UFTDI_DEV(DRESDENELEKTRONIK, DE_RFNODE, 0), UFTDI_DEV(DRESDENELEKTRONIK, LEVELSHIFTERSTICKLOWCOST, 0), UFTDI_DEV(ELEKTOR, FT323R, 0), UFTDI_DEV(EVOLUTION, ER1, 0), UFTDI_DEV(EVOLUTION, HYBRID, 0), UFTDI_DEV(EVOLUTION, RCM4, 0), UFTDI_DEV(FALCOM, SAMBA, 0), UFTDI_DEV(FALCOM, TWIST, 0), UFTDI_DEV(FIC, NEO1973_DEBUG, UFTDI_JTAG_IFACE(0)), UFTDI_DEV(FIC, NEO1973_DEBUG, UFTDI_JTAG_IFACE(0)), UFTDI_DEV(FTDI, 232EX, 0), UFTDI_DEV(FTDI, 232H, 0), UFTDI_DEV(FTDI, 232RL, 0), UFTDI_DEV(FTDI, 4N_GALAXY_DE_1, 0), UFTDI_DEV(FTDI, 4N_GALAXY_DE_2, 0), UFTDI_DEV(FTDI, 4N_GALAXY_DE_3, 0), UFTDI_DEV(FTDI, 8U232AM_ALT, 0), UFTDI_DEV(FTDI, ACCESSO, 0), UFTDI_DEV(FTDI, ACG_HFDUAL, 0), UFTDI_DEV(FTDI, ACTIVE_ROBOTS, 0), UFTDI_DEV(FTDI, ACTZWAVE, 0), UFTDI_DEV(FTDI, AMC232, 0), UFTDI_DEV(FTDI, ARTEMIS, 0), UFTDI_DEV(FTDI, ASK_RDR400, 0), UFTDI_DEV(FTDI, ATIK_ATK16, 0), UFTDI_DEV(FTDI, ATIK_ATK16C, 0), UFTDI_DEV(FTDI, ATIK_ATK16HR, 0), UFTDI_DEV(FTDI, ATIK_ATK16HRC, 0), UFTDI_DEV(FTDI, ATIK_ATK16IC, 0), UFTDI_DEV(FTDI, BCS_SE923, 0), UFTDI_DEV(FTDI, CANDAPTER, 0), UFTDI_DEV(FTDI, CANUSB, 0), UFTDI_DEV(FTDI, CCSICDU20_0, 0), UFTDI_DEV(FTDI, CCSICDU40_1, 0), UFTDI_DEV(FTDI, CCSICDU64_4, 0), UFTDI_DEV(FTDI, CCSLOAD_N_GO_3, 0), UFTDI_DEV(FTDI, CCSMACHX_2, 0), UFTDI_DEV(FTDI, CCSPRIME8_5, 0), UFTDI_DEV(FTDI, CFA_631, 0), UFTDI_DEV(FTDI, CFA_632, 0), UFTDI_DEV(FTDI, CFA_633, 0), UFTDI_DEV(FTDI, CFA_634, 0), UFTDI_DEV(FTDI, CFA_635, 0), UFTDI_DEV(FTDI, CHAMSYS_24_MASTER_WING, 0), UFTDI_DEV(FTDI, CHAMSYS_MAXI_WING, 0), UFTDI_DEV(FTDI, CHAMSYS_MEDIA_WING, 0), UFTDI_DEV(FTDI, CHAMSYS_MIDI_TIMECODE, 0), UFTDI_DEV(FTDI, CHAMSYS_MINI_WING, 0), UFTDI_DEV(FTDI, CHAMSYS_PC_WING, 0), UFTDI_DEV(FTDI, CHAMSYS_USB_DMX, 0), UFTDI_DEV(FTDI, CHAMSYS_WING, 0), UFTDI_DEV(FTDI, COM4SM, 0), UFTDI_DEV(FTDI, CONVERTER_0, 0), UFTDI_DEV(FTDI, CONVERTER_1, 0), UFTDI_DEV(FTDI, CONVERTER_2, 0), UFTDI_DEV(FTDI, CONVERTER_3, 0), UFTDI_DEV(FTDI, CONVERTER_4, 0), UFTDI_DEV(FTDI, CONVERTER_5, 0), UFTDI_DEV(FTDI, CONVERTER_6, 0), UFTDI_DEV(FTDI, CONVERTER_7, 0), UFTDI_DEV(FTDI, CTI_USB_MINI_485, 0), UFTDI_DEV(FTDI, CTI_USB_NANO_485, 0), UFTDI_DEV(FTDI, DMX4ALL, 0), UFTDI_DEV(FTDI, DOMINTELL_DGQG, 0), UFTDI_DEV(FTDI, DOMINTELL_DUSB, 0), UFTDI_DEV(FTDI, DOTEC, 0), UFTDI_DEV(FTDI, ECLO_COM_1WIRE, 0), UFTDI_DEV(FTDI, ECO_PRO_CDS, 0), UFTDI_DEV(FTDI, EISCOU, 0), UFTDI_DEV(FTDI, ELSTER_UNICOM, 0), UFTDI_DEV(FTDI, ELV_ALC8500, 0), UFTDI_DEV(FTDI, ELV_CLI7000, 0), UFTDI_DEV(FTDI, ELV_CSI8, 0), UFTDI_DEV(FTDI, ELV_EC3000, 0), UFTDI_DEV(FTDI, ELV_EM1000DL, 0), UFTDI_DEV(FTDI, ELV_EM1010PC, 0), UFTDI_DEV(FTDI, ELV_FEM, 0), UFTDI_DEV(FTDI, ELV_FHZ1000PC, 0), UFTDI_DEV(FTDI, ELV_FHZ1300PC, 0), UFTDI_DEV(FTDI, ELV_FM3RX, 0), UFTDI_DEV(FTDI, ELV_FS20SIG, 0), UFTDI_DEV(FTDI, ELV_HS485, 0), UFTDI_DEV(FTDI, ELV_KL100, 0), UFTDI_DEV(FTDI, ELV_MSM1, 0), UFTDI_DEV(FTDI, ELV_PCD200, 0), UFTDI_DEV(FTDI, ELV_PCK100, 0), UFTDI_DEV(FTDI, ELV_PPS7330, 0), UFTDI_DEV(FTDI, ELV_RFP500, 0), UFTDI_DEV(FTDI, ELV_T1100, 0), UFTDI_DEV(FTDI, ELV_TFD128, 0), UFTDI_DEV(FTDI, ELV_TFM100, 0), UFTDI_DEV(FTDI, ELV_TWS550, 0), UFTDI_DEV(FTDI, ELV_UAD8, 0), UFTDI_DEV(FTDI, ELV_UDA7, 0), UFTDI_DEV(FTDI, ELV_UDF77, 0), UFTDI_DEV(FTDI, ELV_UIO88, 0), UFTDI_DEV(FTDI, ELV_ULA200, 0), UFTDI_DEV(FTDI, ELV_UM100, 0), UFTDI_DEV(FTDI, ELV_UMS100, 0), UFTDI_DEV(FTDI, ELV_UO100, 0), UFTDI_DEV(FTDI, ELV_UR100, 0), UFTDI_DEV(FTDI, ELV_USI2, 0), UFTDI_DEV(FTDI, ELV_USR, 0), UFTDI_DEV(FTDI, ELV_UTP8, 0), UFTDI_DEV(FTDI, ELV_WS300PC, 0), UFTDI_DEV(FTDI, ELV_WS444PC, 0), UFTDI_DEV(FTDI, ELV_WS500, 0), UFTDI_DEV(FTDI, ELV_WS550, 0), UFTDI_DEV(FTDI, ELV_WS777, 0), UFTDI_DEV(FTDI, ELV_WS888, 0), UFTDI_DEV(FTDI, EMCU2D, 0), UFTDI_DEV(FTDI, EMCU2H, 0), UFTDI_DEV(FTDI, FUTURE_0, 0), UFTDI_DEV(FTDI, FUTURE_1, 0), UFTDI_DEV(FTDI, FUTURE_2, 0), UFTDI_DEV(FTDI, GAMMASCOUT, 0), UFTDI_DEV(FTDI, GENERIC, 0), UFTDI_DEV(FTDI, GUDEADS_E808, 0), UFTDI_DEV(FTDI, GUDEADS_E809, 0), UFTDI_DEV(FTDI, GUDEADS_E80A, 0), UFTDI_DEV(FTDI, GUDEADS_E80B, 0), UFTDI_DEV(FTDI, GUDEADS_E80C, 0), UFTDI_DEV(FTDI, GUDEADS_E80D, 0), UFTDI_DEV(FTDI, GUDEADS_E80E, 0), UFTDI_DEV(FTDI, GUDEADS_E80F, 0), UFTDI_DEV(FTDI, GUDEADS_E88D, 0), UFTDI_DEV(FTDI, GUDEADS_E88E, 0), UFTDI_DEV(FTDI, GUDEADS_E88F, 0), UFTDI_DEV(FTDI, HD_RADIO, 0), UFTDI_DEV(FTDI, HO720, 0), UFTDI_DEV(FTDI, HO730, 0), UFTDI_DEV(FTDI, HO820, 0), UFTDI_DEV(FTDI, HO870, 0), UFTDI_DEV(FTDI, IBS_APP70, 0), UFTDI_DEV(FTDI, IBS_PCMCIA, 0), UFTDI_DEV(FTDI, IBS_PEDO, 0), UFTDI_DEV(FTDI, IBS_PICPRO, 0), UFTDI_DEV(FTDI, IBS_PK1, 0), UFTDI_DEV(FTDI, IBS_PROD, 0), UFTDI_DEV(FTDI, IBS_RS232MON, 0), UFTDI_DEV(FTDI, IBS_US485, 0), UFTDI_DEV(FTDI, IPLUS, 0), UFTDI_DEV(FTDI, IPLUS2, 0), UFTDI_DEV(FTDI, IRTRANS, 0), UFTDI_DEV(FTDI, KBS, 0), UFTDI_DEV(FTDI, KTLINK, UFTDI_JTAG_IFACE(0)), UFTDI_DEV(FTDI, LENZ_LIUSB, 0), UFTDI_DEV(FTDI, LK202, 0), UFTDI_DEV(FTDI, LK204, 0), UFTDI_DEV(FTDI, LM3S_DEVEL_BOARD, UFTDI_JTAG_IFACE(0)), UFTDI_DEV(FTDI, LM3S_EVAL_BOARD, UFTDI_JTAG_IFACE(0)), UFTDI_DEV(FTDI, LM3S_ICDI_B_BOARD, UFTDI_JTAG_IFACE(0)), UFTDI_DEV(FTDI, MASTERDEVEL2, 0), UFTDI_DEV(FTDI, MAXSTREAM, 0), UFTDI_DEV(FTDI, MHAM_DB9, 0), UFTDI_DEV(FTDI, MHAM_IC, 0), UFTDI_DEV(FTDI, MHAM_KW, 0), UFTDI_DEV(FTDI, MHAM_RS232, 0), UFTDI_DEV(FTDI, MHAM_Y6, 0), UFTDI_DEV(FTDI, MHAM_Y8, 0), UFTDI_DEV(FTDI, MHAM_Y9, 0), UFTDI_DEV(FTDI, MHAM_YS, 0), UFTDI_DEV(FTDI, MICRO_CHAMELEON, 0), UFTDI_DEV(FTDI, MTXORB_5, 0), UFTDI_DEV(FTDI, MTXORB_6, 0), UFTDI_DEV(FTDI, MX2_3, 0), UFTDI_DEV(FTDI, MX4_5, 0), UFTDI_DEV(FTDI, NXTCAM, 0), UFTDI_DEV(FTDI, OCEANIC, 0), UFTDI_DEV(FTDI, OOCDLINK, UFTDI_JTAG_IFACE(0)), UFTDI_DEV(FTDI, OPENDCC, 0), UFTDI_DEV(FTDI, OPENDCC_GATEWAY, 0), UFTDI_DEV(FTDI, OPENDCC_GBM, 0), UFTDI_DEV(FTDI, OPENDCC_SNIFFER, 0), UFTDI_DEV(FTDI, OPENDCC_THROTTLE, 0), UFTDI_DEV(FTDI, PCDJ_DAC2, 0), UFTDI_DEV(FTDI, PCMSFU, 0), UFTDI_DEV(FTDI, PERLE_ULTRAPORT, 0), UFTDI_DEV(FTDI, PHI_FISCO, 0), UFTDI_DEV(FTDI, PIEGROUP, 0), UFTDI_DEV(FTDI, PROPOX_JTAGCABLEII, 0), UFTDI_DEV(FTDI, R2000KU_TRUE_RNG, 0), UFTDI_DEV(FTDI, R2X0, 0), UFTDI_DEV(FTDI, RELAIS, 0), UFTDI_DEV(FTDI, REU_TINY, 0), UFTDI_DEV(FTDI, RMP200, 0), UFTDI_DEV(FTDI, RM_CANVIEW, 0), UFTDI_DEV(FTDI, RRCIRKITS_LOCOBUFFER, 0), UFTDI_DEV(FTDI, SCIENCESCOPE_HS_LOGBOOK, 0), UFTDI_DEV(FTDI, SCIENCESCOPE_LOGBOOKML, 0), UFTDI_DEV(FTDI, SCIENCESCOPE_LS_LOGBOOK, 0), UFTDI_DEV(FTDI, SCS_DEVICE_0, 0), UFTDI_DEV(FTDI, SCS_DEVICE_1, 0), UFTDI_DEV(FTDI, SCS_DEVICE_2, 0), UFTDI_DEV(FTDI, SCS_DEVICE_3, 0), UFTDI_DEV(FTDI, SCS_DEVICE_4, 0), UFTDI_DEV(FTDI, SCS_DEVICE_5, 0), UFTDI_DEV(FTDI, SCS_DEVICE_6, 0), UFTDI_DEV(FTDI, SCS_DEVICE_7, 0), UFTDI_DEV(FTDI, SCX8_USB_PHOENIX, 0), UFTDI_DEV(FTDI, SDMUSBQSS, 0), UFTDI_DEV(FTDI, SEMC_DSS20, 0), UFTDI_DEV(FTDI, SERIAL_2232C, UFTDI_JTAG_CHECK_STRING), UFTDI_DEV(FTDI, SERIAL_2232D, 0), UFTDI_DEV(FTDI, SERIAL_232RL, 0), UFTDI_DEV(FTDI, SERIAL_4232H, 0), UFTDI_DEV(FTDI, SERIAL_8U100AX, 0), UFTDI_DEV(FTDI, SERIAL_8U232AM, 0), UFTDI_DEV(FTDI, SERIAL_8U232AM4, 0), UFTDI_DEV(FTDI, SIGNALYZER_SH2, UFTDI_JTAG_IFACE(0)), UFTDI_DEV(FTDI, SIGNALYZER_SH4, UFTDI_JTAG_IFACE(0)), UFTDI_DEV(FTDI, SIGNALYZER_SLITE, UFTDI_JTAG_IFACE(0)), UFTDI_DEV(FTDI, SIGNALYZER_ST, UFTDI_JTAG_IFACE(0)), UFTDI_DEV(FTDI, SITOP_UPS500S, 0), UFTDI_DEV(FTDI, SPECIAL_1, 0), UFTDI_DEV(FTDI, SPECIAL_3, 0), UFTDI_DEV(FTDI, SPECIAL_4, 0), UFTDI_DEV(FTDI, SPROG_II, 0), UFTDI_DEV(FTDI, SR_RADIO, 0), UFTDI_DEV(FTDI, SUUNTO_SPORTS, 0), UFTDI_DEV(FTDI, TACTRIX_OPENPORT_13M, 0), UFTDI_DEV(FTDI, TACTRIX_OPENPORT_13S, 0), UFTDI_DEV(FTDI, TACTRIX_OPENPORT_13U, 0), UFTDI_DEV(FTDI, TAVIR_STK500, 0), UFTDI_DEV(FTDI, TERATRONIK_D2XX, 0), UFTDI_DEV(FTDI, TERATRONIK_VCP, 0), UFTDI_DEV(FTDI, THORLABS, 0), UFTDI_DEV(FTDI, TIAO, 0), UFTDI_DEV(FTDI, TNC_X, 0), UFTDI_DEV(FTDI, TTUSB, 0), UFTDI_DEV(FTDI, TURTELIZER2, UFTDI_JTAG_IFACE(0)), UFTDI_DEV(FTDI, UOPTBR, 0), UFTDI_DEV(FTDI, USBSERIAL, 0), UFTDI_DEV(FTDI, USBX_707, 0), UFTDI_DEV(FTDI, USB_UIRT, 0), UFTDI_DEV(FTDI, USINT_CAT, 0), UFTDI_DEV(FTDI, USINT_RS232, 0), UFTDI_DEV(FTDI, USINT_WKEY, 0), UFTDI_DEV(FTDI, VARDAAN, 0), UFTDI_DEV(FTDI, VNHCPCUSB_D, 0), UFTDI_DEV(FTDI, WESTREX_MODEL_777, 0), UFTDI_DEV(FTDI, WESTREX_MODEL_8900F, 0), UFTDI_DEV(FTDI, XDS100V2, UFTDI_JTAG_IFACE(0)), UFTDI_DEV(FTDI, XDS100V3, UFTDI_JTAG_IFACE(0)), UFTDI_DEV(FTDI, XF_547, 0), UFTDI_DEV(FTDI, XF_640, 0), UFTDI_DEV(FTDI, XF_642, 0), UFTDI_DEV(FTDI, XM_RADIO, 0), UFTDI_DEV(FTDI, YEI_SERVOCENTER31, 0), UFTDI_DEV(GNOTOMETRICS, USB, 0), UFTDI_DEV(ICOM, SP1, 0), UFTDI_DEV(ICOM, OPC_U_UC, 0), UFTDI_DEV(ICOM, RP2C1, 0), UFTDI_DEV(ICOM, RP2C2, 0), UFTDI_DEV(ICOM, RP2D, 0), UFTDI_DEV(ICOM, RP2KVR, 0), UFTDI_DEV(ICOM, RP2KVT, 0), UFTDI_DEV(ICOM, RP2VR, 0), UFTDI_DEV(ICOM, RP2VT, 0), UFTDI_DEV(ICOM, RP4KVR, 0), UFTDI_DEV(ICOM, RP4KVT, 0), UFTDI_DEV(IDTECH, IDT1221U, 0), UFTDI_DEV(INTERBIOMETRICS, IOBOARD, 0), UFTDI_DEV(INTERBIOMETRICS, MINI_IOBOARD, 0), UFTDI_DEV(INTREPIDCS, NEOVI, 0), UFTDI_DEV(INTREPIDCS, VALUECAN, 0), UFTDI_DEV(IONICS, PLUGCOMPUTER, UFTDI_JTAG_IFACE(0)), UFTDI_DEV(JETI, SPC1201, 0), UFTDI_DEV(KOBIL, CONV_B1, 0), UFTDI_DEV(KOBIL, CONV_KAAN, 0), UFTDI_DEV(LARSENBRUSGAARD, ALTITRACK, 0), UFTDI_DEV(MARVELL, SHEEVAPLUG, UFTDI_JTAG_IFACE(0)), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0100, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0101, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0102, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0103, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0104, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0105, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0106, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0107, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0108, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0109, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_010A, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_010B, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_010C, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_010D, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_010E, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_010F, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0110, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0111, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0112, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0113, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0114, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0115, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0116, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0117, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0118, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0119, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_011A, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_011B, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_011C, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_011D, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_011E, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_011F, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0120, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0121, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0122, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0123, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0124, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0125, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0126, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0128, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0129, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_012A, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_012B, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_012D, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_012E, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_012F, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0130, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0131, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0132, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0133, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0134, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0135, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0136, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0137, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0138, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0139, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_013A, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_013B, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_013C, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_013D, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_013E, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_013F, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0140, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0141, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0142, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0143, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0144, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0145, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0146, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0147, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0148, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0149, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_014A, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_014B, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_014C, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_014D, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_014E, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_014F, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0150, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0151, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0152, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0159, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_015A, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_015B, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_015C, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_015D, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_015E, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_015F, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0160, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0161, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0162, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0163, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0164, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0165, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0166, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0167, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0168, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0169, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_016A, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_016B, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_016C, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_016D, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_016E, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_016F, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0170, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0171, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0172, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0173, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0174, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0175, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0176, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0177, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0178, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0179, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_017A, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_017B, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_017C, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_017D, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_017E, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_017F, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0180, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0181, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0182, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0183, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0184, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0185, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0186, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0187, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0188, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0189, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_018A, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_018B, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_018C, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_018D, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_018E, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_018F, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0190, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0191, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0192, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0193, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0194, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0195, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0196, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0197, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0198, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_0199, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_019A, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_019B, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_019C, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_019D, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_019E, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_019F, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01A0, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01A1, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01A2, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01A3, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01A4, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01A5, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01A6, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01A7, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01A8, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01A9, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01AA, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01AB, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01AC, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01AD, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01AE, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01AF, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01B0, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01B1, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01B2, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01B3, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01B4, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01B5, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01B6, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01B7, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01B8, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01B9, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01BA, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01BB, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01BC, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01BD, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01BE, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01BF, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01C0, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01C1, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01C2, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01C3, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01C4, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01C5, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01C6, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01C7, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01C8, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01C9, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01CA, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01CB, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01CC, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01CD, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01CE, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01CF, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01D0, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01D1, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01D2, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01D3, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01D4, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01D5, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01D6, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01D7, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01D8, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01D9, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01DA, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01DB, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01DC, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01DD, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01DE, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01DF, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01E0, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01E1, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01E2, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01E3, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01E4, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01E5, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01E6, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01E7, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01E8, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01E9, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01EA, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01EB, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01EC, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01ED, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01EE, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01EF, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01F0, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01F1, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01F2, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01F3, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01F4, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01F5, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01F6, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01F7, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01F8, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01F9, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01FA, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01FB, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01FC, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01FD, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01FE, 0), UFTDI_DEV(MATRIXORBITAL, FTDI_RANGE_01FF, 0), UFTDI_DEV(MATRIXORBITAL, MOUA, 0), UFTDI_DEV(MELCO, PCOPRS1, 0), UFTDI_DEV(METAGEEK, TELLSTICK, 0), UFTDI_DEV(MOBILITY, USB_SERIAL, 0), UFTDI_DEV(OLIMEX, ARM_USB_OCD, UFTDI_JTAG_IFACE(0)), UFTDI_DEV(OLIMEX, ARM_USB_OCD_H, UFTDI_JTAG_IFACE(0)), UFTDI_DEV(OPTO, CRD7734, 0), UFTDI_DEV(OPTO, CRD7734_1, 0), UFTDI_DEV(PAPOUCH, AD4USB, 0), UFTDI_DEV(PAPOUCH, AP485, 0), UFTDI_DEV(PAPOUCH, AP485_2, 0), UFTDI_DEV(PAPOUCH, DRAK5, 0), UFTDI_DEV(PAPOUCH, DRAK6, 0), UFTDI_DEV(PAPOUCH, GMSR, 0), UFTDI_DEV(PAPOUCH, GMUX, 0), UFTDI_DEV(PAPOUCH, IRAMP, 0), UFTDI_DEV(PAPOUCH, LEC, 0), UFTDI_DEV(PAPOUCH, MU, 0), UFTDI_DEV(PAPOUCH, QUIDO10X1, 0), UFTDI_DEV(PAPOUCH, QUIDO2X16, 0), UFTDI_DEV(PAPOUCH, QUIDO2X2, 0), UFTDI_DEV(PAPOUCH, QUIDO30X3, 0), UFTDI_DEV(PAPOUCH, QUIDO3X32, 0), UFTDI_DEV(PAPOUCH, QUIDO4X4, 0), UFTDI_DEV(PAPOUCH, QUIDO60X3, 0), UFTDI_DEV(PAPOUCH, QUIDO8X8, 0), UFTDI_DEV(PAPOUCH, SB232, 0), UFTDI_DEV(PAPOUCH, SB422, 0), UFTDI_DEV(PAPOUCH, SB422_2, 0), UFTDI_DEV(PAPOUCH, SB485, 0), UFTDI_DEV(PAPOUCH, SB485C, 0), UFTDI_DEV(PAPOUCH, SB485S, 0), UFTDI_DEV(PAPOUCH, SB485_2, 0), UFTDI_DEV(PAPOUCH, SIMUKEY, 0), UFTDI_DEV(PAPOUCH, TMU, 0), UFTDI_DEV(PAPOUCH, UPSUSB, 0), UFTDI_DEV(POSIFLEX, PP7000, 0), UFTDI_DEV(QIHARDWARE, JTAGSERIAL, UFTDI_JTAG_IFACE(0)), UFTDI_DEV(RATOC, REXUSB60F, 0), UFTDI_DEV(RTSYSTEMS, CT29B, 0), UFTDI_DEV(RTSYSTEMS, SERIAL_VX7, 0), UFTDI_DEV(SEALEVEL, 2101, 0), UFTDI_DEV(SEALEVEL, 2102, 0), UFTDI_DEV(SEALEVEL, 2103, 0), UFTDI_DEV(SEALEVEL, 2104, 0), UFTDI_DEV(SEALEVEL, 2106, 0), UFTDI_DEV(SEALEVEL, 2201_1, 0), UFTDI_DEV(SEALEVEL, 2201_2, 0), UFTDI_DEV(SEALEVEL, 2202_1, 0), UFTDI_DEV(SEALEVEL, 2202_2, 0), UFTDI_DEV(SEALEVEL, 2203_1, 0), UFTDI_DEV(SEALEVEL, 2203_2, 0), UFTDI_DEV(SEALEVEL, 2401_1, 0), UFTDI_DEV(SEALEVEL, 2401_2, 0), UFTDI_DEV(SEALEVEL, 2401_3, 0), UFTDI_DEV(SEALEVEL, 2401_4, 0), UFTDI_DEV(SEALEVEL, 2402_1, 0), UFTDI_DEV(SEALEVEL, 2402_2, 0), UFTDI_DEV(SEALEVEL, 2402_3, 0), UFTDI_DEV(SEALEVEL, 2402_4, 0), UFTDI_DEV(SEALEVEL, 2403_1, 0), UFTDI_DEV(SEALEVEL, 2403_2, 0), UFTDI_DEV(SEALEVEL, 2403_3, 0), UFTDI_DEV(SEALEVEL, 2403_4, 0), UFTDI_DEV(SEALEVEL, 2801_1, 0), UFTDI_DEV(SEALEVEL, 2801_2, 0), UFTDI_DEV(SEALEVEL, 2801_3, 0), UFTDI_DEV(SEALEVEL, 2801_4, 0), UFTDI_DEV(SEALEVEL, 2801_5, 0), UFTDI_DEV(SEALEVEL, 2801_6, 0), UFTDI_DEV(SEALEVEL, 2801_7, 0), UFTDI_DEV(SEALEVEL, 2801_8, 0), UFTDI_DEV(SEALEVEL, 2802_1, 0), UFTDI_DEV(SEALEVEL, 2802_2, 0), UFTDI_DEV(SEALEVEL, 2802_3, 0), UFTDI_DEV(SEALEVEL, 2802_4, 0), UFTDI_DEV(SEALEVEL, 2802_5, 0), UFTDI_DEV(SEALEVEL, 2802_6, 0), UFTDI_DEV(SEALEVEL, 2802_7, 0), UFTDI_DEV(SEALEVEL, 2802_8, 0), UFTDI_DEV(SEALEVEL, 2803_1, 0), UFTDI_DEV(SEALEVEL, 2803_2, 0), UFTDI_DEV(SEALEVEL, 2803_3, 0), UFTDI_DEV(SEALEVEL, 2803_4, 0), UFTDI_DEV(SEALEVEL, 2803_5, 0), UFTDI_DEV(SEALEVEL, 2803_6, 0), UFTDI_DEV(SEALEVEL, 2803_7, 0), UFTDI_DEV(SEALEVEL, 2803_8, 0), UFTDI_DEV(SIIG2, DK201, 0), UFTDI_DEV(SIIG2, US2308, 0), UFTDI_DEV(TESTO, USB_INTERFACE, 0), UFTDI_DEV(TML, USB_SERIAL, 0), UFTDI_DEV(TTI, QL355P, 0), UFTDI_DEV(UNKNOWN4, NF_RIC, 0), #undef UFTDI_DEV }; DRIVER_MODULE(uftdi, uhub, uftdi_driver, uftdi_devclass, NULL, NULL); MODULE_DEPEND(uftdi, ucom, 1, 1, 1); MODULE_DEPEND(uftdi, usb, 1, 1, 1); MODULE_VERSION(uftdi, 1); USB_PNP_HOST_INFO(uftdi_devs); /* * Jtag product name strings table. Some products have one or more interfaces * dedicated to jtag or gpio, but use a product ID that's the same as other * products which don't. They are marked with a flag in the table above, and * the following string table is checked for flagged products. The string check * is done with strstr(); in effect there is an implicit wildcard at the * beginning and end of each product name string in table. */ static const struct jtag_by_name { const char * product_name; uint32_t jtag_interfaces; } jtag_products_by_name[] = { /* TI Beaglebone and TI XDS100Vn jtag product line. */ {"XDS100V", UFTDI_JTAG_IFACE(0)}, }; /* * Set up a sysctl and tunable to en/disable the feature of skipping the * creation of tty devices for jtag interfaces. Enabled by default. */ static int skip_jtag_interfaces = 1; SYSCTL_INT(_hw_usb_uftdi, OID_AUTO, skip_jtag_interfaces, CTLFLAG_RWTUN, &skip_jtag_interfaces, 1, "Skip creating tty devices for jtag interfaces"); static boolean_t is_jtag_interface(struct usb_attach_arg *uaa, const struct usb_device_id *id) { int i, iface_bit; const char * product_name; const struct jtag_by_name *jbn; /* We only allocate 8 flag bits for jtag interface flags. */ if (uaa->info.bIfaceIndex >= UFTDI_JTAG_IFACES_MAX) return (0); iface_bit = UFTDI_JTAG_IFACE(uaa->info.bIfaceIndex); /* * If requested, search the name strings table and use the interface * bits from that table when the product name string matches, else use * the jtag interface bits from the main ID table. */ if ((id->driver_info & UFTDI_JTAG_MASK) == UFTDI_JTAG_CHECK_STRING) { product_name = usb_get_product(uaa->device); for (i = 0; i < nitems(jtag_products_by_name); i++) { jbn = &jtag_products_by_name[i]; if (strstr(product_name, jbn->product_name) != NULL && (jbn->jtag_interfaces & iface_bit) != 0) return (1); } } else if ((id->driver_info & iface_bit) != 0) return (1); return (0); } /* * Set up softc fields whose value depends on the device type. * * Note that the 2232C and 2232D devices are the same for our purposes. In the * silicon the difference is that the D series has CPU FIFO mode and C doesn't. * I haven't found any way of determining the C/D difference from info provided * by the chip other than trying to set CPU FIFO mode and having it work or not. * * Due to a hardware bug, a 232B chip without an eeprom reports itself as a * 232A, but if the serial number is also zero we know it's really a 232B. */ static void uftdi_devtype_setup(struct uftdi_softc *sc, struct usb_attach_arg *uaa) { struct usb_device_descriptor *dd; sc->sc_bcdDevice = uaa->info.bcdDevice; switch (uaa->info.bcdDevice) { case 0x200: dd = usbd_get_device_descriptor(sc->sc_udev); if (dd->iSerialNumber == 0) { sc->sc_devtype = DEVT_232B; } else { sc->sc_devtype = DEVT_232A; } sc->sc_ucom.sc_portno = 0; break; case 0x400: sc->sc_devtype = DEVT_232B; sc->sc_ucom.sc_portno = 0; break; case 0x500: sc->sc_devtype = DEVT_2232D; sc->sc_devflags |= DEVF_BAUDBITS_HINDEX; sc->sc_ucom.sc_portno = FTDI_PIT_SIOA + uaa->info.bIfaceNum; break; case 0x600: sc->sc_devtype = DEVT_232R; sc->sc_ucom.sc_portno = 0; break; case 0x700: sc->sc_devtype = DEVT_2232H; sc->sc_devflags |= DEVF_BAUDBITS_HINDEX | DEVF_BAUDCLK_12M; sc->sc_ucom.sc_portno = FTDI_PIT_SIOA + uaa->info.bIfaceNum; break; case 0x800: sc->sc_devtype = DEVT_4232H; sc->sc_devflags |= DEVF_BAUDBITS_HINDEX | DEVF_BAUDCLK_12M; sc->sc_ucom.sc_portno = FTDI_PIT_SIOA + uaa->info.bIfaceNum; break; case 0x900: sc->sc_devtype = DEVT_232H; sc->sc_devflags |= DEVF_BAUDBITS_HINDEX | DEVF_BAUDCLK_12M; sc->sc_ucom.sc_portno = FTDI_PIT_SIOA + uaa->info.bIfaceNum; break; case 0x1000: sc->sc_devtype = DEVT_230X; sc->sc_devflags |= DEVF_BAUDBITS_HINDEX; sc->sc_ucom.sc_portno = FTDI_PIT_SIOA + uaa->info.bIfaceNum; break; default: if (uaa->info.bcdDevice < 0x200) { sc->sc_devtype = DEVT_SIO; sc->sc_hdrlen = 1; } else { sc->sc_devtype = DEVT_232R; device_printf(sc->sc_dev, "Warning: unknown FTDI " "device type, bcdDevice=0x%04x, assuming 232R\n", uaa->info.bcdDevice); } sc->sc_ucom.sc_portno = 0; break; } } static int uftdi_probe(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); const struct usb_device_id *id; if (uaa->usb_mode != USB_MODE_HOST) { return (ENXIO); } if (uaa->info.bConfigIndex != UFTDI_CONFIG_INDEX) { return (ENXIO); } /* * Attach to all present interfaces unless this is a JTAG one, which * we leave for userland. */ id = usbd_lookup_id_by_info(uftdi_devs, sizeof(uftdi_devs), &uaa->info); if (id == NULL) return (ENXIO); if (skip_jtag_interfaces && is_jtag_interface(uaa, id)) { printf("%s: skipping JTAG interface #%d for '%s' at %u.%u\n", device_get_name(dev), uaa->info.bIfaceIndex, usb_get_product(uaa->device), usbd_get_bus_index(uaa->device), usbd_get_device_index(uaa->device)); return (ENXIO); } uaa->driver_info = id->driver_info; return (BUS_PROBE_SPECIFIC); } static int uftdi_attach(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); struct uftdi_softc *sc = device_get_softc(dev); int error; DPRINTF("\n"); sc->sc_udev = uaa->device; sc->sc_dev = dev; sc->sc_unit = device_get_unit(dev); sc->sc_bitmode = UFTDI_BITMODE_NONE; device_set_usb_desc(dev); mtx_init(&sc->sc_mtx, "uftdi", NULL, MTX_DEF); ucom_ref(&sc->sc_super_ucom); uftdi_devtype_setup(sc, uaa); error = usbd_transfer_setup(uaa->device, &uaa->info.bIfaceIndex, sc->sc_xfer, uftdi_config, UFTDI_N_TRANSFER, sc, &sc->sc_mtx); if (error) { device_printf(dev, "allocating USB " "transfers failed\n"); goto detach; } /* clear stall at first run */ mtx_lock(&sc->sc_mtx); - usbd_xfer_set_stall(sc->sc_xfer[UFTDI_BULK_DT_WR]); - usbd_xfer_set_stall(sc->sc_xfer[UFTDI_BULK_DT_RD]); + usbd_xfer_set_zlp(sc->sc_xfer[UFTDI_BULK_DT_WR]); mtx_unlock(&sc->sc_mtx); /* set a valid "lcr" value */ sc->sc_last_lcr = (FTDI_SIO_SET_DATA_STOP_BITS_2 | FTDI_SIO_SET_DATA_PARITY_NONE | FTDI_SIO_SET_DATA_BITS(8)); /* Indicate tx bits in sc_lsr can be used to determine busy vs idle. */ ucom_use_lsr_txbits(&sc->sc_ucom); error = ucom_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc, &uftdi_callback, &sc->sc_mtx); if (error) { goto detach; } ucom_set_pnpinfo_usb(&sc->sc_super_ucom, dev); return (0); /* success */ detach: uftdi_detach(dev); return (ENXIO); } static int uftdi_detach(device_t dev) { struct uftdi_softc *sc = device_get_softc(dev); ucom_detach(&sc->sc_super_ucom, &sc->sc_ucom); usbd_transfer_unsetup(sc->sc_xfer, UFTDI_N_TRANSFER); device_claim_softc(dev); uftdi_free_softc(sc); return (0); } UCOM_UNLOAD_DRAIN(uftdi); static void uftdi_free_softc(struct uftdi_softc *sc) { if (ucom_unref(&sc->sc_super_ucom)) { mtx_destroy(&sc->sc_mtx); device_free_softc(sc); } } static void uftdi_free(struct ucom_softc *ucom) { uftdi_free_softc(ucom->sc_parent); } static void uftdi_cfg_open(struct ucom_softc *ucom) { /* * This do-nothing open routine exists for the sole purpose of this * DPRINTF() so that you can see the point at which open gets called * when debugging is enabled. */ DPRINTF("\n"); } static void uftdi_cfg_close(struct ucom_softc *ucom) { /* * This do-nothing close routine exists for the sole purpose of this * DPRINTF() so that you can see the point at which close gets called * when debugging is enabled. */ DPRINTF("\n"); } static void uftdi_write_callback(struct usb_xfer *xfer, usb_error_t error) { struct uftdi_softc *sc = usbd_xfer_softc(xfer); struct usb_page_cache *pc; uint32_t pktlen; uint32_t buflen; uint8_t buf[1]; DPRINTFN(3, "\n"); switch (USB_GET_STATE(xfer)) { default: /* Error */ if (error != USB_ERR_CANCELLED) { /* try to clear stall first */ usbd_xfer_set_stall(xfer); } /* FALLTHROUGH */ case USB_ST_SETUP: case USB_ST_TRANSFERRED: + if (usbd_xfer_get_and_clr_zlp(xfer)) + break; + /* * If output packets don't require headers (the common case) we * can just load the buffer up with payload bytes all at once. * Otherwise, loop to format packets into the buffer while there * is data available, and room for a packet header and at least * one byte of payload. * * NOTE: The FTDI chip doesn't accept zero length * packets. This cannot happen because the "pktlen" * will always be non-zero when "ucom_get_data()" * returns non-zero which we check below. */ pc = usbd_xfer_get_frame(xfer, 0); if (sc->sc_hdrlen == 0) { if (ucom_get_data(&sc->sc_ucom, pc, 0, UFTDI_OBUFSIZE, &buflen) == 0) break; } else { buflen = 0; while (buflen < UFTDI_OBUFSIZE - sc->sc_hdrlen - 1 && ucom_get_data(&sc->sc_ucom, pc, buflen + sc->sc_hdrlen, UFTDI_OPKTSIZE - sc->sc_hdrlen, &pktlen) != 0) { buf[0] = FTDI_OUT_TAG(pktlen, sc->sc_ucom.sc_portno); usbd_copy_in(pc, buflen, buf, 1); buflen += pktlen + sc->sc_hdrlen; } } if (buflen != 0) { usbd_xfer_set_frame_len(xfer, 0, buflen); usbd_transfer_submit(xfer); } break; } } static void uftdi_read_callback(struct usb_xfer *xfer, usb_error_t error) { struct uftdi_softc *sc = usbd_xfer_softc(xfer); struct usb_page_cache *pc; uint8_t buf[2]; uint8_t ftdi_msr; uint8_t msr; uint8_t lsr; int buflen; int pktlen; int pktmax; int offset; DPRINTFN(3, "\n"); usbd_xfer_status(xfer, &buflen, NULL, NULL, NULL); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: if (buflen < UFTDI_IHDRSIZE) goto tr_setup; pc = usbd_xfer_get_frame(xfer, 0); pktmax = xfer->max_packet_size - UFTDI_IHDRSIZE; lsr = 0; msr = 0; offset = 0; /* * Extract packet headers and payload bytes from the buffer. * Feed payload bytes to ucom/tty layer; OR-accumulate the * receiver-related header status bits which are transient and * could toggle with each packet, but for transmitter-related * bits keep only the ones from the last packet. * * After processing all packets in the buffer, process the * accumulated transient MSR and LSR values along with the * non-transient bits from the last packet header. */ while (buflen >= UFTDI_IHDRSIZE) { usbd_copy_out(pc, offset, buf, UFTDI_IHDRSIZE); offset += UFTDI_IHDRSIZE; buflen -= UFTDI_IHDRSIZE; lsr &= ~(ULSR_TXRDY | ULSR_TSRE); lsr |= FTDI_GET_LSR(buf); if (FTDI_GET_MSR(buf) & FTDI_SIO_RI_MASK) msr |= SER_RI; pktlen = min(buflen, pktmax); if (pktlen != 0) { ucom_put_data(&sc->sc_ucom, pc, offset, pktlen); offset += pktlen; buflen -= pktlen; } } ftdi_msr = FTDI_GET_MSR(buf); if (ftdi_msr & FTDI_SIO_CTS_MASK) msr |= SER_CTS; if (ftdi_msr & FTDI_SIO_DSR_MASK) msr |= SER_DSR; if (ftdi_msr & FTDI_SIO_RI_MASK) msr |= SER_RI; if (ftdi_msr & FTDI_SIO_RLSD_MASK) msr |= SER_DCD; if (sc->sc_msr != msr || sc->sc_lsr != lsr) { DPRINTF("status change msr=0x%02x (0x%02x) " "lsr=0x%02x (0x%02x)\n", msr, sc->sc_msr, lsr, sc->sc_lsr); sc->sc_msr = msr; sc->sc_lsr = lsr; ucom_status_change(&sc->sc_ucom); } /* FALLTHROUGH */ case USB_ST_SETUP: tr_setup: usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); usbd_transfer_submit(xfer); return; default: /* Error */ if (error != USB_ERR_CANCELLED) { /* try to clear stall first */ usbd_xfer_set_stall(xfer); goto tr_setup; } return; } } static void uftdi_cfg_set_dtr(struct ucom_softc *ucom, uint8_t onoff) { struct uftdi_softc *sc = ucom->sc_parent; uint16_t wIndex = ucom->sc_portno; uint16_t wValue; struct usb_device_request req; DPRINTFN(2, "DTR=%u\n", onoff); wValue = onoff ? FTDI_SIO_SET_DTR_HIGH : FTDI_SIO_SET_DTR_LOW; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = FTDI_SIO_MODEM_CTRL; USETW(req.wValue, wValue); USETW(req.wIndex, wIndex); USETW(req.wLength, 0); ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, &req, NULL, 0, 1000); } static void uftdi_cfg_set_rts(struct ucom_softc *ucom, uint8_t onoff) { struct uftdi_softc *sc = ucom->sc_parent; uint16_t wIndex = ucom->sc_portno; uint16_t wValue; struct usb_device_request req; DPRINTFN(2, "RTS=%u\n", onoff); wValue = onoff ? FTDI_SIO_SET_RTS_HIGH : FTDI_SIO_SET_RTS_LOW; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = FTDI_SIO_MODEM_CTRL; USETW(req.wValue, wValue); USETW(req.wIndex, wIndex); USETW(req.wLength, 0); ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, &req, NULL, 0, 1000); } static void uftdi_cfg_set_break(struct ucom_softc *ucom, uint8_t onoff) { struct uftdi_softc *sc = ucom->sc_parent; uint16_t wIndex = ucom->sc_portno; uint16_t wValue; struct usb_device_request req; DPRINTFN(2, "BREAK=%u\n", onoff); if (onoff) { sc->sc_last_lcr |= FTDI_SIO_SET_BREAK; } else { sc->sc_last_lcr &= ~FTDI_SIO_SET_BREAK; } wValue = sc->sc_last_lcr; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = FTDI_SIO_SET_DATA; USETW(req.wValue, wValue); USETW(req.wIndex, wIndex); USETW(req.wLength, 0); ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, &req, NULL, 0, 1000); } /* * Return true if the given speed is within operational tolerance of the target * speed. FTDI recommends that the hardware speed be within 3% of nominal. */ static inline boolean_t uftdi_baud_within_tolerance(uint64_t speed, uint64_t target) { return ((speed >= (target * 100) / 103) && (speed <= (target * 100) / 97)); } static int uftdi_sio_encode_baudrate(struct uftdi_softc *sc, speed_t speed, struct uftdi_param_config *cfg) { u_int i; const speed_t sio_speeds[] = { 300, 600, 1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200 }; /* * The original SIO chips were limited to a small choice of speeds * listed in an internal table of speeds chosen by an index value. */ for (i = 0; i < nitems(sio_speeds); ++i) { if (speed == sio_speeds[i]) { cfg->baud_lobits = i; cfg->baud_hibits = 0; return (0); } } return (ERANGE); } static int uftdi_encode_baudrate(struct uftdi_softc *sc, speed_t speed, struct uftdi_param_config *cfg) { static const uint8_t encoded_fraction[8] = {0, 3, 2, 4, 1, 5, 6, 7}; static const uint8_t roundoff_232a[16] = { 0, 1, 0, 1, 0, -1, 2, 1, 0, -1, -2, -3, 4, 3, 2, 1, }; uint32_t clk, divisor, fastclk_flag, frac, hwspeed; /* * If this chip has the fast clock capability and the speed is within * range, use the 12MHz clock, otherwise the standard clock is 3MHz. */ if ((sc->sc_devflags & DEVF_BAUDCLK_12M) && speed >= 1200) { clk = 12000000; fastclk_flag = (1 << 17); } else { clk = 3000000; fastclk_flag = 0; } /* * Make sure the requested speed is reachable with the available clock * and a 14-bit divisor. */ if (speed < (clk >> 14) || speed > clk) return (ERANGE); /* * Calculate the divisor, initially yielding a fixed point number with a * 4-bit (1/16ths) fraction, then round it to the nearest fraction the * hardware can handle. When the integral part of the divisor is * greater than one, the fractional part is in 1/8ths of the base clock. * The FT8U232AM chips can handle only 0.125, 0.250, and 0.5 fractions. * Later chips can handle all 1/8th fractions. * * If the integral part of the divisor is 1, a special rule applies: the * fractional part can only be .0 or .5 (this is a limitation of the * hardware). We handle this by truncating the fraction rather than * rounding, because this only applies to the two fastest speeds the * chip can achieve and rounding doesn't matter, either you've asked for * that exact speed or you've asked for something the chip can't do. * * For the FT8U232AM chips, use a roundoff table to adjust the result * to the nearest 1/8th fraction that is supported by the hardware, * leaving a fixed-point number with a 3-bit fraction which exactly * represents the math the hardware divider will do. For later-series * chips that support all 8 fractional divisors, just round 16ths to * 8ths by adding 1 and dividing by 2. */ divisor = (clk << 4) / speed; if ((divisor & 0xf) == 1) divisor &= 0xfffffff8; else if (sc->sc_devtype == DEVT_232A) divisor += roundoff_232a[divisor & 0x0f]; else divisor += 1; /* Rounds odd 16ths up to next 8th. */ divisor >>= 1; /* * Ensure the resulting hardware speed will be within operational * tolerance (within 3% of nominal). */ hwspeed = (clk << 3) / divisor; if (!uftdi_baud_within_tolerance(hwspeed, speed)) return (ERANGE); /* * Re-pack the divisor into hardware format. The lower 14-bits hold the * integral part, while the upper bits specify the fraction by indexing * a table of fractions within the hardware which is laid out as: * {0.0, 0.5, 0.25, 0.125, 0.325, 0.625, 0.725, 0.875} * The A-series chips only have the first four table entries; the * roundoff table logic above ensures that the fractional part for those * chips will be one of the first four values. * * When the divisor is 1 a special encoding applies: 1.0 is encoded as * 0.0, and 1.5 is encoded as 1.0. The rounding logic above has already * ensured that the fraction is either .0 or .5 if the integral is 1. */ frac = divisor & 0x07; divisor >>= 3; if (divisor == 1) { if (frac == 0) divisor = 0; /* 1.0 becomes 0.0 */ else frac = 0; /* 1.5 becomes 1.0 */ } divisor |= (encoded_fraction[frac] << 14) | fastclk_flag; cfg->baud_lobits = (uint16_t)divisor; cfg->baud_hibits = (uint16_t)(divisor >> 16); /* * If this chip requires the baud bits to be in the high byte of the * index word, move the bits up to that location. */ if (sc->sc_devflags & DEVF_BAUDBITS_HINDEX) { cfg->baud_hibits <<= 8; } return (0); } static int uftdi_set_parm_soft(struct ucom_softc *ucom, struct termios *t, struct uftdi_param_config *cfg) { struct uftdi_softc *sc = ucom->sc_parent; int err; memset(cfg, 0, sizeof(*cfg)); if (sc->sc_devtype == DEVT_SIO) err = uftdi_sio_encode_baudrate(sc, t->c_ospeed, cfg); else err = uftdi_encode_baudrate(sc, t->c_ospeed, cfg); if (err != 0) return (err); if (t->c_cflag & CSTOPB) cfg->lcr = FTDI_SIO_SET_DATA_STOP_BITS_2; else cfg->lcr = FTDI_SIO_SET_DATA_STOP_BITS_1; if (t->c_cflag & PARENB) { if (t->c_cflag & PARODD) { cfg->lcr |= FTDI_SIO_SET_DATA_PARITY_ODD; } else { cfg->lcr |= FTDI_SIO_SET_DATA_PARITY_EVEN; } } else { cfg->lcr |= FTDI_SIO_SET_DATA_PARITY_NONE; } switch (t->c_cflag & CSIZE) { case CS5: cfg->lcr |= FTDI_SIO_SET_DATA_BITS(5); break; case CS6: cfg->lcr |= FTDI_SIO_SET_DATA_BITS(6); break; case CS7: cfg->lcr |= FTDI_SIO_SET_DATA_BITS(7); break; case CS8: cfg->lcr |= FTDI_SIO_SET_DATA_BITS(8); break; } if (t->c_cflag & CRTSCTS) { cfg->v_flow = FTDI_SIO_RTS_CTS_HS; } else if (t->c_iflag & (IXON | IXOFF)) { cfg->v_flow = FTDI_SIO_XON_XOFF_HS; cfg->v_start = t->c_cc[VSTART]; cfg->v_stop = t->c_cc[VSTOP]; } else { cfg->v_flow = FTDI_SIO_DISABLE_FLOW_CTRL; } return (0); } static int uftdi_pre_param(struct ucom_softc *ucom, struct termios *t) { struct uftdi_param_config cfg; DPRINTF("\n"); return (uftdi_set_parm_soft(ucom, t, &cfg)); } static void uftdi_cfg_param(struct ucom_softc *ucom, struct termios *t) { struct uftdi_softc *sc = ucom->sc_parent; uint16_t wIndex = ucom->sc_portno; struct uftdi_param_config cfg; struct usb_device_request req; DPRINTF("\n"); if (uftdi_set_parm_soft(ucom, t, &cfg)) { /* should not happen */ return; } sc->sc_last_lcr = cfg.lcr; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = FTDI_SIO_SET_BAUD_RATE; USETW(req.wValue, cfg.baud_lobits); USETW(req.wIndex, cfg.baud_hibits | wIndex); USETW(req.wLength, 0); ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, &req, NULL, 0, 1000); req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = FTDI_SIO_SET_DATA; USETW(req.wValue, cfg.lcr); USETW(req.wIndex, wIndex); USETW(req.wLength, 0); ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, &req, NULL, 0, 1000); req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = FTDI_SIO_SET_FLOW_CTRL; USETW2(req.wValue, cfg.v_stop, cfg.v_start); USETW2(req.wIndex, cfg.v_flow, wIndex); USETW(req.wLength, 0); ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, &req, NULL, 0, 1000); } static void uftdi_cfg_get_status(struct ucom_softc *ucom, uint8_t *lsr, uint8_t *msr) { struct uftdi_softc *sc = ucom->sc_parent; DPRINTFN(3, "msr=0x%02x lsr=0x%02x\n", sc->sc_msr, sc->sc_lsr); *msr = sc->sc_msr; *lsr = sc->sc_lsr; } static int uftdi_reset(struct ucom_softc *ucom, int reset_type) { struct uftdi_softc *sc = ucom->sc_parent; usb_device_request_t req; DPRINTFN(2, "\n"); req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = FTDI_SIO_RESET; USETW(req.wIndex, sc->sc_ucom.sc_portno); USETW(req.wLength, 0); USETW(req.wValue, reset_type); return (usbd_do_request(sc->sc_udev, &sc->sc_mtx, &req, NULL)); } static int uftdi_set_bitmode(struct ucom_softc *ucom, uint8_t bitmode, uint8_t iomask) { struct uftdi_softc *sc = ucom->sc_parent; usb_device_request_t req; int rv; DPRINTFN(2, "\n"); req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = FTDI_SIO_SET_BITMODE; USETW(req.wIndex, sc->sc_ucom.sc_portno); USETW(req.wLength, 0); if (bitmode == UFTDI_BITMODE_NONE) USETW2(req.wValue, 0, 0); else USETW2(req.wValue, (1 << bitmode), iomask); rv = usbd_do_request(sc->sc_udev, &sc->sc_mtx, &req, NULL); if (rv == USB_ERR_NORMAL_COMPLETION) sc->sc_bitmode = bitmode; return (rv); } static int uftdi_get_bitmode(struct ucom_softc *ucom, uint8_t *bitmode, uint8_t *iomask) { struct uftdi_softc *sc = ucom->sc_parent; usb_device_request_t req; DPRINTFN(2, "\n"); req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = FTDI_SIO_GET_BITMODE; USETW(req.wIndex, sc->sc_ucom.sc_portno); USETW(req.wLength, 1); USETW(req.wValue, 0); *bitmode = sc->sc_bitmode; return (usbd_do_request(sc->sc_udev, &sc->sc_mtx, &req, iomask)); } static int uftdi_set_latency(struct ucom_softc *ucom, int latency) { struct uftdi_softc *sc = ucom->sc_parent; usb_device_request_t req; DPRINTFN(2, "\n"); if (latency < 0 || latency > 255) return (USB_ERR_INVAL); req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = FTDI_SIO_SET_LATENCY; USETW(req.wIndex, sc->sc_ucom.sc_portno); USETW(req.wLength, 0); USETW2(req.wValue, 0, latency); return (usbd_do_request(sc->sc_udev, &sc->sc_mtx, &req, NULL)); } static int uftdi_get_latency(struct ucom_softc *ucom, int *latency) { struct uftdi_softc *sc = ucom->sc_parent; usb_device_request_t req; usb_error_t err; uint8_t buf; DPRINTFN(2, "\n"); req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = FTDI_SIO_GET_LATENCY; USETW(req.wIndex, sc->sc_ucom.sc_portno); USETW(req.wLength, 1); USETW(req.wValue, 0); err = usbd_do_request(sc->sc_udev, &sc->sc_mtx, &req, &buf); *latency = buf; return (err); } static int uftdi_set_event_char(struct ucom_softc *ucom, int echar) { struct uftdi_softc *sc = ucom->sc_parent; usb_device_request_t req; uint8_t enable; DPRINTFN(2, "\n"); enable = (echar == -1) ? 0 : 1; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = FTDI_SIO_SET_EVENT_CHAR; USETW(req.wIndex, sc->sc_ucom.sc_portno); USETW(req.wLength, 0); USETW2(req.wValue, enable, echar & 0xff); return (usbd_do_request(sc->sc_udev, &sc->sc_mtx, &req, NULL)); } static int uftdi_set_error_char(struct ucom_softc *ucom, int echar) { struct uftdi_softc *sc = ucom->sc_parent; usb_device_request_t req; uint8_t enable; DPRINTFN(2, "\n"); enable = (echar == -1) ? 0 : 1; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = FTDI_SIO_SET_ERROR_CHAR; USETW(req.wIndex, sc->sc_ucom.sc_portno); USETW(req.wLength, 0); USETW2(req.wValue, enable, echar & 0xff); return (usbd_do_request(sc->sc_udev, &sc->sc_mtx, &req, NULL)); } static int uftdi_read_eeprom(struct ucom_softc *ucom, struct uftdi_eeio *eeio) { struct uftdi_softc *sc = ucom->sc_parent; usb_device_request_t req; usb_error_t err; uint16_t widx, wlength, woffset; DPRINTFN(3, "\n"); /* Offset and length must both be evenly divisible by two. */ if ((eeio->offset | eeio->length) & 0x01) return (EINVAL); woffset = eeio->offset / 2U; wlength = eeio->length / 2U; for (widx = 0; widx < wlength; widx++) { req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = FTDI_SIO_READ_EEPROM; USETW(req.wIndex, widx + woffset); USETW(req.wLength, 2); USETW(req.wValue, 0); err = usbd_do_request(sc->sc_udev, &sc->sc_mtx, &req, &eeio->data[widx]); if (err != USB_ERR_NORMAL_COMPLETION) return (err); } return (USB_ERR_NORMAL_COMPLETION); } static int uftdi_write_eeprom(struct ucom_softc *ucom, struct uftdi_eeio *eeio) { struct uftdi_softc *sc = ucom->sc_parent; usb_device_request_t req; usb_error_t err; uint16_t widx, wlength, woffset; DPRINTFN(3, "\n"); /* Offset and length must both be evenly divisible by two. */ if ((eeio->offset | eeio->length) & 0x01) return (EINVAL); woffset = eeio->offset / 2U; wlength = eeio->length / 2U; for (widx = 0; widx < wlength; widx++) { req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = FTDI_SIO_WRITE_EEPROM; USETW(req.wIndex, widx + woffset); USETW(req.wLength, 0); USETW(req.wValue, eeio->data[widx]); err = usbd_do_request(sc->sc_udev, &sc->sc_mtx, &req, NULL); if (err != USB_ERR_NORMAL_COMPLETION) return (err); } return (USB_ERR_NORMAL_COMPLETION); } static int uftdi_erase_eeprom(struct ucom_softc *ucom, int confirmation) { struct uftdi_softc *sc = ucom->sc_parent; usb_device_request_t req; usb_error_t err; DPRINTFN(2, "\n"); /* Small effort to prevent accidental erasure. */ if (confirmation != UFTDI_CONFIRM_ERASE) return (EINVAL); req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = FTDI_SIO_ERASE_EEPROM; USETW(req.wIndex, 0); USETW(req.wLength, 0); USETW(req.wValue, 0); err = usbd_do_request(sc->sc_udev, &sc->sc_mtx, &req, NULL); return (err); } static int uftdi_ioctl(struct ucom_softc *ucom, uint32_t cmd, caddr_t data, int flag, struct thread *td) { struct uftdi_softc *sc = ucom->sc_parent; int err; struct uftdi_bitmode * mode; switch (cmd) { case UFTDIIOC_RESET_IO: case UFTDIIOC_RESET_RX: case UFTDIIOC_RESET_TX: err = uftdi_reset(ucom, cmd == UFTDIIOC_RESET_IO ? FTDI_SIO_RESET_SIO : (cmd == UFTDIIOC_RESET_RX ? FTDI_SIO_RESET_PURGE_RX : FTDI_SIO_RESET_PURGE_TX)); break; case UFTDIIOC_SET_BITMODE: mode = (struct uftdi_bitmode *)data; err = uftdi_set_bitmode(ucom, mode->mode, mode->iomask); break; case UFTDIIOC_GET_BITMODE: mode = (struct uftdi_bitmode *)data; err = uftdi_get_bitmode(ucom, &mode->mode, &mode->iomask); break; case UFTDIIOC_SET_LATENCY: err = uftdi_set_latency(ucom, *((int *)data)); break; case UFTDIIOC_GET_LATENCY: err = uftdi_get_latency(ucom, (int *)data); break; case UFTDIIOC_SET_ERROR_CHAR: err = uftdi_set_error_char(ucom, *(int *)data); break; case UFTDIIOC_SET_EVENT_CHAR: err = uftdi_set_event_char(ucom, *(int *)data); break; case UFTDIIOC_GET_HWREV: *(int *)data = sc->sc_bcdDevice; err = 0; break; case UFTDIIOC_READ_EEPROM: err = uftdi_read_eeprom(ucom, (struct uftdi_eeio *)data); break; case UFTDIIOC_WRITE_EEPROM: err = uftdi_write_eeprom(ucom, (struct uftdi_eeio *)data); break; case UFTDIIOC_ERASE_EEPROM: err = uftdi_erase_eeprom(ucom, *(int *)data); break; default: return (ENOIOCTL); } if (err != USB_ERR_NORMAL_COMPLETION) return (EIO); return (0); } static void uftdi_start_read(struct ucom_softc *ucom) { struct uftdi_softc *sc = ucom->sc_parent; usbd_transfer_start(sc->sc_xfer[UFTDI_BULK_DT_RD]); } static void uftdi_stop_read(struct ucom_softc *ucom) { struct uftdi_softc *sc = ucom->sc_parent; usbd_transfer_stop(sc->sc_xfer[UFTDI_BULK_DT_RD]); } static void uftdi_start_write(struct ucom_softc *ucom) { struct uftdi_softc *sc = ucom->sc_parent; usbd_transfer_start(sc->sc_xfer[UFTDI_BULK_DT_WR]); } static void uftdi_stop_write(struct ucom_softc *ucom) { struct uftdi_softc *sc = ucom->sc_parent; usbd_transfer_stop(sc->sc_xfer[UFTDI_BULK_DT_WR]); } static void uftdi_poll(struct ucom_softc *ucom) { struct uftdi_softc *sc = ucom->sc_parent; usbd_transfer_poll(sc->sc_xfer, UFTDI_N_TRANSFER); } diff --git a/sys/dev/usb/serial/ugensa.c b/sys/dev/usb/serial/ugensa.c index ba694e4805bd..1d2f1e57ef58 100644 --- a/sys/dev/usb/serial/ugensa.c +++ b/sys/dev/usb/serial/ugensa.c @@ -1,407 +1,408 @@ /* $FreeBSD$ */ /* $NetBSD: ugensa.c,v 1.9.2.1 2007/03/24 14:55:50 yamt Exp $ */ /*- * SPDX-License-Identifier: BSD-2-Clause-NetBSD * * Copyright (c) 2004, 2005 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Roland C. Dowdeswell . * * 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. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 THE FOUNDATION OR CONTRIBUTORS * 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. */ /* * NOTE: all function names beginning like "ugensa_cfg_" can only * be called from within the config thread function ! */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "usbdevs.h" #define USB_DEBUG_VAR usb_debug #include #include #include #define UGENSA_BUF_SIZE 2048 /* bytes */ #define UGENSA_CONFIG_INDEX 0 #define UGENSA_IFACE_INDEX 0 #define UGENSA_IFACE_MAX 8 /* exclusivly */ #define UGENSA_PORT_MAX 8 /* exclusivly */ enum { UGENSA_BULK_DT_WR, UGENSA_BULK_DT_RD, UGENSA_N_TRANSFER, }; struct ugensa_sub_softc { struct ucom_softc *sc_ucom_ptr; struct usb_xfer *sc_xfer[UGENSA_N_TRANSFER]; }; struct ugensa_softc { struct ucom_super_softc sc_super_ucom; struct ucom_softc sc_ucom[UGENSA_PORT_MAX]; struct ugensa_sub_softc sc_sub[UGENSA_PORT_MAX]; struct mtx sc_mtx; uint8_t sc_nports; }; /* prototypes */ static device_probe_t ugensa_probe; static device_attach_t ugensa_attach; static device_detach_t ugensa_detach; static void ugensa_free_softc(struct ugensa_softc *); static usb_callback_t ugensa_bulk_write_callback; static usb_callback_t ugensa_bulk_read_callback; static void ugensa_free(struct ucom_softc *); static void ugensa_start_read(struct ucom_softc *); static void ugensa_stop_read(struct ucom_softc *); static void ugensa_start_write(struct ucom_softc *); static void ugensa_stop_write(struct ucom_softc *); static void ugensa_poll(struct ucom_softc *ucom); static const struct usb_config ugensa_xfer_config[UGENSA_N_TRANSFER] = { [UGENSA_BULK_DT_WR] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, .bufsize = UGENSA_BUF_SIZE, .flags = {.pipe_bof = 1,.force_short_xfer = 1,}, .callback = &ugensa_bulk_write_callback, }, [UGENSA_BULK_DT_RD] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, .bufsize = UGENSA_BUF_SIZE, .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, .callback = &ugensa_bulk_read_callback, }, }; static const struct ucom_callback ugensa_callback = { .ucom_start_read = &ugensa_start_read, .ucom_stop_read = &ugensa_stop_read, .ucom_start_write = &ugensa_start_write, .ucom_stop_write = &ugensa_stop_write, .ucom_poll = &ugensa_poll, .ucom_free = &ugensa_free, }; static device_method_t ugensa_methods[] = { /* Device methods */ DEVMETHOD(device_probe, ugensa_probe), DEVMETHOD(device_attach, ugensa_attach), DEVMETHOD(device_detach, ugensa_detach), DEVMETHOD_END }; static devclass_t ugensa_devclass; static driver_t ugensa_driver = { .name = "ugensa", .methods = ugensa_methods, .size = sizeof(struct ugensa_softc), }; /* Driver-info is max number of serial ports per interface */ static const STRUCT_USB_HOST_ID ugensa_devs[] = { {USB_VPI(USB_VENDOR_AIRPRIME, USB_PRODUCT_AIRPRIME_PC5220, 1)}, {USB_VPI(USB_VENDOR_CMOTECH, USB_PRODUCT_CMOTECH_CDMA_MODEM1, 1)}, {USB_VPI(USB_VENDOR_KYOCERA2, USB_PRODUCT_KYOCERA2_CDMA_MSM_K, 1)}, {USB_VPI(USB_VENDOR_HP, USB_PRODUCT_HP_49GPLUS, 1)}, {USB_VPI(USB_VENDOR_NOVATEL2, USB_PRODUCT_NOVATEL2_FLEXPACKGPS, 3)}, {USB_VENDOR(USB_VENDOR_GOOGLE), USB_IFACE_CLASS(UICLASS_VENDOR), USB_IFACE_SUBCLASS(0x50), USB_IFACE_PROTOCOL(0x01), USB_DRIVER_INFO(10)}, }; DRIVER_MODULE(ugensa, uhub, ugensa_driver, ugensa_devclass, NULL, 0); MODULE_DEPEND(ugensa, ucom, 1, 1, 1); MODULE_DEPEND(ugensa, usb, 1, 1, 1); MODULE_VERSION(ugensa, 1); USB_PNP_HOST_INFO(ugensa_devs); static int ugensa_probe(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); if (uaa->usb_mode != USB_MODE_HOST) { return (ENXIO); } if (uaa->info.bConfigIndex != UGENSA_CONFIG_INDEX) { return (ENXIO); } if (uaa->info.bIfaceIndex != 0) { return (ENXIO); } return (usbd_lookup_id_by_uaa(ugensa_devs, sizeof(ugensa_devs), uaa)); } static int ugensa_attach(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); struct ugensa_softc *sc = device_get_softc(dev); struct ugensa_sub_softc *ssc; struct usb_interface *iface; struct usb_config xfer_config[UGENSA_N_TRANSFER]; int32_t error; uint8_t iface_index; int x, maxports; maxports = USB_GET_DRIVER_INFO(uaa); device_set_usb_desc(dev); mtx_init(&sc->sc_mtx, "ugensa", NULL, MTX_DEF); ucom_ref(&sc->sc_super_ucom); for (iface_index = UGENSA_IFACE_INDEX; iface_index < UGENSA_IFACE_MAX; iface_index++) { iface = usbd_get_iface(uaa->device, iface_index); if (iface == NULL || iface->idesc->bInterfaceClass != UICLASS_VENDOR) /* Not a serial port, most likely a SD reader */ continue; /* Loop over all endpoints pairwise */ for (x = 0; x < maxports && sc->sc_nports < UGENSA_PORT_MAX; x++) { ssc = sc->sc_sub + sc->sc_nports; ssc->sc_ucom_ptr = sc->sc_ucom + sc->sc_nports; memcpy(xfer_config, ugensa_xfer_config, sizeof ugensa_xfer_config); xfer_config[UGENSA_BULK_DT_RD].ep_index = x; xfer_config[UGENSA_BULK_DT_WR].ep_index = x; error = usbd_transfer_setup(uaa->device, &iface_index, ssc->sc_xfer, xfer_config, UGENSA_N_TRANSFER, ssc, &sc->sc_mtx); if (error) { if (x == 0) { device_printf(dev, "allocating USB " "transfers failed (%d)\n", error); goto detach; } break; } - /* clear stall at first run */ mtx_lock(&sc->sc_mtx); - usbd_xfer_set_stall(ssc->sc_xfer[UGENSA_BULK_DT_WR]); - usbd_xfer_set_stall(ssc->sc_xfer[UGENSA_BULK_DT_RD]); + usbd_xfer_set_zlp(ssc->sc_xfer[UGENSA_BULK_DT_WR]); mtx_unlock(&sc->sc_mtx); /* initialize port number */ ssc->sc_ucom_ptr->sc_portno = sc->sc_nports; if (iface_index != uaa->info.bIfaceIndex) { usbd_set_parent_iface(uaa->device, iface_index, uaa->info.bIfaceIndex); } sc->sc_nports++; } } device_printf(dev, "Found %d serial ports.\n", sc->sc_nports); error = ucom_attach(&sc->sc_super_ucom, sc->sc_ucom, sc->sc_nports, sc, &ugensa_callback, &sc->sc_mtx); if (error) { DPRINTF("ucom attach failed\n"); goto detach; } ucom_set_pnpinfo_usb(&sc->sc_super_ucom, dev); return (0); /* success */ detach: ugensa_detach(dev); return (ENXIO); /* failure */ } static int ugensa_detach(device_t dev) { struct ugensa_softc *sc = device_get_softc(dev); uint8_t x; ucom_detach(&sc->sc_super_ucom, sc->sc_ucom); for (x = 0; x < sc->sc_nports; x++) { usbd_transfer_unsetup(sc->sc_sub[x].sc_xfer, UGENSA_N_TRANSFER); } device_claim_softc(dev); ugensa_free_softc(sc); return (0); } UCOM_UNLOAD_DRAIN(ugensa); static void ugensa_free_softc(struct ugensa_softc *sc) { if (ucom_unref(&sc->sc_super_ucom)) { mtx_destroy(&sc->sc_mtx); device_free_softc(sc); } } static void ugensa_free(struct ucom_softc *ucom) { ugensa_free_softc(ucom->sc_parent); } static void ugensa_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error) { struct ugensa_sub_softc *ssc = usbd_xfer_softc(xfer); struct usb_page_cache *pc; uint32_t actlen; switch (USB_GET_STATE(xfer)) { case USB_ST_SETUP: case USB_ST_TRANSFERRED: tr_setup: + if (usbd_xfer_get_and_clr_zlp(xfer)) + break; + pc = usbd_xfer_get_frame(xfer, 0); if (ucom_get_data(ssc->sc_ucom_ptr, pc, 0, UGENSA_BUF_SIZE, &actlen)) { usbd_xfer_set_frame_len(xfer, 0, actlen); usbd_transfer_submit(xfer); } - return; + break; default: /* Error */ if (error != USB_ERR_CANCELLED) { /* try to clear stall first */ usbd_xfer_set_stall(xfer); goto tr_setup; } - return; + break; } } static void ugensa_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error) { struct ugensa_sub_softc *ssc = usbd_xfer_softc(xfer); struct usb_page_cache *pc; int actlen; usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: pc = usbd_xfer_get_frame(xfer, 0); ucom_put_data(ssc->sc_ucom_ptr, pc, 0, actlen); case USB_ST_SETUP: tr_setup: usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); usbd_transfer_submit(xfer); return; default: /* Error */ if (error != USB_ERR_CANCELLED) { /* try to clear stall first */ usbd_xfer_set_stall(xfer); goto tr_setup; } return; } } static void ugensa_start_read(struct ucom_softc *ucom) { struct ugensa_softc *sc = ucom->sc_parent; struct ugensa_sub_softc *ssc = sc->sc_sub + ucom->sc_portno; usbd_transfer_start(ssc->sc_xfer[UGENSA_BULK_DT_RD]); } static void ugensa_stop_read(struct ucom_softc *ucom) { struct ugensa_softc *sc = ucom->sc_parent; struct ugensa_sub_softc *ssc = sc->sc_sub + ucom->sc_portno; usbd_transfer_stop(ssc->sc_xfer[UGENSA_BULK_DT_RD]); } static void ugensa_start_write(struct ucom_softc *ucom) { struct ugensa_softc *sc = ucom->sc_parent; struct ugensa_sub_softc *ssc = sc->sc_sub + ucom->sc_portno; usbd_transfer_start(ssc->sc_xfer[UGENSA_BULK_DT_WR]); } static void ugensa_stop_write(struct ucom_softc *ucom) { struct ugensa_softc *sc = ucom->sc_parent; struct ugensa_sub_softc *ssc = sc->sc_sub + ucom->sc_portno; usbd_transfer_stop(ssc->sc_xfer[UGENSA_BULK_DT_WR]); } static void ugensa_poll(struct ucom_softc *ucom) { struct ugensa_softc *sc = ucom->sc_parent; struct ugensa_sub_softc *ssc = sc->sc_sub + ucom->sc_portno; usbd_transfer_poll(ssc->sc_xfer, UGENSA_N_TRANSFER); } diff --git a/sys/dev/usb/serial/uipaq.c b/sys/dev/usb/serial/uipaq.c index 33f2ae4c0845..0449a5663c71 100644 --- a/sys/dev/usb/serial/uipaq.c +++ b/sys/dev/usb/serial/uipaq.c @@ -1,1376 +1,1378 @@ /* $NetBSD: uipaq.c,v 1.4 2006/11/16 01:33:27 christos Exp $ */ /* $OpenBSD: uipaq.c,v 1.1 2005/06/17 23:50:33 deraadt Exp $ */ /*- * SPDX-License-Identifier: BSD-2-Clause-NetBSD * * Copyright (c) 2000-2005 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (lennart@augustsson.net) at * Carlstedt Research & Technology. * * 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. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 THE FOUNDATION OR CONTRIBUTORS * 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. */ /* * iPAQ driver * * 19 July 2003: Incorporated changes suggested by Sam Lawrance from * the uppc module * * * Contact isis@cs.umd.edu if you have any questions/comments about this driver */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "usbdevs.h" #define USB_DEBUG_VAR usb_debug #include #include #include #define UIPAQ_CONFIG_INDEX 0 /* config number 1 */ #define UIPAQ_IFACE_INDEX 0 #define UIPAQ_BUF_SIZE 1024 enum { UIPAQ_BULK_DT_WR, UIPAQ_BULK_DT_RD, UIPAQ_N_TRANSFER, }; struct uipaq_softc { struct ucom_super_softc sc_super_ucom; struct ucom_softc sc_ucom; struct usb_xfer *sc_xfer[UIPAQ_N_TRANSFER]; struct usb_device *sc_udev; struct mtx sc_mtx; uint16_t sc_line; uint8_t sc_lsr; /* local status register */ uint8_t sc_msr; /* modem status register */ }; static device_probe_t uipaq_probe; static device_attach_t uipaq_attach; static device_detach_t uipaq_detach; static void uipaq_free_softc(struct uipaq_softc *); static usb_callback_t uipaq_write_callback; static usb_callback_t uipaq_read_callback; static void uipaq_free(struct ucom_softc *); static void uipaq_start_read(struct ucom_softc *); static void uipaq_stop_read(struct ucom_softc *); static void uipaq_start_write(struct ucom_softc *); static void uipaq_stop_write(struct ucom_softc *); static void uipaq_cfg_set_dtr(struct ucom_softc *, uint8_t); static void uipaq_cfg_set_rts(struct ucom_softc *, uint8_t); static void uipaq_cfg_set_break(struct ucom_softc *, uint8_t); static void uipaq_poll(struct ucom_softc *ucom); static const struct usb_config uipaq_config_data[UIPAQ_N_TRANSFER] = { [UIPAQ_BULK_DT_WR] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, .bufsize = UIPAQ_BUF_SIZE, .flags = {.pipe_bof = 1,.force_short_xfer = 1,}, .callback = &uipaq_write_callback, }, [UIPAQ_BULK_DT_RD] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, .bufsize = UIPAQ_BUF_SIZE, .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, .callback = &uipaq_read_callback, }, }; static const struct ucom_callback uipaq_callback = { .ucom_cfg_set_dtr = &uipaq_cfg_set_dtr, .ucom_cfg_set_rts = &uipaq_cfg_set_rts, .ucom_cfg_set_break = &uipaq_cfg_set_break, .ucom_start_read = &uipaq_start_read, .ucom_stop_read = &uipaq_stop_read, .ucom_start_write = &uipaq_start_write, .ucom_stop_write = &uipaq_stop_write, .ucom_poll = &uipaq_poll, .ucom_free = &uipaq_free, }; /* * Much of this list is generated from lists of other drivers that * support the same hardware. Numeric values are used where no usbdevs * entries exist. */ static const STRUCT_USB_HOST_ID uipaq_devs[] = { /* Socket USB Sync */ {USB_VPI(0x0104, 0x00be, 0)}, /* USB Sync 0301 */ {USB_VPI(0x04ad, 0x0301, 0)}, /* USB Sync 0302 */ {USB_VPI(0x04ad, 0x0302, 0)}, /* USB Sync 0303 */ {USB_VPI(0x04ad, 0x0303, 0)}, /* GPS Pocket PC USB Sync */ {USB_VPI(0x04ad, 0x0306, 0)}, /* HHP PDT */ {USB_VPI(0x0536, 0x01a0, 0)}, /* Intermec Mobile Computer */ {USB_VPI(0x067e, 0x1001, 0)}, /* Linkup Systems USB Sync */ {USB_VPI(0x094b, 0x0001, 0)}, /* BCOM USB Sync 0065 */ {USB_VPI(0x0960, 0x0065, 0)}, /* BCOM USB Sync 0066 */ {USB_VPI(0x0960, 0x0066, 0)}, /* BCOM USB Sync 0067 */ {USB_VPI(0x0960, 0x0067, 0)}, /* Portatec USB Sync */ {USB_VPI(0x0961, 0x0010, 0)}, /* Trimble GeoExplorer */ {USB_VPI(0x099e, 0x0052, 0)}, /* TDS Data Collector */ {USB_VPI(0x099e, 0x4000, 0)}, /* Motorola iDEN Smartphone */ {USB_VPI(0x0c44, 0x03a2, 0)}, /* Cesscom Luxian Series */ {USB_VPI(0x0c8e, 0x6000, 0)}, /* Motorola PowerPad Pocket PCDevice */ {USB_VPI(0x0cad, 0x9001, 0)}, /* Freedom Scientific USB Sync */ {USB_VPI(0x0f4e, 0x0200, 0)}, /* Cyberbank USB Sync */ {USB_VPI(0x0f98, 0x0201, 0)}, /* Wistron USB Sync */ {USB_VPI(0x0fb8, 0x3001, 0)}, /* Wistron USB Sync */ {USB_VPI(0x0fb8, 0x3002, 0)}, /* Wistron USB Sync */ {USB_VPI(0x0fb8, 0x3003, 0)}, /* Wistron USB Sync */ {USB_VPI(0x0fb8, 0x4001, 0)}, /* E-TEN USB Sync */ {USB_VPI(0x1066, 0x00ce, 0)}, /* E-TEN P3XX Pocket PC */ {USB_VPI(0x1066, 0x0300, 0)}, /* E-TEN P5XX Pocket PC */ {USB_VPI(0x1066, 0x0500, 0)}, /* E-TEN P6XX Pocket PC */ {USB_VPI(0x1066, 0x0600, 0)}, /* E-TEN P7XX Pocket PC */ {USB_VPI(0x1066, 0x0700, 0)}, /* Psion Teklogix Sync 753x */ {USB_VPI(0x1114, 0x0001, 0)}, /* Psion Teklogix Sync netBookPro */ {USB_VPI(0x1114, 0x0004, 0)}, /* Psion Teklogix Sync 7525 */ {USB_VPI(0x1114, 0x0006, 0)}, /* VES USB Sync */ {USB_VPI(0x1182, 0x1388, 0)}, /* Rugged Pocket PC 2003 */ {USB_VPI(0x11d9, 0x1002, 0)}, /* Rugged Pocket PC 2003 */ {USB_VPI(0x11d9, 0x1003, 0)}, /* USB Sync 03 */ {USB_VPI(0x1231, 0xce01, 0)}, /* USB Sync 03 */ {USB_VPI(0x1231, 0xce02, 0)}, /* Mio DigiWalker PPC StrongARM */ {USB_VPI(0x3340, 0x011c, 0)}, /* Mio DigiWalker 338 */ {USB_VPI(0x3340, 0x0326, 0)}, /* Mio DigiWalker 338 */ {USB_VPI(0x3340, 0x0426, 0)}, /* Mio DigiWalker USB Sync */ {USB_VPI(0x3340, 0x043a, 0)}, /* MiTAC USB Sync 528 */ {USB_VPI(0x3340, 0x051c, 0)}, /* Mio DigiWalker SmartPhone USB Sync */ {USB_VPI(0x3340, 0x053a, 0)}, /* MiTAC USB Sync */ {USB_VPI(0x3340, 0x071c, 0)}, /* Generic PPC StrongARM */ {USB_VPI(0x3340, 0x0b1c, 0)}, /* Generic PPC USB Sync */ {USB_VPI(0x3340, 0x0e3a, 0)}, /* Itautec USB Sync */ {USB_VPI(0x3340, 0x0f1c, 0)}, /* Generic SmartPhone USB Sync */ {USB_VPI(0x3340, 0x0f3a, 0)}, /* Itautec USB Sync */ {USB_VPI(0x3340, 0x1326, 0)}, /* YAKUMO USB Sync */ {USB_VPI(0x3340, 0x191c, 0)}, /* Vobis USB Sync */ {USB_VPI(0x3340, 0x2326, 0)}, /* MEDION Winodws Moble USB Sync */ {USB_VPI(0x3340, 0x3326, 0)}, /* Legend USB Sync */ {USB_VPI(0x3708, 0x20ce, 0)}, /* Lenovo USB Sync */ {USB_VPI(0x3708, 0x21ce, 0)}, /* Mobile Media Technology USB Sync */ {USB_VPI(0x4113, 0x0210, 0)}, /* Mobile Media Technology USB Sync */ {USB_VPI(0x4113, 0x0211, 0)}, /* Mobile Media Technology USB Sync */ {USB_VPI(0x4113, 0x0400, 0)}, /* Mobile Media Technology USB Sync */ {USB_VPI(0x4113, 0x0410, 0)}, /* Smartphone */ {USB_VPI(0x4505, 0x0010, 0)}, /* SAGEM Wireless Assistant */ {USB_VPI(0x5e04, 0xce00, 0)}, /* c10 Series */ {USB_VPI(USB_VENDOR_ACER, 0x1631, 0)}, /* c20 Series */ {USB_VPI(USB_VENDOR_ACER, 0x1632, 0)}, /* Acer n10 Handheld USB Sync */ {USB_VPI(USB_VENDOR_ACER, 0x16e1, 0)}, /* Acer n20 Handheld USB Sync */ {USB_VPI(USB_VENDOR_ACER, 0x16e2, 0)}, /* Acer n30 Handheld USB Sync */ {USB_VPI(USB_VENDOR_ACER, 0x16e3, 0)}, /* ASUS USB Sync */ {USB_VPI(USB_VENDOR_ASUS, 0x4200, 0)}, /* ASUS USB Sync */ {USB_VPI(USB_VENDOR_ASUS, 0x4201, 0)}, /* ASUS USB Sync */ {USB_VPI(USB_VENDOR_ASUS, 0x4202, 0)}, /* ASUS USB Sync */ {USB_VPI(USB_VENDOR_ASUS, 0x9200, 0)}, /* ASUS USB Sync */ {USB_VPI(USB_VENDOR_ASUS, 0x9202, 0)}, /**/ {USB_VPI(USB_VENDOR_ASUS, USB_PRODUCT_ASUS_P535, 0)}, /* CASIO USB Sync 2001 */ {USB_VPI(USB_VENDOR_CASIO, 0x2001, 0)}, /* CASIO USB Sync 2003 */ {USB_VPI(USB_VENDOR_CASIO, 0x2003, 0)}, /**/ {USB_VPI(USB_VENDOR_CASIO, USB_PRODUCT_CASIO_BE300, 0)}, /* MyGuide 7000 XL USB Sync */ {USB_VPI(USB_VENDOR_COMPAL, 0x0531, 0)}, /* Compaq iPAQ USB Sync */ {USB_VPI(USB_VENDOR_COMPAQ, 0x0032, 0)}, /**/ {USB_VPI(USB_VENDOR_COMPAQ, USB_PRODUCT_COMPAQ_IPAQPOCKETPC, 0)}, /* Dell Axim USB Sync */ {USB_VPI(USB_VENDOR_DELL, 0x4001, 0)}, /* Dell Axim USB Sync */ {USB_VPI(USB_VENDOR_DELL, 0x4002, 0)}, /* Dell Axim USB Sync */ {USB_VPI(USB_VENDOR_DELL, 0x4003, 0)}, /* Dell Axim USB Sync */ {USB_VPI(USB_VENDOR_DELL, 0x4004, 0)}, /* Dell Axim USB Sync */ {USB_VPI(USB_VENDOR_DELL, 0x4005, 0)}, /* Dell Axim USB Sync */ {USB_VPI(USB_VENDOR_DELL, 0x4006, 0)}, /* Dell Axim USB Sync */ {USB_VPI(USB_VENDOR_DELL, 0x4007, 0)}, /* Dell Axim USB Sync */ {USB_VPI(USB_VENDOR_DELL, 0x4008, 0)}, /* Dell Axim USB Sync */ {USB_VPI(USB_VENDOR_DELL, 0x4009, 0)}, /* Fujitsu Siemens Computers USB Sync */ {USB_VPI(USB_VENDOR_FSC, 0x1001, 0)}, /* FUJITSU USB Sync */ {USB_VPI(USB_VENDOR_FUJITSU, 0x1058, 0)}, /* FUJITSU USB Sync */ {USB_VPI(USB_VENDOR_FUJITSU, 0x1079, 0)}, /* Askey USB Sync */ {USB_VPI(USB_VENDOR_GIGASET, 0x0601, 0)}, /* Hitachi USB Sync */ {USB_VPI(USB_VENDOR_HITACHI, 0x0014, 0)}, /* HP USB Sync 1612 */ {USB_VPI(USB_VENDOR_HP, 0x1216, 0)}, /* HP USB Sync 1620 */ {USB_VPI(USB_VENDOR_HP, 0x2016, 0)}, /* HP USB Sync 1621 */ {USB_VPI(USB_VENDOR_HP, 0x2116, 0)}, /* HP USB Sync 1622 */ {USB_VPI(USB_VENDOR_HP, 0x2216, 0)}, /* HP USB Sync 1630 */ {USB_VPI(USB_VENDOR_HP, 0x3016, 0)}, /* HP USB Sync 1631 */ {USB_VPI(USB_VENDOR_HP, 0x3116, 0)}, /* HP USB Sync 1632 */ {USB_VPI(USB_VENDOR_HP, 0x3216, 0)}, /* HP USB Sync 1640 */ {USB_VPI(USB_VENDOR_HP, 0x4016, 0)}, /* HP USB Sync 1641 */ {USB_VPI(USB_VENDOR_HP, 0x4116, 0)}, /* HP USB Sync 1642 */ {USB_VPI(USB_VENDOR_HP, 0x4216, 0)}, /* HP USB Sync 1650 */ {USB_VPI(USB_VENDOR_HP, 0x5016, 0)}, /* HP USB Sync 1651 */ {USB_VPI(USB_VENDOR_HP, 0x5116, 0)}, /* HP USB Sync 1652 */ {USB_VPI(USB_VENDOR_HP, 0x5216, 0)}, /**/ {USB_VPI(USB_VENDOR_HP, USB_PRODUCT_HP_2215, 0)}, /**/ {USB_VPI(USB_VENDOR_HP, USB_PRODUCT_HP_568J, 0)}, /* HTC USB Modem */ {USB_VPI(USB_VENDOR_HTC, 0x00cf, 0)}, /* PocketPC USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a01, 0)}, /* PocketPC USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a02, 0)}, /* PocketPC USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a03, 0)}, /* PocketPC USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a04, 0)}, /* PocketPC USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a05, 0)}, /* PocketPC USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a06, 0)}, /* PocketPC USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a07, 0)}, /* PocketPC USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a08, 0)}, /* PocketPC USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a09, 0)}, /* PocketPC USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a0a, 0)}, /* PocketPC USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a0b, 0)}, /* PocketPC USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a0c, 0)}, /* PocketPC USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a0d, 0)}, /* PocketPC USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a0e, 0)}, /* PocketPC USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a0f, 0)}, /* PocketPC USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a10, 0)}, /* PocketPC USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a11, 0)}, /* PocketPC USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a12, 0)}, /* PocketPC USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a13, 0)}, /* PocketPC USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a14, 0)}, /* PocketPC USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a15, 0)}, /* PocketPC USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a16, 0)}, /* PocketPC USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a17, 0)}, /* PocketPC USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a18, 0)}, /* PocketPC USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a19, 0)}, /* PocketPC USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a1a, 0)}, /* PocketPC USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a1b, 0)}, /* PocketPC USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a1c, 0)}, /* PocketPC USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a1d, 0)}, /* PocketPC USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a1e, 0)}, /* PocketPC USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a1f, 0)}, /* PocketPC USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a20, 0)}, /* PocketPC USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a21, 0)}, /* PocketPC USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a22, 0)}, /* PocketPC USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a23, 0)}, /* PocketPC USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a24, 0)}, /* PocketPC USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a25, 0)}, /* PocketPC USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a26, 0)}, /* PocketPC USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a27, 0)}, /* PocketPC USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a28, 0)}, /* PocketPC USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a29, 0)}, /* PocketPC USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a2a, 0)}, /* PocketPC USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a2b, 0)}, /* PocketPC USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a2c, 0)}, /* PocketPC USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a2d, 0)}, /* PocketPC USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a2e, 0)}, /* PocketPC USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a2f, 0)}, /* PocketPC USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a30, 0)}, /* PocketPC USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a31, 0)}, /* PocketPC USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a32, 0)}, /* PocketPC USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a33, 0)}, /* PocketPC USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a34, 0)}, /* PocketPC USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a35, 0)}, /* PocketPC USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a36, 0)}, /* PocketPC USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a37, 0)}, /* PocketPC USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a38, 0)}, /* PocketPC USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a39, 0)}, /* PocketPC USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a3a, 0)}, /* PocketPC USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a3b, 0)}, /* PocketPC USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a3c, 0)}, /* PocketPC USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a3d, 0)}, /* PocketPC USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a3e, 0)}, /* PocketPC USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a3f, 0)}, /* PocketPC USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a40, 0)}, /* PocketPC USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a41, 0)}, /* PocketPC USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a42, 0)}, /* PocketPC USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a43, 0)}, /* PocketPC USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a44, 0)}, /* PocketPC USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a45, 0)}, /* PocketPC USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a46, 0)}, /* PocketPC USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a47, 0)}, /* PocketPC USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a48, 0)}, /* PocketPC USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a49, 0)}, /* PocketPC USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a4a, 0)}, /* PocketPC USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a4b, 0)}, /* PocketPC USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a4c, 0)}, /* PocketPC USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a4d, 0)}, /* PocketPC USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a4e, 0)}, /* PocketPC USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a4f, 0)}, /* HTC SmartPhone USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a50, 0)}, /* SmartPhone USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a52, 0)}, /* SmartPhone USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a53, 0)}, /* SmartPhone USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a54, 0)}, /* SmartPhone USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a55, 0)}, /* SmartPhone USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a56, 0)}, /* SmartPhone USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a57, 0)}, /* SmartPhone USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a58, 0)}, /* SmartPhone USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a59, 0)}, /* SmartPhone USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a5a, 0)}, /* SmartPhone USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a5b, 0)}, /* SmartPhone USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a5c, 0)}, /* SmartPhone USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a5d, 0)}, /* SmartPhone USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a5e, 0)}, /* SmartPhone USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a5f, 0)}, /* SmartPhone USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a60, 0)}, /* SmartPhone USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a61, 0)}, /* SmartPhone USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a62, 0)}, /* SmartPhone USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a63, 0)}, /* SmartPhone USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a64, 0)}, /* SmartPhone USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a65, 0)}, /* SmartPhone USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a66, 0)}, /* SmartPhone USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a67, 0)}, /* SmartPhone USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a68, 0)}, /* SmartPhone USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a69, 0)}, /* SmartPhone USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a6a, 0)}, /* SmartPhone USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a6b, 0)}, /* SmartPhone USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a6c, 0)}, /* SmartPhone USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a6d, 0)}, /* SmartPhone USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a6e, 0)}, /* SmartPhone USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a6f, 0)}, /* SmartPhone USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a70, 0)}, /* SmartPhone USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a71, 0)}, /* SmartPhone USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a72, 0)}, /* SmartPhone USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a73, 0)}, /* SmartPhone USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a74, 0)}, /* SmartPhone USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a75, 0)}, /* SmartPhone USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a76, 0)}, /* SmartPhone USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a77, 0)}, /* SmartPhone USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a78, 0)}, /* SmartPhone USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a79, 0)}, /* SmartPhone USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a7a, 0)}, /* SmartPhone USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a7b, 0)}, /* SmartPhone USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a7c, 0)}, /* SmartPhone USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a7d, 0)}, /* SmartPhone USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a7e, 0)}, /* SmartPhone USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a7f, 0)}, /* SmartPhone USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a80, 0)}, /* SmartPhone USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a81, 0)}, /* SmartPhone USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a82, 0)}, /* SmartPhone USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a83, 0)}, /* SmartPhone USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a84, 0)}, /* SmartPhone USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a85, 0)}, /* SmartPhone USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a86, 0)}, /* SmartPhone USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a87, 0)}, /* SmartPhone USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a88, 0)}, /* SmartPhone USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a89, 0)}, /* SmartPhone USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a8a, 0)}, /* SmartPhone USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a8b, 0)}, /* SmartPhone USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a8c, 0)}, /* SmartPhone USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a8d, 0)}, /* SmartPhone USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a8e, 0)}, /* SmartPhone USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a8f, 0)}, /* SmartPhone USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a90, 0)}, /* SmartPhone USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a91, 0)}, /* SmartPhone USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a92, 0)}, /* SmartPhone USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a93, 0)}, /* SmartPhone USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a94, 0)}, /* SmartPhone USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a95, 0)}, /* SmartPhone USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a96, 0)}, /* SmartPhone USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a97, 0)}, /* SmartPhone USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a98, 0)}, /* SmartPhone USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a99, 0)}, /* SmartPhone USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a9a, 0)}, /* SmartPhone USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a9b, 0)}, /* SmartPhone USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a9c, 0)}, /* SmartPhone USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a9d, 0)}, /* SmartPhone USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a9e, 0)}, /* SmartPhone USB Sync */ {USB_VPI(USB_VENDOR_HTC, 0x0a9f, 0)}, /**/ {USB_VPI(USB_VENDOR_HTC, USB_PRODUCT_HTC_PPC6700MODEM, 0)}, /**/ {USB_VPI(USB_VENDOR_HTC, USB_PRODUCT_HTC_SMARTPHONE, 0)}, /**/ {USB_VPI(USB_VENDOR_HTC, USB_PRODUCT_HTC_WINMOBILE, 0)}, /* High Tech Computer Wizard Smartphone */ {USB_VPI(USB_VENDOR_HTC, USB_PRODUCT_HTC_WIZARD, 0)}, /* JVC USB Sync */ {USB_VPI(USB_VENDOR_JVC, 0x3011, 0)}, /* JVC USB Sync */ {USB_VPI(USB_VENDOR_JVC, 0x3012, 0)}, /* LGE USB Sync */ {USB_VPI(USB_VENDOR_LG, 0x9c01, 0)}, /* Microsoft USB Sync */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x00ce, 0)}, /* Windows Pocket PC 2002 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0400, 0)}, /* Windows Pocket PC 2002 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0401, 0)}, /* Windows Pocket PC 2002 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0402, 0)}, /* Windows Pocket PC 2002 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0403, 0)}, /* Windows Pocket PC 2002 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0404, 0)}, /* Windows Pocket PC 2002 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0405, 0)}, /* Windows Pocket PC 2002 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0406, 0)}, /* Windows Pocket PC 2002 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0407, 0)}, /* Windows Pocket PC 2002 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0408, 0)}, /* Windows Pocket PC 2002 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0409, 0)}, /* Windows Pocket PC 2002 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x040a, 0)}, /* Windows Pocket PC 2002 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x040b, 0)}, /* Windows Pocket PC 2002 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x040c, 0)}, /* Windows Pocket PC 2002 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x040d, 0)}, /* Windows Pocket PC 2002 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x040e, 0)}, /* Windows Pocket PC 2002 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x040f, 0)}, /* Windows Pocket PC 2002 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0410, 0)}, /* Windows Pocket PC 2002 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0411, 0)}, /* Windows Pocket PC 2002 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0412, 0)}, /* Windows Pocket PC 2002 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0413, 0)}, /* Windows Pocket PC 2002 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0414, 0)}, /* Windows Pocket PC 2002 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0415, 0)}, /* Windows Pocket PC 2002 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0416, 0)}, /* Windows Pocket PC 2002 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0417, 0)}, /* Windows Pocket PC 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0432, 0)}, /* Windows Pocket PC 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0433, 0)}, /* Windows Pocket PC 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0434, 0)}, /* Windows Pocket PC 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0435, 0)}, /* Windows Pocket PC 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0436, 0)}, /* Windows Pocket PC 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0437, 0)}, /* Windows Pocket PC 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0438, 0)}, /* Windows Pocket PC 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0439, 0)}, /* Windows Pocket PC 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x043a, 0)}, /* Windows Pocket PC 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x043b, 0)}, /* Windows Pocket PC 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x043c, 0)}, /* Windows Pocket PC 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x043d, 0)}, /* Windows Pocket PC 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x043e, 0)}, /* Windows Pocket PC 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x043f, 0)}, /* Windows Pocket PC 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0440, 0)}, /* Windows Pocket PC 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0441, 0)}, /* Windows Pocket PC 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0442, 0)}, /* Windows Pocket PC 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0443, 0)}, /* Windows Pocket PC 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0444, 0)}, /* Windows Pocket PC 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0445, 0)}, /* Windows Pocket PC 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0446, 0)}, /* Windows Pocket PC 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0447, 0)}, /* Windows Pocket PC 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0448, 0)}, /* Windows Pocket PC 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0449, 0)}, /* Windows Pocket PC 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x044a, 0)}, /* Windows Pocket PC 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x044b, 0)}, /* Windows Pocket PC 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x044c, 0)}, /* Windows Pocket PC 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x044d, 0)}, /* Windows Pocket PC 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x044e, 0)}, /* Windows Pocket PC 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x044f, 0)}, /* Windows Pocket PC 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0450, 0)}, /* Windows Pocket PC 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0451, 0)}, /* Windows Pocket PC 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0452, 0)}, /* Windows Pocket PC 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0453, 0)}, /* Windows Pocket PC 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0454, 0)}, /* Windows Pocket PC 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0455, 0)}, /* Windows Pocket PC 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0456, 0)}, /* Windows Pocket PC 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0457, 0)}, /* Windows Pocket PC 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0458, 0)}, /* Windows Pocket PC 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0459, 0)}, /* Windows Pocket PC 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x045a, 0)}, /* Windows Pocket PC 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x045b, 0)}, /* Windows Pocket PC 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x045c, 0)}, /* Windows Pocket PC 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x045d, 0)}, /* Windows Pocket PC 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x045e, 0)}, /* Windows Pocket PC 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x045f, 0)}, /* Windows Pocket PC 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0460, 0)}, /* Windows Pocket PC 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0461, 0)}, /* Windows Pocket PC 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0462, 0)}, /* Windows Pocket PC 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0463, 0)}, /* Windows Pocket PC 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0464, 0)}, /* Windows Pocket PC 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0465, 0)}, /* Windows Pocket PC 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0466, 0)}, /* Windows Pocket PC 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0467, 0)}, /* Windows Pocket PC 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0468, 0)}, /* Windows Pocket PC 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0469, 0)}, /* Windows Pocket PC 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x046a, 0)}, /* Windows Pocket PC 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x046b, 0)}, /* Windows Pocket PC 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x046c, 0)}, /* Windows Pocket PC 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x046d, 0)}, /* Windows Pocket PC 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x046e, 0)}, /* Windows Pocket PC 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x046f, 0)}, /* Windows Pocket PC 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0470, 0)}, /* Windows Pocket PC 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0471, 0)}, /* Windows Pocket PC 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0472, 0)}, /* Windows Pocket PC 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0473, 0)}, /* Windows Pocket PC 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0474, 0)}, /* Windows Pocket PC 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0475, 0)}, /* Windows Pocket PC 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0476, 0)}, /* Windows Pocket PC 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0477, 0)}, /* Windows Pocket PC 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0478, 0)}, /* Windows Pocket PC 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x0479, 0)}, /* Windows Pocket PC 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x047a, 0)}, /* Windows Pocket PC 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x047b, 0)}, /* Windows Smartphone 2002 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x04c8, 0)}, /* Windows Smartphone 2002 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x04c9, 0)}, /* Windows Smartphone 2002 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x04ca, 0)}, /* Windows Smartphone 2002 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x04cb, 0)}, /* Windows Smartphone 2002 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x04cc, 0)}, /* Windows Smartphone 2002 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x04cd, 0)}, /* Windows Smartphone 2002 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x04ce, 0)}, /* Windows Smartphone 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x04d7, 0)}, /* Windows Smartphone 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x04d8, 0)}, /* Windows Smartphone 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x04d9, 0)}, /* Windows Smartphone 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x04da, 0)}, /* Windows Smartphone 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x04db, 0)}, /* Windows Smartphone 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x04dc, 0)}, /* Windows Smartphone 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x04dd, 0)}, /* Windows Smartphone 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x04de, 0)}, /* Windows Smartphone 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x04df, 0)}, /* Windows Smartphone 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x04e0, 0)}, /* Windows Smartphone 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x04e1, 0)}, /* Windows Smartphone 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x04e2, 0)}, /* Windows Smartphone 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x04e3, 0)}, /* Windows Smartphone 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x04e4, 0)}, /* Windows Smartphone 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x04e5, 0)}, /* Windows Smartphone 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x04e6, 0)}, /* Windows Smartphone 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x04e7, 0)}, /* Windows Smartphone 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x04e8, 0)}, /* Windows Smartphone 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x04e9, 0)}, /* Windows Smartphone 2003 */ {USB_VPI(USB_VENDOR_MICROSOFT, 0x04ea, 0)}, /* Motorola MPx200 Smartphone */ {USB_VPI(USB_VENDOR_MOTOROLA2, 0x4204, 0)}, /* Motorola MPc GSM */ {USB_VPI(USB_VENDOR_MOTOROLA2, 0x4214, 0)}, /* Motorola MPx220 Smartphone */ {USB_VPI(USB_VENDOR_MOTOROLA2, 0x4224, 0)}, /* Motorola MPc CDMA */ {USB_VPI(USB_VENDOR_MOTOROLA2, 0x4234, 0)}, /* Motorola MPx100 Smartphone */ {USB_VPI(USB_VENDOR_MOTOROLA2, 0x4244, 0)}, /* NEC USB Sync */ {USB_VPI(USB_VENDOR_NEC, 0x00d5, 0)}, /* NEC USB Sync */ {USB_VPI(USB_VENDOR_NEC, 0x00d6, 0)}, /* NEC USB Sync */ {USB_VPI(USB_VENDOR_NEC, 0x00d7, 0)}, /* NEC USB Sync */ {USB_VPI(USB_VENDOR_NEC, 0x8024, 0)}, /* NEC USB Sync */ {USB_VPI(USB_VENDOR_NEC, 0x8025, 0)}, /* Panasonic USB Sync */ {USB_VPI(USB_VENDOR_PANASONIC, 0x2500, 0)}, /* Samsung NEXiO USB Sync */ {USB_VPI(USB_VENDOR_SAMSUNG, 0x5f00, 0)}, /* Samsung NEXiO USB Sync */ {USB_VPI(USB_VENDOR_SAMSUNG, 0x5f01, 0)}, /* Samsung NEXiO USB Sync */ {USB_VPI(USB_VENDOR_SAMSUNG, 0x5f02, 0)}, /* Samsung NEXiO USB Sync */ {USB_VPI(USB_VENDOR_SAMSUNG, 0x5f03, 0)}, /* Samsung NEXiO USB Sync */ {USB_VPI(USB_VENDOR_SAMSUNG, 0x5f04, 0)}, /* Samsung MITs USB Sync */ {USB_VPI(USB_VENDOR_SAMSUNG, 0x6611, 0)}, /* Samsung MITs USB Sync */ {USB_VPI(USB_VENDOR_SAMSUNG, 0x6613, 0)}, /* Samsung MITs USB Sync */ {USB_VPI(USB_VENDOR_SAMSUNG, 0x6615, 0)}, /* Samsung MITs USB Sync */ {USB_VPI(USB_VENDOR_SAMSUNG, 0x6617, 0)}, /* Samsung MITs USB Sync */ {USB_VPI(USB_VENDOR_SAMSUNG, 0x6619, 0)}, /* Samsung MITs USB Sync */ {USB_VPI(USB_VENDOR_SAMSUNG, 0x661b, 0)}, /* Samsung MITs USB Sync */ {USB_VPI(USB_VENDOR_SAMSUNG, 0x662e, 0)}, /* Samsung MITs USB Sync */ {USB_VPI(USB_VENDOR_SAMSUNG, 0x6630, 0)}, /* Samsung MITs USB Sync */ {USB_VPI(USB_VENDOR_SAMSUNG, 0x6632, 0)}, /* SHARP WS003SH USB Modem */ {USB_VPI(USB_VENDOR_SHARP, 0x9102, 0)}, /* SHARP WS004SH USB Modem */ {USB_VPI(USB_VENDOR_SHARP, 0x9121, 0)}, /* SHARP S01SH USB Modem */ {USB_VPI(USB_VENDOR_SHARP, 0x9151, 0)}, /**/ {USB_VPI(USB_VENDOR_SHARP, USB_PRODUCT_SHARP_WZERO3ES, 0)}, /**/ {USB_VPI(USB_VENDOR_SHARP, USB_PRODUCT_SHARP_WZERO3ADES, 0)}, /**/ {USB_VPI(USB_VENDOR_SHARP, USB_PRODUCT_SHARP_WILLCOM03, 0)}, /* Symbol USB Sync */ {USB_VPI(USB_VENDOR_SYMBOL, 0x2000, 0)}, /* Symbol USB Sync 0x2001 */ {USB_VPI(USB_VENDOR_SYMBOL, 0x2001, 0)}, /* Symbol USB Sync 0x2002 */ {USB_VPI(USB_VENDOR_SYMBOL, 0x2002, 0)}, /* Symbol USB Sync 0x2003 */ {USB_VPI(USB_VENDOR_SYMBOL, 0x2003, 0)}, /* Symbol USB Sync 0x2004 */ {USB_VPI(USB_VENDOR_SYMBOL, 0x2004, 0)}, /* Symbol USB Sync 0x2005 */ {USB_VPI(USB_VENDOR_SYMBOL, 0x2005, 0)}, /* Symbol USB Sync 0x2006 */ {USB_VPI(USB_VENDOR_SYMBOL, 0x2006, 0)}, /* Symbol USB Sync 0x2007 */ {USB_VPI(USB_VENDOR_SYMBOL, 0x2007, 0)}, /* Symbol USB Sync 0x2008 */ {USB_VPI(USB_VENDOR_SYMBOL, 0x2008, 0)}, /* Symbol USB Sync 0x2009 */ {USB_VPI(USB_VENDOR_SYMBOL, 0x2009, 0)}, /* Symbol USB Sync 0x200a */ {USB_VPI(USB_VENDOR_SYMBOL, 0x200a, 0)}, /* TOSHIBA USB Sync 0700 */ {USB_VPI(USB_VENDOR_TOSHIBA, 0x0700, 0)}, /* TOSHIBA Pocket PC e310 */ {USB_VPI(USB_VENDOR_TOSHIBA, 0x0705, 0)}, /* TOSHIBA Pocket PC e330 Series */ {USB_VPI(USB_VENDOR_TOSHIBA, 0x0707, 0)}, /* TOSHIBA Pocket PC e350Series */ {USB_VPI(USB_VENDOR_TOSHIBA, 0x0708, 0)}, /* TOSHIBA Pocket PC e750 Series */ {USB_VPI(USB_VENDOR_TOSHIBA, 0x0709, 0)}, /* TOSHIBA Pocket PC e400 Series */ {USB_VPI(USB_VENDOR_TOSHIBA, 0x070a, 0)}, /* TOSHIBA Pocket PC e800 Series */ {USB_VPI(USB_VENDOR_TOSHIBA, 0x070b, 0)}, /* TOSHIBA Pocket PC e740 */ {USB_VPI(USB_VENDOR_TOSHIBA, USB_PRODUCT_TOSHIBA_POCKETPC_E740, 0)}, /* ViewSonic Color Pocket PC V35 */ {USB_VPI(USB_VENDOR_VIEWSONIC, 0x0ed9, 0)}, /* ViewSonic Color Pocket PC V36 */ {USB_VPI(USB_VENDOR_VIEWSONIC, 0x1527, 0)}, /* ViewSonic Color Pocket PC V37 */ {USB_VPI(USB_VENDOR_VIEWSONIC, 0x1529, 0)}, /* ViewSonic Color Pocket PC V38 */ {USB_VPI(USB_VENDOR_VIEWSONIC, 0x152b, 0)}, /* ViewSonic Pocket PC */ {USB_VPI(USB_VENDOR_VIEWSONIC, 0x152e, 0)}, /* ViewSonic Communicator Pocket PC */ {USB_VPI(USB_VENDOR_VIEWSONIC, 0x1921, 0)}, /* ViewSonic Smartphone */ {USB_VPI(USB_VENDOR_VIEWSONIC, 0x1922, 0)}, /* ViewSonic Pocket PC V30 */ {USB_VPI(USB_VENDOR_VIEWSONIC, 0x1923, 0)}, }; static device_method_t uipaq_methods[] = { DEVMETHOD(device_probe, uipaq_probe), DEVMETHOD(device_attach, uipaq_attach), DEVMETHOD(device_detach, uipaq_detach), DEVMETHOD_END }; static devclass_t uipaq_devclass; static driver_t uipaq_driver = { .name = "uipaq", .methods = uipaq_methods, .size = sizeof(struct uipaq_softc), }; DRIVER_MODULE(uipaq, uhub, uipaq_driver, uipaq_devclass, NULL, 0); MODULE_DEPEND(uipaq, ucom, 1, 1, 1); MODULE_DEPEND(uipaq, usb, 1, 1, 1); MODULE_VERSION(uipaq, 1); USB_PNP_HOST_INFO(uipaq_devs); static int uipaq_probe(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); if (uaa->usb_mode != USB_MODE_HOST) { return (ENXIO); } if (uaa->info.bConfigIndex != UIPAQ_CONFIG_INDEX) { return (ENXIO); } if (uaa->info.bIfaceIndex != UIPAQ_IFACE_INDEX) { return (ENXIO); } if (uaa->info.bInterfaceClass == UICLASS_IAD) { DPRINTF("IAD detected - not UIPAQ serial device\n"); return (ENXIO); } return (usbd_lookup_id_by_uaa(uipaq_devs, sizeof(uipaq_devs), uaa)); } static int uipaq_attach(device_t dev) { struct usb_device_request req; struct usb_attach_arg *uaa = device_get_ivars(dev); struct uipaq_softc *sc = device_get_softc(dev); int error; uint8_t iface_index; uint8_t i; sc->sc_udev = uaa->device; device_set_usb_desc(dev); mtx_init(&sc->sc_mtx, "uipaq", NULL, MTX_DEF); ucom_ref(&sc->sc_super_ucom); /* * Send magic bytes, cribbed from Linux ipaq driver that * claims to have sniffed them from Win98. Wait for driver to * become ready on device side? */ req.bmRequestType = UT_WRITE_CLASS_INTERFACE; req.bRequest = UCDC_SET_CONTROL_LINE_STATE; USETW(req.wValue, UCDC_LINE_DTR); USETW(req.wIndex, 0x0); USETW(req.wLength, 0); for (i = 0; i != 64; i++) { error = usbd_do_request_flags(uaa->device, NULL, &req, NULL, 0, NULL, 100); if (error == 0) break; usb_pause_mtx(NULL, hz / 10); } iface_index = UIPAQ_IFACE_INDEX; error = usbd_transfer_setup(uaa->device, &iface_index, sc->sc_xfer, uipaq_config_data, UIPAQ_N_TRANSFER, sc, &sc->sc_mtx); if (error) { goto detach; } /* clear stall at first run */ mtx_lock(&sc->sc_mtx); - usbd_xfer_set_stall(sc->sc_xfer[UIPAQ_BULK_DT_WR]); - usbd_xfer_set_stall(sc->sc_xfer[UIPAQ_BULK_DT_RD]); + usbd_xfer_set_zlp(sc->sc_xfer[UIPAQ_BULK_DT_WR]); mtx_unlock(&sc->sc_mtx); error = ucom_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc, &uipaq_callback, &sc->sc_mtx); if (error) { goto detach; } ucom_set_pnpinfo_usb(&sc->sc_super_ucom, dev); return (0); detach: uipaq_detach(dev); return (ENXIO); } int uipaq_detach(device_t dev) { struct uipaq_softc *sc = device_get_softc(dev); ucom_detach(&sc->sc_super_ucom, &sc->sc_ucom); usbd_transfer_unsetup(sc->sc_xfer, UIPAQ_N_TRANSFER); device_claim_softc(dev); uipaq_free_softc(sc); return (0); } UCOM_UNLOAD_DRAIN(uipaq); static void uipaq_free_softc(struct uipaq_softc *sc) { if (ucom_unref(&sc->sc_super_ucom)) { mtx_destroy(&sc->sc_mtx); device_free_softc(sc); } } static void uipaq_free(struct ucom_softc *ucom) { uipaq_free_softc(ucom->sc_parent); } static void uipaq_start_read(struct ucom_softc *ucom) { struct uipaq_softc *sc = ucom->sc_parent; /* start read endpoint */ usbd_transfer_start(sc->sc_xfer[UIPAQ_BULK_DT_RD]); } static void uipaq_stop_read(struct ucom_softc *ucom) { struct uipaq_softc *sc = ucom->sc_parent; /* stop read endpoint */ usbd_transfer_stop(sc->sc_xfer[UIPAQ_BULK_DT_RD]); } static void uipaq_start_write(struct ucom_softc *ucom) { struct uipaq_softc *sc = ucom->sc_parent; usbd_transfer_start(sc->sc_xfer[UIPAQ_BULK_DT_WR]); } static void uipaq_stop_write(struct ucom_softc *ucom) { struct uipaq_softc *sc = ucom->sc_parent; usbd_transfer_stop(sc->sc_xfer[UIPAQ_BULK_DT_WR]); } static void uipaq_cfg_set_dtr(struct ucom_softc *ucom, uint8_t onoff) { struct uipaq_softc *sc = ucom->sc_parent; struct usb_device_request req; DPRINTF("onoff=%d\n", onoff); if (onoff) sc->sc_line |= UCDC_LINE_DTR; else sc->sc_line &= ~UCDC_LINE_DTR; req.bmRequestType = UT_WRITE_CLASS_INTERFACE; req.bRequest = UCDC_SET_CONTROL_LINE_STATE; USETW(req.wValue, sc->sc_line); req.wIndex[0] = UIPAQ_IFACE_INDEX; req.wIndex[1] = 0; USETW(req.wLength, 0); ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, &req, NULL, 0, 1000); } static void uipaq_cfg_set_rts(struct ucom_softc *ucom, uint8_t onoff) { struct uipaq_softc *sc = ucom->sc_parent; struct usb_device_request req; DPRINTF("onoff=%d\n", onoff); if (onoff) sc->sc_line |= UCDC_LINE_RTS; else sc->sc_line &= ~UCDC_LINE_RTS; req.bmRequestType = UT_WRITE_CLASS_INTERFACE; req.bRequest = UCDC_SET_CONTROL_LINE_STATE; USETW(req.wValue, sc->sc_line); req.wIndex[0] = UIPAQ_IFACE_INDEX; req.wIndex[1] = 0; USETW(req.wLength, 0); ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, &req, NULL, 0, 1000); } static void uipaq_cfg_set_break(struct ucom_softc *ucom, uint8_t onoff) { struct uipaq_softc *sc = ucom->sc_parent; struct usb_device_request req; uint16_t temp; temp = onoff ? UCDC_BREAK_ON : UCDC_BREAK_OFF; req.bmRequestType = UT_WRITE_CLASS_INTERFACE; req.bRequest = UCDC_SEND_BREAK; USETW(req.wValue, temp); req.wIndex[0] = UIPAQ_IFACE_INDEX; req.wIndex[1] = 0; USETW(req.wLength, 0); ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, &req, NULL, 0, 1000); } static void uipaq_write_callback(struct usb_xfer *xfer, usb_error_t error) { struct uipaq_softc *sc = usbd_xfer_softc(xfer); struct usb_page_cache *pc; uint32_t actlen; switch (USB_GET_STATE(xfer)) { case USB_ST_SETUP: case USB_ST_TRANSFERRED: tr_setup: + if (usbd_xfer_get_and_clr_zlp(xfer)) + break; + pc = usbd_xfer_get_frame(xfer, 0); if (ucom_get_data(&sc->sc_ucom, pc, 0, UIPAQ_BUF_SIZE, &actlen)) { usbd_xfer_set_frame_len(xfer, 0, actlen); usbd_transfer_submit(xfer); } - return; + break; default: /* Error */ if (error != USB_ERR_CANCELLED) { /* try to clear stall first */ usbd_xfer_set_stall(xfer); goto tr_setup; } - return; + break; } } static void uipaq_read_callback(struct usb_xfer *xfer, usb_error_t error) { struct uipaq_softc *sc = usbd_xfer_softc(xfer); struct usb_page_cache *pc; int actlen; usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: pc = usbd_xfer_get_frame(xfer, 0); ucom_put_data(&sc->sc_ucom, pc, 0, actlen); case USB_ST_SETUP: tr_setup: usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); usbd_transfer_submit(xfer); return; default: /* Error */ if (error != USB_ERR_CANCELLED) { /* try to clear stall first */ usbd_xfer_set_stall(xfer); goto tr_setup; } return; } } static void uipaq_poll(struct ucom_softc *ucom) { struct uipaq_softc *sc = ucom->sc_parent; usbd_transfer_poll(sc->sc_xfer, UIPAQ_N_TRANSFER); } diff --git a/sys/dev/usb/serial/ulpt.c b/sys/dev/usb/serial/ulpt.c index c566da924374..56f99519d4cd 100644 --- a/sys/dev/usb/serial/ulpt.c +++ b/sys/dev/usb/serial/ulpt.c @@ -1,761 +1,760 @@ #include __FBSDID("$FreeBSD$"); /* $NetBSD: ulpt.c,v 1.60 2003/10/04 21:19:50 augustss Exp $ */ /*- * SPDX-License-Identifier: BSD-2-Clause-NetBSD * * Copyright (c) 1998, 2003 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (lennart@augustsson.net) at * Carlstedt Research & Technology. * * 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. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 THE FOUNDATION OR CONTRIBUTORS * 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. */ /* * Printer Class spec: http://www.usb.org/developers/data/devclass/usbprint109.PDF * Printer Class spec: http://www.usb.org/developers/devclass_docs/usbprint11.pdf */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "usbdevs.h" #define USB_DEBUG_VAR ulpt_debug #include #include #ifdef USB_DEBUG static int ulpt_debug = 0; static SYSCTL_NODE(_hw_usb, OID_AUTO, ulpt, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, "USB ulpt"); SYSCTL_INT(_hw_usb_ulpt, OID_AUTO, debug, CTLFLAG_RWTUN, &ulpt_debug, 0, "Debug level"); #endif #define ULPT_BSIZE (1<<15) /* bytes */ #define ULPT_IFQ_MAXLEN 2 /* units */ #define UR_GET_DEVICE_ID 0x00 #define UR_GET_PORT_STATUS 0x01 #define UR_SOFT_RESET 0x02 #define LPS_NERR 0x08 /* printer no error */ #define LPS_SELECT 0x10 /* printer selected */ #define LPS_NOPAPER 0x20 /* printer out of paper */ #define LPS_INVERT (LPS_SELECT|LPS_NERR) #define LPS_MASK (LPS_SELECT|LPS_NERR|LPS_NOPAPER) enum { ULPT_BULK_DT_WR, ULPT_BULK_DT_RD, ULPT_INTR_DT_RD, ULPT_N_TRANSFER, }; struct ulpt_softc { struct usb_fifo_sc sc_fifo; struct usb_fifo_sc sc_fifo_noreset; struct mtx sc_mtx; struct usb_callout sc_watchdog; device_t sc_dev; struct usb_device *sc_udev; struct usb_fifo *sc_fifo_open[2]; struct usb_xfer *sc_xfer[ULPT_N_TRANSFER]; int sc_fflags; /* current open flags, FREAD and * FWRITE */ uint8_t sc_iface_no; uint8_t sc_last_status; uint8_t sc_zlps; /* number of consequtive zero length * packets received */ }; /* prototypes */ static device_probe_t ulpt_probe; static device_attach_t ulpt_attach; static device_detach_t ulpt_detach; static usb_callback_t ulpt_write_callback; static usb_callback_t ulpt_read_callback; static usb_callback_t ulpt_status_callback; static void ulpt_reset(struct ulpt_softc *); static void ulpt_watchdog(void *); static usb_fifo_close_t ulpt_close; static usb_fifo_cmd_t ulpt_start_read; static usb_fifo_cmd_t ulpt_start_write; static usb_fifo_cmd_t ulpt_stop_read; static usb_fifo_cmd_t ulpt_stop_write; static usb_fifo_ioctl_t ulpt_ioctl; static usb_fifo_open_t ulpt_open; static usb_fifo_open_t unlpt_open; static struct usb_fifo_methods ulpt_fifo_methods = { .f_close = &ulpt_close, .f_ioctl = &ulpt_ioctl, .f_open = &ulpt_open, .f_start_read = &ulpt_start_read, .f_start_write = &ulpt_start_write, .f_stop_read = &ulpt_stop_read, .f_stop_write = &ulpt_stop_write, .basename[0] = "ulpt", }; static struct usb_fifo_methods unlpt_fifo_methods = { .f_close = &ulpt_close, .f_ioctl = &ulpt_ioctl, .f_open = &unlpt_open, .f_start_read = &ulpt_start_read, .f_start_write = &ulpt_start_write, .f_stop_read = &ulpt_stop_read, .f_stop_write = &ulpt_stop_write, .basename[0] = "unlpt", }; static void ulpt_reset(struct ulpt_softc *sc) { struct usb_device_request req; DPRINTFN(2, "\n"); req.bRequest = UR_SOFT_RESET; USETW(req.wValue, 0); USETW(req.wIndex, sc->sc_iface_no); USETW(req.wLength, 0); /* * There was a mistake in the USB printer 1.0 spec that gave the * request type as UT_WRITE_CLASS_OTHER; it should have been * UT_WRITE_CLASS_INTERFACE. Many printers use the old one, * so we try both. */ mtx_lock(&sc->sc_mtx); req.bmRequestType = UT_WRITE_CLASS_OTHER; if (usbd_do_request_flags(sc->sc_udev, &sc->sc_mtx, &req, NULL, 0, NULL, 2 * USB_MS_HZ)) { /* 1.0 */ req.bmRequestType = UT_WRITE_CLASS_INTERFACE; if (usbd_do_request_flags(sc->sc_udev, &sc->sc_mtx, &req, NULL, 0, NULL, 2 * USB_MS_HZ)) { /* 1.1 */ /* ignore error */ } } mtx_unlock(&sc->sc_mtx); } static void ulpt_write_callback(struct usb_xfer *xfer, usb_error_t error) { struct ulpt_softc *sc = usbd_xfer_softc(xfer); struct usb_fifo *f = sc->sc_fifo_open[USB_FIFO_TX]; struct usb_page_cache *pc; int actlen, max; usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); if (f == NULL) { /* should not happen */ DPRINTF("no FIFO\n"); return; } DPRINTF("state=0x%x actlen=%d\n", USB_GET_STATE(xfer), actlen); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: case USB_ST_SETUP: tr_setup: + if (usbd_xfer_get_and_clr_zlp(xfer)) + break; + pc = usbd_xfer_get_frame(xfer, 0); max = usbd_xfer_max_len(xfer); if (usb_fifo_get_data(f, pc, 0, max, &actlen, 0)) { usbd_xfer_set_frame_len(xfer, 0, actlen); usbd_transfer_submit(xfer); } break; default: /* Error */ if (error != USB_ERR_CANCELLED) { /* try to clear stall first */ usbd_xfer_set_stall(xfer); goto tr_setup; } break; } } static void ulpt_read_callback(struct usb_xfer *xfer, usb_error_t error) { struct ulpt_softc *sc = usbd_xfer_softc(xfer); struct usb_fifo *f = sc->sc_fifo_open[USB_FIFO_RX]; struct usb_page_cache *pc; int actlen; usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); if (f == NULL) { /* should not happen */ DPRINTF("no FIFO\n"); return; } DPRINTF("state=0x%x\n", USB_GET_STATE(xfer)); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: if (actlen == 0) { if (sc->sc_zlps == 4) { /* enable BULK throttle */ usbd_xfer_set_interval(xfer, 500); /* ms */ } else { sc->sc_zlps++; } } else { /* disable BULK throttle */ usbd_xfer_set_interval(xfer, 0); sc->sc_zlps = 0; } pc = usbd_xfer_get_frame(xfer, 0); usb_fifo_put_data(f, pc, 0, actlen, 1); case USB_ST_SETUP: tr_setup: if (usb_fifo_put_bytes_max(f) != 0) { usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); usbd_transfer_submit(xfer); } break; default: /* Error */ /* disable BULK throttle */ usbd_xfer_set_interval(xfer, 0); sc->sc_zlps = 0; if (error != USB_ERR_CANCELLED) { /* try to clear stall first */ usbd_xfer_set_stall(xfer); goto tr_setup; } break; } } static void ulpt_status_callback(struct usb_xfer *xfer, usb_error_t error) { struct ulpt_softc *sc = usbd_xfer_softc(xfer); struct usb_device_request req; struct usb_page_cache *pc; uint8_t cur_status; uint8_t new_status; switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: pc = usbd_xfer_get_frame(xfer, 1); usbd_copy_out(pc, 0, &cur_status, 1); cur_status = (cur_status ^ LPS_INVERT) & LPS_MASK; new_status = cur_status & ~sc->sc_last_status; sc->sc_last_status = cur_status; if (new_status & LPS_SELECT) log(LOG_NOTICE, "%s: offline\n", device_get_nameunit(sc->sc_dev)); else if (new_status & LPS_NOPAPER) log(LOG_NOTICE, "%s: out of paper\n", device_get_nameunit(sc->sc_dev)); else if (new_status & LPS_NERR) log(LOG_NOTICE, "%s: output error\n", device_get_nameunit(sc->sc_dev)); break; case USB_ST_SETUP: req.bmRequestType = UT_READ_CLASS_INTERFACE; req.bRequest = UR_GET_PORT_STATUS; USETW(req.wValue, 0); req.wIndex[0] = sc->sc_iface_no; req.wIndex[1] = 0; USETW(req.wLength, 1); 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_frame_len(xfer, 1, 1); usbd_xfer_set_frames(xfer, 2); usbd_transfer_submit(xfer); break; default: /* Error */ DPRINTF("error=%s\n", usbd_errstr(error)); if (error != USB_ERR_CANCELLED) { /* wait for next watchdog timeout */ } break; } } static const struct usb_config ulpt_config[ULPT_N_TRANSFER] = { [ULPT_BULK_DT_WR] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, .bufsize = ULPT_BSIZE, .flags = {.pipe_bof = 1,.proxy_buffer = 1}, .callback = &ulpt_write_callback, }, [ULPT_BULK_DT_RD] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, .bufsize = ULPT_BSIZE, .flags = {.pipe_bof = 1,.short_xfer_ok = 1,.proxy_buffer = 1}, .callback = &ulpt_read_callback, }, [ULPT_INTR_DT_RD] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = UE_DIR_ANY, .bufsize = sizeof(struct usb_device_request) + 1, .callback = &ulpt_status_callback, .timeout = 1000, /* 1 second */ }, }; static void ulpt_start_read(struct usb_fifo *fifo) { struct ulpt_softc *sc = usb_fifo_softc(fifo); usbd_transfer_start(sc->sc_xfer[ULPT_BULK_DT_RD]); } static void ulpt_stop_read(struct usb_fifo *fifo) { struct ulpt_softc *sc = usb_fifo_softc(fifo); usbd_transfer_stop(sc->sc_xfer[ULPT_BULK_DT_RD]); } static void ulpt_start_write(struct usb_fifo *fifo) { struct ulpt_softc *sc = usb_fifo_softc(fifo); usbd_transfer_start(sc->sc_xfer[ULPT_BULK_DT_WR]); } static void ulpt_stop_write(struct usb_fifo *fifo) { struct ulpt_softc *sc = usb_fifo_softc(fifo); usbd_transfer_stop(sc->sc_xfer[ULPT_BULK_DT_WR]); } static int ulpt_open(struct usb_fifo *fifo, int fflags) { struct ulpt_softc *sc = usb_fifo_softc(fifo); /* we assume that open is a serial process */ if (sc->sc_fflags == 0) { /* reset USB parallel port */ ulpt_reset(sc); } return (unlpt_open(fifo, fflags)); } static int unlpt_open(struct usb_fifo *fifo, int fflags) { struct ulpt_softc *sc = usb_fifo_softc(fifo); if (sc->sc_fflags & fflags) { return (EBUSY); } if (fflags & FREAD) { - /* clear stall first */ - mtx_lock(&sc->sc_mtx); - usbd_xfer_set_stall(sc->sc_xfer[ULPT_BULK_DT_RD]); - mtx_unlock(&sc->sc_mtx); if (usb_fifo_alloc_buffer(fifo, usbd_xfer_max_len(sc->sc_xfer[ULPT_BULK_DT_RD]), ULPT_IFQ_MAXLEN)) { return (ENOMEM); } /* set which FIFO is opened */ sc->sc_fifo_open[USB_FIFO_RX] = fifo; } if (fflags & FWRITE) { /* clear stall first */ mtx_lock(&sc->sc_mtx); - usbd_xfer_set_stall(sc->sc_xfer[ULPT_BULK_DT_WR]); + usbd_xfer_set_zlp(sc->sc_xfer[ULPT_BULK_DT_WR]); mtx_unlock(&sc->sc_mtx); if (usb_fifo_alloc_buffer(fifo, usbd_xfer_max_len(sc->sc_xfer[ULPT_BULK_DT_WR]), ULPT_IFQ_MAXLEN)) { return (ENOMEM); } /* set which FIFO is opened */ sc->sc_fifo_open[USB_FIFO_TX] = fifo; } sc->sc_fflags |= fflags & (FREAD | FWRITE); return (0); } static void ulpt_close(struct usb_fifo *fifo, int fflags) { struct ulpt_softc *sc = usb_fifo_softc(fifo); sc->sc_fflags &= ~(fflags & (FREAD | FWRITE)); if (fflags & (FREAD | FWRITE)) { usb_fifo_free_buffer(fifo); } } static int ulpt_ioctl(struct usb_fifo *fifo, u_long cmd, void *data, int fflags) { return (ENODEV); } static const STRUCT_USB_HOST_ID ulpt_devs[] = { /* Uni-directional USB printer */ {USB_IFACE_CLASS(UICLASS_PRINTER), USB_IFACE_SUBCLASS(UISUBCLASS_PRINTER), USB_IFACE_PROTOCOL(UIPROTO_PRINTER_UNI)}, /* Bi-directional USB printer */ {USB_IFACE_CLASS(UICLASS_PRINTER), USB_IFACE_SUBCLASS(UISUBCLASS_PRINTER), USB_IFACE_PROTOCOL(UIPROTO_PRINTER_BI)}, /* 1284 USB printer */ {USB_IFACE_CLASS(UICLASS_PRINTER), USB_IFACE_SUBCLASS(UISUBCLASS_PRINTER), USB_IFACE_PROTOCOL(UIPROTO_PRINTER_1284)}, }; static int ulpt_probe(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); int error; DPRINTFN(11, "\n"); if (uaa->usb_mode != USB_MODE_HOST) return (ENXIO); error = usbd_lookup_id_by_uaa(ulpt_devs, sizeof(ulpt_devs), uaa); if (error) return (error); return (BUS_PROBE_GENERIC); } static int ulpt_attach(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); struct ulpt_softc *sc = device_get_softc(dev); struct usb_interface_descriptor *id; int unit = device_get_unit(dev); int error; uint8_t iface_index = uaa->info.bIfaceIndex; uint8_t alt_index; DPRINTFN(11, "sc=%p\n", sc); sc->sc_dev = dev; sc->sc_udev = uaa->device; device_set_usb_desc(dev); mtx_init(&sc->sc_mtx, "ulpt lock", NULL, MTX_DEF | MTX_RECURSE); usb_callout_init_mtx(&sc->sc_watchdog, &sc->sc_mtx, 0); /* search through all the descriptors looking for bidir mode */ id = usbd_get_interface_descriptor(uaa->iface); alt_index = 0xFF; while (1) { if (id == NULL) { break; } if ((id->bDescriptorType == UDESC_INTERFACE) && (id->bLength >= sizeof(*id))) { if (id->bInterfaceNumber != uaa->info.bIfaceNum) { break; } else { alt_index++; if ((id->bInterfaceClass == UICLASS_PRINTER) && (id->bInterfaceSubClass == UISUBCLASS_PRINTER) && (id->bInterfaceProtocol == UIPROTO_PRINTER_BI)) { goto found; } } } id = (void *)usb_desc_foreach( usbd_get_config_descriptor(uaa->device), (void *)id); } goto detach; found: DPRINTF("setting alternate " "config number: %d\n", alt_index); if (alt_index) { error = usbd_set_alt_interface_index (uaa->device, iface_index, alt_index); if (error) { DPRINTF("could not set alternate " "config, error=%s\n", usbd_errstr(error)); goto detach; } } sc->sc_iface_no = id->bInterfaceNumber; error = usbd_transfer_setup(uaa->device, &iface_index, sc->sc_xfer, ulpt_config, ULPT_N_TRANSFER, sc, &sc->sc_mtx); if (error) { DPRINTF("error=%s\n", usbd_errstr(error)); goto detach; } device_printf(sc->sc_dev, "using bi-directional mode\n"); #if 0 /* * This code is disabled because for some mysterious reason it causes * printing not to work. But only sometimes, and mostly with * UHCI and less often with OHCI. *sigh* */ { struct usb_config_descriptor *cd = usbd_get_config_descriptor(dev); struct usb_device_request req; int len, alen; req.bmRequestType = UT_READ_CLASS_INTERFACE; req.bRequest = UR_GET_DEVICE_ID; USETW(req.wValue, cd->bConfigurationValue); USETW2(req.wIndex, id->bInterfaceNumber, id->bAlternateSetting); USETW(req.wLength, sizeof devinfo - 1); error = usbd_do_request_flags(dev, &req, devinfo, USB_SHORT_XFER_OK, &alen, USB_DEFAULT_TIMEOUT); if (error) { device_printf(sc->sc_dev, "cannot get device id\n"); } else if (alen <= 2) { device_printf(sc->sc_dev, "empty device id, no " "printer connected?\n"); } else { /* devinfo now contains an IEEE-1284 device ID */ len = ((devinfo[0] & 0xff) << 8) | (devinfo[1] & 0xff); if (len > sizeof devinfo - 3) len = sizeof devinfo - 3; devinfo[len] = 0; printf("%s: device id <", device_get_nameunit(sc->sc_dev)); ieee1284_print_id(devinfo + 2); printf(">\n"); } } #endif error = usb_fifo_attach(uaa->device, sc, &sc->sc_mtx, &ulpt_fifo_methods, &sc->sc_fifo, unit, -1, uaa->info.bIfaceIndex, UID_ROOT, GID_OPERATOR, 0644); if (error) { goto detach; } error = usb_fifo_attach(uaa->device, sc, &sc->sc_mtx, &unlpt_fifo_methods, &sc->sc_fifo_noreset, unit, -1, uaa->info.bIfaceIndex, UID_ROOT, GID_OPERATOR, 0644); if (error) { goto detach; } /* start reading of status */ mtx_lock(&sc->sc_mtx); ulpt_watchdog(sc); mtx_unlock(&sc->sc_mtx); return (0); detach: ulpt_detach(dev); return (ENOMEM); } static int ulpt_detach(device_t dev) { struct ulpt_softc *sc = device_get_softc(dev); DPRINTF("sc=%p\n", sc); usb_fifo_detach(&sc->sc_fifo); usb_fifo_detach(&sc->sc_fifo_noreset); mtx_lock(&sc->sc_mtx); usb_callout_stop(&sc->sc_watchdog); mtx_unlock(&sc->sc_mtx); usbd_transfer_unsetup(sc->sc_xfer, ULPT_N_TRANSFER); usb_callout_drain(&sc->sc_watchdog); mtx_destroy(&sc->sc_mtx); return (0); } #if 0 /* XXX This does not belong here. */ /* * Compare two strings until the second ends. */ static uint8_t ieee1284_compare(const char *a, const char *b) { while (1) { if (*b == 0) { break; } if (*a != *b) { return 1; } b++; a++; } return 0; } /* * Print select parts of an IEEE 1284 device ID. */ void ieee1284_print_id(char *str) { char *p, *q; for (p = str - 1; p; p = strchr(p, ';')) { p++; /* skip ';' */ if (ieee1284_compare(p, "MFG:") == 0 || ieee1284_compare(p, "MANUFACTURER:") == 0 || ieee1284_compare(p, "MDL:") == 0 || ieee1284_compare(p, "MODEL:") == 0) { q = strchr(p, ';'); if (q) printf("%.*s", (int)(q - p + 1), p); } } } #endif static void ulpt_watchdog(void *arg) { struct ulpt_softc *sc = arg; mtx_assert(&sc->sc_mtx, MA_OWNED); /* * Only read status while the device is not opened, due to * possible hardware or firmware bug in some printers. */ if (sc->sc_fflags == 0) usbd_transfer_start(sc->sc_xfer[ULPT_INTR_DT_RD]); usb_callout_reset(&sc->sc_watchdog, hz, &ulpt_watchdog, sc); } static devclass_t ulpt_devclass; static device_method_t ulpt_methods[] = { DEVMETHOD(device_probe, ulpt_probe), DEVMETHOD(device_attach, ulpt_attach), DEVMETHOD(device_detach, ulpt_detach), DEVMETHOD_END }; static driver_t ulpt_driver = { .name = "ulpt", .methods = ulpt_methods, .size = sizeof(struct ulpt_softc), }; DRIVER_MODULE(ulpt, uhub, ulpt_driver, ulpt_devclass, NULL, 0); MODULE_DEPEND(ulpt, usb, 1, 1, 1); MODULE_VERSION(ulpt, 1); USB_PNP_HOST_INFO(ulpt_devs); diff --git a/sys/dev/usb/serial/umcs.c b/sys/dev/usb/serial/umcs.c index e3c8f1a764fb..76ecd1ffa452 100644 --- a/sys/dev/usb/serial/umcs.c +++ b/sys/dev/usb/serial/umcs.c @@ -1,1106 +1,1108 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2010 Lev Serebryakov . * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 THE AUTHOR OR CONTRIBUTORS 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. */ /* * This driver supports several multiport USB-to-RS232 serial adapters driven * by MosChip mos7820 and mos7840, bridge chips. * The adapters are sold under many different brand names. * * Datasheets are available at MosChip www site at * http://www.moschip.com. The datasheets don't contain full * programming information for the chip. * * It is nornal to have only two enabled ports in devices, based on * quad-port mos7840. * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "usbdevs.h" #define USB_DEBUG_VAR umcs_debug #include #include #include #include #define UMCS7840_MODVER 1 #ifdef USB_DEBUG static int umcs_debug = 0; static SYSCTL_NODE(_hw_usb, OID_AUTO, umcs, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, "USB umcs quadport serial adapter"); SYSCTL_INT(_hw_usb_umcs, OID_AUTO, debug, CTLFLAG_RWTUN, &umcs_debug, 0, "Debug level"); #endif /* USB_DEBUG */ /* * Two-port devices (both with 7820 chip and 7840 chip configured as two-port) * have ports 0 and 2, with ports 1 and 3 omitted. * So,PHYSICAL port numbers (indexes) on two-port device will be 0 and 2. * This driver trys to use physical numbers as much as possible. */ /* * Indexed by PHYSICAL port number. * Pack non-regular registers to array to easier if-less access. */ struct umcs7840_port_registers { uint8_t reg_sp; /* SP register. */ uint8_t reg_control; /* CONTROL register. */ uint8_t reg_dcr; /* DCR0 register. DCR1 & DCR2 can be * calculated */ }; static const struct umcs7840_port_registers umcs7840_port_registers[UMCS7840_MAX_PORTS] = { {.reg_sp = MCS7840_DEV_REG_SP1,.reg_control = MCS7840_DEV_REG_CONTROL1,.reg_dcr = MCS7840_DEV_REG_DCR0_1}, {.reg_sp = MCS7840_DEV_REG_SP2,.reg_control = MCS7840_DEV_REG_CONTROL2,.reg_dcr = MCS7840_DEV_REG_DCR0_2}, {.reg_sp = MCS7840_DEV_REG_SP3,.reg_control = MCS7840_DEV_REG_CONTROL3,.reg_dcr = MCS7840_DEV_REG_DCR0_3}, {.reg_sp = MCS7840_DEV_REG_SP4,.reg_control = MCS7840_DEV_REG_CONTROL4,.reg_dcr = MCS7840_DEV_REG_DCR0_4}, }; enum { UMCS7840_BULK_RD_EP, UMCS7840_BULK_WR_EP, UMCS7840_N_TRANSFERS }; struct umcs7840_softc_oneport { struct usb_xfer *sc_xfer[UMCS7840_N_TRANSFERS]; /* Control structures * for two transfers */ uint8_t sc_lcr; /* local line control register */ uint8_t sc_mcr; /* local modem control register */ }; struct umcs7840_softc { struct ucom_super_softc sc_super_ucom; struct ucom_softc sc_ucom[UMCS7840_MAX_PORTS]; /* Need to be continuous * array, so indexed by * LOGICAL port * (subunit) number */ struct usb_xfer *sc_intr_xfer; /* Interrupt endpoint */ device_t sc_dev; /* Device for error prints */ struct usb_device *sc_udev; /* USB Device for all operations */ struct mtx sc_mtx; /* ucom requires this */ uint8_t sc_driver_done; /* Flag when enumeration is finished */ uint8_t sc_numports; /* Number of ports (subunits) */ struct umcs7840_softc_oneport sc_ports[UMCS7840_MAX_PORTS]; /* Indexed by PHYSICAL * port number. */ }; /* prototypes */ static usb_error_t umcs7840_get_reg_sync(struct umcs7840_softc *, uint8_t, uint8_t *); static usb_error_t umcs7840_set_reg_sync(struct umcs7840_softc *, uint8_t, uint8_t); static usb_error_t umcs7840_get_UART_reg_sync(struct umcs7840_softc *, uint8_t, uint8_t, uint8_t *); static usb_error_t umcs7840_set_UART_reg_sync(struct umcs7840_softc *, uint8_t, uint8_t, uint8_t); static usb_error_t umcs7840_set_baudrate(struct umcs7840_softc *, uint8_t, uint32_t); static usb_error_t umcs7840_calc_baudrate(uint32_t rate, uint16_t *, uint8_t *); static void umcs7840_free(struct ucom_softc *); static void umcs7840_cfg_get_status(struct ucom_softc *, uint8_t *, uint8_t *); static void umcs7840_cfg_set_dtr(struct ucom_softc *, uint8_t); static void umcs7840_cfg_set_rts(struct ucom_softc *, uint8_t); static void umcs7840_cfg_set_break(struct ucom_softc *, uint8_t); static void umcs7840_cfg_param(struct ucom_softc *, struct termios *); static void umcs7840_cfg_open(struct ucom_softc *); static void umcs7840_cfg_close(struct ucom_softc *); static int umcs7840_pre_param(struct ucom_softc *, struct termios *); static void umcs7840_start_read(struct ucom_softc *); static void umcs7840_stop_read(struct ucom_softc *); static void umcs7840_start_write(struct ucom_softc *); static void umcs7840_stop_write(struct ucom_softc *); static void umcs7840_poll(struct ucom_softc *ucom); static device_probe_t umcs7840_probe; static device_attach_t umcs7840_attach; static device_detach_t umcs7840_detach; static void umcs7840_free_softc(struct umcs7840_softc *); static usb_callback_t umcs7840_intr_callback; static usb_callback_t umcs7840_read_callback1; static usb_callback_t umcs7840_read_callback2; static usb_callback_t umcs7840_read_callback3; static usb_callback_t umcs7840_read_callback4; static usb_callback_t umcs7840_write_callback1; static usb_callback_t umcs7840_write_callback2; static usb_callback_t umcs7840_write_callback3; static usb_callback_t umcs7840_write_callback4; static void umcs7840_read_callbackN(struct usb_xfer *, usb_error_t, uint8_t); static void umcs7840_write_callbackN(struct usb_xfer *, usb_error_t, uint8_t); /* Indexed by LOGICAL port number (subunit), so two-port device uses 0 & 1 */ static usb_callback_t *umcs7840_rw_callbacks[UMCS7840_MAX_PORTS][UMCS7840_N_TRANSFERS] = { {&umcs7840_read_callback1, &umcs7840_write_callback1}, {&umcs7840_read_callback2, &umcs7840_write_callback2}, {&umcs7840_read_callback3, &umcs7840_write_callback3}, {&umcs7840_read_callback4, &umcs7840_write_callback4}, }; static const struct usb_config umcs7840_bulk_config_data[UMCS7840_N_TRANSFERS] = { [UMCS7840_BULK_RD_EP] = { .type = UE_BULK, .endpoint = 0x01, .direction = UE_DIR_IN, .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, .bufsize = 0, /* use wMaxPacketSize */ .callback = &umcs7840_read_callback1, .if_index = 0, }, [UMCS7840_BULK_WR_EP] = { .type = UE_BULK, .endpoint = 0x02, .direction = UE_DIR_OUT, .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, .bufsize = 0, /* use wMaxPacketSize */ .callback = &umcs7840_write_callback1, .if_index = 0, }, }; static const struct usb_config umcs7840_intr_config_data[1] = { [0] = { .type = UE_INTERRUPT, .endpoint = 0x09, .direction = UE_DIR_IN, .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, .bufsize = 0, /* use wMaxPacketSize */ .callback = &umcs7840_intr_callback, .if_index = 0, }, }; static struct ucom_callback umcs7840_callback = { .ucom_cfg_get_status = &umcs7840_cfg_get_status, .ucom_cfg_set_dtr = &umcs7840_cfg_set_dtr, .ucom_cfg_set_rts = &umcs7840_cfg_set_rts, .ucom_cfg_set_break = &umcs7840_cfg_set_break, .ucom_cfg_param = &umcs7840_cfg_param, .ucom_cfg_open = &umcs7840_cfg_open, .ucom_cfg_close = &umcs7840_cfg_close, .ucom_pre_param = &umcs7840_pre_param, .ucom_start_read = &umcs7840_start_read, .ucom_stop_read = &umcs7840_stop_read, .ucom_start_write = &umcs7840_start_write, .ucom_stop_write = &umcs7840_stop_write, .ucom_poll = &umcs7840_poll, .ucom_free = &umcs7840_free, }; static const STRUCT_USB_HOST_ID umcs7840_devs[] = { {USB_VPI(USB_VENDOR_MOSCHIP, USB_PRODUCT_MOSCHIP_MCS7820, 0)}, {USB_VPI(USB_VENDOR_MOSCHIP, USB_PRODUCT_MOSCHIP_MCS7840, 0)}, }; static device_method_t umcs7840_methods[] = { DEVMETHOD(device_probe, umcs7840_probe), DEVMETHOD(device_attach, umcs7840_attach), DEVMETHOD(device_detach, umcs7840_detach), DEVMETHOD_END }; static devclass_t umcs7840_devclass; static driver_t umcs7840_driver = { .name = "umcs7840", .methods = umcs7840_methods, .size = sizeof(struct umcs7840_softc), }; DRIVER_MODULE(umcs7840, uhub, umcs7840_driver, umcs7840_devclass, 0, 0); MODULE_DEPEND(umcs7840, ucom, 1, 1, 1); MODULE_DEPEND(umcs7840, usb, 1, 1, 1); MODULE_VERSION(umcs7840, UMCS7840_MODVER); USB_PNP_HOST_INFO(umcs7840_devs); static int umcs7840_probe(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); if (uaa->usb_mode != USB_MODE_HOST) return (ENXIO); if (uaa->info.bConfigIndex != MCS7840_CONFIG_INDEX) return (ENXIO); if (uaa->info.bIfaceIndex != MCS7840_IFACE_INDEX) return (ENXIO); return (usbd_lookup_id_by_uaa(umcs7840_devs, sizeof(umcs7840_devs), uaa)); } static int umcs7840_attach(device_t dev) { struct usb_config umcs7840_config_tmp[UMCS7840_N_TRANSFERS]; struct usb_attach_arg *uaa = device_get_ivars(dev); struct umcs7840_softc *sc = device_get_softc(dev); uint8_t iface_index = MCS7840_IFACE_INDEX; int error; int subunit; int n; uint8_t data; for (n = 0; n < UMCS7840_N_TRANSFERS; ++n) umcs7840_config_tmp[n] = umcs7840_bulk_config_data[n]; device_set_usb_desc(dev); mtx_init(&sc->sc_mtx, "umcs7840", NULL, MTX_DEF); ucom_ref(&sc->sc_super_ucom); sc->sc_dev = dev; sc->sc_udev = uaa->device; /* * Get number of ports * Documentation (full datasheet) says, that number of ports is * set as MCS7840_DEV_MODE_SELECT24S bit in MODE R/Only * register. But vendor driver uses these undocumented * register & bit. * * Experiments show, that MODE register can have `0' * (4 ports) bit on 2-port device, so use vendor driver's way. * * Also, see notes in header file for these constants. */ umcs7840_get_reg_sync(sc, MCS7840_DEV_REG_GPIO, &data); if (data & MCS7840_DEV_GPIO_4PORTS) { sc->sc_numports = 4; /* Store physical port numbers in sc_portno */ sc->sc_ucom[0].sc_portno = 0; sc->sc_ucom[1].sc_portno = 1; sc->sc_ucom[2].sc_portno = 2; sc->sc_ucom[3].sc_portno = 3; } else { sc->sc_numports = 2; /* Store physical port numbers in sc_portno */ sc->sc_ucom[0].sc_portno = 0; sc->sc_ucom[1].sc_portno = 2; /* '1' is skipped */ } device_printf(dev, "Chip mcs%04x, found %d active ports\n", uaa->info.idProduct, sc->sc_numports); if (!umcs7840_get_reg_sync(sc, MCS7840_DEV_REG_MODE, &data)) { device_printf(dev, "On-die confguration: RST: active %s, HRD: %s, PLL: %s, POR: %s, Ports: %s, EEPROM write %s, IrDA is %savailable\n", (data & MCS7840_DEV_MODE_RESET) ? "low" : "high", (data & MCS7840_DEV_MODE_SER_PRSNT) ? "yes" : "no", (data & MCS7840_DEV_MODE_PLLBYPASS) ? "bypassed" : "avail", (data & MCS7840_DEV_MODE_PORBYPASS) ? "bypassed" : "avail", (data & MCS7840_DEV_MODE_SELECT24S) ? "2" : "4", (data & MCS7840_DEV_MODE_EEPROMWR) ? "enabled" : "disabled", (data & MCS7840_DEV_MODE_IRDA) ? "" : "not "); } /* Setup all transfers */ for (subunit = 0; subunit < sc->sc_numports; ++subunit) { for (n = 0; n < UMCS7840_N_TRANSFERS; ++n) { /* Set endpoint address */ umcs7840_config_tmp[n].endpoint = umcs7840_bulk_config_data[n].endpoint + 2 * sc->sc_ucom[subunit].sc_portno; umcs7840_config_tmp[n].callback = umcs7840_rw_callbacks[subunit][n]; } error = usbd_transfer_setup(uaa->device, &iface_index, sc->sc_ports[sc->sc_ucom[subunit].sc_portno].sc_xfer, umcs7840_config_tmp, UMCS7840_N_TRANSFERS, sc, &sc->sc_mtx); if (error) { device_printf(dev, "allocating USB transfers failed for subunit %d of %d\n", subunit + 1, sc->sc_numports); goto detach; } } error = usbd_transfer_setup(uaa->device, &iface_index, &sc->sc_intr_xfer, umcs7840_intr_config_data, 1, sc, &sc->sc_mtx); if (error) { device_printf(dev, "allocating USB transfers failed for interrupt\n"); goto detach; } /* clear stall at first run */ mtx_lock(&sc->sc_mtx); for (subunit = 0; subunit < sc->sc_numports; ++subunit) { - usbd_xfer_set_stall(sc->sc_ports[sc->sc_ucom[subunit].sc_portno].sc_xfer[UMCS7840_BULK_RD_EP]); - usbd_xfer_set_stall(sc->sc_ports[sc->sc_ucom[subunit].sc_portno].sc_xfer[UMCS7840_BULK_WR_EP]); + usbd_xfer_set_zlp(sc->sc_ports[sc->sc_ucom[subunit].sc_portno].sc_xfer[UMCS7840_BULK_WR_EP]); } mtx_unlock(&sc->sc_mtx); error = ucom_attach(&sc->sc_super_ucom, sc->sc_ucom, sc->sc_numports, sc, &umcs7840_callback, &sc->sc_mtx); if (error) goto detach; ucom_set_pnpinfo_usb(&sc->sc_super_ucom, dev); return (0); detach: umcs7840_detach(dev); return (ENXIO); } static int umcs7840_detach(device_t dev) { struct umcs7840_softc *sc = device_get_softc(dev); int subunit; ucom_detach(&sc->sc_super_ucom, sc->sc_ucom); for (subunit = 0; subunit < sc->sc_numports; ++subunit) usbd_transfer_unsetup(sc->sc_ports[sc->sc_ucom[subunit].sc_portno].sc_xfer, UMCS7840_N_TRANSFERS); usbd_transfer_unsetup(&sc->sc_intr_xfer, 1); device_claim_softc(dev); umcs7840_free_softc(sc); return (0); } UCOM_UNLOAD_DRAIN(umcs7840); static void umcs7840_free_softc(struct umcs7840_softc *sc) { if (ucom_unref(&sc->sc_super_ucom)) { mtx_destroy(&sc->sc_mtx); device_free_softc(sc); } } static void umcs7840_free(struct ucom_softc *ucom) { umcs7840_free_softc(ucom->sc_parent); } static void umcs7840_cfg_open(struct ucom_softc *ucom) { struct umcs7840_softc *sc = ucom->sc_parent; uint16_t pn = ucom->sc_portno; uint8_t data; /* If it very first open, finish global configuration */ if (!sc->sc_driver_done) { /* * USB enumeration is finished, pass internal memory to FIFOs * If it is done in the end of "attach", kernel panics. */ if (umcs7840_get_reg_sync(sc, MCS7840_DEV_REG_CONTROL1, &data)) return; data |= MCS7840_DEV_CONTROL1_DRIVER_DONE; if (umcs7840_set_reg_sync(sc, MCS7840_DEV_REG_CONTROL1, data)) return; sc->sc_driver_done = 1; } /* Toggle reset bit on-off */ if (umcs7840_get_reg_sync(sc, umcs7840_port_registers[pn].reg_sp, &data)) return; data |= MCS7840_DEV_SPx_UART_RESET; if (umcs7840_set_reg_sync(sc, umcs7840_port_registers[pn].reg_sp, data)) return; data &= ~MCS7840_DEV_SPx_UART_RESET; if (umcs7840_set_reg_sync(sc, umcs7840_port_registers[pn].reg_sp, data)) return; /* Set RS-232 mode */ if (umcs7840_set_UART_reg_sync(sc, pn, MCS7840_UART_REG_SCRATCHPAD, MCS7840_UART_SCRATCHPAD_RS232)) return; /* Disable RX on time of initialization */ if (umcs7840_get_reg_sync(sc, umcs7840_port_registers[pn].reg_control, &data)) return; data |= MCS7840_DEV_CONTROLx_RX_DISABLE; if (umcs7840_set_reg_sync(sc, umcs7840_port_registers[pn].reg_control, data)) return; /* Disable all interrupts */ if (umcs7840_set_UART_reg_sync(sc, pn, MCS7840_UART_REG_IER, 0)) return; /* Reset FIFO -- documented */ if (umcs7840_set_UART_reg_sync(sc, pn, MCS7840_UART_REG_FCR, 0)) return; if (umcs7840_set_UART_reg_sync(sc, pn, MCS7840_UART_REG_FCR, MCS7840_UART_FCR_ENABLE | MCS7840_UART_FCR_FLUSHRHR | MCS7840_UART_FCR_FLUSHTHR | MCS7840_UART_FCR_RTL_1_14)) return; /* Set 8 bit, no parity, 1 stop bit -- documented */ sc->sc_ports[pn].sc_lcr = MCS7840_UART_LCR_DATALEN8 | MCS7840_UART_LCR_STOPB1; if (umcs7840_set_UART_reg_sync(sc, pn, MCS7840_UART_REG_LCR, sc->sc_ports[pn].sc_lcr)) return; /* * Enable DTR/RTS on modem control, enable modem interrupts -- * documented */ sc->sc_ports[pn].sc_mcr = MCS7840_UART_MCR_IE; if (ucom->sc_tty == NULL || (ucom->sc_tty->t_termios.c_cflag & CNO_RTSDTR) == 0) sc->sc_ports[pn].sc_mcr |= MCS7840_UART_MCR_DTR | MCS7840_UART_MCR_RTS; if (umcs7840_set_UART_reg_sync(sc, pn, MCS7840_UART_REG_MCR, sc->sc_ports[pn].sc_mcr)) return; /* Clearing Bulkin and Bulkout FIFO */ if (umcs7840_get_reg_sync(sc, umcs7840_port_registers[pn].reg_sp, &data)) return; data |= MCS7840_DEV_SPx_RESET_OUT_FIFO | MCS7840_DEV_SPx_RESET_IN_FIFO; if (umcs7840_set_reg_sync(sc, umcs7840_port_registers[pn].reg_sp, data)) return; data &= ~(MCS7840_DEV_SPx_RESET_OUT_FIFO | MCS7840_DEV_SPx_RESET_IN_FIFO); if (umcs7840_set_reg_sync(sc, umcs7840_port_registers[pn].reg_sp, data)) return; /* Set speed 9600 */ if (umcs7840_set_baudrate(sc, pn, 9600)) return; /* Finally enable all interrupts -- documented */ /* * Copied from vendor driver, I don't know why we should read LCR * here */ if (umcs7840_get_UART_reg_sync(sc, pn, MCS7840_UART_REG_LCR, &sc->sc_ports[pn].sc_lcr)) return; if (umcs7840_set_UART_reg_sync(sc, pn, MCS7840_UART_REG_IER, MCS7840_UART_IER_RXSTAT | MCS7840_UART_IER_MODEM)) return; /* Enable RX */ if (umcs7840_get_reg_sync(sc, umcs7840_port_registers[pn].reg_control, &data)) return; data &= ~MCS7840_DEV_CONTROLx_RX_DISABLE; if (umcs7840_set_reg_sync(sc, umcs7840_port_registers[pn].reg_control, data)) return; DPRINTF("Port %d has been opened\n", pn); } static void umcs7840_cfg_close(struct ucom_softc *ucom) { struct umcs7840_softc *sc = ucom->sc_parent; uint16_t pn = ucom->sc_portno; uint8_t data; umcs7840_stop_read(ucom); umcs7840_stop_write(ucom); umcs7840_set_UART_reg_sync(sc, pn, MCS7840_UART_REG_MCR, 0); umcs7840_set_UART_reg_sync(sc, pn, MCS7840_UART_REG_IER, 0); /* Disable RX */ if (umcs7840_get_reg_sync(sc, umcs7840_port_registers[pn].reg_control, &data)) return; data |= MCS7840_DEV_CONTROLx_RX_DISABLE; if (umcs7840_set_reg_sync(sc, umcs7840_port_registers[pn].reg_control, data)) return; DPRINTF("Port %d has been closed\n", pn); } static void umcs7840_cfg_set_dtr(struct ucom_softc *ucom, uint8_t onoff) { struct umcs7840_softc *sc = ucom->sc_parent; uint8_t pn = ucom->sc_portno; if (onoff) sc->sc_ports[pn].sc_mcr |= MCS7840_UART_MCR_DTR; else sc->sc_ports[pn].sc_mcr &= ~MCS7840_UART_MCR_DTR; umcs7840_set_UART_reg_sync(sc, pn, MCS7840_UART_REG_MCR, sc->sc_ports[pn].sc_mcr); DPRINTF("Port %d DTR set to: %s\n", pn, onoff ? "on" : "off"); } static void umcs7840_cfg_set_rts(struct ucom_softc *ucom, uint8_t onoff) { struct umcs7840_softc *sc = ucom->sc_parent; uint8_t pn = ucom->sc_portno; if (onoff) sc->sc_ports[pn].sc_mcr |= MCS7840_UART_MCR_RTS; else sc->sc_ports[pn].sc_mcr &= ~MCS7840_UART_MCR_RTS; umcs7840_set_UART_reg_sync(sc, pn, MCS7840_UART_REG_MCR, sc->sc_ports[pn].sc_mcr); DPRINTF("Port %d RTS set to: %s\n", pn, onoff ? "on" : "off"); } static void umcs7840_cfg_set_break(struct ucom_softc *ucom, uint8_t onoff) { struct umcs7840_softc *sc = ucom->sc_parent; uint8_t pn = ucom->sc_portno; if (onoff) sc->sc_ports[pn].sc_lcr |= MCS7840_UART_LCR_BREAK; else sc->sc_ports[pn].sc_lcr &= ~MCS7840_UART_LCR_BREAK; umcs7840_set_UART_reg_sync(sc, pn, MCS7840_UART_REG_LCR, sc->sc_ports[pn].sc_lcr); DPRINTF("Port %d BREAK set to: %s\n", pn, onoff ? "on" : "off"); } static void umcs7840_cfg_param(struct ucom_softc *ucom, struct termios *t) { struct umcs7840_softc *sc = ucom->sc_parent; uint8_t pn = ucom->sc_portno; uint8_t lcr = sc->sc_ports[pn].sc_lcr; uint8_t mcr = sc->sc_ports[pn].sc_mcr; DPRINTF("Port %d config:\n", pn); if (t->c_cflag & CSTOPB) { DPRINTF(" 2 stop bits\n"); lcr |= MCS7840_UART_LCR_STOPB2; } else { lcr |= MCS7840_UART_LCR_STOPB1; DPRINTF(" 1 stop bit\n"); } lcr &= ~MCS7840_UART_LCR_PARITYMASK; if (t->c_cflag & PARENB) { lcr |= MCS7840_UART_LCR_PARITYON; if (t->c_cflag & PARODD) { lcr = MCS7840_UART_LCR_PARITYODD; DPRINTF(" parity on - odd\n"); } else { lcr = MCS7840_UART_LCR_PARITYEVEN; DPRINTF(" parity on - even\n"); } } else { lcr &= ~MCS7840_UART_LCR_PARITYON; DPRINTF(" parity off\n"); } lcr &= ~MCS7840_UART_LCR_DATALENMASK; switch (t->c_cflag & CSIZE) { case CS5: lcr |= MCS7840_UART_LCR_DATALEN5; DPRINTF(" 5 bit\n"); break; case CS6: lcr |= MCS7840_UART_LCR_DATALEN6; DPRINTF(" 6 bit\n"); break; case CS7: lcr |= MCS7840_UART_LCR_DATALEN7; DPRINTF(" 7 bit\n"); break; case CS8: lcr |= MCS7840_UART_LCR_DATALEN8; DPRINTF(" 8 bit\n"); break; } if (t->c_cflag & CRTSCTS) { mcr |= MCS7840_UART_MCR_CTSRTS; DPRINTF(" CTS/RTS\n"); } else mcr &= ~MCS7840_UART_MCR_CTSRTS; if (t->c_cflag & (CDTR_IFLOW | CDSR_OFLOW)) { mcr |= MCS7840_UART_MCR_DTRDSR; DPRINTF(" DTR/DSR\n"); } else mcr &= ~MCS7840_UART_MCR_DTRDSR; sc->sc_ports[pn].sc_lcr = lcr; umcs7840_set_UART_reg_sync(sc, pn, MCS7840_UART_REG_LCR, sc->sc_ports[pn].sc_lcr); DPRINTF("Port %d LCR=%02x\n", pn, sc->sc_ports[pn].sc_lcr); sc->sc_ports[pn].sc_mcr = mcr; umcs7840_set_UART_reg_sync(sc, pn, MCS7840_UART_REG_MCR, sc->sc_ports[pn].sc_mcr); DPRINTF("Port %d MCR=%02x\n", pn, sc->sc_ports[pn].sc_mcr); umcs7840_set_baudrate(sc, pn, t->c_ospeed); } static int umcs7840_pre_param(struct ucom_softc *ucom, struct termios *t) { uint8_t clk; uint16_t divisor; if (umcs7840_calc_baudrate(t->c_ospeed, &divisor, &clk) || !divisor) return (EINVAL); return (0); } static void umcs7840_start_read(struct ucom_softc *ucom) { struct umcs7840_softc *sc = ucom->sc_parent; uint8_t pn = ucom->sc_portno; /* Start interrupt transfer */ usbd_transfer_start(sc->sc_intr_xfer); /* Start read transfer */ usbd_transfer_start(sc->sc_ports[pn].sc_xfer[UMCS7840_BULK_RD_EP]); } static void umcs7840_stop_read(struct ucom_softc *ucom) { struct umcs7840_softc *sc = ucom->sc_parent; uint8_t pn = ucom->sc_portno; /* Stop read transfer */ usbd_transfer_stop(sc->sc_ports[pn].sc_xfer[UMCS7840_BULK_RD_EP]); } static void umcs7840_start_write(struct ucom_softc *ucom) { struct umcs7840_softc *sc = ucom->sc_parent; uint8_t pn = ucom->sc_portno; /* Start interrupt transfer */ usbd_transfer_start(sc->sc_intr_xfer); /* Start write transfer */ usbd_transfer_start(sc->sc_ports[pn].sc_xfer[UMCS7840_BULK_WR_EP]); } static void umcs7840_stop_write(struct ucom_softc *ucom) { struct umcs7840_softc *sc = ucom->sc_parent; uint8_t pn = ucom->sc_portno; /* Stop write transfer */ usbd_transfer_stop(sc->sc_ports[pn].sc_xfer[UMCS7840_BULK_WR_EP]); } static void umcs7840_cfg_get_status(struct ucom_softc *ucom, uint8_t *lsr, uint8_t *msr) { struct umcs7840_softc *sc = ucom->sc_parent; uint8_t pn = ucom->sc_portno; uint8_t hw_msr = 0; /* local modem status register */ /* * Read status registers. MSR bits need translation from ns16550 to * SER_* values. LSR bits are ns16550 in hardware and ucom. */ umcs7840_get_UART_reg_sync(sc, pn, MCS7840_UART_REG_LSR, lsr); umcs7840_get_UART_reg_sync(sc, pn, MCS7840_UART_REG_MSR, &hw_msr); if (hw_msr & MCS7840_UART_MSR_NEGCTS) *msr |= SER_CTS; if (hw_msr & MCS7840_UART_MSR_NEGDCD) *msr |= SER_DCD; if (hw_msr & MCS7840_UART_MSR_NEGRI) *msr |= SER_RI; if (hw_msr & MCS7840_UART_MSR_NEGDSR) *msr |= SER_DSR; DPRINTF("Port %d status: LSR=%02x MSR=%02x\n", ucom->sc_portno, *lsr, *msr); } static void umcs7840_intr_callback(struct usb_xfer *xfer, usb_error_t error) { struct umcs7840_softc *sc = usbd_xfer_softc(xfer); struct usb_page_cache *pc; uint8_t buf[13]; int actlen; int subunit; usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: if (actlen == 5 || actlen == 13) { pc = usbd_xfer_get_frame(xfer, 0); usbd_copy_out(pc, 0, buf, actlen); /* Check status of all ports */ for (subunit = 0; subunit < sc->sc_numports; ++subunit) { uint8_t pn = sc->sc_ucom[subunit].sc_portno; if (buf[pn] & MCS7840_UART_ISR_NOPENDING) continue; DPRINTF("Port %d has pending interrupt: %02x (FIFO: %02x)\n", pn, buf[pn] & MCS7840_UART_ISR_INTMASK, buf[pn] & (~MCS7840_UART_ISR_INTMASK)); switch (buf[pn] & MCS7840_UART_ISR_INTMASK) { case MCS7840_UART_ISR_RXERR: case MCS7840_UART_ISR_RXHASDATA: case MCS7840_UART_ISR_RXTIMEOUT: case MCS7840_UART_ISR_MSCHANGE: ucom_status_change(&sc->sc_ucom[subunit]); break; default: /* Do nothing */ break; } } } else device_printf(sc->sc_dev, "Invalid interrupt data length %d", actlen); /* FALLTHROUGH */ case USB_ST_SETUP: tr_setup: usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); usbd_transfer_submit(xfer); return; default: /* Error */ if (error != USB_ERR_CANCELLED) { /* try to clear stall first */ usbd_xfer_set_stall(xfer); goto tr_setup; } return; } } static void umcs7840_read_callback1(struct usb_xfer *xfer, usb_error_t error) { umcs7840_read_callbackN(xfer, error, 0); } static void umcs7840_read_callback2(struct usb_xfer *xfer, usb_error_t error) { umcs7840_read_callbackN(xfer, error, 1); } static void umcs7840_read_callback3(struct usb_xfer *xfer, usb_error_t error) { umcs7840_read_callbackN(xfer, error, 2); } static void umcs7840_read_callback4(struct usb_xfer *xfer, usb_error_t error) { umcs7840_read_callbackN(xfer, error, 3); } static void umcs7840_read_callbackN(struct usb_xfer *xfer, usb_error_t error, uint8_t subunit) { struct umcs7840_softc *sc = usbd_xfer_softc(xfer); struct ucom_softc *ucom = &sc->sc_ucom[subunit]; struct usb_page_cache *pc; int actlen; usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); DPRINTF("Port %d read, state = %d, data length = %d\n", ucom->sc_portno, USB_GET_STATE(xfer), actlen); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: pc = usbd_xfer_get_frame(xfer, 0); ucom_put_data(ucom, pc, 0, actlen); /* FALLTHROUGH */ case USB_ST_SETUP: tr_setup: usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); usbd_transfer_submit(xfer); return; default: /* Error */ if (error != USB_ERR_CANCELLED) { /* try to clear stall first */ usbd_xfer_set_stall(xfer); goto tr_setup; } return; } } static void umcs7840_write_callback1(struct usb_xfer *xfer, usb_error_t error) { umcs7840_write_callbackN(xfer, error, 0); } static void umcs7840_write_callback2(struct usb_xfer *xfer, usb_error_t error) { umcs7840_write_callbackN(xfer, error, 1); } static void umcs7840_write_callback3(struct usb_xfer *xfer, usb_error_t error) { umcs7840_write_callbackN(xfer, error, 2); } static void umcs7840_write_callback4(struct usb_xfer *xfer, usb_error_t error) { umcs7840_write_callbackN(xfer, error, 3); } static void umcs7840_write_callbackN(struct usb_xfer *xfer, usb_error_t error, uint8_t subunit) { struct umcs7840_softc *sc = usbd_xfer_softc(xfer); struct ucom_softc *ucom = &sc->sc_ucom[subunit]; struct usb_page_cache *pc; uint32_t actlen; DPRINTF("Port %d write, state = %d\n", ucom->sc_portno, USB_GET_STATE(xfer)); switch (USB_GET_STATE(xfer)) { case USB_ST_SETUP: case USB_ST_TRANSFERRED: tr_setup: + if (usbd_xfer_get_and_clr_zlp(xfer)) + break; + pc = usbd_xfer_get_frame(xfer, 0); if (ucom_get_data(ucom, pc, 0, usbd_xfer_max_len(xfer), &actlen)) { DPRINTF("Port %d write, has %d bytes\n", ucom->sc_portno, actlen); usbd_xfer_set_frame_len(xfer, 0, actlen); usbd_transfer_submit(xfer); } - return; + break; default: /* Error */ if (error != USB_ERR_CANCELLED) { /* try to clear stall first */ usbd_xfer_set_stall(xfer); goto tr_setup; } - return; + break; } } static void umcs7840_poll(struct ucom_softc *ucom) { struct umcs7840_softc *sc = ucom->sc_parent; DPRINTF("Port %d poll\n", ucom->sc_portno); usbd_transfer_poll(sc->sc_ports[ucom->sc_portno].sc_xfer, UMCS7840_N_TRANSFERS); usbd_transfer_poll(&sc->sc_intr_xfer, 1); } static usb_error_t umcs7840_get_reg_sync(struct umcs7840_softc *sc, uint8_t reg, uint8_t *data) { struct usb_device_request req; usb_error_t err; uint16_t len; req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = MCS7840_RDREQ; USETW(req.wValue, 0); USETW(req.wIndex, reg); USETW(req.wLength, UMCS7840_READ_LENGTH); err = usbd_do_request_proc(sc->sc_udev, &sc->sc_super_ucom.sc_tq, &req, (void *)data, 0, &len, UMCS7840_CTRL_TIMEOUT); if (err == USB_ERR_NORMAL_COMPLETION && len != 1) { device_printf(sc->sc_dev, "Reading register %d failed: invalid length %d\n", reg, len); return (USB_ERR_INVAL); } else if (err) device_printf(sc->sc_dev, "Reading register %d failed: %s\n", reg, usbd_errstr(err)); return (err); } static usb_error_t umcs7840_set_reg_sync(struct umcs7840_softc *sc, uint8_t reg, uint8_t data) { struct usb_device_request req; usb_error_t err; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = MCS7840_WRREQ; USETW(req.wValue, data); USETW(req.wIndex, reg); USETW(req.wLength, 0); err = usbd_do_request_proc(sc->sc_udev, &sc->sc_super_ucom.sc_tq, &req, NULL, 0, NULL, UMCS7840_CTRL_TIMEOUT); if (err) device_printf(sc->sc_dev, "Writing register %d failed: %s\n", reg, usbd_errstr(err)); return (err); } static usb_error_t umcs7840_get_UART_reg_sync(struct umcs7840_softc *sc, uint8_t portno, uint8_t reg, uint8_t *data) { struct usb_device_request req; uint16_t wVal; usb_error_t err; uint16_t len; /* portno is port number */ wVal = ((uint16_t)(portno + 1)) << 8; req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = MCS7840_RDREQ; USETW(req.wValue, wVal); USETW(req.wIndex, reg); USETW(req.wLength, UMCS7840_READ_LENGTH); err = usbd_do_request_proc(sc->sc_udev, &sc->sc_super_ucom.sc_tq, &req, (void *)data, 0, &len, UMCS7840_CTRL_TIMEOUT); if (err == USB_ERR_NORMAL_COMPLETION && len != 1) { device_printf(sc->sc_dev, "Reading UART%d register %d failed: invalid length %d\n", portno, reg, len); return (USB_ERR_INVAL); } else if (err) device_printf(sc->sc_dev, "Reading UART%d register %d failed: %s\n", portno, reg, usbd_errstr(err)); return (err); } static usb_error_t umcs7840_set_UART_reg_sync(struct umcs7840_softc *sc, uint8_t portno, uint8_t reg, uint8_t data) { struct usb_device_request req; usb_error_t err; uint16_t wVal; /* portno is port number */ wVal = ((uint16_t)(portno + 1)) << 8 | data; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = MCS7840_WRREQ; USETW(req.wValue, wVal); USETW(req.wIndex, reg); USETW(req.wLength, 0); err = usbd_do_request_proc(sc->sc_udev, &sc->sc_super_ucom.sc_tq, &req, NULL, 0, NULL, UMCS7840_CTRL_TIMEOUT); if (err) device_printf(sc->sc_dev, "Writing UART%d register %d failed: %s\n", portno, reg, usbd_errstr(err)); return (err); } static usb_error_t umcs7840_set_baudrate(struct umcs7840_softc *sc, uint8_t portno, uint32_t rate) { usb_error_t err; uint16_t divisor; uint8_t clk; uint8_t data; if (umcs7840_calc_baudrate(rate, &divisor, &clk)) { DPRINTF("Port %d bad speed: %d\n", portno, rate); return (-1); } if (divisor == 0 || (clk & MCS7840_DEV_SPx_CLOCK_MASK) != clk) { DPRINTF("Port %d bad speed calculation: %d\n", portno, rate); return (-1); } DPRINTF("Port %d set speed: %d (%02x / %d)\n", portno, rate, clk, divisor); /* Set clock source for standard BAUD frequences */ err = umcs7840_get_reg_sync(sc, umcs7840_port_registers[portno].reg_sp, &data); if (err) return (err); data &= MCS7840_DEV_SPx_CLOCK_MASK; data |= clk; err = umcs7840_set_reg_sync(sc, umcs7840_port_registers[portno].reg_sp, data); if (err) return (err); /* Set divider */ sc->sc_ports[portno].sc_lcr |= MCS7840_UART_LCR_DIVISORS; err = umcs7840_set_UART_reg_sync(sc, portno, MCS7840_UART_REG_LCR, sc->sc_ports[portno].sc_lcr); if (err) return (err); err = umcs7840_set_UART_reg_sync(sc, portno, MCS7840_UART_REG_DLL, (uint8_t)(divisor & 0xff)); if (err) return (err); err = umcs7840_set_UART_reg_sync(sc, portno, MCS7840_UART_REG_DLM, (uint8_t)((divisor >> 8) & 0xff)); if (err) return (err); /* Turn off access to DLL/DLM registers of UART */ sc->sc_ports[portno].sc_lcr &= ~MCS7840_UART_LCR_DIVISORS; err = umcs7840_set_UART_reg_sync(sc, portno, MCS7840_UART_REG_LCR, sc->sc_ports[portno].sc_lcr); if (err) return (err); return (0); } /* Maximum speeds for standard frequences, when PLL is not used */ static const uint32_t umcs7840_baudrate_divisors[] = {0, 115200, 230400, 403200, 460800, 806400, 921600, 1572864, 3145728,}; static const uint8_t umcs7840_baudrate_divisors_len = nitems(umcs7840_baudrate_divisors); static usb_error_t umcs7840_calc_baudrate(uint32_t rate, uint16_t *divisor, uint8_t *clk) { uint8_t i = 0; if (rate > umcs7840_baudrate_divisors[umcs7840_baudrate_divisors_len - 1]) return (-1); for (i = 0; i < umcs7840_baudrate_divisors_len - 1 && !(rate > umcs7840_baudrate_divisors[i] && rate <= umcs7840_baudrate_divisors[i + 1]); ++i); if (rate == 0) *divisor = 1; /* XXX */ else *divisor = umcs7840_baudrate_divisors[i + 1] / rate; /* 0x00 .. 0x70 */ *clk = i << MCS7840_DEV_SPx_CLOCK_SHIFT; return (0); } diff --git a/sys/dev/usb/serial/umodem.c b/sys/dev/usb/serial/umodem.c index 573d74cdb526..b8b7ae2f005c 100644 --- a/sys/dev/usb/serial/umodem.c +++ b/sys/dev/usb/serial/umodem.c @@ -1,1037 +1,1037 @@ /* $NetBSD: umodem.c,v 1.45 2002/09/23 05:51:23 simonb Exp $ */ #include __FBSDID("$FreeBSD$"); /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD AND BSD-2-Clause-NetBSD * * Copyright (c) 2003 M. Warner Losh * * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 THE AUTHOR OR CONTRIBUTORS 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. */ /*- * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Lennart Augustsson (lennart@augustsson.net) at * Carlstedt Research & Technology. * * 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. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 THE FOUNDATION OR CONTRIBUTORS * 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. */ /* * Comm Class spec: http://www.usb.org/developers/devclass_docs/usbccs10.pdf * http://www.usb.org/developers/devclass_docs/usbcdc11.pdf * http://www.usb.org/developers/devclass_docs/cdc_wmc10.zip */ /* * TODO: * - Add error recovery in various places; the big problem is what * to do in a callback if there is an error. * - Implement a Call Device for modems without multiplexed commands. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "usbdevs.h" #include "usb_if.h" #include #define USB_DEBUG_VAR umodem_debug #include #include #include #include #ifdef USB_DEBUG static int umodem_debug = 0; static SYSCTL_NODE(_hw_usb, OID_AUTO, umodem, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, "USB umodem"); SYSCTL_INT(_hw_usb_umodem, OID_AUTO, debug, CTLFLAG_RWTUN, &umodem_debug, 0, "Debug level"); #endif static const STRUCT_USB_DUAL_ID umodem_dual_devs[] = { /* Generic Modem class match */ {USB_IFACE_CLASS(UICLASS_CDC), USB_IFACE_SUBCLASS(UISUBCLASS_ABSTRACT_CONTROL_MODEL), USB_IFACE_PROTOCOL(UIPROTO_CDC_AT)}, {USB_IFACE_CLASS(UICLASS_CDC), USB_IFACE_SUBCLASS(UISUBCLASS_ABSTRACT_CONTROL_MODEL), USB_IFACE_PROTOCOL(UIPROTO_CDC_NONE)}, }; static const STRUCT_USB_HOST_ID umodem_host_devs[] = { /* Huawei Modem class match */ {USB_VENDOR(USB_VENDOR_HUAWEI), USB_IFACE_CLASS(UICLASS_VENDOR), USB_IFACE_SUBCLASS(0x02), USB_IFACE_PROTOCOL(0x01)}, {USB_VENDOR(USB_VENDOR_HUAWEI), USB_IFACE_CLASS(UICLASS_VENDOR), USB_IFACE_SUBCLASS(0x02), USB_IFACE_PROTOCOL(0x02)}, {USB_VENDOR(USB_VENDOR_HUAWEI), USB_IFACE_CLASS(UICLASS_VENDOR), USB_IFACE_SUBCLASS(0x02), USB_IFACE_PROTOCOL(0x10)}, {USB_VENDOR(USB_VENDOR_HUAWEI), USB_IFACE_CLASS(UICLASS_VENDOR), USB_IFACE_SUBCLASS(0x02), USB_IFACE_PROTOCOL(0x12)}, {USB_VENDOR(USB_VENDOR_HUAWEI), USB_IFACE_CLASS(UICLASS_VENDOR), USB_IFACE_SUBCLASS(0x02), USB_IFACE_PROTOCOL(0x61)}, {USB_VENDOR(USB_VENDOR_HUAWEI), USB_IFACE_CLASS(UICLASS_VENDOR), USB_IFACE_SUBCLASS(0x02), USB_IFACE_PROTOCOL(0x62)}, {USB_VENDOR(USB_VENDOR_HUAWEI),USB_IFACE_CLASS(UICLASS_CDC), USB_IFACE_SUBCLASS(UISUBCLASS_ABSTRACT_CONTROL_MODEL), USB_IFACE_PROTOCOL(0xFF)}, /* Kyocera AH-K3001V */ {USB_VPI(USB_VENDOR_KYOCERA, USB_PRODUCT_KYOCERA_AHK3001V, 1)}, {USB_VPI(USB_VENDOR_SIERRA, USB_PRODUCT_SIERRA_MC5720, 1)}, {USB_VPI(USB_VENDOR_CURITEL, USB_PRODUCT_CURITEL_PC5740, 1)}, /* Winbond */ {USB_VENDOR(USB_VENDOR_WINBOND), USB_PRODUCT(USB_PRODUCT_WINBOND_CDC)}, }; /* * As speeds for umodem devices increase, these numbers will need to * be increased. They should be good for G3 speeds and below. * * TODO: The TTY buffers should be increased! */ #define UMODEM_BUF_SIZE 1024 enum { UMODEM_BULK_WR, UMODEM_BULK_RD, UMODEM_INTR_WR, UMODEM_INTR_RD, UMODEM_N_TRANSFER, }; #define UMODEM_MODVER 1 /* module version */ struct umodem_softc { struct ucom_super_softc sc_super_ucom; struct ucom_softc sc_ucom; struct usb_xfer *sc_xfer[UMODEM_N_TRANSFER]; struct usb_device *sc_udev; struct mtx sc_mtx; uint16_t sc_line; uint8_t sc_lsr; /* local status register */ uint8_t sc_msr; /* modem status register */ uint8_t sc_ctrl_iface_no; uint8_t sc_data_iface_no; uint8_t sc_iface_index[2]; uint8_t sc_cm_over_data; uint8_t sc_cm_cap; /* CM capabilities */ uint8_t sc_acm_cap; /* ACM capabilities */ uint8_t sc_line_coding[32]; /* used in USB device mode */ uint8_t sc_abstract_state[32]; /* used in USB device mode */ }; static device_probe_t umodem_probe; static device_attach_t umodem_attach; static device_detach_t umodem_detach; static usb_handle_request_t umodem_handle_request; static void umodem_free_softc(struct umodem_softc *); static usb_callback_t umodem_intr_read_callback; static usb_callback_t umodem_intr_write_callback; static usb_callback_t umodem_write_callback; static usb_callback_t umodem_read_callback; static void umodem_free(struct ucom_softc *); static void umodem_start_read(struct ucom_softc *); static void umodem_stop_read(struct ucom_softc *); static void umodem_start_write(struct ucom_softc *); static void umodem_stop_write(struct ucom_softc *); static void umodem_get_caps(struct usb_attach_arg *, uint8_t *, uint8_t *); static void umodem_cfg_get_status(struct ucom_softc *, uint8_t *, uint8_t *); static int umodem_pre_param(struct ucom_softc *, struct termios *); static void umodem_cfg_param(struct ucom_softc *, struct termios *); static int umodem_ioctl(struct ucom_softc *, uint32_t, caddr_t, int, struct thread *); static void umodem_cfg_set_dtr(struct ucom_softc *, uint8_t); static void umodem_cfg_set_rts(struct ucom_softc *, uint8_t); static void umodem_cfg_set_break(struct ucom_softc *, uint8_t); static void *umodem_get_desc(struct usb_attach_arg *, uint8_t, uint8_t); static usb_error_t umodem_set_comm_feature(struct usb_device *, uint8_t, uint16_t, uint16_t); static void umodem_poll(struct ucom_softc *ucom); static void umodem_find_data_iface(struct usb_attach_arg *uaa, uint8_t, uint8_t *, uint8_t *); static const struct usb_config umodem_config[UMODEM_N_TRANSFER] = { [UMODEM_BULK_WR] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_TX, .if_index = 0, .bufsize = UMODEM_BUF_SIZE, .flags = {.pipe_bof = 1,.force_short_xfer = 1,}, .callback = &umodem_write_callback, .usb_mode = USB_MODE_DUAL, }, [UMODEM_BULK_RD] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_RX, .if_index = 0, .bufsize = UMODEM_BUF_SIZE, .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, .callback = &umodem_read_callback, .usb_mode = USB_MODE_DUAL, }, [UMODEM_INTR_WR] = { .type = UE_INTERRUPT, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_TX, .if_index = 1, .flags = {.pipe_bof = 1,.short_xfer_ok = 1,.no_pipe_ok = 1,}, .bufsize = 0, /* use wMaxPacketSize */ .callback = &umodem_intr_write_callback, .usb_mode = USB_MODE_DEVICE, }, [UMODEM_INTR_RD] = { .type = UE_INTERRUPT, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_RX, .if_index = 1, .flags = {.pipe_bof = 1,.short_xfer_ok = 1,.no_pipe_ok = 1,}, .bufsize = 0, /* use wMaxPacketSize */ .callback = &umodem_intr_read_callback, .usb_mode = USB_MODE_HOST, }, }; static const struct ucom_callback umodem_callback = { .ucom_cfg_get_status = &umodem_cfg_get_status, .ucom_cfg_set_dtr = &umodem_cfg_set_dtr, .ucom_cfg_set_rts = &umodem_cfg_set_rts, .ucom_cfg_set_break = &umodem_cfg_set_break, .ucom_cfg_param = &umodem_cfg_param, .ucom_pre_param = &umodem_pre_param, .ucom_ioctl = &umodem_ioctl, .ucom_start_read = &umodem_start_read, .ucom_stop_read = &umodem_stop_read, .ucom_start_write = &umodem_start_write, .ucom_stop_write = &umodem_stop_write, .ucom_poll = &umodem_poll, .ucom_free = &umodem_free, }; static device_method_t umodem_methods[] = { /* USB interface */ DEVMETHOD(usb_handle_request, umodem_handle_request), /* Device interface */ DEVMETHOD(device_probe, umodem_probe), DEVMETHOD(device_attach, umodem_attach), DEVMETHOD(device_detach, umodem_detach), DEVMETHOD_END }; static devclass_t umodem_devclass; static driver_t umodem_driver = { .name = "umodem", .methods = umodem_methods, .size = sizeof(struct umodem_softc), }; DRIVER_MODULE(umodem, uhub, umodem_driver, umodem_devclass, NULL, 0); MODULE_DEPEND(umodem, ucom, 1, 1, 1); MODULE_DEPEND(umodem, usb, 1, 1, 1); MODULE_VERSION(umodem, UMODEM_MODVER); USB_PNP_DUAL_INFO(umodem_dual_devs); USB_PNP_HOST_INFO(umodem_host_devs); static int umodem_probe(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); int error; DPRINTFN(11, "\n"); error = usbd_lookup_id_by_uaa(umodem_host_devs, sizeof(umodem_host_devs), uaa); if (error) { error = usbd_lookup_id_by_uaa(umodem_dual_devs, sizeof(umodem_dual_devs), uaa); if (error) return (error); } return (BUS_PROBE_GENERIC); } static int umodem_attach(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); struct umodem_softc *sc = device_get_softc(dev); struct usb_cdc_cm_descriptor *cmd; struct usb_cdc_union_descriptor *cud; uint8_t i; int error; device_set_usb_desc(dev); mtx_init(&sc->sc_mtx, "umodem", NULL, MTX_DEF); ucom_ref(&sc->sc_super_ucom); sc->sc_ctrl_iface_no = uaa->info.bIfaceNum; sc->sc_iface_index[1] = uaa->info.bIfaceIndex; sc->sc_udev = uaa->device; umodem_get_caps(uaa, &sc->sc_cm_cap, &sc->sc_acm_cap); /* get the data interface number */ cmd = umodem_get_desc(uaa, UDESC_CS_INTERFACE, UDESCSUB_CDC_CM); if ((cmd == NULL) || (cmd->bLength < sizeof(*cmd))) { cud = usbd_find_descriptor(uaa->device, NULL, uaa->info.bIfaceIndex, UDESC_CS_INTERFACE, 0xFF, UDESCSUB_CDC_UNION, 0xFF); if ((cud == NULL) || (cud->bLength < sizeof(*cud))) { DPRINTF("Missing descriptor. " "Assuming data interface is next.\n"); if (sc->sc_ctrl_iface_no == 0xFF) { goto detach; } else { uint8_t class_match = 0; /* set default interface number */ sc->sc_data_iface_no = 0xFF; /* try to find the data interface backwards */ umodem_find_data_iface(uaa, uaa->info.bIfaceIndex - 1, &sc->sc_data_iface_no, &class_match); /* try to find the data interface forwards */ umodem_find_data_iface(uaa, uaa->info.bIfaceIndex + 1, &sc->sc_data_iface_no, &class_match); /* check if nothing was found */ if (sc->sc_data_iface_no == 0xFF) goto detach; } } else { sc->sc_data_iface_no = cud->bSlaveInterface[0]; } } else { sc->sc_data_iface_no = cmd->bDataInterface; } device_printf(dev, "data interface %d, has %sCM over " "data, has %sbreak\n", sc->sc_data_iface_no, sc->sc_cm_cap & USB_CDC_CM_OVER_DATA ? "" : "no ", sc->sc_acm_cap & USB_CDC_ACM_HAS_BREAK ? "" : "no "); /* get the data interface too */ for (i = 0;; i++) { struct usb_interface *iface; struct usb_interface_descriptor *id; iface = usbd_get_iface(uaa->device, i); if (iface) { id = usbd_get_interface_descriptor(iface); if (id && (id->bInterfaceNumber == sc->sc_data_iface_no)) { sc->sc_iface_index[0] = i; usbd_set_parent_iface(uaa->device, i, uaa->info.bIfaceIndex); break; } } else { device_printf(dev, "no data interface\n"); goto detach; } } if (usb_test_quirk(uaa, UQ_ASSUME_CM_OVER_DATA)) { sc->sc_cm_over_data = 1; } else { if (sc->sc_cm_cap & USB_CDC_CM_OVER_DATA) { if (sc->sc_acm_cap & USB_CDC_ACM_HAS_FEATURE) { error = umodem_set_comm_feature (uaa->device, sc->sc_ctrl_iface_no, UCDC_ABSTRACT_STATE, UCDC_DATA_MULTIPLEXED); /* ignore any errors */ } sc->sc_cm_over_data = 1; } } error = usbd_transfer_setup(uaa->device, sc->sc_iface_index, sc->sc_xfer, umodem_config, UMODEM_N_TRANSFER, sc, &sc->sc_mtx); if (error) { device_printf(dev, "Can't setup transfer\n"); goto detach; } - /* clear stall at first run, if USB host mode */ - if (uaa->usb_mode == USB_MODE_HOST) { - mtx_lock(&sc->sc_mtx); - usbd_xfer_set_stall(sc->sc_xfer[UMODEM_BULK_WR]); - usbd_xfer_set_stall(sc->sc_xfer[UMODEM_BULK_RD]); - mtx_unlock(&sc->sc_mtx); - } + /* send a ZLP at first run */ + mtx_lock(&sc->sc_mtx); + usbd_xfer_set_zlp(sc->sc_xfer[UMODEM_BULK_WR]); + mtx_unlock(&sc->sc_mtx); ucom_set_usb_mode(&sc->sc_super_ucom, uaa->usb_mode); error = ucom_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc, &umodem_callback, &sc->sc_mtx); if (error) { device_printf(dev, "Can't attach com\n"); goto detach; } ucom_set_pnpinfo_usb(&sc->sc_super_ucom, dev); return (0); detach: umodem_detach(dev); return (ENXIO); } static void umodem_find_data_iface(struct usb_attach_arg *uaa, uint8_t iface_index, uint8_t *p_data_no, uint8_t *p_match_class) { struct usb_interface_descriptor *id; struct usb_interface *iface; iface = usbd_get_iface(uaa->device, iface_index); /* check for end of interfaces */ if (iface == NULL) return; id = usbd_get_interface_descriptor(iface); /* check for non-matching interface class */ if (id->bInterfaceClass != UICLASS_CDC_DATA || id->bInterfaceSubClass != UISUBCLASS_DATA) { /* if we got a class match then return */ if (*p_match_class) return; } else { *p_match_class = 1; } DPRINTFN(11, "Match at index %u\n", iface_index); *p_data_no = id->bInterfaceNumber; } static void umodem_start_read(struct ucom_softc *ucom) { struct umodem_softc *sc = ucom->sc_parent; /* start interrupt endpoint, if any */ usbd_transfer_start(sc->sc_xfer[UMODEM_INTR_RD]); /* start read endpoint */ usbd_transfer_start(sc->sc_xfer[UMODEM_BULK_RD]); } static void umodem_stop_read(struct ucom_softc *ucom) { struct umodem_softc *sc = ucom->sc_parent; /* stop interrupt endpoint, if any */ usbd_transfer_stop(sc->sc_xfer[UMODEM_INTR_RD]); /* stop read endpoint */ usbd_transfer_stop(sc->sc_xfer[UMODEM_BULK_RD]); } static void umodem_start_write(struct ucom_softc *ucom) { struct umodem_softc *sc = ucom->sc_parent; usbd_transfer_start(sc->sc_xfer[UMODEM_INTR_WR]); usbd_transfer_start(sc->sc_xfer[UMODEM_BULK_WR]); } static void umodem_stop_write(struct ucom_softc *ucom) { struct umodem_softc *sc = ucom->sc_parent; usbd_transfer_stop(sc->sc_xfer[UMODEM_INTR_WR]); usbd_transfer_stop(sc->sc_xfer[UMODEM_BULK_WR]); } static void umodem_get_caps(struct usb_attach_arg *uaa, uint8_t *cm, uint8_t *acm) { struct usb_cdc_cm_descriptor *cmd; struct usb_cdc_acm_descriptor *cad; cmd = umodem_get_desc(uaa, UDESC_CS_INTERFACE, UDESCSUB_CDC_CM); if ((cmd == NULL) || (cmd->bLength < sizeof(*cmd))) { DPRINTF("no CM desc (faking one)\n"); *cm = USB_CDC_CM_DOES_CM | USB_CDC_CM_OVER_DATA; } else *cm = cmd->bmCapabilities; cad = umodem_get_desc(uaa, UDESC_CS_INTERFACE, UDESCSUB_CDC_ACM); if ((cad == NULL) || (cad->bLength < sizeof(*cad))) { DPRINTF("no ACM desc\n"); *acm = 0; } else *acm = cad->bmCapabilities; } static void umodem_cfg_get_status(struct ucom_softc *ucom, uint8_t *lsr, uint8_t *msr) { struct umodem_softc *sc = ucom->sc_parent; DPRINTF("\n"); /* XXX Note: sc_lsr is always zero */ *lsr = sc->sc_lsr; *msr = sc->sc_msr; } static int umodem_pre_param(struct ucom_softc *ucom, struct termios *t) { return (0); /* we accept anything */ } static void umodem_cfg_param(struct ucom_softc *ucom, struct termios *t) { struct umodem_softc *sc = ucom->sc_parent; struct usb_cdc_line_state ls; struct usb_device_request req; DPRINTF("sc=%p\n", sc); memset(&ls, 0, sizeof(ls)); USETDW(ls.dwDTERate, t->c_ospeed); ls.bCharFormat = (t->c_cflag & CSTOPB) ? UCDC_STOP_BIT_2 : UCDC_STOP_BIT_1; ls.bParityType = (t->c_cflag & PARENB) ? ((t->c_cflag & PARODD) ? UCDC_PARITY_ODD : UCDC_PARITY_EVEN) : UCDC_PARITY_NONE; switch (t->c_cflag & CSIZE) { case CS5: ls.bDataBits = 5; break; case CS6: ls.bDataBits = 6; break; case CS7: ls.bDataBits = 7; break; case CS8: ls.bDataBits = 8; break; } DPRINTF("rate=%d fmt=%d parity=%d bits=%d\n", UGETDW(ls.dwDTERate), ls.bCharFormat, ls.bParityType, ls.bDataBits); req.bmRequestType = UT_WRITE_CLASS_INTERFACE; req.bRequest = UCDC_SET_LINE_CODING; USETW(req.wValue, 0); req.wIndex[0] = sc->sc_ctrl_iface_no; req.wIndex[1] = 0; USETW(req.wLength, sizeof(ls)); ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, &req, &ls, 0, 1000); } static int umodem_ioctl(struct ucom_softc *ucom, uint32_t cmd, caddr_t data, int flag, struct thread *td) { struct umodem_softc *sc = ucom->sc_parent; int error = 0; DPRINTF("cmd=0x%08x\n", cmd); switch (cmd) { case USB_GET_CM_OVER_DATA: *(int *)data = sc->sc_cm_over_data; break; case USB_SET_CM_OVER_DATA: if (*(int *)data != sc->sc_cm_over_data) { /* XXX change it */ } break; default: DPRINTF("unknown\n"); error = ENOIOCTL; break; } return (error); } static void umodem_cfg_set_dtr(struct ucom_softc *ucom, uint8_t onoff) { struct umodem_softc *sc = ucom->sc_parent; struct usb_device_request req; DPRINTF("onoff=%d\n", onoff); if (onoff) sc->sc_line |= UCDC_LINE_DTR; else sc->sc_line &= ~UCDC_LINE_DTR; req.bmRequestType = UT_WRITE_CLASS_INTERFACE; req.bRequest = UCDC_SET_CONTROL_LINE_STATE; USETW(req.wValue, sc->sc_line); req.wIndex[0] = sc->sc_ctrl_iface_no; req.wIndex[1] = 0; USETW(req.wLength, 0); ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, &req, NULL, 0, 1000); } static void umodem_cfg_set_rts(struct ucom_softc *ucom, uint8_t onoff) { struct umodem_softc *sc = ucom->sc_parent; struct usb_device_request req; DPRINTF("onoff=%d\n", onoff); if (onoff) sc->sc_line |= UCDC_LINE_RTS; else sc->sc_line &= ~UCDC_LINE_RTS; req.bmRequestType = UT_WRITE_CLASS_INTERFACE; req.bRequest = UCDC_SET_CONTROL_LINE_STATE; USETW(req.wValue, sc->sc_line); req.wIndex[0] = sc->sc_ctrl_iface_no; req.wIndex[1] = 0; USETW(req.wLength, 0); ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, &req, NULL, 0, 1000); } static void umodem_cfg_set_break(struct ucom_softc *ucom, uint8_t onoff) { struct umodem_softc *sc = ucom->sc_parent; struct usb_device_request req; uint16_t temp; DPRINTF("onoff=%d\n", onoff); if (sc->sc_acm_cap & USB_CDC_ACM_HAS_BREAK) { temp = onoff ? UCDC_BREAK_ON : UCDC_BREAK_OFF; req.bmRequestType = UT_WRITE_CLASS_INTERFACE; req.bRequest = UCDC_SEND_BREAK; USETW(req.wValue, temp); req.wIndex[0] = sc->sc_ctrl_iface_no; req.wIndex[1] = 0; USETW(req.wLength, 0); ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, &req, NULL, 0, 1000); } } static void umodem_intr_write_callback(struct usb_xfer *xfer, usb_error_t error) { int actlen; usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: DPRINTF("Transferred %d bytes\n", actlen); /* FALLTHROUGH */ case USB_ST_SETUP: tr_setup: break; default: /* Error */ if (error != USB_ERR_CANCELLED) { /* start clear stall */ usbd_xfer_set_stall(xfer); goto tr_setup; } break; } } static void umodem_intr_read_callback(struct usb_xfer *xfer, usb_error_t error) { struct usb_cdc_notification pkt; struct umodem_softc *sc = usbd_xfer_softc(xfer); struct usb_page_cache *pc; uint16_t wLen; int actlen; usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: if (actlen < 8) { DPRINTF("received short packet, " "%d bytes\n", actlen); goto tr_setup; } if (actlen > (int)sizeof(pkt)) { DPRINTF("truncating message\n"); actlen = sizeof(pkt); } pc = usbd_xfer_get_frame(xfer, 0); usbd_copy_out(pc, 0, &pkt, actlen); actlen -= 8; wLen = UGETW(pkt.wLength); if (actlen > wLen) { actlen = wLen; } if (pkt.bmRequestType != UCDC_NOTIFICATION) { DPRINTF("unknown message type, " "0x%02x, on notify pipe!\n", pkt.bmRequestType); goto tr_setup; } switch (pkt.bNotification) { case UCDC_N_SERIAL_STATE: /* * Set the serial state in ucom driver based on * the bits from the notify message */ if (actlen < 2) { DPRINTF("invalid notification " "length, %d bytes!\n", actlen); break; } DPRINTF("notify bytes = %02x%02x\n", pkt.data[0], pkt.data[1]); /* Currently, lsr is always zero. */ sc->sc_lsr = 0; sc->sc_msr = 0; if (pkt.data[0] & UCDC_N_SERIAL_RI) { sc->sc_msr |= SER_RI; } if (pkt.data[0] & UCDC_N_SERIAL_DSR) { sc->sc_msr |= SER_DSR; } if (pkt.data[0] & UCDC_N_SERIAL_DCD) { sc->sc_msr |= SER_DCD; } ucom_status_change(&sc->sc_ucom); break; default: DPRINTF("unknown notify message: 0x%02x\n", pkt.bNotification); break; } case USB_ST_SETUP: tr_setup: usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); usbd_transfer_submit(xfer); return; default: /* Error */ if (error != USB_ERR_CANCELLED) { /* try to clear stall first */ usbd_xfer_set_stall(xfer); goto tr_setup; } return; } } static void umodem_write_callback(struct usb_xfer *xfer, usb_error_t error) { struct umodem_softc *sc = usbd_xfer_softc(xfer); struct usb_page_cache *pc; uint32_t actlen; switch (USB_GET_STATE(xfer)) { case USB_ST_SETUP: case USB_ST_TRANSFERRED: tr_setup: + if (usbd_xfer_get_and_clr_zlp(xfer)) + break; + pc = usbd_xfer_get_frame(xfer, 0); if (ucom_get_data(&sc->sc_ucom, pc, 0, UMODEM_BUF_SIZE, &actlen)) { usbd_xfer_set_frame_len(xfer, 0, actlen); usbd_transfer_submit(xfer); } - return; + break; default: /* Error */ if (error != USB_ERR_CANCELLED) { /* try to clear stall first */ usbd_xfer_set_stall(xfer); goto tr_setup; } - return; + break; } } static void umodem_read_callback(struct usb_xfer *xfer, usb_error_t error) { struct umodem_softc *sc = usbd_xfer_softc(xfer); struct usb_page_cache *pc; int actlen; usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: DPRINTF("actlen=%d\n", actlen); pc = usbd_xfer_get_frame(xfer, 0); ucom_put_data(&sc->sc_ucom, pc, 0, actlen); case USB_ST_SETUP: tr_setup: usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); usbd_transfer_submit(xfer); return; default: /* Error */ if (error != USB_ERR_CANCELLED) { /* try to clear stall first */ usbd_xfer_set_stall(xfer); goto tr_setup; } return; } } static void * umodem_get_desc(struct usb_attach_arg *uaa, uint8_t type, uint8_t subtype) { return (usbd_find_descriptor(uaa->device, NULL, uaa->info.bIfaceIndex, type, 0xFF, subtype, 0xFF)); } static usb_error_t umodem_set_comm_feature(struct usb_device *udev, uint8_t iface_no, uint16_t feature, uint16_t state) { struct usb_device_request req; struct usb_cdc_abstract_state ast; DPRINTF("feature=%d state=%d\n", feature, state); req.bmRequestType = UT_WRITE_CLASS_INTERFACE; req.bRequest = UCDC_SET_COMM_FEATURE; USETW(req.wValue, feature); req.wIndex[0] = iface_no; req.wIndex[1] = 0; USETW(req.wLength, UCDC_ABSTRACT_STATE_LENGTH); USETW(ast.wState, state); return (usbd_do_request(udev, NULL, &req, &ast)); } static int umodem_detach(device_t dev) { struct umodem_softc *sc = device_get_softc(dev); DPRINTF("sc=%p\n", sc); ucom_detach(&sc->sc_super_ucom, &sc->sc_ucom); usbd_transfer_unsetup(sc->sc_xfer, UMODEM_N_TRANSFER); device_claim_softc(dev); umodem_free_softc(sc); return (0); } UCOM_UNLOAD_DRAIN(umodem); static void umodem_free_softc(struct umodem_softc *sc) { if (ucom_unref(&sc->sc_super_ucom)) { mtx_destroy(&sc->sc_mtx); device_free_softc(sc); } } static void umodem_free(struct ucom_softc *ucom) { umodem_free_softc(ucom->sc_parent); } static void umodem_poll(struct ucom_softc *ucom) { struct umodem_softc *sc = ucom->sc_parent; usbd_transfer_poll(sc->sc_xfer, UMODEM_N_TRANSFER); } static int umodem_handle_request(device_t dev, const void *preq, void **pptr, uint16_t *plen, uint16_t offset, uint8_t *pstate) { struct umodem_softc *sc = device_get_softc(dev); const struct usb_device_request *req = preq; uint8_t is_complete = *pstate; DPRINTF("sc=%p\n", sc); if (!is_complete) { if ((req->bmRequestType == UT_WRITE_CLASS_INTERFACE) && (req->bRequest == UCDC_SET_LINE_CODING) && (req->wIndex[0] == sc->sc_ctrl_iface_no) && (req->wIndex[1] == 0x00) && (req->wValue[0] == 0x00) && (req->wValue[1] == 0x00)) { if (offset == 0) { *plen = sizeof(sc->sc_line_coding); *pptr = &sc->sc_line_coding; } else { *plen = 0; } return (0); } else if ((req->bmRequestType == UT_WRITE_CLASS_INTERFACE) && (req->wIndex[0] == sc->sc_ctrl_iface_no) && (req->wIndex[1] == 0x00) && (req->bRequest == UCDC_SET_COMM_FEATURE)) { if (offset == 0) { *plen = sizeof(sc->sc_abstract_state); *pptr = &sc->sc_abstract_state; } else { *plen = 0; } return (0); } else if ((req->bmRequestType == UT_WRITE_CLASS_INTERFACE) && (req->wIndex[0] == sc->sc_ctrl_iface_no) && (req->wIndex[1] == 0x00) && (req->bRequest == UCDC_SET_CONTROL_LINE_STATE)) { *plen = 0; return (0); } else if ((req->bmRequestType == UT_WRITE_CLASS_INTERFACE) && (req->wIndex[0] == sc->sc_ctrl_iface_no) && (req->wIndex[1] == 0x00) && (req->bRequest == UCDC_SEND_BREAK)) { *plen = 0; return (0); } } return (ENXIO); /* use builtin handler */ } diff --git a/sys/dev/usb/serial/umoscom.c b/sys/dev/usb/serial/umoscom.c index 724ab7f94092..536772a7f697 100644 --- a/sys/dev/usb/serial/umoscom.c +++ b/sys/dev/usb/serial/umoscom.c @@ -1,733 +1,735 @@ /* $FreeBSD$ */ /* $OpenBSD: umoscom.c,v 1.2 2006/10/26 06:02:43 jsg Exp $ */ /* * Copyright (c) 2006 Jonathan Gray * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "usbdevs.h" #define USB_DEBUG_VAR umoscom_debug #include #include #include #ifdef USB_DEBUG static int umoscom_debug = 0; static SYSCTL_NODE(_hw_usb, OID_AUTO, umoscom, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, "USB umoscom"); SYSCTL_INT(_hw_usb_umoscom, OID_AUTO, debug, CTLFLAG_RWTUN, &umoscom_debug, 0, "Debug level"); #endif #define UMOSCOM_BUFSIZE 1024 /* bytes */ #define UMOSCOM_CONFIG_INDEX 0 #define UMOSCOM_IFACE_INDEX 0 /* interrupt packet */ #define UMOSCOM_IIR_RLS 0x06 #define UMOSCOM_IIR_RDA 0x04 #define UMOSCOM_IIR_CTI 0x0c #define UMOSCOM_IIR_THR 0x02 #define UMOSCOM_IIR_MS 0x00 /* registers */ #define UMOSCOM_READ 0x0d #define UMOSCOM_WRITE 0x0e #define UMOSCOM_UART_REG 0x0300 #define UMOSCOM_VEND_REG 0x0000 #define UMOSCOM_TXBUF 0x00 /* Write */ #define UMOSCOM_RXBUF 0x00 /* Read */ #define UMOSCOM_INT 0x01 #define UMOSCOM_FIFO 0x02 /* Write */ #define UMOSCOM_ISR 0x02 /* Read */ #define UMOSCOM_LCR 0x03 #define UMOSCOM_MCR 0x04 #define UMOSCOM_LSR 0x05 #define UMOSCOM_MSR 0x06 #define UMOSCOM_SCRATCH 0x07 #define UMOSCOM_DIV_LO 0x08 #define UMOSCOM_DIV_HI 0x09 #define UMOSCOM_EFR 0x0a #define UMOSCOM_XON1 0x0b #define UMOSCOM_XON2 0x0c #define UMOSCOM_XOFF1 0x0d #define UMOSCOM_XOFF2 0x0e #define UMOSCOM_BAUDLO 0x00 #define UMOSCOM_BAUDHI 0x01 #define UMOSCOM_INT_RXEN 0x01 #define UMOSCOM_INT_TXEN 0x02 #define UMOSCOM_INT_RSEN 0x04 #define UMOSCOM_INT_MDMEM 0x08 #define UMOSCOM_INT_SLEEP 0x10 #define UMOSCOM_INT_XOFF 0x20 #define UMOSCOM_INT_RTS 0x40 #define UMOSCOM_FIFO_EN 0x01 #define UMOSCOM_FIFO_RXCLR 0x02 #define UMOSCOM_FIFO_TXCLR 0x04 #define UMOSCOM_FIFO_DMA_BLK 0x08 #define UMOSCOM_FIFO_TXLVL_MASK 0x30 #define UMOSCOM_FIFO_TXLVL_8 0x00 #define UMOSCOM_FIFO_TXLVL_16 0x10 #define UMOSCOM_FIFO_TXLVL_32 0x20 #define UMOSCOM_FIFO_TXLVL_56 0x30 #define UMOSCOM_FIFO_RXLVL_MASK 0xc0 #define UMOSCOM_FIFO_RXLVL_8 0x00 #define UMOSCOM_FIFO_RXLVL_16 0x40 #define UMOSCOM_FIFO_RXLVL_56 0x80 #define UMOSCOM_FIFO_RXLVL_80 0xc0 #define UMOSCOM_ISR_MDM 0x00 #define UMOSCOM_ISR_NONE 0x01 #define UMOSCOM_ISR_TX 0x02 #define UMOSCOM_ISR_RX 0x04 #define UMOSCOM_ISR_LINE 0x06 #define UMOSCOM_ISR_RXTIMEOUT 0x0c #define UMOSCOM_ISR_RX_XOFF 0x10 #define UMOSCOM_ISR_RTSCTS 0x20 #define UMOSCOM_ISR_FIFOEN 0xc0 #define UMOSCOM_LCR_DBITS(x) ((x) - 5) #define UMOSCOM_LCR_STOP_BITS_1 0x00 #define UMOSCOM_LCR_STOP_BITS_2 0x04 /* 2 if 6-8 bits/char or 1.5 if 5 */ #define UMOSCOM_LCR_PARITY_NONE 0x00 #define UMOSCOM_LCR_PARITY_ODD 0x08 #define UMOSCOM_LCR_PARITY_EVEN 0x18 #define UMOSCOM_LCR_BREAK 0x40 #define UMOSCOM_LCR_DIVLATCH_EN 0x80 #define UMOSCOM_MCR_DTR 0x01 #define UMOSCOM_MCR_RTS 0x02 #define UMOSCOM_MCR_LOOP 0x04 #define UMOSCOM_MCR_INTEN 0x08 #define UMOSCOM_MCR_LOOPBACK 0x10 #define UMOSCOM_MCR_XONANY 0x20 #define UMOSCOM_MCR_IRDA_EN 0x40 #define UMOSCOM_MCR_BAUD_DIV4 0x80 #define UMOSCOM_LSR_RXDATA 0x01 #define UMOSCOM_LSR_RXOVER 0x02 #define UMOSCOM_LSR_RXPAR_ERR 0x04 #define UMOSCOM_LSR_RXFRM_ERR 0x08 #define UMOSCOM_LSR_RXBREAK 0x10 #define UMOSCOM_LSR_TXEMPTY 0x20 #define UMOSCOM_LSR_TXALLEMPTY 0x40 #define UMOSCOM_LSR_TXFIFO_ERR 0x80 #define UMOSCOM_MSR_CTS_CHG 0x01 #define UMOSCOM_MSR_DSR_CHG 0x02 #define UMOSCOM_MSR_RI_CHG 0x04 #define UMOSCOM_MSR_CD_CHG 0x08 #define UMOSCOM_MSR_CTS 0x10 #define UMOSCOM_MSR_RTS 0x20 #define UMOSCOM_MSR_RI 0x40 #define UMOSCOM_MSR_CD 0x80 #define UMOSCOM_BAUD_REF 115200 enum { UMOSCOM_BULK_DT_WR, UMOSCOM_BULK_DT_RD, UMOSCOM_INTR_DT_RD, UMOSCOM_N_TRANSFER, }; struct umoscom_softc { struct ucom_super_softc sc_super_ucom; struct ucom_softc sc_ucom; struct usb_xfer *sc_xfer[UMOSCOM_N_TRANSFER]; struct usb_device *sc_udev; struct mtx sc_mtx; uint8_t sc_mcr; uint8_t sc_lcr; }; /* prototypes */ static device_probe_t umoscom_probe; static device_attach_t umoscom_attach; static device_detach_t umoscom_detach; static void umoscom_free_softc(struct umoscom_softc *); static usb_callback_t umoscom_write_callback; static usb_callback_t umoscom_read_callback; static usb_callback_t umoscom_intr_callback; static void umoscom_free(struct ucom_softc *); static void umoscom_cfg_open(struct ucom_softc *); static void umoscom_cfg_close(struct ucom_softc *); static void umoscom_cfg_set_break(struct ucom_softc *, uint8_t); static void umoscom_cfg_set_dtr(struct ucom_softc *, uint8_t); static void umoscom_cfg_set_rts(struct ucom_softc *, uint8_t); static int umoscom_pre_param(struct ucom_softc *, struct termios *); static void umoscom_cfg_param(struct ucom_softc *, struct termios *); static void umoscom_cfg_get_status(struct ucom_softc *, uint8_t *, uint8_t *); static void umoscom_cfg_write(struct umoscom_softc *, uint16_t, uint16_t); static uint8_t umoscom_cfg_read(struct umoscom_softc *, uint16_t); static void umoscom_start_read(struct ucom_softc *); static void umoscom_stop_read(struct ucom_softc *); static void umoscom_start_write(struct ucom_softc *); static void umoscom_stop_write(struct ucom_softc *); static void umoscom_poll(struct ucom_softc *ucom); static const struct usb_config umoscom_config_data[UMOSCOM_N_TRANSFER] = { [UMOSCOM_BULK_DT_WR] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, .bufsize = UMOSCOM_BUFSIZE, .flags = {.pipe_bof = 1,.force_short_xfer = 1,}, .callback = &umoscom_write_callback, }, [UMOSCOM_BULK_DT_RD] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, .bufsize = UMOSCOM_BUFSIZE, .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, .callback = &umoscom_read_callback, }, [UMOSCOM_INTR_DT_RD] = { .type = UE_INTERRUPT, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, .bufsize = 0, /* use wMaxPacketSize */ .callback = &umoscom_intr_callback, }, }; static const struct ucom_callback umoscom_callback = { /* configuration callbacks */ .ucom_cfg_get_status = &umoscom_cfg_get_status, .ucom_cfg_set_dtr = &umoscom_cfg_set_dtr, .ucom_cfg_set_rts = &umoscom_cfg_set_rts, .ucom_cfg_set_break = &umoscom_cfg_set_break, .ucom_cfg_param = &umoscom_cfg_param, .ucom_cfg_open = &umoscom_cfg_open, .ucom_cfg_close = &umoscom_cfg_close, /* other callbacks */ .ucom_pre_param = &umoscom_pre_param, .ucom_start_read = &umoscom_start_read, .ucom_stop_read = &umoscom_stop_read, .ucom_start_write = &umoscom_start_write, .ucom_stop_write = &umoscom_stop_write, .ucom_poll = &umoscom_poll, .ucom_free = &umoscom_free, }; static device_method_t umoscom_methods[] = { DEVMETHOD(device_probe, umoscom_probe), DEVMETHOD(device_attach, umoscom_attach), DEVMETHOD(device_detach, umoscom_detach), DEVMETHOD_END }; static devclass_t umoscom_devclass; static driver_t umoscom_driver = { .name = "umoscom", .methods = umoscom_methods, .size = sizeof(struct umoscom_softc), }; static const STRUCT_USB_HOST_ID umoscom_devs[] = { {USB_VPI(USB_VENDOR_MOSCHIP, USB_PRODUCT_MOSCHIP_MCS7703, 0)} }; DRIVER_MODULE(umoscom, uhub, umoscom_driver, umoscom_devclass, NULL, 0); MODULE_DEPEND(umoscom, ucom, 1, 1, 1); MODULE_DEPEND(umoscom, usb, 1, 1, 1); MODULE_VERSION(umoscom, 1); USB_PNP_HOST_INFO(umoscom_devs); static int umoscom_probe(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); if (uaa->usb_mode != USB_MODE_HOST) { return (ENXIO); } if (uaa->info.bConfigIndex != UMOSCOM_CONFIG_INDEX) { return (ENXIO); } if (uaa->info.bIfaceIndex != UMOSCOM_IFACE_INDEX) { return (ENXIO); } return (usbd_lookup_id_by_uaa(umoscom_devs, sizeof(umoscom_devs), uaa)); } static int umoscom_attach(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); struct umoscom_softc *sc = device_get_softc(dev); int error; uint8_t iface_index; sc->sc_udev = uaa->device; sc->sc_mcr = 0x08; /* enable interrupts */ /* XXX the device doesn't provide any ID string, so set a static one */ device_set_desc(dev, "MOSCHIP USB Serial Port Adapter"); device_printf(dev, "\n"); mtx_init(&sc->sc_mtx, "umoscom", NULL, MTX_DEF); ucom_ref(&sc->sc_super_ucom); iface_index = UMOSCOM_IFACE_INDEX; error = usbd_transfer_setup(uaa->device, &iface_index, sc->sc_xfer, umoscom_config_data, UMOSCOM_N_TRANSFER, sc, &sc->sc_mtx); if (error) { goto detach; } /* clear stall at first run */ mtx_lock(&sc->sc_mtx); - usbd_xfer_set_stall(sc->sc_xfer[UMOSCOM_BULK_DT_WR]); - usbd_xfer_set_stall(sc->sc_xfer[UMOSCOM_BULK_DT_RD]); + usbd_xfer_set_zlp(sc->sc_xfer[UMOSCOM_BULK_DT_WR]); mtx_unlock(&sc->sc_mtx); error = ucom_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc, &umoscom_callback, &sc->sc_mtx); if (error) { goto detach; } ucom_set_pnpinfo_usb(&sc->sc_super_ucom, dev); return (0); detach: device_printf(dev, "attach error: %s\n", usbd_errstr(error)); umoscom_detach(dev); return (ENXIO); } static int umoscom_detach(device_t dev) { struct umoscom_softc *sc = device_get_softc(dev); ucom_detach(&sc->sc_super_ucom, &sc->sc_ucom); usbd_transfer_unsetup(sc->sc_xfer, UMOSCOM_N_TRANSFER); device_claim_softc(dev); umoscom_free_softc(sc); return (0); } UCOM_UNLOAD_DRAIN(umoscom); static void umoscom_free_softc(struct umoscom_softc *sc) { if (ucom_unref(&sc->sc_super_ucom)) { mtx_destroy(&sc->sc_mtx); device_free_softc(sc); } } static void umoscom_free(struct ucom_softc *ucom) { umoscom_free_softc(ucom->sc_parent); } static void umoscom_cfg_open(struct ucom_softc *ucom) { struct umoscom_softc *sc = ucom->sc_parent; DPRINTF("\n"); /* Purge FIFOs or odd things happen */ umoscom_cfg_write(sc, UMOSCOM_FIFO, 0x00 | UMOSCOM_UART_REG); /* Enable FIFO */ umoscom_cfg_write(sc, UMOSCOM_FIFO, UMOSCOM_FIFO_EN | UMOSCOM_FIFO_RXCLR | UMOSCOM_FIFO_TXCLR | UMOSCOM_FIFO_DMA_BLK | UMOSCOM_FIFO_RXLVL_MASK | UMOSCOM_UART_REG); /* Enable Interrupt Registers */ umoscom_cfg_write(sc, UMOSCOM_INT, 0x0C | UMOSCOM_UART_REG); /* Magic */ umoscom_cfg_write(sc, 0x01, 0x08); /* Magic */ umoscom_cfg_write(sc, 0x00, 0x02); } static void umoscom_cfg_close(struct ucom_softc *ucom) { return; } static void umoscom_cfg_set_break(struct ucom_softc *ucom, uint8_t onoff) { struct umoscom_softc *sc = ucom->sc_parent; uint16_t val; val = sc->sc_lcr; if (onoff) val |= UMOSCOM_LCR_BREAK; umoscom_cfg_write(sc, UMOSCOM_LCR, val | UMOSCOM_UART_REG); } static void umoscom_cfg_set_dtr(struct ucom_softc *ucom, uint8_t onoff) { struct umoscom_softc *sc = ucom->sc_parent; if (onoff) sc->sc_mcr |= UMOSCOM_MCR_DTR; else sc->sc_mcr &= ~UMOSCOM_MCR_DTR; umoscom_cfg_write(sc, UMOSCOM_MCR, sc->sc_mcr | UMOSCOM_UART_REG); } static void umoscom_cfg_set_rts(struct ucom_softc *ucom, uint8_t onoff) { struct umoscom_softc *sc = ucom->sc_parent; if (onoff) sc->sc_mcr |= UMOSCOM_MCR_RTS; else sc->sc_mcr &= ~UMOSCOM_MCR_RTS; umoscom_cfg_write(sc, UMOSCOM_MCR, sc->sc_mcr | UMOSCOM_UART_REG); } static int umoscom_pre_param(struct ucom_softc *ucom, struct termios *t) { if ((t->c_ospeed <= 1) || (t->c_ospeed > 115200)) return (EINVAL); return (0); } static void umoscom_cfg_param(struct ucom_softc *ucom, struct termios *t) { struct umoscom_softc *sc = ucom->sc_parent; uint16_t data; DPRINTF("speed=%d\n", t->c_ospeed); data = ((uint32_t)UMOSCOM_BAUD_REF) / ((uint32_t)t->c_ospeed); if (data == 0) { DPRINTF("invalid baud rate!\n"); return; } umoscom_cfg_write(sc, UMOSCOM_LCR, UMOSCOM_LCR_DIVLATCH_EN | UMOSCOM_UART_REG); umoscom_cfg_write(sc, UMOSCOM_BAUDLO, (data & 0xFF) | UMOSCOM_UART_REG); umoscom_cfg_write(sc, UMOSCOM_BAUDHI, ((data >> 8) & 0xFF) | UMOSCOM_UART_REG); if (t->c_cflag & CSTOPB) data = UMOSCOM_LCR_STOP_BITS_2; else data = UMOSCOM_LCR_STOP_BITS_1; if (t->c_cflag & PARENB) { if (t->c_cflag & PARODD) data |= UMOSCOM_LCR_PARITY_ODD; else data |= UMOSCOM_LCR_PARITY_EVEN; } else data |= UMOSCOM_LCR_PARITY_NONE; switch (t->c_cflag & CSIZE) { case CS5: data |= UMOSCOM_LCR_DBITS(5); break; case CS6: data |= UMOSCOM_LCR_DBITS(6); break; case CS7: data |= UMOSCOM_LCR_DBITS(7); break; case CS8: data |= UMOSCOM_LCR_DBITS(8); break; } sc->sc_lcr = data; umoscom_cfg_write(sc, UMOSCOM_LCR, data | UMOSCOM_UART_REG); } static void umoscom_cfg_get_status(struct ucom_softc *ucom, uint8_t *p_lsr, uint8_t *p_msr) { struct umoscom_softc *sc = ucom->sc_parent; uint8_t msr; DPRINTFN(5, "\n"); /* * Read status registers. MSR bits need translation from ns16550 to * SER_* values. LSR bits are ns16550 in hardware and ucom. */ *p_lsr = umoscom_cfg_read(sc, UMOSCOM_LSR); msr = umoscom_cfg_read(sc, UMOSCOM_MSR); /* translate bits */ if (msr & UMOSCOM_MSR_CTS) *p_msr |= SER_CTS; if (msr & UMOSCOM_MSR_CD) *p_msr |= SER_DCD; if (msr & UMOSCOM_MSR_RI) *p_msr |= SER_RI; if (msr & UMOSCOM_MSR_RTS) *p_msr |= SER_DSR; } static void umoscom_cfg_write(struct umoscom_softc *sc, uint16_t reg, uint16_t val) { struct usb_device_request req; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = UMOSCOM_WRITE; USETW(req.wValue, val); USETW(req.wIndex, reg); USETW(req.wLength, 0); ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, &req, NULL, 0, 1000); } static uint8_t umoscom_cfg_read(struct umoscom_softc *sc, uint16_t reg) { struct usb_device_request req; uint8_t val; req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = UMOSCOM_READ; USETW(req.wValue, 0); USETW(req.wIndex, reg); USETW(req.wLength, 1); ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, &req, &val, 0, 1000); DPRINTF("reg=0x%04x, val=0x%02x\n", reg, val); return (val); } static void umoscom_start_read(struct ucom_softc *ucom) { struct umoscom_softc *sc = ucom->sc_parent; #if 0 /* start interrupt endpoint */ usbd_transfer_start(sc->sc_xfer[UMOSCOM_INTR_DT_RD]); #endif /* start read endpoint */ usbd_transfer_start(sc->sc_xfer[UMOSCOM_BULK_DT_RD]); } static void umoscom_stop_read(struct ucom_softc *ucom) { struct umoscom_softc *sc = ucom->sc_parent; /* stop interrupt transfer */ usbd_transfer_stop(sc->sc_xfer[UMOSCOM_INTR_DT_RD]); /* stop read endpoint */ usbd_transfer_stop(sc->sc_xfer[UMOSCOM_BULK_DT_RD]); } static void umoscom_start_write(struct ucom_softc *ucom) { struct umoscom_softc *sc = ucom->sc_parent; usbd_transfer_start(sc->sc_xfer[UMOSCOM_BULK_DT_WR]); } static void umoscom_stop_write(struct ucom_softc *ucom) { struct umoscom_softc *sc = ucom->sc_parent; usbd_transfer_stop(sc->sc_xfer[UMOSCOM_BULK_DT_WR]); } static void umoscom_write_callback(struct usb_xfer *xfer, usb_error_t error) { struct umoscom_softc *sc = usbd_xfer_softc(xfer); struct usb_page_cache *pc; uint32_t actlen; switch (USB_GET_STATE(xfer)) { case USB_ST_SETUP: case USB_ST_TRANSFERRED: tr_setup: DPRINTF("\n"); + if (usbd_xfer_get_and_clr_zlp(xfer)) + break; + pc = usbd_xfer_get_frame(xfer, 0); if (ucom_get_data(&sc->sc_ucom, pc, 0, UMOSCOM_BUFSIZE, &actlen)) { usbd_xfer_set_frame_len(xfer, 0, actlen); usbd_transfer_submit(xfer); } - return; + break; default: /* Error */ if (error != USB_ERR_CANCELLED) { DPRINTFN(0, "transfer failed\n"); /* try to clear stall first */ usbd_xfer_set_stall(xfer); goto tr_setup; } - return; + break; } } static void umoscom_read_callback(struct usb_xfer *xfer, usb_error_t error) { struct umoscom_softc *sc = usbd_xfer_softc(xfer); struct usb_page_cache *pc; int actlen; usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: DPRINTF("got %d bytes\n", actlen); pc = usbd_xfer_get_frame(xfer, 0); ucom_put_data(&sc->sc_ucom, pc, 0, actlen); case USB_ST_SETUP: tr_setup: DPRINTF("\n"); usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); usbd_transfer_submit(xfer); return; default: /* Error */ if (error != USB_ERR_CANCELLED) { DPRINTFN(0, "transfer failed\n"); /* try to clear stall first */ usbd_xfer_set_stall(xfer); goto tr_setup; } return; } } static void umoscom_intr_callback(struct usb_xfer *xfer, usb_error_t error) { struct umoscom_softc *sc = usbd_xfer_softc(xfer); int actlen; usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: if (actlen < 2) { DPRINTF("too short message\n"); goto tr_setup; } ucom_status_change(&sc->sc_ucom); case USB_ST_SETUP: tr_setup: usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); usbd_transfer_submit(xfer); return; default: /* Error */ if (error != USB_ERR_CANCELLED) { DPRINTFN(0, "transfer failed\n"); /* try to clear stall first */ usbd_xfer_set_stall(xfer); goto tr_setup; } return; } } static void umoscom_poll(struct ucom_softc *ucom) { struct umoscom_softc *sc = ucom->sc_parent; usbd_transfer_poll(sc->sc_xfer, UMOSCOM_N_TRANSFER); } diff --git a/sys/dev/usb/serial/uplcom.c b/sys/dev/usb/serial/uplcom.c index 9efac9ff6b7c..6808ddd64a4b 100644 --- a/sys/dev/usb/serial/uplcom.c +++ b/sys/dev/usb/serial/uplcom.c @@ -1,1152 +1,1153 @@ /* $NetBSD: uplcom.c,v 1.21 2001/11/13 06:24:56 lukem Exp $ */ #include __FBSDID("$FreeBSD$"); /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD AND BSD-2-Clause-NetBSD * * Copyright (c) 2001-2003, 2005 Shunsuke Akiyama . * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 THE AUTHOR OR CONTRIBUTORS 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. */ /*- * Copyright (c) 2001 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Ichiro FUKUHARA (ichiro@ichiro.org). * * 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. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 THE FOUNDATION OR CONTRIBUTORS * 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. */ /* * This driver supports several USB-to-RS232 serial adapters driven by * Prolific PL-2303, PL-2303X and probably PL-2303HX USB-to-RS232 * bridge chip. The adapters are sold under many different brand * names. * * Datasheets are available at Prolific www site at * http://www.prolific.com.tw. The datasheets don't contain full * programming information for the chip. * * PL-2303HX is probably programmed the same as PL-2303X. * * There are several differences between PL-2303 and PL-2303(H)X. * PL-2303(H)X can do higher bitrate in bulk mode, has _probably_ * different command for controlling CRTSCTS and needs special * sequence of commands for initialization which aren't also * documented in the datasheet. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "usbdevs.h" #define USB_DEBUG_VAR uplcom_debug #include #include #include #ifdef USB_DEBUG static int uplcom_debug = 0; static SYSCTL_NODE(_hw_usb, OID_AUTO, uplcom, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, "USB uplcom"); SYSCTL_INT(_hw_usb_uplcom, OID_AUTO, debug, CTLFLAG_RWTUN, &uplcom_debug, 0, "Debug level"); #endif #define UPLCOM_MODVER 1 /* module version */ #define UPLCOM_CONFIG_INDEX 0 #define UPLCOM_IFACE_INDEX 0 #define UPLCOM_SECOND_IFACE_INDEX 1 #ifndef UPLCOM_INTR_INTERVAL #define UPLCOM_INTR_INTERVAL 0 /* default */ #endif #define UPLCOM_BULK_BUF_SIZE 1024 /* bytes */ #define UPLCOM_SET_REQUEST 0x01 #define UPLCOM_SET_REQUEST_PL2303HXN 0x80 #define UPLCOM_SET_CRTSCTS 0x41 #define UPLCOM_SET_CRTSCTS_PL2303X 0x61 #define UPLCOM_SET_CRTSCTS_PL2303HXN 0xFA #define UPLCOM_CLEAR_CRTSCTS_PL2303HXN 0xFF #define UPLCOM_CRTSCTS_REG_PL2303HXN 0x0A #define UPLCOM_STATUS_REG_PL2303HX 0x8080 #define RSAQ_STATUS_CTS 0x80 #define RSAQ_STATUS_OVERRUN_ERROR 0x40 #define RSAQ_STATUS_PARITY_ERROR 0x20 #define RSAQ_STATUS_FRAME_ERROR 0x10 #define RSAQ_STATUS_RING 0x08 #define RSAQ_STATUS_BREAK_ERROR 0x04 #define RSAQ_STATUS_DSR 0x02 #define RSAQ_STATUS_DCD 0x01 #define TYPE_PL2303 0 #define TYPE_PL2303HX 1 #define TYPE_PL2303HXD 2 #define TYPE_PL2303HXN 3 #define UPLCOM_STATE_INDEX 8 enum { UPLCOM_BULK_DT_WR, UPLCOM_BULK_DT_RD, UPLCOM_INTR_DT_RD, UPLCOM_N_TRANSFER, }; struct uplcom_softc { struct ucom_super_softc sc_super_ucom; struct ucom_softc sc_ucom; struct usb_xfer *sc_xfer[UPLCOM_N_TRANSFER]; struct usb_device *sc_udev; struct mtx sc_mtx; uint16_t sc_line; uint8_t sc_lsr; /* local status register */ uint8_t sc_msr; /* uplcom status register */ uint8_t sc_chiptype; /* type of chip */ uint8_t sc_ctrl_iface_no; uint8_t sc_data_iface_no; uint8_t sc_iface_index[2]; }; /* prototypes */ static usb_error_t uplcom_reset(struct uplcom_softc *, struct usb_device *); static usb_error_t uplcom_pl2303_do(struct usb_device *, uint8_t, uint8_t, uint16_t, uint16_t, uint16_t); static int uplcom_pl2303_init(struct usb_device *, uint8_t); static void uplcom_free(struct ucom_softc *); static void uplcom_cfg_set_dtr(struct ucom_softc *, uint8_t); static void uplcom_cfg_set_rts(struct ucom_softc *, uint8_t); static void uplcom_cfg_set_break(struct ucom_softc *, uint8_t); static int uplcom_pre_param(struct ucom_softc *, struct termios *); static void uplcom_cfg_param(struct ucom_softc *, struct termios *); static void uplcom_start_read(struct ucom_softc *); static void uplcom_stop_read(struct ucom_softc *); static void uplcom_start_write(struct ucom_softc *); static void uplcom_stop_write(struct ucom_softc *); static void uplcom_cfg_get_status(struct ucom_softc *, uint8_t *, uint8_t *); static void uplcom_poll(struct ucom_softc *ucom); static device_probe_t uplcom_probe; static device_attach_t uplcom_attach; static device_detach_t uplcom_detach; static void uplcom_free_softc(struct uplcom_softc *); static usb_callback_t uplcom_intr_callback; static usb_callback_t uplcom_write_callback; static usb_callback_t uplcom_read_callback; static const struct usb_config uplcom_config_data[UPLCOM_N_TRANSFER] = { [UPLCOM_BULK_DT_WR] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, .bufsize = UPLCOM_BULK_BUF_SIZE, .flags = {.pipe_bof = 1,.force_short_xfer = 1,}, .callback = &uplcom_write_callback, .if_index = 0, }, [UPLCOM_BULK_DT_RD] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, .bufsize = UPLCOM_BULK_BUF_SIZE, .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, .callback = &uplcom_read_callback, .if_index = 0, }, [UPLCOM_INTR_DT_RD] = { .type = UE_INTERRUPT, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, .bufsize = 0, /* use wMaxPacketSize */ .callback = &uplcom_intr_callback, .if_index = 1, }, }; static struct ucom_callback uplcom_callback = { .ucom_cfg_get_status = &uplcom_cfg_get_status, .ucom_cfg_set_dtr = &uplcom_cfg_set_dtr, .ucom_cfg_set_rts = &uplcom_cfg_set_rts, .ucom_cfg_set_break = &uplcom_cfg_set_break, .ucom_cfg_param = &uplcom_cfg_param, .ucom_pre_param = &uplcom_pre_param, .ucom_start_read = &uplcom_start_read, .ucom_stop_read = &uplcom_stop_read, .ucom_start_write = &uplcom_start_write, .ucom_stop_write = &uplcom_stop_write, .ucom_poll = &uplcom_poll, .ucom_free = &uplcom_free, }; #define UPLCOM_DEV(v,p) \ { USB_VENDOR(USB_VENDOR_##v), USB_PRODUCT(USB_PRODUCT_##v##_##p) } static const STRUCT_USB_HOST_ID uplcom_devs[] = { UPLCOM_DEV(ACERP, S81), /* BenQ S81 phone */ UPLCOM_DEV(ADLINK, ND6530), /* ADLINK ND-6530 USB-Serial */ UPLCOM_DEV(ALCATEL, OT535), /* Alcatel One Touch 535/735 */ UPLCOM_DEV(ALCOR, AU9720), /* Alcor AU9720 USB 2.0-RS232 */ UPLCOM_DEV(ANCHOR, SERIAL), /* Anchor Serial adapter */ UPLCOM_DEV(ATEN, UC232A), /* PLANEX USB-RS232 URS-03 */ UPLCOM_DEV(ATEN, UC232B), /* Prolific USB-RS232 Controller D */ UPLCOM_DEV(BELKIN, F5U257), /* Belkin F5U257 USB to Serial */ UPLCOM_DEV(COREGA, CGUSBRS232R), /* Corega CG-USBRS232R */ UPLCOM_DEV(EPSON, CRESSI_EDY), /* Cressi Edy diving computer */ UPLCOM_DEV(EPSON, N2ITION3), /* Zeagle N2iTion3 diving computer */ UPLCOM_DEV(ELECOM, UCSGT), /* ELECOM UC-SGT Serial Adapter */ UPLCOM_DEV(ELECOM, UCSGT0), /* ELECOM UC-SGT Serial Adapter */ UPLCOM_DEV(HAL, IMR001), /* HAL Corporation Crossam2+USB */ UPLCOM_DEV(HP, LD220), /* HP LD220 POS Display */ UPLCOM_DEV(IODATA, USBRSAQ), /* I/O DATA USB-RSAQ */ UPLCOM_DEV(IODATA, USBRSAQ5), /* I/O DATA USB-RSAQ5 */ UPLCOM_DEV(ITEGNO, WM1080A), /* iTegno WM1080A GSM/GFPRS modem */ UPLCOM_DEV(ITEGNO, WM2080A), /* iTegno WM2080A CDMA modem */ UPLCOM_DEV(LEADTEK, 9531), /* Leadtek 9531 GPS */ UPLCOM_DEV(MICROSOFT, 700WX), /* Microsoft Palm 700WX */ UPLCOM_DEV(MOBILEACTION, MA620), /* Mobile Action MA-620 Infrared Adapter */ UPLCOM_DEV(NETINDEX, WS002IN), /* Willcom W-S002IN */ UPLCOM_DEV(NOKIA2, CA42), /* Nokia CA-42 cable */ UPLCOM_DEV(OTI, DKU5), /* OTI DKU-5 cable */ UPLCOM_DEV(PANASONIC, TYTP50P6S), /* Panasonic TY-TP50P6-S flat screen */ UPLCOM_DEV(PLX, CA42), /* PLX CA-42 clone cable */ UPLCOM_DEV(PROLIFIC, ALLTRONIX_GPRS), /* Alltronix ACM003U00 modem */ UPLCOM_DEV(PROLIFIC, ALDIGA_AL11U), /* AlDiga AL-11U modem */ UPLCOM_DEV(PROLIFIC, DCU11), /* DCU-11 Phone Cable */ UPLCOM_DEV(PROLIFIC, HCR331), /* HCR331 Card Reader */ UPLCOM_DEV(PROLIFIC, MICROMAX_610U), /* Micromax 610U modem */ UPLCOM_DEV(PROLIFIC, MOTOROLA), /* Motorola cable */ UPLCOM_DEV(PROLIFIC, PHAROS), /* Prolific Pharos */ UPLCOM_DEV(PROLIFIC, PL2303), /* Generic adapter */ UPLCOM_DEV(PROLIFIC, PL2303GC), /* Generic adapter (PL2303HXN, type GC) */ UPLCOM_DEV(PROLIFIC, PL2303GB), /* Generic adapter (PL2303HXN, type GB) */ UPLCOM_DEV(PROLIFIC, PL2303GT), /* Generic adapter (PL2303HXN, type GT) */ UPLCOM_DEV(PROLIFIC, PL2303GL), /* Generic adapter (PL2303HXN, type GL) */ UPLCOM_DEV(PROLIFIC, PL2303GE), /* Generic adapter (PL2303HXN, type GE) */ UPLCOM_DEV(PROLIFIC, PL2303GS), /* Generic adapter (PL2303HXN, type GS) */ UPLCOM_DEV(PROLIFIC, RSAQ2), /* I/O DATA USB-RSAQ2 */ UPLCOM_DEV(PROLIFIC, RSAQ3), /* I/O DATA USB-RSAQ3 */ UPLCOM_DEV(PROLIFIC, UIC_MSR206), /* UIC MSR206 Card Reader */ UPLCOM_DEV(PROLIFIC2, PL2303), /* Prolific adapter */ UPLCOM_DEV(RADIOSHACK, USBCABLE), /* Radio Shack USB Adapter */ UPLCOM_DEV(RATOC, REXUSB60), /* RATOC REX-USB60 */ UPLCOM_DEV(SAGEM, USBSERIAL), /* Sagem USB-Serial Controller */ UPLCOM_DEV(SAMSUNG, I330), /* Samsung I330 phone cradle */ UPLCOM_DEV(SANWA, KB_USB2), /* Sanwa KB-USB2 Multimeter cable */ UPLCOM_DEV(SIEMENS3, EF81), /* Siemens EF81 */ UPLCOM_DEV(SIEMENS3, SX1), /* Siemens SX1 */ UPLCOM_DEV(SIEMENS3, X65), /* Siemens X65 */ UPLCOM_DEV(SIEMENS3, X75), /* Siemens X75 */ UPLCOM_DEV(SITECOM, SERIAL), /* Sitecom USB to Serial */ UPLCOM_DEV(SMART, PL2303), /* SMART Technologies USB to Serial */ UPLCOM_DEV(SONY, QN3), /* Sony QN3 phone cable */ UPLCOM_DEV(SONYERICSSON, DATAPILOT), /* Sony Ericsson Datapilot */ UPLCOM_DEV(SONYERICSSON, DCU10), /* Sony Ericsson DCU-10 Cable */ UPLCOM_DEV(SOURCENEXT, KEIKAI8), /* SOURCENEXT KeikaiDenwa 8 */ UPLCOM_DEV(SOURCENEXT, KEIKAI8_CHG), /* SOURCENEXT KeikaiDenwa 8 with charger */ UPLCOM_DEV(SPEEDDRAGON, MS3303H), /* Speed Dragon USB-Serial */ UPLCOM_DEV(SYNTECH, CPT8001C), /* Syntech CPT-8001C Barcode scanner */ UPLCOM_DEV(TDK, UHA6400), /* TDK USB-PHS Adapter UHA6400 */ UPLCOM_DEV(TDK, UPA9664), /* TDK USB-PHS Adapter UPA9664 */ UPLCOM_DEV(TRIPPLITE, U209), /* Tripp-Lite U209-000-R USB to Serial */ UPLCOM_DEV(YCCABLE, PL2303), /* YC Cable USB-Serial */ }; #undef UPLCOM_DEV static device_method_t uplcom_methods[] = { DEVMETHOD(device_probe, uplcom_probe), DEVMETHOD(device_attach, uplcom_attach), DEVMETHOD(device_detach, uplcom_detach), DEVMETHOD_END }; static devclass_t uplcom_devclass; static driver_t uplcom_driver = { .name = "uplcom", .methods = uplcom_methods, .size = sizeof(struct uplcom_softc), }; DRIVER_MODULE(uplcom, uhub, uplcom_driver, uplcom_devclass, NULL, 0); MODULE_DEPEND(uplcom, ucom, 1, 1, 1); MODULE_DEPEND(uplcom, usb, 1, 1, 1); MODULE_VERSION(uplcom, UPLCOM_MODVER); USB_PNP_HOST_INFO(uplcom_devs); static int uplcom_probe(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); DPRINTFN(11, "\n"); if (uaa->usb_mode != USB_MODE_HOST) { return (ENXIO); } if (uaa->info.bConfigIndex != UPLCOM_CONFIG_INDEX) { return (ENXIO); } if (uaa->info.bIfaceIndex != UPLCOM_IFACE_INDEX) { return (ENXIO); } return (usbd_lookup_id_by_uaa(uplcom_devs, sizeof(uplcom_devs), uaa)); } static int uplcom_attach(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); struct uplcom_softc *sc = device_get_softc(dev); struct usb_interface *iface; struct usb_interface_descriptor *id; struct usb_device_descriptor *dd; int error; struct usb_device_request req; usb_error_t err; uint8_t buf[4]; DPRINTFN(11, "\n"); device_set_usb_desc(dev); mtx_init(&sc->sc_mtx, "uplcom", NULL, MTX_DEF); ucom_ref(&sc->sc_super_ucom); DPRINTF("sc = %p\n", sc); sc->sc_udev = uaa->device; dd = usbd_get_device_descriptor(sc->sc_udev); switch (UGETW(dd->bcdDevice)) { case 0x0300: sc->sc_chiptype = TYPE_PL2303HX; /* or TA, that is HX with external crystal */ break; case 0x0400: sc->sc_chiptype = TYPE_PL2303HXD; /* or EA, that is HXD with ESD protection */ /* or RA, that has internal voltage level converter that works only up to 1Mbaud (!) */ break; case 0x0500: sc->sc_chiptype = TYPE_PL2303HXD; /* in fact it's TB, that is HXD with external crystal */ break; default: /* NOTE: I have no info about the bcdDevice for the base PL2303 (up to 1.2Mbaud, only fixed rates) and for PL2303SA (8-pin chip, up to 115200 baud */ /* Determine the chip type. This algorithm is taken from Linux. */ if (dd->bDeviceClass == 0x02) sc->sc_chiptype = TYPE_PL2303; else if (dd->bMaxPacketSize == 0x40) sc->sc_chiptype = TYPE_PL2303HX; else sc->sc_chiptype = TYPE_PL2303; break; } /* * The new chip revision PL2303HXN is only compatible with the new * UPLCOM_SET_REQUEST_PL2303HXN command. Issuing the old command * UPLCOM_SET_REQUEST to the new chip raises an error. Thus, PL2303HX * and PL2303HXN can be distinguished by issuing an old-style request * (on a status register) to the new chip and checking the error. */ if (sc->sc_chiptype == TYPE_PL2303HX) { req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = UPLCOM_SET_REQUEST; USETW(req.wValue, UPLCOM_STATUS_REG_PL2303HX); req.wIndex[0] = sc->sc_data_iface_no; req.wIndex[1] = 0; USETW(req.wLength, 1); err = usbd_do_request(sc->sc_udev, NULL, &req, buf); if (err) sc->sc_chiptype = TYPE_PL2303HXN; } switch (sc->sc_chiptype) { case TYPE_PL2303: DPRINTF("chiptype: 2303\n"); break; case TYPE_PL2303HX: DPRINTF("chiptype: 2303HX/TA\n"); break; case TYPE_PL2303HXN: DPRINTF("chiptype: 2303HXN\n"); break; case TYPE_PL2303HXD: DPRINTF("chiptype: 2303HXD/TB/RA/EA\n"); break; default: DPRINTF("chiptype: unknown %d\n", sc->sc_chiptype); break; } /* * USB-RSAQ1 has two interface * * USB-RSAQ1 | USB-RSAQ2 * -----------------+----------------- * Interface 0 |Interface 0 * Interrupt(0x81) | Interrupt(0x81) * -----------------+ BulkIN(0x02) * Interface 1 | BulkOUT(0x83) * BulkIN(0x02) | * BulkOUT(0x83) | */ sc->sc_ctrl_iface_no = uaa->info.bIfaceNum; sc->sc_iface_index[1] = UPLCOM_IFACE_INDEX; iface = usbd_get_iface(uaa->device, UPLCOM_SECOND_IFACE_INDEX); if (iface) { id = usbd_get_interface_descriptor(iface); if (id == NULL) { device_printf(dev, "no interface descriptor (2)\n"); goto detach; } sc->sc_data_iface_no = id->bInterfaceNumber; sc->sc_iface_index[0] = UPLCOM_SECOND_IFACE_INDEX; usbd_set_parent_iface(uaa->device, UPLCOM_SECOND_IFACE_INDEX, uaa->info.bIfaceIndex); } else { sc->sc_data_iface_no = sc->sc_ctrl_iface_no; sc->sc_iface_index[0] = UPLCOM_IFACE_INDEX; } error = usbd_transfer_setup(uaa->device, sc->sc_iface_index, sc->sc_xfer, uplcom_config_data, UPLCOM_N_TRANSFER, sc, &sc->sc_mtx); if (error) { DPRINTF("one or more missing USB endpoints, " "error=%s\n", usbd_errstr(error)); goto detach; } error = uplcom_reset(sc, uaa->device); if (error) { device_printf(dev, "reset failed, error=%s\n", usbd_errstr(error)); goto detach; } - if (sc->sc_chiptype == TYPE_PL2303) { - /* HX variants seem to lock up after a clear stall request. */ - mtx_lock(&sc->sc_mtx); - usbd_xfer_set_stall(sc->sc_xfer[UPLCOM_BULK_DT_WR]); - usbd_xfer_set_stall(sc->sc_xfer[UPLCOM_BULK_DT_RD]); - mtx_unlock(&sc->sc_mtx); - } else if (sc->sc_chiptype == TYPE_PL2303HX || - sc->sc_chiptype == TYPE_PL2303HXD) { + mtx_lock(&sc->sc_mtx); + usbd_xfer_set_zlp(sc->sc_xfer[UPLCOM_BULK_DT_WR]); + mtx_unlock(&sc->sc_mtx); + + if (sc->sc_chiptype == TYPE_PL2303HX || + sc->sc_chiptype == TYPE_PL2303HXD) { /* reset upstream data pipes */ if (uplcom_pl2303_do(sc->sc_udev, UT_WRITE_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 8, 0, 0) || uplcom_pl2303_do(sc->sc_udev, UT_WRITE_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 9, 0, 0)) { goto detach; } } else if (sc->sc_chiptype == TYPE_PL2303HXN) { /* reset upstream data pipes */ if (uplcom_pl2303_do(sc->sc_udev, UT_WRITE_VENDOR_DEVICE, UPLCOM_SET_REQUEST_PL2303HXN, 0x07, 0x03, 0)) { goto detach; } } error = ucom_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc, &uplcom_callback, &sc->sc_mtx); if (error) { goto detach; } /* * do the initialization during attach so that the system does not * sleep during open: */ if (uplcom_pl2303_init(uaa->device, sc->sc_chiptype)) { device_printf(dev, "init failed\n"); goto detach; } ucom_set_pnpinfo_usb(&sc->sc_super_ucom, dev); return (0); detach: uplcom_detach(dev); return (ENXIO); } static int uplcom_detach(device_t dev) { struct uplcom_softc *sc = device_get_softc(dev); DPRINTF("sc=%p\n", sc); ucom_detach(&sc->sc_super_ucom, &sc->sc_ucom); usbd_transfer_unsetup(sc->sc_xfer, UPLCOM_N_TRANSFER); device_claim_softc(dev); uplcom_free_softc(sc); return (0); } UCOM_UNLOAD_DRAIN(uplcom); static void uplcom_free_softc(struct uplcom_softc *sc) { if (ucom_unref(&sc->sc_super_ucom)) { mtx_destroy(&sc->sc_mtx); device_free_softc(sc); } } static void uplcom_free(struct ucom_softc *ucom) { uplcom_free_softc(ucom->sc_parent); } static usb_error_t uplcom_reset(struct uplcom_softc *sc, struct usb_device *udev) { struct usb_device_request req; if (sc->sc_chiptype == TYPE_PL2303HXN) { /* PL2303HXN doesn't need this reset sequence */ return (0); } req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = UPLCOM_SET_REQUEST; USETW(req.wValue, 0); req.wIndex[0] = sc->sc_data_iface_no; req.wIndex[1] = 0; USETW(req.wLength, 0); return (usbd_do_request(udev, NULL, &req, NULL)); } static usb_error_t uplcom_pl2303_do(struct usb_device *udev, uint8_t req_type, uint8_t request, uint16_t value, uint16_t index, uint16_t length) { struct usb_device_request req; usb_error_t err; uint8_t buf[4]; req.bmRequestType = req_type; req.bRequest = request; USETW(req.wValue, value); USETW(req.wIndex, index); USETW(req.wLength, length); err = usbd_do_request(udev, NULL, &req, buf); if (err) { DPRINTF("error=%s\n", usbd_errstr(err)); return (1); } return (0); } static int uplcom_pl2303_init(struct usb_device *udev, uint8_t chiptype) { int err; if (chiptype == TYPE_PL2303HXN) { /* PL2303HXN doesn't need this initialization sequence */ return (0); } if (uplcom_pl2303_do(udev, UT_READ_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x8484, 0, 1) || uplcom_pl2303_do(udev, UT_WRITE_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x0404, 0, 0) || uplcom_pl2303_do(udev, UT_READ_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x8484, 0, 1) || uplcom_pl2303_do(udev, UT_READ_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x8383, 0, 1) || uplcom_pl2303_do(udev, UT_READ_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x8484, 0, 1) || uplcom_pl2303_do(udev, UT_WRITE_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x0404, 1, 0) || uplcom_pl2303_do(udev, UT_READ_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x8484, 0, 1) || uplcom_pl2303_do(udev, UT_READ_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0x8383, 0, 1) || uplcom_pl2303_do(udev, UT_WRITE_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 0, 1, 0) || uplcom_pl2303_do(udev, UT_WRITE_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 1, 0, 0)) return (EIO); if (chiptype != TYPE_PL2303) err = uplcom_pl2303_do(udev, UT_WRITE_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 2, 0x44, 0); else err = uplcom_pl2303_do(udev, UT_WRITE_VENDOR_DEVICE, UPLCOM_SET_REQUEST, 2, 0x24, 0); if (err) return (EIO); return (0); } static void uplcom_cfg_set_dtr(struct ucom_softc *ucom, uint8_t onoff) { struct uplcom_softc *sc = ucom->sc_parent; struct usb_device_request req; DPRINTF("onoff = %d\n", onoff); if (onoff) sc->sc_line |= UCDC_LINE_DTR; else sc->sc_line &= ~UCDC_LINE_DTR; req.bmRequestType = UT_WRITE_CLASS_INTERFACE; req.bRequest = UCDC_SET_CONTROL_LINE_STATE; USETW(req.wValue, sc->sc_line); req.wIndex[0] = sc->sc_data_iface_no; req.wIndex[1] = 0; USETW(req.wLength, 0); ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, &req, NULL, 0, 1000); } static void uplcom_cfg_set_rts(struct ucom_softc *ucom, uint8_t onoff) { struct uplcom_softc *sc = ucom->sc_parent; struct usb_device_request req; DPRINTF("onoff = %d\n", onoff); if (onoff) sc->sc_line |= UCDC_LINE_RTS; else sc->sc_line &= ~UCDC_LINE_RTS; req.bmRequestType = UT_WRITE_CLASS_INTERFACE; req.bRequest = UCDC_SET_CONTROL_LINE_STATE; USETW(req.wValue, sc->sc_line); req.wIndex[0] = sc->sc_data_iface_no; req.wIndex[1] = 0; USETW(req.wLength, 0); ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, &req, NULL, 0, 1000); } static void uplcom_cfg_set_break(struct ucom_softc *ucom, uint8_t onoff) { struct uplcom_softc *sc = ucom->sc_parent; struct usb_device_request req; uint16_t temp; DPRINTF("onoff = %d\n", onoff); temp = (onoff ? UCDC_BREAK_ON : UCDC_BREAK_OFF); req.bmRequestType = UT_WRITE_CLASS_INTERFACE; req.bRequest = UCDC_SEND_BREAK; USETW(req.wValue, temp); req.wIndex[0] = sc->sc_data_iface_no; req.wIndex[1] = 0; USETW(req.wLength, 0); ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, &req, NULL, 0, 1000); } /* * NOTE: These baud rates are officially supported, they can be written * directly into dwDTERate register. * * Free baudrate setting is not supported by the base PL2303, and on * other models it requires writing a divisor value to dwDTERate instead * of the raw baudrate. The formula for divisor calculation is not published * by the vendor, so it is speculative, though the official product homepage * refers to the Linux module source as a reference implementation. */ static const uint32_t uplcom_rates[] = { /* * Basic 'standard' speed rates, supported by all models * NOTE: 900 and 56000 actually works as well */ 75, 150, 300, 600, 900, 1200, 1800, 2400, 3600, 4800, 7200, 9600, 14400, 19200, 28800, 38400, 56000, 57600, 115200, /* * Advanced speed rates up to 6Mbs, supported by HX/TA and HXD/TB/EA/RA * NOTE: regardless of the spec, 256000 does not work */ 128000, 134400, 161280, 201600, 230400, 268800, 403200, 460800, 614400, 806400, 921600, 1228800, 2457600, 3000000, 6000000, /* * Advanced speed rates up to 12, supported by HXD/TB/EA/RA */ 12000000 }; #define N_UPLCOM_RATES nitems(uplcom_rates) static int uplcom_baud_supported(unsigned int speed) { int i; for (i = 0; i < N_UPLCOM_RATES; i++) { if (uplcom_rates[i] == speed) return 1; } return 0; } static int uplcom_pre_param(struct ucom_softc *ucom, struct termios *t) { struct uplcom_softc *sc = ucom->sc_parent; DPRINTF("\n"); /** * Check requested baud rate. * * The PL2303 can only set specific baud rates, up to 1228800 baud. * The PL2303HX can set any baud rate up to 6Mb. * The PL2303HX rev. D and PL2303HXN can set any baud rate up to 12Mb. * */ /* accept raw divisor data, if someone wants to do the math in user domain */ if (t->c_ospeed & 0x80000000) return 0; switch (sc->sc_chiptype) { case TYPE_PL2303HXN: if (t->c_ospeed <= 12000000) return (0); break; case TYPE_PL2303HXD: if (t->c_ospeed <= 12000000) return (0); break; case TYPE_PL2303HX: if (t->c_ospeed <= 6000000) return (0); break; default: if (uplcom_baud_supported(t->c_ospeed)) return (0); break; } DPRINTF("uplcom_param: bad baud rate (%d)\n", t->c_ospeed); return (EIO); } static unsigned int uplcom_encode_baud_rate_divisor(uint8_t *buf, unsigned int baud) { unsigned int baseline, mantissa, exponent; /* Determine the baud rate divisor. This algorithm is taken from Linux. */ /* * Apparently the formula is: * baudrate = baseline / (mantissa * 4^exponent) * where * mantissa = buf[8:0] * exponent = buf[11:9] */ if (baud == 0) baud = 1; baseline = 383385600; mantissa = baseline / baud; if (mantissa == 0) mantissa = 1; exponent = 0; while (mantissa >= 512) { if (exponent < 7) { mantissa >>= 2; /* divide by 4 */ exponent++; } else { /* Exponent is maxed. Trim mantissa and leave. This gives approx. 45.8 baud */ mantissa = 511; break; } } buf[3] = 0x80; buf[2] = 0; buf[1] = exponent << 1 | mantissa >> 8; buf[0] = mantissa & 0xff; /* Calculate and return the exact baud rate. */ baud = (baseline / mantissa) >> (exponent << 1); DPRINTF("real baud rate will be %u\n", baud); return baud; } static void uplcom_cfg_param(struct ucom_softc *ucom, struct termios *t) { struct uplcom_softc *sc = ucom->sc_parent; struct usb_cdc_line_state ls; struct usb_device_request req; DPRINTF("sc = %p\n", sc); memset(&ls, 0, sizeof(ls)); /* * NOTE: If unsupported baud rates are set directly, the PL2303* uses 9600 baud. */ if ((t->c_ospeed & 0x80000000) || uplcom_baud_supported(t->c_ospeed)) USETDW(ls.dwDTERate, t->c_ospeed); else t->c_ospeed = uplcom_encode_baud_rate_divisor((uint8_t*)&ls.dwDTERate, t->c_ospeed); if (t->c_cflag & CSTOPB) { if ((t->c_cflag & CSIZE) == CS5) { /* * NOTE: Comply with "real" UARTs / RS232: * use 1.5 instead of 2 stop bits with 5 data bits */ ls.bCharFormat = UCDC_STOP_BIT_1_5; } else { ls.bCharFormat = UCDC_STOP_BIT_2; } } else { ls.bCharFormat = UCDC_STOP_BIT_1; } if (t->c_cflag & PARENB) { if (t->c_cflag & PARODD) { ls.bParityType = UCDC_PARITY_ODD; } else { ls.bParityType = UCDC_PARITY_EVEN; } } else { ls.bParityType = UCDC_PARITY_NONE; } switch (t->c_cflag & CSIZE) { case CS5: ls.bDataBits = 5; break; case CS6: ls.bDataBits = 6; break; case CS7: ls.bDataBits = 7; break; case CS8: ls.bDataBits = 8; break; } DPRINTF("rate=0x%08x fmt=%d parity=%d bits=%d\n", UGETDW(ls.dwDTERate), ls.bCharFormat, ls.bParityType, ls.bDataBits); req.bmRequestType = UT_WRITE_CLASS_INTERFACE; req.bRequest = UCDC_SET_LINE_CODING; USETW(req.wValue, 0); req.wIndex[0] = sc->sc_data_iface_no; req.wIndex[1] = 0; USETW(req.wLength, UCDC_LINE_STATE_LENGTH); ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, &req, &ls, 0, 1000); if (t->c_cflag & CRTSCTS) { DPRINTF("crtscts = on\n"); req.bmRequestType = UT_WRITE_VENDOR_DEVICE; if (sc->sc_chiptype == TYPE_PL2303HXN) { req.bRequest = UPLCOM_SET_REQUEST_PL2303HXN; USETW(req.wValue, UPLCOM_CRTSCTS_REG_PL2303HXN); USETW(req.wIndex, UPLCOM_SET_CRTSCTS_PL2303HXN); } else { req.bRequest = UPLCOM_SET_REQUEST; USETW(req.wValue, 0); if (sc->sc_chiptype != TYPE_PL2303) USETW(req.wIndex, UPLCOM_SET_CRTSCTS_PL2303X); else USETW(req.wIndex, UPLCOM_SET_CRTSCTS); } USETW(req.wLength, 0); ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, &req, NULL, 0, 1000); } else { req.bmRequestType = UT_WRITE_VENDOR_DEVICE; if (sc->sc_chiptype == TYPE_PL2303HXN) { req.bRequest = UPLCOM_SET_REQUEST_PL2303HXN; USETW(req.wValue, UPLCOM_CRTSCTS_REG_PL2303HXN); USETW(req.wIndex, UPLCOM_CLEAR_CRTSCTS_PL2303HXN); } else { req.bRequest = UPLCOM_SET_REQUEST; USETW(req.wValue, 0); USETW(req.wIndex, 0); } USETW(req.wLength, 0); ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, &req, NULL, 0, 1000); } } static void uplcom_start_read(struct ucom_softc *ucom) { struct uplcom_softc *sc = ucom->sc_parent; /* start interrupt endpoint */ usbd_transfer_start(sc->sc_xfer[UPLCOM_INTR_DT_RD]); /* start read endpoint */ usbd_transfer_start(sc->sc_xfer[UPLCOM_BULK_DT_RD]); } static void uplcom_stop_read(struct ucom_softc *ucom) { struct uplcom_softc *sc = ucom->sc_parent; /* stop interrupt endpoint */ usbd_transfer_stop(sc->sc_xfer[UPLCOM_INTR_DT_RD]); /* stop read endpoint */ usbd_transfer_stop(sc->sc_xfer[UPLCOM_BULK_DT_RD]); } static void uplcom_start_write(struct ucom_softc *ucom) { struct uplcom_softc *sc = ucom->sc_parent; usbd_transfer_start(sc->sc_xfer[UPLCOM_BULK_DT_WR]); } static void uplcom_stop_write(struct ucom_softc *ucom) { struct uplcom_softc *sc = ucom->sc_parent; usbd_transfer_stop(sc->sc_xfer[UPLCOM_BULK_DT_WR]); } static void uplcom_cfg_get_status(struct ucom_softc *ucom, uint8_t *lsr, uint8_t *msr) { struct uplcom_softc *sc = ucom->sc_parent; DPRINTF("\n"); *lsr = sc->sc_lsr; *msr = sc->sc_msr; } static void uplcom_intr_callback(struct usb_xfer *xfer, usb_error_t error) { struct uplcom_softc *sc = usbd_xfer_softc(xfer); struct usb_page_cache *pc; uint8_t buf[9]; int actlen; usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: DPRINTF("actlen = %u\n", actlen); if (actlen >= 9) { pc = usbd_xfer_get_frame(xfer, 0); usbd_copy_out(pc, 0, buf, sizeof(buf)); DPRINTF("status = 0x%02x\n", buf[UPLCOM_STATE_INDEX]); sc->sc_lsr = 0; sc->sc_msr = 0; if (buf[UPLCOM_STATE_INDEX] & RSAQ_STATUS_CTS) { sc->sc_msr |= SER_CTS; } if (buf[UPLCOM_STATE_INDEX] & RSAQ_STATUS_OVERRUN_ERROR) { sc->sc_lsr |= ULSR_OE; } if (buf[UPLCOM_STATE_INDEX] & RSAQ_STATUS_PARITY_ERROR) { sc->sc_lsr |= ULSR_PE; } if (buf[UPLCOM_STATE_INDEX] & RSAQ_STATUS_FRAME_ERROR) { sc->sc_lsr |= ULSR_FE; } if (buf[UPLCOM_STATE_INDEX] & RSAQ_STATUS_RING) { sc->sc_msr |= SER_RI; } if (buf[UPLCOM_STATE_INDEX] & RSAQ_STATUS_BREAK_ERROR) { sc->sc_lsr |= ULSR_BI; } if (buf[UPLCOM_STATE_INDEX] & RSAQ_STATUS_DSR) { sc->sc_msr |= SER_DSR; } if (buf[UPLCOM_STATE_INDEX] & RSAQ_STATUS_DCD) { sc->sc_msr |= SER_DCD; } ucom_status_change(&sc->sc_ucom); } case USB_ST_SETUP: tr_setup: usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); usbd_transfer_submit(xfer); return; default: /* Error */ if (error != USB_ERR_CANCELLED) { /* try to clear stall first */ usbd_xfer_set_stall(xfer); goto tr_setup; } return; } } static void uplcom_write_callback(struct usb_xfer *xfer, usb_error_t error) { struct uplcom_softc *sc = usbd_xfer_softc(xfer); struct usb_page_cache *pc; uint32_t actlen; switch (USB_GET_STATE(xfer)) { case USB_ST_SETUP: case USB_ST_TRANSFERRED: tr_setup: + if (usbd_xfer_get_and_clr_zlp(xfer)) + break; + pc = usbd_xfer_get_frame(xfer, 0); if (ucom_get_data(&sc->sc_ucom, pc, 0, UPLCOM_BULK_BUF_SIZE, &actlen)) { DPRINTF("actlen = %d\n", actlen); usbd_xfer_set_frame_len(xfer, 0, actlen); usbd_transfer_submit(xfer); } - return; + break; default: /* Error */ if (error != USB_ERR_CANCELLED) { /* try to clear stall first */ usbd_xfer_set_stall(xfer); goto tr_setup; } - return; + break; } } static void uplcom_read_callback(struct usb_xfer *xfer, usb_error_t error) { struct uplcom_softc *sc = usbd_xfer_softc(xfer); struct usb_page_cache *pc; int actlen; usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: pc = usbd_xfer_get_frame(xfer, 0); ucom_put_data(&sc->sc_ucom, pc, 0, actlen); case USB_ST_SETUP: tr_setup: usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); usbd_transfer_submit(xfer); return; default: /* Error */ if (error != USB_ERR_CANCELLED) { /* try to clear stall first */ usbd_xfer_set_stall(xfer); goto tr_setup; } return; } } static void uplcom_poll(struct ucom_softc *ucom) { struct uplcom_softc *sc = ucom->sc_parent; usbd_transfer_poll(sc->sc_xfer, UPLCOM_N_TRANSFER); } diff --git a/sys/dev/usb/serial/uslcom.c b/sys/dev/usb/serial/uslcom.c index fec18d2a5631..8c40a2c2fc3a 100644 --- a/sys/dev/usb/serial/uslcom.c +++ b/sys/dev/usb/serial/uslcom.c @@ -1,964 +1,966 @@ /* $OpenBSD: uslcom.c,v 1.17 2007/11/24 10:52:12 jsg Exp $ */ #include __FBSDID("$FreeBSD$"); /* * Copyright (c) 2006 Jonathan Gray * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Driver for Silicon Laboratories CP2101/CP2102/CP2103/CP2104/CP2105 * USB-Serial adapters. Based on datasheet AN571, publicly available from * http://www.silabs.com/Support%20Documents/TechnicalDocs/AN571.pdf */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "usbdevs.h" #define USB_DEBUG_VAR uslcom_debug #include #include #include #ifdef USB_DEBUG static int uslcom_debug = 0; static SYSCTL_NODE(_hw_usb, OID_AUTO, uslcom, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, "USB uslcom"); SYSCTL_INT(_hw_usb_uslcom, OID_AUTO, debug, CTLFLAG_RWTUN, &uslcom_debug, 0, "Debug level"); #endif #define USLCOM_BULK_BUF_SIZE 1024 #define USLCOM_CONFIG_INDEX 0 /* Request types */ #define USLCOM_WRITE 0x41 #define USLCOM_READ 0xc1 /* Request codes */ #define USLCOM_IFC_ENABLE 0x00 #define USLCOM_SET_BAUDDIV 0x01 #define USLCOM_SET_LINE_CTL 0x03 #define USLCOM_SET_BREAK 0x05 #define USLCOM_SET_MHS 0x07 #define USLCOM_GET_MDMSTS 0x08 #define USLCOM_SET_FLOW 0x13 #define USLCOM_SET_BAUDRATE 0x1e #define USLCOM_VENDOR_SPECIFIC 0xff /* USLCOM_IFC_ENABLE values */ #define USLCOM_IFC_ENABLE_DIS 0x00 #define USLCOM_IFC_ENABLE_EN 0x01 /* USLCOM_SET_MHS/USLCOM_GET_MDMSTS values */ #define USLCOM_MHS_DTR_ON 0x0001 #define USLCOM_MHS_DTR_SET 0x0100 #define USLCOM_MHS_RTS_ON 0x0002 #define USLCOM_MHS_RTS_SET 0x0200 #define USLCOM_MHS_CTS 0x0010 #define USLCOM_MHS_DSR 0x0020 #define USLCOM_MHS_RI 0x0040 #define USLCOM_MHS_DCD 0x0080 /* USLCOM_SET_BAUDDIV values */ #define USLCOM_BAUDDIV_REF 3686400 /* 3.6864 MHz */ /* USLCOM_SET_LINE_CTL values */ #define USLCOM_STOP_BITS_1 0x00 #define USLCOM_STOP_BITS_2 0x02 #define USLCOM_PARITY_NONE 0x00 #define USLCOM_PARITY_ODD 0x10 #define USLCOM_PARITY_EVEN 0x20 #define USLCOM_SET_DATA_BITS(x) ((x) << 8) /* USLCOM_SET_BREAK values */ #define USLCOM_SET_BREAK_OFF 0x00 #define USLCOM_SET_BREAK_ON 0x01 /* USLCOM_SET_FLOW values - 1st word */ #define USLCOM_FLOW_DTR_ON 0x00000001 /* DTR static active */ #define USLCOM_FLOW_CTS_HS 0x00000008 /* CTS handshake */ /* USLCOM_SET_FLOW values - 2nd word */ #define USLCOM_FLOW_RTS_ON 0x00000040 /* RTS static active */ #define USLCOM_FLOW_RTS_HS 0x00000080 /* RTS handshake */ /* USLCOM_VENDOR_SPECIFIC values */ #define USLCOM_GET_PARTNUM 0x370B #define USLCOM_WRITE_LATCH 0x37E1 #define USLCOM_READ_LATCH 0x00C2 /* USLCOM_GET_PARTNUM values from hardware */ #define USLCOM_PARTNUM_CP2101 1 #define USLCOM_PARTNUM_CP2102 2 #define USLCOM_PARTNUM_CP2103 3 #define USLCOM_PARTNUM_CP2104 4 #define USLCOM_PARTNUM_CP2105 5 enum { USLCOM_BULK_DT_WR, USLCOM_BULK_DT_RD, USLCOM_CTRL_DT_RD, USLCOM_N_TRANSFER, }; struct uslcom_softc { struct ucom_super_softc sc_super_ucom; struct ucom_softc sc_ucom; struct usb_callout sc_watchdog; struct usb_xfer *sc_xfer[USLCOM_N_TRANSFER]; struct usb_device *sc_udev; struct mtx sc_mtx; uint8_t sc_msr; uint8_t sc_lsr; uint8_t sc_iface_no; uint8_t sc_partnum; }; static device_probe_t uslcom_probe; static device_attach_t uslcom_attach; static device_detach_t uslcom_detach; static void uslcom_free_softc(struct uslcom_softc *); static usb_callback_t uslcom_write_callback; static usb_callback_t uslcom_read_callback; static usb_callback_t uslcom_control_callback; static void uslcom_free(struct ucom_softc *); static uint8_t uslcom_get_partnum(struct uslcom_softc *); static void uslcom_cfg_open(struct ucom_softc *); static void uslcom_cfg_close(struct ucom_softc *); static void uslcom_cfg_set_dtr(struct ucom_softc *, uint8_t); static void uslcom_cfg_set_rts(struct ucom_softc *, uint8_t); static void uslcom_cfg_set_break(struct ucom_softc *, uint8_t); static void uslcom_cfg_param(struct ucom_softc *, struct termios *); static void uslcom_cfg_get_status(struct ucom_softc *, uint8_t *, uint8_t *); static int uslcom_ioctl(struct ucom_softc *, uint32_t, caddr_t, int, struct thread *); static int uslcom_pre_param(struct ucom_softc *, struct termios *); static void uslcom_start_read(struct ucom_softc *); static void uslcom_stop_read(struct ucom_softc *); static void uslcom_start_write(struct ucom_softc *); static void uslcom_stop_write(struct ucom_softc *); static void uslcom_poll(struct ucom_softc *ucom); static const struct usb_config uslcom_config[USLCOM_N_TRANSFER] = { [USLCOM_BULK_DT_WR] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, .bufsize = USLCOM_BULK_BUF_SIZE, .flags = {.pipe_bof = 1,}, .callback = &uslcom_write_callback, }, [USLCOM_BULK_DT_RD] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, .bufsize = USLCOM_BULK_BUF_SIZE, .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, .callback = &uslcom_read_callback, }, [USLCOM_CTRL_DT_RD] = { .type = UE_CONTROL, .endpoint = 0x00, .direction = UE_DIR_ANY, .bufsize = sizeof(struct usb_device_request) + 8, .flags = {.pipe_bof = 1,}, .callback = &uslcom_control_callback, .timeout = 1000, /* 1 second timeout */ }, }; static struct ucom_callback uslcom_callback = { .ucom_cfg_get_status = &uslcom_cfg_get_status, .ucom_cfg_set_dtr = &uslcom_cfg_set_dtr, .ucom_cfg_set_rts = &uslcom_cfg_set_rts, .ucom_cfg_set_break = &uslcom_cfg_set_break, .ucom_cfg_open = &uslcom_cfg_open, .ucom_cfg_close = &uslcom_cfg_close, .ucom_cfg_param = &uslcom_cfg_param, .ucom_pre_param = &uslcom_pre_param, .ucom_ioctl = &uslcom_ioctl, .ucom_start_read = &uslcom_start_read, .ucom_stop_read = &uslcom_stop_read, .ucom_start_write = &uslcom_start_write, .ucom_stop_write = &uslcom_stop_write, .ucom_poll = &uslcom_poll, .ucom_free = &uslcom_free, }; static const STRUCT_USB_HOST_ID uslcom_devs[] = { #define USLCOM_DEV(v,p) { USB_VP(USB_VENDOR_##v, USB_PRODUCT_##v##_##p) } USLCOM_DEV(BALTECH, CARDREADER), USLCOM_DEV(CLIPSAL, 5000CT2), USLCOM_DEV(CLIPSAL, 5500PACA), USLCOM_DEV(CLIPSAL, 5500PCU), USLCOM_DEV(CLIPSAL, 560884), USLCOM_DEV(CLIPSAL, 5800PC), USLCOM_DEV(CLIPSAL, C5000CT2), USLCOM_DEV(CLIPSAL, L51xx), USLCOM_DEV(DATAAPEX, MULTICOM), USLCOM_DEV(DELL, DW700), USLCOM_DEV(DIGIANSWER, ZIGBEE802154), USLCOM_DEV(DYNASTREAM, ANTDEVBOARD), USLCOM_DEV(DYNASTREAM, ANTDEVBOARD2), USLCOM_DEV(DYNASTREAM, ANT2USB), USLCOM_DEV(ELV, USBI2C), USLCOM_DEV(FESTO, CMSP), USLCOM_DEV(FESTO, CPX_USB), USLCOM_DEV(FOXCONN, PIRELLI_DP_L10), USLCOM_DEV(FOXCONN, TCOM_TC_300), USLCOM_DEV(GEMALTO, PROXPU), USLCOM_DEV(JABLOTRON, PC60B), USLCOM_DEV(KAMSTRUP, OPTICALEYE), USLCOM_DEV(KAMSTRUP, MBUS_250D), USLCOM_DEV(LAKESHORE, 121), USLCOM_DEV(LAKESHORE, 218A), USLCOM_DEV(LAKESHORE, 219), USLCOM_DEV(LAKESHORE, 233), USLCOM_DEV(LAKESHORE, 235), USLCOM_DEV(LAKESHORE, 335), USLCOM_DEV(LAKESHORE, 336), USLCOM_DEV(LAKESHORE, 350), USLCOM_DEV(LAKESHORE, 371), USLCOM_DEV(LAKESHORE, 411), USLCOM_DEV(LAKESHORE, 425), USLCOM_DEV(LAKESHORE, 455A), USLCOM_DEV(LAKESHORE, 465), USLCOM_DEV(LAKESHORE, 475A), USLCOM_DEV(LAKESHORE, 625A), USLCOM_DEV(LAKESHORE, 642A), USLCOM_DEV(LAKESHORE, 648), USLCOM_DEV(LAKESHORE, 737), USLCOM_DEV(LAKESHORE, 776), USLCOM_DEV(LINKINSTRUMENTS, MSO19), USLCOM_DEV(LINKINSTRUMENTS, MSO28), USLCOM_DEV(LINKINSTRUMENTS, MSO28_2), USLCOM_DEV(MEI, CASHFLOW_SC), USLCOM_DEV(MEI, S2000), USLCOM_DEV(NETGEAR, M4100), USLCOM_DEV(OWEN, AC4), USLCOM_DEV(OWL, CM_160), USLCOM_DEV(PHILIPS, ACE1001), USLCOM_DEV(PLX, CA42), USLCOM_DEV(RENESAS, RX610), USLCOM_DEV(SEL, C662), USLCOM_DEV(SILABS, AC_SERV_CAN), USLCOM_DEV(SILABS, AC_SERV_CIS), USLCOM_DEV(SILABS, AC_SERV_IBUS), USLCOM_DEV(SILABS, AC_SERV_OBD), USLCOM_DEV(SILABS, AEROCOMM), USLCOM_DEV(SILABS, AMBER_AMB2560), USLCOM_DEV(SILABS, ARGUSISP), USLCOM_DEV(SILABS, ARKHAM_DS101_A), USLCOM_DEV(SILABS, ARKHAM_DS101_M), USLCOM_DEV(SILABS, ARYGON_MIFARE), USLCOM_DEV(SILABS, AVIT_USB_TTL), USLCOM_DEV(SILABS, B_G_H3000), USLCOM_DEV(SILABS, BALLUFF_RFID), USLCOM_DEV(SILABS, BEI_VCP), USLCOM_DEV(SILABS, BSM7DUSB), USLCOM_DEV(SILABS, BURNSIDE), USLCOM_DEV(SILABS, C2_EDGE_MODEM), USLCOM_DEV(SILABS, CP2102), USLCOM_DEV(SILABS, CP210X_2), USLCOM_DEV(SILABS, CP210X_3), USLCOM_DEV(SILABS, CP210X_4), USLCOM_DEV(SILABS, CRUMB128), USLCOM_DEV(SILABS, CYGNAL), USLCOM_DEV(SILABS, CYGNAL_DEBUG), USLCOM_DEV(SILABS, CYGNAL_GPS), USLCOM_DEV(SILABS, DEGREE), USLCOM_DEV(SILABS, DEKTEK_DTAPLUS), USLCOM_DEV(SILABS, EMS_C1007), USLCOM_DEV(SILABS, HAMLINKUSB), USLCOM_DEV(SILABS, HELICOM), USLCOM_DEV(SILABS, HUBZ), USLCOM_DEV(SILABS, BV_AV2010_10), USLCOM_DEV(SILABS, IMS_USB_RS422), USLCOM_DEV(SILABS, INFINITY_MIC), USLCOM_DEV(SILABS, INGENI_ZIGBEE), USLCOM_DEV(SILABS, INSYS_MODEM), USLCOM_DEV(SILABS, IRZ_SG10), USLCOM_DEV(SILABS, KYOCERA_GPS), USLCOM_DEV(SILABS, LIPOWSKY_HARP), USLCOM_DEV(SILABS, LIPOWSKY_JTAG), USLCOM_DEV(SILABS, LIPOWSKY_LIN), USLCOM_DEV(SILABS, MC35PU), USLCOM_DEV(SILABS, MMB_ZIGBEE), USLCOM_DEV(SILABS, MJS_TOSLINK), USLCOM_DEV(SILABS, MSD_DASHHAWK), USLCOM_DEV(SILABS, MULTIPLEX_RC), USLCOM_DEV(SILABS, OPTRIS_MSPRO), USLCOM_DEV(SILABS, POLOLU), USLCOM_DEV(SILABS, PROCYON_AVS), USLCOM_DEV(SILABS, SB_PARAMOUNT_ME), USLCOM_DEV(SILABS, SUUNTO), USLCOM_DEV(SILABS, TAMSMASTER), USLCOM_DEV(SILABS, TELEGESIS_ETRX2), USLCOM_DEV(SILABS, TRACIENT), USLCOM_DEV(SILABS, TRAQMATE), USLCOM_DEV(SILABS, USBCOUNT50), USLCOM_DEV(SILABS, USBPULSE100), USLCOM_DEV(SILABS, USBSCOPE50), USLCOM_DEV(SILABS, USBWAVE12), USLCOM_DEV(SILABS, V_PREON32), USLCOM_DEV(SILABS, VSTABI), USLCOM_DEV(SILABS, WAVIT), USLCOM_DEV(SILABS, WMRBATT), USLCOM_DEV(SILABS, WMRRIGBLASTER), USLCOM_DEV(SILABS, WMRRIGTALK), USLCOM_DEV(SILABS, ZEPHYR_BIO), USLCOM_DEV(SILABS2, DCU11CLONE), USLCOM_DEV(SILABS3, GPRS_MODEM), USLCOM_DEV(SILABS4, 100EU_MODEM), USLCOM_DEV(SYNTECH, CYPHERLAB100), USLCOM_DEV(USI, MC60), USLCOM_DEV(VAISALA, CABLE), USLCOM_DEV(WAGO, SERVICECABLE), USLCOM_DEV(WAVESENSE, JAZZ), USLCOM_DEV(WESTMOUNTAIN, RIGBLASTER_ADVANTAGE), USLCOM_DEV(WIENERPLEINBAUS, PL512), USLCOM_DEV(WIENERPLEINBAUS, RCM), USLCOM_DEV(WIENERPLEINBAUS, MPOD), USLCOM_DEV(WIENERPLEINBAUS, CML), #undef USLCOM_DEV }; static device_method_t uslcom_methods[] = { DEVMETHOD(device_probe, uslcom_probe), DEVMETHOD(device_attach, uslcom_attach), DEVMETHOD(device_detach, uslcom_detach), DEVMETHOD_END }; static devclass_t uslcom_devclass; static driver_t uslcom_driver = { .name = "uslcom", .methods = uslcom_methods, .size = sizeof(struct uslcom_softc), }; DRIVER_MODULE(uslcom, uhub, uslcom_driver, uslcom_devclass, NULL, 0); MODULE_DEPEND(uslcom, ucom, 1, 1, 1); MODULE_DEPEND(uslcom, usb, 1, 1, 1); MODULE_VERSION(uslcom, 1); USB_PNP_HOST_INFO(uslcom_devs); static void uslcom_watchdog(void *arg) { struct uslcom_softc *sc = arg; mtx_assert(&sc->sc_mtx, MA_OWNED); usbd_transfer_start(sc->sc_xfer[USLCOM_CTRL_DT_RD]); usb_callout_reset(&sc->sc_watchdog, hz / 4, &uslcom_watchdog, sc); } static int uslcom_probe(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); DPRINTFN(11, "\n"); if (uaa->usb_mode != USB_MODE_HOST) { return (ENXIO); } if (uaa->info.bConfigIndex != USLCOM_CONFIG_INDEX) { return (ENXIO); } return (usbd_lookup_id_by_uaa(uslcom_devs, sizeof(uslcom_devs), uaa)); } static int uslcom_attach(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); struct uslcom_softc *sc = device_get_softc(dev); int error; DPRINTFN(11, "\n"); device_set_usb_desc(dev); mtx_init(&sc->sc_mtx, "uslcom", NULL, MTX_DEF); ucom_ref(&sc->sc_super_ucom); usb_callout_init_mtx(&sc->sc_watchdog, &sc->sc_mtx, 0); sc->sc_udev = uaa->device; /* use the interface number from the USB interface descriptor */ sc->sc_iface_no = uaa->info.bIfaceNum; error = usbd_transfer_setup(uaa->device, &uaa->info.bIfaceIndex, sc->sc_xfer, uslcom_config, USLCOM_N_TRANSFER, sc, &sc->sc_mtx); if (error) { DPRINTF("one or more missing USB endpoints, " "error=%s\n", usbd_errstr(error)); goto detach; } /* clear stall at first run */ mtx_lock(&sc->sc_mtx); - usbd_xfer_set_stall(sc->sc_xfer[USLCOM_BULK_DT_WR]); - usbd_xfer_set_stall(sc->sc_xfer[USLCOM_BULK_DT_RD]); + usbd_xfer_set_zlp(sc->sc_xfer[USLCOM_BULK_DT_WR]); mtx_unlock(&sc->sc_mtx); sc->sc_partnum = uslcom_get_partnum(sc); error = ucom_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc, &uslcom_callback, &sc->sc_mtx); if (error) { goto detach; } ucom_set_pnpinfo_usb(&sc->sc_super_ucom, dev); return (0); detach: uslcom_detach(dev); return (ENXIO); } static int uslcom_detach(device_t dev) { struct uslcom_softc *sc = device_get_softc(dev); DPRINTF("sc=%p\n", sc); ucom_detach(&sc->sc_super_ucom, &sc->sc_ucom); usbd_transfer_unsetup(sc->sc_xfer, USLCOM_N_TRANSFER); usb_callout_drain(&sc->sc_watchdog); device_claim_softc(dev); uslcom_free_softc(sc); return (0); } UCOM_UNLOAD_DRAIN(uslcom); static void uslcom_free_softc(struct uslcom_softc *sc) { if (ucom_unref(&sc->sc_super_ucom)) { mtx_destroy(&sc->sc_mtx); device_free_softc(sc); } } static void uslcom_free(struct ucom_softc *ucom) { uslcom_free_softc(ucom->sc_parent); } static void uslcom_cfg_open(struct ucom_softc *ucom) { struct uslcom_softc *sc = ucom->sc_parent; struct usb_device_request req; req.bmRequestType = USLCOM_WRITE; req.bRequest = USLCOM_IFC_ENABLE; USETW(req.wValue, USLCOM_IFC_ENABLE_EN); USETW(req.wIndex, sc->sc_iface_no); USETW(req.wLength, 0); if (ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, &req, NULL, 0, 1000)) { DPRINTF("UART enable failed (ignored)\n"); } /* start polling status */ uslcom_watchdog(sc); } static void uslcom_cfg_close(struct ucom_softc *ucom) { struct uslcom_softc *sc = ucom->sc_parent; struct usb_device_request req; /* stop polling status */ usb_callout_stop(&sc->sc_watchdog); req.bmRequestType = USLCOM_WRITE; req.bRequest = USLCOM_IFC_ENABLE; USETW(req.wValue, USLCOM_IFC_ENABLE_DIS); USETW(req.wIndex, sc->sc_iface_no); USETW(req.wLength, 0); if (ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, &req, NULL, 0, 1000)) { DPRINTF("UART disable failed (ignored)\n"); } } static uint8_t uslcom_get_partnum(struct uslcom_softc *sc) { struct usb_device_request req; uint8_t partnum; /* Find specific chip type */ partnum = 0; req.bmRequestType = USLCOM_READ; req.bRequest = USLCOM_VENDOR_SPECIFIC; USETW(req.wValue, USLCOM_GET_PARTNUM); USETW(req.wIndex, sc->sc_iface_no); USETW(req.wLength, sizeof(partnum)); if (usbd_do_request_flags(sc->sc_udev, NULL, &req, &partnum, 0, NULL, 1000)) { DPRINTF("GET_PARTNUM failed\n"); } return(partnum); } static void uslcom_cfg_set_dtr(struct ucom_softc *ucom, uint8_t onoff) { struct uslcom_softc *sc = ucom->sc_parent; struct usb_device_request req; uint16_t ctl; DPRINTF("onoff = %d\n", onoff); ctl = onoff ? USLCOM_MHS_DTR_ON : 0; ctl |= USLCOM_MHS_DTR_SET; req.bmRequestType = USLCOM_WRITE; req.bRequest = USLCOM_SET_MHS; USETW(req.wValue, ctl); USETW(req.wIndex, sc->sc_iface_no); USETW(req.wLength, 0); if (ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, &req, NULL, 0, 1000)) { DPRINTF("Setting DTR failed (ignored)\n"); } } static void uslcom_cfg_set_rts(struct ucom_softc *ucom, uint8_t onoff) { struct uslcom_softc *sc = ucom->sc_parent; struct usb_device_request req; uint16_t ctl; DPRINTF("onoff = %d\n", onoff); ctl = onoff ? USLCOM_MHS_RTS_ON : 0; ctl |= USLCOM_MHS_RTS_SET; req.bmRequestType = USLCOM_WRITE; req.bRequest = USLCOM_SET_MHS; USETW(req.wValue, ctl); USETW(req.wIndex, sc->sc_iface_no); USETW(req.wLength, 0); if (ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, &req, NULL, 0, 1000)) { DPRINTF("Setting DTR failed (ignored)\n"); } } static int uslcom_pre_param(struct ucom_softc *ucom, struct termios *t) { struct uslcom_softc *sc = ucom->sc_parent; uint32_t maxspeed; switch (sc->sc_partnum) { case USLCOM_PARTNUM_CP2104: case USLCOM_PARTNUM_CP2105: maxspeed = 2000000; break; case USLCOM_PARTNUM_CP2101: case USLCOM_PARTNUM_CP2102: case USLCOM_PARTNUM_CP2103: default: /* * Datasheet for cp2102 says 921600 max. Testing shows that * 1228800 and 1843200 work fine. */ maxspeed = 1843200; break; } if (t->c_ospeed <= 0 || t->c_ospeed > maxspeed) return (EINVAL); return (0); } static void uslcom_cfg_param(struct ucom_softc *ucom, struct termios *t) { struct uslcom_softc *sc = ucom->sc_parent; struct usb_device_request req; uint32_t baudrate, flowctrl[4]; uint16_t data; DPRINTF("\n"); baudrate = t->c_ospeed; req.bmRequestType = USLCOM_WRITE; req.bRequest = USLCOM_SET_BAUDRATE; USETW(req.wValue, 0); USETW(req.wIndex, sc->sc_iface_no); USETW(req.wLength, sizeof(baudrate)); if (ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, &req, &baudrate, 0, 1000)) { printf("Set baudrate failed (ignored)\n"); } if (t->c_cflag & CSTOPB) data = USLCOM_STOP_BITS_2; else data = USLCOM_STOP_BITS_1; if (t->c_cflag & PARENB) { if (t->c_cflag & PARODD) data |= USLCOM_PARITY_ODD; else data |= USLCOM_PARITY_EVEN; } else data |= USLCOM_PARITY_NONE; switch (t->c_cflag & CSIZE) { case CS5: data |= USLCOM_SET_DATA_BITS(5); break; case CS6: data |= USLCOM_SET_DATA_BITS(6); break; case CS7: data |= USLCOM_SET_DATA_BITS(7); break; case CS8: data |= USLCOM_SET_DATA_BITS(8); break; } req.bmRequestType = USLCOM_WRITE; req.bRequest = USLCOM_SET_LINE_CTL; USETW(req.wValue, data); USETW(req.wIndex, sc->sc_iface_no); USETW(req.wLength, 0); if (ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, &req, NULL, 0, 1000)) { DPRINTF("Set format failed (ignored)\n"); } if (t->c_cflag & CRTSCTS) { flowctrl[0] = htole32(USLCOM_FLOW_DTR_ON | USLCOM_FLOW_CTS_HS); flowctrl[1] = htole32(USLCOM_FLOW_RTS_HS); } else { flowctrl[0] = htole32(USLCOM_FLOW_DTR_ON); flowctrl[1] = htole32(USLCOM_FLOW_RTS_ON); } flowctrl[2] = 0; flowctrl[3] = 0; req.bmRequestType = USLCOM_WRITE; req.bRequest = USLCOM_SET_FLOW; USETW(req.wValue, 0); USETW(req.wIndex, sc->sc_iface_no); USETW(req.wLength, sizeof(flowctrl)); if (ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, &req, flowctrl, 0, 1000)) { DPRINTF("Set flowcontrol failed (ignored)\n"); } } static void uslcom_cfg_get_status(struct ucom_softc *ucom, uint8_t *lsr, uint8_t *msr) { struct uslcom_softc *sc = ucom->sc_parent; DPRINTF("\n"); /* XXX Note: sc_lsr is always zero */ *lsr = sc->sc_lsr; *msr = sc->sc_msr; } static void uslcom_cfg_set_break(struct ucom_softc *ucom, uint8_t onoff) { struct uslcom_softc *sc = ucom->sc_parent; struct usb_device_request req; uint16_t brk = onoff ? USLCOM_SET_BREAK_ON : USLCOM_SET_BREAK_OFF; req.bmRequestType = USLCOM_WRITE; req.bRequest = USLCOM_SET_BREAK; USETW(req.wValue, brk); USETW(req.wIndex, sc->sc_iface_no); USETW(req.wLength, 0); if (ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, &req, NULL, 0, 1000)) { DPRINTF("Set BREAK failed (ignored)\n"); } } static int uslcom_ioctl(struct ucom_softc *ucom, uint32_t cmd, caddr_t data, int flag, struct thread *td) { struct uslcom_softc *sc = ucom->sc_parent; struct usb_device_request req; int error = 0; uint8_t latch; DPRINTF("cmd=0x%08x\n", cmd); switch (cmd) { case USB_GET_GPIO: if (sc->sc_partnum < USLCOM_PARTNUM_CP2103) { error = ENODEV; break; } req.bmRequestType = USLCOM_READ; req.bRequest = USLCOM_VENDOR_SPECIFIC; USETW(req.wValue, USLCOM_READ_LATCH); USETW(req.wIndex, sc->sc_iface_no); USETW(req.wLength, sizeof(latch)); if (ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, &req, &latch, 0, 1000)) { DPRINTF("Get LATCH failed\n"); error = EIO; } *(int *)data = latch; break; case USB_SET_GPIO: if (sc->sc_partnum < USLCOM_PARTNUM_CP2103) error = ENODEV; else if ((sc->sc_partnum == USLCOM_PARTNUM_CP2103) || (sc->sc_partnum == USLCOM_PARTNUM_CP2104)) { req.bmRequestType = USLCOM_WRITE; req.bRequest = USLCOM_VENDOR_SPECIFIC; USETW(req.wValue, USLCOM_WRITE_LATCH); USETW(req.wIndex, (*(int *)data)); USETW(req.wLength, 0); if (ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, &req, NULL, 0, 1000)) { DPRINTF("Set LATCH failed\n"); error = EIO; } } else error = ENODEV; /* Not yet */ break; default: DPRINTF("Unknown IOCTL\n"); error = ENOIOCTL; break; } return (error); } static void uslcom_write_callback(struct usb_xfer *xfer, usb_error_t error) { struct uslcom_softc *sc = usbd_xfer_softc(xfer); struct usb_page_cache *pc; uint32_t actlen; switch (USB_GET_STATE(xfer)) { case USB_ST_SETUP: case USB_ST_TRANSFERRED: tr_setup: + if (usbd_xfer_get_and_clr_zlp(xfer)) + break; + pc = usbd_xfer_get_frame(xfer, 0); if (ucom_get_data(&sc->sc_ucom, pc, 0, USLCOM_BULK_BUF_SIZE, &actlen)) { DPRINTF("actlen = %d\n", actlen); usbd_xfer_set_frame_len(xfer, 0, actlen); usbd_transfer_submit(xfer); } - return; + break; default: /* Error */ if (error != USB_ERR_CANCELLED) { /* try to clear stall first */ usbd_xfer_set_stall(xfer); goto tr_setup; } - return; + break; } } static void uslcom_read_callback(struct usb_xfer *xfer, usb_error_t error) { struct uslcom_softc *sc = usbd_xfer_softc(xfer); struct usb_page_cache *pc; int actlen; usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: pc = usbd_xfer_get_frame(xfer, 0); ucom_put_data(&sc->sc_ucom, pc, 0, actlen); case USB_ST_SETUP: tr_setup: usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); usbd_transfer_submit(xfer); return; default: /* Error */ if (error != USB_ERR_CANCELLED) { /* try to clear stall first */ usbd_xfer_set_stall(xfer); goto tr_setup; } return; } } static void uslcom_control_callback(struct usb_xfer *xfer, usb_error_t error) { struct uslcom_softc *sc = usbd_xfer_softc(xfer); struct usb_page_cache *pc; struct usb_device_request req; uint8_t msr = 0; uint8_t buf; switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: pc = usbd_xfer_get_frame(xfer, 1); usbd_copy_out(pc, 0, &buf, sizeof(buf)); if (buf & USLCOM_MHS_CTS) msr |= SER_CTS; if (buf & USLCOM_MHS_DSR) msr |= SER_DSR; if (buf & USLCOM_MHS_RI) msr |= SER_RI; if (buf & USLCOM_MHS_DCD) msr |= SER_DCD; if (msr != sc->sc_msr) { DPRINTF("status change msr=0x%02x " "(was 0x%02x)\n", msr, sc->sc_msr); sc->sc_msr = msr; ucom_status_change(&sc->sc_ucom); } break; case USB_ST_SETUP: req.bmRequestType = USLCOM_READ; req.bRequest = USLCOM_GET_MDMSTS; USETW(req.wValue, 0); USETW(req.wIndex, sc->sc_iface_no); USETW(req.wLength, sizeof(buf)); usbd_xfer_set_frames(xfer, 2); usbd_xfer_set_frame_len(xfer, 0, sizeof(req)); usbd_xfer_set_frame_len(xfer, 1, sizeof(buf)); pc = usbd_xfer_get_frame(xfer, 0); usbd_copy_in(pc, 0, &req, sizeof(req)); usbd_transfer_submit(xfer); break; default: /* error */ if (error != USB_ERR_CANCELLED) DPRINTF("error=%s\n", usbd_errstr(error)); break; } } static void uslcom_start_read(struct ucom_softc *ucom) { struct uslcom_softc *sc = ucom->sc_parent; /* start read endpoint */ usbd_transfer_start(sc->sc_xfer[USLCOM_BULK_DT_RD]); } static void uslcom_stop_read(struct ucom_softc *ucom) { struct uslcom_softc *sc = ucom->sc_parent; /* stop read endpoint */ usbd_transfer_stop(sc->sc_xfer[USLCOM_BULK_DT_RD]); } static void uslcom_start_write(struct ucom_softc *ucom) { struct uslcom_softc *sc = ucom->sc_parent; usbd_transfer_start(sc->sc_xfer[USLCOM_BULK_DT_WR]); } static void uslcom_stop_write(struct ucom_softc *ucom) { struct uslcom_softc *sc = ucom->sc_parent; usbd_transfer_stop(sc->sc_xfer[USLCOM_BULK_DT_WR]); } static void uslcom_poll(struct ucom_softc *ucom) { struct uslcom_softc *sc = ucom->sc_parent; usbd_transfer_poll(sc->sc_xfer, USLCOM_N_TRANSFER); } diff --git a/sys/dev/usb/serial/uvscom.c b/sys/dev/usb/serial/uvscom.c index 8bb69e4402cf..81fd5334a243 100644 --- a/sys/dev/usb/serial/uvscom.c +++ b/sys/dev/usb/serial/uvscom.c @@ -1,771 +1,773 @@ /* $NetBSD: usb/uvscom.c,v 1.1 2002/03/19 15:08:42 augustss Exp $ */ #include __FBSDID("$FreeBSD$"); /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2001-2003, 2005 Shunsuke Akiyama . * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 THE AUTHOR OR CONTRIBUTORS 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. * */ /* * uvscom: SUNTAC Slipper U VS-10U driver. * Slipper U is a PC Card to USB converter for data communication card * adapter. It supports DDI Pocket's Air H" C@rd, C@rd H" 64, NTT's P-in, * P-in m@ater and various data communication card adapters. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "usbdevs.h" #define USB_DEBUG_VAR uvscom_debug #include #include #include #ifdef USB_DEBUG static int uvscom_debug = 0; static SYSCTL_NODE(_hw_usb, OID_AUTO, uvscom, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, "USB uvscom"); SYSCTL_INT(_hw_usb_uvscom, OID_AUTO, debug, CTLFLAG_RWTUN, &uvscom_debug, 0, "Debug level"); #endif #define UVSCOM_MODVER 1 /* module version */ #define UVSCOM_CONFIG_INDEX 0 #define UVSCOM_IFACE_INDEX 0 /* Request */ #define UVSCOM_SET_SPEED 0x10 #define UVSCOM_LINE_CTL 0x11 #define UVSCOM_SET_PARAM 0x12 #define UVSCOM_READ_STATUS 0xd0 #define UVSCOM_SHUTDOWN 0xe0 /* UVSCOM_SET_SPEED parameters */ #define UVSCOM_SPEED_150BPS 0x00 #define UVSCOM_SPEED_300BPS 0x01 #define UVSCOM_SPEED_600BPS 0x02 #define UVSCOM_SPEED_1200BPS 0x03 #define UVSCOM_SPEED_2400BPS 0x04 #define UVSCOM_SPEED_4800BPS 0x05 #define UVSCOM_SPEED_9600BPS 0x06 #define UVSCOM_SPEED_19200BPS 0x07 #define UVSCOM_SPEED_38400BPS 0x08 #define UVSCOM_SPEED_57600BPS 0x09 #define UVSCOM_SPEED_115200BPS 0x0a /* UVSCOM_LINE_CTL parameters */ #define UVSCOM_BREAK 0x40 #define UVSCOM_RTS 0x02 #define UVSCOM_DTR 0x01 #define UVSCOM_LINE_INIT 0x08 /* UVSCOM_SET_PARAM parameters */ #define UVSCOM_DATA_MASK 0x03 #define UVSCOM_DATA_BIT_8 0x03 #define UVSCOM_DATA_BIT_7 0x02 #define UVSCOM_DATA_BIT_6 0x01 #define UVSCOM_DATA_BIT_5 0x00 #define UVSCOM_STOP_MASK 0x04 #define UVSCOM_STOP_BIT_2 0x04 #define UVSCOM_STOP_BIT_1 0x00 #define UVSCOM_PARITY_MASK 0x18 #define UVSCOM_PARITY_EVEN 0x18 #define UVSCOM_PARITY_ODD 0x08 #define UVSCOM_PARITY_NONE 0x00 /* Status bits */ #define UVSCOM_TXRDY 0x04 #define UVSCOM_RXRDY 0x01 #define UVSCOM_DCD 0x08 #define UVSCOM_NOCARD 0x04 #define UVSCOM_DSR 0x02 #define UVSCOM_CTS 0x01 #define UVSCOM_USTAT_MASK (UVSCOM_NOCARD | UVSCOM_DSR | UVSCOM_CTS) #define UVSCOM_BULK_BUF_SIZE 1024 /* bytes */ enum { UVSCOM_BULK_DT_WR, UVSCOM_BULK_DT_RD, UVSCOM_INTR_DT_RD, UVSCOM_N_TRANSFER, }; struct uvscom_softc { struct ucom_super_softc sc_super_ucom; struct ucom_softc sc_ucom; struct usb_xfer *sc_xfer[UVSCOM_N_TRANSFER]; struct usb_device *sc_udev; struct mtx sc_mtx; uint16_t sc_line; /* line control register */ uint8_t sc_iface_no; /* interface number */ uint8_t sc_iface_index; /* interface index */ uint8_t sc_lsr; /* local status register */ uint8_t sc_msr; /* uvscom status register */ uint8_t sc_unit_status; /* unit status */ }; /* prototypes */ static device_probe_t uvscom_probe; static device_attach_t uvscom_attach; static device_detach_t uvscom_detach; static void uvscom_free_softc(struct uvscom_softc *); static usb_callback_t uvscom_write_callback; static usb_callback_t uvscom_read_callback; static usb_callback_t uvscom_intr_callback; static void uvscom_free(struct ucom_softc *); static void uvscom_cfg_set_dtr(struct ucom_softc *, uint8_t); static void uvscom_cfg_set_rts(struct ucom_softc *, uint8_t); static void uvscom_cfg_set_break(struct ucom_softc *, uint8_t); static int uvscom_pre_param(struct ucom_softc *, struct termios *); static void uvscom_cfg_param(struct ucom_softc *, struct termios *); static int uvscom_pre_open(struct ucom_softc *); static void uvscom_cfg_open(struct ucom_softc *); static void uvscom_cfg_close(struct ucom_softc *); static void uvscom_start_read(struct ucom_softc *); static void uvscom_stop_read(struct ucom_softc *); static void uvscom_start_write(struct ucom_softc *); static void uvscom_stop_write(struct ucom_softc *); static void uvscom_cfg_get_status(struct ucom_softc *, uint8_t *, uint8_t *); static void uvscom_cfg_write(struct uvscom_softc *, uint8_t, uint16_t); static uint16_t uvscom_cfg_read_status(struct uvscom_softc *); static void uvscom_poll(struct ucom_softc *ucom); static const struct usb_config uvscom_config[UVSCOM_N_TRANSFER] = { [UVSCOM_BULK_DT_WR] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_OUT, .bufsize = UVSCOM_BULK_BUF_SIZE, .flags = {.pipe_bof = 1,.force_short_xfer = 1,}, .callback = &uvscom_write_callback, }, [UVSCOM_BULK_DT_RD] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, .bufsize = UVSCOM_BULK_BUF_SIZE, .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, .callback = &uvscom_read_callback, }, [UVSCOM_INTR_DT_RD] = { .type = UE_INTERRUPT, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, .bufsize = 0, /* use wMaxPacketSize */ .callback = &uvscom_intr_callback, }, }; static const struct ucom_callback uvscom_callback = { .ucom_cfg_get_status = &uvscom_cfg_get_status, .ucom_cfg_set_dtr = &uvscom_cfg_set_dtr, .ucom_cfg_set_rts = &uvscom_cfg_set_rts, .ucom_cfg_set_break = &uvscom_cfg_set_break, .ucom_cfg_param = &uvscom_cfg_param, .ucom_cfg_open = &uvscom_cfg_open, .ucom_cfg_close = &uvscom_cfg_close, .ucom_pre_open = &uvscom_pre_open, .ucom_pre_param = &uvscom_pre_param, .ucom_start_read = &uvscom_start_read, .ucom_stop_read = &uvscom_stop_read, .ucom_start_write = &uvscom_start_write, .ucom_stop_write = &uvscom_stop_write, .ucom_poll = &uvscom_poll, .ucom_free = &uvscom_free, }; static const STRUCT_USB_HOST_ID uvscom_devs[] = { /* SUNTAC U-Cable type A4 */ {USB_VPI(USB_VENDOR_SUNTAC, USB_PRODUCT_SUNTAC_AS144L4, 0)}, /* SUNTAC U-Cable type D2 */ {USB_VPI(USB_VENDOR_SUNTAC, USB_PRODUCT_SUNTAC_DS96L, 0)}, /* SUNTAC Ir-Trinity */ {USB_VPI(USB_VENDOR_SUNTAC, USB_PRODUCT_SUNTAC_IS96U, 0)}, /* SUNTAC U-Cable type P1 */ {USB_VPI(USB_VENDOR_SUNTAC, USB_PRODUCT_SUNTAC_PS64P1, 0)}, /* SUNTAC Slipper U */ {USB_VPI(USB_VENDOR_SUNTAC, USB_PRODUCT_SUNTAC_VS10U, 0)}, }; static device_method_t uvscom_methods[] = { DEVMETHOD(device_probe, uvscom_probe), DEVMETHOD(device_attach, uvscom_attach), DEVMETHOD(device_detach, uvscom_detach), DEVMETHOD_END }; static devclass_t uvscom_devclass; static driver_t uvscom_driver = { .name = "uvscom", .methods = uvscom_methods, .size = sizeof(struct uvscom_softc), }; DRIVER_MODULE(uvscom, uhub, uvscom_driver, uvscom_devclass, NULL, 0); MODULE_DEPEND(uvscom, ucom, 1, 1, 1); MODULE_DEPEND(uvscom, usb, 1, 1, 1); MODULE_VERSION(uvscom, UVSCOM_MODVER); USB_PNP_HOST_INFO(uvscom_devs); static int uvscom_probe(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); if (uaa->usb_mode != USB_MODE_HOST) { return (ENXIO); } if (uaa->info.bConfigIndex != UVSCOM_CONFIG_INDEX) { return (ENXIO); } if (uaa->info.bIfaceIndex != UVSCOM_IFACE_INDEX) { return (ENXIO); } return (usbd_lookup_id_by_uaa(uvscom_devs, sizeof(uvscom_devs), uaa)); } static int uvscom_attach(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); struct uvscom_softc *sc = device_get_softc(dev); int error; device_set_usb_desc(dev); mtx_init(&sc->sc_mtx, "uvscom", NULL, MTX_DEF); ucom_ref(&sc->sc_super_ucom); sc->sc_udev = uaa->device; DPRINTF("sc=%p\n", sc); sc->sc_iface_no = uaa->info.bIfaceNum; sc->sc_iface_index = UVSCOM_IFACE_INDEX; error = usbd_transfer_setup(uaa->device, &sc->sc_iface_index, sc->sc_xfer, uvscom_config, UVSCOM_N_TRANSFER, sc, &sc->sc_mtx); if (error) { DPRINTF("could not allocate all USB transfers!\n"); goto detach; } sc->sc_line = UVSCOM_LINE_INIT; /* clear stall at first run */ mtx_lock(&sc->sc_mtx); - usbd_xfer_set_stall(sc->sc_xfer[UVSCOM_BULK_DT_WR]); - usbd_xfer_set_stall(sc->sc_xfer[UVSCOM_BULK_DT_RD]); + usbd_xfer_set_zlp(sc->sc_xfer[UVSCOM_BULK_DT_WR]); mtx_unlock(&sc->sc_mtx); error = ucom_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc, &uvscom_callback, &sc->sc_mtx); if (error) { goto detach; } ucom_set_pnpinfo_usb(&sc->sc_super_ucom, dev); /* start interrupt pipe */ mtx_lock(&sc->sc_mtx); usbd_transfer_start(sc->sc_xfer[UVSCOM_INTR_DT_RD]); mtx_unlock(&sc->sc_mtx); return (0); detach: uvscom_detach(dev); return (ENXIO); } static int uvscom_detach(device_t dev) { struct uvscom_softc *sc = device_get_softc(dev); DPRINTF("sc=%p\n", sc); /* stop interrupt pipe */ if (sc->sc_xfer[UVSCOM_INTR_DT_RD]) usbd_transfer_stop(sc->sc_xfer[UVSCOM_INTR_DT_RD]); ucom_detach(&sc->sc_super_ucom, &sc->sc_ucom); usbd_transfer_unsetup(sc->sc_xfer, UVSCOM_N_TRANSFER); device_claim_softc(dev); uvscom_free_softc(sc); return (0); } UCOM_UNLOAD_DRAIN(uvscom); static void uvscom_free_softc(struct uvscom_softc *sc) { if (ucom_unref(&sc->sc_super_ucom)) { mtx_destroy(&sc->sc_mtx); device_free_softc(sc); } } static void uvscom_free(struct ucom_softc *ucom) { uvscom_free_softc(ucom->sc_parent); } static void uvscom_write_callback(struct usb_xfer *xfer, usb_error_t error) { struct uvscom_softc *sc = usbd_xfer_softc(xfer); struct usb_page_cache *pc; uint32_t actlen; switch (USB_GET_STATE(xfer)) { case USB_ST_SETUP: case USB_ST_TRANSFERRED: tr_setup: + if (usbd_xfer_get_and_clr_zlp(xfer)) + break; + pc = usbd_xfer_get_frame(xfer, 0); if (ucom_get_data(&sc->sc_ucom, pc, 0, UVSCOM_BULK_BUF_SIZE, &actlen)) { usbd_xfer_set_frame_len(xfer, 0, actlen); usbd_transfer_submit(xfer); } - return; + break; default: /* Error */ if (error != USB_ERR_CANCELLED) { /* try to clear stall first */ usbd_xfer_set_stall(xfer); goto tr_setup; } - return; + break; } } static void uvscom_read_callback(struct usb_xfer *xfer, usb_error_t error) { struct uvscom_softc *sc = usbd_xfer_softc(xfer); struct usb_page_cache *pc; int actlen; usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: pc = usbd_xfer_get_frame(xfer, 0); ucom_put_data(&sc->sc_ucom, pc, 0, actlen); case USB_ST_SETUP: tr_setup: usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); usbd_transfer_submit(xfer); return; default: /* Error */ if (error != USB_ERR_CANCELLED) { /* try to clear stall first */ usbd_xfer_set_stall(xfer); goto tr_setup; } return; } } static void uvscom_intr_callback(struct usb_xfer *xfer, usb_error_t error) { struct uvscom_softc *sc = usbd_xfer_softc(xfer); struct usb_page_cache *pc; uint8_t buf[2]; int actlen; usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: if (actlen >= 2) { pc = usbd_xfer_get_frame(xfer, 0); usbd_copy_out(pc, 0, buf, sizeof(buf)); sc->sc_lsr = 0; sc->sc_msr = 0; sc->sc_unit_status = buf[1]; if (buf[0] & UVSCOM_TXRDY) { sc->sc_lsr |= ULSR_TXRDY; } if (buf[0] & UVSCOM_RXRDY) { sc->sc_lsr |= ULSR_RXRDY; } if (buf[1] & UVSCOM_CTS) { sc->sc_msr |= SER_CTS; } if (buf[1] & UVSCOM_DSR) { sc->sc_msr |= SER_DSR; } if (buf[1] & UVSCOM_DCD) { sc->sc_msr |= SER_DCD; } /* * the UCOM layer will ignore this call if the TTY * device is closed! */ ucom_status_change(&sc->sc_ucom); } case USB_ST_SETUP: tr_setup: usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); usbd_transfer_submit(xfer); return; default: /* Error */ if (error != USB_ERR_CANCELLED) { /* try to clear stall first */ usbd_xfer_set_stall(xfer); goto tr_setup; } return; } } static void uvscom_cfg_set_dtr(struct ucom_softc *ucom, uint8_t onoff) { struct uvscom_softc *sc = ucom->sc_parent; DPRINTF("onoff = %d\n", onoff); if (onoff) sc->sc_line |= UVSCOM_DTR; else sc->sc_line &= ~UVSCOM_DTR; uvscom_cfg_write(sc, UVSCOM_LINE_CTL, sc->sc_line); } static void uvscom_cfg_set_rts(struct ucom_softc *ucom, uint8_t onoff) { struct uvscom_softc *sc = ucom->sc_parent; DPRINTF("onoff = %d\n", onoff); if (onoff) sc->sc_line |= UVSCOM_RTS; else sc->sc_line &= ~UVSCOM_RTS; uvscom_cfg_write(sc, UVSCOM_LINE_CTL, sc->sc_line); } static void uvscom_cfg_set_break(struct ucom_softc *ucom, uint8_t onoff) { struct uvscom_softc *sc = ucom->sc_parent; DPRINTF("onoff = %d\n", onoff); if (onoff) sc->sc_line |= UVSCOM_BREAK; else sc->sc_line &= ~UVSCOM_BREAK; uvscom_cfg_write(sc, UVSCOM_LINE_CTL, sc->sc_line); } static int uvscom_pre_param(struct ucom_softc *ucom, struct termios *t) { switch (t->c_ospeed) { case B150: case B300: case B600: case B1200: case B2400: case B4800: case B9600: case B19200: case B38400: case B57600: case B115200: default: return (EINVAL); } return (0); } static void uvscom_cfg_param(struct ucom_softc *ucom, struct termios *t) { struct uvscom_softc *sc = ucom->sc_parent; uint16_t value; DPRINTF("\n"); switch (t->c_ospeed) { case B150: value = UVSCOM_SPEED_150BPS; break; case B300: value = UVSCOM_SPEED_300BPS; break; case B600: value = UVSCOM_SPEED_600BPS; break; case B1200: value = UVSCOM_SPEED_1200BPS; break; case B2400: value = UVSCOM_SPEED_2400BPS; break; case B4800: value = UVSCOM_SPEED_4800BPS; break; case B9600: value = UVSCOM_SPEED_9600BPS; break; case B19200: value = UVSCOM_SPEED_19200BPS; break; case B38400: value = UVSCOM_SPEED_38400BPS; break; case B57600: value = UVSCOM_SPEED_57600BPS; break; case B115200: value = UVSCOM_SPEED_115200BPS; break; default: return; } uvscom_cfg_write(sc, UVSCOM_SET_SPEED, value); value = 0; if (t->c_cflag & CSTOPB) { value |= UVSCOM_STOP_BIT_2; } if (t->c_cflag & PARENB) { if (t->c_cflag & PARODD) { value |= UVSCOM_PARITY_ODD; } else { value |= UVSCOM_PARITY_EVEN; } } else { value |= UVSCOM_PARITY_NONE; } switch (t->c_cflag & CSIZE) { case CS5: value |= UVSCOM_DATA_BIT_5; break; case CS6: value |= UVSCOM_DATA_BIT_6; break; case CS7: value |= UVSCOM_DATA_BIT_7; break; default: case CS8: value |= UVSCOM_DATA_BIT_8; break; } uvscom_cfg_write(sc, UVSCOM_SET_PARAM, value); } static int uvscom_pre_open(struct ucom_softc *ucom) { struct uvscom_softc *sc = ucom->sc_parent; DPRINTF("sc = %p\n", sc); /* check if PC card was inserted */ if (sc->sc_unit_status & UVSCOM_NOCARD) { DPRINTF("no PC card!\n"); return (ENXIO); } return (0); } static void uvscom_cfg_open(struct ucom_softc *ucom) { struct uvscom_softc *sc = ucom->sc_parent; DPRINTF("sc = %p\n", sc); uvscom_cfg_read_status(sc); } static void uvscom_cfg_close(struct ucom_softc *ucom) { struct uvscom_softc *sc = ucom->sc_parent; DPRINTF("sc=%p\n", sc); uvscom_cfg_write(sc, UVSCOM_SHUTDOWN, 0); } static void uvscom_start_read(struct ucom_softc *ucom) { struct uvscom_softc *sc = ucom->sc_parent; usbd_transfer_start(sc->sc_xfer[UVSCOM_BULK_DT_RD]); } static void uvscom_stop_read(struct ucom_softc *ucom) { struct uvscom_softc *sc = ucom->sc_parent; usbd_transfer_stop(sc->sc_xfer[UVSCOM_BULK_DT_RD]); } static void uvscom_start_write(struct ucom_softc *ucom) { struct uvscom_softc *sc = ucom->sc_parent; usbd_transfer_start(sc->sc_xfer[UVSCOM_BULK_DT_WR]); } static void uvscom_stop_write(struct ucom_softc *ucom) { struct uvscom_softc *sc = ucom->sc_parent; usbd_transfer_stop(sc->sc_xfer[UVSCOM_BULK_DT_WR]); } static void uvscom_cfg_get_status(struct ucom_softc *ucom, uint8_t *lsr, uint8_t *msr) { struct uvscom_softc *sc = ucom->sc_parent; *lsr = sc->sc_lsr; *msr = sc->sc_msr; } static void uvscom_cfg_write(struct uvscom_softc *sc, uint8_t index, uint16_t value) { struct usb_device_request req; usb_error_t err; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = index; USETW(req.wValue, value); USETW(req.wIndex, 0); USETW(req.wLength, 0); err = ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, &req, NULL, 0, 1000); if (err) { DPRINTFN(0, "device request failed, err=%s " "(ignored)\n", usbd_errstr(err)); } } static uint16_t uvscom_cfg_read_status(struct uvscom_softc *sc) { struct usb_device_request req; usb_error_t err; uint8_t data[2]; req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = UVSCOM_READ_STATUS; USETW(req.wValue, 0); USETW(req.wIndex, 0); USETW(req.wLength, 2); err = ucom_cfg_do_request(sc->sc_udev, &sc->sc_ucom, &req, data, 0, 1000); if (err) { DPRINTFN(0, "device request failed, err=%s " "(ignored)\n", usbd_errstr(err)); } return (data[0] | (data[1] << 8)); } static void uvscom_poll(struct ucom_softc *ucom) { struct uvscom_softc *sc = ucom->sc_parent; usbd_transfer_poll(sc->sc_xfer, UVSCOM_N_TRANSFER); } diff --git a/sys/dev/usb/usb_transfer.c b/sys/dev/usb/usb_transfer.c index 436e08db14a1..e1582926b08d 100644 --- a/sys/dev/usb/usb_transfer.c +++ b/sys/dev/usb/usb_transfer.c @@ -1,3698 +1,3751 @@ /* $FreeBSD$ */ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * - * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. + * Copyright (c) 2008-2021 Hans Petter Selasky. 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 THE AUTHOR OR CONTRIBUTORS 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. */ #ifdef USB_GLOBAL_INCLUDE_FILE #include USB_GLOBAL_INCLUDE_FILE #else #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define USB_DEBUG_VAR usb_debug #include #include #include #include #include #include #include #include #include #include #endif /* USB_GLOBAL_INCLUDE_FILE */ struct usb_std_packet_size { struct { uint16_t min; /* inclusive */ uint16_t max; /* inclusive */ } range; uint16_t fixed[4]; }; static usb_callback_t usb_request_callback; static const struct usb_config usb_control_ep_cfg[USB_CTRL_XFER_MAX] = { /* This transfer is used for generic control endpoint transfers */ [0] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control endpoint */ .direction = UE_DIR_ANY, .bufsize = USB_EP0_BUFSIZE, /* bytes */ .flags = {.proxy_buffer = 1,}, .callback = &usb_request_callback, .usb_mode = USB_MODE_DUAL, /* both modes */ }, /* This transfer is used for generic clear stall only */ [1] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = UE_DIR_ANY, .bufsize = sizeof(struct usb_device_request), .callback = &usb_do_clear_stall_callback, .timeout = 1000, /* 1 second */ .interval = 50, /* 50ms */ .usb_mode = USB_MODE_HOST, }, }; static const struct usb_config usb_control_ep_quirk_cfg[USB_CTRL_XFER_MAX] = { /* This transfer is used for generic control endpoint transfers */ [0] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control endpoint */ .direction = UE_DIR_ANY, .bufsize = 65535, /* bytes */ .callback = &usb_request_callback, .usb_mode = USB_MODE_DUAL, /* both modes */ }, /* This transfer is used for generic clear stall only */ [1] = { .type = UE_CONTROL, .endpoint = 0x00, /* Control pipe */ .direction = UE_DIR_ANY, .bufsize = sizeof(struct usb_device_request), .callback = &usb_do_clear_stall_callback, .timeout = 1000, /* 1 second */ .interval = 50, /* 50ms */ .usb_mode = USB_MODE_HOST, }, }; /* function prototypes */ static void usbd_update_max_frame_size(struct usb_xfer *); static void usbd_transfer_unsetup_sub(struct usb_xfer_root *, uint8_t); static void usbd_control_transfer_init(struct usb_xfer *); static int usbd_setup_ctrl_transfer(struct usb_xfer *); static void usb_callback_proc(struct usb_proc_msg *); static void usbd_callback_ss_done_defer(struct usb_xfer *); static void usbd_callback_wrapper(struct usb_xfer_queue *); static void usbd_transfer_start_cb(void *); static uint8_t usbd_callback_wrapper_sub(struct usb_xfer *); static void usbd_get_std_packet_size(struct usb_std_packet_size *ptr, uint8_t type, enum usb_dev_speed speed); /*------------------------------------------------------------------------* * usb_request_callback *------------------------------------------------------------------------*/ static void usb_request_callback(struct usb_xfer *xfer, usb_error_t error) { if (xfer->flags_int.usb_mode == USB_MODE_DEVICE) usb_handle_request_callback(xfer, error); else usbd_do_request_callback(xfer, error); } /*------------------------------------------------------------------------* * usbd_update_max_frame_size * * This function updates the maximum frame size, hence high speed USB * can transfer multiple consecutive packets. *------------------------------------------------------------------------*/ static void usbd_update_max_frame_size(struct usb_xfer *xfer) { /* compute maximum frame size */ /* this computation should not overflow 16-bit */ /* max = 15 * 1024 */ xfer->max_frame_size = xfer->max_packet_size * xfer->max_packet_count; } /*------------------------------------------------------------------------* * usbd_get_dma_delay * * The following function is called when we need to * synchronize with DMA hardware. * * Returns: * 0: no DMA delay required * Else: milliseconds of DMA delay *------------------------------------------------------------------------*/ usb_timeout_t usbd_get_dma_delay(struct usb_device *udev) { const struct usb_bus_methods *mtod; uint32_t temp; mtod = udev->bus->methods; temp = 0; if (mtod->get_dma_delay) { (mtod->get_dma_delay) (udev, &temp); /* * Round up and convert to milliseconds. Note that we use * 1024 milliseconds per second. to save a division. */ temp += 0x3FF; temp /= 0x400; } return (temp); } /*------------------------------------------------------------------------* * usbd_transfer_setup_sub_malloc * * This function will allocate one or more DMA'able memory chunks * according to "size", "align" and "count" arguments. "ppc" is * pointed to a linear array of USB page caches afterwards. * * If the "align" argument is equal to "1" a non-contiguous allocation * can happen. Else if the "align" argument is greater than "1", the * allocation will always be contiguous in memory. * * Returns: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ #if USB_HAVE_BUSDMA uint8_t usbd_transfer_setup_sub_malloc(struct usb_setup_params *parm, struct usb_page_cache **ppc, usb_size_t size, usb_size_t align, usb_size_t count) { struct usb_page_cache *pc; struct usb_page *pg; void *buf; usb_size_t n_dma_pc; usb_size_t n_dma_pg; usb_size_t n_obj; usb_size_t x; usb_size_t y; usb_size_t r; usb_size_t z; USB_ASSERT(align > 0, ("Invalid alignment, 0x%08x\n", align)); USB_ASSERT(size > 0, ("Invalid size = 0\n")); if (count == 0) { return (0); /* nothing to allocate */ } /* * Make sure that the size is aligned properly. */ size = -((-size) & (-align)); /* * Try multi-allocation chunks to reduce the number of DMA * allocations, hence DMA allocations are slow. */ if (align == 1) { /* special case - non-cached multi page DMA memory */ n_dma_pc = count; n_dma_pg = (2 + (size / USB_PAGE_SIZE)); n_obj = 1; } else if (size >= USB_PAGE_SIZE) { n_dma_pc = count; n_dma_pg = 1; n_obj = 1; } else { /* compute number of objects per page */ #ifdef USB_DMA_SINGLE_ALLOC n_obj = 1; #else n_obj = (USB_PAGE_SIZE / size); #endif /* * Compute number of DMA chunks, rounded up * to nearest one: */ n_dma_pc = howmany(count, n_obj); n_dma_pg = 1; } /* * DMA memory is allocated once, but mapped twice. That's why * there is one list for auto-free and another list for * non-auto-free which only holds the mapping and not the * allocation. */ if (parm->buf == NULL) { /* reserve memory (auto-free) */ parm->dma_page_ptr += n_dma_pc * n_dma_pg; parm->dma_page_cache_ptr += n_dma_pc; /* reserve memory (no-auto-free) */ parm->dma_page_ptr += count * n_dma_pg; parm->xfer_page_cache_ptr += count; return (0); } for (x = 0; x != n_dma_pc; x++) { /* need to initialize the page cache */ parm->dma_page_cache_ptr[x].tag_parent = &parm->curr_xfer->xroot->dma_parent_tag; } for (x = 0; x != count; x++) { /* need to initialize the page cache */ parm->xfer_page_cache_ptr[x].tag_parent = &parm->curr_xfer->xroot->dma_parent_tag; } if (ppc != NULL) { if (n_obj != 1) *ppc = parm->xfer_page_cache_ptr; else *ppc = parm->dma_page_cache_ptr; } r = count; /* set remainder count */ z = n_obj * size; /* set allocation size */ pc = parm->xfer_page_cache_ptr; pg = parm->dma_page_ptr; if (n_obj == 1) { /* * Avoid mapping memory twice if only a single object * should be allocated per page cache: */ for (x = 0; x != n_dma_pc; x++) { if (usb_pc_alloc_mem(parm->dma_page_cache_ptr, pg, z, align)) { return (1); /* failure */ } /* Make room for one DMA page cache and "n_dma_pg" pages */ parm->dma_page_cache_ptr++; pg += n_dma_pg; } } else { for (x = 0; x != n_dma_pc; x++) { if (r < n_obj) { /* compute last remainder */ z = r * size; n_obj = r; } if (usb_pc_alloc_mem(parm->dma_page_cache_ptr, pg, z, align)) { return (1); /* failure */ } /* Set beginning of current buffer */ buf = parm->dma_page_cache_ptr->buffer; /* Make room for one DMA page cache and "n_dma_pg" pages */ parm->dma_page_cache_ptr++; pg += n_dma_pg; for (y = 0; (y != n_obj); y++, r--, pc++, pg += n_dma_pg) { /* Load sub-chunk into DMA */ if (usb_pc_dmamap_create(pc, size)) { return (1); /* failure */ } pc->buffer = USB_ADD_BYTES(buf, y * size); pc->page_start = pg; USB_MTX_LOCK(pc->tag_parent->mtx); if (usb_pc_load_mem(pc, size, 1 /* synchronous */ )) { USB_MTX_UNLOCK(pc->tag_parent->mtx); return (1); /* failure */ } USB_MTX_UNLOCK(pc->tag_parent->mtx); } } } parm->xfer_page_cache_ptr = pc; parm->dma_page_ptr = pg; return (0); } #endif /*------------------------------------------------------------------------* * usbd_get_max_frame_length * * This function returns the maximum single frame length as computed by * usbd_transfer_setup(). It is useful when computing buffer sizes for * devices having multiple alternate settings. The SuperSpeed endpoint * companion pointer is allowed to be NULL. *------------------------------------------------------------------------*/ uint32_t usbd_get_max_frame_length(const struct usb_endpoint_descriptor *edesc, const struct usb_endpoint_ss_comp_descriptor *ecomp, enum usb_dev_speed speed) { uint32_t max_packet_size; uint32_t max_packet_count; uint8_t type; max_packet_size = UGETW(edesc->wMaxPacketSize); max_packet_count = 1; type = (edesc->bmAttributes & UE_XFERTYPE); switch (speed) { case USB_SPEED_HIGH: switch (type) { case UE_ISOCHRONOUS: case UE_INTERRUPT: max_packet_count += (max_packet_size >> 11) & 3; /* check for invalid max packet count */ if (max_packet_count > 3) max_packet_count = 3; break; default: break; } max_packet_size &= 0x7FF; break; case USB_SPEED_SUPER: max_packet_count += (max_packet_size >> 11) & 3; if (ecomp != NULL) max_packet_count += ecomp->bMaxBurst; if ((max_packet_count == 0) || (max_packet_count > 16)) max_packet_count = 16; switch (type) { case UE_CONTROL: max_packet_count = 1; break; case UE_ISOCHRONOUS: if (ecomp != NULL) { uint8_t mult; mult = UE_GET_SS_ISO_MULT( ecomp->bmAttributes) + 1; if (mult > 3) mult = 3; max_packet_count *= mult; } break; default: break; } max_packet_size &= 0x7FF; break; default: break; } return (max_packet_size * max_packet_count); } /*------------------------------------------------------------------------* * usbd_transfer_setup_sub - transfer setup subroutine * * This function must be called from the "xfer_setup" callback of the * USB Host or Device controller driver when setting up an USB * transfer. This function will setup correct packet sizes, buffer * sizes, flags and more, that are stored in the "usb_xfer" * structure. *------------------------------------------------------------------------*/ void usbd_transfer_setup_sub(struct usb_setup_params *parm) { enum { REQ_SIZE = 8, MIN_PKT = 8, }; struct usb_xfer *xfer = parm->curr_xfer; const struct usb_config *setup = parm->curr_setup; struct usb_endpoint_ss_comp_descriptor *ecomp; struct usb_endpoint_descriptor *edesc; struct usb_std_packet_size std_size; usb_frcount_t n_frlengths; usb_frcount_t n_frbuffers; usb_frcount_t x; uint16_t maxp_old; uint8_t type; uint8_t zmps; /* * Sanity check. The following parameters must be initialized before * calling this function. */ if ((parm->hc_max_packet_size == 0) || (parm->hc_max_packet_count == 0) || (parm->hc_max_frame_size == 0)) { parm->err = USB_ERR_INVAL; goto done; } edesc = xfer->endpoint->edesc; ecomp = xfer->endpoint->ecomp; type = (edesc->bmAttributes & UE_XFERTYPE); xfer->flags = setup->flags; xfer->nframes = setup->frames; xfer->timeout = setup->timeout; xfer->callback = setup->callback; xfer->interval = setup->interval; xfer->endpointno = edesc->bEndpointAddress; xfer->max_packet_size = UGETW(edesc->wMaxPacketSize); xfer->max_packet_count = 1; /* make a shadow copy: */ xfer->flags_int.usb_mode = parm->udev->flags.usb_mode; parm->bufsize = setup->bufsize; switch (parm->speed) { case USB_SPEED_HIGH: switch (type) { case UE_ISOCHRONOUS: case UE_INTERRUPT: xfer->max_packet_count += (xfer->max_packet_size >> 11) & 3; /* check for invalid max packet count */ if (xfer->max_packet_count > 3) xfer->max_packet_count = 3; break; default: break; } xfer->max_packet_size &= 0x7FF; break; case USB_SPEED_SUPER: xfer->max_packet_count += (xfer->max_packet_size >> 11) & 3; if (ecomp != NULL) xfer->max_packet_count += ecomp->bMaxBurst; if ((xfer->max_packet_count == 0) || (xfer->max_packet_count > 16)) xfer->max_packet_count = 16; switch (type) { case UE_CONTROL: xfer->max_packet_count = 1; break; case UE_ISOCHRONOUS: if (ecomp != NULL) { uint8_t mult; mult = UE_GET_SS_ISO_MULT( ecomp->bmAttributes) + 1; if (mult > 3) mult = 3; xfer->max_packet_count *= mult; } break; default: break; } xfer->max_packet_size &= 0x7FF; break; default: break; } /* range check "max_packet_count" */ if (xfer->max_packet_count > parm->hc_max_packet_count) { xfer->max_packet_count = parm->hc_max_packet_count; } /* store max packet size value before filtering */ maxp_old = xfer->max_packet_size; /* filter "wMaxPacketSize" according to HC capabilities */ if ((xfer->max_packet_size > parm->hc_max_packet_size) || (xfer->max_packet_size == 0)) { xfer->max_packet_size = parm->hc_max_packet_size; } /* filter "wMaxPacketSize" according to standard sizes */ usbd_get_std_packet_size(&std_size, type, parm->speed); if (std_size.range.min || std_size.range.max) { if (xfer->max_packet_size < std_size.range.min) { xfer->max_packet_size = std_size.range.min; } if (xfer->max_packet_size > std_size.range.max) { xfer->max_packet_size = std_size.range.max; } } else { if (xfer->max_packet_size >= std_size.fixed[3]) { xfer->max_packet_size = std_size.fixed[3]; } else if (xfer->max_packet_size >= std_size.fixed[2]) { xfer->max_packet_size = std_size.fixed[2]; } else if (xfer->max_packet_size >= std_size.fixed[1]) { xfer->max_packet_size = std_size.fixed[1]; } else { /* only one possibility left */ xfer->max_packet_size = std_size.fixed[0]; } } /* * Check if the max packet size was outside its allowed range * and clamped to a valid value: */ if (maxp_old != xfer->max_packet_size) xfer->flags_int.maxp_was_clamped = 1; /* compute "max_frame_size" */ usbd_update_max_frame_size(xfer); /* check interrupt interval and transfer pre-delay */ if (type == UE_ISOCHRONOUS) { uint16_t frame_limit; xfer->interval = 0; /* not used, must be zero */ xfer->flags_int.isochronous_xfr = 1; /* set flag */ if (xfer->timeout == 0) { /* * set a default timeout in * case something goes wrong! */ xfer->timeout = 1000 / 4; } switch (parm->speed) { case USB_SPEED_LOW: case USB_SPEED_FULL: frame_limit = USB_MAX_FS_ISOC_FRAMES_PER_XFER; xfer->fps_shift = 0; break; default: frame_limit = USB_MAX_HS_ISOC_FRAMES_PER_XFER; xfer->fps_shift = edesc->bInterval; if (xfer->fps_shift > 0) xfer->fps_shift--; if (xfer->fps_shift > 3) xfer->fps_shift = 3; if (xfer->flags.pre_scale_frames != 0) xfer->nframes <<= (3 - xfer->fps_shift); break; } if (xfer->nframes > frame_limit) { /* * this is not going to work * cross hardware */ parm->err = USB_ERR_INVAL; goto done; } if (xfer->nframes == 0) { /* * this is not a valid value */ parm->err = USB_ERR_ZERO_NFRAMES; goto done; } } else { /* * If a value is specified use that else check the * endpoint descriptor! */ if (type == UE_INTERRUPT) { uint32_t temp; if (xfer->interval == 0) { xfer->interval = edesc->bInterval; switch (parm->speed) { case USB_SPEED_LOW: case USB_SPEED_FULL: break; default: /* 125us -> 1ms */ if (xfer->interval < 4) xfer->interval = 1; else if (xfer->interval > 16) xfer->interval = (1 << (16 - 4)); else xfer->interval = (1 << (xfer->interval - 4)); break; } } if (xfer->interval == 0) { /* * One millisecond is the smallest * interval we support: */ xfer->interval = 1; } xfer->fps_shift = 0; temp = 1; while ((temp != 0) && (temp < xfer->interval)) { xfer->fps_shift++; temp *= 2; } switch (parm->speed) { case USB_SPEED_LOW: case USB_SPEED_FULL: break; default: xfer->fps_shift += 3; break; } } } /* * NOTE: we do not allow "max_packet_size" or "max_frame_size" * to be equal to zero when setting up USB transfers, hence * this leads to a lot of extra code in the USB kernel. */ if ((xfer->max_frame_size == 0) || (xfer->max_packet_size == 0)) { zmps = 1; if ((parm->bufsize <= MIN_PKT) && (type != UE_CONTROL) && (type != UE_BULK)) { /* workaround */ xfer->max_packet_size = MIN_PKT; xfer->max_packet_count = 1; parm->bufsize = 0; /* automatic setup length */ usbd_update_max_frame_size(xfer); } else { parm->err = USB_ERR_ZERO_MAXP; goto done; } } else { zmps = 0; } /* * check if we should setup a default * length: */ if (parm->bufsize == 0) { parm->bufsize = xfer->max_frame_size; if (type == UE_ISOCHRONOUS) { parm->bufsize *= xfer->nframes; } } /* * check if we are about to setup a proxy * type of buffer: */ if (xfer->flags.proxy_buffer) { /* round bufsize up */ parm->bufsize += (xfer->max_frame_size - 1); if (parm->bufsize < xfer->max_frame_size) { /* length wrapped around */ parm->err = USB_ERR_INVAL; goto done; } /* subtract remainder */ parm->bufsize -= (parm->bufsize % xfer->max_frame_size); /* add length of USB device request structure, if any */ if (type == UE_CONTROL) { parm->bufsize += REQ_SIZE; /* SETUP message */ } } xfer->max_data_length = parm->bufsize; /* Setup "n_frlengths" and "n_frbuffers" */ if (type == UE_ISOCHRONOUS) { n_frlengths = xfer->nframes; n_frbuffers = 1; } else { if (type == UE_CONTROL) { xfer->flags_int.control_xfr = 1; if (xfer->nframes == 0) { if (parm->bufsize <= REQ_SIZE) { /* * there will never be any data * stage */ xfer->nframes = 1; } else { xfer->nframes = 2; } } } else { if (xfer->nframes == 0) { xfer->nframes = 1; } } n_frlengths = xfer->nframes; n_frbuffers = xfer->nframes; } /* * check if we have room for the * USB device request structure: */ if (type == UE_CONTROL) { if (xfer->max_data_length < REQ_SIZE) { /* length wrapped around or too small bufsize */ parm->err = USB_ERR_INVAL; goto done; } xfer->max_data_length -= REQ_SIZE; } /* * Setup "frlengths" and shadow "frlengths" for keeping the * initial frame lengths when a USB transfer is complete. This * information is useful when computing isochronous offsets. */ xfer->frlengths = parm->xfer_length_ptr; parm->xfer_length_ptr += 2 * n_frlengths; /* setup "frbuffers" */ xfer->frbuffers = parm->xfer_page_cache_ptr; parm->xfer_page_cache_ptr += n_frbuffers; /* initialize max frame count */ xfer->max_frame_count = xfer->nframes; /* * check if we need to setup * a local buffer: */ if (!xfer->flags.ext_buffer) { #if USB_HAVE_BUSDMA struct usb_page_search page_info; struct usb_page_cache *pc; if (usbd_transfer_setup_sub_malloc(parm, &pc, parm->bufsize, 1, 1)) { parm->err = USB_ERR_NOMEM; } else if (parm->buf != NULL) { usbd_get_page(pc, 0, &page_info); xfer->local_buffer = page_info.buffer; usbd_xfer_set_frame_offset(xfer, 0, 0); if ((type == UE_CONTROL) && (n_frbuffers > 1)) { usbd_xfer_set_frame_offset(xfer, REQ_SIZE, 1); } } #else /* align data */ parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1)); if (parm->buf != NULL) { xfer->local_buffer = USB_ADD_BYTES(parm->buf, parm->size[0]); usbd_xfer_set_frame_offset(xfer, 0, 0); if ((type == UE_CONTROL) && (n_frbuffers > 1)) { usbd_xfer_set_frame_offset(xfer, REQ_SIZE, 1); } } parm->size[0] += parm->bufsize; /* align data again */ parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1)); #endif } /* * Compute maximum buffer size */ if (parm->bufsize_max < parm->bufsize) { parm->bufsize_max = parm->bufsize; } #if USB_HAVE_BUSDMA if (xfer->flags_int.bdma_enable) { /* * Setup "dma_page_ptr". * * Proof for formula below: * * Assume there are three USB frames having length "a", "b" and * "c". These USB frames will at maximum need "z" * "usb_page" structures. "z" is given by: * * z = ((a / USB_PAGE_SIZE) + 2) + ((b / USB_PAGE_SIZE) + 2) + * ((c / USB_PAGE_SIZE) + 2); * * Constraining "a", "b" and "c" like this: * * (a + b + c) <= parm->bufsize * * We know that: * * z <= ((parm->bufsize / USB_PAGE_SIZE) + (3*2)); * * Here is the general formula: */ xfer->dma_page_ptr = parm->dma_page_ptr; parm->dma_page_ptr += (2 * n_frbuffers); parm->dma_page_ptr += (parm->bufsize / USB_PAGE_SIZE); } #endif if (zmps) { /* correct maximum data length */ xfer->max_data_length = 0; } /* subtract USB frame remainder from "hc_max_frame_size" */ xfer->max_hc_frame_size = (parm->hc_max_frame_size - (parm->hc_max_frame_size % xfer->max_frame_size)); if (xfer->max_hc_frame_size == 0) { parm->err = USB_ERR_INVAL; goto done; } /* initialize frame buffers */ if (parm->buf) { for (x = 0; x != n_frbuffers; x++) { xfer->frbuffers[x].tag_parent = &xfer->xroot->dma_parent_tag; #if USB_HAVE_BUSDMA if (xfer->flags_int.bdma_enable && (parm->bufsize_max > 0)) { if (usb_pc_dmamap_create( xfer->frbuffers + x, parm->bufsize_max)) { parm->err = USB_ERR_NOMEM; goto done; } } #endif } } done: if (parm->err) { /* * Set some dummy values so that we avoid division by zero: */ xfer->max_hc_frame_size = 1; xfer->max_frame_size = 1; xfer->max_packet_size = 1; xfer->max_data_length = 0; xfer->nframes = 0; xfer->max_frame_count = 0; } } static uint8_t usbd_transfer_setup_has_bulk(const struct usb_config *setup_start, uint16_t n_setup) { while (n_setup--) { uint8_t type = setup_start[n_setup].type; if (type == UE_BULK || type == UE_BULK_INTR || type == UE_TYPE_ANY) return (1); } return (0); } /*------------------------------------------------------------------------* * usbd_transfer_setup - setup an array of USB transfers * * NOTE: You must always call "usbd_transfer_unsetup" after calling * "usbd_transfer_setup" if success was returned. * * The idea is that the USB device driver should pre-allocate all its * transfers by one call to this function. * * Return values: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ usb_error_t usbd_transfer_setup(struct usb_device *udev, const uint8_t *ifaces, struct usb_xfer **ppxfer, const struct usb_config *setup_start, uint16_t n_setup, void *priv_sc, struct mtx *xfer_mtx) { const struct usb_config *setup_end = setup_start + n_setup; const struct usb_config *setup; struct usb_setup_params *parm; struct usb_endpoint *ep; struct usb_xfer_root *info; struct usb_xfer *xfer; void *buf = NULL; usb_error_t error = 0; uint16_t n; uint16_t refcount; uint8_t do_unlock; WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, "usbd_transfer_setup can sleep!"); /* do some checking first */ if (n_setup == 0) { DPRINTFN(6, "setup array has zero length!\n"); return (USB_ERR_INVAL); } if (ifaces == NULL) { DPRINTFN(6, "ifaces array is NULL!\n"); return (USB_ERR_INVAL); } if (xfer_mtx == NULL) { DPRINTFN(6, "using global lock\n"); xfer_mtx = &Giant; } /* more sanity checks */ for (setup = setup_start, n = 0; setup != setup_end; setup++, n++) { if (setup->bufsize == (usb_frlength_t)-1) { error = USB_ERR_BAD_BUFSIZE; DPRINTF("invalid bufsize\n"); } if (setup->callback == NULL) { error = USB_ERR_NO_CALLBACK; DPRINTF("no callback\n"); } ppxfer[n] = NULL; } if (error) return (error); /* Protect scratch area */ do_unlock = usbd_ctrl_lock(udev); refcount = 0; info = NULL; parm = &udev->scratch.xfer_setup[0].parm; memset(parm, 0, sizeof(*parm)); parm->udev = udev; parm->speed = usbd_get_speed(udev); parm->hc_max_packet_count = 1; if (parm->speed >= USB_SPEED_MAX) { parm->err = USB_ERR_INVAL; goto done; } /* setup all transfers */ while (1) { if (buf) { /* * Initialize the "usb_xfer_root" structure, * which is common for all our USB transfers. */ info = USB_ADD_BYTES(buf, 0); info->memory_base = buf; info->memory_size = parm->size[0]; #if USB_HAVE_BUSDMA info->dma_page_cache_start = USB_ADD_BYTES(buf, parm->size[4]); info->dma_page_cache_end = USB_ADD_BYTES(buf, parm->size[5]); #endif info->xfer_page_cache_start = USB_ADD_BYTES(buf, parm->size[5]); info->xfer_page_cache_end = USB_ADD_BYTES(buf, parm->size[2]); cv_init(&info->cv_drain, "WDRAIN"); info->xfer_mtx = xfer_mtx; #if USB_HAVE_BUSDMA usb_dma_tag_setup(&info->dma_parent_tag, parm->dma_tag_p, udev->bus->dma_parent_tag[0].tag, xfer_mtx, &usb_bdma_done_event, udev->bus->dma_bits, parm->dma_tag_max); #endif info->bus = udev->bus; info->udev = udev; TAILQ_INIT(&info->done_q.head); info->done_q.command = &usbd_callback_wrapper; #if USB_HAVE_BUSDMA TAILQ_INIT(&info->dma_q.head); info->dma_q.command = &usb_bdma_work_loop; #endif info->done_m[0].hdr.pm_callback = &usb_callback_proc; info->done_m[0].xroot = info; info->done_m[1].hdr.pm_callback = &usb_callback_proc; info->done_m[1].xroot = info; /* * In device side mode control endpoint * requests need to run from a separate * context, else there is a chance of * deadlock! */ if (setup_start == usb_control_ep_cfg || setup_start == usb_control_ep_quirk_cfg) info->done_p = USB_BUS_CONTROL_XFER_PROC(udev->bus); else if (xfer_mtx == &Giant) info->done_p = USB_BUS_GIANT_PROC(udev->bus); else if (usbd_transfer_setup_has_bulk(setup_start, n_setup)) info->done_p = USB_BUS_NON_GIANT_BULK_PROC(udev->bus); else info->done_p = USB_BUS_NON_GIANT_ISOC_PROC(udev->bus); } /* reset sizes */ parm->size[0] = 0; parm->buf = buf; parm->size[0] += sizeof(info[0]); for (setup = setup_start, n = 0; setup != setup_end; setup++, n++) { /* skip USB transfers without callbacks: */ if (setup->callback == NULL) { continue; } /* see if there is a matching endpoint */ ep = usbd_get_endpoint(udev, ifaces[setup->if_index], setup); /* * Check that the USB PIPE is valid and that * the endpoint mode is proper. * * Make sure we don't allocate a streams * transfer when such a combination is not * valid. */ if ((ep == NULL) || (ep->methods == NULL) || ((ep->ep_mode != USB_EP_MODE_STREAMS) && (ep->ep_mode != USB_EP_MODE_DEFAULT)) || (setup->stream_id != 0 && (setup->stream_id >= USB_MAX_EP_STREAMS || (ep->ep_mode != USB_EP_MODE_STREAMS)))) { if (setup->flags.no_pipe_ok) continue; if ((setup->usb_mode != USB_MODE_DUAL) && (setup->usb_mode != udev->flags.usb_mode)) continue; parm->err = USB_ERR_NO_PIPE; goto done; } /* align data properly */ parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1)); /* store current setup pointer */ parm->curr_setup = setup; if (buf) { /* * Common initialization of the * "usb_xfer" structure. */ xfer = USB_ADD_BYTES(buf, parm->size[0]); xfer->address = udev->address; xfer->priv_sc = priv_sc; xfer->xroot = info; usb_callout_init_mtx(&xfer->timeout_handle, &udev->bus->bus_mtx, 0); } else { /* * Setup a dummy xfer, hence we are * writing to the "usb_xfer" * structure pointed to by "xfer" * before we have allocated any * memory: */ xfer = &udev->scratch.xfer_setup[0].dummy; memset(xfer, 0, sizeof(*xfer)); refcount++; } /* set transfer endpoint pointer */ xfer->endpoint = ep; /* set transfer stream ID */ xfer->stream_id = setup->stream_id; parm->size[0] += sizeof(xfer[0]); parm->methods = xfer->endpoint->methods; parm->curr_xfer = xfer; /* * Call the Host or Device controller transfer * setup routine: */ (udev->bus->methods->xfer_setup) (parm); /* check for error */ if (parm->err) goto done; if (buf) { /* * Increment the endpoint refcount. This * basically prevents setting a new * configuration and alternate setting * when USB transfers are in use on * the given interface. Search the USB * code for "endpoint->refcount_alloc" if you * want more information. */ USB_BUS_LOCK(info->bus); if (xfer->endpoint->refcount_alloc >= USB_EP_REF_MAX) parm->err = USB_ERR_INVAL; xfer->endpoint->refcount_alloc++; if (xfer->endpoint->refcount_alloc == 0) panic("usbd_transfer_setup(): Refcount wrapped to zero\n"); USB_BUS_UNLOCK(info->bus); /* * Whenever we set ppxfer[] then we * also need to increment the * "setup_refcount": */ info->setup_refcount++; /* * Transfer is successfully setup and * can be used: */ ppxfer[n] = xfer; } /* check for error */ if (parm->err) goto done; } if (buf != NULL || parm->err != 0) goto done; /* if no transfers, nothing to do */ if (refcount == 0) goto done; /* align data properly */ parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1)); /* store offset temporarily */ parm->size[1] = parm->size[0]; /* * The number of DMA tags required depends on * the number of endpoints. The current estimate * for maximum number of DMA tags per endpoint * is three: * 1) for loading memory * 2) for allocating memory * 3) for fixing memory [UHCI] */ parm->dma_tag_max += 3 * MIN(n_setup, USB_EP_MAX); /* * DMA tags for QH, TD, Data and more. */ parm->dma_tag_max += 8; parm->dma_tag_p += parm->dma_tag_max; parm->size[0] += ((uint8_t *)parm->dma_tag_p) - ((uint8_t *)0); /* align data properly */ parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1)); /* store offset temporarily */ parm->size[3] = parm->size[0]; parm->size[0] += ((uint8_t *)parm->dma_page_ptr) - ((uint8_t *)0); /* align data properly */ parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1)); /* store offset temporarily */ parm->size[4] = parm->size[0]; parm->size[0] += ((uint8_t *)parm->dma_page_cache_ptr) - ((uint8_t *)0); /* store end offset temporarily */ parm->size[5] = parm->size[0]; parm->size[0] += ((uint8_t *)parm->xfer_page_cache_ptr) - ((uint8_t *)0); /* store end offset temporarily */ parm->size[2] = parm->size[0]; /* align data properly */ parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1)); parm->size[6] = parm->size[0]; parm->size[0] += ((uint8_t *)parm->xfer_length_ptr) - ((uint8_t *)0); /* align data properly */ parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1)); /* allocate zeroed memory */ buf = malloc(parm->size[0], M_USB, M_WAITOK | M_ZERO); #if (USB_HAVE_MALLOC_WAITOK == 0) if (buf == NULL) { parm->err = USB_ERR_NOMEM; DPRINTFN(0, "cannot allocate memory block for " "configuration (%d bytes)\n", parm->size[0]); goto done; } #endif parm->dma_tag_p = USB_ADD_BYTES(buf, parm->size[1]); parm->dma_page_ptr = USB_ADD_BYTES(buf, parm->size[3]); parm->dma_page_cache_ptr = USB_ADD_BYTES(buf, parm->size[4]); parm->xfer_page_cache_ptr = USB_ADD_BYTES(buf, parm->size[5]); parm->xfer_length_ptr = USB_ADD_BYTES(buf, parm->size[6]); } done: if (buf) { if (info->setup_refcount == 0) { /* * "usbd_transfer_unsetup_sub" will unlock * the bus mutex before returning ! */ USB_BUS_LOCK(info->bus); /* something went wrong */ usbd_transfer_unsetup_sub(info, 0); } } /* check if any errors happened */ if (parm->err) usbd_transfer_unsetup(ppxfer, n_setup); error = parm->err; if (do_unlock) usbd_ctrl_unlock(udev); return (error); } /*------------------------------------------------------------------------* * usbd_transfer_unsetup_sub - factored out code *------------------------------------------------------------------------*/ static void usbd_transfer_unsetup_sub(struct usb_xfer_root *info, uint8_t needs_delay) { #if USB_HAVE_BUSDMA struct usb_page_cache *pc; #endif USB_BUS_LOCK_ASSERT(info->bus, MA_OWNED); /* wait for any outstanding DMA operations */ if (needs_delay) { usb_timeout_t temp; temp = usbd_get_dma_delay(info->udev); if (temp != 0) { usb_pause_mtx(&info->bus->bus_mtx, USB_MS_TO_TICKS(temp)); } } /* make sure that our done messages are not queued anywhere */ usb_proc_mwait(info->done_p, &info->done_m[0], &info->done_m[1]); USB_BUS_UNLOCK(info->bus); #if USB_HAVE_BUSDMA /* free DMA'able memory, if any */ pc = info->dma_page_cache_start; while (pc != info->dma_page_cache_end) { usb_pc_free_mem(pc); pc++; } /* free DMA maps in all "xfer->frbuffers" */ pc = info->xfer_page_cache_start; while (pc != info->xfer_page_cache_end) { usb_pc_dmamap_destroy(pc); pc++; } /* free all DMA tags */ usb_dma_tag_unsetup(&info->dma_parent_tag); #endif cv_destroy(&info->cv_drain); /* * free the "memory_base" last, hence the "info" structure is * contained within the "memory_base"! */ free(info->memory_base, M_USB); } /*------------------------------------------------------------------------* * usbd_transfer_unsetup - unsetup/free an array of USB transfers * * NOTE: All USB transfers in progress will get called back passing * the error code "USB_ERR_CANCELLED" before this function * returns. *------------------------------------------------------------------------*/ void usbd_transfer_unsetup(struct usb_xfer **pxfer, uint16_t n_setup) { struct usb_xfer *xfer; struct usb_xfer_root *info; uint8_t needs_delay = 0; WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, "usbd_transfer_unsetup can sleep!"); while (n_setup--) { xfer = pxfer[n_setup]; if (xfer == NULL) continue; info = xfer->xroot; USB_XFER_LOCK(xfer); USB_BUS_LOCK(info->bus); /* * HINT: when you start/stop a transfer, it might be a * good idea to directly use the "pxfer[]" structure: * * usbd_transfer_start(sc->pxfer[0]); * usbd_transfer_stop(sc->pxfer[0]); * * That way, if your code has many parts that will not * stop running under the same lock, in other words * "xfer_mtx", the usbd_transfer_start and * usbd_transfer_stop functions will simply return * when they detect a NULL pointer argument. * * To avoid any races we clear the "pxfer[]" pointer * while holding the private mutex of the driver: */ pxfer[n_setup] = NULL; USB_BUS_UNLOCK(info->bus); USB_XFER_UNLOCK(xfer); usbd_transfer_drain(xfer); #if USB_HAVE_BUSDMA if (xfer->flags_int.bdma_enable) needs_delay = 1; #endif /* * NOTE: default endpoint does not have an * interface, even if endpoint->iface_index == 0 */ USB_BUS_LOCK(info->bus); xfer->endpoint->refcount_alloc--; USB_BUS_UNLOCK(info->bus); usb_callout_drain(&xfer->timeout_handle); USB_BUS_LOCK(info->bus); USB_ASSERT(info->setup_refcount != 0, ("Invalid setup " "reference count\n")); info->setup_refcount--; if (info->setup_refcount == 0) { usbd_transfer_unsetup_sub(info, needs_delay); } else { USB_BUS_UNLOCK(info->bus); } } } /*------------------------------------------------------------------------* * usbd_control_transfer_init - factored out code * * In USB Device Mode we have to wait for the SETUP packet which * containst the "struct usb_device_request" structure, before we can * transfer any data. In USB Host Mode we already have the SETUP * packet at the moment the USB transfer is started. This leads us to * having to setup the USB transfer at two different places in * time. This function just contains factored out control transfer * initialisation code, so that we don't duplicate the code. *------------------------------------------------------------------------*/ static void usbd_control_transfer_init(struct usb_xfer *xfer) { struct usb_device_request req; /* copy out the USB request header */ usbd_copy_out(xfer->frbuffers, 0, &req, sizeof(req)); /* setup remainder */ xfer->flags_int.control_rem = UGETW(req.wLength); /* copy direction to endpoint variable */ xfer->endpointno &= ~(UE_DIR_IN | UE_DIR_OUT); xfer->endpointno |= (req.bmRequestType & UT_READ) ? UE_DIR_IN : UE_DIR_OUT; } /*------------------------------------------------------------------------* * usbd_control_transfer_did_data * * This function returns non-zero if a control endpoint has * transferred the first DATA packet after the SETUP packet. * Else it returns zero. *------------------------------------------------------------------------*/ static uint8_t usbd_control_transfer_did_data(struct usb_xfer *xfer) { struct usb_device_request req; /* SETUP packet is not yet sent */ if (xfer->flags_int.control_hdr != 0) return (0); /* copy out the USB request header */ usbd_copy_out(xfer->frbuffers, 0, &req, sizeof(req)); /* compare remainder to the initial value */ return (xfer->flags_int.control_rem != UGETW(req.wLength)); } /*------------------------------------------------------------------------* * usbd_setup_ctrl_transfer * * This function handles initialisation of control transfers. Control * transfers are special in that regard that they can both transmit * and receive data. * * Return values: * 0: Success * Else: Failure *------------------------------------------------------------------------*/ static int usbd_setup_ctrl_transfer(struct usb_xfer *xfer) { usb_frlength_t len; /* Check for control endpoint stall */ if (xfer->flags.stall_pipe && xfer->flags_int.control_act) { /* the control transfer is no longer active */ xfer->flags_int.control_stall = 1; xfer->flags_int.control_act = 0; } else { /* don't stall control transfer by default */ xfer->flags_int.control_stall = 0; } /* Check for invalid number of frames */ if (xfer->nframes > 2) { /* * If you need to split a control transfer, you * have to do one part at a time. Only with * non-control transfers you can do multiple * parts a time. */ DPRINTFN(0, "Too many frames: %u\n", (unsigned int)xfer->nframes); goto error; } /* * Check if there is a control * transfer in progress: */ if (xfer->flags_int.control_act) { if (xfer->flags_int.control_hdr) { /* clear send header flag */ xfer->flags_int.control_hdr = 0; /* setup control transfer */ if (xfer->flags_int.usb_mode == USB_MODE_DEVICE) { usbd_control_transfer_init(xfer); } } /* get data length */ len = xfer->sumlen; } else { /* the size of the SETUP structure is hardcoded ! */ if (xfer->frlengths[0] != sizeof(struct usb_device_request)) { DPRINTFN(0, "Wrong framelength %u != %zu\n", xfer->frlengths[0], sizeof(struct usb_device_request)); goto error; } /* check USB mode */ if (xfer->flags_int.usb_mode == USB_MODE_DEVICE) { /* check number of frames */ if (xfer->nframes != 1) { /* * We need to receive the setup * message first so that we know the * data direction! */ DPRINTF("Misconfigured transfer\n"); goto error; } /* * Set a dummy "control_rem" value. This * variable will be overwritten later by a * call to "usbd_control_transfer_init()" ! */ xfer->flags_int.control_rem = 0xFFFF; } else { /* setup "endpoint" and "control_rem" */ usbd_control_transfer_init(xfer); } /* set transfer-header flag */ xfer->flags_int.control_hdr = 1; /* get data length */ len = (xfer->sumlen - sizeof(struct usb_device_request)); } /* update did data flag */ xfer->flags_int.control_did_data = usbd_control_transfer_did_data(xfer); /* check if there is a length mismatch */ if (len > xfer->flags_int.control_rem) { DPRINTFN(0, "Length (%d) greater than " "remaining length (%d)\n", len, xfer->flags_int.control_rem); goto error; } /* check if we are doing a short transfer */ if (xfer->flags.force_short_xfer) { xfer->flags_int.control_rem = 0; } else { if ((len != xfer->max_data_length) && (len != xfer->flags_int.control_rem) && (xfer->nframes != 1)) { DPRINTFN(0, "Short control transfer without " "force_short_xfer set\n"); goto error; } xfer->flags_int.control_rem -= len; } /* the status part is executed when "control_act" is 0 */ if ((xfer->flags_int.control_rem > 0) || (xfer->flags.manual_status)) { /* don't execute the STATUS stage yet */ xfer->flags_int.control_act = 1; /* sanity check */ if ((!xfer->flags_int.control_hdr) && (xfer->nframes == 1)) { /* * This is not a valid operation! */ DPRINTFN(0, "Invalid parameter " "combination\n"); goto error; } } else { /* time to execute the STATUS stage */ xfer->flags_int.control_act = 0; } return (0); /* success */ error: return (1); /* failure */ } /*------------------------------------------------------------------------* * usbd_transfer_submit - start USB hardware for the given transfer * * This function should only be called from the USB callback. *------------------------------------------------------------------------*/ void usbd_transfer_submit(struct usb_xfer *xfer) { struct usb_xfer_root *info; struct usb_bus *bus; usb_frcount_t x; info = xfer->xroot; bus = info->bus; DPRINTF("xfer=%p, endpoint=%p, nframes=%d, dir=%s\n", xfer, xfer->endpoint, xfer->nframes, USB_GET_DATA_ISREAD(xfer) ? "read" : "write"); #ifdef USB_DEBUG if (USB_DEBUG_VAR > 0) { USB_BUS_LOCK(bus); usb_dump_endpoint(xfer->endpoint); USB_BUS_UNLOCK(bus); } #endif USB_XFER_LOCK_ASSERT(xfer, MA_OWNED); USB_BUS_LOCK_ASSERT(bus, MA_NOTOWNED); /* Only open the USB transfer once! */ if (!xfer->flags_int.open) { xfer->flags_int.open = 1; DPRINTF("open\n"); USB_BUS_LOCK(bus); (xfer->endpoint->methods->open) (xfer); USB_BUS_UNLOCK(bus); } /* set "transferring" flag */ xfer->flags_int.transferring = 1; #if USB_HAVE_POWERD /* increment power reference */ usbd_transfer_power_ref(xfer, 1); #endif /* * Check if the transfer is waiting on a queue, most * frequently the "done_q": */ if (xfer->wait_queue) { USB_BUS_LOCK(bus); usbd_transfer_dequeue(xfer); USB_BUS_UNLOCK(bus); } /* clear "did_dma_delay" flag */ xfer->flags_int.did_dma_delay = 0; /* clear "did_close" flag */ xfer->flags_int.did_close = 0; #if USB_HAVE_BUSDMA /* clear "bdma_setup" flag */ xfer->flags_int.bdma_setup = 0; #endif /* by default we cannot cancel any USB transfer immediately */ xfer->flags_int.can_cancel_immed = 0; /* clear lengths and frame counts by default */ xfer->sumlen = 0; xfer->actlen = 0; xfer->aframes = 0; /* clear any previous errors */ xfer->error = 0; /* Check if the device is still alive */ if (info->udev->state < USB_STATE_POWERED) { USB_BUS_LOCK(bus); /* * Must return cancelled error code else * device drivers can hang. */ usbd_transfer_done(xfer, USB_ERR_CANCELLED); USB_BUS_UNLOCK(bus); return; } /* sanity check */ if (xfer->nframes == 0) { if (xfer->flags.stall_pipe) { /* * Special case - want to stall without transferring * any data: */ DPRINTF("xfer=%p nframes=0: stall " "or clear stall!\n", xfer); USB_BUS_LOCK(bus); xfer->flags_int.can_cancel_immed = 1; /* start the transfer */ usb_command_wrapper(&xfer->endpoint-> endpoint_q[xfer->stream_id], xfer); USB_BUS_UNLOCK(bus); return; } USB_BUS_LOCK(bus); usbd_transfer_done(xfer, USB_ERR_INVAL); USB_BUS_UNLOCK(bus); return; } /* compute some variables */ for (x = 0; x != xfer->nframes; x++) { /* make a copy of the frlenghts[] */ xfer->frlengths[x + xfer->max_frame_count] = xfer->frlengths[x]; /* compute total transfer length */ xfer->sumlen += xfer->frlengths[x]; if (xfer->sumlen < xfer->frlengths[x]) { /* length wrapped around */ USB_BUS_LOCK(bus); usbd_transfer_done(xfer, USB_ERR_INVAL); USB_BUS_UNLOCK(bus); return; } } /* clear some internal flags */ xfer->flags_int.short_xfer_ok = 0; xfer->flags_int.short_frames_ok = 0; /* check if this is a control transfer */ if (xfer->flags_int.control_xfr) { if (usbd_setup_ctrl_transfer(xfer)) { USB_BUS_LOCK(bus); usbd_transfer_done(xfer, USB_ERR_STALLED); USB_BUS_UNLOCK(bus); return; } } /* * Setup filtered version of some transfer flags, * in case of data read direction */ if (USB_GET_DATA_ISREAD(xfer)) { if (xfer->flags.short_frames_ok) { xfer->flags_int.short_xfer_ok = 1; xfer->flags_int.short_frames_ok = 1; } else if (xfer->flags.short_xfer_ok) { xfer->flags_int.short_xfer_ok = 1; /* check for control transfer */ if (xfer->flags_int.control_xfr) { /* * 1) Control transfers do not support * reception of multiple short USB * frames in host mode and device side * mode, with exception of: * * 2) Due to sometimes buggy device * side firmware we need to do a * STATUS stage in case of short * control transfers in USB host mode. * The STATUS stage then becomes the * "alt_next" to the DATA stage. */ xfer->flags_int.short_frames_ok = 1; } } } /* * Check if BUS-DMA support is enabled and try to load virtual * buffers into DMA, if any: */ #if USB_HAVE_BUSDMA if (xfer->flags_int.bdma_enable) { /* insert the USB transfer last in the BUS-DMA queue */ usb_command_wrapper(&xfer->xroot->dma_q, xfer); return; } #endif /* * Enter the USB transfer into the Host Controller or * Device Controller schedule: */ usbd_pipe_enter(xfer); } /*------------------------------------------------------------------------* * usbd_pipe_enter - factored out code *------------------------------------------------------------------------*/ void usbd_pipe_enter(struct usb_xfer *xfer) { struct usb_endpoint *ep; USB_XFER_LOCK_ASSERT(xfer, MA_OWNED); USB_BUS_LOCK(xfer->xroot->bus); ep = xfer->endpoint; DPRINTF("enter\n"); /* the transfer can now be cancelled */ xfer->flags_int.can_cancel_immed = 1; /* enter the transfer */ (ep->methods->enter) (xfer); /* check for transfer error */ if (xfer->error) { /* some error has happened */ usbd_transfer_done(xfer, 0); USB_BUS_UNLOCK(xfer->xroot->bus); return; } /* start the transfer */ usb_command_wrapper(&ep->endpoint_q[xfer->stream_id], xfer); USB_BUS_UNLOCK(xfer->xroot->bus); } /*------------------------------------------------------------------------* * usbd_transfer_start - start an USB transfer * * NOTE: Calling this function more than one time will only * result in a single transfer start, until the USB transfer * completes. *------------------------------------------------------------------------*/ void usbd_transfer_start(struct usb_xfer *xfer) { if (xfer == NULL) { /* transfer is gone */ return; } USB_XFER_LOCK_ASSERT(xfer, MA_OWNED); /* mark the USB transfer started */ if (!xfer->flags_int.started) { /* lock the BUS lock to avoid races updating flags_int */ USB_BUS_LOCK(xfer->xroot->bus); xfer->flags_int.started = 1; USB_BUS_UNLOCK(xfer->xroot->bus); } /* check if the USB transfer callback is already transferring */ if (xfer->flags_int.transferring) { return; } USB_BUS_LOCK(xfer->xroot->bus); /* call the USB transfer callback */ usbd_callback_ss_done_defer(xfer); USB_BUS_UNLOCK(xfer->xroot->bus); } /*------------------------------------------------------------------------* * usbd_transfer_stop - stop an USB transfer * * NOTE: Calling this function more than one time will only * result in a single transfer stop. * NOTE: When this function returns it is not safe to free nor * reuse any DMA buffers. See "usbd_transfer_drain()". *------------------------------------------------------------------------*/ void usbd_transfer_stop(struct usb_xfer *xfer) { struct usb_endpoint *ep; if (xfer == NULL) { /* transfer is gone */ return; } USB_XFER_LOCK_ASSERT(xfer, MA_OWNED); /* check if the USB transfer was ever opened */ if (!xfer->flags_int.open) { if (xfer->flags_int.started) { /* nothing to do except clearing the "started" flag */ /* lock the BUS lock to avoid races updating flags_int */ USB_BUS_LOCK(xfer->xroot->bus); xfer->flags_int.started = 0; USB_BUS_UNLOCK(xfer->xroot->bus); } return; } /* try to stop the current USB transfer */ USB_BUS_LOCK(xfer->xroot->bus); /* override any previous error */ xfer->error = USB_ERR_CANCELLED; /* * Clear "open" and "started" when both private and USB lock * is locked so that we don't get a race updating "flags_int" */ xfer->flags_int.open = 0; xfer->flags_int.started = 0; /* * Check if we can cancel the USB transfer immediately. */ if (xfer->flags_int.transferring) { if (xfer->flags_int.can_cancel_immed && (!xfer->flags_int.did_close)) { DPRINTF("close\n"); /* * The following will lead to an USB_ERR_CANCELLED * error code being passed to the USB callback. */ (xfer->endpoint->methods->close) (xfer); /* only close once */ xfer->flags_int.did_close = 1; } else { /* need to wait for the next done callback */ } } else { DPRINTF("close\n"); /* close here and now */ (xfer->endpoint->methods->close) (xfer); /* * Any additional DMA delay is done by * "usbd_transfer_unsetup()". */ /* * Special case. Check if we need to restart a blocked * endpoint. */ ep = xfer->endpoint; /* * If the current USB transfer is completing we need * to start the next one: */ if (ep->endpoint_q[xfer->stream_id].curr == xfer) { usb_command_wrapper( &ep->endpoint_q[xfer->stream_id], NULL); } } USB_BUS_UNLOCK(xfer->xroot->bus); } /*------------------------------------------------------------------------* * usbd_transfer_pending * * This function will check if an USB transfer is pending which is a * little bit complicated! * Return values: * 0: Not pending * 1: Pending: The USB transfer will receive a callback in the future. *------------------------------------------------------------------------*/ uint8_t usbd_transfer_pending(struct usb_xfer *xfer) { struct usb_xfer_root *info; struct usb_xfer_queue *pq; if (xfer == NULL) { /* transfer is gone */ return (0); } USB_XFER_LOCK_ASSERT(xfer, MA_OWNED); if (xfer->flags_int.transferring) { /* trivial case */ return (1); } USB_BUS_LOCK(xfer->xroot->bus); if (xfer->wait_queue) { /* we are waiting on a queue somewhere */ USB_BUS_UNLOCK(xfer->xroot->bus); return (1); } info = xfer->xroot; pq = &info->done_q; if (pq->curr == xfer) { /* we are currently scheduled for callback */ USB_BUS_UNLOCK(xfer->xroot->bus); return (1); } /* we are not pending */ USB_BUS_UNLOCK(xfer->xroot->bus); return (0); } /*------------------------------------------------------------------------* * usbd_transfer_drain * * This function will stop the USB transfer and wait for any * additional BUS-DMA and HW-DMA operations to complete. Buffers that * are loaded into DMA can safely be freed or reused after that this * function has returned. *------------------------------------------------------------------------*/ void usbd_transfer_drain(struct usb_xfer *xfer) { WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, "usbd_transfer_drain can sleep!"); if (xfer == NULL) { /* transfer is gone */ return; } if (xfer->xroot->xfer_mtx != &Giant) { USB_XFER_LOCK_ASSERT(xfer, MA_NOTOWNED); } USB_XFER_LOCK(xfer); usbd_transfer_stop(xfer); while (usbd_transfer_pending(xfer) || xfer->flags_int.doing_callback) { /* * It is allowed that the callback can drop its * transfer mutex. In that case checking only * "usbd_transfer_pending()" is not enough to tell if * the USB transfer is fully drained. We also need to * check the internal "doing_callback" flag. */ xfer->flags_int.draining = 1; /* * Wait until the current outstanding USB * transfer is complete ! */ cv_wait(&xfer->xroot->cv_drain, xfer->xroot->xfer_mtx); } USB_XFER_UNLOCK(xfer); } struct usb_page_cache * usbd_xfer_get_frame(struct usb_xfer *xfer, usb_frcount_t frindex) { KASSERT(frindex < xfer->max_frame_count, ("frame index overflow")); return (&xfer->frbuffers[frindex]); } void * usbd_xfer_get_frame_buffer(struct usb_xfer *xfer, usb_frcount_t frindex) { struct usb_page_search page_info; KASSERT(frindex < xfer->max_frame_count, ("frame index overflow")); usbd_get_page(&xfer->frbuffers[frindex], 0, &page_info); return (page_info.buffer); } /*------------------------------------------------------------------------* * usbd_xfer_get_fps_shift * * The following function is only useful for isochronous transfers. It * returns how many times the frame execution rate has been shifted * down. * * Return value: * Success: 0..3 * Failure: 0 *------------------------------------------------------------------------*/ uint8_t usbd_xfer_get_fps_shift(struct usb_xfer *xfer) { return (xfer->fps_shift); } usb_frlength_t usbd_xfer_frame_len(struct usb_xfer *xfer, usb_frcount_t frindex) { KASSERT(frindex < xfer->max_frame_count, ("frame index overflow")); return (xfer->frlengths[frindex]); } /*------------------------------------------------------------------------* * usbd_xfer_set_frame_data * * This function sets the pointer of the buffer that should * loaded directly into DMA for the given USB frame. Passing "ptr" * equal to NULL while the corresponding "frlength" is greater * than zero gives undefined results! *------------------------------------------------------------------------*/ void usbd_xfer_set_frame_data(struct usb_xfer *xfer, usb_frcount_t frindex, void *ptr, usb_frlength_t len) { KASSERT(frindex < xfer->max_frame_count, ("frame index overflow")); /* set virtual address to load and length */ xfer->frbuffers[frindex].buffer = ptr; usbd_xfer_set_frame_len(xfer, frindex, len); } void usbd_xfer_frame_data(struct usb_xfer *xfer, usb_frcount_t frindex, void **ptr, int *len) { KASSERT(frindex < xfer->max_frame_count, ("frame index overflow")); if (ptr != NULL) *ptr = xfer->frbuffers[frindex].buffer; if (len != NULL) *len = xfer->frlengths[frindex]; } /*------------------------------------------------------------------------* * usbd_xfer_old_frame_length * * This function returns the framelength of the given frame at the * time the transfer was submitted. This function can be used to * compute the starting data pointer of the next isochronous frame * when an isochronous transfer has completed. *------------------------------------------------------------------------*/ usb_frlength_t usbd_xfer_old_frame_length(struct usb_xfer *xfer, usb_frcount_t frindex) { KASSERT(frindex < xfer->max_frame_count, ("frame index overflow")); return (xfer->frlengths[frindex + xfer->max_frame_count]); } void usbd_xfer_status(struct usb_xfer *xfer, int *actlen, int *sumlen, int *aframes, int *nframes) { if (actlen != NULL) *actlen = xfer->actlen; if (sumlen != NULL) *sumlen = xfer->sumlen; if (aframes != NULL) *aframes = xfer->aframes; if (nframes != NULL) *nframes = xfer->nframes; } /*------------------------------------------------------------------------* * usbd_xfer_set_frame_offset * * This function sets the frame data buffer offset relative to the beginning * of the USB DMA buffer allocated for this USB transfer. *------------------------------------------------------------------------*/ void usbd_xfer_set_frame_offset(struct usb_xfer *xfer, usb_frlength_t offset, usb_frcount_t frindex) { KASSERT(!xfer->flags.ext_buffer, ("Cannot offset data frame " "when the USB buffer is external\n")); KASSERT(frindex < xfer->max_frame_count, ("frame index overflow")); /* set virtual address to load */ xfer->frbuffers[frindex].buffer = USB_ADD_BYTES(xfer->local_buffer, offset); } void usbd_xfer_set_interval(struct usb_xfer *xfer, int i) { xfer->interval = i; } void usbd_xfer_set_timeout(struct usb_xfer *xfer, int t) { xfer->timeout = t; } void usbd_xfer_set_frames(struct usb_xfer *xfer, usb_frcount_t n) { xfer->nframes = n; } usb_frcount_t usbd_xfer_max_frames(struct usb_xfer *xfer) { return (xfer->max_frame_count); } usb_frlength_t usbd_xfer_max_len(struct usb_xfer *xfer) { return (xfer->max_data_length); } usb_frlength_t usbd_xfer_max_framelen(struct usb_xfer *xfer) { return (xfer->max_frame_size); } void usbd_xfer_set_frame_len(struct usb_xfer *xfer, usb_frcount_t frindex, usb_frlength_t len) { KASSERT(frindex < xfer->max_frame_count, ("frame index overflow")); xfer->frlengths[frindex] = len; } /*------------------------------------------------------------------------* * usb_callback_proc - factored out code * * This function performs USB callbacks. *------------------------------------------------------------------------*/ static void usb_callback_proc(struct usb_proc_msg *_pm) { struct usb_done_msg *pm = (void *)_pm; struct usb_xfer_root *info = pm->xroot; /* Change locking order */ USB_BUS_UNLOCK(info->bus); /* * We exploit the fact that the mutex is the same for all * callbacks that will be called from this thread: */ USB_MTX_LOCK(info->xfer_mtx); USB_BUS_LOCK(info->bus); /* Continue where we lost track */ usb_command_wrapper(&info->done_q, info->done_q.curr); USB_MTX_UNLOCK(info->xfer_mtx); } /*------------------------------------------------------------------------* * usbd_callback_ss_done_defer * * This function will defer the start, stop and done callback to the * correct thread. *------------------------------------------------------------------------*/ static void usbd_callback_ss_done_defer(struct usb_xfer *xfer) { struct usb_xfer_root *info = xfer->xroot; struct usb_xfer_queue *pq = &info->done_q; USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED); if (pq->curr != xfer) { usbd_transfer_enqueue(pq, xfer); } if (!pq->recurse_1) { /* * We have to postpone the callback due to the fact we * will have a Lock Order Reversal, LOR, if we try to * proceed ! */ (void) usb_proc_msignal(info->done_p, &info->done_m[0], &info->done_m[1]); } else { /* clear second recurse flag */ pq->recurse_2 = 0; } return; } /*------------------------------------------------------------------------* * usbd_callback_wrapper * * This is a wrapper for USB callbacks. This wrapper does some * auto-magic things like figuring out if we can call the callback * directly from the current context or if we need to wakeup the * interrupt process. *------------------------------------------------------------------------*/ static void usbd_callback_wrapper(struct usb_xfer_queue *pq) { struct usb_xfer *xfer = pq->curr; struct usb_xfer_root *info = xfer->xroot; USB_BUS_LOCK_ASSERT(info->bus, MA_OWNED); if ((pq->recurse_3 != 0 || mtx_owned(info->xfer_mtx) == 0) && USB_IN_POLLING_MODE_FUNC() == 0) { /* * Cases that end up here: * * 5) HW interrupt done callback or other source. * 6) HW completed transfer during callback */ DPRINTFN(3, "case 5 and 6\n"); /* * We have to postpone the callback due to the fact we * will have a Lock Order Reversal, LOR, if we try to * proceed! * * Postponing the callback also ensures that other USB * transfer queues get a chance. */ (void) usb_proc_msignal(info->done_p, &info->done_m[0], &info->done_m[1]); return; } /* * Cases that end up here: * * 1) We are starting a transfer * 2) We are prematurely calling back a transfer * 3) We are stopping a transfer * 4) We are doing an ordinary callback */ DPRINTFN(3, "case 1-4\n"); /* get next USB transfer in the queue */ info->done_q.curr = NULL; /* set flag in case of drain */ xfer->flags_int.doing_callback = 1; USB_BUS_UNLOCK(info->bus); USB_BUS_LOCK_ASSERT(info->bus, MA_NOTOWNED); /* set correct USB state for callback */ if (!xfer->flags_int.transferring) { xfer->usb_state = USB_ST_SETUP; if (!xfer->flags_int.started) { /* we got stopped before we even got started */ USB_BUS_LOCK(info->bus); goto done; } } else { if (usbd_callback_wrapper_sub(xfer)) { /* the callback has been deferred */ USB_BUS_LOCK(info->bus); goto done; } #if USB_HAVE_POWERD /* decrement power reference */ usbd_transfer_power_ref(xfer, -1); #endif xfer->flags_int.transferring = 0; if (xfer->error) { xfer->usb_state = USB_ST_ERROR; } else { /* set transferred state */ xfer->usb_state = USB_ST_TRANSFERRED; #if USB_HAVE_BUSDMA /* sync DMA memory, if any */ if (xfer->flags_int.bdma_enable && (!xfer->flags_int.bdma_no_post_sync)) { usb_bdma_post_sync(xfer); } #endif } } #if USB_HAVE_PF if (xfer->usb_state != USB_ST_SETUP) { USB_BUS_LOCK(info->bus); usbpf_xfertap(xfer, USBPF_XFERTAP_DONE); USB_BUS_UNLOCK(info->bus); } #endif /* call processing routine */ (xfer->callback) (xfer, xfer->error); /* pickup the USB mutex again */ USB_BUS_LOCK(info->bus); /* * Check if we got started after that we got cancelled, but * before we managed to do the callback. */ if ((!xfer->flags_int.open) && (xfer->flags_int.started) && (xfer->usb_state == USB_ST_ERROR)) { /* clear flag in case of drain */ xfer->flags_int.doing_callback = 0; /* try to loop, but not recursivly */ usb_command_wrapper(&info->done_q, xfer); return; } done: /* clear flag in case of drain */ xfer->flags_int.doing_callback = 0; /* * Check if we are draining. */ if (xfer->flags_int.draining && (!xfer->flags_int.transferring)) { /* "usbd_transfer_drain()" is waiting for end of transfer */ xfer->flags_int.draining = 0; cv_broadcast(&info->cv_drain); } /* do the next callback, if any */ usb_command_wrapper(&info->done_q, info->done_q.curr); } /*------------------------------------------------------------------------* * usb_dma_delay_done_cb * * This function is called when the DMA delay has been exectuded, and * will make sure that the callback is called to complete the USB * transfer. This code path is usually only used when there is an USB * error like USB_ERR_CANCELLED. *------------------------------------------------------------------------*/ void usb_dma_delay_done_cb(struct usb_xfer *xfer) { USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED); DPRINTFN(3, "Completed %p\n", xfer); /* queue callback for execution, again */ usbd_transfer_done(xfer, 0); } /*------------------------------------------------------------------------* * usbd_transfer_dequeue * * - This function is used to remove an USB transfer from a USB * transfer queue. * * - This function can be called multiple times in a row. *------------------------------------------------------------------------*/ void usbd_transfer_dequeue(struct usb_xfer *xfer) { struct usb_xfer_queue *pq; pq = xfer->wait_queue; if (pq) { TAILQ_REMOVE(&pq->head, xfer, wait_entry); xfer->wait_queue = NULL; } } /*------------------------------------------------------------------------* * usbd_transfer_enqueue * * - This function is used to insert an USB transfer into a USB * * transfer queue. * * - This function can be called multiple times in a row. *------------------------------------------------------------------------*/ void usbd_transfer_enqueue(struct usb_xfer_queue *pq, struct usb_xfer *xfer) { /* * Insert the USB transfer into the queue, if it is not * already on a USB transfer queue: */ if (xfer->wait_queue == NULL) { xfer->wait_queue = pq; TAILQ_INSERT_TAIL(&pq->head, xfer, wait_entry); } } /*------------------------------------------------------------------------* * usbd_transfer_done * * - This function is used to remove an USB transfer from the busdma, * pipe or interrupt queue. * * - This function is used to queue the USB transfer on the done * queue. * * - This function is used to stop any USB transfer timeouts. *------------------------------------------------------------------------*/ void usbd_transfer_done(struct usb_xfer *xfer, usb_error_t error) { struct usb_xfer_root *info = xfer->xroot; USB_BUS_LOCK_ASSERT(info->bus, MA_OWNED); DPRINTF("err=%s\n", usbd_errstr(error)); /* * If we are not transferring then just return. * This can happen during transfer cancel. */ if (!xfer->flags_int.transferring) { DPRINTF("not transferring\n"); /* end of control transfer, if any */ xfer->flags_int.control_act = 0; return; } /* only set transfer error, if not already set */ if (xfer->error == USB_ERR_NORMAL_COMPLETION) xfer->error = error; /* stop any callouts */ usb_callout_stop(&xfer->timeout_handle); /* * If we are waiting on a queue, just remove the USB transfer * from the queue, if any. We should have the required locks * locked to do the remove when this function is called. */ usbd_transfer_dequeue(xfer); #if USB_HAVE_BUSDMA if (mtx_owned(info->xfer_mtx)) { struct usb_xfer_queue *pq; /* * If the private USB lock is not locked, then we assume * that the BUS-DMA load stage has been passed: */ pq = &info->dma_q; if (pq->curr == xfer) { /* start the next BUS-DMA load, if any */ usb_command_wrapper(pq, NULL); } } #endif /* keep some statistics */ if (xfer->error == USB_ERR_CANCELLED) { info->udev->stats_cancelled.uds_requests [xfer->endpoint->edesc->bmAttributes & UE_XFERTYPE]++; } else if (xfer->error != USB_ERR_NORMAL_COMPLETION) { info->udev->stats_err.uds_requests [xfer->endpoint->edesc->bmAttributes & UE_XFERTYPE]++; } else { info->udev->stats_ok.uds_requests [xfer->endpoint->edesc->bmAttributes & UE_XFERTYPE]++; } /* call the USB transfer callback */ usbd_callback_ss_done_defer(xfer); } /*------------------------------------------------------------------------* * usbd_transfer_start_cb * * This function is called to start the USB transfer when * "xfer->interval" is greater than zero, and and the endpoint type is * BULK or CONTROL. *------------------------------------------------------------------------*/ static void usbd_transfer_start_cb(void *arg) { struct usb_xfer *xfer = arg; struct usb_endpoint *ep = xfer->endpoint; USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED); DPRINTF("start\n"); #if USB_HAVE_PF usbpf_xfertap(xfer, USBPF_XFERTAP_SUBMIT); #endif /* the transfer can now be cancelled */ xfer->flags_int.can_cancel_immed = 1; /* start USB transfer, if no error */ if (xfer->error == 0) (ep->methods->start) (xfer); /* check for transfer error */ if (xfer->error) { /* some error has happened */ usbd_transfer_done(xfer, 0); } } +/*------------------------------------------------------------------------* + * usbd_xfer_set_zlp + * + * This function sets the USB transfers ZLP flag. + *------------------------------------------------------------------------*/ +void +usbd_xfer_set_zlp(struct usb_xfer *xfer) +{ + if (xfer == NULL) { + /* tearing down */ + return; + } + USB_XFER_LOCK_ASSERT(xfer, MA_OWNED); + + /* avoid any races by locking the USB mutex */ + USB_BUS_LOCK(xfer->xroot->bus); + xfer->flags.send_zlp = 1; + USB_BUS_UNLOCK(xfer->xroot->bus); +} + +/*------------------------------------------------------------------------* + * usbd_xfer_get_and_clr_zlp + * + * This function gets and clears the USB transfers ZLP flag and + * queues a zero-length USB transfer if the flag was set. + *------------------------------------------------------------------------*/ +uint8_t +usbd_xfer_get_and_clr_zlp(struct usb_xfer *xfer) +{ + uint8_t retval; + + if (xfer == NULL) { + /* tearing down */ + return (0); + } + USB_XFER_LOCK_ASSERT(xfer, MA_OWNED); + + retval = xfer->flags.send_zlp; + + if (retval != 0) { + DPRINTFN(1, "Sending zero-length packet.\n"); + + /* avoid any races by locking the USB mutex */ + USB_BUS_LOCK(xfer->xroot->bus); + xfer->flags.send_zlp = 0; + USB_BUS_UNLOCK(xfer->xroot->bus); + + /* queue up a zero-length packet */ + usbd_xfer_set_frame_len(xfer, 0, 0); + usbd_xfer_set_frames(xfer, 1); + usbd_transfer_submit(xfer); + } + return (retval); +} + /*------------------------------------------------------------------------* * usbd_xfer_set_stall * * This function is used to set the stall flag outside the * callback. This function is NULL safe. *------------------------------------------------------------------------*/ void usbd_xfer_set_stall(struct usb_xfer *xfer) { if (xfer == NULL) { /* tearing down */ return; } USB_XFER_LOCK_ASSERT(xfer, MA_OWNED); /* avoid any races by locking the USB mutex */ USB_BUS_LOCK(xfer->xroot->bus); xfer->flags.stall_pipe = 1; USB_BUS_UNLOCK(xfer->xroot->bus); } int usbd_xfer_is_stalled(struct usb_xfer *xfer) { return (xfer->endpoint->is_stalled); } /*------------------------------------------------------------------------* * usbd_transfer_clear_stall * * This function is used to clear the stall flag outside the * callback. This function is NULL safe. *------------------------------------------------------------------------*/ void usbd_transfer_clear_stall(struct usb_xfer *xfer) { if (xfer == NULL) { /* tearing down */ return; } USB_XFER_LOCK_ASSERT(xfer, MA_OWNED); /* avoid any races by locking the USB mutex */ USB_BUS_LOCK(xfer->xroot->bus); - xfer->flags.stall_pipe = 0; - USB_BUS_UNLOCK(xfer->xroot->bus); } /*------------------------------------------------------------------------* * usbd_pipe_start * * This function is used to add an USB transfer to the pipe transfer list. *------------------------------------------------------------------------*/ void usbd_pipe_start(struct usb_xfer_queue *pq) { struct usb_endpoint *ep; struct usb_xfer *xfer; uint8_t type; xfer = pq->curr; ep = xfer->endpoint; USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED); /* * If the endpoint is already stalled we do nothing ! */ if (ep->is_stalled) { return; } /* * Check if we are supposed to stall the endpoint: */ if (xfer->flags.stall_pipe) { struct usb_device *udev; struct usb_xfer_root *info; /* clear stall command */ xfer->flags.stall_pipe = 0; /* get pointer to USB device */ info = xfer->xroot; udev = info->udev; /* * Only stall BULK and INTERRUPT endpoints. */ type = (ep->edesc->bmAttributes & UE_XFERTYPE); if ((type == UE_BULK) || (type == UE_INTERRUPT)) { uint8_t did_stall; did_stall = 1; if (udev->flags.usb_mode == USB_MODE_DEVICE) { (udev->bus->methods->set_stall) ( udev, ep, &did_stall); } else if (udev->ctrl_xfer[1]) { info = udev->ctrl_xfer[1]->xroot; usb_proc_msignal( USB_BUS_CS_PROC(info->bus), &udev->cs_msg[0], &udev->cs_msg[1]); } else { /* should not happen */ DPRINTFN(0, "No stall handler\n"); } /* * Check if we should stall. Some USB hardware * handles set- and clear-stall in hardware. */ if (did_stall) { /* * The transfer will be continued when * the clear-stall control endpoint * message is received. */ ep->is_stalled = 1; return; } } else if (type == UE_ISOCHRONOUS) { /* * Make sure any FIFO overflow or other FIFO * error conditions go away by resetting the * endpoint FIFO through the clear stall * method. */ if (udev->flags.usb_mode == USB_MODE_DEVICE) { (udev->bus->methods->clear_stall) (udev, ep); } } } /* Set or clear stall complete - special case */ if (xfer->nframes == 0) { /* we are complete */ xfer->aframes = 0; usbd_transfer_done(xfer, 0); return; } /* * Handled cases: * * 1) Start the first transfer queued. * * 2) Re-start the current USB transfer. */ /* * Check if there should be any * pre transfer start delay: */ if (xfer->interval > 0) { type = (ep->edesc->bmAttributes & UE_XFERTYPE); if ((type == UE_BULK) || (type == UE_CONTROL)) { usbd_transfer_timeout_ms(xfer, &usbd_transfer_start_cb, xfer->interval); return; } } DPRINTF("start\n"); #if USB_HAVE_PF usbpf_xfertap(xfer, USBPF_XFERTAP_SUBMIT); #endif /* the transfer can now be cancelled */ xfer->flags_int.can_cancel_immed = 1; /* start USB transfer, if no error */ if (xfer->error == 0) (ep->methods->start) (xfer); /* check for transfer error */ if (xfer->error) { /* some error has happened */ usbd_transfer_done(xfer, 0); } } /*------------------------------------------------------------------------* * usbd_transfer_timeout_ms * * This function is used to setup a timeout on the given USB * transfer. If the timeout has been deferred the callback given by * "cb" will get called after "ms" milliseconds. *------------------------------------------------------------------------*/ void usbd_transfer_timeout_ms(struct usb_xfer *xfer, void (*cb) (void *arg), usb_timeout_t ms) { USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED); /* defer delay */ usb_callout_reset(&xfer->timeout_handle, USB_MS_TO_TICKS(ms) + USB_CALLOUT_ZERO_TICKS, cb, xfer); } /*------------------------------------------------------------------------* * usbd_callback_wrapper_sub * * - This function will update variables in an USB transfer after * that the USB transfer is complete. * * - This function is used to start the next USB transfer on the * ep transfer queue, if any. * * NOTE: In some special cases the USB transfer will not be removed from * the pipe queue, but remain first. To enforce USB transfer removal call * this function passing the error code "USB_ERR_CANCELLED". * * Return values: * 0: Success. * Else: The callback has been deferred. *------------------------------------------------------------------------*/ static uint8_t usbd_callback_wrapper_sub(struct usb_xfer *xfer) { struct usb_endpoint *ep; struct usb_bus *bus; usb_frcount_t x; bus = xfer->xroot->bus; if ((!xfer->flags_int.open) && (!xfer->flags_int.did_close)) { DPRINTF("close\n"); USB_BUS_LOCK(bus); (xfer->endpoint->methods->close) (xfer); USB_BUS_UNLOCK(bus); /* only close once */ xfer->flags_int.did_close = 1; return (1); /* wait for new callback */ } /* * If we have a non-hardware induced error we * need to do the DMA delay! */ if (xfer->error != 0 && !xfer->flags_int.did_dma_delay && (xfer->error == USB_ERR_CANCELLED || xfer->error == USB_ERR_TIMEOUT || bus->methods->start_dma_delay != NULL)) { usb_timeout_t temp; /* only delay once */ xfer->flags_int.did_dma_delay = 1; /* we can not cancel this delay */ xfer->flags_int.can_cancel_immed = 0; temp = usbd_get_dma_delay(xfer->xroot->udev); DPRINTFN(3, "DMA delay, %u ms, " "on %p\n", temp, xfer); if (temp != 0) { USB_BUS_LOCK(bus); /* * Some hardware solutions have dedicated * events when it is safe to free DMA'ed * memory. For the other hardware platforms we * use a static delay. */ if (bus->methods->start_dma_delay != NULL) { (bus->methods->start_dma_delay) (xfer); } else { usbd_transfer_timeout_ms(xfer, (void (*)(void *))&usb_dma_delay_done_cb, temp); } USB_BUS_UNLOCK(bus); return (1); /* wait for new callback */ } } /* check actual number of frames */ if (xfer->aframes > xfer->nframes) { if (xfer->error == 0) { panic("%s: actual number of frames, %d, is " "greater than initial number of frames, %d\n", __FUNCTION__, xfer->aframes, xfer->nframes); } else { /* just set some valid value */ xfer->aframes = xfer->nframes; } } /* compute actual length */ xfer->actlen = 0; for (x = 0; x != xfer->aframes; x++) { xfer->actlen += xfer->frlengths[x]; } /* * Frames that were not transferred get zero actual length in * case the USB device driver does not check the actual number * of frames transferred, "xfer->aframes": */ for (; x < xfer->nframes; x++) { usbd_xfer_set_frame_len(xfer, x, 0); } /* check actual length */ if (xfer->actlen > xfer->sumlen) { if (xfer->error == 0) { panic("%s: actual length, %d, is greater than " "initial length, %d\n", __FUNCTION__, xfer->actlen, xfer->sumlen); } else { /* just set some valid value */ xfer->actlen = xfer->sumlen; } } DPRINTFN(1, "xfer=%p endpoint=%p sts=%d alen=%d, slen=%d, afrm=%d, nfrm=%d\n", xfer, xfer->endpoint, xfer->error, xfer->actlen, xfer->sumlen, xfer->aframes, xfer->nframes); if (xfer->error) { /* end of control transfer, if any */ xfer->flags_int.control_act = 0; #if USB_HAVE_TT_SUPPORT switch (xfer->error) { case USB_ERR_NORMAL_COMPLETION: case USB_ERR_SHORT_XFER: case USB_ERR_STALLED: case USB_ERR_CANCELLED: /* nothing to do */ break; default: /* try to reset the TT, if any */ USB_BUS_LOCK(bus); uhub_tt_buffer_reset_async_locked(xfer->xroot->udev, xfer->endpoint); USB_BUS_UNLOCK(bus); break; } #endif /* check if we should block the execution queue */ if ((xfer->error != USB_ERR_CANCELLED) && (xfer->flags.pipe_bof)) { DPRINTFN(2, "xfer=%p: Block On Failure " "on endpoint=%p\n", xfer, xfer->endpoint); goto done; } } else { /* check for short transfers */ if (xfer->actlen < xfer->sumlen) { /* end of control transfer, if any */ xfer->flags_int.control_act = 0; if (!xfer->flags_int.short_xfer_ok) { xfer->error = USB_ERR_SHORT_XFER; if (xfer->flags.pipe_bof) { DPRINTFN(2, "xfer=%p: Block On Failure on " "Short Transfer on endpoint %p.\n", xfer, xfer->endpoint); goto done; } } } else { /* * Check if we are in the middle of a * control transfer: */ if (xfer->flags_int.control_act) { DPRINTFN(5, "xfer=%p: Control transfer " "active on endpoint=%p\n", xfer, xfer->endpoint); goto done; } } } ep = xfer->endpoint; /* * If the current USB transfer is completing we need to start the * next one: */ USB_BUS_LOCK(bus); if (ep->endpoint_q[xfer->stream_id].curr == xfer) { usb_command_wrapper(&ep->endpoint_q[xfer->stream_id], NULL); if (ep->endpoint_q[xfer->stream_id].curr != NULL || TAILQ_FIRST(&ep->endpoint_q[xfer->stream_id].head) != NULL) { /* there is another USB transfer waiting */ } else { /* this is the last USB transfer */ /* clear isochronous sync flag */ xfer->endpoint->is_synced = 0; } } USB_BUS_UNLOCK(bus); done: return (0); } /*------------------------------------------------------------------------* * usb_command_wrapper * * This function is used to execute commands non-recursivly on an USB * transfer. *------------------------------------------------------------------------*/ void usb_command_wrapper(struct usb_xfer_queue *pq, struct usb_xfer *xfer) { if (xfer) { /* * If the transfer is not already processing, * queue it! */ if (pq->curr != xfer) { usbd_transfer_enqueue(pq, xfer); if (pq->curr != NULL) { /* something is already processing */ DPRINTFN(6, "busy %p\n", pq->curr); return; } } } else { /* Get next element in queue */ pq->curr = NULL; } if (!pq->recurse_1) { /* clear third recurse flag */ pq->recurse_3 = 0; do { /* set two first recurse flags */ pq->recurse_1 = 1; pq->recurse_2 = 1; if (pq->curr == NULL) { xfer = TAILQ_FIRST(&pq->head); if (xfer) { TAILQ_REMOVE(&pq->head, xfer, wait_entry); xfer->wait_queue = NULL; pq->curr = xfer; } else { break; } } DPRINTFN(6, "cb %p (enter)\n", pq->curr); (pq->command) (pq); DPRINTFN(6, "cb %p (leave)\n", pq->curr); /* * Set third recurse flag to indicate * recursion happened: */ pq->recurse_3 = 1; } while (!pq->recurse_2); /* clear first recurse flag */ pq->recurse_1 = 0; } else { /* clear second recurse flag */ pq->recurse_2 = 0; } } /*------------------------------------------------------------------------* * usbd_ctrl_transfer_setup * * This function is used to setup the default USB control endpoint * transfer. *------------------------------------------------------------------------*/ void usbd_ctrl_transfer_setup(struct usb_device *udev) { struct usb_xfer *xfer; uint8_t no_resetup; uint8_t iface_index; /* check for root HUB */ if (udev->parent_hub == NULL) return; repeat: xfer = udev->ctrl_xfer[0]; if (xfer) { USB_XFER_LOCK(xfer); no_resetup = ((xfer->address == udev->address) && (udev->ctrl_ep_desc.wMaxPacketSize[0] == udev->ddesc.bMaxPacketSize)); if (udev->flags.usb_mode == USB_MODE_DEVICE) { if (no_resetup) { /* * NOTE: checking "xfer->address" and * starting the USB transfer must be * atomic! */ usbd_transfer_start(xfer); } } USB_XFER_UNLOCK(xfer); } else { no_resetup = 0; } if (no_resetup) { /* * All parameters are exactly the same like before. * Just return. */ return; } /* * Update wMaxPacketSize for the default control endpoint: */ udev->ctrl_ep_desc.wMaxPacketSize[0] = udev->ddesc.bMaxPacketSize; /* * Unsetup any existing USB transfer: */ usbd_transfer_unsetup(udev->ctrl_xfer, USB_CTRL_XFER_MAX); /* * Reset clear stall error counter. */ udev->clear_stall_errors = 0; /* * Try to setup a new USB transfer for the * default control endpoint: */ iface_index = 0; if (usbd_transfer_setup(udev, &iface_index, udev->ctrl_xfer, udev->bus->control_ep_quirk ? usb_control_ep_quirk_cfg : usb_control_ep_cfg, USB_CTRL_XFER_MAX, NULL, &udev->device_mtx)) { DPRINTFN(0, "could not setup default " "USB transfer\n"); } else { goto repeat; } } /*------------------------------------------------------------------------* * usbd_clear_data_toggle - factored out code * * NOTE: the intention of this function is not to reset the hardware * data toggle. *------------------------------------------------------------------------*/ void usbd_clear_stall_locked(struct usb_device *udev, struct usb_endpoint *ep) { USB_BUS_LOCK_ASSERT(udev->bus, MA_OWNED); /* check that we have a valid case */ if (udev->flags.usb_mode == USB_MODE_HOST && udev->parent_hub != NULL && udev->bus->methods->clear_stall != NULL && ep->methods != NULL) { (udev->bus->methods->clear_stall) (udev, ep); } } /*------------------------------------------------------------------------* * usbd_clear_data_toggle - factored out code * * NOTE: the intention of this function is not to reset the hardware * data toggle on the USB device side. *------------------------------------------------------------------------*/ void usbd_clear_data_toggle(struct usb_device *udev, struct usb_endpoint *ep) { DPRINTFN(5, "udev=%p endpoint=%p\n", udev, ep); USB_BUS_LOCK(udev->bus); ep->toggle_next = 0; /* some hardware needs a callback to clear the data toggle */ usbd_clear_stall_locked(udev, ep); USB_BUS_UNLOCK(udev->bus); } /*------------------------------------------------------------------------* * usbd_clear_stall_callback - factored out clear stall callback * * Input parameters: * xfer1: Clear Stall Control Transfer * xfer2: Stalled USB Transfer * * This function is NULL safe. * * Return values: * 0: In progress * Else: Finished * * Clear stall config example: * * static const struct usb_config my_clearstall = { * .type = UE_CONTROL, * .endpoint = 0, * .direction = UE_DIR_ANY, * .interval = 50, //50 milliseconds * .bufsize = sizeof(struct usb_device_request), * .timeout = 1000, //1.000 seconds * .callback = &my_clear_stall_callback, // ** * .usb_mode = USB_MODE_HOST, * }; * * ** "my_clear_stall_callback" calls "usbd_clear_stall_callback" * passing the correct parameters. *------------------------------------------------------------------------*/ uint8_t usbd_clear_stall_callback(struct usb_xfer *xfer1, struct usb_xfer *xfer2) { struct usb_device_request req; if (xfer2 == NULL) { /* looks like we are tearing down */ DPRINTF("NULL input parameter\n"); return (0); } USB_XFER_LOCK_ASSERT(xfer1, MA_OWNED); USB_XFER_LOCK_ASSERT(xfer2, MA_OWNED); switch (USB_GET_STATE(xfer1)) { case USB_ST_SETUP: /* * pre-clear the data toggle to DATA0 ("umass.c" and * "ata-usb.c" depends on this) */ usbd_clear_data_toggle(xfer2->xroot->udev, xfer2->endpoint); /* setup a clear-stall packet */ req.bmRequestType = UT_WRITE_ENDPOINT; req.bRequest = UR_CLEAR_FEATURE; USETW(req.wValue, UF_ENDPOINT_HALT); req.wIndex[0] = xfer2->endpoint->edesc->bEndpointAddress; req.wIndex[1] = 0; USETW(req.wLength, 0); /* * "usbd_transfer_setup_sub()" will ensure that * we have sufficient room in the buffer for * the request structure! */ /* copy in the transfer */ usbd_copy_in(xfer1->frbuffers, 0, &req, sizeof(req)); /* set length */ xfer1->frlengths[0] = sizeof(req); xfer1->nframes = 1; usbd_transfer_submit(xfer1); return (0); case USB_ST_TRANSFERRED: break; default: /* Error */ if (xfer1->error == USB_ERR_CANCELLED) { return (0); } break; } return (1); /* Clear Stall Finished */ } /*------------------------------------------------------------------------* * usbd_transfer_poll * * The following function gets called from the USB keyboard driver and * UMASS when the system has paniced. * * NOTE: It is currently not possible to resume normal operation on * the USB controller which has been polled, due to clearing of the * "up_dsleep" and "up_msleep" flags. *------------------------------------------------------------------------*/ void usbd_transfer_poll(struct usb_xfer **ppxfer, uint16_t max) { struct usb_xfer *xfer; struct usb_xfer_root *xroot; struct usb_device *udev; struct usb_proc_msg *pm; struct usb_bus *bus; uint16_t n; uint16_t drop_bus_spin; uint16_t drop_bus; uint16_t drop_xfer; for (n = 0; n != max; n++) { /* Extra checks to avoid panic */ xfer = ppxfer[n]; if (xfer == NULL) continue; /* no USB transfer */ xroot = xfer->xroot; if (xroot == NULL) continue; /* no USB root */ udev = xroot->udev; if (udev == NULL) continue; /* no USB device */ bus = udev->bus; if (bus == NULL) continue; /* no BUS structure */ if (bus->methods == NULL) continue; /* no BUS methods */ if (bus->methods->xfer_poll == NULL) continue; /* no poll method */ drop_bus_spin = 0; drop_bus = 0; drop_xfer = 0; if (USB_IN_POLLING_MODE_FUNC() == 0) { /* make sure that the BUS spin mutex is not locked */ while (mtx_owned(&bus->bus_spin_lock)) { mtx_unlock_spin(&bus->bus_spin_lock); drop_bus_spin++; } /* make sure that the BUS mutex is not locked */ while (mtx_owned(&bus->bus_mtx)) { mtx_unlock(&bus->bus_mtx); drop_bus++; } /* make sure that the transfer mutex is not locked */ while (mtx_owned(xroot->xfer_mtx)) { mtx_unlock(xroot->xfer_mtx); drop_xfer++; } } /* Make sure cv_signal() and cv_broadcast() is not called */ USB_BUS_CONTROL_XFER_PROC(bus)->up_msleep = 0; USB_BUS_EXPLORE_PROC(bus)->up_msleep = 0; USB_BUS_GIANT_PROC(bus)->up_msleep = 0; USB_BUS_NON_GIANT_ISOC_PROC(bus)->up_msleep = 0; USB_BUS_NON_GIANT_BULK_PROC(bus)->up_msleep = 0; /* poll USB hardware */ (bus->methods->xfer_poll) (bus); USB_BUS_LOCK(xroot->bus); /* check for clear stall */ if (udev->ctrl_xfer[1] != NULL) { /* poll clear stall start */ pm = &udev->cs_msg[0].hdr; (pm->pm_callback) (pm); /* poll clear stall done thread */ pm = &udev->ctrl_xfer[1]-> xroot->done_m[0].hdr; (pm->pm_callback) (pm); } /* poll done thread */ pm = &xroot->done_m[0].hdr; (pm->pm_callback) (pm); USB_BUS_UNLOCK(xroot->bus); /* restore transfer mutex */ while (drop_xfer--) mtx_lock(xroot->xfer_mtx); /* restore BUS mutex */ while (drop_bus--) mtx_lock(&bus->bus_mtx); /* restore BUS spin mutex */ while (drop_bus_spin--) mtx_lock_spin(&bus->bus_spin_lock); } } static void usbd_get_std_packet_size(struct usb_std_packet_size *ptr, uint8_t type, enum usb_dev_speed speed) { static const uint16_t intr_range_max[USB_SPEED_MAX] = { [USB_SPEED_LOW] = 8, [USB_SPEED_FULL] = 64, [USB_SPEED_HIGH] = 1024, [USB_SPEED_VARIABLE] = 1024, [USB_SPEED_SUPER] = 1024, }; static const uint16_t isoc_range_max[USB_SPEED_MAX] = { [USB_SPEED_LOW] = 0, /* invalid */ [USB_SPEED_FULL] = 1023, [USB_SPEED_HIGH] = 1024, [USB_SPEED_VARIABLE] = 3584, [USB_SPEED_SUPER] = 1024, }; static const uint16_t control_min[USB_SPEED_MAX] = { [USB_SPEED_LOW] = 8, [USB_SPEED_FULL] = 8, [USB_SPEED_HIGH] = 64, [USB_SPEED_VARIABLE] = 512, [USB_SPEED_SUPER] = 512, }; static const uint16_t bulk_min[USB_SPEED_MAX] = { [USB_SPEED_LOW] = 8, [USB_SPEED_FULL] = 8, [USB_SPEED_HIGH] = 512, [USB_SPEED_VARIABLE] = 512, [USB_SPEED_SUPER] = 1024, }; uint16_t temp; memset(ptr, 0, sizeof(*ptr)); switch (type) { case UE_INTERRUPT: ptr->range.max = intr_range_max[speed]; break; case UE_ISOCHRONOUS: ptr->range.max = isoc_range_max[speed]; break; default: if (type == UE_BULK) temp = bulk_min[speed]; else /* UE_CONTROL */ temp = control_min[speed]; /* default is fixed */ ptr->fixed[0] = temp; ptr->fixed[1] = temp; ptr->fixed[2] = temp; ptr->fixed[3] = temp; if (speed == USB_SPEED_FULL) { /* multiple sizes */ ptr->fixed[1] = 16; ptr->fixed[2] = 32; ptr->fixed[3] = 64; } if ((speed == USB_SPEED_VARIABLE) && (type == UE_BULK)) { /* multiple sizes */ ptr->fixed[2] = 1024; ptr->fixed[3] = 1536; } break; } } void * usbd_xfer_softc(struct usb_xfer *xfer) { return (xfer->priv_sc); } void * usbd_xfer_get_priv(struct usb_xfer *xfer) { return (xfer->priv_fifo); } void usbd_xfer_set_priv(struct usb_xfer *xfer, void *ptr) { xfer->priv_fifo = ptr; } uint8_t usbd_xfer_state(struct usb_xfer *xfer) { return (xfer->usb_state); } void usbd_xfer_set_flag(struct usb_xfer *xfer, int flag) { switch (flag) { case USB_FORCE_SHORT_XFER: xfer->flags.force_short_xfer = 1; break; case USB_SHORT_XFER_OK: xfer->flags.short_xfer_ok = 1; break; case USB_MULTI_SHORT_OK: xfer->flags.short_frames_ok = 1; break; case USB_MANUAL_STATUS: xfer->flags.manual_status = 1; break; } } void usbd_xfer_clr_flag(struct usb_xfer *xfer, int flag) { switch (flag) { case USB_FORCE_SHORT_XFER: xfer->flags.force_short_xfer = 0; break; case USB_SHORT_XFER_OK: xfer->flags.short_xfer_ok = 0; break; case USB_MULTI_SHORT_OK: xfer->flags.short_frames_ok = 0; break; case USB_MANUAL_STATUS: xfer->flags.manual_status = 0; break; } } /* * The following function returns in milliseconds when the isochronous * transfer was completed by the hardware. The returned value wraps * around 65536 milliseconds. */ uint16_t usbd_xfer_get_timestamp(struct usb_xfer *xfer) { return (xfer->isoc_time_complete); } /* * The following function returns non-zero if the max packet size * field was clamped to a valid value. Else it returns zero. */ uint8_t usbd_xfer_maxp_was_clamped(struct usb_xfer *xfer) { return (xfer->flags_int.maxp_was_clamped); } /* * The following function computes the next isochronous frame number * where the first isochronous packet should be queued. * * The function returns non-zero if there was a discontinuity. * Else zero is returned for normal operation. */ uint8_t usbd_xfer_get_isochronous_start_frame(struct usb_xfer *xfer, uint32_t frame_curr, uint32_t frame_min, uint32_t frame_ms, uint32_t frame_mask, uint32_t *p_frame_start) { uint32_t duration; uint32_t delta; uint8_t retval; uint8_t shift; /* Compute time ahead of current schedule. */ delta = (xfer->endpoint->isoc_next - frame_curr) & frame_mask; /* * Check if it is the first transfer or if the future frame * delta is less than one millisecond or if the frame delta is * negative: */ if (xfer->endpoint->is_synced == 0 || delta < (frame_ms + frame_min) || delta > (frame_mask / 2)) { /* Schedule transfer 2 milliseconds into the future. */ xfer->endpoint->isoc_next = (frame_curr + 2 * frame_ms + frame_min) & frame_mask; xfer->endpoint->is_synced = 1; retval = 1; } else { retval = 0; } /* Store start time, if any. */ if (p_frame_start != NULL) *p_frame_start = xfer->endpoint->isoc_next & frame_mask; /* Get relative completion time, in milliseconds. */ delta = xfer->endpoint->isoc_next - frame_curr + (frame_curr % frame_ms); delta &= frame_mask; delta /= frame_ms; switch (usbd_get_speed(xfer->xroot->udev)) { case USB_SPEED_FULL: shift = 3; break; default: shift = usbd_xfer_get_fps_shift(xfer); break; } /* Get duration in milliseconds, rounded up. */ duration = ((xfer->nframes << shift) + 7) / 8; /* Compute full 32-bit completion time, in milliseconds. */ xfer->isoc_time_complete = usb_isoc_time_expand(xfer->xroot->bus, frame_curr / frame_ms) + delta + duration; /* Compute next isochronous frame. */ xfer->endpoint->isoc_next += duration * frame_ms; xfer->endpoint->isoc_next &= frame_mask; return (retval); } diff --git a/sys/dev/usb/usbdi.h b/sys/dev/usb/usbdi.h index 1b3b4af5f717..287e40d59364 100644 --- a/sys/dev/usb/usbdi.h +++ b/sys/dev/usb/usbdi.h @@ -1,715 +1,718 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2009 Andrew Thompson * * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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. * * $FreeBSD$ */ #ifndef _USB_USBDI_H_ #define _USB_USBDI_H_ struct usb_fifo; struct usb_xfer; struct usb_device; struct usb_attach_arg; struct usb_interface; struct usb_endpoint; struct usb_page_cache; struct usb_page_search; struct usb_process; struct usb_proc_msg; struct usb_mbuf; struct usb_fs_privdata; struct mbuf; typedef enum { /* keep in sync with usb_errstr_table */ USB_ERR_NORMAL_COMPLETION = 0, USB_ERR_PENDING_REQUESTS, /* 1 */ USB_ERR_NOT_STARTED, /* 2 */ USB_ERR_INVAL, /* 3 */ USB_ERR_NOMEM, /* 4 */ USB_ERR_CANCELLED, /* 5 */ USB_ERR_BAD_ADDRESS, /* 6 */ USB_ERR_BAD_BUFSIZE, /* 7 */ USB_ERR_BAD_FLAG, /* 8 */ USB_ERR_NO_CALLBACK, /* 9 */ USB_ERR_IN_USE, /* 10 */ USB_ERR_NO_ADDR, /* 11 */ USB_ERR_NO_PIPE, /* 12 */ USB_ERR_ZERO_NFRAMES, /* 13 */ USB_ERR_ZERO_MAXP, /* 14 */ USB_ERR_SET_ADDR_FAILED, /* 15 */ USB_ERR_NO_POWER, /* 16 */ USB_ERR_TOO_DEEP, /* 17 */ USB_ERR_IOERROR, /* 18 */ USB_ERR_NOT_CONFIGURED, /* 19 */ USB_ERR_TIMEOUT, /* 20 */ USB_ERR_SHORT_XFER, /* 21 */ USB_ERR_STALLED, /* 22 */ USB_ERR_INTERRUPTED, /* 23 */ USB_ERR_DMA_LOAD_FAILED, /* 24 */ USB_ERR_BAD_CONTEXT, /* 25 */ USB_ERR_NO_ROOT_HUB, /* 26 */ USB_ERR_NO_INTR_THREAD, /* 27 */ USB_ERR_NOT_LOCKED, /* 28 */ USB_ERR_MAX } usb_error_t; /* * Flags for transfers */ #define USB_FORCE_SHORT_XFER 0x0001 /* force a short transmit last */ #define USB_SHORT_XFER_OK 0x0004 /* allow short reads */ #define USB_DELAY_STATUS_STAGE 0x0010 /* insert delay before STATUS stage */ #define USB_USER_DATA_PTR 0x0020 /* internal flag */ #define USB_MULTI_SHORT_OK 0x0040 /* allow multiple short frames */ #define USB_MANUAL_STATUS 0x0080 /* manual ctrl status */ #define USB_NO_TIMEOUT 0 #define USB_DEFAULT_TIMEOUT 5000 /* 5000 ms = 5 seconds */ #if defined(_KERNEL) || defined(_STANDALONE) /* typedefs */ typedef void (usb_callback_t)(struct usb_xfer *, usb_error_t); typedef void (usb_proc_callback_t)(struct usb_proc_msg *); typedef usb_error_t (usb_handle_req_t)(struct usb_device *, struct usb_device_request *, const void **, uint16_t *); typedef int (usb_fifo_open_t)(struct usb_fifo *fifo, int fflags); typedef void (usb_fifo_close_t)(struct usb_fifo *fifo, int fflags); typedef int (usb_fifo_ioctl_t)(struct usb_fifo *fifo, u_long cmd, void *addr, int fflags); 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 */ #ifndef USB_GLOBAL_INCLUDE_FILE #include #endif 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 * full division. */ #define USB_MS_HZ 1024 #define USB_MS_TO_TICKS(ms) \ (((uint32_t)((((uint32_t)(ms)) * ((uint32_t)(hz))) + USB_MS_HZ - 1)) / USB_MS_HZ) /* * Common queue structure for USB transfers. */ struct usb_xfer_queue { TAILQ_HEAD(, usb_xfer) head; struct usb_xfer *curr; /* current USB transfer processed */ void (*command) (struct usb_xfer_queue *pq); uint8_t recurse_1:1; uint8_t recurse_2:1; uint8_t recurse_3:1; uint8_t reserved:5; }; /* * The following structure defines an USB endpoint * USB endpoint. */ struct usb_endpoint { /* queue of USB transfers */ struct usb_xfer_queue endpoint_q[USB_MAX_EP_STREAMS]; struct usb_endpoint_descriptor *edesc; struct usb_endpoint_ss_comp_descriptor *ecomp; const struct usb_pipe_methods *methods; /* set by HC driver */ uint16_t isoc_next; uint8_t toggle_next:1; /* next data toggle value */ uint8_t is_stalled:1; /* set if endpoint is stalled */ uint8_t is_synced:1; /* set if we a synchronised */ uint8_t unused:5; uint8_t iface_index; /* not used by "default endpoint" */ uint8_t refcount_alloc; /* allocation refcount */ uint8_t refcount_bw; /* bandwidth refcount */ #define USB_EP_REF_MAX 0x3f /* High-Speed resource allocation (valid if "refcount_bw" > 0) */ uint8_t usb_smask; /* USB start mask */ uint8_t usb_cmask; /* USB complete mask */ uint8_t usb_uframe; /* USB microframe */ /* USB endpoint mode, see USB_EP_MODE_XXX */ uint8_t ep_mode; }; /* * The following structure defines an USB interface. */ struct usb_interface { struct usb_interface_descriptor *idesc; device_t subdev; /* Total number of alternate settings, from 1 to 256 */ uint16_t num_altsetting; /* Current alternate interface index, from 0 to 255 */ uint8_t alt_index; uint8_t parent_iface_index; /* Linux compat */ struct usb_host_interface *altsetting; struct usb_host_interface *cur_altsetting; struct usb_device *linux_udev; void *bsd_priv_sc; /* device specific information */ char *pnpinfo; /* additional PnP-info for this interface */ uint8_t bsd_iface_index; }; /* * The following structure defines a set of USB transfer flags. */ struct usb_xfer_flags { uint8_t force_short_xfer:1; /* force a short transmit transfer * last */ uint8_t short_xfer_ok:1; /* allow short receive transfers */ uint8_t short_frames_ok:1; /* allow short frames */ uint8_t pipe_bof:1; /* block pipe on failure */ uint8_t proxy_buffer:1; /* makes buffer size a factor of * "max_frame_size" */ uint8_t ext_buffer:1; /* uses external DMA buffer */ uint8_t manual_status:1; /* non automatic status stage on * control transfers */ uint8_t no_pipe_ok:1; /* set if "USB_ERR_NO_PIPE" error can * be ignored */ uint8_t stall_pipe:1; /* set if the endpoint belonging to * this USB transfer should be stalled * before starting this transfer! */ uint8_t pre_scale_frames:1; /* "usb_config->frames" is * assumed to give the * buffering time in * milliseconds and is * converted into the nearest * number of frames when the * USB transfer is setup. This * option only has effect for * ISOCHRONOUS transfers. */ + uint8_t send_zlp:1; /* send a zero length packet first */ }; /* * The following structure define an USB configuration, that basically * is used when setting up an USB transfer. */ struct usb_config { usb_callback_t *callback; /* USB transfer callback */ usb_frlength_t bufsize; /* total pipe buffer size in bytes */ usb_frcount_t frames; /* maximum number of USB frames */ usb_timeout_t interval; /* interval in milliseconds */ #define USB_DEFAULT_INTERVAL 0 usb_timeout_t timeout; /* transfer timeout in milliseconds */ struct usb_xfer_flags flags; /* transfer flags */ usb_stream_t stream_id; /* USB3.0 specific */ enum usb_hc_mode usb_mode; /* host or device mode */ uint8_t type; /* pipe type */ uint8_t endpoint; /* pipe number */ uint8_t direction; /* pipe direction */ uint8_t ep_index; /* pipe index match to use */ uint8_t if_index; /* "ifaces" index to use */ }; /* * Use these macro when defining USB device ID arrays if you want to * have your driver module automatically loaded in host, device or * both modes respectively: */ #if USB_HAVE_ID_SECTION #define STRUCT_USB_HOST_ID \ struct usb_device_id __section("usb_host_id") #define STRUCT_USB_DEVICE_ID \ struct usb_device_id __section("usb_device_id") #define STRUCT_USB_DUAL_ID \ struct usb_device_id __section("usb_dual_id") #else #define STRUCT_USB_HOST_ID \ struct usb_device_id #define STRUCT_USB_DEVICE_ID \ struct usb_device_id #define STRUCT_USB_DUAL_ID \ struct usb_device_id #endif /* USB_HAVE_ID_SECTION */ /* * The following structure is used when looking up an USB driver for * an USB device. It is inspired by the Linux structure called * "usb_device_id". */ struct usb_device_id { /* Select which fields to match against */ #if BYTE_ORDER == LITTLE_ENDIAN uint16_t match_flag_vendor:1, match_flag_product:1, match_flag_dev_lo:1, match_flag_dev_hi:1, match_flag_dev_class:1, match_flag_dev_subclass:1, match_flag_dev_protocol:1, match_flag_int_class:1, match_flag_int_subclass:1, match_flag_int_protocol:1, match_flag_unused:6; #else uint16_t match_flag_unused:6, match_flag_int_protocol:1, match_flag_int_subclass:1, match_flag_int_class:1, match_flag_dev_protocol:1, match_flag_dev_subclass:1, match_flag_dev_class:1, match_flag_dev_hi:1, match_flag_dev_lo:1, match_flag_product:1, match_flag_vendor:1; #endif /* Used for product specific matches; the BCD range is inclusive */ uint16_t idVendor; uint16_t idProduct; uint16_t bcdDevice_lo; uint16_t bcdDevice_hi; /* Used for device class matches */ uint8_t bDeviceClass; uint8_t bDeviceSubClass; uint8_t bDeviceProtocol; /* Used for interface class matches */ uint8_t bInterfaceClass; uint8_t bInterfaceSubClass; uint8_t bInterfaceProtocol; #if USB_HAVE_COMPAT_LINUX /* which fields to match against */ uint16_t match_flags; #define USB_DEVICE_ID_MATCH_VENDOR 0x0001 #define USB_DEVICE_ID_MATCH_PRODUCT 0x0002 #define USB_DEVICE_ID_MATCH_DEV_LO 0x0004 #define USB_DEVICE_ID_MATCH_DEV_HI 0x0008 #define USB_DEVICE_ID_MATCH_DEV_CLASS 0x0010 #define USB_DEVICE_ID_MATCH_DEV_SUBCLASS 0x0020 #define USB_DEVICE_ID_MATCH_DEV_PROTOCOL 0x0040 #define USB_DEVICE_ID_MATCH_INT_CLASS 0x0080 #define USB_DEVICE_ID_MATCH_INT_SUBCLASS 0x0100 #define USB_DEVICE_ID_MATCH_INT_PROTOCOL 0x0200 #endif /* Hook for driver specific information */ unsigned long driver_info; } __aligned(32); #define USB_STD_PNP_INFO "M16:mask;U16:vendor;U16:product;L16:release;G16:release;" \ "U8:devclass;U8:devsubclass;U8:devproto;" \ "U8:intclass;U8:intsubclass;U8:intprotocol;" #define USB_STD_PNP_HOST_INFO USB_STD_PNP_INFO "T:mode=host;" #define USB_STD_PNP_DEVICE_INFO USB_STD_PNP_INFO "T:mode=device;" #define USB_PNP_HOST_INFO(table) \ MODULE_PNP_INFO(USB_STD_PNP_HOST_INFO, uhub, table, table, \ sizeof(table) / sizeof(table[0])) #define USB_PNP_DEVICE_INFO(table) \ MODULE_PNP_INFO(USB_STD_PNP_DEVICE_INFO, uhub, table, table, \ sizeof(table) / sizeof(table[0])) #define USB_PNP_DUAL_INFO(table) \ MODULE_PNP_INFO(USB_STD_PNP_INFO, uhub, table, table, \ sizeof(table) / sizeof(table[0])) /* check that the size of the structure above is correct */ extern char usb_device_id_assert[(sizeof(struct usb_device_id) == 32) ? 1 : -1]; #define USB_VENDOR(vend) \ .match_flag_vendor = 1, .idVendor = (vend) #define USB_PRODUCT(prod) \ .match_flag_product = 1, .idProduct = (prod) #define USB_VP(vend,prod) \ USB_VENDOR(vend), USB_PRODUCT(prod) #define USB_VPI(vend,prod,info) \ USB_VENDOR(vend), USB_PRODUCT(prod), USB_DRIVER_INFO(info) #define USB_DEV_BCD_GTEQ(lo) /* greater than or equal */ \ .match_flag_dev_lo = 1, .bcdDevice_lo = (lo) #define USB_DEV_BCD_LTEQ(hi) /* less than or equal */ \ .match_flag_dev_hi = 1, .bcdDevice_hi = (hi) #define USB_DEV_CLASS(dc) \ .match_flag_dev_class = 1, .bDeviceClass = (dc) #define USB_DEV_SUBCLASS(dsc) \ .match_flag_dev_subclass = 1, .bDeviceSubClass = (dsc) #define USB_DEV_PROTOCOL(dp) \ .match_flag_dev_protocol = 1, .bDeviceProtocol = (dp) #define USB_IFACE_CLASS(ic) \ .match_flag_int_class = 1, .bInterfaceClass = (ic) #define USB_IFACE_SUBCLASS(isc) \ .match_flag_int_subclass = 1, .bInterfaceSubClass = (isc) #define USB_IFACE_PROTOCOL(ip) \ .match_flag_int_protocol = 1, .bInterfaceProtocol = (ip) #define USB_IF_CSI(class,subclass,info) \ USB_IFACE_CLASS(class), USB_IFACE_SUBCLASS(subclass), USB_DRIVER_INFO(info) #define USB_DRIVER_INFO(n) \ .driver_info = (n) #define USB_GET_DRIVER_INFO(did) \ (did)->driver_info /* * The following structure keeps information that is used to match * against an array of "usb_device_id" elements. */ struct usbd_lookup_info { uint16_t idVendor; uint16_t idProduct; uint16_t bcdDevice; uint8_t bDeviceClass; uint8_t bDeviceSubClass; uint8_t bDeviceProtocol; uint8_t bInterfaceClass; uint8_t bInterfaceSubClass; uint8_t bInterfaceProtocol; uint8_t bIfaceIndex; uint8_t bIfaceNum; uint8_t bConfigIndex; uint8_t bConfigNum; }; /* Structure used by probe and attach */ struct usb_attach_arg { struct usbd_lookup_info info; device_t temp_dev; /* for internal use */ unsigned long driver_info; /* for internal use */ void *driver_ivar; struct usb_device *device; /* current device */ struct usb_interface *iface; /* current interface */ enum usb_hc_mode usb_mode; /* host or device mode */ uint8_t port; uint8_t dev_state; #define UAA_DEV_READY 0 #define UAA_DEV_DISABLED 1 #define UAA_DEV_EJECTING 2 }; /* * General purpose locking wrappers to ease supporting * USB polled mode: */ #ifdef INVARIANTS #define USB_MTX_ASSERT(_m, _t) do { \ if (!USB_IN_POLLING_MODE_FUNC()) \ mtx_assert(_m, _t); \ } while (0) #else #define USB_MTX_ASSERT(_m, _t) do { } while (0) #endif #define USB_MTX_LOCK(_m) do { \ if (!USB_IN_POLLING_MODE_FUNC()) \ mtx_lock(_m); \ } while (0) #define USB_MTX_UNLOCK(_m) do { \ if (!USB_IN_POLLING_MODE_FUNC()) \ mtx_unlock(_m); \ } while (0) #define USB_MTX_LOCK_SPIN(_m) do { \ if (!USB_IN_POLLING_MODE_FUNC()) \ mtx_lock_spin(_m); \ } while (0) #define USB_MTX_UNLOCK_SPIN(_m) do { \ if (!USB_IN_POLLING_MODE_FUNC()) \ mtx_unlock_spin(_m); \ } while (0) /* * The following is a wrapper for the callout structure to ease * porting the code to other platforms. */ struct usb_callout { struct callout co; }; #define usb_callout_init_mtx(c,m,f) callout_init_mtx(&(c)->co,m,f) #define usb_callout_reset(c,...) do { \ if (!USB_IN_POLLING_MODE_FUNC()) \ callout_reset(&(c)->co, __VA_ARGS__); \ } while (0) #define usb_callout_reset_sbt(c,...) do { \ if (!USB_IN_POLLING_MODE_FUNC()) \ callout_reset_sbt(&(c)->co, __VA_ARGS__); \ } while (0) #define usb_callout_stop(c) do { \ if (!USB_IN_POLLING_MODE_FUNC()) { \ callout_stop(&(c)->co); \ } else { \ /* \ * Cannot stop callout when \ * polling. Set dummy callback \ * function instead: \ */ \ (c)->co.c_func = &usbd_dummy_timeout; \ } \ } while (0) #define usb_callout_drain(c) callout_drain(&(c)->co) #define usb_callout_pending(c) callout_pending(&(c)->co) /* USB transfer states */ #define USB_ST_SETUP 0 #define USB_ST_TRANSFERRED 1 #define USB_ST_ERROR 2 /* USB handle request states */ #define USB_HR_NOT_COMPLETE 0 #define USB_HR_COMPLETE_OK 1 #define USB_HR_COMPLETE_ERR 2 /* * The following macro will return the current state of an USB * transfer like defined by the "USB_ST_XXX" enums. */ #define USB_GET_STATE(xfer) (usbd_xfer_state(xfer)) /* * The following structure defines the USB process message header. */ struct usb_proc_msg { TAILQ_ENTRY(usb_proc_msg) pm_qentry; usb_proc_callback_t *pm_callback; usb_size_t pm_num; }; #define USB_FIFO_TX 0 #define USB_FIFO_RX 1 /* * Locking note for the following functions. All the * "usb_fifo_cmd_t" and "usb_fifo_filter_t" functions are called * locked. The others are called unlocked. */ struct usb_fifo_methods { usb_fifo_open_t *f_open; usb_fifo_close_t *f_close; usb_fifo_ioctl_t *f_ioctl; /* * NOTE: The post-ioctl callback is called after the USB reference * gets locked in the IOCTL handler: */ usb_fifo_ioctl_t *f_ioctl_post; usb_fifo_cmd_t *f_start_read; usb_fifo_cmd_t *f_stop_read; usb_fifo_cmd_t *f_start_write; usb_fifo_cmd_t *f_stop_write; usb_fifo_filter_t *f_filter_read; usb_fifo_filter_t *f_filter_write; const char *basename[4]; const char *postfix[4]; }; struct usb_fifo_sc { struct usb_fifo *fp[2]; struct usb_fs_privdata *dev; }; const char *usbd_errstr(usb_error_t error); void *usbd_find_descriptor(struct usb_device *udev, void *id, uint8_t iface_index, uint8_t type, uint8_t type_mask, uint8_t subtype, uint8_t subtype_mask); struct usb_config_descriptor *usbd_get_config_descriptor( struct usb_device *udev); struct usb_device_descriptor *usbd_get_device_descriptor( struct usb_device *udev); struct usb_interface *usbd_get_iface(struct usb_device *udev, uint8_t iface_index); struct usb_interface_descriptor *usbd_get_interface_descriptor( struct usb_interface *iface); struct usb_endpoint *usbd_get_endpoint(struct usb_device *udev, uint8_t iface_index, const struct usb_config *setup); struct usb_endpoint *usbd_get_ep_by_addr(struct usb_device *udev, uint8_t ea_val); usb_error_t usbd_interface_count(struct usb_device *udev, uint8_t *count); enum usb_hc_mode usbd_get_mode(struct usb_device *udev); enum usb_dev_speed usbd_get_speed(struct usb_device *udev); void device_set_usb_desc(device_t dev); void usb_pause_mtx(struct mtx *mtx, int _ticks); usb_error_t usbd_set_pnpinfo(struct usb_device *udev, uint8_t iface_index, const char *pnpinfo); usb_error_t usbd_add_dynamic_quirk(struct usb_device *udev, uint16_t quirk); usb_error_t usbd_set_endpoint_mode(struct usb_device *udev, struct usb_endpoint *ep, uint8_t ep_mode); uint8_t usbd_get_endpoint_mode(struct usb_device *udev, struct usb_endpoint *ep); const struct usb_device_id *usbd_lookup_id_by_info( const struct usb_device_id *id, usb_size_t sizeof_id, const struct usbd_lookup_info *info); int usbd_lookup_id_by_uaa(const struct usb_device_id *id, usb_size_t sizeof_id, struct usb_attach_arg *uaa); usb_error_t usbd_do_request_flags(struct usb_device *udev, struct mtx *mtx, struct usb_device_request *req, void *data, uint16_t flags, uint16_t *actlen, usb_timeout_t timeout); #define usbd_do_request(u,m,r,d) \ usbd_do_request_flags(u,m,r,d,0,NULL,USB_DEFAULT_TIMEOUT) uint8_t usbd_clear_stall_callback(struct usb_xfer *xfer1, struct usb_xfer *xfer2); uint8_t usbd_get_interface_altindex(struct usb_interface *iface); usb_error_t usbd_set_alt_interface_index(struct usb_device *udev, uint8_t iface_index, uint8_t alt_index); uint32_t usbd_get_isoc_fps(struct usb_device *udev); uint32_t usbd_get_max_frame_length(const struct usb_endpoint_descriptor *, const struct usb_endpoint_ss_comp_descriptor *, enum usb_dev_speed); usb_error_t usbd_transfer_setup(struct usb_device *udev, const uint8_t *ifaces, struct usb_xfer **pxfer, const struct usb_config *setup_start, uint16_t n_setup, void *priv_sc, struct mtx *priv_mtx); void usbd_transfer_submit(struct usb_xfer *xfer); void usbd_transfer_clear_stall(struct usb_xfer *xfer); void usbd_transfer_drain(struct usb_xfer *xfer); uint8_t usbd_transfer_pending(struct usb_xfer *xfer); void usbd_transfer_start(struct usb_xfer *xfer); void usbd_transfer_stop(struct usb_xfer *xfer); void usbd_transfer_unsetup(struct usb_xfer **pxfer, uint16_t n_setup); void usbd_transfer_poll(struct usb_xfer **ppxfer, uint16_t max); void usbd_set_parent_iface(struct usb_device *udev, uint8_t iface_index, uint8_t parent_index); uint8_t usbd_get_bus_index(struct usb_device *udev); uint8_t usbd_get_device_index(struct usb_device *udev); void usbd_set_power_mode(struct usb_device *udev, uint8_t power_mode); uint8_t usbd_filter_power_mode(struct usb_device *udev, uint8_t power_mode); uint8_t usbd_device_attached(struct usb_device *udev); usb_frlength_t usbd_xfer_old_frame_length(struct usb_xfer *xfer, usb_frcount_t frindex); void usbd_xfer_status(struct usb_xfer *xfer, int *actlen, int *sumlen, int *aframes, int *nframes); struct usb_page_cache *usbd_xfer_get_frame(struct usb_xfer *, usb_frcount_t); void *usbd_xfer_get_frame_buffer(struct usb_xfer *, usb_frcount_t); void *usbd_xfer_softc(struct usb_xfer *xfer); void *usbd_xfer_get_priv(struct usb_xfer *xfer); void usbd_xfer_set_priv(struct usb_xfer *xfer, void *); void usbd_xfer_set_interval(struct usb_xfer *xfer, int); uint8_t usbd_xfer_state(struct usb_xfer *xfer); void usbd_xfer_set_frame_data(struct usb_xfer *xfer, usb_frcount_t frindex, void *ptr, usb_frlength_t len); void usbd_xfer_frame_data(struct usb_xfer *xfer, usb_frcount_t frindex, void **ptr, int *len); void usbd_xfer_set_frame_offset(struct usb_xfer *xfer, usb_frlength_t offset, usb_frcount_t frindex); usb_frlength_t usbd_xfer_max_len(struct usb_xfer *xfer); usb_frlength_t usbd_xfer_max_framelen(struct usb_xfer *xfer); usb_frcount_t usbd_xfer_max_frames(struct usb_xfer *xfer); uint8_t usbd_xfer_get_fps_shift(struct usb_xfer *xfer); usb_frlength_t usbd_xfer_frame_len(struct usb_xfer *xfer, usb_frcount_t frindex); void usbd_xfer_set_frame_len(struct usb_xfer *xfer, usb_frcount_t frindex, usb_frlength_t len); void usbd_xfer_set_timeout(struct usb_xfer *xfer, int timeout); void usbd_xfer_set_frames(struct usb_xfer *xfer, usb_frcount_t n); +void usbd_xfer_set_zlp(struct usb_xfer *xfer); +uint8_t usbd_xfer_get_and_clr_zlp(struct usb_xfer *xfer); void usbd_xfer_set_stall(struct usb_xfer *xfer); int usbd_xfer_is_stalled(struct usb_xfer *xfer); void usbd_xfer_set_flag(struct usb_xfer *xfer, int flag); void usbd_xfer_clr_flag(struct usb_xfer *xfer, int flag); uint16_t usbd_xfer_get_timestamp(struct usb_xfer *xfer); uint8_t usbd_xfer_maxp_was_clamped(struct usb_xfer *xfer); void usbd_copy_in(struct usb_page_cache *cache, usb_frlength_t offset, const void *ptr, usb_frlength_t len); int usbd_copy_in_user(struct usb_page_cache *cache, usb_frlength_t offset, const void *ptr, usb_frlength_t len); void usbd_copy_out(struct usb_page_cache *cache, usb_frlength_t offset, void *ptr, usb_frlength_t len); int usbd_copy_out_user(struct usb_page_cache *cache, usb_frlength_t offset, void *ptr, usb_frlength_t len); void usbd_get_page(struct usb_page_cache *pc, usb_frlength_t offset, struct usb_page_search *res); void usbd_m_copy_in(struct usb_page_cache *cache, usb_frlength_t dst_offset, struct mbuf *m, usb_size_t src_offset, usb_frlength_t src_len); void usbd_frame_zero(struct usb_page_cache *cache, usb_frlength_t offset, usb_frlength_t len); void usbd_start_re_enumerate(struct usb_device *udev); usb_error_t usbd_start_set_config(struct usb_device *, uint8_t); int usbd_in_polling_mode(void); void usbd_dummy_timeout(void *); int usb_fifo_attach(struct usb_device *udev, void *priv_sc, struct mtx *priv_mtx, struct usb_fifo_methods *pm, struct usb_fifo_sc *f_sc, uint16_t unit, int16_t subunit, uint8_t iface_index, uid_t uid, gid_t gid, int mode); void usb_fifo_detach(struct usb_fifo_sc *f_sc); int usb_fifo_alloc_buffer(struct usb_fifo *f, uint32_t bufsize, uint16_t nbuf); void usb_fifo_free_buffer(struct usb_fifo *f); uint32_t usb_fifo_put_bytes_max(struct usb_fifo *fifo); void usb_fifo_put_data(struct usb_fifo *fifo, struct usb_page_cache *pc, usb_frlength_t offset, usb_frlength_t len, uint8_t what); void usb_fifo_put_data_linear(struct usb_fifo *fifo, void *ptr, usb_size_t len, uint8_t what); uint8_t usb_fifo_put_data_buffer(struct usb_fifo *f, void *ptr, usb_size_t len); void usb_fifo_put_data_error(struct usb_fifo *fifo); uint8_t usb_fifo_get_data(struct usb_fifo *fifo, struct usb_page_cache *pc, usb_frlength_t offset, usb_frlength_t len, usb_frlength_t *actlen, uint8_t what); uint8_t usb_fifo_get_data_linear(struct usb_fifo *fifo, void *ptr, usb_size_t len, usb_size_t *actlen, uint8_t what); uint8_t usb_fifo_get_data_buffer(struct usb_fifo *f, void **pptr, usb_size_t *plen); void usb_fifo_reset(struct usb_fifo *f); void usb_fifo_wakeup(struct usb_fifo *f); void usb_fifo_get_data_error(struct usb_fifo *fifo); void *usb_fifo_softc(struct usb_fifo *fifo); void usb_fifo_set_close_zlp(struct usb_fifo *, uint8_t); void usb_fifo_set_write_defrag(struct usb_fifo *, uint8_t); void usb_fifo_free(struct usb_fifo *f); #endif /* _KERNEL || _STANDALONE */ #endif /* _USB_USBDI_H_ */