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;
}