The following FAQ was created by developers at SunSoft, Inc. to 
answer many of the questions faced by those of you who may be porting
existing device drivers to Solaris 2.x, or creating new ones.

There is a wealth of information here, so I thought I'd post it to
this newsgroup.  This FAQ is also available via anonymous FTP from
opcom.sun.ca.

A much deserved thanks to the maintainer of this FAQ, and those on
the internal Sun ddi developers alias from whence this FAQ was born.
[I'd love to name them all here, but they'd hate me forever if I did]

Brian.
---
Solaris 2 Migration Support Center,
Toronto, Ontario, Canada


ddi-drivers Frequently-asked Questions           1.10     93/07/13 SMI

Frequently-asked questions about SunOS 5.x device drivers.

If your question isn't here, please try (at *least*)
    man -s 9e Intro
    man -s 9f Intro
    man -s 9s Intro

and looking in the appropriate manuals listed in the Bibliography below.

Updates and additions are welcomed, and should be sent to solaris2@Sun.COM,
where they will be evaluated and forwarded to the FAQ maintainer.

This FAQ is available via anonymous FTP from the Operation Commitment
server opcom.sun.ca [142.77.1.61], in /pub/drivers/ddi-faq.  You can
also get it via email by sending a mail message to ftp@opcom.sun.ca,
with the subject 'get', and the body containing the single line:

file 	/pub/drivers/ddi-faq

To find out more about the server, send a message with the subject 'help'.


New in this version:
	Nothing yet.

Contents:
  1. What is the Solaris 2.x SPARC DDI/DKI?
  2. What does it mean if a driver is _not_ Solaris DDI-compliant?
  3. What do I do if I find a bug/omission in the DDI/DKI or the documentation?
  4. What is a nexus driver and how do I write one?
  5. What are the definitions of, and the relationships between instances,device numbers, minor nodes, major numbers, and minor numbers?
  6. Should I acquire a mutex around _any_ manipulations of a state structure element?
  7. Can, and should, I only have one mutex for each instance?
  8. What structures can my driver access?
  9. Can I access the user or proc structure?
  10. I need to prevent user memory from being paged out, so my driver can do DMA to it. Can I call as_fault()?
  11. Is there a way to send signals to user threads from a device driver?
  12. Why is there no man page for printf() or panic()?
  13. What is a layered driver?
  14. Is there a SunOS 5.x replacement for the SunOS 4.x devinfo command?
  15. What does it mean when prtconf(1M) says "(no driver)"?
  16. What ddi_iblock_cookie_t should I pass mutex_init(9F) for a mutex used with a timeout(9F) routine?
  17. My driver panics the system in ddi_create_minor_node(9F). Is this known bug in the kernel?
  18. My driver needs to keep track of all the processes using it. How can I do this?
  19. I used to pass flags to my driver with the 'flags' keyword in SunOS 4.x. How do I do that in SunOS 5.x?
  20. Do self-identifying devices require a configuration file?
  21. My fcode driver incorrectly specifies the devices "reg" property or is missing some of the values of the "reg" property. How can I fix this in SunOS 5.x?
  22. My fcode driver only creates a "name" property and doesn't create any "reg" property at all? Can I use "registers" wildcarding?
  23. What compiler can I use to compile device drivers?
  24. Can I write a device driver in C++?
  25. I'm using the ddi_soft_state routines to allocate per-instance state structures. How can I access unit n's state structure in kadb?
  26. Periodically, in situations where we have only 1 "xx" card in a machine, we get /dev/xx1 created instead of /dev/xx0. boot -r does not help. At one point we had 2 "xx" boards in this machine, and we've removed one of those. Why isn't the remaining card now /dev/xx0?
  27. What does 'panic: Lock Held and only one CPU' mean?
  28. I want to have two drivers in one module. It doesn't seem to work by putting multiple entries in the modlinkage structure.
  29. How does a driver retrieve the hostid?
  30. What is a 'watchdog reset'?
  31. What is the maximum size of DVMA space?
  32. What is the maximum size that bp_mapin(9F) can map?
  33. My driver needs to do special things based on the machine type, which it determines from the 'cputype' variable. Is this the right thing to do?
  34. Is there a naming convention for I/O controls?



