// SPDX-License-Identifier: GPL-2.0-or-later /* * File Name: * skfddi.c * * Copyright Information: * Copyright SysKonnect 1998,1999. * * The information in this file is provided "AS IS" without warranty. * * Abstract: * A Linux device driver supporting the SysKonnect FDDI PCI controller * familie. * * Maintainers: * CG Christoph Goos ([email protected]) * * Contributors: * DM David S. Miller * * Address all question to: * [email protected] * * The technical manual for the adapters is available from SysKonnect's * web pages: www.syskonnect.com * Goto "Support" and search Knowledge Base for "manual". * * Driver Architecture: * The driver architecture is based on the DEC FDDI driver by * Lawrence V. Stefani and several ethernet drivers. * I also used an existing Windows NT miniport driver. * All hardware dependent functions are handled by the SysKonnect * Hardware Module. * The only headerfiles that are directly related to this source * are skfddi.c, h/types.h, h/osdef1st.h, h/targetos.h. * The others belong to the SysKonnect FDDI Hardware Module and * should better not be changed. * * Modification History: * Date Name Description * 02-Mar-98 CG Created. * * 10-Mar-99 CG Support for 2.2.x added. * 25-Mar-99 CG Corrected IRQ routing for SMP (APIC) * 26-Oct-99 CG Fixed compilation error on 2.2.13 * 12-Nov-99 CG Source code release * 22-Nov-99 CG Included in kernel source. * 07-May-00 DM 64 bit fixes, new dma interface * 31-Jul-03 DB Audit copy_*_user in skfp_ioctl * Daniele Bellucci <[email protected]> * 03-Dec-03 SH Convert to PCI device model * * Compilation options (-Dxxx): * DRIVERDEBUG print lots of messages to log file * DUMPPACKETS print received/transmitted packets to logfile * * Tested cpu architectures: * - i386 * - sparc64 */ /* Version information string - should be updated prior to */ /* each new release!!! */ #define VERSION … static const char * const boot_msg = …; /* Include files */ #include <linux/capability.h> #include <linux/compat.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/errno.h> #include <linux/ioport.h> #include <linux/interrupt.h> #include <linux/pci.h> #include <linux/netdevice.h> #include <linux/etherdevice.h> #include <linux/fddidevice.h> #include <linux/skbuff.h> #include <linux/bitops.h> #include <linux/gfp.h> #include <asm/byteorder.h> #include <asm/io.h> #include <linux/uaccess.h> #include "h/types.h" #undef ADDR // undo Linux definition #include "h/skfbi.h" #include "h/fddi.h" #include "h/smc.h" #include "h/smtstate.h" // Define module-wide (static) routines static int skfp_driver_init(struct net_device *dev); static int skfp_open(struct net_device *dev); static int skfp_close(struct net_device *dev); static irqreturn_t skfp_interrupt(int irq, void *dev_id); static struct net_device_stats *skfp_ctl_get_stats(struct net_device *dev); static void skfp_ctl_set_multicast_list(struct net_device *dev); static void skfp_ctl_set_multicast_list_wo_lock(struct net_device *dev); static int skfp_ctl_set_mac_address(struct net_device *dev, void *addr); static int skfp_siocdevprivate(struct net_device *dev, struct ifreq *rq, void __user *data, int cmd); static netdev_tx_t skfp_send_pkt(struct sk_buff *skb, struct net_device *dev); static void send_queued_packets(struct s_smc *smc); static void CheckSourceAddress(unsigned char *frame, unsigned char *hw_addr); static void ResetAdapter(struct s_smc *smc); // Functions needed by the hardware module void *mac_drv_get_space(struct s_smc *smc, u_int size); void *mac_drv_get_desc_mem(struct s_smc *smc, u_int size); unsigned long mac_drv_virt2phys(struct s_smc *smc, void *virt); unsigned long dma_master(struct s_smc *smc, void *virt, int len, int flag); void dma_complete(struct s_smc *smc, volatile union s_fp_descr *descr, int flag); void mac_drv_tx_complete(struct s_smc *smc, volatile struct s_smt_fp_txd *txd); void llc_restart_tx(struct s_smc *smc); void mac_drv_rx_complete(struct s_smc *smc, volatile struct s_smt_fp_rxd *rxd, int frag_count, int len); void mac_drv_requeue_rxd(struct s_smc *smc, volatile struct s_smt_fp_rxd *rxd, int frag_count); void mac_drv_fill_rxd(struct s_smc *smc); void mac_drv_clear_rxd(struct s_smc *smc, volatile struct s_smt_fp_rxd *rxd, int frag_count); int mac_drv_rx_init(struct s_smc *smc, int len, int fc, char *look_ahead, int la_len); void dump_data(unsigned char *Data, int length); // External functions from the hardware module extern u_int mac_drv_check_space(void); extern int mac_drv_init(struct s_smc *smc); extern void hwm_tx_frag(struct s_smc *smc, char far * virt, u_long phys, int len, int frame_status); extern int hwm_tx_init(struct s_smc *smc, u_char fc, int frag_count, int frame_len, int frame_status); extern void fddi_isr(struct s_smc *smc); extern void hwm_rx_frag(struct s_smc *smc, char far * virt, u_long phys, int len, int frame_status); extern void mac_drv_rx_mode(struct s_smc *smc, int mode); extern void mac_drv_clear_rx_queue(struct s_smc *smc); extern void enable_tx_irq(struct s_smc *smc, u_short queue); static const struct pci_device_id skfddi_pci_tbl[] = …; MODULE_DEVICE_TABLE(pci, skfddi_pci_tbl); MODULE_DESCRIPTION(…) …; MODULE_LICENSE(…) …; MODULE_AUTHOR(…) …; // Define module-wide (static) variables static int num_boards; /* total number of adapters configured */ static const struct net_device_ops skfp_netdev_ops = …; /* * ================= * = skfp_init_one = * ================= * * Overview: * Probes for supported FDDI PCI controllers * * Returns: * Condition code * * Arguments: * pdev - pointer to PCI device information * * Functional Description: * This is now called by PCI driver registration process * for each board found. * * Return Codes: * 0 - This device (fddi0, fddi1, etc) configured successfully * -ENODEV - No devices present, or no SysKonnect FDDI PCI device * present for this device name * * * Side Effects: * Device structures for FDDI adapters (fddi0, fddi1, etc) are * initialized and the board resources are read and stored in * the device structure. */ static int skfp_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) { … } /* * Called for each adapter board from pci_unregister_driver */ static void skfp_remove_one(struct pci_dev *pdev) { … } /* * ==================== * = skfp_driver_init = * ==================== * * Overview: * Initializes remaining adapter board structure information * and makes sure adapter is in a safe state prior to skfp_open(). * * Returns: * Condition code * * Arguments: * dev - pointer to device information * * Functional Description: * This function allocates additional resources such as the host memory * blocks needed by the adapter. * The adapter is also reset. The OS must call skfp_open() to open * the adapter and bring it on-line. * * Return Codes: * 0 - initialization succeeded * -1 - initialization failed */ static int skfp_driver_init(struct net_device *dev) { … } // skfp_driver_init /* * ============= * = skfp_open = * ============= * * Overview: * Opens the adapter * * Returns: * Condition code * * Arguments: * dev - pointer to device information * * Functional Description: * This function brings the adapter to an operational state. * * Return Codes: * 0 - Adapter was successfully opened * -EAGAIN - Could not register IRQ */ static int skfp_open(struct net_device *dev) { … } // skfp_open /* * ============== * = skfp_close = * ============== * * Overview: * Closes the device/module. * * Returns: * Condition code * * Arguments: * dev - pointer to device information * * Functional Description: * This routine closes the adapter and brings it to a safe state. * The interrupt service routine is deregistered with the OS. * The adapter can be opened again with another call to skfp_open(). * * Return Codes: * Always return 0. * * Assumptions: * No further requests for this adapter are made after this routine is * called. skfp_open() can be called to reset and reinitialize the * adapter. */ static int skfp_close(struct net_device *dev) { … } // skfp_close /* * ================== * = skfp_interrupt = * ================== * * Overview: * Interrupt processing routine * * Returns: * None * * Arguments: * irq - interrupt vector * dev_id - pointer to device information * * Functional Description: * This routine calls the interrupt processing routine for this adapter. It * disables and reenables adapter interrupts, as appropriate. We can support * shared interrupts since the incoming dev_id pointer provides our device * structure context. All the real work is done in the hardware module. * * Return Codes: * None * * Assumptions: * The interrupt acknowledgement at the hardware level (eg. ACKing the PIC * on Intel-based systems) is done by the operating system outside this * routine. * * System interrupts are enabled through this call. * * Side Effects: * Interrupts are disabled, then reenabled at the adapter. */ static irqreturn_t skfp_interrupt(int irq, void *dev_id) { … } // skfp_interrupt /* * ====================== * = skfp_ctl_get_stats = * ====================== * * Overview: * Get statistics for FDDI adapter * * Returns: * Pointer to FDDI statistics structure * * Arguments: * dev - pointer to device information * * Functional Description: * Gets current MIB objects from adapter, then * returns FDDI statistics structure as defined * in if_fddi.h. * * Note: Since the FDDI statistics structure is * still new and the device structure doesn't * have an FDDI-specific get statistics handler, * we'll return the FDDI statistics structure as * a pointer to an Ethernet statistics structure. * That way, at least the first part of the statistics * structure can be decoded properly. * We'll have to pay attention to this routine as the * device structure becomes more mature and LAN media * independent. * */ static struct net_device_stats *skfp_ctl_get_stats(struct net_device *dev) { … } // ctl_get_stat /* * ============================== * = skfp_ctl_set_multicast_list = * ============================== * * Overview: * Enable/Disable LLC frame promiscuous mode reception * on the adapter and/or update multicast address table. * * Returns: * None * * Arguments: * dev - pointer to device information * * Functional Description: * This function acquires the driver lock and only calls * skfp_ctl_set_multicast_list_wo_lock then. * This routine follows a fairly simple algorithm for setting the * adapter filters and CAM: * * if IFF_PROMISC flag is set * enable promiscuous mode * else * disable promiscuous mode * if number of multicast addresses <= max. multicast number * add mc addresses to adapter table * else * enable promiscuous mode * update adapter filters * * Assumptions: * Multicast addresses are presented in canonical (LSB) format. * * Side Effects: * On-board adapter filters are updated. */ static void skfp_ctl_set_multicast_list(struct net_device *dev) { … } // skfp_ctl_set_multicast_list static void skfp_ctl_set_multicast_list_wo_lock(struct net_device *dev) { … } // skfp_ctl_set_multicast_list_wo_lock /* * =========================== * = skfp_ctl_set_mac_address = * =========================== * * Overview: * set new mac address on adapter and update dev_addr field in device table. * * Returns: * None * * Arguments: * dev - pointer to device information * addr - pointer to sockaddr structure containing unicast address to set * * Assumptions: * The address pointed to by addr->sa_data is a valid unicast * address and is presented in canonical (LSB) format. */ static int skfp_ctl_set_mac_address(struct net_device *dev, void *addr) { … } // skfp_ctl_set_mac_address /* * ======================= * = skfp_siocdevprivate = * ======================= * * Overview: * * Perform IOCTL call functions here. Some are privileged operations and the * effective uid is checked in those cases. * * Returns: * status value * 0 - success * other - failure * * Arguments: * dev - pointer to device information * rq - pointer to ioctl request structure * cmd - ? * */ static int skfp_siocdevprivate(struct net_device *dev, struct ifreq *rq, void __user *data, int cmd) { … } // skfp_ioctl /* * ===================== * = skfp_send_pkt = * ===================== * * Overview: * Queues a packet for transmission and try to transmit it. * * Returns: * Condition code * * Arguments: * skb - pointer to sk_buff to queue for transmission * dev - pointer to device information * * Functional Description: * Here we assume that an incoming skb transmit request * is contained in a single physically contiguous buffer * in which the virtual address of the start of packet * (skb->data) can be converted to a physical address * by using dma_map_single(). * * We have an internal queue for packets we can not send * immediately. Packets in this queue can be given to the * adapter if transmit buffers are freed. * * We can't free the skb until after it's been DMA'd * out by the adapter, so we'll keep it in the driver and * return it in mac_drv_tx_complete. * * Return Codes: * 0 - driver has queued and/or sent packet * 1 - caller should requeue the sk_buff for later transmission * * Assumptions: * The entire packet is stored in one physically * contiguous buffer which is not cached and whose * 32-bit physical address can be determined. * * It's vital that this routine is NOT reentered for the * same board and that the OS is not in another section of * code (eg. skfp_interrupt) for the same board on a * different thread. * * Side Effects: * None */ static netdev_tx_t skfp_send_pkt(struct sk_buff *skb, struct net_device *dev) { … } // skfp_send_pkt /* * ======================= * = send_queued_packets = * ======================= * * Overview: * Send packets from the driver queue as long as there are some and * transmit resources are available. * * Returns: * None * * Arguments: * smc - pointer to smc (adapter) structure * * Functional Description: * Take a packet from queue if there is any. If not, then we are done. * Check if there are resources to send the packet. If not, requeue it * and exit. * Set packet descriptor flags and give packet to adapter. * Check if any send resources can be freed (we do not use the * transmit complete interrupt). */ static void send_queued_packets(struct s_smc *smc) { … } // send_queued_packets /************************ * * CheckSourceAddress * * Verify if the source address is set. Insert it if necessary. * ************************/ static void CheckSourceAddress(unsigned char *frame, unsigned char *hw_addr) { … } // CheckSourceAddress /************************ * * ResetAdapter * * Reset the adapter and bring it back to operational mode. * Args * smc - A pointer to the SMT context struct. * Out * Nothing. * ************************/ static void ResetAdapter(struct s_smc *smc) { … } // ResetAdapter //--------------- functions called by hardware module ---------------- /************************ * * llc_restart_tx * * The hardware driver calls this routine when the transmit complete * interrupt bits (end of frame) for the synchronous or asynchronous * queue is set. * * NOTE The hardware driver calls this function also if no packets are queued. * The routine must be able to handle this case. * Args * smc - A pointer to the SMT context struct. * Out * Nothing. * ************************/ void llc_restart_tx(struct s_smc *smc) { … } // llc_restart_tx /************************ * * mac_drv_get_space * * The hardware module calls this function to allocate the memory * for the SMT MBufs if the define MB_OUTSIDE_SMC is specified. * Args * smc - A pointer to the SMT context struct. * * size - Size of memory in bytes to allocate. * Out * != 0 A pointer to the virtual address of the allocated memory. * == 0 Allocation error. * ************************/ void *mac_drv_get_space(struct s_smc *smc, unsigned int size) { … } // mac_drv_get_space /************************ * * mac_drv_get_desc_mem * * This function is called by the hardware dependent module. * It allocates the memory for the RxD and TxD descriptors. * * This memory must be non-cached, non-movable and non-swappable. * This memory should start at a physical page boundary. * Args * smc - A pointer to the SMT context struct. * * size - Size of memory in bytes to allocate. * Out * != 0 A pointer to the virtual address of the allocated memory. * == 0 Allocation error. * ************************/ void *mac_drv_get_desc_mem(struct s_smc *smc, unsigned int size) { … } // mac_drv_get_desc_mem /************************ * * mac_drv_virt2phys * * Get the physical address of a given virtual address. * Args * smc - A pointer to the SMT context struct. * * virt - A (virtual) pointer into our 'shared' memory area. * Out * Physical address of the given virtual address. * ************************/ unsigned long mac_drv_virt2phys(struct s_smc *smc, void *virt) { … } // mac_drv_virt2phys /************************ * * dma_master * * The HWM calls this function, when the driver leads through a DMA * transfer. If the OS-specific module must prepare the system hardware * for the DMA transfer, it should do it in this function. * * The hardware module calls this dma_master if it wants to send an SMT * frame. This means that the virt address passed in here is part of * the 'shared' memory area. * Args * smc - A pointer to the SMT context struct. * * virt - The virtual address of the data. * * len - The length in bytes of the data. * * flag - Indicates the transmit direction and the buffer type: * DMA_RD (0x01) system RAM ==> adapter buffer memory * DMA_WR (0x02) adapter buffer memory ==> system RAM * SMT_BUF (0x80) SMT buffer * * >> NOTE: SMT_BUF and DMA_RD are always set for PCI. << * Out * Returns the pyhsical address for the DMA transfer. * ************************/ u_long dma_master(struct s_smc * smc, void *virt, int len, int flag) { … } // dma_master /************************ * * dma_complete * * The hardware module calls this routine when it has completed a DMA * transfer. If the operating system dependent module has set up the DMA * channel via dma_master() (e.g. Windows NT or AIX) it should clean up * the DMA channel. * Args * smc - A pointer to the SMT context struct. * * descr - A pointer to a TxD or RxD, respectively. * * flag - Indicates the DMA transfer direction / SMT buffer: * DMA_RD (0x01) system RAM ==> adapter buffer memory * DMA_WR (0x02) adapter buffer memory ==> system RAM * SMT_BUF (0x80) SMT buffer (managed by HWM) * Out * Nothing. * ************************/ void dma_complete(struct s_smc *smc, volatile union s_fp_descr *descr, int flag) { … } // dma_complete /************************ * * mac_drv_tx_complete * * Transmit of a packet is complete. Release the tx staging buffer. * * Args * smc - A pointer to the SMT context struct. * * txd - A pointer to the last TxD which is used by the frame. * Out * Returns nothing. * ************************/ void mac_drv_tx_complete(struct s_smc *smc, volatile struct s_smt_fp_txd *txd) { … } // mac_drv_tx_complete /************************ * * dump packets to logfile * ************************/ #ifdef DUMPPACKETS void dump_data(unsigned char *Data, int length) { printk(KERN_INFO "---Packet start---\n"); print_hex_dump(KERN_INFO, "", DUMP_PREFIX_NONE, 16, 1, Data, min_t(size_t, length, 64), false); printk(KERN_INFO "------------------\n"); } // dump_data #else #define dump_data(data,len) … #endif // DUMPPACKETS /************************ * * mac_drv_rx_complete * * The hardware module calls this function if an LLC frame is received * in a receive buffer. Also the SMT, NSA, and directed beacon frames * from the network will be passed to the LLC layer by this function * if passing is enabled. * * mac_drv_rx_complete forwards the frame to the LLC layer if it should * be received. It also fills the RxD ring with new receive buffers if * some can be queued. * Args * smc - A pointer to the SMT context struct. * * rxd - A pointer to the first RxD which is used by the receive frame. * * frag_count - Count of RxDs used by the received frame. * * len - Frame length. * Out * Nothing. * ************************/ void mac_drv_rx_complete(struct s_smc *smc, volatile struct s_smt_fp_rxd *rxd, int frag_count, int len) { … } // mac_drv_rx_complete /************************ * * mac_drv_requeue_rxd * * The hardware module calls this function to request the OS-specific * module to queue the receive buffer(s) represented by the pointer * to the RxD and the frag_count into the receive queue again. This * buffer was filled with an invalid frame or an SMT frame. * Args * smc - A pointer to the SMT context struct. * * rxd - A pointer to the first RxD which is used by the receive frame. * * frag_count - Count of RxDs used by the received frame. * Out * Nothing. * ************************/ void mac_drv_requeue_rxd(struct s_smc *smc, volatile struct s_smt_fp_rxd *rxd, int frag_count) { … } // mac_drv_requeue_rxd /************************ * * mac_drv_fill_rxd * * The hardware module calls this function at initialization time * to fill the RxD ring with receive buffers. It is also called by * mac_drv_rx_complete if rx_free is large enough to queue some new * receive buffers into the RxD ring. mac_drv_fill_rxd queues new * receive buffers as long as enough RxDs and receive buffers are * available. * Args * smc - A pointer to the SMT context struct. * Out * Nothing. * ************************/ void mac_drv_fill_rxd(struct s_smc *smc) { … } // mac_drv_fill_rxd /************************ * * mac_drv_clear_rxd * * The hardware module calls this function to release unused * receive buffers. * Args * smc - A pointer to the SMT context struct. * * rxd - A pointer to the first RxD which is used by the receive buffer. * * frag_count - Count of RxDs used by the receive buffer. * Out * Nothing. * ************************/ void mac_drv_clear_rxd(struct s_smc *smc, volatile struct s_smt_fp_rxd *rxd, int frag_count) { … } // mac_drv_clear_rxd /************************ * * mac_drv_rx_init * * The hardware module calls this routine when an SMT or NSA frame of the * local SMT should be delivered to the LLC layer. * * It is necessary to have this function, because there is no other way to * copy the contents of SMT MBufs into receive buffers. * * mac_drv_rx_init allocates the required target memory for this frame, * and receives the frame fragment by fragment by calling mac_drv_rx_frag. * Args * smc - A pointer to the SMT context struct. * * len - The length (in bytes) of the received frame (FC, DA, SA, Data). * * fc - The Frame Control field of the received frame. * * look_ahead - A pointer to the lookahead data buffer (may be NULL). * * la_len - The length of the lookahead data stored in the lookahead * buffer (may be zero). * Out * Always returns zero (0). * ************************/ int mac_drv_rx_init(struct s_smc *smc, int len, int fc, char *look_ahead, int la_len) { … } // mac_drv_rx_init /************************ * * smt_timer_poll * * This routine is called periodically by the SMT module to clean up the * driver. * * Return any queued frames back to the upper protocol layers if the ring * is down. * Args * smc - A pointer to the SMT context struct. * Out * Nothing. * ************************/ void smt_timer_poll(struct s_smc *smc) { … } // smt_timer_poll /************************ * * ring_status_indication * * This function indicates a change of the ring state. * Args * smc - A pointer to the SMT context struct. * * status - The current ring status. * Out * Nothing. * ************************/ void ring_status_indication(struct s_smc *smc, u_long status) { … } // ring_status_indication /************************ * * smt_get_time * * Gets the current time from the system. * Args * None. * Out * The current time in TICKS_PER_SECOND. * * TICKS_PER_SECOND has the unit 'count of timer ticks per second'. It is * defined in "targetos.h". The definition of TICKS_PER_SECOND must comply * to the time returned by smt_get_time(). * ************************/ unsigned long smt_get_time(void) { … } // smt_get_time /************************ * * smt_stat_counter * * Status counter update (ring_op, fifo full). * Args * smc - A pointer to the SMT context struct. * * stat - = 0: A ring operational change occurred. * = 1: The FORMAC FIFO buffer is full / FIFO overflow. * Out * Nothing. * ************************/ void smt_stat_counter(struct s_smc *smc, int stat) { … } // smt_stat_counter /************************ * * cfm_state_change * * Sets CFM state in custom statistics. * Args * smc - A pointer to the SMT context struct. * * c_state - Possible values are: * * EC0_OUT, EC1_IN, EC2_TRACE, EC3_LEAVE, EC4_PATH_TEST, * EC5_INSERT, EC6_CHECK, EC7_DEINSERT * Out * Nothing. * ************************/ void cfm_state_change(struct s_smc *smc, int c_state) { … } // cfm_state_change /************************ * * ecm_state_change * * Sets ECM state in custom statistics. * Args * smc - A pointer to the SMT context struct. * * e_state - Possible values are: * * SC0_ISOLATED, SC1_WRAP_A (5), SC2_WRAP_B (6), SC4_THRU_A (12), * SC5_THRU_B (7), SC7_WRAP_S (8) * Out * Nothing. * ************************/ void ecm_state_change(struct s_smc *smc, int e_state) { … } // ecm_state_change /************************ * * rmt_state_change * * Sets RMT state in custom statistics. * Args * smc - A pointer to the SMT context struct. * * r_state - Possible values are: * * RM0_ISOLATED, RM1_NON_OP, RM2_RING_OP, RM3_DETECT, * RM4_NON_OP_DUP, RM5_RING_OP_DUP, RM6_DIRECTED, RM7_TRACE * Out * Nothing. * ************************/ void rmt_state_change(struct s_smc *smc, int r_state) { … } // rmt_state_change /************************ * * drv_reset_indication * * This function is called by the SMT when it has detected a severe * hardware problem. The driver should perform a reset on the adapter * as soon as possible, but not from within this function. * Args * smc - A pointer to the SMT context struct. * Out * Nothing. * ************************/ void drv_reset_indication(struct s_smc *smc) { … } // drv_reset_indication static struct pci_driver skfddi_pci_driver = …; module_pci_driver(…) …;