]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/spibus/spigen.c
Import the skein hashing algorithm, based on the threefish block cipher
[FreeBSD/FreeBSD.git] / sys / dev / spibus / spigen.c
1 /*-
2  * Copyright (c) 2015 Brian Fundakowski Feldman.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
14  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23  */
24
25 #include <sys/cdefs.h>
26 __FBSDID("$FreeBSD$");
27
28 #include <sys/param.h>
29 #include <sys/systm.h>
30 #include <sys/bus.h>
31 #include <sys/conf.h>
32 #include <sys/kernel.h>
33 #include <sys/lock.h>
34 #include <sys/malloc.h>
35 #include <sys/mman.h>
36 #include <sys/mutex.h>
37 #include <sys/module.h>
38 #include <sys/proc.h>
39 #include <sys/rwlock.h>
40 #include <sys/spigenio.h>
41 #include <sys/sysctl.h>
42 #include <sys/types.h>
43  
44 #include <vm/vm.h>
45 #include <vm/vm_extern.h>
46 #include <vm/vm_object.h>
47 #include <vm/vm_page.h>
48 #include <vm/vm_pager.h>
49
50 #include <dev/spibus/spi.h>
51
52 #include "spibus_if.h"
53
54 struct spigen_softc {
55         device_t sc_dev;
56         struct cdev *sc_cdev;
57         struct mtx sc_mtx;
58         uint32_t sc_clock_speed;
59         uint32_t sc_command_length_max; /* cannot change while mmapped */
60         uint32_t sc_data_length_max;    /* cannot change while mmapped */
61         vm_object_t sc_mmap_buffer;     /* command, then data */
62         vm_offset_t sc_mmap_kvaddr;
63         size_t sc_mmap_buffer_size;
64         int sc_mmap_busy;
65         int sc_debug;
66 };
67
68 static int
69 spigen_probe(device_t dev)
70 {
71         device_set_desc(dev, "SPI Generic IO");
72         return (0);
73 }
74
75 static int spigen_open(struct cdev *, int, int, struct thread *);
76 static int spigen_ioctl(struct cdev *, u_long, caddr_t, int, struct thread *);
77 static int spigen_close(struct cdev *, int, int, struct thread *);
78 static d_mmap_single_t spigen_mmap_single;
79
80 static struct cdevsw spigen_cdevsw = {
81         .d_version =     D_VERSION,
82         .d_name =        "spigen",
83         .d_open =        spigen_open,
84         .d_ioctl =       spigen_ioctl,
85         .d_mmap_single = spigen_mmap_single,
86         .d_close =       spigen_close
87 };
88
89 static int
90 spigen_command_length_max_proc(SYSCTL_HANDLER_ARGS)
91 {
92         struct spigen_softc *sc = (struct spigen_softc *)arg1;
93         uint32_t command_length_max;
94         int error;
95
96         mtx_lock(&sc->sc_mtx);
97         command_length_max = sc->sc_command_length_max;
98         mtx_unlock(&sc->sc_mtx);
99         error = sysctl_handle_int(oidp, &command_length_max,
100             sizeof(command_length_max), req);
101         if (error == 0 && req->newptr != NULL) {
102                 mtx_lock(&sc->sc_mtx);
103                 if (sc->sc_mmap_buffer != NULL)
104                         error = EBUSY;
105                 else
106                         sc->sc_command_length_max = command_length_max;
107                 mtx_unlock(&sc->sc_mtx);
108         }
109         return (error);
110 }
111
112 static int
113 spigen_data_length_max_proc(SYSCTL_HANDLER_ARGS)
114 {
115         struct spigen_softc *sc = (struct spigen_softc *)arg1;
116         uint32_t data_length_max;
117         int error;
118
119         mtx_lock(&sc->sc_mtx);
120         data_length_max = sc->sc_data_length_max;
121         mtx_unlock(&sc->sc_mtx);
122         error = sysctl_handle_int(oidp, &data_length_max,
123             sizeof(data_length_max), req);
124         if (error == 0 && req->newptr != NULL) {
125                 mtx_lock(&sc->sc_mtx);
126                 if (sc->sc_mmap_buffer != NULL)
127                         error = EBUSY;
128                 else
129                         sc->sc_data_length_max = data_length_max;
130                 mtx_unlock(&sc->sc_mtx);
131         }
132         return (error);
133 }
134
135 static void
136 spigen_sysctl_init(struct spigen_softc *sc)
137 {
138         struct sysctl_ctx_list *ctx;
139         struct sysctl_oid *tree_node;
140         struct sysctl_oid_list *tree;
141
142         /*
143          * Add system sysctl tree/handlers.
144          */
145         ctx = device_get_sysctl_ctx(sc->sc_dev);
146         tree_node = device_get_sysctl_tree(sc->sc_dev);
147         tree = SYSCTL_CHILDREN(tree_node);
148         SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "command_length_max",
149             CTLFLAG_MPSAFE | CTLFLAG_RW | CTLTYPE_UINT, sc, sizeof(*sc),
150             spigen_command_length_max_proc, "IU", "SPI command header portion (octets)");
151         SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "data_length_max",
152             CTLFLAG_MPSAFE | CTLFLAG_RW | CTLTYPE_UINT, sc, sizeof(*sc),
153             spigen_data_length_max_proc, "IU", "SPI data trailer portion (octets)");
154         SYSCTL_ADD_INT(ctx, tree, OID_AUTO, "data", CTLFLAG_RW,
155             &sc->sc_debug, 0, "debug flags");
156
157 }
158
159 static int
160 spigen_attach(device_t dev)
161 {
162         struct spigen_softc *sc;
163         const int unit = device_get_unit(dev);
164
165         sc = device_get_softc(dev);
166         sc->sc_dev = dev;
167         sc->sc_cdev = make_dev(&spigen_cdevsw, unit,
168             UID_ROOT, GID_OPERATOR, 0660, "spigen%d", unit);
169         sc->sc_cdev->si_drv1 = dev;
170         sc->sc_command_length_max = PAGE_SIZE;
171         sc->sc_data_length_max = PAGE_SIZE;
172         mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF);
173         spigen_sysctl_init(sc);
174
175         return (0);
176 }
177
178 static int 
179 spigen_open(struct cdev *dev, int oflags, int devtype, struct thread *td)
180 {
181
182         return (0);
183 }
184
185 static int
186 spigen_transfer(struct cdev *cdev, struct spigen_transfer *st)
187 {
188         struct spi_command transfer = SPI_COMMAND_INITIALIZER;
189         device_t dev = cdev->si_drv1;
190         struct spigen_softc *sc = device_get_softc(dev);
191         int error = 0;
192
193         mtx_lock(&sc->sc_mtx);
194         if (st->st_command.iov_len == 0 || st->st_data.iov_len == 0)
195                 error = EINVAL;
196         else if (st->st_command.iov_len > sc->sc_command_length_max ||
197             st->st_data.iov_len > sc->sc_data_length_max)
198                 error = ENOMEM;
199         mtx_unlock(&sc->sc_mtx);
200         if (error)
201                 return (error);
202         
203 #if 0
204         device_printf(dev, "cmd %p %u data %p %u\n", st->st_command.iov_base,
205             st->st_command.iov_len, st->st_data.iov_base, st->st_data.iov_len);
206 #endif
207         transfer.tx_cmd = transfer.rx_cmd = malloc(st->st_command.iov_len,
208             M_DEVBUF, M_WAITOK);
209         if (transfer.tx_cmd == NULL)
210                 return (ENOMEM);
211         transfer.tx_data = transfer.rx_data = malloc(st->st_data.iov_len,
212             M_DEVBUF, M_WAITOK);
213         if (transfer.tx_data == NULL) {
214                 free(transfer.tx_cmd, M_DEVBUF);
215                 return (ENOMEM);
216         }
217
218         error = copyin(st->st_command.iov_base, transfer.tx_cmd,
219             transfer.tx_cmd_sz = transfer.rx_cmd_sz = st->st_command.iov_len);  
220         if (error == 0)
221                 error = copyin(st->st_data.iov_base, transfer.tx_data,
222                     transfer.tx_data_sz = transfer.rx_data_sz =
223                                           st->st_data.iov_len); 
224         if (error == 0)
225                 error = SPIBUS_TRANSFER(device_get_parent(dev), dev, &transfer);
226         if (error == 0) {
227                 error = copyout(transfer.rx_cmd, st->st_command.iov_base,
228                     transfer.rx_cmd_sz);
229                 if (error == 0)
230                         error = copyout(transfer.rx_data, st->st_data.iov_base,
231                             transfer.rx_data_sz);
232         }
233
234         free(transfer.tx_cmd, M_DEVBUF);
235         free(transfer.tx_data, M_DEVBUF);
236         return (error);
237 }
238
239 static int
240 spigen_transfer_mmapped(struct cdev *cdev, struct spigen_transfer_mmapped *stm)
241 {
242         struct spi_command transfer = SPI_COMMAND_INITIALIZER;
243         device_t dev = cdev->si_drv1;
244         struct spigen_softc *sc = device_get_softc(dev);
245         int error = 0;
246
247         mtx_lock(&sc->sc_mtx);
248         if (sc->sc_mmap_busy)
249                 error = EBUSY;
250         else if (stm->stm_command_length > sc->sc_command_length_max ||
251             stm->stm_data_length > sc->sc_data_length_max)
252                 error = E2BIG;
253         else if (sc->sc_mmap_buffer == NULL)
254                 error = EINVAL;
255         else if (sc->sc_mmap_buffer_size <
256             stm->stm_command_length + stm->stm_data_length)
257                 error = ENOMEM;
258         if (error == 0)
259                 sc->sc_mmap_busy = 1;
260         mtx_unlock(&sc->sc_mtx);
261         if (error)
262                 return (error);
263         
264         transfer.tx_cmd = transfer.rx_cmd = (void *)sc->sc_mmap_kvaddr;
265         transfer.tx_cmd_sz = transfer.rx_cmd_sz = stm->stm_command_length;
266         transfer.tx_data = transfer.rx_data =
267             (void *)(sc->sc_mmap_kvaddr + stm->stm_command_length);
268         transfer.tx_data_sz = transfer.rx_data_sz = stm->stm_data_length;
269         error = SPIBUS_TRANSFER(device_get_parent(dev), dev, &transfer);
270
271         mtx_lock(&sc->sc_mtx);
272         KASSERT(sc->sc_mmap_busy, ("mmap no longer marked busy"));
273         sc->sc_mmap_busy = 0;
274         mtx_unlock(&sc->sc_mtx);
275         return (error);
276 }
277
278 static int
279 spigen_ioctl(struct cdev *cdev, u_long cmd, caddr_t data, int fflag,
280     struct thread *td)
281 {
282         device_t dev = cdev->si_drv1;
283         struct spigen_softc *sc = device_get_softc(dev);
284         int error;
285
286         switch (cmd) {
287         case SPIGENIOC_TRANSFER:
288                 error = spigen_transfer(cdev, (struct spigen_transfer *)data);
289                 break;
290         case SPIGENIOC_TRANSFER_MMAPPED:
291                 error = spigen_transfer_mmapped(cdev, (struct spigen_transfer_mmapped *)data);
292                 break;
293         case SPIGENIOC_GET_CLOCK_SPEED:
294                 mtx_lock(&sc->sc_mtx);
295                 *(uint32_t *)data = sc->sc_clock_speed;
296                 /* XXX TODO: implement spibus ivar call */
297                 mtx_unlock(&sc->sc_mtx);
298                 error = 0;
299                 break;
300         case SPIGENIOC_SET_CLOCK_SPEED:
301                 mtx_lock(&sc->sc_mtx);
302                 sc->sc_clock_speed = *(uint32_t *)data;
303                 mtx_unlock(&sc->sc_mtx);
304                 error = 0;
305                 break;
306         default:
307                 error = EOPNOTSUPP;
308         }
309         return (error);
310 }
311
312 static int
313 spigen_mmap_single(struct cdev *cdev, vm_ooffset_t *offset,
314     vm_size_t size, struct vm_object **object, int nprot)
315 {
316         device_t dev = cdev->si_drv1;
317         struct spigen_softc *sc = device_get_softc(dev);
318         vm_page_t *m;
319         size_t n, pages;
320
321         if (size == 0 ||
322             (nprot & (PROT_EXEC | PROT_READ | PROT_WRITE))
323             != (PROT_READ | PROT_WRITE))
324                 return (EINVAL);
325         size = roundup2(size, PAGE_SIZE);
326         pages = size / PAGE_SIZE;
327
328         mtx_lock(&sc->sc_mtx);
329         if (sc->sc_mmap_buffer != NULL) {
330                 mtx_unlock(&sc->sc_mtx);
331                 return (EBUSY);
332         } else if (size > sc->sc_command_length_max + sc->sc_data_length_max) {
333                 mtx_unlock(&sc->sc_mtx);
334                 return (E2BIG);
335         }
336         sc->sc_mmap_buffer_size = size;
337         *offset = 0;
338         sc->sc_mmap_buffer = *object = vm_pager_allocate(OBJT_PHYS, 0, size,
339             nprot, *offset, curthread->td_ucred);
340         m = malloc(sizeof(*m) * pages, M_TEMP, M_WAITOK);
341         VM_OBJECT_WLOCK(*object);
342         vm_object_reference_locked(*object); // kernel and userland both
343         for (n = 0; n < pages; n++) {
344                 m[n] = vm_page_grab(*object, n,
345                     VM_ALLOC_NOBUSY | VM_ALLOC_ZERO | VM_ALLOC_WIRED);
346                 m[n]->valid = VM_PAGE_BITS_ALL;
347         }
348         VM_OBJECT_WUNLOCK(*object);
349         sc->sc_mmap_kvaddr = kva_alloc(size);
350         pmap_qenter(sc->sc_mmap_kvaddr, m, pages);
351         free(m, M_TEMP);
352         mtx_unlock(&sc->sc_mtx);
353
354         if (*object == NULL)
355                  return (EINVAL);
356         return (0);
357 }
358
359 static int 
360 spigen_close(struct cdev *cdev, int fflag, int devtype, struct thread *td)
361 {
362         device_t dev = cdev->si_drv1;
363         struct spigen_softc *sc = device_get_softc(dev);
364
365         mtx_lock(&sc->sc_mtx);
366         if (sc->sc_mmap_buffer != NULL) {
367                 pmap_qremove(sc->sc_mmap_kvaddr,
368                     sc->sc_mmap_buffer_size / PAGE_SIZE);
369                 kva_free(sc->sc_mmap_kvaddr, sc->sc_mmap_buffer_size);
370                 sc->sc_mmap_kvaddr = 0;
371                 vm_object_deallocate(sc->sc_mmap_buffer);
372                 sc->sc_mmap_buffer = NULL;
373                 sc->sc_mmap_buffer_size = 0;
374         }
375         mtx_unlock(&sc->sc_mtx);
376         return (0);
377 }
378
379 static int
380 spigen_detach(device_t dev)
381 {
382
383         return (EIO);
384 }
385
386 static devclass_t spigen_devclass;
387
388 static device_method_t spigen_methods[] = {
389         /* Device interface */
390         DEVMETHOD(device_probe,         spigen_probe),
391         DEVMETHOD(device_attach,        spigen_attach),
392         DEVMETHOD(device_detach,        spigen_detach),
393
394         { 0, 0 }
395 };
396
397 static driver_t spigen_driver = {
398         "spigen",
399         spigen_methods,
400         sizeof(struct spigen_softc),
401 };
402
403 DRIVER_MODULE(spigen, spibus, spigen_driver, spigen_devclass, 0, 0);