|
|
Converting a 4.x Device Driver to SunOS 5.5
A
- This chapter is a guide to the differences between SunOS 4.x and SunOS 5.x device drivers. It can be used by developers to update relatively simple drivers intended to operate on the same platform under SunOS 5.5 that they operated on under SunOS 4.x.
- Drivers that need to operate on multiple platforms, or drivers that intend to take advantage of features such as multithreading must be rethought and rewritten along the lines specified in the collective chapters of this manual.
Before Starting the Conversion
Review Existing Functionality
- Make sure the driver's current functionality is well understood: the way it manages the hardware, and the interfaces it provides to applications (ioctl(2) states the device is put in for example). Maintain this functionality in the new driver.
Read the Manual
- This chapter is not a substitute for the rest of this book. Make sure you have access to the SunOS 5.5 Reference Manuals.
ANSI C
- The unbundled Sun C compiler is now ANSI C compliant. Most ANSI C changes are beyond the scope of this book. There are a number of good ANSI C books available from local bookstores. The following two books are good references:
-
- Kernighan and Ritchie, The C Language, Second Edition, 1988, Prentice-Hall
- Harbison and Steele, C: A Reference Manual, Second Edition, 1987, Prentice-Hall
Development Environment
DDI/DKI
- The DDI/DKI is a new name for the routines formerly called "kernel support routines" in the SunOS 4.x Writing Device Drivers manual, and for the "well-known" entry points in the SunOS 4.x cdevsw and bdevsw structures. The intent is to specify a set of interfaces for drivers that provide a binary and source code interface. If a driver uses only kernel routines and structures described in Section 9 of the Solaris 2.5 Reference Manual AnswerBook, it is called Solaris 2.5 DDI/DKI-compliant. A Solaris 2.5 DDI/DKI-compliant driver is likely to be binary compatible across Sun Solaris platforms with the same processor, and binary compatible with future releases of Solaris on platforms the driver works on.
Things to Avoid
- Many architecture-specific features have been hidden from driver writers behind DDI/DKI interfaces. Specific examples are elements of the dev_info structure, user structure, proc structure, and page tables. If the driver has been using unadvertised interfaces, it must be changed to use DDI/DKI interfaces that provide the required functionality. If the driver continues to use unadvertised interfaces, it loses all the source and binary compatibility features of the DDI/DKI. For example, previous releases had an undocumented routine called as_fault( ) that could be used to lock down user pages in memory. This routine still exists, but is not part of the DDI/DKI, so it should not be used. The only documented way to lock down user memory is to use physio(9F).
- Do not use any undocumented fields of structures. Documented fields are in Section 9S of the Solaris 2.5 Reference Manual AnswerBook. Do not use fields, structures, variables, or macros just because they are in a header file.
- Dynamically allocate structures whenever possible. If buf(9S) structure is needed, do not declare one. Instead, declare a pointer to one, and call getrbuf(9F) to allocate it.
-
Note - Even using kmem_alloc(sizeof(struct buf)) is not allowed, since the size of a buf(9S) structure may change in future releases.
System V Release 4
- SunOS 5.x is the Sun version of AT&T's System V Release 4 (SVR4). The system administration model is different from those in previous SunOS releases, which were more like 4.3 BSD. Differences important to device driver writers are:
-
- For general SVR4 system administration information see the Solaris 1.x to 2.x Transition Guide.
Development Tools
- The only compiler that should be used to compile SunOS 5.x device drivers is the unbundled Sun C compiler. This is either part of SPARCworks 2.0.1 (for SPARC systems) or ProWorks 2.0.1 (for x86 systems). See Chapter 13, "Loading and Unloading Drivers" for information on how to compile and load a driver. Note that the compiler's bin directory (possibly /opt/SUNWspro/bin) and the supporting tools directory (/usr/ccs/bin) should be prepended to the PATH. When compiling a driver, use the -Xa and -D_KERNEL options.
- When building a loadable driver module from the object modules, use ld(1) with the -r flag.
Debugging Tools
-
adb(1), kadb(1M), and crash(1M) are essentially the same as they were in SunOS 4.x, though there are new macros. To debug a live kernel, use /dev/ksyms (see ksyms(7)) instead of the kernel name (which used to be /vmunix):
-
# adb -k /dev/ksyms /dev/mem
|
- See "Debugging Tools" on page 323, for more information.
ANSI C
- The unbundled Sun C compiler is now ANSI C compliant. Two important ANSI C features device driver writers should use are the volatile keyword and function prototyping.
volatile
-
volatile is a new ANSI C keyword. It is used to prevent the optimizer from removing what it thinks are unnecessary accesses to objects. All device registers should be declared volatile. As an example, if the device has a control register that requires two consecutive writes to get it to do something, the optimizer could decide that the first write is unnecessary since the value is unused if there is no intervening read access.
-
Note - It is not an error to declare something volatile unnecessarily.
Function Prototypes
- ANSI C provides function prototypes. This allows the compiler to check the type and number of arguments to functions, and avoids default argument promotions. To prototype functions, declare the type and name of each function in the function definition. Then, provide a prototype declaration (including at least the types) before the function is called.
- Prototypes are provided for most DDI/DKI functions so many potentially fatal errors are now caught at compile time.
Header Files
- For Solaris 2.x DDI/DKI compliance, drivers are allowed to include only the kernel header files listed in the synopsis sections of Section 9 of the Solaris 2.5 Reference Manual AnswerBook. All allowed kernel header files are now located in the /usr/include/sys directory.
- New header files all drivers must include are <sys/ddi.h> and <sys/sunddi.h>. These two headers must appear last in the list of kernel header include files.
Overview of Changes
Autoconfiguration
- Under SunOS 4.1.2 or later, the system initialized all the drivers in the system before starting init(8). The advent of loadable module technology allowed some device drivers to be added and removed manually at later times in the life of the system.
- SunOS 5.X extends this idea to make every driver loadable, and to allow the system to automatically configure itself continually in response to the needs of applications. This, plus the unification of the "mb" style and Open Boot style autoconfiguration, has meant some significant changes to the identify(9E), probe(9E), and attach(9E) routines, and has added detach(9E).
- Because all device drivers are loadable, the kernel no longer needs to be recompiled and relinked to add a driver. The config(8) program has been replaced by Open Boot PROM information and supplemented by information in hardware configuration files (see driver.conf(4)).
-
Changes to Routines
-
- The xxinit( ) routine for loadable modules in SunOS 4.x has been split into three routines. The VDLOAD case has become _init(9E), the VDUNLOAD case has become _fini(9E), and the VDSTAT case has become _info(9E).
- It is no longer guaranteed that identify(9E) is called once before attach(9E). It may now be called any number of times, and may be called at any time. Do not count device units. See ddi_get_instance(9F) for more information.
-
- The SunOS 5.x probe(9E) is not the same as probe(9E) in SunOS 4.x. It is called before attach(9E), and may be called any number of times, so it must be stateless. If it allocates resources before it probes the device, it must deallocate them before returning (regardless of success or failure). attach(9E) will not be called unless probe(9E) succeeds.
-
attach(9E) is called to allocate any resources the driver needs to operate the device. The system now assigns the instance number (previously known as the unit number) to the device.
- The reason the rules are so stringent is that the implementation will change. If driver routines follow these rules, they will not be affected by changes to the implementation. If, however, they assume that the autoconfiguration routines are called only in a certain order (first identify(9E), then probe(9E), then attach(9E) for example), these drivers will break in some future release.
Instance Numbers
- In SunOS 4.x, drivers used to count the number of devices that they found, and assign a unit number to each (in the range 0 to the number of units found less one). Now, these are called instance numbers, and are assigned to devices by the system.
- Instances can be thought of as a shorthand name for a particular instance of a device (foo0 could name instance 0 of device foo). They are assigned and remembered by the system, even after any number of reboots. This is because at open(2) time all the system has is a dev_t. To determine which device is needed (since it may need to be attached), the system needs to get the instance number (which the driver retrieves from the minor number).
- The mapping between instance numbers and minor numbers (see getinfo(9E)) should be static. The driver should not require any state information to do the translation, since that information may not be available (the device may not be attached).
/devices
- All devices in the system are represented by a data structure in the kernel called the device tree. The /devices hierarchy is a representation of this tree in the file system.
- In SunOS 4.x, special device files were created using mknod (or by an installation script running mknod) by the administrator. Now, entries are advertised to the kernel by device drivers calling ddi_create_minor_node(9F) once they have determined a particular device exists. drvconfig(1M) actually maintains the file system nodes. This results in names that completely identify the device.
/dev
- In SunOS 4.x, device special files lived (by convention) in /dev. Now that the /devices directory is used for special files, /dev is used for logical device names. Usually, these are symbolic links to the real names in /devices.
- Logical names can be used for backwards compatibility with SunOS 4.X applications, a shorthand for the real /devices name, or a way to identify a device without having to know where it is in the /devices tree (/dev/fb could refer to a cgsix, cgthree, or bwtwo framebuffer, but the application does not need to know this).
- See disks(1M), tapes(1M), ports(1M), devlinks(1M), and /etc/devlink.tab for system supported ways of creating these links. See Chapter 5, "Autoconfiguration" and Application Packaging Developer's Guide for more information.
Multithreading
- SunOS 5.x supports multiple threads in the kernel, and multiple CPUs. A thread is a sequence of instructions being executed by a program. In SunOS 5.x, there are application threads, and there are kernel threads. Kernel threads are used to execute kernel code, and are the threads of concern to the driver writer.
- Interrupts are also handled as threads. Because of this, there is less of a distinction between the top-half and bottom-half of a driver than there was in SunOS 4.x. All driver code is executed by a thread, which may be running in parallel with threads in other (or the same) part of a driver. The distinction now is whether these threads have user context.
- See Chapter 4, "Multithreading," for more information.
Locking
- Under SunOS 4.1.2 or later, only one processor can be in the kernel at any one time. This is accomplished by using a master lock around the entire kernel. When a processor wants to execute kernel code, it needs to acquire the lock (this excludes other processors from running the code protected by the lock) and then release the lock when it is through. Because of this master lock, drivers written for uniprocessor systems did not change for multiprocessor systems. Two processors could not execute driver code at the same time.
- In SunOS 5.x, instead of one master lock, there are many smaller locks that protect smaller regions of code. For example, there may be a kernel lock that protects access to a particular vnode, and one that protects an inode. Only one processor can be running code dealing with that vnode at a time, but another could be accessing an inode. This allows a greater degree of concurrency.
- However, because the kernel is multithreaded, the possibility exists that two (or more) threads are in driver code at the same time.
-
- One thread could be in an entry point, and another in the interrupt routine. The driver had to deal with this in SunOS 4.x, but with the restriction that the interrupt routine blocked the user context routine while it ran.
- Two threads could be in a routine at the same time. This could not happen in SunOS 4.x.
- Both of these cases are similar to situations present in SunOS 4.x, but now these threads could run at the same time on different CPUs. The driver must be prepared to handle these types of occurrences.
Mutual Exclusion Locks
- In SunOS 4.x, a driver had to be careful when accessing data shared between the top-half and the interrupt routine. Since the interrupt could occur asynchronously, the interrupt routine could corrupt data or simply hang. To prevent this, portions of the top half of the driver would raise, using the various spl routines, the interrupt priority level of the CPU to block the interrupt from being handled:
-
-
s = splr(pritospl(6));
/* access shared data */
(void)splx(s);
- In SunOS 5.x, this no longer works. Changing the interrupt priority level of one CPU does not necessarily prevent another CPU from handling the interrupt. Also, two top-half routines may be running simultaneously with the interrupt running on a third CPU.
- To solve this, SunOS 5.x provides:
-
- A uniform module of execution--even interrupts run as threads. This blurs the distinction between the top-half and the bottom-half, as effectively every routine is a bottom-half routine.
- A number of locking mechanisms - a common mechanism is to use mutual exclusion locks (mutexes):
-
-
mutex_enter(&mu);
/* access shared data */
mutex_exit(&mu);
- A subtle difference from SunOS 4.X is that, because everything is run by kernel threads, the interrupt routine needs to explicitly acquire and release the mutex. In SunOS 4.x, this was implicit since the interrupt handler automatically ran at an elevated priority.
- See "Locking Primitives" on page 82 for more information on locking.
Condition Variables
- In SunOS 4.X, when the driver wanted the current process to wait for something (such as a data transfer to complete), it called sleep( ), specifying a channel and a dispatch priority. The interrupt routine then called wakeup( ) on that channel to notify all processes waiting on that channel that something happened. Since the interrupt could occur at any time, the interrupt priority was usually raised to ensure that the wakeup could not occur until the process was asleep.
-
Code Example 14-1 SunOS 4.x synchronization method
-
-
int busy; /* global device busy flag */
int xxread(dev, uio)
dev_t dev;
struct uio *uio;
{
int s;
-
-
s = splr(pritospl(6));
while (busy)
sleep(&busy, PRIBIO + 1);
busy = 1;
(void)splx(s);
/* do the read */
}
int xxintr()
{
busy = 0;
wakeup(&busy);
}
- SunOS 5.X provides similar functionality with condition variables. Threads are blocked on condition variables until they are notified that the condition has occurred. The driver must acquire a mutex which protects the condition variable before blocking the thread. The mutex is then released before the thread is blocked (similar to blocking/unblocking interrupts in SunOS 4.X)
-
Code Example 14-2 Synchronization in SunOS 5.x similar to SunOS 4.x
-
-
int busy; /* global device busy flag */
kmutex_t busy_mu; /* mutex protecting busy flag */
kcondvar_t busy_cv; /* condition variable for busy flag */
static int
xxread(dev_t dev, struct uio *uiop, cred_t *credp)
{
mutex_enter(&busy_mu);
while (busy)
cv_wait(&busy_cv, &busy_mu);
busy = 1;
mutex_exit(&busy_mu);
/* do the read */
}
static u_int
xxintr(caddr_t arg)
{
mutex_enter(&busy_mu);
busy = 0;
cv_broadcast(&busy_cv);
mutex_exit(&busy_mu);
}
- Like wakeup(), cv_broadcast(9F) unblocks all threads waiting on the condition variable. To wake up one thread, use cv_signal(9F) (there was no documented equivalent for cv_signal(9F) in SunOS 4.x).
-
Note - There is no equivalent to the dispatch priority passed to sleep( ).
- Though the sleep() and wakeup() calls exist, please do not use them, since the result would be an MT-unsafe driver.
- See "Thread Synchronization" on page 85 for more information.
Catching Signals
- There is always the possibility that either the driver accidentally waits for an event that will never occur, or the event will not happen for a long time. In either case, the user may want to abort the process by sending it a signal (or typing a character that causes a signal to be sent to the process). Whether the signal causes the driver to wake up depends on the driver.
- In SunOS 4.x, whether the sleep( ) was signal-interruptible depended on the dispatch priority passed to sleep( ). If the priority was greater than PZERO, the driver was signal-interruptible, otherwise the driver would not be awakened by a signal. Normally, a signal interrupt caused sleep( ) to return back to the user, without letting the driver know the signal had occurred. Drivers that needed to release resources before returning to the user passed the PCATCH flag to sleep( ), then looked at the return value of sleep( ) to determine why they awoke:
-
-
while (busy) {
if (sleep(&busy, PCATCH | (PRIBIO + 1))) {
/* awakened because of a signal */
/* free resources */
return (EINTR);
}
}
- In SunOS 5.x, the driver can use cv_wait_sig(9F) to wait on the condition variable, but be signal interruptible. Note that cv_wait_sig(9F) returns zero to indicate the return was due to a signal, but sleep( ) in SunOS 4.x returned a nonzero value:
-
-
while (busy) {
if (cv_wait_sig(&busy_cv, &busy_mu) == 0) {
/* returned because of signal */
/* free resources */
return (EINTR);
}
}
cv_timedwait( )
- Another solution drivers used to avoid blocking on events that would not occur was to set a timeout before the call to sleep. This timeout would occur far enough in the future that the event should have happened, and if it did run it would awaken the blocked process. The driver would then see if the timeout function had run, and return some sort of error.
- This can still be done in SunOS 5.x, but the same thing may be accomplished with cv_timedwait(9F). An absolute time to wait for is passed to cv_timedwait(9F), and which will return zero if the time is reached and the event has not occurred. See Code Example 4-3 on page 89 for an example usage of cv_timedwait(9F). Also see "Using cv_wait_sig(9F)" on page 90 for information on cv_timedwait_sig(9F).
Other Locks
- Semaphores and readers/writers locks are also available. See semaphore(9F) and rwlock(9F).
Lock Granularity
- Generally, start with one, and add more depending on the abilities of the device. See "Choosing a Locking Scheme" on page 91 and Appendix F, "Advanced Topics," for more information.
Interrupts
- In SunOS 4.x, two distinct methods were used for handling interrupts.
-
- Polled, or autovectored, interrupts were handled by calling the xxpoll( ) routine of the device driver. This routine was responsible for checking all drivers' active units.
-
- Vectored interrupt handlers were called directly in response to a particular hardware interrupt on the basis of the interrupt vector number assigned to the device.
- In SunOS 5.x, the interrupt handler model has been unified. The device driver registers an interrupt handler for each device instance, and the system either polls all the handlers for the currently active interrupt level, or calls that handler directly (if it is vectored). The driver no longer needs to care which type of interrupt mechanism is in use (in the handler).
-
ddi_add_intr(9F) is used to register a handler with the system. A driver-defined argument of type caddr_t to pass to the interrupt handler. The address of the state structure is a good choice. The handler can then cast the caddr_t to whatever was passed. See "Registering Interrupts" on page 120 and "Responsibilities of an Interrupt Handler" on page 121 for more information.
DMA
- In SunOS 4.x, to do a DMA transfer the driver mapped a buffer into the DMA space, retrieved the DMA address and programed the device, did the transfer, then freed the mapping. This was accomplished in a sequence like:
-
-
mb_mapalloc( ) - map buffer into DMA space
-
MBI_ADDR( ) - retrieve address from returned cookie
- program the device and start the DMA
-
mb_mapfree( ) - free mapping when DMA is complete
- The first three usually occurred in a start( ) routine, and the last in the interrupt routine.
- The SunOS 5.x DMA model is similar, but it has been extended. The goal of the new DMA model is to abstract the platform dependent details of DMA away from the driver. A sliding DMA window has been added for drivers that want to do DMA to large objects, and the DMA routines can be informed of device limitations (such as 24-bit addressing).
- The sequence for DMA is as follows: Allocate a DMA handle using ddi_dma_alloc_handle(9F). The DMA handle can be reused for subsequent DMA transfers. Then commit DMA resources using either
-
ddi_dma_buf_bind_handle(9F) or ddi_dma_addr_bind_handle(9F), retrieve the DMA address from the DMA cookie to do the DMA, then free the mapping with ddi_dma_unbind_handle(9F). The new sequence is something like this:
-
-
ddi_dma_alloc_handle(9F) - allocate a DMA handle
-
ddi_dma_buf_bind_handle(9F) - allocate DMA resources and retrieve address from the returned cookie
-
program the device and start the DMA
-
Perform the transfer.
-
Note - If the transfer involves several windows, you can call ddi_dma_getwin(9F) to move to subsequent windows.
-
-
ddi_dma_unbind_handle(9F) - free mapping when DMA is complete
-
ddi_dma_free_handle(9F) - free DMA handle [when no longer needed]
- Additional routines have been added to synchronize any underlying caches and buffers, and handle IOPB memory. See Chapter 7, "DMA" for details.
- In addition, in SunOS 4.x, the driver had to inform the system that it might do DMA, either through the mb_driver structure or with a call to adddma( ). This was needed because the kernel might want to block interrupts to prevent DMA, but needed to know the highest interrupt level to block. Because the new implementation uses mutexes, this is no longer needed.
Conversion Notes
-
-
identify()
SunOS 4.x:
int xxidentify(name)
char *name;
- SunOS 5.x
-
-
int xxidentify(dev_info_t *dip)
- The name property is no longer passed to identify(9E). ddi_get_name(9F) must be used to retrieve the name from the dev_info_t pointer argument.
-
Note - The unit counting is now handled by the framework. To get the unit number in any routine, call ddi_get_instance(9F). Do not count units anywhere.
-
identify(9E) is no longer guaranteed to be called for all units before attach(9E) is ever called. However, identify(9E) is guaranteed be called before attach(9E) on a per-instance basis.
-
-
probe()
SunOS 4.x:
int xxprobe(reg, unit)
caddr_t reg;
int unit;
- SunOS 5.x
-
-
int xxprobe(dev_info_t *dip)
-
probe(9E) is still expected to determine if a device is there or not, but now it may be called any number of times, so it must be stateless (free anything it allocates).
-
-
attach()
SunOS 4.x: VMEbus SBus
int xxattach(md) int xxattach(devinfo)
struct mb_device *md; struct dev_info *devinfo;
- SunOS 5.x
-
-
int xxattach(dev_info_t *dip, ddi_attach_cmd_t cmd)
- As noted in identify(9E), drivers are not allowed to count instances anywhere. Use ddi_get_instance(9F) to get the assigned instance number.
-
new_kmem_alloc( ) and new_kmem_zalloc( ) have become kmem_alloc(9F) and kmem_zalloc(9F). In SunOS 4.x sleep flags were KMEM_SLEEP and KMEM_NOSLEEP; now they are KM_SLEEP and KM_NOSLEEP. Consider using
-
KM_SLEEP only on small requests, as larger requests could deadlock the driver if there is not (or there will not be) enough memory. Instead, use KM_NOSLEEP, possibly shrink the request, and try again.
- Any required memory should be dynamically allocated, as the driver should handle all occurrences of its device rather than a fixed number of them (if possible). Instead of statically allocating an array of controller state structures, each should now be allocated dynamically.
- Remember to call ddi_create_minor_node(9F) for each minor device name that should be visible to applications.
- The module loading process turns the information in any driver.conf(4) file into properties. Information which used to pass in the config file (such as flags) should now be passed as properties.
-
-
getinfo()
SunOS 5.x:
int xxgetinfo(dev_info_t *dip, ddi_info_cmd_t cmd,
void *arg, void **resultp)
- Make sure that the minor number to instance number and the reverse translation is static, since getinfo(9E) may be called when the device is not attached. For example:
-
-
#define XXINST(dev) (getminor(dev) >> 3)
- This is a required entry point; it cannot be replaced with nulldev(9F) or nodev(9F).
-
-
open()
SunOS 4.x:
int xxopen(dev, flag)
dev_t dev;
int flag;
- SunOS 5.x
-
-
int xxopen(dev_t *devp, int flag, int otyp, cred_t *credp)
- The first argument to open(9E) is a pointer to a dev_t. The rest of the cb_ops(9S) routines receive a dev_t.
- Verify that the open type is one that the driver actually supports. This is normally OTYP_CHR for character devices, or OTYP_BLK for block devices. This prevents the driver from allowing future open types that it does not support.
- If the driver used to check for root privileges using suser(), it should now use driv_priv(9F) instead on the passed credential pointer.
-
psize() This entry point does not exist. Instead, block devices should support the nblocks property. This property may be created in attach(9E) if its value will not change. A prop_op(9E) entry point may be required if the value cannot be determined at attach time (such as if the device supports removable media). See "Properties" on page 69 for more information.
-
read() and write() SunOS 4.x:
-
-
int xxread(dev, uio)
int xxwrite(dev, uio)
dev_t dev;
struct uio *uio;
- SunOS 5.x
-
-
int xxread(dev_t dev, uio_t *uiop, cred_t *credp);
int xxwrite(dev_t dev, uio_t *uiop, cred_t *credp);
-
physio(9F) should no longer be called with the address of a statically allocated buf(9S) structure. Instead, pass a NULL pointer as the second argument, which causes physio(9F) to allocate a buf structure. The address of the allocated buf structure should always be saved in strategy(9E), since it is needed to call biodone(9F). An alternative is to use getrbuf(9F) to allocate the buf(9S) structure, and freerbuf(9F) to free it.
-
-
ioctl()
- SunOS 4.x:
-
-
int xxioctl(dev, cmd, data, flag)
dev_t dev;
int cmd, flag;
caddr_t data;
- SunOS 5.x
-
-
int xxioctl(dev_t dev, int cmd, int arg, int mode,
cred_t *credp, int *rvalp);
- In SunOS 4.x, ioctl( ) command arguments were defined as follows:
-
-
#define XXIOCTL1 _IOR(m, 1, u_int)
- The _IOR( ), _IOW( ), and _IOWR( ) macros used to encode the direction and size of the data transfer. The kernel would then automatically copy the data into or out of the kernel. This is no longer the case. To do a data transfer, the driver is now required to use ddi_copyin(9F) and ddi_copyout(9F) explicitly. Do not dereference arg directly.
- In addition, use the new method of a left-shifted letter OR-ed with number:
-
-
#define XXIOC ('x'<<8)
#define XXIOCTL1 (XXIOC | 1)
- The credential pointer can be used to check credentials on the call (with drv_priv(9F)), and the return value pointer can be used to return a value which means something (as opposed to the old method of always getting zero back for success). This number should be positive to avoid confusion with applications that check for ioctl(2) returning a negative value for failure.
-
-
strategy()
SunOS 4.x:
int xxstrategy(buf)
struct buf *bp;
- SunOS 5.x
-
-
int xxstrategy(struct buf *bp);
- Retrieving the minor number from the b_dev field of the buf(9S) structure no longer works (or will work occasionally, and fail in new and interesting ways at other times). Use the b_edev field instead.
- If the driver used to allocate buffers uncached, it should now use ddi_dma_sync(9F) whenever consistent view of the buffer is required.
-
mmap() SunOS 4.x:
-
-
int xxmmap(dev, off, prot)
dev_t dev;
off_t off;
int prot;
- SunOS 5.x
-
-
int xxmmap(dev_t dev, off_t off, int prot);
- Building a page table entry manually is no longer allowed. The driver must use hat_getkpfnum(9F) to retrieve the PTE information from a virtual address. See "Mapping Device Memory" on page 177 for more information.
- If the driver used to check for root privileges using suser(), it should now use drv_priv(9F). Because there is no credential pointer passed to mmap(9E), the driver must use ddi_get_cred(9F) to retrieve the credential pointer.
-
-
chpoll()
-
chpoll(9E) is similar in operation to select( ), but there are more conditions that can be examined. See "Multiplexing I/O on File Descriptors" on page 180, for details.
SunOS 4.1.x to SunOS 5.5 Differences
- This table compares device driver routines on SunOS 4.1.x versus SunOS 5.5. It is not a table of equivalences. That is, simply changing from the function in column one to the function (or group of functions) in column two is not always sufficient. If the 4.1.x driver used a function in column one, read about the function in column two before changing any code.
-
Table A-1
| SunOS 4.1.x | SunOS 5.5 | Description |
| ASSERT() | ASSERT() | expression verification |
| CDELAY() | - | conditional busy-wait |
| DELAY() | drv_usecwait() | busy-wait for specified interval |
| OTHERQ() | OTHERQ() | get pointer to queue's partner queue |
| RD() | RD() | get pointer to the read queue |
| WR() | WR() | get pointer to the write queue |
-
Table A-1
| SunOS 4.1.x | SunOS 5.5 | Description |
| add_intr() | ddi_add_intr() | add an interrupt handler |
| adjmsg() | adjmsg() | trim bytes from a message |
| allocb() | allocb() | allocate a message block |
| backq() | backq() | get pointer to queue behind the current queue |
| bcmp() | bcmp() | compare two byte arrays |
| bcopy() | bcopy() | copy data between address locations in kernel |
biodone()
iodone() | biodone() | indicate I/O is complete |
biowait()
iowait() | biowait() | wait for I/O to complete |
| bp_mapin() | bp_mapin() | allocate virtual address space |
| bp_mapout() | bp_mapout() | deallocate virtual address space |
| brelse() | brelse() | return buffer to the free list |
| btodb() | - | convert bytes to disk sectors |
| btop() | btop()
ddi_btop() | convert size in bytes to size in pages
(round down) |
| btopr() | btopr()
ddi_btopr() | convert size in bytes to size in pages
(round up) |
| bufcall() | bufcall() | call a function when a buffer becomes available |
| bzero() | bzero() | zero out memory |
| canput() | canput() | test for room in a message queue |
| clrbuf() | clrbuf() | erase the contents of a buffer |
| copyb() | copyb() | copy a message block |
| copyin() | ddi_copyin() | copy data from a user program to a driver buffer |
| copymsg() | copymsg() | copy a message |
-
Table A-1
| SunOS 4.1.x | SunOS 5.5 | Description |
| copyout() | ddi_copyout() | copy data from a driver to a user program |
| datamsg() | datamsg() | test whether a message is a data message |
| delay() | delay() | delay execution for a specified number of clock ticks |
| disksort() | disksort() | single direction elevator seek sort for buffers |
| dupb() | dupb() | duplicate a message block descriptor |
| dupmsg() | dupmsg() | duplicate a message |
| enableok() | enableok() | reschedule a queue for service |
| esballoc() | esballoc() | allocate a message block using caller-supplied buffer |
| esbbcall() | esbbcall() | call function when buffer is available |
| ffs() | ddi_ffs() | find first bit set in a long integer |
| fls() | ddi_fls() | find last bit set in a long integer |
| flushq() | flushq() | remove messages from a queue |
| free_pktiopb() | scsi_free_consistent_buf() | free a SCSI packet in the iopb map |
| freeb() | freeb() | free a message block |
| freemsg() | freemsg() | free all message blocks in a message |
| get_pktiopb() | scsi_alloc_consistent_buf( ) | allocate a SCSI packet in the iopb map |
| geterror() | geterror() | get buffer's error number |
| getlongprop() | ddi_getlongprop() | get arbitrary size property information |
| getprop() | ddi_getprop() | get boolean and integer property information |
| getproplen() | ddi_getproplen() | get property information length |
| getq() | getq() | get the next message from a queue |
| gsignal() | - | send signal to process group |
| hat_getpkfnum() | hat_getkpfnum() | get page frame number for kernel address |
-
Table A-1
| SunOS 4.1.x | SunOS 5.5 | Description |
| index() | strchr() | return pointer to first occurrence of character in string |
| insq() | insq() | insert a message into a queue |
| kmem_alloc() | kmem_alloc() | allocate space from kernel free memory |
| kmem_free() | kmem_free() | free previously allocated kernel memory |
| kmem_zalloc() | kmem_zalloc() | allocate and clear space from kernel free memory |
| linkb() | linkb() | concatenate two message blocks |
| log() | strlog() | log kernel errors |
| machineid() | - | get host ID from EPROM |
| major() | getmajor() | get major device number |
| makecom_g0() | makecom_g0() | make packet for SCSI group 0 commands |
| makecom_g0_s() | makecom_g0_s() | make packet for SCSI group 0 sequential commands |
| makecom_g1() | makecom_g1() | make packet for SCSI group 1 commands |
| makecom_g5() | makecom_g5() | make packet for SCSI group 5 commands |
mapin()
map_regs() | ddi_regs_map_setup() | map physical to virtual space |
mapout()
unmap_regs() | ddi_regs_map_free() | remove physical to virtual mappings |
| max() | max() | return the larger of two integers |
| mb_mapalloc() | ddi_dma_buf_bind_handle() | setup system DMA resources and retrieve DMA address |
| mb_mapfree() | ddi_dma_unbind_handle() | release system DMA resources |
| mballoc() | - | allocate a main bus buffer |
| mbrelse() | - | free main bus resources |
| mbsetup() | - | set up use of main bus resources |
| min() | min() | return the lesser of two integers |
| minor() | getminor() | get minor device number |
-
Table A-1
| SunOS 4.1.x | SunOS 5.5 | Description |
| minphys() | minphys() | limit transfer request size to system maximum |
| mp_nbmapalloc() | ddi_dma_addr_bind_handle() | setup system DMA resources and retrieve DMA address |
| MBI_ADDR() | - | retrieve DMA address |
| msgdsize() | msgdsize() | return the number of bytes in a message |
| nodev() | nodev() | error function returning ENXIO |
| noenable() | noenable() | prevent a queue from being scheduled |
| nulldev() | nulldev() | function returning zero |
| ovbcopy() | - | copy overlapping byte memory regions |
| panic() | cmn_err() | reboot at fatal error |
| peek() | ddi_peeks() | read a short value from a location |
| peekc() | ddi_peekc() | read a byte value from a location |
| peekl() | ddi_peekl() | read a long value from a location |
| physio() | physio() | limit transfer request size |
| pkt_transport() | scsi_transport() | request by a SCSI target driver to start a command |
| poke() | ddi_pokes() | write a short value to a location |
| pokec() | ddi_pokec() | write a byte value to a location |
| pokel() | ddi_pokel() | write a long value to a location |
| printf() | cmn_err() | display an error message or panic the system |
| pritospl() | - | convert priority level |
| psignal() | - | send a signal to a process |
| ptob() | ptob()
ddi_ptob() | convert size in pages to size in bytes |
| pullupmsg() | pullupmsg() | concatenate bytes in a message |
| put() | put() | call a STREAMS put procedure |
| putbq() | putbq() | place a message at the head of a queue |
-
Table A-1
| SunOS 4.1.x | SunOS 5.5 | Description |
| putctl() | putctl() | send a control message to a queue |
| putctl1() | putctl1() | send a control message with one-byte parameter to a queue |
| putnext() | putnext() | send a message to the next queue |
| putq() | putq() | put a message on a queue |
| qenable() | qenable() | enable a queue |
| qreply() | qreply() | send a message on a stream in the reverse direction |
| qsize() | qsize() | find the number of messages on a queue |
| remintr() | ddi_remove_intr() | remove an interrupt handler |
| report_dev() | ddi_report_dev() | announce a device |
| rmalloc() | rmallocmap()
rmalloc() | allocate resource map
allocate space from a resource map |
| rmalloc(iopbmap) | ddi_iopb_alloc() | allocate consistent memory |
| rmfree() | rmfreemap()
rmfree() | free resource map
free space back into a resource map |
| rmfree(iopbmap) | ddi_iopb_free() | free consistent memory |
| rmvb() | rmvb() | remove a message block from a message |
| rmvq() | rmvq() | remove a message from a queue |
| scsi_abort() | scsi_abort() | abort a SCSI command |
| scsi_dmafree() | scsi_destroy_pkt() | free DMA resources for SCSI command |
| scsi_dmaget() | scsi_init_pkt() | allocate DMA resources for SCSI command |
| scsi_ifgetcap() | scsi_ifgetcap() | get SCSI transport capability |
| scsi_ifsetcap() | scsi_ifsetcap() | set SCSI transport capability |
| scsi_pktalloc() | scsi_pktalloc() | allocate packet resources for SCSI command |
| scsi_pktfree() | scsi_pktfree() | free packet resources for SCSI command |
| scsi_poll() | scsi_poll() | run a polled SCSI command |
-
Table A-1
| SunOS 4.1.x | SunOS 5.5 | Description |
| scsi_resalloc() | scsi_init_pkt() | prepare a complete SCSI packet |
| scsi_reset() | scsi_reset() | reset a SCSI bus or target |
| scsi_resfree() | scsi_destroy_pkt() | free an allocated SCSI packet |
| scsi_slave() | scsi_probe() | probe for a SCSI target |
| selwakeup() | pollwakeup() | inform a process that an event has occurred |
| slaveslot() | ddi_slaveonly() | tell if device is installed in a slave-only slot |
| sleep() | cv_wait() | suspend calling thread and exit mutex atomically |
| spln() | mutex_enter() | set CPU priority level |
splr()
splx() | mutex_exit() | reset priority level |
| splstr() | - | set processor level for STREAMS |
| strcmp() | strcmp() | compare two null-terminated strings |
| strcpy() | strcmp() | copy a string from one location to another |
| suser() | drv_priv() | verify superuser |
| swab() | swab() | swap bytes in 16-bit halfwords |
| testb() | testb() | check for an available buffer |
| timeout() | timeout() | execute a function after a specified length of time |
| uiomove() | uiomove() | copy kernel data using uio(9S) structure |
| unbufcall() | unbufcall() | cancel an outstanding bufcall request |
| unlinkb() | unlinkb() | remove a message block from the head of a message |
| untimeout() | untimeout() | cancel previous timeout function call |
| uprintf() | cmn_err() | kernel print to controlling terminal |
| ureadc() | ureadc() | add character to a uio structure |
-
Table A-1
| SunOS 4.1.x | SunOS 5.5 | Description |
| useracc() | useracc() | verify whether user has access to memory |
| usleep() | drv_usecwait | busy-wait for specified interval |
| uwritec() | uwritec() | remove a character from a uio structure |
| wakeup() | cv_broadcast() | signal condition and wake all blocked threads |
|
|