1 What is the Solaris 2.x SPARC DDI/DKI?

    In System V Release 4 (SVR4), the interface between device drivers and 
    the rest of the UNIX kernel has been standardized and documented in 
    section 9 of the SunOS 5.2 Reference Manual. The reference manual 
    documents driver entry points, driver callable functions and kernel 
    data structures used by device drivers. These interfaces, known 
    collectively as the Device Driver Interface/Driver-Kernel Interface 
    (DDI/DKI) are divided into three groups.

        The DKI-only interfaces are specific to SVR4. These interfaces 
        may not be supported in future releases of System V. Only two 
        interfaces belong to this group: segmap(9E) and hat_getkpfnum(9F).

        The SPARC DDI interfaces are architecture-specific.

        The remaining interfaces, known as DDI/DKI, are 
        architecture-independent and will be supported throughout System V.

    For more information about the DDI/DKI, see Intro(9) in the SunOS 5.2
    Reference Manual.

    The Solaris 2.x SPARC DDI/DKI, like its SVR4 counterpart, is intended to
    standardize and document all interfaces between device drivers and the 
    SunOS kernel. In addition, the Solaris 2.x SPARC DDI/DKI is designed to 
    allow source compatibility for drivers on any SunOS 5.x based machine, 
    regardless of the machine architecture. It is also intended to provide 
    binary compatibility for drivers running on any SPARC architecture 
    based machine that supports the Solaris 2.x SPARC DDI/DKI, regardless of 
    the specific machine architecture.  Drivers that only use kernel 
    facilities which are part of the Solaris 2.x SPARC DDI/DKI are known 
    as "Solaris 2.x SPARC DDI/DKI-compliant device drivers", or
    "Solaris DDI-compliant" device drivers.

    Note - SunSoft's implementation of the Solaris 2.x SPARC DDI/DKI was
    designed to provide binary compatibility for third-party device drivers 
    across currently supported hardware platforms across minor releases of 
    the operating system. Currently there is no validation process for 
    supported hardware platforms.
 
    However, unforeseen technical issues may force changes to the binary 
    interface of the Solaris 2.x SPARC DDI/DKI. SunSoft cannot therefore 
    promise or in any way assure that Solaris 2.x SPARC DDI/DKI-compliant 
    device drivers will continue to operate correctly on future releases of 
    Solaris.
  
    Furthermore, future releases may contain additions to the Solaris 2.x 
    SPARC DDI/DKI to support future platforms. At that time, device drivers 
    wishing to operate across the new set of supported platforms may 
    require these additions.


2 What does it mean if a driver is _not_ Solaris DDI-compliant?

    It simply means that your driver may not work (or compile) in later
    releases. Functions and structures not documented in the DDI/DKI are
    subject to change/removal at any time.


3 What do I do if I find a bug/omission in the DDI/DKI or the 
  documentation?

    Please file bugs and requests for enhancement (RFEs). We want to 
    improve things, and have been cleaning up the man pages and the 
    WDD to this end. The categories and subcategories to use are:

        kernel/ddi for DDI/DKI framework bugs

        kernel/driver for device driver bugs

        doc/userman for bugs in the 9E/9F/9S man pages 

        doc/devicedriver for bugs in the Writing Device Drivers manual

        doc/streams for bugs in the STREAMS Programmers Guide

    Please add 'ddi' to the keywords so we can track them easier.


4 What is a nexus driver and how do I write one?
    
    Nexus drivers handle bus-specific operations. 

    You cannot currently write one, since they are not documented. The
    reason they are not documented is that they are in a continual
    state of change.


