Solaris 8 Software Developer Supplement

Entry Point and Service Routines

Arguments Used by GLD Routines

macinfo

Pointer to a gld_mac_info(9S) structure.

macaddr

Pointer to the beginning of a character array containing a valid MAC address. The array will be of the length specified by the driver in the gldm_addrlen element of the gld_mac_info(9S) structure.

multicastaddr

Pointer to the beginning of a character array containing a multicast, group, or functional address. The array will be of the length specified by the driver in the gldm_addrlen element of the gld_mac_info(9S) structure.

multiflag

Flag indicating whether reception of the multicast address is to be enabled or disabled. This argument is specified as GLD_MULTI_ENABLE or GLD_MULTI_DISABLE.

promiscflag

Flag indicating what type of promiscuous mode, if any, is to be enabled. This argument is specified as GLD_MAC_PROMISC_PHYS, GLD_MAC_PROMISC_MULTI, or GLD_MAC_PROMISC_NONE.

mp

gld_ioctl() uses mp as a pointer to a STREAMS message block containing the ioctl to be executed. gld_send() uses it as a pointer to a STREAMS message block containing the packet to be transmitted. gld_recv() uses it as a pointer to a message block containing a received packet.

stats

Pointer to a gld_stats(9S) structure to be filled in with the current values of statistics counters.

q

Pointer to the queue(9S) structure to be used in the reply to the ioctl.

dip

Pointer to the device's dev_info structure.

name

Device interface name.

Entry Points

These entry points must be implemented by a device-specific network driver designed to interface with GLD.

As described in gld(7D), the main data structure for communication between the device-specific driver and the GLD module is the gld_mac_info(9S) structure. Some of the elements in that structure are function pointers to the entry points described here. The device-specific driver must, in its attach(9E) routine, initialize these function pointers before calling gld_register().

int prefix_reset(gld_mac_info_t * macinfo);

gldm_reset() resets the hardware to its initial state.

int prefix_start(gld_mac_info_t * macinfo);

gldm_start() enables the device to generate interrupts and prepares the driver to call gld_recv() for delivering received data packets to GLD.

int prefix_stop(gld_mac_info_t * macinfo);

gldm_stop() disables the device from generating any interrupts and stops the driver from calling gld_recv() for delivering data packets to GLD. GLD depends on the gldm_stop() routine to ensure that the device will no longer interrupt, and it must do so without fail. This function should always return GLD_SUCCESS.

int prefix_set_mac_addr(gld_mac_info_t * macinfo, unsigned char * macaddr);

gldm_set_mac_addr() sets the physical address that the hardware is to use for receiving data. This function should program the device to the passed MAC address macaddr. If sufficient resources are currently not available to carry out the request, return GLD_NORESOURCES. Return GLD_NOTSUPPORTED to indicate that the requested function is not supported.

int prefix_set_multicast(gld_mac_info_t * macinfo, unsigned char * multicastaddr, 
    int multiflag);

gldm_set_multicast() enables and disables device-level reception of specific multicast addresses. If the third argument multiflag is set to GLD_MULTI_ENABLE, then the function sets the interface to receive packets with the multicast address pointed to by the second argument. If multiflag is set to GLD_MULTI_DISABLE, the driver is allowed to disable reception of the specified multicast address.

This function is called whenever GLD wants to enable or disable reception of a multicast, group, or functional address. GLD makes no assumptions about how the device does multicast support and calls this function to enable or disable a specific multicast address. Some devices might use a hash algorithm and a bitmask to enable collections of multicast addresses; this procedure is allowed, and GLD filters out any superfluous packets. If disabling an address could result in disabling more than one address at the device level, it is the responsibility of the device driver to keep whatever information it needs in order to avoid disabling an address that GLD has enabled but not disabled.

gldm_set_multicast() will not be called to enable a particular multicast address that is already enabled, nor will it be called to disable an address that is not currently enabled. GLD keeps track of multiple requests for the same multicast address and only calls the driver's entry point when the first request to enable, or the last request to disable, a particular multicast address is made. If sufficient resources are currently not available to carry out the request, return GLD_NORESOURCES. Return GLD_NOTSUPPORTED to indicate that the requested function is not supported.

int prefix_set_promiscuous(gld_mac_info_t * macinfo, int promiscflag);

gldm_set_promiscuous() enables and disables promiscuous mode. This function is called whenever GLD wants to enable or disable the reception of all packets on the medium, or of all multicast packets on the medium. If the second argument promiscflag is set to the value of GLD_MAC_PROMISC_PHYS, then the function enables physical-level promiscuous mode, resulting in the reception of all packets on the medium. If promiscflag is set to GLD_MAC_PROMISC_MULTI, then reception of all multicast packets will be enabled. If promiscflag is set to GLD_MAC_PROMISC_NONE, then promiscuous mode is disabled.

