Fixed MTP to work with TWRP

This commit is contained in:
awab228 2018-06-19 23:16:04 +02:00
commit f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions

View 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

View 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

View 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);

File diff suppressed because it is too large Load diff

2533
drivers/isdn/gigaset/capi.c Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View 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)
{
}

File diff suppressed because it is too large Load diff

View 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
View 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 */
}

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

File diff suppressed because it is too large Load diff

View 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");
}

View 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);

View 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");