📄 ldl.c
字号:
/* * ldl: Yet another unfinished DLPI driver * * Version: 0.4.1 * * Copyright (C) 1998, 1999 Ole Husgaard (sparre@login.dknet.dk) * * Token ring support by Dave Grothe Copyright (C) 1999 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * * * This driver does not attempt to do any hardware programming. Instead * this driver is an interface between the existing Linux network device * driver interface and the DLPI interface. * * It will also work in cooperation with Linux protocol drivers. You can * BIND the IP protocol to a Linux netdevice that is also used by the * Linux IP socket interface: * Incoming packets are delivered to this DLPI driver *and* to the Linux * IP socket interface. But packets sent out by this DLPI driver are *not* * seen by the Linux IP socket interface, and packets send out by the Linux * IP socket interface are *not* seen by this driver. This means that the * Linux IP code will see a response to an IP protocol packet sent out from * this DLPI driver. * To avoid this sort of confusion, you should do one of: * 1) Use this driver for listening only. * 2) Use protocols *not* used by the Linux socket code. * 3) Tell Linux that the interface is down (does this work?) * * The PPA (device number) used for ATTACH is determined by looking at * the Linux netdevice list. The first netdevice has PPA 0. You can see * the device list in /proc/net/dev, or you can do a LDL_FINDPPA IOCTL if * you know the netdevice name. * You can OR the PPA with one of the LDL_FRAME_xxx constants defined in * ldl.h if you want to use other framing types that the default ethernet II. * * Currently only ethernet devices and the loopback device are supported. * * * TODO: Sending IP packets from ldl to the native Linux IP layer across * the loopback interface no longer works. The native IP receive code now * checks a field skb->dst to see if the packet is bogus. This field is set * on receive from a network card, but due to some optimizations this is * set before transmit when using the loopback interface. The skb->dst field * should point to an entry in the ipv4 FIB, and I do not think that there is * any way to get at this without changing the native Linux ipv4 code. * And it looks like doing this would either slow down the native ipv4 code, * open up a security hole, or make the kernel significantly bigger. * Another option could be to change this driver to check for ipv4 on the * loopback interface. When this is detected, we could set skb->dst. * */#ifdef DEBUG#undef DEBUG /* unused symbol that causes a warning */#endif#include <linux/config.h>#include <linux/version.h>#ifdef CONFIG_MODVERSIONS#include <linux/modversions.h>#endif#include <linux/module.h>#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0)#define KERNEL_2_0#else#define KERNEL_2_1# if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,0)# define KERNEL_2_3# endif#endif#include <linux/spinlock.h>#include <sys/stream.h> /* stream.h comes first */#include <sys/stropts.h> /* * irda uses queue_t which interferes with our use later */#define queue_t irda_queue_t#include <asm/byteorder.h> /* ntohs() etc.. */#include <linux/errno.h>#include <linux/string.h> /* memcpy() and family */# ifdef RH_71_KLUDGE /* boogered up incls in 2.4.2 */# undef CONFIG_HIGHMEM /* b_page has semi-circular reference */# endif#include <linux/netdevice.h> /* Linux netdevice header */#include <linux/if_arp.h> /* Linux netdevice types */#include <linux/if_ether.h> /* Linux ethernet defs */#include <linux/skbuff.h> /* Linux socket buffer */#ifdef KERNEL_2_1 /* * This file is not available if kernel source is not installed */#if !defined(NOKSRC)#include <net/pkt_sched.h> /* Linux queue disciplines */#elsevoid qdisc_reset(struct Qdisc *qdisc);#endif#endif#ifdef KERNEL_2_0#define atomic_read(int_ptr) (*(int_ptr))#define ARPHRD_HDLC 513 /* 2.1 compatibility */#endif#undef queue_t /* allow visibility again */#include <sys/dlpi.h>#include <sys/dki.h>#include <sys/ddi.h>#include <sys/ldl.h>#include <sys/LiS/config.h>#include <sys/osif.h>#if defined(KERNEL_2_3)#define ldldev net_device#define driver_started(dev) 1#define START_BH_ATOMIC(dev) spin_lock_bh(&(dev)->queue_lock)#define END_BH_ATOMIC(dev) spin_unlock_bh(&(dev)->queue_lock)#else#define ldldev device#define netif_queue_stopped(dev) ((dev)->tbusy)#define driver_started(dev) ((dev)->start)#define START_BH_ATOMIC(dev) start_bh_atomic()#define END_BH_ATOMIC(dev) end_bh_atomic()#endif#if defined(ARPHRD_IEEE802_TR)#define IS_ARPHRD_IEEE802_TR(dev) (dev)->type == ARPHRD_IEEE802_TR#else#define IS_ARPHRD_IEEE802_TR(dev) 0#endif#if defined(KERNEL_2_1) && defined(LISMODVERS)/*extern void qdisc_reset(struct Qdisc *qdisc);*/#endif/* * Some configuration sanity checks */#ifndef LDL_#error Not configured#endif#if !defined(LDL__CMAJORS) || LDL__CMAJORS != 1#error There should be exactly one major number#endif#if !defined(LDL__CMAJOR_0)#error The major number should be defined#endif#define LDL_MAX_HDR_LEN 64#define MAXADDRLEN 8#define MAXSAPLEN 8#define MAXDLSAPLEN (MAXSAPLEN + MAXADDRLEN)#define MINDATA 46#define MAXDATA 4096#define DONE 0#define RETRY 1typedef struct { unsigned char sap[MAXSAPLEN]; } sap_t;/* * Private per stream flags */#define LDLFLAG_PROMISC_SAP 0x0400 /* Promiscous SAP mode */#define LDLFLAG_SET_ADDR 0x1000 /* Changing physical address */#define LDLFLAG_HANGUP 0x2000 /* Deferred M_HANGUP needed */#define LDLFLAG_HANGUP_DONE 0x4000 /* M_HANGUP completed */#define LDLFLAG_PRIVATE 0x7400 /* Private flag mask *//* * Transmission priorities */#if defined(TC_PRIO_BULK)#define LDLPRI_LO TC_PRIO_BULK#define LDLPRI_MED TC_PRIO_BESTEFFORT#define LDLPRI_HI TC_PRIO_INTERACTIVE#elif defined(SOPRI_BACKGROUND)#define LDLPRI_LO SOPRI_BACKGROUND#define LDLPRI_MED SOPRI_NORMAL#define LDLPRI_HI SOPRI_INTERACTIVE#else#define LDLPRI_LO 0#define LDLPRI_MED 0#define LDLPRI_HI 0#endif/* * How long to backout in case of congestion */#define CONGESTION_BACKOFF_TICKS ((HZ/200 <= 1) ? 2 : (HZ/200))/* * The following structures are a bit complicated, but they give efficient * code in the receive data code path and fair behaviour in the transmit * path when local transmit congestion is happening. * * Data is received from a native Linux netdevice as follows: * - First a struct packet_type that identifies the device and/or protocol * must be registered with the Linux kernel using the dev_add_pack() * function. * - When a packet (with the right protocol) is received (on the right * device) a callback function (a field of the struct packet_type) is * called. Here the callback function is rcv_func(). The callback function * takes three arguments: * struct sk_buff *skb Native Linux network data buffer * struct device *dev The native Linux device that data arrived on * struct packet_type *pt The packet type that matched protocol/device * The callback is not called during the receive interrupt, but just after. * The data is copied into a STREAMS data message. (We could use esballoc()) * - When the receive function should stop, dev_remove_pack() must be called. * * The following considerations are taken: * - For every packet_type (but the first) listening on a Linux netdevice, * a sk_buff structure is copied. The data is not copied, but the sk_buff * is large (compared to mblk_t). This copy should be avoided by using * only a single packet_type when several DLPI devices are bound to the * same SAP on the same device. (Have a look at function net_bh() in * file net/core/dev.c of the Linux kernel if you want to see what happens.) * - When several DLPI devices are bound to the same SAP on the same device, * only the message blocks (not the data blocks) should be copied. * (Use dupmsg() instead of copymsg() whenever possible). * - When several DLPI devices are bound to the same SAP on the same device, * the DLPI devices that data must be delivered to should be trivially easy * to find. * - Subsequent (peer) bindings must be supported by the DLPI device. (This * complicates things a bit.) * - We want to limit the amount of data we have in the Linux netdevice * transmit queue at any time to be fair to the native Linux networking * code. * * To facilitate this the following structures are used: * struct ndev * This is the internal representation of a native Linux netdevice. It has * the following members: * - A pointer to the native Linux netdevice. * - The maximum byte count that is allowed to be on the transmission * queue of this netdevice. * - The current byte count that this driver currently has on the * transmission queue of this netdevice. * - A timer handle for a timeout to restart after congestion has been * detected. * - A list of the endpoints attached to this netdevice. * struct pt * This is the native Linux struct packet_type with some extra fields. The * first member is a struct packet_type so we can use a simple typecast in * the receive callback function. Apart from a magic number for debugging * purposes, the only other member is a pointer to the first entry of a * linked list of listeners for this packet_type. * struct sap * This represents a DLPI binding (and also a listener for a certain packet * type). It has the following members: * - A pointer to the DLPI stream that is bound (is listening). * - A pointer to the struct pt that is used for receiving. * - A pointer to the next entry on the linked list of listeners for this * packet type. * - A pointer to the next entry on a linked list of subsequent (peer) * bindings on the same DLPI stream. * - The SAP that is bound (length and type determined from dl->framing). *//* Netdevice structure */struct ndev{ dl_ulong magic; /* Debugging paranoia (==0 iff free) */ struct ldldev *dev; /* Linux netdevice */ struct dl *endpoints; /* List of endpoints attached to this */ struct ndev *next; /* List of all struct ndev */ atomic_t wr_cur; /* Currently on write queue */ int wr_max; /* Max. outstanding on write queue */ int wr_min; /* Min. outstanding on write queue */ toid_t tx_congest_timer;/* Tx. congestion backoff restart timer */ int sleeping; /* Tx. sleeping flag */};/* Packet type structure */struct pt{ struct packet_type pt; /* Packet type filter */ dl_ulong magic; /* Debugging paranoia (==0 iff free) */ lis_rw_lock_t lock; /* listening SAP list synchronization */ struct sap *listen; /* listening SAP list (==NULL iff free) */ struct pt *next; /* List of all struct pt */};/* Binding structure */struct sap { sap_t sap; /* Bound SAP */ dl_ulong magic; /* Debugging paranoia (==0 iff free) */ struct dl *dl; /* DLPI minor pointer (==NULL iff free) */ struct pt *pt; /* Packet type filter */ struct sap *next_listen;/* Listening sap list (same pkt. type) */ struct sap *next_sap; /* Subsequent sap list (same dl) */};/* DLPI endpoint structure */struct dl{ dl_ulong magic; /* Debugging paranoia (==0 iff free) */ queue_t *rq; /* Read queue */ ulong dlstate; /* State */ dl_ulong flags; /* Current flags */ dl_ulong lost_rcv, lost_send; /* Statistics */ dl_ulong priority; /* Current priority */ struct dl *next_open; /* List of open devices */ struct ndev *ndev; /* Our netdevice, known after ATTACH */ struct dl *next_ndev; /* List of endpoints on same netdevice */ int bufwait; /* Flag: We are waiting for a buffer */ int addr_len; /* Link physical address length */ int sap_len; /* Link SAP length */ int machdr_len; /* MAC header length */ int machdr_reserve; /* MAC header alloc length */ int mtu; /* DLSDU */ struct sap *sap; /* Primary SAP */ struct sap *subs; /* Subsequent (peer) SAPs */ dl_ulong framing; /* Data link framing used */ unsigned char oui[3]; /* OUI for SNAP framing */ int (*wantsframe)(struct dl *dl, unsigned char *fr, int len); mblk_t *(*rcvind)(struct dl *dl, mblk_t *dp); int (*mkhdr)(struct dl *dl, unsigned char *dst, int datalen, struct sk_buff *skb);};/* Magic numbers */#define NDEV_MAGIC 0xA5B4C3D1#define PT_MAGIC 0xA5B4C3D2#define SAP_MAGIC 0xA5B4C3D3#define DL_MAGIC 0xA5B4C3D4/* Default netdevice write queue water marks */#define DEV_WR_MIN 0x08000 /* Low water: 32 Kb */#define DEV_WR_MAX 0x10000 /* High water: 64 Kb */ldl_gstats_ioctl_t ldl_gstats ;#define ginc(field) lis_atomic_inc(&ldl_gstats.field)STATIC unsigned long ldl_debug_mask ;STATIC struct pt *first_pt;STATIC lis_spin_lock_t first_pt_lock;STATIC struct ndev *first_ndev;STATIC struct dl dl_dl[LDL_N_MINOR];STATIC struct dl *first_open;STATIC lis_spin_lock_t first_open_lock;STATIC int initialized = 0;STATIC int n_hangup;STATIC int pt_n_alloc = 0;STATIC int sap_n_alloc = 0;STATIC int ndev_n_alloc = 0;STATIC char *ldl_pkt_type(unsigned saptype) ;STATIC int dl_open(queue_t *, dev_t *, int, int, cred_t *);STATIC int dl_close(queue_t *, int, cred_t *);STATIC int dl_rsrv(queue_t *);STATIC int dl_wput(queue_t *, mblk_t *);STATIC int dl_wsrv(queue_t *);STATIC int rcv_func(struct sk_buff *skb, struct ldldev *dev, struct packet_type *pt);STATIC INLINE int tx_func_raw(struct dl *dl, mblk_t *mp);STATIC struct module_info dl_minfo = { 0x7253, /* Module ID number */ "ldl", /* Module name: Linux DataLink */ 4, /* Min packet size accepted */ INFPSZ, /* Max packet size accepted */ 0x10000, /* Hi water mark: 64 Kb */ 0x04000 /* Low water mark: 16 Kb */};STATIC struct qinit dl_rinit = { NULL, /* No read put */ dl_rsrv, /* Read service */ dl_open, /* Each open */ dl_close, /* Last close */ NULL, /* Reserved */ &dl_minfo, /* Information */ NULL /* No statistics */};STATIC struct qinit dl_winit = { dl_wput, /* Write put */ dl_wsrv, /* Write service */ NULL, /* Ignored */ NULL, /* Ignored */ NULL, /* Reserved */ &dl_minfo, /* Information */ NULL /* No statistics */};#ifdef MODULESTATIC#endifstruct streamtab ldl_info = { &dl_rinit, /* Read queue */ &dl_winit, /* Write queue */ NULL, /* Unused */ NULL /* Unused */};STATIC void ldl_bfr_dump(char *msg, void *ptr, int len, int alldata){ unsigned char *p = ptr ; int bytes = 0 ; int trunc = 0 ; int nl = 0 ; printk("%s -- %d bytes\n", msg, len) ; if (!alldata && len > 24) { len = 24 ; trunc = 1 ; } for (; len > 0; len--, p++) { printk("%02x ", *p) ; nl = 0 ; if (!trunc && ++bytes == 16) { printk("\n"); nl = 1 ; bytes = 0 ; } } if (!nl) printk("\n") ;}STATIC void ldl_mp_dump(char *msg, mblk_t *mp, int alldata){ ldl_bfr_dump(msg, mp->b_rptr, mp->b_wptr - mp->b_rptr, alldata) ;}STATIC void ldl_mp_data_dump(char *msg, mblk_t *mp, int alldata){ for (; mp != NULL && mp->b_datap->db_type != M_DATA; mp = mp->b_cont) ; if (mp == NULL) return ; ldl_mp_dump(msg, mp, alldata) ;}STATIC void ldl_skbuff_dump(char *msg, struct sk_buff *skb, int alldata){#define L unsigned long int cnt ; if (msg != NULL) printk("%s:\n", msg) ; printk("head=%lx data=%lx tail=%lx end=%lx truesize=%d\n" "h.raw=%lx nh.raw=%lx mac.raw=%lx type=%s len=%d\n", (L)skb->head, (L)skb->data, (L)skb->tail, (L)skb->end, skb->truesize, (L)skb->h.raw, (L)skb->nh.raw, (L)skb->mac.raw, ldl_pkt_type(skb->pkt_type),
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -