static int if_version(struct cardstate *cs, unsigned arg[4]) { static const unsigned version[4] = GIG_VERSION; static const unsigned compat[4] = GIG_COMPAT; unsigned cmd = arg[0]; gig_dbg(DEBUG_IF, "%u: if_version (%d)", cs->minor_index, cmd); switch (cmd) { case GIGVER_DRIVER: memcpy(arg, version, sizeof version); return 0; case GIGVER_COMPAT: memcpy(arg, compat, sizeof compat); return 0; case GIGVER_FWBASE: cs->waiting = 1; if (!gigaset_add_event(cs, &cs->at_state, EV_IF_VER, NULL, 0, arg)) { cs->waiting = 0; return -ENOMEM; } gigaset_schedule_event(cs); wait_event(cs->waitqueue, !cs->waiting); if (cs->cmd_result >= 0) return 0; return cs->cmd_result; default: return -EINVAL; } }
static ssize_t set_cidmode(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct cardstate *cs = dev_get_drvdata(dev); long int value; char *end; value = simple_strtol(buf, &end, 0); while (*end) if (!isspace(*end++)) return -EINVAL; if (value < 0 || value > 1) return -EINVAL; if (mutex_lock_interruptible(&cs->mutex)) return -ERESTARTSYS; cs->waiting = 1; if (!gigaset_add_event(cs, &cs->at_state, EV_PROC_CIDMODE, NULL, value, NULL)) { cs->waiting = 0; mutex_unlock(&cs->mutex); return -ENOMEM; } gig_dbg(DEBUG_CMD, "scheduling PROC_CIDMODE"); gigaset_schedule_event(cs); wait_event(cs->waitqueue, !cs->waiting); mutex_unlock(&cs->mutex); return count; }
static int if_lock(struct cardstate *cs, int *arg) { int cmd = *arg; gig_dbg(DEBUG_IF, "%u: if_lock (%d)", cs->minor_index, cmd); if (cmd > 1) return -EINVAL; if (cmd < 0) { *arg = cs->mstate == MS_LOCKED; return 0; } if (!cmd && cs->mstate == MS_LOCKED && cs->connected) { cs->ops->set_modem_ctrl(cs, 0, TIOCM_DTR|TIOCM_RTS); cs->ops->baud_rate(cs, B115200); cs->ops->set_line_ctrl(cs, CS8); cs->control_state = TIOCM_DTR|TIOCM_RTS; } cs->waiting = 1; if (!gigaset_add_event(cs, &cs->at_state, EV_IF_LOCK, NULL, cmd, NULL)) { cs->waiting = 0; return -ENOMEM; } gig_dbg(DEBUG_CMD, "scheduling IF_LOCK"); gigaset_schedule_event(cs); wait_event(cs->waitqueue, !cs->waiting); if (cs->cmd_result >= 0) { *arg = cs->cmd_result; return 0; } return cs->cmd_result; }
static void schedule_sequence(struct cardstate *cs, struct at_state_t *at_state, int sequence) { cs->cur_at_seq = sequence; gigaset_add_event(cs, at_state, RSP_INIT, NULL, sequence, NULL); }
/* State machine to do the calling and hangup procedure */ static void process_event(struct cardstate *cs, struct event_t *ev) { struct bc_state *bcs; char *p_command = NULL; struct reply_t *rep; int rcode; int genresp = 0; int resp_code = RSP_ERROR; struct at_state_t *at_state; int index; int curact; unsigned long flags; if (ev->cid >= 0) { at_state = at_state_from_cid(cs, ev->cid); if (!at_state) { gig_dbg(DEBUG_EVENT, "event %d for invalid cid %d", ev->type, ev->cid); gigaset_add_event(cs, &cs->at_state, RSP_WRONG_CID, NULL, 0, NULL); return; } } else { at_state = ev->at_state; if (at_state_invalid(cs, at_state)) { gig_dbg(DEBUG_EVENT, "event for invalid at_state %p", at_state); return; } } gig_dbg(DEBUG_EVENT, "connection state %d, event %d", at_state->ConState, ev->type); bcs = at_state->bcs; /* Setting the pointer to the dial array */ rep = at_state->replystruct; spin_lock_irqsave(&cs->lock, flags); if (ev->type == EV_TIMEOUT) { if (ev->parameter != at_state->timer_index || !at_state->timer_active) { ev->type = RSP_NONE; /* old timeout */ gig_dbg(DEBUG_EVENT, "old timeout"); } else { if (at_state->waiting) gig_dbg(DEBUG_EVENT, "stopped waiting"); else gig_dbg(DEBUG_EVENT, "timeout occurred"); } } spin_unlock_irqrestore(&cs->lock, flags); /* if the response belongs to a variable in at_state->int_var[VAR_XXXX] or at_state->str_var[STR_XXXX], set it */ if (ev->type >= RSP_VAR && ev->type < RSP_VAR + VAR_NUM) { index = ev->type - RSP_VAR; at_state->int_var[index] = ev->parameter; } else if (ev->type >= RSP_STR && ev->type < RSP_STR + STR_NUM) { index = ev->type - RSP_STR; kfree(at_state->str_var[index]); at_state->str_var[index] = ev->ptr; ev->ptr = NULL; /* prevent process_events() from deallocating ptr */ } if (ev->type == EV_TIMEOUT || ev->type == RSP_STRING) at_state->getstring = 0; /* Search row in dial array which matches modem response and current constate */ for (;; rep++) { rcode = rep->resp_code; if (rcode == RSP_LAST) { /* found nothing...*/ dev_warn(cs->dev, "%s: rcode=RSP_LAST: " "resp_code %d in ConState %d!\n", __func__, ev->type, at_state->ConState); return; } if ((rcode == RSP_ANY || rcode == ev->type) && ((int) at_state->ConState >= rep->min_ConState) && (rep->max_ConState < 0 || (int) at_state->ConState <= rep->max_ConState) && (rep->parameter < 0 || rep->parameter == ev->parameter)) break; } p_command = rep->command; at_state->waiting = 0; for (curact = 0; curact < MAXACT; ++curact) { /* The row tells us what we should do .. */ do_action(rep->action[curact], cs, bcs, &at_state, &p_command, &genresp, &resp_code, ev); if (!at_state) /* at_state destroyed by disconnect */ return; } /* Jump to the next con-state regarding the array */ if (rep->new_ConState >= 0) at_state->ConState = rep->new_ConState; if (genresp) { spin_lock_irqsave(&cs->lock, flags); at_state->timer_expires = 0; at_state->timer_active = 0; spin_unlock_irqrestore(&cs->lock, flags); gigaset_add_event(cs, at_state, resp_code, NULL, 0, NULL); } else { /* Send command to modem if not NULL... */ if (p_command) { if (cs->connected) send_command(cs, p_command, at_state); else gigaset_add_event(cs, at_state, RSP_NODEV, NULL, 0, NULL); } spin_lock_irqsave(&cs->lock, flags); if (!rep->timeout) { at_state->timer_expires = 0; at_state->timer_active = 0; } else if (rep->timeout > 0) { /* new timeout */ at_state->timer_expires = rep->timeout * 10; at_state->timer_active = 1; ++at_state->timer_index; } spin_unlock_irqrestore(&cs->lock, flags); } }
/** * gigaset_handle_modem_response() - process received modem response * @cs: device descriptor structure. * * Called by asyncdata/isocdata if a block of data received from the * device must be processed as a modem command response. The data is * already in the cs structure. */ void gigaset_handle_modem_response(struct cardstate *cs) { unsigned char *argv[MAX_REC_PARAMS + 1]; int params; int i, j; const struct resp_type_t *rt; const struct zsau_resp_t *zr; 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; 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_EVENT, "CMD received: %s", argv[0]); if (cid) { --params; gig_dbg(DEBUG_EVENT, "CID: %s", argv[params]); } gig_dbg(DEBUG_EVENT, "available params: %d", params - 1); for (j = 1; j < params; j++) gig_dbg(DEBUG_EVENT, "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_NONE; gig_dbg(DEBUG_EVENT, "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; } for (zr = zsau_resp; zr->str; ++zr) if (!strcmp(argv[curarg], zr->str)) break; event->parameter = zr->code; if (!zr->str) 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; } gig_dbg(DEBUG_EVENT, "string==%s", event->ptr ? (char *) event->ptr : "NULL"); break; case RT_ZCAU: event->parameter = -1; if (curarg + 1 < params) { u8 type, value; i = kstrtou8(argv[curarg++], 16, &type); j = kstrtou8(argv[curarg++], 16, &value); if (i == 0 && j == 0) event->parameter = (type << 8) | value; } else curarg = params - 1; break; case RT_NUMBER: if (curarg >= params || kstrtoint(argv[curarg++], 10, &event->parameter)) event->parameter = -1; gig_dbg(DEBUG_EVENT, "parameter==%d", event->parameter); 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_EVENT, "invalid number of processed parameters: %d/%d", curarg, params); }