ppbconf.c revision 39134
128219Smsmith/*-
238061Smsmith * Copyright (c) 1997, 1998 Nicolas Souchu
328219Smsmith * All rights reserved.
428219Smsmith *
528219Smsmith * Redistribution and use in source and binary forms, with or without
628219Smsmith * modification, are permitted provided that the following conditions
728219Smsmith * are met:
828219Smsmith * 1. Redistributions of source code must retain the above copyright
928219Smsmith *    notice, this list of conditions and the following disclaimer.
1028219Smsmith * 2. Redistributions in binary form must reproduce the above copyright
1128219Smsmith *    notice, this list of conditions and the following disclaimer in the
1228219Smsmith *    documentation and/or other materials provided with the distribution.
1328219Smsmith *
1428219Smsmith * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1528219Smsmith * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1628219Smsmith * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1728219Smsmith * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1828219Smsmith * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1928219Smsmith * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2028219Smsmith * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2128219Smsmith * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2228219Smsmith * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2328219Smsmith * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2428219Smsmith * SUCH DAMAGE.
2528219Smsmith *
2639134Snsouch *	$Id: ppbconf.c,v 1.6 1998/08/03 19:14:31 msmith Exp $
2728219Smsmith *
2828219Smsmith */
2928219Smsmith#include <sys/param.h>
3028219Smsmith#include <sys/systm.h>
3128219Smsmith#include <sys/kernel.h>
3228219Smsmith#include <sys/malloc.h>
3328219Smsmith
3428219Smsmith#include <vm/vm.h>
3528219Smsmith#include <vm/pmap.h>
3628219Smsmith
3728219Smsmith#include <dev/ppbus/ppbconf.h>
3828257Smsmith#include <dev/ppbus/ppb_1284.h>
3928219Smsmith
4038061SmsmithLIST_HEAD(, ppb_data)	ppbdata;	/* list of existing ppbus */
4128219Smsmith
4228219Smsmith/*
4328219Smsmith * Add a null driver so that the linker set always exists.
4428219Smsmith */
4528219Smsmith
4628219Smsmithstatic struct ppb_driver nulldriver = {
4728219Smsmith    NULL, NULL, "null"
4828219Smsmith};
4928219SmsmithDATA_SET(ppbdriver_set, nulldriver);
5028219Smsmith
5128219Smsmith
5228219Smsmith/*
5328219Smsmith * ppb_alloc_bus()
5428219Smsmith *
5528219Smsmith * Allocate area to store the ppbus description.
5628219Smsmith */
5728219Smsmithstruct ppb_data *
5828219Smsmithppb_alloc_bus(void)
5928219Smsmith{
6028219Smsmith	struct ppb_data *ppb;
6128219Smsmith	static int ppbdata_initted = 0;		/* done-init flag */
6228219Smsmith
6328219Smsmith	ppb = (struct ppb_data *) malloc(sizeof(struct ppb_data),
6428219Smsmith		M_TEMP, M_NOWAIT);
6528219Smsmith
6628219Smsmith	/*
6728219Smsmith	 * Add the new parallel port bus to the list of existing ppbus.
6828219Smsmith	 */
6928219Smsmith	if (ppb) {
7028219Smsmith		bzero(ppb, sizeof(struct ppb_data));
7128219Smsmith
7228219Smsmith		if (!ppbdata_initted) {		/* list not initialised */
7328219Smsmith		    LIST_INIT(&ppbdata);
7428219Smsmith		    ppbdata_initted = 1;
7528219Smsmith		}
7628219Smsmith		LIST_INSERT_HEAD(&ppbdata, ppb, ppb_chain);
7728219Smsmith	} else {
7828219Smsmith		printf("ppb_alloc_bus: cannot malloc!\n");
7928219Smsmith	}
8028219Smsmith	return(ppb);
8128219Smsmith}
8228219Smsmith
8328257Smsmithstatic char *pnp_tokens[] = {
8428257Smsmith	"PRINTER", "MODEM", "NET", "HDC", "PCMCIA", "MEDIA",
8528257Smsmith	"FDC", "PORTS", "SCANNER", "DIGICAM", "", NULL };
8628257Smsmith
8728257Smsmithstatic char *pnp_classes[] = {
8828257Smsmith	"printer", "modem", "network device",
8928257Smsmith	"hard disk", "PCMCIA", "multimedia device",
9028257Smsmith	"floppy disk", "ports", "scanner",
9128257Smsmith	"digital camera", "unknown device", NULL };
9228257Smsmith
9328219Smsmith/*
9428257Smsmith * search_token()
9528257Smsmith *
9628257Smsmith * Search the first occurence of a token within a string
9739134Snsouch *
9839134Snsouch * XXX should use strxxx() calls
9928257Smsmith */
10028257Smsmithstatic char *
10128257Smsmithsearch_token(char *str, int slen, char *token)
10228257Smsmith{
10328257Smsmith	char *p;
10428257Smsmith	int tlen, i, j;
10528257Smsmith
10628257Smsmith#define UNKNOWN_LENGTH	-1
10728257Smsmith
10828257Smsmith	if (slen == UNKNOWN_LENGTH)
10928257Smsmith		/* get string's length */
11028257Smsmith		for (slen = 0, p = str; *p != '\0'; p++)
11128257Smsmith			slen ++;
11228257Smsmith
11328257Smsmith	/* get token's length */
11428257Smsmith	for (tlen = 0, p = token; *p != '\0'; p++)
11528257Smsmith		tlen ++;
11628257Smsmith
11728257Smsmith	if (tlen == 0)
11828257Smsmith		return (str);
11928257Smsmith
12028257Smsmith	for (i = 0; i <= slen-tlen; i++) {
12128257Smsmith		for (j = 0; j < tlen; j++)
12228257Smsmith			if (str[i+j] != token[j])
12328257Smsmith				break;
12428257Smsmith		if (j == tlen)
12528257Smsmith			return (&str[i]);
12628257Smsmith	}
12728257Smsmith
12828257Smsmith	return (NULL);
12928257Smsmith}
13028257Smsmith
13128257Smsmith/*
13228257Smsmith * ppb_pnp_detect()
13328257Smsmith *
13428257Smsmith * Returns the class id. of the peripherial, -1 otherwise
13528257Smsmith */
13628257Smsmithstatic int
13728257Smsmithppb_pnp_detect(struct ppb_data *ppb)
13828257Smsmith{
13928257Smsmith	char *token, *q, *class = 0;
14028257Smsmith	int i, len, error;
14138061Smsmith	int class_id = -1;
14228257Smsmith	char str[PPB_PnP_STRING_SIZE+1];
14328257Smsmith	struct ppb_device pnpdev;	/* temporary device to perform I/O */
14428257Smsmith
14528257Smsmith	/* initialize the pnpdev structure for future use */
14628257Smsmith	bzero(&pnpdev, sizeof(pnpdev));
14728257Smsmith
14828257Smsmith	pnpdev.ppb = ppb;
14928257Smsmith
15038061Smsmith	if (bootverbose)
15138061Smsmith		printf("ppb: <PnP> probing devices on ppbus %d...\n",
15238061Smsmith			ppb->ppb_link->adapter_unit);
15328257Smsmith
15438061Smsmith	if (ppb_request_bus(&pnpdev, PPB_DONTWAIT)) {
15538061Smsmith		if (bootverbose)
15638061Smsmith			printf("ppb: <PnP> cannot allocate ppbus!\n");
15738061Smsmith		return (-1);
15838061Smsmith	}
15938061Smsmith
16039134Snsouch	if ((error = ppb_1284_negociate(&pnpdev, NIBBLE_1284_REQUEST_ID))) {
16139134Snsouch		if (bootverbose)
16239134Snsouch			printf("ppb: <PnP> ppb_1284_negociate()=%d\n", error);
16328257Smsmith
16438061Smsmith		goto end_detect;
16528257Smsmith	}
16639134Snsouch
16728257Smsmith	len = 0;
16839134Snsouch	for (q=str; !(ppb_rstr(&pnpdev) & PERROR); q++) {
16928257Smsmith		if ((error = nibble_1284_inbyte(&pnpdev, q))) {
17039134Snsouch			if (bootverbose) {
17139134Snsouch				*q = '\0';
17239134Snsouch				printf("ppb: <PnP> len=%d, %s\n", len, str);
17338061Smsmith				printf("ppb: <PnP> nibble_1284_inbyte()=%d\n",
17438061Smsmith					error);
17539134Snsouch			}
17638061Smsmith			goto end_detect;
17728257Smsmith		}
17839134Snsouch
17928257Smsmith		if (len++ >= PPB_PnP_STRING_SIZE) {
18028257Smsmith			printf("ppb: <PnP> not space left!\n");
18138061Smsmith			goto end_detect;
18228257Smsmith		}
18328257Smsmith	}
18428257Smsmith	*q = '\0';
18528257Smsmith
18628257Smsmith	nibble_1284_sync(&pnpdev);
18728257Smsmith
18838061Smsmith	if (bootverbose) {
18938061Smsmith		printf("ppb: <PnP> %d characters: ", len);
19038061Smsmith		for (i = 0; i < len; i++)
19138061Smsmith			printf("0x%x ", str[i]);
19238061Smsmith		printf("\n");
19338061Smsmith	}
19428257Smsmith
19528257Smsmith	/* replace ';' characters by '\0' */
19628257Smsmith	for (i = 0; i < len; i++)
19728257Smsmith		str[i] = (str[i] == ';') ? '\0' : str[i];
19828257Smsmith
19928257Smsmith	if ((token = search_token(str, len, "MFG")) != NULL)
20028257Smsmith		printf("ppbus%d: <%s", ppb->ppb_link->adapter_unit,
20128257Smsmith			search_token(token, UNKNOWN_LENGTH, ":") + 1);
20228257Smsmith	else
20328257Smsmith		printf("ppbus%d: <unknown", ppb->ppb_link->adapter_unit);
20428257Smsmith
20528257Smsmith	if ((token = search_token(str, len, "MDL")) != NULL)
20628257Smsmith		printf(" %s",
20728257Smsmith			search_token(token, UNKNOWN_LENGTH, ":") + 1);
20828257Smsmith	else
20928257Smsmith		printf(" unknown");
21028257Smsmith
21128257Smsmith	if ((token = search_token(str, len, "VER")) != NULL)
21228257Smsmith		printf("/%s",
21328257Smsmith			search_token(token, UNKNOWN_LENGTH, ":") + 1);
21428257Smsmith
21528257Smsmith	if ((token = search_token(str, len, "REV")) != NULL)
21628257Smsmith		printf(".%s",
21728257Smsmith			search_token(token, UNKNOWN_LENGTH, ":") + 1);
21828257Smsmith
21928257Smsmith	printf(">");
22028257Smsmith
22128257Smsmith	if ((token = search_token(str, len, "CLS")) != NULL) {
22228257Smsmith		class = search_token(token, UNKNOWN_LENGTH, ":") + 1;
22328257Smsmith		printf(" %s", class);
22428257Smsmith	}
22528257Smsmith
22628257Smsmith	if ((token = search_token(str, len, "CMD")) != NULL)
22728257Smsmith		printf(" %s",
22828257Smsmith			search_token(token, UNKNOWN_LENGTH, ":") + 1);
22928257Smsmith
23028257Smsmith	printf("\n");
23128257Smsmith
23228257Smsmith	if (class)
23328257Smsmith		/* identify class ident */
23428257Smsmith		for (i = 0; pnp_tokens[i] != NULL; i++) {
23528257Smsmith			if (search_token(class, len, pnp_tokens[i]) != NULL) {
23638061Smsmith				class_id = i;
23738061Smsmith				goto end_detect;
23828257Smsmith			}
23928257Smsmith		}
24028257Smsmith
24138061Smsmith	class_id = PPB_PnP_UNKNOWN;
24238061Smsmith
24338061Smsmithend_detect:
24439134Snsouch	if ((error = ppb_1284_terminate(&pnpdev, VALID_STATE)) && bootverbose)
24539134Snsouch		printf("ppb: ppb_1284_terminate()=%d\n", error);
24639134Snsouch
24738061Smsmith	ppb_release_bus(&pnpdev);
24838061Smsmith	return (class_id);
24928257Smsmith}
25028257Smsmith
25128257Smsmith/*
25228219Smsmith * ppb_attachdevs()
25328219Smsmith *
25428219Smsmith * Called by ppcattach(), this function probes the ppbus and
25528219Smsmith * attaches found devices.
25628219Smsmith */
25728219Smsmithint
25828219Smsmithppb_attachdevs(struct ppb_data *ppb)
25928219Smsmith{
26028219Smsmith	int error;
26128219Smsmith	struct ppb_device *dev;
26228219Smsmith	struct ppb_driver **p_drvpp, *p_drvp;
26328257Smsmith
26428219Smsmith	LIST_INIT(&ppb->ppb_devs);	/* initialise device/driver list */
26528219Smsmith	p_drvpp = (struct ppb_driver **)ppbdriver_set.ls_items;
26628257Smsmith
26728257Smsmith	/* detect PnP devices */
26828257Smsmith	ppb->class_id = ppb_pnp_detect(ppb);
26928219Smsmith
27028219Smsmith	/*
27128219Smsmith	 * Blindly try all probes here.  Later we should look at
27228219Smsmith	 * the parallel-port PnP standard, and intelligently seek
27328219Smsmith	 * drivers based on configuration first.
27428219Smsmith	 */
27528219Smsmith	while ((p_drvp = *p_drvpp++) != NULL) {
27628219Smsmith	    if (p_drvp->probe && (dev = (p_drvp->probe(ppb))) != NULL) {
27728219Smsmith		/*
27828219Smsmith		 * Add the device to the list of probed devices.
27928219Smsmith		 */
28028219Smsmith		LIST_INSERT_HEAD(&ppb->ppb_devs, dev, chain);
28128219Smsmith
28228219Smsmith		/* Call the device's attach routine */
28328219Smsmith		(void)p_drvp->attach(dev);
28428219Smsmith	    }
28528219Smsmith	}
28628219Smsmith	return (0);
28728219Smsmith}
28828219Smsmith
28928219Smsmith/*
29028257Smsmith * ppb_next_bus()
29128219Smsmith *
29228257Smsmith * Return the next bus in ppbus queue
29328219Smsmith */
29428257Smsmithstruct ppb_data *
29528257Smsmithppb_next_bus(struct ppb_data *ppb)
29628219Smsmith{
29728219Smsmith
29828257Smsmith	if (ppb == NULL)
29928257Smsmith		return (ppbdata.lh_first);
30028219Smsmith
30128257Smsmith	return (ppb->ppb_chain.le_next);
30228257Smsmith}
30328219Smsmith
30428257Smsmith/*
30528257Smsmith * ppb_lookup_bus()
30628257Smsmith *
30728257Smsmith * Get ppb_data structure pointer according to the base address of the ppbus
30828257Smsmith */
30928257Smsmithstruct ppb_data *
31028257Smsmithppb_lookup_bus(int base_port)
31128257Smsmith{
31228257Smsmith	struct ppb_data *ppb;
31328219Smsmith
31428257Smsmith	for (ppb = ppbdata.lh_first; ppb; ppb = ppb->ppb_chain.le_next)
31528257Smsmith		if (ppb->ppb_link->base == base_port)
31628257Smsmith			break;
31728219Smsmith
31828257Smsmith	return (ppb);
31928219Smsmith}
32028219Smsmith
32128219Smsmith/*
32238061Smsmith * ppb_lookup_link()
32338061Smsmith *
32438061Smsmith * Get ppb_data structure pointer according to the unit value
32538061Smsmith * of the corresponding link structure
32638061Smsmith */
32738061Smsmithstruct ppb_data *
32838061Smsmithppb_lookup_link(int unit)
32938061Smsmith{
33038061Smsmith	struct ppb_data *ppb;
33138061Smsmith
33238061Smsmith	for (ppb = ppbdata.lh_first; ppb; ppb = ppb->ppb_chain.le_next)
33338061Smsmith		if (ppb->ppb_link->adapter_unit == unit)
33438061Smsmith			break;
33538061Smsmith
33638061Smsmith	return (ppb);
33738061Smsmith}
33838061Smsmith
33938061Smsmith/*
34028257Smsmith * ppb_attach_device()
34128219Smsmith *
34228257Smsmith * Called by loadable kernel modules to add a device
34328219Smsmith */
34428219Smsmithint
34528257Smsmithppb_attach_device(struct ppb_device *dev)
34628219Smsmith{
34728219Smsmith	struct ppb_data *ppb = dev->ppb;
34828219Smsmith
34928257Smsmith	/* add the device to the list of probed devices */
35028257Smsmith	LIST_INSERT_HEAD(&ppb->ppb_devs, dev, chain);
35128219Smsmith
35228219Smsmith	return (0);
35328219Smsmith}
35428219Smsmith
35528219Smsmith/*
35628257Smsmith * ppb_remove_device()
35728219Smsmith *
35828257Smsmith * Called by loadable kernel modules to remove a device
35928219Smsmith */
36028219Smsmithvoid
36128257Smsmithppb_remove_device(struct ppb_device *dev)
36228219Smsmith{
36328219Smsmith
36428257Smsmith	/* remove the device from the list of probed devices */
36528257Smsmith	LIST_REMOVE(dev, chain);
36628219Smsmith
36728219Smsmith	return;
36828219Smsmith}
36928219Smsmith
37028219Smsmith/*
37128257Smsmith * ppb_request_bus()
37228219Smsmith *
37328257Smsmith * Allocate the device to perform transfers.
37428257Smsmith *
37528257Smsmith * how	: PPB_WAIT or PPB_DONTWAIT
37628219Smsmith */
37728219Smsmithint
37828257Smsmithppb_request_bus(struct ppb_device *dev, int how)
37928219Smsmith{
38028257Smsmith	int s, error = 0;
38128219Smsmith	struct ppb_data *ppb = dev->ppb;
38228219Smsmith
38328257Smsmith	while (!error) {
38428257Smsmith		s = splhigh();
38528257Smsmith		if (ppb->ppb_owner) {
38628257Smsmith			splx(s);
38728219Smsmith
38828257Smsmith			switch (how) {
38928257Smsmith			case (PPB_WAIT | PPB_INTR):
39028257Smsmith				error = tsleep(ppb, PPBPRI|PCATCH, "ppbreq", 0);
39128257Smsmith				break;
39228219Smsmith
39328257Smsmith			case (PPB_WAIT | PPB_NOINTR):
39428257Smsmith				error = tsleep(ppb, PPBPRI, "ppbreq", 0);
39528257Smsmith				break;
39628219Smsmith
39728257Smsmith			default:
39828257Smsmith				return (EWOULDBLOCK);
39928257Smsmith				break;
40028257Smsmith			}
40128219Smsmith
40228257Smsmith		} else {
40328257Smsmith			ppb->ppb_owner = dev;
40428219Smsmith
40538061Smsmith			/* restore the context of the device
40638061Smsmith			 * The first time, ctx.valid is certainly false
40738061Smsmith			 * then do not change anything. This is usefull for
40838061Smsmith			 * drivers that do not set there operating mode
40938061Smsmith			 * during attachement
41038061Smsmith			 */
41138061Smsmith			if (dev->ctx.valid)
41238061Smsmith				ppb_set_mode(dev, dev->ctx.mode);
41338061Smsmith
41428257Smsmith			splx(s);
41528257Smsmith			return (0);
41628257Smsmith		}
41728257Smsmith	}
41828219Smsmith
41928257Smsmith	return (error);
42028219Smsmith}
42128219Smsmith
42228219Smsmith/*
42328257Smsmith * ppb_release_bus()
42428219Smsmith *
42528257Smsmith * Release the device allocated with ppb_request_dev()
42628219Smsmith */
42728219Smsmithint
42828257Smsmithppb_release_bus(struct ppb_device *dev)
42928219Smsmith{
43028257Smsmith	int s;
43128219Smsmith	struct ppb_data *ppb = dev->ppb;
43228219Smsmith
43328257Smsmith	s = splhigh();
43428257Smsmith	if (ppb->ppb_owner != dev) {
43528257Smsmith		splx(s);
43628219Smsmith		return (EACCES);
43728257Smsmith	}
43828219Smsmith
43928257Smsmith	ppb->ppb_owner = 0;
44028257Smsmith	splx(s);
44128219Smsmith
44238061Smsmith	/* save the context of the device */
44338061Smsmith	dev->ctx.mode = ppb_get_mode(dev);
44438061Smsmith
44538061Smsmith	/* ok, now the context of the device is valid */
44638061Smsmith	dev->ctx.valid = 1;
44738061Smsmith
44828257Smsmith	/* wakeup waiting processes */
44928257Smsmith	wakeup(ppb);
45028219Smsmith
45128219Smsmith	return (0);
45228219Smsmith}
453