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