5 What are the definitions of, and the relationships between instances,
  device numbers, minor nodes, major numbers, and minor numbers?

    "Instance numbers" are part of the 'shorthand' name for dev_info nodes
    owned by a particular driver.  dev_info nodes are usually associated
    one-to-one with a particular hardware instance: the two 85C30 chips
    in a SPARC station each have a dev_info node called 'zs' - instance
    zero and instance one. Instance numbers are assigned (and owned) by 
    the system, and returned to the driver by ddi_get_instance(9F). Instance
    numbers and 'shorthand names' are not normally visible outside the kernel.

        host% ls -l /devices/pseudo/mm:zero
        crw-rw-rw-  1 root      13,  12 Oct 11 02:20 /devices/pseudo/mm:zero

    "minor nodes" consist of all the information that is held about a
    user-accessible device i.e. the minor number (exported by the special
    file), part of the name of the special file (the "zero" in "mm:zero",
    minor number 12 of the 'mm' driver), and whether it is block or
    character.  See ddi_create_minor_node(9F) for how to build one.

    "Device numbers" are contained in an opaque type 'dev_t'. They consist
    of a major number and a minor number.

    The "major number" is an internal magic number used by the system to 
    bind special files (such as /devices/pseudo/mm:zero) to device drivers. 
    In the example above, the 'major number' is 13.  There is usually no 
    reason for the driver to care what its major number is - they are 
    assigned by the system.

    The "minor number" is a component of the device number. The meaning
    of it is entirely up to the device driver, and is associated with
    a special file by calling ddi_create_minor_node(9F). In the above
    example, the minor number is 12. Minor names are associated with
    minor numbers, as the part following the colon in the name of
    the special file ('zero' in this case).


6 Should I acquire a mutex around _any_ manipulations of a state 
  structure element?

    It depends.  If it is an element you only initialize in attach then 
    -read- everywhere else, then there is no need.  If it is an element 
    of the data structure thats shared-writable, then yes.

    In general though, you should probably start by protecting everything,
    and relax the protection as you get more confident and a better 
    understanding of your driver and the multithreaded world.


7 Can, and should, I only have one mutex for each instance?

    You can, and it often makes sense, but you do not have to.  
    The general rule is not to overdo things.


8 What structures can my driver access?

    Only those defined in section 9S of the manual pages. Note that these
    structures may change size, so you should never declare one. For example,
    if you need a buf(9S) structure, declare a pointer and dynamically 
    allocate one with getrbuf(9F). 


9 Can I access the user or proc structure?

    No.


10 I need to prevent user memory from being paged out, so my driver
  can do DMA to it. Can I call as_fault()?

    No. as_fault() is not part of the DDI/DKI. What it does and how
    it does it may change more or less arbitrarily from minor release
    to minor release.  The only DDI-compliant way to lock down memory
    is with physio(9F).


11 Is there a way to send signals to user threads from a device driver?

    Currently, only STREAMS drivers can send signals. SIGPOLL can be sent
    to the Stream head, which will deliver it to any processes that have
    registered an interest with the I_SETSIG streamio(7) I/O control. Any
    other signal goes to the process group, and then only if the
    Stream represents a terminal device. The 4.x psignal() routine 
    still exists, but is not part of the DDI/DKI. 

    If you really, really, really, want to send signals from a 
    non-STREAMS driver, psignal may actually work - try

        psignal(curproc, SIGKILL);

    with the usual caveats about using undocumented interfaces i.e. subject
    to arbitrary change or removal without notice etc. from minor release
    to minor release.  (Actually, if your customer cares, add a customer
    call entry to bugid 1103956.)

    Note that to send signals from interrupt handlers you have to get a 
    reliable handle on the process that can be used even if the process 
    exits before the handler sends the signal, which is one reason
    psignal() is not documented.  The comments on the bug (1103956) 
    explain about proc_ref, proc_signal, and proc_unref.
    You can also look at the chpoll(9E) entry point. This is a documented, 
    DDI-compliant way for non-STREAMS drivers to implement 
    polling. In many cases, this is what you really want.  Applications 
    can express an interest in a set of file descriptors and be notified by 
    the driver that something has changed.  See the man pages for 
    chpoll(9E) and poll(2).  poll(2) replaces the SunOS 4.x select(2) system.


