aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/isdn/gigaset/bas-gigaset.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/isdn/gigaset/bas-gigaset.c')
-rw-r--r--drivers/isdn/gigaset/bas-gigaset.c87
1 files changed, 52 insertions, 35 deletions
diff --git a/drivers/isdn/gigaset/bas-gigaset.c b/drivers/isdn/gigaset/bas-gigaset.c
index 781c4041f7b0..5ed1d99eb9f3 100644
--- a/drivers/isdn/gigaset/bas-gigaset.c
+++ b/drivers/isdn/gigaset/bas-gigaset.c
@@ -134,6 +134,7 @@ struct bas_cardstate {
134#define BS_ATRDPEND 0x040 /* urb_cmd_in in use */ 134#define BS_ATRDPEND 0x040 /* urb_cmd_in in use */
135#define BS_ATWRPEND 0x080 /* urb_cmd_out in use */ 135#define BS_ATWRPEND 0x080 /* urb_cmd_out in use */
136#define BS_SUSPEND 0x100 /* USB port suspended */ 136#define BS_SUSPEND 0x100 /* USB port suspended */
137#define BS_RESETTING 0x200 /* waiting for HD_RESET_INTERRUPT_PIPE_ACK */
137 138
138 139
139static struct gigaset_driver *driver = NULL; 140static struct gigaset_driver *driver = NULL;
@@ -319,6 +320,21 @@ static int gigaset_set_line_ctrl(struct cardstate *cs, unsigned cflag)
319 return -EINVAL; 320 return -EINVAL;
320} 321}
321 322
323/* set/clear bits in base connection state, return previous state
324 */
325static inline int update_basstate(struct bas_cardstate *ucs,
326 int set, int clear)
327{
328 unsigned long flags;
329 int state;
330
331 spin_lock_irqsave(&ucs->lock, flags);
332 state = ucs->basstate;
333 ucs->basstate = (state & ~clear) | set;
334 spin_unlock_irqrestore(&ucs->lock, flags);
335 return state;
336}
337
322/* error_hangup 338/* error_hangup
323 * hang up any existing connection because of an unrecoverable error 339 * hang up any existing connection because of an unrecoverable error
324 * This function may be called from any context and takes care of scheduling 340 * This function may be called from any context and takes care of scheduling
@@ -350,12 +366,9 @@ static inline void error_hangup(struct bc_state *bcs)
350 */ 366 */
351static inline void error_reset(struct cardstate *cs) 367static inline void error_reset(struct cardstate *cs)
352{ 368{
353 /* close AT command channel to recover (ignore errors) */ 369 /* reset interrupt pipe to recover (ignore errors) */
354 req_submit(cs->bcs, HD_CLOSE_ATCHANNEL, 0, BAS_TIMEOUT); 370 update_basstate(cs->hw.bas, BS_RESETTING, 0);
355 371 req_submit(cs->bcs, HD_RESET_INTERRUPT_PIPE, 0, BAS_TIMEOUT);
356 //FIXME try to recover without bothering the user
357 dev_err(cs->dev,
358 "unrecoverable error - please disconnect Gigaset base to reset\n");
359} 372}
360 373
361/* check_pending 374/* check_pending
@@ -398,8 +411,13 @@ static void check_pending(struct bas_cardstate *ucs)
398 case HD_DEVICE_INIT_ACK: /* no reply expected */ 411 case HD_DEVICE_INIT_ACK: /* no reply expected */
399 ucs->pending = 0; 412 ucs->pending = 0;
400 break; 413 break;
401 /* HD_READ_ATMESSAGE, HD_WRITE_ATMESSAGE, HD_RESET_INTERRUPTPIPE 414 case HD_RESET_INTERRUPT_PIPE:
402 * are handled separately and should never end up here 415 if (!(ucs->basstate & BS_RESETTING))
416 ucs->pending = 0;
417 break;
418 /*
419 * HD_READ_ATMESSAGE and HD_WRITE_ATMESSAGE are handled separately
420 * and should never end up here
403 */ 421 */
404 default: 422 default:
405 dev_warn(&ucs->interface->dev, 423 dev_warn(&ucs->interface->dev,
@@ -449,21 +467,6 @@ static void cmd_in_timeout(unsigned long data)
449 error_reset(cs); 467 error_reset(cs);
450} 468}
451 469
452/* set/clear bits in base connection state, return previous state
453 */
454inline static int update_basstate(struct bas_cardstate *ucs,
455 int set, int clear)
456{
457 unsigned long flags;
458 int state;
459
460 spin_lock_irqsave(&ucs->lock, flags);
461 state = ucs->basstate;
462 ucs->basstate = (state & ~clear) | set;
463 spin_unlock_irqrestore(&ucs->lock, flags);
464 return state;
465}
466
467/* read_ctrl_callback 470/* read_ctrl_callback
468 * USB completion handler for control pipe input 471 * USB completion handler for control pipe input
469 * called by the USB subsystem in interrupt context 472 * called by the USB subsystem in interrupt context
@@ -762,7 +765,8 @@ static void read_int_callback(struct urb *urb)
762 break; 765 break;
763 766
764 case HD_RESET_INTERRUPT_PIPE_ACK: 767 case HD_RESET_INTERRUPT_PIPE_ACK:
765 gig_dbg(DEBUG_USBREQ, "HD_RESET_INTERRUPT_PIPE_ACK"); 768 update_basstate(ucs, 0, BS_RESETTING);
769 dev_notice(cs->dev, "interrupt pipe reset\n");
766 break; 770 break;
767 771
768 case HD_SUSPEND_END: 772 case HD_SUSPEND_END:
@@ -1331,28 +1335,24 @@ static void read_iso_tasklet(unsigned long data)
1331 rcvbuf = urb->transfer_buffer; 1335 rcvbuf = urb->transfer_buffer;
1332 totleft = urb->actual_length; 1336 totleft = urb->actual_length;
1333 for (frame = 0; totleft > 0 && frame < BAS_NUMFRAMES; frame++) { 1337 for (frame = 0; totleft > 0 && frame < BAS_NUMFRAMES; frame++) {
1334 if (unlikely(urb->iso_frame_desc[frame].status)) { 1338 numbytes = urb->iso_frame_desc[frame].actual_length;
1339 if (unlikely(urb->iso_frame_desc[frame].status))
1335 dev_warn(cs->dev, 1340 dev_warn(cs->dev,
1336 "isochronous read: frame %d: %s\n", 1341 "isochronous read: frame %d[%d]: %s\n",
1337 frame, 1342 frame, numbytes,
1338 get_usb_statmsg( 1343 get_usb_statmsg(
1339 urb->iso_frame_desc[frame].status)); 1344 urb->iso_frame_desc[frame].status));
1340 break; 1345 if (unlikely(numbytes > BAS_MAXFRAME))
1341 }
1342 numbytes = urb->iso_frame_desc[frame].actual_length;
1343 if (unlikely(numbytes > BAS_MAXFRAME)) {
1344 dev_warn(cs->dev, 1346 dev_warn(cs->dev,
1345 "isochronous read: frame %d: " 1347 "isochronous read: frame %d: "
1346 "numbytes (%d) > BAS_MAXFRAME\n", 1348 "numbytes (%d) > BAS_MAXFRAME\n",
1347 frame, numbytes); 1349 frame, numbytes);
1348 break;
1349 }
1350 if (unlikely(numbytes > totleft)) { 1350 if (unlikely(numbytes > totleft)) {
1351 dev_warn(cs->dev, 1351 dev_warn(cs->dev,
1352 "isochronous read: frame %d: " 1352 "isochronous read: frame %d: "
1353 "numbytes (%d) > totleft (%d)\n", 1353 "numbytes (%d) > totleft (%d)\n",
1354 frame, numbytes, totleft); 1354 frame, numbytes, totleft);
1355 break; 1355 numbytes = totleft;
1356 } 1356 }
1357 offset = urb->iso_frame_desc[frame].offset; 1357 offset = urb->iso_frame_desc[frame].offset;
1358 if (unlikely(offset + numbytes > BAS_INBUFSIZE)) { 1358 if (unlikely(offset + numbytes > BAS_INBUFSIZE)) {
@@ -1361,7 +1361,7 @@ static void read_iso_tasklet(unsigned long data)
1361 "offset (%d) + numbytes (%d) " 1361 "offset (%d) + numbytes (%d) "
1362 "> BAS_INBUFSIZE\n", 1362 "> BAS_INBUFSIZE\n",
1363 frame, offset, numbytes); 1363 frame, offset, numbytes);
1364 break; 1364 numbytes = BAS_INBUFSIZE - offset;
1365 } 1365 }
1366 gigaset_isoc_receive(rcvbuf + offset, numbytes, bcs); 1366 gigaset_isoc_receive(rcvbuf + offset, numbytes, bcs);
1367 totleft -= numbytes; 1367 totleft -= numbytes;
@@ -1433,6 +1433,7 @@ static void req_timeout(unsigned long data)
1433 1433
1434 case HD_CLOSE_ATCHANNEL: 1434 case HD_CLOSE_ATCHANNEL:
1435 dev_err(bcs->cs->dev, "timeout closing AT channel\n"); 1435 dev_err(bcs->cs->dev, "timeout closing AT channel\n");
1436 error_reset(bcs->cs);
1436 break; 1437 break;
1437 1438
1438 case HD_CLOSE_B2CHANNEL: 1439 case HD_CLOSE_B2CHANNEL:
@@ -1442,6 +1443,13 @@ static void req_timeout(unsigned long data)
1442 error_reset(bcs->cs); 1443 error_reset(bcs->cs);
1443 break; 1444 break;
1444 1445
1446 case HD_RESET_INTERRUPT_PIPE:
1447 /* error recovery escalation */
1448 dev_err(bcs->cs->dev,
1449 "reset interrupt pipe timeout, attempting USB reset\n");
1450 usb_queue_reset_device(bcs->cs->hw.bas->interface);
1451 break;
1452
1445 default: 1453 default:
1446 dev_warn(bcs->cs->dev, "request 0x%02x timed out, clearing\n", 1454 dev_warn(bcs->cs->dev, "request 0x%02x timed out, clearing\n",
1447 pending); 1455 pending);
@@ -1934,6 +1942,15 @@ static int gigaset_write_cmd(struct cardstate *cs,
1934 goto notqueued; 1942 goto notqueued;
1935 } 1943 }
1936 1944
1945 /* translate "+++" escape sequence sent as a single separate command
1946 * into "close AT channel" command for error recovery
1947 * The next command will reopen the AT channel automatically.
1948 */
1949 if (len == 3 && !memcmp(buf, "+++", 3)) {
1950 rc = req_submit(cs->bcs, HD_CLOSE_ATCHANNEL, 0, BAS_TIMEOUT);
1951 goto notqueued;
1952 }
1953
1937 if (len > IF_WRITEBUF) 1954 if (len > IF_WRITEBUF)
1938 len = IF_WRITEBUF; 1955 len = IF_WRITEBUF;
1939 if (!(cb = kmalloc(sizeof(struct cmdbuf_t) + len, GFP_ATOMIC))) { 1956 if (!(cb = kmalloc(sizeof(struct cmdbuf_t) + len, GFP_ATOMIC))) {