OpenSS7 SS7 for the Common Man |
© Copyright 1997-2004,OpenSS7 Corporation, All Rights Reserved. |
||||||||||||||||||||||||||
Home | Overview | Status | News | Documentation | Resources | About | |||||||||||||||||||||
File /code/strss7/drivers/dl/dl_lapd.c#ident "@(#) $RCSfile: dl_lapd.c,v $ $Name: $($Revision: 0.8.2.5 $) $Date: 2003/06/24 19:23:23 $" static char const ident[] = "$RCSfile: dl_lapd.c,v $ $Name: $($Revision: 0.8.2.5 $) $Date: 2003/06/24 19:23:23 $"; #include <linux/config.h> #include <linux/version.h> #ifdef MODVERSIONS #include <linux/modversions.h> #endif #include <linux/module.h> #include <sys/stream.h> #include <sys/stropts.h> #include <sys/cmn_err.h> #include <sys/dki.h> #include <sys/ddi.h> #include <ss7/lmi.h> #include <ss7/lmi_ioctl.h> #include <sys/cdi.h> #include <sys/cdi_hdlc.h> #include <sys/dlpi.h> #include <sys/dlpi_lapd.h> #include <ss7/lapd_ioctl.h> #include <ss7/hdlc_ioctl.h> #include "../debug.h" #include "../bufq.h" #include "../priv.h" #include "../lock.h" #include "../queue.h" #include "../allocb.h" #include "../timer.h" #define LAPD_DESCRIP "LAPD Data Link (DL-LAPD) STREAMS MULTIPLEXING DRIVER ($Revision: 0.8.2.5 $)" #define LAPD_COPYRIGHT "Copyright (c) 1997-2003 OpenSS7 Corporation. All Rights Reserved." #define LAPD_DEVICE "OpenSS7 CDI Devices." #define LAPD_CONTACT "Brian Bidulock <bidulock@openss7.org>" #define LAPD_LICENSE "GPL" #define LAPD_BANNER LAPD_DESCRIP "\n" \ LAPD_COPYRIGHT "\n" \ LAPD_DEVICE "\n" \ LAPD_CONTACT MODULE_AUTHOR(LAPD_CONTACT); MODULE_DESCRIPTION(LAPD_DESCRIP); MODULE_SUPPORTED_DEVICE(LAPD_DEVICE); #ifdef MODULE_LICENSE MODULE_LICENSE(LAPD_LICENSE); #endif #ifndef LAPD_CMAJOR #define LAPD_CMAJOR 0 #endif #ifndef LAPD_NMAJOR #define LAPD_NMAJOR 4 #endif #define LAPD_NMINOR 255 #define MODULE_STATIC STATIC /* * ========================================================================= * * STREAMS Definitions * * ========================================================================= */ #define LAPD_DRV_ID LAPD_IOC_MAGIC #define LAPD_DRV_NAME "dl-lapd" STATIC struct module_info dl_winfo = { mi_idnum:LAPD_DRV_ID, /* Module ID number */ mi_idname:LAPD_DRV_NAME, /* Module ID name */ mi_minpsz:(1), /* Min packet size accepted */ mi_maxpsz:INFPSZ, /* Max packet size accepted */ mi_hiwat:(1 << 15), /* Hi water mark */ mi_lowat:(1 << 10), /* Lo water mark */ }; STATIC struct module_info dl_rinfo = { mi_idnum:LAPD_DRV_ID, /* Module ID number */ mi_idname:LAPD_DRV_NAME, /* Module ID name */ mi_minpsz:(1), /* Min packet size accepted */ mi_maxpsz:INFPSZ, /* Max packet size accepted */ mi_hiwat:(1 << 15), /* Hi water mark */ mi_lowat:(1 << 10), /* Lo water mark */ }; STATIC struct module_info cd_winfo = { mi_idnum:LAPD_DRV_ID, /* Module ID number */ mi_idname:LAPD_DRV_NAME, /* Module ID name */ mi_minpsz:(1), /* Min packet size accepted */ mi_maxpsz:INFPSZ, /* Max packet size accepted */ mi_hiwat:(1 << 15), /* Hi water mark */ mi_lowat:(1 << 10), /* Lo water mark */ }; STATIC struct module_info cd_rinfo = { mi_idnum:LAPD_DRV_ID, /* Module ID number */ mi_idname:LAPD_DRV_NAME, /* Module ID name */ mi_minpsz:(1), /* Min packet size accepted */ mi_maxpsz:INFPSZ, /* Max packet size accepted */ mi_hiwat:(1 << 15), /* Hi water mark */ mi_lowat:(1 << 10), /* Lo water mark */ }; STATIC int dl_open(queue_t *, dev_t *, int, int, cred_t *); STATIC int dl_close(queue_t *, int, cred_t *); STATIC struct qinit dl_rinit = { qi_putp:ss7_oput, /* Read put (message from below) */ qi_srvp:ss7_osrv, /* Read queue service */ qi_qopen:dl_open, /* Each open */ qi_qclose:dl_close, /* Last close */ qi_minfo:&dl_rinfo, /* Information */ }; STATIC struct qinit dl_winit = { qi_putp:ss7_iput, /* Write put (message from above) */ qi_srvp:ss7_isrv, /* Write queue service */ qi_minfo:&dl_winfo, /* Information */ }; STATIC struct qinit cd_rinit = { qi_putp:ss7_iput, /* Read put (message from below) */ qi_srvp:ss7_isrv, /* Read queue service */ qi_minfo:&cd_rinfo, /* Information */ }; STATIC struct qinit cd_winit = { qi_putp:ss7_oput, /* Write put (message from above) */ qi_srvp:ss7_osrv, /* Write queue service */ qi_minfo:&cd_winfo, /* Information */ }; MODULE_STATIC struct streamtab lapd_info = { st_rdinit:&dl_rinit, /* Upper read queue */ st_wrinit:&dl_winit, /* Upper write queue */ st_muxrinit:&cd_rinit, /* Lower read queue */ st_muxwinit:&cd_winit, /* Lower write queue */ }; STATIC int dl_r_prim(queue_t *, mblk_t *); STATIC int dl_w_prim(queue_t *, mblk_t *); STATIC int cd_r_prim(queue_t *, mblk_t *); STATIC int cd_w_prim(queue_t *, mblk_t *); /* * ========================================================================= * * Private structures * * ========================================================================= */ /* forward declarations */ struct df; /* Default structure */ struct dl; /* DL User */ struct cd; /* CD provider */ /* default */ struct df { lis_spin_lock_t lock; /* master list lock */ SLIST_HEAD (dl, dl); /* list of DL structures */ SLIST_HEAD (cd, cd); /* list of CD structures */ struct lapd_proto proto; /* default protocol variant and options */ struct lapd_timers_df timers; /* default timers */ struct lapd_option_df config; /* default configuration */ struct lapd_statem_df statem; /* default state */ struct lapd_stats_df statsp; /* default statistics periods */ struct lapd_stats_df stamp; /* default statistics timestamps */ struct lapd_stats_df stats; /* default statistics */ struct lapd_notify_df notify; /* default notifications */ }; STATIC struct df master; STATIC INLINE struct df *df_lookup(ulong id) { if (id) return (NULL); return (&master); } /* * DL - DL User * ----------------------------------- */ struct dl { STR_DECLARATION (struct dl); /* stream declaration */ SLIST_LINKAGE (cd, dl, bind); /* CD provider bind hash linkage */ SLIST_LINKAGE (cd, dl, conn); /* CD provider conn hash linkage */ SLIST_LINKAGE (cd, dl, list); /* CD provider list hash linkage */ SLIST_LINKAGE (cd, dl, mgmt); /* CD provider mgmt hash linkage */ SLIST_LINKAGE (cd, dl, wait); /* CD provider wait list linkage */ SLIST_LINKAGE (cd, dl, teim); /* CD provider teim list linkage */ SLIST_LINKAGE (cd, dl, cd); /* CD provider attached list linkage */ struct lapd_addr dlc; /* DL data link connection address */ bufq_t conq; /* connection queue */ bufq_t sndq; /* data for transmission */ bufq_t rtxq; /* data for retransmission */ bufq_t rcvq; /* data that was received */ bufq_t ackq; /* data awaiting acknowledgement */ ulong conind; /* max number of connection indications */ ulong va; /* next I frame acknowledged */ ulong vs; /* next I frame sent */ ulong vr; /* next I frame expected */ ulong rc; /* retransmission count */ ulong ref; /* reference for TEI management */ dl_info_ack_t info; /* DL information */ struct lapd_proto proto; /* DL protocol variant and options */ struct lapd_timers_dl timers; /* DL timers */ struct lapd_option_dl config; /* DL configuration */ struct lapd_statem_dl statem; /* DL state */ struct lapd_stats_dl statsp; /* DL statistics periods */ struct lapd_stats_dl stamp; /* DL statistics timestamps */ struct lapd_stats_dl stats; /* DL statistics */ struct lapd_notify_dl notify; /* DL notifications */ }; #define DL_PRIV(__q) ((struct dl *)(__q)->q_ptr) #define DLF_AUTO_XID 0x00000001 #define DLF_AUTO_TEST 0x00000002 #define DLF_TEI_ASSIGNED 0x00000004 #define LAPD_TEI_UNASSIGNED 0 #define LAPD_WAIT_TEI_ASSIGN 1 #define LAPD_WAIT_TEI_ESTABLISH 2 #define LAPD_TEI_ASSIGNED 3 #define LAPD_WAIT_ESTABLISH 4 #define LAPD_WAIT_REESTABLISH 5 #define LAPD_PEND_RELEASE 6 #define LAPD_WAIT_RELEASE 7 #define LAPD_ESTABLISHED 8 #define LAPD_TIME_RECOVERY 9 STATIC INLINE struct dl *dl_lookup(ulong); STATIC INLINE struct dl *dl_get(struct dl *); STATIC INLINE void dl_put(struct dl *); STATIC INLINE ulong dl_get_id(ulong); STATIC struct dl *dl_alloc_priv(queue_t *, struct dl **, dev_t *, cred_t *, int); STATIC void dl_free_priv(struct dl *); STATIC INLINE ulong dl_get_state(struct dl *); STATIC INLINE ulong dl_set_state(struct dl *, ulong); #define DL_BIND_SIZE (1<<4) #define DL_CONN_SIZE (1<<4) #define DL_LIST_SIZE (1<<4) #define DL_BIND_HASHMASK (DL_BIND_SIZE-1) #define DL_CONN_HASHMASK (DL_CONN_SIZE-1) #define DL_LIST_HASHMASK (DL_LIST_SIZE-1) #define DLF_UNATTACHED (1<<DL_UNATTACHED) #define DLF_ATTACH_PENDING (1<<DL_ATTACH_PENDING) #define DLF_DETACH_PENDING (1<<DL_DETACH_PENDING) #define DLF_UNBOUND (1<<DL_UNBOUND) #define DLF_BIND_PENDING (1<<DL_BIND_PENDING) #define DLF_UNBIND_PENDING (1<<DL_UNBIND_PENDING) #define DLF_IDLE (1<<DL_IDLE) #define DLF_UDQOS_PENDING (1<<DL_UDQOS_PENDING) #define DLF_OUTCON_PENDING (1<<DL_OUTCON_PENDING) #define DLF_INCON_PENDING (1<<DL_INCON_PENDING) #define DLF_CONN_RES_PENDING (1<<DL_CONN_RES_PENDING) #define DLF_DATAXFER (1<<DL_DATAXFER) #define DLF_USER_RESET_PENDING (1<<DL_USER_RESET_PENDING) #define DLF_PROV_RESET_PENDING (1<<DL_PROV_RESET_PENDING) #define DLF_RESET_RES_PENDING (1<<DL_RESET_RES_PENDING) #define DLF_DISCON8_PENDING (1<<DL_DISCON8_PENDING) #define DLF_DISCON9_PENDING (1<<DL_DISCON9_PENDING) #define DLF_DISCON11_PENDING (1<<DL_DISCON11_PENDING) #define DLF_DISCON12_PENDING (1<<DL_DISCON12_PENDING) #define DLF_DISCON13_PENDING (1<<DL_DISCON13_PENDING) #define DLF_SUBS_BIND_PND (1<<DL_SUBS_BIND_PND) #define DLF_SUBS_UNBIND_PND (1<<DL_SUBS_UNBIND_PND) /* * CD - CD Provider * ----------------------------------- */ struct cd { STR_DECLARATION (struct cd); /* stream declaration */ SLIST_HASH (dl, bind, DL_BIND_SIZE); /* bind hash */ SLIST_HASH (dl, conn, DL_CONN_SIZE); /* conn hash */ SLIST_HASH (dl, list, DL_LIST_SIZE); /* list hash */ struct dl *mgmt; /* connection management stream */ SLIST_HEAD (dl, wait); /* DLs waiting on CD state change */ SLIST_HEAD (dl, teim); /* DLs performing TEI management */ SLIST_HEAD (dl, dl); /* CD attached DL list */ ulong ppa; /* CD physical point of attachment */ ulong mode; /* user/ntwk/both mode */ cd_info_ack_t info; /* CD information */ struct lapd_proto proto; /* CD protocol variant and options */ struct lapd_timers_cd timers; /* CD timers */ struct lapd_option_cd config; /* CD configuration */ struct lapd_statem_cd statem; /* CD state */ struct lapd_stats_cd statsp; /* CD statistics periods */ struct lapd_stats_cd stamp; /* CD statistics timestamps */ struct lapd_stats_cd stats; /* CD statistics */ struct lapd_notify_cd notify; /* CD notifications */ }; #define CD_PRIV(__q) ((struct cd *)(__q)->q_ptr) STATIC INLINE struct cd *cd_lookup(ulong); STATIC INLINE struct cd *cd_get(struct cd *); STATIC INLINE void cd_put(struct cd *); STATIC INLINE ulong cd_get_id(ulong); STATIC struct cd *dl_alloc_link(queue_t *, struct cd **, ulong, cred_t *); STATIC void dl_free_link(struct cd *); STATIC INLINE ulong cd_get_state(struct cd *); STATIC INLINE ulong cd_set_state(struct cd *, ulong); #define CDF_UNATTACHED (1<<CD_UNATTACHED) #define CDF_UNUSABLE (1<<CD_UNUSABLE) #define CDF_DISABLED (1<<CD_DISABLED) #define CDF_ENABLE_PENDING (1<<CD_ENABLE_PENDING) #define CDF_ENABLED (1<<CD_ENABLED) #define CDF_READ_ACTIVE (1<<CD_READ_ACTIVE) #define CDF_INPUT_ALLOWED (1<<CD_INPUT_ALLOWED) #define CDF_DISABLE_PENDING (1<<CD_DISABLE_PENDING) #define CDF_OUTPUT_ACTIVE (1<<CD_OUTPUT_ACTIVE) #define CDF_XRAY (1<<CD_XRAY) /* * ========================================================================= * * STATE TRANSISIONS * * ========================================================================= */ #ifdef _DEBUG STATIC INLINE const char *dl_state_name(ulong state) { switch (state) { case DL_UNATTACHED: return ("DL_UNATTACHED"); case DL_ATTACH_PENDING: return ("DL_ATTACH_PENDING"); case DL_DETACH_PENDING: return ("DL_DETACH_PENDING"); case DL_UNBOUND: return ("DL_UNBOUND"); case DL_BIND_PENDING: return ("DL_BIND_PENDING"); case DL_UNBIND_PENDING: return ("DL_UNBIND_PENDING"); case DL_IDLE: return ("DL_IDLE"); case DL_UDQOS_PENDING: return ("DL_UDQOS_PENDING"); case DL_OUTCON_PENDING: return ("DL_OUTCON_PENDING"); case DL_INCON_PENDING: return ("DL_INCON_PENDING"); case DL_CONN_RES_PENDING: return ("DL_CONN_RES_PENDING"); case DL_DATAXFER: return ("DL_DATAXFER"); case DL_USER_RESET_PENDING: return ("DL_USER_RESET_PENDING"); case DL_PROV_RESET_PENDING: return ("DL_PROV_RESET_PENDING"); case DL_RESET_RES_PENDING: return ("DL_RESET_RES_PENDING"); case DL_DISCON8_PENDING: return ("DL_DISCON8_PENDING"); case DL_DISCON9_PENDING: return ("DL_DISCON9_PENDING"); case DL_DISCON11_PENDING: return ("DL_DISCON11_PENDING"); case DL_DISCON12_PENDING: return ("DL_DISCON12_PENDING"); case DL_DISCON13_PENDING: return ("DL_DISCON13_PENDING"); case DL_SUBS_BIND_PND: return ("DL_SUBS_BIND_PND"); case DL_SUBS_UNBIND_PND: return ("DL_SUBS_UNBIND_PND"); case -1UL: return ("DL_UNUSABLE"); default: swerr(); return ("DL_????"); } } #endif STATIC INLINE ulong dl_get_state(struct dl * dl) { return (dl->i_state); } STATIC INLINE ulong dl_set_state(struct dl * dl, ulong newstate) { ulong oldstate = dl_get_state(dl); (void) oldstate; dl->info.dl_current_state = dl->i_state = newstate; printd(("%s: %p: %s <- %s\n", LAPD_DRV_NAME, dl, dl_state_name(newstate), dl_state_name(oldstate))); return (newstate); } #ifdef _DEBUG STATIC INLINE const char *cd_state_name(ulong state) { switch (state) { case CD_UNATTACHED: return ("CD_UNATTACHED"); case CD_UNUSABLE: return ("CD_UNUSABLE"); case CD_DISABLED: return ("CD_DISABLED"); case CD_ENABLE_PENDING: return ("CD_ENABLE_PENDING"); case CD_ENABLED: return ("CD_ENABLED"); case CD_READ_ACTIVE: return ("CD_READ_ACTIVE"); case CD_INPUT_ALLOWED: return ("CD_INPUT_ALLOWED"); case CD_DISABLE_PENDING: return ("CD_DISABLE_PENDING"); case CD_OUTPUT_ACTIVE: return ("CD_OUTPUT_ACTIVE"); case CD_XRAY: return ("CD_XRAY"); default: swerr(); return ("CD_????"); } } #endif STATIC INLINE ulong cd_get_state(struct cd * cd) { return (cd->i_state); } STATIC INLINE ulong cd_set_state(struct cd * cd, ulong newstate) { ulong oldstate = cd_get_state(cd); (void) oldstate; cd->info.cd_state = cd->i_state = newstate; printd(("%s: %p: %s <- %s\n", LAPD_DRV_NAME, cd, cd_state_name(newstate), cd_state_name(oldstate))); return (newstate); } /* * ========================================================================= * * FUNCTIONS * * ========================================================================= */ STATIC void dl_teim_link(struct dl *dl, struct cd *cd) { ensure(dl, return); unless(dl->teim.cd, return); if ((dl->teim.next = cd->teim.list)) dl->teim.next->teim.prev = &dl->teim.next; dl->teim.prev = &cd->teim.list; dl->teim.cd = cd_get(cd); cd->teim.list = dl_get(dl); cd->teim.numb++; } STATIC void dl_teim_unlink(struct dl *dl) { ensure(dl->teim.cd, return); if ((*dl->teim.prev = dl->teim.next)) dl->teim.next->teim.prev = dl->teim.prev; dl->teim.next = NULL; dl->teim.prev = &dl->teim.next; dl->teim.cd->teim.numb--; cd_put(xchg(&dl->teim.cd, NULL)); dl_put(dl); } STATIC void dl_wait_link(struct dl *dl, struct cd *cd) { ensure(dl, return); unless(dl->wait.cd, return); if ((dl->wait.next = cd->wait.list)) dl->wait.next->wait.prev = &dl->wait.next; dl->wait.prev = &cd->wait.list; dl->wait.cd = cd_get(cd); cd->wait.list = dl_get(dl); cd->wait.numb++; } STATIC void dl_wait_unlink(struct dl *dl) { ensure(dl->wait.cd, return); if ((*dl->wait.prev = dl->wait.next)) dl->wait.next->wait.prev = dl->wait.prev; dl->wait.next = NULL; dl->wait.prev = &dl->wait.next; dl->wait.cd->wait.numb--; cd_put(xchg(&dl->wait.cd, NULL)); dl_put(dl); } STATIC void dl_mgmt_link(struct dl *dl, struct cd *cd) { unless(cd->mgmt, return); if ((dl->mgmt.next = cd->mgmt)) dl->mgmt.next->mgmt.prev = &dl->mgmt.next; dl->mgmt.prev = &cd->mgmt; dl->mgmt.cd = cd_get(cd); cd->mgmt = dl_get(dl); } STATIC void dl_mgmt_unlink(struct dl *dl) { ensure(dl->mgmt.cd, return); if ((*dl->mgmt.prev = dl->mgmt.next)) dl->mgmt.next->mgmt.prev = dl->mgmt.prev; dl->mgmt.next = NULL; dl->mgmt.prev = &dl->mgmt.next; cd_put(xchg(&dl->mgmt.cd, NULL)); dl_put(dl); } STATIC void dl_atta_link(struct dl *dl, struct cd *cd) { unless(dl->cd.cd, return); if ((dl->cd.next = cd->dl.list)) dl->cd.next->cd.prev = &dl->cd.next; dl->cd.prev = &cd->dl.list; dl->cd.cd = cd_get(cd); cd->dl.list = dl_get(dl); cd->dl.numb++; } STATIC void dl_atta_unlink(struct dl *dl) { ensure(dl->cd.cd, return); if ((*dl->cd.prev = dl->cd.next)) dl->cd.next->cd.prev = dl->cd.prev; dl->cd.next = NULL; dl->cd.prev = &dl->cd.next; dl->cd.cd->dl.numb--; cd_put(xchg(&dl->cd.cd, NULL)); dl_put(dl); } STATIC void dl_bind_link(struct dl *dl, struct cd *cd) { int i; ensure(dl, return); ensure(cd, return); unless(dl->bind.cd, return); i = ((dl->dlc.dl_sap + dl->dlc.dl_tei) & DL_BIND_HASHMASK); if ((dl->bind.next = cd->bind.hash[i])) dl->bind.next->bind.prev = &dl->bind.next; dl->bind.prev = &cd->bind.hash[i]; dl->bind.cd = cd_get(cd); cd->bind.hash[i] = dl_get(dl); cd->bind.numb++; } STATIC void dl_bind_unlink(struct dl *dl) { ensure(dl->bind.cd, return); if ((*dl->bind.prev = dl->bind.next)) dl->bind.next->bind.prev = dl->bind.prev; dl->bind.next = NULL; dl->bind.prev = &dl->bind.next; dl->bind.cd->bind.numb--; cd_put(xchg(&dl->bind.cd, NULL)); dl_put(dl); } STATIC void dl_conn_link(struct dl *dl, struct cd *cd) { int i; ensure(dl, return); ensure(cd, return); unless(dl->conn.cd, return); i = ((dl->dlc.dl_sap + dl->dlc.dl_tei) & DL_CONN_HASHMASK); if ((dl->conn.next = cd->conn.hash[i])) dl->conn.next->conn.prev = &dl->conn.next; dl->conn.prev = &cd->conn.hash[i]; dl->conn.cd = cd_get(cd); cd->conn.hash[i] = dl_get(dl); cd->conn.numb++; } STATIC void dl_conn_unlink(struct dl *dl) { ensure(dl->conn.cd, return); if ((*dl->conn.prev = dl->conn.next)) dl->conn.next->conn.prev = dl->conn.prev; dl->conn.next = NULL; dl->conn.prev = &dl->conn.next; dl->conn.cd->conn.numb--; cd_put(xchg(&dl->conn.cd, NULL)); dl_put(dl); } STATIC void dl_list_link(struct dl *dl, struct cd *cd) { int i; ensure(dl, return); ensure(cd, return); unless(dl->list.cd, return); i = ((dl->dlc.dl_sap + dl->dlc.dl_tei) & DL_LIST_HASHMASK); if ((dl->list.next = cd->list.hash[i])) dl->list.next->list.prev = &dl->list.next; dl->list.prev = &cd->list.hash[i]; dl->list.cd = cd_get(cd); cd->list.hash[i] = dl_get(dl); cd->list.numb++; } STATIC void dl_list_unlink(struct dl *dl) { ensure(dl->list.cd, return); if ((*dl->list.prev = dl->list.next)) dl->list.next->list.prev = dl->list.prev; dl->list.next = NULL; dl->list.prev = &dl->list.next; dl->list.cd->list.numb--; cd_put(xchg(&dl->list.cd, NULL)); dl_put(dl); } STATIC INLINE void dl_mast_link(struct dl *dl, struct dl **dlp) { if ((dl->next = *dlp)) dl->next->prev = &dl->next; dl->prev = dlp; *dlp = dl_get(dl); master.dl.numb++; } STATIC INLINE void dl_mast_unlink(struct dl *dl) { if ((*dl->prev = dl->next)) dl->next->prev = dl->prev; dl->next = NULL; dl->prev = &dl->next; dl_put(dl); master.dl.numb--; } STATIC INLINE void cd_mast_link(struct cd *cd, struct cd **cdp) { if ((cd->next = *cdp)) cd->next->prev = &cd->next; cd->prev = cdp; *cdp = cd_get(cd); master.cd.numb++; } STATIC INLINE void cd_mast_unlink(struct cd *cd) { if ((*cd->prev = cd->next)) cd->next->prev = cd->prev; cd->next = NULL; cd->prev = &cd->next; cd_put(cd); master.cd.numb--; } /* * ========================================================================= * * TIMERS * * ========================================================================= */ enum { tall, t200, t201, t202, t203 }; /* * DL timers * ------------------------------------------------------------------------- */ SS7_DECLARE_TIMER(LAPD_DRV_NAME, dl, t200, config.timers); SS7_DECLARE_TIMER(LAPD_DRV_NAME, dl, t201, config.timers); SS7_DECLARE_TIMER(LAPD_DRV_NAME, dl, t202, config.timers); SS7_DECLARE_TIMER(LAPD_DRV_NAME, dl, t203, config.timers); STATIC INLINE void __dl_timer_stop(struct dl *dl, const uint t) { int single = 1; switch (t) { case tall: single = 0; /* fall through */ case t200: dl_stop_timer_t200(dl); if (single) break; /* fall through */ case t201: dl_stop_timer_t201(dl); if (single) break; /* fall through */ case t202: dl_stop_timer_t202(dl); if (single) break; /* fall through */ case t203: dl_stop_timer_t203(dl); if (single) break; /* fall through */ break; default: swerr(); break; } } STATIC INLINE void dl_timer_stop(struct dl *dl, const uint t) { int flags; lis_spin_lock_irqsave(&dl->lock, &flags); { __dl_timer_stop(dl, t); } lis_spin_unlock_irqrestore(&dl->lock, &flags); } STATIC INLINE void dl_timer_start(struct dl *dl, const uint t) { int flags; lis_spin_lock_irqsave(&dl->lock, &flags); { __dl_timer_stop(dl, t); switch (t) { case t200: dl_start_timer_t200(dl); break; case t201: dl_start_timer_t201(dl); break; case t202: dl_start_timer_t202(dl); break; case t203: dl_start_timer_t203(dl); break; default: swerr(); break; } } lis_spin_unlock_irqrestore(&dl->lock, &flags); } /* * CD timers * ------------------------------------------------------------------------- */ SS7_DECLARE_TIMER(LAPD_DRV_NAME, cd, t201, config.timers); SS7_DECLARE_TIMER(LAPD_DRV_NAME, cd, t202, config.timers); STATIC INLINE void __cd_timer_stop(struct cd *cd, const uint t) { int single = 1; switch (t) { case tall: single = 0; /* fall through */ case t201: cd_stop_timer_t201(cd); if (single) break; /* fall through */ case t202: cd_stop_timer_t202(cd); if (single) break; /* fall through */ break; default: swerr(); break; } } STATIC INLINE void cd_timer_stop(struct cd *cd, const uint t) { int flags; lis_spin_lock_irqsave(&cd->lock, &flags); { __cd_timer_stop(cd, t); } lis_spin_unlock_irqrestore(&cd->lock, &flags); } STATIC INLINE void cd_timer_start(struct cd *cd, const uint t) { int flags; lis_spin_lock_irqsave(&cd->lock, &flags); { __cd_timer_stop(cd, t); switch (t) { case t201: cd_start_timer_t201(cd); break; case t202: cd_start_timer_t202(cd); break; default: swerr(); break; } } lis_spin_unlock_irqrestore(&cd->lock, &flags); } /* * ========================================================================= * * PRIMITIVES * * ========================================================================= */ /* * ------------------------------------------------------------------------- * * Primitive sent to upstream * * ------------------------------------------------------------------------- */ /* * M_HANGUP * ----------------------------------- */ STATIC int m_hangup(queue_t *q, struct dl *dl) { mblk_t *mp; if ((mp = ss7_allocb(q, 0, BPRI_MED))) { mp->b_datap->db_type = M_HANGUP; printd(("%s: %p: <- M_HANGUP\n", LAPD_DRV_NAME, dl)); putnext(dl->oq, mp); return (QR_DONE); } rare(); return (-ENOBUFS); } /* * M_ERROR * ----------------------------------- */ STATIC int m_error(queue_t *q, struct dl *dl, uint8_t rerr, uint8_t werr) { mblk_t *mp; if ((mp = ss7_allocb(q, 2, BPRI_MED))) { mp->b_datap->db_type = M_ERROR; *mp->b_wptr++ = rerr; *mp->b_wptr++ = werr; printd(("%s: %p: <- M_ERROR\n", LAPD_DRV_NAME, dl)); putnext(dl->oq, mp); return (QR_DONE); } rare(); return (-ENOBUFS); } /* * M_HANGUP All * ----------------------------------- */ STATIC int m_hangup_all(queue_t *q, struct cd *cd) { struct dl *dl; int err; fixme(("re-write this function\n")); /* this function should also detach the dl */ for (dl = cd->dl.list; dl; dl = dl->cd.next) if ((err = m_hangup(q, dl))) return (err); return (QR_DONE); } /* * M_ERROR All * ----------------------------------- */ STATIC int m_error_all(queue_t *q, struct cd *cd, uint8_t rerr, uint8_t werr) { struct dl *dl; int err; fixme(("re-write this function\n")); /* this function should also detach the dl */ for (dl = cd->dl.list; dl; dl = dl->cd.next) if ((err = m_error(q, dl, rerr, werr))) return (err); return (QR_DONE); } /* * DL_INFO_ACK 0x03 * ----------------------------------- */ STATIC INLINE int dl_info_ack(queue_t *q, struct dl *dl) { mblk_t *mp; dl_info_ack_t *p; size_t alen, blen = 0; caddr_t aptr, bptr = NULL; if (dl_get_state(dl) > DL_UNATTACHED) { alen = sizeof(dl->dlc); aptr = (caddr_t) (&dl->dlc); } else { alen = 0; aptr = NULL; } if ((mp = ss7_allocb(q, sizeof(*p) + alen + blen, BPRI_MED))) { mp->b_datap->db_type = M_PCPROTO; p = (typeof(p)) mp->b_wptr++; *p = dl->info; p->dl_primitive = DL_INFO_ACK; p->dl_addr_length = alen; p->dl_addr_offset = alen ? sizeof(*p) : 0; p->dl_brdcst_addr_length = blen; p->dl_brdcst_addr_offset = blen ? sizeof(*p) + alen : 0; if (alen) { bcopy(aptr, mp->b_wptr, alen); mp->b_wptr += alen; } todo(("should return broadcast address\n")); if (blen) { bcopy(bptr, mp->b_wptr, blen); mp->b_wptr += blen; } printd(("%s: %p: <- DL_INFO_ACK\n", LAPD_DRV_NAME, dl)); putnext(dl->oq, mp); return (QR_DONE); } rare(); return (-ENOBUFS); } /* * DL_BIND_ACK 0x04 * ----------------------------------- */ STATIC INLINE int dl_bind_ack(queue_t *q, struct dl *dl, struct cd *cd) { mblk_t *mp; dl_bind_ack_t *p; size_t add_len = 0; caddr_t add_ptr = NULL; if ((mp = ss7_allocb(q, sizeof(*p) + add_len, BPRI_MED))) { mp->b_datap->db_type = M_PCPROTO; p = (typeof(p)) mp->b_wptr++; p->dl_primitive = DL_BIND_ACK; p->dl_sap = dl->dlc.dl_sap; p->dl_addr_length = add_len; p->dl_addr_offset = add_len ? sizeof(*p) : 0; p->dl_max_conind = dl->conind; p->dl_xidtest_flg = ((dl->flags & DLF_AUTO_XID) ? DL_AUTO_XID : 0) | ((dl-> flags & DLF_AUTO_TEST) ? DL_AUTO_TEST : 0); if (add_len) { bcopy(add_ptr, mp->b_wptr, add_len); mp->b_wptr += add_len; } if (!dl->bind.cd) dl_bind_link(dl, cd); if (!dl->list.cd && dl->conind) dl_list_link(dl, cd); if (dl->wait.cd) dl_wait_unlink(dl); dl->state = LAPD_TEI_UNASSIGNED; dl_set_state(dl, DL_IDLE); printd(("%s: %p: <- DL_BIND_ACK\n", LAPD_DRV_NAME, dl)); putnext(dl->oq, mp); return (QR_DONE); } rare(); return (-ENOBUFS); } /* * DL_SUBS_BIND_ACK 0x1c * ----------------------------------- */ STATIC INLINE int dl_subs_bind_ack(queue_t *q, struct dl *dl, uchar tei) { mblk_t *mp; dl_subs_bind_ack_t *p; size_t sap_len = sizeof(tei); caddr_t sap_ptr = (caddr_t) &tei; if ((mp = ss7_allocb(q, sizeof(*p) + sap_len, BPRI_MED))) { mp->b_datap->db_type = M_PCPROTO; p = (typeof(p)) mp->b_wptr++; p->dl_primitive = DL_SUBS_BIND_ACK; p->dl_subs_sap_length = sap_len; p->dl_subs_sap_offset = sap_len ? sizeof(*p) : 0; if (sap_len) { bcopy(sap_ptr, mp->b_wptr, sap_len); mp->b_wptr += sap_len; } dl_bind_unlink(dl); if (dl->conind) dl_list_unlink(dl); dl->dlc.dl_tei = tei; dl_bind_link(dl, dl->cd.cd); if (dl->conind) dl_list_link(dl, dl->cd.cd); printd(("%s: %p: <- DL_SUBS_BIND_ACK\n", LAPD_DRV_NAME, dl)); putnext(dl->oq, mp); return (QR_DONE); } rare(); return (-ENOBUFS); } /* * DL_OK_ACK 0x06 * ----------------------------------- */ STATIC INLINE int send_UA_res(queue_t *q, struct cd *cd, uint sapi, uint tei, uint pf); STATIC INLINE int send_DM_res(queue_t *q, struct cd *cd, uint sapi, uint tei, uint pf); STATIC INLINE int send_DISC_cmd(queue_t *q, struct cd *cd, uint sapi, uint tei, uint pf); STATIC INLINE int dl_ok_ack(queue_t *q, struct dl *dl, ulong prim, struct cd *cd, struct dl *ap, mblk_t *cp) { mblk_t *mp; dl_ok_ack_t *p; int err; if ((mp = ss7_allocb(q, sizeof(*p), BPRI_MED))) { mp->b_datap->db_type = M_PCPROTO; p = (typeof(p)) mp->b_wptr++; p->dl_primitive = DL_OK_ACK; p->dl_correct_primitive = prim; if (dl->wait.cd) dl_wait_unlink(dl); /* remove from wait list */ switch (dl_get_state(dl)) { case DL_ATTACH_PENDING: if (!dl->cd.cd) dl_atta_link(dl, cd); /* add dl to cd attach list */ dl->state = LAPD_TEI_UNASSIGNED; dl_set_state(dl, DL_UNBOUND); break; case DL_DETACH_PENDING: if (dl->cd.cd) dl_atta_unlink(dl); /* remove from cd attach list */ dl->state = LAPD_TEI_UNASSIGNED; dl_set_state(dl, DL_UNATTACHED); break; case DL_UNBIND_PENDING: if (dl->bind.cd) dl_bind_unlink(dl); /* remove from bind hash */ if (dl->list.cd) dl_list_unlink(dl); /* remove from list hash */ dl->state = LAPD_TEI_UNASSIGNED; dl_set_state(dl, DL_UNBOUND); break; case DL_CONN_RES_PENDING: ensure(ap, ap = dl); if (cp) { uchar sapi = (cp->b_rptr[0] >> 2) & 0x3f; uchar tei = (cp->b_rptr[1] >> 1) & 0x7f; uchar pf = (cp->b_rptr[3] & 0x01); if ((err = send_UA_res(q, cd, sapi, tei, pf)) < 0) { freemsg(mp); return (err); } dl_timer_start(ap, t203); bufq_unlink(&dl->conq, cp); freemsg(cp); } if (ap != dl) { /* auto attach */ if (dl->cd.cd != ap->cd.cd) { dl_atta_unlink(ap); dl_atta_link(ap, dl->cd.cd); } /* auto bind */ if (dl->dlc.dl_sap != ap->dlc.dl_sap || dl->dlc.dl_tei != ap->dlc.dl_tei) { dl_bind_unlink(ap); ap->dlc = dl->dlc; dl_bind_link(ap, ap->cd.cd); } if (dl->conq.q_msgs) dl_set_state(dl, DL_INCON_PENDING); else dl_set_state(dl, DL_IDLE); } /* move to data transfer state */ dl->state = LAPD_ESTABLISHED; dl_set_state(ap, DL_DATAXFER); /* place in connection hashes */ dl_conn_link(ap, ap->cd.cd); ap->vs = ap->vr = ap->va = 0; break; case DL_RESET_RES_PENDING: dl->state = LAPD_ESTABLISHED; dl_set_state(dl, DL_DATAXFER); break; case DL_DISCON9_PENDING: if (cp) { uchar sapi = (cp->b_rptr[0] >> 2) & 0x3f; uchar tei = (cp->b_rptr[1] >> 1) & 0x7f; uchar pf = (cp->b_rptr[3] & 0x01); if ((err = send_DM_res(q, cd, sapi, tei, pf)) < 0) { freemsg(mp); return (err); } bufq_unlink(&dl->conq, cp); freemsg(cp); } dl->state = LAPD_TEI_ASSIGNED; dl_set_state(dl, DL_IDLE); break; case DL_DISCON8_PENDING: case DL_DISCON11_PENDING: case DL_DISCON12_PENDING: case DL_DISCON13_PENDING: if ((1 << cd_get_state(cd)) & (CDF_ENABLED | CDF_READ_ACTIVE | CDF_INPUT_ALLOWED | CDF_OUTPUT_ACTIVE)) { /* layer 1 active */ uchar sapi = dl->dlc.dl_sap; uchar tei = dl->dlc.dl_tei; uchar pf = 1; if ((err = send_DISC_cmd(q, cd, sapi, tei, pf))) { freemsg(mp); return (err); } } dl->state = LAPD_WAIT_RELEASE; dl_set_state(dl, DL_IDLE); break; case DL_SUBS_UNBIND_REQ: /* just jam tei */ dl_bind_unlink(dl); if (dl->conind) dl_list_unlink(dl); dl->dlc.dl_tei = -1; dl_bind_link(dl, dl->cd.cd); if (dl->conind) dl_list_link(dl, dl->cd.cd); dl->state = LAPD_TEI_UNASSIGNED; dl_set_state(dl, DL_IDLE); break; default: /* happens for promicon, promiscoff, enabmulti, disabmulti */ /* remain in the same state */ break; } printd(("%s: %p: <- DL_OK_ACK\n", LAPD_DRV_NAME, dl)); putnext(dl->oq, mp); return (QR_DONE); } rare(); return (-ENOBUFS); } /* * DL_ERROR_ACK 0x05 * ----------------------------------- */ STATIC INLINE int dl_error_ack(queue_t *q, struct dl *dl, ulong prim, ulong error, ulong reason) { mblk_t *mp; dl_error_ack_t *p; if ((mp = ss7_allocb(q, sizeof(*p), BPRI_MED))) { mp->b_datap->db_type = M_PCPROTO; p = (typeof(p)) mp->b_wptr++; p->dl_primitive = DL_ERROR_ACK; p->dl_error_primitive = prim; p->dl_errno = error; p->dl_unix_errno = reason; if (dl->wait.cd) dl_wait_unlink(dl); /* remove from wait list */ /* restore previous state */ dl_set_state(dl, dl->i_oldstate); printd(("%s: %p: <- DL_ERROR_ACK\n", LAPD_DRV_NAME, dl)); putnext(dl->oq, mp); return (QR_DONE); } rare(); return (-ENOBUFS); } /* * DL_CONNECT_IND 0x0e * ----------------------------------- */ STATIC INLINE int dl_connect_ind(queue_t *q, struct dl *dl, mblk_t *cp, size_t cda_len, caddr_t cda_ptr, size_t cga_len, caddr_t cga_ptr) { mblk_t *mp; dl_connect_ind_t *p; if ((mp = ss7_allocb(q, sizeof(*p) + cda_len + cga_len, BPRI_MED))) { mp->b_datap->db_type = M_PROTO; p = (typeof(p)) mp->b_wptr++; p->dl_primitive = DL_CONNECT_IND; p->dl_correlation = (typeof(p->dl_correlation)) cp; p->dl_called_addr_length = cda_len; p->dl_called_addr_offset = cda_len ? sizeof(*p) : 0; p->dl_calling_addr_length = cga_len; p->dl_calling_addr_offset = cga_len ? sizeof(*p) + cda_len : 0; p->dl_qos_length = 0; p->dl_qos_offset = 0; p->dl_growth = 0; if (cda_len) { bcopy(cda_ptr, mp->b_wptr, cda_len); mp->b_wptr += cda_len; } if (cga_len) { bcopy(cga_ptr, mp->b_wptr, cga_len); mp->b_wptr += cga_len; } bufq_queue(&dl->conq, cp); dl_set_state(dl, DL_INCON_PENDING); printd(("%s: %p: <- DL_CONNECT_IND\n", LAPD_DRV_NAME, dl)); putnext(dl->oq, mp); return (QR_ABSORBED); } rare(); return (-ENOBUFS); } /* * DL_CONNECT_CON 0x10 * ----------------------------------- */ STATIC INLINE int dl_connect_con(queue_t *q, struct dl *dl, size_t res_len, caddr_t res_ptr) { mblk_t *mp; dl_connect_con_t *p; if ((mp = ss7_allocb(q, sizeof(*p) + res_len, BPRI_MED))) { mp->b_datap->db_type = M_PROTO; p = (typeof(p)) mp->b_wptr++; p->dl_primitive = DL_CONNECT_CON; p->dl_resp_addr_length = res_len; p->dl_resp_addr_offset = res_len ? sizeof(*p) : 0; p->dl_qos_length = 0; p->dl_qos_offset = 0; p->dl_growth = 0; if (res_len) { bcopy(res_ptr, mp->b_wptr, res_len); mp->b_wptr += res_len; } dl->state = LAPD_ESTABLISHED; dl_set_state(dl, DL_DATAXFER); dl_timer_start(dl, t200); dl_timer_start(dl, t203); dl->vs = dl->vr = dl->va = 0; printd(("%s: %p: <- DL_CONNECT_CON\n", LAPD_DRV_NAME, dl)); putnext(dl->oq, mp); return (QR_DONE); } rare(); return (-ENOBUFS); } /* * DL_TOKEN_ACK 0x12 * ----------------------------------- */ STATIC INLINE int dl_token_ack(queue_t *q, struct dl *dl) { mblk_t *mp; dl_token_ack_t *p; if ((mp = ss7_allocb(q, sizeof(*p), BPRI_MED))) { mp->b_datap->db_type = M_PCPROTO; p = (typeof(p)) mp->b_wptr++; p->dl_primitive = DL_TOKEN_ACK; p->dl_token = dl->id; printd(("%s: %p: <- DL_TOKEN_ACK\n", LAPD_DRV_NAME, dl)); putnext(dl->oq, mp); return (QR_DONE); } rare(); return (-ENOBUFS); } /* * DL_DISCONNECT_IND 0x14 * ----------------------------------- */ STATIC INLINE int dl_disconnect_ind(queue_t *q, struct dl *dl, ulong orig, ulong reason, mblk_t *cp) { mblk_t *mp; dl_disconnect_ind_t *p; if ((mp = ss7_allocb(q, sizeof(*p), BPRI_MED))) { mp->b_datap->db_type = M_PROTO; p = (typeof(p)) mp->b_wptr++; p->dl_primitive = DL_DISCONNECT_IND; p->dl_originator = orig; p->dl_reason = reason; p->dl_correlation = (ulong) cp; if (cp) { bufq_unlink(&dl->conq, cp); if (dl->conq.q_msgs) dl_set_state(dl, DL_INCON_PENDING); else dl_set_state(dl, DL_IDLE); } else { dl->state = LAPD_TEI_ASSIGNED; dl_set_state(dl, DL_IDLE); } printd(("%s: %p: <- DL_DISCONNECT_IND\n", LAPD_DRV_NAME, dl)); putnext(dl->oq, mp); return (QR_DONE); } rare(); return (-ENOBUFS); } /* * DL_RESET_IND 0x18 * ----------------------------------- */ STATIC INLINE int dl_reset_ind(queue_t *q, struct dl *dl, ulong orig, ulong reason) { mblk_t *mp; dl_reset_ind_t *p; if ((mp = ss7_allocb(q, sizeof(*p), BPRI_MED))) { mp->b_datap->db_type = M_PROTO; p = (typeof(p)) mp->b_wptr++; p->dl_primitive = DL_RESET_IND; p->dl_originator = orig; p->dl_reason = reason; dl_set_state(dl, DL_PROV_RESET_PENDING); printd(("%s: %p: <- DL_RESET_IND\n", LAPD_DRV_NAME, dl)); putnext(dl->oq, mp); return (QR_DONE); } rare(); return (-ENOBUFS); } /* * DL_RESET_CON 0x1a * ----------------------------------- */ STATIC INLINE int dl_reset_con(queue_t *q, struct dl *dl) { mblk_t *mp; dl_reset_con_t *p; if ((mp = ss7_allocb(q, sizeof(*p), BPRI_MED))) { mp->b_datap->db_type = M_PROTO; p = (typeof(p)) mp->b_wptr++; p->dl_primitive = DL_RESET_CON; dl->state = LAPD_ESTABLISHED; dl_set_state(dl, DL_DATAXFER); printd(("%s: %p: <- DL_RESET_CON\n", LAPD_DRV_NAME, dl)); putnext(dl->oq, mp); return (QR_DONE); } rare(); return (-ENOBUFS); } /* * DL_UNITDATA_IND 0x08 * ----------------------------------- */ STATIC INLINE int dl_unitdata_ind(queue_t *q, struct dl *dl, mblk_t *dp, size_t dst_len, caddr_t dst_ptr, size_t src_len, caddr_t src_ptr, ulong flag) { mblk_t *mp; dl_unitdata_ind_t *p; if ((mp = ss7_allocb(q, sizeof(*p) + dst_len + src_len, BPRI_MED))) { mp->b_datap->db_type = M_PROTO; p = (typeof(p)) mp->b_wptr++; p->dl_primitive = DL_UNITDATA_IND; p->dl_dest_addr_length = dst_len; p->dl_dest_addr_offset = dst_len ? sizeof(*p) : 0; p->dl_src_addr_length = src_len; p->dl_src_addr_offset = src_len ? sizeof(*p) + dst_len : 0; p->dl_group_address = flag; if (dst_len) { bcopy(dst_ptr, mp->b_wptr, dst_len); mp->b_wptr += dst_len; } if (src_len) { bcopy(src_ptr, mp->b_wptr, src_len); mp->b_wptr += src_len; } mp->b_cont = dp; printd(("%s: %p: <- DL_UNITDATA_IND\n", LAPD_DRV_NAME, dl)); putnext(dl->oq, mp); return (QR_DONE); } rare(); return (-ENOBUFS); } /* * DL_UDERROR_IND 0x09 * ----------------------------------- */ STATIC INLINE int dl_uderror_ind(queue_t *q, struct dl *dl, ulong errno, ulong reason, size_t dst_len, caddr_t dst_ptr, mblk_t *dp) { mblk_t *mp; dl_uderror_ind_t *p; if ((mp = ss7_allocb(q, sizeof(*p) + dst_len, BPRI_MED))) { mp->b_datap->db_type = M_PCPROTO; p = (typeof(p)) mp->b_wptr++; p->dl_primitive = DL_UDERROR_IND; p->dl_dest_addr_length = dst_len; p->dl_dest_addr_offset = dst_len ? sizeof(*p) : 0; p->dl_unix_errno = reason; p->dl_errno = errno; if (dst_len) { bcopy(dst_ptr, mp->b_wptr, dst_len); mp->b_wptr += dst_len; } mp->b_cont = dp; printd(("%s: %p: <- DL_UDERROR_IND\n", LAPD_DRV_NAME, dl)); putnext(dl->oq, mp); return (QR_DONE); } rare(); return (-ENOBUFS); } /* * DL_TEST_IND 0x2e * ----------------------------------- */ STATIC INLINE int dl_test_ind(queue_t *q, struct dl *dl, ulong flag, size_t dst_len, caddr_t dst_ptr, size_t src_len, caddr_t src_ptr, mblk_t *dp) { mblk_t *mp; dl_test_ind_t *p; if ((mp = ss7_allocb(q, sizeof(*p), BPRI_MED))) { mp->b_datap->db_type = M_PROTO; p = (typeof(p)) mp->b_wptr++; p->dl_primitive = DL_TEST_IND; p->dl_flag = flag; p->dl_dest_addr_length = dst_len; p->dl_dest_addr_offset = dst_len ? sizeof(*p) : 0; p->dl_src_addr_length = src_len; p->dl_src_addr_offset = src_len ? sizeof(*p) + dst_len : 0; if (dst_len) { bcopy(dst_ptr, mp->b_wptr, dst_len); mp->b_wptr += dst_len; } if (src_len) { bcopy(src_ptr, mp->b_wptr, src_len); mp->b_wptr += src_len; } mp->b_cont = dp; printd(("%s: %p: <- DL_TEST_IND\n", LAPD_DRV_NAME, dl)); putnext(dl->oq, mp); return (QR_DONE); } rare(); return (-ENOBUFS); } /* * DL_TEST_CON 0x30 * ----------------------------------- */ STATIC INLINE int dl_test_con(queue_t *q, struct dl *dl, ulong flag, size_t dst_len, caddr_t dst_ptr, size_t src_len, caddr_t src_ptr, mblk_t *dp) { mblk_t *mp; dl_test_con_t *p; if ((mp = ss7_allocb(q, sizeof(*p), BPRI_MED))) { mp->b_datap->db_type = M_PROTO; p = (typeof(p)) mp->b_wptr++; p->dl_primitive = DL_TEST_CON; p->dl_flag = flag; p->dl_dest_addr_length = dst_len; p->dl_dest_addr_offset = dst_len ? sizeof(*p) : 0; p->dl_src_addr_length = src_len; p->dl_src_addr_offset = src_len ? sizeof(*p) + dst_len : 0; if (dst_len) { bcopy(dst_ptr, mp->b_wptr, dst_len); mp->b_wptr += dst_len; } if (src_len) { bcopy(src_ptr, mp->b_wptr, src_len); mp->b_wptr += src_len; } mp->b_cont = dp; printd(("%s: %p: <- DL_TEST_CON\n", LAPD_DRV_NAME, dl)); putnext(dl->oq, mp); return (QR_DONE); } rare(); return (-ENOBUFS); } /* * DL_XID_IND 0x2a * ----------------------------------- */ STATIC INLINE int dl_xid_ind(queue_t *q, struct dl *dl, ulong flag, size_t dst_len, caddr_t dst_ptr, size_t src_len, caddr_t src_ptr, mblk_t *dp) { mblk_t *mp; dl_xid_ind_t *p; if ((mp = ss7_allocb(q, sizeof(*p), BPRI_MED))) { mp->b_datap->db_type = M_PROTO; p = (typeof(p)) mp->b_wptr++; p->dl_primitive = DL_XID_IND; p->dl_flag = flag; p->dl_dest_addr_length = dst_len; p->dl_dest_addr_offset = dst_len ? sizeof(*p) : 0; p->dl_src_addr_length = src_len; p->dl_src_addr_offset = src_len ? sizeof(*p) + dst_len : 0; if (dst_len) { bcopy(dst_ptr, mp->b_wptr, dst_len); mp->b_wptr += dst_len; } if (src_len) { bcopy(src_ptr, mp->b_wptr, src_len); mp->b_wptr += src_len; } mp->b_cont = dp; printd(("%s: %p: <- DL_XID_IND\n", LAPD_DRV_NAME, dl)); putnext(dl->oq, mp); return (QR_DONE); } rare(); return (-ENOBUFS); } /* * DL_XID_CON 0x2c * ----------------------------------- */ STATIC INLINE int dl_xid_con(queue_t *q, struct dl *dl, ulong flag, size_t dst_len, caddr_t dst_ptr, size_t src_len, caddr_t src_ptr, mblk_t *dp) { mblk_t *mp; dl_xid_con_t *p; if ((mp = ss7_allocb(q, sizeof(*p), BPRI_MED))) { mp->b_datap->db_type = M_PROTO; p = (typeof(p)) mp->b_wptr++; p->dl_primitive = DL_XID_CON; p->dl_flag = flag; p->dl_dest_addr_length = dst_len; p->dl_dest_addr_offset = dst_len ? sizeof(*p) : 0; p->dl_src_addr_length = src_len; p->dl_src_addr_offset = src_len ? sizeof(*p) + dst_len : 0; if (dst_len) { bcopy(dst_ptr, mp->b_wptr, dst_len); mp->b_wptr += dst_len; } if (src_len) { bcopy(src_ptr, mp->b_wptr, src_len); mp->b_wptr += src_len; } mp->b_cont = dp; printd(("%s: %p: <- DL_XID_CON\n", LAPD_DRV_NAME, dl)); putnext(dl->oq, mp); return (QR_DONE); } rare(); return (-ENOBUFS); } /* * DL_DATA_ACK_IND 0x22 * ----------------------------------- */ STATIC INLINE int dl_data_ack_ind(queue_t *q, struct dl *dl, size_t dst_len, caddr_t dst_ptr, size_t src_len, caddr_t src_ptr, ulong prio, ulong sclass, mblk_t *dp) { mblk_t *mp; dl_data_ack_ind_t *p; if ((mp = ss7_allocb(q, sizeof(*p) + dst_len + src_len, BPRI_MED))) { mp->b_datap->db_type = M_PROTO; p = (typeof(p)) mp->b_wptr++; p->dl_primitive = DL_DATA_ACK_IND; p->dl_dest_addr_length = dst_len; p->dl_dest_addr_offset = dst_len ? sizeof(*p) : 0; p->dl_src_addr_length = src_len; p->dl_src_addr_offset = src_len ? sizeof(*p) + dst_len : 0; p->dl_priority = prio; p->dl_service_class = sclass; if (dst_len) { bcopy(dst_ptr, mp->b_wptr, dst_len); mp->b_wptr += dst_len; } if (src_len) { bcopy(src_ptr, mp->b_wptr, src_len); mp->b_wptr += src_len; } mp->b_cont = dp; printd(("%s: %p: <- DL_DATA_ACK_IND\n", LAPD_DRV_NAME, dl)); putnext(dl->oq, mp); return (QR_DONE); } rare(); return (-ENOBUFS); } /* * DL_DATA_ACK_STATUS_IND 0x23 * ----------------------------------- */ STATIC INLINE int dl_data_ack_status_ind(queue_t *q, struct dl *dl, ulong corr, ulong status) { mblk_t *mp; dl_data_ack_status_ind_t *p; if ((mp = ss7_allocb(q, sizeof(*p), BPRI_MED))) { mp->b_datap->db_type = M_PROTO; p = (typeof(p)) mp->b_wptr++; p->dl_primitive = DL_DATA_ACK_STATUS_IND; p->dl_correlation = corr; p->dl_status = status; printd(("%s: %p: <- DL_DATA_ACK_STATUS_IND\n", LAPD_DRV_NAME, dl)); putnext(dl->oq, mp); return (QR_DONE); } rare(); return (-ENOBUFS); } /* * DL_REPLY_IND 0x25 * ----------------------------------- */ STATIC INLINE int dl_reply_ind(queue_t *q, struct dl *dl, size_t dst_len, caddr_t dst_ptr, size_t src_len, caddr_t src_ptr, ulong prio, ulong sclass, mblk_t *dp) { mblk_t *mp; dl_reply_ind_t *p; if ((mp = ss7_allocb(q, sizeof(*p) + dst_len + src_len, BPRI_MED))) { mp->b_datap->db_type = M_PROTO; p = (typeof(p)) mp->b_wptr++; p->dl_primitive = DL_REPLY_IND; p->dl_dest_addr_length = dst_len; p->dl_dest_addr_offset = dst_len ? sizeof(*p) : 0; p->dl_src_addr_length = src_len; p->dl_src_addr_offset = src_len ? sizeof(*p) + dst_len : 0; p->dl_priority = prio; p->dl_service_class = sclass; if (dst_len) { bcopy(dst_ptr, mp->b_wptr, dst_len); mp->b_wptr += dst_len; } if (src_len) { bcopy(src_ptr, mp->b_wptr, src_len); mp->b_wptr += src_len; } mp->b_cont = dp; printd(("%s: %p: <- DL_REPLY_IND\n", LAPD_DRV_NAME, dl)); putnext(dl->oq, mp); return (QR_DONE); } rare(); return (-ENOBUFS); } /* * DL_REPLY_STATUS_IND 0x26 * ----------------------------------- */ STATIC INLINE int dl_reply_status_ind(queue_t *q, struct dl *dl, ulong corr, ulong status) { mblk_t *mp; dl_reply_status_ind_t *p; if ((mp = ss7_allocb(q, sizeof(*p), BPRI_MED))) { mp->b_datap->db_type = M_PROTO; p = (typeof(p)) mp->b_wptr++; p->dl_primitive = DL_REPLY_STATUS_IND; p->dl_correlation = corr; p->dl_status = status; printd(("%s: %p: <- DL_REPLY_STATUS_IND\n", LAPD_DRV_NAME, dl)); putnext(dl->oq, mp); return (QR_DONE); } rare(); return (-ENOBUFS); } /* * DL_REPLY_UPDATE_STATUS_IND 0x28 * ----------------------------------- */ STATIC INLINE int dl_reply_update_status_ind(queue_t *q, struct dl *dl, ulong corr, ulong status) { mblk_t *mp; dl_reply_update_status_ind_t *p; if ((mp = ss7_allocb(q, sizeof(*p), BPRI_MED))) { mp->b_datap->db_type = M_PROTO; p = (typeof(p)) mp->b_wptr++; p->dl_primitive = DL_REPLY_UPDATE_STATUS_IND; p->dl_correlation = corr; p->dl_status = status; printd(("%s: %p: <- DL_REPLY_UPDATE_STATUS_IND\n", LAPD_DRV_NAME, dl)); putnext(dl->oq, mp); return (QR_DONE); } rare(); return (-ENOBUFS); } /* * DL_PHYS_ADDR_ACK 0x32 * ----------------------------------- */ STATIC INLINE int dl_phys_addr_ack(queue_t *q, struct dl *dl, size_t add_len, caddr_t add_ptr) { mblk_t *mp; dl_phys_addr_ack_t *p; if ((mp = ss7_allocb(q, sizeof(*p) + add_len, BPRI_MED))) { mp->b_datap->db_type = M_PCPROTO; p = (typeof(p)) mp->b_wptr++; p->dl_primitive = DL_PHYS_ADDR_ACK; p->dl_addr_length = add_len; p->dl_addr_offset = add_len ? sizeof(*p) : 0; if (add_len) { bcopy(add_ptr, mp->b_wptr, add_len); mp->b_wptr += add_len; } printd(("%s: %p: <- DL_PHYS_ADDR_ACK\n", LAPD_DRV_NAME, dl)); putnext(dl->oq, mp); return (QR_DONE); } rare(); return (-ENOBUFS); } /* * DL_GET_STATISTICS_ACK 0x35 * ----------------------------------- */ STATIC INLINE int dl_get_statistics_ack(queue_t *q, struct dl *dl, size_t sta_len, caddr_t sta_ptr) { mblk_t *mp; dl_get_statistics_ack_t *p; if ((mp = ss7_allocb(q, sizeof(*p) + sta_len, BPRI_MED))) { mp->b_datap->db_type = M_PCPROTO; p = (typeof(p)) mp->b_wptr++; p->dl_primitive = DL_GET_STATISTICS_ACK; p->dl_stat_length = sta_len; p->dl_stat_offset = sta_len ? sizeof(*p) : 0; if (sta_len) { bcopy(sta_ptr, mp->b_wptr, sta_len); mp->b_wptr += sta_len; } printd(("%s: %p: <- DL_GET_STATISTICS_ACK\n", LAPD_DRV_NAME, dl)); putnext(dl->oq, mp); return (QR_DONE); } rare(); return (-ENOBUFS); } /* * ------------------------------------------------------------------------- * * Primitive sent to downstream * * ------------------------------------------------------------------------- */ /* * CD_INFO_REQ 0x00 - Information request * ------------------------------------------------------------------------- */ STATIC INLINE int cd_info_req(queue_t *q, struct cd *cd) { mblk_t *mp; cd_info_req_t *p; if ((mp = ss7_allocb(q, sizeof(*p), BPRI_MED))) { mp->b_datap->db_type = M_PCPROTO; p = (typeof(p)) mp->b_wptr++; p->cd_primitive = CD_INFO_REQ; printd(("%s: %p: <- CD_INFO_REQ\n", LAPD_DRV_NAME, dl)); putnext(cd->oq, mp); return (QR_DONE); } rare(); return (-ENOBUFS); } /* * CD_ATTACH_REQ 0x02 - Attach a PPA * ------------------------------------------------------------------------- */ STATIC INLINE int cd_attach_req(queue_t *q, struct cd *cd, struct dl *dl) { mblk_t *mp; cd_attach_req_t *p; if ((mp = ss7_allocb(q, sizeof(*p), BPRI_MED))) { mp->b_datap->db_type = M_PROTO; p = (typeof(p)) mp->b_wptr++; p->cd_primitive = CD_ATTACH_REQ; p->cd_ppa = cd->ppa; dl_wait_link(dl, cd); /* put the dl into the wait list */ printd(("%s: %p: <- CD_ATTACH_REQ\n", LAPD_DRV_NAME, dl)); putnext(cd->oq, mp); return (QR_DONE); } rare(); return (-ENOBUFS); } /* * CD_DETACH_REQ 0x03 - Detach a PPA * ------------------------------------------------------------------------- */ STATIC INLINE int cd_detach_req(queue_t *q, struct cd *cd, struct dl *dl) { mblk_t *mp; cd_detach_req_t *p; if ((mp = ss7_allocb(q, sizeof(*p), BPRI_MED))) { mp->b_datap->db_type = M_PROTO; p = (typeof(p)) mp->b_wptr++; p->cd_primitive = CD_DETACH_REQ; dl_wait_link(dl, cd); /* put the dl into the wait list */ printd(("%s: %p: <- CD_DETACH_REQ\n", LAPD_DRV_NAME, dl)); putnext(cd->oq, mp); return (QR_DONE); } rare(); return (-ENOBUFS); } /* * CD_ENABLE_REQ 0x04 - Prepare a device * ------------------------------------------------------------------------- */ STATIC INLINE int cd_enable_req(queue_t *q, struct cd *cd, struct dl *dl) { mblk_t *mp; cd_enable_req_t *p; if ((mp = ss7_allocb(q, sizeof(*p), BPRI_MED))) { mp->b_datap->db_type = M_PROTO; p = (typeof(p)) mp->b_wptr++; p->cd_primitive = CD_ENABLE_REQ; p->cd_dial_type = CD_NODIAL; p->cd_dial_length = 0; p->cd_dial_offset = 0; cd_set_state(cd, CD_ENABLE_PENDING); dl_wait_link(dl, cd); /* put the dl into the wait list */ printd(("%s: %p: <- CD_ENABLE_REQ\n", LAPD_DRV_NAME, dl)); putnext(cd->oq, mp); return (QR_DONE); } rare(); return (-ENOBUFS); } /* * CD_DISABLE_REQ 0x05 - Disable a device * ------------------------------------------------------------------------- */ STATIC INLINE int cd_disable_req(queue_t *q, struct cd *cd, struct dl *dl, ulong disposal) { mblk_t *mp; cd_disable_req_t *p; if ((mp = ss7_allocb(q, sizeof(*p), BPRI_MED))) { mp->b_datap->db_type = M_PROTO; p = (typeof(p)) mp->b_wptr++; p->cd_primitive = CD_DISABLE_REQ; p->cd_disposal = disposal; cd_set_state(cd, CD_DISABLE_PENDING); dl_wait_link(dl, cd); /* put the dl into the wait list */ printd(("%s: %p: <- CD_DISABLE_REQ\n", LAPD_DRV_NAME, dl)); putnext(cd->oq, mp); return (QR_DONE); } rare(); return (-ENOBUFS); } /* * CD_ALLOW_INPUT_REQ 0x0b - Allow input * ------------------------------------------------------------------------- */ STATIC INLINE int cd_allow_input_req(queue_t *q, struct cd *cd) { mblk_t *mp; cd_allow_input_req_t *p; if ((mp = ss7_allocb(q, sizeof(*p), BPRI_MED))) { mp->b_datap->db_type = M_PROTO; p = (typeof(p)) mp->b_wptr++; p->cd_primitive = CD_ALLOW_INPUT_REQ; cd_set_state(cd, CD_INPUT_ALLOWED); printd(("%s: %p: <- CD_ALLOW_INPUT_REQ\n", LAPD_DRV_NAME, dl)); putnext(cd->oq, mp); return (QR_DONE); } rare(); return (-ENOBUFS); } /* * CD_READ_REQ 0x0c - Wait-for-input request * ------------------------------------------------------------------------- */ STATIC INLINE int cd_read_req(queue_t *q, struct cd *cd, ulong msec) { mblk_t *mp; cd_read_req_t *p; if ((mp = ss7_allocb(q, sizeof(*p), BPRI_MED))) { mp->b_datap->db_type = M_PROTO; p = (typeof(p)) mp->b_wptr++; p->cd_primitive = CD_READ_REQ; p->cd_msec = msec; cd_set_state(cd, CD_READ_ACTIVE); printd(("%s: %p: <- CD_READ_REQ\n", LAPD_DRV_NAME, dl)); putnext(cd->oq, mp); return (QR_DONE); } rare(); return (-ENOBUFS); } /* * CD_UNITDATA_REQ 0x0d - Data send request * ------------------------------------------------------------------------- */ STATIC INLINE int cd_unitdata_req(queue_t *q, struct cd *cd, ulong atype, ulong prio, size_t dst_len, caddr_t dst_ptr, mblk_t *dp) { mblk_t *mp; cd_unitdata_req_t *p; if ((mp = ss7_allocb(q, sizeof(*p) + dst_len, BPRI_MED))) { mp->b_datap->db_type = M_PROTO; p = (typeof(p)) mp->b_wptr++; p->cd_primitive = CD_UNITDATA_REQ; p->cd_addr_type = atype; p->cd_priority = prio; p->cd_dest_addr_length = dst_len; p->cd_dest_addr_offset = dst_len ? sizeof(*p) : 0; if (dst_len) { bcopy(dst_ptr, mp->b_wptr, dst_len); mp->b_wptr += dst_len; } mp->b_cont = dp; printd(("%s: %p: <- CD_UNITDATA_REQ\n", LAPD_DRV_NAME, dl)); putnext(cd->oq, mp); return (QR_DONE); } rare(); return (-ENOBUFS); } /* * CD_WRITE_READ_REQ 0x0e - Write/read request * ------------------------------------------------------------------------- */ STATIC INLINE int cd_write_read_req(queue_t *q, struct cd *cd, ulong atype, ulong prio, size_t dst_len, caddr_t dst_ptr, mblk_t *dp, ulong msec) { mblk_t *mp; cd_write_read_req_t *p; if ((mp = ss7_allocb(q, sizeof(*p) + dst_len, BPRI_MED))) { mp->b_datap->db_type = M_PROTO; p = (typeof(p)) mp->b_wptr++; p->cd_primitive = CD_WRITE_READ_REQ; p->cd_unitdata_req.cd_primitive = CD_UNITDATA_REQ; p->cd_unitdata_req.cd_addr_type = atype; p->cd_unitdata_req.cd_priority = prio; p->cd_unitdata_req.cd_dest_addr_length = dst_len; p->cd_unitdata_req.cd_dest_addr_offset = dst_len ? sizeof(*p) : 0; p->cd_read_req.cd_primitive = CD_READ_REQ; p->cd_read_req.cd_msec = msec; if (dst_len) { bcopy(dst_ptr, mp->b_wptr, dst_len); mp->b_wptr += dst_len; } mp->b_cont = dp; printd(("%s: %p: <- CD_WRITE_READ_REQ\n", LAPD_DRV_NAME, dl)); putnext(cd->oq, mp); return (QR_DONE); } rare(); return (-ENOBUFS); } /* * CD_HALT_INPUT_REQ 0x11 - Halt input * ------------------------------------------------------------------------- */ STATIC INLINE int cd_halt_input_req(queue_t *q, struct cd *cd, ulong disposal) { mblk_t *mp; cd_halt_input_req_t *p; if ((mp = ss7_allocb(q, sizeof(*p), BPRI_MED))) { mp->b_datap->db_type = M_PROTO; p = (typeof(p)) mp->b_wptr++; p->cd_primitive = CD_HALT_INPUT_REQ; p->cd_disposal = disposal; cd_set_state(cd, CD_ENABLED); printd(("%s: %p: <- CD_HALT_INPUT_REQ\n", LAPD_DRV_NAME, dl)); putnext(cd->oq, mp); return (QR_DONE); } rare(); return (-ENOBUFS); } /* * CD_ABORT_OUTPUT_REQ 0x12 - Abort output * ------------------------------------------------------------------------- */ STATIC INLINE int cd_abort_output_req(queue_t *q, struct cd *cd) { mblk_t *mp; cd_abort_output_req_t *p; if ((mp = ss7_allocb(q, sizeof(*p), BPRI_MED))) { mp->b_datap->db_type = M_PCPROTO; p = (typeof(p)) mp->b_wptr++; p->cd_primitive = CD_ABORT_OUTPUT_REQ; printd(("%s: %p: <- CD_ABORT_OUTPUT_REQ\n", LAPD_DRV_NAME, dl)); putnext(cd->oq, mp); return (QR_DONE); } rare(); return (-ENOBUFS); } #if 0 /* * CD_MUX_NAME_REQ 0x13 - get mux name (Gcom) * ------------------------------------------------------------------------- */ STATIC INLINE int cd_mux_name_req(queue_t *q, struct cd *cd) { mblk_t *mp; cd_mux_name_req_t *p; if ((mp = ss7_allocb(q, sizeof(*p), BPRI_MED))) { mp->b_datap->db_type = M_PCPROTO; p = (typeof(p)) mp->b_wptr++; p->cd_primitive = CD_MUX_NAME_REQ; printd(("%s: %p: <- CD_MUX_NAME_REQ\n", LAPD_DRV_NAME, dl)); putnext(cd->oq, mp); return (QR_DONE); } rare(); return (-ENOBUFS); } #endif /* * CD_MODEM_SIG_REQ 0x15 - Assert modem signals (Gcom) * ------------------------------------------------------------------------- */ STATIC INLINE int cd_modem_sig_req(queue_t *q, struct cd *cd, ulong sigs) { mblk_t *mp; cd_modem_sig_req_t *p; if ((mp = ss7_allocb(q, sizeof(*p), BPRI_MED))) { mp->b_datap->db_type = M_PCPROTO; p = (typeof(p)) mp->b_wptr++; p->cd_primitive = CD_MODEM_SIG_REQ; p->cd_sigs = sigs; printd(("%s: %p: <- CD_MODEM_SIG_REQ\n", LAPD_DRV_NAME, dl)); putnext(cd->oq, mp); return (QR_DONE); } rare(); return (-ENOBUFS); } /* * CD_MODEM_SIG_POLL 0x17 - requests a CD_MODEM_SIG_IND (Gcom) * ------------------------------------------------------------------------- */ STATIC INLINE int cd_modem_sig_poll(queue_t *q, struct cd *cd) { mblk_t *mp; cd_modem_sig_poll_t *p; if ((mp = ss7_allocb(q, sizeof(*p), BPRI_MED))) { mp->b_datap->db_type = M_PCPROTO; p = (typeof(p)) mp->b_wptr++; p->cd_primitive = CD_MODEM_SIG_POLL; printd(("%s: %p: <- CD_MODEM_SIG_POLL\n", LAPD_DRV_NAME, dl)); putnext(cd->oq, mp); return (QR_DONE); } rare(); return (-ENOBUFS); } /* * ========================================================================= * * STATE MACHINES * * ========================================================================= */ /* * ------------------------------------------------------------------------- * * Build Message Functions * * ------------------------------------------------------------------------- */ STATIC INLINE void build_addr(mblk_t *mp, uint sapi, uint cr, uint tei) { *mp->b_wptr++ = (sapi << 2) | (cr << 1) | (0x00); *mp->b_wptr++ = (tei << 1) | (0x01); } STATIC INLINE void build_I_header(mblk_t *mp, uint sapi, uint cr, uint tei, uint pf, uint nr, uint ns) { build_addr(mp, sapi, cr, tei); *mp->b_wptr++ = ns << 1; *mp->b_wptr++ = (nr << 1) | pf; } STATIC INLINE void build_S_header(mblk_t *mp, uint sapi, uint cr, uint tei, uint pf, uint nr, uint s) { build_addr(mp, sapi, cr, tei); *mp->b_wptr++ = (s << 2) | 0x01; *mp->b_wptr++ = (nr << 1) | pf; } STATIC INLINE void build_U_header(mblk_t *mp, uint sapi, uint cr, uint tei, uint pf, uint m) { build_addr(mp, sapi, cr, tei); *mp->b_wptr++ = (m << 2) | (pf << 4) | 0x03; } STATIC mblk_t *build_msg(queue_t *q, ulong prio, size_t len) { mblk_t *mp; cd_unitdata_req_t *p; if ((mp = ss7_allocb(q, sizeof(*p), BPRI_MED))) { mblk_t *bp; if ((bp = ss7_allocb(q, len, BPRI_MED))) { mp->b_datap->db_type = M_PROTO; mp->b_band = prio; p = (typeof(p)) mp->b_wptr++; bzero(p, sizeof(*p)); p->cd_primitive = CD_UNITDATA_REQ; p->cd_priority = prio; p->cd_addr_type = CD_IMPLICIT; p->cd_dest_addr_length = 0; p->cd_dest_addr_offset = 0; mp->b_cont = bp; bp->b_datap->db_type = M_DATA; mp->b_band = prio; return (QR_DONE); } freemsg(mp); } return (NULL); } /* * SEND I (Information) * ----------------------------------- */ STATIC INLINE int send_I_cmd(queue_t *q, struct cd *cd, uint sapi, uint tei, uint pf, uint nr, uint ns, mblk_t *dp) { /* a dl entity receiving a I with the P bit set to 1 shall set the F bit to 1 in the next RR, RNR, REJ (or FRMR or DM in LAPB) frame it transmits */ if (canputnext(cd->oq)) { mblk_t *mp; if ((mp = build_msg(q, 0, 4))) { uint cr = (cd->mode != CD_MODE_NTWK) ? 0 : 1; build_I_header(mp->b_cont, sapi, cr, tei, pf, nr, ns); linkb(mp, dp); putnext(cd->oq, mp); return (QR_ABSORBED); } return (-ENOBUFS); } return (-EBUSY); } /* * SEND RR (Receive Ready) command * ----------------------------------- */ STATIC INLINE int send_RR_cmd(queue_t *q, struct cd *cd, uint sapi, uint tei, uint pf, uint nr) { /* a dl entity receiving a RR with the P bit set to 1 shall set the F bit to 1 in the next RR, RNR, REJ frame it transmits */ if (canputnext(cd->oq)) { mblk_t *mp; if ((mp = build_msg(q, 0, 4))) { uint cr = (cd->mode != CD_MODE_NTWK) ? 0 : 1; build_S_header(mp->b_cont, sapi, cr, tei, pf, nr, 0x00); putnext(cd->oq, mp); return (QR_DONE); } return (-ENOBUFS); } return (-EBUSY); } /* * SEND RR (Receive Ready) response * ----------------------------------- */ STATIC INLINE int send_RR_res(queue_t *q, struct cd *cd, uint sapi, uint tei, uint pf, uint nr) { if (canputnext(cd->oq)) { mblk_t *mp; if ((mp = build_msg(q, 0, 4))) { uint cr = (cd->mode != CD_MODE_NTWK) ? 1 : 0; build_S_header(mp->b_cont, sapi, cr, tei, pf, nr, 0x00); putnext(cd->oq, mp); return (QR_DONE); } return (-ENOBUFS); } return (-EBUSY); } /* * SEND RNR (Receive Not Ready) command * ----------------------------------- */ STATIC INLINE int send_RNR_cmd(queue_t *q, struct cd *cd, uint sapi, uint tei, uint pf, uint nr) { /* a dl entity receiving a RNR with the P bit set to 1 shall set the F bit to 1 in the next RR, RNR, REJ frame it transmits */ if (canputnext(cd->oq)) { mblk_t *mp; if ((mp = build_msg(q, 0, 4))) { uint cr = (cd->mode != CD_MODE_NTWK) ? 0 : 1; build_S_header(mp->b_cont, sapi, cr, tei, pf, nr, 0x01); putnext(cd->oq, mp); return (QR_DONE); } return (-ENOBUFS); } return (-EBUSY); } /* * SEND RNR (Receive Not Ready) response * ----------------------------------- */ STATIC INLINE int send_RNR_res(queue_t *q, struct cd *cd, uint sapi, uint tei, uint pf, uint nr) { if (canputnext(cd->oq)) { mblk_t *mp; if ((mp = build_msg(q, 0, 4))) { uint cr = (cd->mode != CD_MODE_NTWK) ? 1 : 0; build_S_header(mp->b_cont, sapi, cr, tei, pf, nr, 0x01); putnext(cd->oq, mp); return (QR_DONE); } return (-ENOBUFS); } return (-EBUSY); } /* * SEND REJ (Reject) command * ----------------------------------- */ STATIC INLINE int send_REJ_cmd(queue_t *q, struct cd *cd, uint sapi, uint tei, uint pf, uint nr) { /* a dl entity receiving a REJ with the P bit set to 1 shall set the F bit to 1 in the next RR, RNR, REJ frame it transmits */ if (canputnext(cd->oq)) { mblk_t *mp; if ((mp = build_msg(q, 0, 4))) { uint cr = (cd->mode != CD_MODE_NTWK) ? 0 : 1; build_S_header(mp->b_cont, sapi, cr, tei, pf, nr, 0x02); putnext(cd->oq, mp); return (QR_DONE); } return (-ENOBUFS); } return (-EBUSY); } /* * SEND REJ (Reject) response * ----------------------------------- */ STATIC INLINE int send_REJ_res(queue_t *q, struct cd *cd, uint sapi, uint tei, uint pf, uint nr) { if (canputnext(cd->oq)) { mblk_t *mp; if ((mp = build_msg(q, 0, 4))) { uint cr = (cd->mode != CD_MODE_NTWK) ? 1 : 0; build_S_header(mp->b_cont, sapi, cr, tei, pf, nr, 0x02); putnext(cd->oq, mp); return (QR_DONE); } return (-ENOBUFS); } return (-EBUSY); } /* * SEND UI (Unnumbered Information) command * ----------------------------------- */ STATIC INLINE int send_UI_cmd(queue_t *q, struct cd *cd, uint sapi, uint tei, uint pf, mblk_t *dp) { if (canputnext(cd->oq)) { mblk_t *mp; if ((mp = build_msg(q, 0, 3))) { uint cr = (cd->mode != CD_MODE_NTWK) ? 0 : 1; /* the P bit shall be set to zero */ assure(pf == 0); build_U_header(mp->b_cont, sapi, cr, tei, pf, 0x00); linkb(mp, dp); putnext(cd->oq, mp); return (QR_ABSORBED); } return (-ENOBUFS); } return (-EBUSY); } /* * SEND DM (Disconnected Mode) response * ----------------------------------- */ STATIC INLINE int send_DM_res(queue_t *q, struct cd *cd, uint sapi, uint tei, uint pf) { if (canputnext(cd->oq)) { mblk_t *mp; if ((mp = build_msg(q, 0, 3))) { uint cr = (cd->mode != CD_MODE_NTWK) ? 1 : 0; build_U_header(mp->b_cont, sapi, cr, tei, pf, 0x03); putnext(cd->oq, mp); return (QR_DONE); } return (-ENOBUFS); } return (-EBUSY); } /* * SEND DISC (Disconnect) command * ----------------------------------- */ STATIC INLINE int send_DISC_cmd(queue_t *q, struct cd *cd, uint sapi, uint tei, uint pf) { /* a dl entity receiving a DISC with the P bit set to 1 shall set the F bit to 1 in the next UA or DM frame it transmits */ if (canputnext(cd->oq)) { mblk_t *mp; if ((mp = build_msg(q, 0, 3))) { uint cr = (cd->mode != CD_MODE_NTWK) ? 0 : 1; build_U_header(mp->b_cont, sapi, cr, tei, pf, 0x10); putnext(cd->oq, mp); return (QR_DONE); } return (-ENOBUFS); } return (-EBUSY); } /* * SEND UA (Unnumbered Acknowledgeemnt) response * ----------------------------------- */ STATIC INLINE int send_UA_res(queue_t *q, struct cd *cd, uint sapi, uint tei, uint pf) { if (canputnext(cd->oq)) { mblk_t *mp; if ((mp = build_msg(q, 0, 3))) { uint cr = (cd->mode != CD_MODE_NTWK) ? 1 : 0; build_U_header(mp->b_cont, sapi, cr, tei, pf, 0x18); putnext(cd->oq, mp); return (QR_DONE); } return (-ENOBUFS); } return (-EBUSY); } /* * SEND SABME (Set Asynchronous Balanced Mode Extended) command * ----------------------------------- */ STATIC INLINE int send_SABME_cmd(queue_t *q, struct cd *cd, uint sapi, uint tei, uint pf) { /* a dl entity receiving a SABME with the P bit set to 1 shall set the F bit to 1 in the next UA or DM frame it transmits */ /* no information field is permitted with a SAMBE */ if (canputnext(cd->oq)) { mblk_t *mp; if ((mp = build_msg(q, 0, 3))) { uint cr = (cd->mode != CD_MODE_NTWK) ? 0 : 1; build_U_header(mp->b_cont, sapi, cr, tei, pf, 0x1b); putnext(cd->oq, mp); return (QR_DONE); } return (-ENOBUFS); } return (-EBUSY); } /* * SEND FRMR (Frame Reject) response * ----------------------------------- */ STATIC INLINE int send_FRMR_res(queue_t *q, struct cd *cd, uint sapi, uint tei, uint pf, uchar *ctl, uint vs, uint vr, uint oldcr, uint flags) { if (canputnext(cd->oq)) { mblk_t *mp; if ((mp = build_msg(q, 0, 3))) { uint cr = (cd->mode != CD_MODE_NTWK) ? 1 : 0; build_U_header(mp->b_cont, sapi, cr, tei, pf, 0x21); *mp->b_wptr++ = ctl[0]; *mp->b_wptr++ = ctl[1]; *mp->b_wptr++ = (vs << 1) | 0x00; *mp->b_wptr++ = (vr << 1) | oldcr; *mp->b_wptr++ = flags; putnext(cd->oq, mp); return (QR_DONE); } return (-ENOBUFS); } return (-EBUSY); } /* * SEND XID (Exchange Identification) command * ----------------------------------- */ STATIC INLINE int send_XID_cmd(queue_t *q, struct cd *cd, uint sapi, uint tei, uint pf, mblk_t *dp) { if (canputnext(cd->oq)) { mblk_t *mp; if ((mp = build_msg(q, 0, 3))) { uint cr = (cd->mode != CD_MODE_NTWK) ? 0 : 1; build_U_header(mp->b_cont, sapi, cr, tei, pf, 0x2b); linkb(mp, dp); putnext(cd->oq, mp); return (QR_ABSORBED); } return (-ENOBUFS); } return (-EBUSY); } /* * SEND XID (Exchange Identification) response * ----------------------------------- */ STATIC INLINE int send_XID_res(queue_t *q, struct cd *cd, uint sapi, uint tei, uint pf, mblk_t *dp) { if (canputnext(cd->oq)) { mblk_t *mp; if ((mp = build_msg(q, 0, 3))) { uint cr = (cd->mode != CD_MODE_NTWK) ? 1 : 0; build_U_header(mp->b_cont, sapi, cr, tei, pf, 0x2b); linkb(mp, dp); putnext(cd->oq, mp); return (QR_ABSORBED); } return (-ENOBUFS); } return (-EBUSY); } #define MEI 0x0f #define TEI_ANY 127 #define TEI_ALL 127 #define TEI_MGMT_SAPI 63 #define TEI_MGMT_TEI 127 #define LAPD_TEI_MT_I_REQ 0x01 /* Identity request (user to network) */ #define LAPD_TEI_MT_I_ACK 0x02 /* Identity assigned (network to user) */ #define LAPD_TEI_MT_I_REJ 0x03 /* Identity denied (network to user) */ #define LAPD_TEI_MT_C_REQ 0x04 /* Identity check request (network to user) */ #define LAPD_TEI_MT_C_RES 0x05 /* Identity check response (user to network) */ #define LAPD_TEI_MT_I_RMV 0x06 /* Identity remove (network to user) */ #define LAPD_TEI_MT_I_VER 0x07 /* Identity verify (user to network) */ STATIC INLINE int send_TEI_i_req(queue_t *q, struct cd *cd, ushort ref, uchar ai) { if (canputnext(cd->oq)) { mblk_t *mp; if ((mp = build_msg(q, 0, 8))) { uint cr = (cd->mode != CD_MODE_NTWK) ? 0 : 1; build_U_header(mp->b_cont, TEI_MGMT_SAPI, cr, TEI_MGMT_TEI, 0, 0x00); *mp->b_cont->b_wptr++ = MEI; *mp->b_cont->b_wptr++ = ref; *mp->b_cont->b_wptr++ = ref >> 8; *mp->b_cont->b_wptr++ = LAPD_TEI_MT_I_REQ; *mp->b_cont->b_wptr++ = (ai << 1) | 0x01; putnext(cd->oq, mp); return (QR_ABSORBED); } return (-ENOBUFS); } return (-EBUSY); } STATIC INLINE int send_TEI_i_ack(queue_t *q, struct cd *cd, ushort ref, uchar ai) { if (canputnext(cd->oq)) { mblk_t *mp; if ((mp = build_msg(q, 0, 8))) { uint cr = (cd->mode != CD_MODE_NTWK) ? 0 : 1; build_U_header(mp->b_cont, TEI_MGMT_SAPI, cr, TEI_MGMT_TEI, 0, 0x00); *mp->b_cont->b_wptr++ = MEI; *mp->b_cont->b_wptr++ = ref; *mp->b_cont->b_wptr++ = ref >> 8; *mp->b_cont->b_wptr++ = LAPD_TEI_MT_I_ACK; *mp->b_cont->b_wptr++ = (ai << 1) | 0x01; putnext(cd->oq, mp); return (QR_ABSORBED); } return (-ENOBUFS); } return (-EBUSY); } STATIC INLINE int send_TEI_i_rej(queue_t *q, struct cd *cd, ushort ref, uchar ai) { if (canputnext(cd->oq)) { mblk_t *mp; if ((mp = build_msg(q, 0, 8))) { uint cr = (cd->mode != CD_MODE_NTWK) ? 0 : 1; build_U_header(mp->b_cont, TEI_MGMT_SAPI, cr, TEI_MGMT_TEI, 0, 0x00); *mp->b_cont->b_wptr++ = MEI; *mp->b_cont->b_wptr++ = ref; *mp->b_cont->b_wptr++ = ref >> 8; *mp->b_cont->b_wptr++ = LAPD_TEI_MT_I_REJ; *mp->b_cont->b_wptr++ = (ai << 1) | 0x01; putnext(cd->oq, mp); return (QR_ABSORBED); } return (-ENOBUFS); } return (-EBUSY); } STATIC INLINE int send_TEI_c_req(queue_t *q, struct cd *cd, uchar ai) { if (canputnext(cd->oq)) { mblk_t *mp; if ((mp = build_msg(q, 0, 8))) { uint cr = (cd->mode != CD_MODE_NTWK) ? 0 : 1; build_U_header(mp->b_cont, TEI_MGMT_SAPI, cr, TEI_MGMT_TEI, 0, 0x00); *mp->b_cont->b_wptr++ = MEI; *mp->b_cont->b_wptr++ = 0; *mp->b_cont->b_wptr++ = 0; *mp->b_cont->b_wptr++ = LAPD_TEI_MT_C_REQ; *mp->b_cont->b_wptr++ = (ai << 1) | 0x01; putnext(cd->oq, mp); return (QR_ABSORBED); } return (-ENOBUFS); } return (-EBUSY); } STATIC INLINE int send_TEI_c_res(queue_t *q, struct cd *cd, ushort ref, uchar *ai_ptr, size_t ai_len) { if (canputnext(cd->oq)) { mblk_t *mp; if ((mp = build_msg(q, 0, 7 + ai_len))) { uint cr = (cd->mode != CD_MODE_NTWK) ? 0 : 1; build_U_header(mp->b_cont, TEI_MGMT_SAPI, cr, TEI_MGMT_TEI, 0, 0x00); *mp->b_cont->b_wptr++ = MEI; *mp->b_cont->b_wptr++ = ref; *mp->b_cont->b_wptr++ = ref >> 8; *mp->b_cont->b_wptr++ = LAPD_TEI_MT_C_RES; while (ai_len--) *mp->b_cont->b_wptr++ = ((*ai_ptr++) << 1) | (ai_len ? 0 : 1); putnext(cd->oq, mp); return (QR_ABSORBED); } return (-ENOBUFS); } return (-EBUSY); } STATIC INLINE int send_TEI_i_rmv(queue_t *q, struct cd *cd, uchar ai) { if (canputnext(cd->oq)) { mblk_t *mp; if ((mp = build_msg(q, 0, 8))) { uint cr = (cd->mode != CD_MODE_NTWK) ? 0 : 1; build_U_header(mp->b_cont, TEI_MGMT_SAPI, cr, TEI_MGMT_TEI, 0, 0x00); *mp->b_cont->b_wptr++ = MEI; *mp->b_cont->b_wptr++ = 0; *mp->b_cont->b_wptr++ = 0; *mp->b_cont->b_wptr++ = LAPD_TEI_MT_I_RMV; *mp->b_cont->b_wptr++ = (ai << 1) | 0x01; putnext(cd->oq, mp); return (QR_ABSORBED); } return (-ENOBUFS); } return (-EBUSY); } STATIC INLINE int send_TEI_i_ver(queue_t *q, struct cd *cd, uchar ai) { if (canputnext(cd->oq)) { mblk_t *mp; if ((mp = build_msg(q, 0, 8))) { uint cr = (cd->mode != CD_MODE_NTWK) ? 0 : 1; build_U_header(mp->b_cont, TEI_MGMT_SAPI, cr, TEI_MGMT_TEI, 0, 0x00); *mp->b_cont->b_wptr++ = MEI; *mp->b_cont->b_wptr++ = 0; *mp->b_cont->b_wptr++ = 0; *mp->b_cont->b_wptr++ = LAPD_TEI_MT_I_VER; *mp->b_cont->b_wptr++ = (ai << 1) | 0x01; putnext(cd->oq, mp); return (QR_ABSORBED); } return (-ENOBUFS); } return (-EBUSY); } /* * ------------------------------------------------------------------------- * * Receive Message Functions * * ------------------------------------------------------------------------- */ /* * RECV I (Information) * ----------------------------------- */ STATIC int recv_I_cmd(queue_t *q, mblk_t *mp, struct cd *cd, uint sapi, uint tei, uint pf, uint nr, uint ns) { /* a dl entity receiving a I with the P bit set to 1 shall set the F bit to 1 in the next RR, RNR, REJ (or FRMR or DM in LAPB) frame it transmits */ fixme(("Write this function!\n")); return (-EIO); } /* * RECV RR (Receive Ready) command * ----------------------------------- */ STATIC int recv_RR_cmd(queue_t *q, mblk_t *mp, struct cd *cd, uint sapi, uint tei, uint pf, uint nr) { /* a dl entity receiving a RR with the P bit set to 1 shall set the F bit to 1 in the next RR, RNR, REJ frame it transmits */ if (pf) { } else { } fixme(("Write this function!\n")); return (-EIO); } /* * RECV RR (Receive Ready) response * ----------------------------------- */ STATIC int recv_RR_res(queue_t *q, mblk_t *mp, struct cd *cd, uint sapi, uint tei, uint pf, uint nr) { if (pf) { } else { } fixme(("Write this function!\n")); return (-EIO); } /* * RECV RNR (Receive Not Ready) command * ----------------------------------- */ STATIC int recv_RNR_cmd(queue_t *q, mblk_t *mp, struct cd *cd, uint sapi, uint tei, uint pf, uint nr) { /* a dl entity receiving a RNR with the P bit set to 1 shall set the F bit to 1 in the next RR, RNR, REJ frame it transmits */ if (pf) { } else { } fixme(("Write this function!\n")); return (-EIO); } /* * RECV RNR (Receive Not Ready) response * ----------------------------------- */ STATIC int recv_RNR_res(queue_t *q, mblk_t *mp, struct cd *cd, uint sapi, uint tei, uint pf, uint nr) { if (pf) { } else { } fixme(("Write this function!\n")); return (-EIO); } /* * RECV REJ (Reject) command * ----------------------------------- */ STATIC int recv_REJ_cmd(queue_t *q, mblk_t *mp, struct cd *cd, uint sapi, uint tei, uint pf, uint nr) { /* a dl entity receiving a REJ with the P bit set to 1 shall set the F bit to 1 in the next RR, RNR, REJ frame it transmits */ if (pf) { } else { } fixme(("Write this function!\n")); return (-EIO); } /* * RECV REJ (Reject) response * ----------------------------------- */ STATIC int recv_REJ_res(queue_t *q, mblk_t *mp, struct cd *cd, uint sapi, uint tei, uint pf, uint nr) { if (pf) { } else { } fixme(("Write this function!\n")); return (-EIO); } /* * RECV UI (Unnumbered Information) command * ----------------------------------- */ STATIC int recv_UI_cmd(queue_t *q, mblk_t *mp, struct cd *cd, uint sapi, uint tei, uint pf) { /* the P bit shall be set to zero */ assure(pf == 0); if (sapi == 63 && tei == 127 && mp->b_rptr[3] == 0x0f) { struct dl *dl; ulong ref; uchar ai; int err; ref = mp->b_rptr[5]; ref <<= 8; ref |= mp->b_rptr[4]; ai = (mp->b_rptr[7] >> 1) & 0x7f; switch (mp->b_rptr[6]) { case LAPD_TEI_MT_I_REQ: /* Identity request (user to network) */ if (cd->mode == CD_MODE_USER) goto discard; if (ai == 127) { /* pick any TEI */ } else { /* requested TEI */ } break; case LAPD_TEI_MT_I_ACK: /* Identity assigned (network to user) */ if (cd->mode == CD_MODE_NTWK) goto discard; if (ai < 64 || ai > 126) goto discard; /* find requesting data link */ for (dl = cd->teim.list; dl && dl->ref != ref; dl = dl->teim.next) ; if (!dl) goto discard; /* accept assigned value */ switch (dl->state) { case LAPD_WAIT_TEI_ASSIGN: /* was connectionless */ dl_timer_stop(dl, t202); switch (dl_get_state(dl)) { case DL_SUBS_BIND_PND: if ((err = dl_subs_bind_ack(q, dl, ai))) return (err); /* fall through */ case DL_IDLE: dl->dlc.dl_tei = ai; if (dl->bind.cd) { dl_bind_unlink(dl); dl_bind_link(dl, cd); } dl->state = LAPD_TEI_ASSIGNED; dl_teim_unlink(dl); return (QR_DONE); } goto discard; case LAPD_WAIT_TEI_ESTABLISH: /* was connection oriented */ dl_timer_stop(dl, t202); switch (dl_get_state(dl)) { case DL_OUTCON_PENDING: if ((err = send_SABME_cmd(q, cd, dl->dlc.dl_sap, ai, 1))) return (err); dl->dlc.dl_tei = ai; if (dl->bind.cd) { dl_bind_unlink(dl); dl_bind_link(dl, cd); } if (dl->list.cd) { dl_list_unlink(dl); dl_list_link(dl, cd); } dl->state = LAPD_TEI_ASSIGNED; dl_teim_unlink(dl); dl->rc = 0; dl_timer_start(dl, t200); dl_conn_link(dl, cd); dl->state = LAPD_WAIT_ESTABLISH; dl_set_state(dl, DL_OUTCON_PENDING); return (QR_DONE); } goto discard; default: goto discard; } break; case LAPD_TEI_MT_I_REJ: /* Identity denied (network to user) */ if (cd->mode == CD_MODE_NTWK) goto discard; /* find requesting data link */ /* ai doesn't matter really */ break; case LAPD_TEI_MT_C_REQ: /* Identity check request (network to user) */ if (cd->mode == CD_MODE_NTWK) goto discard; /* no reference */ break; case LAPD_TEI_MT_C_RES: /* Identity check response (user to network) */ if (cd->mode == CD_MODE_USER) goto discard; break; case LAPD_TEI_MT_I_RMV: /* Identity remove (network to user) */ if (cd->mode == CD_MODE_NTWK) goto discard; /* no reference */ break; case LAPD_TEI_MT_I_VER: /* Identity verify (user to network) */ if (cd->mode == CD_MODE_USER) goto discard; /* no reference */ break; default: goto discard; } } else { } fixme(("Write this function!\n")); return (-EIO); discard: return (QR_DONE); } /* * RECV DM (Disconnected Mode) response * ----------------------------------- */ STATIC int recv_DM_res(queue_t *q, mblk_t *mp, struct cd *cd, uint sapi, uint tei, uint pf) { struct dl *dl; /* Upon reception of a DM response with the F bit set to 1, the originator of the SABME command shall indicate this to layer 3 by means of a DL-RELEASE indication primitive, and reset timer T200. It shall the enter the TEI-assigned state. DM responses with the F bit set to zero shall be ignored in this case. */ if (!pf) goto ignore; for (dl = cd->conn.hash[((sapi + tei) & DL_CONN_HASHMASK)]; dl && (dl->dlc.dl_sap != sapi || dl->dlc.dl_tei != tei); dl = dl->conn.next) ; if (!dl) goto unexpected; switch (dl->state) { case LAPD_TEI_UNASSIGNED: fixme(("Write this procedure\n")); break; case LAPD_WAIT_TEI_ASSIGN: fixme(("Write this procedure\n")); break; case LAPD_TEI_ASSIGNED: /* 5.5.4 While in the TEI-assigned state, on receipt of an unsolicited DM response with the F bit set to 0, the data link layer entity shall, if it is able to, initiate the esnablishment procedures by the transmission of a SABME (see 5.5.1.2). Otherwise, the DM shall be ignored */ if (!pf) goto ignore; goto unexpected; case LAPD_WAIT_ESTABLISH: dl_timer_stop(dl, t200); return dl_disconnect_ind(q, dl, DL_USER, DL_DISC_UNSPECIFIED, NULL); case LAPD_WAIT_RELEASE: fixme(("Write this procedure\n")); break; case LAPD_ESTABLISHED: fixme(("Write this procedure\n")); break; case LAPD_TIME_RECOVERY: fixme(("Write this procedure\n")); break; default: swerr(); break; } unexpected: fixme(("Write this function!\n")); return (-EIO); ignore: return (QR_DONE); } /* * RECV DISC (Disconnect) command * ----------------------------------- */ STATIC int recv_DISC_cmd(queue_t *q, mblk_t *mp, struct cd *cd, uint sapi, uint tei, uint pf) { struct dl *dl; /* a dl entity receiving a DISC with the P bit set to 1 shall set the F bit to 1 in the next UA or DM frame it transmits */ for (dl = cd->conn.hash[((sapi + tei) & DL_CONN_HASHMASK)]; dl && (dl->dlc.dl_sap != sapi || dl->dlc.dl_tei != tei); dl = dl->conn.next) ; if (!dl) goto unexpected; switch (dl->state) { case LAPD_TEI_UNASSIGNED: fixme(("Write this procedure\n")); break; case LAPD_WAIT_TEI_ASSIGN: fixme(("Write this procedure\n")); break; case LAPD_TEI_ASSIGNED: /* 5.5.4 While in the TEI-assigned state, the receipt of a DISC command shall result in the transmission of a DM response with the F bit set to the value of the received P bit */ return send_DM_res(q, cd, sapi, tei, pf); case LAPD_WAIT_ESTABLISH: fixme(("Write this procedure\n")); break; case LAPD_WAIT_RELEASE: fixme(("Write this procedure\n")); break; case LAPD_ESTABLISHED: fixme(("Write this procedure\n")); break; case LAPD_TIME_RECOVERY: fixme(("Write this procedure\n")); break; default: swerr(); break; } unexpected: fixme(("Write this function!\n")); return (-EIO); } /* * RECV UA (Unnumbered Acknowledgeemnt) response * ----------------------------------- */ STATIC int recv_UA_res(queue_t *q, mblk_t *mp, struct cd *cd, uint sapi, uint tei, uint pf) { struct dl *dl; /* upon reception of a UA response with the F-bit set to 1, the originator of the SABME command shall: reset timer T200; start timer T203, if implemented; set V(S), V(R) and V(A) to 0; and enter the multiple-frame-established state and inform layer 3 using the DL-ESTABLISH confirm primitive. */ for (dl = cd->conn.hash[((sapi + tei) & DL_CONN_HASHMASK)]; dl && (dl->dlc.dl_sap != sapi || dl->dlc.dl_tei != tei); dl = dl->conn.next) ; if (!dl) goto unexpected; if (dl_get_state(dl) == DL_OUTCON_PENDING) { struct lapd_addr a = { dl_sap:sapi, dl_tei:tei, }; size_t alen = sizeof(a); caddr_t aptr = (caddr_t) &a; return dl_connect_con(q, dl, alen, aptr); } unexpected: fixme(("Write this function!\n")); return (-EIO); } /* * RECV SABME (Set Asynchronous Balanced Mode Extended) command * ----------------------------------- */ STATIC int recv_SABME_cmd(queue_t *q, mblk_t *mp, struct cd *cd, uchar sapi, uchar tei, uint pf) { struct dl *dl; /* a dl entity receiving a SABME with the P bit set to 1 shall set the F bit to 1 in the next UA or DM frame it transmits */ /* no information field is permitted with a SAMBE */ if (msgdsize(mp) > 3) return (-EINVAL); for (dl = cd->conn.hash[((sapi + tei) & DL_CONN_HASHMASK)]; dl && (dl->dlc.dl_sap != sapi || dl->dlc.dl_tei != tei); dl = dl->conn.next) ; if (dl) goto discard; /* dl already in the connected state, this could be late SABME, discard it */ for (dl = cd->list.hash[((sapi + tei) & DL_LIST_HASHMASK)]; dl && (dl->dlc.dl_sap != sapi || dl->dlc.dl_tei != tei); dl = dl->list.next) ; if (!dl) dl = cd->mgmt; if (dl && ((1 << dl_get_state(dl)) & (DLF_IDLE | DLF_INCON_PENDING))) { /* dl in listening state */ mblk_t *cp; for (cp = dl->conq.q_head; cp && (((cp->b_rptr[0] >> 2) & 0x3f) != sapi || ((cp->b_rptr[1] >> 1) & 0x7f) != tei); cp = cp->b_next) ; if (cp) goto discard; /* already processing connection indication, discard duplicate */ if (dl->conq.q_msgs < dl->conind) { struct lapd_addr a = { dl_sap:sapi, dl_tei:tei, }; size_t alen = sizeof(a); caddr_t aptr = (caddr_t) &a; return dl_connect_ind(q, dl, mp, alen, aptr, alen, aptr); } } /* If the data link layer entity is unable to enter the multiple-frame-established state, it shall respond to the SABME command with a DM response with the F bit set to the same binary value as the P bit in the received SABME command. */ return send_DM_res(q, cd, sapi, tei, pf); discard: return (QR_DONE); } /* * RECV FRMR (Frame Reject) response * ----------------------------------- */ STATIC int recv_FRMR_res(queue_t *q, mblk_t *mp, struct cd *cd, uint sapi, uint tei, uint pf) { fixme(("Write this function!\n")); return (-EIO); } /* * RECV XID (Exchange Identification) command * ----------------------------------- */ STATIC int recv_XID_cmd(queue_t *q, mblk_t *mp, struct cd *cd, uint sapi, uint tei, uint pf) { fixme(("Write this function!\n")); return (-EIO); } /* * RECV XID (Exchange Identification) response * ----------------------------------- */ STATIC int recv_XID_res(queue_t *q, mblk_t *mp, struct cd *cd, uint sapi, uint tei, uint pf) { fixme(("Write this function!\n")); return (-EIO); } /* * RECV MSG - Receive a LAPD message * ----------------------------------- */ STATIC int recv_msg(queue_t *q, mblk_t *mp) { struct cd *cd = CD_PRIV(q); size_t dlen = msgdsize(mp); int err; uint sapi, cr, tei, ctl, ns, nr, s, m, pf; (void) recv_msg; if (dlen < 3) goto discard; if (mp->b_cont && mp->b_wptr < mp->b_rptr + FASTBUF && (err = ss7_pullupmsg(q, mp, FASTBUF))) return (err); sapi = mp->b_rptr[0]; if (sapi & 0x01) /* invlaid one-octet address */ goto discard; cr = (sapi & 0x02) ? 1 : 0; if (cd->mode != CD_MODE_NTWK) cr ^= 0x1; sapi >>= 2; sapi &= 0x3f; tei = mp->b_rptr[1]; if (!(tei & 0x01)) /* invalid three+ octet address */ goto discard; tei >>= 1; tei &= 0x7f; ctl = mp->b_rptr[2]; switch (ctl & 0x03) { case 0x00: case 0x02: /* I format */ { if (dlen < 4) goto discard; ns = (mp->b_rptr[2] >> 1) & 0x7f; nr = (mp->b_rptr[3] >> 1) & 0x7f; pf = (mp->b_rptr[3] & 0x1); if (cr) return recv_I_cmd(q, mp, cd, sapi, tei, pf, nr, ns); else goto reject; break; } case 0x01: /* S format */ { if (dlen < 4) goto discard; s = (mp->b_rptr[2] >> 2) & 0x03; nr = (mp->b_rptr[3] >> 1) & 0x7f; pf = (mp->b_rptr[3] & 0x1); switch (s) { case 0x00: /* RR (receive ready) */ if (cr) return recv_RR_cmd(q, mp, cd, sapi, tei, pf, nr); else return recv_RR_res(q, mp, cd, sapi, tei, pf, nr); break; case 0x01: /* RNR (receive not ready) */ if (cr) return recv_RNR_cmd(q, mp, cd, sapi, tei, pf, nr); else return recv_RNR_res(q, mp, cd, sapi, tei, pf, nr); break; case 0x02: /* REJ (reject) */ if (cr) return recv_REJ_cmd(q, mp, cd, sapi, tei, pf, nr); else return recv_REJ_res(q, mp, cd, sapi, tei, pf, nr); break; case 0x03: goto reject; } break; } case 0x03: /* U format */ { m = (mp->b_rptr[2] >> 2) & 0x3b; pf = (mp->b_rptr[2] >> 2) & 0x1; switch (m) { case 0x00: /* UI (unnumbered information) */ if (cr) return recv_UI_cmd(q, mp, cd, sapi, tei, pf); else goto reject; break; case 0x03: /* DM (disconnected mode) */ if (cr) goto reject; else return recv_DM_res(q, mp, cd, sapi, tei, pf); break; case 0x10: /* DISC (disconnect) */ if (cr) return recv_DISC_cmd(q, mp, cd, sapi, tei, pf); else goto reject; break; case 0x18: /* UA (unnumbered acknowledgement) */ if (cr) goto reject; else return recv_UA_res(q, mp, cd, sapi, tei, pf); break; case 0x1b: /* SABME */ if (cr) return recv_SABME_cmd(q, mp, cd, sapi, tei, pf); else goto reject; break; case 0x21: /* FRMR (frame reject) */ if (dlen < 8) goto discard; if (cr) goto reject; else return recv_FRMR_res(q, mp, cd, sapi, tei, pf); break; case 0x2b: /* XID (Exchange identification) */ if (cr) return recv_XID_cmd(q, mp, cd, sapi, tei, pf); else return recv_XID_res(q, mp, cd, sapi, tei, pf); break; } break; } } discard: return (QR_DONE); reject: return (-ENXIO); } /* * ========================================================================= * * PRIVATE STRUCTURE ALLOCATION AND DEALLOCATION * * ========================================================================= */ STATIC kmem_cache_t *dl_priv_cachep = NULL; STATIC kmem_cache_t *dl_link_cachep = NULL; STATIC void lapd_term_caches(void) { if (dl_priv_cachep) { if (kmem_cache_destroy(dl_priv_cachep)) cmn_err(CE_WARN, "%s: did not destroy dl_priv_cachep", LAPD_DRV_NAME); else { printd(("%s: destroyed dl_priv_cachep\n", LAPD_DRV_NAME)); dl_priv_cachep = NULL; } } if (dl_link_cachep) { if (kmem_cache_destroy(dl_link_cachep)) cmn_err(CE_WARN, "%s: did not destroy dl_link_cachep", LAPD_DRV_NAME); else { printd(("%s: destroyed dl_link_cachep\n", LAPD_DRV_NAME)); dl_link_cachep = NULL; } } return; } STATIC int lapd_init_caches(void) { if (!dl_priv_cachep && !(dl_priv_cachep = kmem_cache_create("dl_priv_cachep", sizeof(struct dl), 0, SLAB_HWCACHE_ALIGN, NULL, NULL))) { cmn_err(CE_PANIC, "%s: did not allocate dl_priv_cachep", LAPD_DRV_NAME); lapd_term_caches(); return (-ENOMEM); } else printd(("%s: initialized dl priv structure cache\n", LAPD_DRV_NAME)); if (!dl_link_cachep && !(dl_link_cachep = kmem_cache_create("dl_link_cachep", sizeof(struct cd), 0, SLAB_HWCACHE_ALIGN, NULL, NULL))) { cmn_err(CE_PANIC, "%s: did not allocate dl_link_cachep", LAPD_DRV_NAME); lapd_term_caches(); return (-ENOMEM); } else printd(("%s: initialized dl link structure cache\n", LAPD_DRV_NAME)); return (0); } /* * DL PRIV structure allocation and deallocation * ------------------------------------------------------------------------- */ STATIC INLINE struct dl *__dl_lookup(ulong id) { struct dl *dl; for (dl = master.dl.list; dl && dl->id != id; dl = dl->next) ; return (dl); } STATIC INLINE struct dl *dl_lookup(ulong id) { struct dl *dl; int flags = 0; lis_spin_lock_irqsave(&master.lock, &flags); dl = __dl_lookup(id); lis_spin_unlock_irqrestore(&master.lock, &flags); return (dl); } STATIC INLINE struct dl *dl_get(struct dl *dl) { assure(dl); if (dl) atomic_inc(&dl->refcnt); return (dl); } STATIC INLINE void dl_put(struct dl *dl) { assure(dl); if (dl && atomic_dec_and_test(&dl->refcnt)) { kmem_cache_free(dl_priv_cachep, dl); printd(("%s: %p: deallocated dl priv structure\n", LAPD_DRV_NAME, dl)); } } STATIC INLINE ulong dl_get_id(ulong id) { if (!id) { int flags = 0; static ulong sequence = 0; lis_spin_lock_irqsave(&master.lock, &flags); while (__dl_lookup(++sequence)) ; id = sequence; lis_spin_unlock_irqrestore(&master.lock, &flags); } return (id); } STATIC struct dl *dl_alloc_priv(queue_t *q, struct dl **dlp, dev_t *devp, cred_t *crp, int style) { struct dl *dl; ushort cmajor = getmajor(*devp); ushort cminor = getminor(*devp); printd(("%s: create dl dev = %d:%d\n", LAPD_DRV_NAME, cmajor, cminor)); if ((dl = kmem_cache_alloc(dl_priv_cachep, SLAB_ATOMIC))) { bzero(dl, sizeof(*dl)); dl_get(dl); /* first get */ dl->u.dev.cmajor = cmajor; dl->u.dev.cminor = cminor; dl->cred = *crp; lis_spin_lock_init(&dl->qlock, "dl-queue-lock"); (dl->oq = RD(q))->q_ptr = dl_get(dl); (dl->iq = WR(q))->q_ptr = dl_get(dl); dl->o_prim = &dl_r_prim; dl->i_prim = &dl_w_prim; dl->o_wakeup = NULL; dl->i_wakeup = NULL; dl->i_state = DL_UNATTACHED; dl->i_style = DL_STYLE2; dl->i_version = DL_CURRENT_VERSION; lis_spin_lock_init(&dl->lock, "dl-lock"); /* place in master list - open holds the master list lock */ dl_mast_link(dl, dlp); bufq_init(&dl->conq); dl->id = dl_get_id(0); /* fill out info ack defaults */ dl->info.dl_primitive = DL_INFO_ACK; dl->info.dl_max_sdu = 256; dl->info.dl_min_sdu = 3; dl->info.dl_addr_length = 0; dl->info.dl_mac_type = DL_ISDN; dl->info.dl_reserved = 0; dl->info.dl_current_state = DL_UNATTACHED; dl->info.dl_sap_length = 0; dl->info.dl_service_mode = (DL_CLDLS | DL_CODLS); dl->info.dl_qos_length = 0; dl->info.dl_qos_offset = 0; dl->info.dl_qos_range_length = 0; dl->info.dl_qos_range_offset = 0; dl->info.dl_provider_style = DL_STYLE2; dl->info.dl_addr_offset = 0; dl->info.dl_version = DL_CURRENT_VERSION; dl->info.dl_brdcst_addr_length = 0; dl->info.dl_brdcst_addr_offset = 0; dl->info.dl_growth = 0; fixme(("allocate option defaults")); } else ptrace(("%s: ERROR: failed to allocated dl priv structure\n", LAPD_DRV_NAME)); return (dl); } STATIC void dl_free_priv(struct dl *dl) { int flags = 0; ensure(dl, return); printd(("%s: %p: free dl dev = %d:%d\n", LAPD_DRV_NAME, dl, dl->u.dev.cmajor, dl->u.dev.cminor)); lis_spin_lock_irqsave(&dl->lock, &flags); { ss7_unbufcall((struct str *) dl); flushq(dl->oq, FLUSHALL); flushq(dl->iq, FLUSHALL); bufq_purge(&dl->conq); /* remove from wait list */ if (dl->wait.cd) dl_wait_unlink(dl); /* remove from teim list */ if (dl->teim.cd) dl_teim_unlink(dl); /* remove from bind hashes */ if (dl->bind.cd) dl_bind_unlink(dl); /* remove from conn hashes */ if (dl->conn.cd) dl_conn_unlink(dl); /* remove from list hashes */ if (dl->list.cd) dl_list_unlink(dl); /* remove form mgmt hashes */ if (dl->mgmt.cd) dl_mgmt_unlink(dl); /* remove from attachment list */ if (dl->cd.cd) dl_atta_unlink(dl); /* remove from master list */ dl_mast_unlink(dl); /* remove from queues */ dl_put(xchg(&dl->oq->q_ptr, NULL)); dl_put(xchg(&dl->iq->q_ptr, NULL)); dl->oq = NULL; dl->iq = NULL; } lis_spin_unlock_irqrestore(&dl->lock, &flags); dl_put(dl); /* final put */ return; } /* * DL LINK structure allocation and deallocation * ------------------------------------------------------------------------- */ STATIC INLINE struct cd *__cd_lookup(ulong id) { struct cd *cd; for (cd = master.cd.list; cd && cd->id != id; cd = cd->next) ; return (cd); } STATIC INLINE struct cd *cd_lookup(ulong id) { struct cd *cd = NULL; if (id) { int flags = 0; lis_spin_lock_irqsave(&master.lock, &flags); cd = __cd_lookup(id); lis_spin_unlock_irqrestore(&master.lock, &flags); } return (cd); } STATIC INLINE struct cd *cd_get(struct cd *cd) { assure(cd); if (cd) atomic_inc(&cd->refcnt); return (cd); } STATIC INLINE void cd_put(struct cd *cd) { assure(cd); if (cd && atomic_dec_and_test(&cd->refcnt)) { kmem_cache_free(dl_link_cachep, cd); printd(("%s: %p: deallocated dl link structure\n", LAPD_DRV_NAME, cd)); } } STATIC INLINE ulong cd_get_id(ulong id) { if (!id) { int flags = 0; static ulong sequence = 0; lis_spin_lock_irqsave(&master.lock, &flags); while (__cd_lookup(++sequence)) ; id = sequence; lis_spin_unlock_irqrestore(&master.lock, &flags); } return (id); } STATIC struct cd *dl_alloc_link(queue_t *q, struct cd **cdp, ulong index, cred_t *crp) { struct cd *cd; printd(("%s: create cd mux = %lu\n", LAPD_DRV_NAME, index)); if ((cd = kmem_cache_alloc(dl_link_cachep, SLAB_ATOMIC))) { bzero(cd, sizeof(*cd)); cd_get(cd); /* first get */ cd->u.mux.index = index; cd->cred = *crp; lis_spin_lock_init(&cd->qlock, "cd-queue-lock"); (cd->iq = RD(q))->q_ptr = cd_get(cd); (cd->oq = WR(q))->q_ptr = cd_get(cd); cd->o_prim = cd_w_prim; cd->i_prim = cd_r_prim; cd->o_wakeup = NULL; cd->i_wakeup = NULL; cd->i_state = CD_UNATTACHED; cd->i_style = CD_STYLE2; cd->i_version = 1; lis_spin_lock_init(&cd->lock, "cd-lock"); /* place in master list - link holds the master list lock */ cd_mast_link(cd, cdp); /* fill out info ack defaults */ cd->info.cd_primitive = CD_INFO_ACK; cd->info.cd_state = CD_UNATTACHED; cd->info.cd_max_sdu = 256; cd->info.cd_min_sdu = 3; cd->info.cd_class = CD_HDLC; cd->info.cd_duplex = CD_FULLDUPLEX; cd->info.cd_output_style = CD_UNACKEDOUTPUT; cd->info.cd_features = (CD_CANREAD | CD_AUTOALLOW); cd->info.cd_addr_length = 0; cd->info.cd_ppa_style = CD_STYLE2; fixme(("allocate option defaults")); } else ptrace(("%s: ERROR: failed to allocate dl link structure\n", LAPD_DRV_NAME)); return (cd); } STATIC void dl_free_link(struct cd *cd) { int flags = 0; ensure(cd, return); printd(("%s: %p: free cd mux = %lu\N", LAPD_DRV_NAME, cd, cd->u.mux.index)); lis_spin_lock_irqsave(&cd->lock, &flags); { int i; struct dl *dl; ss7_unbufcall((struct str *) cd); flushq(cd->oq, FLUSHALL); flushq(cd->iq, FLUSHALL); /* empty wait list */ while ((dl = cd->wait.list)) dl_wait_unlink(dl); /* empty teim list */ while ((dl = cd->teim.list)) dl_teim_unlink(dl); /* empty conn hash */ for (i = 0; i < DL_CONN_SIZE; i++) while ((dl = cd->conn.hash[i])) dl_conn_unlink(dl); /* empty list hash */ for (i = 0; i < DL_LIST_SIZE; i++) while ((dl = cd->list.hash[i])) dl_list_unlink(dl); /* empty bind hash */ for (i = 0; i < DL_BIND_SIZE; i++) while ((dl = cd->bind.hash[i])) dl_bind_unlink(dl); /* empty connection management list */ if ((dl = cd->mgmt)) { dl->state = LAPD_TEI_UNASSIGNED; dl_set_state(dl, -1); dl_mgmt_unlink(dl); } /* empty attach list */ while ((dl = cd->dl.list)) dl_atta_unlink(dl); /* remove from master list */ cd_mast_unlink(cd); /* remove from queues */ cd_put(xchg(&cd->oq->q_ptr, NULL)); cd_put(xchg(&cd->iq->q_ptr, NULL)); cd->oq = NULL; cd->iq = NULL; } lis_spin_unlock_irqrestore(&cd->lock, &flags); cd_put(cd); /* final put */ return; } /* * ========================================================================= * * EVENTS * * ========================================================================= */ /* * ------------------------------------------------------------------------- * * Timeouts * * ------------------------------------------------------------------------- */ /* * T200 Timeout * ----------------------------------- * 5.5.1.3 ... If timer T200 expires before the UA or DM response with the F * bit set to 1 is received, the data link layer entity shall: restransmit * the SABME command; restart timer T200; and increment the retransmission * counter. After retransmission of theh SABME command N200 times, the data * link layer entity shall indicate this to layer 3 and the connection * management entity by means of the DL-RELEASE indication and MDL-ERROR * indication primitives, respectively, and enter the TEI-assigned state, * after discarding all outstanding DL-DATA request primitives and all I * frames in queue. The valud of N200 is defined in 5.9.2. */ STATIC int dl_t200_timeout(struct dl *dl) { int err; switch (dl->state) { case LAPD_WAIT_ESTABLISH: switch (dl_get_state(dl)) { case DL_OUTCON_PENDING: ensure(dl->cd.cd, return (-EIO)); if (dl->rc < dl->cd.cd->config.n200) { if ((err = send_SABME_cmd(NULL, dl->cd.cd, dl->dlc.dl_sap, dl->dlc.dl_tei, 1)) < 0) return (err); dl_timer_start(dl, t200); dl->rc++; return (QR_DONE); } return dl_disconnect_ind(NULL, dl, DL_PROVIDER, DL_CONREJ_DEST_UNKNOWN, NULL); default: swerr(); break; } break; case LAPD_WAIT_RELEASE: switch (dl_get_state(dl)) { case DL_DISCON11_PENDING: case DL_DISCON12_PENDING: case DL_DISCON13_PENDING: ensure(dl->cd.cd, return (-EIO)); if (dl->rc < dl->cd.cd->config.n200) { if ((err = send_DISC_cmd(NULL, dl->cd.cd, dl->dlc.dl_sap, dl->dlc.dl_tei, 1)) < 0) return (err); dl_timer_start(dl, t200); dl->rc++; return (QR_DONE); } dl->state = LAPD_TEI_ASSIGNED; return (QR_DONE); /* LAPD actually sends a DL-RELEASE confirm */ default: swerr(); break; } break; } fixme(("Write this function\n")); return (-EIO); } /* * T201 Timeout * ----------------------------------- */ STATIC int dl_t201_timeout(struct dl *dl) { fixme(("Write this function\n")); return (-EIO); } STATIC int cd_t201_timeout(struct cd *cd) { fixme(("Write this function\n")); return (-EIO); } /* * T202 Timeout * ----------------------------------- */ STATIC int dl_t202_timeout(struct dl *dl) { fixme(("Write this function\n")); return (-EIO); } STATIC int cd_t202_timeout(struct cd *cd) { fixme(("Write this function\n")); return (-EIO); } /* * T203 Timeout * ----------------------------------- */ STATIC int dl_t203_timeout(struct dl *dl) { fixme(("Write this function\n")); return (-EIO); } /* * ------------------------------------------------------------------------- * * Primitive recv from upstream * * ------------------------------------------------------------------------- */ /* * DL_DATA_REQ (M_DATA) * ----------------------------------- */ STATIC int dl_data_req(queue_t *q, mblk_t *mp) { struct dl *dl = DL_PRIV(q); size_t dlen = msgdsize(mp); if (dlen < dl->info.dl_min_sdu || dlen > dl->info.dl_max_sdu) goto eproto; switch (dl_get_state(dl)) { case DL_IDLE: goto discard; case DL_DATAXFER: case DL_USER_RESET_PENDING: case DL_PROV_RESET_PENDING: case DL_RESET_RES_PENDING: bufq_queue(&dl->sndq, mp); return (QR_ABSORBED); default: goto eproto; } eproto: return m_error(q, dl, EPROTO, EPROTO); discard: return (QR_DONE); } /* * DL_INFO_REQ 0x00 * ----------------------------------- */ STATIC int dl_info_req(queue_t *q, mblk_t *mp) { struct dl *dl = DL_PRIV(q); dl_info_req_t *p = (typeof(p)) mp->b_rptr; if (mp->b_wptr > mp->b_rptr + sizeof(*p)) goto badprim; return dl_info_ack(q, dl); badprim: return dl_error_ack(q, dl, p->dl_primitive, DL_SYSERR, EMSGSIZE); } /* * DL_ATTACH_REQ 0x0b * ----------------------------------- * This DL multiplexor is meant to work with the OpenSS7 CD multiplexor. Each CD multiplexor * (STYLE2) stream linked below the DL module has access to all PPAs on the system. Each CD * STYLE1 stream has a PPA assigned when the device is linked. When an attach is requested, * we first look for a matching STYLE1 or STYLE2 (with assigned CDI PPA) lower stream. If one * is found then we attached to the found stream. LAPD DL PPAs are identifier values * associated with a linked lower CD stream. */ STATIC int dl_attach_req(queue_t *q, mblk_t *mp) { struct dl *dl = DL_PRIV(q); struct cd *cd; dl_attach_req_t *p = (typeof(p)) mp->b_rptr; if (mp->b_wptr > mp->b_rptr + sizeof(*p)) goto badprim; if (dl_get_state(dl) != DL_UNATTACHED) goto outstate; if (!(cd = cd_lookup(p->dl_ppa)) || cd->info.cd_class == CD_NODEV) goto badppa; if (dl->cd.cd) goto eio; dl_set_state(dl, DL_ATTACH_PENDING); if (cd_get_state(cd) != CD_UNATTACHED) return dl_ok_ack(q, dl, p->dl_primitive, cd, NULL, NULL); /* when the first DL attaches we will attach the comm device if it is in the unattached state (implicitly a style 2 comm device) */ if (!cd->wait.list) /* nobody attaching yet */ return cd_attach_req(q, cd, dl); dl_wait_link(dl, cd); /* add to wait list */ return (QR_DONE); eio: return m_error(q, dl, EIO, EIO); badppa: return dl_error_ack(q, dl, p->dl_primitive, DL_BADPPA, ENXIO); outstate: return dl_error_ack(q, dl, p->dl_primitive, DL_OUTSTATE, EPROTO); badprim: return dl_error_ack(q, dl, p->dl_primitive, DL_SYSERR, EMSGSIZE); } /* * DL_DETACH_REQ 0x0c * ----------------------------------- */ STATIC int dl_detach_req(queue_t *q, mblk_t *mp) { struct dl *dl = DL_PRIV(q); struct cd *cd; dl_detach_req_t *p = (typeof(p)) mp->b_rptr; if (mp->b_wptr > mp->b_rptr + sizeof(*p)) goto badprim; if (dl_get_state(dl) != DL_UNBOUND) goto outstate; if (!(cd = dl->cd.cd)) goto eio; dl_set_state(dl, DL_DETACH_PENDING); if (cd->dl.numb > 1 || cd->info.cd_ppa_style == CD_STYLE1) /* we are not the last */ return dl_ok_ack(q, dl, p->dl_primitive, cd, NULL, NULL); return cd_detach_req(q, cd, dl); eio: return m_error(q, dl, EIO, EIO); outstate: return dl_error_ack(q, dl, p->dl_primitive, DL_OUTSTATE, EPROTO); badprim: return dl_error_ack(q, dl, p->dl_primitive, DL_SYSERR, EMSGSIZE); } /* * DL_BIND_REQ 0x01 * ----------------------------------- */ STATIC int dl_bind_req(queue_t *q, mblk_t *mp) { struct dl *dl = DL_PRIV(q), *d2; struct cd *cd = dl->cd.cd; int hash; dl_bind_req_t *p = (typeof(p)) mp->b_rptr; if (mp->b_wptr > mp->b_rptr + sizeof(*p)) goto badprim; if (dl_get_state(dl) != DL_UNBOUND) goto outstate; dl_set_state(dl, DL_BIND_PENDING); if (!cd || dl->bind.cd) goto eio; switch (cd_get_state(cd)) { case CD_UNUSABLE: case CD_UNATTACHED: goto initfailed; } switch (p->dl_service_mode) { case DL_CODLS: case DL_CLDLS: break; case DL_ACLDLS: goto unsupported; } if (p->dl_conn_mgmt) { if (cd->mgmt) goto bound; if (p->dl_service_mode != DL_CODLS) goto unsupported; } else { switch (dl->info.dl_mac_type) { case DL_ISDN: fixme(("check SAPI\n")); break; goto badaddr; case DL_X25: fixme(("check SAPI\n")); break; goto badaddr; case DL_FRAME: fixme(("check SAPI\n")); break; goto badaddr; #ifdef DL_MTP2 case DL_MTP2: fixme(("check SAPI\n")); break; goto badaddr; #endif #ifdef DL_GSMA case DL_GSMA: fixme(("check SAPI\n")); break; goto badaddr; #endif #ifdef DL_V5 case DL_V5: fixme(("check SAPI\n")); break; goto badaddr; #endif #ifdef DL_GR303 case DL_GR303: fixme(("check SAPI\n")); break; goto badaddr; #endif default: goto enodev; } hash = ((dl->dlc.dl_sap + dl->dlc.dl_tei) & DL_BIND_HASHMASK); for (d2 = cd->bind.hash[hash]; d2; d2 = d2->bind.next) if (d2->dlc.dl_sap == p->dl_sap && d2->dlc.dl_tei == -1U) goto bound; } dl->dlc.dl_sap = p->dl_sap; dl->dlc.dl_tei = -1; if (p->dl_xidtest_flg & DL_AUTO_XID) dl->flags |= DLF_AUTO_XID; if (p->dl_xidtest_flg & DL_AUTO_TEST) dl->flags |= DLF_AUTO_TEST; dl->info.dl_service_mode = p->dl_service_mode; if (cd_get_state(cd) != CD_DISABLED) { dl->info.dl_min_sdu = cd->info.cd_min_sdu; dl->info.dl_max_sdu = cd->info.cd_max_sdu; return dl_bind_ack(q, dl, cd); } if (!cd->wait.list) /* nobody binding yet */ return cd_enable_req(q, cd, dl); dl_wait_link(dl, cd); /* add to wait list */ return (QR_DONE); badaddr: return dl_error_ack(q, dl, p->dl_primitive, DL_BADADDR, ENXIO); bound: return dl_error_ack(q, dl, p->dl_primitive, DL_BOUND, EADDRINUSE); unsupported: return dl_error_ack(q, dl, p->dl_primitive, DL_UNSUPPORTED, EINVAL); initfailed: return dl_error_ack(q, dl, p->dl_primitive, DL_INITFAILED, EIO); eio: return m_error(q, dl, EIO, EIO); enodev: return dl_error_ack(q, dl, p->dl_primitive, DL_SYSERR, ENODEV); outstate: return dl_error_ack(q, dl, p->dl_primitive, DL_OUTSTATE, EPROTO); badprim: return dl_error_ack(q, dl, p->dl_primitive, DL_SYSERR, EMSGSIZE); } /* * DL_UNBIND_REQ 0x02 * ----------------------------------- */ STATIC int dl_unbind_req(queue_t *q, mblk_t *mp) { struct dl *dl = DL_PRIV(q); struct cd *cd; dl_unbind_req_t *p = (typeof(p)) mp->b_rptr; if (mp->b_wptr > mp->b_rptr + sizeof(*p)) goto badprim; if (dl_get_state(dl) != DL_IDLE) goto outstate; if (!(cd = dl->cd.cd)) goto eio; dl_set_state(dl, DL_UNBIND_PENDING); if (cd->bind.numb > 1) /* we are not the last bound stream */ return dl_ok_ack(q, dl, p->dl_primitive, cd, NULL, NULL); return cd_disable_req(q, cd, dl, CD_WAIT); eio: return m_error(q, dl, EIO, EIO); outstate: return dl_error_ack(q, dl, p->dl_primitive, DL_OUTSTATE, EPROTO); badprim: return dl_error_ack(q, dl, p->dl_primitive, DL_SYSERR, EMSGSIZE); } /* * DL_SUBS_BIND_REQ 0x1b * ----------------------------------- */ STATIC int dl_subs_bind_req(queue_t *q, mblk_t *mp) { struct dl *dl = DL_PRIV(q); dl_subs_bind_req_t *p = (typeof(p)) mp->b_rptr; uchar tei; if (mp->b_wptr > mp->b_rptr + sizeof(*p)) goto badprim; if (dl_get_state(dl) != DL_IDLE) goto outstate; if (p->dl_subs_sap_length == 0) goto noaddr; if (p->dl_subs_sap_length < sizeof(dl->dlc.dl_tei)) goto badaddr; if (mp->b_wptr > mp->b_rptr + p->dl_subs_sap_offset + p->dl_subs_sap_length) goto badaddr; switch (p->dl_subs_bind_class) { case DL_PEER_BIND: goto unsupported; case DL_HIERARCHICAL_BIND: break; default: goto unsupported; } if (dl->dlc.dl_tei != -1U) goto toomany; bcopy(mp->b_rptr + p->dl_subs_sap_offset, &tei, sizeof(tei)); if (dl->conind) { struct dl *d2; int slot = ((dl->dlc.dl_sap + tei) & DL_BIND_HASHMASK); for (d2 = dl->cd.cd->list.hash[slot]; d2; d2 = d2->list.next) if (d2->dlc.dl_sap == dl->dlc.dl_sap && d2->dlc.dl_tei == tei) goto bound; } dl_set_state(dl, DL_SUBS_BIND_PND); return dl_subs_bind_ack(q, dl, tei); #if 0 access: return dl_error_ack(q, dl, p->dl_primitive, DL_ACCESS, EPERM); #endif bound: return dl_error_ack(q, dl, p->dl_primitive, DL_BOUND, EINVAL); noaddr: return dl_error_ack(q, dl, p->dl_primitive, DL_NOADDR, EINVAL); badaddr: return dl_error_ack(q, dl, p->dl_primitive, DL_BADADDR, EINVAL); outstate: return dl_error_ack(q, dl, p->dl_primitive, DL_OUTSTATE, EPROTO); toomany: return dl_error_ack(q, dl, p->dl_primitive, DL_TOOMANY, ENXIO); unsupported: return dl_error_ack(q, dl, p->dl_primitive, DL_UNSUPPORTED, EOPNOTSUPP); badprim: return dl_error_ack(q, dl, p->dl_primitive, DL_SYSERR, EMSGSIZE); } /* * DL_SUBS_UNBIND_REQ 0x15 * ----------------------------------- */ STATIC int dl_subs_unbind_req(queue_t *q, mblk_t *mp) { struct dl *dl = DL_PRIV(q); dl_subs_unbind_req_t *p = (typeof(p)) mp->b_rptr; if (mp->b_wptr > mp->b_rptr + sizeof(*p)) goto badprim; dl_set_state(dl, DL_SUBS_UNBIND_PND); goto notsupported; return dl_ok_ack(q, dl, p->dl_primitive, dl->cd.cd, NULL, NULL); notsupported: return dl_error_ack(q, dl, p->dl_primitive, DL_NOTSUPPORTED, EOPNOTSUPP); badprim: return dl_error_ack(q, dl, p->dl_primitive, DL_SYSERR, EMSGSIZE); } /* * DL_ENABMULTI_REQ 0x1d * ----------------------------------- */ STATIC int dl_enabmulti_req(queue_t *q, mblk_t *mp) { struct dl *dl = DL_PRIV(q); dl_enabmulti_req_t *p = (typeof(p)) mp->b_rptr; if (mp->b_wptr > mp->b_rptr + sizeof(*p)) goto badprim; goto notsupported; return dl_ok_ack(q, dl, p->dl_primitive, dl->cd.cd, NULL, NULL); notsupported: return dl_error_ack(q, dl, p->dl_primitive, DL_NOTSUPPORTED, EOPNOTSUPP); badprim: return dl_error_ack(q, dl, p->dl_primitive, DL_SYSERR, EMSGSIZE); } /* * DL_DISABMULTI_REQ 0x1e * ----------------------------------- */ STATIC int dl_disabmulti_req(queue_t *q, mblk_t *mp) { struct dl *dl = DL_PRIV(q); dl_disabmulti_req_t *p = (typeof(p)) mp->b_rptr; if (mp->b_wptr > mp->b_rptr + sizeof(*p)) goto badprim; goto notsupported; return dl_ok_ack(q, dl, p->dl_primitive, dl->cd.cd, NULL, NULL); notsupported: return dl_error_ack(q, dl, p->dl_primitive, DL_NOTSUPPORTED, EOPNOTSUPP); badprim: return dl_error_ack(q, dl, p->dl_primitive, DL_SYSERR, EMSGSIZE); } /* * DL_PROMISCON_REQ 0x1f * ----------------------------------- */ STATIC int dl_promiscon_req(queue_t *q, mblk_t *mp) { struct dl *dl = DL_PRIV(q); dl_promiscon_req_t *p = (typeof(p)) mp->b_rptr; if (mp->b_wptr > mp->b_rptr + sizeof(*p)) goto badprim; goto notsupported; return dl_ok_ack(q, dl, p->dl_primitive, dl->cd.cd, NULL, NULL); notsupported: return dl_error_ack(q, dl, p->dl_primitive, DL_NOTSUPPORTED, EOPNOTSUPP); badprim: return dl_error_ack(q, dl, p->dl_primitive, DL_SYSERR, EMSGSIZE); } /* * DL_PROMISCOFF_REQ 0x20 * ----------------------------------- */ STATIC int dl_promiscoff_req(queue_t *q, mblk_t *mp) { struct dl *dl = DL_PRIV(q); dl_promiscoff_req_t *p = (typeof(p)) mp->b_rptr; if (mp->b_wptr > mp->b_rptr + sizeof(*p)) goto badprim; goto notsupported; return dl_ok_ack(q, dl, p->dl_primitive, dl->cd.cd, NULL, NULL); notsupported: return dl_error_ack(q, dl, p->dl_primitive, DL_NOTSUPPORTED, EOPNOTSUPP); badprim: return dl_error_ack(q, dl, p->dl_primitive, DL_SYSERR, EMSGSIZE); } /* * DL_CONNECT_REQ 0x0d * ----------------------------------- * If the DLSAP destination address is not provided, the TEI used in * SUBS_BIND will be used. If there was no SUBS_BIND, a TEI will be aquired * from the peer and assigned. If the TEI is provided and it is 127 (the * broadcast address), the stream must be opened with root credentials. */ STATIC int dl_connect_req(queue_t *q, mblk_t *mp) { struct dl *dl = DL_PRIV(q); dl_connect_req_t *p = (typeof(p)) mp->b_rptr; struct cd *cd = dl->cd.cd; uchar tei; int err; if (dl_get_state(dl) != DL_IDLE) goto outstate; if (mp->b_wptr > mp->b_rptr + sizeof(*p)) goto badprim; if (p->dl_qos_length) goto badqostype; ensure(cd, goto eio); if (p->dl_dest_addr_length) { if (mp->b_wptr > mp->b_rptr + p->dl_dest_addr_length + p->dl_dest_addr_offset) goto badaddr; if (p->dl_dest_addr_length < sizeof(dl->dlc.dl_tei)) goto badaddr; if (dl->dlc.dl_tei != -1U) goto badaddr; /* a tei is already assigned */ bcopy(mp->b_rptr + p->dl_dest_addr_offset, &tei, sizeof(tei)); if (tei > 127) goto badaddr; if (tei == 127 && dl->cred.cr_uid != 0) goto access; } else if ((tei = dl->dlc.dl_tei) == -1U) { todo(("request assignment of a TEI\n")); goto badaddr; /* for now */ } switch (dl->state) { case LAPD_TEI_UNASSIGNED: dl->ref = (jiffies ^ (ulong) & q); dl->ref ^= dl->ref >> 16; dl->ref &= 0x0000ffff; /* initiate TEI assignment */ if ((err = send_TEI_i_req(q, cd, dl->ref, TEI_ANY))) return (err); dl_teim_link(dl, cd); dl_timer_start(dl, t202); dl->state = LAPD_WAIT_TEI_ESTABLISH; dl_set_state(dl, DL_OUTCON_PENDING); break; case LAPD_WAIT_TEI_ASSIGN: /* wait for assignment to complete */ dl->state = LAPD_WAIT_TEI_ESTABLISH; dl_set_state(dl, DL_OUTCON_PENDING); break; case LAPD_TEI_ASSIGNED: if ((err = send_SABME_cmd(q, cd, dl->dlc.dl_sap, tei, 1))) return (err); dl->rc = 0; dl_timer_start(dl, t200); dl_conn_link(dl, cd); dl->state = LAPD_WAIT_ESTABLISH; dl_set_state(dl, DL_OUTCON_PENDING); break; case LAPD_WAIT_REESTABLISH: /* DISC, I QUEUE */ dl->state = LAPD_WAIT_ESTABLISH; dl_set_state(dl, DL_OUTCON_PENDING); break; default: swerr(); goto eio; } return (QR_DONE); access: return dl_error_ack(q, dl, p->dl_primitive, DL_ACCESS, EPERM); badqostype: return dl_error_ack(q, dl, p->dl_primitive, DL_BADQOSTYPE, EINVAL); outstate: return dl_error_ack(q, dl, p->dl_primitive, DL_OUTSTATE, EPROTO); badaddr: return dl_error_ack(q, dl, p->dl_primitive, DL_BADADDR, EINVAL); badprim: return dl_error_ack(q, dl, p->dl_primitive, DL_SYSERR, EMSGSIZE); eio: return m_error(q, dl, EIO, EIO); } /* * DL_CONNECT_RES 0x0f * ----------------------------------- */ STATIC int dl_connect_res(queue_t *q, mblk_t *mp) { struct dl *dl = DL_PRIV(q); dl_connect_res_t *p = (typeof(p)) mp->b_rptr; struct dl *ap = dl; mblk_t *cp; if (mp->b_wptr > mp->b_rptr + sizeof(*p)) goto badprim; if (dl_get_state(dl) != DL_INCON_PENDING) goto outstate; if (p->dl_qos_length) goto badqostype; if (p->dl_resp_token && !(ap = dl_lookup(p->dl_resp_token))) goto badtoken; if (ap == dl) { if (dl->conq.q_msgs > 1) goto pending; } else { if ((1 << dl_get_state(ap)) & ~(DLF_UNATTACHED | DLF_UNBOUND | DLF_IDLE)) goto badtoken; if (ap->conind) goto badtoken; } if (!p->dl_correlation) goto badcorr; for (cp = dl->conq.q_head; cp && (p->dl_correlation != (typeof(p->dl_correlation)) cp); cp = cp->b_next) ; if (!cp) goto badcorr; dl_set_state(dl, DL_CONN_RES_PENDING); return dl_ok_ack(q, dl, p->dl_primitive, dl->cd.cd, ap, cp); badcorr: return dl_error_ack(q, dl, p->dl_primitive, DL_BADCORR, EINVAL); pending: return dl_error_ack(q, dl, p->dl_primitive, DL_PENDING, EINVAL); badtoken: return dl_error_ack(q, dl, p->dl_primitive, DL_BADTOKEN, EINVAL); badqostype: return dl_error_ack(q, dl, p->dl_primitive, DL_BADQOSTYPE, EINVAL); outstate: return dl_error_ack(q, dl, p->dl_primitive, DL_OUTSTATE, EPROTO); badprim: return dl_error_ack(q, dl, p->dl_primitive, DL_SYSERR, EMSGSIZE); } /* * DL_TOKEN_REQ 0x11 * ----------------------------------- */ STATIC int dl_token_req(queue_t *q, mblk_t *mp) { struct dl *dl = DL_PRIV(q); dl_token_req_t *p = (typeof(p)) mp->b_rptr; if (mp->b_wptr > mp->b_rptr + sizeof(*p)) goto badprim; badprim: return dl_error_ack(q, dl, p->dl_primitive, DL_SYSERR, EMSGSIZE); } /* * DL_DISCONNECT_REQ 0x13 * ----------------------------------- */ STATIC int dl_disconnect_req(queue_t *q, mblk_t *mp) { struct dl *dl = DL_PRIV(q); struct cd *cd = dl->cd.cd; dl_disconnect_req_t *p = (typeof(p)) mp->b_rptr; mblk_t *cp = NULL; if (mp->b_wptr > mp->b_rptr + sizeof(*p)) goto badprim; switch (dl_get_state(dl)) { case DL_OUTCON_PENDING: dl_set_state(dl, DL_DISCON8_PENDING); break; case DL_INCON_PENDING: if (!p->dl_correlation) goto badcorr; for (cp = dl->conq.q_head; cp && (p->dl_correlation != (typeof(p->dl_correlation)) cp); cp = cp->b_next) ; if (!cp) goto badcorr; dl_set_state(dl, DL_DISCON9_PENDING); break; case DL_DATAXFER: dl_set_state(dl, DL_DISCON11_PENDING); break; case DL_USER_RESET_PENDING: dl_set_state(dl, DL_DISCON12_PENDING); break; case DL_PROV_RESET_PENDING: dl_set_state(dl, DL_DISCON13_PENDING); break; case DL_IDLE: if (dl->state == LAPD_TEI_ASSIGNED) break; /* fall through */ default: goto outstate; } return dl_ok_ack(q, dl, p->dl_primitive, cd, NULL, cp); badcorr: return dl_error_ack(q, dl, p->dl_primitive, DL_BADCORR, EINVAL); outstate: return dl_error_ack(q, dl, p->dl_primitive, DL_OUTSTATE, EPROTO); badprim: return dl_error_ack(q, dl, p->dl_primitive, DL_SYSERR, EMSGSIZE); } /* * DL_RESET_REQ 0x17 * ----------------------------------- */ STATIC int dl_reset_req(queue_t *q, mblk_t *mp) { struct dl *dl = DL_PRIV(q); dl_reset_req_t *p = (typeof(p)) mp->b_rptr; if (mp->b_wptr > mp->b_rptr + sizeof(*p)) goto badprim; badprim: return dl_error_ack(q, dl, p->dl_primitive, DL_SYSERR, EMSGSIZE); } /* * DL_RESET_RES 0x19 * ----------------------------------- */ STATIC int dl_reset_res(queue_t *q, mblk_t *mp) { struct dl *dl = DL_PRIV(q); dl_reset_res_t *p = (typeof(p)) mp->b_rptr; if (mp->b_wptr > mp->b_rptr + sizeof(*p)) goto badprim; dl_set_state(dl, DL_RESET_RES_PENDING); return dl_ok_ack(q, dl, p->dl_primitive, dl->cd.cd, NULL, NULL); badprim: return dl_error_ack(q, dl, p->dl_primitive, DL_SYSERR, EMSGSIZE); } /* * DL_UNITDATA_REQ 0x07 * ----------------------------------- */ STATIC int dl_unitdata_req(queue_t *q, mblk_t *mp) { struct dl *dl = DL_PRIV(q); dl_unitdata_req_t *p = (typeof(p)) mp->b_rptr; if (mp->b_wptr > mp->b_rptr + sizeof(*p)) goto badprim; switch (dl_get_state(dl)) { case DL_IDLE: switch (dl->state) { case LAPD_TEI_UNASSIGNED: /* initiate assignment procedures */ dl->state = LAPD_WAIT_TEI_ASSIGN; return (-EAGAIN); case LAPD_WAIT_TEI_ASSIGN: /* wait more for assignment */ return (-EAGAIN); case LAPD_WAIT_TEI_ESTABLISH: case LAPD_TEI_ASSIGNED: case LAPD_WAIT_ESTABLISH: case LAPD_WAIT_REESTABLISH: case LAPD_PEND_RELEASE: case LAPD_WAIT_RELEASE: case LAPD_ESTABLISHED: case LAPD_TIME_RECOVERY: default: } return (QR_DONE); default: goto outstate; } outstate: return dl_error_ack(q, dl, p->dl_primitive, DL_OUTSTATE, EPROTO); badprim: return dl_error_ack(q, dl, p->dl_primitive, DL_SYSERR, EMSGSIZE); } /* * DL_UDQOS_REQ 0x0a * ----------------------------------- */ STATIC int dl_udqos_req(queue_t *q, mblk_t *mp) { struct dl *dl = DL_PRIV(q); dl_udqos_req_t *p = (typeof(p)) mp->b_rptr; if (mp->b_wptr > mp->b_rptr + sizeof(*p)) goto badprim; badprim: return dl_error_ack(q, dl, p->dl_primitive, DL_SYSERR, EMSGSIZE); } /* * DL_TEST_REQ 0x2d * ----------------------------------- */ STATIC int dl_test_req(queue_t *q, mblk_t *mp) { struct dl *dl = DL_PRIV(q); dl_test_req_t *p = (typeof(p)) mp->b_rptr; if (mp->b_wptr > mp->b_rptr + sizeof(*p)) goto badprim; badprim: return dl_error_ack(q, dl, p->dl_primitive, DL_SYSERR, EMSGSIZE); } /* * DL_TEST_RES 0x2f * ----------------------------------- */ STATIC int dl_test_res(queue_t *q, mblk_t *mp) { struct dl *dl = DL_PRIV(q); dl_test_res_t *p = (typeof(p)) mp->b_rptr; if (mp->b_wptr > mp->b_rptr + sizeof(*p)) goto badprim; badprim: return dl_error_ack(q, dl, p->dl_primitive, DL_SYSERR, EMSGSIZE); } /* * DL_XID_REQ 0x29 * ----------------------------------- */ STATIC int dl_xid_req(queue_t *q, mblk_t *mp) { struct dl *dl = DL_PRIV(q); dl_xid_req_t *p = (typeof(p)) mp->b_rptr; if (mp->b_wptr > mp->b_rptr + sizeof(*p)) goto badprim; badprim: return dl_error_ack(q, dl, p->dl_primitive, DL_SYSERR, EMSGSIZE); } /* * DL_XID_RES 0x2b * ----------------------------------- */ STATIC int dl_xid_res(queue_t *q, mblk_t *mp) { struct dl *dl = DL_PRIV(q); dl_xid_res_t *p = (typeof(p)) mp->b_rptr; if (mp->b_wptr > mp->b_rptr + sizeof(*p)) goto badprim; badprim: return dl_error_ack(q, dl, p->dl_primitive, DL_SYSERR, EMSGSIZE); } /* * DL_DATA_ACK_REQ 0x21 * ----------------------------------- */ STATIC int dl_data_ack_req(queue_t *q, mblk_t *mp) { struct dl *dl = DL_PRIV(q); dl_data_ack_req_t *p = (typeof(p)) mp->b_rptr; if (mp->b_wptr > mp->b_rptr + sizeof(*p)) goto badprim; badprim: return dl_error_ack(q, dl, p->dl_primitive, DL_SYSERR, EMSGSIZE); } /* * DL_REPLY_REQ 0x24 * ----------------------------------- */ STATIC int dl_reply_req(queue_t *q, mblk_t *mp) { struct dl *dl = DL_PRIV(q); dl_reply_req_t *p = (typeof(p)) mp->b_rptr; if (mp->b_wptr > mp->b_rptr + sizeof(*p)) goto badprim; badprim: return dl_error_ack(q, dl, p->dl_primitive, DL_SYSERR, EMSGSIZE); } /* * DL_REPLY_UPDATE_REQ 0x27 * ----------------------------------- */ STATIC int dl_reply_update_req(queue_t *q, mblk_t *mp) { struct dl *dl = DL_PRIV(q); dl_reply_update_req_t *p = (typeof(p)) mp->b_rptr; if (mp->b_wptr > mp->b_rptr + sizeof(*p)) goto badprim; badprim: return dl_error_ack(q, dl, p->dl_primitive, DL_SYSERR, EMSGSIZE); } /* * DL_PHYS_ADDR_REQ 0x31 * ----------------------------------- */ STATIC int dl_phys_addr_req(queue_t *q, mblk_t *mp) { struct dl *dl = DL_PRIV(q); dl_phys_addr_req_t *p = (typeof(p)) mp->b_rptr; if (mp->b_wptr > mp->b_rptr + sizeof(*p)) goto badprim; badprim: return dl_error_ack(q, dl, p->dl_primitive, DL_SYSERR, EMSGSIZE); } /* * DL_SET_PHYS_ADDR_REQ 0x33 * ----------------------------------- */ STATIC int dl_set_phys_addr_req(queue_t *q, mblk_t *mp) { struct dl *dl = DL_PRIV(q); dl_set_phys_addr_req_t *p = (typeof(p)) mp->b_rptr; if (mp->b_wptr > mp->b_rptr + sizeof(*p)) goto badprim; badprim: return dl_error_ack(q, dl, p->dl_primitive, DL_SYSERR, EMSGSIZE); } /* * DL_GET_STATISTICS_REQ 0x34 * ----------------------------------- */ STATIC int dl_get_statistics_req(queue_t *q, mblk_t *mp) { struct dl *dl = DL_PRIV(q); dl_get_statistics_req_t *p = (typeof(p)) mp->b_rptr; if (mp->b_wptr > mp->b_rptr + sizeof(*p)) goto badprim; badprim: return dl_error_ack(q, dl, p->dl_primitive, DL_SYSERR, EMSGSIZE); } /* * ------------------------------------------------------------------------- * * Primitive recv from downstream * * ------------------------------------------------------------------------- */ /* * CD_DATA_IND (M_DATA) * ------------------------------------------------------------------------- */ STATIC int cd_data_ind(queue_t *q, mblk_t *mp) { swerr(); return (-EFAULT); } /* * CD_INFO_ACK 0x01 - Information acknowledgement * ------------------------------------------------------------------------- */ STATIC int cd_info_ack(queue_t *q, mblk_t *mp) { struct cd *cd = CD_PRIV(q); cd_info_ack_t *p = (typeof(p)) mp->b_rptr; struct dl *dl; int err; if (mp->b_wptr > mp->b_rptr + sizeof(*p)) goto eio; cd->info = *p; /* just adopt the state */ /* this is just a educated guess really, but here it goes */ switch (cd_set_state(cd, p->cd_state)) { case CD_UNATTACHED: case CD_DISABLED: /* initial info request on link */ break; case CD_ENABLED: case CD_INPUT_ALLOWED: case CD_READ_ACTIVE: case CD_OUTPUT_ACTIVE: for (dl = cd->wait.list; dl; dl = dl->wait.next) if (dl_get_state(dl) == DL_BIND_PENDING) if ((err = dl_bind_ack(q, dl, cd))) return (err); return (QR_DONE); } return (QR_DONE); eio: swerr(); return m_error_all(q, cd, EIO, EIO); } /* * CD_OK_ACK 0x06 - Success acknowledgement * ------------------------------------------------------------------------- */ STATIC int cd_ok_ack(queue_t *q, mblk_t *mp) { struct cd *cd = CD_PRIV(q); cd_ok_ack_t *p = (typeof(p)) mp->b_rptr; struct dl *dl; int err; if (mp->b_wptr > mp->b_rptr + sizeof(*p)) goto eio; cd_set_state(cd, p->cd_state); switch (p->cd_correct_primitive) { case CD_ATTACH_REQ: for (dl = cd->wait.list; dl; dl = dl->wait.next) if (dl_get_state(dl) == DL_ATTACH_PENDING) if ((err = dl_ok_ack(q, dl, DL_ATTACH_REQ, cd, NULL, NULL))) return (err); return (QR_DONE); case CD_DETACH_REQ: for (dl = cd->wait.list; dl; dl = dl->wait.next) if (dl_get_state(dl) == DL_DETACH_PENDING) if ((err = dl_ok_ack(q, dl, DL_DETACH_REQ, cd, NULL, NULL))) return (err); return (QR_DONE); } return (QR_DONE); eio: swerr(); return m_error_all(q, cd, EIO, EIO); } /* * CD_ERROR_ACK 0x07 - Error acknowledgement * ------------------------------------------------------------------------- */ STATIC int cd_error_ack(queue_t *q, mblk_t *mp) { struct cd *cd = CD_PRIV(q); cd_error_ack_t *p = (typeof(p)) mp->b_rptr; struct dl *dl; int err; if (mp->b_wptr > mp->b_rptr + sizeof(*p)) goto eio; switch (p->cd_error_primitive) { case CD_ATTACH_REQ: cd_set_state(cd, p->cd_state); for (dl = cd->wait.list; dl; dl = dl->wait.next) if (dl_get_state(dl) == DL_ATTACH_PENDING) if ((err = dl_error_ack(q, dl, DL_ATTACH_REQ, DL_SYSERR, EIO))) return (err); break; case CD_DETACH_REQ: cd_set_state(cd, p->cd_state); for (dl = cd->wait.list; dl; dl = dl->wait.next) if (dl_get_state(dl) == DL_DETACH_PENDING) if ((err = dl_error_ack(q, dl, DL_DETACH_REQ, DL_SYSERR, EIO))) return (err); break; case CD_ENABLE_REQ: cd_set_state(cd, p->cd_state); for (dl = cd->wait.list; dl; dl = dl->wait.next) if (dl_get_state(dl) == DL_BIND_PENDING) if ((err = dl_error_ack(q, dl, DL_BIND_REQ, DL_INITFAILED, EIO))) return (err); break; case CD_DISABLE_REQ: cd_set_state(cd, p->cd_state); for (dl = cd->wait.list; dl; dl = dl->wait.next) if (dl_get_state(dl) == DL_UNBIND_PENDING) if ((err = dl_error_ack(q, dl, DL_UNBIND_REQ, DL_SYSERR, EIO))) return (err); break; } return (QR_DONE); eio: swerr(); return m_error_all(q, cd, EIO, EIO); } /* * CD_ENABLE_CON 0x08 - Enable confirmation * ------------------------------------------------------------------------- */ STATIC int cd_enable_con(queue_t *q, mblk_t *mp) { struct cd *cd = CD_PRIV(q); cd_enable_con_t *p = (typeof(p)) mp->b_rptr; if (mp->b_wptr > mp->b_rptr + sizeof(*p)) goto eio; cd_set_state(cd, p->cd_state); /* turn around immediate info request */ mp->b_datap->db_type = M_PCPROTO; mp->b_wptr = mp->b_rptr; *(ulong *) mp->b_wptr++ = CD_INFO_REQ; qreply(q, mp); return (QR_ABSORBED); eio: swerr(); return m_error_all(q, cd, EIO, EIO); } /* * CD_DISABLE_CON 0x09 - Disable confirmation * ------------------------------------------------------------------------- */ STATIC int cd_disable_con(queue_t *q, mblk_t *mp) { struct cd *cd = CD_PRIV(q); cd_disable_con_t *p = (typeof(p)) mp->b_rptr; struct dl *dl; int err; if (mp->b_wptr > mp->b_rptr + sizeof(*p)) goto eio; if (p->cd_state != CD_DISABLED) goto eio; if (cd_get_state(cd) != CD_DISABLE_PENDING) goto eio; cd_set_state(cd, p->cd_state); for (dl = cd->wait.list; dl; dl = dl->wait.next) if (dl_get_state(dl) == DL_UNBIND_PENDING) if ((err = dl_ok_ack(q, dl, DL_UNBIND_REQ, cd, NULL, NULL))) return (err); return (QR_DONE); eio: swerr(); return m_error_all(q, cd, EIO, EIO); } /* * CD_ERROR_IND 0x0a - Error indication * ------------------------------------------------------------------------- * This is a particularly ugly primitive. It can occur at any time and can * change the state to any state. */ STATIC int cd_error_ind(queue_t *q, mblk_t *mp) { struct cd *cd = CD_PRIV(q); cd_error_ind_t *p = (typeof(p)) mp->b_rptr; if (mp->b_wptr > mp->b_rptr + sizeof(*p)) goto eio; cd_set_state(cd, p->cd_state); if (cd->i_oldstate == p->cd_state) goto nochange; if (p->cd_state == CD_UNUSABLE) { assure(p->cd_errno == CD_FATALERR); goto eio; /* fatal error */ } /* can't switch to pending, unattached or xray state */ switch (p->cd_state) { case CD_ENABLE_PENDING: case CD_DISABLE_PENDING: case CD_UNATTACHED: case CD_XRAY: goto eio; } /* these required error ack not error ind */ switch (cd->i_oldstate) { case CD_UNATTACHED: case CD_DISABLED: case CD_DISABLE_PENDING: case CD_ENABLE_PENDING: goto eio; } switch (cd->i_oldstate) { case CD_UNUSABLE: if (p->cd_state != CD_DISABLED) goto eio; assure(p->cd_errno == CD_EVENT); /* handle autonomous recovery */ return m_error_all(q, cd, 0, 0); case CD_ENABLED: case CD_READ_ACTIVE: case CD_INPUT_ALLOWED: case CD_OUTPUT_ACTIVE: if (p->cd_state != CD_DISABLED) break; assure(p->cd_errno == CD_DISC); /* handle autonomous disable */ return m_hangup_all(q, cd); default: goto eio; } nochange: return (QR_DONE); eio: swerr(); return m_error_all(q, cd, EIO, EIO); } /* * CD_UNITDATA_ACK 0x0f - Data send acknowledgement * ------------------------------------------------------------------------- */ STATIC int cd_unitdata_ack(queue_t *q, mblk_t *mp) { struct cd *cd = CD_PRIV(q); cd_unitdata_ack_t *p = (typeof(p)) mp->b_rptr; if (mp->b_wptr > mp->b_rptr + sizeof(*p)) goto eio; fixme(("input to state machine\n")); eio: swerr(); return m_error_all(q, cd, EIO, EIO); } /* * CD_UNITDATA_IND 0x10 - Data receive indication * ------------------------------------------------------------------------- */ STATIC int cd_unitdata_ind(queue_t *q, mblk_t *mp) { struct cd *cd = CD_PRIV(q); cd_unitdata_ind_t *p = (typeof(p)) mp->b_rptr; if (mp->b_wptr > mp->b_rptr + sizeof(*p)) goto eio; fixme(("input to state machine\n")); eio: swerr(); return m_error_all(q, cd, EIO, EIO); } /* * CD_BAD_FRAME_IND 0x14 - frame w/error (Gcom extension) * ------------------------------------------------------------------------- */ STATIC int cd_bad_frame_ind(queue_t *q, mblk_t *mp) { struct cd *cd = CD_PRIV(q); cd_bad_frame_ind_t *p = (typeof(p)) mp->b_rptr; if (mp->b_wptr > mp->b_rptr + sizeof(*p)) goto eio; fixme(("input to state machine\n")); eio: swerr(); return m_error_all(q, cd, EIO, EIO); } /* * CD_MODEM_SIG_IND 0x16 - Report modem signal state (Gcom) * ------------------------------------------------------------------------- */ STATIC int cd_modem_sig_ind(queue_t *q, mblk_t *mp) { struct cd *cd = CD_PRIV(q); cd_modem_sig_ind_t *p = (typeof(p)) mp->b_rptr; if (mp->b_wptr > mp->b_rptr + sizeof(*p)) goto eio; fixme(("input to state machine\n")); eio: swerr(); return m_error_all(q, cd, EIO, EIO); } /* * ========================================================================= * * IO CONTROLS * * ========================================================================= */ /* * LAPD_IOCGOPTIONS * ----------------------------------- */ STATIC INLINE int lapd_iocgoptions(queue_t *q, struct dl *dl, struct lapd_iochdr *hdr, int size) { switch (hdr->type) { case LAPD_OBJ_TYPE_DL: { struct dl *dl = dl_lookup(hdr->id); struct lapd_option_dl *opt = (typeof(opt)) (hdr + 1); if (!dl) return (-EINVAL); if ((size -= sizeof(*opt)) < 0) return (-EFAULT); *opt = dl->config; return (QR_DONE); } case LAPD_OBJ_TYPE_CD: { struct cd *cd = cd_lookup(hdr->id); struct lapd_option_cd *opt = (typeof(opt)) (hdr + 1); if (!cd) return (-EINVAL); if ((size -= sizeof(*opt)) < 0) return (-EFAULT); *opt = cd->config; return (QR_DONE); } } rare(); return (-EINVAL); } /* * LAPD_IOCSOPTIONS * ----------------------------------- */ STATIC INLINE int lapd_iocsoptions(queue_t *q, struct dl *dl, struct lapd_iochdr *hdr, int size) { switch (hdr->type) { case LAPD_OBJ_TYPE_DL: { struct dl *dl = dl_lookup(hdr->id); struct lapd_option_dl *opt = (typeof(opt)) (hdr + 1); if (!dl) return (-EINVAL); if ((size -= sizeof(*opt)) < 0) return (-EFAULT); fixme(("check options first!\n")); dl->config = *opt; return (QR_DONE); } case LAPD_OBJ_TYPE_CD: { struct cd *cd = cd_lookup(hdr->id); struct lapd_option_cd *opt = (typeof(opt)) (hdr + 1); if (!cd) return (-EINVAL); if ((size -= sizeof(*opt)) < 0) return (-EFAULT); fixme(("check options first!\n")); cd->config = *opt; return (QR_DONE); } } rare(); return (-EINVAL); } /* * LAPD_IOCXCONFIG Common configuration * ----------------------------------- */ STATIC INLINE int lapd_iocxconfig(queue_t *q, struct dl *dl, struct lapd_iochdr *hdr, int size, int force, int test) { switch (hdr->type) { case LAPD_OBJ_TYPE_DL: { struct dl *dl = dl_lookup(hdr->id); struct lapd_config_dl *cnf = (typeof(cnf)) (hdr + 1); if ((size -= sizeof(*cnf)) < 0) return (-EFAULT); switch (hdr->cmd) { case LAPD_GET: { if (size < sizeof(*hdr)) return (-EFAULT); if (hdr->id) { /* get specific object */ if (!dl) return (-ENXIO); hdr->type = LAPD_OBJ_TYPE_DL; hdr->id = dl->id; hdr->cmd = LAPD_GET; cnf->proto = dl->proto; cnf->dlc = dl->dlc; cnf->ppa = dl->cd.cd ? dl->cd.cd->id : 0; hdr = (typeof(hdr)) (cnf + 1); } else { /* get list of all objects */ for (dl = master.dl.list; dl && size >= sizeof(*hdr) + sizeof(*cnf) + sizeof(*hdr); dl = dl->next, size -= sizeof(*hdr) + sizeof(*cnf), hdr = (typeof(hdr)) (cnf + 1), cnf = (typeof(cnf)) (hdr + 1)) { hdr->type = LAPD_OBJ_TYPE_DL; hdr->id = dl->id; cnf->proto = dl->proto; cnf->dlc = dl->dlc; cnf->ppa = dl->cd.cd ? dl->cd.cd->id : 0; } } /* terminate list with zero object type */ hdr->type = 0; hdr->id = 0; hdr->cmd = 0; return (QR_DONE); } case LAPD_ADD: { if (dl) return (-EEXIST); /* DL objects normally add with open(2), but we might support permanent data links */ return (-EOPNOTSUPP); } case LAPD_CHA: { if (!dl) return (-ENXIO); /* DL objects normally cha with bind, but we might support permanent data links */ return (-EOPNOTSUPP); } case LAPD_DEL: { if (!dl) return (-ENXIO); /* DL objects normally del with close(2), but we might support permanent data links */ return (-EOPNOTSUPP); } } break; } case LAPD_OBJ_TYPE_CD: { struct cd *cd = cd_lookup(hdr->id); struct lapd_config_cd *cnf = (typeof(cnf)) (hdr + 1); if ((size -= sizeof(*cnf)) < 0) return (-EFAULT); switch (hdr->cmd) { case LAPD_GET: { if (size < sizeof(*hdr)) return (-EFAULT); if (hdr->id) { struct dl *dl; struct lapd_config_dl *cdl; /* get specific object */ if (!cd) return (-ENXIO); hdr->type = LAPD_OBJ_TYPE_CD; hdr->id = cd->id; hdr->cmd = LAPD_GET; cnf->proto = cd->proto; cnf->ppa = cd->ppa; hdr = (typeof(hdr)) (cnf + 1); cdl = (typeof(cdl)) (hdr + 1); /* list all attached data links */ for (dl = cd->dl.list; dl && size >= sizeof(*hdr) + sizeof(*cdl) + sizeof(*hdr); dl = dl->next, size -= sizeof(*hdr) + sizeof(*cdl), hdr = (typeof(hdr)) (cdl + 1), cdl = (typeof(cdl)) (hdr + 1)) { hdr->type = LAPD_OBJ_TYPE_DL; hdr->id = dl->id; hdr->cmd = LAPD_GET; cdl->proto = dl->proto; cdl->dlc = dl->dlc; cdl->ppa = dl->cd.cd ? dl->cd.cd->id : 0; } } else { /* get list of all objects */ for (cd = master.cd.list; cd && size >= sizeof(*hdr) + sizeof(*cnf) + sizeof(*hdr); cd = cd->next, size -= sizeof(*hdr) + sizeof(*cnf), hdr = (typeof(hdr)) (cnf + 1), cnf = (typeof(cnf)) (hdr + 1)) { hdr->type = LAPD_OBJ_TYPE_CD; hdr->id = cd->id; cnf->proto = cd->proto; cnf->ppa = cd->ppa; } } /* terminate list with zero object type */ hdr->type = 0; hdr->id = 0; hdr->cmd = 0; return (QR_DONE); } case LAPD_ADD: { if (cd) return (-EEXIST); for (cd = master.cd.list; cd; cd = cd->next) { if (cd->u.mux.index == cnf->muxid) { if (cd->id) return (-EEXIST); break; } } if (!force) { /* nothing to skip */ } if (!test) { hdr->id = cd->id = cd_get_id(hdr->id); cd->proto = cnf->proto; cd->ppa = cnf->ppa; fixme(("Actually add the object.\n")); } return (QR_DONE); } case LAPD_CHA: { if (!cd) return (-ENXIO); if (!force) { /* we have attached data links */ if (cd->dl.list) return (-EBUSY); } if (!test) { cd->proto = cnf->proto; cd->ppa = cnf->ppa; fixme(("Actually cha the object.\n")); } return (QR_DONE); } case LAPD_DEL: { if (!cd) return (-ENXIO); if (!force) { fixme(("Check if CD is active first.\n")); } if (!test) { fixme(("Actually del the object.\n")); } return (QR_DONE); } } break; } case LAPD_OBJ_TYPE_DF: { struct df *df = df_lookup(hdr->id); struct lapd_config_df *cnf = (typeof(cnf)) (hdr + 1); if ((size -= sizeof(*cnf)) < 0) return (-EFAULT); switch (hdr->cmd) { case LAPD_GET: { if (size < sizeof(*hdr)) return (-EFAULT); if (!df) return (-ENXIO); fixme(("Get configuration\n")); return (QR_DONE); } case LAPD_ADD: { if (df) return (-EEXIST); /* The default object always exists. */ return (-EEXIST); } case LAPD_CHA: { if (!df) return (-ENXIO); if (!force) { /* nothing to skip */ } if (!test) { df->proto = cnf->proto; fixme(("Actually cha the object.\n")); } return (QR_DONE); } case LAPD_DEL: { if (!df) return (-ENXIO); /* Can't delete the default object. */ return (-EOPNOTSUPP); } } break; } } rare(); return (-EINVAL); } /* * LAPD_IOCGCONFIG * ----------------------------------- */ STATIC INLINE int lapd_iocgconfig(queue_t *q, struct dl *dl, struct lapd_iochdr *hdr, int size) { switch (hdr->cmd) { case LAPD_GET: return lapd_iocxconfig(q, dl, hdr, size, 0, 0); } rare(); return (-EINVAL); } /* * LAPD_IOCSCONFIG * ----------------------------------- */ STATIC INLINE int lapd_iocsconfig(queue_t *q, struct dl *dl, struct lapd_iochdr *hdr, int size) { switch (hdr->cmd) { case LAPD_ADD: case LAPD_CHA: case LAPD_DEL: return lapd_iocxconfig(q, dl, hdr, size, 0, 0); } rare(); return (-EINVAL); } /* * LAPD_IOCTCONFIG * ----------------------------------- */ STATIC INLINE int lapd_ioctconfig(queue_t *q, struct dl *dl, struct lapd_iochdr *hdr, int size) { switch (hdr->cmd) { case LAPD_ADD: case LAPD_CHA: case LAPD_DEL: return lapd_iocxconfig(q, dl, hdr, size, 1, 0); } rare(); return (-EINVAL); } /* * LAPD_IOCCCONFIG * ----------------------------------- */ STATIC INLINE int lapd_ioccconfig(queue_t *q, struct dl *dl, struct lapd_iochdr *hdr, int size) { switch (hdr->cmd) { case LAPD_ADD: case LAPD_CHA: case LAPD_DEL: return lapd_iocxconfig(q, dl, hdr, size, 0, 1); } rare(); return (-EINVAL); } /* * LAPD_IOCGSTATEM * ----------------------------------- */ STATIC INLINE int lapd_iocgstatem(queue_t *q, struct dl *dl, struct lapd_iochdr *hdr, int size) { switch (hdr->type) { case LAPD_OBJ_TYPE_DL: { struct dl *dl = dl_lookup(hdr->id); struct lapd_statem_dl *sta = (typeof(sta)) (hdr + 1); if (!dl) return (-EINVAL); if ((size -= sizeof(*sta)) < 0) return (-EFAULT); *sta = dl->statem; return (QR_DONE); } case LAPD_OBJ_TYPE_CD: { struct cd *cd = cd_lookup(hdr->id); struct lapd_statem_cd *sta = (typeof(sta)) (hdr + 1); if (!cd) return (-EINVAL); if ((size -= sizeof(*sta)) < 0) return (-EFAULT); *sta = cd->statem; return (QR_DONE); } } rare(); return (-EINVAL); } /* * LAPD_IOCCMRESET * ----------------------------------- */ STATIC INLINE int lapd_ioccmreset(queue_t *q, struct dl *dl, struct lapd_iochdr *hdr, int size) { switch (hdr->type) { case LAPD_OBJ_TYPE_DL: { struct dl *dl = dl_lookup(hdr->id); struct lapd_statem_dl *sta = (typeof(sta)) (hdr + 1); if (!dl) return (-EINVAL); if ((size -= sizeof(*sta)) < 0) return (-EFAULT); *sta = dl->statem; return (QR_DONE); } case LAPD_OBJ_TYPE_CD: { struct cd *cd = cd_lookup(hdr->id); struct lapd_statem_cd *sta = (typeof(sta)) (hdr + 1); if (!cd) return (-EINVAL); if ((size -= sizeof(*sta)) < 0) return (-EFAULT); *sta = cd->statem; return (QR_DONE); } } rare(); return (-EINVAL); } /* * LAPD_IOCGSTATSP * ----------------------------------- */ STATIC INLINE int lapd_iocgstatsp(queue_t *q, struct dl *dl, struct lapd_iochdr *hdr, int size) { switch (hdr->type) { case LAPD_OBJ_TYPE_DL: { struct dl *dl = dl_lookup(hdr->id); struct lapd_stats_dl *per = (typeof(per)) (hdr + 1); if (!dl) return (-EINVAL); if ((size -= sizeof(*per)) < 0) return (-EFAULT); *per = dl->statsp; return (QR_DONE); } case LAPD_OBJ_TYPE_CD: { struct cd *cd = cd_lookup(hdr->id); struct lapd_stats_cd *per = (typeof(per)) (hdr + 1); if (!cd) return (-EINVAL); if ((size -= sizeof(*per)) < 0) return (-EFAULT); *per = cd->statsp; return (QR_DONE); } } rare(); return (-EINVAL); } /* * LAPD_IOCSSTATSP * ----------------------------------- */ STATIC INLINE int lapd_iocsstatsp(queue_t *q, struct dl *dl, struct lapd_iochdr *hdr, int size) { switch (hdr->type) { case LAPD_OBJ_TYPE_DL: { struct dl *dl = dl_lookup(hdr->id); struct lapd_stats_dl *per = (typeof(per)) (hdr + 1); if (!dl) return (-EINVAL); if ((size -= sizeof(*per)) < 0) return (-EFAULT); *per = dl->statsp; return (QR_DONE); } case LAPD_OBJ_TYPE_CD: { struct cd *cd = cd_lookup(hdr->id); struct lapd_stats_cd *per = (typeof(per)) (hdr + 1); if (!cd) return (-EINVAL); if ((size -= sizeof(*per)) < 0) return (-EFAULT); *per = cd->statsp; return (QR_DONE); } } rare(); return (-EINVAL); } /* * LAPD_IOCGSTATS * ----------------------------------- */ STATIC INLINE int lapd_iocgstats(queue_t *q, struct dl *dl, struct lapd_iochdr *hdr, int size) { switch (hdr->type) { case LAPD_OBJ_TYPE_DL: { struct dl *dl = dl_lookup(hdr->id); struct lapd_stats_dl *sta = (typeof(sta)) (hdr + 1); if (!dl) return (-EINVAL); if ((size -= sizeof(*sta)) < 0) return (-EFAULT); *sta = dl->stats; return (QR_DONE); } case LAPD_OBJ_TYPE_CD: { struct cd *cd = cd_lookup(hdr->id); struct lapd_stats_cd *sta = (typeof(sta)) (hdr + 1); if (!cd) return (-EINVAL); if ((size -= sizeof(*sta)) < 0) return (-EFAULT); *sta = cd->stats; return (QR_DONE); } } rare(); return (-EINVAL); } /* * LAPD_IOCCSTATS * ----------------------------------- */ STATIC INLINE int lapd_ioccstats(queue_t *q, struct dl *dl, struct lapd_iochdr *hdr, int size) { switch (hdr->type) { case LAPD_OBJ_TYPE_DL: { struct dl *dl = dl_lookup(hdr->id); struct lapd_stats_dl *sta = (typeof(sta)) (hdr + 1); if (!dl) return (-EINVAL); if ((size -= sizeof(*sta)) < 0) return (-EFAULT); *sta = dl->stats; return (QR_DONE); } case LAPD_OBJ_TYPE_CD: { struct cd *cd = cd_lookup(hdr->id); struct lapd_stats_cd *sta = (typeof(sta)) (hdr + 1); if (!cd) return (-EINVAL); if ((size -= sizeof(*sta)) < 0) return (-EFAULT); *sta = cd->stats; return (QR_DONE); } } rare(); return (-EINVAL); } /* * LAPD_IOCGNOTIFY * ----------------------------------- */ STATIC INLINE int lapd_iocgnotify(queue_t *q, struct dl *dl, struct lapd_iochdr *hdr, int size) { switch (hdr->type) { case LAPD_OBJ_TYPE_DL: { struct dl *dl = dl_lookup(hdr->id); struct lapd_notify_dl *not = (typeof(not)) (hdr + 1); if (!dl) return (-EINVAL); if ((size -= sizeof(*not)) < 0) return (-EFAULT); *not = dl->notify; return (QR_DONE); } case LAPD_OBJ_TYPE_CD: { struct cd *cd = cd_lookup(hdr->id); struct lapd_notify_cd *not = (typeof(not)) (hdr + 1); if (!cd) return (-EINVAL); if ((size -= sizeof(*not)) < 0) return (-EFAULT); *not = cd->notify; return (QR_DONE); } } rare(); return (-EINVAL); } /* * LAPD_IOCSNOTIFY * ----------------------------------- */ STATIC INLINE int lapd_iocsnotify(queue_t *q, struct dl *dl, struct lapd_iochdr *hdr, int size) { switch (hdr->type) { case LAPD_OBJ_TYPE_DL: { struct dl *dl = dl_lookup(hdr->id); struct lapd_notify_dl *not = (typeof(not)) (hdr + 1); if (!dl) return (-EINVAL); if ((size -= sizeof(*not)) < 0) return (-EFAULT); *not = dl->notify; return (QR_DONE); } case LAPD_OBJ_TYPE_CD: { struct cd *cd = cd_lookup(hdr->id); struct lapd_notify_cd *not = (typeof(not)) (hdr + 1); if (!cd) return (-EINVAL); if ((size -= sizeof(*not)) < 0) return (-EFAULT); *not = cd->notify; return (QR_DONE); } } rare(); return (-EINVAL); } /* * LAPD_IOCCNOTIFY * ----------------------------------- */ STATIC INLINE int lapd_ioccnotify(queue_t *q, struct dl *dl, struct lapd_iochdr *hdr, int size) { switch (hdr->type) { case LAPD_OBJ_TYPE_DL: { struct dl *dl = dl_lookup(hdr->id); struct lapd_notify_dl *not = (typeof(not)) (hdr + 1); if (!dl) return (-EINVAL); if ((size -= sizeof(*not)) < 0) return (-EFAULT); *not = dl->notify; return (QR_DONE); } case LAPD_OBJ_TYPE_CD: { struct cd *cd = cd_lookup(hdr->id); struct lapd_notify_cd *not = (typeof(not)) (hdr + 1); if (!cd) return (-EINVAL); if ((size -= sizeof(*not)) < 0) return (-EFAULT); *not = cd->notify; return (QR_DONE); } } rare(); return (-EINVAL); } /* * LAPD_IOCCMGMT * ----------------------------------- */ STATIC INLINE int lapd_ioccmgmt(queue_t *q, struct dl *dl, struct lapd_iochdr *hdr, int size) { switch (hdr->type) { case LAPD_OBJ_TYPE_DL: { struct dl *dl = dl_lookup(hdr->id); if (!dl) return (-EINVAL); return (QR_DONE); } case LAPD_OBJ_TYPE_CD: { struct cd *cd = cd_lookup(hdr->id); if (!cd) return (-EINVAL); return (QR_DONE); } } rare(); return (-EINVAL); } /* * LAPD_IOCCPASS * ----------------------------------- */ STATIC INLINE int lapd_ioccpass(queue_t *q, struct dl *dl, struct lapd_iochdr *hdr, int size) { switch (hdr->type) { case LAPD_OBJ_TYPE_DL: { struct dl *dl = dl_lookup(hdr->id); if (!dl) return (-EINVAL); return (QR_DONE); } case LAPD_OBJ_TYPE_CD: { struct cd *cd = cd_lookup(hdr->id); if (!cd) return (-EINVAL); return (QR_DONE); } } rare(); return (-EINVAL); } /* * ========================================================================= * * STREAMS MESSAGE HANDLING * * ========================================================================= */ /* * ------------------------------------------------------------------------- * * M_IOCTL, M_IOCDATA, M_IOCACK, M_IOCNAK, M_COPYIN, M_COPYOUT Handling * * ------------------------------------------------------------------------- */ STATIC INLINE int dl_w_ioctl(queue_t *q, mblk_t *mp) { struct dl *dl = DL_PRIV(q); struct iocblk *iocp = (struct iocblk *) mp->b_rptr; void *arg = mp->b_cont ? mp->b_cont->b_rptr : NULL; int cmd = iocp->ioc_cmd, count = iocp->ioc_count; int type = _IOC_TYPE(cmd), nr = _IOC_NR(cmd), size = _IOC_SIZE(cmd); int ret = 0, flags = 0; (void) dl; switch (type) { case _IOC_TYPE(__SID): { struct linkblk *lb; struct cd *cd, **cdp; if (!(lb = arg)) { swerr(); ret = (-EINVAL); break; } switch (nr) { case _IOC_NR(I_PLINK): ptrace(("%s: %p: -> I_PLINK\n", LAPD_DRV_NAME, dl)); if (iocp->ioc_cr->cr_uid != 0) { ptrace(("%s: %p: ERROR: Non-root attempt to I_PLINK\n", LAPD_DRV_NAME, dl)); ret = (-EPERM); break; } case _IOC_NR(I_LINK): ptrace(("%s: %p: -> I_LINK\n", LAPD_DRV_NAME, dl)); MOD_INC_USE_COUNT; /* keep module from unloading */ lis_spin_lock_irqsave(&master.lock, &flags); { /* place in list in ascending index order */ for (cdp = &master.cd.list; *cdp && (*cdp)->u.mux.index < lb->l_index; cdp = &(*cdp)->next) ; if ((cd = dl_alloc_link(lb->l_qbot, cdp, lb->l_index, iocp->ioc_cr))) { lis_spin_unlock_irqrestore(&master.lock, &flags); break; } MOD_DEC_USE_COUNT; ret = (-ENOMEM); } lis_spin_unlock_irqrestore(&master.lock, &flags); break; case _IOC_NR(I_PUNLINK): ptrace(("%s: %p: -> I_PUNLINK\n", LAPD_DRV_NAME, dl)); if (iocp->ioc_cr->cr_uid != 0) { ptrace(("%s: %p: ERROR: Non-root attempt to I_PUNLINK\n", LAPD_DRV_NAME, dl)); ret = (-EPERM); break; } case _IOC_NR(I_UNLINK): ptrace(("%s: %p: -> I_UNLINK\n", LAPD_DRV_NAME, dl)); lis_spin_lock_irqsave(&master.lock, &flags); { for (cd = master.cd.list; cd; cd = cd->next) if (cd->u.mux.index == lb->l_index) break; if (!cd) { ret = (-EINVAL); lis_spin_unlock_irqrestore(&master.lock, &flags); break; } dl_free_link(cd); MOD_DEC_USE_COUNT; } lis_spin_unlock_irqrestore(&master.lock, &flags); break; default: ptrace(("%s: %p: ERROR: Unsupported STREAMS ioctl %c, %d\n", LAPD_DRV_NAME, dl, (char) type, nr)); ret = (-EOPNOTSUPP); break; } break; } case LAPD_IOC_MAGIC: { struct lapd_iochdr *hdr; if (!(hdr = arg) || count < size || (count -= sizeof(*hdr)) < 0) { ret = (-EFAULT); break; } switch (nr) { case _IOC_NR(LAPD_IOCGOPTIONS): printd(("%d: %p: -> LAPD_IOCGOPTIONS\n", LAPD_DRV_NAME, dl)); ret = lapd_iocgoptions(q, dl, hdr, count); break; case _IOC_NR(LAPD_IOCSOPTIONS): printd(("%d: %p: -> LAPD_IOCSOPTIONS\n", LAPD_DRV_NAME, dl)); ret = lapd_iocsoptions(q, dl, hdr, count); break; case _IOC_NR(LAPD_IOCGCONFIG): printd(("%d: %p: -> LAPD_IOCGCONFIG\n", LAPD_DRV_NAME, dl)); ret = lapd_iocgconfig(q, dl, hdr, count); break; case _IOC_NR(LAPD_IOCSCONFIG): printd(("%d: %p: -> LAPD_IOCSCONFIG\n", LAPD_DRV_NAME, dl)); ret = lapd_iocsconfig(q, dl, hdr, count); break; case _IOC_NR(LAPD_IOCTCONFIG): printd(("%d: %p: -> LAPD_IOCTCONFIG\n", LAPD_DRV_NAME, dl)); ret = lapd_ioctconfig(q, dl, hdr, count); break; case _IOC_NR(LAPD_IOCCCONFIG): printd(("%d: %p: -> LAPD_IOCCCONFIG\n", LAPD_DRV_NAME, dl)); ret = lapd_ioccconfig(q, dl, hdr, count); break; case _IOC_NR(LAPD_IOCGSTATEM): printd(("%d: %p: -> LAPD_IOCGSTATEM\n", LAPD_DRV_NAME, dl)); ret = lapd_iocgstatem(q, dl, hdr, count); break; case _IOC_NR(LAPD_IOCCMRESET): printd(("%d: %p: -> LAPD_IOCCMRESET\n", LAPD_DRV_NAME, dl)); ret = lapd_ioccmreset(q, dl, hdr, count); break; case _IOC_NR(LAPD_IOCGSTATSP): printd(("%d: %p: -> LAPD_IOCGSTATSP\n", LAPD_DRV_NAME, dl)); ret = lapd_iocgstatsp(q, dl, hdr, count); break; case _IOC_NR(LAPD_IOCSSTATSP): printd(("%d: %p: -> LAPD_IOCSSTATSP\n", LAPD_DRV_NAME, dl)); ret = lapd_iocsstatsp(q, dl, hdr, count); break; case _IOC_NR(LAPD_IOCGSTATS): printd(("%d: %p: -> LAPD_IOCGSTATS\n", LAPD_DRV_NAME, dl)); ret = lapd_iocgstats(q, dl, hdr, count); break; case _IOC_NR(LAPD_IOCCSTATS): printd(("%d: %p: -> LAPD_IOCCSTATS\n", LAPD_DRV_NAME, dl)); ret = lapd_ioccstats(q, dl, hdr, count); break; case _IOC_NR(LAPD_IOCGNOTIFY): printd(("%d: %p: -> LAPD_IOCGNOTIFY\n", LAPD_DRV_NAME, dl)); ret = lapd_iocgnotify(q, dl, hdr, count); break; case _IOC_NR(LAPD_IOCSNOTIFY): printd(("%d: %p: -> LAPD_IOCSNOTIFY\n", LAPD_DRV_NAME, dl)); ret = lapd_iocsnotify(q, dl, hdr, count); break; case _IOC_NR(LAPD_IOCCNOTIFY): printd(("%d: %p: -> LAPD_IOCCNOTIFY\n", LAPD_DRV_NAME, dl)); ret = lapd_ioccnotify(q, dl, hdr, count); break; case _IOC_NR(LAPD_IOCCMGMT): printd(("%d: %p: -> LAPD_IOCCMGMT\n", LAPD_DRV_NAME, dl)); ret = lapd_ioccmgmt(q, dl, hdr, count); break; case _IOC_NR(LAPD_IOCCPASS): printd(("%d: %p: -> LAPD_IOCCPASS\n", LAPD_DRV_NAME, dl)); ret = lapd_ioccpass(q, dl, hdr, count); break; default: ptrace(("%s: %p: ERROR: Unsupported DL ioctl %c, %d\n", LAPD_DRV_NAME, dl, (char) type, nr)); ret = (-EOPNOTSUPP); break; } break; } case HDLC_IOC_MAGIC: { struct cd *cd; if (!(cd = dl->cd.cd)) { /* not attached to ppa */ ret = (-ENXIO); break; } if (!canputnext(cd->oq)) { ret = (-EAGAIN); break; } putnext(cd->oq, mp); return (QR_ABSORBED); } default: ptrace(("%s: %p: ERROR: Unsuported DL ioctl %c, %d\n", LAPD_DRV_NAME, dl, (char) type, nr)); ret = (-EOPNOTSUPP); break; } if (ret == 0) { mp->b_datap->db_type = M_IOCACK; iocp->ioc_error = -ret; iocp->ioc_rval = 0; } else if (ret < 0) { mp->b_datap->db_type = M_IOCNAK; iocp->ioc_error = -ret; iocp->ioc_rval = -1U; } else return (ret); qreply(q, mp); return (QR_ABSORBED); } STATIC INLINE int dl_w_iocdata(queue_t *q, mblk_t *mp) { fixme(("write this function\n")); return (-EFAULT); } STATIC INLINE int cd_r_iocack(queue_t *q, mblk_t *mp) { fixme(("write this function\n")); return (-EFAULT); } STATIC INLINE int cd_r_iocnak(queue_t *q, mblk_t *mp) { fixme(("write this function\n")); return (-EFAULT); } STATIC INLINE int cd_r_copyin(queue_t *q, mblk_t *mp) { fixme(("write this function\n")); return (-EFAULT); } STATIC INLINE int cd_r_copyout(queue_t *q, mblk_t *mp) { fixme(("write this function\n")); return (-EFAULT); } /* * ------------------------------------------------------------------------- * * M_PROTO, M_PCPROTO Message Handling * * ------------------------------------------------------------------------- */ STATIC int dl_w_proto(queue_t *q, mblk_t *mp) { struct dl *dl = DL_PRIV(q); ulong prim; int rtn; dl->i_oldstate = dl_get_state(dl); if (mp->b_wptr > mp->b_rptr + sizeof(prim)) return (-EMSGSIZE); switch ((prim = *(ulong *) mp->b_rptr)) { case DL_INFO_REQ: printd(("%s: %p: -> DL_INFO_REQ\n", LAPD_DRV_NAME, dl)); rtn = dl_info_req(q, mp); break; case DL_ATTACH_REQ: printd(("%s: %p: -> DL_ATTACH_REQ\n", LAPD_DRV_NAME, dl)); rtn = dl_attach_req(q, mp); break; case DL_DETACH_REQ: printd(("%s: %p: -> DL_DETACH_REQ\n", LAPD_DRV_NAME, dl)); rtn = dl_detach_req(q, mp); break; case DL_BIND_REQ: printd(("%s: %p: -> DL_BIND_REQ\n", LAPD_DRV_NAME, dl)); rtn = dl_bind_req(q, mp); break; case DL_UNBIND_REQ: printd(("%s: %p: -> DL_UNBIND_REQ\n", LAPD_DRV_NAME, dl)); rtn = dl_unbind_req(q, mp); break; case DL_SUBS_BIND_REQ: printd(("%s: %p: -> DL_SUBS_BIND_REQ\n", LAPD_DRV_NAME, dl)); rtn = dl_subs_bind_req(q, mp); break; case DL_SUBS_UNBIND_REQ: printd(("%s: %p: -> DL_SUBS_UNBIND_REQ\n", LAPD_DRV_NAME, dl)); rtn = dl_subs_unbind_req(q, mp); break; case DL_ENABMULTI_REQ: printd(("%s: %p: -> DL_ENABMULTI_REQ\n", LAPD_DRV_NAME, dl)); rtn = dl_enabmulti_req(q, mp); break; case DL_DISABMULTI_REQ: printd(("%s: %p: -> DL_DISABMULTI_REQ\n", LAPD_DRV_NAME, dl)); rtn = dl_disabmulti_req(q, mp); break; case DL_PROMISCON_REQ: printd(("%s: %p: -> DL_PROMISCON_REQ\n", LAPD_DRV_NAME, dl)); rtn = dl_promiscon_req(q, mp); break; case DL_PROMISCOFF_REQ: printd(("%s: %p: -> DL_PROMISCOFF_REQ\n", LAPD_DRV_NAME, dl)); rtn = dl_promiscoff_req(q, mp); break; case DL_CONNECT_REQ: printd(("%s: %p: -> DL_CONNECT_REQ\n", LAPD_DRV_NAME, dl)); rtn = dl_connect_req(q, mp); break; case DL_CONNECT_RES: printd(("%s: %p: -> DL_CONNECT_RES\n", LAPD_DRV_NAME, dl)); rtn = dl_connect_res(q, mp); break; case DL_TOKEN_REQ: printd(("%s: %p: -> DL_TOKEN_REQ\n", LAPD_DRV_NAME, dl)); rtn = dl_token_req(q, mp); break; case DL_DISCONNECT_REQ: printd(("%s: %p: -> DL_DISCONNECT_REQ\n", LAPD_DRV_NAME, dl)); rtn = dl_disconnect_req(q, mp); break; case DL_RESET_REQ: printd(("%s: %p: -> DL_RESET_REQ\n", LAPD_DRV_NAME, dl)); rtn = dl_reset_req(q, mp); break; case DL_RESET_RES: printd(("%s: %p: -> DL_RESET_RES\n", LAPD_DRV_NAME, dl)); rtn = dl_reset_res(q, mp); break; case DL_UNITDATA_REQ: printd(("%s: %p: -> DL_UNITDATA_REQ\n", LAPD_DRV_NAME, dl)); rtn = dl_unitdata_req(q, mp); break; case DL_UDQOS_REQ: printd(("%s: %p: -> DL_UDQOS_REQ\n", LAPD_DRV_NAME, dl)); rtn = dl_udqos_req(q, mp); break; case DL_TEST_REQ: printd(("%s: %p: -> DL_TEST_REQ\n", LAPD_DRV_NAME, dl)); rtn = dl_test_req(q, mp); break; case DL_TEST_RES: printd(("%s: %p: -> DL_TEST_RES\n", LAPD_DRV_NAME, dl)); rtn = dl_test_res(q, mp); break; case DL_XID_REQ: printd(("%s: %p: -> DL_XID_REQ\n", LAPD_DRV_NAME, dl)); rtn = dl_xid_req(q, mp); break; case DL_XID_RES: printd(("%s: %p: -> DL_XID_RES\n", LAPD_DRV_NAME, dl)); rtn = dl_xid_res(q, mp); break; case DL_DATA_ACK_REQ: printd(("%s: %p: -> DL_DATA_ACK_REQ\n", LAPD_DRV_NAME, dl)); rtn = dl_data_ack_req(q, mp); break; case DL_REPLY_REQ: printd(("%s: %p: -> DL_REPLY_REQ\n", LAPD_DRV_NAME, dl)); rtn = dl_reply_req(q, mp); break; case DL_REPLY_UPDATE_REQ: printd(("%s: %p: -> DL_REPLY_UPDATE_REQ\n", LAPD_DRV_NAME, dl)); rtn = dl_reply_update_req(q, mp); break; case DL_PHYS_ADDR_REQ: printd(("%s: %p: -> DL_PHYS_ADD_REQ\n", LAPD_DRV_NAME, dl)); rtn = dl_phys_addr_req(q, mp); break; case DL_SET_PHYS_ADDR_REQ: printd(("%s: %p: -> DL_SET_PHYS_ADDR_REQ\n", LAPD_DRV_NAME, dl)); rtn = dl_set_phys_addr_req(q, mp); break; case DL_GET_STATISTICS_REQ: printd(("%s: %p: -> DL_GET_STATISTICS_REQ\n", LAPD_DRV_NAME, dl)); rtn = dl_get_statistics_req(q, mp); break; default: printd(("%s: %p: -> DL_?? %lu ??\n", LAPD_DRV_NAME, dl, prim)); rtn = dl_error_ack(q, dl, prim, DL_BADPRIM, EOPNOTSUPP); break; } if (rtn < 0) dl_set_state(dl, dl->i_oldstate); return (rtn); } STATIC int cd_r_proto(queue_t *q, mblk_t *mp) { struct cd *cd = CD_PRIV(q); ulong prim; int rtn; cd->i_oldstate = cd_get_state(cd); if (mp->b_wptr > mp->b_rptr + sizeof(prim)) return (-EMSGSIZE); switch ((prim = *(ulong *) mp->b_rptr)) { case CD_INFO_ACK: printd(("%s: %p: CD_INFO_ACK <-\n", LAPD_DRV_NAME, cd)); rtn = cd_info_ack(q, mp); break; case CD_OK_ACK: printd(("%s: %p: CD_OK_ACK <-\n", LAPD_DRV_NAME, cd)); rtn = cd_ok_ack(q, mp); break; case CD_ERROR_ACK: printd(("%s: %p: CD_ERROR_ACK <-\n", LAPD_DRV_NAME, cd)); rtn = cd_error_ack(q, mp); break; case CD_ENABLE_CON: printd(("%s: %p: CD_ENABLE_CON <-\n", LAPD_DRV_NAME, cd)); rtn = cd_enable_con(q, mp); break; case CD_DISABLE_CON: printd(("%s: %p: CD_DISABLE_CON <-\n", LAPD_DRV_NAME, cd)); rtn = cd_disable_con(q, mp); break; case CD_ERROR_IND: printd(("%s: %p: CD_ERROR_IND <-\n", LAPD_DRV_NAME, cd)); rtn = cd_error_ind(q, mp); break; case CD_UNITDATA_ACK: printd(("%s: %p: CD_UNITDATA_ACK <-\n", LAPD_DRV_NAME, cd)); rtn = cd_unitdata_ack(q, mp); break; case CD_UNITDATA_IND: printd(("%s: %p: CD_UNITDATA_IND <-\n", LAPD_DRV_NAME, cd)); rtn = cd_unitdata_ind(q, mp); break; case CD_BAD_FRAME_IND: printd(("%s: %p: CD_BAD_FRAME_IND <-\n", LAPD_DRV_NAME, cd)); rtn = cd_bad_frame_ind(q, mp); break; case CD_MODEM_SIG_IND: printd(("%s: %p: CD_MODEM_SIG_IND <-\n", LAPD_DRV_NAME, cd)); rtn = cd_modem_sig_ind(q, mp); break; default: pswerr(("%s: %p: CD_?? %lu ?? <-\n", LAPD_DRV_NAME, cd, prim)); rtn = m_hangup_all(q, cd); break; } if (rtn < 0) cd_set_state(cd, cd->i_oldstate); return (rtn); } /* * ------------------------------------------------------------------------- * * M_DATA Message Handling * * ------------------------------------------------------------------------- */ STATIC INLINE int dl_w_data(queue_t *q, mblk_t *mp) { struct dl *dl = DL_PRIV(q); (void) dl; printd(("%s: %p: -> M_DATA [%d]\n", LAPD_DRV_NAME, dl, msgdsize(mp))); return dl_data_req(q, mp); } STATIC INLINE int cd_r_data(queue_t *q, mblk_t *mp) { struct cd *cd = CD_PRIV(q); (void) cd; printd(("%s: %p: M_DATA [%d] <-\n", LAPD_DRV_NAME, cd, msgdsize(mp))); return cd_data_ind(q, mp); } /* * ------------------------------------------------------------------------- * * M_ERROR, M_HANGUP, Message Handling * * ------------------------------------------------------------------------- */ STATIC INLINE int cd_r_error(queue_t *q, mblk_t *mp) { struct cd *cd = CD_PRIV(q); printd(("%s: %p: M_ERROR <-\n", LAPD_DRV_NAME, cd)); return m_error_all(q, cd, ((uchar *) mp->b_rptr)[0], ((uchar *) mp->b_rptr++)[1]); } STATIC INLINE int cd_r_hangup(queue_t *q, mblk_t *mp) { struct cd *cd = CD_PRIV(q); printd(("%s: %p: M_HANGUP <-\n", LAPD_DRV_NAME, cd)); return m_hangup_all(q, cd); } /* * ========================================================================= * * PUT AND SRV * * ========================================================================= */ STATIC int dl_r_prim(queue_t *q, mblk_t *mp) { switch (mp->b_datap->db_type) { case M_FLUSH: return ss7_r_flush(q, mp); } return (QR_PASSFLOW); } STATIC int dl_w_prim(queue_t *q, mblk_t *mp) { /* Fast Path */ if (mp->b_datap->db_type == M_DATA) return dl_w_data(q, mp); switch (mp->b_datap->db_type) { case M_DATA: return dl_w_data(q, mp); case M_PROTO: case M_PCPROTO: return dl_w_proto(q, mp); case M_FLUSH: return ss7_w_flush(q, mp); case M_IOCTL: return dl_w_ioctl(q, mp); case M_IOCDATA: return dl_w_iocdata(q, mp); } swerr(); return (-EOPNOTSUPP); } STATIC int cd_r_prim(queue_t *q, mblk_t *mp) { /* Fast Path */ if (mp->b_datap->db_type == M_DATA) return cd_r_data(q, mp); switch (mp->b_datap->db_type) { case M_DATA: return cd_r_data(q, mp); case M_PROTO: case M_PCPROTO: return cd_r_proto(q, mp); case M_FLUSH: return ss7_r_flush(q, mp); case M_IOCACK: return cd_r_iocnak(q, mp); case M_IOCNAK: return cd_r_iocack(q, mp); case M_COPYIN: return cd_r_copyin(q, mp); case M_COPYOUT: return cd_r_copyout(q, mp); case M_ERROR: return cd_r_error(q, mp); case M_HANGUP: return cd_r_hangup(q, mp); } swerr(); return (-EIO); } STATIC int cd_w_prim(queue_t *q, mblk_t *mp) { switch (mp->b_datap->db_type) { case M_FLUSH: return ss7_w_flush(q, mp); } return (QR_PASSFLOW); } /* * ========================================================================= * * OPEN AND CLOSE * * ========================================================================= */ STATIC int lapd_majors[LAPD_NMAJOR] = { LAPD_CMAJOR, }; /* * Open * ------------------------------------------------------------------------- * This open function automatically allocates major device numbers up to * LAPD_NMAJOR device numbers maximum. Majors will be deallocated when the * module is unloaded. This ensures that the driver only uses one major * device number when less that about 250 devices are not needed. */ STATIC int dl_open(queue_t *q, dev_t *devp, int flag, int sflag, cred_t *crp) { int flags, mindex = 0; ushort cmajor = getmajor(*devp); ushort cminor = getminor(*devp); ushort bminor = cminor; struct dl *dl, **dlp = &master.dl.list; MOD_INC_USE_COUNT; if (q->q_ptr != NULL) { MOD_DEC_USE_COUNT; return (0); /* already open */ } if (sflag == MODOPEN || WR(q)->q_next) { ptrace(("%s: ERROR: cannot push as module\n", LAPD_DRV_NAME)); MOD_DEC_USE_COUNT; return (EIO); } if ((cmajor && cmajor != lapd_majors[0]) || cminor >= 1) { MOD_DEC_USE_COUNT; return (ENXIO); } /* allocate a new device */ cminor = 1; lis_spin_lock_irqsave(&master.lock, &flags); for (dlp = &master.dl.list; *dlp; dlp = &(*dlp)->next) { ushort dmajor = (*dlp)->u.dev.cmajor; ushort dminor = (*dlp)->u.dev.cminor; if (!dmajor) continue; /* skip headless opens */ if (!cmajor || cmajor != dmajor || cminor < dminor) break; if (cminor > dminor || ++cminor < LAPD_NMINOR) continue; if (++mindex >= LAPD_NMAJOR) break; if (!(cmajor = lapd_majors[mindex])) { if ((cmajor = lis_register_strdev(lapd_majors[mindex], &lapd_info, LAPD_NMINOR, LAPD_DRV_NAME)) <= 0) break; lapd_majors[mindex] = cmajor; LIS_DEVFLAGS(cmajor) |= LIS_MODFLG_CLONE; } cminor = 0; } if (mindex >= LAPD_NMAJOR || !lapd_majors[mindex]) { ptrace(("%s: ERROR: no device numbers available\n", LAPD_DRV_NAME)); lis_spin_unlock_irqrestore(&master.lock, &flags); MOD_DEC_USE_COUNT; return (ENXIO); } printd(("%s: opened chanracter device %d:%d\n", LAPD_DRV_NAME, cmajor, cminor)); *devp = makedevice(cmajor, cminor); if (!(dl = dl_alloc_priv(q, dlp, devp, crp, bminor))) { ptrace(("%s: ERROR: no memory\n", LAPD_DRV_NAME)); lis_spin_unlock_irqrestore(&master.lock, &flags); MOD_DEC_USE_COUNT; return (ENOMEM); } lis_spin_unlock_irqrestore(&master.lock, &flags); return (0); } /* * Close * ------------------------------------------------------------------------- */ STATIC int dl_close(queue_t *q, int flag, cred_t *crp) { struct dl *dl = DL_PRIV(q); int flags; (void) flag; (void) crp; printd(("%s: closing character device %d:%d\n", LAPD_DRV_NAME, dl->u.dev.cmajor, dl->u.dev.cminor)); lis_spin_lock_irqsave(&master.lock, &flags); dl_free_priv(dl); lis_spin_unlock_irqrestore(&master.lock, &flags); MOD_DEC_USE_COUNT; return (0); } /* * ========================================================================= * * LIS MODULE INITIALIZATION * * ========================================================================= */ STATIC int lapd_initialized = 0; MODULE_STATIC void lapd_terminate(void) { int rtn, mindex; for (mindex = 0; mindex < LAPD_NMAJOR; mindex++) { if (lapd_majors[mindex]) { if ((rtn = lis_unregister_strdev(lapd_majors[mindex]))) cmn_err(CE_PANIC, "%s: Cannot unregister major %d\n", LAPD_DRV_NAME, lapd_majors[mindex]); if (mindex) lapd_majors[mindex] = 0; } } lapd_term_caches(); lapd_initialized = 0; return; } MODULE_STATIC void lapd_init(void) { int rtn, mindex = 0; unless(lapd_initialized, return); cmn_err(CE_NOTE, LAPD_BANNER); /* console splash */ if ((rtn = lapd_init_caches())) { cmn_err(CE_PANIC, "%s: ERROR; Could no allocate caches", LAPD_DRV_NAME); goto error; } if ((rtn = lis_register_strdev(lapd_majors[mindex], &lapd_info, LAPD_NMINOR, LAPD_DRV_NAME)) <= 0) { cmn_err(CE_PANIC, "%s: ERROR: Cannot register 1'st major %d", LAPD_DRV_NAME, lapd_majors[mindex]); goto error; } #if 0 if (!lapd_majors[mindex]) { dev_t dev = makedevice(LIS_CLONE, rtn); /* we have auto allocated a major, so we have to create a clone device to access the major */ if ((rtn = lis_unlink("/dev/dl-lapd") < 0)) { cmn_err(CE_PANIC, "%s: ERROR: Cannot unlink /dev/dl-lapd\n", LAPD_DRV_NAME); goto error; } if ((rtn = lis_mknod("/dev/dl-lapd", S_IFCHR | 0666, dev) < 0)) { cmn_err(CE_PANIC, "%s: ERROR: Cannot create /dev/dl-lapd\n", LAPD_DRV_NAME); goto error; } } #endif lapd_majors[mindex] = rtn; LIS_DEVFLAGS(rtn) |= LIS_MODFLG_CLONE; lis_spin_lock_init(&master.lock, "dl-open-list-lock"); lapd_initialized = 1; return; error: lapd_terminate(); lapd_initialized = rtn; return; } /* * ========================================================================= * * LINUX MODULE INITIALIZATION * * ========================================================================= */ int init_module(void) { lapd_init(); if (lapd_initialized < 0) return lapd_initialized; return (0); } void cleanup_module(void) { lapd_terminate(); }
|
|||||||||||||||||||||||||||
OpenSS7 SS7 for the Common Man |
Home | Overview | Status | News | Documentation | Resources | About | ||||||||||||||||||||
© Copyright 1997-2004,OpenSS7 Corporation, All Rights Reserved. |