xref: /netbsd-src/sys/dev/usb/stuirda.c (revision 3b24a1344ebe874599d235f52b0d5bcce4a2454d)
1 /*	$NetBSD: stuirda.c,v 1.20 2019/05/05 03:17:54 mrg Exp $	*/
2 
3 /*
4  * Copyright (c) 2001,2007 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Lennart Augustsson (lennart@augustsson.net).
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 __KERNEL_RCSID(0, "$NetBSD: stuirda.c,v 1.20 2019/05/05 03:17:54 mrg Exp $");
34 
35 #ifdef _KERNEL_OPT
36 #include "opt_usb.h"
37 #endif
38 
39 #include <sys/param.h>
40 
41 #include <sys/device.h>
42 #include <sys/systm.h>
43 #include <sys/kernel.h>
44 #include <sys/device.h>
45 #include <sys/ioctl.h>
46 #include <sys/conf.h>
47 #include <sys/file.h>
48 #include <sys/poll.h>
49 #include <sys/select.h>
50 #include <sys/proc.h>
51 
52 
53 #include <dev/firmload.h>
54 #include <dev/usb/usb.h>
55 #include <dev/usb/usbdi.h>
56 #include <dev/usb/usbdi_util.h>
57 #include <dev/usb/usbdevs.h>
58 
59 #include <dev/ir/ir.h>
60 #include <dev/ir/irdaio.h>
61 #include <dev/ir/irframevar.h>
62 
63 #include <dev/usb/uirdavar.h>
64 
65 #ifdef UIRDA_DEBUG
66 #define DPRINTF(x)	if (stuirdadebug) printf x
67 #define DPRINTFN(n,x)	if (stuirdadebug>(n)) printf x
68 int	stuirdadebug = 1;
69 #else
70 #define DPRINTF(x)
71 #define DPRINTFN(n,x)
72 #endif
73 
74 struct stuirda_softc {
75 	struct uirda_softc sc_uirda;
76 };
77 
78 int stuirda_fwload(struct uirda_softc *sc);
79 
80 /*
81  * These devices need firmware download.
82  */
83 Static const struct usb_devno stuirda_devs[] = {
84 	{ USB_VENDOR_SIGMATEL, USB_PRODUCT_SIGMATEL_SIR4116 },
85 	{ USB_VENDOR_SIGMATEL, USB_PRODUCT_SIGMATEL_FIR4210 },
86 	{ USB_VENDOR_SIGMATEL, USB_PRODUCT_SIGMATEL_VFIR4220 },
87 };
88 #define stuirda_lookup(v, p) (usb_lookup(stuirda_devs, v, p))
89 
90 int stuirda_write(void *h, struct uio *uio, int flag);
91 
92 struct irframe_methods stuirda_methods = {
93 	uirda_open, uirda_close, uirda_read, stuirda_write, uirda_poll,
94 	uirda_kqfilter, uirda_set_params, uirda_get_speeds,
95 	uirda_get_turnarounds
96 };
97 
98 #define STUIRDA_HEADER_SIZE 3
99 
100 #define stuirda_activate uirda_activate
101 #define stuirda_detach uirda_detach
102 
103 int	stuirda_match(device_t, cfdata_t, void *);
104 void	stuirda_attach(device_t, device_t, void *);
105 int	stuirda_detach(device_t, int);
106 int	stuirda_activate(device_t, enum devact);
107 
108 CFATTACH_DECL_NEW(stuirda, sizeof(struct stuirda_softc), stuirda_match,
109     stuirda_attach, stuirda_detach, stuirda_activate);
110 
111 int
stuirda_match(device_t parent,cfdata_t match,void * aux)112 stuirda_match(device_t parent, cfdata_t match, void *aux)
113 {
114 	struct usbif_attach_arg *uiaa = aux;
115 
116 	DPRINTFN(50,("stuirda_match\n"));
117 
118 	if (stuirda_lookup(uiaa->uiaa_vendor, uiaa->uiaa_product) != NULL)
119 		return UMATCH_VENDOR_PRODUCT;
120 
121 	return UMATCH_NONE;
122 }
123 
124 void uirda_attach(device_t, device_t, void *);
125 
126 void
stuirda_attach(device_t parent,device_t self,void * aux)127 stuirda_attach(device_t parent, device_t self, void *aux)
128 {
129 	struct stuirda_softc *sc = device_private(self);
130 
131 	sc->sc_uirda.sc_loadfw = stuirda_fwload;
132 	sc->sc_uirda.sc_irm = &stuirda_methods;
133 	sc->sc_uirda.sc_hdszi = STUIRDA_HEADER_SIZE;
134 
135 	uirda_attach(parent,self,aux);
136 }
137 
138 int
stuirda_fwload(struct uirda_softc * sc)139 stuirda_fwload(struct uirda_softc *sc)
140 {
141 
142 
143 	int rc;
144 	firmware_handle_t fh;
145 	off_t fwsize;
146 	usb_device_descriptor_t usbddsc;
147 	struct usbd_xfer *	fwxfer;
148 	struct usbd_pipe *	fwpipe;
149 	usbd_status status;
150 	usb_device_request_t req;
151 	char *buffer;
152 	char *p;
153 	char fwname[12];
154 	int n;
155 	uint8_t *usbbuf;
156 	/* size_t bsize; */
157 
158 	printf("%s: needing to download firmware\n",
159 		device_xname(sc->sc_dev));
160 
161 	status = usbd_get_device_desc(sc->sc_udev, &usbddsc);
162 	if (status) {
163 		printf("%s: can't get device descriptor, status %d\n",
164 		    device_xname(sc->sc_dev), status);
165 		return status;
166 	}
167 
168 	rc = usbd_get_class_desc(sc->sc_udev, UDESC_IRDA, 0,
169 		USB_IRDA_DESCRIPTOR_SIZE, &sc->sc_irdadesc);
170 	printf("error %d reading class desc\n", rc);
171 
172 	snprintf(fwname, sizeof(fwname), "4210%02x%02x.sb",
173 		usbddsc.bcdDevice[1],
174 		usbddsc.bcdDevice[0]);
175 
176 	printf("%s: Attempting to load firmware %s\n",
177 		device_xname(sc->sc_dev), fwname);
178 
179 	rc = firmware_open("stuirda", fwname, &fh);
180 
181 	if (rc) {
182 		printf("%s: Cannot load firmware\n",
183 			device_xname(sc->sc_dev));
184 		return rc;
185 	}
186 	fwsize = firmware_get_size(fh);
187 
188 	printf("%s: Firmware size %lld\n",
189 		device_xname(sc->sc_dev), (long long)fwsize);
190 
191 	buffer = firmware_malloc(fwsize);
192 	if (buffer == NULL) {
193 		printf("%s: Cannot load firmware: out of memory\n",
194 			device_xname(sc->sc_dev));
195 		goto giveup2;
196 	}
197 
198 	rc = firmware_read(fh, 0, buffer, (size_t)fwsize);
199 
200 	if (rc) {
201 		printf("%s: Cannot read firmware\n", device_xname(sc->sc_dev));
202 		goto giveup3;
203 	}
204 
205 	for (p = buffer + sizeof("Product Version:");
206 	    p < buffer + fwsize - 5; p++) {
207 
208 		if (0x1A == *p)
209 			break;
210 	}
211 	if (0x1a != *p || memcmp(p+1, "STMP", 4) != 0) {
212 		/* firmware bad */
213 		printf("%s: Bad firmware\n", device_xname(sc->sc_dev));
214 		goto giveup3;
215 	}
216 
217 	p += 5;
218 
219 	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
220 	req.bRequest = 2 /* XXX magic */;
221 	USETW(req.wValue, 0);
222 	USETW(req.wIndex, 0);
223 	USETW(req.wLength, 0);
224 	rc = usbd_do_request(sc->sc_udev, &req, 0);
225 	if (rc) {
226 		printf("%s: Cannot switch to f/w d/l mode, error %d\n",
227 			device_xname(sc->sc_dev), rc);
228 		goto giveup3;
229 	}
230 
231 	delay(100000);
232 
233 	rc = usbd_open_pipe(sc->sc_iface, sc->sc_wr_addr, 0, &fwpipe);
234 	if (rc) {
235 		printf("%s: Cannot open pipe, rc=%d\n",
236 		    device_xname(sc->sc_dev), rc);
237 		goto giveup3;
238 	}
239 
240 	int err = usbd_create_xfer(fwpipe, 1024, USBD_FORCE_SHORT_XFER, 0,
241 	    &fwxfer);
242 	if (err) {
243 		printf("%s: Cannot alloc xfer\n", device_xname(sc->sc_dev));
244 		goto giveup4;
245 	}
246 	usbbuf = usbd_get_buffer(fwxfer);
247 	n = (buffer + fwsize - p);
248 	while (n > 0) {
249 		if (n > 1023)
250 			n = 1023;
251 		memcpy(usbbuf, p, n);
252 		rc = usbd_bulk_transfer(fwxfer, fwpipe, USBD_FORCE_SHORT_XFER,
253 		    5000, usbbuf, &n);
254 		printf("%s: write: rc=%d, %d left\n",
255 		    device_xname(sc->sc_dev), rc, n);
256 		if (rc) {
257 			printf("%s: write: rc=%d, %d bytes written\n",
258 			    device_xname(sc->sc_dev), rc, n);
259 			goto giveup4;
260 		}
261 		printf("%s: written %d\n", device_xname(sc->sc_dev), n);
262 		p += n;
263 		n = (buffer + fwsize - p);
264 	}
265 	delay(100000);
266 	/* TODO: more code here */
267 	rc = 0;
268 	usbd_destroy_xfer(fwxfer);
269 
270 	giveup4: usbd_close_pipe(fwpipe);
271 	giveup3: firmware_free(buffer, fwsize);
272 	giveup2: firmware_close(fh);
273 
274 	return rc;
275 
276 }
277 
278 int
stuirda_write(void * h,struct uio * uio,int flag)279 stuirda_write(void *h, struct uio *uio, int flag)
280 {
281 	struct uirda_softc *sc = h;
282 	usbd_status err;
283 	uint32_t n;
284 	int error = 0;
285 
286 	DPRINTFN(1,("%s: sc=%p\n", __func__, sc));
287 
288 	if (sc->sc_dying)
289 		return EIO;
290 
291 #ifdef DIAGNOSTIC
292 	if (sc->sc_wr_buf == NULL)
293 		return EINVAL;
294 #endif
295 
296 	n = uio->uio_resid;
297 	if (n > sc->sc_params.maxsize)
298 		return EINVAL;
299 
300 	sc->sc_refcnt++;
301 	mutex_enter(&sc->sc_wr_buf_lk);
302 
303 	sc->sc_wr_buf[0] = UIRDA_EB_NO_CHANGE | UIRDA_NO_SPEED;
304 
305 	sc->sc_wr_buf[1] = 0;
306 	sc->sc_wr_buf[2] = 7; /* XXX turnaround - maximum for now */
307 	if ((n > 0 && (n % 128) == 0 && (n % 512) != 0)) {
308 		sc->sc_wr_buf[1] = 1;
309 	}
310 
311 	error = uiomove(sc->sc_wr_buf + STUIRDA_HEADER_SIZE, n, uio);
312 	if (error)
313 		goto done;
314 
315 	DPRINTFN(1, ("uirdawrite: transfer %d bytes\n", n));
316 
317 	n += STUIRDA_HEADER_SIZE + sc->sc_wr_buf[1];
318 
319 	err = usbd_bulk_transfer(sc->sc_wr_xfer, sc->sc_wr_pipe,
320 	    USBD_FORCE_SHORT_XFER, UIRDA_WR_TIMEOUT, sc->sc_wr_buf, &n);
321 	DPRINTFN(2, ("uirdawrite: err=%d\n", err));
322 	if (err) {
323 		if (err == USBD_INTERRUPTED)
324 			error = EINTR;
325 		else if (err == USBD_TIMEOUT)
326 			error = ETIMEDOUT;
327 		else
328 			error = EIO;
329 	}
330 done:
331 	mutex_exit(&sc->sc_wr_buf_lk);
332 	if (--sc->sc_refcnt < 0)
333 		usb_detach_wakeupold(sc->sc_dev);
334 
335 	DPRINTFN(1,("%s: sc=%p done\n", __func__, sc));
336 	return error;
337 }
338