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