12 Why is there no man page for printf() or panic()?

    Drivers should not normally print messages. However, though printf()
    exists in the kernel, if messages need to be printed to the console
    (and/or the kernel message buffer) drivers should use cmn_err(9F). 
    There is no equivalent to the SunOS 4.x uprintf() routine.

    cmn_err() also allows the driver to panic the system. It is unlikely
    that drivers will need to do so, however.


13 What is a layered driver?

    A layered driver is a driver that calls another drivers routines.
    An example might be a pseudo device driver that provides a pseudo
    disk consisting of several real disks. This driver translates
    strategy(9E) requests into calls to the real disk driver strategy(9E)
    routines.

    There is no way to write a Solaris DDI-compliant driver that performs 
    this layering. However, drivers must be aware that they may be used
    by a layered driver (such as in open(9E), close(9E), and ioctl(9E)).


14 Is there a SunOS 5.x replacement for the SunOS 4.x devinfo command?

    Similar functionality is provided by prtconf(1M).


15 What does it mean when prtconf(1M) says "(no driver)"?
  I loaded the driver with modload(1M), and prtconf(1M) said the same thing.
  Referencing the driver by opening it and sending it data causes
  prtconf(1M) to remove the "(no driver)". What gives?

    At some point, the installation of modules was decoupled from the
    loading of modules, which was a good idea.  modload(1M) just loads a module
    and does not install it.  Basically, if you use this command, you are
    just wasting memory.  The command does have other valid uses.

    If you want to load and install your driver, there are two ways to do it.

    1)  Reference the device. (echo < /dev/ or /devices/...)
        and the system framework will autoload and install your driver.

    2)  drvconfig -i 
        loads and installs the named driver and build the device nodes for
        the driver.

    You'll have to use the second method if the device nodes for the
    device are not yet in the file system.  add_drv(1M) does
    a drvconfig(1M) on your behalf.

    One should also note that the notion of the module subsystem installing
    a driver is slightly different than the notion of the driver/system 
    frameworks notion of installing a driver.  drvconfig makes sure to do both.


16 What ddi_iblock_cookie_t should I pass mutex_init(9F) for a mutex
  used with a timeout(9F) routine?

    NULL is fine for mutexes that are not used in interrupt routines.


17 My driver panics the system in ddi_create_minor_node(9F). Is this 
  known bug in the kernel?

    No, it's probably a bug in the driver. Check your getinfo(9E) routine
    and be sure that it is doing the right thing (see the man page and
    the WDD for the proper form).


18 My driver needs to keep track of all the processes using it. This can
  be done in SunOS 4.x by looking at proc structure information in
  open(9E), but i can't "forget" about processes that are no longer 
  using it since close(9E) is only called when the last reference goes 
  away. How can I do this?

    You can't look at the proc structure in SunOS 5.x, so you
    might consider using the clone mechanism. Essentially, this
    allows you to allocate an "available" minor device on each open(9E),
    and since each open(9E) results in a new minor number, you get one
    close per process. This works fine for the normal case, though processes
    that fork(2), dup(2), or send file descriptors to other processes may
    be problems.

    There are two ways to do this: 
        1. STREAMS drivers can set the is_clone argument of 
           ddi_create_minor_node(9F) to CLONE_DEV. When the node
           is opened, the clone driver calls the open(9E) routine of
           the driver with the CLONEOPEN flag set.
        2. _All_ drivers can decide to clone by changing the minor
           number of the dev_t pointer passed to them.


19 I used to pass flags to my driver with the 'flags' keyword in SunOS 4.x.
  How do I do that in SunOS 5.x?

    You can create arbitrary properties in your driver.conf(4) file.
    These can be retrieved with one of the ddi_getprop(9F) routines
    in the driver, and are the preferred way to get information to the
    driver.


