### Implement tran_start(9E) for SCSI HBA Driver Source: https://illumos.org/books/wdd/scsihba-32898.html This example demonstrates how to implement the tran_start(9E) entry point. It handles command initialization, DMA segment setup for data transfers, and differentiates between interrupt-driven and polled command execution. ```C static int isp_scsi_start( struct scsi_address *ap, struct scsi_pkt *pkt) { struct isp_cmd *sp; struct isp *isp; struct isp_request *req; u_long cur_lbolt; int xfercount; int rval = TRAN_ACCEPT; int i; sp = (struct isp_cmd *)pkt->pkt_ha_private; isp = (struct isp *)ap->a_hba_tran->tran_hba_private; sp->cmd_flags = (sp->cmd_flags & ~CFLAG_TRANFLAG) | CFLAG_IN_TRANSPORT; pkt->pkt_reason = CMD_CMPLT; /* * set up request in cmd_isp_request area so it is ready to * go once we have the request mutex */ req = &sp->cmd_isp_request; req->req_header.cq_entry_type = CQ_TYPE_REQUEST; req->req_header.cq_entry_count = 1; req->req_header.cq_flags = 0; req->req_header.cq_seqno = 0; req->req_reserved = 0; req->req_token = (opaque_t)sp; req->req_target = TGT(sp); req->req_lun_trn = LUN(sp); req->req_time = pkt->pkt_time; ISP_SET_PKT_FLAGS(pkt->pkt_flags, req->req_flags); /* * Set up data segments for dma transfers. */ if (sp->cmd_flags & CFLAG_DMAVALID) { if (sp->cmd_flags & CFLAG_CMDIOPB) { (void) ddi_dma_sync(sp->cmd_dmahandle, sp->cmd_dma_offset, sp->cmd_dma_len, DDI_DMA_SYNC_FORDEV); } ASSERT(sp->cmd_cookiecnt > 0 && sp->cmd_cookiecnt <= ISP_NDATASEGS); xfercount = 0; req->req_seg_count = sp->cmd_cookiecnt; for (i = 0; i < sp->cmd_cookiecnt; i++) { req->req_dataseg[i].d_count = sp->cmd_dmacookies[i].dmac_size; req->req_dataseg[i].d_base = sp->cmd_dmacookies[i].dmac_address; xfercount += sp->cmd_dmacookies[i].dmac_size; } for (; i < ISP_NDATASEGS; i++) { req->req_dataseg[i].d_count = 0; req->req_dataseg[i].d_base = 0; } pkt->pkt_resid = xfercount; if (sp->cmd_flags & CFLAG_DMASEND) { req->req_flags |= ISP_REQ_FLAG_DATA_WRITE; } else { req->req_flags |= ISP_REQ_FLAG_DATA_READ; } } else { req->req_seg_count = 0; req->req_dataseg[0].d_count = 0; } /* * Set up cdb in the request */ req->req_cdblen = sp->cmd_cdblen; bcopy((caddr_t)pkt->pkt_cdbp, (caddr_t)req->req_cdb, sp->cmd_cdblen); /* * Start the cmd. If NO_INTR, must poll for cmd completion. */ if ((pkt->pkt_flags & FLAG_NOINTR) == 0) { mutex_enter(ISP_REQ_MUTEX(isp)); rval = isp_i_start_cmd(isp, sp); mutex_exit(ISP_REQ_MUTEX(isp)); } else { rval = isp_i_polled_cmd_start(isp, sp); } return (rval); } ``` -------------------------------- ### Initialize Driver Module with _init Source: https://illumos.org/books/wdd/autoconf-17.html Example of the _init(9E) routine used to initialize global driver state and install the module into the kernel. ```C static void *xxstatep; int _init(void) { int error; const int max_instance = 20; ddi_soft_state_init(&xxstatep, sizeof (struct xxstate), max_instance); error = mod_install(&xxmodlinkage); if (error != 0) { ddi_soft_state_fini(&xxstatep); } return (error); } ``` -------------------------------- ### Install SCSI HBA Driver via Command Line Source: https://illumos.org/books/wdd/scsihba-32898.html Example command to install a SCSI HBA driver using add_drv with specific class and hardware identification properties. ```Shell add_drv -m" * 0666 root root" -i'"pci1077,1020"' -c scsi isp ``` -------------------------------- ### Example: Allocating IOPB Memory and DMA Resources Source: https://illumos.org/books/wdd/dma-29901.html This example demonstrates allocating memory for an I/O Parameter Block (IOPB) and the necessary DMA resources. It first calls ddi_dma_mem_alloc with DDI_DMA_CONSISTENT and then uses ddi_dma_addr_bind_handle to map the allocated memory for DMA access. Error handling and freeing resources on failure are included. ```c if (ddi_dma_mem_alloc(xsp->iopb_handle, size, &accattr, DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &xsp->iopb_array, &real_length, &xsp->acchandle) != DDI_SUCCESS) { /* error handling */ goto failure; } if (ddi_dma_addr_bind_handle(xsp->iopb_handle, NULL, xsp->iopb_array, real_length, DDI_DMA_READ | DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &cookie, &count) != DDI_DMA_MAPPED) { /* error handling */ ddi_dma_mem_free(&xsp->acchandle); goto failure; } ``` -------------------------------- ### DMA Callback Example Function (C) Source: https://illumos.org/books/wdd/dma-29901.html An example of a DMA callback function (`xxstart`) used for handling DMA resource allocation. It checks for busy status, sets transfer flags, attempts to bind the DMA handle using `ddi_dma_buf_bind_handle`, and returns `DDI_DMA_CALLBACK_RUNOUT` if allocation fails, or `DDI_DMA_CALLBACK_DONE` on success. ```c static int xxstart(caddr_t arg) { struct xxstate *xsp = (struct xxstate *)arg; struct device_reg *regp; int flags; mutex_enter(&xsp->mu); if (xsp->busy) { /* transfer in progress */ mutex_exit(&xsp->mu) return (DDI_DMA_CALLBACK_RUNOUT); } xsp->busy = 1; regp = xsp->regp; if ( /* transfer is a read */ ) { flags = DDI_DMA_READ; } else { flags = DDI_DMA_WRITE; } mutex_exit(&xsp->mu); if (ddi_dma_buf_bind_handle(xsp->handle,xsp->bp,flags, xxstart, (caddr_t)xsp, &cookie, &ccount) != DDI_DMA_MAPPED) { /* really should check all return values in a switch */ mutex_enter(&xsp->mu); xsp->busy=0; mutex_exit(&xsp->mu); return (DDI_DMA_CALLBACK_RUNOUT); } /* Program the DMA engine. */ return (DDI_DMA_CALLBACK_DONE); } ``` -------------------------------- ### Implement tran_tgt_init Entry Point Source: https://illumos.org/books/wdd/scsihba-32898.html Provides an example of the tran_tgt_init entry point for SCSA HBA drivers, used to validate target addresses and initialize per-target resources. ```C static int isp_tran_tgt_init( dev_info_t *hba_dip, dev_info_t *tgt_dip, scsi_hba_tran_t *tran, struct scsi_device *sd) { return ((sd->sd_address.a_target < N_ISP_TARGETS_WIDE && sd->sd_address.a_lun < 8) ? DDI_SUCCESS : DDI_FAILURE); } ``` -------------------------------- ### Driver Compilation and Installation Commands Source: https://illumos.org/books/wdd/ldi-1.html Shell commands for compiling the driver object, linking the binary, installing configuration files, and loading the driver into the kernel. ```bash # Compile for SPARC cc -c -D_KERNEL -xarch=v9 lyr.c # Compile for 32-bit x86 cc -c -D_KERNEL lyr.c # Link the driver ld -r -o lyr lyr.o # Install configuration and binary cp lyr.conf /usr/kernel/drv cp lyr /usr/kernel/drv/sparcv9 # Load the driver add_drv lyr ``` -------------------------------- ### Implement devmap_contextmgt for device context management Source: https://illumos.org/books/wdd/devcnmgt-19679.html A complete implementation example of devmap_contextmgt. It demonstrates how to unload the current mapping, save/restore hardware context, and load the new mapping. ```C static int xxdevmap_contextmgt(devmap_cookie_t handle, void *devprivate, offset_t off, size_t len, uint_t type, uint_t rw) { int error; struct xxctx *ctxp = devprivate; struct xxstate *xsp = ctxp->xsp; mutex_enter(&xsp->ctx_lock); /* unload mapping for current context */ if (xsp->current_ctx != NULL) { if ((error = devmap_unload(xsp->current_ctx->handle, off, len)) != 0) { xsp->current_ctx = NULL; mutex_exit(&xsp->ctx_lock); return (error); } } /* Switch device context - device dependent */ if (xxctxsave(xsp->current_ctx, off, len) < 0) { xsp->current_ctx = NULL; mutex_exit(&xsp->ctx_lock); return (-1); } if (xxctxrestore(ctxp, off, len) < 0){ xsp->current_ctx = NULL; mutex_exit(&xsp->ctx_lock); return (-1); } xsp->current_ctx = ctxp; /* establish mapping for new context and return */ error = devmap_load(handle, off, len, type, rw); if (error) xsp->current_ctx = NULL; mutex_exit(&xsp->ctx_lock); return (error); } ``` -------------------------------- ### Starting the First Data Transfer from a Queue (C) Source: https://illumos.org/books/wdd/block-34861.html This C code snippet implements the start routine for a block driver, responsible for dequeuing and initiating data transfers. It checks if the transfer list is empty or if the device is busy before proceeding. If a transfer can begin, it marks the device as busy, retrieves the next buffer from the head of the list, updates the list pointers, and then initiates the hardware transfer by writing to device registers. ```c static int xxstart(caddr_t arg) { struct xxstate *xsp = (struct xxstate *)arg; struct buf *bp; mutex_enter(&xsp->mu); /* * If there is nothing more to do, or the device is * busy, return. */ if (xsp->list_head == NULL || xsp->busy) { mutex_exit(&xsp->mu); return (0); } xsp->busy = 1; /* Get the first buffer off the transfer list */ bp = xsp->list_head; /* Update the head and tail pointer */ xsp->list_head = xsp->list_head->av_forw; if (xsp->list_head == NULL) xsp->list_tail = NULL; bp->av_forw = NULL; mutex_exit(&xsp->mu); /* * If the device has power manageable components, * mark the device busy with pm_busy_components(9F), * and then ensure that the device * is powered up by calling pm_raise_power(9F). * * Set up DMA resources with ddi_dma_alloc_handle(9F) and * ddi_dma_buf_bind_handle(9F). */ xsp->bp = bp; ddi_put32(xsp->data_access_handle, &xsp->regp->dma_addr, cookie.dmac_address); ddi_put32(xsp->data_access_handle, &xsp->regp->dma_size, (uint32_t)cookie.dmac_size); ddi_put8(xsp->data_access_handle, &xsp->regp->csr, ENABLE_INTERRUPTS | START_TRANSFER); return (0); } ``` -------------------------------- ### Implement Fault Management Initialization in a Driver Source: https://illumos.org/books/wdd/gevsi.html This example demonstrates how the bge driver initializes fault management by checking capabilities and calling ddi_fm_init, followed by setting up error reporting and callbacks. ```C static void bge_fm_init(bge_t *bgep) { ddi_iblock_cookie_t iblk; /* Only register with IO Fault Services if we have some capability */ if (bgep->fm_capabilities) { bge_reg_accattr.devacc_attr_access = DDI_FLAGERR_ACC; bge_desc_accattr.devacc_attr_access = DDI_FLAGERR_ACC; dma_attr.dma_attr_flags = DDI_DMA_FLAGERR; /* * Register capabilities with IO Fault Services */ ddi_fm_init(bgep->devinfo, &bgep->fm_capabilities, &iblk); /* * Initialize pci ereport capabilities if ereport capable */ if (DDI_FM_EREPORT_CAP(bgep->fm_capabilities) || DDI_FM_ERRCB_CAP(bgep->fm_capabilities)) pci_ereport_setup(bgep->devinfo); /* * Register error callback if error callback capable */ if (DDI_FM_ERRCB_CAP(bgep->fm_capabilities)) ddi_fm_handler_register(bgep->devinfo, bge_fm_error_cb, (void*) bgep); } else { /* * These fields have to be cleared of FMA if there are no * FMA capabilities at runtime. */ bge_reg_accattr.devacc_attr_access = DDI_DEFAULT_ACC; bge_desc_accattr.devacc_attr_access = DDI_DEFAULT_ACC; dma_attr.dma_attr_flags = 0; } } ``` -------------------------------- ### Retrieve Module Information with _info Source: https://illumos.org/books/wdd/autoconf-17.html Example of the _info(9E) routine used to return module information to the system. ```C int _info(struct modinfo *modinfop) { return (mod_info(&xxmodlinkage, modinfop)); } ``` -------------------------------- ### Display System Configuration and Device Usage with prtconf Source: https://illumos.org/books/wdd/ldi-1.html Examples of using the prtconf command to inspect device paths, ancestor nodes, child nodes, and detailed device layering information. ```shell prtconf /dev/cfg/c0 prtconf -a /dev/cfg/c0 prtconf -c /dev/cfg/c0 prtconf -v /dev/kbd prtconf -v /dev/iprb0 ``` -------------------------------- ### segmap(9E) Example: Adjusting for Non-Page-Aligned Buffers Source: https://illumos.org/books/wdd/devmap-24338.html An example C implementation of the segmap(9E) entry point that handles non-page-aligned device buffers. It uses ddi_devmap_segmap(9F) to get a page-aligned address and then adjusts it by adding a buffer offset to return the correct address for the start of the buffer. ```c #define BUFFER_OFFSET 0x800 int xx_segmap(dev_t dev, off_t off, ddi_as_handle_t as, caddr_t *addrp, off_t len, uint_t prot, uint_t maxprot, uint_t flags, cred_t *credp) { int rval; unsigned long pagemask = ptob(1L) - 1L; if ((rval = ddi_devmap_segmap(dev, off, as, addrp, len, prot, maxprot, flags, credp)) == DDI_SUCCESS) { /* * The address returned by ddi_devmap_segmap is the start of the page * that contains the buffer. Add the offset of the buffer to get the * final address. */ *addrp += BUFFER_OFFSET & pagemask); } return (rval); } ``` -------------------------------- ### Implement devmap(9E) Entry Point with Context Management Source: https://illumos.org/books/wdd/devcnmgt-19679.html This snippet demonstrates how to define a devmap_callback_ctl structure and implement the devmap(9E) entry point to associate device memory with a user mapping using devmap_devmem_setup. ```C static struct devmap_callback_ctl xx_callback_ctl = { DEVMAP_OPS_REV, xxdevmap_map, xxdevmap_access, xxdevmap_dup, xxdevmap_unmap }; static int xxdevmap(dev_t dev, devmap_cookie_t handle, offset_t off, size_t len, size_t *maplen, uint_t model) { struct xxstate *xsp; uint_t rnumber; int error; /* Setup data access attribute structure */ struct ddi_device_acc_attr xx_acc_attr = { DDI_DEVICE_ATTR_V0, DDI_NEVERSWAP_ACC, DDI_STRICTORDER_ACC }; xsp = ddi_get_soft_state(statep, getminor(dev)); if (xsp == NULL) return (ENXIO); len = ptob(btopr(len)); rnumber = 0; /* Set up the device mapping */ error = devmap_devmem_setup(handle, xsp->dip, &xx_callback_ctl, rnumber, off, len, PROT_ALL, 0, &xx_acc_attr); *maplen = len; return (error); } ``` -------------------------------- ### GLD Management Functions Source: https://illumos.org/books/wdd/idx.html This section details the functions used for managing the GLD (Generic LAN Driver) interface, including reset, send, set MAC address, multicast, promiscuous mode, start, stop, and get statistics. ```APIDOC ## GLD Management Functions ### Description Provides functions to manage the Generic LAN Driver (GLD) interface, including initialization, configuration, and data transmission. ### Method Various (typically internal kernel calls, not exposed as HTTP endpoints) ### Endpoint N/A ### Parameters #### Path Parameters N/A #### Query Parameters N/A #### Request Body N/A ### Request Example N/A ### Response #### Success Response (200) N/A #### Response Example N/A ## Specific GLD Functions: - **`gldm_reset`**: Resets the GLD interface. - **`gldm_send`**: Sends data packets through the GLD interface. - **`gldm_set_mac_addr`**: Sets the MAC address for the GLD interface. - **`gldm_set_multicast`**: Configures multicast address filtering for the GLD interface. - **`gldm_set_promiscuous`**: Enables or disables promiscuous mode for the GLD interface. - **`gldm_start`**: Starts the GLD interface. - **`gldm_stop`**: Stops the GLD interface. - **`gldm_get_stats`**: Retrieves statistics for the GLD interface. ``` -------------------------------- ### Implement Block Driver open(9E) Entry Point in C Source: https://illumos.org/books/wdd/block-34861.html Demonstrates the open(9E) entry point for a block driver, handling device access checks, exclusive opens, and different open types (block vs. layered). It uses soft state and mutexes for managing device state. ```c static int xxopen(dev_t *devp, int flags, int otyp, cred_t *credp) { minor_t instance; struct xxstate *xsp; instance = getminor(*devp); xsp = ddi_get_soft_state(statep, instance); if (xsp == NULL) return (ENXIO); mutex_enter(&xsp->mu); /* * only honor FEXCL. If a regular open or a layered open * is still outstanding on the device, the exclusive open * must fail. */ if ((flags & FEXCL) && (xsp->open || xsp->nlayered)) { mutex_exit(&xsp->mu); return (EAGAIN); } switch (otyp) { case OTYP_LYR: xsp->nlayered++; break; case OTYP_BLK: xsp->open = 1; break; default: mutex_exit(&xsp->mu); return (EINVAL); } mutex_exit(&xsp->mu); return (0); } ``` -------------------------------- ### Registering USB Drivers with add_drv Source: https://illumos.org/books/wdd/usb-1.html Examples of using the add_drv command to bind a driver to a specific device or a class of devices based on compatible name strings. The -n flag is used to prevent the system from automatically reconfiguring the driver during installation. ```shell add_drv -n -i '"usb430,100.102"' specific_mouse_driver add_drv -n -i '"usbif430,class3.1.2"' more_generic_mouse_driver ``` -------------------------------- ### Character-Oriented DMA Strategy Routine (C) Source: https://illumos.org/books/wdd/character-21002.html Example of a `xxstrategy` routine for a character-oriented DMA device. It retrieves the minor device number, gets the soft state, and prepares for a synchronous data transfer by programming the device register. It also handles power management by marking the device busy and ensuring it's powered up. DMA resources are set up using `ddi_dma_alloc_handle` and `ddi_dma_buf_bind_handle`. ```c static int xxstrategy(struct buf *bp) { minor_t instance; struct xxstate *xsp; ddi_dma_cookie_t cookie; instance = getminor(bp->b_edev); xsp = ddi_get_soft_state(statep, instance); /* ... */ * If the device has power manageable components, * mark the device busy with pm_busy_components(9F), * and then ensure that the device is * powered up by calling pm_raise_power(9F). */ /* Set up DMA resources with ddi_dma_alloc_handle(9F) and * ddi_dma_buf_bind_handle(9F). */ xsp->bp = bp; /* remember bp */ /* Program DMA engine and start command */ return (0); } ``` -------------------------------- ### Define DMA Attributes for SBus and ISA Bus Source: https://illumos.org/books/wdd/dma-29901.html Examples of initializing the ddi_dma_attr_t structure for specific hardware constraints. The SBus example targets SPARC architectures, while the ISA example targets x86 systems with specific memory boundary and scatter-gather requirements. ```C /* SBus DMA Attributes */ static ddi_dma_attr_t sbus_attributes = { DMA_ATTR_V0, 0xFF000000, 0xFFFFFFFF, 0xFFFFFFFF, 1, 0x7, 0x1, 0xFFFFFFFF, 0xFFFFFFFF, 1, 512, 0 }; /* ISA Bus DMA Attributes */ static ddi_dma_attr_t isa_attributes = { DMA_ATTR_V0, 0x00000000, 0x00FFFFFF, 0xFFFF, 1, 0x7, 0x1, 0xFFFFFFFF, 0x000FFFFF, 17, 512, 0 }; ``` -------------------------------- ### gldm_start Entry Point Source: https://illumos.org/books/wdd/gld-1.html The `gldm_start` entry point enables the network device to generate interrupts and prepares the driver to deliver received data packets to the GLD module using `gld_recv`. ```APIDOC ## gldm_start Entry Point ### Description Enables the device to generate interrupts and prepares the driver to call `gld_recv` to deliver received data packets to GLD. ### Method (Not applicable, this is a function pointer) ### Endpoint (Not applicable, this is a function pointer) ### Parameters #### Path Parameters (None) #### Query Parameters (None) #### Request Body (None) ### Request Example ```c int _prefix__start(gld_mac_info_t *_macinfo_); ``` ### Response #### Success Response (0) Returns `GLD_SUCCESS` (typically 0) on successful start. #### Response Example ```c 0 ``` ``` -------------------------------- ### Interrupt Function Examples Source: https://illumos.org/books/wdd/interrupt-15678.html Examples demonstrating the usage of DDI functions for managing soft interrupt priority, checking for pending interrupts, and setting/clearing interrupt masks. ```APIDOC ## Interrupt Function Examples ### Description This section provides examples for performing the following tasks: * Changing soft interrupt priority * Checking for pending interrupts * Setting interrupt masks * Clearing interrupt masks ### Changing Soft Interrupt Priority Use the `ddi_intr_set_softint_pri(9F)` function to change the soft interrupt priority. ```c if (ddi_intr_set_softint_pri(mydev->mydev_softint_hdl, 9) != DDI_SUCCESS) cmn_err (CE_WARN, "ddi_intr_set_softint_pri failed"); ``` ### Checking for Pending Interrupts Use the `ddi_intr_get_pending(9F)` function to check whether an interrupt is pending. ```c if (ddi_intr_get_pending(mydevp->htable[0], &pending) != DDI_SUCCESS) cmn_err(CE_WARN, "ddi_intr_get_pending() failed"); else if (pending) cmn_err(CE_NOTE, "ddi_intr_get_pending(): Interrupt pending"); ``` ### Setting Interrupt Masks Use the `ddi_intr_set_mask(9F)` function to set interrupt masking to prevent the device from receiving interrupts. ```c if ((ddi_intr_set_mask(mydevp->htable[0]) != DDI_SUCCESS)) cmn_err(CE_WARN, "ddi_intr_set_mask() failed"); ``` ### Clearing Interrupt Masks Use the `ddi_intr_clr_mask(9F)` function to clear interrupt masking. This function fails if the specified interrupt is not enabled. If it succeeds, the device starts generating interrupts. ```c if (ddi_intr_clr_mask(mydevp->htable[0]) != DDI_SUCCESS) cmn_err(CE_WARN, "ddi_intr_clr_mask() failed"); ``` ``` -------------------------------- ### Implement SCSI HBA Module Initialization and Lifecycle Source: https://illumos.org/books/wdd/scsihba-32898.html Demonstrates the dev_ops structure and the _init and _fini entry points for a SCSI HBA driver. These functions manage global mutexes, soft-state initialization, and registration with the SCSI HBA framework. ```c static struct dev_ops isp_dev_ops = { DEVO_REV, /* devo_rev */ 0, /* refcnt */ isp_getinfo, /* getinfo */ nulldev, /* probe */ isp_attach, /* attach */ isp_detach, /* detach */ nodev, /* reset */ NULL, /* driver operations */ NULL, /* bus operations */ isp_power, /* power management */ }; static kmutex_t isp_global_mutex; static void *isp_state; int _init(void) { int err; if ((err = ddi_soft_state_init(&isp_state, sizeof (struct isp), 0)) != 0) { return (err); } if ((err = scsi_hba_init(&modlinkage)) == 0) { mutex_init(&isp_global_mutex, "isp global mutex", MUTEX_DRIVER, NULL); if ((err = mod_install(&modlinkage)) != 0) { mutex_destroy(&isp_global_mutex); scsi_hba_fini(&modlinkage); ddi_soft_state_fini(&isp_state); } } return (err); } int _fini(void) { int err; if ((err = mod_remove(&modlinkage)) == 0) { mutex_destroy(&isp_global_mutex); scsi_hba_fini(&modlinkage); ddi_soft_state_fini(&isp_state); } return (err); } ``` -------------------------------- ### Installing Driver with add_drv (Shell) Source: https://illumos.org/books/wdd/loading-15035.html Installs a device driver into the system using the `add_drv` command. If successful, it automatically runs `devfsadm` to create device nodes in `/dev`. This command can also accept aliases for the device. ```shell # add_drv _xx_ ``` -------------------------------- ### segmap(9E) Example: Restricting Write Access Source: https://illumos.org/books/wdd/devmap-24338.html An example C implementation of the segmap(9E) entry point that restricts write access to a frame buffer device. It checks if read access is requested and returns EINVAL if it is, otherwise it proceeds with the mapping using ddi_devmap_segmap(9F). ```c static int xxsegmap(dev_t dev, off_t off, struct as *asp, caddr_t *addrp, off_t len, unsigned int prot, unsigned int maxprot, unsigned int flags, cred_t *credp) { if (prot & PROT_READ) return (EINVAL); return (ddi_devmap_segmap(dev, off, as, addrp, len, prot, maxprot, flags, cred)); } ``` -------------------------------- ### devmap_map Entry Point Implementation (C) Source: https://illumos.org/books/wdd/devcnmgt-19679.html An example implementation of the devmap_map(9E) entry point for a device driver. This function is responsible for allocating and initializing context structures for device mappings, handling both private and shared contexts, and setting context timeouts. It returns a pointer to the allocated private data. ```c static int int xxdevmap_map(devmap_cookie_t handle, dev_t dev, uint_t flags, offset_t offset, size_t len, void **new_devprivate) { struct xxstate *xsp = ddi_get_soft_state(statep, getminor(dev)); struct xxctx *newctx; /* create a new context structure */ newctx = kmem_alloc(sizeof (struct xxctx), KM_SLEEP); newctx->xsp = xsp; newctx->handle = handle; newctx->offset = offset; newctx->flags = flags; newctx->len = len; mutex_enter(&xsp->ctx_lock); if (flags & MAP_PRIVATE) { /* allocate a private context and initialize it */ newctx->context = kmem_alloc(XXCTX_SIZE, KM_SLEEP); xxctxinit(newctx); } else { /* set a pointer to the shared context */ newctx->context = xsp->ctx_shared; } mutex_exit(&xsp->ctx_lock); /* give at least 1 ms access before context switching */ devmap_set_ctx_timeout(handle, drv_usectohz(1000)); /* return the context structure */ *new_devprivate = newctx; return(0); } ``` -------------------------------- ### Add Driver Postinstall Script (Shell) Source: https://illumos.org/books/wdd/loading-15035.html This shell script is executed after a package containing a driver binary is installed. It uses the `add_drv` command to complete the driver installation, touching `/reconfigure` for a reconfigure boot. It handles different scenarios for running systems versus client systems. ```shell #!/bin/sh # # @(#)postinstall 1.1 PATH="/usr/bin:/usr/sbin:${PATH}" export PATH # # Driver info # DRV= DRVALIAS="," DRVPERM='* 0666 root sys' ADD_DRV=/usr/sbin/add_drv # # Select the correct add_drv options to execute. # add_drv touches /reconfigure to cause the # next boot to be a reconfigure boot. # if [ "${BASEDIR}" = "/" ]; then # # On a running system, modify the # system files and attach the driver # ADD_DRV_FLAGS="" else # # On a client, modify the system files # relative to BASEDIR # ADD_DRV_FLAGS="-b ${BASEDIR}" fi # # Make sure add_drv has not been previously executed # before attempting to add the driver. # grep "^${DRV} " $BASEDIR/etc/name_to_major > /dev/null 2>&1 if [ $? -ne 0 ]; then ${ADD_DRV} ${ADD_DRV_FLAGS} -m "${DRVPERM}" -i "${DRVALIAS}" ${DRV} if [ $? -ne 0 ]; then echo "postinstall: add_drv $DRV failed\n" >&2 exit 1 fi fi exit 0 ``` -------------------------------- ### Interrupt Service Routine (ISR) Example in C Source: https://illumos.org/books/wdd/interrupt-15678.html An example of an interrupt service routine (ISR) for a device named 'mydev'. It demonstrates how to claim or reject an interrupt, read device status, inform the device of servicing, and re-enable interrupts. This code assumes specific device register layouts and bitmasks. ```c static uint_t mydev_intr(caddr_t arg1, caddr_t arg2) { struct mydevstate *xsp = (struct mydevstate *)arg1; uint8_t status; volatile uint8_t temp; /* * Claim or reject the interrupt. This example assumes * that the device's CSR includes this information. */ mutex_enter(&xsp->high_mu); /* use data access routines to read status */ status = ddi_get8(xsp->data_access_handle, &xsp->regp->csr); if (!(status & INTERRUPTING)) { mutex_exit(&xsp->high_mu); return (DDI_INTR_UNCLAIMED); /* dev not interrupting */ } /* * Inform the device that it is being serviced, and re-enable * interrupts. The example assumes that writing to the * CSR accomplishes this. The driver must ensure that this data * access operation makes it to the device before the interrupt * service routine returns. For example, using the data access * functions to read the CSR, if it does not result in unwanted * effects, can ensure this. */ ddi_put8(xsp->data_access_handle, &xsp->regp->csr, CLEAR_INTERRUPT | ENABLE_INTERRUPTS); /* flush store buffers */ temp = ddi_get8(xsp->data_access_handle, &xsp->regp->csr); mutex_exit(&xsp->mu); return (DDI_INTR_CLAIMED); } ``` -------------------------------- ### Implement attach(9E) with DDI_RESUME Source: https://illumos.org/books/wdd/powermgt-37437.html Demonstrates the attach(9E) entry point implementation to handle system resume events. It manages device state restoration, timeout handling, and component power level notification. ```C int xxattach(devinfo_t *dip, ddi_attach_cmd_t cmd) { struct xxstate *xsp; int instance; instance = ddi_get_instance(dip); xsp = ddi_get_soft_state(statep, instance); switch (cmd) { case DDI_ATTACH: /* ... */ case DDI_RESUME: mutex_enter(&xsp->mu); if (xsp->xx_pm_state_saved) { /* Restore device register contents */ } xsp->xx_timeout_id = timeout( /* ... */ ); xsp->xx_suspended = 0; cv_broadcast(&xsp->xx_suspend_cv); for (i = 0; i < num_components; i++) { xsp->xx_power_level[i] = xx_get_power_level(dip, i); if (xsp->xx_power_level[i] != XX_LEVEL_UNKNOWN) (void) pm_power_has_changed(dip, i, xsp->xx_power_level[i]); } mutex_exit(&xsp->mu); return(DDI_SUCCESS); default: return(DDI_FAILURE); } } ``` -------------------------------- ### Create Alternate Kernel for Testing Source: https://illumos.org/books/wdd/debug-60.html To avoid data loss and system reinstallation due to driver bugs, create a backup copy of the kernel and associated binaries. This involves copying the kernel directory and placing new driver modules in the copied directory before booting the alternate kernel. ```shell # `cp -r /platform/`uname -i`/kernel /platform/`uname -i`/kernel.test` ``` -------------------------------- ### GET ddi_dma_burstsizes Source: https://illumos.org/books/wdd/dma-29901.html Retrieves the allowed burst size bitmap for a DMA handle after resources have been allocated. ```APIDOC ## ddi_dma_burstsizes(9F) ### Description Returns the appropriate burst size bitmap for a device based on the DMA resources allocated to the handle. Drivers use this to determine which burst sizes are supported by the system for their DMA engine. ### Method Function Call ### Parameters #### Arguments - **handle** (ddi_dma_handle_t) - Required - The DMA handle returned from ddi_dma_alloc_handle(9F). ### Request Example ```c burst = ddi_dma_burstsizes(xsp->handle); ``` ### Response #### Success Response - **return_value** (uint_t) - A bitmap representing the allowed burst sizes for the DMA engine. #### Response Example ```c /* Example check for 32-byte burst size */ #define BEST_BURST_SIZE 0x20 if (burst & BEST_BURST_SIZE) { /* Program DMA engine to use 32 bytes */ } ``` ``` -------------------------------- ### Define devmap_contextmgt entry point Source: https://illumos.org/books/wdd/devcnmgt-19679.html The syntax for the devmap_contextmgt(9E) entry point. It requires a handle, private device data, offset, length, access type, and read/write flags. ```C int xxdevmap_contextmgt(devmap_cookie_t handle, void *devprivate, offset_t offset, size_t len, uint_t type, uint_t rw); ``` -------------------------------- ### Initialize DMA Transfer with Partial Windows in C Source: https://illumos.org/books/wdd/dma-29901.html Demonstrates how to initiate a DMA transfer using the DDI_DMA_PARTIAL flag. It checks the return status to determine if the system allocated resources for the entire object or established partial DMA windows. ```C static int xxstart (caddr_t arg) { struct xxstate *xsp = (struct xxstate *)arg; struct device_reg *regp = xsp->reg; ddi_dma_cookie_t cookie; int status; mutex_enter(&xsp->mu); if (xsp->busy) { mutex_exit(&xsp->mu); return (DDI_DMA_CALLBACK_RUNOUT); } xsp->busy = 1; mutex_exit(&xsp->mu); if ( /* transfer is a read */) { flags = DDI_DMA_READ; } else { flags = DDI_DMA_WRITE; } flags |= DDI_DMA_PARTIAL; status = ddi_dma_buf_bind_handle(xsp->handle, xsp->bp, flags, xxstart, (caddr_t)xsp, &cookie, &ccount); if (status != DDI_DMA_MAPPED && status != DDI_DMA_PARTIAL_MAP) return (DDI_DMA_CALLBACK_RUNOUT); if (status == DDI_DMA_PARTIAL_MAP) { ddi_dma_numwin(xsp->handle, &xsp->nwin); xsp->partial = 1; xsp->windex = 0; } else { xsp->partial = 0; } /* Program the DMA engine. */ return (DDI_DMA_CALLBACK_DONE); } ``` -------------------------------- ### Implement dump(9E) Entry Point for System Dumps (C) Source: https://illumos.org/books/wdd/scsi-36812.html The dump(9E) entry point copies a portion of virtual address space directly to a specified device. It is used for system failure or checkpoint operations and must operate without interrupts. This C function demonstrates the core logic for handling dump operations, including buffer management and SCSI command setup. ```c static int xxdump(dev_t dev, caddr_t addr, daddr_t blkno, int nblk) { struct xxstate *xsp; struct buf *bp; struct scsi_pkt *pkt; int rval; int instance; instance = getminor(dev); xsp = ddi_get_soft_state(statep, instance); if (tgt->suspended) { (void) pm_raise_power(DEVINFO(tgt), 0, 1); } bp = getrbuf(KM_NOSLEEP); if (bp == NULL) { return (EIO); } /* Calculate block number relative to partition. */ bp->b_un.b_addr = addr; bp->b_edev = dev; bp->b_bcount = nblk * DEV_BSIZE; bp->b_flags = B_WRITE | B_BUSY; bp->b_blkno = blkno; pkt = scsi_init_pkt(ROUTE(tgt), NULL, bp, CDB_GROUP1, sizeof (struct scsi_arq_status), sizeof (struct bst_pkt_private), 0, NULL_FUNC, NULL); if (pkt == NULL) { freerbuf(bp); return (EIO); } (void) scsi_setup_cdb((union scsi_cdb *)pkt->pkt_cdbp, SCMD_WRITE_G1, blkno, nblk, 0); /* * While dumping in polled mode, other cmds might complete * and these should not be resubmitted. we set the * dumping flag here which prevents requeueing cmds. */ tgt->dumping = 1; rval = scsi_poll(pkt); tgt->dumping = 0; scsi_destroy_pkt(pkt); freerbuf(bp); if (rval != DDI_SUCCESS) { rval = EIO; } return (rval); } ```