xref: /freebsd/sys/dev/usb/serial/ugensa.c (revision 5956d97f4b3204318ceb6aa9c77bd0bc6ea87a41)
1  /* $FreeBSD$ */
2  /*	$NetBSD: ugensa.c,v 1.9.2.1 2007/03/24 14:55:50 yamt Exp $	*/
3  
4  /*-
5   * SPDX-License-Identifier: BSD-2-Clause-NetBSD
6   *
7   * Copyright (c) 2004, 2005 The NetBSD Foundation, Inc.
8   * All rights reserved.
9   *
10   * This code is derived from software contributed to The NetBSD Foundation
11   * by Roland C. Dowdeswell <elric@netbsd.org>.
12   *
13   * Redistribution and use in source and binary forms, with or without
14   * modification, are permitted provided that the following conditions
15   * are met:
16   * 1. Redistributions of source code must retain the above copyright
17   *    notice, this list of conditions and the following disclaimer.
18   * 2. Redistributions in binary form must reproduce the above copyright
19   *    notice, this list of conditions and the following disclaimer in the
20   *    documentation and/or other materials provided with the distribution.
21   *
22   * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
23   * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
24   * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
25   * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
26   * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29   * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30   * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32   * POSSIBILITY OF SUCH DAMAGE.
33   */
34  
35  /*
36   * NOTE: all function names beginning like "ugensa_cfg_" can only
37   * be called from within the config thread function !
38   */
39  
40  #include <sys/stdint.h>
41  #include <sys/stddef.h>
42  #include <sys/param.h>
43  #include <sys/queue.h>
44  #include <sys/types.h>
45  #include <sys/systm.h>
46  #include <sys/kernel.h>
47  #include <sys/bus.h>
48  #include <sys/module.h>
49  #include <sys/lock.h>
50  #include <sys/mutex.h>
51  #include <sys/condvar.h>
52  #include <sys/sysctl.h>
53  #include <sys/sx.h>
54  #include <sys/unistd.h>
55  #include <sys/callout.h>
56  #include <sys/malloc.h>
57  #include <sys/priv.h>
58  
59  #include <dev/usb/usb.h>
60  #include <dev/usb/usbdi.h>
61  #include "usbdevs.h"
62  
63  #define	USB_DEBUG_VAR usb_debug
64  #include <dev/usb/usb_debug.h>
65  #include <dev/usb/usb_process.h>
66  
67  #include <dev/usb/serial/usb_serial.h>
68  
69  #define	UGENSA_BUF_SIZE		2048	/* bytes */
70  #define	UGENSA_CONFIG_INDEX	0
71  #define	UGENSA_IFACE_INDEX	0
72  #define	UGENSA_IFACE_MAX	8	/* exclusivly */
73  #define	UGENSA_PORT_MAX		8	/* exclusivly */
74  
75  enum {
76  	UGENSA_BULK_DT_WR,
77  	UGENSA_BULK_DT_RD,
78  	UGENSA_N_TRANSFER,
79  };
80  
81  struct ugensa_sub_softc {
82  	struct ucom_softc *sc_ucom_ptr;
83  	struct usb_xfer *sc_xfer[UGENSA_N_TRANSFER];
84  };
85  
86  struct ugensa_softc {
87  	struct ucom_super_softc sc_super_ucom;
88  	struct ucom_softc sc_ucom[UGENSA_PORT_MAX];
89  	struct ugensa_sub_softc sc_sub[UGENSA_PORT_MAX];
90  
91  	struct mtx sc_mtx;
92  	uint8_t	sc_nports;
93  };
94  
95  /* prototypes */
96  
97  static device_probe_t ugensa_probe;
98  static device_attach_t ugensa_attach;
99  static device_detach_t ugensa_detach;
100  static void ugensa_free_softc(struct ugensa_softc *);
101  
102  static usb_callback_t ugensa_bulk_write_callback;
103  static usb_callback_t ugensa_bulk_read_callback;
104  
105  static void	ugensa_free(struct ucom_softc *);
106  static void	ugensa_start_read(struct ucom_softc *);
107  static void	ugensa_stop_read(struct ucom_softc *);
108  static void	ugensa_start_write(struct ucom_softc *);
109  static void	ugensa_stop_write(struct ucom_softc *);
110  static void	ugensa_poll(struct ucom_softc *ucom);
111  
112  static const struct usb_config ugensa_xfer_config[UGENSA_N_TRANSFER] = {
113  	[UGENSA_BULK_DT_WR] = {
114  		.type = UE_BULK,
115  		.endpoint = UE_ADDR_ANY,
116  		.direction = UE_DIR_OUT,
117  		.bufsize = UGENSA_BUF_SIZE,
118  		.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
119  		.callback = &ugensa_bulk_write_callback,
120  	},
121  
122  	[UGENSA_BULK_DT_RD] = {
123  		.type = UE_BULK,
124  		.endpoint = UE_ADDR_ANY,
125  		.direction = UE_DIR_IN,
126  		.bufsize = UGENSA_BUF_SIZE,
127  		.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
128  		.callback = &ugensa_bulk_read_callback,
129  	},
130  };
131  
132  static const struct ucom_callback ugensa_callback = {
133  	.ucom_start_read = &ugensa_start_read,
134  	.ucom_stop_read = &ugensa_stop_read,
135  	.ucom_start_write = &ugensa_start_write,
136  	.ucom_stop_write = &ugensa_stop_write,
137  	.ucom_poll = &ugensa_poll,
138  	.ucom_free = &ugensa_free,
139  };
140  
141  static device_method_t ugensa_methods[] = {
142  	/* Device methods */
143  	DEVMETHOD(device_probe, ugensa_probe),
144  	DEVMETHOD(device_attach, ugensa_attach),
145  	DEVMETHOD(device_detach, ugensa_detach),
146  	DEVMETHOD_END
147  };
148  
149  static driver_t ugensa_driver = {
150  	.name = "ugensa",
151  	.methods = ugensa_methods,
152  	.size = sizeof(struct ugensa_softc),
153  };
154  
155  /* Driver-info is max number of serial ports per interface */
156  static const STRUCT_USB_HOST_ID ugensa_devs[] = {
157  	{USB_VPI(USB_VENDOR_AIRPRIME, USB_PRODUCT_AIRPRIME_PC5220, 1)},
158  	{USB_VPI(USB_VENDOR_CMOTECH, USB_PRODUCT_CMOTECH_CDMA_MODEM1, 1)},
159  	{USB_VPI(USB_VENDOR_KYOCERA2, USB_PRODUCT_KYOCERA2_CDMA_MSM_K, 1)},
160  	{USB_VPI(USB_VENDOR_HP, USB_PRODUCT_HP_49GPLUS, 1)},
161  	{USB_VPI(USB_VENDOR_NOVATEL2, USB_PRODUCT_NOVATEL2_FLEXPACKGPS, 3)},
162  	{USB_VENDOR(USB_VENDOR_GOOGLE), USB_IFACE_CLASS(UICLASS_VENDOR),
163  		USB_IFACE_SUBCLASS(0x50), USB_IFACE_PROTOCOL(0x01), USB_DRIVER_INFO(10)},
164  };
165  
166  DRIVER_MODULE(ugensa, uhub, ugensa_driver, NULL, NULL);
167  MODULE_DEPEND(ugensa, ucom, 1, 1, 1);
168  MODULE_DEPEND(ugensa, usb, 1, 1, 1);
169  MODULE_VERSION(ugensa, 1);
170  USB_PNP_HOST_INFO(ugensa_devs);
171  
172  static int
173  ugensa_probe(device_t dev)
174  {
175  	struct usb_attach_arg *uaa = device_get_ivars(dev);
176  
177  	if (uaa->usb_mode != USB_MODE_HOST) {
178  		return (ENXIO);
179  	}
180  	if (uaa->info.bConfigIndex != UGENSA_CONFIG_INDEX) {
181  		return (ENXIO);
182  	}
183  	if (uaa->info.bIfaceIndex != 0) {
184  		return (ENXIO);
185  	}
186  	return (usbd_lookup_id_by_uaa(ugensa_devs, sizeof(ugensa_devs), uaa));
187  }
188  
189  static int
190  ugensa_attach(device_t dev)
191  {
192  	struct usb_attach_arg *uaa = device_get_ivars(dev);
193  	struct ugensa_softc *sc = device_get_softc(dev);
194  	struct ugensa_sub_softc *ssc;
195  	struct usb_interface *iface;
196  	struct usb_config xfer_config[UGENSA_N_TRANSFER];
197  	int32_t error;
198  	uint8_t iface_index;
199  	int x, maxports;
200  
201  	maxports = USB_GET_DRIVER_INFO(uaa);
202  	device_set_usb_desc(dev);
203  	mtx_init(&sc->sc_mtx, "ugensa", NULL, MTX_DEF);
204  	ucom_ref(&sc->sc_super_ucom);
205  
206  	for (iface_index = UGENSA_IFACE_INDEX; iface_index < UGENSA_IFACE_MAX; iface_index++) {
207  		iface = usbd_get_iface(uaa->device, iface_index);
208  		if (iface == NULL || iface->idesc->bInterfaceClass != UICLASS_VENDOR)
209  			/* Not a serial port, most likely a SD reader */
210  			continue;
211  
212  		/* Loop over all endpoints pairwise */
213  		for (x = 0; x < maxports && sc->sc_nports < UGENSA_PORT_MAX; x++) {
214  			ssc = sc->sc_sub + sc->sc_nports;
215  			ssc->sc_ucom_ptr = sc->sc_ucom + sc->sc_nports;
216  
217  			memcpy(xfer_config, ugensa_xfer_config, sizeof ugensa_xfer_config);
218  			xfer_config[UGENSA_BULK_DT_RD].ep_index = x;
219  			xfer_config[UGENSA_BULK_DT_WR].ep_index = x;
220  
221  			error = usbd_transfer_setup(uaa->device,
222  			    &iface_index, ssc->sc_xfer, xfer_config,
223  			    UGENSA_N_TRANSFER, ssc, &sc->sc_mtx);
224  
225  			if (error) {
226  				if (x == 0) {
227  					device_printf(dev, "allocating USB "
228  					    "transfers failed (%d)\n", error);
229  					goto detach;
230  				}
231  				break;
232  			}
233  
234  			/* clear stall at first run */
235  			mtx_lock(&sc->sc_mtx);
236  			usbd_xfer_set_stall(ssc->sc_xfer[UGENSA_BULK_DT_WR]);
237  			usbd_xfer_set_stall(ssc->sc_xfer[UGENSA_BULK_DT_RD]);
238  			mtx_unlock(&sc->sc_mtx);
239  
240  			/* initialize port number */
241  			ssc->sc_ucom_ptr->sc_portno = sc->sc_nports;
242  			if (iface_index != uaa->info.bIfaceIndex) {
243  				usbd_set_parent_iface(uaa->device, iface_index,
244  				    uaa->info.bIfaceIndex);
245  			}
246  			sc->sc_nports++;
247  		}
248  	}
249  	device_printf(dev, "Found %d serial ports.\n", sc->sc_nports);
250  
251  	error = ucom_attach(&sc->sc_super_ucom, sc->sc_ucom, sc->sc_nports, sc,
252  	    &ugensa_callback, &sc->sc_mtx);
253  
254  	if (error) {
255  		DPRINTF("ucom attach failed\n");
256  		goto detach;
257  	}
258  	ucom_set_pnpinfo_usb(&sc->sc_super_ucom, dev);
259  
260  	return (0);			/* success */
261  
262  detach:
263  	ugensa_detach(dev);
264  	return (ENXIO);			/* failure */
265  }
266  
267  static int
268  ugensa_detach(device_t dev)
269  {
270  	struct ugensa_softc *sc = device_get_softc(dev);
271  	uint8_t x;
272  
273  	ucom_detach(&sc->sc_super_ucom, sc->sc_ucom);
274  
275  	for (x = 0; x < sc->sc_nports; x++) {
276  		usbd_transfer_unsetup(sc->sc_sub[x].sc_xfer, UGENSA_N_TRANSFER);
277  	}
278  
279  	device_claim_softc(dev);
280  
281  	ugensa_free_softc(sc);
282  
283  	return (0);
284  }
285  
286  UCOM_UNLOAD_DRAIN(ugensa);
287  
288  static void
289  ugensa_free_softc(struct ugensa_softc *sc)
290  {
291  	if (ucom_unref(&sc->sc_super_ucom)) {
292  		mtx_destroy(&sc->sc_mtx);
293  		device_free_softc(sc);
294  	}
295  }
296  
297  static void
298  ugensa_free(struct ucom_softc *ucom)
299  {
300  	ugensa_free_softc(ucom->sc_parent);
301  }
302  
303  static void
304  ugensa_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error)
305  {
306  	struct ugensa_sub_softc *ssc = usbd_xfer_softc(xfer);
307  	struct usb_page_cache *pc;
308  	uint32_t actlen;
309  
310  	switch (USB_GET_STATE(xfer)) {
311  	case USB_ST_SETUP:
312  	case USB_ST_TRANSFERRED:
313  tr_setup:
314  		pc = usbd_xfer_get_frame(xfer, 0);
315  		if (ucom_get_data(ssc->sc_ucom_ptr, pc, 0,
316  		    UGENSA_BUF_SIZE, &actlen)) {
317  			usbd_xfer_set_frame_len(xfer, 0, actlen);
318  			usbd_transfer_submit(xfer);
319  		}
320  		return;
321  
322  	default:			/* Error */
323  		if (error != USB_ERR_CANCELLED) {
324  			/* try to clear stall first */
325  			usbd_xfer_set_stall(xfer);
326  			goto tr_setup;
327  		}
328  		return;
329  	}
330  }
331  
332  static void
333  ugensa_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error)
334  {
335  	struct ugensa_sub_softc *ssc = usbd_xfer_softc(xfer);
336  	struct usb_page_cache *pc;
337  	int actlen;
338  
339  	usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
340  
341  	switch (USB_GET_STATE(xfer)) {
342  	case USB_ST_TRANSFERRED:
343  		pc = usbd_xfer_get_frame(xfer, 0);
344  		ucom_put_data(ssc->sc_ucom_ptr, pc, 0, actlen);
345  
346  	case USB_ST_SETUP:
347  tr_setup:
348  		usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer));
349  		usbd_transfer_submit(xfer);
350  		return;
351  
352  	default:			/* Error */
353  		if (error != USB_ERR_CANCELLED) {
354  			/* try to clear stall first */
355  			usbd_xfer_set_stall(xfer);
356  			goto tr_setup;
357  		}
358  		return;
359  	}
360  }
361  
362  static void
363  ugensa_start_read(struct ucom_softc *ucom)
364  {
365  	struct ugensa_softc *sc = ucom->sc_parent;
366  	struct ugensa_sub_softc *ssc = sc->sc_sub + ucom->sc_portno;
367  
368  	usbd_transfer_start(ssc->sc_xfer[UGENSA_BULK_DT_RD]);
369  }
370  
371  static void
372  ugensa_stop_read(struct ucom_softc *ucom)
373  {
374  	struct ugensa_softc *sc = ucom->sc_parent;
375  	struct ugensa_sub_softc *ssc = sc->sc_sub + ucom->sc_portno;
376  
377  	usbd_transfer_stop(ssc->sc_xfer[UGENSA_BULK_DT_RD]);
378  }
379  
380  static void
381  ugensa_start_write(struct ucom_softc *ucom)
382  {
383  	struct ugensa_softc *sc = ucom->sc_parent;
384  	struct ugensa_sub_softc *ssc = sc->sc_sub + ucom->sc_portno;
385  
386  	usbd_transfer_start(ssc->sc_xfer[UGENSA_BULK_DT_WR]);
387  }
388  
389  static void
390  ugensa_stop_write(struct ucom_softc *ucom)
391  {
392  	struct ugensa_softc *sc = ucom->sc_parent;
393  	struct ugensa_sub_softc *ssc = sc->sc_sub + ucom->sc_portno;
394  
395  	usbd_transfer_stop(ssc->sc_xfer[UGENSA_BULK_DT_WR]);
396  }
397  
398  static void
399  ugensa_poll(struct ucom_softc *ucom)
400  {
401  	struct ugensa_softc *sc = ucom->sc_parent;
402  	struct ugensa_sub_softc *ssc = sc->sc_sub + ucom->sc_portno;
403  
404  	usbd_transfer_poll(ssc->sc_xfer, UGENSA_N_TRANSFER);
405  }
406