The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/dev/ppbus/ppbconf.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
    3  *
    4  * Copyright (c) 1997, 1998, 1999 Nicolas Souchu
    5  * All rights reserved.
    6  *
    7  * Redistribution and use in source and binary forms, with or without
    8  * modification, are permitted provided that the following conditions
    9  * are met:
   10  * 1. Redistributions of source code must retain the above copyright
   11  *    notice, this list of conditions and the following disclaimer.
   12  * 2. Redistributions in binary form must reproduce the above copyright
   13  *    notice, this list of conditions and the following disclaimer in the
   14  *    documentation and/or other materials provided with the distribution.
   15  *
   16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   26  * SUCH DAMAGE.
   27  *
   28  *
   29  */
   30 
   31 #include <sys/cdefs.h>
   32 __FBSDID("$FreeBSD$");
   33 #include "opt_ppb_1284.h"
   34 
   35 #include <sys/param.h>
   36 #include <sys/systm.h>
   37 #include <sys/kernel.h>
   38 #include <sys/lock.h>
   39 #include <sys/module.h>
   40 #include <sys/mutex.h>
   41 #include <sys/bus.h>
   42 #include <sys/malloc.h>
   43 #include <sys/rman.h>
   44 
   45 #include <machine/resource.h>
   46 
   47 #include <dev/ppbus/ppbconf.h>
   48 #include <dev/ppbus/ppb_1284.h>
   49 
   50 #include "ppbus_if.h"
   51 
   52 #define DEVTOSOFTC(dev) ((struct ppb_data *)device_get_softc(dev))
   53 
   54 static MALLOC_DEFINE(M_PPBUSDEV, "ppbusdev", "Parallel Port bus device");
   55 
   56 static int      ppbus_intr(void *arg);
   57 
   58 /*
   59  * Device methods
   60  */
   61 
   62 static int
   63 ppbus_print_child(device_t bus, device_t dev)
   64 {
   65         struct ppb_device *ppbdev;
   66         int retval;
   67 
   68         retval = bus_print_child_header(bus, dev);
   69 
   70         ppbdev = (struct ppb_device *)device_get_ivars(dev);
   71 
   72         if (ppbdev->flags != 0)
   73                 retval += printf(" flags 0x%x", ppbdev->flags);
   74 
   75         retval += bus_print_child_footer(bus, dev);
   76 
   77         return (retval);
   78 }
   79 
   80 static int
   81 ppbus_probe(device_t dev)
   82 {
   83         device_set_desc(dev, "Parallel port bus");
   84 
   85         return (0);
   86 }
   87 
   88 /*
   89  * ppbus_add_child()
   90  *
   91  * Add a ppbus device, allocate/initialize the ivars
   92  */
   93 static device_t
   94 ppbus_add_child(device_t dev, u_int order, const char *name, int unit)
   95 {
   96         struct ppb_device *ppbdev;
   97         device_t child;
   98 
   99         /* allocate ivars for the new ppbus child */
  100         ppbdev = malloc(sizeof(struct ppb_device), M_PPBUSDEV,
  101                 M_NOWAIT | M_ZERO);
  102         if (!ppbdev)
  103                 return (NULL);
  104 
  105         /* initialize the ivars */
  106         ppbdev->name = name;
  107 
  108         /* add the device as a child to the ppbus bus with the allocated
  109          * ivars */
  110         child = device_add_child_ordered(dev, order, name, unit);
  111         device_set_ivars(child, ppbdev);
  112 
  113         return (child);
  114 }
  115 
  116 static int
  117 ppbus_read_ivar(device_t bus, device_t dev, int index, uintptr_t* val)
  118 {
  119 
  120         switch (index) {
  121         case PPBUS_IVAR_MODE:
  122                 /* XXX yet device mode = ppbus mode = chipset mode */
  123                 *val = (u_long)ppb_get_mode(bus);
  124                 break;
  125         default:
  126                 return (ENOENT);
  127         }
  128 
  129         return (0);
  130 }
  131 
  132 static int
  133 ppbus_write_ivar(device_t bus, device_t dev, int index, uintptr_t val)
  134 {
  135 
  136         switch (index) {
  137         case PPBUS_IVAR_MODE:
  138                 /* XXX yet device mode = ppbus mode = chipset mode */
  139                 ppb_set_mode(bus, val);
  140                 break;
  141         default:
  142                 return (ENOENT);
  143         }
  144 
  145         return (0);
  146 }
  147 
  148 #define PPB_PNP_PRINTER         0
  149 #define PPB_PNP_MODEM           1
  150 #define PPB_PNP_NET             2
  151 #define PPB_PNP_HDC             3
  152 #define PPB_PNP_PCMCIA          4
  153 #define PPB_PNP_MEDIA           5
  154 #define PPB_PNP_FDC             6
  155 #define PPB_PNP_PORTS           7
  156 #define PPB_PNP_SCANNER         8
  157 #define PPB_PNP_DIGICAM         9
  158 
  159 #ifndef DONTPROBE_1284
  160 
  161 static char *pnp_tokens[] = {
  162         "PRINTER", "MODEM", "NET", "HDC", "PCMCIA", "MEDIA",
  163         "FDC", "PORTS", "SCANNER", "DIGICAM", "", NULL };
  164 
  165 #if 0
  166 static char *pnp_classes[] = {
  167         "printer", "modem", "network device",
  168         "hard disk", "PCMCIA", "multimedia device",
  169         "floppy disk", "ports", "scanner",
  170         "digital camera", "unknown device", NULL };
  171 #endif
  172 
  173 /*
  174  * search_token()
  175  *
  176  * Search the first occurrence of a token within a string
  177  */
  178 static char *
  179 search_token(char *str, int slen, char *token)
  180 {
  181         int tlen, i;
  182 
  183 #define UNKNOWN_LENGTH  -1
  184 
  185         if (slen == UNKNOWN_LENGTH)
  186                 /* get string's length */
  187                 slen = strlen(str);
  188 
  189         /* get token's length */
  190         tlen = strlen(token);
  191         if (tlen == 0)
  192                 return (str);
  193 
  194         for (i = 0; i <= slen-tlen; i++) {
  195                 if (strncmp(str + i, token, tlen) == 0)
  196                         return (&str[i]);
  197         }
  198 
  199         return (NULL);
  200 }
  201 
  202 /*
  203  * ppb_pnp_detect()
  204  *
  205  * Returns the class id. of the peripherial, -1 otherwise
  206  */
  207 static int
  208 ppb_pnp_detect(device_t bus)
  209 {
  210         char *token, *class = NULL;
  211         int i, len, error;
  212         int class_id = -1;
  213         char str[PPB_PnP_STRING_SIZE+1];
  214 
  215         device_printf(bus, "Probing for PnP devices:\n");
  216 
  217         if ((error = ppb_1284_read_id(bus, PPB_NIBBLE, str,
  218                                         PPB_PnP_STRING_SIZE, &len)))
  219                 goto end_detect;
  220 
  221 #ifdef DEBUG_1284
  222         device_printf(bus, "<PnP> %d characters: ", len);
  223         for (i = 0; i < len; i++)
  224                 printf("%c(0x%x) ", str[i], str[i]);
  225         printf("\n");
  226 #endif
  227 
  228         /* replace ';' characters by '\0' */
  229         for (i = 0; i < len; i++)
  230                 str[i] = (str[i] == ';') ? '\0' : str[i];
  231 
  232         if ((token = search_token(str, len, "MFG")) != NULL ||
  233                 (token = search_token(str, len, "MANUFACTURER")) != NULL)
  234                 device_printf(bus, "<%s",
  235                         search_token(token, UNKNOWN_LENGTH, ":") + 1);
  236         else
  237                 device_printf(bus, "<unknown");
  238 
  239         if ((token = search_token(str, len, "MDL")) != NULL ||
  240                 (token = search_token(str, len, "MODEL")) != NULL)
  241                 printf(" %s",
  242                         search_token(token, UNKNOWN_LENGTH, ":") + 1);
  243         else
  244                 printf(" unknown");
  245 
  246         if ((token = search_token(str, len, "VER")) != NULL)
  247                 printf("/%s",
  248                         search_token(token, UNKNOWN_LENGTH, ":") + 1);
  249 
  250         if ((token = search_token(str, len, "REV")) != NULL)
  251                 printf(".%s",
  252                         search_token(token, UNKNOWN_LENGTH, ":") + 1);
  253 
  254         printf(">");
  255 
  256         if ((token = search_token(str, len, "CLS")) != NULL) {
  257                 class = search_token(token, UNKNOWN_LENGTH, ":") + 1;
  258                 printf(" %s", class);
  259         }
  260 
  261         if ((token = search_token(str, len, "CMD")) != NULL ||
  262                 (token = search_token(str, len, "COMMAND")) != NULL)
  263                 printf(" %s",
  264                         search_token(token, UNKNOWN_LENGTH, ":") + 1);
  265 
  266         printf("\n");
  267 
  268         if (class)
  269                 /* identify class ident */
  270                 for (i = 0; pnp_tokens[i] != NULL; i++) {
  271                         if (search_token(class, len, pnp_tokens[i]) != NULL) {
  272                                 class_id = i;
  273                                 goto end_detect;
  274                         }
  275                 }
  276 
  277         class_id = PPB_PnP_UNKNOWN;
  278 
  279 end_detect:
  280         return (class_id);
  281 }
  282 
  283 /*
  284  * ppb_scan_bus()
  285  *
  286  * Scan the ppbus for IEEE1284 compliant devices
  287  */
  288 static int
  289 ppb_scan_bus(device_t bus)
  290 {
  291         struct ppb_data * ppb = (struct ppb_data *)device_get_softc(bus);
  292         int error = 0;
  293 
  294         /* try all IEEE1284 modes, for one device only
  295          *
  296          * XXX We should implement the IEEE1284.3 standard to detect
  297          * daisy chained devices
  298          */
  299 
  300         error = ppb_1284_negociate(bus, PPB_NIBBLE, PPB_REQUEST_ID);
  301 
  302         if ((ppb->state == PPB_ERROR) && (ppb->error == PPB_NOT_IEEE1284))
  303                 goto end_scan;
  304 
  305         ppb_1284_terminate(bus);
  306 
  307         device_printf(bus, "IEEE1284 device found ");
  308 
  309         if (!(error = ppb_1284_negociate(bus, PPB_NIBBLE, 0))) {
  310                 printf("/NIBBLE");
  311                 ppb_1284_terminate(bus);
  312         }
  313 
  314         if (!(error = ppb_1284_negociate(bus, PPB_PS2, 0))) {
  315                 printf("/PS2");
  316                 ppb_1284_terminate(bus);
  317         }
  318 
  319         if (!(error = ppb_1284_negociate(bus, PPB_ECP, 0))) {
  320                 printf("/ECP");
  321                 ppb_1284_terminate(bus);
  322         }
  323 
  324         if (!(error = ppb_1284_negociate(bus, PPB_ECP, PPB_USE_RLE))) {
  325                 printf("/ECP_RLE");
  326                 ppb_1284_terminate(bus);
  327         }
  328 
  329         if (!(error = ppb_1284_negociate(bus, PPB_EPP, 0))) {
  330                 printf("/EPP");
  331                 ppb_1284_terminate(bus);
  332         }
  333 
  334         /* try more IEEE1284 modes */
  335         if (bootverbose) {
  336                 if (!(error = ppb_1284_negociate(bus, PPB_NIBBLE,
  337                                 PPB_REQUEST_ID))) {
  338                         printf("/NIBBLE_ID");
  339                         ppb_1284_terminate(bus);
  340                 }
  341 
  342                 if (!(error = ppb_1284_negociate(bus, PPB_PS2,
  343                                 PPB_REQUEST_ID))) {
  344                         printf("/PS2_ID");
  345                         ppb_1284_terminate(bus);
  346                 }
  347 
  348                 if (!(error = ppb_1284_negociate(bus, PPB_ECP,
  349                                 PPB_REQUEST_ID))) {
  350                         printf("/ECP_ID");
  351                         ppb_1284_terminate(bus);
  352                 }
  353 
  354                 if (!(error = ppb_1284_negociate(bus, PPB_ECP,
  355                                 PPB_REQUEST_ID | PPB_USE_RLE))) {
  356                         printf("/ECP_RLE_ID");
  357                         ppb_1284_terminate(bus);
  358                 }
  359 
  360                 if (!(error = ppb_1284_negociate(bus, PPB_COMPATIBLE,
  361                                 PPB_EXTENSIBILITY_LINK))) {
  362                         printf("/Extensibility Link");
  363                         ppb_1284_terminate(bus);
  364                 }
  365         }
  366 
  367         printf("\n");
  368 
  369         /* detect PnP devices */
  370         ppb->class_id = ppb_pnp_detect(bus);
  371 
  372         return (0);
  373 
  374 end_scan:
  375         return (error);
  376 }
  377 
  378 #endif /* !DONTPROBE_1284 */
  379 
  380 static int
  381 ppbus_attach(device_t dev)
  382 {
  383         struct ppb_data *ppb = device_get_softc(dev);
  384         int error, rid;
  385 
  386         error = BUS_READ_IVAR(device_get_parent(dev), dev, PPC_IVAR_LOCK,
  387             (uintptr_t *)&ppb->ppc_lock);
  388         if (error) {
  389                 device_printf(dev, "Unable to fetch parent's lock\n");
  390                 return (error);
  391         }
  392 
  393         rid = 0;
  394         ppb->ppc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
  395             RF_SHAREABLE);
  396         if (ppb->ppc_irq_res != NULL) {
  397                 mtx_lock(ppb->ppc_lock);
  398                 error = BUS_WRITE_IVAR(device_get_parent(dev), dev,
  399                     PPC_IVAR_INTR_HANDLER, (uintptr_t)&ppbus_intr);
  400                 mtx_unlock(ppb->ppc_lock);
  401                 if (error) {
  402                         device_printf(dev, "Unable to set interrupt handler\n");
  403                         return (error);
  404                 }
  405         }
  406 
  407         /* Locate our children */
  408         bus_generic_probe(dev);
  409 
  410 #ifndef DONTPROBE_1284
  411         /* detect IEEE1284 compliant devices */
  412         mtx_lock(ppb->ppc_lock);
  413         ppb_scan_bus(dev);
  414         mtx_unlock(ppb->ppc_lock);
  415 #endif /* !DONTPROBE_1284 */
  416 
  417         /* launch attachment of the added children */
  418         bus_generic_attach(dev);
  419 
  420         return (0);
  421 }
  422 
  423 static int
  424 ppbus_detach(device_t dev)
  425 {
  426         int error;
  427 
  428         error = bus_generic_detach(dev);
  429         if (error)
  430                 return (error);
  431 
  432         /* detach & delete all children */
  433         device_delete_children(dev);
  434 
  435         return (0);
  436 }
  437 
  438 static int
  439 ppbus_intr(void *arg)
  440 {
  441         struct ppb_device *ppbdev;
  442         struct ppb_data *ppb = arg;
  443 
  444         mtx_assert(ppb->ppc_lock, MA_OWNED);
  445         if (ppb->ppb_owner == NULL)
  446                 return (ENOENT);
  447 
  448         ppbdev = device_get_ivars(ppb->ppb_owner);
  449         if (ppbdev->intr_hook == NULL)
  450                 return (ENOENT);
  451 
  452         ppbdev->intr_hook(ppbdev->intr_arg);
  453         return (0);
  454 }
  455 
  456 static int
  457 ppbus_setup_intr(device_t bus, device_t child, struct resource *r, int flags,
  458     driver_filter_t *filt, void (*ihand)(void *), void *arg, void **cookiep)
  459 {
  460         struct ppb_device *ppbdev = device_get_ivars(child);
  461         struct ppb_data *ppb = DEVTOSOFTC(bus);
  462 
  463         /* We do not support filters. */
  464         if (filt != NULL || ihand == NULL)
  465                 return (EINVAL);
  466 
  467         /* Can only attach handlers to the parent device's resource. */
  468         if (ppb->ppc_irq_res != r)
  469                 return (EINVAL);
  470 
  471         mtx_lock(ppb->ppc_lock);
  472         ppbdev->intr_hook = ihand;
  473         ppbdev->intr_arg = arg;
  474         *cookiep = ppbdev;
  475         mtx_unlock(ppb->ppc_lock);
  476 
  477         return (0);
  478 }
  479 
  480 static int
  481 ppbus_teardown_intr(device_t bus, device_t child, struct resource *r, void *ih)
  482 {
  483         struct ppb_device *ppbdev = device_get_ivars(child);
  484         struct ppb_data *ppb = DEVTOSOFTC(bus);
  485 
  486         mtx_lock(ppb->ppc_lock);
  487         if (ppbdev != ih || ppb->ppc_irq_res != r) {
  488                 mtx_unlock(ppb->ppc_lock);
  489                 return (EINVAL);
  490         }
  491 
  492         ppbdev->intr_hook = NULL;
  493         mtx_unlock(ppb->ppc_lock);
  494 
  495         return (0);
  496 }
  497 
  498 /*
  499  * ppb_request_bus()
  500  *
  501  * Allocate the device to perform transfers.
  502  *
  503  * how  : PPB_WAIT or PPB_DONTWAIT
  504  */
  505 int
  506 ppb_request_bus(device_t bus, device_t dev, int how)
  507 {
  508         struct ppb_data *ppb = DEVTOSOFTC(bus);
  509         struct ppb_device *ppbdev = (struct ppb_device *)device_get_ivars(dev);
  510         int error = 0;
  511 
  512         mtx_assert(ppb->ppc_lock, MA_OWNED);
  513         while (!error) {
  514                 if (ppb->ppb_owner) {
  515                         switch (how) {
  516                         case PPB_WAIT | PPB_INTR:
  517                                 error = mtx_sleep(ppb, ppb->ppc_lock,
  518                                     PPBPRI | PCATCH, "ppbreq", 0);
  519                                 break;
  520 
  521                         case PPB_WAIT | PPB_NOINTR:
  522                                 error = mtx_sleep(ppb, ppb->ppc_lock, PPBPRI,
  523                                     "ppbreq", 0);
  524                                 break;
  525 
  526                         default:
  527                                 return (EWOULDBLOCK);
  528                         }
  529 
  530                 } else {
  531                         ppb->ppb_owner = dev;
  532 
  533                         /* restore the context of the device
  534                          * The first time, ctx.valid is certainly false
  535                          * then do not change anything. This is useful for
  536                          * drivers that do not set there operating mode
  537                          * during attachement
  538                          */
  539                         if (ppbdev->ctx.valid)
  540                                 ppb_set_mode(bus, ppbdev->ctx.mode);
  541 
  542                         return (0);
  543                 }
  544         }
  545 
  546         return (error);
  547 }
  548 
  549 /*
  550  * ppb_release_bus()
  551  *
  552  * Release the device allocated with ppb_request_bus()
  553  */
  554 int
  555 ppb_release_bus(device_t bus, device_t dev)
  556 {
  557         struct ppb_data *ppb = DEVTOSOFTC(bus);
  558         struct ppb_device *ppbdev = (struct ppb_device *)device_get_ivars(dev);
  559 
  560         mtx_assert(ppb->ppc_lock, MA_OWNED);
  561         if (ppb->ppb_owner != dev)
  562                 return (EACCES);
  563 
  564         /* save the context of the device */
  565         ppbdev->ctx.mode = ppb_get_mode(bus);
  566 
  567         /* ok, now the context of the device is valid */
  568         ppbdev->ctx.valid = 1;
  569 
  570         ppb->ppb_owner = 0;
  571 
  572         /* wakeup waiting processes */
  573         wakeup(ppb);
  574 
  575         return (0);
  576 }
  577 
  578 static device_method_t ppbus_methods[] = {
  579         /* device interface */
  580         DEVMETHOD(device_probe,         ppbus_probe),
  581         DEVMETHOD(device_attach,        ppbus_attach),
  582         DEVMETHOD(device_detach,        ppbus_detach),
  583 
  584         /* bus interface */
  585         DEVMETHOD(bus_add_child,        ppbus_add_child),
  586         DEVMETHOD(bus_print_child,      ppbus_print_child),
  587         DEVMETHOD(bus_read_ivar,        ppbus_read_ivar),
  588         DEVMETHOD(bus_write_ivar,       ppbus_write_ivar),
  589         DEVMETHOD(bus_setup_intr,       ppbus_setup_intr),
  590         DEVMETHOD(bus_teardown_intr,    ppbus_teardown_intr),
  591         DEVMETHOD(bus_alloc_resource,   bus_generic_alloc_resource),
  592         DEVMETHOD(bus_release_resource, bus_generic_release_resource),
  593         { 0, 0 }
  594 };
  595 
  596 static driver_t ppbus_driver = {
  597         "ppbus",
  598         ppbus_methods,
  599         sizeof(struct ppb_data),
  600 };
  601 
  602 DRIVER_MODULE(ppbus, ppc, ppbus_driver, 0, 0);

Cache object: 3aba86f4e2aac2d2d25d441a95208dd6


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.