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:
- What is the Solaris 2.x SPARC DDI/DKI?
- What does it mean if a driver is _not_ Solaris DDI-compliant?
- What do I do if I find a bug/omission in the DDI/DKI or the documentation?
- What is a nexus driver and how do I write one?
- What are the definitions of, and the relationships between instances,device numbers, minor nodes, major numbers, and minor numbers?
- Should I acquire a mutex around _any_ manipulations of a state structure element?
- Can, and should, I only have one mutex for each instance?
- What structures can my driver access?
- Can I access the user or proc structure?
- I need to prevent user memory from being paged out, so my driver can do DMA to it. Can I call as_fault()?
- Is there a way to send signals to user threads from a device driver?
- Why is there no man page for printf() or panic()?
- What is a layered driver?
- Is there a SunOS 5.x replacement for the SunOS 4.x devinfo command?
- What does it mean when prtconf(1M) says "(no driver)"?
- What ddi_iblock_cookie_t should I pass mutex_init(9F) for a mutex used with a timeout(9F) routine?
- My driver panics the system in ddi_create_minor_node(9F). Is this known bug in the kernel?
- My driver needs to keep track of all the processes using it. How can I do this?
- 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?
- Do self-identifying devices require a configuration file?
- 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?
- My fcode driver only creates a "name" property and doesn't create any "reg" property at all? Can I use "registers" wildcarding?
- What compiler can I use to compile device drivers?
- Can I write a device driver in C++?
- 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?
- 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?
- What does 'panic: Lock Held and only one CPU' mean?
- I want to have two drivers in one module. It doesn't seem to work
by putting multiple entries in the modlinkage structure.
- How does a driver retrieve the hostid?
- What is a 'watchdog reset'?
- What is the maximum size of DVMA space?
- What is the maximum size that bp_mapin(9F) can map?
- 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?
- 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