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/adb/adb_bus.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) 2008 Nathan Whitehorn
    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 ``AS IS'' AND ANY EXPRESS OR
   17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
   19  * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   20  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
   21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
   22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
   23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
   24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
   25  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   26  *
   27  * $FreeBSD: releng/12.0/sys/dev/adb/adb_bus.c 326255 2017-11-27 14:52:40Z pfg $
   28  */
   29 
   30 #include <sys/cdefs.h>
   31 #include <sys/param.h>
   32 #include <sys/systm.h>
   33 #include <sys/module.h>
   34 #include <sys/bus.h>
   35 #include <sys/conf.h>
   36 #include <sys/kernel.h>
   37 
   38 #include <machine/bus.h>
   39 
   40 #include <vm/vm.h>
   41 #include <vm/pmap.h>
   42 
   43 #include "adb.h"
   44 #include "adbvar.h"
   45 
   46 static int adb_bus_probe(device_t dev);
   47 static int adb_bus_attach(device_t dev);
   48 static int adb_bus_detach(device_t dev);
   49 static void adb_bus_enumerate(void *xdev);
   50 static void adb_probe_nomatch(device_t dev, device_t child);
   51 static int adb_print_child(device_t dev, device_t child);
   52 
   53 static int adb_send_raw_packet_sync(device_t dev, uint8_t to, uint8_t command, uint8_t reg, int len, u_char *data, u_char *reply);
   54 
   55 static char *adb_device_string[] = {
   56         "HOST", "dongle", "keyboard", "mouse", "tablet", "modem", "RESERVED", "misc"
   57 };
   58 
   59 static device_method_t adb_bus_methods[] = {
   60         /* Device interface */
   61         DEVMETHOD(device_probe,         adb_bus_probe),
   62         DEVMETHOD(device_attach,        adb_bus_attach),
   63         DEVMETHOD(device_detach,        adb_bus_detach),
   64         DEVMETHOD(device_shutdown,      bus_generic_shutdown),
   65         DEVMETHOD(device_suspend,       bus_generic_suspend),
   66         DEVMETHOD(device_resume,        bus_generic_resume),
   67 
   68         /* Bus Interface */
   69         DEVMETHOD(bus_probe_nomatch,    adb_probe_nomatch),
   70         DEVMETHOD(bus_print_child,      adb_print_child),
   71 
   72         { 0, 0 },
   73 };
   74 
   75 driver_t adb_driver = {
   76         "adb",
   77         adb_bus_methods,
   78         sizeof(struct adb_softc),
   79 };
   80 
   81 devclass_t adb_devclass;
   82 
   83 static int
   84 adb_bus_probe(device_t dev)
   85 {
   86         device_set_desc(dev, "Apple Desktop Bus");
   87         return (0);
   88 }
   89 
   90 static int
   91 adb_bus_attach(device_t dev)
   92 {
   93         struct adb_softc *sc = device_get_softc(dev);
   94         sc->enum_hook.ich_func = adb_bus_enumerate;
   95         sc->enum_hook.ich_arg = dev;
   96 
   97         /*
   98          * We should wait until interrupts are enabled to try to probe
   99          * the bus. Enumerating the ADB involves receiving packets,
  100          * which works best with interrupts enabled.
  101          */
  102         
  103         if (config_intrhook_establish(&sc->enum_hook) != 0)
  104                 return (ENOMEM);
  105 
  106         return (0);
  107 }
  108         
  109 static void
  110 adb_bus_enumerate(void *xdev)
  111 {
  112         device_t dev = (device_t)xdev;
  113 
  114         struct adb_softc *sc = device_get_softc(dev);
  115         uint8_t i, next_free;
  116         uint16_t r3;
  117 
  118         sc->sc_dev = dev;
  119         sc->parent = device_get_parent(dev);
  120 
  121         sc->packet_reply = 0;
  122         sc->autopoll_mask = 0;
  123         sc->sync_packet = 0xffff;
  124 
  125         /* Initialize devinfo */
  126         for (i = 0; i < 16; i++) {
  127                 sc->devinfo[i].address = i;
  128                 sc->devinfo[i].default_address = 0;
  129         }
  130         
  131         /* Reset ADB bus */
  132         adb_send_raw_packet_sync(dev,0,ADB_COMMAND_BUS_RESET,0,0,NULL,NULL);
  133         DELAY(1500);
  134 
  135         /* Enumerate bus */
  136         next_free = 8;
  137 
  138         for (i = 1; i <= 7; i++) {
  139             int8_t first_relocated = -1;
  140             int reply = 0;
  141 
  142             do {
  143                 reply = adb_send_raw_packet_sync(dev,i,
  144                             ADB_COMMAND_TALK,3,0,NULL,NULL);
  145         
  146                 if (reply) {
  147                         /* If we got a response, relocate to next_free */
  148                         r3 = sc->devinfo[i].register3;
  149                         r3 &= 0xf000;
  150                         r3 |= ((uint16_t)(next_free) & 0x000f) << 8;
  151                         r3 |= 0x00fe;
  152 
  153                         adb_send_raw_packet_sync(dev,i, ADB_COMMAND_LISTEN,3,
  154                             sizeof(uint16_t),(u_char *)(&r3),NULL);
  155 
  156                         adb_send_raw_packet_sync(dev,next_free,
  157                             ADB_COMMAND_TALK,3,0,NULL,NULL);
  158 
  159                         sc->devinfo[next_free].default_address = i;
  160                         if (first_relocated < 0)
  161                                 first_relocated = next_free;
  162 
  163                         next_free++;
  164                 } else if (first_relocated > 0) {
  165                         /* Collisions removed, relocate first device back */
  166 
  167                         r3 = sc->devinfo[i].register3;
  168                         r3 &= 0xf000;
  169                         r3 |= ((uint16_t)(i) & 0x000f) << 8;
  170                         
  171                         adb_send_raw_packet_sync(dev,first_relocated,
  172                             ADB_COMMAND_LISTEN,3,
  173                             sizeof(uint16_t),(u_char *)(&r3),NULL);
  174                         adb_send_raw_packet_sync(dev,i,
  175                             ADB_COMMAND_TALK,3,0,NULL,NULL);
  176 
  177                         sc->devinfo[i].default_address = i;
  178                         sc->devinfo[(int)(first_relocated)].default_address = 0;
  179                         break;
  180                 }
  181             } while (reply);
  182         }
  183 
  184         for (i = 0; i < 16; i++) {
  185                 if (sc->devinfo[i].default_address) {
  186                         sc->children[i] = device_add_child(dev, NULL, -1);
  187                         device_set_ivars(sc->children[i], &sc->devinfo[i]);
  188                 }
  189         }
  190 
  191         bus_generic_attach(dev);
  192 
  193         config_intrhook_disestablish(&sc->enum_hook);
  194 }
  195 
  196 static int adb_bus_detach(device_t dev)
  197 {
  198         return (bus_generic_detach(dev));
  199 }
  200         
  201 
  202 static void
  203 adb_probe_nomatch(device_t dev, device_t child)
  204 {
  205         struct adb_devinfo *dinfo;
  206 
  207         if (bootverbose) {
  208                 dinfo = device_get_ivars(child);
  209 
  210                 device_printf(dev,"ADB %s at device %d (no driver attached)\n",
  211                     adb_device_string[dinfo->default_address],dinfo->address);
  212         }
  213 }
  214 
  215 u_int
  216 adb_receive_raw_packet(device_t dev, u_char status, u_char command, int len, 
  217     u_char *data) 
  218 {
  219         struct adb_softc *sc = device_get_softc(dev);
  220         u_char addr = command >> 4;
  221 
  222         if (len > 0 && (command & 0x0f) == ((ADB_COMMAND_TALK << 2) | 3)) {
  223                 memcpy(&sc->devinfo[addr].register3,data,2);
  224                 sc->devinfo[addr].handler_id = data[1];
  225         }
  226 
  227         if (sc->sync_packet == command)  {
  228                 memcpy(sc->syncreg,data,(len > 8) ? 8 : len);
  229                 atomic_store_rel_int(&sc->packet_reply,len + 1);
  230                 wakeup(sc);
  231         }
  232 
  233         if (sc->children[addr] != NULL) {
  234                 ADB_RECEIVE_PACKET(sc->children[addr],status,
  235                         (command & 0x0f) >> 2,command & 0x03,len,data);
  236         }
  237         
  238         return (0);
  239 }
  240 
  241 static int
  242 adb_print_child(device_t dev, device_t child)
  243 {
  244         struct adb_devinfo *dinfo;
  245         int retval = 0;
  246         
  247         dinfo = device_get_ivars(child);
  248         
  249         retval += bus_print_child_header(dev,child);
  250         printf(" at device %d",dinfo->address);
  251         retval += bus_print_child_footer(dev, child);
  252 
  253         return (retval);
  254 }
  255 
  256 u_int 
  257 adb_send_packet(device_t dev, u_char command, u_char reg, int len, u_char *data)
  258 {
  259         u_char command_byte = 0;
  260         struct adb_devinfo *dinfo;
  261         struct adb_softc *sc;
  262 
  263         sc = device_get_softc(device_get_parent(dev));
  264         dinfo = device_get_ivars(dev);
  265         
  266         command_byte |= dinfo->address << 4;
  267         command_byte |= command << 2;
  268         command_byte |= reg;
  269 
  270         ADB_HB_SEND_RAW_PACKET(sc->parent, command_byte, len, data, 1);
  271 
  272         return (0);
  273 }
  274 
  275 u_int
  276 adb_set_autopoll(device_t dev, u_char enable) 
  277 {
  278         struct adb_devinfo *dinfo;
  279         struct adb_softc *sc;
  280         uint16_t mod = 0;
  281 
  282         sc = device_get_softc(device_get_parent(dev));
  283         dinfo = device_get_ivars(dev);
  284         
  285         mod = enable << dinfo->address;
  286         if (enable) {
  287                 sc->autopoll_mask |= mod;
  288         } else {
  289                 mod = ~mod;
  290                 sc->autopoll_mask &= mod;
  291         }
  292 
  293         ADB_HB_SET_AUTOPOLL_MASK(sc->parent,sc->autopoll_mask);
  294 
  295         return (0);
  296 }
  297 
  298 uint8_t 
  299 adb_get_device_type(device_t dev) 
  300 {
  301         struct adb_devinfo *dinfo;
  302 
  303         dinfo = device_get_ivars(dev);
  304         return (dinfo->default_address);
  305 }
  306 
  307 uint8_t 
  308 adb_get_device_handler(device_t dev) 
  309 {
  310         struct adb_devinfo *dinfo;
  311 
  312         dinfo = device_get_ivars(dev);
  313         return (dinfo->handler_id);
  314 }
  315 
  316 static int 
  317 adb_send_raw_packet_sync(device_t dev, uint8_t to, uint8_t command, 
  318     uint8_t reg, int len, u_char *data, u_char *reply) 
  319 {
  320         u_char command_byte = 0;
  321         struct adb_softc *sc;
  322         int result = -1;
  323         int i = 1;
  324 
  325         sc = device_get_softc(dev);
  326         
  327         command_byte |= to << 4;
  328         command_byte |= command << 2;
  329         command_byte |= reg;
  330 
  331         /* Wait if someone else has a synchronous request pending */
  332         while (!atomic_cmpset_int(&sc->sync_packet, 0xffff, command_byte))
  333                 tsleep(sc, 0, "ADB sync", hz/10);
  334 
  335         sc->packet_reply = 0;
  336         sc->sync_packet = command_byte;
  337 
  338         ADB_HB_SEND_RAW_PACKET(sc->parent, command_byte, len, data, 1);
  339 
  340         while (!atomic_fetchadd_int(&sc->packet_reply,0)) {
  341                 /*
  342                  * Maybe the command got lost? Try resending and polling the 
  343                  * controller.
  344                  */
  345                 if (i % 40 == 0)
  346                         ADB_HB_SEND_RAW_PACKET(sc->parent, command_byte, 
  347                             len, data, 1);
  348 
  349                 tsleep(sc, 0, "ADB sync", hz/10);
  350                 i++;
  351         }
  352 
  353         result = sc->packet_reply - 1;
  354 
  355         if (reply != NULL && result > 0)
  356                 memcpy(reply,sc->syncreg,result);
  357 
  358         /* Clear packet sync */
  359         sc->packet_reply = 0;
  360 
  361         /*
  362          * We can't match a value beyond 8 bits, so set sync_packet to 
  363          * 0xffff to avoid collisions.
  364          */
  365         atomic_set_int(&sc->sync_packet, 0xffff); 
  366 
  367         return (result);
  368 }
  369 
  370 uint8_t 
  371 adb_set_device_handler(device_t dev, uint8_t newhandler) 
  372 {
  373         struct adb_softc *sc;
  374         struct adb_devinfo *dinfo;
  375         uint16_t newr3;
  376 
  377         dinfo = device_get_ivars(dev);
  378         sc = device_get_softc(device_get_parent(dev));
  379 
  380         newr3 = dinfo->register3 & 0xff00;
  381         newr3 |= (uint16_t)(newhandler);
  382 
  383         adb_send_raw_packet_sync(sc->sc_dev,dinfo->address, ADB_COMMAND_LISTEN, 
  384             3, sizeof(uint16_t), (u_char *)(&newr3), NULL);
  385         adb_send_raw_packet_sync(sc->sc_dev,dinfo->address, 
  386             ADB_COMMAND_TALK, 3, 0, NULL, NULL);
  387 
  388         return (dinfo->handler_id);
  389 }
  390 
  391 size_t 
  392 adb_read_register(device_t dev, u_char reg, void *data) 
  393 {
  394         struct adb_softc *sc;
  395         struct adb_devinfo *dinfo;
  396         size_t result;
  397 
  398         dinfo = device_get_ivars(dev);
  399         sc = device_get_softc(device_get_parent(dev));
  400 
  401         result = adb_send_raw_packet_sync(sc->sc_dev,dinfo->address,
  402                    ADB_COMMAND_TALK, reg, 0, NULL, data);
  403 
  404         return (result);
  405 }
  406 
  407 size_t 
  408 adb_write_register(device_t dev, u_char reg, size_t len, void *data) 
  409 {
  410         struct adb_softc *sc;
  411         struct adb_devinfo *dinfo;
  412         size_t result;
  413         
  414         dinfo = device_get_ivars(dev);
  415         sc = device_get_softc(device_get_parent(dev));
  416         
  417         result = adb_send_raw_packet_sync(sc->sc_dev,dinfo->address,
  418                    ADB_COMMAND_LISTEN, reg, len, (u_char *)data, NULL);
  419         
  420         result = adb_send_raw_packet_sync(sc->sc_dev,dinfo->address,
  421                    ADB_COMMAND_TALK, reg, 0, NULL, NULL);
  422 
  423         return (result);
  424 }

Cache object: 0903d59b20a54fd0ecf346538da236b0


[ 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.