add idl4k kernel firmware version 1.13.0.105

This commit is contained in:
Jaroslav Kysela
2015-03-26 17:22:37 +01:00
parent 5194d2792e
commit e9070cdc77
31064 changed files with 12769984 additions and 0 deletions

View File

@@ -0,0 +1,46 @@
menuconfig ISDN_DRV_GIGASET
tristate "Siemens Gigaset support"
depends on ISDN_I4L
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".
if ISDN_DRV_GIGASET
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,9 @@
gigaset-y := common.o interface.o proc.o ev-layer.o i4l.o asyncdata.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,588 @@
/*
* 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>
/* check if byte must be stuffed/escaped
* I'm not sure which data should be encoded.
* Therefore I will go the hard way and decode 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 (modem response)
* Return value:
* number of processed bytes
*/
static inline int cmd_loop(unsigned char c, unsigned char *src, int numbytes,
struct inbuf_t *inbuf)
{
struct cardstate *cs = inbuf->cs;
unsigned cbytes = cs->cbytes;
int inputstate = inbuf->inputstate;
int startbytes = numbytes;
for (;;) {
cs->respdata[cbytes] = c;
if (c == 10 || c == 13) {
gig_dbg(DEBUG_TRANSCMD, "%s: End of Command (%d Bytes)",
__func__, cbytes);
cs->cbytes = cbytes;
gigaset_handle_modem_response(cs); /* can change
cs->dle */
cbytes = 0;
if (cs->dle &&
!(inputstate & INS_DLE_command)) {
inputstate &= ~INS_command;
break;
}
} else {
/* advance in line buffer, checking for overflow */
if (cbytes < MAX_RESP_SIZE - 1)
cbytes++;
else
dev_warn(cs->dev, "response too large\n");
}
if (!numbytes)
break;
c = *src++;
--numbytes;
if (c == DLE_FLAG &&
(cs->dle || inputstate & INS_DLE_command)) {
inputstate |= INS_DLE_char;
break;
}
}
cs->cbytes = cbytes;
inbuf->inputstate = inputstate;
return startbytes - numbytes;
}
/* process a block of received bytes in lock mode (tty i/f)
* Return value:
* number of processed bytes
*/
static inline int lock_loop(unsigned char *src, int numbytes,
struct inbuf_t *inbuf)
{
struct cardstate *cs = inbuf->cs;
gigaset_dbg_buffer(DEBUG_LOCKCMD, "received response",
numbytes, src);
gigaset_if_receive(cs, src, numbytes);
return numbytes;
}
/* process a block of received bytes in HDLC data mode
* 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
* numbytes (all bytes processed) on error --FIXME
*/
static inline int hdlc_loop(unsigned char c, unsigned char *src, int numbytes,
struct inbuf_t *inbuf)
{
struct cardstate *cs = inbuf->cs;
struct bc_state *bcs = inbuf->bcs;
int inputstate = bcs->inputstate;
__u16 fcs = bcs->fcs;
struct sk_buff *skb = bcs->skb;
unsigned char error;
struct sk_buff *compskb;
int startbytes = numbytes;
int l;
if (unlikely(inputstate & INS_byte_stuff)) {
inputstate &= ~INS_byte_stuff;
goto byte_stuff;
}
for (;;) {
if (unlikely(c == PPP_ESCAPE)) {
if (unlikely(!numbytes)) {
inputstate |= INS_byte_stuff;
break;
}
c = *src++;
--numbytes;
if (unlikely(c == DLE_FLAG &&
(cs->dle ||
inbuf->inputstate & INS_DLE_command))) {
inbuf->inputstate |= INS_DLE_char;
inputstate |= INS_byte_stuff;
break;
}
byte_stuff:
c ^= PPP_TRANS;
if (unlikely(!muststuff(c)))
gig_dbg(DEBUG_HDLC, "byte stuffed: 0x%02x", c);
} else if (unlikely(c == PPP_FLAG)) {
if (unlikely(inputstate & INS_skip_frame)) {
#ifdef CONFIG_GIGASET_DEBUG
if (!(inputstate & INS_have_data)) { /* 7E 7E */
++bcs->emptycount;
} else
gig_dbg(DEBUG_HDLC,
"7e----------------------------");
#endif
/* end of frame */
error = 1;
gigaset_rcv_error(NULL, cs, bcs);
} else if (!(inputstate & INS_have_data)) { /* 7E 7E */
#ifdef CONFIG_GIGASET_DEBUG
++bcs->emptycount;
#endif
break;
} else {
gig_dbg(DEBUG_HDLC,
"7e----------------------------");
/* end of frame */
error = 0;
if (unlikely(fcs != PPP_GOODFCS)) {
dev_err(cs->dev,
"Checksum failed, %u bytes corrupted!\n",
skb->len);
compskb = NULL;
gigaset_rcv_error(compskb, cs, bcs);
error = 1;
} else {
if (likely((l = skb->len) > 2)) {
skb->tail -= 2;
skb->len -= 2;
} else {
dev_kfree_skb(skb);
skb = NULL;
inputstate |= INS_skip_frame;
if (l == 1) {
dev_err(cs->dev,
"invalid packet size (1)!\n");
error = 1;
gigaset_rcv_error(NULL,
cs, bcs);
}
}
if (likely(!(error ||
(inputstate &
INS_skip_frame)))) {
gigaset_rcv_skb(skb, cs, bcs);
}
}
}
if (unlikely(error))
if (skb)
dev_kfree_skb(skb);
fcs = PPP_INITFCS;
inputstate &= ~(INS_have_data | INS_skip_frame);
if (unlikely(bcs->ignore)) {
inputstate |= INS_skip_frame;
skb = NULL;
} else if (likely((skb = dev_alloc_skb(SBUFSIZE + HW_HDR_LEN)) != NULL)) {
skb_reserve(skb, HW_HDR_LEN);
} else {
dev_warn(cs->dev,
"could not allocate new skb\n");
inputstate |= INS_skip_frame;
}
break;
} else if (unlikely(muststuff(c))) {
/* Should not happen. Possible after ZDLE=1<CR><LF>. */
gig_dbg(DEBUG_HDLC, "not byte stuffed: 0x%02x", c);
}
/* add character */
#ifdef CONFIG_GIGASET_DEBUG
if (unlikely(!(inputstate & INS_have_data))) {
gig_dbg(DEBUG_HDLC, "7e (%d x) ================",
bcs->emptycount);
bcs->emptycount = 0;
}
#endif
inputstate |= INS_have_data;
if (likely(!(inputstate & INS_skip_frame))) {
if (unlikely(skb->len == SBUFSIZE)) {
dev_warn(cs->dev, "received packet too long\n");
dev_kfree_skb_any(skb);
skb = NULL;
inputstate |= INS_skip_frame;
break;
}
*__skb_put(skb, 1) = c;
fcs = crc_ccitt_byte(fcs, c);
}
if (unlikely(!numbytes))
break;
c = *src++;
--numbytes;
if (unlikely(c == DLE_FLAG &&
(cs->dle ||
inbuf->inputstate & INS_DLE_command))) {
inbuf->inputstate |= INS_DLE_char;
break;
}
}
bcs->inputstate = inputstate;
bcs->fcs = fcs;
bcs->skb = skb;
return startbytes - numbytes;
}
/* process a block of received bytes in transparent data mode
* 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
* numbytes (all bytes processed) on error --FIXME
*/
static inline int iraw_loop(unsigned char c, unsigned char *src, int numbytes,
struct inbuf_t *inbuf)
{
struct cardstate *cs = inbuf->cs;
struct bc_state *bcs = inbuf->bcs;
int inputstate = bcs->inputstate;
struct sk_buff *skb = bcs->skb;
int startbytes = numbytes;
for (;;) {
/* add character */
inputstate |= INS_have_data;
if (likely(!(inputstate & INS_skip_frame))) {
if (unlikely(skb->len == SBUFSIZE)) {
//FIXME just pass skb up and allocate a new one
dev_warn(cs->dev, "received packet too long\n");
dev_kfree_skb_any(skb);
skb = NULL;
inputstate |= INS_skip_frame;
break;
}
*__skb_put(skb, 1) = bitrev8(c);
}
if (unlikely(!numbytes))
break;
c = *src++;
--numbytes;
if (unlikely(c == DLE_FLAG &&
(cs->dle ||
inbuf->inputstate & INS_DLE_command))) {
inbuf->inputstate |= INS_DLE_char;
break;
}
}
/* pass data up */
if (likely(inputstate & INS_have_data)) {
if (likely(!(inputstate & INS_skip_frame))) {
gigaset_rcv_skb(skb, cs, bcs);
}
inputstate &= ~(INS_have_data | INS_skip_frame);
if (unlikely(bcs->ignore)) {
inputstate |= INS_skip_frame;
skb = NULL;
} else if (likely((skb = dev_alloc_skb(SBUFSIZE + HW_HDR_LEN))
!= NULL)) {
skb_reserve(skb, HW_HDR_LEN);
} else {
dev_warn(cs->dev, "could not allocate new skb\n");
inputstate |= INS_skip_frame;
}
}
bcs->inputstate = inputstate;
bcs->skb = skb;
return startbytes - numbytes;
}
/**
* 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;
unsigned tail, head, numbytes;
unsigned char *src, c;
int procbytes;
head = inbuf->head;
tail = inbuf->tail;
gig_dbg(DEBUG_INTR, "buffer state: %u -> %u", head, tail);
if (head != tail) {
cs = inbuf->cs;
src = inbuf->data + head;
numbytes = (head > tail ? RBUFSIZE : tail) - head;
gig_dbg(DEBUG_INTR, "processing %u bytes", numbytes);
while (numbytes) {
if (cs->mstate == MS_LOCKED) {
procbytes = lock_loop(src, numbytes, inbuf);
src += procbytes;
numbytes -= procbytes;
} else {
c = *src++;
--numbytes;
if (c == DLE_FLAG && (cs->dle ||
inbuf->inputstate & INS_DLE_command)) {
if (!(inbuf->inputstate & INS_DLE_char)) {
inbuf->inputstate |= INS_DLE_char;
goto nextbyte;
}
/* <DLE> <DLE> => <DLE> in data stream */
inbuf->inputstate &= ~INS_DLE_char;
}
if (!(inbuf->inputstate & INS_DLE_char)) {
/* FIXME use function pointers? */
if (inbuf->inputstate & INS_command)
procbytes = cmd_loop(c, src, numbytes, inbuf);
else if (inbuf->bcs->proto2 == ISDN_PROTO_L2_HDLC)
procbytes = hdlc_loop(c, src, numbytes, inbuf);
else
procbytes = iraw_loop(c, src, numbytes, inbuf);
src += procbytes;
numbytes -= procbytes;
} else { /* DLE char */
inbuf->inputstate &= ~INS_DLE_char;
switch (c) {
case 'X': /*begin of command*/
if (inbuf->inputstate & INS_command)
dev_warn(cs->dev,
"received <DLE> 'X' in command mode\n");
inbuf->inputstate |=
INS_command | INS_DLE_command;
break;
case '.': /*end of command*/
if (!(inbuf->inputstate & INS_command))
dev_warn(cs->dev,
"received <DLE> '.' in hdlc mode\n");
inbuf->inputstate &= cs->dle ?
~(INS_DLE_command|INS_command)
: ~INS_DLE_command;
break;
//case DLE_FLAG: /*DLE_FLAG in data stream*/ /* schon oben behandelt! */
default:
dev_err(cs->dev,
"received 0x10 0x%02x!\n",
(int) c);
/* FIXME: reset driver?? */
}
}
}
nextbyte:
if (!numbytes) {
/* end of buffer, check for wrap */
if (head > tail) {
head = 0;
src = inbuf->data;
numbytes = tail;
} else {
head = tail;
break;
}
}
}
gig_dbg(DEBUG_INTR, "setting head to %u", head);
inbuf->head = head;
}
}
EXPORT_SYMBOL_GPL(gigaset_m10x_input);
/* == data output ========================================================== */
/* Encoding of a PPP packet into an octet stuffed HDLC frame
* with FCS, opening and closing flags.
* parameters:
* skb skb containing original packet (freed upon return)
* head number of headroom bytes to allocate in result skb
* tail number of tailroom bytes to allocate in result skb
* Return value:
* pointer to newly allocated skb containing the result frame
*/
static struct sk_buff *HDLC_Encode(struct sk_buff *skb, int head, int tail)
{
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
*/
hdlc_skb = dev_alloc_skb(skb->len + stuf_cnt + 6 + tail + head);
if (!hdlc_skb) {
dev_kfree_skb(skb);
return NULL;
}
skb_reserve(hdlc_skb, head);
/* Copy acknowledge request into new skb */
memcpy(hdlc_skb->head, skb->head, 2);
/* 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(skb);
return hdlc_skb;
}
/* Encoding of a raw packet into an octet stuffed bit inverted frame
* parameters:
* skb skb containing original packet (freed upon return)
* head number of headroom bytes to allocate in result skb
* tail number of tailroom bytes to allocate in result skb
* Return value:
* pointer to newly allocated skb containing the result frame
*/
static struct sk_buff *iraw_encode(struct sk_buff *skb, int head, int tail)
{
struct sk_buff *iraw_skb;
unsigned char c;
unsigned char *cp;
int len;
/* worst case: every byte must be stuffed */
iraw_skb = dev_alloc_skb(2*skb->len + tail + head);
if (!iraw_skb) {
dev_kfree_skb(skb);
return NULL;
}
skb_reserve(iraw_skb, head);
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(skb);
return iraw_skb;
}
/**
* gigaset_m10x_send_skb() - queue an skb for sending
* @bcs: B channel descriptor structure.
* @skb: data to send.
*
* Called by i4l.c to encode and queue an skb for sending, and start
* transmission if necessary.
*
* 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)
{
unsigned len = skb->len;
unsigned long flags;
if (bcs->proto2 == ISDN_PROTO_L2_HDLC)
skb = HDLC_Encode(skb, HW_HDR_LEN, 0);
else
skb = iraw_encode(skb, HW_HDR_LEN, 0);
if (!skb) {
dev_err(bcs->cs->dev,
"unable to allocate memory for encoding!\n");
return -ENOMEM;
}
skb_queue_tail(&bcs->squeue, skb);
spin_lock_irqsave(&bcs->cs->lock, flags);
if (bcs->cs->connected)
tasklet_schedule(&bcs->cs->write_tasklet);
spin_unlock_irqrestore(&bcs->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

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,869 @@
/*
* 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/compiler.h>
#include <linux/types.h>
#include <linux/spinlock.h>
#include <linux/isdnif.h>
#include <linux/usb.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 <asm/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 512 /* Max. size of a response string */
#define HW_HDR_LEN 2 /* Header size used to store ack info */
#define MAX_EVENTS 64 /* size of event queue */
#define RBUFSIZE 8192
#define SBUFSIZE 4096 /* sk_buff payload size */
#define TRANSBUFSIZE 768 /* bytes per skb for transparent receive */
#define MAX_BUF_SIZE (SBUFSIZE - 2) /* Max. size of a data packet from LL */
/* compile time options */
#define GIG_MAJOR 0
#define GIG_MAYINITONDIAL
#define GIG_RETRYCID
#define GIG_X75
#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_DRIVER = 0x00400, /* driver structure */
DEBUG_HDLC = 0x00800, /* M10x HDLC processing */
DEBUG_WRITE = 0x01000, /* M105 data write */
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_DISCONNECT_IND 4
#define ZSAU_OUTGOING_CALL_PROCEEDING 1
#define ZSAU_PROCEEDING 1
#define ZSAU_CALL_DELIVERED 2
#define ZSAU_ACTIVE 3
#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)
/* int-in-events 3070 */
#define HD_B1_FLOW_CONTROL 0x80
#define HD_B2_FLOW_CONTROL 0x81
#define HD_RECEIVEATDATA_ACK (0x35) // 3070
// att: HD_RECEIVE>>AT<<DATA_ACK
#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
// Powermangment
#define HD_SUSPEND_END (0x61) // ISurf USB
// Configuration
#define HD_RESET_INTERRUPT_PIPE_ACK (0xFF) // ISurf USB + 3070
/* control requests 3070 */
#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_HLC 6
#define AT_NUM 7
/* variables in struct at_state_t */
#define VAR_ZSAU 0
#define VAR_ZDLE 1
#define VAR_ZVLS 2
#define VAR_ZCTP 3
#define VAR_NUM 4
#define STR_NMBR 0
#define STR_ZCPN 1
#define STR_ZCON 2
#define STR_ZBC 3
#define STR_ZHLC 4
#define STR_NUM 5
#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_PROTO_L2 -113
#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
#define INS_DLE_char 0x0002
#define INS_byte_stuff 0x0004
#define INS_have_data 0x0008
#define INS_skip_frame 0x0010
#define INS_DLE_command 0x0020
#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 */
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 {
unsigned char *rcvbuf; /* usb-gigaset receive buffer */
struct bc_state *bcs;
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 resp_type_t {
unsigned char *response;
int resp_code; /* RSP_XXXX */
int type; /* RT_XXXX */
};
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;
__u16 fcs;
struct sk_buff *skb;
int inputstate; /* see INS_XXXX */
int channel;
struct cardstate *cs;
unsigned chstate; /* bitmap (CHS_*) */
int ignore;
unsigned proto2; /* Layer 2 protocol (ISDN_PROTO_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;
};
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;
unsigned open_count;
struct tty_struct *tty;
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 ISDN_STAT_RUN */
unsigned cidmode;
int myid; /* id for communication with LL */
isdn_if iif;
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 modem commands/responses are
dle encoded */
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];
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,
const unsigned char *buf, int len,
struct tasklet_struct *wake_tasklet);
/* 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 */
int (*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 i4l.c to put an skb into the send-queue. */
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 i4l.c to put an skb into the send-queue.
* After sending gigaset_skb_sent() should be called. */
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 i4l.c to put an skb into the send-queue.
* After sending gigaset_skb_sent() should be called. */
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 i4l.c/gigaset.h
*/
/* Called by gigaset_initcs() for setting up with the isdn4linux subsystem */
int gigaset_register_to_LL(struct cardstate *cs, const char *isdnid);
/* Called from xxx-gigaset.c to indicate completion of sending an skb */
void gigaset_skb_sent(struct bc_state *bcs, struct sk_buff *skb);
/* Called from common.c/ev-layer.c to indicate events relevant to the LL */
int gigaset_isdn_icall(struct at_state_t *at_state);
int gigaset_isdn_setup_accept(struct at_state_t *at_state);
int gigaset_isdn_setup_dial(struct at_state_t *at_state, void *data);
void gigaset_i4l_cmd(struct cardstate *cs, int cmd);
void gigaset_i4l_channel_cmd(struct bc_state *bcs, int cmd);
static inline void gigaset_isdn_rcv_err(struct bc_state *bcs)
{
isdn_ctrl response;
/* 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;
bcs->cs->iif.statcallb(&response);
}
/* ===========================================================================
* 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);
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);
void gigaset_debugdrivers(void);
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); //0: success <0: errorcode
/* 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);
gig_dbg(DEBUG_CMD, "scheduling BC_CLOSED");
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);
gig_dbg(DEBUG_CMD, "scheduling BC_OPEN");
gigaset_schedule_event(bcs->cs);
}
/* handling routines for sk_buff */
/* ============================= */
/* pass received skb to LL
* Warning: skb must not be accessed anymore!
*/
static inline void gigaset_rcv_skb(struct sk_buff *skb,
struct cardstate *cs,
struct bc_state *bcs)
{
cs->iif.rcvcallb_skb(cs->myid, bcs->channel, skb);
bcs->trans_down++;
}
/* handle reception of corrupted skb
* Warning: skb must not be accessed anymore!
*/
static inline void gigaset_rcv_error(struct sk_buff *procskb,
struct cardstate *cs,
struct bc_state *bcs)
{
if (procskb)
dev_kfree_skb(procskb);
if (bcs->ignore)
--bcs->ignore;
else {
++bcs->corrupted;
gigaset_isdn_rcv_err(bcs);
}
}
/* 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

View File

@@ -0,0 +1,601 @@
/*
* 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"
/* == 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;
struct bc_state *bcs;
unsigned len;
unsigned skblen;
if (!(cs = gigaset_get_cs_by_id(driverID))) {
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;
}
skblen = ack ? len : 0;
skb->head[0] = skblen & 0xff;
skb->head[1] = skblen >> 8;
gig_dbg(DEBUG_MCMD, "skb: len=%u, skblen=%u: %02x %02x",
len, skblen, (unsigned) skb->head[0], (unsigned) skb->head[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)
{
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 = (unsigned char) skb->head[0] |
(unsigned) (unsigned char) skb->head[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;
bcs->cs->iif.statcallb(&response);
}
}
EXPORT_SYMBOL_GPL(gigaset_skb_sent);
/* 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 = gigaset_get_cs_by_id(cntrl->driver);
struct bc_state *bcs;
int retval = 0;
struct setup_parm *sp;
gigaset_debugdrivers();
if (!cs) {
pr_err("%s: invalid driver ID (%d)\n", __func__, cntrl->driver);
return -ENODEV;
}
switch (cntrl->command) {
case ISDN_CMD_IOCTL:
gig_dbg(DEBUG_ANY, "ISDN_CMD_IOCTL (driver: %d, arg: %ld)",
cntrl->driver, cntrl->arg);
dev_warn(cs->dev, "ISDN_CMD_IOCTL not supported\n");
return -EINVAL;
case ISDN_CMD_DIAL:
gig_dbg(DEBUG_ANY,
"ISDN_CMD_DIAL (driver: %d, ch: %ld, "
"phone: %s, ownmsn: %s, si1: %d, si2: %d)",
cntrl->driver, cntrl->arg,
cntrl->parm.setup.phone, cntrl->parm.setup.eazmsn,
cntrl->parm.setup.si1, cntrl->parm.setup.si2);
if (cntrl->arg >= cs->channels) {
dev_err(cs->dev,
"ISDN_CMD_DIAL: invalid channel (%d)\n",
(int) cntrl->arg);
return -EINVAL;
}
bcs = cs->bcs + cntrl->arg;
if (!gigaset_get_channel(bcs)) {
dev_err(cs->dev, "ISDN_CMD_DIAL: channel not free\n");
return -EBUSY;
}
sp = kmalloc(sizeof *sp, GFP_ATOMIC);
if (!sp) {
gigaset_free_channel(bcs);
dev_err(cs->dev, "ISDN_CMD_DIAL: out of memory\n");
return -ENOMEM;
}
*sp = cntrl->parm.setup;
if (!gigaset_add_event(cs, &bcs->at_state, EV_DIAL, sp,
bcs->at_state.seq_index, NULL)) {
//FIXME what should we do?
kfree(sp);
gigaset_free_channel(bcs);
return -ENOMEM;
}
gig_dbg(DEBUG_CMD, "scheduling DIAL");
gigaset_schedule_event(cs);
break;
case ISDN_CMD_ACCEPTD: //FIXME
gig_dbg(DEBUG_ANY, "ISDN_CMD_ACCEPTD");
if (cntrl->arg >= cs->channels) {
dev_err(cs->dev,
"ISDN_CMD_ACCEPTD: invalid channel (%d)\n",
(int) cntrl->arg);
return -EINVAL;
}
if (!gigaset_add_event(cs, &cs->bcs[cntrl->arg].at_state,
EV_ACCEPT, NULL, 0, NULL)) {
//FIXME what should we do?
return -ENOMEM;
}
gig_dbg(DEBUG_CMD, "scheduling ACCEPT");
gigaset_schedule_event(cs);
break;
case ISDN_CMD_ACCEPTB:
gig_dbg(DEBUG_ANY, "ISDN_CMD_ACCEPTB");
break;
case ISDN_CMD_HANGUP:
gig_dbg(DEBUG_ANY, "ISDN_CMD_HANGUP (ch: %d)",
(int) cntrl->arg);
if (cntrl->arg >= cs->channels) {
dev_err(cs->dev,
"ISDN_CMD_HANGUP: invalid channel (%d)\n",
(int) cntrl->arg);
return -EINVAL;
}
if (!gigaset_add_event(cs, &cs->bcs[cntrl->arg].at_state,
EV_HUP, NULL, 0, NULL)) {
//FIXME what should we do?
return -ENOMEM;
}
gig_dbg(DEBUG_CMD, "scheduling HUP");
gigaset_schedule_event(cs);
break;
case ISDN_CMD_CLREAZ: /* Do not signal incoming signals */ //FIXME
gig_dbg(DEBUG_ANY, "ISDN_CMD_CLREAZ");
break;
case ISDN_CMD_SETEAZ: /* Signal incoming calls for given MSN */ //FIXME
gig_dbg(DEBUG_ANY,
"ISDN_CMD_SETEAZ (id: %d, ch: %ld, number: %s)",
cntrl->driver, cntrl->arg, cntrl->parm.num);
break;
case ISDN_CMD_SETL2: /* Set L2 to given protocol */
gig_dbg(DEBUG_ANY, "ISDN_CMD_SETL2 (ch: %ld, proto: %lx)",
cntrl->arg & 0xff, (cntrl->arg >> 8));
if ((cntrl->arg & 0xff) >= cs->channels) {
dev_err(cs->dev,
"ISDN_CMD_SETL2: invalid channel (%d)\n",
(int) cntrl->arg & 0xff);
return -EINVAL;
}
if (!gigaset_add_event(cs, &cs->bcs[cntrl->arg & 0xff].at_state,
EV_PROTO_L2, NULL, cntrl->arg >> 8,
NULL)) {
//FIXME what should we do?
return -ENOMEM;
}
gig_dbg(DEBUG_CMD, "scheduling PROTO_L2");
gigaset_schedule_event(cs);
break;
case ISDN_CMD_SETL3: /* Set L3 to given protocol */
gig_dbg(DEBUG_ANY, "ISDN_CMD_SETL3 (ch: %ld, proto: %lx)",
cntrl->arg & 0xff, (cntrl->arg >> 8));
if ((cntrl->arg & 0xff) >= cs->channels) {
dev_err(cs->dev,
"ISDN_CMD_SETL3: invalid channel (%d)\n",
(int) cntrl->arg & 0xff);
return -EINVAL;
}
if (cntrl->arg >> 8 != ISDN_PROTO_L3_TRANS) {
dev_err(cs->dev,
"ISDN_CMD_SETL3: invalid protocol %lu\n",
cntrl->arg >> 8);
return -EINVAL;
}
break;
case ISDN_CMD_PROCEED:
gig_dbg(DEBUG_ANY, "ISDN_CMD_PROCEED"); //FIXME
break;
case ISDN_CMD_ALERT:
gig_dbg(DEBUG_ANY, "ISDN_CMD_ALERT"); //FIXME
if (cntrl->arg >= cs->channels) {
dev_err(cs->dev,
"ISDN_CMD_ALERT: invalid channel (%d)\n",
(int) cntrl->arg);
return -EINVAL;
}
//bcs = cs->bcs + cntrl->arg;
//bcs->proto2 = -1;
// FIXME
break;
case ISDN_CMD_REDIR:
gig_dbg(DEBUG_ANY, "ISDN_CMD_REDIR"); //FIXME
break;
case ISDN_CMD_PROT_IO:
gig_dbg(DEBUG_ANY, "ISDN_CMD_PROT_IO");
break;
case ISDN_CMD_FAXCMD:
gig_dbg(DEBUG_ANY, "ISDN_CMD_FAXCMD");
break;
case ISDN_CMD_GETL2:
gig_dbg(DEBUG_ANY, "ISDN_CMD_GETL2");
break;
case ISDN_CMD_GETL3:
gig_dbg(DEBUG_ANY, "ISDN_CMD_GETL3");
break;
case ISDN_CMD_GETEAZ:
gig_dbg(DEBUG_ANY, "ISDN_CMD_GETEAZ");
break;
case ISDN_CMD_SETSIL:
gig_dbg(DEBUG_ANY, "ISDN_CMD_SETSIL");
break;
case ISDN_CMD_GETSIL:
gig_dbg(DEBUG_ANY, "ISDN_CMD_GETSIL");
break;
default:
dev_err(cs->dev, "unknown command %d from LL\n",
cntrl->command);
return -EINVAL;
}
return retval;
}
void gigaset_i4l_cmd(struct cardstate *cs, int cmd)
{
isdn_ctrl command;
command.driver = cs->myid;
command.command = cmd;
command.arg = 0;
cs->iif.statcallb(&command);
}
void gigaset_i4l_channel_cmd(struct bc_state *bcs, int cmd)
{
isdn_ctrl command;
command.driver = bcs->cs->myid;
command.command = cmd;
command.arg = bcs->channel;
bcs->cs->iif.statcallb(&command);
}
int gigaset_isdn_setup_dial(struct at_state_t *at_state, void *data)
{
struct bc_state *bcs = at_state->bcs;
unsigned proto;
const char *bc;
size_t length[AT_NUM];
size_t l;
int i;
struct setup_parm *sp = data;
switch (bcs->proto2) {
case ISDN_PROTO_L2_HDLC:
proto = 1; /* 0: Bitsynchron, 1: HDLC, 2: voice */
break;
case ISDN_PROTO_L2_TRANS:
proto = 2; /* 0: Bitsynchron, 1: HDLC, 2: voice */
break;
default:
dev_err(bcs->cs->dev, "%s: invalid L2 protocol: %u\n",
__func__, bcs->proto2);
return -EINVAL;
}
switch (sp->si1) {
case 1: /* audio */
bc = "9090A3"; /* 3.1 kHz audio, A-law */
break;
case 7: /* data */
default: /* hope the app knows what it is doing */
bc = "8890"; /* unrestricted digital information */
}
//FIXME add missing si1 values from 1TR6, inspect si2, set HLC/LLC
length[AT_DIAL ] = 1 + strlen(sp->phone) + 1 + 1;
l = strlen(sp->eazmsn);
length[AT_MSN ] = l ? 6 + l + 1 + 1 : 0;
length[AT_BC ] = 5 + strlen(bc) + 1 + 1;
length[AT_PROTO] = 6 + 1 + 1 + 1; /* proto: 1 character */
length[AT_ISO ] = 6 + 1 + 1 + 1; /* channel: 1 character */
length[AT_TYPE ] = 6 + 1 + 1 + 1; /* call type: 1 character */
length[AT_HLC ] = 0;
for (i = 0; i < AT_NUM; ++i) {
kfree(bcs->commands[i]);
bcs->commands[i] = NULL;
if (length[i] &&
!(bcs->commands[i] = kmalloc(length[i], GFP_ATOMIC))) {
dev_err(bcs->cs->dev, "out of memory\n");
return -ENOMEM;
}
}
/* type = 1: extern, 0: intern, 2: recall, 3: door, 4: centrex */
if (sp->phone[0] == '*' && sp->phone[1] == '*') {
/* internal call: translate ** prefix to CTP value */
snprintf(bcs->commands[AT_DIAL], length[AT_DIAL],
"D%s\r", sp->phone+2);
strncpy(bcs->commands[AT_TYPE], "^SCTP=0\r", length[AT_TYPE]);
} else {
snprintf(bcs->commands[AT_DIAL], length[AT_DIAL],
"D%s\r", sp->phone);
strncpy(bcs->commands[AT_TYPE], "^SCTP=1\r", length[AT_TYPE]);
}
if (bcs->commands[AT_MSN])
snprintf(bcs->commands[AT_MSN], length[AT_MSN],
"^SMSN=%s\r", sp->eazmsn);
snprintf(bcs->commands[AT_BC ], length[AT_BC ],
"^SBC=%s\r", bc);
snprintf(bcs->commands[AT_PROTO], length[AT_PROTO],
"^SBPR=%u\r", proto);
snprintf(bcs->commands[AT_ISO ], length[AT_ISO ],
"^SISO=%u\r", (unsigned)bcs->channel + 1);
return 0;
}
int gigaset_isdn_setup_accept(struct at_state_t *at_state)
{
unsigned proto;
size_t length[AT_NUM];
int i;
struct bc_state *bcs = at_state->bcs;
switch (bcs->proto2) {
case ISDN_PROTO_L2_HDLC:
proto = 1; /* 0: Bitsynchron, 1: HDLC, 2: voice */
break;
case ISDN_PROTO_L2_TRANS:
proto = 2; /* 0: Bitsynchron, 1: HDLC, 2: voice */
break;
default:
dev_err(at_state->cs->dev, "%s: invalid protocol: %u\n",
__func__, bcs->proto2);
return -EINVAL;
}
length[AT_DIAL ] = 0;
length[AT_MSN ] = 0;
length[AT_BC ] = 0;
length[AT_PROTO] = 6 + 1 + 1 + 1; /* proto: 1 character */
length[AT_ISO ] = 6 + 1 + 1 + 1; /* channel: 1 character */
length[AT_TYPE ] = 0;
length[AT_HLC ] = 0;
for (i = 0; i < AT_NUM; ++i) {
kfree(bcs->commands[i]);
bcs->commands[i] = NULL;
if (length[i] &&
!(bcs->commands[i] = kmalloc(length[i], GFP_ATOMIC))) {
dev_err(at_state->cs->dev, "out of memory\n");
return -ENOMEM;
}
}
snprintf(bcs->commands[AT_PROTO], length[AT_PROTO],
"^SBPR=%u\r", proto);
snprintf(bcs->commands[AT_ISO ], length[AT_ISO ],
"^SISO=%u\r", (unsigned) bcs->channel + 1);
return 0;
}
/**
* 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_ctrl response;
int retval;
/* fill ICALL structure */
response.parm.setup.si1 = 0; /* default: unknown */
response.parm.setup.si2 = 0;
response.parm.setup.screen = 0; //FIXME how to set these?
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]) {
strncpy(response.parm.setup.phone, at_state->str_var[STR_NMBR],
sizeof response.parm.setup.phone - 1);
response.parm.setup.phone[sizeof response.parm.setup.phone - 1] = 0;
} else
response.parm.setup.phone[0] = 0;
if (at_state->str_var[STR_ZCPN]) {
strncpy(response.parm.setup.eazmsn, at_state->str_var[STR_ZCPN],
sizeof response.parm.setup.eazmsn - 1);
response.parm.setup.eazmsn[sizeof response.parm.setup.eazmsn - 1] = 0;
} 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; //FIXME
} else {
gig_dbg(DEBUG_CMD, "Sending ICALL");
response.command = ISDN_STAT_ICALL;
response.arg = bcs->channel; //FIXME
}
response.driver = cs->myid;
retval = cs->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;
}
}
/* Set Callback function pointer */
int gigaset_register_to_LL(struct cardstate *cs, const char *isdnid)
{
isdn_if *iif = &cs->iif;
gig_dbg(DEBUG_ANY, "Register driver capabilities to LL");
if (snprintf(iif->id, sizeof iif->id, "%s_%u", isdnid, cs->minor_index)
>= sizeof iif->id) {
pr_err("ID too long: %s\n", isdnid);
return 0;
}
iif->owner = THIS_MODULE;
iif->channels = cs->channels;
iif->maxbufsize = MAX_BUF_SIZE;
iif->features = ISDN_FEATURE_L2_TRANS |
ISDN_FEATURE_L2_HDLC |
#ifdef GIG_X75
ISDN_FEATURE_L2_X75I |
#endif
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");
return 0;
}
cs->myid = iif->channels; /* Set my device id */
return 1;
}

