Initial import from FreeBSD RELENG_4:
[dragonfly.git] / sys / bus / pccard / pccard.c
1 /*
2  *      pccard.c - Interface code for PC-CARD controllers.
3  *
4  *      June 1995, Andrew McRae (andrew@mega.com.au)
5  *-------------------------------------------------------------------------
6  *
7  * Copyright (c) 2001 M. Warner Losh.  All rights reserved.
8  * Copyright (c) 1995 Andrew McRae.  All rights reserved.
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  * 3. The name of the author may not be used to endorse or promote products
19  *    derived from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  *
32  * $FreeBSD: src/sys/pccard/pccard.c,v 1.106.2.15 2003/02/26 18:42:00 imp Exp $
33  */
34
35 #include <sys/param.h>
36 #include <sys/types.h>
37 #include <sys/systm.h>
38 #include <sys/kernel.h>
39 #include <sys/malloc.h>
40 #include <sys/sysctl.h>
41 #include <sys/conf.h>
42 #include <sys/uio.h>
43 #include <sys/poll.h>
44 #include <sys/bus.h>
45 #include <sys/proc.h>
46 #include <machine/bus.h>
47
48 #include <pccard/cardinfo.h>
49 #include <pccard/driver.h>
50 #include <pccard/slot.h>
51 #include <pccard/pccard_nbk.h>
52
53 #include <machine/md_var.h>
54
55 #define MIN(a,b)        ((a)<(b)?(a):(b))
56
57 static int              allocate_driver(struct slot *, struct dev_desc *);
58 static void             inserted(void *);
59 static void             disable_slot(struct slot *);
60 static void             disable_slot_to(struct slot *);
61 static void             power_off_slot(void *);
62
63 /*
64  *      The driver interface for read/write uses a block
65  *      of memory in the ISA I/O memory space allocated via
66  *      an ioctl setting.
67  *
68  *      Now that we have different bus attachments, we should really
69  *      use a better algorythm to allocate memory.
70  */
71 static unsigned long pccard_mem;        /* Physical memory */
72 static unsigned char *pccard_kmem;      /* Kernel virtual address */
73 static struct resource *pccard_mem_res;
74 static int pccard_mem_rid;
75
76 static  d_open_t        crdopen;
77 static  d_close_t       crdclose;
78 static  d_read_t        crdread;
79 static  d_write_t       crdwrite;
80 static  d_ioctl_t       crdioctl;
81 static  d_poll_t        crdpoll;
82
83 #define CDEV_MAJOR 50
84 static struct cdevsw crd_cdevsw = {
85         /* open */      crdopen,
86         /* close */     crdclose,
87         /* read */      crdread,
88         /* write */     crdwrite,
89         /* ioctl */     crdioctl,
90         /* poll */      crdpoll,
91         /* mmap */      nommap,
92         /* strategy */  nostrategy,
93         /* name */      "crd",
94         /* maj */       CDEV_MAJOR,
95         /* dump */      nodump,
96         /* psize */     nopsize,
97         /* flags */     0,
98 };
99
100 /*
101  *      Power off the slot.
102  *      (doing it immediately makes the removal of some cards unstable)
103  */
104 static void
105 power_off_slot(void *arg)
106 {
107         struct slot *slt = (struct slot *)arg;
108         int s;
109
110         /*
111          * The following will generate an interrupt.  So, to hold off
112          * the interrupt unitl after disable runs so that we can get rid
113          * rid of the interrupt before it becomes unsafe to touch the
114          * device.
115          *
116          * XXX In current, the spl stuff is a nop.
117          */
118         s = splhigh();
119         /* Power off the slot. */
120         slt->pwr_off_pending = 0;
121         slt->ctrl->disable(slt);
122         splx(s);
123 }
124
125 /*
126  *      disable_slot - Disables the slot by removing
127  *      the power and unmapping the I/O
128  */
129 static void
130 disable_slot(struct slot *slt)
131 {
132         device_t pccarddev;
133         device_t *kids;
134         int nkids;
135         int i;
136         int ret;
137
138         /*
139          * Note that a race condition is possible here; if a
140          * driver is accessing the device and it is removed, then
141          * all bets are off...
142          */
143         pccarddev = slt->dev;
144         device_get_children(pccarddev, &kids, &nkids);
145         for (i = 0; i < nkids; i++) {
146                 if ((ret = device_delete_child(pccarddev, kids[i])) != 0)
147                         printf("pccard: delete of %s failed: %d\n",
148                                 device_get_nameunit(kids[i]), ret);
149         }
150         free(kids, M_TEMP);
151
152         /* Power off the slot 1/2 second after removal of the card */
153         slt->poff_ch = timeout(power_off_slot, (caddr_t)slt, hz / 2);
154         slt->pwr_off_pending = 1;
155 }
156
157 static void
158 disable_slot_to(struct slot *slt)
159 {
160         disable_slot(slt);
161         if (slt->state == empty)
162                 printf("pccard: card removed, slot %d\n", slt->slotnum);
163         else
164                 printf("pccard: card deactivated, slot %d\n", slt->slotnum);
165         pccard_remove_beep();
166         selwakeup(&slt->selp);
167 }
168
169 /*
170  *      pccard_init_slot - Initialize the slot controller and attach various
171  * things to it.  We also make the device for it.  We create the device that
172  * will be exported to devfs.
173  */
174 struct slot *
175 pccard_init_slot(device_t dev, struct slot_ctrl *ctrl)
176 {
177         int             slotno;
178         struct slot     *slt;
179
180         slt = PCCARD_DEVICE2SOFTC(dev);
181         slotno = device_get_unit(dev);
182         slt->dev = dev;
183         slt->d = make_dev(&crd_cdevsw, slotno, 0, 0, 0600, "card%d", slotno);
184         slt->d->si_drv1 = slt;
185         slt->ctrl = ctrl;
186         slt->slotnum = slotno;
187         callout_handle_init(&slt->insert_ch);
188         callout_handle_init(&slt->poff_ch);
189
190         return (slt);
191 }
192
193 /*
194  *      allocate_driver - Create a new device entry for this
195  *      slot, and attach a driver to it.
196  */
197 static int
198 allocate_driver(struct slot *slt, struct dev_desc *desc)
199 {
200         struct pccard_devinfo *devi;
201         device_t pccarddev;
202         int err, irq = 0;
203         device_t child;
204         device_t *devs;
205         int count;
206
207         pccarddev = slt->dev;
208         err = device_get_children(pccarddev, &devs, &count);
209         if (err != 0)
210                 return (err);
211         free(devs, M_TEMP);
212         if (count) {
213                 device_printf(pccarddev,
214                     "Can not attach more than one child.\n");
215                 return (EIO);
216         }
217         irq = ffs(desc->irqmask) - 1;
218         MALLOC(devi, struct pccard_devinfo *, sizeof(*devi), M_DEVBUF,
219             M_WAITOK | M_ZERO);
220         strcpy(devi->name, desc->name);
221         /*
222          *      Create an entry for the device under this slot.
223          */
224         devi->running = 1;
225         devi->slt = slt;
226         bcopy(desc->misc, devi->misc, sizeof(desc->misc));
227         strcpy(devi->manufstr, desc->manufstr);
228         strcpy(devi->versstr, desc->versstr);
229         devi->manufacturer = desc->manufacturer;
230         devi->product = desc->product;
231         devi->prodext = desc->prodext;
232         resource_list_init(&devi->resources);
233         child = device_add_child(pccarddev, devi->name, desc->unit);
234         if (child == NULL) {
235                 if (desc->unit != -1)
236                         device_printf(pccarddev,
237                             "Unit %d failed for %s, try a different unit\n",
238                             desc->unit, devi->name);
239                 else
240                         device_printf(pccarddev,
241                             "No units available for %s.  Impossible?\n",
242                             devi->name);
243                 return (EIO);
244         }
245         device_set_flags(child, desc->flags);
246         device_set_ivars(child, devi);
247         if (bootverbose) {
248                 device_printf(pccarddev, "Assigning %s:",
249                     device_get_nameunit(child));
250                 if (desc->iobase)
251                         printf(" io 0x%x-0x%x",
252                             desc->iobase, desc->iobase + desc->iosize - 1);
253                 if (irq)
254                         printf(" irq %d", irq);
255                 if (desc->mem)
256                         printf(" mem 0x%lx-0x%lx", desc->mem,
257                             desc->mem + desc->memsize - 1);
258                 printf(" flags 0x%x\n", desc->flags);
259         }
260         err = bus_set_resource(child, SYS_RES_IOPORT, 0, desc->iobase,
261             desc->iosize);
262         if (err)
263                 goto err;
264         if (irq)
265                 err = bus_set_resource(child, SYS_RES_IRQ, 0, irq, 1);
266         if (err)
267                 goto err;
268         if (desc->memsize) {
269                 err = bus_set_resource(child, SYS_RES_MEMORY, 0, desc->mem,
270                     desc->memsize);
271                 if (err)
272                         goto err;
273         }
274         err = device_probe_and_attach(child);
275         /*
276          * XXX We unwisely assume that the detach code won't run while the
277          * XXX the attach code is attaching.  Someone should put some
278          * XXX interlock code.  This can happen if probe/attach takes a while
279          * XXX and the user ejects the card, which causes the detach
280          * XXX function to be called.
281          */
282         strncpy(desc->name, device_get_nameunit(child), sizeof(desc->name));
283         desc->name[sizeof(desc->name) - 1] = '\0';
284 err:
285         if (err)
286                 device_delete_child(pccarddev, child);
287         return (err);
288 }
289
290 /*
291  *      card insert routine - Called from a timeout to debounce
292  *      insertion events.
293  */
294 static void
295 inserted(void *arg)
296 {
297         struct slot *slt = arg;
298
299         slt->state = filled;
300         /*
301          * Disable any pending timeouts for this slot, and explicitly
302          * power it off right now.  Then, re-enable the power using
303          * the (possibly new) power settings.
304          */
305         untimeout(power_off_slot, (caddr_t)slt, slt->poff_ch);
306         power_off_slot(slt);
307
308         /*
309          *      Enable 5V to the card so that the CIS can be read.  Well,
310          * enable the most natural voltage so that the CIS can be read.
311          */
312         slt->pwr.vcc = -1;
313         slt->pwr.vpp = -1;
314         slt->ctrl->power(slt);
315
316         printf("pccard: card inserted, slot %d\n", slt->slotnum);
317         pccard_insert_beep();
318         slt->ctrl->reset(slt);
319 }
320
321 /*
322  *      Card event callback. Called at splhigh to prevent
323  *      device interrupts from interceding.
324  */
325 void
326 pccard_event(struct slot *slt, enum card_event event)
327 {
328         if (slt->insert_seq) {
329                 slt->insert_seq = 0;
330                 untimeout(inserted, (void *)slt, slt->insert_ch);
331         }
332
333         switch(event) {
334         case card_removed:
335         case card_deactivated:
336                 if (slt->state == filled || slt->state == inactive) {
337                         if (event == card_removed)
338                                 slt->state = empty;
339                         else
340                                 slt->state = inactive;
341                         disable_slot_to(slt);
342                 }
343                 break;
344         case card_inserted:
345                 slt->insert_seq = 1;
346                 slt->insert_ch = timeout(inserted, (void *)slt, hz/4);
347                 break;
348         }
349 }
350
351 /*
352  *      Device driver interface.
353  */
354 static  int
355 crdopen(dev_t dev, int oflags, int devtype, d_thread_t *td)
356 {
357         struct slot *slt = PCCARD_DEV2SOFTC(dev);
358
359         if (slt == NULL)
360                 return (ENXIO);
361         if (slt->rwmem == 0)
362                 slt->rwmem = MDF_ATTR;
363         return (0);
364 }
365
366 /*
367  *      Close doesn't de-allocate any resources, since
368  *      slots may be assigned to drivers already.
369  */
370 static  int
371 crdclose(dev_t dev, int fflag, int devtype, d_thread_t *td)
372 {
373         return (0);
374 }
375
376 /*
377  *      read interface. Map memory at lseek offset,
378  *      then transfer to user space.
379  */
380 static  int
381 crdread(dev_t dev, struct uio *uio, int ioflag)
382 {
383         struct slot *slt = PCCARD_DEV2SOFTC(dev);
384         struct mem_desc *mp, oldmap;
385         unsigned char *p;
386         unsigned int offs;
387         int error = 0, win, count;
388
389         if (slt == 0 || slt->state != filled)
390                 return (ENXIO);
391         if (pccard_mem == 0)
392                 return (ENOMEM);
393         for (win = slt->ctrl->maxmem - 1; win >= 0; win--)
394                 if ((slt->mem[win].flags & MDF_ACTIVE) == 0)
395                         break;
396         if (win < 0)
397                 return (EBUSY);
398         mp = &slt->mem[win];
399         oldmap = *mp;
400         mp->flags = slt->rwmem | MDF_ACTIVE;
401         while (uio->uio_resid && error == 0) {
402                 mp->card = uio->uio_offset;
403                 mp->size = PCCARD_MEMSIZE;
404                 mp->start = (caddr_t)(void *)(uintptr_t)pccard_mem;
405                 if ((error = slt->ctrl->mapmem(slt, win)) != 0)
406                         break;
407                 offs = (unsigned int)uio->uio_offset & (PCCARD_MEMSIZE - 1);
408                 p = pccard_kmem + offs;
409                 count = MIN(PCCARD_MEMSIZE - offs, uio->uio_resid);
410                 error = uiomove(p, count, uio);
411         }
412         /*
413          *      Restore original map.
414          */
415         *mp = oldmap;
416         slt->ctrl->mapmem(slt, win);
417
418         return (error);
419 }
420
421 /*
422  *      crdwrite - Write data to card memory.
423  *      Handles wrap around so that only one memory
424  *      window is used.
425  */
426 static  int
427 crdwrite(dev_t dev, struct uio *uio, int ioflag)
428 {
429         struct slot *slt = PCCARD_DEV2SOFTC(dev);
430         struct mem_desc *mp, oldmap;
431         unsigned char *p;
432         unsigned int offs;
433         int error = 0, win, count;
434
435         if (slt == 0 || slt->state != filled)
436                 return (ENXIO);
437         if (pccard_mem == 0)
438                 return (ENOMEM);
439         for (win = slt->ctrl->maxmem - 1; win >= 0; win--)
440                 if ((slt->mem[win].flags & MDF_ACTIVE) == 0)
441                         break;
442         if (win < 0)
443                 return (EBUSY);
444         mp = &slt->mem[win];
445         oldmap = *mp;
446         mp->flags = slt->rwmem | MDF_ACTIVE;
447         while (uio->uio_resid && error == 0) {
448                 mp->card = uio->uio_offset;
449                 mp->size = PCCARD_MEMSIZE;
450                 mp->start = (caddr_t)(void *)(uintptr_t)pccard_mem;
451                 if ((error = slt->ctrl->mapmem(slt, win)) != 0)
452                         break;
453                 offs = (unsigned int)uio->uio_offset & (PCCARD_MEMSIZE - 1);
454                 p = pccard_kmem + offs;
455                 count = MIN(PCCARD_MEMSIZE - offs, uio->uio_resid);
456                 error = uiomove(p, count, uio);
457         }
458         /*
459          *      Restore original map.
460          */
461         *mp = oldmap;
462         slt->ctrl->mapmem(slt, win);
463
464         return (error);
465 }
466
467 /*
468  *      ioctl calls - allows setting/getting of memory and I/O
469  *      descriptors, and assignment of drivers.
470  */
471 static  int
472 crdioctl(dev_t dev, u_long cmd, caddr_t data, int fflag, d_thread_t *td)
473 {
474         u_int32_t       addr;
475         int             err;
476         struct io_desc  *ip;
477         struct mem_desc *mp;
478         device_t        pccarddev;
479         int             pwval;
480         int             s;
481         struct slot     *slt = PCCARD_DEV2SOFTC(dev);
482
483         if (slt == 0 && cmd != PIOCRWMEM)
484                 return (ENXIO);
485         switch(cmd) {
486         default:
487                 if (slt->ctrl->ioctl)
488                         return (slt->ctrl->ioctl(slt, cmd, data));
489                 return (ENOTTY);
490         /*
491          * Get slot state.
492          */
493         case PIOCGSTATE:
494                 s = splhigh();
495                 ((struct slotstate *)data)->state = slt->state;
496                 ((struct slotstate *)data)->laststate = slt->laststate;
497                 slt->laststate = slt->state;
498                 splx(s);
499                 ((struct slotstate *)data)->maxmem = slt->ctrl->maxmem;
500                 ((struct slotstate *)data)->maxio = slt->ctrl->maxio;
501                 ((struct slotstate *)data)->irqs = 0;
502                 break;
503         /*
504          * Get memory context.
505          */
506         case PIOCGMEM:
507                 s = ((struct mem_desc *)data)->window;
508                 if (s < 0 || s >= slt->ctrl->maxmem)
509                         return (EINVAL);
510                 mp = &slt->mem[s];
511                 ((struct mem_desc *)data)->flags = mp->flags;
512                 ((struct mem_desc *)data)->start = mp->start;
513                 ((struct mem_desc *)data)->size = mp->size;
514                 ((struct mem_desc *)data)->card = mp->card;
515                 break;
516         /*
517          * Set memory context. If context already active, then unmap it.
518          * It is hard to see how the parameters can be checked.
519          * At the very least, we only allow root to set the context.
520          */
521         case PIOCSMEM:
522                 if (suser(td))
523                         return (EPERM);
524                 if (slt->state != filled)
525                         return (ENXIO);
526                 s = ((struct mem_desc *)data)->window;
527                 if (s < 0 || s >= slt->ctrl->maxmem)
528                         return (EINVAL);
529                 slt->mem[s] = *((struct mem_desc *)data);
530                 return (slt->ctrl->mapmem(slt, s));
531         /*
532          * Get I/O port context.
533          */
534         case PIOCGIO:
535                 s = ((struct io_desc *)data)->window;
536                 if (s < 0 || s >= slt->ctrl->maxio)
537                         return (EINVAL);
538                 ip = &slt->io[s];
539                 ((struct io_desc *)data)->flags = ip->flags;
540                 ((struct io_desc *)data)->start = ip->start;
541                 ((struct io_desc *)data)->size = ip->size;
542                 break;
543         /*
544          * Set I/O port context.
545          */
546         case PIOCSIO:
547                 if (suser(td))
548                         return (EPERM);
549                 if (slt->state != filled)
550                         return (ENXIO);
551                 s = ((struct io_desc *)data)->window;
552                 if (s < 0 || s >= slt->ctrl->maxio)
553                         return (EINVAL);
554                 slt->io[s] = *((struct io_desc *)data);
555                 /* XXX Don't actually map */
556                 return (0);
557                 break;
558         /*
559          * Set memory window flags for read/write interface.
560          */
561         case PIOCRWFLAG:
562                 slt->rwmem = *(int *)data;
563                 break;
564         /*
565          * Set the memory window to be used for the read/write interface.
566          */
567         case PIOCRWMEM:
568                 if (*(unsigned long *)data == 0) {
569                         *(unsigned long *)data = pccard_mem;
570                         break;
571                 }
572                 if (suser(td))
573                         return (EPERM);
574                 /*
575                  * Validate the memory by checking it against the I/O
576                  * memory range. It must also start on an aligned block size.
577                  */
578                 if (*(unsigned long *)data & (PCCARD_MEMSIZE-1))
579                         return (EINVAL);
580                 pccarddev = PCCARD_DEV2SOFTC(dev)->dev;
581                 pccard_mem_rid = 0;
582                 addr = *(unsigned long *)data;
583                 if (pccard_mem_res)
584                         bus_release_resource(pccarddev, SYS_RES_MEMORY,
585                             pccard_mem_rid, pccard_mem_res);
586                 pccard_mem_res = bus_alloc_resource(pccarddev, SYS_RES_MEMORY,
587                     &pccard_mem_rid, addr, addr, PCCARD_MEMSIZE,
588                     RF_ACTIVE | rman_make_alignment_flags(PCCARD_MEMSIZE));
589                 if (pccard_mem_res == NULL)
590                         return (EINVAL);
591                 pccard_mem = rman_get_start(pccard_mem_res);
592                 pccard_kmem = rman_get_virtual(pccard_mem_res);
593                 break;
594         /*
595          * Set power values.
596          */
597         case PIOCSPOW:
598                 slt->pwr = *(struct power *)data;
599                 return (slt->ctrl->power(slt));
600         /*
601          * Allocate a driver to this slot.
602          */
603         case PIOCSDRV:
604                 if (suser(td))
605                         return (EPERM);
606                 err = allocate_driver(slt, (struct dev_desc *)data);
607                 if (!err)
608                         pccard_success_beep();
609                 else
610                         pccard_failure_beep();
611                 return (err);
612         /*
613          * Virtual removal/insertion
614          */
615         case PIOCSVIR:
616                 pwval = *(int *)data;
617                 if (!pwval) {
618                         if (slt->state != filled)
619                                 return (EINVAL);
620                         pccard_event(slt, card_deactivated);
621                 } else {
622                         if (slt->state != empty && slt->state != inactive)
623                                 return (EINVAL);
624                         pccard_event(slt, card_inserted);
625                 }
626                 break;
627         case PIOCSBEEP:
628                 if (pccard_beep_select(*(int *)data)) {
629                         return (EINVAL);
630                 }
631                 break;
632         }
633         return (0);
634 }
635
636 /*
637  *      poll - Poll on exceptions will return true
638  *      when a change in card status occurs.
639  */
640 static  int
641 crdpoll(dev_t dev, int events, d_thread_t *td)
642 {
643         int     revents = 0;
644         int     s;
645         struct slot *slt = PCCARD_DEV2SOFTC(dev);
646
647         if (events & (POLLIN | POLLRDNORM))
648                 revents |= events & (POLLIN | POLLRDNORM);
649
650         if (events & (POLLOUT | POLLWRNORM))
651                 revents |= events & (POLLIN | POLLRDNORM);
652
653         s = splhigh();
654         /*
655          *      select for exception - card event.
656          */
657         if (events & POLLRDBAND)
658                 if (slt == 0 || slt->laststate != slt->state)
659                         revents |= POLLRDBAND;
660
661         if (revents == 0)
662                 selrecord(td, &slt->selp);
663
664         splx(s);
665         return (revents);
666 }
667
668 /*
669  *      APM hooks for suspending and resuming.
670  */
671 int
672 pccard_suspend(device_t dev)
673 {
674         struct slot *slt = PCCARD_DEVICE2SOFTC(dev);
675
676         /* This code stolen from pccard_event:card_removed */
677         if (slt->state == filled) {
678                 int s = splhigh();              /* nop on current */
679                 disable_slot(slt);
680                 slt->laststate = suspend;       /* for pccardd */
681                 slt->state = empty;
682                 splx(s);
683                 printf("pccard: card disabled, slot %d\n", slt->slotnum);
684         }
685         /*
686          * Disable any pending timeouts for this slot since we're
687          * powering it down/disabling now.
688          */
689         untimeout(power_off_slot, (caddr_t)slt, slt->poff_ch);
690         slt->ctrl->disable(slt);
691         return (0);
692 }
693
694 int
695 pccard_resume(device_t dev)
696 {
697         struct slot *slt = PCCARD_DEVICE2SOFTC(dev);
698
699         slt->ctrl->resume(slt);
700         return (0);
701 }