static void isdn_register_devfs(int);
static void isdn_unregister_devfs(int);
-static int isdn_drv_writebuf_skb(int di, int ch, int x, struct sk_buff *skb);
/* ====================================================================== */
static int __slot_command(struct isdn_slot *slot, isdn_ctrl *cmd);
+static void isdn_v110_setl2(struct isdn_slot *slot, isdn_ctrl *cmd);
+static void __isdn_v110_open(struct isdn_slot *slot);
+static void __isdn_v110_close(struct isdn_slot *slot);
+static void __isdn_v110_bsent(struct isdn_slot *slot, isdn_ctrl *cmd);
+static int isdn_v110_data_ind(struct isdn_slot *slot, struct sk_buff *skb);
+static int isdn_v110_data_req(struct isdn_slot *slot, struct sk_buff *skb);
+
static inline int
do_stat_cb(struct isdn_slot *slot, isdn_ctrl *ctrl)
{
return 0;
}
+/* just pass through command */
+static int
+slot_setl2(struct fsm_inst *fi, int pr, void *arg)
+{
+ struct isdn_slot *slot = fi->userdata;
+ isdn_ctrl *c = arg;
+
+ isdn_v110_setl2(slot, c);
+
+ return __slot_command(slot, c);
+}
+
static int
slot_dial(struct fsm_inst *fi, int pr, void *arg)
{
int retval;
retval = __slot_command(slot, ctrl);
- if (retval >= 0)
+ if (retval >= 0) {
fsm_change_state(fi, ST_SLOT_WAIT_BHUP);
-
+ }
return retval;
}
isdn_ctrl *ctrl = arg;
fsm_change_state(fi, ST_SLOT_ACTIVE);
+ __isdn_v110_open(slot);
isdn_info_update();
struct isdn_slot *slot = fi->userdata;
isdn_ctrl *ctrl = arg;
+ __isdn_v110_close(slot);
fsm_change_state(fi, ST_SLOT_WAIT_DHUP);
do_stat_cb(slot, ctrl);
struct isdn_slot *slot = fi->userdata;
struct sk_buff *skb = arg;
- return isdn_drv_writebuf_skb(slot->di, slot->ch, 1, skb);
+ return isdn_v110_data_req(slot, skb);
}
static int
slot_data_ind(struct fsm_inst *fi, int pr, void *arg)
{
struct isdn_slot *slot = fi->userdata;
- int sl = slot - slots;
struct sk_buff *skb = arg;
/* Update statistics */
slot->ibytes += skb->len;
- slot->recv_cb(sl, skb);
- return 0;
-#if 0
- /* V.110 handling
- * makes sense for async streams only, so it is
- * called after possible net-device delivery.
- */
- if (slots[i].iv110.v110) {
- atomic_inc(&slots[i].iv110.v110use);
- skb = isdn_v110_decode(slots[i].iv110.v110, skb);
- atomic_dec(&slots[i].iv110.v110use);
- if (!skb)
- return;
- }
-#endif
+ return isdn_v110_data_ind(slot, skb);
}
static int
struct isdn_slot *slot = fi->userdata;
isdn_ctrl *ctrl = arg;
- do_stat_cb(slot, ctrl);
+ __isdn_v110_bsent(slot, ctrl);
return 0;
}
strcpy(slot->num, "???");
slot->ibytes = 0;
slot->obytes = 0;
-// 20.10.99 JIM, try to reinitialize v110 !
- slot->iv110.v110emu = 0;
- atomic_set(&slot->iv110.v110use, 0);
- isdn_v110_close(slot->iv110.v110);
- slot->iv110.v110 = NULL;
-// 20.10.99 JIM, try to reinitialize v110 !
slot->usage = ISDN_USAGE_NONE;
isdn_info_update();
return 0;
{ ST_SLOT_BOUND, EV_CMD_CLREAZ, slot_command },
{ ST_SLOT_BOUND, EV_CMD_SETEAZ, slot_command },
- { ST_SLOT_BOUND, EV_CMD_SETL2, slot_command },
+ { ST_SLOT_BOUND, EV_CMD_SETL2, slot_setl2 },
{ ST_SLOT_BOUND, EV_CMD_SETL3, slot_command },
{ ST_SLOT_BOUND, EV_CMD_DIAL, slot_dial },
{ ST_SLOT_BOUND, EV_SLOT_UNBIND, slot_unbind },
- { ST_SLOT_IN, EV_CMD_SETL2, slot_command },
+ { ST_SLOT_IN, EV_CMD_SETL2, slot_setl2 },
{ ST_SLOT_IN, EV_CMD_SETL3, slot_command },
{ ST_SLOT_IN, EV_CMD_ACCEPTD, slot_acceptd },
{ ST_SLOT_IN, EV_STAT_DHUP, slot_in_dhup },
char id[20];
atomic_t refcnt;
unsigned long flags; /* Misc driver Flags */
+ unsigned long features;
int locks; /* Number of locks */
int channels; /* Number of channels */
wait_queue_head_t st_waitq; /* Wait-Queue for status-reads */
static struct isdn_driver *drivers[ISDN_MAX_DRIVERS];
static int
-isdn_drv_writebuf_skb(int di, int ch, int x, struct sk_buff *skb)
+isdn_writebuf_skb(struct isdn_slot *slot, struct sk_buff *skb)
{
struct sk_buff *skb2;
- struct isdn_driver *drv = drivers[di];
+ struct isdn_driver *drv = slot->drv;
int hl = drv->interface->hl_hdrlen;
int retval;
- if (skb_headroom(skb) >= hl)
- return drivers[di]->interface->writebuf_skb(di, ch, 1, skb);
-
+ if (skb_headroom(skb) >= hl) {
+ retval = drv->interface->writebuf_skb(slot->di, slot->ch, 1, skb);
+ goto out;
+ }
skb2 = skb_realloc_headroom(skb, hl);
- if (!skb2)
- return -ENOMEM;
-
- retval = drivers[di]->interface->writebuf_skb(di, ch, 1, skb2);
+ if (!skb2) {
+ retval = -ENOMEM;
+ goto out;
+ }
+ retval = drv->interface->writebuf_skb(slot->di, slot->ch, 1, skb2);
if (retval < 0)
kfree_skb(skb2);
else
kfree_skb(skb);
+ out:
+ if (retval > 0)
+ slots->obytes += retval;
+
return retval;
}
for (drvidx = 0; drvidx < ISDN_MAX_DRIVERS; drvidx++) {
if (!drivers[drvidx])
continue;
- if (drivers[drvidx]->interface)
- dev->global_features |= drivers[drvidx]->interface->features;
+ if (drivers[drvidx]->fi.state != ST_DRV_RUNNING)
+ continue;
+ dev->global_features |= drivers[drvidx]->features;
}
spin_unlock_irqrestore(&drivers_lock, flags);
}
static void isdn_receive_skb_callback(int di, int ch, struct sk_buff *skb);
static int isdn_status_callback(isdn_ctrl * c);
+static void isdn_v110_add_features(struct isdn_driver *drv);
+
static int
drv_register(struct fsm_inst *fi, int pr, void *arg)
{
iif->statcallb = isdn_status_callback;
isdn_info_update();
- set_global_features();
}
static int
drv_stat_run(struct fsm_inst *fi, int pr, void *arg)
{
+ struct isdn_driver *drv = fi->userdata;
fsm_change_state(fi, ST_DRV_RUNNING);
+
+ drv->features = drv->interface->features;
+ isdn_v110_add_features(drv);
set_global_features();
}
}
dev->channels -= drv->channels;
- set_global_features();
isdn_info_update();
return 0;
}
break;
#if 0
case ISDN_STAT_ICALL:
- isdn_v110_stat_callback(&slots[i].iv110, c);
/* Find any ttyI, waiting for D-channel setup */
if (isdn_tty_stat_callback(i, c)) {
cmd.driver = di;
return retval;
break;
case ISDN_STAT_DHUP:
- isdn_v110_stat_callback(&slots[i].iv110, c);
if (divert_if)
divert_if->stat_callback(c);
break;
case ISDN_STAT_BCONN:
- isdn_v110_stat_callback(&slots[i].iv110, c);
break;
case ISDN_STAT_BHUP:
- isdn_v110_stat_callback(&slots[i].iv110, c);
break;
#endif
#if 0 // FIXME
return drv->interface->command(c);
}
-/*
- * Intercept command from Linklevel to Lowlevel.
- * If layer 2 protocol is V.110 and this is not supported by current
- * lowlevel-driver, use driver's transparent mode and handle V.110 in
- * linklevel instead.
- */
static int
__slot_command(struct isdn_slot *slot, isdn_ctrl *cmd)
{
struct isdn_driver *drv = slot->drv;
- if (cmd->command == ISDN_CMD_SETL2) {
- unsigned long l2prot = (cmd->arg >> 8) & 255;
- unsigned long features = (drv->interface->features
- >> ISDN_FEATURE_L2_SHIFT) &
- ISDN_FEATURE_L2_MASK;
- unsigned long l2_feature = 1 << l2prot;
-
- switch (l2prot) {
- case ISDN_PROTO_L2_V11096:
- case ISDN_PROTO_L2_V11019:
- case ISDN_PROTO_L2_V11038:
- /* If V.110 requested, but not supported by
- * HL-driver, set emulator-flag and change
- * Layer-2 to transparent
- */
- if (!(features & l2_feature)) {
- slot->iv110.v110emu = l2prot;
- cmd->arg = (cmd->arg & 255) |
- (ISDN_PROTO_L2_TRANS << 8);
- } else
- slot->iv110.v110emu = 0;
- }
- }
return __drv_command(drv, cmd);
}
* Find an unused ISDN-channel, whose feature-flags match the
* given L2- and L3-protocols.
*/
-#define L2V (~(ISDN_FEATURE_L2_V11096|ISDN_FEATURE_L2_V11019|ISDN_FEATURE_L2_V11038))
-
int
isdn_get_free_slot(int usage, int l2_proto, int l3_proto,
int pre_dev, int pre_chan, char *msn)
{
struct isdn_slot *slot;
int i;
- ulong flags;
- ulong features;
- ulong vfeatures;
+ unsigned long flags;
+ unsigned long features;
save_flags(flags);
cli();
features = ((1 << l2_proto) | (0x10000 << l3_proto));
- vfeatures = (((1 << l2_proto) | (0x10000 << l3_proto)) &
- ~(ISDN_FEATURE_L2_V11096|ISDN_FEATURE_L2_V11019|ISDN_FEATURE_L2_V11038));
- /* If Layer-2 protocol is V.110, accept drivers with
- * transparent feature even if these don't support V.110
- * because we can emulate this in linklevel.
- */
for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
slot = &slots[i];
if (strcmp(isdn_map_eaz2msn(msn, slot->di), "-") == 0)
continue;
- if (((drivers[slot->di]->interface->features & features) == features) ||
- (((drivers[slot->di]->interface->features & vfeatures) == vfeatures) &&
- (drivers[slot->di]->interface->features & ISDN_FEATURE_L2_TRANS))) {
- if (pre_dev < 0 || pre_chan < 0 ||
- (pre_dev == slot->di && pre_chan == slot->ch)) {
- slot->usage = usage;
- isdn_info_update();
- fsm_event(&slot->fi, EV_SLOT_BIND, NULL);
- return i;
- }
+ if (!(drivers[slot->di]->features & features) == features)
+ continue;
+
+ if (pre_dev < 0 || pre_chan < 0 ||
+ (pre_dev == slot->di && pre_chan == slot->ch)) {
+ slot->usage = usage;
+ isdn_info_update();
+ fsm_event(&slot->fi, EV_SLOT_BIND, NULL);
+ return i;
}
}
restore_flags(flags);
int
isdn_slot_write(int sl, struct sk_buff *skb)
{
- int ret;
- struct sk_buff *nskb = NULL;
- int v110_ret = skb->len;
+ struct isdn_slot *slot = &slots[sl];
BUG_ON(sl < 0);
- if (slots[sl].iv110.v110) {
- atomic_inc(&slots[sl].iv110.v110use);
- nskb = isdn_v110_encode(slots[sl].iv110.v110, skb);
- atomic_dec(&slots[sl].iv110.v110use);
- if (!nskb)
- return 0;
- v110_ret = *((int *)nskb->data);
- skb_pull(nskb, sizeof(int));
- if (!nskb->len) {
- dev_kfree_skb(nskb);
- return v110_ret;
- }
- /* V.110 must always be acknowledged */
- ret = fsm_event(&slots[sl].fi, EV_DATA_REQ, nskb);
- } else {
- int hl = isdn_slot_hdrlen(sl);
-
- if( skb_headroom(skb) < hl ){
- /*
- * This should only occur when new HL driver with
- * increased hl_hdrlen was loaded after netdevice
- * was created and connected to the new driver.
- *
- * The V.110 branch (re-allocates on its own) does
- * not need this
- */
- struct sk_buff * skb_tmp;
-
- skb_tmp = skb_realloc_headroom(skb, hl);
- printk(KERN_DEBUG "isdn_writebuf_skb_stub: reallocating headroom%s\n", skb_tmp ? "" : " failed");
- if (!skb_tmp) return -ENOMEM; /* 0 better? */
- ret = fsm_event(&slots[sl].fi, EV_DATA_REQ, skb_tmp);
- if( ret > 0 ){
- dev_kfree_skb(skb);
- } else {
- dev_kfree_skb(skb_tmp);
- }
- } else {
- ret = fsm_event(&slots[sl].fi, EV_DATA_REQ, skb);
- }
- }
- if (ret > 0) {
- slots[sl].obytes += ret;
- if (slots[sl].iv110.v110) {
- atomic_inc(&slots[sl].iv110.v110use);
- slots[sl].iv110.v110->skbuser++;
- atomic_dec(&slots[sl].iv110.v110use);
- /* For V.110 return unencoded data length */
- ret = v110_ret;
- /* if the complete frame was send we free the skb;
- if not upper function will requeue the skb */
- if (ret == skb->len)
- dev_kfree_skb(skb);
- }
- } else
- if (slots[sl].iv110.v110)
- dev_kfree_skb(nskb);
- return ret;
+ return fsm_event(&slot->fi, EV_DATA_REQ, skb);
}
static int
module_init(isdn_init);
module_exit(isdn_exit);
+
+static void
+isdn_v110_add_features(struct isdn_driver *drv)
+{
+ unsigned long features = drv->features >> ISDN_FEATURE_L2_SHIFT;
+
+ if (features & ISDN_FEATURE_L2_TRANS)
+ drv->features |= (ISDN_FEATURE_L2_V11096|
+ ISDN_FEATURE_L2_V11019|
+ ISDN_FEATURE_L2_V11038) <<
+ ISDN_FEATURE_L2_SHIFT;
+}
+
+static void
+__isdn_v110_open(struct isdn_slot *slot)
+{
+ int sl = slot - slots;
+
+ if (!slot->iv110.v110emu)
+ return;
+
+ isdn_v110_open(sl, &slot->iv110);
+}
+
+static void
+__isdn_v110_close(struct isdn_slot *slot)
+{
+ int sl = slot - slots;
+
+ if (!slot->iv110.v110emu)
+ return;
+
+ isdn_v110_close(sl, &slot->iv110);
+}
+
+static void
+__isdn_v110_bsent(struct isdn_slot *slot, isdn_ctrl *c)
+{
+ int sl = slot - slots;
+
+ if (!slot->iv110.v110emu) {
+ do_stat_cb(slot, c);
+ return;
+ }
+ isdn_v110_bsent(sl, &slot->iv110);
+}
+
+/*
+ * Intercept command from Linklevel to Lowlevel.
+ * If layer 2 protocol is V.110 and this is not supported by current
+ * lowlevel-driver, use driver's transparent mode and handle V.110 in
+ * linklevel instead.
+ */
+static void
+isdn_v110_setl2(struct isdn_slot *slot, isdn_ctrl *cmd)
+{
+ struct isdn_driver *drv = slot->drv;
+
+ unsigned long l2prot = (cmd->arg >> 8) & 255;
+ unsigned long l2_feature = 1 << l2prot;
+ unsigned long features = drv->interface->features >>
+ ISDN_FEATURE_L2_SHIFT;
+
+ switch (l2prot) {
+ case ISDN_PROTO_L2_V11096:
+ case ISDN_PROTO_L2_V11019:
+ case ISDN_PROTO_L2_V11038:
+ /* If V.110 requested, but not supported by
+ * HL-driver, set emulator-flag and change
+ * Layer-2 to transparent
+ */
+ if (!(features & l2_feature)) {
+ slot->iv110.v110emu = l2prot;
+ cmd->arg = (cmd->arg & 255) |
+ (ISDN_PROTO_L2_TRANS << 8);
+ } else
+ slot->iv110.v110emu = 0;
+ }
+}
+
+static int
+isdn_v110_data_ind(struct isdn_slot *slot, struct sk_buff *skb)
+{
+ int sl = slot - slots;
+
+ if (!slot->iv110.v110emu)
+ goto recv;
+
+ skb = isdn_v110_decode(slot->iv110.v110, skb);
+ if (!skb)
+ return 0;
+
+recv:
+ slot->recv_cb(sl, skb);
+ return 0;
+}
+
+static int
+isdn_v110_data_req(struct isdn_slot *slot, struct sk_buff *skb)
+{
+ int retval, v110_ret;
+ struct sk_buff *nskb = NULL;
+
+ if (!slot->iv110.v110emu)
+ return isdn_writebuf_skb(slot, skb);
+
+ atomic_inc(&slot->iv110.v110use);
+ nskb = isdn_v110_encode(slot->iv110.v110, skb);
+ atomic_dec(&slot->iv110.v110use);
+ if (!nskb)
+ return -ENOMEM;
+
+ v110_ret = *(int *)nskb->data;
+ skb_pull(nskb, sizeof(int));
+ if (!nskb->len) {
+ dev_kfree_skb(nskb);
+ return v110_ret;
+ }
+
+ retval = isdn_writebuf_skb(slot, nskb);
+ if (retval <= 0) {
+ dev_kfree_skb(nskb);
+ return retval;
+ }
+ dev_kfree_skb(skb);
+
+ atomic_inc(&slot->iv110.v110use);
+ slots->iv110.v110->skbuser++;
+ atomic_dec(&slot->iv110.v110use);
+
+ /* For V.110 return unencoded data length */
+ return v110_ret;
+}
* structures and returns a pointer to these.
*/
static isdn_v110_stream *
-isdn_v110_open(unsigned char key, int hdrlen, int maxsize)
+do_isdn_v110_open(unsigned char key, int hdrlen, int maxsize)
{
int i;
isdn_v110_stream *v;
}
/* isdn_v110_close frees private V.110 data structures */
-void
-isdn_v110_close(isdn_v110_stream * v)
+static void
+do_isdn_v110_close(isdn_v110_stream * v)
{
if (v == NULL)
return;
return nskb;
}
+
+void
+isdn_v110_open(int sl, struct isdn_v110 *iv110)
+{
+ isdn_v110_stream *v;
+ int hdrlen = isdn_slot_hdrlen(sl);
+ int maxsize = isdn_slot_maxbufsize(sl);
+
+ atomic_inc(&iv110->v110use);
+ switch (iv110->v110emu) {
+ case ISDN_PROTO_L2_V11096:
+ iv110->v110 = do_isdn_v110_open(V110_9600, hdrlen, maxsize);
+ break;
+ case ISDN_PROTO_L2_V11019:
+ iv110->v110 = do_isdn_v110_open(V110_19200, hdrlen, maxsize);
+ break;
+ case ISDN_PROTO_L2_V11038:
+ iv110->v110 = do_isdn_v110_open(V110_38400, hdrlen, maxsize);
+ break;
+ }
+ if ((v = iv110->v110)) {
+ while (v->SyncInit) {
+ struct sk_buff *skb = isdn_v110_sync(v);
+ if (isdn_slot_write(sl, skb) <= 0) {
+ dev_kfree_skb(skb);
+ /* Unable to send, try later */
+ break;
+ }
+ v->SyncInit--;
+ v->skbidle++;
+ }
+ } else
+ printk(KERN_WARNING "isdn_v110: Couldn't open stream\n");
+ atomic_dec(&iv110->v110use);
+}
+
+void
+isdn_v110_close(int sl, struct isdn_v110 *iv110)
+{
+ while (1) {
+ atomic_inc(&iv110->v110use);
+ if (atomic_dec_and_test(&iv110->v110use)) {
+ do_isdn_v110_close(iv110->v110);
+ iv110->v110 = NULL;
+ break;
+ }
+ }
+}
+
int
-isdn_v110_stat_callback(int sl, struct isdn_v110 *iv110, isdn_ctrl *c)
+isdn_v110_bsent(int sl, struct isdn_v110 *iv110)
{
- isdn_v110_stream *v = NULL;
- int i;
- int ret;
-
- switch (c->command) {
- case ISDN_STAT_BSENT:
- /* Keep the send-queue of the driver filled
- * with frames:
- * If number of outstanding frames < 3,
- * send down an Idle-Frame (or an Sync-Frame, if
- * v->SyncInit != 0).
- */
- if (!(v = iv110->v110))
- return 0;
- atomic_inc(&iv110->v110use);
- if (v->skbidle > 0) {
- v->skbidle--;
- ret = 1;
+ isdn_v110_stream *v = iv110->v110;
+ int i, ret;
+
+ /* Keep the send-queue of the driver filled
+ * with frames:
+ * If number of outstanding frames < 3,
+ * send down an Idle-Frame (or an Sync-Frame, if
+ * v->SyncInit != 0).
+ */
+ atomic_inc(&iv110->v110use);
+ if (v->skbidle > 0) {
+ v->skbidle--;
+ ret = 1;
+ } else {
+ if (v->skbuser > 0)
+ v->skbuser--;
+ ret = 0;
+ }
+ for (i = v->skbuser + v->skbidle; i < 2; i++) {
+ struct sk_buff *skb;
+ if (v->SyncInit > 0)
+ skb = isdn_v110_sync(v);
+ else
+ skb = isdn_v110_idle(v);
+ if (skb) {
+ if (isdn_slot_write(sl, skb) <= 0) {
+ dev_kfree_skb(skb);
+ break;
} else {
- if (v->skbuser > 0)
- v->skbuser--;
- ret = 0;
- }
- for (i = v->skbuser + v->skbidle; i < 2; i++) {
- struct sk_buff *skb;
- if (v->SyncInit > 0)
- skb = isdn_v110_sync(v);
- else
- skb = isdn_v110_idle(v);
- if (skb) {
- if (isdn_slot_write(sl, skb) <= 0) {
- dev_kfree_skb(skb);
- break;
- } else {
- if (v->SyncInit)
- v->SyncInit--;
- v->skbidle++;
- }
- } else
- break;
- }
- atomic_dec(&iv110->v110use);
- return ret;
- case ISDN_STAT_DHUP:
- case ISDN_STAT_BHUP:
- while (1) {
- atomic_inc(&iv110->v110use);
- if (atomic_dec_and_test(&iv110->v110use)) {
- isdn_v110_close(iv110->v110);
- iv110->v110 = NULL;
- break;
- }
- sti();
+ if (v->SyncInit)
+ v->SyncInit--;
+ v->skbidle++;
}
+ } else
break;
- case ISDN_STAT_BCONN:
- if (iv110->v110emu && (iv110->v110 == NULL)) {
- int hdrlen = isdn_slot_hdrlen(sl);
- int maxsize = isdn_slot_maxbufsize(sl);
- atomic_inc(&iv110->v110use);
- switch (iv110->v110emu) {
- case ISDN_PROTO_L2_V11096:
- iv110->v110 = isdn_v110_open(V110_9600, hdrlen, maxsize);
- break;
- case ISDN_PROTO_L2_V11019:
- iv110->v110 = isdn_v110_open(V110_19200, hdrlen, maxsize);
- break;
- case ISDN_PROTO_L2_V11038:
- iv110->v110 = isdn_v110_open(V110_38400, hdrlen, maxsize);
- break;
- default:;
- }
- if ((v = iv110->v110)) {
- while (v->SyncInit) {
- struct sk_buff *skb = isdn_v110_sync(v);
- if (isdn_slot_write(sl, skb) <= 0) {
- dev_kfree_skb(skb);
- /* Unable to send, try later */
- break;
- }
- v->SyncInit--;
- v->skbidle++;
- }
- } else
- printk(KERN_WARNING "isdn_v110: Couldn't open stream\n");
- atomic_dec(&iv110->v110use);
- }
- break;
- default:
- return 0;
}
- return 0;
+ atomic_dec(&iv110->v110use);
+ return ret;
}