20 Do self-identifying devices require a configuration file?

    No. However, they can be used to augment the hardware devinfo
    node information. 
    
    In order for the hwconf file to augment the properties of a hardware 
    devinfo node, it must match the 'name' property, the 'parent' property, 
    and the first 'reg' property. If they don't match, you'll get two 
    devinfo nodes - one from the hardware (PROM) and one from the .conf file.

    There is no need to specify the 'intr' property if your PROM
    already specifies the correct one.  In general, it's a bug to use
    'intr' anyway, you should use the 'interrupts' property which allows
    you to specify the interrupt in terms of the parent bus (i.e. as an
    SBus level) rather than in terms of the implementation's mapping of
    that SBus level to the ipl (which is what 'intr' does).

    Now to the really exciting bit.  The 'reg' property of an SBus card
    consists of the tuple

        slot-number,offset-in-slot,size-in-bytes.

    Thus if your token ring card is plugged into slot 3, you need to specify

        name="tr" parent="sbus"
                reg=3,0xc00000,0x70,3,0,0x10000,3,0x400000,0x4;

    This should make register set 0 match the proms register mapping, register
    set 1 will be the 64k of PROM that you want to map, and register set 2
    will be the s4dma register you want to use.

    Of course the problem is that you need to specify the slot number.  You
    can probably specify a different line for every possible slot the
    device may be plugged into, then deal with the devinfo nodes that get
    created that don't match the hardware .. sure you can't fix the PROM?


21 My fcode driver incorrectly specifies the devices "reg" property
  or is missing some of the values of the "reg" property.  In SunOS 4.x,
  I used to be able to workaround this by manipulating the data in the
  dev_info structure.  How can I fix this in SunOS 5.x?

    As long as the fcode driver specifies at least one "reg" property,
    correctly or incorrectly for the device, you can use the "registers"
    property in the drivers hw.conf file to specify wildcarded values
    for any incorrectly specified "reg" properties.  Here's how to do
    this in the driver.conf(4) file.

    Example 1: Device has registers at offset 2000 for 100 bytes and
    a second set of registers at offset 10000 for 200 bytes.  Device
    is an sbus device whose parent node is always an sbus.  The fcode
    driver "forgot" to export the second set of registers.

        name="foo" class="sbus" registers=-1,2000,100,-1,10000,200;

    Example 2: Device has registers at offset 3000 for 256 bytes.  The
    fcode driver only specifies that the registers have 32 bytes.  The
    device is an sbus device whose parent node is always an sbus.

        name="foo" class="sbus" registers=-1,3000,256;

    In both of these examples, when the framework is creating internal
    data structures, the wildcarding will apply to all self-identifying
    devices with the same name, and the correct "sbus slot number" will
    be filled in to the "reg" property by the framework.  The information
    specified in these examples *augments* the information provided by
    the fcode driver and overrides the fcode drivers specification of the
    "reg" property.

    When using "registers", note that the external name of the device
    in the /devices tree will be the name as exported by the fcode drivers
    "reg" property. (So we can correctly mount root or use the device
    as a console, for example.)
 

22 My fcode driver only creates a "name" property and doesn't create
  any "reg" property at all?  Can I use "registers" wildcarding?

    No.  We must be able to fill in the correct slot number.  If your
    device's fcode driver does not export even an incorrect "reg" property,
    we cannot figure this information out and cannot complete the information.
    In this case, the only thing you can do is to provide site specific
    driver.conf(4) files using the "reg" property with one line (one entry) 
    for each instance of your card at that particular site.  In this case, 
    you will not be able access other properties that may be correctly 
    specified by the fcode driver, so you must include all of them (including 
    interrupts and any other properties you might need defined) in the 
    driver.conf(4) file.


23 What compiler can I use to compile device drivers?

    SunPro C is the only supported compiler. Others may work, but
    are not tested. gcc is known not to work for drivers in Solaris 2.1,
    but gcc 2.3.3 is able to compile the ramdisk driver under Solaris 2.2,
    though that is a simple driver.

    Even the SunPro compiler does not work in compliant mode (-Xc), since
    the system header files are not fully compliant.

    Note that, in order to ensure that compilers with better optimizers
    (such as SC 2.0.1) do not optimize away accesses to device registers,
    you _must_ use volatile properly.


