OpenSS7: Device Drivers

OpenSS7 | Brian F. G. Bidulock, © Copyright 2000, Brian F. G. Bidulock, All Rights Reserved.
All OpenSS7 website, www.openss7.org resources have provided as a courtesy of the OpenSwitch Project and Herlein Engineering.

# Device Drivers

## Introduction

Writing SS7 device drivers is similar to writing device drivers for serial device drivers for WAN IP interfaces. Many of the facilities of the SS7 interface are borrowed from the Linux IP NET4 distribution by Alan Cox, et al. Some of these facilities are as follows:

- the device structure, `struct device`;
- the device notifier,
- the packet scheduler,
- the packet filter and packet socket,
- the netdevice register,
- socket buffers,

## General Notes

For an example of how to use the facilities of the SS7/LINK ss7 signalling link interface facility, see `acb56.c` in `drivers/net` (or here). The file `acb56.c` is a complete working example of a serial interface card implementation to this driver interface for the Sealevel ACB56 card.

You must use the struct device structure from `include/linux/netdevice.h` in the kernel sources. This device structure is shared by IP and SS7.

## Registration and Deregistration of SS7 Devices

There are three ways to register a device as an SS7 interface and attach a level 2 SS7 link state machine to a device (in order of preference):

1. Fill out your device structure wtih all the necessary interface service routines (as per IP) and set the `dev->type` to `ARPHRD_SS7,` and then register the device with `register_netdevice().` The netdevice notifier will invoke the attach automagically via the `ss7mtp` module.

   This is the preferred method because the netdevice notifier is for notification of other changes to the SS7/MTP protocol layer. In particular, it has the ability to be extended to provide notification of card insertion and removal events for cPCI, and is consistent with the approach used for IP network devices.

2. Fill out your device structure with all necessary interface service routines (as per IP) and call the exported `ss7if_attach()` routine from the `ss7link` module.

   This is a BSD-like approach and is preferrable for device drivers which are attempting to maintain consistency with future BSD implementations of OpenSS7.

3. Fill out an `ss7if_regs` structure with the interface service routines which would normally be in the device structure and call `ss7if_register()` from the `ss7link` module with a master device and the register structure.

   This approach is particularly useful for multichannel devices which do not require or maintain a full device structure per channel.

There are only two ways to detach from an SS7 interface and undo what was done during the attach:

1. Deregister the device with the `unregister_netdevice()` which will automagically detach (from the destructor of the device structure which was set by the attach operation).

2. Call either `ss7if_unregister()` or `ss7if_detach()` (which are really the same function call).

