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