24 Can I write a device driver in C++?

    You may be able to, but it isn't supported. For one thing, the system
    header files are not guarenteed to work with both __cplusplus and 
    _KERNEL set. 

    Another reason is that unlike C, C++ requires significant runtime support.
    Object constructors and destructors... object initialization... This 
    must come from somewhere and libC isn't available in the kernel.  You 
    can avoid this requirement by avoiding most uses of objects, but if 
    you do that, why use C++ at all?


25 I'm using the ddi_soft_state routines to allocate per-instance
  state structures. Using the global pointer they require, in this
  case 'statep', how can I access unit n's state structure in kadb?

    For 5.0 and 5.1, the way to do it in kadb is:

        *(*(*statep+10)+4n)/mX

    For instance #3 (starting at #0), longs #0 through #3 (starting at #0):

    in this case, '4n' is 4 times 3 = 0xc in hex.  So to look at
    the first four longs of instance #3's soft state structure, use:

        *(*(*statep+10)+c)/4X

    To just get the address, use:

        *(*(*statep+10)+c)=X

    For 5.2, the 0x10 is no longer needed. Use:

        *(*(*statep)+4n)/mX


26 Periodically, in situations where we have only 1 "xx" card in a machine,
  we get /dev/xx1 created instead of /dev/xx0. boot -r does not help.
  At one point we had 2 "xx" boards in this machine, and we've
  removed one of those. Why isn't the remaining card now /dev/xx0?

    When you move a card from one slot to another, or remove a card,
    we remember the old card.  This is what makes instance numbers
    and dev_t's stick and solves the sliding controller number problems
    that we've had forever.

    The device we probed first (ever) should be instance #0.  Look in
    /etc/path_to_inst and you should see which device pathname
    is instance #0.


27 What does 'panic: Lock Held and only one CPU' mean?

    This only occurs on a uniprocessor, and says that a spin mutex is held 
    and it would spin forever, because there is no other CPU to release it.
    This could be because the driver forgot to release the mutex on
    one code path, or blocked while holding it.

    A common cause of this panic is incorrect usage of a spin-type mutex
    used by the device's high-level interrupt handler.  If the interrupt is
    high-level (see ddi_intr_hilevel(9F) and Intro(9F)) the mutex
    a spin-type mutex which blocks the interrupt while held.  The interrupt
    handler cannot use an adaptive mutex or a condition variable since these
    might effect dispatcher state, and the dispatcher doesn't block high
    level interrupts.  If the spin-type mutex is held while calling
    a routine that blocks via cv_wait(9F) or via mutex_enter(9F) on an
    adaptive (normal non-high-level driver mutex), then the interrupt
    could occur during that time and spin forever on the mutex (on an MP)
    or get this panic on a uniprocessor.


28 I want to have two drivers in one module. It doesn't seem to work
  by putting multiple entries in the modlinkage structure.

    Including two drivers in a single loadable module is not supported.
    However, fret not, it can be done in a non-compliant way by making 
    three loadable modules.

    Put all the driver code in one common module and write two small wrapper
    modules for each driver.  The small wrappers need include at a minimum
    _init, _fini and _info and dev_ops/cb_ops structures for the driver
    and a line so the two small driver wrappers
    depend on the main module and get linked with its symbols.
    Each mini-driver stub should include

    char _depends_on[] = "misc/foo";

    where "misc/foo" would be the name of the common code module.
    The common module would be a "misc." module, with it's own _init, _fini
    and _info functions.  It's module ops would be of the "misc" type
    using a modlmisc structure (look this up in ).

    The underlying driver just includes the common shared code.
    The mini-driver stubs each have their own separate dev_ops structures
    and can be made unloadable.  The misc module with the common driver
    code will be automatically held via the driver stub modules depending
    on them.

    Note that this method, and using modlmisc, are not DDI-compliant.


29 How does a driver retrieve the hostid?

    There is no DDI-compliant way to retrieve it, though applications
    may retrieve it with the sysinfo(SI_HW_SERIAL) system call.


