| OpenSS7 SS7 for the Common Man | © Copyright 1997-2007 OpenSS7 Corporation All Rights Reserved. Last modified: Sat, 16 Aug 2008 01:55:14 GMT | ||||||||||||||||
| |||||||||||||||||
| Description: CodeFile /code/strxns/src/drivers/ldl.c
#ident "@(#) ldl.c,v openss7-0_9_2_F(0.9.2.37) 2007/04/02 12:01:47"
static char const ident[] =
"ldl.c,v openss7-0_9_2_F(0.9.2.37) 2007/04/02 12:01:47";
#define _SVR4_SOURCE
#define _LIS_SOURCE
#include <sys/os7/compat.h>
#include <linux/bitops.h>
#include <linux/netdevice.h>
#include <linux/if_arp.h>
#include <linux/if_ether.h>
#include <linux/skbuff.h>
#include <net/pkt_sched.h>
#include <sys/dlpi.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 <sys/ldl.h>
#undef WITH_32BIT_CONVERSION
#if defined LFS && defined __LP64__
#define WITH_32BIT_CONVERSION 1
#endif
#define LDL_DESCRIP "UNIX SYSTEM V RELEASE 4.2 FAST STREAMS FOR LINUX"
#define LDL_EXTRA "Part of the OpenSS7 Stack for Linux Fast-STREAMS."
#define LDL_COPYRIGHT "Copyright (c) 1997-2006 OpenSS7 Corporation. All Rights Reserved."
#define LDL_REVISION "LfS ldl.c,v openss7-0_9_2_F (0.9.2.37) 2007/04/02 12:01:47"
#define LDL_DEVICE "SVR 4.2 STREAMS INET DLPI Drivers (NET4)"
#define LDL_CONTACT "Brian Bidulock <bidulock@openss7.org>"
#define LDL_LICENSE "GPL"
#define LDL_BANNER LDL_DESCRIP "\n" \
LDL_EXTRA "\n" \
LDL_REVISION "\n" \
LDL_COPYRIGHT "\n" \
LDL_DEVICE "\n" \
LDL_CONTACT
#define LDL_SPLASH LDL_DEVICE "\n" \
LDL_REVISION
#ifdef LINUX
MODULE_AUTHOR(LDL_CONTACT);
MODULE_DESCRIPTION(LDL_DESCRIP);
MODULE_SUPPORTED_DEVICE(LDL_DEVICE);
#ifdef MODULE_LICENSE
MODULE_LICENSE(LDL_LICENSE);
#endif
#if defined MODULE_ALIAS
MODULE_ALIAS("streams-ldl");
MODULE_ALIAS("streams-link-driver");
MODULE_ALIAS("streams-link-dri");
#endif
#endif
#ifdef LFS
#define LDL_DRV_ID CONFIG_STREAMS_LDL_MODID
#define LDL_DRV_NAME CONFIG_STREAMS_LDL_NAME
#define LDL_CMAJORS CONFIG_STREAMS_LDL_NMAJORS
#define LDL_CMAJOR_0 CONFIG_STREAMS_LDL_MAJOR
#define LDL_UNITS CONFIG_STREAMS_LDL_NMINORS
#endif
#ifdef LINUX
#ifdef MODULE_ALIAS
#ifdef LFS
MODULE_ALIAS("streams-modid-" __stringify(CONFIG_STREAMS_LDL_MODID));
MODULE_ALIAS("streams-driver-link-driver");
MODULE_ALIAS("streams-driver-link-dri");
MODULE_ALIAS("streams-major-" __stringify(CONFIG_STREAMS_LDL_MAJOR));
MODULE_ALIAS("/dev/streams/link-dri");
MODULE_ALIAS("/dev/streams/link-dri
MODULE_ALIAS("char-major-" __stringify(LDL_CMAJOR_0));
MODULE_ALIAS("char-major-" __stringify(LDL_CMAJOR_0) "-*");
MODULE_ALIAS("char-major-" __stringify(LDL_CMAJOR_0) "-0");
MODULE_ALIAS("/dev/ldl");
#endif
#endif
#define DRV_ID LDL_DRV_ID
#define DRV_NAME LDL_DRV_NAME
#define CMAJORS LDL_CMAJORS
#define CMAJOR_0 LDL_CMAJOR_0
#define UNITS LDL_UNITS
#ifdef MODULE
#define DRV_BANNER LDL_BANNER
#else
#define DRV_BANNER LDL_SPLASH
#endif
STATIC struct module_info ldl_minfo = {
.mi_idnum = DRV_ID,
.mi_idname = DRV_NAME,
.mi_minpsz = 4,
.mi_maxpsz = INFPSZ,
.mi_hiwat = 0x10000,
.mi_lowat = 0x04000,
};
STATIC struct module_stat ldl_rdmstat = { };
STATIC struct module_stat ldl_wrmstat = { };
STATIC int streamscall ldl_open(queue_t *, dev_t *, int, int, cred_t *);
STATIC int streamscall ldl_close(queue_t *, int, cred_t *);
STATIC int streamscall ldl_rsrv(queue_t *);
STATIC int streamscall ldl_wput(queue_t *, mblk_t *);
STATIC int streamscall ldl_wsrv(queue_t *);
STATIC struct qinit ldl_rinit = {
.qi_srvp = ldl_rsrv,
.qi_qopen = ldl_open,
.qi_qclose = ldl_close,
.qi_minfo = &ldl_minfo,
.qi_mstat = &ldl_rdmstat,
};
STATIC struct qinit ldl_winit = {
.qi_putp = ldl_wput,
.qi_srvp = ldl_wsrv,
.qi_minfo = &ldl_minfo,
.qi_mstat = &ldl_wrmstat,
};
struct streamtab ldl_info = {
.st_rdinit = &ldl_rinit,
.st_wrinit = &ldl_winit,
};
#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
#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 1
typedef struct {
unsigned char sap[MAXSAPLEN];
} sap_t;
#define LDLFLAG_PROMISC_SAP 0x0400
#define LDLFLAG_SET_ADDR 0x1000
#define LDLFLAG_HANGUP 0x2000
#define LDLFLAG_HANGUP_DONE 0x4000
#define LDLFLAG_PRIVATE 0x7400
#ifdef TC_PRIO_BULK
#define LDLPRI_LO TC_PRIO_BULK
#define LDLPRI_MED TC_PRIO_BESTEFFORT
#define LDLPRI_HI TC_PRIO_INTERACTIVE
#else
#ifdef 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
#endif
#define CONGESTION_BACKOFF_TICKS ((HZ/200 <= 1) ? 2 : (HZ/200))
struct ndev {
dl_ulong magic;
struct ldldev *dev;
struct dl *endpoints;
struct ndev *next;
atomic_t wr_cur;
int wr_max;
int wr_min;
toid_t tx_congest_timer;
int sleeping;
};
struct pt {
struct packet_type pt;
dl_ulong magic;
rwlock_t lock;
struct sap *listen;
struct pt *next;
};
struct sap {
sap_t sap;
dl_ulong magic;
struct dl *dl;
struct pt *pt;
struct sap *next_listen;
struct sap *next_sap;
};
struct dl {
dl_ulong magic;
queue_t *rq;
ulong dlstate;
dl_ulong flags;
dl_ulong lost_rcv, lost_send;
dl_ulong priority;
struct dl *next_open;
struct ndev *ndev;
struct dl *next_ndev;
int bufwait;
int addr_len;
int sap_len;
int machdr_len;
int machdr_reserve;
int mtu;
struct sap *sap;
struct sap *subs;
dl_ulong framing;
unsigned char oui[3];
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);
};
#define NDEV_MAGIC 0xA5B4C3D1
#define PT_MAGIC 0xA5B4C3D2
#define SAP_MAGIC 0xA5B4C3D3
#define DL_MAGIC 0xA5B4C3D4
#define DEV_WR_MIN 0x08000
#define DEV_WR_MAX 0x10000
typedef struct ldl_gstats {
atomic_t attach_req_cnt;
atomic_t detach_req_cnt;
atomic_t bind_req_cnt;
atomic_t unbind_req_cnt;
atomic_t subs_bind_req_cnt;
atomic_t subs_unbind_req_cnt;
atomic_t udqos_req_cnt;
atomic_t ok_ack_cnt;
atomic_t error_ack_cnt;
atomic_t unitdata_req_cnt;
atomic_t unitdata_req_q_cnt;
atomic_t unitdata_ind_cnt;
atomic_t unitdata_q_cnt;
atomic_t unitdata_drp_cnt;
atomic_t uderror_ind_cnt;
atomic_t ioctl_cnt;
atomic_t net_rx_cnt;
atomic_t net_rx_drp_cnt;
atomic_t net_tx_cnt;
atomic_t net_tx_fail_cnt;
} ldl_gstats_t;
typedef struct ldl_lstats {
int to_be_done;
} ldl_lstats_t;
ldl_gstats_t ldl_gstats;
ldl_lstats_t ldl_lstats;
#define ginc(field) atomic_inc(&ldl_gstats.field)
STATIC unsigned long ldl_debug_mask;
#undef SPLSTR
#undef SPLX
STATIC spinlock_t ldl_spin_lock = SPIN_LOCK_UNLOCKED;
#define SPLSTR(__psw) while(0){ (void)(__psw); spin_lock(&ldl_spin_lock); }
#define SPLX(__psw) while(0){ (void)(__psw); spin_unlock(&ldl_spin_lock); }
STATIC struct pt *first_pt = NULL;
STATIC spinlock_t first_pt_lock = SPIN_LOCK_UNLOCKED;
STATIC struct ndev *first_ndev = NULL;
STATIC struct dl dl_dl[LDL_N_MINOR] = { {}, };
STATIC struct dl *first_open = NULL;
STATIC spinlock_t first_open_lock = SPIN_LOCK_UNLOCKED;
STATIC int n_hangup = 0;
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);
#ifdef HAVE_KMEMB_STRUCT_PACKET_TYPE_FUNC_4_ARGS
STATIC int rcv_func(struct sk_buff *skb, struct ldldev *dev, struct packet_type *pt,
struct ldldev *dev2);
#else
STATIC int rcv_func(struct sk_buff *skb, struct ldldev *dev, struct packet_type *pt);
#endif
STATIC int tx_func_raw(struct dl *dl, mblk_t *mp);
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), skb->len);
if (alldata)
cnt = skb->tail - skb->head;
else
alldata = cnt = 64;
ldl_bfr_dump("Dump from skb->head", skb->head, cnt, alldata);
#undef L
}
STATIC char *
ldl_framing_type(unsigned long framing)
{
switch (framing & LDL_FRAME_MASK) {
case LDL_FRAME_EII:
return ("LDL_FRAME_EII");
case LDL_FRAME_802_2:
return ("LDL_FRAME_802_2");
case LDL_FRAME_SNAP:
return ("LDL_FRAME_SNAP");
case LDL_FRAME_802_3:
return ("LDL_FRAME_802_3");
case LDL_FRAME_RAW_LLC:
return ("LDL_FRAME_RAW_LLC");
}
return ("LDL_FRAME_UNKNOWN");
}
STATIC char *
ldl_pkt_type(unsigned saptype)
{
switch (saptype) {
#ifdef ETH_P_ECHO
case ETH_P_ECHO:
return ("ETH_P_ECHO");
#endif
case ETH_P_PUP:
return ("ETH_P_PUP");
case ETH_P_IP:
return ("ETH_P_IP");
case ETH_P_X25:
return ("ETH_P_X25");
case ETH_P_ARP:
return ("ETH_P_ARP");
case ETH_P_BPQ:
return ("ETH_P_BPQ");
case ETH_P_DEC:
return ("ETH_P_DEC");
case ETH_P_DNA_DL:
return ("ETH_P_DNA_DL");
case ETH_P_DNA_RC:
return ("ETH_P_DNA_RC");
case ETH_P_DNA_RT:
return ("ETH_P_DNA_RT");
case ETH_P_LAT:
return ("ETH_P_LAT");
case ETH_P_DIAG:
return ("ETH_P_DIAG");
case ETH_P_CUST:
return ("ETH_P_CUST");
case ETH_P_SCA:
return ("ETH_P_SCA");
case ETH_P_RARP:
return ("ETH_P_RARP");
case ETH_P_ATALK:
return ("ETH_P_ATALK");
case ETH_P_AARP:
return ("ETH_P_AARP");
case ETH_P_IPX:
return ("ETH_P_IPX");
case ETH_P_IPV6:
return ("ETH_P_IPV6");
case ETH_P_802_3:
return ("ETH_P_802_3");
case ETH_P_AX25:
return ("ETH_P_AX25");
case ETH_P_ALL:
return ("ETH_P_ALL");
case ETH_P_802_2:
return ("ETH_P_802_2");
case ETH_P_SNAP:
return ("ETH_P_SNAP");
case ETH_P_DDCMP:
return ("ETH_P_DDCMP");
case ETH_P_WAN_PPP:
return ("ETH_P_WAN_PPP");
case ETH_P_PPP_MP:
return ("ETH_P_PPP_MP");
case ETH_P_LOCALTALK:
return ("ETH_P_LOCALTALK");
case ETH_P_PPPTALK:
return ("ETH_P_PPPTALK");
case ETH_P_TR_802_2:
return ("ETH_P_TR_802_2");
case ETH_P_MOBITEX:
return ("ETH_P_MOBITEX");
case ETH_P_CONTROL:
return ("ETH_P_CONTROL");
case ETH_P_IRDA:
return ("ETH_P_IRDA");
case 0:
return ("NULL(0)");
}
return ("ETH_P_UNKNOWN");
}
STATIC int
sap_create(struct dl *dl, sap_t dlsap, dl_ushort saptype)
{
struct pt *pt, *npt;
struct sap *sap;
ASSERT(dl != NULL);
ASSERT(dl->magic == DL_MAGIC);
ASSERT(dl->ndev != NULL);
saptype = htons(saptype);
if ((sap = ALLOC(sizeof(*sap))) == NULL)
return -1;
++sap_n_alloc;
memset(sap, 0, sizeof *sap);
sap->magic = SAP_MAGIC;
sap->dl = dl;
sap->sap = dlsap;
if (dl->sap == NULL) {
sap->next_sap = NULL;
dl->sap = sap;
} else {
sap->next_sap = dl->subs;
dl->subs = sap;
}
npt = NULL;
spin_lock(&first_pt_lock);
do {
for (pt = first_pt; pt; pt = pt->next)
if (pt->pt.type == saptype && pt->pt.dev == dl->ndev->dev)
break;
if (pt == NULL) {
if (npt == NULL) {
spin_unlock(&first_pt_lock);
if ((npt = ALLOC(sizeof(*pt))) == NULL) {
FREE(sap);
sap_n_alloc--;
return -1;
}
++pt_n_alloc;
memset(npt, 0, sizeof *npt);
spin_lock(&first_pt_lock);
} else {
npt->next = first_pt;
first_pt = pt = npt;
}
} else if (npt != NULL) {
FREE(npt);
npt = NULL;
pt_n_alloc--;
}
} while (pt == NULL);
sap->pt = pt;
if (pt->listen == NULL) {
ASSERT(pt->magic == 0);
sap->next_listen = pt->listen;
pt->listen = sap;
rwlock_init(&pt->lock);
pt->magic = PT_MAGIC;
pt->pt.type = saptype;
pt->pt.dev = dl->ndev->dev;
pt->pt.func = rcv_func;
#ifdef HAVE_KMEMB_STRUCT_PACKET_TYPE_AF_PACKET_PRIV
pt->pt.af_packet_priv = (void *) 1;
#else
#ifdef HAVE_KMEMB_STRUCT_PACKET_TYPE_DATA
pt->pt.data = (void *) 1;
#else
#error Must have HAVE_KMEMB_STRUCT_PACKET_TYPE_DATA or HAVE_KMEMB_STRUCT_PACKET_TYPE_AF_PACKET_PRIV defined.
#endif
#endif
#ifdef HAVE_KMEMB_STRUCT_PACKET_TYPE_NEXT
pt->pt.next = NULL;
#else
#ifdef HAVE_KMEMB_STRUCT_PACKET_TYPE_LIST
pt->pt.list = (struct list_head) LIST_HEAD_INIT(pt->pt.list);
#else
#error Must have HAVE_KMEMB_STRUCT_PACKET_TYPE_NEXT or HAVE_KMEMB_STRUCT_PACKET_TYPE_LIST defined.
#endif
#endif
spin_unlock(&first_pt_lock);
dev_add_pack(&pt->pt);
} else {
ASSERT(pt->magic == PT_MAGIC);
ASSERT(pt->pt.type == saptype);
ASSERT(pt->pt.func == rcv_func);
write_lock_bh(&pt->lock);
sap->next_listen = pt->listen;
pt->listen = sap;
write_unlock_bh(&pt->lock);
spin_unlock(&first_pt_lock);
}
return 0;
}
STATIC int
sap_destroy(struct dl *dl, struct sap *sap)
{
pl_t psw;
struct pt *pt, *opt;
struct sap **sapp_dl, **sapp_pt;
ASSERT(dl != NULL);
ASSERT(dl->magic == DL_MAGIC);
ASSERT(sap != NULL);
ASSERT(sap->magic == SAP_MAGIC);
ASSERT(dl->subs != NULL || sap == dl->sap);
pt = sap->pt;
ASSERT(pt != NULL);
ASSERT(pt->magic == PT_MAGIC);
SPLSTR(psw);
spin_lock(&first_pt_lock);
write_lock_bh(&pt->lock);
if (pt->listen == sap && sap->next_listen == NULL) {
write_unlock_bh(&pt->lock);
if (pt == first_pt)
first_pt = pt->next;
else {
for (opt = first_pt; opt->next != NULL; opt = opt->next)
if (opt->next == pt) {
opt->next = pt->next;
break;
}
}
spin_unlock(&first_pt_lock);
dev_remove_pack(&pt->pt);
pt->magic = 0;
FREE(pt);
--pt_n_alloc;
} else {
spin_unlock(&first_pt_lock);
sapp_pt = &pt->listen;
for (;;) {
ASSERT(*sapp_pt != NULL);
if (*sapp_pt == NULL) {
write_unlock_bh(&pt->lock);
SPLX(psw);
return -1;
}
if (*sapp_pt == sap)
break;
sapp_pt = &(*sapp_pt)->next_listen;
}
*sapp_pt = sap->next_listen;
write_unlock_bh(&pt->lock);
}
if (dl->sap == sap) {
ASSERT(dl->subs == NULL);
dl->sap = NULL;
} else {
sapp_dl = &dl->subs;
for (;;) {
ASSERT(*sapp_dl != NULL);
if (*sapp_dl == NULL) {
SPLX(psw);
return -1;
}
ASSERT((*sapp_dl)->magic == SAP_MAGIC);
ASSERT((*sapp_dl)->dl == dl);
if (*sapp_dl == sap)
break;
sapp_dl = &(*sapp_dl)->next_sap;
}
*sapp_dl = sap->next_sap;
}
sap->dl = NULL;
sap->pt = NULL;
sap->magic = 0;
FREE(sap);
--sap_n_alloc;
SPLX(psw);
return 0;
}
STATIC void
sap_destroy_all(struct dl *dl)
{
int ret;
ASSERT(dl != NULL);
while (dl->subs) {
ASSERT(dl->subs->pt != NULL);
ret = sap_destroy(dl, dl->subs);
ASSERT(ret == 0);
if (ret != 0)
return;
}
if (dl->sap) {
ASSERT(dl->sap->pt != NULL);
ret = sap_destroy(dl, dl->sap);
ASSERT(ret == 0);
}
}
STATIC INLINE void
hangup_set(struct dl *dl)
{
if ((dl->flags & LDLFLAG_HANGUP) == 0) {
ASSERT(dl->dlstate != DL_UNATTACHED);
ASSERT((dl->flags & LDLFLAG_HANGUP_DONE) != 0);
ASSERT(dl->ndev == NULL);
dl->flags |= LDLFLAG_HANGUP;
++n_hangup;
}
}
STATIC void ndev_release(struct dl *dl);
STATIC INLINE void
hangup_do(struct dl *dl)
{
pl_t psw;
SPLSTR(psw);
ASSERT((dl->flags & LDLFLAG_HANGUP) != 0);
if ((dl->flags & LDLFLAG_HANGUP_DONE) != 0) {
SPLX(psw);
return;
}
ASSERT(n_hangup > 0);
if (dl->dlstate == DL_UNBIND_PENDING || dl->dlstate == DL_SUBS_UNBIND_PND) {
SPLX(psw);
return;
}
if (dl->dlstate == DL_IDLE) {
sap_destroy_all(dl);
dl->dlstate = DL_UNBOUND;
}
if (dl->dlstate == DL_UNBOUND) {
ndev_release(dl);
dl->addr_len = 0;
dl->ndev = NULL;
dl->dlstate = DL_UNATTACHED;
}
ASSERT(dl->dlstate == DL_UNATTACHED);
SPLX(psw);
if (putctl(dl->rq->q_next, M_HANGUP)) {
SPLSTR(psw);
if ((dl->flags & LDLFLAG_HANGUP_DONE) == 0) {
dl->flags |= LDLFLAG_HANGUP_DONE;
--n_hangup;
}
SPLX(psw);
} else
printk("ldl: cannot send M_HANGUP\n");
}
STATIC INLINE struct ndev *
ndev_find(struct ldldev *dev)
{
struct ndev *ndev;
for (ndev = first_ndev; ndev; ndev = ndev->next)
if (ndev->dev == dev)
break;
return ndev;
}
STATIC struct ndev *
ndev_get(dl_ulong ppa)
{
dl_ulong i;
struct ldldev *dev;
struct ndev *ndev;
for (dev = dev_base, i = 0; dev; dev = dev->next, ++i)
if (ppa == i)
break;
if (dev == NULL)
return NULL;
ASSERT(ppa == i);
if ((ndev = ndev_find(dev)) == NULL) {
if ((ndev = ALLOC(sizeof(*ndev))) == NULL)
return NULL;
++ndev_n_alloc;
memset(ndev, 0, sizeof *ndev);
ndev->magic = NDEV_MAGIC;
ndev->dev = dev;
ndev->wr_max = DEV_WR_MAX;
ndev->wr_min = DEV_WR_MIN;
}
return ndev;
}
STATIC void
ndev_attach(struct ndev *ndev, struct dl *dl)
{
ASSERT(ndev != NULL);
ASSERT(ndev->magic == NDEV_MAGIC);
ASSERT(dl != NULL);
ASSERT(dl->magic == DL_MAGIC);
ASSERT(dl->ndev == NULL);
dl->ndev = ndev;
dl->next_ndev = ndev->endpoints;
ndev->endpoints = dl;
}
STATIC void
ndev_free(struct ndev *ndev)
{
ASSERT(ndev != NULL);
ASSERT(ndev->magic == NDEV_MAGIC);
ASSERT(ndev->dev == NULL);
if (atomic_read(&ndev->wr_cur) > 0 || ndev->endpoints != NULL)
return;
if (ndev->tx_congest_timer) {
untimeout(ndev->tx_congest_timer);
ndev->tx_congest_timer = 0;
}
ndev->magic = 0;
FREE(ndev);
--ndev_n_alloc;
}
STATIC void
ndev_release(struct dl *dl)
{
struct ndev *ndev = dl->ndev;
struct dl **dlp;
ASSERT(ndev != NULL);
ASSERT(ndev->magic == NDEV_MAGIC);
for (dlp = &ndev->endpoints; *dlp; dlp = &(*dlp)->next_ndev) {
ASSERT((*dlp)->magic == DL_MAGIC);
ASSERT((*dlp)->ndev == ndev);
if (*dlp == dl)
break;
}
ASSERT(*dlp == dl);
*dlp = dl->next_ndev;
dl->ndev = NULL;
if (ndev->endpoints == NULL) {
if (ndev->dev != NULL) {
START_BH_ATOMIC(ndev->dev);
qdisc_reset(ndev->dev->qdisc);
END_BH_ATOMIC(ndev->dev);
ndev->dev = NULL;
}
ndev_free(ndev);
}
}
STATIC void
ndev_down(struct ndev *ndev, int hard)
{
struct dl *dl;
ASSERT(ndev->magic == NDEV_MAGIC);
if (hard)
ndev->dev = NULL;
for (dl = ndev->endpoints; dl; dl = dl->next_ndev) {
ASSERT(dl->magic == DL_MAGIC);
if (hard || (dl->flags & LDLFLAG_SET_ADDR) == 0) {
ndev_release(dl);
hangup_set(dl);
}
}
}
STATIC void
ndev_wr_wakeup_endp(struct ndev *ndev)
{
struct dl *dl;
ASSERT(ndev->magic == NDEV_MAGIC);
for (dl = ndev->endpoints; dl; dl = dl->next_ndev) {
ASSERT(dl->magic == DL_MAGIC);
ASSERT(dl->ndev == ndev);
qenable(WR(dl->rq));
}
}
STATIC void
ndev_wr_wakeup(struct ndev *ndev)
{
ASSERT(ndev->magic == NDEV_MAGIC);
ASSERT(ndev->sleeping);
if (ndev->tx_congest_timer) {
untimeout(ndev->tx_congest_timer);
ndev->tx_congest_timer = 0;
}
ndev->sleeping = 0;
ndev_wr_wakeup_endp(ndev);
}
STATIC void streamscall
tx_congestion_timeout(caddr_t dp)
{
pl_t psw;
struct ndev *ndev = (struct ndev *) dp;
SPLSTR(psw);
ASSERT(ndev->magic == NDEV_MAGIC);
if (ndev->tx_congest_timer != 0) {
ndev->tx_congest_timer = 0;
ndev_wr_wakeup_endp(ndev);
}
SPLX(psw);
}
STATIC void
ndev_wr_sleep(struct ndev *ndev)
{
ASSERT(!ndev->sleeping);
if (!ndev->tx_congest_timer)
ndev->tx_congest_timer =
timeout(tx_congestion_timeout, (caddr_t) ndev, CONGESTION_BACKOFF_TICKS);
if (ndev->tx_congest_timer)
ndev->sleeping = 1;
else
printk("ldl: ndev_wr_sleep() failed\n");
}
STATIC void
ndev_skb_destruct(struct sk_buff *skb)
{
struct ndev *ndev;
ASSERT(skb != NULL);
#ifdef KERNEL_2_1
if (skb_cloned(skb))
return;
#else
if (skb->data_skb != skb)
return;
#endif
ndev = (struct ndev *) skb->sk;
skb->sk = NULL;
ASSERT(ndev != NULL);
ASSERT(ndev->magic == NDEV_MAGIC);
ASSERT(atomic_read(&ndev->wr_cur) >= skb->truesize);
atomic_sub(skb->truesize, &ndev->wr_cur);
if (ndev->dev != NULL) {
if (atomic_read(&ndev->wr_cur) <= ndev->wr_min) {
if (ndev->sleeping)
ndev_wr_wakeup(ndev);
}
} else {
ASSERT(ndev->endpoints == NULL);
if (atomic_read(&ndev->wr_cur) == 0)
ndev_free(ndev);
}
}
#ifdef KERNEL_2_1
STATIC int
ndev_xmit(struct ndev *ndev, struct sk_buff *skb)
{
pl_t psw;
int ret;
ASSERT(skb != NULL);
ASSERT(ndev != NULL);
ASSERT(ndev->magic == NDEV_MAGIC);
ASSERT(ndev->dev != NULL);
skb->mac.raw = skb->data;
skb->dev = ndev->dev;
atomic_add(skb->truesize, &ndev->wr_cur);
skb->sk = (struct sock *) ndev;
skb->destructor = ndev_skb_destruct;
if (atomic_read(&ndev->wr_cur) <= ndev->wr_max) {
switch (dev_queue_xmit(skb)) {
case NET_XMIT_SUCCESS:
default:
return DONE;
case NET_XMIT_DROP:
return RETRY;
case NET_XMIT_CN:
case NET_XMIT_POLICED:
ret = DONE;
break;
}
} else {
kfree_skb(skb);
ret = RETRY;
}
SPLSTR(psw);
if (!ndev->sleeping)
ndev_wr_sleep(ndev);
SPLX(psw);
return ret;
}
#else
#ifdef too_complicated_KERNEL_2_1
STATIC int
ndev_xmit(struct ndev *ndev, struct sk_buff *skb)
{
pl_t psw;
struct ldldev *dev;
ASSERT(skb != NULL);
ASSERT(ndev != NULL);
ASSERT(ndev->magic == NDEV_MAGIC);
ASSERT(ndev->dev != NULL);
skb->mac.raw = skb->data;
skb->dev = dev = ndev->dev;
atomic_add(skb->truesize, &ndev->wr_cur);
(struct ndev *) skb->sk = ndev;
skb->destructor = ndev_skb_destruct;
if (atomic_read(&ndev->wr_cur) <= ndev->wr_max) {
struct Qdisc *q;
START_BH_ATOMIC(dev);
q = dev->qdisc;
ASSERT(q != NULL);
if (q->enqueue) {
int ret;
ret = q->enqueue(skb, q);
qdisc_wakeup(dev);
END_BH_ATOMIC(dev);
if (ret == 1)
return DONE;
} else {
END_BH_ATOMIC(dev);
if (dev_queue_xmit(skb) >= 0)
return DONE;
}
SPLSTR(psw);
if (!ndev->sleeping)
ndev_wr_sleep(ndev);
SPLX(psw);
return RETRY;
} else {
SPLSTR(psw);
if (!ndev->sleeping)
ndev_wr_sleep(ndev);
SPLX(psw);
kfree_skb(skb);
return RETRY;
}
}
#else
STATIC int
ndev_xmit(struct ndev *ndev, struct sk_buff *skb, int pri)
{
atomic_add(skb->truesize, &ndev->wr_cur);
(struct ndev *) skb->sk = ndev;
skb->dev = ndev->dev;
skb->arp = 1;
skb->destructor = ndev_skb_destruct;
dev_queue_xmit(skb, ndev->dev, pri);
return DONE;
}
#endif
#endif
STATIC int
device_notification(struct notifier_block *notifier, unsigned long event, void *ptr)
{
struct ldldev *dev = ptr;
struct ndev *d;
struct dl *dl;
pl_t psw;
SPLSTR(psw);
#ifdef KERNEL_2_1
if (event == NETDEV_DOWN || event == NETDEV_UNREGISTER) {
#else
if (event == NETDEV_DOWN) {
#endif
if ((d = ndev_find(dev)) != NULL) {
ndev_down(d, event != NETDEV_DOWN);
if (n_hangup) {
SPLX(psw);
spin_lock(&first_open_lock);
for (dl = first_open; dl; dl = dl->next_open)
if ((dl->flags & LDLFLAG_HANGUP) != 0)
hangup_do(dl);
spin_unlock(&first_open_lock);
return NOTIFY_DONE;
}
}
}
SPLX(psw);
return NOTIFY_DONE;
}
STATIC struct notifier_block dl_notifier = {
device_notification,
NULL,
0
};
STATIC INLINE int
notifier_register(void)
{
return register_netdevice_notifier(&dl_notifier);
}
STATIC INLINE int
notifier_unregister(void)
{
return unregister_netdevice_notifier(&dl_notifier);
}
STATIC void streamscall
dl_bufcallback(long idx)
{
struct dl *dl = &dl_dl[idx];
ASSERT(dl->rq != NULL);
ASSERT(dl->bufwait);
dl->bufwait = 0;
qenable(WR(dl->rq));
}
STATIC int
dl_bufcall(struct dl *dl, mblk_t *mp, int size)
{
ASSERT(!dl->bufwait);
if ((dl->bufwait = bufcall(size, BPRI_HI, dl_bufcallback, dl - dl_dl)) == 0) {
printk("ldl: bufcall failed\n");
freemsg(mp);
return DONE;
}
return RETRY;
}
STATIC INLINE int
pri_dlpi2netdevice(dl_ulong pri)
{
ASSERT(pri >= 0);
ASSERT(pri <= 100);
return (pri < 33) ? LDLPRI_HI : (pri < 66) ? LDLPRI_MED : LDLPRI_LO;
}
STATIC INLINE int
reuse_msg(mblk_t *mp, dl_ushort size)
{
mblk_t *bp;
ASSERT(mp != NULL);
ASSERT(mp->b_datap != NULL);
if (mp->b_datap->db_lim - mp->b_datap->db_base < size) ;
return 0;
if (mp->b_datap->db_ref != 1)
return 0;
if ((bp = unlinkb(mp)) != NULL)
freemsg(bp);
mp->b_wptr = mp->b_rptr = mp->b_datap->db_base;
return 1;
}
STATIC INLINE void
make_dl_ok_ack(mblk_t *mp, dl_ulong primitive)
{
dl_ok_ack_t *ackp;
mp->b_datap->db_type = M_PCPROTO;
ackp = (dl_ok_ack_t *) mp->b_datap->db_base;
ackp->dl_primitive = DL_OK_ACK;
ackp->dl_correct_primitive = primitive;
mp->b_wptr += DL_OK_ACK_SIZE;
}
STATIC INLINE void
make_dl_error_ack(mblk_t *mp, dl_ulong primitive, dl_ulong err, dl_ulong uerr)
{
dl_error_ack_t *ackp;
mp->b_datap->db_type = M_PCPROTO;
ackp = (dl_error_ack_t *) mp->b_datap->db_base;
ackp->dl_primitive = DL_ERROR_ACK;
ackp->dl_error_primitive = primitive;
ackp->dl_errno = err;
ackp->dl_unix_errno = uerr;
mp->b_wptr += DL_ERROR_ACK_SIZE;
}
STATIC INLINE int
do_ok_ack(struct dl *dl, mblk_t **mp, dl_ulong primitive)
{
if (!reuse_msg(*mp, DL_OK_ACK_SIZE)) {
mblk_t *bp;
if ((bp = allocb(DL_OK_ACK_SIZE, BPRI_HI)) == NULL) {
if (dl_bufcall(dl, *mp, DL_OK_ACK_SIZE) == RETRY)
return RETRY;
else {
*mp = NULL;
return DONE;
}
}
freemsg(*mp);
*mp = bp;
}
make_dl_ok_ack(*mp, primitive);
return DONE;
}
STATIC int
reply_error_ack(struct dl *dl, mblk_t *mp, dl_ulong primitive, dl_ulong err, dl_ulong uerr)
{
if (!reuse_msg(mp, DL_ERROR_ACK_SIZE)) {
mblk_t *bp;
if ((bp = allocb(DL_ERROR_ACK_SIZE, BPRI_HI)) == NULL)
return dl_bufcall(dl, mp, DL_ERROR_ACK_SIZE);
freemsg(mp);
mp = bp;
}
make_dl_error_ack(mp, primitive, err, uerr);
putnext(dl->rq, mp);
ginc(error_ack_cnt);
return DONE;
}
STATIC int
eth_ii_want(struct dl *dl, unsigned char *fr, int len)
{
ASSERT(dl->sap_len == 2);
if (len < 14)
return 0;
if (*(short *) (fr + 12) != *(short *) dl->sap->sap.sap) {
struct sap *sap = dl->subs;
while (sap != NULL) {
if (*(short *) (fr + 12) == *(short *) sap->sap.sap)
break;
sap = sap->next_sap;
}
if (sap == NULL)
return 0;
}
if (memcmp(fr, dl->ndev->dev->dev_addr, 6) && memcmp(fr, dl->ndev->dev->broadcast, 6))
return 0;
return 1;
}
STATIC mblk_t *
eth_ii_rcvind(struct dl *dl, mblk_t *dp)
{
mblk_t *bp;
dl_unitdata_ind_t *ud;
unsigned short dsap = ntohs(*(unsigned short *) (dp->b_rptr + 12));
ASSERT(dl->sap_len == 2);
ASSERT(dl->addr_len == 6);
if ((bp = allocb(DL_UNITDATA_IND_SIZE + 16, BPRI_LO)) == NULL) {
freeb(dp);
return NULL;
}
bp->b_datap->db_type = M_PROTO;
ud = (dl_unitdata_ind_t *) bp->b_rptr;
ud->dl_primitive = DL_UNITDATA_IND;
ud->dl_dest_addr_length = 8;
ud->dl_dest_addr_offset = DL_UNITDATA_IND_SIZE + 8;
ud->dl_src_addr_length = 8;
ud->dl_src_addr_offset = DL_UNITDATA_IND_SIZE;
ud->dl_group_address = *dp->b_rptr & 1;
bp->b_wptr += DL_UNITDATA_IND_SIZE;
memcpy(bp->b_wptr, dp->b_rptr + 6, 6);
bp->b_wptr += 6;
*(unsigned short *) (bp->b_wptr) = dsap;
bp->b_wptr += 2;
memcpy(bp->b_wptr, dp->b_rptr, 6);
bp->b_wptr += 6;
*(unsigned short *) (bp->b_wptr) = dsap;
bp->b_wptr += 2;
dp->b_rptr += 14;
linkb(bp, dp);
return bp;
}
STATIC int
eth_ii_mkhdr(struct dl *dl, unsigned char *dst, int datalen, struct sk_buff *skb)
{
unsigned char *hdr;
unsigned short dsap;
dsap = htons(*(unsigned short *) (dst + 6));
hdr = skb_push(skb, 14);
memcpy(hdr, dst, 6);
memcpy(hdr + 6, dl->ndev->dev->dev_addr, 6);
*(unsigned short *) (hdr + 12) = dsap;
if (dsap != *(unsigned short *) dl->sap->sap.sap) {
struct sap *sap;
for (sap = dl->subs; sap; sap = sap->next_sap)
if (dsap == *(unsigned short *) sap->sap.sap)
return 1;
return 0;
}
return 1;
}
STATIC int
eth_8022_want(struct dl *dl, unsigned char *fr, int len)
{
ASSERT(dl->sap_len == 1);
if (len < 17 || (!(dl->flags & LDLFLAG_RAW) && *(fr + 16) != 0x03))
return 0;
if (ntohs(*(unsigned short *) (fr + 12)) < 3)
return 0;
if (dl->flags & LDLFLAG_PROMISC_SAP)
return 1;
if (*(fr + 14) != dl->sap->sap.sap[0]) {
struct sap *sap = dl->subs;
while (sap != NULL) {
if (*(fr + 14) == sap->sap.sap[0])
break;
sap = sap->next_sap;
}
if (sap == NULL)
return 0;
}
#if 0
{
int i;
printk("eth_8022_want: SAP checked, MAC@ not yet ...");
for (i = 0; i < 16; i++)
printk("%02x ", fr[i]);
printk("\n");
}
#endif
if (!memcmp(fr, dl->ndev->dev->dev_addr, 6))
return 1;
if (!memcmp(fr, dl->ndev->dev->broadcast, 6))
return 1;
{
struct dev_mc_list *dmi = dl->ndev->dev->mc_list;
while (dmi != NULL)
if (!memcmp(fr, dmi->dmi_addr, 6))
return 1;
else
dmi = dmi->next;
}
return 0;
}
STATIC mblk_t *
eth_8022_rcvind(struct dl *dl, mblk_t *dp)
{
mblk_t *bp;
dl_unitdata_ind_t *ud;
unsigned short len;
ASSERT(dl->sap_len == 1);
ASSERT(dl->addr_len == 6);
if ((bp = allocb(DL_UNITDATA_IND_SIZE + 14, BPRI_LO)) == NULL) {
freeb(dp);
return NULL;
}
bp->b_datap->db_type = M_PROTO;
ud = (dl_unitdata_ind_t *) bp->b_rptr;
ud->dl_primitive = DL_UNITDATA_IND;
ud->dl_dest_addr_length = 7;
ud->dl_dest_addr_offset = DL_UNITDATA_IND_SIZE + 7;
ud->dl_src_addr_length = 7;
ud->dl_src_addr_offset = DL_UNITDATA_IND_SIZE;
ud->dl_group_address = *dp->b_rptr & 1;
bp->b_wptr += DL_UNITDATA_IND_SIZE;
memcpy(bp->b_wptr, dp->b_rptr + 6, 6);
bp->b_wptr += 6;
*bp->b_wptr++ = *(dp->b_rptr + 15);
memcpy(bp->b_wptr, dp->b_rptr, 6);
bp->b_wptr += 6;
*bp->b_wptr++ = *(dp->b_rptr + 14);
len = ntohs(*(unsigned short *) (dp->b_rptr + 12)) - 3;
dp->b_rptr += 17;
if (dp->b_wptr - dp->b_rptr > len)
dp->b_wptr = dp->b_rptr + len;
linkb(bp, dp);
return bp;
}
STATIC int
eth_8022_mkhdr(struct dl *dl, unsigned char *dst, int datalen, struct sk_buff *skb)
{
unsigned char *hdr;
unsigned char dsap;
dsap = *(dst + 6);
if (dsap != dl->sap->sap.sap[0]) {
struct sap *sap;
for (sap = dl->subs; sap; sap = sap->next_sap)
if (dsap == sap->sap.sap[0])
break;
if (sap == NULL)
return 0;
}
hdr = skb_push(skb, 17);
memcpy(hdr, dst, 6);
memcpy(hdr + 6, dl->ndev->dev->dev_addr, 6);
*(unsigned short *) (hdr + 12) = htons(datalen + 3);
*(hdr + 14) = *(hdr + 15) = dsap;
*(hdr + 16) = 3;
return 1;
}
STATIC int
eth_raw8022_want(struct dl *dl, unsigned char *fr, int len)
{
ASSERT(dl->sap_len == 1);
if (len < 17 || ntohs(*(unsigned short *) (fr + 12)) < 3)
return 0;
if (*(unsigned short *) (fr + 14) == 0xffff)
return 0;
if (dl->flags & LDLFLAG_PROMISC_SAP)
return 1;
if (*(fr + 14) != dl->sap->sap.sap[0]) {
struct sap *sap = dl->subs;
while (sap != NULL) {
if (*(fr + 14) == sap->sap.sap[0])
break;
sap = sap->next_sap;
}
if (sap == NULL)
return 0;
}
if (memcmp(fr, dl->ndev->dev->dev_addr, 6) && memcmp(fr, dl->ndev->dev->broadcast, 6))
return 0;
return 1;
}
STATIC mblk_t *
eth_raw8022_rcvind(struct dl *dl, mblk_t *dp)
{
mblk_t *bp;
dl_unitdata_ind_t *ud;
unsigned short len;
ASSERT(dl->sap_len == 1);
ASSERT(dl->addr_len == 6);
if ((bp = allocb(DL_UNITDATA_IND_SIZE + 14, BPRI_LO)) == NULL) {
freeb(dp);
return NULL;
}
bp->b_datap->db_type = M_PROTO;
ud = (dl_unitdata_ind_t *) bp->b_rptr;
ud->dl_primitive = DL_UNITDATA_IND;
ud->dl_dest_addr_length = 7;
ud->dl_dest_addr_offset = DL_UNITDATA_IND_SIZE + 7;
ud->dl_src_addr_length = 7;
ud->dl_src_addr_offset = DL_UNITDATA_IND_SIZE;
ud->dl_group_address = *dp->b_rptr & 1;
bp->b_wptr += DL_UNITDATA_IND_SIZE;
memcpy(bp->b_wptr, dp->b_rptr + 6, 6);
bp->b_wptr += 6;
*bp->b_wptr++ = *(dp->b_rptr + 15);
memcpy(bp->b_wptr, dp->b_rptr, 6);
bp->b_wptr += 6;
*bp->b_wptr++ = *(dp->b_rptr + 14);
len = ntohs(*(unsigned short *) (dp->b_rptr + 12));
dp->b_rptr += 14;
if (dp->b_wptr - dp->b_rptr > len)
dp->b_wptr = dp->b_rptr + len;
linkb(bp, dp);
return bp;
}
STATIC int
eth_raw8022_mkhdr(struct dl *dl, unsigned char *dst, int datalen, struct sk_buff *skb)
{
unsigned char *hdr;
hdr = skb_push(skb, 14);
memcpy(hdr, dst, 6);
memcpy(hdr + 6, dl->ndev->dev->dev_addr, 6);
*(unsigned short *) (hdr + 12) = htons(datalen);
*(hdr + 14) = *(dst + 6);
*(hdr + 15) = dl->sap->sap.sap[0];
return 1;
}
STATIC int
eth_snap_want(struct dl *dl, unsigned char *fr, int len)
{
ASSERT(dl->sap_len == 2);
if (len < 22 || ntohs(*(unsigned short *) (fr + 12)) < 8
|| *(unsigned short *) (fr + 14) != 0xAAAA || *(fr + 16) != 0x03
|| memcmp(dl->oui, fr + 17, 3))
return 0;
if (memcmp(fr + 20, dl->sap->sap.sap, 2)) {
struct sap *sap = dl->subs;
while (sap != NULL) {
if (!memcmp(fr + 20, sap->sap.sap, 2))
break;
sap = sap->next_sap;
}
if (sap == NULL)
return 0;
}
if (memcmp(fr, dl->ndev->dev->dev_addr, 6) && memcmp(fr, dl->ndev->dev->broadcast, 6))
return 0;
return 1;
}
STATIC mblk_t *
eth_snap_rcvind(struct dl *dl, mblk_t *dp)
{
mblk_t *bp;
dl_unitdata_ind_t *ud;
unsigned short len;
unsigned short dsap = ntohs(*(unsigned short *) (dp->b_rptr + 17));
ASSERT(dl->sap_len == 2);
ASSERT(dl->addr_len == 6);
if ((bp = allocb(DL_UNITDATA_IND_SIZE + 22, BPRI_LO)) == NULL) {
freeb(dp);
return NULL;
}
bp->b_datap->db_type = M_PROTO;
ud = (dl_unitdata_ind_t *) bp->b_rptr;
ud->dl_primitive = DL_UNITDATA_IND;
ud->dl_dest_addr_length = 8;
ud->dl_dest_addr_offset = DL_UNITDATA_IND_SIZE + 8;
ud->dl_src_addr_length = 8;
ud->dl_src_addr_offset = DL_UNITDATA_IND_SIZE;
ud->dl_group_address = *dp->b_rptr & 1;
bp->b_wptr += DL_UNITDATA_IND_SIZE;
memcpy(bp->b_wptr, dp->b_rptr + 6, 6);
bp->b_wptr += 6;
*(unsigned short *) (bp->b_wptr) = dsap;
bp->b_wptr += 2;
memcpy(bp->b_wptr, dp->b_rptr, 6);
bp->b_wptr += 6;
*(unsigned short *) (bp->b_wptr) = dsap;
bp->b_wptr += 2;
len = ntohs(*(unsigned short *) (dp->b_rptr + 12)) - 8;
dp->b_rptr += 22;
if (dp->b_wptr - dp->b_rptr > len)
dp->b_wptr = dp->b_rptr + len;
linkb(bp, dp);
return bp;
}
STATIC int
eth_snap_mkhdr(struct dl *dl, unsigned char *dst, int datalen, struct sk_buff *skb)
{
unsigned char *hdr;
unsigned short dsap;
dsap = *(unsigned short *) (dst + 6);
if (dsap != *(unsigned short *) dl->sap->sap.sap) {
struct sap *sap = dl->subs;
while (sap != NULL) {
if (dsap == *(unsigned short *) sap->sap.sap)
break;
sap = sap->next_sap;
}
if (sap == NULL)
return 0;
}
hdr = skb_push(skb, 22);
memcpy(hdr, dst, 6);
memcpy(hdr + 6, dl->ndev->dev->dev_addr, 6);
*(unsigned short *) (hdr + 12) = htons(datalen + 8);
*(unsigned short *) (hdr + 14) = 0xaaaa;
*(hdr + 16) = 3;
memcpy(hdr + 17, dl->oui, 3);
*(unsigned short *) (hdr + 20) = dsap;
return 1;
}
STATIC int
ipx_8023_want(struct dl *dl, unsigned char *fr, int len)
{
ASSERT(dl->sap_len == 0);
if (len < 44 || ntohs(*(unsigned short *) (fr + 12)) < 30)
return 0;
if (ntohs(*(unsigned short *) (fr + 14)) != 0xffff)
return 0;
if (memcmp(fr, dl->ndev->dev->dev_addr, 6) && memcmp(fr, dl->ndev->dev->broadcast, 6))
return 0;
return 1;
}
STATIC mblk_t *
ipx_8023_rcvind(struct dl *dl, mblk_t *dp)
{
mblk_t *bp;
dl_unitdata_ind_t *ud;
unsigned short len;
ASSERT(dl->sap_len == 0);
ASSERT(dl->addr_len == 6);
if ((bp = allocb(DL_UNITDATA_IND_SIZE + 12, BPRI_LO)) == NULL) {
freeb(dp);
return NULL;
}
bp->b_datap->db_type = M_PROTO;
ud = (dl_unitdata_ind_t *) bp->b_rptr;
ud->dl_primitive = DL_UNITDATA_IND;
ud->dl_dest_addr_length = 6;
ud->dl_dest_addr_offset = DL_UNITDATA_IND_SIZE;
ud->dl_src_addr_length = 6;
ud->dl_src_addr_offset = DL_UNITDATA_IND_SIZE + 6;
ud->dl_group_address = *dp->b_rptr & 1;
bp->b_wptr += DL_UNITDATA_IND_SIZE;
memcpy(bp->b_wptr, dp->b_rptr, 12);
bp->b_wptr += 12;
len = *(unsigned short *) (dp->b_rptr + 12);
dp->b_rptr += 14;
if (dp->b_wptr - dp->b_rptr > len)
dp->b_wptr = dp->b_rptr + len;
linkb(bp, dp);
return bp;
}
STATIC int
ipx_8023_mkhdr(struct dl *dl, unsigned char *dst, int datalen, struct sk_buff *skb)
{
unsigned char *hdr;
hdr = skb_push(skb, 14);
memcpy(hdr, dst, 6);
memcpy(hdr + 6, dl->ndev->dev->dev_addr, 6);
*(unsigned short *) (hdr + 12) = htons(datalen);
return 1;
}
typedef struct tr_hdr {
u8 acf;
u8 fcf;
u8 dst_addr[6];
u8 src_addr[6];
u8 bl;
u8 df;
} tr_hdr_t;
typedef struct tr_llc_frm_hdr {
u8 llc_dsap;
u8 llc_ssap;
u8 llc_ctl[1];
} tr_llc_frm_hdr_t;
#define SADDR_0_RTE_PRES 0x80
#define RCF_0_LLLLL 0x1F
#define FCF_FF 0xC0
#define FCF_MAC 0x00
#define FCF_LLC 0x40
STATIC int
tr_8022_want(struct dl *dl, unsigned char *fr, int len)
{
tr_hdr_t *trp;
tr_llc_frm_hdr_t *llcp;
int rtelgth;
#if 0
{
int i;
printk("tr_8022_want:\n");
for (i = 0; i < 16; i++)
printk("%02x ", fr[i]);
printk("\n");
for (; i < 32; i++)
printk("%02x ", fr[i]);
printk("\n");
}
#endif
trp = (tr_hdr_t *) fr;
ASSERT(dl->sap_len == 1);
if (len < sizeof(tr_hdr_t) - 2 + sizeof(tr_llc_frm_hdr_t))
return 0;
if ((trp->fcf & FCF_FF) != FCF_LLC)
return (0);
if (trp->src_addr[0] & SADDR_0_RTE_PRES) {
rtelgth = trp->bl & RCF_0_LLLLL;
if (rtelgth < 2)
return 0;
if (rtelgth & 0x01)
return 0;
llcp = (tr_llc_frm_hdr_t *) (fr + sizeof(*trp) - 2 + rtelgth);
} else {
llcp = (tr_llc_frm_hdr_t *) (fr + sizeof(*trp) - 2);
}
if (!(dl->flags & LDLFLAG_RAW) && llcp->llc_ctl[0] != 0x03)
return 0;
if (dl->flags & LDLFLAG_PROMISC_SAP)
return 1;
if (llcp->llc_dsap != dl->sap->sap.sap[0]) {
struct sap *sap = dl->subs;
while (sap != NULL) {
if (llcp->llc_dsap == sap->sap.sap[0])
break;
sap = sap->next_sap;
}
if (sap == NULL)
return 0;
}
return 1;
}
STATIC mblk_t *
tr_8022_rcvind(struct dl *dl, mblk_t *dp)
{
mblk_t *bp;
dl_unitdata_ind_t *ud;
tr_hdr_t *trp;
tr_llc_frm_hdr_t *llcp;
int rtelgth = 0;
ASSERT(dl->sap_len == 1);
ASSERT(dl->addr_len == 6);
trp = (tr_hdr_t *) dp->b_rptr;
if (trp->src_addr[0] & SADDR_0_RTE_PRES) {
rtelgth = trp->bl & RCF_0_LLLLL;
}
llcp = (tr_llc_frm_hdr_t *) (dp->b_rptr + sizeof(*trp) - 2 + rtelgth);
if ((bp = allocb(DL_UNITDATA_IND_SIZE + 14, BPRI_LO)) == NULL) {
freeb(dp);
return NULL;
}
bp->b_datap->db_type = M_PROTO;
ud = (dl_unitdata_ind_t *) bp->b_rptr;
ud->dl_primitive = DL_UNITDATA_IND;
ud->dl_dest_addr_length = 7;
ud->dl_dest_addr_offset = DL_UNITDATA_IND_SIZE + 7;
ud->dl_src_addr_length = 7;
ud->dl_src_addr_offset = DL_UNITDATA_IND_SIZE;
ud->dl_group_address = 0;
bp->b_wptr += DL_UNITDATA_IND_SIZE;
memcpy(bp->b_wptr, trp->dst_addr, 6);
bp->b_wptr += 6;
*bp->b_wptr++ = llcp->llc_dsap;
memcpy(bp->b_wptr + 6, trp->src_addr, 6);
bp->b_wptr += 6;
*bp->b_wptr++ = llcp->llc_ssap;
dp->b_rptr += sizeof(*trp) - 2 + rtelgth + sizeof(tr_llc_frm_hdr_t);
linkb(bp, dp);
return bp;
}
STATIC int
tr_8022_mkhdr(struct dl *dl, unsigned char *dst, int datalen, struct sk_buff *skb)
{
tr_hdr_t *trp;
tr_llc_frm_hdr_t *llcp;
unsigned char dsap;
dsap = *(dst + 6);
if (dsap != dl->sap->sap.sap[0]) {
struct sap *sap;
for (sap = dl->subs; sap; sap = sap->next_sap)
if (dsap == sap->sap.sap[0])
break;
if (sap == NULL)
return 0;
}
trp = (tr_hdr_t *) skb_push(skb, sizeof(tr_hdr_t) - 2 + sizeof(*llcp));
trp->acf = 0x10;
trp->fcf = 0x40;
memcpy(trp->dst_addr, dst, 6);
memcpy(trp->src_addr, dl->ndev->dev->dev_addr, 6);
trp->src_addr[0] &= ~SADDR_0_RTE_PRES;
llcp = (tr_llc_frm_hdr_t *) (((char *) (trp + 1)) - 2);
llcp->llc_dsap = dsap;
llcp->llc_ssap = dsap;
llcp->llc_ctl[0] = 0x03;
return 1;
}
STATIC int
hdlc_raw_want(struct dl *dl, unsigned char *fr, int len)
{
return 1;
}
STATIC mblk_t *
hdlc_raw_rcvind(struct dl *dl, mblk_t *dp)
{
mblk_t *bp;
dl_unitdata_ind_t *ud;
if ((bp = allocb(DL_UNITDATA_IND_SIZE + 14, BPRI_LO)) == NULL) {
freeb(dp);
return NULL;
}
bp->b_datap->db_type = M_PROTO;
ud = (dl_unitdata_ind_t *) bp->b_rptr;
ud->dl_primitive = DL_UNITDATA_IND;
ud->dl_dest_addr_length = 0;
ud->dl_dest_addr_offset = 0;
ud->dl_src_addr_length = 0;
ud->dl_src_addr_offset = 0;
ud->dl_group_address = 0;
bp->b_wptr += DL_UNITDATA_IND_SIZE;
linkb(bp, dp);
return bp;
}
STATIC int
hdlc_raw_mkhdr(struct dl *dl, unsigned char *dst, int datalen, struct sk_buff *skb)
{
return 1;
}
STATIC int
fddi_8022_want(struct dl *dl, unsigned char *fr, int len)
{
ASSERT(0);
return 0;
}
STATIC mblk_t *
fddi_8022_rcvind(struct dl *dl, mblk_t *dp)
{
ASSERT(0);
return NULL;
}
STATIC int
fddi_8022_mkhdr(struct dl *dl, unsigned char *dst, int datalen, struct sk_buff *skb)
{
ASSERT(0);
return 0;
}
STATIC int
fddi_snap_want(struct dl *dl, unsigned char *fr, int len)
{
ASSERT(0);
return 0;
}
STATIC mblk_t *
fddi_snap_rcvind(struct dl *dl, mblk_t *dp)
{
ASSERT(0);
return NULL;
}
STATIC int
fddi_snap_mkhdr(struct dl *dl, unsigned char *dst, int datalen, struct sk_buff *skb)
{
ASSERT(0);
return 0;
}
STATIC INLINE void
dl_rcv_put(mblk_t *dp, struct dl *dl, int copy)
{
mblk_t *mp;
if (dp == NULL) {
if (copy == 0)
freeb(dp);
++dl->lost_rcv;
return;
}
if (copy != 0 && (dp = dupb(dp)) == NULL) {
++dl->lost_rcv;
ginc(net_rx_drp_cnt);
return;
}
ginc(net_rx_cnt);
if ((dl->flags & LDLFLAG_RAW) == 0) {
if ((mp = dl->rcvind(dl, dp)) == NULL) {
freeb(dp);
++dl->lost_rcv;
return;
}
} else
mp = dp;
#if 1
if (canput(dl->rq)) {
if (!putq(dl->rq, mp))
freemsg(mp);
ginc(unitdata_q_cnt);
} else {
freemsg(mp);
ginc(unitdata_drp_cnt);
++dl->lost_rcv;
}
#else
if (qsize(dl->rq) == 0 && canputnext(dl->rq)) {
if (ldl_debug_mask & LDL_DEBUG_UDIND)
ldl_mp_data_dump("ldl_unitdata_ind", mp,
ldl_debug_mask & LDL_DEBUG_ALLDATA);
putnext(dl->rq, mp);
ginc(unitdata_ind_cnt);
} else if (canput(dl->rq)) {
if (!putq(dl->rq, mp))
freemsg(mp);
ginc(unitdata_q_cnt);
} else {
freemsg(mp);
ginc(unitdata_drp_cnt);
++dl->lost_rcv;
}
#endif
}
void streamscall
mblk_destructor(caddr_t arg)
{
kfree_skb((struct sk_buff *) arg);
}
STATIC int
#ifdef HAVE_KMEMB_STRUCT_PACKET_TYPE_FUNC_4_ARGS
rcv_func(struct sk_buff *skb, struct ldldev *dev, struct packet_type *pt, struct ldldev *dev2)
#else
rcv_func(struct sk_buff *skb, struct ldldev *dev, struct packet_type *pt)
#endif
{
mblk_t *dp;
struct dl *dl, *last = NULL;
struct sap *sap;
#if 0
struct ethhdr *hdr = (struct ethhdr *) skb->mac.raw;
#endif
unsigned char *fr_ptr, fr_buf[LDL_MAX_HDR_LEN];
int fr_len;
struct free_rtn mblk_rtn;
ASSERT(dev->type == ARPHRD_ETHER || dev->type == ARPHRD_LOOPBACK
|| dev->type == ARPHRD_IEEE802 || IS_ARPHRD_IEEE802_TR(dev)
|| dev->type == ARPHRD_HDLC);
if (skb_is_nonlinear(skb)) {
struct sk_buff *b;
printk("ldl: non linear skb");
b = skb_clone(skb, GFP_ATOMIC);
kfree_skb(skb);
if (b == NULL)
return 0;
#ifdef HAVE_KFUNC_SKB_LINEARIZE_1_ARG
if (skb_linearize(b)) {
kfree_skb(b);
return 0;
}
#else
if (skb_linearize(b, GFP_ATOMIC)) {
kfree_skb(b);
return 0;
}
#endif
skb = b;
}
#if 1
mblk_rtn.free_func = mblk_destructor;
mblk_rtn.free_arg = (char *) skb;
fr_len = skb->tail - skb->mac.raw;
if ((dp = esballoc(skb->mac.raw - 2, fr_len + 2, BPRI_LO, &mblk_rtn)) != NULL) {
dp->b_rptr = dp->b_wptr += 2;
fr_ptr = dp->b_rptr;
dp->b_wptr += fr_len;
skb_get(skb);
} else {
fr_ptr = &fr_buf[0];
fr_len = min(skb->end - skb->mac.raw, (ptrdiff_t) LDL_MAX_HDR_LEN);
ASSERT(fr_len > 0);
memcpy(fr_buf, skb->mac.raw, fr_len);
}
#else
if ((dp = allocb(2 + fr_len, BPRI_LO)) != NULL) {
dp->b_rptr = dp->b_wptr += 2;
dp->b_datap->db_type = M_DATA;
memcpy(dp->b_wptr, skb->mac.raw, fr_len);
fr_ptr = dp->b_rptr;
dp->b_wptr += fr_len;
} else {
fr_ptr = &fr_buf[0];
fr_len = min(skb->end - skb->mac.raw, LDL_MAX_HDR_LEN);
ASSERT(fr_len > 0);
memcpy(fr_buf, skb->mac.raw, fr_len);
}
#endif
if (ldl_debug_mask & LDL_DEBUG_RCV_FUNC)
ldl_skbuff_dump("ldl_rcv_func: skb", skb, ldl_debug_mask & LDL_DEBUG_ALLDATA);
#ifdef KERNEL_2_1
dev_kfree_skb(skb);
#else
dev_kfree_skb(skb, FREE_WRITE);
#endif
if (ldl_debug_mask & LDL_DEBUG_RCV_FUNC)
ldl_bfr_dump("ldl_rcv_func", fr_ptr, fr_len, ldl_debug_mask & LDL_DEBUG_ALLDATA);
read_lock(&((struct pt *) pt)->lock);
ASSERT(((struct pt *) pt)->magic == PT_MAGIC);
for (sap = ((struct pt *) pt)->listen; sap != NULL; sap = sap->next_listen) {
dl = sap->dl;
ASSERT(dl != NULL);
ASSERT(dl->magic == DL_MAGIC);
ASSERT(dl->rq != NULL);
ASSERT(dl->ndev->dev == dev);
if (dl->wantsframe(dl, fr_ptr, fr_len)) {
if (last != NULL)
dl_rcv_put(dp, last, 1);
last = dl;
}
}
if (last != NULL) {
dl_rcv_put(dp, last, 0);
} else {
if (dp != NULL)
freeb(dp);
}
read_unlock(&((struct pt *) pt)->lock);
return 0;
}
STATIC void
send_uderror(struct dl *dl, char *addr, int addrlen, dl_ulong err, dl_ulong uerr)
{
mblk_t *mp;
dl_uderror_ind_t *nakp;
if ((mp = allocb(DL_UDERROR_IND_SIZE + addrlen, BPRI_HI)) == NULL)
return;
mp->b_datap->db_type = M_PCPROTO;
nakp = (dl_uderror_ind_t *) mp->b_wptr;
nakp->dl_primitive = DL_UDERROR_IND;
if (addrlen) {
nakp->dl_dest_addr_length = addrlen;
nakp->dl_dest_addr_offset = DL_UDERROR_IND_SIZE;
memcpy(mp->b_rptr + DL_UDERROR_IND_SIZE, addr, addrlen);
} else {
nakp->dl_dest_addr_length = 0;
nakp->dl_dest_addr_offset = 0;
}
nakp->dl_unix_errno = uerr;
nakp->dl_errno = err;
mp->b_wptr += DL_UDERROR_IND_SIZE + addrlen;
putnext(dl->rq, mp);
ginc(uderror_ind_cnt);
}
#define TXE_OUTSTATE 1
#define TXE_BADADDR 2
#define TXE_BADMTU 3
#define TXE_NOMEM 4
#define TXE_NULL 5
#define TXE_BADPRIO 6
STATIC int
tx_failed(struct dl *dl, mblk_t *mp, int err)
{
dl_unitdata_req_t *reqp = (dl_unitdata_req_t *) mp->b_rptr;
static dl_unitdata_req_t dummy;
ginc(net_tx_fail_cnt);
if (ldl_debug_mask & LDL_DEBUG_TX) {
ldl_mp_dump("ldl: tx_failed", mp, ldl_debug_mask & LDL_DEBUG_ALLDATA);
printk("ldl: tx_failed(%d)\n", err);
}
if (mp->b_datap->db_ref == 0) {
printk("ldl: message with ref-count zero\n");
return DONE;
}
if (mp->b_datap->db_type == M_DATA)
reqp = &dummy;
else
ASSERT(reqp->dl_primitive == DL_UNITDATA_REQ);
switch (err) {
case TXE_OUTSTATE:
send_uderror(dl, mp->b_rptr + reqp->dl_dest_addr_offset, reqp->dl_dest_addr_length,
DL_OUTSTATE, 0);
break;
case TXE_BADADDR:
send_uderror(dl, mp->b_rptr + reqp->dl_dest_addr_offset, reqp->dl_dest_addr_length,
DL_BADADDR, 0);
break;
case TXE_BADMTU:
send_uderror(dl, mp->b_rptr + reqp->dl_dest_addr_offset, reqp->dl_dest_addr_length,
DL_BADDATA, 0);
break;
case TXE_NOMEM:
case TXE_NULL:
send_uderror(dl, mp->b_rptr + reqp->dl_dest_addr_offset, reqp->dl_dest_addr_length,
DL_UNDELIVERABLE, 0);
break;
case TXE_BADPRIO:
send_uderror(dl, mp->b_rptr + reqp->dl_dest_addr_offset, reqp->dl_dest_addr_length,
DL_UNSUPPORTED, 0);
break;
default:
ASSERT(0);
}
freemsg(mp);
return DONE;
}
#ifndef KERNEL_2_1
STATIC INLINE int
tx_pri(struct dl *dl, dl_unitdata_req_t * reqp)
{
int p_min, p_max;
if (reqp != NULL) {
if (reqp->dl_priority.dl_min == DL_QOS_DONT_CARE)
p_min = 100;
else
p_min = reqp->dl_priority.dl_min;
if (reqp->dl_priority.dl_max == DL_QOS_DONT_CARE)
p_max = 0;
else
p_max = reqp->dl_priority.dl_max;
if (p_min < 0 || p_max < 0 || p_min > 100 || p_max > 100)
return -2;
if (p_max < dl->priority)
p_max = dl->priority;
if (p_max > p_min)
p_max = p_min;
p_min = pri_dlpi2netdevice(p_min);
p_max = pri_dlpi2netdevice(p_max);
} else {
p_min = LDLPRI_LO;
p_max = pri_dlpi2netdevice(dl->priority);
}
while (skb_queue_len(dl->ndev->dev->buffs + p_max) > dl->ndev->dev->tx_queue_len
&& p_max <= p_min)
++p_max;
if (p_max > p_min) {
pl_t psw;
SPLSTR(psw);
if (!dl->ndev->sleeping)
ndev_wr_sleep(dl->ndev);
SPLX(psw);
return -1;
}
return p_max;
}
#else
STATIC INLINE int
tx_pri(struct dl *dl, dl_unitdata_req_t * reqp)
{
int p_min, p_max;
if (reqp != NULL) {
if (reqp->dl_priority.dl_max == DL_QOS_DONT_CARE)
p_max = dl->priority;
else {
p_max = reqp->dl_priority.dl_max;
if (p_max < dl->priority)
p_max = dl->priority;
}
if (reqp->dl_priority.dl_min != DL_QOS_DONT_CARE) {
p_min = reqp->dl_priority.dl_min;
if (p_max > p_min)
p_max = p_min;
}
} else
p_max = dl->priority;
if (p_max < 0 || p_max > 100)
return -2;
return pri_dlpi2netdevice(p_max);
}
#endif
STATIC INLINE int
tx_func_proto(struct dl *dl, mblk_t *mp)
{
dl_unitdata_req_t *reqp;
mblk_t *dmp;
struct sk_buff *skb;
int pri, dlen;
ginc(unitdata_req_cnt);
ASSERT(mp->b_datap->db_type == M_PROTO || mp->b_datap->db_type == M_PCPROTO);
ASSERT(dl->magic == DL_MAGIC);
if (ldl_debug_mask & LDL_DEBUG_UDREQ)
ldl_mp_data_dump("ldl_unitdata_req", mp, ldl_debug_mask & LDL_DEBUG_ALLDATA);
if (dl->dlstate != DL_IDLE)
return tx_failed(dl, mp, TXE_OUTSTATE);
reqp = (dl_unitdata_req_t *) mp->b_rptr;
ASSERT(reqp->dl_primitive == DL_UNITDATA_REQ);
if ((dl->flags & LDLFLAG_RAW) != 0) {
int rslt;
dmp = mp->b_cont;
ASSERT(dmp != NULL);
if (dmp == NULL)
return tx_failed(dl, mp, TXE_NULL);
rslt = tx_func_raw(dl, dmp);
if (rslt == DONE) {
unlinkb(mp);
freemsg(mp);
}
return rslt;
}
if (reqp->dl_dest_addr_length != dl->addr_len + dl->sap_len)
return tx_failed(dl, mp, TXE_BADADDR);
if ((dmp = mp->b_cont) != NULL) {
ASSERT(dmp->b_datap->db_type == M_DATA);
dlen = msgdsize(dmp);
if (dlen > dl->mtu)
return tx_failed(dl, mp, TXE_BADMTU);
} else
dlen = 0;
if ((pri = tx_pri(dl, reqp)) < 0)
return pri == -1 ? RETRY : tx_failed(dl, mp, TXE_BADPRIO);
if ((skb = alloc_skb(dlen + dl->machdr_reserve, GFP_ATOMIC)) == NULL)
return tx_failed(dl, mp, TXE_NOMEM);
#ifndef KERNEL_2_1
skb->free = 1;
#endif
ASSERT(dlen == 0 || dmp != NULL);
skb_reserve(skb, dl->machdr_reserve);
#ifdef KERNEL_2_1
skb->nh.raw = skb->data;
#endif
if (dl->mkhdr(dl, (char *) reqp + reqp->dl_dest_addr_offset, dlen, skb) != 1) {
#ifdef KERNEL_2_1
kfree_skb(skb);
#else
kfree_skb(skb, FREE_WRITE);
#endif
return tx_failed(dl, mp, TXE_BADADDR);
}
while (dmp) {
void *p;
int dlen;
ASSERT(dmp->b_datap->db_type == M_DATA);
dlen = dmp->b_wptr - dmp->b_rptr;
ASSERT(dlen > 0);
p = skb_put(skb, dlen);
ASSERT(p != NULL);
memcpy(p, dmp->b_rptr, dlen);
dmp = dmp->b_cont;
}
ASSERT(skb->len == dlen + dl->machdr_len);
if (ldl_debug_mask & LDL_DEBUG_TX)
ldl_bfr_dump("ldl_tx_func", skb->data, skb->len,
ldl_debug_mask & LDL_DEBUG_ALLDATA);
#ifdef KERNEL_2_1
skb->priority = pri;
if (ndev_xmit(dl->ndev, skb) == RETRY)
return RETRY;
#else
if (ndev_xmit(dl->ndev, skb, pri) == RETRY)
return RETRY;
#if 0
atomic_add(skb->truesize, &ndev->wr_cur);
(struct ndev *) skb->sk = dl->ndev;
skb->dev = dl->ndev->dev;
skb->free = 1;
skb->arp = 1;
skb->destructor = ndev_skb_destruct;
dev_queue_xmit(skb, dl->ndev->dev, pri);
#endif
#endif
freemsg(mp);
ginc(net_tx_cnt);
return DONE;
}
STATIC int
tx_func_raw(struct dl *dl, mblk_t *mp)
{
mblk_t *omp = mp;
struct sk_buff *skb;
int pri, dlen;
ASSERT(mp->b_datap->db_type == M_DATA);
ASSERT(dl->magic == DL_MAGIC);
ASSERT((dl->flags & LDLFLAG_RAW) != 0);
if (dl->dlstate != DL_IDLE || !driver_started(dl->ndev->dev)
|| netif_queue_stopped(dl->ndev->dev))
return tx_failed(dl, mp, TXE_OUTSTATE);
if ((pri = tx_pri(dl, NULL)) < 0)
return pri == -1 ? RETRY : tx_failed(dl, mp, TXE_BADPRIO);
dlen = msgdsize(mp);
if (dlen > dl->mtu || dlen < dl->machdr_len)
return tx_failed(dl, mp, TXE_BADMTU);
if ((skb = alloc_skb(dlen, GFP_ATOMIC)) == NULL)
return tx_failed(dl, mp, TXE_NOMEM);
#ifdef KERNEL_2_1
skb->nh.raw = skb->data + dl->machdr_len;
#else
skb->free = 1;
#endif
while (mp) {
void *p;
int dlen;
ASSERT(mp->b_datap->db_type == M_DATA);
dlen = mp->b_wptr - mp->b_rptr;
ASSERT(dlen > 0);
p = skb_put(skb, dlen);
ASSERT(p != NULL);
memcpy(p, mp->b_rptr, dlen);
mp = mp->b_cont;
}
ASSERT(skb->len == dlen);
if (ldl_debug_mask & LDL_DEBUG_TX)
ldl_bfr_dump("ldl_tx_func", skb->data, skb->len,
ldl_debug_mask & LDL_DEBUG_ALLDATA);
#ifdef KERNEL_2_1
skb->priority = pri;
if (ndev_xmit(dl->ndev, skb) == RETRY)
return RETRY;
#else
if (ndev_xmit(dl->ndev, skb, pri) == RETRY)
return RETRY;
#endif
freemsg(omp);
ginc(net_tx_cnt);
return DONE;
}
STATIC INLINE int
ws_info(struct dl *dl, mblk_t *mp)
{
dl_info_ack_t *ackp;
dl_qos_cl_sel1_t *qos;
dl_qos_cl_range1_t *qos_range;
int len;
len = DL_INFO_ACK_SIZE + sizeof(dl_qos_cl_sel1_t) + sizeof(dl_qos_cl_range1_t);
if (dl->dlstate != DL_UNATTACHED)
len += dl->addr_len;
if (dl->dlstate == DL_IDLE)
len += dl->addr_len + dl->sap_len;
if (!reuse_msg(mp, len)) {
mblk_t *bp;
if ((bp = allocb(len, BPRI_HI)) == NULL)
return dl_bufcall(dl, mp, DL_ERROR_ACK_SIZE);
freemsg(mp);
mp = bp;
}
mp->b_datap->db_type = M_PCPROTO;
ackp = (dl_info_ack_t *) mp->b_wptr;
ackp->dl_primitive = DL_INFO_ACK;
ackp->dl_min_sdu = MINDATA;
ackp->dl_reserved = 0;
ackp->dl_current_state = dl->dlstate;
ackp->dl_sap_length = (dl->dlstate == DL_UNATTACHED) ? -2 : -dl->sap_len;
ackp->dl_service_mode = DL_CLDLS;
ackp->dl_provider_style = DL_STYLE2;
ackp->dl_version = DL_VERSION_2;
ackp->dl_growth = 0;
mp->b_wptr += DL_INFO_ACK_SIZE;
qos = (dl_qos_cl_sel1_t *) mp->b_wptr;
qos->dl_qos_type = DL_QOS_CL_SEL1;
qos->dl_trans_delay = DL_UNKNOWN;
qos->dl_priority = dl->priority;
qos->dl_protection = DL_NONE;
qos->dl_residual_error = DL_UNKNOWN;
ackp->dl_qos_length = sizeof(dl_qos_cl_sel1_t);
ackp->dl_qos_offset = (char *) qos - (char *) ackp;
mp->b_wptr += sizeof(dl_qos_cl_sel1_t);
qos_range = (dl_qos_cl_range1_t *) mp->b_wptr;
qos_range->dl_qos_type = DL_QOS_CL_RANGE1;
qos_range->dl_trans_delay.dl_target_value = DL_UNKNOWN;
qos_range->dl_trans_delay.dl_accept_value = DL_UNKNOWN;
qos_range->dl_priority.dl_min = 100;
qos_range->dl_priority.dl_max = 0;
qos_range->dl_protection.dl_min = DL_NONE;
qos_range->dl_protection.dl_max = DL_NONE;
qos_range->dl_residual_error = DL_UNKNOWN;
ackp->dl_qos_range_length = sizeof(dl_qos_cl_range1_t);
ackp->dl_qos_range_offset = (char *) qos_range - (char *) ackp;
mp->b_wptr += sizeof(dl_qos_cl_range1_t);
if (dl->dlstate != DL_UNATTACHED) {
int ofs = DL_INFO_ACK_SIZE + sizeof(dl_qos_cl_sel1_t) + sizeof(dl_qos_cl_range1_t);
ASSERT(dl->ndev != NULL);
ASSERT(dl->ndev->dev != NULL);
ackp->dl_sap_length = -dl->sap_len;
ackp->dl_max_sdu = dl->ndev->dev->mtu;
switch (dl->ndev->dev->type) {
case ARPHRD_ETHER:
ackp->dl_mac_type = DL_ETHER;
break;
case ARPHRD_LOOPBACK:
ackp->dl_mac_type = DL_LOOP;
break;
case ARPHRD_FDDI:
ackp->dl_mac_type = DL_FDDI;
break;
case ARPHRD_IEEE802:
#if defined(ARPHRD_IEEE802_TR)
case ARPHRD_IEEE802_TR:
#endif
ackp->dl_mac_type = DL_TPR;
break;
case ARPHRD_HDLC:
ackp->dl_mac_type = DL_HDLC;
break;
default:
ackp->dl_mac_type = DL_OTHER;
break;
}
ackp->dl_brdcst_addr_offset = ofs;
ackp->dl_brdcst_addr_length = dl->addr_len;
memcpy(mp->b_wptr, dl->ndev->dev->broadcast, dl->addr_len);
mp->b_wptr += dl->addr_len;
if (dl->dlstate == DL_IDLE) {
ASSERT(dl->sap != NULL);
ofs += dl->addr_len;
ackp->dl_addr_offset = ofs;
ackp->dl_addr_length = dl->addr_len + dl->sap_len;
memcpy(mp->b_wptr, dl->ndev->dev->dev_addr, dl->addr_len);
mp->b_wptr += dl->addr_len;
if (dl->framing == LDL_FRAME_EII || dl->framing == LDL_FRAME_SNAP)
*(unsigned short *) mp->b_wptr =
ntohs(*(unsigned short *) &dl->sap->sap.sap[0]);
else
memcpy(mp->b_wptr, &dl->sap->sap.sap[0], dl->sap_len);
mp->b_wptr += dl->sap_len;
ackp->dl_max_sdu = dl->ndev->dev->mtu;
} else {
if ((dl->flags & LDLFLAG_PEDANTIC_STANDARD) != 0)
ackp->dl_sap_length = 0;
ackp->dl_addr_length = 0;
ackp->dl_addr_offset = 0;
}
} else {
ackp->dl_sap_length = 0;
ackp->dl_mac_type = DL_OTHER;
ackp->dl_max_sdu = MAXDATA;
ackp->dl_addr_length = 0;
ackp->dl_addr_offset = 0;
ackp->dl_brdcst_addr_length = 0;
ackp->dl_brdcst_addr_offset = 0;
}
putnext(dl->rq, mp);
return DONE;
}
STATIC INLINE int
ws_phys_addr(struct dl *dl, mblk_t *mp)
{
dl_phys_addr_req_t *reqp;
dl_phys_addr_ack_t *ackp;
int len;
if (dl->dlstate == DL_UNATTACHED)
return reply_error_ack(dl, mp, DL_PHYS_ADDR_REQ, DL_OUTSTATE, 0);
reqp = (dl_phys_addr_req_t *) mp->b_rptr;
switch (reqp->dl_addr_type) {
case DL_CURR_PHYS_ADDR:
len = DL_PHYS_ADDR_ACK_SIZE + dl->addr_len;
if (!reuse_msg(mp, len)) {
mblk_t *bp;
if ((bp = allocb(len, BPRI_HI)) == NULL)
return dl_bufcall(dl, mp, DL_ERROR_ACK_SIZE);
freemsg(mp);
mp = bp;
}
mp->b_datap->db_type = M_PCPROTO;
ackp = (dl_phys_addr_ack_t *) mp->b_wptr;
ackp->dl_primitive = DL_PHYS_ADDR_ACK;
ackp->dl_addr_length = dl->addr_len;
ackp->dl_addr_offset = DL_PHYS_ADDR_ACK_SIZE;
mp->b_wptr += DL_PHYS_ADDR_ACK_SIZE;
memcpy(mp->b_wptr, dl->ndev->dev->dev_addr, dl->addr_len);
mp->b_wptr += dl->addr_len;
putnext(dl->rq, mp);
return DONE;
case DL_FACT_PHYS_ADDR:
default:
return reply_error_ack(dl, mp, DL_PHYS_ADDR_REQ, DL_NOTSUPPORTED, 0);
}
}
#if 0
STATIC INLINE int
ws_set_phys_addr(struct dl *dl, mblk_t *mp)
{
dl_set_phys_addr_req_t *reqp;
int err;
mblk_t *ack_mp;
pl_t psw;
if (dl->dlstate == DL_UNATTACHED)
return reply_error_ack(dl, mp, DL_SET_PHYS_ADDR_REQ, DL_OUTSTATE, 0);
if (dl->ndev->dev->set_mac_address == NULL)
return reply_error_ack(dl, mp, DL_SET_PHYS_ADDR_REQ, DL_NOTSUPPORTED, 0);
reqp = (dl_set_phys_addr_req_t *) mp->b_rptr;
if (reqp->dl_addr_length != dl->addr_len)
return reply_error_ack(dl, mp, DL_SET_PHYS_ADDR_REQ, DL_BADADDR, 0);
ASSERT(DL_ERROR_ACK_SIZE >= DL_OK_ACK_SIZE);
if ((ack_mp = allocb(DL_ERROR_ACK_SIZE, BPRI_HI)) == NULL)
return dl_bufcall(dl, ack_mp, DL_ERROR_ACK_SIZE);
SPLSTR(psw);
dl->flags |= LDLFLAG_SET_ADDR;
SPLX(psw);
err = dev_close(dl->ndev->dev);
SPLSTR(psw);
dl->flags &= ~LDLFLAG_SET_ADDR;
SPLX(psw);
if (err) {
freemsg(mp);
make_dl_error_ack(ack_mp, DL_SET_PHYS_ADDR_REQ, DL_SYSERR, -err);
putnext(dl->rq, ack_mp);
ginc(error_ack_cnt);
return DONE;
}
dl->ndev->dev->set_mac_address(dl->ndev->dev, mp->b_rptr + reqp->dl_addr_offset);
freemsg(mp);
err = dev_open(dl->ndev->dev);
if (err) {
freemsg(ack_mp);
SPLSTR(psw);
hangup_set(dl);
SPLX(psw);
hangup_do(dl);
return DONE;
}
ASSERT((dl->ndev->dev->flags & IFF_UP) != 0);
ASSERT((dl->ndev->dev->flags & IFF_RUNNING) != 0);
make_dl_ok_ack(ack_mp, DL_SET_PHYS_ADDR_REQ);
putnext(dl->rq, ack_mp);
ginc(ok_ack_cnt);
return DONE;
}
#endif
STATIC INLINE int
ws_attach(struct dl *dl, mblk_t *mp)
{
struct ndev *ndev;
struct ldldev *dev;
dl_attach_req_t *reqp;
dl_ulong framing, ppa;
pl_t psw;
ginc(attach_req_cnt);
if (dl->dlstate != DL_UNATTACHED)
return reply_error_ack(dl, mp, DL_ATTACH_REQ, DL_OUTSTATE, 0);
reqp = (dl_attach_req_t *) mp->b_rptr;
ppa = reqp->dl_ppa & ~LDL_FRAME_MASK;
framing = reqp->dl_ppa & LDL_FRAME_MASK;
if (framing != LDL_FRAME_EII && framing != LDL_FRAME_802_2 && framing != LDL_FRAME_SNAP
&& framing != LDL_FRAME_802_3 && framing != LDL_FRAME_RAW_LLC)
return reply_error_ack(dl, mp, DL_ATTACH_REQ, DL_BADPPA, 0);
SPLSTR(psw);
ndev = ndev_get(ppa);
if (ndev == NULL) {
SPLX(psw);
return reply_error_ack(dl, mp, DL_ATTACH_REQ, DL_BADPPA, 0);
}
dev = ndev->dev;
ASSERT(dev != NULL);
switch (dev->type) {
case ARPHRD_ETHER:
case ARPHRD_LOOPBACK:
if (framing != LDL_FRAME_EII && framing != LDL_FRAME_802_2
&& framing != LDL_FRAME_SNAP && framing != LDL_FRAME_802_3
&& framing != LDL_FRAME_RAW_LLC) {
SPLX(psw);
return reply_error_ack(dl, mp, DL_ATTACH_REQ, DL_BADPPA, 0);
}
break;
case ARPHRD_FDDI:
if (framing == 0)
framing = LDL_FRAME_SNAP;
if (framing != LDL_FRAME_802_2 && framing != LDL_FRAME_SNAP
&& framing != LDL_FRAME_RAW_LLC) {
SPLX(psw);
return reply_error_ack(dl, mp, DL_ATTACH_REQ, DL_BADPPA, 0);
}
break;
case ARPHRD_IEEE802:
#if defined(ARPHRD_IEEE802_TR)
case ARPHRD_IEEE802_TR:
#endif
if (framing != LDL_FRAME_802_2 && framing != LDL_FRAME_SNAP
&& framing != LDL_FRAME_RAW_LLC) {
SPLX(psw);
return reply_error_ack(dl, mp, DL_ATTACH_REQ, DL_BADPPA, 0);
}
break;
case ARPHRD_HDLC:
if (framing != LDL_FRAME_RAW_LLC) {
SPLX(psw);
return reply_error_ack(dl, mp, DL_ATTACH_REQ, DL_BADPPA, 0);
}
break;
default:
printk("ldl: ws_attach: Unknown dev->type (%d)\n", (int) (dev->type));
SPLX(psw);
return reply_error_ack(dl, mp, DL_ATTACH_REQ, DL_BADPPA, 0);
}
if (do_ok_ack(dl, &mp, DL_ATTACH_REQ) == RETRY) {
SPLX(psw);
return RETRY;
} else if (mp == NULL) {
SPLX(psw);
return DONE;
}
dl->dlstate = DL_UNBOUND;
dl->addr_len = ETH_ALEN;
dl->framing = framing;
switch (dl->framing) {
case LDL_FRAME_EII:
dl->mtu = 1500;
dl->machdr_len = 14;
dl->machdr_reserve = 16;
dl->sap_len = 2;
dl->wantsframe = eth_ii_want;
dl->rcvind = eth_ii_rcvind;
dl->mkhdr = eth_ii_mkhdr;
break;
case LDL_FRAME_SNAP:
dl->oui[0] = dl->oui[1] = dl->oui[2] = '\0';
dl->sap_len = 2;
if (dev->type == ARPHRD_FDDI) {
dl->mtu = 4470;
dl->machdr_len = 21;
dl->machdr_reserve = 24;
dl->wantsframe = fddi_snap_want;
dl->rcvind = fddi_snap_rcvind;
dl->mkhdr = fddi_snap_mkhdr;
} else {
dl->mtu = 1492;
dl->machdr_len = 22;
dl->machdr_reserve = 24;
dl->wantsframe = eth_snap_want;
dl->rcvind = eth_snap_rcvind;
dl->mkhdr = eth_snap_mkhdr;
}
break;
case LDL_FRAME_802_2:
dl->sap_len = 1;
switch (dev->type) {
case ARPHRD_IEEE802:
#if defined(ARPHRD_IEEE802_TR)
case ARPHRD_IEEE802_TR:
#endif
token_ring_case:
dl->sap_len = 1;
dl->mtu = 1514;
dl->machdr_len = sizeof(tr_hdr_t) - 2 + sizeof(tr_llc_frm_hdr_t);
dl->machdr_reserve = dl->machdr_len + 64;
dl->wantsframe = tr_8022_want;
dl->rcvind = tr_8022_rcvind;
dl->mkhdr = tr_8022_mkhdr;
break;
case ARPHRD_FDDI:
dl->mtu = 4475;
dl->machdr_len = 16;
dl->machdr_reserve = 24;
dl->wantsframe = fddi_8022_want;
dl->rcvind = fddi_8022_rcvind;
dl->mkhdr = fddi_8022_mkhdr;
break;
case ARPHRD_ETHER:
default:
dl->mtu = 1497;
dl->machdr_len = 17;
dl->machdr_reserve = 24;
dl->wantsframe = eth_8022_want;
dl->rcvind = eth_8022_rcvind;
dl->mkhdr = eth_8022_mkhdr;
break;
}
break;
case LDL_FRAME_RAW_LLC:
dl->sap_len = 1;
dl->flags |= LDLFLAG_RAW;
switch (dev->type) {
case ARPHRD_IEEE802:
#if defined(ARPHRD_IEEE802_TR)
case ARPHRD_IEEE802_TR:
#endif
goto token_ring_case;
case ARPHRD_FDDI:
ASSERT(0);
SPLX(psw);
return reply_error_ack(dl, mp, DL_ATTACH_REQ, DL_BADPPA, 0);
case ARPHRD_HDLC:
if (dev->mtu)
dl->mtu = dev->mtu;
else
dl->mtu = 1500;
dl->machdr_len = 0;
dl->machdr_reserve = 0;
dl->wantsframe = hdlc_raw_want;
dl->rcvind = hdlc_raw_rcvind;
dl->mkhdr = hdlc_raw_mkhdr;
break;
case ARPHRD_ETHER:
default:
dl->mtu = 1500;
dl->machdr_len = 14;
dl->machdr_reserve = 16;
dl->wantsframe = eth_raw8022_want;
dl->rcvind = eth_raw8022_rcvind;
dl->mkhdr = eth_raw8022_mkhdr;
break;
}
break;
case LDL_FRAME_802_3:
dl->mtu = 1500;
dl->machdr_len = 14;
dl->machdr_reserve = 16;
dl->sap_len = 0;
dl->wantsframe = ipx_8023_want;
dl->rcvind = ipx_8023_rcvind;
dl->mkhdr = ipx_8023_mkhdr;
break;
default:
ASSERT(0);
SPLX(psw);
return reply_error_ack(dl, mp, DL_ATTACH_REQ, DL_BADPPA, 0);
}
ndev_attach(ndev, dl);
SPLX(psw);
if (ldl_debug_mask & LDL_DEBUG_ATTACH)
printk("ldl: ws_attach: " "ppa=%lx dev=\"%s\" framing=%s\n", (long) ppa, dev->name,
ldl_framing_type(framing));
putnext(dl->rq, mp);
ginc(ok_ack_cnt);
return DONE;
}
STATIC INLINE int
ws_detach(struct dl *dl, mblk_t *mp)
{
pl_t psw;
ginc(detach_req_cnt);
if (dl->dlstate != DL_UNBOUND)
return reply_error_ack(dl, mp, DL_DETACH_REQ, DL_OUTSTATE, 0);
if (do_ok_ack(dl, &mp, DL_DETACH_REQ) == RETRY)
return RETRY;
else if (mp == NULL)
return DONE;
SPLSTR(psw);
ASSERT(dl->dlstate == DL_UNBOUND);
dl->dlstate = DL_UNATTACHED;
dl->addr_len = 0;
ndev_release(dl);
SPLX(psw);
putnext(dl->rq, mp);
ginc(ok_ack_cnt);
return DONE;
}
STATIC INLINE int
ws_bind(struct dl *dl, mblk_t *mp)
{
dl_bind_req_t *reqp;
dl_bind_ack_t *ackp;
sap_t dlsap;
unsigned short saptype;
pl_t psw;
int len;
ginc(bind_req_cnt);
SPLSTR(psw);
if (dl->dlstate != DL_UNBOUND) {
SPLX(psw);
return reply_error_ack(dl, mp, DL_BIND_REQ, DL_OUTSTATE, 0);
}
reqp = (dl_bind_req_t *) mp->b_rptr;
if (reqp->dl_service_mode != DL_CLDLS) {
SPLX(psw);
return reply_error_ack(dl, mp, DL_BIND_REQ, DL_UNSUPPORTED, 0);
}
if (reqp->dl_xidtest_flg != 0) {
SPLX(psw);
return reply_error_ack(dl, mp, DL_BIND_REQ, DL_NOAUTO, 0);
}
switch (dl->framing) {
case LDL_FRAME_SNAP:
if (reqp->dl_sap <= 0xffff) {
*(unsigned short *) &dlsap.sap[0] = htons((unsigned short) reqp->dl_sap);
saptype = ETH_P_802_2;
break;
} else {
SPLX(psw);
return reply_error_ack(dl, mp, DL_BIND_REQ, DL_BADADDR, 0);
}
case LDL_FRAME_EII:
if (reqp->dl_sap <= 0xffff) {
*(unsigned short *) &dlsap.sap[0] = htons((unsigned short) reqp->dl_sap);
saptype = (unsigned short) reqp->dl_sap;
break;
} else {
SPLX(psw);
return reply_error_ack(dl, mp, DL_BIND_REQ, DL_BADADDR, 0);
}
case LDL_FRAME_802_2:
case LDL_FRAME_RAW_LLC:
if (reqp->dl_sap <= 0xff) {
dlsap.sap[0] = reqp->dl_sap;
saptype = ETH_P_802_2;
break;
} else {
SPLX(psw);
return reply_error_ack(dl, mp, DL_BIND_REQ, DL_BADADDR, 0);
}
case LDL_FRAME_802_3:
if (reqp->dl_sap == 0) {
saptype = ETH_P_802_3;
break;
} else {
SPLX(psw);
return reply_error_ack(dl, mp, DL_BIND_REQ, DL_BADADDR, 0);
}
default:
ASSERT(0);
return reply_error_ack(dl, mp, DL_BIND_REQ, DL_BADADDR, 0);
}
if (sap_create(dl, dlsap, saptype) < 0) {
SPLX(psw);
return reply_error_ack(dl, mp, DL_BIND_REQ, DL_SYSERR, ENOMEM);
}
len = DL_BIND_ACK_SIZE + dl->addr_len + dl->sap_len;
if (!reuse_msg(mp, len)) {
mblk_t *bp;
if ((bp = allocb(len, BPRI_HI)) == NULL) {
SPLX(psw);
return dl_bufcall(dl, mp, len);
}
freemsg(mp);
mp = bp;
}
mp->b_datap->db_type = M_PCPROTO;
ackp = (dl_bind_ack_t *) mp->b_wptr;
ackp->dl_primitive = DL_BIND_ACK;
ackp->dl_sap = reqp->dl_sap;
ackp->dl_addr_length = dl->addr_len + dl->sap_len;
ackp->dl_addr_offset = DL_BIND_ACK_SIZE;
ackp->dl_max_conind = 0;
ackp->dl_xidtest_flg = 0;
mp->b_wptr += DL_BIND_ACK_SIZE;
memcpy(mp->b_wptr, dl->ndev->dev->dev_addr, dl->addr_len);
mp->b_wptr += dl->addr_len;
if (dl->framing == LDL_FRAME_EII || dl->framing == LDL_FRAME_SNAP)
*(unsigned short *) mp->b_wptr = ntohs(*(unsigned short *) &dlsap.sap[0]);
else
memcpy(mp->b_wptr, &dl->sap->sap.sap[0], dl->sap_len);
mp->b_wptr += dl->sap_len;
dl->dlstate = DL_IDLE;
SPLX(psw);
if (ldl_debug_mask & LDL_DEBUG_BIND)
printk("ldl: ws_bind: " "dl_sap=%lx framing=%s pkt-type=%s\n", (long) reqp->dl_sap,
ldl_framing_type(dl->framing), ldl_pkt_type(saptype));
putnext(dl->rq, mp);
return DONE;
}
STATIC INLINE int
ws_unbind(struct dl *dl, mblk_t *mp)
{
pl_t psw;
ginc(unbind_req_cnt);
SPLSTR(psw);
if (dl->dlstate != DL_IDLE) {
SPLX(psw);
return reply_error_ack(dl, mp, DL_UNBIND_REQ, DL_OUTSTATE, 0);
}
if (do_ok_ack(dl, &mp, DL_UNBIND_REQ) == RETRY) {
SPLX(psw);
return RETRY;
} else if (mp == NULL) {
SPLX(psw);
return DONE;
}
dl->dlstate = DL_UNBIND_PENDING;
SPLX(psw);
sap_destroy_all(dl);
if (putctl1(dl->rq->q_next, M_FLUSH, FLUSHRW) == 0)
printk("ldl: cannot flush stream on unbind\n");
SPLSTR(psw);
ASSERT(dl->dlstate == DL_UNBIND_PENDING);
dl->dlstate = DL_UNBOUND;
if ((dl->flags & LDLFLAG_HANGUP) != 0) {
SPLX(psw);
freemsg(mp);
hangup_do(dl);
return DONE;
}
SPLX(psw);
putnext(dl->rq, mp);
ginc(ok_ack_cnt);
return DONE;
}
STATIC INLINE int
ws_subs_bind(struct dl *dl, mblk_t *mp)
{
dl_subs_bind_req_t *reqp;
dl_subs_bind_ack_t *ackp;
int len;
pl_t psw;
ginc(subs_bind_req_cnt);
SPLSTR(psw);
if (dl->dlstate != DL_IDLE) {
SPLX(psw);
return reply_error_ack(dl, mp, DL_SUBS_BIND_REQ, DL_OUTSTATE, 0);
}
if (dl->framing == LDL_FRAME_802_3) {
SPLX(psw);
return reply_error_ack(dl, mp, DL_SUBS_BIND_REQ, DL_UNSUPPORTED, 0);
}
reqp = (dl_subs_bind_req_t *) mp->b_rptr;
if (reqp->dl_subs_bind_class == DL_HIERARCHICAL_BIND) {
unsigned char oui[3];
if (dl->framing != LDL_FRAME_SNAP) {
SPLX(psw);
return reply_error_ack(dl, mp, DL_SUBS_BIND_REQ, DL_UNSUPPORTED, 0);
}
if (reqp->dl_subs_sap_length != 3) {
SPLX(psw);
return reply_error_ack(dl, mp, DL_SUBS_BIND_REQ, DL_BADADDR, 0);
}
ASSERT(dl->sap_len == 2);
memcpy(oui, mp->b_rptr + reqp->dl_subs_sap_offset, 3);
len = DL_SUBS_BIND_ACK_SIZE + 3;
if (!reuse_msg(mp, len)) {
mblk_t *bp;
if ((bp = allocb(len, BPRI_HI)) == NULL) {
SPLX(psw);
return dl_bufcall(dl, mp, len);
}
freemsg(mp);
mp = bp;
}
memcpy(dl->oui, oui, 3);
SPLX(psw);
mp->b_datap->db_type = M_PCPROTO;
ackp = (dl_subs_bind_ack_t *) mp->b_wptr;
ackp->dl_primitive = DL_SUBS_BIND_ACK;
ackp->dl_subs_sap_length = 3;
ackp->dl_subs_sap_offset = DL_SUBS_BIND_ACK_SIZE;
mp->b_wptr += DL_SUBS_BIND_ACK_SIZE;
memcpy(mp->b_wptr, dl->oui, 3);
mp->b_wptr += 3;
putnext(dl->rq, mp);
return DONE;
} else if (reqp->dl_subs_bind_class == DL_PEER_BIND) {
sap_t dlsap;
dl_ushort saptype;
int saplen = reqp->dl_subs_sap_length;
unsigned char *sapptr = mp->b_rptr + reqp->dl_subs_sap_offset;
switch (dl->framing) {
case LDL_FRAME_EII:
if (saplen != 2) {
SPLX(psw);
return reply_error_ack(dl, mp, DL_SUBS_BIND_REQ, DL_BADADDR, 0);
}
*(unsigned short *) &dlsap.sap[0] = htons(*(unsigned short *) sapptr);
saptype = *(unsigned short *) sapptr;
break;
case LDL_FRAME_802_2:
case LDL_FRAME_RAW_LLC:
if (saplen != 1) {
SPLX(psw);
return reply_error_ack(dl, mp, DL_SUBS_BIND_REQ, DL_BADADDR, 0);
}
dlsap.sap[0] = *sapptr;
saptype = ETH_P_802_2;
break;
case LDL_FRAME_SNAP:
if (saplen != 2) {
SPLX(psw);
return reply_error_ack(dl, mp, DL_SUBS_BIND_REQ, DL_BADADDR, 0);
}
*(unsigned short *) &dlsap.sap[0] = htons(*(unsigned short *) sapptr);
saptype = ETH_P_802_2;
break;
case LDL_FRAME_802_3:
default:
ASSERT(0);
freemsg(mp);
return DONE;
}
len = DL_SUBS_BIND_ACK_SIZE + saplen;
if (!reuse_msg(mp, len)) {
mblk_t *bp;
if ((bp = allocb(len, BPRI_HI)) == NULL) {
SPLX(psw);
return dl_bufcall(dl, mp, len);
}
freemsg(mp);
mp = bp;
}
if (sap_create(dl, dlsap, saptype) < 0) {
SPLX(psw);
return reply_error_ack(dl, mp, DL_SUBS_BIND_REQ, DL_TOOMANY, 0);
}
SPLX(psw);
mp->b_datap->db_type = M_PCPROTO;
ackp = (dl_subs_bind_ack_t *) mp->b_wptr;
ackp->dl_primitive = DL_SUBS_BIND_ACK;
ackp->dl_subs_sap_length = saplen;
ackp->dl_subs_sap_offset = DL_SUBS_BIND_ACK_SIZE;
mp->b_wptr += DL_SUBS_BIND_ACK_SIZE;
if (dl->framing == LDL_FRAME_EII || dl->framing == LDL_FRAME_SNAP)
*(unsigned short *) mp->b_wptr = ntohs(*(unsigned short *) &dlsap.sap[0]);
else
memcpy(mp->b_wptr, dlsap.sap, saplen);
mp->b_wptr += saplen;
putnext(dl->rq, mp);
return DONE;
} else {
SPLX(psw);
return reply_error_ack(dl, mp, DL_SUBS_BIND_REQ, DL_UNSUPPORTED, 0);
}
}
STATIC INLINE int
ws_subs_unbind(struct dl *dl, mblk_t *mp)
{
dl_subs_unbind_req_t *reqp;
int saplen;
unsigned char *sapptr;
struct sap *sap;
sap_t dlsap;
pl_t psw;
ginc(subs_unbind_req_cnt);
if (dl->dlstate != DL_IDLE)
return reply_error_ack(dl, mp, DL_SUBS_UNBIND_REQ, DL_OUTSTATE, 0);
if (dl->subs != NULL)
return reply_error_ack(dl, mp, DL_SUBS_UNBIND_REQ, DL_BADADDR, 0);
reqp = (dl_subs_unbind_req_t *) mp->b_rptr;
saplen = reqp->dl_subs_sap_length;
sapptr = mp->b_rptr + reqp->dl_subs_sap_offset;
switch (dl->framing) {
case LDL_FRAME_EII:
case LDL_FRAME_SNAP:
ASSERT(dl->sap_len == 2);
if (saplen != 2)
return reply_error_ack(dl, mp, DL_SUBS_UNBIND_REQ, DL_BADADDR, 0);
*(unsigned short *) &dlsap.sap[0] = htons(*(unsigned short *) sapptr);
break;
case LDL_FRAME_802_2:
case LDL_FRAME_RAW_LLC:
ASSERT(dl->sap_len == 1);
if (saplen != 1)
return reply_error_ack(dl, mp, DL_SUBS_UNBIND_REQ, DL_BADADDR, 0);
dlsap.sap[0] = *sapptr;
break;
case LDL_FRAME_802_3:
default:
return reply_error_ack(dl, mp, DL_SUBS_UNBIND_REQ, DL_BADADDR, 0);
}
for (sap = dl->subs; sap != NULL && memcmp(dlsap.sap, sap->sap.sap, dl->sap_len);
sap = sap->next_sap) ;
if (sap == NULL)
return reply_error_ack(dl, mp, DL_SUBS_UNBIND_REQ, DL_BADADDR, 0);
ASSERT(!memcmp(dlsap.sap, sap->sap.sap, dl->sap_len));
ASSERT(sap->magic == SAP_MAGIC);
ASSERT(sap->dl == dl);
SPLSTR(psw);
if (dl->dlstate != DL_IDLE) {
SPLX(psw);
return reply_error_ack(dl, mp, DL_SUBS_UNBIND_REQ, DL_OUTSTATE, 0);
}
dl->dlstate = DL_SUBS_UNBIND_PND;
SPLX(psw);
if (do_ok_ack(dl, &mp, DL_SUBS_UNBIND_REQ) == RETRY)
return RETRY;
else if (mp == NULL)
return DONE;
if (sap_destroy(dl, sap) < 0) {
dl->dlstate = DL_IDLE;
return reply_error_ack(dl, mp, DL_SUBS_UNBIND_REQ, DL_BADADDR, 0);
}
SPLSTR(psw);
ASSERT(dl->dlstate == DL_SUBS_UNBIND_PND);
dl->dlstate = DL_IDLE;
if ((dl->flags & LDLFLAG_HANGUP) != 0) {
hangup_set(dl);
SPLX(psw);
freemsg(mp);
hangup_do(dl);
return DONE;
}
SPLX(psw);
putnext(dl->rq, mp);
ginc(ok_ack_cnt);
return DONE;
}
STATIC INLINE int
ws_udqos(struct dl *dl, mblk_t *mp)
{
dl_udqos_req_t *reqp;
dl_qos_cl_sel1_t *qos;
long priority;
ginc(udqos_req_cnt);
if (dl->dlstate != DL_IDLE)
return reply_error_ack(dl, mp, DL_UDQOS_REQ, DL_OUTSTATE, 0);
reqp = (dl_udqos_req_t *) mp->b_rptr;
if (reqp->dl_qos_length < sizeof(dl_qos_cl_sel1_t)
|| reqp->dl_qos_offset < DL_UDQOS_REQ_SIZE)
return reply_error_ack(dl, mp, DL_UDQOS_REQ, DL_BADQOSTYPE, 0);
qos = (dl_qos_cl_sel1_t *) (mp->b_rptr + reqp->dl_qos_offset);
if (qos->dl_qos_type != DL_QOS_CL_SEL1)
return reply_error_ack(dl, mp, DL_UDQOS_REQ, DL_BADQOSTYPE, 0);
if (qos->dl_trans_delay >= 0)
return reply_error_ack(dl, mp, DL_UDQOS_REQ, DL_BADQOSPARAM, 0);
if (qos->dl_protection >= 0 && qos->dl_protection != DL_NONE)
return reply_error_ack(dl, mp, DL_UDQOS_REQ, DL_BADQOSPARAM, 0);
if (qos->dl_residual_error >= 0)
return reply_error_ack(dl, mp, DL_UDQOS_REQ, DL_BADQOSPARAM, 0);
if (qos->dl_priority > 100)
return reply_error_ack(dl, mp, DL_UDQOS_REQ, DL_BADQOSPARAM, 0);
if (qos->dl_priority >= 0 && qos->dl_priority <= 100)
priority = qos->dl_priority;
else
priority = dl->priority;
if (do_ok_ack(dl, &mp, DL_UDQOS_REQ) == RETRY)
return RETRY;
else if (mp == NULL)
return DONE;
dl->priority = priority;
putnext(dl->rq, mp);
ginc(ok_ack_cnt);
return DONE;
}
STATIC INLINE int
ws_promiscon(struct dl *dl, mblk_t *mp)
{
dl_promiscon_req_t *reqp;
if (dl->dlstate != DL_UNBOUND && dl->dlstate != DL_IDLE)
return reply_error_ack(dl, mp, DL_PROMISCON_REQ, DL_OUTSTATE, 0);
if (dl->framing != LDL_FRAME_802_2 && dl->framing != LDL_FRAME_RAW_LLC)
return reply_error_ack(dl, mp, DL_PROMISCON_REQ, DL_NOTSUPPORTED, 0);
reqp = (dl_promiscon_req_t *) mp->b_rptr;
switch (reqp->dl_level) {
case DL_PROMISC_SAP:
if (do_ok_ack(dl, &mp, DL_PROMISCON_REQ) == RETRY)
return RETRY;
if (mp != NULL) {
dl->flags |= LDLFLAG_PROMISC_SAP;
putnext(dl->rq, mp);
ginc(ok_ack_cnt);
}
return DONE;
case DL_PROMISC_PHYS:
case DL_PROMISC_MULTI:
default:
return reply_error_ack(dl, mp, DL_PROMISCON_REQ, DL_NOTSUPPORTED, 0);
}
}
STATIC INLINE int
ws_promiscoff(struct dl *dl, mblk_t *mp)
{
dl_promiscoff_req_t *reqp;
if (dl->dlstate != DL_UNBOUND && dl->dlstate != DL_IDLE)
return reply_error_ack(dl, mp, DL_PROMISCOFF_REQ, DL_OUTSTATE, 0);
if (dl->framing != LDL_FRAME_802_2 && dl->framing != LDL_FRAME_RAW_LLC)
return reply_error_ack(dl, mp, DL_PROMISCOFF_REQ, DL_NOTSUPPORTED, 0);
reqp = (dl_promiscoff_req_t *) mp->b_rptr;
switch (reqp->dl_level) {
case DL_PROMISC_SAP:
if (do_ok_ack(dl, &mp, DL_PROMISCOFF_REQ) == RETRY)
return RETRY;
if (mp != NULL) {
dl->flags &= ~LDLFLAG_PROMISC_SAP;
putnext(dl->rq, mp);
ginc(ok_ack_cnt);
}
return DONE;
case DL_PROMISC_PHYS:
case DL_PROMISC_MULTI:
default:
return reply_error_ack(dl, mp, DL_PROMISCOFF_REQ, DL_NOTSUPPORTED, 0);
}
}
STATIC INLINE int
ws_enabmulti(struct dl *dl, mblk_t *mp)
{
dl_enabmulti_req_t *reqp;
if (dl->dlstate == DL_UNATTACHED)
return reply_error_ack(dl, mp, DL_ENABMULTI_REQ, DL_OUTSTATE, 0);
reqp = (dl_enabmulti_req_t *) mp->b_rptr;
switch (dev_mc_add
(dl->ndev->dev, mp->b_rptr + reqp->dl_addr_offset, reqp->dl_addr_length, 0)) {
case 0:
if (do_ok_ack(dl, &mp, DL_ENABMULTI_REQ) == RETRY)
return RETRY;
else if (mp == NULL)
return DONE;
break;
default:
return reply_error_ack(dl, mp, DL_ENABMULTI_REQ, DL_NOTSUPPORTED, 0);
}
putnext(dl->rq, mp);
ginc(ok_ack_cnt);
return DONE;
}
STATIC INLINE int
ws_disabmulti(struct dl *dl, mblk_t *mp)
{
dl_disabmulti_req_t *reqp;
if (dl->dlstate == DL_UNATTACHED)
return reply_error_ack(dl, mp, DL_DISABMULTI_REQ, DL_OUTSTATE, 0);
reqp = (dl_disabmulti_req_t *) mp->b_rptr;
return 0;
}
STATIC INLINE int
ws_error(struct dl *dl, mblk_t *mp, dl_ulong primitive, dl_ulong err)
{
if (reply_error_ack(dl, mp, primitive, err, 0) == RETRY)
return RETRY;
return DONE;
}
STATIC INLINE int
ioc_nak(struct dl *dl, mblk_t *mp)
{
mp->b_datap->db_type = M_IOCNAK;
qreply(WR(dl->rq), mp);
return DONE;
}
STATIC INLINE int
ioc_setflags(struct dl *dl, struct iocblk *iocp, mblk_t *mp)
{
caddr_t uaddr = NULL;
size_t usize;
pl_t psw;
ASSERT(iocp->ioc_cmd == LDL_SETFLAGS);
if (mp->b_cont == NULL || mp->b_cont->b_datap->db_type != M_DATA)
return ioc_nak(dl, mp);
#ifdef WITH_32BIT_CONVERSION
if (iocp->ioc_flag == IOC_ILP32) {
struct ldl_flags_ioctl32 *flg;
usize = sizeof(*flg);
if (iocp->ioc_count == TRANSPARENT) {
uaddr = (caddr_t) (unsigned long) (uint32_t)
*(unsigned long *) mp->b_cont->b_rptr;
} else {
if (iocp->ioc_count != usize)
return ioc_nak(dl, mp);
flg = (typeof(flg)) mp->b_cont->b_rptr;
flg->mask &= ~LDLFLAG_PRIVATE;
SPLSTR(psw);
dl->flags = (dl->flags & ~flg->mask) | (flg->flags & flg->mask);
SPLX(psw);
flg->flags = dl->flags;
}
} else
#endif
{
struct ldl_flags_ioctl *flg;
usize = sizeof(*flg);
if (iocp->ioc_count == TRANSPARENT) {
uaddr = (caddr_t)
*(unsigned long *) mp->b_cont->b_rptr;
} else {
if (iocp->ioc_count != usize)
return ioc_nak(dl, mp);
flg = (typeof(flg)) mp->b_cont->b_rptr;
flg->mask &= ~LDLFLAG_PRIVATE;
SPLSTR(psw);
dl->flags = (dl->flags & ~flg->mask) | (flg->flags & flg->mask);
SPLX(psw);
flg->flags = dl->flags;
}
}
if (iocp->ioc_count == TRANSPARENT) {
struct copyreq *cq = (typeof(cq)) iocp;
mp->b_datap->db_type = M_COPYOUT;
cq->cq_addr = uaddr;
cq->cq_size = usize;
} else {
mp->b_datap->db_type = M_IOCACK;
iocp->ioc_count = 0;
iocp->ioc_error = 0;
iocp->ioc_rval = 0;
}
qreply(WR(dl->rq), mp);
return DONE;
}
STATIC INLINE int
ioc_findppa(struct dl *dl, struct iocblk *iocp, mblk_t *mp)
{
struct ldldev *dev;
dl_ulong ppa;
mblk_t *dp;
ASSERT(iocp->ioc_cmd == LDL_FINDPPA);
if (mp->b_cont == NULL || mp->b_cont->b_datap->db_type != M_DATA)
return ioc_nak(dl, mp);
if (iocp->ioc_count == TRANSPARENT)
return ioc_nak(dl, mp);
if (iocp->ioc_count <= 0)
return ioc_nak(dl, mp);
dp = mp->b_cont;
for (dev = dev_base, ppa = 0; dev != NULL; dev = dev->next, ++ppa)
if (!strcmp(dev->name, dp->b_rptr))
break;
if (dev == NULL)
return ioc_nak(dl, mp);
dp->b_wptr = dp->b_rptr + sizeof(dl_ulong);
*(dl_ulong *) dp->b_rptr = ppa;
mp->b_datap->db_type = M_IOCACK;
iocp->ioc_count = sizeof(dl_ulong);
iocp->ioc_error = 0;
iocp->ioc_rval = 0;
qreply(WR(dl->rq), mp);
return DONE;
}
STATIC INLINE int
ioc_getname(struct dl *dl, struct iocblk *iocp, mblk_t *mp)
{
mblk_t *dp;
caddr_t uaddr = NULL;
size_t usize;
ASSERT(iocp->ioc_cmd == LDL_GETNAME);
if (dl->dlstate == DL_UNATTACHED)
return ioc_nak(dl, mp);
ASSERT(dl->ndev != NULL);
ASSERT(dl->ndev->dev != NULL);
usize = strlen(dl->ndev->dev->name) + 1;
if ((dp = allocb(usize, BPRI_HI)) == NULL)
return RETRY;
memcpy(dp->b_rptr, dl->ndev->dev->name, usize);
dp->b_wptr += usize;
#ifdef WITH_32BIT_CONVERSION
if (iocp->ioc_flag == IOC_ILP32) {
if (iocp->ioc_count == TRANSPARENT) {
uaddr = (caddr_t) (unsigned long) (uint32_t)
*(unsigned long *) mp->b_cont->b_rptr;
}
} else
#endif
{
if (iocp->ioc_count == TRANSPARENT) {
uaddr = (caddr_t)
*(unsigned long *) mp->b_cont->b_rptr;
}
}
if (mp->b_cont != NULL)
freemsg(unlinkb(mp));
linkb(mp, dp);
if (iocp->ioc_count == TRANSPARENT) {
struct copyreq *cq = (typeof(cq)) iocp;
mp->b_datap->db_type = M_COPYOUT;
cq->cq_addr = uaddr;
cq->cq_size = usize;
} else {
mp->b_datap->db_type = M_IOCACK;
iocp->ioc_count = usize;
iocp->ioc_error = 0;
iocp->ioc_rval = 0;
}
qreply(WR(dl->rq), mp);
return DONE;
}
STATIC INLINE int
ioc_getgstats(struct dl *dl, struct iocblk *iocp, mblk_t *mp)
{
mblk_t *dp;
caddr_t uaddr = NULL;
size_t usize;
ASSERT(iocp->ioc_cmd == LDL_GETGSTATS);
#ifdef WITH_32BIT_CONVERSION
if (iocp->ioc_flag == IOC_ILP32) {
struct ldl_gstats_ioctl32 *out;
if ((dp = allocb(sizeof(struct ldl_gstats_ioctl32), BPRI_HI)) == NULL)
return RETRY;
out = (typeof(out)) dp->b_rptr;
usize = sizeof(*out);
dp->b_wptr += usize;
out->attach_req_cnt = (int32_t) atomic_read(&ldl_gstats.attach_req_cnt);
out->detach_req_cnt = (int32_t) atomic_read(&ldl_gstats.detach_req_cnt);
out->bind_req_cnt = (int32_t) atomic_read(&ldl_gstats.bind_req_cnt);
out->unbind_req_cnt = (int32_t) atomic_read(&ldl_gstats.unbind_req_cnt);
out->subs_bind_req_cnt = (int32_t) atomic_read(&ldl_gstats.subs_bind_req_cnt);
out->subs_unbind_req_cnt = (int32_t) atomic_read(&ldl_gstats.subs_unbind_req_cnt);
out->udqos_req_cnt = (int32_t) atomic_read(&ldl_gstats.udqos_req_cnt);
out->ok_ack_cnt = (int32_t) atomic_read(&ldl_gstats.ok_ack_cnt);
out->error_ack_cnt = (int32_t) atomic_read(&ldl_gstats.error_ack_cnt);
out->unitdata_req_cnt = (int32_t) atomic_read(&ldl_gstats.unitdata_req_cnt);
out->unitdata_req_q_cnt = (int32_t) atomic_read(&ldl_gstats.unitdata_req_q_cnt);
out->unitdata_ind_cnt = (int32_t) atomic_read(&ldl_gstats.unitdata_ind_cnt);
out->unitdata_q_cnt = (int32_t) atomic_read(&ldl_gstats.unitdata_q_cnt);
out->unitdata_drp_cnt = (int32_t) atomic_read(&ldl_gstats.unitdata_drp_cnt);
out->uderror_ind_cnt = (int32_t) atomic_read(&ldl_gstats.uderror_ind_cnt);
out->ioctl_cnt = (int32_t) atomic_read(&ldl_gstats.ioctl_cnt);
out->net_rx_cnt = (int32_t) atomic_read(&ldl_gstats.net_rx_cnt);
out->net_rx_drp_cnt = (int32_t) atomic_read(&ldl_gstats.net_rx_drp_cnt);
out->net_tx_cnt = (int32_t) atomic_read(&ldl_gstats.net_tx_cnt);
out->net_tx_fail_cnt = (int32_t) atomic_read(&ldl_gstats.net_tx_fail_cnt);
if (iocp->ioc_count == TRANSPARENT) {
uaddr = (caddr_t) (unsigned long) (uint32_t)
*(unsigned long *) mp->b_cont->b_rptr;
}
} else
#endif
{
struct ldl_gstats_ioctl *out;
if ((dp = allocb(sizeof(struct ldl_gstats_ioctl), BPRI_HI)) == NULL)
return RETRY;
out = (typeof(out)) dp->b_rptr;
usize = sizeof(*out);
dp->b_wptr += usize;
out->attach_req_cnt = (long) atomic_read(&ldl_gstats.attach_req_cnt);
out->detach_req_cnt = (long) atomic_read(&ldl_gstats.detach_req_cnt);
out->bind_req_cnt = (long) atomic_read(&ldl_gstats.bind_req_cnt);
out->unbind_req_cnt = (long) atomic_read(&ldl_gstats.unbind_req_cnt);
out->subs_bind_req_cnt = (long) atomic_read(&ldl_gstats.subs_bind_req_cnt);
out->subs_unbind_req_cnt = (long) atomic_read(&ldl_gstats.subs_unbind_req_cnt);
out->udqos_req_cnt = (long) atomic_read(&ldl_gstats.udqos_req_cnt);
out->ok_ack_cnt = (long) atomic_read(&ldl_gstats.ok_ack_cnt);
out->error_ack_cnt = (long) atomic_read(&ldl_gstats.error_ack_cnt);
out->unitdata_req_cnt = (long) atomic_read(&ldl_gstats.unitdata_req_cnt);
out->unitdata_req_q_cnt = (long) atomic_read(&ldl_gstats.unitdata_req_q_cnt);
out->unitdata_ind_cnt = (long) atomic_read(&ldl_gstats.unitdata_ind_cnt);
out->unitdata_q_cnt = (long) atomic_read(&ldl_gstats.unitdata_q_cnt);
out->unitdata_drp_cnt = (long) atomic_read(&ldl_gstats.unitdata_drp_cnt);
out->uderror_ind_cnt = (long) atomic_read(&ldl_gstats.uderror_ind_cnt);
out->ioctl_cnt = (long) atomic_read(&ldl_gstats.ioctl_cnt);
out->net_rx_cnt = (long) atomic_read(&ldl_gstats.net_rx_cnt);
out->net_rx_drp_cnt = (long) atomic_read(&ldl_gstats.net_rx_drp_cnt);
out->net_tx_cnt = (long) atomic_read(&ldl_gstats.net_tx_cnt);
out->net_tx_fail_cnt = (long) atomic_read(&ldl_gstats.net_tx_fail_cnt);
if (iocp->ioc_count == TRANSPARENT) {
uaddr = (caddr_t)
*(unsigned long *) mp->b_cont->b_rptr;
}
}
if (mp->b_cont != NULL)
freemsg(unlinkb(mp));
linkb(mp, dp);
if (iocp->ioc_count != TRANSPARENT) {
mp->b_datap->db_type = M_IOCACK;
iocp->ioc_count = usize;
iocp->ioc_error = 0;
iocp->ioc_rval = 0;
} else {
struct copyreq *cq = (typeof(cq)) iocp;
mp->b_datap->db_type = M_COPYOUT;
cq->cq_addr = uaddr;
cq->cq_size = usize;
}
qreply(WR(dl->rq), mp);
return DONE;
}
STATIC INLINE int
ioc_set_debug_mask(struct dl *dl, struct iocblk *iocp, mblk_t *mp)
{
ASSERT(iocp->ioc_cmd == LDL_SETDEBUG);
if (mp->b_cont == NULL || mp->b_cont->b_datap->db_type != M_DATA)
return ioc_nak(dl, mp);
#ifdef WITH_32BIT_CONVERSION
if (iocp->ioc_flag == IOC_ILP32) {
if (iocp->ioc_count == TRANSPARENT) {
ldl_debug_mask = *(unsigned long *) mp->b_cont->b_rptr;
} else {
if (iocp->ioc_count != sizeof(uint32_t))
return ioc_nak(dl, mp);
ldl_debug_mask = *(uint32_t *) mp->b_cont->b_rptr;
}
} else
#endif
{
if (iocp->ioc_count == TRANSPARENT) {
ldl_debug_mask = *(unsigned long *) mp->b_cont->b_rptr;
} else {
if (iocp->ioc_count != sizeof(unsigned long))
return ioc_nak(dl, mp);
ldl_debug_mask = *(unsigned long *) mp->b_cont->b_rptr;
}
}
mp->b_datap->db_type = M_IOCACK;
iocp->ioc_count = 0;
iocp->ioc_error = 0;
iocp->ioc_rval = 0;
qreply(WR(dl->rq), mp);
return DONE;
}
STATIC int streamscall
ldl_open(queue_t *q, dev_t *devp, int flag, int sflag, cred_t *crp)
{
dev_t i;
spin_lock(&first_open_lock);
if (sflag == CLONEOPEN) {
for (i = 1; i < LDL_N_MINOR; i++)
if (dl_dl[i].rq == NULL)
break;
if (i == LDL_N_MINOR) {
spin_unlock(&first_open_lock);
return ENXIO;
}
} else {
if ((i = getminor(*devp)) >= LDL_N_MINOR) {
spin_unlock(&first_open_lock);
return EBUSY;
}
}
*devp = makedevice(getmajor(*devp), i);
if (q->q_ptr != NULL) {
spin_unlock(&first_open_lock);
return 0;
}
ASSERT(dl_dl[i].magic == 0);
dl_dl[i].magic = DL_MAGIC;
dl_dl[i].rq = q;
dl_dl[i].lost_rcv = dl_dl[i].lost_send = 0;
dl_dl[i].bufwait = 0;
dl_dl[i].dlstate = DL_UNATTACHED;
dl_dl[i].priority = 50;
dl_dl[i].subs = NULL;
dl_dl[i].flags = LDLFLAG_DEFAULT;
q->q_ptr = WR(q)->q_ptr = &dl_dl[i];
dl_dl[i].next_open = first_open;
first_open = &dl_dl[i];
if (dl_dl[i].next_open == NULL) {
spin_unlock(&first_open_lock);
if (notifier_register() < 0)
printk("ldl: ldl_open: Unable to add notifier\n");
} else
spin_unlock(&first_open_lock);
qprocson(q);
return 0;
}
STATIC int streamscall
ldl_close(queue_t *q, int flag, cred_t *crp)
{
struct dl **dlp, *dl = (struct dl *) q->q_ptr;
pl_t psw;
ASSERT(dl != NULL);
ASSERT(dl->magic == DL_MAGIC);
ASSERT(dl->rq == q);
SPLSTR(psw);
if (dl->bufwait) {
unbufcall(dl->bufwait);
dl->bufwait = 0;
}
if (dl->dlstate == DL_IDLE) {
sap_destroy_all(dl);
dl->dlstate = DL_UNBOUND;
}
if (dl->dlstate != DL_UNATTACHED) {
ndev_release(dl);
dl->addr_len = 0;
dl->ndev = NULL;
dl->dlstate = DL_UNATTACHED;
}
SPLX(psw);
spin_lock(&first_open_lock);
dl->rq = q->q_ptr = WR(q)->q_ptr = NULL;
dl->magic = 0;
for (dlp = &first_open; *dlp; dlp = &(*dlp)->next_open)
if (*dlp == dl)
break;
if (*dlp != NULL)
*dlp = dl->next_open;
else
printk("ldl: ldl_close: Endpoint not on open list\n");
if (first_open == NULL) {
spin_unlock(&first_open_lock);
if (notifier_unregister() < 0)
printk("ldl: ldl_close: Unable to remove notifier\n");
} else
spin_unlock(&first_open_lock);
qprocsoff(q);
return 0;
}
STATIC int
write_service_raw(struct dl *dl, mblk_t *mp)
{
ASSERT(dl != NULL);
ASSERT(dl->magic == DL_MAGIC);
ASSERT(!dl->bufwait);
ASSERT(mp != NULL);
ASSERT(mp->b_datap != NULL);
ASSERT(mp->b_rptr != NULL);
ASSERT(mp->b_datap->db_type == M_DATA);
if ((dl->flags & LDLFLAG_RAW) != 0) {
if (ldl_debug_mask & LDL_DEBUG_UDREQ)
ldl_mp_data_dump("ldl_unitdata_req", mp,
ldl_debug_mask & LDL_DEBUG_ALLDATA);
ginc(unitdata_req_cnt);
return tx_func_raw(dl, mp);
}
printk("dl: Unexpected raw M_DATA message.\n");
freemsg(mp);
return DONE;
}
STATIC int
write_service(struct dl *dl, mblk_t *mp)
{
dl_ulong primitive;
ASSERT(dl != NULL);
ASSERT(dl->magic == DL_MAGIC);
ASSERT(!dl->bufwait);
ASSERT(mp != NULL);
ASSERT(mp->b_datap != NULL);
ASSERT(mp->b_rptr != NULL);
ASSERT(mp->b_datap->db_type == M_PROTO || mp->b_datap->db_type == M_PCPROTO);
primitive = ((union DL_primitives *) mp->b_rptr)->dl_primitive;
if (primitive == DL_UNITDATA_REQ)
return tx_func_proto(dl, mp);
switch (primitive) {
case DL_INFO_REQ:
return ws_info(dl, mp);
case DL_PHYS_ADDR_REQ:
return ws_phys_addr(dl, mp);
case DL_ATTACH_REQ:
return ws_attach(dl, mp);
case DL_DETACH_REQ:
return ws_detach(dl, mp);
case DL_BIND_REQ:
return ws_bind(dl, mp);
case DL_UNBIND_REQ:
return ws_unbind(dl, mp);
case DL_SUBS_BIND_REQ:
return ws_subs_bind(dl, mp);
case DL_SUBS_UNBIND_REQ:
return ws_subs_unbind(dl, mp);
case DL_UDQOS_REQ:
return ws_udqos(dl, mp);
case DL_PROMISCON_REQ:
return ws_promiscon(dl, mp);
case DL_PROMISCOFF_REQ:
return ws_promiscoff(dl, mp);
case DL_ENABMULTI_REQ:
return ws_enabmulti(dl, mp);
case DL_DISABMULTI_REQ:
return ws_disabmulti(dl, mp);
case DL_CONNECT_REQ:
case DL_CONNECT_RES:
case DL_TOKEN_REQ:
case DL_DISCONNECT_REQ:
case DL_RESET_REQ:
case DL_RESET_RES:
case DL_DATA_ACK_REQ:
case DL_REPLY_REQ:
case DL_REPLY_UPDATE_REQ:
case DL_XID_REQ:
case DL_TEST_REQ:
case DL_SET_PHYS_ADDR_REQ:
case DL_GET_STATISTICS_REQ:
return ws_error(dl, mp, primitive, DL_NOTSUPPORTED);
default:
return ws_error(dl, mp, primitive, DL_BADPRIM);
}
}
STATIC int
do_ioctl(struct dl *dl, mblk_t *mp)
{
struct iocblk *iocp;
ginc(ioctl_cnt);
ASSERT(dl != NULL);
ASSERT(dl->magic == DL_MAGIC);
ASSERT(mp != NULL);
ASSERT(mp->b_datap->db_type == M_IOCTL);
iocp = (struct iocblk *) mp->b_rptr;
switch (iocp->ioc_cmd) {
case LDL_SETFLAGS:
return ioc_setflags(dl, iocp, mp);
case LDL_FINDPPA:
return ioc_findppa(dl, iocp, mp);
case LDL_GETNAME:
return ioc_getname(dl, iocp, mp);
case LDL_GETGSTATS:
return ioc_getgstats(dl, iocp, mp);
case LDL_SETDEBUG:
return ioc_set_debug_mask(dl, iocp, mp);
default:
return ioc_nak(dl, mp);
}
}
STATIC int
do_iocdata(struct dl *dl, mblk_t *mp)
{
struct iocblk *iocp = (struct iocblk *) mp->b_rptr;
struct copyresp *cp = (struct copyresp *) mp->b_rptr;
if (cp->cp_rval != (caddr_t) 0) {
freemsg(mp);
return DONE;
}
switch (iocp->ioc_cmd) {
case LDL_SETFLAGS:
#ifdef WITH_32BIT_CONVERSION
if (cp->cp_flag == IOC_ILP32) {
struct ldl_flags_ioctl32 *flg;
psw_t psw;
flg = (typeof(flg)) mp->b_cont->b_rptr;
flg->mask &= ~LDLFLAG_PRIVATE;
SPLSTR(psw);
dl->flags = (dl->flags & ~flg->mask) | (flg->flags & flg->mask);
SPLX(psw);
flg->flags = dl->flags;
} else
#endif
{
struct ldl_flags_ioctl *flg;
psw_t psw;
flg = (typeof(flg)) mp->b_cont->b_rptr;
flg->mask &= ~LDLFLAG_PRIVATE;
SPLSTR(psw);
dl->flags = (dl->flags & ~flg->mask) | (flg->flags & flg->mask);
SPLX(psw);
flg->flags = dl->flags;
}
break;
case LDL_FINDPPA:
return ioc_nak(dl, mp);
case LDL_GETNAME:
break;
case LDL_GETGSTATS:
break;
case LDL_SETDEBUG:
return ioc_nak(dl, mp);
default:
return ioc_nak(dl, mp);
}
mp->b_datap->db_type = M_IOCACK;
iocp->ioc_count = 0;
iocp->ioc_error = 0;
iocp->ioc_rval = 0;
qreply(WR(dl->rq), mp);
return DONE;
}
STATIC streamscall int
ldl_wput(queue_t *q, mblk_t *mp)
{
unsigned char msg_type = mp->b_datap->db_type;
dl_ulong primitive;
if (msg_type == M_PROTO) {
primitive = ((union DL_primitives *) mp->b_rptr)->dl_primitive;
if (primitive == DL_UNITDATA_REQ && q->q_count != 0) {
if (!putq(q, mp))
freemsg(mp);
ginc(unitdata_req_q_cnt);
return (0);
}
if (write_service((struct dl *) q->q_ptr, mp) == DONE)
return (0);
} else {
switch (msg_type) {
case M_PCPROTO:
if (write_service((struct dl *) q->q_ptr, mp) == DONE)
return (0);
break;
case M_DATA:
if (q->q_count != 0) {
if (!putq(q, mp))
freemsg(mp);
return (0);
}
if (write_service_raw((struct dl *) q->q_ptr, mp) == DONE)
return (0);
break;
case M_FLUSH:
if (mp->b_rptr[0] & FLUSHW) {
if (mp->b_rptr[0] & FLUSHBAND)
flushband(q, mp->b_rptr[1], FLUSHDATA);
else
flushq(q, FLUSHDATA);
mp->b_rptr[0] &= ~FLUSHW;
}
if (mp->b_rptr[0] & FLUSHR) {
if (mp->b_rptr[0] & FLUSHBAND)
flushband(RD(q), mp->b_rptr[1], FLUSHDATA);
else
flushq(RD(q), FLUSHDATA);
qreply(q, mp);
} else
freemsg(mp);
return (0);
case M_IOCTL:
if (do_ioctl((struct dl *) q->q_ptr, mp) == DONE)
return (0);
break;
case M_IOCDATA:
if (do_iocdata((struct dl *) q->q_ptr, mp) == DONE)
return (0);
break;
default:
freemsg(mp);
return (0);
}
}
if (!putq(q, mp))
freemsg(mp);
return (0);
}
STATIC streamscall int
ldl_wsrv(queue_t *q)
{
mblk_t *mp;
struct dl *dl = (struct dl *) q->q_ptr;
ASSERT(q != NULL);
ASSERT(dl != NULL);
ASSERT(dl->magic == DL_MAGIC);
ASSERT(!dl->bufwait);
while ((mp = getq(q)) != NULL) {
if (mp->b_datap->db_type != M_DATA) {
ASSERT(mp->b_datap->db_type == M_PROTO
|| mp->b_datap->db_type == M_PCPROTO);
if (write_service(dl, mp) == RETRY) {
if (!putbq(q, mp))
freemsg(mp);
return (0);
}
} else if (write_service_raw(dl, mp) == RETRY) {
if (!putbq(q, mp))
freemsg(mp);
return (0);
}
}
return (0);
}
STATIC streamscall int
ldl_rsrv(queue_t *q)
{
mblk_t *mp;
while (canputnext(q)) {
if ((mp = getq(q)) == NULL)
break;
if (ldl_debug_mask & LDL_DEBUG_UDIND)
ldl_mp_data_dump("ldl_unitdata_ind", mp,
ldl_debug_mask & LDL_DEBUG_ALLDATA);
putnext(q, mp);
ginc(unitdata_ind_cnt);
}
return (0);
}
#ifdef LINUX
unsigned short modid = DRV_ID;
#ifndef module_param
MODULE_PARM(modid, "h");
#else
module_param(modid, ushort, 0444);
#endif
MODULE_PARM_DESC(modid, "Module ID number for LDL driver (0 for allocation).");
major_t major = CMAJOR_0;
#ifndef module_param
MODULE_PARM(major, "h");
#else
module_param(major, uint, 0444);
#endif
MODULE_PARM_DESC(major, "Major device number for LDL driver (0 for allocation).");
#endif
#ifdef LFS
STATIC struct cdevsw ldl_cdev = {
.d_name = DRV_NAME,
.d_str = &ldl_info,
.d_flag = D_MTPERQ,
.d_fop = NULL,
.d_mode = S_IFCHR,
.d_kmod = THIS_MODULE,
};
struct ldl_ioctl {
unsigned int cmd;
void *handle;
};
static struct ldl_ioctl ldl_map[] = {
{.cmd = LDL_GETLSTATS,}
, {.cmd = LDL_GETGSTATS,}
, {.cmd = LDL_SETDEBUG,}
, {.cmd = LDL_SETFLAGS,}
, {.cmd = LDL_FINDPPA,}
, {.cmd = LDL_GETNAME,}
, {.cmd = 0,}
};
static void
ldl_unregister_ioctl32(void)
{
struct ldl_ioctl *i;
for (i = ldl_map; i->cmd != 0; i++)
if (i->handle != NULL)
unregister_ioctl32(i->handle);
}
static int
ldl_register_ioctl32(void)
{
struct ldl_ioctl *i;
for (i = ldl_map; i->cmd != 0; i++) {
if ((i->handle = register_ioctl32(i->cmd)) == NULL) {
ldl_unregister_ioctl32();
return (-ENOMEM);
}
}
return (0);
}
int __init
ldl_init(void)
{
int err;
cmn_err(CE_NOTE, DRV_BANNER);
if ((err = ldl_register_ioctl32()) < 0)
return (err);
if ((err = register_strdev(&ldl_cdev, major)) < 0) {
cmn_err(CE_WARN, "%s: Cannot register major %d, err = %d\n", DRV_NAME, (int)major, -err);
ldl_unregister_ioctl32();
return (err);
}
if (err > 0)
major = err;
return (0);
}
void __exit
ldl_exit(void)
{
unregister_strdev(&ldl_cdev, major);
ldl_unregister_ioctl32();
}
#ifdef CONFIG_STREAMS_LDL_MODULE
module_init(ldl_init);
module_exit(ldl_exit);
#endif
#else
#ifdef LIS
STATIC int ldl_initialized = 0;
STATIC void
ldl_init(void)
{
int err;
if (ldl_initialized != 0)
return;
cmn_err(CE_NOTE, DRV_BANNER);
if ((err = lis_register_strdev(major, &ldl_info, UNITS, DRV_NAME)) < 0) {
cmn_err(CE_WARN, "%s: Cannot register major %d\n", DRV_NAME, major);
ldl_initialized = err;
return;
}
ldl_initialized = 1;
if (major == 0 && err > 0) {
major = err;
ldl_initialized = 2;
}
return;
}
STATIC void
ldl_terminate(void)
{
int err;
if (ldl_initialized <= 0)
return;
if (major) {
if ((err = lis_unregister_strdev(major)) < 0)
cmn_err(CE_PANIC, "%s: Cannot unregister major %d\n", DRV_NAME, major);
major = 0;
}
ldl_initialized = 0;
return;
}
int
init_module(void)
{
(void) major;
ldl_init();
if (ldl_initialized < 0)
return ldl_initialized;
return (0);
}
void
cleanup_module(void)
{
return ldl_terminate();
}
#endif
#endif
| ||||||||||||||||
| OpenSS7 SS7 for the Common Man |
| ||||||||||||||||
| Last modified: Sat, 16 Aug 2008 01:55:14 GMT © Copyright 1997-2007 OpenSS7 Corporation All Rights Reserved. |