ev-layer.c
来自「LINUX 2.6.17.4的源码」· C语言 代码 · 共 1,986 行 · 第 1/4 页
C
1,986 行
if (cid < 0) return 0; /* CID not numeric */ if (cid < 1 || cid > 65535) return -1; /* CID out of range */ return cid; //FIXME is ;<digit>+ at end of non-CID response really impossible?}/* This function will be called via task queue from the callback handler. * We received a modem response and have to handle it.. */void gigaset_handle_modem_response(struct cardstate *cs){ unsigned char *argv[MAX_REC_PARAMS + 1]; int params; int i, j; struct resp_type_t *rt; int curarg; unsigned long flags; unsigned next, tail, head; struct event_t *event; int resp_code; int param_type; int abort; size_t len; int cid; int rawstring; len = cs->cbytes; if (!len) { /* ignore additional LFs/CRs (M10x config mode or cx100) */ gig_dbg(DEBUG_MCMD, "skipped EOL [%02X]", cs->respdata[len]); return; } cs->respdata[len] = 0; gig_dbg(DEBUG_TRANSCMD, "raw string: '%s'", cs->respdata); argv[0] = cs->respdata; params = 1; if (cs->at_state.getstring) { /* getstring only allowed without cid at the moment */ cs->at_state.getstring = 0; rawstring = 1; cid = 0; } else { /* parse line */ for (i = 0; i < len; i++) switch (cs->respdata[i]) { case ';': case ',': case '=': if (params > MAX_REC_PARAMS) { dev_warn(cs->dev, "too many parameters in response\n"); /* need last parameter (might be CID) */ params--; } argv[params++] = cs->respdata + i + 1; } rawstring = 0; cid = params > 1 ? cid_of_response(argv[params-1]) : 0; if (cid < 0) { gigaset_add_event(cs, &cs->at_state, RSP_INVAL, NULL, 0, NULL); return; } for (j = 1; j < params; ++j) argv[j][-1] = 0; gig_dbg(DEBUG_TRANSCMD, "CMD received: %s", argv[0]); if (cid) { --params; gig_dbg(DEBUG_TRANSCMD, "CID: %s", argv[params]); } gig_dbg(DEBUG_TRANSCMD, "available params: %d", params - 1); for (j = 1; j < params; j++) gig_dbg(DEBUG_TRANSCMD, "param %d: %s", j, argv[j]); } spin_lock_irqsave(&cs->ev_lock, flags); head = cs->ev_head; tail = cs->ev_tail; abort = 1; curarg = 0; while (curarg < params) { next = (tail + 1) % MAX_EVENTS; if (unlikely(next == head)) { dev_err(cs->dev, "event queue full\n"); break; } event = cs->events + tail; event->at_state = NULL; event->cid = cid; event->ptr = NULL; event->arg = NULL; tail = next; if (rawstring) { resp_code = RSP_STRING; param_type = RT_STRING; } else { for (rt = resp_type; rt->response; ++rt) if (!strcmp(argv[curarg], rt->response)) break; if (!rt->response) { event->type = RSP_UNKNOWN; dev_warn(cs->dev, "unknown modem response: %s\n", argv[curarg]); break; } resp_code = rt->resp_code; param_type = rt->type; ++curarg; } event->type = resp_code; switch (param_type) { case RT_NOTHING: break; case RT_RING: if (!cid) { dev_err(cs->dev, "received RING without CID!\n"); event->type = RSP_INVAL; abort = 1; } else { event->cid = 0; event->parameter = cid; abort = 0; } break; case RT_ZSAU: if (curarg >= params) { event->parameter = ZSAU_NONE; break; } if (!strcmp(argv[curarg], "OUTGOING_CALL_PROCEEDING")) event->parameter = ZSAU_OUTGOING_CALL_PROCEEDING; else if (!strcmp(argv[curarg], "CALL_DELIVERED")) event->parameter = ZSAU_CALL_DELIVERED; else if (!strcmp(argv[curarg], "ACTIVE")) event->parameter = ZSAU_ACTIVE; else if (!strcmp(argv[curarg], "DISCONNECT_IND")) event->parameter = ZSAU_DISCONNECT_IND; else if (!strcmp(argv[curarg], "NULL")) event->parameter = ZSAU_NULL; else if (!strcmp(argv[curarg], "DISCONNECT_REQ")) event->parameter = ZSAU_DISCONNECT_REQ; else { event->parameter = ZSAU_UNKNOWN; dev_warn(cs->dev, "%s: unknown parameter %s after ZSAU\n", __func__, argv[curarg]); } ++curarg; break; case RT_STRING: if (curarg < params) { event->ptr = kstrdup(argv[curarg], GFP_ATOMIC); if (!event->ptr) dev_err(cs->dev, "out of memory\n"); ++curarg; }#ifdef CONFIG_GIGASET_DEBUG if (!event->ptr) gig_dbg(DEBUG_CMD, "string==NULL"); else gig_dbg(DEBUG_CMD, "string==%s", (char *) event->ptr);#endif break; case RT_ZCAU: event->parameter = -1; if (curarg + 1 < params) { i = isdn_gethex(argv[curarg]); j = isdn_gethex(argv[curarg + 1]); if (i >= 0 && i < 256 && j >= 0 && j < 256) event->parameter = (unsigned) i << 8 | j; curarg += 2; } else curarg = params - 1; break; case RT_NUMBER: case RT_HEX: if (curarg < params) { if (param_type == RT_HEX) event->parameter = isdn_gethex(argv[curarg]); else event->parameter = isdn_getnum(argv[curarg]); ++curarg; } else event->parameter = -1;#ifdef CONFIG_GIGASET_DEBUG gig_dbg(DEBUG_CMD, "parameter==%d", event->parameter);#endif break; } if (resp_code == RSP_ZDLE) cs->dle = event->parameter; if (abort) break; } cs->ev_tail = tail; spin_unlock_irqrestore(&cs->ev_lock, flags); if (curarg != params) gig_dbg(DEBUG_ANY, "invalid number of processed parameters: %d/%d", curarg, params);}EXPORT_SYMBOL_GPL(gigaset_handle_modem_response);/* disconnect * process closing of connection associated with given AT state structure */static void disconnect(struct at_state_t **at_state_p){ unsigned long flags; struct bc_state *bcs = (*at_state_p)->bcs; struct cardstate *cs = (*at_state_p)->cs; spin_lock_irqsave(&cs->lock, flags); ++(*at_state_p)->seq_index; /* revert to selected idle mode */ if (!cs->cidmode) { cs->at_state.pending_commands |= PC_UMMODE; atomic_set(&cs->commands_pending, 1); //FIXME gig_dbg(DEBUG_CMD, "Scheduling PC_UMMODE"); } spin_unlock_irqrestore(&cs->lock, flags); if (bcs) { /* B channel assigned: invoke hardware specific handler */ cs->ops->close_bchannel(bcs); } else { /* no B channel assigned: just deallocate */ spin_lock_irqsave(&cs->lock, flags); list_del(&(*at_state_p)->list); kfree(*at_state_p); *at_state_p = NULL; spin_unlock_irqrestore(&cs->lock, flags); }}/* get_free_channel * get a free AT state structure: either one of those associated with the * B channels of the Gigaset device, or if none of those is available, * a newly allocated one with bcs=NULL * The structure should be freed by calling disconnect() after use. */static inline struct at_state_t *get_free_channel(struct cardstate *cs, int cid)/* cids: >0: siemens-cid 0: without cid -1: no cid assigned yet*/{ unsigned long flags; int i; struct at_state_t *ret; for (i = 0; i < cs->channels; ++i) if (gigaset_get_channel(cs->bcs + i)) { ret = &cs->bcs[i].at_state; ret->cid = cid; return ret; } spin_lock_irqsave(&cs->lock, flags); ret = kmalloc(sizeof(struct at_state_t), GFP_ATOMIC); if (ret) { gigaset_at_init(ret, NULL, cs, cid); list_add(&ret->list, &cs->temp_at_states); } spin_unlock_irqrestore(&cs->lock, flags); return ret;}static void init_failed(struct cardstate *cs, int mode){ int i; struct at_state_t *at_state; cs->at_state.pending_commands &= ~PC_INIT; atomic_set(&cs->mode, mode); atomic_set(&cs->mstate, MS_UNINITIALIZED); gigaset_free_channels(cs); for (i = 0; i < cs->channels; ++i) { at_state = &cs->bcs[i].at_state; if (at_state->pending_commands & PC_CID) { at_state->pending_commands &= ~PC_CID; at_state->pending_commands |= PC_NOCID; atomic_set(&cs->commands_pending, 1); } }}static void schedule_init(struct cardstate *cs, int state){ if (cs->at_state.pending_commands & PC_INIT) { gig_dbg(DEBUG_CMD, "not scheduling PC_INIT again"); return; } atomic_set(&cs->mstate, state); atomic_set(&cs->mode, M_UNKNOWN); gigaset_block_channels(cs); cs->at_state.pending_commands |= PC_INIT; atomic_set(&cs->commands_pending, 1); gig_dbg(DEBUG_CMD, "Scheduling PC_INIT");}/* Add "AT" to a command, add the cid, dle encode it, send the result to the hardware. */static void send_command(struct cardstate *cs, const char *cmd, int cid, int dle, gfp_t kmallocflags){ size_t cmdlen, buflen; char *cmdpos, *cmdbuf, *cmdtail; cmdlen = strlen(cmd); buflen = 11 + cmdlen; if (unlikely(buflen <= cmdlen)) { dev_err(cs->dev, "integer overflow in buflen\n"); return; } cmdbuf = kmalloc(buflen, kmallocflags); if (unlikely(!cmdbuf)) { dev_err(cs->dev, "out of memory\n"); return; } cmdpos = cmdbuf + 9; cmdtail = cmdpos + cmdlen; memcpy(cmdpos, cmd, cmdlen); if (cid > 0 && cid <= 65535) { do { *--cmdpos = '0' + cid % 10; cid /= 10; ++cmdlen; } while (cid); } cmdlen += 2; *--cmdpos = 'T'; *--cmdpos = 'A'; if (dle) { cmdlen += 4; *--cmdpos = '('; *--cmdpos = 0x10; *cmdtail++ = 0x10; *cmdtail++ = ')'; } cs->ops->write_cmd(cs, cmdpos, cmdlen, NULL); kfree(cmdbuf);}static struct at_state_t *at_state_from_cid(struct cardstate *cs, int cid){ struct at_state_t *at_state; int i; unsigned long flags; if (cid == 0) return &cs->at_state; for (i = 0; i < cs->channels; ++i) if (cid == cs->bcs[i].at_state.cid) return &cs->bcs[i].at_state; spin_lock_irqsave(&cs->lock, flags); list_for_each_entry(at_state, &cs->temp_at_states, list) if (cid == at_state->cid) { spin_unlock_irqrestore(&cs->lock, flags); return at_state; } spin_unlock_irqrestore(&cs->lock, flags); return NULL;}static void bchannel_down(struct bc_state *bcs){ if (bcs->chstate & CHS_B_UP) { bcs->chstate &= ~CHS_B_UP; gigaset_i4l_channel_cmd(bcs, ISDN_STAT_BHUP); } if (bcs->chstate & (CHS_D_UP | CHS_NOTIFY_LL)) { bcs->chstate &= ~(CHS_D_UP | CHS_NOTIFY_LL); gigaset_i4l_channel_cmd(bcs, ISDN_STAT_DHUP); } gigaset_free_channel(bcs); gigaset_bcs_reinit(bcs);}static void bchannel_up(struct bc_state *bcs){ if (!(bcs->chstate & CHS_D_UP)) { dev_notice(bcs->cs->dev, "%s: D channel not up\n", __func__); bcs->chstate |= CHS_D_UP; gigaset_i4l_channel_cmd(bcs, ISDN_STAT_DCONN); } if (bcs->chstate & CHS_B_UP) { dev_notice(bcs->cs->dev, "%s: B channel already up\n", __func__); return; } bcs->chstate |= CHS_B_UP; gigaset_i4l_channel_cmd(bcs, ISDN_STAT_BCONN);}static void start_dial(struct at_state_t *at_state, void *data, unsigned seq_index){ struct bc_state *bcs = at_state->bcs; struct cardstate *cs = at_state->cs; int retval; unsigned long flags; bcs->chstate |= CHS_NOTIFY_LL; spin_lock_irqsave(&cs->lock, flags); if (at_state->seq_index != seq_index) { spin_unlock_irqrestore(&cs->lock, flags); goto error; } spin_unlock_irqrestore(&cs->lock, flags); retval = gigaset_isdn_setup_dial(at_state, data); if (retval != 0) goto error; at_state->pending_commands |= PC_CID; gig_dbg(DEBUG_CMD, "Scheduling PC_CID"); atomic_set(&cs->commands_pending, 1); return;error: at_state->pending_commands |= PC_NOCID; gig_dbg(DEBUG_CMD, "Scheduling PC_NOCID"); atomic_set(&cs->commands_pending, 1); return;}static void start_accept(struct at_state_t *at_state){ struct cardstate *cs = at_state->cs; int retval; retval = gigaset_isdn_setup_accept(at_state); if (retval == 0) { at_state->pending_commands |= PC_ACCEPT; gig_dbg(DEBUG_CMD, "Scheduling PC_ACCEPT"); atomic_set(&cs->commands_pending, 1); } else { //FIXME at_state->pending_commands |= PC_HUP; gig_dbg(DEBUG_CMD, "Scheduling PC_HUP"); atomic_set(&cs->commands_pending, 1); }}static void do_start(struct cardstate *cs){ gigaset_free_channels(cs); if (atomic_read(&cs->mstate) != MS_LOCKED) schedule_init(cs, MS_INIT); cs->isdn_up = 1; gigaset_i4l_cmd(cs, ISDN_STAT_RUN); // FIXME: not in locked mode
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?