mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-09-08 09:08:05 -04:00
Fixed MTP to work with TWRP
This commit is contained in:
commit
f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions
70
drivers/isdn/gigaset/Kconfig
Normal file
70
drivers/isdn/gigaset/Kconfig
Normal file
|
@ -0,0 +1,70 @@
|
|||
menuconfig ISDN_DRV_GIGASET
|
||||
tristate "Siemens Gigaset support"
|
||||
depends on TTY
|
||||
select CRC_CCITT
|
||||
select BITREVERSE
|
||||
help
|
||||
This driver supports the Siemens Gigaset SX205/255 family of
|
||||
ISDN DECT bases, including the predecessors Gigaset 3070/3075
|
||||
and 4170/4175 and their T-Com versions Sinus 45isdn and Sinus
|
||||
721X.
|
||||
If you have one of these devices, say M here and for at least
|
||||
one of the connection specific parts that follow.
|
||||
This will build a module called "gigaset".
|
||||
Note: If you build your ISDN subsystem (ISDN_CAPI or ISDN_I4L)
|
||||
as a module, you have to build this driver as a module too,
|
||||
otherwise the Gigaset device won't show up as an ISDN device.
|
||||
|
||||
if ISDN_DRV_GIGASET
|
||||
|
||||
config GIGASET_CAPI
|
||||
bool "Gigaset CAPI support"
|
||||
depends on ISDN_CAPI='y'||(ISDN_CAPI='m'&&ISDN_DRV_GIGASET='m')
|
||||
default ISDN_I4L='n'
|
||||
help
|
||||
Build the Gigaset driver as a CAPI 2.0 driver interfacing with
|
||||
the Kernel CAPI subsystem. To use it with the old ISDN4Linux
|
||||
subsystem you'll have to enable the capidrv glue driver.
|
||||
(select ISDN_CAPI_CAPIDRV.)
|
||||
Say N to build the old native ISDN4Linux variant.
|
||||
If unsure, say Y.
|
||||
|
||||
config GIGASET_I4L
|
||||
bool
|
||||
depends on ISDN_I4L='y'||(ISDN_I4L='m'&&ISDN_DRV_GIGASET='m')
|
||||
default !GIGASET_CAPI
|
||||
|
||||
config GIGASET_DUMMYLL
|
||||
bool
|
||||
default !GIGASET_CAPI&&!GIGASET_I4L
|
||||
|
||||
config GIGASET_BASE
|
||||
tristate "Gigaset base station support"
|
||||
depends on USB
|
||||
help
|
||||
Say M here if you want to use the USB interface of the Gigaset
|
||||
base for connection to your system.
|
||||
This will build a module called "bas_gigaset".
|
||||
|
||||
config GIGASET_M105
|
||||
tristate "Gigaset M105 support"
|
||||
depends on USB
|
||||
help
|
||||
Say M here if you want to connect to the Gigaset base via DECT
|
||||
using a Gigaset M105 (Sinus 45 Data 2) USB DECT device.
|
||||
This will build a module called "usb_gigaset".
|
||||
|
||||
config GIGASET_M101
|
||||
tristate "Gigaset M101 support"
|
||||
help
|
||||
Say M here if you want to connect to the Gigaset base via DECT
|
||||
using a Gigaset M101 (Sinus 45 Data 1) RS232 DECT device.
|
||||
This will build a module called "ser_gigaset".
|
||||
|
||||
config GIGASET_DEBUG
|
||||
bool "Gigaset debugging"
|
||||
help
|
||||
This enables debugging code in the Gigaset drivers.
|
||||
If in doubt, say yes.
|
||||
|
||||
endif # ISDN_DRV_GIGASET
|
12
drivers/isdn/gigaset/Makefile
Normal file
12
drivers/isdn/gigaset/Makefile
Normal file
|
@ -0,0 +1,12 @@
|
|||
gigaset-y := common.o interface.o proc.o ev-layer.o asyncdata.o
|
||||
gigaset-$(CONFIG_GIGASET_CAPI) += capi.o
|
||||
gigaset-$(CONFIG_GIGASET_I4L) += i4l.o
|
||||
gigaset-$(CONFIG_GIGASET_DUMMYLL) += dummyll.o
|
||||
usb_gigaset-y := usb-gigaset.o
|
||||
ser_gigaset-y := ser-gigaset.o
|
||||
bas_gigaset-y := bas-gigaset.o isocdata.o
|
||||
|
||||
obj-$(CONFIG_ISDN_DRV_GIGASET) += gigaset.o
|
||||
obj-$(CONFIG_GIGASET_M105) += usb_gigaset.o
|
||||
obj-$(CONFIG_GIGASET_BASE) += bas_gigaset.o
|
||||
obj-$(CONFIG_GIGASET_M101) += ser_gigaset.o
|
609
drivers/isdn/gigaset/asyncdata.c
Normal file
609
drivers/isdn/gigaset/asyncdata.c
Normal file
|
@ -0,0 +1,609 @@
|
|||
/*
|
||||
* Common data handling layer for ser_gigaset and usb_gigaset
|
||||
*
|
||||
* Copyright (c) 2005 by Tilman Schmidt <tilman@imap.cc>,
|
||||
* Hansjoerg Lipp <hjlipp@web.de>,
|
||||
* Stefan Eilers.
|
||||
*
|
||||
* =====================================================================
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
* =====================================================================
|
||||
*/
|
||||
|
||||
#include "gigaset.h"
|
||||
#include <linux/crc-ccitt.h>
|
||||
#include <linux/bitrev.h>
|
||||
#include <linux/export.h>
|
||||
|
||||
/* check if byte must be stuffed/escaped
|
||||
* I'm not sure which data should be encoded.
|
||||
* Therefore I will go the hard way and encode every value
|
||||
* less than 0x20, the flag sequence and the control escape char.
|
||||
*/
|
||||
static inline int muststuff(unsigned char c)
|
||||
{
|
||||
if (c < PPP_TRANS) return 1;
|
||||
if (c == PPP_FLAG) return 1;
|
||||
if (c == PPP_ESCAPE) return 1;
|
||||
/* other possible candidates: */
|
||||
/* 0x91: XON with parity set */
|
||||
/* 0x93: XOFF with parity set */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* == data input =========================================================== */
|
||||
|
||||
/* process a block of received bytes in command mode
|
||||
* (mstate != MS_LOCKED && (inputstate & INS_command))
|
||||
* Append received bytes to the command response buffer and forward them
|
||||
* line by line to the response handler. Exit whenever a mode/state change
|
||||
* might have occurred.
|
||||
* Note: Received lines may be terminated by CR, LF, or CR LF, which will be
|
||||
* removed before passing the line to the response handler.
|
||||
* Return value:
|
||||
* number of processed bytes
|
||||
*/
|
||||
static unsigned cmd_loop(unsigned numbytes, struct inbuf_t *inbuf)
|
||||
{
|
||||
unsigned char *src = inbuf->data + inbuf->head;
|
||||
struct cardstate *cs = inbuf->cs;
|
||||
unsigned cbytes = cs->cbytes;
|
||||
unsigned procbytes = 0;
|
||||
unsigned char c;
|
||||
|
||||
while (procbytes < numbytes) {
|
||||
c = *src++;
|
||||
procbytes++;
|
||||
|
||||
switch (c) {
|
||||
case '\n':
|
||||
if (cbytes == 0 && cs->respdata[0] == '\r') {
|
||||
/* collapse LF with preceding CR */
|
||||
cs->respdata[0] = 0;
|
||||
break;
|
||||
}
|
||||
/* --v-- fall through --v-- */
|
||||
case '\r':
|
||||
/* end of message line, pass to response handler */
|
||||
if (cbytes >= MAX_RESP_SIZE) {
|
||||
dev_warn(cs->dev, "response too large (%d)\n",
|
||||
cbytes);
|
||||
cbytes = MAX_RESP_SIZE;
|
||||
}
|
||||
cs->cbytes = cbytes;
|
||||
gigaset_dbg_buffer(DEBUG_TRANSCMD, "received response",
|
||||
cbytes, cs->respdata);
|
||||
gigaset_handle_modem_response(cs);
|
||||
cbytes = 0;
|
||||
|
||||
/* store EOL byte for CRLF collapsing */
|
||||
cs->respdata[0] = c;
|
||||
|
||||
/* cs->dle may have changed */
|
||||
if (cs->dle && !(inbuf->inputstate & INS_DLE_command))
|
||||
inbuf->inputstate &= ~INS_command;
|
||||
|
||||
/* return for reevaluating state */
|
||||
goto exit;
|
||||
|
||||
case DLE_FLAG:
|
||||
if (inbuf->inputstate & INS_DLE_char) {
|
||||
/* quoted DLE: clear quote flag */
|
||||
inbuf->inputstate &= ~INS_DLE_char;
|
||||
} else if (cs->dle ||
|
||||
(inbuf->inputstate & INS_DLE_command)) {
|
||||
/* DLE escape, pass up for handling */
|
||||
inbuf->inputstate |= INS_DLE_char;
|
||||
goto exit;
|
||||
}
|
||||
/* quoted or not in DLE mode: treat as regular data */
|
||||
/* --v-- fall through --v-- */
|
||||
default:
|
||||
/* append to line buffer if possible */
|
||||
if (cbytes < MAX_RESP_SIZE)
|
||||
cs->respdata[cbytes] = c;
|
||||
cbytes++;
|
||||
}
|
||||
}
|
||||
exit:
|
||||
cs->cbytes = cbytes;
|
||||
return procbytes;
|
||||
}
|
||||
|
||||
/* process a block of received bytes in lock mode
|
||||
* All received bytes are passed unmodified to the tty i/f.
|
||||
* Return value:
|
||||
* number of processed bytes
|
||||
*/
|
||||
static unsigned lock_loop(unsigned numbytes, struct inbuf_t *inbuf)
|
||||
{
|
||||
unsigned char *src = inbuf->data + inbuf->head;
|
||||
|
||||
gigaset_dbg_buffer(DEBUG_LOCKCMD, "received response", numbytes, src);
|
||||
gigaset_if_receive(inbuf->cs, src, numbytes);
|
||||
return numbytes;
|
||||
}
|
||||
|
||||
/* process a block of received bytes in HDLC data mode
|
||||
* (mstate != MS_LOCKED && !(inputstate & INS_command) && proto2 == L2_HDLC)
|
||||
* Collect HDLC frames, undoing byte stuffing and watching for DLE escapes.
|
||||
* When a frame is complete, check the FCS and pass valid frames to the LL.
|
||||
* If DLE is encountered, return immediately to let the caller handle it.
|
||||
* Return value:
|
||||
* number of processed bytes
|
||||
*/
|
||||
static unsigned hdlc_loop(unsigned numbytes, struct inbuf_t *inbuf)
|
||||
{
|
||||
struct cardstate *cs = inbuf->cs;
|
||||
struct bc_state *bcs = cs->bcs;
|
||||
int inputstate = bcs->inputstate;
|
||||
__u16 fcs = bcs->rx_fcs;
|
||||
struct sk_buff *skb = bcs->rx_skb;
|
||||
unsigned char *src = inbuf->data + inbuf->head;
|
||||
unsigned procbytes = 0;
|
||||
unsigned char c;
|
||||
|
||||
if (inputstate & INS_byte_stuff) {
|
||||
if (!numbytes)
|
||||
return 0;
|
||||
inputstate &= ~INS_byte_stuff;
|
||||
goto byte_stuff;
|
||||
}
|
||||
|
||||
while (procbytes < numbytes) {
|
||||
c = *src++;
|
||||
procbytes++;
|
||||
if (c == DLE_FLAG) {
|
||||
if (inputstate & INS_DLE_char) {
|
||||
/* quoted DLE: clear quote flag */
|
||||
inputstate &= ~INS_DLE_char;
|
||||
} else if (cs->dle || (inputstate & INS_DLE_command)) {
|
||||
/* DLE escape, pass up for handling */
|
||||
inputstate |= INS_DLE_char;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (c == PPP_ESCAPE) {
|
||||
/* byte stuffing indicator: pull in next byte */
|
||||
if (procbytes >= numbytes) {
|
||||
/* end of buffer, save for later processing */
|
||||
inputstate |= INS_byte_stuff;
|
||||
break;
|
||||
}
|
||||
byte_stuff:
|
||||
c = *src++;
|
||||
procbytes++;
|
||||
if (c == DLE_FLAG) {
|
||||
if (inputstate & INS_DLE_char) {
|
||||
/* quoted DLE: clear quote flag */
|
||||
inputstate &= ~INS_DLE_char;
|
||||
} else if (cs->dle ||
|
||||
(inputstate & INS_DLE_command)) {
|
||||
/* DLE escape, pass up for handling */
|
||||
inputstate |=
|
||||
INS_DLE_char | INS_byte_stuff;
|
||||
break;
|
||||
}
|
||||
}
|
||||
c ^= PPP_TRANS;
|
||||
#ifdef CONFIG_GIGASET_DEBUG
|
||||
if (!muststuff(c))
|
||||
gig_dbg(DEBUG_HDLC, "byte stuffed: 0x%02x", c);
|
||||
#endif
|
||||
} else if (c == PPP_FLAG) {
|
||||
/* end of frame: process content if any */
|
||||
if (inputstate & INS_have_data) {
|
||||
gig_dbg(DEBUG_HDLC,
|
||||
"7e----------------------------");
|
||||
|
||||
/* check and pass received frame */
|
||||
if (!skb) {
|
||||
/* skipped frame */
|
||||
gigaset_isdn_rcv_err(bcs);
|
||||
} else if (skb->len < 2) {
|
||||
/* frame too short for FCS */
|
||||
dev_warn(cs->dev,
|
||||
"short frame (%d)\n",
|
||||
skb->len);
|
||||
gigaset_isdn_rcv_err(bcs);
|
||||
dev_kfree_skb_any(skb);
|
||||
} else if (fcs != PPP_GOODFCS) {
|
||||
/* frame check error */
|
||||
dev_err(cs->dev,
|
||||
"Checksum failed, %u bytes corrupted!\n",
|
||||
skb->len);
|
||||
gigaset_isdn_rcv_err(bcs);
|
||||
dev_kfree_skb_any(skb);
|
||||
} else {
|
||||
/* good frame */
|
||||
__skb_trim(skb, skb->len - 2);
|
||||
gigaset_skb_rcvd(bcs, skb);
|
||||
}
|
||||
|
||||
/* prepare reception of next frame */
|
||||
inputstate &= ~INS_have_data;
|
||||
skb = gigaset_new_rx_skb(bcs);
|
||||
} else {
|
||||
/* empty frame (7E 7E) */
|
||||
#ifdef CONFIG_GIGASET_DEBUG
|
||||
++bcs->emptycount;
|
||||
#endif
|
||||
if (!skb) {
|
||||
/* skipped (?) */
|
||||
gigaset_isdn_rcv_err(bcs);
|
||||
skb = gigaset_new_rx_skb(bcs);
|
||||
}
|
||||
}
|
||||
|
||||
fcs = PPP_INITFCS;
|
||||
continue;
|
||||
#ifdef CONFIG_GIGASET_DEBUG
|
||||
} else if (muststuff(c)) {
|
||||
/* Should not happen. Possible after ZDLE=1<CR><LF>. */
|
||||
gig_dbg(DEBUG_HDLC, "not byte stuffed: 0x%02x", c);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* regular data byte, append to skb */
|
||||
#ifdef CONFIG_GIGASET_DEBUG
|
||||
if (!(inputstate & INS_have_data)) {
|
||||
gig_dbg(DEBUG_HDLC, "7e (%d x) ================",
|
||||
bcs->emptycount);
|
||||
bcs->emptycount = 0;
|
||||
}
|
||||
#endif
|
||||
inputstate |= INS_have_data;
|
||||
if (skb) {
|
||||
if (skb->len >= bcs->rx_bufsize) {
|
||||
dev_warn(cs->dev, "received packet too long\n");
|
||||
dev_kfree_skb_any(skb);
|
||||
/* skip remainder of packet */
|
||||
bcs->rx_skb = skb = NULL;
|
||||
} else {
|
||||
*__skb_put(skb, 1) = c;
|
||||
fcs = crc_ccitt_byte(fcs, c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bcs->inputstate = inputstate;
|
||||
bcs->rx_fcs = fcs;
|
||||
return procbytes;
|
||||
}
|
||||
|
||||
/* process a block of received bytes in transparent data mode
|
||||
* (mstate != MS_LOCKED && !(inputstate & INS_command) && proto2 != L2_HDLC)
|
||||
* Invert bytes, undoing byte stuffing and watching for DLE escapes.
|
||||
* If DLE is encountered, return immediately to let the caller handle it.
|
||||
* Return value:
|
||||
* number of processed bytes
|
||||
*/
|
||||
static unsigned iraw_loop(unsigned numbytes, struct inbuf_t *inbuf)
|
||||
{
|
||||
struct cardstate *cs = inbuf->cs;
|
||||
struct bc_state *bcs = cs->bcs;
|
||||
int inputstate = bcs->inputstate;
|
||||
struct sk_buff *skb = bcs->rx_skb;
|
||||
unsigned char *src = inbuf->data + inbuf->head;
|
||||
unsigned procbytes = 0;
|
||||
unsigned char c;
|
||||
|
||||
if (!skb) {
|
||||
/* skip this block */
|
||||
gigaset_new_rx_skb(bcs);
|
||||
return numbytes;
|
||||
}
|
||||
|
||||
while (procbytes < numbytes && skb->len < bcs->rx_bufsize) {
|
||||
c = *src++;
|
||||
procbytes++;
|
||||
|
||||
if (c == DLE_FLAG) {
|
||||
if (inputstate & INS_DLE_char) {
|
||||
/* quoted DLE: clear quote flag */
|
||||
inputstate &= ~INS_DLE_char;
|
||||
} else if (cs->dle || (inputstate & INS_DLE_command)) {
|
||||
/* DLE escape, pass up for handling */
|
||||
inputstate |= INS_DLE_char;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* regular data byte: append to current skb */
|
||||
inputstate |= INS_have_data;
|
||||
*__skb_put(skb, 1) = bitrev8(c);
|
||||
}
|
||||
|
||||
/* pass data up */
|
||||
if (inputstate & INS_have_data) {
|
||||
gigaset_skb_rcvd(bcs, skb);
|
||||
inputstate &= ~INS_have_data;
|
||||
gigaset_new_rx_skb(bcs);
|
||||
}
|
||||
|
||||
bcs->inputstate = inputstate;
|
||||
return procbytes;
|
||||
}
|
||||
|
||||
/* process DLE escapes
|
||||
* Called whenever a DLE sequence might be encountered in the input stream.
|
||||
* Either processes the entire DLE sequence or, if that isn't possible,
|
||||
* notes the fact that an initial DLE has been received in the INS_DLE_char
|
||||
* inputstate flag and resumes processing of the sequence on the next call.
|
||||
*/
|
||||
static void handle_dle(struct inbuf_t *inbuf)
|
||||
{
|
||||
struct cardstate *cs = inbuf->cs;
|
||||
|
||||
if (cs->mstate == MS_LOCKED)
|
||||
return; /* no DLE processing in lock mode */
|
||||
|
||||
if (!(inbuf->inputstate & INS_DLE_char)) {
|
||||
/* no DLE pending */
|
||||
if (inbuf->data[inbuf->head] == DLE_FLAG &&
|
||||
(cs->dle || inbuf->inputstate & INS_DLE_command)) {
|
||||
/* start of DLE sequence */
|
||||
inbuf->head++;
|
||||
if (inbuf->head == inbuf->tail ||
|
||||
inbuf->head == RBUFSIZE) {
|
||||
/* end of buffer, save for later processing */
|
||||
inbuf->inputstate |= INS_DLE_char;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
/* regular data byte */
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* consume pending DLE */
|
||||
inbuf->inputstate &= ~INS_DLE_char;
|
||||
|
||||
switch (inbuf->data[inbuf->head]) {
|
||||
case 'X': /* begin of event message */
|
||||
if (inbuf->inputstate & INS_command)
|
||||
dev_notice(cs->dev,
|
||||
"received <DLE>X in command mode\n");
|
||||
inbuf->inputstate |= INS_command | INS_DLE_command;
|
||||
inbuf->head++; /* byte consumed */
|
||||
break;
|
||||
case '.': /* end of event message */
|
||||
if (!(inbuf->inputstate & INS_DLE_command))
|
||||
dev_notice(cs->dev,
|
||||
"received <DLE>. without <DLE>X\n");
|
||||
inbuf->inputstate &= ~INS_DLE_command;
|
||||
/* return to data mode if in DLE mode */
|
||||
if (cs->dle)
|
||||
inbuf->inputstate &= ~INS_command;
|
||||
inbuf->head++; /* byte consumed */
|
||||
break;
|
||||
case DLE_FLAG: /* DLE in data stream */
|
||||
/* mark as quoted */
|
||||
inbuf->inputstate |= INS_DLE_char;
|
||||
if (!(cs->dle || inbuf->inputstate & INS_DLE_command))
|
||||
dev_notice(cs->dev,
|
||||
"received <DLE><DLE> not in DLE mode\n");
|
||||
break; /* quoted byte left in buffer */
|
||||
default:
|
||||
dev_notice(cs->dev, "received <DLE><%02x>\n",
|
||||
inbuf->data[inbuf->head]);
|
||||
/* quoted byte left in buffer */
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* gigaset_m10x_input() - process a block of data received from the device
|
||||
* @inbuf: received data and device descriptor structure.
|
||||
*
|
||||
* Called by hardware module {ser,usb}_gigaset with a block of received
|
||||
* bytes. Separates the bytes received over the serial data channel into
|
||||
* user data and command replies (locked/unlocked) according to the
|
||||
* current state of the interface.
|
||||
*/
|
||||
void gigaset_m10x_input(struct inbuf_t *inbuf)
|
||||
{
|
||||
struct cardstate *cs = inbuf->cs;
|
||||
unsigned numbytes, procbytes;
|
||||
|
||||
gig_dbg(DEBUG_INTR, "buffer state: %u -> %u", inbuf->head, inbuf->tail);
|
||||
|
||||
while (inbuf->head != inbuf->tail) {
|
||||
/* check for DLE escape */
|
||||
handle_dle(inbuf);
|
||||
|
||||
/* process a contiguous block of bytes */
|
||||
numbytes = (inbuf->head > inbuf->tail ?
|
||||
RBUFSIZE : inbuf->tail) - inbuf->head;
|
||||
gig_dbg(DEBUG_INTR, "processing %u bytes", numbytes);
|
||||
/*
|
||||
* numbytes may be 0 if handle_dle() ate the last byte.
|
||||
* This does no harm, *_loop() will just return 0 immediately.
|
||||
*/
|
||||
|
||||
if (cs->mstate == MS_LOCKED)
|
||||
procbytes = lock_loop(numbytes, inbuf);
|
||||
else if (inbuf->inputstate & INS_command)
|
||||
procbytes = cmd_loop(numbytes, inbuf);
|
||||
else if (cs->bcs->proto2 == L2_HDLC)
|
||||
procbytes = hdlc_loop(numbytes, inbuf);
|
||||
else
|
||||
procbytes = iraw_loop(numbytes, inbuf);
|
||||
inbuf->head += procbytes;
|
||||
|
||||
/* check for buffer wraparound */
|
||||
if (inbuf->head >= RBUFSIZE)
|
||||
inbuf->head = 0;
|
||||
|
||||
gig_dbg(DEBUG_INTR, "head set to %u", inbuf->head);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(gigaset_m10x_input);
|
||||
|
||||
|
||||
/* == data output ========================================================== */
|
||||
|
||||
/*
|
||||
* Encode a data packet into an octet stuffed HDLC frame with FCS,
|
||||
* opening and closing flags, preserving headroom data.
|
||||
* parameters:
|
||||
* skb skb containing original packet (freed upon return)
|
||||
* Return value:
|
||||
* pointer to newly allocated skb containing the result frame
|
||||
* and the original link layer header, NULL on error
|
||||
*/
|
||||
static struct sk_buff *HDLC_Encode(struct sk_buff *skb)
|
||||
{
|
||||
struct sk_buff *hdlc_skb;
|
||||
__u16 fcs;
|
||||
unsigned char c;
|
||||
unsigned char *cp;
|
||||
int len;
|
||||
unsigned int stuf_cnt;
|
||||
|
||||
stuf_cnt = 0;
|
||||
fcs = PPP_INITFCS;
|
||||
cp = skb->data;
|
||||
len = skb->len;
|
||||
while (len--) {
|
||||
if (muststuff(*cp))
|
||||
stuf_cnt++;
|
||||
fcs = crc_ccitt_byte(fcs, *cp++);
|
||||
}
|
||||
fcs ^= 0xffff; /* complement */
|
||||
|
||||
/* size of new buffer: original size + number of stuffing bytes
|
||||
* + 2 bytes FCS + 2 stuffing bytes for FCS (if needed) + 2 flag bytes
|
||||
* + room for link layer header
|
||||
*/
|
||||
hdlc_skb = dev_alloc_skb(skb->len + stuf_cnt + 6 + skb->mac_len);
|
||||
if (!hdlc_skb) {
|
||||
dev_kfree_skb_any(skb);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Copy link layer header into new skb */
|
||||
skb_reset_mac_header(hdlc_skb);
|
||||
skb_reserve(hdlc_skb, skb->mac_len);
|
||||
memcpy(skb_mac_header(hdlc_skb), skb_mac_header(skb), skb->mac_len);
|
||||
hdlc_skb->mac_len = skb->mac_len;
|
||||
|
||||
/* Add flag sequence in front of everything.. */
|
||||
*(skb_put(hdlc_skb, 1)) = PPP_FLAG;
|
||||
|
||||
/* Perform byte stuffing while copying data. */
|
||||
while (skb->len--) {
|
||||
if (muststuff(*skb->data)) {
|
||||
*(skb_put(hdlc_skb, 1)) = PPP_ESCAPE;
|
||||
*(skb_put(hdlc_skb, 1)) = (*skb->data++) ^ PPP_TRANS;
|
||||
} else
|
||||
*(skb_put(hdlc_skb, 1)) = *skb->data++;
|
||||
}
|
||||
|
||||
/* Finally add FCS (byte stuffed) and flag sequence */
|
||||
c = (fcs & 0x00ff); /* least significant byte first */
|
||||
if (muststuff(c)) {
|
||||
*(skb_put(hdlc_skb, 1)) = PPP_ESCAPE;
|
||||
c ^= PPP_TRANS;
|
||||
}
|
||||
*(skb_put(hdlc_skb, 1)) = c;
|
||||
|
||||
c = ((fcs >> 8) & 0x00ff);
|
||||
if (muststuff(c)) {
|
||||
*(skb_put(hdlc_skb, 1)) = PPP_ESCAPE;
|
||||
c ^= PPP_TRANS;
|
||||
}
|
||||
*(skb_put(hdlc_skb, 1)) = c;
|
||||
|
||||
*(skb_put(hdlc_skb, 1)) = PPP_FLAG;
|
||||
|
||||
dev_kfree_skb_any(skb);
|
||||
return hdlc_skb;
|
||||
}
|
||||
|
||||
/*
|
||||
* Encode a data packet into an octet stuffed raw bit inverted frame,
|
||||
* preserving headroom data.
|
||||
* parameters:
|
||||
* skb skb containing original packet (freed upon return)
|
||||
* Return value:
|
||||
* pointer to newly allocated skb containing the result frame
|
||||
* and the original link layer header, NULL on error
|
||||
*/
|
||||
static struct sk_buff *iraw_encode(struct sk_buff *skb)
|
||||
{
|
||||
struct sk_buff *iraw_skb;
|
||||
unsigned char c;
|
||||
unsigned char *cp;
|
||||
int len;
|
||||
|
||||
/* size of new buffer (worst case = every byte must be stuffed):
|
||||
* 2 * original size + room for link layer header
|
||||
*/
|
||||
iraw_skb = dev_alloc_skb(2 * skb->len + skb->mac_len);
|
||||
if (!iraw_skb) {
|
||||
dev_kfree_skb_any(skb);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* copy link layer header into new skb */
|
||||
skb_reset_mac_header(iraw_skb);
|
||||
skb_reserve(iraw_skb, skb->mac_len);
|
||||
memcpy(skb_mac_header(iraw_skb), skb_mac_header(skb), skb->mac_len);
|
||||
iraw_skb->mac_len = skb->mac_len;
|
||||
|
||||
/* copy and stuff data */
|
||||
cp = skb->data;
|
||||
len = skb->len;
|
||||
while (len--) {
|
||||
c = bitrev8(*cp++);
|
||||
if (c == DLE_FLAG)
|
||||
*(skb_put(iraw_skb, 1)) = c;
|
||||
*(skb_put(iraw_skb, 1)) = c;
|
||||
}
|
||||
dev_kfree_skb_any(skb);
|
||||
return iraw_skb;
|
||||
}
|
||||
|
||||
/**
|
||||
* gigaset_m10x_send_skb() - queue an skb for sending
|
||||
* @bcs: B channel descriptor structure.
|
||||
* @skb: data to send.
|
||||
*
|
||||
* Called by LL to encode and queue an skb for sending, and start
|
||||
* transmission if necessary.
|
||||
* Once the payload data has been transmitted completely, gigaset_skb_sent()
|
||||
* will be called with the skb's link layer header preserved.
|
||||
*
|
||||
* Return value:
|
||||
* number of bytes accepted for sending (skb->len) if ok,
|
||||
* error code < 0 (eg. -ENOMEM) on error
|
||||
*/
|
||||
int gigaset_m10x_send_skb(struct bc_state *bcs, struct sk_buff *skb)
|
||||
{
|
||||
struct cardstate *cs = bcs->cs;
|
||||
unsigned len = skb->len;
|
||||
unsigned long flags;
|
||||
|
||||
if (bcs->proto2 == L2_HDLC)
|
||||
skb = HDLC_Encode(skb);
|
||||
else
|
||||
skb = iraw_encode(skb);
|
||||
if (!skb) {
|
||||
dev_err(cs->dev,
|
||||
"unable to allocate memory for encoding!\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
skb_queue_tail(&bcs->squeue, skb);
|
||||
spin_lock_irqsave(&cs->lock, flags);
|
||||
if (cs->connected)
|
||||
tasklet_schedule(&cs->write_tasklet);
|
||||
spin_unlock_irqrestore(&cs->lock, flags);
|
||||
|
||||
return len; /* ok so far */
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(gigaset_m10x_send_skb);
|
2676
drivers/isdn/gigaset/bas-gigaset.c
Normal file
2676
drivers/isdn/gigaset/bas-gigaset.c
Normal file
File diff suppressed because it is too large
Load diff
2533
drivers/isdn/gigaset/capi.c
Normal file
2533
drivers/isdn/gigaset/capi.c
Normal file
File diff suppressed because it is too large
Load diff
1157
drivers/isdn/gigaset/common.c
Normal file
1157
drivers/isdn/gigaset/common.c
Normal file
File diff suppressed because it is too large
Load diff
77
drivers/isdn/gigaset/dummyll.c
Normal file
77
drivers/isdn/gigaset/dummyll.c
Normal file
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* Dummy LL interface for the Gigaset driver
|
||||
*
|
||||
* Copyright (c) 2009 by Tilman Schmidt <tilman@imap.cc>.
|
||||
*
|
||||
* =====================================================================
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
* =====================================================================
|
||||
*/
|
||||
|
||||
#include <linux/export.h>
|
||||
#include "gigaset.h"
|
||||
|
||||
void gigaset_skb_sent(struct bc_state *bcs, struct sk_buff *skb)
|
||||
{
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(gigaset_skb_sent);
|
||||
|
||||
void gigaset_skb_rcvd(struct bc_state *bcs, struct sk_buff *skb)
|
||||
{
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(gigaset_skb_rcvd);
|
||||
|
||||
void gigaset_isdn_rcv_err(struct bc_state *bcs)
|
||||
{
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(gigaset_isdn_rcv_err);
|
||||
|
||||
int gigaset_isdn_icall(struct at_state_t *at_state)
|
||||
{
|
||||
return ICALL_IGNORE;
|
||||
}
|
||||
|
||||
void gigaset_isdn_connD(struct bc_state *bcs)
|
||||
{
|
||||
}
|
||||
|
||||
void gigaset_isdn_hupD(struct bc_state *bcs)
|
||||
{
|
||||
}
|
||||
|
||||
void gigaset_isdn_connB(struct bc_state *bcs)
|
||||
{
|
||||
}
|
||||
|
||||
void gigaset_isdn_hupB(struct bc_state *bcs)
|
||||
{
|
||||
}
|
||||
|
||||
void gigaset_isdn_start(struct cardstate *cs)
|
||||
{
|
||||
}
|
||||
|
||||
void gigaset_isdn_stop(struct cardstate *cs)
|
||||
{
|
||||
}
|
||||
|
||||
int gigaset_isdn_regdev(struct cardstate *cs, const char *isdnid)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void gigaset_isdn_unregdev(struct cardstate *cs)
|
||||
{
|
||||
}
|
||||
|
||||
void gigaset_isdn_regdrv(void)
|
||||
{
|
||||
pr_info("no ISDN subsystem interface\n");
|
||||
}
|
||||
|
||||
void gigaset_isdn_unregdrv(void)
|
||||
{
|
||||
}
|
1888
drivers/isdn/gigaset/ev-layer.c
Normal file
1888
drivers/isdn/gigaset/ev-layer.c
Normal file
File diff suppressed because it is too large
Load diff
833
drivers/isdn/gigaset/gigaset.h
Normal file
833
drivers/isdn/gigaset/gigaset.h
Normal file
|
@ -0,0 +1,833 @@
|
|||
/*
|
||||
* Siemens Gigaset 307x driver
|
||||
* Common header file for all connection variants
|
||||
*
|
||||
* Written by Stefan Eilers
|
||||
* and Hansjoerg Lipp <hjlipp@web.de>
|
||||
*
|
||||
* =====================================================================
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
* =====================================================================
|
||||
*/
|
||||
|
||||
#ifndef GIGASET_H
|
||||
#define GIGASET_H
|
||||
|
||||
/* define global prefix for pr_ macros in linux/kernel.h */
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/ppp_defs.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/tty.h>
|
||||
#include <linux/tty_driver.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/atomic.h>
|
||||
|
||||
#define GIG_VERSION {0, 5, 0, 0}
|
||||
#define GIG_COMPAT {0, 4, 0, 0}
|
||||
|
||||
#define MAX_REC_PARAMS 10 /* Max. number of params in response string */
|
||||
#define MAX_RESP_SIZE 511 /* Max. size of a response string */
|
||||
|
||||
#define MAX_EVENTS 64 /* size of event queue */
|
||||
|
||||
#define RBUFSIZE 8192
|
||||
|
||||
#define GIG_TICK 100 /* in milliseconds */
|
||||
|
||||
/* timeout values (unit: 1 sec) */
|
||||
#define INIT_TIMEOUT 1
|
||||
|
||||
/* timeout values (unit: 0.1 sec) */
|
||||
#define RING_TIMEOUT 3 /* for additional parameters to RING */
|
||||
#define BAS_TIMEOUT 20 /* for response to Base USB ops */
|
||||
#define ATRDY_TIMEOUT 3 /* for HD_READY_SEND_ATDATA */
|
||||
|
||||
#define BAS_RETRY 3 /* max. retries for base USB ops */
|
||||
|
||||
#define MAXACT 3
|
||||
|
||||
extern int gigaset_debuglevel; /* "needs" cast to (enum debuglevel) */
|
||||
|
||||
/* debug flags, combine by adding/bitwise OR */
|
||||
enum debuglevel {
|
||||
DEBUG_INTR = 0x00008, /* interrupt processing */
|
||||
DEBUG_CMD = 0x00020, /* sent/received LL commands */
|
||||
DEBUG_STREAM = 0x00040, /* application data stream I/O events */
|
||||
DEBUG_STREAM_DUMP = 0x00080, /* application data stream content */
|
||||
DEBUG_LLDATA = 0x00100, /* sent/received LL data */
|
||||
DEBUG_EVENT = 0x00200, /* event processing */
|
||||
DEBUG_HDLC = 0x00800, /* M10x HDLC processing */
|
||||
DEBUG_CHANNEL = 0x01000, /* channel allocation/deallocation */
|
||||
DEBUG_TRANSCMD = 0x02000, /* AT-COMMANDS+RESPONSES */
|
||||
DEBUG_MCMD = 0x04000, /* COMMANDS THAT ARE SENT VERY OFTEN */
|
||||
DEBUG_INIT = 0x08000, /* (de)allocation+initialization of data
|
||||
structures */
|
||||
DEBUG_SUSPEND = 0x10000, /* suspend/resume processing */
|
||||
DEBUG_OUTPUT = 0x20000, /* output to device */
|
||||
DEBUG_ISO = 0x40000, /* isochronous transfers */
|
||||
DEBUG_IF = 0x80000, /* character device operations */
|
||||
DEBUG_USBREQ = 0x100000, /* USB communication (except payload
|
||||
data) */
|
||||
DEBUG_LOCKCMD = 0x200000, /* AT commands and responses when
|
||||
MS_LOCKED */
|
||||
|
||||
DEBUG_ANY = 0x3fffff, /* print message if any of the others is
|
||||
activated */
|
||||
};
|
||||
|
||||
#ifdef CONFIG_GIGASET_DEBUG
|
||||
|
||||
#define gig_dbg(level, format, arg...) \
|
||||
do { \
|
||||
if (unlikely(((enum debuglevel)gigaset_debuglevel) & (level))) \
|
||||
printk(KERN_DEBUG KBUILD_MODNAME ": " format "\n", \
|
||||
## arg); \
|
||||
} while (0)
|
||||
#define DEBUG_DEFAULT (DEBUG_TRANSCMD | DEBUG_CMD | DEBUG_USBREQ)
|
||||
|
||||
#else
|
||||
|
||||
#define gig_dbg(level, format, arg...) do {} while (0)
|
||||
#define DEBUG_DEFAULT 0
|
||||
|
||||
#endif
|
||||
|
||||
void gigaset_dbg_buffer(enum debuglevel level, const unsigned char *msg,
|
||||
size_t len, const unsigned char *buf);
|
||||
|
||||
/* connection state */
|
||||
#define ZSAU_NONE 0
|
||||
#define ZSAU_PROCEEDING 1
|
||||
#define ZSAU_CALL_DELIVERED 2
|
||||
#define ZSAU_ACTIVE 3
|
||||
#define ZSAU_DISCONNECT_IND 4
|
||||
#define ZSAU_NULL 5
|
||||
#define ZSAU_DISCONNECT_REQ 6
|
||||
#define ZSAU_UNKNOWN -1
|
||||
|
||||
/* USB control transfer requests */
|
||||
#define OUT_VENDOR_REQ (USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT)
|
||||
#define IN_VENDOR_REQ (USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT)
|
||||
|
||||
/* interrupt pipe messages */
|
||||
#define HD_B1_FLOW_CONTROL 0x80
|
||||
#define HD_B2_FLOW_CONTROL 0x81
|
||||
#define HD_RECEIVEATDATA_ACK (0x35) /* 3070 */
|
||||
#define HD_READY_SEND_ATDATA (0x36) /* 3070 */
|
||||
#define HD_OPEN_ATCHANNEL_ACK (0x37) /* 3070 */
|
||||
#define HD_CLOSE_ATCHANNEL_ACK (0x38) /* 3070 */
|
||||
#define HD_DEVICE_INIT_OK (0x11) /* ISurf USB + 3070 */
|
||||
#define HD_OPEN_B1CHANNEL_ACK (0x51) /* ISurf USB + 3070 */
|
||||
#define HD_OPEN_B2CHANNEL_ACK (0x52) /* ISurf USB + 3070 */
|
||||
#define HD_CLOSE_B1CHANNEL_ACK (0x53) /* ISurf USB + 3070 */
|
||||
#define HD_CLOSE_B2CHANNEL_ACK (0x54) /* ISurf USB + 3070 */
|
||||
#define HD_SUSPEND_END (0x61) /* ISurf USB */
|
||||
#define HD_RESET_INTERRUPT_PIPE_ACK (0xFF) /* ISurf USB + 3070 */
|
||||
|
||||
/* control requests */
|
||||
#define HD_OPEN_B1CHANNEL (0x23) /* ISurf USB + 3070 */
|
||||
#define HD_CLOSE_B1CHANNEL (0x24) /* ISurf USB + 3070 */
|
||||
#define HD_OPEN_B2CHANNEL (0x25) /* ISurf USB + 3070 */
|
||||
#define HD_CLOSE_B2CHANNEL (0x26) /* ISurf USB + 3070 */
|
||||
#define HD_RESET_INTERRUPT_PIPE (0x27) /* ISurf USB + 3070 */
|
||||
#define HD_DEVICE_INIT_ACK (0x34) /* ISurf USB + 3070 */
|
||||
#define HD_WRITE_ATMESSAGE (0x12) /* 3070 */
|
||||
#define HD_READ_ATMESSAGE (0x13) /* 3070 */
|
||||
#define HD_OPEN_ATCHANNEL (0x28) /* 3070 */
|
||||
#define HD_CLOSE_ATCHANNEL (0x29) /* 3070 */
|
||||
|
||||
/* number of B channels supported by base driver */
|
||||
#define BAS_CHANNELS 2
|
||||
|
||||
/* USB frames for isochronous transfer */
|
||||
#define BAS_FRAMETIME 1 /* number of milliseconds between frames */
|
||||
#define BAS_NUMFRAMES 8 /* number of frames per URB */
|
||||
#define BAS_MAXFRAME 16 /* allocated bytes per frame */
|
||||
#define BAS_NORMFRAME 8 /* send size without flow control */
|
||||
#define BAS_HIGHFRAME 10 /* " " with positive flow control */
|
||||
#define BAS_LOWFRAME 5 /* " " with negative flow control */
|
||||
#define BAS_CORRFRAMES 4 /* flow control multiplicator */
|
||||
|
||||
#define BAS_INBUFSIZE (BAS_MAXFRAME * BAS_NUMFRAMES) /* size of isoc in buf
|
||||
* per URB */
|
||||
#define BAS_OUTBUFSIZE 4096 /* size of common isoc out buffer */
|
||||
#define BAS_OUTBUFPAD BAS_MAXFRAME /* size of pad area for isoc out buf */
|
||||
|
||||
#define BAS_INURBS 3
|
||||
#define BAS_OUTURBS 3
|
||||
|
||||
/* variable commands in struct bc_state */
|
||||
#define AT_ISO 0
|
||||
#define AT_DIAL 1
|
||||
#define AT_MSN 2
|
||||
#define AT_BC 3
|
||||
#define AT_PROTO 4
|
||||
#define AT_TYPE 5
|
||||
#define AT_CLIP 6
|
||||
/* total number */
|
||||
#define AT_NUM 7
|
||||
|
||||
/* variables in struct at_state_t */
|
||||
/* - numeric */
|
||||
#define VAR_ZSAU 0
|
||||
#define VAR_ZDLE 1
|
||||
#define VAR_ZCTP 2
|
||||
/* total number */
|
||||
#define VAR_NUM 3
|
||||
/* - string */
|
||||
#define STR_NMBR 0
|
||||
#define STR_ZCPN 1
|
||||
#define STR_ZCON 2
|
||||
#define STR_ZBC 3
|
||||
#define STR_ZHLC 4
|
||||
/* total number */
|
||||
#define STR_NUM 5
|
||||
|
||||
/* event types */
|
||||
#define EV_TIMEOUT -105
|
||||
#define EV_IF_VER -106
|
||||
#define EV_PROC_CIDMODE -107
|
||||
#define EV_SHUTDOWN -108
|
||||
#define EV_START -110
|
||||
#define EV_STOP -111
|
||||
#define EV_IF_LOCK -112
|
||||
#define EV_ACCEPT -114
|
||||
#define EV_DIAL -115
|
||||
#define EV_HUP -116
|
||||
#define EV_BC_OPEN -117
|
||||
#define EV_BC_CLOSED -118
|
||||
|
||||
/* input state */
|
||||
#define INS_command 0x0001 /* receiving messages (not payload data) */
|
||||
#define INS_DLE_char 0x0002 /* DLE flag received (in DLE mode) */
|
||||
#define INS_byte_stuff 0x0004
|
||||
#define INS_have_data 0x0008
|
||||
#define INS_DLE_command 0x0020 /* DLE message start (<DLE> X) received */
|
||||
#define INS_flag_hunt 0x0040
|
||||
|
||||
/* channel state */
|
||||
#define CHS_D_UP 0x01
|
||||
#define CHS_B_UP 0x02
|
||||
#define CHS_NOTIFY_LL 0x04
|
||||
|
||||
#define ICALL_REJECT 0
|
||||
#define ICALL_ACCEPT 1
|
||||
#define ICALL_IGNORE 2
|
||||
|
||||
/* device state */
|
||||
#define MS_UNINITIALIZED 0
|
||||
#define MS_INIT 1
|
||||
#define MS_LOCKED 2
|
||||
#define MS_SHUTDOWN 3
|
||||
#define MS_RECOVER 4
|
||||
#define MS_READY 5
|
||||
|
||||
/* mode */
|
||||
#define M_UNKNOWN 0
|
||||
#define M_CONFIG 1
|
||||
#define M_UNIMODEM 2
|
||||
#define M_CID 3
|
||||
|
||||
/* start mode */
|
||||
#define SM_LOCKED 0
|
||||
#define SM_ISDN 1 /* default */
|
||||
|
||||
/* layer 2 protocols (AT^SBPR=...) */
|
||||
#define L2_BITSYNC 0
|
||||
#define L2_HDLC 1
|
||||
#define L2_VOICE 2
|
||||
|
||||
struct gigaset_ops;
|
||||
struct gigaset_driver;
|
||||
|
||||
struct usb_cardstate;
|
||||
struct ser_cardstate;
|
||||
struct bas_cardstate;
|
||||
|
||||
struct bc_state;
|
||||
struct usb_bc_state;
|
||||
struct ser_bc_state;
|
||||
struct bas_bc_state;
|
||||
|
||||
struct reply_t {
|
||||
int resp_code; /* RSP_XXXX */
|
||||
int min_ConState; /* <0 => ignore */
|
||||
int max_ConState; /* <0 => ignore */
|
||||
int parameter; /* e.g. ZSAU_XXXX <0: ignore*/
|
||||
int new_ConState; /* <0 => ignore */
|
||||
int timeout; /* >0 => *HZ; <=0 => TOUT_XXXX*/
|
||||
int action[MAXACT]; /* ACT_XXXX */
|
||||
char *command; /* NULL==none */
|
||||
};
|
||||
|
||||
extern struct reply_t gigaset_tab_cid[];
|
||||
extern struct reply_t gigaset_tab_nocid[];
|
||||
|
||||
struct inbuf_t {
|
||||
struct cardstate *cs;
|
||||
int inputstate;
|
||||
int head, tail;
|
||||
unsigned char data[RBUFSIZE];
|
||||
};
|
||||
|
||||
/* isochronous write buffer structure
|
||||
* circular buffer with pad area for extraction of complete USB frames
|
||||
* - data[read..nextread-1] is valid data already submitted to the USB subsystem
|
||||
* - data[nextread..write-1] is valid data yet to be sent
|
||||
* - data[write] is the next byte to write to
|
||||
* - in byte-oriented L2 procotols, it is completely free
|
||||
* - in bit-oriented L2 procotols, it may contain a partial byte of valid data
|
||||
* - data[write+1..read-1] is free
|
||||
* - wbits is the number of valid data bits in data[write], starting at the LSB
|
||||
* - writesem is the semaphore for writing to the buffer:
|
||||
* if writesem <= 0, data[write..read-1] is currently being written to
|
||||
* - idle contains the byte value to repeat when the end of valid data is
|
||||
* reached; if nextread==write (buffer contains no data to send), either the
|
||||
* BAS_OUTBUFPAD bytes immediately before data[write] (if
|
||||
* write>=BAS_OUTBUFPAD) or those of the pad area (if write<BAS_OUTBUFPAD)
|
||||
* are also filled with that value
|
||||
*/
|
||||
struct isowbuf_t {
|
||||
int read;
|
||||
int nextread;
|
||||
int write;
|
||||
atomic_t writesem;
|
||||
int wbits;
|
||||
unsigned char data[BAS_OUTBUFSIZE + BAS_OUTBUFPAD];
|
||||
unsigned char idle;
|
||||
};
|
||||
|
||||
/* isochronous write URB context structure
|
||||
* data to be stored along with the URB and retrieved when it is returned
|
||||
* as completed by the USB subsystem
|
||||
* - urb: pointer to the URB itself
|
||||
* - bcs: pointer to the B Channel control structure
|
||||
* - limit: end of write buffer area covered by this URB
|
||||
* - status: URB completion status
|
||||
*/
|
||||
struct isow_urbctx_t {
|
||||
struct urb *urb;
|
||||
struct bc_state *bcs;
|
||||
int limit;
|
||||
int status;
|
||||
};
|
||||
|
||||
/* AT state structure
|
||||
* data associated with the state of an ISDN connection, whether or not
|
||||
* it is currently assigned a B channel
|
||||
*/
|
||||
struct at_state_t {
|
||||
struct list_head list;
|
||||
int waiting;
|
||||
int getstring;
|
||||
unsigned timer_index;
|
||||
unsigned long timer_expires;
|
||||
int timer_active;
|
||||
unsigned int ConState; /* State of connection */
|
||||
struct reply_t *replystruct;
|
||||
int cid;
|
||||
int int_var[VAR_NUM]; /* see VAR_XXXX */
|
||||
char *str_var[STR_NUM]; /* see STR_XXXX */
|
||||
unsigned pending_commands; /* see PC_XXXX */
|
||||
unsigned seq_index;
|
||||
|
||||
struct cardstate *cs;
|
||||
struct bc_state *bcs;
|
||||
};
|
||||
|
||||
struct event_t {
|
||||
int type;
|
||||
void *ptr, *arg;
|
||||
int parameter;
|
||||
int cid;
|
||||
struct at_state_t *at_state;
|
||||
};
|
||||
|
||||
/* This buffer holds all information about the used B-Channel */
|
||||
struct bc_state {
|
||||
struct sk_buff *tx_skb; /* Current transfer buffer to modem */
|
||||
struct sk_buff_head squeue; /* B-Channel send Queue */
|
||||
|
||||
/* Variables for debugging .. */
|
||||
int corrupted; /* Counter for corrupted packages */
|
||||
int trans_down; /* Counter of packages (downstream) */
|
||||
int trans_up; /* Counter of packages (upstream) */
|
||||
|
||||
struct at_state_t at_state;
|
||||
|
||||
/* receive buffer */
|
||||
unsigned rx_bufsize; /* max size accepted by application */
|
||||
struct sk_buff *rx_skb;
|
||||
__u16 rx_fcs;
|
||||
int inputstate; /* see INS_XXXX */
|
||||
|
||||
int channel;
|
||||
|
||||
struct cardstate *cs;
|
||||
|
||||
unsigned chstate; /* bitmap (CHS_*) */
|
||||
int ignore;
|
||||
unsigned proto2; /* layer 2 protocol (L2_*) */
|
||||
char *commands[AT_NUM]; /* see AT_XXXX */
|
||||
|
||||
#ifdef CONFIG_GIGASET_DEBUG
|
||||
int emptycount;
|
||||
#endif
|
||||
int busy;
|
||||
int use_count;
|
||||
|
||||
/* private data of hardware drivers */
|
||||
union {
|
||||
struct ser_bc_state *ser; /* serial hardware driver */
|
||||
struct usb_bc_state *usb; /* usb hardware driver (m105) */
|
||||
struct bas_bc_state *bas; /* usb hardware driver (base) */
|
||||
} hw;
|
||||
|
||||
void *ap; /* associated LL application */
|
||||
int apconnstate; /* LL application connection state */
|
||||
spinlock_t aplock;
|
||||
};
|
||||
|
||||
struct cardstate {
|
||||
struct gigaset_driver *driver;
|
||||
unsigned minor_index;
|
||||
struct device *dev;
|
||||
struct device *tty_dev;
|
||||
unsigned flags;
|
||||
|
||||
const struct gigaset_ops *ops;
|
||||
|
||||
/* Stuff to handle communication */
|
||||
wait_queue_head_t waitqueue;
|
||||
int waiting;
|
||||
int mode; /* see M_XXXX */
|
||||
int mstate; /* Modem state: see MS_XXXX */
|
||||
/* only changed by the event layer */
|
||||
int cmd_result;
|
||||
|
||||
int channels;
|
||||
struct bc_state *bcs; /* Array of struct bc_state */
|
||||
|
||||
int onechannel; /* data and commands transmitted in one
|
||||
stream (M10x) */
|
||||
|
||||
spinlock_t lock;
|
||||
struct at_state_t at_state; /* at_state_t for cid == 0 */
|
||||
struct list_head temp_at_states;/* list of temporary "struct
|
||||
at_state_t"s without B channel */
|
||||
|
||||
struct inbuf_t *inbuf;
|
||||
|
||||
struct cmdbuf_t *cmdbuf, *lastcmdbuf;
|
||||
spinlock_t cmdlock;
|
||||
unsigned curlen, cmdbytes;
|
||||
|
||||
struct tty_port port;
|
||||
struct tasklet_struct if_wake_tasklet;
|
||||
unsigned control_state;
|
||||
|
||||
unsigned fwver[4];
|
||||
int gotfwver;
|
||||
|
||||
unsigned running; /* !=0 if events are handled */
|
||||
unsigned connected; /* !=0 if hardware is connected */
|
||||
unsigned isdn_up; /* !=0 after gigaset_isdn_start() */
|
||||
|
||||
unsigned cidmode;
|
||||
|
||||
int myid; /* id for communication with LL */
|
||||
void *iif; /* LL interface structure */
|
||||
unsigned short hw_hdr_len; /* headroom needed in data skbs */
|
||||
|
||||
struct reply_t *tabnocid;
|
||||
struct reply_t *tabcid;
|
||||
int cs_init;
|
||||
int ignoreframes; /* frames to ignore after setting up the
|
||||
B channel */
|
||||
struct mutex mutex; /* locks this structure:
|
||||
* connected is not changed,
|
||||
* hardware_up is not changed,
|
||||
* MState is not changed to or from
|
||||
* MS_LOCKED */
|
||||
|
||||
struct timer_list timer;
|
||||
int retry_count;
|
||||
int dle; /* !=0 if DLE mode is active
|
||||
(ZDLE=1 received -- M10x only) */
|
||||
int cur_at_seq; /* sequence of AT commands being
|
||||
processed */
|
||||
int curchannel; /* channel those commands are meant
|
||||
for */
|
||||
int commands_pending; /* flag(s) in xxx.commands_pending have
|
||||
been set */
|
||||
struct tasklet_struct
|
||||
event_tasklet; /* tasklet for serializing AT commands.
|
||||
* Scheduled
|
||||
* -> for modem reponses (and
|
||||
* incoming data for M10x)
|
||||
* -> on timeout
|
||||
* -> after setting bits in
|
||||
* xxx.at_state.pending_command
|
||||
* (e.g. command from LL) */
|
||||
struct tasklet_struct
|
||||
write_tasklet; /* tasklet for serial output
|
||||
* (not used in base driver) */
|
||||
|
||||
/* event queue */
|
||||
struct event_t events[MAX_EVENTS];
|
||||
unsigned ev_tail, ev_head;
|
||||
spinlock_t ev_lock;
|
||||
|
||||
/* current modem response */
|
||||
unsigned char respdata[MAX_RESP_SIZE + 1];
|
||||
unsigned cbytes;
|
||||
|
||||
/* private data of hardware drivers */
|
||||
union {
|
||||
struct usb_cardstate *usb; /* USB hardware driver (m105) */
|
||||
struct ser_cardstate *ser; /* serial hardware driver */
|
||||
struct bas_cardstate *bas; /* USB hardware driver (base) */
|
||||
} hw;
|
||||
};
|
||||
|
||||
struct gigaset_driver {
|
||||
struct list_head list;
|
||||
spinlock_t lock; /* locks minor tables and blocked */
|
||||
struct tty_driver *tty;
|
||||
unsigned have_tty;
|
||||
unsigned minor;
|
||||
unsigned minors;
|
||||
struct cardstate *cs;
|
||||
int blocked;
|
||||
|
||||
const struct gigaset_ops *ops;
|
||||
struct module *owner;
|
||||
};
|
||||
|
||||
struct cmdbuf_t {
|
||||
struct cmdbuf_t *next, *prev;
|
||||
int len, offset;
|
||||
struct tasklet_struct *wake_tasklet;
|
||||
unsigned char buf[0];
|
||||
};
|
||||
|
||||
struct bas_bc_state {
|
||||
/* isochronous output state */
|
||||
int running;
|
||||
atomic_t corrbytes;
|
||||
spinlock_t isooutlock;
|
||||
struct isow_urbctx_t isoouturbs[BAS_OUTURBS];
|
||||
struct isow_urbctx_t *isooutdone, *isooutfree, *isooutovfl;
|
||||
struct isowbuf_t *isooutbuf;
|
||||
unsigned numsub; /* submitted URB counter
|
||||
(for diagnostic messages only) */
|
||||
struct tasklet_struct sent_tasklet;
|
||||
|
||||
/* isochronous input state */
|
||||
spinlock_t isoinlock;
|
||||
struct urb *isoinurbs[BAS_INURBS];
|
||||
unsigned char isoinbuf[BAS_INBUFSIZE * BAS_INURBS];
|
||||
struct urb *isoindone; /* completed isoc read URB */
|
||||
int isoinstatus; /* status of completed URB */
|
||||
int loststatus; /* status of dropped URB */
|
||||
unsigned isoinlost; /* number of bytes lost */
|
||||
/* state of bit unstuffing algorithm
|
||||
(in addition to BC_state.inputstate) */
|
||||
unsigned seqlen; /* number of '1' bits not yet
|
||||
unstuffed */
|
||||
unsigned inbyte, inbits; /* collected bits for next byte */
|
||||
/* statistics */
|
||||
unsigned goodbytes; /* bytes correctly received */
|
||||
unsigned alignerrs; /* frames with incomplete byte at end */
|
||||
unsigned fcserrs; /* FCS errors */
|
||||
unsigned frameerrs; /* framing errors */
|
||||
unsigned giants; /* long frames */
|
||||
unsigned runts; /* short frames */
|
||||
unsigned aborts; /* HDLC aborts */
|
||||
unsigned shared0s; /* '0' bits shared between flags */
|
||||
unsigned stolen0s; /* '0' stuff bits also serving as
|
||||
leading flag bits */
|
||||
struct tasklet_struct rcvd_tasklet;
|
||||
};
|
||||
|
||||
struct gigaset_ops {
|
||||
/* Called from ev-layer.c/interface.c for sending AT commands to the
|
||||
device */
|
||||
int (*write_cmd)(struct cardstate *cs, struct cmdbuf_t *cb);
|
||||
|
||||
/* Called from interface.c for additional device control */
|
||||
int (*write_room)(struct cardstate *cs);
|
||||
int (*chars_in_buffer)(struct cardstate *cs);
|
||||
int (*brkchars)(struct cardstate *cs, const unsigned char buf[6]);
|
||||
|
||||
/* Called from ev-layer.c after setting up connection
|
||||
* Should call gigaset_bchannel_up(), when finished. */
|
||||
int (*init_bchannel)(struct bc_state *bcs);
|
||||
|
||||
/* Called from ev-layer.c after hanging up
|
||||
* Should call gigaset_bchannel_down(), when finished. */
|
||||
int (*close_bchannel)(struct bc_state *bcs);
|
||||
|
||||
/* Called by gigaset_initcs() for setting up bcs->hw.xxx */
|
||||
int (*initbcshw)(struct bc_state *bcs);
|
||||
|
||||
/* Called by gigaset_freecs() for freeing bcs->hw.xxx */
|
||||
void (*freebcshw)(struct bc_state *bcs);
|
||||
|
||||
/* Called by gigaset_bchannel_down() for resetting bcs->hw.xxx */
|
||||
void (*reinitbcshw)(struct bc_state *bcs);
|
||||
|
||||
/* Called by gigaset_initcs() for setting up cs->hw.xxx */
|
||||
int (*initcshw)(struct cardstate *cs);
|
||||
|
||||
/* Called by gigaset_freecs() for freeing cs->hw.xxx */
|
||||
void (*freecshw)(struct cardstate *cs);
|
||||
|
||||
/* Called from common.c/interface.c for additional serial port
|
||||
control */
|
||||
int (*set_modem_ctrl)(struct cardstate *cs, unsigned old_state,
|
||||
unsigned new_state);
|
||||
int (*baud_rate)(struct cardstate *cs, unsigned cflag);
|
||||
int (*set_line_ctrl)(struct cardstate *cs, unsigned cflag);
|
||||
|
||||
/* Called from LL interface to put an skb into the send-queue.
|
||||
* After sending is completed, gigaset_skb_sent() must be called
|
||||
* with the skb's link layer header preserved. */
|
||||
int (*send_skb)(struct bc_state *bcs, struct sk_buff *skb);
|
||||
|
||||
/* Called from ev-layer.c to process a block of data
|
||||
* received through the common/control channel. */
|
||||
void (*handle_input)(struct inbuf_t *inbuf);
|
||||
|
||||
};
|
||||
|
||||
/* = Common structures and definitions =======================================
|
||||
*/
|
||||
|
||||
/* Parser states for DLE-Event:
|
||||
* <DLE-EVENT>: <DLE_FLAG> "X" <EVENT> <DLE_FLAG> "."
|
||||
* <DLE_FLAG>: 0x10
|
||||
* <EVENT>: ((a-z)* | (A-Z)* | (0-10)*)+
|
||||
*/
|
||||
#define DLE_FLAG 0x10
|
||||
|
||||
/* ===========================================================================
|
||||
* Functions implemented in asyncdata.c
|
||||
*/
|
||||
|
||||
/* Called from LL interface to put an skb into the send queue. */
|
||||
int gigaset_m10x_send_skb(struct bc_state *bcs, struct sk_buff *skb);
|
||||
|
||||
/* Called from ev-layer.c to process a block of data
|
||||
* received through the common/control channel. */
|
||||
void gigaset_m10x_input(struct inbuf_t *inbuf);
|
||||
|
||||
/* ===========================================================================
|
||||
* Functions implemented in isocdata.c
|
||||
*/
|
||||
|
||||
/* Called from LL interface to put an skb into the send queue. */
|
||||
int gigaset_isoc_send_skb(struct bc_state *bcs, struct sk_buff *skb);
|
||||
|
||||
/* Called from ev-layer.c to process a block of data
|
||||
* received through the common/control channel. */
|
||||
void gigaset_isoc_input(struct inbuf_t *inbuf);
|
||||
|
||||
/* Called from bas-gigaset.c to process a block of data
|
||||
* received through the isochronous channel */
|
||||
void gigaset_isoc_receive(unsigned char *src, unsigned count,
|
||||
struct bc_state *bcs);
|
||||
|
||||
/* Called from bas-gigaset.c to put a block of data
|
||||
* into the isochronous output buffer */
|
||||
int gigaset_isoc_buildframe(struct bc_state *bcs, unsigned char *in, int len);
|
||||
|
||||
/* Called from bas-gigaset.c to initialize the isochronous output buffer */
|
||||
void gigaset_isowbuf_init(struct isowbuf_t *iwb, unsigned char idle);
|
||||
|
||||
/* Called from bas-gigaset.c to retrieve a block of bytes for sending */
|
||||
int gigaset_isowbuf_getbytes(struct isowbuf_t *iwb, int size);
|
||||
|
||||
/* ===========================================================================
|
||||
* Functions implemented in LL interface
|
||||
*/
|
||||
|
||||
/* Called from common.c for setting up/shutting down with the ISDN subsystem */
|
||||
void gigaset_isdn_regdrv(void);
|
||||
void gigaset_isdn_unregdrv(void);
|
||||
int gigaset_isdn_regdev(struct cardstate *cs, const char *isdnid);
|
||||
void gigaset_isdn_unregdev(struct cardstate *cs);
|
||||
|
||||
/* Called from hardware module to indicate completion of an skb */
|
||||
void gigaset_skb_sent(struct bc_state *bcs, struct sk_buff *skb);
|
||||
void gigaset_skb_rcvd(struct bc_state *bcs, struct sk_buff *skb);
|
||||
void gigaset_isdn_rcv_err(struct bc_state *bcs);
|
||||
|
||||
/* Called from common.c/ev-layer.c to indicate events relevant to the LL */
|
||||
void gigaset_isdn_start(struct cardstate *cs);
|
||||
void gigaset_isdn_stop(struct cardstate *cs);
|
||||
int gigaset_isdn_icall(struct at_state_t *at_state);
|
||||
void gigaset_isdn_connD(struct bc_state *bcs);
|
||||
void gigaset_isdn_hupD(struct bc_state *bcs);
|
||||
void gigaset_isdn_connB(struct bc_state *bcs);
|
||||
void gigaset_isdn_hupB(struct bc_state *bcs);
|
||||
|
||||
/* ===========================================================================
|
||||
* Functions implemented in ev-layer.c
|
||||
*/
|
||||
|
||||
/* tasklet called from common.c to process queued events */
|
||||
void gigaset_handle_event(unsigned long data);
|
||||
|
||||
/* called from isocdata.c / asyncdata.c
|
||||
* when a complete modem response line has been received */
|
||||
void gigaset_handle_modem_response(struct cardstate *cs);
|
||||
|
||||
/* ===========================================================================
|
||||
* Functions implemented in proc.c
|
||||
*/
|
||||
|
||||
/* initialize sysfs for device */
|
||||
void gigaset_init_dev_sysfs(struct cardstate *cs);
|
||||
void gigaset_free_dev_sysfs(struct cardstate *cs);
|
||||
|
||||
/* ===========================================================================
|
||||
* Functions implemented in common.c/gigaset.h
|
||||
*/
|
||||
|
||||
void gigaset_bcs_reinit(struct bc_state *bcs);
|
||||
void gigaset_at_init(struct at_state_t *at_state, struct bc_state *bcs,
|
||||
struct cardstate *cs, int cid);
|
||||
int gigaset_get_channel(struct bc_state *bcs);
|
||||
struct bc_state *gigaset_get_free_channel(struct cardstate *cs);
|
||||
void gigaset_free_channel(struct bc_state *bcs);
|
||||
int gigaset_get_channels(struct cardstate *cs);
|
||||
void gigaset_free_channels(struct cardstate *cs);
|
||||
void gigaset_block_channels(struct cardstate *cs);
|
||||
|
||||
/* Allocate and initialize driver structure. */
|
||||
struct gigaset_driver *gigaset_initdriver(unsigned minor, unsigned minors,
|
||||
const char *procname,
|
||||
const char *devname,
|
||||
const struct gigaset_ops *ops,
|
||||
struct module *owner);
|
||||
|
||||
/* Deallocate driver structure. */
|
||||
void gigaset_freedriver(struct gigaset_driver *drv);
|
||||
|
||||
struct cardstate *gigaset_get_cs_by_tty(struct tty_struct *tty);
|
||||
struct cardstate *gigaset_get_cs_by_id(int id);
|
||||
void gigaset_blockdriver(struct gigaset_driver *drv);
|
||||
|
||||
/* Allocate and initialize card state. Calls hardware dependent
|
||||
gigaset_init[b]cs(). */
|
||||
struct cardstate *gigaset_initcs(struct gigaset_driver *drv, int channels,
|
||||
int onechannel, int ignoreframes,
|
||||
int cidmode, const char *modulename);
|
||||
|
||||
/* Free card state. Calls hardware dependent gigaset_free[b]cs(). */
|
||||
void gigaset_freecs(struct cardstate *cs);
|
||||
|
||||
/* Tell common.c that hardware and driver are ready. */
|
||||
int gigaset_start(struct cardstate *cs);
|
||||
|
||||
/* Tell common.c that the device is not present any more. */
|
||||
void gigaset_stop(struct cardstate *cs);
|
||||
|
||||
/* Tell common.c that the driver is being unloaded. */
|
||||
int gigaset_shutdown(struct cardstate *cs);
|
||||
|
||||
/* Tell common.c that an skb has been sent. */
|
||||
void gigaset_skb_sent(struct bc_state *bcs, struct sk_buff *skb);
|
||||
|
||||
/* Append event to the queue.
|
||||
* Returns NULL on failure or a pointer to the event on success.
|
||||
* ptr must be kmalloc()ed (and not be freed by the caller).
|
||||
*/
|
||||
struct event_t *gigaset_add_event(struct cardstate *cs,
|
||||
struct at_state_t *at_state, int type,
|
||||
void *ptr, int parameter, void *arg);
|
||||
|
||||
/* Called on CONFIG1 command from frontend. */
|
||||
int gigaset_enterconfigmode(struct cardstate *cs);
|
||||
|
||||
/* cs->lock must not be locked */
|
||||
static inline void gigaset_schedule_event(struct cardstate *cs)
|
||||
{
|
||||
unsigned long flags;
|
||||
spin_lock_irqsave(&cs->lock, flags);
|
||||
if (cs->running)
|
||||
tasklet_schedule(&cs->event_tasklet);
|
||||
spin_unlock_irqrestore(&cs->lock, flags);
|
||||
}
|
||||
|
||||
/* Tell common.c that B channel has been closed. */
|
||||
/* cs->lock must not be locked */
|
||||
static inline void gigaset_bchannel_down(struct bc_state *bcs)
|
||||
{
|
||||
gigaset_add_event(bcs->cs, &bcs->at_state, EV_BC_CLOSED, NULL, 0, NULL);
|
||||
gigaset_schedule_event(bcs->cs);
|
||||
}
|
||||
|
||||
/* Tell common.c that B channel has been opened. */
|
||||
/* cs->lock must not be locked */
|
||||
static inline void gigaset_bchannel_up(struct bc_state *bcs)
|
||||
{
|
||||
gigaset_add_event(bcs->cs, &bcs->at_state, EV_BC_OPEN, NULL, 0, NULL);
|
||||
gigaset_schedule_event(bcs->cs);
|
||||
}
|
||||
|
||||
/* set up next receive skb for data mode */
|
||||
static inline struct sk_buff *gigaset_new_rx_skb(struct bc_state *bcs)
|
||||
{
|
||||
struct cardstate *cs = bcs->cs;
|
||||
unsigned short hw_hdr_len = cs->hw_hdr_len;
|
||||
|
||||
if (bcs->ignore) {
|
||||
bcs->rx_skb = NULL;
|
||||
} else {
|
||||
bcs->rx_skb = dev_alloc_skb(bcs->rx_bufsize + hw_hdr_len);
|
||||
if (bcs->rx_skb == NULL)
|
||||
dev_warn(cs->dev, "could not allocate skb\n");
|
||||
else
|
||||
skb_reserve(bcs->rx_skb, hw_hdr_len);
|
||||
}
|
||||
return bcs->rx_skb;
|
||||
}
|
||||
|
||||
/* append received bytes to inbuf */
|
||||
int gigaset_fill_inbuf(struct inbuf_t *inbuf, const unsigned char *src,
|
||||
unsigned numbytes);
|
||||
|
||||
/* ===========================================================================
|
||||
* Functions implemented in interface.c
|
||||
*/
|
||||
|
||||
/* initialize interface */
|
||||
void gigaset_if_initdriver(struct gigaset_driver *drv, const char *procname,
|
||||
const char *devname);
|
||||
/* release interface */
|
||||
void gigaset_if_freedriver(struct gigaset_driver *drv);
|
||||
/* add minor */
|
||||
void gigaset_if_init(struct cardstate *cs);
|
||||
/* remove minor */
|
||||
void gigaset_if_free(struct cardstate *cs);
|
||||
/* device received data */
|
||||
void gigaset_if_receive(struct cardstate *cs,
|
||||
unsigned char *buffer, size_t len);
|
||||
|
||||
#endif
|
695
drivers/isdn/gigaset/i4l.c
Normal file
695
drivers/isdn/gigaset/i4l.c
Normal file
|
@ -0,0 +1,695 @@
|
|||
/*
|
||||
* Stuff used by all variants of the driver
|
||||
*
|
||||
* Copyright (c) 2001 by Stefan Eilers,
|
||||
* Hansjoerg Lipp <hjlipp@web.de>,
|
||||
* Tilman Schmidt <tilman@imap.cc>.
|
||||
*
|
||||
* =====================================================================
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
* =====================================================================
|
||||
*/
|
||||
|
||||
#include "gigaset.h"
|
||||
#include <linux/isdnif.h>
|
||||
#include <linux/export.h>
|
||||
|
||||
#define SBUFSIZE 4096 /* sk_buff payload size */
|
||||
#define TRANSBUFSIZE 768 /* bytes per skb for transparent receive */
|
||||
#define HW_HDR_LEN 2 /* Header size used to store ack info */
|
||||
#define MAX_BUF_SIZE (SBUFSIZE - HW_HDR_LEN) /* max data packet from LL */
|
||||
|
||||
/* == Handling of I4L IO =====================================================*/
|
||||
|
||||
/* writebuf_from_LL
|
||||
* called by LL to transmit data on an open channel
|
||||
* inserts the buffer data into the send queue and starts the transmission
|
||||
* Note that this operation must not sleep!
|
||||
* When the buffer is processed completely, gigaset_skb_sent() should be called.
|
||||
* parameters:
|
||||
* driverID driver ID as assigned by LL
|
||||
* channel channel number
|
||||
* ack if != 0 LL wants to be notified on completion via
|
||||
* statcallb(ISDN_STAT_BSENT)
|
||||
* skb skb containing data to send
|
||||
* return value:
|
||||
* number of accepted bytes
|
||||
* 0 if temporarily unable to accept data (out of buffer space)
|
||||
* <0 on error (eg. -EINVAL)
|
||||
*/
|
||||
static int writebuf_from_LL(int driverID, int channel, int ack,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct cardstate *cs = gigaset_get_cs_by_id(driverID);
|
||||
struct bc_state *bcs;
|
||||
unsigned char *ack_header;
|
||||
unsigned len;
|
||||
|
||||
if (!cs) {
|
||||
pr_err("%s: invalid driver ID (%d)\n", __func__, driverID);
|
||||
return -ENODEV;
|
||||
}
|
||||
if (channel < 0 || channel >= cs->channels) {
|
||||
dev_err(cs->dev, "%s: invalid channel ID (%d)\n",
|
||||
__func__, channel);
|
||||
return -ENODEV;
|
||||
}
|
||||
bcs = &cs->bcs[channel];
|
||||
|
||||
/* can only handle linear sk_buffs */
|
||||
if (skb_linearize(skb) < 0) {
|
||||
dev_err(cs->dev, "%s: skb_linearize failed\n", __func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
len = skb->len;
|
||||
|
||||
gig_dbg(DEBUG_LLDATA,
|
||||
"Receiving data from LL (id: %d, ch: %d, ack: %d, sz: %d)",
|
||||
driverID, channel, ack, len);
|
||||
|
||||
if (!len) {
|
||||
if (ack)
|
||||
dev_notice(cs->dev, "%s: not ACKing empty packet\n",
|
||||
__func__);
|
||||
return 0;
|
||||
}
|
||||
if (len > MAX_BUF_SIZE) {
|
||||
dev_err(cs->dev, "%s: packet too large (%d bytes)\n",
|
||||
__func__, len);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* set up acknowledgement header */
|
||||
if (skb_headroom(skb) < HW_HDR_LEN) {
|
||||
/* should never happen */
|
||||
dev_err(cs->dev, "%s: insufficient skb headroom\n", __func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
skb_set_mac_header(skb, -HW_HDR_LEN);
|
||||
skb->mac_len = HW_HDR_LEN;
|
||||
ack_header = skb_mac_header(skb);
|
||||
if (ack) {
|
||||
ack_header[0] = len & 0xff;
|
||||
ack_header[1] = len >> 8;
|
||||
} else {
|
||||
ack_header[0] = ack_header[1] = 0;
|
||||
}
|
||||
gig_dbg(DEBUG_MCMD, "skb: len=%u, ack=%d: %02x %02x",
|
||||
len, ack, ack_header[0], ack_header[1]);
|
||||
|
||||
/* pass to device-specific module */
|
||||
return cs->ops->send_skb(bcs, skb);
|
||||
}
|
||||
|
||||
/**
|
||||
* gigaset_skb_sent() - acknowledge sending an skb
|
||||
* @bcs: B channel descriptor structure.
|
||||
* @skb: sent data.
|
||||
*
|
||||
* Called by hardware module {bas,ser,usb}_gigaset when the data in a
|
||||
* skb has been successfully sent, for signalling completion to the LL.
|
||||
*/
|
||||
void gigaset_skb_sent(struct bc_state *bcs, struct sk_buff *skb)
|
||||
{
|
||||
isdn_if *iif = bcs->cs->iif;
|
||||
unsigned char *ack_header = skb_mac_header(skb);
|
||||
unsigned len;
|
||||
isdn_ctrl response;
|
||||
|
||||
++bcs->trans_up;
|
||||
|
||||
if (skb->len)
|
||||
dev_warn(bcs->cs->dev, "%s: skb->len==%d\n",
|
||||
__func__, skb->len);
|
||||
|
||||
len = ack_header[0] + ((unsigned) ack_header[1] << 8);
|
||||
if (len) {
|
||||
gig_dbg(DEBUG_MCMD, "ACKing to LL (id: %d, ch: %d, sz: %u)",
|
||||
bcs->cs->myid, bcs->channel, len);
|
||||
|
||||
response.driver = bcs->cs->myid;
|
||||
response.command = ISDN_STAT_BSENT;
|
||||
response.arg = bcs->channel;
|
||||
response.parm.length = len;
|
||||
iif->statcallb(&response);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(gigaset_skb_sent);
|
||||
|
||||
/**
|
||||
* gigaset_skb_rcvd() - pass received skb to LL
|
||||
* @bcs: B channel descriptor structure.
|
||||
* @skb: received data.
|
||||
*
|
||||
* Called by hardware module {bas,ser,usb}_gigaset when user data has
|
||||
* been successfully received, for passing to the LL.
|
||||
* Warning: skb must not be accessed anymore!
|
||||
*/
|
||||
void gigaset_skb_rcvd(struct bc_state *bcs, struct sk_buff *skb)
|
||||
{
|
||||
isdn_if *iif = bcs->cs->iif;
|
||||
|
||||
iif->rcvcallb_skb(bcs->cs->myid, bcs->channel, skb);
|
||||
bcs->trans_down++;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(gigaset_skb_rcvd);
|
||||
|
||||
/**
|
||||
* gigaset_isdn_rcv_err() - signal receive error
|
||||
* @bcs: B channel descriptor structure.
|
||||
*
|
||||
* Called by hardware module {bas,ser,usb}_gigaset when a receive error
|
||||
* has occurred, for signalling to the LL.
|
||||
*/
|
||||
void gigaset_isdn_rcv_err(struct bc_state *bcs)
|
||||
{
|
||||
isdn_if *iif = bcs->cs->iif;
|
||||
isdn_ctrl response;
|
||||
|
||||
/* if currently ignoring packets, just count down */
|
||||
if (bcs->ignore) {
|
||||
bcs->ignore--;
|
||||
return;
|
||||
}
|
||||
|
||||
/* update statistics */
|
||||
bcs->corrupted++;
|
||||
|
||||
/* error -> LL */
|
||||
gig_dbg(DEBUG_CMD, "sending L1ERR");
|
||||
response.driver = bcs->cs->myid;
|
||||
response.command = ISDN_STAT_L1ERR;
|
||||
response.arg = bcs->channel;
|
||||
response.parm.errcode = ISDN_STAT_L1ERR_RECV;
|
||||
iif->statcallb(&response);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(gigaset_isdn_rcv_err);
|
||||
|
||||
/* This function will be called by LL to send commands
|
||||
* NOTE: LL ignores the returned value, for commands other than ISDN_CMD_IOCTL,
|
||||
* so don't put too much effort into it.
|
||||
*/
|
||||
static int command_from_LL(isdn_ctrl *cntrl)
|
||||
{
|
||||
struct cardstate *cs;
|
||||
struct bc_state *bcs;
|
||||
int retval = 0;
|
||||
char **commands;
|
||||
int ch;
|
||||
int i;
|
||||
size_t l;
|
||||
|
||||
gig_dbg(DEBUG_CMD, "driver: %d, command: %d, arg: 0x%lx",
|
||||
cntrl->driver, cntrl->command, cntrl->arg);
|
||||
|
||||
cs = gigaset_get_cs_by_id(cntrl->driver);
|
||||
if (cs == NULL) {
|
||||
pr_err("%s: invalid driver ID (%d)\n", __func__, cntrl->driver);
|
||||
return -ENODEV;
|
||||
}
|
||||
ch = cntrl->arg & 0xff;
|
||||
|
||||
switch (cntrl->command) {
|
||||
case ISDN_CMD_IOCTL:
|
||||
dev_warn(cs->dev, "ISDN_CMD_IOCTL not supported\n");
|
||||
return -EINVAL;
|
||||
|
||||
case ISDN_CMD_DIAL:
|
||||
gig_dbg(DEBUG_CMD,
|
||||
"ISDN_CMD_DIAL (phone: %s, msn: %s, si1: %d, si2: %d)",
|
||||
cntrl->parm.setup.phone, cntrl->parm.setup.eazmsn,
|
||||
cntrl->parm.setup.si1, cntrl->parm.setup.si2);
|
||||
|
||||
if (ch >= cs->channels) {
|
||||
dev_err(cs->dev,
|
||||
"ISDN_CMD_DIAL: invalid channel (%d)\n", ch);
|
||||
return -EINVAL;
|
||||
}
|
||||
bcs = cs->bcs + ch;
|
||||
if (gigaset_get_channel(bcs) < 0) {
|
||||
dev_err(cs->dev, "ISDN_CMD_DIAL: channel not free\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
switch (bcs->proto2) {
|
||||
case L2_HDLC:
|
||||
bcs->rx_bufsize = SBUFSIZE;
|
||||
break;
|
||||
default: /* assume transparent */
|
||||
bcs->rx_bufsize = TRANSBUFSIZE;
|
||||
}
|
||||
dev_kfree_skb(bcs->rx_skb);
|
||||
gigaset_new_rx_skb(bcs);
|
||||
|
||||
commands = kzalloc(AT_NUM * (sizeof *commands), GFP_ATOMIC);
|
||||
if (!commands) {
|
||||
gigaset_free_channel(bcs);
|
||||
dev_err(cs->dev, "ISDN_CMD_DIAL: out of memory\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
l = 3 + strlen(cntrl->parm.setup.phone);
|
||||
commands[AT_DIAL] = kmalloc(l, GFP_ATOMIC);
|
||||
if (!commands[AT_DIAL])
|
||||
goto oom;
|
||||
if (cntrl->parm.setup.phone[0] == '*' &&
|
||||
cntrl->parm.setup.phone[1] == '*') {
|
||||
/* internal call: translate ** prefix to CTP value */
|
||||
commands[AT_TYPE] = kstrdup("^SCTP=0\r", GFP_ATOMIC);
|
||||
if (!commands[AT_TYPE])
|
||||
goto oom;
|
||||
snprintf(commands[AT_DIAL], l,
|
||||
"D%s\r", cntrl->parm.setup.phone + 2);
|
||||
} else {
|
||||
commands[AT_TYPE] = kstrdup("^SCTP=1\r", GFP_ATOMIC);
|
||||
if (!commands[AT_TYPE])
|
||||
goto oom;
|
||||
snprintf(commands[AT_DIAL], l,
|
||||
"D%s\r", cntrl->parm.setup.phone);
|
||||
}
|
||||
|
||||
l = strlen(cntrl->parm.setup.eazmsn);
|
||||
if (l) {
|
||||
l += 8;
|
||||
commands[AT_MSN] = kmalloc(l, GFP_ATOMIC);
|
||||
if (!commands[AT_MSN])
|
||||
goto oom;
|
||||
snprintf(commands[AT_MSN], l, "^SMSN=%s\r",
|
||||
cntrl->parm.setup.eazmsn);
|
||||
}
|
||||
|
||||
switch (cntrl->parm.setup.si1) {
|
||||
case 1: /* audio */
|
||||
/* BC = 9090A3: 3.1 kHz audio, A-law */
|
||||
commands[AT_BC] = kstrdup("^SBC=9090A3\r", GFP_ATOMIC);
|
||||
if (!commands[AT_BC])
|
||||
goto oom;
|
||||
break;
|
||||
case 7: /* data */
|
||||
default: /* hope the app knows what it is doing */
|
||||
/* BC = 8890: unrestricted digital information */
|
||||
commands[AT_BC] = kstrdup("^SBC=8890\r", GFP_ATOMIC);
|
||||
if (!commands[AT_BC])
|
||||
goto oom;
|
||||
}
|
||||
/* ToDo: other si1 values, inspect si2, set HLC/LLC */
|
||||
|
||||
commands[AT_PROTO] = kmalloc(9, GFP_ATOMIC);
|
||||
if (!commands[AT_PROTO])
|
||||
goto oom;
|
||||
snprintf(commands[AT_PROTO], 9, "^SBPR=%u\r", bcs->proto2);
|
||||
|
||||
commands[AT_ISO] = kmalloc(9, GFP_ATOMIC);
|
||||
if (!commands[AT_ISO])
|
||||
goto oom;
|
||||
snprintf(commands[AT_ISO], 9, "^SISO=%u\r",
|
||||
(unsigned) bcs->channel + 1);
|
||||
|
||||
if (!gigaset_add_event(cs, &bcs->at_state, EV_DIAL, commands,
|
||||
bcs->at_state.seq_index, NULL)) {
|
||||
for (i = 0; i < AT_NUM; ++i)
|
||||
kfree(commands[i]);
|
||||
kfree(commands);
|
||||
gigaset_free_channel(bcs);
|
||||
return -ENOMEM;
|
||||
}
|
||||
gigaset_schedule_event(cs);
|
||||
break;
|
||||
case ISDN_CMD_ACCEPTD:
|
||||
gig_dbg(DEBUG_CMD, "ISDN_CMD_ACCEPTD");
|
||||
if (ch >= cs->channels) {
|
||||
dev_err(cs->dev,
|
||||
"ISDN_CMD_ACCEPTD: invalid channel (%d)\n", ch);
|
||||
return -EINVAL;
|
||||
}
|
||||
bcs = cs->bcs + ch;
|
||||
switch (bcs->proto2) {
|
||||
case L2_HDLC:
|
||||
bcs->rx_bufsize = SBUFSIZE;
|
||||
break;
|
||||
default: /* assume transparent */
|
||||
bcs->rx_bufsize = TRANSBUFSIZE;
|
||||
}
|
||||
dev_kfree_skb(bcs->rx_skb);
|
||||
gigaset_new_rx_skb(bcs);
|
||||
if (!gigaset_add_event(cs, &bcs->at_state,
|
||||
EV_ACCEPT, NULL, 0, NULL))
|
||||
return -ENOMEM;
|
||||
gigaset_schedule_event(cs);
|
||||
|
||||
break;
|
||||
case ISDN_CMD_HANGUP:
|
||||
gig_dbg(DEBUG_CMD, "ISDN_CMD_HANGUP");
|
||||
if (ch >= cs->channels) {
|
||||
dev_err(cs->dev,
|
||||
"ISDN_CMD_HANGUP: invalid channel (%d)\n", ch);
|
||||
return -EINVAL;
|
||||
}
|
||||
bcs = cs->bcs + ch;
|
||||
if (!gigaset_add_event(cs, &bcs->at_state,
|
||||
EV_HUP, NULL, 0, NULL))
|
||||
return -ENOMEM;
|
||||
gigaset_schedule_event(cs);
|
||||
|
||||
break;
|
||||
case ISDN_CMD_CLREAZ: /* Do not signal incoming signals */
|
||||
dev_info(cs->dev, "ignoring ISDN_CMD_CLREAZ\n");
|
||||
break;
|
||||
case ISDN_CMD_SETEAZ: /* Signal incoming calls for given MSN */
|
||||
dev_info(cs->dev, "ignoring ISDN_CMD_SETEAZ (%s)\n",
|
||||
cntrl->parm.num);
|
||||
break;
|
||||
case ISDN_CMD_SETL2: /* Set L2 to given protocol */
|
||||
if (ch >= cs->channels) {
|
||||
dev_err(cs->dev,
|
||||
"ISDN_CMD_SETL2: invalid channel (%d)\n", ch);
|
||||
return -EINVAL;
|
||||
}
|
||||
bcs = cs->bcs + ch;
|
||||
if (bcs->chstate & CHS_D_UP) {
|
||||
dev_err(cs->dev,
|
||||
"ISDN_CMD_SETL2: channel active (%d)\n", ch);
|
||||
return -EINVAL;
|
||||
}
|
||||
switch (cntrl->arg >> 8) {
|
||||
case ISDN_PROTO_L2_HDLC:
|
||||
gig_dbg(DEBUG_CMD, "ISDN_CMD_SETL2: setting L2_HDLC");
|
||||
bcs->proto2 = L2_HDLC;
|
||||
break;
|
||||
case ISDN_PROTO_L2_TRANS:
|
||||
gig_dbg(DEBUG_CMD, "ISDN_CMD_SETL2: setting L2_VOICE");
|
||||
bcs->proto2 = L2_VOICE;
|
||||
break;
|
||||
default:
|
||||
dev_err(cs->dev,
|
||||
"ISDN_CMD_SETL2: unsupported protocol (%lu)\n",
|
||||
cntrl->arg >> 8);
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
case ISDN_CMD_SETL3: /* Set L3 to given protocol */
|
||||
gig_dbg(DEBUG_CMD, "ISDN_CMD_SETL3");
|
||||
if (ch >= cs->channels) {
|
||||
dev_err(cs->dev,
|
||||
"ISDN_CMD_SETL3: invalid channel (%d)\n", ch);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (cntrl->arg >> 8 != ISDN_PROTO_L3_TRANS) {
|
||||
dev_err(cs->dev,
|
||||
"ISDN_CMD_SETL3: unsupported protocol (%lu)\n",
|
||||
cntrl->arg >> 8);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
gig_dbg(DEBUG_CMD, "unknown command %d from LL",
|
||||
cntrl->command);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return retval;
|
||||
|
||||
oom:
|
||||
dev_err(bcs->cs->dev, "out of memory\n");
|
||||
for (i = 0; i < AT_NUM; ++i)
|
||||
kfree(commands[i]);
|
||||
kfree(commands);
|
||||
gigaset_free_channel(bcs);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static void gigaset_i4l_cmd(struct cardstate *cs, int cmd)
|
||||
{
|
||||
isdn_if *iif = cs->iif;
|
||||
isdn_ctrl command;
|
||||
|
||||
command.driver = cs->myid;
|
||||
command.command = cmd;
|
||||
command.arg = 0;
|
||||
iif->statcallb(&command);
|
||||
}
|
||||
|
||||
static void gigaset_i4l_channel_cmd(struct bc_state *bcs, int cmd)
|
||||
{
|
||||
isdn_if *iif = bcs->cs->iif;
|
||||
isdn_ctrl command;
|
||||
|
||||
command.driver = bcs->cs->myid;
|
||||
command.command = cmd;
|
||||
command.arg = bcs->channel;
|
||||
iif->statcallb(&command);
|
||||
}
|
||||
|
||||
/**
|
||||
* gigaset_isdn_icall() - signal incoming call
|
||||
* @at_state: connection state structure.
|
||||
*
|
||||
* Called by main module to notify the LL that an incoming call has been
|
||||
* received. @at_state contains the parameters of the call.
|
||||
*
|
||||
* Return value: call disposition (ICALL_*)
|
||||
*/
|
||||
int gigaset_isdn_icall(struct at_state_t *at_state)
|
||||
{
|
||||
struct cardstate *cs = at_state->cs;
|
||||
struct bc_state *bcs = at_state->bcs;
|
||||
isdn_if *iif = cs->iif;
|
||||
isdn_ctrl response;
|
||||
int retval;
|
||||
|
||||
/* fill ICALL structure */
|
||||
response.parm.setup.si1 = 0; /* default: unknown */
|
||||
response.parm.setup.si2 = 0;
|
||||
response.parm.setup.screen = 0;
|
||||
response.parm.setup.plan = 0;
|
||||
if (!at_state->str_var[STR_ZBC]) {
|
||||
/* no BC (internal call): assume speech, A-law */
|
||||
response.parm.setup.si1 = 1;
|
||||
} else if (!strcmp(at_state->str_var[STR_ZBC], "8890")) {
|
||||
/* unrestricted digital information */
|
||||
response.parm.setup.si1 = 7;
|
||||
} else if (!strcmp(at_state->str_var[STR_ZBC], "8090A3")) {
|
||||
/* speech, A-law */
|
||||
response.parm.setup.si1 = 1;
|
||||
} else if (!strcmp(at_state->str_var[STR_ZBC], "9090A3")) {
|
||||
/* 3,1 kHz audio, A-law */
|
||||
response.parm.setup.si1 = 1;
|
||||
response.parm.setup.si2 = 2;
|
||||
} else {
|
||||
dev_warn(cs->dev, "RING ignored - unsupported BC %s\n",
|
||||
at_state->str_var[STR_ZBC]);
|
||||
return ICALL_IGNORE;
|
||||
}
|
||||
if (at_state->str_var[STR_NMBR]) {
|
||||
strlcpy(response.parm.setup.phone, at_state->str_var[STR_NMBR],
|
||||
sizeof response.parm.setup.phone);
|
||||
} else
|
||||
response.parm.setup.phone[0] = 0;
|
||||
if (at_state->str_var[STR_ZCPN]) {
|
||||
strlcpy(response.parm.setup.eazmsn, at_state->str_var[STR_ZCPN],
|
||||
sizeof response.parm.setup.eazmsn);
|
||||
} else
|
||||
response.parm.setup.eazmsn[0] = 0;
|
||||
|
||||
if (!bcs) {
|
||||
dev_notice(cs->dev, "no channel for incoming call\n");
|
||||
response.command = ISDN_STAT_ICALLW;
|
||||
response.arg = 0;
|
||||
} else {
|
||||
gig_dbg(DEBUG_CMD, "Sending ICALL");
|
||||
response.command = ISDN_STAT_ICALL;
|
||||
response.arg = bcs->channel;
|
||||
}
|
||||
response.driver = cs->myid;
|
||||
retval = iif->statcallb(&response);
|
||||
gig_dbg(DEBUG_CMD, "Response: %d", retval);
|
||||
switch (retval) {
|
||||
case 0: /* no takers */
|
||||
return ICALL_IGNORE;
|
||||
case 1: /* alerting */
|
||||
bcs->chstate |= CHS_NOTIFY_LL;
|
||||
return ICALL_ACCEPT;
|
||||
case 2: /* reject */
|
||||
return ICALL_REJECT;
|
||||
case 3: /* incomplete */
|
||||
dev_warn(cs->dev,
|
||||
"LL requested unsupported feature: Incomplete Number\n");
|
||||
return ICALL_IGNORE;
|
||||
case 4: /* proceeding */
|
||||
/* Gigaset will send ALERTING anyway.
|
||||
* There doesn't seem to be a way to avoid this.
|
||||
*/
|
||||
return ICALL_ACCEPT;
|
||||
case 5: /* deflect */
|
||||
dev_warn(cs->dev,
|
||||
"LL requested unsupported feature: Call Deflection\n");
|
||||
return ICALL_IGNORE;
|
||||
default:
|
||||
dev_err(cs->dev, "LL error %d on ICALL\n", retval);
|
||||
return ICALL_IGNORE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* gigaset_isdn_connD() - signal D channel connect
|
||||
* @bcs: B channel descriptor structure.
|
||||
*
|
||||
* Called by main module to notify the LL that the D channel connection has
|
||||
* been established.
|
||||
*/
|
||||
void gigaset_isdn_connD(struct bc_state *bcs)
|
||||
{
|
||||
gig_dbg(DEBUG_CMD, "sending DCONN");
|
||||
gigaset_i4l_channel_cmd(bcs, ISDN_STAT_DCONN);
|
||||
}
|
||||
|
||||
/**
|
||||
* gigaset_isdn_hupD() - signal D channel hangup
|
||||
* @bcs: B channel descriptor structure.
|
||||
*
|
||||
* Called by main module to notify the LL that the D channel connection has
|
||||
* been shut down.
|
||||
*/
|
||||
void gigaset_isdn_hupD(struct bc_state *bcs)
|
||||
{
|
||||
gig_dbg(DEBUG_CMD, "sending DHUP");
|
||||
gigaset_i4l_channel_cmd(bcs, ISDN_STAT_DHUP);
|
||||
}
|
||||
|
||||
/**
|
||||
* gigaset_isdn_connB() - signal B channel connect
|
||||
* @bcs: B channel descriptor structure.
|
||||
*
|
||||
* Called by main module to notify the LL that the B channel connection has
|
||||
* been established.
|
||||
*/
|
||||
void gigaset_isdn_connB(struct bc_state *bcs)
|
||||
{
|
||||
gig_dbg(DEBUG_CMD, "sending BCONN");
|
||||
gigaset_i4l_channel_cmd(bcs, ISDN_STAT_BCONN);
|
||||
}
|
||||
|
||||
/**
|
||||
* gigaset_isdn_hupB() - signal B channel hangup
|
||||
* @bcs: B channel descriptor structure.
|
||||
*
|
||||
* Called by main module to notify the LL that the B channel connection has
|
||||
* been shut down.
|
||||
*/
|
||||
void gigaset_isdn_hupB(struct bc_state *bcs)
|
||||
{
|
||||
gig_dbg(DEBUG_CMD, "sending BHUP");
|
||||
gigaset_i4l_channel_cmd(bcs, ISDN_STAT_BHUP);
|
||||
}
|
||||
|
||||
/**
|
||||
* gigaset_isdn_start() - signal device availability
|
||||
* @cs: device descriptor structure.
|
||||
*
|
||||
* Called by main module to notify the LL that the device is available for
|
||||
* use.
|
||||
*/
|
||||
void gigaset_isdn_start(struct cardstate *cs)
|
||||
{
|
||||
gig_dbg(DEBUG_CMD, "sending RUN");
|
||||
gigaset_i4l_cmd(cs, ISDN_STAT_RUN);
|
||||
}
|
||||
|
||||
/**
|
||||
* gigaset_isdn_stop() - signal device unavailability
|
||||
* @cs: device descriptor structure.
|
||||
*
|
||||
* Called by main module to notify the LL that the device is no longer
|
||||
* available for use.
|
||||
*/
|
||||
void gigaset_isdn_stop(struct cardstate *cs)
|
||||
{
|
||||
gig_dbg(DEBUG_CMD, "sending STOP");
|
||||
gigaset_i4l_cmd(cs, ISDN_STAT_STOP);
|
||||
}
|
||||
|
||||
/**
|
||||
* gigaset_isdn_regdev() - register to LL
|
||||
* @cs: device descriptor structure.
|
||||
* @isdnid: device name.
|
||||
*
|
||||
* Return value: 0 on success, error code < 0 on failure
|
||||
*/
|
||||
int gigaset_isdn_regdev(struct cardstate *cs, const char *isdnid)
|
||||
{
|
||||
isdn_if *iif;
|
||||
|
||||
iif = kmalloc(sizeof *iif, GFP_KERNEL);
|
||||
if (!iif) {
|
||||
pr_err("out of memory\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (snprintf(iif->id, sizeof iif->id, "%s_%u", isdnid, cs->minor_index)
|
||||
>= sizeof iif->id) {
|
||||
pr_err("ID too long: %s\n", isdnid);
|
||||
kfree(iif);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
iif->owner = THIS_MODULE;
|
||||
iif->channels = cs->channels;
|
||||
iif->maxbufsize = MAX_BUF_SIZE;
|
||||
iif->features = ISDN_FEATURE_L2_TRANS |
|
||||
ISDN_FEATURE_L2_HDLC |
|
||||
ISDN_FEATURE_L2_X75I |
|
||||
ISDN_FEATURE_L3_TRANS |
|
||||
ISDN_FEATURE_P_EURO;
|
||||
iif->hl_hdrlen = HW_HDR_LEN; /* Area for storing ack */
|
||||
iif->command = command_from_LL;
|
||||
iif->writebuf_skb = writebuf_from_LL;
|
||||
iif->writecmd = NULL; /* Don't support isdnctrl */
|
||||
iif->readstat = NULL; /* Don't support isdnctrl */
|
||||
iif->rcvcallb_skb = NULL; /* Will be set by LL */
|
||||
iif->statcallb = NULL; /* Will be set by LL */
|
||||
|
||||
if (!register_isdn(iif)) {
|
||||
pr_err("register_isdn failed\n");
|
||||
kfree(iif);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
cs->iif = iif;
|
||||
cs->myid = iif->channels; /* Set my device id */
|
||||
cs->hw_hdr_len = HW_HDR_LEN;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* gigaset_isdn_unregdev() - unregister device from LL
|
||||
* @cs: device descriptor structure.
|
||||
*/
|
||||
void gigaset_isdn_unregdev(struct cardstate *cs)
|
||||
{
|
||||
gig_dbg(DEBUG_CMD, "sending UNLOAD");
|
||||
gigaset_i4l_cmd(cs, ISDN_STAT_UNLOAD);
|
||||
kfree(cs->iif);
|
||||
cs->iif = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* gigaset_isdn_regdrv() - register driver to LL
|
||||
*/
|
||||
void gigaset_isdn_regdrv(void)
|
||||
{
|
||||
pr_info("ISDN4Linux interface\n");
|
||||
/* nothing to do */
|
||||
}
|
||||
|
||||
/**
|
||||
* gigaset_isdn_unregdrv() - unregister driver from LL
|
||||
*/
|
||||
void gigaset_isdn_unregdrv(void)
|
||||
{
|
||||
/* nothing to do */
|
||||
}
|
605
drivers/isdn/gigaset/interface.c
Normal file
605
drivers/isdn/gigaset/interface.c
Normal file
|
@ -0,0 +1,605 @@
|
|||
/*
|
||||
* interface to user space for the gigaset driver
|
||||
*
|
||||
* Copyright (c) 2004 by Hansjoerg Lipp <hjlipp@web.de>
|
||||
*
|
||||
* =====================================================================
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
* =====================================================================
|
||||
*/
|
||||
|
||||
#include "gigaset.h"
|
||||
#include <linux/gigaset_dev.h>
|
||||
#include <linux/tty_flip.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
/*** our ioctls ***/
|
||||
|
||||
static int if_lock(struct cardstate *cs, int *arg)
|
||||
{
|
||||
int cmd = *arg;
|
||||
|
||||
gig_dbg(DEBUG_IF, "%u: if_lock (%d)", cs->minor_index, cmd);
|
||||
|
||||
if (cmd > 1)
|
||||
return -EINVAL;
|
||||
|
||||
if (cmd < 0) {
|
||||
*arg = cs->mstate == MS_LOCKED;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!cmd && cs->mstate == MS_LOCKED && cs->connected) {
|
||||
cs->ops->set_modem_ctrl(cs, 0, TIOCM_DTR | TIOCM_RTS);
|
||||
cs->ops->baud_rate(cs, B115200);
|
||||
cs->ops->set_line_ctrl(cs, CS8);
|
||||
cs->control_state = TIOCM_DTR | TIOCM_RTS;
|
||||
}
|
||||
|
||||
cs->waiting = 1;
|
||||
if (!gigaset_add_event(cs, &cs->at_state, EV_IF_LOCK,
|
||||
NULL, cmd, NULL)) {
|
||||
cs->waiting = 0;
|
||||
return -ENOMEM;
|
||||
}
|
||||
gigaset_schedule_event(cs);
|
||||
|
||||
wait_event(cs->waitqueue, !cs->waiting);
|
||||
|
||||
if (cs->cmd_result >= 0) {
|
||||
*arg = cs->cmd_result;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return cs->cmd_result;
|
||||
}
|
||||
|
||||
static int if_version(struct cardstate *cs, unsigned arg[4])
|
||||
{
|
||||
static const unsigned version[4] = GIG_VERSION;
|
||||
static const unsigned compat[4] = GIG_COMPAT;
|
||||
unsigned cmd = arg[0];
|
||||
|
||||
gig_dbg(DEBUG_IF, "%u: if_version (%d)", cs->minor_index, cmd);
|
||||
|
||||
switch (cmd) {
|
||||
case GIGVER_DRIVER:
|
||||
memcpy(arg, version, sizeof version);
|
||||
return 0;
|
||||
case GIGVER_COMPAT:
|
||||
memcpy(arg, compat, sizeof compat);
|
||||
return 0;
|
||||
case GIGVER_FWBASE:
|
||||
cs->waiting = 1;
|
||||
if (!gigaset_add_event(cs, &cs->at_state, EV_IF_VER,
|
||||
NULL, 0, arg)) {
|
||||
cs->waiting = 0;
|
||||
return -ENOMEM;
|
||||
}
|
||||
gigaset_schedule_event(cs);
|
||||
|
||||
wait_event(cs->waitqueue, !cs->waiting);
|
||||
|
||||
if (cs->cmd_result >= 0)
|
||||
return 0;
|
||||
|
||||
return cs->cmd_result;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int if_config(struct cardstate *cs, int *arg)
|
||||
{
|
||||
gig_dbg(DEBUG_IF, "%u: if_config (%d)", cs->minor_index, *arg);
|
||||
|
||||
if (*arg != 1)
|
||||
return -EINVAL;
|
||||
|
||||
if (cs->mstate != MS_LOCKED)
|
||||
return -EBUSY;
|
||||
|
||||
if (!cs->connected) {
|
||||
pr_err("%s: not connected\n", __func__);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
*arg = 0;
|
||||
return gigaset_enterconfigmode(cs);
|
||||
}
|
||||
|
||||
/*** the terminal driver ***/
|
||||
|
||||
static int if_open(struct tty_struct *tty, struct file *filp)
|
||||
{
|
||||
struct cardstate *cs;
|
||||
|
||||
gig_dbg(DEBUG_IF, "%d+%d: %s()",
|
||||
tty->driver->minor_start, tty->index, __func__);
|
||||
|
||||
cs = gigaset_get_cs_by_tty(tty);
|
||||
if (!cs || !try_module_get(cs->driver->owner))
|
||||
return -ENODEV;
|
||||
|
||||
if (mutex_lock_interruptible(&cs->mutex)) {
|
||||
module_put(cs->driver->owner);
|
||||
return -ERESTARTSYS;
|
||||
}
|
||||
tty->driver_data = cs;
|
||||
|
||||
++cs->port.count;
|
||||
|
||||
if (cs->port.count == 1) {
|
||||
tty_port_tty_set(&cs->port, tty);
|
||||
cs->port.low_latency = 1;
|
||||
}
|
||||
|
||||
mutex_unlock(&cs->mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void if_close(struct tty_struct *tty, struct file *filp)
|
||||
{
|
||||
struct cardstate *cs = tty->driver_data;
|
||||
|
||||
if (!cs) { /* happens if we didn't find cs in open */
|
||||
gig_dbg(DEBUG_IF, "%s: no cardstate", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
|
||||
|
||||
mutex_lock(&cs->mutex);
|
||||
|
||||
if (!cs->connected)
|
||||
gig_dbg(DEBUG_IF, "not connected"); /* nothing to do */
|
||||
else if (!cs->port.count)
|
||||
dev_warn(cs->dev, "%s: device not opened\n", __func__);
|
||||
else if (!--cs->port.count)
|
||||
tty_port_tty_set(&cs->port, NULL);
|
||||
|
||||
mutex_unlock(&cs->mutex);
|
||||
|
||||
module_put(cs->driver->owner);
|
||||
}
|
||||
|
||||
static int if_ioctl(struct tty_struct *tty,
|
||||
unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct cardstate *cs = tty->driver_data;
|
||||
int retval = -ENODEV;
|
||||
int int_arg;
|
||||
unsigned char buf[6];
|
||||
unsigned version[4];
|
||||
|
||||
gig_dbg(DEBUG_IF, "%u: %s(0x%x)", cs->minor_index, __func__, cmd);
|
||||
|
||||
if (mutex_lock_interruptible(&cs->mutex))
|
||||
return -ERESTARTSYS;
|
||||
|
||||
if (!cs->connected) {
|
||||
gig_dbg(DEBUG_IF, "not connected");
|
||||
retval = -ENODEV;
|
||||
} else {
|
||||
retval = 0;
|
||||
switch (cmd) {
|
||||
case GIGASET_REDIR:
|
||||
retval = get_user(int_arg, (int __user *) arg);
|
||||
if (retval >= 0)
|
||||
retval = if_lock(cs, &int_arg);
|
||||
if (retval >= 0)
|
||||
retval = put_user(int_arg, (int __user *) arg);
|
||||
break;
|
||||
case GIGASET_CONFIG:
|
||||
retval = get_user(int_arg, (int __user *) arg);
|
||||
if (retval >= 0)
|
||||
retval = if_config(cs, &int_arg);
|
||||
if (retval >= 0)
|
||||
retval = put_user(int_arg, (int __user *) arg);
|
||||
break;
|
||||
case GIGASET_BRKCHARS:
|
||||
retval = copy_from_user(&buf,
|
||||
(const unsigned char __user *) arg, 6)
|
||||
? -EFAULT : 0;
|
||||
if (retval >= 0) {
|
||||
gigaset_dbg_buffer(DEBUG_IF, "GIGASET_BRKCHARS",
|
||||
6, (const unsigned char *) arg);
|
||||
retval = cs->ops->brkchars(cs, buf);
|
||||
}
|
||||
break;
|
||||
case GIGASET_VERSION:
|
||||
retval = copy_from_user(version,
|
||||
(unsigned __user *) arg, sizeof version)
|
||||
? -EFAULT : 0;
|
||||
if (retval >= 0)
|
||||
retval = if_version(cs, version);
|
||||
if (retval >= 0)
|
||||
retval = copy_to_user((unsigned __user *) arg,
|
||||
version, sizeof version)
|
||||
? -EFAULT : 0;
|
||||
break;
|
||||
default:
|
||||
gig_dbg(DEBUG_IF, "%s: arg not supported - 0x%04x",
|
||||
__func__, cmd);
|
||||
retval = -ENOIOCTLCMD;
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&cs->mutex);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int if_tiocmget(struct tty_struct *tty)
|
||||
{
|
||||
struct cardstate *cs = tty->driver_data;
|
||||
int retval;
|
||||
|
||||
gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
|
||||
|
||||
if (mutex_lock_interruptible(&cs->mutex))
|
||||
return -ERESTARTSYS;
|
||||
|
||||
retval = cs->control_state & (TIOCM_RTS | TIOCM_DTR);
|
||||
|
||||
mutex_unlock(&cs->mutex);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int if_tiocmset(struct tty_struct *tty,
|
||||
unsigned int set, unsigned int clear)
|
||||
{
|
||||
struct cardstate *cs = tty->driver_data;
|
||||
int retval;
|
||||
unsigned mc;
|
||||
|
||||
gig_dbg(DEBUG_IF, "%u: %s(0x%x, 0x%x)",
|
||||
cs->minor_index, __func__, set, clear);
|
||||
|
||||
if (mutex_lock_interruptible(&cs->mutex))
|
||||
return -ERESTARTSYS;
|
||||
|
||||
if (!cs->connected) {
|
||||
gig_dbg(DEBUG_IF, "not connected");
|
||||
retval = -ENODEV;
|
||||
} else {
|
||||
mc = (cs->control_state | set) & ~clear & (TIOCM_RTS | TIOCM_DTR);
|
||||
retval = cs->ops->set_modem_ctrl(cs, cs->control_state, mc);
|
||||
cs->control_state = mc;
|
||||
}
|
||||
|
||||
mutex_unlock(&cs->mutex);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int if_write(struct tty_struct *tty, const unsigned char *buf, int count)
|
||||
{
|
||||
struct cardstate *cs = tty->driver_data;
|
||||
struct cmdbuf_t *cb;
|
||||
int retval;
|
||||
|
||||
gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
|
||||
|
||||
if (mutex_lock_interruptible(&cs->mutex))
|
||||
return -ERESTARTSYS;
|
||||
|
||||
if (!cs->connected) {
|
||||
gig_dbg(DEBUG_IF, "not connected");
|
||||
retval = -ENODEV;
|
||||
goto done;
|
||||
}
|
||||
if (cs->mstate != MS_LOCKED) {
|
||||
dev_warn(cs->dev, "can't write to unlocked device\n");
|
||||
retval = -EBUSY;
|
||||
goto done;
|
||||
}
|
||||
if (count <= 0) {
|
||||
/* nothing to do */
|
||||
retval = 0;
|
||||
goto done;
|
||||
}
|
||||
|
||||
cb = kmalloc(sizeof(struct cmdbuf_t) + count, GFP_KERNEL);
|
||||
if (!cb) {
|
||||
dev_err(cs->dev, "%s: out of memory\n", __func__);
|
||||
retval = -ENOMEM;
|
||||
goto done;
|
||||
}
|
||||
|
||||
memcpy(cb->buf, buf, count);
|
||||
cb->len = count;
|
||||
cb->offset = 0;
|
||||
cb->next = NULL;
|
||||
cb->wake_tasklet = &cs->if_wake_tasklet;
|
||||
retval = cs->ops->write_cmd(cs, cb);
|
||||
done:
|
||||
mutex_unlock(&cs->mutex);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int if_write_room(struct tty_struct *tty)
|
||||
{
|
||||
struct cardstate *cs = tty->driver_data;
|
||||
int retval;
|
||||
|
||||
gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
|
||||
|
||||
if (mutex_lock_interruptible(&cs->mutex))
|
||||
return -ERESTARTSYS;
|
||||
|
||||
if (!cs->connected) {
|
||||
gig_dbg(DEBUG_IF, "not connected");
|
||||
retval = -ENODEV;
|
||||
} else if (cs->mstate != MS_LOCKED) {
|
||||
dev_warn(cs->dev, "can't write to unlocked device\n");
|
||||
retval = -EBUSY;
|
||||
} else
|
||||
retval = cs->ops->write_room(cs);
|
||||
|
||||
mutex_unlock(&cs->mutex);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int if_chars_in_buffer(struct tty_struct *tty)
|
||||
{
|
||||
struct cardstate *cs = tty->driver_data;
|
||||
int retval = 0;
|
||||
|
||||
gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
|
||||
|
||||
mutex_lock(&cs->mutex);
|
||||
|
||||
if (!cs->connected)
|
||||
gig_dbg(DEBUG_IF, "not connected");
|
||||
else if (cs->mstate != MS_LOCKED)
|
||||
dev_warn(cs->dev, "can't write to unlocked device\n");
|
||||
else
|
||||
retval = cs->ops->chars_in_buffer(cs);
|
||||
|
||||
mutex_unlock(&cs->mutex);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void if_throttle(struct tty_struct *tty)
|
||||
{
|
||||
struct cardstate *cs = tty->driver_data;
|
||||
|
||||
gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
|
||||
|
||||
mutex_lock(&cs->mutex);
|
||||
|
||||
if (!cs->connected)
|
||||
gig_dbg(DEBUG_IF, "not connected"); /* nothing to do */
|
||||
else
|
||||
gig_dbg(DEBUG_IF, "%s: not implemented\n", __func__);
|
||||
|
||||
mutex_unlock(&cs->mutex);
|
||||
}
|
||||
|
||||
static void if_unthrottle(struct tty_struct *tty)
|
||||
{
|
||||
struct cardstate *cs = tty->driver_data;
|
||||
|
||||
gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
|
||||
|
||||
mutex_lock(&cs->mutex);
|
||||
|
||||
if (!cs->connected)
|
||||
gig_dbg(DEBUG_IF, "not connected"); /* nothing to do */
|
||||
else
|
||||
gig_dbg(DEBUG_IF, "%s: not implemented\n", __func__);
|
||||
|
||||
mutex_unlock(&cs->mutex);
|
||||
}
|
||||
|
||||
static void if_set_termios(struct tty_struct *tty, struct ktermios *old)
|
||||
{
|
||||
struct cardstate *cs = tty->driver_data;
|
||||
unsigned int iflag;
|
||||
unsigned int cflag;
|
||||
unsigned int old_cflag;
|
||||
unsigned int control_state, new_state;
|
||||
|
||||
gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
|
||||
|
||||
mutex_lock(&cs->mutex);
|
||||
|
||||
if (!cs->connected) {
|
||||
gig_dbg(DEBUG_IF, "not connected");
|
||||
goto out;
|
||||
}
|
||||
|
||||
iflag = tty->termios.c_iflag;
|
||||
cflag = tty->termios.c_cflag;
|
||||
old_cflag = old ? old->c_cflag : cflag;
|
||||
gig_dbg(DEBUG_IF, "%u: iflag %x cflag %x old %x",
|
||||
cs->minor_index, iflag, cflag, old_cflag);
|
||||
|
||||
/* get a local copy of the current port settings */
|
||||
control_state = cs->control_state;
|
||||
|
||||
/*
|
||||
* Update baud rate.
|
||||
* Do not attempt to cache old rates and skip settings,
|
||||
* disconnects screw such tricks up completely.
|
||||
* Premature optimization is the root of all evil.
|
||||
*/
|
||||
|
||||
/* reassert DTR and (maybe) RTS on transition from B0 */
|
||||
if ((old_cflag & CBAUD) == B0) {
|
||||
new_state = control_state | TIOCM_DTR;
|
||||
/* don't set RTS if using hardware flow control */
|
||||
if (!(old_cflag & CRTSCTS))
|
||||
new_state |= TIOCM_RTS;
|
||||
gig_dbg(DEBUG_IF, "%u: from B0 - set DTR%s",
|
||||
cs->minor_index,
|
||||
(new_state & TIOCM_RTS) ? " only" : "/RTS");
|
||||
cs->ops->set_modem_ctrl(cs, control_state, new_state);
|
||||
control_state = new_state;
|
||||
}
|
||||
|
||||
cs->ops->baud_rate(cs, cflag & CBAUD);
|
||||
|
||||
if ((cflag & CBAUD) == B0) {
|
||||
/* Drop RTS and DTR */
|
||||
gig_dbg(DEBUG_IF, "%u: to B0 - drop DTR/RTS", cs->minor_index);
|
||||
new_state = control_state & ~(TIOCM_DTR | TIOCM_RTS);
|
||||
cs->ops->set_modem_ctrl(cs, control_state, new_state);
|
||||
control_state = new_state;
|
||||
}
|
||||
|
||||
/*
|
||||
* Update line control register (LCR)
|
||||
*/
|
||||
|
||||
cs->ops->set_line_ctrl(cs, cflag);
|
||||
|
||||
/* save off the modified port settings */
|
||||
cs->control_state = control_state;
|
||||
|
||||
out:
|
||||
mutex_unlock(&cs->mutex);
|
||||
}
|
||||
|
||||
static const struct tty_operations if_ops = {
|
||||
.open = if_open,
|
||||
.close = if_close,
|
||||
.ioctl = if_ioctl,
|
||||
.write = if_write,
|
||||
.write_room = if_write_room,
|
||||
.chars_in_buffer = if_chars_in_buffer,
|
||||
.set_termios = if_set_termios,
|
||||
.throttle = if_throttle,
|
||||
.unthrottle = if_unthrottle,
|
||||
.tiocmget = if_tiocmget,
|
||||
.tiocmset = if_tiocmset,
|
||||
};
|
||||
|
||||
|
||||
/* wakeup tasklet for the write operation */
|
||||
static void if_wake(unsigned long data)
|
||||
{
|
||||
struct cardstate *cs = (struct cardstate *)data;
|
||||
|
||||
tty_port_tty_wakeup(&cs->port);
|
||||
}
|
||||
|
||||
/*** interface to common ***/
|
||||
|
||||
void gigaset_if_init(struct cardstate *cs)
|
||||
{
|
||||
struct gigaset_driver *drv;
|
||||
|
||||
drv = cs->driver;
|
||||
if (!drv->have_tty)
|
||||
return;
|
||||
|
||||
tasklet_init(&cs->if_wake_tasklet, if_wake, (unsigned long) cs);
|
||||
|
||||
mutex_lock(&cs->mutex);
|
||||
cs->tty_dev = tty_port_register_device(&cs->port, drv->tty,
|
||||
cs->minor_index, NULL);
|
||||
|
||||
if (!IS_ERR(cs->tty_dev))
|
||||
dev_set_drvdata(cs->tty_dev, cs);
|
||||
else {
|
||||
pr_warning("could not register device to the tty subsystem\n");
|
||||
cs->tty_dev = NULL;
|
||||
}
|
||||
mutex_unlock(&cs->mutex);
|
||||
}
|
||||
|
||||
void gigaset_if_free(struct cardstate *cs)
|
||||
{
|
||||
struct gigaset_driver *drv;
|
||||
|
||||
drv = cs->driver;
|
||||
if (!drv->have_tty)
|
||||
return;
|
||||
|
||||
tasklet_disable(&cs->if_wake_tasklet);
|
||||
tasklet_kill(&cs->if_wake_tasklet);
|
||||
cs->tty_dev = NULL;
|
||||
tty_unregister_device(drv->tty, cs->minor_index);
|
||||
}
|
||||
|
||||
/**
|
||||
* gigaset_if_receive() - pass a received block of data to the tty device
|
||||
* @cs: device descriptor structure.
|
||||
* @buffer: received data.
|
||||
* @len: number of bytes received.
|
||||
*
|
||||
* Called by asyncdata/isocdata if a block of data received from the
|
||||
* device must be sent to userspace through the ttyG* device.
|
||||
*/
|
||||
void gigaset_if_receive(struct cardstate *cs,
|
||||
unsigned char *buffer, size_t len)
|
||||
{
|
||||
tty_insert_flip_string(&cs->port, buffer, len);
|
||||
tty_flip_buffer_push(&cs->port);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(gigaset_if_receive);
|
||||
|
||||
/* gigaset_if_initdriver
|
||||
* Initialize tty interface.
|
||||
* parameters:
|
||||
* drv Driver
|
||||
* procname Name of the driver (e.g. for /proc/tty/drivers)
|
||||
* devname Name of the device files (prefix without minor number)
|
||||
*/
|
||||
void gigaset_if_initdriver(struct gigaset_driver *drv, const char *procname,
|
||||
const char *devname)
|
||||
{
|
||||
int ret;
|
||||
struct tty_driver *tty;
|
||||
|
||||
drv->have_tty = 0;
|
||||
|
||||
drv->tty = tty = alloc_tty_driver(drv->minors);
|
||||
if (tty == NULL)
|
||||
goto enomem;
|
||||
|
||||
tty->type = TTY_DRIVER_TYPE_SERIAL;
|
||||
tty->subtype = SERIAL_TYPE_NORMAL;
|
||||
tty->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
|
||||
|
||||
tty->driver_name = procname;
|
||||
tty->name = devname;
|
||||
tty->minor_start = drv->minor;
|
||||
|
||||
tty->init_termios = tty_std_termios;
|
||||
tty->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
|
||||
tty_set_operations(tty, &if_ops);
|
||||
|
||||
ret = tty_register_driver(tty);
|
||||
if (ret < 0) {
|
||||
pr_err("error %d registering tty driver\n", ret);
|
||||
goto error;
|
||||
}
|
||||
gig_dbg(DEBUG_IF, "tty driver initialized");
|
||||
drv->have_tty = 1;
|
||||
return;
|
||||
|
||||
enomem:
|
||||
pr_err("out of memory\n");
|
||||
error:
|
||||
if (drv->tty)
|
||||
put_tty_driver(drv->tty);
|
||||
}
|
||||
|
||||
void gigaset_if_freedriver(struct gigaset_driver *drv)
|
||||
{
|
||||
if (!drv->have_tty)
|
||||
return;
|
||||
|
||||
drv->have_tty = 0;
|
||||
tty_unregister_driver(drv->tty);
|
||||
put_tty_driver(drv->tty);
|
||||
}
|
1009
drivers/isdn/gigaset/isocdata.c
Normal file
1009
drivers/isdn/gigaset/isocdata.c
Normal file
File diff suppressed because it is too large
Load diff
80
drivers/isdn/gigaset/proc.c
Normal file
80
drivers/isdn/gigaset/proc.c
Normal file
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
* Stuff used by all variants of the driver
|
||||
*
|
||||
* Copyright (c) 2001 by Stefan Eilers,
|
||||
* Hansjoerg Lipp <hjlipp@web.de>,
|
||||
* Tilman Schmidt <tilman@imap.cc>.
|
||||
*
|
||||
* =====================================================================
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
* =====================================================================
|
||||
*/
|
||||
|
||||
#include "gigaset.h"
|
||||
|
||||
static ssize_t show_cidmode(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct cardstate *cs = dev_get_drvdata(dev);
|
||||
|
||||
return sprintf(buf, "%u\n", cs->cidmode);
|
||||
}
|
||||
|
||||
static ssize_t set_cidmode(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct cardstate *cs = dev_get_drvdata(dev);
|
||||
long int value;
|
||||
char *end;
|
||||
|
||||
value = simple_strtol(buf, &end, 0);
|
||||
while (*end)
|
||||
if (!isspace(*end++))
|
||||
return -EINVAL;
|
||||
if (value < 0 || value > 1)
|
||||
return -EINVAL;
|
||||
|
||||
if (mutex_lock_interruptible(&cs->mutex))
|
||||
return -ERESTARTSYS;
|
||||
|
||||
cs->waiting = 1;
|
||||
if (!gigaset_add_event(cs, &cs->at_state, EV_PROC_CIDMODE,
|
||||
NULL, value, NULL)) {
|
||||
cs->waiting = 0;
|
||||
mutex_unlock(&cs->mutex);
|
||||
return -ENOMEM;
|
||||
}
|
||||
gigaset_schedule_event(cs);
|
||||
|
||||
wait_event(cs->waitqueue, !cs->waiting);
|
||||
|
||||
mutex_unlock(&cs->mutex);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(cidmode, S_IRUGO | S_IWUSR, show_cidmode, set_cidmode);
|
||||
|
||||
/* free sysfs for device */
|
||||
void gigaset_free_dev_sysfs(struct cardstate *cs)
|
||||
{
|
||||
if (!cs->tty_dev)
|
||||
return;
|
||||
|
||||
gig_dbg(DEBUG_INIT, "removing sysfs entries");
|
||||
device_remove_file(cs->tty_dev, &dev_attr_cidmode);
|
||||
}
|
||||
|
||||
/* initialize sysfs for device */
|
||||
void gigaset_init_dev_sysfs(struct cardstate *cs)
|
||||
{
|
||||
if (!cs->tty_dev)
|
||||
return;
|
||||
|
||||
gig_dbg(DEBUG_INIT, "setting up sysfs");
|
||||
if (device_create_file(cs->tty_dev, &dev_attr_cidmode))
|
||||
pr_err("could not create sysfs attribute\n");
|
||||
}
|
820
drivers/isdn/gigaset/ser-gigaset.c
Normal file
820
drivers/isdn/gigaset/ser-gigaset.c
Normal file
|
@ -0,0 +1,820 @@
|
|||
/* This is the serial hardware link layer (HLL) for the Gigaset 307x isdn
|
||||
* DECT base (aka Sinus 45 isdn) using the RS232 DECT data module M101,
|
||||
* written as a line discipline.
|
||||
*
|
||||
* =====================================================================
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
* =====================================================================
|
||||
*/
|
||||
|
||||
#include "gigaset.h"
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/completion.h>
|
||||
|
||||
/* Version Information */
|
||||
#define DRIVER_AUTHOR "Tilman Schmidt"
|
||||
#define DRIVER_DESC "Serial Driver for Gigaset 307x using Siemens M101"
|
||||
|
||||
#define GIGASET_MINORS 1
|
||||
#define GIGASET_MINOR 0
|
||||
#define GIGASET_MODULENAME "ser_gigaset"
|
||||
#define GIGASET_DEVNAME "ttyGS"
|
||||
|
||||
/* length limit according to Siemens 3070usb-protokoll.doc ch. 2.1 */
|
||||
#define IF_WRITEBUF 264
|
||||
|
||||
MODULE_AUTHOR(DRIVER_AUTHOR);
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS_LDISC(N_GIGASET_M101);
|
||||
|
||||
static int startmode = SM_ISDN;
|
||||
module_param(startmode, int, S_IRUGO);
|
||||
MODULE_PARM_DESC(startmode, "initial operation mode");
|
||||
static int cidmode = 1;
|
||||
module_param(cidmode, int, S_IRUGO);
|
||||
MODULE_PARM_DESC(cidmode, "stay in CID mode when idle");
|
||||
|
||||
static struct gigaset_driver *driver;
|
||||
|
||||
struct ser_cardstate {
|
||||
struct platform_device dev;
|
||||
struct tty_struct *tty;
|
||||
atomic_t refcnt;
|
||||
struct completion dead_cmp;
|
||||
};
|
||||
|
||||
static struct platform_driver device_driver = {
|
||||
.driver = {
|
||||
.name = GIGASET_MODULENAME,
|
||||
},
|
||||
};
|
||||
|
||||
static void flush_send_queue(struct cardstate *);
|
||||
|
||||
/* transmit data from current open skb
|
||||
* result: number of bytes sent or error code < 0
|
||||
*/
|
||||
static int write_modem(struct cardstate *cs)
|
||||
{
|
||||
struct tty_struct *tty = cs->hw.ser->tty;
|
||||
struct bc_state *bcs = &cs->bcs[0]; /* only one channel */
|
||||
struct sk_buff *skb = bcs->tx_skb;
|
||||
int sent = -EOPNOTSUPP;
|
||||
|
||||
if (!tty || !tty->driver || !skb)
|
||||
return -EINVAL;
|
||||
|
||||
if (!skb->len) {
|
||||
dev_kfree_skb_any(skb);
|
||||
bcs->tx_skb = NULL;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
|
||||
if (tty->ops->write)
|
||||
sent = tty->ops->write(tty, skb->data, skb->len);
|
||||
gig_dbg(DEBUG_OUTPUT, "write_modem: sent %d", sent);
|
||||
if (sent < 0) {
|
||||
/* error */
|
||||
flush_send_queue(cs);
|
||||
return sent;
|
||||
}
|
||||
skb_pull(skb, sent);
|
||||
if (!skb->len) {
|
||||
/* skb sent completely */
|
||||
gigaset_skb_sent(bcs, skb);
|
||||
|
||||
gig_dbg(DEBUG_INTR, "kfree skb (Adr: %lx)!",
|
||||
(unsigned long) skb);
|
||||
dev_kfree_skb_any(skb);
|
||||
bcs->tx_skb = NULL;
|
||||
}
|
||||
return sent;
|
||||
}
|
||||
|
||||
/*
|
||||
* transmit first queued command buffer
|
||||
* result: number of bytes sent or error code < 0
|
||||
*/
|
||||
static int send_cb(struct cardstate *cs)
|
||||
{
|
||||
struct tty_struct *tty = cs->hw.ser->tty;
|
||||
struct cmdbuf_t *cb, *tcb;
|
||||
unsigned long flags;
|
||||
int sent = 0;
|
||||
|
||||
if (!tty || !tty->driver)
|
||||
return -EFAULT;
|
||||
|
||||
cb = cs->cmdbuf;
|
||||
if (!cb)
|
||||
return 0; /* nothing to do */
|
||||
|
||||
if (cb->len) {
|
||||
set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
|
||||
sent = tty->ops->write(tty, cb->buf + cb->offset, cb->len);
|
||||
if (sent < 0) {
|
||||
/* error */
|
||||
gig_dbg(DEBUG_OUTPUT, "send_cb: write error %d", sent);
|
||||
flush_send_queue(cs);
|
||||
return sent;
|
||||
}
|
||||
cb->offset += sent;
|
||||
cb->len -= sent;
|
||||
gig_dbg(DEBUG_OUTPUT, "send_cb: sent %d, left %u, queued %u",
|
||||
sent, cb->len, cs->cmdbytes);
|
||||
}
|
||||
|
||||
while (cb && !cb->len) {
|
||||
spin_lock_irqsave(&cs->cmdlock, flags);
|
||||
cs->cmdbytes -= cs->curlen;
|
||||
tcb = cb;
|
||||
cs->cmdbuf = cb = cb->next;
|
||||
if (cb) {
|
||||
cb->prev = NULL;
|
||||
cs->curlen = cb->len;
|
||||
} else {
|
||||
cs->lastcmdbuf = NULL;
|
||||
cs->curlen = 0;
|
||||
}
|
||||
spin_unlock_irqrestore(&cs->cmdlock, flags);
|
||||
|
||||
if (tcb->wake_tasklet)
|
||||
tasklet_schedule(tcb->wake_tasklet);
|
||||
kfree(tcb);
|
||||
}
|
||||
return sent;
|
||||
}
|
||||
|
||||
/*
|
||||
* send queue tasklet
|
||||
* If there is already a skb opened, put data to the transfer buffer
|
||||
* by calling "write_modem".
|
||||
* Otherwise take a new skb out of the queue.
|
||||
*/
|
||||
static void gigaset_modem_fill(unsigned long data)
|
||||
{
|
||||
struct cardstate *cs = (struct cardstate *) data;
|
||||
struct bc_state *bcs;
|
||||
struct sk_buff *nextskb;
|
||||
int sent = 0;
|
||||
|
||||
if (!cs) {
|
||||
gig_dbg(DEBUG_OUTPUT, "%s: no cardstate", __func__);
|
||||
return;
|
||||
}
|
||||
bcs = cs->bcs;
|
||||
if (!bcs) {
|
||||
gig_dbg(DEBUG_OUTPUT, "%s: no cardstate", __func__);
|
||||
return;
|
||||
}
|
||||
if (!bcs->tx_skb) {
|
||||
/* no skb is being sent; send command if any */
|
||||
sent = send_cb(cs);
|
||||
gig_dbg(DEBUG_OUTPUT, "%s: send_cb -> %d", __func__, sent);
|
||||
if (sent)
|
||||
/* something sent or error */
|
||||
return;
|
||||
|
||||
/* no command to send; get skb */
|
||||
nextskb = skb_dequeue(&bcs->squeue);
|
||||
if (!nextskb)
|
||||
/* no skb either, nothing to do */
|
||||
return;
|
||||
bcs->tx_skb = nextskb;
|
||||
|
||||
gig_dbg(DEBUG_INTR, "Dequeued skb (Adr: %lx)",
|
||||
(unsigned long) bcs->tx_skb);
|
||||
}
|
||||
|
||||
/* send skb */
|
||||
gig_dbg(DEBUG_OUTPUT, "%s: tx_skb", __func__);
|
||||
if (write_modem(cs) < 0)
|
||||
gig_dbg(DEBUG_OUTPUT, "%s: write_modem failed", __func__);
|
||||
}
|
||||
|
||||
/*
|
||||
* throw away all data queued for sending
|
||||
*/
|
||||
static void flush_send_queue(struct cardstate *cs)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
struct cmdbuf_t *cb;
|
||||
unsigned long flags;
|
||||
|
||||
/* command queue */
|
||||
spin_lock_irqsave(&cs->cmdlock, flags);
|
||||
while ((cb = cs->cmdbuf) != NULL) {
|
||||
cs->cmdbuf = cb->next;
|
||||
if (cb->wake_tasklet)
|
||||
tasklet_schedule(cb->wake_tasklet);
|
||||
kfree(cb);
|
||||
}
|
||||
cs->cmdbuf = cs->lastcmdbuf = NULL;
|
||||
cs->cmdbytes = cs->curlen = 0;
|
||||
spin_unlock_irqrestore(&cs->cmdlock, flags);
|
||||
|
||||
/* data queue */
|
||||
if (cs->bcs->tx_skb)
|
||||
dev_kfree_skb_any(cs->bcs->tx_skb);
|
||||
while ((skb = skb_dequeue(&cs->bcs->squeue)) != NULL)
|
||||
dev_kfree_skb_any(skb);
|
||||
}
|
||||
|
||||
|
||||
/* Gigaset Driver Interface */
|
||||
/* ======================== */
|
||||
|
||||
/*
|
||||
* queue an AT command string for transmission to the Gigaset device
|
||||
* parameters:
|
||||
* cs controller state structure
|
||||
* buf buffer containing the string to send
|
||||
* len number of characters to send
|
||||
* wake_tasklet tasklet to run when transmission is complete, or NULL
|
||||
* return value:
|
||||
* number of bytes queued, or error code < 0
|
||||
*/
|
||||
static int gigaset_write_cmd(struct cardstate *cs, struct cmdbuf_t *cb)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
gigaset_dbg_buffer(cs->mstate != MS_LOCKED ?
|
||||
DEBUG_TRANSCMD : DEBUG_LOCKCMD,
|
||||
"CMD Transmit", cb->len, cb->buf);
|
||||
|
||||
spin_lock_irqsave(&cs->cmdlock, flags);
|
||||
cb->prev = cs->lastcmdbuf;
|
||||
if (cs->lastcmdbuf)
|
||||
cs->lastcmdbuf->next = cb;
|
||||
else {
|
||||
cs->cmdbuf = cb;
|
||||
cs->curlen = cb->len;
|
||||
}
|
||||
cs->cmdbytes += cb->len;
|
||||
cs->lastcmdbuf = cb;
|
||||
spin_unlock_irqrestore(&cs->cmdlock, flags);
|
||||
|
||||
spin_lock_irqsave(&cs->lock, flags);
|
||||
if (cs->connected)
|
||||
tasklet_schedule(&cs->write_tasklet);
|
||||
spin_unlock_irqrestore(&cs->lock, flags);
|
||||
return cb->len;
|
||||
}
|
||||
|
||||
/*
|
||||
* tty_driver.write_room interface routine
|
||||
* return number of characters the driver will accept to be written
|
||||
* parameter:
|
||||
* controller state structure
|
||||
* return value:
|
||||
* number of characters
|
||||
*/
|
||||
static int gigaset_write_room(struct cardstate *cs)
|
||||
{
|
||||
unsigned bytes;
|
||||
|
||||
bytes = cs->cmdbytes;
|
||||
return bytes < IF_WRITEBUF ? IF_WRITEBUF - bytes : 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* tty_driver.chars_in_buffer interface routine
|
||||
* return number of characters waiting to be sent
|
||||
* parameter:
|
||||
* controller state structure
|
||||
* return value:
|
||||
* number of characters
|
||||
*/
|
||||
static int gigaset_chars_in_buffer(struct cardstate *cs)
|
||||
{
|
||||
return cs->cmdbytes;
|
||||
}
|
||||
|
||||
/*
|
||||
* implementation of ioctl(GIGASET_BRKCHARS)
|
||||
* parameter:
|
||||
* controller state structure
|
||||
* return value:
|
||||
* -EINVAL (unimplemented function)
|
||||
*/
|
||||
static int gigaset_brkchars(struct cardstate *cs, const unsigned char buf[6])
|
||||
{
|
||||
/* not implemented */
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Open B channel
|
||||
* Called by "do_action" in ev-layer.c
|
||||
*/
|
||||
static int gigaset_init_bchannel(struct bc_state *bcs)
|
||||
{
|
||||
/* nothing to do for M10x */
|
||||
gigaset_bchannel_up(bcs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Close B channel
|
||||
* Called by "do_action" in ev-layer.c
|
||||
*/
|
||||
static int gigaset_close_bchannel(struct bc_state *bcs)
|
||||
{
|
||||
/* nothing to do for M10x */
|
||||
gigaset_bchannel_down(bcs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set up B channel structure
|
||||
* This is called by "gigaset_initcs" in common.c
|
||||
*/
|
||||
static int gigaset_initbcshw(struct bc_state *bcs)
|
||||
{
|
||||
/* unused */
|
||||
bcs->hw.ser = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Free B channel structure
|
||||
* Called by "gigaset_freebcs" in common.c
|
||||
*/
|
||||
static void gigaset_freebcshw(struct bc_state *bcs)
|
||||
{
|
||||
/* unused */
|
||||
}
|
||||
|
||||
/*
|
||||
* Reinitialize B channel structure
|
||||
* This is called by "bcs_reinit" in common.c
|
||||
*/
|
||||
static void gigaset_reinitbcshw(struct bc_state *bcs)
|
||||
{
|
||||
/* nothing to do for M10x */
|
||||
}
|
||||
|
||||
/*
|
||||
* Free hardware specific device data
|
||||
* This will be called by "gigaset_freecs" in common.c
|
||||
*/
|
||||
static void gigaset_freecshw(struct cardstate *cs)
|
||||
{
|
||||
tasklet_kill(&cs->write_tasklet);
|
||||
if (!cs->hw.ser)
|
||||
return;
|
||||
dev_set_drvdata(&cs->hw.ser->dev.dev, NULL);
|
||||
platform_device_unregister(&cs->hw.ser->dev);
|
||||
kfree(cs->hw.ser);
|
||||
cs->hw.ser = NULL;
|
||||
}
|
||||
|
||||
static void gigaset_device_release(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
|
||||
/* adapted from platform_device_release() in drivers/base/platform.c */
|
||||
kfree(dev->platform_data);
|
||||
kfree(pdev->resource);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set up hardware specific device data
|
||||
* This is called by "gigaset_initcs" in common.c
|
||||
*/
|
||||
static int gigaset_initcshw(struct cardstate *cs)
|
||||
{
|
||||
int rc;
|
||||
struct ser_cardstate *scs;
|
||||
|
||||
scs = kzalloc(sizeof(struct ser_cardstate), GFP_KERNEL);
|
||||
if (!scs) {
|
||||
pr_err("out of memory\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
cs->hw.ser = scs;
|
||||
|
||||
cs->hw.ser->dev.name = GIGASET_MODULENAME;
|
||||
cs->hw.ser->dev.id = cs->minor_index;
|
||||
cs->hw.ser->dev.dev.release = gigaset_device_release;
|
||||
rc = platform_device_register(&cs->hw.ser->dev);
|
||||
if (rc != 0) {
|
||||
pr_err("error %d registering platform device\n", rc);
|
||||
kfree(cs->hw.ser);
|
||||
cs->hw.ser = NULL;
|
||||
return rc;
|
||||
}
|
||||
dev_set_drvdata(&cs->hw.ser->dev.dev, cs);
|
||||
|
||||
tasklet_init(&cs->write_tasklet,
|
||||
gigaset_modem_fill, (unsigned long) cs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* set modem control lines
|
||||
* Parameters:
|
||||
* card state structure
|
||||
* modem control line state ([TIOCM_DTR]|[TIOCM_RTS])
|
||||
* Called by "gigaset_start" and "gigaset_enterconfigmode" in common.c
|
||||
* and by "if_lock" and "if_termios" in interface.c
|
||||
*/
|
||||
static int gigaset_set_modem_ctrl(struct cardstate *cs, unsigned old_state,
|
||||
unsigned new_state)
|
||||
{
|
||||
struct tty_struct *tty = cs->hw.ser->tty;
|
||||
unsigned int set, clear;
|
||||
|
||||
if (!tty || !tty->driver || !tty->ops->tiocmset)
|
||||
return -EINVAL;
|
||||
set = new_state & ~old_state;
|
||||
clear = old_state & ~new_state;
|
||||
if (!set && !clear)
|
||||
return 0;
|
||||
gig_dbg(DEBUG_IF, "tiocmset set %x clear %x", set, clear);
|
||||
return tty->ops->tiocmset(tty, set, clear);
|
||||
}
|
||||
|
||||
static int gigaset_baud_rate(struct cardstate *cs, unsigned cflag)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int gigaset_set_line_ctrl(struct cardstate *cs, unsigned cflag)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct gigaset_ops ops = {
|
||||
gigaset_write_cmd,
|
||||
gigaset_write_room,
|
||||
gigaset_chars_in_buffer,
|
||||
gigaset_brkchars,
|
||||
gigaset_init_bchannel,
|
||||
gigaset_close_bchannel,
|
||||
gigaset_initbcshw,
|
||||
gigaset_freebcshw,
|
||||
gigaset_reinitbcshw,
|
||||
gigaset_initcshw,
|
||||
gigaset_freecshw,
|
||||
gigaset_set_modem_ctrl,
|
||||
gigaset_baud_rate,
|
||||
gigaset_set_line_ctrl,
|
||||
gigaset_m10x_send_skb, /* asyncdata.c */
|
||||
gigaset_m10x_input, /* asyncdata.c */
|
||||
};
|
||||
|
||||
|
||||
/* Line Discipline Interface */
|
||||
/* ========================= */
|
||||
|
||||
/* helper functions for cardstate refcounting */
|
||||
static struct cardstate *cs_get(struct tty_struct *tty)
|
||||
{
|
||||
struct cardstate *cs = tty->disc_data;
|
||||
|
||||
if (!cs || !cs->hw.ser) {
|
||||
gig_dbg(DEBUG_ANY, "%s: no cardstate", __func__);
|
||||
return NULL;
|
||||
}
|
||||
atomic_inc(&cs->hw.ser->refcnt);
|
||||
return cs;
|
||||
}
|
||||
|
||||
static void cs_put(struct cardstate *cs)
|
||||
{
|
||||
if (atomic_dec_and_test(&cs->hw.ser->refcnt))
|
||||
complete(&cs->hw.ser->dead_cmp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Called by the tty driver when the line discipline is pushed onto the tty.
|
||||
* Called in process context.
|
||||
*/
|
||||
static int
|
||||
gigaset_tty_open(struct tty_struct *tty)
|
||||
{
|
||||
struct cardstate *cs;
|
||||
int rc;
|
||||
|
||||
gig_dbg(DEBUG_INIT, "Starting HLL for Gigaset M101");
|
||||
|
||||
pr_info(DRIVER_DESC "\n");
|
||||
|
||||
if (!driver) {
|
||||
pr_err("%s: no driver structure\n", __func__);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* allocate memory for our device state and initialize it */
|
||||
cs = gigaset_initcs(driver, 1, 1, 0, cidmode, GIGASET_MODULENAME);
|
||||
if (!cs) {
|
||||
rc = -ENODEV;
|
||||
goto error;
|
||||
}
|
||||
|
||||
cs->dev = &cs->hw.ser->dev.dev;
|
||||
cs->hw.ser->tty = tty;
|
||||
atomic_set(&cs->hw.ser->refcnt, 1);
|
||||
init_completion(&cs->hw.ser->dead_cmp);
|
||||
|
||||
tty->disc_data = cs;
|
||||
|
||||
/* OK.. Initialization of the datastructures and the HW is done.. Now
|
||||
* startup system and notify the LL that we are ready to run
|
||||
*/
|
||||
if (startmode == SM_LOCKED)
|
||||
cs->mstate = MS_LOCKED;
|
||||
rc = gigaset_start(cs);
|
||||
if (rc < 0) {
|
||||
tasklet_kill(&cs->write_tasklet);
|
||||
goto error;
|
||||
}
|
||||
|
||||
gig_dbg(DEBUG_INIT, "Startup of HLL done");
|
||||
return 0;
|
||||
|
||||
error:
|
||||
gig_dbg(DEBUG_INIT, "Startup of HLL failed");
|
||||
tty->disc_data = NULL;
|
||||
gigaset_freecs(cs);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* Called by the tty driver when the line discipline is removed.
|
||||
* Called from process context.
|
||||
*/
|
||||
static void
|
||||
gigaset_tty_close(struct tty_struct *tty)
|
||||
{
|
||||
struct cardstate *cs = tty->disc_data;
|
||||
|
||||
gig_dbg(DEBUG_INIT, "Stopping HLL for Gigaset M101");
|
||||
|
||||
if (!cs) {
|
||||
gig_dbg(DEBUG_INIT, "%s: no cardstate", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
/* prevent other callers from entering ldisc methods */
|
||||
tty->disc_data = NULL;
|
||||
|
||||
if (!cs->hw.ser)
|
||||
pr_err("%s: no hw cardstate\n", __func__);
|
||||
else {
|
||||
/* wait for running methods to finish */
|
||||
if (!atomic_dec_and_test(&cs->hw.ser->refcnt))
|
||||
wait_for_completion(&cs->hw.ser->dead_cmp);
|
||||
}
|
||||
|
||||
/* stop operations */
|
||||
gigaset_stop(cs);
|
||||
tasklet_kill(&cs->write_tasklet);
|
||||
flush_send_queue(cs);
|
||||
cs->dev = NULL;
|
||||
gigaset_freecs(cs);
|
||||
|
||||
gig_dbg(DEBUG_INIT, "Shutdown of HLL done");
|
||||
}
|
||||
|
||||
/*
|
||||
* Called by the tty driver when the tty line is hung up.
|
||||
* Wait for I/O to driver to complete and unregister ISDN device.
|
||||
* This is already done by the close routine, so just call that.
|
||||
* Called from process context.
|
||||
*/
|
||||
static int gigaset_tty_hangup(struct tty_struct *tty)
|
||||
{
|
||||
gigaset_tty_close(tty);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read on the tty.
|
||||
* Unused, received data goes only to the Gigaset driver.
|
||||
*/
|
||||
static ssize_t
|
||||
gigaset_tty_read(struct tty_struct *tty, struct file *file,
|
||||
unsigned char __user *buf, size_t count)
|
||||
{
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
/*
|
||||
* Write on the tty.
|
||||
* Unused, transmit data comes only from the Gigaset driver.
|
||||
*/
|
||||
static ssize_t
|
||||
gigaset_tty_write(struct tty_struct *tty, struct file *file,
|
||||
const unsigned char *buf, size_t count)
|
||||
{
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
/*
|
||||
* Ioctl on the tty.
|
||||
* Called in process context only.
|
||||
* May be re-entered by multiple ioctl calling threads.
|
||||
*/
|
||||
static int
|
||||
gigaset_tty_ioctl(struct tty_struct *tty, struct file *file,
|
||||
unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct cardstate *cs = cs_get(tty);
|
||||
int rc, val;
|
||||
int __user *p = (int __user *)arg;
|
||||
|
||||
if (!cs)
|
||||
return -ENXIO;
|
||||
|
||||
switch (cmd) {
|
||||
|
||||
case FIONREAD:
|
||||
/* unused, always return zero */
|
||||
val = 0;
|
||||
rc = put_user(val, p);
|
||||
break;
|
||||
|
||||
case TCFLSH:
|
||||
/* flush our buffers and the serial port's buffer */
|
||||
switch (arg) {
|
||||
case TCIFLUSH:
|
||||
/* no own input buffer to flush */
|
||||
break;
|
||||
case TCIOFLUSH:
|
||||
case TCOFLUSH:
|
||||
flush_send_queue(cs);
|
||||
break;
|
||||
}
|
||||
/* Pass through */
|
||||
|
||||
default:
|
||||
/* pass through to underlying serial device */
|
||||
rc = n_tty_ioctl_helper(tty, file, cmd, arg);
|
||||
break;
|
||||
}
|
||||
cs_put(cs);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* Called by the tty driver when a block of data has been received.
|
||||
* Will not be re-entered while running but other ldisc functions
|
||||
* may be called in parallel.
|
||||
* Can be called from hard interrupt level as well as soft interrupt
|
||||
* level or mainline.
|
||||
* Parameters:
|
||||
* tty tty structure
|
||||
* buf buffer containing received characters
|
||||
* cflags buffer containing error flags for received characters (ignored)
|
||||
* count number of received characters
|
||||
*/
|
||||
static void
|
||||
gigaset_tty_receive(struct tty_struct *tty, const unsigned char *buf,
|
||||
char *cflags, int count)
|
||||
{
|
||||
struct cardstate *cs = cs_get(tty);
|
||||
unsigned tail, head, n;
|
||||
struct inbuf_t *inbuf;
|
||||
|
||||
if (!cs)
|
||||
return;
|
||||
inbuf = cs->inbuf;
|
||||
if (!inbuf) {
|
||||
dev_err(cs->dev, "%s: no inbuf\n", __func__);
|
||||
cs_put(cs);
|
||||
return;
|
||||
}
|
||||
|
||||
tail = inbuf->tail;
|
||||
head = inbuf->head;
|
||||
gig_dbg(DEBUG_INTR, "buffer state: %u -> %u, receive %u bytes",
|
||||
head, tail, count);
|
||||
|
||||
if (head <= tail) {
|
||||
/* possible buffer wraparound */
|
||||
n = min_t(unsigned, count, RBUFSIZE - tail);
|
||||
memcpy(inbuf->data + tail, buf, n);
|
||||
tail = (tail + n) % RBUFSIZE;
|
||||
buf += n;
|
||||
count -= n;
|
||||
}
|
||||
|
||||
if (count > 0) {
|
||||
/* tail < head and some data left */
|
||||
n = head - tail - 1;
|
||||
if (count > n) {
|
||||
dev_err(cs->dev,
|
||||
"inbuf overflow, discarding %d bytes\n",
|
||||
count - n);
|
||||
count = n;
|
||||
}
|
||||
memcpy(inbuf->data + tail, buf, count);
|
||||
tail += count;
|
||||
}
|
||||
|
||||
gig_dbg(DEBUG_INTR, "setting tail to %u", tail);
|
||||
inbuf->tail = tail;
|
||||
|
||||
/* Everything was received .. Push data into handler */
|
||||
gig_dbg(DEBUG_INTR, "%s-->BH", __func__);
|
||||
gigaset_schedule_event(cs);
|
||||
cs_put(cs);
|
||||
}
|
||||
|
||||
/*
|
||||
* Called by the tty driver when there's room for more data to send.
|
||||
*/
|
||||
static void
|
||||
gigaset_tty_wakeup(struct tty_struct *tty)
|
||||
{
|
||||
struct cardstate *cs = cs_get(tty);
|
||||
|
||||
clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
|
||||
if (!cs)
|
||||
return;
|
||||
tasklet_schedule(&cs->write_tasklet);
|
||||
cs_put(cs);
|
||||
}
|
||||
|
||||
static struct tty_ldisc_ops gigaset_ldisc = {
|
||||
.owner = THIS_MODULE,
|
||||
.magic = TTY_LDISC_MAGIC,
|
||||
.name = "ser_gigaset",
|
||||
.open = gigaset_tty_open,
|
||||
.close = gigaset_tty_close,
|
||||
.hangup = gigaset_tty_hangup,
|
||||
.read = gigaset_tty_read,
|
||||
.write = gigaset_tty_write,
|
||||
.ioctl = gigaset_tty_ioctl,
|
||||
.receive_buf = gigaset_tty_receive,
|
||||
.write_wakeup = gigaset_tty_wakeup,
|
||||
};
|
||||
|
||||
|
||||
/* Initialization / Shutdown */
|
||||
/* ========================= */
|
||||
|
||||
static int __init ser_gigaset_init(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
gig_dbg(DEBUG_INIT, "%s", __func__);
|
||||
rc = platform_driver_register(&device_driver);
|
||||
if (rc != 0) {
|
||||
pr_err("error %d registering platform driver\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* allocate memory for our driver state and initialize it */
|
||||
driver = gigaset_initdriver(GIGASET_MINOR, GIGASET_MINORS,
|
||||
GIGASET_MODULENAME, GIGASET_DEVNAME,
|
||||
&ops, THIS_MODULE);
|
||||
if (!driver)
|
||||
goto error;
|
||||
|
||||
rc = tty_register_ldisc(N_GIGASET_M101, &gigaset_ldisc);
|
||||
if (rc != 0) {
|
||||
pr_err("error %d registering line discipline\n", rc);
|
||||
goto error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
if (driver) {
|
||||
gigaset_freedriver(driver);
|
||||
driver = NULL;
|
||||
}
|
||||
platform_driver_unregister(&device_driver);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void __exit ser_gigaset_exit(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
gig_dbg(DEBUG_INIT, "%s", __func__);
|
||||
|
||||
if (driver) {
|
||||
gigaset_freedriver(driver);
|
||||
driver = NULL;
|
||||
}
|
||||
|
||||
rc = tty_unregister_ldisc(N_GIGASET_M101);
|
||||
if (rc != 0)
|
||||
pr_err("error %d unregistering line discipline\n", rc);
|
||||
|
||||
platform_driver_unregister(&device_driver);
|
||||
}
|
||||
|
||||
module_init(ser_gigaset_init);
|
||||
module_exit(ser_gigaset_exit);
|
954
drivers/isdn/gigaset/usb-gigaset.c
Normal file
954
drivers/isdn/gigaset/usb-gigaset.c
Normal file
|
@ -0,0 +1,954 @@
|
|||
/*
|
||||
* USB driver for Gigaset 307x directly or using M105 Data.
|
||||
*
|
||||
* Copyright (c) 2001 by Stefan Eilers
|
||||
* and Hansjoerg Lipp <hjlipp@web.de>.
|
||||
*
|
||||
* This driver was derived from the USB skeleton driver by
|
||||
* Greg Kroah-Hartman <greg@kroah.com>
|
||||
*
|
||||
* =====================================================================
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
* =====================================================================
|
||||
*/
|
||||
|
||||
#include "gigaset.h"
|
||||
#include <linux/usb.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
|
||||
/* Version Information */
|
||||
#define DRIVER_AUTHOR "Hansjoerg Lipp <hjlipp@web.de>, Stefan Eilers"
|
||||
#define DRIVER_DESC "USB Driver for Gigaset 307x using M105"
|
||||
|
||||
/* Module parameters */
|
||||
|
||||
static int startmode = SM_ISDN;
|
||||
static int cidmode = 1;
|
||||
|
||||
module_param(startmode, int, S_IRUGO);
|
||||
module_param(cidmode, int, S_IRUGO);
|
||||
MODULE_PARM_DESC(startmode, "start in isdn4linux mode");
|
||||
MODULE_PARM_DESC(cidmode, "Call-ID mode");
|
||||
|
||||
#define GIGASET_MINORS 1
|
||||
#define GIGASET_MINOR 8
|
||||
#define GIGASET_MODULENAME "usb_gigaset"
|
||||
#define GIGASET_DEVNAME "ttyGU"
|
||||
|
||||
/* length limit according to Siemens 3070usb-protokoll.doc ch. 2.1 */
|
||||
#define IF_WRITEBUF 264
|
||||
|
||||
/* Values for the Gigaset M105 Data */
|
||||
#define USB_M105_VENDOR_ID 0x0681
|
||||
#define USB_M105_PRODUCT_ID 0x0009
|
||||
|
||||
/* table of devices that work with this driver */
|
||||
static const struct usb_device_id gigaset_table[] = {
|
||||
{ USB_DEVICE(USB_M105_VENDOR_ID, USB_M105_PRODUCT_ID) },
|
||||
{ } /* Terminating entry */
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(usb, gigaset_table);
|
||||
|
||||
/*
|
||||
* Control requests (empty fields: 00)
|
||||
*
|
||||
* RT|RQ|VALUE|INDEX|LEN |DATA
|
||||
* In:
|
||||
* C1 08 01
|
||||
* Get flags (1 byte). Bits: 0=dtr,1=rts,3-7:?
|
||||
* C1 0F ll ll
|
||||
* Get device information/status (llll: 0x200 and 0x40 seen).
|
||||
* Real size: I only saw MIN(llll,0x64).
|
||||
* Contents: seems to be always the same...
|
||||
* offset 0x00: Length of this structure (0x64) (len: 1,2,3 bytes)
|
||||
* offset 0x3c: String (16 bit chars): "MCCI USB Serial V2.0"
|
||||
* rest: ?
|
||||
* Out:
|
||||
* 41 11
|
||||
* Initialize/reset device ?
|
||||
* 41 00 xx 00
|
||||
* ? (xx=00 or 01; 01 on start, 00 on close)
|
||||
* 41 07 vv mm
|
||||
* Set/clear flags vv=value, mm=mask (see RQ 08)
|
||||
* 41 12 xx
|
||||
* Used before the following configuration requests are issued
|
||||
* (with xx=0x0f). I've seen other values<0xf, though.
|
||||
* 41 01 xx xx
|
||||
* Set baud rate. xxxx=ceil(0x384000/rate)=trunc(0x383fff/rate)+1.
|
||||
* 41 03 ps bb
|
||||
* Set byte size and parity. p: 0x20=even,0x10=odd,0x00=no parity
|
||||
* [ 0x30: m, 0x40: s ]
|
||||
* [s: 0: 1 stop bit; 1: 1.5; 2: 2]
|
||||
* bb: bits/byte (seen 7 and 8)
|
||||
* 41 13 -- -- -- -- 10 00 ww 00 00 00 xx 00 00 00 yy 00 00 00 zz 00 00 00
|
||||
* ??
|
||||
* Initialization: 01, 40, 00, 00
|
||||
* Open device: 00 40, 00, 00
|
||||
* yy and zz seem to be equal, either 0x00 or 0x0a
|
||||
* (ww,xx) pairs seen: (00,00), (00,40), (01,40), (09,80), (19,80)
|
||||
* 41 19 -- -- -- -- 06 00 00 00 00 xx 11 13
|
||||
* Used after every "configuration sequence" (RQ 12, RQs 01/03/13).
|
||||
* xx is usually 0x00 but was 0x7e before starting data transfer
|
||||
* in unimodem mode. So, this might be an array of characters that
|
||||
* need special treatment ("commit all bufferd data"?), 11=^Q, 13=^S.
|
||||
*
|
||||
* Unimodem mode: use "modprobe ppp_async flag_time=0" as the device _needs_ two
|
||||
* flags per packet.
|
||||
*/
|
||||
|
||||
/* functions called if a device of this driver is connected/disconnected */
|
||||
static int gigaset_probe(struct usb_interface *interface,
|
||||
const struct usb_device_id *id);
|
||||
static void gigaset_disconnect(struct usb_interface *interface);
|
||||
|
||||
/* functions called before/after suspend */
|
||||
static int gigaset_suspend(struct usb_interface *intf, pm_message_t message);
|
||||
static int gigaset_resume(struct usb_interface *intf);
|
||||
static int gigaset_pre_reset(struct usb_interface *intf);
|
||||
|
||||
static struct gigaset_driver *driver;
|
||||
|
||||
/* usb specific object needed to register this driver with the usb subsystem */
|
||||
static struct usb_driver gigaset_usb_driver = {
|
||||
.name = GIGASET_MODULENAME,
|
||||
.probe = gigaset_probe,
|
||||
.disconnect = gigaset_disconnect,
|
||||
.id_table = gigaset_table,
|
||||
.suspend = gigaset_suspend,
|
||||
.resume = gigaset_resume,
|
||||
.reset_resume = gigaset_resume,
|
||||
.pre_reset = gigaset_pre_reset,
|
||||
.post_reset = gigaset_resume,
|
||||
.disable_hub_initiated_lpm = 1,
|
||||
};
|
||||
|
||||
struct usb_cardstate {
|
||||
struct usb_device *udev; /* usb device pointer */
|
||||
struct usb_interface *interface; /* interface for this device */
|
||||
int busy; /* bulk output in progress */
|
||||
|
||||
/* Output buffer */
|
||||
unsigned char *bulk_out_buffer;
|
||||
int bulk_out_size;
|
||||
int bulk_out_epnum;
|
||||
struct urb *bulk_out_urb;
|
||||
|
||||
/* Input buffer */
|
||||
unsigned char *rcvbuf;
|
||||
int rcvbuf_size;
|
||||
struct urb *read_urb;
|
||||
|
||||
char bchars[6]; /* for request 0x19 */
|
||||
};
|
||||
|
||||
static inline unsigned tiocm_to_gigaset(unsigned state)
|
||||
{
|
||||
return ((state & TIOCM_DTR) ? 1 : 0) | ((state & TIOCM_RTS) ? 2 : 0);
|
||||
}
|
||||
|
||||
static int gigaset_set_modem_ctrl(struct cardstate *cs, unsigned old_state,
|
||||
unsigned new_state)
|
||||
{
|
||||
struct usb_device *udev = cs->hw.usb->udev;
|
||||
unsigned mask, val;
|
||||
int r;
|
||||
|
||||
mask = tiocm_to_gigaset(old_state ^ new_state);
|
||||
val = tiocm_to_gigaset(new_state);
|
||||
|
||||
gig_dbg(DEBUG_USBREQ, "set flags 0x%02x with mask 0x%02x", val, mask);
|
||||
r = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 7, 0x41,
|
||||
(val & 0xff) | ((mask & 0xff) << 8), 0,
|
||||
NULL, 0, 2000 /* timeout? */);
|
||||
if (r < 0)
|
||||
return r;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set M105 configuration value
|
||||
* using undocumented device commands reverse engineered from USB traces
|
||||
* of the Siemens Windows driver
|
||||
*/
|
||||
static int set_value(struct cardstate *cs, u8 req, u16 val)
|
||||
{
|
||||
struct usb_device *udev = cs->hw.usb->udev;
|
||||
int r, r2;
|
||||
|
||||
gig_dbg(DEBUG_USBREQ, "request %02x (%04x)",
|
||||
(unsigned)req, (unsigned)val);
|
||||
r = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x12, 0x41,
|
||||
0xf /*?*/, 0, NULL, 0, 2000 /*?*/);
|
||||
/* no idea what this does */
|
||||
if (r < 0) {
|
||||
dev_err(&udev->dev, "error %d on request 0x12\n", -r);
|
||||
return r;
|
||||
}
|
||||
|
||||
r = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), req, 0x41,
|
||||
val, 0, NULL, 0, 2000 /*?*/);
|
||||
if (r < 0)
|
||||
dev_err(&udev->dev, "error %d on request 0x%02x\n",
|
||||
-r, (unsigned)req);
|
||||
|
||||
r2 = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x19, 0x41,
|
||||
0, 0, cs->hw.usb->bchars, 6, 2000 /*?*/);
|
||||
if (r2 < 0)
|
||||
dev_err(&udev->dev, "error %d on request 0x19\n", -r2);
|
||||
|
||||
return r < 0 ? r : (r2 < 0 ? r2 : 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* set the baud rate on the internal serial adapter
|
||||
* using the undocumented parameter setting command
|
||||
*/
|
||||
static int gigaset_baud_rate(struct cardstate *cs, unsigned cflag)
|
||||
{
|
||||
u16 val;
|
||||
u32 rate;
|
||||
|
||||
cflag &= CBAUD;
|
||||
|
||||
switch (cflag) {
|
||||
case B300: rate = 300; break;
|
||||
case B600: rate = 600; break;
|
||||
case B1200: rate = 1200; break;
|
||||
case B2400: rate = 2400; break;
|
||||
case B4800: rate = 4800; break;
|
||||
case B9600: rate = 9600; break;
|
||||
case B19200: rate = 19200; break;
|
||||
case B38400: rate = 38400; break;
|
||||
case B57600: rate = 57600; break;
|
||||
case B115200: rate = 115200; break;
|
||||
default:
|
||||
rate = 9600;
|
||||
dev_err(cs->dev, "unsupported baudrate request 0x%x,"
|
||||
" using default of B9600\n", cflag);
|
||||
}
|
||||
|
||||
val = 0x383fff / rate + 1;
|
||||
|
||||
return set_value(cs, 1, val);
|
||||
}
|
||||
|
||||
/*
|
||||
* set the line format on the internal serial adapter
|
||||
* using the undocumented parameter setting command
|
||||
*/
|
||||
static int gigaset_set_line_ctrl(struct cardstate *cs, unsigned cflag)
|
||||
{
|
||||
u16 val = 0;
|
||||
|
||||
/* set the parity */
|
||||
if (cflag & PARENB)
|
||||
val |= (cflag & PARODD) ? 0x10 : 0x20;
|
||||
|
||||
/* set the number of data bits */
|
||||
switch (cflag & CSIZE) {
|
||||
case CS5:
|
||||
val |= 5 << 8; break;
|
||||
case CS6:
|
||||
val |= 6 << 8; break;
|
||||
case CS7:
|
||||
val |= 7 << 8; break;
|
||||
case CS8:
|
||||
val |= 8 << 8; break;
|
||||
default:
|
||||
dev_err(cs->dev, "CSIZE was not CS5-CS8, using default of 8\n");
|
||||
val |= 8 << 8;
|
||||
break;
|
||||
}
|
||||
|
||||
/* set the number of stop bits */
|
||||
if (cflag & CSTOPB) {
|
||||
if ((cflag & CSIZE) == CS5)
|
||||
val |= 1; /* 1.5 stop bits */
|
||||
else
|
||||
val |= 2; /* 2 stop bits */
|
||||
}
|
||||
|
||||
return set_value(cs, 3, val);
|
||||
}
|
||||
|
||||
|
||||
/*============================================================================*/
|
||||
static int gigaset_init_bchannel(struct bc_state *bcs)
|
||||
{
|
||||
/* nothing to do for M10x */
|
||||
gigaset_bchannel_up(bcs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gigaset_close_bchannel(struct bc_state *bcs)
|
||||
{
|
||||
/* nothing to do for M10x */
|
||||
gigaset_bchannel_down(bcs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int write_modem(struct cardstate *cs);
|
||||
static int send_cb(struct cardstate *cs, struct cmdbuf_t *cb);
|
||||
|
||||
|
||||
/* Write tasklet handler: Continue sending current skb, or send command, or
|
||||
* start sending an skb from the send queue.
|
||||
*/
|
||||
static void gigaset_modem_fill(unsigned long data)
|
||||
{
|
||||
struct cardstate *cs = (struct cardstate *) data;
|
||||
struct bc_state *bcs = &cs->bcs[0]; /* only one channel */
|
||||
struct cmdbuf_t *cb;
|
||||
int again;
|
||||
|
||||
gig_dbg(DEBUG_OUTPUT, "modem_fill");
|
||||
|
||||
if (cs->hw.usb->busy) {
|
||||
gig_dbg(DEBUG_OUTPUT, "modem_fill: busy");
|
||||
return;
|
||||
}
|
||||
|
||||
do {
|
||||
again = 0;
|
||||
if (!bcs->tx_skb) { /* no skb is being sent */
|
||||
cb = cs->cmdbuf;
|
||||
if (cb) { /* commands to send? */
|
||||
gig_dbg(DEBUG_OUTPUT, "modem_fill: cb");
|
||||
if (send_cb(cs, cb) < 0) {
|
||||
gig_dbg(DEBUG_OUTPUT,
|
||||
"modem_fill: send_cb failed");
|
||||
again = 1; /* no callback will be
|
||||
called! */
|
||||
}
|
||||
} else { /* skbs to send? */
|
||||
bcs->tx_skb = skb_dequeue(&bcs->squeue);
|
||||
if (bcs->tx_skb)
|
||||
gig_dbg(DEBUG_INTR,
|
||||
"Dequeued skb (Adr: %lx)!",
|
||||
(unsigned long) bcs->tx_skb);
|
||||
}
|
||||
}
|
||||
|
||||
if (bcs->tx_skb) {
|
||||
gig_dbg(DEBUG_OUTPUT, "modem_fill: tx_skb");
|
||||
if (write_modem(cs) < 0) {
|
||||
gig_dbg(DEBUG_OUTPUT,
|
||||
"modem_fill: write_modem failed");
|
||||
again = 1; /* no callback will be called! */
|
||||
}
|
||||
}
|
||||
} while (again);
|
||||
}
|
||||
|
||||
/*
|
||||
* Interrupt Input URB completion routine
|
||||
*/
|
||||
static void gigaset_read_int_callback(struct urb *urb)
|
||||
{
|
||||
struct cardstate *cs = urb->context;
|
||||
struct inbuf_t *inbuf = cs->inbuf;
|
||||
int status = urb->status;
|
||||
int r;
|
||||
unsigned numbytes;
|
||||
unsigned char *src;
|
||||
unsigned long flags;
|
||||
|
||||
if (!status) {
|
||||
numbytes = urb->actual_length;
|
||||
|
||||
if (numbytes) {
|
||||
src = cs->hw.usb->rcvbuf;
|
||||
if (unlikely(*src))
|
||||
dev_warn(cs->dev,
|
||||
"%s: There was no leading 0, but 0x%02x!\n",
|
||||
__func__, (unsigned) *src);
|
||||
++src; /* skip leading 0x00 */
|
||||
--numbytes;
|
||||
if (gigaset_fill_inbuf(inbuf, src, numbytes)) {
|
||||
gig_dbg(DEBUG_INTR, "%s-->BH", __func__);
|
||||
gigaset_schedule_event(inbuf->cs);
|
||||
}
|
||||
} else
|
||||
gig_dbg(DEBUG_INTR, "Received zero block length");
|
||||
} else {
|
||||
/* The urb might have been killed. */
|
||||
gig_dbg(DEBUG_ANY, "%s - nonzero status received: %d",
|
||||
__func__, status);
|
||||
if (status == -ENOENT || status == -ESHUTDOWN)
|
||||
/* killed or endpoint shutdown: don't resubmit */
|
||||
return;
|
||||
}
|
||||
|
||||
/* resubmit URB */
|
||||
spin_lock_irqsave(&cs->lock, flags);
|
||||
if (!cs->connected) {
|
||||
spin_unlock_irqrestore(&cs->lock, flags);
|
||||
pr_err("%s: disconnected\n", __func__);
|
||||
return;
|
||||
}
|
||||
r = usb_submit_urb(urb, GFP_ATOMIC);
|
||||
spin_unlock_irqrestore(&cs->lock, flags);
|
||||
if (r)
|
||||
dev_err(cs->dev, "error %d resubmitting URB\n", -r);
|
||||
}
|
||||
|
||||
|
||||
/* This callback routine is called when data was transmitted to the device. */
|
||||
static void gigaset_write_bulk_callback(struct urb *urb)
|
||||
{
|
||||
struct cardstate *cs = urb->context;
|
||||
int status = urb->status;
|
||||
unsigned long flags;
|
||||
|
||||
switch (status) {
|
||||
case 0: /* normal completion */
|
||||
break;
|
||||
case -ENOENT: /* killed */
|
||||
gig_dbg(DEBUG_ANY, "%s: killed", __func__);
|
||||
cs->hw.usb->busy = 0;
|
||||
return;
|
||||
default:
|
||||
dev_err(cs->dev, "bulk transfer failed (status %d)\n",
|
||||
-status);
|
||||
/* That's all we can do. Communication problems
|
||||
are handled by timeouts or network protocols. */
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&cs->lock, flags);
|
||||
if (!cs->connected) {
|
||||
pr_err("%s: disconnected\n", __func__);
|
||||
} else {
|
||||
cs->hw.usb->busy = 0;
|
||||
tasklet_schedule(&cs->write_tasklet);
|
||||
}
|
||||
spin_unlock_irqrestore(&cs->lock, flags);
|
||||
}
|
||||
|
||||
static int send_cb(struct cardstate *cs, struct cmdbuf_t *cb)
|
||||
{
|
||||
struct cmdbuf_t *tcb;
|
||||
unsigned long flags;
|
||||
int count;
|
||||
int status = -ENOENT;
|
||||
struct usb_cardstate *ucs = cs->hw.usb;
|
||||
|
||||
do {
|
||||
if (!cb->len) {
|
||||
tcb = cb;
|
||||
|
||||
spin_lock_irqsave(&cs->cmdlock, flags);
|
||||
cs->cmdbytes -= cs->curlen;
|
||||
gig_dbg(DEBUG_OUTPUT, "send_cb: sent %u bytes, %u left",
|
||||
cs->curlen, cs->cmdbytes);
|
||||
cs->cmdbuf = cb = cb->next;
|
||||
if (cb) {
|
||||
cb->prev = NULL;
|
||||
cs->curlen = cb->len;
|
||||
} else {
|
||||
cs->lastcmdbuf = NULL;
|
||||
cs->curlen = 0;
|
||||
}
|
||||
spin_unlock_irqrestore(&cs->cmdlock, flags);
|
||||
|
||||
if (tcb->wake_tasklet)
|
||||
tasklet_schedule(tcb->wake_tasklet);
|
||||
kfree(tcb);
|
||||
}
|
||||
if (cb) {
|
||||
count = min(cb->len, ucs->bulk_out_size);
|
||||
gig_dbg(DEBUG_OUTPUT, "send_cb: send %d bytes", count);
|
||||
|
||||
usb_fill_bulk_urb(ucs->bulk_out_urb, ucs->udev,
|
||||
usb_sndbulkpipe(ucs->udev,
|
||||
ucs->bulk_out_epnum),
|
||||
cb->buf + cb->offset, count,
|
||||
gigaset_write_bulk_callback, cs);
|
||||
|
||||
cb->offset += count;
|
||||
cb->len -= count;
|
||||
ucs->busy = 1;
|
||||
|
||||
spin_lock_irqsave(&cs->lock, flags);
|
||||
status = cs->connected ?
|
||||
usb_submit_urb(ucs->bulk_out_urb, GFP_ATOMIC) :
|
||||
-ENODEV;
|
||||
spin_unlock_irqrestore(&cs->lock, flags);
|
||||
|
||||
if (status) {
|
||||
ucs->busy = 0;
|
||||
dev_err(cs->dev,
|
||||
"could not submit urb (error %d)\n",
|
||||
-status);
|
||||
cb->len = 0; /* skip urb => remove cb+wakeup
|
||||
in next loop cycle */
|
||||
}
|
||||
}
|
||||
} while (cb && status); /* next command on error */
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/* Send command to device. */
|
||||
static int gigaset_write_cmd(struct cardstate *cs, struct cmdbuf_t *cb)
|
||||
{
|
||||
unsigned long flags;
|
||||
int len;
|
||||
|
||||
gigaset_dbg_buffer(cs->mstate != MS_LOCKED ?
|
||||
DEBUG_TRANSCMD : DEBUG_LOCKCMD,
|
||||
"CMD Transmit", cb->len, cb->buf);
|
||||
|
||||
spin_lock_irqsave(&cs->cmdlock, flags);
|
||||
cb->prev = cs->lastcmdbuf;
|
||||
if (cs->lastcmdbuf)
|
||||
cs->lastcmdbuf->next = cb;
|
||||
else {
|
||||
cs->cmdbuf = cb;
|
||||
cs->curlen = cb->len;
|
||||
}
|
||||
cs->cmdbytes += cb->len;
|
||||
cs->lastcmdbuf = cb;
|
||||
spin_unlock_irqrestore(&cs->cmdlock, flags);
|
||||
|
||||
spin_lock_irqsave(&cs->lock, flags);
|
||||
len = cb->len;
|
||||
if (cs->connected)
|
||||
tasklet_schedule(&cs->write_tasklet);
|
||||
spin_unlock_irqrestore(&cs->lock, flags);
|
||||
return len;
|
||||
}
|
||||
|
||||
static int gigaset_write_room(struct cardstate *cs)
|
||||
{
|
||||
unsigned bytes;
|
||||
|
||||
bytes = cs->cmdbytes;
|
||||
return bytes < IF_WRITEBUF ? IF_WRITEBUF - bytes : 0;
|
||||
}
|
||||
|
||||
static int gigaset_chars_in_buffer(struct cardstate *cs)
|
||||
{
|
||||
return cs->cmdbytes;
|
||||
}
|
||||
|
||||
/*
|
||||
* set the break characters on the internal serial adapter
|
||||
* using undocumented device commands reverse engineered from USB traces
|
||||
* of the Siemens Windows driver
|
||||
*/
|
||||
static int gigaset_brkchars(struct cardstate *cs, const unsigned char buf[6])
|
||||
{
|
||||
struct usb_device *udev = cs->hw.usb->udev;
|
||||
|
||||
gigaset_dbg_buffer(DEBUG_USBREQ, "brkchars", 6, buf);
|
||||
memcpy(cs->hw.usb->bchars, buf, 6);
|
||||
return usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x19, 0x41,
|
||||
0, 0, &buf, 6, 2000);
|
||||
}
|
||||
|
||||
static void gigaset_freebcshw(struct bc_state *bcs)
|
||||
{
|
||||
/* unused */
|
||||
}
|
||||
|
||||
/* Initialize the b-channel structure */
|
||||
static int gigaset_initbcshw(struct bc_state *bcs)
|
||||
{
|
||||
/* unused */
|
||||
bcs->hw.usb = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void gigaset_reinitbcshw(struct bc_state *bcs)
|
||||
{
|
||||
/* nothing to do for M10x */
|
||||
}
|
||||
|
||||
static void gigaset_freecshw(struct cardstate *cs)
|
||||
{
|
||||
tasklet_kill(&cs->write_tasklet);
|
||||
kfree(cs->hw.usb);
|
||||
}
|
||||
|
||||
static int gigaset_initcshw(struct cardstate *cs)
|
||||
{
|
||||
struct usb_cardstate *ucs;
|
||||
|
||||
cs->hw.usb = ucs =
|
||||
kmalloc(sizeof(struct usb_cardstate), GFP_KERNEL);
|
||||
if (!ucs) {
|
||||
pr_err("out of memory\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ucs->bchars[0] = 0;
|
||||
ucs->bchars[1] = 0;
|
||||
ucs->bchars[2] = 0;
|
||||
ucs->bchars[3] = 0;
|
||||
ucs->bchars[4] = 0x11;
|
||||
ucs->bchars[5] = 0x13;
|
||||
ucs->bulk_out_buffer = NULL;
|
||||
ucs->bulk_out_urb = NULL;
|
||||
ucs->read_urb = NULL;
|
||||
tasklet_init(&cs->write_tasklet,
|
||||
gigaset_modem_fill, (unsigned long) cs);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Send data from current skb to the device. */
|
||||
static int write_modem(struct cardstate *cs)
|
||||
{
|
||||
int ret = 0;
|
||||
int count;
|
||||
struct bc_state *bcs = &cs->bcs[0]; /* only one channel */
|
||||
struct usb_cardstate *ucs = cs->hw.usb;
|
||||
unsigned long flags;
|
||||
|
||||
gig_dbg(DEBUG_OUTPUT, "len: %d...", bcs->tx_skb->len);
|
||||
|
||||
if (!bcs->tx_skb->len) {
|
||||
dev_kfree_skb_any(bcs->tx_skb);
|
||||
bcs->tx_skb = NULL;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Copy data to bulk out buffer and transmit data */
|
||||
count = min(bcs->tx_skb->len, (unsigned) ucs->bulk_out_size);
|
||||
skb_copy_from_linear_data(bcs->tx_skb, ucs->bulk_out_buffer, count);
|
||||
skb_pull(bcs->tx_skb, count);
|
||||
ucs->busy = 1;
|
||||
gig_dbg(DEBUG_OUTPUT, "write_modem: send %d bytes", count);
|
||||
|
||||
spin_lock_irqsave(&cs->lock, flags);
|
||||
if (cs->connected) {
|
||||
usb_fill_bulk_urb(ucs->bulk_out_urb, ucs->udev,
|
||||
usb_sndbulkpipe(ucs->udev,
|
||||
ucs->bulk_out_epnum),
|
||||
ucs->bulk_out_buffer, count,
|
||||
gigaset_write_bulk_callback, cs);
|
||||
ret = usb_submit_urb(ucs->bulk_out_urb, GFP_ATOMIC);
|
||||
} else {
|
||||
ret = -ENODEV;
|
||||
}
|
||||
spin_unlock_irqrestore(&cs->lock, flags);
|
||||
|
||||
if (ret) {
|
||||
dev_err(cs->dev, "could not submit urb (error %d)\n", -ret);
|
||||
ucs->busy = 0;
|
||||
}
|
||||
|
||||
if (!bcs->tx_skb->len) {
|
||||
/* skb sent completely */
|
||||
gigaset_skb_sent(bcs, bcs->tx_skb);
|
||||
|
||||
gig_dbg(DEBUG_INTR, "kfree skb (Adr: %lx)!",
|
||||
(unsigned long) bcs->tx_skb);
|
||||
dev_kfree_skb_any(bcs->tx_skb);
|
||||
bcs->tx_skb = NULL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int gigaset_probe(struct usb_interface *interface,
|
||||
const struct usb_device_id *id)
|
||||
{
|
||||
int retval;
|
||||
struct usb_device *udev = interface_to_usbdev(interface);
|
||||
struct usb_host_interface *hostif = interface->cur_altsetting;
|
||||
struct cardstate *cs = NULL;
|
||||
struct usb_cardstate *ucs = NULL;
|
||||
struct usb_endpoint_descriptor *endpoint;
|
||||
int buffer_size;
|
||||
|
||||
gig_dbg(DEBUG_ANY, "%s: Check if device matches ...", __func__);
|
||||
|
||||
/* See if the device offered us matches what we can accept */
|
||||
if ((le16_to_cpu(udev->descriptor.idVendor) != USB_M105_VENDOR_ID) ||
|
||||
(le16_to_cpu(udev->descriptor.idProduct) != USB_M105_PRODUCT_ID)) {
|
||||
gig_dbg(DEBUG_ANY, "device ID (0x%x, 0x%x) not for me - skip",
|
||||
le16_to_cpu(udev->descriptor.idVendor),
|
||||
le16_to_cpu(udev->descriptor.idProduct));
|
||||
return -ENODEV;
|
||||
}
|
||||
if (hostif->desc.bInterfaceNumber != 0) {
|
||||
gig_dbg(DEBUG_ANY, "interface %d not for me - skip",
|
||||
hostif->desc.bInterfaceNumber);
|
||||
return -ENODEV;
|
||||
}
|
||||
if (hostif->desc.bAlternateSetting != 0) {
|
||||
dev_notice(&udev->dev, "unsupported altsetting %d - skip",
|
||||
hostif->desc.bAlternateSetting);
|
||||
return -ENODEV;
|
||||
}
|
||||
if (hostif->desc.bInterfaceClass != 255) {
|
||||
dev_notice(&udev->dev, "unsupported interface class %d - skip",
|
||||
hostif->desc.bInterfaceClass);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
dev_info(&udev->dev, "%s: Device matched ... !\n", __func__);
|
||||
|
||||
/* allocate memory for our device state and initialize it */
|
||||
cs = gigaset_initcs(driver, 1, 1, 0, cidmode, GIGASET_MODULENAME);
|
||||
if (!cs)
|
||||
return -ENODEV;
|
||||
ucs = cs->hw.usb;
|
||||
|
||||
/* save off device structure ptrs for later use */
|
||||
usb_get_dev(udev);
|
||||
ucs->udev = udev;
|
||||
ucs->interface = interface;
|
||||
cs->dev = &interface->dev;
|
||||
|
||||
/* save address of controller structure */
|
||||
usb_set_intfdata(interface, cs);
|
||||
|
||||
endpoint = &hostif->endpoint[0].desc;
|
||||
|
||||
buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);
|
||||
ucs->bulk_out_size = buffer_size;
|
||||
ucs->bulk_out_epnum = usb_endpoint_num(endpoint);
|
||||
ucs->bulk_out_buffer = kmalloc(buffer_size, GFP_KERNEL);
|
||||
if (!ucs->bulk_out_buffer) {
|
||||
dev_err(cs->dev, "Couldn't allocate bulk_out_buffer\n");
|
||||
retval = -ENOMEM;
|
||||
goto error;
|
||||
}
|
||||
|
||||
ucs->bulk_out_urb = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!ucs->bulk_out_urb) {
|
||||
dev_err(cs->dev, "Couldn't allocate bulk_out_urb\n");
|
||||
retval = -ENOMEM;
|
||||
goto error;
|
||||
}
|
||||
|
||||
endpoint = &hostif->endpoint[1].desc;
|
||||
|
||||
ucs->busy = 0;
|
||||
|
||||
ucs->read_urb = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!ucs->read_urb) {
|
||||
dev_err(cs->dev, "No free urbs available\n");
|
||||
retval = -ENOMEM;
|
||||
goto error;
|
||||
}
|
||||
buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);
|
||||
ucs->rcvbuf_size = buffer_size;
|
||||
ucs->rcvbuf = kmalloc(buffer_size, GFP_KERNEL);
|
||||
if (!ucs->rcvbuf) {
|
||||
dev_err(cs->dev, "Couldn't allocate rcvbuf\n");
|
||||
retval = -ENOMEM;
|
||||
goto error;
|
||||
}
|
||||
/* Fill the interrupt urb and send it to the core */
|
||||
usb_fill_int_urb(ucs->read_urb, udev,
|
||||
usb_rcvintpipe(udev, usb_endpoint_num(endpoint)),
|
||||
ucs->rcvbuf, buffer_size,
|
||||
gigaset_read_int_callback,
|
||||
cs, endpoint->bInterval);
|
||||
|
||||
retval = usb_submit_urb(ucs->read_urb, GFP_KERNEL);
|
||||
if (retval) {
|
||||
dev_err(cs->dev, "Could not submit URB (error %d)\n", -retval);
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* tell common part that the device is ready */
|
||||
if (startmode == SM_LOCKED)
|
||||
cs->mstate = MS_LOCKED;
|
||||
|
||||
retval = gigaset_start(cs);
|
||||
if (retval < 0) {
|
||||
tasklet_kill(&cs->write_tasklet);
|
||||
goto error;
|
||||
}
|
||||
return 0;
|
||||
|
||||
error:
|
||||
usb_kill_urb(ucs->read_urb);
|
||||
kfree(ucs->bulk_out_buffer);
|
||||
usb_free_urb(ucs->bulk_out_urb);
|
||||
kfree(ucs->rcvbuf);
|
||||
usb_free_urb(ucs->read_urb);
|
||||
usb_set_intfdata(interface, NULL);
|
||||
ucs->read_urb = ucs->bulk_out_urb = NULL;
|
||||
ucs->rcvbuf = ucs->bulk_out_buffer = NULL;
|
||||
usb_put_dev(ucs->udev);
|
||||
ucs->udev = NULL;
|
||||
ucs->interface = NULL;
|
||||
gigaset_freecs(cs);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void gigaset_disconnect(struct usb_interface *interface)
|
||||
{
|
||||
struct cardstate *cs;
|
||||
struct usb_cardstate *ucs;
|
||||
|
||||
cs = usb_get_intfdata(interface);
|
||||
ucs = cs->hw.usb;
|
||||
|
||||
dev_info(cs->dev, "disconnecting Gigaset USB adapter\n");
|
||||
|
||||
usb_kill_urb(ucs->read_urb);
|
||||
|
||||
gigaset_stop(cs);
|
||||
|
||||
usb_set_intfdata(interface, NULL);
|
||||
tasklet_kill(&cs->write_tasklet);
|
||||
|
||||
usb_kill_urb(ucs->bulk_out_urb);
|
||||
|
||||
kfree(ucs->bulk_out_buffer);
|
||||
usb_free_urb(ucs->bulk_out_urb);
|
||||
kfree(ucs->rcvbuf);
|
||||
usb_free_urb(ucs->read_urb);
|
||||
ucs->read_urb = ucs->bulk_out_urb = NULL;
|
||||
ucs->rcvbuf = ucs->bulk_out_buffer = NULL;
|
||||
|
||||
usb_put_dev(ucs->udev);
|
||||
ucs->interface = NULL;
|
||||
ucs->udev = NULL;
|
||||
cs->dev = NULL;
|
||||
gigaset_freecs(cs);
|
||||
}
|
||||
|
||||
/* gigaset_suspend
|
||||
* This function is called before the USB connection is suspended or reset.
|
||||
*/
|
||||
static int gigaset_suspend(struct usb_interface *intf, pm_message_t message)
|
||||
{
|
||||
struct cardstate *cs = usb_get_intfdata(intf);
|
||||
|
||||
/* stop activity */
|
||||
cs->connected = 0; /* prevent rescheduling */
|
||||
usb_kill_urb(cs->hw.usb->read_urb);
|
||||
tasklet_kill(&cs->write_tasklet);
|
||||
usb_kill_urb(cs->hw.usb->bulk_out_urb);
|
||||
|
||||
gig_dbg(DEBUG_SUSPEND, "suspend complete");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* gigaset_resume
|
||||
* This function is called after the USB connection has been resumed or reset.
|
||||
*/
|
||||
static int gigaset_resume(struct usb_interface *intf)
|
||||
{
|
||||
struct cardstate *cs = usb_get_intfdata(intf);
|
||||
int rc;
|
||||
|
||||
/* resubmit interrupt URB */
|
||||
cs->connected = 1;
|
||||
rc = usb_submit_urb(cs->hw.usb->read_urb, GFP_KERNEL);
|
||||
if (rc) {
|
||||
dev_err(cs->dev, "Could not submit read URB (error %d)\n", -rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
gig_dbg(DEBUG_SUSPEND, "resume complete");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* gigaset_pre_reset
|
||||
* This function is called before the USB connection is reset.
|
||||
*/
|
||||
static int gigaset_pre_reset(struct usb_interface *intf)
|
||||
{
|
||||
/* same as suspend */
|
||||
return gigaset_suspend(intf, PMSG_ON);
|
||||
}
|
||||
|
||||
static const struct gigaset_ops ops = {
|
||||
gigaset_write_cmd,
|
||||
gigaset_write_room,
|
||||
gigaset_chars_in_buffer,
|
||||
gigaset_brkchars,
|
||||
gigaset_init_bchannel,
|
||||
gigaset_close_bchannel,
|
||||
gigaset_initbcshw,
|
||||
gigaset_freebcshw,
|
||||
gigaset_reinitbcshw,
|
||||
gigaset_initcshw,
|
||||
gigaset_freecshw,
|
||||
gigaset_set_modem_ctrl,
|
||||
gigaset_baud_rate,
|
||||
gigaset_set_line_ctrl,
|
||||
gigaset_m10x_send_skb,
|
||||
gigaset_m10x_input,
|
||||
};
|
||||
|
||||
/*
|
||||
* This function is called while kernel-module is loaded
|
||||
*/
|
||||
static int __init usb_gigaset_init(void)
|
||||
{
|
||||
int result;
|
||||
|
||||
/* allocate memory for our driver state and initialize it */
|
||||
driver = gigaset_initdriver(GIGASET_MINOR, GIGASET_MINORS,
|
||||
GIGASET_MODULENAME, GIGASET_DEVNAME,
|
||||
&ops, THIS_MODULE);
|
||||
if (driver == NULL) {
|
||||
result = -ENOMEM;
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* register this driver with the USB subsystem */
|
||||
result = usb_register(&gigaset_usb_driver);
|
||||
if (result < 0) {
|
||||
pr_err("error %d registering USB driver\n", -result);
|
||||
goto error;
|
||||
}
|
||||
|
||||
pr_info(DRIVER_DESC "\n");
|
||||
return 0;
|
||||
|
||||
error:
|
||||
if (driver)
|
||||
gigaset_freedriver(driver);
|
||||
driver = NULL;
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function is called while unloading the kernel-module
|
||||
*/
|
||||
static void __exit usb_gigaset_exit(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
gigaset_blockdriver(driver); /* => probe will fail
|
||||
* => no gigaset_start any more
|
||||
*/
|
||||
|
||||
/* stop all connected devices */
|
||||
for (i = 0; i < driver->minors; i++)
|
||||
gigaset_shutdown(driver->cs + i);
|
||||
|
||||
/* from now on, no isdn callback should be possible */
|
||||
|
||||
/* deregister this driver with the USB subsystem */
|
||||
usb_deregister(&gigaset_usb_driver);
|
||||
/* this will call the disconnect-callback */
|
||||
/* from now on, no disconnect/probe callback should be running */
|
||||
|
||||
gigaset_freedriver(driver);
|
||||
driver = NULL;
|
||||
}
|
||||
|
||||
|
||||
module_init(usb_gigaset_init);
|
||||
module_exit(usb_gigaset_exit);
|
||||
|
||||
MODULE_AUTHOR(DRIVER_AUTHOR);
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
Loading…
Add table
Add a link
Reference in a new issue