diff options
| author | Hansjoerg Lipp <hjlipp@web.de> | 2006-03-26 04:38:31 -0500 |
|---|---|---|
| committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-03-26 11:57:05 -0500 |
| commit | 3c66a22545dd88356bbf4efa45a7b178df8154e9 (patch) | |
| tree | 96d6115cddf69655d6b4b7b3866fdad8e2cef0a8 | |
| parent | 14fa73a75df6a0ffe866f750e8dcd218d0171f51 (diff) | |
[PATCH] isdn4linux: Siemens Gigaset drivers - isdn4linux interface
And: Tilman Schmidt <tilman@imap.cc>
This patch adds the isdn4linux subsystem interface to the gigaset module. The
isdn4linux subsystem interface handles requests from and notifications to the
isdn4linux subsystem.
Signed-off-by: Hansjoerg Lipp <hjlipp@web.de>
Signed-off-by: Tilman Schmidt <tilman@imap.cc>
Cc: Karsten Keil <kkeil@suse.de>
Cc: Greg KH <greg@kroah.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
| -rw-r--r-- | drivers/isdn/gigaset/i4l.c | 567 |
1 files changed, 567 insertions, 0 deletions
diff --git a/drivers/isdn/gigaset/i4l.c b/drivers/isdn/gigaset/i4l.c new file mode 100644 index 000000000000..731a675f21b0 --- /dev/null +++ b/drivers/isdn/gigaset/i4l.c | |||
| @@ -0,0 +1,567 @@ | |||
| 1 | /* | ||
| 2 | * Stuff used by all variants of the driver | ||
| 3 | * | ||
| 4 | * Copyright (c) 2001 by Stefan Eilers (Eilers.Stefan@epost.de), | ||
| 5 | * Hansjoerg Lipp (hjlipp@web.de), | ||
| 6 | * Tilman Schmidt (tilman@imap.cc). | ||
| 7 | * | ||
| 8 | * ===================================================================== | ||
| 9 | * This program is free software; you can redistribute it and/or | ||
| 10 | * modify it under the terms of the GNU General Public License as | ||
| 11 | * published by the Free Software Foundation; either version 2 of | ||
| 12 | * the License, or (at your option) any later version. | ||
| 13 | * ===================================================================== | ||
| 14 | * ToDo: ... | ||
| 15 | * ===================================================================== | ||
| 16 | * Version: $Id: i4l.c,v 1.3.2.9 2006/02/04 18:28:16 hjlipp Exp $ | ||
| 17 | * ===================================================================== | ||
| 18 | */ | ||
| 19 | |||
| 20 | #include "gigaset.h" | ||
| 21 | |||
| 22 | /* == Handling of I4L IO ============================================================================*/ | ||
| 23 | |||
| 24 | /* writebuf_from_LL | ||
| 25 | * called by LL to transmit data on an open channel | ||
| 26 | * inserts the buffer data into the send queue and starts the transmission | ||
| 27 | * Note that this operation must not sleep! | ||
| 28 | * When the buffer is processed completely, gigaset_skb_sent() should be called. | ||
| 29 | * parameters: | ||
| 30 | * driverID driver ID as assigned by LL | ||
| 31 | * channel channel number | ||
| 32 | * ack if != 0 LL wants to be notified on completion via statcallb(ISDN_STAT_BSENT) | ||
| 33 | * skb skb containing data to send | ||
| 34 | * return value: | ||
| 35 | * number of accepted bytes | ||
| 36 | * 0 if temporarily unable to accept data (out of buffer space) | ||
| 37 | * <0 on error (eg. -EINVAL) | ||
| 38 | */ | ||
| 39 | static int writebuf_from_LL(int driverID, int channel, int ack, struct sk_buff *skb) | ||
| 40 | { | ||
| 41 | struct cardstate *cs; | ||
| 42 | struct bc_state *bcs; | ||
| 43 | unsigned len; | ||
| 44 | unsigned skblen; | ||
| 45 | |||
| 46 | if (!(cs = gigaset_get_cs_by_id(driverID))) { | ||
| 47 | err("%s: invalid driver ID (%d)", __func__, driverID); | ||
| 48 | return -ENODEV; | ||
| 49 | } | ||
| 50 | if (channel < 0 || channel >= cs->channels) { | ||
| 51 | err("%s: invalid channel ID (%d)", __func__, channel); | ||
| 52 | return -ENODEV; | ||
| 53 | } | ||
| 54 | bcs = &cs->bcs[channel]; | ||
| 55 | len = skb->len; | ||
| 56 | |||
| 57 | dbg(DEBUG_LLDATA, | ||
| 58 | "Receiving data from LL (id: %d, channel: %d, ack: %d, size: %d)", | ||
| 59 | driverID, channel, ack, len); | ||
| 60 | |||
| 61 | if (!len) { | ||
| 62 | if (ack) | ||
| 63 | warn("not ACKing empty packet from LL"); | ||
| 64 | return 0; | ||
| 65 | } | ||
| 66 | if (len > MAX_BUF_SIZE) { | ||
| 67 | err("%s: packet too large (%d bytes)", __func__, channel); | ||
| 68 | return -EINVAL; | ||
| 69 | } | ||
| 70 | |||
| 71 | if (!atomic_read(&cs->connected)) | ||
| 72 | return -ENODEV; | ||
| 73 | |||
| 74 | skblen = ack ? len : 0; | ||
| 75 | skb->head[0] = skblen & 0xff; | ||
| 76 | skb->head[1] = skblen >> 8; | ||
| 77 | dbg(DEBUG_MCMD, "skb: len=%u, skblen=%u: %02x %02x", len, skblen, | ||
| 78 | (unsigned) skb->head[0], (unsigned) skb->head[1]); | ||
| 79 | |||
| 80 | /* pass to device-specific module */ | ||
| 81 | return cs->ops->send_skb(bcs, skb); | ||
| 82 | } | ||
| 83 | |||
| 84 | void gigaset_skb_sent(struct bc_state *bcs, struct sk_buff *skb) | ||
| 85 | { | ||
| 86 | unsigned len; | ||
| 87 | isdn_ctrl response; | ||
| 88 | |||
| 89 | ++bcs->trans_up; | ||
| 90 | |||
| 91 | if (skb->len) | ||
| 92 | warn("%s: skb->len==%d", __func__, skb->len); | ||
| 93 | |||
| 94 | len = (unsigned char) skb->head[0] | | ||
| 95 | (unsigned) (unsigned char) skb->head[1] << 8; | ||
| 96 | if (len) { | ||
| 97 | dbg(DEBUG_MCMD, | ||
| 98 | "Acknowledge sending to LL (id: %d, channel: %d size: %u)", | ||
| 99 | bcs->cs->myid, bcs->channel, len); | ||
| 100 | |||
| 101 | response.driver = bcs->cs->myid; | ||
| 102 | response.command = ISDN_STAT_BSENT; | ||
| 103 | response.arg = bcs->channel; | ||
| 104 | response.parm.length = len; | ||
| 105 | bcs->cs->iif.statcallb(&response); | ||
| 106 | } | ||
| 107 | } | ||
| 108 | EXPORT_SYMBOL_GPL(gigaset_skb_sent); | ||
| 109 | |||
| 110 | /* This function will be called by LL to send commands | ||
| 111 | * NOTE: LL ignores the returned value, for commands other than ISDN_CMD_IOCTL, | ||
| 112 | * so don't put too much effort into it. | ||
| 113 | */ | ||
| 114 | static int command_from_LL(isdn_ctrl *cntrl) | ||
| 115 | { | ||
| 116 | struct cardstate *cs = gigaset_get_cs_by_id(cntrl->driver); | ||
| 117 | //isdn_ctrl response; | ||
| 118 | //unsigned long flags; | ||
| 119 | struct bc_state *bcs; | ||
| 120 | int retval = 0; | ||
| 121 | struct setup_parm *sp; | ||
| 122 | |||
| 123 | //dbg(DEBUG_ANY, "Gigaset_HW: Receiving command"); | ||
| 124 | gigaset_debugdrivers(); | ||
| 125 | |||
| 126 | /* Terminate this call if no device is present. Bt if the command is "ISDN_CMD_LOCK" or | ||
| 127 | * "ISDN_CMD_UNLOCK" then execute it due to the fact that they are device independent ! | ||
| 128 | */ | ||
| 129 | //FIXME "remove test for &connected" | ||
| 130 | if ((!cs || !atomic_read(&cs->connected))) { | ||
| 131 | warn("LL tried to access unknown device with nr. %d", | ||
| 132 | cntrl->driver); | ||
| 133 | return -ENODEV; | ||
| 134 | } | ||
| 135 | |||
| 136 | switch (cntrl->command) { | ||
| 137 | case ISDN_CMD_IOCTL: | ||
| 138 | |||
| 139 | dbg(DEBUG_ANY, "ISDN_CMD_IOCTL (driver:%d,arg: %ld)", | ||
| 140 | cntrl->driver, cntrl->arg); | ||
| 141 | |||
| 142 | warn("ISDN_CMD_IOCTL is not supported."); | ||
| 143 | return -EINVAL; | ||
| 144 | |||
| 145 | case ISDN_CMD_DIAL: | ||
| 146 | dbg(DEBUG_ANY, "ISDN_CMD_DIAL (driver: %d, channel: %ld, " | ||
| 147 | "phone: %s,ownmsn: %s, si1: %d, si2: %d)", | ||
| 148 | cntrl->driver, cntrl->arg, | ||
| 149 | cntrl->parm.setup.phone, cntrl->parm.setup.eazmsn, | ||
| 150 | cntrl->parm.setup.si1, cntrl->parm.setup.si2); | ||
| 151 | |||
| 152 | if (cntrl->arg >= cs->channels) { | ||
| 153 | err("invalid channel (%d)", (int) cntrl->arg); | ||
| 154 | return -EINVAL; | ||
| 155 | } | ||
| 156 | |||
| 157 | bcs = cs->bcs + cntrl->arg; | ||
| 158 | |||
| 159 | if (!gigaset_get_channel(bcs)) { | ||
| 160 | err("channel not free"); | ||
| 161 | return -EBUSY; | ||
| 162 | } | ||
| 163 | |||
| 164 | sp = kmalloc(sizeof *sp, GFP_ATOMIC); | ||
| 165 | if (!sp) { | ||
| 166 | gigaset_free_channel(bcs); | ||
| 167 | err("ISDN_CMD_DIAL: out of memory"); | ||
| 168 | return -ENOMEM; | ||
| 169 | } | ||
| 170 | *sp = cntrl->parm.setup; | ||
| 171 | |||
| 172 | if (!gigaset_add_event(cs, &bcs->at_state, EV_DIAL, sp, | ||
| 173 | atomic_read(&bcs->at_state.seq_index), | ||
| 174 | NULL)) { | ||
| 175 | //FIXME what should we do? | ||
| 176 | kfree(sp); | ||
| 177 | gigaset_free_channel(bcs); | ||
| 178 | return -ENOMEM; | ||
| 179 | } | ||
| 180 | |||
| 181 | dbg(DEBUG_CMD, "scheduling DIAL"); | ||
| 182 | gigaset_schedule_event(cs); | ||
| 183 | break; | ||
| 184 | case ISDN_CMD_ACCEPTD: //FIXME | ||
| 185 | dbg(DEBUG_ANY, "ISDN_CMD_ACCEPTD"); | ||
| 186 | |||
| 187 | if (cntrl->arg >= cs->channels) { | ||
| 188 | err("invalid channel (%d)", (int) cntrl->arg); | ||
| 189 | return -EINVAL; | ||
| 190 | } | ||
| 191 | |||
| 192 | if (!gigaset_add_event(cs, &cs->bcs[cntrl->arg].at_state, | ||
| 193 | EV_ACCEPT, NULL, 0, NULL)) { | ||
| 194 | //FIXME what should we do? | ||
