Initial import of binutils 2.22 on the new vendor branch
[dragonfly.git] / sys / dev / usbmisc / umct / umct.c
1 /*-
2  * Copyright (c) 2003 Scott Long
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD: src/sys/dev/usb/umct.c,v 1.12 2006/09/07 00:06:42 imp Exp $
27  */
28
29 /*
30  * Driver for the MCT (Magic Control Technology) USB-RS232 Converter.
31  * Based on the superb documentation from the linux mct_u232 driver by
32  * Wolfgang Grandeggar <wolfgang@cec.ch>.
33  * This device smells a lot like the Belkin F5U103, except that it has
34  * suffered some mild brain-damage.  This driver is based off of the ubsa.c
35  * driver from Alexander Kabaev <kan@freebsd.org>.  Merging the two together
36  * might be useful, though the subtle differences might lead to lots of
37  * #ifdef's.
38  */
39
40 #include <sys/param.h>
41 #include <sys/systm.h>
42 #include <sys/kernel.h>
43 #include <sys/malloc.h>
44 #include <sys/bus.h>
45 #include <sys/tty.h>
46 #include <sys/taskqueue.h>
47
48 #include <bus/usb/usb.h>
49 #include <bus/usb/usbdi.h>
50 #include <bus/usb/usbdi_util.h>
51 #include "../ucom/ucomvar.h"
52
53 /* The UMCT advertises the standard 8250 UART registers */
54 #define UMCT_GET_MSR            2       /* Get Modem Status Register */
55 #define UMCT_GET_MSR_SIZE       1
56 #define UMCT_GET_LCR            6       /* Get Line Control Register */
57 #define UMCT_GET_LCR_SIZE       1
58 #define UMCT_SET_BAUD           5       /* Set the Baud Rate Divisor */
59 #define UMCT_SET_BAUD_SIZE      4
60 #define UMCT_SET_LCR            7       /* Set Line Control Register */
61 #define UMCT_SET_LCR_SIZE       1
62 #define UMCT_SET_MCR            10      /* Set Modem Control Register */
63 #define UMCT_SET_MCR_SIZE       1
64 #define UMCT_SET_UNKNOWN1       11
65 #define UMCT_SET_UNKNOWN1_SIZE  1
66 #define UMCT_SET_UNKNOWN2       12
67 #define UMCT_SET_UNKNOWN2_SIZE  1
68
69
70 #define UMCT_INTR_INTERVAL      100
71 #define UMCT_IFACE_INDEX        0
72 #define UMCT_CONFIG_INDEX       1
73
74 struct umct_softc {
75         struct ucom_softc       sc_ucom;
76         int                     sc_iface_number;
77         usbd_interface_handle   sc_intr_iface;
78         int                     sc_intr_number;
79         usbd_pipe_handle        sc_intr_pipe;
80         u_char                  *sc_intr_buf;
81         int                     sc_isize;
82         uint8_t                 sc_lsr;
83         uint8_t                 sc_msr;
84         uint8_t                 sc_lcr;
85         uint8_t                 sc_mcr;
86         struct task             sc_task;
87 };
88
89 static void umct_intr(usbd_xfer_handle, usbd_private_handle, usbd_status);
90 static void umct_get_status(void *, int, u_char *, u_char *);
91 static void umct_set(void *, int, int, int);
92 static int  umct_param(void *, int, struct termios *);
93 static int  umct_open(void *, int);
94 static void umct_close(void *, int);
95 static void umct_notify(void *, int);
96
97 static struct ucom_callback umct_callback = {
98         umct_get_status,        /* ucom_get_status */
99         umct_set,               /* ucom_set */
100         umct_param,             /* ucom_param */
101         NULL,                   /* ucom_ioctl */
102         umct_open,              /* ucom_open */
103         umct_close,             /* ucom_close */
104         NULL,                   /* ucom_read */
105         NULL                    /* ucom_write */
106 };
107
108 static const struct usb_devno umct_products[] = {
109         { USB_DEVICE(0x050d, 0x0109) }, /* Belkin F5U109 */
110         { USB_DEVICE(0x050d, 0x0409) }, /* Belkin F5U409 */
111         { USB_DEVICE(0x0711, 0x0200) }, /* D-Link DU-H3SP USB BAY Hub */
112         { USB_DEVICE(0x0711, 0x0210) }, /* MCT USB-232 */
113         { USB_DEVICE(0x0711, 0x0230) }, /* Sitecom USB-232 */
114 };
115
116 static device_probe_t   umct_match;
117 static device_attach_t  umct_attach;
118 static device_detach_t  umct_detach;
119
120 static device_method_t umct_methods[] = {
121         DEVMETHOD(device_probe, umct_match),
122         DEVMETHOD(device_attach, umct_attach),
123         DEVMETHOD(device_detach, umct_detach),
124         { 0, 0 }
125 };
126
127 static driver_t umct_driver = {
128         "ucom",
129         umct_methods,
130         sizeof(struct umct_softc)
131 };
132
133 DRIVER_MODULE(umct, uhub, umct_driver, ucom_devclass, usbd_driver_load, NULL);
134 MODULE_DEPEND(umct, usb, 1, 1, 1);
135 MODULE_DEPEND(umct, ucom, UCOM_MINVER, UCOM_PREFVER, UCOM_MAXVER);
136 MODULE_VERSION(umct, 1);
137
138 static int
139 umct_match(device_t self)
140 {
141         struct usb_attach_arg *uaa = device_get_ivars(self);
142
143         if (uaa->iface != NULL)
144                 return (UMATCH_NONE);
145
146         return (usb_lookup(umct_products, uaa->vendor, uaa->product) != NULL) ?
147             UMATCH_VENDOR_PRODUCT : UMATCH_NONE;
148 }
149
150 static int
151 umct_attach(device_t self)
152 {
153         struct umct_softc *sc = device_get_softc(self);
154         struct usb_attach_arg *uaa = device_get_ivars(self);
155         usbd_device_handle dev;
156         struct ucom_softc *ucom;
157         usb_config_descriptor_t *cdesc;
158         usb_interface_descriptor_t *id;
159         usb_endpoint_descriptor_t *ed;
160         usbd_status err;
161         int i;
162
163         dev = uaa->device;
164         bzero(sc, sizeof(struct umct_softc));
165         ucom = &sc->sc_ucom;
166         ucom->sc_dev = self;
167         ucom->sc_udev = dev;
168         ucom->sc_iface = uaa->iface;
169
170         ucom->sc_bulkout_no = -1;
171         ucom->sc_bulkin_no = -1;
172         sc->sc_intr_number = -1;
173         sc->sc_intr_pipe = NULL;
174
175         err = usbd_set_config_index(dev, UMCT_CONFIG_INDEX, 1);
176         if (err) {
177                 device_printf(ucom->sc_dev, "failed to set configuration: %s\n",
178                               usbd_errstr(err));
179                 ucom->sc_dying = 1;
180                 goto error;
181         }
182
183         cdesc = usbd_get_config_descriptor(ucom->sc_udev);
184         if (cdesc == NULL) {
185                 device_printf(ucom->sc_dev, "failed to get configuration "
186                               "descriptor\n");
187                 ucom->sc_dying = 1;
188                 goto error;
189         }
190
191         err = usbd_device2interface_handle(dev, UMCT_IFACE_INDEX,
192             &ucom->sc_iface);
193         if (err) {
194                 device_printf(ucom->sc_dev, "failed to get interface: %s\n",
195                               usbd_errstr(err));
196                 ucom->sc_dying = 1;
197                 goto error;
198         }
199
200         id = usbd_get_interface_descriptor(ucom->sc_iface);
201         sc->sc_iface_number = id->bInterfaceNumber;
202
203         for (i = 0; i < id->bNumEndpoints; i++) {
204                 ed = usbd_interface2endpoint_descriptor(ucom->sc_iface, i);
205                 if (ed == NULL) {
206                         device_printf(ucom->sc_dev, "no endpoint descriptor "
207                                       "for %d\n", i);
208                         ucom->sc_dying = 1;
209                         goto error;
210                 }
211
212                 /*
213                  * The real bulk-in endpoint is also marked as an interrupt.
214                  * The only way to differentiate it from the real interrupt
215                  * endpoint is to look at the wMaxPacketSize field.
216                  */
217                 if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN) {
218                         if (UGETW(ed->wMaxPacketSize) == 0x2) {
219                                 sc->sc_intr_number = ed->bEndpointAddress;
220                                 sc->sc_isize = UGETW(ed->wMaxPacketSize);
221                         } else {
222                                 ucom->sc_bulkin_no = ed->bEndpointAddress;
223                                 ucom->sc_ibufsize = UGETW(ed->wMaxPacketSize);
224                         }
225                         continue;
226                 }
227
228                 if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT) {
229                         ucom->sc_bulkout_no = ed->bEndpointAddress;
230                         /* MCT Sitecom USB-232 device is broken. The bulk out
231                          * endpoint descriptor reports 32 bytes, but data will
232                          * get dropped if this value is used. */
233                         if (uaa->vendor == 0x0711 && uaa->product == 0x0230)
234                                 ucom->sc_obufsize = 16;
235                         else
236                                 ucom->sc_obufsize = UGETW(ed->wMaxPacketSize);
237                         continue;
238                 }
239
240                 device_printf(ucom->sc_dev, "warning - unsupported endpoint "
241                               "0x%x\n", ed->bEndpointAddress);
242         }
243
244         if (sc->sc_intr_number == -1) {
245                 device_printf(ucom->sc_dev, "could not find interrupt in\n");
246                 ucom->sc_dying = 1;
247                 goto error;
248         }
249
250         sc->sc_intr_iface = ucom->sc_iface;
251
252         if (ucom->sc_bulkout_no == -1) {
253                 device_printf(ucom->sc_dev, "rould not find data bulk out\n");
254                 ucom->sc_dying = 1;
255                 goto error;
256         }
257
258         ucom->sc_parent = sc;
259         ucom->sc_portno = UCOM_UNK_PORTNO;
260         ucom->sc_opkthdrlen = 0;
261         ucom->sc_callback = &umct_callback;
262         TASK_INIT(&sc->sc_task, 0, umct_notify, sc);
263         ucom_attach(ucom);
264
265         return 0;
266
267 error:
268         return ENXIO;
269 }
270
271 static int
272 umct_detach(device_t self)
273 {
274         struct umct_softc *sc = device_get_softc(self);
275         int rv;
276
277         if (sc->sc_intr_pipe != NULL) {
278                 usbd_abort_pipe(sc->sc_intr_pipe);
279                 usbd_close_pipe(sc->sc_intr_pipe);
280                 kfree(sc->sc_intr_buf, M_USBDEV);
281                 sc->sc_intr_pipe = NULL;
282         }
283
284         sc->sc_ucom.sc_dying = 1;
285         rv = ucom_detach(&sc->sc_ucom);
286         return (rv);
287 }
288
289 static int
290 umct_request(struct umct_softc *sc, uint8_t request, int len, uint32_t value)
291 {
292         usb_device_request_t req;
293         usbd_status err;
294         uint8_t oval[4];
295
296         req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
297         req.bRequest = request;
298         USETW(req.wValue, 0);
299         USETW(req.wIndex, sc->sc_iface_number);
300         USETW(req.wLength, len);
301         USETDW(oval, value);
302
303         err = usbd_do_request(sc->sc_ucom.sc_udev, &req, oval);
304         if (err)
305                 device_printf(sc->sc_ucom.sc_dev, "ubsa_request: %s\n",
306                               usbd_errstr(err));
307         return (err);
308 }
309
310 static void
311 umct_intr(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
312 {
313         struct umct_softc *sc;
314         u_char *buf;
315
316         sc = (struct umct_softc *)priv;
317         buf = sc->sc_intr_buf;
318         if (sc->sc_ucom.sc_dying)
319                 return;
320
321         if (status != USBD_NORMAL_COMPLETION) {
322                 if (status == USBD_NOT_STARTED || status == USBD_CANCELLED)
323                         return;
324
325                 usbd_clear_endpoint_stall_async(sc->sc_intr_pipe);
326                 return;
327         }
328
329         sc->sc_msr = buf[0];
330         sc->sc_lsr = buf[1];
331
332         /*
333          * Defer notifying the ucom layer as it doesn't like to be bothered
334          * from an interrupt context.
335          */
336         taskqueue_enqueue(taskqueue_swi, &sc->sc_task);
337 }
338
339 static void
340 umct_notify(void *arg, int count)
341 {
342         struct umct_softc *sc;
343
344         sc = (struct umct_softc *)arg;
345         if (sc->sc_ucom.sc_dying == 0)
346                 ucom_status_change(&sc->sc_ucom);
347 }
348
349 static void
350 umct_get_status(void *addr, int portno, u_char *lsr, u_char *msr)
351 {
352         struct umct_softc *sc;
353
354         sc = addr;
355         if (lsr != NULL)
356                 *lsr = sc->sc_lsr;
357         if (msr != NULL)
358                 *msr = sc->sc_msr;
359
360         return;
361 }
362
363 static void
364 umct_set(void *addr, int portno, int reg, int onoff)
365 {
366         struct umct_softc *sc;
367
368         sc = addr;
369         switch (reg) {
370         case UCOM_SET_BREAK:
371                 sc->sc_lcr &= ~0x40;
372                 sc->sc_lcr |= (onoff) ? 0x40 : 0;
373                 umct_request(sc, UMCT_SET_LCR, UMCT_SET_LCR_SIZE, sc->sc_lcr);
374                 break;
375         case UCOM_SET_DTR:
376                 sc->sc_mcr &= ~0x01;
377                 sc->sc_mcr |= (onoff) ? 0x01 : 0;
378                 umct_request(sc, UMCT_SET_MCR, UMCT_SET_MCR_SIZE, sc->sc_mcr);
379                 break;
380         case UCOM_SET_RTS:
381                 sc->sc_mcr &= ~0x2;
382                 sc->sc_mcr |= (onoff) ? 0x02 : 0;
383                 umct_request(sc, UMCT_SET_MCR, UMCT_SET_MCR_SIZE, sc->sc_mcr);
384                 break;
385         default:
386                 break;
387         }
388 }
389
390 static int
391 umct_calc_baud(u_int baud)
392 {
393         switch(baud) {
394         case B300: return (0x1);
395         case B600: return (0x2);
396         case B1200: return (0x3);
397         case B2400: return (0x4);
398         case B4800: return (0x6);
399         case B9600: return (0x8);
400         case B19200: return (0x9);
401         case B38400: return (0xa);
402         case B57600: return (0xb);
403         case B115200: return (0xc);
404         case B0:
405         default:
406                 break;
407         }
408
409         return (0x0);
410 }
411
412 static int
413 umct_param(void *addr, int portno, struct termios *ti)
414 {
415         struct umct_softc *sc;
416         uint32_t value;
417
418         sc = addr;
419         value = umct_calc_baud(ti->c_ospeed);
420         umct_request(sc, UMCT_SET_BAUD, UMCT_SET_BAUD_SIZE, value);
421         umct_request(sc, UMCT_SET_UNKNOWN1, UMCT_SET_UNKNOWN1_SIZE, 0);
422         umct_request(sc, UMCT_SET_UNKNOWN2, UMCT_SET_UNKNOWN2_SIZE, 0);
423
424         value = sc->sc_lcr & 0x40;
425
426         switch (ti->c_cflag & CSIZE) {
427         case CS5: value |= 0x0; break;
428         case CS6: value |= 0x1; break;
429         case CS7: value |= 0x2; break;
430         case CS8: value |= 0x3; break;
431         default: value |= 0x0; break;
432         }
433
434         value |= (ti->c_cflag & CSTOPB) ? 0x4 : 0;
435         if (ti->c_cflag & PARENB) {
436                 value |= 0x8;
437                 value |= (ti->c_cflag & PARODD) ? 0x0 : 0x10;
438         }
439
440         /*
441          * XXX There doesn't seem to be a way to tell the device to use flow
442          * control.
443          */
444
445         sc->sc_lcr = value;
446         umct_request(sc, UMCT_SET_LCR, UMCT_SET_LCR_SIZE, value);
447
448         return (0);
449 }
450
451 static int
452 umct_open(void *addr, int portno)
453 {
454         struct umct_softc *sc;
455         int err;
456
457         sc = addr;
458         if (sc->sc_ucom.sc_dying) {
459                 return (ENXIO);
460         }
461
462         if (sc->sc_intr_number != -1 && sc->sc_intr_pipe == NULL) {
463                 sc->sc_intr_buf = kmalloc(sc->sc_isize, M_USBDEV, M_WAITOK);
464                 err = usbd_open_pipe_intr(sc->sc_intr_iface, sc->sc_intr_number,
465                     USBD_SHORT_XFER_OK, &sc->sc_intr_pipe, sc, sc->sc_intr_buf,
466                     sc->sc_isize, umct_intr, UMCT_INTR_INTERVAL);
467                 if (err) {
468                         device_printf(sc->sc_ucom.sc_dev, "cannot open "
469                                       "interrupt pipe (addr %d)\n",
470                                       sc->sc_intr_number);
471                         kfree(sc->sc_intr_buf, M_USBDEV);
472                         return (EIO);
473                 }
474         }
475
476         return (0);
477 }
478
479 static void
480 umct_close(void *addr, int portno)
481 {
482         struct umct_softc *sc;
483         int err;
484
485         sc = addr;
486         if (sc->sc_ucom.sc_dying)
487                 return;
488
489         if (sc->sc_intr_pipe != NULL) {
490                 err = usbd_abort_pipe(sc->sc_intr_pipe);
491                 if (err)
492                         device_printf(sc->sc_ucom.sc_dev, "abort interrupt "
493                                       "pipe failed: %s\n", usbd_errstr(err));
494                 err = usbd_close_pipe(sc->sc_intr_pipe);
495                 if (err)
496                         device_printf(sc->sc_ucom.sc_dev, "close interrupt "
497                                       "pipe failed: %s\n", usbd_errstr(err));
498                 kfree(sc->sc_intr_buf, M_USBDEV);
499                 sc->sc_intr_pipe = NULL;
500         }
501 }