|
|
|
@ -73,6 +73,8 @@ module_param(debug, int, 0600);
|
|
|
|
|
*/
|
|
|
|
|
#define MAX_MRU 1500
|
|
|
|
|
#define MAX_MTU 1500
|
|
|
|
|
/* SOF, ADDR, CTRL, LEN1, LEN2, ..., FCS, EOF */
|
|
|
|
|
#define PROT_OVERHEAD 7
|
|
|
|
|
#define GSM_NET_TX_TIMEOUT (HZ*10)
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
@ -219,7 +221,6 @@ struct gsm_mux {
|
|
|
|
|
int encoding;
|
|
|
|
|
u8 control;
|
|
|
|
|
u8 fcs;
|
|
|
|
|
u8 received_fcs;
|
|
|
|
|
u8 *txframe; /* TX framing buffer */
|
|
|
|
|
|
|
|
|
|
/* Method for the receiver side */
|
|
|
|
@ -231,6 +232,7 @@ struct gsm_mux {
|
|
|
|
|
int initiator; /* Did we initiate connection */
|
|
|
|
|
bool dead; /* Has the mux been shut down */
|
|
|
|
|
struct gsm_dlci *dlci[NUM_DLCI];
|
|
|
|
|
int old_c_iflag; /* termios c_iflag value before attach */
|
|
|
|
|
bool constipated; /* Asked by remote to shut up */
|
|
|
|
|
|
|
|
|
|
spinlock_t tx_lock;
|
|
|
|
@ -271,10 +273,6 @@ static DEFINE_SPINLOCK(gsm_mux_lock);
|
|
|
|
|
|
|
|
|
|
static struct tty_driver *gsm_tty_driver;
|
|
|
|
|
|
|
|
|
|
/* Save dlci open address */
|
|
|
|
|
static int addr_open[256] = { 0 };
|
|
|
|
|
/* Save dlci open count */
|
|
|
|
|
static int addr_cnt;
|
|
|
|
|
/*
|
|
|
|
|
* This section of the driver logic implements the GSM encodings
|
|
|
|
|
* both the basic and the 'advanced'. Reliable transport is not
|
|
|
|
@ -369,6 +367,7 @@ static const u8 gsm_fcs8[256] = {
|
|
|
|
|
#define GOOD_FCS 0xCF
|
|
|
|
|
|
|
|
|
|
static int gsmld_output(struct gsm_mux *gsm, u8 *data, int len);
|
|
|
|
|
static int gsm_modem_update(struct gsm_dlci *dlci, u8 brk);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* gsm_fcs_add - update FCS
|
|
|
|
@ -832,7 +831,7 @@ static int gsm_dlci_data_output(struct gsm_mux *gsm, struct gsm_dlci *dlci)
|
|
|
|
|
break;
|
|
|
|
|
case 2: /* Unstructed with modem bits.
|
|
|
|
|
Always one byte as we never send inline break data */
|
|
|
|
|
*dp++ = gsm_encode_modem(dlci);
|
|
|
|
|
*dp++ = (gsm_encode_modem(dlci) << 1) | EA;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
WARN_ON(kfifo_out_locked(&dlci->fifo, dp , len, &dlci->lock) != len);
|
|
|
|
@ -916,6 +915,66 @@ static int gsm_dlci_data_output_framed(struct gsm_mux *gsm,
|
|
|
|
|
return size;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* gsm_dlci_modem_output - try and push modem status out of a DLCI
|
|
|
|
|
* @gsm: mux
|
|
|
|
|
* @dlci: the DLCI to pull modem status from
|
|
|
|
|
* @brk: break signal
|
|
|
|
|
*
|
|
|
|
|
* Push an empty frame in to the transmit queue to update the modem status
|
|
|
|
|
* bits and to transmit an optional break.
|
|
|
|
|
*
|
|
|
|
|
* Caller must hold the tx_lock of the mux.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
static int gsm_dlci_modem_output(struct gsm_mux *gsm, struct gsm_dlci *dlci,
|
|
|
|
|
u8 brk)
|
|
|
|
|
{
|
|
|
|
|
u8 *dp = NULL;
|
|
|
|
|
struct gsm_msg *msg;
|
|
|
|
|
int size = 0;
|
|
|
|
|
|
|
|
|
|
/* for modem bits without break data */
|
|
|
|
|
switch (dlci->adaption) {
|
|
|
|
|
case 1: /* Unstructured */
|
|
|
|
|
break;
|
|
|
|
|
case 2: /* Unstructured with modem bits. */
|
|
|
|
|
size++;
|
|
|
|
|
if (brk > 0)
|
|
|
|
|
size++;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
pr_err("%s: unsupported adaption %d\n", __func__,
|
|
|
|
|
dlci->adaption);
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
msg = gsm_data_alloc(gsm, dlci->addr, size, gsm->ftype);
|
|
|
|
|
if (!msg) {
|
|
|
|
|
pr_err("%s: gsm_data_alloc error", __func__);
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
}
|
|
|
|
|
dp = msg->data;
|
|
|
|
|
switch (dlci->adaption) {
|
|
|
|
|
case 1: /* Unstructured */
|
|
|
|
|
break;
|
|
|
|
|
case 2: /* Unstructured with modem bits. */
|
|
|
|
|
if (brk == 0) {
|
|
|
|
|
*dp++ = (gsm_encode_modem(dlci) << 1) | EA;
|
|
|
|
|
} else {
|
|
|
|
|
*dp++ = gsm_encode_modem(dlci) << 1;
|
|
|
|
|
*dp++ = (brk << 4) | 2 | EA; /* Length, Break, EA */
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
/* Handled above */
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
__gsm_data_queue(dlci, msg);
|
|
|
|
|
return size;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* gsm_dlci_data_sweep - look for data to send
|
|
|
|
|
* @gsm: the GSM mux
|
|
|
|
@ -1093,7 +1152,6 @@ static void gsm_control_modem(struct gsm_mux *gsm, const u8 *data, int clen)
|
|
|
|
|
{
|
|
|
|
|
unsigned int addr = 0;
|
|
|
|
|
unsigned int modem = 0;
|
|
|
|
|
unsigned int brk = 0;
|
|
|
|
|
struct gsm_dlci *dlci;
|
|
|
|
|
int len = clen;
|
|
|
|
|
int slen;
|
|
|
|
@ -1123,17 +1181,8 @@ static void gsm_control_modem(struct gsm_mux *gsm, const u8 *data, int clen)
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
len--;
|
|
|
|
|
if (len > 0) {
|
|
|
|
|
while (gsm_read_ea(&brk, *dp++) == 0) {
|
|
|
|
|
len--;
|
|
|
|
|
if (len == 0)
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
modem <<= 7;
|
|
|
|
|
modem |= (brk & 0x7f);
|
|
|
|
|
}
|
|
|
|
|
tty = tty_port_tty_get(&dlci->port);
|
|
|
|
|
gsm_process_modem(tty, dlci, modem, slen);
|
|
|
|
|
gsm_process_modem(tty, dlci, modem, slen - len);
|
|
|
|
|
if (tty) {
|
|
|
|
|
tty_wakeup(tty);
|
|
|
|
|
tty_kref_put(tty);
|
|
|
|
@ -1193,7 +1242,6 @@ static void gsm_control_rls(struct gsm_mux *gsm, const u8 *data, int clen)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void gsm_dlci_begin_close(struct gsm_dlci *dlci);
|
|
|
|
|
static void gsm_dlci_close(struct gsm_dlci *dlci);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* gsm_control_message - DLCI 0 control processing
|
|
|
|
@ -1212,28 +1260,15 @@ static void gsm_control_message(struct gsm_mux *gsm, unsigned int command,
|
|
|
|
|
{
|
|
|
|
|
u8 buf[1];
|
|
|
|
|
unsigned long flags;
|
|
|
|
|
struct gsm_dlci *dlci;
|
|
|
|
|
int i;
|
|
|
|
|
int address;
|
|
|
|
|
|
|
|
|
|
switch (command) {
|
|
|
|
|
case CMD_CLD: {
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
struct gsm_dlci *dlci = gsm->dlci[0];
|
|
|
|
|
/* Modem wishes to close down */
|
|
|
|
|
dlci = gsm->dlci[0];
|
|
|
|
|
if (dlci) {
|
|
|
|
|
dlci->dead = true;
|
|
|
|
|
gsm->dead = true;
|
|
|
|
|
gsm_dlci_close(dlci);
|
|
|
|
|
addr_cnt = 0;
|
|
|
|
|
gsm_response(gsm, 0, UA|PF);
|
|
|
|
|
gsm_dlci_begin_close(dlci);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
@ -1326,11 +1361,12 @@ static void gsm_control_response(struct gsm_mux *gsm, unsigned int command,
|
|
|
|
|
|
|
|
|
|
static void gsm_control_transmit(struct gsm_mux *gsm, struct gsm_control *ctrl)
|
|
|
|
|
{
|
|
|
|
|
struct gsm_msg *msg = gsm_data_alloc(gsm, 0, ctrl->len + 1, gsm->ftype);
|
|
|
|
|
struct gsm_msg *msg = gsm_data_alloc(gsm, 0, ctrl->len + 2, gsm->ftype);
|
|
|
|
|
if (msg == NULL)
|
|
|
|
|
return;
|
|
|
|
|
msg->data[0] = (ctrl->cmd << 1) | 2 | EA; /* command */
|
|
|
|
|
memcpy(msg->data + 1, ctrl->data, ctrl->len);
|
|
|
|
|
msg->data[0] = (ctrl->cmd << 1) | CR | EA; /* command */
|
|
|
|
|
msg->data[1] = (ctrl->len << 1) | EA;
|
|
|
|
|
memcpy(msg->data + 2, ctrl->data, ctrl->len);
|
|
|
|
|
gsm_data_queue(gsm->dlci[0], msg);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -1353,7 +1389,6 @@ static void gsm_control_retransmit(struct timer_list *t)
|
|
|
|
|
spin_lock_irqsave(&gsm->control_lock, flags);
|
|
|
|
|
ctrl = gsm->pending_cmd;
|
|
|
|
|
if (ctrl) {
|
|
|
|
|
gsm->cretries--;
|
|
|
|
|
if (gsm->cretries == 0) {
|
|
|
|
|
gsm->pending_cmd = NULL;
|
|
|
|
|
ctrl->error = -ETIMEDOUT;
|
|
|
|
@ -1362,6 +1397,7 @@ static void gsm_control_retransmit(struct timer_list *t)
|
|
|
|
|
wake_up(&gsm->event);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
gsm->cretries--;
|
|
|
|
|
gsm_control_transmit(gsm, ctrl);
|
|
|
|
|
mod_timer(&gsm->t2_timer, jiffies + gsm->t2 * HZ / 100);
|
|
|
|
|
}
|
|
|
|
@ -1402,7 +1438,7 @@ static struct gsm_control *gsm_control_send(struct gsm_mux *gsm,
|
|
|
|
|
|
|
|
|
|
/* If DLCI0 is in ADM mode skip retries, it won't respond */
|
|
|
|
|
if (gsm->dlci[0]->mode == DLCI_MODE_ADM)
|
|
|
|
|
gsm->cretries = 1;
|
|
|
|
|
gsm->cretries = 0;
|
|
|
|
|
else
|
|
|
|
|
gsm->cretries = gsm->n2;
|
|
|
|
|
|
|
|
|
@ -1450,20 +1486,22 @@ static int gsm_control_wait(struct gsm_mux *gsm, struct gsm_control *control)
|
|
|
|
|
|
|
|
|
|
static void gsm_dlci_close(struct gsm_dlci *dlci)
|
|
|
|
|
{
|
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
|
|
del_timer(&dlci->t1);
|
|
|
|
|
if (debug & 8)
|
|
|
|
|
pr_debug("DLCI %d goes closed.\n", dlci->addr);
|
|
|
|
|
dlci->state = DLCI_CLOSED;
|
|
|
|
|
if (dlci->addr != 0) {
|
|
|
|
|
tty_port_tty_hangup(&dlci->port, false);
|
|
|
|
|
spin_lock_irqsave(&dlci->lock, flags);
|
|
|
|
|
kfifo_reset(&dlci->fifo);
|
|
|
|
|
spin_unlock_irqrestore(&dlci->lock, flags);
|
|
|
|
|
/* Ensure that gsmtty_open() can return. */
|
|
|
|
|
tty_port_set_initialized(&dlci->port, 0);
|
|
|
|
|
wake_up_interruptible(&dlci->port.open_wait);
|
|
|
|
|
} else
|
|
|
|
|
dlci->gsm->dead = true;
|
|
|
|
|
/* Unregister gsmtty driver,report gsmtty dev remove uevent for user */
|
|
|
|
|
tty_unregister_device(gsm_tty_driver, dlci->addr);
|
|
|
|
|
wake_up(&dlci->gsm->event);
|
|
|
|
|
/* A DLCI 0 close is a MUX termination so we need to kick that
|
|
|
|
|
back to userspace somehow */
|
|
|
|
@ -1485,8 +1523,9 @@ static void gsm_dlci_open(struct gsm_dlci *dlci)
|
|
|
|
|
dlci->state = DLCI_OPEN;
|
|
|
|
|
if (debug & 8)
|
|
|
|
|
pr_debug("DLCI %d goes open.\n", dlci->addr);
|
|
|
|
|
/* Register gsmtty driver,report gsmtty dev add uevent for user */
|
|
|
|
|
tty_register_device(gsm_tty_driver, dlci->addr, NULL);
|
|
|
|
|
/* Send current modem state */
|
|
|
|
|
if (dlci->addr)
|
|
|
|
|
gsm_modem_update(dlci, 0);
|
|
|
|
|
wake_up(&dlci->gsm->event);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -1623,6 +1662,7 @@ static void gsm_dlci_data(struct gsm_dlci *dlci, const u8 *data, int clen)
|
|
|
|
|
tty = tty_port_tty_get(port);
|
|
|
|
|
if (tty) {
|
|
|
|
|
gsm_process_modem(tty, dlci, modem, slen);
|
|
|
|
|
tty_wakeup(tty);
|
|
|
|
|
tty_kref_put(tty);
|
|
|
|
|
}
|
|
|
|
|
fallthrough;
|
|
|
|
@ -1793,19 +1833,7 @@ 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)
|
|
|
|
@ -1836,11 +1864,6 @@ static void gsm_queue(struct gsm_mux *gsm)
|
|
|
|
|
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:
|
|
|
|
@ -1851,35 +1874,9 @@ static void gsm_queue(struct gsm_mux *gsm)
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
gsm_response(gsm, address, UA|PF);
|
|
|
|
|
gsm_dlci_close(dlci);
|
|
|
|
|
break;
|
|
|
|
|
case UA:
|
|
|
|
|
case UA|PF:
|
|
|
|
|
if (cr == 0 || dlci == NULL)
|
|
|
|
|
break;
|
|
|
|
@ -1993,19 +1990,25 @@ static void gsm0_receive(struct gsm_mux *gsm, unsigned char c)
|
|
|
|
|
break;
|
|
|
|
|
case GSM_DATA: /* Data */
|
|
|
|
|
gsm->buf[gsm->count++] = c;
|
|
|
|
|
if (gsm->count == gsm->len)
|
|
|
|
|
if (gsm->count == gsm->len) {
|
|
|
|
|
/* Calculate final FCS for UI frames over all data */
|
|
|
|
|
if ((gsm->control & ~PF) != UIH) {
|
|
|
|
|
gsm->fcs = gsm_fcs_add_block(gsm->fcs, gsm->buf,
|
|
|
|
|
gsm->count);
|
|
|
|
|
}
|
|
|
|
|
gsm->state = GSM_FCS;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case GSM_FCS: /* FCS follows the packet */
|
|
|
|
|
gsm->received_fcs = c;
|
|
|
|
|
gsm_queue(gsm);
|
|
|
|
|
gsm->fcs = gsm_fcs_add(gsm->fcs, c);
|
|
|
|
|
gsm->state = GSM_SSOF;
|
|
|
|
|
break;
|
|
|
|
|
case GSM_SSOF:
|
|
|
|
|
if (c == GSM0_SOF) {
|
|
|
|
|
gsm->state = GSM_SEARCH;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
gsm->state = GSM_SEARCH;
|
|
|
|
|
if (c == GSM0_SOF)
|
|
|
|
|
gsm_queue(gsm);
|
|
|
|
|
else
|
|
|
|
|
gsm->bad_size++;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
pr_debug("%s: unhandled state: %d\n", __func__, gsm->state);
|
|
|
|
@ -2023,12 +2026,35 @@ static void gsm0_receive(struct gsm_mux *gsm, unsigned char c)
|
|
|
|
|
|
|
|
|
|
static void gsm1_receive(struct gsm_mux *gsm, unsigned char c)
|
|
|
|
|
{
|
|
|
|
|
/* handle XON/XOFF */
|
|
|
|
|
if ((c & ISO_IEC_646_MASK) == XON) {
|
|
|
|
|
gsm->constipated = true;
|
|
|
|
|
return;
|
|
|
|
|
} else if ((c & ISO_IEC_646_MASK) == XOFF) {
|
|
|
|
|
gsm->constipated = false;
|
|
|
|
|
/* Kick the link in case it is idling */
|
|
|
|
|
gsm_data_kick(gsm, NULL);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (c == GSM1_SOF) {
|
|
|
|
|
/* EOF is only valid in frame if we have got to the data state
|
|
|
|
|
and received at least one byte (the FCS) */
|
|
|
|
|
if (gsm->state == GSM_DATA && gsm->count) {
|
|
|
|
|
/* Extract the FCS */
|
|
|
|
|
/* EOF is only valid in frame if we have got to the data state */
|
|
|
|
|
if (gsm->state == GSM_DATA) {
|
|
|
|
|
if (gsm->count < 1) {
|
|
|
|
|
/* Missing FSC */
|
|
|
|
|
gsm->malformed++;
|
|
|
|
|
gsm->state = GSM_START;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
/* Remove the FCS from data */
|
|
|
|
|
gsm->count--;
|
|
|
|
|
if ((gsm->control & ~PF) != UIH) {
|
|
|
|
|
/* Calculate final FCS for UI frames over all
|
|
|
|
|
* data but FCS
|
|
|
|
|
*/
|
|
|
|
|
gsm->fcs = gsm_fcs_add_block(gsm->fcs, gsm->buf,
|
|
|
|
|
gsm->count);
|
|
|
|
|
}
|
|
|
|
|
/* Add the FCS itself to test against GOOD_FCS */
|
|
|
|
|
gsm->fcs = gsm_fcs_add(gsm->fcs, gsm->buf[gsm->count]);
|
|
|
|
|
gsm->len = gsm->count;
|
|
|
|
|
gsm_queue(gsm);
|
|
|
|
@ -2037,7 +2063,8 @@ static void gsm1_receive(struct gsm_mux *gsm, unsigned char c)
|
|
|
|
|
}
|
|
|
|
|
/* Any partial frame was a runt so go back to start */
|
|
|
|
|
if (gsm->state != GSM_START) {
|
|
|
|
|
gsm->malformed++;
|
|
|
|
|
if (gsm->state != GSM_SEARCH)
|
|
|
|
|
gsm->malformed++;
|
|
|
|
|
gsm->state = GSM_START;
|
|
|
|
|
}
|
|
|
|
|
/* A SOF in GSM_START means we are still reading idling or
|
|
|
|
@ -2106,74 +2133,43 @@ static void gsm_error(struct gsm_mux *gsm)
|
|
|
|
|
gsm->io_error++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int gsm_disconnect(struct gsm_mux *gsm)
|
|
|
|
|
{
|
|
|
|
|
struct gsm_dlci *dlci = gsm->dlci[0];
|
|
|
|
|
struct gsm_control *gc;
|
|
|
|
|
|
|
|
|
|
if (!dlci)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
/* In theory disconnecting DLCI 0 is sufficient but for some
|
|
|
|
|
modems this is apparently not the case. */
|
|
|
|
|
gc = gsm_control_send(gsm, CMD_CLD, NULL, 0);
|
|
|
|
|
if (gc)
|
|
|
|
|
gsm_control_wait(gsm, gc);
|
|
|
|
|
|
|
|
|
|
del_timer_sync(&gsm->t2_timer);
|
|
|
|
|
/* Now we are sure T2 has stopped */
|
|
|
|
|
|
|
|
|
|
gsm_dlci_begin_close(dlci);
|
|
|
|
|
wait_event_interruptible(gsm->event,
|
|
|
|
|
dlci->state == DLCI_CLOSED);
|
|
|
|
|
|
|
|
|
|
if (signal_pending(current))
|
|
|
|
|
return -EINTR;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* gsm_cleanup_mux - generic GSM protocol cleanup
|
|
|
|
|
* @gsm: our mux
|
|
|
|
|
* @disc: disconnect link?
|
|
|
|
|
*
|
|
|
|
|
* Clean up the bits of the mux which are the same for all framing
|
|
|
|
|
* protocols. Remove the mux from the mux table, stop all the timers
|
|
|
|
|
* and then shut down each device hanging up the channels as we go.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
static void gsm_cleanup_mux(struct gsm_mux *gsm)
|
|
|
|
|
static void gsm_cleanup_mux(struct gsm_mux *gsm, bool disc)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
struct gsm_dlci *dlci = gsm->dlci[0];
|
|
|
|
|
struct gsm_msg *txq, *ntxq;
|
|
|
|
|
|
|
|
|
|
gsm->dead = true;
|
|
|
|
|
|
|
|
|
|
spin_lock(&gsm_mux_lock);
|
|
|
|
|
for (i = 0; i < MAX_MUX; i++) {
|
|
|
|
|
if (gsm_mux[i] == gsm) {
|
|
|
|
|
gsm_mux[i] = NULL;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
spin_unlock(&gsm_mux_lock);
|
|
|
|
|
/* open failed before registering => nothing to do */
|
|
|
|
|
if (i == MAX_MUX)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
del_timer_sync(&gsm->t2_timer);
|
|
|
|
|
/* Now we are sure T2 has stopped */
|
|
|
|
|
if (dlci)
|
|
|
|
|
dlci->dead = true;
|
|
|
|
|
|
|
|
|
|
/* Free up any link layer users */
|
|
|
|
|
mutex_lock(&gsm->mutex);
|
|
|
|
|
for (i = 0; i < NUM_DLCI; i++)
|
|
|
|
|
|
|
|
|
|
if (dlci) {
|
|
|
|
|
if (disc && dlci->state != DLCI_CLOSED) {
|
|
|
|
|
gsm_dlci_begin_close(dlci);
|
|
|
|
|
wait_event(gsm->event, dlci->state == DLCI_CLOSED);
|
|
|
|
|
}
|
|
|
|
|
dlci->dead = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Finish outstanding timers, making sure they are done */
|
|
|
|
|
del_timer_sync(&gsm->t2_timer);
|
|
|
|
|
|
|
|
|
|
/* Free up any link layer users and finally the control channel */
|
|
|
|
|
for (i = NUM_DLCI - 1; i >= 0; i--)
|
|
|
|
|
if (gsm->dlci[i])
|
|
|
|
|
gsm_dlci_release(gsm->dlci[i]);
|
|
|
|
|
mutex_unlock(&gsm->mutex);
|
|
|
|
|
/* Now wipe the queues */
|
|
|
|
|
tty_ldisc_flush(gsm->tty);
|
|
|
|
|
list_for_each_entry_safe(txq, ntxq, &gsm->tx_list, list)
|
|
|
|
|
kfree(txq);
|
|
|
|
|
INIT_LIST_HEAD(&gsm->tx_list);
|
|
|
|
@ -2191,7 +2187,6 @@ static void gsm_cleanup_mux(struct gsm_mux *gsm)
|
|
|
|
|
static int gsm_activate_mux(struct gsm_mux *gsm)
|
|
|
|
|
{
|
|
|
|
|
struct gsm_dlci *dlci;
|
|
|
|
|
int i = 0;
|
|
|
|
|
|
|
|
|
|
timer_setup(&gsm->t2_timer, gsm_control_retransmit, 0);
|
|
|
|
|
init_waitqueue_head(&gsm->event);
|
|
|
|
@ -2203,18 +2198,6 @@ static int gsm_activate_mux(struct gsm_mux *gsm)
|
|
|
|
|
else
|
|
|
|
|
gsm->receive = gsm1_receive;
|
|
|
|
|
|
|
|
|
|
spin_lock(&gsm_mux_lock);
|
|
|
|
|
for (i = 0; i < MAX_MUX; i++) {
|
|
|
|
|
if (gsm_mux[i] == NULL) {
|
|
|
|
|
gsm->num = i;
|
|
|
|
|
gsm_mux[i] = gsm;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
spin_unlock(&gsm_mux_lock);
|
|
|
|
|
if (i == MAX_MUX)
|
|
|
|
|
return -EBUSY;
|
|
|
|
|
|
|
|
|
|
dlci = gsm_dlci_alloc(gsm, 0);
|
|
|
|
|
if (dlci == NULL)
|
|
|
|
|
return -ENOMEM;
|
|
|
|
@ -2230,6 +2213,15 @@ static int gsm_activate_mux(struct gsm_mux *gsm)
|
|
|
|
|
*/
|
|
|
|
|
static void gsm_free_mux(struct gsm_mux *gsm)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < MAX_MUX; i++) {
|
|
|
|
|
if (gsm == gsm_mux[i]) {
|
|
|
|
|
gsm_mux[i] = NULL;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
mutex_destroy(&gsm->mutex);
|
|
|
|
|
kfree(gsm->txframe);
|
|
|
|
|
kfree(gsm->buf);
|
|
|
|
|
kfree(gsm);
|
|
|
|
@ -2249,12 +2241,20 @@ static void gsm_free_muxr(struct kref *ref)
|
|
|
|
|
|
|
|
|
|
static inline void mux_get(struct gsm_mux *gsm)
|
|
|
|
|
{
|
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
|
|
spin_lock_irqsave(&gsm_mux_lock, flags);
|
|
|
|
|
kref_get(&gsm->ref);
|
|
|
|
|
spin_unlock_irqrestore(&gsm_mux_lock, flags);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline void mux_put(struct gsm_mux *gsm)
|
|
|
|
|
{
|
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
|
|
spin_lock_irqsave(&gsm_mux_lock, flags);
|
|
|
|
|
kref_put(&gsm->ref, gsm_free_muxr);
|
|
|
|
|
spin_unlock_irqrestore(&gsm_mux_lock, flags);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline unsigned int mux_num_to_base(struct gsm_mux *gsm)
|
|
|
|
@ -2275,6 +2275,7 @@ static inline unsigned int mux_line_to_num(unsigned int line)
|
|
|
|
|
|
|
|
|
|
static struct gsm_mux *gsm_alloc_mux(void)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
struct gsm_mux *gsm = kzalloc(sizeof(struct gsm_mux), GFP_KERNEL);
|
|
|
|
|
if (gsm == NULL)
|
|
|
|
|
return NULL;
|
|
|
|
@ -2283,7 +2284,7 @@ static struct gsm_mux *gsm_alloc_mux(void)
|
|
|
|
|
kfree(gsm);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
gsm->txframe = kmalloc(2 * MAX_MRU + 2, GFP_KERNEL);
|
|
|
|
|
gsm->txframe = kmalloc(2 * (MAX_MTU + PROT_OVERHEAD - 1), GFP_KERNEL);
|
|
|
|
|
if (gsm->txframe == NULL) {
|
|
|
|
|
kfree(gsm->buf);
|
|
|
|
|
kfree(gsm);
|
|
|
|
@ -2304,6 +2305,26 @@ static struct gsm_mux *gsm_alloc_mux(void)
|
|
|
|
|
gsm->mtu = 64;
|
|
|
|
|
gsm->dead = true; /* Avoid early tty opens */
|
|
|
|
|
|
|
|
|
|
/* Store the instance to the mux array or abort if no space is
|
|
|
|
|
* available.
|
|
|
|
|
*/
|
|
|
|
|
spin_lock(&gsm_mux_lock);
|
|
|
|
|
for (i = 0; i < MAX_MUX; i++) {
|
|
|
|
|
if (!gsm_mux[i]) {
|
|
|
|
|
gsm_mux[i] = gsm;
|
|
|
|
|
gsm->num = i;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
spin_unlock(&gsm_mux_lock);
|
|
|
|
|
if (i == MAX_MUX) {
|
|
|
|
|
mutex_destroy(&gsm->mutex);
|
|
|
|
|
kfree(gsm->txframe);
|
|
|
|
|
kfree(gsm->buf);
|
|
|
|
|
kfree(gsm);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return gsm;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -2339,7 +2360,7 @@ static int gsm_config(struct gsm_mux *gsm, struct gsm_config *c)
|
|
|
|
|
/* Check the MRU/MTU range looks sane */
|
|
|
|
|
if (c->mru > MAX_MRU || c->mtu > MAX_MTU || c->mru < 8 || c->mtu < 8)
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
if (c->n2 < 3)
|
|
|
|
|
if (c->n2 > 255)
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
if (c->encapsulation > 1) /* Basic, advanced, no I */
|
|
|
|
|
return -EINVAL;
|
|
|
|
@ -2370,19 +2391,11 @@ static int gsm_config(struct gsm_mux *gsm, struct gsm_config *c)
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Close down what is needed, restart and initiate the new
|
|
|
|
|
* configuration
|
|
|
|
|
* configuration. On the first time there is no DLCI[0]
|
|
|
|
|
* and closing or cleaning up is not necessary.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
if (gsm->initiator && (need_close || need_restart)) {
|
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
|
|
ret = gsm_disconnect(gsm);
|
|
|
|
|
|
|
|
|
|
if (ret)
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
if (need_restart)
|
|
|
|
|
gsm_cleanup_mux(gsm);
|
|
|
|
|
if (need_close || need_restart)
|
|
|
|
|
gsm_cleanup_mux(gsm, true);
|
|
|
|
|
|
|
|
|
|
gsm->initiator = c->initiator;
|
|
|
|
|
gsm->mru = c->mru;
|
|
|
|
@ -2450,25 +2463,26 @@ static int gsmld_attach_gsm(struct tty_struct *tty, struct gsm_mux *gsm)
|
|
|
|
|
int ret, i;
|
|
|
|
|
|
|
|
|
|
gsm->tty = tty_kref_get(tty);
|
|
|
|
|
/* Turn off tty XON/XOFF handling to handle it explicitly. */
|
|
|
|
|
gsm->old_c_iflag = tty->termios.c_iflag;
|
|
|
|
|
tty->termios.c_iflag &= (IXON | IXOFF);
|
|
|
|
|
ret = gsm_activate_mux(gsm);
|
|
|
|
|
if (ret != 0)
|
|
|
|
|
tty_kref_put(gsm->tty);
|
|
|
|
|
else {
|
|
|
|
|
/* Don't register device 0 - this is the control channel and not
|
|
|
|
|
a usable tty interface */
|
|
|
|
|
if (gsm->initiator) {
|
|
|
|
|
base = mux_num_to_base(gsm); /* Base for this MUX */
|
|
|
|
|
for (i = 1; i < NUM_DLCI; i++) {
|
|
|
|
|
struct device *dev;
|
|
|
|
|
base = mux_num_to_base(gsm); /* Base for this MUX */
|
|
|
|
|
for (i = 1; i < NUM_DLCI; i++) {
|
|
|
|
|
struct device *dev;
|
|
|
|
|
|
|
|
|
|
dev = tty_register_device(gsm_tty_driver,
|
|
|
|
|
dev = tty_register_device(gsm_tty_driver,
|
|
|
|
|
base + i, NULL);
|
|
|
|
|
if (IS_ERR(dev)) {
|
|
|
|
|
for (i--; i >= 1; i--)
|
|
|
|
|
tty_unregister_device(gsm_tty_driver,
|
|
|
|
|
base + i);
|
|
|
|
|
return PTR_ERR(dev);
|
|
|
|
|
}
|
|
|
|
|
if (IS_ERR(dev)) {
|
|
|
|
|
for (i--; i >= 1; i--)
|
|
|
|
|
tty_unregister_device(gsm_tty_driver,
|
|
|
|
|
base + i);
|
|
|
|
|
return PTR_ERR(dev);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -2490,11 +2504,10 @@ static void gsmld_detach_gsm(struct tty_struct *tty, struct gsm_mux *gsm)
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
WARN_ON(tty != gsm->tty);
|
|
|
|
|
if (gsm->initiator) {
|
|
|
|
|
for (i = 1; i < NUM_DLCI; i++)
|
|
|
|
|
tty_unregister_device(gsm_tty_driver, base + i);
|
|
|
|
|
}
|
|
|
|
|
gsm_cleanup_mux(gsm);
|
|
|
|
|
for (i = 1; i < NUM_DLCI; i++)
|
|
|
|
|
tty_unregister_device(gsm_tty_driver, base + i);
|
|
|
|
|
/* Restore tty XON/XOFF handling. */
|
|
|
|
|
gsm->tty->termios.c_iflag = gsm->old_c_iflag;
|
|
|
|
|
tty_kref_put(gsm->tty);
|
|
|
|
|
gsm->tty = NULL;
|
|
|
|
|
}
|
|
|
|
@ -2559,6 +2572,12 @@ static void gsmld_close(struct tty_struct *tty)
|
|
|
|
|
{
|
|
|
|
|
struct gsm_mux *gsm = tty->disc_data;
|
|
|
|
|
|
|
|
|
|
/* The ldisc locks and closes the port before calling our close. This
|
|
|
|
|
* means we have no way to do a proper disconnect. We will not bother
|
|
|
|
|
* to do one.
|
|
|
|
|
*/
|
|
|
|
|
gsm_cleanup_mux(gsm, false);
|
|
|
|
|
|
|
|
|
|
gsmld_detach_gsm(tty, gsm);
|
|
|
|
|
|
|
|
|
|
gsmld_flush_buffer(tty);
|
|
|
|
@ -2597,7 +2616,7 @@ static int gsmld_open(struct tty_struct *tty)
|
|
|
|
|
|
|
|
|
|
ret = gsmld_attach_gsm(tty, gsm);
|
|
|
|
|
if (ret != 0) {
|
|
|
|
|
gsm_cleanup_mux(gsm);
|
|
|
|
|
gsm_cleanup_mux(gsm, false);
|
|
|
|
|
mux_put(gsm);
|
|
|
|
|
}
|
|
|
|
|
return ret;
|
|
|
|
@ -2954,26 +2973,78 @@ static struct tty_ldisc_ops tty_ldisc_packet = {
|
|
|
|
|
|
|
|
|
|
#define TX_SIZE 512
|
|
|
|
|
|
|
|
|
|
static int gsmtty_modem_update(struct gsm_dlci *dlci, u8 brk)
|
|
|
|
|
/**
|
|
|
|
|
* gsm_modem_upd_via_data - send modem bits via convergence layer
|
|
|
|
|
* @dlci: channel
|
|
|
|
|
* @brk: break signal
|
|
|
|
|
*
|
|
|
|
|
* Send an empty frame to signal mobile state changes and to transmit the
|
|
|
|
|
* break signal for adaption 2.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
static void gsm_modem_upd_via_data(struct gsm_dlci *dlci, u8 brk)
|
|
|
|
|
{
|
|
|
|
|
u8 modembits[5];
|
|
|
|
|
struct gsm_mux *gsm = dlci->gsm;
|
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
|
|
if (dlci->state != DLCI_OPEN || dlci->adaption != 2)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
spin_lock_irqsave(&gsm->tx_lock, flags);
|
|
|
|
|
gsm_dlci_modem_output(gsm, dlci, brk);
|
|
|
|
|
spin_unlock_irqrestore(&gsm->tx_lock, flags);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* gsm_modem_upd_via_msc - send modem bits via control frame
|
|
|
|
|
* @dlci: channel
|
|
|
|
|
* @brk: break signal
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
static int gsm_modem_upd_via_msc(struct gsm_dlci *dlci, u8 brk)
|
|
|
|
|
{
|
|
|
|
|
u8 modembits[3];
|
|
|
|
|
struct gsm_control *ctrl;
|
|
|
|
|
int len = 2;
|
|
|
|
|
|
|
|
|
|
if (brk)
|
|
|
|
|
len++;
|
|
|
|
|
if (dlci->gsm->encoding != 0)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
modembits[0] = len << 1 | EA; /* Data bytes */
|
|
|
|
|
modembits[1] = dlci->addr << 2 | 3; /* DLCI, EA, 1 */
|
|
|
|
|
modembits[2] = gsm_encode_modem(dlci) << 1 | EA;
|
|
|
|
|
if (brk)
|
|
|
|
|
modembits[3] = brk << 4 | 2 | EA; /* Valid, EA */
|
|
|
|
|
ctrl = gsm_control_send(dlci->gsm, CMD_MSC, modembits, len + 1);
|
|
|
|
|
modembits[0] = (dlci->addr << 2) | 2 | EA; /* DLCI, Valid, EA */
|
|
|
|
|
if (!brk) {
|
|
|
|
|
modembits[1] = (gsm_encode_modem(dlci) << 1) | EA;
|
|
|
|
|
} else {
|
|
|
|
|
modembits[1] = gsm_encode_modem(dlci) << 1;
|
|
|
|
|
modembits[2] = (brk << 4) | 2 | EA; /* Length, Break, EA */
|
|
|
|
|
len++;
|
|
|
|
|
}
|
|
|
|
|
ctrl = gsm_control_send(dlci->gsm, CMD_MSC, modembits, len);
|
|
|
|
|
if (ctrl == NULL)
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
return gsm_control_wait(dlci->gsm, ctrl);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* gsm_modem_update - send modem status line state
|
|
|
|
|
* @dlci: channel
|
|
|
|
|
* @brk: break signal
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
static int gsm_modem_update(struct gsm_dlci *dlci, u8 brk)
|
|
|
|
|
{
|
|
|
|
|
if (dlci->adaption == 2) {
|
|
|
|
|
/* Send convergence layer type 2 empty data frame. */
|
|
|
|
|
gsm_modem_upd_via_data(dlci, brk);
|
|
|
|
|
return 0;
|
|
|
|
|
} else if (dlci->gsm->encoding == 0) {
|
|
|
|
|
/* Send as MSC control message. */
|
|
|
|
|
return gsm_modem_upd_via_msc(dlci, brk);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Modem status lines are not supported. */
|
|
|
|
|
return -EPROTONOSUPPORT;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int gsm_carrier_raised(struct tty_port *port)
|
|
|
|
|
{
|
|
|
|
|
struct gsm_dlci *dlci = container_of(port, struct gsm_dlci, port);
|
|
|
|
@ -3006,7 +3077,7 @@ static void gsm_dtr_rts(struct tty_port *port, int onoff)
|
|
|
|
|
modem_tx &= ~(TIOCM_DTR | TIOCM_RTS);
|
|
|
|
|
if (modem_tx != dlci->modem_tx) {
|
|
|
|
|
dlci->modem_tx = modem_tx;
|
|
|
|
|
gsmtty_modem_update(dlci, 0);
|
|
|
|
|
gsm_modem_update(dlci, 0);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -3155,13 +3226,17 @@ static unsigned int gsmtty_chars_in_buffer(struct tty_struct *tty)
|
|
|
|
|
static void gsmtty_flush_buffer(struct tty_struct *tty)
|
|
|
|
|
{
|
|
|
|
|
struct gsm_dlci *dlci = tty->driver_data;
|
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
|
|
if (dlci->state == DLCI_CLOSED)
|
|
|
|
|
return;
|
|
|
|
|
/* Caution needed: If we implement reliable transport classes
|
|
|
|
|
then the data being transmitted can't simply be junked once
|
|
|
|
|
it has first hit the stack. Until then we can just blow it
|
|
|
|
|
away */
|
|
|
|
|
spin_lock_irqsave(&dlci->lock, flags);
|
|
|
|
|
kfifo_reset(&dlci->fifo);
|
|
|
|
|
spin_unlock_irqrestore(&dlci->lock, flags);
|
|
|
|
|
/* Need to unhook this DLCI from the transmit queue logic */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -3193,7 +3268,7 @@ static int gsmtty_tiocmset(struct tty_struct *tty,
|
|
|
|
|
|
|
|
|
|
if (modem_tx != dlci->modem_tx) {
|
|
|
|
|
dlci->modem_tx = modem_tx;
|
|
|
|
|
return gsmtty_modem_update(dlci, 0);
|
|
|
|
|
return gsm_modem_update(dlci, 0);
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
@ -3254,7 +3329,7 @@ static void gsmtty_throttle(struct tty_struct *tty)
|
|
|
|
|
dlci->modem_tx &= ~TIOCM_RTS;
|
|
|
|
|
dlci->throttled = true;
|
|
|
|
|
/* Send an MSC with RTS cleared */
|
|
|
|
|
gsmtty_modem_update(dlci, 0);
|
|
|
|
|
gsm_modem_update(dlci, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void gsmtty_unthrottle(struct tty_struct *tty)
|
|
|
|
@ -3266,7 +3341,7 @@ static void gsmtty_unthrottle(struct tty_struct *tty)
|
|
|
|
|
dlci->modem_tx |= TIOCM_RTS;
|
|
|
|
|
dlci->throttled = false;
|
|
|
|
|
/* Send an MSC with RTS set */
|
|
|
|
|
gsmtty_modem_update(dlci, 0);
|
|
|
|
|
gsm_modem_update(dlci, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int gsmtty_break_ctl(struct tty_struct *tty, int state)
|
|
|
|
@ -3284,7 +3359,7 @@ static int gsmtty_break_ctl(struct tty_struct *tty, int state)
|
|
|
|
|
if (encode > 0x0F)
|
|
|
|
|
encode = 0x0F; /* Best effort */
|
|
|
|
|
}
|
|
|
|
|
return gsmtty_modem_update(dlci, encode);
|
|
|
|
|
return gsm_modem_update(dlci, encode);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void gsmtty_cleanup(struct tty_struct *tty)
|
|
|
|
|