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