30 What is a 'watchdog reset'?

    A watchdog reset occurs when a synchronous trap condition occurs with
    traps disabled.  Since traps are disabled, the processor can't take a
    trap, but it can't continue executing, either.  So it enters error
    mode, and (logically) raises an error_mode signal.  This signal is
    (logically) wired back to the watchdog_reset_in pin.  (Note that with
    some chips there is no external error_mode signal, nor external
    watchdog_reset_in pin.  It all happens inside the chip.  But it's
    easier to explain, and to understand, this way.)

    One specific cause is if a device doesn't respond to an access.
    There's an SBus timer that generates SBus timeout conditions; this can
    cause a data_exception condition (trap).

    On an SS-1 or SS-2 the SBus is also the memory
    bus, and if you muck with the lines when you shouldn't (or lock up the
    bus when you shouldn't) you can cause bad instructions or data (or
    TIMEOUT) to be returned.  If this happens while the kernel is in some
    trap-disabled code (initial processing of a page-fault, a system call,
    a window over- or under-flow, or an interrupt, for example), the system
    will watchdog

    Reset causes the CPU to start fetching from location 0; it also causes
    the MMU to enter boot mode, and translate location 0 to the PROM.
    Early code in the PROM examines system registers to distinguish a
    watchdog reset from a power-on reset, and branches accordingly.

    One common way that a new driver can watchdog the system is by using
    too much stack space (commonly, too many/large local variables).
    Since the kernel stack doesn't grow, but has red-zones, and the first
    access into the red-zone could be with traps off from the trap handlers,
    that could cause a watchdog.

31 What is the maximum size of DVMA space?

    There are two maps for allocating DVMA space on sun4c (5.1). The DVMA
    map (1 MB) is used for mappings that are below a certain limit.
    Currently that's 128K (the size you get if you call minphys(9F)). Above
    that limit (and if DDI_DMA_PARTIAL is not set) the request will be handled
    from kernelmap. It works much like the bp_mapin() trick in SunOS 4.x,
    however everything is hidden behind the DDI interfaces so your driver 
    does not care anymore. Setting up mappings is a bit more expensive for 
    kernelmap than for the DVMA map, so smaller mappings are faster than 
    larger ones.

    This is a resource/time tradeoff. Currently the SPARCprinter uses
    larger dvma mappings on sun4c. It needs 2 MB? mappings at a time, and
    has a small buffer that doesn't allow for interrupt and ddi_dma_movwin(9F)
    latency.

    Note with DMA windows you can setup large mappings for larger requests
    however you might have to do multiple transfers with ddi_dma_movwin.

    *Single* DMA transfers are different (missing DDI_DMA_PARTIAL flag).
    Currently, the limits are:

        sun4    1 MB  (VME)

        sun4m   1 MB  (A24 VME)
                8 MB  (A32 VME)
               16 MB  (SBus 5.1)
               64 MB  (SBus 5.2)

        sun4d  64 MB  (per SBus)

        sun4c/e 8 MB  (5.1)

    This can be changed if there is need.


32 What is the maximum size that bp_mapin(9F) can map?

    Kernel virtual address space for bp_mapin(9F) is allocated from a map 
    called "kernelmap". The size of the map depends on the platform:

        sun4      9 M
        sun4m    36 M
        sun4c/e  32 M (5.1)
        sun4d   128 M (5.2)
        x86       ?

    There are other functions that allocate vitual address space out of
    this map such as kmem_alloc(9F) and ddi_map_regs(9F) (to map registers 
    in the kernel).  Actually, on sun4/c/e kmem_alloc(9F) first tries 
    a different map before accessing the kernelmap, so bp_mapin(9F) might 
    get close to 8 M on sun4.

    Keep in mind that bp_mapin(9F) takes away resources from a pool which is
    *shared* by multiple functions!

