ppbconf.c revision 182014
16059Samurai/*-
26059Samurai * Copyright (c) 1997, 1998, 1999 Nicolas Souchu
36059Samurai * All rights reserved.
46059Samurai *
56059Samurai * Redistribution and use in source and binary forms, with or without
66059Samurai * modification, are permitted provided that the following conditions
76059Samurai * are met:
86059Samurai * 1. Redistributions of source code must retain the above copyright
96059Samurai *    notice, this list of conditions and the following disclaimer.
106059Samurai * 2. Redistributions in binary form must reproduce the above copyright
116059Samurai *    notice, this list of conditions and the following disclaimer in the
126059Samurai *    documentation and/or other materials provided with the distribution.
136059Samurai *
146059Samurai * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
156059Samurai * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
166059Samurai * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
176059Samurai * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
186059Samurai * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
198857Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2031962Sbrian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
218857Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
226059Samurai * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2330715Sbrian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2430715Sbrian * SUCH DAMAGE.
2526031Sbrian *
2630715Sbrian *
2726031Sbrian */
2830715Sbrian
2926031Sbrian#include <sys/cdefs.h>
3030715Sbrian__FBSDID("$FreeBSD: head/sys/dev/ppbus/ppbconf.c 182014 2008-08-22 18:42:18Z jhb $");
3130715Sbrian#include "opt_ppb_1284.h"
3231343Sbrian
3326031Sbrian#include <sys/param.h>
3431343Sbrian#include <sys/systm.h>
3530715Sbrian#include <sys/kernel.h>
3630715Sbrian#include <sys/module.h>
3726516Sbrian#include <sys/bus.h>
3830715Sbrian#include <sys/malloc.h>
3930715Sbrian#include <sys/rman.h>
4030715Sbrian
4130715Sbrian#include <machine/resource.h>
4230715Sbrian
4330715Sbrian#include <dev/ppbus/ppbconf.h>
4430715Sbrian#include <dev/ppbus/ppb_1284.h>
4530715Sbrian
4630715Sbrian#include "ppbus_if.h"
4730715Sbrian
4831343Sbrian#define DEVTOSOFTC(dev) ((struct ppb_data *)device_get_softc(dev))
4930715Sbrian
5030715Sbrianstatic MALLOC_DEFINE(M_PPBUSDEV, "ppbusdev", "Parallel Port bus device");
5130715Sbrian
5230715Sbrian
536059Samurai/*
546059Samurai * Device methods
556059Samurai */
5631690Sbrian
576059Samuraistatic void
586059Samuraippbus_print_child(device_t bus, device_t dev)
5913389Sphk{
6031343Sbrian	struct ppb_device *ppbdev;
6126031Sbrian
6231343Sbrian	bus_print_child_header(bus, dev);
636059Samurai
6426142Sbrian	ppbdev = (struct ppb_device *)device_get_ivars(dev);
656059Samurai
6625630Sbrian	if (ppbdev->flags != 0)
6725630Sbrian		printf(" flags 0x%x", ppbdev->flags);
686059Samurai
6926940Sbrian	printf(" on %s%d\n", device_get_name(bus), device_get_unit(bus));
7030715Sbrian
7130715Sbrian	return;
7230715Sbrian}
7330733Sbrian
7430715Sbrianstatic int
7531080Sbrianppbus_probe(device_t dev)
766059Samurai{
776059Samurai	device_set_desc(dev, "Parallel port bus");
7831822Sbrian
796059Samurai	return (0);
8031343Sbrian}
8131343Sbrian
8231343Sbrian/*
8331343Sbrian * ppbus_add_child()
8431343Sbrian *
8531343Sbrian * Add a ppbus device, allocate/initialize the ivars
8631343Sbrian */
8731343Sbrianstatic device_t
8831343Sbrianppbus_add_child(device_t dev, int order, const char *name, int unit)
8931343Sbrian{
9031343Sbrian	struct ppb_device *ppbdev;
9131343Sbrian	device_t child;
9231343Sbrian
9331343Sbrian	/* allocate ivars for the new ppbus child */
9431343Sbrian	ppbdev = malloc(sizeof(struct ppb_device), M_PPBUSDEV,
9531343Sbrian		M_NOWAIT | M_ZERO);
9631343Sbrian	if (!ppbdev)
976059Samurai		return NULL;
986059Samurai
9931343Sbrian	/* initialize the ivars */
1006059Samurai	ppbdev->name = name;
10128679Sbrian
10231372Sbrian	/* add the device as a child to the ppbus bus with the allocated
1036059Samurai	 * ivars */
10426516Sbrian	child = device_add_child_ordered(dev, order, name, unit);
10526516Sbrian	device_set_ivars(child, ppbdev);
10626516Sbrian
10731343Sbrian	return child;
10831343Sbrian}
10931372Sbrian
11031372Sbrianstatic int
11128679Sbrianppbus_read_ivar(device_t bus, device_t dev, int index, uintptr_t* val)
11228679Sbrian{
1136059Samurai	struct ppb_device *ppbdev = (struct ppb_device *)device_get_ivars(dev);
11426516Sbrian
1156059Samurai	switch (index) {
11631372Sbrian	case PPBUS_IVAR_MODE:
11731372Sbrian		/* XXX yet device mode = ppbus mode = chipset mode */
11831372Sbrian		*val = (u_long)ppb_get_mode(bus);
11931372Sbrian		ppbdev->mode = (u_short)*val;
12031372Sbrian		break;
12131372Sbrian	case PPBUS_IVAR_AVM:
12231372Sbrian		*val = (u_long)ppbdev->avm;
12331372Sbrian		break;
12431372Sbrian	case PPBUS_IVAR_IRQ:
12531372Sbrian		BUS_READ_IVAR(device_get_parent(bus), bus, PPC_IVAR_IRQ, val);
1266059Samurai		break;
12731343Sbrian	default:
1286764Samurai		return (ENOENT);
12931372Sbrian	}
13031372Sbrian
13131372Sbrian	return (0);
13231372Sbrian}
1336059Samurai
13431372Sbrianstatic int
13526516Sbrianppbus_write_ivar(device_t bus, device_t dev, int index, u_long val)
13626516Sbrian{
13726516Sbrian	struct ppb_device *ppbdev = (struct ppb_device *)device_get_ivars(dev);
1386059Samurai
1396059Samurai	switch (index) {
1406059Samurai	case PPBUS_IVAR_MODE:
14130913Sbrian		/* XXX yet device mode = ppbus mode = chipset mode */
1426059Samurai		ppb_set_mode(bus,val);
14331343Sbrian		ppbdev->mode = ppb_get_mode(bus);
1446059Samurai		break;
14520120Snate	default:
14620120Snate		return (ENOENT);
14725908Sbrian  	}
14825908Sbrian
14920120Snate	return (0);
15010528Samurai}
1516059Samurai
15210528Samurai#define PPB_PNP_PRINTER		0
1536059Samurai#define PPB_PNP_MODEM		1
15410528Samurai#define PPB_PNP_NET		2
1556059Samurai#define PPB_PNP_HDC		3
15630913Sbrian#define PPB_PNP_PCMCIA		4
15726516Sbrian#define PPB_PNP_MEDIA		5
15826516Sbrian#define PPB_PNP_FDC		6
1596059Samurai#define PPB_PNP_PORTS		7
16026516Sbrian#define PPB_PNP_SCANNER		8
1616059Samurai#define PPB_PNP_DIGICAM		9
1626059Samurai
1636059Samurai#ifndef DONTPROBE_1284
16431343Sbrian
1656059Samuraistatic char *pnp_tokens[] = {
16611336Samurai	"PRINTER", "MODEM", "NET", "HDC", "PCMCIA", "MEDIA",
16726858Sbrian	"FDC", "PORTS", "SCANNER", "DIGICAM", "", NULL };
16811336Samurai
1696059Samurai#if 0
17026516Sbrianstatic char *pnp_classes[] = {
17126516Sbrian	"printer", "modem", "network device",
17226516Sbrian	"hard disk", "PCMCIA", "multimedia device",
1736059Samurai	"floppy disk", "ports", "scanner",
17426516Sbrian	"digital camera", "unknown device", NULL };
17531343Sbrian#endif
17631140Sbrian
17731121Sbrian/*
17811336Samurai * search_token()
17911336Samurai *
18026516Sbrian * Search the first occurence of a token within a string
18126516Sbrian */
18231034Sbrianstatic char *
18326516Sbriansearch_token(char *str, int slen, char *token)
18428679Sbrian{
18511336Samurai	int tlen, i;
18611336Samurai
18726858Sbrian#define UNKNOWN_LENGTH	-1
18830697Sbrian
18931343Sbrian	if (slen == UNKNOWN_LENGTH)
19011336Samurai		/* get string's length */
19111336Samurai		slen = strlen(str);
19226858Sbrian
19326858Sbrian	/* get token's length */
19411336Samurai	tlen = strlen(token);
19526516Sbrian	if (tlen == 0)
19626516Sbrian		return (str);
1976059Samurai
1986059Samurai	for (i = 0; i <= slen-tlen; i++) {
19910528Samurai		if (strncmp(str + i, token, tlen) == 0)
20031343Sbrian			return (&str[i]);
20128536Sbrian	}
20231343Sbrian
20331343Sbrian	return (NULL);
20428536Sbrian}
20531141Sbrian
20631141Sbrian/*
20731343Sbrian * ppb_pnp_detect()
20828536Sbrian *
20931141Sbrian * Returns the class id. of the peripherial, -1 otherwise
21031141Sbrian */
21128536Sbrianstatic int
21228536Sbrianppb_pnp_detect(device_t bus)
21328536Sbrian{
21428536Sbrian	char *token, *class = 0;
21531343Sbrian	int i, len, error;
21610528Samurai	int class_id = -1;
21710528Samurai	char str[PPB_PnP_STRING_SIZE+1];
21810528Samurai	int unit = device_get_unit(bus);
21926516Sbrian
22031343Sbrian	printf("Probing for PnP devices on ppbus%d:\n", unit);
22131343Sbrian
22220813Sjkh	if ((error = ppb_1284_read_id(bus, PPB_NIBBLE, str,
22318856Ssos					PPB_PnP_STRING_SIZE, &len)))
22426911Sbrian		goto end_detect;
22526516Sbrian
22626516Sbrian#ifdef DEBUG_1284
22726516Sbrian	printf("ppb: <PnP> %d characters: ", len);
22810528Samurai	for (i = 0; i < len; i++)
22926911Sbrian		printf("%c(0x%x) ", str[i], str[i]);
23026911Sbrian	printf("\n");
23128679Sbrian#endif
23226911Sbrian
23328679Sbrian	/* replace ';' characters by '\0' */
23428679Sbrian	for (i = 0; i < len; i++)
23526911Sbrian		str[i] = (str[i] == ';') ? '\0' : str[i];
23631121Sbrian
23728679Sbrian	if ((token = search_token(str, len, "MFG")) != NULL ||
23826516Sbrian		(token = search_token(str, len, "MANUFACTURER")) != NULL)
23926516Sbrian		printf("ppbus%d: <%s", unit,
24026516Sbrian			search_token(token, UNKNOWN_LENGTH, ":") + 1);
24126516Sbrian	else
24231343Sbrian		printf("ppbus%d: <unknown", unit);
24328381Sbrian
24431121Sbrian	if ((token = search_token(str, len, "MDL")) != NULL ||
24531121Sbrian		(token = search_token(str, len, "MODEL")) != NULL)
24631121Sbrian		printf(" %s",
24731121Sbrian			search_token(token, UNKNOWN_LENGTH, ":") + 1);
24831121Sbrian	else
24931121Sbrian		printf(" unknown");
25028381Sbrian
25128381Sbrian	if ((token = search_token(str, len, "VER")) != NULL)
25228381Sbrian		printf("/%s",
25328679Sbrian			search_token(token, UNKNOWN_LENGTH, ":") + 1);
25428381Sbrian
25528381Sbrian	if ((token = search_token(str, len, "REV")) != NULL)
25628679Sbrian		printf(".%s",
25726516Sbrian			search_token(token, UNKNOWN_LENGTH, ":") + 1);
25826516Sbrian
25928679Sbrian	printf(">");
26028679Sbrian
26118531Sbde	if ((token = search_token(str, len, "CLS")) != NULL) {
26228679Sbrian		class = search_token(token, UNKNOWN_LENGTH, ":") + 1;
26328679Sbrian		printf(" %s", class);
26428679Sbrian	}
26528679Sbrian
26628679Sbrian	if ((token = search_token(str, len, "CMD")) != NULL ||
26728679Sbrian		(token = search_token(str, len, "COMMAND")) != NULL)
26828679Sbrian		printf(" %s",
26928679Sbrian			search_token(token, UNKNOWN_LENGTH, ":") + 1);
27026516Sbrian
27128679Sbrian	printf("\n");
27228679Sbrian
27328679Sbrian	if (class)
27428679Sbrian		/* identify class ident */
27528679Sbrian		for (i = 0; pnp_tokens[i] != NULL; i++) {
27628679Sbrian			if (search_token(class, len, pnp_tokens[i]) != NULL) {
27728679Sbrian				class_id = i;
27828679Sbrian				goto end_detect;
27926516Sbrian			}
28028679Sbrian		}
28131343Sbrian
28226516Sbrian	class_id = PPB_PnP_UNKNOWN;
28328679Sbrian
28431061Sbrianend_detect:
28531343Sbrian	return (class_id);
28628679Sbrian}
28731343Sbrian
28831343Sbrian/*
28931343Sbrian * ppb_scan_bus()
29031343Sbrian *
29131343Sbrian * Scan the ppbus for IEEE1284 compliant devices
29231343Sbrian */
29331343Sbrianstatic int
29431343Sbrianppb_scan_bus(device_t bus)
29531343Sbrian{
29631343Sbrian	struct ppb_data * ppb = (struct ppb_data *)device_get_softc(bus);
29731343Sbrian	int error = 0;
29831343Sbrian	int unit = device_get_unit(bus);
29928679Sbrian
30028679Sbrian	/* try all IEEE1284 modes, for one device only
30110528Samurai	 *
30228679Sbrian	 * XXX We should implement the IEEE1284.3 standard to detect
30328679Sbrian	 * daisy chained devices
30428974Sbrian	 */
30528679Sbrian
30628679Sbrian	error = ppb_1284_negociate(bus, PPB_NIBBLE, PPB_REQUEST_ID);
30731125Sbrian
30831343Sbrian	if ((ppb->state == PPB_ERROR) && (ppb->error == PPB_NOT_IEEE1284))
30931343Sbrian		goto end_scan;
31030316Sbrian
31130316Sbrian	ppb_1284_terminate(bus);
31230316Sbrian
31331343Sbrian	printf("ppbus%d: IEEE1284 device found ", unit);
31430316Sbrian
31520813Sjkh	if (!(error = ppb_1284_negociate(bus, PPB_NIBBLE, 0))) {
31631343Sbrian		printf("/NIBBLE");
31728679Sbrian		ppb_1284_terminate(bus);
31810528Samurai	}
31928679Sbrian
32026516Sbrian	if (!(error = ppb_1284_negociate(bus, PPB_PS2, 0))) {
32110528Samurai		printf("/PS2");
32210528Samurai		ppb_1284_terminate(bus);
32328679Sbrian	}
32431343Sbrian
32510528Samurai	if (!(error = ppb_1284_negociate(bus, PPB_ECP, 0))) {
32620813Sjkh		printf("/ECP");
32710528Samurai		ppb_1284_terminate(bus);
32820813Sjkh	}
32928679Sbrian
33010528Samurai	if (!(error = ppb_1284_negociate(bus, PPB_ECP, PPB_USE_RLE))) {
33110528Samurai		printf("/ECP_RLE");
33231343Sbrian		ppb_1284_terminate(bus);
33331343Sbrian	}
33431343Sbrian
33531343Sbrian	if (!(error = ppb_1284_negociate(bus, PPB_EPP, 0))) {
33631343Sbrian		printf("/EPP");
33731343Sbrian		ppb_1284_terminate(bus);
33831343Sbrian	}
33931343Sbrian
34031343Sbrian	/* try more IEEE1284 modes */
34131343Sbrian	if (bootverbose) {
34231343Sbrian		if (!(error = ppb_1284_negociate(bus, PPB_NIBBLE,
34331343Sbrian				PPB_REQUEST_ID))) {
34431343Sbrian			printf("/NIBBLE_ID");
34531343Sbrian			ppb_1284_terminate(bus);
34630715Sbrian		}
34728679Sbrian
34828679Sbrian		if (!(error = ppb_1284_negociate(bus, PPB_PS2,
34928679Sbrian				PPB_REQUEST_ID))) {
35028679Sbrian			printf("/PS2_ID");
35131121Sbrian			ppb_1284_terminate(bus);
35231121Sbrian		}
35328679Sbrian
35431372Sbrian		if (!(error = ppb_1284_negociate(bus, PPB_ECP,
35528679Sbrian				PPB_REQUEST_ID))) {
35628679Sbrian			printf("/ECP_ID");
35728679Sbrian			ppb_1284_terminate(bus);
35831631Sbrian		}
35928679Sbrian
36028679Sbrian		if (!(error = ppb_1284_negociate(bus, PPB_ECP,
36128679Sbrian				PPB_REQUEST_ID | PPB_USE_RLE))) {
36228679Sbrian			printf("/ECP_RLE_ID");
36328679Sbrian			ppb_1284_terminate(bus);
36428679Sbrian		}
36528679Sbrian
36628679Sbrian		if (!(error = ppb_1284_negociate(bus, PPB_COMPATIBLE,
36728679Sbrian				PPB_EXTENSIBILITY_LINK))) {
36828679Sbrian			printf("/Extensibility Link");
36928679Sbrian			ppb_1284_terminate(bus);
37029083Sbrian		}
37128679Sbrian	}
37228679Sbrian
37328679Sbrian	printf("\n");
37428679Sbrian
37528679Sbrian	/* detect PnP devices */
37628679Sbrian	ppb->class_id = ppb_pnp_detect(bus);
37728679Sbrian
37828679Sbrian	return (0);
37928679Sbrian
38031372Sbrianend_scan:
38128679Sbrian	return (error);
38231372Sbrian}
38331343Sbrian
38428679Sbrian#endif /* !DONTPROBE_1284 */
38528679Sbrian
38631343Sbrianstatic void
38728679Sbrianppbus_dummy_intr(void *arg)
38828679Sbrian{
38928679Sbrian}
39031343Sbrian
39128679Sbrianstatic int
39228679Sbrianppbus_attach(device_t dev)
39328679Sbrian{
3946059Samurai	struct ppb_data *ppb = (struct ppb_data *)device_get_softc(dev);
3956059Samurai	uintptr_t irq;
39628536Sbrian	int error, rid;
39731343Sbrian
39828536Sbrian	/* Attach a dummy interrupt handler to suck up any stray interrupts. */
39928536Sbrian	BUS_READ_IVAR(device_get_parent(dev), dev, PPC_IVAR_IRQ, &irq);
40028536Sbrian
40128536Sbrian	if (irq > 0) {
40228536Sbrian		rid = 0;
40328536Sbrian		ppb->irq_res = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, irq,
40428536Sbrian		    irq, 1, RF_SHAREABLE);
40528679Sbrian		if (ppb->irq_res != NULL) {
40631343Sbrian			error = bus_setup_intr(dev, ppb->irq_res,
4076059Samurai			    INTR_TYPE_TTY | INTR_MPSAFE, NULL, ppbus_dummy_intr,
4086059Samurai			    ppb, &ppb->intr_cookie);
4096059Samurai			if (error) {
41026516Sbrian				device_printf(dev,
41126516Sbrian				    "failed to setup interrupt handler\n");
41230913Sbrian				bus_release_resource(dev, SYS_RES_IRQ, 0,
41330913Sbrian				    ppb->irq_res);
41430913Sbrian				return (error);
41530913Sbrian			}
41626516Sbrian		}
41730913Sbrian	}
41830913Sbrian
41930913Sbrian	/* Locate our children */
42030913Sbrian	bus_generic_probe(dev);
42130913Sbrian
42230913Sbrian#ifndef DONTPROBE_1284
42326516Sbrian	/* detect IEEE1284 compliant devices */
42426516Sbrian	ppb_scan_bus(dev);
42526516Sbrian#endif /* !DONTPROBE_1284 */
4266059Samurai
4276059Samurai	/* launch attachement of the added children */
42828679Sbrian	bus_generic_attach(dev);
42931343Sbrian
4306059Samurai	return 0;
4316059Samurai}
4326059Samurai
43326516Sbrianstatic int
43426516Sbrianppbus_detach(device_t dev)
4356059Samurai{
43626516Sbrian	struct ppb_data *ppb = (struct ppb_data *)device_get_softc(dev);
43726516Sbrian        device_t *children;
43828679Sbrian        int nchildren, i;
43928679Sbrian
44028679Sbrian	/* detach & delete all children */
44126516Sbrian	if (!device_get_children(dev, &children, &nchildren)) {
4426059Samurai		for (i = 0; i < nchildren; i++)
44331077Sbrian			if (children[i])
4446059Samurai				device_delete_child(dev, children[i]);
4456059Samurai		free(children, M_TEMP);
44628679Sbrian        }
44731343Sbrian
4486059Samurai	if (ppb->irq_res != NULL) {
44931077Sbrian		bus_teardown_intr(dev, ppb->irq_res, ppb->intr_cookie);
45031077Sbrian		bus_release_resource(dev, SYS_RES_IRQ, 0, ppb->irq_res);
45131077Sbrian	}
45231077Sbrian	return (0);
45331077Sbrian}
4546059Samurai
4556059Samuraistatic int
45628679Sbrianppbus_setup_intr(device_t bus, device_t child, struct resource *r, int flags,
45731343Sbrian    driver_filter_t *filt, void (*ihand)(void *), void *arg, void **cookiep)
45828327Sbrian{
45928327Sbrian	int error;
46028327Sbrian	struct ppb_data *ppb = DEVTOSOFTC(bus);
46128461Sbrian	struct ppb_device *ppbdev = device_get_ivars(child);
46228461Sbrian
46328461Sbrian	/* a device driver must own the bus to register an interrupt */
46428461Sbrian	if (ppb->ppb_owner != child)
46528327Sbrian		return (EINVAL);
46628461Sbrian
46728461Sbrian	if ((error = BUS_SETUP_INTR(device_get_parent(bus), child, r, flags,
46828461Sbrian					filt, ihand, arg, cookiep)))
46928461Sbrian		return (error);
47028461Sbrian
47128461Sbrian	/* store the resource and the cookie for eventually forcing
47228461Sbrian	 * handler unregistration
47328461Sbrian	 */
47428461Sbrian	ppbdev->intr_cookie = *cookiep;
47528461Sbrian	ppbdev->intr_resource = r;
47628461Sbrian
47728461Sbrian	return (0);
47828461Sbrian}
47928461Sbrian
48028461Sbrianstatic int
48128461Sbrianppbus_teardown_intr(device_t bus, device_t child, struct resource *r, void *ih)
48231077Sbrian{
48328327Sbrian	struct ppb_data *ppb = DEVTOSOFTC(bus);
48428327Sbrian	struct ppb_device *ppbdev = (struct ppb_device *)device_get_ivars(child);
48528679Sbrian
48631343Sbrian	/* a device driver must own the bus to unregister an interrupt */
4876059Samurai	if ((ppb->ppb_owner != child) || (ppbdev->intr_cookie != ih) ||
48826516Sbrian			(ppbdev->intr_resource != r))
48926516Sbrian		return (EINVAL);
49026516Sbrian
49131822Sbrian	ppbdev->intr_cookie = 0;
49229840Sbrian	ppbdev->intr_resource = 0;
49329840Sbrian
49429840Sbrian	/* pass unregistration to the upper layer */
49531077Sbrian	return (BUS_TEARDOWN_INTR(device_get_parent(bus), child, r, ih));
4966059Samurai}
4976059Samurai
49828679Sbrian/*
49931343Sbrian * ppb_request_bus()
5006059Samurai *
50131077Sbrian * Allocate the device to perform transfers.
50231077Sbrian *
50331077Sbrian * how	: PPB_WAIT or PPB_DONTWAIT
5046059Samurai */
5056059Samuraiint
50628679Sbrianppb_request_bus(device_t bus, device_t dev, int how)
50731343Sbrian{
50826326Sbrian	int s, error = 0;
50931077Sbrian	struct ppb_data *ppb = DEVTOSOFTC(bus);
51031077Sbrian	struct ppb_device *ppbdev = (struct ppb_device *)device_get_ivars(dev);
51131077Sbrian
51226326Sbrian	while (!error) {
51326326Sbrian		s = splhigh();
51428679Sbrian		if (ppb->ppb_owner) {
51531343Sbrian			splx(s);
51626326Sbrian
51731077Sbrian			switch (how) {
51831077Sbrian			case (PPB_WAIT | PPB_INTR):
51931077Sbrian				error = tsleep(ppb, PPBPRI|PCATCH, "ppbreq", 0);
52031077Sbrian				break;
52131077Sbrian
52231077Sbrian			case (PPB_WAIT | PPB_NOINTR):
52326326Sbrian				error = tsleep(ppb, PPBPRI, "ppbreq", 0);
52426326Sbrian				break;
52528679Sbrian
52631343Sbrian			default:
52725067Sbrian				return (EWOULDBLOCK);
52831077Sbrian				break;
52931077Sbrian			}
53031077Sbrian
53131077Sbrian		} else {
53225067Sbrian			ppb->ppb_owner = dev;
53325067Sbrian
53428679Sbrian			/* restore the context of the device
53531343Sbrian			 * The first time, ctx.valid is certainly false
53611336Samurai			 * then do not change anything. This is usefull for
53726516Sbrian			 * drivers that do not set there operating mode
53826516Sbrian			 * during attachement
53926516Sbrian			 */
54011336Samurai			if (ppbdev->ctx.valid)
54111336Samurai				ppb_set_mode(bus, ppbdev->ctx.mode);
54226516Sbrian
54328679Sbrian			splx(s);
54426516Sbrian			return (0);
54511336Samurai		}
54611336Samurai	}
54726516Sbrian
54824939Sbrian	return (error);
54924939Sbrian}
55026516Sbrian
55128679Sbrian/*
55226516Sbrian * ppb_release_bus()
55324939Sbrian *
55424939Sbrian * Release the device allocated with ppb_request_bus()
55511336Samurai */
55628679Sbrianint
55711336Samuraippb_release_bus(device_t bus, device_t dev)
55826516Sbrian{
55911336Samurai	int s, error;
56031077Sbrian	struct ppb_data *ppb = DEVTOSOFTC(bus);
56111336Samurai	struct ppb_device *ppbdev = (struct ppb_device *)device_get_ivars(dev);
56211336Samurai
56326516Sbrian	if (ppbdev->intr_resource != 0)
56428679Sbrian		/* force interrupt handler unregistration when the ppbus is released */
56531343Sbrian		if ((error = BUS_TEARDOWN_INTR(bus, dev, ppbdev->intr_resource,
56618752Sjkh					       ppbdev->intr_cookie)))
56731077Sbrian			return (error);
56831077Sbrian
56931077Sbrian	s = splhigh();
57031077Sbrian	if (ppb->ppb_owner != dev) {
57131077Sbrian		splx(s);
57231077Sbrian		return (EACCES);
57331077Sbrian	}
57431077Sbrian
57518752Sjkh	ppb->ppb_owner = 0;
57628679Sbrian	splx(s);
57726516Sbrian
57818752Sjkh	/* save the context of the device */
57930715Sbrian	ppbdev->ctx.mode = ppb_get_mode(bus);
58028679Sbrian
58131372Sbrian	/* ok, now the context of the device is valid */
58228679Sbrian	ppbdev->ctx.valid = 1;
58331372Sbrian
58428679Sbrian	/* wakeup waiting processes */
58528679Sbrian	wakeup(ppb);
58628679Sbrian
58731372Sbrian	return (0);
58828679Sbrian}
58928679Sbrian
59028679Sbrianstatic devclass_t ppbus_devclass;
59128679Sbrian
59228679Sbrianstatic device_method_t ppbus_methods[] = {
59331372Sbrian        /* device interface */
59428679Sbrian	DEVMETHOD(device_probe,         ppbus_probe),
59528679Sbrian	DEVMETHOD(device_attach,        ppbus_attach),
59628679Sbrian	DEVMETHOD(device_detach,        ppbus_detach),
59728679Sbrian
59828679Sbrian        /* bus interface */
59928679Sbrian	DEVMETHOD(bus_add_child,	ppbus_add_child),
60028679Sbrian	DEVMETHOD(bus_print_child,	ppbus_print_child),
60131372Sbrian	DEVMETHOD(bus_read_ivar,        ppbus_read_ivar),
60228679Sbrian	DEVMETHOD(bus_write_ivar,       ppbus_write_ivar),
60331372Sbrian	DEVMETHOD(bus_setup_intr,	ppbus_setup_intr),
60428679Sbrian	DEVMETHOD(bus_teardown_intr,	ppbus_teardown_intr),
60528679Sbrian	DEVMETHOD(bus_alloc_resource,   bus_generic_alloc_resource),
60628679Sbrian
60728679Sbrian        { 0, 0 }
60828679Sbrian};
60928679Sbrian
61028679Sbrianstatic driver_t ppbus_driver = {
61128679Sbrian        "ppbus",
61228679Sbrian        ppbus_methods,
61328679Sbrian        sizeof(struct ppb_data),
61428679Sbrian};
61528679SbrianDRIVER_MODULE(ppbus, ppc, ppbus_driver, ppbus_devclass, 0, 0);
61628679Sbrian