1 | /* $NetBSD: stuirda.c,v 1.18 2016/07/07 06:55:42 msaitoh 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.18 2016/07/07 06:55:42 msaitoh Exp $" ); |
34 | |
35 | #include <sys/param.h> |
36 | |
37 | #include <sys/device.h> |
38 | #include <sys/systm.h> |
39 | #include <sys/kernel.h> |
40 | #include <sys/device.h> |
41 | #include <sys/ioctl.h> |
42 | #include <sys/conf.h> |
43 | #include <sys/file.h> |
44 | #include <sys/poll.h> |
45 | #include <sys/select.h> |
46 | #include <sys/proc.h> |
47 | |
48 | |
49 | #include <dev/firmload.h> |
50 | #include <dev/usb/usb.h> |
51 | #include <dev/usb/usbdi.h> |
52 | #include <dev/usb/usbdi_util.h> |
53 | #include <dev/usb/usbdevs.h> |
54 | |
55 | #include <dev/ir/ir.h> |
56 | #include <dev/ir/irdaio.h> |
57 | #include <dev/ir/irframevar.h> |
58 | |
59 | #include <dev/usb/uirdavar.h> |
60 | |
61 | #ifdef UIRDA_DEBUG |
62 | #define DPRINTF(x) if (stuirdadebug) printf x |
63 | #define DPRINTFN(n,x) if (stuirdadebug>(n)) printf x |
64 | int stuirdadebug = 1; |
65 | #else |
66 | #define DPRINTF(x) |
67 | #define DPRINTFN(n,x) |
68 | #endif |
69 | |
70 | struct stuirda_softc { |
71 | struct uirda_softc sc_uirda; |
72 | }; |
73 | |
74 | int stuirda_fwload(struct uirda_softc *sc); |
75 | |
76 | /* |
77 | * These devices need firmware download. |
78 | */ |
79 | Static const struct usb_devno stuirda_devs[] = { |
80 | { USB_VENDOR_SIGMATEL, USB_PRODUCT_SIGMATEL_SIR4116 }, |
81 | { USB_VENDOR_SIGMATEL, USB_PRODUCT_SIGMATEL_FIR4210 }, |
82 | { USB_VENDOR_SIGMATEL, USB_PRODUCT_SIGMATEL_VFIR4220 }, |
83 | }; |
84 | #define stuirda_lookup(v, p) (usb_lookup(stuirda_devs, v, p)) |
85 | |
86 | int stuirda_write(void *h, struct uio *uio, int flag); |
87 | |
88 | struct irframe_methods stuirda_methods = { |
89 | uirda_open, uirda_close, uirda_read, stuirda_write, uirda_poll, |
90 | uirda_kqfilter, uirda_set_params, uirda_get_speeds, |
91 | uirda_get_turnarounds |
92 | }; |
93 | |
94 | #define 3 |
95 | |
96 | #define stuirda_activate uirda_activate |
97 | #define stuirda_detach uirda_detach |
98 | |
99 | int stuirda_match(device_t, cfdata_t, void *); |
100 | void stuirda_attach(device_t, device_t, void *); |
101 | int stuirda_detach(device_t, int); |
102 | int stuirda_activate(device_t, enum devact); |
103 | extern struct cfdriver stuirda_cd; |
104 | CFATTACH_DECL_NEW(stuirda, sizeof(struct stuirda_softc), stuirda_match, |
105 | stuirda_attach, stuirda_detach, stuirda_activate); |
106 | |
107 | int |
108 | stuirda_match(device_t parent, cfdata_t match, void *aux) |
109 | { |
110 | struct usbif_attach_arg *uiaa = aux; |
111 | |
112 | DPRINTFN(50,("stuirda_match\n" )); |
113 | |
114 | if (stuirda_lookup(uiaa->uiaa_vendor, uiaa->uiaa_product) != NULL) |
115 | return UMATCH_VENDOR_PRODUCT; |
116 | |
117 | return UMATCH_NONE; |
118 | } |
119 | |
120 | void uirda_attach(device_t, device_t, void *); |
121 | |
122 | void |
123 | stuirda_attach(device_t parent, device_t self, void *aux) |
124 | { |
125 | struct stuirda_softc *sc = device_private(self); |
126 | |
127 | sc->sc_uirda.sc_loadfw = stuirda_fwload; |
128 | sc->sc_uirda.sc_irm = &stuirda_methods; |
129 | sc->sc_uirda.sc_hdszi = STUIRDA_HEADER_SIZE; |
130 | |
131 | uirda_attach(parent,self,aux); |
132 | } |
133 | |
134 | int |
135 | stuirda_fwload(struct uirda_softc *sc) |
136 | { |
137 | |
138 | |
139 | int rc; |
140 | firmware_handle_t fh; |
141 | off_t fwsize; |
142 | usb_device_descriptor_t usbddsc; |
143 | struct usbd_xfer * fwxfer; |
144 | struct usbd_pipe * fwpipe; |
145 | usbd_status status; |
146 | usb_device_request_t req; |
147 | char *buffer; |
148 | char *p; |
149 | char fwname[12]; |
150 | int n; |
151 | uint8_t *usbbuf; |
152 | /* size_t bsize; */ |
153 | |
154 | printf("%s: needing to download firmware\n" , |
155 | device_xname(sc->sc_dev)); |
156 | |
157 | status = usbd_get_device_desc(sc->sc_udev, &usbddsc); |
158 | if (status) { |
159 | printf("%s: can't get device descriptor, status %d\n" , |
160 | device_xname(sc->sc_dev), status); |
161 | return status; |
162 | } |
163 | |
164 | rc = usbd_get_class_desc(sc->sc_udev, UDESC_IRDA, 0, |
165 | USB_IRDA_DESCRIPTOR_SIZE, &sc->sc_irdadesc); |
166 | printf("error %d reading class desc\n" , rc); |
167 | |
168 | snprintf(fwname, sizeof(fwname), "4210%02x%02x.sb" , |
169 | usbddsc.bcdDevice[1], |
170 | usbddsc.bcdDevice[0]); |
171 | |
172 | printf("%s: Attempting to load firmware %s\n" , |
173 | device_xname(sc->sc_dev), fwname); |
174 | |
175 | rc = firmware_open("stuirda" , fwname, &fh); |
176 | |
177 | if (rc) { |
178 | printf("%s: Cannot load firmware\n" , |
179 | device_xname(sc->sc_dev)); |
180 | return rc; |
181 | } |
182 | fwsize = firmware_get_size(fh); |
183 | |
184 | printf("%s: Firmware size %lld\n" , |
185 | device_xname(sc->sc_dev), (long long)fwsize); |
186 | |
187 | buffer = firmware_malloc(fwsize); |
188 | if (buffer == NULL) { |
189 | printf("%s: Cannot load firmware: out of memory\n" , |
190 | device_xname(sc->sc_dev)); |
191 | goto giveup2; |
192 | } |
193 | |
194 | rc = firmware_read(fh, 0, buffer, (size_t)fwsize); |
195 | |
196 | if (rc) { |
197 | printf("%s: Cannot read firmware\n" , device_xname(sc->sc_dev)); |
198 | goto giveup3; |
199 | } |
200 | |
201 | for (p = buffer + sizeof("Product Version:" ); |
202 | p < buffer + fwsize - 5; p++) { |
203 | |
204 | if (0x1A == *p) |
205 | break; |
206 | } |
207 | if (0x1a != *p || memcmp(p+1, "STMP" , 4) != 0) { |
208 | /* firmware bad */ |
209 | printf("%s: Bad firmware\n" , device_xname(sc->sc_dev)); |
210 | goto giveup3; |
211 | } |
212 | |
213 | p += 5; |
214 | |
215 | req.bmRequestType = UT_WRITE_VENDOR_DEVICE; |
216 | req.bRequest = 2 /* XXX magic */; |
217 | USETW(req.wValue, 0); |
218 | USETW(req.wIndex, 0); |
219 | USETW(req.wLength, 0); |
220 | rc = usbd_do_request(sc->sc_udev, &req, 0); |
221 | if (rc) { |
222 | printf("%s: Cannot switch to f/w d/l mode, error %d\n" , |
223 | device_xname(sc->sc_dev), rc); |
224 | goto giveup3; |
225 | } |
226 | |
227 | delay(100000); |
228 | |
229 | rc = usbd_open_pipe(sc->sc_iface, sc->sc_wr_addr, 0, &fwpipe); |
230 | if (rc) { |
231 | printf("%s: Cannot open pipe, rc=%d\n" , |
232 | device_xname(sc->sc_dev), rc); |
233 | goto giveup3; |
234 | } |
235 | |
236 | int err = usbd_create_xfer(fwpipe, 1024, USBD_FORCE_SHORT_XFER, 0, |
237 | &fwxfer); |
238 | if (err) { |
239 | printf("%s: Cannot alloc xfer\n" , device_xname(sc->sc_dev)); |
240 | goto giveup4; |
241 | } |
242 | usbbuf = usbd_get_buffer(fwxfer); |
243 | n = (buffer + fwsize - p); |
244 | while (n > 0) { |
245 | if (n > 1023) |
246 | n = 1023; |
247 | memcpy(usbbuf, p, n); |
248 | rc = usbd_bulk_transfer(fwxfer, fwpipe, USBD_FORCE_SHORT_XFER, |
249 | 5000, usbbuf, &n); |
250 | printf("%s: write: rc=%d, %d left\n" , |
251 | device_xname(sc->sc_dev), rc, n); |
252 | if (rc) { |
253 | printf("%s: write: rc=%d, %d bytes written\n" , |
254 | device_xname(sc->sc_dev), rc, n); |
255 | goto giveup4; |
256 | } |
257 | printf("%s: written %d\n" , device_xname(sc->sc_dev), n); |
258 | p += n; |
259 | n = (buffer + fwsize - p); |
260 | } |
261 | delay(100000); |
262 | /* TODO: more code here */ |
263 | rc = 0; |
264 | usbd_destroy_xfer(fwxfer); |
265 | |
266 | giveup4: usbd_close_pipe(fwpipe); |
267 | giveup3: firmware_free(buffer, fwsize); |
268 | giveup2: firmware_close(fh); |
269 | |
270 | return rc; |
271 | |
272 | } |
273 | |
274 | int |
275 | stuirda_write(void *h, struct uio *uio, int flag) |
276 | { |
277 | struct uirda_softc *sc = h; |
278 | usbd_status err; |
279 | uint32_t n; |
280 | int error = 0; |
281 | |
282 | DPRINTFN(1,("%s: sc=%p\n" , __func__, sc)); |
283 | |
284 | if (sc->sc_dying) |
285 | return EIO; |
286 | |
287 | #ifdef DIAGNOSTIC |
288 | if (sc->sc_wr_buf == NULL) |
289 | return EINVAL; |
290 | #endif |
291 | |
292 | n = uio->uio_resid; |
293 | if (n > sc->sc_params.maxsize) |
294 | return EINVAL; |
295 | |
296 | sc->sc_refcnt++; |
297 | mutex_enter(&sc->sc_wr_buf_lk); |
298 | |
299 | sc->sc_wr_buf[0] = UIRDA_EB_NO_CHANGE | UIRDA_NO_SPEED; |
300 | |
301 | sc->sc_wr_buf[1] = 0; |
302 | sc->sc_wr_buf[2] = 7; /* XXX turnaround - maximum for now */ |
303 | if ((n > 0 && (n % 128) == 0 && (n % 512) != 0)) { |
304 | sc->sc_wr_buf[1] = 1; |
305 | } |
306 | |
307 | error = uiomove(sc->sc_wr_buf + STUIRDA_HEADER_SIZE, n, uio); |
308 | if (error) |
309 | goto done; |
310 | |
311 | DPRINTFN(1, ("uirdawrite: transfer %d bytes\n" , n)); |
312 | |
313 | n += STUIRDA_HEADER_SIZE + sc->sc_wr_buf[1]; |
314 | |
315 | err = usbd_bulk_transfer(sc->sc_wr_xfer, sc->sc_wr_pipe, |
316 | USBD_FORCE_SHORT_XFER, UIRDA_WR_TIMEOUT, sc->sc_wr_buf, &n); |
317 | DPRINTFN(2, ("uirdawrite: err=%d\n" , err)); |
318 | if (err) { |
319 | if (err == USBD_INTERRUPTED) |
320 | error = EINTR; |
321 | else if (err == USBD_TIMEOUT) |
322 | error = ETIMEDOUT; |
323 | else |
324 | error = EIO; |
325 | } |
326 | done: |
327 | mutex_exit(&sc->sc_wr_buf_lk); |
328 | if (--sc->sc_refcnt < 0) |
329 | usb_detach_wakeupold(sc->sc_dev); |
330 | |
331 | DPRINTFN(1,("%s: sc=%p done\n" , __func__, sc)); |
332 | return error; |
333 | } |
334 | |