Bug Summary

File:dev/usb/umcs.c
Warning:line 543, column 3
Value stored to 'lcr' is never read

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple amd64-unknown-openbsd7.0 -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name umcs.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model static -mframe-pointer=all -relaxed-aliasing -fno-rounding-math -mconstructor-aliases -ffreestanding -mcmodel=kernel -target-cpu x86-64 -target-feature +retpoline-indirect-calls -target-feature +retpoline-indirect-branches -target-feature -sse2 -target-feature -sse -target-feature -3dnow -target-feature -mmx -target-feature +save-args -disable-red-zone -no-implicit-float -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/usr/src/sys/arch/amd64/compile/GENERIC.MP/obj -nostdsysteminc -nobuiltininc -resource-dir /usr/local/lib/clang/13.0.0 -I /usr/src/sys -I /usr/src/sys/arch/amd64/compile/GENERIC.MP/obj -I /usr/src/sys/arch -I /usr/src/sys/dev/pci/drm/include -I /usr/src/sys/dev/pci/drm/include/uapi -I /usr/src/sys/dev/pci/drm/amd/include/asic_reg -I /usr/src/sys/dev/pci/drm/amd/include -I /usr/src/sys/dev/pci/drm/amd/amdgpu -I /usr/src/sys/dev/pci/drm/amd/display -I /usr/src/sys/dev/pci/drm/amd/display/include -I /usr/src/sys/dev/pci/drm/amd/display/dc -I /usr/src/sys/dev/pci/drm/amd/display/amdgpu_dm -I /usr/src/sys/dev/pci/drm/amd/pm/inc -I /usr/src/sys/dev/pci/drm/amd/pm/swsmu -I /usr/src/sys/dev/pci/drm/amd/pm/swsmu/smu11 -I /usr/src/sys/dev/pci/drm/amd/pm/swsmu/smu12 -I /usr/src/sys/dev/pci/drm/amd/pm/powerplay -I /usr/src/sys/dev/pci/drm/amd/pm/powerplay/hwmgr -I /usr/src/sys/dev/pci/drm/amd/pm/powerplay/smumgr -I /usr/src/sys/dev/pci/drm/amd/display/dc/inc -I /usr/src/sys/dev/pci/drm/amd/display/dc/inc/hw -I /usr/src/sys/dev/pci/drm/amd/display/dc/clk_mgr -I /usr/src/sys/dev/pci/drm/amd/display/modules/inc -I /usr/src/sys/dev/pci/drm/amd/display/modules/hdcp -I /usr/src/sys/dev/pci/drm/amd/display/dmub/inc -I /usr/src/sys/dev/pci/drm/i915 -D DDB -D DIAGNOSTIC -D KTRACE -D ACCOUNTING -D KMEMSTATS -D PTRACE -D POOL_DEBUG -D CRYPTO -D SYSVMSG -D SYSVSEM -D SYSVSHM -D UVM_SWAP_ENCRYPT -D FFS -D FFS2 -D FFS_SOFTUPDATES -D UFS_DIRHASH -D QUOTA -D EXT2FS -D MFS -D NFSCLIENT -D NFSSERVER -D CD9660 -D UDF -D MSDOSFS -D FIFO -D FUSE -D SOCKET_SPLICE -D TCP_ECN -D TCP_SIGNATURE -D INET6 -D IPSEC -D PPP_BSDCOMP -D PPP_DEFLATE -D PIPEX -D MROUTING -D MPLS -D BOOT_CONFIG -D USER_PCICONF -D APERTURE -D MTRR -D NTFS -D HIBERNATE -D PCIVERBOSE -D USBVERBOSE -D WSDISPLAY_COMPAT_USL -D WSDISPLAY_COMPAT_RAWKBD -D WSDISPLAY_DEFAULTSCREENS=6 -D X86EMU -D ONEWIREVERBOSE -D MULTIPROCESSOR -D MAXUSERS=80 -D _KERNEL -D CONFIG_DRM_AMD_DC_DCN3_0 -O2 -Wno-pointer-sign -Wno-address-of-packed-member -Wno-constant-conversion -Wno-unused-but-set-variable -Wno-gnu-folding-constant -fdebug-compilation-dir=/usr/src/sys/arch/amd64/compile/GENERIC.MP/obj -ferror-limit 19 -fwrapv -D_RET_PROTECTOR -ret-protector -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-valloc -fno-builtin-free -fno-builtin-strdup -fno-builtin-strndup -analyzer-output=html -faddrsig -o /usr/obj/sys/arch/amd64/compile/GENERIC.MP/scan-build/2022-01-12-131800-47421-1 -x c /usr/src/sys/dev/usb/umcs.c
1/* $OpenBSD: umcs.c,v 1.9 2022/01/09 05:43:01 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 */
76struct 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
86struct 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
100int umcs_get_reg(struct umcs_softc *, uint8_t, uint8_t *);
101int umcs_set_reg(struct umcs_softc *, uint8_t, uint8_t);
102int umcs_get_uart_reg(struct umcs_softc *, uint8_t, uint8_t, uint8_t *);
103int umcs_set_uart_reg(struct umcs_softc *, uint8_t, uint8_t, uint8_t);
104int umcs_calc_baudrate(uint32_t, uint16_t *, uint8_t *);
105int umcs_set_baudrate(struct umcs_softc *, uint8_t, uint32_t);
106void umcs_dtr(struct umcs_softc *, int, int);
107void umcs_rts(struct umcs_softc *, int, int);
108void umcs_break(struct umcs_softc *, int, int);
109
110int umcs_match(struct device *, void *, void *);
111void umcs_attach(struct device *, struct device *, void *);
112int umcs_detach(struct device *, int);
113void umcs_intr(struct usbd_xfer *, void *, usbd_status);
114void umcs_status_task(void *);
115
116void umcs_get_status(void *, int, uint8_t *, uint8_t *);
117void umcs_set(void *, int, int, int);
118int umcs_param(void *, int, struct termios *);
119int umcs_open(void *, int);
120void umcs_close(void *, int);
121
122struct 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
133const 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
140struct cfdriver umcs_cd = {
141 NULL((void *)0), "umcs", DV_DULL
142};
143
144const struct cfattach umcs_ca = {
145 sizeof(struct umcs_softc), umcs_match, umcs_attach, umcs_detach
146};
147
148
149static inline int
150umcs_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
162static inline int
163umcs_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
175int
176umcs_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
187void
188umcs_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
322int
323umcs_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
339int
340umcs_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
356int
357umcs_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
377int
378umcs_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
398int
399umcs_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 */
440static const uint32_t umcs_baudrate_divisors[] = {
441 0, 115200, 230400, 403200, 460800, 806400, 921600, 1572864, 3145728,
442};
443
444int
445umcs_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
466int
467umcs_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
486void
487umcs_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
505void
506umcs_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
528int
529umcs_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;
Value stored to 'lcr' is never read
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
592void
593umcs_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
607void
608umcs_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
622void
623umcs_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
637int
638umcs_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;
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
736void
737umcs_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
756void
757umcs_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
808void
809umcs_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}