File: | dev/usb/umcs.c |
Warning: | line 645, column 10 Value stored to 'lcr' during its initialization is never read |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* $OpenBSD: umcs.c,v 1.11 2023/04/10 12:11:22 jsg Exp $ */ |
2 | /* $NetBSD: umcs.c,v 1.8 2014/08/23 21:37:56 martin Exp $ */ |
3 | /* $FreeBSD: head/sys/dev/usb/serial/umcs.c 260559 2014-01-12 11:44:28Z hselasky $ */ |
4 | |
5 | /*- |
6 | * Copyright (c) 2010 Lev Serebryakov <lev@FreeBSD.org>. |
7 | * All rights reserved. |
8 | * |
9 | * Redistribution and use in source and binary forms, with or without |
10 | * modification, are permitted provided that the following conditions |
11 | * are met: |
12 | * 1. Redistributions of source code must retain the above copyright |
13 | * notice, this list of conditions and the following disclaimer. |
14 | * 2. Redistributions in binary form must reproduce the above copyright |
15 | * notice, this list of conditions and the following disclaimer in the |
16 | * documentation and/or other materials provided with the distribution. |
17 | * |
18 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
19 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE |
22 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
23 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
24 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
25 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
26 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
27 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
28 | * SUCH DAMAGE. |
29 | */ |
30 | |
31 | /* |
32 | * This driver supports several multiport USB-to-RS232 serial adapters driven |
33 | * by MosChip mos7820 and mos7840, bridge chips. The adapters are sold under |
34 | * many different brand names. |
35 | * |
36 | * Datasheets are available at MosChip www site at http://www.moschip.com. |
37 | * The datasheets don't contain full programming information for the chip. |
38 | * |
39 | * It is normal to have only two enabled ports in devices, based on quad-port |
40 | * mos7840. |
41 | */ |
42 | |
43 | #include <sys/param.h> |
44 | #include <sys/systm.h> |
45 | #include <sys/kernel.h> |
46 | #include <sys/malloc.h> |
47 | #include <sys/tty.h> |
48 | #include <sys/device.h> |
49 | #include <sys/task.h> |
50 | |
51 | #include <dev/usb/usb.h> |
52 | #include <dev/usb/usbdi.h> |
53 | #include <dev/usb/usbdi_util.h> |
54 | #include <dev/usb/usbdevs.h> |
55 | |
56 | #include <dev/usb/ucomvar.h> |
57 | |
58 | #include "umcs.h" |
59 | |
60 | #ifdef UMCS_DEBUG |
61 | #define DPRINTF(x...) printf(x) |
62 | #else |
63 | #define DPRINTF(x...) |
64 | #endif |
65 | |
66 | #define DEVNAME(_sc)((_sc)->sc_dev.dv_xname) ((_sc)->sc_dev.dv_xname) |
67 | |
68 | /* |
69 | * Two-port devices (both with 7820 chip and 7840 chip configured as two-port) |
70 | * have ports 0 and 2, with ports 1 and 3 omitted. |
71 | * So, PHYSICAL port numbers on two-port device will be 0 and 2. |
72 | * |
73 | * We use an array of the following struct, indexed by ucom port index, |
74 | * and include the physical port number in it. |
75 | */ |
76 | struct umcs_port { |
77 | struct ucom_softc *ucom; /* ucom subdevice */ |
78 | unsigned int pn; /* physical port number */ |
79 | int flags; |
80 | #define UMCS_STATCHG0x01 0x01 |
81 | |
82 | uint8_t lcr; /* local line control reg. */ |
83 | uint8_t mcr; /* local modem control reg. */ |
84 | }; |
85 | |
86 | struct umcs_softc { |
87 | struct device sc_dev; |
88 | struct usbd_device *sc_udev; /* the usb device */ |
89 | struct usbd_pipe *sc_ipipe; /* interrupt pipe */ |
90 | uint8_t *sc_ibuf; /* buffer for interrupt xfer */ |
91 | unsigned int sc_isize; /* size of buffer */ |
92 | |
93 | struct umcs_port sc_subdevs[UMCS_MAX_PORTS4]; |
94 | uint8_t sc_numports; /* number of ports */ |
95 | |
96 | int sc_init_done; |
97 | struct task sc_status_task; |
98 | }; |
99 | |
100 | int umcs_get_reg(struct umcs_softc *, uint8_t, uint8_t *); |
101 | int umcs_set_reg(struct umcs_softc *, uint8_t, uint8_t); |
102 | int umcs_get_uart_reg(struct umcs_softc *, uint8_t, uint8_t, uint8_t *); |
103 | int umcs_set_uart_reg(struct umcs_softc *, uint8_t, uint8_t, uint8_t); |
104 | int umcs_calc_baudrate(uint32_t, uint16_t *, uint8_t *); |
105 | int umcs_set_baudrate(struct umcs_softc *, uint8_t, uint32_t); |
106 | void umcs_dtr(struct umcs_softc *, int, int); |
107 | void umcs_rts(struct umcs_softc *, int, int); |
108 | void umcs_break(struct umcs_softc *, int, int); |
109 | |
110 | int umcs_match(struct device *, void *, void *); |
111 | void umcs_attach(struct device *, struct device *, void *); |
112 | int umcs_detach(struct device *, int); |
113 | void umcs_intr(struct usbd_xfer *, void *, usbd_status); |
114 | void umcs_status_task(void *); |
115 | |
116 | void umcs_get_status(void *, int, uint8_t *, uint8_t *); |
117 | void umcs_set(void *, int, int, int); |
118 | int umcs_param(void *, int, struct termios *); |
119 | int umcs_open(void *, int); |
120 | void umcs_close(void *, int); |
121 | |
122 | const struct ucom_methods umcs_methods = { |
123 | umcs_get_status, |
124 | umcs_set, |
125 | umcs_param, |
126 | NULL((void *)0), |
127 | umcs_open, |
128 | umcs_close, |
129 | NULL((void *)0), |
130 | NULL((void *)0), |
131 | }; |
132 | |
133 | const struct usb_devno umcs_devs[] = { |
134 | { USB_VENDOR_MOSCHIP0x9710, USB_PRODUCT_MOSCHIP_MCS78100x7810 }, |
135 | { USB_VENDOR_MOSCHIP0x9710, USB_PRODUCT_MOSCHIP_MCS78200x7820 }, |
136 | { USB_VENDOR_MOSCHIP0x9710, USB_PRODUCT_MOSCHIP_MCS78400x7840 }, |
137 | { USB_VENDOR_ATEN0x0557, USB_PRODUCT_ATEN_UC23240x2011 } |
138 | }; |
139 | |
140 | struct cfdriver umcs_cd = { |
141 | NULL((void *)0), "umcs", DV_DULL |
142 | }; |
143 | |
144 | const struct cfattach umcs_ca = { |
145 | sizeof(struct umcs_softc), umcs_match, umcs_attach, umcs_detach |
146 | }; |
147 | |
148 | |
149 | static inline int |
150 | umcs_reg_sp(int pn) |
151 | { |
152 | KASSERT(pn >= 0 && pn < 4)((pn >= 0 && pn < 4) ? (void)0 : __assert("diagnostic " , "/usr/src/sys/dev/usb/umcs.c", 152, "pn >= 0 && pn < 4" )); |
153 | switch (pn) { |
154 | default: |
155 | case 0: return UMCS_SP10x00; |
156 | case 1: return UMCS_SP20x08; |
157 | case 2: return UMCS_SP30x0a; |
158 | case 3: return UMCS_SP40x0c; |
159 | } |
160 | } |
161 | |
162 | static inline int |
163 | umcs_reg_ctrl(int pn) |
164 | { |
165 | KASSERT(pn >= 0 && pn < 4)((pn >= 0 && pn < 4) ? (void)0 : __assert("diagnostic " , "/usr/src/sys/dev/usb/umcs.c", 165, "pn >= 0 && pn < 4" )); |
166 | switch (pn) { |
167 | default: |
168 | case 0: return UMCS_CTRL10x01; |
169 | case 1: return UMCS_CTRL20x09; |
170 | case 2: return UMCS_CTRL30x0b; |
171 | case 3: return UMCS_CTRL40x0d; |
172 | } |
173 | } |
174 | |
175 | int |
176 | umcs_match(struct device *dev, void *match, void *aux) |
177 | { |
178 | struct usb_attach_arg *uaa = aux; |
179 | |
180 | if (uaa->iface == NULL((void *)0) || uaa->ifaceno != UMCS_IFACE_NO0) |
181 | return (UMATCH_NONE0); |
182 | |
183 | return (usb_lookup(umcs_devs, uaa->vendor, uaa->product)usbd_match_device((const struct usb_devno *)(umcs_devs), sizeof (umcs_devs) / sizeof ((umcs_devs)[0]), sizeof ((umcs_devs)[0 ]), (uaa->vendor), (uaa->product)) != NULL((void *)0)) ? |
184 | UMATCH_VENDOR_PRODUCT13 : UMATCH_NONE0; |
185 | } |
186 | |
187 | void |
188 | umcs_attach(struct device *parent, struct device *self, void *aux) |
189 | { |
190 | struct umcs_softc *sc = (struct umcs_softc *)self; |
191 | struct usb_attach_arg *uaa = aux; |
192 | usb_interface_descriptor_t *id; |
193 | usb_endpoint_descriptor_t *ed; |
194 | struct ucom_attach_args uca; |
195 | int error, i, intr_addr; |
196 | uint8_t data; |
197 | |
198 | sc->sc_udev = uaa->device; |
199 | |
200 | /* |
201 | * Get number of ports |
202 | * Documentation (full datasheet) says, that number of ports is |
203 | * set as UMCS_MODE_SELECT24S bit in MODE R/Only |
204 | * register. But vendor driver uses these undocumented |
205 | * register & bit. |
206 | * |
207 | * Experiments show, that MODE register can have `0' |
208 | * (4 ports) bit on 2-port device, so use vendor driver's way. |
209 | * |
210 | * Also, see notes in header file for these constants. |
211 | */ |
212 | if (umcs_get_reg(sc, UMCS_GPIO0x07, &data)) { |
213 | printf("%s: unable to get number of ports\n", DEVNAME(sc)((sc)->sc_dev.dv_xname)); |
214 | usbd_deactivate(sc->sc_udev); |
215 | return; |
216 | } |
217 | if (data & UMCS_GPIO_4PORTS0x01) |
218 | sc->sc_numports = 4; /* physical port no are : 0, 1, 2, 3 */ |
219 | else if (uaa->product == USB_PRODUCT_MOSCHIP_MCS78100x7810) |
220 | sc->sc_numports = 1; |
221 | else |
222 | sc->sc_numports = 2; /* physical port no are: 0 and 2 */ |
223 | |
224 | #ifdef UMCS_DEBUG |
225 | if (!umcs_get_reg(sc, UMCS_MODE0x2b, &data)) { |
226 | printf("%s: On-die configuration: RST: active %s, " |
227 | "HRD: %s, PLL: %s, POR: %s, Ports: %s, EEPROM write %s, " |
228 | "IrDA is %savailable\n", DEVNAME(sc)((sc)->sc_dev.dv_xname), |
229 | (data & UMCS_MODE_RESET0x02) ? "low" : "high", |
230 | (data & UMCS_MODE_SER_PRSNT0x04) ? "yes" : "no", |
231 | (data & UMCS_MODE_PLLBYPASS0x08) ? "bypassed" : "avail", |
232 | (data & UMCS_MODE_PORBYPASS0x10) ? "bypassed" : "avail", |
233 | (data & UMCS_MODE_SELECT24S0x20) ? "2" : "4", |
234 | (data & UMCS_MODE_EEPROMWR0x40) ? "enabled" : "disabled", |
235 | (data & UMCS_MODE_IRDA0x80) ? "" : "not "); |
236 | } |
237 | #endif |
238 | |
239 | /* Set up the interrupt pipe */ |
240 | id = usbd_get_interface_descriptor(uaa->iface); |
241 | intr_addr = -1; |
242 | for (i = 0 ; i < id->bNumEndpoints ; i++) { |
243 | ed = usbd_interface2endpoint_descriptor(uaa->iface, i); |
244 | if (ed == NULL((void *)0)) { |
245 | printf("%s: no endpoint descriptor found for %d\n", |
246 | DEVNAME(sc)((sc)->sc_dev.dv_xname), i); |
247 | usbd_deactivate(sc->sc_udev); |
248 | return; |
249 | } |
250 | |
251 | if (UE_GET_DIR(ed->bEndpointAddress)((ed->bEndpointAddress) & 0x80) != UE_DIR_IN0x80 || |
252 | UE_GET_XFERTYPE(ed->bmAttributes)((ed->bmAttributes) & 0x03) != UE_INTERRUPT0x03) |
253 | continue; |
254 | sc->sc_isize = UGETW(ed->wMaxPacketSize)(*(u_int16_t *)(ed->wMaxPacketSize)); |
255 | intr_addr = ed->bEndpointAddress; |
256 | break; |
257 | } |
258 | if (intr_addr < 0) { |
259 | printf("%s: missing endpoint\n", DEVNAME(sc)((sc)->sc_dev.dv_xname)); |
260 | usbd_deactivate(sc->sc_udev); |
261 | return; |
262 | } |
263 | sc->sc_ibuf = malloc(sc->sc_isize, M_USBDEV102, M_WAITOK0x0001); |
264 | |
265 | error = usbd_open_pipe_intr(uaa->iface, intr_addr, |
266 | USBD_SHORT_XFER_OK0x04, &sc->sc_ipipe, sc, sc->sc_ibuf, |
267 | sc->sc_isize, umcs_intr, 100 /* XXX */); |
268 | if (error) { |
269 | printf("%s: cannot open interrupt pipe (addr %d)\n", |
270 | DEVNAME(sc)((sc)->sc_dev.dv_xname), intr_addr); |
271 | usbd_deactivate(sc->sc_udev); |
272 | return; |
273 | } |
274 | |
275 | memset(&uca, 0, sizeof uca)__builtin_memset((&uca), (0), (sizeof uca)); |
276 | uca.ibufsize = 256; |
277 | uca.obufsize = 256; |
278 | uca.ibufsizepad = 256; |
279 | uca.opkthdrlen = 0; |
280 | uca.device = sc->sc_udev; |
281 | uca.iface = uaa->iface; |
282 | uca.methods = &umcs_methods; |
283 | uca.arg = sc; |
284 | |
285 | for (i = 0; i < sc->sc_numports; i++) { |
286 | uca.bulkin = uca.bulkout = -1; |
287 | |
288 | /* |
289 | * On 4 port cards, endpoints are 0/1, 2/3, 4/5, and 6/7. |
290 | * On 2 port cards, they are 0/1 and 4/5. |
291 | * On single port, just 0/1 will be used. |
292 | */ |
293 | int pn = i * (sc->sc_numports == 2 ? 2 : 1); |
294 | |
295 | ed = usbd_interface2endpoint_descriptor(uaa->iface, pn*2); |
296 | if (ed == NULL((void *)0)) { |
297 | printf("%s: no bulk in endpoint found for %d\n", |
298 | DEVNAME(sc)((sc)->sc_dev.dv_xname), i); |
299 | usbd_deactivate(sc->sc_udev); |
300 | return; |
301 | } |
302 | uca.bulkin = ed->bEndpointAddress; |
303 | |
304 | ed = usbd_interface2endpoint_descriptor(uaa->iface, pn*2+1); |
305 | if (ed == NULL((void *)0)) { |
306 | printf("%s: no bulk out endpoint found for %d\n", |
307 | DEVNAME(sc)((sc)->sc_dev.dv_xname), i); |
308 | usbd_deactivate(sc->sc_udev); |
309 | return; |
310 | } |
311 | uca.bulkout = ed->bEndpointAddress; |
312 | uca.portno = i; |
313 | |
314 | sc->sc_subdevs[i].pn = pn; |
315 | sc->sc_subdevs[i].ucom = (struct ucom_softc *) |
316 | config_found_sm(self, &uca, ucomprint, ucomsubmatch); |
317 | } |
318 | |
319 | task_set(&sc->sc_status_task, umcs_status_task, sc); |
320 | } |
321 | |
322 | int |
323 | umcs_get_reg(struct umcs_softc *sc, uint8_t reg, uint8_t *data) |
324 | { |
325 | usb_device_request_t req; |
326 | |
327 | req.bmRequestType = UT_READ_VENDOR_DEVICE(0x80 | 0x40 | 0x00); |
328 | req.bRequest = UMCS_READ0x0d; |
329 | USETW(req.wValue, 0)(*(u_int16_t *)(req.wValue) = (0)); |
330 | USETW(req.wIndex, reg)(*(u_int16_t *)(req.wIndex) = (reg)); |
331 | USETW(req.wLength, UMCS_READ_LENGTH)(*(u_int16_t *)(req.wLength) = (1)); |
332 | |
333 | if (usbd_do_request(sc->sc_udev, &req, data)) |
334 | return (EIO5); |
335 | |
336 | return (0); |
337 | } |
338 | |
339 | int |
340 | umcs_set_reg(struct umcs_softc *sc, uint8_t reg, uint8_t data) |
341 | { |
342 | usb_device_request_t req; |
343 | |
344 | req.bmRequestType = UT_WRITE_VENDOR_DEVICE(0x00 | 0x40 | 0x00); |
345 | req.bRequest = UMCS_WRITE0x0e; |
346 | USETW(req.wValue, data)(*(u_int16_t *)(req.wValue) = (data)); |
347 | USETW(req.wIndex, reg)(*(u_int16_t *)(req.wIndex) = (reg)); |
348 | USETW(req.wLength, 0)(*(u_int16_t *)(req.wLength) = (0)); |
349 | |
350 | if (usbd_do_request(sc->sc_udev, &req, NULL((void *)0))) |
351 | return (EIO5); |
352 | |
353 | return (0); |
354 | } |
355 | |
356 | int |
357 | umcs_get_uart_reg(struct umcs_softc *sc, uint8_t portno, uint8_t reg, |
358 | uint8_t *data) |
359 | { |
360 | usb_device_request_t req; |
361 | uint16_t wVal; |
362 | |
363 | wVal = ((uint16_t)(sc->sc_subdevs[portno].pn + 1)) << 8; |
364 | |
365 | req.bmRequestType = UT_READ_VENDOR_DEVICE(0x80 | 0x40 | 0x00); |
366 | req.bRequest = UMCS_READ0x0d; |
367 | USETW(req.wValue, wVal)(*(u_int16_t *)(req.wValue) = (wVal)); |
368 | USETW(req.wIndex, reg)(*(u_int16_t *)(req.wIndex) = (reg)); |
369 | USETW(req.wLength, UMCS_READ_LENGTH)(*(u_int16_t *)(req.wLength) = (1)); |
370 | |
371 | if (usbd_do_request(sc->sc_udev, &req, data)) |
372 | return (EIO5); |
373 | |
374 | return (0); |
375 | } |
376 | |
377 | int |
378 | umcs_set_uart_reg(struct umcs_softc *sc, uint8_t portno, uint8_t reg, |
379 | uint8_t data) |
380 | { |
381 | usb_device_request_t req; |
382 | uint16_t wVal; |
383 | |
384 | wVal = ((uint16_t)(sc->sc_subdevs[portno].pn + 1)) << 8 | data; |
385 | |
386 | req.bmRequestType = UT_WRITE_VENDOR_DEVICE(0x00 | 0x40 | 0x00); |
387 | req.bRequest = UMCS_WRITE0x0e; |
388 | USETW(req.wValue, wVal)(*(u_int16_t *)(req.wValue) = (wVal)); |
389 | USETW(req.wIndex, reg)(*(u_int16_t *)(req.wIndex) = (reg)); |
390 | USETW(req.wLength, 0)(*(u_int16_t *)(req.wLength) = (0)); |
391 | |
392 | if (usbd_do_request(sc->sc_udev, &req, NULL((void *)0))) |
393 | return (EIO5); |
394 | |
395 | return (0); |
396 | } |
397 | |
398 | int |
399 | umcs_set_baudrate(struct umcs_softc *sc, uint8_t portno, uint32_t rate) |
400 | { |
401 | int pn = sc->sc_subdevs[portno].pn; |
402 | int spreg = umcs_reg_sp(pn); |
403 | uint8_t lcr = sc->sc_subdevs[portno].lcr; |
404 | uint8_t clk, data; |
405 | uint16_t div; |
406 | |
407 | if (umcs_calc_baudrate(rate, &div, &clk)) |
408 | return (EINVAL22); |
409 | |
410 | DPRINTF("%s: portno %d set speed: %d (%02x/%d)\n", DEVNAME(sc), portno, |
411 | rate, clk, div); |
412 | |
413 | /* Set clock source for standard BAUD frequencies */ |
414 | if (umcs_get_reg(sc, spreg, &data)) |
415 | return (EIO5); |
416 | data &= UMCS_SPx_CLK_MASK0x70; |
417 | if (umcs_set_reg(sc, spreg, data | clk)) |
418 | return (EIO5); |
419 | |
420 | /* Set divider */ |
421 | lcr |= UMCS_LCR_DIVISORS0x80; |
422 | if (umcs_set_uart_reg(sc, portno, UMCS_REG_LCR0x03, lcr)) |
423 | return (EIO5); |
424 | sc->sc_subdevs[portno].lcr = lcr; |
425 | |
426 | if (umcs_set_uart_reg(sc, portno, UMCS_REG_DLL0x00, div & 0xff) || |
427 | umcs_set_uart_reg(sc, portno, UMCS_REG_DLM0x01, (div >> 8) & 0xff)) |
428 | return (EIO5); |
429 | |
430 | /* Turn off access to DLL/DLM registers of UART */ |
431 | lcr &= ~UMCS_LCR_DIVISORS0x80; |
432 | if (umcs_set_uart_reg(sc, portno, UMCS_REG_LCR0x03, lcr)) |
433 | return (EIO5); |
434 | sc->sc_subdevs[portno].lcr = lcr; |
435 | |
436 | return (0); |
437 | } |
438 | |
439 | /* Maximum speeds for standard frequencies, when PLL is not used */ |
440 | static const uint32_t umcs_baudrate_divisors[] = { |
441 | 0, 115200, 230400, 403200, 460800, 806400, 921600, 1572864, 3145728, |
442 | }; |
443 | |
444 | int |
445 | umcs_calc_baudrate(uint32_t rate, uint16_t *divisor, uint8_t *clk) |
446 | { |
447 | const uint8_t divisors_len = nitems(umcs_baudrate_divisors)(sizeof((umcs_baudrate_divisors)) / sizeof((umcs_baudrate_divisors )[0])); |
448 | uint8_t i = 0; |
449 | |
450 | if (rate > umcs_baudrate_divisors[divisors_len - 1]) |
451 | return (-1); |
452 | |
453 | for (i = 0; i < divisors_len - 1; i++) { |
454 | if (rate > umcs_baudrate_divisors[i] && |
455 | rate <= umcs_baudrate_divisors[i + 1]) { |
456 | *divisor = umcs_baudrate_divisors[i + 1] / rate; |
457 | /* 0x00 .. 0x70 */ |
458 | *clk = i << UMCS_SPx_CLK_SHIFT4; |
459 | return (0); |
460 | } |
461 | } |
462 | |
463 | return (-1); |
464 | } |
465 | |
466 | int |
467 | umcs_detach(struct device *self, int flags) |
468 | { |
469 | struct umcs_softc *sc = (struct umcs_softc *)self; |
470 | |
471 | task_del(systq, &sc->sc_status_task); |
472 | |
473 | if (sc->sc_ipipe != NULL((void *)0)) { |
474 | usbd_close_pipe(sc->sc_ipipe); |
475 | sc->sc_ipipe = NULL((void *)0); |
476 | } |
477 | |
478 | if (sc->sc_ibuf != NULL((void *)0)) { |
479 | free(sc->sc_ibuf, M_USBDEV102, sc->sc_isize); |
480 | sc->sc_ibuf = NULL((void *)0); |
481 | } |
482 | |
483 | return (config_detach_children(self, flags)); |
484 | } |
485 | |
486 | void |
487 | umcs_get_status(void *self, int portno, uint8_t *lsr, uint8_t *msr) |
488 | { |
489 | struct umcs_softc *sc = self; |
490 | uint8_t hw_lsr = 0; /* local line status register */ |
491 | uint8_t hw_msr = 0; /* local modem status register */ |
492 | |
493 | if (usbd_is_dying(sc->sc_udev)) |
494 | return; |
495 | |
496 | /* Read LSR & MSR */ |
497 | if (umcs_get_uart_reg(sc, portno, UMCS_REG_LSR0x05, &hw_lsr) || |
498 | umcs_get_uart_reg(sc, portno, UMCS_REG_MSR0x06, &hw_msr)) |
499 | return; |
500 | |
501 | *lsr = hw_lsr; |
502 | *msr = hw_msr; |
503 | } |
504 | |
505 | void |
506 | umcs_set(void *self, int portno, int reg, int onoff) |
507 | { |
508 | struct umcs_softc *sc = self; |
509 | |
510 | if (usbd_is_dying(sc->sc_udev)) |
511 | return; |
512 | |
513 | switch (reg) { |
514 | case UCOM_SET_DTR1: |
515 | umcs_dtr(sc, portno, onoff); |
516 | break; |
517 | case UCOM_SET_RTS2: |
518 | umcs_rts(sc, portno, onoff); |
519 | break; |
520 | case UCOM_SET_BREAK3: |
521 | umcs_break(sc, portno, onoff); |
522 | break; |
523 | default: |
524 | break; |
525 | } |
526 | } |
527 | |
528 | int |
529 | umcs_param(void *self, int portno, struct termios *t) |
530 | { |
531 | struct umcs_softc *sc = self; |
532 | uint8_t lcr = sc->sc_subdevs[portno].lcr; |
533 | uint8_t mcr = sc->sc_subdevs[portno].mcr; |
534 | int error = 0; |
535 | |
536 | if (t->c_cflag & CSTOPB0x00000400) |
537 | lcr |= UMCS_LCR_STOPB20x04; |
538 | else |
539 | lcr |= UMCS_LCR_STOPB10x00; |
540 | |
541 | lcr &= ~UMCS_LCR_PARITYMASK0x38; |
542 | if (t->c_cflag & PARENB0x00001000) { |
543 | lcr |= UMCS_LCR_PARITYON0x08; |
544 | if (t->c_cflag & PARODD0x00002000) { |
545 | lcr |= UMCS_LCR_PARITYODD0x00; |
546 | } else { |
547 | lcr |= UMCS_LCR_PARITYEVEN0x10; |
548 | } |
549 | } else { |
550 | lcr &= ~UMCS_LCR_PARITYON0x08; |
551 | } |
552 | |
553 | lcr &= ~UMCS_LCR_DATALENMASK0x03; |
554 | switch (t->c_cflag & CSIZE0x00000300) { |
555 | case CS50x00000000: |
556 | lcr |= UMCS_LCR_DATALEN50x00; |
557 | break; |
558 | case CS60x00000100: |
559 | lcr |= UMCS_LCR_DATALEN60x01; |
560 | break; |
561 | case CS70x00000200: |
562 | lcr |= UMCS_LCR_DATALEN70x02; |
563 | break; |
564 | case CS80x00000300: |
565 | lcr |= UMCS_LCR_DATALEN80x03; |
566 | break; |
567 | } |
568 | |
569 | if (t->c_cflag & CRTSCTS0x00010000) |
570 | mcr |= UMCS_MCR_CTSRTS0x20; |
571 | else |
572 | mcr &= ~UMCS_MCR_CTSRTS0x20; |
573 | |
574 | if (t->c_cflag & CLOCAL0x00008000) |
575 | mcr &= ~UMCS_MCR_DTRDSR0x40; |
576 | else |
577 | mcr |= UMCS_MCR_DTRDSR0x40; |
578 | |
579 | if (umcs_set_uart_reg(sc, portno, UMCS_REG_LCR0x03, lcr)) |
580 | return (EIO5); |
581 | sc->sc_subdevs[portno].lcr = lcr; |
582 | |
583 | if (umcs_set_uart_reg(sc, portno, UMCS_REG_MCR0x04, mcr)) |
584 | return (EIO5); |
585 | sc->sc_subdevs[portno].mcr = mcr; |
586 | |
587 | error = umcs_set_baudrate(sc, portno, t->c_ospeed); |
588 | |
589 | return (error); |
590 | } |
591 | |
592 | void |
593 | umcs_dtr(struct umcs_softc *sc, int portno, int onoff) |
594 | { |
595 | uint8_t mcr = sc->sc_subdevs[portno].mcr; |
596 | |
597 | if (onoff) |
598 | mcr |= UMCS_MCR_DTR0x01; |
599 | else |
600 | mcr &= ~UMCS_MCR_DTR0x01; |
601 | |
602 | if (umcs_set_uart_reg(sc, portno, UMCS_REG_MCR0x04, mcr)) |
603 | return; |
604 | sc->sc_subdevs[portno].mcr = mcr; |
605 | } |
606 | |
607 | void |
608 | umcs_rts(struct umcs_softc *sc, int portno, int onoff) |
609 | { |
610 | uint8_t mcr = sc->sc_subdevs[portno].mcr; |
611 | |
612 | if (onoff) |
613 | mcr |= UMCS_MCR_RTS0x02; |
614 | else |
615 | mcr &= ~UMCS_MCR_RTS0x02; |
616 | |
617 | if (umcs_set_uart_reg(sc, portno, UMCS_REG_MCR0x04, mcr)) |
618 | return; |
619 | sc->sc_subdevs[portno].mcr = mcr; |
620 | } |
621 | |
622 | void |
623 | umcs_break(struct umcs_softc *sc, int portno, int onoff) |
624 | { |
625 | uint8_t lcr = sc->sc_subdevs[portno].lcr; |
626 | |
627 | if (onoff) |
628 | lcr |= UMCS_LCR_BREAK0x40; |
629 | else |
630 | lcr &= ~UMCS_LCR_BREAK0x40; |
631 | |
632 | if (umcs_set_uart_reg(sc, portno, UMCS_REG_LCR0x03, lcr)) |
633 | return; |
634 | sc->sc_subdevs[portno].lcr = lcr; |
635 | } |
636 | |
637 | int |
638 | umcs_open(void *self, int portno) |
639 | { |
640 | struct umcs_softc *sc = self; |
641 | int pn = sc->sc_subdevs[portno].pn; |
642 | int spreg = umcs_reg_sp(pn); |
643 | int ctrlreg = umcs_reg_ctrl(pn); |
644 | uint8_t mcr = sc->sc_subdevs[portno].mcr; |
645 | uint8_t lcr = sc->sc_subdevs[portno].lcr; |
Value stored to 'lcr' during its initialization is never read | |
646 | uint8_t data; |
647 | int error; |
648 | |
649 | if (usbd_is_dying(sc->sc_udev)) |
650 | return (EIO5); |
651 | |
652 | /* If it very first open, finish global configuration */ |
653 | if (!sc->sc_init_done) { |
654 | if (umcs_get_reg(sc, UMCS_CTRL10x01, &data) || |
655 | umcs_set_reg(sc, UMCS_CTRL10x01, data | UMCS_CTRL1_DRIVER_DONE0x08)) |
656 | return (EIO5); |
657 | sc->sc_init_done = 1; |
658 | } |
659 | |
660 | /* Toggle reset bit on-off */ |
661 | if (umcs_get_reg(sc, spreg, &data) || |
662 | umcs_set_reg(sc, spreg, data | UMCS_SPx_UART_RESET0x80) || |
663 | umcs_set_reg(sc, spreg, data & ~UMCS_SPx_UART_RESET0x80)) |
664 | return (EIO5); |
665 | |
666 | /* Set RS-232 mode */ |
667 | if (umcs_set_uart_reg(sc, portno, UMCS_REG_SCRATCHPAD0x07, |
668 | UMCS_SCRATCHPAD_RS2320x00)) |
669 | return (EIO5); |
670 | |
671 | /* Disable RX on time of initialization */ |
672 | if (umcs_get_reg(sc, ctrlreg, &data) || |
673 | umcs_set_reg(sc, ctrlreg, data | UMCS_CTRL_RX_DISABLE0x20)) |
674 | return (EIO5); |
675 | |
676 | /* Disable all interrupts */ |
677 | if (umcs_set_uart_reg(sc, portno, UMCS_REG_IER0x01, 0)) |
678 | return (EIO5); |
679 | |
680 | /* Reset FIFO -- documented */ |
681 | if (umcs_set_uart_reg(sc, portno, UMCS_REG_FCR0x02, 0) || |
682 | umcs_set_uart_reg(sc, portno, UMCS_REG_FCR0x02, |
683 | UMCS_FCR_ENABLE0x01 | UMCS_FCR_FLUSHRHR0x02 | |
684 | UMCS_FCR_FLUSHTHR0x04 | UMCS_FCR_RTL_1_140xa0)) |
685 | return (EIO5); |
686 | |
687 | /* Set 8 bit, no parity, 1 stop bit -- documented */ |
688 | lcr = UMCS_LCR_DATALEN80x03 | UMCS_LCR_STOPB10x00; |
689 | if (umcs_set_uart_reg(sc, portno, UMCS_REG_LCR0x03, lcr)) |
690 | return (EIO5); |
691 | sc->sc_subdevs[portno].lcr = lcr; |
692 | |
693 | /* |
694 | * Enable DTR/RTS on modem control, enable modem interrupts -- |
695 | * documented |
696 | */ |
697 | mcr = UMCS_MCR_DTR0x01 | UMCS_MCR_RTS0x02 | UMCS_MCR_IE0x04; |
698 | if (umcs_set_uart_reg(sc, portno, UMCS_REG_MCR0x04, mcr)) |
699 | return (EIO5); |
700 | sc->sc_subdevs[portno].mcr = mcr; |
701 | |
702 | /* Clearing Bulkin and Bulkout FIFO */ |
703 | if (umcs_get_reg(sc, spreg, &data)) |
704 | return (EIO5); |
705 | data |= UMCS_SPx_RESET_OUT_FIFO0x04|UMCS_SPx_RESET_IN_FIFO0x08; |
706 | if (umcs_set_reg(sc, spreg, data)) |
707 | return (EIO5); |
708 | data &= ~(UMCS_SPx_RESET_OUT_FIFO0x04|UMCS_SPx_RESET_IN_FIFO0x08); |
709 | if (umcs_set_reg(sc, spreg, data)) |
710 | return (EIO5); |
711 | |
712 | /* Set speed 9600 */ |
713 | if ((error = umcs_set_baudrate(sc, portno, 9600)) != 0) |
714 | return (error); |
715 | |
716 | /* Finally enable all interrupts -- documented */ |
717 | /* |
718 | * Copied from vendor driver, I don't know why we should read LCR |
719 | * here |
720 | */ |
721 | if (umcs_get_uart_reg(sc, portno, UMCS_REG_LCR0x03, |
722 | &sc->sc_subdevs[portno].lcr)) |
723 | return (EIO5); |
724 | if (umcs_set_uart_reg(sc, portno, UMCS_REG_IER0x01, |
725 | UMCS_IER_RXSTAT0x04 | UMCS_IER_MODEM0x08)) |
726 | return (EIO5); |
727 | |
728 | /* Enable RX */ |
729 | if (umcs_get_reg(sc, ctrlreg, &data) || |
730 | umcs_set_reg(sc, ctrlreg, data & ~UMCS_CTRL_RX_DISABLE0x20)) |
731 | return (EIO5); |
732 | |
733 | return (0); |
734 | } |
735 | |
736 | void |
737 | umcs_close(void *self, int portno) |
738 | { |
739 | struct umcs_softc *sc = self; |
740 | int pn = sc->sc_subdevs[portno].pn; |
741 | int ctrlreg = umcs_reg_ctrl(pn); |
742 | uint8_t data; |
743 | |
744 | if (usbd_is_dying(sc->sc_udev)) |
745 | return; |
746 | |
747 | umcs_set_uart_reg(sc, portno, UMCS_REG_MCR0x04, 0); |
748 | umcs_set_uart_reg(sc, portno, UMCS_REG_IER0x01, 0); |
749 | |
750 | /* Disable RX */ |
751 | if (umcs_get_reg(sc, ctrlreg, &data) || |
752 | umcs_set_reg(sc, ctrlreg, data | UMCS_CTRL_RX_DISABLE0x20)) |
753 | return; |
754 | } |
755 | |
756 | void |
757 | umcs_intr(struct usbd_xfer *xfer, void *priv, usbd_status status) |
758 | { |
759 | struct umcs_softc *sc = priv; |
760 | uint8_t *buf = sc->sc_ibuf; |
761 | int actlen, i; |
762 | |
763 | if (usbd_is_dying(sc->sc_udev)) |
764 | return; |
765 | |
766 | if (status == USBD_CANCELLED || status == USBD_IOERROR) |
767 | return; |
768 | |
769 | if (status != USBD_NORMAL_COMPLETION) { |
770 | DPRINTF("%s: interrupt status=%d\n", DEVNAME(sc), status); |
771 | usbd_clear_endpoint_stall_async(sc->sc_ipipe); |
772 | return; |
773 | } |
774 | |
775 | usbd_get_xfer_status(xfer, NULL((void *)0), NULL((void *)0), &actlen, NULL((void *)0)); |
776 | if (actlen != 5 && actlen != 13) { |
777 | printf("%s: invalid interrupt data length %d\n", DEVNAME(sc)((sc)->sc_dev.dv_xname), |
778 | actlen); |
779 | return; |
780 | } |
781 | |
782 | /* Check status of all ports */ |
783 | for (i = 0; i < sc->sc_numports; i++) { |
784 | uint8_t pn = sc->sc_subdevs[i].pn; |
785 | |
786 | if (buf[pn] & UMCS_ISR_NOPENDING0x01) |
787 | continue; |
788 | |
789 | DPRINTF("%s: port %d has pending interrupt: %02x, FIFO=%02x\n", |
790 | DEVNAME(sc), i, buf[pn] & UMCS_ISR_INTMASK, |
791 | buf[pn] & (~UMCS_ISR_INTMASK)); |
792 | |
793 | switch (buf[pn] & UMCS_ISR_INTMASK0x3f) { |
794 | case UMCS_ISR_RXERR0x06: |
795 | case UMCS_ISR_RXHASDATA0x04: |
796 | case UMCS_ISR_RXTIMEOUT0x0c: |
797 | case UMCS_ISR_MSCHANGE0x00: |
798 | sc->sc_subdevs[i].flags |= UMCS_STATCHG0x01; |
799 | task_add(systq, &sc->sc_status_task); |
800 | break; |
801 | default: |
802 | /* Do nothing */ |
803 | break; |
804 | } |
805 | } |
806 | } |
807 | |
808 | void |
809 | umcs_status_task(void *arg) |
810 | { |
811 | struct umcs_softc *sc = arg; |
812 | int i; |
813 | |
814 | for (i = 0; i < sc->sc_numports; i++) { |
815 | if ((sc->sc_subdevs[i].flags & UMCS_STATCHG0x01) == 0) |
816 | continue; |
817 | |
818 | sc->sc_subdevs[i].flags &= ~UMCS_STATCHG0x01; |
819 | ucom_status_change(sc->sc_subdevs[i].ucom); |
820 | } |
821 | } |