ppbconf.c revision 38061
1/*- 2 * Copyright (c) 1997, 1998 Nicolas Souchu 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 * $Id: ppbconf.c,v 1.4 1997/09/01 00:51:46 bde Exp $ 27 * 28 */ 29#include <sys/param.h> 30#include <sys/systm.h> 31#include <sys/kernel.h> 32#include <sys/malloc.h> 33 34#include <vm/vm.h> 35#include <vm/pmap.h> 36 37#include <dev/ppbus/ppbconf.h> 38#include <dev/ppbus/ppb_1284.h> 39 40LIST_HEAD(, ppb_data) ppbdata; /* list of existing ppbus */ 41 42/* 43 * Add a null driver so that the linker set always exists. 44 */ 45 46static struct ppb_driver nulldriver = { 47 NULL, NULL, "null" 48}; 49DATA_SET(ppbdriver_set, nulldriver); 50 51 52/* 53 * ppb_alloc_bus() 54 * 55 * Allocate area to store the ppbus description. 56 */ 57struct ppb_data * 58ppb_alloc_bus(void) 59{ 60 struct ppb_data *ppb; 61 static int ppbdata_initted = 0; /* done-init flag */ 62 63 ppb = (struct ppb_data *) malloc(sizeof(struct ppb_data), 64 M_TEMP, M_NOWAIT); 65 66 /* 67 * Add the new parallel port bus to the list of existing ppbus. 68 */ 69 if (ppb) { 70 bzero(ppb, sizeof(struct ppb_data)); 71 72 if (!ppbdata_initted) { /* list not initialised */ 73 LIST_INIT(&ppbdata); 74 ppbdata_initted = 1; 75 } 76 LIST_INSERT_HEAD(&ppbdata, ppb, ppb_chain); 77 } else { 78 printf("ppb_alloc_bus: cannot malloc!\n"); 79 } 80 return(ppb); 81} 82 83static char *pnp_tokens[] = { 84 "PRINTER", "MODEM", "NET", "HDC", "PCMCIA", "MEDIA", 85 "FDC", "PORTS", "SCANNER", "DIGICAM", "", NULL }; 86 87static char *pnp_classes[] = { 88 "printer", "modem", "network device", 89 "hard disk", "PCMCIA", "multimedia device", 90 "floppy disk", "ports", "scanner", 91 "digital camera", "unknown device", NULL }; 92 93/* 94 * search_token() 95 * 96 * Search the first occurence of a token within a string 97 */ 98static char * 99search_token(char *str, int slen, char *token) 100{ 101 char *p; 102 int tlen, i, j; 103 104#define UNKNOWN_LENGTH -1 105 106 if (slen == UNKNOWN_LENGTH) 107 /* get string's length */ 108 for (slen = 0, p = str; *p != '\0'; p++) 109 slen ++; 110 111 /* get token's length */ 112 for (tlen = 0, p = token; *p != '\0'; p++) 113 tlen ++; 114 115 if (tlen == 0) 116 return (str); 117 118 for (i = 0; i <= slen-tlen; i++) { 119 for (j = 0; j < tlen; j++) 120 if (str[i+j] != token[j]) 121 break; 122 if (j == tlen) 123 return (&str[i]); 124 } 125 126 return (NULL); 127} 128 129/* 130 * ppb_pnp_detect() 131 * 132 * Returns the class id. of the peripherial, -1 otherwise 133 */ 134static int 135ppb_pnp_detect(struct ppb_data *ppb) 136{ 137 char *token, *q, *class = 0; 138 int i, len, error; 139 int class_id = -1; 140 char str[PPB_PnP_STRING_SIZE+1]; 141 struct ppb_device pnpdev; /* temporary device to perform I/O */ 142 143 /* initialize the pnpdev structure for future use */ 144 bzero(&pnpdev, sizeof(pnpdev)); 145 146 pnpdev.ppb = ppb; 147 148 if (bootverbose) 149 printf("ppb: <PnP> probing devices on ppbus %d...\n", 150 ppb->ppb_link->adapter_unit); 151 152 if (ppb_request_bus(&pnpdev, PPB_DONTWAIT)) { 153 if (bootverbose) 154 printf("ppb: <PnP> cannot allocate ppbus!\n"); 155 return (-1); 156 } 157 158 ppb_wctr(&pnpdev, nINIT | SELECTIN); 159 160 /* select NIBBLE_1284_REQUEST_ID mode */ 161 if ((error = nibble_1284_mode(&pnpdev, NIBBLE_1284_REQUEST_ID))) { 162 if (bootverbose) 163 printf("ppb: <PnP> nibble_1284_mode()=%d\n", error); 164 goto end_detect; 165 } 166 167 len = 0; 168 for (q = str; !(ppb_rstr(&pnpdev) & ERROR); q++) { 169 if ((error = nibble_1284_inbyte(&pnpdev, q))) { 170 if (bootverbose) 171 printf("ppb: <PnP> nibble_1284_inbyte()=%d\n", 172 error); 173 goto end_detect; 174 } 175 if (len++ >= PPB_PnP_STRING_SIZE) { 176 printf("ppb: <PnP> not space left!\n"); 177 goto end_detect; 178 } 179 } 180 *q = '\0'; 181 182 nibble_1284_sync(&pnpdev); 183 184 if (bootverbose) { 185 printf("ppb: <PnP> %d characters: ", len); 186 for (i = 0; i < len; i++) 187 printf("0x%x ", str[i]); 188 printf("\n"); 189 } 190 191 /* replace ';' characters by '\0' */ 192 for (i = 0; i < len; i++) 193 str[i] = (str[i] == ';') ? '\0' : str[i]; 194 195 if ((token = search_token(str, len, "MFG")) != NULL) 196 printf("ppbus%d: <%s", ppb->ppb_link->adapter_unit, 197 search_token(token, UNKNOWN_LENGTH, ":") + 1); 198 else 199 printf("ppbus%d: <unknown", ppb->ppb_link->adapter_unit); 200 201 if ((token = search_token(str, len, "MDL")) != NULL) 202 printf(" %s", 203 search_token(token, UNKNOWN_LENGTH, ":") + 1); 204 else 205 printf(" unknown"); 206 207 if ((token = search_token(str, len, "VER")) != NULL) 208 printf("/%s", 209 search_token(token, UNKNOWN_LENGTH, ":") + 1); 210 211 if ((token = search_token(str, len, "REV")) != NULL) 212 printf(".%s", 213 search_token(token, UNKNOWN_LENGTH, ":") + 1); 214 215 printf(">"); 216 217 if ((token = search_token(str, len, "CLS")) != NULL) { 218 class = search_token(token, UNKNOWN_LENGTH, ":") + 1; 219 printf(" %s", class); 220 } 221 222 if ((token = search_token(str, len, "CMD")) != NULL) 223 printf(" %s", 224 search_token(token, UNKNOWN_LENGTH, ":") + 1); 225 226 printf("\n"); 227 228 if (class) 229 /* identify class ident */ 230 for (i = 0; pnp_tokens[i] != NULL; i++) { 231 if (search_token(class, len, pnp_tokens[i]) != NULL) { 232 class_id = i; 233 goto end_detect; 234 } 235 } 236 237 class_id = PPB_PnP_UNKNOWN; 238 239end_detect: 240 ppb_release_bus(&pnpdev); 241 return (class_id); 242} 243 244/* 245 * ppb_attachdevs() 246 * 247 * Called by ppcattach(), this function probes the ppbus and 248 * attaches found devices. 249 */ 250int 251ppb_attachdevs(struct ppb_data *ppb) 252{ 253 int error; 254 struct ppb_device *dev; 255 struct ppb_driver **p_drvpp, *p_drvp; 256 257 LIST_INIT(&ppb->ppb_devs); /* initialise device/driver list */ 258 p_drvpp = (struct ppb_driver **)ppbdriver_set.ls_items; 259 260 /* detect PnP devices */ 261 ppb->class_id = ppb_pnp_detect(ppb); 262 263 /* 264 * Blindly try all probes here. Later we should look at 265 * the parallel-port PnP standard, and intelligently seek 266 * drivers based on configuration first. 267 */ 268 while ((p_drvp = *p_drvpp++) != NULL) { 269 if (p_drvp->probe && (dev = (p_drvp->probe(ppb))) != NULL) { 270 /* 271 * Add the device to the list of probed devices. 272 */ 273 LIST_INSERT_HEAD(&ppb->ppb_devs, dev, chain); 274 275 /* Call the device's attach routine */ 276 (void)p_drvp->attach(dev); 277 } 278 } 279 return (0); 280} 281 282/* 283 * ppb_next_bus() 284 * 285 * Return the next bus in ppbus queue 286 */ 287struct ppb_data * 288ppb_next_bus(struct ppb_data *ppb) 289{ 290 291 if (ppb == NULL) 292 return (ppbdata.lh_first); 293 294 return (ppb->ppb_chain.le_next); 295} 296 297/* 298 * ppb_lookup_bus() 299 * 300 * Get ppb_data structure pointer according to the base address of the ppbus 301 */ 302struct ppb_data * 303ppb_lookup_bus(int base_port) 304{ 305 struct ppb_data *ppb; 306 307 for (ppb = ppbdata.lh_first; ppb; ppb = ppb->ppb_chain.le_next) 308 if (ppb->ppb_link->base == base_port) 309 break; 310 311 return (ppb); 312} 313 314/* 315 * ppb_lookup_link() 316 * 317 * Get ppb_data structure pointer according to the unit value 318 * of the corresponding link structure 319 */ 320struct ppb_data * 321ppb_lookup_link(int unit) 322{ 323 struct ppb_data *ppb; 324 325 for (ppb = ppbdata.lh_first; ppb; ppb = ppb->ppb_chain.le_next) 326 if (ppb->ppb_link->adapter_unit == unit) 327 break; 328 329 return (ppb); 330} 331 332/* 333 * ppb_attach_device() 334 * 335 * Called by loadable kernel modules to add a device 336 */ 337int 338ppb_attach_device(struct ppb_device *dev) 339{ 340 struct ppb_data *ppb = dev->ppb; 341 342 /* add the device to the list of probed devices */ 343 LIST_INSERT_HEAD(&ppb->ppb_devs, dev, chain); 344 345 return (0); 346} 347 348/* 349 * ppb_remove_device() 350 * 351 * Called by loadable kernel modules to remove a device 352 */ 353void 354ppb_remove_device(struct ppb_device *dev) 355{ 356 357 /* remove the device from the list of probed devices */ 358 LIST_REMOVE(dev, chain); 359 360 return; 361} 362 363/* 364 * ppb_request_bus() 365 * 366 * Allocate the device to perform transfers. 367 * 368 * how : PPB_WAIT or PPB_DONTWAIT 369 */ 370int 371ppb_request_bus(struct ppb_device *dev, int how) 372{ 373 int s, error = 0; 374 struct ppb_data *ppb = dev->ppb; 375 376 while (!error) { 377 s = splhigh(); 378 if (ppb->ppb_owner) { 379 splx(s); 380 381 switch (how) { 382 case (PPB_WAIT | PPB_INTR): 383 error = tsleep(ppb, PPBPRI|PCATCH, "ppbreq", 0); 384 break; 385 386 case (PPB_WAIT | PPB_NOINTR): 387 error = tsleep(ppb, PPBPRI, "ppbreq", 0); 388 break; 389 390 default: 391 return (EWOULDBLOCK); 392 break; 393 } 394 395 } else { 396 ppb->ppb_owner = dev; 397 398 /* restore the context of the device 399 * The first time, ctx.valid is certainly false 400 * then do not change anything. This is usefull for 401 * drivers that do not set there operating mode 402 * during attachement 403 */ 404 if (dev->ctx.valid) 405 ppb_set_mode(dev, dev->ctx.mode); 406 407 splx(s); 408 return (0); 409 } 410 } 411 412 return (error); 413} 414 415/* 416 * ppb_release_bus() 417 * 418 * Release the device allocated with ppb_request_dev() 419 */ 420int 421ppb_release_bus(struct ppb_device *dev) 422{ 423 int s; 424 struct ppb_data *ppb = dev->ppb; 425 426 s = splhigh(); 427 if (ppb->ppb_owner != dev) { 428 splx(s); 429 return (EACCES); 430 } 431 432 ppb->ppb_owner = 0; 433 splx(s); 434 435 /* save the context of the device */ 436 dev->ctx.mode = ppb_get_mode(dev); 437 438 /* ok, now the context of the device is valid */ 439 dev->ctx.valid = 1; 440 441 /* wakeup waiting processes */ 442 wakeup(ppb); 443 444 return (0); 445} 446