View File

@@ -0,0 +1,706 @@
/*
* 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.h>
#include <linux/tty_flip.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;
}
gig_dbg(DEBUG_CMD, "scheduling IF_LOCK");
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;
}
gig_dbg(DEBUG_CMD, "scheduling IF_VER");
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 ***/
/* stolen from usbserial and some other tty drivers */
static int if_open(struct tty_struct *tty, struct file *filp);
static void if_close(struct tty_struct *tty, struct file *filp);
static int if_ioctl(struct tty_struct *tty, struct file *file,
unsigned int cmd, unsigned long arg);
static int if_write_room(struct tty_struct *tty);
static int if_chars_in_buffer(struct tty_struct *tty);
static void if_throttle(struct tty_struct *tty);
static void if_unthrottle(struct tty_struct *tty);
static void if_set_termios(struct tty_struct *tty, struct ktermios *old);
static int if_tiocmget(struct tty_struct *tty, struct file *file);
static int if_tiocmset(struct tty_struct *tty, struct file *file,
unsigned int set, unsigned int clear);
static int if_write(struct tty_struct *tty,
const unsigned char *buf, int count);
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,
};
static int if_open(struct tty_struct *tty, struct file *filp)
{
struct cardstate *cs;
unsigned long flags;
gig_dbg(DEBUG_IF, "%d+%d: %s()",
tty->driver->minor_start, tty->index, __func__);
tty->driver_data = NULL;
cs = gigaset_get_cs_by_tty(tty);
if (!cs || !try_module_get(cs->driver->owner))
return -ENODEV;
if (mutex_lock_interruptible(&cs->mutex))
return -ERESTARTSYS; // FIXME -EINTR?
tty->driver_data = cs;
++cs->open_count;
if (cs->open_count == 1) {
spin_lock_irqsave(&cs->lock, flags);
cs->tty = tty;
spin_unlock_irqrestore(&cs->lock, flags);
tty->low_latency = 1; //FIXME test
}
mutex_unlock(&cs->mutex);
return 0;
}
static void if_close(struct tty_struct *tty, struct file *filp)
{
struct cardstate *cs;
unsigned long flags;
cs = (struct cardstate *) tty->driver_data;
if (!cs) {
pr_err("%s: no cardstate\n", __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->open_count)
dev_warn(cs->dev, "%s: device not opened\n", __func__);
else {
if (!--cs->open_count) {
spin_lock_irqsave(&cs->lock, flags);
cs->tty = NULL;
spin_unlock_irqrestore(&cs->lock, flags);
}
}
mutex_unlock(&cs->mutex);
module_put(cs->driver->owner);
}
static int if_ioctl(struct tty_struct *tty, struct file *file,
unsigned int cmd, unsigned long arg)
{
struct cardstate *cs;
int retval = -ENODEV;
int int_arg;
unsigned char buf[6];
unsigned version[4];
cs = (struct cardstate *) tty->driver_data;
if (!cs) {
pr_err("%s: no cardstate\n", __func__);
return -ENODEV;
}
gig_dbg(DEBUG_IF, "%u: %s(0x%x)", cs->minor_index, __func__, cmd);
if (mutex_lock_interruptible(&cs->mutex))
return -ERESTARTSYS; // FIXME -EINTR?
if (!cs->connected) {
gig_dbg(DEBUG_IF, "not connected");
retval = -ENODEV;
} else if (!cs->open_count)
dev_warn(cs->dev, "%s: device not opened\n", __func__);
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_ANY, "%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 file *file)
{
struct cardstate *cs;
int retval;
cs = (struct cardstate *) tty->driver_data;
if (!cs) {
pr_err("%s: no cardstate\n", __func__);
return -ENODEV;
}
gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
if (mutex_lock_interruptible(&cs->mutex))
return -ERESTARTSYS; // FIXME -EINTR?
// FIXME read from device?
retval = cs->control_state & (TIOCM_RTS|TIOCM_DTR);
mutex_unlock(&cs->mutex);
return retval;
}
static int if_tiocmset(struct tty_struct *tty, struct file *file,
unsigned int set, unsigned int clear)
{
struct cardstate *cs;
int retval;
unsigned mc;
cs = (struct cardstate *) tty->driver_data;
if (!cs) {
pr_err("%s: no cardstate\n", __func__);
return -ENODEV;
}
gig_dbg(DEBUG_IF, "%u: %s(0x%x, 0x%x)",
cs->minor_index, __func__, set, clear);
if (mutex_lock_interruptible(&cs->mutex))
return -ERESTARTSYS; // FIXME -EINTR?
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;
int retval = -ENODEV;
cs = (struct cardstate *) tty->driver_data;
if (!cs) {
pr_err("%s: no cardstate\n", __func__);
return -ENODEV;
}
gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
if (mutex_lock_interruptible(&cs->mutex))
return -ERESTARTSYS; // FIXME -EINTR?
if (!cs->connected) {
gig_dbg(DEBUG_IF, "not connected");
retval = -ENODEV;
} else if (!cs->open_count)
dev_warn(cs->dev, "%s: device not opened\n", __func__);
else if (cs->mstate != MS_LOCKED) {
dev_warn(cs->dev, "can't write to unlocked device\n");
retval = -EBUSY;
} else {
retval = cs->ops->write_cmd(cs, buf, count,
&cs->if_wake_tasklet);
}
mutex_unlock(&cs->mutex);
return retval;
}
static int if_write_room(struct tty_struct *tty)
{
struct cardstate *cs;
int retval = -ENODEV;
cs = (struct cardstate *) tty->driver_data;
if (!cs) {
pr_err("%s: no cardstate\n", __func__);
return -ENODEV;
}
gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
if (mutex_lock_interruptible(&cs->mutex))
return -ERESTARTSYS; // FIXME -EINTR?
if (!cs->connected) {
gig_dbg(DEBUG_IF, "not connected");
retval = -ENODEV;
} else if (!cs->open_count)
dev_warn(cs->dev, "%s: device not opened\n", __func__);
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;
int retval = 0;
cs = (struct cardstate *) tty->driver_data;
if (!cs) {
pr_err("%s: no cardstate\n", __func__);
return 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->open_count)
dev_warn(cs->dev, "%s: device not opened\n", __func__);
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;
cs = (struct cardstate *) tty->driver_data;
if (!cs) {
pr_err("%s: no cardstate\n", __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->open_count)
dev_warn(cs->dev, "%s: device not opened\n", __func__);
else {
//FIXME
}
mutex_unlock(&cs->mutex);
}
static void if_unthrottle(struct tty_struct *tty)
{
struct cardstate *cs;
cs = (struct cardstate *) tty->driver_data;
if (!cs) {
pr_err("%s: no cardstate\n", __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->open_count)
dev_warn(cs->dev, "%s: device not opened\n", __func__);
else {
//FIXME
}
mutex_unlock(&cs->mutex);
}
static void if_set_termios(struct tty_struct *tty, struct ktermios *old)
{
struct cardstate *cs;
unsigned int iflag;
unsigned int cflag;
unsigned int old_cflag;
unsigned int control_state, new_state;
cs = (struct cardstate *) tty->driver_data;
if (!cs) {
pr_err("%s: no cardstate\n", __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");
goto out;
}
if (!cs->open_count) {
dev_warn(cs->dev, "%s: device not opened\n", __func__);
goto out;
}
// stolen from mct_u232.c
iflag = tty->termios->c_iflag;
cflag = tty->termios->c_cflag;
old_cflag = old ? old->c_cflag : cflag; //FIXME?
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);
}
/* wakeup tasklet for the write operation */
static void if_wake(unsigned long data)
{
struct cardstate *cs = (struct cardstate *) data;
if (cs->tty)
tty_wakeup(cs->tty);
}
/*** 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_register_device(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)
{
unsigned long flags;
struct tty_struct *tty;
spin_lock_irqsave(&cs->lock, flags);
if ((tty = cs->tty) == NULL)
gig_dbg(DEBUG_ANY, "receive on closed device");
else {
tty_insert_flip_string(tty, buffer, len);
tty_flip_buffer_push(tty);
}
spin_unlock_irqrestore(&cs->lock, flags);
}
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)
{
unsigned minors = drv->minors;
int ret;
struct tty_driver *tty;
drv->have_tty = 0;
if ((drv->tty = alloc_tty_driver(minors)) == NULL)
goto enomem;
tty = drv->tty;
tty->magic = TTY_DRIVER_MAGIC,
tty->major = GIG_MAJOR,
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->num = drv->minors;
tty->owner = THIS_MODULE;
tty->init_termios = tty_std_termios; //FIXME
tty->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; //FIXME
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,83 @@
/*
* 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/ctype.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; // FIXME -EINTR?
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;
}
gig_dbg(DEBUG_CMD, "scheduling PROC_CIDMODE");
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,818 @@
/* 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/tty.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;
int sent = 0;
if (!cs || !(bcs = cs->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 */
if (!(bcs->tx_skb = skb_dequeue(&bcs->squeue)))
/* no skb either, nothing to do */
return;
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, const unsigned char *buf,
int len, struct tasklet_struct *wake_tasklet)
{
struct cmdbuf_t *cb;
unsigned long flags;
gigaset_dbg_buffer(cs->mstate != MS_LOCKED ?
DEBUG_TRANSCMD : DEBUG_LOCKCMD,
"CMD Transmit", len, buf);
if (len <= 0)
return 0;
if (!(cb = kmalloc(sizeof(struct cmdbuf_t) + len, GFP_ATOMIC))) {
dev_err(cs->dev, "%s: out of memory!\n", __func__);
return -ENOMEM;
}
memcpy(cb->buf, buf, len);
cb->len = len;
cb->offset = 0;
cb->next = NULL;
cb->wake_tasklet = wake_tasklet;
spin_lock_irqsave(&cs->cmdlock, flags);
cb->prev = cs->lastcmdbuf;
if (cs->lastcmdbuf)
cs->lastcmdbuf->next = cb;
else {
cs->cmdbuf = cb;
cs->curlen = len;
}
cs->cmdbytes += 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 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 1;
}
/*
* Free B channel structure
* Called by "gigaset_freebcs" in common.c
*/
static int gigaset_freebcshw(struct bc_state *bcs)
{
/* unused */
return 1;
}
/*
* 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 */
//FIXME is this actually necessary?
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;
if (!(cs->hw.ser = kzalloc(sizeof(struct ser_cardstate), GFP_KERNEL))) {
pr_err("out of memory\n");
return 0;
}
cs->hw.ser->dev.name = GIGASET_MODULENAME;
cs->hw.ser->dev.id = cs->minor_index;
cs->hw.ser->dev.dev.release = gigaset_device_release;
if ((rc = platform_device_register(&cs->hw.ser->dev)) != 0) {
pr_err("error %d registering platform device\n", rc);
kfree(cs->hw.ser);
cs->hw.ser = NULL;
return 0;
}
dev_set_drvdata(&cs->hw.ser->dev.dev, cs);
tasklet_init(&cs->write_tasklet,
&gigaset_modem_fill, (unsigned long) cs);
return 1;
}
/*
* 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, NULL, 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;
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 intialize it */
if (!(cs = gigaset_initcs(driver, 1, 1, 0, cidmode,
GIGASET_MODULENAME)))
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;
if (!gigaset_start(cs)) {
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 -ENODEV;
}
/*
* 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;
if (!(inbuf = cs->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__);
if ((rc = platform_driver_register(&device_driver)) != 0) {
pr_err("error %d registering platform driver\n", rc);
return rc;
}
/* allocate memory for our driver state and intialize it */
if (!(driver = gigaset_initdriver(GIGASET_MINOR, GIGASET_MINORS,
GIGASET_MODULENAME, GIGASET_DEVNAME,
&ops, THIS_MODULE)))
goto error;
if ((rc = tty_register_ldisc(N_GIGASET_M101, &gigaset_ldisc)) != 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;
}
if ((rc = tty_unregister_ldisc(N_GIGASET_M101)) != 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,974 @@
/*
* 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/errno.h>
#include <linux/init.h>
#include <linux/slab.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"
#define IF_WRITEBUF 2000 //FIXME // WAKEUP_CHARS: 256
/* 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 = NULL;
/* 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,
};
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;
__u8 bulk_out_endpointAddr;
struct urb *bulk_out_urb;
/* Input buffer */
int rcvbuf_size;
struct urb *read_urb;
__u8 int_in_endpointAddr;
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);
// don't use this in an interrupt/BH
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) {
//FIXME more values?
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 */ //FIXME is this okay?
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");
// FIXME should we tell the LL?
again = 1; /* no callback will be called! */
}
}
} while (again);
}
/*
* Interrupt Input URB completion routine
*/
static void gigaset_read_int_callback(struct urb *urb)
{
struct inbuf_t *inbuf = urb->context;
struct cardstate *cs = inbuf->cs;
int status = urb->status;
int r;
unsigned numbytes;
unsigned char *src;
unsigned long flags;
if (!status) {
numbytes = urb->actual_length;
if (numbytes) {
src = inbuf->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; // FIXME
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_endpointAddr & 0x0f),
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, const unsigned char *buf,
int len, struct tasklet_struct *wake_tasklet)
{
struct cmdbuf_t *cb;
unsigned long flags;
gigaset_dbg_buffer(cs->mstate != MS_LOCKED ?
DEBUG_TRANSCMD : DEBUG_LOCKCMD,
"CMD Transmit", len, buf);
if (len <= 0)
return 0;
if (!(cb = kmalloc(sizeof(struct cmdbuf_t) + len, GFP_ATOMIC))) {
dev_err(cs->dev, "%s: out of memory\n", __func__);
return -ENOMEM;
}
memcpy(cb->buf, buf, len);
cb->len = len;
cb->offset = 0;
cb->next = NULL;
cb->wake_tasklet = wake_tasklet;
spin_lock_irqsave(&cs->cmdlock, flags);
cb->prev = cs->lastcmdbuf;
if (cs->lastcmdbuf)
cs->lastcmdbuf->next = cb;
else {
cs->cmdbuf = cb;
cs->curlen = len;
}
cs->cmdbytes += 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 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 int gigaset_freebcshw(struct bc_state *bcs)
{
/* unused */
return 1;
}
/* Initialize the b-channel structure */
static int gigaset_initbcshw(struct bc_state *bcs)
{
/* unused */
bcs->hw.usb = NULL;
return 1;
}
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 0;
}
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 1;
}
/* 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_WRITE, "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 // FIXME copying not necessary
* 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_endpointAddr & 0x0f),
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); //FIXME also, when ret<0?
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 intialize 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_endpointAddr = endpoint->bEndpointAddress;
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->int_in_endpointAddr = endpoint->bEndpointAddress;
cs->inbuf[0].rcvbuf = kmalloc(buffer_size, GFP_KERNEL);
if (!cs->inbuf[0].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,
endpoint->bEndpointAddress & 0x0f),
cs->inbuf[0].rcvbuf, buffer_size,
gigaset_read_int_callback,
cs->inbuf + 0, 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;
if (!gigaset_start(cs)) {
tasklet_kill(&cs->write_tasklet);
retval = -ENODEV; //FIXME
goto error;
}
return 0;
error:
usb_kill_urb(ucs->read_urb);
kfree(ucs->bulk_out_buffer);
usb_free_urb(ucs->bulk_out_urb);
kfree(cs->inbuf[0].rcvbuf);
usb_free_urb(ucs->read_urb);
usb_set_intfdata(interface, NULL);
ucs->read_urb = ucs->bulk_out_urb = NULL;
cs->inbuf[0].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(cs->inbuf[0].rcvbuf);
usb_free_urb(ucs->read_urb);
ucs->read_urb = ucs->bulk_out_urb = NULL;
cs->inbuf[0].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 intialize it */
if ((driver = gigaset_initdriver(GIGASET_MINOR, GIGASET_MINORS,
GIGASET_MODULENAME, GIGASET_DEVNAME,
&ops, THIS_MODULE)) == NULL)
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 -1;
}
/*
* 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");