33 My driver needs to do special things based on the machine type, which
  it determines from the 'cputype' variable. Is this the right
  thing to do?

    No. Though you may be able to use the device tree to determine the
    "cpu" type, you really should be using more specific attributes in 
    the device tree to figure out what you really want to know.

    Put another way, in the distant past, where there were only a couple
    of machines with limited configurations, the driver programming model
    was:

        if (cputype is mumble-frotz) {
                /* I "know" I have hardware feature xyzzy */
        }

    In a world where we have many similar-but-different SPARC machines,
    made by both SMCC and the clone vendors, and many different SBus
    cards that would like to plug into them, this is a bogus inference
    that is almost always proved wrong by history.

    History?  Well, when the SS-1+ was first released, it might've seemed
    very tempting to just use '0x53' to deduce that the system had a
    25MHz SBus.

    However, many sun4c clones use the 'cputype' 0x53 (which means
    sun4c/65), others use 0x20 (which means "sun4 architecture"); it's
    clearly rather hard to use one magic number to capture all the attributes
    of these similar-but-different machines.

    The device tree exported by the OBP gives you a better way to
    handle this:

        if (get_property(..., "xyzzy", ...)) {
                /* I know I have hardware feature xyzzy */
                /* -or- "xyzzy" has value 42 on this configuration */
        }

    Note that the device tree deliberately does not try and lump together
    the description of the capabilities of the whole machine into one
    magic number - it gives us a rich database of attributes which can
    be queried by name.

    To continue the 0x53 example, there's a property called "clock-frequency"
    that an SBus device driver can look up if it cares about the SBus
    clock frequency (with a default value of 20MHz) using ddi_getprop(9F):

        clk_freq = ddi_getprop(DDI_DEV_T_ANY, dip, 0, "clock-frequency",
                20 * 1000 * 1000);

    A property with the same meaning is available on every machine
    after the SS-1+ -- regardless of machine 'type', so the above line
    will "just work" on any machine that has an SBus.

    So, the next question should be obvious.  Why does your driver -care-
    what platform it's running on?  What hardware feature are you trying
    to find out about?  (Because when you answer that, we should be able
    to come up with a better way to ask the system the question, using
    the device tree to provide the answer).

    See ddi_getprop*(9F) for how to query the attributes in the device tree.
    Look at the output of "prtconf -v" to see the attributes published by
    the kernel and PROM.

34 Is there a naming convention for I/O controls?

    The convention is to pick a letter (say 'M'), and left-shift its
    ASCII value 8 bits. Then, number your I/O controls in the lower
    eight bits. For example,
        #define MYIOC		('M' << 8)
        #define MYIOCTL_1	(MYIOC | 1)
        #define MYIOCTL_2	(MYIOC | 2)

    There are two issues to be aware of, however:

    source (namespace pollution):

        Choose a descriptive prefix that does not conflict with any system
        macros. For example, TIOC is a particularly dangerous one to use.
        First of all, the identifier is already defined in ,
        and there are already a number of TIO* ioctl values.  TIO* is sort
        of "reserved" for terminal stuff via historical precedent.

    binary:
        In principle, USL are supposed to keep a registry of ioctl numbers
        so that a driver can easily flag when it is being given an
        inappropriate ioctl.  In practice, this is very broken - there are not
        enough letters in the alphabet to deal with all the possible values.
        Of course this is really a 32-bit value - the ioctl command numbers 
	do not have to be crunched into 16 bits ... but still, an ioctl number
        registry doesn't really work unless everyone uses it.



Bibliography

    The SunOS 5.2 Writing Device Drivers manual (part #800-6502) is much
    more complete than it used to be, and will continue to be improved.

    The SunOS 5.2 STREAMS Programmers Guide (part #801-4054) contains
    STREAMS specifics, though the WDD should be considered a prerequisite.

    The manual pages in section 9 define the DDI/DKI:
        9E - driver entry points
        9F - kernel support routines
        9S - structures

    Other manual pages of interest include:
        sbus(4)
        vme(4)
        scsi(4)
        driver.conf(4)


    There are also sample drivers on the Operation Commitment
    Internet FTP server, opcom.sun.ca. These are available
    in /pub/drivers/svr4_sample_drivers.tar.Z via anonymous ftp. They
    are intended to be compliant, but are not guarenteed to be so.
--
Brian A. Onn,
Solaris 2 Migration Support Center,
Toronto, Ontario, Canada