ppbconf.c revision 28863
1/*- 2 * Copyright (c) 1997 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.2 1997/08/16 14:05:35 msmith Exp $ 27 * 28 */ 29#include <sys/param.h> 30#include <sys/systm.h> 31#include <sys/errno.h> 32#include <sys/conf.h> 33#include <sys/proc.h> 34#include <sys/buf.h> 35#include <sys/kernel.h> 36#include <sys/malloc.h> 37#include <sys/uio.h> 38#include <sys/syslog.h> 39 40#include <machine/clock.h> 41 42#include <vm/vm.h> 43#include <vm/vm_param.h> 44#include <vm/pmap.h> 45 46#include <dev/ppbus/ppbconf.h> 47#include <dev/ppbus/ppb_1284.h> 48 49LIST_HEAD(, ppb_data) ppbdata; /* list of existing ppbus */ 50 51/* 52 * Add a null driver so that the linker set always exists. 53 */ 54 55static struct ppb_driver nulldriver = { 56 NULL, NULL, "null" 57}; 58DATA_SET(ppbdriver_set, nulldriver); 59 60 61/* 62 * ppb_alloc_bus() 63 * 64 * Allocate area to store the ppbus description. 65 * This function is called by ppcattach(). 66 */ 67struct ppb_data * 68ppb_alloc_bus(void) 69{ 70 struct ppb_data *ppb; 71 static int ppbdata_initted = 0; /* done-init flag */ 72 73 ppb = (struct ppb_data *) malloc(sizeof(struct ppb_data), 74 M_TEMP, M_NOWAIT); 75 76 /* 77 * Add the new parallel port bus to the list of existing ppbus. 78 */ 79 if (ppb) { 80 bzero(ppb, sizeof(struct ppb_data)); 81 82 if (!ppbdata_initted) { /* list not initialised */ 83 LIST_INIT(&ppbdata); 84 ppbdata_initted = 1; 85 } 86 LIST_INSERT_HEAD(&ppbdata, ppb, ppb_chain); 87 } else { 88 printf("ppb_alloc_bus: cannot malloc!\n"); 89 } 90 return(ppb); 91} 92 93static char *pnp_tokens[] = { 94 "PRINTER", "MODEM", "NET", "HDC", "PCMCIA", "MEDIA", 95 "FDC", "PORTS", "SCANNER", "DIGICAM", "", NULL }; 96 97static char *pnp_classes[] = { 98 "printer", "modem", "network device", 99 "hard disk", "PCMCIA", "multimedia device", 100 "floppy disk", "ports", "scanner", 101 "digital camera", "unknown device", NULL }; 102 103/* 104 * search_token() 105 * 106 * Search the first occurence of a token within a string 107 */ 108static char * 109search_token(char *str, int slen, char *token) 110{ 111 char *p; 112 int tlen, i, j; 113 114#define UNKNOWN_LENGTH -1 115 116 if (slen == UNKNOWN_LENGTH) 117 /* get string's length */ 118 for (slen = 0, p = str; *p != '\0'; p++) 119 slen ++; 120 121 /* get token's length */ 122 for (tlen = 0, p = token; *p != '\0'; p++) 123 tlen ++; 124 125 if (tlen == 0) 126 return (str); 127 128 for (i = 0; i <= slen-tlen; i++) { 129 for (j = 0; j < tlen; j++) 130 if (str[i+j] != token[j]) 131 break; 132 if (j == tlen) 133 return (&str[i]); 134 } 135 136 return (NULL); 137} 138 139/* 140 * ppb_pnp_detect() 141 * 142 * Returns the class id. of the peripherial, -1 otherwise 143 */ 144static int 145ppb_pnp_detect(struct ppb_data *ppb) 146{ 147 char *token, *q, *class = 0; 148 int i, len, error; 149 char str[PPB_PnP_STRING_SIZE+1]; 150 151 struct ppb_device pnpdev; /* temporary device to perform I/O */ 152 153 /* initialize the pnpdev structure for future use */ 154 bzero(&pnpdev, sizeof(pnpdev)); 155 156 pnpdev.ppb = ppb; 157 158#ifdef PnP_DEBUG 159 printf("ppb: <PnP> probing PnP devices on ppbus%d...\n", 160 ppb->ppb_link->adapter_unit); 161#endif 162 163 ppb_wctr(&pnpdev, nINIT | SELECTIN); 164 165 /* select NIBBLE_1284_REQUEST_ID mode */ 166 if ((error = nibble_1284_mode(&pnpdev, NIBBLE_1284_REQUEST_ID))) { 167#ifdef PnP_DEBUG 168 printf("ppb: <PnP> nibble_1284_mode()=%d\n", error); 169#endif 170 return (-1); 171 } 172 173 len = 0; 174 for (q = str; !(ppb_rstr(&pnpdev) & ERROR); q++) { 175 if ((error = nibble_1284_inbyte(&pnpdev, q))) { 176#ifdef PnP_DEBUG 177 printf("ppb: <PnP> nibble_1284_inbyte()=%d\n", error); 178#endif 179 return (-1); 180 } 181 if (len++ >= PPB_PnP_STRING_SIZE) { 182 printf("ppb: <PnP> not space left!\n"); 183 return (-1); 184 } 185 } 186 *q = '\0'; 187 188 nibble_1284_sync(&pnpdev); 189 190#ifdef PnP_DEBUG 191 printf("ppb: <PnP> %d characters: ", len); 192 for (i = 0; i < len; i++) 193 printf("0x%x ", str[i]); 194 printf("\n"); 195#endif 196 197 /* replace ';' characters by '\0' */ 198 for (i = 0; i < len; i++) 199 str[i] = (str[i] == ';') ? '\0' : str[i]; 200 201 if ((token = search_token(str, len, "MFG")) != NULL) 202 printf("ppbus%d: <%s", ppb->ppb_link->adapter_unit, 203 search_token(token, UNKNOWN_LENGTH, ":") + 1); 204 else 205 printf("ppbus%d: <unknown", ppb->ppb_link->adapter_unit); 206 207 if ((token = search_token(str, len, "MDL")) != NULL) 208 printf(" %s", 209 search_token(token, UNKNOWN_LENGTH, ":") + 1); 210 else 211 printf(" unknown"); 212 213 if ((token = search_token(str, len, "VER")) != NULL) 214 printf("/%s", 215 search_token(token, UNKNOWN_LENGTH, ":") + 1); 216 217 if ((token = search_token(str, len, "REV")) != NULL) 218 printf(".%s", 219 search_token(token, UNKNOWN_LENGTH, ":") + 1); 220 221 printf(">"); 222 223 if ((token = search_token(str, len, "CLS")) != NULL) { 224 class = search_token(token, UNKNOWN_LENGTH, ":") + 1; 225 printf(" %s", class); 226 } 227 228 if ((token = search_token(str, len, "CMD")) != NULL) 229 printf(" %s", 230 search_token(token, UNKNOWN_LENGTH, ":") + 1); 231 232 printf("\n"); 233 234 if (class) 235 /* identify class ident */ 236 for (i = 0; pnp_tokens[i] != NULL; i++) { 237 if (search_token(class, len, pnp_tokens[i]) != NULL) { 238 return (i); 239 break; 240 } 241 } 242 243 return (PPB_PnP_UNKNOWN); 244} 245 246/* 247 * ppb_attachdevs() 248 * 249 * Called by ppcattach(), this function probes the ppbus and 250 * attaches found devices. 251 */ 252int 253ppb_attachdevs(struct ppb_data *ppb) 254{ 255 int error; 256 struct ppb_device *dev; 257 struct ppb_driver **p_drvpp, *p_drvp; 258 259 LIST_INIT(&ppb->ppb_devs); /* initialise device/driver list */ 260 p_drvpp = (struct ppb_driver **)ppbdriver_set.ls_items; 261 262 /* detect PnP devices */ 263 ppb->class_id = ppb_pnp_detect(ppb); 264 265 /* 266 * Blindly try all probes here. Later we should look at 267 * the parallel-port PnP standard, and intelligently seek 268 * drivers based on configuration first. 269 */ 270 while ((p_drvp = *p_drvpp++) != NULL) { 271 if (p_drvp->probe && (dev = (p_drvp->probe(ppb))) != NULL) { 272 /* 273 * Add the device to the list of probed devices. 274 */ 275 LIST_INSERT_HEAD(&ppb->ppb_devs, dev, chain); 276 277 /* Call the device's attach routine */ 278 (void)p_drvp->attach(dev); 279 } 280 } 281 return (0); 282} 283 284/* 285 * ppb_next_bus() 286 * 287 * Return the next bus in ppbus queue 288 */ 289struct ppb_data * 290ppb_next_bus(struct ppb_data *ppb) 291{ 292 293 if (ppb == NULL) 294 return (ppbdata.lh_first); 295 296 return (ppb->ppb_chain.le_next); 297} 298 299/* 300 * ppb_lookup_bus() 301 * 302 * Get ppb_data structure pointer according to the base address of the ppbus 303 */ 304struct ppb_data * 305ppb_lookup_bus(int base_port) 306{ 307 struct ppb_data *ppb; 308 309 for (ppb = ppbdata.lh_first; ppb; ppb = ppb->ppb_chain.le_next) 310 if (ppb->ppb_link->base == base_port) 311 break; 312 313 return (ppb); 314} 315 316/* 317 * ppb_attach_device() 318 * 319 * Called by loadable kernel modules to add a device 320 */ 321int 322ppb_attach_device(struct ppb_device *dev) 323{ 324 struct ppb_data *ppb = dev->ppb; 325 326 /* add the device to the list of probed devices */ 327 LIST_INSERT_HEAD(&ppb->ppb_devs, dev, chain); 328 329 return (0); 330} 331 332/* 333 * ppb_remove_device() 334 * 335 * Called by loadable kernel modules to remove a device 336 */ 337void 338ppb_remove_device(struct ppb_device *dev) 339{ 340 341 /* remove the device from the list of probed devices */ 342 LIST_REMOVE(dev, chain); 343 344 return; 345} 346 347/* 348 * ppb_request_bus() 349 * 350 * Allocate the device to perform transfers. 351 * 352 * how : PPB_WAIT or PPB_DONTWAIT 353 */ 354int 355ppb_request_bus(struct ppb_device *dev, int how) 356{ 357 int s, error = 0; 358 struct ppb_data *ppb = dev->ppb; 359 360 while (!error) { 361 s = splhigh(); 362 if (ppb->ppb_owner) { 363 splx(s); 364 365 switch (how) { 366 case (PPB_WAIT | PPB_INTR): 367 error = tsleep(ppb, PPBPRI|PCATCH, "ppbreq", 0); 368 break; 369 370 case (PPB_WAIT | PPB_NOINTR): 371 error = tsleep(ppb, PPBPRI, "ppbreq", 0); 372 break; 373 374 default: 375 return (EWOULDBLOCK); 376 break; 377 } 378 379 } else { 380 ppb->ppb_owner = dev; 381 382 splx(s); 383 return (0); 384 } 385 } 386 387 return (error); 388} 389 390/* 391 * ppb_release_bus() 392 * 393 * Release the device allocated with ppb_request_dev() 394 */ 395int 396ppb_release_bus(struct ppb_device *dev) 397{ 398 int s; 399 struct ppb_data *ppb = dev->ppb; 400 401 s = splhigh(); 402 if (ppb->ppb_owner != dev) { 403 splx(s); 404 return (EACCES); 405 } 406 407 ppb->ppb_owner = 0; 408 splx(s); 409 410 /* wakeup waiting processes */ 411 wakeup(ppb); 412 413 return (0); 414} 415