static void gsm_queue()

in n_gsm.c [1772:1909]


static void gsm_queue(struct gsm_mux *gsm)
{
	struct gsm_dlci *dlci;
	u8 cr;
	int address;
	int i, j, k, address_tmp;
	/* We have to sneak a look at the packet body to do the FCS.
	   A somewhat layering violation in the spec */

	if ((gsm->control & ~PF) == UI)
		gsm->fcs = gsm_fcs_add_block(gsm->fcs, gsm->buf, gsm->len);
	if (gsm->encoding == 0) {
		/* WARNING: gsm->received_fcs is used for
		gsm->encoding = 0 only.
		In this case it contain the last piece of data
		required to generate final CRC */
		gsm->fcs = gsm_fcs_add(gsm->fcs, gsm->received_fcs);
	}
	if (gsm->fcs != GOOD_FCS) {
		gsm->bad_fcs++;
		if (debug & 4)
			pr_debug("BAD FCS %02x\n", gsm->fcs);
		return;
	}
	address = gsm->address >> 1;
	if (address >= NUM_DLCI)
		goto invalid;

	cr = gsm->address & 1;		/* C/R bit */

	gsm_print_packet("<--", address, cr, gsm->control, gsm->buf, gsm->len);

	cr ^= 1 - gsm->initiator;	/* Flip so 1 always means command */
	dlci = gsm->dlci[address];

	switch (gsm->control) {
	case SABM|PF:
		if (cr == 1)
			goto invalid;
		if (dlci == NULL)
			dlci = gsm_dlci_alloc(gsm, address);
		if (dlci == NULL)
			return;
		if (dlci->dead)
			gsm_response(gsm, address, DM|PF);
		else {
			gsm_response(gsm, address, UA|PF);
			gsm_dlci_open(dlci);
			/* Save dlci open address */
			if (address) {
				addr_open[addr_cnt] = address;
				addr_cnt++;
			}
		}
		break;
	case DISC|PF:
		if (cr == 1)
			goto invalid;
		if (dlci == NULL || dlci->state == DLCI_CLOSED) {
			gsm_response(gsm, address, DM|PF);
			return;
		}
		/* Real close complete */
		if (!address) {
			if (addr_cnt > 0) {
				for (i = 0; i < addr_cnt; i++) {
					address = addr_open[i];
					dlci = gsm->dlci[address];
					gsm_dlci_close(dlci);
					addr_open[i] = 0;
				}
			}
			dlci = gsm->dlci[0];
			gsm_dlci_close(dlci);
			addr_cnt = 0;
			gsm_response(gsm, 0, UA|PF);
		} else {
			gsm_response(gsm, address, UA|PF);
			gsm_dlci_close(dlci);
			/* clear dlci address */
			for (j = 0; j < addr_cnt; j++) {
				address_tmp = addr_open[j];
				if (address_tmp == address) {
					for (k = j; k < addr_cnt; k++)
						addr_open[k] = addr_open[k+1];
					addr_cnt--;
					break;
				}
			}
		}
		break;
	case UA:
	case UA|PF:
		if (cr == 0 || dlci == NULL)
			break;
		switch (dlci->state) {
		case DLCI_CLOSING:
			gsm_dlci_close(dlci);
			break;
		case DLCI_OPENING:
			gsm_dlci_open(dlci);
			break;
		default:
			pr_debug("%s: unhandled state: %d\n", __func__,
					dlci->state);
			break;
		}
		break;
	case DM:	/* DM can be valid unsolicited */
	case DM|PF:
		if (cr)
			goto invalid;
		if (dlci == NULL)
			return;
		gsm_dlci_close(dlci);
		break;
	case UI:
	case UI|PF:
	case UIH:
	case UIH|PF:
#if 0
		if (cr)
			goto invalid;
#endif
		if (dlci == NULL || dlci->state != DLCI_OPEN) {
			gsm_command(gsm, address, DM|PF);
			return;
		}
		dlci->data(dlci, gsm->buf, gsm->len);
		break;
	default:
		goto invalid;
	}
	return;
invalid:
	gsm->malformed++;
	return;
}