In the case of a request for promiscuous multicast mode, drivers for devices that have no multicast-only promiscuous mode must set the device to physical promiscuous mode to ensure that all multicast packets are received. In this case the routine should return GLD_SUCCESS. The GLD software filters out any superfluous packets. If sufficient resources are currently not available to carry out the request, return GLD_NORESOURCES. Return GLD_NOTSUPPORTED to indicate that the requested function is not supported.

For forward compatibility, gldm_set_promiscuous() routines should treat any unrecognized values for promiscflag as though they were GLD_MAC_PROMISC_PHYS.

int prefix_send(gld_mac_info_t * macinfo, mblk_t * mp);

gldm_send() queues a packet to the device for transmission. This routine is passed a STREAMS message containing the packet to be sent. The message might include multiple message blocks, and the send routine must traverse all the message blocks in the message to access the entire packet to be sent. The driver should be prepared to handle and skip over any zero-length message continuation blocks in the chain. The driver should check that the packet does not exceed the maximum allowable packet size, and it must pad the packet, if necessary, to the minimum allowable packet size. If the send routine successfully transmits or queues the packet, it should return GLD_SUCCESS.

The send routine should return GLD_NORESOURCES if it cannot immediately accept the packet for transmission; in this case GLD will retry it later. If gldm_send() ever returns GLD_NORESOURCES, the driver must, at a later time when resources have become available, call gld_sched(). This call to gld_sched() informs GLD that it should retry packets that the driver previously failed to queue for transmission. (If the driver's gldm_stop() routine is called, the driver is absolved from this obligation until it later again returns GLD_NORESOURCES from its gldm_send() routine. However, extra calls to gld_sched() will not cause incorrect operation.)

If the driver's send routine returns GLD_SUCCESS, then the driver is responsible for freeing the message when the driver and the hardware no longer need it. If the send routine copied the message into the device, or into a private buffer, then the send routine is permitted to free the message after the copy is made. If the hardware uses DMA to read the data directly out of the message data blocks, then the driver must not free the message until the hardware has completed reading the data. In this case the driver will probably free the message in the interrupt routine, or in a buffer reclaim operation at the beginning of a future send operation. If the send routine returns anything other than GLD_SUCCESS, then the driver must not free the message. Return GLD_NOLINK if gldm_send() is called when there is no physical connection to the network or link partner.

int prefix_intr(gld_mac_info_t * macinfo);

gldm_intr() is called when the device might have interrupted. Because it is possible to share interrupts with other devices, the driver must check the device status to determine whether it actually caused an interrupt. If the device that the driver controls did not cause the interrupt, then this routine must return DDI_INTR_UNCLAIMED. Otherwise, it must service the interrupt and should return DDI_INTR_CLAIMED. If the interrupt was caused by successful receipt of a packet, this routine should put the received packet into a STREAMS message of type M_DATA and pass that message to gld_recv().

gld_recv() will pass the inbound packet upstream to the appropriate next layer of the network protocol stack. It is important to correctly set the b_rptr and b_wptr members of the STREAMS message before calling gld_recv().

The driver should avoid holding mutex or other locks during the call to gld_recv(). In particular, locks that could be taken by a transmit thread must not be held during a call to gld_recv(): the interrupt thread that calls gld_recv() will in some cases carry out processing that includes sending an outgoing packet, resulting in a call to the driver's gldm_send() routine. If the gldm_send() routine were to try to acquire a mutex being held by the gldm_intr() routine at the time it calls gld_recv(), this would result in a panic due to recursive mutex entry. If other driver entry points attempt to acquire a mutex that the driver holds across a call to gld_recv(), deadlock can result.

The interrupt code should increment statistics counters for any errors. This includes failure to allocate a buffer needed for the received data and any hardware-specific errors, such as CRC errors or framing errors.

int prefix_get_stats(gld_mac_info_t * macinfo, struct gld_stats * stats);

gldm_get_stats() gathers statistics from the hardware, driver private counters, or both, and updates the gld_stats(9S) structure pointed to by stats. This routine is called by GLD when it gets a request for statistics, and provides the mechanism by which GLD acquires device-dependent statistics from the driver before composing its reply to the statistics request. See gld_stats(9S) and gld(7D) for a description of the defined statistics counters.

int prefix_ioctl(gld_mac_info_t * macinfo, queue_t * q, mblk_t * mp);

gldm_ioctl() implements any device-specific ioctl commands. This element is allowed to be NULL if the driver does not implement any ioctl functions. The driver is responsible for converting the message block into an ioctl reply message and calling the qreply(9F) function before returning GLD_SUCCESS. This function should always return GLD_SUCCESS; any errors the driver might want to report should be returned by the message passed to qreply(9F). If the gldm_ioctl element is specified as NULL, GLD returns a message of type M_IOCNAK with an error of EINVAL.

Return Values

In addition to the return values described above, and subject to the restrictions above, it is permitted for some of the GLD entry point functions to return these values:

GLD_BADARG

If the function detected an unsuitable argument, for example, a bad multicast address, a bad MAC address, or a bad packet or packet length

GLD_FAILURE

On hardware failure

GLD_SUCCESS

On success

Service Routines

gld_mac_info_t * gld_mac_alloc(dev_info_t * dip);

gld_mac_alloc() allocates a new gld_mac_info(9S) structure and returns a pointer to it. Some of the GLD-private elements of the structure might be initialized before gld_mac_alloc() returns; all other elements are initialized to zero. The device driver must initialize some structure members, as described in gld_mac_info(9S), before passing the pointer to the mac_info structure to gld_register().

void gld_mac_free(gld_mac_info_t * macinfo);

gld_mac_free() frees a gld_mac_info(9S) structure previously allocated by gld_mac_alloc().

int gld_register(dev_info_t * dip, char * name, gld_mac_info_t * macinfo);

gld_register() is called from the device driver's attach(9E) routine and is used to link the GLD-based device driver with the GLD framework. Before calling gld_register(), the device driver's attach(9E) routine must first use gld_mac_alloc() to allocate a gld_mac_info(9S) structure, and initialize several of its structure elements. See gld_mac_info(9S) for more information. A successful call to gld_register() performs the following actions:

The device interface name passed to gld_register() must exactly match the name of the driver module as it exists in the file system.

The driver's attach(9E) routine should return DDI_SUCCESS if gld_register() succeeds. If gld_register() does not return DDI_SUCCESS, the attach(9E) routine should deallocate any resources it allocated before calling gld_register(), and then return DDI_FAILURE.

int gld_unregister(gld_mac_info_t * macinfo);

gld_unregister() is called by the device driver's detach(9E) function, and if successful, performs the following tasks:

If gld_unregister() returns DDI_SUCCESS, the detach(9E) routine should deallocate any data structures allocated in the attach(9E) routine, using gld_mac_free() to deallocate the macinfo structure, and return DDI_SUCCESS. If gld_unregister() does not return DDI_SUCCESS, the driver's detach(9E) routine must leave the device operational and return DDI_FAILURE.

void gld_recv(gld_mac_info_t * macinfo, mblk_t * mp);

gld_recv() is called by the driver's interrupt handler to pass a received packet upstream. The driver must construct and pass a STREAMS M_DATA message containing the raw packet. gld_recv() determines which STREAMS queues, if any, should receive a copy of the packet, duplicating it if necessary. It then formats a DL_UNITDATA_IND message, if required, and passes the data up all appropriate Streams.

The driver should avoid holding mutex or other locks during the call to gld_recv(). In particular, locks that could be taken by a transmit thread must not be held during a call to gld_recv(): the interrupt thread that calls gld_recv() will in some cases carry out processing that includes sending an outgoing packet, resulting in a call to the driver's gldm_send() routine. If the gldm_send() routine were to try to acquire a mutex being held by the gldm_intr() routine at the time it calls gld_recv(), this would result in a panic caused by a recursive mutex entry. If other driver entry points attempt to acquire a mutex that the driver holds across a call to gld_recv(), deadlock can result.

void gld_sched(gld_mac_info_t * macinfo);

gld_sched() is called by the device driver to reschedule stalled outbound packets. Whenever the driver's gldm_send() routine has returned GLD_NORESOURCES, the driver must later call gld_sched() to inform the GLD framework that it should retry the packets that previously could not be sent. gld_sched() should be called as soon as possible after resources are again available, to ensure that GLD resumes passing outbound packets to the driver's gldm_send() routine in a timely way. (If the driver's gldm_stop() routine is called, the driver is absolved from this obligation until it later again returns GLD_NORESOURCES from its gldm_send() routine; however, extra calls to gld_sched() will not cause incorrect operation.)

uint_t gld_intr(caddr_t);

gld_intr() is GLD's main interrupt handler. Normally, gld_intr() is specified as the interrupt routine in the device driver's call to ddi_add_intr(9F). The argument to the interrupt handler (specified as int_handler_arg in the call to ddi_add_intr(9F)) must be a pointer to the gld_mac_info(9S) structure. gld_intr() will, when appropriate, call the device driver's gldm_intr() function, passing that pointer to the gld_mac_info(9S) structure. However, if the driver uses a high-level interrupt, it must provide its own high-level interrupt handler and trigger a soft interrupt from within that. In this case, gld_intr() would normally be specified as the soft interrupt handler in the call to ddi_add_softintr(). gld_intr() will return a value appropriate for an interrupt handler.