diff options
Diffstat (limited to 'drivers/isdn/gigaset/i4l.c')
-rw-r--r-- | drivers/isdn/gigaset/i4l.c | 506 |
1 files changed, 301 insertions, 205 deletions
diff --git a/drivers/isdn/gigaset/i4l.c b/drivers/isdn/gigaset/i4l.c index 654489d836cd..aca72a06184e 100644 --- a/drivers/isdn/gigaset/i4l.c +++ b/drivers/isdn/gigaset/i4l.c | |||
@@ -14,6 +14,9 @@ | |||
14 | */ | 14 | */ |
15 | 15 | ||
16 | #include "gigaset.h" | 16 | #include "gigaset.h" |
17 | #include <linux/isdnif.h> | ||
18 | |||
19 | #define HW_HDR_LEN 2 /* Header size used to store ack info */ | ||
17 | 20 | ||
18 | /* == Handling of I4L IO =====================================================*/ | 21 | /* == Handling of I4L IO =====================================================*/ |
19 | 22 | ||
@@ -95,6 +98,7 @@ static int writebuf_from_LL(int driverID, int channel, int ack, | |||
95 | */ | 98 | */ |
96 | void gigaset_skb_sent(struct bc_state *bcs, struct sk_buff *skb) | 99 | void gigaset_skb_sent(struct bc_state *bcs, struct sk_buff *skb) |
97 | { | 100 | { |
101 | isdn_if *iif = bcs->cs->iif; | ||
98 | unsigned len; | 102 | unsigned len; |
99 | isdn_ctrl response; | 103 | isdn_ctrl response; |
100 | 104 | ||
@@ -114,71 +118,177 @@ void gigaset_skb_sent(struct bc_state *bcs, struct sk_buff *skb) | |||
114 | response.command = ISDN_STAT_BSENT; | 118 | response.command = ISDN_STAT_BSENT; |
115 | response.arg = bcs->channel; | 119 | response.arg = bcs->channel; |
116 | response.parm.length = len; | 120 | response.parm.length = len; |
117 | bcs->cs->iif.statcallb(&response); | 121 | iif->statcallb(&response); |
118 | } | 122 | } |
119 | } | 123 | } |
120 | EXPORT_SYMBOL_GPL(gigaset_skb_sent); | 124 | EXPORT_SYMBOL_GPL(gigaset_skb_sent); |
121 | 125 | ||
126 | /** | ||
127 | * gigaset_skb_rcvd() - pass received skb to LL | ||
128 | * @bcs: B channel descriptor structure. | ||
129 | * @skb: received data. | ||
130 | * | ||
131 | * Called by hardware module {bas,ser,usb}_gigaset when user data has | ||
132 | * been successfully received, for passing to the LL. | ||
133 | * Warning: skb must not be accessed anymore! | ||
134 | */ | ||
135 | void gigaset_skb_rcvd(struct bc_state *bcs, struct sk_buff *skb) | ||
136 | { | ||
137 | isdn_if *iif = bcs->cs->iif; | ||
138 | |||
139 | iif->rcvcallb_skb(bcs->cs->myid, bcs->channel, skb); | ||
140 | bcs->trans_down++; | ||
141 | } | ||
142 | EXPORT_SYMBOL_GPL(gigaset_skb_rcvd); | ||
143 | |||
144 | /** | ||
145 | * gigaset_isdn_rcv_err() - signal receive error | ||
146 | * @bcs: B channel descriptor structure. | ||
147 | * | ||
148 | * Called by hardware module {bas,ser,usb}_gigaset when a receive error | ||
149 | * has occurred, for signalling to the LL. | ||
150 | */ | ||
151 | void gigaset_isdn_rcv_err(struct bc_state *bcs) | ||
152 | { | ||
153 | isdn_if *iif = bcs->cs->iif; | ||
154 | isdn_ctrl response; | ||
155 | |||
156 | /* if currently ignoring packets, just count down */ | ||
157 | if (bcs->ignore) { | ||
158 | bcs->ignore--; | ||
159 | return; | ||
160 | } | ||
161 | |||
162 | /* update statistics */ | ||
163 | bcs->corrupted++; | ||
164 | |||
165 | /* error -> LL */ | ||
166 | gig_dbg(DEBUG_CMD, "sending L1ERR"); | ||
167 | response.driver = bcs->cs->myid; | ||
168 | response.command = ISDN_STAT_L1ERR; | ||
169 | response.arg = bcs->channel; | ||
170 | response.parm.errcode = ISDN_STAT_L1ERR_RECV; | ||
171 | iif->statcallb(&response); | ||
172 | } | ||
173 | EXPORT_SYMBOL_GPL(gigaset_isdn_rcv_err); | ||
174 | |||
122 | /* This function will be called by LL to send commands | 175 | /* This function will be called by LL to send commands |
123 | * NOTE: LL ignores the returned value, for commands other than ISDN_CMD_IOCTL, | 176 | * NOTE: LL ignores the returned value, for commands other than ISDN_CMD_IOCTL, |
124 | * so don't put too much effort into it. | 177 | * so don't put too much effort into it. |
125 | */ | 178 | */ |
126 | static int command_from_LL(isdn_ctrl *cntrl) | 179 | static int command_from_LL(isdn_ctrl *cntrl) |
127 | { | 180 | { |
128 | struct cardstate *cs = gigaset_get_cs_by_id(cntrl->driver); | 181 | struct cardstate *cs; |
129 | struct bc_state *bcs; | 182 | struct bc_state *bcs; |
130 | int retval = 0; | 183 | int retval = 0; |
131 | struct setup_parm *sp; | 184 | char **commands; |
185 | int ch; | ||
186 | int i; | ||
187 | size_t l; | ||
132 | 188 | ||
133 | gigaset_debugdrivers(); | 189 | gigaset_debugdrivers(); |
134 | 190 | ||
135 | if (!cs) { | 191 | gig_dbg(DEBUG_CMD, "driver: %d, command: %d, arg: 0x%lx", |
192 | cntrl->driver, cntrl->command, cntrl->arg); | ||
193 | |||
194 | cs = gigaset_get_cs_by_id(cntrl->driver); | ||
195 | if (cs == NULL) { | ||
136 | pr_err("%s: invalid driver ID (%d)\n", __func__, cntrl->driver); | 196 | pr_err("%s: invalid driver ID (%d)\n", __func__, cntrl->driver); |
137 | return -ENODEV; | 197 | return -ENODEV; |
138 | } | 198 | } |
199 | ch = cntrl->arg & 0xff; | ||
139 | 200 | ||
140 | switch (cntrl->command) { | 201 | switch (cntrl->command) { |
141 | case ISDN_CMD_IOCTL: | 202 | case ISDN_CMD_IOCTL: |
142 | gig_dbg(DEBUG_ANY, "ISDN_CMD_IOCTL (driver: %d, arg: %ld)", | ||
143 | cntrl->driver, cntrl->arg); | ||
144 | |||
145 | dev_warn(cs->dev, "ISDN_CMD_IOCTL not supported\n"); | 203 | dev_warn(cs->dev, "ISDN_CMD_IOCTL not supported\n"); |
146 | return -EINVAL; | 204 | return -EINVAL; |
147 | 205 | ||
148 | case ISDN_CMD_DIAL: | 206 | case ISDN_CMD_DIAL: |
149 | gig_dbg(DEBUG_ANY, | 207 | gig_dbg(DEBUG_ANY, |
150 | "ISDN_CMD_DIAL (driver: %d, ch: %ld, " | 208 | "ISDN_CMD_DIAL (phone: %s, msn: %s, si1: %d, si2: %d)", |
151 | "phone: %s, ownmsn: %s, si1: %d, si2: %d)", | ||
152 | cntrl->driver, cntrl->arg, | ||
153 | cntrl->parm.setup.phone, cntrl->parm.setup.eazmsn, | 209 | cntrl->parm.setup.phone, cntrl->parm.setup.eazmsn, |
154 | cntrl->parm.setup.si1, cntrl->parm.setup.si2); | 210 | cntrl->parm.setup.si1, cntrl->parm.setup.si2); |
155 | 211 | ||
156 | if (cntrl->arg >= cs->channels) { | 212 | if (ch >= cs->channels) { |
157 | dev_err(cs->dev, | 213 | dev_err(cs->dev, |
158 | "ISDN_CMD_DIAL: invalid channel (%d)\n", | 214 | "ISDN_CMD_DIAL: invalid channel (%d)\n", ch); |
159 | (int) cntrl->arg); | ||
160 | return -EINVAL; | 215 | return -EINVAL; |
161 | } | 216 | } |
162 | 217 | bcs = cs->bcs + ch; | |
163 | bcs = cs->bcs + cntrl->arg; | ||
164 | |||
165 | if (!gigaset_get_channel(bcs)) { | 218 | if (!gigaset_get_channel(bcs)) { |
166 | dev_err(cs->dev, "ISDN_CMD_DIAL: channel not free\n"); | 219 | dev_err(cs->dev, "ISDN_CMD_DIAL: channel not free\n"); |
167 | return -EBUSY; | 220 | return -EBUSY; |
168 | } | 221 | } |
169 | 222 | ||
170 | sp = kmalloc(sizeof *sp, GFP_ATOMIC); | 223 | commands = kzalloc(AT_NUM*(sizeof *commands), GFP_ATOMIC); |
171 | if (!sp) { | 224 | if (!commands) { |
172 | gigaset_free_channel(bcs); | 225 | gigaset_free_channel(bcs); |
173 | dev_err(cs->dev, "ISDN_CMD_DIAL: out of memory\n"); | 226 | dev_err(cs->dev, "ISDN_CMD_DIAL: out of memory\n"); |
174 | return -ENOMEM; | 227 | return -ENOMEM; |
175 | } | 228 | } |
176 | *sp = cntrl->parm.setup; | ||
177 | 229 | ||
178 | if (!gigaset_add_event(cs, &bcs->at_state, EV_DIAL, sp, | 230 | l = 3 + strlen(cntrl->parm.setup.phone); |
231 | commands[AT_DIAL] = kmalloc(l, GFP_ATOMIC); | ||
232 | if (!commands[AT_DIAL]) | ||
233 | goto oom; | ||
234 | if (cntrl->parm.setup.phone[0] == '*' && | ||
235 | cntrl->parm.setup.phone[1] == '*') { | ||
236 | /* internal call: translate ** prefix to CTP value */ | ||
237 | commands[AT_TYPE] = kstrdup("^SCTP=0\r", GFP_ATOMIC); | ||
238 | if (!commands[AT_TYPE]) | ||
239 | goto oom; | ||
240 | snprintf(commands[AT_DIAL], l, | ||
241 | "D%s\r", cntrl->parm.setup.phone+2); | ||
242 | } else { | ||
243 | commands[AT_TYPE] = kstrdup("^SCTP=1\r", GFP_ATOMIC); | ||
244 | if (!commands[AT_TYPE]) | ||
245 | goto oom; | ||
246 | snprintf(commands[AT_DIAL], l, | ||
247 | "D%s\r", cntrl->parm.setup.phone); | ||
248 | } | ||
249 | |||
250 | l = strlen(cntrl->parm.setup.eazmsn); | ||
251 | if (l) { | ||
252 | l += 8; | ||
253 | commands[AT_MSN] = kmalloc(l, GFP_ATOMIC); | ||
254 | if (!commands[AT_MSN]) | ||
255 | goto oom; | ||
256 | snprintf(commands[AT_MSN], l, "^SMSN=%s\r", | ||
257 | cntrl->parm.setup.eazmsn); | ||
258 | } | ||
259 | |||
260 | switch (cntrl->parm.setup.si1) { | ||
261 | case 1: /* audio */ | ||
262 | /* BC = 9090A3: 3.1 kHz audio, A-law */ | ||
263 | commands[AT_BC] = kstrdup("^SBC=9090A3\r", GFP_ATOMIC); | ||
264 | if (!commands[AT_BC]) | ||
265 | goto oom; | ||
266 | break; | ||
267 | case 7: /* data */ | ||
268 | default: /* hope the app knows what it is doing */ | ||
269 | /* BC = 8890: unrestricted digital information */ | ||
270 | commands[AT_BC] = kstrdup("^SBC=8890\r", GFP_ATOMIC); | ||
271 | if (!commands[AT_BC]) | ||
272 | goto oom; | ||
273 | } | ||
274 | /* ToDo: other si1 values, inspect si2, set HLC/LLC */ | ||
275 | |||
276 | commands[AT_PROTO] = kmalloc(9, GFP_ATOMIC); | ||
277 | if (!commands[AT_PROTO]) | ||
278 | goto oom; | ||
279 | snprintf(commands[AT_PROTO], 9, "^SBPR=%u\r", bcs->proto2); | ||
280 | |||
281 | commands[AT_ISO] = kmalloc(9, GFP_ATOMIC); | ||
282 | if (!commands[AT_ISO]) | ||
283 | goto oom; | ||
284 | snprintf(commands[AT_ISO], 9, "^SISO=%u\r", | ||
285 | (unsigned) bcs->channel + 1); | ||
286 | |||
287 | if (!gigaset_add_event(cs, &bcs->at_state, EV_DIAL, commands, | ||
179 | bcs->at_state.seq_index, NULL)) { | 288 | bcs->at_state.seq_index, NULL)) { |
180 | //FIXME what should we do? | 289 | for (i = 0; i < AT_NUM; ++i) |
181 | kfree(sp); | 290 | kfree(commands[i]); |
291 | kfree(commands); | ||
182 | gigaset_free_channel(bcs); | 292 | gigaset_free_channel(bcs); |
183 | return -ENOMEM; | 293 | return -ENOMEM; |
184 | } | 294 | } |
@@ -186,93 +296,83 @@ static int command_from_LL(isdn_ctrl *cntrl) | |||
186 | gig_dbg(DEBUG_CMD, "scheduling DIAL"); | 296 | gig_dbg(DEBUG_CMD, "scheduling DIAL"); |
187 | gigaset_schedule_event(cs); | 297 | gigaset_schedule_event(cs); |
188 | break; | 298 | break; |
189 | case ISDN_CMD_ACCEPTD: //FIXME | 299 | case ISDN_CMD_ACCEPTD: |
190 | gig_dbg(DEBUG_ANY, "ISDN_CMD_ACCEPTD"); | 300 | if (ch >= cs->channels) { |
191 | |||
192 | if (cntrl->arg >= cs->channels) { | ||
193 | dev_err(cs->dev, | 301 | dev_err(cs->dev, |
194 | "ISDN_CMD_ACCEPTD: invalid channel (%d)\n", | 302 | "ISDN_CMD_ACCEPTD: invalid channel (%d)\n", ch); |
195 | (int) cntrl->arg); | ||
196 | return -EINVAL; | 303 | return -EINVAL; |
197 | } | 304 | } |
198 | 305 | bcs = cs->bcs + ch; | |
199 | if (!gigaset_add_event(cs, &cs->bcs[cntrl->arg].at_state, | 306 | if (!gigaset_add_event(cs, &bcs->at_state, |
200 | EV_ACCEPT, NULL, 0, NULL)) { | 307 | EV_ACCEPT, NULL, 0, NULL)) |
201 | //FIXME what should we do? | ||
202 | return -ENOMEM; | 308 | return -ENOMEM; |
203 | } | ||
204 | 309 | ||
205 | gig_dbg(DEBUG_CMD, "scheduling ACCEPT"); | 310 | gig_dbg(DEBUG_CMD, "scheduling ACCEPT"); |
206 | gigaset_schedule_event(cs); | 311 | gigaset_schedule_event(cs); |
207 | 312 | ||
208 | break; | 313 | break; |
209 | case ISDN_CMD_ACCEPTB: | 314 | case ISDN_CMD_ACCEPTB: |
210 | gig_dbg(DEBUG_ANY, "ISDN_CMD_ACCEPTB"); | ||
211 | break; | 315 | break; |
212 | case ISDN_CMD_HANGUP: | 316 | case ISDN_CMD_HANGUP: |
213 | gig_dbg(DEBUG_ANY, "ISDN_CMD_HANGUP (ch: %d)", | 317 | if (ch >= cs->channels) { |
214 | (int) cntrl->arg); | ||
215 | |||
216 | if (cntrl->arg >= cs->channels) { | ||
217 | dev_err(cs->dev, | 318 | dev_err(cs->dev, |
218 | "ISDN_CMD_HANGUP: invalid channel (%d)\n", | 319 | "ISDN_CMD_HANGUP: invalid channel (%d)\n", ch); |
219 | (int) cntrl->arg); | ||
220 | return -EINVAL; | 320 | return -EINVAL; |
221 | } | 321 | } |
222 | 322 | bcs = cs->bcs + ch; | |
223 | if (!gigaset_add_event(cs, &cs->bcs[cntrl->arg].at_state, | 323 | if (!gigaset_add_event(cs, &bcs->at_state, |
224 | EV_HUP, NULL, 0, NULL)) { | 324 | EV_HUP, NULL, 0, NULL)) |
225 | //FIXME what should we do? | ||
226 | return -ENOMEM; | 325 | return -ENOMEM; |
227 | } | ||
228 | 326 | ||
229 | gig_dbg(DEBUG_CMD, "scheduling HUP"); | 327 | gig_dbg(DEBUG_CMD, "scheduling HUP"); |
230 | gigaset_schedule_event(cs); | 328 | gigaset_schedule_event(cs); |
231 | 329 | ||
232 | break; | 330 | break; |
233 | case ISDN_CMD_CLREAZ: /* Do not signal incoming signals */ //FIXME | 331 | case ISDN_CMD_CLREAZ: /* Do not signal incoming signals */ |
234 | gig_dbg(DEBUG_ANY, "ISDN_CMD_CLREAZ"); | 332 | dev_info(cs->dev, "ignoring ISDN_CMD_CLREAZ\n"); |
235 | break; | 333 | break; |
236 | case ISDN_CMD_SETEAZ: /* Signal incoming calls for given MSN */ //FIXME | 334 | case ISDN_CMD_SETEAZ: /* Signal incoming calls for given MSN */ |
237 | gig_dbg(DEBUG_ANY, | 335 | dev_info(cs->dev, "ignoring ISDN_CMD_SETEAZ (%s)\n", |
238 | "ISDN_CMD_SETEAZ (id: %d, ch: %ld, number: %s)", | 336 | cntrl->parm.num); |
239 | cntrl->driver, cntrl->arg, cntrl->parm.num); | ||
240 | break; | 337 | break; |
241 | case ISDN_CMD_SETL2: /* Set L2 to given protocol */ | 338 | case ISDN_CMD_SETL2: /* Set L2 to given protocol */ |
242 | gig_dbg(DEBUG_ANY, "ISDN_CMD_SETL2 (ch: %ld, proto: %lx)", | 339 | if (ch >= cs->channels) { |
243 | cntrl->arg & 0xff, (cntrl->arg >> 8)); | ||
244 | |||
245 | if ((cntrl->arg & 0xff) >= cs->channels) { | ||
246 | dev_err(cs->dev, | 340 | dev_err(cs->dev, |
247 | "ISDN_CMD_SETL2: invalid channel (%d)\n", | 341 | "ISDN_CMD_SETL2: invalid channel (%d)\n", ch); |
248 | (int) cntrl->arg & 0xff); | ||
249 | return -EINVAL; | 342 | return -EINVAL; |
250 | } | 343 | } |
251 | 344 | bcs = cs->bcs + ch; | |
252 | if (!gigaset_add_event(cs, &cs->bcs[cntrl->arg & 0xff].at_state, | 345 | if (bcs->chstate & CHS_D_UP) { |
253 | EV_PROTO_L2, NULL, cntrl->arg >> 8, | 346 | dev_err(cs->dev, |
254 | NULL)) { | 347 | "ISDN_CMD_SETL2: channel active (%d)\n", ch); |
255 | //FIXME what should we do? | 348 | return -EINVAL; |
256 | return -ENOMEM; | 349 | } |
350 | switch (cntrl->arg >> 8) { | ||
351 | case ISDN_PROTO_L2_HDLC: | ||
352 | gig_dbg(DEBUG_CMD, "ISDN_CMD_SETL2: setting L2_HDLC"); | ||
353 | bcs->proto2 = L2_HDLC; | ||
354 | break; | ||
355 | case ISDN_PROTO_L2_TRANS: | ||
356 | gig_dbg(DEBUG_CMD, "ISDN_CMD_SETL2: setting L2_VOICE"); | ||
357 | bcs->proto2 = L2_VOICE; | ||
358 | break; | ||
359 | default: | ||
360 | dev_err(cs->dev, | ||
361 | "ISDN_CMD_SETL2: unsupported protocol (%lu)\n", | ||
362 | cntrl->arg >> 8); | ||
363 | return -EINVAL; | ||
257 | } | 364 | } |
258 | |||
259 | gig_dbg(DEBUG_CMD, "scheduling PROTO_L2"); | ||
260 | gigaset_schedule_event(cs); | ||
261 | break; | 365 | break; |
262 | case ISDN_CMD_SETL3: /* Set L3 to given protocol */ | 366 | case ISDN_CMD_SETL3: /* Set L3 to given protocol */ |
263 | gig_dbg(DEBUG_ANY, "ISDN_CMD_SETL3 (ch: %ld, proto: %lx)", | 367 | if (ch >= cs->channels) { |
264 | cntrl->arg & 0xff, (cntrl->arg >> 8)); | ||
265 | |||
266 | if ((cntrl->arg & 0xff) >= cs->channels) { | ||
267 | dev_err(cs->dev, | 368 | dev_err(cs->dev, |
268 | "ISDN_CMD_SETL3: invalid channel (%d)\n", | 369 | "ISDN_CMD_SETL3: invalid channel (%d)\n", ch); |
269 | (int) cntrl->arg & 0xff); | ||
270 | return -EINVAL; | 370 | return -EINVAL; |
271 | } | 371 | } |
272 | 372 | ||
273 | if (cntrl->arg >> 8 != ISDN_PROTO_L3_TRANS) { | 373 | if (cntrl->arg >> 8 != ISDN_PROTO_L3_TRANS) { |
274 | dev_err(cs->dev, | 374 | dev_err(cs->dev, |
275 | "ISDN_CMD_SETL3: invalid protocol %lu\n", | 375 | "ISDN_CMD_SETL3: unsupported protocol (%lu)\n", |
276 | cntrl->arg >> 8); | 376 | cntrl->arg >> 8); |
277 | return -EINVAL; | 377 | return -EINVAL; |
278 | } | 378 | } |
@@ -324,149 +424,34 @@ static int command_from_LL(isdn_ctrl *cntrl) | |||
324 | } | 424 | } |
325 | 425 | ||
326 | return retval; | 426 | return retval; |
427 | |||
428 | oom: | ||
429 | dev_err(bcs->cs->dev, "out of memory\n"); | ||
430 | for (i = 0; i < AT_NUM; ++i) | ||
431 | kfree(commands[i]); | ||
432 | return -ENOMEM; | ||
327 | } | 433 | } |
328 | 434 | ||
329 | void gigaset_i4l_cmd(struct cardstate *cs, int cmd) | 435 | static void gigaset_i4l_cmd(struct cardstate *cs, int cmd) |
330 | { | 436 | { |
437 | isdn_if *iif = cs->iif; | ||
331 | isdn_ctrl command; | 438 | isdn_ctrl command; |
332 | 439 | ||
333 | command.driver = cs->myid; | 440 | command.driver = cs->myid; |
334 | command.command = cmd; | 441 | command.command = cmd; |
335 | command.arg = 0; | 442 | command.arg = 0; |
336 | cs->iif.statcallb(&command); | 443 | iif->statcallb(&command); |
337 | } | 444 | } |
338 | 445 | ||
339 | void gigaset_i4l_channel_cmd(struct bc_state *bcs, int cmd) | 446 | static void gigaset_i4l_channel_cmd(struct bc_state *bcs, int cmd) |
340 | { | 447 | { |
448 | isdn_if *iif = bcs->cs->iif; | ||
341 | isdn_ctrl command; | 449 | isdn_ctrl command; |
342 | 450 | ||
343 | command.driver = bcs->cs->myid; | 451 | command.driver = bcs->cs->myid; |
344 | command.command = cmd; | 452 | command.command = cmd; |
345 | command.arg = bcs->channel; | 453 | command.arg = bcs->channel; |
346 | bcs->cs->iif.statcallb(&command); | 454 | iif->statcallb(&command); |
347 | } | ||
348 | |||
349 | int gigaset_isdn_setup_dial(struct at_state_t *at_state, void *data) | ||
350 | { | ||
351 | struct bc_state *bcs = at_state->bcs; | ||
352 | unsigned proto; | ||
353 | const char *bc; | ||
354 | size_t length[AT_NUM]; | ||
355 | size_t l; | ||
356 | int i; | ||
357 | struct setup_parm *sp = data; | ||
358 | |||
359 | switch (bcs->proto2) { | ||
360 | case ISDN_PROTO_L2_HDLC: | ||
361 | proto = 1; /* 0: Bitsynchron, 1: HDLC, 2: voice */ | ||
362 | break; | ||
363 | case ISDN_PROTO_L2_TRANS: | ||
364 | proto = 2; /* 0: Bitsynchron, 1: HDLC, 2: voice */ | ||
365 | break; | ||
366 | default: | ||
367 | dev_err(bcs->cs->dev, "%s: invalid L2 protocol: %u\n", | ||
368 | __func__, bcs->proto2); | ||
369 | return -EINVAL; | ||
370 | } | ||
371 | |||
372 | switch (sp->si1) { | ||
373 | case 1: /* audio */ | ||
374 | bc = "9090A3"; /* 3.1 kHz audio, A-law */ | ||
375 | break; | ||
376 | case 7: /* data */ | ||
377 | default: /* hope the app knows what it is doing */ | ||
378 | bc = "8890"; /* unrestricted digital information */ | ||
379 | } | ||
380 | //FIXME add missing si1 values from 1TR6, inspect si2, set HLC/LLC | ||
381 | |||
382 | length[AT_DIAL ] = 1 + strlen(sp->phone) + 1 + 1; | ||
383 | l = strlen(sp->eazmsn); | ||
384 | length[AT_MSN ] = l ? 6 + l + 1 + 1 : 0; | ||
385 | length[AT_BC ] = 5 + strlen(bc) + 1 + 1; | ||
386 | length[AT_PROTO] = 6 + 1 + 1 + 1; /* proto: 1 character */ | ||
387 | length[AT_ISO ] = 6 + 1 + 1 + 1; /* channel: 1 character */ | ||
388 | length[AT_TYPE ] = 6 + 1 + 1 + 1; /* call type: 1 character */ | ||
389 | length[AT_HLC ] = 0; | ||
390 | |||
391 | for (i = 0; i < AT_NUM; ++i) { | ||
392 | kfree(bcs->commands[i]); | ||
393 | bcs->commands[i] = NULL; | ||
394 | if (length[i] && | ||
395 | !(bcs->commands[i] = kmalloc(length[i], GFP_ATOMIC))) { | ||
396 | dev_err(bcs->cs->dev, "out of memory\n"); | ||
397 | return -ENOMEM; | ||
398 | } | ||
399 | } | ||
400 | |||
401 | /* type = 1: extern, 0: intern, 2: recall, 3: door, 4: centrex */ | ||
402 | if (sp->phone[0] == '*' && sp->phone[1] == '*') { | ||
403 | /* internal call: translate ** prefix to CTP value */ | ||
404 | snprintf(bcs->commands[AT_DIAL], length[AT_DIAL], | ||
405 | "D%s\r", sp->phone+2); | ||
406 | strncpy(bcs->commands[AT_TYPE], "^SCTP=0\r", length[AT_TYPE]); | ||
407 | } else { | ||
408 | snprintf(bcs->commands[AT_DIAL], length[AT_DIAL], | ||
409 | "D%s\r", sp->phone); | ||
410 | strncpy(bcs->commands[AT_TYPE], "^SCTP=1\r", length[AT_TYPE]); | ||
411 | } | ||
412 | |||
413 | if (bcs->commands[AT_MSN]) | ||
414 | snprintf(bcs->commands[AT_MSN], length[AT_MSN], | ||
415 | "^SMSN=%s\r", sp->eazmsn); | ||
416 | snprintf(bcs->commands[AT_BC ], length[AT_BC ], | ||
417 | "^SBC=%s\r", bc); | ||
418 | snprintf(bcs->commands[AT_PROTO], length[AT_PROTO], | ||
419 | "^SBPR=%u\r", proto); | ||
420 | snprintf(bcs->commands[AT_ISO ], length[AT_ISO ], | ||
421 | "^SISO=%u\r", (unsigned)bcs->channel + 1); | ||
422 | |||
423 | return 0; | ||
424 | } | ||
425 | |||
426 | int gigaset_isdn_setup_accept(struct at_state_t *at_state) | ||
427 | { | ||
428 | unsigned proto; | ||
429 | size_t length[AT_NUM]; | ||
430 | int i; | ||
431 | struct bc_state *bcs = at_state->bcs; | ||
432 | |||
433 | switch (bcs->proto2) { | ||
434 | case ISDN_PROTO_L2_HDLC: | ||
435 | proto = 1; /* 0: Bitsynchron, 1: HDLC, 2: voice */ | ||
436 | break; | ||
437 | case ISDN_PROTO_L2_TRANS: | ||
438 | proto = 2; /* 0: Bitsynchron, 1: HDLC, 2: voice */ | ||
439 | break; | ||
440 | default: | ||
441 | dev_err(at_state->cs->dev, "%s: invalid protocol: %u\n", | ||
442 | __func__, bcs->proto2); | ||
443 | return -EINVAL; | ||
444 | } | ||
445 | |||
446 | length[AT_DIAL ] = 0; | ||
447 | length[AT_MSN ] = 0; | ||
448 | length[AT_BC ] = 0; | ||
449 | length[AT_PROTO] = 6 + 1 + 1 + 1; /* proto: 1 character */ | ||
450 | length[AT_ISO ] = 6 + 1 + 1 + 1; /* channel: 1 character */ | ||
451 | length[AT_TYPE ] = 0; | ||
452 | length[AT_HLC ] = 0; | ||
453 | |||
454 | for (i = 0; i < AT_NUM; ++i) { | ||
455 | kfree(bcs->commands[i]); | ||
456 | bcs->commands[i] = NULL; | ||
457 | if (length[i] && | ||
458 | !(bcs->commands[i] = kmalloc(length[i], GFP_ATOMIC))) { | ||
459 | dev_err(at_state->cs->dev, "out of memory\n"); | ||
460 | return -ENOMEM; | ||
461 | } | ||
462 | } | ||
463 | |||
464 | snprintf(bcs->commands[AT_PROTO], length[AT_PROTO], | ||
465 | "^SBPR=%u\r", proto); | ||
466 | snprintf(bcs->commands[AT_ISO ], length[AT_ISO ], | ||
467 | "^SISO=%u\r", (unsigned) bcs->channel + 1); | ||
468 | |||
469 | return 0; | ||
470 | } | 455 | } |
471 | 456 | ||
472 | /** | 457 | /** |
@@ -482,6 +467,7 @@ int gigaset_isdn_icall(struct at_state_t *at_state) | |||
482 | { | 467 | { |
483 | struct cardstate *cs = at_state->cs; | 468 | struct cardstate *cs = at_state->cs; |
484 | struct bc_state *bcs = at_state->bcs; | 469 | struct bc_state *bcs = at_state->bcs; |
470 | isdn_if *iif = cs->iif; | ||
485 | isdn_ctrl response; | 471 | isdn_ctrl response; |
486 | int retval; | 472 | int retval; |
487 | 473 | ||
@@ -531,7 +517,7 @@ int gigaset_isdn_icall(struct at_state_t *at_state) | |||
531 | response.arg = bcs->channel; //FIXME | 517 | response.arg = bcs->channel; //FIXME |
532 | } | 518 | } |
533 | response.driver = cs->myid; | 519 | response.driver = cs->myid; |
534 | retval = cs->iif.statcallb(&response); | 520 | retval = iif->statcallb(&response); |
535 | gig_dbg(DEBUG_CMD, "Response: %d", retval); | 521 | gig_dbg(DEBUG_CMD, "Response: %d", retval); |
536 | switch (retval) { | 522 | switch (retval) { |
537 | case 0: /* no takers */ | 523 | case 0: /* no takers */ |
@@ -560,16 +546,109 @@ int gigaset_isdn_icall(struct at_state_t *at_state) | |||
560 | } | 546 | } |
561 | } | 547 | } |
562 | 548 | ||
563 | /* Set Callback function pointer */ | 549 | /** |
564 | int gigaset_register_to_LL(struct cardstate *cs, const char *isdnid) | 550 | * gigaset_isdn_connD() - signal D channel connect |
551 | * @bcs: B channel descriptor structure. | ||
552 | * | ||
553 | * Called by main module to notify the LL that the D channel connection has | ||
554 | * been established. | ||
555 | */ | ||
556 | void gigaset_isdn_connD(struct bc_state *bcs) | ||
565 | { | 557 | { |
566 | isdn_if *iif = &cs->iif; | 558 | gig_dbg(DEBUG_CMD, "sending DCONN"); |
559 | gigaset_i4l_channel_cmd(bcs, ISDN_STAT_DCONN); | ||
560 | } | ||
567 | 561 | ||
568 | gig_dbg(DEBUG_ANY, "Register driver capabilities to LL"); | 562 | /** |
563 | * gigaset_isdn_hupD() - signal D channel hangup | ||
564 | * @bcs: B channel descriptor structure. | ||
565 | * | ||
566 | * Called by main module to notify the LL that the D channel connection has | ||
567 | * been shut down. | ||
568 | */ | ||
569 | void gigaset_isdn_hupD(struct bc_state *bcs) | ||
570 | { | ||
571 | gig_dbg(DEBUG_CMD, "sending DHUP"); | ||
572 | gigaset_i4l_channel_cmd(bcs, ISDN_STAT_DHUP); | ||
573 | } | ||
574 | |||
575 | /** | ||
576 | * gigaset_isdn_connB() - signal B channel connect | ||
577 | * @bcs: B channel descriptor structure. | ||
578 | * | ||
579 | * Called by main module to notify the LL that the B channel connection has | ||
580 | * been established. | ||
581 | */ | ||
582 | void gigaset_isdn_connB(struct bc_state *bcs) | ||
583 | { | ||
584 | gig_dbg(DEBUG_CMD, "sending BCONN"); | ||
585 | gigaset_i4l_channel_cmd(bcs, ISDN_STAT_BCONN); | ||
586 | } | ||
587 | |||
588 | /** | ||
589 | * gigaset_isdn_hupB() - signal B channel hangup | ||
590 | * @bcs: B channel descriptor structure. | ||
591 | * | ||
592 | * Called by main module to notify the LL that the B channel connection has | ||
593 | * been shut down. | ||
594 | */ | ||
595 | void gigaset_isdn_hupB(struct bc_state *bcs) | ||
596 | { | ||
597 | gig_dbg(DEBUG_CMD, "sending BHUP"); | ||
598 | gigaset_i4l_channel_cmd(bcs, ISDN_STAT_BHUP); | ||
599 | } | ||
600 | |||
601 | /** | ||
602 | * gigaset_isdn_start() - signal device availability | ||
603 | * @cs: device descriptor structure. | ||
604 | * | ||
605 | * Called by main module to notify the LL that the device is available for | ||
606 | * use. | ||
607 | */ | ||
608 | void gigaset_isdn_start(struct cardstate *cs) | ||
609 | { | ||
610 | gig_dbg(DEBUG_CMD, "sending RUN"); | ||
611 | gigaset_i4l_cmd(cs, ISDN_STAT_RUN); | ||
612 | } | ||
613 | |||
614 | /** | ||
615 | * gigaset_isdn_stop() - signal device unavailability | ||
616 | * @cs: device descriptor structure. | ||
617 | * | ||
618 | * Called by main module to notify the LL that the device is no longer | ||
619 | * available for use. | ||
620 | */ | ||
621 | void gigaset_isdn_stop(struct cardstate *cs) | ||
622 | { | ||
623 | gig_dbg(DEBUG_CMD, "sending STOP"); | ||
624 | gigaset_i4l_cmd(cs, ISDN_STAT_STOP); | ||
625 | } | ||
626 | |||
627 | /** | ||
628 | * gigaset_isdn_register() - register to LL | ||
629 | * @cs: device descriptor structure. | ||
630 | * @isdnid: device name. | ||
631 | * | ||
632 | * Called by main module to register the device with the LL. | ||
633 | * | ||
634 | * Return value: 1 for success, 0 for failure | ||
635 | */ | ||
636 | int gigaset_isdn_register(struct cardstate *cs, const char *isdnid) | ||
637 | { | ||
638 | isdn_if *iif; | ||
639 | |||
640 | pr_info("ISDN4Linux interface\n"); | ||
641 | |||
642 | iif = kmalloc(sizeof *iif, GFP_KERNEL); | ||
643 | if (!iif) { | ||
644 | pr_err("out of memory\n"); | ||
645 | return 0; | ||
646 | } | ||
569 | 647 | ||
570 | if (snprintf(iif->id, sizeof iif->id, "%s_%u", isdnid, cs->minor_index) | 648 | if (snprintf(iif->id, sizeof iif->id, "%s_%u", isdnid, cs->minor_index) |
571 | >= sizeof iif->id) { | 649 | >= sizeof iif->id) { |
572 | pr_err("ID too long: %s\n", isdnid); | 650 | pr_err("ID too long: %s\n", isdnid); |
651 | kfree(iif); | ||
573 | return 0; | 652 | return 0; |
574 | } | 653 | } |
575 | 654 | ||
@@ -593,9 +672,26 @@ int gigaset_register_to_LL(struct cardstate *cs, const char *isdnid) | |||
593 | 672 | ||
594 | if (!register_isdn(iif)) { | 673 | if (!register_isdn(iif)) { |
595 | pr_err("register_isdn failed\n"); | 674 | pr_err("register_isdn failed\n"); |
675 | kfree(iif); | ||
596 | return 0; | 676 | return 0; |
597 | } | 677 | } |
598 | 678 | ||
679 | cs->iif = iif; | ||
599 | cs->myid = iif->channels; /* Set my device id */ | 680 | cs->myid = iif->channels; /* Set my device id */ |
681 | cs->hw_hdr_len = HW_HDR_LEN; | ||
600 | return 1; | 682 | return 1; |
601 | } | 683 | } |
684 | |||
685 | /** | ||
686 | * gigaset_isdn_unregister() - unregister from LL | ||
687 | * @cs: device descriptor structure. | ||
688 | * | ||
689 | * Called by main module to unregister the device from the LL. | ||
690 | */ | ||
691 | void gigaset_isdn_unregister(struct cardstate *cs) | ||
692 | { | ||
693 | gig_dbg(DEBUG_CMD, "sending UNLOAD"); | ||
694 | gigaset_i4l_cmd(cs, ISDN_STAT_UNLOAD); | ||
695 | kfree(cs->iif); | ||
696 | cs->iif = NULL; | ||
697 | } | ||