NOTE: devices should be closed with `dev_close()` before calling `unregister_netdevice().` `dev_close()` should not be called on an operating signalling link (you'll get lots of error messages, but the device will close and unregister correctly).

### Driver Service Routines

As shown in Figure 1, below, the SS7 LINK interface attaches itself between the `struct device` provided or allocated for the device driver and the device driver itself. The device driver's private interface service routines are saved in the `struct ss7if_regs` structure in the SS7 LINK state machine and used to invoke the driver. The SS7 MTP or SCCP protocol layers communicate to the SS7 LINK interface through the replaced device service routines.

Figure 1 - Interface Service Routines

Pertinent driver service routines are those routines which are normally completed against the `struct device` structure as follows:

`destructor()` | The device driver can have a private destructor service routine. The SS7 LINK interface will interpose its own destructor service routine which is used by `unregister_netdev` to automagically detach the SS7 LINK interface from the driver. This also results in a call to the driver's private destructor function (if any).
`open()` | The device driver provides its own open service routine. The SS7 LINK interface will interpose its own open service routine which will initialize the L2 state machines associated with the driver.
`stop()` | The device driver provides its own close service routine. The SS7 LINK interface will interpose its own close service routine which will gracefully shut-down the SS7 LINK state machines associated with the driver before calling the driver's private close service routine.
`hard_start_xmit()` | The device driver must provide a hard start transmit service routine. This service routine must follow the behavior specified later under Transmit Interrupt Service Routine. The SS7 LINK interface interposes its own hard start transmit service routine which is responsible for queuing and processing frames and service primitives for processing in the L2 state machine. Only when the L2 state machine deems it necessary to transmit a frame does it call the device's private hard start transmit service routine.
`do_ioctl()` | The device driver may (if necessary) provide its own ioctl service routine. The SS7 LINK interface interposes its own ioctl service routine which is used to control characteristics and attributes of the SS7 LINK state machines; however, this interface will call the device private ioct service routine for all device private ioctls within the private range.
`get_stats()` | The device driver should (if possible) provide its own statistics service routine. The SS7 LINK interposes its own statistics service routine which is responsible for gathering statistics collected by the SS7 LINK state machines on L2 operation. This interposed routine; however, calls the device driver private routine to collect information which is not possible to collect in the L2 state machines (such as compressed FISUs, repeated FISUs, types of errors, etc).

The SS7 LINK interface interposes its own driver service routines by replacing the service routines in the device's `struct device` structure and saving the device drivers private service routines in an interface registration structure (`struct ss7if_regs`) at the time of registration or attachment. At the time that the driver is deregistered or detached from the SS7 LINK interface, these driver service routines are reinstated back into the device driver's `struct device` structure and the internal registration structure and associated link state machine structures are freed.

## Receive Interrupt Service Routine Interface

When delivering received packets in the driver, the driver should not call `netif_rx()` as it would in the IP case, but, instead, should call `ss7if_rx()` so that the SS7 L2 state machine has a crack at the packet. Failing to do so will result in erroneous operation. `ss7if_rx()` processes each frame through the L2 state machines which is part of the `ss7link` module, before ultimately delivering each packet up using the actual `netif_rx()` call.

Please see the `acb56.c` file in `drivers/net` for more detail.

### Rx ISR API

Following is the Rx ISR API to the SS7 LINK state machines. There are four functions which must be called in different circumstances and have different behaviors which must be followed in the driver. These are a diversion from the calls which one would normally see in an IP WAN driver. To summarize, they are:

`ss7if_rx()` |  | Called with an `sk_buff` when the driver has a valid received frame. A valid received frame is a frame which was received without error and is between 3 and `dev->mtu` in length and has a valid length indicator in the frame. If the driver is in octet counting mode, it must stop octet counting. The `sk_buff` must have `skb->dev,` `skb->protocol,` `skb->pkt_type` and `skb->protocol` appropriately set. That is, `skb->dev` is set to the device structure of the receiving device, `skb->protocol` is set to `AF_SS7,` `skb->pkt_type` is set to `ETH_P_SS7,` and `skb->priority` is set to `TC_PRIO_BESTEFFORT.`
`ss7if_los()` |  | Called with the device pointer when the driver loses flag syncrhonization on the line. The driver must enter octet counting mode after sending this indication.
`ss7if_err()` |  | Called with the device pointer when the driver detects frame-related error such as an abort, residue error, framing error, CRC error. The driver must enter octet counting mode after sending this indication.
`ss7if_oct()` |  | Called wtih the device pointer when the driver is in octet counting mode. It must be called every 16 octet times (128 bit times) when the driver is in octet counting mode.
`ss7if_rpt()` |  | Called with the device pointer when the driver receives a repeat of the last received FISU or LSSU (while not in octet counting mode). This is used because the driver has extremely critical timings while the line is idling FISUs or LSSUs. The state machine does not do its own FISU/LSSU compression, it must be done in the driver.

All in all, these calls to the SS7 interface are sufficient to meet the requirements of the SS7 Level 2 driver interface and provide full SS7 Level 2 capabilities.

The driver receivers can be in one of three states as illustrated in Figure 2, below. The driver receiver must move throught the proscribed states in the proscribed manner for the SS7 L2 state machines to operate correctly.

Note that the state diagram illustrated in Figure 2 is similar to the Delimitation, Alignment, and Error Detection for Receive (DAEDR) state machine provided in Q.703.

Figure 2 - Receiver State Diagram

The receivers can be in one of three modes as illustrated in Figure 2 above, and described as follows:

1. Normal Mode

   In this state, the receivers are synchronized on frames and unique frames are being received. This is the normal situation which corresponds to the state of the receivers in most IP WAN drivers. This is also the power-on reset state of the driver receivers.

   Whenever a correct MSU is received (as contrasted with a FISU or LSSU), the `ss7if_rx()` interface method is called with the received frame and the receivers stay in the normal mode. Whenever a correct FISU or LSSU is received, the driver calls `ss7if_rx()` with the received frame and moves to the compression mode. Whenever an erroneous frame (aborted frame, frame with residual bits, frames greater than SS7_MTU in length, frames with erroneous length indicators) or a loss of synchronization wtih flags occurs, the receiver calls `ss7if_err()` or `ss7if_los()` and moves to the octet counting mode state.

2. Compression Mode

   In this state, a FISU or LSSU has been received and the receiver is compressing repeating occurences of identical FISU or LSSU frames.

   One of the reasons for requiring the driver to peform this function is that the overhead associated with compressing repeating FISUs or LSSUs can be most efficiently achieved from within the Rx ISR itself, rather than in the SS7 interface L2 state machines.

   For example, the `acb56.c` driver keeps an `sk_buff` as a compression buffer and compares the 3 to 5 bytes of the previously received FISU or LSSU with the currently received FISU or LSSU to determine whether to call `ss7if_rx()` with the received frame or `ss7if_rpt()` and discard the frame.

   Whenever a correct MSU is received, the driver calls `ss7if_rx()` with the received frame and moves to the normal mode. Whenever a correct FISU or LSSU is received, the driver calls `ss7if_rx()` with the received frame and moves to the compression mode. When no correct SU has been received, the driver calls `ss7if_oct()` every 16 octet times (or 128 line rate bit times).

3. Octet Counting Mode

   In this state, the receivers have lost synchronization with flags and are generating an erroneous SU indication to the L2 state machines every 16 octets (or 128 bit times) at the line rate.

   One of the major reasons for requiring the driver to perform this function is the lack of a sub-millisecond timing reference in the Linux kernel. Most device drivers are capable of generating a higher precision interrupt which is used to generate the erroneous SU indications to the L2 state machines when in this mode. Furthermore, some chips support octet counting mode directly.

   For example, the `acb56.c` driver sets an interrupt which is based on the Baud Rate Generator in the Z80530 which generates an interrupt every bit time at 56 kbps. The ISR for this interrupt counts the bits until 128 bit times have been accumulated and then calls the `ss7if_oct()` interface method while in octet counting mode. When not in octet counting mode the driver does not request that the chip generate the interrupt (to reduce ISR overheads).

   Whenever a correct and duplicate FISU or LSSU is received, the driver calls `ss7if_rpt()` and discards the repeated frame and stays in the compression mode. Whenever a correct and unique FISU or LSSU is received, the driver calls `ss7if_rx()` with the received frame and stays in the compression mode. Whenever a correct MSU is received, the driver calls `ss7if_rx()` with the received frame and moves to the normal mode. Whenever an erroneous frame (see above) or loss of synchronization with flags occurs, the driver calls `ss7if_err()` or `ss7if_los()` and moves to the octet counting mode.

## Transmit Interrupt Service Routine Interface

When transmitting packets provided to the driver for transmission using the driver's `hard_start_xmit()` driver service routine, the driver must behave somewhat differently that for normal IP WAN operation. This is because the transmitters in SS7 are always busy and must idle FISUs or repeat LSSUs as required to fill times when there are no packets available for transmission.

All retransmissions are handled within the L2 state machines and MSUs which are to be retransmitted are provided to the driver using the driver's `hard_start_xmit()` routine in the same manner that new frames for transmission are provided (in an `sk_buff` with the level 2 header already provided).

### Tx ISR API

The interface to the tranmitters in the device driver is the driver's `hard_start_xmit()` driver method which the device driver filled into the `struct device` structure at registration with the SS7 LINK interface. This is the method that the SS7 LINK interface uses to send frames for transmission to the device driver.

When an new or retransmitted SU is available for transmission or retransmission, the SS7 LINK interface will call the device driver's `hard_start_xmit()` driver service routine with an `sk_buff` which contains the frame for transmission. The driver should queue the frame (if possible) and return the depth of the driver's queue. The SS7 LINK interface should not let this queue depth increase beyond 1; however, for short sequences, the SS7 LINK interface may send 2 or 3 frames win rapid succession.

When the transmitters are available to transmit a new frame (at the completion of the previous frame which was transmitted), the driver should check the internal queue to see if there are new frames for transmission. If there are no frames for transmission, the transmitter behaviour depends on the previous frame which was transmitted, as illustrated in Figure 3 below.

Note that the state diagram illustrated in Figure 3 is similar to the Delimitation, Alignment, and Error Detection for Transmit (DAEDT) state machine provided in Q.703.

Figure 3 - Transmitter State Diagram

The behavior of the transmitters in the driver are dependent upon the last transmitted frame as illustrated in Figure 3 above, and described as follows:

No frame previously transmitted: power-on situation.
  In this situation, and this situation only, the transmitters are permitted to idle mark or flags depending upon the implementation. At the first receipt of a frame for transmission (normally LSSU SIOS) the transmitters must start generating flags and transmit the frame, moving to one of the non-reset states.

Previous frame was an MSU (Message Signal Unit).
  If the previous frame transmitted was an MSU and there are no frames for transmission, a FISU is generated using the `BSN/BIB` and `FSN/FIB` of the last transmitted MSU and the FISU is transmitted (moving to the Transmit FISU state). If a frame is available for transmission, that frame is transmitted and the appropriate state entered.

Previous frame was a FISU (Fill In Signal Unit).
  If the previous frame transmitted was a FISU and there are no frames for transmission, the FISU is simply repeated. If a frame is available for transmission, that frame is transmitted and the appropriate state entered.

Previous frame was an LSSU (Link Status Signal Unit).
  If the previous frame transmitted was an LSSU (and not a SIB) and there are no frames for transmission, that LSSU (not a SIB) is simply repeated. If the previous frame transmitted was an LSSU (and a SIB) and there are no frames for transmission, a FISU is generated using the `BSN/BIB` and `FSN/FIB` of the last transmitted SIB and the FISU is transmitted (moving to the Transmit FISU state). If a frame is available for transmission, that frame is transmitted and the appropriate state entered.

With this approach, it is possible for the SS7 LINK interface and state machines to interface to the driver through the single `hard_start_xmit()` function.

## Summary

The SS7 LINK interface provides a simple and straightforward mechanism for attaching an SS7 L2 state machine onto a device driver and providing and interface mechanism which permits the simplified design of an SS7 LINK device driver. This design permits a simple and straightforward DAEDR and DEADT implemenation in the device driver, without the driver having to worry about the other details of the SS7 LINK state machine.

The writing of SS7 LINK device drivers is somewhat different from the development of IP WAN drivers for synchronous serial interfaces, because of the need to peform octet counting mode and SU Compression capabilities in the receiver and FISU/LSSU Idling capabilities in the transmitter; however, these mechanisms are simple and straightforward to implement.

For an example implementation of a synchronous serial SS7 LINK interface driver, see the Sealevel ACB56 driver in `acb56.c` in the `drivers/net` directory.

OpenSS7 | Brian F. G. Bidulock, © 2000, Brian F. G. Bidulock, All Rights Reserved. Last modified: $Date: 2000/10/04 03:52:24 $