ppbconf.c revision 28257
128219Smsmith/*-
228219Smsmith * Copyright (c) 1997 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 *
2628257Smsmith *	$Id: ppbconf.c,v 1.1 1997/08/14 13:57:41 msmith Exp $
2728219Smsmith *
2828219Smsmith */
2928219Smsmith#include <sys/param.h>
3028219Smsmith#include <sys/systm.h>
3128219Smsmith#include <sys/errno.h>
3228219Smsmith#include <sys/conf.h>
3328219Smsmith#include <sys/proc.h>
3428219Smsmith#include <sys/buf.h>
3528219Smsmith#include <sys/kernel.h>
3628219Smsmith#include <sys/malloc.h>
3728219Smsmith#include <sys/uio.h>
3828219Smsmith#include <sys/syslog.h>
3928219Smsmith
4028219Smsmith#include <machine/clock.h>
4128219Smsmith#include <machine/lpt.h>
4228219Smsmith
4328219Smsmith#include <vm/vm.h>
4428219Smsmith#include <vm/vm_param.h>
4528219Smsmith#include <vm/pmap.h>
4628219Smsmith
4728219Smsmith#include <i386/isa/isa.h>
4828219Smsmith#include <i386/isa/isa_device.h>
4928219Smsmith
5028219Smsmith#include <dev/ppbus/ppbconf.h>
5128257Smsmith#include <dev/ppbus/ppb_1284.h>
5228219Smsmith
5328219SmsmithLIST_HEAD(, ppb_data)	ppbdata;	/* list of existing ppbus */
5428219Smsmith
5528219Smsmith/*
5628219Smsmith * Add a null driver so that the linker set always exists.
5728219Smsmith */
5828219Smsmith
5928219Smsmithstatic struct ppb_driver nulldriver = {
6028219Smsmith    NULL, NULL, "null"
6128219Smsmith};
6228219SmsmithDATA_SET(ppbdriver_set, nulldriver);
6328219Smsmith
6428219Smsmith
6528219Smsmith/*
6628219Smsmith * ppb_alloc_bus()
6728219Smsmith *
6828219Smsmith * Allocate area to store the ppbus description.
6928219Smsmith * This function is called by ppcattach().
7028219Smsmith */
7128219Smsmithstruct ppb_data *
7228219Smsmithppb_alloc_bus(void)
7328219Smsmith{
7428219Smsmith	struct ppb_data *ppb;
7528219Smsmith	static int ppbdata_initted = 0;		/* done-init flag */
7628219Smsmith
7728219Smsmith	ppb = (struct ppb_data *) malloc(sizeof(struct ppb_data),
7828219Smsmith		M_TEMP, M_NOWAIT);
7928219Smsmith
8028219Smsmith	/*
8128219Smsmith	 * Add the new parallel port bus to the list of existing ppbus.
8228219Smsmith	 */
8328219Smsmith	if (ppb) {
8428219Smsmith		bzero(ppb, sizeof(struct ppb_data));
8528219Smsmith
8628219Smsmith		if (!ppbdata_initted) {		/* list not initialised */
8728219Smsmith		    LIST_INIT(&ppbdata);
8828219Smsmith		    ppbdata_initted = 1;
8928219Smsmith		}
9028219Smsmith		LIST_INSERT_HEAD(&ppbdata, ppb, ppb_chain);
9128219Smsmith	} else {
9228219Smsmith		printf("ppb_alloc_bus: cannot malloc!\n");
9328219Smsmith	}
9428219Smsmith	return(ppb);
9528219Smsmith}
9628219Smsmith
9728257Smsmithstatic char *pnp_tokens[] = {
9828257Smsmith	"PRINTER", "MODEM", "NET", "HDC", "PCMCIA", "MEDIA",
9928257Smsmith	"FDC", "PORTS", "SCANNER", "DIGICAM", "", NULL };
10028257Smsmith
10128257Smsmithstatic char *pnp_classes[] = {
10228257Smsmith	"printer", "modem", "network device",
10328257Smsmith	"hard disk", "PCMCIA", "multimedia device",
10428257Smsmith	"floppy disk", "ports", "scanner",
10528257Smsmith	"digital camera", "unknown device", NULL };
10628257Smsmith
10728219Smsmith/*
10828257Smsmith * search_token()
10928257Smsmith *
11028257Smsmith * Search the first occurence of a token within a string
11128257Smsmith */
11228257Smsmithstatic char *
11328257Smsmithsearch_token(char *str, int slen, char *token)
11428257Smsmith{
11528257Smsmith	char *p;
11628257Smsmith	int tlen, i, j;
11728257Smsmith
11828257Smsmith#define UNKNOWN_LENGTH	-1
11928257Smsmith
12028257Smsmith	if (slen == UNKNOWN_LENGTH)
12128257Smsmith		/* get string's length */
12228257Smsmith		for (slen = 0, p = str; *p != '\0'; p++)
12328257Smsmith			slen ++;
12428257Smsmith
12528257Smsmith	/* get token's length */
12628257Smsmith	for (tlen = 0, p = token; *p != '\0'; p++)
12728257Smsmith		tlen ++;
12828257Smsmith
12928257Smsmith	if (tlen == 0)
13028257Smsmith		return (str);
13128257Smsmith
13228257Smsmith	for (i = 0; i <= slen-tlen; i++) {
13328257Smsmith		for (j = 0; j < tlen; j++)
13428257Smsmith			if (str[i+j] != token[j])
13528257Smsmith				break;
13628257Smsmith		if (j == tlen)
13728257Smsmith			return (&str[i]);
13828257Smsmith	}
13928257Smsmith
14028257Smsmith	return (NULL);
14128257Smsmith}
14228257Smsmith
14328257Smsmith/*
14428257Smsmith * ppb_pnp_detect()
14528257Smsmith *
14628257Smsmith * Returns the class id. of the peripherial, -1 otherwise
14728257Smsmith */
14828257Smsmithstatic int
14928257Smsmithppb_pnp_detect(struct ppb_data *ppb)
15028257Smsmith{
15128257Smsmith	char *token, *q, *class = 0;
15228257Smsmith	int i, len, error;
15328257Smsmith	char str[PPB_PnP_STRING_SIZE+1];
15428257Smsmith
15528257Smsmith	struct ppb_device pnpdev;	/* temporary device to perform I/O */
15628257Smsmith
15728257Smsmith	/* initialize the pnpdev structure for future use */
15828257Smsmith	bzero(&pnpdev, sizeof(pnpdev));
15928257Smsmith
16028257Smsmith	pnpdev.ppb = ppb;
16128257Smsmith
16228257Smsmith#ifdef PnP_DEBUG
16328257Smsmith	printf("ppb: <PnP> probing PnP devices on ppbus%d...\n",
16428257Smsmith		ppb->ppb_link->adapter_unit);
16528257Smsmith#endif
16628257Smsmith
16728257Smsmith	ppb_wctr(&pnpdev, nINIT | SELECTIN);
16828257Smsmith
16928257Smsmith	/* select NIBBLE_1284_REQUEST_ID mode */
17028257Smsmith	if ((error = nibble_1284_mode(&pnpdev, NIBBLE_1284_REQUEST_ID))) {
17128257Smsmith#ifdef PnP_DEBUG
17228257Smsmith		printf("ppb: <PnP> nibble_1284_mode()=%d\n", error);
17328257Smsmith#endif
17428257Smsmith		return (-1);
17528257Smsmith	}
17628257Smsmith
17728257Smsmith	len = 0;
17828257Smsmith	for (q = str; !(ppb_rstr(&pnpdev) & ERROR); q++) {
17928257Smsmith		if ((error = nibble_1284_inbyte(&pnpdev, q))) {
18028257Smsmith#ifdef PnP_DEBUG
18128257Smsmith			printf("ppb: <PnP> nibble_1284_inbyte()=%d\n", error);
18228257Smsmith#endif
18328257Smsmith			return (-1);
18428257Smsmith		}
18528257Smsmith		if (len++ >= PPB_PnP_STRING_SIZE) {
18628257Smsmith			printf("ppb: <PnP> not space left!\n");
18728257Smsmith			return (-1);
18828257Smsmith		}
18928257Smsmith	}
19028257Smsmith	*q = '\0';
19128257Smsmith
19228257Smsmith	nibble_1284_sync(&pnpdev);
19328257Smsmith
19428257Smsmith#ifdef PnP_DEBUG
19528257Smsmith	printf("ppb: <PnP> %d characters: ", len);
19628257Smsmith	for (i = 0; i < len; i++)
19728257Smsmith		printf("0x%x ", str[i]);
19828257Smsmith	printf("\n");
19928257Smsmith#endif
20028257Smsmith
20128257Smsmith	/* replace ';' characters by '\0' */
20228257Smsmith	for (i = 0; i < len; i++)
20328257Smsmith		str[i] = (str[i] == ';') ? '\0' : str[i];
20428257Smsmith
20528257Smsmith	if ((token = search_token(str, len, "MFG")) != NULL)
20628257Smsmith		printf("ppbus%d: <%s", ppb->ppb_link->adapter_unit,
20728257Smsmith			search_token(token, UNKNOWN_LENGTH, ":") + 1);
20828257Smsmith	else
20928257Smsmith		printf("ppbus%d: <unknown", ppb->ppb_link->adapter_unit);
21028257Smsmith
21128257Smsmith	if ((token = search_token(str, len, "MDL")) != NULL)
21228257Smsmith		printf(" %s",
21328257Smsmith			search_token(token, UNKNOWN_LENGTH, ":") + 1);
21428257Smsmith	else
21528257Smsmith		printf(" unknown");
21628257Smsmith
21728257Smsmith	if ((token = search_token(str, len, "VER")) != NULL)
21828257Smsmith		printf("/%s",
21928257Smsmith			search_token(token, UNKNOWN_LENGTH, ":") + 1);
22028257Smsmith
22128257Smsmith	if ((token = search_token(str, len, "REV")) != NULL)
22228257Smsmith		printf(".%s",
22328257Smsmith			search_token(token, UNKNOWN_LENGTH, ":") + 1);
22428257Smsmith
22528257Smsmith	printf(">");
22628257Smsmith
22728257Smsmith	if ((token = search_token(str, len, "CLS")) != NULL) {
22828257Smsmith		class = search_token(token, UNKNOWN_LENGTH, ":") + 1;
22928257Smsmith		printf(" %s", class);
23028257Smsmith	}
23128257Smsmith
23228257Smsmith	if ((token = search_token(str, len, "CMD")) != NULL)
23328257Smsmith		printf(" %s",
23428257Smsmith			search_token(token, UNKNOWN_LENGTH, ":") + 1);
23528257Smsmith
23628257Smsmith	printf("\n");
23728257Smsmith
23828257Smsmith	if (class)
23928257Smsmith		/* identify class ident */
24028257Smsmith		for (i = 0; pnp_tokens[i] != NULL; i++) {
24128257Smsmith			if (search_token(class, len, pnp_tokens[i]) != NULL) {
24228257Smsmith				return (i);
24328257Smsmith				break;
24428257Smsmith			}
24528257Smsmith		}
24628257Smsmith
24728257Smsmith	return (PPB_PnP_UNKNOWN);
24828257Smsmith}
24928257Smsmith
25028257Smsmith/*
25128219Smsmith * ppb_attachdevs()
25228219Smsmith *
25328219Smsmith * Called by ppcattach(), this function probes the ppbus and
25428219Smsmith * attaches found devices.
25528219Smsmith */
25628219Smsmithint
25728219Smsmithppb_attachdevs(struct ppb_data *ppb)
25828219Smsmith{
25928219Smsmith	int error;
26028219Smsmith	struct ppb_device *dev;
26128219Smsmith	struct ppb_driver **p_drvpp, *p_drvp;
26228257Smsmith
26328219Smsmith	LIST_INIT(&ppb->ppb_devs);	/* initialise device/driver list */
26428219Smsmith	p_drvpp = (struct ppb_driver **)ppbdriver_set.ls_items;
26528257Smsmith
26628257Smsmith	/* detect PnP devices */
26728257Smsmith	ppb->class_id = ppb_pnp_detect(ppb);
26828219Smsmith
26928219Smsmith	/*
27028219Smsmith	 * Blindly try all probes here.  Later we should look at
27128219Smsmith	 * the parallel-port PnP standard, and intelligently seek
27228219Smsmith	 * drivers based on configuration first.
27328219Smsmith	 */
27428219Smsmith	while ((p_drvp = *p_drvpp++) != NULL) {
27528219Smsmith	    if (p_drvp->probe && (dev = (p_drvp->probe(ppb))) != NULL) {
27628219Smsmith		/*
27728219Smsmith		 * Add the device to the list of probed devices.
27828219Smsmith		 */
27928219Smsmith		LIST_INSERT_HEAD(&ppb->ppb_devs, dev, chain);
28028219Smsmith
28128219Smsmith		/* Call the device's attach routine */
28228219Smsmith		(void)p_drvp->attach(dev);
28328219Smsmith	    }
28428219Smsmith	}
28528219Smsmith	return (0);
28628219Smsmith}
28728219Smsmith
28828219Smsmith/*
28928257Smsmith * ppb_next_bus()
29028219Smsmith *
29128257Smsmith * Return the next bus in ppbus queue
29228219Smsmith */
29328257Smsmithstruct ppb_data *
29428257Smsmithppb_next_bus(struct ppb_data *ppb)
29528219Smsmith{
29628219Smsmith
29728257Smsmith	if (ppb == NULL)
29828257Smsmith		return (ppbdata.lh_first);
29928219Smsmith
30028257Smsmith	return (ppb->ppb_chain.le_next);
30128257Smsmith}
30228219Smsmith
30328257Smsmith/*
30428257Smsmith * ppb_lookup_bus()
30528257Smsmith *
30628257Smsmith * Get ppb_data structure pointer according to the base address of the ppbus
30728257Smsmith */
30828257Smsmithstruct ppb_data *
30928257Smsmithppb_lookup_bus(int base_port)
31028257Smsmith{
31128257Smsmith	struct ppb_data *ppb;
31228219Smsmith
31328257Smsmith	for (ppb = ppbdata.lh_first; ppb; ppb = ppb->ppb_chain.le_next)
31428257Smsmith		if (ppb->ppb_link->base == base_port)
31528257Smsmith			break;
31628219Smsmith
31728257Smsmith	return (ppb);
31828219Smsmith}
31928219Smsmith
32028219Smsmith/*
32128257Smsmith * ppb_attach_device()
32228219Smsmith *
32328257Smsmith * Called by loadable kernel modules to add a device
32428219Smsmith */
32528219Smsmithint
32628257Smsmithppb_attach_device(struct ppb_device *dev)
32728219Smsmith{
32828219Smsmith	struct ppb_data *ppb = dev->ppb;
32928219Smsmith
33028257Smsmith	/* add the device to the list of probed devices */
33128257Smsmith	LIST_INSERT_HEAD(&ppb->ppb_devs, dev, chain);
33228219Smsmith
33328219Smsmith	return (0);
33428219Smsmith}
33528219Smsmith
33628219Smsmith/*
33728257Smsmith * ppb_remove_device()
33828219Smsmith *
33928257Smsmith * Called by loadable kernel modules to remove a device
34028219Smsmith */
34128219Smsmithvoid
34228257Smsmithppb_remove_device(struct ppb_device *dev)
34328219Smsmith{
34428219Smsmith
34528257Smsmith	/* remove the device from the list of probed devices */
34628257Smsmith	LIST_REMOVE(dev, chain);
34728219Smsmith
34828219Smsmith	return;
34928219Smsmith}
35028219Smsmith
35128219Smsmith/*
35228257Smsmith * ppb_request_bus()
35328219Smsmith *
35428257Smsmith * Allocate the device to perform transfers.
35528257Smsmith *
35628257Smsmith * how	: PPB_WAIT or PPB_DONTWAIT
35728219Smsmith */
35828219Smsmithint
35928257Smsmithppb_request_bus(struct ppb_device *dev, int how)
36028219Smsmith{
36128257Smsmith	int s, error = 0;
36228219Smsmith	struct ppb_data *ppb = dev->ppb;
36328219Smsmith
36428257Smsmith	while (!error) {
36528257Smsmith		s = splhigh();
36628257Smsmith		if (ppb->ppb_owner) {
36728257Smsmith			splx(s);
36828219Smsmith
36928257Smsmith			switch (how) {
37028257Smsmith			case (PPB_WAIT | PPB_INTR):
37128257Smsmith				error = tsleep(ppb, PPBPRI|PCATCH, "ppbreq", 0);
37228257Smsmith				break;
37328219Smsmith
37428257Smsmith			case (PPB_WAIT | PPB_NOINTR):
37528257Smsmith				error = tsleep(ppb, PPBPRI, "ppbreq", 0);
37628257Smsmith				break;
37728219Smsmith
37828257Smsmith			default:
37928257Smsmith				return (EWOULDBLOCK);
38028257Smsmith				break;
38128257Smsmith			}
38228219Smsmith
38328257Smsmith		} else {
38428257Smsmith			ppb->ppb_owner = dev;
38528219Smsmith
38628257Smsmith			splx(s);
38728257Smsmith			return (0);
38828257Smsmith		}
38928257Smsmith	}
39028219Smsmith
39128257Smsmith	return (error);
39228219Smsmith}
39328219Smsmith
39428219Smsmith/*
39528257Smsmith * ppb_release_bus()
39628219Smsmith *
39728257Smsmith * Release the device allocated with ppb_request_dev()
39828219Smsmith */
39928219Smsmithint
40028257Smsmithppb_release_bus(struct ppb_device *dev)
40128219Smsmith{
40228257Smsmith	int s;
40328219Smsmith	struct ppb_data *ppb = dev->ppb;
40428219Smsmith
40528257Smsmith	s = splhigh();
40628257Smsmith	if (ppb->ppb_owner != dev) {
40728257Smsmith		splx(s);
40828219Smsmith		return (EACCES);
40928257Smsmith	}
41028219Smsmith
41128257Smsmith	ppb->ppb_owner = 0;
41228257Smsmith	splx(s);
41328219Smsmith
41428257Smsmith	/* wakeup waiting processes */
41528257Smsmith	wakeup(ppb);
41628219Smsmith
41728219Smsmith	return (0);
41828219Smsmith}
419