ev-layer.c

来自「linux 内核源代码」· C语言 代码 · 共 1,981 行 · 第 1/4 页

C
1,981
字号
	cid = isdn_getnum(s);	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;	const 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;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?