ev-layer.c

来自「LINUX 2.6.17.4的源码」· C语言 代码 · 共 1,986 行 · 第 1/4 页

C
1,986
字号
				--count;		}		break;#endif	case ACT_DEBUG:		gig_dbg(DEBUG_ANY, "%s: resp_code %d in ConState %d",			__func__, ev->type, at_state->ConState);		break;	case ACT_WARN:		dev_warn(cs->dev, "%s: resp_code %d in ConState %d!\n",			 __func__, ev->type, at_state->ConState);		break;	case ACT_ZCAU:		dev_warn(cs->dev, "cause code %04x in connection state %d.\n",			 ev->parameter, at_state->ConState);		break;	/* events from the LL */	case ACT_DIAL:		start_dial(at_state, ev->ptr, ev->parameter);		break;	case ACT_ACCEPT:		start_accept(at_state);		break;	case ACT_PROTO_L2:		gig_dbg(DEBUG_CMD, "set protocol to %u",			(unsigned) ev->parameter);		at_state->bcs->proto2 = ev->parameter;		break;	case ACT_HUP:		at_state->pending_commands |= PC_HUP;		atomic_set(&cs->commands_pending, 1);		gig_dbg(DEBUG_CMD, "Scheduling PC_HUP");		break;	/* hotplug events */	case ACT_STOP:		do_stop(cs);		break;	case ACT_START:		do_start(cs);		break;	/* events from the interface */ // FIXME without ACT_xxxx?	case ACT_IF_LOCK:		cs->cmd_result = ev->parameter ? do_lock(cs) : do_unlock(cs);		cs->waiting = 0;		wake_up(&cs->waitqueue);		break;	case ACT_IF_VER:		if (ev->parameter != 0)			cs->cmd_result = -EINVAL;		else if (cs->gotfwver != 1) {			cs->cmd_result = -ENOENT;		} else {			memcpy(ev->arg, cs->fwver, sizeof cs->fwver);			cs->cmd_result = 0;		}		cs->waiting = 0;		wake_up(&cs->waitqueue);		break;	/* events from the proc file system */ // FIXME without ACT_xxxx?	case ACT_PROC_CIDMODE:		spin_lock_irqsave(&cs->lock, flags);		if (ev->parameter != cs->cidmode) {			cs->cidmode = ev->parameter;			if (ev->parameter) {				cs->at_state.pending_commands |= PC_CIDMODE;				gig_dbg(DEBUG_CMD, "Scheduling PC_CIDMODE");			} else {				cs->at_state.pending_commands |= PC_UMMODE;				gig_dbg(DEBUG_CMD, "Scheduling PC_UMMODE");			}			atomic_set(&cs->commands_pending, 1);		}		spin_unlock_irqrestore(&cs->lock, flags);		cs->waiting = 0;		wake_up(&cs->waitqueue);		break;	/* events from the hardware drivers */	case ACT_NOTIFY_BC_DOWN:		bchannel_down(bcs);		break;	case ACT_NOTIFY_BC_UP:		bchannel_up(bcs);		break;	case ACT_SHUTDOWN:		do_shutdown(cs);		break;	default:		if (action >= ACT_CMD && action < ACT_CMD + AT_NUM) {			*pp_command = at_state->bcs->commands[action - ACT_CMD];			if (!*pp_command) {				*p_genresp = 1;				*p_resp_code = RSP_NULL;			}		} else			dev_err(cs->dev, "%s: action==%d!\n", __func__, action);	}}/* 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;	int sendcid;	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) {			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_ANY, "event for invalid at_state %p",				at_state);			return;		}	}	gig_dbg(DEBUG_CMD, "connection state %d, event %d",		at_state->ConState, ev->type);	bcs = at_state->bcs;	sendcid = at_state->cid;	/* 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_ANY, "old timeout");		} else if (!at_state->waiting)			gig_dbg(DEBUG_ANY, "timeout occurred");		else			gig_dbg(DEBUG_ANY, "stopped waiting");	}	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)			break; /* may be freed after disconnect */	}	if (at_state) {		/* 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; //FIXME			at_state->timer_active = 0; //FIXME			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/*rep->command*/) {				if (cs->connected)					send_command(cs, p_command,						     sendcid, cs->dle,						     GFP_ATOMIC);				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);		}	}}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);}static void process_command_flags(struct cardstate *cs){	struct at_state_t *at_state = NULL;	struct bc_state *bcs;	int i;	int sequence;	unsigned long flags;	atomic_set(&cs->commands_pending, 0);	if (cs->cur_at_seq) {		gig_dbg(DEBUG_CMD, "not searching scheduled commands: busy");		return;	}	gig_dbg(DEBUG_CMD, "searching scheduled commands");	sequence = SEQ_NONE;	/* clear pending_commands and hangup channels on shutdown */	if (cs->at_state.pending_commands & PC_SHUTDOWN) {		cs->at_state.pending_commands &= ~PC_CIDMODE;		for (i = 0; i < cs->channels; ++i) {			bcs = cs->bcs + i;			at_state = &bcs->at_state;			at_state->pending_commands &=				~(PC_DLE1 | PC_ACCEPT | PC_DIAL);			if (at_state->cid > 0)				at_state->pending_commands |= PC_HUP;			if (at_state->pending_commands & PC_CID) {				at_state->pending_commands |= PC_NOCID;				at_state->pending_commands &= ~PC_CID;			}		}	}	/* clear pending_commands and hangup channels on reset */	if (cs->at_state.pending_commands & PC_INIT) {		cs->at_state.pending_commands &= ~PC_CIDMODE;		for (i = 0; i < cs->channels; ++i) {			bcs = cs->bcs + i;			at_state = &bcs->at_state;			at_state->pending_commands &=				~(PC_DLE1 | PC_ACCEPT | PC_DIAL);			if (at_state->cid > 0)				at_state->pending_commands |= PC_HUP;			if (atomic_read(&cs->mstate) == MS_RECOVER) {				if (at_state->pending_commands & PC_CID) {					at_state->pending_commands |= PC_NOCID;					at_state->pending_commands &= ~PC_CID;				}			}		}	}	/* only switch back to unimodem mode, if no commands are pending and no channels are up */	spin_lock_irqsave(&cs->lock, flags);	if (cs->at_state.pending_commands == PC_UMMODE	    && !cs->cidmode	    && list_empty(&cs->temp_at_states)	    && atomic_read(&cs->mode) == M_CID) {		sequence = SEQ_UMMODE;		at_state = &cs->at_state;		for (i = 0; i < cs->channels; ++i) {			bcs = cs->bcs + i;			if (bcs->at_state.pending_commands ||			    bcs->at_state.cid > 0) {				sequence = SEQ_NONE;				break;			}		}	}	spin_unlock_irqrestore(&cs->lock, flags);	cs->at_state.pending_commands &= ~PC_UMMODE;	if (sequence != SEQ_NONE) {		schedule_sequence(cs, at_state, sequence);		return;	}	for (i = 0; i < cs->channels; ++i) {		bcs = cs->bcs + i;		if (bcs->at_state.pending_commands & PC_HUP) {			bcs->at_state.pending_commands &= ~PC_HUP;			if (bcs->at_state.pending_commands & PC_CID) {				/* not yet dialing: PC_NOCID is sufficient */				bcs->at_state.pending_commands |= PC_NOCID;				bcs->at_state.pending_commands &= ~PC_CID;			} else {				schedule_sequence(cs, &bcs->at_state, SEQ_HUP);				return;			}		}		if (bcs->at_state.pending_commands & PC_NOCID) {			bcs->at_state.pending_commands &= ~PC_NOCID;			cs->curchannel = bcs->channel;			schedule_sequence(cs, &cs->at_state, SEQ_NOCID);			return;		} else if (bcs->at_state.pending_commands & PC_DLE0) {			bcs->at_state.pending_commands &= ~PC_DLE0;			cs->curchannel = bcs->channel;			schedule_sequence(cs, &cs->at_state, SEQ_DLE0);			return;		}	}	list_for_each_entry(at_state, &cs->temp_at_states, list)		if (at_state->pending_commands & PC_HUP) {			at_state->pending_commands &= ~PC_HUP;			schedule_sequence(cs, at_state, SEQ_HUP);			return;		}	if (cs->at_state.pending_commands & PC_INIT) {		cs->at_state.pending_commands &= ~PC_INIT;		cs->dle = 0; //FIXME		cs->inbuf->inputstate = INS_command;		//FIXME reset card state (or -> LOCK0)?		schedule_sequence(cs, &cs->at_state, SEQ_INIT);		return;	}	if (cs->at_state.pending_commands & PC_SHUTDOWN) {		cs->at_state.pending_commands &= ~PC_SHUTDOWN;		schedule_sequence(cs, &cs->at_state, SEQ_SHUTDOWN);		return;	}	if (cs->at_state.pending_commands & PC_CIDMODE) {		cs->at_state.pending_commands &= ~PC_CIDMODE;		if (atomic_read(&cs->mode) == M_UNIMODEM) {			cs->retry_count = 1;			schedule_sequence(cs, &cs->at_state, SEQ_CIDMODE);			return;		}	}	for (i = 0; i < cs->channels; ++i) {		bcs = cs->bcs + i;		if (bcs->at_state.pending_commands & PC_DLE1) {			bcs->at_state.pending_commands &= ~PC_DLE1;			cs->curchannel = bcs->channel;			schedule_sequence(cs, &cs->at_state, SEQ_DLE1);			return;		}		if (bcs->at_state.pending_commands & PC_ACCEPT) {			bcs->at_state.pending_commands &= ~PC_ACCEPT;			schedule_sequence(cs, &bcs->at_state, SEQ_ACCEPT);			return;		}		if (bcs->at_state.pending_commands & PC_DIAL) {			bcs->at_state.pending_commands &= ~PC_DIAL;			schedule_sequence(cs, &bcs->at_state, SEQ_DIAL);			return;		}		if (bcs->at_state.pending_commands & PC_CID) {			switch (atomic_read(&cs->mode)) {			case M_UNIMODEM:				cs->at_state.pending_commands |= PC_CIDMODE;				gig_dbg(DEBUG_CMD, "Scheduling PC_CIDMODE");				atomic_set(&cs->commands_pending, 1);				return;#ifdef GIG_MAYINITONDIAL			case M_UNKNOWN:				schedule_init(cs, MS_INIT);				return;#endif			}			bcs->at_state.pending_commands &= ~PC_CID;			cs->curchannel = bcs->channel;#ifdef GIG_RETRYCID			cs->retry_count = 2;#else			cs->retry_count = 1;#endif			schedule_sequence(cs, &cs->at_state, SEQ_CID);			return;		}	}}static void process_events(struct cardstate *cs){	struct event_t *ev;	unsigned head, tail;	int i;	int check_flags = 0;	int was_busy;	unsigned long flags;	spin_lock_irqsave(&cs->ev_lock, flags);	head = cs->ev_head;	for (i = 0; i < 2 * MAX_EVENTS; ++i) {		tail = cs->ev_tail;		if (tail == head) {			if (!check_flags && !atomic_read(&cs->commands_pending))				break;			check_flags = 0;			spin_unlock_irqrestore(&cs->ev_lock, flags);			process_command_flags(cs);			spin_lock_irqsave(&cs->ev_lock, flags);			tail = cs->ev_tail;			if (tail == head) {				if (!atomic_read(&cs->commands_pending))					break;				continue;			}		}		ev = cs->events + head;		was_busy = cs->cur_at_seq != SEQ_NONE;		spin_unlock_irqrestore(&cs->ev_lock, flags);		process_event(cs, ev);		spin_lock_irqsave(&cs->ev_lock, flags);		kfree(ev->ptr);		ev->ptr = NULL;		if (was_busy && cs->cur_at_seq == SEQ_NONE)			check_flags = 1;		head = (head + 1) % MAX_EVENTS;		cs->ev_head = head;	}	spin_unlock_irqrestore(&cs->ev_lock, flags);	if (i == 2 * MAX_EVENTS) {		dev_err(cs->dev,			"infinite loop in process_events; aborting.\n");	}}/* tasklet scheduled on any event received from the Gigaset device * parameter: *	data	ISDN controller state structure */void gigaset_handle_event(unsigned long data){	struct cardstate *cs = (struct cardstate *) data;	/* handle incoming data on control/common channel */	if (atomic_read(&cs->inbuf->head) != atomic_read(&cs->inbuf->tail)) {		gig_dbg(DEBUG_INTR, "processing new data");		cs->ops->handle_input(cs->inbuf);	}	process_events(cs);}

⌨️ 快捷键说明

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