2006-03-26 13:38:37 +04:00
/*
* Common data handling layer for ser_gigaset and usb_gigaset
*
* Copyright ( c ) 2005 by Tilman Schmidt < tilman @ imap . cc > ,
* Hansjoerg Lipp < hjlipp @ web . de > ,
2006-04-11 09:55:14 +04:00
* Stefan Eilers .
2006-03-26 13:38:37 +04:00
*
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
* 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>
2006-12-08 13:36:30 +03:00
# include <linux/bitrev.h>
2006-03-26 13:38:37 +04:00
/* check if byte must be stuffed/escaped
* I ' m not sure which data should be encoded .
2009-10-25 12:30:07 +03:00
* Therefore I will go the hard way and encode every value
2006-03-26 13:38:37 +04:00
* 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 =========================================================== */
2009-10-25 12:30:07 +03:00
/* process a block of received bytes in command mode
* ( mstate ! = MS_LOCKED & & ( inputstate & INS_command ) )
* Append received bytes to the command response buffer and forward them
* line by line to the response handler . Exit whenever a mode / state change
* might have occurred .
2006-03-26 13:38:37 +04:00
* Return value :
* number of processed bytes
*/
2009-10-25 12:30:07 +03:00
static unsigned cmd_loop ( unsigned numbytes , struct inbuf_t * inbuf )
2006-03-26 13:38:37 +04:00
{
2009-10-25 12:30:07 +03:00
unsigned char * src = inbuf - > data + inbuf - > head ;
2006-03-26 13:38:37 +04:00
struct cardstate * cs = inbuf - > cs ;
2009-10-25 12:30:07 +03:00
unsigned cbytes = cs - > cbytes ;
unsigned procbytes = 0 ;
unsigned char c ;
while ( procbytes < numbytes ) {
c = * src + + ;
procbytes + + ;
switch ( c ) {
case ' \n ' :
if ( cbytes = = 0 & & cs - > respdata [ 0 ] = = ' \r ' ) {
/* collapse LF with preceding CR */
cs - > respdata [ 0 ] = 0 ;
break ;
}
/* --v-- fall through --v-- */
case ' \r ' :
/* end of message line, pass to response handler */
gig_dbg ( DEBUG_TRANSCMD , " %s: End of Message (%d Bytes) " ,
2006-04-11 09:55:04 +04:00
__func__ , cbytes ) ;
2009-10-25 12:30:07 +03:00
if ( cbytes > = MAX_RESP_SIZE ) {
dev_warn ( cs - > dev , " response too large (%d) \n " ,
cbytes ) ;
cbytes = MAX_RESP_SIZE ;
}
2006-03-26 13:38:37 +04:00
cs - > cbytes = cbytes ;
2009-10-25 12:30:07 +03:00
gigaset_handle_modem_response ( cs ) ;
2006-03-26 13:38:37 +04:00
cbytes = 0 ;
2009-10-25 12:30:07 +03:00
/* store EOL byte for CRLF collapsing */
cs - > respdata [ 0 ] = c ;
2006-03-26 13:38:37 +04:00
2009-10-25 12:30:07 +03:00
/* cs->dle may have changed */
if ( cs - > dle & & ! ( inbuf - > inputstate & INS_DLE_command ) )
inbuf - > inputstate & = ~ INS_command ;
/* return for reevaluating state */
goto exit ;
case DLE_FLAG :
if ( inbuf - > inputstate & INS_DLE_char ) {
/* quoted DLE: clear quote flag */
inbuf - > inputstate & = ~ INS_DLE_char ;
} else if ( cs - > dle | |
( inbuf - > inputstate & INS_DLE_command ) ) {
/* DLE escape, pass up for handling */
inbuf - > inputstate | = INS_DLE_char ;
goto exit ;
}
/* quoted or not in DLE mode: treat as regular data */
/* --v-- fall through --v-- */
default :
/* append to line buffer if possible */
if ( cbytes < MAX_RESP_SIZE )
cs - > respdata [ cbytes ] = c ;
cbytes + + ;
2006-03-26 13:38:37 +04:00
}
}
2009-10-25 12:30:07 +03:00
exit :
2006-03-26 13:38:37 +04:00
cs - > cbytes = cbytes ;
2009-10-25 12:30:07 +03:00
return procbytes ;
2006-03-26 13:38:37 +04:00
}
2009-10-25 12:30:07 +03:00
/* process a block of received bytes in lock mode
* All received bytes are passed unmodified to the tty i / f .
2006-03-26 13:38:37 +04:00
* Return value :
* number of processed bytes
*/
2009-10-25 12:30:07 +03:00
static unsigned lock_loop ( unsigned numbytes , struct inbuf_t * inbuf )
2006-03-26 13:38:37 +04:00
{
2009-10-25 12:30:07 +03:00
unsigned char * src = inbuf - > data + inbuf - > head ;
2006-03-26 13:38:37 +04:00
2009-10-25 12:30:07 +03:00
gigaset_dbg_buffer ( DEBUG_LOCKCMD , " received response " , numbytes , src ) ;
gigaset_if_receive ( inbuf - > cs , src , numbytes ) ;
2006-03-26 13:38:37 +04:00
return numbytes ;
}
2009-10-25 12:30:07 +03:00
/* set up next receive skb for data mode
*/
static void new_rcv_skb ( struct bc_state * bcs )
{
struct cardstate * cs = bcs - > cs ;
unsigned short hw_hdr_len = cs - > hw_hdr_len ;
if ( bcs - > ignore ) {
bcs - > skb = NULL ;
return ;
}
bcs - > skb = dev_alloc_skb ( SBUFSIZE + hw_hdr_len ) ;
if ( bcs - > skb = = NULL ) {
dev_warn ( cs - > dev , " could not allocate new skb \n " ) ;
return ;
}
skb_reserve ( bcs - > skb , hw_hdr_len ) ;
}
2006-03-26 13:38:37 +04:00
/* process a block of received bytes in HDLC data mode
2009-10-25 12:30:07 +03:00
* ( mstate ! = MS_LOCKED & & ! ( inputstate & INS_command ) & & proto2 = = L2_HDLC )
2006-03-26 13:38:37 +04:00
* 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
*/
2009-10-25 12:30:07 +03:00
static unsigned hdlc_loop ( unsigned numbytes , struct inbuf_t * inbuf )
2006-03-26 13:38:37 +04:00
{
struct cardstate * cs = inbuf - > cs ;
2009-10-25 12:30:07 +03:00
struct bc_state * bcs = cs - > bcs ;
2006-04-11 09:55:08 +04:00
int inputstate = bcs - > inputstate ;
__u16 fcs = bcs - > fcs ;
struct sk_buff * skb = bcs - > skb ;
2009-10-25 12:30:07 +03:00
unsigned char * src = inbuf - > data + inbuf - > head ;
unsigned procbytes = 0 ;
unsigned char c ;
2006-03-26 13:38:37 +04:00
2009-10-25 12:30:07 +03:00
if ( inputstate & INS_byte_stuff ) {
if ( ! numbytes )
return 0 ;
2006-03-26 13:38:37 +04:00
inputstate & = ~ INS_byte_stuff ;
goto byte_stuff ;
}
2009-10-25 12:30:07 +03:00
while ( procbytes < numbytes ) {
c = * src + + ;
procbytes + + ;
if ( c = = DLE_FLAG ) {
if ( inputstate & INS_DLE_char ) {
/* quoted DLE: clear quote flag */
inputstate & = ~ INS_DLE_char ;
} else if ( cs - > dle | | ( inputstate & INS_DLE_command ) ) {
/* DLE escape, pass up for handling */
inputstate | = INS_DLE_char ;
2006-03-26 13:38:37 +04:00
break ;
}
2009-10-25 12:30:07 +03:00
}
if ( c = = PPP_ESCAPE ) {
/* byte stuffing indicator: pull in next byte */
if ( procbytes > = numbytes ) {
/* end of buffer, save for later processing */
2006-03-26 13:38:37 +04:00
inputstate | = INS_byte_stuff ;
break ;
}
byte_stuff :
2009-10-25 12:30:07 +03:00
c = * src + + ;
procbytes + + ;
if ( c = = DLE_FLAG ) {
if ( inputstate & INS_DLE_char ) {
/* quoted DLE: clear quote flag */
inputstate & = ~ INS_DLE_char ;
} else if ( cs - > dle | |
( inputstate & INS_DLE_command ) ) {
/* DLE escape, pass up for handling */
inputstate | =
INS_DLE_char | INS_byte_stuff ;
break ;
}
}
2006-03-26 13:38:37 +04:00
c ^ = PPP_TRANS ;
# ifdef CONFIG_GIGASET_DEBUG
2009-10-25 12:30:07 +03:00
if ( ! muststuff ( c ) )
gig_dbg ( DEBUG_HDLC , " byte stuffed: 0x%02x " , c ) ;
2006-03-26 13:38:37 +04:00
# endif
2009-10-25 12:30:07 +03:00
} else if ( c = = PPP_FLAG ) {
/* end of frame: process content if any */
if ( inputstate & INS_have_data ) {
2006-04-11 09:55:04 +04:00
gig_dbg ( DEBUG_HDLC ,
" 7e---------------------------- " ) ;
2006-03-26 13:38:37 +04:00
2009-10-25 12:30:07 +03:00
/* check and pass received frame */
if ( ! skb ) {
/* skipped frame */
gigaset_isdn_rcv_err ( bcs ) ;
} else if ( skb - > len < 2 ) {
/* frame too short for FCS */
dev_warn ( cs - > dev ,
" short frame (%d) \n " ,
skb - > len ) ;
gigaset_isdn_rcv_err ( bcs ) ;
dev_kfree_skb_any ( skb ) ;
} else if ( fcs ! = PPP_GOODFCS ) {
/* frame check error */
2006-04-11 09:55:04 +04:00
dev_err ( cs - > dev ,
2009-05-13 16:44:18 +04:00
" Checksum failed, %u bytes corrupted! \n " ,
skb - > len ) ;
2009-10-06 16:19:07 +04:00
gigaset_isdn_rcv_err ( bcs ) ;
2009-10-25 12:29:57 +03:00
dev_kfree_skb_any ( skb ) ;
2009-10-25 12:30:07 +03:00
} else {
/* good frame */
2009-10-06 16:19:07 +04:00
__skb_trim ( skb , skb - > len - 2 ) ;
gigaset_skb_rcvd ( bcs , skb ) ;
2006-03-26 13:38:37 +04:00
}
2009-10-25 12:30:07 +03:00
/* prepare reception of next frame */
inputstate & = ~ INS_have_data ;
new_rcv_skb ( bcs ) ;
skb = bcs - > skb ;
2006-03-26 13:38:37 +04:00
} else {
2009-10-25 12:30:07 +03:00
/* empty frame (7E 7E) */
# ifdef CONFIG_GIGASET_DEBUG
+ + bcs - > emptycount ;
# endif
if ( ! skb ) {
/* skipped (?) */
gigaset_isdn_rcv_err ( bcs ) ;
new_rcv_skb ( bcs ) ;
skb = bcs - > skb ;
2009-10-06 16:19:07 +04:00
}
2006-03-26 13:38:37 +04:00
}
2009-10-25 12:30:07 +03:00
fcs = PPP_INITFCS ;
continue ;
# ifdef CONFIG_GIGASET_DEBUG
} else if ( muststuff ( c ) ) {
2006-03-26 13:38:37 +04:00
/* Should not happen. Possible after ZDLE=1<CR><LF>. */
2006-04-11 09:55:04 +04:00
gig_dbg ( DEBUG_HDLC , " not byte stuffed: 0x%02x " , c ) ;
2009-10-25 12:30:07 +03:00
# endif
2006-03-26 13:38:37 +04:00
}
2009-10-25 12:30:07 +03:00
/* regular data byte, append to skb */
2006-03-26 13:38:37 +04:00
# ifdef CONFIG_GIGASET_DEBUG
2009-10-25 12:30:07 +03:00
if ( ! ( inputstate & INS_have_data ) ) {
2006-04-11 09:55:04 +04:00
gig_dbg ( DEBUG_HDLC , " 7e (%d x) ================ " ,
bcs - > emptycount ) ;
2006-03-26 13:38:37 +04:00
bcs - > emptycount = 0 ;
}
# endif
inputstate | = INS_have_data ;
2009-10-25 12:30:07 +03:00
if ( skb ) {
if ( skb - > len = = SBUFSIZE ) {
2006-04-11 09:55:04 +04:00
dev_warn ( cs - > dev , " received packet too long \n " ) ;
2006-03-26 13:38:37 +04:00
dev_kfree_skb_any ( skb ) ;
2009-10-25 12:30:07 +03:00
/* skip remainder of packet */
bcs - > skb = skb = NULL ;
} else {
* __skb_put ( skb , 1 ) = c ;
fcs = crc_ccitt_byte ( fcs , c ) ;
2006-03-26 13:38:37 +04:00
}
}
}
2009-10-25 12:30:07 +03:00
2006-03-26 13:38:37 +04:00
bcs - > inputstate = inputstate ;
bcs - > fcs = fcs ;
2009-10-25 12:30:07 +03:00
return procbytes ;
2006-03-26 13:38:37 +04:00
}
/* process a block of received bytes in transparent data mode
2009-10-25 12:30:07 +03:00
* ( mstate ! = MS_LOCKED & & ! ( inputstate & INS_command ) & & proto2 ! = L2_HDLC )
2006-03-26 13:38:37 +04:00
* 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
*/
2009-10-25 12:30:07 +03:00
static unsigned iraw_loop ( unsigned numbytes , struct inbuf_t * inbuf )
2006-03-26 13:38:37 +04:00
{
struct cardstate * cs = inbuf - > cs ;
2009-10-25 12:30:07 +03:00
struct bc_state * bcs = cs - > bcs ;
2006-04-11 09:55:08 +04:00
int inputstate = bcs - > inputstate ;
struct sk_buff * skb = bcs - > skb ;
2009-10-25 12:30:07 +03:00
unsigned char * src = inbuf - > data + inbuf - > head ;
unsigned procbytes = 0 ;
unsigned char c ;
2006-03-26 13:38:37 +04:00
2009-10-25 12:30:07 +03:00
if ( ! skb ) {
/* skip this block */
new_rcv_skb ( bcs ) ;
return numbytes ;
}
2006-03-26 13:38:37 +04:00
2009-10-25 12:30:07 +03:00
while ( procbytes < numbytes & & skb - > len < SBUFSIZE ) {
c = * src + + ;
procbytes + + ;
if ( c = = DLE_FLAG ) {
if ( inputstate & INS_DLE_char ) {
/* quoted DLE: clear quote flag */
inputstate & = ~ INS_DLE_char ;
} else if ( cs - > dle | | ( inputstate & INS_DLE_command ) ) {
/* DLE escape, pass up for handling */
inputstate | = INS_DLE_char ;
2006-03-26 13:38:37 +04:00
break ;
}
}
2009-10-25 12:30:07 +03:00
/* regular data byte: append to current skb */
inputstate | = INS_have_data ;
* __skb_put ( skb , 1 ) = bitrev8 ( c ) ;
2006-03-26 13:38:37 +04:00
}
/* pass data up */
2009-10-25 12:30:07 +03:00
if ( inputstate & INS_have_data ) {
gigaset_skb_rcvd ( bcs , skb ) ;
inputstate & = ~ INS_have_data ;
new_rcv_skb ( bcs ) ;
}
bcs - > inputstate = inputstate ;
return procbytes ;
}
/* process DLE escapes
* Called whenever a DLE sequence might be encountered in the input stream .
* Either processes the entire DLE sequence or , if that isn ' t possible ,
* notes the fact that an initial DLE has been received in the INS_DLE_char
* inputstate flag and resumes processing of the sequence on the next call .
*/
static void handle_dle ( struct inbuf_t * inbuf )
{
struct cardstate * cs = inbuf - > cs ;
if ( cs - > mstate = = MS_LOCKED )
return ; /* no DLE processing in lock mode */
if ( ! ( inbuf - > inputstate & INS_DLE_char ) ) {
/* no DLE pending */
if ( inbuf - > data [ inbuf - > head ] = = DLE_FLAG & &
( cs - > dle | | inbuf - > inputstate & INS_DLE_command ) ) {
/* start of DLE sequence */
inbuf - > head + + ;
if ( inbuf - > head = = inbuf - > tail | |
inbuf - > head = = RBUFSIZE ) {
/* end of buffer, save for later processing */
inbuf - > inputstate | = INS_DLE_char ;
return ;
2009-10-06 16:19:07 +04:00
}
2009-10-25 12:30:07 +03:00
} else {
/* regular data byte */
return ;
2006-03-26 13:38:37 +04:00
}
}
2009-10-25 12:30:07 +03:00
/* consume pending DLE */
inbuf - > inputstate & = ~ INS_DLE_char ;
switch ( inbuf - > data [ inbuf - > head ] ) {
case ' X ' : /* begin of event message */
if ( inbuf - > inputstate & INS_command )
dev_notice ( cs - > dev ,
" received <DLE>X in command mode \n " ) ;
inbuf - > inputstate | = INS_command | INS_DLE_command ;
inbuf - > head + + ; /* byte consumed */
break ;
case ' . ' : /* end of event message */
if ( ! ( inbuf - > inputstate & INS_DLE_command ) )
dev_notice ( cs - > dev ,
" received <DLE>. without <DLE>X \n " ) ;
inbuf - > inputstate & = ~ INS_DLE_command ;
/* return to data mode if in DLE mode */
if ( cs - > dle )
inbuf - > inputstate & = ~ INS_command ;
inbuf - > head + + ; /* byte consumed */
break ;
case DLE_FLAG : /* DLE in data stream */
/* mark as quoted */
inbuf - > inputstate | = INS_DLE_char ;
if ( ! ( cs - > dle | | inbuf - > inputstate & INS_DLE_command ) )
dev_notice ( cs - > dev ,
" received <DLE><DLE> not in DLE mode \n " ) ;
break ; /* quoted byte left in buffer */
default :
dev_notice ( cs - > dev , " received <DLE><%02x> \n " ,
inbuf - > data [ inbuf - > head ] ) ;
/* quoted byte left in buffer */
}
2006-03-26 13:38:37 +04:00
}
2009-10-06 16:19:01 +04:00
/**
* 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 .
2006-03-26 13:38:37 +04:00
*/
void gigaset_m10x_input ( struct inbuf_t * inbuf )
{
2009-10-25 12:30:07 +03:00
struct cardstate * cs = inbuf - > cs ;
unsigned numbytes , procbytes ;
2006-03-26 13:38:37 +04:00
2009-10-25 12:30:07 +03:00
gig_dbg ( DEBUG_INTR , " buffer state: %u -> %u " , inbuf - > head , inbuf - > tail ) ;
2006-03-26 13:38:37 +04:00
2009-10-25 12:30:07 +03:00
while ( inbuf - > head ! = inbuf - > tail ) {
/* check for DLE escape */
handle_dle ( inbuf ) ;
2006-03-26 13:38:37 +04:00
2009-10-25 12:30:07 +03:00
/* process a contiguous block of bytes */
numbytes = ( inbuf - > head > inbuf - > tail ?
RBUFSIZE : inbuf - > tail ) - inbuf - > head ;
gig_dbg ( DEBUG_INTR , " processing %u bytes " , numbytes ) ;
/*
* numbytes may be 0 if handle_dle ( ) ate the last byte .
* This does no harm , * _loop ( ) will just return 0 immediately .
*/
if ( cs - > mstate = = MS_LOCKED )
procbytes = lock_loop ( numbytes , inbuf ) ;
else if ( inbuf - > inputstate & INS_command )
procbytes = cmd_loop ( numbytes , inbuf ) ;
else if ( cs - > bcs - > proto2 = = L2_HDLC )
procbytes = hdlc_loop ( numbytes , inbuf ) ;
else
procbytes = iraw_loop ( numbytes , inbuf ) ;
inbuf - > head + = procbytes ;
/* check for buffer wraparound */
if ( inbuf - > head > = RBUFSIZE )
inbuf - > head = 0 ;
gig_dbg ( DEBUG_INTR , " head set to %u " , inbuf - > head ) ;
2006-03-26 13:38:37 +04:00
}
}
2007-02-21 00:58:17 +03:00
EXPORT_SYMBOL_GPL ( gigaset_m10x_input ) ;
2006-03-26 13:38:37 +04:00
/* == data output ========================================================== */
2009-10-06 16:19:07 +04:00
/*
* Encode a data packet into an octet stuffed HDLC frame with FCS ,
* opening and closing flags , preserving headroom data .
2006-03-26 13:38:37 +04:00
* parameters :
2009-10-06 16:19:07 +04:00
* skb skb containing original packet ( freed upon return )
2006-03-26 13:38:37 +04:00
* Return value :
* pointer to newly allocated skb containing the result frame
2009-10-25 12:29:57 +03:00
* and the original link layer header , NULL on error
2006-03-26 13:38:37 +04:00
*/
2009-10-25 12:29:57 +03:00
static struct sk_buff * HDLC_Encode ( struct sk_buff * skb )
2006-03-26 13:38:37 +04:00
{
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 + + ) ;
}
2006-04-11 09:55:04 +04:00
fcs ^ = 0xffff ; /* complement */
2006-03-26 13:38:37 +04:00
/* size of new buffer: original size + number of stuffing bytes
* + 2 bytes FCS + 2 stuffing bytes for FCS ( if needed ) + 2 flag bytes
2009-10-25 12:29:57 +03:00
* + room for link layer header
2006-03-26 13:38:37 +04:00
*/
2009-10-25 12:29:57 +03:00
hdlc_skb = dev_alloc_skb ( skb - > len + stuf_cnt + 6 + skb - > mac_len ) ;
2006-03-26 13:38:37 +04:00
if ( ! hdlc_skb ) {
2009-10-25 12:29:57 +03:00
dev_kfree_skb_any ( skb ) ;
2006-03-26 13:38:37 +04:00
return NULL ;
}
2009-10-25 12:29:57 +03:00
/* Copy link layer header into new skb */
skb_reset_mac_header ( hdlc_skb ) ;
skb_reserve ( hdlc_skb , skb - > mac_len ) ;
memcpy ( skb_mac_header ( hdlc_skb ) , skb_mac_header ( skb ) , skb - > mac_len ) ;
hdlc_skb - > mac_len = skb - > mac_len ;
2006-03-26 13:38:37 +04:00
/* 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 */
2006-04-11 09:55:04 +04:00
c = ( fcs & 0x00ff ) ; /* least significant byte first */
2006-03-26 13:38:37 +04:00
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 ;
2009-10-25 12:29:57 +03:00
dev_kfree_skb_any ( skb ) ;
2006-03-26 13:38:37 +04:00
return hdlc_skb ;
}
2009-10-06 16:19:07 +04:00
/*
* Encode a data packet into an octet stuffed raw bit inverted frame ,
* preserving headroom data .
2006-03-26 13:38:37 +04:00
* parameters :
2009-10-06 16:19:07 +04:00
* skb skb containing original packet ( freed upon return )
2006-03-26 13:38:37 +04:00
* Return value :
* pointer to newly allocated skb containing the result frame
2009-10-25 12:29:57 +03:00
* and the original link layer header , NULL on error
2006-03-26 13:38:37 +04:00
*/
2009-10-25 12:29:57 +03:00
static struct sk_buff * iraw_encode ( struct sk_buff * skb )
2006-03-26 13:38:37 +04:00
{
struct sk_buff * iraw_skb ;
unsigned char c ;
unsigned char * cp ;
int len ;
2009-10-25 12:29:57 +03:00
/* size of new buffer (worst case = every byte must be stuffed):
* 2 * original size + room for link layer header
*/
iraw_skb = dev_alloc_skb ( 2 * skb - > len + skb - > mac_len ) ;
2006-03-26 13:38:37 +04:00
if ( ! iraw_skb ) {
2009-10-25 12:29:57 +03:00
dev_kfree_skb_any ( skb ) ;
2006-03-26 13:38:37 +04:00
return NULL ;
}
2009-10-06 16:19:07 +04:00
2009-10-25 12:29:57 +03:00
/* copy link layer header into new skb */
skb_reset_mac_header ( iraw_skb ) ;
skb_reserve ( iraw_skb , skb - > mac_len ) ;
memcpy ( skb_mac_header ( iraw_skb ) , skb_mac_header ( skb ) , skb - > mac_len ) ;
iraw_skb - > mac_len = skb - > mac_len ;
2006-03-26 13:38:37 +04:00
2009-10-25 12:29:57 +03:00
/* copy and stuff data */
2006-03-26 13:38:37 +04:00
cp = skb - > data ;
len = skb - > len ;
while ( len - - ) {
2006-12-08 13:36:30 +03:00
c = bitrev8 ( * cp + + ) ;
2006-03-26 13:38:37 +04:00
if ( c = = DLE_FLAG )
* ( skb_put ( iraw_skb , 1 ) ) = c ;
* ( skb_put ( iraw_skb , 1 ) ) = c ;
}
2009-10-25 12:29:57 +03:00
dev_kfree_skb_any ( skb ) ;
2006-03-26 13:38:37 +04:00
return iraw_skb ;
}
2009-10-06 16:19:01 +04:00
/**
* gigaset_m10x_send_skb ( ) - queue an skb for sending
* @ bcs : B channel descriptor structure .
* @ skb : data to send .
*
2009-10-06 16:19:07 +04:00
* Called by LL to encode and queue an skb for sending , and start
2009-10-06 16:19:01 +04:00
* transmission if necessary .
2009-10-06 16:19:07 +04:00
* Once the payload data has been transmitted completely , gigaset_skb_sent ( )
2009-10-25 12:29:57 +03:00
* will be called with the skb ' s link layer header preserved .
2009-10-06 16:19:01 +04:00
*
2006-03-26 13:38:37 +04:00
* Return value :
2009-10-06 16:19:01 +04:00
* number of bytes accepted for sending ( skb - > len ) if ok ,
* error code < 0 ( eg . - ENOMEM ) on error
2006-03-26 13:38:37 +04:00
*/
int gigaset_m10x_send_skb ( struct bc_state * bcs , struct sk_buff * skb )
{
2009-10-25 12:30:07 +03:00
struct cardstate * cs = bcs - > cs ;
2006-04-11 09:55:08 +04:00
unsigned len = skb - > len ;
2006-04-11 09:55:16 +04:00
unsigned long flags ;
2006-03-26 13:38:37 +04:00
2009-10-06 16:19:07 +04:00
if ( bcs - > proto2 = = L2_HDLC )
2009-10-25 12:29:57 +03:00
skb = HDLC_Encode ( skb ) ;
2006-03-26 13:38:37 +04:00
else
2009-10-25 12:29:57 +03:00
skb = iraw_encode ( skb ) ;
2006-04-11 09:55:04 +04:00
if ( ! skb ) {
2009-10-25 12:30:07 +03:00
dev_err ( cs - > dev ,
2008-07-24 08:28:27 +04:00
" unable to allocate memory for encoding! \n " ) ;
2006-03-26 13:38:37 +04:00
return - ENOMEM ;
2006-04-11 09:55:04 +04:00
}
2006-03-26 13:38:37 +04:00
skb_queue_tail ( & bcs - > squeue , skb ) ;
2009-10-25 12:30:07 +03:00
spin_lock_irqsave ( & cs - > lock , flags ) ;
if ( cs - > connected )
tasklet_schedule ( & cs - > write_tasklet ) ;
spin_unlock_irqrestore ( & cs - > lock , flags ) ;
2006-03-26 13:38:37 +04:00
return len ; /* ok so far */
}
2007-02-21 00:58:17 +03:00
EXPORT_SYMBOL_GPL ( gigaset_m10x_send_skb ) ;